diff --git a/.github/ISSUE_TEMPLATE/ice.md b/.github/ISSUE_TEMPLATE/ice.md index e669e4912f8c9..03bc4bab45137 100644 --- a/.github/ISSUE_TEMPLATE/ice.md +++ b/.github/ISSUE_TEMPLATE/ice.md @@ -14,7 +14,7 @@ http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ ### Code -``` +```Rust ``` diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md index 9457cce11afbe..51bf0c3ee6736 100644 --- a/.github/ISSUE_TEMPLATE/tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/tracking_issue.md @@ -1,6 +1,6 @@ --- name: Tracking Issue -about: A tracking issue for a feature in Rust. +about: A tracking issue for an accepted feature or RFC in Rust. title: Tracking Issue for XXX labels: C-tracking-issue --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000..291dbf603612a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,663 @@ +############################################################# +# WARNING: automatically generated file, DO NOT CHANGE! # +############################################################# + +# This file was automatically generated by the expand-yaml-anchors tool. The +# source file that generated this one is: +# +# src/ci/github-actions/ci.yml +# +# Once you make changes to that file you need to run: +# +# ./x.py run src/tools/expand-yaml-anchors/ +# +# The CI build will fail if the tool is not run after changes to this file. + +--- +name: CI +"on": + push: + branches: + - auto + - try + - master + pull_request: + branches: + - "**" +defaults: + run: + shell: "python src/ci/exec-with-shell.py {0}" +jobs: + pr: + name: PR + env: + CI_JOB_NAME: "${{ matrix.name }}" + SCCACHE_BUCKET: rust-lang-gha-caches + TOOLSTATE_REPO: "https://github.com/pietroalbini/rust-toolstate" + CACHE_DOMAIN: ci-caches-gha.rust-lang.org + if: "github.event_name == 'pull_request'" + strategy: + matrix: + include: + - name: mingw-check + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-llvm-8 + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-tools + env: + CI_ONLY_WHEN_SUBMODULES_CHANGED: 1 + os: ubuntu-latest-xl + timeout-minutes: 600 + runs-on: "${{ matrix.os }}" + steps: + - name: disable git crlf conversion + run: git config --global core.autocrlf false + shell: bash + - name: checkout the source code + uses: actions/checkout@v1 + with: + fetch-depth: 2 + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" + - name: add extra environment variables + run: src/ci/scripts/setup-environment.sh + env: + EXTRA_VARIABLES: "${{ toJson(matrix.env) }}" + if: success() && !env.SKIP_JOB + - name: decide whether to skip this job + run: src/ci/scripts/should-skip-this.sh + if: success() && !env.SKIP_JOB + - name: collect CPU statistics + run: src/ci/scripts/collect-cpu-stats.sh + if: success() && !env.SKIP_JOB + - name: show the current environment + run: src/ci/scripts/dump-environment.sh + if: success() && !env.SKIP_JOB + - name: install awscli + run: src/ci/scripts/install-awscli.sh + if: success() && !env.SKIP_JOB + - name: install sccache + run: src/ci/scripts/install-sccache.sh + if: success() && !env.SKIP_JOB + - name: install clang + run: src/ci/scripts/install-clang.sh + if: success() && !env.SKIP_JOB + - name: install WIX + run: src/ci/scripts/install-wix.sh + if: success() && !env.SKIP_JOB + - name: install InnoSetup + run: src/ci/scripts/install-innosetup.sh + if: success() && !env.SKIP_JOB + - name: ensure the build happens on a partition with enough space + run: src/ci/scripts/symlink-build-dir.sh + if: success() && !env.SKIP_JOB + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + if: success() && !env.SKIP_JOB + - name: install MSYS2 + run: src/ci/scripts/install-msys2.sh + if: success() && !env.SKIP_JOB + - name: install MSYS2 packages + run: src/ci/scripts/install-msys2-packages.sh + if: success() && !env.SKIP_JOB + - name: install MinGW + run: src/ci/scripts/install-mingw.sh + if: success() && !env.SKIP_JOB + - name: install ninja + run: src/ci/scripts/install-ninja.sh + if: success() && !env.SKIP_JOB + - name: enable ipv6 on Docker + run: src/ci/scripts/enable-docker-ipv6.sh + if: success() && !env.SKIP_JOB + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + if: success() && !env.SKIP_JOB + - name: checkout submodules + run: src/ci/scripts/checkout-submodules.sh + if: success() && !env.SKIP_JOB + - name: ensure line endings are correct + run: src/ci/scripts/verify-line-endings.sh + if: success() && !env.SKIP_JOB + - name: run the build + run: src/ci/scripts/run-build-from-ci.sh + 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)] }}" + TOOLSTATE_REPO_ACCESS_TOKEN: "${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }}" + if: success() && !env.SKIP_JOB + - name: upload artifacts to S3 + run: src/ci/scripts/upload-artifacts.sh + env: + AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}" + if: "success() && !env.SKIP_JOB && (github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1')" + try: + name: try + env: + CI_JOB_NAME: "${{ matrix.name }}" + SCCACHE_BUCKET: rust-lang-gha-caches + DEPLOY_BUCKET: rust-lang-gha + TOOLSTATE_REPO: "https://github.com/pietroalbini/rust-toolstate" + TOOLSTATE_ISSUES_API_URL: "https://api.github.com/repos/pietroalbini/rust-toolstate/issues" + TOOLSTATE_PUBLISH: 1 + CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZOMUQATD5 + ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZH5AYXDVF + CACHE_DOMAIN: ci-caches-gha.rust-lang.org + if: "github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + strategy: + matrix: + include: + - name: dist-x86_64-linux + os: ubuntu-latest-xl + env: {} + - name: dist-x86_64-linux-alt + env: + IMAGE: dist-x86_64-linux + os: ubuntu-latest-xl + timeout-minutes: 600 + runs-on: "${{ matrix.os }}" + steps: + - name: disable git crlf conversion + run: git config --global core.autocrlf false + shell: bash + - name: checkout the source code + uses: actions/checkout@v1 + with: + fetch-depth: 2 + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" + - name: add extra environment variables + run: src/ci/scripts/setup-environment.sh + env: + EXTRA_VARIABLES: "${{ toJson(matrix.env) }}" + if: success() && !env.SKIP_JOB + - name: decide whether to skip this job + run: src/ci/scripts/should-skip-this.sh + if: success() && !env.SKIP_JOB + - name: collect CPU statistics + run: src/ci/scripts/collect-cpu-stats.sh + if: success() && !env.SKIP_JOB + - name: show the current environment + run: src/ci/scripts/dump-environment.sh + if: success() && !env.SKIP_JOB + - name: install awscli + run: src/ci/scripts/install-awscli.sh + if: success() && !env.SKIP_JOB + - name: install sccache + run: src/ci/scripts/install-sccache.sh + if: success() && !env.SKIP_JOB + - name: install clang + run: src/ci/scripts/install-clang.sh + if: success() && !env.SKIP_JOB + - name: install WIX + run: src/ci/scripts/install-wix.sh + if: success() && !env.SKIP_JOB + - name: install InnoSetup + run: src/ci/scripts/install-innosetup.sh + if: success() && !env.SKIP_JOB + - name: ensure the build happens on a partition with enough space + run: src/ci/scripts/symlink-build-dir.sh + if: success() && !env.SKIP_JOB + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + if: success() && !env.SKIP_JOB + - name: install MSYS2 + run: src/ci/scripts/install-msys2.sh + if: success() && !env.SKIP_JOB + - name: install MSYS2 packages + run: src/ci/scripts/install-msys2-packages.sh + if: success() && !env.SKIP_JOB + - name: install MinGW + run: src/ci/scripts/install-mingw.sh + if: success() && !env.SKIP_JOB + - name: install ninja + run: src/ci/scripts/install-ninja.sh + if: success() && !env.SKIP_JOB + - name: enable ipv6 on Docker + run: src/ci/scripts/enable-docker-ipv6.sh + if: success() && !env.SKIP_JOB + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + if: success() && !env.SKIP_JOB + - name: checkout submodules + run: src/ci/scripts/checkout-submodules.sh + if: success() && !env.SKIP_JOB + - name: ensure line endings are correct + run: src/ci/scripts/verify-line-endings.sh + if: success() && !env.SKIP_JOB + - name: run the build + run: src/ci/scripts/run-build-from-ci.sh + 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)] }}" + TOOLSTATE_REPO_ACCESS_TOKEN: "${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }}" + if: success() && !env.SKIP_JOB + - name: upload artifacts to S3 + run: src/ci/scripts/upload-artifacts.sh + env: + AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}" + if: "success() && !env.SKIP_JOB && (github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1')" + auto: + name: auto + env: + CI_JOB_NAME: "${{ matrix.name }}" + SCCACHE_BUCKET: rust-lang-gha-caches + DEPLOY_BUCKET: rust-lang-gha + TOOLSTATE_REPO: "https://github.com/pietroalbini/rust-toolstate" + TOOLSTATE_ISSUES_API_URL: "https://api.github.com/repos/pietroalbini/rust-toolstate/issues" + TOOLSTATE_PUBLISH: 1 + CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZOMUQATD5 + ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZH5AYXDVF + CACHE_DOMAIN: ci-caches-gha.rust-lang.org + if: "github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" + strategy: + matrix: + include: + - name: arm-android + os: ubuntu-latest-xl + env: {} + - name: armhf-gnu + os: ubuntu-latest-xl + env: {} + - name: dist-aarch64-linux + os: ubuntu-latest-xl + env: {} + - name: dist-android + os: ubuntu-latest-xl + env: {} + - name: dist-arm-linux + os: ubuntu-latest-xl + env: {} + - name: dist-armhf-linux + os: ubuntu-latest-xl + env: {} + - name: dist-armv7-linux + os: ubuntu-latest-xl + env: {} + - name: dist-i586-gnu-i586-i686-musl + os: ubuntu-latest-xl + env: {} + - name: dist-i686-freebsd + os: ubuntu-latest-xl + env: {} + - name: dist-i686-linux + os: ubuntu-latest-xl + env: {} + - name: dist-mips-linux + os: ubuntu-latest-xl + env: {} + - name: dist-mips64-linux + os: ubuntu-latest-xl + env: {} + - name: dist-mips64el-linux + os: ubuntu-latest-xl + env: {} + - name: dist-mipsel-linux + os: ubuntu-latest-xl + env: {} + - name: dist-powerpc-linux + os: ubuntu-latest-xl + env: {} + - name: dist-powerpc64-linux + os: ubuntu-latest-xl + env: {} + - name: dist-powerpc64le-linux + os: ubuntu-latest-xl + env: {} + - name: dist-s390x-linux + os: ubuntu-latest-xl + env: {} + - name: dist-various-1 + os: ubuntu-latest-xl + env: {} + - name: dist-various-2 + os: ubuntu-latest-xl + env: {} + - name: dist-x86_64-freebsd + os: ubuntu-latest-xl + env: {} + - name: dist-x86_64-linux + os: ubuntu-latest-xl + env: {} + - name: dist-x86_64-linux-alt + env: + IMAGE: dist-x86_64-linux + os: ubuntu-latest-xl + - name: dist-x86_64-musl + os: ubuntu-latest-xl + env: {} + - name: dist-x86_64-netbsd + os: ubuntu-latest-xl + env: {} + - name: i686-gnu + os: ubuntu-latest-xl + env: {} + - name: i686-gnu-nopt + os: ubuntu-latest-xl + env: {} + - name: mingw-check + os: ubuntu-latest-xl + env: {} + - name: test-various + os: ubuntu-latest-xl + env: {} + - name: wasm32 + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-aux + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-debug + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-distcheck + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-full-bootstrap + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-llvm-8 + env: + RUST_BACKTRACE: 1 + os: ubuntu-latest-xl + - name: x86_64-gnu-nopt + os: ubuntu-latest-xl + env: {} + - name: x86_64-gnu-tools + env: + DEPLOY_TOOLSTATES_JSON: toolstates-linux.json + os: ubuntu-latest-xl + - name: dist-x86_64-apple + env: + SCRIPT: "./x.py dist" + RUST_CONFIGURE_ARGS: "--target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc" + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + os: macos-latest + - name: dist-x86_64-apple-alt + env: + SCRIPT: "./x.py dist" + RUST_CONFIGURE_ARGS: "--enable-extended --enable-profiler --set rust.jemalloc" + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + os: macos-latest + - name: x86_64-apple + env: + SCRIPT: "./x.py test" + RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc" + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.8 + MACOSX_STD_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + os: macos-latest + - name: x86_64-msvc-1 + env: + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler" + SCRIPT: make ci-subset-1 + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + os: windows-latest-xl + - name: x86_64-msvc-2 + env: + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler" + SCRIPT: make ci-subset-2 + os: windows-latest-xl + - name: i686-msvc-1 + env: + RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc" + SCRIPT: make ci-subset-1 + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + os: windows-latest-xl + - name: i686-msvc-2 + env: + RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc" + SCRIPT: make ci-subset-2 + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + os: windows-latest-xl + - name: x86_64-msvc-aux + env: + RUST_CHECK_TARGET: check-aux EXCLUDE_CARGO=1 + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc" + os: windows-latest-xl + - name: x86_64-msvc-cargo + env: + SCRIPT: python x.py test src/tools/cargotest src/tools/cargo + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc" + VCVARS_BAT: vcvars64.bat + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + os: windows-latest-xl + - name: x86_64-msvc-tools + env: + SCRIPT: src/ci/docker/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstate/toolstates.json" + os: windows-latest-xl + - name: i686-mingw-1 + env: + RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu" + SCRIPT: make ci-mingw-subset-1 + CUSTOM_MINGW: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + os: windows-latest-xl + - name: i686-mingw-2 + env: + RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu" + SCRIPT: make ci-mingw-subset-2 + CUSTOM_MINGW: 1 + os: windows-latest-xl + - name: x86_64-mingw-1 + env: + SCRIPT: make ci-mingw-subset-1 + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu" + CUSTOM_MINGW: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + os: windows-latest-xl + - name: x86_64-mingw-2 + env: + SCRIPT: make ci-mingw-subset-2 + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu" + CUSTOM_MINGW: 1 + os: windows-latest-xl + - name: dist-x86_64-msvc + env: + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc --enable-full-tools --enable-profiler" + SCRIPT: python x.py dist + DIST_REQUIRE_ALL_TOOLS: 1 + os: windows-latest-xl + - name: dist-i686-msvc + env: + RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc --target=i586-pc-windows-msvc --enable-full-tools --enable-profiler" + SCRIPT: python x.py dist + DIST_REQUIRE_ALL_TOOLS: 1 + os: windows-latest-xl + - name: dist-i686-mingw + env: + RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu --enable-full-tools --enable-profiler" + SCRIPT: python x.py dist + CUSTOM_MINGW: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + os: windows-latest-xl + - name: dist-x86_64-mingw + env: + SCRIPT: python x.py dist + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler" + CUSTOM_MINGW: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + os: windows-latest-xl + - name: dist-x86_64-msvc-alt + env: + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-extended --enable-profiler" + SCRIPT: python x.py dist + os: windows-latest-xl + timeout-minutes: 600 + runs-on: "${{ matrix.os }}" + steps: + - name: disable git crlf conversion + run: git config --global core.autocrlf false + shell: bash + - name: checkout the source code + uses: actions/checkout@v1 + with: + fetch-depth: 2 + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" + - name: add extra environment variables + run: src/ci/scripts/setup-environment.sh + env: + EXTRA_VARIABLES: "${{ toJson(matrix.env) }}" + if: success() && !env.SKIP_JOB + - name: decide whether to skip this job + run: src/ci/scripts/should-skip-this.sh + if: success() && !env.SKIP_JOB + - name: collect CPU statistics + run: src/ci/scripts/collect-cpu-stats.sh + if: success() && !env.SKIP_JOB + - name: show the current environment + run: src/ci/scripts/dump-environment.sh + if: success() && !env.SKIP_JOB + - name: install awscli + run: src/ci/scripts/install-awscli.sh + if: success() && !env.SKIP_JOB + - name: install sccache + run: src/ci/scripts/install-sccache.sh + if: success() && !env.SKIP_JOB + - name: install clang + run: src/ci/scripts/install-clang.sh + if: success() && !env.SKIP_JOB + - name: install WIX + run: src/ci/scripts/install-wix.sh + if: success() && !env.SKIP_JOB + - name: install InnoSetup + run: src/ci/scripts/install-innosetup.sh + if: success() && !env.SKIP_JOB + - name: ensure the build happens on a partition with enough space + run: src/ci/scripts/symlink-build-dir.sh + if: success() && !env.SKIP_JOB + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + if: success() && !env.SKIP_JOB + - name: install MSYS2 + run: src/ci/scripts/install-msys2.sh + if: success() && !env.SKIP_JOB + - name: install MSYS2 packages + run: src/ci/scripts/install-msys2-packages.sh + if: success() && !env.SKIP_JOB + - name: install MinGW + run: src/ci/scripts/install-mingw.sh + if: success() && !env.SKIP_JOB + - name: install ninja + run: src/ci/scripts/install-ninja.sh + if: success() && !env.SKIP_JOB + - name: enable ipv6 on Docker + run: src/ci/scripts/enable-docker-ipv6.sh + if: success() && !env.SKIP_JOB + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + if: success() && !env.SKIP_JOB + - name: checkout submodules + run: src/ci/scripts/checkout-submodules.sh + if: success() && !env.SKIP_JOB + - name: ensure line endings are correct + run: src/ci/scripts/verify-line-endings.sh + if: success() && !env.SKIP_JOB + - name: run the build + run: src/ci/scripts/run-build-from-ci.sh + 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)] }}" + TOOLSTATE_REPO_ACCESS_TOKEN: "${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }}" + if: success() && !env.SKIP_JOB + - name: upload artifacts to S3 + run: src/ci/scripts/upload-artifacts.sh + env: + AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}" + if: "success() && !env.SKIP_JOB && (github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1')" + master: + name: master + runs-on: ubuntu-latest + env: + SCCACHE_BUCKET: rust-lang-gha-caches + DEPLOY_BUCKET: rust-lang-gha + TOOLSTATE_REPO: "https://github.com/pietroalbini/rust-toolstate" + TOOLSTATE_ISSUES_API_URL: "https://api.github.com/repos/pietroalbini/rust-toolstate/issues" + TOOLSTATE_PUBLISH: 1 + CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZOMUQATD5 + ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZH5AYXDVF + CACHE_DOMAIN: ci-caches-gha.rust-lang.org + if: "github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust'" + steps: + - name: checkout the source code + uses: actions/checkout@v1 + with: + fetch-depth: 2 + - name: publish toolstate + run: src/ci/publish_toolstate.sh + env: + TOOLSTATE_REPO_ACCESS_TOKEN: "${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }}" + if: success() && !env.SKIP_JOB + try-success: + needs: + - try + if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + steps: + - name: mark the job as a success + run: exit 0 + shell: bash + name: bors build finished + runs-on: ubuntu-latest + try-failure: + needs: + - try + if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + steps: + - name: mark the job as a failure + run: exit 1 + shell: bash + name: bors build finished + runs-on: ubuntu-latest + auto-success: + needs: + - auto + if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" + steps: + - name: mark the job as a success + run: exit 0 + shell: bash + name: bors build finished + runs-on: ubuntu-latest + auto-failure: + needs: + - auto + if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" + steps: + - name: mark the job as a failure + run: exit 1 + shell: bash + name: bors build finished + runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index d9761ce40927c..856ff7dbb0f33 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ __pycache__/ /obj/ /rustllvm/ /unicode-downloads -/target/ +/target # Generated by compiletest for incremental: /tmp/ tags diff --git a/.gitmodules b/.gitmodules index 003e50d0788e4..a9210cfc69ec5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,9 +16,6 @@ [submodule "src/tools/rls"] path = src/tools/rls url = https://github.com/rust-lang/rls.git -[submodule "src/tools/clippy"] - path = src/tools/clippy - url = https://github.com/rust-lang/rust-clippy.git [submodule "src/tools/rustfmt"] path = src/tools/rustfmt url = https://github.com/rust-lang/rustfmt.git @@ -31,16 +28,16 @@ [submodule "src/stdarch"] path = src/stdarch url = https://github.com/rust-lang/stdarch.git -[submodule "src/doc/rustc-guide"] - path = src/doc/rustc-guide - url = https://github.com/rust-lang/rustc-guide.git +[submodule "src/doc/rustc-dev-guide"] + path = src/doc/rustc-dev-guide + url = https://github.com/rust-lang/rustc-dev-guide.git [submodule "src/doc/edition-guide"] path = src/doc/edition-guide url = https://github.com/rust-lang/edition-guide.git [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/9.0-2019-12-19 + branch = rustc/10.0-2020-05-05 [submodule "src/doc/embedded-book"] path = src/doc/embedded-book url = https://github.com/rust-embedded/book.git diff --git a/.mailmap b/.mailmap index 78c3c3019af50..680aa04078f97 100644 --- a/.mailmap +++ b/.mailmap @@ -49,6 +49,7 @@ Carol (Nichols || Goulding) <193874+carols10cents@user Carol (Nichols || Goulding) Carol (Nichols || Goulding) Carol Willing +Charles Lew CrLF0710 Chris C Cerami Chris C Cerami Chris Pressey Chris Thorn Chris Thorn @@ -133,7 +134,7 @@ João Oliveira joaoxsouls Johann Hofmann Johann John Clements John Hodge John Hodge -John Kåre Alsaker +John Kåre Alsaker John Talling Jonathan Bailey Jonathan S Jonathan S @@ -153,7 +154,7 @@ Laurențiu Nicola Lee Jeffery Lee Jeffery Lee Wondong Lennart Kudling -Léo Testard +Léo Testard Lindsey Kuper Lindsey Kuper Luke Metz diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2843944b2e181..9c4afcefa1f22 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -188,11 +188,73 @@ with one another are rolled up. Speaking of tests, Rust has a comprehensive test suite. More information about it can be found [here][rctd]. -### External Dependencies +### External Dependencies (subtree) + +As a developer to this repository, you don't have to treat the following external projects +differently from other crates that are directly in this repo: + +* Clippy + +They are just regular files and directories. This is in contrast to `submodule` dependencies +(see below for those). Only tool authors will actually use any operations here. + +#### Synchronizing a subtree + +There are two synchronization directions: `subtree push` and `subtree pull`. + +``` +git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust +``` + +takes all the changes that +happened to the copy in this repo and creates commits on the remote repo that match the local +changes. Every local commit that touched the subtree causes a commit on the remote repo, but is +modified to move the files from the specified directory to the tool repo root. + +Make sure to not pick the `master` branch on the tool repo, so you can open a normal PR to the tool +to merge that subrepo push. + +``` +git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master +``` + +takes all changes since the last `subtree pull` from the tool repo +repo and adds these commits to the rustc repo + a merge commit that moves the tool changes into +the specified directory in the rust repository. + +It is recommended that you always do a push first and get that merged to the tool master branch. +Then, when you do a pull, the merge works without conflicts. +While it's definitely possible to resolve conflicts during a pull, you may have to redo the conflict +resolution if your PR doesn't get merged fast enough and there are new conflicts. Do not try to +rebase the result of a `git subtree pull`, rebasing merge commits is a bad idea in general. + +You always need to specify the `-P` prefix to the subtree directory and the corresponding remote +repository. If you specify the wrong directory or repository +you'll get very fun merges that try to push the wrong directory to the wrong remote repository. +Luckily you can just abort this without any consequences by throwing away either the pulled commits +in rustc or the pushed branch on the remote and try again. It is usually fairly obvious +that this is happening because you suddenly get thousands of commits that want to be synchronized. + +#### Creating a new subtree dependency + +If you want to create a new subtree dependency from an existing repository, call (from this +repository's root directory!) + +``` +git subtree add -P src/tools/clippy https://github.com/rust-lang/rust-clippy.git master +``` + +This will create a new commit, which you may not rebase under any circumstances! Delete the commit +and redo the operation if you need to rebase. + +Now you're done, the `src/tools/clippy` directory behaves as if Clippy were part of the rustc +monorepo, so no one but you (or others that synchronize subtrees) actually needs to use `git subtree`. + + +### External Dependencies (submodules) Currently building Rust will also build the following external projects: -* [clippy](https://github.com/rust-lang/rust-clippy) * [miri](https://github.com/rust-lang/miri) * [rustfmt](https://github.com/rust-lang/rustfmt) * [rls](https://github.com/rust-lang/rls/) @@ -221,7 +283,6 @@ before the PR is merged. Rust's build system builds a number of tools that make use of the internals of the compiler. This includes -[Clippy](https://github.com/rust-lang/rust-clippy), [RLS](https://github.com/rust-lang/rls) and [rustfmt](https://github.com/rust-lang/rustfmt). If these tools break because of your changes, you may run into a sort of "chicken and egg" @@ -331,10 +392,18 @@ You can find documentation style guidelines in [RFC 1574][rfc1574]. [rfc1574]: https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md#appendix-a-full-conventions-text -In many cases, you don't need a full `./x.py doc`. You can use `rustdoc` directly -to check small fixes. For example, `rustdoc src/doc/reference.md` will render -reference to `doc/reference.html`. The CSS might be messed up, but you can -verify that the HTML is right. +In many cases, you don't need a full `./x.py doc`, which will build the entire +stage 2 compiler and compile the various books published on +[doc.rust-lang.org]. When updating documentation for the standard library, +first try `./x.py doc --stage 0 src/libstd`. If that fails, or if you need to +see the output from the latest version of `rustdoc`, use `--stage 1` instead of +`--stage 0`. Results should appear in `build/$TARGET/crate-docs`. + +[doc.rust-lang.org]: htts://doc.rust-lang.org + +You can also use `rustdoc` directly to check small fixes. For example, +`rustdoc src/doc/reference.md` will render reference to `doc/reference.html`. +The CSS might be messed up, but you can verify that the HTML is right. Additionally, contributions to the [rustc-dev-guide] are always welcome. Contributions can be made directly at [the @@ -449,7 +518,7 @@ are: * Don't be afraid to ask! The Rust community is friendly and helpful. [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/about-this-guide.html -[gdfrustc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ +[gdfrustc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ [gsearchdocs]: https://www.google.com/search?q=site:doc.rust-lang.org+your+query+here [rif]: http://internals.rust-lang.org [rr]: https://doc.rust-lang.org/book/README.html diff --git a/Cargo.lock b/Cargo.lock index 22a06151353ba..d81fd6e8d3afd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,7 +74,7 @@ name = "arena" version = "0.0.0" dependencies = [ "rustc_data_structures", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -87,12 +87,6 @@ dependencies = [ "scoped_threadpool", ] -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" - [[package]] name = "arrayvec" version = "0.4.7" @@ -119,11 +113,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "backtrace" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" +checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" dependencies = [ "backtrace-sys", "cfg-if", @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.34" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" +checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" dependencies = [ "cc", "compiler_builtins", @@ -181,11 +181,22 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.3.3" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "arrayref", "byte-tools", ] @@ -234,9 +245,9 @@ version = "0.1.0" [[package]] name = "byte-tools" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytecount" @@ -281,7 +292,7 @@ dependencies = [ [[package]] name = "cargo" -version = "0.45.0" +version = "0.46.0" dependencies = [ "anyhow", "atty", @@ -292,14 +303,13 @@ dependencies = [ "clap", "core-foundation 0.7.0", "crates-io", - "crossbeam-utils 0.7.0", + "crossbeam-utils 0.7.2", "crypto-hash", "curl", "curl-sys", "env_logger 0.7.1", "filetime", "flate2", - "fs2", "fwdansi", "git2", "git2-curl", @@ -324,7 +334,7 @@ dependencies = [ "pretty_env_logger", "remove_dir_all", "rustc-workspace-hack", - "rustfix 0.5.0", + "rustfix", "same-file", "semver", "serde", @@ -402,9 +412,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" dependencies = [ "jobserver", ] @@ -419,6 +429,77 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "chalk-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4620afad4d4d9e63f915cfa10c930b7a3c9c3ca5cd88dd771ff8e5bf04ea10" +dependencies = [ + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.11", + "synstructure", +] + +[[package]] +name = "chalk-engine" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca6e5cef10197789da0b4ec310eda58da4c55530613b2323432642a97372735" +dependencies = [ + "chalk-macros", + "rustc-hash", +] + +[[package]] +name = "chalk-ir" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d45df5fb6328527f976e8a32c9e1c9970084d937ebe93d0d34f5bbf4231cb956" +dependencies = [ + "chalk-derive", + "chalk-engine", + "chalk-macros", +] + +[[package]] +name = "chalk-macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e4782d108e420a1fcf94d8a919cf248db33c5071678e87d9c2d4f20ed1feb32" +dependencies = [ + "lazy_static 1.4.0", +] + +[[package]] +name = "chalk-rust-ir" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ec96dbe0ab5fdbadfca4179ec2e1d35f0439c3b53a74988b1aec239c63eb08" +dependencies = [ + "chalk-derive", + "chalk-engine", + "chalk-ir", + "chalk-macros", +] + +[[package]] +name = "chalk-solve" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb99fa9530f0e101475fb60adc931f51bdea05b4642a48928b814d7f0141a6b" +dependencies = [ + "chalk-derive", + "chalk-engine", + "chalk-ir", + "chalk-macros", + "chalk-rust-ir", + "ena 0.13.1", + "itertools 0.9.0", + "petgraph", + "rustc-hash", +] + [[package]] name = "chrono" version = "0.4.6" @@ -443,7 +524,7 @@ dependencies = [ "textwrap", "unicode-width", "vec_map", - "yaml-rust", + "yaml-rust 0.3.5", ] [[package]] @@ -456,7 +537,6 @@ dependencies = [ "compiletest_rs", "derive-new", "lazy_static 1.4.0", - "regex", "rustc-workspace-hack", "rustc_tools_util 0.2.0", "semver", @@ -477,13 +557,12 @@ dependencies = [ "if_chain", "itertools 0.9.0", "lazy_static 1.4.0", - "matches", - "pulldown-cmark 0.7.0", + "pulldown-cmark 0.7.1", "quine-mc_cluskey", "regex-syntax", "semver", "serde", - "smallvec 1.0.0", + "smallvec 1.4.0", "toml", "unicode-normalization", "url 2.1.0", @@ -556,9 +635,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.25" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "438ac08ddc5efe81452f984a9e33ba425b00b31d1f48e6acd9e2210aa28cc52e" +checksum = "439a6fab343b1dab347823537734a5cd4ae6ae2000b465ab886f64cdb723bd14" dependencies = [ "cc", "rustc-std-workspace-core", @@ -576,7 +655,7 @@ dependencies = [ "log", "miow 0.3.3", "regex", - "rustfix 0.5.0", + "rustfix", "serde", "serde_json", "walkdir", @@ -585,9 +664,9 @@ dependencies = [ [[package]] name = "compiletest_rs" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b678957210a00ba0fbeacc23d38cbfbf29895564da1616564634351e1dac5e" +checksum = "9f737835bfbbe29ed1ff82d5137520338d7ed5bf1a1d4b9c1c7c58bb45b8fa29" dependencies = [ "diff", "filetime", @@ -596,7 +675,7 @@ dependencies = [ "log", "miow 0.3.3", "regex", - "rustfix 0.4.6", + "rustfix", "serde", "serde_derive", "serde_json", @@ -680,7 +759,7 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" [[package]] name = "crates-io" -version = "0.31.0" +version = "0.31.1" dependencies = [ "anyhow", "curl", @@ -706,7 +785,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" dependencies = [ - "crossbeam-utils 0.7.0", + "crossbeam-utils 0.7.2", ] [[package]] @@ -754,11 +833,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cfg-if", "lazy_static 1.4.0", ] @@ -775,6 +854,16 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "ctor" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1" +dependencies = [ + "quote 1.0.2", + "syn 1.0.11", +] + [[package]] name = "curl" version = "0.4.25" @@ -848,13 +937,13 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "derive-new" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ca414e896ae072546f4d789f452daaecf60ddee4c9df5dc6d5936d769e3d87c" +checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.11", ] [[package]] @@ -882,9 +971,9 @@ checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" [[package]] name = "digest" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ "generic-array", ] @@ -923,9 +1012,9 @@ dependencies = [ [[package]] name = "dlmalloc" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f283302e035e61c23f2b86b3093e8c6273a4c3125742d6087e96ade001ca5e63" +checksum = "35055b1021724f4eb5262eb49130eebff23fc59fc5a14160e05faad8eeb36673" dependencies = [ "compiler_builtins", "libc", @@ -974,6 +1063,15 @@ dependencies = [ "log", ] +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + [[package]] name = "encoding_rs" version = "0.8.17" @@ -1026,6 +1124,14 @@ dependencies = [ "walkdir", ] +[[package]] +name = "expand-yaml-anchors" +version = "0.1.0" +dependencies = [ + "yaml-merge-keys", + "yaml-rust 0.4.3", +] + [[package]] name = "failure" version = "0.1.5" @@ -1038,14 +1144,14 @@ dependencies = [ [[package]] name = "failure_derive" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", - "synstructure 0.10.2", + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.11", + "synstructure", ] [[package]] @@ -1056,9 +1162,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "filetime" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" +checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e" dependencies = [ "cfg-if", "libc", @@ -1066,6 +1172,12 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" + [[package]] name = "flate2" version = "1.0.12" @@ -1118,16 +1230,6 @@ dependencies = [ "rustc-std-workspace-core", ] -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi 0.3.8", -] - [[package]] name = "fs_extra" version = "1.1.0" @@ -1203,9 +1305,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.9.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ "typenum", ] @@ -1234,9 +1336,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.13.0" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7da16ceafe24cedd9ba02c4463a2b506b6493baf4317c79c5acb553134a3c15" +checksum = "e1e02a51cd90229028c9bd8be0a0364f85b6b3199cccaa0ef39005ddbd5ac165" dependencies = [ "bitflags", "libc", @@ -1302,37 +1404,25 @@ dependencies = [ [[package]] name = "handlebars" -version = "2.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df044dd42cdb7e32f28557b661406fc0f2494be75199779998810dbc35030e0d" +checksum = "ba758d094d31274eb49d15da6f326b96bf3185239a6359bf684f3d5321148900" dependencies = [ - "hashbrown 0.5.0", - "lazy_static 1.4.0", "log", "pest", "pest_derive", "quick-error", - "regex", "serde", "serde_json", ] -[[package]] -name = "hashbrown" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" -dependencies = [ - "serde", -] - [[package]] name = "hashbrown" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cd9867f119b19fecb08cd5c326ad4488d7a1da4bf75b4d95d71db742525aaab" dependencies = [ - "autocfg", + "autocfg 0.1.7", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -1349,9 +1439,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.1" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22b8f315b98f415780ddbe9163c7dbbc5a07225b6d102ace1d8aeef85775140" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "compiler_builtins", "libc", @@ -1535,9 +1625,9 @@ dependencies = [ [[package]] name = "im-rc" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9ad726dce25993be6352b0bff048e4d2647440c0a673d32257c4fac49356d18" +checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f" dependencies = [ "bitmaps", "rand_core 0.5.1", @@ -1719,7 +1809,7 @@ checksum = "5b31c9b90731276fdd24d896f31bb10aecf2e5151733364ae81123186643d939" dependencies = [ "jsonrpc-core", "log", - "parking_lot 0.10.0", + "parking_lot 0.10.2", "serde", ] @@ -1769,18 +1859,18 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" dependencies = [ "rustc-std-workspace-core", ] [[package]] name = "libgit2-sys" -version = "0.12.0+0.99.0" +version = "0.12.5+1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05dff41ac39e7b653f5f1550886cf00ba52f8e7f57210b633cdeedb3de5b236c" +checksum = "3eadeec65514971355bf7134967a543f71372f35b53ac6c7143e7bd157f07535" dependencies = [ "cc", "libc", @@ -1830,11 +1920,17 @@ dependencies = [ name = "linkchecker" version = "0.1.0" +[[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" + [[package]] name = "lock_api" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ "scopeguard", ] @@ -1933,11 +2029,22 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "md-5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18af3dcaf2b0219366cdb4e2af65a6101457b415c3d1a5c71dd9c2b7c77b9c8" +dependencies = [ + "block-buffer", + "digest", + "opaque-debug", +] + [[package]] name = "mdbook" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031bdd9d4893c983e2f69ebc4b59070feee8276a584c4aabdcb351235ea28016" +checksum = "e7ec525f7ebccc2dd935c263717250cd37f9a4b264a77c5dbc950ea2734d8159" dependencies = [ "ammonia", "chrono", @@ -2221,6 +2328,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6a04cb71e910d0034815600180f62a95bf6e67942d7ab52a166a68c7d7e9cd0" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "open" version = "1.2.1" @@ -2258,20 +2371,20 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-src" -version = "111.6.1+1.1.1d" +version = "111.9.0+1.1.1g" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91b04cb43c1a8a90e934e0cd612e2a5715d976d2d6cff4490278a0cddf35005" +checksum = "a2dbe10ddd1eb335aba3780eb2eaa13e1b7b441d2562fd962398740927f39ec4" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.53" +version = "0.9.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" +checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cc", "libc", "openssl-src", @@ -2279,12 +2392,27 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordermap" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" + [[package]] name = "ordslice" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd20eec3dbe4376829cb7d80ae6ac45e0a766831dca50202ff2d40db46a8a024" +[[package]] +name = "output_vt100" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "packed_simd" version = "0.3.1" @@ -2346,12 +2474,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ "lock_api", - "parking_lot_core 0.7.0", + "parking_lot_core 0.7.1", ] [[package]] @@ -2371,15 +2499,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +checksum = "0e136c1904604defe99ce5fd71a28d473fa60a12255d511aa78a9ddf11237aeb" dependencies = [ "cfg-if", "cloudabi", "libc", "redox_syscall", - "smallvec 1.0.0", + "smallvec 1.4.0", "winapi 0.3.8", ] @@ -2416,28 +2544,38 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.11", ] [[package]] name = "pest_meta" -version = "2.1.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ "maplit", "pest", "sha-1", ] +[[package]] +name = "petgraph" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" +dependencies = [ + "fixedbitset", + "ordermap", +] + [[package]] name = "phf" version = "0.7.24" @@ -2484,9 +2622,9 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "polonius-engine" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d8ef65e3f89ecaec9ca7cb0e0911b4617352d4494018bcf934992f03f2024c" +checksum = "ef2558a4b464e185b36ee08a2937ebb62ea5464c38856cfb1465c97cb38db52d" dependencies = [ "datafrog", "log", @@ -2507,12 +2645,14 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" +checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" dependencies = [ "ansi_term", + "ctor", "difference", + "output_vt100", ] [[package]] @@ -2579,6 +2719,15 @@ dependencies = [ "core", ] +[[package]] +name = "psm" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "659ecfea2142a458893bb7673134bad50b752fea932349c213d6a23874ce3aa7" +dependencies = [ + "cc", +] + [[package]] name = "publicsuffix" version = "1.5.3" @@ -2606,9 +2755,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c2d7fd131800e0d63df52aff46201acaab70b431a4a1ec6f0343fe8e64f35a4" +checksum = "3e142c3b8f49d2200605ee6ba0b1d757310e9e7a72afe78c36ee2ef67300ee00" dependencies = [ "bitflags", "memchr", @@ -2623,9 +2772,9 @@ checksum = "6ddd112cca70a4d30883b2d21568a1d376ff8be4758649f64f973c6845128ad3" [[package]] name = "quick-error" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quine-mc_cluskey" @@ -2653,9 +2802,9 @@ dependencies = [ [[package]] name = "racer" -version = "2.1.31" +version = "2.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ff33fa15ac0384376741d16ddb05a65263dde4e2c5d0f7a9f3747db788764aa" +checksum = "54322b696f7df20e0d79d0244a1088f387b7164a5f17987c4ab984dec1a23e42" dependencies = [ "bitflags", "clap", @@ -2665,13 +2814,13 @@ dependencies = [ "lazy_static 1.4.0", "log", "rls-span", + "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_parse", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "rustc-ap-syntax", ] [[package]] @@ -3093,72 +3242,76 @@ dependencies = [ ] [[package]] -name = "rustc" -version = "0.0.0" +name = "rustc-ap-arena" +version = "654.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfcfbb0ddfd533abf8c076e3b49d1e5042d1962526a12ce2c66d514b24cca3" dependencies = [ - "arena", - "backtrace", - "bitflags", - "byteorder", - "jobserver", - "log", - "measureme", - "parking_lot 0.9.0", - "polonius-engine", - "rustc-rayon", - "rustc-rayon-core", - "rustc_apfloat", - "rustc_ast", - "rustc_attr", - "rustc_data_structures", - "rustc_errors", - "rustc_feature", - "rustc_hir", - "rustc_index", - "rustc_macros", - "rustc_query_system", - "rustc_session", - "rustc_span", - "rustc_target", - "scoped-tls", - "serialize", - "smallvec 1.0.0", + "rustc-ap-rustc_data_structures", + "smallvec 1.4.0", ] [[package]] -name = "rustc-ap-arena" -version = "642.0.0" +name = "rustc-ap-graphviz" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea82fa3d9a8add7422228ca1a2cbba0784fa8861f56148ff64da08b3c7921b03" +checksum = "7490bb07b014a7f9531bde33c905a805e08095dbefdb4c9988a1b19fe6d019fd" + +[[package]] +name = "rustc-ap-rustc_ast" +version = "654.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "189f16dbb8dd11089274c9ced58b0cae9e1ea3e434a58f3db683817eda849e58" dependencies = [ + "log", "rustc-ap-rustc_data_structures", - "smallvec 1.0.0", + "rustc-ap-rustc_index", + "rustc-ap-rustc_lexer", + "rustc-ap-rustc_macros", + "rustc-ap-rustc_span", + "rustc-ap-serialize", + "scoped-tls", + "smallvec 1.4.0", ] [[package]] -name = "rustc-ap-graphviz" -version = "642.0.0" +name = "rustc-ap-rustc_ast_passes" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "638d0b2b3bcf99824e0cb5a25dbc547b61dc20942e11daf6a97e981918aa18e5" +checksum = "bbe619609b56a617fa986332b066d53270093c816d8ff8281fc90e1dbe74c1cc" +dependencies = [ + "itertools 0.8.0", + "log", + "rustc-ap-rustc_ast", + "rustc-ap-rustc_ast_pretty", + "rustc-ap-rustc_attr", + "rustc-ap-rustc_data_structures", + "rustc-ap-rustc_errors", + "rustc-ap-rustc_feature", + "rustc-ap-rustc_parse", + "rustc-ap-rustc_session", + "rustc-ap-rustc_span", +] [[package]] name = "rustc-ap-rustc_ast_pretty" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38bab04dd676dee6d2f9670506a18c31bfce38bf7f8420aa83eb1140ecde049" +checksum = "26ab1495f7b420e937688749c1da5763aaabd6ebe8cacb758665a0b8481da094" dependencies = [ "log", + "rustc-ap-rustc_ast", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_span", - "rustc-ap-syntax", ] [[package]] name = "rustc-ap-rustc_attr" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b843ba8b1ed43739133047673b9f6a54d3b3b4d328d69c6ea89ff971395f35" +checksum = "2e057495724c60729c1d1d9d49374e0b3ebd6d3481cd161b2871f52fe017b7b5" dependencies = [ + "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", @@ -3167,42 +3320,42 @@ dependencies = [ "rustc-ap-rustc_session", "rustc-ap-rustc_span", "rustc-ap-serialize", - "rustc-ap-syntax", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "rustc-ap-rustc_data_structures" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3d1c6d0a80ab0c1df76405377cec0f3d5423fb5b0953a8eac70a2ad6c44df2" +checksum = "d2130997667833692f4bec4681d0e73b066d5a01dac1d8a68f22068b82bf173a" dependencies = [ "bitflags", "cfg-if", - "crossbeam-utils 0.6.5", - "ena", + "crossbeam-utils 0.7.2", + "ena 0.13.1", "indexmap", "jobserver", "lazy_static 1.4.0", + "libc", "log", "measureme", - "parking_lot 0.9.0", + "parking_lot 0.10.2", "rustc-ap-graphviz", "rustc-ap-rustc_index", "rustc-ap-serialize", "rustc-hash", "rustc-rayon", "rustc-rayon-core", - "smallvec 1.0.0", + "smallvec 1.4.0", "stable_deref_trait", "winapi 0.3.8", ] [[package]] name = "rustc-ap-rustc_errors" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4909a1eca29331332257230f29120a8ff68c9e37d868c564fcd599e430cf8914" +checksum = "908e1ea187c6bb368af4ba6db980001e920515e67371ddc4086e749baabe6080" dependencies = [ "annotate-snippets", "atty", @@ -3216,11 +3369,33 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "rustc-ap-rustc_expand" +version = "654.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50066a75bca872ff933b0ee8a582d18ef1876c8054a392f60c39e538446bfb00" +dependencies = [ + "log", + "rustc-ap-rustc_ast", + "rustc-ap-rustc_ast_passes", + "rustc-ap-rustc_ast_pretty", + "rustc-ap-rustc_attr", + "rustc-ap-rustc_data_structures", + "rustc-ap-rustc_errors", + "rustc-ap-rustc_feature", + "rustc-ap-rustc_lexer", + "rustc-ap-rustc_parse", + "rustc-ap-rustc_session", + "rustc-ap-rustc_span", + "rustc-ap-serialize", + "smallvec 1.4.0", +] + [[package]] name = "rustc-ap-rustc_feature" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ab887a181d795cf5fd3edadf367760deafb90aefb844f168ab5255266e3478" +checksum = "96fb53e1710e6de7c2e371ca56c857b79f9b399aba58aa6b6fbed6e2f677d3f6" dependencies = [ "lazy_static 1.4.0", "rustc-ap-rustc_data_structures", @@ -3229,71 +3404,71 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_fs_util" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70814116df3c5fbec8f06f6a1d013ca481f620fd22a9475754e9bf3ee9ba70d8" +checksum = "e3f91357e5e468fc2729211571d769723c728a34e200d90a70164e945f881e09" [[package]] name = "rustc-ap-rustc_index" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1bf1d3cf3d119d41353d6fd229ef7272d5097bc0924de021c0294bf86d48bf" +checksum = "32220c3e6cdf226f38e4474b747dca15f3106bb680c74f10b299af3f6cdb1663" dependencies = [ "rustc-ap-serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "rustc-ap-rustc_lexer" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cda21a32cebdc11ec4f5393aa2fcde5ed1b2f673a8571e5a4dcdf07e4ae9cac" +checksum = "3b324d2a2bacad344e53e182e5ca04ffb74745b932849aa074f8f7fec8177da5" dependencies = [ "unicode-xid 0.2.0", ] [[package]] name = "rustc-ap-rustc_macros" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c47b48ea51910ecfd853c9248a9bf4c767bc823449ab6a1d864dff65fbae16" +checksum = "59686c56d5f1b3ed47d0f070c257ed35caf24ecf2d744dd11fe44b1014baee0f" dependencies = [ - "itertools 0.8.0", "proc-macro2 1.0.3", "quote 1.0.2", "syn 1.0.11", - "synstructure 0.12.1", + "synstructure", ] [[package]] name = "rustc-ap-rustc_parse" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd88e89cd5b5d28dcd3a347a3d534c08627d9455570dc1a2d402cb8437b9d30" +checksum = "2dfb0c11c591ec5f87bbadb10819795abc9035ff79a26703c1b6c9487ac51f49" dependencies = [ "bitflags", "log", + "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", - "rustc-ap-rustc_attr", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", "rustc-ap-rustc_lexer", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "rustc-ap-syntax", - "smallvec 1.0.0", + "smallvec 1.4.0", "unicode-normalization", ] [[package]] name = "rustc-ap-rustc_session" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8487b4575fbb2d1fc6f1cd61225efd108a4d36817e6fb9b643d57fcae9cb12" +checksum = "3d1a194b1a81d7233ee492847638dc9ebdb7d084300e5ade8dea0ceaa98f95b9" dependencies = [ + "getopts", "log", "num_cpus", + "rustc-ap-rustc_ast", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", @@ -3302,31 +3477,32 @@ dependencies = [ "rustc-ap-rustc_span", "rustc-ap-rustc_target", "rustc-ap-serialize", - "rustc-ap-syntax", ] [[package]] name = "rustc-ap-rustc_span" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f69746c0d4c21bf20a5bb2bd247261a1aa8631f04202d7303352942dde70d987" +checksum = "a648146050fed6b58e681ec22488e728f60e16036bb7497c9815e3debd1e4242" dependencies = [ "cfg-if", "log", + "md-5", "rustc-ap-arena", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_index", "rustc-ap-rustc_macros", "rustc-ap-serialize", "scoped-tls", + "sha-1", "unicode-width", ] [[package]] name = "rustc-ap-rustc_target" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bbc6ae09b5d42ec66edd520e8412e0615c53a7c93607fe33dc4abab60ba7c8b" +checksum = "28cf28798f0988b808e3616713630e4098d68c6f1f41052a2f7e922e094da744" dependencies = [ "bitflags", "log", @@ -3339,29 +3515,12 @@ dependencies = [ [[package]] name = "rustc-ap-serialize" -version = "642.0.0" +version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13a1ead0252fc3d96da4c336a95950be6795f2b00c84a67ccadf26142f8cb41" +checksum = "756e8f526ec7906e132188bf25e3c10a6ee42ab77294ecb3b3602647f0508eef" dependencies = [ "indexmap", - "smallvec 1.0.0", -] - -[[package]] -name = "rustc-ap-syntax" -version = "642.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f59f48ca3a2ec16a7e82e718ed5aadf9c9e08cf63015d28b4e774767524a6a" -dependencies = [ - "log", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_index", - "rustc-ap-rustc_lexer", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_span", - "rustc-ap-serialize", - "scoped-tls", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3441,12 +3600,15 @@ dependencies = [ name = "rustc-workspace-hack" version = "1.0.0" dependencies = [ - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", + "proc-macro2 1.0.3", + "quote 1.0.2", "serde", "serde_json", "smallvec 0.6.10", - "smallvec 1.0.0", + "smallvec 1.4.0", "syn 0.15.35", + "syn 1.0.11", "url 2.1.0", "winapi 0.3.8", ] @@ -3456,13 +3618,14 @@ name = "rustc_apfloat" version = "0.0.0" dependencies = [ "bitflags", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "rustc_ast" version = "0.0.0" dependencies = [ + "bitflags", "log", "rustc_data_structures", "rustc_index", @@ -3471,7 +3634,7 @@ dependencies = [ "rustc_span", "scoped-tls", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3489,13 +3652,14 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "rustc_ast_passes" version = "0.0.0" dependencies = [ + "itertools 0.8.0", "log", "rustc_ast", "rustc_ast_pretty", @@ -3514,8 +3678,8 @@ version = "0.0.0" dependencies = [ "log", "rustc_ast", - "rustc_data_structures", "rustc_span", + "rustc_target", ] [[package]] @@ -3531,7 +3695,7 @@ dependencies = [ "rustc_session", "rustc_span", "serialize", - "smallvec 1.0.0", + "version_check", ] [[package]] @@ -3551,7 +3715,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3563,7 +3727,6 @@ dependencies = [ "libc", "log", "measureme", - "rustc", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3576,11 +3739,12 @@ dependencies = [ "rustc_incremental", "rustc_index", "rustc_llvm", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3594,7 +3758,6 @@ dependencies = [ "log", "memmap", "num_cpus", - "rustc", "rustc_apfloat", "rustc_ast", "rustc_attr", @@ -3604,7 +3767,7 @@ dependencies = [ "rustc_hir", "rustc_incremental", "rustc_index", - "rustc_metadata", + "rustc_middle", "rustc_session", "rustc_span", "rustc_symbol_mangling", @@ -3619,22 +3782,24 @@ version = "0.0.0" dependencies = [ "bitflags", "cfg-if", - "crossbeam-utils 0.6.5", - "ena", + "crossbeam-utils 0.7.2", + "ena 0.14.0", "graphviz", "indexmap", "jobserver", "lazy_static 1.4.0", + "libc", "log", "measureme", - "parking_lot 0.9.0", + "parking_lot 0.10.2", "rustc-hash", "rustc-rayon", "rustc-rayon-core", "rustc_index", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", "stable_deref_trait", + "stacker", "winapi 0.3.8", ] @@ -3644,8 +3809,8 @@ version = "0.0.0" dependencies = [ "env_logger 0.7.1", "lazy_static 1.4.0", + "libc", "log", - "rustc", "rustc_ast", "rustc_ast_pretty", "rustc_codegen_ssa", @@ -3654,9 +3819,11 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_hir", + "rustc_hir_pretty", "rustc_interface", "rustc_lint", "rustc_metadata", + "rustc_middle", "rustc_mir", "rustc_parse", "rustc_plugin_impl", @@ -3705,7 +3872,7 @@ dependencies = [ "rustc_session", "rustc_span", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3728,15 +3895,24 @@ dependencies = [ "lazy_static 1.4.0", "log", "rustc_ast", - "rustc_ast_pretty", "rustc_data_structures", - "rustc_errors", "rustc_index", "rustc_macros", "rustc_span", "rustc_target", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", +] + +[[package]] +name = "rustc_hir_pretty" +version = "0.0.0" +dependencies = [ + "rustc_ast", + "rustc_ast_pretty", + "rustc_hir", + "rustc_span", + "rustc_target", ] [[package]] @@ -3746,11 +3922,11 @@ dependencies = [ "graphviz", "log", "rand 0.7.3", - "rustc", "rustc_ast", "rustc_data_structures", "rustc_fs_util", "rustc_hir", + "rustc_middle", "rustc_session", "rustc_span", "serialize", @@ -3761,7 +3937,7 @@ name = "rustc_index" version = "0.0.0" dependencies = [ "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3770,26 +3946,27 @@ version = "0.0.0" dependencies = [ "graphviz", "log", - "rustc", "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_hir", "rustc_index", "rustc_macros", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "serialize", + "smallvec 1.4.0", ] [[package]] name = "rustc_interface" version = "0.0.0" dependencies = [ + "libc", "log", "once_cell", - "rustc", "rustc-rayon", "rustc_ast", "rustc_ast_lowering", @@ -3803,9 +3980,9 @@ dependencies = [ "rustc_expand", "rustc_hir", "rustc_incremental", - "rustc_infer", "rustc_lint", "rustc_metadata", + "rustc_middle", "rustc_mir", "rustc_mir_build", "rustc_parse", @@ -3822,7 +3999,7 @@ dependencies = [ "rustc_ty", "rustc_typeck", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", "tempfile", "winapi 0.3.8", ] @@ -3839,7 +4016,6 @@ name = "rustc_lint" version = "0.0.0" dependencies = [ "log", - "rustc", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -3848,7 +4024,7 @@ dependencies = [ "rustc_feature", "rustc_hir", "rustc_index", - "rustc_infer", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", @@ -3872,7 +4048,7 @@ dependencies = [ "proc-macro2 1.0.3", "quote 1.0.2", "syn 1.0.11", - "synstructure 0.12.1", + "synstructure", ] [[package]] @@ -3880,25 +4056,57 @@ name = "rustc_metadata" version = "0.0.0" dependencies = [ "flate2", + "libc", "log", "memmap", - "rustc", "rustc_ast", "rustc_attr", "rustc_data_structures", "rustc_errors", "rustc_expand", "rustc_hir", + "rustc_hir_pretty", "rustc_index", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", "stable_deref_trait", "winapi 0.3.8", ] +[[package]] +name = "rustc_middle" +version = "0.0.0" +dependencies = [ + "arena", + "bitflags", + "byteorder", + "chalk-ir", + "log", + "measureme", + "polonius-engine", + "rustc-rayon-core", + "rustc_apfloat", + "rustc_ast", + "rustc_attr", + "rustc_data_structures", + "rustc_errors", + "rustc_feature", + "rustc_hir", + "rustc_index", + "rustc_macros", + "rustc_query_system", + "rustc_session", + "rustc_span", + "rustc_target", + "scoped-tls", + "serialize", + "smallvec 1.4.0", +] + [[package]] name = "rustc_mir" version = "0.0.0" @@ -3909,10 +4117,8 @@ dependencies = [ "log", "log_settings", "polonius-engine", - "rustc", "rustc_apfloat", "rustc_ast", - "rustc_ast_pretty", "rustc_attr", "rustc_data_structures", "rustc_errors", @@ -3921,12 +4127,13 @@ dependencies = [ "rustc_infer", "rustc_lexer", "rustc_macros", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", "rustc_trait_selection", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3935,7 +4142,6 @@ version = "0.0.0" dependencies = [ "arena", "log", - "rustc", "rustc_apfloat", "rustc_ast", "rustc_attr", @@ -3944,13 +4150,13 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_infer", - "rustc_macros", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", "rustc_trait_selection", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3961,14 +4167,12 @@ dependencies = [ "log", "rustc_ast", "rustc_ast_pretty", - "rustc_attr", "rustc_data_structures", "rustc_errors", "rustc_feature", "rustc_lexer", "rustc_session", "rustc_span", - "smallvec 1.0.0", "unicode-normalization", ] @@ -3977,15 +4181,13 @@ name = "rustc_passes" version = "0.0.0" dependencies = [ "log", - "rustc", "rustc_ast", "rustc_attr", "rustc_data_structures", "rustc_errors", - "rustc_feature", "rustc_hir", "rustc_index", - "rustc_infer", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", @@ -3996,12 +4198,12 @@ dependencies = [ name = "rustc_plugin_impl" version = "0.0.0" dependencies = [ - "rustc", "rustc_ast", "rustc_errors", "rustc_hir", "rustc_lint", "rustc_metadata", + "rustc_middle", "rustc_session", "rustc_span", ] @@ -4011,12 +4213,11 @@ name = "rustc_privacy" version = "0.0.0" dependencies = [ "log", - "rustc", - "rustc_ast", "rustc_attr", "rustc_data_structures", "rustc_errors", "rustc_hir", + "rustc_middle", "rustc_session", "rustc_span", "rustc_typeck", @@ -4026,16 +4227,16 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ + "arena", "log", - "parking_lot 0.9.0", - "rustc_ast", + "parking_lot 0.10.2", + "rustc-rayon-core", "rustc_data_structures", "rustc_errors", - "rustc_hir", "rustc_index", - "rustc_macros", + "rustc_span", "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4045,7 +4246,6 @@ dependencies = [ "arena", "bitflags", "log", - "rustc", "rustc_ast", "rustc_ast_lowering", "rustc_ast_pretty", @@ -4056,9 +4256,10 @@ dependencies = [ "rustc_feature", "rustc_hir", "rustc_metadata", + "rustc_middle", "rustc_session", "rustc_span", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4068,11 +4269,12 @@ dependencies = [ "log", "rls-data", "rls-span", - "rustc", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", "rustc_hir", + "rustc_hir_pretty", + "rustc_middle", "rustc_parse", "rustc_session", "rustc_span", @@ -4083,6 +4285,7 @@ dependencies = [ name = "rustc_session" version = "0.0.0" dependencies = [ + "getopts", "log", "num_cpus", "rustc_ast", @@ -4090,7 +4293,6 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_fs_util", - "rustc_index", "rustc_span", "rustc_target", "serialize", @@ -4103,11 +4305,13 @@ dependencies = [ "arena", "cfg-if", "log", + "md-5", "rustc_data_structures", "rustc_index", "rustc_macros", "scoped-tls", "serialize", + "sha-1", "unicode-width", ] @@ -4117,12 +4321,11 @@ version = "0.0.0" dependencies = [ "log", "punycode", - "rustc", "rustc-demangle", "rustc_ast", "rustc_data_structures", "rustc_hir", - "rustc_metadata", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", @@ -4157,7 +4360,6 @@ version = "0.0.0" dependencies = [ "fmt_macros", "log", - "rustc", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -4166,27 +4368,30 @@ dependencies = [ "rustc_index", "rustc_infer", "rustc_macros", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "rustc_traits" version = "0.0.0" dependencies = [ + "chalk-ir", + "chalk-rust-ir", + "chalk-solve", "log", - "rustc", "rustc_ast", "rustc_data_structures", "rustc_hir", + "rustc_index", "rustc_infer", - "rustc_macros", + "rustc_middle", "rustc_span", - "rustc_target", "rustc_trait_selection", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4194,10 +4399,11 @@ name = "rustc_ty" version = "0.0.0" dependencies = [ "log", - "rustc", "rustc_data_structures", + "rustc_errors", "rustc_hir", "rustc_infer", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", @@ -4210,7 +4416,6 @@ version = "0.0.0" dependencies = [ "arena", "log", - "rustc", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -4218,11 +4423,12 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_infer", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4240,7 +4446,7 @@ version = "0.0.0" dependencies = [ "itertools 0.8.0", "minifier", - "pulldown-cmark 0.7.0", + "pulldown-cmark 0.7.1", "rustc-rayon", "serde", "serde_json", @@ -4258,18 +4464,6 @@ dependencies = [ "rustdoc", ] -[[package]] -name = "rustfix" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7150ac777a2931a53489f5a41eb0937b84e3092a20cd0e73ad436b65b507f607" -dependencies = [ - "failure", - "log", - "serde", - "serde_json", -] - [[package]] name = "rustfix" version = "0.5.0" @@ -4294,7 +4488,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.12" +version = "1.4.14" dependencies = [ "annotate-snippets", "bytecount", @@ -4310,14 +4504,14 @@ dependencies = [ "lazy_static 1.4.0", "log", "regex", + "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", + "rustc-ap-rustc_expand", "rustc-ap-rustc_parse", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "rustc-ap-rustc_target", - "rustc-ap-syntax", "rustc-workspace-hack", "rustfmt-config_proc_macro", "serde", @@ -4421,13 +4615,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.81" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "477b13b646f5b5b56fc95bedfc3b550d12141ce84f466f6c44b9a17589923885" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.11", ] [[package]] @@ -4478,19 +4672,19 @@ name = "serialize" version = "0.0.0" dependencies = [ "indexmap", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "sha-1" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ "block-buffer", - "byte-tools", "digest", "fake-simd", + "opaque-debug", ] [[package]] @@ -4523,9 +4717,9 @@ checksum = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" [[package]] name = "sized-chunks" -version = "0.5.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62db64dd92b3b54314b1e216c274634ca2b3fe8da8b3873be670cb1ac4dad30f" +checksum = "1ec31ceca5644fa6d444cc77548b88b67f46db6f7c71683b0f9336e671830d2f" dependencies = [ "bitmaps", "typenum", @@ -4545,15 +4739,15 @@ checksum = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" [[package]] name = "smallvec" -version = "1.0.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] name = "socket2" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" dependencies = [ "cfg-if", "libc", @@ -4567,6 +4761,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbc596e092fe5f598b12ef46cc03754085ac2f4d8c739ad61c4ae266cc3b3fa" +[[package]] +name = "stacker" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd941b456e1c006d6b9f27c526d5b69281288aeea8cba82c19d3843d8ccdd2" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi 0.3.8", +] + [[package]] name = "std" version = "0.0.0" @@ -4578,7 +4785,7 @@ dependencies = [ "core", "dlmalloc", "fortanix-sgx-abi", - "hashbrown 0.6.2", + "hashbrown", "hermit-abi", "libc", "panic_abort", @@ -4710,18 +4917,6 @@ dependencies = [ "unicode-xid 0.2.0", ] -[[package]] -name = "synstructure" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", - "unicode-xid 0.1.0", -] - [[package]] name = "synstructure" version = "0.12.1" @@ -4854,6 +5049,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fb62ff737e573b1e677459bea6fd023cd5d6e868c3242d3cdf3ef2f0554824" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24069c0ba08aab54289d6a25f5036d94afc61e1538bbc42ae5501df141c9027d" +dependencies = [ + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.11", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -5196,9 +5411,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "ucd-parse" @@ -5249,11 +5464,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -5264,10 +5479,11 @@ checksum = "5b2c5c29e805da6817f5af6a627d65adb045cebf05cccd5a3493d6109454391c" [[package]] name = "unicode-security" -version = "0.0.2" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49d35967fa037b881acc34ef717c38c4b5560eba10e3685271b3f530bb19634" +checksum = "a5f9011bbed9c13372bc8df618b55a38138445199caf3b61d432c6859c36dee0" dependencies = [ + "unicode-normalization", "unicode-script", ] @@ -5536,8 +5752,28 @@ dependencies = [ "lzma-sys", ] +[[package]] +name = "yaml-merge-keys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59893318ba3ad2b704498c7761214a10eaf42c5f838bce9fc0145bf2ba658cfa" +dependencies = [ + "lazy_static 1.4.0", + "thiserror", + "yaml-rust 0.4.3", +] + [[package]] name = "yaml-rust" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" + +[[package]] +name = "yaml-rust" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml index 2f5a708e8dc6c..7b5e0fa1c2817 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ members = [ "src/tools/miri", "src/tools/rustdoc-themes", "src/tools/unicode-table-generator", + "src/tools/expand-yaml-anchors", ] exclude = [ "build", diff --git a/README.md b/README.md index 1cd9b8a3a2ae1..00bb501941dd7 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ or reading the [rustc dev guide][rustcguidebuild]. 1. Make sure you have installed the dependencies: * `g++` 5.1 or later or `clang++` 3.5 or later - * `python` 2.7 (but not 3.x) + * `python` 3 or 2.7 * GNU `make` 3.81 or later * `cmake` 3.4.3 or later * `curl` @@ -113,7 +113,7 @@ build. make \ diffutils \ tar \ - mingw-w64-x86_64-python2 \ + mingw-w64-x86_64-python \ mingw-w64-x86_64-cmake \ mingw-w64-x86_64-gcc ``` @@ -256,7 +256,7 @@ Also, you may find the [rustdocs for the compiler itself][rustdocs] useful. [rust-discord]: https://discord.gg/rust-lang [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/about-this-guide.html -[rustdocs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ +[rustdocs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ ## License diff --git a/RELEASES.md b/RELEASES.md index 9ff0d14b353cd..8ea481f7e18cd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,164 @@ +Version 1.43.1 (2020-05-07) +=========================== + +* [Updated openssl-src to 1.1.1g for CVE-2020-1967.][71430] +* [Fixed the stabilization of AVX-512 features.][71473] +* [Fixed `cargo package --list` not working with unpublished dependencies.][cargo/8151] + +[71430]: https://github.com/rust-lang/rust/pull/71430 +[71473]: https://github.com/rust-lang/rust/issues/71473 +[cargo/8151]: https://github.com/rust-lang/cargo/issues/8151 + + +Version 1.43.0 (2020-04-23) +========================== + +Language +-------- +- [Fixed using binary operations with `&{number}` (e.g. `&1.0`) not having + the type inferred correctly.][68129] +- [Attributes such as `#[cfg()]` can now be used on `if` expressions.][69201] + +**Syntax only changes** +- [Allow `type Foo: Ord` syntactically.][69361] +- [Fuse associated and extern items up to defaultness.][69194] +- [Syntactically allow `self` in all `fn` contexts.][68764] +- [Merge `fn` syntax + cleanup item parsing.][68728] +- [`item` macro fragments can be interpolated into `trait`s, `impl`s, and `extern` blocks.][69366] + For example, you may now write: + ```rust + macro_rules! mac_trait { + ($i:item) => { + trait T { $i } + } + } + mac_trait! { + fn foo() {} + } + ``` + +These are still rejected *semantically*, so you will likely receive an error but +these changes can be seen and parsed by macros and +conditional compilation. + + +Compiler +-------- +- [You can now pass multiple lint flags to rustc to override the previous + flags.][67885] For example; `rustc -D unused -A unused-variables` denies + everything in the `unused` lint group except `unused-variables` which + is explicitly allowed. However, passing `rustc -A unused-variables -D unused` denies + everything in the `unused` lint group **including** `unused-variables` since + the allow flag is specified before the deny flag (and therefore overridden). +- [rustc will now prefer your system MinGW libraries over its bundled libraries + if they are available on `windows-gnu`.][67429] +- [rustc now buffers errors/warnings printed in JSON.][69227] + +Libraries +--------- +- [`Arc<[T; N]>`, `Box<[T; N]>`, and `Rc<[T; N]>`, now implement + `TryFrom>`,`TryFrom>`, and `TryFrom>` + respectively.][69538] **Note** These conversions are only available when `N` + is `0..=32`. +- [You can now use associated constants on floats and integers directly, rather + than having to import the module.][68952] e.g. You can now write `u32::MAX` or + `f32::NAN` with no imports. +- [`u8::is_ascii` is now `const`.][68984] +- [`String` now implements `AsMut`.][68742] +- [Added the `primitive` module to `std` and `core`.][67637] This module + reexports Rust's primitive types. This is mainly useful in macros + where you want avoid these types being shadowed. +- [Relaxed some of the trait bounds on `HashMap` and `HashSet`.][67642] +- [`string::FromUtf8Error` now implements `Clone + Eq`.][68738] + +Stabilized APIs +--------------- +- [`Once::is_completed`] +- [`f32::LOG10_2`] +- [`f32::LOG2_10`] +- [`f64::LOG10_2`] +- [`f64::LOG2_10`] +- [`iter::once_with`] + +Cargo +----- +- [You can now set config `[profile]`s in your `.cargo/config`, or through + your environment.][cargo/7823] +- [Cargo will now set `CARGO_BIN_EXE_` pointing to a binary's + executable path when running integration tests or benchmarks.][cargo/7697] + `` is the name of your binary as-is e.g. If you wanted the executable + path for a binary named `my-program`you would use `env!("CARGO_BIN_EXE_my-program")`. + +Misc +---- +- [Certain checks in the `const_err` lint were deemed unrelated to const + evaluation][69185], and have been moved to the `unconditional_panic` and + `arithmetic_overflow` lints. + +Compatibility Notes +------------------- + +- [Having trailing syntax in the `assert!` macro is now a hard error.][69548] This + has been a warning since 1.36.0. +- [Fixed `Self` not having the correctly inferred type.][69340] This incorrectly + led to some instances being accepted, and now correctly emits a hard error. + +[69340]: https://github.com/rust-lang/rust/pull/69340 + +Internal Only +------------- +These changes provide no direct user facing benefits, but represent significant +improvements to the internals and overall performance of `rustc` and +related tools. + +- [All components are now built with `opt-level=3` instead of `2`.][67878] +- [Improved how rustc generates drop code.][67332] +- [Improved performance from `#[inline]`-ing certain hot functions.][69256] +- [traits: preallocate 2 Vecs of known initial size][69022] +- [Avoid exponential behaviour when relating types][68772] +- [Skip `Drop` terminators for enum variants without drop glue][68943] +- [Improve performance of coherence checks][68966] +- [Deduplicate types in the generator witness][68672] +- [Invert control in struct_lint_level.][68725] + +[67332]: https://github.com/rust-lang/rust/pull/67332/ +[67429]: https://github.com/rust-lang/rust/pull/67429/ +[67637]: https://github.com/rust-lang/rust/pull/67637/ +[67642]: https://github.com/rust-lang/rust/pull/67642/ +[67878]: https://github.com/rust-lang/rust/pull/67878/ +[67885]: https://github.com/rust-lang/rust/pull/67885/ +[68129]: https://github.com/rust-lang/rust/pull/68129/ +[68672]: https://github.com/rust-lang/rust/pull/68672/ +[68725]: https://github.com/rust-lang/rust/pull/68725/ +[68728]: https://github.com/rust-lang/rust/pull/68728/ +[68738]: https://github.com/rust-lang/rust/pull/68738/ +[68742]: https://github.com/rust-lang/rust/pull/68742/ +[68764]: https://github.com/rust-lang/rust/pull/68764/ +[68772]: https://github.com/rust-lang/rust/pull/68772/ +[68943]: https://github.com/rust-lang/rust/pull/68943/ +[68952]: https://github.com/rust-lang/rust/pull/68952/ +[68966]: https://github.com/rust-lang/rust/pull/68966/ +[68984]: https://github.com/rust-lang/rust/pull/68984/ +[69022]: https://github.com/rust-lang/rust/pull/69022/ +[69185]: https://github.com/rust-lang/rust/pull/69185/ +[69194]: https://github.com/rust-lang/rust/pull/69194/ +[69201]: https://github.com/rust-lang/rust/pull/69201/ +[69227]: https://github.com/rust-lang/rust/pull/69227/ +[69548]: https://github.com/rust-lang/rust/pull/69548/ +[69256]: https://github.com/rust-lang/rust/pull/69256/ +[69361]: https://github.com/rust-lang/rust/pull/69361/ +[69366]: https://github.com/rust-lang/rust/pull/69366/ +[69538]: https://github.com/rust-lang/rust/pull/69538/ +[cargo/7823]: https://github.com/rust-lang/cargo/pull/7823 +[cargo/7697]: https://github.com/rust-lang/cargo/pull/7697 +[`Once::is_completed`]: https://doc.rust-lang.org/std/sync/struct.Once.html#method.is_completed +[`f32::LOG10_2`]: https://doc.rust-lang.org/std/f32/consts/constant.LOG10_2.html +[`f32::LOG2_10`]: https://doc.rust-lang.org/std/f32/consts/constant.LOG2_10.html +[`f64::LOG10_2`]: https://doc.rust-lang.org/std/f64/consts/constant.LOG10_2.html +[`f64::LOG2_10`]: https://doc.rust-lang.org/std/f64/consts/constant.LOG2_10.html +[`iter::once_with`]: https://doc.rust-lang.org/std/iter/fn.once_with.html + + Version 1.42.0 (2020-03-12) ========================== @@ -24,7 +185,7 @@ Language (e.g. `type Foo: Ord;`). - `...` (the C-variadic type) may occur syntactically directly as the type of any function parameter. - + These are still rejected *semantically*, so you will likely receive an error but these changes can be seen and parsed by procedural macros and conditional compilation. @@ -316,7 +477,7 @@ Compatibility Notes - [Using `#[inline]` on function prototypes and consts now emits a warning under `unused_attribute` lint.][65294] Using `#[inline]` anywhere else inside traits or `extern` blocks now correctly emits a hard error. - + [65294]: https://github.com/rust-lang/rust/pull/65294/ [66103]: https://github.com/rust-lang/rust/pull/66103/ [65843]: https://github.com/rust-lang/rust/pull/65843/ diff --git a/config.toml.example b/config.toml.example index ce21b63467f53..ffe907c9da97c 100644 --- a/config.toml.example +++ b/config.toml.example @@ -84,8 +84,9 @@ #link-shared = false # When building llvm, this configures what is being appended to the version. -# If absent, we let the version as-is. -#version-suffix = "-rust" +# The default is "-rust-$version-$channel", except for dev channel where rustc +# version number is omitted. To use LLVM version as is, provide an empty string. +#version-suffix = "-rust-dev" # 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 @@ -130,6 +131,10 @@ # for each target triple. #target = ["x86_64-unknown-linux-gnu"] # defaults to just the build triple +# Use this directory to store build artifacts. +# You can use "$ROOT" to indicate the root of the git repository. +#build-dir = "build" + # Instead of downloading the src/stage0.txt version of Cargo specified, use # this Cargo binary instead to build all Rust code #cargo = "/path/to/bin/cargo" @@ -168,11 +173,9 @@ # Python interpreter to use for various tasks throughout the build, notably # rustdoc tests, the lldb python interpreter, and some dist bits and pieces. -# Note that Python 2 is currently required. # -# Defaults to python2.7, then python2. If neither executable can be found, then -# it defaults to the Python interpreter used to execute x.py. -#python = "python2.7" +# Defaults to the Python interpreter used to execute x.py. +#python = "python" # Force Cargo to check that Cargo.lock describes the precise dependency # set that all the Cargo.toml files create, instead of updating it. @@ -311,6 +314,10 @@ # library. #debug-assertions = false +# Whether or not debug assertions are enabled for the standard library. +# Overrides the `debug-assertions` option, if defined. +#debug-assertions-std = false + # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info # `1` - line tables only @@ -408,10 +415,6 @@ # sysroot. #llvm-tools = false -# Indicates whether LLDB will be made available in the sysroot. -# This is only built if LLVM is also being built. -#lldb = false - # Whether to deny warnings in crates #deny-warnings = true diff --git a/configure b/configure index eeb8d081d3454..81e2001e4a583 100755 --- a/configure +++ b/configure @@ -11,6 +11,7 @@ try() { fi } +try python3 "$@" try python2.7 "$@" try python27 "$@" try python2 "$@" diff --git a/rustfmt.toml b/rustfmt.toml index 8f4c901fb9740..9b2c08200362e 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -20,7 +20,7 @@ ignore = [ "src/doc/nomicon", "src/doc/reference", "src/doc/rust-by-example", - "src/doc/rustc-guide", + "src/doc/rustc-dev-guide", "src/llvm-project", "src/stdarch", "src/tools/cargo", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index c09f58cc591a6..f7856f6a7fcb1 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -54,4 +54,4 @@ version = "0.3" features = ["fileapi", "ioapiset", "jobapi2", "handleapi", "winioctl"] [dev-dependencies] -pretty_assertions = "0.5" +pretty_assertions = "0.6" diff --git a/src/bootstrap/bin/llvm-config-wrapper.rs b/src/bootstrap/bin/llvm-config-wrapper.rs index cf77af44ff606..89984bb55dfd8 100644 --- a/src/bootstrap/bin/llvm-config-wrapper.rs +++ b/src/bootstrap/bin/llvm-config-wrapper.rs @@ -10,7 +10,14 @@ fn main() { let mut cmd = Command::new(real_llvm_config); cmd.args(env::args().skip(1)).stderr(Stdio::piped()); let output = cmd.output().expect("failed to spawn llvm-config"); - let stdout = String::from_utf8_lossy(&output.stdout); + let mut stdout = String::from_utf8_lossy(&output.stdout); + + if let Ok(to_replace) = env::var("LLVM_CONFIG_SHIM_REPLACE") { + if let Ok(replace_with) = env::var("LLVM_CONFIG_SHIM_REPLACE_WITH") { + stdout = stdout.replace(&to_replace, &replace_with).into(); + } + } + print!("{}", stdout.replace("\\", "/")); io::stdout().flush().unwrap(); process::exit(output.status.code().unwrap_or(1)); diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index daa030c59d641..a8c00c8c3ca88 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -134,11 +134,6 @@ fn main() { cmd.arg(format!("-Clinker={}", host_linker)); } - // Override linker flavor if necessary. - if let Ok(host_linker_flavor) = env::var("RUSTC_HOST_LINKER_FLAVOR") { - cmd.arg(format!("-Clinker-flavor={}", host_linker_flavor)); - } - if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") { if s == "true" { cmd.arg("-C").arg("target-feature=+crt-static"); diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index d5efed61b541e..b7d0fac5be31f 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -2,6 +2,7 @@ import argparse import contextlib import datetime +import distutils.version import hashlib import os import re @@ -78,6 +79,7 @@ def _download(path, url, probably_big, verbose, exception): option = "-#" else: option = "-s" + require(["curl", "--version"]) run(["curl", option, "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds "--connect-timeout", "30", # timeout if cannot connect within 30 seconds @@ -142,6 +144,21 @@ def run(args, verbose=False, exception=False, **kwargs): sys.exit(err) +def require(cmd, exit=True): + '''Run a command, returning its output. + On error, + If `exit` is `True`, exit the process. + Otherwise, return None.''' + try: + return subprocess.check_output(cmd).strip() + except (subprocess.CalledProcessError, OSError) as exc: + if not exit: + return None + print("error: unable to run `{}`: {}".format(' '.join(cmd), exc)) + print("Please make sure it's installed and in the path.") + sys.exit(1) + + def stage0_data(rust_root): """Build a dictionary from stage0.txt""" nightlies = os.path.join(rust_root, "src/stage0.txt") @@ -163,16 +180,15 @@ def format_build_time(duration): def default_build_triple(): """Build triple as in LLVM""" default_encoding = sys.getdefaultencoding() - try: - ostype = subprocess.check_output( - ['uname', '-s']).strip().decode(default_encoding) - cputype = subprocess.check_output( - ['uname', '-m']).strip().decode(default_encoding) - except (subprocess.CalledProcessError, OSError): - if sys.platform == 'win32': - return 'x86_64-pc-windows-msvc' - err = "uname not found" - sys.exit(err) + required = sys.platform != 'win32' + ostype = require(["uname", "-s"], exit=required) + cputype = require(['uname', '-m'], exit=required) + + if ostype is None or cputype is None: + return 'x86_64-pc-windows-msvc' + + ostype = ostype.decode(default_encoding) + cputype = cputype.decode(default_encoding) # The goal here is to come up with the same triple as LLVM would, # at least for the subset of platforms we're willing to target. @@ -202,12 +218,7 @@ def default_build_triple(): # output from that option is too generic for our purposes (it will # always emit 'i386' on x86/amd64 systems). As such, isainfo -k # must be used instead. - try: - cputype = subprocess.check_output( - ['isainfo', '-k']).strip().decode(default_encoding) - except (subprocess.CalledProcessError, OSError): - err = "isainfo not found" - sys.exit(err) + cputype = require(['isainfo', '-k']).decode(default_encoding) elif ostype.startswith('MINGW'): # msys' `uname` does not print gcc configuration, but prints msys # configuration. so we cannot believe `uname -m`: @@ -324,13 +335,14 @@ def __init__(self): self.rustc_channel = '' self.rustfmt_channel = '' self.build = '' - self.build_dir = os.path.join(os.getcwd(), "build") + self.build_dir = '' self.clean = False self.config_toml = '' self.rust_root = '' self.use_locked_deps = '' self.use_vendored_sources = '' self.verbose = False + self.git_version = None def download_stage0(self): """Fetch the build system for Rust, written in Rust @@ -743,15 +755,13 @@ def update_submodule(self, module, checked_out, recorded_submodules): run(["git", "submodule", "-q", "sync", module], cwd=self.rust_root, verbose=self.verbose) - try: - run(["git", "submodule", "update", - "--init", "--recursive", "--progress", module], - cwd=self.rust_root, verbose=self.verbose, exception=True) - except RuntimeError: - # Some versions of git don't support --progress. - run(["git", "submodule", "update", - "--init", "--recursive", module], - cwd=self.rust_root, verbose=self.verbose) + + update_args = ["git", "submodule", "update", "--init", "--recursive"] + if self.git_version >= distutils.version.LooseVersion("2.11.0"): + update_args.append("--progress") + update_args.append(module) + run(update_args, cwd=self.rust_root, verbose=self.verbose, exception=True) + run(["git", "reset", "-q", "--hard"], cwd=module_path, verbose=self.verbose) run(["git", "clean", "-qdfx"], @@ -763,12 +773,11 @@ def update_submodules(self): self.get_toml('submodules') == "false": return - # check the existence of 'git' command - try: - subprocess.check_output(['git', '--version']) - except (subprocess.CalledProcessError, OSError): - print("error: `git` is not found, please make sure it's installed and in the path.") - sys.exit(1) + default_encoding = sys.getdefaultencoding() + + # check the existence and version of 'git' command + git_version_str = require(['git', '--version']).split()[2].decode(default_encoding) + self.git_version = distutils.version.LooseVersion(git_version_str) slow_submodules = self.get_toml('fast-submodules') == "false" start_time = time() @@ -885,7 +894,11 @@ def bootstrap(help_triggered): build.clean = args.clean try: - with open(args.config or 'config.toml') as config: + toml_path = args.config or 'config.toml' + if not os.path.exists(toml_path): + toml_path = os.path.join(build.rust_root, toml_path) + + with open(toml_path) as config: build.config_toml = config.read() except (OSError, IOError): pass @@ -900,6 +913,9 @@ def bootstrap(help_triggered): build.check_vendored_status() + build_dir = build.get_toml('build-dir', 'build') or 'build' + build.build_dir = os.path.abspath(build_dir.replace("$ROOT", build.rust_root)) + data = stage0_data(build.rust_root) build.date = data['date'] build.rustc_channel = data['rustc'] diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index dd519506d42a0..4bc81a7b42dc0 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -21,6 +21,7 @@ use crate::doc; use crate::flags::Subcommand; use crate::install; use crate::native; +use crate::run; use crate::test; use crate::tool; use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir}; @@ -313,6 +314,7 @@ pub enum Kind { Dist, Doc, Install, + Run, } impl<'a> Builder<'a> { @@ -349,14 +351,14 @@ impl<'a> Builder<'a> { native::Lld ), Kind::Check | Kind::Clippy | Kind::Fix | Kind::Format => { - describe!(check::Std, check::Rustc, check::Rustdoc) + describe!(check::Std, check::Rustc, check::Rustdoc, check::Clippy) } Kind::Test => describe!( crate::toolstate::ToolStateCheck, + test::ExpandYamlAnchors, test::Tidy, test::Ui, test::CompileFail, - test::RunFail, test::RunPassValgrind, test::MirOpt, test::Codegen, @@ -367,7 +369,6 @@ impl<'a> Builder<'a> { test::UiFullDeps, test::Rustdoc, test::Pretty, - test::RunFailPretty, test::RunPassValgrindPretty, test::Crate, test::CrateLibrustc, @@ -438,7 +439,6 @@ impl<'a> Builder<'a> { dist::Clippy, dist::Miri, dist::LlvmTools, - dist::Lldb, dist::Extended, dist::HashSign ), @@ -454,6 +454,7 @@ impl<'a> Builder<'a> { install::Src, install::Rustc ), + Kind::Run => describe!(run::ExpandYamlAnchors,), } } @@ -507,6 +508,7 @@ impl<'a> Builder<'a> { Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]), Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), + Subcommand::Run { ref paths } => (Kind::Run, &paths[..]), Subcommand::Format { .. } | Subcommand::Clean { .. } => panic!(), }; @@ -760,9 +762,17 @@ impl<'a> Builder<'a> { } // Set a flag for `check`/`clippy`/`fix`, so that certain build - // scripts can do less work (e.g. not building/requiring LLVM). + // scripts can do less work (i.e. not building/requiring LLVM). if cmd == "check" || cmd == "clippy" || cmd == "fix" { - cargo.env("RUST_CHECK", "1"); + // If we've not yet built LLVM, or it's stale, then bust + // the librustc_llvm cache. That will always work, even though it + // may mean that on the next non-check build we'll need to rebuild + // librustc_llvm. But if LLVM is stale, that'll be a tiny amount + // of work comparitively, and we'd likely need to rebuild it anyway, + // so that's okay. + if crate::native::prebuilt_llvm_config(self, target).is_err() { + cargo.env("RUST_CHECK", "1"); + } } let stage = if compiler.stage == 0 && self.local_rebuild { @@ -786,6 +796,11 @@ impl<'a> Builder<'a> { rustflags.arg("--cfg=bootstrap"); } + // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, + // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See + // #71458. + let rustdocflags = rustflags.clone(); + if let Ok(s) = env::var("CARGOFLAGS") { cargo.args(s.split_whitespace()); } @@ -900,7 +915,14 @@ impl<'a> Builder<'a> { .env("RUSTC", self.out.join("bootstrap/debug/rustc")) .env("RUSTC_REAL", self.rustc(compiler)) .env("RUSTC_STAGE", stage.to_string()) - .env("RUSTC_DEBUG_ASSERTIONS", self.config.rust_debug_assertions.to_string()) + .env( + "RUSTC_DEBUG_ASSERTIONS", + if mode == Mode::Std { + self.config.rust_debug_assertions_std.to_string() + } else { + self.config.rust_debug_assertions.to_string() + }, + ) .env("RUSTC_SYSROOT", &sysroot) .env("RUSTC_LIBDIR", &libdir) .env("RUSTDOC", self.out.join("bootstrap/debug/rustdoc")) @@ -964,27 +986,11 @@ impl<'a> Builder<'a> { // See https://github.com/rust-lang/rust/issues/68647. let can_use_lld = mode != Mode::Std; - // FIXME: The beta compiler doesn't pick the `lld-link` flavor for `*-pc-windows-msvc` - // Remove `RUSTC_HOST_LINKER_FLAVOR` when this is fixed - let lld_linker_flavor = |linker: &Path, target: Interned| { - compiler.stage == 0 - && linker.file_name() == Some(OsStr::new("rust-lld")) - && target.contains("pc-windows-msvc") - }; - if let Some(host_linker) = self.linker(compiler.host, can_use_lld) { - if lld_linker_flavor(host_linker, compiler.host) { - cargo.env("RUSTC_HOST_LINKER_FLAVOR", "lld-link"); - } - cargo.env("RUSTC_HOST_LINKER", host_linker); } if let Some(target_linker) = self.linker(target, can_use_lld) { - if lld_linker_flavor(target_linker, target) { - rustflags.arg("-Clinker-flavor=lld-link"); - } - let target = crate::envify(&target); cargo.env(&format!("CARGO_TARGET_{}_LINKER", target), target_linker); } @@ -1017,8 +1023,13 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_HOST_CRT_STATIC", x.to_string()); } - if let Some(map) = self.build.debuginfo_map(GitRepo::Rustc) { + if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) { + let map = format!("{}={}", self.build.src.display(), map_to); cargo.env("RUSTC_DEBUGINFO_MAP", map); + + // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to, + // in order to opportunistically reverse it later. + cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); } // Enable usage of unstable features @@ -1090,6 +1101,13 @@ impl<'a> Builder<'a> { if self.config.deny_warnings { rustflags.arg("-Dwarnings"); } + + // FIXME(#58633) hide "unused attribute" errors in incremental + // builds of the standard library, as the underlying checks are + // not yet properly integrated with incremental recompilation. + if mode == Mode::Std && compiler.stage == 0 && self.config.incremental { + rustflags.arg("-Aunused-attributes"); + } } if let Mode::Rustc | Mode::Codegen = mode { @@ -1268,7 +1286,7 @@ impl<'a> Builder<'a> { } } - Cargo { command: cargo, rustflags } + Cargo { command: cargo, rustflags, rustdocflags } } /// Ensure that a given step is built, returning its output. This will @@ -1326,7 +1344,7 @@ impl<'a> Builder<'a> { #[cfg(test)] mod tests; -#[derive(Debug)] +#[derive(Debug, Clone)] struct Rustflags(String); impl Rustflags { @@ -1366,6 +1384,7 @@ impl Rustflags { pub struct Cargo { command: Command, rustflags: Rustflags, + rustdocflags: Rustflags, } impl Cargo { @@ -1398,7 +1417,16 @@ impl Cargo { impl From for Command { fn from(mut cargo: Cargo) -> Command { - cargo.command.env("RUSTFLAGS", &cargo.rustflags.0); + let rustflags = &cargo.rustflags.0; + if !rustflags.is_empty() { + cargo.command.env("RUSTFLAGS", rustflags); + } + + let rustdocflags = &cargo.rustdocflags.0; + if !rustdocflags.is_empty() { + cargo.command.env("RUSTDOCFLAGS", rustdocflags); + } + cargo.command } } diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index be2b0f36d14a7..f9d3b454246b1 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -13,7 +13,7 @@ use build_helper::output; use crate::Build; // The version number -pub const CFG_RELEASE_NUM: &str = "1.44.0"; +pub const CFG_RELEASE_NUM: &str = "1.45.0"; pub struct GitInfo { inner: Option, diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index b76515763fbdb..7a8bfb2d5d877 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -45,7 +45,7 @@ impl Step for Std { let compiler = builder.compiler(0, builder.config.build); let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind)); - std_cargo(builder, target, &mut cargo); + std_cargo(builder, target, compiler.stage, &mut cargo); builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target)); run_cargo( @@ -112,83 +112,89 @@ impl Step for Rustc { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct Rustdoc { - pub target: Interned, +macro_rules! tool_check_step { + ($name:ident, $path:expr) => { + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub struct $name { + pub target: Interned, + } + + impl Step for $name { + type Output = (); + const ONLY_HOSTS: bool = true; + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path($path) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure($name { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + let compiler = builder.compiler(0, builder.config.build); + let target = self.target; + + builder.ensure(Rustc { target }); + + let cargo = prepare_tool_cargo( + builder, + compiler, + Mode::ToolRustc, + target, + cargo_subcommand(builder.kind), + $path, + SourceType::InTree, + &[], + ); + + println!( + "Checking {} artifacts ({} -> {})", + stringify!($name).to_lowercase(), + &compiler.host, + target + ); + run_cargo( + builder, + cargo, + args(builder.kind), + &stamp(builder, compiler, target), + vec![], + true, + ); + + let libdir = builder.sysroot_libdir(compiler, target); + let hostdir = builder.sysroot_libdir(compiler, compiler.host); + add_to_sysroot(&builder, &libdir, &hostdir, &stamp(builder, compiler, target)); + + /// Cargo's output path in a given stage, compiled by a particular + /// compiler for the specified target. + fn stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: Interned, + ) -> PathBuf { + builder + .cargo_out(compiler, Mode::ToolRustc, target) + .join(format!(".{}-check.stamp", stringify!($name).to_lowercase())) + } + } + } + }; } -impl Step for Rustdoc { - type Output = (); - const ONLY_HOSTS: bool = true; - const DEFAULT: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/rustdoc") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Rustdoc { target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(0, builder.config.build); - let target = self.target; - - builder.ensure(Rustc { target }); - - let cargo = prepare_tool_cargo( - builder, - compiler, - Mode::ToolRustc, - target, - cargo_subcommand(builder.kind), - "src/tools/rustdoc", - SourceType::InTree, - &[], - ); - - println!("Checking rustdoc artifacts ({} -> {})", &compiler.host, target); - run_cargo( - builder, - cargo, - args(builder.kind), - &rustdoc_stamp(builder, compiler, target), - vec![], - true, - ); - - let libdir = builder.sysroot_libdir(compiler, target); - let hostdir = builder.sysroot_libdir(compiler, compiler.host); - add_to_sysroot(&builder, &libdir, &hostdir, &rustdoc_stamp(builder, compiler, target)); - } -} +tool_check_step!(Rustdoc, "src/tools/rustdoc"); +tool_check_step!(Clippy, "src/tools/clippy"); /// Cargo's output path for the standard library in a given stage, compiled /// by a particular compiler for the specified target. -pub fn libstd_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: Interned, -) -> PathBuf { +fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: Interned) -> PathBuf { builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp") } /// Cargo's output path for librustc in a given stage, compiled by a particular /// compiler for the specified target. -pub fn librustc_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: Interned, -) -> PathBuf { +fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: Interned) -> PathBuf { builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp") } - -/// Cargo's output path for rustdoc in a given stage, compiled by a particular -/// compiler for the specified target. -pub fn rustdoc_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: Interned, -) -> PathBuf { - builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rustdoc-check.stamp") -} diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index ad494b88b3af2..c56114f14caa9 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -22,7 +22,7 @@ use serde::Deserialize; use crate::builder::Cargo; use crate::dist; use crate::native; -use crate::util::{exe, is_dylib}; +use crate::util::{exe, is_dylib, symlink_dir}; use crate::{Compiler, GitRepo, Mode}; use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; @@ -86,7 +86,7 @@ impl Step for Std { target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter()); let mut cargo = builder.cargo(compiler, Mode::Std, target, "build"); - std_cargo(builder, target, &mut cargo); + std_cargo(builder, target, compiler.stage, &mut cargo); builder.info(&format!( "Building stage{} std artifacts ({} -> {})", @@ -125,15 +125,16 @@ fn copy_third_party_objects( target_deps.push(target); }; - // Copies the crt(1,i,n).o startup objects + // Copies the CRT objects. // - // Since musl supports fully static linking, we can cross link for it even - // with a glibc-targeting toolchain, given we have the appropriate startup - // files. As those shipped with glibc won't work, copy the ones provided by - // musl so we have them on linux-gnu hosts. + // rustc historically provides a more self-contained installation for musl targets + // not requiring the presence of a native musl toolchain. For example, it can fall back + // to using gcc from a glibc-targeting toolchain for linking. + // To do that we have to distribute musl startup objects as a part of Rust toolchain + // and link with them manually in the self-contained mode. if target.contains("musl") { let srcdir = builder.musl_root(target).unwrap().join("lib"); - for &obj in &["crt1.o", "crti.o", "crtn.o"] { + for &obj in &["crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] { copy_and_stamp(&srcdir, obj); } } else if target.ends_with("-wasi") { @@ -164,7 +165,7 @@ fn copy_third_party_objects( /// Configure cargo to compile the standard library, adding appropriate env vars /// and such. -pub fn std_cargo(builder: &Builder<'_>, target: Interned, cargo: &mut Cargo) { +pub fn std_cargo(builder: &Builder<'_>, target: Interned, stage: u32, cargo: &mut Cargo) { if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { cargo.env("MACOSX_DEPLOYMENT_TARGET", target); } @@ -186,6 +187,8 @@ pub fn std_cargo(builder: &Builder<'_>, target: Interned, cargo: &mut Ca // `compiler-rt` is located. let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); let compiler_builtins_c_feature = if compiler_builtins_root.exists() { + // Note that `libprofiler_builtins/build.rs` also computes this so if + // you're changing something here please also change that. cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); " compiler-builtins-c".to_string() } else { @@ -229,6 +232,18 @@ pub fn std_cargo(builder: &Builder<'_>, target: Interned, cargo: &mut Ca } } } + + // By default, rustc uses `-Cembed-bitcode=yes`, and Cargo overrides that + // with `-Cembed-bitcode=no` for non-LTO builds. However, libstd must be + // built with bitcode so that the produced rlibs can be used for both LTO + // builds (which use bitcode) and non-LTO builds (which use object code). + // So we override the override here! + // + // But we don't bother for the stage 0 compiler because it's never used + // with LTO. + if stage >= 1 { + cargo.rustflag("-Cembed-bitcode=yes"); + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -503,9 +518,13 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: Interne // librustc_llvm and librustc_codegen_llvm. // // Note that this is disabled if LLVM itself is disabled or we're in a check - // build, where if we're in a check build there's no need to build all of - // LLVM and such. - if builder.config.llvm_enabled() && builder.kind != Kind::Check { + // build. If we are in a check build we still go ahead here presuming we've + // detected that LLVM is alreay built and good to go which helps prevent + // busting caches (e.g. like #71152). + if builder.config.llvm_enabled() + && (builder.kind != Kind::Check + || crate::native::prebuilt_llvm_config(builder, target).is_ok()) + { if builder.is_rust_llvm(target) { cargo.env("LLVM_RUSTLLVM", "1"); } @@ -633,6 +652,30 @@ impl Step for Sysroot { }; let _ = fs::remove_dir_all(&sysroot); t!(fs::create_dir_all(&sysroot)); + + // Symlink the source root into the same location inside the sysroot, + // where `rust-src` component would go (`$sysroot/lib/rustlib/src/rust`), + // so that any tools relying on `rust-src` also work for local builds, + // and also for translating the virtual `/rustc/$hash` back to the real + // directory (for running tests with `rust.remap-debuginfo = true`). + let sysroot_lib_rustlib_src = sysroot.join("lib/rustlib/src"); + t!(fs::create_dir_all(&sysroot_lib_rustlib_src)); + let sysroot_lib_rustlib_src_rust = sysroot_lib_rustlib_src.join("rust"); + if let Err(e) = symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_src_rust) { + eprintln!( + "warning: creating symbolic link `{}` to `{}` failed with {}", + sysroot_lib_rustlib_src_rust.display(), + builder.src.display(), + e, + ); + if builder.config.rust_remap_debuginfo { + eprintln!( + "warning: some `src/test/ui` tests will fail when lacking `{}`", + sysroot_lib_rustlib_src_rust.display(), + ); + } + } + INTERNER.intern_path(sysroot) } } @@ -911,7 +954,11 @@ pub fn stream_cargo( } // Instruct Cargo to give us json messages on stdout, critically leaving // stderr as piped so we can get those pretty colors. - let mut message_format = String::from("json-render-diagnostics"); + let mut message_format = if builder.config.json_output { + String::from("json") + } else { + String::from("json-render-diagnostics") + }; if let Some(s) = &builder.config.rustc_error_format { message_format.push_str(",json-diagnostic-"); message_format.push_str(s); @@ -970,4 +1017,7 @@ pub enum CargoMessage<'a> { BuildScriptExecuted { package_id: Cow<'a, str>, }, + BuildFinished { + success: bool, + }, } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 56164b74f3088..771f952abc013 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -48,6 +48,7 @@ pub struct Config { pub ignore_git: bool, pub exclude: Vec, pub rustc_error_format: Option, + pub json_output: bool, pub test_compare_mode: bool, pub llvm_libunwind: bool, @@ -84,7 +85,6 @@ pub struct Config { pub use_lld: bool, pub lld_enabled: bool, - pub lldb_enabled: bool, pub llvm_tools_enabled: bool, pub llvm_cflags: Option, @@ -97,6 +97,7 @@ pub struct Config { pub rust_codegen_units: Option, pub rust_codegen_units_std: Option, pub rust_debug_assertions: bool, + pub rust_debug_assertions_std: bool, pub rust_debuginfo_level_rustc: u32, pub rust_debuginfo_level_std: u32, pub rust_debuginfo_level_tools: u32, @@ -211,6 +212,8 @@ struct Build { host: Vec, #[serde(default)] target: Vec, + // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable + build_dir: Option, cargo: Option, rustc: Option, rustfmt: Option, /* allow bootstrap.py to use rustfmt key */ @@ -312,6 +315,7 @@ struct Rust { codegen_units: Option, codegen_units_std: Option, debug_assertions: Option, + debug_assertions_std: Option, debuginfo_level: Option, debuginfo_level_rustc: Option, debuginfo_level_std: Option, @@ -334,7 +338,6 @@ struct Rust { lld: Option, use_lld: Option, llvm_tools: Option, - lldb: Option, deny_warnings: Option, backtrace_on_ice: Option, verify_llvm_ir: Option, @@ -415,6 +418,7 @@ impl Config { let mut config = Config::default_opts(); config.exclude = flags.exclude; config.rustc_error_format = flags.rustc_error_format; + config.json_output = flags.json_output; config.on_fail = flags.on_fail; config.stage = flags.stage; config.jobs = flags.jobs.map(threads_from_config); @@ -516,6 +520,7 @@ impl Config { let mut llvm_assertions = None; let mut debug = None; let mut debug_assertions = None; + let mut debug_assertions_std = None; let mut debuginfo_level = None; let mut debuginfo_level_rustc = None; let mut debuginfo_level_std = None; @@ -558,6 +563,7 @@ impl Config { if let Some(ref rust) = toml.rust { debug = rust.debug; debug_assertions = rust.debug_assertions; + debug_assertions_std = rust.debug_assertions_std; debuginfo_level = rust.debuginfo_level; debuginfo_level_rustc = rust.debuginfo_level_rustc; debuginfo_level_std = rust.debuginfo_level_std; @@ -581,7 +587,6 @@ impl Config { } set(&mut config.use_lld, rust.use_lld); set(&mut config.lld_enabled, rust.lld); - set(&mut config.lldb_enabled, rust.lldb); set(&mut config.llvm_tools_enabled, rust.llvm_tools); config.rustc_parallel = rust.parallel_compiler.unwrap_or(false); config.rustc_default_linker = rust.default_linker.clone(); @@ -657,6 +662,8 @@ impl Config { let default = debug == Some(true); config.rust_debug_assertions = debug_assertions.unwrap_or(default); + config.rust_debug_assertions_std = + debug_assertions_std.unwrap_or(config.rust_debug_assertions); let with_defaults = |debuginfo_level_specific: Option| { debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) { diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 2a46c563d1f87..d1e53db573e4c 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -57,7 +57,6 @@ def v(*args): o("profiler", "build.profiler", "build the profiler runtime") o("full-tools", None, "enable all tools") o("lld", "rust.lld", "build lld") -o("lldb", "rust.lldb", "build lldb") o("missing-tools", "dist.missing-tools", "allow failures when building tools") o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++") o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard") diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 8215211ea1c9d..c4bca4a004089 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -38,8 +38,6 @@ pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { format!("{}-{}", component, builder.rustfmt_package_vers()) } else if component == "llvm-tools" { format!("{}-{}", component, builder.llvm_tools_package_vers()) - } else if component == "lldb" { - format!("{}-{}", component, builder.lldb_package_vers()) } else { assert!(component.starts_with("rust")); format!("{}-{}", component, builder.rust_package_vers()) @@ -1330,7 +1328,7 @@ pub struct Clippy { } impl Step for Clippy { - type Output = Option; + type Output = PathBuf; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1348,7 +1346,7 @@ impl Step for Clippy { }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> PathBuf { let compiler = self.compiler; let target = self.target; assert!(builder.config.extended); @@ -1368,16 +1366,10 @@ impl Step for Clippy { // state for clippy isn't testing. let clippy = builder .ensure(tool::Clippy { compiler, target, extra_features: Vec::new() }) - .or_else(|| { - missing_tool("clippy", builder.build.config.missing_tools); - None - })?; + .expect("clippy expected to build - essential tool"); let cargoclippy = builder .ensure(tool::CargoClippy { compiler, target, extra_features: Vec::new() }) - .or_else(|| { - missing_tool("cargo clippy", builder.build.config.missing_tools); - None - })?; + .expect("clippy expected to build - essential tool"); builder.install(&clippy, &image.join("bin"), 0o755); builder.install(&cargoclippy, &image.join("bin"), 0o755); @@ -1416,7 +1408,7 @@ impl Step for Clippy { builder.info(&format!("Dist clippy stage{} ({})", compiler.stage, target)); let _time = timeit(builder); builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target))) + distdir(builder).join(format!("{}-{}.tar.gz", name, target)) } } @@ -1651,7 +1643,6 @@ impl Step for Extended { let llvm_tools_installer = builder.ensure(LlvmTools { target }); let clippy_installer = builder.ensure(Clippy { compiler, target }); let miri_installer = builder.ensure(Miri { compiler, target }); - let lldb_installer = builder.ensure(Lldb { target }); let mingw_installer = builder.ensure(Mingw { host: target }); let analysis_installer = builder.ensure(Analysis { compiler, target }); @@ -1683,11 +1674,10 @@ impl Step for Extended { tarballs.push(rustc_installer); tarballs.push(cargo_installer); tarballs.extend(rls_installer.clone()); - tarballs.extend(clippy_installer.clone()); + tarballs.push(clippy_installer); tarballs.extend(miri_installer.clone()); tarballs.extend(rustfmt_installer.clone()); tarballs.extend(llvm_tools_installer); - tarballs.extend(lldb_installer); tarballs.push(analysis_installer); tarballs.push(std_installer); if builder.config.docs { @@ -1761,9 +1751,6 @@ impl Step for Extended { if rls_installer.is_none() { contents = filter(&contents, "rls"); } - if clippy_installer.is_none() { - contents = filter(&contents, "clippy"); - } if miri_installer.is_none() { contents = filter(&contents, "miri"); } @@ -1805,13 +1792,11 @@ impl Step for Extended { prepare("rust-docs"); prepare("rust-std"); prepare("rust-analysis"); + prepare("clippy"); if rls_installer.is_some() { prepare("rls"); } - if clippy_installer.is_some() { - prepare("clippy"); - } if miri_installer.is_some() { prepare("miri"); } @@ -1863,12 +1848,10 @@ impl Step for Extended { prepare("rust-analysis"); prepare("rust-docs"); prepare("rust-std"); + prepare("clippy"); if rls_installer.is_some() { prepare("rls"); } - if clippy_installer.is_some() { - prepare("clippy"); - } if miri_installer.is_some() { prepare("miri"); } @@ -1989,25 +1972,23 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")), ); } - if clippy_installer.is_some() { - builder.run( - Command::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("clippy") - .args(&heat_flags) - .arg("-cg") - .arg("ClippyGroup") - .arg("-dr") - .arg("Clippy") - .arg("-var") - .arg("var.ClippyDir") - .arg("-out") - .arg(exe.join("ClippyGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); - } + builder.run( + Command::new(&heat) + .current_dir(&exe) + .arg("dir") + .arg("clippy") + .args(&heat_flags) + .arg("-cg") + .arg("ClippyGroup") + .arg("-dr") + .arg("Clippy") + .arg("-var") + .arg("var.ClippyDir") + .arg("-out") + .arg(exe.join("ClippyGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")), + ); if miri_installer.is_some() { builder.run( Command::new(&heat) @@ -2073,6 +2054,7 @@ impl Step for Extended { .arg("-dCargoDir=cargo") .arg("-dStdDir=rust-std") .arg("-dAnalysisDir=rust-analysis") + .arg("-dClippyDir=clippy") .arg("-arch") .arg(&arch) .arg("-out") @@ -2083,9 +2065,6 @@ impl Step for Extended { if rls_installer.is_some() { cmd.arg("-dRlsDir=rls"); } - if clippy_installer.is_some() { - cmd.arg("-dClippyDir=clippy"); - } if miri_installer.is_some() { cmd.arg("-dMiriDir=miri"); } @@ -2101,12 +2080,10 @@ impl Step for Extended { candle("DocsGroup.wxs".as_ref()); candle("CargoGroup.wxs".as_ref()); candle("StdGroup.wxs".as_ref()); + candle("ClippyGroup.wxs".as_ref()); if rls_installer.is_some() { candle("RlsGroup.wxs".as_ref()); } - if clippy_installer.is_some() { - candle("ClippyGroup.wxs".as_ref()); - } if miri_installer.is_some() { candle("MiriGroup.wxs".as_ref()); } @@ -2138,14 +2115,12 @@ impl Step for Extended { .arg("CargoGroup.wixobj") .arg("StdGroup.wixobj") .arg("AnalysisGroup.wixobj") + .arg("ClippyGroup.wixobj") .current_dir(&exe); if rls_installer.is_some() { cmd.arg("RlsGroup.wixobj"); } - if clippy_installer.is_some() { - cmd.arg("ClippyGroup.wixobj"); - } if miri_installer.is_some() { cmd.arg("MiriGroup.wixobj"); } @@ -2243,7 +2218,6 @@ impl Step for HashSign { cmd.arg(builder.package_vers(&builder.release_num("miri"))); cmd.arg(builder.package_vers(&builder.release_num("rustfmt"))); cmd.arg(builder.llvm_tools_package_vers()); - cmd.arg(builder.lldb_package_vers()); builder.create_dir(&distdir(builder)); @@ -2370,119 +2344,3 @@ impl Step for LlvmTools { Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target))) } } - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct Lldb { - pub target: Interned, -} - -impl Step for Lldb { - type Output = Option; - const ONLY_HOSTS: bool = true; - const DEFAULT: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/llvm-project/lldb").path("src/tools/lldb") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Lldb { target: run.target }); - } - - fn run(self, builder: &Builder<'_>) -> Option { - let target = self.target; - - if builder.config.dry_run { - return None; - } - - let bindir = builder.llvm_out(target).join("bin"); - let lldb_exe = bindir.join(exe("lldb", &target)); - if !lldb_exe.exists() { - return None; - } - - builder.info(&format!("Dist Lldb ({})", target)); - let src = builder.src.join("src/llvm-project/lldb"); - let name = pkgname(builder, "lldb"); - - let tmp = tmpdir(builder); - let image = tmp.join("lldb-image"); - drop(fs::remove_dir_all(&image)); - - // Prepare the image directory - let root = image.join("lib/rustlib").join(&*target); - let dst = root.join("bin"); - t!(fs::create_dir_all(&dst)); - for program in &["lldb", "lldb-argdumper", "lldb-mi", "lldb-server"] { - let exe = bindir.join(exe(program, &target)); - builder.install(&exe, &dst, 0o755); - } - - // The libraries. - let libdir = builder.llvm_out(target).join("lib"); - let dst = root.join("lib"); - t!(fs::create_dir_all(&dst)); - for entry in t!(fs::read_dir(&libdir)) { - let entry = entry.unwrap(); - if let Ok(name) = entry.file_name().into_string() { - if name.starts_with("liblldb.") && !name.ends_with(".a") { - if t!(entry.file_type()).is_symlink() { - builder.copy_to_folder(&entry.path(), &dst); - } else { - builder.install(&entry.path(), &dst, 0o755); - } - } - } - } - - // The lldb scripts might be installed in lib/python$version - // or in lib64/python$version. If lib64 exists, use it; - // otherwise lib. - let libdir = builder.llvm_out(target).join("lib64"); - let (libdir, libdir_name) = if libdir.exists() { - (libdir, "lib64") - } else { - (builder.llvm_out(target).join("lib"), "lib") - }; - for entry in t!(fs::read_dir(&libdir)) { - let entry = t!(entry); - if let Ok(name) = entry.file_name().into_string() { - if name.starts_with("python") { - let dst = root.join(libdir_name).join(entry.file_name()); - t!(fs::create_dir_all(&dst)); - builder.cp_r(&entry.path(), &dst); - break; - } - } - } - - // Prepare the overlay - let overlay = tmp.join("lldb-overlay"); - drop(fs::remove_dir_all(&overlay)); - builder.create_dir(&overlay); - builder.install(&src.join("LICENSE.TXT"), &overlay, 0o644); - builder.create(&overlay.join("version"), &builder.lldb_vers()); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=lldb-installed.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=lldb-preview"); - - builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target))) - } -} diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 04da3cc1015b8..7eab92ddc92a9 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -394,7 +394,7 @@ impl Step for Std { let run_cargo_rustdoc_for = |package: &str| { let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc"); - compile::std_cargo(builder, target, &mut cargo); + compile::std_cargo(builder, target, compiler.stage, &mut cargo); // Keep a whitelist so we do not build internal stdlib crates, these will be // build by the rustc step later if enabled. @@ -478,7 +478,11 @@ impl Step for Rustc { // Build cargo command. let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "doc"); - cargo.env("RUSTDOCFLAGS", "--document-private-items"); + cargo.env( + "RUSTDOCFLAGS", + "--document-private-items \ + --enable-index-page -Zunstable-options", + ); compile::rustc_cargo(builder, &mut cargo, target); // Only include compiler crates, no dependencies of those, such as `libc`. diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index d8831c6d9e566..646b9e05d99c3 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -31,6 +31,7 @@ pub struct Flags { pub incremental: bool, pub exclude: Vec, pub rustc_error_format: Option, + pub json_output: bool, pub dry_run: bool, // This overrides the deny-warnings configuration option, @@ -86,6 +87,9 @@ pub enum Subcommand { Install { paths: Vec, }, + Run { + paths: Vec, + }, } impl Default for Subcommand { @@ -102,17 +106,18 @@ impl Flags { Usage: x.py [options] [...] Subcommands: - build Compile either the compiler or libraries - check Compile either the compiler or libraries, using cargo check + build, b Compile either the compiler or libraries + check, c Compile either the compiler or libraries, using cargo check clippy Run clippy (uses rustup/cargo-installed clippy binary) fix Run cargo fix fmt Run rustfmt - test Build and run some test suites + test, t Build and run some test suites bench Build and run some benchmarks doc Build documentation clean Clean out build directories dist Build distribution artifacts install Install distribution artifacts + run, r Run tools contained in this repository To learn more about a subcommand, run `./x.py -h`", ); @@ -152,6 +157,7 @@ To learn more about a subcommand, run `./x.py -h`", "VALUE", ); opts.optopt("", "error-format", "rustc error format", "FORMAT"); + opts.optflag("", "json-output", "use message-format=json"); opts.optopt( "", "llvm-skip-rebuild", @@ -178,16 +184,21 @@ To learn more about a subcommand, run `./x.py -h`", // there on out. let subcommand = args.iter().find(|&s| { (s == "build") + || (s == "b") || (s == "check") + || (s == "c") || (s == "clippy") || (s == "fix") || (s == "fmt") || (s == "test") + || (s == "t") || (s == "bench") || (s == "doc") || (s == "clean") || (s == "dist") || (s == "install") + || (s == "run") + || (s == "r") }); let subcommand = match subcommand { Some(s) => s, @@ -203,7 +214,7 @@ To learn more about a subcommand, run `./x.py -h`", // Some subcommands get extra options match subcommand.as_str() { - "test" => { + "test" | "t" => { opts.optflag("", "no-fail-fast", "Run all tests regardless of failure"); opts.optmulti("", "test-args", "extra arguments", "ARGS"); opts.optmulti( @@ -278,7 +289,7 @@ To learn more about a subcommand, run `./x.py -h`", } // Extra help text for some commands match subcommand.as_str() { - "build" => { + "build" | "b" => { subcommand_help.push_str( "\n Arguments: @@ -305,7 +316,7 @@ Arguments: Once this is done, build/$ARCH/stage1 contains a usable compiler.", ); } - "check" => { + "check" | "c" => { subcommand_help.push_str( "\n Arguments: @@ -355,7 +366,7 @@ Arguments: ./x.py fmt --check", ); } - "test" => { + "test" | "t" => { subcommand_help.push_str( "\n Arguments: @@ -400,6 +411,18 @@ Arguments: ./x.py doc --stage 1", ); } + "run" | "r" => { + subcommand_help.push_str( + "\n +Arguments: + This subcommand accepts a number of paths to tools to build and run. For + example: + + ./x.py run src/tool/expand-yaml-anchors + + At least a tool needs to be called.", + ); + } _ => {} }; // Get any optional paths which occur after the subcommand @@ -434,11 +457,11 @@ Arguments: } let cmd = match subcommand.as_str() { - "build" => Subcommand::Build { paths }, - "check" => Subcommand::Check { paths }, + "build" | "b" => Subcommand::Build { paths }, + "check" | "c" => Subcommand::Check { paths }, "clippy" => Subcommand::Clippy { paths }, "fix" => Subcommand::Fix { paths }, - "test" => Subcommand::Test { + "test" | "t" => Subcommand::Test { paths, bless: matches.opt_present("bless"), compare_mode: matches.opt_str("compare-mode"), @@ -468,17 +491,39 @@ Arguments: "fmt" => Subcommand::Format { check: matches.opt_present("check") }, "dist" => Subcommand::Dist { paths }, "install" => Subcommand::Install { paths }, + "run" | "r" => { + if paths.is_empty() { + println!("\nrun requires at least a path!\n"); + usage(1, &opts, &subcommand_help, &extra_help); + } + Subcommand::Run { paths } + } _ => { usage(1, &opts, &subcommand_help, &extra_help); } }; + if let Subcommand::Check { .. } = &cmd { + if matches.opt_str("stage").is_some() { + println!("{}", "--stage not supported for x.py check, always treated as stage 0"); + process::exit(1); + } + if matches.opt_str("keep-stage").is_some() { + println!( + "{}", + "--keep-stage not supported for x.py check, only one stage available" + ); + process::exit(1); + } + } + Flags { verbose: matches.opt_count("verbose"), stage: matches.opt_str("stage").map(|j| j.parse().expect("`stage` should be a number")), dry_run: matches.opt_present("dry-run"), on_fail: matches.opt_str("on-fail"), rustc_error_format: matches.opt_str("error-format"), + json_output: matches.opt_present("json-output"), keep_stage: matches .opt_strs("keep-stage") .into_iter() diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index 1616ae15d31d6..390b7e96b9a54 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -4,7 +4,7 @@ use crate::Build; use build_helper::{output, t}; use ignore::WalkBuilder; use std::path::Path; -use std::process::Command; +use std::process::{Command, Stdio}; fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) { let mut cmd = Command::new(&rustfmt); @@ -23,7 +23,7 @@ fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) { if !status.success() { eprintln!( "Running `{}` failed.\nIf you're running `tidy`, \ - try again with `--bless` flag. Or, you just want to format \ + try again with `--bless`. Or, if you just want to format \ code, run `./x.py fmt` instead.", cmd_debug, ); @@ -56,16 +56,48 @@ pub fn format(build: &Build, check: bool) { for ignore in rustfmt_config.ignore { ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore); } - let untracked_paths_output = output( - Command::new("git").arg("status").arg("--porcelain").arg("--untracked-files=normal"), - ); - let untracked_paths = untracked_paths_output - .lines() - .filter(|entry| entry.starts_with("??")) - .map(|entry| entry.split(" ").nth(1).expect("every git status entry should list a path")); - for untracked_path in untracked_paths { - eprintln!("skip untracked path {} during rustfmt invocations", untracked_path); - ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path); + let git_available = match Command::new("git") + .arg("--version") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + { + Ok(status) => status.success(), + Err(_) => false, + }; + if git_available { + let in_working_tree = match Command::new("git") + .arg("rev-parse") + .arg("--is-inside-work-tree") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + { + Ok(status) => status.success(), + Err(_) => false, + }; + if in_working_tree { + let untracked_paths_output = output( + Command::new("git") + .arg("status") + .arg("--porcelain") + .arg("--untracked-files=normal"), + ); + let untracked_paths = untracked_paths_output + .lines() + .filter(|entry| entry.starts_with("??")) + .map(|entry| { + entry.split(" ").nth(1).expect("every git status entry should list a path") + }); + for untracked_path in untracked_paths { + eprintln!("skip untracked path {} during rustfmt invocations", untracked_path); + ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path); + } + } else { + eprintln!("Not in git tree. Skipping git-aware format checks"); + } + } else { + eprintln!("Could not find usable git. Skipping git-aware format checks"); } let ignore_fmt = ignore_fmt.build().unwrap(); diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 6549262811b9f..fafd3cdf927c0 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -214,10 +214,8 @@ install!((self, builder, _config), } }; Clippy, "clippy", Self::should_build(_config), only_hosts: true, { - if builder.ensure(dist::Clippy { - compiler: self.compiler, - target: self.target, - }).is_some() || Self::should_install(builder) { + builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target }); + if Self::should_install(builder) { install_clippy(builder, self.compiler.stage, self.target); } else { builder.info( diff --git a/src/bootstrap/job.rs b/src/bootstrap/job.rs index efeb86540b7b7..2fe9b06e42643 100644 --- a/src/bootstrap/job.rs +++ b/src/bootstrap/job.rs @@ -103,7 +103,12 @@ pub unsafe fn setup(build: &mut Build) { }; let parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid.parse().unwrap()); - assert!(!parent.is_null(), "{}", io::Error::last_os_error()); + assert!( + !parent.is_null(), + "PID `{}` doesn't seem to exist: {}", + pid, + io::Error::last_os_error() + ); let mut parent_handle = ptr::null_mut(); let r = DuplicateHandle( GetCurrentProcess(), diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a476d25f10214..15bf831a14835 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -140,6 +140,7 @@ mod format; mod install; mod metadata; mod native; +mod run; mod sanity; mod test; mod tool; @@ -739,19 +740,18 @@ impl Build { self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32) } - fn debuginfo_map(&self, which: GitRepo) -> Option { + fn debuginfo_map_to(&self, which: GitRepo) -> Option { if !self.config.rust_remap_debuginfo { return None; } - let path = match which { + match which { GitRepo::Rustc => { let sha = self.rust_sha().unwrap_or(channel::CFG_RELEASE_NUM); - format!("/rustc/{}", sha) + Some(format!("/rustc/{}", sha)) } - GitRepo::Llvm => String::from("/rustc/llvm"), - }; - Some(format!("{}={}", self.src.display(), path)) + GitRepo::Llvm => Some(String::from("/rustc/llvm")), + } } /// Returns the path to the C compiler for the target specified. @@ -786,7 +786,8 @@ impl Build { base.push("-fno-omit-frame-pointer".into()); } - if let Some(map) = self.debuginfo_map(which) { + if let Some(map_to) = self.debuginfo_map_to(which) { + let map = format!("{}={}", self.src.display(), map_to); let cc = self.cc(target); if cc.ends_with("clang") || cc.ends_with("gcc") { base.push(format!("-fdebug-prefix-map={}", map)); @@ -1028,14 +1029,6 @@ impl Build { self.rust_version() } - fn lldb_package_vers(&self) -> String { - self.package_vers(channel::CFG_RELEASE_NUM) - } - - fn lldb_vers(&self) -> String { - self.rust_version() - } - fn llvm_link_tools_dynamically(&self, target: Interned) -> bool { target.contains("linux-gnu") || target.contains("apple-darwin") } diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index e0a1f46078d32..d8c97fc741478 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -48,7 +48,6 @@ check: $(Q)$(BOOTSTRAP) test $(BOOTSTRAP_ARGS) check-aux: $(Q)$(BOOTSTRAP) test \ - src/test/run-fail/pretty \ src/test/run-pass-valgrind/pretty \ $(AUX_ARGS) \ $(BOOTSTRAP_ARGS) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index d4d66abd520a1..5b6e953484369 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -24,6 +24,72 @@ use crate::util::{self, exe}; use crate::GitRepo; use build_helper::up_to_date; +pub struct Meta { + stamp: HashStamp, + build_llvm_config: PathBuf, + out_dir: PathBuf, + root: String, +} + +// This returns whether we've already previously built LLVM. +// +// It's used to avoid busting caches during x.py check -- if we've already built +// LLVM, it's fine for us to not try to avoid doing so. +// +// This will return the llvm-config if it can get it (but it will not build it +// if not). +pub fn prebuilt_llvm_config( + builder: &Builder<'_>, + target: Interned, +) -> Result { + // If we're using a custom LLVM bail out here, but we can only use a + // custom LLVM for the build triple. + if let Some(config) = builder.config.target_config.get(&target) { + if let Some(ref s) = config.llvm_config { + check_llvm_version(builder, s); + return Ok(s.to_path_buf()); + } + } + + let root = "src/llvm-project/llvm"; + let out_dir = builder.llvm_out(target); + let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); + if !builder.config.build.contains("msvc") || builder.config.ninja { + llvm_config_ret_dir.push("build"); + } + llvm_config_ret_dir.push("bin"); + + let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", &*builder.config.build)); + + let stamp = out_dir.join("llvm-finished-building"); + let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); + + if builder.config.llvm_skip_rebuild && stamp.path.exists() { + builder.info( + "Warning: \ + Using a potentially stale build of LLVM; \ + This may not behave well.", + ); + return Ok(build_llvm_config); + } + + if stamp.is_done() { + if stamp.hash.is_none() { + builder.info( + "Could not determine the LLVM submodule commit hash. \ + Assuming that an LLVM rebuild is not necessary.", + ); + builder.info(&format!( + "To force LLVM to rebuild, remove the file `{}`", + stamp.path.display() + )); + } + return Ok(build_llvm_config); + } + + Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() }) +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Llvm { pub target: Interned, @@ -46,51 +112,11 @@ impl Step for Llvm { fn run(self, builder: &Builder<'_>) -> PathBuf { let target = self.target; - // If we're using a custom LLVM bail out here, but we can only use a - // custom LLVM for the build triple. - if let Some(config) = builder.config.target_config.get(&target) { - if let Some(ref s) = config.llvm_config { - check_llvm_version(builder, s); - return s.to_path_buf(); - } - } - - let root = "src/llvm-project/llvm"; - let out_dir = builder.llvm_out(target); - let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); - if !builder.config.build.contains("msvc") || builder.config.ninja { - llvm_config_ret_dir.push("build"); - } - llvm_config_ret_dir.push("bin"); - - let build_llvm_config = - llvm_config_ret_dir.join(exe("llvm-config", &*builder.config.build)); - - let stamp = out_dir.join("llvm-finished-building"); - let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); - - if builder.config.llvm_skip_rebuild && stamp.path.exists() { - builder.info( - "Warning: \ - Using a potentially stale build of LLVM; \ - This may not behave well.", - ); - return build_llvm_config; - } - - if stamp.is_done() { - if stamp.hash.is_none() { - builder.info( - "Could not determine the LLVM submodule commit hash. \ - Assuming that an LLVM rebuild is not necessary.", - ); - builder.info(&format!( - "To force LLVM to rebuild, remove the file `{}`", - stamp.path.display() - )); - } - return build_llvm_config; - } + let Meta { stamp, build_llvm_config, out_dir, root } = + match prebuilt_llvm_config(builder, target) { + Ok(p) => return p, + Err(m) => m, + }; builder.info(&format!("Building LLVM for {}", target)); t!(stamp.remove()); @@ -158,7 +184,7 @@ impl Step for Llvm { } // For distribution we want the LLVM tools to be *statically* linked to libstdc++ - if builder.config.llvm_tools_enabled || builder.config.lldb_enabled { + if builder.config.llvm_tools_enabled { if !target.contains("msvc") { if target.contains("apple") { cfg.define("CMAKE_EXE_LINKER_FLAGS", "-static-libstdc++"); @@ -186,17 +212,9 @@ impl Step for Llvm { enabled_llvm_projects.push("compiler-rt"); } - if builder.config.lldb_enabled { - enabled_llvm_projects.push("clang"); - enabled_llvm_projects.push("lldb"); - // For the time being, disable code signing. - cfg.define("LLDB_CODESIGN_IDENTITY", ""); - cfg.define("LLDB_NO_DEBUGSERVER", "ON"); - } else { - // LLDB requires libxml2; but otherwise we want it to be disabled. - // See https://github.com/rust-lang/rust/pull/50104 - cfg.define("LLVM_ENABLE_LIBXML2", "OFF"); - } + // We want libxml to be disabled. + // See https://github.com/rust-lang/rust/pull/50104 + cfg.define("LLVM_ENABLE_LIBXML2", "OFF"); if !enabled_llvm_projects.is_empty() { enabled_llvm_projects.sort(); @@ -235,10 +253,14 @@ impl Step for Llvm { if !suffix.is_empty() { cfg.define("LLVM_VERSION_SUFFIX", suffix); } + } else if builder.config.channel == "dev" { + // Changes to a version suffix require a complete rebuild of the LLVM. + // To avoid rebuilds during a time of version bump, don't include rustc + // release number on the dev channel. + cfg.define("LLVM_VERSION_SUFFIX", "-rust-dev"); } else { - let default_suffix = - format!("-rust-{}-{}", channel::CFG_RELEASE_NUM, builder.config.channel); - cfg.define("LLVM_VERSION_SUFFIX", default_suffix); + let suffix = format!("-rust-{}-{}", channel::CFG_RELEASE_NUM, builder.config.channel); + cfg.define("LLVM_VERSION_SUFFIX", suffix); } if let Some(ref linker) = builder.config.llvm_use_linker { @@ -285,11 +307,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = output(cmd.arg("--version")); 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 >= 7 { + if major >= 8 { return; } } - panic!("\n\nbad LLVM version: {}, need >=7.0\n\n", version) + panic!("\n\nbad LLVM version: {}, need >=8.0\n\n", version) } fn configure_cmake( @@ -475,10 +497,33 @@ impl Step for Lld { let llvm_config_shim = env::current_exe().unwrap().with_file_name("llvm-config-wrapper"); cfg.out_dir(&out_dir) .profile("Release") - .env("LLVM_CONFIG_REAL", llvm_config) + .env("LLVM_CONFIG_REAL", &llvm_config) .define("LLVM_CONFIG_PATH", llvm_config_shim) .define("LLVM_INCLUDE_TESTS", "OFF"); + // While we're using this horrible workaround to shim the execution of + // llvm-config, let's just pile on more. I can't seem to figure out how + // to build LLD as a standalone project and also cross-compile it at the + // same time. It wants a natively executable `llvm-config` to learn + // about LLVM, but then it learns about all the host configuration of + // LLVM and tries to link to host LLVM libraries. + // + // To work around that we tell our shim to replace anything with the + // build target with the actual target instead. This'll break parts of + // LLD though which try to execute host tools, such as llvm-tblgen, so + // we specifically tell it where to find those. This is likely super + // brittle and will break over time. If anyone knows better how to + // cross-compile LLD it would be much appreciated to fix this! + if target != builder.config.build { + cfg.env("LLVM_CONFIG_SHIM_REPLACE", &builder.config.build) + .env("LLVM_CONFIG_SHIM_REPLACE_WITH", &target) + .define("LLVM_TABLEGEN_EXE", llvm_config.with_file_name("llvm-tblgen")); + } + + // Explicitly set C++ standard, because upstream doesn't do so + // for standalone builds. + cfg.define("CMAKE_CXX_STANDARD", "14"); + cfg.build(); t!(File::create(&done_stamp)); diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs new file mode 100644 index 0000000000000..900534714277c --- /dev/null +++ b/src/bootstrap/run.rs @@ -0,0 +1,43 @@ +use crate::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::tool::Tool; +use std::process::Command; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct ExpandYamlAnchors; + +impl Step for ExpandYamlAnchors { + type Output = (); + + /// Runs the `expand-yaml_anchors` tool. + /// + /// This tool in `src/tools` read the CI configuration files written in YAML and expands the + /// anchors in them, since GitHub Actions doesn't support them. + fn run(self, builder: &Builder<'_>) { + builder.info("Expanding YAML anchors in the GitHub Actions configuration"); + try_run( + builder, + &mut builder.tool_cmd(Tool::ExpandYamlAnchors).arg("generate").arg(&builder.src), + ); + } + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/expand-yaml-anchors") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(ExpandYamlAnchors); + } +} + +fn try_run(builder: &Builder<'_>, cmd: &mut Command) -> bool { + if !builder.fail_fast { + if !builder.try_run(cmd) { + let mut failures = builder.delayed_failures.borrow_mut(); + failures.push(format!("{:?}", cmd)); + return false; + } + } else { + builder.run(cmd); + } + true +} diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 530e74da8cac0..74b47d0772837 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -117,14 +117,6 @@ pub fn check(build: &mut Build) { build.config.ninja = true; } } - - if build.config.lldb_enabled { - cmd_finder.must_have("swig"); - let out = output(Command::new("swig").arg("-version")); - if !out.contains("SWIG Version 3") && !out.contains("SWIG Version 4") { - panic!("Ensure that Swig 3.x.x or 4.x.x is installed."); - } - } } build.config.python = build @@ -132,8 +124,6 @@ pub fn check(build: &mut Build) { .python .take() .map(|p| cmd_finder.must_have(p)) - .or_else(|| cmd_finder.maybe_have("python2.7")) - .or_else(|| cmd_finder.maybe_have("python2")) .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py .or_else(|| Some(cmd_finder.must_have("python"))); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index b52fbe4666eb3..96196a80be466 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -21,7 +21,7 @@ use crate::flags::Subcommand; use crate::native; use crate::tool::{self, SourceType, Tool}; use crate::toolstate::ToolState; -use crate::util::{self, dylib_path, dylib_path_var}; +use crate::util::{self, add_link_lib_path, dylib_path, dylib_path_var}; use crate::Crate as CargoCrate; use crate::{envify, DocTests, GitRepo, Mode}; @@ -436,7 +436,6 @@ impl Step for Miri { // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", miri_sysroot); - cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); cargo.env("MIRI_PATH", miri); @@ -528,7 +527,7 @@ impl Step for Clippy { host, "test", "src/tools/clippy", - SourceType::Submodule, + SourceType::InTree, &[], ); @@ -548,9 +547,7 @@ impl Step for Clippy { builder.add_rustc_lib_path(compiler, &mut cargo); - if try_run(builder, &mut cargo.into()) { - builder.save_toolstate("clippy-driver", ToolState::TestPass); - } + try_run(builder, &mut cargo.into()); } else { eprintln!("failed to test clippy: could not build"); } @@ -627,8 +624,14 @@ impl Step for RustdocJSStd { if let Some(ref nodejs) = builder.config.nodejs { let mut command = Command::new(nodejs); command - .arg(builder.src.join("src/tools/rustdoc-js-std/tester.js")) + .arg(builder.src.join("src/tools/rustdoc-js/tester.js")) + .arg("--crate-name") + .arg("std") + .arg("--resource-suffix") + .arg(crate::channel::CFG_RELEASE_NUM) + .arg("--doc-folder") .arg(builder.doc_out(self.target)) + .arg("--test-folder") .arg(builder.src.join("src/test/rustdoc-js-std")); builder.ensure(crate::doc::Std { target: self.target, stage: builder.top_stage }); builder.run(&mut command); @@ -750,6 +753,35 @@ impl Step for Tidy { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct ExpandYamlAnchors; + +impl Step for ExpandYamlAnchors { + type Output = (); + const ONLY_HOSTS: bool = true; + + /// Ensure the `generate-ci-config` tool was run locally. + /// + /// The tool in `src/tools` reads the CI definition in `src/ci/builders.yml` and generates the + /// appropriate configuration for all our CI providers. This step ensures the tool was called + /// by the user before committing CI changes. + fn run(self, builder: &Builder<'_>) { + builder.info("Ensuring the YAML anchors in the GitHub Actions config were expanded"); + try_run( + builder, + &mut builder.tool_cmd(Tool::ExpandYamlAnchors).arg("check").arg(&builder.src), + ); + } + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/expand-yaml-anchors") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(ExpandYamlAnchors); + } +} + fn testdir(builder: &Builder<'_>, host: Interned) -> PathBuf { builder.out.join(host).join("test") } @@ -865,8 +897,6 @@ default_test!(CompileFail { suite: "compile-fail" }); -default_test!(RunFail { path: "src/test/run-fail", mode: "run-fail", suite: "run-fail" }); - default_test!(RunPassValgrind { path: "src/test/run-pass-valgrind", mode: "run-pass-valgrind", @@ -896,13 +926,6 @@ host_test!(UiFullDeps { path: "src/test/ui-fulldeps", mode: "ui", suite: "ui-ful host_test!(Rustdoc { path: "src/test/rustdoc", mode: "rustdoc", suite: "rustdoc" }); host_test!(Pretty { path: "src/test/pretty", mode: "pretty", suite: "pretty" }); -test!(RunFailPretty { - path: "src/test/run-fail/pretty", - mode: "pretty", - suite: "run-fail", - default: false, - host: true -}); test!(RunPassValgrindPretty { path: "src/test/run-pass-valgrind/pretty", mode: "pretty", @@ -1073,20 +1096,15 @@ impl Step for Compiletest { .to_string() }) }; - let lldb_exe = if builder.config.lldb_enabled { - // Test against the lldb that was just built. - builder.llvm_out(target).join("bin").join("lldb") - } else { - PathBuf::from("lldb") - }; - let lldb_version = Command::new(&lldb_exe) + let lldb_exe = "lldb"; + let lldb_version = Command::new(lldb_exe) .arg("--version") .output() .map(|output| String::from_utf8_lossy(&output.stdout).to_string()) .ok(); if let Some(ref vers) = lldb_version { cmd.arg("--lldb-version").arg(vers); - let lldb_python_dir = run(Command::new(&lldb_exe).arg("-P")).ok(); + let lldb_python_dir = run(Command::new(lldb_exe).arg("-P")).ok(); if let Some(ref dir) = lldb_python_dir { cmd.arg("--lldb-python-dir").arg(dir); } @@ -1149,6 +1167,15 @@ impl Step for Compiletest { cmd.arg("--system-llvm"); } + // Tests that use compiler libraries may inherit the `-lLLVM` link + // requirement, but the `-L` library path is not propagated across + // separate compilations. We can add LLVM's library path to the + // platform-specific environment variable as a workaround. + if !builder.config.dry_run && suite.ends_with("fulldeps") { + let llvm_libdir = output(Command::new(&llvm_config).arg("--libdir")); + add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd); + } + // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. if !builder.config.dry_run && suite == "run-make-fulldeps" { @@ -1494,7 +1521,7 @@ impl Step for RustcGuide { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/doc/rustc-guide") + run.path("src/doc/rustc-dev-guide") } fn make_run(run: RunConfig<'_>) { @@ -1502,14 +1529,14 @@ impl Step for RustcGuide { } fn run(self, builder: &Builder<'_>) { - let src = builder.src.join("src/doc/rustc-guide"); + let src = builder.src.join("src/doc/rustc-dev-guide"); let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); let toolstate = if try_run(builder, rustbook_cmd.arg("linkcheck").arg(&src)) { ToolState::TestPass } else { ToolState::TestFail }; - builder.save_toolstate("rustc-guide", toolstate); + builder.save_toolstate("rustc-dev-guide", toolstate); } } @@ -1681,7 +1708,7 @@ impl Step for Crate { let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand()); match mode { Mode::Std => { - compile::std_cargo(builder, target, &mut cargo); + compile::std_cargo(builder, target, compiler.stage, &mut cargo); } Mode::Rustc => { builder.ensure(compile::Rustc { compiler, target }); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index c8ccba467e509..8ebad95ea1762 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -378,6 +378,7 @@ bootstrap_tool!( RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; RustInstaller, "src/tools/rust-installer", "fabricate", is_external_tool = true; RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes"; + ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; ); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -606,7 +607,15 @@ macro_rules! tool_extended { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; - run.path($path).default_condition(builder.config.extended) + run.path($path).default_condition( + builder.config.extended + && builder.config.tools.as_ref().map_or(true, |tools| { + tools.iter().any(|tool| match tool.as_ref() { + "clippy" => $tool_name == "clippy-driver", + x => $tool_name == x, + }) + }), + ) } fn make_run(run: RunConfig<'_>) { @@ -643,14 +652,12 @@ tool_extended!((self, builder), Miri, miri, "src/tools/miri", "miri", {}; CargoMiri, miri, "src/tools/miri", "cargo-miri", {}; Rls, rls, "src/tools/rls", "rls", { - let clippy = builder.ensure(Clippy { + builder.ensure(Clippy { compiler: self.compiler, target: self.target, extra_features: Vec::new(), }); - if clippy.is_some() { - self.extra_features.push("clippy".to_owned()); - } + self.extra_features.push("clippy".to_owned()); }; Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {}; ); diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index f0e0f92af55fc..6c219cee01ee5 100644 --- a/src/bootstrap/toolstate.rs +++ b/src/bootstrap/toolstate.rs @@ -78,7 +78,6 @@ static STABLE_TOOLS: &[(&str, &str)] = &[ ("edition-guide", "src/doc/edition-guide"), ("rls", "src/tools/rls"), ("rustfmt", "src/tools/rustfmt"), - ("clippy-driver", "src/tools/clippy"), ]; // These tools are permitted to not build on the beta/stable channels. @@ -89,16 +88,16 @@ static STABLE_TOOLS: &[(&str, &str)] = &[ static NIGHTLY_TOOLS: &[(&str, &str)] = &[ ("miri", "src/tools/miri"), ("embedded-book", "src/doc/embedded-book"), - ("rustc-guide", "src/doc/rustc-guide"), + // ("rustc-dev-guide", "src/doc/rustc-dev-guide"), ]; fn print_error(tool: &str, submodule: &str) { - eprintln!(""); + eprintln!(); eprintln!("We detected that this PR updated '{}', but its tests failed.", tool); - eprintln!(""); + eprintln!(); eprintln!("If you do intend to update '{}', please check the error messages above and", tool); eprintln!("commit another update."); - eprintln!(""); + eprintln!(); eprintln!("If you do NOT intend to update '{}', please ensure you did not accidentally", tool); eprintln!("change the submodule at '{}'. You may ask your reviewer for the", submodule); eprintln!("proper steps."); diff --git a/src/ci/azure-pipelines/auto.yml b/src/ci/azure-pipelines/auto.yml index 13db4725ded2e..46d3cf7a38ca2 100644 --- a/src/ci/azure-pipelines/auto.yml +++ b/src/ci/azure-pipelines/auto.yml @@ -1,3 +1,14 @@ +##################################### +## READ BEFORE CHANGING THIS ## +##################################### + +# We're in the process of evaluating GitHub Actions as a possible replacement +# for Azure Pipelines, and at the moment the configuration is duplicated +# between the two CI providers. Be sure to also change the configuration in +# src/ci/github-actions when changing this file. + +##################################### + # # Azure Pipelines "auto" branch build for Rust on Linux, macOS, and Windows. # @@ -18,7 +29,7 @@ jobs: - template: steps/run.yml strategy: matrix: - x86_64-gnu-llvm-7: + x86_64-gnu-llvm-8: RUST_BACKTRACE: 1 dist-x86_64-linux: {} dist-x86_64-linux-alt: diff --git a/src/ci/azure-pipelines/master.yml b/src/ci/azure-pipelines/master.yml index 9c5a15a3cf4ed..485b80398c8e4 100644 --- a/src/ci/azure-pipelines/master.yml +++ b/src/ci/azure-pipelines/master.yml @@ -1,3 +1,14 @@ +##################################### +## READ BEFORE CHANGING THIS ## +##################################### + +# We're in the process of evaluating GitHub Actions as a possible replacement +# for Azure Pipelines, and at the moment the configuration is duplicated +# between the two CI providers. Be sure to also change the configuration in +# src/ci/github-actions when changing this file. + +##################################### + # # Azure Pipelines job to publish toolstate. Only triggers on pushes to master. # diff --git a/src/ci/azure-pipelines/pr.yml b/src/ci/azure-pipelines/pr.yml index 1f0be53677de2..1fc8d187242f8 100644 --- a/src/ci/azure-pipelines/pr.yml +++ b/src/ci/azure-pipelines/pr.yml @@ -1,3 +1,14 @@ +##################################### +## READ BEFORE CHANGING THIS ## +##################################### + +# We're in the process of evaluating GitHub Actions as a possible replacement +# for Azure Pipelines, and at the moment the configuration is duplicated +# between the two CI providers. Be sure to also change the configuration in +# src/ci/github-actions when changing this file. + +##################################### + # # Azure Pipelines pull request build for Rust # @@ -18,7 +29,7 @@ jobs: - template: steps/run.yml strategy: matrix: - x86_64-gnu-llvm-7: {} + x86_64-gnu-llvm-8: {} mingw-check: {} x86_64-gnu-tools: CI_ONLY_WHEN_SUBMODULES_CHANGED: 1 diff --git a/src/ci/azure-pipelines/steps/run.yml b/src/ci/azure-pipelines/steps/run.yml index ee9425aa1c51f..85ff3e52a841d 100644 --- a/src/ci/azure-pipelines/steps/run.yml +++ b/src/ci/azure-pipelines/steps/run.yml @@ -1,3 +1,14 @@ +##################################### +## READ BEFORE CHANGING THIS ## +##################################### + +# We're in the process of evaluating GitHub Actions as a possible replacement +# for Azure Pipelines, and at the moment the configuration is duplicated +# between the two CI providers. Be sure to also change the configuration in +# src/ci/github-actions when changing this file. + +##################################### + # FIXME(linux): need to configure core dumps, enable them, and then dump # backtraces on failure from all core dumps: # @@ -59,8 +70,8 @@ steps: displayName: Install InnoSetup condition: and(succeeded(), not(variables.SKIP_JOB)) -- bash: src/ci/scripts/windows-symlink-build-dir.sh - displayName: Ensure the build happens on C:\ instead of D:\ +- bash: src/ci/scripts/symlink-build-dir.sh + displayName: Ensure the build happens on a partition with enough space condition: and(succeeded(), not(variables.SKIP_JOB)) - bash: src/ci/scripts/disable-git-crlf-conversion.sh diff --git a/src/ci/azure-pipelines/try.yml b/src/ci/azure-pipelines/try.yml index 698608795e24d..38a0685e0f75a 100644 --- a/src/ci/azure-pipelines/try.yml +++ b/src/ci/azure-pipelines/try.yml @@ -1,3 +1,14 @@ +##################################### +## READ BEFORE CHANGING THIS ## +##################################### + +# We're in the process of evaluating GitHub Actions as a possible replacement +# for Azure Pipelines, and at the moment the configuration is duplicated +# between the two CI providers. Be sure to also change the configuration in +# src/ci/github-actions when changing this file. + +##################################### + pr: none trigger: - try diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 872f2c3467d20..95936d65432fa 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -138,15 +138,14 @@ $category > $option = $value -- $comment For targets: `arm-unknown-linux-gnueabi` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches +- Path and misc options > Patches origin = Bundled only - Target options > Target Architecture = arm - Target options > Architecture level = armv6 -- (+) - Target options > Floating point = software (no FPU) -- (\*) - Operating System > Target OS = linux -- Operating System > Linux kernel version = 3.2.72 -- Precise kernel -- C-library > glibc version = 2.16.0 -- C compiler > gcc version = 5.2.0 +- Operating System > Linux kernel version = 3.2.101 +- C-library > glibc version = 2.17.0 +- C compiler > gcc version = 8.3.0 - C compiler > C++ = ENABLE -- to cross compile LLVM ### `arm-linux-gnueabihf.config` @@ -154,17 +153,16 @@ For targets: `arm-unknown-linux-gnueabi` For targets: `arm-unknown-linux-gnueabihf` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches +- Path and misc options > Patches origin = Bundled only - Target options > Target Architecture = arm - Target options > Architecture level = armv6 -- (+) - Target options > Use specific FPU = vfp -- (+) - Target options > Floating point = hardware (FPU) -- (\*) - Target options > Default instruction set mode = arm -- (+) - Operating System > Target OS = linux -- Operating System > Linux kernel version = 3.2.72 -- Precise kernel -- C-library > glibc version = 2.16.0 -- C compiler > gcc version = 5.2.0 +- Operating System > Linux kernel version = 3.2.101 +- C-library > glibc version = 2.17.0 +- C compiler > gcc version = 8.3.0 - C compiler > C++ = ENABLE -- to cross compile LLVM ### `armv7-linux-gnueabihf.config` diff --git a/src/ci/docker/arm-android/Dockerfile b/src/ci/docker/arm-android/Dockerfile index b934d1ce97124..7751484816706 100644 --- a/src/ci/docker/arm-android/Dockerfile +++ b/src/ci/docker/arm-android/Dockerfile @@ -31,7 +31,7 @@ ENV TARGETS=arm-linux-androideabi ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/arm-14 -ENV SCRIPT python2.7 ../x.py test --target $TARGETS +ENV SCRIPT python3 ../x.py test --target $TARGETS COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/armhf-gnu/Dockerfile b/src/ci/docker/armhf-gnu/Dockerfile index 5373612279bca..d1bc70689e1e7 100644 --- a/src/ci/docker/armhf-gnu/Dockerfile +++ b/src/ci/docker/armhf-gnu/Dockerfile @@ -14,7 +14,7 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends \ libc6-dev \ libc6-dev-armhf-cross \ make \ - python2.7 \ + python3 \ qemu-system-arm \ xz-utils @@ -78,6 +78,6 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --qemu-armhf-rootfs=/tmp/rootfs -ENV SCRIPT python2.7 ../x.py test --target arm-unknown-linux-gnueabihf +ENV SCRIPT python3 ../x.py test --target arm-unknown-linux-gnueabihf ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/disabled/aarch64-gnu/Dockerfile b/src/ci/docker/disabled/aarch64-gnu/Dockerfile index b2a3ba3ec2600..9dd0435ac4f0c 100644 --- a/src/ci/docker/disabled/aarch64-gnu/Dockerfile +++ b/src/ci/docker/disabled/aarch64-gnu/Dockerfile @@ -14,7 +14,7 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends \ libc6-dev \ libc6-dev-arm64-cross \ make \ - python2.7 \ + python3 \ qemu-system-aarch64 \ xz-utils @@ -75,5 +75,5 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS \ --qemu-aarch64-rootfs=/tmp/rootfs -ENV SCRIPT python2.7 ../x.py test --target aarch64-unknown-linux-gnu +ENV SCRIPT python3 ../x.py test --target aarch64-unknown-linux-gnu ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/disabled/asmjs/Dockerfile b/src/ci/docker/disabled/asmjs/Dockerfile index e27a2a529a8ca..3fa65511e94f7 100644 --- a/src/ci/docker/disabled/asmjs/Dockerfile +++ b/src/ci/docker/disabled/asmjs/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python \ + python3 \ git \ cmake \ sudo \ @@ -33,7 +33,7 @@ ENV EMCC_CFLAGS=-O1 # Emscripten installation is user-specific ENV NO_CHANGE_USER=1 -ENV SCRIPT python2.7 ../x.py test --target $TARGETS +ENV SCRIPT python3 ../x.py test --target $TARGETS # This is almost identical to the wasm32-unknown-emscripten target, so # running with assertions again is not useful diff --git a/src/ci/docker/disabled/dist-aarch64-android/Dockerfile b/src/ci/docker/disabled/dist-aarch64-android/Dockerfile index a7903b6f42501..dea445c295c99 100644 --- a/src/ci/docker/disabled/dist-aarch64-android/Dockerfile +++ b/src/ci/docker/disabled/dist-aarch64-android/Dockerfile @@ -19,7 +19,7 @@ ENV RUST_CONFIGURE_ARGS \ --enable-extended \ --enable-cargo-openssl-static -ENV SCRIPT python2.7 ../x.py dist --target $HOSTS --host $HOSTS +ENV SCRIPT python3 ../x.py dist --target $HOSTS --host $HOSTS COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/disabled/dist-armv7-android/Dockerfile b/src/ci/docker/disabled/dist-armv7-android/Dockerfile index c02a5e5a09542..7227c41ccca9a 100644 --- a/src/ci/docker/disabled/dist-armv7-android/Dockerfile +++ b/src/ci/docker/disabled/dist-armv7-android/Dockerfile @@ -33,11 +33,11 @@ ENV RUST_CONFIGURE_ARGS \ # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). ENV SCRIPT \ - python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ + python3 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ rm /android/ndk/arm && \ ln -s /android/ndk/arm-14 /android/ndk/arm && \ - python2.7 ../x.py dist --host $HOSTS --target $HOSTS) + python3 ../x.py dist --host $HOSTS --target $HOSTS) COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/disabled/dist-i686-android/Dockerfile b/src/ci/docker/disabled/dist-i686-android/Dockerfile index 04e83a431c455..b74dcefa3516c 100644 --- a/src/ci/docker/disabled/dist-i686-android/Dockerfile +++ b/src/ci/docker/disabled/dist-i686-android/Dockerfile @@ -33,11 +33,11 @@ ENV RUST_CONFIGURE_ARGS \ # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). ENV SCRIPT \ - python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ + python3 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ rm /android/ndk/x86 && \ ln -s /android/ndk/x86-14 /android/ndk/x86 && \ - python2.7 ../x.py dist --host $HOSTS --target $HOSTS) + python3 ../x.py dist --host $HOSTS --target $HOSTS) COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/disabled/dist-powerpcspe-linux/Dockerfile b/src/ci/docker/disabled/dist-powerpcspe-linux/Dockerfile index 3227819dad54d..19df9d6cf6231 100644 --- a/src/ci/docker/disabled/dist-powerpcspe-linux/Dockerfile +++ b/src/ci/docker/disabled/dist-powerpcspe-linux/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -23,4 +23,4 @@ RUN sh /scripts/sccache.sh ENV HOSTS=powerpc-unknown-linux-gnuspe ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/disabled/dist-sparc64-linux/Dockerfile b/src/ci/docker/disabled/dist-sparc64-linux/Dockerfile index 952c265a1390b..62d0bfc71b2fa 100644 --- a/src/ci/docker/disabled/dist-sparc64-linux/Dockerfile +++ b/src/ci/docker/disabled/dist-sparc64-linux/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -23,4 +23,4 @@ RUN sh /scripts/sccache.sh ENV HOSTS=sparc64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/disabled/dist-x86_64-android/Dockerfile b/src/ci/docker/disabled/dist-x86_64-android/Dockerfile index 937301864cd05..d44779763e544 100644 --- a/src/ci/docker/disabled/dist-x86_64-android/Dockerfile +++ b/src/ci/docker/disabled/dist-x86_64-android/Dockerfile @@ -19,7 +19,7 @@ ENV RUST_CONFIGURE_ARGS \ --enable-extended \ --enable-cargo-openssl-static -ENV SCRIPT python2.7 ../x.py dist --target $HOSTS --host $HOSTS +ENV SCRIPT python3 ../x.py dist --target $HOSTS --host $HOSTS COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/disabled/dist-x86_64-dragonfly/Dockerfile b/src/ci/docker/disabled/dist-x86_64-dragonfly/Dockerfile index dbff9e32e1311..5b7196c573c8b 100644 --- a/src/ci/docker/disabled/dist-x86_64-dragonfly/Dockerfile +++ b/src/ci/docker/disabled/dist-x86_64-dragonfly/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -33,4 +33,4 @@ ENV \ ENV HOSTS=x86_64-unknown-dragonfly ENV RUST_CONFIGURE_ARGS --enable-extended -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/disabled/dist-x86_64-haiku/Dockerfile b/src/ci/docker/disabled/dist-x86_64-haiku/Dockerfile index 440afd7c97f5e..5b65335bfe4e4 100644 --- a/src/ci/docker/disabled/dist-x86_64-haiku/Dockerfile +++ b/src/ci/docker/disabled/dist-x86_64-haiku/Dockerfile @@ -18,7 +18,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ make \ nasm \ pkg-config \ - python2.7 \ + python3 \ sudo \ texinfo \ wget \ @@ -46,4 +46,4 @@ ENV RUST_CONFIGURE_ARGS --disable-jemalloc \ --set=$TARGET.cc=x86_64-unknown-haiku-gcc \ --set=$TARGET.cxx=x86_64-unknown-haiku-g++ \ --set=$TARGET.llvm-config=/bin/llvm-config-haiku -ENV SCRIPT python2.7 ../x.py dist --host=$HOST --target=$HOST +ENV SCRIPT python3 ../x.py dist --host=$HOST --target=$HOST diff --git a/src/ci/docker/disabled/dist-x86_64-redox/Dockerfile b/src/ci/docker/disabled/dist-x86_64-redox/Dockerfile index 11a3acd68e3e8..7b53e73d700ba 100644 --- a/src/ci/docker/disabled/dist-x86_64-redox/Dockerfile +++ b/src/ci/docker/disabled/dist-x86_64-redox/Dockerfile @@ -19,4 +19,4 @@ ENV \ CXX_x86_64_unknown_redox=x86_64-unknown-redox-g++ ENV RUST_CONFIGURE_ARGS --enable-extended -ENV SCRIPT python2.7 ../x.py dist --target x86_64-unknown-redox +ENV SCRIPT python3 ../x.py dist --target x86_64-unknown-redox diff --git a/src/ci/docker/dist-aarch64-linux/Dockerfile b/src/ci/docker/dist-aarch64-linux/Dockerfile index f5eb66ed7142f..74766dc970d9f 100644 --- a/src/ci/docker/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/dist-aarch64-linux/Dockerfile @@ -33,7 +33,7 @@ ENV CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnueabi-gcc \ ENV HOSTS=aarch64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS \ - --enable-extended \ + --enable-full-tools \ --enable-profiler \ --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-android/Dockerfile b/src/ci/docker/dist-android/Dockerfile index 64f31750dd7b6..6d38e199564b1 100644 --- a/src/ci/docker/dist-android/Dockerfile +++ b/src/ci/docker/dist-android/Dockerfile @@ -32,7 +32,7 @@ ENV RUST_CONFIGURE_ARGS \ --x86_64-linux-android-ndk=/android/ndk/x86_64-21 \ --disable-docs -ENV SCRIPT python2.7 ../x.py dist --target $TARGETS +ENV SCRIPT python3 ../x.py dist --target $TARGETS COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/dist-arm-linux/Dockerfile b/src/ci/docker/dist-arm-linux/Dockerfile index 48851ae232c99..06870a8a982f3 100644 --- a/src/ci/docker/dist-arm-linux/Dockerfile +++ b/src/ci/docker/dist-arm-linux/Dockerfile @@ -3,20 +3,14 @@ FROM ubuntu:16.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -# Ubuntu 16.04 (this container) ships with make 4, but something in the -# toolchains we build below chokes on that, so go back to make 3 -COPY scripts/make3.sh /scripts/ -RUN sh /scripts/make3.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh +COPY scripts/crosstool-ng-1.24.sh /scripts/ +RUN sh /scripts/crosstool-ng-1.24.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh USER rustbuild WORKDIR /tmp -COPY dist-arm-linux/patches/ /tmp/patches/ COPY dist-arm-linux/arm-linux-gnueabi.config dist-arm-linux/build-toolchains.sh /tmp/ RUN ./build-toolchains.sh @@ -33,5 +27,5 @@ ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \ ENV HOSTS=arm-unknown-linux-gnueabi -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS --enable-full-tools --disable-docs +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-arm-linux/arm-linux-gnueabi.config b/src/ci/docker/dist-arm-linux/arm-linux-gnueabi.config index 4185112d8be90..1dcdbd1a9008b 100644 --- a/src/ci/docker/dist-arm-linux/arm-linux-gnueabi.config +++ b/src/ci/docker/dist-arm-linux/arm-linux-gnueabi.config @@ -1,9 +1,29 @@ # # Automatically generated file; DO NOT EDIT. -# Crosstool-NG Configuration -# -CT_CONFIGURE_has_make381=y -CT_CONFIGURE_has_xz=y +# crosstool-NG Configuration +# +CT_CONFIGURE_has_static_link=y +CT_CONFIGURE_has_cxx11=y +CT_CONFIGURE_has_wget=y +CT_CONFIGURE_has_curl=y +CT_CONFIGURE_has_make_3_81_or_newer=y +CT_CONFIGURE_has_make_4_0_or_newer=y +CT_CONFIGURE_has_libtool_2_4_or_newer=y +CT_CONFIGURE_has_libtoolize_2_4_or_newer=y +CT_CONFIGURE_has_autoconf_2_65_or_newer=y +CT_CONFIGURE_has_autoreconf_2_65_or_newer=y +CT_CONFIGURE_has_automake_1_15_or_newer=y +CT_CONFIGURE_has_gnu_m4_1_4_12_or_newer=y +CT_CONFIGURE_has_bison_2_7_or_newer=y +CT_CONFIGURE_has_python=y +CT_CONFIGURE_has_git=y +CT_CONFIGURE_has_md5sum=y +CT_CONFIGURE_has_sha1sum=y +CT_CONFIGURE_has_sha256sum=y +CT_CONFIGURE_has_sha512sum=y +CT_CONFIGURE_has_install_with_strip_program=y +CT_CONFIG_VERSION_CURRENT="3" +CT_CONFIG_VERSION="3" CT_MODULES=y # @@ -20,41 +40,48 @@ CT_MODULES=y # # Paths # -CT_LOCAL_TARBALLS_DIR="" +CT_LOCAL_TARBALLS_DIR="${HOME}/src" +CT_SAVE_TARBALLS=y +# CT_TARBALLS_BUILDROOT_LAYOUT is not set CT_WORK_DIR="${CT_TOP_DIR}/.build" +CT_BUILD_TOP_DIR="${CT_WORK_DIR:-${CT_TOP_DIR}/.build}/${CT_HOST:+HOST-${CT_HOST}/}${CT_TARGET}" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" -CT_INSTALL_DIR="${CT_PREFIX_DIR}" CT_RM_RF_PREFIX_DIR=y CT_REMOVE_DOCS=y -CT_INSTALL_DIR_RO=y +CT_INSTALL_LICENSES=y +CT_PREFIX_DIR_RO=y CT_STRIP_HOST_TOOLCHAIN_EXECUTABLES=y # CT_STRIP_TARGET_TOOLCHAIN_EXECUTABLES is not set # # Downloading # +CT_DOWNLOAD_AGENT_WGET=y +# CT_DOWNLOAD_AGENT_CURL is not set +# CT_DOWNLOAD_AGENT_NONE is not set # CT_FORBID_DOWNLOAD is not set # CT_FORCE_DOWNLOAD is not set CT_CONNECT_TIMEOUT=10 +CT_DOWNLOAD_WGET_OPTIONS="--passive-ftp --tries=3 -nc --progress=dot:binary" # CT_ONLY_DOWNLOAD is not set # CT_USE_MIRROR is not set +CT_VERIFY_DOWNLOAD_DIGEST=y +CT_VERIFY_DOWNLOAD_DIGEST_SHA512=y +# CT_VERIFY_DOWNLOAD_DIGEST_SHA256 is not set +# CT_VERIFY_DOWNLOAD_DIGEST_SHA1 is not set +# CT_VERIFY_DOWNLOAD_DIGEST_MD5 is not set +CT_VERIFY_DOWNLOAD_DIGEST_ALG="sha512" +# CT_VERIFY_DOWNLOAD_SIGNATURE is not set # # Extracting # # CT_FORCE_EXTRACT is not set -CT_OVERIDE_CONFIG_GUESS_SUB=y +CT_OVERRIDE_CONFIG_GUESS_SUB=y # CT_ONLY_EXTRACT is not set -# CT_PATCH_BUNDLED is not set -# CT_PATCH_LOCAL is not set -CT_PATCH_BUNDLED_LOCAL=y -# CT_PATCH_LOCAL_BUNDLED is not set -# CT_PATCH_BUNDLED_FALLBACK_LOCAL is not set -# CT_PATCH_LOCAL_FALLBACK_BUNDLED is not set -# CT_PATCH_NONE is not set -CT_PATCH_ORDER="bundled,local" -CT_PATCH_USE_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" +CT_PATCH_BUNDLED=y +# CT_PATCH_BUNDLED_LOCAL is not set +CT_PATCH_ORDER="bundled" # # Build behavior @@ -77,11 +104,11 @@ CT_CONFIG_SHELL="${bash}" # # CT_LOG_ERROR is not set # CT_LOG_WARN is not set -CT_LOG_INFO=y -# CT_LOG_EXTRA is not set +# CT_LOG_INFO is not set +CT_LOG_EXTRA=y # CT_LOG_ALL is not set # CT_LOG_DEBUG is not set -CT_LOG_LEVEL_MAX="INFO" +CT_LOG_LEVEL_MAX="EXTRA" # CT_LOG_SEE_TOOLS_WARN is not set CT_LOG_PROGRESS_BAR=y CT_LOG_TO_FILE=y @@ -90,85 +117,86 @@ CT_LOG_FILE_COMPRESS=y # # Target options # +# CT_ARCH_ALPHA is not set +# CT_ARCH_ARC is not set +CT_ARCH_ARM=y +# CT_ARCH_AVR is not set +# CT_ARCH_M68K is not set +# CT_ARCH_MIPS is not set +# CT_ARCH_NIOS2 is not set +# CT_ARCH_POWERPC is not set +# CT_ARCH_S390 is not set +# CT_ARCH_SH is not set +# CT_ARCH_SPARC is not set +# CT_ARCH_X86 is not set +# CT_ARCH_XTENSA is not set CT_ARCH="arm" -CT_ARCH_SUPPORTS_BOTH_MMU=y -CT_ARCH_SUPPORTS_BOTH_ENDIAN=y -CT_ARCH_SUPPORTS_32=y -CT_ARCH_SUPPORTS_64=y -CT_ARCH_SUPPORTS_WITH_ARCH=y -CT_ARCH_SUPPORTS_WITH_CPU=y -CT_ARCH_SUPPORTS_WITH_TUNE=y -CT_ARCH_SUPPORTS_WITH_FLOAT=y -CT_ARCH_SUPPORTS_WITH_FPU=y -CT_ARCH_SUPPORTS_SOFTFP=y -CT_ARCH_DEFAULT_HAS_MMU=y -CT_ARCH_DEFAULT_LE=y -CT_ARCH_DEFAULT_32=y -CT_ARCH_ARCH="armv6" +CT_ARCH_CHOICE_KSYM="ARM" +# CT_ARCH_ALPHA_EV4 is not set +# CT_ARCH_ALPHA_EV45 is not set +# CT_ARCH_ALPHA_EV5 is not set +# CT_ARCH_ALPHA_EV56 is not set +# CT_ARCH_ALPHA_EV6 is not set +# CT_ARCH_ALPHA_EV67 is not set CT_ARCH_CPU="" CT_ARCH_TUNE="" -CT_ARCH_FPU="" -# CT_ARCH_BE is not set -CT_ARCH_LE=y -CT_ARCH_32=y -# CT_ARCH_64 is not set -CT_ARCH_BITNESS=32 -# CT_ARCH_FLOAT_HW is not set -CT_ARCH_FLOAT_SW=y -CT_TARGET_CFLAGS="" -CT_TARGET_LDFLAGS="" -# CT_ARCH_alpha is not set -CT_ARCH_arm=y -# CT_ARCH_avr is not set -# CT_ARCH_m68k is not set -# CT_ARCH_mips is not set -# CT_ARCH_nios2 is not set -# CT_ARCH_powerpc is not set -# CT_ARCH_s390 is not set -# CT_ARCH_sh is not set -# CT_ARCH_sparc is not set -# CT_ARCH_x86 is not set -# CT_ARCH_xtensa is not set -CT_ARCH_alpha_AVAILABLE=y -CT_ARCH_arm_AVAILABLE=y -CT_ARCH_avr_AVAILABLE=y -CT_ARCH_m68k_AVAILABLE=y -CT_ARCH_microblaze_AVAILABLE=y -CT_ARCH_mips_AVAILABLE=y -CT_ARCH_nios2_AVAILABLE=y -CT_ARCH_powerpc_AVAILABLE=y -CT_ARCH_s390_AVAILABLE=y -CT_ARCH_sh_AVAILABLE=y -CT_ARCH_sparc_AVAILABLE=y -CT_ARCH_x86_AVAILABLE=y -CT_ARCH_xtensa_AVAILABLE=y +CT_ARCH_ARM_SHOW=y + +# +# Options for arm +# +CT_ARCH_ARM_PKG_KSYM="" +CT_ARCH_ARM_MODE="arm" +CT_ARCH_ARM_MODE_ARM=y +# CT_ARCH_ARM_MODE_THUMB is not set +# CT_ARCH_ARM_INTERWORKING is not set +CT_ARCH_ARM_EABI_FORCE=y +CT_ARCH_ARM_EABI=y +CT_ALL_ARCH_CHOICES="ALPHA ARC ARM AVR M68K MICROBLAZE MIPS MOXIE MSP430 NIOS2 POWERPC RISCV S390 SH SPARC X86 XTENSA" CT_ARCH_SUFFIX="" +# CT_OMIT_TARGET_VENDOR is not set # # Generic target options # # CT_MULTILIB is not set +CT_DEMULTILIB=y +CT_ARCH_SUPPORTS_BOTH_MMU=y +CT_ARCH_DEFAULT_HAS_MMU=y CT_ARCH_USE_MMU=y +CT_ARCH_SUPPORTS_FLAT_FORMAT=y +CT_ARCH_SUPPORTS_EITHER_ENDIAN=y +CT_ARCH_DEFAULT_LE=y +# CT_ARCH_BE is not set +CT_ARCH_LE=y CT_ARCH_ENDIAN="little" +CT_ARCH_SUPPORTS_32=y +CT_ARCH_SUPPORTS_64=y +CT_ARCH_DEFAULT_32=y +CT_ARCH_BITNESS=32 +CT_ARCH_32=y +# CT_ARCH_64 is not set # # Target optimisations # +CT_ARCH_SUPPORTS_WITH_ARCH=y +CT_ARCH_SUPPORTS_WITH_CPU=y +CT_ARCH_SUPPORTS_WITH_TUNE=y +CT_ARCH_SUPPORTS_WITH_FLOAT=y +CT_ARCH_SUPPORTS_WITH_FPU=y +CT_ARCH_SUPPORTS_SOFTFP=y CT_ARCH_EXCLUSIVE_WITH_CPU=y +CT_ARCH_ARCH="armv6" +CT_ARCH_FPU="" # CT_ARCH_FLOAT_AUTO is not set +# CT_ARCH_FLOAT_HW is not set # CT_ARCH_FLOAT_SOFTFP is not set +CT_ARCH_FLOAT_SW=y +CT_TARGET_CFLAGS="" +CT_TARGET_LDFLAGS="" CT_ARCH_FLOAT="soft" -# -# arm other options -# -CT_ARCH_ARM_MODE="arm" -CT_ARCH_ARM_MODE_ARM=y -# CT_ARCH_ARM_MODE_THUMB is not set -# CT_ARCH_ARM_INTERWORKING is not set -CT_ARCH_ARM_EABI_FORCE=y -CT_ARCH_ARM_EABI=y - # # Toolchain options # @@ -181,7 +209,9 @@ CT_USE_SYSROOT=y CT_SYSROOT_NAME="sysroot" CT_SYSROOT_DIR_PREFIX="" CT_WANTS_STATIC_LINK=y +CT_WANTS_STATIC_LINK_CXX=y # CT_STATIC_TOOLCHAIN is not set +CT_SHOW_CT_VERSION=y CT_TOOLCHAIN_PKGVERSION="" CT_TOOLCHAIN_BUGURL="" @@ -215,126 +245,215 @@ CT_BUILD_SUFFIX="" # Operating System # CT_KERNEL_SUPPORTS_SHARED_LIBS=y +# CT_KERNEL_BARE_METAL is not set +CT_KERNEL_LINUX=y CT_KERNEL="linux" -CT_KERNEL_VERSION="3.2.72" -# CT_KERNEL_bare_metal is not set -CT_KERNEL_linux=y -CT_KERNEL_bare_metal_AVAILABLE=y -CT_KERNEL_linux_AVAILABLE=y -# CT_KERNEL_V_4_3 is not set -# CT_KERNEL_V_4_2 is not set -# CT_KERNEL_V_4_1 is not set -# CT_KERNEL_V_3_18 is not set -# CT_KERNEL_V_3_14 is not set -# CT_KERNEL_V_3_12 is not set -# CT_KERNEL_V_3_10 is not set -# CT_KERNEL_V_3_4 is not set -CT_KERNEL_V_3_2=y -# CT_KERNEL_V_2_6_32 is not set -# CT_KERNEL_LINUX_CUSTOM is not set -CT_KERNEL_windows_AVAILABLE=y - -# -# Common kernel options -# -CT_SHARED_LIBS=y - -# -# linux other options -# +CT_KERNEL_CHOICE_KSYM="LINUX" +CT_KERNEL_LINUX_SHOW=y + +# +# Options for linux +# +CT_KERNEL_LINUX_PKG_KSYM="LINUX" +CT_LINUX_DIR_NAME="linux" +CT_LINUX_PKG_NAME="linux" +CT_LINUX_SRC_RELEASE=y +CT_LINUX_PATCH_ORDER="global" +# CT_LINUX_V_4_20 is not set +# CT_LINUX_V_4_19 is not set +# CT_LINUX_V_4_18 is not set +# CT_LINUX_V_4_17 is not set +# CT_LINUX_V_4_16 is not set +# CT_LINUX_V_4_15 is not set +# CT_LINUX_V_4_14 is not set +# CT_LINUX_V_4_13 is not set +# CT_LINUX_V_4_12 is not set +# CT_LINUX_V_4_11 is not set +# CT_LINUX_V_4_10 is not set +# CT_LINUX_V_4_9 is not set +# CT_LINUX_V_4_4 is not set +# CT_LINUX_V_4_1 is not set +# CT_LINUX_V_3_16 is not set +# CT_LINUX_V_3_13 is not set +# CT_LINUX_V_3_12 is not set +# CT_LINUX_V_3_10 is not set +# CT_LINUX_V_3_4 is not set +CT_LINUX_V_3_2=y +# CT_LINUX_V_2_6_32 is not set +# CT_LINUX_NO_VERSIONS is not set +CT_LINUX_VERSION="3.2.101" +CT_LINUX_MIRRORS="$(CT_Mirrors kernel.org linux ${CT_LINUX_VERSION})" +CT_LINUX_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_LINUX_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_LINUX_ARCHIVE_FORMATS=".tar.xz .tar.gz" +CT_LINUX_SIGNATURE_FORMAT="unpacked/.sign" +CT_LINUX_4_8_or_older=y +CT_LINUX_older_than_4_8=y +CT_LINUX_3_7_or_older=y +CT_LINUX_older_than_3_7=y +CT_LINUX_later_than_3_2=y +CT_LINUX_3_2_or_later=y CT_KERNEL_LINUX_VERBOSITY_0=y # CT_KERNEL_LINUX_VERBOSITY_1 is not set # CT_KERNEL_LINUX_VERBOSITY_2 is not set CT_KERNEL_LINUX_VERBOSE_LEVEL=0 CT_KERNEL_LINUX_INSTALL_CHECK=y +CT_ALL_KERNEL_CHOICES="BARE_METAL LINUX WINDOWS" + +# +# Common kernel options +# +CT_SHARED_LIBS=y # # Binary utilities # CT_ARCH_BINFMT_ELF=y +CT_BINUTILS_BINUTILS=y CT_BINUTILS="binutils" -CT_BINUTILS_binutils=y +CT_BINUTILS_CHOICE_KSYM="BINUTILS" +CT_BINUTILS_BINUTILS_SHOW=y + +# +# Options for binutils +# +CT_BINUTILS_BINUTILS_PKG_KSYM="BINUTILS" +CT_BINUTILS_DIR_NAME="binutils" +CT_BINUTILS_USE_GNU=y +CT_BINUTILS_USE="BINUTILS" +CT_BINUTILS_PKG_NAME="binutils" +CT_BINUTILS_SRC_RELEASE=y +CT_BINUTILS_PATCH_ORDER="global" +CT_BINUTILS_V_2_32=y +# CT_BINUTILS_V_2_31 is not set +# CT_BINUTILS_V_2_30 is not set +# CT_BINUTILS_V_2_29 is not set +# CT_BINUTILS_V_2_28 is not set +# CT_BINUTILS_V_2_27 is not set +# CT_BINUTILS_V_2_26 is not set +# CT_BINUTILS_NO_VERSIONS is not set +CT_BINUTILS_VERSION="2.32" +CT_BINUTILS_MIRRORS="$(CT_Mirrors GNU binutils) $(CT_Mirrors sourceware binutils/releases)" +CT_BINUTILS_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_BINUTILS_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_BINUTILS_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz" +CT_BINUTILS_SIGNATURE_FORMAT="packed/.sig" +CT_BINUTILS_later_than_2_30=y +CT_BINUTILS_2_30_or_later=y +CT_BINUTILS_later_than_2_27=y +CT_BINUTILS_2_27_or_later=y +CT_BINUTILS_later_than_2_25=y +CT_BINUTILS_2_25_or_later=y +CT_BINUTILS_later_than_2_23=y +CT_BINUTILS_2_23_or_later=y # # GNU binutils # -# CT_CC_BINUTILS_SHOW_LINARO is not set -CT_BINUTILS_V_2_25_1=y -# CT_BINUTILS_V_2_25 is not set -# CT_BINUTILS_V_2_24 is not set -# CT_BINUTILS_V_2_23_2 is not set -# CT_BINUTILS_V_2_23_1 is not set -# CT_BINUTILS_V_2_22 is not set -# CT_BINUTILS_V_2_21_53 is not set -# CT_BINUTILS_V_2_21_1a is not set -# CT_BINUTILS_V_2_20_1a is not set -# CT_BINUTILS_V_2_19_1a is not set -# CT_BINUTILS_V_2_18a is not set -CT_BINUTILS_VERSION="2.25.1" -CT_BINUTILS_2_25_1_or_later=y -CT_BINUTILS_2_25_or_later=y -CT_BINUTILS_2_24_or_later=y -CT_BINUTILS_2_23_or_later=y -CT_BINUTILS_2_22_or_later=y -CT_BINUTILS_2_21_or_later=y -CT_BINUTILS_2_20_or_later=y -CT_BINUTILS_2_19_or_later=y -CT_BINUTILS_2_18_or_later=y CT_BINUTILS_HAS_HASH_STYLE=y CT_BINUTILS_HAS_GOLD=y -CT_BINUTILS_GOLD_SUPPORTS_ARCH=y -CT_BINUTILS_GOLD_SUPPORT=y CT_BINUTILS_HAS_PLUGINS=y CT_BINUTILS_HAS_PKGVERSION_BUGURL=y -CT_BINUTILS_FORCE_LD_BFD=y +CT_BINUTILS_GOLD_SUPPORTS_ARCH=y +CT_BINUTILS_GOLD_SUPPORT=y +CT_BINUTILS_FORCE_LD_BFD_DEFAULT=y CT_BINUTILS_LINKER_LD=y # CT_BINUTILS_LINKER_LD_GOLD is not set -# CT_BINUTILS_LINKER_GOLD_LD is not set CT_BINUTILS_LINKERS_LIST="ld" CT_BINUTILS_LINKER_DEFAULT="bfd" # CT_BINUTILS_PLUGINS is not set +CT_BINUTILS_RELRO=m CT_BINUTILS_EXTRA_CONFIG_ARRAY="" # CT_BINUTILS_FOR_TARGET is not set - -# -# binutils other options -# +CT_ALL_BINUTILS_CHOICES="BINUTILS" # # C-library # +CT_LIBC_GLIBC=y +# CT_LIBC_NEWLIB is not set +# CT_LIBC_NONE is not set +# CT_LIBC_UCLIBC is not set CT_LIBC="glibc" -CT_LIBC_VERSION="2.16.0" -CT_LIBC_glibc=y -# CT_LIBC_musl is not set -# CT_LIBC_uClibc is not set -CT_LIBC_avr_libc_AVAILABLE=y -CT_LIBC_glibc_AVAILABLE=y +CT_LIBC_CHOICE_KSYM="GLIBC" CT_THREADS="nptl" -# CT_CC_GLIBC_SHOW_LINARO is not set -# CT_LIBC_GLIBC_V_2_22 is not set -# CT_LIBC_GLIBC_V_2_21 is not set -# CT_LIBC_GLIBC_V_2_20 is not set -# CT_LIBC_GLIBC_V_2_19 is not set -# CT_LIBC_GLIBC_V_2_18 is not set -# CT_LIBC_GLIBC_V_2_17 is not set -CT_LIBC_GLIBC_V_2_16_0=y -# CT_LIBC_GLIBC_V_2_15 is not set -# CT_LIBC_GLIBC_V_2_14_1 is not set -# CT_LIBC_GLIBC_V_2_14 is not set -# CT_LIBC_GLIBC_V_2_13 is not set -# CT_LIBC_GLIBC_V_2_12_2 is not set -# CT_LIBC_GLIBC_V_2_12_1 is not set -# CT_LIBC_GLIBC_V_2_11_1 is not set -# CT_LIBC_GLIBC_V_2_11 is not set -# CT_LIBC_GLIBC_V_2_10_1 is not set -# CT_LIBC_GLIBC_V_2_9 is not set -# CT_LIBC_GLIBC_V_2_8 is not set -CT_LIBC_mingw_AVAILABLE=y -CT_LIBC_musl_AVAILABLE=y -CT_LIBC_newlib_AVAILABLE=y -CT_LIBC_none_AVAILABLE=y -CT_LIBC_uClibc_AVAILABLE=y +CT_LIBC_GLIBC_SHOW=y + +# +# Options for glibc +# +CT_LIBC_GLIBC_PKG_KSYM="GLIBC" +CT_GLIBC_DIR_NAME="glibc" +CT_GLIBC_USE_GNU=y +CT_GLIBC_USE="GLIBC" +CT_GLIBC_PKG_NAME="glibc" +CT_GLIBC_SRC_RELEASE=y +CT_GLIBC_PATCH_ORDER="global" +# CT_GLIBC_V_2_28 is not set +# CT_GLIBC_V_2_27 is not set +# CT_GLIBC_V_2_26 is not set +# CT_GLIBC_V_2_25 is not set +# CT_GLIBC_V_2_24 is not set +# CT_GLIBC_V_2_23 is not set +# CT_GLIBC_V_2_19 is not set +CT_GLIBC_V_2_17=y +# CT_GLIBC_V_2_12_1 is not set +# CT_GLIBC_NO_VERSIONS is not set +CT_GLIBC_VERSION="2.17" +CT_GLIBC_MIRRORS="$(CT_Mirrors GNU glibc)" +CT_GLIBC_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GLIBC_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GLIBC_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz" +CT_GLIBC_SIGNATURE_FORMAT="packed/.sig" +CT_GLIBC_2_29_or_older=y +CT_GLIBC_older_than_2_29=y +CT_GLIBC_REQUIRE_older_than_2_29=y +CT_GLIBC_2_27_or_older=y +CT_GLIBC_older_than_2_27=y +CT_GLIBC_2_26_or_older=y +CT_GLIBC_older_than_2_26=y +CT_GLIBC_2_25_or_older=y +CT_GLIBC_older_than_2_25=y +CT_GLIBC_2_24_or_older=y +CT_GLIBC_older_than_2_24=y +CT_GLIBC_2_23_or_older=y +CT_GLIBC_older_than_2_23=y +CT_GLIBC_2_20_or_older=y +CT_GLIBC_older_than_2_20=y +CT_GLIBC_2_17_or_later=y +CT_GLIBC_2_17_or_older=y +CT_GLIBC_later_than_2_14=y +CT_GLIBC_2_14_or_later=y +CT_GLIBC_DEP_KERNEL_HEADERS_VERSION=y +CT_GLIBC_DEP_BINUTILS=y +CT_GLIBC_DEP_GCC=y +CT_GLIBC_DEP_PYTHON=y +CT_GLIBC_HAS_NPTL_ADDON=y +CT_GLIBC_HAS_PORTS_ADDON=y +CT_GLIBC_HAS_LIBIDN_ADDON=y +CT_GLIBC_USE_PORTS_ADDON=y +CT_GLIBC_USE_NPTL_ADDON=y +# CT_GLIBC_USE_LIBIDN_ADDON is not set +CT_GLIBC_HAS_OBSOLETE_RPC=y +CT_GLIBC_EXTRA_CONFIG_ARRAY="" +CT_GLIBC_CONFIGPARMS="" +CT_GLIBC_EXTRA_CFLAGS="" +CT_GLIBC_ENABLE_OBSOLETE_RPC=y +# CT_GLIBC_DISABLE_VERSIONING is not set +CT_GLIBC_OLDEST_ABI="" +CT_GLIBC_FORCE_UNWIND=y +# CT_GLIBC_LOCALES is not set +# CT_GLIBC_KERNEL_VERSION_NONE is not set +CT_GLIBC_KERNEL_VERSION_AS_HEADERS=y +# CT_GLIBC_KERNEL_VERSION_CHOSEN is not set +CT_GLIBC_MIN_KERNEL="3.2.101" +# CT_GLIBC_SSP_DEFAULT is not set +# CT_GLIBC_SSP_NO is not set +# CT_GLIBC_SSP_YES is not set +# CT_GLIBC_SSP_ALL is not set +# CT_GLIBC_SSP_STRONG is not set +# CT_NEWLIB_USE_REDHAT is not set +CT_ALL_LIBC_CHOICES="AVR_LIBC BIONIC GLIBC MINGW_W64 MOXIEBOX MUSL NEWLIB NONE UCLIBC" CT_LIBC_SUPPORT_THREADS_ANY=y CT_LIBC_SUPPORT_THREADS_NATIVE=y @@ -342,100 +461,71 @@ CT_LIBC_SUPPORT_THREADS_NATIVE=y # Common C library options # CT_THREADS_NATIVE=y +# CT_CREATE_LDSO_CONF is not set CT_LIBC_XLDD=y -# -# glibc other options -# -CT_LIBC_GLIBC_PORTS_EXTERNAL=y -CT_LIBC_GLIBC_MAY_FORCE_PORTS=y -CT_LIBC_glibc_familly=y -CT_LIBC_GLIBC_EXTRA_CONFIG_ARRAY="" -CT_LIBC_GLIBC_CONFIGPARMS="" -CT_LIBC_GLIBC_EXTRA_CFLAGS="" -CT_LIBC_EXTRA_CC_ARGS="" -# CT_LIBC_DISABLE_VERSIONING is not set -CT_LIBC_OLDEST_ABI="" -CT_LIBC_GLIBC_FORCE_UNWIND=y -CT_LIBC_GLIBC_USE_PORTS=y -CT_LIBC_ADDONS_LIST="" - -# -# WARNING !!! -# - -# -# For glibc >= 2.8, it can happen that the tarballs -# - -# -# for the addons are not available for download. -# - -# -# If that happens, bad luck... Try a previous version -# - -# -# or try again later... :-( -# -# CT_LIBC_LOCALES is not set -# CT_LIBC_GLIBC_KERNEL_VERSION_NONE is not set -CT_LIBC_GLIBC_KERNEL_VERSION_AS_HEADERS=y -# CT_LIBC_GLIBC_KERNEL_VERSION_CHOSEN is not set -CT_LIBC_GLIBC_MIN_KERNEL="3.2.72" - # # C compiler # -CT_CC="gcc" CT_CC_CORE_PASSES_NEEDED=y CT_CC_CORE_PASS_1_NEEDED=y CT_CC_CORE_PASS_2_NEEDED=y -CT_CC_gcc=y -# CT_CC_GCC_SHOW_LINARO is not set -CT_CC_GCC_V_5_2_0=y -# CT_CC_GCC_V_4_9_3 is not set -# CT_CC_GCC_V_4_8_5 is not set -# CT_CC_GCC_V_4_7_4 is not set -# CT_CC_GCC_V_4_6_4 is not set -# CT_CC_GCC_V_4_5_4 is not set -# CT_CC_GCC_V_4_4_7 is not set -# CT_CC_GCC_V_4_3_6 is not set -# CT_CC_GCC_V_4_2_4 is not set -CT_CC_GCC_4_2_or_later=y -CT_CC_GCC_4_3_or_later=y -CT_CC_GCC_4_4_or_later=y -CT_CC_GCC_4_5_or_later=y -CT_CC_GCC_4_6_or_later=y -CT_CC_GCC_4_7_or_later=y -CT_CC_GCC_4_8_or_later=y -CT_CC_GCC_4_9_or_later=y -CT_CC_GCC_5=y -CT_CC_GCC_5_or_later=y -CT_CC_GCC_HAS_GRAPHITE=y -CT_CC_GCC_USE_GRAPHITE=y -CT_CC_GCC_HAS_LTO=y -CT_CC_GCC_USE_LTO=y -CT_CC_GCC_HAS_PKGVERSION_BUGURL=y -CT_CC_GCC_HAS_BUILD_ID=y -CT_CC_GCC_HAS_LNK_HASH_STYLE=y -CT_CC_GCC_USE_GMP_MPFR=y -CT_CC_GCC_USE_MPC=y -CT_CC_GCC_HAS_LIBQUADMATH=y -CT_CC_GCC_HAS_LIBSANITIZER=y -CT_CC_GCC_VERSION="5.2.0" -# CT_CC_LANG_FORTRAN is not set +CT_CC_SUPPORT_CXX=y +CT_CC_SUPPORT_FORTRAN=y +CT_CC_SUPPORT_ADA=y +CT_CC_SUPPORT_OBJC=y +CT_CC_SUPPORT_OBJCXX=y +CT_CC_SUPPORT_GOLANG=y +CT_CC_GCC=y +CT_CC="gcc" +CT_CC_CHOICE_KSYM="GCC" +CT_CC_GCC_SHOW=y + +# +# Options for gcc +# +CT_CC_GCC_PKG_KSYM="GCC" +CT_GCC_DIR_NAME="gcc" +CT_GCC_USE_GNU=y +CT_GCC_USE="GCC" +CT_GCC_PKG_NAME="gcc" +CT_GCC_SRC_RELEASE=y +CT_GCC_PATCH_ORDER="global" +CT_GCC_V_8=y +# CT_GCC_V_7 is not set +# CT_GCC_V_6 is not set +# CT_GCC_V_5 is not set +# CT_GCC_V_4_9 is not set +# CT_GCC_NO_VERSIONS is not set +CT_GCC_VERSION="8.3.0" +CT_GCC_MIRRORS="$(CT_Mirrors GNU gcc/gcc-${CT_GCC_VERSION}) $(CT_Mirrors sourceware gcc/releases/gcc-${CT_GCC_VERSION})" +CT_GCC_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GCC_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GCC_ARCHIVE_FORMATS=".tar.xz .tar.gz" +CT_GCC_SIGNATURE_FORMAT="" +CT_GCC_later_than_7=y +CT_GCC_7_or_later=y +CT_GCC_later_than_6=y +CT_GCC_6_or_later=y +CT_GCC_later_than_5=y +CT_GCC_5_or_later=y +CT_GCC_later_than_4_9=y +CT_GCC_4_9_or_later=y +CT_GCC_later_than_4_8=y +CT_GCC_4_8_or_later=y +CT_CC_GCC_HAS_LIBMPX=y CT_CC_GCC_ENABLE_CXX_FLAGS="" CT_CC_GCC_CORE_EXTRA_CONFIG_ARRAY="" CT_CC_GCC_EXTRA_CONFIG_ARRAY="" -CT_CC_GCC_EXTRA_ENV_ARRAY="" CT_CC_GCC_STATIC_LIBSTDCXX=y # CT_CC_GCC_SYSTEM_ZLIB is not set +CT_CC_GCC_CONFIG_TLS=m # # Optimisation features # +CT_CC_GCC_USE_GRAPHITE=y +CT_CC_GCC_USE_LTO=y # # Settings for libraries running on target @@ -464,97 +554,208 @@ CT_CC_GCC_DEC_FLOAT_AUTO=y # CT_CC_GCC_DEC_FLOAT_BID is not set # CT_CC_GCC_DEC_FLOAT_DPD is not set # CT_CC_GCC_DEC_FLOATS_NO is not set -CT_CC_SUPPORT_CXX=y -CT_CC_SUPPORT_FORTRAN=y -CT_CC_SUPPORT_JAVA=y -CT_CC_SUPPORT_ADA=y -CT_CC_SUPPORT_OBJC=y -CT_CC_SUPPORT_OBJCXX=y -CT_CC_SUPPORT_GOLANG=y +CT_ALL_CC_CHOICES="GCC" # # Additional supported languages: # CT_CC_LANG_CXX=y -# CT_CC_LANG_JAVA is not set +# CT_CC_LANG_FORTRAN is not set # # Debug facilities # -# CT_DEBUG_dmalloc is not set -# CT_DEBUG_duma is not set -# CT_DEBUG_gdb is not set -# CT_DEBUG_ltrace is not set -# CT_DEBUG_strace is not set +# CT_DEBUG_DUMA is not set +# CT_DEBUG_GDB is not set +# CT_DEBUG_LTRACE is not set +# CT_DEBUG_STRACE is not set +CT_ALL_DEBUG_CHOICES="DUMA GDB LTRACE STRACE" # # Companion libraries # -CT_COMPLIBS_NEEDED=y +# CT_COMPLIBS_CHECK is not set +# CT_COMP_LIBS_CLOOG is not set +# CT_COMP_LIBS_EXPAT is not set +CT_COMP_LIBS_GETTEXT=y +CT_COMP_LIBS_GETTEXT_PKG_KSYM="GETTEXT" +CT_GETTEXT_DIR_NAME="gettext" +CT_GETTEXT_PKG_NAME="gettext" +CT_GETTEXT_SRC_RELEASE=y +CT_GETTEXT_PATCH_ORDER="global" +CT_GETTEXT_V_0_19_8_1=y +# CT_GETTEXT_NO_VERSIONS is not set +CT_GETTEXT_VERSION="0.19.8.1" +CT_GETTEXT_MIRRORS="$(CT_Mirrors GNU gettext)" +CT_GETTEXT_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GETTEXT_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GETTEXT_ARCHIVE_FORMATS=".tar.xz .tar.lz .tar.gz" +CT_GETTEXT_SIGNATURE_FORMAT="packed/.sig" +CT_COMP_LIBS_GMP=y +CT_COMP_LIBS_GMP_PKG_KSYM="GMP" +CT_GMP_DIR_NAME="gmp" +CT_GMP_PKG_NAME="gmp" +CT_GMP_SRC_RELEASE=y +CT_GMP_PATCH_ORDER="global" +CT_GMP_V_6_1=y +# CT_GMP_NO_VERSIONS is not set +CT_GMP_VERSION="6.1.2" +CT_GMP_MIRRORS="https://gmplib.org/download/gmp https://gmplib.org/download/gmp/archive $(CT_Mirrors GNU gmp)" +CT_GMP_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GMP_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GMP_ARCHIVE_FORMATS=".tar.xz .tar.lz .tar.bz2" +CT_GMP_SIGNATURE_FORMAT="packed/.sig" +CT_GMP_later_than_5_1_0=y +CT_GMP_5_1_0_or_later=y +CT_GMP_later_than_5_0_0=y +CT_GMP_5_0_0_or_later=y +CT_GMP_REQUIRE_5_0_0_or_later=y +CT_COMP_LIBS_ISL=y +CT_COMP_LIBS_ISL_PKG_KSYM="ISL" +CT_ISL_DIR_NAME="isl" +CT_ISL_PKG_NAME="isl" +CT_ISL_SRC_RELEASE=y +CT_ISL_PATCH_ORDER="global" +CT_ISL_V_0_20=y +# CT_ISL_V_0_19 is not set +# CT_ISL_V_0_18 is not set +# CT_ISL_V_0_17 is not set +# CT_ISL_V_0_16 is not set +# CT_ISL_V_0_15 is not set +# CT_ISL_NO_VERSIONS is not set +CT_ISL_VERSION="0.20" +CT_ISL_MIRRORS="http://isl.gforge.inria.fr" +CT_ISL_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_ISL_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_ISL_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz" +CT_ISL_SIGNATURE_FORMAT="" +CT_ISL_later_than_0_18=y +CT_ISL_0_18_or_later=y +CT_ISL_later_than_0_15=y +CT_ISL_0_15_or_later=y +CT_ISL_REQUIRE_0_15_or_later=y +CT_ISL_later_than_0_14=y +CT_ISL_0_14_or_later=y +CT_ISL_REQUIRE_0_14_or_later=y +CT_ISL_later_than_0_13=y +CT_ISL_0_13_or_later=y +CT_ISL_later_than_0_12=y +CT_ISL_0_12_or_later=y +CT_ISL_REQUIRE_0_12_or_later=y +# CT_COMP_LIBS_LIBELF is not set +CT_COMP_LIBS_LIBICONV=y +CT_COMP_LIBS_LIBICONV_PKG_KSYM="LIBICONV" +CT_LIBICONV_DIR_NAME="libiconv" +CT_LIBICONV_PKG_NAME="libiconv" +CT_LIBICONV_SRC_RELEASE=y +CT_LIBICONV_PATCH_ORDER="global" +CT_LIBICONV_V_1_15=y +# CT_LIBICONV_NO_VERSIONS is not set +CT_LIBICONV_VERSION="1.15" +CT_LIBICONV_MIRRORS="$(CT_Mirrors GNU libiconv)" +CT_LIBICONV_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_LIBICONV_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_LIBICONV_ARCHIVE_FORMATS=".tar.gz" +CT_LIBICONV_SIGNATURE_FORMAT="packed/.sig" +CT_COMP_LIBS_MPC=y +CT_COMP_LIBS_MPC_PKG_KSYM="MPC" +CT_MPC_DIR_NAME="mpc" +CT_MPC_PKG_NAME="mpc" +CT_MPC_SRC_RELEASE=y +CT_MPC_PATCH_ORDER="global" +CT_MPC_V_1_1=y +# CT_MPC_V_1_0 is not set +# CT_MPC_NO_VERSIONS is not set +CT_MPC_VERSION="1.1.0" +CT_MPC_MIRRORS="http://www.multiprecision.org/downloads $(CT_Mirrors GNU mpc)" +CT_MPC_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_MPC_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_MPC_ARCHIVE_FORMATS=".tar.gz" +CT_MPC_SIGNATURE_FORMAT="packed/.sig" +CT_MPC_1_1_0_or_later=y +CT_MPC_1_1_0_or_older=y +CT_COMP_LIBS_MPFR=y +CT_COMP_LIBS_MPFR_PKG_KSYM="MPFR" +CT_MPFR_DIR_NAME="mpfr" +CT_MPFR_PKG_NAME="mpfr" +CT_MPFR_SRC_RELEASE=y +CT_MPFR_PATCH_ORDER="global" +CT_MPFR_V_4_0=y +# CT_MPFR_V_3_1 is not set +# CT_MPFR_NO_VERSIONS is not set +CT_MPFR_VERSION="4.0.2" +CT_MPFR_MIRRORS="http://www.mpfr.org/mpfr-${CT_MPFR_VERSION} $(CT_Mirrors GNU mpfr)" +CT_MPFR_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_MPFR_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_MPFR_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz .zip" +CT_MPFR_SIGNATURE_FORMAT="packed/.asc" +CT_MPFR_later_than_4_0_0=y +CT_MPFR_4_0_0_or_later=y +CT_MPFR_later_than_3_0_0=y +CT_MPFR_3_0_0_or_later=y +CT_MPFR_REQUIRE_3_0_0_or_later=y +CT_COMP_LIBS_NCURSES=y +CT_COMP_LIBS_NCURSES_PKG_KSYM="NCURSES" +CT_NCURSES_DIR_NAME="ncurses" +CT_NCURSES_PKG_NAME="ncurses" +CT_NCURSES_SRC_RELEASE=y +CT_NCURSES_PATCH_ORDER="global" +CT_NCURSES_V_6_1=y +# CT_NCURSES_V_6_0 is not set +# CT_NCURSES_NO_VERSIONS is not set +CT_NCURSES_VERSION="6.1" +CT_NCURSES_MIRRORS="ftp://invisible-island.net/ncurses $(CT_Mirrors GNU ncurses)" +CT_NCURSES_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_NCURSES_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_NCURSES_ARCHIVE_FORMATS=".tar.gz" +CT_NCURSES_SIGNATURE_FORMAT="packed/.sig" +CT_NCURSES_HOST_CONFIG_ARGS="" +CT_NCURSES_HOST_DISABLE_DB=y +CT_NCURSES_HOST_FALLBACKS="linux,xterm,xterm-color,xterm-256color,vt100" +CT_NCURSES_TARGET_CONFIG_ARGS="" +# CT_NCURSES_TARGET_DISABLE_DB is not set +CT_NCURSES_TARGET_FALLBACKS="" +CT_COMP_LIBS_ZLIB=y +CT_COMP_LIBS_ZLIB_PKG_KSYM="ZLIB" +CT_ZLIB_DIR_NAME="zlib" +CT_ZLIB_PKG_NAME="zlib" +CT_ZLIB_SRC_RELEASE=y +CT_ZLIB_PATCH_ORDER="global" +CT_ZLIB_V_1_2_11=y +# CT_ZLIB_NO_VERSIONS is not set +CT_ZLIB_VERSION="1.2.11" +CT_ZLIB_MIRRORS="http://downloads.sourceforge.net/project/libpng/zlib/${CT_ZLIB_VERSION}" +CT_ZLIB_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_ZLIB_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_ZLIB_ARCHIVE_FORMATS=".tar.xz .tar.gz" +CT_ZLIB_SIGNATURE_FORMAT="packed/.asc" +CT_ALL_COMP_LIBS_CHOICES="CLOOG EXPAT GETTEXT GMP ISL LIBELF LIBICONV MPC MPFR NCURSES ZLIB" CT_LIBICONV_NEEDED=y CT_GETTEXT_NEEDED=y CT_GMP_NEEDED=y CT_MPFR_NEEDED=y CT_ISL_NEEDED=y CT_MPC_NEEDED=y -CT_COMPLIBS=y +CT_NCURSES_NEEDED=y +CT_ZLIB_NEEDED=y CT_LIBICONV=y CT_GETTEXT=y CT_GMP=y CT_MPFR=y CT_ISL=y CT_MPC=y -CT_LIBICONV_V_1_14=y -CT_LIBICONV_VERSION="1.14" -CT_GETTEXT_V_0_19_6=y -CT_GETTEXT_VERSION="0.19.6" -CT_GMP_V_6_0_0=y -# CT_GMP_V_5_1_3 is not set -# CT_GMP_V_5_1_1 is not set -# CT_GMP_V_5_0_2 is not set -# CT_GMP_V_5_0_1 is not set -# CT_GMP_V_4_3_2 is not set -# CT_GMP_V_4_3_1 is not set -# CT_GMP_V_4_3_0 is not set -CT_GMP_5_0_2_or_later=y -CT_GMP_VERSION="6.0.0a" -CT_MPFR_V_3_1_3=y -# CT_MPFR_V_3_1_2 is not set -# CT_MPFR_V_3_1_0 is not set -# CT_MPFR_V_3_0_1 is not set -# CT_MPFR_V_3_0_0 is not set -# CT_MPFR_V_2_4_2 is not set -# CT_MPFR_V_2_4_1 is not set -# CT_MPFR_V_2_4_0 is not set -CT_MPFR_VERSION="3.1.3" -CT_ISL_V_0_14=y -# CT_ISL_V_0_12_2 is not set -CT_ISL_V_0_14_or_later=y -CT_ISL_V_0_12_or_later=y -CT_ISL_VERSION="0.14" -# CT_CLOOG_V_0_18_4 is not set -# CT_CLOOG_V_0_18_1 is not set -# CT_CLOOG_V_0_18_0 is not set -CT_MPC_V_1_0_3=y -# CT_MPC_V_1_0_2 is not set -# CT_MPC_V_1_0_1 is not set -# CT_MPC_V_1_0 is not set -# CT_MPC_V_0_9 is not set -# CT_MPC_V_0_8_2 is not set -# CT_MPC_V_0_8_1 is not set -# CT_MPC_V_0_7 is not set -CT_MPC_VERSION="1.0.3" - -# -# Companion libraries common options -# -# CT_COMPLIBS_CHECK is not set +CT_NCURSES=y +CT_ZLIB=y # # Companion tools # - -# -# READ HELP before you say 'Y' below !!! -# -# CT_COMP_TOOLS is not set +# CT_COMP_TOOLS_FOR_HOST is not set +# CT_COMP_TOOLS_AUTOCONF is not set +# CT_COMP_TOOLS_AUTOMAKE is not set +# CT_COMP_TOOLS_BISON is not set +# CT_COMP_TOOLS_DTC is not set +# CT_COMP_TOOLS_LIBTOOL is not set +# CT_COMP_TOOLS_M4 is not set +# CT_COMP_TOOLS_MAKE is not set +CT_ALL_COMP_TOOLS_CHOICES="AUTOCONF AUTOMAKE BISON DTC LIBTOOL M4 MAKE" diff --git a/src/ci/docker/dist-arm-linux/patches/glibc/ports-2.16.0/001-arm-libgcc_s_resume-used.patch b/src/ci/docker/dist-arm-linux/patches/glibc/ports-2.16.0/001-arm-libgcc_s_resume-used.patch deleted file mode 100644 index 871d5225c0f71..0000000000000 --- a/src/ci/docker/dist-arm-linux/patches/glibc/ports-2.16.0/001-arm-libgcc_s_resume-used.patch +++ /dev/null @@ -1,48 +0,0 @@ -commit bdb24c2851fd5f0ad9b82d7ea1db911d334b02d2 -Author: Joseph Myers -Date: Tue May 20 21:27:13 2014 +0000 - - Fix ARM build with GCC trunk. - - sysdeps/unix/sysv/linux/arm/unwind-resume.c and - sysdeps/unix/sysv/linux/arm/unwind-forcedunwind.c have static - variables that are written in C code but only read from toplevel asms. - Current GCC trunk now optimizes away such apparently write-only static - variables, so causing a build failure. This patch marks those - variables with __attribute_used__ to avoid that optimization. - - Tested that this fixes the build for ARM. - - * sysdeps/unix/sysv/linux/arm/unwind-forcedunwind.c - (libgcc_s_resume): Use __attribute_used__. - * sysdeps/unix/sysv/linux/arm/unwind-resume.c (libgcc_s_resume): - Likewise. - -diff --git a/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c b/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c -index 29e2c2b00b04..e848bfeffdcb 100644 ---- a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c -+++ b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c -@@ -22,7 +22,8 @@ - #include - - static void *libgcc_s_handle; --static void (*libgcc_s_resume) (struct _Unwind_Exception *exc); -+static void (*libgcc_s_resume) (struct _Unwind_Exception *exc) -+ __attribute_used__; - static _Unwind_Reason_Code (*libgcc_s_personality) - (_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *); - static _Unwind_Reason_Code (*libgcc_s_forcedunwind) -diff --git a/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c b/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c -index 285b99b5ed0d..48d00fc83641 100644 ---- a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c -+++ b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c -@@ -20,7 +20,8 @@ - #include - #include - --static void (*libgcc_s_resume) (struct _Unwind_Exception *exc); -+static void (*libgcc_s_resume) (struct _Unwind_Exception *exc) -+ __attribute_used__; - static _Unwind_Reason_Code (*libgcc_s_personality) - (_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *); - diff --git a/src/ci/docker/dist-armhf-linux/Dockerfile b/src/ci/docker/dist-armhf-linux/Dockerfile index d1dd9faaa1035..155dd84891435 100644 --- a/src/ci/docker/dist-armhf-linux/Dockerfile +++ b/src/ci/docker/dist-armhf-linux/Dockerfile @@ -3,20 +3,14 @@ FROM ubuntu:16.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -# Ubuntu 16.04 (this container) ships with make 4, but something in the -# toolchains we build below chokes on that, so go back to make 3 -COPY scripts/make3.sh /scripts/ -RUN sh /scripts/make3.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh +COPY scripts/crosstool-ng-1.24.sh /scripts/ +RUN sh /scripts/crosstool-ng-1.24.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh USER rustbuild WORKDIR /tmp -COPY dist-armhf-linux/patches/ /tmp/patches/ COPY dist-armhf-linux/arm-linux-gnueabihf.config dist-armhf-linux/build-toolchains.sh /tmp/ RUN ./build-toolchains.sh @@ -33,5 +27,5 @@ ENV CC_arm_unknown_linux_gnueabihf=arm-unknown-linux-gnueabihf-gcc \ ENV HOSTS=arm-unknown-linux-gnueabihf -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS --enable-full-tools --disable-docs +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-armhf-linux/arm-linux-gnueabihf.config b/src/ci/docker/dist-armhf-linux/arm-linux-gnueabihf.config index bebbcd1670a5e..a3dcff1c93635 100644 --- a/src/ci/docker/dist-armhf-linux/arm-linux-gnueabihf.config +++ b/src/ci/docker/dist-armhf-linux/arm-linux-gnueabihf.config @@ -1,9 +1,29 @@ # # Automatically generated file; DO NOT EDIT. -# Crosstool-NG Configuration -# -CT_CONFIGURE_has_make381=y -CT_CONFIGURE_has_xz=y +# crosstool-NG Configuration +# +CT_CONFIGURE_has_static_link=y +CT_CONFIGURE_has_cxx11=y +CT_CONFIGURE_has_wget=y +CT_CONFIGURE_has_curl=y +CT_CONFIGURE_has_make_3_81_or_newer=y +CT_CONFIGURE_has_make_4_0_or_newer=y +CT_CONFIGURE_has_libtool_2_4_or_newer=y +CT_CONFIGURE_has_libtoolize_2_4_or_newer=y +CT_CONFIGURE_has_autoconf_2_65_or_newer=y +CT_CONFIGURE_has_autoreconf_2_65_or_newer=y +CT_CONFIGURE_has_automake_1_15_or_newer=y +CT_CONFIGURE_has_gnu_m4_1_4_12_or_newer=y +CT_CONFIGURE_has_bison_2_7_or_newer=y +CT_CONFIGURE_has_python=y +CT_CONFIGURE_has_git=y +CT_CONFIGURE_has_md5sum=y +CT_CONFIGURE_has_sha1sum=y +CT_CONFIGURE_has_sha256sum=y +CT_CONFIGURE_has_sha512sum=y +CT_CONFIGURE_has_install_with_strip_program=y +CT_CONFIG_VERSION_CURRENT="3" +CT_CONFIG_VERSION="3" CT_MODULES=y # @@ -20,41 +40,48 @@ CT_MODULES=y # # Paths # -CT_LOCAL_TARBALLS_DIR="" +CT_LOCAL_TARBALLS_DIR="${HOME}/src" +CT_SAVE_TARBALLS=y +# CT_TARBALLS_BUILDROOT_LAYOUT is not set CT_WORK_DIR="${CT_TOP_DIR}/.build" +CT_BUILD_TOP_DIR="${CT_WORK_DIR:-${CT_TOP_DIR}/.build}/${CT_HOST:+HOST-${CT_HOST}/}${CT_TARGET}" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" -CT_INSTALL_DIR="${CT_PREFIX_DIR}" CT_RM_RF_PREFIX_DIR=y CT_REMOVE_DOCS=y -CT_INSTALL_DIR_RO=y +CT_INSTALL_LICENSES=y +CT_PREFIX_DIR_RO=y CT_STRIP_HOST_TOOLCHAIN_EXECUTABLES=y # CT_STRIP_TARGET_TOOLCHAIN_EXECUTABLES is not set # # Downloading # +CT_DOWNLOAD_AGENT_WGET=y +# CT_DOWNLOAD_AGENT_CURL is not set +# CT_DOWNLOAD_AGENT_NONE is not set # CT_FORBID_DOWNLOAD is not set # CT_FORCE_DOWNLOAD is not set CT_CONNECT_TIMEOUT=10 +CT_DOWNLOAD_WGET_OPTIONS="--passive-ftp --tries=3 -nc --progress=dot:binary" # CT_ONLY_DOWNLOAD is not set # CT_USE_MIRROR is not set +CT_VERIFY_DOWNLOAD_DIGEST=y +CT_VERIFY_DOWNLOAD_DIGEST_SHA512=y +# CT_VERIFY_DOWNLOAD_DIGEST_SHA256 is not set +# CT_VERIFY_DOWNLOAD_DIGEST_SHA1 is not set +# CT_VERIFY_DOWNLOAD_DIGEST_MD5 is not set +CT_VERIFY_DOWNLOAD_DIGEST_ALG="sha512" +# CT_VERIFY_DOWNLOAD_SIGNATURE is not set # # Extracting # # CT_FORCE_EXTRACT is not set -CT_OVERIDE_CONFIG_GUESS_SUB=y +CT_OVERRIDE_CONFIG_GUESS_SUB=y # CT_ONLY_EXTRACT is not set -# CT_PATCH_BUNDLED is not set -# CT_PATCH_LOCAL is not set -CT_PATCH_BUNDLED_LOCAL=y -# CT_PATCH_LOCAL_BUNDLED is not set -# CT_PATCH_BUNDLED_FALLBACK_LOCAL is not set -# CT_PATCH_LOCAL_FALLBACK_BUNDLED is not set -# CT_PATCH_NONE is not set -CT_PATCH_ORDER="bundled,local" -CT_PATCH_USE_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" +CT_PATCH_BUNDLED=y +# CT_PATCH_BUNDLED_LOCAL is not set +CT_PATCH_ORDER="bundled" # # Build behavior @@ -77,11 +104,11 @@ CT_CONFIG_SHELL="${bash}" # # CT_LOG_ERROR is not set # CT_LOG_WARN is not set -CT_LOG_INFO=y -# CT_LOG_EXTRA is not set +# CT_LOG_INFO is not set +CT_LOG_EXTRA=y # CT_LOG_ALL is not set # CT_LOG_DEBUG is not set -CT_LOG_LEVEL_MAX="INFO" +CT_LOG_LEVEL_MAX="EXTRA" # CT_LOG_SEE_TOOLS_WARN is not set CT_LOG_PROGRESS_BAR=y CT_LOG_TO_FILE=y @@ -90,86 +117,87 @@ CT_LOG_FILE_COMPRESS=y # # Target options # +# CT_ARCH_ALPHA is not set +# CT_ARCH_ARC is not set +CT_ARCH_ARM=y +# CT_ARCH_AVR is not set +# CT_ARCH_M68K is not set +# CT_ARCH_MIPS is not set +# CT_ARCH_NIOS2 is not set +# CT_ARCH_POWERPC is not set +# CT_ARCH_S390 is not set +# CT_ARCH_SH is not set +# CT_ARCH_SPARC is not set +# CT_ARCH_X86 is not set +# CT_ARCH_XTENSA is not set CT_ARCH="arm" -CT_ARCH_SUPPORTS_BOTH_MMU=y -CT_ARCH_SUPPORTS_BOTH_ENDIAN=y -CT_ARCH_SUPPORTS_32=y -CT_ARCH_SUPPORTS_64=y -CT_ARCH_SUPPORTS_WITH_ARCH=y -CT_ARCH_SUPPORTS_WITH_CPU=y -CT_ARCH_SUPPORTS_WITH_TUNE=y -CT_ARCH_SUPPORTS_WITH_FLOAT=y -CT_ARCH_SUPPORTS_WITH_FPU=y -CT_ARCH_SUPPORTS_SOFTFP=y -CT_ARCH_DEFAULT_HAS_MMU=y -CT_ARCH_DEFAULT_LE=y -CT_ARCH_DEFAULT_32=y -CT_ARCH_ARCH="armv6" +CT_ARCH_CHOICE_KSYM="ARM" +# CT_ARCH_ALPHA_EV4 is not set +# CT_ARCH_ALPHA_EV45 is not set +# CT_ARCH_ALPHA_EV5 is not set +# CT_ARCH_ALPHA_EV56 is not set +# CT_ARCH_ALPHA_EV6 is not set +# CT_ARCH_ALPHA_EV67 is not set CT_ARCH_CPU="" CT_ARCH_TUNE="" -CT_ARCH_FPU="vfp" -# CT_ARCH_BE is not set -CT_ARCH_LE=y -CT_ARCH_32=y -# CT_ARCH_64 is not set -CT_ARCH_BITNESS=32 -CT_ARCH_FLOAT_HW=y -# CT_ARCH_FLOAT_SW is not set -CT_TARGET_CFLAGS="" -CT_TARGET_LDFLAGS="" -# CT_ARCH_alpha is not set -CT_ARCH_arm=y -# CT_ARCH_avr is not set -# CT_ARCH_m68k is not set -# CT_ARCH_mips is not set -# CT_ARCH_nios2 is not set -# CT_ARCH_powerpc is not set -# CT_ARCH_s390 is not set -# CT_ARCH_sh is not set -# CT_ARCH_sparc is not set -# CT_ARCH_x86 is not set -# CT_ARCH_xtensa is not set -CT_ARCH_alpha_AVAILABLE=y -CT_ARCH_arm_AVAILABLE=y -CT_ARCH_avr_AVAILABLE=y -CT_ARCH_m68k_AVAILABLE=y -CT_ARCH_microblaze_AVAILABLE=y -CT_ARCH_mips_AVAILABLE=y -CT_ARCH_nios2_AVAILABLE=y -CT_ARCH_powerpc_AVAILABLE=y -CT_ARCH_s390_AVAILABLE=y -CT_ARCH_sh_AVAILABLE=y -CT_ARCH_sparc_AVAILABLE=y -CT_ARCH_x86_AVAILABLE=y -CT_ARCH_xtensa_AVAILABLE=y +CT_ARCH_ARM_SHOW=y + +# +# Options for arm +# +CT_ARCH_ARM_PKG_KSYM="" +CT_ARCH_ARM_MODE="arm" +CT_ARCH_ARM_MODE_ARM=y +# CT_ARCH_ARM_MODE_THUMB is not set +# CT_ARCH_ARM_INTERWORKING is not set +CT_ARCH_ARM_EABI_FORCE=y +CT_ARCH_ARM_EABI=y +CT_ARCH_ARM_TUPLE_USE_EABIHF=y +CT_ALL_ARCH_CHOICES="ALPHA ARC ARM AVR M68K MICROBLAZE MIPS MOXIE MSP430 NIOS2 POWERPC RISCV S390 SH SPARC X86 XTENSA" CT_ARCH_SUFFIX="" +# CT_OMIT_TARGET_VENDOR is not set # # Generic target options # # CT_MULTILIB is not set +CT_DEMULTILIB=y +CT_ARCH_SUPPORTS_BOTH_MMU=y +CT_ARCH_DEFAULT_HAS_MMU=y CT_ARCH_USE_MMU=y +CT_ARCH_SUPPORTS_FLAT_FORMAT=y +CT_ARCH_SUPPORTS_EITHER_ENDIAN=y +CT_ARCH_DEFAULT_LE=y +# CT_ARCH_BE is not set +CT_ARCH_LE=y CT_ARCH_ENDIAN="little" +CT_ARCH_SUPPORTS_32=y +CT_ARCH_SUPPORTS_64=y +CT_ARCH_DEFAULT_32=y +CT_ARCH_BITNESS=32 +CT_ARCH_32=y +# CT_ARCH_64 is not set # # Target optimisations # +CT_ARCH_SUPPORTS_WITH_ARCH=y +CT_ARCH_SUPPORTS_WITH_CPU=y +CT_ARCH_SUPPORTS_WITH_TUNE=y +CT_ARCH_SUPPORTS_WITH_FLOAT=y +CT_ARCH_SUPPORTS_WITH_FPU=y +CT_ARCH_SUPPORTS_SOFTFP=y CT_ARCH_EXCLUSIVE_WITH_CPU=y +CT_ARCH_ARCH="armv6" +CT_ARCH_FPU="vfp" # CT_ARCH_FLOAT_AUTO is not set +CT_ARCH_FLOAT_HW=y # CT_ARCH_FLOAT_SOFTFP is not set +# CT_ARCH_FLOAT_SW is not set +CT_TARGET_CFLAGS="" +CT_TARGET_LDFLAGS="" CT_ARCH_FLOAT="hard" -# -# arm other options -# -CT_ARCH_ARM_MODE="arm" -CT_ARCH_ARM_MODE_ARM=y -# CT_ARCH_ARM_MODE_THUMB is not set -# CT_ARCH_ARM_INTERWORKING is not set -CT_ARCH_ARM_EABI_FORCE=y -CT_ARCH_ARM_EABI=y -CT_ARCH_ARM_TUPLE_USE_EABIHF=y - # # Toolchain options # @@ -182,7 +210,9 @@ CT_USE_SYSROOT=y CT_SYSROOT_NAME="sysroot" CT_SYSROOT_DIR_PREFIX="" CT_WANTS_STATIC_LINK=y +CT_WANTS_STATIC_LINK_CXX=y # CT_STATIC_TOOLCHAIN is not set +CT_SHOW_CT_VERSION=y CT_TOOLCHAIN_PKGVERSION="" CT_TOOLCHAIN_BUGURL="" @@ -216,126 +246,215 @@ CT_BUILD_SUFFIX="" # Operating System # CT_KERNEL_SUPPORTS_SHARED_LIBS=y +# CT_KERNEL_BARE_METAL is not set +CT_KERNEL_LINUX=y CT_KERNEL="linux" -CT_KERNEL_VERSION="3.2.72" -# CT_KERNEL_bare_metal is not set -CT_KERNEL_linux=y -CT_KERNEL_bare_metal_AVAILABLE=y -CT_KERNEL_linux_AVAILABLE=y -# CT_KERNEL_V_4_3 is not set -# CT_KERNEL_V_4_2 is not set -# CT_KERNEL_V_4_1 is not set -# CT_KERNEL_V_3_18 is not set -# CT_KERNEL_V_3_14 is not set -# CT_KERNEL_V_3_12 is not set -# CT_KERNEL_V_3_10 is not set -# CT_KERNEL_V_3_4 is not set -CT_KERNEL_V_3_2=y -# CT_KERNEL_V_2_6_32 is not set -# CT_KERNEL_LINUX_CUSTOM is not set -CT_KERNEL_windows_AVAILABLE=y - -# -# Common kernel options -# -CT_SHARED_LIBS=y - -# -# linux other options -# +CT_KERNEL_CHOICE_KSYM="LINUX" +CT_KERNEL_LINUX_SHOW=y + +# +# Options for linux +# +CT_KERNEL_LINUX_PKG_KSYM="LINUX" +CT_LINUX_DIR_NAME="linux" +CT_LINUX_PKG_NAME="linux" +CT_LINUX_SRC_RELEASE=y +CT_LINUX_PATCH_ORDER="global" +# CT_LINUX_V_4_20 is not set +# CT_LINUX_V_4_19 is not set +# CT_LINUX_V_4_18 is not set +# CT_LINUX_V_4_17 is not set +# CT_LINUX_V_4_16 is not set +# CT_LINUX_V_4_15 is not set +# CT_LINUX_V_4_14 is not set +# CT_LINUX_V_4_13 is not set +# CT_LINUX_V_4_12 is not set +# CT_LINUX_V_4_11 is not set +# CT_LINUX_V_4_10 is not set +# CT_LINUX_V_4_9 is not set +# CT_LINUX_V_4_4 is not set +# CT_LINUX_V_4_1 is not set +# CT_LINUX_V_3_16 is not set +# CT_LINUX_V_3_13 is not set +# CT_LINUX_V_3_12 is not set +# CT_LINUX_V_3_10 is not set +# CT_LINUX_V_3_4 is not set +CT_LINUX_V_3_2=y +# CT_LINUX_V_2_6_32 is not set +# CT_LINUX_NO_VERSIONS is not set +CT_LINUX_VERSION="3.2.101" +CT_LINUX_MIRRORS="$(CT_Mirrors kernel.org linux ${CT_LINUX_VERSION})" +CT_LINUX_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_LINUX_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_LINUX_ARCHIVE_FORMATS=".tar.xz .tar.gz" +CT_LINUX_SIGNATURE_FORMAT="unpacked/.sign" +CT_LINUX_4_8_or_older=y +CT_LINUX_older_than_4_8=y +CT_LINUX_3_7_or_older=y +CT_LINUX_older_than_3_7=y +CT_LINUX_later_than_3_2=y +CT_LINUX_3_2_or_later=y CT_KERNEL_LINUX_VERBOSITY_0=y # CT_KERNEL_LINUX_VERBOSITY_1 is not set # CT_KERNEL_LINUX_VERBOSITY_2 is not set CT_KERNEL_LINUX_VERBOSE_LEVEL=0 CT_KERNEL_LINUX_INSTALL_CHECK=y +CT_ALL_KERNEL_CHOICES="BARE_METAL LINUX WINDOWS" + +# +# Common kernel options +# +CT_SHARED_LIBS=y # # Binary utilities # CT_ARCH_BINFMT_ELF=y +CT_BINUTILS_BINUTILS=y CT_BINUTILS="binutils" -CT_BINUTILS_binutils=y +CT_BINUTILS_CHOICE_KSYM="BINUTILS" +CT_BINUTILS_BINUTILS_SHOW=y + +# +# Options for binutils +# +CT_BINUTILS_BINUTILS_PKG_KSYM="BINUTILS" +CT_BINUTILS_DIR_NAME="binutils" +CT_BINUTILS_USE_GNU=y +CT_BINUTILS_USE="BINUTILS" +CT_BINUTILS_PKG_NAME="binutils" +CT_BINUTILS_SRC_RELEASE=y +CT_BINUTILS_PATCH_ORDER="global" +CT_BINUTILS_V_2_32=y +# CT_BINUTILS_V_2_31 is not set +# CT_BINUTILS_V_2_30 is not set +# CT_BINUTILS_V_2_29 is not set +# CT_BINUTILS_V_2_28 is not set +# CT_BINUTILS_V_2_27 is not set +# CT_BINUTILS_V_2_26 is not set +# CT_BINUTILS_NO_VERSIONS is not set +CT_BINUTILS_VERSION="2.32" +CT_BINUTILS_MIRRORS="$(CT_Mirrors GNU binutils) $(CT_Mirrors sourceware binutils/releases)" +CT_BINUTILS_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_BINUTILS_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_BINUTILS_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz" +CT_BINUTILS_SIGNATURE_FORMAT="packed/.sig" +CT_BINUTILS_later_than_2_30=y +CT_BINUTILS_2_30_or_later=y +CT_BINUTILS_later_than_2_27=y +CT_BINUTILS_2_27_or_later=y +CT_BINUTILS_later_than_2_25=y +CT_BINUTILS_2_25_or_later=y +CT_BINUTILS_later_than_2_23=y +CT_BINUTILS_2_23_or_later=y # # GNU binutils # -# CT_CC_BINUTILS_SHOW_LINARO is not set -CT_BINUTILS_V_2_25_1=y -# CT_BINUTILS_V_2_25 is not set -# CT_BINUTILS_V_2_24 is not set -# CT_BINUTILS_V_2_23_2 is not set -# CT_BINUTILS_V_2_23_1 is not set -# CT_BINUTILS_V_2_22 is not set -# CT_BINUTILS_V_2_21_53 is not set -# CT_BINUTILS_V_2_21_1a is not set -# CT_BINUTILS_V_2_20_1a is not set -# CT_BINUTILS_V_2_19_1a is not set -# CT_BINUTILS_V_2_18a is not set -CT_BINUTILS_VERSION="2.25.1" -CT_BINUTILS_2_25_1_or_later=y -CT_BINUTILS_2_25_or_later=y -CT_BINUTILS_2_24_or_later=y -CT_BINUTILS_2_23_or_later=y -CT_BINUTILS_2_22_or_later=y -CT_BINUTILS_2_21_or_later=y -CT_BINUTILS_2_20_or_later=y -CT_BINUTILS_2_19_or_later=y -CT_BINUTILS_2_18_or_later=y CT_BINUTILS_HAS_HASH_STYLE=y CT_BINUTILS_HAS_GOLD=y -CT_BINUTILS_GOLD_SUPPORTS_ARCH=y -CT_BINUTILS_GOLD_SUPPORT=y CT_BINUTILS_HAS_PLUGINS=y CT_BINUTILS_HAS_PKGVERSION_BUGURL=y -CT_BINUTILS_FORCE_LD_BFD=y +CT_BINUTILS_GOLD_SUPPORTS_ARCH=y +CT_BINUTILS_GOLD_SUPPORT=y +CT_BINUTILS_FORCE_LD_BFD_DEFAULT=y CT_BINUTILS_LINKER_LD=y # CT_BINUTILS_LINKER_LD_GOLD is not set -# CT_BINUTILS_LINKER_GOLD_LD is not set CT_BINUTILS_LINKERS_LIST="ld" CT_BINUTILS_LINKER_DEFAULT="bfd" # CT_BINUTILS_PLUGINS is not set +CT_BINUTILS_RELRO=m CT_BINUTILS_EXTRA_CONFIG_ARRAY="" # CT_BINUTILS_FOR_TARGET is not set - -# -# binutils other options -# +CT_ALL_BINUTILS_CHOICES="BINUTILS" # # C-library # +CT_LIBC_GLIBC=y +# CT_LIBC_NEWLIB is not set +# CT_LIBC_NONE is not set +# CT_LIBC_UCLIBC is not set CT_LIBC="glibc" -CT_LIBC_VERSION="2.16.0" -CT_LIBC_glibc=y -# CT_LIBC_musl is not set -# CT_LIBC_uClibc is not set -CT_LIBC_avr_libc_AVAILABLE=y -CT_LIBC_glibc_AVAILABLE=y +CT_LIBC_CHOICE_KSYM="GLIBC" CT_THREADS="nptl" -# CT_CC_GLIBC_SHOW_LINARO is not set -# CT_LIBC_GLIBC_V_2_22 is not set -# CT_LIBC_GLIBC_V_2_21 is not set -# CT_LIBC_GLIBC_V_2_20 is not set -# CT_LIBC_GLIBC_V_2_19 is not set -# CT_LIBC_GLIBC_V_2_18 is not set -# CT_LIBC_GLIBC_V_2_17 is not set -CT_LIBC_GLIBC_V_2_16_0=y -# CT_LIBC_GLIBC_V_2_15 is not set -# CT_LIBC_GLIBC_V_2_14_1 is not set -# CT_LIBC_GLIBC_V_2_14 is not set -# CT_LIBC_GLIBC_V_2_13 is not set -# CT_LIBC_GLIBC_V_2_12_2 is not set -# CT_LIBC_GLIBC_V_2_12_1 is not set -# CT_LIBC_GLIBC_V_2_11_1 is not set -# CT_LIBC_GLIBC_V_2_11 is not set -# CT_LIBC_GLIBC_V_2_10_1 is not set -# CT_LIBC_GLIBC_V_2_9 is not set -# CT_LIBC_GLIBC_V_2_8 is not set -CT_LIBC_mingw_AVAILABLE=y -CT_LIBC_musl_AVAILABLE=y -CT_LIBC_newlib_AVAILABLE=y -CT_LIBC_none_AVAILABLE=y -CT_LIBC_uClibc_AVAILABLE=y +CT_LIBC_GLIBC_SHOW=y + +# +# Options for glibc +# +CT_LIBC_GLIBC_PKG_KSYM="GLIBC" +CT_GLIBC_DIR_NAME="glibc" +CT_GLIBC_USE_GNU=y +CT_GLIBC_USE="GLIBC" +CT_GLIBC_PKG_NAME="glibc" +CT_GLIBC_SRC_RELEASE=y +CT_GLIBC_PATCH_ORDER="global" +# CT_GLIBC_V_2_28 is not set +# CT_GLIBC_V_2_27 is not set +# CT_GLIBC_V_2_26 is not set +# CT_GLIBC_V_2_25 is not set +# CT_GLIBC_V_2_24 is not set +# CT_GLIBC_V_2_23 is not set +# CT_GLIBC_V_2_19 is not set +CT_GLIBC_V_2_17=y +# CT_GLIBC_V_2_12_1 is not set +# CT_GLIBC_NO_VERSIONS is not set +CT_GLIBC_VERSION="2.17" +CT_GLIBC_MIRRORS="$(CT_Mirrors GNU glibc)" +CT_GLIBC_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GLIBC_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GLIBC_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz" +CT_GLIBC_SIGNATURE_FORMAT="packed/.sig" +CT_GLIBC_2_29_or_older=y +CT_GLIBC_older_than_2_29=y +CT_GLIBC_REQUIRE_older_than_2_29=y +CT_GLIBC_2_27_or_older=y +CT_GLIBC_older_than_2_27=y +CT_GLIBC_2_26_or_older=y +CT_GLIBC_older_than_2_26=y +CT_GLIBC_2_25_or_older=y +CT_GLIBC_older_than_2_25=y +CT_GLIBC_2_24_or_older=y +CT_GLIBC_older_than_2_24=y +CT_GLIBC_2_23_or_older=y +CT_GLIBC_older_than_2_23=y +CT_GLIBC_2_20_or_older=y +CT_GLIBC_older_than_2_20=y +CT_GLIBC_2_17_or_later=y +CT_GLIBC_2_17_or_older=y +CT_GLIBC_later_than_2_14=y +CT_GLIBC_2_14_or_later=y +CT_GLIBC_DEP_KERNEL_HEADERS_VERSION=y +CT_GLIBC_DEP_BINUTILS=y +CT_GLIBC_DEP_GCC=y +CT_GLIBC_DEP_PYTHON=y +CT_GLIBC_HAS_NPTL_ADDON=y +CT_GLIBC_HAS_PORTS_ADDON=y +CT_GLIBC_HAS_LIBIDN_ADDON=y +CT_GLIBC_USE_PORTS_ADDON=y +CT_GLIBC_USE_NPTL_ADDON=y +# CT_GLIBC_USE_LIBIDN_ADDON is not set +CT_GLIBC_HAS_OBSOLETE_RPC=y +CT_GLIBC_EXTRA_CONFIG_ARRAY="" +CT_GLIBC_CONFIGPARMS="" +CT_GLIBC_EXTRA_CFLAGS="" +CT_GLIBC_ENABLE_OBSOLETE_RPC=y +# CT_GLIBC_DISABLE_VERSIONING is not set +CT_GLIBC_OLDEST_ABI="" +CT_GLIBC_FORCE_UNWIND=y +# CT_GLIBC_LOCALES is not set +# CT_GLIBC_KERNEL_VERSION_NONE is not set +CT_GLIBC_KERNEL_VERSION_AS_HEADERS=y +# CT_GLIBC_KERNEL_VERSION_CHOSEN is not set +CT_GLIBC_MIN_KERNEL="3.2.101" +# CT_GLIBC_SSP_DEFAULT is not set +# CT_GLIBC_SSP_NO is not set +# CT_GLIBC_SSP_YES is not set +# CT_GLIBC_SSP_ALL is not set +# CT_GLIBC_SSP_STRONG is not set +# CT_NEWLIB_USE_REDHAT is not set +CT_ALL_LIBC_CHOICES="AVR_LIBC BIONIC GLIBC MINGW_W64 MOXIEBOX MUSL NEWLIB NONE UCLIBC" CT_LIBC_SUPPORT_THREADS_ANY=y CT_LIBC_SUPPORT_THREADS_NATIVE=y @@ -343,100 +462,71 @@ CT_LIBC_SUPPORT_THREADS_NATIVE=y # Common C library options # CT_THREADS_NATIVE=y +# CT_CREATE_LDSO_CONF is not set CT_LIBC_XLDD=y -# -# glibc other options -# -CT_LIBC_GLIBC_PORTS_EXTERNAL=y -CT_LIBC_GLIBC_MAY_FORCE_PORTS=y -CT_LIBC_glibc_familly=y -CT_LIBC_GLIBC_EXTRA_CONFIG_ARRAY="" -CT_LIBC_GLIBC_CONFIGPARMS="" -CT_LIBC_GLIBC_EXTRA_CFLAGS="" -CT_LIBC_EXTRA_CC_ARGS="" -# CT_LIBC_DISABLE_VERSIONING is not set -CT_LIBC_OLDEST_ABI="" -CT_LIBC_GLIBC_FORCE_UNWIND=y -CT_LIBC_GLIBC_USE_PORTS=y -CT_LIBC_ADDONS_LIST="" - -# -# WARNING !!! -# - -# -# For glibc >= 2.8, it can happen that the tarballs -# - -# -# for the addons are not available for download. -# - -# -# If that happens, bad luck... Try a previous version -# - -# -# or try again later... :-( -# -# CT_LIBC_LOCALES is not set -# CT_LIBC_GLIBC_KERNEL_VERSION_NONE is not set -CT_LIBC_GLIBC_KERNEL_VERSION_AS_HEADERS=y -# CT_LIBC_GLIBC_KERNEL_VERSION_CHOSEN is not set -CT_LIBC_GLIBC_MIN_KERNEL="3.2.72" - # # C compiler # -CT_CC="gcc" CT_CC_CORE_PASSES_NEEDED=y CT_CC_CORE_PASS_1_NEEDED=y CT_CC_CORE_PASS_2_NEEDED=y -CT_CC_gcc=y -# CT_CC_GCC_SHOW_LINARO is not set -CT_CC_GCC_V_5_2_0=y -# CT_CC_GCC_V_4_9_3 is not set -# CT_CC_GCC_V_4_8_5 is not set -# CT_CC_GCC_V_4_7_4 is not set -# CT_CC_GCC_V_4_6_4 is not set -# CT_CC_GCC_V_4_5_4 is not set -# CT_CC_GCC_V_4_4_7 is not set -# CT_CC_GCC_V_4_3_6 is not set -# CT_CC_GCC_V_4_2_4 is not set -CT_CC_GCC_4_2_or_later=y -CT_CC_GCC_4_3_or_later=y -CT_CC_GCC_4_4_or_later=y -CT_CC_GCC_4_5_or_later=y -CT_CC_GCC_4_6_or_later=y -CT_CC_GCC_4_7_or_later=y -CT_CC_GCC_4_8_or_later=y -CT_CC_GCC_4_9_or_later=y -CT_CC_GCC_5=y -CT_CC_GCC_5_or_later=y -CT_CC_GCC_HAS_GRAPHITE=y -CT_CC_GCC_USE_GRAPHITE=y -CT_CC_GCC_HAS_LTO=y -CT_CC_GCC_USE_LTO=y -CT_CC_GCC_HAS_PKGVERSION_BUGURL=y -CT_CC_GCC_HAS_BUILD_ID=y -CT_CC_GCC_HAS_LNK_HASH_STYLE=y -CT_CC_GCC_USE_GMP_MPFR=y -CT_CC_GCC_USE_MPC=y -CT_CC_GCC_HAS_LIBQUADMATH=y -CT_CC_GCC_HAS_LIBSANITIZER=y -CT_CC_GCC_VERSION="5.2.0" -# CT_CC_LANG_FORTRAN is not set +CT_CC_SUPPORT_CXX=y +CT_CC_SUPPORT_FORTRAN=y +CT_CC_SUPPORT_ADA=y +CT_CC_SUPPORT_OBJC=y +CT_CC_SUPPORT_OBJCXX=y +CT_CC_SUPPORT_GOLANG=y +CT_CC_GCC=y +CT_CC="gcc" +CT_CC_CHOICE_KSYM="GCC" +CT_CC_GCC_SHOW=y + +# +# Options for gcc +# +CT_CC_GCC_PKG_KSYM="GCC" +CT_GCC_DIR_NAME="gcc" +CT_GCC_USE_GNU=y +CT_GCC_USE="GCC" +CT_GCC_PKG_NAME="gcc" +CT_GCC_SRC_RELEASE=y +CT_GCC_PATCH_ORDER="global" +CT_GCC_V_8=y +# CT_GCC_V_7 is not set +# CT_GCC_V_6 is not set +# CT_GCC_V_5 is not set +# CT_GCC_V_4_9 is not set +# CT_GCC_NO_VERSIONS is not set +CT_GCC_VERSION="8.3.0" +CT_GCC_MIRRORS="$(CT_Mirrors GNU gcc/gcc-${CT_GCC_VERSION}) $(CT_Mirrors sourceware gcc/releases/gcc-${CT_GCC_VERSION})" +CT_GCC_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GCC_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GCC_ARCHIVE_FORMATS=".tar.xz .tar.gz" +CT_GCC_SIGNATURE_FORMAT="" +CT_GCC_later_than_7=y +CT_GCC_7_or_later=y +CT_GCC_later_than_6=y +CT_GCC_6_or_later=y +CT_GCC_later_than_5=y +CT_GCC_5_or_later=y +CT_GCC_later_than_4_9=y +CT_GCC_4_9_or_later=y +CT_GCC_later_than_4_8=y +CT_GCC_4_8_or_later=y +CT_CC_GCC_HAS_LIBMPX=y CT_CC_GCC_ENABLE_CXX_FLAGS="" CT_CC_GCC_CORE_EXTRA_CONFIG_ARRAY="" CT_CC_GCC_EXTRA_CONFIG_ARRAY="" -CT_CC_GCC_EXTRA_ENV_ARRAY="" CT_CC_GCC_STATIC_LIBSTDCXX=y # CT_CC_GCC_SYSTEM_ZLIB is not set +CT_CC_GCC_CONFIG_TLS=m # # Optimisation features # +CT_CC_GCC_USE_GRAPHITE=y +CT_CC_GCC_USE_LTO=y # # Settings for libraries running on target @@ -465,97 +555,208 @@ CT_CC_GCC_DEC_FLOAT_AUTO=y # CT_CC_GCC_DEC_FLOAT_BID is not set # CT_CC_GCC_DEC_FLOAT_DPD is not set # CT_CC_GCC_DEC_FLOATS_NO is not set -CT_CC_SUPPORT_CXX=y -CT_CC_SUPPORT_FORTRAN=y -CT_CC_SUPPORT_JAVA=y -CT_CC_SUPPORT_ADA=y -CT_CC_SUPPORT_OBJC=y -CT_CC_SUPPORT_OBJCXX=y -CT_CC_SUPPORT_GOLANG=y +CT_ALL_CC_CHOICES="GCC" # # Additional supported languages: # CT_CC_LANG_CXX=y -# CT_CC_LANG_JAVA is not set +# CT_CC_LANG_FORTRAN is not set # # Debug facilities # -# CT_DEBUG_dmalloc is not set -# CT_DEBUG_duma is not set -# CT_DEBUG_gdb is not set -# CT_DEBUG_ltrace is not set -# CT_DEBUG_strace is not set +# CT_DEBUG_DUMA is not set +# CT_DEBUG_GDB is not set +# CT_DEBUG_LTRACE is not set +# CT_DEBUG_STRACE is not set +CT_ALL_DEBUG_CHOICES="DUMA GDB LTRACE STRACE" # # Companion libraries # -CT_COMPLIBS_NEEDED=y +# CT_COMPLIBS_CHECK is not set +# CT_COMP_LIBS_CLOOG is not set +# CT_COMP_LIBS_EXPAT is not set +CT_COMP_LIBS_GETTEXT=y +CT_COMP_LIBS_GETTEXT_PKG_KSYM="GETTEXT" +CT_GETTEXT_DIR_NAME="gettext" +CT_GETTEXT_PKG_NAME="gettext" +CT_GETTEXT_SRC_RELEASE=y +CT_GETTEXT_PATCH_ORDER="global" +CT_GETTEXT_V_0_19_8_1=y +# CT_GETTEXT_NO_VERSIONS is not set +CT_GETTEXT_VERSION="0.19.8.1" +CT_GETTEXT_MIRRORS="$(CT_Mirrors GNU gettext)" +CT_GETTEXT_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GETTEXT_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GETTEXT_ARCHIVE_FORMATS=".tar.xz .tar.lz .tar.gz" +CT_GETTEXT_SIGNATURE_FORMAT="packed/.sig" +CT_COMP_LIBS_GMP=y +CT_COMP_LIBS_GMP_PKG_KSYM="GMP" +CT_GMP_DIR_NAME="gmp" +CT_GMP_PKG_NAME="gmp" +CT_GMP_SRC_RELEASE=y +CT_GMP_PATCH_ORDER="global" +CT_GMP_V_6_1=y +# CT_GMP_NO_VERSIONS is not set +CT_GMP_VERSION="6.1.2" +CT_GMP_MIRRORS="https://gmplib.org/download/gmp https://gmplib.org/download/gmp/archive $(CT_Mirrors GNU gmp)" +CT_GMP_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_GMP_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_GMP_ARCHIVE_FORMATS=".tar.xz .tar.lz .tar.bz2" +CT_GMP_SIGNATURE_FORMAT="packed/.sig" +CT_GMP_later_than_5_1_0=y +CT_GMP_5_1_0_or_later=y +CT_GMP_later_than_5_0_0=y +CT_GMP_5_0_0_or_later=y +CT_GMP_REQUIRE_5_0_0_or_later=y +CT_COMP_LIBS_ISL=y +CT_COMP_LIBS_ISL_PKG_KSYM="ISL" +CT_ISL_DIR_NAME="isl" +CT_ISL_PKG_NAME="isl" +CT_ISL_SRC_RELEASE=y +CT_ISL_PATCH_ORDER="global" +CT_ISL_V_0_20=y +# CT_ISL_V_0_19 is not set +# CT_ISL_V_0_18 is not set +# CT_ISL_V_0_17 is not set +# CT_ISL_V_0_16 is not set +# CT_ISL_V_0_15 is not set +# CT_ISL_NO_VERSIONS is not set +CT_ISL_VERSION="0.20" +CT_ISL_MIRRORS="http://isl.gforge.inria.fr" +CT_ISL_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_ISL_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_ISL_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz" +CT_ISL_SIGNATURE_FORMAT="" +CT_ISL_later_than_0_18=y +CT_ISL_0_18_or_later=y +CT_ISL_later_than_0_15=y +CT_ISL_0_15_or_later=y +CT_ISL_REQUIRE_0_15_or_later=y +CT_ISL_later_than_0_14=y +CT_ISL_0_14_or_later=y +CT_ISL_REQUIRE_0_14_or_later=y +CT_ISL_later_than_0_13=y +CT_ISL_0_13_or_later=y +CT_ISL_later_than_0_12=y +CT_ISL_0_12_or_later=y +CT_ISL_REQUIRE_0_12_or_later=y +# CT_COMP_LIBS_LIBELF is not set +CT_COMP_LIBS_LIBICONV=y +CT_COMP_LIBS_LIBICONV_PKG_KSYM="LIBICONV" +CT_LIBICONV_DIR_NAME="libiconv" +CT_LIBICONV_PKG_NAME="libiconv" +CT_LIBICONV_SRC_RELEASE=y +CT_LIBICONV_PATCH_ORDER="global" +CT_LIBICONV_V_1_15=y +# CT_LIBICONV_NO_VERSIONS is not set +CT_LIBICONV_VERSION="1.15" +CT_LIBICONV_MIRRORS="$(CT_Mirrors GNU libiconv)" +CT_LIBICONV_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_LIBICONV_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_LIBICONV_ARCHIVE_FORMATS=".tar.gz" +CT_LIBICONV_SIGNATURE_FORMAT="packed/.sig" +CT_COMP_LIBS_MPC=y +CT_COMP_LIBS_MPC_PKG_KSYM="MPC" +CT_MPC_DIR_NAME="mpc" +CT_MPC_PKG_NAME="mpc" +CT_MPC_SRC_RELEASE=y +CT_MPC_PATCH_ORDER="global" +CT_MPC_V_1_1=y +# CT_MPC_V_1_0 is not set +# CT_MPC_NO_VERSIONS is not set +CT_MPC_VERSION="1.1.0" +CT_MPC_MIRRORS="http://www.multiprecision.org/downloads $(CT_Mirrors GNU mpc)" +CT_MPC_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_MPC_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_MPC_ARCHIVE_FORMATS=".tar.gz" +CT_MPC_SIGNATURE_FORMAT="packed/.sig" +CT_MPC_1_1_0_or_later=y +CT_MPC_1_1_0_or_older=y +CT_COMP_LIBS_MPFR=y +CT_COMP_LIBS_MPFR_PKG_KSYM="MPFR" +CT_MPFR_DIR_NAME="mpfr" +CT_MPFR_PKG_NAME="mpfr" +CT_MPFR_SRC_RELEASE=y +CT_MPFR_PATCH_ORDER="global" +CT_MPFR_V_4_0=y +# CT_MPFR_V_3_1 is not set +# CT_MPFR_NO_VERSIONS is not set +CT_MPFR_VERSION="4.0.2" +CT_MPFR_MIRRORS="http://www.mpfr.org/mpfr-${CT_MPFR_VERSION} $(CT_Mirrors GNU mpfr)" +CT_MPFR_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_MPFR_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_MPFR_ARCHIVE_FORMATS=".tar.xz .tar.bz2 .tar.gz .zip" +CT_MPFR_SIGNATURE_FORMAT="packed/.asc" +CT_MPFR_later_than_4_0_0=y +CT_MPFR_4_0_0_or_later=y +CT_MPFR_later_than_3_0_0=y +CT_MPFR_3_0_0_or_later=y +CT_MPFR_REQUIRE_3_0_0_or_later=y +CT_COMP_LIBS_NCURSES=y +CT_COMP_LIBS_NCURSES_PKG_KSYM="NCURSES" +CT_NCURSES_DIR_NAME="ncurses" +CT_NCURSES_PKG_NAME="ncurses" +CT_NCURSES_SRC_RELEASE=y +CT_NCURSES_PATCH_ORDER="global" +CT_NCURSES_V_6_1=y +# CT_NCURSES_V_6_0 is not set +# CT_NCURSES_NO_VERSIONS is not set +CT_NCURSES_VERSION="6.1" +CT_NCURSES_MIRRORS="ftp://invisible-island.net/ncurses $(CT_Mirrors GNU ncurses)" +CT_NCURSES_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_NCURSES_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_NCURSES_ARCHIVE_FORMATS=".tar.gz" +CT_NCURSES_SIGNATURE_FORMAT="packed/.sig" +CT_NCURSES_HOST_CONFIG_ARGS="" +CT_NCURSES_HOST_DISABLE_DB=y +CT_NCURSES_HOST_FALLBACKS="linux,xterm,xterm-color,xterm-256color,vt100" +CT_NCURSES_TARGET_CONFIG_ARGS="" +# CT_NCURSES_TARGET_DISABLE_DB is not set +CT_NCURSES_TARGET_FALLBACKS="" +CT_COMP_LIBS_ZLIB=y +CT_COMP_LIBS_ZLIB_PKG_KSYM="ZLIB" +CT_ZLIB_DIR_NAME="zlib" +CT_ZLIB_PKG_NAME="zlib" +CT_ZLIB_SRC_RELEASE=y +CT_ZLIB_PATCH_ORDER="global" +CT_ZLIB_V_1_2_11=y +# CT_ZLIB_NO_VERSIONS is not set +CT_ZLIB_VERSION="1.2.11" +CT_ZLIB_MIRRORS="http://downloads.sourceforge.net/project/libpng/zlib/${CT_ZLIB_VERSION}" +CT_ZLIB_ARCHIVE_FILENAME="@{pkg_name}-@{version}" +CT_ZLIB_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" +CT_ZLIB_ARCHIVE_FORMATS=".tar.xz .tar.gz" +CT_ZLIB_SIGNATURE_FORMAT="packed/.asc" +CT_ALL_COMP_LIBS_CHOICES="CLOOG EXPAT GETTEXT GMP ISL LIBELF LIBICONV MPC MPFR NCURSES ZLIB" CT_LIBICONV_NEEDED=y CT_GETTEXT_NEEDED=y CT_GMP_NEEDED=y CT_MPFR_NEEDED=y CT_ISL_NEEDED=y CT_MPC_NEEDED=y -CT_COMPLIBS=y +CT_NCURSES_NEEDED=y +CT_ZLIB_NEEDED=y CT_LIBICONV=y CT_GETTEXT=y CT_GMP=y CT_MPFR=y CT_ISL=y CT_MPC=y -CT_LIBICONV_V_1_14=y -CT_LIBICONV_VERSION="1.14" -CT_GETTEXT_V_0_19_6=y -CT_GETTEXT_VERSION="0.19.6" -CT_GMP_V_6_0_0=y -# CT_GMP_V_5_1_3 is not set -# CT_GMP_V_5_1_1 is not set -# CT_GMP_V_5_0_2 is not set -# CT_GMP_V_5_0_1 is not set -# CT_GMP_V_4_3_2 is not set -# CT_GMP_V_4_3_1 is not set -# CT_GMP_V_4_3_0 is not set -CT_GMP_5_0_2_or_later=y -CT_GMP_VERSION="6.0.0a" -CT_MPFR_V_3_1_3=y -# CT_MPFR_V_3_1_2 is not set -# CT_MPFR_V_3_1_0 is not set -# CT_MPFR_V_3_0_1 is not set -# CT_MPFR_V_3_0_0 is not set -# CT_MPFR_V_2_4_2 is not set -# CT_MPFR_V_2_4_1 is not set -# CT_MPFR_V_2_4_0 is not set -CT_MPFR_VERSION="3.1.3" -CT_ISL_V_0_14=y -# CT_ISL_V_0_12_2 is not set -CT_ISL_V_0_14_or_later=y -CT_ISL_V_0_12_or_later=y -CT_ISL_VERSION="0.14" -# CT_CLOOG_V_0_18_4 is not set -# CT_CLOOG_V_0_18_1 is not set -# CT_CLOOG_V_0_18_0 is not set -CT_MPC_V_1_0_3=y -# CT_MPC_V_1_0_2 is not set -# CT_MPC_V_1_0_1 is not set -# CT_MPC_V_1_0 is not set -# CT_MPC_V_0_9 is not set -# CT_MPC_V_0_8_2 is not set -# CT_MPC_V_0_8_1 is not set -# CT_MPC_V_0_7 is not set -CT_MPC_VERSION="1.0.3" - -# -# Companion libraries common options -# -# CT_COMPLIBS_CHECK is not set +CT_NCURSES=y +CT_ZLIB=y # # Companion tools # - -# -# READ HELP before you say 'Y' below !!! -# -# CT_COMP_TOOLS is not set +# CT_COMP_TOOLS_FOR_HOST is not set +# CT_COMP_TOOLS_AUTOCONF is not set +# CT_COMP_TOOLS_AUTOMAKE is not set +# CT_COMP_TOOLS_BISON is not set +# CT_COMP_TOOLS_DTC is not set +# CT_COMP_TOOLS_LIBTOOL is not set +# CT_COMP_TOOLS_M4 is not set +# CT_COMP_TOOLS_MAKE is not set +CT_ALL_COMP_TOOLS_CHOICES="AUTOCONF AUTOMAKE BISON DTC LIBTOOL M4 MAKE" diff --git a/src/ci/docker/dist-armhf-linux/patches/glibc/ports-2.16.0/001-arm-libgcc_s_resume-used.patch b/src/ci/docker/dist-armhf-linux/patches/glibc/ports-2.16.0/001-arm-libgcc_s_resume-used.patch deleted file mode 100644 index 871d5225c0f71..0000000000000 --- a/src/ci/docker/dist-armhf-linux/patches/glibc/ports-2.16.0/001-arm-libgcc_s_resume-used.patch +++ /dev/null @@ -1,48 +0,0 @@ -commit bdb24c2851fd5f0ad9b82d7ea1db911d334b02d2 -Author: Joseph Myers -Date: Tue May 20 21:27:13 2014 +0000 - - Fix ARM build with GCC trunk. - - sysdeps/unix/sysv/linux/arm/unwind-resume.c and - sysdeps/unix/sysv/linux/arm/unwind-forcedunwind.c have static - variables that are written in C code but only read from toplevel asms. - Current GCC trunk now optimizes away such apparently write-only static - variables, so causing a build failure. This patch marks those - variables with __attribute_used__ to avoid that optimization. - - Tested that this fixes the build for ARM. - - * sysdeps/unix/sysv/linux/arm/unwind-forcedunwind.c - (libgcc_s_resume): Use __attribute_used__. - * sysdeps/unix/sysv/linux/arm/unwind-resume.c (libgcc_s_resume): - Likewise. - -diff --git a/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c b/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c -index 29e2c2b00b04..e848bfeffdcb 100644 ---- a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c -+++ b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c -@@ -22,7 +22,8 @@ - #include - - static void *libgcc_s_handle; --static void (*libgcc_s_resume) (struct _Unwind_Exception *exc); -+static void (*libgcc_s_resume) (struct _Unwind_Exception *exc) -+ __attribute_used__; - static _Unwind_Reason_Code (*libgcc_s_personality) - (_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *); - static _Unwind_Reason_Code (*libgcc_s_forcedunwind) -diff --git a/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c b/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c -index 285b99b5ed0d..48d00fc83641 100644 ---- a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c -+++ b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c -@@ -20,7 +20,8 @@ - #include - #include - --static void (*libgcc_s_resume) (struct _Unwind_Exception *exc); -+static void (*libgcc_s_resume) (struct _Unwind_Exception *exc) -+ __attribute_used__; - static _Unwind_Reason_Code (*libgcc_s_personality) - (_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *); - diff --git a/src/ci/docker/dist-armv7-linux/Dockerfile b/src/ci/docker/dist-armv7-linux/Dockerfile index 417171a861d4a..649049da5dfe3 100644 --- a/src/ci/docker/dist-armv7-linux/Dockerfile +++ b/src/ci/docker/dist-armv7-linux/Dockerfile @@ -3,8 +3,8 @@ FROM ubuntu:16.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -COPY dist-armv7-linux/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh +COPY scripts/crosstool-ng-1.24.sh /scripts/ +RUN sh /scripts/crosstool-ng-1.24.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh @@ -27,5 +27,5 @@ ENV CC_armv7_unknown_linux_gnueabihf=armv7-unknown-linux-gnueabihf-gcc \ ENV HOSTS=armv7-unknown-linux-gnueabihf -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS --enable-full-tools --disable-docs +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-i586-gnu-i586-i686-musl/Dockerfile b/src/ci/docker/dist-i586-gnu-i586-i686-musl/Dockerfile index 61c363fbfd675..996fffeb871cf 100644 --- a/src/ci/docker/dist-i586-gnu-i586-i686-musl/Dockerfile +++ b/src/ci/docker/dist-i586-gnu-i586-i686-musl/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ xz-utils \ @@ -46,5 +46,5 @@ ENV CFLAGS_i586_unknown_linux_musl=-Wa,-mrelax-relocations=no ENV TARGETS=i586-unknown-linux-gnu,i686-unknown-linux-musl ENV SCRIPT \ - python2.7 ../x.py test --target $TARGETS && \ - python2.7 ../x.py dist --target $TARGETS,i586-unknown-linux-musl + python3 ../x.py test --target $TARGETS && \ + python3 ../x.py dist --target $TARGETS,i586-unknown-linux-musl diff --git a/src/ci/docker/dist-i686-freebsd/Dockerfile b/src/ci/docker/dist-i686-freebsd/Dockerfile index 6f6a663a33093..7978bb7086965 100644 --- a/src/ci/docker/dist-i686-freebsd/Dockerfile +++ b/src/ci/docker/dist-i686-freebsd/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -30,4 +30,4 @@ ENV \ ENV HOSTS=i686-unknown-freebsd ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-mips-linux/Dockerfile b/src/ci/docker/dist-mips-linux/Dockerfile index 466def1f80fbf..57a7fc25b5c13 100644 --- a/src/ci/docker/dist-mips-linux/Dockerfile +++ b/src/ci/docker/dist-mips-linux/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -23,4 +23,4 @@ RUN sh /scripts/sccache.sh ENV HOSTS=mips-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-mips64-linux/Dockerfile b/src/ci/docker/dist-mips64-linux/Dockerfile index 2205b733e99f1..63f1028e2be59 100644 --- a/src/ci/docker/dist-mips64-linux/Dockerfile +++ b/src/ci/docker/dist-mips64-linux/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -22,4 +22,4 @@ RUN sh /scripts/sccache.sh ENV HOSTS=mips64-unknown-linux-gnuabi64 ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-mips64el-linux/Dockerfile b/src/ci/docker/dist-mips64el-linux/Dockerfile index f1d9dad46ea3f..a51edbc9c7923 100644 --- a/src/ci/docker/dist-mips64el-linux/Dockerfile +++ b/src/ci/docker/dist-mips64el-linux/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -23,4 +23,4 @@ RUN sh /scripts/sccache.sh ENV HOSTS=mips64el-unknown-linux-gnuabi64 ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-mipsel-linux/Dockerfile b/src/ci/docker/dist-mipsel-linux/Dockerfile index ee73e29c76e35..908cef90cef69 100644 --- a/src/ci/docker/dist-mipsel-linux/Dockerfile +++ b/src/ci/docker/dist-mipsel-linux/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -22,4 +22,4 @@ RUN sh /scripts/sccache.sh ENV HOSTS=mipsel-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-powerpc-linux/Dockerfile b/src/ci/docker/dist-powerpc-linux/Dockerfile index 8c052db1b0dde..b8792b0c7fa92 100644 --- a/src/ci/docker/dist-powerpc-linux/Dockerfile +++ b/src/ci/docker/dist-powerpc-linux/Dockerfile @@ -35,4 +35,4 @@ ENV \ ENV HOSTS=powerpc-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-powerpc64-linux/Dockerfile b/src/ci/docker/dist-powerpc64-linux/Dockerfile index bb30210c0563a..a790a143ac5e7 100644 --- a/src/ci/docker/dist-powerpc64-linux/Dockerfile +++ b/src/ci/docker/dist-powerpc64-linux/Dockerfile @@ -36,4 +36,4 @@ ENV \ ENV HOSTS=powerpc64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/dist-powerpc64le-linux/Dockerfile index ee9e455048352..5c17bc321c171 100644 --- a/src/ci/docker/dist-powerpc64le-linux/Dockerfile +++ b/src/ci/docker/dist-powerpc64le-linux/Dockerfile @@ -33,4 +33,4 @@ ENV \ ENV HOSTS=powerpc64le-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-s390x-linux/Dockerfile b/src/ci/docker/dist-s390x-linux/Dockerfile index 7ba6fe643c2ae..76d29a471c396 100644 --- a/src/ci/docker/dist-s390x-linux/Dockerfile +++ b/src/ci/docker/dist-s390x-linux/Dockerfile @@ -35,4 +35,4 @@ ENV \ ENV HOSTS=s390x-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-various-1/Dockerfile b/src/ci/docker/dist-various-1/Dockerfile index 2a68a25be21b3..35b598e20f099 100644 --- a/src/ci/docker/dist-various-1/Dockerfile +++ b/src/ci/docker/dist-various-1/Dockerfile @@ -18,7 +18,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ python3 \ git \ cmake \ @@ -72,6 +71,9 @@ RUN ./install-mips-musl.sh COPY dist-various-1/install-mipsel-musl.sh /build RUN ./install-mipsel-musl.sh +COPY dist-various-1/install-aarch64-none-elf.sh /build +RUN ./install-aarch64-none-elf.sh + # Suppress some warnings in the openwrt toolchains we downloaded ENV STAGING_DIR=/tmp @@ -140,6 +142,8 @@ ENV TARGETS=$TARGETS,armv5te-unknown-linux-gnueabi ENV TARGETS=$TARGETS,armv5te-unknown-linux-musleabi ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabihf ENV TARGETS=$TARGETS,aarch64-unknown-linux-musl +ENV TARGETS=$TARGETS,aarch64-unknown-none +ENV TARGETS=$TARGETS,aarch64-unknown-none-softfloat ENV TARGETS=$TARGETS,sparc64-unknown-linux-gnu ENV TARGETS=$TARGETS,x86_64-unknown-redox ENV TARGETS=$TARGETS,thumbv6m-none-eabi @@ -178,6 +182,10 @@ ENV CC_mipsel_unknown_linux_musl=mipsel-openwrt-linux-gcc \ CC_armv7a_none_eabihf=arm-none-eabi-gcc \ CFLAGS_armv7a_none_eabi=-march=armv7-a \ CFLAGS_armv7a_none_eabihf=-march=armv7-a+vfpv3 \ + CC_aarch64_unknown_none_softfloat=aarch64-none-elf-gcc \ + CFLAGS_aarch64_unknown_none_softfloat=-mstrict-align -march=armv8-a+nofp+nosimd \ + CC_aarch64_unknown_none=aarch64-none-elf-gcc \ + CFLAGS_aarch64_unknown_none=-mstrict-align -march=armv8-a+fp+simd \ CC_riscv64gc_unknown_linux_gnu=riscv64-unknown-linux-gnu-gcc \ AR_riscv64gc_unknown_linux_gnu=riscv64-unknown-linux-gnu-ar \ CXX_riscv64gc_unknown_linux_gnu=riscv64-unknown-linux-gnu-g++ \ @@ -200,8 +208,8 @@ ENV RUST_CONFIGURE_ARGS \ --disable-docs ENV SCRIPT \ - python2.7 ../x.py test --target $RUN_MAKE_TARGETS src/test/run-make && \ - python2.7 ../x.py dist --target $TARGETS + python3 ../x.py test --target $RUN_MAKE_TARGETS src/test/run-make && \ + python3 ../x.py dist --target $TARGETS # sccache COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/dist-various-1/install-aarch64-none-elf.sh b/src/ci/docker/dist-various-1/install-aarch64-none-elf.sh new file mode 100755 index 0000000000000..d72976c285842 --- /dev/null +++ b/src/ci/docker/dist-various-1/install-aarch64-none-elf.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -ex + +curl -L https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf.tar.xz \ +| tar --extract --xz --strip 1 --directory /usr/local diff --git a/src/ci/docker/dist-various-2/Dockerfile b/src/ci/docker/dist-various-2/Dockerfile index 5bb5436bec59d..5864b5ffab286 100644 --- a/src/ci/docker/dist-various-2/Dockerfile +++ b/src/ci/docker/dist-various-2/Dockerfile @@ -17,7 +17,7 @@ RUN apt-get update && apt-get build-dep -y clang llvm && apt-get install -y --no libmpfr-dev \ ninja-build \ nodejs \ - python2.7-dev \ + python3-dev \ software-properties-common \ unzip \ # Needed for apt-key to work: @@ -110,4 +110,4 @@ ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --disable-docs \ --set target.wasm32-wasi.wasi-root=/wasm32-wasi \ --musl-root-armv7=/musl-armv7 -ENV SCRIPT python2.7 ../x.py dist --target $TARGETS +ENV SCRIPT python3 ../x.py dist --target $TARGETS diff --git a/src/ci/docker/dist-x86_64-freebsd/Dockerfile b/src/ci/docker/dist-x86_64-freebsd/Dockerfile index 698b81a92e935..12170a3661487 100644 --- a/src/ci/docker/dist-x86_64-freebsd/Dockerfile +++ b/src/ci/docker/dist-x86_64-freebsd/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -30,4 +30,4 @@ ENV \ ENV HOSTS=x86_64-unknown-freebsd ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-x86_64-musl/Dockerfile b/src/ci/docker/dist-x86_64-musl/Dockerfile index 385eefde846c2..c026506b10661 100644 --- a/src/ci/docker/dist-x86_64-musl/Dockerfile +++ b/src/ci/docker/dist-x86_64-musl/Dockerfile @@ -7,7 +7,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ wget \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ xz-utils \ @@ -34,6 +34,7 @@ ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ --enable-extended \ --disable-docs \ + --enable-lld \ --set target.x86_64-unknown-linux-musl.crt-static=false \ --build $HOSTS @@ -47,4 +48,4 @@ ENV CFLAGS_x86_64_unknown_linux_musl="-Wa,-mrelax-relocations=no -Wa,--compress- -Wl,--compress-debug-sections=none" # To run native tests replace `dist` below with `test` -ENV SCRIPT python2.7 ../x.py dist --build $HOSTS +ENV SCRIPT python3 ../x.py dist --build $HOSTS diff --git a/src/ci/docker/dist-x86_64-netbsd/Dockerfile b/src/ci/docker/dist-x86_64-netbsd/Dockerfile index 44b1aaa24b19d..135bb33cef717 100644 --- a/src/ci/docker/dist-x86_64-netbsd/Dockerfile +++ b/src/ci/docker/dist-x86_64-netbsd/Dockerfile @@ -19,4 +19,4 @@ ENV \ ENV HOSTS=x86_64-unknown-netbsd ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/i686-gnu-nopt/Dockerfile b/src/ci/docker/i686-gnu-nopt/Dockerfile index 517b59c38dcb0..c15b437e6d315 100644 --- a/src/ci/docker/i686-gnu-nopt/Dockerfile +++ b/src/ci/docker/i686-gnu-nopt/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -18,7 +18,7 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests -ENV SCRIPT python2.7 ../x.py test +ENV SCRIPT python3 ../x.py test # FIXME(#59637) takes too long on CI right now ENV NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 diff --git a/src/ci/docker/i686-gnu/Dockerfile b/src/ci/docker/i686-gnu/Dockerfile index 03db3ba0995d6..377f07cef4e46 100644 --- a/src/ci/docker/i686-gnu/Dockerfile +++ b/src/ci/docker/i686-gnu/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -20,7 +20,7 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu # Exclude some tests that are unlikely to be platform specific, to speed up # this slow job. -ENV SCRIPT python2.7 ../x.py test \ +ENV SCRIPT python3 ../x.py test \ --exclude src/bootstrap \ --exclude src/test/rustdoc-js \ --exclude src/tools/error_index_generator \ diff --git a/src/ci/docker/mingw-check/Dockerfile b/src/ci/docker/mingw-check/Dockerfile index e973ba2e33c64..97e4d3fd7499e 100644 --- a/src/ci/docker/mingw-check/Dockerfile +++ b/src/ci/docker/mingw-check/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -22,8 +22,10 @@ RUN sh /scripts/sccache.sh COPY mingw-check/validate-toolstate.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 -ENV SCRIPT python2.7 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ - python2.7 ../x.py build --stage 0 src/tools/build-manifest && \ - python2.7 ../x.py test --stage 0 src/tools/compiletest && \ - python2.7 ../x.py test src/tools/tidy && \ +ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors && \ + python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py build --stage 0 src/tools/build-manifest && \ + python3 ../x.py test --stage 0 src/tools/compiletest && \ + python3 ../x.py test src/tools/tidy && \ + python3 ../x.py doc --stage 0 src/libstd && \ /scripts/validate-toolstate.sh diff --git a/src/ci/docker/mingw-check/validate-toolstate.sh b/src/ci/docker/mingw-check/validate-toolstate.sh index 2ebf1d6d5ae7f..c6d728eb80dd0 100755 --- a/src/ci/docker/mingw-check/validate-toolstate.sh +++ b/src/ci/docker/mingw-check/validate-toolstate.sh @@ -7,12 +7,12 @@ IFS=$'\n\t' rm -rf rust-toolstate git clone --depth=1 https://github.com/rust-lang-nursery/rust-toolstate.git cd rust-toolstate -python2.7 "../../src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" \ +python3 "../../src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" \ "$(git log --format=%s -n1 HEAD)" "" "" # Only check maintainers if this build is supposed to publish toolstate. # Builds that are not supposed to publish don't have the access token. if [ -n "${TOOLSTATE_PUBLISH+is_set}" ]; then - TOOLSTATE_VALIDATE_MAINTAINERS_REPO=rust-lang/rust python2.7 \ + TOOLSTATE_VALIDATE_MAINTAINERS_REPO=rust-lang/rust python3 \ "../../src/tools/publish_toolstate.py" fi cd .. diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index f29f9f3bf1c45..d891ad1b6680e 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -17,6 +17,8 @@ dist=$objdir/build/dist source "$ci_dir/shared.sh" +CACHE_DOMAIN="${CACHE_DOMAIN:-ci-caches.rust-lang.org}" + if [ -f "$docker_dir/$image/Dockerfile" ]; then if [ "$CI" != "" ]; then hash_key=/tmp/.docker-hash-key.txt @@ -38,9 +40,7 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then cksum=$(sha512sum $hash_key | \ awk '{print $1}') - s3url="s3://$SCCACHE_BUCKET/docker/$cksum" - url="https://$SCCACHE_BUCKET.s3.amazonaws.com/docker/$cksum" - upload="aws s3 cp - $s3url" + url="https://$CACHE_DOMAIN/docker/$cksum" echo "Attempting to download $url" rm -f /tmp/rustci_docker_cache @@ -65,7 +65,9 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then -f "$dockerfile" \ "$context" - if [ "$upload" != "" ]; then + if [ "$CI" != "" ]; then + s3url="s3://$SCCACHE_BUCKET/docker/$cksum" + upload="aws s3 cp - $s3url" digest=$(docker inspect rust-ci --format '{{.Id}}') echo "Built container $digest" if ! grep -q "$digest" <(echo "$loaded_images"); then diff --git a/src/ci/docker/scripts/android-base-apt-get.sh b/src/ci/docker/scripts/android-base-apt-get.sh index 738410c58fcfa..391b68ea637b0 100644 --- a/src/ci/docker/scripts/android-base-apt-get.sh +++ b/src/ci/docker/scripts/android-base-apt-get.sh @@ -11,7 +11,7 @@ apt-get install -y --no-install-recommends \ libssl-dev \ make \ pkg-config \ - python2.7 \ + python3 \ sudo \ unzip \ xz-utils diff --git a/src/ci/docker/scripts/android-ndk.sh b/src/ci/docker/scripts/android-ndk.sh index 0db30e420e33a..dafcb3cb7a719 100644 --- a/src/ci/docker/scripts/android-ndk.sh +++ b/src/ci/docker/scripts/android-ndk.sh @@ -13,7 +13,7 @@ download_ndk() { make_standalone_toolchain() { # See https://developer.android.com/ndk/guides/standalone_toolchain.htm - python2.7 /android/ndk/ndk/build/tools/make_standalone_toolchain.py \ + python3 /android/ndk/ndk/build/tools/make_standalone_toolchain.py \ --install-dir /android/ndk/$1-$2 \ --arch $1 \ --api $2 diff --git a/src/ci/docker/scripts/cross-apt-packages.sh b/src/ci/docker/scripts/cross-apt-packages.sh index bb72e33def21c..7030cd74cae23 100644 --- a/src/ci/docker/scripts/cross-apt-packages.sh +++ b/src/ci/docker/scripts/cross-apt-packages.sh @@ -19,7 +19,7 @@ apt-get update && apt-get install -y --no-install-recommends \ make \ patch \ pkg-config \ - python2.7 \ + python3 \ sudo \ texinfo \ unzip \ diff --git a/src/ci/docker/dist-armv7-linux/crosstool-ng.sh b/src/ci/docker/scripts/crosstool-ng-1.24.sh similarity index 100% rename from src/ci/docker/dist-armv7-linux/crosstool-ng.sh rename to src/ci/docker/scripts/crosstool-ng-1.24.sh diff --git a/src/ci/docker/test-various/Dockerfile b/src/ci/docker/test-various/Dockerfile index 6a2600d875642..9276e4ed82d78 100644 --- a/src/ci/docker/test-various/Dockerfile +++ b/src/ci/docker/test-various/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python \ + python3 \ git \ cmake \ sudo \ @@ -40,7 +40,7 @@ ENV RUST_CONFIGURE_ARGS \ ENV NO_DEBUG_ASSERTIONS=1 ENV WASM_TARGETS=wasm32-unknown-unknown -ENV WASM_SCRIPT python2.7 /checkout/x.py test --target $WASM_TARGETS \ +ENV WASM_SCRIPT python3 /checkout/x.py test --target $WASM_TARGETS \ src/test/run-make \ src/test/ui \ src/test/compile-fail \ @@ -49,13 +49,13 @@ ENV WASM_SCRIPT python2.7 /checkout/x.py test --target $WASM_TARGETS \ src/libcore ENV NVPTX_TARGETS=nvptx64-nvidia-cuda -ENV NVPTX_SCRIPT python2.7 /checkout/x.py test --target $NVPTX_TARGETS \ +ENV NVPTX_SCRIPT python3 /checkout/x.py test --target $NVPTX_TARGETS \ src/test/run-make \ src/test/assembly ENV MUSL_TARGETS=x86_64-unknown-linux-musl \ CC_x86_64_unknown_linux_musl=x86_64-linux-musl-gcc \ CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++ -ENV MUSL_SCRIPT python2.7 /checkout/x.py test --target $MUSL_TARGETS +ENV MUSL_SCRIPT python3 /checkout/x.py test --target $MUSL_TARGETS ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT diff --git a/src/ci/docker/wasm32/Dockerfile b/src/ci/docker/wasm32/Dockerfile index a0f35afd995b3..91c492d03c179 100644 --- a/src/ci/docker/wasm32/Dockerfile +++ b/src/ci/docker/wasm32/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python \ + python3 \ git \ cmake \ sudo \ @@ -20,6 +20,9 @@ RUN bash /scripts/emscripten.sh COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +# emcc seems to need python to specifically be "python" and not "python3" +RUN ln `which python3` /usr/bin/python + ENV PATH=$PATH:/emsdk-portable ENV PATH=$PATH:/emsdk-portable/upstream/emscripten/ ENV PATH=$PATH:/emsdk-portable/node/12.9.1_64bit/bin/ @@ -35,7 +38,7 @@ ENV NO_CHANGE_USER=1 # FIXME: Re-enable these tests once https://github.com/rust-lang/cargo/pull/7476 # is picked up by CI -ENV SCRIPT python2.7 ../x.py test --target $TARGETS \ +ENV SCRIPT python3 ../x.py test --target $TARGETS \ --exclude src/libcore \ --exclude src/liballoc \ --exclude src/libproc_macro \ diff --git a/src/ci/docker/x86_64-gnu-aux/Dockerfile b/src/ci/docker/x86_64-gnu-aux/Dockerfile index 44bee199911b8..86ac0256d2820 100644 --- a/src/ci/docker/x86_64-gnu-aux/Dockerfile +++ b/src/ci/docker/x86_64-gnu-aux/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ libssl-dev \ diff --git a/src/ci/docker/x86_64-gnu-debug/Dockerfile b/src/ci/docker/x86_64-gnu-debug/Dockerfile index 890e13232c3fd..c5e41b8a75afe 100644 --- a/src/ci/docker/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/x86_64-gnu-debug/Dockerfile @@ -6,8 +6,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ - python2.7-dev \ + python3 \ + python3-dev \ libxml2-dev \ libncurses-dev \ libedit-dev \ @@ -40,5 +40,5 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.cxx=clang++ ENV SCRIPT \ - python2.7 ../x.py build && \ - python2.7 ../x.py test src/test/run-make-fulldeps --test-args clang + python3 ../x.py build && \ + python3 ../x.py test src/test/run-make-fulldeps --test-args clang diff --git a/src/ci/docker/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/x86_64-gnu-distcheck/Dockerfile index 364f45aba2c00..cc07a591cc17b 100644 --- a/src/ci/docker/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/x86_64-gnu-distcheck/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -19,7 +19,7 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false -ENV SCRIPT python2.7 ../x.py test distcheck +ENV SCRIPT python3 ../x.py test distcheck ENV DIST_SRC 1 # The purpose of this builder is to test that we can `./x.py test` successfully diff --git a/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile b/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile index 207f972c3cdae..de7ee6950b51b 100644 --- a/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile +++ b/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -21,7 +21,7 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-full-bootstrap -ENV SCRIPT python2.7 ../x.py build +ENV SCRIPT python3 ../x.py build # In general this just slows down the build and we're just a smoke test that # a full bootstrap works in general, so there's not much need to take this diff --git a/src/ci/docker/x86_64-gnu-llvm-7/Dockerfile b/src/ci/docker/x86_64-gnu-llvm-7/Dockerfile deleted file mode 100644 index 4b5d5cac5163c..0000000000000 --- a/src/ci/docker/x86_64-gnu-llvm-7/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -FROM ubuntu:18.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - make \ - file \ - curl \ - ca-certificates \ - python2.7 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-7-tools \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# 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-7 \ - --enable-llvm-link-shared \ - --set rust.thin-lto-import-instr-limit=10 - -ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && python2.7 ../x.py test src/tools/tidy - -# The purpose of this container isn't to test with debug assertions and -# this is run on all PRs, so let's get speedier builds by disabling these extra -# checks. -ENV NO_DEBUG_ASSERTIONS=1 -ENV NO_LLVM_ASSERTIONS=1 diff --git a/src/ci/docker/x86_64-gnu-llvm-8/Dockerfile b/src/ci/docker/x86_64-gnu-llvm-8/Dockerfile new file mode 100644 index 0000000000000..58fdc6f262376 --- /dev/null +++ b/src/ci/docker/x86_64-gnu-llvm-8/Dockerfile @@ -0,0 +1,55 @@ +FROM ubuntu:18.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + g++-arm-linux-gnueabi \ + make \ + file \ + curl \ + ca-certificates \ + python2.7 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-8-tools \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# 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-8 \ + --enable-llvm-link-shared \ + --set rust.thin-lto-import-instr-limit=10 + +ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && \ + # Run the `mir-opt` tests again but this time for a 32-bit target. + # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have + # both 32-bit and 64-bit outputs updated by the PR author, before + # the PR is approved and tested for merging. + # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, + # despite having different output on 32-bit vs 64-bit targets. + # + # HACK(eddyb) `armv5te` is used (not `i686`) to support older LLVM than LLVM 9: + # https://github.com/rust-lang/compiler-builtins/pull/311#issuecomment-612270089. + # This also requires `--pass=build` because we can't execute the tests + # on the `x86_64` host when they're built as `armv5te` binaries. + # (we're only interested in the MIR output, so this doesn't matter) + python2.7 ../x.py test src/test/mir-opt --pass=build \ + --target=armv5te-unknown-linux-gnueabi && \ + # Run tidy at the very end, after all the other tests. + python2.7 ../x.py test src/tools/tidy + +# The purpose of this container isn't to test with debug assertions and +# this is run on all PRs, so let's get speedier builds by disabling these extra +# checks. +ENV NO_DEBUG_ASSERTIONS=1 +ENV NO_LLVM_ASSERTIONS=1 diff --git a/src/ci/docker/x86_64-gnu-nopt/Dockerfile b/src/ci/docker/x86_64-gnu-nopt/Dockerfile index 6a5c7f5d9e610..096f67e13d1e6 100644 --- a/src/ci/docker/x86_64-gnu-nopt/Dockerfile +++ b/src/ci/docker/x86_64-gnu-nopt/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -21,4 +21,4 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \ --disable-optimize-tests \ --set rust.test-compare-mode -ENV SCRIPT python2.7 ../x.py test +ENV SCRIPT python3 ../x.py test diff --git a/src/ci/docker/x86_64-gnu-tools/Dockerfile b/src/ci/docker/x86_64-gnu-tools/Dockerfile index 51193402ee842..148e09f6ad104 100644 --- a/src/ci/docker/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/x86_64-gnu-tools/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ libssl-dev \ diff --git a/src/ci/docker/x86_64-gnu-tools/checktools.sh b/src/ci/docker/x86_64-gnu-tools/checktools.sh index e57fe2271398f..c9d1cb21da8b0 100755 --- a/src/ci/docker/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh @@ -7,14 +7,13 @@ X_PY="$1" # Try to test all the tools and store the build/test success in the TOOLSTATE_FILE set +e -python2.7 "$X_PY" test --no-fail-fast \ +python3 "$X_PY" test --no-fail-fast \ src/doc/book \ src/doc/nomicon \ src/doc/reference \ src/doc/rust-by-example \ src/doc/embedded-book \ src/doc/edition-guide \ - src/doc/rustc-guide \ src/tools/clippy \ src/tools/rls \ src/tools/rustfmt \ @@ -22,4 +21,4 @@ python2.7 "$X_PY" test --no-fail-fast \ set -e -python2.7 "$X_PY" test check-tools +python3 "$X_PY" test check-tools diff --git a/src/ci/docker/x86_64-gnu/Dockerfile b/src/ci/docker/x86_64-gnu/Dockerfile index b864c09ea8c45..af6e131806276 100644 --- a/src/ci/docker/x86_64-gnu/Dockerfile +++ b/src/ci/docker/x86_64-gnu/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ curl \ ca-certificates \ - python2.7 \ + python3 \ git \ cmake \ sudo \ @@ -23,4 +23,4 @@ ENV RUST_CONFIGURE_ARGS \ --enable-sanitizers \ --enable-profiler \ --enable-compiler-docs -ENV SCRIPT python2.7 ../x.py test +ENV SCRIPT python3 ../x.py test diff --git a/src/ci/exec-with-shell.py b/src/ci/exec-with-shell.py new file mode 100755 index 0000000000000..26ce69e33d9c3 --- /dev/null +++ b/src/ci/exec-with-shell.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# A simple wrapper that forwards the arguments to bash, unless the +# CI_OVERRIDE_SHELL environment variable is present: in that case the content +# of that environment variable is used as the shell path. + +import os +import sys +import subprocess + +try: + shell = os.environ["CI_OVERRIDE_SHELL"] +except KeyError: + shell = "bash" + +res = subprocess.call([shell] + sys.argv[1:]) +sys.exit(res) diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml new file mode 100644 index 0000000000000..1c120f8163459 --- /dev/null +++ b/src/ci/github-actions/ci.yml @@ -0,0 +1,649 @@ +###################################################### +# WARNING! Action needed when changing this file # +###################################################### + +# Due to GitHub Actions limitations, we can't use YAML Anchors directly in the +# CI configuration stored on the repository. To work around that this file is +# expanded by a tool in the repository, and the expansion is committed as well. +# +# After you make any change to the file you'll need to run this command: +# +# ./x.py run src/tools/expand-yaml-anchors +# +# ...and commit the file it updated in addition to this one. If you forget this +# step CI will fail. + +--- + +############################### +# YAML Anchors Definition # +############################### + +# This key contains most of the YAML anchors that will be used later in the +# document. YAML anchors allows us to greatly reduce duplication inside the CI +# configuration by reusing parts of the configuration. +# +# YAML anchors work by defining an anchor with `&anchor-name` and reusing its +# content in another place with `*anchor-name`. The special `<<` map key merges +# the content of the map with the content of the anchor (or list of anchors). +# +# The expand-yaml-anchors tool will automatically remove this block from the +# output YAML file. +x--expand-yaml-anchors--remove: + + - &shared-ci-variables + CI_JOB_NAME: ${{ matrix.name }} + + - &public-variables + SCCACHE_BUCKET: rust-lang-gha-caches + TOOLSTATE_REPO: https://github.com/pietroalbini/rust-toolstate + CACHE_DOMAIN: ci-caches-gha.rust-lang.org + + - &prod-variables + SCCACHE_BUCKET: rust-lang-gha-caches + DEPLOY_BUCKET: rust-lang-gha + TOOLSTATE_REPO: https://github.com/pietroalbini/rust-toolstate + TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/pietroalbini/rust-toolstate/issues + TOOLSTATE_PUBLISH: 1 + # AWS_SECRET_ACCESS_KEYs are stored in GitHub's secrets storage, named + # AWS_SECRET_ACCESS_KEY_. Including the key id in the name allows to + # rotate them in a single branch while keeping the old key in another + # branch, which wouldn't be possible if the key was named with the kind + # (caches, artifacts...). + CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZOMUQATD5 + ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZH5AYXDVF + CACHE_DOMAIN: ci-caches-gha.rust-lang.org + + - &base-job + env: {} + + - &job-linux-xl + os: ubuntu-latest-xl + <<: *base-job + + - &job-macos-xl + os: macos-latest # We don't have an XL builder for this + <<: *base-job + + - &job-windows-xl + os: windows-latest-xl + <<: *base-job + + - &step + if: success() && !env.SKIP_JOB + + - &base-ci-job + timeout-minutes: 600 + runs-on: "${{ matrix.os }}" + env: *shared-ci-variables + steps: + - name: disable git crlf conversion + run: git config --global core.autocrlf false + shell: bash + + - name: checkout the source code + uses: actions/checkout@v1 + with: + fetch-depth: 2 + + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' + <<: *step + + - name: add extra environment variables + run: src/ci/scripts/setup-environment.sh + env: + # Since it's not possible to merge `${{ matrix.env }}` with the other + # variables in `job..env`, the variables defined in the matrix + # are passed to the `setup-environment.sh` script encoded in JSON, + # which then uses log commands to actually set them. + EXTRA_VARIABLES: ${{ toJson(matrix.env) }} + <<: *step + + - name: decide whether to skip this job + run: src/ci/scripts/should-skip-this.sh + <<: *step + + - name: collect CPU statistics + run: src/ci/scripts/collect-cpu-stats.sh + <<: *step + + - name: show the current environment + run: src/ci/scripts/dump-environment.sh + <<: *step + + - name: install awscli + run: src/ci/scripts/install-awscli.sh + <<: *step + + - name: install sccache + run: src/ci/scripts/install-sccache.sh + <<: *step + + - name: install clang + run: src/ci/scripts/install-clang.sh + <<: *step + + - name: install WIX + run: src/ci/scripts/install-wix.sh + <<: *step + + - name: install InnoSetup + run: src/ci/scripts/install-innosetup.sh + <<: *step + + - name: ensure the build happens on a partition with enough space + run: src/ci/scripts/symlink-build-dir.sh + <<: *step + + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + <<: *step + + - name: install MSYS2 + run: src/ci/scripts/install-msys2.sh + <<: *step + + - name: install MSYS2 packages + run: src/ci/scripts/install-msys2-packages.sh + <<: *step + + - name: install MinGW + run: src/ci/scripts/install-mingw.sh + <<: *step + + - name: install ninja + run: src/ci/scripts/install-ninja.sh + <<: *step + + - name: enable ipv6 on Docker + run: src/ci/scripts/enable-docker-ipv6.sh + <<: *step + + # Disable automatic line ending conversion (again). On Windows, when we're + # installing dependencies, something switches the git configuration directory or + # re-enables autocrlf. We've not tracked down the exact cause -- and there may + # be multiple -- but this should ensure submodules are checked out with the + # appropriate line endings. + - name: disable git crlf conversion + run: src/ci/scripts/disable-git-crlf-conversion.sh + <<: *step + + - name: checkout submodules + run: src/ci/scripts/checkout-submodules.sh + <<: *step + + - name: ensure line endings are correct + run: src/ci/scripts/verify-line-endings.sh + <<: *step + + - name: run the build + run: src/ci/scripts/run-build-from-ci.sh + 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)] }} + TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} + <<: *step + + - name: upload artifacts to S3 + run: src/ci/scripts/upload-artifacts.sh + env: + AWS_ACCESS_KEY_ID: ${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }} + # Adding a condition on DEPLOY=1 or DEPLOY_ALT=1 is not needed as all deploy + # builders *should* have the AWS credentials available. Still, explicitly + # adding the condition is helpful as this way CI will not silently skip + # deploying artifacts from a dist builder if the variables are misconfigured, + # erroring about invalid credentials instead. + if: success() && !env.SKIP_JOB && (github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1') + <<: *step + + # These snippets are used by the try-success, try-failure, auto-success and auto-failure jobs. + # Check out their documentation for more information on why they're needed. + + - &base-outcome-job + name: bors build finished + runs-on: ubuntu-latest + + - &base-success-job + steps: + - name: mark the job as a success + run: exit 0 + shell: bash + <<: *base-outcome-job + + - &base-failure-job + steps: + - name: mark the job as a failure + run: exit 1 + shell: bash + <<: *base-outcome-job + +########################### +# Builders definition # +########################### + +name: CI +on: + push: + branches: + - auto + - try + - master + pull_request: + branches: + - "**" + +defaults: + run: + # While on Linux and macOS builders it just forwards the arguments to the + # system bash, this wrapper allows switching from the host's bash.exe to + # the one we install along with MSYS2 mid-build on Windows. + # + # Once the step to install MSYS2 is executed, the CI_OVERRIDE_SHELL + # environment variable is set pointing to our MSYS2's bash.exe. From that + # moment the host's bash.exe will not be called anymore. + # + # This is needed because we can't launch our own bash.exe from the host + # bash.exe, as that would load two different cygwin1.dll in memory, causing + # "cygwin heap mismatch" errors. + shell: python src/ci/exec-with-shell.py {0} + +jobs: + pr: + <<: *base-ci-job + name: PR + env: + <<: [*shared-ci-variables, *public-variables] + if: github.event_name == 'pull_request' + strategy: + matrix: + include: + - name: mingw-check + <<: *job-linux-xl + + - name: x86_64-gnu-llvm-8 + <<: *job-linux-xl + + - name: x86_64-gnu-tools + env: + CI_ONLY_WHEN_SUBMODULES_CHANGED: 1 + <<: *job-linux-xl + + try: + <<: *base-ci-job + name: try + env: + <<: [*shared-ci-variables, *prod-variables] + if: github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust' + strategy: + matrix: + include: + - name: dist-x86_64-linux + <<: *job-linux-xl + + - name: dist-x86_64-linux-alt + env: + IMAGE: dist-x86_64-linux + <<: *job-linux-xl + + auto: + <<: *base-ci-job + name: auto + env: + <<: [*shared-ci-variables, *prod-variables] + if: github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust' + strategy: + matrix: + include: + ############################# + # Linux/Docker builders # + ############################# + + - name: arm-android + <<: *job-linux-xl + + - name: armhf-gnu + <<: *job-linux-xl + + - name: dist-aarch64-linux + <<: *job-linux-xl + + - name: dist-android + <<: *job-linux-xl + + - name: dist-arm-linux + <<: *job-linux-xl + + - name: dist-armhf-linux + <<: *job-linux-xl + + - name: dist-armv7-linux + <<: *job-linux-xl + + - name: dist-i586-gnu-i586-i686-musl + <<: *job-linux-xl + + - name: dist-i686-freebsd + <<: *job-linux-xl + + - name: dist-i686-linux + <<: *job-linux-xl + + - name: dist-mips-linux + <<: *job-linux-xl + + - name: dist-mips64-linux + <<: *job-linux-xl + + - name: dist-mips64el-linux + <<: *job-linux-xl + + - name: dist-mipsel-linux + <<: *job-linux-xl + + - name: dist-powerpc-linux + <<: *job-linux-xl + + - name: dist-powerpc64-linux + <<: *job-linux-xl + + - name: dist-powerpc64le-linux + <<: *job-linux-xl + + - name: dist-s390x-linux + <<: *job-linux-xl + + - name: dist-various-1 + <<: *job-linux-xl + + - name: dist-various-2 + <<: *job-linux-xl + + - name: dist-x86_64-freebsd + <<: *job-linux-xl + + - name: dist-x86_64-linux + <<: *job-linux-xl + + - name: dist-x86_64-linux-alt + env: + IMAGE: dist-x86_64-linux + <<: *job-linux-xl + + - name: dist-x86_64-musl + <<: *job-linux-xl + + - name: dist-x86_64-netbsd + <<: *job-linux-xl + + - name: i686-gnu + <<: *job-linux-xl + + - name: i686-gnu-nopt + <<: *job-linux-xl + + - name: mingw-check + <<: *job-linux-xl + + - name: test-various + <<: *job-linux-xl + + - name: wasm32 + <<: *job-linux-xl + + - name: x86_64-gnu + <<: *job-linux-xl + + - name: x86_64-gnu-aux + <<: *job-linux-xl + + - name: x86_64-gnu-debug + <<: *job-linux-xl + + - name: x86_64-gnu-distcheck + <<: *job-linux-xl + + - name: x86_64-gnu-full-bootstrap + <<: *job-linux-xl + + - name: x86_64-gnu-llvm-8 + env: + RUST_BACKTRACE: 1 + <<: *job-linux-xl + + - name: x86_64-gnu-nopt + <<: *job-linux-xl + + - name: x86_64-gnu-tools + env: + DEPLOY_TOOLSTATES_JSON: toolstates-linux.json + <<: *job-linux-xl + + #################### + # macOS Builders # + #################### + + - name: dist-x86_64-apple + env: + SCRIPT: ./x.py dist + RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-macos-xl + + - name: dist-x86_64-apple-alt + env: + SCRIPT: ./x.py dist + RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + <<: *job-macos-xl + + - name: x86_64-apple + env: + SCRIPT: ./x.py test + RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.8 + MACOSX_STD_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + <<: *job-macos-xl + + ###################### + # Windows Builders # + ###################### + + - name: x86_64-msvc-1 + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler + SCRIPT: make ci-subset-1 + # FIXME(#59637) + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + <<: *job-windows-xl + + - name: x86_64-msvc-2 + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler + SCRIPT: make ci-subset-2 + <<: *job-windows-xl + + - name: i686-msvc-1 + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc + SCRIPT: make ci-subset-1 + # FIXME(#59637) + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + <<: *job-windows-xl + + - name: i686-msvc-2 + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc + SCRIPT: make ci-subset-2 + # FIXME(#59637) + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + <<: *job-windows-xl + + - name: x86_64-msvc-aux + env: + RUST_CHECK_TARGET: check-aux EXCLUDE_CARGO=1 + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc + <<: *job-windows-xl + + - name: x86_64-msvc-cargo + env: + SCRIPT: python x.py test src/tools/cargotest src/tools/cargo + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc + VCVARS_BAT: vcvars64.bat + # FIXME(#59637) + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + <<: *job-windows-xl + + - name: x86_64-msvc-tools + env: + SCRIPT: src/ci/docker/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstate/toolstates.json + <<: *job-windows-xl + + # 32/64-bit MinGW builds. + # + # We are using MinGW with posix threads since LLVM does not compile with + # the win32 threads version due to missing support for C++'s std::thread. + # + # Instead of relying on the MinGW version installed on appveryor we download + # and install one ourselves so we won't be surprised by changes to appveyor's + # build image. + # + # Finally, note that the downloads below are all in the `rust-lang-ci` S3 + # bucket, but they cleraly didn't originate there! The downloads originally + # came from the mingw-w64 SourceForge download site. Unfortunately + # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. + + - name: i686-mingw-1 + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu + SCRIPT: make ci-mingw-subset-1 + CUSTOM_MINGW: 1 + # FIXME(#59637) + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + <<: *job-windows-xl + + - name: i686-mingw-2 + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu + SCRIPT: make ci-mingw-subset-2 + CUSTOM_MINGW: 1 + <<: *job-windows-xl + + - name: x86_64-mingw-1 + env: + SCRIPT: make ci-mingw-subset-1 + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu + CUSTOM_MINGW: 1 + # FIXME(#59637) + NO_DEBUG_ASSERTIONS: 1 + NO_LLVM_ASSERTIONS: 1 + <<: *job-windows-xl + + - name: x86_64-mingw-2 + env: + SCRIPT: make ci-mingw-subset-2 + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu + CUSTOM_MINGW: 1 + <<: *job-windows-xl + + - name: dist-x86_64-msvc + env: + RUST_CONFIGURE_ARGS: >- + --build=x86_64-pc-windows-msvc + --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc + --enable-full-tools + --enable-profiler + SCRIPT: python x.py dist + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-xl + + - name: dist-i686-msvc + env: + RUST_CONFIGURE_ARGS: >- + --build=i686-pc-windows-msvc + --target=i586-pc-windows-msvc + --enable-full-tools + --enable-profiler + SCRIPT: python x.py dist + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-xl + + - name: dist-i686-mingw + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-full-tools --enable-profiler + SCRIPT: python x.py dist + CUSTOM_MINGW: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-xl + + - name: dist-x86_64-mingw + env: + SCRIPT: python x.py dist + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler + CUSTOM_MINGW: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-xl + + - name: dist-x86_64-msvc-alt + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler + SCRIPT: python x.py dist + <<: *job-windows-xl + + master: + name: master + runs-on: ubuntu-latest + env: + <<: [*prod-variables] + if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust' + steps: + - name: checkout the source code + uses: actions/checkout@v1 + with: + fetch-depth: 2 + + - name: publish toolstate + run: src/ci/publish_toolstate.sh + env: + TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} + <<: *step + + # These jobs don't actually test anything, but they're used to tell bors the + # build completed, as there is no practical way to detect when a workflow is + # successful listening to webhooks only. + try-success: + needs: [try] + if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + <<: *base-success-job + try-failure: + needs: [try] + if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + <<: *base-failure-job + auto-success: + needs: [auto] + if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" + <<: *base-success-job + auto-failure: + needs: [auto] + if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" + <<: *base-failure-job diff --git a/src/ci/run.sh b/src/ci/run.sh index 73c3a964f5396..59f2736cbd406 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -23,6 +23,14 @@ fi ci_dir=`cd $(dirname $0) && pwd` source "$ci_dir/shared.sh" +if command -v python > /dev/null; then + PYTHON="python" +elif command -v python3 > /dev/null; then + PYTHON="python3" +else + PYTHON="python2" +fi + if ! isCI || isCiBranch auto || isCiBranch beta; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests" fi @@ -107,7 +115,7 @@ SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then $SRC/configure --enable-parallel-compiler - CARGO_INCREMENTAL=0 python2.7 ../x.py check + CARGO_INCREMENTAL=0 $PYTHON ../x.py check rm -f config.toml rm -rf build fi diff --git a/src/ci/scripts/install-awscli.sh b/src/ci/scripts/install-awscli.sh index e21187938504c..f9b759fe343f0 100755 --- a/src/ci/scripts/install-awscli.sh +++ b/src/ci/scripts/install-awscli.sh @@ -28,7 +28,7 @@ if isLinux; then pipflags="--user" sudo apt-get install -y python3-setuptools - echo "##vso[task.prependpath]$HOME/.local/bin" + ciCommandAddPath "${HOME}/.local/bin" fi mkdir -p "${DEPS_DIR}" diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh index 98373df7fce50..ae85d5cab0122 100755 --- a/src/ci/scripts/install-mingw.sh +++ b/src/ci/scripts/install-mingw.sh @@ -51,7 +51,8 @@ if isWindows; then if [[ "${CUSTOM_MINGW-0}" -ne 1 ]]; then pacman -S --noconfirm --needed mingw-w64-$arch-toolchain mingw-w64-$arch-cmake \ - mingw-w64-$arch-gcc mingw-w64-$arch-python2 + mingw-w64-$arch-gcc \ + mingw-w64-$arch-python # the python package is actually for python3 ciCommandAddPath "$(ciCheckoutPath)/msys2/mingw${bits}/bin" else mingw_dir="mingw${bits}" diff --git a/src/ci/scripts/install-msys2-packages.sh b/src/ci/scripts/install-msys2-packages.sh index 22b9854ad5eb4..8fefddd959c9c 100755 --- a/src/ci/scripts/install-msys2-packages.sh +++ b/src/ci/scripts/install-msys2-packages.sh @@ -6,14 +6,33 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" if isWindows; then + # FIXME(mati865): temporary workaround until chocolatey updates their MSYS2 + base_url='https://ci-mirrors.rust-lang.org/rustc/msys2-repo/msys/x86_64' + curl ${base_url}/libzstd-1.4.4-2-x86_64.pkg.tar.xz -o libzstd-1.4.4-2-x86_64.pkg.tar.xz + curl ${base_url}/pacman-5.2.1-6-x86_64.pkg.tar.xz -o pacman-5.2.1-6-x86_64.pkg.tar.xz + curl ${base_url}/zstd-1.4.4-2-x86_64.pkg.tar.xz -o zstd-1.4.4-2-x86_64.pkg.tar.xz + pacman -U --noconfirm libzstd-1.4.4-2-x86_64.pkg.tar.xz pacman-5.2.1-6-x86_64.pkg.tar.xz \ + zstd-1.4.4-2-x86_64.pkg.tar.xz + rm libzstd-1.4.4-2-x86_64.pkg.tar.xz pacman-5.2.1-6-x86_64.pkg.tar.xz \ + zstd-1.4.4-2-x86_64.pkg.tar.xz + pacman -Sy + pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar \ binutils + # Detect the native Python version installed on the agent. On GitHub + # Actions, the C:\hostedtoolcache\windows\Python directory contains a + # subdirectory for each installed Python version. + # + # The -V flag of the sort command sorts the input by version number. + native_python_version="$(ls /c/hostedtoolcache/windows/Python | sort -Vr | head -n 1)" + # Make sure we use the native python interpreter instead of some msys equivalent # one way or another. The msys interpreters seem to have weird path conversions # baked in which break LLVM's build system one way or another, so let's use the # native version which keeps everything as native as possible. - python_home="C:/hostedtoolcache/windows/Python/2.7.17/x64" - cp "${python_home}/python.exe" "${python_home}/python2.7.exe" - ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\2.7.17\\x64" + python_home="/c/hostedtoolcache/windows/Python/${native_python_version}/x64" + cp "${python_home}/python.exe" "${python_home}/python3.exe" + ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64" + ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64\\Scripts" fi diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh index 9e899ba9d8947..b94eb5fc6ddd5 100755 --- a/src/ci/scripts/install-msys2.sh +++ b/src/ci/scripts/install-msys2.sh @@ -17,9 +17,13 @@ if isWindows; then msys2.nupkg curl -sSL https://packages.chocolatey.org/chocolatey-core.extension.1.3.5.1.nupkg > \ chocolatey-core.extension.nupkg + # FIXME(mati865): remove `/NoUpdate` once chocolatey updates MSYS2 choco install -s . msys2 \ - --params="/InstallDir:$(ciCheckoutPath)/msys2 /NoPath" -y --no-progress + --params="/InstallDir:$(ciCheckoutPath)/msys2 /NoPath /NoUpdate" -y --no-progress rm msys2.nupkg chocolatey-core.extension.nupkg mkdir -p "$(ciCheckoutPath)/msys2/home/${USERNAME}" ciCommandAddPath "$(ciCheckoutPath)/msys2/usr/bin" + + echo "switching shell to use our own bash" + ciCommandSetEnv CI_OVERRIDE_SHELL "$(ciCheckoutPath)/msys2/usr/bin/bash.exe" fi diff --git a/src/ci/scripts/setup-environment.sh b/src/ci/scripts/setup-environment.sh index d134fcd47baf1..411ef6f9b2822 100755 --- a/src/ci/scripts/setup-environment.sh +++ b/src/ci/scripts/setup-environment.sh @@ -11,16 +11,34 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" # Since matrix variables are readonly in Azure Pipelines, we take # INITIAL_RUST_CONFIGURE_ARGS and establish RUST_CONFIGURE_ARGS # which downstream steps can alter -# macOS ships with Bash 3.16, so we cannot use [[ -v FOO ]], -# which was introduced in Bash 4.2 -if [[ -z "${INITIAL_RUST_CONFIGURE_ARGS+x}" ]]; then - INITIAL_RUST_CONFIG="" - echo "No initial Rust configure args set" -else - INITIAL_RUST_CONFIG="${INITIAL_RUST_CONFIGURE_ARGS}" - ciCommandSetEnv RUST_CONFIGURE_ARGS "${INITIAL_RUST_CONFIG}" +if isAzurePipelines; then + # macOS ships with Bash 3.16, so we cannot use [[ -v FOO ]], + # which was introduced in Bash 4.2 + if [[ -z "${INITIAL_RUST_CONFIGURE_ARGS+x}" ]]; then + INITIAL_RUST_CONFIG="" + echo "No initial Rust configure args set" + else + INITIAL_RUST_CONFIG="${INITIAL_RUST_CONFIGURE_ARGS}" + ciCommandSetEnv RUST_CONFIGURE_ARGS "${INITIAL_RUST_CONFIG}" + fi fi +# Load extra environment variables +vars="${EXTRA_VARIABLES-}" +echo "${vars}" | jq '' >/dev/null # Validate JSON and exit on errors +for key in $(echo "${vars}" | jq "keys[]" -r); do + # On Windows, for whatever reason, $key contains the BOM character in it, + # and that messes up `jq ".${key}"`. This line strips the BOM from the key. + # + # https://unix.stackexchange.com/a/381263 + key="$(echo "${key}" | sed '1s/^\xEF\xBB\xBF//')" + + echo "adding extra environment variable ${key}" + value="$(echo "${vars}" | jq ".${key}" -r)" + export "${key}"="${value}" + ciCommandSetEnv "${key}" "${value}" +done + # Builders starting with `dist-` are dist builders, but if they also end with # `-alt` they are alternate dist builders. if [[ "${CI_JOB_NAME}" = dist-* ]]; then diff --git a/src/ci/scripts/symlink-build-dir.sh b/src/ci/scripts/symlink-build-dir.sh new file mode 100755 index 0000000000000..50178b9c33ed4 --- /dev/null +++ b/src/ci/scripts/symlink-build-dir.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# We've had multiple issues with the default disk running out of disk space +# during builds, and it looks like other disks mounted in the VMs have more +# space available. This script synlinks the build directory to those other +# disks, in the CI providers and OSes affected by this. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows && isAzurePipelines; then + cmd //c "mkdir c:\\MORE_SPACE" + cmd //c "mklink /J build c:\\MORE_SPACE" +elif isLinux && isGitHubActions; then + sudo mkdir -p /mnt/more-space + sudo chown -R "$(whoami):" /mnt/more-space + + # Switch the whole workspace to the /mnt partition, which has more space. + # We don't just symlink the `obj` directory as doing that creates problems + # with the docker container. + current_dir="$(readlink -f "$(pwd)")" + cd /tmp + mv "${current_dir}" /mnt/more-space/workspace + ln -s /mnt/more-space/workspace "${current_dir}" + cd "${current_dir}" + + # Move the Docker data directory to /mnt + sudo systemctl stop docker.service + sudo mv /var/lib/docker /mnt/docker + sudo ln -s /mnt/docker /var/lib/docker + sudo systemctl start docker.service +fi diff --git a/src/ci/scripts/windows-symlink-build-dir.sh b/src/ci/scripts/windows-symlink-build-dir.sh deleted file mode 100755 index e57128c70f5f1..0000000000000 --- a/src/ci/scripts/windows-symlink-build-dir.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# We've had issues with the default drive in use running out of space during a -# build, and it looks like the `C:` drive has more space than the default `D:` -# drive. We should probably confirm this with the azure pipelines team at some -# point, but this seems to fix our "disk space full" problems. - -set -euo pipefail -IFS=$'\n\t' - -source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" - -if isWindows; then - cmd //c "mkdir c:\\MORE_SPACE" - cmd //c "mklink /J build c:\\MORE_SPACE" -fi diff --git a/src/doc/book b/src/doc/book index 6fb3705e52303..6247be15a7f75 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 6fb3705e5230311b096d47f7e2c91f9ce24393d0 +Subproject commit 6247be15a7f7509559f7981ee2209b9e0cc121df diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 37f9e68484111..49270740c7a4b 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 37f9e6848411188a1062ead1bd8ebe4b8aa16899 +Subproject commit 49270740c7a4bff2763e6bc730b191d45b7d5167 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index d22a9c487c780..366c50a03bed9 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit d22a9c487c78095afc4584f1d9b4ec43529d713c +Subproject commit 366c50a03bed928589771eba8a6f18e0c0c01d23 diff --git a/src/doc/nomicon b/src/doc/nomicon index 9f797e65e6bcc..d1517d4e3f292 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 9f797e65e6bcc79419975b17aff8e21c9adc039f +Subproject commit d1517d4e3f29264c5c67bce2658516bb5202c800 diff --git a/src/doc/reference b/src/doc/reference index e2f11fe4d6a5e..892b928b565e3 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit e2f11fe4d6a5ecb471c70323197da43c70cb96b6 +Subproject commit 892b928b565e35d25b6f9c47faee03b94bc41489 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index cb369ae95ca36..ab072b14393cb 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit cb369ae95ca36b841960182d26f6d5d9b2e3cc18 +Subproject commit ab072b14393cbd9e8a1d1d75879bf51e27217bbb diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide new file mode 160000 index 0000000000000..7adfab42bab04 --- /dev/null +++ b/src/doc/rustc-dev-guide @@ -0,0 +1 @@ +Subproject commit 7adfab42bab045a848126895c2f1e09927c1331a diff --git a/src/doc/rustc-guide b/src/doc/rustc-guide deleted file mode 160000 index 5bd60bc51efae..0000000000000 --- a/src/doc/rustc-guide +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5bd60bc51efaec04e69e2e18b59678e2af066433 diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 0dc81378e05b2..0b4bb05c1db23 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -7,12 +7,164 @@ a version of this list for your exact compiler by running `rustc -C help`. This option is deprecated and does nothing. -## linker +## code-model + +This option lets you choose which code model to use. \ +Code models put constraints on address ranges that the program and its symbols may use. \ +With smaller address ranges machine instructions +may be able to use use more compact addressing modes. + +The specific ranges depend on target architectures and addressing modes available to them. \ +For x86 more detailed description of its code models can be found in +[System V Application Binary Interface](https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf) +specification. + +Supported values for this option are: + +- `tiny` - Tiny code model. +- `small` - Small code model. This is the default model for majority of supported targets. +- `kernel` - Kernel code model. +- `medium` - Medium code model. +- `large` - Large code model. + +Supported values can also be discovered by running `rustc --print code-models`. + +## codegen-units + +This flag controls how many code generation units the crate is split into. It +takes an integer greater than 0. + +When a crate is split into multiple codegen units, LLVM is able to process +them in parallel. Increasing parallelism may speed up compile times, but may +also produce slower code. Setting this to 1 may improve the performance of +generated code, but may be slower to compile. + +The default value, if not specified, is 16 for non-incremental builds. For +incremental builds the default is 256 which allows caching to be more granular. + +## debug-assertions + +This flag lets you turn `cfg(debug_assertions)` [conditional +compilation](../../reference/conditional-compilation.md#debug_assertions) on +or off. It takes one of the following values: + +* `y`, `yes`, `on`, or no value: enable debug-assertions. +* `n`, `no`, or `off`: disable debug-assertions. + +If not specified, debug assertions are automatically enabled only if the +[opt-level](#opt-level) is 0. + +## debuginfo + +This flag controls the generation of debug information. It takes one of the +following values: + +* `0`: no debug info at all (the default). +* `1`: line tables only. +* `2`: full debug info. + +Note: The [`-g` flag][option-g-debug] is an alias for `-C debuginfo=2`. + +## default-linker-libraries + +This flag controls whether or not the linker includes its default libraries. +It takes one of the following values: + +* `y`, `yes`, `on`, or no value: include default libraries (the default). +* `n`, `no`, or `off`: exclude default libraries. -This flag lets you control which linker `rustc` invokes to link your code. It -takes a path to the linker executable. If this flag is not specified, the -linker will be inferred based on the target. See also the -[linker-flavor](#linker-flavor) flag for another way to specify the linker. +For example, for gcc flavor linkers, this issues the `-nodefaultlibs` flag to +the linker. + +## embed-bitcode + +This flag controls whether or not the compiler embeds LLVM bitcode into object +files. It takes one of the following values: + +* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default). +* `n`, `no`, or `off`: omit bitcode from rlibs. + +LLVM bitcode is required when rustc is performing link-time optimization (LTO). +It is also required on some targets like iOS ones where vendors look for LLVM +bitcode. Embedded bitcode will appear in rustc-generated object files inside of +a section whose name is defined by the target platform. Most of the time this is +`.llvmbc`. + +The use of `-C embed-bitcode=no` can significantly improve compile times and +reduce generated file sizes if your compilation does not actually need bitcode +(e.g. if you're not compiling for iOS or you're not performing LTO). For these +reasons, Cargo uses `-C embed-bitcode=no` whenever possible. Likewise, if you +are building directly with `rustc` we recommend using `-C embed-bitcode=no` +whenever you are not using LTO. + +If combined with `-C lto`, `-C embed-bitcode=no` will cause `rustc` to abort +at start-up, because the combination is invalid. + +> **Note**: if you're building Rust code with LTO then you probably don't even +> need the `embed-bitcode` option turned on. You'll likely want to use +> `-Clinker-plugin-lto` instead which skips generating object files entirely and +> simply replaces object files with LLVM bitcode. The only purpose for +> `-Cembed-bitcode` is when you're generating an rlib that is both being used +> with and without LTO. For example Rust's standard library ships with embedded +> bitcode since users link to it both with and without LTO. +> +> This also may make you wonder why the default is `yes` for this option. The +> reason for that is that it's how it was for rustc 1.44 and prior. In 1.45 this +> option was added to turn off what had always been the default. + +## extra-filename + +This option allows you to put extra data in each output filename. It takes a +string to add as a suffix to the filename. See the [`--emit` +flag][option-emit] for more information. + +## force-frame-pointers + +This flag forces the use of frame pointers. It takes one of the following +values: + +* `y`, `yes`, `on`, or no value: force-enable frame pointers. +* `n`, `no`, or `off`: do not force-enable frame pointers. This does + not necessarily mean frame pointers will be removed. + +The default behaviour, if frame pointers are not force-enabled, depends on the +target. + +## force-unwind-tables + +This flag forces the generation of unwind tables. It takes one of the following +values: + +* `y`, `yes`, `on`, or no value: Unwind tables are forced to be generated. +* `n`, `no`, or `off`: Unwind tables are not forced to be generated. If unwind + tables are required by the target or `-C panic=unwind`, an error will be + emitted. + +The default if not specified depends on the target. + +## incremental + +This flag allows you to enable incremental compilation, which allows `rustc` +to save information after compiling a crate to be reused when recompiling the +crate, improving re-compile times. This takes a path to a directory where +incremental files will be stored. + +## inline-threshold + +This option lets you set the default threshold for inlining a function. It +takes an unsigned integer as a value. Inlining is based on a cost model, where +a higher threshold will allow more inlining. + +The default depends on the [opt-level](#opt-level): + +| opt-level | Threshold | +|-----------|-----------| +| 0 | N/A, only inlines always-inline functions | +| 1 | N/A, only inlines always-inline functions and LLVM lifetime intrinsics | +| 2 | 225 | +| 3 | 275 | +| s | 75 | +| z | 25 | ## link-arg @@ -25,51 +177,91 @@ This flag lets you append a single extra argument to the linker invocation. This flag lets you append multiple extra arguments to the linker invocation. The options should be separated by spaces. -## linker-flavor +## link-dead-code + +This flag controls whether the linker will keep dead code. It takes one of +the following values: + +* `y`, `yes`, `on`, or no value: keep dead code. +* `n`, `no`, or `off`: remove dead code (the default). + +An example of when this flag might be useful is when trying to construct code coverage +metrics. -This flag lets you control the linker flavor used by `rustc`. If a linker is given with the -[`-C linker` flag](#linker), then the linker flavor is inferred from the value provided. If no -linker is given then the linker flavor is used to determine the linker to use. Every `rustc` target -defaults to some linker flavor. Valid options are: +## linker + +This flag controls which linker `rustc` invokes to link your code. It takes a +path to the linker executable. If this flag is not specified, the linker will +be inferred based on the target. See also the [linker-flavor](#linker-flavor) +flag for another way to specify the linker. -* `em`: Uses [Emscripten `emcc`](https://emscripten.org/docs/tools_reference/emcc.html). -* `gcc`: Uses the `cc` executable, which is typically gcc or clang on many systems. -* `ld`: Uses the `ld` executable. -* `msvc`: Uses the `link.exe` executable from Microsoft Visual Studio MSVC. -* `ptx-linker`: Uses +## linker-flavor + +This flag controls the linker flavor used by `rustc`. If a linker is given with +the [`-C linker` flag](#linker), then the linker flavor is inferred from the +value provided. If no linker is given then the linker flavor is used to +determine the linker to use. Every `rustc` target defaults to some linker +flavor. Valid options are: + +* `em`: use [Emscripten `emcc`](https://emscripten.org/docs/tools_reference/emcc.html). +* `gcc`: use the `cc` executable, which is typically gcc or clang on many systems. +* `ld`: use the `ld` executable. +* `msvc`: use the `link.exe` executable from Microsoft Visual Studio MSVC. +* `ptx-linker`: use [`rust-ptx-linker`](https://github.com/denzp/rust-ptx-linker) for Nvidia NVPTX GPGPU support. -* `wasm-ld`: Uses the [`wasm-ld`](https://lld.llvm.org/WebAssembly.html) +* `wasm-ld`: use the [`wasm-ld`](https://lld.llvm.org/WebAssembly.html) executable, a port of LLVM `lld` for WebAssembly. -* `ld64.lld`: Uses the LLVM `lld` executable with the [`-flavor darwin` +* `ld64.lld`: use the LLVM `lld` executable with the [`-flavor darwin` flag][lld-flavor] for Apple's `ld`. -* `ld.lld`: Uses the LLVM `lld` executable with the [`-flavor gnu` +* `ld.lld`: use the LLVM `lld` executable with the [`-flavor gnu` flag][lld-flavor] for GNU binutils' `ld`. -* `lld-link`: Uses the LLVM `lld` executable with the [`-flavor link` +* `lld-link`: use the LLVM `lld` executable with the [`-flavor link` flag][lld-flavor] for Microsoft's `link.exe`. [lld-flavor]: https://lld.llvm.org/Driver.html -## link-dead-code +## linker-plugin-lto -Normally, the linker will remove dead code. This flag disables this behavior. +This flag defers LTO optimizations to the linker. See +[linker-plugin-LTO](../linker-plugin-lto.md) for more details. It takes one of +the following values: -An example of when this flag might be useful is when trying to construct code coverage -metrics. +* `y`, `yes`, `on`, or no value: enable linker plugin LTO. +* `n`, `no`, or `off`: disable linker plugin LTO (the default). +* A path to the linker plugin. + +More specifically this flag will cause the compiler to replace its typical +object file output with LLVM bitcode files. For example an rlib produced with +`-Clinker-plugin-lto` will still have `*.o` files in it, but they'll all be LLVM +bitcode instead of actual machine code. It is expected that the native platform +linker is capable of loading these LLVM bitcode files and generating code at +link time (typically after performing optimizations). + +Note that rustc can also read its own object files produced with +`-Clinker-plugin-lto`. If an rlib is only ever going to get used later with a +`-Clto` compilation then you can pass `-Clinker-plugin-lto` to speed up +compilation and avoid generating object files that aren't used. + +## llvm-args + +This flag can be used to pass a list of arguments directly to LLVM. + +The list must be separated by spaces. + +Pass `--help` to see a list of options. ## lto -This flag instructs LLVM to use [link time +This flag controls whether LLVM uses [link time optimizations](https://llvm.org/docs/LinkTimeOptimization.html) to produce better optimized code, using whole-program analysis, at the cost of longer -linking time. +linking time. It takes one of the following values: -This flag may take one of the following values: - -* `y`, `yes`, `on`, `fat`, or no value: Performs "fat" LTO which attempts to +* `y`, `yes`, `on`, `fat`, or no value: perform "fat" LTO which attempts to perform optimizations across all crates within the dependency graph. -* `n`, `no`, `off`: Disables LTO. -* `thin`: Performs ["thin" +* `n`, `no`, `off`: disables LTO. +* `thin`: perform ["thin" LTO](http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html). This is similar to "fat", but takes substantially less time to run while still achieving performance gains similar to "fat". @@ -81,81 +273,67 @@ disabled if codegen units is 1 or optimizations are disabled ([`-C opt-level=0`](#opt-level)). That is: * When `-C lto` is not specified: - * `codegen-units=1`: Disables LTO. - * `opt-level=0`: Disables LTO. + * `codegen-units=1`: disable LTO. + * `opt-level=0`: disable LTO. * When `-C lto=true`: - * `lto=true`: 16 codegen units, performs fat LTO across crates. + * `lto=true`: 16 codegen units, perform fat LTO across crates. * `codegen-units=1` + `lto=true`: 1 codegen unit, fat LTO across crates. See also [linker-plugin-lto](#linker-plugin-lto) for cross-language LTO. -## linker-plugin-lto - -Defers LTO optimizations to the linker. See -[linkger-plugin-LTO](../linker-plugin-lto.md) for more details. Takes one of -the following values: - -* `y`, `yes`, `on`, or no value: Enabled. -* `n`, `no`, or `off`: Disabled (default). -* A path to the linker plugin. - -## target-cpu - -This instructs `rustc` to generate code specifically for a particular processor. - -You can run `rustc --print target-cpus` to see the valid options to pass -here. Additionally, `native` can be passed to use the processor of the host -machine. Each target has a default base CPU. - -## target-feature +## metadata -Individual targets will support different features; this flag lets you control -enabling or disabling a feature. Each feature should be prefixed with a `+` to -enable it or `-` to disable it. Separate multiple features with commas. +This option allows you to control the metadata used for symbol mangling. This +takes a space-separated list of strings. Mangled symbols will incorporate a +hash of the metadata. This may be used, for example, to differentiate symbols +between two different versions of the same crate being linked. -To see the valid options and an example of use, run `rustc --print -target-features`. +## no-prepopulate-passes -Using this flag is unsafe and might result in [undefined runtime -behavior](../targets/known-issues.md). +This flag tells the pass manager to use an empty list of passes, instead of the +usual pre-populated list of passes. -See also the [`target_feature` -attribute](../../reference/attributes/codegen.md#the-target_feature-attribute) -for controlling features per-function. +## no-redzone -This also supports the feature `+crt-static` and `-crt-static` to control -[static C runtime linkage](../../reference/linkage.html#static-and-dynamic-c-runtimes). +This flag allows you to disable [the +red zone](https://en.wikipedia.org/wiki/Red_zone_\(computing\)). It takes one +of the following values: -Each target and [`target-cpu`](#target-cpu) has a default set of enabled -features. +* `y`, `yes`, `on`, or no value: disable the red zone. +* `n`, `no`, or `off`: enable the red zone. -## passes +The default behaviour, if the flag is not specified, depends on the target. -This flag can be used to add extra [LLVM -passes](http://llvm.org/docs/Passes.html) to the compilation. +## no-stack-check -The list must be separated by spaces. +This option is deprecated and does nothing. -See also the [`no-prepopulate-passes`](#no-prepopulate-passes) flag. +## no-vectorize-loops -## llvm-args +This flag disables [loop +vectorization](https://llvm.org/docs/Vectorizers.html#the-loop-vectorizer). -This flag can be used to pass a list of arguments directly to LLVM. +## no-vectorize-slp -The list must be separated by spaces. +This flag disables vectorization using +[superword-level +parallelism](https://llvm.org/docs/Vectorizers.html#the-slp-vectorizer). -Pass `--help` to see a list of options. +## opt-level -## save-temps +This flag controls the optimization level. -`rustc` will generate temporary files during compilation; normally it will -delete them after it's done with its work. This option will cause them to be -preserved instead of removed. +* `0`: no optimizations, also turns on + [`cfg(debug_assertions)`](#debug-assertions) (the default). +* `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. -## rpath +Note: The [`-O` flag][option-o-optimize] is an alias for `-C opt-level=2`. -This option allows you to enable -[`rpath`](https://en.wikipedia.org/wiki/Rpath). +The default is `0`. ## overflow-checks @@ -164,35 +342,29 @@ overflow](../../reference/expressions/operator-expr.md#overflow). When overflow-checks are enabled, a panic will occur on overflow. This flag takes one of the following values: -* `y`, `yes`, `on`, or no value: Enable overflow checks. -* `n`, `no`, or `off`: Disable overflow checks. +* `y`, `yes`, `on`, or no value: enable overflow checks. +* `n`, `no`, or `off`: disable overflow checks. If not specified, overflow checks are enabled if [debug-assertions](#debug-assertions) are enabled, disabled otherwise. -## no-prepopulate-passes +## panic -The pass manager comes pre-populated with a list of passes; this flag -ensures that list is empty. +This option lets you control what happens when the code panics. -## no-vectorize-loops +* `abort`: terminate the process upon panic +* `unwind`: unwind the stack upon panic -By default, `rustc` will attempt to [vectorize -loops](https://llvm.org/docs/Vectorizers.html#the-loop-vectorizer). This -flag will turn that behavior off. +If not specified, the default depends on the target. -## no-vectorize-slp +## passes -By default, `rustc` will attempt to vectorize code using [superword-level -parallelism](https://llvm.org/docs/Vectorizers.html#the-slp-vectorizer). This -flag will turn that behavior off. +This flag can be used to add extra [LLVM +passes](http://llvm.org/docs/Passes.html) to the compilation. -## soft-float +The list must be separated by spaces. -This option will make `rustc` generate code using "soft floats." By default, -a lot of hardware supports floating point instructions, and so the code generated -will take advantage of this. "soft floats" emulate floating point instructions -in software. +See also the [`no-prepopulate-passes`](#no-prepopulate-passes) flag. ## prefer-dynamic @@ -201,65 +373,68 @@ indicate that dynamic linking should be used if possible if both a static and dynamic versions of a library are available. There is an internal algorithm for determining whether or not it is possible to statically or dynamically link with a dependency. For example, `cdylib` crate types may only use static -linkage. - -## no-integrated-as +linkage. This flag takes one of the following values: -`rustc` normally uses the LLVM internal assembler to create object code. This -flag will disable the internal assembler and emit assembly code to be -translated using an external assembler, currently the linker such as `cc`. +* `y`, `yes`, `on`, or no value: use dynamic linking. +* `n`, `no`, or `off`: use static linking (the default). -## no-redzone +## profile-generate -This flag allows you to disable [the -red zone](https://en.wikipedia.org/wiki/Red_zone_\(computing\)). This flag can -be passed one of the following options: +This flag allows for creating instrumented binaries that will collect +profiling data for use with profile-guided optimization (PGO). The flag takes +an optional argument which is the path to a directory into which the +instrumented binary will emit the collected data. See the chapter on +[profile-guided optimization] for more information. -* `y`, `yes`, `on`, or no value: Disables the red zone. -* `n`, `no`, or `off`: Enables the red zone. +## profile-use -The default if not specified depends on the target. +This flag specifies the profiling data file to be used for profile-guided +optimization (PGO). The flag takes a mandatory argument which is the path +to a valid `.profdata` file. See the chapter on +[profile-guided optimization] for more information. ## relocation-model -This option lets you choose which -[relocation](https://en.wikipedia.org/wiki/Relocation_\(computing\)) model to -use. - -To find the valid options for this flag, run `rustc --print relocation-models`. +This option controls generation of +[position-independent code (PIC)](https://en.wikipedia.org/wiki/Position-independent_code). -## code-model +Supported values for this option are: -This option lets you choose which code model to use. +#### Primary relocation models -To find the valid options for this flag, run `rustc --print code-models`. - -## metadata +- `static` - non-relocatable code, machine instructions may use absolute addressing modes. -This option allows you to control the metadata used for symbol mangling. This -takes a space-separated list of strings. Mangled symbols will incorporate a -hash of the metadata. This may be used, for example, to differentiate symbols -between two different versions of the same crate being linked. +- `pic` - fully relocatable position independent code, +machine instructions need to use relative addressing modes. \ +Equivalent to the "uppercase" `-fPIC` or `-fPIE` options in other compilers, +depending on the produced crate types. \ +This is the default model for majority of supported targets. -## extra-filename +#### Special relocation models -This option allows you to put extra data in each output filename. It takes a -string to add as a suffix to the filename. See the [`--emit` -flag][option-emit] for more information. +- `dynamic-no-pic` - relocatable external references, non-relocatable code. \ +Only makes sense on Darwin and is rarely used. \ +If StackOverflow tells you to use this as an opt-out of PIC or PIE, don't believe it, +use `-C relocation-model=static` instead. +- `ropi`, `rwpi` and `ropi-rwpi` - relocatable code and read-only data, relocatable read-write data, +and combination of both, respectively. \ +Only makes sense for certain embedded ARM targets. +- `default` - relocation model default to the current target. \ +Only makes sense as an override for some other explicitly specified relocation model +previously set on the command line. -## codegen-units +Supported values can also be discovered by running `rustc --print relocation-models`. -This flag controls how many code generation units the crate is split into. It -takes an integer greater than 0. +#### Linking effects -When a crate is split into multiple codegen units, LLVM is able to process -them in parallel. Increasing parallelism may speed up compile times, but may -also produce slower code. Setting this to 1 may improve the performance of -generated code, but may be slower to compile. +In addition to codegen effects, `relocation-model` has effects during linking. -The default, if not specified, is 16. This flag is ignored if -[incremental](#incremental) is enabled, in which case an internal heuristic is -used to split the crate. +If the relocation model is `pic` and the current target supports position-independent executables +(PIE), the linker will be instructed (`-pie`) to produce one. \ +If the target doesn't support both position-independent and statically linked executables, +then `-C target-feature=+crt-static` "wins" over `-C relocation-model=pic`, +and the linker is instructed (`-static`) to produce a statically linked +but not position-independent executable. ## remark @@ -269,118 +444,67 @@ The list of passes should be separated by spaces. `all` will remark on every pass. -## no-stack-check - -This option is deprecated and does nothing. - -## debuginfo - -This flag lets you control debug information: - -* `0`: no debug info at all (default) -* `1`: line tables only -* `2`: full debug info - -Note: The [`-g` flag][option-g-debug] is an alias for `-C debuginfo=2`. - -## opt-level - -This flag lets you control the optimization level. - -* `0`: no optimizations, also turns on [`cfg(debug_assertions)`](#debug-assertions). -* `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. - -Note: The [`-O` flag][option-o-optimize] is an alias for `-C opt-level=2`. - -The default is `0`. - -## debug-assertions - -This flag lets you turn `cfg(debug_assertions)` [conditional -compilation](../../reference/conditional-compilation.md#debug_assertions) on -or off. It takes one of the following values: - -* `y`, `yes`, `on`, or no value: Enable debug-assertions. -* `n`, `no`, or `off`: Disable debug-assertions. - -If not specified, debug assertions are automatically enabled only if the -[opt-level](#opt-level) is 0. - -## inline-threshold - -This option lets you set the default threshold for inlining a function. It -takes an unsigned integer as a value. Inlining is based on a cost model, where -a higher threshold will allow more inlining. - -The default depends on the [opt-level](#opt-level): +## rpath -| opt-level | Threshold | -|-----------|-----------| -| 0 | N/A, only inlines always-inline functions | -| 1 | N/A, only inlines always-inline functions and LLVM lifetime intrinsics | -| 2 | 225 | -| 3 | 275 | -| s | 75 | -| z | 25 | +This flag controls whether [`rpath`](https://en.wikipedia.org/wiki/Rpath) is +enabled. It takes one of the following values: -## panic +* `y`, `yes`, `on`, or no value: enable rpath. +* `n`, `no`, or `off`: disable rpath (the default). -This option lets you control what happens when the code panics. +## save-temps -* `abort`: terminate the process upon panic -* `unwind`: unwind the stack upon panic +This flag controls whether temporary files generated during compilation are +deleted once compilation finishes. It takes one of the following values: -If not specified, the default depends on the target. +* `y`, `yes`, `on`, or no value: save temporary files. +* `n`, `no`, or `off`: delete temporary files (the default). -## incremental +## soft-float -This flag allows you to enable incremental compilation, which allows `rustc` -to save information after compiling a crate to be reused when recompiling the -crate, improving re-compile times. This takes a path to a directory where -incremental files will be stored. +This option controls whether `rustc` generates code that emulates floating +point instructions in software. It takes one of the following values: -## profile-generate +* `y`, `yes`, `on`, or no value: use soft floats. +* `n`, `no`, or `off`: use hardware floats (the default). -This flag allows for creating instrumented binaries that will collect -profiling data for use with profile-guided optimization (PGO). The flag takes -an optional argument which is the path to a directory into which the -instrumented binary will emit the collected data. See the chapter on -[profile-guided optimization] for more information. - -## profile-use +## target-cpu -This flag specifies the profiling data file to be used for profile-guided -optimization (PGO). The flag takes a mandatory argument which is the path -to a valid `.profdata` file. See the chapter on -[profile-guided optimization] for more information. +This instructs `rustc` to generate code specifically for a particular processor. -## force-frame-pointers +You can run `rustc --print target-cpus` to see the valid options to pass +here. Additionally, `native` can be passed to use the processor of the host +machine. Each target has a default base CPU. -This flag forces the use of frame pointers. It takes one of the following -values: +## target-feature -* `y`, `yes`, `on`, or no value: Frame pointers are forced to be enabled. -* `n`, `no`, or `off`: Frame pointers are not forced to be enabled. This does - not necessarily mean frame pointers will be removed. +Individual targets will support different features; this flag lets you control +enabling or disabling a feature. Each feature should be prefixed with a `+` to +enable it or `-` to disable it. -The default if not specified depends on the target. +Features from multiple `-C target-feature` options are combined. \ +Multiple features can be specified in a single option by separating them +with commas - `-C target-feature=+x,-y`. \ +If some feature is specified more than once with both `+` and `-`, +then values passed later override values passed earlier. \ +For example, `-C target-feature=+x,-y,+z -Ctarget-feature=-x,+y` +is equivalent to `-C target-feature=-x,+y,+z`. -## default-linker-libraries +To see the valid options and an example of use, run `rustc --print +target-features`. -This flag controls whether or not the linker includes its default libraries. -It takes one of the following values: +Using this flag is unsafe and might result in [undefined runtime +behavior](../targets/known-issues.md). -* `y`, `yes`, `on`, or no value: Default libraries are included. -* `n`, `no`, or `off`: Default libraries are **not** included. +See also the [`target_feature` +attribute](../../reference/attributes/codegen.md#the-target_feature-attribute) +for controlling features per-function. -For example, for gcc flavor linkers, this issues the `-nodefaultlibs` flag to -the linker. +This also supports the feature `+crt-static` and `-crt-static` to control +[static C runtime linkage](../../reference/linkage.html#static-and-dynamic-c-runtimes). -The default is `yes` if not specified. +Each target and [`target-cpu`](#target-cpu) has a default set of enabled +features. [option-emit]: ../command-line-arguments.md#option-emit [option-o-optimize]: ../command-line-arguments.md#option-o-optimize diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md index c46380f1505df..5dee603142dcd 100644 --- a/src/doc/rustc/src/json.md +++ b/src/doc/rustc/src/json.md @@ -59,8 +59,11 @@ Diagnostics have the following format: "spans": [ { /* The file where the span is located. - For spans located within a macro expansion, this will be the - name of the expanded macro in the format "". + Note that this path may not exist. For example, if the path + points to the standard library, and the rust src is not + available in the sysroot, then it may point to a non-existent + file. Beware that this may also point to the source of an + external crate. */ "file_name": "lib.rs", /* The byte offset where the span starts (0-based, inclusive). */ diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md index 6f1bbe60569fd..c0b14352b7d1a 100644 --- a/src/doc/rustc/src/linker-plugin-lto.md +++ b/src/doc/rustc/src/linker-plugin-lto.md @@ -100,11 +100,17 @@ LLVM. However, the approximation is usually reliable. The following table shows known good combinations of toolchain versions. -| | Clang 7 | Clang 8 | -|-----------|-----------|-----------| -| Rust 1.34 | ✗ | ✓ | -| Rust 1.35 | ✗ | ✓ | -| Rust 1.36 | ✗ | ✓ | -| Rust 1.37 | ✗ | ✓ | +| | Clang 7 | Clang 8 | Clang 9 | +|-----------|-----------|-----------|-----------| +| Rust 1.34 | ✗ | ✓ | ✗ | +| Rust 1.35 | ✗ | ✓ | ✗ | +| Rust 1.36 | ✗ | ✓ | ✗ | +| Rust 1.37 | ✗ | ✓ | ✗ | +| Rust 1.38 | ✗ | ✗ | ✓ | +| Rust 1.39 | ✗ | ✗ | ✓ | +| Rust 1.40 | ✗ | ✗ | ✓ | +| Rust 1.41 | ✗ | ✗ | ✓ | +| Rust 1.42 | ✗ | ✗ | ✓ | +| Rust 1.43 | ✗ | ✗ | ✓ | Note that the compatibility policy for this feature might change in the future. diff --git a/src/doc/rustc/src/lints/levels.md b/src/doc/rustc/src/lints/levels.md index 3cfe2f698f3e0..64cbbbb003585 100644 --- a/src/doc/rustc/src/lints/levels.md +++ b/src/doc/rustc/src/lints/levels.md @@ -170,7 +170,7 @@ The order of these command line arguments is taken into account. The following a $ rustc lib.rs --crate-type=lib -D unused-variables -A unused-variables ``` -You can make use of this behavior by overriding the level of one specific lint out of a group of lints. The following example denies all the lints in the `unused` group, but explicitly allows the `unused-variables` lint in that group: +You can make use of this behavior by overriding the level of one specific lint out of a group of lints. The following example denies all the lints in the `unused` group, but explicitly allows the `unused-variables` lint in that group (forbid still trumps everything regardless of ordering): ```bash $ rustc lib.rs --crate-type=lib -D unused -A unused-variables diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index 78181156e250f..efadae1c5fb9d 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -352,9 +352,9 @@ are added. /// ``` ``` -`edition2018` tells `rustdoc` that the code sample should be compiled the 2018 -edition of Rust. Similarly, you can specify `edition2015` to compile the code -with the 2015 edition. +`edition2018` tells `rustdoc` that the code sample should be compiled using +the 2018 edition of Rust. Similarly, you can specify `edition2015` to compile +the code with the 2015 edition. ## Syntax reference diff --git a/src/doc/rustdoc/src/passes.md b/src/doc/rustdoc/src/passes.md index 12d4ea205b31e..081e477de8010 100644 --- a/src/doc/rustdoc/src/passes.md +++ b/src/doc/rustdoc/src/passes.md @@ -17,7 +17,7 @@ By default, rustdoc will run some passes, namely: * `collapse-docs` * `unindent-comments` -However, `strip-private` implies `strip-private-imports`, and so effectively, +However, `strip-private` implies `strip-priv-imports`, and so effectively, all passes are run by default. ## `strip-hidden` diff --git a/src/doc/unstable-book/book.toml b/src/doc/unstable-book/book.toml index 5b2e19bd7aa78..0cd56d0940451 100644 --- a/src/doc/unstable-book/book.toml +++ b/src/doc/unstable-book/book.toml @@ -1,3 +1,6 @@ [book] title = "The Rust Unstable Book" author = "The Rust Community" + +[output.html] +git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/unstable-book" diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 414ac7e63a331..7ebd8054ba0b0 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -6,73 +6,78 @@ The tracking issue for this feature is: [#39699](https://github.com/rust-lang/ru This feature allows for use of one of following sanitizers: -* [AddressSanitizer][clang-asan] a faster memory error detector. Can - detect out-of-bounds access to heap, stack, and globals, use after free, use - after return, double free, invalid free, memory leaks. +* [AddressSanitizer][clang-asan] a fast memory error detector. * [LeakSanitizer][clang-lsan] a run-time memory leak detector. * [MemorySanitizer][clang-msan] a detector of uninitialized reads. * [ThreadSanitizer][clang-tsan] a fast data race detector. -To enable a sanitizer compile with `-Zsanitizer=...` option, where value is one -of `address`, `leak`, `memory` or `thread`. +To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`, +`-Zsanitizer=memory` or `-Zsanitizer=thread`. Only a single sanitizer can be +enabled at a time. -# Examples +# AddressSanitizer -This sections show various issues that can be detected with sanitizers. For -simplicity, the examples are prepared under assumption that optimization level -used is zero. +AddressSanitizer is a memory error detector. It can detect the following types +of bugs: -## AddressSanitizer +* Out of bound accesses to heap, stack and globals +* Use after free +* Use after return (runtime flag `ASAN_OPTIONS=detect_stack_use_after_return=1`) +* Use after scope +* Double-free, invalid free +* Memory leaks + +AddressSanitizer is supported on the following targets: + +* `x86_64-apple-darwin` +* `x86_64-unknown-linux-gnu` + +AddressSanitizer works with non-instrumented code although it will impede its +ability to detect some bugs. It is not expected to produce false positive +reports. + +## Examples Stack buffer overflow: -```shell -$ cat a.rs +```rust fn main() { let xs = [0, 1, 2, 3]; let _y = unsafe { *xs.as_ptr().offset(4) }; } -$ rustc -Zsanitizer=address a.rs -$ ./a -================================================================= -==10029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcc15f43d0 at pc 0x55f77dc015c5 bp 0x7ffcc15f4390 sp 0x7ffcc15f4388 -READ of size 4 at 0x7ffcc15f43d0 thread T0 - #0 0x55f77dc015c4 in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa5c4) - #1 0x55f77dc01cdb in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::haa8c76d1faa7b7ca (/tmp/a+0xacdb) - #2 0x55f77dc90f02 in std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::hfeb9a1aef9ac820d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:48:12 - #3 0x55f77dc90f02 in std::panicking::try::do_call::h12f0919717b8e0a6 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:288:39 - #4 0x55f77dc926c9 in __rust_maybe_catch_panic /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libpanic_unwind/lib.rs:80:7 - #5 0x55f77dc9197c in std::panicking::try::h413b21cdcd6cfd86 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:267:12 - #6 0x55f77dc9197c in std::panic::catch_unwind::hc5cc8ef2fd73424d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panic.rs:396:8 - #7 0x55f77dc9197c in std::rt::lang_start_internal::h2039f418ab92218f /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:47:24 - #8 0x55f77dc01c61 in std::rt::lang_start::ha905d28f6b61d691 (/tmp/a+0xac61) - #9 0x55f77dc0163a in main (/tmp/a+0xa63a) - #10 0x7f9b3cf5bbba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba) - #11 0x55f77dc01289 in _start (/tmp/a+0xa289) - -Address 0x7ffcc15f43d0 is located in stack of thread T0 at offset 48 in frame - #0 0x55f77dc0135f in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa35f) +``` + +```shell +$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu +==37882==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe400e6250 at pc 0x5609a841fb20 bp 0x7ffe400e6210 sp 0x7ffe400e6208 +READ of size 4 at 0x7ffe400e6250 thread T0 + #0 0x5609a841fb1f in example::main::h628ffc6626ed85b2 /.../src/main.rs:3:23 + ... + +Address 0x7ffe400e6250 is located in stack of thread T0 at offset 48 in frame + #0 0x5609a841f8af in example::main::h628ffc6626ed85b2 /.../src/main.rs:1 This frame has 1 object(s): - [32, 48) 'xs' <== Memory access at offset 48 overflows this variable + [32, 48) 'xs' (line 2) <== Memory access at offset 48 overflows this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) -SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/a+0xa5c4) in a::main::hab3bd2a745c2d0ac +SUMMARY: AddressSanitizer: stack-buffer-overflow /.../src/main.rs:3:23 in example::main::h628ffc6626ed85b2 Shadow bytes around the buggy address: - 0x1000182b6820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -=>0x1000182b6870: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00 - 0x1000182b6880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b68a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b68b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b68c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x100048014c40: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00 + 0x100048014c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c70: f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 00 00 00 00 + 0x100048014c80: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 + 0x100048014c90: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 - Partially addressable: 01 02 03 04 05 06 07 + Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 @@ -90,13 +95,12 @@ Shadow byte legend (one shadow byte represents 8 application bytes): Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc -==10029==ABORTING +==37882==ABORTING ``` Use of a stack object after its scope has already ended: -```shell -$ cat b.rs +```rust static mut P: *mut usize = std::ptr::null_mut(); fn main() { @@ -108,42 +112,38 @@ fn main() { std::ptr::write_volatile(P, 123); } } -$ rustc -Zsanitizer=address b.rs -$./b +``` + +```shell +$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu ================================================================= -==424427==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fff67be6be0 at pc 0x5647a3ea4658 bp 0x7fff67be6b90 sp 0x7fff67be6b88 -WRITE of size 8 at 0x7fff67be6be0 thread T0 - #0 0x5647a3ea4657 in core::ptr::write_volatile::h4b04601757d0376d (/tmp/b+0xb8657) - #1 0x5647a3ea4432 in b::main::h5574a756e615c9cf (/tmp/b+0xb8432) - #2 0x5647a3ea480b in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hd57e7ee01866077e (/tmp/b+0xb880b) - #3 0x5647a3eab412 in std::panicking::try::do_call::he0421ca82dd11ba3 (.llvm.8083791802951296215) (/tmp/b+0xbf412) - #4 0x5647a3eacb26 in __rust_maybe_catch_panic (/tmp/b+0xc0b26) - #5 0x5647a3ea5b66 in std::rt::lang_start_internal::h19bc96b28f670a64 (/tmp/b+0xb9b66) - #6 0x5647a3ea4788 in std::rt::lang_start::h642d10b4b6965fb8 (/tmp/b+0xb8788) - #7 0x5647a3ea449a in main (/tmp/b+0xb849a) - #8 0x7fd1d18b3bba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba) - #9 0x5647a3df7299 in _start (/tmp/b+0xb299) - -Address 0x7fff67be6be0 is located in stack of thread T0 at offset 32 in frame - #0 0x5647a3ea433f in b::main::h5574a756e615c9cf (/tmp/b+0xb833f) +==39249==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc7ed3e1a0 at pc 0x55c98b262a8e bp 0x7ffc7ed3e050 sp 0x7ffc7ed3e048 +WRITE of size 8 at 0x7ffc7ed3e1a0 thread T0 + #0 0x55c98b262a8d in core::ptr::write_volatile::he21f1df5a82f329a /.../src/rust/src/libcore/ptr/mod.rs:1048:5 + #1 0x55c98b262cd2 in example::main::h628ffc6626ed85b2 /.../src/main.rs:9:9 + ... + +Address 0x7ffc7ed3e1a0 is located in stack of thread T0 at offset 32 in frame + #0 0x55c98b262bdf in example::main::h628ffc6626ed85b2 /.../src/main.rs:3 This frame has 1 object(s): - [32, 40) 'x' <== Memory access at offset 32 is inside this variable + [32, 40) 'x' (line 6) <== Memory access at offset 32 is inside this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) -SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/b+0xb8657) in core::ptr::write_volatile::h4b04601757d0376d +SUMMARY: AddressSanitizer: stack-use-after-scope /.../src/rust/src/libcore/ptr/mod.rs:1048:5 in core::ptr::write_volatile::he21f1df5a82f329a Shadow bytes around the buggy address: - 0x10006cf74d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -=>0x10006cf74d70: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[f8]f3 f3 f3 - 0x10006cf74d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74dc0: f1 f1 f1 f1 00 f3 f3 f3 00 00 00 00 00 00 00 00 + 0x10000fd9fbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc00: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 + 0x10000fd9fc10: f8 f8 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x10000fd9fc30: f1 f1 f1 f1[f8]f3 f3 f3 00 00 00 00 00 00 00 00 + 0x10000fd9fc40: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 + 0x10000fd9fc50: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc60: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 + 0x10000fd9fc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc80: 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 @@ -164,17 +164,26 @@ Shadow byte legend (one shadow byte represents 8 application bytes): Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc -==424427==ABORTING +==39249==ABORTING ``` -## MemorySanitizer +# MemorySanitizer + +MemorySanitizer is detector of uninitialized reads. It is only supported on the +`x86_64-unknown-linux-gnu` target. + +MemorySanitizer requires all program code to be instrumented. C/C++ dependencies +need to be recompiled using Clang with `-fsanitize=memory` option. Failing to +achieve that will result in false positive reports. + +## Example -Use of uninitialized memory. Note that we are using `-Zbuild-std` to instrument -the standard library, and passing `-Zsanitizer-track-origins` to track the +Detecting the use of uninitialized memory. The `-Zbuild-std` flag rebuilds and +instruments the standard library, and is strictly necessary for the correct +operation of the tool. The `-Zsanitizer-track-origins` enables tracking of the origins of uninitialized memory: -```shell -$ cat src/main.rs +```rust use std::mem::MaybeUninit; fn main() { @@ -184,7 +193,9 @@ fn main() { println!("{}", a[2]); } } +``` +```shell $ export \ CC=clang \ CXX=clang++ \ @@ -193,7 +204,7 @@ $ export \ RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' \ RUSTDOCFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' $ cargo clean -$ cargo -Zbuild-std run --target x86_64-unknown-linux-gnu +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu ==9416==WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x560c04f7488a in core::fmt::num::imp::fmt_u64::haa293b0b098501ca $RUST/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/src/libcore/fmt/num.rs:202:16 ... @@ -205,6 +216,55 @@ $ cargo -Zbuild-std run --target x86_64-unknown-linux-gnu #0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3 ``` +# ThreadSanitizer + +ThreadSanitizer is a data race detection tool. It is supported on the following +targets: + +* `x86_64-apple-darwin` +* `x86_64-unknown-linux-gnu` + +To work correctly ThreadSanitizer needs to be "aware" of all synchronization +operations in a program. It generally achieves that through combination of +library interception (for example synchronization performed through +`pthread_mutex_lock` / `pthread_mutex_unlock`) and compile time instrumentation +(e.g. atomic operations). Using it without instrumenting all the program code +can lead to false positive reports. + +ThreadSanitizer does not support atomic fences `std::sync::atomic::fence`, +nor synchronization performed using inline assembly code. + +## Example + +```rust +static mut A: usize = 0; + +fn main() { + let t = std::thread::spawn(|| { + unsafe { A += 1 }; + }); + unsafe { A += 1 }; + + t.join().unwrap(); +} +``` + +```shell +$ export RUSTFLAGS=-Zsanitizer=thread RUSTDOCFLAGS=-Zsanitizer=thread +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu +================== +WARNING: ThreadSanitizer: data race (pid=10574) + Read of size 8 at 0x5632dfe3d030 by thread T1: + #0 example::main::_$u7b$$u7b$closure$u7d$$u7d$::h23f64b0b2f8c9484 ../src/main.rs:5:18 (example+0x86cec) + ... + + Previous write of size 8 at 0x5632dfe3d030 by main thread: + #0 example::main::h628ffc6626ed85b2 /.../src/main.rs:7:14 (example+0x868c8) + ... + #11 main (example+0x86a1a) + + Location is global 'example::A::h43ac149ddf992709' of size 8 at 0x5632dfe3d030 (example+0x000000bd9030) +``` # Instrumentation of external dependencies and std @@ -231,6 +291,10 @@ In more practical terms when using cargo always remember to pass `--target` flag, so that rustflags will not be applied to build scripts and procedural macros. +# Symbolizing the Reports + +Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PATH`. + # Additional Information * [Sanitizers project page](https://github.com/google/sanitizers/wiki/) diff --git a/src/doc/unstable-book/src/compiler-flags/src-hash-algorithm.md b/src/doc/unstable-book/src/compiler-flags/src-hash-algorithm.md new file mode 100644 index 0000000000000..5a7d0655a440a --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/src-hash-algorithm.md @@ -0,0 +1,11 @@ +# `src-hash-algorithm` + +The tracking issue for this feature is: [#70401](https://github.com/rust-lang/rust/issues/70401). + +------------------------ + +The `-Z src-hash-algorithm` compiler flag controls which algorithm is used when hashing each source file. The hash is stored in the debug info and can be used by a debugger to verify the source code matches the executable. + +Supported hash algorithms are: `md5`, and `sha1`. Note that not all hash algorithms are supported by all debug info formats. + +By default, the compiler chooses the hash algorithm based on the target specification. diff --git a/src/doc/unstable-book/src/compiler-flags/strip.md b/src/doc/unstable-book/src/compiler-flags/strip.md new file mode 100644 index 0000000000000..52cb98113c0c1 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/strip.md @@ -0,0 +1,17 @@ +# `strip` + +The tracking issue for this feature is: [#72110](https://github.com/rust-lang/rust/issues/72110). + +------------------------ + +Option `-Z strip=val` controls stripping of debuginfo and similar auxiliary data from binaries +during linking. + +Supported values for this option are: + +- `none` - debuginfo and symbols (if they exist) are copied to the produced binary or separate files +depending on the target (e.g. `.pdb` files in case of MSVC). +- `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table section +are stripped at link time and are not copied to the produced binary or separate files. +- `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well +if the linker supports it. diff --git a/src/doc/unstable-book/src/compiler-flags/tls-model.md b/src/doc/unstable-book/src/compiler-flags/tls-model.md new file mode 100644 index 0000000000000..0aefaa7fb0177 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/tls-model.md @@ -0,0 +1,25 @@ +# `tls_model` + +The tracking issue for this feature is: None. + +------------------------ + +Option `-Z tls-model` controls [TLS model](https://www.akkadia.org/drepper/tls.pdf) used to +generate code for accessing `#[thread_local]` `static` items. + +Supported values for this option are: + +- `global-dynamic` - General Dynamic TLS Model (alternatively called Global Dynamic) is the most +general option usable in all circumstances, even if the TLS data is defined in a shared library +loaded at runtime and is accessed from code outside of that library. +This is the default for most targets. +- `local-dynamic` - model usable if the TLS data is only accessed from the shared library or +executable it is defined in. The TLS data may be in a library loaded after startup (via `dlopen`). +- `initial-exec` - model usable if the TLS data is defined in the executable or in a shared library +loaded at program startup. +The TLS data must not be in a library loaded after startup (via `dlopen`). +- `local-exec` - model usable only if the TLS data is defined directly in the executable, +but not in a shared library, and is accessed only from that executable. + +`rustc` and LLVM may use a more optimized model than specified if they know that we are producing +and executable rather than a library, or that the `static` item is private enough. diff --git a/src/doc/unstable-book/src/language-features/cfg-sanitize.md b/src/doc/unstable-book/src/language-features/cfg-sanitize.md index 949f24ab9c11e..3442abf46df86 100644 --- a/src/doc/unstable-book/src/language-features/cfg-sanitize.md +++ b/src/doc/unstable-book/src/language-features/cfg-sanitize.md @@ -11,26 +11,24 @@ depending on whether a particular sanitizer is enabled or not. ## Examples -``` rust +```rust #![feature(cfg_sanitize)] #[cfg(sanitize = "thread")] fn a() { - // ... + // ... } #[cfg(not(sanitize = "thread"))] fn a() { - // ... + // ... } fn b() { - if cfg!(sanitize = "leak") { - // ... - } else { - // ... - } + if cfg!(sanitize = "leak") { + // ... + } else { + // ... + } } - ``` - diff --git a/src/doc/unstable-book/src/language-features/cfg-version.md b/src/doc/unstable-book/src/language-features/cfg-version.md new file mode 100644 index 0000000000000..2b1e50835b767 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/cfg-version.md @@ -0,0 +1,34 @@ +# `cfg_version` + +The tracking issue for this feature is: [#64796] + +[#64796]: https://github.com/rust-lang/rust/issues/64796 + +------------------------ + +The `cfg_version` feature makes it possible to execute different code +depending on the compiler version. + +## Examples + +```rust +#![feature(cfg_version)] + +#[cfg(version("1.42"))] +fn a() { + // ... +} + +#[cfg(not(version("1.42")))] +fn a() { + // ... +} + +fn b() { + if cfg!(version("1.42")) { + // ... + } else { + // ... + } +} +``` diff --git a/src/doc/unstable-book/src/language-features/ffi-const.md b/src/doc/unstable-book/src/language-features/ffi-const.md new file mode 100644 index 0000000000000..9a1ced4033b22 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/ffi-const.md @@ -0,0 +1,47 @@ +# `ffi_const` + +The `#[ffi_const]` attribute applies clang's `const` attribute to foreign +functions declarations. + +That is, `#[ffi_const]` functions shall have no effects except for its return +value, which can only depend on the values of the function parameters, and is +not affected by changes to the observable state of the program. + +Applying the `#[ffi_const]` attribute to a function that violates these +requirements is undefined behaviour. + +This attribute enables Rust to perform common optimizations, like sub-expression +elimination, and it can avoid emitting some calls in repeated invocations of the +function with the same argument values regardless of other operations being +performed in between these functions calls (as opposed to `#[ffi_pure]` +functions). + +## Pitfalls + +A `#[ffi_const]` function can only read global memory that would not affect +its return value for the whole execution of the program (e.g. immutable global +memory). `#[ffi_const]` functions are referentially-transparent and therefore +more strict than `#[ffi_pure]` functions. + +A common pitfall involves applying the `#[ffi_const]` attribute to a +function that reads memory through pointer arguments which do not necessarily +point to immutable global memory. + +A `#[ffi_const]` function that returns unit has no effect on the abstract +machine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`. + +A `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a +call to `abort`) nor by infinite loops. + +When translating C headers to Rust FFI, it is worth verifying for which targets +the `const` attribute is enabled in those headers, and using the appropriate +`cfg` macros in the Rust side to match those definitions. While the semantics of +`const` are implemented identically by many C and C++ compilers, e.g., clang, +[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily +implemented in this way on all of them. It is therefore also worth verifying +that the semantics of the C toolchain used to compile the binary being linked +against are compatible with those of the `#[ffi_const]`. + +[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html +[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute +[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm diff --git a/src/doc/unstable-book/src/language-features/ffi-pure.md b/src/doc/unstable-book/src/language-features/ffi-pure.md new file mode 100644 index 0000000000000..7bfd7a378f00b --- /dev/null +++ b/src/doc/unstable-book/src/language-features/ffi-pure.md @@ -0,0 +1,51 @@ +# `ffi_pure` + +The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign +functions declarations. + +That is, `#[ffi_pure]` functions shall have no effects except for its return +value, which shall not change across two consecutive function calls with +the same parameters. + +Applying the `#[ffi_pure]` attribute to a function that violates these +requirements is undefined behavior. + +This attribute enables Rust to perform common optimizations, like sub-expression +elimination and loop optimizations. Some common examples of pure functions are +`strlen` or `memcmp`. + +These optimizations are only applicable when the compiler can prove that no +program state observable by the `#[ffi_pure]` function has changed between calls +of the function, which could alter the result. See also the `#[ffi_const]` +attribute, which provides stronger guarantees regarding the allowable behavior +of a function, enabling further optimization. + +## Pitfalls + +A `#[ffi_pure]` function can read global memory through the function +parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not +referentially-transparent, and are therefore more relaxed than `#[ffi_const]` +functions. + +However, accesing global memory through volatile or atomic reads can violate the +requirement that two consecutive function calls shall return the same value. + +A `pure` function that returns unit has no effect on the abstract machine's +state. + +A `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a +call to `abort`) nor by infinite loops. + +When translating C headers to Rust FFI, it is worth verifying for which targets +the `pure` attribute is enabled in those headers, and using the appropriate +`cfg` macros in the Rust side to match those definitions. While the semantics of +`pure` are implemented identically by many C and C++ compilers, e.g., clang, +[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily +implemented in this way on all of them. It is therefore also worth verifying +that the semantics of the C toolchain used to compile the binary being linked +against are compatible with those of the `#[ffi_pure]`. + + +[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html +[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute +[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm diff --git a/src/doc/unstable-book/src/language-features/generators.md b/src/doc/unstable-book/src/language-features/generators.md index 8bc62418b3969..7b865c9c679bc 100644 --- a/src/doc/unstable-book/src/language-features/generators.md +++ b/src/doc/unstable-book/src/language-features/generators.md @@ -87,7 +87,7 @@ Feedback on the design and usage is always appreciated! The `Generator` trait in `std::ops` currently looks like: -``` +```rust # #![feature(arbitrary_self_types, generator_trait)] # use std::ops::GeneratorState; # use std::pin::Pin; @@ -107,7 +107,7 @@ point for executing the `Generator` itself. The return value of `resume`, `GeneratorState`, looks like: -``` +```rust pub enum GeneratorState { Yielded(Y), Complete(R), diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index 250824321920d..20c7d7dcec8d6 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -287,6 +287,7 @@ the source code. - `unsize`: `libcore/marker.rs` - `sync`: `libcore/marker.rs` - `phantom_data`: `libcore/marker.rs` + - `discriminant_kind`: `libcore/marker.rs` - `freeze`: `libcore/marker.rs` - `debug_trait`: `libcore/fmt/mod.rs` - `non_zero`: `libcore/nonzero.rs` diff --git a/src/doc/unstable-book/src/language-features/negative-impls.md b/src/doc/unstable-book/src/language-features/negative-impls.md new file mode 100644 index 0000000000000..151520f0e4abc --- /dev/null +++ b/src/doc/unstable-book/src/language-features/negative-impls.md @@ -0,0 +1,57 @@ +# `negative_impls` + +The tracking issue for this feature is [#68318]. + +[#68318]: https://github.com/rust-lang/rust/issues/68318 + +---- + +With the feature gate `negative_impls`, you can write negative impls as well as positive ones: + +```rust +#![feature(negative_impls)] +trait DerefMut { } +impl !DerefMut for &T { } +``` + +Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below. + +Negative impls have the following characteristics: + +* They do not have any items. +* They must obey the orphan rules as if they were a positive impl. +* They cannot "overlap" with any positive impls. + +## Semver interaction + +It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types. + +## Orphan and overlap rules + +Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth. + +Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.) + +## Interaction with auto traits + +Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an +auto-trait serves two purposes: + +* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`; +* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated. + +Note that, at present, there is no way to indicate that a given type +does not implement an auto trait *but that it may do so in the +future*. For ordinary types, this is done by simply not declaring any +impl at all, but that is not an option for auto traits. A workaround +is that one could embed a marker type as one of the fields, where the +marker type is `!AutoTrait`. + +## Immediate uses + +Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544). + +This serves two purposes: + +* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists. +* It prevents downstream crates from creating such impls. diff --git a/src/doc/unstable-book/src/language-features/optin-builtin-traits.md b/src/doc/unstable-book/src/language-features/optin-builtin-traits.md index 1ec8b8bf8ddbd..6f0f0cfd33e4d 100644 --- a/src/doc/unstable-book/src/language-features/optin-builtin-traits.md +++ b/src/doc/unstable-book/src/language-features/optin-builtin-traits.md @@ -10,7 +10,8 @@ The `optin_builtin_traits` feature gate allows you to define auto traits. Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits that are automatically implemented for every type, unless the type, or a type it contains, -has explicitly opted out via a negative impl. +has explicitly opted out via a negative impl. (Negative impls are separately controlled +by the `negative_impls` feature.) [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html @@ -22,6 +23,7 @@ impl !Trait for Type Example: ```rust +#![feature(negative_impls)] #![feature(optin_builtin_traits)] auto trait Valid {} @@ -43,3 +45,63 @@ fn main() { // must_be_valid( MaybeValid(False) ); } ``` + +## Automatic trait implementations + +When a type is declared as an `auto trait`, we will automatically +create impls for every struct/enum/union, unless an explicit impl is +provided. These automatic impls contain a where clause for each field +of the form `T: AutoTrait`, where `T` is the type of the field and +`AutoTrait` is the auto trait in question. As an example, consider the +struct `List` and the auto trait `Send`: + +```rust +struct List { + data: T, + next: Option>>, +} +``` + +Presuming that there is no explicit impl of `Send` for `List`, the +compiler will supply an automatic impl of the form: + +```rust +struct List { + data: T, + next: Option>>, +} + +unsafe impl Send for List +where + T: Send, // from the field `data` + Option>>: Send, // from the field `next` +{ } +``` + +Explicit impls may be either positive or negative. They take the form: + +```rust,ignore +impl<...> AutoTrait for StructName<..> { } +impl<...> !AutoTrait for StructName<..> { } +``` + +## Coinduction: Auto traits permit cyclic matching + +Unlike ordinary trait matching, auto traits are **coinductive**. This +means, in short, that cycles which occur in trait matching are +considered ok. As an example, consider the recursive struct `List` +introduced in the previous section. In attempting to determine whether +`List: Send`, we would wind up in a cycle: to apply the impl, we must +show that `Option>: Send`, which will in turn require +`Box: Send` and then finally `List: Send` again. Under ordinary +trait matching, this cycle would be an error, but for an auto trait it +is considered a successful match. + +## Items + +Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations. + +## Supertraits + +Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile. + diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 2a1b6397781f9..0b68991fce2a1 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -1,8 +1,8 @@ # `asm` -The tracking issue for this feature is: [#29722] +The tracking issue for this feature is: [#72016] -[#29722]: https://github.com/rust-lang/rust/issues/29722 +[#72016]: https://github.com/rust-lang/rust/issues/72016 ------------------------ @@ -10,184 +10,690 @@ For extremely low-level manipulations and performance reasons, one might wish to control the CPU directly. Rust supports using inline assembly to do this via the `asm!` macro. -```rust,ignore -asm!(assembly template - : output operands - : input operands - : clobbers - : options - ); +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Rust provides support for inline assembly via the `asm!` macro. +It can be used to embed handwritten assembly in the assembly output generated by the compiler. +Generally this should not be necessary, but might be where the required performance or timing +cannot be otherwise achieved. Accessing low level hardware primitives, e.g. in kernel code, may also demand this functionality. + +> **Note**: the examples here are given in x86/x86-64 assembly, but ARM, AArch64 and RISC-V are also supported. + +## Basic usage + +Let us start with the simplest possible example: + +```rust,allow_fail +# #![feature(asm)] +unsafe { + asm!("nop"); +} +``` + +This will insert a NOP (no operation) instruction into the assembly generated by the compiler. +Note that all `asm!` invocations have to be inside an `unsafe` block, as they could insert +arbitrary instructions and break various invariants. The instructions to be inserted are listed +in the first argument of the `asm!` macro as a string literal. + +## Inputs and outputs + +Now inserting an instruction that does nothing is rather boring. Let us do something that +actually acts on data: + +```rust,allow_fail +# #![feature(asm)] +let x: u64; +unsafe { + asm!("mov {}, 5", out(reg) x); +} +assert_eq!(x, 5); ``` -Any use of `asm` is feature gated (requires `#![feature(asm)]` on the -crate to allow) and of course requires an `unsafe` block. +This will write the value `5` into the `u64` variable `x`. +You can see that the string literal we use to specify instructions is actually a template string. +It is governed by the same rules as Rust [format strings][format-syntax]. +The arguments that are inserted into the template however look a bit different then you may +be familiar with. First we need to specify if the variable is an input or an output of the +inline assembly. In this case it is an output. We declared this by writing `out`. +We also need to specify in what kind of register the assembly expects the variable. +In this case we put it in an arbitrary general purpose register by specifying `reg`. +The compiler will choose an appropriate register to insert into +the template and will read the variable from there after the inline assembly finishes executing. -> **Note**: the examples here are given in x86/x86-64 assembly, but -> all platforms are supported. +Let us see another example that also uses an input: -## Assembly template +```rust,allow_fail +# #![feature(asm)] +let i: u64 = 3; +let o: u64; +unsafe { + asm!(" + mov {0}, {1} + add {0}, {number} + ", out(reg) o, in(reg) i, number = const 5); +} +assert_eq!(o, 8); +``` -The `assembly template` is the only required parameter and must be a -literal string (i.e. `""`) +This will add `5` to the input in variable `i` and write the result to variable `o`. +The particular way this assembly does this is first copying the value from `i` to the output, +and then adding `5` to it. -```rust -#![feature(asm)] +The example shows a few things: -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn foo() { - unsafe { - asm!("NOP"); - } +First we can see that inputs are declared by writing `in` instead of `out`. + +Second one of our operands has a type we haven't seen yet, `const`. +This tells the compiler to expand this argument to value directly inside the assembly template. +This is only possible for constants and literals. + +Third we can see that we can specify an argument number, or name as in any format string. +For inline assembly templates this is particularly useful as arguments are often used more than once. +For more complex inline assembly using this facility is generally recommended, as it improves +readability, and allows reordering instructions without changing the argument order. + +We can further refine the above example to avoid the `mov` instruction: + +```rust,allow_fail +# #![feature(asm)] +let mut x: u64 = 3; +unsafe { + asm!("add {0}, {number}", inout(reg) x, number = const 5); +} +assert_eq!(x, 8); +``` + +We can see that `inout` is used to specify an argument that is both input and output. +This is different from specifying an input and output separately in that it is guaranteed to assign both to the same register. + +It is also possible to specify different variables for the input and output parts of an `inout` operand: + +```rust,allow_fail +# #![feature(asm)] +let x: u64 = 3; +let y: u64; +unsafe { + asm!("add {0}, {number}", inout(reg) x => y, number = const 5); } +assert_eq!(y, 8); +``` -// Other platforms: -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -fn foo() { /* ... */ } +## Late output operands -fn main() { - // ... - foo(); - // ... +The Rust compiler is conservative with its allocation of operands. It is assumed that an `out` +can be written at any time, and can therefore not share its location with any other argument. +However, to guarantee optimal performance it is important to use as few registers as possible, +so they won't have to be saved and reloaded around the inline assembly block. +To achieve this Rust provides a `lateout` specifier. This can be used on any output that is +written only after all inputs have been consumed. +There is also a `inlateout` variant of this specifier. + +Here is an example where `inlateout` *cannot* be used: + +```rust,allow_fail +# #![feature(asm)] +let mut a: u64 = 4; +let b: u64 = 4; +let c: u64 = 4; +unsafe { + asm!(" + add {0}, {1} + add {0}, {2} + ", inout(reg) a, in(reg) b, in(reg) c); } +assert_eq!(a, 12); ``` -(The `feature(asm)` and `#[cfg]`s are omitted from now on.) +Here the compiler is free to allocate the same register for inputs `b` and `c` since it knows they have the same value. However it must allocate a separate register for `a` since it uses `inout` and not `inlateout`. If `inlateout` was used, then `a` and `c` could be allocated to the same register, in which case the first instruction to overwrite the value of `c` and cause the assembly code to produce the wrong result. -Output operands, input operands, clobbers and options are all optional -but you must add the right number of `:` if you skip them: +However the following example can use `inlateout` since the output is only modified after all input registers have been read: -```rust +```rust,allow_fail # #![feature(asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { unsafe { -asm!("xor %eax, %eax" - : - : - : "eax" - ); -# } } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} +let mut a: u64 = 4; +let b: u64 = 4; +unsafe { + asm!("add {0}, {1}", inlateout(reg) a, in(reg) b); +} +assert_eq!(a, 8); ``` -Whitespace also doesn't matter: +As you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register. + +## Explicit register operands + +Some instructions require that the operands be in a specific register. +Therefore, Rust inline assembly provides some more specific constraint specifiers. +While `reg` is generally available on any architecture, these are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi` +among others can be addressed by their name. -```rust +```rust,allow_fail,no_run # #![feature(asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { unsafe { -asm!("xor %eax, %eax" ::: "eax"); -# } } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} +let cmd = 0xd1; +unsafe { + asm!("out 0x64, eax", in("eax") cmd); +} ``` -## Operands +In this example we call the `out` instruction to output the content of the `cmd` variable +to port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand +we had to use the `eax` constraint specifier. + +Note that unlike other operand types, explicit register operands cannot be used in the template string: you can't use `{}` and should write the register name directly instead. Also, they must appear at the end of the operand list after all other operand types. -Input and output operands follow the same format: `: -"constraints1"(expr1), "constraints2"(expr2), ..."`. Output operand -expressions must be mutable lvalues, or not yet assigned: +Consider this example which uses the x86 `mul` instruction: -```rust +```rust,allow_fail # #![feature(asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn add(a: i32, b: i32) -> i32 { - let c: i32; +fn mul(a: u64, b: u64) -> u128 { + let lo: u64; + let hi: u64; + unsafe { - asm!("add $2, $0" - : "=r"(c) - : "0"(a), "r"(b) - ); + asm!( + // The x86 mul instruction takes rax as an implicit input and writes + // the 128-bit result of the multiplication to rax:rdx. + "mul {}", + in(reg) a, + inlateout("rax") b => lo, + lateout("rdx") hi + ); } - c + + (hi as u128) << 64 + lo as u128 +} +``` + +This uses the `mul` instruction to multiply two 64-bit inputs with a 128-bit result. +The only explicit operand is a register, that we fill from the variable `a`. +The second operand is implicit, and must be the `rax` register, which we fill from the variable `b`. +The lower 64 bits of the result are stored in `rax` from which we fill the variable `lo`. +The higher 64 bits are stored in `rdx` from which we fill the variable `hi`. + +## Clobbered registers + +In many cases inline assembly will modify state that is not needed as an output. +Usually this is either because we have to use a scratch register in the assembly, +or instructions modify state that we don't need to further examine. +This state is generally referred to as being "clobbered". +We need to tell the compiler about this since it may need to save and restore this state +around the inline assembly block. + +```rust,allow_fail +# #![feature(asm)] +let ebx: u32; +let ecx: u32; + +unsafe { + asm!( + "cpuid", + // EAX 4 selects the "Deterministic Cache Parameters" CPUID leaf + inout("eax") 4 => _, + // ECX 0 selects the L0 cache information. + inout("ecx") 0 => ecx, + lateout("ebx") ebx, + lateout("edx") _ + ); } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn add(a: i32, b: i32) -> i32 { a + b } -fn main() { - assert_eq!(add(3, 14159), 14162) +println!( + "L1 Cache: {}", + ((ebx >> 22) + 1) * (((ebx >> 12) & 0x3ff) + 1) * ((ebx & 0xfff) + 1) * (ecx + 1) +); +``` + +In the example above we use the `cpuid` instruction to get the L1 cache size. +This instruction writes to `eax`, `ebx`, `ecx`, and `edx`, but for the cache size we only care about the contents of `ebx` and `ecx`. + +However we still need to tell the compiler that `eax` and `edx` have been modified so that it can save any values that were in these registers before the asm. This is done by declaring these as outputs but with `_` instead of a variable name, which indicates that the output value is to be discarded. + +This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code: + +```rust,allow_fail +# #![feature(asm)] +// Multiply x by 6 using shifts and adds +let mut x: u64 = 4; +unsafe { + asm!(" + mov {tmp}, {x} + shl {tmp}, 1 + shl {x}, 2 + add {x}, {tmp} + ", x = inout(reg) x, tmp = out(reg) _); } +assert_eq!(x, 4 * 6); ``` -If you would like to use real operands in this position, however, -you are required to put curly braces `{}` around the register that -you want, and you are required to put the specific size of the -operand. This is useful for very low level programming, where -which register you use is important: +## Symbol operands + +A special operand type, `sym`, allows you to use the symbol name of a `fn` or `static` in inline assembly code. +This allows you to call a function or access a global variable without needing to keep its address in a register. -```rust +```rust,allow_fail # #![feature(asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# unsafe fn read_byte_in(port: u16) -> u8 { -let result: u8; -asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port)); -result -# } +extern "C" fn foo(arg: i32) { + println!("arg = {}", arg); +} + +fn call_foo(arg: i32) { + unsafe { + asm!( + "call {}", + sym foo, + // 1st argument in rdi, which is caller-saved + inout("rdi") arg => _, + // All caller-saved registers must be marked as clobberred + out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _, + out("r8") _, out("r9") _, out("r10") _, out("r11") _, + out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _, + out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _, + out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _, + out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _, + ) + } +} ``` -## Clobbers +Note that the `fn` or `static` item does not need to be public or `#[no_mangle]`: +the compiler will automatically insert the appropriate mangled symbol name into the assembly code. + +## Register template modifiers -Some instructions modify registers which might otherwise have held -different values so we use the clobbers list to indicate to the -compiler not to assume any values loaded into those registers will -stay valid. +In some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a "view" over a subset of the register (e.g. the low 32 bits of a 64-bit register). -```rust +By default the compiler will always choose the name that refers to the full register size (e.g. `rax` on x86-64, `eax` on x86, etc). + +This default can be overriden by using modifiers on the template string operands, just like you would with format strings: + +```rust,allow_fail # #![feature(asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { unsafe { -// Put the value 0x200 in eax: -asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "eax"); -# } } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} +let mut x: u16 = 0xab; + +unsafe { + asm!("mov {0:h}, {0:l}", inout(reg_abcd) x); +} + +assert_eq!(x, 0xabab); ``` -Input and output registers need not be listed since that information -is already communicated by the given constraints. Otherwise, any other -registers used either implicitly or explicitly should be listed. +In this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 register (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently. -If the assembly changes the condition code register `cc` should be -specified as one of the clobbers. Similarly, if the assembly modifies -memory, `memory` should also be specified. +Let us assume that the register allocator has chosen to allocate `x` in the `ax` register. +The `h` modifier will emit the register name for the high byte of that register and the `l` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte. -## Options +If you use a smaller data type (e.g. `u16`) with an operand and forget the use template modifiers, the compiler will emit a warning and suggest the correct modifier to use. -The last section, `options` is specific to Rust. The format is comma -separated literal strings (i.e. `:"foo", "bar", "baz"`). It's used to -specify some extra info about the inline assembly: +## Options -Current valid options are: +By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. -1. *volatile* - specifying this is analogous to - `__asm__ __volatile__ (...)` in gcc/clang. -2. *alignstack* - certain instructions expect the stack to be - aligned a certain way (i.e. SSE) and specifying this indicates to - the compiler to insert its usual stack alignment code -3. *intel* - use intel syntax instead of the default AT&T. +Let's take our previous example of an `add` instruction: -```rust +```rust,allow_fail # #![feature(asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { -let result: i32; +let mut a: u64 = 4; +let b: u64 = 4; unsafe { - asm!("mov eax, 2" : "={eax}"(result) : : : "intel") + asm!( + "add {0}, {1}", + inlateout(reg) a, in(reg) b, + options(pure, nomem, nostack) + ); } -println!("eax is currently {}", result); -# } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} +assert_eq!(a, 8); ``` -## More Information +Options can be provided as an optional final argument to the `asm!` macro. We specified three options here: +- `pure` means that the asm code has no observable side effects and that its output depends only on its inputs. This allows the compiler optimizer to call the inline asm fewer times or even eliminate it entirely. +- `nomem` means that the asm code does not read or write to memory. By default the compiler will assume that inline assembly can read or write any memory address that is accessible to it (e.g. through a pointer passed as an operand, or a global). +- `nostack` means that the asm code does not push any data onto the stack. This allows the compiler to use optimizations such as the stack red zone on x86-64 to avoid stack pointer adjustments. + +These allow the compiler to better optimize code using `asm!`, for example by eliminating pure `asm!` blocks whose outputs are not needed. + +See the reference for the full list of available options and their effects. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation -The current implementation of the `asm!` macro is a direct binding to [LLVM's -inline assembler expressions][llvm-docs], so be sure to check out [their -documentation as well][llvm-docs] for more information about clobbers, -constraints, etc. +Inline assembler is implemented as an unsafe macro `asm!()`. +The first argument to this macro is a template string literal used to build the final assembly. +The following arguments specify input and output operands. +When required, options are specified as the final argument. -[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions +The following ABNF specifies the general syntax: + +```ignore +dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout" +reg_spec := / "" +operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_" +reg_operand := dir_spec "(" reg_spec ")" operand_expr +operand := reg_operand / "const" const_expr / "sym" path +option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "att_syntax" +options := "options(" option *["," option] [","] ")" +asm := "asm!(" format_string *("," [ident "="] operand) ["," options] [","] ")" +``` + +The macro will initially be supported only on ARM, AArch64, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target. + +[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax + +## Template string + +The assembler template uses the same syntax as [format strings][format-syntax] (i.e. placeholders are specified by curly braces). The corresponding arguments are accessed in order, by index, or by name. However, implicit named arguments (introduced by [RFC #2795][rfc-2795]) are not supported. + +As with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after any named arguments if any. Explicit register operands cannot be used by placeholders in the template string. All other operands must appear at least once in the template string, otherwise a compiler error is generated. + +The exact assembly code syntax is target-specific and opaque to the compiler except for the way operands are substituted into the template string to form the code passed to the assembler. + +The 4 targets specified in this RFC (x86, ARM, AArch64, RISC-V) all use the assembly code syntax of the GNU assembler (GAS). On x86, the `.intel_syntax noprefix` mode of GAS is used by default. On ARM, the `.syntax unified` mode is used. These targets impose an additional restriction on the assembly code: any assembler state (e.g. the current section which can be changed with `.section`) must be restored to its original value at the end of the asm string. Assembly code that does not conform to the GAS syntax will result in assembler-specific behavior. + +[rfc-2795]: https://github.com/rust-lang/rfcs/pull/2795 + +## Operand type + +Several types of operands are supported: + +* `in() ` + - `` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string. + - The allocated register will contain the value of `` at the start of the asm code. + - The allocated register must contain the same value at the end of the asm code (except if a `lateout` is allocated to the same register). +* `out() ` + - `` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string. + - The allocated register will contain an undefined value at the start of the asm code. + - `` must be a (possibly uninitialized) place expression, to which the contents of the allocated register is written to at the end of the asm code. + - An underscore (`_`) may be specified instead of an expression, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber). +* `lateout() ` + - Identical to `out` except that the register allocator can reuse a register allocated to an `in`. + - You should only write to the register after all inputs are read, otherwise you may clobber an input. +* `inout() ` + - `` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string. + - The allocated register will contain the value of `` at the start of the asm code. + - `` must be a mutable initialized place expression, to which the contents of the allocated register is written to at the end of the asm code. +* `inout() => ` + - Same as `inout` except that the initial value of the register is taken from the value of ``. + - `` must be a (possibly uninitialized) place expression, to which the contents of the allocated register is written to at the end of the asm code. + - An underscore (`_`) may be specified instead of an expression for ``, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber). + - `` and `` may have different types. +* `inlateout() ` / `inlateout() => ` + - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`). + - You should only write to the register after all inputs are read, otherwise you may clobber an input. +* `const ` + - `` must be an integer or floating-point constant expression. + - The value of the expression is formatted as a string and substituted directly into the asm template string. +* `sym ` + - `` must refer to a `fn` or `static`. + - A mangled symbol name referring to the item is substituted into the asm template string. + - The substituted string does not include any modifiers (e.g. GOT, PLT, relocations, etc). + - `` is allowed to point to a `#[thread_local]` static, in which case the asm code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data. + +Operand expressions are evaluated from left to right, just like function call arguments. After the `asm!` has executed, outputs are written to in left to right order. This is significant if two outputs point to the same place: that place will contain the value of the rightmost output. + +## Register operands + +Input and output operands can be specified either as an explicit register or as a register class from which the register allocator can select a register. Explicit registers are specified as string literals (e.g. `"eax"`) while register classes are specified as identifiers (e.g. `reg`). Using string literals for register names enables support for architectures that use special characters in register names, such as MIPS (`$0`, `$1`, etc). + +Note that explicit registers treat register aliases (e.g. `r14` vs `lr` on ARM) and smaller views of a register (e.g. `eax` vs `rax`) as equivalent to the base register. It is a compile-time error to use the same explicit register for two input operands or two output operands. Additionally, it is also a compile-time error to use overlapping registers (e.g. ARM VFP) in input operands or in output operands. + +Only the following types are allowed as operands for inline assembly: +- Integers (signed and unsigned) +- Floating-point numbers +- Pointers (thin only) +- Function pointers +- SIMD vectors (structs defined with `#[repr(simd)]` and which implement `Copy`). This includes architecture-specific vector types defined in `std::arch` such as `__m128` (x86) or `int8x16_t` (ARM). + +Here is the list of currently supported register classes: + +| Architecture | Register class | Registers | LLVM constraint code | +| ------------ | -------------- | --------- | -------------------- | +| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` | +| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` | +| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` | +| x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\*, `bh`\*, `ch`\*, `dh`\* | `q` | +| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` | +| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | +| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | +| x86 | `kreg` | `k[1-7]` | `Yk` | +| AArch64 | `reg` | `x[0-28]`, `x30` | `r` | +| AArch64 | `vreg` | `v[0-31]` | `w` | +| AArch64 | `vreg_low16` | `v[0-15]` | `x` | +| ARM | `reg` | `r[0-r10]`, `r12`, `r14` | `r` | +| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` | +| ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` | +| ARM | `sreg` | `s[0-31]` | `t` | +| ARM | `sreg_low16` | `s[0-15]` | `x` | +| ARM | `dreg` | `d[0-31]` | `w` | +| ARM | `dreg_low16` | `d[0-15]` | `t` | +| ARM | `dreg_low8` | `d[0-8]` | `x` | +| ARM | `qreg` | `q[0-15]` | `w` | +| ARM | `qreg_low8` | `q[0-7]` | `t` | +| ARM | `qreg_low4` | `q[0-3]` | `x` | +| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | +| RISC-V | `freg` | `f[0-31]` | `f` | + +> **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. +> +> Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register. + +Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc). + +Each register class has constraints on which value types they can be used with. This is necessary because the way a value is loaded into a register depends on its type. For example, on big-endian systems, loading a `i32x4` and a `i8x16` into a SIMD register may result in different register contents even if the byte-wise memory representation of both values is identical. The availability of supported types for a particular register class may depend on what target features are currently enabled. + +| Architecture | Register class | Target feature | Allowed types | +| ------------ | -------------- | -------------- | ------------- | +| x86-32 | `reg` | None | `i16`, `i32`, `f32` | +| x86-64 | `reg` | None | `i16`, `i32`, `f32`, `i64`, `f64` | +| x86 | `reg_byte` | None | `i8` | +| x86 | `xmm_reg` | `sse` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| x86 | `ymm_reg` | `avx` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | +| x86 | `zmm_reg` | `avx512f` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4`
`i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` | +| x86 | `kreg` | `axv512f` | `i8`, `i16` | +| x86 | `kreg` | `axv512bw` | `i32`, `i64` | +| AArch64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | +| AArch64 | `vreg` | `fp` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| ARM | `reg` | None | `i8`, `i16`, `i32`, `f32` | +| ARM | `sreg` | `vfp2` | `i32`, `f32` | +| ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | +| ARM | `qreg` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | +| RISC-V32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | +| RISC-V64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | +| RISC-V | `freg` | `f` | `f32` | +| RISC-V | `freg` | `d` | `f64` | + +> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). + +If a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. The only exception is the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture. + +When separate input and output expressions are specified for an `inout` operand, both expressions must have the same type. The only exception is if both operands are pointers or integers, in which case they are only required to have the same size. This restriction exists because the register allocators in LLVM and GCC sometimes cannot handle tied operands with different types. + +## Register names + +Some registers have multiple names. These are all treated by the compiler as identical to the base register name. Here is the list of all supported register aliases: + +| Architecture | Base register | Aliases | +| ------------ | ------------- | ------- | +| x86 | `ax` | `eax`, `rax` | +| x86 | `bx` | `ebx`, `rbx` | +| x86 | `cx` | `ecx`, `rcx` | +| x86 | `dx` | `edx`, `rdx` | +| x86 | `si` | `esi`, `rsi` | +| x86 | `di` | `edi`, `rdi` | +| x86 | `bp` | `bpl`, `ebp`, `rbp` | +| x86 | `sp` | `spl`, `esp`, `rsp` | +| x86 | `ip` | `eip`, `rip` | +| x86 | `st(0)` | `st` | +| x86 | `r[8-15]` | `r[8-15]b`, `r[8-15]w`, `r[8-15]d` | +| x86 | `xmm[0-31]` | `ymm[0-31]`, `zmm[0-31]` | +| AArch64 | `x[0-30]` | `w[0-30]` | +| AArch64 | `x29` | `fp` | +| AArch64 | `x30` | `lr` | +| AArch64 | `sp` | `wsp` | +| AArch64 | `xzr` | `wzr` | +| AArch64 | `v[0-31]` | `b[0-31]`, `h[0-31]`, `s[0-31]`, `d[0-31]`, `q[0-31]` | +| ARM | `r[0-3]` | `a[1-4]` | +| ARM | `r[4-9]` | `v[1-6]` | +| ARM | `r9` | `rfp` | +| ARM | `r10` | `sl` | +| ARM | `r11` | `fp` | +| ARM | `r12` | `ip` | +| ARM | `r13` | `sp` | +| ARM | `r14` | `lr` | +| ARM | `r15` | `pc` | +| RISC-V | `x0` | `zero` | +| RISC-V | `x1` | `ra` | +| RISC-V | `x2` | `sp` | +| RISC-V | `x3` | `gp` | +| RISC-V | `x4` | `tp` | +| RISC-V | `x[5-7]` | `t[0-2]` | +| RISC-V | `x8` | `fp`, `s0` | +| RISC-V | `x9` | `s1` | +| RISC-V | `x[10-17]` | `a[0-7]` | +| RISC-V | `x[18-27]` | `s[2-11]` | +| RISC-V | `x[28-31]` | `t[3-6]` | +| RISC-V | `f[0-7]` | `ft[0-7]` | +| RISC-V | `f[8-9]` | `fs[0-1]` | +| RISC-V | `f[10-17]` | `fa[0-7]` | +| RISC-V | `f[18-27]` | `fs[2-11]` | +| RISC-V | `f[28-31]` | `ft[8-11]` | + +Some registers cannot be used for input or output operands: + +| Architecture | Unsupported register | Reason | +| ------------ | -------------------- | ------ | +| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | +| All | `bp` (x86), `r11` (ARM), `x29` (AArch64), `x8` (RISC-V) | The frame pointer cannot be used as an input or output. | +| x86 | `k0` | This is a constant zero register which can't be modified. | +| x86 | `ip` | This is the program counter, not a real register. | +| x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). | +| x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). | +| AArch64 | `xzr` | This is a constant zero register which can't be modified. | +| ARM | `pc` | This is the program counter, not a real register. | +| RISC-V | `x0` | This is a constant zero register which can't be modified. | +| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | + +## Template modifiers + +The placeholders can be augmented by modifiers which are specified after the `:` in the curly braces. These modifiers do not affect register allocation, but change the way operands are formatted when inserted into the template string. Only one modifier is allowed per template placeholder. + +The supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes. + +| Architecture | Register class | Modifier | Example output | LLVM modifier | +| ------------ | -------------- | -------- | -------------- | ------------- | +| x86-32 | `reg` | None | `eax` | `k` | +| x86-64 | `reg` | None | `rax` | `q` | +| x86-32 | `reg_abcd` | `l` | `al` | `b` | +| x86-64 | `reg` | `l` | `al` | `b` | +| x86 | `reg_abcd` | `h` | `ah` | `h` | +| x86 | `reg` | `x` | `ax` | `w` | +| x86 | `reg` | `e` | `eax` | `k` | +| x86-64 | `reg` | `r` | `rax` | `q` | +| x86 | `reg_byte` | None | `al` / `ah` | None | +| x86 | `xmm_reg` | None | `xmm0` | `x` | +| x86 | `ymm_reg` | None | `ymm0` | `t` | +| x86 | `zmm_reg` | None | `zmm0` | `g` | +| x86 | `*mm_reg` | `x` | `xmm0` | `x` | +| x86 | `*mm_reg` | `y` | `ymm0` | `t` | +| x86 | `*mm_reg` | `z` | `zmm0` | `g` | +| x86 | `kreg` | None | `k1` | None | +| AArch64 | `reg` | None | `x0` | `x` | +| AArch64 | `reg` | `w` | `w0` | `w` | +| AArch64 | `reg` | `x` | `x0` | `x` | +| AArch64 | `vreg` | None | `v0` | None | +| AArch64 | `vreg` | `v` | `v0` | None | +| AArch64 | `vreg` | `b` | `b0` | `b` | +| AArch64 | `vreg` | `h` | `h0` | `h` | +| AArch64 | `vreg` | `s` | `s0` | `s` | +| AArch64 | `vreg` | `d` | `d0` | `d` | +| AArch64 | `vreg` | `q` | `q0` | `q` | +| ARM | `reg` | None | `r0` | None | +| ARM | `sreg` | None | `s0` | None | +| ARM | `dreg` | None | `d0` | `P` | +| ARM | `qreg` | None | `q0` | `q` | +| ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` | +| RISC-V | `reg` | None | `x1` | None | +| RISC-V | `freg` | None | `f0` | None | + +> Notes: +> - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register. +> - on x86: our behavior for `reg` with no modifiers differs from what GCC does. GCC will infer the modifier based on the operand value type, while we default to the full register size. +> - on x86 `xmm_reg`: the `x`, `t` and `g` LLVM modifiers are not yet implemented in LLVM (they are supported by GCC only), but this should be a simple change. + +As stated in the previous section, passing an input value smaller than the register width will result in the upper bits of the register containing undefined values. This is not a problem if the inline asm only accesses the lower bits of the register, which can be done by using a template modifier to use a subregister name in the asm code (e.g. `ax` instead of `rax`). Since this an easy pitfall, the compiler will suggest a template modifier to use where appropriate given the input type. If all references to an operand already have modifiers then the warning is suppressed for that operand. + +[llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers + +## Options -If you need more power and don't mind losing some of the niceties of -`asm!`, check out [global_asm](global-asm.md). +Flags are used to further influence the behavior of the inline assembly block. +Currently the following options are defined: +- `pure`: The `asm` block has no side effects, and its outputs depend only on its direct inputs (i.e. the values themselves, not what they point to) or values read from memory (unless the `nomem` options is also set). This allows the compiler to execute the `asm` block fewer times than specified in the program (e.g. by hoisting it out of a loop) or even eliminate it entirely if the outputs are not used. +- `nomem`: The `asm` blocks does not read or write to any memory. This allows the compiler to cache the values of modified global variables in registers across the `asm` block since it knows that they are not read or written to by the `asm`. +- `readonly`: The `asm` block does not write to any memory. This allows the compiler to cache the values of unmodified global variables in registers across the `asm` block since it knows that they are not written to by the `asm`. +- `preserves_flags`: The `asm` block does not modify the flags register (defined in the rules below). This allows the compiler to avoid recomputing the condition flags after the `asm` block. +- `noreturn`: The `asm` block never returns, and its return type is defined as `!` (never). Behavior is undefined if execution falls through past the end of the asm code. A `noreturn` asm block behaves just like a function which doesn't return; notably, local variables in scope are not dropped before it is invoked. +- `nostack`: The `asm` block does not push data to the stack, or write to the stack red-zone (if supported by the target). If this option is *not* used then the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call. +- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler. Register operands are substituted in with a leading `%`. + +The compiler performs some additional checks on options: +- The `nomem` and `readonly` options are mutually exclusive: it is a compile-time error to specify both. +- The `pure` option must be combined with either the `nomem` or `readonly` options, otherwise a compile-time error is emitted. +- It is a compile-time error to specify `pure` on an asm block with no outputs or only discarded outputs (`_`). +- It is a compile-time error to specify `noreturn` on an asm block with outputs. + +## Rules for inline assembly + +- Any registers not specified as inputs will contain an undefined value on entry to the asm block. + - An "undefined value" in the context of inline assembly means that the register can (non-deterministically) have any one of the possible values allowed by the architecture. Notably it is not the same as an LLVM `undef` which can have a different value every time you read it (since such a concept does not exist in assembly code). +- Any registers not specified as outputs must have the same value upon exiting the asm block as they had on entry, otherwise behavior is undefined. + - This only applies to registers which can be specified as an input or output. Other registers follow target-specific rules. + - Note that a `lateout` may be allocated to the same register as an `in`, in which case this rule does not apply. Code should not rely on this however since it depends on the results of register allocation. +- Behavior is undefined if execution unwinds out of an asm block. + - This also applies if the assembly code calls a function which then unwinds. +- The set of memory locations that assembly code is allowed the read and write are the same as those allowed for an FFI function. + - Refer to the unsafe code guidelines for the exact rules. + - If the `readonly` option is set, then only memory reads are allowed. + - If the `nomem` option is set then no reads or writes to memory are allowed. + - These rules do not apply to memory which is private to the asm code, such as stack space allocated within the asm block. +- The compiler cannot assume that the instructions in the asm are the ones that will actually end up executed. + - This effectively means that the compiler must treat the `asm!` as a black box and only take the interface specification into account, not the instructions themselves. + - Runtime code patching is allowed, via target-specific mechanisms (outside the scope of this RFC). +- Unless the `nostack` option is set, asm code is allowed to use stack space below the stack pointer. + - On entry to the asm block the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call. + - You are responsible for making sure you don't overflow the stack (e.g. use stack probing to ensure you hit a guard page). + - You should adjust the stack pointer when allocating stack memory as required by the target ABI. + - The stack pointer must be restored to its original value before leaving the asm block. +- If the `noreturn` option is set then behavior is undefined if execution falls through to the end of the asm block. +- If the `pure` option is set then behavior is undefined if the `asm` has side-effects other than its direct outputs. Behavior is also undefined if two executions of the `asm` code with the same inputs result in different outputs. + - When used with the `nomem` option, "inputs" are just the direct inputs of the `asm!`. + - When used with the `readonly` option, "inputs" comprise the direct inputs of the `asm!` and any memory that the `asm!` block is allowed to read. +- These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: + - x86 + - Status flags in `EFLAGS` (CF, PF, AF, ZF, SF, OF). + - Floating-point status word (all). + - Floating-point exception flags in `MXCSR` (PE, UE, OE, ZE, DE, IE). + - ARM + - Condition flags in `CPSR` (N, Z, C, V) + - Saturation flag in `CPSR` (Q) + - Greater than or equal flags in `CPSR` (GE). + - Condition flags in `FPSCR` (N, Z, C, V) + - Saturation flag in `FPSCR` (QC) + - Floating-point exception flags in `FPSCR` (IDC, IXC, UFC, OFC, DZC, IOC). + - AArch64 + - Condition flags (`NZCV` register). + - Floating-point status (`FPSR` register). + - RISC-V + - Floating-point exception flags in `fcsr` (`fflags`). +- On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit. + - Behavior is undefined if the direction flag is set on exiting an asm block. +- The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block. + - This means that `asm!` blocks that never return (even if not marked `noreturn`) don't need to preserve these registers. + - When returning to a different `asm!` block than you entered (e.g. for context switching), these registers must contain the value they had upon entering the `asm!` block that you are *exiting*. + - You cannot exit an `asm!` block that has not been entered. Neither can you exit an `asm!` block that has already been exited. + - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds). + - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited. +- You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places. + - As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. + +> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. + +[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels diff --git a/src/doc/unstable-book/src/library-features/llvm-asm.md b/src/doc/unstable-book/src/library-features/llvm-asm.md new file mode 100644 index 0000000000000..da01d9228f14e --- /dev/null +++ b/src/doc/unstable-book/src/library-features/llvm-asm.md @@ -0,0 +1,193 @@ +# `llvm_asm` + +The tracking issue for this feature is: [#70173] + +[#70173]: https://github.com/rust-lang/rust/issues/70173 + +------------------------ + +For extremely low-level manipulations and performance reasons, one +might wish to control the CPU directly. Rust supports using inline +assembly to do this via the `llvm_asm!` macro. + +```rust,ignore +llvm_asm!(assembly template + : output operands + : input operands + : clobbers + : options + ); +``` + +Any use of `llvm_asm` is feature gated (requires `#![feature(llvm_asm)]` on the +crate to allow) and of course requires an `unsafe` block. + +> **Note**: the examples here are given in x86/x86-64 assembly, but +> all platforms are supported. + +## Assembly template + +The `assembly template` is the only required parameter and must be a +literal string (i.e. `""`) + +```rust +#![feature(llvm_asm)] + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn foo() { + unsafe { + llvm_asm!("NOP"); + } +} + +// Other platforms: +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn foo() { /* ... */ } + +fn main() { + // ... + foo(); + // ... +} +``` + +(The `feature(llvm_asm)` and `#[cfg]`s are omitted from now on.) + +Output operands, input operands, clobbers and options are all optional +but you must add the right number of `:` if you skip them: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { unsafe { +llvm_asm!("xor %eax, %eax" + : + : + : "eax" + ); +# } } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +Whitespace also doesn't matter: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { unsafe { +llvm_asm!("xor %eax, %eax" ::: "eax"); +# } } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +## Operands + +Input and output operands follow the same format: `: +"constraints1"(expr1), "constraints2"(expr2), ..."`. Output operand +expressions must be mutable place, or not yet assigned: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn add(a: i32, b: i32) -> i32 { + let c: i32; + unsafe { + llvm_asm!("add $2, $0" + : "=r"(c) + : "0"(a), "r"(b) + ); + } + c +} +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn add(a: i32, b: i32) -> i32 { a + b } + +fn main() { + assert_eq!(add(3, 14159), 14162) +} +``` + +If you would like to use real operands in this position, however, +you are required to put curly braces `{}` around the register that +you want, and you are required to put the specific size of the +operand. This is useful for very low level programming, where +which register you use is important: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# unsafe fn read_byte_in(port: u16) -> u8 { +let result: u8; +llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port)); +result +# } +``` + +## Clobbers + +Some instructions modify registers which might otherwise have held +different values so we use the clobbers list to indicate to the +compiler not to assume any values loaded into those registers will +stay valid. + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { unsafe { +// Put the value 0x200 in eax: +llvm_asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "eax"); +# } } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +Input and output registers need not be listed since that information +is already communicated by the given constraints. Otherwise, any other +registers used either implicitly or explicitly should be listed. + +If the assembly changes the condition code register `cc` should be +specified as one of the clobbers. Similarly, if the assembly modifies +memory, `memory` should also be specified. + +## Options + +The last section, `options` is specific to Rust. The format is comma +separated literal strings (i.e. `:"foo", "bar", "baz"`). It's used to +specify some extra info about the inline assembly: + +Current valid options are: + +1. *volatile* - specifying this is analogous to + `__asm__ __volatile__ (...)` in gcc/clang. +2. *alignstack* - certain instructions expect the stack to be + aligned a certain way (i.e. SSE) and specifying this indicates to + the compiler to insert its usual stack alignment code +3. *intel* - use intel syntax instead of the default AT&T. + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { +let result: i32; +unsafe { + llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel") +} +println!("eax is currently {}", result); +# } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +## More Information + +The current implementation of the `llvm_asm!` macro is a direct binding to [LLVM's +inline assembler expressions][llvm-docs], so be sure to check out [their +documentation as well][llvm-docs] for more information about clobbers, +constraints, etc. + +[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions + +If you need more power and don't mind losing some of the niceties of +`llvm_asm!`, check out [global_asm](global-asm.md). diff --git a/src/etc/dec2flt_table.py b/src/etc/dec2flt_table.py index 4979882ffeaff..9bbcaf7c4cc49 100755 --- a/src/etc/dec2flt_table.py +++ b/src/etc/dec2flt_table.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 """ Generate powers of ten using William Clinger's ``AlgorithmM`` for use in diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py index 51dfc3fff3f92..d580329cb504e 100755 --- a/src/etc/gdb_rust_pretty_printing.py +++ b/src/etc/gdb_rust_pretty_printing.py @@ -374,7 +374,7 @@ def children(self): innermap = GdbValue(self.__val.get_wrapped_value()['map']) if innermap.get_wrapped_value()['length'] > 0: root = GdbValue(innermap.get_wrapped_value()['root']) - type_name = str(root.type.ty.name).replace('core::option::Option<', '')[:-1] + type_name = str(root.type.ty.name).replace('core::option::Option<', '', 1)[:-1] root = root.get_wrapped_value().cast(gdb.lookup_type(type_name)) node_ptr = root['node'] i = 0 @@ -398,7 +398,7 @@ def to_string(self): def children(self): if self.__val.get_wrapped_value()['length'] > 0: root = GdbValue(self.__val.get_wrapped_value()['root']) - type_name = str(root.type.ty.name).replace('core::option::Option<', '')[:-1] + type_name = str(root.type.ty.name).replace('core::option::Option<', '', 1)[:-1] root = root.get_wrapped_value().cast(gdb.lookup_type(type_name)) node_ptr = root['node'] i = 0 diff --git a/src/etc/generate-deriving-span-tests.py b/src/etc/generate-deriving-span-tests.py index c42f942c63cf5..a0ba47e1dbe31 100755 --- a/src/etc/generate-deriving-span-tests.py +++ b/src/etc/generate-deriving-span-tests.py @@ -15,9 +15,6 @@ os.path.join(os.path.dirname(__file__), '../test/ui/derives/')) TEMPLATE = """\ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' {error_deriving} diff --git a/src/etc/lldb_batchmode.py b/src/etc/lldb_batchmode.py index d9c4bc5562f00..629c8e04ec533 100644 --- a/src/etc/lldb_batchmode.py +++ b/src/etc/lldb_batchmode.py @@ -139,11 +139,17 @@ def listen(): def start_watchdog(): """Starts a watchdog thread that will terminate the process after a certain period of time""" - watchdog_start_time = time.clock() + + try: + from time import clock + except ImportError: + from time import perf_counter as clock + + watchdog_start_time = clock() watchdog_max_time = watchdog_start_time + 30 def watchdog(): - while time.clock() < watchdog_max_time: + while clock() < watchdog_max_time: time.sleep(1) print("TIMEOUT: lldb_batchmode.py has been running for too long. Aborting!") thread.interrupt_main() diff --git a/src/etc/rust-gdb b/src/etc/rust-gdb index 23ba93da8e529..b950cea79edfe 100755 --- a/src/etc/rust-gdb +++ b/src/etc/rust-gdb @@ -2,8 +2,16 @@ # Exit if anything fails set -e +# Prefer rustc in the same directory as this script +DIR="$(dirname "$0")" +if [ -x "$DIR/rustc" ]; then + RUSTC="$DIR/rustc" +else + RUSTC="rustc" +fi + # Find out where the pretty printer Python module is -RUSTC_SYSROOT=`rustc --print=sysroot` +RUSTC_SYSROOT="$("$RUSTC" --print=sysroot)" GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc" # Run GDB with the additional arguments that load the pretty printers diff --git a/src/etc/rust-gdbgui b/src/etc/rust-gdbgui index 08d598cde1c3d..9744913b68650 100755 --- a/src/etc/rust-gdbgui +++ b/src/etc/rust-gdbgui @@ -31,8 +31,16 @@ icon to start your program running. exit 0 fi +# Prefer rustc in the same directory as this script +DIR="$(dirname "$0")" +if [ -x "$DIR/rustc" ]; then + RUSTC="$DIR/rustc" +else + RUSTC="rustc" +fi + # Find out where the pretty printer Python module is -RUSTC_SYSROOT=`rustc --print=sysroot` +RUSTC_SYSROOT="$("$RUSTC" --print=sysroot)" GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc" # Set the environment variable `RUST_GDB` to overwrite the call to a diff --git a/src/etc/test-float-parse/runtests.py b/src/etc/test-float-parse/runtests.py index 852bc77589616..fe6fd45f9a5f8 100644 --- a/src/etc/test-float-parse/runtests.py +++ b/src/etc/test-float-parse/runtests.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 """ Testing dec2flt diff --git a/src/etc/test-float-parse/u64-pow2.rs b/src/etc/test-float-parse/u64-pow2.rs index 1c9bda948ffd3..7e67e2b1246ef 100644 --- a/src/etc/test-float-parse/u64-pow2.rs +++ b/src/etc/test-float-parse/u64-pow2.rs @@ -1,7 +1,6 @@ mod _common; use _common::validate; -use std::u64; fn main() { for exp in 19..64 { diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 9f82b2c6fa66d..d31c73cc1bd8d 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -2,9 +2,8 @@ #![stable(feature = "alloc_module", since = "1.28.0")] -use core::intrinsics::{min_align_of_val, size_of_val}; +use core::intrinsics::{self, min_align_of_val, size_of_val}; use core::ptr::{NonNull, Unique}; -use core::usize; #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] @@ -165,11 +164,19 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for Global { #[inline] - fn alloc(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { - if layout.size() == 0 { - Ok((layout.dangling(), 0)) - } else { - unsafe { NonNull::new(alloc(layout)).ok_or(AllocErr).map(|p| (p, layout.size())) } + fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { + unsafe { + let size = layout.size(); + if size == 0 { + Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) + } else { + let raw_ptr = match init { + AllocInit::Uninitialized => alloc(layout), + AllocInit::Zeroed => alloc_zeroed(layout), + }; + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + Ok(MemoryBlock { ptr, size }) + } } } @@ -181,32 +188,71 @@ unsafe impl AllocRef for Global { } #[inline] - unsafe fn realloc( + unsafe fn grow( &mut self, ptr: NonNull, layout: Layout, new_size: usize, - ) -> Result<(NonNull, usize), AllocErr> { - match (layout.size(), new_size) { - (0, 0) => Ok((layout.dangling(), 0)), - (0, _) => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())), - (_, 0) => { - self.dealloc(ptr, layout); - Ok((layout.dangling(), 0)) + placement: ReallocPlacement, + init: AllocInit, + ) -> Result { + let size = layout.size(); + debug_assert!( + new_size >= size, + "`new_size` must be greater than or equal to `memory.size()`" + ); + + if size == new_size { + return Ok(MemoryBlock { ptr, size }); + } + + match placement { + ReallocPlacement::InPlace => Err(AllocErr), + ReallocPlacement::MayMove if layout.size() == 0 => { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + self.alloc(new_layout, init) + } + ReallocPlacement::MayMove => { + // `realloc` probably checks for `new_size > size` or something similar. + intrinsics::assume(new_size > size); + let ptr = realloc(ptr.as_ptr(), layout, new_size); + let memory = + MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }; + init.init_offset(memory, size); + Ok(memory) } - (_, _) => NonNull::new(realloc(ptr.as_ptr(), layout, new_size)) - .ok_or(AllocErr) - .map(|p| (p, new_size)), } } #[inline] - fn alloc_zeroed(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { - if layout.size() == 0 { - Ok((layout.dangling(), 0)) - } else { - unsafe { - NonNull::new(alloc_zeroed(layout)).ok_or(AllocErr).map(|p| (p, layout.size())) + unsafe fn shrink( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + ) -> Result { + let size = layout.size(); + debug_assert!( + new_size <= size, + "`new_size` must be smaller than or equal to `memory.size()`" + ); + + if size == new_size { + return Ok(MemoryBlock { ptr, size }); + } + + match placement { + ReallocPlacement::InPlace => Err(AllocErr), + ReallocPlacement::MayMove if new_size == 0 => { + self.dealloc(ptr, layout); + Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) + } + ReallocPlacement::MayMove => { + // `realloc` probably checks for `new_size < size` or something similar. + intrinsics::assume(new_size < size); + let ptr = realloc(ptr.as_ptr(), layout, new_size); + Ok(MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }) } } } @@ -218,14 +264,10 @@ unsafe impl AllocRef for Global { #[lang = "exchange_malloc"] #[inline] unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { - if size == 0 { - align as *mut u8 - } else { - let layout = Layout::from_size_align_unchecked(size, align); - match Global.alloc(layout) { - Ok((ptr, _)) => ptr.as_ptr(), - Err(_) => handle_alloc_error(layout), - } + let layout = Layout::from_size_align_unchecked(size, align); + match Global.alloc(layout, AllocInit::Uninitialized) { + Ok(memory) => memory.ptr.as_ptr(), + Err(_) => handle_alloc_error(layout), } } @@ -239,11 +281,8 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { pub(crate) unsafe fn box_free(ptr: Unique) { let size = size_of_val(ptr.as_ref()); let align = min_align_of_val(ptr.as_ref()); - // We do not allocate for Box when T is ZST, so deallocation is also not necessary. - if size != 0 { - let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr.cast().into(), layout); - } + let layout = Layout::from_size_align_unchecked(size, align); + Global.dealloc(ptr.cast().into(), layout) } /// Abort on memory allocation error or failure. diff --git a/src/liballoc/alloc/tests.rs b/src/liballoc/alloc/tests.rs index 55944398e1677..1c003983df989 100644 --- a/src/liballoc/alloc/tests.rs +++ b/src/liballoc/alloc/tests.rs @@ -8,21 +8,22 @@ use test::Bencher; fn allocate_zeroed() { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); - let (ptr, _) = - Global.alloc_zeroed(layout.clone()).unwrap_or_else(|_| handle_alloc_error(layout)); + let memory = Global + .alloc(layout.clone(), AllocInit::Zeroed) + .unwrap_or_else(|_| handle_alloc_error(layout)); - let mut i = ptr.cast::().as_ptr(); + let mut i = memory.ptr.cast::().as_ptr(); let end = i.add(layout.size()); while i < end { assert_eq!(*i, 0); i = i.offset(1); } - Global.dealloc(ptr, layout); + Global.dealloc(memory.ptr, layout); } } #[bench] -#[cfg_attr(miri, ignore)] // Miri does not support benchmarks +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn alloc_owned_small(b: &mut Bencher) { b.iter(|| { let _: Box<_> = box 10; diff --git a/src/liballoc/benches/btree/map.rs b/src/liballoc/benches/btree/map.rs index 83cdebf0e3f4a..38d19c59ad186 100644 --- a/src/liballoc/benches/btree/map.rs +++ b/src/liballoc/benches/btree/map.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; use std::iter::Iterator; -use std::ops::Bound::{Excluded, Unbounded}; +use std::ops::RangeBounds; use std::vec::Vec; use rand::{seq::SliceRandom, thread_rng, Rng}; @@ -117,7 +117,7 @@ map_find_rand_bench! {find_rand_10_000, 10_000, BTreeMap} map_find_seq_bench! {find_seq_100, 100, BTreeMap} map_find_seq_bench! {find_seq_10_000, 10_000, BTreeMap} -fn bench_iter(b: &mut Bencher, size: i32) { +fn bench_iteration(b: &mut Bencher, size: i32) { let mut map = BTreeMap::::new(); let mut rng = thread_rng(); @@ -133,21 +133,21 @@ fn bench_iter(b: &mut Bencher, size: i32) { } #[bench] -pub fn iter_20(b: &mut Bencher) { - bench_iter(b, 20); +pub fn iteration_20(b: &mut Bencher) { + bench_iteration(b, 20); } #[bench] -pub fn iter_1000(b: &mut Bencher) { - bench_iter(b, 1000); +pub fn iteration_1000(b: &mut Bencher) { + bench_iteration(b, 1000); } #[bench] -pub fn iter_100000(b: &mut Bencher) { - bench_iter(b, 100000); +pub fn iteration_100000(b: &mut Bencher) { + bench_iteration(b, 100000); } -fn bench_iter_mut(b: &mut Bencher, size: i32) { +fn bench_iteration_mut(b: &mut Bencher, size: i32) { let mut map = BTreeMap::::new(); let mut rng = thread_rng(); @@ -163,18 +163,18 @@ fn bench_iter_mut(b: &mut Bencher, size: i32) { } #[bench] -pub fn iter_mut_20(b: &mut Bencher) { - bench_iter_mut(b, 20); +pub fn iteration_mut_20(b: &mut Bencher) { + bench_iteration_mut(b, 20); } #[bench] -pub fn iter_mut_1000(b: &mut Bencher) { - bench_iter_mut(b, 1000); +pub fn iteration_mut_1000(b: &mut Bencher) { + bench_iteration_mut(b, 1000); } #[bench] -pub fn iter_mut_100000(b: &mut Bencher) { - bench_iter_mut(b, 100000); +pub fn iteration_mut_100000(b: &mut Bencher) { + bench_iteration_mut(b, 100000); } fn bench_first_and_last(b: &mut Bencher, size: i32) { @@ -202,57 +202,83 @@ pub fn first_and_last_10k(b: &mut Bencher) { bench_first_and_last(b, 10_000); } -#[bench] -pub fn range_excluded_excluded(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); +const BENCH_RANGE_SIZE: i32 = 145; +const BENCH_RANGE_COUNT: i32 = BENCH_RANGE_SIZE * (BENCH_RANGE_SIZE - 1) / 2; + +fn bench_range(b: &mut Bencher, f: F) +where + F: Fn(i32, i32) -> R, + R: RangeBounds, +{ + let map: BTreeMap<_, _> = (0..BENCH_RANGE_SIZE).map(|i| (i, i)).collect(); b.iter(|| { - for first in 0..size { - for last in first + 1..size { - black_box(map.range((Excluded(first), Excluded(last)))); + let mut c = 0; + for i in 0..BENCH_RANGE_SIZE { + for j in i + 1..BENCH_RANGE_SIZE { + black_box(map.range(f(i, j))); + c += 1; } } + debug_assert_eq!(c, BENCH_RANGE_COUNT); }); } #[bench] -pub fn range_excluded_unbounded(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| { - for first in 0..size { - black_box(map.range((Excluded(first), Unbounded))); - } - }); +pub fn range_included_excluded(b: &mut Bencher) { + bench_range(b, |i, j| i..j); } #[bench] pub fn range_included_included(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| { - for first in 0..size { - for last in first..size { - black_box(map.range(first..=last)); - } - } - }); + bench_range(b, |i, j| i..=j); } #[bench] pub fn range_included_unbounded(b: &mut Bencher) { - let size = 144; + bench_range(b, |i, _| i..); +} + +#[bench] +pub fn range_unbounded_unbounded(b: &mut Bencher) { + bench_range(b, |_, _| ..); +} + +fn bench_iter(b: &mut Bencher, repeats: i32, size: i32) { let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); b.iter(|| { - for first in 0..size { - black_box(map.range(first..)); + for _ in 0..repeats { + black_box(map.iter()); } }); } +/// Contrast range_unbounded_unbounded with `iter()`. #[bench] -pub fn range_unbounded_unbounded(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| map.range(..)); +pub fn range_unbounded_vs_iter(b: &mut Bencher) { + bench_iter(b, BENCH_RANGE_COUNT, BENCH_RANGE_SIZE); +} + +#[bench] +pub fn iter_0(b: &mut Bencher) { + bench_iter(b, 1_000, 0); +} + +#[bench] +pub fn iter_1(b: &mut Bencher) { + bench_iter(b, 1_000, 1); +} + +#[bench] +pub fn iter_100(b: &mut Bencher) { + bench_iter(b, 1_000, 100); +} + +#[bench] +pub fn iter_10k(b: &mut Bencher) { + bench_iter(b, 1_000, 10_000); +} + +#[bench] +pub fn iter_1m(b: &mut Bencher) { + bench_iter(b, 1_000, 1_000_000); } diff --git a/src/liballoc/benches/btree/set.rs b/src/liballoc/benches/btree/set.rs index d9e75ab7fa4ef..2518506b9b5f3 100644 --- a/src/liballoc/benches/btree/set.rs +++ b/src/liballoc/benches/btree/set.rs @@ -62,6 +62,22 @@ pub fn clone_100_and_clear(b: &mut Bencher) { b.iter(|| src.clone().clear()) } +#[bench] +pub fn clone_100_and_drain_all(b: &mut Bencher) { + let src = pos(100); + b.iter(|| src.clone().drain_filter(|_| true).count()) +} + +#[bench] +pub fn clone_100_and_drain_half(b: &mut Bencher) { + let src = pos(100); + b.iter(|| { + let mut set = src.clone(); + assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 100 / 2); + assert_eq!(set.len(), 100 / 2); + }) +} + #[bench] pub fn clone_100_and_into_iter(b: &mut Bencher) { let src = pos(100); @@ -115,6 +131,22 @@ pub fn clone_10k_and_clear(b: &mut Bencher) { b.iter(|| src.clone().clear()) } +#[bench] +pub fn clone_10k_and_drain_all(b: &mut Bencher) { + let src = pos(10_000); + b.iter(|| src.clone().drain_filter(|_| true).count()) +} + +#[bench] +pub fn clone_10k_and_drain_half(b: &mut Bencher) { + let src = pos(10_000); + b.iter(|| { + let mut set = src.clone(); + assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(set.len(), 10_000 / 2); + }) +} + #[bench] pub fn clone_10k_and_into_iter(b: &mut Bencher) { let src = pos(10_000); diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs index 951477a24c8ed..f31717d9fd517 100644 --- a/src/liballoc/benches/lib.rs +++ b/src/liballoc/benches/lib.rs @@ -1,3 +1,4 @@ +#![feature(btree_drain_filter)] #![feature(map_first_last)] #![feature(repr_simd)] #![feature(test)] diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 36641284a769b..8ef6090c743a9 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -143,10 +143,10 @@ use core::ops::{ }; use core::pin::Pin; use core::ptr::{self, NonNull, Unique}; -use core::slice; use core::task::{Context, Poll}; -use crate::alloc::{self, AllocRef, Global}; +use crate::alloc::{self, AllocInit, AllocRef, Global}; +use crate::borrow::Cow; use crate::raw_vec::RawVec; use crate::str::from_boxed_utf8_unchecked; use crate::vec::Vec; @@ -196,14 +196,12 @@ impl Box { #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit() -> Box> { let layout = alloc::Layout::new::>(); - unsafe { - let ptr = if layout.size() == 0 { - NonNull::dangling() - } else { - Global.alloc(layout).unwrap_or_else(|_| alloc::handle_alloc_error(layout)).0.cast() - }; - Box::from_raw(ptr.as_ptr()) - } + let ptr = Global + .alloc(layout, AllocInit::Uninitialized) + .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) + .ptr + .cast(); + unsafe { Box::from_raw(ptr.as_ptr()) } } /// Constructs a new `Box` with uninitialized contents, with the memory @@ -226,11 +224,13 @@ impl Box { /// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_zeroed() -> Box> { - unsafe { - let mut uninit = Self::new_uninit(); - ptr::write_bytes::(uninit.as_mut_ptr(), 0, 1); - uninit - } + let layout = alloc::Layout::new::>(); + let ptr = Global + .alloc(layout, AllocInit::Zeroed) + .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) + .ptr + .cast(); + unsafe { Box::from_raw(ptr.as_ptr()) } } /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then @@ -240,6 +240,16 @@ impl Box { pub fn pin(x: T) -> Pin> { (box x).into() } + + /// Converts a `Box` into a `Box<[T]>` + /// + /// This conversion does not allocate on the heap and happens in place. + /// + #[unstable(feature = "box_into_boxed_slice", issue = "71582")] + pub fn into_boxed_slice(boxed: Box) -> Box<[T]> { + // *mut T and *mut [T; 1] have the same size and alignment + unsafe { Box::from_raw(Box::into_raw(boxed) as *mut [T; 1] as *mut [T]) } + } } impl Box<[T]> { @@ -265,15 +275,7 @@ impl Box<[T]> { /// ``` #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit]> { - let layout = alloc::Layout::array::>(len).unwrap(); - unsafe { - let ptr = if layout.size() == 0 { - NonNull::dangling() - } else { - Global.alloc(layout).unwrap_or_else(|_| alloc::handle_alloc_error(layout)).0.cast() - }; - Box::from_raw(slice::from_raw_parts_mut(ptr.as_ptr(), len)) - } + unsafe { RawVec::with_capacity(len).into_box(len) } } } @@ -437,7 +439,12 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Box) -> *mut T { - Box::into_raw_non_null(b).as_ptr() + // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a + // raw pointer for the type system. Turning it directly into a raw pointer would not be + // recognized as "releasing" the unique pointer to permit aliased raw accesses, + // so all raw pointer methods go through `leak` which creates a (unique) + // mutable reference. Turning *that* to a raw pointer behaves correctly. + Box::leak(b) as *mut T } /// Consumes the `Box`, returning the wrapped pointer as `NonNull`. @@ -460,6 +467,7 @@ impl Box { /// /// ``` /// #![feature(box_into_raw_non_null)] + /// #![allow(deprecated)] /// /// let x = Box::new(5); /// let ptr = Box::into_raw_non_null(x); @@ -469,24 +477,34 @@ impl Box { /// let x = unsafe { Box::from_raw(ptr.as_ptr()) }; /// ``` #[unstable(feature = "box_into_raw_non_null", issue = "47336")] + #[rustc_deprecated( + since = "1.44.0", + reason = "use `Box::leak(b).into()` or `NonNull::from(Box::leak(b))` instead" + )] #[inline] pub fn into_raw_non_null(b: Box) -> NonNull { - Box::into_unique(b).into() - } - - #[unstable(feature = "ptr_internals", issue = "none", reason = "use into_raw_non_null instead")] + // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a + // raw pointer for the type system. Turning it directly into a raw pointer would not be + // recognized as "releasing" the unique pointer to permit aliased raw accesses, + // so all raw pointer methods go through `leak` which creates a (unique) + // mutable reference. Turning *that* to a raw pointer behaves correctly. + Box::leak(b).into() + } + + #[unstable( + feature = "ptr_internals", + issue = "none", + reason = "use `Box::leak(b).into()` or `Unique::from(Box::leak(b))` instead" + )] #[inline] #[doc(hidden)] pub fn into_unique(b: Box) -> Unique { - let mut unique = b.0; - mem::forget(b); - // Box is kind-of a library type, but recognized as a "unique pointer" by - // Stacked Borrows. This function here corresponds to "reborrowing to - // a raw pointer", but there is no actual reborrow here -- so - // without some care, the pointer we are returning here still carries - // the tag of `b`, with `Unique` permission. - // We round-trip through a mutable reference to avoid that. - unsafe { Unique::new_unchecked(unique.as_mut() as *mut T) } + // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a + // raw pointer for the type system. Turning it directly into a raw pointer would not be + // recognized as "releasing" the unique pointer to permit aliased raw accesses, + // so all raw pointer methods go through `leak` which creates a (unique) + // mutable reference. Turning *that* to a raw pointer behaves correctly. + Box::leak(b).into() } /// Consumes and leaks the `Box`, returning a mutable reference, @@ -532,7 +550,7 @@ impl Box { where T: 'a, // Technically not needed, but kept to be explicit. { - unsafe { &mut *Box::into_raw(b) } + unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() } } /// Converts a `Box` into a `Pin>` @@ -778,7 +796,18 @@ impl From<&[T]> for Box<[T]> { let buf = RawVec::with_capacity(len); unsafe { ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len); - buf.into_box() + buf.into_box(slice.len()).assume_init() + } + } +} + +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box<[T]> { + #[inline] + fn from(cow: Cow<'_, [T]>) -> Box<[T]> { + match cow { + Cow::Borrowed(slice) => Box::from(slice), + Cow::Owned(slice) => Box::from(slice), } } } @@ -801,6 +830,17 @@ impl From<&str> for Box { } } +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + #[inline] + fn from(cow: Cow<'_, str>) -> Box { + match cow { + Cow::Borrowed(s) => Box::from(s), + Cow::Owned(s) => Box::from(s), + } + } +} + #[stable(feature = "boxed_str_conv", since = "1.19.0")] impl From> for Box<[u8]> { /// Converts a `Box>` into a `Box<[u8]>` diff --git a/src/liballoc/collections/binary_heap.rs b/src/liballoc/collections/binary_heap.rs index 9908a3049763a..a3ef998918433 100644 --- a/src/liballoc/collections/binary_heap.rs +++ b/src/liballoc/collections/binary_heap.rs @@ -1,10 +1,10 @@ //! A priority queue implemented with a binary heap. //! -//! Insertion and popping the largest element have `O(log n)` time complexity. +//! Insertion and popping the largest element have `O(log(n))` time complexity. //! Checking the largest element is `O(1)`. Converting a vector to a binary heap //! can be done in-place, and has `O(n)` complexity. A binary heap can also be -//! converted to a sorted vector in-place, allowing it to be used for an `O(n -//! log n)` in-place heapsort. +//! converted to a sorted vector in-place, allowing it to be used for an `O(n * log(n))` +//! in-place heapsort. //! //! # Examples //! @@ -20,7 +20,6 @@ //! ``` //! use std::cmp::Ordering; //! use std::collections::BinaryHeap; -//! use std::usize; //! //! #[derive(Copy, Clone, Eq, PartialEq)] //! struct State { @@ -234,9 +233,9 @@ use super::SpecExtend; /// /// # Time complexity /// -/// | [push] | [pop] | [peek]/[peek\_mut] | -/// |--------|----------|--------------------| -/// | O(1)~ | O(log n) | O(1) | +/// | [push] | [pop] | [peek]/[peek\_mut] | +/// |--------|-----------|--------------------| +/// | O(1)~ | O(log(n)) | O(1) | /// /// The value for `push` is an expected cost; the method documentation gives a /// more detailed analysis. @@ -399,7 +398,7 @@ impl BinaryHeap { /// /// # Time complexity /// - /// Cost is O(1) in the worst case. + /// Cost is `O(1)` in the worst case. #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] pub fn peek_mut(&mut self) -> Option> { if self.is_empty() { None } else { Some(PeekMut { heap: self, sift: true }) } @@ -423,8 +422,7 @@ impl BinaryHeap { /// /// # Time complexity /// - /// The worst case cost of `pop` on a heap containing *n* elements is O(log - /// n). + /// The worst case cost of `pop` on a heap containing *n* elements is `O(log(n))`. #[stable(feature = "rust1", since = "1.0.0")] pub fn pop(&mut self) -> Option { self.data.pop().map(|mut item| { @@ -457,15 +455,15 @@ impl BinaryHeap { /// /// The expected cost of `push`, averaged over every possible ordering of /// the elements being pushed, and over a sufficiently large number of - /// pushes, is O(1). This is the most meaningful cost metric when pushing + /// pushes, is `O(1)`. This is the most meaningful cost metric when pushing /// elements that are *not* already in any sorted pattern. /// /// The time complexity degrades if elements are pushed in predominantly /// ascending order. In the worst case, elements are pushed in ascending - /// sorted order and the amortized cost per push is O(log n) against a heap + /// sorted order and the amortized cost per push is `O(log(n))` against a heap /// containing *n* elements. /// - /// The worst case cost of a *single* call to `push` is O(n). The worst case + /// The worst case cost of a *single* call to `push` is `O(n)`. The worst case /// occurs when capacity is exhausted and needs a resize. The resize cost /// has been amortized in the previous figures. #[stable(feature = "rust1", since = "1.0.0")] @@ -624,7 +622,7 @@ impl BinaryHeap { // `rebuild` takes O(len1 + len2) operations // and about 2 * (len1 + len2) comparisons in the worst case - // while `extend` takes O(len2 * log_2(len1)) operations + // while `extend` takes O(len2 * log(len1)) operations // and about 1 * len2 * log_2(len1) comparisons in the worst case, // assuming len1 >= len2. #[inline] @@ -645,7 +643,7 @@ impl BinaryHeap { /// The remaining elements will be removed on drop in heap order. /// /// Note: - /// * `.drain_sorted()` is O(n lg n); much slower than `.drain()`. + /// * `.drain_sorted()` is `O(n * log(n))`; much slower than `.drain()`. /// You should use the latter for most cases. /// /// # Examples @@ -667,6 +665,34 @@ impl BinaryHeap { pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> { DrainSorted { inner: self } } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns + /// `false`. The elements are visited in unsorted (and unspecified) order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_retain)] + /// use std::collections::BinaryHeap; + /// + /// let mut heap = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); + /// + /// heap.retain(|x| x % 2 == 0); // only keep even numbers + /// + /// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4]) + /// ``` + #[unstable(feature = "binary_heap_retain", issue = "71503")] + pub fn retain(&mut self, f: F) + where + F: FnMut(&T) -> bool, + { + self.data.retain(f); + self.rebuild(); + } } impl BinaryHeap { @@ -730,7 +756,7 @@ impl BinaryHeap { /// /// # Time complexity /// - /// Cost is O(1) in the worst case. + /// Cost is `O(1)` in the worst case. #[stable(feature = "rust1", since = "1.0.0")] pub fn peek(&self) -> Option<&T> { self.data.get(0) @@ -1243,7 +1269,7 @@ impl<'a, T: Ord> Drop for DrainSorted<'a, T> { impl<'r, 'a, T: Ord> Drop for DropGuard<'r, 'a, T> { fn drop(&mut self) { - while let Some(_) = self.0.inner.pop() {} + while self.0.inner.pop().is_some() {} } } diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index bde66c406af7f..c6cb39b1bf511 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -4,9 +4,10 @@ use core::fmt::Debug; use core::hash::{Hash, Hasher}; use core::iter::{FromIterator, FusedIterator, Peekable}; use core::marker::PhantomData; +use core::mem::{self, ManuallyDrop}; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, RangeBounds}; -use core::{fmt, mem, ptr}; +use core::{fmt, ptr}; use super::node::{self, marker, ForceResult::*, Handle, InsertResult::*, NodeRef}; use super::search::{self, SearchResult::*}; @@ -39,7 +40,7 @@ use UnderflowResult::*; /// performance on *small* nodes of elements which are cheap to compare. However in the future we /// would like to further explore choosing the optimal search strategy based on the choice of B, /// and possibly other factors. Using linear search, searching for a random element is expected -/// to take O(B logBn) comparisons, which is generally worse than a BST. In practice, +/// to take O(B * log(n)) comparisons, which is generally worse than a BST. In practice, /// however, performance is excellent. /// /// It is a logic error for a key to be modified in such a way that the key's ordering relative to @@ -190,9 +191,9 @@ impl Clone for BTreeMap { // We can't destructure subtree directly // because BTreeMap implements Drop let (subroot, sublength) = unsafe { + let subtree = ManuallyDrop::new(subtree); let root = ptr::read(&subtree.root); let length = subtree.length; - mem::forget(subtree); (root, length) }; @@ -214,59 +215,6 @@ impl Clone for BTreeMap { clone_subtree(self.root.as_ref().unwrap().as_ref()) } } - - fn clone_from(&mut self, other: &Self) { - BTreeClone::clone_from(self, other); - } -} - -trait BTreeClone { - fn clone_from(&mut self, other: &Self); -} - -impl BTreeClone for BTreeMap { - default fn clone_from(&mut self, other: &Self) { - *self = other.clone(); - } -} - -impl BTreeClone for BTreeMap { - fn clone_from(&mut self, other: &Self) { - // This truncates `self` to `other.len()` by calling `split_off` on - // the first key after `other.len()` elements if it exists. - let split_off_key = if self.len() > other.len() { - let diff = self.len() - other.len(); - if diff <= other.len() { - self.iter().nth_back(diff - 1).map(|pair| (*pair.0).clone()) - } else { - self.iter().nth(other.len()).map(|pair| (*pair.0).clone()) - } - } else { - None - }; - if let Some(key) = split_off_key { - self.split_off(&key); - } - - let mut siter = self.range_mut(..); - let mut oiter = other.iter(); - // After truncation, `self` is at most as long as `other` so this loop - // replaces every key-value pair in `self`. Since `oiter` is in sorted - // order and the structure of the `BTreeMap` stays the same, - // the BTree invariants are maintained at the end of the loop. - while !siter.is_empty() { - if let Some((ok, ov)) = oiter.next() { - // SAFETY: This is safe because `siter` is nonempty. - let (sk, sv) = unsafe { siter.next_unchecked() }; - sk.clone_from(ok); - sv.clone_from(ov); - } else { - break; - } - } - // If `other` is longer than `self`, the remaining elements are inserted. - self.extend(oiter.map(|(k, v)| ((*k).clone(), (*v).clone()))); - } } impl super::Recover for BTreeMap @@ -555,7 +503,8 @@ impl BTreeMap { /// map.insert(1, "a"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> BTreeMap { + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn new() -> BTreeMap { BTreeMap { root: None, length: 0 } } @@ -652,11 +601,7 @@ impl BTreeMap { /// assert_eq!(map.first_key_value(), Some((&1, &"b"))); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first_key_value(&self) -> Option<(&K, &V)> - where - T: Ord, - K: Borrow, - { + pub fn first_key_value(&self) -> Option<(&K, &V)> { let front = self.root.as_ref()?.as_ref().first_leaf_edge(); front.right_kv().ok().map(Handle::into_kv) } @@ -666,8 +611,6 @@ impl BTreeMap { /// /// # Examples /// - /// Contrived way to `clear` a map: - /// /// ``` /// #![feature(map_first_last)] /// use std::collections::BTreeMap; @@ -675,27 +618,47 @@ impl BTreeMap { /// let mut map = BTreeMap::new(); /// map.insert(1, "a"); /// map.insert(2, "b"); - /// while let Some(entry) = map.first_entry() { - /// let (key, val) = entry.remove_entry(); - /// assert!(!map.contains_key(&key)); + /// if let Some(mut entry) = map.first_entry() { + /// if *entry.key() > 0 { + /// entry.insert("first"); + /// } /// } + /// assert_eq!(*map.get(&1).unwrap(), "first"); + /// assert_eq!(*map.get(&2).unwrap(), "b"); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first_entry(&mut self) -> Option> - where - T: Ord, - K: Borrow, - { + pub fn first_entry(&mut self) -> Option> { let front = self.root.as_mut()?.as_mut().first_leaf_edge(); - if let Ok(kv) = front.right_kv() { - Some(OccupiedEntry { - handle: kv.forget_node_type(), - length: &mut self.length, - _marker: PhantomData, - }) - } else { - None - } + let kv = front.right_kv().ok()?; + Some(OccupiedEntry { + handle: kv.forget_node_type(), + length: &mut self.length, + _marker: PhantomData, + }) + } + + /// Removes and returns the first element in the map. + /// The key of this element is the minimum key that was in the map. + /// + /// # Examples + /// + /// Draining elements in ascending order, while keeping a usable map each iteration. + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// while let Some((key, _val)) = map.pop_first() { + /// assert!(map.iter().all(|(k, _v)| *k > key)); + /// } + /// assert!(map.is_empty()); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn pop_first(&mut self) -> Option<(K, V)> { + self.first_entry().map(|entry| entry.remove_entry()) } /// Returns the last key-value pair in the map. @@ -715,11 +678,7 @@ impl BTreeMap { /// assert_eq!(map.last_key_value(), Some((&2, &"a"))); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last_key_value(&self) -> Option<(&K, &V)> - where - T: Ord, - K: Borrow, - { + pub fn last_key_value(&self) -> Option<(&K, &V)> { let back = self.root.as_ref()?.as_ref().last_leaf_edge(); back.left_kv().ok().map(Handle::into_kv) } @@ -729,8 +688,6 @@ impl BTreeMap { /// /// # Examples /// - /// Contrived way to `clear` a map: - /// /// ``` /// #![feature(map_first_last)] /// use std::collections::BTreeMap; @@ -738,27 +695,47 @@ impl BTreeMap { /// let mut map = BTreeMap::new(); /// map.insert(1, "a"); /// map.insert(2, "b"); - /// while let Some(entry) = map.last_entry() { - /// let (key, val) = entry.remove_entry(); - /// assert!(!map.contains_key(&key)); + /// if let Some(mut entry) = map.last_entry() { + /// if *entry.key() > 0 { + /// entry.insert("last"); + /// } /// } + /// assert_eq!(*map.get(&1).unwrap(), "a"); + /// assert_eq!(*map.get(&2).unwrap(), "last"); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last_entry(&mut self) -> Option> - where - T: Ord, - K: Borrow, - { + pub fn last_entry(&mut self) -> Option> { let back = self.root.as_mut()?.as_mut().last_leaf_edge(); - if let Ok(kv) = back.left_kv() { - Some(OccupiedEntry { - handle: kv.forget_node_type(), - length: &mut self.length, - _marker: PhantomData, - }) - } else { - None - } + let kv = back.left_kv().ok()?; + Some(OccupiedEntry { + handle: kv.forget_node_type(), + length: &mut self.length, + _marker: PhantomData, + }) + } + + /// Removes and returns the last element in the map. + /// The key of this element is the maximum key that was in the map. + /// + /// # Examples + /// + /// Draining elements in descending order, while keeping a usable map each iteration. + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// while let Some((key, _val)) = map.pop_last() { + /// assert!(map.iter().all(|(k, _v)| *k < key)); + /// } + /// assert!(map.is_empty()); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn pop_last(&mut self) -> Option<(K, V)> { + self.last_entry().map(|entry| entry.remove_entry()) } /// Returns `true` if the map contains a value for the specified key. @@ -894,7 +871,6 @@ impl BTreeMap { /// Basic usage: /// /// ``` - /// #![feature(btreemap_remove_entry)] /// use std::collections::BTreeMap; /// /// let mut map = BTreeMap::new(); @@ -902,7 +878,7 @@ impl BTreeMap { /// assert_eq!(map.remove_entry(&1), Some((1, "a"))); /// assert_eq!(map.remove_entry(&1), None); /// ``` - #[unstable(feature = "btreemap_remove_entry", issue = "66714")] + #[stable(feature = "btreemap_remove_entry", since = "1.45.0")] pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> where K: Borrow, @@ -1005,9 +981,7 @@ impl BTreeMap { R: RangeBounds, { if let Some(root) = &self.root { - let root1 = root.as_ref(); - let root2 = root.as_ref(); - let (f, b) = range_search(root1, root2, range); + let (f, b) = range_search(root.as_ref(), range); Range { front: Some(f), back: Some(b) } } else { @@ -1053,9 +1027,7 @@ impl BTreeMap { R: RangeBounds, { if let Some(root) = &mut self.root { - let root1 = root.as_mut(); - let root2 = unsafe { ptr::read(&root1) }; - let (f, b) = range_search(root1, root2, range); + let (f, b) = range_search(root.as_mut(), range); RangeMut { front: Some(f), back: Some(b), _marker: PhantomData } } else { @@ -1256,6 +1228,48 @@ impl BTreeMap { right } + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, the element is removed from the map and yielded. + /// If the closure returns false, or panics, the element remains in the map and will not be + /// yielded. + /// + /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// elements will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more elements will be subjected to the closure + /// if a panic occurs in the closure, or a panic occurs while dropping an element, + /// or if the `DrainFilter` value is leaked. + /// + /// # Examples + /// + /// Splitting a map into even and odd keys, reusing the original map: + /// + /// ``` + /// #![feature(btree_drain_filter)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); + /// let evens: BTreeMap<_, _> = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// let odds = map; + /// assert_eq!(evens.keys().copied().collect::>(), vec![0, 2, 4, 6]); + /// assert_eq!(odds.keys().copied().collect::>(), vec![1, 3, 5, 7]); + /// ``` + #[unstable(feature = "btree_drain_filter", issue = "70530")] + pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> + where + F: FnMut(&K, &mut V) -> bool, + { + DrainFilter { pred, inner: self.drain_filter_inner() } + } + pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V> { + let front = self.root.as_mut().map(|r| r.as_mut().first_leaf_edge()); + DrainFilterInner { length: &mut self.length, cur_leaf_edge: front } + } + /// Calculates the number of elements if it is incorrect. fn recalc_length(&mut self) { fn dfs<'a, K, V>(node: NodeRef, K, V, marker::LeafOrInternal>) -> usize @@ -1473,20 +1487,13 @@ impl IntoIterator for BTreeMap { type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - if self.root.is_none() { - mem::forget(self); - return IntoIter { front: None, back: None, length: 0 }; - } - - let root1 = unsafe { unwrap_unchecked(ptr::read(&self.root)).into_ref() }; - let root2 = unsafe { unwrap_unchecked(ptr::read(&self.root)).into_ref() }; - let len = self.length; - mem::forget(self); + let mut me = ManuallyDrop::new(self); + if let Some(root) = me.root.take() { + let (f, b) = full_range_search(root.into_ref()); - IntoIter { - front: Some(root1.first_leaf_edge()), - back: Some(root2.last_leaf_edge()), - length: len, + IntoIter { front: Some(f), back: Some(b), length: me.length } + } else { + IntoIter { front: None, back: None, length: 0 } } } } @@ -1653,6 +1660,101 @@ impl Clone for Values<'_, K, V> { } } +/// An iterator produced by calling `drain_filter` on BTreeMap. +#[unstable(feature = "btree_drain_filter", issue = "70530")] +pub struct DrainFilter<'a, K, V, F> +where + K: 'a, + V: 'a, + F: 'a + FnMut(&K, &mut V) -> bool, +{ + pred: F, + inner: DrainFilterInner<'a, K, V>, +} +pub(super) struct DrainFilterInner<'a, K: 'a, V: 'a> { + length: &'a mut usize, + cur_leaf_edge: Option, K, V, marker::Leaf>, marker::Edge>>, +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl Drop for DrainFilter<'_, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + fn drop(&mut self) { + self.for_each(drop); + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl fmt::Debug for DrainFilter<'_, K, V, F> +where + K: fmt::Debug, + V: fmt::Debug, + F: FnMut(&K, &mut V) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DrainFilter").field(&self.inner.peek()).finish() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl Iterator for DrainFilter<'_, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + type Item = (K, V); + + fn next(&mut self) -> Option<(K, V)> { + self.inner.next(&mut self.pred) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a, K: 'a, V: 'a> DrainFilterInner<'a, K, V> { + /// Allow Debug implementations to predict the next element. + pub(super) fn peek(&self) -> Option<(&K, &V)> { + let edge = self.cur_leaf_edge.as_ref()?; + edge.reborrow().next_kv().ok().map(|kv| kv.into_kv()) + } + + unsafe fn next_kv( + &mut self, + ) -> Option, K, V, marker::LeafOrInternal>, marker::KV>> { + let edge = self.cur_leaf_edge.as_ref()?; + ptr::read(edge).next_kv().ok() + } + + /// Implementation of a typical `DrainFilter::next` method, given the predicate. + pub(super) fn next(&mut self, pred: &mut F) -> Option<(K, V)> + where + F: FnMut(&K, &mut V) -> bool, + { + while let Some(mut kv) = unsafe { self.next_kv() } { + let (k, v) = kv.kv_mut(); + if pred(k, v) { + *self.length -= 1; + let (k, v, leaf_edge_location) = kv.remove_kv_tracking(); + self.cur_leaf_edge = Some(leaf_edge_location); + return Some((k, v)); + } + self.cur_leaf_edge = Some(kv.next_leaf_edge()); + } + None + } + + /// Implementation of a typical `DrainFilter::size_hint` method. + pub(super) fn size_hint(&self) -> (usize, Option) { + (0, Some(*self.length)) + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} + #[stable(feature = "btree_range", since = "1.17.0")] impl<'a, K, V> Iterator for Range<'a, K, V> { type Item = (&'a K, &'a V); @@ -1877,9 +1979,9 @@ where } } +/// Finds the leaf edges delimiting a specified range in or underneath a node. fn range_search>( - root1: NodeRef, - root2: NodeRef, + root: NodeRef, range: R, ) -> ( Handle, marker::Edge>, @@ -1893,19 +1995,16 @@ where (Excluded(s), Excluded(e)) if s == e => { panic!("range start and end are equal and excluded in BTreeMap") } - (Included(s), Included(e)) - | (Included(s), Excluded(e)) - | (Excluded(s), Included(e)) - | (Excluded(s), Excluded(e)) - if s > e => - { + (Included(s) | Excluded(s), Included(e) | Excluded(e)) if s > e => { panic!("range start is greater than range end in BTreeMap") } _ => {} }; - let mut min_node = root1; - let mut max_node = root2; + // We duplicate the root NodeRef here -- we will never access it in a way + // that overlaps references obtained from the root. + let mut min_node = unsafe { ptr::read(&root) }; + let mut max_node = root; let mut min_found = false; let mut max_found = false; @@ -1966,6 +2065,33 @@ where } } +/// Equivalent to `range_search(k, v, ..)` without the `Ord` bound. +fn full_range_search( + root: NodeRef, +) -> ( + Handle, marker::Edge>, + Handle, marker::Edge>, +) { + // We duplicate the root NodeRef here -- we will never access it in a way + // that overlaps references obtained from the root. + let mut min_node = unsafe { ptr::read(&root) }; + let mut max_node = root; + loop { + let front = min_node.first_edge(); + let back = max_node.last_edge(); + match (front.force(), back.force()) { + (Leaf(f), Leaf(b)) => { + return (f, b); + } + (Internal(min_int), Internal(max_int)) => { + min_node = min_int.descend(); + max_node = max_int.descend(); + } + _ => unreachable!("BTreeMap has different depths"), + }; + } +} + impl BTreeMap { /// Gets an iterator over the entries of the map, sorted by key. /// @@ -1990,12 +2116,12 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<'_, K, V> { - Iter { - range: Range { - front: self.root.as_ref().map(|r| r.as_ref().first_leaf_edge()), - back: self.root.as_ref().map(|r| r.as_ref().last_leaf_edge()), - }, - length: self.length, + if let Some(root) = &self.root { + let (f, b) = full_range_search(root.as_ref()); + + Iter { range: Range { front: Some(f), back: Some(b) }, length: self.length } + } else { + Iter { range: Range { front: None, back: None }, length: 0 } } } @@ -2022,19 +2148,15 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - IterMut { - range: if let Some(root) = &mut self.root { - let root1 = root.as_mut(); - let root2 = unsafe { ptr::read(&root1) }; - RangeMut { - front: Some(root1.first_leaf_edge()), - back: Some(root2.last_leaf_edge()), - _marker: PhantomData, - } - } else { - RangeMut { front: None, back: None, _marker: PhantomData } - }, - length: self.length, + if let Some(root) = &mut self.root { + let (f, b) = full_range_search(root.as_mut()); + + IterMut { + range: RangeMut { front: Some(f), back: Some(b), _marker: PhantomData }, + length: self.length, + } + } else { + IterMut { range: RangeMut { front: None, back: None, _marker: PhantomData }, length: 0 } } } @@ -2196,6 +2318,34 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { } } + #[unstable(feature = "or_insert_with_key", issue = "71024")] + /// Ensures a value is in the entry by inserting, if empty, the result of the default function, + /// which takes the key as its argument, and returns a mutable reference to the value in the + /// entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(or_insert_with_key)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); + /// + /// assert_eq!(map["poneyland"], 9); + /// ``` + #[inline] + pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let value = default(entry.key()); + entry.insert(value) + } + } + } + /// Returns a reference to this entry's key. /// /// # Examples @@ -2315,15 +2465,14 @@ impl<'a, K: Ord, V> VacantEntry<'a, K, V> { /// /// ``` /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; /// - /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); + /// let mut map: BTreeMap<&str, u32> = BTreeMap::new(); /// - /// // count the number of occurrences of letters in the vec - /// for x in vec!["a","b","a","c","a","b"] { - /// *count.entry(x).or_insert(0) += 1; + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// o.insert(37); /// } - /// - /// assert_eq!(count["a"], 3); + /// assert_eq!(map["poneyland"], 37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(self, value: V) -> &'a mut V { @@ -2531,16 +2680,33 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { fn remove_kv(self) -> (K, V) { *self.length -= 1; - let (small_leaf, old_key, old_val) = match self.handle.force() { + let (old_key, old_val, _) = self.handle.remove_kv_tracking(); + (old_key, old_val) + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInternal>, marker::KV> { + /// Removes a key/value-pair from the map, and returns that pair, as well as + /// the leaf edge corresponding to that former pair. + fn remove_kv_tracking( + self, + ) -> (K, V, Handle, K, V, marker::Leaf>, marker::Edge>) { + let (mut pos, old_key, old_val, was_internal) = match self.force() { Leaf(leaf) => { let (hole, old_key, old_val) = leaf.remove(); - (hole.into_node(), old_key, old_val) + (hole, old_key, old_val, false) } Internal(mut internal) => { + // Replace the location freed in the internal node with the next KV, + // and remove that next KV from its leaf. + let key_loc = internal.kv_mut().0 as *mut K; let val_loc = internal.kv_mut().1 as *mut V; - let to_remove = internal.right_edge().descend().first_leaf_edge().right_kv().ok(); + // Deleting from the left side is typically faster since we can + // just pop an element from the end of the KV array without + // needing to shift the other values. + let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok(); let to_remove = unsafe { unwrap_unchecked(to_remove) }; let (hole, key, val) = to_remove.remove(); @@ -2548,68 +2714,95 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { let old_key = unsafe { mem::replace(&mut *key_loc, key) }; let old_val = unsafe { mem::replace(&mut *val_loc, val) }; - (hole.into_node(), old_key, old_val) + (hole, old_key, old_val, true) } }; // Handle underflow - let mut cur_node = small_leaf.forget_type(); + let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() }; + let mut at_leaf = true; while cur_node.len() < node::MIN_LEN { match handle_underfull_node(cur_node) { AtRoot => break, - EmptyParent(_) => unreachable!(), - Merged(parent) => { + Merged(edge, merged_with_left, offset) => { + // If we merged with our right sibling then our tracked + // position has not changed. However if we merged with our + // left sibling then our tracked position is now dangling. + if at_leaf && merged_with_left { + let idx = pos.idx() + offset; + let node = match unsafe { ptr::read(&edge).descend().force() } { + Leaf(leaf) => leaf, + Internal(_) => unreachable!(), + }; + pos = unsafe { Handle::new_edge(node, idx) }; + } + + let parent = edge.into_node(); if parent.len() == 0 { // We must be at the root parent.into_root_mut().pop_level(); break; } else { cur_node = parent.forget_type(); + at_leaf = false; } } - Stole(_) => break, + Stole(stole_from_left) => { + // Adjust the tracked position if we stole from a left sibling + if stole_from_left && at_leaf { + // SAFETY: This is safe since we just added an element to our node. + unsafe { + pos.next_unchecked(); + } + } + break; + } } } - (old_key, old_val) + // If we deleted from an internal node then we need to compensate for + // the earlier swap and adjust the tracked position to point to the + // next element. + if was_internal { + pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() }; + } + + (old_key, old_val, pos) } } enum UnderflowResult<'a, K, V> { AtRoot, - EmptyParent(NodeRef, K, V, marker::Internal>), - Merged(NodeRef, K, V, marker::Internal>), - Stole(NodeRef, K, V, marker::Internal>), + Merged(Handle, K, V, marker::Internal>, marker::Edge>, bool, usize), + Stole(bool), } fn handle_underfull_node( node: NodeRef, K, V, marker::LeafOrInternal>, ) -> UnderflowResult<'_, K, V> { - let parent = if let Ok(parent) = node.ascend() { - parent - } else { - return AtRoot; + let parent = match node.ascend() { + Ok(parent) => parent, + Err(_) => return AtRoot, }; let (is_left, mut handle) = match parent.left_kv() { Ok(left) => (true, left), - Err(parent) => match parent.right_kv() { - Ok(right) => (false, right), - Err(parent) => { - return EmptyParent(parent.into_node()); - } - }, + Err(parent) => { + let right = unsafe { unwrap_unchecked(parent.right_kv().ok()) }; + (false, right) + } }; if handle.can_merge() { - Merged(handle.merge().into_node()) + let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 }; + Merged(handle.merge(), is_left, offset) } else { if is_left { handle.steal_left(); } else { handle.steal_right(); } - Stole(handle.into_node()) + Stole(is_left) } } diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs index 6ebb98c42cd4f..5569c293e2f66 100644 --- a/src/liballoc/collections/btree/node.rs +++ b/src/liballoc/collections/btree/node.rs @@ -131,7 +131,7 @@ impl BoxedNode { } unsafe fn from_ptr(ptr: NonNull>) -> Self { - BoxedNode { ptr: Unique::from(ptr) } + BoxedNode { ptr: Unique::new_unchecked(ptr.as_ptr()) } } fn as_ptr(&self) -> NonNull> { @@ -161,7 +161,7 @@ impl Root { NodeRef { height: self.height, node: self.node.as_ptr(), - root: self as *const _ as *mut _, + root: ptr::null(), _marker: PhantomData, } } @@ -179,7 +179,7 @@ impl Root { NodeRef { height: self.height, node: self.node.as_ptr(), - root: ptr::null_mut(), // FIXME: Is there anything better to do here? + root: ptr::null(), _marker: PhantomData, } } @@ -723,6 +723,11 @@ impl Handle { pub fn into_node(self) -> Node { self.node } + + /// Returns the position of this handle in the node. + pub fn idx(&self) -> usize { + self.idx + } } impl Handle, marker::KV> { @@ -1142,7 +1147,7 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: (*left_node.as_leaf_mut()).len += right_len as u16 + 1; - if self.node.height > 1 { + let layout = if self.node.height > 1 { ptr::copy_nonoverlapping( right_node.cast_unchecked().as_internal().edges.as_ptr(), left_node @@ -1159,10 +1164,11 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: .correct_parent_link(); } - Global.dealloc(right_node.node.cast(), Layout::new::>()); + Layout::new::>() } else { - Global.dealloc(right_node.node.cast(), Layout::new::>()); - } + Layout::new::>() + }; + Global.dealloc(right_node.node.cast(), layout); Handle::new_edge(self.node, self.idx) } diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs index b100ce754caad..dee5fb878ff2a 100644 --- a/src/liballoc/collections/btree/set.rs +++ b/src/liballoc/collections/btree/set.rs @@ -8,8 +8,8 @@ use core::fmt::{self, Debug}; use core::iter::{FromIterator, FusedIterator, Peekable}; use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; +use super::map::{BTreeMap, Keys}; use super::Recover; -use crate::collections::btree_map::{self, BTreeMap, Keys}; // FIXME(conventions): implement bounded iterators @@ -102,7 +102,7 @@ impl fmt::Debug for Iter<'_, T> { #[stable(feature = "rust1", since = "1.0.0")] #[derive(Debug)] pub struct IntoIter { - iter: btree_map::IntoIter, + iter: super::map::IntoIter, } /// An iterator over a sub-range of items in a `BTreeSet`. @@ -115,7 +115,7 @@ pub struct IntoIter { #[derive(Debug)] #[stable(feature = "btree_range", since = "1.17.0")] pub struct Range<'a, T: 'a> { - iter: btree_map::Range<'a, T, ()>, + iter: super::map::Range<'a, T, ()>, } /// Core of SymmetricDifference and Union. @@ -309,7 +309,8 @@ impl BTreeSet { /// let mut set: BTreeSet = BTreeSet::new(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> BTreeSet { + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn new() -> BTreeSet { BTreeSet { map: BTreeMap::new() } } @@ -944,6 +945,41 @@ impl BTreeSet { { BTreeSet { map: self.map.split_off(key) } } + + /// Creates an iterator which uses a closure to determine if a value should be removed. + /// + /// If the closure returns true, then the value is removed and yielded. + /// If the closure returns false, the value will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// values will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more values will be subjected to the closure + /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the + /// `DrainFilter` itself is leaked. + /// + /// # Examples + /// + /// Splitting a set into even and odd values, reusing the original set: + /// + /// ``` + /// #![feature(btree_drain_filter)] + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet = (0..8).collect(); + /// let evens: BTreeSet<_> = set.drain_filter(|v| v % 2 == 0).collect(); + /// let odds = set; + /// assert_eq!(evens.into_iter().collect::>(), vec![0, 2, 4, 6]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 7]); + /// ``` + #[unstable(feature = "btree_drain_filter", issue = "70530")] + pub fn drain_filter<'a, F>(&'a mut self, pred: F) -> DrainFilter<'a, T, F> + where + F: 'a + FnMut(&T) -> bool, + { + DrainFilter { pred, inner: self.map.drain_filter_inner() } + } } impl BTreeSet { @@ -1055,6 +1091,59 @@ impl<'a, T> IntoIterator for &'a BTreeSet { } } +/// An iterator produced by calling `drain_filter` on BTreeSet. +#[unstable(feature = "btree_drain_filter", issue = "70530")] +pub struct DrainFilter<'a, T, F> +where + T: 'a, + F: 'a + FnMut(&T) -> bool, +{ + pred: F, + inner: super::map::DrainFilterInner<'a, T, ()>, +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl Drop for DrainFilter<'_, T, F> +where + F: FnMut(&T) -> bool, +{ + fn drop(&mut self) { + self.for_each(drop); + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl fmt::Debug for DrainFilter<'_, T, F> +where + T: fmt::Debug, + F: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DrainFilter").field(&self.inner.peek().map(|(k, _)| k)).finish() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, T, F> Iterator for DrainFilter<'_, T, F> +where + F: 'a + FnMut(&T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + let pred = &mut self.pred; + let mut mapped_pred = |k: &T, _v: &mut ()| pred(k); + self.inner.next(&mut mapped_pred).map(|(k, _)| k) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl FusedIterator for DrainFilter<'_, T, F> where F: FnMut(&T) -> bool {} + #[stable(feature = "rust1", since = "1.0.0")] impl Extend for BTreeSet { #[inline] diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs index 53d4f7239b76e..cc0f07b822741 100644 --- a/src/liballoc/collections/linked_list.rs +++ b/src/liballoc/collections/linked_list.rs @@ -143,7 +143,7 @@ impl LinkedList { unsafe { node.next = self.head; node.prev = None; - let node = Some(Box::into_raw_non_null(node)); + let node = Some(Box::leak(node).into()); match self.head { None => self.tail = node, @@ -184,7 +184,7 @@ impl LinkedList { unsafe { node.next = None; node.prev = self.tail; - let node = Some(Box::into_raw_non_null(node)); + let node = Some(Box::leak(node).into()); match self.tail { None => self.head = node, @@ -390,7 +390,7 @@ impl LinkedList { /// This reuses all the nodes from `other` and moves them into `self`. After /// this operation, `other` becomes empty. /// - /// This operation should compute in O(1) time and O(1) memory. + /// This operation should compute in `O(1)` time and `O(1)` memory. /// /// # Examples /// @@ -547,7 +547,7 @@ impl LinkedList { /// Returns `true` if the `LinkedList` is empty. /// - /// This operation should compute in O(1) time. + /// This operation should compute in `O(1)` time. /// /// # Examples /// @@ -568,7 +568,7 @@ impl LinkedList { /// Returns the length of the `LinkedList`. /// - /// This operation should compute in O(1) time. + /// This operation should compute in `O(1)` time. /// /// # Examples /// @@ -594,7 +594,7 @@ impl LinkedList { /// Removes all elements from the `LinkedList`. /// - /// This operation should compute in O(n) time. + /// This operation should compute in `O(n)` time. /// /// # Examples /// @@ -737,7 +737,7 @@ impl LinkedList { /// Adds an element first in the list. /// - /// This operation should compute in O(1) time. + /// This operation should compute in `O(1)` time. /// /// # Examples /// @@ -760,7 +760,7 @@ impl LinkedList { /// Removes the first element and returns it, or `None` if the list is /// empty. /// - /// This operation should compute in O(1) time. + /// This operation should compute in `O(1)` time. /// /// # Examples /// @@ -783,7 +783,7 @@ impl LinkedList { /// Appends an element to the back of a list. /// - /// This operation should compute in O(1) time. + /// This operation should compute in `O(1)` time. /// /// # Examples /// @@ -803,7 +803,7 @@ impl LinkedList { /// Removes the last element from a list and returns it, or `None` if /// it is empty. /// - /// This operation should compute in O(1) time. + /// This operation should compute in `O(1)` time. /// /// # Examples /// @@ -824,7 +824,7 @@ impl LinkedList { /// Splits the list into two at the given index. Returns everything after the given index, /// including the index. /// - /// This operation should compute in O(n) time. + /// This operation should compute in `O(n)` time. /// /// # Panics /// @@ -880,7 +880,7 @@ impl LinkedList { /// Removes the element at the given index and returns it. /// - /// This operation should compute in O(n) time. + /// This operation should compute in `O(n)` time. /// /// # Panics /// Panics if at >= len @@ -972,7 +972,7 @@ unsafe impl<#[may_dangle] T> Drop for LinkedList { fn drop(&mut self) { // Continue the same loop we do below. This only runs when a destructor has // panicked. If another one panics this will abort. - while let Some(_) = self.0.pop_front_node() {} + while self.0.pop_front_node().is_some() {} } } @@ -1133,11 +1133,9 @@ impl IterMut<'_, T> { Some(prev) => prev, }; - let node = Some(Box::into_raw_non_null(box Node { - next: Some(head), - prev: Some(prev), - element, - })); + let node = Some( + Box::leak(box Node { next: Some(head), prev: Some(prev), element }).into(), + ); // Not creating references to entire nodes to not invalidate the // reference to `element` we handed to the user. @@ -1197,6 +1195,14 @@ pub struct Cursor<'a, T: 'a> { list: &'a LinkedList, } +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl Clone for Cursor<'_, T> { + fn clone(&self) -> Self { + let Cursor { index, current, list } = *self; + Cursor { index, current, list } + } +} + #[unstable(feature = "linked_list_cursors", issue = "58533")] impl fmt::Debug for Cursor<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1442,7 +1448,7 @@ impl<'a, T> CursorMut<'a, T> { #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn insert_after(&mut self, item: T) { unsafe { - let spliced_node = Box::into_raw_non_null(Box::new(Node::new(item))); + let spliced_node = Box::leak(Box::new(Node::new(item))).into(); let node_next = match self.current { None => self.list.head, Some(node) => node.as_ref().next, @@ -1462,7 +1468,7 @@ impl<'a, T> CursorMut<'a, T> { #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn insert_before(&mut self, item: T) { unsafe { - let spliced_node = Box::into_raw_non_null(Box::new(Node::new(item))); + let spliced_node = Box::leak(Box::new(Node::new(item))).into(); let node_prev = match self.current { None => self.list.tail, Some(node) => node.as_ref().prev, @@ -1490,6 +1496,31 @@ impl<'a, T> CursorMut<'a, T> { } } + /// Removes the current element from the `LinkedList` without deallocating the list node. + /// + /// The node that was removed is returned as a new `LinkedList` containing only this node. + /// The cursor is moved to point to the next element in the current `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current_as_list(&mut self) -> Option> { + let mut unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + + unlinked_node.as_mut().prev = None; + unlinked_node.as_mut().next = None; + Some(LinkedList { + head: Some(unlinked_node), + tail: Some(unlinked_node), + len: 1, + marker: PhantomData, + }) + } + } + /// Inserts the elements from the given `LinkedList` after the current one. /// /// If the cursor is pointing at the "ghost" non-element then the new elements are @@ -1835,3 +1866,15 @@ unsafe impl Send for IterMut<'_, T> {} #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for IterMut<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for Cursor<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for Cursor<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for CursorMut<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for CursorMut<'_, T> {} diff --git a/src/liballoc/collections/linked_list/tests.rs b/src/liballoc/collections/linked_list/tests.rs index 085f734ed916a..b8c93a28bba81 100644 --- a/src/liballoc/collections/linked_list/tests.rs +++ b/src/liballoc/collections/linked_list/tests.rs @@ -182,7 +182,6 @@ fn test_insert_prev() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] -#[cfg_attr(miri, ignore)] // Miri does not support threads fn test_send() { let n = list_from(&[1, 2, 3]); thread::spawn(move || { diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index 9d56f17700a85..540649c61b332 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -12,7 +12,7 @@ use core::cmp::{self, Ordering}; use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::{once, repeat_with, FromIterator, FusedIterator}; -use core::mem::{self, replace}; +use core::mem::{self, replace, ManuallyDrop}; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, IndexMut, RangeBounds, Try}; use core::ptr::{self, NonNull}; @@ -50,6 +50,7 @@ const MAXIMUM_ZST_CAPACITY: usize = 1 << (64 - 1); // Largest possible power of /// [`pop_front`]: #method.pop_front /// [`extend`]: #method.extend /// [`append`]: #method.append +#[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct VecDeque { // tail and head are pointers into the buffer. Tail always points @@ -72,7 +73,7 @@ pub struct VecDeque { /// It produces the following sequence of matching slices: /// /// ([0 1], [a b]) -/// ([2], [c]) +/// (\[2\], \[c\]) /// ([3 4], [d e]) /// /// and the uneven remainder of either A or B is skipped. @@ -488,7 +489,7 @@ impl VecDeque { VecDeque { tail: 0, head: 0, buf: RawVec::with_capacity(cap) } } - /// Retrieves an element in the `VecDeque` by index. + /// Provides a reference to the element at the given index. /// /// Element at index 0 is the front of the queue. /// @@ -513,7 +514,7 @@ impl VecDeque { } } - /// Retrieves an element in the `VecDeque` mutably by index. + /// Provides a mutable reference to the element at the given index. /// /// Element at index 0 is the front of the queue. /// @@ -651,7 +652,7 @@ impl VecDeque { } } - /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// Tries to reserve the minimum capacity for exactly `additional` more elements to /// be inserted in the given `VecDeque`. After calling `reserve_exact`, /// capacity will be greater than or equal to `self.len() + additional`. /// Does nothing if the capacity is already sufficient. @@ -662,7 +663,7 @@ impl VecDeque { /// /// # Errors /// - /// If the capacity overflows, or the allocator reports a failure, then an error + /// If the capacity overflows `usize`, or the allocator reports a failure, then an error /// is returned. /// /// # Examples @@ -678,7 +679,7 @@ impl VecDeque { /// // Pre-reserve the memory, exiting if we can't /// output.try_reserve_exact(data.len())?; /// - /// // Now we know this can't OOM in the middle of our complex work + /// // Now we know this can't OOM(Out-Of-Memory) in the middle of our complex work /// output.extend(data.iter().map(|&val| { /// val * 2 + 5 // very complicated /// })); @@ -700,7 +701,7 @@ impl VecDeque { /// /// # Errors /// - /// If the capacity overflows, or the allocator reports a failure, then an error + /// If the capacity overflows `usize`, or the allocator reports a failure, then an error /// is returned. /// /// # Examples @@ -959,6 +960,9 @@ impl VecDeque { /// Returns a pair of slices which contain, in order, the contents of the /// `VecDeque`. /// + /// If [`make_contiguous`](#method.make_contiguous) was previously called, all elements + /// of the `VecDeque` will be in the first slice and the second slice will be empty. + /// /// # Examples /// /// ``` @@ -989,6 +993,9 @@ impl VecDeque { /// Returns a pair of slices which contain, in order, the contents of the /// `VecDeque`. /// + /// If [`make_contiguous`](#method.make_contiguous) was previously called, all elements + /// of the `VecDeque` will be in the first slice and the second slice will be empty. + /// /// # Examples /// /// ``` @@ -1347,7 +1354,9 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn push_front(&mut self, value: T) { - self.grow_if_necessary(); + if self.is_full() { + self.grow(); + } self.tail = self.wrap_sub(self.tail, 1); let tail = self.tail; @@ -1370,7 +1379,9 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn push_back(&mut self, value: T) { - self.grow_if_necessary(); + if self.is_full() { + self.grow(); + } let head = self.head; self.head = self.wrap_add(self.head, 1); @@ -1385,7 +1396,7 @@ impl VecDeque { /// Removes an element from anywhere in the `VecDeque` and returns it, /// replacing it with the first element. /// - /// This does not preserve ordering, but is O(1). + /// This does not preserve ordering, but is `O(1)`. /// /// Returns `None` if `index` is out of bounds. /// @@ -1420,7 +1431,7 @@ impl VecDeque { /// Removes an element from anywhere in the `VecDeque` and returns it, replacing it with the /// last element. /// - /// This does not preserve ordering, but is O(1). + /// This does not preserve ordering, but is `O(1)`. /// /// Returns `None` if `index` is out of bounds. /// @@ -1478,7 +1489,9 @@ impl VecDeque { #[stable(feature = "deque_extras_15", since = "1.5.0")] pub fn insert(&mut self, index: usize, value: T) { assert!(index <= self.len(), "index out of bounds"); - self.grow_if_necessary(); + if self.is_full() { + self.grow(); + } // Move the least number of elements in the ring buffer and insert // the given object @@ -1876,6 +1889,7 @@ impl VecDeque { /// assert_eq!(buf2, [2, 3]); /// ``` #[inline] + #[must_use = "use `.truncate()` if you don't need the other half"] #[stable(feature = "split_off", since = "1.4.0")] pub fn split_off(&mut self, at: usize) -> Self { let len = self.len(); @@ -1995,11 +2009,13 @@ impl VecDeque { } // This may panic or abort - #[inline] - fn grow_if_necessary(&mut self) { + #[inline(never)] + fn grow(&mut self) { if self.is_full() { let old_cap = self.cap(); - self.buf.double(); + // Double the buffer size. + self.buf.reserve_exact(old_cap, old_cap); + assert!(self.cap() == old_cap * 2); unsafe { self.handle_capacity_increase(old_cap); } @@ -2043,6 +2059,148 @@ impl VecDeque { } } + /// Rearranges the internal storage of this deque so it is one contiguous slice, which is then returned. + /// + /// This method does not allocate and does not change the order of the inserted elements. + /// As it returns a mutable slice, this can be used to sort or binary search a deque. + /// + /// Once the internal storage is contiguous, the [`as_slices`](#method.as_slices) and + /// [`as_mut_slices`](#method.as_mut_slices) methods will return the entire contents of the + /// `VecDeque` in a single slice. + /// + /// # Examples + /// + /// Sorting the content of a deque. + /// + /// ``` + /// #![feature(deque_make_contiguous)] + /// + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::with_capacity(15); + /// + /// buf.push_back(2); + /// buf.push_back(1); + /// buf.push_front(3); + /// + /// // sorting the deque + /// buf.make_contiguous().sort(); + /// assert_eq!(buf.as_slices(), (&[1, 2, 3] as &[_], &[] as &[_])); + /// + /// // sorting it in reverse order + /// buf.make_contiguous().sort_by(|a, b| b.cmp(a)); + /// assert_eq!(buf.as_slices(), (&[3, 2, 1] as &[_], &[] as &[_])); + /// ``` + /// + /// Getting immutable access to the contiguous slice. + /// + /// ```rust + /// #![feature(deque_make_contiguous)] + /// + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// + /// buf.push_back(2); + /// buf.push_back(1); + /// buf.push_front(3); + /// + /// buf.make_contiguous(); + /// if let (slice, &[]) = buf.as_slices() { + /// // we can now be sure that `slice` contains all elements of the deque, + /// // while still having immutable access to `buf`. + /// assert_eq!(buf.len(), slice.len()); + /// assert_eq!(slice, &[3, 2, 1] as &[_]); + /// } + /// ``` + #[unstable(feature = "deque_make_contiguous", issue = "70929")] + pub fn make_contiguous(&mut self) -> &mut [T] { + if self.is_contiguous() { + let tail = self.tail; + let head = self.head; + return unsafe { &mut self.buffer_as_mut_slice()[tail..head] }; + } + + let buf = self.buf.ptr(); + let cap = self.cap(); + let len = self.len(); + + let free = self.tail - self.head; + let tail_len = cap - self.tail; + + if free >= tail_len { + // there is enough free space to copy the tail in one go, + // this means that we first shift the head backwards, and then + // copy the tail to the correct position. + // + // from: DEFGH....ABC + // to: ABCDEFGH.... + unsafe { + ptr::copy(buf, buf.add(tail_len), self.head); + // ...DEFGH.ABC + ptr::copy_nonoverlapping(buf.add(self.tail), buf, tail_len); + // ABCDEFGH.... + + self.tail = 0; + self.head = len; + } + } else if free >= self.head { + // there is enough free space to copy the head in one go, + // this means that we first shift the tail forwards, and then + // copy the head to the correct position. + // + // from: FGH....ABCDE + // to: ...ABCDEFGH. + unsafe { + ptr::copy(buf.add(self.tail), buf.add(self.head), tail_len); + // FGHABCDE.... + ptr::copy_nonoverlapping(buf, buf.add(self.head + tail_len), self.head); + // ...ABCDEFGH. + + self.tail = self.head; + self.head = self.tail + len; + } + } else { + // free is smaller than both head and tail, + // this means we have to slowly "swap" the tail and the head. + // + // from: EFGHI...ABCD or HIJK.ABCDEFG + // to: ABCDEFGHI... or ABCDEFGHIJK. + let mut left_edge: usize = 0; + let mut right_edge: usize = self.tail; + unsafe { + // The general problem looks like this + // GHIJKLM...ABCDEF - before any swaps + // ABCDEFM...GHIJKL - after 1 pass of swaps + // ABCDEFGHIJM...KL - swap until the left edge reaches the temp store + // - then restart the algorithm with a new (smaller) store + // Sometimes the temp store is reached when the right edge is at the end + // of the buffer - this means we've hit the right order with fewer swaps! + // E.g + // EF..ABCD + // ABCDEF.. - after four only swaps we've finished + while left_edge < len && right_edge != cap { + let mut right_offset = 0; + for i in left_edge..right_edge { + right_offset = (i - left_edge) % (cap - right_edge); + let src: isize = (right_edge + right_offset) as isize; + ptr::swap(buf.add(i), buf.offset(src)); + } + let n_ops = right_edge - left_edge; + left_edge += n_ops; + right_edge += right_offset + 1; + } + + self.tail = 0; + self.head = len; + } + } + + let tail = self.tail; + let head = self.head; + unsafe { &mut self.buffer_as_mut_slice()[tail..head] } + } + /// Rotates the double-ended queue `mid` places to the left. /// /// Equivalently, @@ -2749,12 +2907,12 @@ impl From> for VecDeque { /// This avoids reallocating where possible, but the conditions for that are /// strict, and subject to change, and so shouldn't be relied upon unless the /// `Vec` came from `From>` and hasn't been reallocated. - fn from(mut other: Vec) -> Self { + fn from(other: Vec) -> Self { unsafe { + let mut other = ManuallyDrop::new(other); let other_buf = other.as_mut_ptr(); let mut buf = RawVec::from_raw_parts(other_buf, other.capacity()); let len = other.len(); - mem::forget(other); // We need to extend the buf if it's not a power of two, too small // or doesn't have at least one free space @@ -2778,7 +2936,7 @@ impl From> for Vec { /// [`Vec`]: crate::vec::Vec /// [`VecDeque`]: crate::collections::VecDeque /// - /// This never needs to re-allocate, but does need to do O(n) data movement if + /// This never needs to re-allocate, but does need to do `O(n)` data movement if /// the circular buffer doesn't happen to be at the beginning of the allocation. /// /// # Examples @@ -2802,67 +2960,19 @@ impl From> for Vec { /// assert_eq!(vec, [8, 9, 1, 2, 3, 4]); /// assert_eq!(vec.as_ptr(), ptr); /// ``` - fn from(other: VecDeque) -> Self { + fn from(mut other: VecDeque) -> Self { + other.make_contiguous(); + unsafe { + let other = ManuallyDrop::new(other); let buf = other.buf.ptr(); let len = other.len(); - let tail = other.tail; - let head = other.head; let cap = other.cap(); - // Need to move the ring to the front of the buffer, as vec will expect this. - if other.is_contiguous() { - ptr::copy(buf.add(tail), buf, len); - } else { - if (tail - head) >= cmp::min(cap - tail, head) { - // There is enough free space in the centre for the shortest block so we can - // do this in at most three copy moves. - if (cap - tail) > head { - // right hand block is the long one; move that enough for the left - ptr::copy(buf.add(tail), buf.add(tail - head), cap - tail); - // copy left in the end - ptr::copy(buf, buf.add(cap - head), head); - // shift the new thing to the start - ptr::copy(buf.add(tail - head), buf, len); - } else { - // left hand block is the long one, we can do it in two! - ptr::copy(buf, buf.add(cap - tail), head); - ptr::copy(buf.add(tail), buf, cap - tail); - } - } else { - // Need to use N swaps to move the ring - // We can use the space at the end of the ring as a temp store - - let mut left_edge: usize = 0; - let mut right_edge: usize = tail; - - // The general problem looks like this - // GHIJKLM...ABCDEF - before any swaps - // ABCDEFM...GHIJKL - after 1 pass of swaps - // ABCDEFGHIJM...KL - swap until the left edge reaches the temp store - // - then restart the algorithm with a new (smaller) store - // Sometimes the temp store is reached when the right edge is at the end - // of the buffer - this means we've hit the right order with fewer swaps! - // E.g - // EF..ABCD - // ABCDEF.. - after four only swaps we've finished - - while left_edge < len && right_edge != cap { - let mut right_offset = 0; - for i in left_edge..right_edge { - right_offset = (i - left_edge) % (cap - right_edge); - let src: isize = (right_edge + right_offset) as isize; - ptr::swap(buf.add(i), buf.offset(src)); - } - let n_ops = right_edge - left_edge; - left_edge += n_ops; - right_edge += right_offset + 1; - } - } + if other.head != 0 { + ptr::copy(buf.add(other.tail), buf, len); } - let out = Vec::from_raw_parts(buf, len, cap); - mem::forget(other); - out + Vec::from_raw_parts(buf, len, cap) } } } diff --git a/src/liballoc/collections/vec_deque/tests.rs b/src/liballoc/collections/vec_deque/tests.rs index f2ce5b1d15dde..fc2ec7908e823 100644 --- a/src/liballoc/collections/vec_deque/tests.rs +++ b/src/liballoc/collections/vec_deque/tests.rs @@ -1,9 +1,9 @@ use super::*; -use ::test; +use test; #[bench] -#[cfg_attr(miri, ignore)] // Miri does not support benchmarks +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_push_back_100(b: &mut test::Bencher) { let mut deq = VecDeque::with_capacity(101); b.iter(|| { @@ -16,7 +16,7 @@ fn bench_push_back_100(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // Miri does not support benchmarks +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_push_front_100(b: &mut test::Bencher) { let mut deq = VecDeque::with_capacity(101); b.iter(|| { @@ -29,7 +29,7 @@ fn bench_push_front_100(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // Miri does not support benchmarks +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_pop_back_100(b: &mut test::Bencher) { let mut deq = VecDeque::::with_capacity(101); @@ -43,7 +43,7 @@ fn bench_pop_back_100(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // Miri does not support benchmarks +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_pop_front_100(b: &mut test::Bencher) { let mut deq = VecDeque::::with_capacity(101); @@ -130,6 +130,87 @@ fn test_insert() { } } +#[test] +fn make_contiguous_big_tail() { + let mut tester = VecDeque::with_capacity(15); + + for i in 0..3 { + tester.push_back(i); + } + + for i in 3..10 { + tester.push_front(i); + } + + // 012......9876543 + assert_eq!(tester.capacity(), 15); + assert_eq!((&[9, 8, 7, 6, 5, 4, 3] as &[_], &[0, 1, 2] as &[_]), tester.as_slices()); + + let expected_start = tester.head; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!((&[9, 8, 7, 6, 5, 4, 3, 0, 1, 2] as &[_], &[] as &[_]), tester.as_slices()); +} + +#[test] +fn make_contiguous_big_head() { + let mut tester = VecDeque::with_capacity(15); + + for i in 0..8 { + tester.push_back(i); + } + + for i in 8..10 { + tester.push_front(i); + } + + // 01234567......98 + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!((&[9, 8, 0, 1, 2, 3, 4, 5, 6, 7] as &[_], &[] as &[_]), tester.as_slices()); +} + +#[test] +fn make_contiguous_small_free() { + let mut tester = VecDeque::with_capacity(15); + + for i in 'A' as u8..'I' as u8 { + tester.push_back(i as char); + } + + for i in 'I' as u8..'N' as u8 { + tester.push_front(i as char); + } + + // ABCDEFGH...MLKJI + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!( + (&['M', 'L', 'K', 'J', 'I', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] as &[_], &[] as &[_]), + tester.as_slices() + ); + + tester.clear(); + for i in 'I' as u8..'N' as u8 { + tester.push_back(i as char); + } + + for i in 'A' as u8..'I' as u8 { + tester.push_front(i as char); + } + + // IJKLM...HGFEDCBA + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!( + (&['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'I', 'J', 'K', 'L', 'M'] as &[_], &[] as &[_]), + tester.as_slices() + ); +} + #[test] fn test_remove() { // This test checks that every single combination of tail position, length, and @@ -305,10 +386,8 @@ fn test_vec_from_vecdeque() { assert!(vec.into_iter().eq(vd)); } - #[cfg(not(miri))] // Miri is too slow - let max_pwr = 7; - #[cfg(miri)] - let max_pwr = 5; + // Miri is too slow + let max_pwr = if cfg!(miri) { 5 } else { 7 }; for cap_pwr in 0..max_pwr { // Make capacity as a (2^x)-1, so that the ring size is 2^x diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 13ef2f063f95f..26077f3c8d150 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -50,8 +50,8 @@ //! The internal iterator over the argument has not been advanced by the time //! the first `{}` is seen, so it prints the first argument. Then upon reaching //! the second `{}`, the iterator has advanced forward to the second argument. -//! Essentially, parameters which explicitly name their argument do not affect -//! parameters which do not name an argument in terms of positional specifiers. +//! Essentially, parameters that explicitly name their argument do not affect +//! parameters that do not name an argument in terms of positional specifiers. //! //! A format string is required to use all of its arguments, otherwise it is a //! compile-time error. You may refer to the same argument more than once in the @@ -60,7 +60,7 @@ //! ## Named parameters //! //! Rust itself does not have a Python-like equivalent of named parameters to a -//! function, but the [`format!`] macro is a syntax extension which allows it to +//! function, but the [`format!`] macro is a syntax extension that allows it to //! leverage named parameters. Named parameters are listed at the end of the //! argument list and have the syntax: //! @@ -77,7 +77,7 @@ //! ``` //! //! It is not valid to put positional parameters (those without names) after -//! arguments which have names. Like with positional parameters, it is not +//! arguments that have names. Like with positional parameters, it is not //! valid to provide named parameters that are unused by the format string. //! //! # Formatting Parameters @@ -130,7 +130,7 @@ //! //! The default [fill/alignment](#fillalignment) for non-numerics is a space and //! left-aligned. The -//! defaults for numeric formatters is also a space but with right-alignment. If +//! default for numeric formatters is also a space character but with right-alignment. If //! the `0` flag (see below) is specified for numerics, then the implicit fill character is //! `0`. //! @@ -161,7 +161,7 @@ //! `Signed` trait. This flag indicates that the correct sign (`+` or `-`) //! should always be printed. //! * `-` - Currently not used -//! * `#` - This flag is indicates that the "alternate" form of printing should +//! * `#` - This flag indicates that the "alternate" form of printing should //! be used. The alternate forms are: //! * `#?` - pretty-print the [`Debug`] formatting //! * `#x` - precedes the argument with a `0x` @@ -173,9 +173,9 @@ //! like `{:08}` would yield `00000001` for the integer `1`, while the //! same format would yield `-0000001` for the integer `-1`. Notice that //! the negative version has one fewer zero than the positive version. -//! Note that padding zeroes are always placed after the sign (if any) +//! Note that padding zeros are always placed after the sign (if any) //! and before the digits. When used together with the `#` flag, a similar -//! rule applies: padding zeroes are inserted after the prefix but before +//! rule applies: padding zeros are inserted after the prefix but before //! the digits. The prefix is included in the total width. //! //! ## Precision @@ -251,7 +251,7 @@ //! //! In some programming languages, the behavior of string formatting functions //! depends on the operating system's locale setting. The format functions -//! provided by Rust's standard library do not have any concept of locale, and +//! provided by Rust's standard library do not have any concept of locale and //! will produce the same results on all systems regardless of user //! configuration. //! @@ -470,7 +470,7 @@ //! //! ### `format_args!` //! -//! This is a curious macro which is used to safely pass around +//! This is a curious macro used to safely pass around //! an opaque object describing the format string. This object //! does not require any heap allocations to create, and it only //! references information on the stack. Under the hood, all of @@ -495,7 +495,7 @@ //! This structure can then be passed to the [`write`] and [`format`] functions //! inside this module in order to process the format string. //! The goal of this macro is to even further prevent intermediate allocations -//! when dealing formatting strings. +//! when dealing with formatting strings. //! //! For example, a logging library could use the standard formatting syntax, but //! it would internally pass around this structure until it has been determined diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index d55a1a3b63584..7aaa91ee10d97 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -77,12 +77,12 @@ #![feature(allocator_api)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] -#![feature(box_into_raw_non_null)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] #![feature(coerce_unsized)] +#![feature(const_btree_new)] #![feature(const_generic_impls_guard)] #![feature(const_generics)] #![feature(const_in_array_repeat_expressions)] @@ -99,14 +99,17 @@ #![feature(internal_uninit_const)] #![feature(lang_items)] #![feature(libc)] +#![feature(negative_impls)] +#![feature(new_uninit)] #![feature(nll)] #![feature(optin_builtin_traits)] +#![feature(or_patterns)] #![feature(pattern)] #![feature(ptr_internals)] #![feature(ptr_offset_from)] #![feature(rustc_attrs)] #![feature(receiver_trait)] -#![feature(specialization)] +#![feature(min_specialization)] #![feature(staged_api)] #![feature(std_internals)] #![feature(str_internals)] diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs index 422d3486f92b2..e163a166b498f 100644 --- a/src/liballoc/macros.rs +++ b/src/liballoc/macros.rs @@ -36,13 +36,15 @@ #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(box_syntax)] macro_rules! vec { + () => ( + $crate::vec::Vec::new() + ); ($elem:expr; $n:expr) => ( $crate::vec::from_elem($elem, $n) ); - ($($x:expr),*) => ( - <[_]>::into_vec(box [$($x),*]) + ($($x:expr),+ $(,)?) => ( + <[_]>::into_vec(box [$($x),+]) ); - ($($x:expr,)*) => ($crate::vec![$($x),*]) } // HACK(japaric): with cfg(test) the inherent `[T]::into_vec` method, which is @@ -51,6 +53,9 @@ macro_rules! vec { // NB see the slice::hack module in slice.rs for more information #[cfg(test)] macro_rules! vec { + () => ( + $crate::vec::Vec::new() + ); ($elem:expr; $n:expr) => ( $crate::vec::from_elem($elem, $n) ); diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index b31fec7f037c9..2bd4733db420b 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -1,13 +1,19 @@ #![unstable(feature = "raw_vec_internals", reason = "implementation detail", issue = "none")] #![doc(hidden)] +use core::alloc::{LayoutErr, MemoryBlock}; use core::cmp; -use core::mem; +use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::Drop; -use core::ptr::{self, NonNull, Unique}; +use core::ptr::{NonNull, Unique}; use core::slice; -use crate::alloc::{handle_alloc_error, AllocErr, AllocRef, Global, Layout}; +use crate::alloc::{ + handle_alloc_error, AllocErr, + AllocInit::{self, *}, + AllocRef, Global, Layout, + ReallocPlacement::{self, *}, +}; use crate::boxed::Box; use crate::collections::TryReserveError::{self, *}; @@ -19,83 +25,28 @@ mod tests; /// involved. This type is excellent for building your own data structures like Vec and VecDeque. /// In particular: /// -/// * Produces `Unique::empty()` on zero-sized types. -/// * Produces `Unique::empty()` on zero-length allocations. +/// * 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. -/// * Aborts on OOM or calls `handle_alloc_error` as applicable. -/// * Avoids freeing `Unique::empty()`. +/// * 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 a `RawVec` always forces its capacity to be `usize::MAX` for zero-sized types. -/// This enables you to use capacity-growing logic catch the overflows in your length -/// that might occur with zero-sized types. -/// -/// The above means that you need to be careful when round-tripping this type with a -/// `Box<[T]>`, since `capacity()` won't yield the length. However, `with_capacity`, -/// `shrink_to_fit`, and `from_box` will actually set `RawVec`'s private capacity -/// field. This allows zero-sized types to not be special-cased by consumers of -/// this type. +/// 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 struct RawVec { ptr: Unique, cap: usize, - a: A, -} - -impl RawVec { - /// Like `new`, but parameterized over the choice of allocator for - /// the returned `RawVec`. - pub const fn new_in(a: A) -> Self { - let cap = if mem::size_of::() == 0 { core::usize::MAX } else { 0 }; - - // `Unique::empty()` doubles as "unallocated" and "zero-sized allocation". - RawVec { ptr: Unique::empty(), cap, a } - } - - /// Like `with_capacity`, but parameterized over the choice of - /// allocator for the returned `RawVec`. - #[inline] - pub fn with_capacity_in(capacity: usize, a: A) -> Self { - RawVec::allocate_in(capacity, false, a) - } - - /// Like `with_capacity_zeroed`, but parameterized over the choice - /// of allocator for the returned `RawVec`. - #[inline] - pub fn with_capacity_zeroed_in(capacity: usize, a: A) -> Self { - RawVec::allocate_in(capacity, true, a) - } - - fn allocate_in(mut capacity: usize, zeroed: bool, mut a: A) -> Self { - let elem_size = mem::size_of::(); - - let alloc_size = capacity.checked_mul(elem_size).unwrap_or_else(|| capacity_overflow()); - alloc_guard(alloc_size).unwrap_or_else(|_| capacity_overflow()); - - // Handles ZSTs and `capacity == 0` alike. - let ptr = if alloc_size == 0 { - NonNull::::dangling() - } else { - let align = mem::align_of::(); - let layout = Layout::from_size_align(alloc_size, align).unwrap(); - let result = if zeroed { a.alloc_zeroed(layout) } else { a.alloc(layout) }; - match result { - Ok((ptr, size)) => { - capacity = size / elem_size; - ptr.cast() - } - Err(_) => handle_alloc_error(layout), - } - }; - - RawVec { ptr: ptr.into(), cap: capacity, a } - } + alloc: A, } impl RawVec { @@ -138,54 +89,90 @@ impl RawVec { /// Aborts on OOM. #[inline] pub fn with_capacity(capacity: usize) -> Self { - RawVec::allocate_in(capacity, false, Global) + Self::with_capacity_in(capacity, Global) } /// Like `with_capacity`, but guarantees the buffer is zeroed. #[inline] pub fn with_capacity_zeroed(capacity: usize) -> Self { - RawVec::allocate_in(capacity, true, Global) + Self::with_capacity_zeroed_in(capacity, Global) } -} -impl RawVec { - /// Reconstitutes a `RawVec` from a pointer, capacity, and allocator. - /// - /// # Undefined Behavior - /// - /// The `ptr` must be allocated (via the given allocator `a`), and with the given `capacity`. - /// The `capacity` cannot exceed `isize::MAX` (only a concern on 32-bit systems). - /// If the `ptr` and `capacity` come from a `RawVec` created via `a`, then this is guaranteed. - pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, a: A) -> Self { - RawVec { ptr: Unique::new_unchecked(ptr), cap: capacity, a } - } -} - -impl RawVec { /// Reconstitutes a `RawVec` from a pointer and capacity. /// - /// # Undefined Behavior + /// # Safety /// /// The `ptr` must be allocated (on the system heap), and with the given `capacity`. - /// The `capacity` cannot exceed `isize::MAX` (only a concern on 32-bit systems). + /// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit + /// systems). ZST vectors may have a capacity up to `usize::MAX`. /// If the `ptr` and `capacity` come from a `RawVec`, then this is guaranteed. + #[inline] pub unsafe fn from_raw_parts(ptr: *mut T, capacity: usize) -> Self { - RawVec { ptr: Unique::new_unchecked(ptr), cap: capacity, a: Global } + Self::from_raw_parts_in(ptr, capacity, Global) } /// Converts a `Box<[T]>` into a `RawVec`. - pub fn from_box(mut slice: Box<[T]>) -> Self { + pub fn from_box(slice: Box<[T]>) -> Self { unsafe { - let result = RawVec::from_raw_parts(slice.as_mut_ptr(), slice.len()); - mem::forget(slice); - result + let mut slice = ManuallyDrop::new(slice); + RawVec::from_raw_parts(slice.as_mut_ptr(), slice.len()) } } } impl RawVec { + /// Like `new`, but parameterized over the choice of allocator for + /// the returned `RawVec`. + pub const fn new_in(alloc: A) -> Self { + // `cap: 0` means "unallocated". zero-sized types are ignored. + Self { ptr: Unique::dangling(), cap: 0, alloc } + } + + /// Like `with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[inline] + pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + Self::allocate_in(capacity, Uninitialized, alloc) + } + + /// Like `with_capacity_zeroed`, but parameterized over the choice + /// of allocator for the returned `RawVec`. + #[inline] + pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + Self::allocate_in(capacity, Zeroed, alloc) + } + + fn allocate_in(capacity: usize, init: AllocInit, mut alloc: A) -> Self { + if mem::size_of::() == 0 { + Self::new_in(alloc) + } else { + let layout = Layout::array::(capacity).unwrap_or_else(|_| capacity_overflow()); + alloc_guard(layout.size()).unwrap_or_else(|_| capacity_overflow()); + + let memory = alloc.alloc(layout, init).unwrap_or_else(|_| handle_alloc_error(layout)); + Self { + ptr: unsafe { Unique::new_unchecked(memory.ptr.cast().as_ptr()) }, + cap: Self::capacity_from_bytes(memory.size), + alloc, + } + } + } + + /// Reconstitutes a `RawVec` from a pointer, capacity, and allocator. + /// + /// # Safety + /// + /// The `ptr` must be allocated (via the given allocator `a`), and with the given `capacity`. + /// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit + /// systems). ZST vectors may have a capacity up to `usize::MAX`. + /// If the `ptr` and `capacity` come from a `RawVec` created via `a`, then this is guaranteed. + #[inline] + pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, a: A) -> Self { + Self { ptr: Unique::new_unchecked(ptr), cap: capacity, alloc: a } + } + /// Gets a raw pointer to the start of the allocation. Note that this is - /// `Unique::empty()` if `capacity == 0` or `T` is zero-sized. In the former case, you must + /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. pub fn ptr(&self) -> *mut T { self.ptr.as_ptr() @@ -196,21 +183,21 @@ impl RawVec { /// This will always be `usize::MAX` if `T` is zero-sized. #[inline(always)] pub fn capacity(&self) -> usize { - if mem::size_of::() == 0 { !0 } else { self.cap } + if mem::size_of::() == 0 { usize::MAX } else { self.cap } } /// Returns a shared reference to the allocator backing this `RawVec`. pub fn alloc(&self) -> &A { - &self.a + &self.alloc } /// Returns a mutable reference to the allocator backing this `RawVec`. pub fn alloc_mut(&mut self) -> &mut A { - &mut self.a + &mut self.alloc } - fn current_layout(&self) -> Option { - if self.cap == 0 { + fn current_memory(&self) -> Option<(NonNull, Layout)> { + if mem::size_of::() == 0 || self.cap == 0 { None } else { // We have an allocated chunk of memory, so we can bypass runtime @@ -218,219 +205,12 @@ impl RawVec { unsafe { let align = mem::align_of::(); let size = mem::size_of::() * self.cap; - Some(Layout::from_size_align_unchecked(size, align)) + let layout = Layout::from_size_align_unchecked(size, align); + Some((self.ptr.cast().into(), layout)) } } } - /// Doubles the size of the type's backing allocation. This is common enough - /// to want to do that it's easiest to just have a dedicated method. Slightly - /// more efficient logic can be provided for this than the general case. - /// - /// This function is ideal for when pushing elements one-at-a-time because - /// you don't need to incur the costs of the more general computations - /// reserve needs to do to guard against overflow. You do however need to - /// manually check if your `len == capacity`. - /// - /// # Panics - /// - /// * Panics if `T` is zero-sized on the assumption that you managed to exhaust - /// all `usize::MAX` slots in your imaginary buffer. - /// * Panics on 32-bit platforms if the requested capacity exceeds - /// `isize::MAX` bytes. - /// - /// # Aborts - /// - /// Aborts on OOM - /// - /// # Examples - /// - /// ``` - /// # #![feature(raw_vec_internals)] - /// # extern crate alloc; - /// # use std::ptr; - /// # use alloc::raw_vec::RawVec; - /// struct MyVec { - /// buf: RawVec, - /// len: usize, - /// } - /// - /// impl MyVec { - /// pub fn push(&mut self, elem: T) { - /// if self.len == self.buf.capacity() { self.buf.double(); } - /// // double would have aborted or panicked if the len exceeded - /// // `isize::MAX` so this is safe to do unchecked now. - /// unsafe { - /// ptr::write(self.buf.ptr().add(self.len), elem); - /// } - /// self.len += 1; - /// } - /// } - /// # fn main() { - /// # let mut vec = MyVec { buf: RawVec::new(), len: 0 }; - /// # vec.push(1); - /// # } - /// ``` - #[inline(never)] - #[cold] - pub fn double(&mut self) { - unsafe { - let elem_size = mem::size_of::(); - - // Since we set the capacity to `usize::MAX` when `elem_size` is - // 0, getting to here necessarily means the `RawVec` is overfull. - assert!(elem_size != 0, "capacity overflow"); - - let (ptr, new_cap) = match self.current_layout() { - Some(cur) => { - // Since we guarantee that we never allocate more than - // `isize::MAX` bytes, `elem_size * self.cap <= isize::MAX` as - // a precondition, so this can't overflow. Additionally the - // alignment will never be too large as to "not be - // satisfiable", so `Layout::from_size_align` will always - // return `Some`. - // - // TL;DR, we bypass runtime checks due to dynamic assertions - // in this module, allowing us to use - // `from_size_align_unchecked`. - let new_cap = 2 * self.cap; - let new_size = new_cap * elem_size; - alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); - let ptr_res = self.a.realloc(NonNull::from(self.ptr).cast(), cur, new_size); - match ptr_res { - Ok((ptr, new_size)) => (ptr, new_size / elem_size), - Err(_) => handle_alloc_error(Layout::from_size_align_unchecked( - new_size, - cur.align(), - )), - } - } - None => { - // Skip to 4 because tiny `Vec`'s are dumb; but not if that - // would cause overflow. - let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; - let layout = Layout::array::(new_cap).unwrap(); - match self.a.alloc(layout) { - Ok((ptr, new_size)) => (ptr, new_size / elem_size), - Err(_) => handle_alloc_error(layout), - } - } - }; - self.ptr = ptr.cast().into(); - self.cap = new_cap; - } - } - - /// Attempts to double the size of the type's backing allocation in place. This is common - /// enough to want to do that it's easiest to just have a dedicated method. Slightly - /// more efficient logic can be provided for this than the general case. - /// - /// Returns `true` if the reallocation attempt has succeeded. - /// - /// # Panics - /// - /// * Panics if `T` is zero-sized on the assumption that you managed to exhaust - /// all `usize::MAX` slots in your imaginary buffer. - /// * Panics on 32-bit platforms if the requested capacity exceeds - /// `isize::MAX` bytes. - #[inline(never)] - #[cold] - pub fn double_in_place(&mut self) -> bool { - unsafe { - let elem_size = mem::size_of::(); - let old_layout = match self.current_layout() { - Some(layout) => layout, - None => return false, // nothing to double - }; - - // Since we set the capacity to `usize::MAX` when `elem_size` is - // 0, getting to here necessarily means the `RawVec` is overfull. - assert!(elem_size != 0, "capacity overflow"); - - // Since we guarantee that we never allocate more than `isize::MAX` - // bytes, `elem_size * self.cap <= isize::MAX` as a precondition, so - // this can't overflow. - // - // Similarly to with `double` above, we can go straight to - // `Layout::from_size_align_unchecked` as we know this won't - // overflow and the alignment is sufficiently small. - let new_cap = 2 * self.cap; - let new_size = new_cap * elem_size; - alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); - match self.a.grow_in_place(NonNull::from(self.ptr).cast(), old_layout, new_size) { - Ok(_) => { - // We can't directly divide `size`. - self.cap = new_cap; - true - } - Err(_) => false, - } - } - } - - /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. - pub fn try_reserve_exact( - &mut self, - used_capacity: usize, - needed_extra_capacity: usize, - ) -> Result<(), TryReserveError> { - self.reserve_internal(used_capacity, needed_extra_capacity, Fallible, Exact) - } - - /// Ensures that the buffer contains at least enough space to hold - /// `used_capacity + needed_extra_capacity` 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 `used_capacity` 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 requested capacity exceeds `usize::MAX` bytes. - /// * Panics on 32-bit platforms if the requested capacity exceeds - /// `isize::MAX` bytes. - /// - /// # Aborts - /// - /// Aborts on OOM. - pub fn reserve_exact(&mut self, used_capacity: usize, needed_extra_capacity: usize) { - match self.reserve_internal(used_capacity, needed_extra_capacity, Infallible, Exact) { - Err(CapacityOverflow) => capacity_overflow(), - Err(AllocError { .. }) => unreachable!(), - Ok(()) => { /* yay */ } - } - } - - /// Calculates the buffer's new size given that it'll hold `used_capacity + - /// needed_extra_capacity` elements. This logic is used in amortized reserve methods. - /// Returns `(new_capacity, new_alloc_size)`. - fn amortized_new_size( - &self, - used_capacity: usize, - needed_extra_capacity: usize, - ) -> Result { - // Nothing we can really do about these checks, sadly. - let required_cap = - used_capacity.checked_add(needed_extra_capacity).ok_or(CapacityOverflow)?; - // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`. - let double_cap = self.cap * 2; - // `double_cap` guarantees exponential growth. - Ok(cmp::max(double_cap, required_cap)) - } - - /// The same as `reserve`, but returns on errors instead of panicking or aborting. - pub fn try_reserve( - &mut self, - used_capacity: usize, - needed_extra_capacity: usize, - ) -> Result<(), TryReserveError> { - self.reserve_internal(used_capacity, needed_extra_capacity, Fallible, Amortized) - } - /// Ensures that the buffer contains at least enough space to hold /// `used_capacity + needed_extra_capacity` elements. If it doesn't already have /// enough capacity, will reallocate enough space plus comfortable slack @@ -484,12 +264,26 @@ impl RawVec { /// # } /// ``` pub fn reserve(&mut self, used_capacity: usize, needed_extra_capacity: usize) { - match self.reserve_internal(used_capacity, needed_extra_capacity, Infallible, Amortized) { + match self.try_reserve(used_capacity, needed_extra_capacity) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocError { .. }) => unreachable!(), + Err(AllocError { layout, .. }) => handle_alloc_error(layout), Ok(()) => { /* yay */ } } } + + /// The same as `reserve`, but returns on errors instead of panicking or aborting. + pub fn try_reserve( + &mut self, + used_capacity: usize, + needed_extra_capacity: usize, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(used_capacity, needed_extra_capacity) { + self.grow_amortized(used_capacity, needed_extra_capacity, MayMove) + } else { + Ok(()) + } + } + /// Attempts to ensure that the buffer contains at least enough space to hold /// `used_capacity + needed_extra_capacity` elements. If it doesn't already have /// enough capacity, will reallocate in place enough space plus comfortable slack @@ -508,45 +302,53 @@ impl RawVec { /// * Panics on 32-bit platforms if the requested capacity exceeds /// `isize::MAX` bytes. pub fn reserve_in_place(&mut self, used_capacity: usize, needed_extra_capacity: usize) -> bool { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. If the current `cap` is 0, we can't - // reallocate in place. - // Wrapping in case they give a bad `used_capacity` - let old_layout = match self.current_layout() { - Some(layout) => layout, - None => return false, - }; - if self.capacity().wrapping_sub(used_capacity) >= needed_extra_capacity { - return false; - } + // This is more readable than putting this in one line: + // `!self.needs_to_grow(...) || self.grow(...).is_ok()` + if self.needs_to_grow(used_capacity, needed_extra_capacity) { + self.grow_amortized(used_capacity, needed_extra_capacity, InPlace).is_ok() + } else { + true + } + } - let new_cap = self - .amortized_new_size(used_capacity, needed_extra_capacity) - .unwrap_or_else(|_| capacity_overflow()); - - // Here, `cap < used_capacity + needed_extra_capacity <= new_cap` - // (regardless of whether `self.cap - used_capacity` wrapped). - // Therefore, we can safely call `grow_in_place`. - - let new_layout = Layout::new::().repeat(new_cap).unwrap().0; - // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow()); - match self.a.grow_in_place( - NonNull::from(self.ptr).cast(), - old_layout, - new_layout.size(), - ) { - Ok(_) => { - self.cap = new_cap; - true - } - Err(_) => false, - } + /// Ensures that the buffer contains at least enough space to hold + /// `used_capacity + needed_extra_capacity` 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 `used_capacity` 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 requested capacity exceeds `usize::MAX` bytes. + /// * Panics on 32-bit platforms if the requested capacity exceeds + /// `isize::MAX` bytes. + /// + /// # Aborts + /// + /// Aborts on OOM. + pub fn reserve_exact(&mut self, used_capacity: usize, needed_extra_capacity: usize) { + match self.try_reserve_exact(used_capacity, needed_extra_capacity) { + Err(CapacityOverflow) => capacity_overflow(), + Err(AllocError { layout, .. }) => handle_alloc_error(layout), + Ok(()) => { /* yay */ } + } + } + + /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. + pub fn try_reserve_exact( + &mut self, + used_capacity: usize, + needed_extra_capacity: usize, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(used_capacity, needed_extra_capacity) { + self.grow_exact(used_capacity, needed_extra_capacity) + } else { + Ok(()) } } @@ -561,166 +363,194 @@ impl RawVec { /// /// Aborts on OOM. pub fn shrink_to_fit(&mut self, amount: usize) { - let elem_size = mem::size_of::(); - - // Set the `cap` because they might be about to promote to a `Box<[T]>` - if elem_size == 0 { - self.cap = amount; - return; + match self.shrink(amount, MayMove) { + Err(CapacityOverflow) => capacity_overflow(), + Err(AllocError { layout, .. }) => handle_alloc_error(layout), + Ok(()) => { /* yay */ } } + } +} - // This check is my waterloo; it's the only thing `Vec` wouldn't have to do. - assert!(self.cap >= amount, "Tried to shrink to a larger capacity"); +impl RawVec { + /// Returns if the buffer needs to grow to fulfill the needed extra capacity. + /// Mainly used to make inlining reserve-calls possible without inlining `grow`. + fn needs_to_grow(&self, used_capacity: usize, needed_extra_capacity: usize) -> bool { + needed_extra_capacity > self.capacity().wrapping_sub(used_capacity) + } - if amount == 0 { - // We want to create a new zero-length vector within the - // same allocator. We use `ptr::write` to avoid an - // erroneous attempt to drop the contents, and we use - // `ptr::read` to sidestep condition against destructuring - // types that implement Drop. + fn capacity_from_bytes(excess: usize) -> usize { + debug_assert_ne!(mem::size_of::(), 0); + excess / mem::size_of::() + } - unsafe { - let a = ptr::read(&self.a as *const A); - self.dealloc_buffer(); - ptr::write(self, RawVec::new_in(a)); - } - } else if self.cap != amount { - unsafe { - // We know here that our `amount` is greater than zero. This - // implies, via the assert above, that capacity is also greater - // than zero, which means that we've got a current layout that - // "fits" - // - // We also know that `self.cap` is greater than `amount`, and - // consequently we don't need runtime checks for creating either - // layout. - let old_size = elem_size * self.cap; - let new_size = elem_size * amount; - let align = mem::align_of::(); - let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(NonNull::from(self.ptr).cast(), old_layout, new_size) { - Ok((ptr, _)) => self.ptr = ptr.cast().into(), - Err(_) => { - handle_alloc_error(Layout::from_size_align_unchecked(new_size, align)) - } - } - } - self.cap = amount; - } + fn set_memory(&mut self, memory: MemoryBlock) { + self.ptr = unsafe { Unique::new_unchecked(memory.ptr.cast().as_ptr()) }; + self.cap = Self::capacity_from_bytes(memory.size); } -} -enum Fallibility { - Fallible, - Infallible, -} + // This method is usually instantiated many times. So we want it to be as + // small as possible, to improve compile times. But we also want as much of + // its contents to be statically computable as possible, to make the + // generated code run faster. Therefore, this method is carefully written + // so that all of the code that depends on `T` is within it, while as much + // of the code that doesn't depend on `T` as possible is in functions that + // are non-generic over `T`. + fn grow_amortized( + &mut self, + used_capacity: usize, + needed_extra_capacity: usize, + placement: ReallocPlacement, + ) -> Result<(), TryReserveError> { + // This is ensured by the calling contexts. + debug_assert!(needed_extra_capacity > 0); -use Fallibility::*; + if mem::size_of::() == 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); + } -enum ReserveStrategy { - Exact, - Amortized, -} + // Nothing we can really do about these checks, sadly. + let required_cap = + used_capacity.checked_add(needed_extra_capacity).ok_or(CapacityOverflow)?; -use ReserveStrategy::*; + // 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 * 2, required_cap); -impl RawVec { - fn reserve_internal( + // 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. + // Note that `min_non_zero_cap` is computed statically. + let elem_size = mem::size_of::(); + let min_non_zero_cap = if elem_size == 1 { + 8 + } else if elem_size <= 1024 { + 4 + } else { + 1 + }; + let cap = cmp::max(min_non_zero_cap, cap); + + let new_layout = Layout::array::(cap); + + // `finish_grow` is non-generic over `T`. + let memory = finish_grow(new_layout, placement, self.current_memory(), &mut self.alloc)?; + self.set_memory(memory); + Ok(()) + } + + // The constraints on this method are much the same as those on + // `grow_amortized`, but this method is usually instantiated less often so + // it's less critical. + fn grow_exact( &mut self, used_capacity: usize, needed_extra_capacity: usize, - fallibility: Fallibility, - strategy: ReserveStrategy, ) -> Result<(), TryReserveError> { - let elem_size = mem::size_of::(); + if mem::size_of::() == 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); + } - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they gave a bad `used_capacity`. - if self.capacity().wrapping_sub(used_capacity) >= needed_extra_capacity { - return Ok(()); - } + let cap = used_capacity.checked_add(needed_extra_capacity).ok_or(CapacityOverflow)?; + let new_layout = Layout::array::(cap); - // Nothing we can really do about these checks, sadly. - let new_cap = match strategy { - Exact => { - used_capacity.checked_add(needed_extra_capacity).ok_or(CapacityOverflow)? - } - Amortized => self.amortized_new_size(used_capacity, needed_extra_capacity)?, - }; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; + // `finish_grow` is non-generic over `T`. + let memory = finish_grow(new_layout, MayMove, self.current_memory(), &mut self.alloc)?; + self.set_memory(memory); + Ok(()) + } - alloc_guard(new_layout.size())?; + fn shrink( + &mut self, + amount: usize, + placement: ReallocPlacement, + ) -> Result<(), TryReserveError> { + assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity"); - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).cast(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - let (ptr, new_cap) = match (res, fallibility) { - (Err(AllocErr), Infallible) => handle_alloc_error(new_layout), - (Err(AllocErr), Fallible) => { - return Err(TryReserveError::AllocError { - layout: new_layout, - non_exhaustive: (), - }); - } - (Ok((ptr, new_size)), _) => (ptr, new_size / elem_size), - }; + let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; + let new_size = amount * mem::size_of::(); - self.ptr = ptr.cast().into(); - self.cap = new_cap; + let memory = unsafe { + self.alloc.shrink(ptr, layout, new_size, placement).map_err(|_| { + TryReserveError::AllocError { + layout: Layout::from_size_align_unchecked(new_size, layout.align()), + non_exhaustive: (), + } + })? + }; + self.set_memory(memory); + Ok(()) + } +} - Ok(()) +// This function is outside `RawVec` to minimize compile times. See the comment +// above `RawVec::grow_amortized` for details. (The `A` parameter isn't +// significant, because the number of different `A` types seen in practice is +// much smaller than the number of `T` types.) +fn finish_grow( + new_layout: Result, + placement: ReallocPlacement, + current_memory: Option<(NonNull, Layout)>, + alloc: &mut A, +) -> Result +where + A: AllocRef, +{ + // Check for the error here to minimize the size of `RawVec::grow_*`. + let new_layout = new_layout.map_err(|_| CapacityOverflow)?; + + alloc_guard(new_layout.size())?; + + let memory = if let Some((ptr, old_layout)) = current_memory { + debug_assert_eq!(old_layout.align(), new_layout.align()); + unsafe { alloc.grow(ptr, old_layout, new_layout.size(), placement, Uninitialized) } + } else { + match placement { + MayMove => alloc.alloc(new_layout, Uninitialized), + InPlace => Err(AllocErr), } } + .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?; + + Ok(memory) } impl RawVec { - /// Converts the entire buffer into `Box<[T]>`. + /// 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.) /// - /// # Undefined Behavior - /// - /// All elements of `RawVec` must be initialized. Notice that - /// the rules around uninitialized boxed values are not finalized yet, - /// but until they are, it is advisable to avoid them. - pub unsafe fn into_box(self) -> Box<[T]> { - // NOTE: not calling `capacity()` here; actually using the real `cap` field! - let slice = slice::from_raw_parts_mut(self.ptr(), self.cap); - let output: Box<[T]> = Box::from_raw(slice); - mem::forget(self); - output - } -} + /// # 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 unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit]> { + // 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()`" + ); -impl RawVec { - /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. - pub unsafe fn dealloc_buffer(&mut self) { - let elem_size = mem::size_of::(); - if elem_size != 0 { - if let Some(layout) = self.current_layout() { - self.a.dealloc(NonNull::from(self.ptr).cast(), layout); - } - } + let me = ManuallyDrop::new(self); + let slice = slice::from_raw_parts_mut(me.ptr() as *mut MaybeUninit, len); + Box::from_raw(slice) } } unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec { /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. fn drop(&mut self) { - unsafe { - self.dealloc_buffer(); + if let Some((ptr, layout)) = self.current_memory() { + unsafe { self.alloc.dealloc(ptr, layout) } } } } @@ -736,7 +566,7 @@ unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec { #[inline] fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { - if mem::size_of::() < 8 && alloc_size > core::isize::MAX as usize { + if mem::size_of::() < 8 && alloc_size > isize::MAX as usize { Err(CapacityOverflow) } else { Ok(()) diff --git a/src/liballoc/raw_vec/tests.rs b/src/liballoc/raw_vec/tests.rs index 21a8a76d0a75b..17622d72a059c 100644 --- a/src/liballoc/raw_vec/tests.rs +++ b/src/liballoc/raw_vec/tests.rs @@ -12,6 +12,7 @@ fn allocator_param() { // // Instead, this just checks that the `RawVec` methods do at // least go through the Allocator API when it reserves + // storage. // A dumb allocator that consumes a fixed amount of fuel @@ -20,12 +21,12 @@ fn allocator_param() { fuel: usize, } unsafe impl AllocRef for BoundedAlloc { - fn alloc(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { + fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { let size = layout.size(); if size > self.fuel { return Err(AllocErr); } - match Global.alloc(layout) { + match Global.alloc(layout, init) { ok @ Ok(_) => { self.fuel -= size; ok @@ -40,9 +41,9 @@ fn allocator_param() { let a = BoundedAlloc { fuel: 500 }; let mut v: RawVec = RawVec::with_capacity_in(50, a); - assert_eq!(v.a.fuel, 450); + assert_eq!(v.alloc.fuel, 450); v.reserve(50, 150); // (causes a realloc, thus using 50 + 150 = 200 units of fuel) - assert_eq!(v.a.fuel, 250); + assert_eq!(v.alloc.fuel, 250); } #[test] @@ -58,7 +59,7 @@ fn reserve_does_not_overallocate() { let mut v: RawVec = RawVec::new(); v.reserve(0, 7); assert_eq!(7, v.capacity()); - // 97 if more than double of 7, so `reserve` should work + // 97 is more than double of 7, so `reserve` should work // like `reserve_exact`. v.reserve(7, 90); assert_eq!(97, v.capacity()); diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index e7f7608e676a2..ad96a138d7e55 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -249,10 +249,10 @@ use core::mem::{self, align_of, align_of_val, forget, size_of_val}; use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; use core::pin::Pin; use core::ptr::{self, NonNull}; -use core::slice::{self, from_raw_parts_mut}; -use core::usize; +use core::slice::from_raw_parts_mut; -use crate::alloc::{box_free, handle_alloc_error, AllocRef, Global, Layout}; +use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout}; +use crate::borrow::{Cow, ToOwned}; use crate::string::String; use crate::vec::Vec; @@ -280,7 +280,7 @@ struct RcBox { /// type `T`. /// /// [get_mut]: #method.get_mut -#[cfg_attr(not(test), lang = "rc")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Rc")] #[stable(feature = "rust1", since = "1.0.0")] pub struct Rc { ptr: NonNull>, @@ -324,11 +324,9 @@ impl Rc { // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. - Self::from_inner(Box::into_raw_non_null(box RcBox { - strong: Cell::new(1), - weak: Cell::new(1), - value, - })) + Self::from_inner( + Box::leak(box RcBox { strong: Cell::new(1), weak: Cell::new(1), value }).into(), + ) } /// Constructs a new `Rc` with uninitialized contents. @@ -569,9 +567,33 @@ impl Rc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub fn into_raw(this: Self) -> *const T { + let ptr = Self::as_ptr(&this); + mem::forget(this); + ptr + } + + /// Provides a raw pointer to the data. + /// + /// The counts are not affected in any way and the `Rc` is not consumed. The pointer is valid + /// for as long there are strong counts in the `Rc`. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::rc::Rc; + /// + /// let x = Rc::new("hello".to_owned()); + /// let y = Rc::clone(&x); + /// let x_ptr = Rc::as_ptr(&x); + /// assert_eq!(x_ptr, Rc::as_ptr(&y)); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// ``` + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub fn as_ptr(this: &Self) -> *const T { let ptr: *mut RcBox = NonNull::as_ptr(this.ptr); let fake_ptr = ptr as *mut T; - mem::forget(this); // SAFETY: This cannot go through Deref::deref. // Instead, we manually offset the pointer rather than manifesting a reference. @@ -638,6 +660,7 @@ impl Rc { /// /// ``` /// #![feature(rc_into_raw_non_null)] + /// #![allow(deprecated)] /// /// use std::rc::Rc; /// @@ -647,6 +670,7 @@ impl Rc { /// assert_eq!(deref, "hello"); /// ``` #[unstable(feature = "rc_into_raw_non_null", issue = "47336")] + #[rustc_deprecated(since = "1.44.0", reason = "use `Rc::into_raw` instead")] #[inline] pub fn into_raw_non_null(this: Self) -> NonNull { // safe because Rc guarantees its pointer is non-null @@ -936,10 +960,12 @@ impl Rc { let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); // Allocate for the layout. - let (mem, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + let mem = Global + .alloc(layout, AllocInit::Uninitialized) + .unwrap_or_else(|_| handle_alloc_error(layout)); // Initialize the RcBox - let inner = mem_to_rcbox(mem.as_ptr()); + let inner = mem_to_rcbox(mem.ptr.as_ptr()); debug_assert_eq!(Layout::for_value(&*inner), layout); ptr::write(&mut (*inner).strong, Cell::new(1)); @@ -998,7 +1024,7 @@ unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { } impl Rc<[T]> { - /// Copy elements from slice into newly allocated Rc<[T]> + /// Copy elements from slice into newly allocated Rc<\[T\]> /// /// Unsafe because the caller must either take ownership or bind `T: Copy` unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { @@ -1196,6 +1222,12 @@ impl RcEqIdent for Rc { } } +// Hack to allow specializing on `Eq` even though `Eq` has a method. +#[rustc_unsafe_specialization_marker] +pub(crate) trait MarkerEq: PartialEq {} + +impl MarkerEq for T {} + /// We're doing this specialization here, and not as a more general optimization on `&T`, because it /// would otherwise add a cost to all equality checks on refs. We assume that `Rc`s are used to /// store large values, that are slow to clone, but also heavy to check for equality, causing this @@ -1204,7 +1236,7 @@ impl RcEqIdent for Rc { /// /// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. #[stable(feature = "rust1", since = "1.0.0")] -impl RcEqIdent for Rc { +impl RcEqIdent for Rc { #[inline] fn eq(&self, other: &Rc) -> bool { Rc::ptr_eq(self, other) || **self == **other @@ -1466,6 +1498,21 @@ impl From> for Rc<[T]> { } } +#[stable(feature = "shared_from_cow", since = "1.45.0")] +impl<'a, B> From> for Rc +where + B: ToOwned + ?Sized, + Rc: From<&'a B> + From, +{ + #[inline] + fn from(cow: Cow<'a, B>) -> Rc { + match cow { + Cow::Borrowed(s) => Rc::from(s), + Cow::Owned(s) => Rc::from(s), + } + } +} + #[stable(feature = "boxed_slice_try_from", since = "1.43.0")] impl TryFrom> for Rc<[T; N]> where @@ -1523,25 +1570,25 @@ impl iter::FromIterator for Rc<[T]> { /// # assert_eq!(&*evens, &*(0..10).collect::>()); /// ``` fn from_iter>(iter: I) -> Self { - RcFromIter::from_iter(iter.into_iter()) + ToRcSlice::to_rc_slice(iter.into_iter()) } } /// Specialization trait used for collecting into `Rc<[T]>`. -trait RcFromIter { - fn from_iter(iter: I) -> Self; +trait ToRcSlice: Iterator + Sized { + fn to_rc_slice(self) -> Rc<[T]>; } -impl> RcFromIter for Rc<[T]> { - default fn from_iter(iter: I) -> Self { - iter.collect::>().into() +impl> ToRcSlice for I { + default fn to_rc_slice(self) -> Rc<[T]> { + self.collect::>().into() } } -impl> RcFromIter for Rc<[T]> { - default fn from_iter(iter: I) -> Self { +impl> ToRcSlice for I { + fn to_rc_slice(self) -> Rc<[T]> { // This is the case for a `TrustedLen` iterator. - let (low, high) = iter.size_hint(); + let (low, high) = self.size_hint(); if let Some(high) = high { debug_assert_eq!( low, @@ -1552,29 +1599,15 @@ impl> RcFromIter for Rc<[T]> { unsafe { // SAFETY: We need to ensure that the iterator has an exact length and we have. - Rc::from_iter_exact(iter, low) + Rc::from_iter_exact(self, low) } } else { // Fall back to normal implementation. - iter.collect::>().into() + self.collect::>().into() } } } -impl<'a, T: 'a + Clone> RcFromIter<&'a T, slice::Iter<'a, T>> for Rc<[T]> { - fn from_iter(iter: slice::Iter<'a, T>) -> Self { - // Delegate to `impl From<&[T]> for Rc<[T]>`. - // - // In the case that `T: Copy`, we get to use `ptr::copy_nonoverlapping` - // which is even more performant. - // - // In the fall-back case we have `T: Clone`. This is still better - // than the `TrustedLen` implementation as slices have a known length - // and so we get to avoid calling `size_hint` and avoid the branching. - iter.as_slice().into() - } -} - /// `Weak` is a version of [`Rc`] that holds a non-owning reference to the /// managed allocation. The allocation is accessed by calling [`upgrade`] on the `Weak` /// pointer, which returns an [`Option`]`<`[`Rc`]`>`. @@ -1642,8 +1675,8 @@ impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// - /// The pointer is valid only if there are some strong references. The pointer may be dangling - /// or even [`null`] otherwise. + /// The pointer is valid only if there are some strong references. The pointer may be dangling, + /// unaligned or even [`null`] otherwise. /// /// # Examples /// @@ -1656,31 +1689,22 @@ impl Weak { /// let strong = Rc::new("hello".to_owned()); /// let weak = Rc::downgrade(&strong); /// // Both point to the same object - /// assert!(ptr::eq(&*strong, weak.as_raw())); + /// assert!(ptr::eq(&*strong, weak.as_ptr())); /// // The strong here keeps it alive, so we can still access the object. - /// assert_eq!("hello", unsafe { &*weak.as_raw() }); + /// assert_eq!("hello", unsafe { &*weak.as_ptr() }); /// /// drop(strong); - /// // But not any more. We can do weak.as_raw(), but accessing the pointer would lead to + /// // But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to /// // undefined behaviour. - /// // assert_eq!("hello", unsafe { &*weak.as_raw() }); + /// // assert_eq!("hello", unsafe { &*weak.as_ptr() }); /// ``` /// /// [`null`]: ../../std/ptr/fn.null.html #[unstable(feature = "weak_into_raw", issue = "60728")] - pub fn as_raw(&self) -> *const T { - match self.inner() { - None => ptr::null(), - Some(inner) => { - let offset = data_offset_sized::(); - let ptr = inner as *const RcBox; - // Note: while the pointer we create may already point to dropped value, the - // allocation still lives (it must hold the weak point as long as we are alive). - // Therefore, the offset is OK to do, it won't get out of the allocation. - let ptr = unsafe { (ptr as *const u8).offset(offset) }; - ptr as *const T - } - } + pub fn as_ptr(&self) -> *const T { + let offset = data_offset_sized::(); + let ptr = self.ptr.cast::().as_ptr().wrapping_offset(offset); + ptr as *const T } /// Consumes the `Weak` and turns it into a raw pointer. @@ -1689,7 +1713,7 @@ impl Weak { /// can be turned back into the `Weak` with [`from_raw`]. /// /// The same restrictions of accessing the target of the pointer as with - /// [`as_raw`] apply. + /// [`as_ptr`] apply. /// /// # Examples /// @@ -1710,10 +1734,10 @@ impl Weak { /// ``` /// /// [`from_raw`]: struct.Weak.html#method.from_raw - /// [`as_raw`]: struct.Weak.html#method.as_raw + /// [`as_ptr`]: struct.Weak.html#method.as_ptr #[unstable(feature = "weak_into_raw", issue = "60728")] pub fn into_raw(self) -> *const T { - let result = self.as_raw(); + let result = self.as_ptr(); mem::forget(self); result } @@ -1728,9 +1752,8 @@ impl Weak { /// /// # Safety /// - /// The pointer must have originated from the [`into_raw`] (or [`as_raw`], provided there was - /// a corresponding [`forget`] on the `Weak`) and must still own its potential weak reference - /// count. + /// The pointer must have originated from the [`into_raw`] and must still own its potential + /// weak reference count. /// /// It is allowed for the strong count to be 0 at the time of calling this, but the weak count /// must be non-zero or the pointer must have originated from a dangling `Weak` (one created @@ -1763,7 +1786,6 @@ impl Weak { /// [`upgrade`]: struct.Weak.html#method.upgrade /// [`Rc`]: struct.Rc.html /// [`Weak`]: struct.Weak.html - /// [`as_raw`]: struct.Weak.html#method.as_raw /// [`new`]: struct.Weak.html#method.new /// [`forget`]: ../../std/mem/fn.forget.html #[unstable(feature = "weak_into_raw", issue = "60728")] @@ -2021,6 +2043,8 @@ trait RcBoxPtr { // nevertheless, we insert an abort here to hint LLVM at // an otherwise missed optimization. if strong == 0 || strong == usize::max_value() { + // remove `unsafe` on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] unsafe { abort(); } @@ -2047,6 +2071,8 @@ trait RcBoxPtr { // nevertheless, we insert an abort here to hint LLVM at // an otherwise missed optimization. if weak == 0 || weak == usize::max_value() { + // remove `unsafe` on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] unsafe { abort(); } diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index d8fc1faca3a39..53477288b59ee 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -90,7 +90,6 @@ use core::borrow::{Borrow, BorrowMut}; use core::cmp::Ordering::{self, Less}; use core::mem::{self, size_of}; use core::ptr; -use core::{u16, u32, u8}; use crate::borrow::ToOwned; use crate::boxed::Box; @@ -141,6 +140,9 @@ mod hack { use crate::string::ToString; use crate::vec::Vec; + // We shouldn't add inline attribute to this since this is used in + // `vec!` macro mostly and causes perf regression. See #71204 for + // discussion and perf results. pub fn into_vec(b: Box<[T]>) -> Vec { unsafe { let len = b.len(); @@ -165,7 +167,7 @@ mod hack { impl [T] { /// Sorts the slice. /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(n log n)` worst-case. + /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. @@ -200,7 +202,7 @@ impl [T] { /// Sorts the slice with a comparator function. /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(n log n)` worst-case. + /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. /// /// The comparator function must define a total ordering for the elements in the slice. If /// the ordering is not total, the order of the elements is unspecified. An order is a @@ -254,7 +256,7 @@ impl [T] { /// Sorts the slice with a key extraction function. /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(m n log(m n))` + /// This sort is stable (i.e., does not reorder equal elements) and `O(m * n * log(n))` /// worst-case, where the key function is `O(m)`. /// /// For expensive key functions (e.g. functions that are not simple property accesses or @@ -297,7 +299,7 @@ impl [T] { /// /// During sorting, the key function is called only once per element. /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(m n + n log n)` + /// This sort is stable (i.e., does not reorder equal elements) and `O(m * n + n * log(n))` /// worst-case, where the key function is `O(m)`. /// /// For simple key functions (e.g., functions that are property accesses or @@ -432,7 +434,7 @@ impl [T] { /// /// ```should_panic /// // this will panic at runtime - /// b"0123456789abcdef".repeat(usize::max_value()); + /// b"0123456789abcdef".repeat(usize::MAX); /// ``` #[stable(feature = "repeat_generic_slice", since = "1.40.0")] pub fn repeat(&self, n: usize) -> Vec @@ -734,14 +736,14 @@ impl ToOwned for [T] { fn clone_into(&self, target: &mut Vec) { // drop anything in target that will not be overwritten target.truncate(self.len()); - let len = target.len(); - - // reuse the contained values' allocations/resources. - target.clone_from_slice(&self[..len]); // target.len <= self.len due to the truncate above, so the - // slice here is always in-bounds. - target.extend_from_slice(&self[len..]); + // slices here are always in-bounds. + let (init, tail) = self.split_at(target.len()); + + // reuse the contained values' allocations/resources. + target.clone_from_slice(init); + target.extend_from_slice(tail); } } @@ -935,7 +937,7 @@ where /// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` /// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` /// -/// The invariants ensure that the total running time is `O(n log n)` worst-case. +/// The invariants ensure that the total running time is `O(n * log(n))` worst-case. fn merge_sort(v: &mut [T], mut is_less: F) where F: FnMut(&T, &T) -> bool, diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 843a2f1f8e9fc..70860c09a2c31 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -499,7 +499,7 @@ impl str { /// /// ```should_panic /// // this will panic at runtime - /// "0123456789abcdef".repeat(usize::max_value()); + /// "0123456789abcdef".repeat(usize::MAX); /// ``` #[stable(feature = "repeat_str", since = "1.16.0")] pub fn repeat(&self, n: usize) -> String { diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 0e48f1548e6d1..f3fe1adebb141 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -278,6 +278,7 @@ use crate::vec::Vec; /// [`Deref`]: ../../std/ops/trait.Deref.html /// [`as_str()`]: struct.String.html#method.as_str #[derive(PartialOrd, Eq, Ord)] +#[cfg_attr(not(test), rustc_diagnostic_item = "string_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct String { vec: Vec, @@ -482,6 +483,7 @@ impl String { /// [`String`]: struct.String.html /// [`u8`]: ../../std/primitive.u8.html /// [`Vec`]: ../../std/vec/struct.Vec.html + /// [`&str`]: ../../std/primitive.str.html /// [`str::from_utf8`]: ../../std/str/fn.from_utf8.html /// [`into_bytes`]: struct.String.html#method.into_bytes /// [`FromUtf8Error`]: struct.FromUtf8Error.html @@ -1461,6 +1463,7 @@ impl String { /// ``` #[inline] #[stable(feature = "string_split_off", since = "1.16.0")] + #[must_use = "use `.truncate()` if you don't need the other half"] pub fn split_off(&mut self, at: usize) -> String { assert!(self.is_char_boundary(at)); let other = self.vec.split_off(at); @@ -1826,7 +1829,13 @@ impl<'a> Extend> for String { } } -/// A convenience impl that delegates to the impl for `&str` +/// A convenience impl that delegates to the impl for `&str`. +/// +/// # Examples +/// +/// ``` +/// assert_eq!(String::from("Hello world").find("world"), Some(6)); +/// ``` #[unstable( feature = "pattern", reason = "API not fully fleshed out and ready to be stabilized", @@ -1848,6 +1857,21 @@ impl<'a, 'b> Pattern<'a> for &'b String { fn is_prefix_of(self, haystack: &'a str) -> bool { self[..].is_prefix_of(haystack) } + + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + self[..].strip_prefix_of(haystack) + } + + #[inline] + fn is_suffix_of(self, haystack: &'a str) -> bool { + self[..].is_suffix_of(haystack) + } + + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + self[..].strip_suffix_of(haystack) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index e8985e202567b..b7be8042ea49f 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -20,12 +20,12 @@ use core::mem::{self, align_of, align_of_val, size_of_val}; use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; use core::pin::Pin; use core::ptr::{self, NonNull}; -use core::slice::{self, from_raw_parts_mut}; +use core::slice::from_raw_parts_mut; use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; -use core::{isize, usize}; -use crate::alloc::{box_free, handle_alloc_error, AllocRef, Global, Layout}; +use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout}; +use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::is_dangling; use crate::string::String; @@ -208,7 +208,7 @@ macro_rules! acquire { /// counting in general. /// /// [rc_examples]: ../../std/rc/index.html#examples -#[cfg_attr(not(test), lang = "arc")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] #[stable(feature = "rust1", since = "1.0.0")] pub struct Arc { ptr: NonNull>, @@ -325,7 +325,7 @@ impl Arc { weak: atomic::AtomicUsize::new(1), data, }; - Self::from_inner(Box::into_raw_non_null(x)) + Self::from_inner(Box::leak(x).into()) } /// Constructs a new `Arc` with uninitialized contents. @@ -566,9 +566,33 @@ impl Arc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub fn into_raw(this: Self) -> *const T { + let ptr = Self::as_ptr(&this); + mem::forget(this); + ptr + } + + /// Provides a raw pointer to the data. + /// + /// The counts are not affected in way and the `Arc` is not consumed. The pointer is valid for + /// as long as there are strong counts in the `Arc`. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let y = Arc::clone(&x); + /// let x_ptr = Arc::as_ptr(&x); + /// assert_eq!(x_ptr, Arc::as_ptr(&y)); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// ``` + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub fn as_ptr(this: &Self) -> *const T { let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); let fake_ptr = ptr as *mut T; - mem::forget(this); // SAFETY: This cannot go through Deref::deref. // Instead, we manually offset the pointer rather than manifesting a reference. @@ -635,6 +659,7 @@ impl Arc { /// /// ``` /// #![feature(rc_into_raw_non_null)] + /// #![allow(deprecated)] /// /// use std::sync::Arc; /// @@ -644,6 +669,7 @@ impl Arc { /// assert_eq!(deref, "hello"); /// ``` #[unstable(feature = "rc_into_raw_non_null", issue = "47336")] + #[rustc_deprecated(since = "1.44.0", reason = "use `Arc::into_raw` instead")] #[inline] pub fn into_raw_non_null(this: Self) -> NonNull { // safe because Arc guarantees its pointer is non-null @@ -751,6 +777,81 @@ impl Arc { this.inner().strong.load(SeqCst) } + /// Increments the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw`, and the + /// associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_mutate_strong_count)] + /// + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// unsafe { + /// let ptr = Arc::into_raw(five); + /// Arc::incr_strong_count(ptr); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] + pub unsafe fn incr_strong_count(ptr: *const T) { + // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop + let arc = mem::ManuallyDrop::new(Arc::::from_raw(ptr)); + // Now increase refcount, but don't drop new refcount either + let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); + } + + /// Decrements the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw`, and the + /// associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) when invoking this method. This method can be used to release the final + /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been + /// released. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_mutate_strong_count)] + /// + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// unsafe { + /// let ptr = Arc::into_raw(five); + /// Arc::incr_strong_count(ptr); + /// + /// // Those assertions are deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// Arc::decr_strong_count(ptr); + /// assert_eq!(1, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] + pub unsafe fn decr_strong_count(ptr: *const T) { + mem::drop(Arc::from_raw(ptr)); + } + #[inline] fn inner(&self) -> &ArcInner { // This unsafety is ok because while this arc is alive we're guaranteed @@ -814,10 +915,12 @@ impl Arc { // reference (see #54908). let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); - let (mem, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + let mem = Global + .alloc(layout, AllocInit::Uninitialized) + .unwrap_or_else(|_| handle_alloc_error(layout)); // Initialize the ArcInner - let inner = mem_to_arcinner(mem.as_ptr()); + let inner = mem_to_arcinner(mem.ptr.as_ptr()); debug_assert_eq!(Layout::for_value(&*inner), layout); ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); @@ -876,7 +979,7 @@ unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { } impl Arc<[T]> { - /// Copy elements from slice into newly allocated Arc<[T]> + /// Copy elements from slice into newly allocated Arc<\[T\]> /// /// Unsafe because the caller must either take ownership or bind `T: Copy`. unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { @@ -994,6 +1097,8 @@ impl Clone for Arc { // We abort because such a program is incredibly degenerate, and we // don't care to support it. if old_size > MAX_REFCOUNT { + // remove `unsafe` on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] unsafe { abort(); } @@ -1338,8 +1443,8 @@ impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// - /// The pointer is valid only if there are some strong references. The pointer may be dangling - /// or even [`null`] otherwise. + /// The pointer is valid only if there are some strong references. The pointer may be dangling, + /// unaligned or even [`null`] otherwise. /// /// # Examples /// @@ -1352,31 +1457,22 @@ impl Weak { /// let strong = Arc::new("hello".to_owned()); /// let weak = Arc::downgrade(&strong); /// // Both point to the same object - /// assert!(ptr::eq(&*strong, weak.as_raw())); + /// assert!(ptr::eq(&*strong, weak.as_ptr())); /// // The strong here keeps it alive, so we can still access the object. - /// assert_eq!("hello", unsafe { &*weak.as_raw() }); + /// assert_eq!("hello", unsafe { &*weak.as_ptr() }); /// /// drop(strong); - /// // But not any more. We can do weak.as_raw(), but accessing the pointer would lead to + /// // But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to /// // undefined behaviour. - /// // assert_eq!("hello", unsafe { &*weak.as_raw() }); + /// // assert_eq!("hello", unsafe { &*weak.as_ptr() }); /// ``` /// /// [`null`]: ../../std/ptr/fn.null.html #[unstable(feature = "weak_into_raw", issue = "60728")] - pub fn as_raw(&self) -> *const T { - match self.inner() { - None => ptr::null(), - Some(inner) => { - let offset = data_offset_sized::(); - let ptr = inner as *const ArcInner; - // Note: while the pointer we create may already point to dropped value, the - // allocation still lives (it must hold the weak point as long as we are alive). - // Therefore, the offset is OK to do, it won't get out of the allocation. - let ptr = unsafe { (ptr as *const u8).offset(offset) }; - ptr as *const T - } - } + pub fn as_ptr(&self) -> *const T { + let offset = data_offset_sized::(); + let ptr = self.ptr.cast::().as_ptr().wrapping_offset(offset); + ptr as *const T } /// Consumes the `Weak` and turns it into a raw pointer. @@ -1385,7 +1481,7 @@ impl Weak { /// can be turned back into the `Weak` with [`from_raw`]. /// /// The same restrictions of accessing the target of the pointer as with - /// [`as_raw`] apply. + /// [`as_ptr`] apply. /// /// # Examples /// @@ -1406,10 +1502,10 @@ impl Weak { /// ``` /// /// [`from_raw`]: struct.Weak.html#method.from_raw - /// [`as_raw`]: struct.Weak.html#method.as_raw + /// [`as_ptr`]: struct.Weak.html#method.as_ptr #[unstable(feature = "weak_into_raw", issue = "60728")] pub fn into_raw(self) -> *const T { - let result = self.as_raw(); + let result = self.as_ptr(); mem::forget(self); result } @@ -1425,9 +1521,8 @@ impl Weak { /// /// # Safety /// - /// The pointer must have originated from the [`into_raw`] (or [`as_raw'], provided there was - /// a corresponding [`forget`] on the `Weak`) and must still own its potential weak reference - /// count. + /// The pointer must have originated from the [`into_raw`] and must still own its potential + /// weak reference count. /// /// It is allowed for the strong count to be 0 at the time of calling this, but the weak count /// must be non-zero or the pointer must have originated from a dangling `Weak` (one created @@ -1456,7 +1551,6 @@ impl Weak { /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); /// ``` /// - /// [`as_raw`]: struct.Weak.html#method.as_raw /// [`new`]: struct.Weak.html#method.new /// [`into_raw`]: struct.Weak.html#method.into_raw /// [`upgrade`]: struct.Weak.html#method.upgrade @@ -1523,6 +1617,8 @@ impl Weak { // See comments in `Arc::clone` for why we do this (for `mem::forget`). if n > MAX_REFCOUNT { + // remove `unsafe` on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] unsafe { abort(); } @@ -1662,6 +1758,7 @@ impl Clone for Weak { // See comments in Arc::clone() for why we do this (for mem::forget). if old_size > MAX_REFCOUNT { + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump unsafe { abort(); } @@ -1763,7 +1860,7 @@ impl ArcEqIdent for Arc { /// /// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. #[stable(feature = "rust1", since = "1.0.0")] -impl ArcEqIdent for Arc { +impl ArcEqIdent for Arc { #[inline] fn eq(&self, other: &Arc) -> bool { Arc::ptr_eq(self, other) || **self == **other @@ -2032,6 +2129,21 @@ impl From> for Arc<[T]> { } } +#[stable(feature = "shared_from_cow", since = "1.45.0")] +impl<'a, B> From> for Arc +where + B: ToOwned + ?Sized, + Arc: From<&'a B> + From, +{ + #[inline] + fn from(cow: Cow<'a, B>) -> Arc { + match cow { + Cow::Borrowed(s) => Arc::from(s), + Cow::Owned(s) => Arc::from(s), + } + } +} + #[stable(feature = "boxed_slice_try_from", since = "1.43.0")] impl TryFrom> for Arc<[T; N]> where @@ -2089,25 +2201,25 @@ impl iter::FromIterator for Arc<[T]> { /// # assert_eq!(&*evens, &*(0..10).collect::>()); /// ``` fn from_iter>(iter: I) -> Self { - ArcFromIter::from_iter(iter.into_iter()) + ToArcSlice::to_arc_slice(iter.into_iter()) } } /// Specialization trait used for collecting into `Arc<[T]>`. -trait ArcFromIter { - fn from_iter(iter: I) -> Self; +trait ToArcSlice: Iterator + Sized { + fn to_arc_slice(self) -> Arc<[T]>; } -impl> ArcFromIter for Arc<[T]> { - default fn from_iter(iter: I) -> Self { - iter.collect::>().into() +impl> ToArcSlice for I { + default fn to_arc_slice(self) -> Arc<[T]> { + self.collect::>().into() } } -impl> ArcFromIter for Arc<[T]> { - default fn from_iter(iter: I) -> Self { +impl> ToArcSlice for I { + fn to_arc_slice(self) -> Arc<[T]> { // This is the case for a `TrustedLen` iterator. - let (low, high) = iter.size_hint(); + let (low, high) = self.size_hint(); if let Some(high) = high { debug_assert_eq!( low, @@ -2118,29 +2230,15 @@ impl> ArcFromIter for Arc<[T]> { unsafe { // SAFETY: We need to ensure that the iterator has an exact length and we have. - Arc::from_iter_exact(iter, low) + Arc::from_iter_exact(self, low) } } else { // Fall back to normal implementation. - iter.collect::>().into() + self.collect::>().into() } } } -impl<'a, T: 'a + Clone> ArcFromIter<&'a T, slice::Iter<'a, T>> for Arc<[T]> { - fn from_iter(iter: slice::Iter<'a, T>) -> Self { - // Delegate to `impl From<&[T]> for Arc<[T]>`. - // - // In the case that `T: Copy`, we get to use `ptr::copy_nonoverlapping` - // which is even more performant. - // - // In the fall-back case we have `T: Clone`. This is still better - // than the `TrustedLen` implementation as slices have a known length - // and so we get to avoid calling `size_hint` and avoid the branching. - iter.as_slice().into() - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl borrow::Borrow for Arc { fn borrow(&self) -> &T { diff --git a/src/liballoc/sync/tests.rs b/src/liballoc/sync/tests.rs index edc2820ee22f1..a2bb651e2b778 100644 --- a/src/liballoc/sync/tests.rs +++ b/src/liballoc/sync/tests.rs @@ -32,7 +32,6 @@ impl Drop for Canary { #[test] #[cfg_attr(target_os = "emscripten", ignore)] -#[cfg_attr(miri, ignore)] // Miri does not support threads fn manually_share_arc() { let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let arc_v = Arc::new(v); @@ -337,12 +336,13 @@ fn test_ptr_eq() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] -#[cfg_attr(miri, ignore)] // Miri does not support threads fn test_weak_count_locked() { let mut a = Arc::new(atomic::AtomicBool::new(false)); let a2 = a.clone(); let t = thread::spawn(move || { - for _i in 0..1000000 { + // Miri is too slow + let count = if cfg!(miri) { 1000 } else { 1000000 }; + for _i in 0..count { Arc::get_mut(&mut a); } a.store(true, SeqCst); @@ -351,6 +351,8 @@ fn test_weak_count_locked() { while !a2.load(SeqCst) { let n = Arc::weak_count(&a2); assert!(n < 2, "bad weak count: {}", n); + #[cfg(miri)] // Miri's scheduler does not guarantee liveness, and thus needs this hint. + atomic::spin_loop_hint(); } t.join().unwrap(); } diff --git a/src/liballoc/task.rs b/src/liballoc/task.rs index 981095302c730..745444a152e1b 100644 --- a/src/liballoc/task.rs +++ b/src/liballoc/task.rs @@ -1,6 +1,6 @@ #![unstable(feature = "wake_trait", issue = "69912")] //! Types and Traits for working with asynchronous tasks. -use core::mem::{self, ManuallyDrop}; +use core::mem::ManuallyDrop; use core::task::{RawWaker, RawWakerVTable, Waker}; use crate::sync::Arc; @@ -12,10 +12,12 @@ use crate::sync::Arc; /// to the tasks that are executed on that executor. /// /// This trait is a memory-safe and ergonomic alternative to constructing a -/// [`RawWaker`]. It supports the common executor design in which the data -/// used to wake up a task is stored in an [`Arc`]. Some executors (especially +/// [`RawWaker`]. It supports the common executor design in which the data used +/// to wake up a task is stored in an [`Arc`][arc]. Some executors (especially /// those for embedded systems) cannot use this API, which is why [`RawWaker`] /// exists as an alternative for those systems. +/// +/// [arc]: ../../std/sync/struct.Arc.html #[unstable(feature = "wake_trait", issue = "69912")] pub trait Wake { /// Wake this task. @@ -58,9 +60,11 @@ impl From> for RawWaker { fn raw_waker(waker: Arc) -> RawWaker { // Increment the reference count of the arc to clone it. unsafe fn clone_waker(waker: *const ()) -> RawWaker { - let waker: Arc = Arc::from_raw(waker as *const W); - mem::forget(Arc::clone(&waker)); - raw_waker(waker) + Arc::incr_strong_count(waker as *const W); + RawWaker::new( + waker as *const (), + &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + ) } // Wake by value, moving the Arc into the Wake::wake function @@ -77,7 +81,7 @@ fn raw_waker(waker: Arc) -> RawWaker { // Decrement the reference count of the Arc on drop unsafe fn drop_waker(waker: *const ()) { - mem::drop(Arc::from_raw(waker as *const W)); + Arc::decr_strong_count(waker as *const W); } RawWaker::new( diff --git a/src/liballoc/tests.rs b/src/liballoc/tests.rs index 1b6e0bb291c35..bddaab0c76188 100644 --- a/src/liballoc/tests.rs +++ b/src/liballoc/tests.rs @@ -3,8 +3,6 @@ use core::any::Any; use core::clone::Clone; use core::convert::TryInto; -use core::f64; -use core::i64; use core::ops::Deref; use core::result::Result::{Err, Ok}; diff --git a/src/liballoc/tests/arc.rs b/src/liballoc/tests/arc.rs index 34384cfcba96b..c02ba267056d6 100644 --- a/src/liballoc/tests/arc.rs +++ b/src/liballoc/tests/arc.rs @@ -50,7 +50,7 @@ fn trait_object() { #[test] fn float_nan_ne() { - let x = Arc::new(std::f32::NAN); + let x = Arc::new(f32::NAN); assert!(x != x); assert!(!(x == x)); } diff --git a/src/liballoc/tests/binary_heap.rs b/src/liballoc/tests/binary_heap.rs index be5516f54f37b..62084ccf53c59 100644 --- a/src/liballoc/tests/binary_heap.rs +++ b/src/liballoc/tests/binary_heap.rs @@ -372,6 +372,14 @@ fn assert_covariance() { } } +#[test] +fn test_retain() { + let mut a = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); + a.retain(|x| x % 2 == 0); + + assert_eq!(a.into_sorted_vec(), [-10, 2, 4]) +} + // old binaryheap failed this test // // Integrity means that all elements are present after a comparison panics, @@ -409,16 +417,14 @@ fn panic_safe() { } let mut rng = thread_rng(); const DATASZ: usize = 32; - #[cfg(not(miri))] // Miri is too slow - const NTEST: usize = 10; - #[cfg(miri)] - const NTEST: usize = 1; + // Miri is too slow + let ntest = if cfg!(miri) { 1 } else { 10 }; // don't use 0 in the data -- we want to catch the zeroed-out case. let data = (1..=DATASZ).collect::>(); // since it's a fuzzy test, run several tries. - for _ in 0..NTEST { + for _ in 0..ntest { for i in 1..=DATASZ { DROP_COUNTER.store(0, Ordering::SeqCst); diff --git a/src/liballoc/tests/borrow.rs b/src/liballoc/tests/borrow.rs new file mode 100644 index 0000000000000..8bfcf323f674a --- /dev/null +++ b/src/liballoc/tests/borrow.rs @@ -0,0 +1,47 @@ +use std::borrow::{Cow, ToOwned}; +use std::ffi::{CStr, OsStr}; +use std::path::Path; +use std::rc::Rc; +use std::sync::Arc; + +macro_rules! test_from_cow { + ($value:ident => $($ty:ty),+) => {$( + let borrowed = <$ty>::from(Cow::Borrowed($value)); + let owned = <$ty>::from(Cow::Owned($value.to_owned())); + assert_eq!($value, &*borrowed); + assert_eq!($value, &*owned); + )+}; + ($value:ident : & $ty:ty) => { + test_from_cow!($value => Box<$ty>, Rc<$ty>, Arc<$ty>); + } +} + +#[test] +fn test_from_cow_slice() { + let slice: &[i32] = &[1, 2, 3]; + test_from_cow!(slice: &[i32]); +} + +#[test] +fn test_from_cow_str() { + let string = "hello"; + test_from_cow!(string: &str); +} + +#[test] +fn test_from_cow_c_str() { + let string = CStr::from_bytes_with_nul(b"hello\0").unwrap(); + test_from_cow!(string: &CStr); +} + +#[test] +fn test_from_cow_os_str() { + let string = OsStr::new("hello"); + test_from_cow!(string: &OsStr); +} + +#[test] +fn test_from_cow_path() { + let path = Path::new("hello"); + test_from_cow!(path: &Path); +} diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs index 3a3462d546f7a..731a1b5f875b7 100644 --- a/src/liballoc/tests/btree/map.rs +++ b/src/liballoc/tests/btree/map.rs @@ -5,19 +5,31 @@ use std::fmt::Debug; use std::iter::FromIterator; use std::ops::Bound::{self, Excluded, Included, Unbounded}; use std::ops::RangeBounds; -use std::panic::catch_unwind; +use std::panic::{catch_unwind, AssertUnwindSafe}; use std::rc::Rc; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering}; use super::DeterministicRng; +// Value of node::CAPACITY, thus capacity of a tree with a single level, +// i.e. a tree who's root is a leaf node at height 0. +const NODE_CAPACITY: usize = 11; + +// Minimum number of elements to insert in order to guarantee a tree with 2 levels, +// i.e. a tree who's root is an internal node at height 1, with edges to leaf nodes. +// It's not the minimum size: removing an element from such a tree does not always reduce height. +const MIN_INSERTS_HEIGHT_1: usize = NODE_CAPACITY + 1; + +// Minimum number of elements to insert in order to guarantee a tree with 3 levels, +// i.e. a tree who's root is an internal node at height 2, with edges to more internal nodes. +// It's not the minimum size: removing an element from such a tree does not always reduce height. +const MIN_INSERTS_HEIGHT_2: usize = NODE_CAPACITY + (NODE_CAPACITY + 1) * NODE_CAPACITY + 1; + #[test] fn test_basic_large() { let mut map = BTreeMap::new(); - #[cfg(not(miri))] // Miri is too slow - let size = 10000; - #[cfg(miri)] - let size = 144; // to obtain height 3 tree (having edges to both kinds of nodes) + // Miri is too slow + let size = if cfg!(miri) { MIN_INSERTS_HEIGHT_2 } else { 10000 }; assert_eq!(map.len(), 0); for i in 0..size { @@ -141,10 +153,8 @@ fn test_basic_small() { #[test] fn test_iter() { - #[cfg(not(miri))] // Miri is too slow - let size = 10000; - #[cfg(miri)] - let size = 200; + // Miri is too slow + let size = if cfg!(miri) { 200 } else { 10000 }; let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); @@ -166,10 +176,8 @@ fn test_iter() { #[test] fn test_iter_rev() { - #[cfg(not(miri))] // Miri is too slow - let size = 10000; - #[cfg(miri)] - let size = 200; + // Miri is too slow + let size = if cfg!(miri) { 200 } else { 10000 }; let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); @@ -237,30 +245,26 @@ impl TryFrom for Align32 { #[test] fn test_iter_mut_mutation() { - // Check many alignments because various fields precede array in NodeHeader. - // Check with size 0 which should not iterate at all. - // Check with size 1 for a tree with one kind of node (root = leaf). - // Check with size 12 for a tree with two kinds of nodes (root and leaves). - // Check with size 144 for a tree with all kinds of nodes (root, internals and leaves). + // Check many alignments and trees with roots at various heights. do_test_iter_mut_mutation::(0); do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(12); - do_test_iter_mut_mutation::(127); // not enough unique values to test 144 + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(127); // not enough unique values to test MIN_INSERTS_HEIGHT_2 do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(12); - do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(12); - do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(12); - do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(12); - do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(12); - do_test_iter_mut_mutation::(144); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); } #[test] @@ -279,10 +283,8 @@ fn test_values_mut() { #[test] fn test_iter_mixed() { - #[cfg(not(miri))] // Miri is too slow - let size = 10000; - #[cfg(miri)] - let size = 200; + // Miri is too slow + let size = if cfg!(miri) { 200 } else { 10000 }; let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); @@ -376,12 +378,11 @@ fn test_range_small() { } #[test] -fn test_range_height_2() { - // Assuming that node.CAPACITY is 11, having 12 pairs implies a height 2 tree - // with 2 leaves. Depending on details we don't want or need to rely upon, - // the single key at the root will be 6 or 7. +fn test_range_height_1() { + // Tests tree with a root and 2 leaves. Depending on details we don't want or need + // to rely upon, the single key at the root will be 6 or 7. - let map: BTreeMap<_, _> = (1..=12).map(|i| (i, i)).collect(); + let map: BTreeMap<_, _> = (1..=MIN_INSERTS_HEIGHT_1 as i32).map(|i| (i, i)).collect(); for &root in &[6, 7] { assert_eq!(range_keys(&map, (Excluded(root), Excluded(root + 1))), vec![]); assert_eq!(range_keys(&map, (Excluded(root), Included(root + 1))), vec![root + 1]); @@ -466,7 +467,7 @@ fn test_range_large() { #[test] fn test_range_inclusive_max_value() { - let max = std::usize::MAX; + let max = usize::MAX; let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect(); assert_eq!(map.range(max..=max).collect::>(), &[(&max, &0)]); @@ -516,10 +517,8 @@ fn test_range_backwards_4() { #[test] fn test_range_1000() { - #[cfg(not(miri))] // Miri is too slow - let size = 1000; - #[cfg(miri)] - let size = 144; // to obtain height 3 tree (having edges to both kinds of nodes) + // Miri is too slow + let size = if cfg!(miri) { MIN_INSERTS_HEIGHT_2 as u32 } else { 1000 }; let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); fn test(map: &BTreeMap, size: u32, min: Bound<&u32>, max: Bound<&u32>) { @@ -557,10 +556,8 @@ fn test_range_borrowed_key() { #[test] fn test_range() { let size = 200; - #[cfg(not(miri))] // Miri is too slow - let step = 1; - #[cfg(miri)] - let step = 66; + // Miri is too slow + let step = if cfg!(miri) { 66 } else { 1 }; let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); for i in (0..size).step_by(step) { @@ -580,10 +577,8 @@ fn test_range() { #[test] fn test_range_mut() { let size = 200; - #[cfg(not(miri))] // Miri is too slow - let step = 1; - #[cfg(miri)] - let step = 66; + // Miri is too slow + let step = if cfg!(miri) { 66 } else { 1 }; let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); for i in (0..size).step_by(step) { @@ -600,6 +595,263 @@ fn test_range_mut() { } } +mod test_drain_filter { + use super::*; + + #[test] + fn empty() { + let mut map: BTreeMap = BTreeMap::new(); + map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + assert!(map.is_empty()); + } + + #[test] + fn consuming_nothing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!(map.drain_filter(|_, _| false).eq(std::iter::empty())); + } + + #[test] + fn consuming_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + assert!(map.drain_filter(|_, _| true).eq(pairs)); + } + + #[test] + fn mutating_and_keeping() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + false + }) + .eq(std::iter::empty()) + ); + assert!(map.keys().copied().eq(0..3)); + assert!(map.values().copied().eq(6..9)); + } + + #[test] + fn mutating_and_removing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + true + }) + .eq((0..3).map(|i| (i, i + 6))) + ); + assert!(map.is_empty()); + } + + #[test] + fn underfull_keeping_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| false); + assert!(map.keys().copied().eq(0..3)); + } + + #[test] + fn underfull_removing_one() { + let pairs = (0..3).map(|i| (i, i)); + for doomed in 0..3 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), 2); + } + } + + #[test] + fn underfull_keeping_one() { + let pairs = (0..3).map(|i| (i, i)); + for sacred in 0..3 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[test] + fn underfull_removing_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn height_0_keeping_all() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| false); + assert!(map.keys().copied().eq(0..NODE_CAPACITY)); + } + + #[test] + fn height_0_removing_one() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + for doomed in 0..NODE_CAPACITY { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), NODE_CAPACITY - 1); + } + } + + #[test] + fn height_0_keeping_one() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + for sacred in 0..NODE_CAPACITY { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[test] + fn height_0_removing_all() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn height_0_keeping_half() { + let mut map: BTreeMap<_, _> = (0..16).map(|i| (i, i)).collect(); + assert_eq!(map.drain_filter(|i, _| *i % 2 == 0).count(), 8); + assert_eq!(map.len(), 8); + } + + #[test] + fn height_1_removing_all() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn height_1_removing_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + for doomed in 0..MIN_INSERTS_HEIGHT_1 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1); + } + } + + #[test] + fn height_1_keeping_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + for sacred in 0..MIN_INSERTS_HEIGHT_1 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[cfg(not(miri))] // Miri is too slow + #[test] + fn height_2_removing_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); + } + } + + #[cfg(not(miri))] // Miri is too slow + #[test] + fn height_2_keeping_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + } + } + + #[test] + fn height_2_removing_all() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + } + + #[test] + fn drop_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut map = BTreeMap::new(); + map.insert(0, D); + map.insert(4, D); + map.insert(8, D); + + catch_unwind(move || { + drop(map.drain_filter(|i, _| { + PREDS.fetch_add(1usize << i, Ordering::SeqCst); + true + })) + }) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); + } + + #[test] + fn pred_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut map = BTreeMap::new(); + map.insert(0, D); + map.insert(4, D); + map.insert(8, D); + + catch_unwind(AssertUnwindSafe(|| { + drop(map.drain_filter(|i, _| { + PREDS.fetch_add(1usize << i, Ordering::SeqCst); + match i { + 0 => true, + _ => panic!(), + } + })) + })) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + assert_eq!(map.first_entry().unwrap().key(), &4); + assert_eq!(map.last_entry().unwrap().key(), &8); + } +} + #[test] fn test_borrow() { // make sure these compile -- using the Borrow trait @@ -755,7 +1007,7 @@ fn test_bad_zst() { #[test] fn test_clone() { let mut map = BTreeMap::new(); - let size = 12; // to obtain height 2 tree (having edges to leaf nodes) + let size = MIN_INSERTS_HEIGHT_1; assert_eq!(map.len(), 0); for i in 0..size { @@ -783,20 +1035,19 @@ fn test_clone() { assert_eq!(map, map.clone()); } - // Full 2-level and minimal 3-level tree (sizes 143, 144 -- the only ones we clone for). - for i in 1..=144 { - assert_eq!(map.insert(i, i), None); - assert_eq!(map.len(), i); - if i >= 143 { - assert_eq!(map, map.clone()); - } - } + // Test a tree with 2 chock-full levels and a tree with 3 levels. + map = (1..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)).collect(); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); + assert_eq!(map, map.clone()); + map.insert(0, 0); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2); + assert_eq!(map, map.clone()); } #[test] fn test_clone_from() { let mut map1 = BTreeMap::new(); - let max_size = 12; // to obtain height 2 tree (having edges to leaf nodes) + let max_size = MIN_INSERTS_HEIGHT_1; // Range to max_size inclusive, because i is the size of map1 being tested. for i in 0..=max_size { @@ -998,10 +1249,8 @@ fn test_split_off_empty_left() { #[test] fn test_split_off_large_random_sorted() { - #[cfg(not(miri))] // Miri is too slow - let mut data = rand_data(1529); - #[cfg(miri)] - let mut data = rand_data(529); + // Miri is too slow + let mut data = if cfg!(miri) { rand_data(529) } else { rand_data(1529) }; // special case with maximum height. data.sort(); @@ -1014,8 +1263,8 @@ fn test_split_off_large_random_sorted() { } #[test] -fn test_into_iter_drop_leak_1() { - static DROPS: AtomicU32 = AtomicU32::new(0); +fn test_into_iter_drop_leak_height_0() { + static DROPS: AtomicUsize = AtomicUsize::new(0); struct D; @@ -1040,10 +1289,10 @@ fn test_into_iter_drop_leak_1() { } #[test] -fn test_into_iter_drop_leak_2() { - let size = 12; // to obtain tree with 2 levels (having edges to leaf nodes) - static DROPS: AtomicU32 = AtomicU32::new(0); - static PANIC_POINT: AtomicU32 = AtomicU32::new(0); +fn test_into_iter_drop_leak_height_1() { + let size = MIN_INSERTS_HEIGHT_1; + static DROPS: AtomicUsize = AtomicUsize::new(0); + static PANIC_POINT: AtomicUsize = AtomicUsize::new(0); struct D; impl Drop for D { diff --git a/src/liballoc/tests/btree/set.rs b/src/liballoc/tests/btree/set.rs index 1a2b62d026b2e..75251ca0d51e9 100644 --- a/src/liballoc/tests/btree/set.rs +++ b/src/liballoc/tests/btree/set.rs @@ -1,5 +1,7 @@ use std::collections::BTreeSet; use std::iter::FromIterator; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::atomic::{AtomicU32, Ordering}; use super::DeterministicRng; @@ -302,6 +304,85 @@ fn test_is_subset() { assert_eq!(is_subset(&[99, 100], &large), false); } +#[test] +fn test_drain_filter() { + let mut x: BTreeSet<_> = [1].iter().copied().collect(); + let mut y: BTreeSet<_> = [1].iter().copied().collect(); + + x.drain_filter(|_| true); + y.drain_filter(|_| false); + assert_eq!(x.len(), 0); + assert_eq!(y.len(), 1); +} + +#[test] +fn test_drain_filter_drop_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut set = BTreeSet::new(); + set.insert(D(0)); + set.insert(D(4)); + set.insert(D(8)); + + catch_unwind(move || { + drop(set.drain_filter(|d| { + PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); + true + })) + }) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); +} + +#[test] +fn test_drain_filter_pred_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut set = BTreeSet::new(); + set.insert(D(0)); + set.insert(D(4)); + set.insert(D(8)); + + catch_unwind(AssertUnwindSafe(|| { + drop(set.drain_filter(|d| { + PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); + match d.0 { + 0 => true, + _ => panic!(), + } + })) + })) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(set.len(), 2); + assert_eq!(set.first().unwrap().0, 4); + assert_eq!(set.last().unwrap().0, 8); +} + #[test] fn test_clear() { let mut x = BTreeSet::new(); @@ -540,10 +621,8 @@ fn test_split_off_empty_left() { #[test] fn test_split_off_large_random_sorted() { - #[cfg(not(miri))] // Miri is too slow - let mut data = rand_data(1529); - #[cfg(miri)] - let mut data = rand_data(529); + // Miri is too slow + let mut data = if cfg!(miri) { rand_data(529) } else { rand_data(1529) }; // special case with maximum height. data.sort(); diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs index d159126f426c5..62f062b83d75d 100644 --- a/src/liballoc/tests/heap.rs +++ b/src/liballoc/tests/heap.rs @@ -1,4 +1,4 @@ -use std::alloc::{AllocRef, Global, Layout, System}; +use std::alloc::{AllocInit, AllocRef, Global, Layout, System}; /// Issue #45955 and #62251. #[test] @@ -20,7 +20,13 @@ fn check_overalign_requests(mut allocator: T) { unsafe { let pointers: Vec<_> = (0..iterations) .map(|_| { - allocator.alloc(Layout::from_size_align(size, align).unwrap()).unwrap().0 + allocator + .alloc( + Layout::from_size_align(size, align).unwrap(), + AllocInit::Uninitialized, + ) + .unwrap() + .ptr }) .collect(); for &ptr in &pointers { diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index ea75f8903c368..f3da46bd0cc4e 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -1,5 +1,6 @@ #![feature(allocator_api)] #![feature(box_syntax)] +#![feature(btree_drain_filter)] #![feature(drain_filter)] #![feature(exact_size_is_empty)] #![feature(map_first_last)] @@ -13,12 +14,14 @@ #![feature(binary_heap_drain_sorted)] #![feature(vec_remove_item)] #![feature(split_inclusive)] +#![feature(binary_heap_retain)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; mod arc; mod binary_heap; +mod borrow; mod boxed; mod btree; mod cow_str; diff --git a/src/liballoc/tests/rc.rs b/src/liballoc/tests/rc.rs index 884856cd1b4d2..501b4f0f816be 100644 --- a/src/liballoc/tests/rc.rs +++ b/src/liballoc/tests/rc.rs @@ -50,7 +50,7 @@ fn trait_object() { #[test] fn float_nan_ne() { - let x = Rc::new(std::f32::NAN); + let x = Rc::new(f32::NAN); assert!(x != x); assert!(!(x == x)); } diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 8e49e6d8ebad9..75b76bb73ed9e 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -463,15 +463,9 @@ fn test_sort() { #[test] fn test_sort_stability() { - #[cfg(not(miri))] // Miri is too slow - let large_range = 500..510; - #[cfg(not(miri))] // Miri is too slow - let rounds = 10; - - #[cfg(miri)] - let large_range = 0..0; // empty range - #[cfg(miri)] - let rounds = 1; + // Miri is too slow + let large_range = if cfg!(miri) { 0..0 } else { 500..510 }; + let rounds = if cfg!(miri) { 1 } else { 10 }; for len in (2..25).chain(large_range) { for _ in 0..rounds { @@ -1727,15 +1721,9 @@ fn panic_safe() { let mut rng = thread_rng(); - #[cfg(not(miri))] // Miri is too slow - let lens = (1..20).chain(70..MAX_LEN); - #[cfg(not(miri))] // Miri is too slow - let moduli = &[5, 20, 50]; - - #[cfg(miri)] - let lens = 1..10; - #[cfg(miri)] - let moduli = &[5]; + // Miri is too slow + let lens = if cfg!(miri) { (1..10).chain(20..21) } else { (1..20).chain(70..MAX_LEN) }; + let moduli: &[u32] = if cfg!(miri) { &[5] } else { &[5, 20, 50] }; for len in lens { for &modulus in moduli { diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 08859b2b24bde..9ea020d2d19f4 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use std::collections::TryReserveError::*; use std::mem::size_of; -use std::{isize, usize}; pub trait IntoCow<'a, B: ?Sized> where @@ -266,14 +265,14 @@ fn test_split_off_empty() { fn test_split_off_past_end() { let orig = "Hello, world!"; let mut split = String::from(orig); - split.split_off(orig.len() + 1); + let _ = split.split_off(orig.len() + 1); } #[test] #[should_panic] fn test_split_off_mid_char() { let mut orig = String::from("山"); - orig.split_off(1); + let _ = orig.split_off(1); } #[test] @@ -556,6 +555,7 @@ fn test_reserve_exact() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -645,6 +645,7 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 9c4ac52acac2a..b16044d964045 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -3,7 +3,6 @@ use std::collections::TryReserveError::*; use std::mem::size_of; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::vec::{Drain, IntoIter}; -use std::{isize, usize}; struct DropCounter<'a> { count: &'a mut u32, @@ -1138,6 +1137,7 @@ fn test_reserve_exact() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -1255,6 +1255,7 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. @@ -1351,17 +1352,26 @@ fn test_try_reserve_exact() { } #[test] -fn test_stable_push_pop() { +fn test_stable_pointers() { + /// Pull an element from the iterator, then drop it. + /// Useful to cover both the `next` and `drop` paths of an iterator. + fn next_then_drop(mut i: I) { + i.next().unwrap(); + drop(i); + } + // Test that, if we reserved enough space, adding and removing elements does not // invalidate references into the vector (such as `v0`). This test also // runs in Miri, which would detect such problems. - let mut v = Vec::with_capacity(10); + 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 = unsafe { &*(&v[0] as *const _) }; - + // 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); @@ -1369,6 +1379,58 @@ fn test_stable_push_pop() { v.remove(1); v.pop().unwrap(); assert_eq!(*v0, 13); + v.push(1); + v.swap_remove(1); + assert_eq!(v.len(), 2); + v.swap_remove(1); // swap_remove the last element + assert_eq!(*v0, 13); + + // Appending + v.append(&mut vec![27, 19]); + assert_eq!(*v0, 13); + + // Extending + v.extend_from_slice(&[1, 2]); + v.extend(&[1, 2]); // `slice::Iter` (with `T: Copy`) specialization + v.extend(vec![2, 3]); // `vec::IntoIter` specialization + v.extend(std::iter::once(3)); // `TrustedLen` specialization + v.extend(std::iter::empty::()); // `TrustedLen` specialization with empty iterator + v.extend(std::iter::once(3).filter(|_| true)); // base case + v.extend(std::iter::once(&3)); // `cloned` specialization + assert_eq!(*v0, 13); + + // Truncation + v.truncate(2); + assert_eq!(*v0, 13); + + // Resizing + v.resize_with(v.len() + 10, || 42); + assert_eq!(*v0, 13); + v.resize_with(2, || panic!()); + assert_eq!(*v0, 13); + + // No-op reservation + v.reserve(32); + v.reserve_exact(32); + assert_eq!(*v0, 13); + + // Partial draining + v.resize_with(10, || 42); + next_then_drop(v.drain(5..)); + assert_eq!(*v0, 13); + + // Splicing + v.resize_with(10, || 42); + next_then_drop(v.splice(5.., vec![1, 2, 3, 4, 5])); // empty tail after range + assert_eq!(*v0, 13); + next_then_drop(v.splice(5..8, vec![1])); // replacement is smaller than original range + assert_eq!(*v0, 13); + next_then_drop(v.splice(5..6, vec![1; 10].into_iter().filter(|_| true))); // lower bound not exact + assert_eq!(*v0, 13); + + // Smoke test that would fire even outside Miri if an actual relocation happened. + *v0 -= 13; + assert_eq!(v[0], 0); } // https://github.com/rust-lang/rust/pull/49496 introduced specialization based on: @@ -1411,3 +1473,118 @@ fn vec_macro_repeating_null_raw_fat_pointer() { 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); + } + } +} diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index 101dd67d97a9a..762dc4be44d62 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -3,7 +3,6 @@ use std::collections::{vec_deque::Drain, VecDeque}; use std::fmt::Debug; use std::mem::size_of; use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::{isize, usize}; use crate::hash; @@ -955,16 +954,14 @@ fn test_append_permutations() { out } - #[cfg(not(miri))] // Miri is too slow - const MAX: usize = 5; - #[cfg(miri)] - const MAX: usize = 3; + // Miri is too slow + let max = if cfg!(miri) { 3 } else { 5 }; // Many different permutations of both the `VecDeque` getting appended to // and the one getting appended are generated to check `append`. // This ensures all 6 code paths of `append` are tested. - for src_push_back in 0..MAX { - for src_push_front in 0..MAX { + for src_push_back in 0..max { + for src_push_front in 0..max { // doesn't pop more values than are pushed for src_pop_back in 0..(src_push_back + src_push_front) { for src_pop_front in 0..(src_push_back + src_push_front - src_pop_back) { @@ -975,8 +972,8 @@ fn test_append_permutations() { src_pop_front, ); - for dst_push_back in 0..MAX { - for dst_push_front in 0..MAX { + for dst_push_back in 0..max { + for dst_push_front in 0..max { for dst_pop_back in 0..(dst_push_back + dst_push_front) { for dst_pop_front in 0..(dst_push_back + dst_push_front - dst_pop_back) @@ -1135,6 +1132,7 @@ fn test_reserve_exact_2() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -1249,6 +1247,7 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 4769091183a37..d26cd77aae4b7 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! A contiguous growable array type with heap-allocated contents, written //! `Vec`. //! @@ -65,7 +66,7 @@ use core::hash::{self, Hash}; use core::intrinsics::{arith_offset, assume}; use core::iter::{FromIterator, FusedIterator, TrustedLen}; use core::marker::PhantomData; -use core::mem; +use core::mem::{self, ManuallyDrop}; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Index, IndexMut, RangeBounds}; use core::ptr::{self, NonNull}; @@ -391,7 +392,7 @@ impl Vec { /// ``` #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts(self) -> (*mut T, usize, usize) { - let mut me = mem::ManuallyDrop::new(self); + let mut me = ManuallyDrop::new(self); (me.as_mut_ptr(), me.len(), me.capacity()) } @@ -677,9 +678,10 @@ impl Vec { pub fn into_boxed_slice(mut self) -> Box<[T]> { unsafe { self.shrink_to_fit(); - let buf = ptr::read(&self.buf); - mem::forget(self); - buf.into_box() + let me = ManuallyDrop::new(self); + let buf = ptr::read(&me.buf); + let len = me.len(); + buf.into_box(len).assume_init() } } @@ -738,7 +740,8 @@ impl Vec { if len > self.len { return; } - let s = self.get_unchecked_mut(len..) as *mut _; + let remaining_len = self.len - len; + let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len); self.len = len; ptr::drop_in_place(s); } @@ -961,13 +964,23 @@ impl Vec { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn swap_remove(&mut self, index: usize) -> T { + #[cold] + #[inline(never)] + fn assert_failed(index: usize, len: usize) -> ! { + panic!("swap_remove index (is {}) should be < len (is {})", index, len); + } + + let len = self.len(); + if index >= len { + assert_failed(index, len); + } unsafe { // We replace self[index] with the last element. Note that if the - // bounds check on hole succeeds there must be a last element (which + // bounds check above succeeds there must be a last element (which // can be self[index] itself). - let hole: *mut T = &mut self[index]; - let last = ptr::read(self.get_unchecked(self.len - 1)); - self.len -= 1; + let last = ptr::read(self.as_ptr().add(len - 1)); + let hole: *mut T = self.as_mut_ptr().add(index); + self.set_len(len - 1); ptr::replace(hole, last) } } @@ -990,8 +1003,16 @@ impl Vec { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(&mut self, index: usize, element: T) { + #[cold] + #[inline(never)] + fn assert_failed(index: usize, len: usize) -> ! { + panic!("insertion index (is {}) should be <= len (is {})", index, len); + } + let len = self.len(); - assert!(index <= len); + if index > len { + assert_failed(index, len); + } // space for the new element if len == self.buf.capacity() { @@ -1030,8 +1051,16 @@ impl Vec { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(&mut self, index: usize) -> T { + #[cold] + #[inline(never)] + fn assert_failed(index: usize, len: usize) -> ! { + panic!("removal index (is {}) should be < len (is {})", index, len); + } + let len = self.len(); - assert!(index < len); + if index >= len { + assert_failed(index, len); + } unsafe { // infallible let ret; @@ -1198,7 +1227,7 @@ impl Vec { } else { unsafe { self.len -= 1; - Some(ptr::read(self.get_unchecked(self.len()))) + Some(ptr::read(self.as_ptr().add(self.len()))) } } } @@ -1289,8 +1318,25 @@ impl Vec { Excluded(&n) => n, Unbounded => len, }; - assert!(start <= end); - assert!(end <= len); + + #[cold] + #[inline(never)] + fn start_assert_failed(start: usize, end: usize) -> ! { + panic!("start drain index (is {}) should be <= end drain index (is {})", start, end); + } + + #[cold] + #[inline(never)] + fn end_assert_failed(end: usize, len: usize) -> ! { + panic!("end drain index (is {}) should be <= len (is {})", end, len); + } + + if start > end { + start_assert_failed(start, end); + } + if end > len { + end_assert_failed(end, len); + } unsafe { // set self.vec length's to start, to be safe in case Drain is leaked @@ -1380,7 +1426,15 @@ impl Vec { #[must_use = "use `.truncate()` if you don't need the other half"] #[stable(feature = "split_off", since = "1.4.0")] pub fn split_off(&mut self, at: usize) -> Self { - assert!(at <= self.len(), "`at` out of bounds"); + #[cold] + #[inline(never)] + fn assert_failed(at: usize, len: usize) -> ! { + panic!("`at` split index (is {}) should be <= len (is {})", at, len); + } + + if at > self.len() { + assert_failed(at, self.len()); + } let other_len = self.len - at; let mut other = Vec::with_capacity(other_len); @@ -1565,8 +1619,8 @@ impl Vec { #[unstable(feature = "vec_resize_default", issue = "41758")] #[rustc_deprecated( reason = "This is moving towards being removed in favor \ - of `.resize_with(Default::default)`. If you disagree, please comment \ - in the tracking issue.", + of `.resize_with(Default::default)`. If you disagree, please comment \ + in the tracking issue.", since = "1.33.0" )] pub fn resize_default(&mut self, new_len: usize) { @@ -1771,6 +1825,7 @@ impl SpecFromElem for T { } } +#[rustc_specialization_trait] unsafe trait IsZero { /// Whether this value is zero fn is_zero(&self) -> bool; @@ -1820,9 +1875,12 @@ unsafe impl IsZero for *mut T { } } -// `Option<&T>`, `Option<&mut T>` and `Option>` are guaranteed to represent `None` as null. -// For fat pointers, the bytes that would be the pointer metadata in the `Some` variant -// are padding in the `None` variant, so ignoring them and zero-initializing instead is ok. +// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. +// For fat pointers, the bytes that would be the pointer metadata in the `Some` +// variant are padding in the `None` variant, so ignoring them and +// zero-initializing instead is ok. +// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of +// `SpecFromElem`. unsafe impl IsZero for Option<&T> { #[inline] @@ -1831,13 +1889,6 @@ unsafe impl IsZero for Option<&T> { } } -unsafe impl IsZero for Option<&mut T> { - #[inline] - fn is_zero(&self) -> bool { - self.is_none() - } -} - unsafe impl IsZero for Option> { #[inline] fn is_zero(&self) -> bool { @@ -1947,16 +1998,16 @@ impl IntoIterator for Vec { /// } /// ``` #[inline] - fn into_iter(mut self) -> IntoIter { + fn into_iter(self) -> IntoIter { unsafe { - let begin = self.as_mut_ptr(); + let mut me = ManuallyDrop::new(self); + let begin = me.as_mut_ptr(); let end = if mem::size_of::() == 0 { - arith_offset(begin as *const i8, self.len() as isize) as *const T + arith_offset(begin as *const i8, me.len() as isize) as *const T } else { - begin.add(self.len()) as *const T + begin.add(me.len()) as *const T }; - let cap = self.buf.capacity(); - mem::forget(self); + let cap = me.buf.capacity(); IntoIter { buf: NonNull::new_unchecked(begin), phantom: PhantomData, @@ -2018,7 +2069,7 @@ where let (lower, _) = iterator.size_hint(); let mut vector = Vec::with_capacity(lower.saturating_add(1)); unsafe { - ptr::write(vector.get_unchecked_mut(0), element); + ptr::write(vector.as_mut_ptr(), element); vector.set_len(1); } vector @@ -2079,9 +2130,8 @@ impl SpecExtend> for Vec { // has not been advanced at all. if iterator.buf.as_ptr() as *const _ == iterator.ptr { unsafe { - let vec = Vec::from_raw_parts(iterator.buf.as_ptr(), iterator.len(), iterator.cap); - mem::forget(iterator); - vec + let it = ManuallyDrop::new(iterator); + Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap) } } else { let mut vector = Vec::new(); @@ -2121,8 +2171,9 @@ where self.reserve(slice.len()); unsafe { let len = self.len(); + let dst_slice = slice::from_raw_parts_mut(self.as_mut_ptr().add(len), slice.len()); + dst_slice.copy_from_slice(slice); self.set_len(len + slice.len()); - self.get_unchecked_mut(len..).copy_from_slice(slice); } } } @@ -2143,7 +2194,7 @@ impl Vec { self.reserve(lower.saturating_add(1)); } unsafe { - ptr::write(self.get_unchecked_mut(len), element); + ptr::write(self.as_mut_ptr().add(len), element); // NB can't overflow since we would have had to alloc the address space self.set_len(len + 1); } @@ -2325,7 +2376,9 @@ unsafe impl<#[may_dangle] T> Drop for Vec { fn drop(&mut self) { unsafe { // use drop for [T] - ptr::drop_in_place(&mut self[..]); + // use a raw slice to refer to the elements of the vector as weakest necessary type; + // could avoid questions of validity in certain cases + ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.as_mut_ptr(), self.len)) } // RawVec handles deallocation } @@ -2398,6 +2451,21 @@ impl From<&mut [T]> for Vec { } } +#[stable(feature = "vec_from_array", since = "1.44.0")] +impl From<[T; N]> for Vec +where + [T; N]: LengthAtMost32, +{ + #[cfg(not(test))] + fn from(s: [T; N]) -> Vec { + <[T]>::into_vec(box s) + } + #[cfg(test)] + fn from(s: [T; N]) -> Vec { + crate::slice::into_vec(box s) + } +} + #[stable(feature = "vec_from_cow_slice", since = "1.14.0")] impl<'a, T> From> for Vec where @@ -2527,7 +2595,11 @@ impl IntoIter { /// ``` #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] pub fn as_mut_slice(&mut self) -> &mut [T] { - unsafe { slice::from_raw_parts_mut(self.ptr as *mut T, self.len()) } + unsafe { &mut *self.as_raw_mut_slice() } + } + + fn as_raw_mut_slice(&mut self) -> *mut [T] { + ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) } } @@ -2639,7 +2711,7 @@ unsafe impl<#[may_dangle] T> Drop for IntoIter { let guard = DropGuard(self); // destroy the remaining elements unsafe { - ptr::drop_in_place(guard.0.as_mut_slice()); + ptr::drop_in_place(guard.0.as_raw_mut_slice()); } // now `guard` will be dropped and do the rest } diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 0f0bd617f439c..bbe80c26dcbf9 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -5,8 +5,7 @@ //! of individual objects while the arena itself is still alive. The benefit //! of an arena is very fast allocation; just a pointer bump. //! -//! This crate implements `TypedArena`, a simple arena that can only hold -//! objects of a single type. +//! This crate implements several kinds of arena. #![doc( html_root_url = "https://doc.rust-lang.org/nightly/", @@ -98,7 +97,13 @@ impl TypedArenaChunk { } } +// The arenas start with PAGE-sized chunks, and then each new chunk is twice as +// big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon +// we stop growing. This scales well, from arenas that are barely used up to +// arenas that are used for 100s of MiBs. Note also that the chosen sizes match +// the usual sizes of pages and huge pages on Linux. const PAGE: usize = 4096; +const HUGE_PAGE: usize = 2 * 1024 * 1024; impl Default for TypedArena { /// Creates a new `TypedArena`. @@ -211,6 +216,9 @@ impl TypedArena { #[cold] fn grow(&self, n: usize) { unsafe { + // We need the element size in to convert chunk sizes (ranging from + // PAGE to HUGE_PAGE bytes) to element counts. + let elem_size = cmp::max(1, mem::size_of::()); let mut chunks = self.chunks.borrow_mut(); let (chunk, mut new_capacity); if let Some(last_chunk) = chunks.last_mut() { @@ -221,18 +229,20 @@ impl TypedArena { self.end.set(last_chunk.end()); return; } else { + // If the previous chunk's capacity is less than HUGE_PAGE + // bytes, then this chunk will be least double the previous + // chunk's size. new_capacity = last_chunk.storage.capacity(); - loop { + if new_capacity < HUGE_PAGE / elem_size { new_capacity = new_capacity.checked_mul(2).unwrap(); - if new_capacity >= currently_used_cap + n { - break; - } } } } else { - let elem_size = cmp::max(1, mem::size_of::()); - new_capacity = cmp::max(n, PAGE / elem_size); + new_capacity = PAGE / elem_size; } + // Also ensure that this chunk can fit `n`. + new_capacity = cmp::max(n, new_capacity); + chunk = TypedArenaChunk::::new(new_capacity); self.ptr.set(chunk.start()); self.end.set(chunk.end()); @@ -347,17 +357,20 @@ impl DroplessArena { self.end.set(last_chunk.end()); return; } else { + // If the previous chunk's capacity is less than HUGE_PAGE + // bytes, then this chunk will be least double the previous + // chunk's size. new_capacity = last_chunk.storage.capacity(); - loop { + if new_capacity < HUGE_PAGE { new_capacity = new_capacity.checked_mul(2).unwrap(); - if new_capacity >= used_bytes + needed_bytes { - break; - } } } } else { - new_capacity = cmp::max(needed_bytes, PAGE); + new_capacity = PAGE; } + // Also ensure that this chunk can fit `needed_bytes`. + new_capacity = cmp::max(needed_bytes, new_capacity); + chunk = TypedArenaChunk::::new(new_capacity); self.ptr.set(chunk.start()); self.end.set(chunk.end()); diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs deleted file mode 100644 index be20a1cde3694..0000000000000 --- a/src/libcore/alloc.rs +++ /dev/null @@ -1,1043 +0,0 @@ -//! Memory allocation APIs - -// ignore-tidy-undocumented-unsafe - -#![stable(feature = "alloc_module", since = "1.28.0")] - -use crate::cmp; -use crate::fmt; -use crate::mem; -use crate::num::NonZeroUsize; -use crate::ptr::{self, NonNull}; -use crate::usize; - -const fn size_align() -> (usize, usize) { - (mem::size_of::(), mem::align_of::()) -} - -/// Layout of a block of memory. -/// -/// An instance of `Layout` describes a particular layout of memory. -/// You build a `Layout` up as an input to give to an allocator. -/// -/// All layouts have an associated non-negative size and a -/// power-of-two alignment. -/// -/// (Note however that layouts are *not* required to have positive -/// size, even though many allocators require that all memory -/// requests have positive size. A caller to the `AllocRef::alloc` -/// method must either ensure that conditions like this are met, or -/// use specific allocators with looser requirements.) -#[stable(feature = "alloc_layout", since = "1.28.0")] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[lang = "alloc_layout"] -pub struct Layout { - // size of the requested block of memory, measured in bytes. - size_: usize, - - // alignment of the requested block of memory, measured in bytes. - // we ensure that this is always a power-of-two, because API's - // like `posix_memalign` require it and it is a reasonable - // constraint to impose on Layout constructors. - // - // (However, we do not analogously require `align >= sizeof(void*)`, - // even though that is *also* a requirement of `posix_memalign`.) - align_: NonZeroUsize, -} - -impl Layout { - /// Constructs a `Layout` from a given `size` and `align`, - /// or returns `LayoutErr` if any of the following conditions - /// are not met: - /// - /// * `align` must not be zero, - /// - /// * `align` must be a power of two, - /// - /// * `size`, when rounded up to the nearest multiple of `align`, - /// must not overflow (i.e., the rounded value must be less than - /// `usize::MAX`). - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn from_size_align(size: usize, align: usize) -> Result { - if !align.is_power_of_two() { - return Err(LayoutErr { private: () }); - } - - // (power-of-two implies align != 0.) - - // Rounded up size is: - // size_rounded_up = (size + align - 1) & !(align - 1); - // - // We know from above that align != 0. If adding (align - 1) - // does not overflow, then rounding up will be fine. - // - // Conversely, &-masking with !(align - 1) will subtract off - // only low-order-bits. Thus if overflow occurs with the sum, - // the &-mask cannot subtract enough to undo that overflow. - // - // Above implies that checking for summation overflow is both - // necessary and sufficient. - if size > usize::MAX - (align - 1) { - return Err(LayoutErr { private: () }); - } - - unsafe { Ok(Layout::from_size_align_unchecked(size, align)) } - } - - /// Creates a layout, bypassing all checks. - /// - /// # Safety - /// - /// This function is unsafe as it does not verify the preconditions from - /// [`Layout::from_size_align`](#method.from_size_align). - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "alloc_layout", since = "1.28.0")] - #[inline] - pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { - Layout { size_: size, align_: NonZeroUsize::new_unchecked(align) } - } - - /// The minimum size in bytes for a memory block of this layout. - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn size(&self) -> usize { - self.size_ - } - - /// The minimum byte alignment for a memory block of this layout. - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn align(&self) -> usize { - self.align_.get() - } - - /// Constructs a `Layout` suitable for holding a value of type `T`. - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")] - #[inline] - pub const fn new() -> Self { - let (size, align) = size_align::(); - // Note that the align is guaranteed by rustc to be a power of two and - // the size+align combo is guaranteed to fit in our address space. As a - // result use the unchecked constructor here to avoid inserting code - // that panics if it isn't optimized well enough. - unsafe { Layout::from_size_align_unchecked(size, align) } - } - - /// Produces layout describing a record that could be used to - /// allocate backing structure for `T` (which could be a trait - /// or other unsized type like a slice). - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[inline] - pub fn for_value(t: &T) -> Self { - let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); - // See rationale in `new` for why this is using an unsafe variant below - debug_assert!(Layout::from_size_align(size, align).is_ok()); - unsafe { Layout::from_size_align_unchecked(size, align) } - } - - /// Produces layout describing a record that could be used to - /// allocate backing structure for `T` (which could be a trait - /// or other unsized type like a slice). - /// - /// # Safety - /// - /// This function is only safe to call if the following conditions hold: - /// - /// - If `T` is `Sized`, this function is always safe to call. - /// - If the unsized tail of `T` is: - /// - a [slice], then the length of the slice tail must be an intialized - /// integer, and the size of the *entire value* - /// (dynamic tail length + statically sized prefix) must fit in `isize`. - /// - a [trait object], then the vtable part of the pointer must point - /// to a valid vtable acquired by an unsizing coersion, and the size - /// of the *entire value* (dynamic tail length + statically sized prefix) - /// must fit in `isize`. - /// - an (unstable) [extern type], then this function is always safe to - /// call, but may panic or otherwise return the wrong value, as the - /// extern type's layout is not known. This is the same behavior as - /// [`Layout::for_value`] on a reference to an extern type tail. - /// - otherwise, it is conservatively not allowed to call this function. - /// - /// [slice]: ../../std/primitive.slice.html - /// [trait object]: ../../book/ch17-02-trait-objects.html - /// [extern type]: ../../unstable-book/language-features/extern-types.html - #[inline] - #[cfg(not(bootstrap))] - #[unstable(feature = "layout_for_ptr", issue = "69835")] - pub unsafe fn for_value_raw(t: *const T) -> Self { - let (size, align) = (mem::size_of_val_raw(t), mem::align_of_val_raw(t)); - // See rationale in `new` for why this is using an unsafe variant below - debug_assert!(Layout::from_size_align(size, align).is_ok()); - Layout::from_size_align_unchecked(size, align) - } - - /// Creates a `NonNull` that is dangling, but well-aligned for this Layout. - /// - /// Note that the pointer value may potentially represent a valid pointer, - /// which means this must not be used as a "not yet initialized" - /// sentinel value. Types that lazily allocate must track initialization by - /// some other means. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - pub const fn dangling(&self) -> NonNull { - // align is non-zero and a power of two - unsafe { NonNull::new_unchecked(self.align() as *mut u8) } - } - - /// Creates a layout describing the record that can hold a value - /// of the same layout as `self`, but that also is aligned to - /// alignment `align` (measured in bytes). - /// - /// If `self` already meets the prescribed alignment, then returns - /// `self`. - /// - /// Note that this method does not add any padding to the overall - /// size, regardless of whether the returned layout has a different - /// alignment. In other words, if `K` has size 16, `K.align_to(32)` - /// will *still* have size 16. - /// - /// Returns an error if the combination of `self.size()` and the given - /// `align` violates the conditions listed in - /// [`Layout::from_size_align`](#method.from_size_align). - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn align_to(&self, align: usize) -> Result { - Layout::from_size_align(self.size(), cmp::max(self.align(), align)) - } - - /// Returns the amount of padding we must insert after `self` - /// to ensure that the following address will satisfy `align` - /// (measured in bytes). - /// - /// e.g., if `self.size()` is 9, then `self.padding_needed_for(4)` - /// returns 3, because that is the minimum number of bytes of - /// padding required to get a 4-aligned address (assuming that the - /// corresponding memory block starts at a 4-aligned address). - /// - /// The return value of this function has no meaning if `align` is - /// not a power-of-two. - /// - /// Note that the utility of the returned value requires `align` - /// to be less than or equal to the alignment of the starting - /// address for the whole allocated block of memory. One way to - /// satisfy this constraint is to ensure `align <= self.align()`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn padding_needed_for(&self, align: usize) -> usize { - let len = self.size(); - - // Rounded up value is: - // len_rounded_up = (len + align - 1) & !(align - 1); - // and then we return the padding difference: `len_rounded_up - len`. - // - // We use modular arithmetic throughout: - // - // 1. align is guaranteed to be > 0, so align - 1 is always - // valid. - // - // 2. `len + align - 1` can overflow by at most `align - 1`, - // so the &-mask with `!(align - 1)` will ensure that in the - // case of overflow, `len_rounded_up` will itself be 0. - // Thus the returned padding, when added to `len`, yields 0, - // which trivially satisfies the alignment `align`. - // - // (Of course, attempts to allocate blocks of memory whose - // size and padding overflow in the above manner should cause - // the allocator to yield an error anyway.) - - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); - len_rounded_up.wrapping_sub(len) - } - - /// Creates a layout by rounding the size of this layout up to a multiple - /// of the layout's alignment. - /// - /// This is equivalent to adding the result of `padding_needed_for` - /// to the layout's current size. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn pad_to_align(&self) -> Layout { - let pad = self.padding_needed_for(self.align()); - // This cannot overflow. Quoting from the invariant of Layout: - // > `size`, when rounded up to the nearest multiple of `align`, - // > must not overflow (i.e., the rounded value must be less than - // > `usize::MAX`) - let new_size = self.size() + pad; - - Layout::from_size_align(new_size, self.align()).unwrap() - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with a suitable amount of padding between each to - /// ensure that each instance is given its requested size and - /// alignment. On success, returns `(k, offs)` where `k` is the - /// layout of the array and `offs` is the distance between the start - /// of each element in the array. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> { - // This cannot overflow. Quoting from the invariant of Layout: - // > `size`, when rounded up to the nearest multiple of `align`, - // > must not overflow (i.e., the rounded value must be less than - // > `usize::MAX`) - let padded_size = self.size() + self.padding_needed_for(self.align()); - let alloc_size = padded_size.checked_mul(n).ok_or(LayoutErr { private: () })?; - - unsafe { - // self.align is already known to be valid and alloc_size has been - // padded already. - Ok((Layout::from_size_align_unchecked(alloc_size, self.align()), padded_size)) - } - } - - /// Creates a layout describing the record for `self` followed by - /// `next`, including any necessary padding to ensure that `next` - /// will be properly aligned. Note that the resulting layout will - /// satisfy the alignment properties of both `self` and `next`. - /// - /// The resulting layout will be the same as that of a C struct containing - /// two fields with the layouts of `self` and `next`, in that order. - /// - /// Returns `Some((k, offset))`, where `k` is layout of the concatenated - /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded within the concatenated record - /// (assuming that the record itself starts at offset 0). - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> { - let new_align = cmp::max(self.align(), next.align()); - let pad = self.padding_needed_for(next.align()); - - let offset = self.size().checked_add(pad).ok_or(LayoutErr { private: () })?; - let new_size = offset.checked_add(next.size()).ok_or(LayoutErr { private: () })?; - - let layout = Layout::from_size_align(new_size, new_align)?; - Ok((layout, offset)) - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with no padding between each instance. - /// - /// Note that, unlike `repeat`, `repeat_packed` does not guarantee - /// that the repeated instances of `self` will be properly - /// aligned, even if a given instance of `self` is properly - /// aligned. In other words, if the layout returned by - /// `repeat_packed` is used to allocate an array, it is not - /// guaranteed that all elements in the array will be properly - /// aligned. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn repeat_packed(&self, n: usize) -> Result { - let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?; - Layout::from_size_align(size, self.align()) - } - - /// Creates a layout describing the record for `self` followed by - /// `next` with no additional padding between the two. Since no - /// padding is inserted, the alignment of `next` is irrelevant, - /// and is not incorporated *at all* into the resulting layout. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn extend_packed(&self, next: Self) -> Result { - let new_size = self.size().checked_add(next.size()).ok_or(LayoutErr { private: () })?; - Layout::from_size_align(new_size, self.align()) - } - - /// Creates a layout describing the record for a `[T; n]`. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn array(n: usize) -> Result { - Layout::new::().repeat(n).map(|(k, offs)| { - debug_assert!(offs == mem::size_of::()); - k - }) - } -} - -/// The parameters given to `Layout::from_size_align` -/// or some other `Layout` constructor -/// do not satisfy its documented constraints. -#[stable(feature = "alloc_layout", since = "1.28.0")] -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct LayoutErr { - private: (), -} - -// (we need this for downstream impl of trait Error) -#[stable(feature = "alloc_layout", since = "1.28.0")] -impl fmt::Display for LayoutErr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("invalid parameters to Layout::from_size_align") - } -} - -/// The `AllocErr` error indicates an allocation failure -/// that may be due to resource exhaustion or to -/// something wrong when combining the given input arguments with this -/// allocator. -#[unstable(feature = "allocator_api", issue = "32838")] -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct AllocErr; - -// (we need this for downstream impl of trait Error) -#[unstable(feature = "allocator_api", issue = "32838")] -impl fmt::Display for AllocErr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("memory allocation failed") - } -} - -/// The `CannotReallocInPlace` error is used when [`grow_in_place`] or -/// [`shrink_in_place`] were unable to reuse the given memory block for -/// a requested layout. -/// -/// [`grow_in_place`]: ./trait.AllocRef.html#method.grow_in_place -/// [`shrink_in_place`]: ./trait.AllocRef.html#method.shrink_in_place -#[unstable(feature = "allocator_api", issue = "32838")] -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct CannotReallocInPlace; - -#[unstable(feature = "allocator_api", issue = "32838")] -impl CannotReallocInPlace { - pub fn description(&self) -> &str { - "cannot reallocate allocator's memory in place" - } -} - -// (we need this for downstream impl of trait Error) -#[unstable(feature = "allocator_api", issue = "32838")] -impl fmt::Display for CannotReallocInPlace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -/// A memory allocator that can be registered as the standard library’s default -/// through the `#[global_allocator]` attribute. -/// -/// Some of the methods require that a memory block be *currently -/// allocated* via an allocator. This means that: -/// -/// * the starting address for that memory block was previously -/// returned by a previous call to an allocation method -/// such as `alloc`, and -/// -/// * the memory block has not been subsequently deallocated, where -/// blocks are deallocated either by being passed to a deallocation -/// method such as `dealloc` or by being -/// passed to a reallocation method that returns a non-null pointer. -/// -/// -/// # Example -/// -/// ```no_run -/// use std::alloc::{GlobalAlloc, Layout, alloc}; -/// use std::ptr::null_mut; -/// -/// struct MyAllocator; -/// -/// unsafe impl GlobalAlloc for MyAllocator { -/// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() } -/// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} -/// } -/// -/// #[global_allocator] -/// static A: MyAllocator = MyAllocator; -/// -/// fn main() { -/// unsafe { -/// assert!(alloc(Layout::new::()).is_null()) -/// } -/// } -/// ``` -/// -/// # Safety -/// -/// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and -/// implementors must ensure that they adhere to these contracts: -/// -/// * It's undefined behavior if global allocators unwind. This restriction may -/// be lifted in the future, but currently a panic from any of these -/// functions may lead to memory unsafety. -/// -/// * `Layout` queries and calculations in general must be correct. Callers of -/// this trait are allowed to rely on the contracts defined on each method, -/// and implementors must ensure such contracts remain true. -#[stable(feature = "global_alloc", since = "1.28.0")] -pub unsafe trait GlobalAlloc { - /// Allocate memory as described by the given `layout`. - /// - /// Returns a pointer to newly-allocated memory, - /// or null to indicate allocation failure. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure that `layout` has non-zero size. - /// - /// (Extension subtraits might provide more specific bounds on - /// behavior, e.g., guarantee a sentinel address or a null pointer - /// in response to a zero-size allocation request.) - /// - /// The allocated block of memory may or may not be initialized. - /// - /// # Errors - /// - /// Returning a null pointer indicates that either memory is exhausted - /// or `layout` does not meet this allocator's size or alignment constraints. - /// - /// Implementations are encouraged to return null on memory - /// exhaustion rather than aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - #[stable(feature = "global_alloc", since = "1.28.0")] - unsafe fn alloc(&self, layout: Layout) -> *mut u8; - - /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must denote a block of memory currently allocated via - /// this allocator, - /// - /// * `layout` must be the same layout that was used - /// to allocate that block of memory, - #[stable(feature = "global_alloc", since = "1.28.0")] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); - - /// Behaves like `alloc`, but also ensures that the contents - /// are set to zero before being returned. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `alloc` is. - /// However the allocated block of memory is guaranteed to be initialized. - /// - /// # Errors - /// - /// Returning a null pointer indicates that either memory is exhausted - /// or `layout` does not meet allocator's size or alignment constraints, - /// just as in `alloc`. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - #[stable(feature = "global_alloc", since = "1.28.0")] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let size = layout.size(); - let ptr = self.alloc(layout); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, size); - } - ptr - } - - /// Shrink or grow a block of memory to the given `new_size`. - /// The block is described by the given `ptr` pointer and `layout`. - /// - /// If this returns a non-null pointer, then ownership of the memory block - /// referenced by `ptr` has been transferred to this allocator. - /// The memory may or may not have been deallocated, - /// and should be considered unusable (unless of course it was - /// transferred back to the caller again via the return value of - /// this method). The new memory block is allocated with `layout`, but - /// with the `size` updated to `new_size`. - /// - /// If this method returns null, then ownership of the memory - /// block has not been transferred to this allocator, and the - /// contents of the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must be the same layout that was used - /// to allocate that block of memory, - /// - /// * `new_size` must be greater than zero. - /// - /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, - /// must not overflow (i.e., the rounded value must be less than `usize::MAX`). - /// - /// (Extension subtraits might provide more specific bounds on - /// behavior, e.g., guarantee a sentinel address or a null pointer - /// in response to a zero-size allocation request.) - /// - /// # Errors - /// - /// Returns null if the new layout does not meet the size - /// and alignment constraints of the allocator, or if reallocation - /// otherwise fails. - /// - /// Implementations are encouraged to return null on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - #[stable(feature = "global_alloc", since = "1.28.0")] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); - let new_ptr = self.alloc(new_layout); - if !new_ptr.is_null() { - ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size)); - self.dealloc(ptr, layout); - } - new_ptr - } -} - -/// An implementation of `AllocRef` can allocate, reallocate, and -/// deallocate arbitrary blocks of data described via `Layout`. -/// -/// `AllocRef` is designed to be implemented on ZSTs, references, or -/// smart pointers because having an allocator like `MyAlloc([u8; N])` -/// cannot be moved, without updating the pointers to the allocated -/// memory. -/// -/// Some of the methods require that a memory block be *currently -/// allocated* via an allocator. This means that: -/// -/// * the starting address for that memory block was previously -/// returned by a previous call to an allocation method (`alloc`, -/// `alloc_zeroed`) or reallocation method (`realloc`), and -/// -/// * the memory block has not been subsequently deallocated, where -/// blocks are deallocated either by being passed to a deallocation -/// method (`dealloc`) or by being passed to a reallocation method -/// (see above) that returns `Ok`. -/// -/// Unlike [`GlobalAlloc`], zero-sized allocations are allowed in -/// `AllocRef`. If an underlying allocator does not support this (like -/// jemalloc) or return a null pointer (such as `libc::malloc`), this case -/// must be caught. In this case [`Layout::dangling()`] can be used to -/// create a dangling, but aligned `NonNull`. -/// -/// Some of the methods require that a layout *fit* a memory block. -/// What it means for a layout to "fit" a memory block means (or -/// equivalently, for a memory block to "fit" a layout) is that the -/// following two conditions must hold: -/// -/// 1. The block's starting address must be aligned to `layout.align()`. -/// -/// 2. The block's size must fall in the range `[use_min, use_max]`, where: -/// -/// * `use_min` is `layout.size()`, and -/// -/// * `use_max` is the capacity that was returned. -/// -/// Note that: -/// -/// * the size of the layout most recently used to allocate the block -/// is guaranteed to be in the range `[use_min, use_max]`, and -/// -/// * a lower-bound on `use_max` can be safely approximated by a call to -/// `usable_size`. -/// -/// * if a layout `k` fits a memory block (denoted by `ptr`) -/// currently allocated via an allocator `a`, then it is legal to -/// use that layout to deallocate it, i.e., `a.dealloc(ptr, k);`. -/// -/// * if an allocator does not support overallocating, it is fine to -/// simply return `layout.size()` as the allocated size. -/// -/// [`GlobalAlloc`]: self::GlobalAlloc -/// [`Layout::dangling()`]: self::Layout::dangling -/// -/// # Safety -/// -/// The `AllocRef` trait is an `unsafe` trait for a number of reasons, and -/// implementors must ensure that they adhere to these contracts: -/// -/// * Pointers returned from allocation functions must point to valid memory and -/// retain their validity until at least one instance of `AllocRef` is dropped -/// itself. -/// -/// * Cloning or moving the allocator must not invalidate pointers returned -/// from this allocator. Cloning must return a reference to the same allocator. -/// -/// * `Layout` queries and calculations in general must be correct. Callers of -/// this trait are allowed to rely on the contracts defined on each method, -/// and implementors must ensure such contracts remain true. -/// -/// Note that this list may get tweaked over time as clarifications are made in -/// the future. -#[unstable(feature = "allocator_api", issue = "32838")] -pub unsafe trait AllocRef { - /// On success, returns a pointer meeting the size and alignment - /// guarantees of `layout` and the actual size of the allocated block, - /// which must be greater than or equal to `layout.size()`. - /// - /// If this method returns an `Ok(addr)`, then the `addr` returned - /// will be non-null address pointing to a block of storage - /// suitable for holding an instance of `layout`. - /// - /// The returned block of storage may or may not have its contents - /// initialized. (Extension subtraits might restrict this - /// behavior, e.g., to ensure initialization to particular sets of - /// bit patterns.) - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints. - /// - /// Implementations are encouraged to return `Err` on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - fn alloc(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr>; - - /// Deallocate the memory referenced by `ptr`. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must denote a block of memory currently allocated via - /// this allocator, - /// - /// * `layout` must *fit* that block of memory, - /// - /// * In addition to fitting the block of memory `layout`, the - /// alignment of the `layout` must match the alignment used - /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); - - /// Behaves like `alloc`, but also ensures that the contents - /// are set to zero before being returned. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints, just as in `alloc`. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - fn alloc_zeroed(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { - let size = layout.size(); - let result = self.alloc(layout); - if let Ok((p, _)) = result { - unsafe { ptr::write_bytes(p.as_ptr(), 0, size) } - } - result - } - - // == METHODS FOR MEMORY REUSE == - // realloc, realloc_zeroed, grow_in_place, grow_in_place_zeroed, shrink_in_place - - /// Returns a pointer suitable for holding data described by - /// a new layout with `layout`’s alignment and a size given - /// by `new_size` and the actual size of the allocated block. - /// The latter is greater than or equal to `layout.size()`. - /// To accomplish this, the allocator may extend or shrink - /// the allocation referenced by `ptr` to fit the new layout. - /// - /// If this returns `Ok`, then ownership of the memory block - /// referenced by `ptr` has been transferred to this - /// allocator. The memory may or may not have been freed, and - /// should be considered unusable (unless of course it was - /// transferred back to the caller again via the return value of - /// this method). - /// - /// If this method returns `Err`, then ownership of the memory - /// block has not been transferred to this allocator, and the - /// contents of the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above). (The `new_size` - /// argument need not fit it.) - /// - /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, - /// must not overflow (i.e., the rounded value must be less than `usize::MAX`). - /// - /// (Extension subtraits might provide more specific bounds on - /// behavior, e.g., guarantee a sentinel address or a null pointer - /// in response to a zero-size allocation request.) - /// - /// # Errors - /// - /// Returns `Err` only if the new layout - /// does not meet the allocator's size - /// and alignment constraints of the allocator, or if reallocation - /// otherwise fails. - /// - /// Implementations are encouraged to return `Err` on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - unsafe fn realloc( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - ) -> Result<(NonNull, usize), AllocErr> { - let old_size = layout.size(); - - if new_size > old_size { - if let Ok(size) = self.grow_in_place(ptr, layout, new_size) { - return Ok((ptr, size)); - } - } else if new_size < old_size { - if let Ok(size) = self.shrink_in_place(ptr, layout, new_size) { - return Ok((ptr, size)); - } - } else { - return Ok((ptr, new_size)); - } - - // otherwise, fall back on alloc + copy + dealloc. - let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); - let result = self.alloc(new_layout); - if let Ok((new_ptr, _)) = result { - ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), cmp::min(old_size, new_size)); - self.dealloc(ptr, layout); - } - result - } - - /// Behaves like `realloc`, but also ensures that the new contents - /// are set to zero before being returned. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `realloc` is. - /// - /// # Errors - /// - /// Returns `Err` only if the new layout - /// does not meet the allocator's size - /// and alignment constraints of the allocator, or if reallocation - /// otherwise fails. - /// - /// Implementations are encouraged to return `Err` on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - unsafe fn realloc_zeroed( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - ) -> Result<(NonNull, usize), AllocErr> { - let old_size = layout.size(); - - if new_size > old_size { - if let Ok(size) = self.grow_in_place_zeroed(ptr, layout, new_size) { - return Ok((ptr, size)); - } - } else if new_size < old_size { - if let Ok(size) = self.shrink_in_place(ptr, layout, new_size) { - return Ok((ptr, size)); - } - } else { - return Ok((ptr, new_size)); - } - - // otherwise, fall back on alloc + copy + dealloc. - let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); - let result = self.alloc_zeroed(new_layout); - if let Ok((new_ptr, _)) = result { - ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), cmp::min(old_size, new_size)); - self.dealloc(ptr, layout); - } - result - } - - /// Attempts to extend the allocation referenced by `ptr` to fit `new_size`. - /// - /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_size`, and thus can - /// be used to carry data of a layout of that size and same alignment as - /// `layout`. The returned value is the new size of the allocated block. - /// (The allocator is allowed to expend effort to accomplish this, such - /// as extending the memory block to include successor blocks, or virtual - /// memory tricks.) - /// - /// Regardless of what this method returns, ownership of the - /// memory block referenced by `ptr` has not been transferred, and - /// the contents of the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_size` argument need not fit it, - /// - /// * `new_size` must not be less than `layout.size()`, - /// - /// # Errors - /// - /// Returns `Err(CannotReallocInPlace)` when the allocator is - /// unable to assert that the memory block referenced by `ptr` - /// could fit `layout`. - /// - /// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error` - /// function; clients are expected either to be able to recover from - /// `grow_in_place` failures without aborting, or to fall back on - /// another reallocation method before resorting to an abort. - #[inline] - unsafe fn grow_in_place( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - ) -> Result { - let _ = ptr; - let _ = layout; - let _ = new_size; - Err(CannotReallocInPlace) - } - - /// Behaves like `grow_in_place`, but also ensures that the new - /// contents are set to zero before being returned. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `grow_in_place` is. - /// - /// # Errors - /// - /// Returns `Err(CannotReallocInPlace)` when the allocator is - /// unable to assert that the memory block referenced by `ptr` - /// could fit `layout`. - /// - /// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error` - /// function; clients are expected either to be able to recover from - /// `grow_in_place` failures without aborting, or to fall back on - /// another reallocation method before resorting to an abort. - unsafe fn grow_in_place_zeroed( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - ) -> Result { - let size = self.grow_in_place(ptr, layout, new_size)?; - ptr.as_ptr().add(layout.size()).write_bytes(0, new_size - layout.size()); - Ok(size) - } - - /// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`. - /// - /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_size`, and - /// thus can only be used to carry data of that smaller - /// layout. The returned value is the new size the allocated block. - /// (The allocator is allowed to take advantage of this, - /// carving off portions of the block for reuse elsewhere.) The - /// truncated contents of the block within the smaller layout are - /// unaltered, and ownership of block has not been transferred. - /// - /// If this returns `Err`, then the memory block is considered to - /// still represent the original (larger) `layout`. None of the - /// block has been carved off for reuse elsewhere, ownership of - /// the memory block has not been transferred, and the contents of - /// the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_size` argument need not fit it, - /// - /// * `new_size` must not be greater than `layout.size()`, - /// - /// # Errors - /// - /// Returns `Err(CannotReallocInPlace)` when the allocator is - /// unable to assert that the memory block referenced by `ptr` - /// could fit `layout`. - /// - /// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error` - /// function; clients are expected either to be able to recover from - /// `shrink_in_place` failures without aborting, or to fall back - /// on another reallocation method before resorting to an abort. - #[inline] - unsafe fn shrink_in_place( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - ) -> Result { - let _ = ptr; - let _ = layout; - let _ = new_size; - Err(CannotReallocInPlace) - } -} diff --git a/src/libcore/alloc/global.rs b/src/libcore/alloc/global.rs new file mode 100644 index 0000000000000..147fe696ac02f --- /dev/null +++ b/src/libcore/alloc/global.rs @@ -0,0 +1,198 @@ +use crate::alloc::Layout; +use crate::cmp; +use crate::ptr; + +/// A memory allocator that can be registered as the standard library’s default +/// through the `#[global_allocator]` attribute. +/// +/// Some of the methods require that a memory block be *currently +/// allocated* via an allocator. This means that: +/// +/// * the starting address for that memory block was previously +/// returned by a previous call to an allocation method +/// such as `alloc`, and +/// +/// * the memory block has not been subsequently deallocated, where +/// blocks are deallocated either by being passed to a deallocation +/// method such as `dealloc` or by being +/// passed to a reallocation method that returns a non-null pointer. +/// +/// +/// # Example +/// +/// ```no_run +/// use std::alloc::{GlobalAlloc, Layout, alloc}; +/// use std::ptr::null_mut; +/// +/// struct MyAllocator; +/// +/// unsafe impl GlobalAlloc for MyAllocator { +/// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() } +/// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +/// } +/// +/// #[global_allocator] +/// static A: MyAllocator = MyAllocator; +/// +/// fn main() { +/// unsafe { +/// assert!(alloc(Layout::new::()).is_null()) +/// } +/// } +/// ``` +/// +/// # Safety +/// +/// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and +/// implementors must ensure that they adhere to these contracts: +/// +/// * It's undefined behavior if global allocators unwind. This restriction may +/// be lifted in the future, but currently a panic from any of these +/// functions may lead to memory unsafety. +/// +/// * `Layout` queries and calculations in general must be correct. Callers of +/// this trait are allowed to rely on the contracts defined on each method, +/// and implementors must ensure such contracts remain true. +#[stable(feature = "global_alloc", since = "1.28.0")] +pub unsafe trait GlobalAlloc { + /// Allocate memory as described by the given `layout`. + /// + /// Returns a pointer to newly-allocated memory, + /// or null to indicate allocation failure. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure that `layout` has non-zero size. + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g., guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// The allocated block of memory may or may not be initialized. + /// + /// # Errors + /// + /// Returning a null pointer indicates that either memory is exhausted + /// or `layout` does not meet this allocator's size or alignment constraints. + /// + /// Implementations are encouraged to return null on memory + /// exhaustion rather than aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the [`handle_alloc_error`] function, + /// rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "global_alloc", since = "1.28.0")] + unsafe fn alloc(&self, layout: Layout) -> *mut u8; + + /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must denote a block of memory currently allocated via + /// this allocator, + /// + /// * `layout` must be the same layout that was used + /// to allocate that block of memory, + #[stable(feature = "global_alloc", since = "1.28.0")] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); + + /// Behaves like `alloc`, but also ensures that the contents + /// are set to zero before being returned. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// However the allocated block of memory is guaranteed to be initialized. + /// + /// # Errors + /// + /// Returning a null pointer indicates that either memory is exhausted + /// or `layout` does not meet allocator's size or alignment constraints, + /// just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the [`handle_alloc_error`] function, + /// rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "global_alloc", since = "1.28.0")] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let ptr = self.alloc(layout); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, size); + } + ptr + } + + /// Shrink or grow a block of memory to the given `new_size`. + /// The block is described by the given `ptr` pointer and `layout`. + /// + /// If this returns a non-null pointer, then ownership of the memory block + /// referenced by `ptr` has been transferred to this allocator. + /// The memory may or may not have been deallocated, + /// and should be considered unusable (unless of course it was + /// transferred back to the caller again via the return value of + /// this method). The new memory block is allocated with `layout`, but + /// with the `size` updated to `new_size`. + /// + /// If this method returns null, then ownership of the memory + /// block has not been transferred to this allocator, and the + /// contents of the memory block are unaltered. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must be the same layout that was used + /// to allocate that block of memory, + /// + /// * `new_size` must be greater than zero. + /// + /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, + /// must not overflow (i.e., the rounded value must be less than `usize::MAX`). + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g., guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// # Errors + /// + /// Returns null if the new layout does not meet the size + /// and alignment constraints of the allocator, or if reallocation + /// otherwise fails. + /// + /// Implementations are encouraged to return null on memory + /// exhaustion rather than panicking or aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, + /// rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "global_alloc", since = "1.28.0")] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + let new_ptr = self.alloc(new_layout); + if !new_ptr.is_null() { + ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size)); + self.dealloc(ptr, layout); + } + new_ptr + } +} diff --git a/src/libcore/alloc/layout.rs b/src/libcore/alloc/layout.rs new file mode 100644 index 0000000000000..a09c2387d0de2 --- /dev/null +++ b/src/libcore/alloc/layout.rs @@ -0,0 +1,374 @@ +use crate::cmp; +use crate::fmt; +use crate::mem; +use crate::num::NonZeroUsize; +use crate::ptr::NonNull; + +const fn size_align() -> (usize, usize) { + (mem::size_of::(), mem::align_of::()) +} + +/// Layout of a block of memory. +/// +/// An instance of `Layout` describes a particular layout of memory. +/// You build a `Layout` up as an input to give to an allocator. +/// +/// All layouts have an associated size and a power-of-two alignment. +/// +/// (Note that layouts are *not* required to have non-zero size, +/// even though `GlobalAlloc` requires that all memory requests +/// be non-zero in size. A caller must either ensure that conditions +/// like this are met, use specific allocators with looser +/// requirements, or use the more lenient `AllocRef` interface.) +#[stable(feature = "alloc_layout", since = "1.28.0")] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[lang = "alloc_layout"] +pub struct Layout { + // size of the requested block of memory, measured in bytes. + size_: usize, + + // alignment of the requested block of memory, measured in bytes. + // we ensure that this is always a power-of-two, because API's + // like `posix_memalign` require it and it is a reasonable + // constraint to impose on Layout constructors. + // + // (However, we do not analogously require `align >= sizeof(void*)`, + // even though that is *also* a requirement of `posix_memalign`.) + align_: NonZeroUsize, +} + +impl Layout { + /// Constructs a `Layout` from a given `size` and `align`, + /// or returns `LayoutErr` if any of the following conditions + /// are not met: + /// + /// * `align` must not be zero, + /// + /// * `align` must be a power of two, + /// + /// * `size`, when rounded up to the nearest multiple of `align`, + /// must not overflow (i.e., the rounded value must be less than + /// or equal to `usize::MAX`). + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn from_size_align(size: usize, align: usize) -> Result { + if !align.is_power_of_two() { + return Err(LayoutErr { private: () }); + } + + // (power-of-two implies align != 0.) + + // Rounded up size is: + // size_rounded_up = (size + align - 1) & !(align - 1); + // + // We know from above that align != 0. If adding (align - 1) + // does not overflow, then rounding up will be fine. + // + // Conversely, &-masking with !(align - 1) will subtract off + // only low-order-bits. Thus if overflow occurs with the sum, + // the &-mask cannot subtract enough to undo that overflow. + // + // Above implies that checking for summation overflow is both + // necessary and sufficient. + if size > usize::MAX - (align - 1) { + return Err(LayoutErr { private: () }); + } + + // SAFETY: the conditions for `from_size_align_unchecked` have been + // checked above. + unsafe { Ok(Layout::from_size_align_unchecked(size, align)) } + } + + /// Creates a layout, bypassing all checks. + /// + /// # Safety + /// + /// This function is unsafe as it does not verify the preconditions from + /// [`Layout::from_size_align`](#method.from_size_align). + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_stable(feature = "alloc_layout", since = "1.28.0")] + #[inline] + pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { + Layout { size_: size, align_: NonZeroUsize::new_unchecked(align) } + } + + /// The minimum size in bytes for a memory block of this layout. + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn size(&self) -> usize { + self.size_ + } + + /// The minimum byte alignment for a memory block of this layout. + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn align(&self) -> usize { + self.align_.get() + } + + /// Constructs a `Layout` suitable for holding a value of type `T`. + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")] + #[inline] + pub const fn new() -> Self { + let (size, align) = size_align::(); + // SAFETY: the align is guaranteed by Rust to be a power of two and + // the size+align combo is guaranteed to fit in our address space. As a + // result use the unchecked constructor here to avoid inserting code + // that panics if it isn't optimized well enough. + unsafe { Layout::from_size_align_unchecked(size, align) } + } + + /// Produces layout describing a record that could be used to + /// allocate backing structure for `T` (which could be a trait + /// or other unsized type like a slice). + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[inline] + pub fn for_value(t: &T) -> Self { + let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); + debug_assert!(Layout::from_size_align(size, align).is_ok()); + // SAFETY: see rationale in `new` for why this is using an unsafe variant below + unsafe { Layout::from_size_align_unchecked(size, align) } + } + + /// Creates a `NonNull` that is dangling, but well-aligned for this Layout. + /// + /// Note that the pointer value may potentially represent a valid pointer, + /// which means this must not be used as a "not yet initialized" + /// sentinel value. Types that lazily allocate must track initialization by + /// some other means. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub const fn dangling(&self) -> NonNull { + // SAFETY: align is guaranteed to be non-zero + unsafe { NonNull::new_unchecked(self.align() as *mut u8) } + } + + /// Creates a layout describing the record that can hold a value + /// of the same layout as `self`, but that also is aligned to + /// alignment `align` (measured in bytes). + /// + /// If `self` already meets the prescribed alignment, then returns + /// `self`. + /// + /// Note that this method does not add any padding to the overall + /// size, regardless of whether the returned layout has a different + /// alignment. In other words, if `K` has size 16, `K.align_to(32)` + /// will *still* have size 16. + /// + /// Returns an error if the combination of `self.size()` and the given + /// `align` violates the conditions listed in + /// [`Layout::from_size_align`](#method.from_size_align). + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn align_to(&self, align: usize) -> Result { + Layout::from_size_align(self.size(), cmp::max(self.align(), align)) + } + + /// Returns the amount of padding we must insert after `self` + /// to ensure that the following address will satisfy `align` + /// (measured in bytes). + /// + /// e.g., if `self.size()` is 9, then `self.padding_needed_for(4)` + /// returns 3, because that is the minimum number of bytes of + /// padding required to get a 4-aligned address (assuming that the + /// corresponding memory block starts at a 4-aligned address). + /// + /// The return value of this function has no meaning if `align` is + /// not a power-of-two. + /// + /// Note that the utility of the returned value requires `align` + /// to be less than or equal to the alignment of the starting + /// address for the whole allocated block of memory. One way to + /// satisfy this constraint is to ensure `align <= self.align()`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn padding_needed_for(&self, align: usize) -> usize { + let len = self.size(); + + // Rounded up value is: + // len_rounded_up = (len + align - 1) & !(align - 1); + // and then we return the padding difference: `len_rounded_up - len`. + // + // We use modular arithmetic throughout: + // + // 1. align is guaranteed to be > 0, so align - 1 is always + // valid. + // + // 2. `len + align - 1` can overflow by at most `align - 1`, + // so the &-mask with `!(align - 1)` will ensure that in the + // case of overflow, `len_rounded_up` will itself be 0. + // Thus the returned padding, when added to `len`, yields 0, + // which trivially satisfies the alignment `align`. + // + // (Of course, attempts to allocate blocks of memory whose + // size and padding overflow in the above manner should cause + // the allocator to yield an error anyway.) + + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + len_rounded_up.wrapping_sub(len) + } + + /// Creates a layout by rounding the size of this layout up to a multiple + /// of the layout's alignment. + /// + /// This is equivalent to adding the result of `padding_needed_for` + /// to the layout's current size. + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn pad_to_align(&self) -> Layout { + let pad = self.padding_needed_for(self.align()); + // This cannot overflow. Quoting from the invariant of Layout: + // > `size`, when rounded up to the nearest multiple of `align`, + // > must not overflow (i.e., the rounded value must be less than + // > `usize::MAX`) + let new_size = self.size() + pad; + + Layout::from_size_align(new_size, self.align()).unwrap() + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with a suitable amount of padding between each to + /// ensure that each instance is given its requested size and + /// alignment. On success, returns `(k, offs)` where `k` is the + /// layout of the array and `offs` is the distance between the start + /// of each element in the array. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> { + // This cannot overflow. Quoting from the invariant of Layout: + // > `size`, when rounded up to the nearest multiple of `align`, + // > must not overflow (i.e., the rounded value must be less than + // > `usize::MAX`) + let padded_size = self.size() + self.padding_needed_for(self.align()); + let alloc_size = padded_size.checked_mul(n).ok_or(LayoutErr { private: () })?; + + // SAFETY: self.align is already known to be valid and alloc_size has been + // padded already. + unsafe { Ok((Layout::from_size_align_unchecked(alloc_size, self.align()), padded_size)) } + } + + /// Creates a layout describing the record for `self` followed by + /// `next`, including any necessary padding to ensure that `next` + /// will be properly aligned, but *no trailing padding*. + /// + /// In order to match C representation layout `repr(C)`, you should + /// call `pad_to_align` after extending the layout with all fields. + /// (There is no way to match the default Rust representation + /// layout `repr(Rust)`, as it is unspecified.) + /// + /// Note that the alignment of the resulting layout will be the maximum of + /// those of `self` and `next`, in order to ensure alignment of both parts. + /// + /// Returns `Ok((k, offset))`, where `k` is layout of the concatenated + /// record and `offset` is the relative location, in bytes, of the + /// start of the `next` embedded within the concatenated record + /// (assuming that the record itself starts at offset 0). + /// + /// On arithmetic overflow, returns `LayoutErr`. + /// + /// # Examples + /// + /// To calculate the layout of a `#[repr(C)]` structure and the offsets of + /// the fields from its fields' layouts: + /// + /// ```rust + /// # use std::alloc::{Layout, LayoutErr}; + /// pub fn repr_c(fields: &[Layout]) -> Result<(Layout, Vec), LayoutErr> { + /// let mut offsets = Vec::new(); + /// let mut layout = Layout::from_size_align(0, 1)?; + /// for &field in fields { + /// let (new_layout, offset) = layout.extend(field)?; + /// layout = new_layout; + /// offsets.push(offset); + /// } + /// // Remember to finalize with `pad_to_align`! + /// Ok((layout.pad_to_align(), offsets)) + /// } + /// # // test that it works + /// # #[repr(C)] struct S { a: u64, b: u32, c: u16, d: u32 } + /// # let s = Layout::new::(); + /// # let u16 = Layout::new::(); + /// # let u32 = Layout::new::(); + /// # let u64 = Layout::new::(); + /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); + /// ``` + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> { + let new_align = cmp::max(self.align(), next.align()); + let pad = self.padding_needed_for(next.align()); + + let offset = self.size().checked_add(pad).ok_or(LayoutErr { private: () })?; + let new_size = offset.checked_add(next.size()).ok_or(LayoutErr { private: () })?; + + let layout = Layout::from_size_align(new_size, new_align)?; + Ok((layout, offset)) + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with no padding between each instance. + /// + /// Note that, unlike `repeat`, `repeat_packed` does not guarantee + /// that the repeated instances of `self` will be properly + /// aligned, even if a given instance of `self` is properly + /// aligned. In other words, if the layout returned by + /// `repeat_packed` is used to allocate an array, it is not + /// guaranteed that all elements in the array will be properly + /// aligned. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub fn repeat_packed(&self, n: usize) -> Result { + let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?; + Layout::from_size_align(size, self.align()) + } + + /// Creates a layout describing the record for `self` followed by + /// `next` with no additional padding between the two. Since no + /// padding is inserted, the alignment of `next` is irrelevant, + /// and is not incorporated *at all* into the resulting layout. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub fn extend_packed(&self, next: Self) -> Result { + let new_size = self.size().checked_add(next.size()).ok_or(LayoutErr { private: () })?; + Layout::from_size_align(new_size, self.align()) + } + + /// Creates a layout describing the record for a `[T; n]`. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn array(n: usize) -> Result { + let (layout, offset) = Layout::new::().repeat(n)?; + debug_assert_eq!(offset, mem::size_of::()); + Ok(layout.pad_to_align()) + } +} + +/// The parameters given to `Layout::from_size_align` +/// or some other `Layout` constructor +/// do not satisfy its documented constraints. +#[stable(feature = "alloc_layout", since = "1.28.0")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct LayoutErr { + private: (), +} + +// (we need this for downstream impl of trait Error) +#[stable(feature = "alloc_layout", since = "1.28.0")] +impl fmt::Display for LayoutErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("invalid parameters to Layout::from_size_align") + } +} diff --git a/src/libcore/alloc/mod.rs b/src/libcore/alloc/mod.rs new file mode 100644 index 0000000000000..1346fbd481003 --- /dev/null +++ b/src/libcore/alloc/mod.rs @@ -0,0 +1,414 @@ +//! Memory allocation APIs + +#![stable(feature = "alloc_module", since = "1.28.0")] + +mod global; +mod layout; + +#[stable(feature = "global_alloc", since = "1.28.0")] +pub use self::global::GlobalAlloc; +#[stable(feature = "alloc_layout", since = "1.28.0")] +pub use self::layout::{Layout, LayoutErr}; + +use crate::fmt; +use crate::ptr::{self, NonNull}; + +/// The `AllocErr` error indicates an allocation failure +/// that may be due to resource exhaustion or to +/// something wrong when combining the given input arguments with this +/// allocator. +#[unstable(feature = "allocator_api", issue = "32838")] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct AllocErr; + +// (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] +impl fmt::Display for AllocErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("memory allocation failed") + } +} + +/// A desired initial state for allocated memory. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[unstable(feature = "allocator_api", issue = "32838")] +pub enum AllocInit { + /// The contents of the new memory are uninitialized. + Uninitialized, + /// The new memory is guaranteed to be zeroed. + Zeroed, +} + +impl AllocInit { + /// Initialize the specified memory block. + /// + /// This behaves like calling [`AllocInit::init_offset(memory, 0)`][off]. + /// + /// [off]: AllocInit::init_offset + /// + /// # Safety + /// + /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes. + /// + /// [valid]: ../../core/ptr/index.html#safety + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub unsafe fn init(self, memory: MemoryBlock) { + self.init_offset(memory, 0) + } + + /// Initialize the memory block like specified by `init` at the specified `offset`. + /// + /// This is a no-op for [`AllocInit::Uninitialized`][] and writes zeroes for + /// [`AllocInit::Zeroed`][] at `ptr + offset` until `ptr + layout.size()`. + /// + /// # Safety + /// + /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes. + /// * `offset` must be smaller than or equal to `memory.size` + /// + /// [valid]: ../../core/ptr/index.html#safety + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub unsafe fn init_offset(self, memory: MemoryBlock, offset: usize) { + debug_assert!( + offset <= memory.size, + "`offset` must be smaller than or equal to `memory.size`" + ); + match self { + AllocInit::Uninitialized => (), + AllocInit::Zeroed => { + memory.ptr.as_ptr().add(offset).write_bytes(0, memory.size - offset) + } + } + } +} + +/// Represents a block of allocated memory returned by an allocator. +#[derive(Debug, Copy, Clone)] +#[unstable(feature = "allocator_api", issue = "32838")] +pub struct MemoryBlock { + pub ptr: NonNull, + pub size: usize, +} + +/// A placement constraint when growing or shrinking an existing allocation. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[unstable(feature = "allocator_api", issue = "32838")] +pub enum ReallocPlacement { + /// The allocator is allowed to move the allocation to a different memory address. + // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal + // allocator" and link it at "valid location". + /// + /// If the allocation _does_ move, it's the responsibility of the allocator + /// to also move the data from the previous location to the new location. + MayMove, + /// The address of the new memory must not change. + /// + /// If the allocation would have to be moved to a new location to fit, the + /// reallocation request will fail. + InPlace, +} + +/// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of +/// data described via [`Layout`][]. +/// +/// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having +/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the +/// allocated memory. +/// +/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying +/// allocator does not support this (like jemalloc) or return a null pointer (such as +/// `libc::malloc`), this must be caught by the implementation. +/// +/// ### Currently allocated memory +/// +/// Some of the methods require that a memory block be *currently allocated* via an allocator. This +/// means that: +/// +/// * the starting address for that memory block was previously returned by [`alloc`], [`grow`], or +/// [`shrink`], and +/// +/// * the memory block has not been subsequently deallocated, where blocks are either deallocated +/// directly by being passed to [`dealloc`] or were changed by being passed to [`grow`] or +/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer +/// remains valid. +/// +/// [`alloc`]: AllocRef::alloc +/// [`grow`]: AllocRef::grow +/// [`shrink`]: AllocRef::shrink +/// [`dealloc`]: AllocRef::dealloc +/// +/// ### Memory fitting +/// +/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to +/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the +/// following conditions must hold: +/// +/// * The block must be allocated with the same alignment as [`layout.align()`], and +/// +/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: +/// - `min` is the size of the layout most recently used to allocate the block, and +/// - `max` is the latest actual size returned from [`alloc`], [`grow`], or [`shrink`]. +/// +/// [`layout.align()`]: Layout::align +/// [`layout.size()`]: Layout::size +/// +/// # Safety +/// +/// * Memory blocks returned from an allocator must point to valid memory and retain their validity +/// until the instance and all of its clones are dropped, +/// +/// * cloning or moving the allocator must not invalidate memory blocks returned from this +/// allocator. A cloned allocator must behave like the same allocator, and +/// +/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other +/// method of the allocator. +/// +/// [*currently allocated*]: #currently-allocated-memory +#[unstable(feature = "allocator_api", issue = "32838")] +pub unsafe trait AllocRef { + /// Attempts to allocate a block of memory. + /// + /// On success, returns a [`MemoryBlock`][] meeting the size and alignment guarantees of `layout`. + /// + /// The returned block may have a larger size than specified by `layout.size()` and is + /// initialized as specified by [`init`], all the way up to the returned size of the block. + /// + /// [`init`]: AllocInit + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet + /// allocator's size or alignment constraints. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result; + + /// Deallocates the memory referenced by `ptr`. + /// + /// # Safety + /// + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and + /// * `layout` must [*fit*] that block of memory. + /// + /// [*currently allocated*]: #currently-allocated-memory + /// [*fit*]: #memory-fitting + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); + + /// Attempts to extend the memory block. + /// + /// Returns a new [`MemoryBlock`][] containing a pointer and the actual size of the allocated + /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s + /// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the + /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is + /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`. + /// + /// If [`MayMove`] is used then ownership of the memory block referenced by `ptr` + /// is transferred to this allocator. The memory may or may not be freed, and should be + /// considered unusable (unless of course it is transferred back to the caller again via the + /// return value of this method). + /// + /// If this method returns `Err`, then ownership of the memory block has not been transferred to + /// this allocator, and the contents of the memory block are unaltered. + /// + /// The memory block will contain the following contents after a successful call to `grow`: + /// * Bytes `0..layout.size()` are preserved from the original allocation. + /// * Bytes `layout.size()..old_size` will either be preserved or initialized according to + /// [`init`], depending on the allocator implementation. `old_size` refers to the size of + /// the `MemoryBlock` prior to the `grow` call, which may be larger than the size + /// that was originally requested when it was allocated. + /// * Bytes `old_size..new_size` are initialized according to [`init`]. `new_size` refers to + /// the size of the `MemoryBlock` returned by the `grow` call. + /// + /// [`InPlace`]: ReallocPlacement::InPlace + /// [`MayMove`]: ReallocPlacement::MayMove + /// [`placement`]: ReallocPlacement + /// [`init`]: AllocInit + /// + /// # Safety + /// + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, + /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), + // We can't require that `new_size` is strictly greater than `memory.size` because of ZSTs. + // An alternative would be + // * `new_size must be strictly greater than `memory.size` or both are zero + /// * `new_size` must be greater than or equal to `layout.size()`, and + /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow + /// (i.e., the rounded value must be less than or equal to `usize::MAX`). + /// + /// [*currently allocated*]: #currently-allocated-memory + /// [*fit*]: #memory-fitting + /// + /// # Errors + /// + /// Returns `Err` if the new layout does not meet the allocator's size and alignment + /// constraints of the allocator, or if growing otherwise fails. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + unsafe fn grow( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + init: AllocInit, + ) -> Result { + match placement { + ReallocPlacement::InPlace => Err(AllocErr), + ReallocPlacement::MayMove => { + let size = layout.size(); + debug_assert!( + new_size >= size, + "`new_size` must be greater than or equal to `layout.size()`" + ); + + if new_size == size { + return Ok(MemoryBlock { ptr, size }); + } + + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + let new_memory = self.alloc(new_layout, init)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), size); + self.dealloc(ptr, layout); + Ok(new_memory) + } + } + } + + /// Attempts to shrink the memory block. + /// + /// Returns a new [`MemoryBlock`][] containing a pointer and the actual size of the allocated + /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s + /// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the + /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is + /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`. + /// + /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been + /// transferred to this allocator. The memory may or may not have been freed, and should be + /// considered unusable unless it was transferred back to the caller again via the + /// return value of this method. + /// + /// If this method returns `Err`, then ownership of the memory block has not been transferred to + /// this allocator, and the contents of the memory block are unaltered. + /// + /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`]. + /// + /// [`InPlace`]: ReallocPlacement::InPlace + /// [`placement`]: ReallocPlacement + /// + /// # Safety + /// + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, + /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), and + // We can't require that `new_size` is strictly smaller than `memory.size` because of ZSTs. + // An alternative would be + // * `new_size must be strictly smaller than `memory.size` or both are zero + /// * `new_size` must be smaller than or equal to `layout.size()`. + /// + /// [*currently allocated*]: #currently-allocated-memory + /// [*fit*]: #memory-fitting + /// + /// # Errors + /// + /// Returns `Err` if the new layout does not meet the allocator's size and alignment + /// constraints of the allocator, or if shrinking otherwise fails. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + unsafe fn shrink( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + ) -> Result { + match placement { + ReallocPlacement::InPlace => Err(AllocErr), + ReallocPlacement::MayMove => { + let size = layout.size(); + debug_assert!( + new_size <= size, + "`new_size` must be smaller than or equal to `layout.size()`" + ); + + if new_size == size { + return Ok(MemoryBlock { ptr, size }); + } + + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), new_size); + self.dealloc(ptr, layout); + Ok(new_memory) + } + } + } + + /// Creates a "by reference" adaptor for this instance of `AllocRef`. + /// + /// The returned adaptor also implements `AllocRef` and will simply borrow this. + #[inline(always)] + fn by_ref(&mut self) -> &mut Self { + self + } +} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl AllocRef for &mut A +where + A: AllocRef + ?Sized, +{ + #[inline] + fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { + (**self).alloc(layout, init) + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + (**self).dealloc(ptr, layout) + } + + #[inline] + unsafe fn grow( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + init: AllocInit, + ) -> Result { + (**self).grow(ptr, layout, new_size, placement, init) + } + + #[inline] + unsafe fn shrink( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + ) -> Result { + (**self).shrink(ptr, layout, new_size, placement) + } +} diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 39df803bbea30..79b6304958d51 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -446,14 +446,16 @@ impl TypeId { /// # Note /// /// This is intended for diagnostic use. The exact contents and format of the -/// string are not specified, other than being a best-effort description of the -/// type. For example, `type_name::>()` could return the -/// `"Option"` or `"std::option::Option"`, but not -/// `"foobar"`. In addition, the output may change between versions of the -/// compiler. +/// string returned are not specified, other than being a best-effort +/// description of the type. For example, amongst the strings +/// that `type_name::>()` might return are `"Option"` and +/// `"std::option::Option"`. /// -/// The type name should not be considered a unique identifier of a type; -/// multiple types may share the same type name. +/// The returned string must not be considered to be a unique identifier of a +/// type as multiple types may map to the same type name. Similarly, there is no +/// guarantee that all parts of a type will appear in the returned string: for +/// example, lifetime specifiers are currently not included. In addition, the +/// output may change between versions of the compiler. /// /// The current implementation uses the same infrastructure as compiler /// diagnostics and debuginfo, but this is not guaranteed. diff --git a/src/libcore/array/iter.rs b/src/libcore/array/iter.rs index 80eaae0d4afb5..f6b8d4ba08146 100644 --- a/src/libcore/array/iter.rs +++ b/src/libcore/array/iter.rs @@ -39,7 +39,7 @@ where alive: Range, } -impl IntoIter +impl IntoIter where [T; N]: LengthAtMost32, { @@ -99,7 +99,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl Iterator for IntoIter +impl Iterator for IntoIter where [T; N]: LengthAtMost32, { @@ -146,7 +146,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl DoubleEndedIterator for IntoIter +impl DoubleEndedIterator for IntoIter where [T; N]: LengthAtMost32, { @@ -182,7 +182,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl Drop for IntoIter +impl Drop for IntoIter where [T; N]: LengthAtMost32, { @@ -195,7 +195,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl ExactSizeIterator for IntoIter +impl ExactSizeIterator for IntoIter where [T; N]: LengthAtMost32, { @@ -210,17 +210,17 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl FusedIterator for IntoIter where [T; N]: LengthAtMost32 {} +impl FusedIterator for IntoIter where [T; N]: LengthAtMost32 {} // The iterator indeed reports the correct length. The number of "alive" // elements (that will still be yielded) is the length of the range `alive`. // This range is decremented in length in either `next` or `next_back`. It is // always decremented by 1 in those methods, but only if `Some(_)` is returned. #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -unsafe impl TrustedLen for IntoIter where [T; N]: LengthAtMost32 {} +unsafe impl TrustedLen for IntoIter where [T; N]: LengthAtMost32 {} #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl Clone for IntoIter +impl Clone for IntoIter where [T; N]: LengthAtMost32, { @@ -249,7 +249,7 @@ where } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl fmt::Debug for IntoIter +impl fmt::Debug for IntoIter where [T; N]: LengthAtMost32, { diff --git a/src/libcore/array/mod.rs b/src/libcore/array/mod.rs index 937451274cfc2..549228ffffaa4 100644 --- a/src/libcore/array/mod.rs +++ b/src/libcore/array/mod.rs @@ -375,6 +375,7 @@ where } } +/// Implements comparison of arrays lexicographically. #[stable(feature = "rust1", since = "1.0.0")] impl Ord for [T; N] where diff --git a/src/libcore/benches/num/dec2flt/mod.rs b/src/libcore/benches/num/dec2flt/mod.rs index 561a4bee87ad9..305baa68729eb 100644 --- a/src/libcore/benches/num/dec2flt/mod.rs +++ b/src/libcore/benches/num/dec2flt/mod.rs @@ -1,4 +1,3 @@ -use std::f64; use test::Bencher; #[bench] diff --git a/src/libcore/benches/num/flt2dec/mod.rs b/src/libcore/benches/num/flt2dec/mod.rs index b810dd12ab61b..a1ce33d0bb49e 100644 --- a/src/libcore/benches/num/flt2dec/mod.rs +++ b/src/libcore/benches/num/flt2dec/mod.rs @@ -5,7 +5,6 @@ mod strategy { use core::num::flt2dec::MAX_SIG_DIGITS; use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; -use std::f64; use std::io::Write; use std::vec::Vec; use test::Bencher; diff --git a/src/libcore/benches/num/flt2dec/strategy/dragon.rs b/src/libcore/benches/num/flt2dec/strategy/dragon.rs index 4052fec33813c..4e1fd8bf753ca 100644 --- a/src/libcore/benches/num/flt2dec/strategy/dragon.rs +++ b/src/libcore/benches/num/flt2dec/strategy/dragon.rs @@ -1,6 +1,5 @@ use super::super::*; use core::num::flt2dec::strategy::dragon::*; -use std::{f64, i16}; use test::Bencher; #[bench] diff --git a/src/libcore/benches/num/flt2dec/strategy/grisu.rs b/src/libcore/benches/num/flt2dec/strategy/grisu.rs index 495074747c283..77ca901a90af3 100644 --- a/src/libcore/benches/num/flt2dec/strategy/grisu.rs +++ b/src/libcore/benches/num/flt2dec/strategy/grisu.rs @@ -1,6 +1,5 @@ use super::super::*; use core::num::flt2dec::strategy::grisu::*; -use std::{f64, i16}; use test::Bencher; pub fn decode_finite(v: T) -> Decoded { diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index a922d4f118b5a..fad3095f8a3fc 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -133,10 +133,9 @@ //! `Cell`. //! //! ``` -//! #![feature(core_intrinsics)] //! use std::cell::Cell; //! use std::ptr::NonNull; -//! use std::intrinsics::abort; +//! use std::process::abort; //! use std::marker::PhantomData; //! //! struct Rc { @@ -173,7 +172,7 @@ //! .strong //! .set(self.strong() //! .checked_add(1) -//! .unwrap_or_else(|| unsafe { abort() })); +//! .unwrap_or_else(|| abort() )); //! } //! } //! @@ -1023,6 +1022,31 @@ impl RefCell { } } +impl RefCell { + /// Takes the wrapped value, leaving `Default::default()` in its place. + /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_take)] + /// use std::cell::RefCell; + /// + /// let c = RefCell::new(5); + /// let five = c.take(); + /// + /// assert_eq!(five, 5); + /// assert_eq!(c.into_inner(), 0); + /// ``` + #[unstable(feature = "refcell_take", issue = "71395")] + pub fn take(&self) -> T { + self.replace(Default::default()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for RefCell where T: Send {} diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index 302400744e25d..5c5bc9adb5df2 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs @@ -9,6 +9,243 @@ use super::*; #[lang = "char"] impl char { + /// The highest valid code point a `char` can have. + /// + /// A `char` is a [Unicode Scalar Value], which means that it is a [Code + /// Point], but only ones within a certain range. `MAX` is the highest valid + /// code point that's a valid [Unicode Scalar Value]. + /// + /// [Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value + /// [Code Point]: http://www.unicode.org/glossary/#code_point + #[unstable(feature = "assoc_char_consts", reason = "recently added", issue = "71763")] + pub const MAX: char = '\u{10ffff}'; + + /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a + /// decoding error. + /// + /// It can occur, for example, when giving ill-formed UTF-8 bytes to + /// [`String::from_utf8_lossy`](string/struct.String.html#method.from_utf8_lossy). + #[unstable(feature = "assoc_char_consts", reason = "recently added", issue = "71763")] + pub const REPLACEMENT_CHARACTER: char = '\u{FFFD}'; + + /// The version of [Unicode](http://www.unicode.org/) that the Unicode parts of + /// `char` and `str` methods are based on. + /// + /// New versions of Unicode are released regularly and subsequently all methods + /// in the standard library depending on Unicode are updated. Therefore the + /// behavior of some `char` and `str` methods and the value of this constant + /// changes over time. This is *not* considered to be a breaking change. + /// + /// The version numbering scheme is explained in + /// [Unicode 11.0 or later, Section 3.1 Versions of the Unicode Standard](https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf#page=4). + #[unstable(feature = "assoc_char_consts", reason = "recently added", issue = "71763")] + pub const UNICODE_VERSION: (u8, u8, u8) = crate::unicode::UNICODE_VERSION; + + /// Creates an iterator over the UTF-16 encoded code points in `iter`, + /// returning unpaired surrogates as `Err`s. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::char::decode_utf16; + /// + /// // 𝄞music + /// let v = [ + /// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, + /// ]; + /// + /// assert_eq!( + /// decode_utf16(v.iter().cloned()) + /// .map(|r| r.map_err(|e| e.unpaired_surrogate())) + /// .collect::>(), + /// vec![ + /// Ok('𝄞'), + /// Ok('m'), Ok('u'), Ok('s'), + /// Err(0xDD1E), + /// Ok('i'), Ok('c'), + /// Err(0xD834) + /// ] + /// ); + /// ``` + /// + /// A lossy decoder can be obtained by replacing `Err` results with the replacement character: + /// + /// ``` + /// use std::char::{decode_utf16, REPLACEMENT_CHARACTER}; + /// + /// // 𝄞music + /// let v = [ + /// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, + /// ]; + /// + /// assert_eq!( + /// decode_utf16(v.iter().cloned()) + /// .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) + /// .collect::(), + /// "𝄞mus�ic�" + /// ); + /// ``` + #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[inline] + pub fn decode_utf16>(iter: I) -> DecodeUtf16 { + super::decode::decode_utf16(iter) + } + + /// Converts a `u32` to a `char`. + /// + /// Note that all `char`s are valid [`u32`]s, and can be cast to one with + /// `as`: + /// + /// ``` + /// let c = '💯'; + /// let i = c as u32; + /// + /// assert_eq!(128175, i); + /// ``` + /// + /// However, the reverse is not true: not all valid [`u32`]s are valid + /// `char`s. `from_u32()` will return `None` if the input is not a valid value + /// for a `char`. + /// + /// [`u32`]: primitive.u32.html + /// + /// For an unsafe version of this function which ignores these checks, see + /// [`from_u32_unchecked`]. + /// + /// [`from_u32_unchecked`]: #method.from_u32_unchecked + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::char; + /// + /// let c = char::from_u32(0x2764); + /// + /// assert_eq!(Some('❤'), c); + /// ``` + /// + /// Returning `None` when the input is not a valid `char`: + /// + /// ``` + /// use std::char; + /// + /// let c = char::from_u32(0x110000); + /// + /// assert_eq!(None, c); + /// ``` + #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[inline] + pub fn from_u32(i: u32) -> Option { + super::convert::from_u32(i) + } + + /// Converts a `u32` to a `char`, ignoring validity. + /// + /// Note that all `char`s are valid [`u32`]s, and can be cast to one with + /// `as`: + /// + /// ``` + /// let c = '💯'; + /// let i = c as u32; + /// + /// assert_eq!(128175, i); + /// ``` + /// + /// However, the reverse is not true: not all valid [`u32`]s are valid + /// `char`s. `from_u32_unchecked()` will ignore this, and blindly cast to + /// `char`, possibly creating an invalid one. + /// + /// [`u32`]: primitive.u32.html + /// + /// # Safety + /// + /// This function is unsafe, as it may construct invalid `char` values. + /// + /// For a safe version of this function, see the [`from_u32`] function. + /// + /// [`from_u32`]: #method.from_u32 + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::char; + /// + /// let c = unsafe { char::from_u32_unchecked(0x2764) }; + /// + /// assert_eq!('❤', c); + /// ``` + #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[inline] + pub unsafe fn from_u32_unchecked(i: u32) -> char { + super::convert::from_u32_unchecked(i) + } + + /// Converts a digit in the given radix to a `char`. + /// + /// A 'radix' here is sometimes also called a 'base'. A radix of two + /// indicates a binary number, a radix of ten, decimal, and a radix of + /// sixteen, hexadecimal, to give some common values. Arbitrary + /// radices are supported. + /// + /// `from_digit()` will return `None` if the input is not a digit in + /// the given radix. + /// + /// # Panics + /// + /// Panics if given a radix larger than 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::char; + /// + /// let c = char::from_digit(4, 10); + /// + /// assert_eq!(Some('4'), c); + /// + /// // Decimal 11 is a single digit in base 16 + /// let c = char::from_digit(11, 16); + /// + /// assert_eq!(Some('b'), c); + /// ``` + /// + /// Returning `None` when the input is not a digit: + /// + /// ``` + /// use std::char; + /// + /// let c = char::from_digit(20, 10); + /// + /// assert_eq!(None, c); + /// ``` + /// + /// Passing a large radix, causing a panic: + /// + /// ``` + /// use std::thread; + /// use std::char; + /// + /// let result = thread::spawn(|| { + /// // this panics + /// let c = char::from_digit(1, 37); + /// }).join(); + /// + /// assert!(result.is_err()); + /// ``` + #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[inline] + pub fn from_digit(num: u32, radix: u32) -> Option { + super::convert::from_digit(num, radix) + } + /// Checks if a `char` is a digit in the given radix. /// /// A 'radix' here is sometimes also called a 'base'. A radix of two @@ -575,8 +812,9 @@ impl char { /// assert!(!'A'.is_lowercase()); /// assert!(!'Δ'.is_lowercase()); /// - /// // The various Chinese scripts do not have case, and so: + /// // The various Chinese scripts and punctuation do not have case, and so: /// assert!(!'中'.is_lowercase()); + /// assert!(!' '.is_lowercase()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -606,8 +844,9 @@ impl char { /// assert!('A'.is_uppercase()); /// assert!('Δ'.is_uppercase()); /// - /// // The various Chinese scripts do not have case, and so: + /// // The various Chinese scripts and punctuation do not have case, and so: /// assert!(!'中'.is_uppercase()); + /// assert!(!' '.is_uppercase()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] diff --git a/src/libcore/char/mod.rs b/src/libcore/char/mod.rs index cf5576e549cdf..bf65c31e13597 100644 --- a/src/libcore/char/mod.rs +++ b/src/libcore/char/mod.rs @@ -34,11 +34,7 @@ pub use self::convert::ParseCharError; pub use self::convert::{from_digit, from_u32}; #[stable(feature = "decode_utf16", since = "1.9.0")] pub use self::decode::{decode_utf16, DecodeUtf16, DecodeUtf16Error}; - -// unstable re-exports -#[unstable(feature = "unicode_version", issue = "49726")] -pub use crate::unicode::version::UnicodeVersion; -#[unstable(feature = "unicode_version", issue = "49726")] +#[stable(feature = "unicode_version", since = "1.45.0")] pub use crate::unicode::UNICODE_VERSION; use crate::fmt::{self, Write}; @@ -96,7 +92,7 @@ const MAX_THREE_B: u32 = 0x10000; /// [Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value /// [Code Point]: http://www.unicode.org/glossary/#code_point #[stable(feature = "rust1", since = "1.0.0")] -pub const MAX: char = '\u{10ffff}'; +pub const MAX: char = char::MAX; /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a /// decoding error. @@ -104,7 +100,7 @@ pub const MAX: char = '\u{10ffff}'; /// It can occur, for example, when giving ill-formed UTF-8 bytes to /// [`String::from_utf8_lossy`](../../std/string/struct.String.html#method.from_utf8_lossy). #[stable(feature = "decode_utf16", since = "1.9.0")] -pub const REPLACEMENT_CHARACTER: char = '\u{FFFD}'; +pub const REPLACEMENT_CHARACTER: char = char::REPLACEMENT_CHARACTER; /// Returns an iterator that yields the hexadecimal Unicode escape of a /// character, as `char`s. diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index 9a412e5729427..7784ec687ea9a 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -169,7 +169,8 @@ pub struct AssertParamIsCopy { /// Implementations of `Clone` for primitive types. /// /// Implementations that cannot be described in Rust -/// are implemented in `SelectionContext::copy_clone_conditions()` in librustc. +/// are implemented in `traits::SelectionContext::copy_clone_conditions()` +/// in `rustc_trait_selection`. mod impls { use super::Clone; @@ -219,7 +220,7 @@ mod impls { } } - // Shared references can be cloned, but mutable references *cannot*! + /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] impl Clone for &T { #[inline] @@ -227,4 +228,8 @@ mod impls { *self } } + + /// Shared references can be cloned, but mutable references *cannot*! + #[stable(feature = "rust1", since = "1.0.0")] + impl !Clone for &mut T {} } diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 604be7d5f68d0..9856efc6bd8a4 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -191,6 +191,7 @@ use self::Ordering::*; /// assert_eq!(x.eq(&y), false); /// ``` /// +/// [`Eq`]: Eq /// [`eq`]: PartialEq::eq /// [`ne`]: PartialEq::ne #[lang = "eq"] @@ -817,7 +818,7 @@ pub trait PartialOrd: PartialEq { /// When comparison is impossible: /// /// ``` - /// let result = std::f64::NAN.partial_cmp(&1.0); + /// let result = f64::NAN.partial_cmp(&1.0); /// assert_eq!(result, None); /// ``` #[must_use] @@ -858,7 +859,7 @@ pub trait PartialOrd: PartialEq { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn le(&self, other: &Rhs) -> bool { - matches!(self.partial_cmp(other), Some(Less) | Some(Equal)) + matches!(self.partial_cmp(other), Some(Less | Equal)) } /// This method tests greater than (for `self` and `other`) and is used by the `>` operator. @@ -895,7 +896,7 @@ pub trait PartialOrd: PartialEq { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn ge(&self, other: &Rhs) -> bool { - matches!(self.partial_cmp(other), Some(Greater) | Some(Equal)) + matches!(self.partial_cmp(other), Some(Greater | Equal)) } } diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs index 47ab8715cfa14..eef9ee7cb0093 100644 --- a/src/libcore/convert/mod.rs +++ b/src/libcore/convert/mod.rs @@ -41,6 +41,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; +use crate::hash::{Hash, Hasher}; mod num; @@ -746,3 +747,10 @@ impl From for Infallible { x } } + +#[stable(feature = "convert_infallible_hash", since = "1.44.0")] +impl Hash for Infallible { + fn hash(&self, _: &mut H) { + match *self {} + } +} diff --git a/src/libcore/convert/num.rs b/src/libcore/convert/num.rs index 752199c94b8ae..6dd0522f7f610 100644 --- a/src/libcore/convert/num.rs +++ b/src/libcore/convert/num.rs @@ -13,9 +13,9 @@ mod private { /// Typically doesn’t need to be used directly. #[unstable(feature = "convert_float_to_int", issue = "67057")] pub trait FloatToInt: private::Sealed + Sized { - #[unstable(feature = "float_approx_unchecked_to", issue = "67058")] + #[unstable(feature = "convert_float_to_int", issue = "67057")] #[doc(hidden)] - unsafe fn approx_unchecked(self) -> Int; + unsafe fn to_int_unchecked(self) -> Int; } macro_rules! impl_float_to_int { @@ -27,8 +27,8 @@ macro_rules! impl_float_to_int { impl FloatToInt<$Int> for $Float { #[doc(hidden)] #[inline] - unsafe fn approx_unchecked(self) -> $Int { - crate::intrinsics::float_to_int_approx_unchecked(self) + unsafe fn to_int_unchecked(self) -> $Int { + crate::intrinsics::float_to_int_unchecked(self) } } )+ diff --git a/src/libcore/default.rs b/src/libcore/default.rs index 15ac3aea8b7ba..06402a05d2687 100644 --- a/src/libcore/default.rs +++ b/src/libcore/default.rs @@ -54,7 +54,7 @@ /// /// ## How can I implement `Default`? /// -/// Provides an implementation for the `default()` method that returns the value of +/// Provide an implementation for the `default()` method that returns the value of /// your type that should be the default: /// /// ``` diff --git a/src/libcore/ffi.rs b/src/libcore/ffi.rs index 6277da4f123f2..7bc2866dc2e67 100644 --- a/src/libcore/ffi.rs +++ b/src/libcore/ffi.rs @@ -282,7 +282,7 @@ impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { mod sealed_trait { /// Trait which whitelists the allowed types to be used with [VaList::arg] /// - /// [VaList::va_arg]: struct.VaList.html#method.arg + /// [VaList::arg]: ../struct.VaList.html#method.arg #[unstable( feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \ diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index fe728d42c76f7..95411b525d0db 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -852,7 +852,7 @@ pub trait LowerHex { /// } /// } /// -/// let l = Length(i32::max_value()); +/// let l = Length(i32::MAX); /// /// assert_eq!(format!("l as hex is: {:X}", l), "l as hex is: 7FFFFFFF"); /// diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs index 8dfda7a4a3236..b5a102916a07e 100644 --- a/src/libcore/future/mod.rs +++ b/src/libcore/future/mod.rs @@ -2,7 +2,6 @@ //! Asynchronous values. -#[cfg(not(bootstrap))] use crate::{ ops::{Generator, GeneratorState}, pin::Pin, @@ -11,9 +10,17 @@ use crate::{ }; mod future; +mod pending; +mod ready; + #[stable(feature = "futures_api", since = "1.36.0")] pub use self::future::Future; +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub use pending::{pending, Pending}; +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub use ready::{ready, Ready}; + /// This type is needed because: /// /// a) Generators cannot implement `for<'a, 'b> Generator<&'a mut Context<'b>>`, so we need to pass @@ -24,16 +31,13 @@ pub use self::future::Future; /// It also simplifies the HIR lowering of `.await`. #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] -#[cfg(not(bootstrap))] #[derive(Debug, Copy, Clone)] pub struct ResumeTy(NonNull>); #[unstable(feature = "gen_future", issue = "50547")] -#[cfg(not(bootstrap))] unsafe impl Send for ResumeTy {} #[unstable(feature = "gen_future", issue = "50547")] -#[cfg(not(bootstrap))] unsafe impl Sync for ResumeTy {} /// Wrap a generator in a future. @@ -43,7 +47,6 @@ unsafe impl Sync for ResumeTy {} // This is `const` to avoid extra errors after we recover from `const async fn` #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] -#[cfg(not(bootstrap))] #[inline] pub const fn from_generator(gen: T) -> impl Future where @@ -75,11 +78,7 @@ where #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] -#[cfg(not(bootstrap))] #[inline] -pub unsafe fn poll_with_context(f: Pin<&mut F>, mut cx: ResumeTy) -> Poll -where - F: Future, -{ - F::poll(f, cx.0.as_mut()) +pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> { + &mut *cx.0.as_ptr().cast() } diff --git a/src/libcore/future/pending.rs b/src/libcore/future/pending.rs new file mode 100644 index 0000000000000..74887b68aa0fa --- /dev/null +++ b/src/libcore/future/pending.rs @@ -0,0 +1,57 @@ +use crate::future::Future; +use crate::marker; +use crate::pin::Pin; +use crate::task::{Context, Poll}; + +/// Creates a future which never resolves, representing a computation that never +/// finishes. +/// +/// This `struct` is created by the [`pending`] function. See its +/// documentation for more. +/// +/// [`pending`]: fn.pending.html +#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Pending { + _data: marker::PhantomData, +} + +/// Creates a future which never resolves, representing a computation that never +/// finishes. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(future_readiness_fns)] +/// use core::future; +/// +/// # async fn run() { +/// let future = future::pending(); +/// let () = future.await; +/// unreachable!(); +/// # } +/// ``` +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub fn pending() -> Pending { + Pending { _data: marker::PhantomData } +} + +#[unstable(feature = "future_readiness_fns", issue = "70921")] +impl Future for Pending { + type Output = T; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +#[unstable(feature = "future_readiness_fns", issue = "70921")] +impl Unpin for Pending {} + +#[unstable(feature = "future_readiness_fns", issue = "70921")] +impl Clone for Pending { + fn clone(&self) -> Self { + pending() + } +} diff --git a/src/libcore/future/ready.rs b/src/libcore/future/ready.rs new file mode 100644 index 0000000000000..31b39d7fb6cd5 --- /dev/null +++ b/src/libcore/future/ready.rs @@ -0,0 +1,45 @@ +use crate::future::Future; +use crate::pin::Pin; +use crate::task::{Context, Poll}; + +/// Creates a future that is immediately ready with a value. +/// +/// This `struct` is created by the [`ready`] function. See its +/// documentation for more. +/// +/// [`ready`]: fn.ready.html +#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[derive(Debug, Clone)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Ready(Option); + +#[unstable(feature = "future_readiness_fns", issue = "70921")] +impl Unpin for Ready {} + +#[unstable(feature = "future_readiness_fns", issue = "70921")] +impl Future for Ready { + type Output = T; + + #[inline] + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(self.0.take().expect("Ready polled after completion")) + } +} + +/// Creates a future that is immediately ready with a value. +/// +/// # Examples +/// +/// ``` +/// #![feature(future_readiness_fns)] +/// use core::future; +/// +/// # async fn run() { +/// let a = future::ready(1); +/// assert_eq!(a.await, 1); +/// # } +/// ``` +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub fn ready(t: T) -> Ready { + Ready(Some(t)) +} diff --git a/src/libcore/hash/mod.rs b/src/libcore/hash/mod.rs index 2a7fa58dd30ec..d80101753cbef 100644 --- a/src/libcore/hash/mod.rs +++ b/src/libcore/hash/mod.rs @@ -79,8 +79,6 @@ //! } //! ``` -// ignore-tidy-undocumented-unsafe - #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; @@ -572,6 +570,10 @@ mod impls { fn hash_slice(data: &[$ty], state: &mut H) { let newlen = data.len() * mem::size_of::<$ty>(); let ptr = data.as_ptr() as *const u8; + // SAFETY: `ptr` is valid and aligned, as this macro is only used + // for numeric primitives which have no padding. The new slice only + // spans across `data` and is never mutated, and its total size is the + // same as the original `data` so it can't be over `isize::MAX`. state.write(unsafe { slice::from_raw_parts(ptr, newlen) }) } } @@ -691,6 +693,11 @@ mod impls { state.write_usize(*self as *const () as usize); } else { // Fat pointer + // SAFETY: we are accessing the memory occupied by `self` + // which is guaranteed to be valid. + // This assumes a fat pointer can be represented by a `(usize, usize)`, + // which is safe to do in `std` because it is shipped and kept in sync + // with the implementation of fat pointers in `rustc`. let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; state.write_usize(a); state.write_usize(b); @@ -706,6 +713,11 @@ mod impls { state.write_usize(*self as *const () as usize); } else { // Fat pointer + // SAFETY: we are accessing the memory occupied by `self` + // which is guaranteed to be valid. + // This assumes a fat pointer can be represented by a `(usize, usize)`, + // which is safe to do in `std` because it is shipped and kept in sync + // with the implementation of fat pointers in `rustc`. let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; state.write_usize(a); state.write_usize(b); diff --git a/src/libcore/hash/sip.rs b/src/libcore/hash/sip.rs index adfbe243512bd..ac058609f45ed 100644 --- a/src/libcore/hash/sip.rs +++ b/src/libcore/hash/sip.rs @@ -1,7 +1,5 @@ //! An implementation of SipHash. -// ignore-tidy-undocumented-unsafe - #![allow(deprecated)] // the types in this module are deprecated use crate::cmp; @@ -265,6 +263,7 @@ impl super::Hasher for Hasher { if self.ntail != 0 { needed = 8 - self.ntail; + // SAFETY: `cmp::min(length, needed)` is guaranteed to not be over `length` self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail); if length < needed { self.ntail += length; @@ -279,10 +278,13 @@ impl super::Hasher for Hasher { // Buffered tail is now flushed, process new input. let len = length - needed; - let left = len & 0x7; + let left = len & 0x7; // len % 8 let mut i = needed; while i < len - left { + // SAFETY: because `len - left` is the biggest multiple of 8 under + // `len`, and because `i` starts at `needed` where `len` is `length - needed`, + // `i + 8` is guaranteed to be less than or equal to `length`. let mi = unsafe { load_int_le!(msg, i, u64) }; self.state.v3 ^= mi; @@ -292,6 +294,9 @@ impl super::Hasher for Hasher { i += 8; } + // SAFETY: `i` is now `needed + len.div_euclid(8) * 8`, + // so `i + left` = `needed + len` = `length`, which is by + // definition equal to `msg.len()`. self.tail = unsafe { u8to64_le(msg, i, left) }; self.ntail = left; } diff --git a/src/libcore/hint.rs b/src/libcore/hint.rs index 6cbd26a78de72..0d794de5fe84b 100644 --- a/src/libcore/hint.rs +++ b/src/libcore/hint.rs @@ -2,8 +2,6 @@ //! Hints to compiler that affects how code should be emitted or optimized. -// ignore-tidy-undocumented-unsafe - use crate::intrinsics; /// Informs the compiler that this point in the code is not reachable, enabling @@ -43,7 +41,7 @@ use crate::intrinsics; /// /// assert_eq!(div_1(7, 0), 7); /// assert_eq!(div_1(9, 1), 4); -/// assert_eq!(div_1(11, std::u32::MAX), 0); +/// assert_eq!(div_1(11, u32::MAX), 0); /// ``` #[inline] #[stable(feature = "unreachable", since = "1.27.0")] @@ -68,11 +66,13 @@ pub fn spin_loop() { { #[cfg(target_arch = "x86")] { + // SAFETY: the `cfg` attr ensures that we only execute this on x86 targets. unsafe { crate::arch::x86::_mm_pause() }; } #[cfg(target_arch = "x86_64")] { + // SAFETY: the `cfg` attr ensures that we only execute this on x86_64 targets. unsafe { crate::arch::x86_64::_mm_pause() }; } } @@ -81,10 +81,13 @@ pub fn spin_loop() { { #[cfg(target_arch = "aarch64")] { + // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets. unsafe { crate::arch::aarch64::__yield() }; } #[cfg(target_arch = "arm")] { + // SAFETY: the `cfg` attr ensures that we only execute this on arm targets + // with support for the v6 feature. unsafe { crate::arch::arm::__yield() }; } } @@ -112,8 +115,10 @@ pub fn black_box(dummy: T) -> T { // this. LLVM's interpretation of inline assembly is that it's, well, a black // box. This isn't the greatest implementation since it probably deoptimizes // more than we want, but it's so far good enough. + + // SAFETY: the inline assembly is a no-op. unsafe { - asm!("" : : "r"(&dummy)); + llvm_asm!("" : : "r"(&dummy)); dummy } } diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index eb71497cc4782..9006e4cfaf7bb 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -54,6 +54,8 @@ )] #![allow(missing_docs)] +#[cfg(not(bootstrap))] +use crate::marker::DiscriminantKind; use crate::mem; #[stable(feature = "drop_in_place", since = "1.8.0")] @@ -76,7 +78,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -86,7 +88,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acq(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_acq(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -98,7 +100,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_rel(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_rel(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -110,7 +112,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -120,7 +122,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -132,7 +134,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -144,7 +146,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_failacq(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_failacq(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -156,7 +158,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -168,7 +170,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange`][compare_exchange]. /// /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchg_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// @@ -179,7 +181,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -189,7 +191,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acq(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_acq(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -201,7 +203,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_rel(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_rel(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -213,7 +215,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -223,7 +225,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -235,7 +237,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -247,7 +249,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_failacq(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_failacq(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -259,7 +261,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. /// /// The stabilized version of this intrinsic is available on the @@ -271,7 +273,7 @@ extern "rust-intrinsic" { /// [`AtomicBool::compare_exchange_weak`][cew]. /// /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Loads the current value of the pointer. /// @@ -280,7 +282,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::load`](../../std/sync/atomic/struct.AtomicBool.html#method.load). - pub fn atomic_load(src: *const T) -> T; + pub fn atomic_load(src: *const T) -> T; /// Loads the current value of the pointer. /// /// The stabilized version of this intrinsic is available on the @@ -288,7 +290,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::load`](../../std/sync/atomic/struct.AtomicBool.html#method.load). - pub fn atomic_load_acq(src: *const T) -> T; + pub fn atomic_load_acq(src: *const T) -> T; /// Loads the current value of the pointer. /// /// The stabilized version of this intrinsic is available on the @@ -296,8 +298,8 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::load`](../../std/sync/atomic/struct.AtomicBool.html#method.load). - pub fn atomic_load_relaxed(src: *const T) -> T; - pub fn atomic_load_unordered(src: *const T) -> T; + pub fn atomic_load_relaxed(src: *const T) -> T; + pub fn atomic_load_unordered(src: *const T) -> T; /// Stores the value at the specified memory location. /// @@ -306,7 +308,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::store`](../../std/sync/atomic/struct.AtomicBool.html#method.store). - pub fn atomic_store(dst: *mut T, val: T); + pub fn atomic_store(dst: *mut T, val: T); /// Stores the value at the specified memory location. /// /// The stabilized version of this intrinsic is available on the @@ -314,7 +316,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::store`](../../std/sync/atomic/struct.AtomicBool.html#method.store). - pub fn atomic_store_rel(dst: *mut T, val: T); + pub fn atomic_store_rel(dst: *mut T, val: T); /// Stores the value at the specified memory location. /// /// The stabilized version of this intrinsic is available on the @@ -322,8 +324,8 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::store`](../../std/sync/atomic/struct.AtomicBool.html#method.store). - pub fn atomic_store_relaxed(dst: *mut T, val: T); - pub fn atomic_store_unordered(dst: *mut T, val: T); + pub fn atomic_store_relaxed(dst: *mut T, val: T); + pub fn atomic_store_unordered(dst: *mut T, val: T); /// Stores the value at the specified memory location, returning the old value. /// @@ -332,7 +334,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg(dst: *mut T, src: T) -> T; + pub fn atomic_xchg(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. /// /// The stabilized version of this intrinsic is available on the @@ -340,7 +342,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_acq(dst: *mut T, src: T) -> T; + pub fn atomic_xchg_acq(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. /// /// The stabilized version of this intrinsic is available on the @@ -348,7 +350,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_rel(dst: *mut T, src: T) -> T; + pub fn atomic_xchg_rel(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. /// /// The stabilized version of this intrinsic is available on the @@ -356,7 +358,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_xchg_acqrel(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. /// /// The stabilized version of this intrinsic is available on the @@ -364,7 +366,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_xchg_relaxed(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. /// @@ -373,7 +375,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd(dst: *mut T, src: T) -> T; + pub fn atomic_xadd(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -381,7 +383,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_acq(dst: *mut T, src: T) -> T; + pub fn atomic_xadd_acq(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -389,7 +391,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_rel(dst: *mut T, src: T) -> T; + pub fn atomic_xadd_rel(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -397,7 +399,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -405,7 +407,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_xadd_relaxed(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. /// @@ -414,7 +416,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub(dst: *mut T, src: T) -> T; + pub fn atomic_xsub(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -422,7 +424,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_acq(dst: *mut T, src: T) -> T; + pub fn atomic_xsub_acq(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -430,7 +432,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_rel(dst: *mut T, src: T) -> T; + pub fn atomic_xsub_rel(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -438,7 +440,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_xsub_acqrel(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -446,7 +448,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_xsub_relaxed(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. /// @@ -455,7 +457,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and(dst: *mut T, src: T) -> T; + pub fn atomic_and(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -463,7 +465,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_acq(dst: *mut T, src: T) -> T; + pub fn atomic_and_acq(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -471,7 +473,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_rel(dst: *mut T, src: T) -> T; + pub fn atomic_and_rel(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -479,7 +481,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_and_acqrel(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -487,7 +489,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_and_relaxed(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. /// @@ -496,7 +498,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand(dst: *mut T, src: T) -> T; + pub fn atomic_nand(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -504,7 +506,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_acq(dst: *mut T, src: T) -> T; + pub fn atomic_nand_acq(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -512,7 +514,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_rel(dst: *mut T, src: T) -> T; + pub fn atomic_nand_rel(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -520,7 +522,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_nand_acqrel(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -528,7 +530,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_nand_relaxed(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. /// @@ -537,7 +539,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or(dst: *mut T, src: T) -> T; + pub fn atomic_or(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -545,7 +547,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_acq(dst: *mut T, src: T) -> T; + pub fn atomic_or_acq(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -553,7 +555,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_rel(dst: *mut T, src: T) -> T; + pub fn atomic_or_rel(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -561,7 +563,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_or_acqrel(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -569,7 +571,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_or_relaxed(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. /// @@ -578,7 +580,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor(dst: *mut T, src: T) -> T; + pub fn atomic_xor(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -586,7 +588,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_acq(dst: *mut T, src: T) -> T; + pub fn atomic_xor_acq(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -594,7 +596,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_rel(dst: *mut T, src: T) -> T; + pub fn atomic_xor_rel(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -602,7 +604,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_xor_acqrel(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. /// /// The stabilized version of this intrinsic is available on the @@ -610,7 +612,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) /// as the `order`. For example, /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_xor_relaxed(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. /// @@ -619,7 +621,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) /// as the `order`. For example, /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max(dst: *mut T, src: T) -> T; + pub fn atomic_max(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. /// /// The stabilized version of this intrinsic is available on the @@ -627,7 +629,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) /// as the `order`. For example, /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_acq(dst: *mut T, src: T) -> T; + pub fn atomic_max_acq(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. /// /// The stabilized version of this intrinsic is available on the @@ -635,7 +637,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) /// as the `order`. For example, /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_rel(dst: *mut T, src: T) -> T; + pub fn atomic_max_rel(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. /// /// The stabilized version of this intrinsic is available on the @@ -643,7 +645,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) /// as the `order`. For example, /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_max_acqrel(dst: *mut T, src: T) -> T; /// Maximum with the current value. /// /// The stabilized version of this intrinsic is available on the @@ -651,7 +653,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) /// as the `order`. For example, /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_max_relaxed(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. /// @@ -660,7 +662,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) /// as the `order`. For example, /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min(dst: *mut T, src: T) -> T; + pub fn atomic_min(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. /// /// The stabilized version of this intrinsic is available on the @@ -668,7 +670,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) /// as the `order`. For example, /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_acq(dst: *mut T, src: T) -> T; + pub fn atomic_min_acq(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. /// /// The stabilized version of this intrinsic is available on the @@ -676,7 +678,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) /// as the `order`. For example, /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_rel(dst: *mut T, src: T) -> T; + pub fn atomic_min_rel(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. /// /// The stabilized version of this intrinsic is available on the @@ -684,7 +686,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) /// as the `order`. For example, /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_min_acqrel(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. /// /// The stabilized version of this intrinsic is available on the @@ -692,7 +694,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) /// as the `order`. For example, /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_min_relaxed(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. /// @@ -701,7 +703,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) /// as the `order`. For example, /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin(dst: *mut T, src: T) -> T; + pub fn atomic_umin(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -709,7 +711,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) /// as the `order`. For example, /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_acq(dst: *mut T, src: T) -> T; + pub fn atomic_umin_acq(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -717,7 +719,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) /// as the `order`. For example, /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_rel(dst: *mut T, src: T) -> T; + pub fn atomic_umin_rel(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -725,7 +727,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) /// as the `order`. For example, /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_umin_acqrel(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -733,7 +735,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) /// as the `order`. For example, /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_umin_relaxed(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. /// @@ -742,7 +744,7 @@ extern "rust-intrinsic" { /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) /// as the `order`. For example, /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax(dst: *mut T, src: T) -> T; + pub fn atomic_umax(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -750,7 +752,7 @@ extern "rust-intrinsic" { /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) /// as the `order`. For example, /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_acq(dst: *mut T, src: T) -> T; + pub fn atomic_umax_acq(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -758,7 +760,7 @@ extern "rust-intrinsic" { /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) /// as the `order`. For example, /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_rel(dst: *mut T, src: T) -> T; + pub fn atomic_umax_rel(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -766,7 +768,7 @@ extern "rust-intrinsic" { /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) /// as the `order`. For example, /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_umax_acqrel(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. /// /// The stabilized version of this intrinsic is available on the @@ -774,7 +776,7 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) /// as the `order`. For example, /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_relaxed(dst: *mut T, src: T) -> T; + pub fn atomic_umax_relaxed(dst: *mut T, src: T) -> T; /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction /// if supported; otherwise, it is a no-op. @@ -782,7 +784,9 @@ extern "rust-intrinsic" { /// characteristics. /// /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. pub fn prefetch_read_data(data: *const T, locality: i32); /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction /// if supported; otherwise, it is a no-op. @@ -790,7 +794,9 @@ extern "rust-intrinsic" { /// characteristics. /// /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. pub fn prefetch_write_data(data: *const T, locality: i32); /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction /// if supported; otherwise, it is a no-op. @@ -798,7 +804,9 @@ extern "rust-intrinsic" { /// characteristics. /// /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. pub fn prefetch_read_instruction(data: *const T, locality: i32); /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction /// if supported; otherwise, it is a no-op. @@ -806,12 +814,13 @@ extern "rust-intrinsic" { /// characteristics. /// /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. pub fn prefetch_write_instruction(data: *const T, locality: i32); } extern "rust-intrinsic" { - /// An atomic fence. /// /// The stabilized version of this intrinsic is available in @@ -905,12 +914,14 @@ extern "rust-intrinsic" { /// that `rustc_peek(potentially_uninitialized)` would actually /// double-check that dataflow did indeed compute that it is /// uninitialized at that point in the control flow. + /// + /// This intrinsic should not be used outside of the compiler. pub fn rustc_peek(_: T) -> T; /// Aborts the execution of the process. /// - /// The stabilized version of this intrinsic is - /// [`std::process::abort`](../../std/process/fn.abort.html) + /// A more user-friendly and stable version of this operation is + /// [`std::process::abort`](../../std/process/fn.abort.html). pub fn abort() -> !; /// Tells LLVM that this point in the code is not reachable, enabling @@ -932,21 +943,29 @@ extern "rust-intrinsic" { /// with optimization of surrounding code and reduce performance. It should /// not be used if the invariant can be discovered by the optimizer on its /// own, or if it does not enable any significant optimizations. + /// + /// This intrinsic does not have a stable counterpart. pub fn assume(b: bool); /// Hints to the compiler that branch condition is likely to be true. /// Returns the value passed to it. /// /// Any use other than with `if` statements will probably not have an effect. + /// + /// This intrinsic does not have a stable counterpart. pub fn likely(b: bool) -> bool; /// Hints to the compiler that branch condition is likely to be false. /// Returns the value passed to it. /// /// Any use other than with `if` statements will probably not have an effect. + /// + /// This intrinsic does not have a stable counterpart. pub fn unlikely(b: bool) -> bool; /// Executes a breakpoint trap, for inspection by a debugger. + /// + /// This intrinsic does not have a stable counterpart. pub fn breakpoint(); /// The size of a type in bytes. @@ -973,6 +992,9 @@ extern "rust-intrinsic" { /// [`std::mem::align_of`](../../std/mem/fn.align_of.html). #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] pub fn min_align_of() -> usize; + /// The prefered alignment of a type. + /// + /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_pref_align_of", issue = "none")] pub fn pref_align_of() -> usize; @@ -980,22 +1002,11 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html). - #[cfg(bootstrap)] - pub fn size_of_val(_: &T) -> usize; - /// The minimum alignment of the type of the value that `val` points to. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::min_align_of_val`](../../std/mem/fn.min_align_of_val.html). - #[cfg(bootstrap)] - pub fn min_align_of_val(_: &T) -> usize; - - /// The size of the referenced value in bytes. + pub fn size_of_val(_: *const T) -> usize; + /// The required alignment of the referenced value. /// /// The stabilized version of this intrinsic is - /// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html). - #[cfg(not(bootstrap))] - pub fn size_of_val(_: *const T) -> usize; - #[cfg(not(bootstrap))] + /// [`std::mem::align_of_val`](../../std/mem/fn.align_of_val.html). pub fn min_align_of_val(_: *const T) -> usize; /// Gets a static string slice containing the name of a type. @@ -1016,30 +1027,33 @@ extern "rust-intrinsic" { /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: /// This will statically either panic, or do nothing. - #[cfg(bootstrap)] - pub fn panic_if_uninhabited(); - - /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: - /// This will statically either panic, or do nothing. - #[cfg(not(bootstrap))] + /// + /// This intrinsic does not have a stable counterpart. pub fn assert_inhabited(); /// A guard for unsafe functions that cannot ever be executed if `T` does not permit /// zero-initialization: This will statically either panic, or do nothing. - #[cfg(not(bootstrap))] + /// + /// This intrinsic does not have a stable counterpart. pub fn assert_zero_valid(); /// A guard for unsafe functions that cannot ever be executed if `T` has invalid /// bit patterns: This will statically either panic, or do nothing. - #[cfg(not(bootstrap))] + /// + /// This intrinsic does not have a stable counterpart. pub fn assert_uninit_valid(); /// Gets a reference to a static `Location` indicating where it was called. + /// + /// Consider using [`std::panic::Location::caller`](../../std/panic/struct.Location.html#method.caller) + /// instead. #[rustc_const_unstable(feature = "const_caller_location", issue = "47809")] pub fn caller_location() -> &'static crate::panic::Location<'static>; /// Moves a value out of scope without running drop glue. - /// This exists solely for `mem::forget_unsized`; normal `forget` uses `ManuallyDrop` instead. + /// + /// This exists solely for [`mem::forget_unsized`](../../std/mem/fn.forget_unsized.html); + /// normal `forget` uses `ManuallyDrop` instead. pub fn forget(_: T); /// Reinterprets the bits of a value of one type as another type. @@ -1100,6 +1114,24 @@ extern "rust-intrinsic" { /// Below are common applications of `transmute` which can be replaced with safer /// constructs. /// + /// Turning raw bytes(`&[u8]`) to `u32`, `f64`, etc.: + /// + /// ``` + /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; + /// + /// let num = unsafe { + /// std::mem::transmute::<[u8; 4], u32>(raw_bytes); + /// }; + /// + /// // use `u32::from_ne_bytes` instead + /// let num = u32::from_ne_bytes(raw_bytes); + /// // or use `u32::from_le_bytes` or `u32::from_ge_bytes` to specify the endianness + /// let num = u32::from_le_bytes(raw_bytes); + /// assert_eq!(num, 0x12345678); + /// let num = u32::from_be_bytes(raw_bytes); + /// assert_eq!(num, 0x78563412); + /// ``` + /// /// Turning a pointer into a `usize`: /// /// ``` @@ -1281,6 +1313,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::pointer::offset`](../../std/primitive.pointer.html#method.offset). + #[must_use = "returns a new pointer rather than modifying its argument"] pub fn offset(dst: *const T, offset: isize) -> *const T; /// Calculates the offset from a pointer, potentially wrapping. @@ -1297,6 +1330,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::pointer::wrapping_offset`](../../std/primitive.pointer.html#method.wrapping_offset). + #[must_use = "returns a new pointer rather than modifying its argument"] pub fn arith_offset(dst: *const T, offset: isize) -> *const T; /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with @@ -1305,6 +1339,8 @@ extern "rust-intrinsic" { /// /// The volatile parameter is set to `true`, so it will not be optimized out /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. pub fn volatile_copy_nonoverlapping_memory(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with /// a size of `count` * `size_of::()` and an alignment of @@ -1312,6 +1348,8 @@ extern "rust-intrinsic" { /// /// The volatile parameter is set to `true`, so it will not be optimized out /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a /// size of `count` * `size_of::()` and an alignment of @@ -1319,6 +1357,8 @@ extern "rust-intrinsic" { /// /// The volatile parameter is set to `true`, so it will not be optimized out /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. pub fn volatile_set_memory(dst: *mut T, val: u8, count: usize); /// Performs a volatile load from the `src` pointer. @@ -1334,9 +1374,13 @@ extern "rust-intrinsic" { /// Performs a volatile load from the `src` pointer /// The pointer is not required to be aligned. + /// + /// This intrinsic does not have a stable counterpart. pub fn unaligned_volatile_load(src: *const T) -> T; /// Performs a volatile store to the `dst` pointer. /// The pointer is not required to be aligned. + /// + /// This intrinsic does not have a stable counterpart. pub fn unaligned_volatile_store(dst: *mut T, val: T); /// Returns the square root of an `f32` @@ -1544,8 +1588,12 @@ extern "rust-intrinsic" { pub fn rintf64(x: f64) -> f64; /// Returns the nearest integer to an `f32`. + /// + /// This intrinsic does not have a stable counterpart. pub fn nearbyintf32(x: f32) -> f32; /// Returns the nearest integer to an `f64`. + /// + /// This intrinsic does not have a stable counterpart. pub fn nearbyintf64(x: f64) -> f64; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. @@ -1561,28 +1609,40 @@ extern "rust-intrinsic" { /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. - pub fn fadd_fast(a: T, b: T) -> T; + /// + /// This intrinsic does not have a stable counterpart. + pub fn fadd_fast(a: T, b: T) -> T; /// Float subtraction that allows optimizations based on algebraic rules. /// May assume inputs are finite. - pub fn fsub_fast(a: T, b: T) -> T; + /// + /// This intrinsic does not have a stable counterpart. + pub fn fsub_fast(a: T, b: T) -> T; /// Float multiplication that allows optimizations based on algebraic rules. /// May assume inputs are finite. - pub fn fmul_fast(a: T, b: T) -> T; + /// + /// This intrinsic does not have a stable counterpart. + pub fn fmul_fast(a: T, b: T) -> T; /// Float division that allows optimizations based on algebraic rules. /// May assume inputs are finite. - pub fn fdiv_fast(a: T, b: T) -> T; + /// + /// This intrinsic does not have a stable counterpart. + pub fn fdiv_fast(a: T, b: T) -> T; /// Float remainder that allows optimizations based on algebraic rules. /// May assume inputs are finite. - pub fn frem_fast(a: T, b: T) -> T; + /// + /// This intrinsic does not have a stable counterpart. + pub fn frem_fast(a: T, b: T) -> T; /// Convert with LLVM’s fptoui/fptosi, which may return undef for values out of range /// () - /// This is under stabilization at - pub fn float_to_int_approx_unchecked(value: Float) -> Int; + /// + /// Stabilized as [`f32::to_int_unchecked`](../../std/primitive.f32.html#method.to_int_unchecked) + /// and [`f64::to_int_unchecked`](../../std/primitive.f64.html#method.to_int_unchecked). + pub fn float_to_int_unchecked(value: Float) -> Int; /// Returns the number of bits set in an integer type `T` /// @@ -1590,7 +1650,7 @@ extern "rust-intrinsic" { /// primitives via the `count_ones` method. For example, /// [`std::u32::count_ones`](../../std/primitive.u32.html#method.count_ones) #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")] - pub fn ctpop(x: T) -> T; + pub fn ctpop(x: T) -> T; /// Returns the number of leading unset bits (zeroes) in an integer type `T`. /// @@ -1622,11 +1682,13 @@ extern "rust-intrinsic" { /// assert_eq!(num_leading, 16); /// ``` #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")] - pub fn ctlz(x: T) -> T; + pub fn ctlz(x: T) -> T; /// Like `ctlz`, but extra-unsafe as it returns `undef` when /// given an `x` with value `0`. /// + /// This intrinsic does not have a stable counterpart. + /// /// # Examples /// /// ``` @@ -1639,7 +1701,7 @@ extern "rust-intrinsic" { /// assert_eq!(num_leading, 3); /// ``` #[rustc_const_unstable(feature = "constctlz", issue = "none")] - pub fn ctlz_nonzero(x: T) -> T; + pub fn ctlz_nonzero(x: T) -> T; /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. /// @@ -1671,11 +1733,13 @@ extern "rust-intrinsic" { /// assert_eq!(num_trailing, 16); /// ``` #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")] - pub fn cttz(x: T) -> T; + pub fn cttz(x: T) -> T; /// Like `cttz`, but extra-unsafe as it returns `undef` when /// given an `x` with value `0`. /// + /// This intrinsic does not have a stable counterpart. + /// /// # Examples /// /// ``` @@ -1688,7 +1752,7 @@ extern "rust-intrinsic" { /// assert_eq!(num_trailing, 3); /// ``` #[rustc_const_unstable(feature = "const_cttz", issue = "none")] - pub fn cttz_nonzero(x: T) -> T; + pub fn cttz_nonzero(x: T) -> T; /// Reverses the bytes in an integer type `T`. /// @@ -1696,7 +1760,7 @@ extern "rust-intrinsic" { /// primitives via the `swap_bytes` method. For example, /// [`std::u32::swap_bytes`](../../std/primitive.u32.html#method.swap_bytes) #[rustc_const_stable(feature = "const_bswap", since = "1.40.0")] - pub fn bswap(x: T) -> T; + pub fn bswap(x: T) -> T; /// Reverses the bits in an integer type `T`. /// @@ -1704,7 +1768,7 @@ extern "rust-intrinsic" { /// primitives via the `reverse_bits` method. For example, /// [`std::u32::reverse_bits`](../../std/primitive.u32.html#method.reverse_bits) #[rustc_const_stable(feature = "const_bitreverse", since = "1.40.0")] - pub fn bitreverse(x: T) -> T; + pub fn bitreverse(x: T) -> T; /// Performs checked integer addition. /// @@ -1712,7 +1776,7 @@ extern "rust-intrinsic" { /// primitives via the `overflowing_add` method. For example, /// [`std::u32::overflowing_add`](../../std/primitive.u32.html#method.overflowing_add) #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - pub fn add_with_overflow(x: T, y: T) -> (T, bool); + pub fn add_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer subtraction /// @@ -1720,7 +1784,7 @@ extern "rust-intrinsic" { /// primitives via the `overflowing_sub` method. For example, /// [`std::u32::overflowing_sub`](../../std/primitive.u32.html#method.overflowing_sub) #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - pub fn sub_with_overflow(x: T, y: T) -> (T, bool); + pub fn sub_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer multiplication /// @@ -1728,60 +1792,68 @@ extern "rust-intrinsic" { /// primitives via the `overflowing_mul` method. For example, /// [`std::u32::overflowing_mul`](../../std/primitive.u32.html#method.overflowing_mul) #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - pub fn mul_with_overflow(x: T, y: T) -> (T, bool); + pub fn mul_with_overflow(x: T, y: T) -> (T, bool); /// Performs an exact division, resulting in undefined behavior where - /// `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1` - pub fn exact_div(x: T, y: T) -> T; + /// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1` + /// + /// This intrinsic does not have a stable counterpart. + pub fn exact_div(x: T, y: T) -> T; /// Performs an unchecked division, resulting in undefined behavior - /// where y = 0 or x = `T::min_value()` and y = -1 + /// where y = 0 or x = `T::MIN` and y = -1 /// - /// The stabilized versions of this intrinsic are available on the integer + /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_div` method. For example, /// [`std::u32::checked_div`](../../std/primitive.u32.html#method.checked_div) #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_div(x: T, y: T) -> T; + pub fn unchecked_div(x: T, y: T) -> T; /// Returns the remainder of an unchecked division, resulting in - /// undefined behavior where y = 0 or x = `T::min_value()` and y = -1 + /// undefined behavior where y = 0 or x = `T::MIN` and y = -1 /// - /// The stabilized versions of this intrinsic are available on the integer + /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_rem` method. For example, /// [`std::u32::checked_rem`](../../std/primitive.u32.html#method.checked_rem) #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_rem(x: T, y: T) -> T; + pub fn unchecked_rem(x: T, y: T) -> T; /// Performs an unchecked left shift, resulting in undefined behavior when /// y < 0 or y >= N, where N is the width of T in bits. /// - /// The stabilized versions of this intrinsic are available on the integer + /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shl` method. For example, /// [`std::u32::checked_shl`](../../std/primitive.u32.html#method.checked_shl) #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] - pub fn unchecked_shl(x: T, y: T) -> T; + pub fn unchecked_shl(x: T, y: T) -> T; /// Performs an unchecked right shift, resulting in undefined behavior when /// y < 0 or y >= N, where N is the width of T in bits. /// - /// The stabilized versions of this intrinsic are available on the integer + /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shr` method. For example, /// [`std::u32::checked_shr`](../../std/primitive.u32.html#method.checked_shr) #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] - pub fn unchecked_shr(x: T, y: T) -> T; + pub fn unchecked_shr(x: T, y: T) -> T; /// Returns the result of an unchecked addition, resulting in - /// undefined behavior when `x + y > T::max_value()` or `x + y < T::min_value()`. + /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`. + /// + /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_add(x: T, y: T) -> T; + pub fn unchecked_add(x: T, y: T) -> T; /// Returns the result of an unchecked subtraction, resulting in - /// undefined behavior when `x - y > T::max_value()` or `x - y < T::min_value()`. + /// undefined behavior when `x - y > T::MAX` or `x - y < T::MIN`. + /// + /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_sub(x: T, y: T) -> T; + pub fn unchecked_sub(x: T, y: T) -> T; /// Returns the result of an unchecked multiplication, resulting in - /// undefined behavior when `x * y > T::max_value()` or `x * y < T::min_value()`. + /// undefined behavior when `x * y > T::MAX` or `x * y < T::MIN`. + /// + /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_mul(x: T, y: T) -> T; + pub fn unchecked_mul(x: T, y: T) -> T; /// Performs rotate left. /// @@ -1789,7 +1861,7 @@ extern "rust-intrinsic" { /// primitives via the `rotate_left` method. For example, /// [`std::u32::rotate_left`](../../std/primitive.u32.html#method.rotate_left) #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] - pub fn rotate_left(x: T, y: T) -> T; + pub fn rotate_left(x: T, y: T) -> T; /// Performs rotate right. /// @@ -1797,7 +1869,7 @@ extern "rust-intrinsic" { /// primitives via the `rotate_right` method. For example, /// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right) #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] - pub fn rotate_right(x: T, y: T) -> T; + pub fn rotate_right(x: T, y: T) -> T; /// Returns (a + b) mod 2N, where N is the width of T in bits. /// @@ -1805,21 +1877,21 @@ extern "rust-intrinsic" { /// primitives via the `checked_add` method. For example, /// [`std::u32::checked_add`](../../std/primitive.u32.html#method.checked_add) #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - pub fn wrapping_add(a: T, b: T) -> T; + pub fn wrapping_add(a: T, b: T) -> T; /// Returns (a - b) mod 2N, where N is the width of T in bits. /// /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `checked_sub` method. For example, /// [`std::u32::checked_sub`](../../std/primitive.u32.html#method.checked_sub) #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - pub fn wrapping_sub(a: T, b: T) -> T; + pub fn wrapping_sub(a: T, b: T) -> T; /// Returns (a * b) mod 2N, where N is the width of T in bits. /// /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `checked_mul` method. For example, /// [`std::u32::checked_mul`](../../std/primitive.u32.html#method.checked_mul) #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - pub fn wrapping_mul(a: T, b: T) -> T; + pub fn wrapping_mul(a: T, b: T) -> T; /// Computes `a + b`, while saturating at numeric bounds. /// @@ -1827,14 +1899,14 @@ extern "rust-intrinsic" { /// primitives via the `saturating_add` method. For example, /// [`std::u32::saturating_add`](../../std/primitive.u32.html#method.saturating_add) #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] - pub fn saturating_add(a: T, b: T) -> T; + pub fn saturating_add(a: T, b: T) -> T; /// Computes `a - b`, while saturating at numeric bounds. /// /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_sub` method. For example, /// [`std::u32::saturating_sub`](../../std/primitive.u32.html#method.saturating_sub) #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] - pub fn saturating_sub(a: T, b: T) -> T; + pub fn saturating_sub(a: T, b: T) -> T; /// Returns the value of the discriminant for the variant in 'v', /// cast to a `u64`; if `T` has no discriminant, returns 0. @@ -1842,6 +1914,10 @@ extern "rust-intrinsic" { /// The stabilized version of this intrinsic is /// [`std::mem::discriminant`](../../std/mem/fn.discriminant.html) #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] + #[cfg(not(bootstrap))] + pub fn discriminant_value(v: &T) -> ::Discriminant; + #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] + #[cfg(bootstrap)] pub fn discriminant_value(v: &T) -> u64; /// Rust's "try catch" construct which invokes the function pointer `try_fn` @@ -1851,10 +1927,7 @@ extern "rust-intrinsic" { /// takes the data pointer and a pointer to the target-specific exception /// object that was caught. For more information see the compiler's /// source as well as std's catch implementation. - #[cfg(not(bootstrap))] pub fn r#try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32; - #[cfg(bootstrap)] - pub fn r#try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32; /// Emits a `!nontemporal` store according to LLVM (see their docs). /// Probably will never become stable. diff --git a/src/libcore/iter/adapters/chain.rs b/src/libcore/iter/adapters/chain.rs index 3611a1aadaddb..6700ef017bde4 100644 --- a/src/libcore/iter/adapters/chain.rs +++ b/src/libcore/iter/adapters/chain.rs @@ -1,8 +1,7 @@ +use crate::iter::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen}; use crate::ops::Try; use crate::usize; -use super::super::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen}; - /// An iterator that links two iterators together, in a chain. /// /// This `struct` is created by the [`chain`] method on [`Iterator`]. See its @@ -14,37 +13,48 @@ use super::super::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen}; #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] pub struct Chain { - a: A, - b: B, - state: ChainState, + // These are "fused" with `Option` so we don't need separate state to track which part is + // already exhausted, and we may also get niche layout for `None`. We don't use the real `Fuse` + // adapter because its specialization for `FusedIterator` unconditionally descends into the + // iterator, and that could be expensive to keep revisiting stuff like nested chains. It also + // hurts compiler performance to add more iterator layers to `Chain`. + // + // Only the "first" iterator is actually set `None` when exhausted, depending on whether you + // iterate forward or backward. If you mix directions, then both sides may be `None`. + a: Option, + b: Option, } impl Chain { pub(in super::super) fn new(a: A, b: B) -> Chain { - Chain { a, b, state: ChainState::Both } + Chain { a: Some(a), b: Some(b) } } } -// The iterator protocol specifies that iteration ends with the return value -// `None` from `.next()` (or `.next_back()`) and it is unspecified what -// further calls return. The chain adaptor must account for this since it uses -// two subiterators. -// -// It uses three states: -// -// - Both: `a` and `b` are remaining -// - Front: `a` remaining -// - Back: `b` remaining -// -// The fourth state (neither iterator is remaining) only occurs after Chain has -// returned None once, so we don't need to store this state. -#[derive(Clone, Debug)] -enum ChainState { - // both front and back iterator are remaining - Both, - // only front is remaining - Front, - // only back is remaining - Back, +/// Fuse the iterator if the expression is `None`. +macro_rules! fuse { + ($self:ident . $iter:ident . $($call:tt)+) => { + match $self.$iter { + Some(ref mut iter) => match iter.$($call)+ { + None => { + $self.$iter = None; + None + } + item => item, + }, + None => None, + } + }; +} + +/// Try an iterator method without fusing, +/// like an inline `.as_mut().and_then(...)` +macro_rules! maybe { + ($self:ident . $iter:ident . $($call:tt)+) => { + match $self.$iter { + Some(ref mut iter) => iter.$($call)+, + None => None, + } + }; } #[stable(feature = "rust1", since = "1.0.0")] @@ -57,88 +67,68 @@ where #[inline] fn next(&mut self) -> Option { - match self.state { - ChainState::Both => match self.a.next() { - elt @ Some(..) => elt, - None => { - self.state = ChainState::Back; - self.b.next() - } - }, - ChainState::Front => self.a.next(), - ChainState::Back => self.b.next(), + match fuse!(self.a.next()) { + None => maybe!(self.b.next()), + item => item, } } #[inline] #[rustc_inherit_overflow_checks] fn count(self) -> usize { - match self.state { - ChainState::Both => self.a.count() + self.b.count(), - ChainState::Front => self.a.count(), - ChainState::Back => self.b.count(), - } + let a_count = match self.a { + Some(a) => a.count(), + None => 0, + }; + let b_count = match self.b { + Some(b) => b.count(), + None => 0, + }; + a_count + b_count } - fn try_fold(&mut self, init: Acc, mut f: F) -> R + fn try_fold(&mut self, mut acc: Acc, mut f: F) -> R where Self: Sized, F: FnMut(Acc, Self::Item) -> R, R: Try, { - let mut accum = init; - match self.state { - ChainState::Both | ChainState::Front => { - accum = self.a.try_fold(accum, &mut f)?; - if let ChainState::Both = self.state { - self.state = ChainState::Back; - } - } - _ => {} + if let Some(ref mut a) = self.a { + acc = a.try_fold(acc, &mut f)?; + self.a = None; } - if let ChainState::Back = self.state { - accum = self.b.try_fold(accum, &mut f)?; + if let Some(ref mut b) = self.b { + acc = b.try_fold(acc, f)?; + // we don't fuse the second iterator } - Try::from_ok(accum) + Try::from_ok(acc) } - fn fold(self, init: Acc, mut f: F) -> Acc + fn fold(self, mut acc: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { - let mut accum = init; - match self.state { - ChainState::Both | ChainState::Front => { - accum = self.a.fold(accum, &mut f); - } - _ => {} + if let Some(a) = self.a { + acc = a.fold(acc, &mut f); } - match self.state { - ChainState::Both | ChainState::Back => { - accum = self.b.fold(accum, &mut f); - } - _ => {} + if let Some(b) = self.b { + acc = b.fold(acc, f); } - accum + acc } #[inline] fn nth(&mut self, mut n: usize) -> Option { - match self.state { - ChainState::Both | ChainState::Front => { - for x in self.a.by_ref() { - if n == 0 { - return Some(x); - } - n -= 1; - } - if let ChainState::Both = self.state { - self.state = ChainState::Back; + if let Some(ref mut a) = self.a { + while let Some(x) = a.next() { + if n == 0 { + return Some(x); } + n -= 1; } - ChainState::Back => {} + self.a = None; } - if let ChainState::Back = self.state { self.b.nth(n) } else { None } + maybe!(self.b.nth(n)) } #[inline] @@ -146,39 +136,32 @@ where where P: FnMut(&Self::Item) -> bool, { - match self.state { - ChainState::Both => match self.a.find(&mut predicate) { - None => { - self.state = ChainState::Back; - self.b.find(predicate) - } - v => v, - }, - ChainState::Front => self.a.find(predicate), - ChainState::Back => self.b.find(predicate), + match fuse!(self.a.find(&mut predicate)) { + None => maybe!(self.b.find(predicate)), + item => item, } } #[inline] fn last(self) -> Option { - match self.state { - ChainState::Both => { - // Must exhaust a before b. - let a_last = self.a.last(); - let b_last = self.b.last(); - b_last.or(a_last) - } - ChainState::Front => self.a.last(), - ChainState::Back => self.b.last(), - } + // Must exhaust a before b. + let a_last = match self.a { + Some(a) => a.last(), + None => None, + }; + let b_last = match self.b { + Some(b) => b.last(), + None => None, + }; + b_last.or(a_last) } #[inline] fn size_hint(&self) -> (usize, Option) { - match self.state { - ChainState::Both => { - let (a_lower, a_upper) = self.a.size_hint(); - let (b_lower, b_upper) = self.b.size_hint(); + match self { + Chain { a: Some(a), b: Some(b) } => { + let (a_lower, a_upper) = a.size_hint(); + let (b_lower, b_upper) = b.size_hint(); let lower = a_lower.saturating_add(b_lower); @@ -189,8 +172,9 @@ where (lower, upper) } - ChainState::Front => self.a.size_hint(), - ChainState::Back => self.b.size_hint(), + Chain { a: Some(a), b: None } => a.size_hint(), + Chain { a: None, b: Some(b) } => b.size_hint(), + Chain { a: None, b: None } => (0, Some(0)), } } } @@ -203,78 +187,65 @@ where { #[inline] fn next_back(&mut self) -> Option { - match self.state { - ChainState::Both => match self.b.next_back() { - elt @ Some(..) => elt, - None => { - self.state = ChainState::Front; - self.a.next_back() - } - }, - ChainState::Front => self.a.next_back(), - ChainState::Back => self.b.next_back(), + match fuse!(self.b.next_back()) { + None => maybe!(self.a.next_back()), + item => item, } } #[inline] fn nth_back(&mut self, mut n: usize) -> Option { - match self.state { - ChainState::Both | ChainState::Back => { - for x in self.b.by_ref().rev() { - if n == 0 { - return Some(x); - } - n -= 1; - } - if let ChainState::Both = self.state { - self.state = ChainState::Front; + if let Some(ref mut b) = self.b { + while let Some(x) = b.next_back() { + if n == 0 { + return Some(x); } + n -= 1; } - ChainState::Front => {} + self.b = None; } - if let ChainState::Front = self.state { self.a.nth_back(n) } else { None } + maybe!(self.a.nth_back(n)) } - fn try_rfold(&mut self, init: Acc, mut f: F) -> R + #[inline] + fn rfind

(&mut self, mut predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + match fuse!(self.b.rfind(&mut predicate)) { + None => maybe!(self.a.rfind(predicate)), + item => item, + } + } + + fn try_rfold(&mut self, mut acc: Acc, mut f: F) -> R where Self: Sized, F: FnMut(Acc, Self::Item) -> R, R: Try, { - let mut accum = init; - match self.state { - ChainState::Both | ChainState::Back => { - accum = self.b.try_rfold(accum, &mut f)?; - if let ChainState::Both = self.state { - self.state = ChainState::Front; - } - } - _ => {} + if let Some(ref mut b) = self.b { + acc = b.try_rfold(acc, &mut f)?; + self.b = None; } - if let ChainState::Front = self.state { - accum = self.a.try_rfold(accum, &mut f)?; + if let Some(ref mut a) = self.a { + acc = a.try_rfold(acc, f)?; + // we don't fuse the second iterator } - Try::from_ok(accum) + Try::from_ok(acc) } - fn rfold(self, init: Acc, mut f: F) -> Acc + fn rfold(self, mut acc: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { - let mut accum = init; - match self.state { - ChainState::Both | ChainState::Back => { - accum = self.b.rfold(accum, &mut f); - } - _ => {} + if let Some(b) = self.b { + acc = b.rfold(acc, &mut f); } - match self.state { - ChainState::Both | ChainState::Front => { - accum = self.a.rfold(accum, &mut f); - } - _ => {} + if let Some(a) = self.a { + acc = a.rfold(acc, f); } - accum + acc } } diff --git a/src/libcore/iter/adapters/fuse.rs b/src/libcore/iter/adapters/fuse.rs new file mode 100644 index 0000000000000..502fc2e631502 --- /dev/null +++ b/src/libcore/iter/adapters/fuse.rs @@ -0,0 +1,512 @@ +use crate::intrinsics; +use crate::iter::{ + DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedRandomAccess, +}; +use crate::ops::Try; + +/// An iterator that yields `None` forever after the underlying iterator +/// yields `None` once. +/// +/// This `struct` is created by the [`fuse`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`fuse`]: trait.Iterator.html#method.fuse +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Fuse { + // NOTE: for `I: FusedIterator`, this is always assumed `Some`! + iter: Option, +} +impl Fuse { + pub(in crate::iter) fn new(iter: I) -> Fuse { + Fuse { iter: Some(iter) } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Fuse where I: Iterator {} + +/// Fuse the iterator if the expression is `None`. +macro_rules! fuse { + ($self:ident . iter . $($call:tt)+) => { + match $self.iter { + Some(ref mut iter) => match iter.$($call)+ { + None => { + $self.iter = None; + None + } + item => item, + }, + None => None, + } + }; +} + +// NOTE: for `I: FusedIterator`, we assume that the iterator is always `Some`. +// Implementing this as a directly-expanded macro helps codegen performance. +macro_rules! unchecked { + ($self:ident) => { + match $self { + Fuse { iter: Some(iter) } => iter, + // SAFETY: the specialized iterator never sets `None` + Fuse { iter: None } => unsafe { intrinsics::unreachable() }, + } + }; +} + +// Any implementation here is made internal to avoid exposing default fns outside this trait +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Fuse +where + I: Iterator, +{ + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option { + FuseImpl::next(self) + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + FuseImpl::nth(self, n) + } + + #[inline] + fn last(self) -> Option { + FuseImpl::last(self) + } + + #[inline] + fn count(self) -> usize { + FuseImpl::count(self) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + FuseImpl::size_hint(self) + } + + #[inline] + fn try_fold(&mut self, acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + FuseImpl::try_fold(self, acc, fold) + } + + #[inline] + fn fold(self, acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + FuseImpl::fold(self, acc, fold) + } + + #[inline] + fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + FuseImpl::find(self, predicate) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Fuse +where + I: DoubleEndedIterator, +{ + #[inline] + fn next_back(&mut self) -> Option<::Item> { + FuseImpl::next_back(self) + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option<::Item> { + FuseImpl::nth_back(self, n) + } + + #[inline] + fn try_rfold(&mut self, acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + FuseImpl::try_rfold(self, acc, fold) + } + + #[inline] + fn rfold(self, acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + FuseImpl::rfold(self, acc, fold) + } + + #[inline] + fn rfind

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + FuseImpl::rfind(self, predicate) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Fuse +where + I: ExactSizeIterator, +{ + fn len(&self) -> usize { + FuseImpl::len(self) + } + + fn is_empty(&self) -> bool { + FuseImpl::is_empty(self) + } +} + +unsafe impl TrustedRandomAccess for Fuse +where + I: TrustedRandomAccess, +{ + unsafe fn get_unchecked(&mut self, i: usize) -> I::Item { + match self.iter { + Some(ref mut iter) => iter.get_unchecked(i), + // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted. + None => intrinsics::unreachable(), + } + } + + fn may_have_side_effect() -> bool { + I::may_have_side_effect() + } +} + +// Fuse specialization trait +#[doc(hidden)] +trait FuseImpl { + type Item; + + // Functions specific to any normal Iterators + fn next(&mut self) -> Option; + fn nth(&mut self, n: usize) -> Option; + fn last(self) -> Option; + fn count(self) -> usize; + fn size_hint(&self) -> (usize, Option); + fn try_fold(&mut self, acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try; + fn fold(self, acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc; + fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool; + + // Functions specific to DoubleEndedIterators + fn next_back(&mut self) -> Option + where + I: DoubleEndedIterator; + fn nth_back(&mut self, n: usize) -> Option + where + I: DoubleEndedIterator; + fn try_rfold(&mut self, acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + I: DoubleEndedIterator; + fn rfold(self, acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + I: DoubleEndedIterator; + fn rfind

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + I: DoubleEndedIterator; + + // Functions specific to ExactSizeIterator + fn len(&self) -> usize + where + I: ExactSizeIterator; + fn is_empty(&self) -> bool + where + I: ExactSizeIterator; +} + +// General Fuse impl +#[doc(hidden)] +impl FuseImpl for Fuse +where + I: Iterator, +{ + type Item = ::Item; + + #[inline] + default fn next(&mut self) -> Option<::Item> { + fuse!(self.iter.next()) + } + + #[inline] + default fn nth(&mut self, n: usize) -> Option { + fuse!(self.iter.nth(n)) + } + + #[inline] + default fn last(self) -> Option { + match self.iter { + Some(iter) => iter.last(), + None => None, + } + } + + #[inline] + default fn count(self) -> usize { + match self.iter { + Some(iter) => iter.count(), + None => 0, + } + } + + #[inline] + default fn size_hint(&self) -> (usize, Option) { + match self.iter { + Some(ref iter) => iter.size_hint(), + None => (0, Some(0)), + } + } + + #[inline] + default fn try_fold(&mut self, mut acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + if let Some(ref mut iter) = self.iter { + acc = iter.try_fold(acc, fold)?; + self.iter = None; + } + Try::from_ok(acc) + } + + #[inline] + default fn fold(self, mut acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if let Some(iter) = self.iter { + acc = iter.fold(acc, fold); + } + acc + } + + #[inline] + default fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + fuse!(self.iter.find(predicate)) + } + + #[inline] + default fn next_back(&mut self) -> Option<::Item> + where + I: DoubleEndedIterator, + { + fuse!(self.iter.next_back()) + } + + #[inline] + default fn nth_back(&mut self, n: usize) -> Option<::Item> + where + I: DoubleEndedIterator, + { + fuse!(self.iter.nth_back(n)) + } + + #[inline] + default fn try_rfold(&mut self, mut acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + I: DoubleEndedIterator, + { + if let Some(ref mut iter) = self.iter { + acc = iter.try_rfold(acc, fold)?; + self.iter = None; + } + Try::from_ok(acc) + } + + #[inline] + default fn rfold(self, mut acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + I: DoubleEndedIterator, + { + if let Some(iter) = self.iter { + acc = iter.rfold(acc, fold); + } + acc + } + + #[inline] + default fn rfind

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + I: DoubleEndedIterator, + { + fuse!(self.iter.rfind(predicate)) + } + + #[inline] + default fn len(&self) -> usize + where + I: ExactSizeIterator, + { + match self.iter { + Some(ref iter) => iter.len(), + None => 0, + } + } + + #[inline] + default fn is_empty(&self) -> bool + where + I: ExactSizeIterator, + { + match self.iter { + Some(ref iter) => iter.is_empty(), + None => true, + } + } +} + +#[doc(hidden)] +impl FuseImpl for Fuse +where + I: FusedIterator, +{ + #[inline] + fn next(&mut self) -> Option<::Item> { + unchecked!(self).next() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + unchecked!(self).nth(n) + } + + #[inline] + fn last(self) -> Option { + unchecked!(self).last() + } + + #[inline] + fn count(self) -> usize { + unchecked!(self).count() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + unchecked!(self).size_hint() + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + unchecked!(self).try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + unchecked!(self).fold(init, fold) + } + + #[inline] + fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + unchecked!(self).find(predicate) + } + + #[inline] + fn next_back(&mut self) -> Option<::Item> + where + I: DoubleEndedIterator, + { + unchecked!(self).next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option<::Item> + where + I: DoubleEndedIterator, + { + unchecked!(self).nth_back(n) + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + I: DoubleEndedIterator, + { + unchecked!(self).try_rfold(init, fold) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + I: DoubleEndedIterator, + { + unchecked!(self).rfold(init, fold) + } + + #[inline] + fn rfind

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

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

(&mut self, mut predicate: P) -> Option where P: FnMut(Self::Item) -> bool, Self: Sized + ExactSizeIterator + DoubleEndedIterator { - // No need for an overflow check here, because `ExactSizeIterator` let n = len!(self); - self.try_rfold(n, move |i, x| { - let i = i - 1; - if predicate(x) { Err(i) } - else { Ok(i) } - }).err() - .map(|i| { + let mut i = n; + while let Some(x) = self.next_back() { + i -= 1; + if predicate(x) { unsafe { assume(i < n) }; - i - }) + return Some(i); + } + } + None } $($extra)* @@ -3734,7 +3852,7 @@ impl FusedIterator for Split<'_, T, P> where P: FnMut(&T) -> bool {} /// /// [`split_inclusive`]: ../../std/primitive.slice.html#method.split_inclusive /// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] pub struct SplitInclusive<'a, T: 'a, P> where P: FnMut(&T) -> bool, @@ -3744,7 +3862,7 @@ where finished: bool, } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl fmt::Debug for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool, @@ -3758,7 +3876,7 @@ where } // FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl Clone for SplitInclusive<'_, T, P> where P: Clone + FnMut(&T) -> bool, @@ -3768,7 +3886,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, T, P> Iterator for SplitInclusive<'a, T, P> where P: FnMut(&T) -> bool, @@ -3797,7 +3915,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, T, P> DoubleEndedIterator for SplitInclusive<'a, T, P> where P: FnMut(&T) -> bool, @@ -3822,7 +3940,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl FusedIterator for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool {} /// An iterator over the mutable subslices of the vector which are separated @@ -3947,7 +4065,7 @@ impl FusedIterator for SplitMut<'_, T, P> where P: FnMut(&T) -> bool {} /// /// [`split_inclusive_mut`]: ../../std/primitive.slice.html#method.split_inclusive_mut /// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] pub struct SplitInclusiveMut<'a, T: 'a, P> where P: FnMut(&T) -> bool, @@ -3957,7 +4075,7 @@ where finished: bool, } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl fmt::Debug for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool, @@ -3970,7 +4088,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, T, P> Iterator for SplitInclusiveMut<'a, T, P> where P: FnMut(&T) -> bool, @@ -4010,7 +4128,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, T, P> DoubleEndedIterator for SplitInclusiveMut<'a, T, P> where P: FnMut(&T) -> bool, @@ -4044,7 +4162,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl FusedIterator for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool {} /// An iterator over subslices separated by elements that match a predicate diff --git a/src/libcore/slice/sort.rs b/src/libcore/slice/sort.rs index 019832e16f89c..be3e7aaa2e89a 100644 --- a/src/libcore/slice/sort.rs +++ b/src/libcore/slice/sort.rs @@ -143,7 +143,7 @@ where } } -/// Sorts `v` using heapsort, which guarantees `O(n log n)` worst-case. +/// Sorts `v` using heapsort, which guarantees `O(n * log(n))` worst-case. #[cold] pub fn heapsort(v: &mut [T], is_less: &mut F) where @@ -621,7 +621,7 @@ where } // If too many bad pivot choices were made, simply fall back to heapsort in order to - // guarantee `O(n log n)` worst-case. + // guarantee `O(n * log(n))` worst-case. if limit == 0 { heapsort(v, is_less); return; @@ -684,7 +684,7 @@ where } } -/// Sorts `v` using pattern-defeating quicksort, which is `O(n log n)` worst-case. +/// Sorts `v` using pattern-defeating quicksort, which is `O(n * log(n))` worst-case. pub fn quicksort(v: &mut [T], mut is_less: F) where F: FnMut(&T, &T) -> bool, diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 6ad0e68a88f3b..c517286d49898 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -9,7 +9,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use self::pattern::Pattern; -use self::pattern::{DoubleEndedSearcher, ReverseSearcher, SearchStep, Searcher}; +use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; use crate::char; use crate::fmt::{self, Write}; @@ -1794,6 +1794,7 @@ mod traits { #[inline(never)] #[cold] + #[track_caller] fn str_index_overflow_fail() -> ! { panic!("attempted to index str up to maximum usize"); } @@ -2185,6 +2186,7 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) { #[inline(never)] #[cold] +#[track_caller] fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { const MAX_DISPLAY_LENGTH: usize = 256; let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); @@ -2640,7 +2642,7 @@ impl str { /// # Panics /// /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// beyond the last code point of the string slice. + /// past the end of the last code point of the string slice. /// /// # Examples /// @@ -2681,7 +2683,7 @@ impl str { /// # Panics /// /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// beyond the last code point of the string slice. + /// past the end of the last code point of the string slice. /// /// # Examples /// @@ -3008,6 +3010,12 @@ impl str { /// /// Returns `false` if it does not. /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html + /// /// # Examples /// /// Basic usage: @@ -3029,6 +3037,12 @@ impl str { /// /// Returns `false` if it does not. /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html + /// /// # Examples /// /// Basic usage: @@ -3049,6 +3063,12 @@ impl str { /// /// Returns `false` if it does not. /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html + /// /// # Examples /// /// Basic usage: @@ -3072,10 +3092,12 @@ impl str { /// /// Returns [`None`] if the pattern doesn't match. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. /// /// [`None`]: option/enum.Option.html#variant.None + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Examples /// @@ -3119,10 +3141,12 @@ impl str { /// /// Returns [`None`] if the pattern doesn't match. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. /// /// [`None`]: option/enum.Option.html#variant.None + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Examples /// @@ -3164,8 +3188,11 @@ impl str { /// An iterator over substrings of this string slice, separated by /// characters matched by a pattern. /// - /// The pattern can be any type that implements the Pattern trait. Notable - /// examples are `&str`, [`char`], and closures that determines the split. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3283,6 +3310,12 @@ impl str { /// `split` in that `split_inclusive` leaves the matched part as the /// terminator of the substring. /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html + /// /// # Examples /// /// ``` @@ -3302,7 +3335,7 @@ impl str { /// .split_inclusive('\n').collect(); /// assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb.\n"]); /// ``` - #[unstable(feature = "split_inclusive", issue = "none")] + #[unstable(feature = "split_inclusive", issue = "72360")] #[inline] pub fn split_inclusive<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitInclusive<'a, P> { SplitInclusive(SplitInternal { @@ -3317,8 +3350,11 @@ impl str { /// An iterator over substrings of the given string slice, separated by /// characters matched by a pattern and yielded in reverse order. /// - /// The pattern can be any type that implements the Pattern trait. Notable - /// examples are `&str`, [`char`], and closures that determines the split. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3368,8 +3404,11 @@ impl str { /// An iterator over substrings of the given string slice, separated by /// characters matched by a pattern. /// - /// The pattern can be any type that implements the Pattern trait. Notable - /// examples are `&str`, [`char`], and closures that determines the split. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// Equivalent to [`split`], except that the trailing substring /// is skipped if empty. @@ -3412,10 +3451,11 @@ impl str { /// An iterator over substrings of `self`, separated by characters /// matched by a pattern and yielded in reverse order. /// - /// The pattern can be any type that implements the Pattern trait. Notable - /// examples are `&str`, [`char`], and closures that determines the split. - /// Additional libraries might provide more complex patterns like - /// regular expressions. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// Equivalent to [`split`], except that the trailing substring is /// skipped if empty. @@ -3460,8 +3500,11 @@ impl str { /// If `n` substrings are returned, the last substring (the `n`th substring) /// will contain the remainder of the string. /// - /// The pattern can be any type that implements the Pattern trait. Notable - /// examples are `&str`, [`char`], and closures that determines the split. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3510,8 +3553,11 @@ impl str { /// If `n` substrings are returned, the last substring (the `n`th substring) /// will contain the remainder of the string. /// - /// The pattern can be any type that implements the Pattern trait. Notable - /// examples are `&str`, [`char`], and closures that determines the split. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3555,8 +3601,11 @@ impl str { /// An iterator over the disjoint matches of a pattern within the given string /// slice. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3591,8 +3640,11 @@ impl str { /// An iterator over the disjoint matches of a pattern within this string slice, /// yielded in reverse order. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3632,8 +3684,11 @@ impl str { /// For matches of `pat` within `self` that overlap, only the indices /// corresponding to the first match are returned. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines - /// if a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3674,8 +3729,11 @@ impl str { /// For matches of `pat` within `self` that overlap, only the indices /// corresponding to the last match are returned. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if a - /// character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Iterator behavior /// @@ -3892,8 +3950,11 @@ impl str { /// Returns a string slice with all prefixes and suffixes that match a /// pattern repeatedly removed. /// - /// The pattern can be a [`char`] or a closure that determines if a - /// character matches. + /// The [pattern] can be a [`char`], a slice of [`char`]s, or a function + /// or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Examples /// @@ -3937,8 +3998,11 @@ impl str { /// Returns a string slice with all prefixes that match a pattern /// repeatedly removed. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Text directionality /// @@ -3979,31 +4043,26 @@ impl str { /// /// If the string does not start with `prefix`, `None` is returned. /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html + /// /// # Examples /// /// ``` /// #![feature(str_strip)] /// - /// assert_eq!("foobar".strip_prefix("foo"), Some("bar")); - /// assert_eq!("foobar".strip_prefix("bar"), None); + /// assert_eq!("foo:bar".strip_prefix("foo:"), Some("bar")); + /// assert_eq!("foo:bar".strip_prefix("bar"), None); /// assert_eq!("foofoo".strip_prefix("foo"), Some("foo")); /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[unstable(feature = "str_strip", reason = "newly added", issue = "67302")] pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { - let mut matcher = prefix.into_searcher(self); - if let SearchStep::Match(start, len) = matcher.next() { - debug_assert_eq!( - start, 0, - "The first search step from Searcher \ - must include the first character" - ); - // SAFETY: `Searcher` is known to return valid indices. - unsafe { Some(self.get_unchecked(len..)) } - } else { - None - } + prefix.strip_prefix_of(self) } /// Returns a string slice with the suffix removed. @@ -4014,12 +4073,18 @@ impl str { /// /// If the string does not end with `suffix`, `None` is returned. /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html + /// /// # Examples /// /// ``` /// #![feature(str_strip)] - /// assert_eq!("barfoo".strip_suffix("foo"), Some("bar")); - /// assert_eq!("barfoo".strip_suffix("bar"), None); + /// assert_eq!("bar:foo".strip_suffix(":foo"), Some("bar")); + /// assert_eq!("bar:foo".strip_suffix("bar"), None); /// assert_eq!("foofoo".strip_suffix("foo"), Some("foo")); /// ``` #[must_use = "this returns the remaining substring as a new slice, \ @@ -4030,26 +4095,17 @@ impl str { P: Pattern<'a>,

>::Searcher: ReverseSearcher<'a>, { - let mut matcher = suffix.into_searcher(self); - if let SearchStep::Match(start, end) = matcher.next_back() { - debug_assert_eq!( - end, - self.len(), - "The first search step from ReverseSearcher \ - must include the last character" - ); - // SAFETY: `Searcher` is known to return valid indices. - unsafe { Some(self.get_unchecked(..start)) } - } else { - None - } + suffix.strip_suffix_of(self) } /// Returns a string slice with all suffixes that match a pattern /// repeatedly removed. /// - /// The pattern can be a `&str`, [`char`], or a closure that - /// determines if a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Text directionality /// @@ -4094,10 +4150,11 @@ impl str { /// Returns a string slice with all prefixes that match a pattern /// repeatedly removed. /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. /// /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Text directionality /// @@ -4130,10 +4187,11 @@ impl str { /// Returns a string slice with all suffixes that match a pattern /// repeatedly removed. /// - /// The pattern can be a `&str`, [`char`], or a closure that - /// determines if a character matches. + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. /// /// [`char`]: primitive.char.html + /// [pattern]: str/pattern/index.html /// /// # Text directionality /// @@ -4517,7 +4575,7 @@ pub struct SplitAsciiWhitespace<'a> { /// /// [`split_inclusive`]: ../../std/primitive.str.html#method.split_inclusive /// [`str`]: ../../std/primitive.str.html -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] pub struct SplitInclusive<'a, P: Pattern<'a>>(SplitInternal<'a, P>); impl_fn_for_zst! { @@ -4610,7 +4668,7 @@ impl<'a> DoubleEndedIterator for SplitAsciiWhitespace<'a> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] impl FusedIterator for SplitAsciiWhitespace<'_> {} -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { type Item = &'a str; @@ -4620,7 +4678,7 @@ impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInclusive").field("0", &self.0).finish() @@ -4628,14 +4686,14 @@ impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, } // FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, P: Pattern<'a, Searcher: Clone>> Clone for SplitInclusive<'a, P> { fn clone(&self) -> Self { SplitInclusive(self.0.clone()) } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, P: Pattern<'a, Searcher: ReverseSearcher<'a>>> DoubleEndedIterator for SplitInclusive<'a, P> { @@ -4645,7 +4703,7 @@ impl<'a, P: Pattern<'a, Searcher: ReverseSearcher<'a>>> DoubleEndedIterator } } -#[unstable(feature = "split_inclusive", issue = "none")] +#[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} /// An iterator of [`u16`] over the string encoded as UTF-16. diff --git a/src/libcore/str/pattern.rs b/src/libcore/str/pattern.rs index ffa418cba6c99..1a2b612b2f95c 100644 --- a/src/libcore/str/pattern.rs +++ b/src/libcore/str/pattern.rs @@ -1,7 +1,41 @@ //! The string Pattern API. //! +//! The Pattern API provides a generic mechanism for using different pattern +//! types when searching through a string. +//! //! For more details, see the traits [`Pattern`], [`Searcher`], //! [`ReverseSearcher`], and [`DoubleEndedSearcher`]. +//! +//! Although this API is unstable, it is exposed via stable APIs on the +//! [`str`] type. +//! +//! # Examples +//! +//! [`Pattern`] is [implemented][pattern-impls] in the stable API for +//! [`&str`], [`char`], slices of [`char`], and functions and closures +//! implementing `FnMut(char) -> bool`. +//! +//! ``` +//! let s = "Can you find a needle in a haystack?"; +//! +//! // &str pattern +//! assert_eq!(s.find("you"), Some(4)); +//! // char pattern +//! assert_eq!(s.find('n'), Some(2)); +//! // slice of chars pattern +//! assert_eq!(s.find(&['a', 'e', 'i', 'o', 'u'][..]), Some(1)); +//! // closure pattern +//! assert_eq!(s.find(|c: char| c.is_ascii_punctuation()), Some(35)); +//! ``` +//! +//! [`&str`]: ../../../std/primitive.str.html +//! [`char`]: ../../../std/primitive.char.html +//! [`str`]: ../../../std/primitive.str.html +//! [`DoubleEndedSearcher`]: trait.DoubleEndedSearcher.html +//! [`Pattern`]: trait.Pattern.html +//! [`ReverseSearcher`]: trait.ReverseSearcher.html +//! [`Searcher`]: trait.Searcher.html +//! [pattern-impls]: trait.Pattern.html#implementors #![unstable( feature = "pattern", @@ -12,7 +46,6 @@ use crate::cmp; use crate::fmt; use crate::slice::memchr; -use crate::usize; // Pattern @@ -47,6 +80,22 @@ pub trait Pattern<'a>: Sized { matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _)) } + /// Removes the pattern from the front of haystack, if it matches. + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + if let SearchStep::Match(start, len) = self.into_searcher(haystack).next() { + debug_assert_eq!( + start, 0, + "The first search step from Searcher \ + must include the first character" + ); + // SAFETY: `Searcher` is known to return valid indices. + unsafe { Some(haystack.get_unchecked(len..)) } + } else { + None + } + } + /// Checks whether the pattern matches at the back of the haystack #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool @@ -55,6 +104,26 @@ pub trait Pattern<'a>: Sized { { matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) } + + /// Removes the pattern from the back of haystack, if it matches. + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + where + Self::Searcher: ReverseSearcher<'a>, + { + if let SearchStep::Match(start, end) = self.into_searcher(haystack).next_back() { + debug_assert_eq!( + end, + haystack.len(), + "The first search step from ReverseSearcher \ + must include the last character" + ); + // SAFETY: `Searcher` is known to return valid indices. + unsafe { Some(haystack.get_unchecked(..start)) } + } else { + None + } + } } // Searcher @@ -415,7 +484,13 @@ unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> { impl<'a> DoubleEndedSearcher<'a> for CharSearcher<'a> {} -/// Searches for chars that are equal to a given char +/// Searches for chars that are equal to a given `char`. +/// +/// # Examples +/// +/// ``` +/// assert_eq!("Hello world".find('o'), Some(4)); +/// ``` impl<'a> Pattern<'a> for char { type Searcher = CharSearcher<'a>; @@ -448,6 +523,11 @@ impl<'a> Pattern<'a> for char { self.encode_utf8(&mut [0u8; 4]).is_prefix_of(haystack) } + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + self.encode_utf8(&mut [0u8; 4]).strip_prefix_of(haystack) + } + #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool where @@ -455,6 +535,14 @@ impl<'a> Pattern<'a> for char { { self.encode_utf8(&mut [0u8; 4]).is_suffix_of(haystack) } + + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + where + Self::Searcher: ReverseSearcher<'a>, + { + self.encode_utf8(&mut [0u8; 4]).strip_suffix_of(haystack) + } } ///////////////////////////////////////////////////////////////////////////// @@ -569,6 +657,11 @@ macro_rules! pattern_methods { ($pmap)(self).is_prefix_of(haystack) } + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + ($pmap)(self).strip_prefix_of(haystack) + } + #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool where @@ -576,6 +669,14 @@ macro_rules! pattern_methods { { ($pmap)(self).is_suffix_of(haystack) } + + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + where + $t: ReverseSearcher<'a>, + { + ($pmap)(self).strip_suffix_of(haystack) + } }; } @@ -634,7 +735,14 @@ unsafe impl<'a, 'b> ReverseSearcher<'a> for CharSliceSearcher<'a, 'b> { impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {} -/// Searches for chars that are equal to any of the chars in the array +/// Searches for chars that are equal to any of the chars in the slice. +/// +/// # Examples +/// +/// ``` +/// assert_eq!("Hello world".find(&['l', 'l'] as &[_]), Some(2)); +/// assert_eq!("Hello world".find(&['l', 'l'][..]), Some(2)); +/// ``` impl<'a, 'b> Pattern<'a> for &'b [char] { pattern_methods!(CharSliceSearcher<'a, 'b>, MultiCharEqPattern, CharSliceSearcher); } @@ -676,7 +784,14 @@ where impl<'a, F> DoubleEndedSearcher<'a> for CharPredicateSearcher<'a, F> where F: FnMut(char) -> bool {} -/// Searches for chars that match the given predicate +/// Searches for chars that match the given predicate. +/// +/// # Examples +/// +/// ``` +/// assert_eq!("Hello world".find(char::is_uppercase), Some(0)); +/// assert_eq!("Hello world".find(|c| "aeiou".contains(c)), Some(1)); +/// ``` impl<'a, F> Pattern<'a> for F where F: FnMut(char) -> bool, @@ -701,6 +816,12 @@ impl<'a, 'b, 'c> Pattern<'a> for &'c &'b str { /// /// Will handle the pattern `""` as returning empty matches at each character /// boundary. +/// +/// # Examples +/// +/// ``` +/// assert_eq!("Hello world".find("world"), Some(6)); +/// ``` impl<'a, 'b> Pattern<'a> for &'b str { type Searcher = StrSearcher<'a, 'b>; @@ -709,17 +830,40 @@ impl<'a, 'b> Pattern<'a> for &'b str { StrSearcher::new(haystack, self) } - /// Checks whether the pattern matches at the front of the haystack + /// Checks whether the pattern matches at the front of the haystack. #[inline] fn is_prefix_of(self, haystack: &'a str) -> bool { haystack.as_bytes().starts_with(self.as_bytes()) } - /// Checks whether the pattern matches at the back of the haystack + /// Removes the pattern from the front of haystack, if it matches. + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + if self.is_prefix_of(haystack) { + // SAFETY: prefix was just verified to exist. + unsafe { Some(haystack.get_unchecked(self.as_bytes().len()..)) } + } else { + None + } + } + + /// Checks whether the pattern matches at the back of the haystack. #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool { haystack.as_bytes().ends_with(self.as_bytes()) } + + /// Removes the pattern from the back of haystack, if it matches. + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + if self.is_suffix_of(haystack) { + let i = haystack.len() - self.as_bytes().len(); + // SAFETY: suffix was just verified to exist. + unsafe { Some(haystack.get_unchecked(..i)) } + } else { + None + } + } } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/libcore/tests/fmt/num.rs b/src/libcore/tests/fmt/num.rs index a50c2b46a919b..275a1d062cafb 100644 --- a/src/libcore/tests/fmt/num.rs +++ b/src/libcore/tests/fmt/num.rs @@ -104,7 +104,6 @@ fn test_format_int() { #[test] fn test_format_int_exp_limits() { - use core::{i128, i16, i32, i64, i8, u128, u16, u32, u64, u8}; assert_eq!(format!("{:e}", i8::MIN), "-1.28e2"); assert_eq!(format!("{:e}", i8::MAX), "1.27e2"); assert_eq!(format!("{:e}", i16::MIN), "-3.2768e4"); @@ -125,8 +124,6 @@ fn test_format_int_exp_limits() { #[test] fn test_format_int_exp_precision() { - use core::{i128, i16, i32, i64, i8}; - //test that float and integer match let big_int: u32 = 314_159_265; assert_eq!(format!("{:.1e}", big_int), format!("{:.1e}", f64::from(big_int))); @@ -214,7 +211,6 @@ fn test_format_int_sign_padding() { #[test] fn test_format_int_twos_complement() { - use core::{i16, i32, i64, i8}; assert_eq!(format!("{}", i8::MIN), "-128"); assert_eq!(format!("{}", i16::MIN), "-32768"); assert_eq!(format!("{}", i32::MIN), "-2147483648"); diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 98e3eeb982bde..52cf068f0a567 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -3,8 +3,6 @@ use core::cell::Cell; use core::convert::TryFrom; use core::iter::*; -use core::usize; -use core::{i16, i8, isize}; #[test] fn test_lt() { @@ -78,7 +76,6 @@ fn test_cmp_by() { #[test] fn test_partial_cmp_by() { use core::cmp::Ordering; - use core::f64; let f = |x: i32, y: i32| (x * x).partial_cmp(&y); let xs = || [1, 2, 3, 4].iter().copied(); @@ -210,50 +207,64 @@ fn test_iterator_chain_find() { assert_eq!(iter.next(), None); } -#[test] -fn test_iterator_chain_size_hint() { - struct Iter { - is_empty: bool, - } +struct Toggle { + is_empty: bool, +} - impl Iterator for Iter { - type Item = (); +impl Iterator for Toggle { + type Item = (); - // alternates between `None` and `Some(())` - fn next(&mut self) -> Option { - if self.is_empty { - self.is_empty = false; - None - } else { - self.is_empty = true; - Some(()) - } + // alternates between `None` and `Some(())` + fn next(&mut self) -> Option { + if self.is_empty { + self.is_empty = false; + None + } else { + self.is_empty = true; + Some(()) } + } - fn size_hint(&self) -> (usize, Option) { - if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } - } + fn size_hint(&self) -> (usize, Option) { + if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } } +} - impl DoubleEndedIterator for Iter { - fn next_back(&mut self) -> Option { - self.next() - } +impl DoubleEndedIterator for Toggle { + fn next_back(&mut self) -> Option { + self.next() } +} +#[test] +fn test_iterator_chain_size_hint() { // this chains an iterator of length 0 with an iterator of length 1, // so after calling `.next()` once, the iterator is empty and the // state is `ChainState::Back`. `.size_hint()` should now disregard // the size hint of the left iterator - let mut iter = Iter { is_empty: true }.chain(once(())); + let mut iter = Toggle { is_empty: true }.chain(once(())); assert_eq!(iter.next(), Some(())); assert_eq!(iter.size_hint(), (0, Some(0))); - let mut iter = once(()).chain(Iter { is_empty: true }); + let mut iter = once(()).chain(Toggle { is_empty: true }); assert_eq!(iter.next_back(), Some(())); assert_eq!(iter.size_hint(), (0, Some(0))); } +#[test] +fn test_iterator_chain_unfused() { + // Chain shouldn't be fused in its second iterator, depending on direction + let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); + iter.next().unwrap_none(); + iter.next().unwrap(); + iter.next().unwrap_none(); + + let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); + iter.next_back().unwrap_none(); + iter.next_back().unwrap(); + iter.next_back().unwrap_none(); +} + #[test] fn test_zip_nth() { let xs = [0, 1, 2, 4, 5]; @@ -2128,6 +2139,24 @@ fn test_range_inclusive_nth_back() { assert_eq!(ExactSizeIterator::is_empty(&r), true); } +#[test] +fn test_range_len() { + assert_eq!((0..10_u8).len(), 10); + assert_eq!((9..10_u8).len(), 1); + assert_eq!((10..10_u8).len(), 0); + assert_eq!((11..10_u8).len(), 0); + assert_eq!((100..10_u8).len(), 0); +} + +#[test] +fn test_range_inclusive_len() { + assert_eq!((0..=10_u8).len(), 11); + assert_eq!((9..=10_u8).len(), 2); + assert_eq!((10..=10_u8).len(), 1); + assert_eq!((11..=10_u8).len(), 0); + assert_eq!((100..=10_u8).len(), 0); +} + #[test] fn test_range_step() { #![allow(deprecated)] @@ -2251,62 +2280,58 @@ fn test_range_inclusive_folds() { #[test] fn test_range_size_hint() { - use core::usize::MAX as UMAX; assert_eq!((0..0usize).size_hint(), (0, Some(0))); assert_eq!((0..100usize).size_hint(), (100, Some(100))); - assert_eq!((0..UMAX).size_hint(), (UMAX, Some(UMAX))); + assert_eq!((0..usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); - let umax = u128::try_from(UMAX).unwrap(); + let umax = u128::try_from(usize::MAX).unwrap(); assert_eq!((0..0u128).size_hint(), (0, Some(0))); assert_eq!((0..100u128).size_hint(), (100, Some(100))); - assert_eq!((0..umax).size_hint(), (UMAX, Some(UMAX))); - assert_eq!((0..umax + 1).size_hint(), (UMAX, None)); + assert_eq!((0..umax).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..umax + 1).size_hint(), (usize::MAX, None)); - use core::isize::{MAX as IMAX, MIN as IMIN}; assert_eq!((0..0isize).size_hint(), (0, Some(0))); assert_eq!((-100..100isize).size_hint(), (200, Some(200))); - assert_eq!((IMIN..IMAX).size_hint(), (UMAX, Some(UMAX))); + assert_eq!((isize::MIN..isize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); - let imin = i128::try_from(IMIN).unwrap(); - let imax = i128::try_from(IMAX).unwrap(); + let imin = i128::try_from(isize::MIN).unwrap(); + let imax = i128::try_from(isize::MAX).unwrap(); assert_eq!((0..0i128).size_hint(), (0, Some(0))); assert_eq!((-100..100i128).size_hint(), (200, Some(200))); - assert_eq!((imin..imax).size_hint(), (UMAX, Some(UMAX))); - assert_eq!((imin..imax + 1).size_hint(), (UMAX, None)); + assert_eq!((imin..imax).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((imin..imax + 1).size_hint(), (usize::MAX, None)); } #[test] fn test_range_inclusive_size_hint() { - use core::usize::MAX as UMAX; assert_eq!((1..=0usize).size_hint(), (0, Some(0))); assert_eq!((0..=0usize).size_hint(), (1, Some(1))); assert_eq!((0..=100usize).size_hint(), (101, Some(101))); - assert_eq!((0..=UMAX - 1).size_hint(), (UMAX, Some(UMAX))); - assert_eq!((0..=UMAX).size_hint(), (UMAX, None)); + assert_eq!((0..=usize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..=usize::MAX).size_hint(), (usize::MAX, None)); - let umax = u128::try_from(UMAX).unwrap(); + let umax = u128::try_from(usize::MAX).unwrap(); assert_eq!((1..=0u128).size_hint(), (0, Some(0))); assert_eq!((0..=0u128).size_hint(), (1, Some(1))); assert_eq!((0..=100u128).size_hint(), (101, Some(101))); - assert_eq!((0..=umax - 1).size_hint(), (UMAX, Some(UMAX))); - assert_eq!((0..=umax).size_hint(), (UMAX, None)); - assert_eq!((0..=umax + 1).size_hint(), (UMAX, None)); + assert_eq!((0..=umax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..=umax).size_hint(), (usize::MAX, None)); + assert_eq!((0..=umax + 1).size_hint(), (usize::MAX, None)); - use core::isize::{MAX as IMAX, MIN as IMIN}; assert_eq!((0..=-1isize).size_hint(), (0, Some(0))); assert_eq!((0..=0isize).size_hint(), (1, Some(1))); assert_eq!((-100..=100isize).size_hint(), (201, Some(201))); - assert_eq!((IMIN..=IMAX - 1).size_hint(), (UMAX, Some(UMAX))); - assert_eq!((IMIN..=IMAX).size_hint(), (UMAX, None)); + assert_eq!((isize::MIN..=isize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((isize::MIN..=isize::MAX).size_hint(), (usize::MAX, None)); - let imin = i128::try_from(IMIN).unwrap(); - let imax = i128::try_from(IMAX).unwrap(); + let imin = i128::try_from(isize::MIN).unwrap(); + let imax = i128::try_from(isize::MAX).unwrap(); assert_eq!((0..=-1i128).size_hint(), (0, Some(0))); assert_eq!((0..=0i128).size_hint(), (1, Some(1))); assert_eq!((-100..=100i128).size_hint(), (201, Some(201))); - assert_eq!((imin..=imax - 1).size_hint(), (UMAX, Some(UMAX))); - assert_eq!((imin..=imax).size_hint(), (UMAX, None)); - assert_eq!((imin..=imax + 1).size_hint(), (UMAX, None)); + assert_eq!((imin..=imax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((imin..=imax).size_hint(), (usize::MAX, None)); + assert_eq!((imin..=imax + 1).size_hint(), (usize::MAX, None)); } #[test] @@ -2502,42 +2527,91 @@ fn test_chain_fold() { } #[test] -fn test_step_replace_unsigned() { - let mut x = 4u32; - let y = x.replace_zero(); - assert_eq!(x, 0); - assert_eq!(y, 4); +fn test_steps_between() { + assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize)); + assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize)); + assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize)); + assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize)); + assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize)); + assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize)); + + // Skip u64/i64 to avoid differences with 32-bit vs 64-bit platforms - x = 5; - let y = x.replace_one(); - assert_eq!(x, 1); - assert_eq!(y, 5); + assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize)); + assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize)); + if cfg!(target_pointer_width = "64") { + assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128), Some(usize::MAX)); + } + assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), None); + assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), None); + assert_eq!( + Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128,), + None, + ); } #[test] -fn test_step_replace_signed() { - let mut x = 4i32; - let y = x.replace_zero(); - assert_eq!(x, 0); - assert_eq!(y, 4); +fn test_step_forward() { + assert_eq!(Step::forward_checked(55_u8, 200_usize), Some(255_u8)); + assert_eq!(Step::forward_checked(252_u8, 200_usize), None); + assert_eq!(Step::forward_checked(0_u8, 256_usize), None); + assert_eq!(Step::forward_checked(-110_i8, 200_usize), Some(90_i8)); + assert_eq!(Step::forward_checked(-110_i8, 248_usize), None); + assert_eq!(Step::forward_checked(-126_i8, 256_usize), None); - x = 5; - let y = x.replace_one(); - assert_eq!(x, 1); - assert_eq!(y, 5); + assert_eq!(Step::forward_checked(35_u16, 100_usize), Some(135_u16)); + assert_eq!(Step::forward_checked(35_u16, 65500_usize), Some(u16::MAX)); + assert_eq!(Step::forward_checked(36_u16, 65500_usize), None); + assert_eq!(Step::forward_checked(-110_i16, 200_usize), Some(90_i16)); + assert_eq!(Step::forward_checked(-20_030_i16, 50_050_usize), Some(30_020_i16)); + assert_eq!(Step::forward_checked(-10_i16, 40_000_usize), None); + assert_eq!(Step::forward_checked(-10_i16, 70_000_usize), None); + + assert_eq!(Step::forward_checked(10_u128, 70_000_usize), Some(70_010_u128)); + assert_eq!(Step::forward_checked(10_i128, 70_030_usize), Some(70_040_i128)); + assert_eq!( + Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0xff_usize), + Some(u128::MAX), + ); + assert_eq!( + Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0x100_usize), + None + ); + assert_eq!( + Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0xff_usize), + Some(i128::MAX), + ); + assert_eq!( + Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), + None + ); } #[test] -fn test_step_replace_no_between() { - let mut x = 4u128; - let y = x.replace_zero(); - assert_eq!(x, 0); - assert_eq!(y, 4); +fn test_step_backward() { + assert_eq!(Step::backward_checked(255_u8, 200_usize), Some(55_u8)); + assert_eq!(Step::backward_checked(100_u8, 200_usize), None); + assert_eq!(Step::backward_checked(255_u8, 256_usize), None); + assert_eq!(Step::backward_checked(90_i8, 200_usize), Some(-110_i8)); + assert_eq!(Step::backward_checked(110_i8, 248_usize), None); + assert_eq!(Step::backward_checked(127_i8, 256_usize), None); - x = 5; - let y = x.replace_one(); - assert_eq!(x, 1); - assert_eq!(y, 5); + assert_eq!(Step::backward_checked(135_u16, 100_usize), Some(35_u16)); + assert_eq!(Step::backward_checked(u16::MAX, 65500_usize), Some(35_u16)); + assert_eq!(Step::backward_checked(10_u16, 11_usize), None); + assert_eq!(Step::backward_checked(90_i16, 200_usize), Some(-110_i16)); + assert_eq!(Step::backward_checked(30_020_i16, 50_050_usize), Some(-20_030_i16)); + assert_eq!(Step::backward_checked(-10_i16, 40_000_usize), None); + assert_eq!(Step::backward_checked(-10_i16, 70_000_usize), None); + + assert_eq!(Step::backward_checked(70_010_u128, 70_000_usize), Some(10_u128)); + assert_eq!(Step::backward_checked(70_020_i128, 70_030_usize), Some(-10_i128)); + assert_eq!(Step::backward_checked(10_u128, 7_usize), Some(3_u128)); + assert_eq!(Step::backward_checked(10_u128, 11_usize), None); + assert_eq!( + Step::backward_checked(-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), + Some(i128::MIN) + ); } #[test] @@ -2900,7 +2974,7 @@ fn test_is_sorted() { assert!(![1, 3, 2].iter().is_sorted()); assert!([0].iter().is_sorted()); assert!(std::iter::empty::().is_sorted()); - assert!(![0.0, 1.0, std::f32::NAN].iter().is_sorted()); + assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); assert!([-2, -1, 0, 3].iter().is_sorted()); assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs())); assert!(!["c", "bb", "aaa"].iter().is_sorted()); diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 05f958cbe81fe..6a8e908b9c618 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -17,11 +17,11 @@ #![feature(pattern)] #![feature(range_is_empty)] #![feature(raw)] -#![feature(saturating_neg)] #![feature(sort_internals)] #![feature(slice_partition_at_index)] #![feature(specialization)] #![feature(step_trait)] +#![feature(step_trait_ext)] #![feature(str_internals)] #![feature(test)] #![feature(trusted_len)] @@ -42,6 +42,7 @@ #![feature(unwrap_infallible)] #![feature(leading_trailing_ones)] #![feature(const_forget)] +#![feature(option_unwrap_none)] extern crate test; diff --git a/src/libcore/tests/nonzero.rs b/src/libcore/tests/nonzero.rs index 6c5d19845e406..0227a66b8633a 100644 --- a/src/libcore/tests/nonzero.rs +++ b/src/libcore/tests/nonzero.rs @@ -141,3 +141,38 @@ fn test_from_str() { Some(IntErrorKind::Overflow) ); } + +#[test] +fn test_nonzero_bitor() { + let nz_alt = NonZeroU8::new(0b1010_1010).unwrap(); + let nz_low = NonZeroU8::new(0b0000_1111).unwrap(); + + let both_nz: NonZeroU8 = nz_alt | nz_low; + assert_eq!(both_nz.get(), 0b1010_1111); + + let rhs_int: NonZeroU8 = nz_low | 0b1100_0000u8; + assert_eq!(rhs_int.get(), 0b1100_1111); + + let rhs_zero: NonZeroU8 = nz_alt | 0u8; + assert_eq!(rhs_zero.get(), 0b1010_1010); + + let lhs_int: NonZeroU8 = 0b0110_0110u8 | nz_alt; + assert_eq!(lhs_int.get(), 0b1110_1110); + + let lhs_zero: NonZeroU8 = 0u8 | nz_low; + assert_eq!(lhs_zero.get(), 0b0000_1111); +} + +#[test] +fn test_nonzero_bitor_assign() { + let mut target = NonZeroU8::new(0b1010_1010).unwrap(); + + target |= NonZeroU8::new(0b0000_1111).unwrap(); + assert_eq!(target.get(), 0b1010_1111); + + target |= 0b0001_0000; + assert_eq!(target.get(), 0b1011_1111); + + target |= 0; + assert_eq!(target.get(), 0b1011_1111); +} diff --git a/src/libcore/tests/num/dec2flt/mod.rs b/src/libcore/tests/num/dec2flt/mod.rs index a1fa5556ae5db..1c172f49c279c 100644 --- a/src/libcore/tests/num/dec2flt/mod.rs +++ b/src/libcore/tests/num/dec2flt/mod.rs @@ -1,7 +1,5 @@ #![allow(overflowing_literals)] -use std::{f32, f64, i64}; - mod parse; mod rawfp; diff --git a/src/libcore/tests/num/dec2flt/rawfp.rs b/src/libcore/tests/num/dec2flt/rawfp.rs index 665fb6b9efb8c..c098b9c2ba27d 100644 --- a/src/libcore/tests/num/dec2flt/rawfp.rs +++ b/src/libcore/tests/num/dec2flt/rawfp.rs @@ -1,8 +1,6 @@ use core::num::dec2flt::rawfp::RawFloat; use core::num::dec2flt::rawfp::{fp_to_float, next_float, prev_float, round_normal}; use core::num::diy_float::Fp; -use std::f32; -use std::f64; fn integer_decode(f: f64) -> (u64, i16, i8) { RawFloat::integer_decode(f) diff --git a/src/libcore/tests/num/flt2dec/estimator.rs b/src/libcore/tests/num/flt2dec/estimator.rs index 8ee06d895cae3..da203b5f3620e 100644 --- a/src/libcore/tests/num/flt2dec/estimator.rs +++ b/src/libcore/tests/num/flt2dec/estimator.rs @@ -52,12 +52,10 @@ fn test_estimate_scaling_factor() { assert_almost_eq!(estimate_scaling_factor(1, -1074), -323); assert_almost_eq!(estimate_scaling_factor(0x1fffffffffffff, 971), 309); - #[cfg(not(miri))] // Miri is too slow - let iter = -1074..972; - #[cfg(miri)] - let iter = (-1074..972).step_by(37); + // Miri is too slow + let step = if cfg!(miri) { 37 } else { 1 }; - for i in iter { + for i in (-1074..972).step_by(step) { let expected = super::ldexp_f64(1.0, i).log10().ceil(); assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16); } diff --git a/src/libcore/tests/num/flt2dec/mod.rs b/src/libcore/tests/num/flt2dec/mod.rs index e945d9c4a54ce..ae892e3b0bfbf 100644 --- a/src/libcore/tests/num/flt2dec/mod.rs +++ b/src/libcore/tests/num/flt2dec/mod.rs @@ -1,4 +1,4 @@ -use std::{f32, f64, fmt, i16, str}; +use std::{fmt, str}; use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; use core::num::flt2dec::{round_up, Formatted, Part, Sign, MAX_SIG_DIGITS}; diff --git a/src/libcore/tests/num/flt2dec/random.rs b/src/libcore/tests/num/flt2dec/random.rs index ecdfc4b30a59f..0ebc0881f5223 100644 --- a/src/libcore/tests/num/flt2dec/random.rs +++ b/src/libcore/tests/num/flt2dec/random.rs @@ -1,6 +1,5 @@ #![cfg(not(target_arch = "wasm32"))] -use std::i16; use std::str; use core::num::flt2dec::strategy::grisu::format_exact_opt; @@ -139,13 +138,11 @@ where #[test] fn shortest_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_shortest as fallback; - #[cfg(not(miri))] // Miri is too slow - const N: usize = 10_000; - #[cfg(miri)] - const N: usize = 10; + // Miri is too slow + let n = if cfg!(miri) { 10 } else { 10_000 }; - f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, N); - f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, N); + f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); + f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); } #[test] @@ -174,17 +171,15 @@ fn shortest_f64_hard_random_equivalence_test() { #[test] fn exact_f32_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_exact as fallback; - #[cfg(not(miri))] // Miri is too slow - const N: usize = 1_000; - #[cfg(miri)] - const N: usize = 3; + // Miri is too slow + let n = if cfg!(miri) { 3 } else { 1_000 }; for k in 1..21 { f32_random_equivalence_test( |d, buf| format_exact_opt(d, buf, i16::MIN), |d, buf| fallback(d, buf, i16::MIN), k, - N, + n, ); } } @@ -192,17 +187,15 @@ fn exact_f32_random_equivalence_test() { #[test] fn exact_f64_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_exact as fallback; - #[cfg(not(miri))] // Miri is too slow - const N: usize = 1_000; - #[cfg(miri)] - const N: usize = 3; + // Miri is too slow + let n = if cfg!(miri) { 3 } else { 1_000 }; for k in 1..21 { f64_random_equivalence_test( |d, buf| format_exact_opt(d, buf, i16::MIN), |d, buf| fallback(d, buf, i16::MIN), k, - N, + n, ); } } diff --git a/src/libcore/tests/num/int_macros.rs b/src/libcore/tests/num/int_macros.rs index 48a49073b2cf5..8396a0dd62db9 100644 --- a/src/libcore/tests/num/int_macros.rs +++ b/src/libcore/tests/num/int_macros.rs @@ -2,7 +2,6 @@ macro_rules! int_module { ($T:ident, $T_i:ident) => { #[cfg(test)] mod tests { - use core::isize; use core::mem; use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::$T_i::*; diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index f61793a3bca81..181bbb8e18784 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -205,8 +205,6 @@ test_impl_from! { test_u32f64, u32, f64 } // Float -> Float #[test] fn test_f32f64() { - use core::f32; - let max: f64 = f32::MAX.into(); assert_eq!(max as f32, f32::MAX); assert!(max.is_normal()); @@ -704,5 +702,5 @@ macro_rules! test_float { }; } -test_float!(f32, f32, ::core::f32::INFINITY, ::core::f32::NEG_INFINITY, ::core::f32::NAN); -test_float!(f64, f64, ::core::f64::INFINITY, ::core::f64::NEG_INFINITY, ::core::f64::NAN); +test_float!(f32, f32, f32::INFINITY, f32::NEG_INFINITY, f32::NAN); +test_float!(f64, f64, f64::INFINITY, f64::NEG_INFINITY, f64::NAN); diff --git a/src/libcore/tests/ops.rs b/src/libcore/tests/ops.rs index 43eb498d26ab1..3c83f0f230003 100644 --- a/src/libcore/tests/ops.rs +++ b/src/libcore/tests/ops.rs @@ -61,25 +61,23 @@ fn test_range_inclusive() { #[test] fn test_range_is_empty() { - use core::f32::*; - assert!(!(0.0..10.0).is_empty()); assert!((-0.0..0.0).is_empty()); assert!((10.0..0.0).is_empty()); - assert!(!(NEG_INFINITY..INFINITY).is_empty()); - assert!((EPSILON..NAN).is_empty()); - assert!((NAN..EPSILON).is_empty()); - assert!((NAN..NAN).is_empty()); + assert!(!(f32::NEG_INFINITY..f32::INFINITY).is_empty()); + assert!((f32::EPSILON..f32::NAN).is_empty()); + assert!((f32::NAN..f32::EPSILON).is_empty()); + assert!((f32::NAN..f32::NAN).is_empty()); assert!(!(0.0..=10.0).is_empty()); assert!(!(-0.0..=0.0).is_empty()); assert!((10.0..=0.0).is_empty()); - assert!(!(NEG_INFINITY..=INFINITY).is_empty()); - assert!((EPSILON..=NAN).is_empty()); - assert!((NAN..=EPSILON).is_empty()); - assert!((NAN..=NAN).is_empty()); + assert!(!(f32::NEG_INFINITY..=f32::INFINITY).is_empty()); + assert!((f32::EPSILON..=f32::NAN).is_empty()); + assert!((f32::NAN..=f32::EPSILON).is_empty()); + assert!((f32::NAN..=f32::NAN).is_empty()); } #[test] diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index dbab433e33f48..54a585415bce2 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -1108,14 +1108,14 @@ mod slice_index { // note: using 0 specifically ensures that the result of overflowing is 0..0, // so that `get` doesn't simply return None for the wrong reason. - bad: data[0 ..= ::std::usize::MAX]; + bad: data[0 ..= usize::MAX]; message: "maximum usize"; } in mod rangetoinclusive_overflow { data: [0, 1]; - bad: data[..= ::std::usize::MAX]; + bad: data[..= usize::MAX]; message: "maximum usize"; } } // panic_cases! @@ -1227,15 +1227,9 @@ fn sort_unstable() { use core::slice::heapsort; use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; - #[cfg(not(miri))] // Miri is too slow - let large_range = 500..510; - #[cfg(not(miri))] // Miri is too slow - let rounds = 100; - - #[cfg(miri)] - let large_range = 0..0; // empty range - #[cfg(miri)] - let rounds = 1; + // Miri is too slow + let large_range = if cfg!(miri) { 0..0 } else { 500..510 }; + let rounds = if cfg!(miri) { 1 } else { 100 }; let mut v = [0; 600]; let mut tmp = [0; 600]; @@ -1709,7 +1703,7 @@ fn test_is_sorted() { assert!(![1, 3, 2].is_sorted()); assert!([0].is_sorted()); assert!(empty.is_sorted()); - assert!(![0.0, 1.0, std::f32::NAN].is_sorted()); + assert!(![0.0, 1.0, f32::NAN].is_sorted()); assert!([-2, -1, 0, 3].is_sorted()); assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs())); assert!(!["c", "bb", "aaa"].is_sorted()); diff --git a/src/libcore/tests/time.rs b/src/libcore/tests/time.rs index c1fbdf7df76fc..7a6675dc82fa6 100644 --- a/src/libcore/tests/time.rs +++ b/src/libcore/tests/time.rs @@ -14,7 +14,7 @@ fn creation() { #[test] #[should_panic] fn new_overflow() { - let _ = Duration::new(::core::u64::MAX, 1_000_000_000); + let _ = Duration::new(u64::MAX, 1_000_000_000); } #[test] @@ -86,7 +86,7 @@ fn checked_add() { Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)), Some(Duration::new(1, 1)) ); - assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::core::u64::MAX, 0)), None); + assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None); } #[test] @@ -133,7 +133,7 @@ fn checked_mul() { assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3))); assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4))); assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000), Some(Duration::new(2000, 4000))); - assert_eq!(Duration::new(::core::u64::MAX - 1, 0).checked_mul(2), None); + assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None); } #[test] diff --git a/src/libcore/tests/tuple.rs b/src/libcore/tests/tuple.rs index 3a2914698cc26..ea1e281425c89 100644 --- a/src/libcore/tests/tuple.rs +++ b/src/libcore/tests/tuple.rs @@ -1,5 +1,4 @@ use std::cmp::Ordering::{Equal, Greater, Less}; -use std::f64::NAN; #[test] fn test_clone() { @@ -34,12 +33,12 @@ fn test_partial_ord() { assert!(big >= small); assert!(big >= big); - assert!(!((1.0f64, 2.0f64) < (NAN, 3.0))); - assert!(!((1.0f64, 2.0f64) <= (NAN, 3.0))); - assert!(!((1.0f64, 2.0f64) > (NAN, 3.0))); - assert!(!((1.0f64, 2.0f64) >= (NAN, 3.0))); - assert!(((1.0f64, 2.0f64) < (2.0, NAN))); - assert!(!((2.0f64, 2.0f64) < (2.0, NAN))); + assert!(!((1.0f64, 2.0f64) < (f64::NAN, 3.0))); + assert!(!((1.0f64, 2.0f64) <= (f64::NAN, 3.0))); + assert!(!((1.0f64, 2.0f64) > (f64::NAN, 3.0))); + assert!(!((1.0f64, 2.0f64) >= (f64::NAN, 3.0))); + assert!(((1.0f64, 2.0f64) < (2.0, f64::NAN))); + assert!(!((2.0f64, 2.0f64) < (2.0, f64::NAN))); } #[test] diff --git a/src/libcore/time.rs b/src/libcore/time.rs index 2ece2150e6bed..ed1d5d46db5c4 100644 --- a/src/libcore/time.rs +++ b/src/libcore/time.rs @@ -12,9 +12,9 @@ //! assert_eq!(Duration::new(5, 0), Duration::from_secs(5)); //! ``` +use crate::fmt; use crate::iter::Sum; use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; -use crate::{fmt, u64}; const NANOS_PER_SEC: u32 = 1_000_000_000; const NANOS_PER_MILLI: u32 = 1_000_000; @@ -389,7 +389,7 @@ impl Duration { /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); - /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(std::u64::MAX, 0)), None); + /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None); /// ``` #[stable(feature = "duration_checked_ops", since = "1.16.0")] #[inline] @@ -460,7 +460,7 @@ impl Duration { /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2))); - /// assert_eq!(Duration::new(std::u64::MAX - 1, 0).checked_mul(2), None); + /// assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None); /// ``` #[stable(feature = "duration_checked_ops", since = "1.16.0")] #[inline] diff --git a/src/libcore/unicode/mod.rs b/src/libcore/unicode/mod.rs index b6eaf06aa7f63..28c07f7717046 100644 --- a/src/libcore/unicode/mod.rs +++ b/src/libcore/unicode/mod.rs @@ -3,19 +3,19 @@ pub(crate) mod printable; mod unicode_data; -pub(crate) mod version; - -use version::UnicodeVersion; /// The version of [Unicode](http://www.unicode.org/) that the Unicode parts of /// `char` and `str` methods are based on. -#[unstable(feature = "unicode_version", issue = "49726")] -pub const UNICODE_VERSION: UnicodeVersion = UnicodeVersion { - major: unicode_data::UNICODE_VERSION.0, - minor: unicode_data::UNICODE_VERSION.1, - micro: unicode_data::UNICODE_VERSION.2, - _priv: (), -}; +/// +/// New versions of Unicode are released regularly and subsequently all methods +/// in the standard library depending on Unicode are updated. Therefore the +/// behavior of some `char` and `str` methods and the value of this constant +/// changes over time. This is *not* considered to be a breaking change. +/// +/// The version numbering scheme is explained in +/// [Unicode 11.0 or later, Section 3.1 Versions of the Unicode Standard](https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf#page=4). +#[stable(feature = "unicode_version", since = "1.45.0")] +pub const UNICODE_VERSION: (u8, u8, u8) = unicode_data::UNICODE_VERSION; // For use in liballoc, not re-exported in libstd. pub mod derived_property { @@ -32,28 +32,3 @@ pub use unicode_data::lowercase::lookup as Lowercase; pub use unicode_data::n::lookup as N; pub use unicode_data::uppercase::lookup as Uppercase; pub use unicode_data::white_space::lookup as White_Space; - -#[inline(always)] -fn range_search( - needle: u32, - chunk_idx_map: &[u8; N], - (last_chunk_idx, last_chunk_mapping): (u16, u8), - bitset_chunk_idx: &[[u8; 16]; N1], - bitset: &[u64; N2], -) -> bool { - let bucket_idx = (needle / 64) as usize; - let chunk_map_idx = bucket_idx / 16; - let chunk_piece = bucket_idx % 16; - let chunk_idx = if chunk_map_idx >= N { - if chunk_map_idx == last_chunk_idx as usize { - last_chunk_mapping - } else { - return false; - } - } else { - chunk_idx_map[chunk_map_idx] - }; - let idx = bitset_chunk_idx[(chunk_idx as usize)][chunk_piece]; - let word = bitset[(idx as usize)]; - (word & (1 << (needle % 64) as u64)) != 0 -} diff --git a/src/libcore/unicode/unicode_data.rs b/src/libcore/unicode/unicode_data.rs index 3e90028613ce1..9c92a8ba28ae4 100644 --- a/src/libcore/unicode/unicode_data.rs +++ b/src/libcore/unicode/unicode_data.rs @@ -1,618 +1,547 @@ ///! This file is generated by src/tools/unicode-table-generator; do not edit manually! -use super::range_search; -pub const UNICODE_VERSION: (u32, u32, u32) = (13, 0, 0); +#[inline(always)] +fn bitset_search< + const N: usize, + const CHUNK_SIZE: usize, + const N1: usize, + const CANONICAL: usize, + const CANONICALIZED: usize, +>( + needle: u32, + chunk_idx_map: &[u8; N], + bitset_chunk_idx: &[[u8; CHUNK_SIZE]; N1], + bitset_canonical: &[u64; CANONICAL], + bitset_canonicalized: &[(u8, u8); CANONICALIZED], +) -> bool { + let bucket_idx = (needle / 64) as usize; + let chunk_map_idx = bucket_idx / CHUNK_SIZE; + let chunk_piece = bucket_idx % CHUNK_SIZE; + let chunk_idx = if let Some(&v) = chunk_idx_map.get(chunk_map_idx) { + v + } else { + return false; + }; + let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize; + let word = if let Some(word) = bitset_canonical.get(idx) { + *word + } else { + let (real_idx, mapping) = bitset_canonicalized[idx - bitset_canonical.len()]; + let mut word = bitset_canonical[real_idx as usize]; + let should_invert = mapping & (1 << 6) != 0; + if should_invert { + word = !word; + } + // Lower 6 bits + let quantity = mapping & ((1 << 6) - 1); + if mapping & (1 << 7) != 0 { + // shift + word >>= quantity as u64; + } else { + word = word.rotate_left(quantity as u32); + } + word + }; + (word & (1 << (needle % 64) as u64)) != 0 +} + +fn decode_prefix_sum(short_offset_run_header: u32) -> u32 { + short_offset_run_header & ((1 << 21) - 1) +} + +fn decode_length(short_offset_run_header: u32) -> usize { + (short_offset_run_header >> 21) as usize +} + +#[inline(always)] +fn skip_search( + needle: u32, + short_offset_runs: &[u32; 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 last_idx = + match short_offset_runs.binary_search_by_key(&(needle << 11), |header| header << 11) { + Ok(idx) => idx + 1, + Err(idx) => idx, + }; + + let mut offset_idx = decode_length(short_offset_runs[last_idx]); + let length = if let Some(next) = short_offset_runs.get(last_idx + 1) { + decode_length(*next) - 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); + + let total = needle - prev; + let mut prefix_sum = 0; + for _ in 0..(length - 1) { + let offset = offsets[offset_idx]; + prefix_sum += offset as u32; + if prefix_sum > total { + break; + } + offset_idx += 1; + } + offset_idx % 2 == 1 +} + +pub const UNICODE_VERSION: (u8, u8, u8) = (13, 0, 0); #[rustfmt::skip] pub mod alphabetic { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (196, 44); - static BITSET_CHUNKS_MAP: [u8; 196] = [ - 6, 32, 10, 18, 19, 23, 21, 12, 7, 5, 0, 20, 14, 50, 50, 50, 50, 50, 50, 37, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 50, 30, 8, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 46, 0, 0, 0, 0, 0, 0, 0, 0, 4, 36, 17, 31, 16, 25, 24, 26, 13, 15, - 45, 27, 0, 0, 50, 11, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 39, 1, 50, 50, 50, 50, 50, 48, - 50, 34, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 28, 0, 0, 0, 0, 0, 29, 0, 0, 9, 0, 33, 2, 3, 0, 0, - 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 42, 50, 50, 50, - 43, 22, 50, 50, 50, 50, 41, 50, 50, 50, 50, 50, 50, 47, 0, 0, 0, 38, 0, 50, 50, 50, 50, + static SHORT_OFFSET_RUNS: [u32; 52] = [ + 706, 33559113, 868226669, 947920662, 1157637302, 1306536960, 1310732293, 1398813696, + 1449151936, 1451270141, 1455465613, 1459660301, 1468061604, 1648425216, 1658911342, + 1661009214, 1707147904, 1793132343, 1853951616, 1994464256, 2330009312, 2418090906, + 2428579840, 2439066671, 2441167872, 2443265607, 2445371392, 2447469113, 2449567296, + 2476836856, 2508295382, 2512498688, 2518790431, 2520888060, 2533473280, 2535576576, + 2556548774, 2634145792, 2682380992, 2715936768, 2720132608, 2736910640, 2875326464, + 2887952094, 2890053429, 2894253730, 2902649825, 2906847232, 2908944926, 2911043584, + 2913145675, 2916356939, ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 51] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 0, 0, 254, 247, 39, 68], - [0, 0, 0, 0, 0, 0, 0, 0, 111, 135, 113, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 195, 205, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 254, 254, 254, 254, 254, 210, 254, 25, 136, 251, 71, 243], - [0, 0, 182, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 107, 103, 180, 254, 254, 254, 254, 254, 254, 254, 61, 0, 155, 222, 181], - [0, 148, 30, 0, 172, 226, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [48, 80, 254, 169, 206, 123, 189, 139, 95, 179, 145, 86, 211, 204, 254, 56], - [53, 0, 0, 0, 129, 17, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0], - [59, 54, 185, 203, 171, 191, 161, 117, 158, 87, 164, 118, 162, 67, 159, 23], - [62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [95, 131, 168, 105, 254, 254, 254, 82, 254, 254, 254, 254, 236, 130, 137, 120], - [101, 0, 225, 146, 151, 2, 217, 45, 144, 246, 32, 101, 0, 0, 0, 0], - [119, 253, 224, 175, 193, 254, 227, 195, 0, 0, 0, 0, 0, 0, 0, 0], - [143, 190, 91, 0, 153, 218, 24, 0, 0, 0, 0, 92, 0, 0, 66, 0], - [150, 94, 37, 85, 102, 0, 157, 0, 88, 122, 31, 46, 89, 74, 20, 0], - [154, 34, 254, 110, 0, 84, 0, 0, 0, 0, 233, 19, 216, 108, 237, 21], - [166, 42, 165, 72, 167, 177, 126, 76, 109, 16, 127, 38, 1, 192, 124, 0], - [176, 246, 234, 174, 254, 254, 254, 254, 254, 235, 140, 241, 240, 26, 228, 128], - [213, 239, 254, 77, 209, 64, 142, 238, 63, 0, 0, 0, 0, 0, 0, 0], - [225, 101, 207, 89, 98, 81, 208, 10, 232, 83, 147, 1, 188, 13, 178, 70], - [237, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254], - [253, 254, 254, 254, 254, 254, 254, 254, 254, 214, 231, 99, 79, 78, 183, 27], - [254, 6, 100, 50, 75, 90, 254, 28, 134, 0, 202, 51, 163, 43, 0, 0], - [254, 9, 75, 75, 49, 0, 0, 0, 0, 0, 69, 0, 199, 6, 195, 93], - [254, 41, 254, 8, 0, 0, 141, 33, 145, 4, 97, 0, 55, 0, 0, 0], - [254, 62, 254, 254, 254, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [254, 121, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [254, 242, 170, 252, 138, 245, 254, 254, 254, 254, 220, 173, 186, 212, 219, 14], - [254, 254, 15, 132, 254, 254, 254, 254, 57, 149, 254, 65, 223, 254, 249, 187], - [254, 254, 196, 114, 201, 44, 0, 0, 254, 254, 254, 254, 95, 47, 0, 0], - [254, 254, 250, 254, 194, 229, 156, 73, 230, 215, 254, 152, 246, 248, 71, 104], - [254, 254, 254, 5, 254, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [254, 254, 254, 22, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [254, 254, 254, 254, 37, 200, 254, 254, 254, 254, 254, 116, 0, 0, 0, 0], - [254, 254, 254, 254, 133, 246, 244, 112, 0, 184, 254, 125, 106, 221, 145, 29], - [254, 254, 254, 254, 254, 254, 254, 0, 254, 254, 254, 254, 254, 254, 254, 254], - [254, 254, 254, 254, 254, 254, 254, 254, 35, 0, 0, 0, 0, 0, 0, 0], - [254, 254, 254, 254, 254, 254, 254, 254, 101, 37, 0, 60, 65, 160, 18, 0], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 7, 0, 0, 0, 0, 0, 0], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 197, 254, 254, 254, 254, 254], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 35, 254, 254, 254, 254], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 84, 254, 254, 254], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 11, 0, 0], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 25, 0], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 198, 115], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 40], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 96], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 125], - [254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254], + static OFFSETS: [u8; 1391] = [ + 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, 42, + 5, 1, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 2, 1, 6, 41, 39, + 14, 1, 1, 1, 2, 1, 2, 1, 1, 8, 27, 4, 4, 29, 11, 5, 56, 1, 7, 14, 102, 1, 8, 4, 8, 4, 3, 10, + 3, 2, 1, 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 53, 21, 1, 18, + 12, 12, 3, 7, 6, 76, 1, 16, 1, 3, 4, 15, 13, 19, 1, 8, 2, 2, 2, 22, 1, 7, 1, 1, 3, 4, 3, 8, + 2, 2, 2, 2, 1, 1, 8, 1, 4, 2, 1, 5, 12, 2, 10, 1, 4, 3, 1, 6, 4, 2, 2, 22, 1, 7, 1, 2, 1, 2, + 1, 2, 4, 5, 4, 2, 2, 2, 4, 1, 7, 4, 1, 1, 17, 6, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, 1, 2, 1, 5, + 3, 9, 1, 3, 1, 2, 3, 1, 15, 4, 21, 4, 4, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, + 2, 2, 9, 2, 4, 2, 1, 5, 13, 1, 16, 2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, 3, 3, 3, 12, + 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 4, 1, 8, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, 2, 1, 3, + 5, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 7, 1, 1, 4, 13, 2, 13, + 13, 1, 3, 1, 41, 2, 8, 1, 3, 1, 3, 1, 1, 5, 4, 7, 5, 22, 6, 1, 3, 1, 18, 3, 24, 1, 9, 1, 1, + 2, 7, 8, 6, 1, 1, 1, 8, 18, 2, 13, 58, 5, 7, 6, 1, 51, 2, 1, 1, 1, 5, 1, 24, 1, 1, 1, 19, 1, + 3, 2, 5, 1, 1, 6, 1, 14, 4, 32, 1, 63, 8, 1, 36, 4, 17, 6, 16, 1, 36, 67, 55, 1, 1, 2, 5, + 16, 64, 10, 4, 2, 38, 1, 1, 5, 1, 2, 43, 1, 0, 1, 4, 2, 7, 1, 1, 1, 4, 2, 41, 1, 4, 2, 33, + 1, 4, 2, 7, 1, 1, 1, 4, 2, 15, 1, 57, 1, 4, 2, 67, 37, 16, 16, 86, 2, 6, 3, 0, 2, 17, 1, 26, + 5, 75, 3, 11, 7, 13, 1, 6, 12, 20, 12, 20, 12, 13, 1, 3, 1, 2, 12, 52, 2, 19, 14, 1, 4, 1, + 67, 89, 7, 43, 5, 70, 10, 31, 1, 12, 4, 9, 23, 30, 2, 5, 11, 44, 4, 26, 54, 28, 4, 63, 2, + 20, 50, 1, 23, 2, 63, 52, 1, 15, 1, 7, 52, 42, 2, 4, 10, 44, 1, 11, 14, 55, 22, 3, 10, 36, + 2, 9, 7, 43, 2, 3, 41, 4, 1, 6, 1, 2, 3, 1, 5, 192, 39, 14, 11, 0, 2, 6, 2, 38, 2, 6, 2, 8, + 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, 5, 3, 1, 7, 116, + 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, 1, 11, 2, 4, 5, + 5, 4, 1, 17, 41, 0, 52, 0, 47, 1, 47, 1, 133, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 2, 56, 7, 1, + 16, 23, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 32, 47, 1, 0, 3, 25, 9, 7, 5, 2, + 5, 4, 86, 6, 3, 1, 90, 1, 4, 5, 43, 1, 94, 17, 32, 48, 16, 0, 0, 64, 0, 3, 0, 67, 46, 2, 0, + 3, 16, 10, 2, 20, 47, 5, 8, 3, 113, 39, 9, 2, 103, 2, 53, 2, 9, 42, 17, 1, 33, 24, 52, 12, + 68, 1, 1, 44, 6, 3, 1, 1, 3, 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, 1, + 55, 9, 14, 18, 23, 3, 69, 1, 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, 1, + 43, 1, 14, 6, 123, 21, 0, 12, 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, 1, + 1, 1, 2, 1, 2, 1, 108, 33, 0, 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, 89, + 3, 6, 2, 6, 2, 6, 2, 3, 35, 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, 29, 3, + 49, 47, 32, 13, 30, 5, 43, 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, 8, 52, + 156, 0, 9, 22, 10, 8, 152, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, + 10, 22, 10, 26, 70, 56, 6, 2, 64, 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, + 27, 54, 10, 22, 10, 19, 13, 18, 110, 73, 55, 51, 13, 51, 13, 40, 0, 42, 1, 2, 3, 2, 78, 29, + 10, 1, 8, 22, 106, 21, 27, 23, 9, 70, 60, 55, 23, 25, 23, 51, 17, 4, 8, 35, 3, 1, 9, 64, 1, + 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, 1, 65, 7, 1, 1, 1, 4, 1, 15, 1, 10, 7, 57, 23, + 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, 3, 1, 6, 1, 5, 7, 156, 66, 1, 3, + 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, 3, 1, 59, 54, 2, 1, 71, + 27, 2, 14, 213, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, 1, 2, 2, 2, 2, 4, 93, 8, 2, 46, + 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 34, 57, 0, 9, 1, 45, 1, 7, 1, 1, 49, 30, 2, + 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, 1, 2, 2, 24, 6, 1, 2, 1, 37, 1, 2, 1, 4, + 1, 1, 0, 23, 185, 1, 79, 0, 102, 111, 17, 196, 0, 0, 0, 0, 0, 0, 7, 31, 113, 30, 18, 48, 16, + 4, 31, 21, 5, 19, 0, 64, 128, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 2, 14, 0, 8, 0, 42, 9, 0, + 0, 49, 3, 17, 4, 8, 0, 0, 107, 5, 13, 3, 9, 7, 10, 4, 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, + 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, + 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 7, 1, 17, 2, 7, 1, + 2, 1, 5, 213, 45, 10, 7, 16, 1, 0, 44, 0, 197, 59, 68, 3, 1, 3, 1, 0, 4, 1, 27, 1, 2, 1, 1, + 2, 1, 1, 10, 1, 4, 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, 10, 1, 17, 5, 3, 1, 5, 1, 17, 0, + 26, 6, 26, 6, 26, 0, 0, 34, 0, 11, 222, 2, 0, 14, 0, 0, 0, 0, 0, 0, ]; - static BITSET: [u64; 255] = [ - 0, 1, 7, 15, 17, 31, 63, 127, 179, 511, 1023, 2047, 2191, 4079, 4087, 8191, 8319, 16384, - 65535, 131071, 262143, 4128527, 4194303, 8461767, 24870911, 67108863, 134217727, 276824575, - 335593502, 486341884, 536805376, 536870911, 553648127, 1056964608, 1073692671, 1073741823, - 1140785663, 2147483647, 4026540127, 4294934783, 8589934591, 15032387515, 64548249055, - 68191066527, 68719476735, 115913785343, 137438953215, 1095220854783, 1099511627711, - 1099511627775, 2199023190016, 2199023255551, 4398046511103, 8641373536127, 8791831609343, - 8795690369023, 8796093022207, 13198434443263, 17592186044415, 35184321757183, - 70368744112128, 88094074470339, 140737488355327, 140737488355328, 141836999983103, - 281474976710655, 281474976710656, 563017343310239, 844472174772224, 875211255709695, - 1125625028935679, 1125899906842623, 1688915364814303, 2119858418286774, 2251795522912255, - 2251799813685247, 3377704004976767, 3509778554814463, 3905461007941631, 4503595333443583, - 4503599627370495, 8796093022142464, 9006649498927104, 9007192812290047, 9007199254740991, - 15762594400829440, 17169970223906821, 17732925109967239, 18014398491652207, - 18014398509481983, 20266198323101936, 36027697507139583, 36028792723996672, - 36028792723996703, 36028792728190975, 36028797018963967, 72057594037927935, - 90071992547409919, 143851303137705983, 144053615424700415, 144115188075855868, - 144115188075855871, 288230371860938751, 297241973452963840, 301749971126844416, - 319718190147960832, 576460743713488896, 576460743847706622, 576460752303359999, - 576460752303423486, 576460752303423487, 790380184120328175, 1152640029630136575, - 1152917029519358975, 1152921504591118335, 1152921504606845055, 1152921504606846975, - 1153765996922689951, 2161727885562420159, 2251241253188403424, 2295745090394464220, - 2305570330330005503, 2305843004918726656, 2305843004919250943, 2305843009196916483, - 2305843009213693951, 3457638613854978030, 4323455298678290390, 4557642822898941951, - 4575692405780512767, 4611686017001275199, 4611686018360336384, 4611686018427322368, - 4611686018427387903, 4656722014700830719, 6843210385291930244, 6881498031078244479, - 6908521828386340863, 8935141660164089791, 8935423131384840192, 9168765891372858879, - 9169328841326329855, 9187201948305063935, 9187343239835811327, 9216616637413720063, - 9223372036854775807, 9223372041149743103, 9223372586610589696, 9223934986808197120, - 10371930679322607615, 10502394331027995967, 11078855083321979519, 11241233151490523135, - 13006395723845991295, 13258596753222922239, 13609596598936928288, 13834776580305453567, - 13907115649320091647, 14082190885810440174, 14123225865944680428, 16212958624174047247, - 16412803692974677999, 16424062692043104238, 16424062692043104239, 16424062692043243502, - 16424625641996804079, 16429129241624174575, 16717361816799141887, 16717361816799216127, - 16788293510930366511, 17005555242810474495, 17293822569102704639, 17581979622616071300, - 17870283321271910397, 17870283321406070975, 17870283321406128127, 17978369712463020031, - 18158513764145585631, 18158781978395017215, 18194542490281852927, 18410715276682199039, - 18428729675200069631, 18428729675200069632, 18433233274827440127, 18437455399478099968, - 18437736870159843328, 18437736874452713471, 18437736874454812668, 18442240474082181119, - 18444492273895866367, 18445618173802708993, 18446181192473632767, 18446216308128218879, - 18446462598732840928, 18446462598732840959, 18446462598732840960, 18446462599806582783, - 18446462615912710143, 18446462667452317695, 18446463149025525759, 18446463629525450752, - 18446463698244468735, 18446464796682337663, 18446466966713532671, 18446466996645134335, - 18446466996779287551, 18446471394825862144, 18446471394825863167, 18446480190918885375, - 18446498607738650623, 18446532967477018623, 18446602782178705022, 18446603336221163519, - 18446603336221196287, 18446638520593285119, 18446673709243564031, 18446708893632430079, - 18446740770879700992, 18446741595513422027, 18446741874686295551, 18446743249075830783, - 18446743798965862398, 18446744056529672000, 18446744060816261120, 18446744068886102015, - 18446744069414584320, 18446744069414601696, 18446744069414617087, 18446744069414649855, - 18446744069456527359, 18446744069548736512, 18446744069548802046, 18446744069683019775, - 18446744069951455231, 18446744070421282815, 18446744070446333439, 18446744070475743231, - 18446744070488326143, 18446744071553646463, 18446744071562067967, 18446744073696837631, - 18446744073701162813, 18446744073707454463, 18446744073709027328, 18446744073709355007, - 18446744073709419615, 18446744073709486080, 18446744073709520895, 18446744073709543424, - 18446744073709550079, 18446744073709550595, 18446744073709551579, 18446744073709551599, - 18446744073709551614, 18446744073709551615, - ]; - pub fn lookup(c: char) -> bool { - super::range_search( + super::skip_search( c as u32, - &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, - &BITSET_INDEX_CHUNKS, - &BITSET, + &SHORT_OFFSET_RUNS, + &OFFSETS, ) } } #[rustfmt::skip] pub mod case_ignorable { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (896, 33); - static BITSET_CHUNKS_MAP: [u8; 125] = [ - 25, 14, 21, 30, 28, 4, 17, 23, 22, 0, 0, 16, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 13, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, 6, 9, 0, 7, 11, 32, 31, 26, 29, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, - 10, 0, 8, 0, 19, 0, 12, 0, 1, - ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 34] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 47, 57], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 173, 3], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 90, 136, 38], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 104, 7, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 78, 27, 0, 148, 138, 81, 44, 119], - [0, 0, 0, 0, 0, 0, 0, 0, 154, 0, 0, 58, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 167, 99, 77, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 130, 0, 0, 0, 48, 0, 116, 0, 0], - [0, 0, 0, 0, 0, 172, 70, 0, 0, 8, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 67, 0, 0, 24, 0, 0], - [0, 0, 0, 29, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 135, 0, 0, 0, 0, 16, 162, 46, 86, 51, 80, 13, 111], - [0, 0, 12, 0, 0, 43, 163, 92, 35, 82, 0, 71, 175, 14, 83, 131], - [0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 133, 0, 87, 0, 150, 0, 178, 75, 0, 0, 0, 0, 0, 0, 0], - [20, 5, 61, 0, 120, 0, 0, 0, 32, 156, 176, 1, 126, 91, 69, 88], - [26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [62, 0, 0, 0, 137, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, 0], - [66, 0, 0, 152, 72, 25, 134, 59, 102, 124, 165, 101, 0, 64, 0, 68], - [73, 33, 0, 181, 125, 85, 122, 139, 123, 100, 123, 169, 155, 54, 4, 18], - [74, 151, 36, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [106, 135, 0, 112, 177, 107, 180, 168, 0, 0, 0, 0, 0, 0, 157, 142], - [109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [113, 50, 108, 0, 0, 0, 0, 0, 0, 0, 174, 182, 182, 114, 10, 0], - [115, 0, 0, 0, 141, 5, 0, 49, 145, 34, 31, 0, 0, 0, 0, 0], - [118, 0, 42, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [143, 95, 37, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0], - [161, 0, 103, 0, 160, 11, 30, 0, 0, 0, 0, 93, 0, 0, 0, 0], - [164, 55, 155, 53, 127, 52, 2, 28, 117, 21, 128, 19, 110, 147, 129, 9], - [170, 41, 153, 6, 0, 0, 159, 39, 158, 1, 105, 0, 65, 0, 0, 0], - [171, 149, 132, 17, 98, 89, 146, 23, 140, 0, 0, 63, 127, 97, 0, 0], - [179, 182, 0, 0, 182, 182, 182, 79, 0, 0, 0, 0, 0, 0, 0, 0], + static SHORT_OFFSET_RUNS: [u32; 32] = [ + 688, 44045149, 555751186, 559947709, 794831996, 866136069, 891330581, 916497656, 920692236, + 924908318, 1122041344, 1130430973, 1193347585, 1205931300, 1231097515, 1235294255, + 1445009723, 1453399088, 1512120051, 1575040048, 1579248368, 1583443791, 1596046493, + 1612829031, 1621219840, 1642192896, 1667359024, 1688330988, 1692526800, 1696723963, + 1705902081, 1711210992, ]; - static BITSET: [u64; 183] = [ - 0, 1, 2, 3, 4, 8, 13, 15, 28, 64, 176, 191, 1016, 1792, 2047, 4080, 4096, 8192, 8193, - 16192, 30720, 32704, 32768, 40448, 131008, 262016, 2097152, 2359296, 6030336, 8323072, - 10682368, 58719232, 159383552, 234881024, 243138688, 402587711, 536805376, 536879204, - 546307648, 805306369, 1073741824, 1073741916, 2113929216, 2181038080, 3221225472, - 3758096384, 4026531840, 4294934528, 4294967296, 4512022528, 5368709120, 17179869183, - 51539615774, 51539619904, 51545907230, 51545914817, 66035122176, 115964116992, 412316860416, - 412316893184, 1030792151040, 2199023255648, 8641373536127, 8763880767488, 15397323538432, - 17303886364672, 18004502906948, 26388279066624, 36421322670080, 65128884076547, - 65970697670631, 68168642985984, 70093866270720, 70368739983360, 136957967529984, - 140737488355328, 263882790666240, 281470547525648, 281470682333183, 281474976710655, - 281474976710656, 281474976710657, 281479271675905, 562675075514368, 562949953355776, - 563001509683710, 844424930131968, 985162418487296, 1023920203366400, 2251799813685248, - 3377699721314304, 4494803534348292, 4503599627370678, 6755399441055744, 7881299349733376, - 8444256867844096, 8725724278030336, 8760633772212225, 8989057312882695, 9042383626829823, - 9851624185018758, 24822575045541890, 28848986089586688, 30958948903026688, - 35747322042253312, 53805701016846336, 58529202969772032, 72066390130950143, - 112767012056334336, 143833713099145216, 189151184399892480, 216172782113783808, - 220713756545974272, 288301294651703296, 302022650010533887, 504262420777140224, - 558446353793941504, 572520102629474304, 593978171557150752, 1008806350890729472, - 1009933895770046464, 1152921504606846976, 1152921504606846978, 1152921504606846982, - 1153202979583561736, 1441151880758558727, 1715871458028158991, 1729382256910270467, - 2301902359539744768, 2305843009196908767, 2305843009213693952, 2612078987781865472, - 2771965570646540291, 3458764513820540928, 3731232291276455943, 4539628424389459968, - 4589168020290535424, 4611404543450677248, 4611686018494513280, 4611686069967003678, - 4671217976001691648, 6341068275337658368, 6917775322003857411, 7421334051581067264, - 8070450532247928832, 8788774672813524990, 9205357638345293827, 9222809086901354496, - 9223372036854775808, 9223372036854775935, 9223512774343131136, 9224216320050987008, - 9224497932466651184, 9653465801268658176, 9727775195120332910, 10376293541461622786, - 11526998316797657088, 11529215046068469760, 12103423998558208000, 12699025049277956096, - 13005832773892571136, 13798747783286489088, 13832665517980123136, 13835058055282032640, - 13835058055282163729, 13951307220663664640, 17870283321406128128, 17906312118425092095, - 18158513697557839871, 18158513749097456062, 18374686479671623680, 18374686479671623682, - 18444496122186563584, 18445618173802708992, 18446462598732840960, 18446462598733004800, - 18446463148488654848, 18446726481523507200, 18446744069414584320, 18446744069414584322, - 18446744073575333888, 18446744073709027328, 18446744073709551615, + static OFFSETS: [u8; 821] = [ + 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, + 1, 1, 251, 7, 207, 1, 5, 1, 49, 45, 1, 1, 1, 2, 1, 2, 1, 1, 44, 1, 11, 6, 10, 11, 1, 1, 35, + 1, 10, 21, 16, 1, 101, 8, 1, 10, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 11, 4, 1, 2, 1, 24, + 24, 43, 3, 119, 48, 55, 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 13, 1, 15, 1, 58, 1, 4, 4, 8, 1, + 20, 2, 26, 1, 2, 2, 57, 1, 4, 2, 4, 2, 2, 3, 3, 1, 30, 2, 3, 1, 11, 2, 57, 1, 4, 5, 1, 2, 4, + 1, 20, 2, 22, 6, 1, 1, 58, 1, 2, 1, 1, 4, 8, 1, 7, 2, 11, 2, 30, 1, 61, 1, 12, 1, 50, 1, 3, + 1, 57, 3, 5, 3, 1, 4, 7, 2, 11, 2, 29, 1, 58, 1, 2, 1, 6, 1, 5, 2, 20, 2, 28, 2, 57, 2, 4, + 4, 8, 1, 20, 2, 29, 1, 72, 1, 7, 3, 1, 1, 90, 1, 2, 7, 11, 9, 98, 1, 2, 9, 9, 1, 1, 6, 74, + 2, 27, 1, 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, 6, 1, 2, 2, 2, 25, + 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 94, 1, 0, 3, 0, 3, 29, 3, 29, 2, 30, 2, 64, 2, 1, + 7, 8, 1, 2, 11, 3, 1, 5, 1, 45, 4, 52, 1, 65, 2, 34, 1, 118, 3, 4, 2, 9, 1, 6, 3, 219, 2, 2, + 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 17, 63, 4, 48, 1, 1, 5, 1, 1, 5, 1, + 40, 9, 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, + 1, 7, 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 95, 1, 5, 0, 1, 1, 3, 11, 3, 13, 3, 13, 3, 13, 2, + 12, 5, 8, 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, 16, 13, 51, 33, 0, 2, 113, 3, + 125, 1, 15, 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, 93, 3, 0, 1, 0, 6, 0, 1, 98, + 4, 1, 10, 1, 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 109, 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, + 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, + 2, 2, 12, 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, + 100, 5, 9, 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 16, 0, 16, 3, 1, 12, 16, 34, 1, 2, 1, 169, 1, + 7, 1, 6, 1, 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, 0, 1, 226, 1, 149, 5, 0, 3, 1, + 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 0, 2, 153, 11, 176, 1, 54, 15, 56, 3, 49, 4, 2, 2, 2, 1, + 15, 1, 50, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 160, + 1, 3, 8, 21, 2, 57, 2, 3, 1, 37, 7, 3, 5, 195, 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, 1, 4, 2, 1, + 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, 1, 101, 3, 2, 4, 1, + 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, + 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, + 72, 2, 3, 1, 1, 1, 0, 2, 0, 9, 0, 5, 59, 7, 9, 4, 0, 1, 63, 17, 64, 2, 1, 2, 0, 2, 1, 4, 0, + 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, 7, 1, 17, 2, 7, + 1, 2, 1, 5, 0, 14, 0, 4, 0, 7, 109, 8, 0, 5, 0, 1, 30, 96, 128, 240, 0, ]; - pub fn lookup(c: char) -> bool { - super::range_search( + super::skip_search( c as u32, - &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, - &BITSET_INDEX_CHUNKS, - &BITSET, + &SHORT_OFFSET_RUNS, + &OFFSETS, ) } } #[rustfmt::skip] pub mod cased { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (124, 6); - static BITSET_CHUNKS_MAP: [u8; 123] = [ - 13, 18, 0, 0, 12, 0, 0, 9, 14, 10, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 0, 16, 0, 8, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, - 0, 0, 0, 7, + static SHORT_OFFSET_RUNS: [u32; 19] = [ + 4256, 115348384, 136322176, 144711446, 163587254, 320875520, 325101120, 358656816, + 392231680, 404815649, 413205504, 421596288, 434182304, 442592832, 446813184, 451008166, + 528607488, 576844080, 582152586, ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 19] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 8, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 43, 62, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 10, 0, 50, 62, 58, 20], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 42, 44, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 62, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 31, 0, 62, 62, 62, 0, 62, 62, 62, 62, 54, 26, 27, 24], - [0, 0, 39, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 51, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 25], - [0, 22, 19, 37, 62, 62, 36, 61, 62, 62, 18, 12, 0, 30, 49, 38], - [0, 29, 9, 0, 34, 52, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [46, 55, 62, 17, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [62, 6, 42, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [62, 56, 33, 60, 28, 57, 62, 62, 62, 62, 48, 35, 40, 45, 47, 5], - [62, 62, 59, 62, 41, 53, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0], - ]; - static BITSET: [u64; 63] = [ - 0, 15, 24, 511, 1023, 4087, 65535, 16253055, 134217726, 536805376, 1073741823, 4294967295, - 133143986179, 4398046511103, 36009005809663, 70368744177663, 2251799813685247, - 3509778554814463, 144115188074807295, 297241973452963840, 531424756029720572, - 576460743713488896, 576460743847706622, 1152921504591118335, 2295745090394464220, - 4557642822898941951, 4611686017001275199, 6908521828386340863, 8935141660164089791, - 9223934986808197120, 13605092999309557792, 16717361816799216127, 16717361816799223999, - 17005555242810474495, 17446871633794956420, 17870283321271910397, 17870283321406128127, - 18410715276682199039, 18428729675200069631, 18428729675200069632, 18437736874452713471, - 18446462598732840959, 18446462598732840960, 18446464797621878783, 18446466996779287551, - 18446603336221163519, 18446603336221196287, 18446741874686295551, 18446743249075830783, - 18446744056529672000, 18446744056529682432, 18446744069414584320, 18446744069414601696, - 18446744069422972927, 18446744070475743231, 18446744071562067967, 18446744073707454463, - 18446744073709419615, 18446744073709517055, 18446744073709550595, 18446744073709551599, - 18446744073709551600, 18446744073709551615, + static OFFSETS: [u8; 283] = [ + 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, + 96, 1, 42, 4, 2, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 9, + 41, 0, 38, 1, 1, 5, 1, 2, 43, 2, 3, 0, 86, 2, 6, 0, 9, 7, 43, 2, 3, 64, 192, 64, 0, 2, 6, 2, + 38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, + 5, 3, 1, 7, 116, 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, + 1, 6, 4, 1, 2, 4, 5, 5, 4, 1, 17, 32, 3, 2, 0, 52, 0, 47, 1, 47, 1, 133, 6, 4, 3, 2, 12, 38, + 1, 1, 5, 1, 0, 46, 18, 30, 132, 102, 3, 4, 1, 48, 2, 9, 42, 2, 1, 3, 0, 43, 1, 13, 7, 80, 0, + 7, 12, 5, 0, 26, 6, 26, 0, 80, 96, 36, 4, 36, 0, 51, 13, 51, 0, 64, 0, 64, 0, 85, 1, 71, 1, + 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, + 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 68, + 0, 26, 6, 26, 6, 26, 0, ]; - pub fn lookup(c: char) -> bool { - super::range_search( + super::skip_search( c as u32, - &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, - &BITSET_INDEX_CHUNKS, - &BITSET, + &SHORT_OFFSET_RUNS, + &OFFSETS, ) } } #[rustfmt::skip] pub mod cc { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (0, 0); - static BITSET_CHUNKS_MAP: [u8; 0] = [ + static SHORT_OFFSET_RUNS: [u32; 1] = [ + 1114272, ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 1] = [ - [1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + static OFFSETS: [u8; 5] = [ + 0, 32, 95, 33, 0, ]; - static BITSET: [u64; 3] = [ - 0, 4294967295, 9223372036854775808, - ]; - pub fn lookup(c: char) -> bool { - super::range_search( + super::skip_search( c as u32, - &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, - &BITSET_INDEX_CHUNKS, - &BITSET, + &SHORT_OFFSET_RUNS, + &OFFSETS, ) } } #[rustfmt::skip] pub mod grapheme_extend { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (896, 30); - static BITSET_CHUNKS_MAP: [u8; 123] = [ - 4, 15, 21, 27, 25, 3, 18, 23, 17, 0, 0, 14, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 7, 10, 0, 8, 12, 29, 28, 24, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, - 11, 0, 9, 0, 19, 0, 13, + static SHORT_OFFSET_RUNS: [u32; 31] = [ + 768, 2098307, 6292881, 10490717, 513808146, 518004748, 723528943, 731918378, 744531567, + 752920578, 769719070, 899743232, 903937950, 912327165, 916523521, 929107236, 954273451, + 958470191, 1180769328, 1252073203, 1315007216, 1319202639, 1327611037, 1340199269, + 1344395776, 1373757440, 1398923568, 1419895532, 1424091344, 1429078048, 1438581232, ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 31] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 20, 46], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 74, 106, 31], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 66, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 87, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 37, 70, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 37, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 48, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 134, 82, 64, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 103, 0, 0, 0, 39, 0, 94, 0, 0], - [0, 0, 0, 0, 0, 133, 58, 0, 0, 5, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 55, 0, 0, 18, 0, 0], - [0, 0, 0, 21, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 71, 0, 118, 0, 142, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 9, 0, 0, 0, 129, 7, 26, 67, 0, 59, 140, 11, 68, 104], - [0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [12, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [13, 0, 50, 0, 96, 0, 0, 0, 27, 123, 139, 1, 100, 75, 57, 72], - [51, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0], - [54, 0, 0, 120, 61, 19, 105, 47, 85, 98, 131, 84, 0, 0, 0, 56], - [60, 28, 0, 141, 99, 45, 111, 109, 97, 83, 97, 136, 132, 44, 108, 22], - [63, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [89, 0, 0, 91, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 0, 0], - [93, 0, 0, 0, 113, 3, 0, 40, 115, 29, 24, 0, 0, 0, 0, 0], - [114, 78, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0], - [128, 0, 86, 0, 127, 8, 23, 0, 0, 0, 0, 76, 0, 0, 0, 0], - [130, 42, 122, 41, 112, 43, 2, 36, 95, 15, 101, 14, 90, 117, 102, 6], - [137, 34, 124, 4, 0, 0, 126, 32, 125, 1, 88, 0, 53, 0, 0, 0], - [138, 119, 92, 0, 81, 73, 116, 17, 110, 0, 0, 52, 112, 80, 0, 0], - [142, 143, 0, 0, 143, 143, 143, 66, 0, 0, 0, 0, 0, 0, 0, 0], - ]; - static BITSET: [u64; 144] = [ - 0, 1, 2, 8, 13, 28, 64, 182, 191, 1016, 2032, 2047, 4096, 14336, 16128, 32640, 32768, - 40448, 131008, 262016, 491520, 8323072, 8396801, 10682368, 58719232, 100663296, 134152192, - 159383552, 234881024, 243138688, 536879204, 537919040, 805306369, 1073741824, 1073741916, - 1610612736, 2153546752, 3221225472, 3758096384, 4294967296, 4512022528, 51545911364, - 51545914817, 51548004382, 51554295838, 51556262398, 68719476736, 137438953472, 412316860416, - 1030792151040, 2199023255648, 8641373536127, 8763880767488, 17303886364672, 18004502906948, - 26388279066624, 36421322670080, 65128884076547, 65970697670631, 67755789254656, - 69200441769984, 70093866270720, 263882790666240, 277076930199552, 281470547525648, - 281470681808895, 281474976710655, 281479271675904, 562675075514368, 562949953355776, - 844424930131968, 985162418487296, 1023920203366400, 2251799813685248, 3377699721314304, - 4494803534348292, 6755399441055744, 7881299349733376, 8444256867844096, 8725724278030336, - 8760633780600833, 8989057312882695, 9042383626829823, 9851624185018758, 18067175067615234, - 28848986089586688, 30958948903026688, 35747322042253312, 53805701016846336, - 58529202969772032, 189151184399892480, 220713756545974272, 466122561432846339, - 504262420777140224, 558446353793941504, 572520102629474304, 1009933895770046464, - 1152921504606846982, 1152921504606851080, 1441151880758558727, 1724878657282899983, - 2301902359539744768, 2305843009196908767, 2305843009213693952, 2310337812748042240, - 3731232291276455943, 4589168020290535424, 4609293481125347328, 4611686018427387908, - 4611686069975392286, 4671217976001691648, 5764607523034234882, 6341068275337658371, - 6341349750314369024, 7421334051581067264, 8788774672813524990, 9205357638345293827, - 9222809086901354496, 9223372036854775808, 9223372036854775935, 9224497932466651184, - 9727775195120332910, 10376293541461622786, 11526998316797657088, 11959590285459062784, - 12103423998558208000, 12699165786766311424, 13005832773892571136, 13798747783286489088, - 13835058055282032640, 13835058055282163729, 13951307220663664640, 14987979559889010690, - 17872468738205286400, 17906312118425092095, 18158513697557839871, 18158513749097456062, - 18374686479671623680, 18374686479671623682, 18446462598732840960, 18446462598732972032, - 18446744056529158144, 18446744069414584320, 18446744073709551615, + static OFFSETS: [u8; 689] = [ + 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, + 4, 35, 1, 30, 27, 91, 11, 58, 9, 9, 1, 24, 4, 1, 9, 1, 3, 1, 5, 43, 3, 119, 15, 1, 32, 55, + 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 29, 1, 58, 1, 1, 1, 2, 4, 8, 1, 9, 1, 10, 2, 26, 1, 2, 2, + 57, 1, 4, 2, 4, 2, 2, 3, 3, 1, 30, 2, 3, 1, 11, 2, 57, 1, 4, 5, 1, 2, 4, 1, 20, 2, 22, 6, 1, + 1, 58, 1, 1, 2, 1, 4, 8, 1, 7, 3, 10, 2, 30, 1, 59, 1, 1, 1, 12, 1, 9, 1, 40, 1, 3, 1, 57, + 3, 5, 3, 1, 4, 7, 2, 11, 2, 29, 1, 58, 1, 2, 1, 2, 1, 3, 1, 5, 2, 7, 2, 11, 2, 28, 2, 57, 2, + 1, 1, 2, 4, 8, 1, 9, 1, 10, 2, 29, 1, 72, 1, 4, 1, 2, 3, 1, 1, 8, 1, 81, 1, 2, 7, 12, 8, 98, + 1, 2, 9, 11, 6, 74, 2, 27, 1, 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, + 6, 1, 2, 2, 2, 25, 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 0, 3, 0, 3, 29, 3, 29, 2, 30, + 2, 64, 2, 1, 7, 8, 1, 2, 11, 9, 1, 45, 3, 119, 2, 34, 1, 118, 3, 4, 2, 9, 1, 6, 3, 219, 2, + 2, 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 48, 17, 63, 4, 48, 7, 1, 1, 5, 1, 40, 9, + 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 152, 3, 1, 13, 1, 7, 4, 1, + 6, 1, 3, 2, 198, 58, 1, 5, 0, 1, 195, 33, 0, 3, 141, 1, 96, 32, 0, 6, 105, 2, 0, 4, 1, 10, + 32, 2, 80, 2, 0, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, + 48, 1, 2, 4, 2, 2, 39, 1, 67, 6, 2, 2, 2, 2, 12, 1, 8, 1, 47, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, + 1, 42, 2, 8, 1, 238, 1, 2, 1, 4, 1, 0, 1, 0, 16, 16, 16, 0, 2, 0, 1, 226, 1, 149, 5, 0, 3, + 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 0, 2, 153, 11, 176, 1, 54, 15, 56, 3, 49, 4, 2, 2, + 69, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 160, 1, 3, 8, + 21, 2, 57, 2, 1, 1, 1, 1, 22, 1, 14, 7, 3, 5, 195, 8, 2, 3, 1, 1, 23, 1, 81, 1, 2, 6, 1, 1, + 2, 1, 1, 2, 1, 2, 235, 1, 2, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, + 1, 101, 3, 2, 4, 1, 5, 0, 9, 1, 2, 245, 1, 10, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, + 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, + 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 0, 5, 59, 7, 0, 1, 63, 4, 81, 1, 0, + 2, 0, 1, 1, 3, 4, 5, 8, 8, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, + 7, 1, 17, 2, 7, 1, 2, 1, 5, 0, 7, 0, 4, 0, 7, 109, 7, 0, 96, 128, 240, 0, ]; - pub fn lookup(c: char) -> bool { - super::range_search( + super::skip_search( c as u32, - &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, - &BITSET_INDEX_CHUNKS, - &BITSET, + &SHORT_OFFSET_RUNS, + &OFFSETS, ) } } #[rustfmt::skip] pub mod lowercase { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (122, 6); - static BITSET_CHUNKS_MAP: [u8; 118] = [ - 12, 16, 0, 0, 10, 0, 0, 11, 13, 8, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 1, 0, 17, 0, 9, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, + static BITSET_CHUNKS_MAP: [u8; 123] = [ + 13, 16, 0, 0, 8, 0, 0, 11, 12, 9, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 1, 0, 14, 0, 7, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, + 0, 0, 6, ]; static BITSET_INDEX_CHUNKS: [[u8; 16]; 18] = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 62, 71, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 9, 0, 50, 42, 44, 28], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 68, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35], - [0, 0, 3, 0, 71, 71, 71, 0, 46, 46, 48, 46, 24, 37, 38, 23], - [0, 29, 27, 57, 39, 51, 52, 43, 41, 70, 26, 11, 0, 34, 64, 32], - [0, 40, 8, 0, 33, 60, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [22, 13, 54, 66, 25, 15, 56, 63, 30, 19, 12, 55, 58, 61, 65, 4], - [59, 36, 46, 21, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [59, 49, 45, 47, 18, 69, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [67, 5, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 52, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 39, 0, 47, 43, 45, 30], + [0, 0, 0, 0, 10, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26], + [0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 54, 0, 52, 52, 52, 0, 21, 21, 64, 21, 33, 24, 23, 34], + [0, 5, 71, 0, 28, 15, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 61, 31, 17, 22, 48, 49, 44, 42, 8, 32, 38, 0, 27, 13, 29], + [11, 55, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 25, 21, 35, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 46, 2, 20, 63, 9, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [60, 37, 51, 12, 70, 58, 18, 1, 6, 59, 68, 19, 65, 66, 3, 41], + ]; + static BITSET_CANONICAL: [u64; 52] = [ + 0b0000000000000000000000000000000000000000000000000000000000000000, + 0b1111111111111111110000000000000000000000000011111111111111111111, + 0b1010101010101010101010101010101010101010101010101010100000000010, + 0b1111111111111111111111000000000000000000000000001111110111111111, + 0b0000111111111111111111111111111111111111000000000000000000000000, + 0b1000000000000010000000000000000000000000000000000000000000000000, + 0b0000111111111111111111111111110000000000000000000000000011111111, + 0b0000000000000111111111111111111111111111111111111111111111111111, + 0b1111111111111111111111111111111111111111111111111010101010000101, + 0b1111111111111111111111111111111100000000000000000000000000000000, + 0b1111111111111111111111111111110000000000000000000000000000000000, + 0b1111111111111111111111110000000000000000000000000000000000000000, + 0b1111111111111111111111000000000000000000000000001111111111101111, + 0b1111111111111111111100000000000000000000000000010000000000000000, + 0b1111111111111111000000011111111111110111111111111111111111111111, + 0b1111111111111111000000000000000000000000000000000100001111000000, + 0b1111111111111111000000000000000000000000000000000000000000000000, + 0b1111111101111111111111111111111110000000000000000000000000000000, + 0b1111110000000000000000000000000011111111111111111111111111000000, + 0b1111000000000000000000000000001111110111111111111111111111111100, + 0b1010101010101010101010101010101010101010101010101101010101010100, + 0b1010101010101010101010101010101010101010101010101010101010101010, + 0b0101010110101010101010101010101010101010101010101010101010101010, + 0b0100000011011111000000001111111100000000111111110000000011111111, + 0b0011111111111111000000001111111100000000111111110000000000111111, + 0b0011111111011010000101010110001001111111111111111111111111111111, + 0b0011111100000000000000000000000000000000000000000000000000000000, + 0b0011110010001010000000000000000000000000000000000000000000100000, + 0b0011001000010000100000000000000000000000000010001100010000000000, + 0b0001100100101111101010101010101010101010111000110111111111111111, + 0b0000011101000000000000000000000000000000000000000000010100001000, + 0b0000010000100000000001000000000000000000000000000000000000000000, + 0b0000000111111111111111111111111111111111111011111111111111111111, + 0b0000000011111111000000001111111100000000001111110000000011111111, + 0b0000000011011100000000001111111100000000110011110000000011011100, + 0b0000000000001000010100000001101010101010101010101010101010101010, + 0b0000000000000000001000001011111111111111111111111111111111111111, + 0b0000000000000000000000001111111111111111110111111100000000000000, + 0b0000000000000000000000000001111100000000000000000000000000000011, + 0b0000000000000000000000000000000000111010101010101010101010101010, + 0b0000000000000000000000000000000000000000111110000000000001111111, + 0b0000000000000000000000000000000000000000000000000000101111110111, + 0b1001001111111010101010101010101010101010101010101010101010101010, + 0b1001010111111111101010101010101010101010101010101010101010101010, + 0b1010101000101001101010101010101010110101010101010101001001000000, + 0b1010101010100000100000101010101010101010101110100101000010101010, + 0b1010101010101010101010101010101011111111111111111111111111111111, + 0b1010101010101011101010101010100000000000000000000000000000000000, + 0b1101010010101010101010101010101010101010101010101010101101010101, + 0b1110011001010001001011010010101001001110001001000011000100101001, + 0b1110011111111111111111111111111111111111111111110000000000000000, + 0b1110101111000000000000000000000000001111111111111111111111111100, ]; - static BITSET: [u64; 72] = [ - 0, 15, 16, 511, 3063, 65535, 16253055, 134217726, 536805376, 984263338, 4294967295, - 133143986179, 274877905920, 1099509514240, 4398046445568, 17592185782272, 36009005809663, - 46912496118442, 187649984473770, 281474972516352, 2251799813685247, 2339875276368554, - 4503599560261632, 61925590106570972, 71777214282006783, 72057592964186127, - 144115188074807295, 297241973452963840, 522417556774978824, 576460743713488896, - 1152921487426978047, 1152921504590069760, 1814856824841797631, 3607524039012697088, - 4362299189061746720, 4539628424389459968, 4601013482110844927, 4611405638684049471, - 4674456033467236607, 6172933889249159850, 9223934986808197120, 10663022717737544362, - 10808545280696953514, 12261519110656315968, 12294970652241842346, 12297829382473033730, - 12297829382473034410, 12297829382473045332, 12297829382829550250, 12297829383904690175, - 12298110845996498944, 15324248332066007893, 16596095761559859497, 16717361816799215616, - 16987577794709946364, 17293822586148356092, 18158513701852807104, 18410715274543104000, - 18428729675466407935, 18446462598732840960, 18446462598732858304, 18446462598737002495, - 18446464797621878783, 18446673704966422527, 18446726481523572736, 18446739675663105535, - 18446739675663106031, 18446742974197923840, 18446744056529682432, 18446744069414584320, - 18446744073709529733, 18446744073709551615, + static BITSET_MAPPING: [(u8, u8); 20] = [ + (0, 64), (1, 188), (1, 183), (1, 176), (1, 109), (1, 124), (1, 126), (1, 66), (1, 70), + (1, 77), (2, 146), (2, 144), (2, 83), (3, 12), (3, 6), (4, 156), (4, 78), (5, 187), + (6, 132), (7, 93), ]; pub fn lookup(c: char) -> bool { - super::range_search( + super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, &BITSET_INDEX_CHUNKS, - &BITSET, + &BITSET_CANONICAL, + &BITSET_MAPPING, ) } } #[rustfmt::skip] pub mod n { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (127, 0); - static BITSET_CHUNKS_MAP: [u8; 127] = [ - 31, 8, 11, 25, 19, 4, 29, 21, 24, 28, 0, 16, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 13, 18, 26, 17, 23, 20, 15, 22, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 2, 0, 0, 10, 0, 14, 27, 12, 0, 1, + static SHORT_OFFSET_RUNS: [u32; 38] = [ + 1632, 18876774, 31461440, 102765417, 111154926, 115349830, 132128880, 165684320, 186656630, + 195046653, 199241735, 203436434, 216049184, 241215536, 249605104, 274792208, 278987015, + 283181793, 295766104, 320933114, 383848032, 392238160, 434181712, 442570976, 455154768, + 463544256, 476128256, 480340576, 484535936, 497144544, 501340110, 509731136, 513925872, + 518121671, 522316913, 530706688, 551681008, 556989434, ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 34] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 49], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 43, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 22, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 47, 0, 0, 0, 2], - [0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 31, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 31, 0, 45, 0, 31, 0, 31, 0, 41, 0, 34], - [0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 37, 44, 4, 0, 0, 0, 0, 52, 23, 3, 0, 13], - [0, 0, 0, 7, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 35, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 62, 47, 0, 0, 0, 0, 60, 0, 0, 24, 10, 0, 5], - [0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 2, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 0], - [0, 15, 0, 15, 0, 0, 0, 0, 0, 15, 0, 2, 51, 0, 0, 0], - [0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 26, 0, 0, 0, 15, 25, 0, 0, 0, 0, 0, 0, 0, 0, 11], - [0, 32, 0, 47, 65, 0, 0, 39, 0, 0, 0, 47, 0, 0, 0, 0], - [0, 46, 2, 0, 0, 71, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 59, 0, 31, 0, 42, 0, 31, 0, 15, 0, 15, 36, 0, 0, 0], - [0, 63, 30, 61, 18, 0, 55, 70, 0, 57, 20, 28, 0, 64, 29, 0], - [0, 66, 38, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 69, 19, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 9, 0], - [15, 0, 0, 0, 0, 8, 0, 17, 0, 0, 16, 0, 0, 15, 47, 0], - [40, 0, 0, 15, 2, 0, 0, 48, 0, 15, 0, 0, 0, 0, 0, 47], - [47, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [50, 0, 0, 0, 0, 0, 12, 0, 25, 21, 67, 0, 0, 0, 0, 0], - [73, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - ]; - static BITSET: [u64; 74] = [ - 0, 999, 1023, 1026, 3072, 4064, 8191, 65408, 65472, 1048575, 1966080, 2097151, 3932160, - 4063232, 8388607, 67043328, 67044351, 134152192, 264241152, 268435455, 3758096384, - 4294901504, 17112694784, 64424509440, 549218942976, 4393751543808, 35184372023296, - 140737488355327, 272678883688448, 279275953455104, 280925220896768, 281200098803712, - 281474976448512, 492581209243648, 2251524935778304, 2251795518717952, 4503595332403200, - 4503599627370368, 8708132091985919, 9007190731849728, 17732923532771328, 71212894229889024, - 144114915328655360, 144115183780888576, 144115188075855871, 284007976623144960, - 284008251501051904, 287948901175001088, 287948901242044416, 287953294926544896, - 504407547722072192, 1152640029630136320, 1152921496016912384, 2305840810190438400, - 2305843009213693952, 3458764513820540928, 4611615649683210238, 6917529027641082367, - 8217943420044312576, 9151595642915651584, 9223372032559808512, 17870283321406128128, - 18158513697557839872, 18302628889911885824, 18374686483949813760, 18428729675200069632, - 18446181123756130304, 18446181123756131327, 18446739675663040512, 18446744069414584320, - 18446744073709355007, 18446744073709486080, 18446744073709535232, 18446744073709551615, + static OFFSETS: [u8; 267] = [ + 48, 10, 120, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, + 10, 118, 10, 2, 6, 110, 13, 115, 10, 8, 7, 103, 10, 104, 7, 7, 19, 109, 10, 96, 10, 118, 10, + 70, 20, 0, 10, 70, 10, 0, 20, 0, 3, 239, 10, 6, 10, 22, 10, 0, 10, 128, 11, 165, 10, 6, 10, + 182, 10, 86, 10, 134, 10, 6, 10, 0, 1, 3, 6, 6, 10, 198, 51, 2, 5, 0, 60, 78, 22, 0, 30, 0, + 1, 0, 1, 25, 9, 14, 3, 0, 4, 138, 10, 30, 8, 1, 15, 32, 10, 39, 15, 0, 10, 188, 10, 0, 6, + 154, 10, 38, 10, 198, 10, 22, 10, 86, 10, 0, 10, 0, 10, 0, 45, 12, 57, 17, 2, 0, 27, 36, 4, + 29, 1, 8, 1, 134, 5, 202, 10, 0, 8, 25, 7, 39, 9, 75, 5, 22, 6, 160, 2, 2, 16, 2, 46, 64, 9, + 52, 2, 30, 3, 75, 5, 104, 8, 24, 8, 41, 7, 0, 6, 48, 10, 0, 31, 158, 10, 42, 4, 112, 7, 134, + 30, 128, 10, 60, 10, 144, 10, 7, 20, 251, 10, 0, 10, 118, 10, 0, 10, 102, 10, 102, 12, 0, + 19, 93, 10, 0, 29, 227, 10, 70, 10, 0, 21, 0, 111, 0, 10, 230, 10, 1, 7, 0, 23, 0, 20, 108, + 25, 0, 50, 0, 10, 0, 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::range_search( + super::skip_search( c as u32, - &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, - &BITSET_INDEX_CHUNKS, - &BITSET, + &SHORT_OFFSET_RUNS, + &OFFSETS, ) } } #[rustfmt::skip] pub mod uppercase { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (124, 6); - static BITSET_CHUNKS_MAP: [u8; 123] = [ - 12, 15, 0, 0, 11, 0, 0, 8, 5, 9, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 13, 0, 7, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, - 0, 0, 4, + static BITSET_CHUNKS_MAP: [u8; 125] = [ + 12, 15, 5, 5, 0, 5, 5, 2, 4, 11, 5, 14, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 6, 5, 13, 5, 10, 5, 5, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 16, 5, 5, + 5, 5, 9, 5, 3, ]; static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 9, 0, 38, 46, 44, 28], - [0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 51, 23, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 60, 62, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 54, 0, 0, 0, 0, 0, 43, 43, 40, 43, 56, 22, 34, 35], - [0, 0, 57, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 66, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 30], - [0, 10, 0, 11, 50, 37, 36, 45, 47, 5, 0, 0, 0, 49, 17, 53], - [14, 0, 60, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [21, 52, 43, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [24, 39, 42, 41, 59, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [58, 65, 29, 16, 48, 63, 31, 19, 55, 61, 64, 32, 27, 20, 15, 3], + [41, 41, 5, 33, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 0], + [41, 41, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [41, 41, 38, 41, 41, 41, 41, 41, 17, 17, 61, 17, 40, 29, 24, 23], + [41, 41, 41, 41, 9, 8, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [41, 41, 41, 41, 35, 28, 65, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 56, 41, 41, 41], + [41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 41, 41, 41, 41, 41, 41], + [41, 41, 41, 41, 41, 41, 41, 41, 41, 60, 59, 41, 20, 14, 16, 4], + [41, 41, 41, 41, 47, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [41, 41, 51, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [41, 41, 52, 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [41, 53, 41, 31, 34, 21, 22, 15, 13, 32, 41, 41, 41, 11, 30, 37], + [48, 41, 9, 44, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [49, 36, 17, 27, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [50, 19, 2, 18, 10, 45, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41], + [57, 1, 26, 54, 12, 7, 25, 55, 39, 58, 6, 3, 64, 63, 62, 66], + ]; + static BITSET_CANONICAL: [u64; 41] = [ + 0b0000000000111111111111111111111111111111111111111111111111111111, + 0b1111111111111111111111110000000000000000000000000011111111111111, + 0b0101010101010101010101010101010101010101010101010101010000000001, + 0b0000011111111111111111111111110000000000000000000000000000000001, + 0b0000000000100000000000000000000000000000000000000000001011110100, + 0b1111111111111111111111111111111100000000000000000000000000000000, + 0b1111111111111111111111110000000000000000000000000000001111111111, + 0b1111111111111111111100000000000000000000000000011111110001011111, + 0b1111111111111111000000111111111111111111111111110000001111111111, + 0b1111111111111111000000000000000000000000000000000000000000000000, + 0b1111111111111110010101010101010101010101010101010101010101010101, + 0b1000000001000101000000000000000000000000000000000000000000000000, + 0b0111101100000000000000000000000000011111110111111110011110110000, + 0b0110110000000101010101010101010101010101010101010101010101010101, + 0b0110101000000000010101010101010101010101010101010101010101010101, + 0b0101010111010010010101010101010101001010101010101010010010010000, + 0b0101010101011111011111010101010101010101010001010010100001010101, + 0b0101010101010101010101010101010101010101010101010101010101010101, + 0b0101010101010101010101010101010101010101010101010010101010101011, + 0b0101010101010101010101010101010100000000000000000000000000000000, + 0b0101010101010100010101010101010000000000000000000000000000000000, + 0b0010101101010101010101010101010101010101010101010101010010101010, + 0b0001000110101110110100101101010110110001110110111100111011010110, + 0b0000111100000000000111110000000000001111000000000000111100000000, + 0b0000111100000000000000000000000000000000000000000000000000000000, + 0b0000001111111111111111111111111100000000000000000000000000111111, + 0b0000000000111111110111100110010011010000000000000000000000000011, + 0b0000000000000100001010000000010101010101010101010101010101010101, + 0b0000000000000000111111111111111100000000000000000000000000100000, + 0b0000000000000000111111110000000010101010000000000011111100000000, + 0b0000000000000000000011111111101111111111111111101101011101000000, + 0b0000000000000000000000000000000001111111011111111111111111111111, + 0b0000000000000000000000000000000000000000000000000101010101111010, + 0b0000000000000000000000000000000000000000000000000010000010111111, + 0b1010101001010101010101010101010101010101010101010101010101010101, + 0b1100000000001111001111010101000000111110001001110011100010000100, + 0b1100000000100101111010101001110100000000000000000000000000000000, + 0b1110011010010000010101010101010101010101000111001000000000000000, + 0b1110011111111111111111111111111111111111111111110000000000000000, + 0b1111000000000000000000000000001111111111111111111111111100000000, + 0b1111111100000000111111110000000000111111000000001111111100000000, ]; - static BITSET: [u64; 67] = [ - 0, 8, 1023, 1024, 8383, 21882, 65535, 1048575, 8388607, 89478485, 134217726, 2139095039, - 4294967295, 17179869183, 1099511627775, 2199023190016, 4398046445568, 17575006099264, - 23456248059221, 70368743129088, 140737484161024, 140737488355327, 280378317225728, - 281470681743392, 281474976710655, 1169903278445909, 2251799813685247, 9007198986305536, - 9007199254741748, 17977448100528131, 18014398509481983, 288230371856744511, - 576460735123554305, 576460743713488896, 1080863910568919040, 1080897995681042176, - 1274187559846268630, 3122495741643543722, 6148633210533183488, 6148914689804861440, - 6148914690880001365, 6148914691236506283, 6148914691236516865, 6148914691236517205, - 6151773421467674709, 6184099063146390672, 7638198793012598101, 7783721355972007253, - 8863084067199903664, 9242793810247811072, 12273810184460391765, 13839347594782259332, - 13845730589451223040, 16613872850358272000, 16717361816799215616, 17293822586282573568, - 18374966856193736448, 18428729675200069632, 18442240474149289983, 18446274948748367189, - 18446462598732840960, 18446462598737035263, 18446466996779287551, 18446726481523637343, - 18446742974197924863, 18446742974197940223, 18446744069414584320, + static BITSET_MAPPING: [(u8, u8); 26] = [ + (0, 182), (0, 74), (0, 166), (0, 162), (0, 159), (0, 150), (0, 148), (0, 142), (0, 135), + (0, 134), (0, 131), (0, 64), (1, 115), (1, 66), (1, 70), (1, 83), (1, 12), (1, 8), (2, 164), + (2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), ]; pub fn lookup(c: char) -> bool { - super::range_search( + super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, &BITSET_INDEX_CHUNKS, - &BITSET, + &BITSET_CANONICAL, + &BITSET_MAPPING, ) } } #[rustfmt::skip] pub mod white_space { - static BITSET_LAST_CHUNK_MAP: (u16, u8) = (12, 2); - static BITSET_CHUNKS_MAP: [u8; 9] = [ - 3, 0, 0, 0, 0, 1, 0, 0, 4, - ]; - static BITSET_INDEX_CHUNKS: [[u8; 16]; 5] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + static SHORT_OFFSET_RUNS: [u32; 4] = [ + 5760, 18882560, 23080960, 40972289, ]; - static BITSET: [u64; 6] = [ - 0, 1, 2147483648, 4294967328, 4294983168, 144036023240703, + static OFFSETS: [u8; 21] = [ + 9, 5, 18, 1, 100, 1, 26, 1, 0, 1, 0, 11, 29, 2, 5, 1, 47, 1, 0, 1, 0, ]; - pub fn lookup(c: char) -> bool { - super::range_search( + super::skip_search( c as u32, - &BITSET_CHUNKS_MAP, - BITSET_LAST_CHUNK_MAP, - &BITSET_INDEX_CHUNKS, - &BITSET, + &SHORT_OFFSET_RUNS, + &OFFSETS, ) } } diff --git a/src/libcore/unicode/version.rs b/src/libcore/unicode/version.rs deleted file mode 100644 index 4d68d2e8c2ef7..0000000000000 --- a/src/libcore/unicode/version.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// Represents a Unicode Version. -/// -/// See also: -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -#[unstable(feature = "unicode_version", issue = "49726")] -pub struct UnicodeVersion { - /// Major version. - pub major: u32, - - /// Minor version. - pub minor: u32, - - /// Micro (or Update) version. - pub micro: u32, - - // Private field to keep struct expandable. - pub(crate) _priv: (), -} diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index e138503b508d5..677c027f17b54 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -10,6 +10,7 @@ test(attr(deny(warnings))) )] #![feature(nll)] +#![feature(or_patterns)] #![feature(rustc_private)] #![feature(unicode_internals)] #![feature(bool_to_option)] @@ -26,6 +27,15 @@ use std::string; use rustc_span::{InnerSpan, Symbol}; +/// The type of format string that we are parsing. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ParseMode { + /// A normal format string as per `format_args!`. + Format, + /// An inline assembly template string for `asm!`. + InlineAsm, +} + #[derive(Copy, Clone)] struct InnerOffset(usize); @@ -162,6 +172,7 @@ pub struct ParseError { /// This is a recursive-descent parser for the sake of simplicity, and if /// necessary there's probably lots of room for improvement performance-wise. pub struct Parser<'a> { + mode: ParseMode, input: &'a str, cur: iter::Peekable>, /// Error messages accumulated during parsing @@ -178,6 +189,8 @@ pub struct Parser<'a> { last_opening_brace: Option, /// Whether the source string is comes from `println!` as opposed to `format!` or `print!` append_newline: bool, + /// Whether this formatting string is a literal or it comes from a macro. + is_literal: bool, } impl<'a> Iterator for Parser<'a> { @@ -200,7 +213,9 @@ impl<'a> Iterator for Parser<'a> { if let Some(end) = self.must_consume('}') { let start = self.to_span_index(pos); let end = self.to_span_index(end + 1); - self.arg_places.push(start.to(end)); + if self.is_literal { + self.arg_places.push(start.to(end)); + } } Some(NextArgument(arg)) } @@ -234,10 +249,13 @@ impl<'a> Parser<'a> { pub fn new( s: &'a str, style: Option, - skips: Vec, + snippet: Option, append_newline: bool, + mode: ParseMode, ) -> Parser<'a> { + let (skips, is_literal) = find_skips_from_snippet(snippet, style); Parser { + mode, input: s, cur: s.char_indices().peekable(), errors: vec![], @@ -247,6 +265,7 @@ impl<'a> Parser<'a> { skips, last_opening_brace: None, append_newline, + is_literal, } } @@ -425,7 +444,10 @@ impl<'a> Parser<'a> { /// Parses an `Argument` structure, or what's contained within braces inside the format string. fn argument(&mut self) -> Argument<'a> { let pos = self.position(); - let format = self.format(); + let format = match self.mode { + ParseMode::Format => self.format(), + ParseMode::InlineAsm => self.inline_asm(), + }; // Resolve position after parsing format spec. let pos = match pos { @@ -482,7 +504,7 @@ impl<'a> Parser<'a> { // fill character if let Some(&(_, c)) = self.cur.peek() { match self.cur.clone().nth(1) { - Some((_, '>')) | Some((_, '<')) | Some((_, '^')) => { + Some((_, '>' | '<' | '^')) => { spec.fill = Some(c); self.cur.next(); } @@ -573,6 +595,36 @@ impl<'a> Parser<'a> { spec } + /// Parses an inline assembly template modifier at the current position, returning the modifier + /// in the `ty` field of the `FormatSpec` struct. + fn inline_asm(&mut self) -> FormatSpec<'a> { + let mut spec = FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + precision_span: None, + width: CountImplied, + width_span: None, + ty: &self.input[..0], + ty_span: None, + }; + if !self.consume(':') { + return spec; + } + + let ty_span_start = self.cur.peek().map(|(pos, _)| *pos); + spec.ty = self.word(); + let ty_span_end = self.cur.peek().map(|(pos, _)| *pos); + if !spec.ty.is_empty() { + spec.ty_span = ty_span_start + .and_then(|s| ty_span_end.map(|e| (s, e))) + .map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end))); + } + + spec + } + /// Parses a `Count` parameter at the current position. This does not check /// for 'CountIsNextParam' because that is only used in precision, not /// width. @@ -651,5 +703,103 @@ impl<'a> Parser<'a> { } } +/// Finds the indices of all characters that have been processed and differ between the actual +/// written code (code snippet) and the `InternedString` that gets processed in the `Parser` +/// in order to properly synthethise the intra-string `Span`s for error diagnostics. +fn find_skips_from_snippet( + snippet: Option, + str_style: Option, +) -> (Vec, bool) { + let snippet = match snippet { + Some(ref s) if s.starts_with('"') || s.starts_with("r#") => s, + _ => return (vec![], false), + }; + + fn find_skips(snippet: &str, is_raw: bool) -> Vec { + let mut eat_ws = false; + let mut s = snippet.chars().enumerate().peekable(); + let mut skips = vec![]; + while let Some((pos, c)) = s.next() { + match (c, s.peek()) { + // skip whitespace and empty lines ending in '\\' + ('\\', Some((next_pos, '\n'))) if !is_raw => { + eat_ws = true; + skips.push(pos); + skips.push(*next_pos); + let _ = s.next(); + } + ('\\', Some((next_pos, '\n' | 'n' | 't'))) if eat_ws => { + skips.push(pos); + skips.push(*next_pos); + let _ = s.next(); + } + (' ' | '\n' | '\t', _) if eat_ws => { + skips.push(pos); + } + ('\\', Some((next_pos, 'n' | 't' | '0' | '\\' | '\'' | '\"'))) => { + skips.push(*next_pos); + let _ = s.next(); + } + ('\\', Some((_, 'x'))) if !is_raw => { + for _ in 0..3 { + // consume `\xAB` literal + if let Some((pos, _)) = s.next() { + skips.push(pos); + } else { + break; + } + } + } + ('\\', Some((_, 'u'))) if !is_raw => { + if let Some((pos, _)) = s.next() { + skips.push(pos); + } + if let Some((next_pos, next_c)) = s.next() { + if next_c == '{' { + skips.push(next_pos); + let mut i = 0; // consume up to 6 hexanumeric chars + closing `}` + while let (Some((next_pos, c)), true) = (s.next(), i < 7) { + if c.is_digit(16) { + skips.push(next_pos); + } else if c == '}' { + skips.push(next_pos); + break; + } else { + break; + } + i += 1; + } + } else if next_c.is_digit(16) { + skips.push(next_pos); + // We suggest adding `{` and `}` when appropriate, accept it here as if + // it were correct + let mut i = 0; // consume up to 6 hexanumeric chars + while let (Some((next_pos, c)), _) = (s.next(), i < 6) { + if c.is_digit(16) { + skips.push(next_pos); + } else { + break; + } + i += 1; + } + } + } + } + _ if eat_ws => { + // `take_while(|c| c.is_whitespace())` + eat_ws = false; + } + _ => {} + } + } + skips + } + + let r_start = str_style.map(|r| r + 1).unwrap_or(0); + let r_end = str_style.map(|r| r).unwrap_or(0); + let s = &snippet[r_start + 1..snippet.len() - r_end - 1]; + (find_skips(s, str_style.is_some()), true) +} + #[cfg(test)] mod tests; diff --git a/src/libfmt_macros/tests.rs b/src/libfmt_macros/tests.rs index 98c2a17f0dd54..9932c1df7a935 100644 --- a/src/libfmt_macros/tests.rs +++ b/src/libfmt_macros/tests.rs @@ -1,7 +1,7 @@ use super::*; fn same(fmt: &'static str, p: &[Piece<'static>]) { - let parser = Parser::new(fmt, None, vec![], false); + let parser = Parser::new(fmt, None, None, false, ParseMode::Format); assert_eq!(parser.collect::>>(), p); } @@ -20,7 +20,7 @@ fn fmtdflt() -> FormatSpec<'static> { } fn musterr(s: &str) { - let mut p = Parser::new(s, None, vec![], false); + let mut p = Parser::new(s, None, None, false, ParseMode::Format); p.next(); assert!(!p.errors.is_empty()); } diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index f44a875c9d0d5..fd3e11858cef6 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -106,15 +106,6 @@ pub mod personalities { 1 // `ExceptionContinueSearch` } - // Similar to above, this corresponds to the `eh_unwind_resume` lang item - // that's only used on Windows currently. - // - // Note that we don't execute landing pads, so this is never called, so it's - // body is empty. - #[rustc_std_internal_symbol] - #[cfg(all(bootstrap, target_os = "windows", target_env = "gnu"))] - pub extern "C" fn rust_eh_unwind_resume() {} - // These two are called by our startup objects on i686-pc-windows-gnu, but // they don't need to do anything so the bodies are nops. #[rustc_std_internal_symbol] diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index 1622442a5eb45..f5d83c21da068 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -311,18 +311,6 @@ unsafe fn find_eh_action( eh::find_eh_action(lsda, &eh_context, foreign_exception) } -#[cfg(all( - bootstrap, - target_os = "windows", - any(target_arch = "x86", target_arch = "x86_64"), - target_env = "gnu" -))] -#[lang = "eh_unwind_resume"] -#[unwind(allowed)] -unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { - uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception); -} - // Frame unwind info registration // // Each module's image contains a frame unwind info section (usually diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 0a2a0e9e045c6..f791fe82a27e7 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -47,9 +47,6 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "hermit")] { #[path = "hermit.rs"] mod real_imp; - } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] { - #[path = "dummy.rs"] - mod real_imp; } else if #[cfg(target_env = "msvc")] { #[path = "seh.rs"] mod real_imp; diff --git a/src/libpanic_unwind/seh.rs b/src/libpanic_unwind/seh.rs index 10b765a5b411b..b1e87a7cac26a 100644 --- a/src/libpanic_unwind/seh.rs +++ b/src/libpanic_unwind/seh.rs @@ -117,7 +117,7 @@ mod imp { } } -#[cfg(any(target_arch = "x86_64", target_arch = "arm"))] +#[cfg(not(target_arch = "x86"))] #[macro_use] mod imp { pub type ptr_t = u32; @@ -213,7 +213,6 @@ extern "C" { // // This is fine since the MSVC runtime uses string comparison on the type name // to match TypeDescriptors rather than pointer equality. -#[cfg_attr(bootstrap, lang = "eh_catch_typeinfo")] static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, spare: core::ptr::null_mut(), @@ -328,5 +327,8 @@ pub unsafe fn cleanup(payload: *mut u8) -> Box { #[lang = "eh_personality"] #[cfg(not(test))] fn rust_eh_personality() { - unsafe { core::intrinsics::abort() } + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + core::intrinsics::abort() + } } diff --git a/src/libproc_macro/bridge/client.rs b/src/libproc_macro/bridge/client.rs index 088db92253acf..283aa25b0ea13 100644 --- a/src/libproc_macro/bridge/client.rs +++ b/src/libproc_macro/bridge/client.rs @@ -202,10 +202,16 @@ impl Clone for Literal { } } -// FIXME(eddyb) `Literal` should not expose internal `Debug` impls. impl fmt::Debug for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.debug()) + f.debug_struct("Literal") + // format the kind without quotes, as in `kind: Float` + .field("kind", &format_args!("{}", &self.debug_kind())) + .field("symbol", &self.symbol()) + // format `Some("...")` on one line even in {:#?} mode + .field("suffix", &format_args!("{:?}", &self.suffix())) + .field("span", &self.span()) + .finish() } } @@ -290,6 +296,13 @@ impl BridgeState<'_> { } impl Bridge<'_> { + pub(crate) fn is_available() -> bool { + BridgeState::with(|state| match state { + BridgeState::Connected(_) | BridgeState::InUse => true, + BridgeState::NotConnected => false, + }) + } + fn enter(self, f: impl FnOnce() -> R) -> R { // Hide the default panic output within `proc_macro` expansions. // NB. the server can't do this because it may use a different libstd. diff --git a/src/libproc_macro/bridge/mod.rs b/src/libproc_macro/bridge/mod.rs index a0e7d90f4974e..bf0d8fcee5b8f 100644 --- a/src/libproc_macro/bridge/mod.rs +++ b/src/libproc_macro/bridge/mod.rs @@ -103,8 +103,9 @@ macro_rules! with_api { Literal { fn drop($self: $S::Literal); fn clone($self: &$S::Literal) -> $S::Literal; - // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. - fn debug($self: &$S::Literal) -> String; + fn debug_kind($self: &$S::Literal) -> String; + fn symbol($self: &$S::Literal) -> String; + fn suffix($self: &$S::Literal) -> Option; fn integer(n: &str) -> $S::Literal; fn typed_integer(n: &str, kind: &str) -> $S::Literal; fn float(n: &str) -> $S::Literal; diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 8f16e3c08ec5b..f11401b5a0c7c 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -24,10 +24,10 @@ #![feature(decl_macro)] #![feature(extern_types)] #![feature(in_band_lifetimes)] +#![feature(negative_impls)] #![feature(optin_builtin_traits)] #![feature(rustc_attrs)] -#![cfg_attr(bootstrap, feature(specialization))] -#![cfg_attr(not(bootstrap), feature(min_specialization))] +#![feature(min_specialization)] #![recursion_limit = "256"] #[unstable(feature = "proc_macro_internals", issue = "27812")] @@ -44,6 +44,24 @@ use std::path::PathBuf; use std::str::FromStr; use std::{error, fmt, iter, mem}; +/// Determines whether proc_macro has been made accessible to the currently +/// running program. +/// +/// The proc_macro crate is only intended for use inside the implementation of +/// procedural macros. All the functions in this crate panic if invoked from +/// outside of a procedural macro, such as from a build script or unit test or +/// ordinary Rust binary. +/// +/// With consideration for Rust libraries that are designed to support both +/// macro and non-macro use cases, `proc_macro::is_available()` provides a +/// non-panicking way to detect whether the infrastructure required to use the +/// API of proc_macro is presently available. Returns true if invoked from +/// inside of a procedural macro, false if invoked from any other binary. +#[unstable(feature = "proc_macro_is_available", issue = "71436")] +pub fn is_available() -> bool { + bridge::Bridge::is_available() +} + /// The main type provided by this crate, representing an abstract stream of /// tokens, or, more specifically, a sequence of token trees. /// The type provide interfaces for iterating over those token trees and, conversely, @@ -140,6 +158,13 @@ impl fmt::Debug for TokenStream { } } +#[stable(feature = "proc_macro_token_stream_default", since = "1.45.0")] +impl Default for TokenStream { + fn default() -> Self { + TokenStream::new() + } +} + #[unstable(feature = "proc_macro_quote", issue = "54722")] pub use quote::{quote, quote_span}; @@ -255,14 +280,14 @@ impl !Send for Span {} impl !Sync for Span {} macro_rules! diagnostic_method { - ($name:ident, $level:expr) => ( + ($name:ident, $level:expr) => { /// Creates a new `Diagnostic` with the given `message` at the span /// `self`. #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub fn $name>(self, message: T) -> Diagnostic { Diagnostic::spanned(self, $level, message) } - ) + }; } impl Span { @@ -285,7 +310,7 @@ impl Span { /// definition site (local variables, labels, `$crate`) and sometimes at the macro /// call site (everything else). /// The span location is taken from the call-site. - #[unstable(feature = "proc_macro_mixed_site", issue = "65049")] + #[stable(feature = "proc_macro_mixed_site", since = "1.45.0")] pub fn mixed_site() -> Span { Span(bridge::client::Span::mixed_site()) } @@ -333,14 +358,14 @@ impl Span { /// Creates a new span with the same line/column information as `self` but /// that resolves symbols as though it were at `other`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] pub fn resolved_at(&self, other: Span) -> Span { Span(self.0.resolved_at(other.0)) } /// Creates a new span with the same name resolution behavior as `self` but /// with the line/column information of `other`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_located_at", since = "1.45.0")] pub fn located_at(&self, other: Span) -> Span { other.resolved_at(*self) } @@ -1116,7 +1141,6 @@ impl fmt::Display for Literal { #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl fmt::Debug for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. self.0.fmt(f) } } diff --git a/src/libprofiler_builtins/build.rs b/src/libprofiler_builtins/build.rs index c990b28933504..e23e2f2c1306f 100644 --- a/src/libprofiler_builtins/build.rs +++ b/src/libprofiler_builtins/build.rs @@ -63,8 +63,9 @@ fn main() { cfg.define("COMPILER_RT_HAS_ATOMICS", Some("1")); } - let root = env::var_os("RUST_COMPILER_RT_ROOT").unwrap(); - let root = Path::new(&root); + // Note that this should exist if we're going to run (otherwise we just + // don't build profiler builtins at all). + let root = Path::new("../llvm-project/compiler-rt"); let src_root = root.join("lib").join("profile"); for src in profile_sources { diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml deleted file mode 100644 index 47b94a2f1a4b4..0000000000000 --- a/src/librustc/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc" -path = "lib.rs" -doctest = false - -[dependencies] -arena = { path = "../libarena" } -bitflags = "1.2.1" -jobserver = "0.1" -scoped-tls = "1.0" -log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc-rayon = "0.3.0" -rustc-rayon-core = "0.3.0" -polonius-engine = "0.12.0" -rustc_apfloat = { path = "../librustc_apfloat" } -rustc_attr = { path = "../librustc_attr" } -rustc_feature = { path = "../librustc_feature" } -rustc_hir = { path = "../librustc_hir" } -rustc_target = { path = "../librustc_target" } -rustc_macros = { path = "../librustc_macros" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_query_system = { path = "../librustc_query_system" } -rustc_errors = { path = "../librustc_errors" } -rustc_index = { path = "../librustc_index" } -rustc_serialize = { path = "../libserialize", package = "serialize" } -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -backtrace = "0.3.40" -parking_lot = "0.9" -byteorder = { version = "1.3" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } -measureme = "0.7.1" -rustc_session = { path = "../librustc_session" } diff --git a/src/librustc/arena.rs b/src/librustc/arena.rs deleted file mode 100644 index 57c1e5521a9d9..0000000000000 --- a/src/librustc/arena.rs +++ /dev/null @@ -1,132 +0,0 @@ -/// This declares a list of types which can be allocated by `Arena`. -/// -/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. -/// This is faster and more memory efficient if there's only a few allocations of the type. -/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is -/// faster and more memory efficient if there is lots of allocations. -/// -/// Specifying the `decode` modifier will add decode impls for &T and &[T] where T is the type -/// listed. These impls will appear in the implement_ty_decoder! macro. -#[macro_export] -macro_rules! arena_types { - ($macro:path, $args:tt, $tcx:lifetime) => ( - $macro!($args, [ - [] layouts: rustc::ty::layout::LayoutDetails, - [] generics: rustc::ty::Generics, - [] trait_def: rustc::ty::TraitDef, - [] adt_def: rustc::ty::AdtDef, - [] steal_mir: rustc::ty::steal::Steal>, - [] mir: rustc::mir::BodyAndCache<$tcx>, - [] steal_promoted: rustc::ty::steal::Steal< - rustc_index::vec::IndexVec< - rustc::mir::Promoted, - rustc::mir::BodyAndCache<$tcx> - > - >, - [] promoted: rustc_index::vec::IndexVec< - rustc::mir::Promoted, - rustc::mir::BodyAndCache<$tcx> - >, - [decode] tables: rustc::ty::TypeckTables<$tcx>, - [decode] borrowck_result: rustc::mir::BorrowCheckResult<$tcx>, - [] const_allocs: rustc::mir::interpret::Allocation, - [] vtable_method: Option<( - rustc_hir::def_id::DefId, - rustc::ty::subst::SubstsRef<$tcx> - )>, - [few, decode] mir_keys: rustc_hir::def_id::DefIdSet, - [decode] specialization_graph: rustc::traits::specialization_graph::Graph, - [] region_scope_tree: rustc::middle::region::ScopeTree, - [] item_local_set: rustc_hir::ItemLocalSet, - [decode] mir_const_qualif: rustc_index::bit_set::BitSet, - [] trait_impls_of: rustc::ty::trait_def::TraitImpls, - [] associated_items: rustc::ty::AssociatedItems, - [] dropck_outlives: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, - rustc::traits::query::DropckOutlivesResult<'tcx> - > - >, - [] normalize_projection_ty: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, - rustc::traits::query::NormalizationResult<'tcx> - > - >, - [] implied_outlives_bounds: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, - Vec> - > - >, - [] type_op_subtype: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, ()> - >, - [] type_op_normalize_poly_fn_sig: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::PolyFnSig<'tcx>> - >, - [] type_op_normalize_fn_sig: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::FnSig<'tcx>> - >, - [] type_op_normalize_predicate: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::Predicate<'tcx>> - >, - [] type_op_normalize_ty: - rustc::infer::canonical::Canonical<'tcx, - rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::Ty<'tcx>> - >, - [few] crate_inherent_impls: rustc::ty::CrateInherentImpls, - [few] upstream_monomorphizations: - rustc_hir::def_id::DefIdMap< - rustc_data_structures::fx::FxHashMap< - rustc::ty::subst::SubstsRef<'tcx>, - rustc_hir::def_id::CrateNum - > - >, - [few] diagnostic_items: rustc_data_structures::fx::FxHashMap< - rustc_span::symbol::Symbol, - rustc_hir::def_id::DefId, - >, - [few] resolve_lifetimes: rustc::middle::resolve_lifetime::ResolveLifetimes, - [few] lint_levels: rustc::lint::LintLevelMap, - [few] stability_index: rustc::middle::stability::Index<'tcx>, - [few] features: rustc_feature::Features, - [few] all_traits: Vec, - [few] privacy_access_levels: rustc::middle::privacy::AccessLevels, - [few] target_features_whitelist: rustc_data_structures::fx::FxHashMap< - String, - Option - >, - [few] wasm_import_module_map: rustc_data_structures::fx::FxHashMap< - rustc_hir::def_id::DefId, - String - >, - [few] get_lib_features: rustc::middle::lib_features::LibFeatures, - [few] defined_lib_features: rustc::middle::lang_items::LanguageItems, - [few] visible_parent_map: rustc_hir::def_id::DefIdMap, - [few] foreign_module: rustc::middle::cstore::ForeignModule, - [few] foreign_modules: Vec, - [few] reachable_non_generics: rustc_hir::def_id::DefIdMap< - rustc::middle::exported_symbols::SymbolExportLevel - >, - [few] crate_variances: rustc::ty::CrateVariancesMap<'tcx>, - [few] inferred_outlives_crate: rustc::ty::CratePredicatesMap<'tcx>, - [] upvars: rustc_data_structures::fx::FxIndexMap, - - // Interned types - [] tys: rustc::ty::TyS<$tcx>, - - // HIR query types - [few] indexed_hir: rustc::hir::map::IndexedHir<$tcx>, - [few] hir_definitions: rustc_hir::definitions::Definitions, - [] hir_owner: rustc::hir::Owner<$tcx>, - [] hir_owner_nodes: rustc::hir::OwnerNodes<$tcx>, - ], $tcx); - ) -} - -arena_types!(arena::declare_arena, [], 'tcx); diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs deleted file mode 100644 index 3c39597584df5..0000000000000 --- a/src/librustc/dep_graph/mod.rs +++ /dev/null @@ -1,191 +0,0 @@ -use crate::ich::StableHashingContext; -use crate::ty::query::try_load_from_on_disk_cache; -use crate::ty::{self, TyCtxt}; -use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::Diagnostic; -use rustc_hir::def_id::DefId; - -mod dep_node; -mod safe; - -pub(crate) use rustc_query_system::dep_graph::DepNodeParams; -pub use rustc_query_system::dep_graph::{ - debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, - WorkProduct, WorkProductFileKind, WorkProductId, -}; - -pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt}; -pub use safe::AssertDepGraphSafe; -pub use safe::DepGraphSafe; - -pub type DepGraph = rustc_query_system::dep_graph::DepGraph; -pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps; -pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery; -pub type PreviousDepGraph = rustc_query_system::dep_graph::PreviousDepGraph; -pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph; - -impl rustc_query_system::dep_graph::DepKind for DepKind { - fn is_eval_always(&self) -> bool { - DepKind::is_eval_always(self) - } - - fn has_params(&self) -> bool { - DepKind::has_params(self) - } - - fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", node.kind)?; - - if !node.kind.has_params() && !node.kind.is_anon() { - return Ok(()); - } - - write!(f, "(")?; - - ty::tls::with_opt(|opt_tcx| { - if let Some(tcx) = opt_tcx { - if let Some(def_id) = node.extract_def_id(tcx) { - write!(f, "{}", tcx.def_path_debug_str(def_id))?; - } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*node) { - write!(f, "{}", s)?; - } else { - write!(f, "{}", node.hash)?; - } - } else { - write!(f, "{}", node.hash)?; - } - Ok(()) - })?; - - write!(f, ")") - } - - fn with_deps(task_deps: Option<&Lock>, op: OP) -> R - where - OP: FnOnce() -> R, - { - ty::tls::with_context(|icx| { - let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; - - ty::tls::enter_context(&icx, |_| op()) - }) - } - - fn read_deps(op: OP) -> () - where - OP: for<'a> FnOnce(Option<&'a Lock>) -> (), - { - ty::tls::with_context_opt(|icx| { - let icx = if let Some(icx) = icx { icx } else { return }; - op(icx.task_deps) - }) - } -} - -impl<'tcx> DepContext for TyCtxt<'tcx> { - type DepKind = DepKind; - type StableHashingContext = StableHashingContext<'tcx>; - - fn create_stable_hashing_context(&self) -> Self::StableHashingContext { - TyCtxt::create_stable_hashing_context(*self) - } - - fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool { - // FIXME: This match is just a workaround for incremental bugs and should - // be removed. https://github.com/rust-lang/rust/issues/62649 is one such - // bug that must be fixed before removing this. - match dep_node.kind { - DepKind::hir_owner | DepKind::hir_owner_nodes | DepKind::CrateMetadata => { - if let Some(def_id) = dep_node.extract_def_id(*self) { - if def_id_corresponds_to_hir_dep_node(*self, def_id) { - if dep_node.kind == DepKind::CrateMetadata { - // The `DefPath` has corresponding node, - // and that node should have been marked - // either red or green in `data.colors`. - bug!( - "DepNode {:?} should have been \ - pre-marked as red or green but wasn't.", - dep_node - ); - } - } else { - // This `DefPath` does not have a - // corresponding `DepNode` (e.g. a - // struct field), and the ` DefPath` - // collided with the `DefPath` of a - // proper item that existed in the - // previous compilation session. - // - // Since the given `DefPath` does not - // denote the item that previously - // existed, we just fail to mark green. - return false; - } - } else { - // If the node does not exist anymore, we - // just fail to mark green. - return false; - } - } - _ => { - // For other kinds of nodes it's OK to be - // forced. - } - } - - debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); - ty::query::force_from_dep_node(*self, dep_node) - } - - fn has_errors_or_delayed_span_bugs(&self) -> bool { - self.sess.has_errors_or_delayed_span_bugs() - } - - fn diagnostic(&self) -> &rustc_errors::Handler { - self.sess.diagnostic() - } - - // Interactions with on_disk_cache - fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { - try_load_from_on_disk_cache(*self, dep_node) - } - - fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec { - self.queries.on_disk_cache.load_diagnostics(*self, prev_dep_node_index) - } - - fn store_diagnostics(&self, dep_node_index: DepNodeIndex, diagnostics: ThinVec) { - self.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics) - } - - fn profiler(&self) -> &SelfProfilerRef { - &self.prof - } -} - -fn def_id_corresponds_to_hir_dep_node(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - def_id.index == hir_id.owner.local_def_index -} - -impl rustc_query_system::HashStableContext for StableHashingContext<'_> { - fn debug_dep_tasks(&self) -> bool { - self.sess().opts.debugging_opts.dep_tasks - } -} - -impl rustc_query_system::HashStableContextProvider> for TyCtxt<'tcx> { - fn get_stable_hashing_context(&self) -> StableHashingContext<'tcx> { - self.create_stable_hashing_context() - } -} - -impl rustc_query_system::HashStableContextProvider> - for StableHashingContext<'a> -{ - fn get_stable_hashing_context(&self) -> Self { - self.clone() - } -} diff --git a/src/librustc/dep_graph/safe.rs b/src/librustc/dep_graph/safe.rs deleted file mode 100644 index 47a1c09672ff6..0000000000000 --- a/src/librustc/dep_graph/safe.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! The `DepGraphSafe` trait - -use crate::ty::TyCtxt; - -pub use rustc_query_system::dep_graph::{AssertDepGraphSafe, DepGraphSafe}; - -/// The type context itself can be used to access all kinds of tracked -/// state, but those accesses should always generate read events. -impl<'tcx> DepGraphSafe for TyCtxt<'tcx> {} diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs deleted file mode 100644 index 7024e86f95d06..0000000000000 --- a/src/librustc/hir/map/blocks.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! This module provides a simplified abstraction for working with -//! code blocks identified by their integer `NodeId`. In particular, -//! it captures a common set of attributes that all "function-like -//! things" (represented by `FnLike` instances) share. For example, -//! all `FnLike` instances have a type signature (be it explicit or -//! inferred). And all `FnLike` instances have a body, i.e., the code -//! that is run when the function-like thing it represents is invoked. -//! -//! With the above abstraction in place, one can treat the program -//! text as a collection of blocks of code (and most such blocks are -//! nested within a uniquely determined `FnLike`), and users can ask -//! for the `Code` associated with a particular NodeId. - -use crate::hir::map::Map; -use rustc_ast::ast::{Attribute, Ident}; -use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Expr, FnDecl, Node}; -use rustc_span::Span; - -/// An FnLikeNode is a Node that is like a fn, in that it has a decl -/// and a body (as well as a NodeId, a span, etc). -/// -/// More specifically, it is one of either: -/// -/// - A function item, -/// - A closure expr (i.e., an ExprKind::Closure), or -/// - The default implementation for a trait method. -/// -/// To construct one, use the `Code::from_node` function. -#[derive(Copy, Clone, Debug)] -pub struct FnLikeNode<'a> { - node: Node<'a>, -} - -/// MaybeFnLike wraps a method that indicates if an object -/// corresponds to some FnLikeNode. -trait MaybeFnLike { - fn is_fn_like(&self) -> bool; -} - -impl MaybeFnLike for hir::Item<'_> { - fn is_fn_like(&self) -> bool { - match self.kind { - hir::ItemKind::Fn(..) => true, - _ => false, - } - } -} - -impl MaybeFnLike for hir::ImplItem<'_> { - fn is_fn_like(&self) -> bool { - match self.kind { - hir::ImplItemKind::Fn(..) => true, - _ => false, - } - } -} - -impl MaybeFnLike for hir::TraitItem<'_> { - fn is_fn_like(&self) -> bool { - match self.kind { - hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => true, - _ => false, - } - } -} - -impl MaybeFnLike for hir::Expr<'_> { - fn is_fn_like(&self) -> bool { - match self.kind { - hir::ExprKind::Closure(..) => true, - _ => false, - } - } -} - -/// Carries either an FnLikeNode or a Expr, as these are the two -/// constructs that correspond to "code" (as in, something from which -/// we can construct a control-flow graph). -#[derive(Copy, Clone)] -pub enum Code<'a> { - FnLike(FnLikeNode<'a>), - Expr(&'a Expr<'a>), -} - -impl<'a> Code<'a> { - pub fn id(&self) -> hir::HirId { - match *self { - Code::FnLike(node) => node.id(), - Code::Expr(block) => block.hir_id, - } - } - - /// Attempts to construct a Code from presumed FnLike or Expr node input. - pub fn from_node(map: &Map<'a>, id: hir::HirId) -> Option> { - match map.get(id) { - Node::Block(_) => { - // Use the parent, hopefully an expression node. - Code::from_node(map, map.get_parent_node(id)) - } - Node::Expr(expr) => Some(Code::Expr(expr)), - node => FnLikeNode::from_node(node).map(Code::FnLike), - } - } -} - -/// These are all the components one can extract from a fn item for -/// use when implementing FnLikeNode operations. -struct ItemFnParts<'a> { - ident: Ident, - decl: &'a hir::FnDecl<'a>, - header: hir::FnHeader, - vis: &'a hir::Visibility<'a>, - generics: &'a hir::Generics<'a>, - body: hir::BodyId, - id: hir::HirId, - span: Span, - attrs: &'a [Attribute], -} - -/// These are all the components one can extract from a closure expr -/// for use when implementing FnLikeNode operations. -struct ClosureParts<'a> { - decl: &'a FnDecl<'a>, - body: hir::BodyId, - id: hir::HirId, - span: Span, - attrs: &'a [Attribute], -} - -impl<'a> ClosureParts<'a> { - fn new( - d: &'a FnDecl<'a>, - b: hir::BodyId, - id: hir::HirId, - s: Span, - attrs: &'a [Attribute], - ) -> Self { - ClosureParts { decl: d, body: b, id, span: s, attrs } - } -} - -impl<'a> FnLikeNode<'a> { - /// Attempts to construct a FnLikeNode from presumed FnLike node input. - pub fn from_node(node: Node<'_>) -> Option> { - let fn_like = match node { - Node::Item(item) => item.is_fn_like(), - Node::TraitItem(tm) => tm.is_fn_like(), - Node::ImplItem(it) => it.is_fn_like(), - Node::Expr(e) => e.is_fn_like(), - _ => false, - }; - fn_like.then_some(FnLikeNode { node }) - } - - pub fn body(self) -> hir::BodyId { - self.handle( - |i: ItemFnParts<'a>| i.body, - |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _, _| body, - |c: ClosureParts<'a>| c.body, - ) - } - - pub fn decl(self) -> &'a FnDecl<'a> { - self.handle( - |i: ItemFnParts<'a>| &*i.decl, - |_, _, sig: &'a hir::FnSig<'a>, _, _, _, _| &sig.decl, - |c: ClosureParts<'a>| c.decl, - ) - } - - pub fn span(self) -> Span { - self.handle( - |i: ItemFnParts<'_>| i.span, - |_, _, _: &'a hir::FnSig<'a>, _, _, span, _| span, - |c: ClosureParts<'_>| c.span, - ) - } - - pub fn id(self) -> hir::HirId { - self.handle( - |i: ItemFnParts<'_>| i.id, - |id, _, _: &'a hir::FnSig<'a>, _, _, _, _| id, - |c: ClosureParts<'_>| c.id, - ) - } - - pub fn constness(self) -> hir::Constness { - self.kind().header().map_or(hir::Constness::NotConst, |header| header.constness) - } - - pub fn asyncness(self) -> hir::IsAsync { - self.kind().header().map_or(hir::IsAsync::NotAsync, |header| header.asyncness) - } - - pub fn unsafety(self) -> hir::Unsafety { - self.kind().header().map_or(hir::Unsafety::Normal, |header| header.unsafety) - } - - pub fn kind(self) -> FnKind<'a> { - let item = |p: ItemFnParts<'a>| -> FnKind<'a> { - FnKind::ItemFn(p.ident, p.generics, p.header, p.vis, p.attrs) - }; - let closure = |c: ClosureParts<'a>| FnKind::Closure(c.attrs); - let method = |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _, attrs| { - FnKind::Method(ident, sig, vis, attrs) - }; - self.handle(item, method, closure) - } - - fn handle(self, item_fn: I, method: M, closure: C) -> A - where - I: FnOnce(ItemFnParts<'a>) -> A, - M: FnOnce( - hir::HirId, - Ident, - &'a hir::FnSig<'a>, - Option<&'a hir::Visibility<'a>>, - hir::BodyId, - Span, - &'a [Attribute], - ) -> A, - C: FnOnce(ClosureParts<'a>) -> A, - { - match self.node { - Node::Item(i) => match i.kind { - hir::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts { - id: i.hir_id, - ident: i.ident, - decl: &sig.decl, - body: block, - vis: &i.vis, - span: i.span, - attrs: &i.attrs, - header: sig.header, - generics, - }), - _ => bug!("item FnLikeNode that is not fn-like"), - }, - Node::TraitItem(ti) => match ti.kind { - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - method(ti.hir_id, ti.ident, sig, None, body, ti.span, &ti.attrs) - } - _ => bug!("trait method FnLikeNode that is not fn-like"), - }, - Node::ImplItem(ii) => match ii.kind { - hir::ImplItemKind::Fn(ref sig, body) => { - method(ii.hir_id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs) - } - _ => bug!("impl method FnLikeNode that is not fn-like"), - }, - Node::Expr(e) => match e.kind { - hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => { - closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs)) - } - _ => bug!("expr FnLikeNode that is not fn-like"), - }, - _ => bug!("other FnLikeNode that is not fn-like"), - } - } -} diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs deleted file mode 100644 index eb8e57743b8f8..0000000000000 --- a/src/librustc/hir/map/mod.rs +++ /dev/null @@ -1,1168 +0,0 @@ -use self::collector::NodeCollector; - -use crate::hir::{Owner, OwnerNodes}; -use crate::ty::query::Providers; -use crate::ty::TyCtxt; -use rustc_ast::ast::{self, Name, NodeId}; -use rustc_data_structures::svh::Svh; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -pub use rustc_hir::definitions; -pub use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; -pub use rustc_hir::definitions::{Definitions, DisambiguatedDefPathData}; -use rustc_hir::intravisit; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::print::Nested; -use rustc_hir::*; -use rustc_index::vec::IndexVec; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::kw; -use rustc_span::Span; -use rustc_target::spec::abi::Abi; - -pub mod blocks; -mod collector; -mod hir_id_validator; -pub use hir_id_validator::check_crate; - -/// Represents an entry and its parent `HirId`. -#[derive(Copy, Clone, Debug)] -pub struct Entry<'hir> { - parent: HirId, - node: Node<'hir>, -} - -impl<'hir> Entry<'hir> { - fn parent_node(self) -> Option { - match self.node { - Node::Crate(_) | Node::MacroDef(_) => None, - _ => Some(self.parent), - } - } -} - -fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { - match node { - Node::Item(ref item) => match item.kind { - ItemKind::Fn(ref sig, _, _) => Some(&sig.decl), - _ => None, - }, - - Node::TraitItem(ref item) => match item.kind { - TraitItemKind::Fn(ref sig, _) => Some(&sig.decl), - _ => None, - }, - - Node::ImplItem(ref item) => match item.kind { - ImplItemKind::Fn(ref sig, _) => Some(&sig.decl), - _ => None, - }, - - Node::Expr(ref expr) => match expr.kind { - ExprKind::Closure(_, ref fn_decl, ..) => Some(fn_decl), - _ => None, - }, - - _ => None, - } -} - -fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { - match &node { - Node::Item(item) => match &item.kind { - ItemKind::Fn(sig, _, _) => Some(sig), - _ => None, - }, - - Node::TraitItem(item) => match &item.kind { - TraitItemKind::Fn(sig, _) => Some(sig), - _ => None, - }, - - Node::ImplItem(item) => match &item.kind { - ImplItemKind::Fn(sig, _) => Some(sig), - _ => None, - }, - - _ => None, - } -} - -fn associated_body<'hir>(node: Node<'hir>) -> Option { - match node { - Node::Item(item) => match item.kind { - ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body) => { - Some(body) - } - _ => None, - }, - - Node::TraitItem(item) => match item.kind { - TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)) => { - Some(body) - } - _ => None, - }, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body) => Some(body), - _ => None, - }, - - Node::AnonConst(constant) => Some(constant.body), - - Node::Expr(expr) => match expr.kind { - ExprKind::Closure(.., body, _, _) => Some(body), - _ => None, - }, - - _ => None, - } -} - -fn is_body_owner<'hir>(node: Node<'hir>, hir_id: HirId) -> bool { - match associated_body(node) { - Some(b) => b.hir_id == hir_id, - None => false, - } -} - -pub(super) struct HirOwnerData<'hir> { - pub(super) signature: Option<&'hir Owner<'hir>>, - pub(super) with_bodies: Option<&'hir mut OwnerNodes<'hir>>, -} - -pub struct IndexedHir<'hir> { - /// The SVH of the local crate. - pub crate_hash: Svh, - - pub(super) map: IndexVec>, -} - -#[derive(Copy, Clone)] -pub struct Map<'hir> { - pub(super) tcx: TyCtxt<'hir>, -} - -/// An iterator that walks up the ancestor tree of a given `HirId`. -/// Constructed using `tcx.hir().parent_iter(hir_id)`. -pub struct ParentHirIterator<'map, 'hir> { - current_id: HirId, - map: &'map Map<'hir>, -} - -impl<'hir> Iterator for ParentHirIterator<'_, 'hir> { - type Item = (HirId, Node<'hir>); - - fn next(&mut self) -> Option { - if self.current_id == CRATE_HIR_ID { - return None; - } - loop { - // There are nodes that do not have entries, so we need to skip them. - let parent_id = self.map.get_parent_node(self.current_id); - - if parent_id == self.current_id { - self.current_id = CRATE_HIR_ID; - return None; - } - - self.current_id = parent_id; - if let Some(entry) = self.map.find_entry(parent_id) { - return Some((parent_id, entry.node)); - } - // If this `HirId` doesn't have an `Entry`, skip it and look for its `parent_id`. - } - } -} - -impl<'hir> Map<'hir> { - pub fn krate(&self) -> &'hir Crate<'hir> { - self.tcx.hir_crate(LOCAL_CRATE) - } - - #[inline] - pub fn definitions(&self) -> &'hir Definitions { - &self.tcx.definitions - } - - pub fn def_key(&self, def_id: LocalDefId) -> DefKey { - self.tcx.definitions.def_key(def_id) - } - - pub fn def_path_from_hir_id(&self, id: HirId) -> Option { - self.opt_local_def_id(id).map(|def_id| self.def_path(def_id.expect_local())) - } - - pub fn def_path(&self, def_id: LocalDefId) -> DefPath { - self.tcx.definitions.def_path(def_id) - } - - // FIXME(eddyb) this function can and should return `LocalDefId`. - #[inline] - pub fn local_def_id_from_node_id(&self, node: NodeId) -> DefId { - self.opt_local_def_id_from_node_id(node).unwrap_or_else(|| { - let hir_id = self.node_to_hir_id(node); - bug!( - "local_def_id_from_node_id: no entry for `{}`, which has a map of `{:?}`", - node, - self.find_entry(hir_id) - ) - }) - } - - // FIXME(eddyb) this function can and should return `LocalDefId`. - #[inline] - pub fn local_def_id(&self, hir_id: HirId) -> DefId { - self.opt_local_def_id(hir_id).unwrap_or_else(|| { - bug!( - "local_def_id: no entry for `{:?}`, which has a map of `{:?}`", - hir_id, - self.find_entry(hir_id) - ) - }) - } - - #[inline] - pub fn opt_local_def_id(&self, hir_id: HirId) -> Option { - let node_id = self.hir_to_node_id(hir_id); - self.opt_local_def_id_from_node_id(node_id) - } - - #[inline] - pub fn opt_local_def_id_from_node_id(&self, node: NodeId) -> Option { - Some(self.tcx.definitions.opt_local_def_id(node)?.to_def_id()) - } - - #[inline] - pub fn as_local_node_id(&self, def_id: DefId) -> Option { - self.tcx.definitions.as_local_node_id(def_id) - } - - #[inline] - pub fn as_local_hir_id(&self, def_id: DefId) -> Option { - self.tcx.definitions.as_local_hir_id(def_id) - } - - #[inline] - pub fn hir_to_node_id(&self, hir_id: HirId) -> NodeId { - self.tcx.definitions.hir_to_node_id(hir_id) - } - - #[inline] - pub fn node_to_hir_id(&self, node_id: NodeId) -> HirId { - self.tcx.definitions.node_to_hir_id(node_id) - } - - #[inline] - pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { - self.tcx.definitions.local_def_id_to_hir_id(def_id) - } - - pub fn def_kind(&self, hir_id: HirId) -> Option { - let node = self.find(hir_id)?; - - Some(match node { - Node::Item(item) => match item.kind { - ItemKind::Static(..) => DefKind::Static, - ItemKind::Const(..) => DefKind::Const, - ItemKind::Fn(..) => DefKind::Fn, - ItemKind::Mod(..) => DefKind::Mod, - ItemKind::OpaqueTy(..) => DefKind::OpaqueTy, - ItemKind::TyAlias(..) => DefKind::TyAlias, - ItemKind::Enum(..) => DefKind::Enum, - ItemKind::Struct(..) => DefKind::Struct, - ItemKind::Union(..) => DefKind::Union, - ItemKind::Trait(..) => DefKind::Trait, - ItemKind::TraitAlias(..) => DefKind::TraitAlias, - ItemKind::ExternCrate(_) - | ItemKind::Use(..) - | ItemKind::ForeignMod(..) - | ItemKind::GlobalAsm(..) - | ItemKind::Impl { .. } => return None, - }, - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(..) => DefKind::Fn, - ForeignItemKind::Static(..) => DefKind::Static, - ForeignItemKind::Type => DefKind::ForeignTy, - }, - Node::TraitItem(item) => match item.kind { - TraitItemKind::Const(..) => DefKind::AssocConst, - TraitItemKind::Fn(..) => DefKind::AssocFn, - TraitItemKind::Type(..) => DefKind::AssocTy, - }, - Node::ImplItem(item) => match item.kind { - ImplItemKind::Const(..) => DefKind::AssocConst, - ImplItemKind::Fn(..) => DefKind::AssocFn, - ImplItemKind::TyAlias(..) => DefKind::AssocTy, - ImplItemKind::OpaqueTy(..) => DefKind::AssocOpaqueTy, - }, - Node::Variant(_) => DefKind::Variant, - Node::Ctor(variant_data) => { - // FIXME(eddyb) is this even possible, if we have a `Node::Ctor`? - variant_data.ctor_hir_id()?; - - let ctor_of = match self.find(self.get_parent_node(hir_id)) { - Some(Node::Item(..)) => def::CtorOf::Struct, - Some(Node::Variant(..)) => def::CtorOf::Variant, - _ => unreachable!(), - }; - DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data)) - } - Node::AnonConst(_) - | Node::Field(_) - | Node::Expr(_) - | Node::Stmt(_) - | Node::PathSegment(_) - | Node::Ty(_) - | Node::TraitRef(_) - | Node::Pat(_) - | Node::Binding(_) - | Node::Local(_) - | Node::Param(_) - | Node::Arm(_) - | Node::Lifetime(_) - | Node::Visibility(_) - | Node::Block(_) - | Node::Crate(_) => return None, - Node::MacroDef(_) => DefKind::Macro(MacroKind::Bang), - Node::GenericParam(param) => match param.kind { - GenericParamKind::Lifetime { .. } => return None, - GenericParamKind::Type { .. } => DefKind::TyParam, - GenericParamKind::Const { .. } => DefKind::ConstParam, - }, - }) - } - - fn find_entry(&self, id: HirId) -> Option> { - if id.local_id == ItemLocalId::from_u32(0) { - let owner = self.tcx.hir_owner(id.owner); - owner.map(|owner| Entry { parent: owner.parent, node: owner.node }) - } else { - let owner = self.tcx.hir_owner_nodes(id.owner); - owner.and_then(|owner| { - let node = owner.nodes[id.local_id].as_ref(); - // FIXME(eddyb) use a single generic type insted of having both - // `Entry` and `ParentedNode`, which are effectively the same. - // Alternatively, rewrite code using `Entry` to use `ParentedNode`. - node.map(|node| Entry { - parent: HirId { owner: id.owner, local_id: node.parent }, - node: node.node, - }) - }) - } - } - - fn get_entry(&self, id: HirId) -> Entry<'hir> { - self.find_entry(id).unwrap() - } - - pub fn item(&self, id: HirId) -> &'hir Item<'hir> { - match self.find(id).unwrap() { - Node::Item(item) => item, - _ => bug!(), - } - } - - pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { - match self.find(id.hir_id).unwrap() { - Node::TraitItem(item) => item, - _ => bug!(), - } - } - - pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { - match self.find(id.hir_id).unwrap() { - Node::ImplItem(item) => item, - _ => bug!(), - } - } - - pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { - self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies.get(&id.hir_id.local_id).unwrap() - } - - pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { - if let Some(node) = self.find(hir_id) { - fn_decl(node) - } else { - bug!("no node for hir_id `{}`", hir_id) - } - } - - pub fn fn_sig_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { - if let Some(node) = self.find(hir_id) { - fn_sig(node) - } else { - bug!("no node for hir_id `{}`", hir_id) - } - } - - /// Returns the `HirId` that corresponds to the definition of - /// which this is the body of, i.e., a `fn`, `const` or `static` - /// item (possibly associated), a closure, or a `hir::AnonConst`. - pub fn body_owner(&self, BodyId { hir_id }: BodyId) -> HirId { - let parent = self.get_parent_node(hir_id); - assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id))); - parent - } - - // FIXME(eddyb) this function can and should return `LocalDefId`. - pub fn body_owner_def_id(&self, id: BodyId) -> DefId { - self.local_def_id(self.body_owner(id)) - } - - /// Given a `HirId`, returns the `BodyId` associated with it, - /// if the node is a body owner, otherwise returns `None`. - pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { - if let Some(node) = self.find(hir_id) { - associated_body(node) - } else { - bug!("no entry for id `{}`", hir_id) - } - } - - /// Given a body owner's id, returns the `BodyId` associated with it. - pub fn body_owned_by(&self, id: HirId) -> BodyId { - self.maybe_body_owned_by(id).unwrap_or_else(|| { - span_bug!( - self.span(id), - "body_owned_by: {} has no associated body", - self.node_to_string(id) - ); - }) - } - - pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { - match self.get(id) { - Node::Item(&Item { kind: ItemKind::Const(..), .. }) - | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) - | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), .. }) - | Node::AnonConst(_) => BodyOwnerKind::Const, - Node::Ctor(..) - | Node::Item(&Item { kind: ItemKind::Fn(..), .. }) - | Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) - | Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(..), .. }) => BodyOwnerKind::Fn, - Node::Item(&Item { kind: ItemKind::Static(_, m, _), .. }) => BodyOwnerKind::Static(m), - Node::Expr(&Expr { kind: ExprKind::Closure(..), .. }) => BodyOwnerKind::Closure, - node => bug!("{:#?} is not a body node", node), - } - } - - pub fn ty_param_owner(&self, id: HirId) -> HirId { - match self.get(id) { - Node::Item(&Item { kind: ItemKind::Trait(..), .. }) - | Node::Item(&Item { kind: ItemKind::TraitAlias(..), .. }) => id, - Node::GenericParam(_) => self.get_parent_node(id), - _ => bug!("ty_param_owner: {} not a type parameter", self.node_to_string(id)), - } - } - - pub fn ty_param_name(&self, id: HirId) -> Name { - match self.get(id) { - Node::Item(&Item { kind: ItemKind::Trait(..), .. }) - | Node::Item(&Item { kind: ItemKind::TraitAlias(..), .. }) => kw::SelfUpper, - Node::GenericParam(param) => param.name.ident().name, - _ => bug!("ty_param_name: {} not a type parameter", self.node_to_string(id)), - } - } - - pub fn trait_impls(&self, trait_did: DefId) -> &'hir [HirId] { - self.tcx.all_local_trait_impls(LOCAL_CRATE).get(&trait_did).map_or(&[], |xs| &xs[..]) - } - - /// Gets the attributes on the crate. This is preferable to - /// invoking `krate.attrs` because it registers a tighter - /// dep-graph access. - pub fn krate_attrs(&self) -> &'hir [ast::Attribute] { - match self.get_entry(CRATE_HIR_ID).node { - Node::Crate(item) => item.attrs, - _ => bug!(), - } - } - - pub fn get_module(&self, module: DefId) -> (&'hir Mod<'hir>, Span, HirId) { - let hir_id = self.as_local_hir_id(module).unwrap(); - match self.get_entry(hir_id).node { - Node::Item(&Item { span, kind: ItemKind::Mod(ref m), .. }) => (m, span, hir_id), - Node::Crate(item) => (&item.module, item.span, hir_id), - node => panic!("not a module: {:?}", node), - } - } - - pub fn visit_item_likes_in_module(&self, module: DefId, visitor: &mut V) - where - V: ItemLikeVisitor<'hir>, - { - let module = self.tcx.hir_module_items(module.expect_local()); - - for id in &module.items { - visitor.visit_item(self.expect_item(*id)); - } - - for id in &module.trait_items { - visitor.visit_trait_item(self.expect_trait_item(id.hir_id)); - } - - for id in &module.impl_items { - visitor.visit_impl_item(self.expect_impl_item(id.hir_id)); - } - } - - /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. - pub fn get(&self, id: HirId) -> Node<'hir> { - self.find(id).unwrap_or_else(|| bug!("couldn't find hir id {} in the HIR map", id)) - } - - pub fn get_if_local(&self, id: DefId) -> Option> { - self.as_local_hir_id(id).map(|id| self.get(id)) - } - - pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics<'hir>> { - self.get_if_local(id).and_then(|node| match node { - Node::ImplItem(ref impl_item) => Some(&impl_item.generics), - Node::TraitItem(ref trait_item) => Some(&trait_item.generics), - Node::Item(ref item) => match item.kind { - ItemKind::Fn(_, ref generics, _) - | ItemKind::TyAlias(_, ref generics) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) - | ItemKind::Trait(_, _, ref generics, ..) - | ItemKind::TraitAlias(ref generics, _) - | ItemKind::Impl { ref generics, .. } => Some(generics), - _ => None, - }, - _ => None, - }) - } - - /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. - pub fn find(&self, hir_id: HirId) -> Option> { - self.find_entry(hir_id).and_then(|entry| { - if let Node::Crate(..) = entry.node { None } else { Some(entry.node) } - }) - } - - /// Similar to `get_parent`; returns the parent HIR Id, or just `hir_id` if there - /// is no parent. Note that the parent may be `CRATE_HIR_ID`, which is not itself - /// present in the map, so passing the return value of `get_parent_node` to - /// `get` may in fact panic. - /// This function returns the immediate parent in the HIR, whereas `get_parent` - /// returns the enclosing item. Note that this might not be the actual parent - /// node in the HIR -- some kinds of nodes are not in the map and these will - /// never appear as the parent node. Thus, you can always walk the parent nodes - /// from a node to the root of the HIR (unless you get back the same ID here, - /// which can happen if the ID is not in the map itself or is just weird). - pub fn get_parent_node(&self, hir_id: HirId) -> HirId { - self.get_entry(hir_id).parent_node().unwrap_or(hir_id) - } - - /// Returns an iterator for the nodes in the ancestor tree of the `current_id` - /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`. - pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> { - ParentHirIterator { current_id, map: self } - } - - /// Checks if the node is an argument. An argument is a local variable whose - /// immediate parent is an item or a closure. - pub fn is_argument(&self, id: HirId) -> bool { - match self.find(id) { - Some(Node::Binding(_)) => (), - _ => return false, - } - match self.find(self.get_parent_node(id)) { - Some(Node::Item(_)) | Some(Node::TraitItem(_)) | Some(Node::ImplItem(_)) => true, - Some(Node::Expr(e)) => match e.kind { - ExprKind::Closure(..) => true, - _ => false, - }, - _ => false, - } - } - - /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. - /// Used exclusively for diagnostics, to avoid suggestion function calls. - pub fn is_const_context(&self, hir_id: HirId) -> bool { - let parent_id = self.get_parent_item(hir_id); - match self.get(parent_id) { - Node::Item(&Item { kind: ItemKind::Const(..), .. }) - | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) - | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), .. }) - | Node::AnonConst(_) - | Node::Item(&Item { kind: ItemKind::Static(..), .. }) => true, - Node::Item(&Item { kind: ItemKind::Fn(ref sig, ..), .. }) => { - sig.header.constness == Constness::Const - } - _ => false, - } - } - - /// Whether `hir_id` corresponds to a `mod` or a crate. - pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { - match self.get_entry(hir_id) { - Entry { node: Node::Item(Item { kind: ItemKind::Mod(_), .. }), .. } - | Entry { node: Node::Crate(..), .. } => true, - _ => false, - } - } - - /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a - /// `while` or `loop` before reaching it, as block tail returns are not - /// available in them. - /// - /// ``` - /// fn foo(x: usize) -> bool { - /// if x == 1 { - /// true // If `get_return_block` gets passed the `id` corresponding - /// } else { // to this, it will return `foo`'s `HirId`. - /// false - /// } - /// } - /// ``` - /// - /// ``` - /// fn foo(x: usize) -> bool { - /// loop { - /// true // If `get_return_block` gets passed the `id` corresponding - /// } // to this, it will return `None`. - /// false - /// } - /// ``` - pub fn get_return_block(&self, id: HirId) -> Option { - let mut iter = self.parent_iter(id).peekable(); - let mut ignore_tail = false; - if let Some(entry) = self.find_entry(id) { - if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = entry.node { - // When dealing with `return` statements, we don't care about climbing only tail - // expressions. - ignore_tail = true; - } - } - while let Some((hir_id, node)) = iter.next() { - if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { - match next_node { - Node::Block(Block { expr: None, .. }) => return None, - Node::Block(Block { expr: Some(expr), .. }) => { - if hir_id != expr.hir_id { - // The current node is not the tail expression of its parent. - return None; - } - } - _ => {} - } - } - match node { - Node::Item(_) - | Node::ForeignItem(_) - | Node::TraitItem(_) - | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }) - | Node::ImplItem(_) => return Some(hir_id), - Node::Expr(ref expr) => { - match expr.kind { - // Ignore `return`s on the first iteration - ExprKind::Loop(..) | ExprKind::Ret(..) => return None, - _ => {} - } - } - Node::Local(_) => return None, - _ => {} - } - } - None - } - - /// Retrieves the `HirId` for `id`'s parent item, or `id` itself if no - /// parent item is in this map. The "parent item" is the closest parent node - /// in the HIR which is recorded by the map and is an item, either an item - /// in a module, trait, or impl. - pub fn get_parent_item(&self, hir_id: HirId) -> HirId { - for (hir_id, node) in self.parent_iter(hir_id) { - match node { - Node::Crate(_) - | Node::Item(_) - | Node::ForeignItem(_) - | Node::TraitItem(_) - | Node::ImplItem(_) => return hir_id, - _ => {} - } - } - hir_id - } - - /// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no - /// module parent is in this map. - pub(super) fn get_module_parent_node(&self, hir_id: HirId) -> HirId { - for (hir_id, node) in self.parent_iter(hir_id) { - if let Node::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { - return hir_id; - } - } - CRATE_HIR_ID - } - - /// When on a match arm tail expression or on a match arm, give back the enclosing `match` - /// expression. - /// - /// Used by error reporting when there's a type error in a match arm caused by the `match` - /// expression needing to be unit. - pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> { - for (_, node) in self.parent_iter(hir_id) { - match node { - Node::Item(_) | Node::ForeignItem(_) | Node::TraitItem(_) | Node::ImplItem(_) => { - break; - } - Node::Expr(expr) => match expr.kind { - ExprKind::Match(_, _, _) => return Some(expr), - _ => {} - }, - Node::Stmt(stmt) => match stmt.kind { - StmtKind::Local(_) => break, - _ => {} - }, - _ => {} - } - } - None - } - - /// Returns the nearest enclosing scope. A scope is roughly an item or block. - pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option { - for (hir_id, node) in self.parent_iter(hir_id) { - if match node { - Node::Item(i) => match i.kind { - ItemKind::Fn(..) - | ItemKind::Mod(..) - | ItemKind::Enum(..) - | ItemKind::Struct(..) - | ItemKind::Union(..) - | ItemKind::Trait(..) - | ItemKind::Impl { .. } => true, - _ => false, - }, - Node::ForeignItem(fi) => match fi.kind { - ForeignItemKind::Fn(..) => true, - _ => false, - }, - Node::TraitItem(ti) => match ti.kind { - TraitItemKind::Fn(..) => true, - _ => false, - }, - Node::ImplItem(ii) => match ii.kind { - ImplItemKind::Fn(..) => true, - _ => false, - }, - Node::Block(_) => true, - _ => false, - } { - return Some(hir_id); - } - } - None - } - - /// Returns the defining scope for an opaque type definition. - pub fn get_defining_scope(&self, id: HirId) -> HirId { - let mut scope = id; - loop { - scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); - if scope == CRATE_HIR_ID { - return CRATE_HIR_ID; - } - match self.get(scope) { - Node::Item(i) => match i.kind { - ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }) => {} - _ => break, - }, - Node::Block(_) => {} - _ => break, - } - } - scope - } - - // FIXME(eddyb) this function can and should return `LocalDefId`. - pub fn get_parent_did(&self, id: HirId) -> DefId { - self.local_def_id(self.get_parent_item(id)) - } - - pub fn get_foreign_abi(&self, hir_id: HirId) -> Abi { - let parent = self.get_parent_item(hir_id); - if let Some(entry) = self.find_entry(parent) { - if let Entry { - node: Node::Item(Item { kind: ItemKind::ForeignMod(ref nm), .. }), .. - } = entry - { - return nm.abi; - } - } - bug!("expected foreign mod or inlined parent, found {}", self.node_to_string(parent)) - } - - pub fn expect_item(&self, id: HirId) -> &'hir Item<'hir> { - match self.find(id) { - Some(Node::Item(item)) => item, - _ => bug!("expected item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_impl_item(&self, id: HirId) -> &'hir ImplItem<'hir> { - match self.find(id) { - Some(Node::ImplItem(item)) => item, - _ => bug!("expected impl item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_trait_item(&self, id: HirId) -> &'hir TraitItem<'hir> { - match self.find(id) { - Some(Node::TraitItem(item)) => item, - _ => bug!("expected trait item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_variant_data(&self, id: HirId) -> &'hir VariantData<'hir> { - match self.find(id) { - Some(Node::Item(i)) => match i.kind { - ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => { - struct_def - } - _ => bug!("struct ID bound to non-struct {}", self.node_to_string(id)), - }, - Some(Node::Variant(variant)) => &variant.data, - Some(Node::Ctor(data)) => data, - _ => bug!("expected struct or variant, found {}", self.node_to_string(id)), - } - } - - pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> { - match self.find(id) { - Some(Node::Variant(variant)) => variant, - _ => bug!("expected variant, found {}", self.node_to_string(id)), - } - } - - pub fn expect_foreign_item(&self, id: HirId) -> &'hir ForeignItem<'hir> { - match self.find(id) { - Some(Node::ForeignItem(item)) => item, - _ => bug!("expected foreign item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> { - match self.find(id) { - Some(Node::Expr(expr)) => expr, - _ => bug!("expected expr, found {}", self.node_to_string(id)), - } - } - - pub fn opt_name(&self, id: HirId) -> Option { - Some(match self.get(id) { - Node::Item(i) => i.ident.name, - Node::ForeignItem(fi) => fi.ident.name, - Node::ImplItem(ii) => ii.ident.name, - Node::TraitItem(ti) => ti.ident.name, - Node::Variant(v) => v.ident.name, - Node::Field(f) => f.ident.name, - Node::Lifetime(lt) => lt.name.ident().name, - Node::GenericParam(param) => param.name.ident().name, - Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name, - Node::Ctor(..) => self.name(self.get_parent_item(id)), - _ => return None, - }) - } - - pub fn name(&self, id: HirId) -> Name { - match self.opt_name(id) { - Some(name) => name, - None => bug!("no name for {}", self.node_to_string(id)), - } - } - - /// Given a node ID, gets a list of attributes associated with the AST - /// corresponding to the node-ID. - pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { - let attrs = match self.find_entry(id).map(|entry| entry.node) { - Some(Node::Param(a)) => Some(&a.attrs[..]), - Some(Node::Local(l)) => Some(&l.attrs[..]), - Some(Node::Item(i)) => Some(&i.attrs[..]), - Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]), - Some(Node::TraitItem(ref ti)) => Some(&ti.attrs[..]), - Some(Node::ImplItem(ref ii)) => Some(&ii.attrs[..]), - Some(Node::Variant(ref v)) => Some(&v.attrs[..]), - Some(Node::Field(ref f)) => Some(&f.attrs[..]), - Some(Node::Expr(ref e)) => Some(&*e.attrs), - Some(Node::Stmt(ref s)) => Some(s.kind.attrs()), - Some(Node::Arm(ref a)) => Some(&*a.attrs), - Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), - // Unit/tuple structs/variants take the attributes straight from - // the struct/variant definition. - Some(Node::Ctor(..)) => return self.attrs(self.get_parent_item(id)), - Some(Node::Crate(item)) => Some(&item.attrs[..]), - _ => None, - }; - attrs.unwrap_or(&[]) - } - - pub fn span(&self, hir_id: HirId) -> Span { - match self.find_entry(hir_id).map(|entry| entry.node) { - Some(Node::Param(param)) => param.span, - Some(Node::Item(item)) => item.span, - Some(Node::ForeignItem(foreign_item)) => foreign_item.span, - Some(Node::TraitItem(trait_method)) => trait_method.span, - Some(Node::ImplItem(impl_item)) => impl_item.span, - Some(Node::Variant(variant)) => variant.span, - Some(Node::Field(field)) => field.span, - Some(Node::AnonConst(constant)) => self.body(constant.body).value.span, - Some(Node::Expr(expr)) => expr.span, - Some(Node::Stmt(stmt)) => stmt.span, - Some(Node::PathSegment(seg)) => seg.ident.span, - Some(Node::Ty(ty)) => ty.span, - Some(Node::TraitRef(tr)) => tr.path.span, - Some(Node::Binding(pat)) => pat.span, - Some(Node::Pat(pat)) => pat.span, - Some(Node::Arm(arm)) => arm.span, - Some(Node::Block(block)) => block.span, - Some(Node::Ctor(..)) => match self.find(self.get_parent_node(hir_id)) { - Some(Node::Item(item)) => item.span, - Some(Node::Variant(variant)) => variant.span, - _ => unreachable!(), - }, - Some(Node::Lifetime(lifetime)) => lifetime.span, - Some(Node::GenericParam(param)) => param.span, - Some(Node::Visibility(&Spanned { - node: VisibilityKind::Restricted { ref path, .. }, - .. - })) => path.span, - Some(Node::Visibility(v)) => bug!("unexpected Visibility {:?}", v), - Some(Node::Local(local)) => local.span, - Some(Node::MacroDef(macro_def)) => macro_def.span, - Some(Node::Crate(item)) => item.span, - None => bug!("hir::map::Map::span: id not in map: {:?}", hir_id), - } - } - - pub fn span_if_local(&self, id: DefId) -> Option { - self.as_local_hir_id(id).map(|id| self.span(id)) - } - - pub fn res_span(&self, res: Res) -> Option { - match res { - Res::Err => None, - Res::Local(id) => Some(self.span(id)), - res => self.span_if_local(res.opt_def_id()?), - } - } - - pub fn node_to_string(&self, id: HirId) -> String { - hir_id_to_string(self, id, true) - } - - pub fn hir_to_user_string(&self, id: HirId) -> String { - hir_id_to_string(self, id, false) - } - - pub fn hir_to_pretty_string(&self, id: HirId) -> String { - print::to_string(self, |s| s.print_node(self.get(id))) - } -} - -impl<'hir> intravisit::Map<'hir> for Map<'hir> { - fn body(&self, id: BodyId) -> &'hir Body<'hir> { - self.body(id) - } - - fn item(&self, id: HirId) -> &'hir Item<'hir> { - self.item(id) - } - - fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { - self.trait_item(id) - } - - fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { - self.impl_item(id) - } -} - -trait Named { - fn name(&self) -> Name; -} - -impl Named for Spanned { - fn name(&self) -> Name { - self.node.name() - } -} - -impl Named for Item<'_> { - fn name(&self) -> Name { - self.ident.name - } -} -impl Named for ForeignItem<'_> { - fn name(&self) -> Name { - self.ident.name - } -} -impl Named for Variant<'_> { - fn name(&self) -> Name { - self.ident.name - } -} -impl Named for StructField<'_> { - fn name(&self) -> Name { - self.ident.name - } -} -impl Named for TraitItem<'_> { - fn name(&self) -> Name { - self.ident.name - } -} -impl Named for ImplItem<'_> { - fn name(&self) -> Name { - self.ident.name - } -} - -pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx IndexedHir<'tcx> { - assert_eq!(cnum, LOCAL_CRATE); - - let _prof_timer = tcx.sess.prof.generic_activity("build_hir_map"); - - let (map, crate_hash) = { - let hcx = tcx.create_stable_hashing_context(); - - let mut collector = - NodeCollector::root(tcx.sess, &**tcx.arena, tcx.untracked_crate, &tcx.definitions, hcx); - intravisit::walk_crate(&mut collector, tcx.untracked_crate); - - let crate_disambiguator = tcx.sess.local_crate_disambiguator(); - let cmdline_args = tcx.sess.opts.dep_tracking_hash(); - collector.finalize_and_compute_crate_hash(crate_disambiguator, &*tcx.cstore, cmdline_args) - }; - - tcx.arena.alloc(IndexedHir { crate_hash, map }) -} - -/// Identical to the `PpAnn` implementation for `hir::Crate`, -/// except it avoids creating a dependency on the whole crate. -impl<'hir> print::PpAnn for Map<'hir> { - fn nested(&self, state: &mut print::State<'_>, nested: print::Nested) { - match nested { - Nested::Item(id) => state.print_item(self.expect_item(id.id)), - Nested::TraitItem(id) => state.print_trait_item(self.trait_item(id)), - Nested::ImplItem(id) => state.print_impl_item(self.impl_item(id)), - Nested::Body(id) => state.print_expr(&self.body(id).value), - Nested::BodyParamPat(id, i) => state.print_pat(&self.body(id).params[i].pat), - } - } -} - -fn hir_id_to_string(map: &Map<'_>, id: HirId, include_id: bool) -> String { - let id_str = format!(" (hir_id={})", id); - let id_str = if include_id { &id_str[..] } else { "" }; - - let path_str = || { - // This functionality is used for debugging, try to use `TyCtxt` to get - // the user-friendly path, otherwise fall back to stringifying `DefPath`. - crate::ty::tls::with_opt(|tcx| { - if let Some(tcx) = tcx { - let def_id = map.local_def_id(id); - tcx.def_path_str(def_id) - } else if let Some(path) = map.def_path_from_hir_id(id) { - path.data - .into_iter() - .map(|elem| elem.data.to_string()) - .collect::>() - .join("::") - } else { - String::from("") - } - }) - }; - - match map.find(id) { - Some(Node::Item(item)) => { - let item_str = match item.kind { - ItemKind::ExternCrate(..) => "extern crate", - ItemKind::Use(..) => "use", - ItemKind::Static(..) => "static", - ItemKind::Const(..) => "const", - ItemKind::Fn(..) => "fn", - ItemKind::Mod(..) => "mod", - ItemKind::ForeignMod(..) => "foreign mod", - ItemKind::GlobalAsm(..) => "global asm", - ItemKind::TyAlias(..) => "ty", - ItemKind::OpaqueTy(..) => "opaque type", - ItemKind::Enum(..) => "enum", - ItemKind::Struct(..) => "struct", - ItemKind::Union(..) => "union", - ItemKind::Trait(..) => "trait", - ItemKind::TraitAlias(..) => "trait alias", - ItemKind::Impl { .. } => "impl", - }; - format!("{} {}{}", item_str, path_str(), id_str) - } - Some(Node::ForeignItem(_)) => format!("foreign item {}{}", path_str(), id_str), - Some(Node::ImplItem(ii)) => match ii.kind { - ImplItemKind::Const(..) => { - format!("assoc const {} in {}{}", ii.ident, path_str(), id_str) - } - ImplItemKind::Fn(..) => format!("method {} in {}{}", ii.ident, path_str(), id_str), - ImplItemKind::TyAlias(_) => { - format!("assoc type {} in {}{}", ii.ident, path_str(), id_str) - } - ImplItemKind::OpaqueTy(_) => { - format!("assoc opaque type {} in {}{}", ii.ident, path_str(), id_str) - } - }, - Some(Node::TraitItem(ti)) => { - let kind = match ti.kind { - TraitItemKind::Const(..) => "assoc constant", - TraitItemKind::Fn(..) => "trait method", - TraitItemKind::Type(..) => "assoc type", - }; - - format!("{} {} in {}{}", kind, ti.ident, path_str(), id_str) - } - Some(Node::Variant(ref variant)) => { - format!("variant {} in {}{}", variant.ident, path_str(), id_str) - } - Some(Node::Field(ref field)) => { - format!("field {} in {}{}", field.ident, path_str(), id_str) - } - Some(Node::AnonConst(_)) => format!("const {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Expr(_)) => format!("expr {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Stmt(_)) => format!("stmt {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::PathSegment(_)) => { - format!("path segment {}{}", map.hir_to_pretty_string(id), id_str) - } - Some(Node::Ty(_)) => format!("type {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::TraitRef(_)) => format!("trait_ref {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Binding(_)) => format!("local {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Pat(_)) => format!("pat {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Param(_)) => format!("param {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Arm(_)) => format!("arm {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Block(_)) => format!("block {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Local(_)) => format!("local {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str), - Some(Node::Lifetime(_)) => format!("lifetime {}{}", map.hir_to_pretty_string(id), id_str), - Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str), - Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str), - Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str), - Some(Node::Crate(..)) => String::from("root_crate"), - None => format!("unknown node{}", id_str), - } -} - -pub fn provide(providers: &mut Providers<'_>) { - providers.def_kind = |tcx, def_id| { - if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) { - tcx.hir().def_kind(hir_id) - } else { - bug!("calling local def_kind query provider for upstream DefId: {:?}", def_id); - } - }; -} diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs deleted file mode 100644 index ce8e1f48daa77..0000000000000 --- a/src/librustc/hir/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! HIR datatypes. See the [rustc dev guide] for more info. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html - -pub mod exports; -pub mod map; - -use crate::ich::StableHashingContext; -use crate::ty::query::Providers; -use crate::ty::TyCtxt; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; -use rustc_hir::Body; -use rustc_hir::HirId; -use rustc_hir::ItemLocalId; -use rustc_hir::Node; -use rustc_index::vec::IndexVec; - -pub struct Owner<'tcx> { - parent: HirId, - node: Node<'tcx>, -} - -impl<'a, 'tcx> HashStable> for Owner<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let Owner { parent, node } = self; - hcx.while_hashing_hir_bodies(false, |hcx| { - parent.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); - }); - } -} - -#[derive(Clone)] -pub struct ParentedNode<'tcx> { - parent: ItemLocalId, - node: Node<'tcx>, -} - -pub struct OwnerNodes<'tcx> { - hash: Fingerprint, - nodes: IndexVec>>, - bodies: FxHashMap>, -} - -impl<'a, 'tcx> HashStable> for OwnerNodes<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - // We ignore the `nodes` and `bodies` fields since these refer to information included in - // `hash` which is hashed in the collector and used for the crate hash. - let OwnerNodes { hash, nodes: _, bodies: _ } = *self; - hash.hash_stable(hcx, hasher); - } -} - -impl<'tcx> TyCtxt<'tcx> { - #[inline(always)] - pub fn hir(self) -> map::Map<'tcx> { - map::Map { tcx: self } - } - - pub fn parent_module(self, id: HirId) -> LocalDefId { - self.parent_module_from_def_id(id.owner) - } -} - -pub fn provide(providers: &mut Providers<'_>) { - providers.parent_module_from_def_id = |tcx, id| { - let hir = tcx.hir(); - hir.local_def_id(hir.get_module_parent_node(hir.as_local_hir_id(id.to_def_id()).unwrap())) - .expect_local() - }; - providers.hir_crate = |tcx, _| tcx.untracked_crate; - providers.index_hir = map::index_hir; - providers.hir_module_items = |tcx, id| { - let hir = tcx.hir(); - let module = hir.as_local_hir_id(id.to_def_id()).unwrap(); - &tcx.untracked_crate.modules[&module] - }; - providers.hir_owner = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].signature; - providers.hir_owner_nodes = - |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].with_bodies.as_ref().map(|nodes| &**nodes); - map::provide(providers); -} diff --git a/src/librustc/ich/mod.rs b/src/librustc/ich/mod.rs deleted file mode 100644 index 2c4618dcd42cf..0000000000000 --- a/src/librustc/ich/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! ICH - Incremental Compilation Hash - -pub use self::hcx::{ - hash_stable_trait_impls, NodeIdHashingMode, StableHashingContext, StableHashingContextProvider, -}; -crate use rustc_data_structures::fingerprint::Fingerprint; -use rustc_span::symbol::{sym, Symbol}; -pub use rustc_span::CachingSourceMapView; - -mod hcx; - -mod impls_hir; -mod impls_syntax; -mod impls_ty; - -pub const IGNORED_ATTRIBUTES: &[Symbol] = &[ - sym::cfg, - sym::rustc_if_this_changed, - sym::rustc_then_this_would_need, - sym::rustc_dirty, - sym::rustc_clean, - sym::rustc_partition_reused, - sym::rustc_partition_codegened, - sym::rustc_expected_cgu_reuse, -]; diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs deleted file mode 100644 index 6f18560a02d7f..0000000000000 --- a/src/librustc/lib.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! The "main crate" of the Rust compiler. This crate contains common -//! type definitions that are used by the other crates in the rustc -//! "family". Some prominent examples (note that each of these modules -//! has their own README with further details). -//! -//! - **HIR.** The "high-level (H) intermediate representation (IR)" is -//! defined in the `hir` module. -//! - **MIR.** The "mid-level (M) intermediate representation (IR)" is -//! defined in the `mir` module. This module contains only the -//! *definition* of the MIR; the passes that transform and operate -//! on MIR are found in `librustc_mir` crate. -//! - **Types.** The internal representation of types used in rustc is -//! defined in the `ty` module. This includes the **type context** -//! (or `tcx`), which is the central context during most of -//! compilation, containing the interners and other things. -//! -//! For more information about how rustc works, see the [rustc dev guide]. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bool_to_option)] -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(const_if_match)] -#![feature(const_fn)] -#![feature(const_panic)] -#![feature(const_transmute)] -#![feature(core_intrinsics)] -#![feature(drain_filter)] -#![feature(never_type)] -#![feature(exhaustive_patterns)] -#![feature(marker_trait_attr)] -#![feature(extern_types)] -#![feature(nll)] -#![feature(option_expect_none)] -#![feature(or_patterns)] -#![feature(range_is_empty)] -#![feature(specialization)] -#![feature(trusted_len)] -#![feature(vec_remove_item)] -#![feature(stmt_expr_attributes)] -#![feature(test)] -#![feature(in_band_lifetimes)] -#![feature(crate_visibility_modifier)] -#![feature(associated_type_bounds)] -#![feature(rustc_attrs)] -#![feature(hash_raw_entry)] -#![feature(int_error_matching)] -#![recursion_limit = "512"] - -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate scoped_tls; -#[macro_use] -extern crate rustc_macros; -#[macro_use] -extern crate rustc_data_structures; -#[macro_use] -extern crate log; -#[macro_use] -extern crate smallvec; - -#[cfg(test)] -mod tests; - -#[macro_use] -mod macros; - -#[macro_use] -pub mod query; - -#[macro_use] -pub mod arena; -pub mod dep_graph; -pub mod hir; -pub mod ich; -pub mod infer; -pub mod lint; -pub mod middle; -pub mod mir; -pub mod traits; -pub mod ty; - -pub mod util { - pub mod bug; - pub mod common; -} - -// Allows macros to refer to this crate as `::rustc` -extern crate self as rustc; diff --git a/src/librustc/lint.rs b/src/librustc/lint.rs deleted file mode 100644 index 53061436de07a..0000000000000 --- a/src/librustc/lint.rs +++ /dev/null @@ -1,352 +0,0 @@ -use std::cmp; - -use crate::ich::StableHashingContext; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_errors::{DiagnosticBuilder, DiagnosticId}; -use rustc_hir::HirId; -use rustc_session::lint::{builtin, Level, Lint, LintId}; -use rustc_session::{DiagnosticMessageId, Session}; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan}; -use rustc_span::{Span, Symbol}; - -/// How a lint level was set. -#[derive(Clone, Copy, PartialEq, Eq, HashStable)] -pub enum LintSource { - /// Lint is at the default level as declared - /// in rustc or a plugin. - Default, - - /// Lint level was set by an attribute. - Node(Symbol, Span, Option /* RFC 2383 reason */), - - /// Lint level was set by a command-line flag. - CommandLine(Symbol), -} - -pub type LevelSource = (Level, LintSource); - -pub struct LintLevelSets { - pub list: Vec, - pub lint_cap: Level, -} - -pub enum LintSet { - CommandLine { - // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which - // flag. - specs: FxHashMap, - }, - - Node { - specs: FxHashMap, - parent: u32, - }, -} - -impl LintLevelSets { - pub fn new() -> Self { - LintLevelSets { list: Vec::new(), lint_cap: Level::Forbid } - } - - pub fn get_lint_level( - &self, - lint: &'static Lint, - idx: u32, - aux: Option<&FxHashMap>, - sess: &Session, - ) -> LevelSource { - let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux); - - // If `level` is none then we actually assume the default level for this - // lint. - let mut level = level.unwrap_or_else(|| lint.default_level(sess.edition())); - - // 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 - // `allow(warnings)` in scope then we want to respect that instead. - if level == Level::Warn { - let (warnings_level, warnings_src) = - self.get_lint_id_level(LintId::of(builtin::WARNINGS), idx, aux); - if let Some(configured_warning_level) = warnings_level { - if configured_warning_level != Level::Warn { - level = configured_warning_level; - src = warnings_src; - } - } - } - - // Ensure that we never exceed the `--cap-lints` argument. - level = cmp::min(level, self.lint_cap); - - if let Some(driver_level) = sess.driver_lint_caps.get(&LintId::of(lint)) { - // Ensure that we never exceed driver level. - level = cmp::min(*driver_level, level); - } - - (level, src) - } - - pub fn get_lint_id_level( - &self, - id: LintId, - mut idx: u32, - aux: Option<&FxHashMap>, - ) -> (Option, LintSource) { - if let Some(specs) = aux { - if let Some(&(level, src)) = specs.get(&id) { - return (Some(level), src); - } - } - loop { - match self.list[idx as usize] { - LintSet::CommandLine { ref specs } => { - if let Some(&(level, src)) = specs.get(&id) { - return (Some(level), src); - } - return (None, LintSource::Default); - } - LintSet::Node { ref specs, parent } => { - if let Some(&(level, src)) = specs.get(&id) { - return (Some(level), src); - } - idx = parent; - } - } - } - } -} - -pub struct LintLevelMap { - pub sets: LintLevelSets, - pub id_to_set: FxHashMap, -} - -impl LintLevelMap { - /// If the `id` was previously registered with `register_id` when building - /// this `LintLevelMap` this returns the corresponding lint level and source - /// of the lint level for the lint provided. - /// - /// If the `id` was not previously registered, returns `None`. If `None` is - /// returned then the parent of `id` should be acquired and this function - /// should be called again. - pub fn level_and_source( - &self, - lint: &'static Lint, - id: HirId, - session: &Session, - ) -> Option { - self.id_to_set.get(&id).map(|idx| self.sets.get_lint_level(lint, *idx, None, session)) - } -} - -impl<'a> HashStable> for LintLevelMap { - #[inline] - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let LintLevelMap { ref sets, ref id_to_set } = *self; - - id_to_set.hash_stable(hcx, hasher); - - let LintLevelSets { ref list, lint_cap } = *sets; - - lint_cap.hash_stable(hcx, hasher); - - hcx.while_hashing_spans(true, |hcx| { - list.len().hash_stable(hcx, hasher); - - // We are working under the assumption here that the list of - // lint-sets is built in a deterministic order. - for lint_set in list { - ::std::mem::discriminant(lint_set).hash_stable(hcx, hasher); - - match *lint_set { - LintSet::CommandLine { ref specs } => { - specs.hash_stable(hcx, hasher); - } - LintSet::Node { ref specs, parent } => { - specs.hash_stable(hcx, hasher); - parent.hash_stable(hcx, hasher); - } - } - } - }) - } -} - -pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>); - -impl<'a> LintDiagnosticBuilder<'a> { - /// Return the inner DiagnosticBuilder, first setting the primary message to `msg`. - pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> { - self.0.set_primary_message(msg); - self.0 - } - - /// Create a LintDiagnosticBuilder from some existing DiagnosticBuilder. - pub fn new(err: DiagnosticBuilder<'a>) -> LintDiagnosticBuilder<'a> { - LintDiagnosticBuilder(err) - } -} - -pub fn struct_lint_level<'s, 'd>( - sess: &'s Session, - lint: &'static Lint, - level: Level, - src: LintSource, - span: Option, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + 'd, -) { - // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to - // the "real" work. - fn struct_lint_level_impl( - sess: &'s Session, - lint: &'static Lint, - level: Level, - src: LintSource, - span: Option, - decorate: Box FnOnce(LintDiagnosticBuilder<'b>) + 'd>, - ) { - let mut err = match (level, span) { - (Level::Allow, _) => { - return; - } - (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""), - (Level::Warn, None) => sess.struct_warn(""), - (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => { - sess.struct_span_err(span, "") - } - (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(""), - }; - - // Check for future incompatibility lints and issue a stronger warning. - let lint_id = LintId::of(lint); - let future_incompatible = lint.future_incompatible; - - // 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 - // can do about it. We probably want to skip the lint entirely. - if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { - // Any suggestions made here are likely to be incorrect, so anything we - // emit shouldn't be automatically fixed by rustfix. - err.allow_suggestions(false); - - // If this is a future incompatible lint it'll become a hard error, so - // we have to emit *something*. Also allow lints to whitelist themselves - // on a case-by-case basis for emission in a foreign macro. - if future_incompatible.is_none() && !lint.report_in_external_macro { - err.cancel(); - // Don't continue further, since we don't want to have - // `diag_span_note_once` called for a diagnostic that isn't emitted. - return; - } - } - - let name = lint.name_lower(); - match src { - LintSource::Default => { - sess.diag_note_once( - &mut err, - DiagnosticMessageId::from(lint), - &format!("`#[{}({})]` on by default", level.as_str(), name), - ); - } - LintSource::CommandLine(lint_flag_val) => { - let flag = match level { - Level::Warn => "-W", - Level::Deny => "-D", - Level::Forbid => "-F", - Level::Allow => panic!(), - }; - let hyphen_case_lint_name = name.replace("_", "-"); - if lint_flag_val.as_str() == name { - sess.diag_note_once( - &mut err, - DiagnosticMessageId::from(lint), - &format!( - "requested on the command line with `{} {}`", - flag, hyphen_case_lint_name - ), - ); - } else { - let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-"); - sess.diag_note_once( - &mut err, - DiagnosticMessageId::from(lint), - &format!( - "`{} {}` implied by `{} {}`", - flag, hyphen_case_lint_name, flag, hyphen_case_flag_val - ), - ); - } - } - LintSource::Node(lint_attr_name, src, reason) => { - if let Some(rationale) = reason { - err.note(&rationale.as_str()); - } - sess.diag_span_note_once( - &mut err, - DiagnosticMessageId::from(lint), - src, - "the lint level is defined here", - ); - if lint_attr_name.as_str() != name { - let level_str = level.as_str(); - sess.diag_note_once( - &mut err, - DiagnosticMessageId::from(lint), - &format!( - "`#[{}({})]` implied by `#[{}({})]`", - level_str, name, level_str, lint_attr_name - ), - ); - } - } - } - - err.code(DiagnosticId::Lint(name)); - - if let Some(future_incompatible) = future_incompatible { - const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \ - it will become a hard error"; - - let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) { - "once this method is added to the standard library, \ - the ambiguity may cause an error or change in behavior!" - .to_owned() - } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) { - "this borrowing pattern was not meant to be accepted, \ - and may become a hard error in the future" - .to_owned() - } else if let Some(edition) = future_incompatible.edition { - format!("{} in the {} edition!", STANDARD_MESSAGE, edition) - } else { - format!("{} in a future release!", STANDARD_MESSAGE) - }; - let citation = format!("for more information, see {}", future_incompatible.reference); - err.warn(&explanation); - err.note(&citation); - } - - // Finally, run `decorate`. This function is also responsible for emitting the diagnostic. - decorate(LintDiagnosticBuilder::new(err)); - } - struct_lint_level_impl(sess, lint, level, src, span, Box::new(decorate)) -} - -/// Returns whether `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. -pub fn in_external_macro(sess: &Session, span: Span) -> bool { - let expn_data = span.ctxt().outer_expn_data(); - match expn_data.kind { - ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => 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() || sess.source_map().is_imported(expn_data.def_site) - } - ExpnKind::Macro(..) => true, // definitely a plugin - } -} diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs deleted file mode 100644 index 62ccd94674488..0000000000000 --- a/src/librustc/middle/free_region.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! This module handles the relationships between "free regions", i.e., lifetime parameters. -//! Ordinarily, free regions are unrelated to one another, but they can be related via implied -//! or explicit bounds. In that case, we track the bounds using the `TransitiveRelation` type, -//! and use that to decide when one free region outlives another, and so forth. - -use crate::middle::region; -use crate::ty::free_region_map::FreeRegionMap; -use crate::ty::{Region, TyCtxt}; -use rustc_hir::def_id::DefId; - -/// Combines a `region::ScopeTree` (which governs relationships between -/// scopes) and a `FreeRegionMap` (which governs relationships between -/// free regions) to yield a complete relation between concrete -/// regions. -/// -/// This stuff is a bit convoluted and should be refactored, but as we -/// transition to NLL, it'll all go away anyhow. -pub struct RegionRelations<'a, 'tcx> { - pub tcx: TyCtxt<'tcx>, - - /// The context used to fetch the region maps. - pub context: DefId, - - /// The region maps for the given context. - pub region_scope_tree: &'a region::ScopeTree, - - /// Free-region relationships. - pub free_regions: &'a FreeRegionMap<'tcx>, -} - -impl<'a, 'tcx> RegionRelations<'a, 'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - context: DefId, - region_scope_tree: &'a region::ScopeTree, - free_regions: &'a FreeRegionMap<'tcx>, - ) -> Self { - Self { tcx, context, region_scope_tree, free_regions } - } - - pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { - self.free_regions.lub_free_regions(self.tcx, r_a, r_b) - } -} diff --git a/src/librustc/middle/mod.rs b/src/librustc/middle/mod.rs deleted file mode 100644 index 464488964afb7..0000000000000 --- a/src/librustc/middle/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub mod codegen_fn_attrs; -pub mod cstore; -pub mod dependency_format; -pub mod exported_symbols; -pub mod free_region; -pub mod lang_items; -pub mod lib_features { - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - use rustc_span::symbol::Symbol; - - #[derive(HashStable)] - pub struct LibFeatures { - // A map from feature to stabilisation version. - pub stable: FxHashMap, - pub unstable: FxHashSet, - } - - impl LibFeatures { - pub fn to_vec(&self) -> Vec<(Symbol, Option)> { - let mut all_features: Vec<_> = self - .stable - .iter() - .map(|(f, s)| (*f, Some(*s))) - .chain(self.unstable.iter().map(|f| (*f, None))) - .collect(); - all_features.sort_unstable_by_key(|f| f.0.as_str()); - all_features - } - } -} -pub mod limits; -pub mod privacy; -pub mod region; -pub mod resolve_lifetime; -pub mod stability; diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs deleted file mode 100644 index 00ecc7a7a0aa1..0000000000000 --- a/src/librustc/mir/cache.rs +++ /dev/null @@ -1,271 +0,0 @@ -use crate::ich::StableHashingContext; -use crate::mir::{BasicBlock, BasicBlockData, Body, LocalDecls, Location, Successors}; -use rustc_data_structures::graph::dominators::{dominators, Dominators}; -use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_index::vec::IndexVec; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use std::iter; -use std::ops::{Deref, DerefMut, Index, IndexMut}; -use std::vec::IntoIter; - -#[derive(Clone, Debug)] -pub struct Cache { - predecessors: Option>>, -} - -impl rustc_serialize::Encodable for Cache { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - Encodable::encode(&(), s) - } -} - -impl rustc_serialize::Decodable for Cache { - fn decode(d: &mut D) -> Result { - Decodable::decode(d).map(|_v: ()| Self::new()) - } -} - -impl<'a> HashStable> for Cache { - fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) { - // Do nothing. - } -} - -impl Cache { - pub fn new() -> Self { - Self { predecessors: None } - } - - pub fn invalidate_predecessors(&mut self) { - // FIXME: consider being more fine-grained - self.predecessors = None; - } - - pub fn ensure_predecessors(&mut self, body: &Body<'_>) { - if self.predecessors.is_none() { - let mut result = IndexVec::from_elem(vec![], body.basic_blocks()); - for (bb, data) in body.basic_blocks().iter_enumerated() { - if let Some(ref term) = data.terminator { - for &tgt in term.successors() { - result[tgt].push(bb); - } - } - } - - self.predecessors = Some(result) - } - } - - /// This will recompute the predecessors cache if it is not available - fn predecessors(&mut self, body: &Body<'_>) -> &IndexVec> { - self.ensure_predecessors(body); - self.predecessors.as_ref().unwrap() - } - - fn unwrap_predecessors_for(&self, bb: BasicBlock) -> &[BasicBlock] { - &self.predecessors.as_ref().unwrap()[bb] - } - - fn unwrap_predecessor_locations<'a>( - &'a self, - loc: Location, - body: &'a Body<'a>, - ) -> impl Iterator + 'a { - let if_zero_locations = if loc.statement_index == 0 { - let predecessor_blocks = self.unwrap_predecessors_for(loc.block); - let num_predecessor_blocks = predecessor_blocks.len(); - Some( - (0..num_predecessor_blocks) - .map(move |i| predecessor_blocks[i]) - .map(move |bb| body.terminator_loc(bb)), - ) - } else { - None - }; - - let if_not_zero_locations = if loc.statement_index == 0 { - None - } else { - Some(Location { block: loc.block, statement_index: loc.statement_index - 1 }) - }; - - if_zero_locations.into_iter().flatten().chain(if_not_zero_locations) - } - - pub fn basic_blocks_mut<'a, 'tcx>( - &mut self, - body: &'a mut Body<'tcx>, - ) -> &'a mut IndexVec> { - debug!("bbm: Clearing predecessors cache for body at: {:?}", body.span.data()); - self.invalidate_predecessors(); - &mut body.basic_blocks - } - - pub fn basic_blocks_and_local_decls_mut<'a, 'tcx>( - &mut self, - body: &'a mut Body<'tcx>, - ) -> (&'a mut IndexVec>, &'a mut LocalDecls<'tcx>) { - debug!("bbaldm: Clearing predecessors cache for body at: {:?}", body.span.data()); - self.invalidate_predecessors(); - (&mut body.basic_blocks, &mut body.local_decls) - } -} - -#[derive(Clone, Debug, HashStable, RustcEncodable, RustcDecodable, TypeFoldable)] -pub struct BodyAndCache<'tcx> { - body: Body<'tcx>, - cache: Cache, -} - -impl BodyAndCache<'tcx> { - pub fn new(body: Body<'tcx>) -> Self { - Self { body, cache: Cache::new() } - } -} - -#[macro_export] -macro_rules! read_only { - ($body:expr) => {{ - $body.ensure_predecessors(); - $body.unwrap_read_only() - }}; -} - -impl BodyAndCache<'tcx> { - pub fn ensure_predecessors(&mut self) { - self.cache.ensure_predecessors(&self.body); - } - - pub fn predecessors(&mut self) -> &IndexVec> { - self.cache.predecessors(&self.body) - } - - pub fn unwrap_read_only(&self) -> ReadOnlyBodyAndCache<'_, 'tcx> { - ReadOnlyBodyAndCache::new(&self.body, &self.cache) - } - - pub fn basic_blocks_mut(&mut self) -> &mut IndexVec> { - self.cache.basic_blocks_mut(&mut self.body) - } - - pub fn basic_blocks_and_local_decls_mut( - &mut self, - ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { - self.cache.basic_blocks_and_local_decls_mut(&mut self.body) - } -} - -impl<'tcx> Index for BodyAndCache<'tcx> { - type Output = BasicBlockData<'tcx>; - - fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { - &self.body[index] - } -} - -impl<'tcx> IndexMut for BodyAndCache<'tcx> { - fn index_mut(&mut self, index: BasicBlock) -> &mut Self::Output { - &mut self.basic_blocks_mut()[index] - } -} - -impl<'tcx> Deref for BodyAndCache<'tcx> { - type Target = Body<'tcx>; - - fn deref(&self) -> &Self::Target { - &self.body - } -} - -impl<'tcx> DerefMut for BodyAndCache<'tcx> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.body - } -} - -#[derive(Copy, Clone, Debug)] -pub struct ReadOnlyBodyAndCache<'a, 'tcx> { - body: &'a Body<'tcx>, - cache: &'a Cache, -} - -impl ReadOnlyBodyAndCache<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, cache: &'a Cache) -> Self { - assert!( - cache.predecessors.is_some(), - "Cannot construct ReadOnlyBodyAndCache without computed predecessors" - ); - Self { body, cache } - } - - pub fn predecessors(&self) -> &IndexVec> { - self.cache.predecessors.as_ref().unwrap() - } - - pub fn predecessors_for(&self, bb: BasicBlock) -> &[BasicBlock] { - self.cache.unwrap_predecessors_for(bb) - } - - pub fn predecessor_locations(&self, loc: Location) -> impl Iterator + '_ { - self.cache.unwrap_predecessor_locations(loc, self.body) - } - - pub fn basic_blocks(&self) -> &IndexVec> { - &self.body.basic_blocks - } - - pub fn dominators(&self) -> Dominators { - dominators(self) - } -} - -impl graph::DirectedGraph for ReadOnlyBodyAndCache<'a, 'tcx> { - type Node = BasicBlock; -} - -impl graph::GraphPredecessors<'graph> for ReadOnlyBodyAndCache<'a, 'tcx> { - type Item = BasicBlock; - type Iter = IntoIter; -} - -impl graph::WithPredecessors for ReadOnlyBodyAndCache<'a, 'tcx> { - fn predecessors(&self, node: Self::Node) -> >::Iter { - self.cache.unwrap_predecessors_for(node).to_vec().into_iter() - } -} - -impl graph::WithNumNodes for ReadOnlyBodyAndCache<'a, 'tcx> { - fn num_nodes(&self) -> usize { - self.body.num_nodes() - } -} - -impl graph::WithStartNode for ReadOnlyBodyAndCache<'a, 'tcx> { - fn start_node(&self) -> Self::Node { - self.body.start_node() - } -} - -impl graph::WithSuccessors for ReadOnlyBodyAndCache<'a, 'tcx> { - fn successors(&self, node: Self::Node) -> >::Iter { - self.body.successors(node) - } -} - -impl<'a, 'b, 'tcx> graph::GraphSuccessors<'b> for ReadOnlyBodyAndCache<'a, 'tcx> { - type Item = BasicBlock; - type Iter = iter::Cloned>; -} - -impl Deref for ReadOnlyBodyAndCache<'a, 'tcx> { - type Target = &'a Body<'tcx>; - - fn deref(&self) -> &Self::Target { - &self.body - } -} - -CloneTypeFoldableAndLiftImpls! { - Cache, -} diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs deleted file mode 100644 index 8f06b9a69bd15..0000000000000 --- a/src/librustc/mir/interpret/error.rs +++ /dev/null @@ -1,586 +0,0 @@ -use super::{AllocId, CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef}; - -use crate::hir::map::definitions::DefPathData; -use crate::mir::interpret::ConstValue; -use crate::ty::layout::{Align, LayoutError, Size}; -use crate::ty::query::TyCtxtAt; -use crate::ty::tls; -use crate::ty::{self, layout, Ty}; - -use backtrace::Backtrace; -use rustc_data_structures::sync::Lock; -use rustc_errors::{struct_span_err, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_macros::HashStable; -use rustc_session::CtfeBacktrace; -use rustc_span::{def_id::DefId, Pos, Span}; -use std::{any::Any, fmt, mem}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)] -pub enum ErrorHandled { - /// Already reported a lint or an error for this evaluation. - Reported, - /// Don't emit an error, the evaluation failed because the MIR was generic - /// and the substs didn't fully monomorphize it. - TooGeneric, -} - -impl ErrorHandled { - pub fn assert_reported(self) { - match self { - ErrorHandled::Reported => {} - ErrorHandled::TooGeneric => bug!( - "MIR interpretation failed without reporting an error \ - even though it was fully monomorphized" - ), - } - } -} - -CloneTypeFoldableImpls! { - ErrorHandled, -} - -pub type ConstEvalRawResult<'tcx> = Result, ErrorHandled>; -pub type ConstEvalResult<'tcx> = Result, ErrorHandled>; - -#[derive(Debug)] -pub struct ConstEvalErr<'tcx> { - pub span: Span, - pub error: crate::mir::interpret::InterpError<'tcx>, - pub stacktrace: Vec>, -} - -#[derive(Debug)] -pub struct FrameInfo<'tcx> { - /// This span is in the caller. - pub call_site: Span, - pub instance: ty::Instance<'tcx>, - pub lint_root: Option, -} - -impl<'tcx> fmt::Display for FrameInfo<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - if tcx.def_key(self.instance.def_id()).disambiguated_data.data - == DefPathData::ClosureExpr - { - write!(f, "inside call to closure")?; - } else { - write!(f, "inside call to `{}`", self.instance)?; - } - if !self.call_site.is_dummy() { - let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo()); - write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?; - } - Ok(()) - }) - } -} - -impl<'tcx> ConstEvalErr<'tcx> { - pub fn struct_error( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - emit: impl FnOnce(DiagnosticBuilder<'_>), - ) -> Result<(), ErrorHandled> { - self.struct_generic(tcx, message, emit, None) - } - - pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { - match self.struct_error(tcx, message, |mut e| e.emit()) { - Ok(_) => ErrorHandled::Reported, - Err(x) => x, - } - } - - pub fn report_as_lint( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - lint_root: hir::HirId, - span: Option, - ) -> ErrorHandled { - match self.struct_generic( - tcx, - message, - |mut lint: DiagnosticBuilder<'_>| { - // Apply the span. - if let Some(span) = span { - let primary_spans = lint.span.primary_spans().to_vec(); - // point at the actual error as the primary span - lint.replace_span_with(span); - // point to the `const` statement as a secondary span - // they don't have any label - for sp in primary_spans { - if sp != span { - lint.span_label(sp, ""); - } - } - } - lint.emit(); - }, - Some(lint_root), - ) { - Ok(_) => ErrorHandled::Reported, - Err(err) => err, - } - } - - /// Create a diagnostic for this const eval error. - /// - /// Sets the message passed in via `message` and adds span labels with detailed error - /// information before handing control back to `emit` to do any final processing. - /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit` - /// function to dispose of the diagnostic properly. - /// - /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. - /// (Except that for some errors, we ignore all that -- see `must_error` below.) - fn struct_generic( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - emit: impl FnOnce(DiagnosticBuilder<'_>), - lint_root: Option, - ) -> Result<(), ErrorHandled> { - let must_error = match self.error { - err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { - return Err(ErrorHandled::TooGeneric); - } - err_inval!(TypeckError) => return Err(ErrorHandled::Reported), - // We must *always* hard error on these, even if the caller wants just a lint. - err_inval!(Layout(LayoutError::SizeOverflow(_))) => true, - _ => false, - }; - trace!("reporting const eval failure at {:?}", self.span); - - let err_msg = match &self.error { - InterpError::MachineStop(msg) => { - // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`). - // Should be turned into a string by now. - msg.downcast_ref::().expect("invalid MachineStop payload").clone() - } - err => err.to_string(), - }; - - let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option| { - if let Some(span_msg) = span_msg { - err.span_label(self.span, span_msg); - } - // Add spans for the stacktrace. - // Skip the last, which is just the environment of the constant. The stacktrace - // is sometimes empty because we create "fake" eval contexts in CTFE to do work - // on constant values. - if !self.stacktrace.is_empty() { - for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] { - err.span_label(frame_info.call_site, frame_info.to_string()); - } - } - // Let the caller finish the job. - emit(err) - }; - - if must_error { - // The `message` makes little sense here, this is a more serious error than the - // caller thinks anyway. - // See . - finish(struct_error(tcx, &err_msg), None); - } else { - // Regular case. - if let Some(lint_root) = lint_root { - // Report as lint. - let hir_id = self - .stacktrace - .iter() - .rev() - .filter_map(|frame| frame.lint_root) - .next() - .unwrap_or(lint_root); - tcx.struct_span_lint_hir( - rustc_session::lint::builtin::CONST_ERR, - hir_id, - tcx.span, - |lint| finish(lint.build(message), Some(err_msg)), - ); - } else { - // Report as hard error. - finish(struct_error(tcx, message), Some(err_msg)); - } - } - Ok(()) - } -} - -pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> { - struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) -} - -/// Packages the kind of error we got from the const code interpreter -/// up with a Rust-level backtrace of where the error occurred. -/// Thsese should always be constructed by calling `.into()` on -/// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*` -/// macros for this. -#[derive(Debug)] -pub struct InterpErrorInfo<'tcx> { - pub kind: InterpError<'tcx>, - backtrace: Option>, -} - -impl fmt::Display for InterpErrorInfo<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.kind) - } -} - -impl InterpErrorInfo<'_> { - pub fn print_backtrace(&mut self) { - if let Some(ref mut backtrace) = self.backtrace { - print_backtrace(&mut *backtrace); - } - } -} - -fn print_backtrace(backtrace: &mut Backtrace) { - backtrace.resolve(); - eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace); -} - -impl From for InterpErrorInfo<'_> { - fn from(err: ErrorHandled) -> Self { - match err { - ErrorHandled::Reported => err_inval!(ReferencedConstant), - ErrorHandled::TooGeneric => err_inval!(TooGeneric), - } - .into() - } -} - -impl<'tcx> From> for InterpErrorInfo<'tcx> { - fn from(kind: InterpError<'tcx>) -> Self { - let capture_backtrace = tls::with_context_opt(|ctxt| { - if let Some(ctxt) = ctxt { - *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace) - } else { - CtfeBacktrace::Disabled - } - }); - - let backtrace = match capture_backtrace { - CtfeBacktrace::Disabled => None, - CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())), - CtfeBacktrace::Immediate => { - // Print it now. - let mut backtrace = Backtrace::new_unresolved(); - print_backtrace(&mut backtrace); - None - } - }; - - InterpErrorInfo { kind, backtrace } - } -} - -/// Error information for when the program we executed turned out not to actually be a valid -/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp -/// where we work on generic code or execution does not have all information available. -pub enum InvalidProgramInfo<'tcx> { - /// Resolution can fail if we are in a too generic context. - TooGeneric, - /// Cannot compute this constant because it depends on another one - /// which already produced an error. - ReferencedConstant, - /// Abort in case type errors are reached. - TypeckError, - /// An error occurred during layout computation. - Layout(layout::LayoutError<'tcx>), - /// An invalid transmute happened. - TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), -} - -impl fmt::Debug for InvalidProgramInfo<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use InvalidProgramInfo::*; - match self { - TooGeneric => write!(f, "encountered overly generic constant"), - ReferencedConstant => write!(f, "referenced constant has errors"), - TypeckError => write!(f, "encountered constants with type errors, stopping evaluation"), - Layout(ref err) => write!(f, "{}", err), - TransmuteSizeDiff(from_ty, to_ty) => write!( - f, - "tried to transmute from {:?} to {:?}, but their sizes differed", - from_ty, to_ty - ), - } - } -} - -/// Error information for when the program caused Undefined Behavior. -pub enum UndefinedBehaviorInfo { - /// Free-form case. Only for errors that are never caught! - Ub(String), - /// Unreachable code was executed. - Unreachable, - /// An enum discriminant was set to a value which was outside the range of valid values. - InvalidDiscriminant(ScalarMaybeUndef), - /// A slice/array index projection went out-of-bounds. - BoundsCheckFailed { - len: u64, - index: u64, - }, - /// Something was divided by 0 (x / 0). - DivisionByZero, - /// Something was "remainded" by 0 (x % 0). - RemainderByZero, - /// Overflowing inbounds pointer arithmetic. - PointerArithOverflow, - /// Invalid metadata in a wide pointer (using `str` to avoid allocations). - InvalidMeta(&'static str), - /// Reading a C string that does not end within its allocation. - UnterminatedCString(Pointer), - /// Dereferencing a dangling pointer after it got freed. - PointerUseAfterFree(AllocId), - /// Used a pointer outside the bounds it is valid for. - PointerOutOfBounds { - ptr: Pointer, - msg: CheckInAllocMsg, - allocation_size: Size, - }, - /// Used a pointer with bad alignment. - AlignmentCheckFailed { - required: Align, - has: Align, - }, - /// Using an integer as a pointer in the wrong way. - InvalidIntPointerUsage(u64), - /// Writing to read-only memory. - WriteToReadOnly(AllocId), - /// Using a pointer-not-to-a-function as function pointer. - InvalidFunctionPointer(Pointer), - // Trying to access the data behind a function pointer. - DerefFunctionPointer(AllocId), - /// The value validity check found a problem. - /// Should only be thrown by `validity.rs` and always point out which part of the value - /// is the problem. - ValidationFailure(String), - /// Using a non-boolean `u8` as bool. - InvalidBool(u8), - /// Using a non-character `u32` as character. - InvalidChar(u32), - /// Using uninitialized data where it is not allowed. - InvalidUndefBytes(Option), - /// Working with a local that is not currently live. - DeadLocal, - /// Trying to read from the return place of a function. - ReadFromReturnPlace, -} - -impl fmt::Debug for UndefinedBehaviorInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use UndefinedBehaviorInfo::*; - match self { - Ub(msg) => write!(f, "{}", msg), - Unreachable => write!(f, "entering unreachable code"), - InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val), - BoundsCheckFailed { ref len, ref index } => write!( - f, - "indexing out of bounds: the len is {:?} but the index is {:?}", - len, index - ), - DivisionByZero => write!(f, "dividing by zero"), - RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), - PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), - InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), - UnterminatedCString(p) => write!( - f, - "reading a null-terminated string starting at {:?} with no null found before end of allocation", - p, - ), - PointerUseAfterFree(a) => { - write!(f, "pointer to {:?} was dereferenced after this allocation got freed", a) - } - PointerOutOfBounds { ptr, msg, allocation_size } => write!( - f, - "{} failed: pointer must be in-bounds at offset {}, \ - but is outside bounds of {} which has size {}", - msg, - ptr.offset.bytes(), - ptr.alloc_id, - allocation_size.bytes() - ), - InvalidIntPointerUsage(0) => write!(f, "invalid use of NULL pointer"), - InvalidIntPointerUsage(i) => write!(f, "invalid use of {} as a pointer", i), - AlignmentCheckFailed { required, has } => write!( - f, - "accessing memory with alignment {}, but alignment {} is required", - has.bytes(), - required.bytes() - ), - WriteToReadOnly(a) => write!(f, "writing to {:?} which is read-only", a), - InvalidFunctionPointer(p) => { - write!(f, "using {:?} as function pointer but it does not point to a function", p) - } - DerefFunctionPointer(a) => write!(f, "accessing {:?} which contains a function", a), - ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), - InvalidBool(b) => write!(f, "interpreting an invalid 8-bit value as a bool: {}", b), - InvalidChar(c) => write!(f, "interpreting an invalid 32-bit value as a char: {}", c), - InvalidUndefBytes(Some(p)) => write!( - f, - "reading uninitialized memory at {:?}, but this operation requires initialized memory", - p - ), - InvalidUndefBytes(None) => write!( - f, - "using uninitialized data, but this operation requires initialized memory" - ), - DeadLocal => write!(f, "accessing a dead local variable"), - ReadFromReturnPlace => write!(f, "tried to read from the return place"), - } - } -} - -/// Error information for when the program did something that might (or might not) be correct -/// to do according to the Rust spec, but due to limitations in the interpreter, the -/// operation could not be carried out. These limitations can differ between CTFE and the -/// Miri engine, e.g., CTFE does not support casting pointers to "real" integers. -/// -/// Currently, we also use this as fall-back error kind for errors that have not been -/// categorized yet. -pub enum UnsupportedOpInfo { - /// Free-form case. Only for errors that are never caught! - Unsupported(String), - /// Accessing an unsupported foreign static. - ReadForeignStatic(DefId), - /// Could not find MIR for a function. - NoMirFor(DefId), - /// Modified a static during const-eval. - /// FIXME: move this to `ConstEvalErrKind` through a machine hook. - ModifiedStatic, - /// Encountered a pointer where we needed raw bytes. - ReadPointerAsBytes, - /// Encountered raw bytes where we needed a pointer. - ReadBytesAsPointer, -} - -impl fmt::Debug for UnsupportedOpInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use UnsupportedOpInfo::*; - match self { - Unsupported(ref msg) => write!(f, "{}", msg), - ReadForeignStatic(did) => { - write!(f, "tried to read from foreign (extern) static {:?}", did) - } - NoMirFor(did) => write!(f, "could not load MIR for {:?}", did), - ModifiedStatic => write!( - f, - "tried to modify a static's initial value from another static's \ - initializer" - ), - - ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",), - ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), - } - } -} - -/// Error information for when the program exhausted the resources granted to it -/// by the interpreter. -pub enum ResourceExhaustionInfo { - /// The stack grew too big. - StackFrameLimitReached, - /// The program ran for too long. - /// - /// The exact limit is set by the `const_eval_limit` attribute. - StepLimitReached, -} - -impl fmt::Debug for ResourceExhaustionInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ResourceExhaustionInfo::*; - match self { - StackFrameLimitReached => { - write!(f, "reached the configured maximum number of stack frames") - } - StepLimitReached => { - write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)") - } - } - } -} - -/// A trait to work around not having trait object upcasting. -pub trait AsAny: Any { - fn as_any(&self) -> &dyn Any; -} - -impl AsAny for T { - #[inline(always)] - fn as_any(&self) -> &dyn Any { - self - } -} - -/// A trait for machine-specific errors (or other "machine stop" conditions). -pub trait MachineStopType: AsAny + fmt::Debug + Send {} -impl MachineStopType for String {} - -impl dyn MachineStopType { - #[inline(always)] - pub fn downcast_ref(&self) -> Option<&T> { - self.as_any().downcast_ref() - } -} - -pub enum InterpError<'tcx> { - /// The program caused undefined behavior. - UndefinedBehavior(UndefinedBehaviorInfo), - /// The program did something the interpreter does not support (some of these *might* be UB - /// but the interpreter is not sure). - Unsupported(UnsupportedOpInfo), - /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). - InvalidProgram(InvalidProgramInfo<'tcx>), - /// The program exhausted the interpreter's resources (stack/heap too big, - /// execution takes too long, ...). - ResourceExhaustion(ResourceExhaustionInfo), - /// Stop execution for a machine-controlled reason. This is never raised by - /// the core engine itself. - MachineStop(Box), -} - -pub type InterpResult<'tcx, T = ()> = Result>; - -impl fmt::Display for InterpError<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Forward `Display` to `Debug`. - fmt::Debug::fmt(self, f) - } -} - -impl fmt::Debug for InterpError<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use InterpError::*; - match *self { - Unsupported(ref msg) => write!(f, "{:?}", msg), - InvalidProgram(ref msg) => write!(f, "{:?}", msg), - UndefinedBehavior(ref msg) => write!(f, "{:?}", msg), - ResourceExhaustion(ref msg) => write!(f, "{:?}", msg), - MachineStop(ref msg) => write!(f, "{:?}", msg), - } - } -} - -impl InterpError<'_> { - /// Some errors allocate to be created as they contain free-form strings. - /// And sometimes we want to be sure that did not happen as it is a - /// waste of resources. - pub fn allocates(&self) -> bool { - match self { - // Zero-sized boxes do not allocate. - InterpError::MachineStop(b) => mem::size_of_val::(&**b) > 0, - InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) => true, - _ => false, - } - } -} diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs deleted file mode 100644 index 1b5fb4c9954cb..0000000000000 --- a/src/librustc/mir/interpret/mod.rs +++ /dev/null @@ -1,571 +0,0 @@ -//! An interpreter for MIR used in CTFE and by miri. - -#[macro_export] -macro_rules! err_unsup { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::Unsupported( - $crate::mir::interpret::UnsupportedOpInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_unsup_format { - ($($tt:tt)*) => { err_unsup!(Unsupported(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! err_inval { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::InvalidProgram( - $crate::mir::interpret::InvalidProgramInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_ub { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::UndefinedBehavior( - $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_ub_format { - ($($tt:tt)*) => { err_ub!(Ub(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! err_exhaust { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::ResourceExhaustion( - $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_machine_stop { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) - }; -} - -// In the `throw_*` macros, avoid `return` to make them work with `try {}`. -#[macro_export] -macro_rules! throw_unsup { - ($($tt:tt)*) => { Err::(err_unsup!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_unsup_format { - ($($tt:tt)*) => { throw_unsup!(Unsupported(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! throw_inval { - ($($tt:tt)*) => { Err::(err_inval!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_ub { - ($($tt:tt)*) => { Err::(err_ub!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_ub_format { - ($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! throw_exhaust { - ($($tt:tt)*) => { Err::(err_exhaust!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_machine_stop { - ($($tt:tt)*) => { Err::(err_machine_stop!($($tt)*))? }; -} - -mod allocation; -mod error; -mod pointer; -mod queries; -mod value; - -pub use self::error::{ - struct_error, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled, FrameInfo, - InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, - ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, -}; - -pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUndef}; - -pub use self::allocation::{Allocation, AllocationExtra, Relocations, UndefMask}; - -pub use self::pointer::{CheckInAllocMsg, Pointer, PointerArithmetic}; - -use crate::mir; -use crate::ty::codec::TyDecoder; -use crate::ty::layout::{self, Size}; -use crate::ty::subst::GenericArgKind; -use crate::ty::{self, Instance, Ty, TyCtxt}; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; -use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::{HashMapExt, Lock}; -use rustc_data_structures::tiny_list::TinyList; -use rustc_hir::def_id::DefId; -use rustc_macros::HashStable; -use rustc_serialize::{Decodable, Encodable, Encoder}; -use std::fmt; -use std::io; -use std::num::NonZeroU32; -use std::sync::atomic::{AtomicU32, Ordering}; - -/// Uniquely identifies one of the following: -/// - A constant -/// - A static -/// - A const fn where all arguments (if any) are zero-sized types -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, Lift)] -pub struct GlobalId<'tcx> { - /// For a constant or static, the `Instance` of the item itself. - /// For a promoted global, the `Instance` of the function they belong to. - pub instance: ty::Instance<'tcx>, - - /// The index for promoted globals within their function's `mir::Body`. - pub promoted: Option, -} - -/// Input argument for `tcx.lit_to_const`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)] -pub struct LitToConstInput<'tcx> { - /// The absolute value of the resultant constant. - pub lit: &'tcx LitKind, - /// The type of the constant. - pub ty: Ty<'tcx>, - /// If the constant is negative. - pub neg: bool, -} - -/// Error type for `tcx.lit_to_const`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] -pub enum LitToConstError { - /// The literal's inferred type did not match the expected `ty` in the input. - /// This is used for graceful error handling (`delay_span_bug`) in - /// type checking (`Const::from_anon_const`). - TypeError, - UnparseableFloat, - Reported, -} - -#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct AllocId(pub u64); - -impl fmt::Debug for AllocId { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -impl fmt::Display for AllocId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "alloc{}", self.0) - } -} - -impl rustc_serialize::UseSpecializedEncodable for AllocId {} -impl rustc_serialize::UseSpecializedDecodable for AllocId {} - -#[derive(RustcDecodable, RustcEncodable)] -enum AllocDiscriminant { - Alloc, - Fn, - Static, -} - -pub fn specialized_encode_alloc_id<'tcx, E: Encoder>( - encoder: &mut E, - tcx: TyCtxt<'tcx>, - alloc_id: AllocId, -) -> Result<(), E::Error> { - let alloc: GlobalAlloc<'tcx> = - tcx.alloc_map.lock().get(alloc_id).expect("no value for given alloc ID"); - match alloc { - GlobalAlloc::Memory(alloc) => { - trace!("encoding {:?} with {:#?}", alloc_id, alloc); - AllocDiscriminant::Alloc.encode(encoder)?; - alloc.encode(encoder)?; - } - GlobalAlloc::Function(fn_instance) => { - trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); - AllocDiscriminant::Fn.encode(encoder)?; - fn_instance.encode(encoder)?; - } - GlobalAlloc::Static(did) => { - // References to statics doesn't need to know about their allocations, - // just about its `DefId`. - AllocDiscriminant::Static.encode(encoder)?; - did.encode(encoder)?; - } - } - Ok(()) -} - -// Used to avoid infinite recursion when decoding cyclic allocations. -type DecodingSessionId = NonZeroU32; - -#[derive(Clone)] -enum State { - Empty, - InProgressNonAlloc(TinyList), - InProgress(TinyList, AllocId), - Done(AllocId), -} - -pub struct AllocDecodingState { - // For each `AllocId`, we keep track of which decoding state it's currently in. - decoding_state: Vec>, - // The offsets of each allocation in the data stream. - data_offsets: Vec, -} - -impl AllocDecodingState { - pub fn new_decoding_session(&self) -> AllocDecodingSession<'_> { - static DECODER_SESSION_ID: AtomicU32 = AtomicU32::new(0); - let counter = DECODER_SESSION_ID.fetch_add(1, Ordering::SeqCst); - - // Make sure this is never zero. - let session_id = DecodingSessionId::new((counter & 0x7FFFFFFF) + 1).unwrap(); - - AllocDecodingSession { state: self, session_id } - } - - pub fn new(data_offsets: Vec) -> Self { - let decoding_state = vec![Lock::new(State::Empty); data_offsets.len()]; - - Self { decoding_state, data_offsets } - } -} - -#[derive(Copy, Clone)] -pub struct AllocDecodingSession<'s> { - state: &'s AllocDecodingState, - session_id: DecodingSessionId, -} - -impl<'s> AllocDecodingSession<'s> { - /// Decodes an `AllocId` in a thread-safe way. - pub fn decode_alloc_id(&self, decoder: &mut D) -> Result - where - D: TyDecoder<'tcx>, - { - // Read the index of the allocation. - let idx = decoder.read_u32()? as usize; - let pos = self.state.data_offsets[idx] as usize; - - // Decode the `AllocDiscriminant` now so that we know if we have to reserve an - // `AllocId`. - let (alloc_kind, pos) = decoder.with_position(pos, |decoder| { - let alloc_kind = AllocDiscriminant::decode(decoder)?; - Ok((alloc_kind, decoder.position())) - })?; - - // Check the decoding state to see if it's already decoded or if we should - // decode it here. - let alloc_id = { - let mut entry = self.state.decoding_state[idx].lock(); - - match *entry { - State::Done(alloc_id) => { - return Ok(alloc_id); - } - ref mut entry @ State::Empty => { - // We are allowed to decode. - match alloc_kind { - AllocDiscriminant::Alloc => { - // If this is an allocation, we need to reserve an - // `AllocId` so we can decode cyclic graphs. - let alloc_id = decoder.tcx().alloc_map.lock().reserve(); - *entry = - State::InProgress(TinyList::new_single(self.session_id), alloc_id); - Some(alloc_id) - } - AllocDiscriminant::Fn | AllocDiscriminant::Static => { - // Fns and statics cannot be cyclic, and their `AllocId` - // is determined later by interning. - *entry = - State::InProgressNonAlloc(TinyList::new_single(self.session_id)); - None - } - } - } - State::InProgressNonAlloc(ref mut sessions) => { - if sessions.contains(&self.session_id) { - bug!("this should be unreachable"); - } else { - // Start decoding concurrently. - sessions.insert(self.session_id); - None - } - } - State::InProgress(ref mut sessions, alloc_id) => { - if sessions.contains(&self.session_id) { - // Don't recurse. - return Ok(alloc_id); - } else { - // Start decoding concurrently. - sessions.insert(self.session_id); - Some(alloc_id) - } - } - } - }; - - // Now decode the actual data. - let alloc_id = decoder.with_position(pos, |decoder| { - match alloc_kind { - AllocDiscriminant::Alloc => { - let alloc = <&'tcx Allocation as Decodable>::decode(decoder)?; - // We already have a reserved `AllocId`. - let alloc_id = alloc_id.unwrap(); - trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc); - decoder.tcx().alloc_map.lock().set_alloc_id_same_memory(alloc_id, alloc); - Ok(alloc_id) - } - AllocDiscriminant::Fn => { - assert!(alloc_id.is_none()); - trace!("creating fn alloc ID"); - let instance = ty::Instance::decode(decoder)?; - trace!("decoded fn alloc instance: {:?}", instance); - let alloc_id = decoder.tcx().alloc_map.lock().create_fn_alloc(instance); - Ok(alloc_id) - } - AllocDiscriminant::Static => { - assert!(alloc_id.is_none()); - trace!("creating extern static alloc ID"); - let did = DefId::decode(decoder)?; - trace!("decoded static def-ID: {:?}", did); - let alloc_id = decoder.tcx().alloc_map.lock().create_static_alloc(did); - Ok(alloc_id) - } - } - })?; - - self.state.decoding_state[idx].with_lock(|entry| { - *entry = State::Done(alloc_id); - }); - - Ok(alloc_id) - } -} - -/// An allocation in the global (tcx-managed) memory can be either a function pointer, -/// a static, or a "real" allocation with some data in it. -#[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable, HashStable)] -pub enum GlobalAlloc<'tcx> { - /// The alloc ID is used as a function pointer. - Function(Instance<'tcx>), - /// The alloc ID points to a "lazy" static variable that did not get computed (yet). - /// This is also used to break the cycle in recursive statics. - Static(DefId), - /// The alloc ID points to memory. - Memory(&'tcx Allocation), -} - -pub struct AllocMap<'tcx> { - /// Maps `AllocId`s to their corresponding allocations. - alloc_map: FxHashMap>, - - /// Used to ensure that statics and functions only get one associated `AllocId`. - /// Should never contain a `GlobalAlloc::Memory`! - // - // FIXME: Should we just have two separate dedup maps for statics and functions each? - dedup: FxHashMap, AllocId>, - - /// The `AllocId` to assign to the next requested ID. - /// Always incremented; never gets smaller. - next_id: AllocId, -} - -impl<'tcx> AllocMap<'tcx> { - pub fn new() -> Self { - AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(0) } - } - - /// Obtains a new allocation ID that can be referenced but does not - /// yet have an allocation backing it. - /// - /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such - /// an `AllocId` from a query. - pub fn reserve(&mut self) -> AllocId { - let next = self.next_id; - self.next_id.0 = self.next_id.0.checked_add(1).expect( - "You overflowed a u64 by incrementing by 1... \ - You've just earned yourself a free drink if we ever meet. \ - Seriously, how did you do that?!", - ); - next - } - - /// Reserves a new ID *if* this allocation has not been dedup-reserved before. - /// Should only be used for function pointers and statics, we don't want - /// to dedup IDs for "real" memory! - fn reserve_and_set_dedup(&mut self, alloc: GlobalAlloc<'tcx>) -> AllocId { - match alloc { - GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} - GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), - } - if let Some(&alloc_id) = self.dedup.get(&alloc) { - return alloc_id; - } - let id = self.reserve(); - debug!("creating alloc {:?} with id {}", alloc, id); - self.alloc_map.insert(id, alloc.clone()); - self.dedup.insert(alloc, id); - id - } - - /// Generates an `AllocId` for a static or return a cached one in case this function has been - /// called on the same static before. - pub fn create_static_alloc(&mut self, static_id: DefId) -> AllocId { - self.reserve_and_set_dedup(GlobalAlloc::Static(static_id)) - } - - /// Generates an `AllocId` for a function. Depending on the function type, - /// this might get deduplicated or assigned a new ID each time. - pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> AllocId { - // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated - // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be - // duplicated across crates. - // We thus generate a new `AllocId` for every mention of a function. This means that - // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. - // However, formatting code relies on function identity (see #58320), so we only do - // this for generic functions. Lifetime parameters are ignored. - let is_generic = instance.substs.into_iter().any(|kind| match kind.unpack() { - GenericArgKind::Lifetime(_) => false, - _ => true, - }); - if is_generic { - // Get a fresh ID. - let id = self.reserve(); - self.alloc_map.insert(id, GlobalAlloc::Function(instance)); - id - } else { - // Deduplicate. - self.reserve_and_set_dedup(GlobalAlloc::Function(instance)) - } - } - - /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical - /// `Allocation` with a different `AllocId`. - /// Statics with identical content will still point to the same `Allocation`, i.e., - /// their data will be deduplicated through `Allocation` interning -- but they - /// are different places in memory and as such need different IDs. - pub fn create_memory_alloc(&mut self, mem: &'tcx Allocation) -> AllocId { - let id = self.reserve(); - self.set_alloc_id_memory(id, mem); - id - } - - /// Returns `None` in case the `AllocId` is dangling. An `InterpretCx` can still have a - /// local `Allocation` for that `AllocId`, but having such an `AllocId` in a constant is - /// illegal and will likely ICE. - /// This function exists to allow const eval to detect the difference between evaluation- - /// local dangling pointers and allocations in constants/statics. - #[inline] - pub fn get(&self, id: AllocId) -> Option> { - self.alloc_map.get(&id).cloned() - } - - /// Panics if the `AllocId` does not refer to an `Allocation` - pub fn unwrap_memory(&self, id: AllocId) -> &'tcx Allocation { - match self.get(id) { - Some(GlobalAlloc::Memory(mem)) => mem, - _ => bug!("expected allocation ID {} to point to memory", id), - } - } - - /// Panics if the `AllocId` does not refer to a function - pub fn unwrap_fn(&self, id: AllocId) -> Instance<'tcx> { - match self.get(id) { - Some(GlobalAlloc::Function(instance)) => instance, - _ => bug!("expected allocation ID {} to point to a function", id), - } - } - - /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to - /// call this function twice, even with the same `Allocation` will ICE the compiler. - pub fn set_alloc_id_memory(&mut self, id: AllocId, mem: &'tcx Allocation) { - if let Some(old) = self.alloc_map.insert(id, GlobalAlloc::Memory(mem)) { - bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old); - } - } - - /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called - /// twice for the same `(AllocId, Allocation)` pair. - fn set_alloc_id_same_memory(&mut self, id: AllocId, mem: &'tcx Allocation) { - self.alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Methods to access integers in the target endianness -//////////////////////////////////////////////////////////////////////////////// - -#[inline] -pub fn write_target_uint( - endianness: layout::Endian, - mut target: &mut [u8], - data: u128, -) -> Result<(), io::Error> { - let len = target.len(); - match endianness { - layout::Endian::Little => target.write_uint128::(data, len), - layout::Endian::Big => target.write_uint128::(data, len), - } -} - -#[inline] -pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result { - match endianness { - layout::Endian::Little => source.read_uint128::(source.len()), - layout::Endian::Big => source.read_uint128::(source.len()), - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Methods to facilitate working with signed integers stored in a u128 -//////////////////////////////////////////////////////////////////////////////// - -/// Truncates `value` to `size` bits and then sign-extend it to 128 bits -/// (i.e., if it is negative, fill with 1's on the left). -#[inline] -pub fn sign_extend(value: u128, size: Size) -> u128 { - let size = size.bits(); - if size == 0 { - // Truncated until nothing is left. - return 0; - } - // Sign-extend it. - let shift = 128 - size; - // Shift the unsigned value to the left, then shift back to the right as signed - // (essentially fills with FF on the left). - (((value << shift) as i128) >> shift) as u128 -} - -/// Truncates `value` to `size` bits. -#[inline] -pub fn truncate(value: u128, size: Size) -> u128 { - let size = size.bits(); - if size == 0 { - // Truncated until nothing is left. - return 0; - } - let shift = 128 - size; - // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). - (value << shift) >> shift -} diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs deleted file mode 100644 index 7d862d43bba6b..0000000000000 --- a/src/librustc/mir/interpret/pointer.rs +++ /dev/null @@ -1,206 +0,0 @@ -use super::{AllocId, InterpResult}; - -use crate::ty::layout::{self, HasDataLayout, Size}; - -use rustc_macros::HashStable; - -use std::convert::TryFrom; -use std::fmt::{self, Display}; - -/// Used by `check_in_alloc` to indicate context of check -#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] -pub enum CheckInAllocMsg { - MemoryAccessTest, - NullPointerTest, - PointerArithmeticTest, - InboundsTest, -} - -impl Display for CheckInAllocMsg { - /// When this is printed as an error the context looks like this - /// "{test name} failed: pointer must be in-bounds at offset..." - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match *self { - CheckInAllocMsg::MemoryAccessTest => "Memory access", - CheckInAllocMsg::NullPointerTest => "Null pointer test", - CheckInAllocMsg::PointerArithmeticTest => "Pointer arithmetic", - CheckInAllocMsg::InboundsTest => "Inbounds test", - } - ) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Pointer arithmetic -//////////////////////////////////////////////////////////////////////////////// - -pub trait PointerArithmetic: layout::HasDataLayout { - // These are not supposed to be overridden. - - #[inline(always)] - fn pointer_size(&self) -> Size { - self.data_layout().pointer_size - } - - #[inline] - fn usize_max(&self) -> u64 { - let max_usize_plus_1 = 1u128 << self.pointer_size().bits(); - u64::try_from(max_usize_plus_1 - 1).unwrap() - } - - #[inline] - fn isize_max(&self) -> i64 { - let max_isize_plus_1 = 1u128 << (self.pointer_size().bits() - 1); - i64::try_from(max_isize_plus_1 - 1).unwrap() - } - - /// Helper function: truncate given value-"overflowed flag" pair to pointer size and - /// update "overflowed flag" if there was an overflow. - /// This should be called by all the other methods before returning! - #[inline] - fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) { - let val = val as u128; - let max_ptr_plus_1 = 1u128 << self.pointer_size().bits(); - ((val % max_ptr_plus_1) as u64, over || val >= max_ptr_plus_1) - } - - #[inline] - fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { - let res = val.overflowing_add(i); - self.truncate_to_ptr(res) - } - - // Overflow checking only works properly on the range from -u64 to +u64. - #[inline] - fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) { - // FIXME: is it possible to over/underflow here? - if i < 0 { - // Trickery to ensure that `i64::MIN` works fine: compute `n = -i`. - // This formula only works for true negative values; it overflows for zero! - let n = u64::MAX - (i as u64) + 1; - let res = val.overflowing_sub(n); - self.truncate_to_ptr(res) - } else { - self.overflowing_offset(val, i as u64) - } - } - - #[inline] - fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> { - let (res, over) = self.overflowing_offset(val, i); - if over { throw_ub!(PointerArithOverflow) } else { Ok(res) } - } - - #[inline] - fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> { - let (res, over) = self.overflowing_signed_offset(val, i128::from(i)); - if over { throw_ub!(PointerArithOverflow) } else { Ok(res) } - } -} - -impl PointerArithmetic for T {} - -/// `Pointer` is generic over the type that represents a reference to `Allocation`s, -/// thus making it possible for the most convenient representation to be used in -/// each context. -/// -/// Defaults to the index based and loosely coupled `AllocId`. -/// -/// `Pointer` is also generic over the `Tag` associated with each pointer, -/// which is used to do provenance tracking during execution. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] -#[derive(HashStable)] -pub struct Pointer { - pub alloc_id: Id, - pub offset: Size, - pub tag: Tag, -} - -static_assert_size!(Pointer, 16); - -impl fmt::Debug for Pointer { - default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}+{:x}[{:?}]", self.alloc_id, self.offset.bytes(), self.tag) - } -} -// Specialization for no tag -impl fmt::Debug for Pointer<(), Id> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}+{:x}", self.alloc_id, self.offset.bytes()) - } -} - -/// Produces a `Pointer` that points to the beginning of the `Allocation`. -impl From for Pointer { - #[inline(always)] - fn from(alloc_id: AllocId) -> Self { - Pointer::new(alloc_id, Size::ZERO) - } -} - -impl Pointer<()> { - #[inline(always)] - pub fn new(alloc_id: AllocId, offset: Size) -> Self { - Pointer { alloc_id, offset, tag: () } - } - - #[inline(always)] - pub fn with_tag(self, tag: Tag) -> Pointer { - Pointer::new_with_tag(self.alloc_id, self.offset, tag) - } -} - -impl<'tcx, Tag> Pointer { - #[inline(always)] - pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self { - Pointer { alloc_id, offset, tag } - } - - #[inline] - pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - Ok(Pointer::new_with_tag( - self.alloc_id, - Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?), - self.tag, - )) - } - - #[inline] - pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) { - let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes()); - (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over) - } - - #[inline(always)] - pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self { - self.overflowing_offset(i, cx).0 - } - - #[inline] - pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - Ok(Pointer::new_with_tag( - self.alloc_id, - Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?), - self.tag, - )) - } - - #[inline] - pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) { - let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i); - (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over) - } - - #[inline(always)] - pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self { - self.overflowing_signed_offset(i128::from(i), cx).0 - } - - #[inline(always)] - pub fn erase_tag(self) -> Pointer { - Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () } - } -} diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs deleted file mode 100644 index 1e47317cf1ace..0000000000000 --- a/src/librustc/mir/mod.rs +++ /dev/null @@ -1,2666 +0,0 @@ -//! MIR datatypes and passes. See the [rustc dev guide] for more info. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html - -use crate::mir::interpret::{GlobalAlloc, Scalar}; -use crate::mir::visit::MirVisitable; -use crate::ty::adjustment::PointerCast; -use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::layout::VariantIdx; -use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::subst::{Subst, SubstsRef}; -use crate::ty::{ - self, AdtDef, CanonicalUserTypeAnnotations, List, Region, Ty, TyCtxt, UserTypeAnnotationIndex, -}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, Namespace}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self, GeneratorKind}; - -use polonius_engine::Atom; -pub use rustc_ast::ast::Mutability; -use rustc_ast::ast::Name; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::graph::dominators::Dominators; -use rustc_data_structures::graph::{self, GraphSuccessors}; -use rustc_index::bit_set::BitMatrix; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_macros::HashStable; -use rustc_serialize::{Decodable, Encodable}; -use rustc_span::symbol::Symbol; -use rustc_span::{Span, DUMMY_SP}; -use std::borrow::Cow; -use std::fmt::{self, Debug, Display, Formatter, Write}; -use std::ops::Index; -use std::slice; -use std::{iter, mem, option, u32}; - -pub use self::cache::{BodyAndCache, ReadOnlyBodyAndCache}; -pub use self::query::*; -pub use crate::read_only; - -mod cache; -pub mod interpret; -pub mod mono; -mod query; -pub mod tcx; -pub mod traversal; -mod type_foldable; -pub mod visit; - -/// Types for locals -type LocalDecls<'tcx> = IndexVec>; - -pub trait HasLocalDecls<'tcx> { - fn local_decls(&self) -> &LocalDecls<'tcx>; -} - -impl<'tcx> HasLocalDecls<'tcx> for LocalDecls<'tcx> { - fn local_decls(&self) -> &LocalDecls<'tcx> { - self - } -} - -impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> { - fn local_decls(&self) -> &LocalDecls<'tcx> { - &self.local_decls - } -} - -/// The various "big phases" that MIR goes through. -/// -/// Warning: ordering of variants is significant. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[derive(HashStable)] -pub enum MirPhase { - Build = 0, - Const = 1, - Validated = 2, - Optimized = 3, -} - -impl MirPhase { - /// Gets the index of the current MirPhase within the set of all `MirPhase`s. - pub fn phase_index(&self) -> usize { - *self as usize - } -} - -/// The lowered representation of a single function. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable, TypeFoldable)] -pub struct Body<'tcx> { - /// A list of basic blocks. References to basic block use a newtyped index type `BasicBlock` - /// that indexes into this vector. - basic_blocks: IndexVec>, - - /// Records how far through the "desugaring and optimization" process this particular - /// MIR has traversed. This is particularly useful when inlining, since in that context - /// we instantiate the promoted constants and add them to our promoted vector -- but those - /// promoted items have already been optimized, whereas ours have not. This field allows - /// us to see the difference and forego optimization on the inlined promoted items. - pub phase: MirPhase, - - /// A list of source scopes; these are referenced by statements - /// and used for debuginfo. Indexed by a `SourceScope`. - pub source_scopes: IndexVec, - - /// The yield type of the function, if it is a generator. - pub yield_ty: Option>, - - /// Generator drop glue. - pub generator_drop: Option>>, - - /// The layout of a generator. Produced by the state transformation. - pub generator_layout: Option>, - - /// If this is a generator then record the type of source expression that caused this generator - /// to be created. - pub generator_kind: Option, - - /// Declarations of locals. - /// - /// The first local is the return value pointer, followed by `arg_count` - /// locals for the function arguments, followed by any user-declared - /// variables and temporaries. - pub local_decls: LocalDecls<'tcx>, - - /// User type annotations. - pub user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, - - /// The number of arguments this function takes. - /// - /// Starting at local 1, `arg_count` locals will be provided by the caller - /// and can be assumed to be initialized. - /// - /// If this MIR was built for a constant, this will be 0. - pub arg_count: usize, - - /// Mark an argument local (which must be a tuple) as getting passed as - /// its individual components at the LLVM level. - /// - /// This is used for the "rust-call" ABI. - pub spread_arg: Option, - - /// Debug information pertaining to user variables, including captures. - pub var_debug_info: Vec>, - - /// Mark this MIR of a const context other than const functions as having converted a `&&` or - /// `||` expression into `&` or `|` respectively. This is problematic because if we ever stop - /// this conversion from happening and use short circuiting, we will cause the following code - /// to change the value of `x`: `let mut x = 42; false && { x = 55; true };` - /// - /// List of places where control flow was destroyed. Used for error reporting. - pub control_flow_destroyed: Vec<(Span, String)>, - - /// A span representing this MIR, for error reporting. - pub span: Span, - - /// The user may be writing e.g. &[(SOME_CELL, 42)][i].1 and this would get promoted, because - /// we'd statically know that no thing with interior mutability will ever be available to the - /// user without some serious unsafe code. Now this means that our promoted is actually - /// &[(SOME_CELL, 42)] and the MIR using it will do the &promoted[i].1 projection because the - /// index may be a runtime value. Such a promoted value is illegal because it has reachable - /// interior mutability. This flag just makes this situation very obvious where the previous - /// implementation without the flag hid this situation silently. - /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. - pub ignore_interior_mut_in_const_validation: bool, -} - -impl<'tcx> Body<'tcx> { - pub fn new( - basic_blocks: IndexVec>, - source_scopes: IndexVec, - local_decls: LocalDecls<'tcx>, - user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, - arg_count: usize, - var_debug_info: Vec>, - span: Span, - control_flow_destroyed: Vec<(Span, String)>, - generator_kind: Option, - ) -> Self { - // We need `arg_count` locals, and one for the return place. - assert!( - local_decls.len() > arg_count, - "expected at least {} locals, got {}", - arg_count + 1, - local_decls.len() - ); - - Body { - phase: MirPhase::Build, - basic_blocks, - source_scopes, - yield_ty: None, - generator_drop: None, - generator_layout: None, - generator_kind, - local_decls, - user_type_annotations, - arg_count, - spread_arg: None, - var_debug_info, - span, - ignore_interior_mut_in_const_validation: false, - control_flow_destroyed, - } - } - - /// Returns a partially initialized MIR body containing only a list of basic blocks. - /// - /// The returned MIR contains no `LocalDecl`s (even for the return place) or source scopes. It - /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different - /// crate. - pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { - Body { - phase: MirPhase::Build, - basic_blocks, - source_scopes: IndexVec::new(), - yield_ty: None, - generator_drop: None, - generator_layout: None, - local_decls: IndexVec::new(), - user_type_annotations: IndexVec::new(), - arg_count: 0, - spread_arg: None, - span: DUMMY_SP, - control_flow_destroyed: Vec::new(), - generator_kind: None, - var_debug_info: Vec::new(), - ignore_interior_mut_in_const_validation: false, - } - } - - #[inline] - pub fn basic_blocks(&self) -> &IndexVec> { - &self.basic_blocks - } - - /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the - /// `START_BLOCK`. - pub fn is_cfg_cyclic(&self) -> bool { - graph::is_cyclic(self) - } - - #[inline] - pub fn local_kind(&self, local: Local) -> LocalKind { - let index = local.as_usize(); - if index == 0 { - debug_assert!( - self.local_decls[local].mutability == Mutability::Mut, - "return place should be mutable" - ); - - LocalKind::ReturnPointer - } else if index < self.arg_count + 1 { - LocalKind::Arg - } else if self.local_decls[local].is_user_variable() { - LocalKind::Var - } else { - LocalKind::Temp - } - } - - /// Returns an iterator over all temporaries. - #[inline] - pub fn temps_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - if self.local_decls[local].is_user_variable() { None } else { Some(local) } - }) - } - - /// Returns an iterator over all user-declared locals. - #[inline] - pub fn vars_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - self.local_decls[local].is_user_variable().then_some(local) - }) - } - - /// Returns an iterator over all user-declared mutable locals. - #[inline] - pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - let decl = &self.local_decls[local]; - if decl.is_user_variable() && decl.mutability == Mutability::Mut { - Some(local) - } else { - None - } - }) - } - - /// Returns an iterator over all user-declared mutable arguments and locals. - #[inline] - pub fn mut_vars_and_args_iter<'a>(&'a self) -> impl Iterator + 'a { - (1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - let decl = &self.local_decls[local]; - if (decl.is_user_variable() || index < self.arg_count + 1) - && decl.mutability == Mutability::Mut - { - Some(local) - } else { - None - } - }) - } - - /// Returns an iterator over all function arguments. - #[inline] - pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator { - let arg_count = self.arg_count; - (1..arg_count + 1).map(Local::new) - } - - /// Returns an iterator over all user-defined variables and compiler-generated temporaries (all - /// locals that are neither arguments nor the return place). - #[inline] - pub fn vars_and_temps_iter(&self) -> impl Iterator + ExactSizeIterator { - let arg_count = self.arg_count; - let local_count = self.local_decls.len(); - (arg_count + 1..local_count).map(Local::new) - } - - /// Changes a statement to a nop. This is both faster than deleting instructions and avoids - /// invalidating statement indices in `Location`s. - pub fn make_statement_nop(&mut self, location: Location) { - let block = &mut self.basic_blocks[location.block]; - debug_assert!(location.statement_index < block.statements.len()); - block.statements[location.statement_index].make_nop() - } - - /// Returns the source info associated with `location`. - pub fn source_info(&self, location: Location) -> &SourceInfo { - let block = &self[location.block]; - let stmts = &block.statements; - let idx = location.statement_index; - if idx < stmts.len() { - &stmts[idx].source_info - } else { - assert_eq!(idx, stmts.len()); - &block.terminator().source_info - } - } - - /// Checks if `sub` is a sub scope of `sup` - pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool { - while sub != sup { - match self.source_scopes[sub].parent_scope { - None => return false, - Some(p) => sub = p, - } - } - true - } - - /// Returns the return type; it always return first element from `local_decls` array. - pub fn return_ty(&self) -> Ty<'tcx> { - self.local_decls[RETURN_PLACE].ty - } - - /// Gets the location of the terminator for the given block. - pub fn terminator_loc(&self, bb: BasicBlock) -> Location { - Location { block: bb, statement_index: self[bb].statements.len() } - } -} - -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum Safety { - Safe, - /// Unsafe because of a PushUnsafeBlock - BuiltinUnsafe, - /// Unsafe because of an unsafe fn - FnUnsafe, - /// Unsafe because of an `unsafe` block - ExplicitUnsafe(hir::HirId), -} - -impl<'tcx> Index for Body<'tcx> { - type Output = BasicBlockData<'tcx>; - - #[inline] - fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { - &self.basic_blocks()[index] - } -} - -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)] -pub enum ClearCrossCrate { - Clear, - Set(T), -} - -impl ClearCrossCrate { - pub fn as_ref(&self) -> ClearCrossCrate<&T> { - match self { - ClearCrossCrate::Clear => ClearCrossCrate::Clear, - ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v), - } - } - - pub fn assert_crate_local(self) -> T { - match self { - ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"), - ClearCrossCrate::Set(v) => v, - } - } -} - -impl rustc_serialize::UseSpecializedEncodable for ClearCrossCrate {} -impl rustc_serialize::UseSpecializedDecodable for ClearCrossCrate {} - -/// Grouped information about the source code origin of a MIR entity. -/// Intended to be inspected by diagnostics and debuginfo. -/// Most passes can work with it as a whole, within a single function. -// The unofficial Cranelift backend, at least as of #65828, needs `SourceInfo` to implement `Eq` and -// `Hash`. Please ping @bjorn3 if removing them. -#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)] -pub struct SourceInfo { - /// The source span for the AST pertaining to this MIR entity. - pub span: Span, - - /// The source scope, keeping track of which bindings can be - /// seen by debuginfo, active lint levels, `unsafe {...}`, etc. - pub scope: SourceScope, -} - -/////////////////////////////////////////////////////////////////////////// -// Borrow kinds - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] -#[derive(HashStable)] -pub enum BorrowKind { - /// Data must be immutable and is aliasable. - Shared, - - /// The immediately borrowed place must be immutable, but projections from - /// it don't need to be. For example, a shallow borrow of `a.b` doesn't - /// conflict with a mutable borrow of `a.b.c`. - /// - /// This is used when lowering matches: when matching on a place we want to - /// ensure that place have the same value from the start of the match until - /// an arm is selected. This prevents this code from compiling: - /// - /// let mut x = &Some(0); - /// match *x { - /// None => (), - /// Some(_) if { x = &None; false } => (), - /// Some(_) => (), - /// } - /// - /// This can't be a shared borrow because mutably borrowing (*x as Some).0 - /// should not prevent `if let None = x { ... }`, for example, because the - /// mutating `(*x as Some).0` can't affect the discriminant of `x`. - /// We can also report errors with this kind of borrow differently. - Shallow, - - /// Data must be immutable but not aliasable. This kind of borrow - /// cannot currently be expressed by the user and is used only in - /// implicit closure bindings. It is needed when the closure is - /// borrowing or mutating a mutable referent, e.g.: - /// - /// let x: &mut isize = ...; - /// let y = || *x += 5; - /// - /// If we were to try to translate this closure into a more explicit - /// form, we'd encounter an error with the code as written: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// This is then illegal because you cannot mutate an `&mut` found - /// in an aliasable location. To solve, you'd have to translate with - /// an `&mut` borrow: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// Now the assignment to `**env.x` is legal, but creating a - /// mutable pointer to `x` is not because `x` is not mutable. We - /// could fix this by declaring `x` as `let mut x`. This is ok in - /// user code, if awkward, but extra weird for closures, since the - /// borrow is hidden. - /// - /// So we introduce a "unique imm" borrow -- the referent is - /// immutable, but not aliasable. This solves the problem. For - /// simplicity, we don't give users the way to express this - /// borrow, it's just used when translating closures. - Unique, - - /// Data is mutable and not aliasable. - Mut { - /// `true` if this borrow arose from method-call auto-ref - /// (i.e., `adjustment::Adjust::Borrow`). - allow_two_phase_borrow: bool, - }, -} - -impl BorrowKind { - pub fn allows_two_phase_borrow(&self) -> bool { - match *self { - BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false, - BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow, - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Variables and temps - -rustc_index::newtype_index! { - pub struct Local { - derive [HashStable] - DEBUG_FORMAT = "_{}", - const RETURN_PLACE = 0, - } -} - -impl Atom for Local { - fn index(self) -> usize { - Idx::index(self) - } -} - -/// Classifies locals into categories. See `Body::local_kind`. -#[derive(PartialEq, Eq, Debug, HashStable)] -pub enum LocalKind { - /// User-declared variable binding. - Var, - /// Compiler-introduced temporary. - Temp, - /// Function argument. - Arg, - /// Location of function's return value. - ReturnPointer, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct VarBindingForm<'tcx> { - /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? - pub binding_mode: ty::BindingMode, - /// If an explicit type was provided for this variable binding, - /// this holds the source Span of that type. - /// - /// NOTE: if you want to change this to a `HirId`, be wary that - /// doing so breaks incremental compilation (as of this writing), - /// while a `Span` does not cause our tests to fail. - pub opt_ty_info: Option, - /// Place of the RHS of the =, or the subject of the `match` where this - /// variable is initialized. None in the case of `let PATTERN;`. - /// Some((None, ..)) in the case of and `let [mut] x = ...` because - /// (a) the right-hand side isn't evaluated as a place expression. - /// (b) it gives a way to separate this case from the remaining cases - /// for diagnostics. - pub opt_match_place: Option<(Option>, Span)>, - /// The span of the pattern in which this variable was bound. - pub pat_span: Span, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] -pub enum BindingForm<'tcx> { - /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. - Var(VarBindingForm<'tcx>), - /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit. - ImplicitSelf(ImplicitSelfKind), - /// Reference used in a guard expression to ensure immutability. - RefForGuard, -} - -/// Represents what type of implicit self a function has, if any. -#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum ImplicitSelfKind { - /// Represents a `fn x(self);`. - Imm, - /// Represents a `fn x(mut self);`. - Mut, - /// Represents a `fn x(&self);`. - ImmRef, - /// Represents a `fn x(&mut self);`. - MutRef, - /// Represents when a function does not have a self argument or - /// when a function has a `self: X` argument. - None, -} - -CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, } - -mod binding_form_impl { - use crate::ich::StableHashingContext; - use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - - impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - use super::BindingForm::*; - ::std::mem::discriminant(self).hash_stable(hcx, hasher); - - match self { - Var(binding) => binding.hash_stable(hcx, hasher), - ImplicitSelf(kind) => kind.hash_stable(hcx, hasher), - RefForGuard => (), - } - } - } -} - -/// `BlockTailInfo` is attached to the `LocalDecl` for temporaries -/// created during evaluation of expressions in a block tail -/// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`. -/// -/// It is used to improve diagnostics when such temporaries are -/// involved in borrow_check errors, e.g., explanations of where the -/// temporaries come from, when their destructors are run, and/or how -/// one might revise the code to satisfy the borrow checker's rules. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct BlockTailInfo { - /// If `true`, then the value resulting from evaluating this tail - /// expression is ignored by the block's expression context. - /// - /// Examples include `{ ...; tail };` and `let _ = { ...; tail };` - /// but not e.g., `let _x = { ...; tail };` - pub tail_result_is_ignored: bool, -} - -/// A MIR local. -/// -/// This can be a binding declared by the user, a temporary inserted by the compiler, a function -/// argument, or the return place. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct LocalDecl<'tcx> { - /// Whether this is a mutable minding (i.e., `let x` or `let mut x`). - /// - /// Temporaries and the return place are always mutable. - pub mutability: Mutability, - - // FIXME(matthewjasper) Don't store in this in `Body` - pub local_info: LocalInfo<'tcx>, - - /// `true` if this is an internal local. - /// - /// These locals are not based on types in the source code and are only used - /// for a few desugarings at the moment. - /// - /// The generator transformation will sanity check the locals which are live - /// across a suspension point against the type components of the generator - /// which type checking knows are live across a suspension point. We need to - /// flag drop flags to avoid triggering this check as they are introduced - /// after typeck. - /// - /// Unsafety checking will also ignore dereferences of these locals, - /// so they can be used for raw pointers only used in a desugaring. - /// - /// This should be sound because the drop flags are fully algebraic, and - /// therefore don't affect the OIBIT or outlives properties of the - /// generator. - pub internal: bool, - - /// If this local is a temporary and `is_block_tail` is `Some`, - /// then it is a temporary created for evaluation of some - /// subexpression of some block's tail expression (with no - /// intervening statement context). - // FIXME(matthewjasper) Don't store in this in `Body` - pub is_block_tail: Option, - - /// The type of this local. - pub ty: Ty<'tcx>, - - /// If the user manually ascribed a type to this variable, - /// e.g., via `let x: T`, then we carry that type here. The MIR - /// borrow checker needs this information since it can affect - /// region inference. - // FIXME(matthewjasper) Don't store in this in `Body` - pub user_ty: UserTypeProjections, - - /// The *syntactic* (i.e., not visibility) source scope the local is defined - /// in. If the local was defined in a let-statement, this - /// is *within* the let-statement, rather than outside - /// of it. - /// - /// This is needed because the visibility source scope of locals within - /// a let-statement is weird. - /// - /// The reason is that we want the local to be *within* the let-statement - /// for lint purposes, but we want the local to be *after* the let-statement - /// for names-in-scope purposes. - /// - /// That's it, if we have a let-statement like the one in this - /// function: - /// - /// ``` - /// fn foo(x: &str) { - /// #[allow(unused_mut)] - /// let mut x: u32 = { // <- one unused mut - /// let mut y: u32 = x.parse().unwrap(); - /// y + 2 - /// }; - /// drop(x); - /// } - /// ``` - /// - /// Then, from a lint point of view, the declaration of `x: u32` - /// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the - /// lint scopes are the same as the AST/HIR nesting. - /// - /// However, from a name lookup point of view, the scopes look more like - /// as if the let-statements were `match` expressions: - /// - /// ``` - /// fn foo(x: &str) { - /// match { - /// match x.parse().unwrap() { - /// y => y + 2 - /// } - /// } { - /// x => drop(x) - /// }; - /// } - /// ``` - /// - /// We care about the name-lookup scopes for debuginfo - if the - /// debuginfo instruction pointer is at the call to `x.parse()`, we - /// want `x` to refer to `x: &str`, but if it is at the call to - /// `drop(x)`, we want it to refer to `x: u32`. - /// - /// To allow both uses to work, we need to have more than a single scope - /// for a local. We have the `source_info.scope` represent the "syntactic" - /// lint scope (with a variable being under its let block) while the - /// `var_debug_info.source_info.scope` represents the "local variable" - /// scope (where the "rest" of a block is under all prior let-statements). - /// - /// The end result looks like this: - /// - /// ```text - /// ROOT SCOPE - /// │{ argument x: &str } - /// │ - /// │ │{ #[allow(unused_mut)] } // This is actually split into 2 scopes - /// │ │ // in practice because I'm lazy. - /// │ │ - /// │ │← x.source_info.scope - /// │ │← `x.parse().unwrap()` - /// │ │ - /// │ │ │← y.source_info.scope - /// │ │ - /// │ │ │{ let y: u32 } - /// │ │ │ - /// │ │ │← y.var_debug_info.source_info.scope - /// │ │ │← `y + 2` - /// │ - /// │ │{ let x: u32 } - /// │ │← x.var_debug_info.source_info.scope - /// │ │← `drop(x)` // This accesses `x: u32`. - /// ``` - pub source_info: SourceInfo, -} - -/// Extra information about a local that's used for diagnostics. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub enum LocalInfo<'tcx> { - /// A user-defined local variable or function parameter - /// - /// The `BindingForm` is solely used for local diagnostics when generating - /// warnings/errors when compiling the current crate, and therefore it need - /// not be visible across crates. - User(ClearCrossCrate>), - /// A temporary created that references the static with the given `DefId`. - StaticRef { def_id: DefId, is_thread_local: bool }, - /// Any other temporary, the return place, or an anonymous function parameter. - Other, -} - -impl<'tcx> LocalDecl<'tcx> { - /// Returns `true` only if local is a binding that can itself be - /// made mutable via the addition of the `mut` keyword, namely - /// something like the occurrences of `x` in: - /// - `fn foo(x: Type) { ... }`, - /// - `let x = ...`, - /// - or `match ... { C(x) => ... }` - pub fn can_be_made_mutable(&self) -> bool { - match self.local_info { - LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - }))) => true, - - LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( - ImplicitSelfKind::Imm, - ))) => true, - - _ => false, - } - } - - /// Returns `true` if local is definitely not a `ref ident` or - /// `ref mut ident` binding. (Such bindings cannot be made into - /// mutable bindings, but the inverse does not necessarily hold). - pub fn is_nonref_binding(&self) -> bool { - match self.local_info { - LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - }))) => true, - - LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_))) => true, - - _ => false, - } - } - - /// Returns `true` if this variable is a named variable or function - /// parameter declared by the user. - #[inline] - pub fn is_user_variable(&self) -> bool { - match self.local_info { - LocalInfo::User(_) => true, - _ => false, - } - } - - /// Returns `true` if this is a reference to a variable bound in a `match` - /// expression that is used to access said variable for the guard of the - /// match arm. - pub fn is_ref_for_guard(&self) -> bool { - match self.local_info { - LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard)) => true, - _ => false, - } - } - - /// Returns `Some` if this is a reference to a static item that is used to - /// access that static - pub fn is_ref_to_static(&self) -> bool { - match self.local_info { - LocalInfo::StaticRef { .. } => true, - _ => false, - } - } - - /// Returns `Some` if this is a reference to a static item that is used to - /// access that static - pub fn is_ref_to_thread_local(&self) -> bool { - match self.local_info { - LocalInfo::StaticRef { is_thread_local, .. } => is_thread_local, - _ => false, - } - } - - /// Returns `true` is the local is from a compiler desugaring, e.g., - /// `__next` from a `for` loop. - #[inline] - pub fn from_compiler_desugaring(&self) -> bool { - self.source_info.span.desugaring_kind().is_some() - } - - /// Creates a new `LocalDecl` for a temporary. - #[inline] - pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self { - Self::new_local(ty, Mutability::Mut, false, span) - } - - /// Converts `self` into same `LocalDecl` except tagged as immutable. - #[inline] - pub fn immutable(mut self) -> Self { - self.mutability = Mutability::Not; - self - } - - /// Converts `self` into same `LocalDecl` except tagged as internal temporary. - #[inline] - pub fn block_tail(mut self, info: BlockTailInfo) -> Self { - assert!(self.is_block_tail.is_none()); - self.is_block_tail = Some(info); - self - } - - /// Creates a new `LocalDecl` for a internal temporary. - #[inline] - pub fn new_internal(ty: Ty<'tcx>, span: Span) -> Self { - Self::new_local(ty, Mutability::Mut, true, span) - } - - #[inline] - fn new_local(ty: Ty<'tcx>, mutability: Mutability, internal: bool, span: Span) -> Self { - LocalDecl { - mutability, - ty, - user_ty: UserTypeProjections::none(), - source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }, - internal, - local_info: LocalInfo::Other, - is_block_tail: None, - } - } - - /// Builds a `LocalDecl` for the return place. - /// - /// This must be inserted into the `local_decls` list as the first local. - #[inline] - pub fn new_return_place(return_ty: Ty<'_>, span: Span) -> LocalDecl<'_> { - LocalDecl { - mutability: Mutability::Mut, - ty: return_ty, - user_ty: UserTypeProjections::none(), - source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }, - internal: false, - is_block_tail: None, - local_info: LocalInfo::Other, - } - } -} - -/// Debug information pertaining to a user variable. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VarDebugInfo<'tcx> { - pub name: Name, - - /// Source info of the user variable, including the scope - /// within which the variable is visible (to debuginfo) - /// (see `LocalDecl`'s `source_info` field for more details). - pub source_info: SourceInfo, - - /// Where the data for this user variable is to be found. - /// NOTE(eddyb) There's an unenforced invariant that this `Place` is - /// based on a `Local`, not a `Static`, and contains no indexing. - pub place: Place<'tcx>, -} - -/////////////////////////////////////////////////////////////////////////// -// BasicBlock - -rustc_index::newtype_index! { - pub struct BasicBlock { - derive [HashStable] - DEBUG_FORMAT = "bb{}", - const START_BLOCK = 0, - } -} - -impl BasicBlock { - pub fn start_location(self) -> Location { - Location { block: self, statement_index: 0 } - } -} - -/////////////////////////////////////////////////////////////////////////// -// BasicBlockData and Terminator - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct BasicBlockData<'tcx> { - /// List of statements in this block. - pub statements: Vec>, - - /// Terminator for this block. - /// - /// N.B., this should generally ONLY be `None` during construction. - /// Therefore, you should generally access it via the - /// `terminator()` or `terminator_mut()` methods. The only - /// exception is that certain passes, such as `simplify_cfg`, swap - /// out the terminator temporarily with `None` while they continue - /// to recurse over the set of basic blocks. - pub terminator: Option>, - - /// If true, this block lies on an unwind path. This is used - /// during codegen where distinct kinds of basic blocks may be - /// generated (particularly for MSVC cleanup). Unwind blocks must - /// only branch to other unwind blocks. - pub is_cleanup: bool, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct Terminator<'tcx> { - pub source_info: SourceInfo, - pub kind: TerminatorKind<'tcx>, -} - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub enum TerminatorKind<'tcx> { - /// Block should have one successor in the graph; we jump there. - Goto { target: BasicBlock }, - - /// Operand evaluates to an integer; jump depending on its value - /// to one of the targets, and otherwise fallback to `otherwise`. - SwitchInt { - /// The discriminant value being tested. - discr: Operand<'tcx>, - - /// The type of value being tested. - switch_ty: Ty<'tcx>, - - /// Possible values. The locations to branch to in each case - /// are found in the corresponding indices from the `targets` vector. - values: Cow<'tcx, [u128]>, - - /// Possible branch sites. The last element of this vector is used - /// for the otherwise branch, so targets.len() == values.len() + 1 - /// should hold. - // - // This invariant is quite non-obvious and also could be improved. - // One way to make this invariant is to have something like this instead: - // - // branches: Vec<(ConstInt, BasicBlock)>, - // otherwise: Option // exhaustive if None - // - // However we’ve decided to keep this as-is until we figure a case - // where some other approach seems to be strictly better than other. - targets: Vec, - }, - - /// Indicates that the landing pad is finished and unwinding should - /// continue. Emitted by `build::scope::diverge_cleanup`. - Resume, - - /// Indicates that the landing pad is finished and that the process - /// should abort. Used to prevent unwinding for foreign items. - Abort, - - /// Indicates a normal return. The return place should have - /// been filled in by now. This should occur at most once. - Return, - - /// Indicates a terminator that can never be reached. - Unreachable, - - /// Drop the `Place`. - Drop { location: Place<'tcx>, target: BasicBlock, unwind: Option }, - - /// Drop the `Place` and assign the new value over it. This ensures - /// that the assignment to `P` occurs *even if* the destructor for - /// place unwinds. Its semantics are best explained by the - /// elaboration: - /// - /// ``` - /// BB0 { - /// DropAndReplace(P <- V, goto BB1, unwind BB2) - /// } - /// ``` - /// - /// becomes - /// - /// ``` - /// BB0 { - /// Drop(P, goto BB1, unwind BB2) - /// } - /// BB1 { - /// // P is now uninitialized - /// P <- V - /// } - /// BB2 { - /// // P is now uninitialized -- its dtor panicked - /// P <- V - /// } - /// ``` - DropAndReplace { - location: Place<'tcx>, - value: Operand<'tcx>, - target: BasicBlock, - unwind: Option, - }, - - /// Block ends with a call of a converging function. - Call { - /// The function that’s being called. - func: Operand<'tcx>, - /// Arguments the function is called with. - /// These are owned by the callee, which is free to modify them. - /// This allows the memory occupied by "by-value" arguments to be - /// reused across function calls without duplicating the contents. - args: Vec>, - /// Destination for the return value. If some, the call is converging. - destination: Option<(Place<'tcx>, BasicBlock)>, - /// Cleanups to be done if the call unwinds. - cleanup: Option, - /// `true` if this is from a call in HIR rather than from an overloaded - /// operator. True for overloaded function call. - from_hir_call: bool, - }, - - /// Jump to the target if the condition has the expected value, - /// otherwise panic with a message and a cleanup target. - Assert { - cond: Operand<'tcx>, - expected: bool, - msg: AssertMessage<'tcx>, - target: BasicBlock, - cleanup: Option, - }, - - /// A suspend point. - Yield { - /// The value to return. - value: Operand<'tcx>, - /// Where to resume to. - resume: BasicBlock, - /// The place to store the resume argument in. - resume_arg: Place<'tcx>, - /// Cleanup to be done if the generator is dropped at this suspend point. - drop: Option, - }, - - /// Indicates the end of the dropping of a generator. - GeneratorDrop, - - /// A block where control flow only ever takes one real path, but borrowck - /// needs to be more conservative. - FalseEdges { - /// The target normal control flow will take. - real_target: BasicBlock, - /// A block control flow could conceptually jump to, but won't in - /// practice. - imaginary_target: BasicBlock, - }, - /// A terminator for blocks that only take one path in reality, but where we - /// reserve the right to unwind in borrowck, even if it won't happen in practice. - /// This can arise in infinite loops with no function calls for example. - FalseUnwind { - /// The target normal control flow will take. - real_target: BasicBlock, - /// The imaginary cleanup block link. This particular path will never be taken - /// in practice, but in order to avoid fragility we want to always - /// consider it in borrowck. We don't want to accept programs which - /// pass borrowck only when `panic=abort` or some assertions are disabled - /// due to release vs. debug mode builds. This needs to be an `Option` because - /// of the `remove_noop_landing_pads` and `no_landing_pads` passes. - unwind: Option, - }, -} - -/// Information about an assertion failure. -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub enum AssertKind { - BoundsCheck { len: O, index: O }, - Overflow(BinOp), - OverflowNeg, - DivisionByZero, - RemainderByZero, - ResumedAfterReturn(GeneratorKind), - ResumedAfterPanic(GeneratorKind), -} - -/// Type for MIR `Assert` terminator error messages. -pub type AssertMessage<'tcx> = AssertKind>; - -pub type Successors<'a> = - iter::Chain, slice::Iter<'a, BasicBlock>>; -pub type SuccessorsMut<'a> = - iter::Chain, slice::IterMut<'a, BasicBlock>>; - -impl<'tcx> Terminator<'tcx> { - pub fn successors(&self) -> Successors<'_> { - self.kind.successors() - } - - pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { - self.kind.successors_mut() - } - - pub fn unwind(&self) -> Option<&Option> { - self.kind.unwind() - } - - pub fn unwind_mut(&mut self) -> Option<&mut Option> { - self.kind.unwind_mut() - } -} - -impl<'tcx> TerminatorKind<'tcx> { - pub fn if_( - tcx: TyCtxt<'tcx>, - cond: Operand<'tcx>, - t: BasicBlock, - f: BasicBlock, - ) -> TerminatorKind<'tcx> { - static BOOL_SWITCH_FALSE: &[u128] = &[0]; - TerminatorKind::SwitchInt { - discr: cond, - switch_ty: tcx.types.bool, - values: From::from(BOOL_SWITCH_FALSE), - targets: vec![f, t], - } - } - - pub fn successors(&self) -> Successors<'_> { - use self::TerminatorKind::*; - match *self { - Resume - | Abort - | GeneratorDrop - | Return - | Unreachable - | Call { destination: None, cleanup: None, .. } => None.into_iter().chain(&[]), - Goto { target: ref t } - | Call { destination: None, cleanup: Some(ref t), .. } - | Call { destination: Some((_, ref t)), cleanup: None, .. } - | Yield { resume: ref t, drop: None, .. } - | DropAndReplace { target: ref t, unwind: None, .. } - | Drop { target: ref t, unwind: None, .. } - | Assert { target: ref t, cleanup: None, .. } - | FalseUnwind { real_target: ref t, unwind: None } => Some(t).into_iter().chain(&[]), - Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. } - | Yield { resume: ref t, drop: Some(ref u), .. } - | DropAndReplace { target: ref t, unwind: Some(ref u), .. } - | Drop { target: ref t, unwind: Some(ref u), .. } - | Assert { target: ref t, cleanup: Some(ref u), .. } - | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => { - Some(t).into_iter().chain(slice::from_ref(u)) - } - SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]), - FalseEdges { ref real_target, ref imaginary_target } => { - Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)) - } - } - } - - pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { - use self::TerminatorKind::*; - match *self { - Resume - | Abort - | GeneratorDrop - | Return - | Unreachable - | Call { destination: None, cleanup: None, .. } => None.into_iter().chain(&mut []), - Goto { target: ref mut t } - | Call { destination: None, cleanup: Some(ref mut t), .. } - | Call { destination: Some((_, ref mut t)), cleanup: None, .. } - | Yield { resume: ref mut t, drop: None, .. } - | DropAndReplace { target: ref mut t, unwind: None, .. } - | Drop { target: ref mut t, unwind: None, .. } - | Assert { target: ref mut t, cleanup: None, .. } - | FalseUnwind { real_target: ref mut t, unwind: None } => { - Some(t).into_iter().chain(&mut []) - } - Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. } - | Yield { resume: ref mut t, drop: Some(ref mut u), .. } - | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. } - | Drop { target: ref mut t, unwind: Some(ref mut u), .. } - | Assert { target: ref mut t, cleanup: Some(ref mut u), .. } - | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => { - Some(t).into_iter().chain(slice::from_mut(u)) - } - SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]), - FalseEdges { ref mut real_target, ref mut imaginary_target } => { - Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) - } - } - } - - pub fn unwind(&self) -> Option<&Option> { - match *self { - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop - | TerminatorKind::Yield { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::FalseEdges { .. } => None, - TerminatorKind::Call { cleanup: ref unwind, .. } - | TerminatorKind::Assert { cleanup: ref unwind, .. } - | TerminatorKind::DropAndReplace { ref unwind, .. } - | TerminatorKind::Drop { ref unwind, .. } - | TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind), - } - } - - pub fn unwind_mut(&mut self) -> Option<&mut Option> { - match *self { - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop - | TerminatorKind::Yield { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::FalseEdges { .. } => None, - TerminatorKind::Call { cleanup: ref mut unwind, .. } - | TerminatorKind::Assert { cleanup: ref mut unwind, .. } - | TerminatorKind::DropAndReplace { ref mut unwind, .. } - | TerminatorKind::Drop { ref mut unwind, .. } - | TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind), - } - } -} - -impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { - BasicBlockData { statements: vec![], terminator, is_cleanup: false } - } - - /// Accessor for terminator. - /// - /// Terminator may not be None after construction of the basic block is complete. This accessor - /// provides a convenience way to reach the terminator. - pub fn terminator(&self) -> &Terminator<'tcx> { - self.terminator.as_ref().expect("invalid terminator state") - } - - pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> { - self.terminator.as_mut().expect("invalid terminator state") - } - - pub fn retain_statements(&mut self, mut f: F) - where - F: FnMut(&mut Statement<'_>) -> bool, - { - for s in &mut self.statements { - if !f(s) { - s.make_nop(); - } - } - } - - pub fn expand_statements(&mut self, mut f: F) - where - F: FnMut(&mut Statement<'tcx>) -> Option, - I: iter::TrustedLen>, - { - // Gather all the iterators we'll need to splice in, and their positions. - let mut splices: Vec<(usize, I)> = vec![]; - let mut extra_stmts = 0; - for (i, s) in self.statements.iter_mut().enumerate() { - if let Some(mut new_stmts) = f(s) { - if let Some(first) = new_stmts.next() { - // We can already store the first new statement. - *s = first; - - // Save the other statements for optimized splicing. - let remaining = new_stmts.size_hint().0; - if remaining > 0 { - splices.push((i + 1 + extra_stmts, new_stmts)); - extra_stmts += remaining; - } - } else { - s.make_nop(); - } - } - } - - // Splice in the new statements, from the end of the block. - // FIXME(eddyb) This could be more efficient with a "gap buffer" - // where a range of elements ("gap") is left uninitialized, with - // splicing adding new elements to the end of that gap and moving - // existing elements from before the gap to the end of the gap. - // For now, this is safe code, emulating a gap but initializing it. - let mut gap = self.statements.len()..self.statements.len() + extra_stmts; - self.statements.resize( - gap.end, - Statement { - source_info: SourceInfo { span: DUMMY_SP, scope: OUTERMOST_SOURCE_SCOPE }, - kind: StatementKind::Nop, - }, - ); - for (splice_start, new_stmts) in splices.into_iter().rev() { - let splice_end = splice_start + new_stmts.size_hint().0; - while gap.end > splice_end { - gap.start -= 1; - gap.end -= 1; - self.statements.swap(gap.start, gap.end); - } - self.statements.splice(splice_start..splice_end, new_stmts); - gap.end = splice_start; - } - } - - pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { - if index < self.statements.len() { &self.statements[index] } else { &self.terminator } - } -} - -impl AssertKind { - /// Getting a description does not require `O` to be printable, and does not - /// require allocation. - /// The caller is expected to handle `BoundsCheck` separately. - pub fn description(&self) -> &'static str { - use AssertKind::*; - match self { - Overflow(BinOp::Add) => "attempt to add with overflow", - Overflow(BinOp::Sub) => "attempt to subtract with overflow", - Overflow(BinOp::Mul) => "attempt to multiply with overflow", - Overflow(BinOp::Div) => "attempt to divide with overflow", - Overflow(BinOp::Rem) => "attempt to calculate the remainder with overflow", - OverflowNeg => "attempt to negate with overflow", - Overflow(BinOp::Shr) => "attempt to shift right with overflow", - Overflow(BinOp::Shl) => "attempt to shift left with overflow", - Overflow(op) => bug!("{:?} cannot overflow", op), - DivisionByZero => "attempt to divide by zero", - RemainderByZero => "attempt to calculate the remainder with a divisor of zero", - ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion", - ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion", - ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking", - ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking", - BoundsCheck { .. } => bug!("Unexpected AssertKind"), - } - } -} - -impl fmt::Debug for AssertKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use AssertKind::*; - match self { - BoundsCheck { ref len, ref index } => { - write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index) - } - _ => write!(f, "{}", self.description()), - } - } -} - -impl<'tcx> Debug for TerminatorKind<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - self.fmt_head(fmt)?; - let successor_count = self.successors().count(); - let labels = self.fmt_successor_labels(); - assert_eq!(successor_count, labels.len()); - - match successor_count { - 0 => Ok(()), - - 1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()), - - _ => { - write!(fmt, " -> [")?; - for (i, target) in self.successors().enumerate() { - if i > 0 { - write!(fmt, ", ")?; - } - write!(fmt, "{}: {:?}", labels[i], target)?; - } - write!(fmt, "]") - } - } - } -} - -impl<'tcx> TerminatorKind<'tcx> { - /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the - /// successor basic block, if any. The only information not included is the list of possible - /// successors, which may be rendered differently between the text and the graphviz format. - pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { - use self::TerminatorKind::*; - match self { - Goto { .. } => write!(fmt, "goto"), - SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr), - Return => write!(fmt, "return"), - GeneratorDrop => write!(fmt, "generator_drop"), - Resume => write!(fmt, "resume"), - Abort => write!(fmt, "abort"), - Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value), - Unreachable => write!(fmt, "unreachable"), - Drop { location, .. } => write!(fmt, "drop({:?})", location), - DropAndReplace { location, value, .. } => { - write!(fmt, "replace({:?} <- {:?})", location, value) - } - Call { func, args, destination, .. } => { - if let Some((destination, _)) = destination { - write!(fmt, "{:?} = ", destination)?; - } - write!(fmt, "{:?}(", func)?; - for (index, arg) in args.iter().enumerate() { - if index > 0 { - write!(fmt, ", ")?; - } - write!(fmt, "{:?}", arg)?; - } - write!(fmt, ")") - } - Assert { cond, expected, msg, .. } => { - write!(fmt, "assert(")?; - if !expected { - write!(fmt, "!")?; - } - write!(fmt, "{:?}, \"{:?}\")", cond, msg) - } - FalseEdges { .. } => write!(fmt, "falseEdges"), - FalseUnwind { .. } => write!(fmt, "falseUnwind"), - } - } - - /// Returns the list of labels for the edges to the successor basic blocks. - pub fn fmt_successor_labels(&self) -> Vec> { - use self::TerminatorKind::*; - match *self { - Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], - Goto { .. } => vec!["".into()], - SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| { - let param_env = ty::ParamEnv::empty(); - let switch_ty = tcx.lift(&switch_ty).unwrap(); - let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; - values - .iter() - .map(|&u| { - ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) - .to_string() - .into() - }) - .chain(iter::once("otherwise".into())) - .collect() - }), - Call { destination: Some(_), cleanup: Some(_), .. } => { - vec!["return".into(), "unwind".into()] - } - Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()], - Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()], - Call { destination: None, cleanup: None, .. } => vec![], - Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()], - Yield { drop: None, .. } => vec!["resume".into()], - DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => { - vec!["return".into()] - } - DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => { - vec!["return".into(), "unwind".into()] - } - Assert { cleanup: None, .. } => vec!["".into()], - Assert { .. } => vec!["success".into(), "unwind".into()], - FalseEdges { .. } => vec!["real".into(), "imaginary".into()], - FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()], - FalseUnwind { unwind: None, .. } => vec!["real".into()], - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Statements - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct Statement<'tcx> { - pub source_info: SourceInfo, - pub kind: StatementKind<'tcx>, -} - -// `Statement` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(Statement<'_>, 32); - -impl Statement<'_> { - /// Changes a statement to a nop. This is both faster than deleting instructions and avoids - /// invalidating statement indices in `Location`s. - pub fn make_nop(&mut self) { - self.kind = StatementKind::Nop - } - - /// Changes a statement to a nop and returns the original statement. - pub fn replace_nop(&mut self) -> Self { - Statement { - source_info: self.source_info, - kind: mem::replace(&mut self.kind, StatementKind::Nop), - } - } -} - -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub enum StatementKind<'tcx> { - /// Write the RHS Rvalue to the LHS Place. - Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), - - /// This represents all the reading that a pattern match may do - /// (e.g., inspecting constants and discriminant values), and the - /// kind of pattern it comes from. This is in order to adapt potential - /// error messages to these specific patterns. - /// - /// Note that this also is emitted for regular `let` bindings to ensure that locals that are - /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;` - FakeRead(FakeReadCause, Box>), - - /// Write the discriminant for a variant to the enum Place. - SetDiscriminant { place: Box>, variant_index: VariantIdx }, - - /// Start a live range for the storage of the local. - StorageLive(Local), - - /// End the current live range for the storage of the local. - StorageDead(Local), - - /// Executes a piece of inline Assembly. Stored in a Box to keep the size - /// of `StatementKind` low. - InlineAsm(Box>), - - /// Retag references in the given place, ensuring they got fresh tags. This is - /// part of the Stacked Borrows model. These statements are currently only interpreted - /// by miri and only generated when "-Z mir-emit-retag" is passed. - /// See - /// for more details. - Retag(RetagKind, Box>), - - /// Encodes a user's type ascription. These need to be preserved - /// intact so that NLL can respect them. For example: - /// - /// let a: T = y; - /// - /// The effect of this annotation is to relate the type `T_y` of the place `y` - /// to the user-given type `T`. The effect depends on the specified variance: - /// - /// - `Covariant` -- requires that `T_y <: T` - /// - `Contravariant` -- requires that `T_y :> T` - /// - `Invariant` -- requires that `T_y == T` - /// - `Bivariant` -- no effect - AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), - - /// No-op. Useful for deleting instructions without affecting statement indices. - Nop, -} - -/// Describes what kind of retag is to be performed. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, HashStable)] -pub enum RetagKind { - /// The initial retag when entering a function. - FnEntry, - /// Retag preparing for a two-phase borrow. - TwoPhase, - /// Retagging raw pointers. - Raw, - /// A "normal" retag. - Default, -} - -/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable, PartialEq)] -pub enum FakeReadCause { - /// Inject a fake read of the borrowed input at the end of each guards - /// code. - /// - /// This should ensure that you cannot change the variant for an enum while - /// you are in the midst of matching on it. - ForMatchGuard, - - /// `let x: !; match x {}` doesn't generate any read of x so we need to - /// generate a read of x to check that it is initialized and safe. - ForMatchedPlace, - - /// A fake read of the RefWithinGuard version of a bind-by-value variable - /// in a match guard to ensure that it's value hasn't change by the time - /// we create the OutsideGuard version. - ForGuardBinding, - - /// Officially, the semantics of - /// - /// `let pattern = ;` - /// - /// is that `` is evaluated into a temporary and then this temporary is - /// into the pattern. - /// - /// However, if we see the simple pattern `let var = `, we optimize this to - /// evaluate `` directly into the variable `var`. This is mostly unobservable, - /// but in some cases it can affect the borrow checker, as in #53695. - /// Therefore, we insert a "fake read" here to ensure that we get - /// appropriate errors. - ForLet, - - /// If we have an index expression like - /// - /// (*x)[1][{ x = y; 4}] - /// - /// then the first bounds check is invalidated when we evaluate the second - /// index expression. Thus we create a fake borrow of `x` across the second - /// indexer, which will cause a borrow check error. - ForIndex, -} - -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct InlineAsm<'tcx> { - pub asm: hir::InlineAsmInner, - pub outputs: Box<[Place<'tcx>]>, - pub inputs: Box<[(Span, Operand<'tcx>)]>, -} - -impl Debug for Statement<'_> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - use self::StatementKind::*; - match self.kind { - Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv), - FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place), - Retag(ref kind, ref place) => write!( - fmt, - "Retag({}{:?})", - match kind { - RetagKind::FnEntry => "[fn entry] ", - RetagKind::TwoPhase => "[2phase] ", - RetagKind::Raw => "[raw] ", - RetagKind::Default => "", - }, - place, - ), - StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place), - StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place), - SetDiscriminant { ref place, variant_index } => { - write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) - } - InlineAsm(ref asm) => { - write!(fmt, "asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs) - } - AscribeUserType(box (ref place, ref c_ty), ref variance) => { - write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) - } - Nop => write!(fmt, "nop"), - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Places - -/// A path to a value; something that can be evaluated without -/// changing or disturbing program state. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, HashStable)] -pub struct Place<'tcx> { - pub local: Local, - - /// projection out of a place (access a field, deref a pointer, etc) - pub projection: &'tcx List>, -} - -impl<'tcx> rustc_serialize::UseSpecializedDecodable for Place<'tcx> {} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub enum ProjectionElem { - Deref, - Field(Field, T), - Index(V), - - /// These indices are generated by slice patterns. Easiest to explain - /// by example: - /// - /// ``` - /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, - /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, - /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, - /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, - /// ``` - ConstantIndex { - /// index or -index (in Python terms), depending on from_end - offset: u32, - /// The thing being indexed must be at least this long. For arrays this - /// is always the exact length. - min_length: u32, - /// Counting backwards from end? This is always false when indexing an - /// array. - from_end: bool, - }, - - /// These indices are generated by slice patterns. - /// - /// If `from_end` is true `slice[from..slice.len() - to]`. - /// Otherwise `array[from..to]`. - Subslice { - from: u32, - to: u32, - /// Whether `to` counts from the start or end of the array/slice. - /// For `PlaceElem`s this is `true` if and only if the base is a slice. - /// For `ProjectionKind`, this can also be `true` for arrays. - from_end: bool, - }, - - /// "Downcast" to a variant of an ADT. Currently, we only introduce - /// this for ADTs with more than one variant. It may be better to - /// just introduce it always, or always for enums. - /// - /// The included Symbol is the name of the variant, used for printing MIR. - Downcast(Option, VariantIdx), -} - -impl ProjectionElem { - /// Returns `true` if the target of this projection may refer to a different region of memory - /// than the base. - fn is_indirect(&self) -> bool { - match self { - Self::Deref => true, - - Self::Field(_, _) - | Self::Index(_) - | Self::ConstantIndex { .. } - | Self::Subslice { .. } - | Self::Downcast(_, _) => false, - } - } -} - -/// Alias for projections as they appear in places, where the base is a place -/// and the index is a local. -pub type PlaceElem<'tcx> = ProjectionElem>; - -impl<'tcx> Copy for PlaceElem<'tcx> {} - -// At least on 64 bit systems, `PlaceElem` should not be larger than two pointers. -#[cfg(target_arch = "x86_64")] -static_assert_size!(PlaceElem<'_>, 16); - -/// Alias for projections as they appear in `UserTypeProjection`, where we -/// need neither the `V` parameter for `Index` nor the `T` for `Field`. -pub type ProjectionKind = ProjectionElem<(), ()>; - -rustc_index::newtype_index! { - pub struct Field { - derive [HashStable] - DEBUG_FORMAT = "field[{}]" - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PlaceRef<'tcx> { - pub local: Local, - pub projection: &'tcx [PlaceElem<'tcx>], -} - -impl<'tcx> Place<'tcx> { - // FIXME change this to a const fn by also making List::empty a const fn. - pub fn return_place() -> Place<'tcx> { - Place { local: RETURN_PLACE, projection: List::empty() } - } - - /// Returns `true` if this `Place` contains a `Deref` projection. - /// - /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the - /// same region of memory as its base. - pub fn is_indirect(&self) -> bool { - self.projection.iter().any(|elem| elem.is_indirect()) - } - - /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or - /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? - pub fn local_or_deref_local(&self) -> Option { - match self.as_ref() { - PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - } - } - - /// If this place represents a local variable like `_X` with no - /// projections, return `Some(_X)`. - pub fn as_local(&self) -> Option { - self.as_ref().as_local() - } - - pub fn as_ref(&self) -> PlaceRef<'tcx> { - PlaceRef { local: self.local, projection: &self.projection } - } -} - -impl From for Place<'_> { - fn from(local: Local) -> Self { - Place { local, projection: List::empty() } - } -} - -impl<'tcx> PlaceRef<'tcx> { - /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or - /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? - pub fn local_or_deref_local(&self) -> Option { - match *self { - PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - } - } - - /// If this place represents a local variable like `_X` with no - /// projections, return `Some(_X)`. - pub fn as_local(&self) -> Option { - match *self { - PlaceRef { local, projection: [] } => Some(local), - _ => None, - } - } -} - -impl Debug for Place<'_> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - for elem in self.projection.iter().rev() { - match elem { - ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { - write!(fmt, "(").unwrap(); - } - ProjectionElem::Deref => { - write!(fmt, "(*").unwrap(); - } - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => {} - } - } - - write!(fmt, "{:?}", self.local)?; - - for elem in self.projection.iter() { - match elem { - ProjectionElem::Downcast(Some(name), _index) => { - write!(fmt, " as {})", name)?; - } - ProjectionElem::Downcast(None, index) => { - write!(fmt, " as variant#{:?})", index)?; - } - ProjectionElem::Deref => { - write!(fmt, ")")?; - } - ProjectionElem::Field(field, ty) => { - write!(fmt, ".{:?}: {:?})", field.index(), ty)?; - } - ProjectionElem::Index(ref index) => { - write!(fmt, "[{:?}]", index)?; - } - ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => { - write!(fmt, "[{:?} of {:?}]", offset, min_length)?; - } - ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => { - write!(fmt, "[-{:?} of {:?}]", offset, min_length)?; - } - ProjectionElem::Subslice { from, to, from_end: true } if *to == 0 => { - write!(fmt, "[{:?}:]", from)?; - } - ProjectionElem::Subslice { from, to, from_end: true } if *from == 0 => { - write!(fmt, "[:-{:?}]", to)?; - } - ProjectionElem::Subslice { from, to, from_end: true } => { - write!(fmt, "[{:?}:-{:?}]", from, to)?; - } - ProjectionElem::Subslice { from, to, from_end: false } => { - write!(fmt, "[{:?}..{:?}]", from, to)?; - } - } - } - - Ok(()) - } -} - -/////////////////////////////////////////////////////////////////////////// -// Scopes - -rustc_index::newtype_index! { - pub struct SourceScope { - derive [HashStable] - DEBUG_FORMAT = "scope[{}]", - const OUTERMOST_SOURCE_SCOPE = 0, - } -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct SourceScopeData { - pub span: Span, - pub parent_scope: Option, - - /// Crate-local information for this source scope, that can't (and - /// needn't) be tracked across crates. - pub local_data: ClearCrossCrate, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct SourceScopeLocalData { - /// An `HirId` with lint levels equivalent to this scope's lint levels. - pub lint_root: hir::HirId, - /// The unsafe block that contains this node. - pub safety: Safety, -} - -/////////////////////////////////////////////////////////////////////////// -// Operands - -/// These are values that can appear inside an rvalue. They are intentionally -/// limited to prevent rvalues from being nested in one another. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub enum Operand<'tcx> { - /// Copy: The value must be available for use afterwards. - /// - /// This implies that the type of the place must be `Copy`; this is true - /// by construction during build, but also checked by the MIR type checker. - Copy(Place<'tcx>), - - /// Move: The value (including old borrows of it) will not be used again. - /// - /// Safe for values of all types (modulo future developments towards `?Move`). - /// Correct usage patterns are enforced by the borrow checker for safe code. - /// `Copy` may be converted to `Move` to enable "last-use" optimizations. - Move(Place<'tcx>), - - /// Synthesizes a constant value. - Constant(Box>), -} - -impl<'tcx> Debug for Operand<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - use self::Operand::*; - match *self { - Constant(ref a) => write!(fmt, "{:?}", a), - Copy(ref place) => write!(fmt, "{:?}", place), - Move(ref place) => write!(fmt, "move {:?}", place), - } - } -} - -impl<'tcx> Operand<'tcx> { - /// Convenience helper to make a constant that refers to the fn - /// with given `DefId` and substs. Since this is used to synthesize - /// MIR, assumes `user_ty` is None. - pub fn function_handle( - tcx: TyCtxt<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - span: Span, - ) -> Self { - let ty = tcx.type_of(def_id).subst(tcx, substs); - Operand::Constant(box Constant { - span, - user_ty: None, - literal: ty::Const::zero_sized(tcx, ty), - }) - } - - pub fn to_copy(&self) -> Self { - match *self { - Operand::Copy(_) | Operand::Constant(_) => self.clone(), - Operand::Move(place) => Operand::Copy(place), - } - } - - /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a - /// constant. - pub fn place(&self) -> Option<&Place<'tcx>> { - match self { - Operand::Copy(place) | Operand::Move(place) => Some(place), - Operand::Constant(_) => None, - } - } -} - -/////////////////////////////////////////////////////////////////////////// -/// Rvalues - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub enum Rvalue<'tcx> { - /// x (either a move or copy, depending on type of x) - Use(Operand<'tcx>), - - /// [x; 32] - Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>), - - /// &x or &mut x - Ref(Region<'tcx>, BorrowKind, Place<'tcx>), - - /// Create a raw pointer to the given place - /// Can be generated by raw address of expressions (`&raw const x`), - /// or when casting a reference to a raw pointer. - AddressOf(Mutability, Place<'tcx>), - - /// length of a [X] or [X;n] value - Len(Place<'tcx>), - - Cast(CastKind, Operand<'tcx>, Ty<'tcx>), - - BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), - CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), - - NullaryOp(NullOp, Ty<'tcx>), - UnaryOp(UnOp, Operand<'tcx>), - - /// Read the discriminant of an ADT. - /// - /// Undefined (i.e., no effort is made to make it defined, but there’s no reason why it cannot - /// be defined to return, say, a 0) if ADT is not an enum. - Discriminant(Place<'tcx>), - - /// Creates an aggregate value, like a tuple or struct. This is - /// only needed because we want to distinguish `dest = Foo { x: - /// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case - /// that `Foo` has a destructor. These rvalues can be optimized - /// away after type-checking and before lowering. - Aggregate(Box>, Vec>), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum CastKind { - Misc, - Pointer(PointerCast), -} - -#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum AggregateKind<'tcx> { - /// The type is of the element - Array(Ty<'tcx>), - Tuple, - - /// The second field is the variant index. It's equal to 0 for struct - /// and union expressions. The fourth field is - /// active field number and is present only for union expressions - /// -- e.g., for a union expression `SomeUnion { c: .. }`, the - /// active field index would identity the field `c` - Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option, Option), - - Closure(DefId, SubstsRef<'tcx>), - Generator(DefId, SubstsRef<'tcx>, hir::Movability), -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum BinOp { - /// The `+` operator (addition) - Add, - /// The `-` operator (subtraction) - Sub, - /// The `*` operator (multiplication) - Mul, - /// The `/` operator (division) - Div, - /// The `%` operator (modulus) - Rem, - /// The `^` operator (bitwise xor) - BitXor, - /// The `&` operator (bitwise and) - BitAnd, - /// The `|` operator (bitwise or) - BitOr, - /// The `<<` operator (shift left) - Shl, - /// The `>>` operator (shift right) - Shr, - /// The `==` operator (equality) - Eq, - /// The `<` operator (less than) - Lt, - /// The `<=` operator (less than or equal to) - Le, - /// The `!=` operator (not equal to) - Ne, - /// The `>=` operator (greater than or equal to) - Ge, - /// The `>` operator (greater than) - Gt, - /// The `ptr.offset` operator - Offset, -} - -impl BinOp { - pub fn is_checkable(self) -> bool { - use self::BinOp::*; - match self { - Add | Sub | Mul | Shl | Shr => true, - _ => false, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum NullOp { - /// Returns the size of a value of that type - SizeOf, - /// Creates a new uninitialized box for a value of that type - Box, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum UnOp { - /// The `!` operator for logical inversion - Not, - /// The `-` operator for negation - Neg, -} - -impl<'tcx> Debug for Rvalue<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - use self::Rvalue::*; - - match *self { - Use(ref place) => write!(fmt, "{:?}", place), - Repeat(ref a, ref b) => { - write!(fmt, "[{:?}; ", a)?; - pretty_print_const(b, fmt, false)?; - write!(fmt, "]") - } - Len(ref a) => write!(fmt, "Len({:?})", a), - Cast(ref kind, ref place, ref ty) => { - write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) - } - BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), - CheckedBinaryOp(ref op, ref a, ref b) => { - write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) - } - UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), - Discriminant(ref place) => write!(fmt, "discriminant({:?})", place), - NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t), - Ref(region, borrow_kind, ref place) => { - let kind_str = match borrow_kind { - BorrowKind::Shared => "", - BorrowKind::Shallow => "shallow ", - BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ", - }; - - // When printing regions, add trailing space if necessary. - let print_region = ty::tls::with(|tcx| { - tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions - }); - let region = if print_region { - let mut region = region.to_string(); - if !region.is_empty() { - region.push(' '); - } - region - } else { - // Do not even print 'static - String::new() - }; - write!(fmt, "&{}{}{:?}", region, kind_str, place) - } - - AddressOf(mutability, ref place) => { - let kind_str = match mutability { - Mutability::Mut => "mut", - Mutability::Not => "const", - }; - - write!(fmt, "&raw {} {:?}", kind_str, place) - } - - Aggregate(ref kind, ref places) => { - fn fmt_tuple(fmt: &mut Formatter<'_>, places: &[Operand<'_>]) -> fmt::Result { - let mut tuple_fmt = fmt.debug_tuple(""); - for place in places { - tuple_fmt.field(place); - } - tuple_fmt.finish() - } - - match **kind { - AggregateKind::Array(_) => write!(fmt, "{:?}", places), - - AggregateKind::Tuple => match places.len() { - 0 => write!(fmt, "()"), - 1 => write!(fmt, "({:?},)", places[0]), - _ => fmt_tuple(fmt, places), - }, - - AggregateKind::Adt(adt_def, variant, substs, _user_ty, _) => { - let variant_def = &adt_def.variants[variant]; - - let f = &mut *fmt; - ty::tls::with(|tcx| { - let substs = tcx.lift(&substs).expect("could not lift for printing"); - FmtPrinter::new(tcx, f, Namespace::ValueNS) - .print_def_path(variant_def.def_id, substs)?; - Ok(()) - })?; - - match variant_def.ctor_kind { - CtorKind::Const => Ok(()), - CtorKind::Fn => fmt_tuple(fmt, places), - CtorKind::Fictive => { - let mut struct_fmt = fmt.debug_struct(""); - for (field, place) in variant_def.fields.iter().zip(places) { - struct_fmt.field(&field.ident.as_str(), place); - } - struct_fmt.finish() - } - } - } - - AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| { - if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) { - let name = if tcx.sess.opts.debugging_opts.span_free_formats { - let substs = tcx.lift(&substs).unwrap(); - format!( - "[closure@{}]", - tcx.def_path_str_with_substs(def_id, substs), - ) - } else { - format!("[closure@{:?}]", tcx.hir().span(hir_id)) - }; - let mut struct_fmt = fmt.debug_struct(&name); - - if let Some(upvars) = tcx.upvars(def_id) { - for (&var_id, place) in upvars.keys().zip(places) { - let var_name = tcx.hir().name(var_id); - struct_fmt.field(&var_name.as_str(), place); - } - } - - struct_fmt.finish() - } else { - write!(fmt, "[closure]") - } - }), - - AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| { - if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) { - let name = format!("[generator@{:?}]", tcx.hir().span(hir_id)); - let mut struct_fmt = fmt.debug_struct(&name); - - if let Some(upvars) = tcx.upvars(def_id) { - for (&var_id, place) in upvars.keys().zip(places) { - let var_name = tcx.hir().name(var_id); - struct_fmt.field(&var_name.as_str(), place); - } - } - - struct_fmt.finish() - } else { - write!(fmt, "[generator]") - } - }), - } - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -/// Constants -/// -/// Two constants are equal if they are the same constant. Note that -/// this does not necessarily mean that they are "==" in Rust -- in -/// particular one must be wary of `NaN`! - -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub struct Constant<'tcx> { - pub span: Span, - - /// Optional user-given type: for something like - /// `collect::>`, this would be present and would - /// indicate that `Vec<_>` was explicitly specified. - /// - /// Needed for NLL to impose user-given type constraints. - pub user_ty: Option, - - pub literal: &'tcx ty::Const<'tcx>, -} - -impl Constant<'tcx> { - pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { - match self.literal.val.try_to_scalar() { - Some(Scalar::Ptr(ptr)) => match tcx.alloc_map.lock().get(ptr.alloc_id) { - Some(GlobalAlloc::Static(def_id)) => Some(def_id), - Some(_) => None, - None => { - tcx.sess.delay_span_bug(DUMMY_SP, "MIR cannot contain dangling const pointers"); - None - } - }, - _ => None, - } - } -} - -/// A collection of projections into user types. -/// -/// They are projections because a binding can occur a part of a -/// parent pattern that has been ascribed a type. -/// -/// Its a collection because there can be multiple type ascriptions on -/// the path from the root of the pattern down to the binding itself. -/// -/// An example: -/// -/// ```rust -/// struct S<'a>((i32, &'a str), String); -/// let S((_, w): (i32, &'static str), _): S = ...; -/// // ------ ^^^^^^^^^^^^^^^^^^^ (1) -/// // --------------------------------- ^ (2) -/// ``` -/// -/// The highlights labelled `(1)` show the subpattern `(_, w)` being -/// ascribed the type `(i32, &'static str)`. -/// -/// The highlights labelled `(2)` show the whole pattern being -/// ascribed the type `S`. -/// -/// In this example, when we descend to `w`, we will have built up the -/// following two projected types: -/// -/// * base: `S`, projection: `(base.0).1` -/// * base: `(i32, &'static str)`, projection: `base.1` -/// -/// The first will lead to the constraint `w: &'1 str` (for some -/// inferred region `'1`). The second will lead to the constraint `w: -/// &'static str`. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct UserTypeProjections { - pub(crate) contents: Vec<(UserTypeProjection, Span)>, -} - -impl<'tcx> UserTypeProjections { - pub fn none() -> Self { - UserTypeProjections { contents: vec![] } - } - - pub fn from_projections(projs: impl Iterator) -> Self { - UserTypeProjections { contents: projs.collect() } - } - - pub fn projections_and_spans( - &self, - ) -> impl Iterator + ExactSizeIterator { - self.contents.iter() - } - - pub fn projections(&self) -> impl Iterator + ExactSizeIterator { - self.contents.iter().map(|&(ref user_type, _span)| user_type) - } - - pub fn push_projection(mut self, user_ty: &UserTypeProjection, span: Span) -> Self { - self.contents.push((user_ty.clone(), span)); - self - } - - fn map_projections( - mut self, - mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection, - ) -> Self { - self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect(); - self - } - - pub fn index(self) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.index()) - } - - pub fn subslice(self, from: u32, to: u32) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to)) - } - - pub fn deref(self) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.deref()) - } - - pub fn leaf(self, field: Field) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field)) - } - - pub fn variant(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx, field: Field) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field)) - } -} - -/// Encodes the effect of a user-supplied type annotation on the -/// subcomponents of a pattern. The effect is determined by applying the -/// given list of proejctions to some underlying base type. Often, -/// the projection element list `projs` is empty, in which case this -/// directly encodes a type in `base`. But in the case of complex patterns with -/// subpatterns and bindings, we want to apply only a *part* of the type to a variable, -/// in which case the `projs` vector is used. -/// -/// Examples: -/// -/// * `let x: T = ...` -- here, the `projs` vector is empty. -/// -/// * `let (x, _): T = ...` -- here, the `projs` vector would contain -/// `field[0]` (aka `.0`), indicating that the type of `s` is -/// determined by finding the type of the `.0` field from `T`. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub struct UserTypeProjection { - pub base: UserTypeAnnotationIndex, - pub projs: Vec, -} - -impl Copy for ProjectionKind {} - -impl UserTypeProjection { - pub(crate) fn index(mut self) -> Self { - self.projs.push(ProjectionElem::Index(())); - self - } - - pub(crate) fn subslice(mut self, from: u32, to: u32) -> Self { - self.projs.push(ProjectionElem::Subslice { from, to, from_end: true }); - self - } - - pub(crate) fn deref(mut self) -> Self { - self.projs.push(ProjectionElem::Deref); - self - } - - pub(crate) fn leaf(mut self, field: Field) -> Self { - self.projs.push(ProjectionElem::Field(field, ())); - self - } - - pub(crate) fn variant( - mut self, - adt_def: &AdtDef, - variant_index: VariantIdx, - field: Field, - ) -> Self { - self.projs.push(ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - )); - self.projs.push(ProjectionElem::Field(field, ())); - self - } -} - -CloneTypeFoldableAndLiftImpls! { ProjectionKind, } - -impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { - fn super_fold_with>(&self, folder: &mut F) -> Self { - use crate::mir::ProjectionElem::*; - - let base = self.base.fold_with(folder); - let projs: Vec<_> = self - .projs - .iter() - .map(|&elem| match elem { - Deref => Deref, - Field(f, ()) => Field(f, ()), - Index(()) => Index(()), - Downcast(symbol, variantidx) => Downcast(symbol, variantidx), - ConstantIndex { offset, min_length, from_end } => { - ConstantIndex { offset, min_length, from_end } - } - Subslice { from, to, from_end } => Subslice { from, to, from_end }, - }) - .collect(); - - UserTypeProjection { base, projs } - } - - fn super_visit_with>(&self, visitor: &mut Vs) -> bool { - self.base.visit_with(visitor) - // Note: there's nothing in `self.proj` to visit. - } -} - -rustc_index::newtype_index! { - pub struct Promoted { - derive [HashStable] - DEBUG_FORMAT = "promoted[{}]" - } -} - -impl<'tcx> Debug for Constant<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - write!(fmt, "{}", self) - } -} - -impl<'tcx> Display for Constant<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - write!(fmt, "const ")?; - pretty_print_const(self.literal, fmt, true) - } -} - -fn pretty_print_const( - c: &ty::Const<'tcx>, - fmt: &mut Formatter<'_>, - print_types: bool, -) -> fmt::Result { - use crate::ty::print::PrettyPrinter; - ty::tls::with(|tcx| { - let literal = tcx.lift(&c).unwrap(); - let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); - cx.print_alloc_ids = true; - cx.pretty_print_const(literal, print_types)?; - Ok(()) - }) -} - -impl<'tcx> graph::DirectedGraph for Body<'tcx> { - type Node = BasicBlock; -} - -impl<'tcx> graph::WithNumNodes for Body<'tcx> { - fn num_nodes(&self) -> usize { - self.basic_blocks.len() - } -} - -impl<'tcx> graph::WithStartNode for Body<'tcx> { - fn start_node(&self) -> Self::Node { - START_BLOCK - } -} - -impl<'tcx> graph::WithSuccessors for Body<'tcx> { - fn successors(&self, node: Self::Node) -> >::Iter { - self.basic_blocks[node].terminator().successors().cloned() - } -} - -impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { - type Item = BasicBlock; - type Iter = iter::Cloned>; -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, HashStable)] -pub struct Location { - /// The block that the location is within. - pub block: BasicBlock, - - /// The location is the position of the start of the statement; or, if - /// `statement_index` equals the number of statements, then the start of the - /// terminator. - pub statement_index: usize, -} - -impl fmt::Debug for Location { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{:?}[{}]", self.block, self.statement_index) - } -} - -impl Location { - pub const START: Location = Location { block: START_BLOCK, statement_index: 0 }; - - /// Returns the location immediately after this one within the enclosing block. - /// - /// Note that if this location represents a terminator, then the - /// resulting location would be out of bounds and invalid. - pub fn successor_within_block(&self) -> Location { - Location { block: self.block, statement_index: self.statement_index + 1 } - } - - /// Returns `true` if `other` is earlier in the control flow graph than `self`. - pub fn is_predecessor_of<'tcx>( - &self, - other: Location, - body: ReadOnlyBodyAndCache<'_, 'tcx>, - ) -> bool { - // If we are in the same block as the other location and are an earlier statement - // then we are a predecessor of `other`. - if self.block == other.block && self.statement_index < other.statement_index { - return true; - } - - // If we're in another block, then we want to check that block is a predecessor of `other`. - let mut queue: Vec = body.predecessors_for(other.block).to_vec(); - let mut visited = FxHashSet::default(); - - while let Some(block) = queue.pop() { - // If we haven't visited this block before, then make sure we visit it's predecessors. - if visited.insert(block) { - queue.extend(body.predecessors_for(block).iter().cloned()); - } else { - continue; - } - - // If we found the block that `self` is in, then we are a predecessor of `other` (since - // we found that block by looking at the predecessors of `other`). - if self.block == block { - return true; - } - } - - false - } - - pub fn dominates(&self, other: Location, dominators: &Dominators) -> bool { - if self.block == other.block { - self.statement_index <= other.statement_index - } else { - dominators.is_dominated_by(other.block, self.block) - } - } -} diff --git a/src/librustc/mir/query.rs b/src/librustc/mir/query.rs deleted file mode 100644 index 8c81f5227d260..0000000000000 --- a/src/librustc/mir/query.rs +++ /dev/null @@ -1,228 +0,0 @@ -//! Values computed by queries that use MIR. - -use crate::ty::{self, Ty}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::BitMatrix; -use rustc_index::vec::IndexVec; -use rustc_span::{Span, Symbol}; -use rustc_target::abi::VariantIdx; -use smallvec::SmallVec; - -use super::{Field, SourceInfo}; - -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub enum UnsafetyViolationKind { - General, - /// Permitted both in `const fn`s and regular `fn`s. - GeneralAndConstFn, - BorrowPacked(hir::HirId), -} - -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub struct UnsafetyViolation { - pub source_info: SourceInfo, - pub description: Symbol, - pub details: Symbol, - pub kind: UnsafetyViolationKind, -} - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] -pub struct UnsafetyCheckResult { - /// Violations that are propagated *upwards* from this function. - pub violations: Lrc<[UnsafetyViolation]>, - /// `unsafe` blocks in this function, along with whether they are used. This is - /// used for the "unused_unsafe" lint. - pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>, -} - -rustc_index::newtype_index! { - pub struct GeneratorSavedLocal { - derive [HashStable] - DEBUG_FORMAT = "_{}", - } -} - -/// The layout of generator state. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct GeneratorLayout<'tcx> { - /// The type of every local stored inside the generator. - pub field_tys: IndexVec>, - - /// Which of the above fields are in each variant. Note that one field may - /// be stored in multiple variants. - pub variant_fields: IndexVec>, - - /// Which saved locals are storage-live at the same time. Locals that do not - /// have conflicts with each other are allowed to overlap in the computed - /// layout. - pub storage_conflicts: BitMatrix, -} - -#[derive(Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct BorrowCheckResult<'tcx> { - /// All the opaque types that are restricted to concrete types - /// by this function. Unlike the value in `TypeckTables`, this has - /// unerased regions. - pub concrete_opaque_types: FxHashMap>, - pub closure_requirements: Option>, - pub used_mut_upvars: SmallVec<[Field; 8]>, -} - -/// The result of the `mir_const_qualif` query. -/// -/// Each field corresponds to an implementer of the `Qualif` trait in -/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each -/// `Qualif`. -#[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)] -pub struct ConstQualifs { - pub has_mut_interior: bool, - pub needs_drop: bool, -} - -/// After we borrow check a closure, we are left with various -/// requirements that we have inferred between the free regions that -/// appear in the closure's signature or on its field types. These -/// requirements are then verified and proved by the closure's -/// creating function. This struct encodes those requirements. -/// -/// The requirements are listed as being between various `RegionVid`. The 0th -/// region refers to `'static`; subsequent region vids refer to the free -/// regions that appear in the closure (or generator's) type, in order of -/// appearance. (This numbering is actually defined by the `UniversalRegions` -/// struct in the NLL region checker. See for example -/// `UniversalRegions::closure_mapping`.) Note the free regions in the -/// closure's signature and captures are erased. -/// -/// Example: If type check produces a closure with the closure substs: -/// -/// ```text -/// ClosureSubsts = [ -/// 'a, // From the parent. -/// 'b, -/// i8, // the "closure kind" -/// for<'x> fn(&' &'x u32) -> &'x u32, // the "closure signature" -/// &' String, // some upvar -/// ] -/// ``` -/// -/// We would "renumber" each free region to a unique vid, as follows: -/// -/// ```text -/// ClosureSubsts = [ -/// '1, // From the parent. -/// '2, -/// i8, // the "closure kind" -/// for<'x> fn(&'3 &'x u32) -> &'x u32, // the "closure signature" -/// &'4 String, // some upvar -/// ] -/// ``` -/// -/// Now the code might impose a requirement like `'1: '2`. When an -/// instance of the closure is created, the corresponding free regions -/// can be extracted from its type and constrained to have the given -/// outlives relationship. -/// -/// In some cases, we have to record outlives requirements between types and -/// regions as well. In that case, if those types include any regions, those -/// regions are recorded using their external names (`ReStatic`, -/// `ReEarlyBound`, `ReFree`). We use these because in a query response we -/// cannot use `ReVar` (which is what we use internally within the rest of the -/// NLL code). -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct ClosureRegionRequirements<'tcx> { - /// The number of external regions defined on the closure. In our - /// example above, it would be 3 -- one for `'static`, then `'1` - /// and `'2`. This is just used for a sanity check later on, to - /// make sure that the number of regions we see at the callsite - /// matches. - pub num_external_vids: usize, - - /// Requirements between the various free regions defined in - /// indices. - pub outlives_requirements: Vec>, -} - -/// Indicates an outlives-constraint between a type or between two -/// free regions declared on the closure. -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct ClosureOutlivesRequirement<'tcx> { - // This region or type ... - pub subject: ClosureOutlivesSubject<'tcx>, - - // ... must outlive this one. - pub outlived_free_region: ty::RegionVid, - - // If not, report an error here ... - pub blame_span: Span, - - // ... due to this reason. - pub category: ConstraintCategory, -} - -/// Outlives-constraints can be categorized to determine whether and why they -/// are interesting (for error reporting). Order of variants indicates sort -/// order of the category, thereby influencing diagnostic output. -/// -/// See also [rustc_mir::borrow_check::nll::constraints]. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub enum ConstraintCategory { - Return, - Yield, - UseAsConst, - UseAsStatic, - TypeAnnotation, - Cast, - - /// A constraint that came from checking the body of a closure. - /// - /// We try to get the category that the closure used when reporting this. - ClosureBounds, - CallArgument, - CopyBound, - SizedBound, - Assignment, - OpaqueType, - - /// A "boring" constraint (caused by the given location) is one that - /// the user probably doesn't want to see described in diagnostics, - /// because it is kind of an artifact of the type system setup. - /// Example: `x = Foo { field: y }` technically creates - /// intermediate regions representing the "type of `Foo { field: y - /// }`", and data flows from `y` into those variables, but they - /// are not very interesting. The assignment into `x` on the other - /// hand might be. - Boring, - // Boring and applicable everywhere. - BoringNoLocation, - - /// A constraint that doesn't correspond to anything the user sees. - Internal, -} - -/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing -/// that must outlive some region. -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum ClosureOutlivesSubject<'tcx> { - /// Subject is a type, typically a type parameter, but could also - /// be a projection. Indicates a requirement like `T: 'a` being - /// passed to the caller, where the type here is `T`. - /// - /// The type here is guaranteed not to contain any free regions at - /// present. - Ty(Ty<'tcx>), - - /// Subject is a free region from the closure. Indicates a requirement - /// like `'a: 'b` being passed to the caller; the region here is `'a`. - Region(ty::RegionVid), -} - -/// The constituent parts of an ADT or array. -#[derive(Copy, Clone, Debug, HashStable)] -pub struct DestructuredConst<'tcx> { - pub variant: VariantIdx, - pub fields: &'tcx [&'tcx ty::Const<'tcx>], -} diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs deleted file mode 100644 index 54f5103f736ec..0000000000000 --- a/src/librustc/query/mod.rs +++ /dev/null @@ -1,1261 +0,0 @@ -use crate::dep_graph::SerializedDepNodeIndex; -use crate::mir; -use crate::mir::interpret::{GlobalId, LitToConstInput}; -use crate::traits; -use crate::traits::query::{ - CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, -}; -use crate::ty::query::queries; -use crate::ty::query::QueryDescription; -use crate::ty::subst::SubstsRef; -use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; - -use rustc_span::symbol::Symbol; -use std::borrow::Cow; - -fn describe_as_module(def_id: DefId, tcx: TyCtxt<'_>) -> String { - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id)) - } -} - -// Each of these queries corresponds to a function pointer field in the -// `Providers` struct for requesting a value of that type, and a method -// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way -// which memoizes and does dep-graph tracking, wrapping around the actual -// `Providers` that the driver creates (using several `rustc_*` crates). -// -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `fatal_cycle` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. -rustc_queries! { - Other { - query trigger_delay_span_bug(key: DefId) -> () { - desc { "trigger a delay span bug" } - } - } - - Other { - // Represents crate as a whole (as distinct from the top-level crate module). - // If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), - // we will have to assume that any change means that you need to be recompiled. - // This is because the `hir_crate` query gives you access to all other items. - // To avoid this fate, do not call `tcx.hir().krate()`; instead, - // prefer wrappers like `tcx.visit_all_items_in_krate()`. - query hir_crate(key: CrateNum) -> &'tcx Crate<'tcx> { - eval_always - no_hash - desc { "get the crate HIR" } - } - - // The indexed HIR. This can be conveniently accessed by `tcx.hir()`. - // Avoid calling this query directly. - query index_hir(_: CrateNum) -> &'tcx map::IndexedHir<'tcx> { - eval_always - no_hash - desc { "index HIR" } - } - - // The items in a module. - // - // This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. - // Avoid calling this query directly. - query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems { - eval_always - desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } - } - - // Gives access to the HIR node for the HIR owner `key`. - // - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. - query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> { - eval_always - desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } - } - - // Gives access to the HIR nodes and bodies inside the HIR owner `key`. - // - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. - query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> { - eval_always - desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } - } - - /// Records the type of every item. - query type_of(key: DefId) -> Ty<'tcx> { - cache_on_disk_if { key.is_local() } - } - - query analysis(key: CrateNum) -> Result<(), ErrorReported> { - eval_always - desc { "running analysis passes on this crate" } - } - - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its - /// associated generics. - query generics_of(key: DefId) -> &'tcx ty::Generics { - cache_on_disk_if { key.is_local() } - load_cached(tcx, id) { - let generics: Option = tcx.queries.on_disk_cache - .try_load_query_result(tcx, id); - generics.map(|x| &*tcx.arena.alloc(x)) - } - } - - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the - /// predicates (where-clauses) that must be proven true in order - /// to reference it. This is almost always the "predicates query" - /// that you want. - /// - /// `predicates_of` builds on `predicates_defined_on` -- in fact, - /// it is almost always the same as that query, except for the - /// case of traits. For traits, `predicates_of` contains - /// an additional `Self: Trait<...>` predicate that users don't - /// actually write. This reflects the fact that to invoke the - /// trait (e.g., via `Default::default`) you must supply types - /// that actually implement the trait. (However, this extra - /// predicate gets in the way of some checks, which are intended - /// to operate over only the actual where-clauses written by the - /// user.) - query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - cache_on_disk_if { key.is_local() } - } - - query native_libraries(_: CrateNum) -> Lrc> { - desc { "looking up the native libraries of a linked crate" } - } - - query lint_levels(_: CrateNum) -> &'tcx LintLevelMap { - eval_always - desc { "computing the lint levels for items in this crate" } - } - - query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { - eval_always - desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } - } - } - - Codegen { - query is_panic_runtime(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate is_panic_runtime" } - } - } - - Codegen { - /// Set of all the `DefId`s in this crate that have MIR associated with - /// them. This includes all the body owners, but also things like struct - /// constructors. - query mir_keys(_: CrateNum) -> &'tcx DefIdSet { - desc { "getting a list of all mir_keys" } - } - - /// Maps DefId's that have an associated `mir::Body` to the result - /// of the MIR const-checking pass. This is the set of qualifs in - /// the final value of a `const`. - query mir_const_qualif(key: DefId) -> mir::ConstQualifs { - desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - - /// Fetch the MIR for a given `DefId` right after it's built - this includes - /// unreachable code. - query mir_built(_: DefId) -> &'tcx Steal> { - desc { "building MIR for" } - } - - /// Fetch the MIR for a given `DefId` up till the point where it is - /// ready for const evaluation. - /// - /// See the README for the `mir` module for details. - query mir_const(_: DefId) -> &'tcx Steal> { - no_hash - } - - query mir_validated(_: DefId) -> - ( - &'tcx Steal>, - &'tcx Steal>> - ) { - no_hash - } - - /// MIR after our optimization passes have run. This is MIR that is ready - /// for codegen. This is also the only query that can fetch non-local MIR, at present. - query optimized_mir(key: DefId) -> &'tcx mir::BodyAndCache<'tcx> { - cache_on_disk_if { key.is_local() } - load_cached(tcx, id) { - let mir: Option> - = tcx.queries.on_disk_cache.try_load_query_result(tcx, id); - mir.map(|x| { - let cache = tcx.arena.alloc(x); - cache.ensure_predecessors(); - &*cache - }) - } - } - - query promoted_mir(key: DefId) -> &'tcx IndexVec> { - cache_on_disk_if { key.is_local() } - load_cached(tcx, id) { - let promoted: Option< - rustc_index::vec::IndexVec< - crate::mir::Promoted, - crate::mir::BodyAndCache<'tcx> - >> = tcx.queries.on_disk_cache.try_load_query_result(tcx, id); - promoted.map(|p| { - let cache = tcx.arena.alloc(p); - for body in cache.iter_mut() { - body.ensure_predecessors(); - } - &*cache - }) - } - } - } - - TypeChecking { - // Erases regions from `ty` to yield a new type. - // Normally you would just use `tcx.erase_regions(&value)`, - // however, which uses this query as a kind of cache. - query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { - // This query is not expected to have input -- as a result, it - // is not a good candidates for "replay" because it is essentially a - // pure function of its input (and hence the expectation is that - // no caller would be green **apart** from just these - // queries). Making it anonymous avoids hashing the result, which - // may save a bit of time. - anon - desc { "erasing regions from `{:?}`", ty } - } - - query program_clauses_for(_: DefId) -> Clauses<'tcx> { - desc { "generating chalk-style clauses" } - } - - query program_clauses_for_env(_: traits::Environment<'tcx>) -> Clauses<'tcx> { - desc { "generating chalk-style clauses for environment" } - } - - // Get the chalk-style environment of the given item. - query environment(_: DefId) -> traits::Environment<'tcx> { - desc { "return a chalk-style environment" } - } - } - - Linking { - query wasm_import_module_map(_: CrateNum) -> &'tcx FxHashMap { - desc { "wasm import module map" } - } - } - - Other { - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the - /// predicates (where-clauses) directly defined on it. This is - /// equal to the `explicit_predicates_of` predicates plus the - /// `inferred_outlives_of` predicates. - query predicates_defined_on(_: DefId) -> ty::GenericPredicates<'tcx> {} - - /// Returns the predicates written explicitly by the user. - query explicit_predicates_of(_: DefId) -> ty::GenericPredicates<'tcx> {} - - /// Returns the inferred outlives predicates (e.g., for `struct - /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). - query inferred_outlives_of(_: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] {} - - /// Maps from the `DefId` of a trait to the list of - /// super-predicates. This is a subset of the full list of - /// predicates. We store these in a separate map because we must - /// evaluate them even during type conversion, often before the - /// full predicates are available (note that supertraits have - /// additional acyclicity requirements). - query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } - } - - /// To avoid cycles within the predicates of a single item we compute - /// per-type-parameter predicates for resolving `T::AssocTy`. - query type_param_predicates(key: (DefId, DefId)) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing the bounds for type parameter `{}`", { - let id = tcx.hir().as_local_hir_id(key.1).unwrap(); - tcx.hir().ty_param_name(id) - }} - } - - query trait_def(_: DefId) -> &'tcx ty::TraitDef {} - query adt_def(_: DefId) -> &'tcx ty::AdtDef {} - query adt_destructor(_: DefId) -> Option {} - - // The cycle error here should be reported as an error by `check_representable`. - // We consider the type as Sized in the meanwhile to avoid - // further errors (done in impl Value for AdtSizedConstraint). - // Use `cycle_delay_bug` to delay the cycle error here to be emitted later - // in case we accidentally otherwise don't emit an error. - query adt_sized_constraint( - _: DefId - ) -> AdtSizedConstraint<'tcx> { - cycle_delay_bug - } - - query adt_dtorck_constraint( - _: DefId - ) -> Result, NoSolution> {} - - /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate - /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might - /// not have the feature gate active). - /// - /// **Do not call this function manually.** It is only meant to cache the base data for the - /// `is_const_fn` function. - query is_const_fn_raw(key: DefId) -> bool { - desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } - } - - /// Returns `true` if this is a const `impl`. **Do not call this function manually.** - /// - /// This query caches the base data for the `is_const_impl` helper function, which also - /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). - query is_const_impl_raw(key: DefId) -> bool { - desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } - } - - query asyncness(key: DefId) -> hir::IsAsync { - desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } - } - - /// Returns `true` if calls to the function may be promoted. - /// - /// This is either because the function is e.g., a tuple-struct or tuple-variant - /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should - /// be removed in the future in favour of some form of check which figures out whether the - /// function does not inspect the bits of any of its arguments (so is essentially just a - /// constructor function). - query is_promotable_const_fn(_: DefId) -> bool {} - - query const_fn_is_allowed_fn_ptr(_: DefId) -> bool {} - - /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). - query is_foreign_item(_: DefId) -> bool {} - - /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. - query static_mutability(_: DefId) -> Option {} - - /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. - query generator_kind(_: DefId) -> Option {} - - /// Gets a map with the variance of every item; use `item_variance` instead. - query crate_variances(_: CrateNum) -> &'tcx ty::CrateVariancesMap<'tcx> { - desc { "computing the variances for items in this crate" } - } - - /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. - query variances_of(_: DefId) -> &'tcx [ty::Variance] {} - } - - TypeChecking { - /// Maps from thee `DefId` of a type to its (inferred) outlives. - query inferred_outlives_crate(_: CrateNum) - -> &'tcx ty::CratePredicatesMap<'tcx> { - desc { "computing the inferred outlives predicates for items in this crate" } - } - } - - Other { - /// Maps from an impl/trait `DefId to a list of the `DefId`s of its items. - query associated_item_def_ids(_: DefId) -> &'tcx [DefId] {} - - /// Maps from a trait item to the trait item "descriptor". - query associated_item(_: DefId) -> ty::AssocItem {} - - /// Collects the associated items defined on a trait or impl. - query associated_items(key: DefId) -> &'tcx ty::AssociatedItems { - desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } - } - - query impl_trait_ref(_: DefId) -> Option> {} - query impl_polarity(_: DefId) -> ty::ImplPolarity {} - - query issue33140_self_ty(_: DefId) -> Option> {} - } - - TypeChecking { - /// Maps a `DefId` of a type to a list of its inherent impls. - /// Contains implementations of methods that are inherent to a type. - /// Methods in these implementations don't need to be exported. - query inherent_impls(_: DefId) -> &'tcx [DefId] { - eval_always - } - } - - TypeChecking { - /// The result of unsafety-checking this `DefId`. - query unsafety_check_result(key: DefId) -> mir::UnsafetyCheckResult { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - - /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error - query unsafe_derive_on_repr_packed(_: DefId) -> () {} - - /// The signature of functions and closures. - query fn_sig(_: DefId) -> ty::PolyFnSig<'tcx> {} - } - - Other { - query lint_mod(key: DefId) -> () { - desc { |tcx| "linting {}", describe_as_module(key, tcx) } - } - - /// Checks the attributes in the module. - query check_mod_attrs(key: DefId) -> () { - desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } - } - - query check_mod_unstable_api_usage(key: DefId) -> () { - desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } - } - - /// Checks the const bodies in the module for illegal operations (e.g. `if` or `loop`). - query check_mod_const_bodies(key: DefId) -> () { - desc { |tcx| "checking consts in {}", describe_as_module(key, tcx) } - } - - /// Checks the loops in the module. - query check_mod_loops(key: DefId) -> () { - desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } - } - - query check_mod_item_types(key: DefId) -> () { - desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) } - } - - query check_mod_privacy(key: DefId) -> () { - desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) } - } - - query check_mod_intrinsics(key: DefId) -> () { - desc { |tcx| "checking intrinsics in {}", describe_as_module(key, tcx) } - } - - query check_mod_liveness(key: DefId) -> () { - desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) } - } - - query check_mod_impl_wf(key: DefId) -> () { - desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } - } - - query collect_mod_item_types(key: DefId) -> () { - desc { |tcx| "collecting item types in {}", describe_as_module(key, tcx) } - } - - /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(_: DefId) - -> ty::adjustment::CoerceUnsizedInfo {} - } - - TypeChecking { - query typeck_item_bodies(_: CrateNum) -> () { - desc { "type-checking all item bodies" } - } - - query typeck_tables_of(key: DefId) -> &'tcx ty::TypeckTables<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - query diagnostic_only_typeck_tables_of(key: DefId) -> &'tcx ty::TypeckTables<'tcx> { - cache_on_disk_if { key.is_local() } - load_cached(tcx, id) { - let typeck_tables: Option> = tcx - .queries.on_disk_cache - .try_load_query_result(tcx, id); - - typeck_tables.map(|tables| &*tcx.arena.alloc(tables)) - } - } - } - - Other { - query used_trait_imports(key: DefId) -> &'tcx DefIdSet { - cache_on_disk_if { key.is_local() } - } - } - - TypeChecking { - query has_typeck_tables(_: DefId) -> bool {} - - query coherent_trait(def_id: DefId) -> () { - desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } - } - } - - BorrowChecking { - /// Borrow-checks the function body. If this is a closure, returns - /// additional requirements that the closure's creator must verify. - query mir_borrowck(key: DefId) -> &'tcx mir::BorrowCheckResult<'tcx> { - desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if(tcx, opt_result) { - key.is_local() - && (tcx.is_closure(key) - || opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty())) - } - } - } - - TypeChecking { - /// Gets a complete map from all types to their inherent impls. - /// Not meant to be used directly outside of coherence. - /// (Defined only for `LOCAL_CRATE`.) - query crate_inherent_impls(k: CrateNum) - -> &'tcx CrateInherentImpls { - eval_always - desc { "all inherent impls defined in crate `{:?}`", k } - } - - /// Checks all types in the crate for overlap in their inherent impls. Reports errors. - /// Not meant to be used directly outside of coherence. - /// (Defined only for `LOCAL_CRATE`.) - query crate_inherent_impls_overlap_check(_: CrateNum) - -> () { - eval_always - desc { "check for overlap between inherent impls defined in this crate" } - } - } - - Other { - /// Evaluates a constant without running sanity checks. - /// - /// **Do not use this** outside const eval. Const eval uses this to break query cycles - /// during validation. Please add a comment to every use site explaining why using - /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable - /// form to be used outside of const eval. - query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> ConstEvalRawResult<'tcx> { - desc { |tcx| - "const-evaluating `{}`", - tcx.def_path_str(key.value.instance.def.def_id()) - } - } - - /// Results of evaluating const items or constants embedded in - /// other items (such as enum variant explicit discriminants). - /// - /// In contrast to `const_eval_raw` this performs some validation on the constant, and - /// returns a proper constant that is usable by the rest of the compiler. - /// - /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, - /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. - query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> ConstEvalResult<'tcx> { - desc { |tcx| - "const-evaluating + checking `{}`", - tcx.def_path_str(key.value.instance.def.def_id()) - } - cache_on_disk_if(_, opt_result) { - // Only store results without errors - opt_result.map_or(true, |r| r.is_ok()) - } - } - - /// Extracts a field of a (variant of a) const. - query const_field( - key: ty::ParamEnvAnd<'tcx, (&'tcx ty::Const<'tcx>, mir::Field)> - ) -> ConstValue<'tcx> { - desc { "extract field of const" } - } - - /// Destructure a constant ADT or array into its variant indent and its - /// field values. - query destructure_const( - key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> - ) -> mir::DestructuredConst<'tcx> { - desc { "destructure constant" } - } - - query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { - desc { "get a &core::panic::Location referring to a span" } - } - - query lit_to_const( - key: LitToConstInput<'tcx> - ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { - desc { "converting literal to const" } - } - } - - TypeChecking { - query check_match(key: DefId) { - cache_on_disk_if { key.is_local() } - } - - /// Performs part of the privacy check and computes "access levels". - query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels { - eval_always - desc { "privacy access levels" } - } - query check_private_in_public(_: CrateNum) -> () { - eval_always - desc { "checking for private elements in public interfaces" } - } - } - - Other { - query reachable_set(_: CrateNum) -> Lrc { - desc { "reachability" } - } - - /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; - /// in the case of closures, this will be redirected to the enclosing function. - query region_scope_tree(_: DefId) -> &'tcx region::ScopeTree {} - - query mir_shims(key: ty::InstanceDef<'tcx>) -> &'tcx mir::BodyAndCache<'tcx> { - desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } - } - - /// The `symbol_name` query provides the symbol name for calling a - /// given instance from the local crate. In particular, it will also - /// look up the correct symbol name of instances from upstream crates. - query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName { - desc { "computing the symbol for `{}`", key } - cache_on_disk_if { true } - } - - query def_kind(_: DefId) -> Option {} - query def_span(_: DefId) -> Span { - // FIXME(mw): DefSpans are not really inputs since they are derived from - // HIR. But at the moment HIR hashing still contains some hacks that allow - // to make type debuginfo to be source location independent. Declaring - // DefSpan an input makes sure that changes to these are always detected - // regardless of HIR hashing. - eval_always - } - query lookup_stability(_: DefId) -> Option<&'tcx attr::Stability> {} - query lookup_const_stability(_: DefId) -> Option<&'tcx attr::ConstStability> {} - query lookup_deprecation_entry(_: DefId) -> Option {} - query item_attrs(_: DefId) -> Lrc<[ast::Attribute]> {} - } - - Codegen { - query codegen_fn_attrs(_: DefId) -> CodegenFnAttrs { - cache_on_disk_if { true } - } - } - - Other { - query fn_arg_names(_: DefId) -> Vec {} - /// Gets the rendered value of the specified constant or associated constant. - /// Used by rustdoc. - query rendered_const(_: DefId) -> String {} - query impl_parent(_: DefId) -> Option {} - } - - TypeChecking { - query trait_of_item(_: DefId) -> Option {} - } - - Codegen { - query is_mir_available(key: DefId) -> bool { - desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } - } - } - - Other { - query vtable_methods(key: ty::PolyTraitRef<'tcx>) - -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { - desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } - } - } - - Codegen { - query codegen_fulfill_obligation( - key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Option> { - cache_on_disk_if { true } - desc { |tcx| - "checking if `{}` fulfills its obligations", - tcx.def_path_str(key.1.def_id()) - } - } - } - - TypeChecking { - query all_local_trait_impls(key: CrateNum) -> &'tcx BTreeMap> { - desc { "local trait impls" } - } - query trait_impls_of(key: DefId) -> &'tcx ty::trait_def::TraitImpls { - desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) } - } - query specialization_graph_of(key: DefId) -> &'tcx specialization_graph::Graph { - desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - query object_safety_violations(key: DefId) -> Vec { - desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) } - } - - /// Gets the ParameterEnvironment for a given item; this environment - /// will be in "user-facing" mode, meaning that it is suitabe for - /// type-checking etc, and it does not normalize specializable - /// associated types. This is almost always what you want, - /// unless you are doing MIR optimizations, in which case you - /// might want to use `reveal_all()` method to change modes. - query param_env(_: DefId) -> ty::ParamEnv<'tcx> {} - - /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, - /// `ty.is_copy()`, etc, since that will prune the environment where possible. - query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Copy`", env.value } - } - /// Query backing `TyS::is_sized`. - query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Sized`", env.value } - } - /// Query backing `TyS::is_freeze`. - query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is freeze", env.value } - } - /// Query backing `TyS::needs_drop`. - query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` needs drop", env.value } - } - - /// A list of types where the ADT requires drop if and only if any of - /// those types require drop. If the ADT is known to always need drop - /// then `Err(AlwaysRequiresDrop)` is returned. - query adt_drop_tys(_: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - cache_on_disk_if { true } - } - - query layout_raw( - env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> - ) -> Result<&'tcx ty::layout::LayoutDetails, ty::layout::LayoutError<'tcx>> { - desc { "computing layout of `{}`", env.value } - } - } - - Other { - query dylib_dependency_formats(_: CrateNum) - -> &'tcx [(CrateNum, LinkagePreference)] { - desc { "dylib dependency formats of crate" } - } - - query dependency_formats(_: CrateNum) - -> Lrc - { - desc { "get the linkage format of all dependencies" } - } - } - - Codegen { - query is_compiler_builtins(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate is_compiler_builtins" } - } - query has_global_allocator(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate has_global_allocator" } - } - query has_panic_handler(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate has_panic_handler" } - } - query is_profiler_runtime(_: CrateNum) -> bool { - fatal_cycle - desc { "query a crate is `#![profiler_runtime]`" } - } - query panic_strategy(_: CrateNum) -> PanicStrategy { - fatal_cycle - desc { "query a crate's configured panic strategy" } - } - query is_no_builtins(_: CrateNum) -> bool { - fatal_cycle - desc { "test whether a crate has `#![no_builtins]`" } - } - query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { - fatal_cycle - desc { "query a crate's symbol mangling version" } - } - - query extern_crate(_: DefId) -> Option<&'tcx ExternCrate> { - eval_always - desc { "getting crate's ExternCrateData" } - } - } - - TypeChecking { - query specializes(_: (DefId, DefId)) -> bool { - desc { "computing whether impls specialize one another" } - } - query in_scope_traits_map(_: LocalDefId) - -> Option<&'tcx FxHashMap>> { - eval_always - desc { "traits in scope at a block" } - } - } - - Other { - query module_exports(_: DefId) -> Option<&'tcx [Export]> { - eval_always - } - } - - TypeChecking { - query impl_defaultness(_: DefId) -> hir::Defaultness {} - - query check_item_well_formed(_: DefId) -> () {} - query check_trait_item_well_formed(_: DefId) -> () {} - query check_impl_item_well_formed(_: DefId) -> () {} - } - - Linking { - // The `DefId`s of all non-generic functions and statics in the given crate - // that can be reached from outside the crate. - // - // We expect this items to be available for being linked to. - // - // This query can also be called for `LOCAL_CRATE`. In this case it will - // compute which items will be reachable to other crates, taking into account - // the kind of crate that is currently compiled. Crates with only a - // C interface have fewer reachable things. - // - // Does not include external symbols that don't have a corresponding DefId, - // like the compiler-generated `main` function and so on. - query reachable_non_generics(_: CrateNum) - -> &'tcx DefIdMap { - desc { "looking up the exported symbols of a crate" } - } - query is_reachable_non_generic(_: DefId) -> bool {} - query is_unreachable_local_definition(_: DefId) -> bool {} - } - - Codegen { - /// The entire set of monomorphizations the local crate can safely link - /// to because they are exported from upstream crates. Do not depend on - /// this directly, as its value changes anytime a monomorphization gets - /// added or removed in any upstream crate. Instead use the narrower - /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even - /// better, `Instance::upstream_monomorphization()`. - query upstream_monomorphizations( - k: CrateNum - ) -> &'tcx DefIdMap, CrateNum>> { - desc { "collecting available upstream monomorphizations `{:?}`", k } - } - - /// Returns the set of upstream monomorphizations available for the - /// generic function identified by the given `def_id`. The query makes - /// sure to make a stable selection if the same monomorphization is - /// available in multiple upstream crates. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - query upstream_monomorphizations_for(_: DefId) - -> Option<&'tcx FxHashMap, CrateNum>> {} - - /// Returns the upstream crate that exports drop-glue for the given - /// type (`substs` is expected to be a single-item list containing the - /// type one wants drop-glue for). - /// - /// This is a subset of `upstream_monomorphizations_for` in order to - /// increase dep-tracking granularity. Otherwise adding or removing any - /// type with drop-glue in any upstream crate would invalidate all - /// functions calling drop-glue of an upstream type. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - /// - /// NOTE: This query could easily be extended to also support other - /// common functions that have are large set of monomorphizations - /// (like `Clone::clone` for example). - query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option { - desc { "available upstream drop-glue for `{:?}`", substs } - } - } - - Other { - query foreign_modules(_: CrateNum) -> &'tcx [ForeignModule] { - desc { "looking up the foreign modules of a linked crate" } - } - - /// Identifies the entry-point (e.g., the `main` function) for a given - /// crate, returning `None` if there is no entry point (such as for library crates). - query entry_fn(_: CrateNum) -> Option<(DefId, EntryFnType)> { - desc { "looking up the entry function of a crate" } - } - query plugin_registrar_fn(_: CrateNum) -> Option { - desc { "looking up the plugin registrar for a crate" } - } - query proc_macro_decls_static(_: CrateNum) -> Option { - desc { "looking up the derive registrar for a crate" } - } - query crate_disambiguator(_: CrateNum) -> CrateDisambiguator { - eval_always - desc { "looking up the disambiguator a crate" } - } - query crate_hash(_: CrateNum) -> Svh { - eval_always - desc { "looking up the hash a crate" } - } - query crate_host_hash(_: CrateNum) -> Option { - eval_always - desc { "looking up the hash of a host version of a crate" } - } - query original_crate_name(_: CrateNum) -> Symbol { - eval_always - desc { "looking up the original name a crate" } - } - query extra_filename(_: CrateNum) -> String { - eval_always - desc { "looking up the extra filename for a crate" } - } - } - - TypeChecking { - query implementations_of_trait(_: (CrateNum, DefId)) - -> &'tcx [DefId] { - desc { "looking up implementations of a trait in a crate" } - } - query all_trait_implementations(_: CrateNum) - -> &'tcx [DefId] { - desc { "looking up all (?) trait implementations" } - } - } - - Other { - query dllimport_foreign_items(_: CrateNum) - -> &'tcx FxHashSet { - desc { "dllimport_foreign_items" } - } - query is_dllimport_foreign_item(_: DefId) -> bool {} - query is_statically_included_foreign_item(_: DefId) -> bool {} - query native_library_kind(_: DefId) - -> Option {} - } - - Linking { - query link_args(_: CrateNum) -> Lrc> { - eval_always - desc { "looking up link arguments for a crate" } - } - } - - BorrowChecking { - /// Lifetime resolution. See `middle::resolve_lifetimes`. - query resolve_lifetimes(_: CrateNum) -> &'tcx ResolveLifetimes { - desc { "resolving lifetimes" } - } - query named_region_map(_: LocalDefId) -> - Option<&'tcx FxHashMap> { - desc { "looking up a named region" } - } - query is_late_bound_map(_: LocalDefId) -> - Option<&'tcx FxHashSet> { - desc { "testing if a region is late bound" } - } - query object_lifetime_defaults_map(_: LocalDefId) - -> Option<&'tcx FxHashMap>> { - desc { "looking up lifetime defaults for a region" } - } - } - - TypeChecking { - query visibility(_: DefId) -> ty::Visibility {} - } - - Other { - query dep_kind(_: CrateNum) -> DepKind { - eval_always - desc { "fetching what a dependency looks like" } - } - query crate_name(_: CrateNum) -> Symbol { - eval_always - desc { "fetching what a crate is named" } - } - query item_children(_: DefId) -> &'tcx [Export] {} - query extern_mod_stmt_cnum(_: DefId) -> Option {} - - query get_lib_features(_: CrateNum) -> &'tcx LibFeatures { - eval_always - desc { "calculating the lib features map" } - } - query defined_lib_features(_: CrateNum) - -> &'tcx [(Symbol, Option)] { - desc { "calculating the lib features defined in a crate" } - } - /// Returns the lang items defined in another crate by loading it from metadata. - // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid - // of that argument? - query get_lang_items(_: CrateNum) -> &'tcx LanguageItems { - eval_always - desc { "calculating the lang items map" } - } - - /// Returns all diagnostic items defined in all crates. - query all_diagnostic_items(_: CrateNum) -> &'tcx FxHashMap { - eval_always - desc { "calculating the diagnostic items map" } - } - - /// Returns the lang items defined in another crate by loading it from metadata. - query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { - desc { "calculating the lang items defined in a crate" } - } - - /// Returns the diagnostic items defined in a crate. - query diagnostic_items(_: CrateNum) -> &'tcx FxHashMap { - desc { "calculating the diagnostic items map in a crate" } - } - - query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { - desc { "calculating the missing lang items in a crate" } - } - query visible_parent_map(_: CrateNum) - -> &'tcx DefIdMap { - desc { "calculating the visible parent map" } - } - query missing_extern_crate_item(_: CrateNum) -> bool { - eval_always - desc { "seeing if we're missing an `extern crate` item for this crate" } - } - query used_crate_source(_: CrateNum) -> Lrc { - eval_always - desc { "looking at the source for a crate" } - } - query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] { - eval_always - desc { "generating a postorder list of CrateNums" } - } - - query upvars(_: DefId) -> Option<&'tcx FxIndexMap> { - eval_always - } - query maybe_unused_trait_import(_: DefId) -> bool { - eval_always - } - query maybe_unused_extern_crates(_: CrateNum) - -> &'tcx [(DefId, Span)] { - eval_always - desc { "looking up all possibly unused extern crates" } - } - query names_imported_by_glob_use(_: DefId) - -> Lrc> { - eval_always - } - - query stability_index(_: CrateNum) -> &'tcx stability::Index<'tcx> { - eval_always - desc { "calculating the stability index for the local crate" } - } - query all_crate_nums(_: CrateNum) -> &'tcx [CrateNum] { - eval_always - desc { "fetching all foreign CrateNum instances" } - } - - /// A vector of every trait accessible in the whole crate - /// (i.e., including those from subcrates). This is used only for - /// error reporting. - query all_traits(_: CrateNum) -> &'tcx [DefId] { - desc { "fetching all foreign and local traits" } - } - } - - Linking { - /// The list of symbols exported from the given crate. - /// - /// - All names contained in `exported_symbols(cnum)` are guaranteed to - /// correspond to a publicly visible symbol in `cnum` machine code. - /// - The `exported_symbols` sets of different crates do not intersect. - query exported_symbols(_: CrateNum) - -> Arc, SymbolExportLevel)>> { - desc { "exported_symbols" } - } - } - - Codegen { - query collect_and_partition_mono_items(_: CrateNum) - -> (Arc, Arc>>>) { - eval_always - desc { "collect_and_partition_mono_items" } - } - query is_codegened_item(_: DefId) -> bool {} - query codegen_unit(_: Symbol) -> Arc> { - desc { "codegen_unit" } - } - query backend_optimization_level(_: CrateNum) -> OptLevel { - desc { "optimization level used by backend" } - } - } - - Other { - query output_filenames(_: CrateNum) -> Arc { - eval_always - desc { "output_filenames" } - } - } - - TypeChecking { - /// Do not call this query directly: invoke `normalize` instead. - query normalize_projection_ty( - goal: CanonicalProjectionGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: invoke `normalize_erasing_regions` instead. - query normalize_ty_after_erasing_regions( - goal: ParamEnvAnd<'tcx, Ty<'tcx>> - ) -> Ty<'tcx> { - desc { "normalizing `{:?}`", goal } - } - - query implied_outlives_bounds( - goal: CanonicalTyGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, - NoSolution, - > { - desc { "computing implied outlives bounds for `{:?}`", goal } - } - - /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. - query dropck_outlives( - goal: CanonicalTyGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, - NoSolution, - > { - desc { "computing dropck types for `{:?}`", goal } - } - - /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or - /// `infcx.predicate_must_hold()` instead. - query evaluate_obligation( - goal: CanonicalPredicateGoal<'tcx> - ) -> Result { - desc { "evaluating trait selection obligation `{}`", goal.value.value } - } - - /// Do not call this query directly: part of the `Eq` type-op - query type_op_ascribe_user_type( - goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Eq` type-op - query type_op_eq( - goal: CanonicalTypeOpEqGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_eq` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Subtype` type-op - query type_op_subtype( - goal: CanonicalTypeOpSubtypeGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_subtype` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `ProvePredicate` type-op - query type_op_prove_predicate( - goal: CanonicalTypeOpProvePredicateGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_prove_predicate` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_ty( - goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_predicate( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_poly_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { - desc { |tcx| - "testing substituted normalized predicates:`{}`", - tcx.def_path_str(key.0) - } - } - - query method_autoderef_steps( - goal: CanonicalTyGoal<'tcx> - ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{:?}`", goal } - } - } - - Other { - query target_features_whitelist(_: CrateNum) -> &'tcx FxHashMap> { - eval_always - desc { "looking up the whitelist of target features" } - } - - // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. - query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) - -> usize { - desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } - } - - query features_query(_: CrateNum) -> &'tcx rustc_feature::Features { - eval_always - desc { "looking up enabled feature gates" } - } - } -} diff --git a/src/librustc/tests.rs b/src/librustc/tests.rs deleted file mode 100644 index cf3ea2ffa9397..0000000000000 --- a/src/librustc/tests.rs +++ /dev/null @@ -1,13 +0,0 @@ -use super::*; - -// FIXME(#27438): right now the unit tests of librustc don't refer to any actual -// functions generated in librustc_data_structures (all -// references are through generic functions), but statics are -// referenced from time to time. Due to this bug we won't -// actually correctly link in the statics unless we also -// reference a function, so be sure to reference a dummy -// function. -#[test] -fn noop() { - rustc_data_structures::__noop_fix_for_27438(); -} diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs deleted file mode 100644 index 6ebcc8b075462..0000000000000 --- a/src/librustc/traits/mod.rs +++ /dev/null @@ -1,841 +0,0 @@ -//! Trait Resolution. See the [rustc dev guide] for more information on how this works. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html - -pub mod query; -pub mod select; -pub mod specialization_graph; -mod structural_impls; - -use crate::mir::interpret::ErrorHandled; -use crate::ty::subst::SubstsRef; -use crate::ty::{self, AdtKind, List, Ty, TyCtxt}; - -use rustc_ast::ast; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::{Span, DUMMY_SP}; -use smallvec::SmallVec; - -use std::borrow::Cow; -use std::fmt::Debug; -use std::rc::Rc; - -pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; - -pub use self::ObligationCauseCode::*; -pub use self::SelectionError::*; -pub use self::Vtable::*; - -/// Depending on the stage of compilation, we want projection to be -/// more or less conservative. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] -pub enum Reveal { - /// At type-checking time, we refuse to project any associated - /// type that is marked `default`. Non-`default` ("final") types - /// are always projected. This is necessary in general for - /// soundness of specialization. However, we *could* allow - /// projections in fully-monomorphic cases. We choose not to, - /// because we prefer for `default type` to force the type - /// definition to be treated abstractly by any consumers of the - /// impl. Concretely, that means that the following example will - /// fail to compile: - /// - /// ``` - /// trait Assoc { - /// type Output; - /// } - /// - /// impl Assoc for T { - /// default type Output = bool; - /// } - /// - /// fn main() { - /// let <() as Assoc>::Output = true; - /// } - /// ``` - UserFacing, - - /// At codegen time, all monomorphic projections will succeed. - /// Also, `impl Trait` is normalized to the concrete type, - /// which has to be already collected by type-checking. - /// - /// NOTE: as `impl Trait`'s concrete type should *never* - /// be observable directly by the user, `Reveal::All` - /// should not be used by checks which may expose - /// type equality or type contents to the user. - /// There are some exceptions, e.g., around OIBITS and - /// transmute-checking, which expose some details, but - /// not the whole concrete type of the `impl Trait`. - All, -} - -/// The reason why we incurred this obligation; used for error reporting. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct ObligationCause<'tcx> { - pub span: Span, - - /// The ID of the fn body that triggered this obligation. This is - /// used for region obligations to determine the precise - /// environment in which the region obligation should be evaluated - /// (in particular, closures can add new assumptions). See the - /// field `region_obligations` of the `FulfillmentContext` for more - /// information. - pub body_id: hir::HirId, - - pub code: ObligationCauseCode<'tcx>, -} - -impl<'tcx> ObligationCause<'tcx> { - #[inline] - pub fn new( - span: Span, - body_id: hir::HirId, - code: ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx> { - ObligationCause { span, body_id, code } - } - - pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> { - ObligationCause { span, body_id, code: MiscObligation } - } - - pub fn dummy() -> ObligationCause<'tcx> { - ObligationCause { span: DUMMY_SP, body_id: hir::CRATE_HIR_ID, code: MiscObligation } - } - - pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { - match self.code { - ObligationCauseCode::CompareImplMethodObligation { .. } - | ObligationCauseCode::MainFunctionType - | ObligationCauseCode::StartFunctionType => tcx.sess.source_map().def_span(self.span), - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - arm_span, - .. - }) => arm_span, - _ => self.span, - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum ObligationCauseCode<'tcx> { - /// Not well classified or should be obvious from the span. - MiscObligation, - - /// A slice or array is WF only if `T: Sized`. - SliceOrArrayElem, - - /// A tuple is WF only if its middle elements are `Sized`. - TupleElem, - - /// This is the trait reference from the given projection. - ProjectionWf(ty::ProjectionTy<'tcx>), - - /// In an impl of trait `X` for type `Y`, type `Y` must - /// also implement all supertraits of `X`. - ItemObligation(DefId), - - /// Like `ItemObligation`, but with extra detail on the source of the obligation. - BindingObligation(DefId, Span), - - /// A type like `&'a T` is WF only if `T: 'a`. - ReferenceOutlivesReferent(Ty<'tcx>), - - /// A type like `Box + 'b>` is WF only if `'b: 'a`. - ObjectTypeBound(Ty<'tcx>, ty::Region<'tcx>), - - /// Obligation incurred due to an object cast. - ObjectCastObligation(/* Object type */ Ty<'tcx>), - - /// Obligation incurred due to a coercion. - Coercion { - source: Ty<'tcx>, - target: Ty<'tcx>, - }, - - /// Various cases where expressions must be `Sized` / `Copy` / etc. - /// `L = X` implies that `L` is `Sized`. - AssignmentLhsSized, - /// `(x1, .., xn)` must be `Sized`. - TupleInitializerSized, - /// `S { ... }` must be `Sized`. - StructInitializerSized, - /// Type of each variable must be `Sized`. - VariableType(hir::HirId), - /// Argument type must be `Sized`. - SizedArgumentType, - /// Return type must be `Sized`. - SizedReturnType, - /// Yield type must be `Sized`. - SizedYieldType, - /// `[T, ..n]` implies that `T` must be `Copy`. - /// If `true`, suggest `const_in_array_repeat_expressions` feature flag. - RepeatVec(bool), - - /// Types of fields (other than the last, except for packed structs) in a struct must be sized. - FieldSized { - adt_kind: AdtKind, - last: bool, - }, - - /// Constant expressions must be sized. - ConstSized, - - /// `static` items must have `Sync` type. - SharedStatic, - - BuiltinDerivedObligation(DerivedObligationCause<'tcx>), - - ImplDerivedObligation(DerivedObligationCause<'tcx>), - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplMethodObligation { - item_name: ast::Name, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplTypeObligation { - item_name: ast::Name, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, - - /// Checking that this expression can be assigned where it needs to be - // FIXME(eddyb) #11161 is the original Expr required? - ExprAssignable, - - /// Computing common supertype in the arms of a match expression - MatchExpressionArm(Box>), - - /// Type error arising from type checking a pattern against an expected type. - Pattern { - /// The span of the scrutinee or type expression which caused the `root_ty` type. - span: Option, - /// The root expected type induced by a scrutinee or type expression. - root_ty: Ty<'tcx>, - /// Whether the `Span` came from an expression or a type expression. - origin_expr: bool, - }, - - /// Constants in patterns must have `Structural` type. - ConstPatternStructural, - - /// Computing common supertype in an if expression - IfExpression(Box), - - /// Computing common supertype of an if expression with no else counter-part - IfExpressionWithNoElse, - - /// `main` has wrong type - MainFunctionType, - - /// `start` has wrong type - StartFunctionType, - - /// Intrinsic has wrong type - IntrinsicType, - - /// Method receiver - MethodReceiver, - - /// `return` with no expression - ReturnNoExpression, - - /// `return` with an expression - ReturnValue(hir::HirId), - - /// Return type of this function - ReturnType, - - /// Block implicit return - BlockTailExpression(hir::HirId), - - /// #[feature(trivial_bounds)] is not enabled - TrivialBound, - - AssocTypeBound(Box), -} - -impl ObligationCauseCode<'_> { - // Return the base obligation, ignoring derived obligations. - pub fn peel_derives(&self) -> &Self { - let mut base_cause = self; - while let BuiltinDerivedObligation(cause) | ImplDerivedObligation(cause) = base_cause { - base_cause = &cause.parent_code; - } - base_cause - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct AssocTypeBoundData { - pub impl_span: Option, - pub original: Span, - pub bounds: Vec, -} - -// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(ObligationCauseCode<'_>, 32); - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct MatchExpressionArmCause<'tcx> { - pub arm_span: Span, - pub source: hir::MatchSource, - pub prior_arms: Vec, - pub last_ty: Ty<'tcx>, - pub scrut_hir_id: hir::HirId, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct IfExpressionCause { - pub then: Span, - pub outer: Option, - pub semicolon: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct DerivedObligationCause<'tcx> { - /// The trait reference of the parent obligation that led to the - /// current obligation. Note that only trait obligations lead to - /// derived obligations, so we just store the trait reference here - /// directly. - pub parent_trait_ref: ty::PolyTraitRef<'tcx>, - - /// The parent trait had this cause. - pub parent_code: Rc>, -} - -/// The following types: -/// * `WhereClause`, -/// * `WellFormed`, -/// * `FromEnv`, -/// * `DomainGoal`, -/// * `Goal`, -/// * `Clause`, -/// * `Environment`, -/// * `InEnvironment`, -/// are used for representing the trait system in the form of -/// logic programming clauses. They are part of the interface -/// for the chalk SLG solver. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, Lift)] -pub enum WhereClause<'tcx> { - Implemented(ty::TraitPredicate<'tcx>), - ProjectionEq(ty::ProjectionPredicate<'tcx>), - RegionOutlives(ty::RegionOutlivesPredicate<'tcx>), - TypeOutlives(ty::TypeOutlivesPredicate<'tcx>), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, Lift)] -pub enum WellFormed<'tcx> { - Trait(ty::TraitPredicate<'tcx>), - Ty(Ty<'tcx>), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, Lift)] -pub enum FromEnv<'tcx> { - Trait(ty::TraitPredicate<'tcx>), - Ty(Ty<'tcx>), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, Lift)] -pub enum DomainGoal<'tcx> { - Holds(WhereClause<'tcx>), - WellFormed(WellFormed<'tcx>), - FromEnv(FromEnv<'tcx>), - Normalize(ty::ProjectionPredicate<'tcx>), -} - -pub type PolyDomainGoal<'tcx> = ty::Binder>; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable)] -pub enum QuantifierKind { - Universal, - Existential, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, Lift)] -pub enum GoalKind<'tcx> { - Implies(Clauses<'tcx>, Goal<'tcx>), - And(Goal<'tcx>, Goal<'tcx>), - Not(Goal<'tcx>), - DomainGoal(DomainGoal<'tcx>), - Quantified(QuantifierKind, ty::Binder>), - Subtype(Ty<'tcx>, Ty<'tcx>), - CannotProve, -} - -pub type Goal<'tcx> = &'tcx GoalKind<'tcx>; - -pub type Goals<'tcx> = &'tcx List>; - -impl<'tcx> DomainGoal<'tcx> { - pub fn into_goal(self) -> GoalKind<'tcx> { - GoalKind::DomainGoal(self) - } - - pub fn into_program_clause(self) -> ProgramClause<'tcx> { - ProgramClause { - goal: self, - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::Other, - } - } -} - -impl<'tcx> GoalKind<'tcx> { - pub fn from_poly_domain_goal( - domain_goal: PolyDomainGoal<'tcx>, - tcx: TyCtxt<'tcx>, - ) -> GoalKind<'tcx> { - match domain_goal.no_bound_vars() { - Some(p) => p.into_goal(), - None => GoalKind::Quantified( - QuantifierKind::Universal, - domain_goal.map_bound(|p| tcx.mk_goal(p.into_goal())), - ), - } - } -} - -/// This matches the definition from Page 7 of "A Proof Procedure for the Logic of Hereditary -/// Harrop Formulas". -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] -pub enum Clause<'tcx> { - Implies(ProgramClause<'tcx>), - ForAll(ty::Binder>), -} - -impl Clause<'tcx> { - pub fn category(self) -> ProgramClauseCategory { - match self { - Clause::Implies(clause) => clause.category, - Clause::ForAll(clause) => clause.skip_binder().category, - } - } -} - -/// Multiple clauses. -pub type Clauses<'tcx> = &'tcx List>; - -/// A "program clause" has the form `D :- G1, ..., Gn`. It is saying -/// that the domain goal `D` is true if `G1...Gn` are provable. This -/// is equivalent to the implication `G1..Gn => D`; we usually write -/// it with the reverse implication operator `:-` to emphasize the way -/// that programs are actually solved (via backchaining, which starts -/// with the goal to solve and proceeds from there). -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] -pub struct ProgramClause<'tcx> { - /// This goal will be considered true ... - pub goal: DomainGoal<'tcx>, - - /// ... if we can prove these hypotheses (there may be no hypotheses at all): - pub hypotheses: Goals<'tcx>, - - /// Useful for filtering clauses. - pub category: ProgramClauseCategory, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable)] -pub enum ProgramClauseCategory { - ImpliedBound, - WellFormed, - Other, -} - -/// A set of clauses that we assume to be true. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] -pub struct Environment<'tcx> { - pub clauses: Clauses<'tcx>, -} - -impl Environment<'tcx> { - pub fn with(self, goal: G) -> InEnvironment<'tcx, G> { - InEnvironment { environment: self, goal } - } -} - -/// Something (usually a goal), along with an environment. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] -pub struct InEnvironment<'tcx, G> { - pub environment: Environment<'tcx>, - pub goal: G, -} - -#[derive(Clone, Debug, TypeFoldable)] -pub enum SelectionError<'tcx> { - Unimplemented, - OutputTypeParameterMismatch( - ty::PolyTraitRef<'tcx>, - ty::PolyTraitRef<'tcx>, - ty::error::TypeError<'tcx>, - ), - TraitNotObjectSafe(DefId), - ConstEvalFailure(ErrorHandled), - Overflow, -} - -/// When performing resolution, it is typically the case that there -/// can be one of three outcomes: -/// -/// - `Ok(Some(r))`: success occurred with result `r` -/// - `Ok(None)`: could not definitely determine anything, usually due -/// to inconclusive type inference. -/// - `Err(e)`: error `e` occurred -pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; - -/// Given the successful resolution of an obligation, the `Vtable` -/// indicates where the vtable comes from. Note that while we call this -/// a "vtable", it does not necessarily indicate dynamic dispatch at -/// runtime. `Vtable` instances just tell the compiler where to find -/// methods, but in generic code those methods are typically statically -/// dispatched -- only when an object is constructed is a `Vtable` -/// instance reified into an actual vtable. -/// -/// For example, the vtable may be tied to a specific impl (case A), -/// or it may be relative to some bound that is in scope (case B). -/// -/// ``` -/// impl Clone for Option { ... } // Impl_1 -/// impl Clone for Box { ... } // Impl_2 -/// impl Clone for int { ... } // Impl_3 -/// -/// fn foo(concrete: Option>, -/// param: T, -/// mixed: Option) { -/// -/// // Case A: Vtable points at a specific impl. Only possible when -/// // type is concretely known. If the impl itself has bounded -/// // type parameters, Vtable will carry resolutions for those as well: -/// concrete.clone(); // Vtable(Impl_1, [Vtable(Impl_2, [Vtable(Impl_3)])]) -/// -/// // Case B: Vtable must be provided by caller. This applies when -/// // type is a type parameter. -/// param.clone(); // VtableParam -/// -/// // Case C: A mix of cases A and B. -/// mixed.clone(); // Vtable(Impl_1, [VtableParam]) -/// } -/// ``` -/// -/// ### The type parameter `N` -/// -/// See explanation on `VtableImplData`. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub enum Vtable<'tcx, N> { - /// Vtable identifying a particular impl. - VtableImpl(VtableImplData<'tcx, N>), - - /// Vtable for auto trait implementations. - /// This carries the information and nested obligations with regards - /// to an auto implementation for a trait `Trait`. The nested obligations - /// ensure the trait implementation holds for all the constituent types. - VtableAutoImpl(VtableAutoImplData), - - /// Successful resolution to an obligation provided by the caller - /// for some type parameter. The `Vec` represents the - /// obligations incurred from normalizing the where-clause (if - /// any). - VtableParam(Vec), - - /// Virtual calls through an object. - VtableObject(VtableObjectData<'tcx, N>), - - /// Successful resolution for a builtin trait. - VtableBuiltin(VtableBuiltinData), - - /// Vtable automatically generated for a closure. The `DefId` is the ID - /// of the closure expression. This is a `VtableImpl` in spirit, but the - /// impl is generated by the compiler and does not appear in the source. - VtableClosure(VtableClosureData<'tcx, N>), - - /// Same as above, but for a function pointer type with the given signature. - VtableFnPointer(VtableFnPointerData<'tcx, N>), - - /// Vtable automatically generated for a generator. - VtableGenerator(VtableGeneratorData<'tcx, N>), - - /// Vtable for a trait alias. - VtableTraitAlias(VtableTraitAliasData<'tcx, N>), -} - -impl<'tcx, N> Vtable<'tcx, N> { - pub fn nested_obligations(self) -> Vec { - match self { - VtableImpl(i) => i.nested, - VtableParam(n) => n, - VtableBuiltin(i) => i.nested, - VtableAutoImpl(d) => d.nested, - VtableClosure(c) => c.nested, - VtableGenerator(c) => c.nested, - VtableObject(d) => d.nested, - VtableFnPointer(d) => d.nested, - VtableTraitAlias(d) => d.nested, - } - } - - pub fn borrow_nested_obligations(&self) -> &[N] { - match &self { - VtableImpl(i) => &i.nested[..], - VtableParam(n) => &n[..], - VtableBuiltin(i) => &i.nested[..], - VtableAutoImpl(d) => &d.nested[..], - VtableClosure(c) => &c.nested[..], - VtableGenerator(c) => &c.nested[..], - VtableObject(d) => &d.nested[..], - VtableFnPointer(d) => &d.nested[..], - VtableTraitAlias(d) => &d.nested[..], - } - } - - pub fn map(self, f: F) -> Vtable<'tcx, M> - where - F: FnMut(N) -> M, - { - match self { - VtableImpl(i) => VtableImpl(VtableImplData { - impl_def_id: i.impl_def_id, - substs: i.substs, - nested: i.nested.into_iter().map(f).collect(), - }), - VtableParam(n) => VtableParam(n.into_iter().map(f).collect()), - VtableBuiltin(i) => { - VtableBuiltin(VtableBuiltinData { nested: i.nested.into_iter().map(f).collect() }) - } - VtableObject(o) => VtableObject(VtableObjectData { - upcast_trait_ref: o.upcast_trait_ref, - vtable_base: o.vtable_base, - nested: o.nested.into_iter().map(f).collect(), - }), - VtableAutoImpl(d) => VtableAutoImpl(VtableAutoImplData { - trait_def_id: d.trait_def_id, - nested: d.nested.into_iter().map(f).collect(), - }), - VtableClosure(c) => VtableClosure(VtableClosureData { - closure_def_id: c.closure_def_id, - substs: c.substs, - nested: c.nested.into_iter().map(f).collect(), - }), - VtableGenerator(c) => VtableGenerator(VtableGeneratorData { - generator_def_id: c.generator_def_id, - substs: c.substs, - nested: c.nested.into_iter().map(f).collect(), - }), - VtableFnPointer(p) => VtableFnPointer(VtableFnPointerData { - fn_ty: p.fn_ty, - nested: p.nested.into_iter().map(f).collect(), - }), - VtableTraitAlias(d) => VtableTraitAlias(VtableTraitAliasData { - alias_def_id: d.alias_def_id, - substs: d.substs, - nested: d.nested.into_iter().map(f).collect(), - }), - } - } -} - -/// Identifies a particular impl in the source, along with a set of -/// substitutions from the impl's type/lifetime parameters. The -/// `nested` vector corresponds to the nested obligations attached to -/// the impl's type parameters. -/// -/// The type parameter `N` indicates the type used for "nested -/// obligations" that are required by the impl. During type-check, this -/// is `Obligation`, as one might expect. During codegen, however, this -/// is `()`, because codegen only requires a shallow resolution of an -/// impl, and nested obligations are satisfied later. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableImplData<'tcx, N> { - pub impl_def_id: DefId, - pub substs: SubstsRef<'tcx>, - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableGeneratorData<'tcx, N> { - pub generator_def_id: DefId, - pub substs: SubstsRef<'tcx>, - /// Nested obligations. This can be non-empty if the generator - /// signature contains associated types. - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableClosureData<'tcx, N> { - pub closure_def_id: DefId, - pub substs: SubstsRef<'tcx>, - /// Nested obligations. This can be non-empty if the closure - /// signature contains associated types. - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableAutoImplData { - pub trait_def_id: DefId, - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableBuiltinData { - pub nested: Vec, -} - -/// A vtable for some object-safe trait `Foo` automatically derived -/// for the object type `Foo`. -#[derive(PartialEq, Eq, Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableObjectData<'tcx, N> { - /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. - pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, - - /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits; this is the start of - /// `upcast_trait_ref`'s methods in that vtable. - pub vtable_base: usize, - - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableFnPointerData<'tcx, N> { - pub fn_ty: Ty<'tcx>, - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VtableTraitAliasData<'tcx, N> { - pub alias_def_id: DefId, - pub substs: SubstsRef<'tcx>, - pub nested: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] -pub enum ObjectSafetyViolation { - /// `Self: Sized` declared on the trait. - SizedSelf(SmallVec<[Span; 1]>), - - /// Supertrait reference references `Self` an in illegal location - /// (e.g., `trait Foo : Bar`). - SupertraitSelf(SmallVec<[Span; 1]>), - - /// Method has something illegal. - Method(ast::Name, MethodViolationCode, Span), - - /// Associated const. - AssocConst(ast::Name, Span), -} - -impl ObjectSafetyViolation { - pub fn error_msg(&self) -> Cow<'static, str> { - match *self { - ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), - ObjectSafetyViolation::SupertraitSelf(ref spans) => { - if spans.iter().any(|sp| *sp != DUMMY_SP) { - "it uses `Self` as a type parameter in this".into() - } else { - "it cannot use `Self` as a type parameter in a supertrait or `where`-clause" - .into() - } - } - ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { - format!("associated function `{}` has no `self` parameter", name).into() - } - ObjectSafetyViolation::Method( - name, - MethodViolationCode::ReferencesSelfInput(_), - DUMMY_SP, - ) => format!("method `{}` references the `Self` type in its parameters", name).into(), - ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfInput(_), _) => { - format!("method `{}` references the `Self` type in this parameter", name).into() - } - ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfOutput, _) => { - format!("method `{}` references the `Self` type in its return type", name).into() - } - ObjectSafetyViolation::Method( - name, - MethodViolationCode::WhereClauseReferencesSelf, - _, - ) => { - format!("method `{}` references the `Self` type in its `where` clause", name).into() - } - ObjectSafetyViolation::Method(name, MethodViolationCode::Generic, _) => { - format!("method `{}` has generic type parameters", name).into() - } - ObjectSafetyViolation::Method(name, MethodViolationCode::UndispatchableReceiver, _) => { - format!("method `{}`'s `self` parameter cannot be dispatched on", name).into() - } - ObjectSafetyViolation::AssocConst(name, DUMMY_SP) => { - format!("it contains associated `const` `{}`", name).into() - } - ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(), - } - } - - pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> { - Some(match *self { - ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => { - return None; - } - ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => ( - format!( - "consider turning `{}` into a method by giving it a `&self` argument or \ - constraining it so it does not apply to trait objects", - name - ), - sugg.map(|(sugg, sp)| (sugg.to_string(), sp)), - ), - ObjectSafetyViolation::Method( - name, - MethodViolationCode::UndispatchableReceiver, - span, - ) => ( - format!("consider changing method `{}`'s `self` parameter to be `&self`", name), - Some(("&Self".to_string(), span)), - ), - ObjectSafetyViolation::AssocConst(name, _) - | ObjectSafetyViolation::Method(name, ..) => { - (format!("consider moving `{}` to another trait", name), None) - } - }) - } - - pub fn spans(&self) -> SmallVec<[Span; 1]> { - // When `span` comes from a separate crate, it'll be `DUMMY_SP`. Treat it as `None` so - // diagnostics use a `note` instead of a `span_label`. - match self { - ObjectSafetyViolation::SupertraitSelf(spans) - | ObjectSafetyViolation::SizedSelf(spans) => spans.clone(), - ObjectSafetyViolation::AssocConst(_, span) - | ObjectSafetyViolation::Method(_, _, span) - if *span != DUMMY_SP => - { - smallvec![*span] - } - _ => smallvec![], - } - } -} - -/// Reasons a method might not be object-safe. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] -pub enum MethodViolationCode { - /// e.g., `fn foo()` - StaticMethod(Option<(&'static str, Span)>), - - /// e.g., `fn foo(&self, x: Self)` - ReferencesSelfInput(usize), - - /// e.g., `fn foo(&self) -> Self` - ReferencesSelfOutput, - - /// e.g., `fn foo(&self) where Self: Clone` - WhereClauseReferencesSelf, - - /// e.g., `fn foo()` - Generic, - - /// the method's receiver (`self` argument) can't be dispatched on - UndispatchableReceiver, -} diff --git a/src/librustc/traits/query.rs b/src/librustc/traits/query.rs deleted file mode 100644 index 67f4b15f9194d..0000000000000 --- a/src/librustc/traits/query.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! Experimental types for the trait query interface. The methods -//! defined in this module are all based on **canonicalization**, -//! which makes a canonical query by replacing unbound inference -//! variables and regions, so that results can be reused more broadly. -//! The providers for the queries defined here can be found in -//! `librustc_traits`. - -use crate::ich::StableHashingContext; -use crate::infer::canonical::{Canonical, QueryResponse}; -use crate::ty::error::TypeError; -use crate::ty::subst::GenericArg; -use crate::ty::{self, Ty, TyCtxt}; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; -use rustc_span::source_map::Span; -use std::iter::FromIterator; -use std::mem; - -pub mod type_op { - use crate::ty::fold::TypeFoldable; - use crate::ty::subst::UserSubsts; - use crate::ty::{Predicate, Ty}; - use rustc_hir::def_id::DefId; - use std::fmt; - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct AscribeUserType<'tcx> { - pub mir_ty: Ty<'tcx>, - pub def_id: DefId, - pub user_substs: UserSubsts<'tcx>, - } - - impl<'tcx> AscribeUserType<'tcx> { - pub fn new(mir_ty: Ty<'tcx>, def_id: DefId, user_substs: UserSubsts<'tcx>) -> Self { - Self { mir_ty, def_id, user_substs } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct Eq<'tcx> { - pub a: Ty<'tcx>, - pub b: Ty<'tcx>, - } - - impl<'tcx> Eq<'tcx> { - pub fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { - Self { a, b } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct Subtype<'tcx> { - pub sub: Ty<'tcx>, - pub sup: Ty<'tcx>, - } - - impl<'tcx> Subtype<'tcx> { - pub fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { - Self { sub, sup } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct ProvePredicate<'tcx> { - pub predicate: Predicate<'tcx>, - } - - impl<'tcx> ProvePredicate<'tcx> { - pub fn new(predicate: Predicate<'tcx>) -> Self { - ProvePredicate { predicate } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct Normalize { - pub value: T, - } - - impl<'tcx, T> Normalize - where - T: fmt::Debug + TypeFoldable<'tcx>, - { - pub fn new(value: T) -> Self { - Self { value } - } - } -} - -pub type CanonicalProjectionGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; - -pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; - -pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; - -pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; - -pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; - -pub type CanonicalTypeOpSubtypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; - -pub type CanonicalTypeOpProvePredicateGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; - -pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; - -#[derive(Clone, Debug, HashStable)] -pub struct NoSolution; - -pub type Fallible = Result; - -impl<'tcx> From> for NoSolution { - fn from(_: TypeError<'tcx>) -> NoSolution { - NoSolution - } -} - -#[derive(Clone, Debug, Default, HashStable, TypeFoldable, Lift)] -pub struct DropckOutlivesResult<'tcx> { - pub kinds: Vec>, - pub overflows: Vec>, -} - -impl<'tcx> DropckOutlivesResult<'tcx> { - pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { - if let Some(overflow_ty) = self.overflows.iter().next() { - let mut err = struct_span_err!( - tcx.sess, - span, - E0320, - "overflow while adding drop-check rules for {}", - ty, - ); - err.note(&format!("overflowed on {}", overflow_ty)); - err.emit(); - } - } - - pub fn into_kinds_reporting_overflows( - self, - tcx: TyCtxt<'tcx>, - span: Span, - ty: Ty<'tcx>, - ) -> Vec> { - self.report_overflows(tcx, span, ty); - let DropckOutlivesResult { kinds, overflows: _ } = self; - kinds - } -} - -/// A set of constraints that need to be satisfied in order for -/// a type to be valid for destruction. -#[derive(Clone, Debug, HashStable)] -pub struct DtorckConstraint<'tcx> { - /// Types that are required to be alive in order for this - /// type to be valid for destruction. - pub outlives: Vec>, - - /// Types that could not be resolved: projections and params. - pub dtorck_types: Vec>, - - /// If, during the computation of the dtorck constraint, we - /// overflow, that gets recorded here. The caller is expected to - /// report an error. - pub overflows: Vec>, -} - -impl<'tcx> DtorckConstraint<'tcx> { - pub fn empty() -> DtorckConstraint<'tcx> { - DtorckConstraint { outlives: vec![], dtorck_types: vec![], overflows: vec![] } - } -} - -impl<'tcx> FromIterator> for DtorckConstraint<'tcx> { - fn from_iter>>(iter: I) -> Self { - let mut result = Self::empty(); - - for DtorckConstraint { outlives, dtorck_types, overflows } in iter { - result.outlives.extend(outlives); - result.dtorck_types.extend(dtorck_types); - result.overflows.extend(overflows); - } - - result - } -} - -/// This returns true if the type `ty` is "trivial" for -/// dropck-outlives -- that is, if it doesn't require any types to -/// outlive. This is similar but not *quite* the same as the -/// `needs_drop` test in the compiler already -- that is, for every -/// type T for which this function return true, needs-drop would -/// return `false`. But the reverse does not hold: in particular, -/// `needs_drop` returns false for `PhantomData`, but it is not -/// trivial for dropck-outlives. -/// -/// Note also that `needs_drop` requires a "global" type (i.e., one -/// with erased regions), but this function does not. -pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { - // None of these types have a destructor and hence they do not - // require anything in particular to outlive the dtor's - // execution. - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str - | ty::Foreign(..) - | ty::Error => true, - - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), - - // (T1..Tn) and closures have same properties as T1..Tn -- - // check if *any* of those are trivial. - ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), - ty::Closure(_, ref substs) => { - substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) - } - - ty::Adt(def, _) => { - if Some(def.did) == tcx.lang_items().manually_drop() { - // `ManuallyDrop` never has a dtor. - true - } else { - // Other types might. Moreover, PhantomData doesn't - // have a dtor, but it is considered to own its - // content, so it is non-trivial. Unions can have `impl Drop`, - // and hence are non-trivial as well. - false - } - } - - // The following *might* require a destructor: needs deeper inspection. - ty::Dynamic(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Generator(..) => false, - - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - } -} - -#[derive(Debug, HashStable)] -pub struct CandidateStep<'tcx> { - pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, - pub autoderefs: usize, - /// `true` if the type results from a dereference of a raw pointer. - /// when assembling candidates, we include these steps, but not when - /// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods - /// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then - /// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't. - pub from_unsafe_deref: bool, - pub unsize: bool, -} - -#[derive(Clone, Debug, HashStable)] -pub struct MethodAutoderefStepsResult<'tcx> { - /// The valid autoderef steps that could be find. - pub steps: Lrc>>, - /// If Some(T), a type autoderef reported an error on. - pub opt_bad_ty: Option>>, - /// If `true`, `steps` has been truncated due to reaching the - /// recursion limit. - pub reached_recursion_limit: bool, -} - -#[derive(Debug, HashStable)] -pub struct MethodAutoderefBadTy<'tcx> { - pub reached_raw_pointer: bool, - pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, -} - -/// Result from the `normalize_projection_ty` query. -#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] -pub struct NormalizationResult<'tcx> { - /// Result of normalization. - pub normalized_ty: Ty<'tcx>, -} - -/// Outlives bounds are relationships between generic parameters, -/// whether they both be regions (`'a: 'b`) or whether types are -/// involved (`T: 'a`). These relationships can be extracted from the -/// full set of predicates we understand or also from types (in which -/// case they are called implied bounds). They are fed to the -/// `OutlivesEnv` which in turn is supplied to the region checker and -/// other parts of the inference system. -#[derive(Clone, Debug, TypeFoldable, Lift)] -pub enum OutlivesBound<'tcx> { - RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), - RegionSubParam(ty::Region<'tcx>, ty::ParamTy), - RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), -} - -impl<'a, 'tcx> HashStable> for OutlivesBound<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - mem::discriminant(self).hash_stable(hcx, hasher); - match *self { - OutlivesBound::RegionSubRegion(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - OutlivesBound::RegionSubParam(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - OutlivesBound::RegionSubProjection(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - } - } -} diff --git a/src/librustc/traits/specialization_graph.rs b/src/librustc/traits/specialization_graph.rs deleted file mode 100644 index 1847326a742eb..0000000000000 --- a/src/librustc/traits/specialization_graph.rs +++ /dev/null @@ -1,211 +0,0 @@ -use crate::ich::{self, StableHashingContext}; -use crate::ty::fast_reject::SimplifiedType; -use crate::ty::{self, TyCtxt}; -use rustc_ast::ast::Ident; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_errors::ErrorReported; -use rustc_hir::def_id::{DefId, DefIdMap}; - -/// A per-trait graph of impls in specialization order. At the moment, this -/// graph forms a tree rooted with the trait itself, with all other nodes -/// representing impls, and parent-child relationships representing -/// specializations. -/// -/// The graph provides two key services: -/// -/// - Construction. This implicitly checks for overlapping impls (i.e., impls -/// that overlap but where neither specializes the other -- an artifact of the -/// simple "chain" rule. -/// -/// - Parent extraction. In particular, the graph can give you the *immediate* -/// parents of a given specializing impl, which is needed for extracting -/// default items amongst other things. In the simple "chain" rule, every impl -/// has at most one parent. -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub struct Graph { - /// All impls have a parent; the "root" impls have as their parent the `def_id` - /// of the trait. - pub parent: DefIdMap, - - /// The "root" impls are found by looking up the trait's def_id. - pub children: DefIdMap, - - /// Whether an error was emitted while constructing the graph. - pub has_errored: bool, -} - -impl Graph { - pub fn new() -> Graph { - Graph { parent: Default::default(), children: Default::default(), has_errored: false } - } - - /// The parent of a given impl, which is the `DefId` of the trait when the - /// impl is a "specialization root". - pub fn parent(&self, child: DefId) -> DefId { - *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {:?}", child)) - } -} - -/// Children of a given impl, grouped into blanket/non-blanket varieties as is -/// done in `TraitDef`. -#[derive(Default, RustcEncodable, RustcDecodable)] -pub struct Children { - // Impls of a trait (or specializations of a given impl). To allow for - // quicker lookup, the impls are indexed by a simplified version of their - // `Self` type: impls with a simplifiable `Self` are stored in - // `nonblanket_impls` keyed by it, while all other impls are stored in - // `blanket_impls`. - // - // A similar division is used within `TraitDef`, but the lists there collect - // together *all* the impls for a trait, and are populated prior to building - // the specialization graph. - /// Impls of the trait. - pub nonblanket_impls: FxHashMap>, - - /// Blanket impls associated with the trait. - pub blanket_impls: Vec, -} - -/// A node in the specialization graph is either an impl or a trait -/// definition; either can serve as a source of item definitions. -/// There is always exactly one trait definition node: the root. -#[derive(Debug, Copy, Clone)] -pub enum Node { - Impl(DefId), - Trait(DefId), -} - -impl<'tcx> Node { - pub fn is_from_trait(&self) -> bool { - match *self { - Node::Trait(..) => true, - _ => false, - } - } - - /// Iterate over the items defined directly by the given (impl or trait) node. - pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator { - tcx.associated_items(self.def_id()).in_definition_order() - } - - /// Finds an associated item defined in this node. - /// - /// If this returns `None`, the item can potentially still be found in - /// parents of this node. - pub fn item( - &self, - tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - trait_def_id: DefId, - ) -> Option { - use crate::ty::AssocKind::*; - - tcx.associated_items(self.def_id()) - .filter_by_name_unhygienic(trait_item_name.name) - .find(move |impl_item| { - match (trait_item_kind, impl_item.kind) { - | (Const, Const) - | (Method, Method) - | (Type, Type) - | (Type, OpaqueTy) // assoc. types can be made opaque in impls - => tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id), - - | (Const, _) - | (Method, _) - | (Type, _) - | (OpaqueTy, _) - => false, - } - }) - .copied() - } - - pub fn def_id(&self) -> DefId { - match *self { - Node::Impl(did) => did, - Node::Trait(did) => did, - } - } -} - -#[derive(Copy, Clone)] -pub struct Ancestors<'tcx> { - trait_def_id: DefId, - specialization_graph: &'tcx Graph, - current_source: Option, -} - -impl Iterator for Ancestors<'_> { - type Item = Node; - fn next(&mut self) -> Option { - let cur = self.current_source.take(); - if let Some(Node::Impl(cur_impl)) = cur { - let parent = self.specialization_graph.parent(cur_impl); - - self.current_source = if parent == self.trait_def_id { - Some(Node::Trait(parent)) - } else { - Some(Node::Impl(parent)) - }; - } - cur - } -} - -pub struct NodeItem { - pub node: Node, - pub item: T, -} - -impl NodeItem { - pub fn map U>(self, f: F) -> NodeItem { - NodeItem { node: self.node, item: f(self.item) } - } -} - -impl<'tcx> Ancestors<'tcx> { - /// Finds the bottom-most (ie. most specialized) definition of an associated - /// item. - pub fn leaf_def( - mut self, - tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - ) -> Option> { - let trait_def_id = self.trait_def_id; - self.find_map(|node| { - node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) - .map(|item| NodeItem { node, item }) - }) - } -} - -/// Walk up the specialization ancestors of a given impl, starting with that -/// impl itself. Returns `None` if an error was reported while building the -/// specialization graph. -pub fn ancestors( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - start_from_impl: DefId, -) -> Result, ErrorReported> { - let specialization_graph = tcx.specialization_graph_of(trait_def_id); - if specialization_graph.has_errored { - Err(ErrorReported) - } else { - Ok(Ancestors { - trait_def_id, - specialization_graph, - current_source: Some(Node::Impl(start_from_impl)), - }) - } -} - -impl<'a> HashStable> for Children { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let Children { ref nonblanket_impls, ref blanket_impls } = *self; - - ich::hash_stable_trait_impls(hcx, hasher, blanket_impls, nonblanket_impls); - } -} diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs deleted file mode 100644 index a5efea9e5fa4d..0000000000000 --- a/src/librustc/traits/structural_impls.rs +++ /dev/null @@ -1,640 +0,0 @@ -use crate::traits; -use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::{self, Lift, Ty, TyCtxt}; -use rustc_span::symbol::Symbol; -use smallvec::SmallVec; - -use std::collections::{BTreeMap, BTreeSet}; -use std::fmt; -use std::rc::Rc; - -// Structural impls for the structs in `traits`. - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::Vtable<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - super::VtableImpl(ref v) => write!(f, "{:?}", v), - - super::VtableAutoImpl(ref t) => write!(f, "{:?}", t), - - super::VtableClosure(ref d) => write!(f, "{:?}", d), - - super::VtableGenerator(ref d) => write!(f, "{:?}", d), - - super::VtableFnPointer(ref d) => write!(f, "VtableFnPointer({:?})", d), - - super::VtableObject(ref d) => write!(f, "{:?}", d), - - super::VtableParam(ref n) => write!(f, "VtableParam({:?})", n), - - super::VtableBuiltin(ref d) => write!(f, "{:?}", d), - - super::VtableTraitAlias(ref d) => write!(f, "{:?}", d), - } - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableImplData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "VtableImplData(impl_def_id={:?}, substs={:?}, nested={:?})", - self.impl_def_id, self.substs, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableGeneratorData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "VtableGeneratorData(generator_def_id={:?}, substs={:?}, nested={:?})", - self.generator_def_id, self.substs, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableClosureData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "VtableClosureData(closure_def_id={:?}, substs={:?}, nested={:?})", - self.closure_def_id, self.substs, self.nested - ) - } -} - -impl fmt::Debug for traits::VtableBuiltinData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "VtableBuiltinData(nested={:?})", self.nested) - } -} - -impl fmt::Debug for traits::VtableAutoImplData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "VtableAutoImplData(trait_def_id={:?}, nested={:?})", - self.trait_def_id, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableObjectData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "VtableObjectData(upcast={:?}, vtable_base={}, nested={:?})", - self.upcast_trait_ref, self.vtable_base, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableFnPointerData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "VtableFnPointerData(fn_ty={:?}, nested={:?})", self.fn_ty, self.nested) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableTraitAliasData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "VtableTraitAlias(alias_def_id={:?}, substs={:?}, nested={:?})", - self.alias_def_id, self.substs, self.nested - ) - } -} - -impl<'tcx> fmt::Display for traits::WhereClause<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::traits::WhereClause::*; - - // Bypass `ty::print` because it does not print out anonymous regions. - // FIXME(eddyb) implement a custom `PrettyPrinter`, or move this to `ty::print`. - fn write_region_name<'tcx>( - r: ty::Region<'tcx>, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - match r { - ty::ReLateBound(index, br) => match br { - ty::BoundRegion::BrNamed(_, name) => write!(fmt, "{}", name), - ty::BoundRegion::BrAnon(var) => { - if *index == ty::INNERMOST { - write!(fmt, "'^{}", var) - } else { - write!(fmt, "'^{}_{}", index.index(), var) - } - } - _ => write!(fmt, "'_"), - }, - - _ => write!(fmt, "{}", r), - } - } - - match self { - Implemented(trait_ref) => write!(fmt, "Implemented({})", trait_ref), - ProjectionEq(projection) => write!(fmt, "ProjectionEq({})", projection), - RegionOutlives(predicate) => { - write!(fmt, "RegionOutlives({}: ", predicate.0)?; - write_region_name(predicate.1, fmt)?; - write!(fmt, ")") - } - TypeOutlives(predicate) => { - write!(fmt, "TypeOutlives({}: ", predicate.0)?; - write_region_name(predicate.1, fmt)?; - write!(fmt, ")") - } - } - } -} - -impl<'tcx> fmt::Display for traits::WellFormed<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::traits::WellFormed::*; - - match self { - Trait(trait_ref) => write!(fmt, "WellFormed({})", trait_ref), - Ty(ty) => write!(fmt, "WellFormed({})", ty), - } - } -} - -impl<'tcx> fmt::Display for traits::FromEnv<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::traits::FromEnv::*; - - match self { - Trait(trait_ref) => write!(fmt, "FromEnv({})", trait_ref), - Ty(ty) => write!(fmt, "FromEnv({})", ty), - } - } -} - -impl<'tcx> fmt::Display for traits::DomainGoal<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::traits::DomainGoal::*; - - match self { - Holds(wc) => write!(fmt, "{}", wc), - WellFormed(wf) => write!(fmt, "{}", wf), - FromEnv(from_env) => write!(fmt, "{}", from_env), - Normalize(projection) => { - write!(fmt, "Normalize({} -> {})", projection.projection_ty, projection.ty) - } - } - } -} - -impl fmt::Display for traits::QuantifierKind { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::traits::QuantifierKind::*; - - match self { - Universal => write!(fmt, "forall"), - Existential => write!(fmt, "exists"), - } - } -} - -/// Collect names for regions / types bound by a quantified goal / clause. -/// This collector does not try to do anything clever like in `ty::print`, it's just used -/// for debug output in tests anyway. -struct BoundNamesCollector { - // Just sort by name because `BoundRegion::BrNamed` does not have a `BoundVar` index anyway. - regions: BTreeSet, - - // Sort by `BoundVar` index, so usually this should be equivalent to the order given - // by the list of type parameters. - types: BTreeMap, - - binder_index: ty::DebruijnIndex, -} - -impl BoundNamesCollector { - fn new() -> Self { - BoundNamesCollector { - regions: BTreeSet::new(), - types: BTreeMap::new(), - binder_index: ty::INNERMOST, - } - } - - fn is_empty(&self) -> bool { - self.regions.is_empty() && self.types.is_empty() - } - - fn write_names(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut start = true; - for r in &self.regions { - if !start { - write!(fmt, ", ")?; - } - start = false; - write!(fmt, "{}", r)?; - } - for t in self.types.values() { - if !start { - write!(fmt, ", ")?; - } - start = false; - write!(fmt, "{}", t)?; - } - Ok(()) - } -} - -impl<'tcx> TypeVisitor<'tcx> for BoundNamesCollector { - fn visit_binder>(&mut self, t: &ty::Binder) -> bool { - self.binder_index.shift_in(1); - let result = t.super_visit_with(self); - self.binder_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { - ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { - self.types.insert( - bound_ty.var.as_u32(), - match bound_ty.kind { - ty::BoundTyKind::Param(name) => name, - ty::BoundTyKind::Anon => { - Symbol::intern(&format!("^{}", bound_ty.var.as_u32())) - } - }, - ); - } - - _ => (), - }; - - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - match r { - ty::ReLateBound(index, br) if *index == self.binder_index => match br { - ty::BoundRegion::BrNamed(_, name) => { - self.regions.insert(*name); - } - - ty::BoundRegion::BrAnon(var) => { - self.regions.insert(Symbol::intern(&format!("'^{}", var))); - } - - _ => (), - }, - - _ => (), - }; - - r.super_visit_with(self) - } -} - -impl<'tcx> fmt::Display for traits::Goal<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::traits::GoalKind::*; - - match self { - Implies(hypotheses, goal) => { - write!(fmt, "if (")?; - for (index, hyp) in hypotheses.iter().enumerate() { - if index > 0 { - write!(fmt, ", ")?; - } - write!(fmt, "{}", hyp)?; - } - write!(fmt, ") {{ {} }}", goal) - } - And(goal1, goal2) => write!(fmt, "({} && {})", goal1, goal2), - Not(goal) => write!(fmt, "not {{ {} }}", goal), - DomainGoal(goal) => write!(fmt, "{}", goal), - Quantified(qkind, goal) => { - let mut collector = BoundNamesCollector::new(); - goal.skip_binder().visit_with(&mut collector); - - if !collector.is_empty() { - write!(fmt, "{}<", qkind)?; - collector.write_names(fmt)?; - write!(fmt, "> {{ ")?; - } - - write!(fmt, "{}", goal.skip_binder())?; - - if !collector.is_empty() { - write!(fmt, " }}")?; - } - - Ok(()) - } - Subtype(a, b) => write!(fmt, "{} <: {}", a, b), - CannotProve => write!(fmt, "CannotProve"), - } - } -} - -impl<'tcx> fmt::Display for traits::ProgramClause<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let traits::ProgramClause { goal, hypotheses, .. } = self; - write!(fmt, "{}", goal)?; - if !hypotheses.is_empty() { - write!(fmt, " :- ")?; - for (index, condition) in hypotheses.iter().enumerate() { - if index > 0 { - write!(fmt, ", ")?; - } - write!(fmt, "{}", condition)?; - } - } - write!(fmt, ".") - } -} - -impl<'tcx> fmt::Display for traits::Clause<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::traits::Clause::*; - - match self { - Implies(clause) => write!(fmt, "{}", clause), - ForAll(clause) => { - let mut collector = BoundNamesCollector::new(); - clause.skip_binder().visit_with(&mut collector); - - if !collector.is_empty() { - write!(fmt, "forall<")?; - collector.write_names(fmt)?; - write!(fmt, "> {{ ")?; - } - - write!(fmt, "{}", clause.skip_binder())?; - - if !collector.is_empty() { - write!(fmt, " }}")?; - } - - Ok(()) - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Lift implementations - -impl<'a, 'tcx> Lift<'tcx> for traits::SelectionError<'a> { - type Lifted = traits::SelectionError<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - super::Unimplemented => Some(super::Unimplemented), - super::OutputTypeParameterMismatch(a, b, ref err) => { - tcx.lift(&(a, b)).and_then(|(a, b)| { - tcx.lift(err).map(|err| super::OutputTypeParameterMismatch(a, b, err)) - }) - } - super::TraitNotObjectSafe(def_id) => Some(super::TraitNotObjectSafe(def_id)), - super::ConstEvalFailure(err) => Some(super::ConstEvalFailure(err)), - super::Overflow => Some(super::Overflow), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { - type Lifted = traits::ObligationCauseCode<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - super::ReturnNoExpression => Some(super::ReturnNoExpression), - super::MiscObligation => Some(super::MiscObligation), - super::SliceOrArrayElem => Some(super::SliceOrArrayElem), - super::TupleElem => Some(super::TupleElem), - super::ProjectionWf(proj) => tcx.lift(&proj).map(super::ProjectionWf), - super::ItemObligation(def_id) => Some(super::ItemObligation(def_id)), - super::BindingObligation(def_id, span) => Some(super::BindingObligation(def_id, span)), - super::ReferenceOutlivesReferent(ty) => { - tcx.lift(&ty).map(super::ReferenceOutlivesReferent) - } - super::ObjectTypeBound(ty, r) => { - tcx.lift(&ty).and_then(|ty| tcx.lift(&r).map(|r| super::ObjectTypeBound(ty, r))) - } - super::ObjectCastObligation(ty) => tcx.lift(&ty).map(super::ObjectCastObligation), - super::Coercion { source, target } => { - Some(super::Coercion { source: tcx.lift(&source)?, target: tcx.lift(&target)? }) - } - super::AssignmentLhsSized => Some(super::AssignmentLhsSized), - super::TupleInitializerSized => Some(super::TupleInitializerSized), - super::StructInitializerSized => Some(super::StructInitializerSized), - super::VariableType(id) => Some(super::VariableType(id)), - super::ReturnValue(id) => Some(super::ReturnValue(id)), - super::ReturnType => Some(super::ReturnType), - super::SizedArgumentType => Some(super::SizedArgumentType), - super::SizedReturnType => Some(super::SizedReturnType), - super::SizedYieldType => Some(super::SizedYieldType), - super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)), - super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }), - super::ConstSized => Some(super::ConstSized), - super::ConstPatternStructural => Some(super::ConstPatternStructural), - super::SharedStatic => Some(super::SharedStatic), - super::BuiltinDerivedObligation(ref cause) => { - tcx.lift(cause).map(super::BuiltinDerivedObligation) - } - super::ImplDerivedObligation(ref cause) => { - tcx.lift(cause).map(super::ImplDerivedObligation) - } - super::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } => Some(super::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - }), - super::CompareImplTypeObligation { item_name, impl_item_def_id, trait_item_def_id } => { - Some(super::CompareImplTypeObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - }) - } - super::ExprAssignable => Some(super::ExprAssignable), - super::MatchExpressionArm(box super::MatchExpressionArmCause { - arm_span, - source, - ref prior_arms, - last_ty, - scrut_hir_id, - }) => tcx.lift(&last_ty).map(|last_ty| { - super::MatchExpressionArm(box super::MatchExpressionArmCause { - arm_span, - source, - prior_arms: prior_arms.clone(), - last_ty, - scrut_hir_id, - }) - }), - super::Pattern { span, root_ty, origin_expr } => { - tcx.lift(&root_ty).map(|root_ty| super::Pattern { span, root_ty, origin_expr }) - } - super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }) => { - Some(super::IfExpression(box super::IfExpressionCause { then, outer, semicolon })) - } - super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse), - super::MainFunctionType => Some(super::MainFunctionType), - super::StartFunctionType => Some(super::StartFunctionType), - super::IntrinsicType => Some(super::IntrinsicType), - super::MethodReceiver => Some(super::MethodReceiver), - super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)), - super::TrivialBound => Some(super::TrivialBound), - super::AssocTypeBound(ref data) => Some(super::AssocTypeBound(data.clone())), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for traits::DerivedObligationCause<'a> { - type Lifted = traits::DerivedObligationCause<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.parent_trait_ref).and_then(|trait_ref| { - tcx.lift(&*self.parent_code).map(|code| traits::DerivedObligationCause { - parent_trait_ref: trait_ref, - parent_code: Rc::new(code), - }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCause<'a> { - type Lifted = traits::ObligationCause<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.code).map(|code| traits::ObligationCause { - span: self.span, - body_id: self.body_id, - code, - }) - } -} - -// For codegen only. -impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { - type Lifted = traits::Vtable<'tcx, ()>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match self.clone() { - traits::VtableImpl(traits::VtableImplData { impl_def_id, substs, nested }) => { - tcx.lift(&substs).map(|substs| { - traits::VtableImpl(traits::VtableImplData { impl_def_id, substs, nested }) - }) - } - traits::VtableAutoImpl(t) => Some(traits::VtableAutoImpl(t)), - traits::VtableGenerator(traits::VtableGeneratorData { - generator_def_id, - substs, - nested, - }) => tcx.lift(&substs).map(|substs| { - traits::VtableGenerator(traits::VtableGeneratorData { - generator_def_id, - substs, - nested, - }) - }), - traits::VtableClosure(traits::VtableClosureData { closure_def_id, substs, nested }) => { - tcx.lift(&substs).map(|substs| { - traits::VtableClosure(traits::VtableClosureData { - closure_def_id, - substs, - nested, - }) - }) - } - traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, nested }) => { - tcx.lift(&fn_ty).map(|fn_ty| { - traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, nested }) - }) - } - traits::VtableParam(n) => Some(traits::VtableParam(n)), - traits::VtableBuiltin(n) => Some(traits::VtableBuiltin(n)), - traits::VtableObject(traits::VtableObjectData { - upcast_trait_ref, - vtable_base, - nested, - }) => tcx.lift(&upcast_trait_ref).map(|trait_ref| { - traits::VtableObject(traits::VtableObjectData { - upcast_trait_ref: trait_ref, - vtable_base, - nested, - }) - }), - traits::VtableTraitAlias(traits::VtableTraitAliasData { - alias_def_id, - substs, - nested, - }) => tcx.lift(&substs).map(|substs| { - traits::VtableTraitAlias(traits::VtableTraitAliasData { - alias_def_id, - substs, - nested, - }) - }), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for traits::Environment<'a> { - type Lifted = traits::Environment<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.clauses).map(|clauses| traits::Environment { clauses }) - } -} - -impl<'a, 'tcx, G: Lift<'tcx>> Lift<'tcx> for traits::InEnvironment<'a, G> { - type Lifted = traits::InEnvironment<'tcx, G::Lifted>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.environment).and_then(|environment| { - tcx.lift(&self.goal).map(|goal| traits::InEnvironment { environment, goal }) - }) - } -} - -/////////////////////////////////////////////////////////////////////////// -// TypeFoldable implementations. - -CloneTypeFoldableAndLiftImpls! { - traits::QuantifierKind, -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let v = self.iter().map(|t| t.fold_with(folder)).collect::>(); - folder.tcx().intern_goals(&v) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for traits::Goal<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let v = (**self).fold_with(folder); - folder.tcx().mk_goal(v) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - (**self).visit_with(visitor) - } -} - -CloneTypeFoldableAndLiftImpls! { - traits::ProgramClauseCategory, -} - -impl<'tcx> TypeFoldable<'tcx> for traits::Clauses<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let v = self.iter().map(|t| t.fold_with(folder)).collect::>(); - folder.tcx().intern_clauses(&v) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} diff --git a/src/librustc/ty/diagnostics.rs b/src/librustc/ty/diagnostics.rs deleted file mode 100644 index d1eb21e25ffaf..0000000000000 --- a/src/librustc/ty/diagnostics.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Diagnostics related methods for `TyS`. - -use crate::ty::sty::InferTy; -use crate::ty::TyKind::*; -use crate::ty::TyS; - -impl<'tcx> TyS<'tcx> { - /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. - pub fn is_primitive_ty(&self) -> bool { - match self.kind { - Bool - | Char - | Str - | Int(_) - | Uint(_) - | Float(_) - | Infer(InferTy::IntVar(_)) - | Infer(InferTy::FloatVar(_)) - | Infer(InferTy::FreshIntTy(_)) - | Infer(InferTy::FreshFloatTy(_)) => true, - _ => false, - } - } - - /// Whether the type is succinctly representable as a type instead of just referred to with a - /// description in error messages. This is used in the main error message. - pub fn is_simple_ty(&self) -> bool { - match self.kind { - Bool - | Char - | Str - | Int(_) - | Uint(_) - | Float(_) - | Infer(InferTy::IntVar(_)) - | Infer(InferTy::FloatVar(_)) - | Infer(InferTy::FreshIntTy(_)) - | Infer(InferTy::FreshFloatTy(_)) => true, - Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(), - Tuple(tys) if tys.is_empty() => true, - _ => false, - } - } - - /// Whether the type is succinctly representable as a type instead of just referred to with a - /// description in error messages. This is used in the primary span label. Beyond what - /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to - /// ADTs with no type arguments. - pub fn is_simple_text(&self) -> bool { - match self.kind { - Adt(_, substs) => substs.types().next().is_none(), - Ref(_, ty, _) => ty.is_simple_text(), - _ => self.is_simple_ty(), - } - } - - /// Whether the type can be safely suggested during error recovery. - pub fn is_suggestable(&self) -> bool { - match self.kind { - Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) - | Projection(..) => false, - _ => true, - } - } -} diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs deleted file mode 100644 index d0bc0d5fabfae..0000000000000 --- a/src/librustc/ty/error.rs +++ /dev/null @@ -1,495 +0,0 @@ -use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt}; -use rustc_ast::ast; -use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::Span; -use rustc_target::spec::abi; - -use std::borrow::Cow; -use std::fmt; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] -pub struct ExpectedFound { - pub expected: T, - pub found: T, -} - -impl ExpectedFound { - pub fn new(a_is_expected: bool, a: T, b: T) -> Self { - if a_is_expected { - ExpectedFound { expected: a, found: b } - } else { - ExpectedFound { expected: b, found: a } - } - } -} - -// Data structures used in type unification -#[derive(Clone, Debug, TypeFoldable)] -pub enum TypeError<'tcx> { - Mismatch, - UnsafetyMismatch(ExpectedFound), - AbiMismatch(ExpectedFound), - Mutability, - TupleSize(ExpectedFound), - FixedArraySize(ExpectedFound), - ArgCount, - - RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), - RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>), - RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>), - RegionsPlaceholderMismatch, - - Sorts(ExpectedFound>), - IntMismatch(ExpectedFound), - FloatMismatch(ExpectedFound), - Traits(ExpectedFound), - VariadicMismatch(ExpectedFound), - - /// Instantiating a type variable with the given type would have - /// created a cycle (because it appears somewhere within that - /// type). - CyclicTy(Ty<'tcx>), - ProjectionMismatched(ExpectedFound), - ProjectionBoundsLength(ExpectedFound), - ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), - ObjectUnsafeCoercion(DefId), - ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), - - IntrinsicCast, -} - -pub enum UnconstrainedNumeric { - UnconstrainedFloat, - UnconstrainedInt, - Neither, -} - -/// Explains the source of a type err in a short, human readable way. This is meant to be placed -/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` -/// afterwards to present additional details, particularly when it comes to lifetime-related -/// errors. -impl<'tcx> fmt::Display for TypeError<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use self::TypeError::*; - fn report_maybe_different( - f: &mut fmt::Formatter<'_>, - expected: &str, - found: &str, - ) -> fmt::Result { - // A naive approach to making sure that we're not reporting silly errors such as: - // (expected closure, found closure). - if expected == found { - write!(f, "expected {}, found a different {}", expected, found) - } else { - write!(f, "expected {}, found {}", expected, found) - } - } - - let br_string = |br: ty::BoundRegion| match br { - ty::BrNamed(_, name) => format!(" {}", name), - _ => String::new(), - }; - - match *self { - CyclicTy(_) => write!(f, "cyclic type of infinite size"), - Mismatch => write!(f, "types differ"), - UnsafetyMismatch(values) => { - write!(f, "expected {} fn, found {} fn", values.expected, values.found) - } - AbiMismatch(values) => { - write!(f, "expected {} fn, found {} fn", values.expected, values.found) - } - Mutability => write!(f, "types differ in mutability"), - TupleSize(values) => write!( - f, - "expected a tuple with {} element{}, \ - found one with {} element{}", - values.expected, - pluralize!(values.expected), - values.found, - pluralize!(values.found) - ), - FixedArraySize(values) => write!( - f, - "expected an array with a fixed size of {} element{}, \ - found one with {} element{}", - values.expected, - pluralize!(values.expected), - values.found, - pluralize!(values.found) - ), - ArgCount => write!(f, "incorrect number of function parameters"), - RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), - RegionsInsufficientlyPolymorphic(br, _) => write!( - f, - "expected bound lifetime parameter{}, found concrete lifetime", - br_string(br) - ), - RegionsOverlyPolymorphic(br, _) => write!( - f, - "expected concrete lifetime, found bound lifetime parameter{}", - br_string(br) - ), - RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), - Sorts(values) => ty::tls::with(|tcx| { - report_maybe_different( - f, - &values.expected.sort_string(tcx), - &values.found.sort_string(tcx), - ) - }), - Traits(values) => ty::tls::with(|tcx| { - report_maybe_different( - f, - &format!("trait `{}`", tcx.def_path_str(values.expected)), - &format!("trait `{}`", tcx.def_path_str(values.found)), - ) - }), - IntMismatch(ref values) => { - write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) - } - FloatMismatch(ref values) => { - write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) - } - VariadicMismatch(ref values) => write!( - f, - "expected {} fn, found {} function", - if values.expected { "variadic" } else { "non-variadic" }, - if values.found { "variadic" } else { "non-variadic" } - ), - ProjectionMismatched(ref values) => ty::tls::with(|tcx| { - write!( - f, - "expected {}, found {}", - tcx.def_path_str(values.expected), - tcx.def_path_str(values.found) - ) - }), - ProjectionBoundsLength(ref values) => write!( - f, - "expected {} associated type binding{}, found {}", - values.expected, - pluralize!(values.expected), - values.found - ), - ExistentialMismatch(ref values) => report_maybe_different( - f, - &format!("trait `{}`", values.expected), - &format!("trait `{}`", values.found), - ), - ConstMismatch(ref values) => { - write!(f, "expected `{}`, found `{}`", values.expected, values.found) - } - IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"), - ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"), - } - } -} - -impl<'tcx> TypeError<'tcx> { - pub fn must_include_note(&self) -> bool { - use self::TypeError::*; - match self { - CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) - | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) => false, - - Mutability - | TupleSize(_) - | ArgCount - | RegionsDoesNotOutlive(..) - | RegionsInsufficientlyPolymorphic(..) - | RegionsOverlyPolymorphic(..) - | RegionsPlaceholderMismatch - | Traits(_) - | ProjectionMismatched(_) - | ProjectionBoundsLength(_) - | ExistentialMismatch(_) - | ConstMismatch(_) - | IntrinsicCast - | ObjectUnsafeCoercion(_) => true, - } - } -} - -impl<'tcx> ty::TyS<'tcx> { - pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { - match self.kind { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { - format!("`{}`", self).into() - } - ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(), - - ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), - ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), - ty::Array(t, n) => { - let n = tcx.lift(&n).unwrap(); - match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { - _ if t.is_simple_ty() => format!("array `{}`", self).into(), - Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(), - None => "array".into(), - } - } - ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(), - ty::Slice(_) => "slice".into(), - ty::RawPtr(_) => "*-ptr".into(), - ty::Ref(_, ty, mutbl) => { - let tymut = ty::TypeAndMut { ty, mutbl }; - let tymut_string = tymut.to_string(); - if tymut_string != "_" - && (ty.is_simple_text() || tymut_string.len() < "mutable reference".len()) - { - format!("`&{}`", tymut_string).into() - } else { - // Unknown type name, it's long or has type arguments - match mutbl { - hir::Mutability::Mut => "mutable reference", - _ => "reference", - } - .into() - } - } - ty::FnDef(..) => "fn item".into(), - ty::FnPtr(_) => "fn pointer".into(), - ty::Dynamic(ref inner, ..) => { - if let Some(principal) = inner.principal() { - format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() - } else { - "trait object".into() - } - } - ty::Closure(..) => "closure".into(), - ty::Generator(..) => "generator".into(), - ty::GeneratorWitness(..) => "generator witness".into(), - ty::Tuple(..) => "tuple".into(), - ty::Infer(ty::TyVar(_)) => "inferred type".into(), - ty::Infer(ty::IntVar(_)) => "integer".into(), - ty::Infer(ty::FloatVar(_)) => "floating-point number".into(), - ty::Placeholder(..) => "placeholder type".into(), - ty::Bound(..) => "bound type".into(), - ty::Infer(ty::FreshTy(_)) => "fresh type".into(), - ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), - ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), - ty::Projection(_) => "associated type".into(), - ty::UnnormalizedProjection(_) => "non-normalized associated type".into(), - ty::Param(p) => format!("type parameter `{}`", p).into(), - ty::Opaque(..) => "opaque type".into(), - ty::Error => "type error".into(), - } - } - - pub fn prefix_string(&self) -> Cow<'static, str> { - match self.kind { - ty::Infer(_) - | ty::Error - | ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Never => "type".into(), - ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(), - ty::Adt(def, _) => def.descr().into(), - ty::Foreign(_) => "extern type".into(), - ty::Array(..) => "array".into(), - ty::Slice(_) => "slice".into(), - ty::RawPtr(_) => "raw pointer".into(), - ty::Ref(.., mutbl) => match mutbl { - hir::Mutability::Mut => "mutable reference", - _ => "reference", - } - .into(), - ty::FnDef(..) => "fn item".into(), - ty::FnPtr(_) => "fn pointer".into(), - ty::Dynamic(..) => "trait object".into(), - ty::Closure(..) => "closure".into(), - ty::Generator(..) => "generator".into(), - ty::GeneratorWitness(..) => "generator witness".into(), - ty::Tuple(..) => "tuple".into(), - ty::Placeholder(..) => "higher-ranked type".into(), - ty::Bound(..) => "bound type variable".into(), - ty::Projection(_) => "associated type".into(), - ty::UnnormalizedProjection(_) => "associated type".into(), - ty::Param(_) => "type parameter".into(), - ty::Opaque(..) => "opaque type".into(), - } - } -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn note_and_explain_type_err( - self, - db: &mut DiagnosticBuilder<'_>, - err: &TypeError<'tcx>, - sp: Span, - body_owner_def_id: DefId, - ) { - use self::TypeError::*; - - match err { - Sorts(values) => { - let expected_str = values.expected.sort_string(self); - let found_str = values.found.sort_string(self); - if expected_str == found_str && expected_str == "closure" { - db.note("no two closures, even if identical, have the same type"); - db.help("consider boxing your closure and/or using it as a trait object"); - } - if expected_str == found_str && expected_str == "opaque type" { - // Issue #63167 - db.note("distinct uses of `impl Trait` result in different opaque types"); - let e_str = values.expected.to_string(); - let f_str = values.found.to_string(); - if e_str == f_str && &e_str == "impl std::future::Future" { - // FIXME: use non-string based check. - db.help( - "if both `Future`s have the same `Output` type, consider \ - `.await`ing on both of them", - ); - } - } - match (&values.expected.kind, &values.found.kind) { - (ty::Float(_), ty::Infer(ty::IntVar(_))) => { - if let Ok( - // Issue #53280 - snippet, - ) = self.sess.source_map().span_to_snippet(sp) - { - if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { - db.span_suggestion( - sp, - "use a float literal", - format!("{}.0", snippet), - Applicability::MachineApplicable, - ); - } - } - } - (ty::Param(expected), ty::Param(found)) => { - let generics = self.generics_of(body_owner_def_id); - let e_span = self.def_span(generics.type_param(expected, self).def_id); - if !sp.contains(e_span) { - db.span_label(e_span, "expected type parameter"); - } - let f_span = self.def_span(generics.type_param(found, self).def_id); - if !sp.contains(f_span) { - db.span_label(f_span, "found type parameter"); - } - db.note( - "a type parameter was expected, but a different one was found; \ - you might be missing a type parameter or trait bound", - ); - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - (ty::Projection(_), ty::Projection(_)) => { - db.note("an associated type was expected, but a different one was found"); - } - (ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => { - db.note("you might be missing a type parameter or trait bound"); - } - (ty::Param(p), _) | (_, ty::Param(p)) => { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - db.span_label(p_span, "this type parameter"); - } - db.help("type parameters must be constrained to match other types"); - if self.sess.teach(&db.get_code().unwrap()) { - db.help( - "given a type parameter `T` and a method `foo`: -``` -trait Trait { fn foo(&self) -> T; } -``` -the only ways to implement method `foo` are: -- constrain `T` with an explicit type: -``` -impl Trait for X { - fn foo(&self) -> String { String::new() } -} -``` -- add a trait bound to `T` and call a method on that trait that returns `Self`: -``` -impl Trait for X { - fn foo(&self) -> T { ::default() } -} -``` -- change `foo` to return an argument of type `T`: -``` -impl Trait for X { - fn foo(&self, x: T) -> T { x } -} -```", - ); - } - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - (ty::Projection(_), _) => { - db.note(&format!( - "consider constraining the associated type `{}` to `{}` or calling a \ - method that returns `{}`", - values.expected, values.found, values.expected, - )); - if self.sess.teach(&db.get_code().unwrap()) { - db.help( - "given an associated type `T` and a method `foo`: -``` -trait Trait { - type T; - fn foo(&self) -> Self::T; -} -``` -the only way of implementing method `foo` is to constrain `T` with an explicit associated type: -``` -impl Trait for X { - type T = String; - fn foo(&self) -> Self::T { String::new() } -} -```", - ); - } - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - (_, ty::Projection(_)) => { - db.note(&format!( - "consider constraining the associated type `{}` to `{}`", - values.found, values.expected, - )); - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - _ => {} - } - debug!( - "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", - values.expected, values.expected.kind, values.found, values.found.kind, - ); - } - CyclicTy(ty) => { - // Watch out for various cases of cyclic types and try to explain. - if ty.is_closure() || ty.is_generator() { - db.note( - "closures cannot capture themselves or take themselves as argument;\n\ - this error may be the result of a recent compiler bug-fix,\n\ - see issue #46062 \n\ - for more information", - ); - } - } - _ => {} - } - } -} diff --git a/src/librustc/ty/free_region_map.rs b/src/librustc/ty/free_region_map.rs deleted file mode 100644 index 2ab12a4acbfa4..0000000000000 --- a/src/librustc/ty/free_region_map.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::ty::{self, Lift, Region, TyCtxt}; -use rustc_data_structures::transitive_relation::TransitiveRelation; - -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default, HashStable)] -pub struct FreeRegionMap<'tcx> { - // Stores the relation `a < b`, where `a` and `b` are regions. - // - // Invariant: only free regions like `'x` or `'static` are stored - // in this relation, not scopes. - relation: TransitiveRelation>, -} - -impl<'tcx> FreeRegionMap<'tcx> { - pub fn elements(&self) -> impl Iterator> { - self.relation.elements() - } - - pub fn is_empty(&self) -> bool { - self.relation.is_empty() - } - - // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. - // (with the exception that `'static: 'x` is not notable) - pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { - debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); - if self.is_free_or_static(sub) && self.is_free(sup) { - self.relation.add(sub, sup) - } - } - - /// Tests whether `r_a <= r_b`. - /// - /// Both regions must meet `is_free_or_static`. - /// - /// Subtle: one tricky case that this code gets correct is as - /// follows. If we know that `r_b: 'static`, then this function - /// will return true, even though we don't know anything that - /// directly relates `r_a` and `r_b`. - /// - /// Also available through the `FreeRegionRelations` trait below. - pub fn sub_free_regions( - &self, - tcx: TyCtxt<'tcx>, - r_a: Region<'tcx>, - r_b: Region<'tcx>, - ) -> bool { - assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b)); - let re_static = tcx.lifetimes.re_static; - if self.check_relation(re_static, r_b) { - // `'a <= 'static` is always true, and not stored in the - // relation explicitly, so check if `'b` is `'static` (or - // equivalent to it) - true - } else { - self.check_relation(r_a, r_b) - } - } - - /// Check whether `r_a <= r_b` is found in the relation. - fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { - r_a == r_b || self.relation.contains(&r_a, &r_b) - } - - /// True for free regions other than `'static`. - pub fn is_free(&self, r: Region<'_>) -> bool { - match *r { - ty::ReEarlyBound(_) | ty::ReFree(_) => true, - _ => false, - } - } - - /// True if `r` is a free region or static of the sort that this - /// free region map can be used with. - pub fn is_free_or_static(&self, r: Region<'_>) -> bool { - match *r { - ty::ReStatic => true, - _ => self.is_free(r), - } - } - - /// Computes the least-upper-bound of two free regions. In some - /// cases, this is more conservative than necessary, in order to - /// avoid making arbitrary choices. See - /// `TransitiveRelation::postdom_upper_bound` for more details. - pub fn lub_free_regions( - &self, - tcx: TyCtxt<'tcx>, - r_a: Region<'tcx>, - r_b: Region<'tcx>, - ) -> Region<'tcx> { - debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); - assert!(self.is_free(r_a)); - assert!(self.is_free(r_b)); - let result = if r_a == r_b { - r_a - } else { - match self.relation.postdom_upper_bound(&r_a, &r_b) { - None => tcx.lifetimes.re_static, - Some(r) => *r, - } - }; - debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); - result - } -} - -/// The NLL region handling code represents free region relations in a -/// slightly different way; this trait allows functions to be abstract -/// over which version is in use. -pub trait FreeRegionRelations<'tcx> { - /// Tests whether `r_a <= r_b`. Both must be free regions or - /// `'static`. - fn sub_free_regions( - &self, - tcx: TyCtxt<'tcx>, - shorter: ty::Region<'tcx>, - longer: ty::Region<'tcx>, - ) -> bool; -} - -impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> { - fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { - // invoke the "inherent method" - self.sub_free_regions(tcx, r_a, r_b) - } -} - -impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { - type Lifted = FreeRegionMap<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - self.relation.maybe_map(|&fr| tcx.lift(&fr)).map(|relation| FreeRegionMap { relation }) - } -} diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs deleted file mode 100644 index b166c4dea0c85..0000000000000 --- a/src/librustc/ty/inhabitedness/mod.rs +++ /dev/null @@ -1,225 +0,0 @@ -pub use self::def_id_forest::DefIdForest; - -use crate::ty; -use crate::ty::context::TyCtxt; -use crate::ty::TyKind::*; -use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef}; -use crate::ty::{AdtKind, Visibility}; -use crate::ty::{DefId, SubstsRef}; - -mod def_id_forest; - -// The methods in this module calculate `DefIdForest`s of modules in which a -// `AdtDef`/`VariantDef`/`FieldDef` is visibly uninhabited. -// -// # Example -// ```rust -// enum Void {} -// mod a { -// pub mod b { -// pub struct SecretlyUninhabited { -// _priv: !, -// } -// } -// } -// -// mod c { -// pub struct AlsoSecretlyUninhabited { -// _priv: Void, -// } -// mod d { -// } -// } -// -// struct Foo { -// x: a::b::SecretlyUninhabited, -// y: c::AlsoSecretlyUninhabited, -// } -// ``` -// In this code, the type `Foo` will only be visibly uninhabited inside the -// modules `b`, `c` and `d`. Calling `uninhabited_from` on `Foo` or its `AdtDef` will -// return the forest of modules {`b`, `c`->`d`} (represented in a `DefIdForest` by the -// set {`b`, `c`}). -// -// We need this information for pattern-matching on `Foo` or types that contain -// `Foo`. -// -// # Example -// ```rust -// let foo_result: Result = ... ; -// let Ok(t) = foo_result; -// ``` -// This code should only compile in modules where the uninhabitedness of `Foo` is -// visible. - -impl<'tcx> TyCtxt<'tcx> { - /// Checks whether a type is visibly uninhabited from a particular module. - /// - /// # Example - /// ```rust - /// enum Void {} - /// mod a { - /// pub mod b { - /// pub struct SecretlyUninhabited { - /// _priv: !, - /// } - /// } - /// } - /// - /// mod c { - /// pub struct AlsoSecretlyUninhabited { - /// _priv: Void, - /// } - /// mod d { - /// } - /// } - /// - /// struct Foo { - /// x: a::b::SecretlyUninhabited, - /// y: c::AlsoSecretlyUninhabited, - /// } - /// ``` - /// In this code, the type `Foo` will only be visibly uninhabited inside the - /// modules b, c and d. This effects pattern-matching on `Foo` or types that - /// contain `Foo`. - /// - /// # Example - /// ```rust - /// let foo_result: Result = ... ; - /// let Ok(t) = foo_result; - /// ``` - /// This code should only compile in modules where the uninhabitedness of Foo is - /// visible. - pub fn is_ty_uninhabited_from( - self, - module: DefId, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - // To check whether this type is uninhabited at all (not just from the - // given node), you could check whether the forest is empty. - // ``` - // forest.is_empty() - // ``` - ty.uninhabited_from(self, param_env).contains(self, module) - } - - pub fn is_ty_uninhabited_from_any_module( - self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - !ty.uninhabited_from(self, param_env).is_empty() - } -} - -impl<'tcx> AdtDef { - /// Calculates the forest of `DefId`s from which this ADT is visibly uninhabited. - fn uninhabited_from( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { - // Non-exhaustive ADTs from other crates are always considered inhabited. - if self.is_variant_list_non_exhaustive() && !self.did.is_local() { - DefIdForest::empty() - } else { - DefIdForest::intersection( - tcx, - self.variants - .iter() - .map(|v| v.uninhabited_from(tcx, substs, self.adt_kind(), param_env)), - ) - } - } -} - -impl<'tcx> VariantDef { - /// Calculates the forest of `DefId`s from which this variant is visibly uninhabited. - pub fn uninhabited_from( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - adt_kind: AdtKind, - param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { - let is_enum = match adt_kind { - // For now, `union`s are never considered uninhabited. - // The precise semantics of inhabitedness with respect to unions is currently undecided. - AdtKind::Union => return DefIdForest::empty(), - AdtKind::Enum => true, - AdtKind::Struct => false, - }; - // Non-exhaustive variants from other crates are always considered inhabited. - if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { - DefIdForest::empty() - } else { - DefIdForest::union( - tcx, - self.fields.iter().map(|f| f.uninhabited_from(tcx, substs, is_enum, param_env)), - ) - } - } -} - -impl<'tcx> FieldDef { - /// Calculates the forest of `DefId`s from which this field is visibly uninhabited. - fn uninhabited_from( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - is_enum: bool, - param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { - let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx, param_env); - // FIXME(canndrew): Currently enum fields are (incorrectly) stored with - // `Visibility::Invisible` so we need to override `self.vis` if we're - // dealing with an enum. - if is_enum { - data_uninhabitedness() - } else { - match self.vis { - Visibility::Invisible => DefIdForest::empty(), - Visibility::Restricted(from) => { - let forest = DefIdForest::from_id(from); - let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness())); - DefIdForest::intersection(tcx, iter) - } - Visibility::Public => data_uninhabitedness(), - } - } - } -} - -impl<'tcx> TyS<'tcx> { - /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. - fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest { - match self.kind { - Adt(def, substs) => def.uninhabited_from(tcx, substs, param_env), - - Never => DefIdForest::full(tcx), - - Tuple(ref tys) => DefIdForest::union( - tcx, - tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)), - ), - - Array(ty, len) => match len.try_eval_usize(tcx, param_env) { - // If the array is definitely non-empty, it's uninhabited if - // the type of its elements is uninhabited. - Some(n) if n != 0 => ty.uninhabited_from(tcx, param_env), - _ => DefIdForest::empty(), - }, - - // References to uninitialised memory is valid for any type, including - // uninhabited types, in unsafe code, so we treat all references as - // inhabited. - // The precise semantics of inhabitedness with respect to references is currently - // undecided. - Ref(..) => DefIdForest::empty(), - - _ => DefIdForest::empty(), - } - } -} diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs deleted file mode 100644 index c54360e03933e..0000000000000 --- a/src/librustc/ty/layout.rs +++ /dev/null @@ -1,2718 +0,0 @@ -use crate::ich::StableHashingContext; -use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; -use crate::ty::subst::Subst; -use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable}; - -use rustc_ast::ast::{self, Ident, IntTy, UintTy}; -use rustc_attr as attr; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir as hir; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use rustc_span::DUMMY_SP; -use rustc_target::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, Conv, FnAbi, PassMode, Reg, RegKind, -}; -pub use rustc_target::abi::*; -use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec}; - -use std::cmp; -use std::fmt; -use std::iter; -use std::mem; -use std::ops::Bound; - -pub trait IntegerExt { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>; - fn from_attr(cx: &C, ity: attr::IntType) -> Integer; - fn repr_discr<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - repr: &ReprOptions, - min: i128, - max: i128, - ) -> (Integer, bool); -} - -impl IntegerExt for Integer { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> { - match (*self, signed) { - (I8, false) => tcx.types.u8, - (I16, false) => tcx.types.u16, - (I32, false) => tcx.types.u32, - (I64, false) => tcx.types.u64, - (I128, false) => tcx.types.u128, - (I8, true) => tcx.types.i8, - (I16, true) => tcx.types.i16, - (I32, true) => tcx.types.i32, - (I64, true) => tcx.types.i64, - (I128, true) => tcx.types.i128, - } - } - - /// Gets the Integer type from an attr::IntType. - fn from_attr(cx: &C, ity: attr::IntType) -> Integer { - let dl = cx.data_layout(); - - match ity { - attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8, - attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16, - attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32, - attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64, - attr::SignedInt(IntTy::I128) | attr::UnsignedInt(UintTy::U128) => I128, - attr::SignedInt(IntTy::Isize) | attr::UnsignedInt(UintTy::Usize) => { - dl.ptr_sized_integer() - } - } - } - - /// Finds the appropriate Integer type and signedness for the given - /// signed discriminant range and #[repr] attribute. - /// N.B.: u128 values above i128::MAX will be treated as signed, but - /// that shouldn't affect anything, other than maybe debuginfo. - fn repr_discr<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - repr: &ReprOptions, - min: i128, - max: i128, - ) -> (Integer, bool) { - // Theoretically, negative values could be larger in unsigned representation - // than the unsigned representation of the signed minimum. However, if there - // are any negative values, the only valid unsigned representation is u128 - // which can fit all i128 values, so the result remains unaffected. - let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); - let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); - - let mut min_from_extern = None; - let min_default = I8; - - if let Some(ity) = repr.int { - let discr = Integer::from_attr(&tcx, ity); - let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; - if discr < fit { - bug!( - "Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum `{}", - ty - ) - } - return (discr, ity.is_signed()); - } - - if repr.c() { - match &tcx.sess.target.target.arch[..] { - // WARNING: the ARM EABI has two variants; the one corresponding - // to `at_least == I32` appears to be used on Linux and NetBSD, - // but some systems may use the variant corresponding to no - // lower bound. However, we don't run on those yet...? - "arm" => min_from_extern = Some(I32), - _ => min_from_extern = Some(I32), - } - } - - let at_least = min_from_extern.unwrap_or(min_default); - - // If there are no negative values, we can use the unsigned fit. - if min >= 0 { - (cmp::max(unsigned_fit, at_least), false) - } else { - (cmp::max(signed_fit, at_least), true) - } - } -} - -pub trait PrimitiveExt { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; - fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; -} - -impl PrimitiveExt for Primitive { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match *self { - Int(i, signed) => i.to_ty(tcx, signed), - F32 => tcx.types.f32, - F64 => tcx.types.f64, - Pointer => tcx.mk_mut_ptr(tcx.mk_unit()), - } - } - - /// Return an *integer* type matching this primitive. - /// Useful in particular when dealing with enum discriminants. - fn to_int_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match *self { - Int(i, signed) => i.to_ty(tcx, signed), - Pointer => tcx.types.usize, - F32 | F64 => bug!("floats do not have an int type"), - } - } -} - -/// The first half of a fat pointer. -/// -/// - For a trait object, this is the address of the box. -/// - For a slice, this is the base address. -pub const FAT_PTR_ADDR: usize = 0; - -/// The second half of a fat pointer. -/// -/// - For a trait object, this is the address of the vtable. -/// - For a slice, this is the length. -pub const FAT_PTR_EXTRA: usize = 1; - -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] -pub enum LayoutError<'tcx> { - Unknown(Ty<'tcx>), - SizeOverflow(Ty<'tcx>), -} - -impl<'tcx> fmt::Display for LayoutError<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - LayoutError::Unknown(ty) => write!(f, "the type `{:?}` has an unknown layout", ty), - LayoutError::SizeOverflow(ty) => { - write!(f, "the type `{:?}` is too big for the current architecture", ty) - } - } - } -} - -fn layout_raw<'tcx>( - tcx: TyCtxt<'tcx>, - query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> { - ty::tls::with_related_context(tcx, move |icx| { - let rec_limit = *tcx.sess.recursion_limit.get(); - let (param_env, ty) = query.into_parts(); - - if icx.layout_depth > rec_limit { - tcx.sess.fatal(&format!("overflow representing the type `{}`", ty)); - } - - // Update the ImplicitCtxt to increase the layout_depth - let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() }; - - ty::tls::enter_context(&icx, |_| { - let cx = LayoutCx { tcx, param_env }; - let layout = cx.layout_raw_uncached(ty); - // Type-level uninhabitedness should always imply ABI uninhabitedness. - if let Ok(layout) = layout { - if ty.conservative_is_privately_uninhabited(tcx) { - assert!(layout.abi.is_uninhabited()); - } - } - layout - }) - }) -} - -pub fn provide(providers: &mut ty::query::Providers<'_>) { - *providers = ty::query::Providers { layout_raw, ..*providers }; -} - -pub struct LayoutCx<'tcx, C> { - pub tcx: C, - pub param_env: ty::ParamEnv<'tcx>, -} - -#[derive(Copy, Clone, Debug)] -enum StructKind { - /// A tuple, closure, or univariant which cannot be coerced to unsized. - AlwaysSized, - /// A univariant, the last field of which may be coerced to unsized. - MaybeUnsized, - /// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag). - Prefixed(Size, Align), -} - -// Invert a bijective mapping, i.e. `invert(map)[y] = x` if `map[x] = y`. -// This is used to go between `memory_index` (source field order to memory order) -// and `inverse_memory_index` (memory order to source field order). -// See also `FieldPlacement::Arbitrary::memory_index` for more details. -// FIXME(eddyb) build a better abstraction for permutations, if possible. -fn invert_mapping(map: &[u32]) -> Vec { - let mut inverse = vec![0; map.len()]; - for i in 0..map.len() { - inverse[map[i] as usize] = i as u32; - } - inverse -} - -impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { - fn scalar_pair(&self, a: Scalar, b: Scalar) -> LayoutDetails { - let dl = self.data_layout(); - let b_align = b.value.align(dl); - let align = a.value.align(dl).max(b_align).max(dl.aggregate_align); - let b_offset = a.value.size(dl).align_to(b_align.abi); - let size = (b_offset + b.value.size(dl)).align_to(align.abi); - - // HACK(nox): We iter on `b` and then `a` because `max_by_key` - // returns the last maximum. - let largest_niche = Niche::from_scalar(dl, b_offset, b.clone()) - .into_iter() - .chain(Niche::from_scalar(dl, Size::ZERO, a.clone())) - .max_by_key(|niche| niche.available(dl)); - - LayoutDetails { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Arbitrary { - offsets: vec![Size::ZERO, b_offset], - memory_index: vec![0, 1], - }, - abi: Abi::ScalarPair(a, b), - largest_niche, - align, - size, - } - } - - fn univariant_uninterned( - &self, - ty: Ty<'tcx>, - fields: &[TyLayout<'_>], - repr: &ReprOptions, - kind: StructKind, - ) -> Result> { - let dl = self.data_layout(); - let pack = repr.pack; - if pack.is_some() && repr.align.is_some() { - bug!("struct cannot be packed and aligned"); - } - - let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; - - let mut sized = true; - let mut offsets = vec![Size::ZERO; fields.len()]; - let mut inverse_memory_index: Vec = (0..fields.len() as u32).collect(); - - let mut optimize = !repr.inhibit_struct_field_reordering_opt(); - if let StructKind::Prefixed(_, align) = kind { - optimize &= align.bytes() == 1; - } - - if optimize { - let end = - if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; - let optimizing = &mut inverse_memory_index[..end]; - let field_align = |f: &TyLayout<'_>| { - if let Some(pack) = pack { f.align.abi.min(pack) } else { f.align.abi } - }; - match kind { - StructKind::AlwaysSized | StructKind::MaybeUnsized => { - optimizing.sort_by_key(|&x| { - // Place ZSTs first to avoid "interesting offsets", - // especially with only one or two non-ZST fields. - let f = &fields[x as usize]; - (!f.is_zst(), cmp::Reverse(field_align(f))) - }); - } - StructKind::Prefixed(..) => { - optimizing.sort_by_key(|&x| field_align(&fields[x as usize])); - } - } - } - - // inverse_memory_index holds field indices by increasing memory offset. - // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. - // We now write field offsets to the corresponding offset slot; - // field 5 with offset 0 puts 0 in offsets[5]. - // At the bottom of this function, we invert `inverse_memory_index` to - // produce `memory_index` (see `invert_mapping`). - - let mut offset = Size::ZERO; - let mut largest_niche = None; - let mut largest_niche_available = 0; - - if let StructKind::Prefixed(prefix_size, prefix_align) = kind { - let prefix_align = - if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align }; - align = align.max(AbiAndPrefAlign::new(prefix_align)); - offset = prefix_size.align_to(prefix_align); - } - - for &i in &inverse_memory_index { - let field = fields[i as usize]; - if !sized { - bug!("univariant: field #{} of `{}` comes after unsized field", offsets.len(), ty); - } - - if field.is_unsized() { - sized = false; - } - - // Invariant: offset < dl.obj_size_bound() <= 1<<61 - let field_align = if let Some(pack) = pack { - field.align.min(AbiAndPrefAlign::new(pack)) - } else { - field.align - }; - offset = offset.align_to(field_align.abi); - align = align.max(field_align); - - debug!("univariant offset: {:?} field: {:#?}", offset, field); - offsets[i as usize] = offset; - - if !repr.hide_niche() { - if let Some(mut niche) = field.largest_niche.clone() { - let available = niche.available(dl); - if available > largest_niche_available { - largest_niche_available = available; - niche.offset += offset; - largest_niche = Some(niche); - } - } - } - - offset = offset.checked_add(field.size, dl).ok_or(LayoutError::SizeOverflow(ty))?; - } - - if let Some(repr_align) = repr.align { - align = align.max(AbiAndPrefAlign::new(repr_align)); - } - - debug!("univariant min_size: {:?}", offset); - let min_size = offset; - - // As stated above, inverse_memory_index holds field indices by increasing offset. - // This makes it an already-sorted view of the offsets vec. - // To invert it, consider: - // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. - // Field 5 would be the first element, so memory_index is i: - // Note: if we didn't optimize, it's already right. - - let memory_index = - if optimize { invert_mapping(&inverse_memory_index) } else { inverse_memory_index }; - - let size = min_size.align_to(align.abi); - let mut abi = Abi::Aggregate { sized }; - - // Unpack newtype ABIs and find scalar pairs. - if sized && size.bytes() > 0 { - // All other fields must be ZSTs, and we need them to all start at 0. - let mut zst_offsets = offsets.iter().enumerate().filter(|&(i, _)| fields[i].is_zst()); - if zst_offsets.all(|(_, o)| o.bytes() == 0) { - let mut non_zst_fields = fields.iter().enumerate().filter(|&(_, f)| !f.is_zst()); - - match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) { - // We have exactly one non-ZST field. - (Some((i, field)), None, None) => { - // Field fills the struct and it has a scalar or scalar pair ABI. - if offsets[i].bytes() == 0 - && align.abi == field.align.abi - && size == field.size - { - match field.abi { - // For plain scalars, or vectors of them, we can't unpack - // newtypes for `#[repr(C)]`, as that affects C ABIs. - Abi::Scalar(_) | Abi::Vector { .. } if optimize => { - abi = field.abi.clone(); - } - // But scalar pairs are Rust-specific and get - // treated as aggregates by C ABIs anyway. - Abi::ScalarPair(..) => { - abi = field.abi.clone(); - } - _ => {} - } - } - } - - // Two non-ZST fields, and they're both scalars. - ( - Some(( - i, - &TyLayout { - details: &LayoutDetails { abi: Abi::Scalar(ref a), .. }, - .. - }, - )), - Some(( - j, - &TyLayout { - details: &LayoutDetails { abi: Abi::Scalar(ref b), .. }, - .. - }, - )), - None, - ) => { - // Order by the memory placement, not source order. - let ((i, a), (j, b)) = if offsets[i] < offsets[j] { - ((i, a), (j, b)) - } else { - ((j, b), (i, a)) - }; - let pair = self.scalar_pair(a.clone(), b.clone()); - let pair_offsets = match pair.fields { - FieldPlacement::Arbitrary { ref offsets, ref memory_index } => { - assert_eq!(memory_index, &[0, 1]); - offsets - } - _ => bug!(), - }; - if offsets[i] == pair_offsets[0] - && offsets[j] == pair_offsets[1] - && align == pair.align - && size == pair.size - { - // We can use `ScalarPair` only when it matches our - // already computed layout (including `#[repr(C)]`). - abi = pair.abi; - } - } - - _ => {} - } - } - } - - if sized && fields.iter().any(|f| f.abi.is_uninhabited()) { - abi = Abi::Uninhabited; - } - - Ok(LayoutDetails { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Arbitrary { offsets, memory_index }, - abi, - largest_niche, - align, - size, - }) - } - - fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> { - let tcx = self.tcx; - let param_env = self.param_env; - let dl = self.data_layout(); - let scalar_unit = |value: Primitive| { - let bits = value.size(dl).bits(); - assert!(bits <= 128); - Scalar { value, valid_range: 0..=(!0 >> (128 - bits)) } - }; - let scalar = - |value: Primitive| tcx.intern_layout(LayoutDetails::scalar(self, scalar_unit(value))); - - let univariant = |fields: &[TyLayout<'_>], repr: &ReprOptions, kind| { - Ok(tcx.intern_layout(self.univariant_uninterned(ty, fields, repr, kind)?)) - }; - debug_assert!(!ty.has_infer_types_or_consts()); - - Ok(match ty.kind { - // Basic scalars. - ty::Bool => tcx.intern_layout(LayoutDetails::scalar( - self, - Scalar { value: Int(I8, false), valid_range: 0..=1 }, - )), - ty::Char => tcx.intern_layout(LayoutDetails::scalar( - self, - Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF }, - )), - ty::Int(ity) => scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true)), - ty::Uint(ity) => scalar(Int(Integer::from_attr(dl, attr::UnsignedInt(ity)), false)), - ty::Float(fty) => scalar(match fty { - ast::FloatTy::F32 => F32, - ast::FloatTy::F64 => F64, - }), - ty::FnPtr(_) => { - let mut ptr = scalar_unit(Pointer); - ptr.valid_range = 1..=*ptr.valid_range.end(); - tcx.intern_layout(LayoutDetails::scalar(self, ptr)) - } - - // The never type. - ty::Never => tcx.intern_layout(LayoutDetails { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Union(0), - abi: Abi::Uninhabited, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }), - - // Potentially-fat pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - let mut data_ptr = scalar_unit(Pointer); - if !ty.is_unsafe_ptr() { - data_ptr.valid_range = 1..=*data_ptr.valid_range.end(); - } - - let pointee = tcx.normalize_erasing_regions(param_env, pointee); - if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { - return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr))); - } - - let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - let metadata = match unsized_part.kind { - ty::Foreign(..) => { - return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr))); - } - ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), - ty::Dynamic(..) => { - let mut vtable = scalar_unit(Pointer); - vtable.valid_range = 1..=*vtable.valid_range.end(); - vtable - } - _ => return Err(LayoutError::Unknown(unsized_part)), - }; - - // Effectively a (ptr, meta) tuple. - tcx.intern_layout(self.scalar_pair(data_ptr, metadata)) - } - - // Arrays and slices. - ty::Array(element, mut count) => { - if count.has_projections() { - count = tcx.normalize_erasing_regions(param_env, count); - if count.has_projections() { - return Err(LayoutError::Unknown(ty)); - } - } - - let count = count.try_eval_usize(tcx, param_env).ok_or(LayoutError::Unknown(ty))?; - let element = self.layout_of(element)?; - let size = - element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; - - let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) { - Abi::Uninhabited - } else { - Abi::Aggregate { sized: true } - }; - - let largest_niche = if count != 0 { element.largest_niche.clone() } else { None }; - - tcx.intern_layout(LayoutDetails { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Array { stride: element.size, count }, - abi, - largest_niche, - align: element.align, - size, - }) - } - ty::Slice(element) => { - let element = self.layout_of(element)?; - tcx.intern_layout(LayoutDetails { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Array { stride: element.size, count: 0 }, - abi: Abi::Aggregate { sized: false }, - largest_niche: None, - align: element.align, - size: Size::ZERO, - }) - } - ty::Str => tcx.intern_layout(LayoutDetails { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Array { stride: Size::from_bytes(1), count: 0 }, - abi: Abi::Aggregate { sized: false }, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }), - - // Odd unit types. - ty::FnDef(..) => univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?, - ty::Dynamic(..) | ty::Foreign(..) => { - let mut unit = self.univariant_uninterned( - ty, - &[], - &ReprOptions::default(), - StructKind::AlwaysSized, - )?; - match unit.abi { - Abi::Aggregate { ref mut sized } => *sized = false, - _ => bug!(), - } - tcx.intern_layout(unit) - } - - ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, substs)?, - - ty::Closure(_, ref substs) => { - let tys = substs.as_closure().upvar_tys(); - univariant( - &tys.map(|ty| self.layout_of(ty)).collect::, _>>()?, - &ReprOptions::default(), - StructKind::AlwaysSized, - )? - } - - ty::Tuple(tys) => { - let kind = - if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; - - univariant( - &tys.iter() - .map(|k| self.layout_of(k.expect_ty())) - .collect::, _>>()?, - &ReprOptions::default(), - kind, - )? - } - - // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => { - let element = self.layout_of(ty.simd_type(tcx))?; - let count = ty.simd_size(tcx); - assert!(count > 0); - let scalar = match element.abi { - Abi::Scalar(ref scalar) => scalar.clone(), - _ => { - tcx.sess.fatal(&format!( - "monomorphising SIMD type `{}` with \ - a non-machine element type `{}`", - ty, element.ty - )); - } - }; - let size = - element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; - let align = dl.vector_align(size); - let size = size.align_to(align.abi); - - tcx.intern_layout(LayoutDetails { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Array { stride: element.size, count }, - abi: Abi::Vector { element: scalar, count }, - largest_niche: element.largest_niche.clone(), - size, - align, - }) - } - - // ADTs. - ty::Adt(def, substs) => { - // Cache the field layouts. - let variants = def - .variants - .iter() - .map(|v| { - v.fields - .iter() - .map(|field| self.layout_of(field.ty(tcx, substs))) - .collect::, _>>() - }) - .collect::, _>>()?; - - if def.is_union() { - if def.repr.pack.is_some() && def.repr.align.is_some() { - bug!("union cannot be packed and aligned"); - } - - let mut align = - if def.repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; - - if let Some(repr_align) = def.repr.align { - align = align.max(AbiAndPrefAlign::new(repr_align)); - } - - let optimize = !def.repr.inhibit_union_abi_opt(); - let mut size = Size::ZERO; - let mut abi = Abi::Aggregate { sized: true }; - let index = VariantIdx::new(0); - for field in &variants[index] { - assert!(!field.is_unsized()); - align = align.max(field.align); - - // If all non-ZST fields have the same ABI, forward this ABI - if optimize && !field.is_zst() { - // Normalize scalar_unit to the maximal valid range - let field_abi = match &field.abi { - Abi::Scalar(x) => Abi::Scalar(scalar_unit(x.value)), - Abi::ScalarPair(x, y) => { - Abi::ScalarPair(scalar_unit(x.value), scalar_unit(y.value)) - } - Abi::Vector { element: x, count } => { - Abi::Vector { element: scalar_unit(x.value), count: *count } - } - Abi::Uninhabited | Abi::Aggregate { .. } => { - Abi::Aggregate { sized: true } - } - }; - - if size == Size::ZERO { - // first non ZST: initialize 'abi' - abi = field_abi; - } else if abi != field_abi { - // different fields have different ABI: reset to Aggregate - abi = Abi::Aggregate { sized: true }; - } - } - - size = cmp::max(size, field.size); - } - - if let Some(pack) = def.repr.pack { - align = align.min(AbiAndPrefAlign::new(pack)); - } - - return Ok(tcx.intern_layout(LayoutDetails { - variants: Variants::Single { index }, - fields: FieldPlacement::Union(variants[index].len()), - abi, - largest_niche: None, - align, - size: size.align_to(align.abi), - })); - } - - // A variant is absent if it's uninhabited and only has ZST fields. - // Present uninhabited variants only require space for their fields, - // but *not* an encoding of the discriminant (e.g., a tag value). - // See issue #49298 for more details on the need to leave space - // for non-ZST uninhabited data (mostly partial initialization). - let absent = |fields: &[TyLayout<'_>]| { - let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited()); - let is_zst = fields.iter().all(|f| f.is_zst()); - uninhabited && is_zst - }; - let (present_first, present_second) = { - let mut present_variants = variants - .iter_enumerated() - .filter_map(|(i, v)| if absent(v) { None } else { Some(i) }); - (present_variants.next(), present_variants.next()) - }; - let present_first = match present_first { - present_first @ Some(_) => present_first, - // Uninhabited because it has no variants, or only absent ones. - None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)), - // If it's a struct, still compute a layout so that we can still compute the - // field offsets. - None => Some(VariantIdx::new(0)), - }; - - let is_struct = !def.is_enum() || - // Only one variant is present. - (present_second.is_none() && - // Representation optimizations are allowed. - !def.repr.inhibit_enum_layout_opt()); - if is_struct { - // Struct, or univariant enum equivalent to a struct. - // (Typechecking will reject discriminant-sizing attrs.) - - let v = present_first.unwrap(); - let kind = if def.is_enum() || variants[v].is_empty() { - StructKind::AlwaysSized - } else { - let param_env = tcx.param_env(def.did); - let last_field = def.variants[v].fields.last().unwrap(); - let always_sized = - tcx.type_of(last_field.did).is_sized(tcx.at(DUMMY_SP), param_env); - if !always_sized { - StructKind::MaybeUnsized - } else { - StructKind::AlwaysSized - } - }; - - let mut st = self.univariant_uninterned(ty, &variants[v], &def.repr, kind)?; - st.variants = Variants::Single { index: v }; - let (start, end) = self.tcx.layout_scalar_valid_range(def.did); - match st.abi { - Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => { - // the asserts ensure that we are not using the - // `#[rustc_layout_scalar_valid_range(n)]` - // attribute to widen the range of anything as that would probably - // result in UB somewhere - // FIXME(eddyb) the asserts are probably not needed, - // as larger validity ranges would result in missed - // optimizations, *not* wrongly assuming the inner - // value is valid. e.g. unions enlarge validity ranges, - // because the values may be uninitialized. - if let Bound::Included(start) = start { - // FIXME(eddyb) this might be incorrect - it doesn't - // account for wrap-around (end < start) ranges. - assert!(*scalar.valid_range.start() <= start); - scalar.valid_range = start..=*scalar.valid_range.end(); - } - if let Bound::Included(end) = end { - // FIXME(eddyb) this might be incorrect - it doesn't - // account for wrap-around (end < start) ranges. - assert!(*scalar.valid_range.end() >= end); - scalar.valid_range = *scalar.valid_range.start()..=end; - } - - // Update `largest_niche` if we have introduced a larger niche. - let niche = if def.repr.hide_niche() { - None - } else { - Niche::from_scalar(dl, Size::ZERO, scalar.clone()) - }; - if let Some(niche) = niche { - match &st.largest_niche { - Some(largest_niche) => { - // Replace the existing niche even if they're equal, - // because this one is at a lower offset. - if largest_niche.available(dl) <= niche.available(dl) { - st.largest_niche = Some(niche); - } - } - None => st.largest_niche = Some(niche), - } - } - } - _ => assert!( - start == Bound::Unbounded && end == Bound::Unbounded, - "nonscalar layout for layout_scalar_valid_range type {:?}: {:#?}", - def, - st, - ), - } - - return Ok(tcx.intern_layout(st)); - } - - // At this point, we have handled all unions and - // structs. (We have also handled univariant enums - // that allow representation optimization.) - assert!(def.is_enum()); - - // The current code for niche-filling relies on variant indices - // instead of actual discriminants, so dataful enums with - // explicit discriminants (RFC #2363) would misbehave. - let no_explicit_discriminants = def - .variants - .iter_enumerated() - .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32())); - - // Niche-filling enum optimization. - if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants { - let mut dataful_variant = None; - let mut niche_variants = VariantIdx::MAX..=VariantIdx::new(0); - - // Find one non-ZST variant. - 'variants: for (v, fields) in variants.iter_enumerated() { - if absent(fields) { - continue 'variants; - } - for f in fields { - if !f.is_zst() { - if dataful_variant.is_none() { - dataful_variant = Some(v); - continue 'variants; - } else { - dataful_variant = None; - break 'variants; - } - } - } - niche_variants = *niche_variants.start().min(&v)..=v; - } - - if niche_variants.start() > niche_variants.end() { - dataful_variant = None; - } - - if let Some(i) = dataful_variant { - let count = (niche_variants.end().as_u32() - - niche_variants.start().as_u32() - + 1) as u128; - // FIXME(#62691) use the largest niche across all fields, - // not just the first one. - for (field_index, &field) in variants[i].iter().enumerate() { - let niche = match &field.largest_niche { - Some(niche) => niche, - _ => continue, - }; - let (niche_start, niche_scalar) = match niche.reserve(self, count) { - Some(pair) => pair, - None => continue, - }; - - let mut align = dl.aggregate_align; - let st = variants - .iter_enumerated() - .map(|(j, v)| { - let mut st = self.univariant_uninterned( - ty, - v, - &def.repr, - StructKind::AlwaysSized, - )?; - st.variants = Variants::Single { index: j }; - - align = align.max(st.align); - - Ok(st) - }) - .collect::, _>>()?; - - let offset = st[i].fields.offset(field_index) + niche.offset; - let size = st[i].size; - - let abi = if st.iter().all(|v| v.abi.is_uninhabited()) { - Abi::Uninhabited - } else { - match st[i].abi { - Abi::Scalar(_) => Abi::Scalar(niche_scalar.clone()), - Abi::ScalarPair(ref first, ref second) => { - // We need to use scalar_unit to reset the - // valid range to the maximal one for that - // primitive, because only the niche is - // guaranteed to be initialised, not the - // other primitive. - if offset.bytes() == 0 { - Abi::ScalarPair( - niche_scalar.clone(), - scalar_unit(second.value), - ) - } else { - Abi::ScalarPair( - scalar_unit(first.value), - niche_scalar.clone(), - ) - } - } - _ => Abi::Aggregate { sized: true }, - } - }; - - let largest_niche = - Niche::from_scalar(dl, offset, niche_scalar.clone()); - - return Ok(tcx.intern_layout(LayoutDetails { - variants: Variants::Multiple { - discr: niche_scalar, - discr_kind: DiscriminantKind::Niche { - dataful_variant: i, - niche_variants, - niche_start, - }, - discr_index: 0, - variants: st, - }, - fields: FieldPlacement::Arbitrary { - offsets: vec![offset], - memory_index: vec![0], - }, - abi, - largest_niche, - size, - align, - })); - } - } - } - - let (mut min, mut max) = (i128::MAX, i128::MIN); - let discr_type = def.repr.discr_type(); - let bits = Integer::from_attr(self, discr_type).size().bits(); - for (i, discr) in def.discriminants(tcx) { - if variants[i].iter().any(|f| f.abi.is_uninhabited()) { - continue; - } - let mut x = discr.val as i128; - if discr_type.is_signed() { - // sign extend the raw representation to be an i128 - x = (x << (128 - bits)) >> (128 - bits); - } - if x < min { - min = x; - } - if x > max { - max = x; - } - } - // We might have no inhabited variants, so pretend there's at least one. - if (min, max) == (i128::MAX, i128::MIN) { - min = 0; - max = 0; - } - assert!(min <= max, "discriminant range is {}...{}", min, max); - let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); - - let mut align = dl.aggregate_align; - let mut size = Size::ZERO; - - // We're interested in the smallest alignment, so start large. - let mut start_align = Align::from_bytes(256).unwrap(); - assert_eq!(Integer::for_align(dl, start_align), None); - - // repr(C) on an enum tells us to make a (tag, union) layout, - // so we need to grow the prefix alignment to be at least - // the alignment of the union. (This value is used both for - // determining the alignment of the overall enum, and the - // determining the alignment of the payload after the tag.) - let mut prefix_align = min_ity.align(dl).abi; - if def.repr.c() { - for fields in &variants { - for field in fields { - prefix_align = prefix_align.max(field.align.abi); - } - } - } - - // Create the set of structs that represent each variant. - let mut layout_variants = variants - .iter_enumerated() - .map(|(i, field_layouts)| { - let mut st = self.univariant_uninterned( - ty, - &field_layouts, - &def.repr, - StructKind::Prefixed(min_ity.size(), prefix_align), - )?; - st.variants = Variants::Single { index: i }; - // Find the first field we can't move later - // to make room for a larger discriminant. - for field in - st.fields.index_by_increasing_offset().map(|j| field_layouts[j]) - { - if !field.is_zst() || field.align.abi.bytes() != 1 { - start_align = start_align.min(field.align.abi); - break; - } - } - size = cmp::max(size, st.size); - align = align.max(st.align); - Ok(st) - }) - .collect::, _>>()?; - - // Align the maximum variant size to the largest alignment. - size = size.align_to(align.abi); - - if size.bytes() >= dl.obj_size_bound() { - return Err(LayoutError::SizeOverflow(ty)); - } - - let typeck_ity = Integer::from_attr(dl, def.repr.discr_type()); - if typeck_ity < min_ity { - // It is a bug if Layout decided on a greater discriminant size than typeck for - // some reason at this point (based on values discriminant can take on). Mostly - // because this discriminant will be loaded, and then stored into variable of - // type calculated by typeck. Consider such case (a bug): typeck decided on - // byte-sized discriminant, but layout thinks we need a 16-bit to store all - // discriminant values. That would be a bug, because then, in codegen, in order - // to store this 16-bit discriminant into 8-bit sized temporary some of the - // space necessary to represent would have to be discarded (or layout is wrong - // on thinking it needs 16 bits) - bug!( - "layout decided on a larger discriminant type ({:?}) than typeck ({:?})", - min_ity, - typeck_ity - ); - // However, it is fine to make discr type however large (as an optimisation) - // after this point – we’ll just truncate the value we load in codegen. - } - - // Check to see if we should use a different type for the - // discriminant. We can safely use a type with the same size - // as the alignment of the first field of each variant. - // We increase the size of the discriminant to avoid LLVM copying - // padding when it doesn't need to. This normally causes unaligned - // load/stores and excessive memcpy/memset operations. By using a - // bigger integer size, LLVM can be sure about its contents and - // won't be so conservative. - - // Use the initial field alignment - let mut ity = if def.repr.c() || def.repr.int.is_some() { - min_ity - } else { - Integer::for_align(dl, start_align).unwrap_or(min_ity) - }; - - // If the alignment is not larger than the chosen discriminant size, - // don't use the alignment as the final size. - if ity <= min_ity { - ity = min_ity; - } else { - // Patch up the variants' first few fields. - let old_ity_size = min_ity.size(); - let new_ity_size = ity.size(); - for variant in &mut layout_variants { - match variant.fields { - FieldPlacement::Arbitrary { ref mut offsets, .. } => { - for i in offsets { - if *i <= old_ity_size { - assert_eq!(*i, old_ity_size); - *i = new_ity_size; - } - } - // We might be making the struct larger. - if variant.size <= old_ity_size { - variant.size = new_ity_size; - } - } - _ => bug!(), - } - } - } - - let tag_mask = !0u128 >> (128 - ity.size().bits()); - let tag = Scalar { - value: Int(ity, signed), - valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask), - }; - let mut abi = Abi::Aggregate { sized: true }; - if tag.value.size(dl) == size { - abi = Abi::Scalar(tag.clone()); - } else { - // Try to use a ScalarPair for all tagged enums. - let mut common_prim = None; - for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) { - let offsets = match layout_variant.fields { - FieldPlacement::Arbitrary { ref offsets, .. } => offsets, - _ => bug!(), - }; - let mut fields = - field_layouts.iter().zip(offsets).filter(|p| !p.0.is_zst()); - let (field, offset) = match (fields.next(), fields.next()) { - (None, None) => continue, - (Some(pair), None) => pair, - _ => { - common_prim = None; - break; - } - }; - let prim = match field.details.abi { - Abi::Scalar(ref scalar) => scalar.value, - _ => { - common_prim = None; - break; - } - }; - if let Some(pair) = common_prim { - // This is pretty conservative. We could go fancier - // by conflating things like i32 and u32, or even - // realising that (u8, u8) could just cohabit with - // u16 or even u32. - if pair != (prim, offset) { - common_prim = None; - break; - } - } else { - common_prim = Some((prim, offset)); - } - } - if let Some((prim, offset)) = common_prim { - let pair = self.scalar_pair(tag.clone(), scalar_unit(prim)); - let pair_offsets = match pair.fields { - FieldPlacement::Arbitrary { ref offsets, ref memory_index } => { - assert_eq!(memory_index, &[0, 1]); - offsets - } - _ => bug!(), - }; - if pair_offsets[0] == Size::ZERO - && pair_offsets[1] == *offset - && align == pair.align - && size == pair.size - { - // We can use `ScalarPair` only when it matches our - // already computed layout (including `#[repr(C)]`). - abi = pair.abi; - } - } - } - - if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { - abi = Abi::Uninhabited; - } - - let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone()); - - tcx.intern_layout(LayoutDetails { - variants: Variants::Multiple { - discr: tag, - discr_kind: DiscriminantKind::Tag, - discr_index: 0, - variants: layout_variants, - }, - fields: FieldPlacement::Arbitrary { - offsets: vec![Size::ZERO], - memory_index: vec![0], - }, - largest_niche, - abi, - align, - size, - }) - } - - // Types with no meaningful known layout. - ty::Projection(_) | ty::Opaque(..) => { - let normalized = tcx.normalize_erasing_regions(param_env, ty); - if ty == normalized { - return Err(LayoutError::Unknown(ty)); - } - tcx.layout_raw(param_env.and(normalized))? - } - - ty::Bound(..) - | ty::Placeholder(..) - | ty::UnnormalizedProjection(..) - | ty::GeneratorWitness(..) - | ty::Infer(_) => bug!("LayoutDetails::compute: unexpected type `{}`", ty), - - ty::Param(_) | ty::Error => { - return Err(LayoutError::Unknown(ty)); - } - }) - } -} - -/// Overlap eligibility and variant assignment for each GeneratorSavedLocal. -#[derive(Clone, Debug, PartialEq)] -enum SavedLocalEligibility { - Unassigned, - Assigned(VariantIdx), - // FIXME: Use newtype_index so we aren't wasting bytes - Ineligible(Option), -} - -// When laying out generators, we divide our saved local fields into two -// categories: overlap-eligible and overlap-ineligible. -// -// Those fields which are ineligible for overlap go in a "prefix" at the -// beginning of the layout, and always have space reserved for them. -// -// Overlap-eligible fields are only assigned to one variant, so we lay -// those fields out for each variant and put them right after the -// prefix. -// -// Finally, in the layout details, we point to the fields from the -// variants they are assigned to. It is possible for some fields to be -// included in multiple variants. No field ever "moves around" in the -// layout; its offset is always the same. -// -// Also included in the layout are the upvars and the discriminant. -// These are included as fields on the "outer" layout; they are not part -// of any variant. -impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { - /// Compute the eligibility and assignment of each local. - fn generator_saved_local_eligibility( - &self, - info: &GeneratorLayout<'tcx>, - ) -> (BitSet, IndexVec) { - use SavedLocalEligibility::*; - - let mut assignments: IndexVec = - IndexVec::from_elem_n(Unassigned, info.field_tys.len()); - - // The saved locals not eligible for overlap. These will get - // "promoted" to the prefix of our generator. - let mut ineligible_locals = BitSet::new_empty(info.field_tys.len()); - - // Figure out which of our saved locals are fields in only - // one variant. The rest are deemed ineligible for overlap. - for (variant_index, fields) in info.variant_fields.iter_enumerated() { - for local in fields { - match assignments[*local] { - Unassigned => { - assignments[*local] = Assigned(variant_index); - } - Assigned(idx) => { - // We've already seen this local at another suspension - // point, so it is no longer a candidate. - trace!( - "removing local {:?} in >1 variant ({:?}, {:?})", - local, - variant_index, - idx - ); - ineligible_locals.insert(*local); - assignments[*local] = Ineligible(None); - } - Ineligible(_) => {} - } - } - } - - // Next, check every pair of eligible locals to see if they - // conflict. - for local_a in info.storage_conflicts.rows() { - let conflicts_a = info.storage_conflicts.count(local_a); - if ineligible_locals.contains(local_a) { - continue; - } - - for local_b in info.storage_conflicts.iter(local_a) { - // local_a and local_b are storage live at the same time, therefore they - // cannot overlap in the generator layout. The only way to guarantee - // this is if they are in the same variant, or one is ineligible - // (which means it is stored in every variant). - if ineligible_locals.contains(local_b) - || assignments[local_a] == assignments[local_b] - { - continue; - } - - // If they conflict, we will choose one to make ineligible. - // This is not always optimal; it's just a greedy heuristic that - // seems to produce good results most of the time. - let conflicts_b = info.storage_conflicts.count(local_b); - let (remove, other) = - if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) }; - ineligible_locals.insert(remove); - assignments[remove] = Ineligible(None); - trace!("removing local {:?} due to conflict with {:?}", remove, other); - } - } - - // Count the number of variants in use. If only one of them, then it is - // impossible to overlap any locals in our layout. In this case it's - // always better to make the remaining locals ineligible, so we can - // lay them out with the other locals in the prefix and eliminate - // unnecessary padding bytes. - { - let mut used_variants = BitSet::new_empty(info.variant_fields.len()); - for assignment in &assignments { - match assignment { - Assigned(idx) => { - used_variants.insert(*idx); - } - _ => {} - } - } - if used_variants.count() < 2 { - for assignment in assignments.iter_mut() { - *assignment = Ineligible(None); - } - ineligible_locals.insert_all(); - } - } - - // Write down the order of our locals that will be promoted to the prefix. - { - for (idx, local) in ineligible_locals.iter().enumerate() { - assignments[local] = Ineligible(Some(idx as u32)); - } - } - debug!("generator saved local assignments: {:?}", assignments); - - (ineligible_locals, assignments) - } - - /// Compute the full generator layout. - fn generator_layout( - &self, - ty: Ty<'tcx>, - def_id: hir::def_id::DefId, - substs: SubstsRef<'tcx>, - ) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> { - use SavedLocalEligibility::*; - let tcx = self.tcx; - - let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs); - - let info = tcx.generator_layout(def_id); - let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info); - - // Build a prefix layout, including "promoting" all ineligible - // locals as part of the prefix. We compute the layout of all of - // these fields at once to get optimal packing. - let discr_index = substs.as_generator().prefix_tys().count(); - - // `info.variant_fields` already accounts for the reserved variants, so no need to add them. - let max_discr = (info.variant_fields.len() - 1) as u128; - let discr_int = Integer::fit_unsigned(max_discr); - let discr_int_ty = discr_int.to_ty(tcx, false); - let discr = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr }; - let discr_layout = self.tcx.intern_layout(LayoutDetails::scalar(self, discr.clone())); - let discr_layout = TyLayout { ty: discr_int_ty, details: discr_layout }; - - let promoted_layouts = ineligible_locals - .iter() - .map(|local| subst_field(info.field_tys[local])) - .map(|ty| tcx.mk_maybe_uninit(ty)) - .map(|ty| self.layout_of(ty)); - let prefix_layouts = substs - .as_generator() - .prefix_tys() - .map(|ty| self.layout_of(ty)) - .chain(iter::once(Ok(discr_layout))) - .chain(promoted_layouts) - .collect::, _>>()?; - let prefix = self.univariant_uninterned( - ty, - &prefix_layouts, - &ReprOptions::default(), - StructKind::AlwaysSized, - )?; - - let (prefix_size, prefix_align) = (prefix.size, prefix.align); - - // Split the prefix layout into the "outer" fields (upvars and - // discriminant) and the "promoted" fields. Promoted fields will - // get included in each variant that requested them in - // GeneratorLayout. - debug!("prefix = {:#?}", prefix); - let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields { - FieldPlacement::Arbitrary { mut offsets, memory_index } => { - let mut inverse_memory_index = invert_mapping(&memory_index); - - // "a" (`0..b_start`) and "b" (`b_start..`) correspond to - // "outer" and "promoted" fields respectively. - let b_start = (discr_index + 1) as u32; - let offsets_b = offsets.split_off(b_start as usize); - let offsets_a = offsets; - - // Disentangle the "a" and "b" components of `inverse_memory_index` - // by preserving the order but keeping only one disjoint "half" each. - // FIXME(eddyb) build a better abstraction for permutations, if possible. - let inverse_memory_index_b: Vec<_> = - inverse_memory_index.iter().filter_map(|&i| i.checked_sub(b_start)).collect(); - inverse_memory_index.retain(|&i| i < b_start); - let inverse_memory_index_a = inverse_memory_index; - - // Since `inverse_memory_index_{a,b}` each only refer to their - // respective fields, they can be safely inverted - let memory_index_a = invert_mapping(&inverse_memory_index_a); - let memory_index_b = invert_mapping(&inverse_memory_index_b); - - let outer_fields = - FieldPlacement::Arbitrary { offsets: offsets_a, memory_index: memory_index_a }; - (outer_fields, offsets_b, memory_index_b) - } - _ => bug!(), - }; - - let mut size = prefix.size; - let mut align = prefix.align; - let variants = info - .variant_fields - .iter_enumerated() - .map(|(index, variant_fields)| { - // Only include overlap-eligible fields when we compute our variant layout. - let variant_only_tys = variant_fields - .iter() - .filter(|local| match assignments[**local] { - Unassigned => bug!(), - Assigned(v) if v == index => true, - Assigned(_) => bug!("assignment does not match variant"), - Ineligible(_) => false, - }) - .map(|local| subst_field(info.field_tys[*local])); - - let mut variant = self.univariant_uninterned( - ty, - &variant_only_tys - .map(|ty| self.layout_of(ty)) - .collect::, _>>()?, - &ReprOptions::default(), - StructKind::Prefixed(prefix_size, prefix_align.abi), - )?; - variant.variants = Variants::Single { index }; - - let (offsets, memory_index) = match variant.fields { - FieldPlacement::Arbitrary { offsets, memory_index } => (offsets, memory_index), - _ => bug!(), - }; - - // Now, stitch the promoted and variant-only fields back together in - // the order they are mentioned by our GeneratorLayout. - // Because we only use some subset (that can differ between variants) - // of the promoted fields, we can't just pick those elements of the - // `promoted_memory_index` (as we'd end up with gaps). - // So instead, we build an "inverse memory_index", as if all of the - // promoted fields were being used, but leave the elements not in the - // subset as `INVALID_FIELD_IDX`, which we can filter out later to - // obtain a valid (bijective) mapping. - const INVALID_FIELD_IDX: u32 = !0; - let mut combined_inverse_memory_index = - vec![INVALID_FIELD_IDX; promoted_memory_index.len() + memory_index.len()]; - let mut offsets_and_memory_index = offsets.into_iter().zip(memory_index); - let combined_offsets = variant_fields - .iter() - .enumerate() - .map(|(i, local)| { - let (offset, memory_index) = match assignments[*local] { - Unassigned => bug!(), - Assigned(_) => { - let (offset, memory_index) = - offsets_and_memory_index.next().unwrap(); - (offset, promoted_memory_index.len() as u32 + memory_index) - } - Ineligible(field_idx) => { - let field_idx = field_idx.unwrap() as usize; - (promoted_offsets[field_idx], promoted_memory_index[field_idx]) - } - }; - combined_inverse_memory_index[memory_index as usize] = i as u32; - offset - }) - .collect(); - - // Remove the unused slots and invert the mapping to obtain the - // combined `memory_index` (also see previous comment). - combined_inverse_memory_index.retain(|&i| i != INVALID_FIELD_IDX); - let combined_memory_index = invert_mapping(&combined_inverse_memory_index); - - variant.fields = FieldPlacement::Arbitrary { - offsets: combined_offsets, - memory_index: combined_memory_index, - }; - - size = size.max(variant.size); - align = align.max(variant.align); - Ok(variant) - }) - .collect::, _>>()?; - - size = size.align_to(align.abi); - - let abi = if prefix.abi.is_uninhabited() || variants.iter().all(|v| v.abi.is_uninhabited()) - { - Abi::Uninhabited - } else { - Abi::Aggregate { sized: true } - }; - - let layout = tcx.intern_layout(LayoutDetails { - variants: Variants::Multiple { - discr, - discr_kind: DiscriminantKind::Tag, - discr_index, - variants, - }, - fields: outer_fields, - abi, - largest_niche: prefix.largest_niche, - size, - align, - }); - debug!("generator layout ({:?}): {:#?}", ty, layout); - Ok(layout) - } - - /// This is invoked by the `layout_raw` query to record the final - /// layout of each type. - #[inline(always)] - fn record_layout_for_printing(&self, layout: TyLayout<'tcx>) { - // If we are running with `-Zprint-type-sizes`, maybe record layouts - // for dumping later. - if self.tcx.sess.opts.debugging_opts.print_type_sizes { - self.record_layout_for_printing_outlined(layout) - } - } - - fn record_layout_for_printing_outlined(&self, layout: TyLayout<'tcx>) { - // Ignore layouts that are done with non-empty environments or - // non-monomorphic layouts, as the user only wants to see the stuff - // resulting from the final codegen session. - if layout.ty.has_param_types() || !self.param_env.caller_bounds.is_empty() { - return; - } - - // (delay format until we actually need it) - let record = |kind, packed, opt_discr_size, variants| { - let type_desc = format!("{:?}", layout.ty); - self.tcx.sess.code_stats.record_type_size( - kind, - type_desc, - layout.align.abi, - layout.size, - packed, - opt_discr_size, - variants, - ); - }; - - let adt_def = match layout.ty.kind { - ty::Adt(ref adt_def, _) => { - debug!("print-type-size t: `{:?}` process adt", layout.ty); - adt_def - } - - ty::Closure(..) => { - debug!("print-type-size t: `{:?}` record closure", layout.ty); - record(DataTypeKind::Closure, false, None, vec![]); - return; - } - - _ => { - debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); - return; - } - }; - - let adt_kind = adt_def.adt_kind(); - let adt_packed = adt_def.repr.pack.is_some(); - - let build_variant_info = |n: Option, flds: &[ast::Name], layout: TyLayout<'tcx>| { - let mut min_size = Size::ZERO; - let field_info: Vec<_> = flds - .iter() - .enumerate() - .map(|(i, &name)| match layout.field(self, i) { - Err(err) => { - bug!("no layout found for field {}: `{:?}`", name, err); - } - Ok(field_layout) => { - let offset = layout.fields.offset(i); - let field_end = offset + field_layout.size; - if min_size < field_end { - min_size = field_end; - } - FieldInfo { - name: name.to_string(), - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - } - } - }) - .collect(); - - VariantInfo { - name: n.map(|n| n.to_string()), - kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, - align: layout.align.abi.bytes(), - size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, - fields: field_info, - } - }; - - match layout.variants { - Variants::Single { index } => { - debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variants[index].ident); - if !adt_def.variants.is_empty() { - let variant_def = &adt_def.variants[index]; - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.ident.name).collect(); - record( - adt_kind.into(), - adt_packed, - None, - vec![build_variant_info(Some(variant_def.ident), &fields, layout)], - ); - } else { - // (This case arises for *empty* enums; so give it - // zero variants.) - record(adt_kind.into(), adt_packed, None, vec![]); - } - } - - Variants::Multiple { ref discr, ref discr_kind, .. } => { - debug!( - "print-type-size `{:#?}` adt general variants def {}", - layout.ty, - adt_def.variants.len() - ); - let variant_infos: Vec<_> = adt_def - .variants - .iter_enumerated() - .map(|(i, variant_def)| { - let fields: Vec<_> = - variant_def.fields.iter().map(|f| f.ident.name).collect(); - build_variant_info( - Some(variant_def.ident), - &fields, - layout.for_variant(self, i), - ) - }) - .collect(); - record( - adt_kind.into(), - adt_packed, - match discr_kind { - DiscriminantKind::Tag => Some(discr.value.size(self)), - _ => None, - }, - variant_infos, - ); - } - } - } -} - -/// Type size "skeleton", i.e., the only information determining a type's size. -/// While this is conservative, (aside from constant sizes, only pointers, -/// newtypes thereof and null pointer optimized enums are allowed), it is -/// enough to statically check common use cases of transmute. -#[derive(Copy, Clone, Debug)] -pub enum SizeSkeleton<'tcx> { - /// Any statically computable Layout. - Known(Size), - - /// A potentially-fat pointer. - Pointer { - /// If true, this pointer is never null. - non_zero: bool, - /// The type which determines the unsized metadata, if any, - /// of this pointer. Either a type parameter or a projection - /// depending on one, with regions erased. - tail: Ty<'tcx>, - }, -} - -impl<'tcx> SizeSkeleton<'tcx> { - pub fn compute( - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Result, LayoutError<'tcx>> { - debug_assert!(!ty.has_infer_types_or_consts()); - - // First try computing a static layout. - let err = match tcx.layout_of(param_env.and(ty)) { - Ok(layout) => { - return Ok(SizeSkeleton::Known(layout.size)); - } - Err(err) => err, - }; - - match ty.kind { - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - let non_zero = !ty.is_unsafe_ptr(); - let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - match tail.kind { - ty::Param(_) | ty::Projection(_) => { - debug_assert!(tail.has_param_types()); - Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(&tail) }) - } - _ => bug!( - "SizeSkeleton::compute({}): layout errored ({}), yet \ - tail `{}` is not a type parameter or a projection", - ty, - err, - tail - ), - } - } - - ty::Adt(def, substs) => { - // Only newtypes and enums w/ nullable pointer optimization. - if def.is_union() || def.variants.is_empty() || def.variants.len() > 2 { - return Err(err); - } - - // Get a zero-sized variant or a pointer newtype. - let zero_or_ptr_variant = |i| { - let i = VariantIdx::new(i); - let fields = def.variants[i] - .fields - .iter() - .map(|field| SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)); - let mut ptr = None; - for field in fields { - let field = field?; - match field { - SizeSkeleton::Known(size) => { - if size.bytes() > 0 { - return Err(err); - } - } - SizeSkeleton::Pointer { .. } => { - if ptr.is_some() { - return Err(err); - } - ptr = Some(field); - } - } - } - Ok(ptr) - }; - - let v0 = zero_or_ptr_variant(0)?; - // Newtype. - if def.variants.len() == 1 { - if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { - return Ok(SizeSkeleton::Pointer { - non_zero: non_zero - || match tcx.layout_scalar_valid_range(def.did) { - (Bound::Included(start), Bound::Unbounded) => start > 0, - (Bound::Included(start), Bound::Included(end)) => { - 0 < start && start < end - } - _ => false, - }, - tail, - }); - } else { - return Err(err); - } - } - - let v1 = zero_or_ptr_variant(1)?; - // Nullable pointer enum optimization. - match (v0, v1) { - (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) - | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => { - Ok(SizeSkeleton::Pointer { non_zero: false, tail }) - } - _ => Err(err), - } - } - - ty::Projection(_) | ty::Opaque(..) => { - let normalized = tcx.normalize_erasing_regions(param_env, ty); - if ty == normalized { - Err(err) - } else { - SizeSkeleton::compute(normalized, tcx, param_env) - } - } - - _ => Err(err), - } - } - - pub fn same_size(self, other: SizeSkeleton<'_>) -> bool { - match (self, other) { - (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b, - (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { - a == b - } - _ => false, - } - } -} - -pub trait HasTyCtxt<'tcx>: HasDataLayout { - fn tcx(&self) -> TyCtxt<'tcx>; -} - -pub trait HasParamEnv<'tcx> { - fn param_env(&self) -> ty::ParamEnv<'tcx>; -} - -impl<'tcx> HasDataLayout for TyCtxt<'tcx> { - fn data_layout(&self) -> &TargetDataLayout { - &self.data_layout - } -} - -impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - *self - } -} - -impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> { - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } -} - -impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> { - fn data_layout(&self) -> &TargetDataLayout { - self.tcx.data_layout() - } -} - -impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx.tcx() - } -} - -pub type TyLayout<'tcx> = ::rustc_target::abi::TyLayout<'tcx, Ty<'tcx>>; - -impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { - type Ty = Ty<'tcx>; - type TyLayout = Result, LayoutError<'tcx>>; - - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { - let param_env = self.param_env.with_reveal_all(); - let ty = self.tcx.normalize_erasing_regions(param_env, ty); - let details = self.tcx.layout_raw(param_env.and(ty))?; - let layout = TyLayout { ty, details }; - - // N.B., this recording is normally disabled; when enabled, it - // can however trigger recursive invocations of `layout_of`. - // Therefore, we execute it *after* the main query has - // completed, to avoid problems around recursive structures - // and the like. (Admittedly, I wasn't able to reproduce a problem - // here, but it seems like the right thing to do. -nmatsakis) - self.record_layout_for_printing(layout); - - Ok(layout) - } -} - -impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { - type Ty = Ty<'tcx>; - type TyLayout = Result, LayoutError<'tcx>>; - - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { - let param_env = self.param_env.with_reveal_all(); - let ty = self.tcx.normalize_erasing_regions(param_env, ty); - let details = self.tcx.layout_raw(param_env.and(ty))?; - let layout = TyLayout { ty, details }; - - // N.B., this recording is normally disabled; when enabled, it - // can however trigger recursive invocations of `layout_of`. - // Therefore, we execute it *after* the main query has - // completed, to avoid problems around recursive structures - // and the like. (Admittedly, I wasn't able to reproduce a problem - // here, but it seems like the right thing to do. -nmatsakis) - let cx = LayoutCx { tcx: *self.tcx, param_env: self.param_env }; - cx.record_layout_for_printing(layout); - - Ok(layout) - } -} - -// Helper (inherent) `layout_of` methods to avoid pushing `LayoutCx` to users. -impl TyCtxt<'tcx> { - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - #[inline] - pub fn layout_of( - self, - param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result, LayoutError<'tcx>> { - let cx = LayoutCx { tcx: self, param_env: param_env_and_ty.param_env }; - cx.layout_of(param_env_and_ty.value) - } -} - -impl ty::query::TyCtxtAt<'tcx> { - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - #[inline] - pub fn layout_of( - self, - param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result, LayoutError<'tcx>> { - let cx = LayoutCx { tcx: self.at(self.span), param_env: param_env_and_ty.param_env }; - cx.layout_of(param_env_and_ty.value) - } -} - -impl<'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> -where - C: LayoutOf, TyLayout: MaybeResult>> - + HasTyCtxt<'tcx> - + HasParamEnv<'tcx>, -{ - fn for_variant(this: TyLayout<'tcx>, cx: &C, variant_index: VariantIdx) -> TyLayout<'tcx> { - let details = match this.variants { - Variants::Single { index } - // If all variants but one are uninhabited, the variant layout is the enum layout. - if index == variant_index && - // Don't confuse variants of uninhabited enums with the enum itself. - // For more details see https://github.com/rust-lang/rust/issues/69763. - this.fields != FieldPlacement::Union(0) => - { - this.details - } - - Variants::Single { index } => { - // Deny calling for_variant more than once for non-Single enums. - if let Ok(layout) = cx.layout_of(this.ty).to_result() { - assert_eq!(layout.variants, Variants::Single { index }); - } - - let fields = match this.ty.kind { - ty::Adt(def, _) => def.variants[variant_index].fields.len(), - _ => bug!(), - }; - let tcx = cx.tcx(); - tcx.intern_layout(LayoutDetails { - variants: Variants::Single { index: variant_index }, - fields: FieldPlacement::Union(fields), - abi: Abi::Uninhabited, - largest_niche: None, - align: tcx.data_layout.i8_align, - size: Size::ZERO, - }) - } - - Variants::Multiple { ref variants, .. } => &variants[variant_index], - }; - - assert_eq!(details.variants, Variants::Single { index: variant_index }); - - TyLayout { ty: this.ty, details } - } - - fn field(this: TyLayout<'tcx>, cx: &C, i: usize) -> C::TyLayout { - let tcx = cx.tcx(); - let discr_layout = |discr: &Scalar| -> C::TyLayout { - let layout = LayoutDetails::scalar(cx, discr.clone()); - MaybeResult::from(Ok(TyLayout { - details: tcx.intern_layout(layout), - ty: discr.value.to_ty(tcx), - })) - }; - - cx.layout_of(match this.ty.kind { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::FnPtr(_) - | ty::Never - | ty::FnDef(..) - | ty::GeneratorWitness(..) - | ty::Foreign(..) - | ty::Dynamic(..) => bug!("TyLayout::field_type({:?}): not applicable", this), - - // Potentially-fat pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - assert!(i < this.fields.count()); - - // Reuse the fat `*T` type as its own thin pointer data field. - // This provides information about, e.g., DST struct pointees - // (which may have no non-DST form), and will work as long - // as the `Abi` or `FieldPlacement` is checked by users. - if i == 0 { - let nil = tcx.mk_unit(); - let ptr_ty = if this.ty.is_unsafe_ptr() { - tcx.mk_mut_ptr(nil) - } else { - tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) - }; - return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map( - |mut ptr_layout| { - ptr_layout.ty = this.ty; - ptr_layout - }, - )); - } - - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind { - ty::Slice(_) | ty::Str => tcx.types.usize, - ty::Dynamic(_, _) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) - /* FIXME: use actual fn pointers - Warning: naively computing the number of entries in the - vtable by counting the methods on the trait + methods on - all parent traits does not work, because some methods can - be not object safe and thus excluded from the vtable. - Increase this counter if you tried to implement this but - failed to do it without duplicating a lot of code from - other places in the compiler: 2 - tcx.mk_tup(&[ - tcx.mk_array(tcx.types.usize, 3), - tcx.mk_array(Option), - ]) - */ - } - _ => bug!("TyLayout::field_type({:?}): not applicable", this), - } - } - - // Arrays and slices. - ty::Array(element, _) | ty::Slice(element) => element, - ty::Str => tcx.types.u8, - - // Tuples, generators and closures. - ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().nth(i).unwrap(), - - ty::Generator(def_id, ref substs, _) => match this.variants { - Variants::Single { index } => substs - .as_generator() - .state_tys(def_id, tcx) - .nth(index.as_usize()) - .unwrap() - .nth(i) - .unwrap(), - Variants::Multiple { ref discr, discr_index, .. } => { - if i == discr_index { - return discr_layout(discr); - } - substs.as_generator().prefix_tys().nth(i).unwrap() - } - }, - - ty::Tuple(tys) => tys[i].expect_ty(), - - // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => this.ty.simd_type(tcx), - - // ADTs. - ty::Adt(def, substs) => { - match this.variants { - Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs), - - // Discriminant field for enums (where applicable). - Variants::Multiple { ref discr, .. } => { - assert_eq!(i, 0); - return discr_layout(discr); - } - } - } - - ty::Projection(_) - | ty::UnnormalizedProjection(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Opaque(..) - | ty::Param(_) - | ty::Infer(_) - | ty::Error => bug!("TyLayout::field_type: unexpected type `{}`", this.ty), - }) - } - - fn pointee_info_at(this: TyLayout<'tcx>, cx: &C, offset: Size) -> Option { - match this.ty.kind { - ty::RawPtr(mt) if offset.bytes() == 0 => { - cx.layout_of(mt.ty).to_result().ok().map(|layout| PointeeInfo { - size: layout.size, - align: layout.align.abi, - safe: None, - }) - } - - ty::Ref(_, ty, mt) if offset.bytes() == 0 => { - let tcx = cx.tcx(); - let is_freeze = ty.is_freeze(tcx, cx.param_env(), DUMMY_SP); - let kind = match mt { - hir::Mutability::Not => { - if is_freeze { - PointerKind::Frozen - } else { - PointerKind::Shared - } - } - hir::Mutability::Mut => { - // Previously we would only emit noalias annotations for LLVM >= 6 or in - // panic=abort mode. That was deemed right, as prior versions had many bugs - // in conjunction with unwinding, but later versions didn’t seem to have - // said issues. See issue #31681. - // - // Alas, later on we encountered a case where noalias would generate wrong - // code altogether even with recent versions of LLVM in *safe* code with no - // unwinding involved. See #54462. - // - // For now, do not enable mutable_noalias by default at all, while the - // issue is being figured out. - let mutable_noalias = - tcx.sess.opts.debugging_opts.mutable_noalias.unwrap_or(false); - if mutable_noalias { - PointerKind::UniqueBorrowed - } else { - PointerKind::Shared - } - } - }; - - cx.layout_of(ty).to_result().ok().map(|layout| PointeeInfo { - size: layout.size, - align: layout.align.abi, - safe: Some(kind), - }) - } - - _ => { - let mut data_variant = match this.variants { - // Within the discriminant field, only the niche itself is - // always initialized, so we only check for a pointer at its - // offset. - // - // If the niche is a pointer, it's either valid (according - // to its type), or null (which the niche field's scalar - // validity range encodes). This allows using - // `dereferenceable_or_null` for e.g., `Option<&T>`, and - // this will continue to work as long as we don't start - // using more niches than just null (e.g., the first page of - // the address space, or unaligned pointers). - Variants::Multiple { - discr_kind: DiscriminantKind::Niche { dataful_variant, .. }, - discr_index, - .. - } if this.fields.offset(discr_index) == offset => { - Some(this.for_variant(cx, dataful_variant)) - } - _ => Some(this), - }; - - if let Some(variant) = data_variant { - // We're not interested in any unions. - if let FieldPlacement::Union(_) = variant.fields { - data_variant = None; - } - } - - let mut result = None; - - if let Some(variant) = data_variant { - let ptr_end = offset + Pointer.size(cx); - for i in 0..variant.fields.count() { - let field_start = variant.fields.offset(i); - if field_start <= offset { - let field = variant.field(cx, i); - result = field.to_result().ok().and_then(|field| { - if ptr_end <= field_start + field.size { - // We found the right field, look inside it. - field.pointee_info_at(cx, offset - field_start) - } else { - None - } - }); - if result.is_some() { - break; - } - } - } - } - - // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. - if let Some(ref mut pointee) = result { - if let ty::Adt(def, _) = this.ty.kind { - if def.is_box() && offset.bytes() == 0 { - pointee.safe = Some(PointerKind::UniqueOwned); - } - } - } - - result - } - } - } -} - -impl<'a, 'tcx> HashStable> for LayoutError<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - use crate::ty::layout::LayoutError::*; - mem::discriminant(self).hash_stable(hcx, hasher); - - match *self { - Unknown(t) | SizeOverflow(t) => t.hash_stable(hcx, hasher), - } - } -} - -impl<'tcx> ty::Instance<'tcx> { - // NOTE(eddyb) this is private to avoid using it from outside of - // `FnAbi::of_instance` - any other uses are either too high-level - // for `Instance` (e.g. typeck would use `Ty::fn_sig` instead), - // or should go through `FnAbi` instead, to avoid losing any - // adjustments `FnAbi::of_instance` might be performing. - fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { - let ty = self.monomorphic_ty(tcx); - match ty.kind { - ty::FnDef(..) | - // Shims currently have type FnPtr. Not sure this should remain. - ty::FnPtr(_) => { - let mut sig = ty.fn_sig(tcx); - if let ty::InstanceDef::VtableShim(..) = self.def { - // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. - sig = sig.map_bound(|mut sig| { - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]); - sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output); - sig - }); - } - sig - } - ty::Closure(def_id, substs) => { - let sig = substs.as_closure().sig(); - - let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| tcx.mk_fn_sig( - iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi - )) - } - ty::Generator(_, substs, _) => { - let sig = substs.as_generator().poly_sig(); - - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); - let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); - - let pin_did = tcx.lang_items().pin_type().unwrap(); - let pin_adt_ref = tcx.adt_def(pin_did); - let pin_substs = tcx.intern_substs(&[env_ty.into()]); - let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs); - - sig.map_bound(|sig| { - let state_did = tcx.lang_items().gen_state().unwrap(); - let state_adt_ref = tcx.adt_def(state_did); - let state_substs = tcx.intern_substs(&[ - sig.yield_ty.into(), - sig.return_ty.into(), - ]); - let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - - tcx.mk_fn_sig( - [env_ty, sig.resume_ty].iter(), - &ret_ty, - false, - hir::Unsafety::Normal, - rustc_target::spec::abi::Abi::Rust - ) - }) - } - _ => bug!("unexpected type {:?} in Instance::fn_sig", ty) - } - } -} - -pub trait FnAbiExt<'tcx, C> -where - C: LayoutOf, TyLayout = TyLayout<'tcx>> - + HasDataLayout - + HasTargetSpec - + HasTyCtxt<'tcx> - + HasParamEnv<'tcx>, -{ - /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. - /// - /// NB: this doesn't handle virtual calls - those should use `FnAbi::of_instance` - /// instead, where the instance is a `InstanceDef::Virtual`. - fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; - - /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for - /// direct calls to an `fn`. - /// - /// NB: that includes virtual calls, which are represented by "direct calls" - /// to a `InstanceDef::Virtual` instance (of `::fn`). - fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; - - fn new_internal( - cx: &C, - sig: ty::PolyFnSig<'tcx>, - extra_args: &[Ty<'tcx>], - caller_location: Option>, - mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, - ) -> Self; - fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi); -} - -impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>> -where - C: LayoutOf, TyLayout = TyLayout<'tcx>> - + HasDataLayout - + HasTargetSpec - + HasTyCtxt<'tcx> - + HasParamEnv<'tcx>, -{ - fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { - call::FnAbi::new_internal(cx, sig, extra_args, None, |ty, _| ArgAbi::new(cx.layout_of(ty))) - } - - fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { - let sig = instance.fn_sig_for_fn_abi(cx.tcx()); - - let caller_location = if instance.def.requires_caller_location(cx.tcx()) { - Some(cx.tcx().caller_location_ty()) - } else { - None - }; - - call::FnAbi::new_internal(cx, sig, extra_args, caller_location, |ty, arg_idx| { - let mut layout = cx.layout_of(ty); - // Don't pass the vtable, it's not an argument of the virtual fn. - // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` - // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen - if let (ty::InstanceDef::Virtual(..), Some(0)) = (&instance.def, arg_idx) { - let fat_pointer_ty = if layout.is_unsized() { - // unsized `self` is passed as a pointer to `self` - // FIXME (mikeyhew) change this to use &own if it is ever added to the language - cx.tcx().mk_mut_ptr(layout.ty) - } else { - match layout.abi { - Abi::ScalarPair(..) => (), - _ => bug!("receiver type has unsupported layout: {:?}", layout), - } - - // In the case of Rc, we need to explicitly pass a *mut RcBox - // with a Scalar (not ScalarPair) ABI. This is a hack that is understood - // elsewhere in the compiler as a method on a `dyn Trait`. - // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we - // get a built-in pointer type - let mut fat_pointer_layout = layout; - 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() - && !fat_pointer_layout.ty.is_region_ptr() - { - for i in 0..fat_pointer_layout.fields.count() { - let field_layout = fat_pointer_layout.field(cx, i); - - if !field_layout.is_zst() { - fat_pointer_layout = field_layout; - continue 'descend_newtypes; - } - } - - bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout); - } - - fat_pointer_layout.ty - }; - - // we now have a type like `*mut RcBox` - // change its layout to that of `*mut ()`, a thin pointer, but keep the same type - // this is understood as a special case elsewhere in the compiler - let unit_pointer_ty = cx.tcx().mk_mut_ptr(cx.tcx().mk_unit()); - layout = cx.layout_of(unit_pointer_ty); - layout.ty = fat_pointer_ty; - } - ArgAbi::new(layout) - }) - } - - fn new_internal( - cx: &C, - sig: ty::PolyFnSig<'tcx>, - extra_args: &[Ty<'tcx>], - caller_location: Option>, - mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, - ) -> Self { - debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args); - - let sig = cx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - - use rustc_target::spec::abi::Abi::*; - let conv = match cx.tcx().sess.target.target.adjust_abi(sig.abi) { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust, - - // It's the ABI's job to select this, not ours. - System => bug!("system abi should be selected elsewhere"), - EfiApi => bug!("eficall abi should be selected elsewhere"), - - Stdcall => Conv::X86Stdcall, - Fastcall => Conv::X86Fastcall, - Vectorcall => Conv::X86VectorCall, - Thiscall => Conv::X86ThisCall, - C => Conv::C, - Unadjusted => Conv::C, - Win64 => Conv::X86_64Win64, - SysV64 => Conv::X86_64SysV, - Aapcs => Conv::ArmAapcs, - PtxKernel => Conv::PtxKernel, - Msp430Interrupt => Conv::Msp430Intr, - X86Interrupt => Conv::X86Intr, - AmdGpuKernel => Conv::AmdGpuKernel, - - // These API constants ought to be more specific... - Cdecl => Conv::C, - }; - - let mut inputs = sig.inputs(); - let extra_args = if sig.abi == RustCall { - assert!(!sig.c_variadic && extra_args.is_empty()); - - if let Some(input) = sig.inputs().last() { - if let ty::Tuple(tupled_arguments) = input.kind { - inputs = &sig.inputs()[0..sig.inputs().len() - 1]; - tupled_arguments.iter().map(|k| k.expect_ty()).collect() - } else { - bug!( - "argument to function with \"rust-call\" ABI \ - is not a tuple" - ); - } - } else { - bug!( - "argument to function with \"rust-call\" ABI \ - is not a tuple" - ); - } - } else { - assert!(sig.c_variadic || extra_args.is_empty()); - extra_args.to_vec() - }; - - let target = &cx.tcx().sess.target.target; - let target_env_gnu_like = matches!(&target.target_env[..], "gnu" | "musl"); - let win_x64_gnu = - target.target_os == "windows" && target.arch == "x86_64" && target.target_env == "gnu"; - let linux_s390x_gnu_like = - target.target_os == "linux" && target.arch == "s390x" && target_env_gnu_like; - let linux_sparc64_gnu_like = - target.target_os == "linux" && target.arch == "sparc64" && target_env_gnu_like; - let linux_powerpc_gnu_like = - target.target_os == "linux" && target.arch == "powerpc" && target_env_gnu_like; - let rust_abi = match sig.abi { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, - _ => false, - }; - - // Handle safe Rust thin and fat pointers. - let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, - scalar: &Scalar, - layout: TyLayout<'tcx>, - offset: Size, - is_return: bool| { - // Booleans are always an i1 that needs to be zero-extended. - if scalar.is_bool() { - attrs.set(ArgAttribute::ZExt); - return; - } - - // Only pointer types handled below. - if scalar.value != Pointer { - return; - } - - if scalar.valid_range.start() < scalar.valid_range.end() { - if *scalar.valid_range.start() > 0 { - attrs.set(ArgAttribute::NonNull); - } - } - - if let Some(pointee) = layout.pointee_info_at(cx, offset) { - if let Some(kind) = pointee.safe { - attrs.pointee_align = Some(pointee.align); - - // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable - // for the entire duration of the function as they can be deallocated - // any time. Set their valid size to 0. - attrs.pointee_size = match kind { - PointerKind::UniqueOwned => Size::ZERO, - _ => pointee.size, - }; - - // `Box` pointer parameters never alias because ownership is transferred - // `&mut` pointer parameters never alias other parameters, - // or mutable global data - // - // `&T` where `T` contains no `UnsafeCell` is immutable, - // and can be marked as both `readonly` and `noalias`, as - // LLVM's definition of `noalias` is based solely on memory - // dependencies rather than pointer equality - let no_alias = match kind { - PointerKind::Shared => false, - PointerKind::UniqueOwned => true, - PointerKind::Frozen | PointerKind::UniqueBorrowed => !is_return, - }; - if no_alias { - attrs.set(ArgAttribute::NoAlias); - } - - if kind == PointerKind::Frozen && !is_return { - attrs.set(ArgAttribute::ReadOnly); - } - } - } - }; - - let arg_of = |ty: Ty<'tcx>, arg_idx: Option| { - let is_return = arg_idx.is_none(); - let mut arg = mk_arg_type(ty, arg_idx); - if arg.layout.is_zst() { - // For some forsaken reason, x86_64-pc-windows-gnu - // doesn't ignore zero-sized struct arguments. - // The same is true for {s390x,sparc64,powerpc}-unknown-linux-{gnu,musl}. - if is_return - || rust_abi - || (!win_x64_gnu - && !linux_s390x_gnu_like - && !linux_sparc64_gnu_like - && !linux_powerpc_gnu_like) - { - arg.mode = PassMode::Ignore; - } - } - - // FIXME(eddyb) other ABIs don't have logic for scalar pairs. - if !is_return && rust_abi { - if let Abi::ScalarPair(ref a, ref b) = arg.layout.abi { - let mut a_attrs = ArgAttributes::new(); - let mut b_attrs = ArgAttributes::new(); - adjust_for_rust_scalar(&mut a_attrs, a, arg.layout, Size::ZERO, false); - adjust_for_rust_scalar( - &mut b_attrs, - b, - arg.layout, - a.value.size(cx).align_to(b.value.align(cx).abi), - false, - ); - arg.mode = PassMode::Pair(a_attrs, b_attrs); - return arg; - } - } - - if let Abi::Scalar(ref scalar) = arg.layout.abi { - if let PassMode::Direct(ref mut attrs) = arg.mode { - adjust_for_rust_scalar(attrs, scalar, arg.layout, Size::ZERO, is_return); - } - } - - arg - }; - - let mut fn_abi = FnAbi { - ret: arg_of(sig.output(), None), - args: inputs - .iter() - .cloned() - .chain(extra_args) - .chain(caller_location) - .enumerate() - .map(|(i, ty)| arg_of(ty, Some(i))) - .collect(), - c_variadic: sig.c_variadic, - fixed_count: inputs.len(), - conv, - }; - fn_abi.adjust_for_abi(cx, sig.abi); - fn_abi - } - - fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi) { - if abi == SpecAbi::Unadjusted { - return; - } - - if abi == SpecAbi::Rust - || abi == SpecAbi::RustCall - || abi == SpecAbi::RustIntrinsic - || abi == SpecAbi::PlatformIntrinsic - { - let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { - if arg.is_ignore() { - return; - } - - match arg.layout.abi { - Abi::Aggregate { .. } => {} - - // This is a fun case! The gist of what this is doing is - // that we want callers and callees to always agree on the - // ABI of how they pass SIMD arguments. If we were to *not* - // make these arguments indirect then they'd be immediates - // in LLVM, which means that they'd used whatever the - // appropriate ABI is for the callee and the caller. That - // means, for example, if the caller doesn't have AVX - // enabled but the callee does, then passing an AVX argument - // across this boundary would cause corrupt data to show up. - // - // This problem is fixed by unconditionally passing SIMD - // arguments through memory between callers and callees - // which should get them all to agree on ABI regardless of - // target feature sets. Some more information about this - // issue can be found in #44367. - // - // Note that the platform intrinsic ABI is exempt here as - // that's how we connect up to LLVM and it's unstable - // anyway, we control all calls to it in libstd. - Abi::Vector { .. } - if abi != SpecAbi::PlatformIntrinsic - && cx.tcx().sess.target.target.options.simd_types_indirect => - { - arg.make_indirect(); - return; - } - - _ => return, - } - - let size = arg.layout.size; - if arg.layout.is_unsized() || size > Pointer.size(cx) { - arg.make_indirect(); - } else { - // We want to pass small aggregates as immediates, but using - // a LLVM aggregate type for this leads to bad optimizations, - // so we pick an appropriately sized integer type instead. - arg.cast_to(Reg { kind: RegKind::Integer, size }); - } - }; - fixup(&mut self.ret); - for arg in &mut self.args { - fixup(arg); - } - if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { - attrs.set(ArgAttribute::StructRet); - } - return; - } - - if let Err(msg) = self.adjust_for_cabi(cx, abi) { - cx.tcx().sess.fatal(&msg); - } - } -} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs deleted file mode 100644 index 78b0ee271fb74..0000000000000 --- a/src/librustc/ty/mod.rs +++ /dev/null @@ -1,3184 +0,0 @@ -// ignore-tidy-filelength - -pub use self::fold::{TypeFoldable, TypeVisitor}; -pub use self::AssocItemContainer::*; -pub use self::BorrowKind::*; -pub use self::IntVarValue::*; -pub use self::Variance::*; - -use crate::arena::Arena; -use crate::hir::exports::ExportMap; -use crate::hir::map as hir_map; -use crate::ich::Fingerprint; -use crate::ich::StableHashingContext; -use crate::infer::canonical::Canonical; -use crate::middle::cstore::CrateStoreDyn; -use crate::middle::lang_items::{FnMutTraitLangItem, FnOnceTraitLangItem, FnTraitLangItem}; -use crate::middle::resolve_lifetime::ObjectLifetimeDefault; -use crate::mir::interpret::ErrorHandled; -use crate::mir::GeneratorLayout; -use crate::mir::ReadOnlyBodyAndCache; -use crate::traits::{self, Reveal}; -use crate::ty; -use crate::ty::layout::VariantIdx; -use crate::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use crate::ty::util::{Discr, IntTypeExt}; -use crate::ty::walk::TypeWalker; -use rustc_ast::ast::{self, Ident, Name}; -use rustc_ast::node_id::{NodeId, NodeMap, NodeSet}; -use rustc_attr as attr; -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sorted_map::SortedIndexMultiMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{self, par_iter, Lrc, ParallelIterator}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; -use rustc_hir::{Constness, GlobMap, Node, TraitMap}; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_macros::HashStable; -use rustc_serialize::{self, Encodable, Encoder}; -use rustc_session::DataTypeKind; -use rustc_span::hygiene::ExpnId; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; -use rustc_target::abi::Align; - -use std::cell::RefCell; -use std::cmp::{self, Ordering}; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::ops::Deref; -use std::ops::Range; -use std::slice; -use std::{mem, ptr}; - -pub use self::sty::BoundRegion::*; -pub use self::sty::InferTy::*; -pub use self::sty::RegionKind; -pub use self::sty::RegionKind::*; -pub use self::sty::TyKind::*; -pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNERMOST}; -pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; -pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; -pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; -pub use self::sty::{Const, ConstKind, ExistentialProjection, PolyExistentialProjection}; -pub use self::sty::{ConstVid, FloatVid, IntVid, RegionVid, TyVid}; -pub use self::sty::{ExistentialPredicate, InferConst, InferTy, ParamConst, ParamTy, ProjectionTy}; -pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; -pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; -pub use crate::ty::diagnostics::*; - -pub use self::binding::BindingMode; -pub use self::binding::BindingMode::*; - -pub use self::context::{keep_local, tls, FreeRegionInfo, TyCtxt}; -pub use self::context::{ - CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, ResolvedOpaqueTy, - UserType, UserTypeAnnotationIndex, -}; -pub use self::context::{ - CtxtInterners, GeneratorInteriorTypeCause, GlobalCtxt, Lift, TypeckTables, -}; - -pub use self::instance::RESOLVE_INSTANCE; -pub use self::instance::{Instance, InstanceDef}; - -pub use self::trait_def::TraitDef; - -pub use self::query::queries; - -pub mod adjustment; -pub mod binding; -pub mod cast; -#[macro_use] -pub mod codec; -pub mod _match; -mod erase_regions; -pub mod error; -pub mod fast_reject; -pub mod flags; -pub mod fold; -pub mod free_region_map; -pub mod inhabitedness; -pub mod layout; -pub mod normalize_erasing_regions; -pub mod outlives; -pub mod print; -pub mod query; -pub mod relate; -pub mod steal; -pub mod subst; -pub mod trait_def; -pub mod util; -pub mod walk; - -mod context; -mod diagnostics; -mod instance; -mod structural_impls; -mod sty; - -// Data types - -pub struct ResolverOutputs { - pub definitions: hir_map::Definitions, - pub cstore: Box, - pub extern_crate_map: NodeMap, - pub trait_map: TraitMap, - pub maybe_unused_trait_imports: NodeSet, - pub maybe_unused_extern_crates: Vec<(NodeId, Span)>, - pub export_map: ExportMap, - pub glob_map: GlobMap, - /// Extern prelude entries. The value is `true` if the entry was introduced - /// via `extern crate` item and not `--extern` option or compiler built-in. - pub extern_prelude: FxHashMap, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)] -pub enum AssocItemContainer { - TraitContainer(DefId), - ImplContainer(DefId), -} - -impl AssocItemContainer { - /// Asserts that this is the `DefId` of an associated item declared - /// in a trait, and returns the trait `DefId`. - pub fn assert_trait(&self) -> DefId { - match *self { - TraitContainer(id) => id, - _ => bug!("associated item has wrong container type: {:?}", self), - } - } - - pub fn id(&self) -> DefId { - match *self { - TraitContainer(id) => id, - ImplContainer(id) => id, - } - } -} - -/// The "header" of an impl is everything outside the body: a Self type, a trait -/// ref (in the case of a trait impl), and a set of predicates (from the -/// bounds / where-clauses). -#[derive(Clone, Debug, TypeFoldable)] -pub struct ImplHeader<'tcx> { - pub impl_def_id: DefId, - pub self_ty: Ty<'tcx>, - pub trait_ref: Option>, - pub predicates: Vec>, -} - -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub enum ImplPolarity { - /// `impl Trait for Type` - Positive, - /// `impl !Trait for Type` - Negative, - /// `#[rustc_reservation_impl] impl Trait for Type` - /// - /// This is a "stability hack", not a real Rust feature. - /// See #64631 for details. - Reservation, -} - -#[derive(Copy, Clone, Debug, PartialEq, HashStable)] -pub struct AssocItem { - pub def_id: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, - pub kind: AssocKind, - pub vis: Visibility, - pub defaultness: hir::Defaultness, - pub container: AssocItemContainer, - - /// Whether this is a method with an explicit self - /// as its first argument, allowing method calls. - pub method_has_self_argument: bool, -} - -#[derive(Copy, Clone, PartialEq, Debug, HashStable)] -pub enum AssocKind { - Const, - Method, - OpaqueTy, - Type, -} - -impl AssocKind { - pub fn suggestion_descr(&self) -> &'static str { - match self { - ty::AssocKind::Method => "method call", - ty::AssocKind::Type | ty::AssocKind::OpaqueTy => "associated type", - ty::AssocKind::Const => "associated constant", - } - } - - pub fn namespace(&self) -> Namespace { - match *self { - ty::AssocKind::OpaqueTy | ty::AssocKind::Type => Namespace::TypeNS, - ty::AssocKind::Const | ty::AssocKind::Method => Namespace::ValueNS, - } - } -} - -impl AssocItem { - pub fn def_kind(&self) -> DefKind { - match self.kind { - AssocKind::Const => DefKind::AssocConst, - AssocKind::Method => DefKind::AssocFn, - AssocKind::Type => DefKind::AssocTy, - AssocKind::OpaqueTy => DefKind::AssocOpaqueTy, - } - } - - /// Tests whether the associated item admits a non-trivial implementation - /// for ! - pub fn relevant_for_never(&self) -> bool { - match self.kind { - AssocKind::OpaqueTy | AssocKind::Const | AssocKind::Type => true, - // FIXME(canndrew): Be more thorough here, check if any argument is uninhabited. - AssocKind::Method => !self.method_has_self_argument, - } - } - - pub fn signature(&self, tcx: TyCtxt<'_>) -> String { - match self.kind { - ty::AssocKind::Method => { - // We skip the binder here because the binder would deanonymize all - // late-bound regions, and we don't want method signatures to show up - // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound - // regions just fine, showing `fn(&MyType)`. - tcx.fn_sig(self.def_id).skip_binder().to_string() - } - ty::AssocKind::Type => format!("type {};", self.ident), - // FIXME(type_alias_impl_trait): we should print bounds here too. - ty::AssocKind::OpaqueTy => format!("type {};", self.ident), - ty::AssocKind::Const => { - format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) - } - } - } -} - -/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. -/// -/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since -/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is -/// done only on items with the same name. -#[derive(Debug, Clone, PartialEq, HashStable)] -pub struct AssociatedItems { - items: SortedIndexMultiMap, -} - -impl AssociatedItems { - /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. - pub fn new(items_in_def_order: impl IntoIterator) -> Self { - let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); - AssociatedItems { items } - } - - /// Returns a slice of associated items in the order they were defined. - /// - /// New code should avoid relying on definition order. If you need a particular associated item - /// for a known trait, make that trait a lang item instead of indexing this array. - pub fn in_definition_order(&self) -> impl '_ + Iterator { - self.items.iter().map(|(_, v)| v) - } - - /// Returns an iterator over all associated items with the given name, ignoring hygiene. - pub fn filter_by_name_unhygienic( - &self, - name: Symbol, - ) -> impl '_ + Iterator { - self.items.get_by_key(&name) - } - - /// Returns an iterator over all associated items with the given name. - /// - /// Multiple items may have the same name if they are in different `Namespace`s. For example, - /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` - /// methods below if you know which item you are looking for. - pub fn filter_by_name( - &'a self, - tcx: TyCtxt<'a>, - ident: Ident, - parent_def_id: DefId, - ) -> impl 'a + Iterator { - self.filter_by_name_unhygienic(ident.name) - .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } - - /// Returns the associated item with the given name and `AssocKind`, if one exists. - pub fn find_by_name_and_kind( - &self, - tcx: TyCtxt<'_>, - ident: Ident, - kind: AssocKind, - parent_def_id: DefId, - ) -> Option<&ty::AssocItem> { - self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind == kind) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } - - /// Returns the associated item with the given name in the given `Namespace`, if one exists. - pub fn find_by_name_and_namespace( - &self, - tcx: TyCtxt<'_>, - ident: Ident, - ns: Namespace, - parent_def_id: DefId, - ) -> Option<&ty::AssocItem> { - self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind.namespace() == ns) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable, HashStable)] -pub enum Visibility { - /// Visible everywhere (including in other crates). - Public, - /// Visible only in the given crate-local module. - Restricted(DefId), - /// Not visible anywhere in the local crate. This is the visibility of private external items. - Invisible, -} - -pub trait DefIdTree: Copy { - fn parent(self, id: DefId) -> Option; - - fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { - if descendant.krate != ancestor.krate { - return false; - } - - while descendant != ancestor { - match self.parent(descendant) { - Some(parent) => descendant = parent, - None => return false, - } - } - true - } -} - -impl<'tcx> DefIdTree for TyCtxt<'tcx> { - fn parent(self, id: DefId) -> Option { - self.def_key(id).parent.map(|index| DefId { index, ..id }) - } -} - -impl Visibility { - pub fn from_hir(visibility: &hir::Visibility<'_>, id: hir::HirId, tcx: TyCtxt<'_>) -> Self { - match visibility.node { - hir::VisibilityKind::Public => Visibility::Public, - hir::VisibilityKind::Crate(_) => Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)), - hir::VisibilityKind::Restricted { ref path, .. } => match path.res { - // If there is no resolution, `resolve` will have already reported an error, so - // assume that the visibility is public to avoid reporting more privacy errors. - Res::Err => Visibility::Public, - def => Visibility::Restricted(def.def_id()), - }, - hir::VisibilityKind::Inherited => { - Visibility::Restricted(tcx.parent_module(id).to_def_id()) - } - } - } - - /// Returns `true` if an item with this visibility is accessible from the given block. - pub fn is_accessible_from(self, module: DefId, tree: T) -> bool { - let restriction = match self { - // Public items are visible everywhere. - Visibility::Public => return true, - // Private items from other crates are visible nowhere. - Visibility::Invisible => return false, - // Restricted items are visible in an arbitrary local module. - Visibility::Restricted(other) if other.krate != module.krate => return false, - Visibility::Restricted(module) => module, - }; - - tree.is_descendant_of(module, restriction) - } - - /// Returns `true` if this visibility is at least as accessible as the given visibility - pub fn is_at_least(self, vis: Visibility, tree: T) -> bool { - let vis_restriction = match vis { - Visibility::Public => return self == Visibility::Public, - Visibility::Invisible => return true, - Visibility::Restricted(module) => module, - }; - - self.is_accessible_from(vis_restriction, tree) - } - - // Returns `true` if this item is visible anywhere in the local crate. - pub fn is_visible_locally(self) -> bool { - match self { - Visibility::Public => true, - Visibility::Restricted(def_id) => def_id.is_local(), - Visibility::Invisible => false, - } - } -} - -#[derive(Copy, Clone, PartialEq, RustcDecodable, RustcEncodable, HashStable)] -pub enum Variance { - Covariant, // T <: T iff A <: B -- e.g., function return type - Invariant, // T <: T iff B == A -- e.g., type of mutable cell - Contravariant, // T <: T iff B <: A -- e.g., function param type - Bivariant, // T <: T -- e.g., unused type parameter -} - -/// The crate variances map is computed during typeck and contains the -/// variance of every item in the local crate. You should not use it -/// directly, because to do so will make your pass dependent on the -/// HIR of every item in the local crate. Instead, use -/// `tcx.variances_of()` to get the variance for a *particular* -/// item. -#[derive(HashStable)] -pub struct CrateVariancesMap<'tcx> { - /// For each item with generics, maps to a vector of the variance - /// of its generics. If an item has no generics, it will have no - /// entry. - pub variances: FxHashMap, -} - -impl Variance { - /// `a.xform(b)` combines the variance of a context with the - /// variance of a type with the following meaning. If we are in a - /// context with variance `a`, and we encounter a type argument in - /// a position with variance `b`, then `a.xform(b)` is the new - /// variance with which the argument appears. - /// - /// Example 1: - /// - /// *mut Vec - /// - /// Here, the "ambient" variance starts as covariant. `*mut T` is - /// invariant with respect to `T`, so the variance in which the - /// `Vec` appears is `Covariant.xform(Invariant)`, which - /// yields `Invariant`. Now, the type `Vec` is covariant with - /// respect to its type argument `T`, and hence the variance of - /// the `i32` here is `Invariant.xform(Covariant)`, which results - /// (again) in `Invariant`. - /// - /// Example 2: - /// - /// fn(*const Vec, *mut Vec` appears is - /// `Contravariant.xform(Covariant)` or `Contravariant`. The same - /// is true for its `i32` argument. In the `*mut T` case, the - /// variance of `Vec` is `Contravariant.xform(Invariant)`, - /// and hence the outermost type is `Invariant` with respect to - /// `Vec` (and its `i32` argument). - /// - /// Source: Figure 1 of "Taming the Wildcards: - /// Combining Definition- and Use-Site Variance" published in PLDI'11. - pub fn xform(self, v: ty::Variance) -> ty::Variance { - match (self, v) { - // Figure 1, column 1. - (ty::Covariant, ty::Covariant) => ty::Covariant, - (ty::Covariant, ty::Contravariant) => ty::Contravariant, - (ty::Covariant, ty::Invariant) => ty::Invariant, - (ty::Covariant, ty::Bivariant) => ty::Bivariant, - - // Figure 1, column 2. - (ty::Contravariant, ty::Covariant) => ty::Contravariant, - (ty::Contravariant, ty::Contravariant) => ty::Covariant, - (ty::Contravariant, ty::Invariant) => ty::Invariant, - (ty::Contravariant, ty::Bivariant) => ty::Bivariant, - - // Figure 1, column 3. - (ty::Invariant, _) => ty::Invariant, - - // Figure 1, column 4. - (ty::Bivariant, _) => ty::Bivariant, - } - } -} - -// Contains information needed to resolve types and (in the future) look up -// the types of AST nodes. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct CReaderCacheKey { - pub cnum: CrateNum, - pub pos: usize, -} - -bitflags! { - /// Flags that we track on types. These flags are propagated upwards - /// through the type during type construction, so that we can quickly check - /// whether the type has various kinds of types in it without recursing - /// over the type itself. - pub struct TypeFlags: u32 { - // Does this have parameters? Used to determine whether substitution is - // required. - /// Does this have [Param]? - const HAS_TY_PARAM = 1 << 0; - /// Does this have [ReEarlyBound]? - const HAS_RE_PARAM = 1 << 1; - /// Does this have [ConstKind::Param]? - const HAS_CT_PARAM = 1 << 2; - - const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_RE_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits; - - /// Does this have [Infer]? - const HAS_TY_INFER = 1 << 3; - /// Does this have [ReVar]? - const HAS_RE_INFER = 1 << 4; - /// Does this have [ConstKind::Infer]? - const HAS_CT_INFER = 1 << 5; - - /// Does this have inference variables? Used to determine whether - /// inference is required. - const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_RE_INFER.bits - | TypeFlags::HAS_CT_INFER.bits; - - /// Does this have [Placeholder]? - const HAS_TY_PLACEHOLDER = 1 << 6; - /// Does this have [RePlaceholder]? - const HAS_RE_PLACEHOLDER = 1 << 7; - /// Does this have [ConstKind::Placeholder]? - const HAS_CT_PLACEHOLDER = 1 << 8; - - /// `true` if there are "names" of regions and so forth - /// that are local to a particular fn/inferctxt - const HAS_FREE_LOCAL_REGIONS = 1 << 9; - - /// `true` if there are "names" of types and regions and so forth - /// that are local to a particular fn - const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits - | TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_CT_INFER.bits - | TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits - | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; - - /// Does this have [Projection] or [UnnormalizedProjection]? - const HAS_TY_PROJECTION = 1 << 10; - /// Does this have [Opaque]? - const HAS_TY_OPAQUE = 1 << 11; - /// Does this have [ConstKind::Unevaluated]? - const HAS_CT_PROJECTION = 1 << 12; - - /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits - | TypeFlags::HAS_TY_OPAQUE.bits - | TypeFlags::HAS_CT_PROJECTION.bits; - - /// Present if the type belongs in a local type context. - /// Set for placeholders and inference variables that are not "Fresh". - const KEEP_IN_LOCAL_TCX = 1 << 13; - - /// Is an error type reachable? - const HAS_TY_ERR = 1 << 14; - - /// Does this have any region that "appears free" in the type? - /// Basically anything but [ReLateBound] and [ReErased]. - const HAS_FREE_REGIONS = 1 << 15; - - /// Does this have any [ReLateBound] regions? Used to check - /// if a global bound is safe to evaluate. - const HAS_RE_LATE_BOUND = 1 << 16; - - /// Does this have any [ReErased] regions? - const HAS_RE_ERASED = 1 << 17; - - /// Flags representing the nominal content of a type, - /// computed by FlagsComputation. If you add a new nominal - /// flag, it should be added here too. - const NOMINAL_FLAGS = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_RE_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits - | TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_RE_INFER.bits - | TypeFlags::HAS_CT_INFER.bits - | TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_RE_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits - | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits - | TypeFlags::HAS_TY_PROJECTION.bits - | TypeFlags::HAS_TY_OPAQUE.bits - | TypeFlags::HAS_CT_PROJECTION.bits - | TypeFlags::KEEP_IN_LOCAL_TCX.bits - | TypeFlags::HAS_TY_ERR.bits - | TypeFlags::HAS_FREE_REGIONS.bits - | TypeFlags::HAS_RE_LATE_BOUND.bits - | TypeFlags::HAS_RE_ERASED.bits; - } -} - -#[allow(rustc::usage_of_ty_tykind)] -pub struct TyS<'tcx> { - pub kind: TyKind<'tcx>, - pub flags: TypeFlags, - - /// This is a kind of confusing thing: it stores the smallest - /// binder such that - /// - /// (a) the binder itself captures nothing but - /// (b) all the late-bound things within the type are captured - /// by some sub-binder. - /// - /// So, for a type without any late-bound things, like `u32`, this - /// will be *innermost*, because that is the innermost binder that - /// captures nothing. But for a type `&'D u32`, where `'D` is a - /// late-bound region with De Bruijn index `D`, this would be `D + 1` - /// -- the binder itself does not capture `D`, but `D` is captured - /// by an inner binder. - /// - /// We call this concept an "exclusive" binder `D` because all - /// De Bruijn indices within the type are contained within `0..D` - /// (exclusive). - outer_exclusive_binder: ty::DebruijnIndex, -} - -// `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(TyS<'_>, 32); - -impl<'tcx> Ord for TyS<'tcx> { - fn cmp(&self, other: &TyS<'tcx>) -> Ordering { - self.kind.cmp(&other.kind) - } -} - -impl<'tcx> PartialOrd for TyS<'tcx> { - fn partial_cmp(&self, other: &TyS<'tcx>) -> Option { - Some(self.kind.cmp(&other.kind)) - } -} - -impl<'tcx> PartialEq for TyS<'tcx> { - #[inline] - fn eq(&self, other: &TyS<'tcx>) -> bool { - ptr::eq(self, other) - } -} -impl<'tcx> Eq for TyS<'tcx> {} - -impl<'tcx> Hash for TyS<'tcx> { - fn hash(&self, s: &mut H) { - (self as *const TyS<'_>).hash(s) - } -} - -impl<'a, 'tcx> HashStable> for ty::TyS<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ty::TyS { - ref kind, - - // The other fields just provide fast access to information that is - // also contained in `kind`, so no need to hash them. - flags: _, - - outer_exclusive_binder: _, - } = *self; - - kind.hash_stable(hcx, hasher); - } -} - -#[rustc_diagnostic_item = "Ty"] -pub type Ty<'tcx> = &'tcx TyS<'tcx>; - -impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {} -impl<'tcx> rustc_serialize::UseSpecializedDecodable for Ty<'tcx> {} - -pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>; - -extern "C" { - /// A dummy type used to force `List` to be unsized while not requiring references to it be wide - /// pointers. - type OpaqueListContents; -} - -/// A wrapper for slices with the additional invariant -/// that the slice is interned and no other slice with -/// the same contents can exist in the same context. -/// This means we can use pointer for both -/// equality comparisons and hashing. -/// Note: `Slice` was already taken by the `Ty`. -#[repr(C)] -pub struct List { - len: usize, - data: [T; 0], - opaque: OpaqueListContents, -} - -unsafe impl Sync for List {} - -impl List { - #[inline] - fn from_arena<'tcx>(arena: &'tcx Arena<'tcx>, slice: &[T]) -> &'tcx List { - assert!(!mem::needs_drop::()); - assert!(mem::size_of::() != 0); - assert!(!slice.is_empty()); - - // Align up the size of the len (usize) field - let align = mem::align_of::(); - let align_mask = align - 1; - let offset = mem::size_of::(); - let offset = (offset + align_mask) & !align_mask; - - let size = offset + slice.len() * mem::size_of::(); - - let mem = arena - .dropless - .alloc_raw(size, cmp::max(mem::align_of::(), mem::align_of::())); - unsafe { - let result = &mut *(mem.as_mut_ptr() as *mut List); - // Write the length - result.len = slice.len(); - - // Write the elements - let arena_slice = slice::from_raw_parts_mut(result.data.as_mut_ptr(), result.len); - arena_slice.copy_from_slice(slice); - - result - } - } -} - -impl fmt::Debug for List { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Encodable for List { - #[inline] - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} - -impl Ord for List -where - T: Ord, -{ - fn cmp(&self, other: &List) -> Ordering { - if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) } - } -} - -impl PartialOrd for List -where - T: PartialOrd, -{ - fn partial_cmp(&self, other: &List) -> Option { - if self == other { - Some(Ordering::Equal) - } else { - <[T] as PartialOrd>::partial_cmp(&**self, &**other) - } - } -} - -impl PartialEq for List { - #[inline] - fn eq(&self, other: &List) -> bool { - ptr::eq(self, other) - } -} -impl Eq for List {} - -impl Hash for List { - #[inline] - fn hash(&self, s: &mut H) { - (self as *const List).hash(s) - } -} - -impl Deref for List { - type Target = [T]; - #[inline(always)] - fn deref(&self) -> &[T] { - self.as_ref() - } -} - -impl AsRef<[T]> for List { - #[inline(always)] - fn as_ref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.data.as_ptr(), self.len) } - } -} - -impl<'a, T> IntoIterator for &'a List { - type Item = &'a T; - type IntoIter = <&'a [T] as IntoIterator>::IntoIter; - #[inline(always)] - fn into_iter(self) -> Self::IntoIter { - self[..].iter() - } -} - -impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx List> {} - -impl List { - #[inline(always)] - pub fn empty<'a>() -> &'a List { - #[repr(align(64), C)] - struct EmptySlice([u8; 64]); - static EMPTY_SLICE: EmptySlice = EmptySlice([0; 64]); - assert!(mem::align_of::() <= 64); - unsafe { &*(&EMPTY_SLICE as *const _ as *const List) } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub struct UpvarPath { - pub hir_id: hir::HirId, -} - -/// Upvars do not get their own `NodeId`. Instead, we use the pair of -/// the original var ID (that is, the root variable that is referenced -/// by the upvar) and the ID of the closure expression. -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub struct UpvarId { - pub var_path: UpvarPath, - pub closure_expr_id: LocalDefId, -} - -#[derive(Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] -pub enum BorrowKind { - /// Data must be immutable and is aliasable. - ImmBorrow, - - /// Data must be immutable but not aliasable. This kind of borrow - /// cannot currently be expressed by the user and is used only in - /// implicit closure bindings. It is needed when the closure - /// is borrowing or mutating a mutable referent, e.g.: - /// - /// let x: &mut isize = ...; - /// let y = || *x += 5; - /// - /// If we were to try to translate this closure into a more explicit - /// form, we'd encounter an error with the code as written: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// This is then illegal because you cannot mutate a `&mut` found - /// in an aliasable location. To solve, you'd have to translate with - /// an `&mut` borrow: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// Now the assignment to `**env.x` is legal, but creating a - /// mutable pointer to `x` is not because `x` is not mutable. We - /// could fix this by declaring `x` as `let mut x`. This is ok in - /// user code, if awkward, but extra weird for closures, since the - /// borrow is hidden. - /// - /// So we introduce a "unique imm" borrow -- the referent is - /// immutable, but not aliasable. This solves the problem. For - /// simplicity, we don't give users the way to express this - /// borrow, it's just used when translating closures. - UniqueImmBorrow, - - /// Data is mutable and not aliasable. - MutBorrow, -} - -/// Information describing the capture of an upvar. This is computed -/// during `typeck`, specifically by `regionck`. -#[derive(PartialEq, Clone, Debug, Copy, RustcEncodable, RustcDecodable, HashStable)] -pub enum UpvarCapture<'tcx> { - /// Upvar is captured by value. This is always true when the - /// closure is labeled `move`, but can also be true in other cases - /// depending on inference. - ByValue, - - /// Upvar is captured by reference. - ByRef(UpvarBorrow<'tcx>), -} - -#[derive(PartialEq, Clone, Copy, RustcEncodable, RustcDecodable, HashStable)] -pub struct UpvarBorrow<'tcx> { - /// The kind of borrow: by-ref upvars have access to shared - /// immutable borrows, which are not part of the normal language - /// syntax. - pub kind: BorrowKind, - - /// Region of the resulting reference. - pub region: ty::Region<'tcx>, -} - -pub type UpvarListMap = FxHashMap>; -pub type UpvarCaptureMap<'tcx> = FxHashMap>; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum IntVarValue { - IntType(ast::IntTy), - UintType(ast::UintTy), -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FloatVarValue(pub ast::FloatTy); - -impl ty::EarlyBoundRegion { - pub fn to_bound_region(&self) -> ty::BoundRegion { - ty::BoundRegion::BrNamed(self.def_id, self.name) - } - - /// Does this early bound region have a name? Early bound regions normally - /// always have names except when using anonymous lifetimes (`'_`). - pub fn has_name(&self) -> bool { - self.name != kw::UnderscoreLifetime - } -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum GenericParamDefKind { - Lifetime, - Type { - has_default: bool, - object_lifetime_default: ObjectLifetimeDefault, - synthetic: Option, - }, - Const, -} - -impl GenericParamDefKind { - pub fn descr(&self) -> &'static str { - match self { - GenericParamDefKind::Lifetime => "lifetime", - GenericParamDefKind::Type { .. } => "type", - GenericParamDefKind::Const => "constant", - } - } -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct GenericParamDef { - pub name: Symbol, - pub def_id: DefId, - pub index: u32, - - /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute - /// on generic parameter `'a`/`T`, asserts data behind the parameter - /// `'a`/`T` won't be accessed during the parent type's `Drop` impl. - pub pure_wrt_drop: bool, - - pub kind: GenericParamDefKind, -} - -impl GenericParamDef { - pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { - if let GenericParamDefKind::Lifetime = self.kind { - ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } - } else { - bug!("cannot convert a non-lifetime parameter def to an early bound region") - } - } - - pub fn to_bound_region(&self) -> ty::BoundRegion { - if let GenericParamDefKind::Lifetime = self.kind { - self.to_early_bound_region_data().to_bound_region() - } else { - bug!("cannot convert a non-lifetime parameter def to an early bound region") - } - } -} - -#[derive(Default)] -pub struct GenericParamCount { - pub lifetimes: usize, - pub types: usize, - pub consts: usize, -} - -/// Information about the formal type/lifetime parameters associated -/// with an item or method. Analogous to `hir::Generics`. -/// -/// The ordering of parameters is the same as in `Subst` (excluding child generics): -/// `Self` (optionally), `Lifetime` params..., `Type` params... -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct Generics { - pub parent: Option, - pub parent_count: usize, - pub params: Vec, - - /// Reverse map to the `index` field of each `GenericParamDef`. - #[stable_hasher(ignore)] - pub param_def_id_to_index: FxHashMap, - - pub has_self: bool, - pub has_late_bound_regions: Option, -} - -impl<'tcx> Generics { - pub fn count(&self) -> usize { - self.parent_count + self.params.len() - } - - pub fn own_counts(&self) -> GenericParamCount { - // We could cache this as a property of `GenericParamCount`, but - // the aim is to refactor this away entirely eventually and the - // presence of this method will be a constant reminder. - let mut own_counts: GenericParamCount = Default::default(); - - for param in &self.params { - match param.kind { - GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, - GenericParamDefKind::Type { .. } => own_counts.types += 1, - GenericParamDefKind::Const => own_counts.consts += 1, - }; - } - - own_counts - } - - pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { - if self.own_requires_monomorphization() { - return true; - } - - if let Some(parent_def_id) = self.parent { - let parent = tcx.generics_of(parent_def_id); - parent.requires_monomorphization(tcx) - } else { - false - } - } - - pub fn own_requires_monomorphization(&self) -> bool { - for param in &self.params { - match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => return true, - GenericParamDefKind::Lifetime => {} - } - } - false - } - - pub fn region_param( - &'tcx self, - param: &EarlyBoundRegion, - tcx: TyCtxt<'tcx>, - ) -> &'tcx GenericParamDef { - if let Some(index) = param.index.checked_sub(self.parent_count as u32) { - let param = &self.params[index as usize]; - match param.kind { - GenericParamDefKind::Lifetime => param, - _ => bug!("expected lifetime parameter, but found another generic parameter"), - } - } else { - tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) - .region_param(param, tcx) - } - } - - /// Returns the `GenericParamDef` associated with this `ParamTy`. - pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { - if let Some(index) = param.index.checked_sub(self.parent_count as u32) { - let param = &self.params[index as usize]; - match param.kind { - GenericParamDefKind::Type { .. } => param, - _ => bug!("expected type parameter, but found another generic parameter"), - } - } else { - tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) - .type_param(param, tcx) - } - } - - /// Returns the `ConstParameterDef` associated with this `ParamConst`. - pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { - if let Some(index) = param.index.checked_sub(self.parent_count as u32) { - let param = &self.params[index as usize]; - match param.kind { - GenericParamDefKind::Const => param, - _ => bug!("expected const parameter, but found another generic parameter"), - } - } else { - tcx.generics_of(self.parent.expect("parent_count>0 but no parent?")) - .const_param(param, tcx) - } - } -} - -/// Bounds on generics. -#[derive(Copy, Clone, Default, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct GenericPredicates<'tcx> { - pub parent: Option, - pub predicates: &'tcx [(Predicate<'tcx>, Span)], -} - -impl<'tcx> GenericPredicates<'tcx> { - pub fn instantiate( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - let mut instantiated = InstantiatedPredicates::empty(); - self.instantiate_into(tcx, &mut instantiated, substs); - instantiated - } - - pub fn instantiate_own( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - InstantiatedPredicates { - predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(), - spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), - } - } - - fn instantiate_into( - &self, - tcx: TyCtxt<'tcx>, - instantiated: &mut InstantiatedPredicates<'tcx>, - substs: SubstsRef<'tcx>, - ) { - if let Some(def_id) = self.parent { - tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs); - } - instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs))); - instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); - } - - pub fn instantiate_identity(&self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { - let mut instantiated = InstantiatedPredicates::empty(); - self.instantiate_identity_into(tcx, &mut instantiated); - instantiated - } - - fn instantiate_identity_into( - &self, - tcx: TyCtxt<'tcx>, - instantiated: &mut InstantiatedPredicates<'tcx>, - ) { - if let Some(def_id) = self.parent { - tcx.predicates_of(def_id).instantiate_identity_into(tcx, instantiated); - } - instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); - instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); - } - - pub fn instantiate_supertrait( - &self, - tcx: TyCtxt<'tcx>, - poly_trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - assert_eq!(self.parent, None); - InstantiatedPredicates { - predicates: self - .predicates - .iter() - .map(|(pred, _)| pred.subst_supertrait(tcx, poly_trait_ref)) - .collect(), - spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub enum Predicate<'tcx> { - /// Corresponds to `where Foo: Bar`. `Foo` here would be - /// the `Self` type of the trait reference and `A`, `B`, and `C` - /// would be the type parameters. - /// - /// A trait predicate will have `Constness::Const` if it originates - /// from a bound on a `const fn` without the `?const` opt-out (e.g., - /// `const fn foobar() {}`). - Trait(PolyTraitPredicate<'tcx>, Constness), - - /// `where 'a: 'b` - RegionOutlives(PolyRegionOutlivesPredicate<'tcx>), - - /// `where T: 'a` - TypeOutlives(PolyTypeOutlivesPredicate<'tcx>), - - /// `where ::Name == X`, approximately. - /// See the `ProjectionPredicate` struct for details. - Projection(PolyProjectionPredicate<'tcx>), - - /// No syntax: `T` well-formed. - WellFormed(Ty<'tcx>), - - /// Trait must be object-safe. - ObjectSafe(DefId), - - /// No direct syntax. May be thought of as `where T: FnFoo<...>` - /// for some substitutions `...` and `T` being a closure type. - /// Satisfied (or refuted) once we know the closure's kind. - ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind), - - /// `T1 <: T2` - Subtype(PolySubtypePredicate<'tcx>), - - /// Constant initializer must evaluate successfully. - ConstEvaluatable(DefId, SubstsRef<'tcx>), -} - -/// The crate outlives map is computed during typeck and contains the -/// outlives of every item in the local crate. You should not use it -/// directly, because to do so will make your pass dependent on the -/// HIR of every item in the local crate. Instead, use -/// `tcx.inferred_outlives_of()` to get the outlives for a *particular* -/// item. -#[derive(HashStable)] -pub struct CratePredicatesMap<'tcx> { - /// For each struct with outlive bounds, maps to a vector of the - /// predicate of its outlive bounds. If an item has no outlives - /// bounds, it will have no entry. - pub predicates: FxHashMap, Span)]>, -} - -impl<'tcx> AsRef> for Predicate<'tcx> { - fn as_ref(&self) -> &Predicate<'tcx> { - self - } -} - -impl<'tcx> Predicate<'tcx> { - /// Performs a substitution suitable for going from a - /// poly-trait-ref to supertraits that must hold if that - /// poly-trait-ref holds. This is slightly different from a normal - /// substitution in terms of what happens with bound regions. See - /// lengthy comment below for details. - pub fn subst_supertrait( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> ty::Predicate<'tcx> { - // The interaction between HRTB and supertraits is not entirely - // obvious. Let me walk you (and myself) through an example. - // - // Let's start with an easy case. Consider two traits: - // - // trait Foo<'a>: Bar<'a,'a> { } - // trait Bar<'b,'c> { } - // - // Now, if we have a trait reference `for<'x> T: Foo<'x>`, then - // we can deduce that `for<'x> T: Bar<'x,'x>`. Basically, if we - // knew that `Foo<'x>` (for any 'x) then we also know that - // `Bar<'x,'x>` (for any 'x). This more-or-less falls out from - // normal substitution. - // - // In terms of why this is sound, the idea is that whenever there - // is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>` - // holds. So if there is an impl of `T:Foo<'a>` that applies to - // all `'a`, then we must know that `T:Bar<'a,'a>` holds for all - // `'a`. - // - // Another example to be careful of is this: - // - // trait Foo1<'a>: for<'b> Bar1<'a,'b> { } - // trait Bar1<'b,'c> { } - // - // Here, if we have `for<'x> T: Foo1<'x>`, then what do we know? - // The answer is that we know `for<'x,'b> T: Bar1<'x,'b>`. The - // reason is similar to the previous example: any impl of - // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So - // basically we would want to collapse the bound lifetimes from - // the input (`trait_ref`) and the supertraits. - // - // To achieve this in practice is fairly straightforward. Let's - // consider the more complicated scenario: - // - // - We start out with `for<'x> T: Foo1<'x>`. In this case, `'x` - // has a De Bruijn index of 1. We want to produce `for<'x,'b> T: Bar1<'x,'b>`, - // where both `'x` and `'b` would have a DB index of 1. - // The substitution from the input trait-ref is therefore going to be - // `'a => 'x` (where `'x` has a DB index of 1). - // - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an - // early-bound parameter and `'b' is a late-bound parameter with a - // DB index of 1. - // - If we replace `'a` with `'x` from the input, it too will have - // a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>` - // just as we wanted. - // - // There is only one catch. If we just apply the substitution `'a - // => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will - // adjust the DB index because we substituting into a binder (it - // tries to be so smart...) resulting in `for<'x> for<'b> - // Bar1<'x,'b>` (we have no syntax for this, so use your - // imagination). Basically the 'x will have DB index of 2 and 'b - // will have DB index of 1. Not quite what we want. So we apply - // the substitution to the *contents* of the trait reference, - // rather than the trait reference itself (put another way, the - // substitution code expects equal binding levels in the values - // from the substitution and the value being substituted into, and - // this trick achieves that). - - let substs = &trait_ref.skip_binder().substs; - match *self { - Predicate::Trait(ref binder, constness) => { - Predicate::Trait(binder.map_bound(|data| data.subst(tcx, substs)), constness) - } - Predicate::Subtype(ref binder) => { - Predicate::Subtype(binder.map_bound(|data| data.subst(tcx, substs))) - } - Predicate::RegionOutlives(ref binder) => { - Predicate::RegionOutlives(binder.map_bound(|data| data.subst(tcx, substs))) - } - Predicate::TypeOutlives(ref binder) => { - Predicate::TypeOutlives(binder.map_bound(|data| data.subst(tcx, substs))) - } - Predicate::Projection(ref binder) => { - Predicate::Projection(binder.map_bound(|data| data.subst(tcx, substs))) - } - Predicate::WellFormed(data) => Predicate::WellFormed(data.subst(tcx, substs)), - Predicate::ObjectSafe(trait_def_id) => Predicate::ObjectSafe(trait_def_id), - Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - Predicate::ClosureKind(closure_def_id, closure_substs.subst(tcx, substs), kind) - } - Predicate::ConstEvaluatable(def_id, const_substs) => { - Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)) - } - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct TraitPredicate<'tcx> { - pub trait_ref: TraitRef<'tcx>, -} - -pub type PolyTraitPredicate<'tcx> = ty::Binder>; - -impl<'tcx> TraitPredicate<'tcx> { - pub fn def_id(&self) -> DefId { - self.trait_ref.def_id - } - - pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator> + 'a { - self.trait_ref.input_types() - } - - pub fn self_ty(&self) -> Ty<'tcx> { - self.trait_ref.self_ty() - } -} - -impl<'tcx> PolyTraitPredicate<'tcx> { - pub fn def_id(&self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().def_id() - } -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct OutlivesPredicate(pub A, pub B); // `A: B` -pub type PolyOutlivesPredicate = ty::Binder>; -pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder>; -pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder>; - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct SubtypePredicate<'tcx> { - pub a_is_expected: bool, - pub a: Ty<'tcx>, - pub b: Ty<'tcx>, -} -pub type PolySubtypePredicate<'tcx> = ty::Binder>; - -/// This kind of predicate has no *direct* correspondent in the -/// syntax, but it roughly corresponds to the syntactic forms: -/// -/// 1. `T: TraitRef<..., Item = Type>` -/// 2. `>::Item == Type` (NYI) -/// -/// In particular, form #1 is "desugared" to the combination of a -/// normal trait predicate (`T: TraitRef<...>`) and one of these -/// predicates. Form #2 is a broader form in that it also permits -/// equality between arbitrary types. Processing an instance of -/// Form #2 eventually yields one of these `ProjectionPredicate` -/// instances to normalize the LHS. -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct ProjectionPredicate<'tcx> { - pub projection_ty: ProjectionTy<'tcx>, - pub ty: Ty<'tcx>, -} - -pub type PolyProjectionPredicate<'tcx> = Binder>; - -impl<'tcx> PolyProjectionPredicate<'tcx> { - /// Returns the `DefId` of the associated item being projected. - pub fn item_def_id(&self) -> DefId { - self.skip_binder().projection_ty.item_def_id - } - - #[inline] - pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { - // Note: unlike with `TraitRef::to_poly_trait_ref()`, - // `self.0.trait_ref` is permitted to have escaping regions. - // This is because here `self` has a `Binder` and so does our - // return value, so we are preserving the number of binding - // levels. - self.map_bound(|predicate| predicate.projection_ty.trait_ref(tcx)) - } - - pub fn ty(&self) -> Binder> { - self.map_bound(|predicate| predicate.ty) - } - - /// The `DefId` of the `TraitItem` for the associated type. - /// - /// Note that this is not the `DefId` of the `TraitRef` containing this - /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. - pub fn projection_def_id(&self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().projection_ty.item_def_id - } -} - -pub trait ToPolyTraitRef<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; -} - -impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { - ty::Binder::dummy(*self) - } -} - -impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { - self.map_bound_ref(|trait_pred| trait_pred.trait_ref) - } -} - -pub trait ToPredicate<'tcx> { - fn to_predicate(&self) -> Predicate<'tcx>; -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { - fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait( - ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.value }), - self.constness, - ) - } -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&TraitRef<'tcx>> { - fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait( - ty::Binder::dummy(ty::TraitPredicate { trait_ref: *self.value }), - self.constness, - ) - } -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { - fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) - } -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&PolyTraitRef<'tcx>> { - fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) - } -} - -impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { - fn to_predicate(&self) -> Predicate<'tcx> { - Predicate::RegionOutlives(*self) - } -} - -impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { - fn to_predicate(&self) -> Predicate<'tcx> { - Predicate::TypeOutlives(*self) - } -} - -impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { - fn to_predicate(&self) -> Predicate<'tcx> { - Predicate::Projection(*self) - } -} - -// A custom iterator used by `Predicate::walk_tys`. -enum WalkTysIter<'tcx, I, J, K> -where - I: Iterator>, - J: Iterator>, - K: Iterator>, -{ - None, - One(Ty<'tcx>), - Two(Ty<'tcx>, Ty<'tcx>), - Types(I), - InputTypes(J), - ProjectionTypes(K), -} - -impl<'tcx, I, J, K> Iterator for WalkTysIter<'tcx, I, J, K> -where - I: Iterator>, - J: Iterator>, - K: Iterator>, -{ - type Item = Ty<'tcx>; - - fn next(&mut self) -> Option> { - match *self { - WalkTysIter::None => None, - WalkTysIter::One(item) => { - *self = WalkTysIter::None; - Some(item) - } - WalkTysIter::Two(item1, item2) => { - *self = WalkTysIter::One(item2); - Some(item1) - } - WalkTysIter::Types(ref mut iter) => iter.next(), - WalkTysIter::InputTypes(ref mut iter) => iter.next(), - WalkTysIter::ProjectionTypes(ref mut iter) => iter.next(), - } - } -} - -impl<'tcx> Predicate<'tcx> { - /// Iterates over the types in this predicate. Note that in all - /// cases this is skipping over a binder, so late-bound regions - /// with depth 0 are bound by the predicate. - pub fn walk_tys(&'a self) -> impl Iterator> + 'a { - match *self { - ty::Predicate::Trait(ref data, _) => { - WalkTysIter::InputTypes(data.skip_binder().input_types()) - } - ty::Predicate::Subtype(binder) => { - let SubtypePredicate { a, b, a_is_expected: _ } = binder.skip_binder(); - WalkTysIter::Two(a, b) - } - ty::Predicate::TypeOutlives(binder) => WalkTysIter::One(binder.skip_binder().0), - ty::Predicate::RegionOutlives(..) => WalkTysIter::None, - ty::Predicate::Projection(ref data) => { - let inner = data.skip_binder(); - WalkTysIter::ProjectionTypes( - inner.projection_ty.substs.types().chain(Some(inner.ty)), - ) - } - ty::Predicate::WellFormed(data) => WalkTysIter::One(data), - ty::Predicate::ObjectSafe(_trait_def_id) => WalkTysIter::None, - ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) => { - WalkTysIter::Types(closure_substs.types()) - } - ty::Predicate::ConstEvaluatable(_, substs) => WalkTysIter::Types(substs.types()), - } - } - - pub fn to_opt_poly_trait_ref(&self) -> Option> { - match *self { - Predicate::Trait(ref t, _) => Some(t.to_poly_trait_ref()), - Predicate::Projection(..) - | Predicate::Subtype(..) - | Predicate::RegionOutlives(..) - | Predicate::WellFormed(..) - | Predicate::ObjectSafe(..) - | Predicate::ClosureKind(..) - | Predicate::TypeOutlives(..) - | Predicate::ConstEvaluatable(..) => None, - } - } - - pub fn to_opt_type_outlives(&self) -> Option> { - match *self { - Predicate::TypeOutlives(data) => Some(data), - Predicate::Trait(..) - | Predicate::Projection(..) - | Predicate::Subtype(..) - | Predicate::RegionOutlives(..) - | Predicate::WellFormed(..) - | Predicate::ObjectSafe(..) - | Predicate::ClosureKind(..) - | Predicate::ConstEvaluatable(..) => None, - } - } -} - -/// Represents the bounds declared on a particular set of type -/// parameters. Should eventually be generalized into a flag list of -/// where-clauses. You can obtain a `InstantiatedPredicates` list from a -/// `GenericPredicates` by using the `instantiate` method. Note that this method -/// reflects an important semantic invariant of `InstantiatedPredicates`: while -/// the `GenericPredicates` are expressed in terms of the bound type -/// parameters of the impl/trait/whatever, an `InstantiatedPredicates` instance -/// represented a set of bounds for some particular instantiation, -/// meaning that the generic parameters have been substituted with -/// their values. -/// -/// Example: -/// -/// struct Foo> { ... } -/// -/// Here, the `GenericPredicates` for `Foo` would contain a list of bounds like -/// `[[], [U:Bar]]`. Now if there were some particular reference -/// like `Foo`, then the `InstantiatedPredicates` would be `[[], -/// [usize:Bar]]`. -#[derive(Clone, Debug, TypeFoldable)] -pub struct InstantiatedPredicates<'tcx> { - pub predicates: Vec>, - pub spans: Vec, -} - -impl<'tcx> InstantiatedPredicates<'tcx> { - pub fn empty() -> InstantiatedPredicates<'tcx> { - InstantiatedPredicates { predicates: vec![], spans: vec![] } - } - - pub fn is_empty(&self) -> bool { - self.predicates.is_empty() - } -} - -rustc_index::newtype_index! { - /// "Universes" are used during type- and trait-checking in the - /// presence of `for<..>` binders to control what sets of names are - /// visible. Universes are arranged into a tree: the root universe - /// contains names that are always visible. Each child then adds a new - /// set of names that are visible, in addition to those of its parent. - /// We say that the child universe "extends" the parent universe with - /// new names. - /// - /// To make this more concrete, consider this program: - /// - /// ``` - /// struct Foo { } - /// fn bar(x: T) { - /// let y: for<'a> fn(&'a u8, Foo) = ...; - /// } - /// ``` - /// - /// The struct name `Foo` is in the root universe U0. But the type - /// parameter `T`, introduced on `bar`, is in an extended universe U1 - /// -- i.e., within `bar`, we can name both `T` and `Foo`, but outside - /// of `bar`, we cannot name `T`. Then, within the type of `y`, the - /// region `'a` is in a universe U2 that extends U1, because we can - /// name it inside the fn type but not outside. - /// - /// Universes are used to do type- and trait-checking around these - /// "forall" binders (also called **universal quantification**). The - /// idea is that when, in the body of `bar`, we refer to `T` as a - /// type, we aren't referring to any type in particular, but rather a - /// kind of "fresh" type that is distinct from all other types we have - /// actually declared. This is called a **placeholder** type, and we - /// use universes to talk about this. In other words, a type name in - /// universe 0 always corresponds to some "ground" type that the user - /// declared, but a type name in a non-zero universe is a placeholder - /// type -- an idealized representative of "types in general" that we - /// use for checking generic functions. - pub struct UniverseIndex { - derive [HashStable] - DEBUG_FORMAT = "U{}", - } -} - -impl UniverseIndex { - pub const ROOT: UniverseIndex = UniverseIndex::from_u32(0); - - /// Returns the "next" universe index in order -- this new index - /// is considered to extend all previous universes. This - /// corresponds to entering a `forall` quantifier. So, for - /// example, suppose we have this type in universe `U`: - /// - /// ``` - /// for<'a> fn(&'a u32) - /// ``` - /// - /// Once we "enter" into this `for<'a>` quantifier, we are in a - /// new universe that extends `U` -- in this new universe, we can - /// name the region `'a`, but that region was not nameable from - /// `U` because it was not in scope there. - pub fn next_universe(self) -> UniverseIndex { - UniverseIndex::from_u32(self.private.checked_add(1).unwrap()) - } - - /// Returns `true` if `self` can name a name from `other` -- in other words, - /// if the set of names in `self` is a superset of those in - /// `other` (`self >= other`). - pub fn can_name(self, other: UniverseIndex) -> bool { - self.private >= other.private - } - - /// Returns `true` if `self` cannot name some names from `other` -- in other - /// words, if the set of names in `self` is a strict subset of - /// those in `other` (`self < other`). - pub fn cannot_name(self, other: UniverseIndex) -> bool { - self.private < other.private - } -} - -/// The "placeholder index" fully defines a placeholder region. -/// Placeholder regions are identified by both a **universe** as well -/// as a "bound-region" within that universe. The `bound_region` is -/// basically a name -- distinct bound regions within the same -/// universe are just two regions with an unknown relationship to one -/// another. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, PartialOrd, Ord)] -pub struct Placeholder { - pub universe: UniverseIndex, - pub name: T, -} - -impl<'a, T> HashStable> for Placeholder -where - T: HashStable>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.universe.hash_stable(hcx, hasher); - self.name.hash_stable(hcx, hasher); - } -} - -pub type PlaceholderRegion = Placeholder; - -pub type PlaceholderType = Placeholder; - -pub type PlaceholderConst = Placeholder; - -/// When type checking, we use the `ParamEnv` to track -/// details about the set of where-clauses that are in scope at this -/// particular point. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeFoldable)] -pub struct ParamEnv<'tcx> { - /// `Obligation`s that the caller must satisfy. This is basically - /// the set of bounds on the in-scope type parameters, translated - /// into `Obligation`s, and elaborated and normalized. - pub caller_bounds: &'tcx List>, - - /// Typically, this is `Reveal::UserFacing`, but during codegen we - /// want `Reveal::All` -- note that this is always paired with an - /// empty environment. To get that, use `ParamEnv::reveal()`. - pub reveal: traits::Reveal, - - /// If this `ParamEnv` comes from a call to `tcx.param_env(def_id)`, - /// register that `def_id` (useful for transitioning to the chalk trait - /// solver). - pub def_id: Option, -} - -impl<'tcx> ParamEnv<'tcx> { - /// Construct a trait environment suitable for contexts where - /// there are no where-clauses in scope. Hidden types (like `impl - /// Trait`) are left hidden, so this is suitable for ordinary - /// type-checking. - #[inline] - pub fn empty() -> Self { - Self::new(List::empty(), Reveal::UserFacing, None) - } - - /// Construct a trait environment with no where-clauses in scope - /// where the values of all `impl Trait` and other hidden types - /// are revealed. This is suitable for monomorphized, post-typeck - /// environments like codegen or doing optimizations. - /// - /// N.B., if you want to have predicates in scope, use `ParamEnv::new`, - /// or invoke `param_env.with_reveal_all()`. - #[inline] - pub fn reveal_all() -> Self { - Self::new(List::empty(), Reveal::All, None) - } - - /// Construct a trait environment with the given set of predicates. - #[inline] - pub fn new( - caller_bounds: &'tcx List>, - reveal: Reveal, - def_id: Option, - ) -> Self { - ty::ParamEnv { caller_bounds, reveal, def_id } - } - - /// Returns a new parameter environment with the same clauses, but - /// which "reveals" the true results of projections in all cases - /// (even for associated types that are specializable). This is - /// the desired behavior during codegen and certain other special - /// contexts; normally though we want to use `Reveal::UserFacing`, - /// which is the default. - pub fn with_reveal_all(self) -> Self { - ty::ParamEnv { reveal: Reveal::All, ..self } - } - - /// Returns this same environment but with no caller bounds. - pub fn without_caller_bounds(self) -> Self { - ty::ParamEnv { caller_bounds: List::empty(), ..self } - } - - /// Creates a suitable environment in which to perform trait - /// queries on the given value. When type-checking, this is simply - /// the pair of the environment plus value. But when reveal is set to - /// All, then if `value` does not reference any type parameters, we will - /// pair it with the empty environment. This improves caching and is generally - /// invisible. - /// - /// N.B., we preserve the environment when type-checking because it - /// is possible for the user to have wacky where-clauses like - /// `where Box: Copy`, which are clearly never - /// satisfiable. We generally want to behave as if they were true, - /// although the surrounding function is never reachable. - pub fn and>(self, value: T) -> ParamEnvAnd<'tcx, T> { - match self.reveal { - Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, - - Reveal::All => { - if value.is_global() { - ParamEnvAnd { param_env: self.without_caller_bounds(), value } - } else { - ParamEnvAnd { param_env: self, value } - } - } - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct ConstnessAnd { - pub constness: Constness, - pub value: T, -} - -// FIXME(ecstaticmorse): Audit all occurrences of `without_const().to_predicate()` to ensure that -// the constness of trait bounds is being propagated correctly. -pub trait WithConstness: Sized { - #[inline] - fn with_constness(self, constness: Constness) -> ConstnessAnd { - ConstnessAnd { constness, value: self } - } - - #[inline] - fn with_const(self) -> ConstnessAnd { - self.with_constness(Constness::Const) - } - - #[inline] - fn without_const(self) -> ConstnessAnd { - self.with_constness(Constness::NotConst) - } -} - -impl WithConstness for T {} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)] -pub struct ParamEnvAnd<'tcx, T> { - pub param_env: ParamEnv<'tcx>, - pub value: T, -} - -impl<'tcx, T> ParamEnvAnd<'tcx, T> { - pub fn into_parts(self) -> (ParamEnv<'tcx>, T) { - (self.param_env, self.value) - } -} - -impl<'a, 'tcx, T> HashStable> for ParamEnvAnd<'tcx, T> -where - T: HashStable>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ParamEnvAnd { ref param_env, ref value } = *self; - - param_env.hash_stable(hcx, hasher); - value.hash_stable(hcx, hasher); - } -} - -#[derive(Copy, Clone, Debug, HashStable)] -pub struct Destructor { - /// The `DefId` of the destructor method - pub did: DefId, -} - -bitflags! { - #[derive(HashStable)] - pub struct AdtFlags: u32 { - const NO_ADT_FLAGS = 0; - /// Indicates whether the ADT is an enum. - const IS_ENUM = 1 << 0; - /// Indicates whether the ADT is a union. - const IS_UNION = 1 << 1; - /// Indicates whether the ADT is a struct. - const IS_STRUCT = 1 << 2; - /// Indicates whether the ADT is a struct and has a constructor. - const HAS_CTOR = 1 << 3; - /// Indicates whether the type is `PhantomData`. - const IS_PHANTOM_DATA = 1 << 4; - /// Indicates whether the type has a `#[fundamental]` attribute. - const IS_FUNDAMENTAL = 1 << 5; - /// Indicates whether the type is `Box`. - const IS_BOX = 1 << 6; - /// Indicates whether the type is `ManuallyDrop`. - const IS_MANUALLY_DROP = 1 << 7; - // FIXME(matthewjasper) replace these with diagnostic items - /// Indicates whether the type is an `Arc`. - const IS_ARC = 1 << 8; - /// Indicates whether the type is an `Rc`. - const IS_RC = 1 << 9; - /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. - /// (i.e., this flag is never set unless this ADT is an enum). - const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 10; - } -} - -bitflags! { - #[derive(HashStable)] - pub struct VariantFlags: u32 { - const NO_VARIANT_FLAGS = 0; - /// Indicates whether the field list of this variant is `#[non_exhaustive]`. - const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0; - } -} - -/// Definition of a variant -- a struct's fields or a enum variant. -#[derive(Debug, HashStable)] -pub struct VariantDef { - /// `DefId` that identifies the variant itself. - /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. - pub def_id: DefId, - /// `DefId` that identifies the variant's constructor. - /// If this variant is a struct variant, then this is `None`. - pub ctor_def_id: Option, - /// Variant or struct name. - #[stable_hasher(project(name))] - pub ident: Ident, - /// Discriminant of this variant. - pub discr: VariantDiscr, - /// Fields of this variant. - pub fields: Vec, - /// Type of constructor of variant. - pub ctor_kind: CtorKind, - /// Flags of the variant (e.g. is field list non-exhaustive)? - flags: VariantFlags, - /// Variant is obtained as part of recovering from a syntactic error. - /// May be incomplete or bogus. - pub recovered: bool, -} - -impl<'tcx> VariantDef { - /// Creates a new `VariantDef`. - /// - /// `variant_did` is the `DefId` that identifies the enum variant (if this `VariantDef` - /// represents an enum variant). - /// - /// `ctor_did` is the `DefId` that identifies the constructor of unit or - /// tuple-variants/structs. If this is a `struct`-variant then this should be `None`. - /// - /// `parent_did` is the `DefId` of the `AdtDef` representing the enum or struct that - /// owns this variant. It is used for checking if a struct has `#[non_exhaustive]` w/out having - /// to go through the redirect of checking the ctor's attributes - but compiling a small crate - /// requires loading the `AdtDef`s for all the structs in the universe (e.g., coherence for any - /// built-in trait), and we do not want to load attributes twice. - /// - /// If someone speeds up attribute loading to not be a performance concern, they can - /// remove this hack and use the constructor `DefId` everywhere. - pub fn new( - tcx: TyCtxt<'tcx>, - ident: Ident, - variant_did: Option, - ctor_def_id: Option, - discr: VariantDiscr, - fields: Vec, - ctor_kind: CtorKind, - adt_kind: AdtKind, - parent_did: DefId, - recovered: bool, - ) -> Self { - debug!( - "VariantDef::new(ident = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, - fields = {:?}, ctor_kind = {:?}, adt_kind = {:?}, parent_did = {:?})", - ident, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, - ); - - let mut flags = VariantFlags::NO_VARIANT_FLAGS; - if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive) { - debug!("found non-exhaustive field list for {:?}", parent_did); - flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; - } else if let Some(variant_did) = variant_did { - if tcx.has_attr(variant_did, sym::non_exhaustive) { - debug!("found non-exhaustive field list for {:?}", variant_did); - flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; - } - } - - VariantDef { - def_id: variant_did.unwrap_or(parent_did), - ctor_def_id, - ident, - discr, - fields, - ctor_kind, - flags, - recovered, - } - } - - /// Is this field list non-exhaustive? - #[inline] - pub fn is_field_list_non_exhaustive(&self) -> bool { - self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum VariantDiscr { - /// Explicit value for this variant, i.e., `X = 123`. - /// The `DefId` corresponds to the embedded constant. - Explicit(DefId), - - /// The previous variant's discriminant plus one. - /// For efficiency reasons, the distance from the - /// last `Explicit` discriminant is being stored, - /// or `0` for the first variant, if it has none. - Relative(u32), -} - -#[derive(Debug, HashStable)] -pub struct FieldDef { - pub did: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, - pub vis: Visibility, -} - -/// The definition of a user-defined type, e.g., a `struct`, `enum`, or `union`. -/// -/// These are all interned (by `intern_adt_def`) into the `adt_defs` table. -/// -/// The initialism *ADT* stands for an [*algebraic data type (ADT)*][adt]. -/// This is slightly wrong because `union`s are not ADTs. -/// Moreover, Rust only allows recursive data types through indirection. -/// -/// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type -pub struct AdtDef { - /// The `DefId` of the struct, enum or union item. - pub did: DefId, - /// Variants of the ADT. If this is a struct or union, then there will be a single variant. - pub variants: IndexVec, - /// Flags of the ADT (e.g., is this a struct? is this non-exhaustive?). - flags: AdtFlags, - /// Repr options provided by the user. - pub repr: ReprOptions, -} - -impl PartialOrd for AdtDef { - fn partial_cmp(&self, other: &AdtDef) -> Option { - Some(self.cmp(&other)) - } -} - -/// There should be only one AdtDef for each `did`, therefore -/// it is fine to implement `Ord` only based on `did`. -impl Ord for AdtDef { - fn cmp(&self, other: &AdtDef) -> Ordering { - self.did.cmp(&other.did) - } -} - -impl PartialEq for AdtDef { - // `AdtDef`s are always interned, and this is part of `TyS` equality. - #[inline] - fn eq(&self, other: &Self) -> bool { - ptr::eq(self, other) - } -} - -impl Eq for AdtDef {} - -impl Hash for AdtDef { - #[inline] - fn hash(&self, s: &mut H) { - (self as *const AdtDef).hash(s) - } -} - -impl<'tcx> rustc_serialize::UseSpecializedEncodable for &'tcx AdtDef { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - self.did.encode(s) - } -} - -impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx AdtDef {} - -impl<'a> HashStable> for AdtDef { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - thread_local! { - static CACHE: RefCell> = Default::default(); - } - - let hash: Fingerprint = CACHE.with(|cache| { - let addr = self as *const AdtDef as usize; - *cache.borrow_mut().entry(addr).or_insert_with(|| { - let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; - - let mut hasher = StableHasher::new(); - did.hash_stable(hcx, &mut hasher); - variants.hash_stable(hcx, &mut hasher); - flags.hash_stable(hcx, &mut hasher); - repr.hash_stable(hcx, &mut hasher); - - hasher.finish() - }) - }); - - hash.hash_stable(hcx, hasher); - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub enum AdtKind { - Struct, - Union, - Enum, -} - -impl Into for AdtKind { - fn into(self) -> DataTypeKind { - match self { - AdtKind::Struct => DataTypeKind::Struct, - AdtKind::Union => DataTypeKind::Union, - AdtKind::Enum => DataTypeKind::Enum, - } - } -} - -bitflags! { - #[derive(RustcEncodable, RustcDecodable, Default, HashStable)] - pub struct ReprFlags: u8 { - const IS_C = 1 << 0; - const IS_SIMD = 1 << 1; - const IS_TRANSPARENT = 1 << 2; - // Internal only for now. If true, don't reorder fields. - const IS_LINEAR = 1 << 3; - // If true, don't expose any niche to type's context. - const HIDE_NICHE = 1 << 4; - // Any of these flags being set prevent field reordering optimisation. - const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits | - ReprFlags::IS_SIMD.bits | - ReprFlags::IS_LINEAR.bits; - } -} - -/// Represents the repr options provided by the user, -#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Default, HashStable)] -pub struct ReprOptions { - pub int: Option, - pub align: Option, - pub pack: Option, - pub flags: ReprFlags, -} - -impl ReprOptions { - pub fn new(tcx: TyCtxt<'_>, did: DefId) -> ReprOptions { - let mut flags = ReprFlags::empty(); - let mut size = None; - let mut max_align: Option = None; - let mut min_pack: Option = None; - for attr in tcx.get_attrs(did).iter() { - for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) { - flags.insert(match r { - attr::ReprC => ReprFlags::IS_C, - attr::ReprPacked(pack) => { - let pack = Align::from_bytes(pack as u64).unwrap(); - min_pack = Some(if let Some(min_pack) = min_pack { - min_pack.min(pack) - } else { - pack - }); - ReprFlags::empty() - } - attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, - attr::ReprNoNiche => ReprFlags::HIDE_NICHE, - attr::ReprSimd => ReprFlags::IS_SIMD, - attr::ReprInt(i) => { - size = Some(i); - ReprFlags::empty() - } - attr::ReprAlign(align) => { - max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); - ReprFlags::empty() - } - }); - } - } - - // This is here instead of layout because the choice must make it into metadata. - if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.def_path_str(did))) { - flags.insert(ReprFlags::IS_LINEAR); - } - ReprOptions { int: size, align: max_align, pack: min_pack, flags } - } - - #[inline] - pub fn simd(&self) -> bool { - self.flags.contains(ReprFlags::IS_SIMD) - } - #[inline] - pub fn c(&self) -> bool { - self.flags.contains(ReprFlags::IS_C) - } - #[inline] - pub fn packed(&self) -> bool { - self.pack.is_some() - } - #[inline] - pub fn transparent(&self) -> bool { - self.flags.contains(ReprFlags::IS_TRANSPARENT) - } - #[inline] - pub fn linear(&self) -> bool { - self.flags.contains(ReprFlags::IS_LINEAR) - } - #[inline] - pub fn hide_niche(&self) -> bool { - self.flags.contains(ReprFlags::HIDE_NICHE) - } - - pub fn discr_type(&self) -> attr::IntType { - self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize)) - } - - /// Returns `true` if this `#[repr()]` should inhabit "smart enum - /// layout" optimizations, such as representing `Foo<&T>` as a - /// single pointer. - pub fn inhibit_enum_layout_opt(&self) -> bool { - self.c() || self.int.is_some() - } - - /// Returns `true` if this `#[repr()]` should inhibit struct field reordering - /// optimizations, such as with `repr(C)`, `repr(packed(1))`, or `repr()`. - pub fn inhibit_struct_field_reordering_opt(&self) -> bool { - if let Some(pack) = self.pack { - if pack.bytes() == 1 { - return true; - } - } - self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() - } - - /// Returns `true` if this `#[repr()]` should inhibit union ABI optimisations. - pub fn inhibit_union_abi_opt(&self) -> bool { - self.c() - } -} - -impl<'tcx> AdtDef { - /// Creates a new `AdtDef`. - fn new( - tcx: TyCtxt<'_>, - did: DefId, - kind: AdtKind, - variants: IndexVec, - repr: ReprOptions, - ) -> Self { - debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); - let mut flags = AdtFlags::NO_ADT_FLAGS; - - if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { - debug!("found non-exhaustive variant list for {:?}", did); - flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; - } - - flags |= match kind { - AdtKind::Enum => AdtFlags::IS_ENUM, - AdtKind::Union => AdtFlags::IS_UNION, - AdtKind::Struct => AdtFlags::IS_STRUCT, - }; - - if kind == AdtKind::Struct && variants[VariantIdx::new(0)].ctor_def_id.is_some() { - flags |= AdtFlags::HAS_CTOR; - } - - let attrs = tcx.get_attrs(did); - if attr::contains_name(&attrs, sym::fundamental) { - flags |= AdtFlags::IS_FUNDAMENTAL; - } - if Some(did) == tcx.lang_items().phantom_data() { - flags |= AdtFlags::IS_PHANTOM_DATA; - } - if Some(did) == tcx.lang_items().owned_box() { - flags |= AdtFlags::IS_BOX; - } - if Some(did) == tcx.lang_items().manually_drop() { - flags |= AdtFlags::IS_MANUALLY_DROP; - } - if Some(did) == tcx.lang_items().arc() { - flags |= AdtFlags::IS_ARC; - } - if Some(did) == tcx.lang_items().rc() { - flags |= AdtFlags::IS_RC; - } - - AdtDef { did, variants, flags, repr } - } - - /// Returns `true` if this is a struct. - #[inline] - pub fn is_struct(&self) -> bool { - self.flags.contains(AdtFlags::IS_STRUCT) - } - - /// Returns `true` if this is a union. - #[inline] - pub fn is_union(&self) -> bool { - self.flags.contains(AdtFlags::IS_UNION) - } - - /// Returns `true` if this is a enum. - #[inline] - pub fn is_enum(&self) -> bool { - self.flags.contains(AdtFlags::IS_ENUM) - } - - /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. - #[inline] - pub fn is_variant_list_non_exhaustive(&self) -> bool { - self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) - } - - /// Returns the kind of the ADT. - #[inline] - pub fn adt_kind(&self) -> AdtKind { - if self.is_enum() { - AdtKind::Enum - } else if self.is_union() { - AdtKind::Union - } else { - AdtKind::Struct - } - } - - /// Returns a description of this abstract data type. - pub fn descr(&self) -> &'static str { - match self.adt_kind() { - AdtKind::Struct => "struct", - AdtKind::Union => "union", - AdtKind::Enum => "enum", - } - } - - /// Returns a description of a variant of this abstract data type. - #[inline] - pub fn variant_descr(&self) -> &'static str { - match self.adt_kind() { - AdtKind::Struct => "struct", - AdtKind::Union => "union", - AdtKind::Enum => "variant", - } - } - - /// If this function returns `true`, it implies that `is_struct` must return `true`. - #[inline] - pub fn has_ctor(&self) -> bool { - self.flags.contains(AdtFlags::HAS_CTOR) - } - - /// Returns `true` if this type is `#[fundamental]` for the purposes - /// of coherence checking. - #[inline] - pub fn is_fundamental(&self) -> bool { - self.flags.contains(AdtFlags::IS_FUNDAMENTAL) - } - - /// Returns `true` if this is `PhantomData`. - #[inline] - pub fn is_phantom_data(&self) -> bool { - self.flags.contains(AdtFlags::IS_PHANTOM_DATA) - } - - /// Returns `true` if this is `Arc`. - pub fn is_arc(&self) -> bool { - self.flags.contains(AdtFlags::IS_ARC) - } - - /// Returns `true` if this is `Rc`. - pub fn is_rc(&self) -> bool { - self.flags.contains(AdtFlags::IS_RC) - } - - /// Returns `true` if this is Box. - #[inline] - pub fn is_box(&self) -> bool { - self.flags.contains(AdtFlags::IS_BOX) - } - - /// Returns `true` if this is `ManuallyDrop`. - #[inline] - pub fn is_manually_drop(&self) -> bool { - self.flags.contains(AdtFlags::IS_MANUALLY_DROP) - } - - /// Returns `true` if this type has a destructor. - pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { - self.destructor(tcx).is_some() - } - - /// Asserts this is a struct or union and returns its unique variant. - pub fn non_enum_variant(&self) -> &VariantDef { - assert!(self.is_struct() || self.is_union()); - &self.variants[VariantIdx::new(0)] - } - - #[inline] - pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { - tcx.predicates_of(self.did) - } - - /// Returns an iterator over all fields contained - /// by this ADT. - #[inline] - pub fn all_fields(&self) -> impl Iterator + Clone { - self.variants.iter().flat_map(|v| v.fields.iter()) - } - - pub fn is_payloadfree(&self) -> bool { - !self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty()) - } - - /// Return a `VariantDef` given a variant id. - pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { - self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") - } - - /// Return a `VariantDef` given a constructor id. - pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef { - self.variants - .iter() - .find(|v| v.ctor_def_id == Some(cid)) - .expect("variant_with_ctor_id: unknown variant") - } - - /// Return the index of `VariantDef` given a variant id. - pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx { - self.variants - .iter_enumerated() - .find(|(_, v)| v.def_id == vid) - .expect("variant_index_with_id: unknown variant") - .0 - } - - /// Return the index of `VariantDef` given a constructor id. - pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx { - self.variants - .iter_enumerated() - .find(|(_, v)| v.ctor_def_id == Some(cid)) - .expect("variant_index_with_ctor_id: unknown variant") - .0 - } - - pub fn variant_of_res(&self, res: Res) -> &VariantDef { - match res { - Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid), - Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) - | Res::SelfTy(..) - | Res::SelfCtor(..) => self.non_enum_variant(), - _ => bug!("unexpected res {:?} in variant_of_res", res), - } - } - - #[inline] - pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { - let param_env = tcx.param_env(expr_did); - let repr_type = self.repr.discr_type(); - match tcx.const_eval_poly(expr_did) { - Ok(val) => { - let ty = repr_type.to_ty(tcx); - if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { - trace!("discriminants: {} ({:?})", b, repr_type); - Some(Discr { val: b, ty }) - } else { - info!("invalid enum discriminant: {:#?}", val); - crate::mir::interpret::struct_error( - tcx.at(tcx.def_span(expr_did)), - "constant evaluation of enum discriminant resulted in non-integer", - ) - .emit(); - None - } - } - Err(ErrorHandled::Reported) => { - if !expr_did.is_local() { - span_bug!( - tcx.def_span(expr_did), - "variant discriminant evaluation succeeded \ - in its crate but failed locally" - ); - } - None - } - Err(ErrorHandled::TooGeneric) => { - span_bug!(tcx.def_span(expr_did), "enum discriminant depends on generic arguments",) - } - } - } - - #[inline] - pub fn discriminants( - &'tcx self, - tcx: TyCtxt<'tcx>, - ) -> impl Iterator)> + Captures<'tcx> { - let repr_type = self.repr.discr_type(); - let initial = repr_type.initial_discriminant(tcx); - let mut prev_discr = None::>; - self.variants.iter_enumerated().map(move |(i, v)| { - let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); - if let VariantDiscr::Explicit(expr_did) = v.discr { - if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { - discr = new_discr; - } - } - prev_discr = Some(discr); - - (i, discr) - }) - } - - #[inline] - pub fn variant_range(&self) -> Range { - VariantIdx::new(0)..VariantIdx::new(self.variants.len()) - } - - /// Computes the discriminant value used by a specific variant. - /// Unlike `discriminants`, this is (amortized) constant-time, - /// only doing at most one query for evaluating an explicit - /// discriminant (the last one before the requested variant), - /// assuming there are no constant-evaluation errors there. - #[inline] - pub fn discriminant_for_variant( - &self, - tcx: TyCtxt<'tcx>, - variant_index: VariantIdx, - ) -> Discr<'tcx> { - let (val, offset) = self.discriminant_def_for_variant(variant_index); - let explicit_value = val - .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) - .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); - explicit_value.checked_add(tcx, offset as u128).0 - } - - /// Yields a `DefId` for the discriminant and an offset to add to it - /// Alternatively, if there is no explicit discriminant, returns the - /// inferred discriminant directly. - pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option, u32) { - let mut explicit_index = variant_index.as_u32(); - let expr_did; - loop { - match self.variants[VariantIdx::from_u32(explicit_index)].discr { - ty::VariantDiscr::Relative(0) => { - expr_did = None; - break; - } - ty::VariantDiscr::Relative(distance) => { - explicit_index -= distance; - } - ty::VariantDiscr::Explicit(did) => { - expr_did = Some(did); - break; - } - } - } - (expr_did, variant_index.as_u32() - explicit_index) - } - - pub fn destructor(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.adt_destructor(self.did) - } - - /// Returns a list of types such that `Self: Sized` if and only - /// if that type is `Sized`, or `TyErr` if this type is recursive. - /// - /// Oddly enough, checking that the sized-constraint is `Sized` is - /// actually more expressive than checking all members: - /// the `Sized` trait is inductive, so an associated type that references - /// `Self` would prevent its containing ADT from being `Sized`. - /// - /// Due to normalization being eager, this applies even if - /// the associated type is behind a pointer (e.g., issue #31299). - pub fn sized_constraint(&self, tcx: TyCtxt<'tcx>) -> &'tcx [Ty<'tcx>] { - tcx.adt_sized_constraint(self.did).0 - } -} - -impl<'tcx> FieldDef { - /// Returns the type of this field. The `subst` is typically obtained - /// via the second field of `TyKind::AdtDef`. - pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { - tcx.type_of(self.did).subst(tcx, subst) - } -} - -/// Represents the various closure traits in the language. This -/// will determine the type of the environment (`self`, in the -/// desugaring) argument that the closure expects. -/// -/// You can get the environment type of a closure using -/// `tcx.closure_env_ty()`. -#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable)] -pub enum ClosureKind { - // Warning: Ordering is significant here! The ordering is chosen - // because the trait Fn is a subtrait of FnMut and so in turn, and - // hence we order it so that Fn < FnMut < FnOnce. - Fn, - FnMut, - FnOnce, -} - -impl<'tcx> ClosureKind { - // This is the initial value used when doing upvar inference. - pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; - - pub fn trait_did(&self, tcx: TyCtxt<'tcx>) -> DefId { - match *self { - ClosureKind::Fn => tcx.require_lang_item(FnTraitLangItem, None), - ClosureKind::FnMut => tcx.require_lang_item(FnMutTraitLangItem, None), - ClosureKind::FnOnce => tcx.require_lang_item(FnOnceTraitLangItem, None), - } - } - - /// Returns `true` if this a type that impls this closure kind - /// must also implement `other`. - pub fn extends(self, other: ty::ClosureKind) -> bool { - match (self, other) { - (ClosureKind::Fn, ClosureKind::Fn) => true, - (ClosureKind::Fn, ClosureKind::FnMut) => true, - (ClosureKind::Fn, ClosureKind::FnOnce) => true, - (ClosureKind::FnMut, ClosureKind::FnMut) => true, - (ClosureKind::FnMut, ClosureKind::FnOnce) => true, - (ClosureKind::FnOnce, ClosureKind::FnOnce) => true, - _ => false, - } - } - - /// Returns the representative scalar type for this closure kind. - /// See `TyS::to_opt_closure_kind` for more details. - pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self { - ty::ClosureKind::Fn => tcx.types.i8, - ty::ClosureKind::FnMut => tcx.types.i16, - ty::ClosureKind::FnOnce => tcx.types.i32, - } - } -} - -impl<'tcx> TyS<'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: - /// - /// ```notrust - /// isize => { isize } - /// Foo> => { Foo>, Bar, isize } - /// [isize] => { [isize], isize } - /// ``` - pub fn walk(&'tcx self) -> TypeWalker<'tcx> { - TypeWalker::new(self) - } - - /// Iterator that walks the immediate children of `self`. Hence - /// `Foo, u32>` yields the sequence `[Bar, u32]` - /// (but not `i32`, like `walk`). - pub fn walk_shallow(&'tcx self) -> smallvec::IntoIter> { - walk::walk_shallow(self) - } - - /// Walks `ty` and any types appearing within `ty`, invoking the - /// callback `f` on each type. If the callback returns `false`, then the - /// children of the current type are ignored. - /// - /// Note: prefer `ty.walk()` where possible. - pub fn maybe_walk(&'tcx self, mut f: F) - where - F: FnMut(Ty<'tcx>) -> bool, - { - let mut walker = self.walk(); - while let Some(ty) = walker.next() { - if !f(ty) { - walker.skip_current_subtree(); - } - } - } -} - -impl BorrowKind { - pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { - match m { - hir::Mutability::Mut => MutBorrow, - hir::Mutability::Not => ImmBorrow, - } - } - - /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow - /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a - /// mutability that is stronger than necessary so that it at least *would permit* the borrow in - /// question. - pub fn to_mutbl_lossy(self) -> hir::Mutability { - match self { - MutBorrow => hir::Mutability::Mut, - ImmBorrow => hir::Mutability::Not, - - // We have no type corresponding to a unique imm borrow, so - // use `&mut`. It gives all the capabilities of an `&uniq` - // and hence is a safe "over approximation". - UniqueImmBorrow => hir::Mutability::Mut, - } - } - - pub fn to_user_str(&self) -> &'static str { - match *self { - MutBorrow => "mutable", - ImmBorrow => "immutable", - UniqueImmBorrow => "uniquely immutable", - } - } -} - -#[derive(Debug, Clone)] -pub enum Attributes<'tcx> { - Owned(Lrc<[ast::Attribute]>), - Borrowed(&'tcx [ast::Attribute]), -} - -impl<'tcx> ::std::ops::Deref for Attributes<'tcx> { - type Target = [ast::Attribute]; - - fn deref(&self) -> &[ast::Attribute] { - match self { - &Attributes::Owned(ref data) => &data, - &Attributes::Borrowed(data) => data, - } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub enum ImplOverlapKind { - /// These impls are always allowed to overlap. - Permitted { - /// Whether or not the impl is permitted due to the trait being a `#[marker]` trait - marker: bool, - }, - /// These impls are allowed to overlap, but that raises - /// an issue #33140 future-compatibility warning. - /// - /// Some background: in Rust 1.0, the trait-object types `Send + Sync` (today's - /// `dyn Send + Sync`) and `Sync + Send` (now `dyn Sync + Send`) were different. - /// - /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied - /// that difference, making what reduces to the following set of impls: - /// - /// ``` - /// trait Trait {} - /// impl Trait for dyn Send + Sync {} - /// impl Trait for dyn Sync + Send {} - /// ``` - /// - /// Obviously, once we made these types be identical, that code causes a coherence - /// error and a fairly big headache for us. However, luckily for us, the trait - /// `Trait` used in this case is basically a marker trait, and therefore having - /// overlapping impls for it is sound. - /// - /// To handle this, we basically regard the trait as a marker trait, with an additional - /// future-compatibility warning. To avoid accidentally "stabilizing" this feature, - /// it has the following restrictions: - /// - /// 1. The trait must indeed be a marker-like trait (i.e., no items), and must be - /// positive impls. - /// 2. The trait-ref of both impls must be equal. - /// 3. The trait-ref of both impls must be a trait object type consisting only of - /// marker traits. - /// 4. Neither of the impls can have any where-clauses. - /// - /// Once `traitobject` 0.1.0 is no longer an active concern, this hack can be removed. - Issue33140, -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn body_tables(self, body: hir::BodyId) -> &'tcx TypeckTables<'tcx> { - self.typeck_tables_of(self.hir().body_owner_def_id(body)) - } - - /// Returns an iterator of the `DefId`s for all body-owners in this - /// crate. If you would prefer to iterate over the bodies - /// themselves, you can do `self.hir().krate().body_ids.iter()`. - pub fn body_owners(self) -> impl Iterator + Captures<'tcx> + 'tcx { - self.hir() - .krate() - .body_ids - .iter() - .map(move |&body_id| self.hir().body_owner_def_id(body_id)) - } - - pub fn par_body_owners(self, f: F) { - par_iter(&self.hir().krate().body_ids) - .for_each(|&body_id| f(self.hir().body_owner_def_id(body_id))); - } - - pub fn provided_trait_methods(self, id: DefId) -> impl 'tcx + Iterator { - self.associated_items(id) - .in_definition_order() - .filter(|item| item.kind == AssocKind::Method && item.defaultness.has_value()) - } - - pub fn trait_relevant_for_never(self, did: DefId) -> bool { - self.associated_items(did).in_definition_order().any(|item| item.relevant_for_never()) - } - - pub fn opt_item_name(self, def_id: DefId) -> Option { - self.hir().as_local_hir_id(def_id).and_then(|hir_id| self.hir().get(hir_id).ident()) - } - - pub fn opt_associated_item(self, def_id: DefId) -> Option { - let is_associated_item = if let Some(hir_id) = self.hir().as_local_hir_id(def_id) { - match self.hir().get(hir_id) { - Node::TraitItem(_) | Node::ImplItem(_) => true, - _ => false, - } - } else { - match self.def_kind(def_id) { - Some(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy) => true, - _ => false, - } - }; - - is_associated_item.then(|| self.associated_item(def_id)) - } - - pub fn field_index(self, hir_id: hir::HirId, tables: &TypeckTables<'_>) -> usize { - tables.field_indices().get(hir_id).cloned().expect("no index for a field") - } - - pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option { - variant.fields.iter().position(|field| self.hygienic_eq(ident, field.ident, variant.def_id)) - } - - /// Returns `true` if the impls are the same polarity and the trait either - /// has no items or is annotated #[marker] and prevents item overrides. - pub fn impls_are_allowed_to_overlap( - self, - def_id1: DefId, - def_id2: DefId, - ) -> Option { - // If either trait impl references an error, they're allowed to overlap, - // as one of them essentially doesn't exist. - if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error()) - || self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error()) - { - return Some(ImplOverlapKind::Permitted { marker: false }); - } - - match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) { - (ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => { - // `#[rustc_reservation_impl]` impls don't overlap with anything - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)", - def_id1, def_id2 - ); - return Some(ImplOverlapKind::Permitted { marker: false }); - } - (ImplPolarity::Positive, ImplPolarity::Negative) - | (ImplPolarity::Negative, ImplPolarity::Positive) => { - // `impl AutoTrait for Type` + `impl !AutoTrait for Type` - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)", - def_id1, def_id2 - ); - return None; - } - (ImplPolarity::Positive, ImplPolarity::Positive) - | (ImplPolarity::Negative, ImplPolarity::Negative) => {} - }; - - let is_marker_overlap = { - let is_marker_impl = |def_id: DefId| -> bool { - let trait_ref = self.impl_trait_ref(def_id); - trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker) - }; - is_marker_impl(def_id1) && is_marker_impl(def_id2) - }; - - if is_marker_overlap { - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)", - def_id1, def_id2 - ); - Some(ImplOverlapKind::Permitted { marker: true }) - } else { - if let Some(self_ty1) = self.issue33140_self_ty(def_id1) { - if let Some(self_ty2) = self.issue33140_self_ty(def_id2) { - if self_ty1 == self_ty2 { - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK", - def_id1, def_id2 - ); - return Some(ImplOverlapKind::Issue33140); - } else { - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) - found {:?} != {:?}", - def_id1, def_id2, self_ty1, self_ty2 - ); - } - } - } - - debug!("impls_are_allowed_to_overlap({:?}, {:?}) = None", def_id1, def_id2); - None - } - } - - /// Returns `ty::VariantDef` if `res` refers to a struct, - /// or variant or their constructors, panics otherwise. - pub fn expect_variant_res(self, res: Res) -> &'tcx VariantDef { - match res { - Res::Def(DefKind::Variant, did) => { - let enum_did = self.parent(did).unwrap(); - self.adt_def(enum_did).variant_with_id(did) - } - Res::Def(DefKind::Struct, did) | Res::Def(DefKind::Union, did) => { - self.adt_def(did).non_enum_variant() - } - Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_did) => { - let variant_did = self.parent(variant_ctor_did).unwrap(); - let enum_did = self.parent(variant_did).unwrap(); - self.adt_def(enum_did).variant_with_ctor_id(variant_ctor_did) - } - Res::Def(DefKind::Ctor(CtorOf::Struct, ..), ctor_did) => { - let struct_did = self.parent(ctor_did).expect("struct ctor has no parent"); - self.adt_def(struct_did).non_enum_variant() - } - _ => bug!("expect_variant_res used with unexpected res {:?}", res), - } - } - - pub fn item_name(self, id: DefId) -> Symbol { - if id.index == CRATE_DEF_INDEX { - self.original_crate_name(id.krate) - } else { - let def_key = self.def_key(id); - match def_key.disambiguated_data.data { - // The name of a constructor is that of its parent. - hir_map::DefPathData::Ctor => { - self.item_name(DefId { krate: id.krate, index: def_key.parent.unwrap() }) - } - _ => def_key.disambiguated_data.data.get_opt_name().unwrap_or_else(|| { - bug!("item_name: no name for {:?}", self.def_path(id)); - }), - } - } - } - - /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair. - pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> ReadOnlyBodyAndCache<'tcx, 'tcx> { - match instance { - ty::InstanceDef::Item(did) => self.optimized_mir(did).unwrap_read_only(), - ty::InstanceDef::VtableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::Virtual(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::CloneShim(..) => self.mir_shims(instance).unwrap_read_only(), - } - } - - /// Gets the attributes of a definition. - pub fn get_attrs(self, did: DefId) -> Attributes<'tcx> { - if let Some(id) = self.hir().as_local_hir_id(did) { - Attributes::Borrowed(self.hir().attrs(id)) - } else { - Attributes::Owned(self.item_attrs(did)) - } - } - - /// Determines whether an item is annotated with an attribute. - pub fn has_attr(self, did: DefId, attr: Symbol) -> bool { - attr::contains_name(&self.get_attrs(did), attr) - } - - /// Returns `true` if this is an `auto trait`. - pub fn trait_is_auto(self, trait_def_id: DefId) -> bool { - self.trait_def(trait_def_id).has_auto_impl - } - - pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> { - self.optimized_mir(def_id).generator_layout.as_ref().unwrap() - } - - /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements. - /// If it implements no trait, returns `None`. - pub fn trait_id_of_impl(self, def_id: DefId) -> Option { - self.impl_trait_ref(def_id).map(|tr| tr.def_id) - } - - /// If the given defid describes a method belonging to an impl, returns the - /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. - pub fn impl_of_method(self, def_id: DefId) -> Option { - self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container { - TraitContainer(_) => None, - ImplContainer(def_id) => Some(def_id), - }) - } - - /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` - /// with the name of the crate containing the impl. - pub fn span_of_impl(self, impl_did: DefId) -> Result { - if impl_did.is_local() { - let hir_id = self.hir().as_local_hir_id(impl_did).unwrap(); - Ok(self.hir().span(hir_id)) - } else { - Err(self.crate_name(impl_did.krate)) - } - } - - /// Hygienically compares a use-site name (`use_name`) for a field or an associated item with - /// its supposed definition name (`def_name`). The method also needs `DefId` of the supposed - /// definition's parent/scope to perform comparison. - pub fn hygienic_eq(self, use_name: Ident, def_name: Ident, def_parent_def_id: DefId) -> bool { - // We could use `Ident::eq` here, but we deliberately don't. The name - // comparison fails frequently, and we want to avoid the expensive - // `normalize_to_macros_2_0()` calls required for the span comparison whenever possible. - use_name.name == def_name.name - && use_name - .span - .ctxt() - .hygienic_eq(def_name.span.ctxt(), self.expansion_that_defined(def_parent_def_id)) - } - - fn expansion_that_defined(self, scope: DefId) -> ExpnId { - match scope.as_local() { - Some(scope) => self.hir().definitions().expansion_that_defined(scope), - None => ExpnId::root(), - } - } - - pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { - ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)); - ident - } - - pub fn adjust_ident_and_get_scope( - self, - mut ident: Ident, - scope: DefId, - block: hir::HirId, - ) -> (Ident, DefId) { - let scope = - match ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)) - { - Some(actual_expansion) => { - self.hir().definitions().parent_module_of_macro_def(actual_expansion) - } - None => self.parent_module(block).to_def_id(), - }; - (ident, scope) - } - - pub fn is_object_safe(self, key: DefId) -> bool { - self.object_safety_violations(key).is_empty() - } -} - -#[derive(Clone, HashStable)] -pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); - -/// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition. -pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) { - if let Node::Item(item) = tcx.hir().get(hir_id) { - if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { - return opaque_ty.impl_trait_fn; - } - } - } - None -} - -pub fn provide(providers: &mut ty::query::Providers<'_>) { - context::provide(providers); - erase_regions::provide(providers); - layout::provide(providers); - super::util::bug::provide(providers); - *providers = ty::query::Providers { - trait_impls_of: trait_def::trait_impls_of_provider, - all_local_trait_impls: trait_def::all_local_trait_impls, - ..*providers - }; -} - -/// A map for the local crate mapping each type to a vector of its -/// inherent impls. This is not meant to be used outside of coherence; -/// rather, you should request the vector for a specific type via -/// `tcx.inherent_impls(def_id)` so as to minimize your dependencies -/// (constructing this map requires touching the entire crate). -#[derive(Clone, Debug, Default, HashStable)] -pub struct CrateInherentImpls { - pub inherent_impls: DefIdMap>, -} - -#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub struct SymbolName { - // FIXME: we don't rely on interning or equality here - better have - // this be a `&'tcx str`. - pub name: Symbol, -} - -impl SymbolName { - pub fn new(name: &str) -> SymbolName { - SymbolName { name: Symbol::intern(name) } - } -} - -impl PartialOrd for SymbolName { - fn partial_cmp(&self, other: &SymbolName) -> Option { - self.name.as_str().partial_cmp(&other.name.as_str()) - } -} - -/// Ordering must use the chars to ensure reproducible builds. -impl Ord for SymbolName { - fn cmp(&self, other: &SymbolName) -> Ordering { - self.name.as_str().cmp(&other.name.as_str()) - } -} - -impl fmt::Display for SymbolName { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.name, fmt) - } -} - -impl fmt::Debug for SymbolName { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.name, fmt) - } -} diff --git a/src/librustc/ty/outlives.rs b/src/librustc/ty/outlives.rs deleted file mode 100644 index 9dd96f2f2b507..0000000000000 --- a/src/librustc/ty/outlives.rs +++ /dev/null @@ -1,178 +0,0 @@ -// The outlines relation `T: 'a` or `'a: 'b`. This code frequently -// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that -// RFC for reference. - -use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; -use smallvec::SmallVec; - -#[derive(Debug)] -pub enum Component<'tcx> { - Region(ty::Region<'tcx>), - Param(ty::ParamTy), - UnresolvedInferenceVariable(ty::InferTy), - - // Projections like `T::Foo` are tricky because a constraint like - // `T::Foo: 'a` can be satisfied in so many ways. There may be a - // where-clause that says `T::Foo: 'a`, or the defining trait may - // include a bound like `type Foo: 'static`, or -- in the most - // conservative way -- we can prove that `T: 'a` (more generally, - // that all components in the projection outlive `'a`). This code - // is not in a position to judge which is the best technique, so - // we just product the projection as a component and leave it to - // the consumer to decide (but see `EscapingProjection` below). - Projection(ty::ProjectionTy<'tcx>), - - // In the case where a projection has escaping regions -- meaning - // regions bound within the type itself -- we always use - // the most conservative rule, which requires that all components - // outlive the bound. So for example if we had a type like this: - // - // for<'a> Trait1< >::Foo > - // ~~~~~~~~~~~~~~~~~~~~~~~~~ - // - // then the inner projection (underlined) has an escaping region - // `'a`. We consider that outer trait `'c` to meet a bound if `'b` - // outlives `'b: 'c`, and we don't consider whether the trait - // declares that `Foo: 'static` etc. Therefore, we just return the - // free components of such a projection (in this case, `'b`). - // - // However, in the future, we may want to get smarter, and - // actually return a "higher-ranked projection" here. Therefore, - // we mark that these components are part of an escaping - // projection, so that implied bounds code can avoid relying on - // them. This gives us room to improve the regionck reasoning in - // the future without breaking backwards compat. - EscapingProjection(Vec>), -} - -impl<'tcx> TyCtxt<'tcx> { - /// Push onto `out` all the things that must outlive `'a` for the condition - /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. - pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { - compute_components(self, ty0, out); - debug!("components({:?}) = {:?}", ty0, out); - } -} - -fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { - // Descend through the types, looking for the various "base" - // components and collecting them into `out`. This is not written - // with `collect()` because of the need to sometimes skip subtrees - // in the `subtys` iterator (e.g., when encountering a - // projection). - match ty.kind { - ty::Closure(_, ref substs) => { - for upvar_ty in substs.as_closure().upvar_tys() { - compute_components(tcx, upvar_ty, out); - } - } - - ty::Generator(_, ref substs, _) => { - // Same as the closure case - for upvar_ty in substs.as_generator().upvar_tys() { - compute_components(tcx, upvar_ty, out); - } - - // We ignore regions in the generator interior as we don't - // want these to affect region inference - } - - // All regions are bound inside a witness - ty::GeneratorWitness(..) => (), - - // OutlivesTypeParameterEnv -- the actual checking that `X:'a` - // is implied by the environment is done in regionck. - ty::Param(p) => { - out.push(Component::Param(p)); - } - - // For projections, we prefer to generate an obligation like - // `>::Foo: 'a`, because this gives the - // regionck more ways to prove that it holds. However, - // regionck is not (at least currently) prepared to deal with - // higher-ranked regions that may appear in the - // trait-ref. Therefore, if we see any higher-ranke regions, - // we simply fallback to the most restrictive rule, which - // requires that `Pi: 'a` for all `i`. - ty::Projection(ref data) => { - if !data.has_escaping_bound_vars() { - // best case: no escaping regions, so push the - // projection and skip the subtree (thus generating no - // constraints for Pi). This defers the choice between - // the rules OutlivesProjectionEnv, - // OutlivesProjectionTraitDef, and - // OutlivesProjectionComponents to regionck. - out.push(Component::Projection(*data)); - } else { - // fallback case: hard code - // OutlivesProjectionComponents. Continue walking - // through and constrain Pi. - let subcomponents = capture_components(tcx, ty); - out.push(Component::EscapingProjection(subcomponents)); - } - } - - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - - // We assume that inference variables are fully resolved. - // So, if we encounter an inference variable, just record - // the unresolved variable as a component. - ty::Infer(infer_ty) => { - out.push(Component::UnresolvedInferenceVariable(infer_ty)); - } - - // Most types do not introduce any region binders, nor - // involve any other subtle cases, and so the WF relation - // simply constraints any regions referenced directly by - // the type and then visits the types that are lexically - // contained within. (The comments refer to relevant rules - // from RFC1214.) - ty::Bool | // OutlivesScalar - ty::Char | // OutlivesScalar - ty::Int(..) | // OutlivesScalar - ty::Uint(..) | // OutlivesScalar - ty::Float(..) | // OutlivesScalar - ty::Never | // ... - ty::Adt(..) | // OutlivesNominalType - ty::Opaque(..) | // OutlivesNominalType (ish) - ty::Foreign(..) | // OutlivesNominalType - ty::Str | // OutlivesScalar (ish) - ty::Array(..) | // ... - ty::Slice(..) | // ... - ty::RawPtr(..) | // ... - ty::Ref(..) | // OutlivesReference - ty::Tuple(..) | // ... - ty::FnDef(..) | // OutlivesFunction (*) - ty::FnPtr(_) | // OutlivesFunction (*) - ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) - ty::Placeholder(..) | - ty::Bound(..) | - ty::Error => { - // (*) Bare functions and traits are both binders. In the - // RFC, this means we would add the bound regions to the - // "bound regions list". In our representation, no such - // list is maintained explicitly, because bound regions - // themselves can be readily identified. - - push_region_constraints(ty, out); - for subty in ty.walk_shallow() { - compute_components(tcx, subty, out); - } - } - } -} - -fn capture_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Vec> { - let mut temp = smallvec![]; - push_region_constraints(ty, &mut temp); - for subty in ty.walk_shallow() { - compute_components(tcx, subty, &mut temp); - } - temp.into_iter().collect() -} - -fn push_region_constraints<'tcx>(ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { - let mut regions = smallvec![]; - ty.push_regions(&mut regions); - out.extend(regions.iter().filter(|&r| !r.is_late_bound()).map(|r| Component::Region(r))); -} diff --git a/src/librustc/ty/print/mod.rs b/src/librustc/ty/print/mod.rs deleted file mode 100644 index 8d784833bd310..0000000000000 --- a/src/librustc/ty/print/mod.rs +++ /dev/null @@ -1,347 +0,0 @@ -use crate::hir::map::{DefPathData, DisambiguatedDefPathData}; -use crate::ty::subst::{GenericArg, Subst}; -use crate::ty::{self, DefIdTree, Ty, TyCtxt}; - -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::{CrateNum, DefId}; - -// `pretty` is a separate module only for organization. -mod pretty; -pub use self::pretty::*; - -pub mod obsolete; - -// FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`. -#[allow(unused_lifetimes)] -pub trait Print<'tcx, P> { - type Output; - type Error; - - fn print(&self, cx: P) -> Result; -} - -/// Interface for outputting user-facing "type-system entities" -/// (paths, types, lifetimes, constants, etc.) as a side-effect -/// (e.g. formatting, like `PrettyPrinter` implementors do) or by -/// constructing some alternative representation (e.g. an AST), -/// which the associated types allow passing through the methods. -/// -/// For pretty-printing/formatting in particular, see `PrettyPrinter`. -// -// FIXME(eddyb) find a better name; this is more general than "printing". -pub trait Printer<'tcx>: Sized { - type Error; - - type Path; - type Region; - type Type; - type DynExistential; - type Const; - - fn tcx(&'a self) -> TyCtxt<'tcx>; - - fn print_def_path( - self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - self.default_print_def_path(def_id, substs) - } - - fn print_impl_path( - self, - impl_def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self.default_print_impl_path(impl_def_id, substs, self_ty, trait_ref) - } - - fn print_region(self, region: ty::Region<'_>) -> Result; - - fn print_type(self, ty: Ty<'tcx>) -> Result; - - fn print_dyn_existential( - self, - predicates: &'tcx ty::List>, - ) -> Result; - - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result; - - fn path_crate(self, cnum: CrateNum) -> Result; - - fn path_qualified( - self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result; - - fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result; - - fn path_append( - self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result; - - fn path_generic_args( - self, - print_prefix: impl FnOnce(Self) -> Result, - args: &[GenericArg<'tcx>], - ) -> Result; - - // Defaults (should not be overridden): - - fn default_print_def_path( - self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs); - let key = self.tcx().def_key(def_id); - debug!("default_print_def_path: key={:?}", key); - - match key.disambiguated_data.data { - DefPathData::CrateRoot => { - assert!(key.parent.is_none()); - self.path_crate(def_id.krate) - } - - DefPathData::Impl => { - let generics = self.tcx().generics_of(def_id); - let mut self_ty = self.tcx().type_of(def_id); - let mut impl_trait_ref = self.tcx().impl_trait_ref(def_id); - if substs.len() >= generics.count() { - self_ty = self_ty.subst(self.tcx(), substs); - impl_trait_ref = impl_trait_ref.subst(self.tcx(), substs); - } - self.print_impl_path(def_id, substs, self_ty, impl_trait_ref) - } - - _ => { - let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; - - let mut parent_substs = substs; - let mut trait_qualify_parent = false; - if !substs.is_empty() { - let generics = self.tcx().generics_of(def_id); - parent_substs = &substs[..generics.parent_count.min(substs.len())]; - - match key.disambiguated_data.data { - // Closures' own generics are only captures, don't print them. - DefPathData::ClosureExpr => {} - - // If we have any generic arguments to print, we do that - // on top of the same path, but without its own generics. - _ => { - if !generics.params.is_empty() && substs.len() >= generics.count() { - let args = self.generic_args_to_print(generics, substs); - return self.path_generic_args( - |cx| cx.print_def_path(def_id, parent_substs), - args, - ); - } - } - } - - // FIXME(eddyb) try to move this into the parent's printing - // logic, instead of doing it when printing the child. - trait_qualify_parent = generics.has_self - && generics.parent == Some(parent_def_id) - && parent_substs.len() == generics.parent_count - && self.tcx().generics_of(parent_def_id).parent_count == 0; - } - - self.path_append( - |cx: Self| { - if trait_qualify_parent { - let trait_ref = ty::TraitRef::new( - parent_def_id, - cx.tcx().intern_substs(parent_substs), - ); - cx.path_qualified(trait_ref.self_ty(), Some(trait_ref)) - } else { - cx.print_def_path(parent_def_id, parent_substs) - } - }, - &key.disambiguated_data, - ) - } - } - } - - fn generic_args_to_print( - &self, - generics: &'tcx ty::Generics, - substs: &'tcx [GenericArg<'tcx>], - ) -> &'tcx [GenericArg<'tcx>] { - let mut own_params = generics.parent_count..generics.count(); - - // Don't print args for `Self` parameters (of traits). - if generics.has_self && own_params.start == 0 { - own_params.start = 1; - } - - // Don't print args that are the defaults of their respective parameters. - own_params.end -= generics - .params - .iter() - .rev() - .take_while(|param| { - match param.kind { - ty::GenericParamDefKind::Lifetime => false, - ty::GenericParamDefKind::Type { has_default, .. } => { - has_default - && substs[param.index as usize] - == GenericArg::from( - self.tcx().type_of(param.def_id).subst(self.tcx(), substs), - ) - } - ty::GenericParamDefKind::Const => false, // FIXME(const_generics:defaults) - } - }) - .count(); - - &substs[own_params] - } - - fn default_print_impl_path( - self, - impl_def_id: DefId, - _substs: &'tcx [GenericArg<'tcx>], - self_ty: Ty<'tcx>, - impl_trait_ref: Option>, - ) -> Result { - debug!( - "default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}", - impl_def_id, self_ty, impl_trait_ref - ); - - let key = self.tcx().def_key(impl_def_id); - let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; - - // Decide whether to print the parent path for the impl. - // Logically, since impls are global, it's never needed, but - // users may find it useful. Currently, we omit the parent if - // the impl is either in the same module as the self-type or - // as the trait. - let in_self_mod = match characteristic_def_id_of_type(self_ty) { - None => false, - Some(ty_def_id) => self.tcx().parent(ty_def_id) == Some(parent_def_id), - }; - let in_trait_mod = match impl_trait_ref { - None => false, - Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == Some(parent_def_id), - }; - - if !in_self_mod && !in_trait_mod { - // If the impl is not co-located with either self-type or - // trait-type, then fallback to a format that identifies - // the module more clearly. - self.path_append_impl( - |cx| cx.print_def_path(parent_def_id, &[]), - &key.disambiguated_data, - self_ty, - impl_trait_ref, - ) - } else { - // Otherwise, try to give a good form that would be valid language - // syntax. Preferably using associated item notation. - self.path_qualified(self_ty, impl_trait_ref) - } - } -} - -/// As a heuristic, when we see an impl, if we see that the -/// 'self type' is a type defined in the same module as the impl, -/// we can omit including the path to the impl itself. This -/// function tries to find a "characteristic `DefId`" for a -/// type. It's just a heuristic so it makes some questionable -/// decisions and we may want to adjust it later. -pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { - match ty.kind { - ty::Adt(adt_def, _) => Some(adt_def.did), - - ty::Dynamic(data, ..) => data.principal_def_id(), - - ty::Array(subty, _) | ty::Slice(subty) => characteristic_def_id_of_type(subty), - - ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty), - - ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty), - - ty::Tuple(ref tys) => { - tys.iter().filter_map(|ty| characteristic_def_id_of_type(ty.expect_ty())).next() - } - - ty::FnDef(def_id, _) - | ty::Closure(def_id, _) - | ty::Generator(def_id, _, _) - | ty::Foreign(def_id) => Some(def_id), - - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Str - | ty::FnPtr(_) - | ty::Projection(_) - | ty::Placeholder(..) - | ty::UnnormalizedProjection(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Error - | ty::GeneratorWitness(..) - | ty::Never - | ty::Float(_) => None, - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { - type Output = P::Region; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_region(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'_> { - type Output = P::Region; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_region(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { - type Output = P::Type; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_type(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List> { - type Output = P::DynExistential; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_dyn_existential(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::Const<'tcx> { - type Output = P::Const; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_const(self) - } -} diff --git a/src/librustc/ty/query/caches.rs b/src/librustc/ty/query/caches.rs deleted file mode 100644 index a11b3bcba3ed3..0000000000000 --- a/src/librustc/ty/query/caches.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::dep_graph::DepNodeIndex; -use crate::ty::query::plumbing::{QueryLookup, QueryState, QueryStateShard}; -use crate::ty::TyCtxt; - -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sharded::Sharded; -use std::default::Default; -use std::hash::Hash; -use std::marker::PhantomData; - -pub(crate) trait CacheSelector { - type Cache: QueryCache; -} - -pub(crate) trait QueryCache: Default { - type Key; - type Value; - type Sharded: Default; - - /// Checks if the query is already computed and in the cache. - /// It returns the shard index and a lock guard to the shard, - /// which will be used if the query is not in the cache and we need - /// to compute it. - fn lookup<'tcx, R, GetCache, OnHit, OnMiss>( - &self, - state: &'tcx QueryState<'tcx, Self>, - get_cache: GetCache, - key: Self::Key, - // `on_hit` can be called while holding a lock to the query state shard. - on_hit: OnHit, - on_miss: OnMiss, - ) -> R - where - GetCache: for<'a> Fn( - &'a mut QueryStateShard<'tcx, Self::Key, Self::Sharded>, - ) -> &'a mut Self::Sharded, - OnHit: FnOnce(&Self::Value, DepNodeIndex) -> R, - OnMiss: FnOnce(Self::Key, QueryLookup<'tcx, Self::Key, Self::Sharded>) -> R; - - fn complete( - &self, - tcx: TyCtxt<'tcx>, - lock_sharded_storage: &mut Self::Sharded, - key: Self::Key, - value: Self::Value, - index: DepNodeIndex, - ); - - fn iter( - &self, - shards: &Sharded, - get_shard: impl Fn(&mut L) -> &mut Self::Sharded, - f: impl for<'a> FnOnce( - Box + 'a>, - ) -> R, - ) -> R; -} - -pub struct DefaultCacheSelector; - -impl CacheSelector for DefaultCacheSelector { - type Cache = DefaultCache; -} - -pub struct DefaultCache(PhantomData<(K, V)>); - -impl Default for DefaultCache { - fn default() -> Self { - DefaultCache(PhantomData) - } -} - -impl QueryCache for DefaultCache { - type Key = K; - type Value = V; - type Sharded = FxHashMap; - - #[inline(always)] - fn lookup<'tcx, R, GetCache, OnHit, OnMiss>( - &self, - state: &'tcx QueryState<'tcx, Self>, - get_cache: GetCache, - key: K, - on_hit: OnHit, - on_miss: OnMiss, - ) -> R - where - GetCache: - for<'a> Fn(&'a mut QueryStateShard<'tcx, K, Self::Sharded>) -> &'a mut Self::Sharded, - OnHit: FnOnce(&V, DepNodeIndex) -> R, - OnMiss: FnOnce(K, QueryLookup<'tcx, K, Self::Sharded>) -> R, - { - let mut lookup = state.get_lookup(&key); - let lock = &mut *lookup.lock; - - let result = get_cache(lock).raw_entry().from_key_hashed_nocheck(lookup.key_hash, &key); - - if let Some((_, value)) = result { on_hit(&value.0, value.1) } else { on_miss(key, lookup) } - } - - #[inline] - fn complete( - &self, - _: TyCtxt<'tcx>, - lock_sharded_storage: &mut Self::Sharded, - key: K, - value: V, - index: DepNodeIndex, - ) { - lock_sharded_storage.insert(key, (value, index)); - } - - fn iter( - &self, - shards: &Sharded, - get_shard: impl Fn(&mut L) -> &mut Self::Sharded, - f: impl for<'a> FnOnce(Box + 'a>) -> R, - ) -> R { - let mut shards = shards.lock_shards(); - let mut shards: Vec<_> = shards.iter_mut().map(|shard| get_shard(shard)).collect(); - let results = shards.iter_mut().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1)); - f(Box::new(results)) - } -} diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs deleted file mode 100644 index 72a0fdf156726..0000000000000 --- a/src/librustc/ty/query/config.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::dep_graph::SerializedDepNodeIndex; -use crate::dep_graph::{DepKind, DepNode}; -use crate::ty::query::caches::QueryCache; -use crate::ty::query::plumbing::CycleError; -use crate::ty::query::QueryState; -use crate::ty::TyCtxt; -use rustc_data_structures::profiling::ProfileCategory; -use rustc_hir::def_id::DefId; - -use crate::ich::StableHashingContext; -use rustc_data_structures::fingerprint::Fingerprint; -use std::borrow::Cow; -use std::fmt::Debug; -use std::hash::Hash; - -// Query configuration and description traits. - -// FIXME(eddyb) false positive, the lifetime parameter is used for `Key`/`Value`. -#[allow(unused_lifetimes)] -pub trait QueryConfig<'tcx> { - const NAME: &'static str; - const CATEGORY: ProfileCategory; - - type Key: Eq + Hash + Clone + Debug; - type Value: Clone; -} - -pub(crate) trait QueryAccessors<'tcx>: QueryConfig<'tcx> { - const ANON: bool; - const EVAL_ALWAYS: bool; - const DEP_KIND: DepKind; - - type Cache: QueryCache; - - // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state<'a>(tcx: TyCtxt<'tcx>) -> &'a QueryState<'tcx, Self::Cache>; - - fn to_dep_node(tcx: TyCtxt<'tcx>, key: &Self::Key) -> DepNode; - - // Don't use this method to compute query results, instead use the methods on TyCtxt - fn compute(tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value; - - fn hash_result(hcx: &mut StableHashingContext<'_>, result: &Self::Value) - -> Option; - - fn handle_cycle_error(tcx: TyCtxt<'tcx>, error: CycleError<'tcx>) -> Self::Value; -} - -pub(crate) trait QueryDescription<'tcx>: QueryAccessors<'tcx> { - fn describe(tcx: TyCtxt<'_>, key: Self::Key) -> Cow<'static, str>; - - #[inline] - fn cache_on_disk(_: TyCtxt<'tcx>, _: Self::Key, _: Option<&Self::Value>) -> bool { - false - } - - fn try_load_from_disk(_: TyCtxt<'tcx>, _: SerializedDepNodeIndex) -> Option { - bug!("QueryDescription::load_from_disk() called for an unsupported query.") - } -} - -impl<'tcx, M: QueryAccessors<'tcx, Key = DefId>> QueryDescription<'tcx> for M { - default fn describe(tcx: TyCtxt<'_>, def_id: DefId) -> Cow<'static, str> { - if !tcx.sess.verbose() { - format!("processing `{}`", tcx.def_path_str(def_id)).into() - } else { - let name = ::std::any::type_name::(); - format!("processing {:?} with query `{}`", def_id, name).into() - } - } - - default fn cache_on_disk(_: TyCtxt<'tcx>, _: Self::Key, _: Option<&Self::Value>) -> bool { - false - } - - default fn try_load_from_disk( - _: TyCtxt<'tcx>, - _: SerializedDepNodeIndex, - ) -> Option { - bug!("QueryDescription::load_from_disk() called for an unsupported query.") - } -} diff --git a/src/librustc/ty/query/job.rs b/src/librustc/ty/query/job.rs deleted file mode 100644 index 4e88fc5463794..0000000000000 --- a/src/librustc/ty/query/job.rs +++ /dev/null @@ -1,589 +0,0 @@ -use crate::dep_graph::DepKind; -use crate::ty::context::TyCtxt; -use crate::ty::query::plumbing::CycleError; -use crate::ty::query::Query; -use crate::ty::tls; - -use rustc_data_structures::fx::FxHashMap; -use rustc_span::Span; - -use std::convert::TryFrom; -use std::marker::PhantomData; -use std::num::NonZeroU32; - -#[cfg(parallel_compiler)] -use { - parking_lot::{Condvar, Mutex}, - rustc_data_structures::fx::FxHashSet, - rustc_data_structures::stable_hasher::{HashStable, StableHasher}, - rustc_data_structures::sync::Lock, - rustc_data_structures::sync::Lrc, - rustc_data_structures::{jobserver, OnDrop}, - rustc_rayon_core as rayon_core, - rustc_span::DUMMY_SP, - std::iter::FromIterator, - std::{mem, process, thread}, -}; - -/// Represents a span and a query key. -#[derive(Clone, Debug)] -pub struct QueryInfo<'tcx> { - /// The span corresponding to the reason for which this query was required. - pub span: Span, - pub query: Query<'tcx>, -} - -type QueryMap<'tcx> = FxHashMap>; - -/// A value uniquely identifiying an active query job within a shard in the query cache. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct QueryShardJobId(pub NonZeroU32); - -/// A value uniquely identifiying an active query job. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct QueryJobId { - /// Which job within a shard is this - pub job: QueryShardJobId, - - /// In which shard is this job - pub shard: u16, - - /// What kind of query this job is - pub kind: DepKind, -} - -impl QueryJobId { - pub fn new(job: QueryShardJobId, shard: usize, kind: DepKind) -> Self { - QueryJobId { job, shard: u16::try_from(shard).unwrap(), kind } - } - - fn query<'tcx>(self, map: &QueryMap<'tcx>) -> Query<'tcx> { - map.get(&self).unwrap().info.query.clone() - } - - #[cfg(parallel_compiler)] - fn span(self, map: &QueryMap<'_>) -> Span { - map.get(&self).unwrap().job.span - } - - #[cfg(parallel_compiler)] - fn parent(self, map: &QueryMap<'_>) -> Option { - map.get(&self).unwrap().job.parent - } - - #[cfg(parallel_compiler)] - fn latch<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option<&'a QueryLatch<'tcx>> { - map.get(&self).unwrap().job.latch.as_ref() - } -} - -pub struct QueryJobInfo<'tcx> { - pub info: QueryInfo<'tcx>, - pub job: QueryJob<'tcx>, -} - -/// Represents an active query job. -#[derive(Clone)] -pub struct QueryJob<'tcx> { - pub id: QueryShardJobId, - - /// The span corresponding to the reason for which this query was required. - pub span: Span, - - /// The parent query job which created this job and is implicitly waiting on it. - pub parent: Option, - - /// The latch that is used to wait on this job. - #[cfg(parallel_compiler)] - latch: Option>, - - dummy: PhantomData>, -} - -impl<'tcx> QueryJob<'tcx> { - /// Creates a new query job. - pub fn new(id: QueryShardJobId, span: Span, parent: Option) -> Self { - QueryJob { - id, - span, - parent, - #[cfg(parallel_compiler)] - latch: None, - dummy: PhantomData, - } - } - - #[cfg(parallel_compiler)] - pub(super) fn latch(&mut self, _id: QueryJobId) -> QueryLatch<'tcx> { - if self.latch.is_none() { - self.latch = Some(QueryLatch::new()); - } - self.latch.as_ref().unwrap().clone() - } - - #[cfg(not(parallel_compiler))] - pub(super) fn latch(&mut self, id: QueryJobId) -> QueryLatch<'tcx> { - QueryLatch { id, dummy: PhantomData } - } - - /// Signals to waiters that the query is complete. - /// - /// This does nothing for single threaded rustc, - /// as there are no concurrent jobs which could be waiting on us - pub fn signal_complete(self) { - #[cfg(parallel_compiler)] - self.latch.map(|latch| latch.set()); - } -} - -#[cfg(not(parallel_compiler))] -#[derive(Clone)] -pub(super) struct QueryLatch<'tcx> { - id: QueryJobId, - dummy: PhantomData<&'tcx ()>, -} - -#[cfg(not(parallel_compiler))] -impl<'tcx> QueryLatch<'tcx> { - pub(super) fn find_cycle_in_stack(&self, tcx: TyCtxt<'tcx>, span: Span) -> CycleError<'tcx> { - let query_map = tcx.queries.try_collect_active_jobs().unwrap(); - - // Get the current executing query (waiter) and find the waitee amongst its parents - let mut current_job = tls::with_related_context(tcx, |icx| icx.query); - let mut cycle = Vec::new(); - - while let Some(job) = current_job { - let info = query_map.get(&job).unwrap(); - cycle.push(info.info.clone()); - - if job == self.id { - cycle.reverse(); - - // This is the end of the cycle - // The span entry we included was for the usage - // of the cycle itself, and not part of the cycle - // Replace it with the span which caused the cycle to form - cycle[0].span = span; - // Find out why the cycle itself was used - let usage = info - .job - .parent - .as_ref() - .map(|parent| (info.info.span, parent.query(&query_map))); - return CycleError { usage, cycle }; - } - - current_job = info.job.parent; - } - - panic!("did not find a cycle") - } -} - -#[cfg(parallel_compiler)] -struct QueryWaiter<'tcx> { - query: Option, - condvar: Condvar, - span: Span, - cycle: Lock>>, -} - -#[cfg(parallel_compiler)] -impl<'tcx> QueryWaiter<'tcx> { - fn notify(&self, registry: &rayon_core::Registry) { - rayon_core::mark_unblocked(registry); - self.condvar.notify_one(); - } -} - -#[cfg(parallel_compiler)] -struct QueryLatchInfo<'tcx> { - complete: bool, - waiters: Vec>>, -} - -#[cfg(parallel_compiler)] -#[derive(Clone)] -pub(super) struct QueryLatch<'tcx> { - info: Lrc>>, -} - -#[cfg(parallel_compiler)] -impl<'tcx> QueryLatch<'tcx> { - fn new() -> Self { - QueryLatch { - info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), - } - } - - /// Awaits for the query job to complete. - #[cfg(parallel_compiler)] - pub(super) fn wait_on(&self, tcx: TyCtxt<'tcx>, span: Span) -> Result<(), CycleError<'tcx>> { - tls::with_related_context(tcx, move |icx| { - let waiter = Lrc::new(QueryWaiter { - query: icx.query, - span, - cycle: Lock::new(None), - condvar: Condvar::new(), - }); - self.wait_on_inner(&waiter); - // FIXME: Get rid of this lock. We have ownership of the QueryWaiter - // although another thread may still have a Lrc reference so we cannot - // use Lrc::get_mut - let mut cycle = waiter.cycle.lock(); - match cycle.take() { - None => Ok(()), - Some(cycle) => Err(cycle), - } - }) - } - - /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, waiter: &Lrc>) { - let mut info = self.info.lock(); - if !info.complete { - // We push the waiter on to the `waiters` list. It can be accessed inside - // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. - // Both of these will remove it from the `waiters` list before resuming - // this thread. - info.waiters.push(waiter.clone()); - - // If this detects a deadlock and the deadlock handler wants to resume this thread - // we have to be in the `wait` call. This is ensured by the deadlock handler - // getting the self.info lock. - rayon_core::mark_blocked(); - jobserver::release_thread(); - waiter.condvar.wait(&mut info); - // Release the lock before we potentially block in `acquire_thread` - mem::drop(info); - jobserver::acquire_thread(); - } - } - - /// Sets the latch and resumes all waiters on it - fn set(&self) { - let mut info = self.info.lock(); - debug_assert!(!info.complete); - info.complete = true; - let registry = rayon_core::Registry::current(); - for waiter in info.waiters.drain(..) { - waiter.notify(®istry); - } - } - - /// Removes a single waiter from the list of waiters. - /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Lrc> { - let mut info = self.info.lock(); - debug_assert!(!info.complete); - // Remove the waiter from the list of waiters - info.waiters.remove(waiter) - } -} - -/// A resumable waiter of a query. The usize is the index into waiters in the query's latch -#[cfg(parallel_compiler)] -type Waiter = (QueryJobId, usize); - -/// Visits all the non-resumable and resumable waiters of a query. -/// Only waiters in a query are visited. -/// `visit` is called for every waiter and is passed a query waiting on `query_ref` -/// and a span indicating the reason the query waited on `query_ref`. -/// If `visit` returns Some, this function returns. -/// For visits of non-resumable waiters it returns the return value of `visit`. -/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the -/// required information to resume the waiter. -/// If all `visit` calls returns None, this function also returns None. -#[cfg(parallel_compiler)] -fn visit_waiters<'tcx, F>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - mut visit: F, -) -> Option> -where - F: FnMut(Span, QueryJobId) -> Option>, -{ - // Visit the parent query which is a non-resumable waiter since it's on the same stack - if let Some(parent) = query.parent(query_map) { - if let Some(cycle) = visit(query.span(query_map), parent) { - return Some(cycle); - } - } - - // Visit the explicit waiters which use condvars and are resumable - if let Some(latch) = query.latch(query_map) { - for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { - if let Some(waiter_query) = waiter.query { - if visit(waiter.span, waiter_query).is_some() { - // Return a value which indicates that this waiter can be resumed - return Some(Some((query, i))); - } - } - } - } - - None -} - -/// Look for query cycles by doing a depth first search starting at `query`. -/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. -/// If a cycle is detected, this initial value is replaced with the span causing -/// the cycle. -#[cfg(parallel_compiler)] -fn cycle_check<'tcx>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - span: Span, - stack: &mut Vec<(Span, QueryJobId)>, - visited: &mut FxHashSet, -) -> Option> { - if !visited.insert(query) { - return if let Some(p) = stack.iter().position(|q| q.1 == query) { - // We detected a query cycle, fix up the initial span and return Some - - // Remove previous stack entries - stack.drain(0..p); - // Replace the span for the first query with the cycle cause - stack[0].0 = span; - Some(None) - } else { - None - }; - } - - // Query marked as visited is added it to the stack - stack.push((span, query)); - - // Visit all the waiters - let r = visit_waiters(query_map, query, |span, successor| { - cycle_check(query_map, successor, span, stack, visited) - }); - - // Remove the entry in our stack if we didn't find a cycle - if r.is_none() { - stack.pop(); - } - - r -} - -/// Finds out if there's a path to the compiler root (aka. code which isn't in a query) -/// from `query` without going through any of the queries in `visited`. -/// This is achieved with a depth first search. -#[cfg(parallel_compiler)] -fn connected_to_root<'tcx>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - visited: &mut FxHashSet, -) -> bool { - // We already visited this or we're deliberately ignoring it - if !visited.insert(query) { - return false; - } - - // This query is connected to the root (it has no query parent), return true - if query.parent(query_map).is_none() { - return true; - } - - visit_waiters(query_map, query, |_, successor| { - connected_to_root(query_map, successor, visited).then_some(None) - }) - .is_some() -} - -// Deterministically pick an query from a list -#[cfg(parallel_compiler)] -fn pick_query<'a, 'tcx, T, F: Fn(&T) -> (Span, QueryJobId)>( - query_map: &QueryMap<'tcx>, - tcx: TyCtxt<'tcx>, - queries: &'a [T], - f: F, -) -> &'a T { - // Deterministically pick an entry point - // FIXME: Sort this instead - let mut hcx = tcx.create_stable_hashing_context(); - queries - .iter() - .min_by_key(|v| { - let (span, query) = f(v); - let mut stable_hasher = StableHasher::new(); - query.query(query_map).hash_stable(&mut hcx, &mut stable_hasher); - // Prefer entry points which have valid spans for nicer error messages - // We add an integer to the tuple ensuring that entry points - // with valid spans are picked first - let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; - (span_cmp, stable_hasher.finish::()) - }) - .unwrap() -} - -/// Looks for query cycles starting from the last query in `jobs`. -/// If a cycle is found, all queries in the cycle is removed from `jobs` and -/// the function return true. -/// If a cycle was not found, the starting query is removed from `jobs` and -/// the function returns false. -#[cfg(parallel_compiler)] -fn remove_cycle<'tcx>( - query_map: &QueryMap<'tcx>, - jobs: &mut Vec, - wakelist: &mut Vec>>, - tcx: TyCtxt<'tcx>, -) -> bool { - let mut visited = FxHashSet::default(); - let mut stack = Vec::new(); - // Look for a cycle starting with the last query in `jobs` - if let Some(waiter) = - cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) - { - // The stack is a vector of pairs of spans and queries; reverse it so that - // the earlier entries require later entries - let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); - - // Shift the spans so that queries are matched with the span for their waitee - spans.rotate_right(1); - - // Zip them back together - let mut stack: Vec<_> = spans.into_iter().zip(queries).collect(); - - // Remove the queries in our cycle from the list of jobs to look at - for r in &stack { - jobs.remove_item(&r.1); - } - - // Find the queries in the cycle which are - // connected to queries outside the cycle - let entry_points = stack - .iter() - .filter_map(|&(span, query)| { - if query.parent(query_map).is_none() { - // This query is connected to the root (it has no query parent) - Some((span, query, None)) - } else { - let mut waiters = Vec::new(); - // Find all the direct waiters who lead to the root - visit_waiters(query_map, query, |span, waiter| { - // Mark all the other queries in the cycle as already visited - let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); - - if connected_to_root(query_map, waiter, &mut visited) { - waiters.push((span, waiter)); - } - - None - }); - if waiters.is_empty() { - None - } else { - // Deterministically pick one of the waiters to show to the user - let waiter = *pick_query(query_map, tcx, &waiters, |s| *s); - Some((span, query, Some(waiter))) - } - } - }) - .collect::)>>(); - - // Deterministically pick an entry point - let (_, entry_point, usage) = pick_query(query_map, tcx, &entry_points, |e| (e.0, e.1)); - - // Shift the stack so that our entry point is first - let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); - if let Some(pos) = entry_point_pos { - stack.rotate_left(pos); - } - - let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map))); - - // Create the cycle error - let error = CycleError { - usage, - cycle: stack - .iter() - .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) }) - .collect(), - }; - - // We unwrap `waiter` here since there must always be one - // edge which is resumeable / waited using a query latch - let (waitee_query, waiter_idx) = waiter.unwrap(); - - // Extract the waiter we want to resume - let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx); - - // Set the cycle error so it will be picked up when resumed - *waiter.cycle.lock() = Some(error); - - // Put the waiter on the list of things to resume - wakelist.push(waiter); - - true - } else { - false - } -} - -/// Creates a new thread and forwards information in thread locals to it. -/// The new thread runs the deadlock handler. -/// Must only be called when a deadlock is about to happen. -#[cfg(parallel_compiler)] -pub unsafe fn handle_deadlock() { - let registry = rayon_core::Registry::current(); - - let gcx_ptr = tls::GCX_PTR.with(|gcx_ptr| gcx_ptr as *const _); - let gcx_ptr = &*gcx_ptr; - - let rustc_span_globals = - rustc_span::GLOBALS.with(|rustc_span_globals| rustc_span_globals as *const _); - let rustc_span_globals = &*rustc_span_globals; - let syntax_globals = rustc_ast::attr::GLOBALS.with(|syntax_globals| syntax_globals as *const _); - let syntax_globals = &*syntax_globals; - thread::spawn(move || { - tls::GCX_PTR.set(gcx_ptr, || { - rustc_ast::attr::GLOBALS.set(syntax_globals, || { - rustc_span::GLOBALS - .set(rustc_span_globals, || tls::with_global(|tcx| deadlock(tcx, ®istry))) - }); - }) - }); -} - -/// Detects query cycles by using depth first search over all active query jobs. -/// If a query cycle is found it will break the cycle by finding an edge which -/// uses a query latch and then resuming that waiter. -/// There may be multiple cycles involved in a deadlock, so this searches -/// all active queries for cycles before finally resuming all the waiters at once. -#[cfg(parallel_compiler)] -fn deadlock(tcx: TyCtxt<'_>, registry: &rayon_core::Registry) { - let on_panic = OnDrop(|| { - eprintln!("deadlock handler panicked, aborting process"); - process::abort(); - }); - - let mut wakelist = Vec::new(); - let query_map = tcx.queries.try_collect_active_jobs().unwrap(); - let mut jobs: Vec = query_map.keys().cloned().collect(); - - let mut found_cycle = false; - - while jobs.len() > 0 { - if remove_cycle(&query_map, &mut jobs, &mut wakelist, tcx) { - found_cycle = true; - } - } - - // Check that a cycle was found. It is possible for a deadlock to occur without - // a query cycle if a query which can be waited on uses Rayon to do multithreading - // internally. Such a query (X) may be executing on 2 threads (A and B) and A may - // wait using Rayon on B. Rayon may then switch to executing another query (Y) - // which in turn will wait on X causing a deadlock. We have a false dependency from - // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here - // only considers the true dependency and won't detect a cycle. - assert!(found_cycle); - - // FIXME: Ensure this won't cause a deadlock before we return - for waiter in wakelist.into_iter() { - waiter.notify(registry); - } - - on_panic.disable(); -} diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs deleted file mode 100644 index b45b3b3f539ea..0000000000000 --- a/src/librustc/ty/query/mod.rs +++ /dev/null @@ -1,195 +0,0 @@ -use crate::dep_graph::{self, DepConstructor, DepNode, DepNodeParams}; -use crate::hir::exports::Export; -use crate::hir::map; -use crate::infer::canonical::{self, Canonical}; -use crate::lint::LintLevelMap; -use crate::middle::codegen_fn_attrs::CodegenFnAttrs; -use crate::middle::cstore::{CrateSource, DepKind, NativeLibraryKind}; -use crate::middle::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLibrary}; -use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; -use crate::middle::lang_items::{LangItem, LanguageItems}; -use crate::middle::lib_features::LibFeatures; -use crate::middle::privacy::AccessLevels; -use crate::middle::region; -use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLifetimes}; -use crate::middle::stability::{self, DeprecationEntry}; -use crate::mir; -use crate::mir::interpret::GlobalId; -use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue}; -use crate::mir::interpret::{LitToConstError, LitToConstInput}; -use crate::mir::mono::CodegenUnit; -use crate::traits::query::{ - CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, -}; -use crate::traits::query::{ - DropckOutlivesResult, DtorckConstraint, MethodAutoderefStepsResult, NormalizationResult, - OutlivesBound, -}; -use crate::traits::specialization_graph; -use crate::traits::Clauses; -use crate::traits::{self, Vtable}; -use crate::ty::steal::Steal; -use crate::ty::subst::SubstsRef; -use crate::ty::util::AlwaysRequiresDrop; -use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; -use crate::util::common::ErrorReported; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_data_structures::profiling::ProfileCategory::*; -use rustc_data_structures::stable_hasher::StableVec; -use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::Lrc; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; -use rustc_hir::{Crate, HirIdSet, ItemLocalId, TraitCandidate}; -use rustc_index::vec::IndexVec; -use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; -use rustc_session::CrateDisambiguator; -use rustc_target::spec::PanicStrategy; - -use rustc_ast::ast; -use rustc_attr as attr; -use rustc_span::symbol::Symbol; -use rustc_span::{Span, DUMMY_SP}; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::ops::Deref; -use std::sync::Arc; - -#[macro_use] -mod plumbing; -pub(crate) use self::plumbing::CycleError; -use self::plumbing::*; - -mod stats; -pub use self::stats::print_stats; - -mod job; -#[cfg(parallel_compiler)] -pub use self::job::handle_deadlock; -use self::job::QueryJobInfo; -pub use self::job::{QueryInfo, QueryJob, QueryJobId}; - -mod keys; -use self::keys::Key; - -mod values; -use self::values::Value; - -mod caches; -use self::caches::CacheSelector; - -mod config; -use self::config::QueryAccessors; -pub use self::config::QueryConfig; -pub(crate) use self::config::QueryDescription; - -mod on_disk_cache; -pub use self::on_disk_cache::OnDiskCache; - -mod profiling_support; -pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder}; - -// Each of these queries corresponds to a function pointer field in the -// `Providers` struct for requesting a value of that type, and a method -// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way -// which memoizes and does dep-graph tracking, wrapping around the actual -// `Providers` that the driver creates (using several `rustc_*` crates). -// -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `fatal_cycle` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. - -rustc_query_append! { [define_queries!][<'tcx>] } - -/// The red/green evaluation system will try to mark a specific DepNode in the -/// dependency graph as green by recursively trying to mark the dependencies of -/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` -/// where we don't know if it is red or green and we therefore actually have -/// to recompute its value in order to find out. Since the only piece of -/// information that we have at that point is the `DepNode` we are trying to -/// re-evaluate, we need some way to re-run a query from just that. This is what -/// `force_from_dep_node()` implements. -/// -/// In the general case, a `DepNode` consists of a `DepKind` and an opaque -/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint -/// is usually constructed by computing a stable hash of the query-key that the -/// `DepNode` corresponds to. Consequently, it is not in general possible to go -/// back from hash to query-key (since hash functions are not reversible). For -/// this reason `force_from_dep_node()` is expected to fail from time to time -/// because we just cannot find out, from the `DepNode` alone, what the -/// corresponding query-key is and therefore cannot re-run the query. -/// -/// The system deals with this case letting `try_mark_green` fail which forces -/// the root query to be re-evaluated. -/// -/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. -/// Fortunately, we can use some contextual information that will allow us to -/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we -/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a -/// valid `DefPathHash`. Since we also always build a huge table that maps every -/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have -/// everything we need to re-run the query. -/// -/// Take the `mir_validated` query as an example. Like many other queries, it -/// just has a single parameter: the `DefId` of the item it will compute the -/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` -/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` -/// is actually a `DefPathHash`, and can therefore just look up the corresponding -/// `DefId` in `tcx.def_path_hash_to_def_id`. -/// -/// When you implement a new query, it will likely have a corresponding new -/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As -/// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, -/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just -/// add it to the "We don't have enough information to reconstruct..." group in -/// the match below. -pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool { - // We must avoid ever having to call `force_from_dep_node()` for a - // `DepNode::codegen_unit`: - // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we - // would always end up having to evaluate the first caller of the - // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just - // to re-trigger calling the `codegen_unit` query with the right key. At - // that point we would already have re-done all the work we are trying to - // avoid doing in the first place. - // The solution is simple: Just explicitly call the `codegen_unit` query for - // each CGU, right after partitioning. This way `try_mark_green` will always - // hit the cache instead of having to go through `force_from_dep_node`. - // This assertion makes sure, we actually keep applying the solution above. - debug_assert!( - dep_node.kind != crate::dep_graph::DepKind::codegen_unit, - "calling force_from_dep_node() on DepKind::codegen_unit" - ); - - if !dep_node.kind.can_reconstruct_query_key() { - return false; - } - - rustc_dep_node_force!([dep_node, tcx] - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - crate::dep_graph::DepKind::CrateMetadata | - - // These are anonymous nodes. - crate::dep_graph::DepKind::TraitSelect | - - // We don't have enough information to reconstruct the query key of - // these. - crate::dep_graph::DepKind::CompileCodegenUnit => { - bug!("force_from_dep_node: encountered {:?}", dep_node) - } - ); - - false -} - -pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { - rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx) -} diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs deleted file mode 100644 index 80a4e552f02d6..0000000000000 --- a/src/librustc/ty/query/plumbing.rs +++ /dev/null @@ -1,1183 +0,0 @@ -//! The implementation of the query system itself. This defines the macros that -//! generate the actual methods on tcx which find and execute the provider, -//! manage the caches, and so forth. - -use crate::dep_graph::{DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex}; -use crate::ty::query::caches::QueryCache; -use crate::ty::query::config::QueryDescription; -use crate::ty::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId}; -use crate::ty::query::Query; -use crate::ty::tls; -use crate::ty::{self, TyCtxt}; - -#[cfg(not(parallel_compiler))] -use rustc_data_structures::cold_path; -use rustc_data_structures::fx::{FxHashMap, FxHasher}; -use rustc_data_structures::sharded::Sharded; -use rustc_data_structures::sync::{Lock, LockGuard}; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, FatalError, Handler, Level}; -use rustc_span::source_map::DUMMY_SP; -use rustc_span::Span; -use std::collections::hash_map::Entry; -use std::convert::TryFrom; -use std::fmt::Debug; -use std::hash::{Hash, Hasher}; -use std::mem; -use std::num::NonZeroU32; -use std::ptr; -#[cfg(debug_assertions)] -use std::sync::atomic::{AtomicUsize, Ordering}; - -pub(crate) struct QueryStateShard<'tcx, K, C> { - cache: C, - active: FxHashMap>, - - /// Used to generate unique ids for active jobs. - jobs: u32, -} - -impl<'tcx, K, C> QueryStateShard<'tcx, K, C> { - fn get_cache(&mut self) -> &mut C { - &mut self.cache - } -} - -impl<'tcx, K, C: Default> Default for QueryStateShard<'tcx, K, C> { - fn default() -> QueryStateShard<'tcx, K, C> { - QueryStateShard { cache: Default::default(), active: Default::default(), jobs: 0 } - } -} - -pub(crate) struct QueryState<'tcx, C: QueryCache> { - cache: C, - shards: Sharded>, - #[cfg(debug_assertions)] - pub(super) cache_hits: AtomicUsize, -} - -impl<'tcx, C: QueryCache> QueryState<'tcx, C> { - pub(super) fn get_lookup( - &'tcx self, - key: &K2, - ) -> QueryLookup<'tcx, C::Key, C::Sharded> { - // We compute the key's hash once and then use it for both the - // shard lookup and the hashmap lookup. This relies on the fact - // that both of them use `FxHasher`. - let mut hasher = FxHasher::default(); - key.hash(&mut hasher); - let key_hash = hasher.finish(); - - let shard = self.shards.get_shard_index_by_hash(key_hash); - let lock = self.shards.get_shard_by_index(shard).lock(); - QueryLookup { key_hash, shard, lock } - } -} - -/// Indicates the state of a query for a given key in a query map. -enum QueryResult<'tcx> { - /// An already executing query. The query job can be used to await for its completion. - Started(QueryJob<'tcx>), - - /// The query panicked. Queries trying to wait on this will raise a fatal error which will - /// silently panic. - Poisoned, -} - -impl<'tcx, C: QueryCache> QueryState<'tcx, C> { - pub(super) fn iter_results( - &self, - f: impl for<'a> FnOnce( - Box + 'a>, - ) -> R, - ) -> R { - self.cache.iter(&self.shards, |shard| &mut shard.cache, f) - } - pub(super) fn all_inactive(&self) -> bool { - let shards = self.shards.lock_shards(); - shards.iter().all(|shard| shard.active.is_empty()) - } - - pub(super) fn try_collect_active_jobs( - &self, - kind: DepKind, - make_query: fn(C::Key) -> Query<'tcx>, - jobs: &mut FxHashMap>, - ) -> Option<()> - where - C::Key: Clone, - { - // We use try_lock_shards here since we are called from the - // deadlock handler, and this shouldn't be locked. - let shards = self.shards.try_lock_shards()?; - let shards = shards.iter().enumerate(); - jobs.extend(shards.flat_map(|(shard_id, shard)| { - shard.active.iter().filter_map(move |(k, v)| { - if let QueryResult::Started(ref job) = *v { - let id = - QueryJobId { job: job.id, shard: u16::try_from(shard_id).unwrap(), kind }; - let info = QueryInfo { span: job.span, query: make_query(k.clone()) }; - Some((id, QueryJobInfo { info, job: job.clone() })) - } else { - None - } - }) - })); - - Some(()) - } -} - -impl<'tcx, C: QueryCache> Default for QueryState<'tcx, C> { - fn default() -> QueryState<'tcx, C> { - QueryState { - cache: C::default(), - shards: Default::default(), - #[cfg(debug_assertions)] - cache_hits: AtomicUsize::new(0), - } - } -} - -/// Values used when checking a query cache which can be reused on a cache-miss to execute the query. -pub(crate) struct QueryLookup<'tcx, K, C> { - pub(super) key_hash: u64, - shard: usize, - pub(super) lock: LockGuard<'tcx, QueryStateShard<'tcx, K, C>>, -} - -/// A type representing the responsibility to execute the job in the `job` field. -/// This will poison the relevant query if dropped. -struct JobOwner<'tcx, C> -where - C: QueryCache, - C::Key: Eq + Hash + Clone + Debug, - C::Value: Clone, -{ - state: &'tcx QueryState<'tcx, C>, - key: C::Key, - id: QueryJobId, -} - -impl<'tcx, C: QueryCache> JobOwner<'tcx, C> -where - C: QueryCache, - C::Key: Eq + Hash + Clone + Debug, - C::Value: Clone, -{ - /// Either gets a `JobOwner` corresponding the query, allowing us to - /// start executing the query, or returns with the result of the query. - /// This function assumes that `try_get_cached` is already called and returned `lookup`. - /// If the query is executing elsewhere, this will wait for it and return the result. - /// If the query panicked, this will silently panic. - /// - /// This function is inlined because that results in a noticeable speed-up - /// for some compile-time benchmarks. - #[inline(always)] - fn try_start( - tcx: TyCtxt<'tcx>, - span: Span, - key: &C::Key, - mut lookup: QueryLookup<'tcx, C::Key, C::Sharded>, - ) -> TryGetJob<'tcx, C> - where - Q: QueryDescription<'tcx, Key = C::Key, Value = C::Value, Cache = C>, - { - let lock = &mut *lookup.lock; - - let (latch, mut _query_blocked_prof_timer) = match lock.active.entry((*key).clone()) { - Entry::Occupied(mut entry) => { - match entry.get_mut() { - QueryResult::Started(job) => { - // For parallel queries, we'll block and wait until the query running - // in another thread has completed. Record how long we wait in the - // self-profiler. - let _query_blocked_prof_timer = if cfg!(parallel_compiler) { - Some(tcx.prof.query_blocked()) - } else { - None - }; - - // Create the id of the job we're waiting for - let id = QueryJobId::new(job.id, lookup.shard, Q::DEP_KIND); - - (job.latch(id), _query_blocked_prof_timer) - } - QueryResult::Poisoned => FatalError.raise(), - } - } - Entry::Vacant(entry) => { - // No job entry for this query. Return a new one to be started later. - - // Generate an id unique within this shard. - let id = lock.jobs.checked_add(1).unwrap(); - lock.jobs = id; - let id = QueryShardJobId(NonZeroU32::new(id).unwrap()); - - let global_id = QueryJobId::new(id, lookup.shard, Q::DEP_KIND); - - let job = tls::with_related_context(tcx, |icx| QueryJob::new(id, span, icx.query)); - - entry.insert(QueryResult::Started(job)); - - let owner = - JobOwner { state: Q::query_state(tcx), id: global_id, key: (*key).clone() }; - return TryGetJob::NotYetStarted(owner); - } - }; - mem::drop(lookup.lock); - - // If we are single-threaded we know that we have cycle error, - // so we just return the error. - #[cfg(not(parallel_compiler))] - return TryGetJob::Cycle(cold_path(|| { - Q::handle_cycle_error(tcx, latch.find_cycle_in_stack(tcx, span)) - })); - - // With parallel queries we might just have to wait on some other - // thread. - #[cfg(parallel_compiler)] - { - let result = latch.wait_on(tcx, span); - - if let Err(cycle) = result { - return TryGetJob::Cycle(Q::handle_cycle_error(tcx, cycle)); - } - - let cached = tcx.try_get_cached( - Q::query_state(tcx), - (*key).clone(), - |value, index| (value.clone(), index), - |_, _| panic!("value must be in cache after waiting"), - ); - - if let Some(prof_timer) = _query_blocked_prof_timer.take() { - prof_timer.finish_with_query_invocation_id(cached.1.into()); - } - - return TryGetJob::JobCompleted(cached); - } - } - - /// Completes the query by updating the query cache with the `result`, - /// signals the waiter and forgets the JobOwner, so it won't poison the query - #[inline(always)] - fn complete(self, tcx: TyCtxt<'tcx>, result: &C::Value, dep_node_index: DepNodeIndex) { - // We can move out of `self` here because we `mem::forget` it below - let key = unsafe { ptr::read(&self.key) }; - let state = self.state; - - // Forget ourself so our destructor won't poison the query - mem::forget(self); - - let job = { - let result = result.clone(); - let mut lock = state.shards.get_shard_by_value(&key).lock(); - let job = match lock.active.remove(&key).unwrap() { - QueryResult::Started(job) => job, - QueryResult::Poisoned => panic!(), - }; - state.cache.complete(tcx, &mut lock.cache, key, result, dep_node_index); - job - }; - - job.signal_complete(); - } -} - -#[inline(always)] -fn with_diagnostics(f: F) -> (R, ThinVec) -where - F: FnOnce(Option<&Lock>>) -> R, -{ - let diagnostics = Lock::new(ThinVec::new()); - let result = f(Some(&diagnostics)); - (result, diagnostics.into_inner()) -} - -impl<'tcx, C: QueryCache> Drop for JobOwner<'tcx, C> -where - C::Key: Eq + Hash + Clone + Debug, - C::Value: Clone, -{ - #[inline(never)] - #[cold] - fn drop(&mut self) { - // Poison the query so jobs waiting on it panic. - let state = self.state; - let shard = state.shards.get_shard_by_value(&self.key); - let job = { - let mut shard = shard.lock(); - let job = match shard.active.remove(&self.key).unwrap() { - QueryResult::Started(job) => job, - QueryResult::Poisoned => panic!(), - }; - shard.active.insert(self.key.clone(), QueryResult::Poisoned); - job - }; - // Also signal the completion of the job, so waiters - // will continue execution. - job.signal_complete(); - } -} - -#[derive(Clone)] -pub(crate) struct CycleError<'tcx> { - /// The query and related span that uses the cycle. - pub(super) usage: Option<(Span, Query<'tcx>)>, - pub(super) cycle: Vec>, -} - -/// The result of `try_start`. -enum TryGetJob<'tcx, C: QueryCache> -where - C::Key: Eq + Hash + Clone + Debug, - C::Value: Clone, -{ - /// The query is not yet started. Contains a guard to the cache eventually used to start it. - NotYetStarted(JobOwner<'tcx, C>), - - /// The query was already completed. - /// Returns the result of the query and its dep-node index - /// if it succeeded or a cycle error if it failed. - #[cfg(parallel_compiler)] - JobCompleted((C::Value, DepNodeIndex)), - - /// Trying to execute the query resulted in a cycle. - Cycle(C::Value), -} - -impl<'tcx> TyCtxt<'tcx> { - /// Executes a job by changing the `ImplicitCtxt` to point to the - /// new query job while it executes. It returns the diagnostics - /// captured during execution and the actual result. - #[inline(always)] - fn start_query( - self, - token: QueryJobId, - diagnostics: Option<&Lock>>, - compute: F, - ) -> R - where - F: FnOnce(TyCtxt<'tcx>) -> R, - { - // The `TyCtxt` stored in TLS has the same global interner lifetime - // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes - // when accessing the `ImplicitCtxt`. - tls::with_related_context(self, move |current_icx| { - // Update the `ImplicitCtxt` to point to our new query job. - let new_icx = tls::ImplicitCtxt { - tcx: self, - query: Some(token), - diagnostics, - layout_depth: current_icx.layout_depth, - task_deps: current_icx.task_deps, - }; - - // Use the `ImplicitCtxt` while we execute the query. - tls::enter_context(&new_icx, |_| compute(self)) - }) - } - - #[inline(never)] - #[cold] - pub(super) fn report_cycle( - self, - CycleError { usage, cycle: stack }: CycleError<'tcx>, - ) -> DiagnosticBuilder<'tcx> { - assert!(!stack.is_empty()); - - let fix_span = |span: Span, query: &Query<'tcx>| { - self.sess.source_map().def_span(query.default_span(self, span)) - }; - - // Disable naming impls with types in this path, since that - // sometimes cycles itself, leading to extra cycle errors. - // (And cycle errors around impls tend to occur during the - // collect/coherence phases anyhow.) - ty::print::with_forced_impl_filename_line(|| { - let span = fix_span(stack[1 % stack.len()].span, &stack[0].query); - let mut err = struct_span_err!( - self.sess, - span, - E0391, - "cycle detected when {}", - stack[0].query.describe(self) - ); - - for i in 1..stack.len() { - let query = &stack[i].query; - let span = fix_span(stack[(i + 1) % stack.len()].span, query); - err.span_note(span, &format!("...which requires {}...", query.describe(self))); - } - - err.note(&format!( - "...which again requires {}, completing the cycle", - stack[0].query.describe(self) - )); - - if let Some((span, query)) = usage { - err.span_note( - fix_span(span, &query), - &format!("cycle used when {}", query.describe(self)), - ); - } - - err - }) - } - - pub fn try_print_query_stack(handler: &Handler) { - eprintln!("query stack during panic:"); - - // Be careful reyling on global state here: this code is called from - // a panic hook, which means that the global `Handler` may be in a weird - // state if it was responsible for triggering the panic. - tls::with_context_opt(|icx| { - if let Some(icx) = icx { - let query_map = icx.tcx.queries.try_collect_active_jobs(); - - let mut current_query = icx.query; - let mut i = 0; - - while let Some(query) = current_query { - let query_info = - if let Some(info) = query_map.as_ref().and_then(|map| map.get(&query)) { - info - } else { - break; - }; - let mut diag = Diagnostic::new( - Level::FailureNote, - &format!( - "#{} [{}] {}", - i, - query_info.info.query.name(), - query_info.info.query.describe(icx.tcx) - ), - ); - diag.span = icx.tcx.sess.source_map().def_span(query_info.info.span).into(); - handler.force_print_diagnostic(diag); - - current_query = query_info.job.parent; - i += 1; - } - } - }); - - eprintln!("end of query stack"); - } - - /// Checks if the query is already computed and in the cache. - /// It returns the shard index and a lock guard to the shard, - /// which will be used if the query is not in the cache and we need - /// to compute it. - #[inline(always)] - fn try_get_cached( - self, - state: &'tcx QueryState<'tcx, C>, - key: C::Key, - // `on_hit` can be called while holding a lock to the query cache - on_hit: OnHit, - on_miss: OnMiss, - ) -> R - where - C: QueryCache, - OnHit: FnOnce(&C::Value, DepNodeIndex) -> R, - OnMiss: FnOnce(C::Key, QueryLookup<'tcx, C::Key, C::Sharded>) -> R, - { - state.cache.lookup( - state, - QueryStateShard::::get_cache, - key, - |value, index| { - if unlikely!(self.prof.enabled()) { - self.prof.query_cache_hit(index.into()); - } - #[cfg(debug_assertions)] - { - state.cache_hits.fetch_add(1, Ordering::Relaxed); - } - on_hit(value, index) - }, - on_miss, - ) - } - - #[inline(never)] - pub(super) fn get_query + 'tcx>( - self, - span: Span, - key: Q::Key, - ) -> Q::Value { - debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span); - - self.try_get_cached( - Q::query_state(self), - key, - |value, index| { - self.dep_graph.read_index(index); - value.clone() - }, - |key, lookup| self.try_execute_query::(span, key, lookup), - ) - } - - #[inline(always)] - fn try_execute_query + 'tcx>( - self, - span: Span, - key: Q::Key, - lookup: QueryLookup<'tcx, Q::Key, ::Sharded>, - ) -> Q::Value { - let job = match JobOwner::try_start::(self, span, &key, lookup) { - TryGetJob::NotYetStarted(job) => job, - TryGetJob::Cycle(result) => return result, - #[cfg(parallel_compiler)] - TryGetJob::JobCompleted((v, index)) => { - self.dep_graph.read_index(index); - return v; - } - }; - - // Fast path for when incr. comp. is off. `to_dep_node` is - // expensive for some `DepKind`s. - if !self.dep_graph.is_fully_enabled() { - let null_dep_node = DepNode::new_no_params(crate::dep_graph::DepKind::Null); - return self.force_query_with_job::(key, job, null_dep_node).0; - } - - if Q::ANON { - let prof_timer = self.prof.query_provider(); - - let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| { - self.start_query(job.id, diagnostics, |tcx| { - tcx.dep_graph.with_anon_task(Q::DEP_KIND, || Q::compute(tcx, key)) - }) - }); - - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - self.dep_graph.read_index(dep_node_index); - - if unlikely!(!diagnostics.is_empty()) { - self.queries - .on_disk_cache - .store_diagnostics_for_anon_node(dep_node_index, diagnostics); - } - - job.complete(self, &result, dep_node_index); - - return result; - } - - let dep_node = Q::to_dep_node(self, &key); - - if !Q::EVAL_ALWAYS { - // The diagnostics for this query will be - // promoted to the current session during - // `try_mark_green()`, so we can ignore them here. - let loaded = self.start_query(job.id, None, |tcx| { - let marked = tcx.dep_graph.try_mark_green_and_read(tcx, &dep_node); - marked.map(|(prev_dep_node_index, dep_node_index)| { - ( - tcx.load_from_disk_and_cache_in_memory::( - key.clone(), - prev_dep_node_index, - dep_node_index, - &dep_node, - ), - dep_node_index, - ) - }) - }); - if let Some((result, dep_node_index)) = loaded { - job.complete(self, &result, dep_node_index); - return result; - } - } - - let (result, dep_node_index) = self.force_query_with_job::(key, job, dep_node); - self.dep_graph.read_index(dep_node_index); - result - } - - fn load_from_disk_and_cache_in_memory>( - self, - key: Q::Key, - prev_dep_node_index: SerializedDepNodeIndex, - dep_node_index: DepNodeIndex, - dep_node: &DepNode, - ) -> Q::Value { - // Note this function can be called concurrently from the same query - // We must ensure that this is handled correctly. - - debug_assert!(self.dep_graph.is_green(dep_node)); - - // First we try to load the result from the on-disk cache. - let result = if Q::cache_on_disk(self, key.clone(), None) - && self.sess.opts.debugging_opts.incremental_queries - { - let prof_timer = self.prof.incr_cache_loading(); - let result = Q::try_load_from_disk(self, prev_dep_node_index); - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - // We always expect to find a cached result for things that - // can be forced from `DepNode`. - debug_assert!( - !dep_node.kind.can_reconstruct_query_key() || result.is_some(), - "missing on-disk cache entry for {:?}", - dep_node - ); - result - } else { - // Some things are never cached on disk. - None - }; - - let result = if let Some(result) = result { - result - } else { - // We could not load a result from the on-disk cache, so - // recompute. - let prof_timer = self.prof.query_provider(); - - // The dep-graph for this computation is already in-place. - let result = self.dep_graph.with_ignore(|| Q::compute(self, key)); - - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - result - }; - - // If `-Zincremental-verify-ich` is specified, re-hash results from - // the cache and make sure that they have the expected fingerprint. - if unlikely!(self.sess.opts.debugging_opts.incremental_verify_ich) { - self.incremental_verify_ich::(&result, dep_node, dep_node_index); - } - - result - } - - #[inline(never)] - #[cold] - fn incremental_verify_ich>( - self, - result: &Q::Value, - dep_node: &DepNode, - dep_node_index: DepNodeIndex, - ) { - use crate::ich::Fingerprint; - - assert!( - Some(self.dep_graph.fingerprint_of(dep_node_index)) - == self.dep_graph.prev_fingerprint_of(dep_node), - "fingerprint for green query instance not loaded from cache: {:?}", - dep_node, - ); - - debug!("BEGIN verify_ich({:?})", dep_node); - let mut hcx = self.create_stable_hashing_context(); - - let new_hash = Q::hash_result(&mut hcx, result).unwrap_or(Fingerprint::ZERO); - debug!("END verify_ich({:?})", dep_node); - - let old_hash = self.dep_graph.fingerprint_of(dep_node_index); - - assert!(new_hash == old_hash, "found unstable fingerprints for {:?}", dep_node,); - } - - #[inline(always)] - fn force_query_with_job + 'tcx>( - self, - key: Q::Key, - job: JobOwner<'tcx, Q::Cache>, - dep_node: DepNode, - ) -> (Q::Value, DepNodeIndex) { - // If the following assertion triggers, it can have two reasons: - // 1. Something is wrong with DepNode creation, either here or - // in `DepGraph::try_mark_green()`. - // 2. Two distinct query keys get mapped to the same `DepNode` - // (see for example #48923). - assert!( - !self.dep_graph.dep_node_exists(&dep_node), - "forcing query with already existing `DepNode`\n\ - - query-key: {:?}\n\ - - dep-node: {:?}", - key, - dep_node - ); - - let prof_timer = self.prof.query_provider(); - - let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| { - self.start_query(job.id, diagnostics, |tcx| { - if Q::EVAL_ALWAYS { - tcx.dep_graph.with_eval_always_task( - dep_node, - tcx, - key, - Q::compute, - Q::hash_result, - ) - } else { - tcx.dep_graph.with_task(dep_node, tcx, key, Q::compute, Q::hash_result) - } - }) - }); - - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - if unlikely!(!diagnostics.is_empty()) { - if dep_node.kind != crate::dep_graph::DepKind::Null { - self.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics); - } - } - - job.complete(self, &result, dep_node_index); - - (result, dep_node_index) - } - - /// Ensure that either this query has all green inputs or been executed. - /// Executing `query::ensure(D)` is considered a read of the dep-node `D`. - /// - /// This function is particularly useful when executing passes for their - /// side-effects -- e.g., in order to report errors for erroneous programs. - /// - /// Note: The optimization is only available during incr. comp. - pub(super) fn ensure_query + 'tcx>(self, key: Q::Key) { - if Q::EVAL_ALWAYS { - let _ = self.get_query::(DUMMY_SP, key); - return; - } - - // Ensuring an anonymous query makes no sense - assert!(!Q::ANON); - - let dep_node = Q::to_dep_node(self, &key); - - match self.dep_graph.try_mark_green_and_read(self, &dep_node) { - None => { - // A None return from `try_mark_green_and_read` means that this is either - // a new dep node or that the dep node has already been marked red. - // Either way, we can't call `dep_graph.read()` as we don't have the - // DepNodeIndex. We must invoke the query itself. The performance cost - // this introduces should be negligible as we'll immediately hit the - // in-memory cache, or another query down the line will. - let _ = self.get_query::(DUMMY_SP, key); - } - Some((_, dep_node_index)) => { - self.prof.query_cache_hit(dep_node_index.into()); - } - } - } - - #[allow(dead_code)] - pub(super) fn force_query + 'tcx>( - self, - key: Q::Key, - span: Span, - dep_node: DepNode, - ) { - // We may be concurrently trying both execute and force a query. - // Ensure that only one of them runs the query. - - self.try_get_cached( - Q::query_state(self), - key, - |_, _| { - // Cache hit, do nothing - }, - |key, lookup| { - let job = match JobOwner::try_start::(self, span, &key, lookup) { - TryGetJob::NotYetStarted(job) => job, - TryGetJob::Cycle(_) => return, - #[cfg(parallel_compiler)] - TryGetJob::JobCompleted(_) => return, - }; - self.force_query_with_job::(key, job, dep_node); - }, - ); - } -} - -macro_rules! handle_cycle_error { - ([][$tcx: expr, $error:expr]) => {{ - $tcx.report_cycle($error).emit(); - Value::from_cycle_error($tcx) - }}; - ([fatal_cycle $($rest:tt)*][$tcx:expr, $error:expr]) => {{ - $tcx.report_cycle($error).emit(); - $tcx.sess.abort_if_errors(); - unreachable!() - }}; - ([cycle_delay_bug $($rest:tt)*][$tcx:expr, $error:expr]) => {{ - $tcx.report_cycle($error).delay_as_bug(); - Value::from_cycle_error($tcx) - }}; - ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => { - handle_cycle_error!([$($($modifiers)*)*][$($args)*]) - }; -} - -macro_rules! is_anon { - ([]) => {{ - false - }}; - ([anon $($rest:tt)*]) => {{ - true - }}; - ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*]) => { - is_anon!([$($($modifiers)*)*]) - }; -} - -macro_rules! is_eval_always { - ([]) => {{ - false - }}; - ([eval_always $($rest:tt)*]) => {{ - true - }}; - ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*]) => { - is_eval_always!([$($($modifiers)*)*]) - }; -} - -macro_rules! query_storage { - ([][$K:ty, $V:ty]) => { - <<$K as Key>::CacheSelector as CacheSelector<$K, $V>>::Cache - }; - ([storage($ty:ty) $($rest:tt)*][$K:ty, $V:ty]) => { - $ty - }; - ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => { - query_storage!([$($($modifiers)*)*][$($args)*]) - }; -} - -macro_rules! hash_result { - ([][$hcx:expr, $result:expr]) => {{ - dep_graph::hash_result($hcx, &$result) - }}; - ([no_hash $($rest:tt)*][$hcx:expr, $result:expr]) => {{ - None - }}; - ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => { - hash_result!([$($($modifiers)*)*][$($args)*]) - }; -} - -macro_rules! define_queries { - (<$tcx:tt> $($category:tt { - $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)* - },)*) => { - define_queries_inner! { <$tcx> - $($( $(#[$attr])* category<$category> [$($modifiers)*] fn $name: $node($K) -> $V,)*)* - } - } -} - -macro_rules! define_queries_inner { - (<$tcx:tt> - $($(#[$attr:meta])* category<$category:tt> - [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*) => { - - use std::mem; - use crate::{ - rustc_data_structures::stable_hasher::HashStable, - rustc_data_structures::stable_hasher::StableHasher, - ich::StableHashingContext - }; - use rustc_data_structures::profiling::ProfileCategory; - - define_queries_struct! { - tcx: $tcx, - input: ($(([$($modifiers)*] [$($attr)*] [$name]))*) - } - - #[allow(nonstandard_style)] - #[derive(Clone, Debug)] - pub enum Query<$tcx> { - $($(#[$attr])* $name($K)),* - } - - impl<$tcx> Query<$tcx> { - pub fn name(&self) -> &'static str { - match *self { - $(Query::$name(_) => stringify!($name),)* - } - } - - pub fn describe(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { - let (r, name) = match *self { - $(Query::$name(key) => { - (queries::$name::describe(tcx, key), stringify!($name)) - })* - }; - if tcx.sess.verbose() { - format!("{} [{}]", r, name).into() - } else { - r - } - } - - // FIXME(eddyb) Get more valid `Span`s on queries. - pub fn default_span(&self, tcx: TyCtxt<$tcx>, span: Span) -> Span { - if !span.is_dummy() { - return span; - } - // The `def_span` query is used to calculate `default_span`, - // so exit to avoid infinite recursion. - if let Query::def_span(..) = *self { - return span - } - match *self { - $(Query::$name(key) => key.default_span(tcx),)* - } - } - } - - impl<'a, $tcx> HashStable> for Query<$tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - mem::discriminant(self).hash_stable(hcx, hasher); - match *self { - $(Query::$name(key) => key.hash_stable(hcx, hasher),)* - } - } - } - - pub mod queries { - use std::marker::PhantomData; - - $(#[allow(nonstandard_style)] - pub struct $name<$tcx> { - data: PhantomData<&$tcx ()> - })* - } - - $(impl<$tcx> QueryConfig<$tcx> for queries::$name<$tcx> { - type Key = $K; - type Value = $V; - const NAME: &'static str = stringify!($name); - const CATEGORY: ProfileCategory = $category; - } - - impl<$tcx> QueryAccessors<$tcx> for queries::$name<$tcx> { - const ANON: bool = is_anon!([$($modifiers)*]); - const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]); - const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$node; - - type Cache = query_storage!([$($modifiers)*][$K, $V]); - - #[inline(always)] - fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState<$tcx, Self::Cache> { - &tcx.queries.$name - } - - #[allow(unused)] - #[inline(always)] - fn to_dep_node(tcx: TyCtxt<$tcx>, key: &Self::Key) -> DepNode { - DepConstructor::$node(tcx, *key) - } - - #[inline] - fn compute(tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value { - let provider = tcx.queries.providers.get(key.query_crate()) - // HACK(eddyb) it's possible crates may be loaded after - // the query engine is created, and because crate loading - // is not yet integrated with the query engine, such crates - // would be missing appropriate entries in `providers`. - .unwrap_or(&tcx.queries.fallback_extern_providers) - .$name; - provider(tcx, key) - } - - fn hash_result( - _hcx: &mut StableHashingContext<'_>, - _result: &Self::Value - ) -> Option { - hash_result!([$($modifiers)*][_hcx, _result]) - } - - fn handle_cycle_error( - tcx: TyCtxt<'tcx>, - error: CycleError<'tcx> - ) -> Self::Value { - handle_cycle_error!([$($modifiers)*][tcx, error]) - } - })* - - #[derive(Copy, Clone)] - pub struct TyCtxtEnsure<'tcx> { - pub tcx: TyCtxt<'tcx>, - } - - impl TyCtxtEnsure<$tcx> { - $($(#[$attr])* - #[inline(always)] - pub fn $name(self, key: $K) { - self.tcx.ensure_query::>(key) - })* - } - - #[derive(Copy, Clone)] - pub struct TyCtxtAt<'tcx> { - pub tcx: TyCtxt<'tcx>, - pub span: Span, - } - - impl Deref for TyCtxtAt<'tcx> { - type Target = TyCtxt<'tcx>; - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.tcx - } - } - - impl TyCtxt<$tcx> { - /// Returns a transparent wrapper for `TyCtxt`, which ensures queries - /// are executed instead of just returning their results. - #[inline(always)] - pub fn ensure(self) -> TyCtxtEnsure<$tcx> { - TyCtxtEnsure { - tcx: self, - } - } - - /// Returns a transparent wrapper for `TyCtxt` which uses - /// `span` as the location of queries performed through it. - #[inline(always)] - pub fn at(self, span: Span) -> TyCtxtAt<$tcx> { - TyCtxtAt { - tcx: self, - span - } - } - - $($(#[$attr])* - #[inline(always)] - pub fn $name(self, key: $K) -> $V { - self.at(DUMMY_SP).$name(key) - })* - - /// All self-profiling events generated by the query engine use - /// virtual `StringId`s for their `event_id`. This method makes all - /// those virtual `StringId`s point to actual strings. - /// - /// If we are recording only summary data, the ids will point to - /// just the query names. If we are recording query keys too, we - /// allocate the corresponding strings here. - pub fn alloc_self_profile_query_strings(self) { - use crate::ty::query::profiling_support::{ - alloc_self_profile_query_strings_for_query_cache, - QueryKeyStringCache, - }; - - if !self.prof.enabled() { - return; - } - - let mut string_cache = QueryKeyStringCache::new(); - - $({ - alloc_self_profile_query_strings_for_query_cache( - self, - stringify!($name), - &self.queries.$name, - &mut string_cache, - ); - })* - } - } - - impl TyCtxtAt<$tcx> { - $($(#[$attr])* - #[inline(always)] - pub fn $name(self, key: $K) -> $V { - self.tcx.get_query::>(self.span, key) - })* - } - - define_provider_struct! { - tcx: $tcx, - input: ($(([$($modifiers)*] [$name] [$K] [$V]))*) - } - - impl<$tcx> Copy for Providers<$tcx> {} - impl<$tcx> Clone for Providers<$tcx> { - fn clone(&self) -> Self { *self } - } - } -} - -macro_rules! define_queries_struct { - (tcx: $tcx:tt, - input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { - pub struct Queries<$tcx> { - /// This provides access to the incrimental comilation on-disk cache for query results. - /// Do not access this directly. It is only meant to be used by - /// `DepGraph::try_mark_green()` and the query infrastructure. - pub(crate) on_disk_cache: OnDiskCache<'tcx>, - - providers: IndexVec>, - fallback_extern_providers: Box>, - - $($(#[$attr])* $name: QueryState< - $tcx, - as QueryAccessors<'tcx>>::Cache, - >,)* - } - - impl<$tcx> Queries<$tcx> { - pub(crate) fn new( - providers: IndexVec>, - fallback_extern_providers: Providers<$tcx>, - on_disk_cache: OnDiskCache<'tcx>, - ) -> Self { - Queries { - providers, - fallback_extern_providers: Box::new(fallback_extern_providers), - on_disk_cache, - $($name: Default::default()),* - } - } - - pub(crate) fn try_collect_active_jobs( - &self - ) -> Option>> { - let mut jobs = FxHashMap::default(); - - $( - self.$name.try_collect_active_jobs( - as QueryAccessors<'tcx>>::DEP_KIND, - Query::$name, - &mut jobs, - )?; - )* - - Some(jobs) - } - } - }; -} - -macro_rules! define_provider_struct { - (tcx: $tcx:tt, - input: ($(([$($modifiers:tt)*] [$name:ident] [$K:ty] [$R:ty]))*)) => { - pub struct Providers<$tcx> { - $(pub $name: fn(TyCtxt<$tcx>, $K) -> $R,)* - } - - impl<$tcx> Default for Providers<$tcx> { - fn default() -> Self { - $(fn $name<$tcx>(_: TyCtxt<$tcx>, key: $K) -> $R { - bug!("`tcx.{}({:?})` unsupported by its crate", - stringify!($name), key); - })* - Providers { $($name),* } - } - } - }; -} diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs deleted file mode 100644 index 81be5b11143af..0000000000000 --- a/src/librustc/ty/structural_impls.rs +++ /dev/null @@ -1,1082 +0,0 @@ -//! This module contains implements of the `Lift` and `TypeFoldable` -//! traits for various types in the Rust compiler. Most are written by -//! hand, though we've recently added some macros and proc-macros to help with the tedium. - -use crate::mir::interpret; -use crate::mir::ProjectionKind; -use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::{self, InferConst, Lift, Ty, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def::Namespace; -use rustc_hir::def_id::CRATE_DEF_INDEX; -use rustc_index::vec::{Idx, IndexVec}; - -use smallvec::SmallVec; -use std::fmt; -use std::rc::Rc; -use std::sync::Arc; - -impl fmt::Debug for ty::TraitDef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])?; - Ok(()) - }) - } -} - -impl fmt::Debug for ty::AdtDef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])?; - Ok(()) - }) - } -} - -impl fmt::Debug for ty::UpvarId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let name = ty::tls::with(|tcx| tcx.hir().name(self.var_path.hir_id)); - write!(f, "UpvarId({:?};`{}`;{:?})", self.var_path.hir_id, name, self.closure_expr_id) - } -} - -impl fmt::Debug for ty::UpvarBorrow<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region) - } -} - -impl fmt::Debug for ty::ExistentialTraitRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl fmt::Debug for ty::adjustment::Adjustment<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} -> {}", self.kind, self.target) - } -} - -impl fmt::Debug for ty::BoundRegion { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::BrAnon(n) => write!(f, "BrAnon({:?})", n), - ty::BrNamed(did, name) => { - if did.index == CRATE_DEF_INDEX { - write!(f, "BrNamed({})", name) - } else { - write!(f, "BrNamed({:?}, {})", did, name) - } - } - ty::BrEnv => write!(f, "BrEnv"), - } - } -} - -impl fmt::Debug for ty::RegionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::ReEarlyBound(ref data) => write!(f, "ReEarlyBound({}, {})", data.index, data.name), - - ty::ReLateBound(binder_id, ref bound_region) => { - write!(f, "ReLateBound({:?}, {:?})", binder_id, bound_region) - } - - ty::ReFree(ref fr) => fr.fmt(f), - - ty::ReScope(id) => write!(f, "ReScope({:?})", id), - - ty::ReStatic => write!(f, "ReStatic"), - - ty::ReVar(ref vid) => vid.fmt(f), - - ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder), - - ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui), - - ty::ReErased => write!(f, "ReErased"), - } - } -} - -impl fmt::Debug for ty::FreeRegion { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ReFree({:?}, {:?})", self.scope, self.bound_region) - } -} - -impl fmt::Debug for ty::Variance { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match *self { - ty::Covariant => "+", - ty::Contravariant => "-", - ty::Invariant => "o", - ty::Bivariant => "*", - }) - } -} - -impl fmt::Debug for ty::FnSig<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output()) - } -} - -impl fmt::Debug for ty::TyVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}t", self.index) - } -} - -impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}c", self.index) - } -} - -impl fmt::Debug for ty::IntVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}i", self.index) - } -} - -impl fmt::Debug for ty::FloatVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}f", self.index) - } -} - -impl fmt::Debug for ty::RegionVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "'_#{}r", self.index()) - } -} - -impl fmt::Debug for ty::InferTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::TyVar(ref v) => v.fmt(f), - ty::IntVar(ref v) => v.fmt(f), - ty::FloatVar(ref v) => v.fmt(f), - ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), - ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), - ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), - } - } -} - -impl fmt::Debug for ty::IntVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::IntType(ref v) => v.fmt(f), - ty::UintType(ref v) => v.fmt(f), - } - } -} - -impl fmt::Debug for ty::FloatVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::Debug for ty::TraitRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl fmt::Debug for Ty<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl fmt::Debug for ty::ParamTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}/#{}", self.name, self.index) - } -} - -impl fmt::Debug for ty::ParamConst { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}/#{}", self.name, self.index) - } -} - -impl fmt::Debug for ty::TraitPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TraitPredicate({:?})", self.trait_ref) - } -} - -impl fmt::Debug for ty::ProjectionPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.ty) - } -} - -impl fmt::Debug for ty::Predicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::Predicate::Trait(ref a, constness) => { - if let hir::Constness::Const = constness { - write!(f, "const ")?; - } - a.fmt(f) - } - ty::Predicate::Subtype(ref pair) => pair.fmt(f), - ty::Predicate::RegionOutlives(ref pair) => pair.fmt(f), - ty::Predicate::TypeOutlives(ref pair) => pair.fmt(f), - ty::Predicate::Projection(ref pair) => pair.fmt(f), - ty::Predicate::WellFormed(ty) => write!(f, "WellFormed({:?})", ty), - ty::Predicate::ObjectSafe(trait_def_id) => write!(f, "ObjectSafe({:?})", trait_def_id), - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) - } - ty::Predicate::ConstEvaluatable(def_id, substs) => { - write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Atomic structs -// -// For things that don't carry any arena-allocated data (and are -// copy...), just add them to this list. - -CloneTypeFoldableAndLiftImpls! { - (), - bool, - usize, - crate::ty::layout::VariantIdx, - u64, - String, - crate::middle::region::Scope, - ::rustc_ast::ast::FloatTy, - ::rustc_ast::ast::NodeId, - ::rustc_span::symbol::Symbol, - ::rustc_hir::def::Res, - ::rustc_hir::def_id::DefId, - ::rustc_hir::InlineAsmInner, - ::rustc_hir::MatchSource, - ::rustc_hir::Mutability, - ::rustc_hir::Unsafety, - ::rustc_target::spec::abi::Abi, - crate::mir::Local, - crate::mir::Promoted, - crate::traits::Reveal, - crate::ty::adjustment::AutoBorrowMutability, - crate::ty::AdtKind, - // Including `BoundRegion` is a *bit* dubious, but direct - // references to bound region appear in `ty::Error`, and aren't - // really meant to be folded. In general, we can only fold a fully - // general `Region`. - crate::ty::BoundRegion, - crate::ty::Placeholder, - crate::ty::ClosureKind, - crate::ty::FreeRegion, - crate::ty::InferTy, - crate::ty::IntVarValue, - crate::ty::ParamConst, - crate::ty::ParamTy, - crate::ty::adjustment::PointerCast, - crate::ty::RegionVid, - crate::ty::UniverseIndex, - crate::ty::Variance, - ::rustc_span::Span, -} - -/////////////////////////////////////////////////////////////////////////// -// Lift implementations - -// FIXME(eddyb) replace all the uses of `Option::map` with `?`. -impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) { - type Lifted = (A::Lifted, B::Lifted); - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.0).and_then(|a| tcx.lift(&self.1).map(|b| (a, b))) - } -} - -impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) { - type Lifted = (A::Lifted, B::Lifted, C::Lifted); - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.0) - .and_then(|a| tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { - type Lifted = Option; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - Some(ref x) => tcx.lift(x).map(Some), - None => Some(None), - } - } -} - -impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result { - type Lifted = Result; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - Ok(ref x) => tcx.lift(x).map(Ok), - Err(ref e) => tcx.lift(e).map(Err), - } - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { - type Lifted = Box; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Box::new) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { - type Lifted = Rc; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Rc::new) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { - type Lifted = Arc; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Arc::new) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { - type Lifted = Vec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - // type annotation needed to inform `projection_must_outlive` - let mut result: Vec<>::Lifted> = Vec::with_capacity(self.len()); - for x in self { - if let Some(value) = tcx.lift(x) { - result.push(value); - } else { - return None; - } - } - Some(result) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { - type Lifted = Vec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self[..]) - } -} - -impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { - type Lifted = IndexVec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - self.iter().map(|e| tcx.lift(e)).collect() - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> { - type Lifted = ty::TraitRef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::TraitRef { def_id: self.def_id, substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> { - type Lifted = ty::ExistentialTraitRef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ExistentialTraitRef { def_id: self.def_id, substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { - type Lifted = ty::ExistentialPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match self { - ty::ExistentialPredicate::Trait(x) => tcx.lift(x).map(ty::ExistentialPredicate::Trait), - ty::ExistentialPredicate::Projection(x) => { - tcx.lift(x).map(ty::ExistentialPredicate::Projection) - } - ty::ExistentialPredicate::AutoTrait(def_id) => { - Some(ty::ExistentialPredicate::AutoTrait(*def_id)) - } - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { - type Lifted = ty::TraitPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitPredicate { trait_ref }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { - type Lifted = ty::SubtypePredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&(self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { - a_is_expected: self.a_is_expected, - a, - b, - }) - } -} - -impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate { - type Lifted = ty::OutlivesPredicate; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&(self.0, self.1)).map(|(a, b)| ty::OutlivesPredicate(a, b)) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionTy<'a> { - type Lifted = ty::ProjectionTy<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&self.substs) - .map(|substs| ty::ProjectionTy { item_def_id: self.item_def_id, substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionPredicate<'a> { - type Lifted = ty::ProjectionPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&(self.projection_ty, self.ty)) - .map(|(projection_ty, ty)| ty::ProjectionPredicate { projection_ty, ty }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { - type Lifted = ty::ExistentialProjection<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ExistentialProjection { - substs, - ty: tcx.lift(&self.ty).expect("type must lift when substs do"), - item_def_id: self.item_def_id, - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> { - type Lifted = ty::Predicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::Predicate::Trait(ref binder, constness) => { - tcx.lift(binder).map(|binder| ty::Predicate::Trait(binder, constness)) - } - ty::Predicate::Subtype(ref binder) => tcx.lift(binder).map(ty::Predicate::Subtype), - ty::Predicate::RegionOutlives(ref binder) => { - tcx.lift(binder).map(ty::Predicate::RegionOutlives) - } - ty::Predicate::TypeOutlives(ref binder) => { - tcx.lift(binder).map(ty::Predicate::TypeOutlives) - } - ty::Predicate::Projection(ref binder) => { - tcx.lift(binder).map(ty::Predicate::Projection) - } - ty::Predicate::WellFormed(ty) => tcx.lift(&ty).map(ty::Predicate::WellFormed), - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - tcx.lift(&closure_substs).map(|closure_substs| { - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) - }) - } - ty::Predicate::ObjectSafe(trait_def_id) => { - Some(ty::Predicate::ObjectSafe(trait_def_id)) - } - ty::Predicate::ConstEvaluatable(def_id, substs) => { - tcx.lift(&substs).map(|substs| ty::Predicate::ConstEvaluatable(def_id, substs)) - } - } - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder { - type Lifted = ty::Binder; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(self.skip_binder()).map(ty::Binder::bind) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { - type Lifted = ty::ParamEnv<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.caller_bounds).map(|caller_bounds| ty::ParamEnv { - reveal: self.reveal, - caller_bounds, - def_id: self.def_id, - }) - } -} - -impl<'a, 'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::ParamEnvAnd<'a, T> { - type Lifted = ty::ParamEnvAnd<'tcx, T::Lifted>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.param_env).and_then(|param_env| { - tcx.lift(&self.value).map(|value| ty::ParamEnvAnd { param_env, value }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ClosureSubsts<'a> { - type Lifted = ty::ClosureSubsts<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ClosureSubsts { substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorSubsts<'a> { - type Lifted = ty::GeneratorSubsts<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::GeneratorSubsts { substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> { - type Lifted = ty::adjustment::Adjustment<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.kind).and_then(|kind| { - tcx.lift(&self.target).map(|target| ty::adjustment::Adjustment { kind, target }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { - type Lifted = ty::adjustment::Adjust<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::adjustment::Adjust::NeverToAny => Some(ty::adjustment::Adjust::NeverToAny), - ty::adjustment::Adjust::Pointer(ptr) => Some(ty::adjustment::Adjust::Pointer(ptr)), - ty::adjustment::Adjust::Deref(ref overloaded) => { - tcx.lift(overloaded).map(ty::adjustment::Adjust::Deref) - } - ty::adjustment::Adjust::Borrow(ref autoref) => { - tcx.lift(autoref).map(ty::adjustment::Adjust::Borrow) - } - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { - type Lifted = ty::adjustment::OverloadedDeref<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.region) - .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> { - type Lifted = ty::adjustment::AutoBorrow<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::adjustment::AutoBorrow::Ref(r, m) => { - tcx.lift(&r).map(|r| ty::adjustment::AutoBorrow::Ref(r, m)) - } - ty::adjustment::AutoBorrow::RawPtr(m) => Some(ty::adjustment::AutoBorrow::RawPtr(m)), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> { - type Lifted = ty::GenSig<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&(self.resume_ty, self.yield_ty, self.return_ty)) - .map(|(resume_ty, yield_ty, return_ty)| ty::GenSig { resume_ty, yield_ty, return_ty }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> { - type Lifted = ty::FnSig<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.inputs_and_output).map(|x| ty::FnSig { - inputs_and_output: x, - c_variadic: self.c_variadic, - unsafety: self.unsafety, - abi: self.abi, - }) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::error::ExpectedFound { - type Lifted = ty::error::ExpectedFound; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.expected).and_then(|expected| { - tcx.lift(&self.found).map(|found| ty::error::ExpectedFound { expected, found }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { - type Lifted = ty::error::TypeError<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - use crate::ty::error::TypeError::*; - - Some(match *self { - Mismatch => Mismatch, - UnsafetyMismatch(x) => UnsafetyMismatch(x), - AbiMismatch(x) => AbiMismatch(x), - Mutability => Mutability, - TupleSize(x) => TupleSize(x), - FixedArraySize(x) => FixedArraySize(x), - ArgCount => ArgCount, - RegionsDoesNotOutlive(a, b) => { - return tcx.lift(&(a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); - } - RegionsInsufficientlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b)); - } - RegionsOverlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b)); - } - RegionsPlaceholderMismatch => RegionsPlaceholderMismatch, - IntMismatch(x) => IntMismatch(x), - FloatMismatch(x) => FloatMismatch(x), - Traits(x) => Traits(x), - VariadicMismatch(x) => VariadicMismatch(x), - CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)), - ProjectionMismatched(x) => ProjectionMismatched(x), - ProjectionBoundsLength(x) => ProjectionBoundsLength(x), - Sorts(ref x) => return tcx.lift(x).map(Sorts), - ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), - ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch), - IntrinsicCast => IntrinsicCast, - ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion), - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { - type Lifted = ty::InstanceDef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), - ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), - ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), - ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), - ty::InstanceDef::FnPtrShim(def_id, ref ty) => { - Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) - } - ty::InstanceDef::Virtual(def_id, n) => Some(ty::InstanceDef::Virtual(def_id, n)), - ty::InstanceDef::ClosureOnceShim { call_once } => { - Some(ty::InstanceDef::ClosureOnceShim { call_once }) - } - ty::InstanceDef::DropGlue(def_id, ref ty) => { - Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)) - } - ty::InstanceDef::CloneShim(def_id, ref ty) => { - Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)) - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// TypeFoldable implementations. -// -// Ideally, each type should invoke `folder.fold_foo(self)` and -// nothing else. In some cases, though, we haven't gotten around to -// adding methods on the `folder` yet, and thus the folding is -// hard-coded here. This is less-flexible, because folders cannot -// override the behavior, but there are a lot of random types and one -// can easily refactor the folding into the TypeFolder trait as -// needed. - -/// AdtDefs are basically the same as a DefId. -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef { - fn super_fold_with>(&self, _folder: &mut F) -> Self { - *self - } - - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false - } -} - -impl<'tcx, T: TypeFoldable<'tcx>, U: TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { - fn super_fold_with>(&self, folder: &mut F) -> (T, U) { - (self.0.fold_with(folder), self.1.fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.0.visit_with(visitor) || self.1.visit_with(visitor) - } -} - -EnumTypeFoldableImpl! { - impl<'tcx, T> TypeFoldable<'tcx> for Option { - (Some)(a), - (None), - } where T: TypeFoldable<'tcx> -} - -EnumTypeFoldableImpl! { - impl<'tcx, T, E> TypeFoldable<'tcx> for Result { - (Ok)(a), - (Err)(a), - } where T: TypeFoldable<'tcx>, E: TypeFoldable<'tcx>, -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Rc::new((**self).fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - (**self).visit_with(visitor) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Arc::new((**self).fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - (**self).visit_with(visitor) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let content: T = (**self).fold_with(folder); - box content - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - (**self).visit_with(visitor) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect() - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect::>().into_boxed_slice() - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.map_bound_ref(|ty| ty.fold_with(folder)) - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_binder(self) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.skip_binder().visit_with(visitor) - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_binder(self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_existential_predicates(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|p| p.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_type_list(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_projs(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - use crate::ty::InstanceDef::*; - Self { - substs: self.substs.fold_with(folder), - def: match self.def { - Item(did) => Item(did.fold_with(folder)), - VtableShim(did) => VtableShim(did.fold_with(folder)), - ReifyShim(did) => ReifyShim(did.fold_with(folder)), - Intrinsic(did) => Intrinsic(did.fold_with(folder)), - FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), - Virtual(did, i) => Virtual(did.fold_with(folder), i), - ClosureOnceShim { call_once } => { - ClosureOnceShim { call_once: call_once.fold_with(folder) } - } - DropGlue(did, ty) => DropGlue(did.fold_with(folder), ty.fold_with(folder)), - CloneShim(did, ty) => CloneShim(did.fold_with(folder), ty.fold_with(folder)), - }, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - use crate::ty::InstanceDef::*; - self.substs.visit_with(visitor) - || match self.def { - Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { - did.visit_with(visitor) - } - FnPtrShim(did, ty) | CloneShim(did, ty) => { - did.visit_with(visitor) || ty.visit_with(visitor) - } - DropGlue(did, ty) => did.visit_with(visitor) || ty.visit_with(visitor), - ClosureOnceShim { call_once } => call_once.visit_with(visitor), - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Self { instance: self.instance.fold_with(folder), promoted: self.promoted } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.instance.visit_with(visitor) - } -} - -impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let kind = match self.kind { - ty::RawPtr(tm) => ty::RawPtr(tm.fold_with(folder)), - ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), - ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), - ty::Adt(tid, substs) => ty::Adt(tid, substs.fold_with(folder)), - ty::Dynamic(ref trait_ty, ref region) => { - ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) - } - ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), - ty::FnDef(def_id, substs) => ty::FnDef(def_id, substs.fold_with(folder)), - ty::FnPtr(f) => ty::FnPtr(f.fold_with(folder)), - ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl), - ty::Generator(did, substs, movability) => { - ty::Generator(did, substs.fold_with(folder), movability) - } - ty::GeneratorWitness(types) => ty::GeneratorWitness(types.fold_with(folder)), - ty::Closure(did, substs) => ty::Closure(did, substs.fold_with(folder)), - ty::Projection(ref data) => ty::Projection(data.fold_with(folder)), - ty::UnnormalizedProjection(ref data) => { - ty::UnnormalizedProjection(data.fold_with(folder)) - } - ty::Opaque(did, substs) => ty::Opaque(did, substs.fold_with(folder)), - - ty::Bool - | ty::Char - | ty::Str - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Error - | ty::Infer(_) - | ty::Param(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Never - | ty::Foreign(..) => return self, - }; - - if self.kind == kind { self } else { folder.tcx().mk_ty(kind) } - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_ty(*self) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match self.kind { - ty::RawPtr(ref tm) => tm.visit_with(visitor), - ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor), - ty::Slice(typ) => typ.visit_with(visitor), - ty::Adt(_, substs) => substs.visit_with(visitor), - ty::Dynamic(ref trait_ty, ref reg) => { - trait_ty.visit_with(visitor) || reg.visit_with(visitor) - } - ty::Tuple(ts) => ts.visit_with(visitor), - ty::FnDef(_, substs) => substs.visit_with(visitor), - ty::FnPtr(ref f) => f.visit_with(visitor), - ty::Ref(r, ty, _) => r.visit_with(visitor) || ty.visit_with(visitor), - ty::Generator(_did, ref substs, _) => substs.visit_with(visitor), - ty::GeneratorWitness(ref types) => types.visit_with(visitor), - ty::Closure(_did, ref substs) => substs.visit_with(visitor), - ty::Projection(ref data) | ty::UnnormalizedProjection(ref data) => { - data.visit_with(visitor) - } - ty::Opaque(_, ref substs) => substs.visit_with(visitor), - - ty::Bool - | ty::Char - | ty::Str - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Error - | ty::Infer(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Param(..) - | ty::Never - | ty::Foreign(..) => false, - } - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_ty(self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { - fn super_fold_with>(&self, _folder: &mut F) -> Self { - *self - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_region(*self) - } - - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_region(*self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_predicates(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|p| p.visit_with(visitor)) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|x| x.fold_with(folder)).collect() - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let ty = self.ty.fold_with(folder); - let val = self.val.fold_with(folder); - folder.tcx().mk_const(ty::Const { ty, val }) - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_const(*self) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.ty.visit_with(visitor) || self.val.visit_with(visitor) - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_const(self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - match *self { - ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)), - ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)), - ty::ConstKind::Unevaluated(did, substs, promoted) => { - ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted) - } - ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) => { - *self - } - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ty::ConstKind::Infer(ic) => ic.visit_with(visitor), - ty::ConstKind::Param(p) => p.visit_with(visitor), - ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor), - ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { - false - } - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> { - fn super_fold_with>(&self, _folder: &mut F) -> Self { - *self - } - - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false - } -} - -// Does the equivalent of -// ``` -// let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); -// folder.tcx().intern_*(&v) -// ``` -fn fold_list<'tcx, F, T>( - list: &'tcx ty::List, - folder: &mut F, - intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> &'tcx ty::List, -) -> &'tcx ty::List -where - F: TypeFolder<'tcx>, - T: TypeFoldable<'tcx> + PartialEq + Copy, -{ - let mut iter = list.iter(); - // Look for the first element that changed - if let Some((i, new_t)) = iter.by_ref().enumerate().find_map(|(i, t)| { - let new_t = t.fold_with(folder); - if new_t == *t { None } else { Some((i, new_t)) } - }) { - // An element changed, prepare to intern the resulting list - let mut new_list = SmallVec::<[_; 8]>::with_capacity(list.len()); - new_list.extend_from_slice(&list[..i]); - new_list.push(new_t); - new_list.extend(iter.map(|t| t.fold_with(folder))); - intern(folder.tcx(), &new_list) - } else { - list - } -} diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs deleted file mode 100644 index da08fbcf14432..0000000000000 --- a/src/librustc/ty/walk.rs +++ /dev/null @@ -1,138 +0,0 @@ -//! An iterator over the type substructure. -//! WARNING: this does not keep track of the region depth. - -use crate::ty::{self, Ty}; -use smallvec::{self, SmallVec}; - -// The TypeWalker's stack is hot enough that it's worth going to some effort to -// avoid heap allocations. -pub type TypeWalkerArray<'tcx> = [Ty<'tcx>; 8]; -pub type TypeWalkerStack<'tcx> = SmallVec>; - -pub struct TypeWalker<'tcx> { - stack: TypeWalkerStack<'tcx>, - last_subtree: usize, -} - -impl<'tcx> TypeWalker<'tcx> { - pub fn new(ty: Ty<'tcx>) -> TypeWalker<'tcx> { - TypeWalker { stack: smallvec![ty], last_subtree: 1 } - } - - /// Skips the subtree of types corresponding to the last type - /// returned by `next()`. - /// - /// Example: Imagine you are walking `Foo, usize>`. - /// - /// ``` - /// let mut iter: TypeWalker = ...; - /// iter.next(); // yields Foo - /// iter.next(); // yields Bar - /// iter.skip_current_subtree(); // skips int - /// 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 = Ty<'tcx>; - - fn next(&mut self) -> Option> { - debug!("next(): stack={:?}", self.stack); - match self.stack.pop() { - None => None, - Some(ty) => { - self.last_subtree = self.stack.len(); - push_subtypes(&mut self.stack, ty); - debug!("next: stack={:?}", self.stack); - Some(ty) - } - } - } -} - -pub fn walk_shallow(ty: Ty<'_>) -> smallvec::IntoIter> { - let mut stack = SmallVec::new(); - push_subtypes(&mut stack, ty); - stack.into_iter() -} - -// We push types 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_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { - 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::Array(ty, len) => { - if let ty::ConstKind::Unevaluated(_, substs, promoted) = len.val { - assert!(promoted.is_none()); - stack.extend(substs.types().rev()); - } - stack.push(len.ty); - stack.push(ty); - } - ty::Slice(ty) => { - stack.push(ty); - } - ty::RawPtr(ref mt) => { - stack.push(mt.ty); - } - ty::Ref(_, ty, _) => { - stack.push(ty); - } - ty::Projection(ref data) | ty::UnnormalizedProjection(ref data) => { - stack.extend(data.substs.types().rev()); - } - ty::Dynamic(ref obj, ..) => { - stack.extend(obj.iter().rev().flat_map(|predicate| { - let (substs, opt_ty) = match *predicate.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => (tr.substs, None), - ty::ExistentialPredicate::Projection(p) => (p.substs, Some(p.ty)), - ty::ExistentialPredicate::AutoTrait(_) => - // Empty iterator - { - (ty::InternalSubsts::empty(), None) - } - }; - - substs.types().rev().chain(opt_ty) - })); - } - ty::Adt(_, substs) | ty::Opaque(_, substs) => { - stack.extend(substs.types().rev()); - } - ty::Closure(_, ref substs) | ty::Generator(_, ref substs, _) => { - stack.extend(substs.types().rev()); - } - ty::GeneratorWitness(ts) => { - stack.extend(ts.skip_binder().iter().cloned().rev()); - } - ty::Tuple(..) => { - stack.extend(parent_ty.tuple_fields().rev()); - } - ty::FnDef(_, substs) => { - stack.extend(substs.types().rev()); - } - ty::FnPtr(sig) => { - stack.push(sig.skip_binder().output()); - stack.extend(sig.skip_binder().inputs().iter().cloned().rev()); - } - } -} diff --git a/src/librustc_apfloat/ieee.rs b/src/librustc_apfloat/ieee.rs index dd56835edbac5..e3d941cad7ae5 100644 --- a/src/librustc_apfloat/ieee.rs +++ b/src/librustc_apfloat/ieee.rs @@ -744,7 +744,7 @@ impl Float for IeeeFloat { Status::OK } - (Category::Zero, _) | (_, Category::NaN) | (_, Category::Infinity) => { + (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => { self = rhs; Status::OK } @@ -954,7 +954,7 @@ impl Float for IeeeFloat { Status::INVALID_OP.and(Self::NAN) } - (Category::Infinity, _) | (Category::Zero, _) => Status::OK.and(self), + (Category::Infinity | Category::Zero, _) => Status::OK.and(self), (Category::Normal, Category::Infinity) => { self.category = Category::Zero; @@ -989,8 +989,7 @@ impl Float for IeeeFloat { fn c_fmod(mut self, rhs: Self) -> StatusAnd { match (self.category, rhs.category) { (Category::NaN, _) - | (Category::Zero, Category::Infinity) - | (Category::Zero, Category::Normal) + | (Category::Zero, Category::Infinity | Category::Normal) | (Category::Normal, Category::Infinity) => Status::OK.and(self), (_, Category::NaN) => { diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index d08ff60a366cc..09a069ab3136d 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -34,6 +34,7 @@ #![no_std] #![forbid(unsafe_code)] #![feature(nll)] +#![feature(or_patterns)] #[macro_use] extern crate alloc; diff --git a/src/librustc_apfloat/ppc.rs b/src/librustc_apfloat/ppc.rs index e0b306ac47cc4..4ae8edf3157eb 100644 --- a/src/librustc_apfloat/ppc.rs +++ b/src/librustc_apfloat/ppc.rs @@ -186,9 +186,7 @@ where Status::OK.and(self) } - (Category::Zero, _) | (_, Category::NaN) | (_, Category::Infinity) => { - Status::OK.and(rhs) - } + (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => Status::OK.and(rhs), (Category::Normal, Category::Normal) => { let mut status = Status::OK; @@ -288,9 +286,9 @@ where Status::OK.and(Self::NAN) } - (Category::Zero, _) | (Category::Infinity, _) => Status::OK.and(self), + (Category::Zero | Category::Infinity, _) => Status::OK.and(self), - (_, Category::Zero) | (_, Category::Infinity) => Status::OK.and(rhs), + (_, Category::Zero | Category::Infinity) => Status::OK.and(rhs), (Category::Normal, Category::Normal) => { let mut status = Status::OK; diff --git a/src/librustc_ast/Cargo.toml b/src/librustc_ast/Cargo.toml index 867bcf7923264..7d105f9e8863d 100644 --- a/src/librustc_ast/Cargo.toml +++ b/src/librustc_ast/Cargo.toml @@ -19,3 +19,4 @@ rustc_index = { path = "../librustc_index" } rustc_lexer = { path = "../librustc_lexer" } rustc_macros = { path = "../librustc_macros" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } +bitflags = "1.2.1" diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index c796a37553164..7ff835725073d 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -14,16 +14,14 @@ //! - [`Generics`], [`GenericParam`], [`WhereClause`]: Metadata associated with generic parameters. //! - [`EnumDef`] and [`Variant`]: Enum declaration. //! - [`Lit`] and [`LitKind`]: Literal expressions. -//! - [`MacroDef`], [`MacStmtStyle`], [`MacCall`], [`MacDelimeter`]: Macro definition and invocation. +//! - [`MacroDef`], [`MacStmtStyle`], [`MacCall`], [`MacDelimiter`]: Macro definition and invocation. //! - [`Attribute`]: Metadata associated with item. -//! - [`UnOp`], [`UnOpKind`], [`BinOp`], [`BinOpKind`]: Unary and binary operators. +//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. pub use crate::util::parser::ExprPrecedence; pub use GenericArgs::*; pub use UnsafeSource::*; -pub use rustc_span::symbol::{Ident, Symbol as Name}; - use crate::ptr::P; use crate::token::{self, DelimToken}; use crate::tokenstream::{DelimSpan, TokenStream, TokenTree}; @@ -34,7 +32,7 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_macros::HashStable_Generic; use rustc_serialize::{self, Decoder, Encoder}; use rustc_span::source_map::{respan, Spanned}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::convert::TryFrom; @@ -214,11 +212,18 @@ impl GenericArg { pub struct AngleBracketedArgs { /// The overall span. pub span: Span, - /// The arguments for this path segment. - pub args: Vec, - /// Constraints on associated types, if any. - /// E.g., `Foo`. - pub constraints: Vec, + /// The comma separated parts in the `<...>`. + pub args: Vec, +} + +/// Either an argument for a parameter e.g., `'a`, `Vec`, `0`, +/// or a constraint on an associated item, e.g., `Item = String` or `Item: Bound`. +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub enum AngleBracketedArg { + /// Argument for a generic parameter. + Arg(GenericArg), + /// Constraint for an associated item. + Constraint(AssocTyConstraint), } impl Into>> for AngleBracketedArgs { @@ -248,11 +253,13 @@ pub struct ParenthesizedArgs { impl ParenthesizedArgs { pub fn as_angle_bracketed_args(&self) -> AngleBracketedArgs { - AngleBracketedArgs { - span: self.span, - args: self.inputs.iter().cloned().map(GenericArg::Type).collect(), - constraints: vec![], - } + let args = self + .inputs + .iter() + .cloned() + .map(|input| AngleBracketedArg::Arg(GenericArg::Type(input))) + .collect(); + AngleBracketedArgs { span: self.span, args } } } @@ -291,8 +298,8 @@ pub enum GenericBound { impl GenericBound { pub fn span(&self) -> Span { match self { - &GenericBound::Trait(ref t, ..) => t.span, - &GenericBound::Outlives(ref l) => l.ident.span, + GenericBound::Trait(ref t, ..) => t.span, + GenericBound::Outlives(ref l) => l.ident.span, } } } @@ -1114,7 +1121,7 @@ impl Expr { ExprKind::Break(..) => ExprPrecedence::Break, ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, + ExprKind::InlineAsm(..) | ExprKind::LlvmInlineAsm(..) => ExprPrecedence::InlineAsm, ExprKind::MacCall(..) => ExprPrecedence::Mac, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, @@ -1244,7 +1251,9 @@ pub enum ExprKind { Ret(Option>), /// Output of the `asm!()` macro. - InlineAsm(P), + InlineAsm(InlineAsm), + /// Output of the `llvm_asm!()` macro. + LlvmInlineAsm(P), /// A macro invocation; pre-expansion. MacCall(MacCall), @@ -1560,8 +1569,7 @@ impl LitKind { pub fn is_suffixed(&self) -> bool { match *self { // suffixed variants - LitKind::Int(_, LitIntType::Signed(..)) - | LitKind::Int(_, LitIntType::Unsigned(..)) + LitKind::Int(_, LitIntType::Signed(..) | LitIntType::Unsigned(..)) | LitKind::Float(_, LitFloatType::Suffixed(..)) => true, // unsuffixed variants LitKind::Str(..) @@ -1614,7 +1622,7 @@ impl FloatTy { } } - pub fn bit_width(self) -> usize { + pub fn bit_width(self) -> u64 { match self { FloatTy::F32 => 32, FloatTy::F64 => 64, @@ -1663,7 +1671,7 @@ impl IntTy { format!("{}{}", val as u128, self.name_str()) } - pub fn bit_width(&self) -> Option { + pub fn bit_width(&self) -> Option { Some(match *self { IntTy::Isize => return None, IntTy::I8 => 8, @@ -1725,7 +1733,7 @@ impl UintTy { format!("{}{}", val, self.name_str()) } - pub fn bit_width(&self) -> Option { + pub fn bit_width(&self) -> Option { Some(match *self { UintTy::Usize => return None, UintTy::U8 => 8, @@ -1858,39 +1866,145 @@ pub enum TraitObjectSyntax { None, } +/// Inline assembly operand explicit register or register class. +/// +/// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`. +#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)] +pub enum InlineAsmRegOrRegClass { + Reg(Symbol), + RegClass(Symbol), +} + +bitflags::bitflags! { + #[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] + pub struct InlineAsmOptions: u8 { + const PURE = 1 << 0; + const NOMEM = 1 << 1; + const READONLY = 1 << 2; + const PRESERVES_FLAGS = 1 << 3; + const NORETURN = 1 << 4; + const NOSTACK = 1 << 5; + const ATT_SYNTAX = 1 << 6; + } +} + +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +pub enum InlineAsmTemplatePiece { + String(String), + Placeholder { operand_idx: usize, modifier: Option, span: Span }, +} + +impl fmt::Display for InlineAsmTemplatePiece { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::String(s) => { + for c in s.chars() { + match c { + '{' => f.write_str("{{")?, + '}' => f.write_str("}}")?, + _ => write!(f, "{}", c.escape_debug())?, + } + } + Ok(()) + } + Self::Placeholder { operand_idx, modifier: Some(modifier), .. } => { + write!(f, "{{{}:{}}}", operand_idx, modifier) + } + Self::Placeholder { operand_idx, modifier: None, .. } => { + write!(f, "{{{}}}", operand_idx) + } + } + } +} + +impl InlineAsmTemplatePiece { + /// Rebuilds the asm template string from its pieces. + pub fn to_string(s: &[Self]) -> String { + use fmt::Write; + let mut out = String::new(); + for p in s.iter() { + let _ = write!(out, "{}", p); + } + out + } +} + +/// Inline assembly operand. +/// +/// E.g., `out("eax") result` as in `asm!("mov eax, 2", out("eax") result)`. +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub enum InlineAsmOperand { + In { + reg: InlineAsmRegOrRegClass, + expr: P, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: P, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_expr: P, + out_expr: Option>, + }, + Const { + expr: P, + }, + Sym { + expr: P, + }, +} + +/// Inline assembly. +/// +/// E.g., `asm!("NOP");`. +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub struct InlineAsm { + pub template: Vec, + pub operands: Vec<(InlineAsmOperand, Span)>, + pub options: InlineAsmOptions, +} + /// Inline assembly dialect. /// -/// E.g., `"intel"` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. +/// E.g., `"intel"` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy, HashStable_Generic)] -pub enum AsmDialect { +pub enum LlvmAsmDialect { Att, Intel, } -/// Inline assembly. +/// LLVM-style inline assembly. /// -/// E.g., `"={eax}"(result)` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. +/// E.g., `"={eax}"(result)` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] -pub struct InlineAsmOutput { +pub struct LlvmInlineAsmOutput { pub constraint: Symbol, pub expr: P, pub is_rw: bool, pub is_indirect: bool, } -/// Inline assembly. +/// LLVM-style inline assembly. /// -/// E.g., `asm!("NOP");`. +/// E.g., `llvm_asm!("NOP");`. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] -pub struct InlineAsm { +pub struct LlvmInlineAsm { pub asm: Symbol, pub asm_str_style: StrStyle, - pub outputs: Vec, + pub outputs: Vec, pub inputs: Vec<(Symbol, P)>, pub clobbers: Vec, pub volatile: bool, pub alignstack: bool, - pub dialect: AsmDialect, + pub dialect: LlvmAsmDialect, } /// A parameter in a function header. @@ -2203,14 +2317,14 @@ rustc_index::newtype_index! { } impl rustc_serialize::Encodable for AttrId { - fn encode(&self, _: &mut S) -> Result<(), S::Error> { - Ok(()) + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_unit() } } impl rustc_serialize::Decodable for AttrId { - fn decode(_: &mut D) -> Result { - Ok(crate::attr::mk_attr_id()) + fn decode(d: &mut D) -> Result { + d.read_nil().map(|_| crate::attr::mk_attr_id()) } } @@ -2443,7 +2557,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), /// A use declaration item (`use`). /// /// E.g., `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`. diff --git a/src/librustc_ast/attr/mod.rs b/src/librustc_ast/attr/mod.rs index d53d776778598..b812f2dadf6d4 100644 --- a/src/librustc_ast/attr/mod.rs +++ b/src/librustc_ast/attr/mod.rs @@ -3,8 +3,8 @@ use crate::ast; use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute}; use crate::ast::{Expr, GenericParam, Item, Lit, LitKind, Local, Stmt, StmtKind}; -use crate::ast::{Ident, Name, Path, PathSegment}; use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; +use crate::ast::{Path, PathSegment}; use crate::mut_visit::visit_clobber; use crate::ptr::P; use crate::token::{self, Token}; @@ -14,7 +14,7 @@ use rustc_data_structures::sync::Lock; use rustc_index::bit_set::GrowableBitSet; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::source_map::{BytePos, Spanned}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use log::debug; @@ -113,7 +113,7 @@ impl NestedMetaItem { } /// Returns a name and single literal value tuple of the `MetaItem`. - pub fn name_value_literal(&self) -> Option<(Name, &Lit)> { + pub fn name_value_literal(&self) -> Option<(Symbol, &Lit)> { self.meta_item().and_then(|meta_item| { meta_item.meta_item_list().and_then(|meta_item_list| { if meta_item_list.len() == 1 { @@ -442,8 +442,10 @@ impl MetaItem { { // FIXME: Share code with `parse_path`. let path = match tokens.next().map(TokenTree::uninterpolate) { - Some(TokenTree::Token(Token { kind: kind @ token::Ident(..), span })) - | Some(TokenTree::Token(Token { kind: kind @ token::ModSep, span })) => 'arm: { + Some(TokenTree::Token(Token { + kind: kind @ (token::Ident(..) | token::ModSep), + span, + })) => 'arm: { let mut segments = if let token::Ident(name, _) = kind { if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() { diff --git a/src/librustc_ast/entry.rs b/src/librustc_ast/entry.rs index 0a72019bfe986..90d417a45fd93 100644 --- a/src/librustc_ast/entry.rs +++ b/src/librustc_ast/entry.rs @@ -10,7 +10,7 @@ pub enum EntryPointType { OtherMain, // Not an entry point, but some other function named main } -// Beware, this is duplicated in librustc/middle/entry.rs, make sure to keep +// Beware, this is duplicated in librustc_middle/middle/entry.rs, make sure to keep // them in sync. pub fn entry_point_type(item: &Item, depth: usize) -> EntryPointType { match item.kind { diff --git a/src/librustc_ast/expand/mod.rs b/src/librustc_ast/expand/mod.rs index 50df8fa39eddf..3c634ff40ccb1 100644 --- a/src/librustc_ast/expand/mod.rs +++ b/src/librustc_ast/expand/mod.rs @@ -1,4 +1,4 @@ -//! Definitions shared by macros / syntax extensions and e.g. librustc. +//! Definitions shared by macros / syntax extensions and e.g. librustc_middle. use crate::ast::Attribute; use rustc_span::symbol::sym; diff --git a/src/librustc_ast/lib.rs b/src/librustc_ast/lib.rs index a93e0fcbd7193..cb3118cba23dd 100644 --- a/src/librustc_ast/lib.rs +++ b/src/librustc_ast/lib.rs @@ -14,10 +14,15 @@ #![feature(crate_visibility_modifier)] #![feature(label_break_value)] #![feature(nll)] +#![feature(or_patterns)] #![feature(try_trait)] #![feature(unicode_internals)] #![recursion_limit = "256"] +// FIXME(#56935): Work around ICEs during cross-compilation. +#[allow(unused)] +extern crate rustc_macros; + #[macro_export] macro_rules! unwrap_or { ($opt:expr, $default:expr) => { @@ -33,7 +38,6 @@ pub mod util { pub mod comments; pub mod lev_distance; pub mod literal; - pub mod map_in_place; pub mod parser; } @@ -54,7 +58,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc. +/// instead of implementing everything in librustc_middle. pub trait HashStableContext: rustc_span::HashStableContext { fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher); } diff --git a/src/librustc_ast/mut_visit.rs b/src/librustc_ast/mut_visit.rs index aa2968b3cbe44..2c575c3e28861 100644 --- a/src/librustc_ast/mut_visit.rs +++ b/src/librustc_ast/mut_visit.rs @@ -11,10 +11,11 @@ use crate::ast::*; use crate::ptr::P; use crate::token::{self, Token}; use crate::tokenstream::*; -use crate::util::map_in_place::MapInPlace; +use rustc_data_structures::map_in_place::MapInPlace; use rustc_data_structures::sync::Lrc; use rustc_span::source_map::{respan, Spanned}; +use rustc_span::symbol::Ident; use rustc_span::Span; use smallvec::{smallvec, Array, SmallVec}; @@ -546,9 +547,11 @@ pub fn noop_visit_angle_bracketed_parameter_data( data: &mut AngleBracketedArgs, vis: &mut T, ) { - let AngleBracketedArgs { args, constraints, span } = data; - visit_vec(args, |arg| vis.visit_generic_arg(arg)); - visit_vec(constraints, |constraint| vis.visit_ty_constraint(constraint)); + let AngleBracketedArgs { args, span } = data; + visit_vec(args, |arg| match arg { + AngleBracketedArg::Arg(arg) => vis.visit_generic_arg(arg), + AngleBracketedArg::Constraint(constraint) => vis.visit_ty_constraint(constraint), + }); vis.visit_span(span); } @@ -1203,7 +1206,28 @@ pub fn noop_visit_expr(Expr { kind, id, span, attrs }: &mut Expr, visit_opt(expr, |expr| vis.visit_expr(expr)); } ExprKind::InlineAsm(asm) => { - let InlineAsm { + for (op, _) in &mut asm.operands { + match op { + InlineAsmOperand::In { expr, .. } + | InlineAsmOperand::InOut { expr, .. } + | InlineAsmOperand::Const { expr, .. } + | InlineAsmOperand::Sym { expr, .. } => vis.visit_expr(expr), + InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + vis.visit_expr(expr); + } + } + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + vis.visit_expr(in_expr); + if let Some(out_expr) = out_expr { + vis.visit_expr(out_expr); + } + } + } + } + } + ExprKind::LlvmInlineAsm(asm) => { + let LlvmInlineAsm { asm: _, asm_str_style: _, outputs, @@ -1214,7 +1238,7 @@ pub fn noop_visit_expr(Expr { kind, id, span, attrs }: &mut Expr, dialect: _, } = asm.deref_mut(); for out in outputs { - let InlineAsmOutput { constraint: _, expr, is_rw: _, is_indirect: _ } = out; + let LlvmInlineAsmOutput { constraint: _, expr, is_rw: _, is_indirect: _ } = out; vis.visit_expr(expr); } visit_vec(inputs, |(_c, expr)| vis.visit_expr(expr)); diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs index be5d322ba1677..a5b9c2a95bbea 100644 --- a/src/librustc_ast/token.rs +++ b/src/librustc_ast/token.rs @@ -12,7 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_macros::HashStable_Generic; use rustc_span::symbol::kw; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{self, Span, DUMMY_SP}; use std::borrow::Cow; use std::{fmt, mem}; @@ -145,7 +145,7 @@ impl Lit { } } -pub fn ident_can_begin_expr(name: ast::Name, span: Span, is_raw: bool) -> bool { +pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool { let ident_token = Token::new(Ident(name, is_raw), span); !ident_token.is_reserved_ident() @@ -173,7 +173,7 @@ pub fn ident_can_begin_expr(name: ast::Name, span: Span, is_raw: bool) -> bool { .contains(&name) } -fn ident_can_begin_type(name: ast::Name, span: Span, is_raw: bool) -> bool { +fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool { let ident_token = Token::new(Ident(name, is_raw), span); !ident_token.is_reserved_ident() @@ -229,18 +229,18 @@ pub enum TokenKind { /// 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. - Ident(ast::Name, /* is_raw */ bool), + Ident(Symbol, /* is_raw */ bool), /// 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. - Lifetime(ast::Name), + Lifetime(Symbol), Interpolated(Lrc), // Can be expanded into several tokens. /// A doc comment. - DocComment(ast::Name), + DocComment(Symbol), // Junk. These carry no data because we don't really care about the data // they *would* carry, and don't really want to allocate a new ident for @@ -249,9 +249,9 @@ pub enum TokenKind { Whitespace, /// A comment. Comment, - Shebang(ast::Name), + Shebang(Symbol), /// A completely invalid token which should be skipped. - Unknown(ast::Name), + Unknown(Symbol), Eof, } @@ -325,8 +325,8 @@ impl Token { Token::new(TokenKind::Whitespace, DUMMY_SP) } - /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary. - pub fn from_ast_ident(ident: ast::Ident) -> Self { + /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary. + pub fn from_ast_ident(ident: Ident) -> Self { Token::new(Ident(ident.name, ident.is_raw_guess()), ident.span) } @@ -488,19 +488,19 @@ impl Token { } /// Returns an identifier if this token is an identifier. - pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> { + pub fn ident(&self) -> Option<(Ident, /* is_raw */ bool)> { let token = self.uninterpolate(); match token.kind { - Ident(name, is_raw) => Some((ast::Ident::new(name, token.span), is_raw)), + Ident(name, is_raw) => Some((Ident::new(name, token.span), is_raw)), _ => None, } } /// Returns a lifetime identifier if this token is a lifetime. - pub fn lifetime(&self) -> Option { + pub fn lifetime(&self) -> Option { let token = self.uninterpolate(); match token.kind { - Lifetime(name) => Some(ast::Ident::new(name, token.span)), + Lifetime(name) => Some(Ident::new(name, token.span)), _ => None, } } @@ -577,28 +577,28 @@ impl Token { } pub fn is_path_segment_keyword(&self) -> bool { - self.is_non_raw_ident_where(ast::Ident::is_path_segment_keyword) + self.is_non_raw_ident_where(Ident::is_path_segment_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 { - self.is_non_raw_ident_where(ast::Ident::is_special) + self.is_non_raw_ident_where(Ident::is_special) } /// Returns `true` if the token is a keyword used in the language. pub fn is_used_keyword(&self) -> bool { - self.is_non_raw_ident_where(ast::Ident::is_used_keyword) + self.is_non_raw_ident_where(Ident::is_used_keyword) } /// Returns `true` if the token is a keyword reserved for possible future use. pub fn is_unused_keyword(&self) -> bool { - self.is_non_raw_ident_where(ast::Ident::is_unused_keyword) + self.is_non_raw_ident_where(Ident::is_unused_keyword) } /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved_ident(&self) -> bool { - self.is_non_raw_ident_where(ast::Ident::is_reserved) + self.is_non_raw_ident_where(Ident::is_reserved) } /// Returns `true` if the token is the identifier `true` or `false`. @@ -607,7 +607,7 @@ impl Token { } /// Returns `true` if the token is a non-raw identifier for which `pred` holds. - pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool { + pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool { match self.ident() { Some((id, false)) => pred(id), _ => false, @@ -746,8 +746,8 @@ pub enum Nonterminal { NtPat(P), NtExpr(P), NtTy(P), - NtIdent(ast::Ident, /* is_raw */ bool), - NtLifetime(ast::Ident), + NtIdent(Ident, /* is_raw */ bool), + NtLifetime(Ident), NtLiteral(P), /// Stuff inside brackets for attributes NtMeta(P), diff --git a/src/librustc_ast/util/comments.rs b/src/librustc_ast/util/comments.rs index 0e42ae11fa2fa..9874754fcd2f7 100644 --- a/src/librustc_ast/util/comments.rs +++ b/src/librustc_ast/util/comments.rs @@ -5,7 +5,6 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, CharPos, FileName, Pos}; use log::debug; -use std::usize; #[cfg(test)] mod tests; @@ -227,11 +226,11 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec { if !is_block_doc_comment(token_text) { let code_to_the_right = match text[pos + token.len..].chars().next() { - Some('\r') | Some('\n') => false, + Some('\r' | '\n') => false, _ => true, }; let style = match (code_to_the_left, code_to_the_right) { - (true, true) | (false, true) => Mixed, + (_, true) => Mixed, (false, false) => Isolated, (true, false) => Trailing, }; diff --git a/src/librustc_ast/util/literal.rs b/src/librustc_ast/util/literal.rs index 1b17f343a6d67..4428d09902b92 100644 --- a/src/librustc_ast/util/literal.rs +++ b/src/librustc_ast/util/literal.rs @@ -6,8 +6,7 @@ use crate::tokenstream::TokenTree; use rustc_data_structures::sync::Lrc; use rustc_lexer::unescape::{unescape_byte, unescape_char}; -use rustc_lexer::unescape::{unescape_byte_str, unescape_str}; -use rustc_lexer::unescape::{unescape_raw_byte_str, unescape_raw_str}; +use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; @@ -59,45 +58,53 @@ impl LitKind { // new symbol because the string in the LitKind is different to the // string in the token. let s = symbol.as_str(); - let symbol = if s.contains(&['\\', '\r'][..]) { - let mut buf = String::with_capacity(s.len()); - let mut error = Ok(()); - unescape_str(&s, &mut |_, unescaped_char| match unescaped_char { - Ok(c) => buf.push(c), - Err(_) => error = Err(LitError::LexerError), - }); - error?; - Symbol::intern(&buf) - } else { - symbol - }; + let symbol = + if s.contains(&['\\', '\r'][..]) { + let mut buf = String::with_capacity(s.len()); + let mut error = Ok(()); + unescape_literal(&s, Mode::Str, &mut |_, unescaped_char| { + match unescaped_char { + Ok(c) => buf.push(c), + Err(_) => error = Err(LitError::LexerError), + } + }); + error?; + Symbol::intern(&buf) + } else { + symbol + }; LitKind::Str(symbol, ast::StrStyle::Cooked) } token::StrRaw(n) => { // Ditto. let s = symbol.as_str(); - let symbol = if s.contains('\r') { - let mut buf = String::with_capacity(s.len()); - let mut error = Ok(()); - unescape_raw_str(&s, &mut |_, unescaped_char| match unescaped_char { - Ok(c) => buf.push(c), - Err(_) => error = Err(LitError::LexerError), - }); - error?; - buf.shrink_to_fit(); - Symbol::intern(&buf) - } else { - symbol - }; + let symbol = + if s.contains('\r') { + let mut buf = String::with_capacity(s.len()); + let mut error = Ok(()); + unescape_literal(&s, Mode::RawStr, &mut |_, unescaped_char| { + match unescaped_char { + Ok(c) => buf.push(c), + Err(_) => error = Err(LitError::LexerError), + } + }); + error?; + buf.shrink_to_fit(); + Symbol::intern(&buf) + } else { + symbol + }; LitKind::Str(symbol, ast::StrStyle::Raw(n)) } token::ByteStr => { let s = symbol.as_str(); let mut buf = Vec::with_capacity(s.len()); let mut error = Ok(()); - unescape_byte_str(&s, &mut |_, unescaped_byte| match unescaped_byte { - Ok(c) => buf.push(c), - Err(_) => error = Err(LitError::LexerError), + unescape_byte_literal(&s, Mode::ByteStr, &mut |_, unescaped_byte| { + match unescaped_byte { + Ok(c) => buf.push(c), + Err(_) => error = Err(LitError::LexerError), + } }); error?; buf.shrink_to_fit(); @@ -108,9 +115,11 @@ impl LitKind { let bytes = if s.contains('\r') { let mut buf = Vec::with_capacity(s.len()); let mut error = Ok(()); - unescape_raw_byte_str(&s, &mut |_, unescaped_byte| match unescaped_byte { - Ok(c) => buf.push(c), - Err(_) => error = Err(LitError::LexerError), + unescape_byte_literal(&s, Mode::RawByteStr, &mut |_, unescaped_byte| { + match unescaped_byte { + Ok(c) => buf.push(c), + Err(_) => error = Err(LitError::LexerError), + } }); error?; buf.shrink_to_fit(); diff --git a/src/librustc_ast/visit.rs b/src/librustc_ast/visit.rs index 39028b7583c63..41c02734442a4 100644 --- a/src/librustc_ast/visit.rs +++ b/src/librustc_ast/visit.rs @@ -17,6 +17,7 @@ use crate::ast::*; use crate::token::Token; use crate::tokenstream::{TokenStream, TokenTree}; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; #[derive(Copy, Clone, PartialEq)] @@ -74,7 +75,7 @@ impl<'a> FnKind<'a> { /// to monitor future changes to `Visitor` in case a new method with a /// new default implementation gets introduced.) pub trait Visitor<'ast>: Sized { - fn visit_name(&mut self, _span: Span, _name: Name) { + fn visit_name(&mut self, _span: Span, _name: Symbol) { // Nothing to do. } fn visit_ident(&mut self, ident: Ident) { @@ -464,8 +465,12 @@ where { match *generic_args { GenericArgs::AngleBracketed(ref data) => { - walk_list!(visitor, visit_generic_arg, &data.args); - walk_list!(visitor, visit_assoc_ty_constraint, &data.constraints); + for arg in &data.args { + match arg { + AngleBracketedArg::Arg(a) => visitor.visit_generic_arg(a), + AngleBracketedArg::Constraint(c) => visitor.visit_assoc_ty_constraint(c), + } + } } GenericArgs::Parenthesized(ref data) => { walk_list!(visitor, visit_ty, &data.inputs); @@ -814,6 +819,27 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::MacCall(ref mac) => visitor.visit_mac(mac), ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression), ExprKind::InlineAsm(ref ia) => { + for (op, _) in &ia.operands { + match op { + InlineAsmOperand::In { expr, .. } + | InlineAsmOperand::InOut { expr, .. } + | InlineAsmOperand::Const { expr, .. } + | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr), + InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + visitor.visit_expr(expr); + } + } + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + visitor.visit_expr(in_expr); + if let Some(out_expr) = out_expr { + visitor.visit_expr(out_expr); + } + } + } + } + } + ExprKind::LlvmInlineAsm(ref ia) => { for &(_, ref input) in &ia.inputs { visitor.visit_expr(input) } diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs index 239dec9e9746b..5bcd111706f35 100644 --- a/src/librustc_ast_lowering/expr.rs +++ b/src/librustc_ast_lowering/expr.rs @@ -3,12 +3,17 @@ use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericAr use rustc_ast::ast::*; use rustc_ast::attr; use rustc_ast::ptr::P as AstP; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_target::asm; +use std::collections::hash_map::Entry; +use std::fmt::Write; impl<'hir> LoweringContext<'_, 'hir> { fn lower_exprs(&mut self, exprs: &[AstP]) -> &'hir [hir::Expr<'hir>] { @@ -20,192 +25,207 @@ impl<'hir> LoweringContext<'_, 'hir> { } pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { - let kind = match e.kind { - ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)), - ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::Repeat(ref expr, ref count) => { - let expr = self.lower_expr(expr); - let count = self.lower_anon_const(count); - hir::ExprKind::Repeat(expr, count) - } - ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), - ExprKind::Call(ref f, ref args) => { - let f = self.lower_expr(f); - hir::ExprKind::Call(f, self.lower_exprs(args)) - } - ExprKind::MethodCall(ref seg, ref args) => { - let hir_seg = self.arena.alloc(self.lower_path_segment( - e.span, - seg, - ParamMode::Optional, - 0, - ParenthesizedGenericArgs::Err, - ImplTraitContext::disallowed(), - None, - )); - let args = self.lower_exprs(args); - hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args) - } - ExprKind::Binary(binop, ref lhs, ref rhs) => { - let binop = self.lower_binop(binop); - let lhs = self.lower_expr(lhs); - let rhs = self.lower_expr(rhs); - hir::ExprKind::Binary(binop, lhs, rhs) - } - ExprKind::Unary(op, ref ohs) => { - let op = self.lower_unop(op); - let ohs = self.lower_expr(ohs); - hir::ExprKind::Unary(op, ohs) - } - ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.kind.clone())), - ExprKind::Cast(ref expr, ref ty) => { - let expr = self.lower_expr(expr); - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); - hir::ExprKind::Cast(expr, ty) - } - ExprKind::Type(ref expr, ref ty) => { - let expr = self.lower_expr(expr); - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); - hir::ExprKind::Type(expr, ty) - } - ExprKind::AddrOf(k, m, ref ohs) => { - let ohs = self.lower_expr(ohs); - hir::ExprKind::AddrOf(k, m, ohs) - } - ExprKind::Let(ref pat, ref scrutinee) => self.lower_expr_let(e.span, pat, scrutinee), - ExprKind::If(ref cond, ref then, ref else_opt) => { - self.lower_expr_if(e.span, cond, then, else_opt.as_deref()) - } - ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| { - this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label) - }), - ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { - hir::ExprKind::Loop(this.lower_block(body, false), opt_label, hir::LoopSource::Loop) - }), - ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body), - ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match( - self.lower_expr(expr), - self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), - hir::MatchSource::Normal, - ), - ExprKind::Async(capture_clause, closure_node_id, ref block) => self.make_async_expr( - capture_clause, - closure_node_id, - None, - block.span, - hir::AsyncGeneratorKind::Block, - |this| this.with_new_scopes(|this| this.lower_block_expr(block)), - ), - ExprKind::Await(ref expr) => self.lower_expr_await(e.span, expr), - ExprKind::Closure( - capture_clause, - asyncness, - movability, - ref decl, - ref body, - fn_decl_span, - ) => { - if let Async::Yes { closure_id, .. } = asyncness { - self.lower_expr_async_closure( - capture_clause, - closure_id, - decl, - body, - fn_decl_span, + ensure_sufficient_stack(|| { + let kind = match e.kind { + ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)), + ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), + ExprKind::Repeat(ref expr, ref count) => { + let expr = self.lower_expr(expr); + let count = self.lower_anon_const(count); + hir::ExprKind::Repeat(expr, count) + } + ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), + ExprKind::Call(ref f, ref args) => { + let f = self.lower_expr(f); + hir::ExprKind::Call(f, self.lower_exprs(args)) + } + ExprKind::MethodCall(ref seg, ref args) => { + let hir_seg = self.arena.alloc(self.lower_path_segment( + e.span, + seg, + ParamMode::Optional, + 0, + ParenthesizedGenericArgs::Err, + ImplTraitContext::disallowed(), + None, + )); + let args = self.lower_exprs(args); + hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args) + } + ExprKind::Binary(binop, ref lhs, ref rhs) => { + let binop = self.lower_binop(binop); + let lhs = self.lower_expr(lhs); + let rhs = self.lower_expr(rhs); + hir::ExprKind::Binary(binop, lhs, rhs) + } + ExprKind::Unary(op, ref ohs) => { + let op = self.lower_unop(op); + let ohs = self.lower_expr(ohs); + hir::ExprKind::Unary(op, ohs) + } + ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.kind.clone())), + ExprKind::Cast(ref expr, ref ty) => { + let expr = self.lower_expr(expr); + let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + hir::ExprKind::Cast(expr, ty) + } + ExprKind::Type(ref expr, ref ty) => { + let expr = self.lower_expr(expr); + let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + hir::ExprKind::Type(expr, ty) + } + ExprKind::AddrOf(k, m, ref ohs) => { + let ohs = self.lower_expr(ohs); + hir::ExprKind::AddrOf(k, m, ohs) + } + ExprKind::Let(ref pat, ref scrutinee) => { + self.lower_expr_let(e.span, pat, scrutinee) + } + ExprKind::If(ref cond, ref then, ref else_opt) => { + self.lower_expr_if(e.span, cond, then, else_opt.as_deref()) + } + ExprKind::While(ref cond, ref body, opt_label) => self + .with_loop_scope(e.id, |this| { + this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label) + }), + ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { + hir::ExprKind::Loop( + this.lower_block(body, false), + opt_label, + hir::LoopSource::Loop, ) - } else { - self.lower_expr_closure(capture_clause, movability, decl, body, fn_decl_span) + }), + ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body), + ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match( + self.lower_expr(expr), + self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), + hir::MatchSource::Normal, + ), + ExprKind::Async(capture_clause, closure_node_id, ref block) => self + .make_async_expr( + capture_clause, + closure_node_id, + None, + block.span, + hir::AsyncGeneratorKind::Block, + |this| this.with_new_scopes(|this| this.lower_block_expr(block)), + ), + ExprKind::Await(ref expr) => self.lower_expr_await(e.span, expr), + ExprKind::Closure( + capture_clause, + asyncness, + movability, + ref decl, + ref body, + fn_decl_span, + ) => { + if let Async::Yes { closure_id, .. } = asyncness { + self.lower_expr_async_closure( + capture_clause, + closure_id, + decl, + body, + fn_decl_span, + ) + } else { + self.lower_expr_closure( + capture_clause, + movability, + decl, + body, + fn_decl_span, + ) + } } - } - ExprKind::Block(ref blk, opt_label) => { - hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) - } - ExprKind::Assign(ref el, ref er, span) => { - hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span) - } - ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( - self.lower_binop(op), - self.lower_expr(el), - self.lower_expr(er), - ), - ExprKind::Field(ref el, ident) => hir::ExprKind::Field(self.lower_expr(el), ident), - ExprKind::Index(ref el, ref er) => { - hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er)) - } - ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { - self.lower_expr_range_closed(e.span, e1, e2) - } - ExprKind::Range(ref e1, ref e2, lims) => { - self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) - } - ExprKind::Path(ref qself, ref path) => { - let qpath = self.lower_qpath( - e.id, - qself, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); - hir::ExprKind::Path(qpath) - } - ExprKind::Break(opt_label, ref opt_expr) => { - let opt_expr = opt_expr.as_ref().map(|x| self.lower_expr(x)); - hir::ExprKind::Break(self.lower_jump_destination(e.id, opt_label), opt_expr) - } - ExprKind::Continue(opt_label) => { - hir::ExprKind::Continue(self.lower_jump_destination(e.id, opt_label)) - } - ExprKind::Ret(ref e) => { - let e = e.as_ref().map(|x| self.lower_expr(x)); - hir::ExprKind::Ret(e) - } - ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(asm), - ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { - let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); - hir::ExprKind::Struct( - self.arena.alloc(self.lower_qpath( + ExprKind::Block(ref blk, opt_label) => { + hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) + } + ExprKind::Assign(ref el, ref er, span) => { + hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span) + } + ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( + self.lower_binop(op), + self.lower_expr(el), + self.lower_expr(er), + ), + ExprKind::Field(ref el, ident) => hir::ExprKind::Field(self.lower_expr(el), ident), + ExprKind::Index(ref el, ref er) => { + hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er)) + } + ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { + self.lower_expr_range_closed(e.span, e1, e2) + } + ExprKind::Range(ref e1, ref e2, lims) => { + self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) + } + ExprKind::Path(ref qself, ref path) => { + let qpath = self.lower_qpath( e.id, - &None, + qself, path, ParamMode::Optional, ImplTraitContext::disallowed(), - )), - self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), - maybe_expr, - ) - } - ExprKind::Paren(ref ex) => { - let mut ex = self.lower_expr_mut(ex); - // Include parens in span, but only if it is a super-span. - if e.span.contains(ex.span) { - ex.span = e.span; + ); + hir::ExprKind::Path(qpath) + } + ExprKind::Break(opt_label, ref opt_expr) => { + let opt_expr = opt_expr.as_ref().map(|x| self.lower_expr(x)); + hir::ExprKind::Break(self.lower_jump_destination(e.id, opt_label), opt_expr) + } + ExprKind::Continue(opt_label) => { + hir::ExprKind::Continue(self.lower_jump_destination(e.id, opt_label)) + } + ExprKind::Ret(ref e) => { + let e = e.as_ref().map(|x| self.lower_expr(x)); + hir::ExprKind::Ret(e) + } + ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), + ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), + ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { + let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); + hir::ExprKind::Struct( + self.arena.alloc(self.lower_qpath( + e.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + )), + self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), + maybe_expr, + ) + } + ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), + ExprKind::Err => hir::ExprKind::Err, + ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr), + ExprKind::Paren(ref ex) => { + let mut ex = self.lower_expr_mut(ex); + // Include parens in span, but only if it is a super-span. + if e.span.contains(ex.span) { + ex.span = e.span; + } + // Merge attributes into the inner expression. + let mut attrs = e.attrs.clone(); + attrs.extend::>(ex.attrs.into()); + ex.attrs = attrs; + return ex; } - // Merge attributes into the inner expression. - let mut attrs = e.attrs.clone(); - attrs.extend::>(ex.attrs.into()); - ex.attrs = attrs; - return ex; - } - - ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), - ExprKind::Err => hir::ExprKind::Err, + // Desugar `ExprForLoop` + // from: `[opt_ident]: for in ` + ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { + return self.lower_expr_for(e, pat, head, body, opt_label); + } + ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), + }; - // Desugar `ExprForLoop` - // from: `[opt_ident]: for in ` - ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { - return self.lower_expr_for(e, pat, head, body, opt_label); + hir::Expr { + hir_id: self.lower_node_id(e.id), + kind, + span: e.span, + attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), } - ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr), - ExprKind::MacCall(_) => panic!("Shouldn't exist here"), - }; - - hir::Expr { - hir_id: self.lower_node_id(e.id), - kind, - span: e.span, - attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), - } + }) } fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { @@ -397,12 +417,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let then_arm = self.arm(then_pat, self.arena.alloc(then_expr)); // `match { ... }` - let match_expr = self.expr_match( - scrutinee.span, - scrutinee, - arena_vec![self; then_arm, else_arm], - desugar, - ); + let match_expr = + self.expr_match(span, scrutinee, arena_vec![self; then_arm, else_arm], desugar); // `[opt_ident]: loop { ... }` hir::ExprKind::Loop(self.block_expr(self.arena.alloc(match_expr)), opt_label, source) @@ -556,9 +572,9 @@ impl<'hir> LoweringContext<'_, 'hir> { /// ```rust /// match { /// mut pinned => loop { - /// match unsafe { ::std::future::poll_with_context( + /// match unsafe { ::std::future::Future::poll( /// <::std::pin::Pin>::new_unchecked(&mut pinned), - /// task_context, + /// ::std::future::get_context(task_context), /// ) } { /// ::std::task::Poll::Ready(result) => break result, /// ::std::task::Poll::Pending => {} @@ -590,6 +606,7 @@ impl<'hir> LoweringContext<'_, 'hir> { await_span, self.allow_gen_future.clone(), ); + let expr = self.lower_expr(expr); let pinned_ident = Ident::with_dummy_span(sym::pinned); let (pinned_pat, pinned_pat_hid) = @@ -598,9 +615,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let task_context_ident = Ident::with_dummy_span(sym::_task_context); // unsafe { - // ::std::future::poll_with_context( + // ::std::future::Future::poll( // ::std::pin::Pin::new_unchecked(&mut pinned), - // task_context, + // ::std::future::get_context(task_context), // ) // } let poll_expr = { @@ -621,10 +638,15 @@ impl<'hir> LoweringContext<'_, 'hir> { arena_vec![self; ref_mut_pinned], ); let new_unchecked = self.expr(span, new_unchecked_expr_kind, ThinVec::new()); - let call = self.expr_call_std_path( + let get_context = self.expr_call_std_path_mut( gen_future_span, - &[sym::future, sym::poll_with_context], - arena_vec![self; new_unchecked, task_context], + &[sym::future, sym::get_context], + arena_vec![self; task_context], + ); + let call = self.expr_call_std_path( + span, + &[sym::future, sym::Future, sym::poll], + arena_vec![self; new_unchecked, get_context], ); self.arena.alloc(self.expr_unsafe(call)) }; @@ -671,7 +693,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unit = self.expr_unit(span); let yield_expr = self.expr( span, - hir::ExprKind::Yield(unit, hir::YieldSource::Await), + hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr.hir_id) }), ThinVec::new(), ); let yield_expr = self.arena.alloc(yield_expr); @@ -704,7 +726,6 @@ impl<'hir> LoweringContext<'_, 'hir> { // match { // mut pinned => loop { .. } // } - let expr = self.lower_expr(expr); hir::ExprKind::Match(expr, arena_vec![self; pinned_arm], hir::MatchSource::AwaitDesugar) } @@ -952,13 +973,309 @@ impl<'hir> LoweringContext<'_, 'hir> { result } - fn lower_expr_asm(&mut self, asm: &InlineAsm) -> hir::ExprKind<'hir> { - let inner = hir::InlineAsmInner { + fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> { + let asm_arch = if let Some(asm_arch) = self.sess.asm_arch { + asm_arch + } else { + struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit(); + return hir::ExprKind::Err; + }; + if asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { + match asm_arch { + asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64 => {} + _ => self + .sess + .struct_span_err(sp, "the `att_syntax` option is only supported on x86") + .emit(), + } + } + + // Lower operands to HIR, filter_map skips any operands with invalid + // register classes. + let sess = self.sess; + let operands: Vec<_> = asm + .operands + .iter() + .filter_map(|(op, op_sp)| { + let lower_reg = |reg| { + Some(match reg { + InlineAsmRegOrRegClass::Reg(s) => asm::InlineAsmRegOrRegClass::Reg( + asm::InlineAsmReg::parse( + asm_arch, + |feature| { + self.sess.target_features.contains(&Symbol::intern(feature)) + }, + s, + ) + .map_err(|e| { + let msg = format!("invalid register `{}`: {}", s.as_str(), e); + sess.struct_span_err(*op_sp, &msg).emit(); + }) + .ok()?, + ), + InlineAsmRegOrRegClass::RegClass(s) => { + asm::InlineAsmRegOrRegClass::RegClass( + asm::InlineAsmRegClass::parse(asm_arch, s) + .map_err(|e| { + let msg = format!( + "invalid register class `{}`: {}", + s.as_str(), + e + ); + sess.struct_span_err(*op_sp, &msg).emit(); + }) + .ok()?, + ) + } + }) + }; + let op = match op { + InlineAsmOperand::In { reg, expr } => hir::InlineAsmOperand::In { + reg: lower_reg(*reg)?, + expr: self.lower_expr_mut(expr), + }, + InlineAsmOperand::Out { reg, late, expr } => hir::InlineAsmOperand::Out { + reg: lower_reg(*reg)?, + late: *late, + expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)), + }, + InlineAsmOperand::InOut { reg, late, expr } => hir::InlineAsmOperand::InOut { + reg: lower_reg(*reg)?, + late: *late, + expr: self.lower_expr_mut(expr), + }, + InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { + hir::InlineAsmOperand::SplitInOut { + reg: lower_reg(*reg)?, + late: *late, + in_expr: self.lower_expr_mut(in_expr), + out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)), + } + } + InlineAsmOperand::Const { expr } => { + hir::InlineAsmOperand::Const { expr: self.lower_expr_mut(expr) } + } + InlineAsmOperand::Sym { expr } => { + hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) } + } + }; + Some(op) + }) + .collect(); + + // Stop if there were any errors when lowering the register classes + if operands.len() != asm.operands.len() { + return hir::ExprKind::Err; + } + + // Validate template modifiers against the register classes for the operands + for p in &asm.template { + if let InlineAsmTemplatePiece::Placeholder { + operand_idx, + modifier: Some(modifier), + span: placeholder_span, + } = *p + { + let op_sp = asm.operands[operand_idx].1; + match &operands[operand_idx] { + hir::InlineAsmOperand::In { reg, .. } + | hir::InlineAsmOperand::Out { reg, .. } + | hir::InlineAsmOperand::InOut { reg, .. } + | hir::InlineAsmOperand::SplitInOut { reg, .. } => { + let class = reg.reg_class(); + let valid_modifiers = class.valid_modifiers(asm_arch); + if !valid_modifiers.contains(&modifier) { + let mut err = sess.struct_span_err( + placeholder_span, + "invalid asm template modifier for this register class", + ); + err.span_label(placeholder_span, "template modifier"); + err.span_label(op_sp, "argument"); + if !valid_modifiers.is_empty() { + let mut mods = format!("`{}`", valid_modifiers[0]); + for m in &valid_modifiers[1..] { + let _ = write!(mods, ", `{}`", m); + } + err.note(&format!( + "the `{}` register class supports \ + the following template modifiers: {}", + class.name(), + mods + )); + } else { + err.note(&format!( + "the `{}` register class does not support template modifiers", + class.name() + )); + } + err.emit(); + } + } + hir::InlineAsmOperand::Const { .. } => { + let mut err = sess.struct_span_err( + placeholder_span, + "asm template modifiers are not allowed for `const` arguments", + ); + err.span_label(placeholder_span, "template modifier"); + err.span_label(op_sp, "argument"); + err.emit(); + } + hir::InlineAsmOperand::Sym { .. } => { + let mut err = sess.struct_span_err( + placeholder_span, + "asm template modifiers are not allowed for `sym` arguments", + ); + err.span_label(placeholder_span, "template modifier"); + err.span_label(op_sp, "argument"); + err.emit(); + } + } + } + } + + let mut used_input_regs = FxHashMap::default(); + let mut used_output_regs = FxHashMap::default(); + for (idx, op) in operands.iter().enumerate() { + let op_sp = asm.operands[idx].1; + if let Some(reg) = op.reg() { + // Validate register classes against currently enabled target + // features. We check that at least one type is available for + // the current target. + let reg_class = reg.reg_class(); + let mut required_features = vec![]; + for &(_, feature) in reg_class.supported_types(asm_arch) { + if let Some(feature) = feature { + if self.sess.target_features.contains(&Symbol::intern(feature)) { + required_features.clear(); + break; + } else { + required_features.push(feature); + } + } else { + required_features.clear(); + break; + } + } + required_features.sort(); + required_features.dedup(); + match &required_features[..] { + [] => {} + [feature] => { + let msg = format!( + "register class `{}` requires the `{}` target feature", + reg_class.name(), + feature + ); + sess.struct_span_err(op_sp, &msg).emit(); + } + features => { + let msg = format!( + "register class `{}` requires at least one target feature: {}", + reg_class.name(), + features.join(", ") + ); + sess.struct_span_err(op_sp, &msg).emit(); + } + } + + // Check for conflicts between explicit register operands. + if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg { + let (input, output) = match op { + hir::InlineAsmOperand::In { .. } => (true, false), + // Late output do not conflict with inputs, but normal outputs do + hir::InlineAsmOperand::Out { late, .. } => (!late, true), + hir::InlineAsmOperand::InOut { .. } + | hir::InlineAsmOperand::SplitInOut { .. } => (true, true), + hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => { + unreachable!() + } + }; + + // Flag to output the error only once per operand + let mut skip = false; + reg.overlapping_regs(|r| { + let mut check = |used_regs: &mut FxHashMap, + input| { + match used_regs.entry(r) { + Entry::Occupied(o) => { + if !skip { + skip = true; + + let idx2 = *o.get(); + let op2 = &operands[idx2]; + let op_sp2 = asm.operands[idx2].1; + let reg2 = match op2.reg() { + Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r, + _ => unreachable!(), + }; + + let msg = format!( + "register `{}` conflicts with register `{}`", + reg.name(), + reg2.name() + ); + let mut err = sess.struct_span_err(op_sp, &msg); + err.span_label( + op_sp, + &format!("register `{}`", reg.name()), + ); + err.span_label( + op_sp2, + &format!("register `{}`", reg2.name()), + ); + + match (op, op2) { + ( + hir::InlineAsmOperand::In { .. }, + hir::InlineAsmOperand::Out { late, .. }, + ) + | ( + hir::InlineAsmOperand::Out { late, .. }, + hir::InlineAsmOperand::In { .. }, + ) => { + assert!(!*late); + let out_op_sp = if input { op_sp2 } else { op_sp }; + let msg = &format!( + "use `lateout` instead of \ + `out` to avoid conflict" + ); + err.span_help(out_op_sp, msg); + } + _ => {} + } + + err.emit(); + } + } + Entry::Vacant(v) => { + v.insert(idx); + } + } + }; + if input { + check(&mut used_input_regs, true); + } + if output { + check(&mut used_output_regs, false); + } + }); + } + } + } + + let operands = self.arena.alloc_from_iter(operands); + let template = self.arena.alloc_from_iter(asm.template.iter().cloned()); + let hir_asm = hir::InlineAsm { template, operands, options: asm.options }; + hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm)) + } + + fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> { + let inner = hir::LlvmInlineAsmInner { inputs: asm.inputs.iter().map(|&(c, _)| c).collect(), outputs: asm .outputs .iter() - .map(|out| hir::InlineAsmOutput { + .map(|out| hir::LlvmInlineAsmOutput { constraint: out.constraint, is_rw: out.is_rw, is_indirect: out.is_indirect, @@ -972,7 +1289,7 @@ impl<'hir> LoweringContext<'_, 'hir> { alignstack: asm.alignstack, dialect: asm.dialect, }; - let hir_asm = hir::InlineAsm { + let hir_asm = hir::LlvmInlineAsm { inner, inputs_exprs: self.arena.alloc_from_iter( asm.inputs.iter().map(|&(_, ref input)| self.lower_expr_mut(input)), @@ -981,7 +1298,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .arena .alloc_from_iter(asm.outputs.iter().map(|out| self.lower_expr_mut(&out.expr))), }; - hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm)) + hir::ExprKind::LlvmInlineAsm(self.arena.alloc(hir_asm)) } fn lower_field(&mut self, f: &Field) -> hir::Field<'hir> { @@ -1005,7 +1322,6 @@ impl<'hir> LoweringContext<'_, 'hir> { "`async` generators are not yet supported" ) .emit(); - return hir::ExprKind::Err; } None => self.generator_kind = Some(hir::GeneratorKind::Gen), } @@ -1326,25 +1642,43 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]), ThinVec::new())) } + fn expr_call_mut( + &mut self, + span: Span, + e: &'hir hir::Expr<'hir>, + args: &'hir [hir::Expr<'hir>], + ) -> hir::Expr<'hir> { + self.expr(span, hir::ExprKind::Call(e, args), ThinVec::new()) + } + fn expr_call( &mut self, span: Span, e: &'hir hir::Expr<'hir>, args: &'hir [hir::Expr<'hir>], ) -> &'hir hir::Expr<'hir> { - self.arena.alloc(self.expr(span, hir::ExprKind::Call(e, args), ThinVec::new())) + self.arena.alloc(self.expr_call_mut(span, e, args)) } // Note: associated functions must use `expr_call_std_path`. - fn expr_call_std_path( + fn expr_call_std_path_mut( &mut self, span: Span, path_components: &[Symbol], args: &'hir [hir::Expr<'hir>], - ) -> &'hir hir::Expr<'hir> { + ) -> hir::Expr<'hir> { let path = self.arena.alloc(self.expr_std_path(span, path_components, None, ThinVec::new())); - self.expr_call(span, path, args) + self.expr_call_mut(span, path, args) + } + + fn expr_call_std_path( + &mut self, + span: Span, + path_components: &[Symbol], + args: &'hir [hir::Expr<'hir>], + ) -> &'hir hir::Expr<'hir> { + self.arena.alloc(self.expr_call_std_path_mut(span, path_components, args)) } // Create an expression calling an associated function of an std type. diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index 740f62b41a584..eced17c9245f2 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -12,7 +12,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_span::source_map::{respan, DesugaringKind}; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; @@ -165,19 +165,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } ItemKind::MacroDef(..) => SmallVec::new(), ItemKind::Fn(..) | ItemKind::Impl { of_trait: None, .. } => smallvec![i.id], - ItemKind::Static(ref ty, ..) => { + ItemKind::Static(ref ty, ..) | ItemKind::Const(_, ref ty, ..) => { let mut ids = smallvec![i.id]; if self.sess.features_untracked().impl_trait_in_bindings { - let mut visitor = ImplTraitTypeIdVisitor { ids: &mut ids }; - visitor.visit_ty(ty); - } - ids - } - ItemKind::Const(_, ref ty, ..) => { - let mut ids = smallvec![i.id]; - if self.sess.features_untracked().impl_trait_in_bindings { - let mut visitor = ImplTraitTypeIdVisitor { ids: &mut ids }; - visitor.visit_ty(ty); + ImplTraitTypeIdVisitor { ids: &mut ids }.visit_ty(ty); } ids } @@ -268,7 +259,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ItemKind::Const(ty, body_id) } ItemKind::Fn(_, FnSig { ref decl, header }, ref generics, ref body) => { - let fn_def_id = self.resolver.definitions().local_def_id(id).expect_local(); + let fn_def_id = self.resolver.definitions().local_def_id(id); self.with_new_scopes(|this| { this.current_item = Some(ident.span); @@ -355,7 +346,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self_ty: ref ty, items: ref impl_items, } => { - let def_id = self.resolver.definitions().local_def_id(id).expect_local(); + let def_id = self.resolver.definitions().local_def_id(id); // Lower the "impl header" first. This ordering is important // for in-band lifetimes! Consider `'a` here: @@ -654,7 +645,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem<'hir> { - let def_id = self.resolver.definitions().local_def_id(i.id).expect_local(); + let def_id = self.resolver.definitions().local_def_id(i.id); hir::ForeignItem { hir_id: self.lower_node_id(i.id), ident: i.ident, @@ -755,7 +746,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_trait_item(&mut self, i: &AssocItem) -> hir::TraitItem<'hir> { - let trait_item_def_id = self.resolver.definitions().local_def_id(i.id).expect_local(); + let trait_item_def_id = self.resolver.definitions().local_def_id(i.id); let (generics, kind) = match i.kind { AssocItemKind::Const(_, ref ty, ref default) => { @@ -805,7 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (hir::AssocItemKind::Type, default.is_some()) } AssocItemKind::Fn(_, sig, _, default) => { - (hir::AssocItemKind::Method { has_self: sig.decl.has_self() }, default.is_some()) + (hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }, default.is_some()) } AssocItemKind::MacCall(..) => unimplemented!(), }; @@ -820,7 +811,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_impl_item(&mut self, i: &AssocItem) -> hir::ImplItem<'hir> { - let impl_item_def_id = self.resolver.definitions().local_def_id(i.id).expect_local(); + let impl_item_def_id = self.resolver.definitions().local_def_id(i.id); let (generics, kind) = match &i.kind { AssocItemKind::Const(_, ty, expr) => { @@ -903,7 +894,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } AssocItemKind::Fn(_, sig, ..) => { - hir::AssocItemKind::Method { has_self: sig.decl.has_self() } + hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } } AssocItemKind::MacCall(..) => unimplemented!(), }, @@ -972,8 +963,10 @@ impl<'hir> LoweringContext<'_, 'hir> { f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>), ) -> hir::BodyId { let prev_gen_kind = self.generator_kind.take(); + let task_context = self.task_context.take(); let (parameters, result) = f(self); let body_id = self.record_body(parameters, result); + self.task_context = task_context; self.generator_kind = prev_gen_kind; body_id } @@ -1332,17 +1325,14 @@ impl<'hir> LoweringContext<'_, 'hir> { self.resolver.definitions().as_local_node_id(def_id) { for param in &generics.params { - match param.kind { - GenericParamKind::Type { .. } => { - if node_id == param.id { - add_bounds - .entry(param.id) - .or_default() - .push(bound.clone()); - continue 'next_bound; - } + if let GenericParamKind::Type { .. } = param.kind { + if node_id == param.id { + add_bounds + .entry(param.id) + .or_default() + .push(bound.clone()); + continue 'next_bound; } - _ => {} } } } diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs index 68cb1f8585fc7..2cf81af04166c 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -33,7 +33,8 @@ #![feature(array_value_iter)] #![feature(crate_visibility_modifier)] #![feature(marker_trait_attr)] -#![feature(specialization)] +#![feature(specialization)] // FIXME: min_specialization does not work +#![feature(or_patterns)] #![recursion_limit = "256"] use rustc_ast::ast; @@ -62,7 +63,7 @@ use rustc_session::parse::ParseSess; use rustc_session::Session; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind, ExpnData, ExpnKind}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use log::{debug, trace}; @@ -96,7 +97,7 @@ struct LoweringContext<'a, 'hir: 'a> { /// HACK(Centril): there is a cyclic dependency between the parser and lowering /// if we don't have this function pointer. To avoid that dependency so that - /// librustc is independent of the parser, we use dynamic dispatch here. + /// librustc_middle is independent of the parser, we use dynamic dispatch here. nt_to_tokenstream: NtToTokenstream, /// Used to allocate HIR nodes @@ -167,7 +168,7 @@ struct LoweringContext<'a, 'hir: 'a> { current_hir_id_owner: Vec<(LocalDefId, u32)>, item_local_id_counters: NodeMap, - node_id_to_hir_id: IndexVec, + node_id_to_hir_id: IndexVec>, allow_try_trait: Option>, allow_gen_future: Option>, @@ -463,8 +464,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | ItemKind::Enum(_, ref generics) | ItemKind::TyAlias(_, ref generics, ..) | ItemKind::Trait(_, _, ref generics, ..) => { - let def_id = - self.lctx.resolver.definitions().local_def_id(item.id).expect_local(); + let def_id = self.lctx.resolver.definitions().local_def_id(item.id); let count = generics .params .iter() @@ -522,7 +522,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } self.lower_node_id(CRATE_NODE_ID); - debug_assert!(self.node_id_to_hir_id[CRATE_NODE_ID] == hir::CRATE_HIR_ID); + debug_assert!(self.node_id_to_hir_id[CRATE_NODE_ID] == Some(hir::CRATE_HIR_ID)); visit::walk_crate(&mut MiscCollector { lctx: &mut self, hir_id_owner: None }, c); visit::walk_crate(&mut item::ItemLowerer { lctx: &mut self }, c); @@ -530,7 +530,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let module = self.lower_mod(&c.module); let attrs = self.lower_attrs(&c.attrs); let body_ids = body_ids(&self.bodies); - let proc_macros = c.proc_macros.iter().map(|id| self.node_id_to_hir_id[*id]).collect(); + let proc_macros = + c.proc_macros.iter().map(|id| self.node_id_to_hir_id[*id].unwrap()).collect(); self.resolver.definitions().init_node_id_to_hir_id_mapping(self.node_id_to_hir_id); @@ -571,26 +572,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ast_node_id: NodeId, alloc_hir_id: impl FnOnce(&mut Self) -> hir::HirId, ) -> hir::HirId { - if ast_node_id == DUMMY_NODE_ID { - return hir::DUMMY_HIR_ID; - } + assert_ne!(ast_node_id, DUMMY_NODE_ID); let min_size = ast_node_id.as_usize() + 1; if min_size > self.node_id_to_hir_id.len() { - self.node_id_to_hir_id.resize(min_size, hir::DUMMY_HIR_ID); + self.node_id_to_hir_id.resize(min_size, None); } - let existing_hir_id = self.node_id_to_hir_id[ast_node_id]; - - if existing_hir_id == hir::DUMMY_HIR_ID { + if let Some(existing_hir_id) = self.node_id_to_hir_id[ast_node_id] { + existing_hir_id + } else { // Generate a new `HirId`. let hir_id = alloc_hir_id(self); - self.node_id_to_hir_id[ast_node_id] = hir_id; + self.node_id_to_hir_id[ast_node_id] = Some(hir_id); hir_id - } else { - existing_hir_id } } @@ -599,7 +596,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .item_local_id_counters .insert(owner, HIR_ID_COUNTER_LOCKED) .unwrap_or_else(|| panic!("no `item_local_id_counters` entry for {:?}", owner)); - let def_id = self.resolver.definitions().local_def_id(owner).expect_local(); + let def_id = self.resolver.definitions().local_def_id(owner); self.current_hir_id_owner.push((def_id, counter)); let ret = f(self); let (new_def_id, new_counter) = self.current_hir_id_owner.pop().unwrap(); @@ -1242,16 +1239,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bounds = this.arena.alloc_from_iter(bounds.iter().filter_map( |bound| match *bound { - GenericBound::Trait(ref ty, TraitBoundModifier::None) - | GenericBound::Trait(ref ty, TraitBoundModifier::MaybeConst) => { - Some(this.lower_poly_trait_ref(ty, itctx.reborrow())) - } + GenericBound::Trait( + ref ty, + TraitBoundModifier::None | TraitBoundModifier::MaybeConst, + ) => Some(this.lower_poly_trait_ref(ty, itctx.reborrow())), // `?const ?Bound` will cause an error during AST validation // anyways, so treat it like `?Bound` as compilation proceeds. - GenericBound::Trait(_, TraitBoundModifier::Maybe) - | GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => { - None - } + GenericBound::Trait( + _, + TraitBoundModifier::Maybe | TraitBoundModifier::MaybeConstMaybe, + ) => None, GenericBound::Outlives(ref lifetime) => { if lifetime_bound.is_none() { lifetime_bound = Some(this.lower_lifetime(lifetime)); @@ -1279,8 +1276,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } ImplTraitContext::Universal(in_band_ty_params) => { // Add a definition for the in-band `Param`. - let def_id = - self.resolver.definitions().local_def_id(def_node_id).expect_local(); + let def_id = self.resolver.definitions().local_def_id(def_node_id); let hir_bounds = self.lower_param_bounds( bounds, @@ -1368,8 +1364,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // frequently opened issues show. let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); - let opaque_ty_def_id = - self.resolver.definitions().local_def_id(opaque_ty_node_id).expect_local(); + let opaque_ty_def_id = self.resolver.definitions().local_def_id(opaque_ty_node_id); self.allocate_hir_id_counter(opaque_ty_node_id); @@ -1745,8 +1740,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { c_variadic, implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| { let is_mutable_pat = match arg.pat.kind { - PatKind::Ident(BindingMode::ByValue(mt), _, _) - | PatKind::Ident(BindingMode::ByRef(mt), _, _) => mt == Mutability::Mut, + PatKind::Ident(BindingMode::ByValue(mt) | BindingMode::ByRef(mt), _, _) => { + mt == Mutability::Mut + } _ => false, }; @@ -1798,8 +1794,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); - let opaque_ty_def_id = - self.resolver.definitions().local_def_id(opaque_ty_node_id).expect_local(); + let opaque_ty_def_id = self.resolver.definitions().local_def_id(opaque_ty_node_id); self.allocate_hir_id_counter(opaque_ty_node_id); @@ -2474,7 +2469,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::QPath::Resolved(None, path) => { // Turn trait object paths into `TyKind::TraitObject` instead. match path.res { - Res::Def(DefKind::Trait, _) | Res::Def(DefKind::TraitAlias, _) => { + Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => { let principal = hir::PolyTraitRef { bound_generic_params: &[], trait_ref: hir::TraitRef { path, hir_ref_id: hir_id }, diff --git a/src/librustc_ast_lowering/pat.rs b/src/librustc_ast_lowering/pat.rs index 8ba6576f69265..496e401d06124 100644 --- a/src/librustc_ast_lowering/pat.rs +++ b/src/librustc_ast_lowering/pat.rs @@ -2,83 +2,90 @@ use super::{ImplTraitContext, LoweringContext, ParamMode}; use rustc_ast::ast::*; use rustc_ast::ptr::P; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_span::symbol::Ident; use rustc_span::{source_map::Spanned, Span}; impl<'a, 'hir> LoweringContext<'a, 'hir> { crate fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> { - let node = match p.kind { - PatKind::Wild => hir::PatKind::Wild, - PatKind::Ident(ref binding_mode, ident, ref sub) => { - let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s)); - let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub); - node - } - PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)), - PatKind::TupleStruct(ref path, ref pats) => { - let qpath = self.lower_qpath( - p.id, - &None, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); - let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct"); - hir::PatKind::TupleStruct(qpath, pats, ddpos) - } - PatKind::Or(ref pats) => { - hir::PatKind::Or(self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x)))) - } - PatKind::Path(ref qself, ref path) => { - let qpath = self.lower_qpath( - p.id, - qself, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); - hir::PatKind::Path(qpath) - } - PatKind::Struct(ref path, ref fields, etc) => { - let qpath = self.lower_qpath( - p.id, - &None, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); + ensure_sufficient_stack(|| { + let node = match p.kind { + PatKind::Wild => hir::PatKind::Wild, + PatKind::Ident(ref binding_mode, ident, ref sub) => { + let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s)); + let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub); + node + } + PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)), + PatKind::TupleStruct(ref path, ref pats) => { + let qpath = self.lower_qpath( + p.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct"); + hir::PatKind::TupleStruct(qpath, pats, ddpos) + } + PatKind::Or(ref pats) => hir::PatKind::Or( + self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))), + ), + PatKind::Path(ref qself, ref path) => { + let qpath = self.lower_qpath( + p.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + hir::PatKind::Path(qpath) + } + PatKind::Struct(ref path, ref fields, etc) => { + let qpath = self.lower_qpath( + p.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); - let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat { - hir_id: self.next_id(), - ident: f.ident, - pat: self.lower_pat(&f.pat), - is_shorthand: f.is_shorthand, - span: f.span, - })); - hir::PatKind::Struct(qpath, fs, etc) - } - PatKind::Tuple(ref pats) => { - let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple"); - hir::PatKind::Tuple(pats, ddpos) - } - PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)), - PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl), - PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => hir::PatKind::Range( - e1.as_deref().map(|e| self.lower_expr(e)), - e2.as_deref().map(|e| self.lower_expr(e)), - self.lower_range_end(end, e2.is_some()), - ), - PatKind::Slice(ref pats) => self.lower_pat_slice(pats), - PatKind::Rest => { - // If we reach here the `..` pattern is not semantically allowed. - self.ban_illegal_rest_pat(p.span) - } - PatKind::Paren(ref inner) => return self.lower_pat(inner), - PatKind::MacCall(_) => panic!("Shouldn't exist here"), - }; + let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat { + hir_id: self.next_id(), + ident: f.ident, + pat: self.lower_pat(&f.pat), + is_shorthand: f.is_shorthand, + span: f.span, + })); + hir::PatKind::Struct(qpath, fs, etc) + } + PatKind::Tuple(ref pats) => { + let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple"); + hir::PatKind::Tuple(pats, ddpos) + } + PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)), + PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl), + PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => { + hir::PatKind::Range( + e1.as_deref().map(|e| self.lower_expr(e)), + e2.as_deref().map(|e| self.lower_expr(e)), + self.lower_range_end(end, e2.is_some()), + ) + } + PatKind::Slice(ref pats) => self.lower_pat_slice(pats), + PatKind::Rest => { + // If we reach here the `..` pattern is not semantically allowed. + self.ban_illegal_rest_pat(p.span) + } + // FIXME: consider not using recursion to lower this. + PatKind::Paren(ref inner) => return self.lower_pat(inner), + PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", p.span), + }; - self.pat_with_node_id_of(p, node) + self.pat_with_node_id_of(p, node) + }) } fn lower_pat_tuple( @@ -194,7 +201,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::PatKind<'hir> { match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) { // `None` can occur in body-less function signatures - res @ None | res @ Some(Res::Local(_)) => { + res @ (None | Some(Res::Local(_))) => { let canonical_id = match res { Some(Res::Local(id)) => id, _ => p.id, diff --git a/src/librustc_ast_lowering/path.rs b/src/librustc_ast_lowering/path.rs index e996bdac7cb37..e5ce51f8d2d1f 100644 --- a/src/librustc_ast_lowering/path.rs +++ b/src/librustc_ast_lowering/path.rs @@ -9,6 +9,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; use rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS; use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_span::symbol::Ident; use rustc_span::Span; use log::debug; @@ -273,7 +274,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .next(); if !generic_args.parenthesized && !has_lifetimes { generic_args.args = self - .elided_path_lifetimes(path_span, expected_lifetimes) + .elided_path_lifetimes( + first_generic_span.map(|s| s.shrink_to_lo()).unwrap_or(segment.ident.span), + expected_lifetimes, + ) .map(GenericArg::Lifetime) .chain(generic_args.args.into_iter()) .collect(); @@ -366,22 +370,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_mode: ParamMode, mut itctx: ImplTraitContext<'_, 'hir>, ) -> (GenericArgsCtor<'hir>, bool) { - let &AngleBracketedArgs { ref args, ref constraints, .. } = data; - let has_non_lt_args = args.iter().any(|arg| match arg { - ast::GenericArg::Lifetime(_) => false, - ast::GenericArg::Type(_) => true, - ast::GenericArg::Const(_) => true, + let has_non_lt_args = data.args.iter().any(|arg| match arg { + AngleBracketedArg::Arg(ast::GenericArg::Lifetime(_)) + | AngleBracketedArg::Constraint(_) => false, + AngleBracketedArg::Arg(ast::GenericArg::Type(_) | ast::GenericArg::Const(_)) => true, }); - ( - GenericArgsCtor { - args: args.iter().map(|a| self.lower_generic_arg(a, itctx.reborrow())).collect(), - bindings: self.arena.alloc_from_iter( - constraints.iter().map(|b| self.lower_assoc_ty_constraint(b, itctx.reborrow())), - ), - parenthesized: false, - }, - !has_non_lt_args && param_mode == ParamMode::Optional, - ) + let args = data + .args + .iter() + .filter_map(|arg| match arg { + AngleBracketedArg::Arg(arg) => Some(self.lower_generic_arg(arg, itctx.reborrow())), + AngleBracketedArg::Constraint(_) => None, + }) + .collect(); + let bindings = self.arena.alloc_from_iter(data.args.iter().filter_map(|arg| match arg { + AngleBracketedArg::Constraint(c) => { + Some(self.lower_assoc_ty_constraint(c, itctx.reborrow())) + } + AngleBracketedArg::Arg(_) => None, + })); + let ctor = GenericArgsCtor { args, bindings, parenthesized: false }; + (ctor, !has_non_lt_args && param_mode == ParamMode::Optional) } fn lower_parenthesized_parameter_data( diff --git a/src/librustc_ast_passes/Cargo.toml b/src/librustc_ast_passes/Cargo.toml index 5d096e4965ddc..e4d1d79abb2d6 100644 --- a/src/librustc_ast_passes/Cargo.toml +++ b/src/librustc_ast_passes/Cargo.toml @@ -9,6 +9,7 @@ name = "rustc_ast_passes" path = "lib.rs" [dependencies] +itertools = "0.8" log = "0.4" rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index b4363778094b4..8eb125e444053 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -6,6 +6,7 @@ // This pass is supposed to perform only simple checks not requiring name resolution // or type checking or some other kind of complex analysis. +use itertools::{Either, Itertools}; use rustc_ast::ast::*; use rustc_ast::attr; use rustc_ast::expand::is_proc_macro_attr; @@ -14,14 +15,15 @@ use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::walk_list; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{error_code, struct_span_err, Applicability}; +use rustc_errors::{error_code, pluralize, struct_span_err, Applicability}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY; use rustc_session::lint::LintBuffer; use rustc_session::Session; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use std::mem; +use std::ops::DerefMut; const MORE_EXTERN: &str = "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html"; @@ -289,11 +291,7 @@ impl<'a> AstValidator<'a> { match expr.kind { ExprKind::Lit(..) | ExprKind::Err => {} ExprKind::Path(..) if allow_paths => {} - ExprKind::Unary(UnOp::Neg, ref inner) - if match inner.kind { - ExprKind::Lit(_) => true, - _ => false, - } => {} + ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} _ => self.err_handler().span_err( expr.span, "arbitrary expressions aren't allowed \ @@ -402,7 +400,7 @@ impl<'a> AstValidator<'a> { fn check_defaultness(&self, span: Span, defaultness: Defaultness) { if let Defaultness::Default(def_span) = defaultness { - let span = self.session.source_map().def_span(span); + let span = self.session.source_map().guess_head_span(span); self.err_handler() .struct_span_err(span, "`default` is only allowed on items in `impl` definitions") .span_label(def_span, "`default` because of this") @@ -517,7 +515,7 @@ impl<'a> AstValidator<'a> { } fn current_extern_span(&self) -> Span { - self.session.source_map().def_span(self.extern_mod.unwrap().span) + self.session.source_map().guess_head_span(self.extern_mod.unwrap().span) } /// An `fn` in `extern { ... }` cannot have qualfiers, e.g. `async fn`. @@ -564,28 +562,6 @@ impl<'a> AstValidator<'a> { } } - /// We currently do not permit const generics in `const fn`, - /// as this is tantamount to allowing compile-time dependent typing. - /// - /// FIXME(const_generics): Is this really true / necessary? Discuss with @varkor. - /// At any rate, the restriction feels too syntactic. Consider moving it to e.g. typeck. - fn check_const_fn_const_generic(&self, span: Span, sig: &FnSig, generics: &Generics) { - if let Const::Yes(const_span) = sig.header.constness { - // Look for const generics and error if we find any. - for param in &generics.params { - if let GenericParamKind::Const { .. } = param.kind { - self.err_handler() - .struct_span_err( - span, - "const parameters are not permitted in const functions", - ) - .span_label(const_span, "`const` because of this") - .emit(); - } - } - } - } - fn check_item_named(&self, ident: Ident, kind: &str) { if ident.name != kw::Underscore { return; @@ -596,6 +572,35 @@ impl<'a> AstValidator<'a> { .emit(); } + fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) { + if ident.name.as_str().is_ascii() { + return; + } + let head_span = self.session.source_map().guess_head_span(item_span); + struct_span_err!( + self.session, + head_span, + E0754, + "`#[no_mangle]` requires ASCII identifier" + ) + .emit(); + } + + fn check_mod_file_item_asciionly(&self, ident: Ident) { + if ident.name.as_str().is_ascii() { + return; + } + struct_span_err!( + self.session, + ident.span, + E0754, + "trying to load file for module `{}` with non ascii identifer name", + ident.name + ) + .help("consider using `#[path]` attribute to specify filesystem path") + .emit(); + } + fn deny_generic_params(&self, generics: &Generics, ident_span: Span) { if !generics.params.is_empty() { struct_span_err!( @@ -643,6 +648,73 @@ impl<'a> AstValidator<'a> { .emit(); } } + + fn correct_generic_order_suggestion(&self, data: &AngleBracketedArgs) -> String { + // Lifetimes always come first. + let lt_sugg = data.args.iter().filter_map(|arg| match arg { + AngleBracketedArg::Arg(lt @ GenericArg::Lifetime(_)) => { + Some(pprust::to_string(|s| s.print_generic_arg(lt))) + } + _ => None, + }); + let args_sugg = data.args.iter().filter_map(|a| match a { + AngleBracketedArg::Arg(GenericArg::Lifetime(_)) | AngleBracketedArg::Constraint(_) => { + None + } + AngleBracketedArg::Arg(arg) => Some(pprust::to_string(|s| s.print_generic_arg(arg))), + }); + // Constraints always come last. + let constraint_sugg = data.args.iter().filter_map(|a| match a { + AngleBracketedArg::Arg(_) => None, + AngleBracketedArg::Constraint(c) => { + Some(pprust::to_string(|s| s.print_assoc_constraint(c))) + } + }); + format!( + "<{}>", + lt_sugg.chain(args_sugg).chain(constraint_sugg).collect::>().join(", ") + ) + } + + /// Enforce generic args coming before constraints in `<...>` of a path segment. + fn check_generic_args_before_constraints(&self, data: &AngleBracketedArgs) { + // Early exit in case it's partitioned as it should be. + if data.args.iter().is_partitioned(|arg| matches!(arg, AngleBracketedArg::Arg(_))) { + return; + } + // Find all generic argument coming after the first constraint... + let (constraint_spans, arg_spans): (Vec, Vec) = + data.args.iter().partition_map(|arg| match arg { + AngleBracketedArg::Constraint(c) => Either::Left(c.span), + AngleBracketedArg::Arg(a) => Either::Right(a.span()), + }); + let args_len = arg_spans.len(); + let constraint_len = constraint_spans.len(); + // ...and then error: + self.err_handler() + .struct_span_err( + arg_spans.clone(), + "generic arguments must come before the first constraint", + ) + .span_label(constraint_spans[0], &format!("constraint{}", pluralize!(constraint_len))) + .span_label( + *arg_spans.iter().last().unwrap(), + &format!("generic argument{}", pluralize!(args_len)), + ) + .span_labels(constraint_spans, "") + .span_labels(arg_spans, "") + .span_suggestion_verbose( + data.span, + &format!( + "move the constraint{} after the generic argument{}", + pluralize!(constraint_len), + pluralize!(args_len) + ), + self.correct_generic_order_suggestion(&data), + Applicability::MachineApplicable, + ) + .emit(); + } } /// Checks that generic parameters are in the correct order, @@ -720,12 +792,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_expr(&mut self, expr: &'a Expr) { match &expr.kind { - ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => { + ExprKind::LlvmInlineAsm(..) if !self.session.target.target.options.allow_asm => { struct_span_err!( self.session, expr.span, E0472, - "asm! is unsupported on this target" + "llvm_asm! is unsupported on this target" ) .emit(); } @@ -823,6 +895,10 @@ 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); + } + match item.kind { ItemKind::Impl { unsafety, @@ -902,9 +978,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } } - ItemKind::Fn(def, ref sig, ref generics, ref body) => { + ItemKind::Fn(def, _, _, ref body) => { self.check_defaultness(item.span, def); - self.check_const_fn_const_generic(item.span, sig, generics); if body.is_none() { let msg = "free function without a body"; @@ -950,9 +1025,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; } - ItemKind::Mod(_) => { + ItemKind::Mod(Mod { inline, .. }) => { // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584). - attr::first_attr_value_str_by_name(&item.attrs, sym::path); + if !inline && !attr::contains_name(&item.attrs, sym::path) { + self.check_mod_file_item_asciionly(item.ident); + } } ItemKind::Union(ref vdata, _) => { if let VariantData::Tuple(..) | VariantData::Unit(..) = vdata { @@ -1012,17 +1089,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) { match *generic_args { GenericArgs::AngleBracketed(ref data) => { - walk_list!(self, visit_generic_arg, &data.args); - - // Type bindings such as `Item = impl Debug` in `Iterator` - // are allowed to contain nested `impl Trait`. - self.with_impl_trait(None, |this| { - walk_list!( - this, - visit_assoc_ty_constraint_from_generic_args, - &data.constraints - ); - }); + self.check_generic_args_before_constraints(data); + + for arg in &data.args { + match arg { + AngleBracketedArg::Arg(arg) => self.visit_generic_arg(arg), + // Type bindings such as `Item = impl Debug` in `Iterator` + // are allowed to contain nested `impl Trait`. + AngleBracketedArg::Constraint(constraint) => { + self.with_impl_trait(None, |this| { + this.visit_assoc_ty_constraint_from_generic_args(constraint); + }); + } + } + } } GenericArgs::Parenthesized(ref data) => { walk_list!(self, visit_ty, &data.inputs); @@ -1069,17 +1149,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { for predicate in &generics.where_clause.predicates { if let WherePredicate::EqPredicate(ref predicate) = *predicate { - self.err_handler() - .struct_span_err( - predicate.span, - "equality constraints are not yet supported in `where` clauses", - ) - .span_label(predicate.span, "not supported") - .note( - "see issue #20041 \ - for more information", - ) - .emit(); + deny_equality_constraints(self, predicate, generics); } } @@ -1256,6 +1326,89 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } +/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems +/// like it's setting an associated type, provide an appropriate suggestion. +fn deny_equality_constraints( + this: &mut AstValidator<'_>, + predicate: &WhereEqPredicate, + generics: &Generics, +) { + let mut err = this.err_handler().struct_span_err( + predicate.span, + "equality constraints are not yet supported in `where` clauses", + ); + err.span_label(predicate.span, "not supported"); + + // Given `::Bar = RhsTy`, suggest `A: Foo`. + if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind { + if let TyKind::Path(None, path) = &qself.ty.kind { + match &path.segments[..] { + [PathSegment { ident, args: None, .. }] => { + for param in &generics.params { + if param.ident == *ident { + let param = ident; + match &full_path.segments[qself.position..] { + [PathSegment { ident, .. }] => { + // Make a new `Path` from `foo::Bar` to `Foo`. + let mut assoc_path = full_path.clone(); + // Remove `Bar` from `Foo::Bar`. + assoc_path.segments.pop(); + let len = assoc_path.segments.len() - 1; + // Build ``. + let arg = AngleBracketedArg::Constraint(AssocTyConstraint { + id: rustc_ast::node_id::DUMMY_NODE_ID, + ident: *ident, + kind: AssocTyConstraintKind::Equality { + ty: predicate.rhs_ty.clone(), + }, + span: ident.span, + }); + // Add `` to `Foo`. + match &mut assoc_path.segments[len].args { + Some(args) => match args.deref_mut() { + GenericArgs::Parenthesized(_) => continue, + GenericArgs::AngleBracketed(args) => { + args.args.push(arg); + } + }, + empty_args => { + *empty_args = AngleBracketedArgs { + span: ident.span, + args: vec![arg], + } + .into(); + } + } + err.span_suggestion_verbose( + predicate.span, + &format!( + "if `{}` is an associated type you're trying to set, \ + use the associated type binding syntax", + ident + ), + format!( + "{}: {}", + param, + pprust::path_to_string(&assoc_path) + ), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }; + } + } + } + _ => {} + } + } + } + err.note( + "see issue #20041 for more information", + ); + err.emit(); +} + pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool { let mut validator = AstValidator { session, diff --git a/src/librustc_ast_passes/feature_gate.rs b/src/librustc_ast_passes/feature_gate.rs index 364c86bd99b4e..ffd741a7b37b0 100644 --- a/src/librustc_ast_passes/feature_gate.rs +++ b/src/librustc_ast_passes/feature_gate.rs @@ -7,7 +7,7 @@ use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP}; use rustc_feature::{Features, GateIssue, UnstableFeatures}; use rustc_session::parse::{feature_err, feature_err_issue, ParseSess}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use log::debug; @@ -252,12 +252,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - fn visit_name(&mut self, sp: Span, name: ast::Name) { + fn visit_name(&mut self, sp: Span, name: Symbol) { if !name.as_str().is_ascii() { gate_feature_post!( &self, non_ascii_idents, - self.parse_sess.source_map().def_span(sp), + self.parse_sess.source_map().guess_head_span(sp), "non-ascii idents are not fully supported" ); } @@ -286,8 +286,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { start, i.span, "`#[start]` functions are experimental \ - and their signature may change \ - over time" + and their signature may change \ + over time" ); } if attr::contains_name(&i.attrs[..], sym::main) { @@ -296,8 +296,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { main, i.span, "declaration of a non-standard `#[main]` \ - function may change over time, for now \ - a top-level `fn main()` is required" + function may change over time, for now \ + a top-level `fn main()` is required" ); } } @@ -341,7 +341,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { if let ast::ImplPolarity::Negative(span) = polarity { gate_feature_post!( &self, - optin_builtin_traits, + negative_impls, span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(span)), "negative trait bounds are not yet fully implemented; \ use marker types for now" @@ -516,27 +516,25 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } fn visit_generic_param(&mut self, param: &'a GenericParam) { - match param.kind { - GenericParamKind::Const { .. } => gate_feature_post!( + if let GenericParamKind::Const { .. } = param.kind { + gate_feature_post!( &self, const_generics, param.ident.span, "const generics are unstable" - ), - _ => {} + ) } visit::walk_generic_param(self, param) } fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) { - match constraint.kind { - AssocTyConstraintKind::Bound { .. } => gate_feature_post!( + if let AssocTyConstraintKind::Bound { .. } = constraint.kind { + gate_feature_post!( &self, associated_type_bounds, constraint.span, "associated type bounds are unstable" - ), - _ => {} + ) } visit::walk_assoc_ty_constraint(self, constraint) } diff --git a/src/librustc_ast_passes/lib.rs b/src/librustc_ast_passes/lib.rs index 10081d36754ba..bfe304419801d 100644 --- a/src/librustc_ast_passes/lib.rs +++ b/src/librustc_ast_passes/lib.rs @@ -1,10 +1,12 @@ -#![feature(bindings_after_at)] //! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax` //! parsed by `rustc_parse` and then lowered, after the passes in this crate, //! by `rustc_ast_lowering`. //! //! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. +#![feature(bindings_after_at)] +#![feature(iter_is_partitioned)] + pub mod ast_validation; pub mod feature_gate; pub mod node_count; diff --git a/src/librustc_ast_passes/node_count.rs b/src/librustc_ast_passes/node_count.rs index 534d6c7b1ea70..34db59b1b458d 100644 --- a/src/librustc_ast_passes/node_count.rs +++ b/src/librustc_ast_passes/node_count.rs @@ -1,7 +1,8 @@ -// Simply gives a rought count of the number of nodes in an AST. +// Simply gives a rough count of the number of nodes in an AST. use rustc_ast::ast::*; use rustc_ast::visit::*; +use rustc_span::symbol::Ident; use rustc_span::Span; pub struct NodeCounter { diff --git a/src/librustc_ast_pretty/Cargo.toml b/src/librustc_ast_pretty/Cargo.toml index 82be095db8805..6c076d2c5b866 100644 --- a/src/librustc_ast_pretty/Cargo.toml +++ b/src/librustc_ast_pretty/Cargo.toml @@ -12,5 +12,5 @@ doctest = false [dependencies] log = "0.4" rustc_span = { path = "../librustc_span" } -rustc_data_structures = { path = "../librustc_data_structures" } rustc_ast = { path = "../librustc_ast" } +rustc_target = { path = "../librustc_target" } diff --git a/src/librustc_ast_pretty/lib.rs b/src/librustc_ast_pretty/lib.rs index bde5f4bb03d0d..9adc6c604e8ab 100644 --- a/src/librustc_ast_pretty/lib.rs +++ b/src/librustc_ast_pretty/lib.rs @@ -1,5 +1,6 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(or_patterns)] #![recursion_limit = "256"] mod helpers; diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs index bb73e982a9c28..872126646f33d 100644 --- a/src/librustc_ast_pretty/pprust.rs +++ b/src/librustc_ast_pretty/pprust.rs @@ -4,6 +4,8 @@ use crate::pp::{self, Breaks}; use rustc_ast::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax}; use rustc_ast::ast::{Attribute, GenericArg, MacArgs}; use rustc_ast::ast::{GenericBound, SelfKind, TraitBoundModifier}; +use rustc_ast::ast::{InlineAsmOperand, InlineAsmRegOrRegClass}; +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_ast::attr; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, DelimToken, Nonterminal, Token, TokenKind}; @@ -12,7 +14,7 @@ use rustc_ast::util::parser::{self, AssocOp, Fixity}; use rustc_ast::util::{classify, comments}; use rustc_span::edition::Edition; use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::symbol::{kw, sym, IdentPrinter}; +use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol}; use rustc_span::{BytePos, FileName, Span}; use std::borrow::Cow; @@ -26,8 +28,8 @@ pub enum MacHeader<'a> { } pub enum AnnNode<'a> { - Ident(&'a ast::Ident), - Name(&'a ast::Name), + Ident(&'a Ident), + Name(&'a Symbol), Block(&'a ast::Block), Item(&'a ast::Item), SubItem(ast::NodeId), @@ -118,8 +120,8 @@ pub fn print_crate<'a>( // of the feature gate, so we fake them up here. // `#![feature(prelude_import)]` - let pi_nested = attr::mk_nested_word_item(ast::Ident::with_dummy_span(sym::prelude_import)); - let list = attr::mk_list_item(ast::Ident::with_dummy_span(sym::feature), vec![pi_nested]); + let pi_nested = attr::mk_nested_word_item(Ident::with_dummy_span(sym::prelude_import)); + let list = attr::mk_list_item(Ident::with_dummy_span(sym::feature), vec![pi_nested]); let fake_attr = attr::mk_attr_inner(list); s.print_attribute(&fake_attr); @@ -127,7 +129,7 @@ pub fn print_crate<'a>( // root, so this is not needed, and actually breaks things. if edition == Edition::Edition2015 { // `#![no_std]` - let no_std_meta = attr::mk_word_item(ast::Ident::with_dummy_span(sym::no_std)); + let no_std_meta = attr::mk_word_item(Ident::with_dummy_span(sym::no_std)); let fake_attr = attr::mk_attr_inner(no_std_meta); s.print_attribute(&fake_attr); } @@ -389,7 +391,7 @@ impl std::ops::DerefMut for State<'_> { pub trait PrintState<'a>: std::ops::Deref + std::ops::DerefMut { fn comments(&mut self) -> &mut Option>; - fn print_ident(&mut self, ident: ast::Ident); + fn print_ident(&mut self, ident: Ident); fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); fn strsep( @@ -637,9 +639,8 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere match tt { TokenTree::Token(ref token) => { self.word(token_to_string_ext(&token, convert_dollar_crate)); - match token.kind { - token::DocComment(..) => self.hardbreak(), - _ => {} + if let token::DocComment(..) = token.kind { + self.hardbreak() } } TokenTree::Delimited(dspan, delim, tts) => { @@ -672,7 +673,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere &mut self, header: Option>, has_bang: bool, - ident: Option, + ident: Option, delim: DelimToken, tts: TokenStream, convert_dollar_crate: bool, @@ -783,7 +784,7 @@ impl<'a> PrintState<'a> for State<'a> { &mut self.comments } - fn print_ident(&mut self, ident: ast::Ident) { + fn print_ident(&mut self, ident: Ident) { self.s.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string()); self.ann.post(self, AnnNode::Ident(&ident)) } @@ -796,31 +797,10 @@ impl<'a> PrintState<'a> for State<'a> { match *args { ast::GenericArgs::AngleBracketed(ref data) => { self.s.word("<"); - - self.commasep(Inconsistent, &data.args, |s, generic_arg| { - s.print_generic_arg(generic_arg) + self.commasep(Inconsistent, &data.args, |s, arg| match arg { + ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a), + ast::AngleBracketedArg::Constraint(c) => s.print_assoc_constraint(c), }); - - let mut comma = !data.args.is_empty(); - - for constraint in data.constraints.iter() { - if comma { - self.word_space(",") - } - self.print_ident(constraint.ident); - self.s.space(); - match constraint.kind { - ast::AssocTyConstraintKind::Equality { ref ty } => { - self.word_space("="); - self.print_type(ty); - } - ast::AssocTyConstraintKind::Bound { ref bounds } => { - self.print_type_bounds(":", &*bounds); - } - } - comma = true; - } - self.s.word(">") } @@ -891,7 +871,21 @@ impl<'a> State<'a> { } } - crate fn print_generic_arg(&mut self, generic_arg: &GenericArg) { + pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) { + self.print_ident(constraint.ident); + self.s.space(); + match &constraint.kind { + ast::AssocTyConstraintKind::Equality { ty } => { + self.word_space("="); + self.print_type(ty); + } + ast::AssocTyConstraintKind::Bound { bounds } => { + self.print_type_bounds(":", &*bounds); + } + } + } + + pub fn print_generic_arg(&mut self, generic_arg: &GenericArg) { match generic_arg { GenericArg::Lifetime(lt) => self.print_lifetime(*lt), GenericArg::Type(ty) => self.print_type(ty), @@ -1009,7 +1003,7 @@ impl<'a> State<'a> { fn print_item_const( &mut self, - ident: ast::Ident, + ident: Ident, mutbl: Option, ty: &ast::Ty, body: Option<&ast::Expr>, @@ -1040,7 +1034,7 @@ impl<'a> State<'a> { fn print_associated_type( &mut self, - ident: ast::Ident, + ident: Ident, generics: &ast::Generics, bounds: &ast::GenericBounds, ty: Option<&ast::Ty>, @@ -1289,7 +1283,7 @@ impl<'a> State<'a> { &mut self, enum_definition: &ast::EnumDef, generics: &ast::Generics, - ident: ast::Ident, + ident: Ident, span: rustc_span::Span, visibility: &ast::Visibility, ) { @@ -1345,7 +1339,7 @@ impl<'a> State<'a> { &mut self, struct_def: &ast::VariantData, generics: &ast::Generics, - ident: ast::Ident, + ident: Ident, span: rustc_span::Span, print_finalizer: bool, ) { @@ -1397,13 +1391,10 @@ impl<'a> State<'a> { self.print_visibility(&v.vis); let generics = ast::Generics::default(); self.print_struct(&v.data, &generics, v.ident, v.span, false); - match v.disr_expr { - Some(ref d) => { - self.s.space(); - self.word_space("="); - self.print_expr(&d.value) - } - _ => {} + if let Some(ref d) = v.disr_expr { + self.s.space(); + self.word_space("="); + self.print_expr(&d.value) } } @@ -1746,8 +1737,9 @@ impl<'a> State<'a> { // 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. - (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt) - | (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Shl) => parser::PREC_FORCE_PAREN, + (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { + parser::PREC_FORCE_PAREN + } // We are given `(let _ = a) OP b`. // // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens @@ -2025,7 +2017,120 @@ impl<'a> State<'a> { } } ast::ExprKind::InlineAsm(ref a) => { - self.s.word("asm!"); + enum AsmArg<'a> { + Template(String), + Operand(&'a InlineAsmOperand), + Options(InlineAsmOptions), + } + + let mut args = vec![]; + args.push(AsmArg::Template(InlineAsmTemplatePiece::to_string(&a.template))); + args.extend(a.operands.iter().map(|(o, _)| AsmArg::Operand(o))); + if !a.options.is_empty() { + args.push(AsmArg::Options(a.options)); + } + + self.word("asm!"); + self.popen(); + self.commasep(Consistent, &args, |s, arg| match arg { + AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked), + AsmArg::Operand(op) => { + let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r + { + InlineAsmRegOrRegClass::Reg(r) => { + s.print_string(&r.as_str(), ast::StrStyle::Cooked) + } + InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()), + }; + match op { + InlineAsmOperand::In { reg, expr } => { + s.word("in"); + s.popen(); + print_reg_or_class(s, reg); + s.pclose(); + s.space(); + s.print_expr(expr); + } + InlineAsmOperand::Out { reg, late, expr } => { + s.word(if *late { "lateout" } else { "out" }); + s.popen(); + print_reg_or_class(s, reg); + s.pclose(); + s.space(); + match expr { + Some(expr) => s.print_expr(expr), + None => s.word("_"), + } + } + InlineAsmOperand::InOut { reg, late, expr } => { + s.word(if *late { "inlateout" } else { "inout" }); + s.popen(); + print_reg_or_class(s, reg); + s.pclose(); + s.space(); + s.print_expr(expr); + } + InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { + s.word(if *late { "inlateout" } else { "inout" }); + s.popen(); + print_reg_or_class(s, reg); + s.pclose(); + s.space(); + s.print_expr(in_expr); + s.space(); + s.word_space("=>"); + match out_expr { + Some(out_expr) => s.print_expr(out_expr), + None => s.word("_"), + } + } + InlineAsmOperand::Const { expr } => { + s.word("const"); + s.space(); + s.print_expr(expr); + } + InlineAsmOperand::Sym { expr } => { + s.word("sym"); + s.space(); + s.print_expr(expr); + } + } + } + AsmArg::Options(opts) => { + s.word("options"); + s.popen(); + let mut options = vec![]; + if opts.contains(InlineAsmOptions::PURE) { + options.push("pure"); + } + if opts.contains(InlineAsmOptions::NOMEM) { + options.push("nomem"); + } + if opts.contains(InlineAsmOptions::READONLY) { + options.push("readonly"); + } + if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) { + options.push("preserves_flags"); + } + if opts.contains(InlineAsmOptions::NORETURN) { + options.push("noreturn"); + } + if opts.contains(InlineAsmOptions::NOSTACK) { + options.push("nostack"); + } + if opts.contains(InlineAsmOptions::ATT_SYNTAX) { + options.push("att_syntax"); + } + s.commasep(Inconsistent, &options, |s, &opt| { + s.word(opt); + }); + s.pclose(); + } + }); + self.pclose(); + } + ast::ExprKind::LlvmInlineAsm(ref a) => { + self.s.word("llvm_asm!"); self.popen(); self.print_string(&a.asm.as_str(), a.asm_str_style); self.word_space(":"); @@ -2066,7 +2171,7 @@ impl<'a> State<'a> { if a.alignstack { options.push("alignstack"); } - if a.dialect == ast::AsmDialect::Intel { + if a.dialect == ast::LlvmAsmDialect::Intel { options.push("intel"); } @@ -2089,12 +2194,10 @@ impl<'a> State<'a> { } ast::ExprKind::Yield(ref e) => { self.s.word("yield"); - match *e { - Some(ref expr) => { - self.s.space(); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - } - _ => (), + + if let Some(ref expr) = *e { + self.s.space(); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); } } ast::ExprKind::Try(ref e) => { @@ -2128,7 +2231,7 @@ impl<'a> State<'a> { self.s.word(i.to_string()) } - crate fn print_name(&mut self, name: ast::Name) { + crate fn print_name(&mut self, name: Symbol) { self.s.word(name.to_string()); self.ann.post(self, AnnNode::Name(&name)) } @@ -2146,9 +2249,8 @@ impl<'a> State<'a> { self.s.word("::"); let item_segment = path.segments.last().unwrap(); self.print_ident(item_segment.ident); - match item_segment.args { - Some(ref args) => self.print_generic_args(args, colons_before_params), - None => {} + if let Some(ref args) = item_segment.args { + self.print_generic_args(args, colons_before_params) } } @@ -2335,7 +2437,7 @@ impl<'a> State<'a> { fn print_fn_full( &mut self, sig: &ast::FnSig, - name: ast::Ident, + name: Ident, generics: &ast::Generics, vis: &ast::Visibility, defaultness: ast::Defaultness, @@ -2360,7 +2462,7 @@ impl<'a> State<'a> { &mut self, decl: &ast::FnDecl, header: ast::FnHeader, - name: Option, + name: Option, generics: &ast::Generics, ) { self.print_fn_header_info(header); @@ -2627,7 +2729,7 @@ impl<'a> State<'a> { ext: ast::Extern, unsafety: ast::Unsafe, decl: &ast::FnDecl, - name: Option, + name: Option, generic_params: &[ast::GenericParam], ) { self.ibox(INDENT_UNIT); diff --git a/src/librustc_ast_pretty/pprust/tests.rs b/src/librustc_ast_pretty/pprust/tests.rs index 455f2e3da3680..f51439f89ffbe 100644 --- a/src/librustc_ast_pretty/pprust/tests.rs +++ b/src/librustc_ast_pretty/pprust/tests.rs @@ -4,11 +4,12 @@ use rustc_ast::ast; use rustc_ast::with_default_globals; use rustc_span; use rustc_span::source_map::respan; +use rustc_span::symbol::Ident; fn fun_to_string( decl: &ast::FnDecl, header: ast::FnHeader, - name: ast::Ident, + name: Ident, generics: &ast::Generics, ) -> String { to_string(|s| { @@ -26,7 +27,7 @@ fn variant_to_string(var: &ast::Variant) -> String { #[test] fn test_fun_to_string() { with_default_globals(|| { - let abba_ident = ast::Ident::from_str("abba"); + let abba_ident = Ident::from_str("abba"); let decl = ast::FnDecl { inputs: Vec::new(), output: ast::FnRetTy::Default(rustc_span::DUMMY_SP) }; @@ -41,7 +42,7 @@ fn test_fun_to_string() { #[test] fn test_variant_to_string() { with_default_globals(|| { - let ident = ast::Ident::from_str("principal_skinner"); + let ident = Ident::from_str("principal_skinner"); let var = ast::Variant { ident, diff --git a/src/librustc_attr/Cargo.toml b/src/librustc_attr/Cargo.toml index 8aaba15d84ad2..d7af7fe6143e5 100644 --- a/src/librustc_attr/Cargo.toml +++ b/src/librustc_attr/Cargo.toml @@ -3,6 +3,7 @@ authors = ["The Rust Project Developers"] name = "rustc_attr" version = "0.0.0" edition = "2018" +build = "build.rs" [lib] name = "rustc_attr" @@ -17,6 +18,6 @@ rustc_span = { path = "../librustc_span" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_feature = { path = "../librustc_feature" } rustc_macros = { path = "../librustc_macros" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_session = { path = "../librustc_session" } rustc_ast = { path = "../librustc_ast" } +version_check = "0.9" diff --git a/src/librustc_attr/build.rs b/src/librustc_attr/build.rs new file mode 100644 index 0000000000000..863f2b7337b25 --- /dev/null +++ b/src/librustc_attr/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=CFG_RELEASE"); + println!("cargo:rerun-if-env-changed=CFG_RELEASE_CHANNEL"); +} diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs index a16b79993912d..af09779d072c3 100644 --- a/src/librustc_attr/builtin.rs +++ b/src/librustc_attr/builtin.rs @@ -2,7 +2,7 @@ use super::{find_by_name, mark_used}; -use rustc_ast::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem}; +use rustc_ast::ast::{self, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, Handler}; use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg}; @@ -11,6 +11,7 @@ use rustc_session::parse::{feature_err, ParseSess}; use rustc_span::hygiene::Transparency; use rustc_span::{symbol::sym, symbol::Symbol, Span}; use std::num::NonZeroU32; +use version_check::Version; pub fn is_builtin_attr(attr: &Attribute) -> bool { attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some() @@ -98,7 +99,7 @@ pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Op } } - diagnostic.map(|d| { + if let Some(d) = diagnostic { struct_span_err!(d, attr.span, E0633, "malformed `unwind` attribute input") .span_label(attr.span, "invalid argument") .span_suggestions( @@ -110,7 +111,7 @@ pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Op Applicability::MachineApplicable, ) .emit(); - }); + }; } } } @@ -119,7 +120,11 @@ pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Op }) } -/// Represents the #[stable], #[unstable], #[rustc_deprecated] attributes. +/// Represents the following attributes: +/// +/// - `#[stable]` +/// - `#[unstable]` +/// - `#[rustc_deprecated]` #[derive(RustcEncodable, RustcDecodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic)] pub struct Stability { @@ -128,7 +133,7 @@ pub struct Stability { pub rustc_depr: Option, } -/// Represents the #[rustc_const_unstable] and #[rustc_const_stable] attributes. +/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. #[derive(RustcEncodable, RustcDecodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic)] pub struct ConstStability { @@ -564,11 +569,8 @@ pub fn find_crate_name(attrs: &[Attribute]) -> Option { /// Tests if a cfg-pattern matches the cfg set pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool { - eval_condition(cfg, sess, &mut |cfg| { - let gate = find_gated_cfg(|sym| cfg.check_name(sym)); - if let (Some(feats), Some(gated_cfg)) = (features, gate) { - gate_cfg(&gated_cfg, cfg.span, sess, feats); - } + eval_condition(cfg, sess, features, &mut |cfg| { + try_gate_cfg(cfg, sess, features); let error = |span, msg| { sess.span_diagnostic.span_err(span, msg); true @@ -599,6 +601,13 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat }) } +fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) { + let gate = find_gated_cfg(|sym| cfg.check_name(sym)); + if let (Some(feats), Some(gated_cfg)) = (features, gate) { + gate_cfg(&gated_cfg, cfg.span, sess, feats); + } +} + fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) { let (cfg, feature, has_feature) = gated_cfg; if !has_feature(features) && !cfg_span.allows_unstable(*feature) { @@ -612,9 +621,44 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F pub fn eval_condition( cfg: &ast::MetaItem, sess: &ParseSess, + features: Option<&Features>, eval: &mut impl FnMut(&ast::MetaItem) -> bool, ) -> bool { match cfg.kind { + ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => { + try_gate_cfg(cfg, sess, features); + let (min_version, span) = match &mis[..] { + [NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => { + (sym, span) + } + [NestedMetaItem::Literal(Lit { span, .. }) + | NestedMetaItem::MetaItem(MetaItem { span, .. })] => { + sess.span_diagnostic + .struct_span_err(*span, "expected a version literal") + .emit(); + return false; + } + [..] => { + sess.span_diagnostic + .struct_span_err(cfg.span, "expected single version literal") + .emit(); + return false; + } + }; + let min_version = match Version::parse(&min_version.as_str()) { + Some(ver) => ver, + None => { + sess.span_diagnostic.struct_span_err(*span, "invalid version literal").emit(); + return false; + } + }; + let channel = env!("CFG_RELEASE_CHANNEL"); + let nightly = channel == "nightly" || channel == "dev"; + let rustc_version = Version::parse(env!("CFG_RELEASE")).unwrap(); + + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-625474439 for details + if nightly { rustc_version > min_version } else { rustc_version >= min_version } + } ast::MetaItemKind::List(ref mis) => { for mi in mis.iter() { if !mi.is_meta_item() { @@ -630,12 +674,12 @@ 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.iter().any(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval)) - } - sym::all => { - mis.iter().all(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval)) - } + sym::any => mis + .iter() + .any(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)), + sym::all => mis + .iter() + .all(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)), sym::not => { if mis.len() != 1 { struct_span_err!( @@ -648,7 +692,7 @@ pub fn eval_condition( return false; } - !eval_condition(mis[0].meta_item().unwrap(), sess, eval) + !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval) } _ => { struct_span_err!( @@ -870,7 +914,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec { let parse_alignment = |node: &ast::LitKind| -> Result { if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { if literal.is_power_of_two() { - // rustc::ty::layout::Align restricts align to <= 2^29 + // rustc_middle::ty::layout::Align restricts align to <= 2^29 if *literal <= 1 << 29 { Ok(*literal as u32) } else { diff --git a/src/librustc_attr/lib.rs b/src/librustc_attr/lib.rs index 9803501fb96c2..5754bb48d24e1 100644 --- a/src/librustc_attr/lib.rs +++ b/src/librustc_attr/lib.rs @@ -4,6 +4,12 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. +#![feature(or_patterns)] + +// FIXME(#56935): Work around ICEs during cross-compilation. +#[allow(unused)] +extern crate rustc_macros; + mod builtin; pub use builtin::*; diff --git a/src/librustc_builtin_macros/asm.rs b/src/librustc_builtin_macros/asm.rs index 9811a6621afa5..c29739248976c 100644 --- a/src/librustc_builtin_macros/asm.rs +++ b/src/librustc_builtin_macros/asm.rs @@ -1,300 +1,537 @@ -// Inline assembly support. -// -use State::*; +use fmt_macros as parse; -use rustc_ast::ast::{self, AsmDialect}; +use rustc_ast::ast; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Token}; -use rustc_ast::tokenstream::{self, TokenStream}; -use rustc_errors::{struct_span_err, DiagnosticBuilder, PResult}; -use rustc_expand::base::*; +use rustc_ast::token; +use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_expand::base::{self, *}; use rustc_parse::parser::Parser; use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; - -enum State { - Asm, - Outputs, - Inputs, - Clobbers, - Options, - StateNone, -} +use rustc_span::{InnerSpan, Span}; -impl State { - fn next(&self) -> State { - match *self { - Asm => Outputs, - Outputs => Inputs, - Inputs => Clobbers, - Clobbers => Options, - Options => StateNone, - StateNone => StateNone, - } - } +struct AsmArgs { + template: P, + operands: Vec<(ast::InlineAsmOperand, Span)>, + named_args: FxHashMap, + reg_args: FxHashSet, + options: ast::InlineAsmOptions, + options_span: Option, } -const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel]; - -pub fn expand_asm<'cx>( - cx: &'cx mut ExtCtxt<'_>, +fn parse_args<'a>( + ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, -) -> Box { - let mut inline_asm = match parse_inline_asm(cx, sp, tts) { - Ok(Some(inline_asm)) => inline_asm, - Ok(None) => return DummyResult::any(sp), - Err(mut err) => { - err.emit(); - return DummyResult::any(sp); - } - }; +) -> Result> { + let mut p = ecx.new_parser_from_tts(tts); - // If there are no outputs, the inline assembly is executed just for its side effects, - // so ensure that it is volatile - if inline_asm.outputs.is_empty() { - inline_asm.volatile = true; + if p.token == token::Eof { + return Err(ecx.struct_span_err(sp, "requires at least a template string argument")); } - MacEager::expr(P(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::InlineAsm(P(inline_asm)), - span: cx.with_def_site_ctxt(sp), - attrs: ast::AttrVec::new(), - })) -} + // Detect use of the legacy llvm_asm! syntax (which used to be called asm!) + if p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) { + let mut err = ecx.struct_span_err(sp, "legacy asm! syntax is no longer supported"); -fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> { - match p.parse_str_lit() { - Ok(str_lit) => Ok(str_lit.symbol_unescaped), - Err(opt_lit) => { - let span = opt_lit.map_or(p.token.span, |lit| lit.span); - let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); - err.span_label(span, "not a string literal"); - Err(err) + // Find the span of the "asm!" so that we can offer an automatic suggestion + let asm_span = sp.from_inner(InnerSpan::new(0, 4)); + if let Ok(s) = ecx.source_map().span_to_snippet(asm_span) { + if s == "asm!" { + err.span_suggestion( + asm_span, + "replace with", + "llvm_asm!".into(), + Applicability::MachineApplicable, + ); + } } + return Err(err); } -} -fn parse_inline_asm<'a>( - cx: &mut ExtCtxt<'a>, - sp: Span, - tts: TokenStream, -) -> Result, DiagnosticBuilder<'a>> { - // Split the tts before the first colon, to avoid `asm!("x": y)` being - // parsed as `asm!(z)` with `z = "x": y` which is type ascription. - let first_colon = tts - .trees() - .position(|tt| match tt { - tokenstream::TokenTree::Token(Token { kind: token::Colon, .. }) - | tokenstream::TokenTree::Token(Token { kind: token::ModSep, .. }) => true, - _ => false, - }) - .unwrap_or(tts.len()); - let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect()); - let mut asm = kw::Invalid; - let mut asm_str_style = None; - let mut outputs = Vec::new(); - let mut inputs = Vec::new(); - let mut clobs = Vec::new(); - let mut volatile = false; - let mut alignstack = false; - let mut dialect = AsmDialect::Att; - - let mut state = Asm; - - 'statement: loop { - match state { - Asm => { - if asm_str_style.is_some() { - // If we already have a string with instructions, - // ending up in Asm state again is an error. - return Err(struct_span_err!( - cx.parse_sess.span_diagnostic, - sp, - E0660, - "malformed inline assembly" - )); - } - // Nested parser, stop before the first colon (see above). - let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect()); + let template = p.parse_expr()?; + let mut args = AsmArgs { + template, + operands: vec![], + named_args: FxHashMap::default(), + reg_args: FxHashSet::default(), + options: ast::InlineAsmOptions::empty(), + options_span: None, + }; + + let mut first = true; + while p.token != token::Eof { + if !p.eat(&token::Comma) { + if first { + // After `asm!(""` we always expect *only* a comma... + let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`"); + err.span_label(p.token.span, "expected `,`"); + p.maybe_annotate_with_ascription(&mut err, false); + return Err(err); + } else { + // ...after that delegate to `expect` to also include the other expected tokens. + return Err(p.expect(&token::Comma).err().unwrap()); + } + } + first = false; + if p.token == token::Eof { + break; + } // accept trailing commas - if p2.token == token::Eof { - let mut err = - cx.struct_span_err(sp, "macro requires a string literal as an argument"); - err.span_label(sp, "string literal required"); + // Parse options + if p.eat(&token::Ident(sym::options, false)) { + parse_options(&mut p, &mut args)?; + continue; + } + + let span_start = p.token.span; + + // 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(); + p.expect(&token::Eq)?; + Some(ident.name) + } else { + None + }; + + let mut explicit_reg = false; + let op = if p.eat(&token::Ident(kw::In, false)) { + let reg = parse_reg(&mut p, &mut explicit_reg)?; + let expr = p.parse_expr()?; + ast::InlineAsmOperand::In { reg, expr } + } else if p.eat(&token::Ident(sym::out, false)) { + let reg = parse_reg(&mut p, &mut explicit_reg)?; + let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: false } + } else if p.eat(&token::Ident(sym::lateout, false)) { + let reg = parse_reg(&mut p, &mut explicit_reg)?; + let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: true } + } else if p.eat(&token::Ident(sym::inout, false)) { + let reg = parse_reg(&mut p, &mut explicit_reg)?; + let expr = p.parse_expr()?; + if p.eat(&token::FatArrow) { + let out_expr = + if p.eat_keyword(kw::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 p.eat(&token::Ident(sym::inlateout, false)) { + let reg = parse_reg(&mut p, &mut explicit_reg)?; + let expr = p.parse_expr()?; + if p.eat(&token::FatArrow) { + let out_expr = + if p.eat_keyword(kw::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 p.eat(&token::Ident(kw::Const, false)) { + let expr = p.parse_expr()?; + ast::InlineAsmOperand::Const { expr } + } else { + p.expect(&token::Ident(sym::sym, false))?; + let expr = p.parse_expr()?; + match expr.kind { + ast::ExprKind::Path(..) => {} + _ => { + let err = ecx + .struct_span_err(expr.span, "argument to `sym` must be a path expression"); return Err(err); } + } + ast::InlineAsmOperand::Sym { expr } + }; - let expr = p2.parse_expr()?; - let (s, style) = - match expr_to_string(cx, expr, "inline assembly must be a string literal") { - Some((s, st)) => (s, st), - None => return Ok(None), - }; - - // This is most likely malformed. - if p2.token != token::Eof { - let mut extra_tts = p2.parse_all_token_trees()?; - extra_tts.extend(tts.trees().skip(first_colon)); - p = cx.new_parser_from_tts(extra_tts.into_iter().collect()); + 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 options. We do + // this at the end once we have the full span of the argument available. + if let Some(options_span) = args.options_span { + ecx.struct_span_err(span, "arguments are not allowed after options") + .span_label(options_span, "previous options") + .span_label(span, "argument") + .emit(); + } + if explicit_reg { + if name.is_some() { + ecx.struct_span_err(span, "explicit register arguments cannot have names").emit(); + } + args.reg_args.insert(slot); + } else if let Some(name) = name { + if let Some(&prev) = args.named_args.get(&name) { + ecx.struct_span_err(span, &format!("duplicate argument named `{}`", name)) + .span_label(args.operands[prev].1, "previously here") + .span_label(span, "duplicate argument") + .emit(); + continue; + } + if !args.reg_args.is_empty() { + let mut err = ecx.struct_span_err( + span, + "named arguments cannot follow explicit register arguments", + ); + err.span_label(span, "named argument"); + for pos in &args.reg_args { + err.span_label(args.operands[*pos].1, "explicit register argument"); } + err.emit(); + } + args.named_args.insert(name, slot); + } else { + if !args.named_args.is_empty() || !args.reg_args.is_empty() { + let mut err = ecx.struct_span_err( + span, + "positional arguments cannot follow named arguments \ + or explicit register arguments", + ); + err.span_label(span, "positional argument"); + for pos in args.named_args.values() { + err.span_label(args.operands[*pos].1, "named argument"); + } + for pos in &args.reg_args { + err.span_label(args.operands[*pos].1, "explicit register argument"); + } + err.emit(); + } + } + } - asm = s; - asm_str_style = Some(style); + if args.options.contains(ast::InlineAsmOptions::NOMEM) + && args.options.contains(ast::InlineAsmOptions::READONLY) + { + let span = args.options_span.unwrap(); + ecx.struct_span_err(span, "the `nomem` and `readonly` options are mutually exclusive") + .emit(); + } + if args.options.contains(ast::InlineAsmOptions::PURE) + && args.options.contains(ast::InlineAsmOptions::NORETURN) + { + let span = args.options_span.unwrap(); + ecx.struct_span_err(span, "the `pure` and `noreturn` options are mutually exclusive") + .emit(); + } + if args.options.contains(ast::InlineAsmOptions::PURE) + && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) + { + let span = args.options_span.unwrap(); + ecx.struct_span_err( + span, + "the `pure` option must be combined with either `nomem` or `readonly`", + ) + .emit(); + } + + let mut have_real_output = false; + let mut outputs_sp = vec![]; + for (op, op_sp) in &args.operands { + match op { + ast::InlineAsmOperand::Out { expr, .. } + | ast::InlineAsmOperand::SplitInOut { out_expr: expr, .. } => { + outputs_sp.push(*op_sp); + have_real_output |= expr.is_some(); } - Outputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !outputs.is_empty() { - p.eat(&token::Comma); - } + ast::InlineAsmOperand::InOut { .. } => { + outputs_sp.push(*op_sp); + have_real_output = true; + } + _ => {} + } + } + if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { + ecx.struct_span_err( + args.options_span.unwrap(), + "asm with `pure` option must have at least one output", + ) + .emit(); + } + if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() { + let err = ecx + .struct_span_err(outputs_sp, "asm outputs are not allowed with the `noreturn` option"); - let constraint = parse_asm_str(&mut p)?; - - let span = p.prev_token.span; - - p.expect(&token::OpenDelim(token::Paren))?; - let expr = p.parse_expr()?; - p.expect(&token::CloseDelim(token::Paren))?; - - // Expands a read+write operand into two operands. - // - // Use '+' modifier when you want the same expression - // to be both an input and an output at the same time. - // It's the opposite of '=&' which means that the memory - // cannot be shared with any other operand (usually when - // a register is clobbered early.) - let constraint_str = constraint.as_str(); - let mut ch = constraint_str.chars(); - let output = match ch.next() { - Some('=') => None, - Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))), - _ => { - struct_span_err!( - cx.parse_sess.span_diagnostic, - span, - E0661, - "output operand constraint lacks '=' or '+'" - ) - .emit(); - None - } - }; - - let is_rw = output.is_some(); - let is_indirect = constraint_str.contains('*'); - outputs.push(ast::InlineAsmOutput { - constraint: output.unwrap_or(constraint), - expr, - is_rw, - is_indirect, - }); + // Bail out now since this is likely to confuse MIR + return Err(err); + } + + Ok(args) +} + +fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), DiagnosticBuilder<'a>> { + let span_start = p.prev_token.span; + + p.expect(&token::OpenDelim(token::DelimToken::Paren))?; + + while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + if p.eat(&token::Ident(sym::pure, false)) { + args.options |= ast::InlineAsmOptions::PURE; + } else if p.eat(&token::Ident(sym::nomem, false)) { + args.options |= ast::InlineAsmOptions::NOMEM; + } else if p.eat(&token::Ident(sym::readonly, false)) { + args.options |= ast::InlineAsmOptions::READONLY; + } else if p.eat(&token::Ident(sym::preserves_flags, false)) { + args.options |= ast::InlineAsmOptions::PRESERVES_FLAGS; + } else if p.eat(&token::Ident(sym::noreturn, false)) { + args.options |= ast::InlineAsmOptions::NORETURN; + } else if p.eat(&token::Ident(sym::nostack, false)) { + args.options |= ast::InlineAsmOptions::NOSTACK; + } else { + p.expect(&token::Ident(sym::att_syntax, false))?; + args.options |= ast::InlineAsmOptions::ATT_SYNTAX; + } + + // Allow trailing commas + if p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + break; + } + p.expect(&token::Comma)?; + } + + let new_span = span_start.to(p.prev_token.span); + if let Some(options_span) = args.options_span { + p.struct_span_err(new_span, "asm options cannot be specified multiple times") + .span_label(options_span, "previously here") + .span_label(new_span, "duplicate options") + .emit(); + } else { + args.options_span = Some(new_span); + } + + Ok(()) +} + +fn parse_reg<'a>( + p: &mut Parser<'a>, + explicit_reg: &mut bool, +) -> Result> { + p.expect(&token::OpenDelim(token::DelimToken::Paren))?; + let result = match p.token.kind { + token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name), + token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { + *explicit_reg = true; + ast::InlineAsmRegOrRegClass::Reg(symbol) + } + _ => { + return Err( + p.struct_span_err(p.token.span, "expected register class or explicit register") + ); + } + }; + p.bump(); + p.expect(&token::CloseDelim(token::DelimToken::Paren))?; + Ok(result) +} + +fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P { + let msg = "asm template must be a string literal"; + let template_sp = args.template.span; + let (template_str, template_style, template_span) = + match expr_to_spanned_string(ecx, args.template, msg) { + Ok(template) => template, + Err(err) => { + if let Some(mut err) = err { + err.emit(); } + return DummyResult::raw_expr(sp, true); } - Inputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !inputs.is_empty() { - p.eat(&token::Comma); - } + }; - let constraint = parse_asm_str(&mut p)?; + let str_style = match template_style { + ast::StrStyle::Cooked => None, + ast::StrStyle::Raw(raw) => Some(raw as usize), + }; - if constraint.as_str().starts_with('=') { - struct_span_err!( - cx.parse_sess.span_diagnostic, - p.prev_token.span, - E0662, - "input operand constraint contains '='" - ) - .emit(); - } else if constraint.as_str().starts_with('+') { - struct_span_err!( - cx.parse_sess.span_diagnostic, - p.prev_token.span, - E0663, - "input operand constraint contains '+'" - ) - .emit(); - } + let template_str = &template_str.as_str(); + let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok(); + let mut parser = parse::Parser::new( + template_str, + str_style, + template_snippet, + false, + parse::ParseMode::InlineAsm, + ); - p.expect(&token::OpenDelim(token::Paren))?; - let input = p.parse_expr()?; - p.expect(&token::CloseDelim(token::Paren))?; + let mut unverified_pieces = Vec::new(); + while let Some(piece) = parser.next() { + if !parser.errors.is_empty() { + break; + } else { + unverified_pieces.push(piece); + } + } - inputs.push((constraint, input)); - } + if !parser.errors.is_empty() { + let err = parser.errors.remove(0); + let err_sp = template_span.from_inner(err.span); + let mut e = ecx + .struct_span_err(err_sp, &format!("invalid asm template string: {}", err.description)); + e.span_label(err_sp, err.label + " in asm template string"); + if let Some(note) = err.note { + e.note(¬e); + } + if let Some((label, span)) = err.secondary_label { + let err_sp = template_span.from_inner(span); + e.span_label(err_sp, label); + } + e.emit(); + return DummyResult::raw_expr(sp, true); + } + + // Register operands are implicitly used since they are not allowed to be + // referenced in the template string. + let mut used = vec![false; args.operands.len()]; + for pos in &args.reg_args { + used[*pos] = true; + } + + let named_pos: FxHashSet = args.named_args.values().cloned().collect(); + let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span)); + let mut template = vec![]; + for piece in unverified_pieces { + match piece { + parse::Piece::String(s) => { + template.push(ast::InlineAsmTemplatePiece::String(s.to_string())) } - Clobbers => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !clobs.is_empty() { - p.eat(&token::Comma); - } + parse::Piece::NextArgument(arg) => { + let span = arg_spans.next().unwrap_or(template_sp); - let s = parse_asm_str(&mut p)?; - - if OPTIONS.iter().any(|&opt| s == opt) { - cx.span_warn(p.prev_token.span, "expected a clobber, found an option"); - } else if s.as_str().starts_with('{') || s.as_str().ends_with('}') { - struct_span_err!( - cx.parse_sess.span_diagnostic, - p.prev_token.span, - E0664, - "clobber should not be surrounded by braces" - ) - .emit(); + let operand_idx = match arg.position { + parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => { + if idx >= args.operands.len() + || named_pos.contains(&idx) + || args.reg_args.contains(&idx) + { + let msg = format!("invalid reference to argument at index {}", idx); + let mut err = ecx.struct_span_err(span, &msg); + err.span_label(span, "from here"); + + let positional_args = + args.operands.len() - args.named_args.len() - args.reg_args.len(); + let positional = if positional_args != args.operands.len() { + "positional " + } else { + "" + }; + let msg = match positional_args { + 0 => format!("no {}arguments were given", positional), + 1 => format!("there is 1 {}argument", positional), + x => format!("there are {} {}arguments", x, positional), + }; + err.note(&msg); + + if named_pos.contains(&idx) { + err.span_label(args.operands[idx].1, "named argument"); + err.span_note( + args.operands[idx].1, + "named arguments cannot be referenced by position", + ); + } else if args.reg_args.contains(&idx) { + err.span_label(args.operands[idx].1, "explicit register argument"); + err.span_note( + args.operands[idx].1, + "explicit register arguments cannot be used in the asm template", + ); + } + err.emit(); + None + } else { + Some(idx) + } } + parse::ArgumentNamed(name) => match args.named_args.get(&name) { + Some(&idx) => Some(idx), + None => { + let msg = format!("there is no argument named `{}`", name); + ecx.struct_span_err(span, &msg[..]).emit(); + None + } + }, + }; - clobs.push(s); - } - } - Options => { - let option = parse_asm_str(&mut p)?; - - if option == sym::volatile { - // Indicates that the inline assembly has side effects - // and must not be optimized out along with its outputs. - volatile = true; - } else if option == sym::alignstack { - alignstack = true; - } else if option == sym::intel { - dialect = AsmDialect::Intel; - } else { - cx.span_warn(p.prev_token.span, "unrecognized option"); + let mut chars = arg.format.ty.chars(); + let mut modifier = chars.next(); + if !chars.next().is_none() { + let span = arg + .format + .ty_span + .map(|sp| template_sp.from_inner(sp)) + .unwrap_or(template_sp); + ecx.struct_span_err(span, "asm template modifier must be a single character") + .emit(); + modifier = None; } - if p.token == token::Comma { - p.eat(&token::Comma); + if let Some(operand_idx) = operand_idx { + used[operand_idx] = true; + template.push(ast::InlineAsmTemplatePiece::Placeholder { + operand_idx, + modifier, + span, + }); } } - StateNone => (), } + } - loop { - // MOD_SEP is a double colon '::' without space in between. - // When encountered, the state must be advanced twice. - match (&p.token.kind, state.next(), state.next().next()) { - (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => { - p.bump(); - break 'statement; - } - (&token::Colon, st, _) | (&token::ModSep, _, st) => { - p.bump(); - state = st; - } - (&token::Eof, ..) => break 'statement, - _ => break, + let operands = args.operands; + let unused_operands: Vec<_> = used + .into_iter() + .enumerate() + .filter(|&(_, used)| !used) + .map(|(idx, _)| { + if named_pos.contains(&idx) { + // named argument + (operands[idx].1, "named argument never used") + } else { + // positional argument + (operands[idx].1, "argument never used") } + }) + .collect(); + match unused_operands.len() { + 0 => {} + 1 => { + let (sp, msg) = unused_operands.into_iter().next().unwrap(); + let mut err = ecx.struct_span_err(sp, msg); + err.span_label(sp, msg); + err.emit(); + } + _ => { + let mut err = ecx.struct_span_err( + unused_operands.iter().map(|&(sp, _)| sp).collect::>(), + "multiple unused asm arguments", + ); + for (sp, msg) in unused_operands { + err.span_label(sp, msg); + } + err.emit(); } } - Ok(Some(ast::InlineAsm { - asm, - asm_str_style: asm_str_style.unwrap(), - outputs, - inputs, - clobbers: clobs, - volatile, - alignstack, - dialect, - })) + let inline_asm = ast::InlineAsm { template, operands, options: args.options }; + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::InlineAsm(inline_asm), + span: sp, + attrs: ast::AttrVec::new(), + }) +} + +pub fn expand_asm<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box { + match parse_args(ecx, sp, tts) { + Ok(args) => MacEager::expr(expand_preparsed_asm(ecx, sp, args)), + Err(mut err) => { + err.emit(); + DummyResult::any(sp) + } + } } diff --git a/src/librustc_builtin_macros/assert.rs b/src/librustc_builtin_macros/assert.rs index 3a3595b04d287..166cd62835030 100644 --- a/src/librustc_builtin_macros/assert.rs +++ b/src/librustc_builtin_macros/assert.rs @@ -7,7 +7,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_expand::base::*; use rustc_parse::parser::Parser; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; pub fn expand_assert<'cx>( diff --git a/src/librustc_builtin_macros/concat.rs b/src/librustc_builtin_macros/concat.rs index e0ce37b95fcd6..4980ba0d9d30d 100644 --- a/src/librustc_builtin_macros/concat.rs +++ b/src/librustc_builtin_macros/concat.rs @@ -26,9 +26,12 @@ pub fn expand_concat( ast::LitKind::Char(c) => { accumulator.push(c); } - ast::LitKind::Int(i, ast::LitIntType::Unsigned(_)) - | ast::LitKind::Int(i, ast::LitIntType::Signed(_)) - | ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => { + ast::LitKind::Int( + i, + ast::LitIntType::Unsigned(_) + | ast::LitIntType::Signed(_) + | ast::LitIntType::Unsuffixed, + ) => { accumulator.push_str(&i.to_string()); } ast::LitKind::Bool(b) => { diff --git a/src/librustc_builtin_macros/concat_idents.rs b/src/librustc_builtin_macros/concat_idents.rs index b55e71b2518f4..8a1741c065466 100644 --- a/src/librustc_builtin_macros/concat_idents.rs +++ b/src/librustc_builtin_macros/concat_idents.rs @@ -3,7 +3,7 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_expand::base::{self, *}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; pub fn expand_concat_idents<'cx>( @@ -39,10 +39,10 @@ pub fn expand_concat_idents<'cx>( } } - let ident = ast::Ident::new(Symbol::intern(&res_str), cx.with_call_site_ctxt(sp)); + let ident = Ident::new(Symbol::intern(&res_str), cx.with_call_site_ctxt(sp)); struct ConcatIdentsResult { - ident: ast::Ident, + ident: Ident, } impl base::MacResult for ConcatIdentsResult { diff --git a/src/librustc_builtin_macros/deriving/clone.rs b/src/librustc_builtin_macros/deriving/clone.rs index 97569ef813886..5dbf3825ce693 100644 --- a/src/librustc_builtin_macros/deriving/clone.rs +++ b/src/librustc_builtin_macros/deriving/clone.rs @@ -5,7 +5,7 @@ use crate::deriving::path_std; use rustc_ast::ast::{self, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData}; use rustc_ast::ptr::P; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; pub fn expand_deriving_clone( @@ -135,8 +135,7 @@ fn cs_clone_shallow( let mut stmts = Vec::new(); if is_union { // let _: AssertParamIsCopy; - let self_ty = - cx.ty_path(cx.path_ident(trait_span, ast::Ident::with_dummy_span(kw::SelfUpper))); + let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy"); } else { match *substr.fields { diff --git a/src/librustc_builtin_macros/deriving/cmp/eq.rs b/src/librustc_builtin_macros/deriving/cmp/eq.rs index b39f41513ee35..b3b15b897828a 100644 --- a/src/librustc_builtin_macros/deriving/cmp/eq.rs +++ b/src/librustc_builtin_macros/deriving/cmp/eq.rs @@ -2,10 +2,10 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use rustc_ast::ast::{self, Expr, GenericArg, Ident, MetaItem}; +use rustc_ast::ast::{self, Expr, GenericArg, MetaItem}; use rustc_ast::ptr::P; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; pub fn expand_deriving_eq( diff --git a/src/librustc_builtin_macros/deriving/cmp/ord.rs b/src/librustc_builtin_macros/deriving/cmp/ord.rs index b23fbc6f62bdb..030d2c837428b 100644 --- a/src/librustc_builtin_macros/deriving/cmp/ord.rs +++ b/src/librustc_builtin_macros/deriving/cmp/ord.rs @@ -5,7 +5,7 @@ use crate::deriving::path_std; use rustc_ast::ast::{self, Expr, MetaItem}; use rustc_ast::ptr::P; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; pub fn expand_deriving_ord( @@ -45,15 +45,15 @@ pub fn expand_deriving_ord( pub fn ordering_collapsed( cx: &mut ExtCtxt<'_>, span: Span, - self_arg_tags: &[ast::Ident], + self_arg_tags: &[Ident], ) -> P { let lft = cx.expr_ident(span, self_arg_tags[0]); let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); - cx.expr_method_call(span, lft, ast::Ident::new(sym::cmp, span), vec![rgt]) + cx.expr_method_call(span, lft, Ident::new(sym::cmp, span), vec![rgt]) } pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P { - let test_id = ast::Ident::new(sym::cmp, span); + let test_id = Ident::new(sym::cmp, span); let equals_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); diff --git a/src/librustc_builtin_macros/deriving/cmp/partial_ord.rs b/src/librustc_builtin_macros/deriving/cmp/partial_ord.rs index 835ccd1b022e1..f29f91e82312b 100644 --- a/src/librustc_builtin_macros/deriving/cmp/partial_ord.rs +++ b/src/librustc_builtin_macros/deriving/cmp/partial_ord.rs @@ -7,7 +7,7 @@ use crate::deriving::{path_local, path_std, pathvec_std}; use rustc_ast::ast::{self, BinOpKind, Expr, MetaItem}; use rustc_ast::ptr::P; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; pub fn expand_deriving_partial_ord( @@ -104,7 +104,7 @@ pub fn some_ordering_collapsed( cx: &mut ExtCtxt<'_>, span: Span, op: OrderingOp, - self_arg_tags: &[ast::Ident], + self_arg_tags: &[Ident], ) -> P { let lft = cx.expr_ident(span, self_arg_tags[0]); let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); @@ -119,7 +119,7 @@ pub fn some_ordering_collapsed( } pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P { - let test_id = ast::Ident::new(sym::cmp, span); + let test_id = Ident::new(sym::cmp, span); let ordering = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); let ordering_expr = cx.expr_path(ordering.clone()); let equals_expr = cx.expr_some(span, ordering_expr); diff --git a/src/librustc_builtin_macros/deriving/debug.rs b/src/librustc_builtin_macros/deriving/debug.rs index f47be3c3c1975..99c2b6f8a4eac 100644 --- a/src/librustc_builtin_macros/deriving/debug.rs +++ b/src/librustc_builtin_macros/deriving/debug.rs @@ -2,11 +2,11 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_ast::ast::{Expr, MetaItem}; use rustc_ast::ptr::P; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; pub fn expand_deriving_debug( @@ -63,7 +63,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let span = cx.with_def_site_ctxt(span); let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked)); let builder = cx.ident_of("debug_trait_builder", span); - let builder_expr = cx.expr_ident(span, builder.clone()); + let builder_expr = cx.expr_ident(span, builder); let fmt = substr.nonself_args[0].clone(); @@ -88,7 +88,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> // Use `let _ = expr;` to avoid triggering the // unused_results lint. - stmts.push(stmt_let_undescore(cx, span, expr)); + stmts.push(stmt_let_underscore(cx, span, expr)); } } ast::VariantData::Struct(..) => { @@ -112,7 +112,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> Ident::new(sym::field, span), vec![name, field], ); - stmts.push(stmt_let_undescore(cx, span, expr)); + stmts.push(stmt_let_underscore(cx, span, expr)); } } } @@ -124,7 +124,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> cx.expr_block(block) } -fn stmt_let_undescore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P) -> ast::Stmt { +fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P) -> ast::Stmt { let local = P(ast::Local { pat: cx.pat_wild(sp), ty: None, diff --git a/src/librustc_builtin_macros/deriving/generic/mod.rs b/src/librustc_builtin_macros/deriving/generic/mod.rs index 9338f9afbbb31..a9567f20d6925 100644 --- a/src/librustc_builtin_macros/deriving/generic/mod.rs +++ b/src/librustc_builtin_macros/deriving/generic/mod.rs @@ -181,15 +181,14 @@ use std::cell::RefCell; use std::iter; use std::vec; -use rustc_ast::ast::{self, BinOpKind, EnumDef, Expr, Generics, Ident, PatKind}; +use rustc_ast::ast::{self, BinOpKind, EnumDef, Expr, Generics, PatKind}; use rustc_ast::ast::{GenericArg, GenericParamKind, VariantData}; use rustc_ast::ptr::P; -use rustc_ast::util::map_in_place::MapInPlace; use rustc_attr as attr; +use rustc_data_structures::map_in_place::MapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_session::parse::ParseSess; use rustc_span::source_map::respan; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty}; @@ -222,7 +221,7 @@ pub struct TraitDef<'a> { pub methods: Vec>, - pub associated_types: Vec<(ast::Ident, Ty<'a>)>, + pub associated_types: Vec<(Ident, Ty<'a>)>, } pub struct MethodDef<'a> { @@ -336,14 +335,14 @@ pub fn combine_substructure( /// is not global and starts with `T`, or a `TyQPath`. fn find_type_parameters( ty: &ast::Ty, - ty_param_names: &[ast::Name], + ty_param_names: &[Symbol], cx: &ExtCtxt<'_>, ) -> Vec> { use rustc_ast::visit; struct Visitor<'a, 'b> { cx: &'a ExtCtxt<'b>, - ty_param_names: &'a [ast::Name], + ty_param_names: &'a [Symbol], types: Vec>, } @@ -437,14 +436,7 @@ impl<'a> TraitDef<'a> { // This can only cause further compilation errors // downstream in blatantly illegal code, so it // is fine. - self.expand_enum_def( - cx, - enum_def, - &item.attrs, - item.ident, - generics, - from_scratch, - ) + self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch) } ast::ItemKind::Union(ref struct_def, ref generics) => { if self.supports_unions { @@ -620,7 +612,7 @@ impl<'a> TraitDef<'a> { .peekable(); if ty_params.peek().is_some() { - let ty_param_names: Vec = + let ty_param_names: Vec = ty_params.map(|ty_param| ty_param.ident.name).collect(); for field_ty in field_tys { @@ -769,7 +761,6 @@ impl<'a> TraitDef<'a> { &self, cx: &mut ExtCtxt<'_>, enum_def: &'a EnumDef, - type_attrs: &[ast::Attribute], type_ident: Ident, generics: &Generics, from_scratch: bool, @@ -801,7 +792,6 @@ impl<'a> TraitDef<'a> { cx, self, enum_def, - type_attrs, type_ident, self_args, &nonself_args[..], @@ -816,38 +806,6 @@ impl<'a> TraitDef<'a> { } } -fn find_repr_type_name(sess: &ParseSess, type_attrs: &[ast::Attribute]) -> &'static str { - let mut repr_type_name = "isize"; - for a in type_attrs { - for r in &attr::find_repr_attrs(sess, a) { - repr_type_name = match *r { - attr::ReprPacked(_) - | attr::ReprSimd - | attr::ReprAlign(_) - | attr::ReprTransparent - | attr::ReprNoNiche => continue, - - attr::ReprC => "i32", - - attr::ReprInt(attr::SignedInt(ast::IntTy::Isize)) => "isize", - attr::ReprInt(attr::SignedInt(ast::IntTy::I8)) => "i8", - attr::ReprInt(attr::SignedInt(ast::IntTy::I16)) => "i16", - attr::ReprInt(attr::SignedInt(ast::IntTy::I32)) => "i32", - attr::ReprInt(attr::SignedInt(ast::IntTy::I64)) => "i64", - attr::ReprInt(attr::SignedInt(ast::IntTy::I128)) => "i128", - - attr::ReprInt(attr::UnsignedInt(ast::UintTy::Usize)) => "usize", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U8)) => "u8", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U16)) => "u16", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U32)) => "u32", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U64)) => "u64", - attr::ReprInt(attr::UnsignedInt(ast::UintTy::U128)) => "u128", - } - } - } - repr_type_name -} - impl<'a> MethodDef<'a> { fn call_substructure_method( &self, @@ -1148,20 +1106,11 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, - type_attrs: &[ast::Attribute], type_ident: Ident, self_args: Vec>, nonself_args: &[P], ) -> P { - self.build_enum_match_tuple( - cx, - trait_, - enum_def, - type_attrs, - type_ident, - self_args, - nonself_args, - ) + self.build_enum_match_tuple(cx, trait_, enum_def, type_ident, self_args, nonself_args) } /// Creates a match for a tuple of all `self_args`, where either all @@ -1181,11 +1130,11 @@ impl<'a> MethodDef<'a> { /// ```{.text} /// let __self0_vi = unsafe { - /// std::intrinsics::discriminant_value(&self) } as i32; + /// std::intrinsics::discriminant_value(&self) }; /// let __self1_vi = unsafe { - /// std::intrinsics::discriminant_value(&arg1) } as i32; + /// std::intrinsics::discriminant_value(&arg1) }; /// let __self2_vi = unsafe { - /// std::intrinsics::discriminant_value(&arg2) } as i32; + /// std::intrinsics::discriminant_value(&arg2) }; /// /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... { /// match (...) { @@ -1204,7 +1153,6 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, - type_attrs: &[ast::Attribute], type_ident: Ident, mut self_args: Vec>, nonself_args: &[P], @@ -1223,7 +1171,7 @@ impl<'a> MethodDef<'a> { .collect::>(); let self_arg_idents = - self_arg_names.iter().map(|name| cx.ident_of(name, sp)).collect::>(); + self_arg_names.iter().map(|name| cx.ident_of(name, sp)).collect::>(); // The `vi_idents` will be bound, solely in the catch-all, to // a series of let statements mapping each self_arg to an int @@ -1234,7 +1182,7 @@ impl<'a> MethodDef<'a> { let vi_suffix = format!("{}_vi", &name[..]); cx.ident_of(&vi_suffix[..], trait_.span) }) - .collect::>(); + .collect::>(); // Builds, via callback to call_substructure_method, the // delegated expression that handles the catch-all case, @@ -1392,21 +1340,18 @@ impl<'a> MethodDef<'a> { // if variants.len() > 1 && self_args.len() > 1 { // Build a series of let statements mapping each self_arg - // to its discriminant value. If this is a C-style enum - // with a specific repr type, then casts the values to - // that type. Otherwise casts to `i32` (the default repr - // type). + // to its discriminant value. // // i.e., for `enum E { A, B(1), C(T, T) }`, and a deriving // with three Self args, builds three statements: // // ``` // let __self0_vi = unsafe { - // std::intrinsics::discriminant_value(&self) } as i32; + // std::intrinsics::discriminant_value(&self) }; // let __self1_vi = unsafe { - // std::intrinsics::discriminant_value(&arg1) } as i32; + // std::intrinsics::discriminant_value(&arg1) }; // let __self2_vi = unsafe { - // std::intrinsics::discriminant_value(&arg2) } as i32; + // std::intrinsics::discriminant_value(&arg2) }; // ``` let mut index_let_stmts: Vec = Vec::with_capacity(vi_idents.len() + 1); @@ -1414,17 +1359,12 @@ impl<'a> MethodDef<'a> { // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... let mut discriminant_test = cx.expr_bool(sp, true); - let target_type_name = find_repr_type_name(&cx.parse_sess, type_attrs); - let mut first_ident = None; for (&ident, self_arg) in vi_idents.iter().zip(&self_args) { let self_addr = cx.expr_addr_of(sp, self_arg.clone()); let variant_value = deriving::call_intrinsic(cx, sp, "discriminant_value", vec![self_addr]); - - let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name, sp)); - let variant_disr = cx.expr_cast(sp, variant_value, target_ty); - let let_stmt = cx.stmt_let(sp, false, ident, variant_disr); + let let_stmt = cx.stmt_let(sp, false, ident, variant_value); index_let_stmts.push(let_stmt); match first_ident { @@ -1598,7 +1538,7 @@ impl<'a> TraitDef<'a> { fn create_subpatterns( &self, cx: &mut ExtCtxt<'_>, - field_paths: Vec, + field_paths: Vec, mutbl: ast::Mutability, use_temporaries: bool, ) -> Vec> { @@ -1670,7 +1610,7 @@ impl<'a> TraitDef<'a> { fn create_enum_variant_pattern( &self, cx: &mut ExtCtxt<'_>, - enum_ident: ast::Ident, + enum_ident: Ident, variant: &'a ast::Variant, prefix: &str, mutbl: ast::Mutability, diff --git a/src/librustc_builtin_macros/deriving/generic/ty.rs b/src/librustc_builtin_macros/deriving/generic/ty.rs index d83c98572a272..62cbdb19a8894 100644 --- a/src/librustc_builtin_macros/deriving/generic/ty.rs +++ b/src/librustc_builtin_macros/deriving/generic/ty.rs @@ -4,11 +4,11 @@ pub use PtrTy::*; pub use Ty::*; -use rustc_ast::ast::{self, Expr, GenericArg, GenericParamKind, Generics, Ident, SelfKind}; +use rustc_ast::ast::{self, Expr, GenericArg, GenericParamKind, Generics, SelfKind}; use rustc_ast::ptr::P; use rustc_expand::base::ExtCtxt; use rustc_span::source_map::{respan, DUMMY_SP}; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; /// The types of pointers @@ -98,7 +98,7 @@ pub enum Ty<'a> { Self_, /// &/Box/ Ty Ptr(Box>, PtrTy), - /// mod::mod::Type<[lifetime], [Params...]>, including a plain type + /// `mod::mod::Type<[lifetime], [Params...]>`, including a plain type /// parameter, and things like `i32` Literal(Path<'a>), /// includes unit diff --git a/src/librustc_builtin_macros/deriving/mod.rs b/src/librustc_builtin_macros/deriving/mod.rs index b5ad67abf6201..9660cade38241 100644 --- a/src/librustc_builtin_macros/deriving/mod.rs +++ b/src/librustc_builtin_macros/deriving/mod.rs @@ -3,7 +3,7 @@ use rustc_ast::ast::{self, ItemKind, MetaItem}; use rustc_ast::ptr::P; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; macro path_local($x:ident) { @@ -154,7 +154,7 @@ fn inject_impl_of_structural_trait( let newitem = cx.item( span, - ast::Ident::invalid(), + Ident::invalid(), attrs, ItemKind::Impl { unsafety: ast::Unsafe::No, diff --git a/src/librustc_builtin_macros/env.rs b/src/librustc_builtin_macros/env.rs index fba76f8b4962f..21e1889513b01 100644 --- a/src/librustc_builtin_macros/env.rs +++ b/src/librustc_builtin_macros/env.rs @@ -3,10 +3,10 @@ // interface. // -use rustc_ast::ast::{self, GenericArg, Ident}; +use rustc_ast::ast::{self, GenericArg}; use rustc_ast::tokenstream::TokenStream; use rustc_expand::base::{self, *}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::env; diff --git a/src/librustc_builtin_macros/format.rs b/src/librustc_builtin_macros/format.rs index 2883159a9f34c..eed01b262bf0c 100644 --- a/src/librustc_builtin_macros/format.rs +++ b/src/librustc_builtin_macros/format.rs @@ -10,7 +10,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; use rustc_expand::base::{self, *}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span}; use std::borrow::Cow; @@ -108,8 +108,6 @@ struct Context<'a, 'b> { arg_spans: Vec, /// All the formatting arguments that have formatting flags set, in order for diagnostics. arg_with_formatting: Vec>, - /// Whether this formatting string is a literal or it comes from a macro. - is_literal: bool, } /// Parses the arguments from the given list of tokens, returning the diagnostic @@ -324,7 +322,7 @@ impl<'a, 'b> Context<'a, 'b> { /// format string. fn report_invalid_references(&self, numbered_position_args: bool) { let mut e; - let sp = if self.is_literal { + let sp = if !self.arg_spans.is_empty() { // Point at the formatting arguments. MultiSpan::from_spans(self.arg_spans.clone()) } else { @@ -372,7 +370,7 @@ impl<'a, 'b> Context<'a, 'b> { let reg = refs.pop().unwrap(); (format!("arguments {head} and {tail}", head = refs.join(", "), tail = reg,), pos) }; - if !self.is_literal { + if self.arg_spans.is_empty() { sp = MultiSpan::from_span(self.fmtsp); } @@ -502,11 +500,7 @@ impl<'a, 'b> Context<'a, 'b> { } None => { let msg = format!("there is no argument named `{}`", name); - let sp = if self.is_literal { - *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp) - } else { - self.fmtsp - }; + let sp = *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp); let mut err = self.ecx.struct_span_err(sp, &msg[..]); err.emit(); } @@ -535,7 +529,7 @@ impl<'a, 'b> Context<'a, 'b> { self.count_args_index_offset = sofar; } - fn rtpath(ecx: &ExtCtxt<'_>, s: &str) -> Vec { + fn rtpath(ecx: &ExtCtxt<'_>, s: &str) -> Vec { ecx.std_path(&[sym::fmt, sym::rt, sym::v1, Symbol::intern(s)]) } @@ -794,7 +788,7 @@ impl<'a, 'b> Context<'a, 'b> { macsp: Span, mut sp: Span, ty: &ArgumentType, - arg: ast::Ident, + arg: Ident, ) -> P { sp = ecx.with_def_site_ctxt(sp); let arg = ecx.expr_ident(sp, arg); @@ -892,119 +886,20 @@ pub fn expand_preparsed_format_args( } }; - let (is_literal, fmt_snippet) = match ecx.source_map().span_to_snippet(fmt_sp) { - Ok(s) => (s.starts_with('"') || s.starts_with("r#"), Some(s)), - _ => (false, None), - }; - let str_style = match fmt_style { ast::StrStyle::Cooked => None, ast::StrStyle::Raw(raw) => Some(raw as usize), }; - /// Finds the indices of all characters that have been processed and differ between the actual - /// written code (code snippet) and the `InternedString` that gets processed in the `Parser` - /// in order to properly synthethise the intra-string `Span`s for error diagnostics. - fn find_skips(snippet: &str, is_raw: bool) -> Vec { - let mut eat_ws = false; - let mut s = snippet.chars().enumerate().peekable(); - let mut skips = vec![]; - while let Some((pos, c)) = s.next() { - match (c, s.peek()) { - // skip whitespace and empty lines ending in '\\' - ('\\', Some((next_pos, '\n'))) if !is_raw => { - eat_ws = true; - skips.push(pos); - skips.push(*next_pos); - let _ = s.next(); - } - ('\\', Some((next_pos, '\n'))) - | ('\\', Some((next_pos, 'n'))) - | ('\\', Some((next_pos, 't'))) - if eat_ws => - { - skips.push(pos); - skips.push(*next_pos); - let _ = s.next(); - } - (' ', _) | ('\n', _) | ('\t', _) if eat_ws => { - skips.push(pos); - } - ('\\', Some((next_pos, 'n'))) - | ('\\', Some((next_pos, 't'))) - | ('\\', Some((next_pos, '0'))) - | ('\\', Some((next_pos, '\\'))) - | ('\\', Some((next_pos, '\''))) - | ('\\', Some((next_pos, '\"'))) => { - skips.push(*next_pos); - let _ = s.next(); - } - ('\\', Some((_, 'x'))) if !is_raw => { - for _ in 0..3 { - // consume `\xAB` literal - if let Some((pos, _)) = s.next() { - skips.push(pos); - } else { - break; - } - } - } - ('\\', Some((_, 'u'))) if !is_raw => { - if let Some((pos, _)) = s.next() { - skips.push(pos); - } - if let Some((next_pos, next_c)) = s.next() { - if next_c == '{' { - skips.push(next_pos); - let mut i = 0; // consume up to 6 hexanumeric chars + closing `}` - while let (Some((next_pos, c)), true) = (s.next(), i < 7) { - if c.is_digit(16) { - skips.push(next_pos); - } else if c == '}' { - skips.push(next_pos); - break; - } else { - break; - } - i += 1; - } - } else if next_c.is_digit(16) { - skips.push(next_pos); - // We suggest adding `{` and `}` when appropriate, accept it here as if - // it were correct - let mut i = 0; // consume up to 6 hexanumeric chars - while let (Some((next_pos, c)), _) = (s.next(), i < 6) { - if c.is_digit(16) { - skips.push(next_pos); - } else { - break; - } - i += 1; - } - } - } - } - _ if eat_ws => { - // `take_while(|c| c.is_whitespace())` - eat_ws = false; - } - _ => {} - } - } - skips - } - - let skips = if let (true, Some(ref snippet)) = (is_literal, fmt_snippet.as_ref()) { - let r_start = str_style.map(|r| r + 1).unwrap_or(0); - let r_end = str_style.map(|r| r).unwrap_or(0); - let s = &snippet[r_start + 1..snippet.len() - r_end - 1]; - find_skips(s, str_style.is_some()) - } else { - vec![] - }; - let fmt_str = &fmt_str.as_str(); // for the suggestions below - let mut parser = parse::Parser::new(fmt_str, str_style, skips, append_newline); + let fmt_snippet = ecx.source_map().span_to_snippet(fmt_sp).ok(); + let mut parser = parse::Parser::new( + fmt_str, + str_style, + fmt_snippet, + append_newline, + parse::ParseMode::Format, + ); let mut unverified_pieces = Vec::new(); while let Some(piece) = parser.next() { @@ -1057,7 +952,6 @@ pub fn expand_preparsed_format_args( invalid_refs: Vec::new(), arg_spans, arg_with_formatting: Vec::new(), - is_literal, }; // This needs to happen *after* the Parser has consumed all pieces to create all the spans diff --git a/src/librustc_builtin_macros/format_foreign.rs b/src/librustc_builtin_macros/format_foreign.rs index e6a87e4d82586..85cf4c42e9435 100644 --- a/src/librustc_builtin_macros/format_foreign.rs +++ b/src/librustc_builtin_macros/format_foreign.rs @@ -27,11 +27,8 @@ pub mod printf { } pub fn set_position(&mut self, start: usize, end: usize) { - match self { - Substitution::Format(ref mut fmt) => { - fmt.position = InnerSpan::new(start, end); - } - _ => {} + if let Substitution::Format(ref mut fmt) = self { + fmt.position = InnerSpan::new(start, end); } } @@ -152,7 +149,7 @@ pub mod printf { }; let alt = match type_ { - Some("x") | Some("X") => alt, + Some("x" | "X") => alt, _ => false, }; @@ -311,9 +308,8 @@ pub mod printf { let at = { let start = s.find('%')?; - match s[start + 1..].chars().next()? { - '%' => return Some((Substitution::Escape, &s[start + 2..])), - _ => { /* fall-through */ } + if let '%' = s[start + 1..].chars().next()? { + return Some((Substitution::Escape, &s[start + 2..])); } Cur::new_at(&s[..], start) @@ -510,7 +506,7 @@ pub mod printf { move_to!(next1); } - ('h', _) | ('l', _) | ('L', _) | ('z', _) | ('j', _) | ('t', _) | ('q', _) => { + ('h' | 'l' | 'L' | 'z' | 'j' | 't' | 'q', _) => { state = Type; length = Some(at.slice_between(next).unwrap()); move_to!(next); diff --git a/src/librustc_builtin_macros/global_allocator.rs b/src/librustc_builtin_macros/global_allocator.rs index 5d16be3206aaa..feda17c1812cb 100644 --- a/src/librustc_builtin_macros/global_allocator.rs +++ b/src/librustc_builtin_macros/global_allocator.rs @@ -1,13 +1,13 @@ use crate::util::check_builtin_macro_attribute; -use rustc_ast::ast::{self, Attribute, Expr, FnHeader, FnSig, Generics, Ident, Param}; +use rustc_ast::ast::{self, Attribute, Expr, FnHeader, FnSig, Generics, Param}; use rustc_ast::ast::{ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; use rustc_ast::expand::allocator::{ AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, }; use rustc_ast::ptr::P; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; pub fn expand( diff --git a/src/librustc_builtin_macros/global_asm.rs b/src/librustc_builtin_macros/global_asm.rs index 307e7fe71212b..2729239f62b90 100644 --- a/src/librustc_builtin_macros/global_asm.rs +++ b/src/librustc_builtin_macros/global_asm.rs @@ -15,6 +15,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_errors::DiagnosticBuilder; use rustc_expand::base::{self, *}; use rustc_span::source_map::respan; +use rustc_span::symbol::Ident; use rustc_span::Span; use smallvec::smallvec; @@ -25,7 +26,7 @@ pub fn expand_global_asm<'cx>( ) -> Box { match parse_global_asm(cx, sp, tts) { Ok(Some(global_asm)) => MacEager::items(smallvec![P(ast::Item { - ident: ast::Ident::invalid(), + ident: Ident::invalid(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, kind: ast::ItemKind::GlobalAsm(P(global_asm)), diff --git a/src/librustc_builtin_macros/lib.rs b/src/librustc_builtin_macros/lib.rs index 26a59c6b1bedb..a0f82d65618f4 100644 --- a/src/librustc_builtin_macros/lib.rs +++ b/src/librustc_builtin_macros/lib.rs @@ -6,6 +6,7 @@ #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(nll)] +#![feature(or_patterns)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] @@ -13,11 +14,10 @@ extern crate proc_macro; use crate::deriving::*; -use rustc_ast::ast::Ident; use rustc_expand::base::{MacroExpanderFn, Resolver, SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::BangProcMacro; use rustc_span::edition::Edition; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident}; mod asm; mod assert; @@ -32,6 +32,7 @@ mod format; mod format_foreign; mod global_allocator; mod global_asm; +mod llvm_asm; mod log_syntax; mod source_util; mod test; @@ -77,6 +78,7 @@ pub fn register_builtin_macros(resolver: &mut dyn Resolver, edition: Edition) { include_str: source_util::expand_include_str, include: source_util::expand_include, line: source_util::expand_line, + llvm_asm: llvm_asm::expand_llvm_asm, log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, option_env: env::expand_option_env, diff --git a/src/librustc_builtin_macros/llvm_asm.rs b/src/librustc_builtin_macros/llvm_asm.rs new file mode 100644 index 0000000000000..e12fcd98f9dfe --- /dev/null +++ b/src/librustc_builtin_macros/llvm_asm.rs @@ -0,0 +1,299 @@ +// Llvm-style inline assembly support. +// +use State::*; + +use rustc_ast::ast::{self, LlvmAsmDialect}; +use rustc_ast::ptr::P; +use rustc_ast::token::{self, Token}; +use rustc_ast::tokenstream::{self, TokenStream}; +use rustc_errors::{struct_span_err, DiagnosticBuilder, PResult}; +use rustc_expand::base::*; +use rustc_parse::parser::Parser; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::Span; + +enum State { + Asm, + Outputs, + Inputs, + Clobbers, + Options, + StateNone, +} + +impl State { + fn next(&self) -> State { + match *self { + Asm => Outputs, + Outputs => Inputs, + Inputs => Clobbers, + Clobbers => Options, + Options => StateNone, + StateNone => StateNone, + } + } +} + +const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel]; + +pub fn expand_llvm_asm<'cx>( + cx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box { + let mut inline_asm = match parse_inline_asm(cx, sp, tts) { + Ok(Some(inline_asm)) => inline_asm, + Ok(None) => return DummyResult::any(sp), + Err(mut err) => { + err.emit(); + return DummyResult::any(sp); + } + }; + + // If there are no outputs, the inline assembly is executed just for its side effects, + // so ensure that it is volatile + if inline_asm.outputs.is_empty() { + inline_asm.volatile = true; + } + + MacEager::expr(P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::LlvmInlineAsm(P(inline_asm)), + span: cx.with_def_site_ctxt(sp), + attrs: ast::AttrVec::new(), + })) +} + +fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> { + match p.parse_str_lit() { + Ok(str_lit) => Ok(str_lit.symbol_unescaped), + Err(opt_lit) => { + let span = opt_lit.map_or(p.token.span, |lit| lit.span); + let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); + err.span_label(span, "not a string literal"); + Err(err) + } + } +} + +fn parse_inline_asm<'a>( + cx: &mut ExtCtxt<'a>, + sp: Span, + tts: TokenStream, +) -> Result, DiagnosticBuilder<'a>> { + // Split the tts before the first colon, to avoid `llvm_asm!("x": y)` being + // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription. + let first_colon = tts + .trees() + .position(|tt| match tt { + tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) => true, + _ => false, + }) + .unwrap_or(tts.len()); + let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect()); + let mut asm = kw::Invalid; + let mut asm_str_style = None; + let mut outputs = Vec::new(); + let mut inputs = Vec::new(); + let mut clobs = Vec::new(); + let mut volatile = false; + let mut alignstack = false; + let mut dialect = LlvmAsmDialect::Att; + + let mut state = Asm; + + 'statement: loop { + match state { + Asm => { + if asm_str_style.is_some() { + // If we already have a string with instructions, + // ending up in Asm state again is an error. + return Err(struct_span_err!( + cx.parse_sess.span_diagnostic, + sp, + E0660, + "malformed inline assembly" + )); + } + // Nested parser, stop before the first colon (see above). + let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect()); + + if p2.token == token::Eof { + let mut err = + cx.struct_span_err(sp, "macro requires a string literal as an argument"); + err.span_label(sp, "string literal required"); + return Err(err); + } + + let expr = p2.parse_expr()?; + let (s, style) = + match expr_to_string(cx, expr, "inline assembly must be a string literal") { + Some((s, st)) => (s, st), + None => return Ok(None), + }; + + // This is most likely malformed. + if p2.token != token::Eof { + let mut extra_tts = p2.parse_all_token_trees()?; + extra_tts.extend(tts.trees().skip(first_colon)); + p = cx.new_parser_from_tts(extra_tts.into_iter().collect()); + } + + asm = s; + asm_str_style = Some(style); + } + Outputs => { + while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { + if !outputs.is_empty() { + p.eat(&token::Comma); + } + + let constraint = parse_asm_str(&mut p)?; + + let span = p.prev_token.span; + + p.expect(&token::OpenDelim(token::Paren))?; + let expr = p.parse_expr()?; + p.expect(&token::CloseDelim(token::Paren))?; + + // Expands a read+write operand into two operands. + // + // Use '+' modifier when you want the same expression + // to be both an input and an output at the same time. + // It's the opposite of '=&' which means that the memory + // cannot be shared with any other operand (usually when + // a register is clobbered early.) + let constraint_str = constraint.as_str(); + let mut ch = constraint_str.chars(); + let output = match ch.next() { + Some('=') => None, + Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))), + _ => { + struct_span_err!( + cx.parse_sess.span_diagnostic, + span, + E0661, + "output operand constraint lacks '=' or '+'" + ) + .emit(); + None + } + }; + + let is_rw = output.is_some(); + let is_indirect = constraint_str.contains('*'); + outputs.push(ast::LlvmInlineAsmOutput { + constraint: output.unwrap_or(constraint), + expr, + is_rw, + is_indirect, + }); + } + } + Inputs => { + while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { + if !inputs.is_empty() { + p.eat(&token::Comma); + } + + let constraint = parse_asm_str(&mut p)?; + + if constraint.as_str().starts_with('=') { + struct_span_err!( + cx.parse_sess.span_diagnostic, + p.prev_token.span, + E0662, + "input operand constraint contains '='" + ) + .emit(); + } else if constraint.as_str().starts_with('+') { + struct_span_err!( + cx.parse_sess.span_diagnostic, + p.prev_token.span, + E0663, + "input operand constraint contains '+'" + ) + .emit(); + } + + p.expect(&token::OpenDelim(token::Paren))?; + let input = p.parse_expr()?; + p.expect(&token::CloseDelim(token::Paren))?; + + inputs.push((constraint, input)); + } + } + Clobbers => { + while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { + if !clobs.is_empty() { + p.eat(&token::Comma); + } + + let s = parse_asm_str(&mut p)?; + + if OPTIONS.iter().any(|&opt| s == opt) { + cx.span_warn(p.prev_token.span, "expected a clobber, found an option"); + } else if s.as_str().starts_with('{') || s.as_str().ends_with('}') { + struct_span_err!( + cx.parse_sess.span_diagnostic, + p.prev_token.span, + E0664, + "clobber should not be surrounded by braces" + ) + .emit(); + } + + clobs.push(s); + } + } + Options => { + let option = parse_asm_str(&mut p)?; + + if option == sym::volatile { + // Indicates that the inline assembly has side effects + // and must not be optimized out along with its outputs. + volatile = true; + } else if option == sym::alignstack { + alignstack = true; + } else if option == sym::intel { + dialect = LlvmAsmDialect::Intel; + } else { + cx.span_warn(p.prev_token.span, "unrecognized option"); + } + + if p.token == token::Comma { + p.eat(&token::Comma); + } + } + StateNone => (), + } + + loop { + // MOD_SEP is a double colon '::' without space in between. + // When encountered, the state must be advanced twice. + match (&p.token.kind, state.next(), state.next().next()) { + (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => { + p.bump(); + break 'statement; + } + (&token::Colon, st, _) | (&token::ModSep, _, st) => { + p.bump(); + state = st; + } + (&token::Eof, ..) => break 'statement, + _ => break, + } + } + } + + Ok(Some(ast::LlvmInlineAsm { + asm, + asm_str_style: asm_str_style.unwrap(), + outputs, + inputs, + clobbers: clobs, + volatile, + alignstack, + dialect, + })) +} diff --git a/src/librustc_builtin_macros/proc_macro_harness.rs b/src/librustc_builtin_macros/proc_macro_harness.rs index 6540bcc415605..adaf5f03079eb 100644 --- a/src/librustc_builtin_macros/proc_macro_harness.rs +++ b/src/librustc_builtin_macros/proc_macro_harness.rs @@ -1,6 +1,6 @@ use std::mem; -use rustc_ast::ast::{self, Ident, NodeId}; +use rustc_ast::ast::{self, NodeId}; use rustc_ast::attr; use rustc_ast::expand::is_proc_macro_attr; use rustc_ast::ptr::P; @@ -11,17 +11,17 @@ use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_session::parse::ParseSess; use rustc_span::hygiene::AstPass; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use smallvec::smallvec; use std::cell::RefCell; struct ProcMacroDerive { id: NodeId, - trait_name: ast::Name, + trait_name: Symbol, function_name: Ident, span: Span, - attrs: Vec, + attrs: Vec, } enum ProcMacroDefType { @@ -198,7 +198,7 @@ impl<'a> CollectProcMacros<'a> { } else { "functions tagged with `#[proc_macro_derive]` must be `pub`" }; - self.handler.span_err(self.source_map.def_span(item.span), msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } @@ -217,7 +217,7 @@ impl<'a> CollectProcMacros<'a> { } else { "functions tagged with `#[proc_macro_attribute]` must be `pub`" }; - self.handler.span_err(self.source_map.def_span(item.span), msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } @@ -236,7 +236,7 @@ impl<'a> CollectProcMacros<'a> { } else { "functions tagged with `#[proc_macro]` must be `pub`" }; - self.handler.span_err(self.source_map.def_span(item.span), msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } } @@ -247,7 +247,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { let msg = "cannot export macro_rules! macros from a `proc-macro` crate type currently"; - self.handler.span_err(self.source_map.def_span(item.span), msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } @@ -298,7 +298,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { let attr = match found_attr { None => { - self.check_not_pub_in_root(&item.vis, self.source_map.def_span(item.span)); + self.check_not_pub_in_root(&item.vis, self.source_map.guess_head_span(item.span)); let prev_in_root = mem::replace(&mut self.in_root, false); visit::walk_item(self, item); self.in_root = prev_in_root; @@ -480,7 +480,7 @@ fn mk_decls( let anon_constant = cx.item_const( span, - ast::Ident::new(kw::Underscore, span), + Ident::new(kw::Underscore, span), cx.ty(span, ast::TyKind::Tup(Vec::new())), block, ); diff --git a/src/librustc_builtin_macros/standard_library_imports.rs b/src/librustc_builtin_macros/standard_library_imports.rs index f48fd6df9c98b..cd3773c76c483 100644 --- a/src/librustc_builtin_macros/standard_library_imports.rs +++ b/src/librustc_builtin_macros/standard_library_imports.rs @@ -60,17 +60,17 @@ pub fn inject( let name = names[0]; let import_path = if rust_2018 { - [name, sym::prelude, sym::v1].iter().map(|symbol| ast::Ident::new(*symbol, span)).collect() + [name, sym::prelude, sym::v1].iter().map(|symbol| Ident::new(*symbol, span)).collect() } else { [kw::PathRoot, name, sym::prelude, sym::v1] .iter() - .map(|symbol| ast::Ident::new(*symbol, span)) + .map(|symbol| Ident::new(*symbol, span)) .collect() }; let use_item = cx.item( span, - ast::Ident::invalid(), + Ident::invalid(), vec![cx.attribute(cx.meta_word(span, sym::prelude_import))], ast::ItemKind::Use(P(ast::UseTree { prefix: cx.path(span, import_path), diff --git a/src/librustc_builtin_macros/test.rs b/src/librustc_builtin_macros/test.rs index bdc4ae2fe274b..d62f34bab1a31 100644 --- a/src/librustc_builtin_macros/test.rs +++ b/src/librustc_builtin_macros/test.rs @@ -7,7 +7,7 @@ use rustc_ast::attr; use rustc_ast_pretty::pprust; use rustc_expand::base::*; use rustc_span::source_map::respan; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use std::iter; @@ -105,7 +105,7 @@ pub fn expand_test_or_bench( let (sp, attr_sp) = (cx.with_def_site_ctxt(item.span), cx.with_def_site_ctxt(attr_sp)); - let test_id = ast::Ident::new(sym::test, attr_sp); + let test_id = Ident::new(sym::test, attr_sp); // creates test::$name let test_path = |name| cx.path(sp, vec![test_id, cx.ident_of(name, sp)]); @@ -172,12 +172,12 @@ pub fn expand_test_or_bench( let mut test_const = cx.item( sp, - ast::Ident::new(item.ident.name, sp), + Ident::new(item.ident.name, sp), vec![ // #[cfg(test)] cx.attribute(attr::mk_list_item( - ast::Ident::new(sym::cfg, attr_sp), - vec![attr::mk_nested_word_item(ast::Ident::new(sym::test, attr_sp))], + Ident::new(sym::cfg, attr_sp), + vec![attr::mk_nested_word_item(Ident::new(sym::test, attr_sp))], )), // #[rustc_test_marker] cx.attribute(cx.meta_word(attr_sp, sym::rustc_test_marker)), @@ -288,7 +288,7 @@ pub fn expand_test_or_bench( ] } -fn item_path(mod_path: &[ast::Ident], item_ident: &ast::Ident) -> String { +fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String { mod_path .iter() .chain(iter::once(item_ident)) diff --git a/src/librustc_builtin_macros/test_harness.rs b/src/librustc_builtin_macros/test_harness.rs index 160a5204eaf70..34ed4c800e04f 100644 --- a/src/librustc_builtin_macros/test_harness.rs +++ b/src/librustc_builtin_macros/test_harness.rs @@ -1,7 +1,7 @@ // Code that generates a test runner to run all the tests in a crate use log::debug; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_ast::attr; use rustc_ast::entry::{self, EntryPointType}; use rustc_ast::mut_visit::{ExpectOne, *}; @@ -12,7 +12,7 @@ use rustc_feature::Features; use rustc_session::parse::ParseSess; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; use rustc_span::source_map::respan; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::PanicStrategy; use smallvec::{smallvec, SmallVec}; @@ -233,6 +233,7 @@ fn generate_test_harness( /// /// By default this expands to /// +/// ``` /// #[main] /// pub fn main() { /// extern crate test; @@ -242,6 +243,7 @@ fn generate_test_harness( /// &test_const3, /// ]); /// } +/// ``` /// /// Most of the Ident have the usual def-site hygiene for the AST pass. The /// exception is the `test_const`s. These have a syntax context that has two @@ -253,8 +255,8 @@ fn generate_test_harness( /// /// The expansion here can be controlled by two attributes: /// -/// `reexport_test_harness_main` provides a different name for the `main` -/// function and `test_runner` provides a path that replaces +/// [`TestCtxt::reexport_test_harness_main`] provides a different name for the `main` +/// function and [`TestCtxt::test_runner`] provides a path that replaces /// `test::test_main_static`. fn mk_main(cx: &mut TestCtxt<'_>) -> P { let sp = cx.def_site; diff --git a/src/librustc_codegen_llvm/Cargo.toml b/src/librustc_codegen_llvm/Cargo.toml index 16ed0854abe6e..64e66595d92d9 100644 --- a/src/librustc_codegen_llvm/Cargo.toml +++ b/src/librustc_codegen_llvm/Cargo.toml @@ -16,7 +16,7 @@ flate2 = "1.0" libc = "0.2" measureme = "0.7.1" log = "0.4" -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc-demangle = "0.1" rustc_attr = { path = "../librustc_attr" } rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } diff --git a/src/librustc_codegen_llvm/README.md b/src/librustc_codegen_llvm/README.md index 97d8f76623e93..afec60d017ee6 100644 --- a/src/librustc_codegen_llvm/README.md +++ b/src/librustc_codegen_llvm/README.md @@ -4,4 +4,4 @@ that runs towards the end of the compilation process. For more information about how codegen works, see the [rustc dev guide]. -[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/codegen.html +[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/backend/codegen.html diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 470a2bb8e1ea5..8e9c5f25ccb71 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -5,22 +5,20 @@ use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use rustc::bug; -use rustc::ty::layout::{self}; -use rustc::ty::Ty; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::MemFlags; +use rustc_middle::bug; +pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; +use rustc_middle::ty::Ty; use rustc_target::abi::call::ArgAbi; -use rustc_target::abi::{HasDataLayout, LayoutOf}; - -use libc::c_uint; - -pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; pub use rustc_target::abi::call::*; +use rustc_target::abi::{self, HasDataLayout, Int, LayoutOf}; pub use rustc_target::spec::abi::Abi; +use libc::c_uint; + macro_rules! for_each_kind { ($flags: ident, $f: ident, $($kind: ident),+) => ({ $(if $flags.contains(ArgAttribute::$kind) { $f(llvm::Attribute::$kind) })+ @@ -396,6 +394,11 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn); } + // FIXME(eddyb, wesleywiser): apply this to callsites as well? + if !self.can_unwind { + llvm::Attribute::NoUnwind.apply_llfn(llvm::AttributePlace::Function, llfn); + } + let mut i = 0; let mut apply = |attrs: &ArgAttributes, ty: Option<&Type>| { attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn, ty); @@ -431,6 +434,8 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { } fn apply_attrs_callsite(&self, bx: &mut Builder<'a, 'll, 'tcx>, callsite: &'ll Value) { + // FIXME(wesleywiser, eddyb): We should apply `nounwind` and `noreturn` as appropriate to this callsite. + let mut i = 0; let mut apply = |attrs: &ArgAttributes, ty: Option<&Type>| { attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite, ty); @@ -443,11 +448,11 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Indirect(ref attrs, _) => apply(attrs, Some(self.ret.layout.llvm_type(bx))), _ => {} } - if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi { + if let abi::Abi::Scalar(ref scalar) = self.ret.layout.abi { // 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. - if let layout::Int(..) = scalar.value { + if let Int(..) = scalar.value { if !scalar.is_bool() { let range = scalar.valid_range_exclusive(bx); if range.start != range.end { diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs index 4e7bc9fa0e2ae..bc1d9e1818c2f 100644 --- a/src/librustc_codegen_llvm/allocator.rs +++ b/src/librustc_codegen_llvm/allocator.rs @@ -1,8 +1,8 @@ use crate::attributes; use libc::c_uint; -use rustc::bug; -use rustc::ty::TyCtxt; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use rustc_middle::bug; +use rustc_middle::ty::TyCtxt; use crate::llvm::{self, False, True}; use crate::ModuleLlvm; @@ -54,7 +54,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc if tcx.sess.target.target.options.default_hidden_visibility { llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } - if tcx.sess.target.target.options.requires_uwtable { + if tcx.sess.must_emit_unwind_tables() { attributes::emit_uwtable(llfn, true); } diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs index 6edc3d5ecd477..8986ab322c07f 100644 --- a/src/librustc_codegen_llvm/asm.rs +++ b/src/librustc_codegen_llvm/asm.rs @@ -1,22 +1,30 @@ use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm; +use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; +use rustc_ast::ast::LlvmAsmDialect; +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_middle::span_bug; +use rustc_middle::ty::layout::TyAndLayout; use rustc_span::Span; +use rustc_target::abi::*; +use rustc_target::asm::*; use libc::{c_char, c_uint}; use log::debug; impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { - fn codegen_inline_asm( + fn codegen_llvm_inline_asm( &mut self, - ia: &hir::InlineAsmInner, + ia: &hir::LlvmInlineAsmInner, outputs: Vec>, mut inputs: Vec<&'ll Value>, span: Span, @@ -40,7 +48,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { indirect_outputs.push(operand.immediate()); } } else { - output_types.push(place.layout.llvm_type(self.cx())); + output_types.push(place.layout.llvm_type(self.cx)); } } if !indirect_outputs.is_empty() { @@ -89,6 +97,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { ia.volatile, ia.alignstack, ia.dialect, + span, ); if r.is_none() { return false; @@ -102,22 +111,214 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { OperandValue::Immediate(v).store(self, place); } - // Store mark in a metadata node so we can map LLVM errors - // back to source locations. See #17552. - unsafe { - let key = "srcloc"; - let kind = llvm::LLVMGetMDKindIDInContext( - self.llcx, - key.as_ptr() as *const c_char, - key.len() as c_uint, - ); + true + } - let val: &'ll Value = self.const_i32(span.ctxt().outer_expn().as_u32() as i32); + fn codegen_inline_asm( + &mut self, + template: &[InlineAsmTemplatePiece], + operands: &[InlineAsmOperandRef<'tcx, Self>], + options: InlineAsmOptions, + span: Span, + ) { + let asm_arch = self.tcx.sess.asm_arch.unwrap(); - llvm::LLVMSetMetadata(r, kind, llvm::LLVMMDNodeInContext(self.llcx, &val, 1)); + // Collect the types of output operands + let mut constraints = vec![]; + let mut output_types = vec![]; + let mut op_idx = FxHashMap::default(); + for (idx, op) in operands.iter().enumerate() { + match *op { + InlineAsmOperandRef::Out { reg, late, place } => { + let ty = if let Some(place) = place { + llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout) + } else { + // If the output is discarded, we don't really care what + // type is used. We're just using this to tell LLVM to + // reserve the register. + dummy_output_type(self.cx, reg.reg_class()) + }; + output_types.push(ty); + op_idx.insert(idx, constraints.len()); + let prefix = if late { "=" } else { "=&" }; + constraints.push(format!("{}{}", prefix, reg_to_llvm(reg))); + } + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => { + let ty = if let Some(ref out_place) = out_place { + llvm_fixup_output_type(self.cx, reg.reg_class(), &out_place.layout) + } else { + // LLVM required tied operands to have the same type, + // so we just use the type of the input. + llvm_fixup_output_type(self.cx, reg.reg_class(), &in_value.layout) + }; + output_types.push(ty); + op_idx.insert(idx, constraints.len()); + let prefix = if late { "=" } else { "=&" }; + constraints.push(format!("{}{}", prefix, reg_to_llvm(reg))); + } + _ => {} + } } - true + // Collect input operands + let mut inputs = vec![]; + for (idx, op) in operands.iter().enumerate() { + match *op { + InlineAsmOperandRef::In { reg, value } => { + let value = + llvm_fixup_input(self, value.immediate(), reg.reg_class(), &value.layout); + inputs.push(value); + op_idx.insert(idx, constraints.len()); + constraints.push(reg_to_llvm(reg)); + } + InlineAsmOperandRef::InOut { reg, late: _, in_value, out_place: _ } => { + let value = llvm_fixup_input( + self, + in_value.immediate(), + reg.reg_class(), + &in_value.layout, + ); + inputs.push(value); + constraints.push(format!("{}", op_idx[&idx])); + } + InlineAsmOperandRef::SymFn { instance } => { + inputs.push(self.cx.get_fn(instance)); + op_idx.insert(idx, constraints.len()); + constraints.push("s".to_string()); + } + InlineAsmOperandRef::SymStatic { def_id } => { + inputs.push(self.cx.get_static(def_id)); + op_idx.insert(idx, constraints.len()); + constraints.push("s".to_string()); + } + _ => {} + } + } + + // Build the template string + let mut template_str = String::new(); + for piece in template { + match *piece { + InlineAsmTemplatePiece::String(ref s) => { + if s.contains('$') { + for c in s.chars() { + if c == '$' { + template_str.push_str("$$"); + } else { + template_str.push(c); + } + } + } else { + template_str.push_str(s) + } + } + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => { + match operands[operand_idx] { + InlineAsmOperandRef::In { reg, .. } + | InlineAsmOperandRef::Out { reg, .. } + | InlineAsmOperandRef::InOut { reg, .. } => { + let modifier = modifier_to_llvm(asm_arch, reg.reg_class(), modifier); + if let Some(modifier) = modifier { + template_str.push_str(&format!( + "${{{}:{}}}", + op_idx[&operand_idx], modifier + )); + } else { + template_str.push_str(&format!("${{{}}}", op_idx[&operand_idx])); + } + } + InlineAsmOperandRef::Const { ref string } => { + // Const operands get injected directly into the template + template_str.push_str(string); + } + InlineAsmOperandRef::SymFn { .. } + | InlineAsmOperandRef::SymStatic { .. } => { + // Only emit the raw symbol name + template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); + } + } + } + } + } + + if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) { + match asm_arch { + InlineAsmArch::AArch64 | InlineAsmArch::Arm => { + constraints.push("~{cc}".to_string()); + } + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + constraints.extend_from_slice(&[ + "~{dirflag}".to_string(), + "~{fpsr}".to_string(), + "~{flags}".to_string(), + ]); + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {} + } + } + if !options.contains(InlineAsmOptions::NOMEM) { + // This is actually ignored by LLVM, but it's probably best to keep + // it just in case. LLVM instead uses the ReadOnly/ReadNone + // attributes on the call instruction to optimize. + constraints.push("~{memory}".to_string()); + } + let volatile = !options.contains(InlineAsmOptions::PURE); + let alignstack = !options.contains(InlineAsmOptions::NOSTACK); + let output_type = match &output_types[..] { + [] => self.type_void(), + [ty] => ty, + tys => self.type_struct(&tys, false), + }; + let dialect = match asm_arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 + if !options.contains(InlineAsmOptions::ATT_SYNTAX) => + { + LlvmAsmDialect::Intel + } + _ => LlvmAsmDialect::Att, + }; + let result = inline_asm_call( + self, + &template_str, + &constraints.join(","), + &inputs, + output_type, + volatile, + alignstack, + dialect, + span, + ) + .unwrap_or_else(|| span_bug!(span, "LLVM asm constraint validation failed")); + + if options.contains(InlineAsmOptions::PURE) { + if options.contains(InlineAsmOptions::NOMEM) { + llvm::Attribute::ReadNone.apply_callsite(llvm::AttributePlace::Function, result); + } else if options.contains(InlineAsmOptions::READONLY) { + llvm::Attribute::ReadOnly.apply_callsite(llvm::AttributePlace::Function, result); + } + } else { + if options.contains(InlineAsmOptions::NOMEM) { + llvm::Attribute::InaccessibleMemOnly + .apply_callsite(llvm::AttributePlace::Function, result); + } else { + // LLVM doesn't have an attribute to represent ReadOnly + SideEffect + } + } + + // Write results to outputs + for (idx, op) in operands.iter().enumerate() { + if let InlineAsmOperandRef::Out { reg, place: Some(place), .. } + | InlineAsmOperandRef::InOut { reg, out_place: Some(place), .. } = *op + { + let value = if output_types.len() == 1 { + result + } else { + self.extract_value(result, op_idx[&idx] as u64) + }; + let value = llvm_fixup_output(self, value, reg.reg_class(), &place.layout); + OperandValue::Immediate(value).store(self, place); + } + } } } @@ -138,7 +339,8 @@ fn inline_asm_call( output: &'ll llvm::Type, volatile: bool, alignstack: bool, - dia: ::rustc_ast::ast::AsmDialect, + dia: LlvmAsmDialect, + span: Span, ) -> Option<&'ll Value> { let volatile = if volatile { llvm::True } else { llvm::False }; let alignstack = if alignstack { llvm::True } else { llvm::False }; @@ -168,10 +370,339 @@ fn inline_asm_call( alignstack, llvm::AsmDialect::from_generic(dia), ); - Some(bx.call(v, inputs, None)) + let call = bx.call(v, inputs, 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() as *const c_char, + key.len() as c_uint, + ); + + let val: &'ll Value = bx.const_i32(span.ctxt().outer_expn().as_u32() as i32); + llvm::LLVMSetMetadata(call, kind, llvm::LLVMMDNodeInContext(bx.llcx, &val, 1)); + + Some(call) } else { // LLVM has detected an issue with our constraints, bail out None } } } + +/// Converts a register class to an LLVM constraint code. +fn reg_to_llvm(reg: InlineAsmRegOrRegClass) -> String { + match reg { + InlineAsmRegOrRegClass::Reg(reg) => format!("{{{}}}", reg.name()), + InlineAsmRegOrRegClass::RegClass(reg) => match reg { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => "l", + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => "t", + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => "x", + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "w", + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", + 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", + } + .to_string(), + } +} + +/// Converts a modifier into LLVM's equivalent modifier. +fn modifier_to_llvm( + arch: InlineAsmArch, + reg: InlineAsmRegClass, + modifier: Option, +) -> Option { + match reg { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier, + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) + | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { + if modifier == Some('v') { None } else { modifier } + } + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => None, + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => None, + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => Some('P'), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { + if modifier.is_none() { + Some('q') + } else { + modifier + } + } + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) + | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None, + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier { + None if arch == InlineAsmArch::X86_64 => Some('q'), + None => Some('k'), + Some('l') => Some('b'), + Some('h') => Some('h'), + Some('x') => Some('w'), + Some('e') => Some('k'), + Some('r') => Some('q'), + _ => unreachable!(), + }, + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None, + InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg) + | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) { + (X86InlineAsmRegClass::xmm_reg, None) => Some('x'), + (X86InlineAsmRegClass::ymm_reg, None) => Some('t'), + (X86InlineAsmRegClass::zmm_reg, None) => Some('g'), + (_, Some('x')) => Some('x'), + (_, Some('y')) => Some('t'), + (_, Some('z')) => Some('g'), + _ => unreachable!(), + }, + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, + } +} + +/// Type to use for outputs that are discarded. It doesn't really matter what +/// the type is, as long as it is valid for the constraint code. +fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll Type { + match reg { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) + | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { + cx.type_vector(cx.type_i64(), 2) + } + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { + cx.type_vector(cx.type_i64(), 2) + } + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), + } +} + +/// Helper function to get the LLVM type for a Scalar. Pointers are returned as +/// the equivalent integer type. +fn llvm_asm_scalar_type(cx: &CodegenCx<'ll, 'tcx>, scalar: &Scalar) -> &'ll Type { + match scalar.value { + Primitive::Int(Integer::I8, _) => cx.type_i8(), + Primitive::Int(Integer::I16, _) => cx.type_i16(), + Primitive::Int(Integer::I32, _) => cx.type_i32(), + Primitive::Int(Integer::I64, _) => cx.type_i64(), + Primitive::F32 => cx.type_f32(), + Primitive::F64 => cx.type_f64(), + Primitive::Pointer => cx.type_isize(), + _ => unreachable!(), + } +} + +/// Fix up an input value to work around LLVM bugs. +fn llvm_fixup_input( + bx: &mut Builder<'a, 'll, 'tcx>, + mut value: &'ll Value, + reg: InlineAsmRegClass, + layout: &TyAndLayout<'tcx>, +) -> &'ll Value { + match (reg, &layout.abi) { + (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { + if let Primitive::Int(Integer::I8, _) = s.value { + let vec_ty = bx.cx.type_vector(bx.cx.type_i8(), 8); + bx.insert_element(bx.const_undef(vec_ty), value, bx.const_i32(0)) + } else { + value + } + } + (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) => { + let elem_ty = llvm_asm_scalar_type(bx.cx, s); + let count = 16 / layout.size.bytes(); + let vec_ty = bx.cx.type_vector(elem_ty, count); + if let Primitive::Pointer = s.value { + value = bx.ptrtoint(value, bx.cx.type_isize()); + } + bx.insert_element(bx.const_undef(vec_ty), value, bx.const_i32(0)) + } + ( + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), + Abi::Vector { element, count }, + ) if layout.size.bytes() == 8 => { + let elem_ty = llvm_asm_scalar_type(bx.cx, element); + let vec_ty = bx.cx.type_vector(elem_ty, *count); + let indices: Vec<_> = (0..count * 2).map(|x| bx.const_i32(x as i32)).collect(); + bx.shuffle_vector(value, bx.const_undef(vec_ty), bx.const_vector(&indices)) + } + (InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) + if s.value == Primitive::F64 => + { + bx.bitcast(value, bx.cx.type_i64()) + } + ( + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), + Abi::Vector { .. }, + ) if layout.size.bytes() == 64 => bx.bitcast(value, bx.cx.type_vector(bx.cx.type_f64(), 8)), + ( + InlineAsmRegClass::Arm( + ArmInlineAsmRegClass::sreg_low16 + | ArmInlineAsmRegClass::dreg_low8 + | ArmInlineAsmRegClass::qreg_low4 + | ArmInlineAsmRegClass::dreg + | ArmInlineAsmRegClass::qreg, + ), + Abi::Scalar(s), + ) => { + if let Primitive::Int(Integer::I32, _) = s.value { + bx.bitcast(value, bx.cx.type_f32()) + } else { + value + } + } + _ => value, + } +} + +/// Fix up an output value to work around LLVM bugs. +fn llvm_fixup_output( + bx: &mut Builder<'a, 'll, 'tcx>, + mut value: &'ll Value, + reg: InlineAsmRegClass, + layout: &TyAndLayout<'tcx>, +) -> &'ll Value { + match (reg, &layout.abi) { + (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { + if let Primitive::Int(Integer::I8, _) = s.value { + bx.extract_element(value, bx.const_i32(0)) + } else { + value + } + } + (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) => { + value = bx.extract_element(value, bx.const_i32(0)); + if let Primitive::Pointer = s.value { + value = bx.inttoptr(value, layout.llvm_type(bx.cx)); + } + value + } + ( + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), + Abi::Vector { element, count }, + ) if layout.size.bytes() == 8 => { + let elem_ty = llvm_asm_scalar_type(bx.cx, element); + let vec_ty = bx.cx.type_vector(elem_ty, *count * 2); + let indices: Vec<_> = (0..*count).map(|x| bx.const_i32(x as i32)).collect(); + bx.shuffle_vector(value, bx.const_undef(vec_ty), bx.const_vector(&indices)) + } + (InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) + if s.value == Primitive::F64 => + { + bx.bitcast(value, bx.cx.type_f64()) + } + ( + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), + Abi::Vector { .. }, + ) if layout.size.bytes() == 64 => bx.bitcast(value, layout.llvm_type(bx.cx)), + ( + InlineAsmRegClass::Arm( + ArmInlineAsmRegClass::sreg_low16 + | ArmInlineAsmRegClass::dreg_low8 + | ArmInlineAsmRegClass::qreg_low4 + | ArmInlineAsmRegClass::dreg + | ArmInlineAsmRegClass::qreg, + ), + Abi::Scalar(s), + ) => { + if let Primitive::Int(Integer::I32, _) = s.value { + bx.bitcast(value, bx.cx.type_i32()) + } else { + value + } + } + _ => value, + } +} + +/// Output type to use for llvm_fixup_output. +fn llvm_fixup_output_type( + cx: &CodegenCx<'ll, 'tcx>, + reg: InlineAsmRegClass, + layout: &TyAndLayout<'tcx>, +) -> &'ll Type { + match (reg, &layout.abi) { + (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { + if let Primitive::Int(Integer::I8, _) = s.value { + cx.type_vector(cx.type_i8(), 8) + } else { + layout.llvm_type(cx) + } + } + (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) => { + let elem_ty = llvm_asm_scalar_type(cx, s); + let count = 16 / layout.size.bytes(); + cx.type_vector(elem_ty, count) + } + ( + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), + Abi::Vector { element, count }, + ) if layout.size.bytes() == 8 => { + let elem_ty = llvm_asm_scalar_type(cx, element); + cx.type_vector(elem_ty, count * 2) + } + (InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) + if s.value == Primitive::F64 => + { + cx.type_i64() + } + ( + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), + Abi::Vector { .. }, + ) if layout.size.bytes() == 64 => cx.type_vector(cx.type_f64(), 8), + ( + InlineAsmRegClass::Arm( + ArmInlineAsmRegClass::sreg_low16 + | ArmInlineAsmRegClass::dreg_low8 + | ArmInlineAsmRegClass::qreg_low4 + | ArmInlineAsmRegClass::dreg + | ArmInlineAsmRegClass::qreg, + ), + Abi::Scalar(s), + ) => { + if let Primitive::Int(Integer::I32, _) = s.value { + cx.type_f32() + } else { + layout.llvm_type(cx) + } + } + _ => layout.llvm_type(cx), + } +} diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 072607fff85c7..421c6aca1a978 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -2,26 +2,23 @@ use std::ffi::CString; -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::ty::layout::HasTyCtxt; -use rustc::ty::query::Providers; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_codegen_ssa::traits::*; use rustc_data_structures::const_cstr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::{OptLevel, Sanitizer}; use rustc_session::Session; -use rustc_target::abi::call::Conv; -use rustc_target::spec::PanicStrategy; -use crate::abi::FnAbi; use crate::attributes; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, Attribute}; use crate::llvm_util; -pub use rustc_attr::{self as attr, InlineAttr, OptimizeAttr}; +pub use rustc_attr::{InlineAttr, OptimizeAttr}; use crate::context::CodegenCx; use crate::value::Value; @@ -77,12 +74,6 @@ pub fn emit_uwtable(val: &'ll Value, emit: bool) { Attribute::UWTable.toggle_llfn(Function, val, emit); } -/// Tell LLVM whether the function can or cannot unwind. -#[inline] -fn unwind(val: &'ll Value, can_unwind: bool) { - Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind); -} - /// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue. #[inline] fn naked(val: &'ll Value, is_naked: bool) { @@ -91,21 +82,12 @@ fn naked(val: &'ll Value, is_naked: bool) { pub fn set_frame_pointer_elimination(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { if cx.sess().must_not_eliminate_frame_pointers() { - if llvm_util::get_major_version() >= 8 { - llvm::AddFunctionAttrStringValue( - llfn, - llvm::AttributePlace::Function, - const_cstr!("frame-pointer"), - const_cstr!("all"), - ); - } else { - llvm::AddFunctionAttrStringValue( - llfn, - llvm::AttributePlace::Function, - const_cstr!("no-frame-pointer-elim"), - const_cstr!("true"), - ); - } + llvm::AddFunctionAttrStringValue( + llfn, + llvm::AttributePlace::Function, + const_cstr!("frame-pointer"), + const_cstr!("all"), + ); } } @@ -142,7 +124,7 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { // sanitizer and thread sanitizer. With asan we're already protected from // stack overflow anyway so we don't really need stack probes regardless. match cx.sess().opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) | Some(Sanitizer::Thread) => return, + Some(Sanitizer::Address | Sanitizer::Thread) => return, _ => {} } @@ -246,12 +228,7 @@ pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) { /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. -pub fn from_fn_attrs( - cx: &CodegenCx<'ll, 'tcx>, - llfn: &'ll Value, - instance: ty::Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, -) { +pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::Instance<'tcx>) { let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); match codegen_fn_attrs.optimize { @@ -275,7 +252,7 @@ pub fn from_fn_attrs( inline(cx, llfn, attributes::InlineAttr::Hint); } - inline(cx, llfn, codegen_fn_attrs.inline); + inline(cx, llfn, codegen_fn_attrs.inline.clone()); // The `uwtable` attribute according to LLVM is: // @@ -293,7 +270,7 @@ pub fn from_fn_attrs( // // You can also find more info on why Windows is whitelisted here in: // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 - if !cx.sess().no_landing_pads() || cx.sess().target.target.options.requires_uwtable { + if cx.sess().must_emit_unwind_tables() { attributes::emit_uwtable(llfn, true); } @@ -307,6 +284,12 @@ pub fn from_fn_attrs( if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) { Attribute::ReturnsTwice.apply_llfn(Function, llfn); } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) { + Attribute::ReadOnly.apply_llfn(Function, llfn); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) { + Attribute::ReadNone.apply_llfn(Function, llfn); + } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { naked(llfn, true); } @@ -315,46 +298,6 @@ pub fn from_fn_attrs( } sanitize(cx, codegen_fn_attrs.flags, llfn); - unwind( - llfn, - if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind { - // In panic=abort mode we assume nothing can unwind anywhere, so - // optimize based on this! - false - } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { - // If a specific #[unwind] attribute is present, use that. - true - } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { - // Special attribute for allocator functions, which can't unwind. - false - } else { - if fn_abi.conv == Conv::Rust { - // Any Rust method (or `extern "Rust" fn` or `extern - // "rust-call" fn`) is explicitly allowed to unwind - // (unless it has no-unwind attribute, handled above). - true - } else { - // Anything else is either: - // - // 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or - // - // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`). - // - // Foreign items (case 1) are assumed to not unwind; it is - // UB otherwise. (At least for now; see also - // rust-lang/rust#63909 and Rust RFC 2753.) - // - // Items defined in Rust with non-Rust ABIs (case 2) are also - // not supposed to unwind. Whether this should be enforced - // (versus stating it is UB) and *how* it would be enforced - // is currently under discussion; see rust-lang/rust#58794. - // - // In either case, we mark item as explicitly nounwind. - false - } - }, - ); - // Always annotate functions with the target-cpu they are compiled for. // Without this, ThinLTO won't inline Rust functions into Clang generated // functions (because Clang annotates functions this way too). @@ -410,15 +353,12 @@ pub fn provide(providers: &mut Providers<'_>) { if tcx.sess.opts.actually_rustdoc { // rustdoc needs to be able to document functions that use all the features, so // whitelist them all - tcx.arena - .alloc(llvm_util::all_known_features().map(|(a, b)| (a.to_string(), b)).collect()) + llvm_util::all_known_features().map(|(a, b)| (a.to_string(), b)).collect() } else { - tcx.arena.alloc( - llvm_util::target_feature_whitelist(tcx.sess) - .iter() - .map(|&(a, b)| (a.to_string(), b)) - .collect(), - ) + llvm_util::target_feature_whitelist(tcx.sess) + .iter() + .map(|&(a, b)| (a.to_string(), b)) + .collect() } }; @@ -450,7 +390,7 @@ pub fn provide_extern(providers: &mut Providers<'_>) { })); } - tcx.arena.alloc(ret) + ret }; } diff --git a/src/librustc_codegen_llvm/back/archive.rs b/src/librustc_codegen_llvm/back/archive.rs index f1fe40d919eeb..a115a1e95163e 100644 --- a/src/librustc_codegen_llvm/back/archive.rs +++ b/src/librustc_codegen_llvm/back/archive.rs @@ -10,7 +10,7 @@ use std::str; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind}; use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder}; -use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME, RLIB_BYTECODE_EXTENSION}; +use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME}; use rustc_session::Session; use rustc_span::symbol::Symbol; @@ -129,8 +129,8 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { let obj_start = name.to_owned(); self.add_archive(rlib, move |fname: &str| { - // Ignore bytecode/metadata files, no matter the name. - if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME { + // Ignore metadata files, no matter the name. + if fname == METADATA_FILENAME { return true; } diff --git a/src/librustc_codegen_llvm/back/bytecode.rs b/src/librustc_codegen_llvm/back/bytecode.rs deleted file mode 100644 index 0c8ce39132abb..0000000000000 --- a/src/librustc_codegen_llvm/back/bytecode.rs +++ /dev/null @@ -1,141 +0,0 @@ -//! Management of the encoding of LLVM bytecode into rlibs -//! -//! This module contains the management of encoding LLVM bytecode into rlibs, -//! primarily for the usage in LTO situations. Currently the compiler will -//! unconditionally encode LLVM-IR into rlibs regardless of what's happening -//! elsewhere, so we currently compress the bytecode via deflate to avoid taking -//! up too much space on disk. -//! -//! After compressing the bytecode we then have the rest of the format to -//! basically deal with various bugs in various archive implementations. The -//! format currently is: -//! -//! RLIB LLVM-BYTECODE OBJECT LAYOUT -//! Version 2 -//! Bytes Data -//! 0..10 "RUST_OBJECT" encoded in ASCII -//! 11..14 format version as little-endian u32 -//! 15..19 the length of the module identifier string -//! 20..n the module identifier string -//! n..n+8 size in bytes of deflate compressed LLVM bitcode as -//! little-endian u64 -//! n+9.. compressed LLVM bitcode -//! ? maybe a byte to make this whole thing even length - -use std::io::{Read, Write}; -use std::ptr; -use std::str; - -use flate2::read::DeflateDecoder; -use flate2::write::DeflateEncoder; -use flate2::Compression; - -// This is the "magic number" expected at the beginning of a LLVM bytecode -// object in an rlib. -pub const RLIB_BYTECODE_OBJECT_MAGIC: &[u8] = b"RUST_OBJECT"; - -// The version number this compiler will write to bytecode objects in rlibs -pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2; - -pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec { - let mut encoded = Vec::new(); - - // Start off with the magic string - encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC); - - // Next up is the version - encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]); - - // Next is the LLVM module identifier length + contents - let identifier_len = identifier.len(); - encoded.extend_from_slice(&[ - (identifier_len >> 0) as u8, - (identifier_len >> 8) as u8, - (identifier_len >> 16) as u8, - (identifier_len >> 24) as u8, - ]); - encoded.extend_from_slice(identifier.as_bytes()); - - // Next is the LLVM module deflate compressed, prefixed with its length. We - // don't know its length yet, so fill in 0s - let deflated_size_pos = encoded.len(); - encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); - - let before = encoded.len(); - DeflateEncoder::new(&mut encoded, Compression::fast()).write_all(bytecode).unwrap(); - let after = encoded.len(); - - // Fill in the length we reserved space for before - let bytecode_len = (after - before) as u64; - encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8; - encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8; - encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8; - encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8; - encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8; - encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8; - encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8; - encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8; - - // If the number of bytes written to the object so far is odd, add a - // padding byte to make it even. This works around a crash bug in LLDB - // (see issue #15950) - if encoded.len() % 2 == 1 { - encoded.push(0); - } - - encoded -} - -pub struct DecodedBytecode<'a> { - identifier: &'a str, - encoded_bytecode: &'a [u8], -} - -impl<'a> DecodedBytecode<'a> { - pub fn new(data: &'a [u8]) -> Result, &'static str> { - if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) { - return Err("magic bytecode prefix not found"); - } - let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..]; - if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) { - return Err("wrong version prefix found in bytecode"); - } - let data = &data[4..]; - if data.len() < 4 { - return Err("bytecode corrupted"); - } - let identifier_len = - unsafe { u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize }; - let data = &data[4..]; - if data.len() < identifier_len { - return Err("bytecode corrupted"); - } - let identifier = match str::from_utf8(&data[..identifier_len]) { - Ok(s) => s, - Err(_) => return Err("bytecode corrupted"), - }; - let data = &data[identifier_len..]; - if data.len() < 8 { - return Err("bytecode corrupted"); - } - let bytecode_len = - unsafe { u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize }; - let data = &data[8..]; - if data.len() < bytecode_len { - return Err("bytecode corrupted"); - } - let encoded_bytecode = &data[..bytecode_len]; - - Ok(DecodedBytecode { identifier, encoded_bytecode }) - } - - pub fn bytecode(&self) -> Vec { - let mut data = Vec::new(); - DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap(); - data - } - - pub fn identifier(&self) -> &'a str { - self.identifier - } -} diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 1b64750f51feb..d3e3441b087c2 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -1,4 +1,3 @@ -use crate::back::bytecode::DecodedBytecode; use crate::back::write::{ self, save_temp_bitcode, to_llvm_opt_settings, with_llvm_pmb, DiagnosticHandlers, }; @@ -6,19 +5,19 @@ use crate::llvm::archive_ro::ArchiveRO; use crate::llvm::{self, False, True}; use crate::{LlvmCodegenBackend, ModuleLlvm}; use log::{debug, info}; -use rustc::bug; -use rustc::dep_graph::WorkProduct; -use rustc::middle::exported_symbols::SymbolExportLevel; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, RLIB_BYTECODE_EXTENSION}; +use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{FatalError, Handler}; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::bug; +use rustc_middle::dep_graph::WorkProduct; +use rustc_middle::middle::exported_symbols::SymbolExportLevel; use rustc_session::cgu_reuse_tracker::CguReuse; -use rustc_session::config::{self, Lto}; +use rustc_session::config::{self, CrateType, Lto}; use std::ffi::{CStr, CString}; use std::fs::File; @@ -34,13 +33,10 @@ use std::sync::Arc; /// compilation session. pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-imports.bin"; -pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { +pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { match crate_type { - config::CrateType::Executable - | config::CrateType::Staticlib - | config::CrateType::Cdylib => true, - - config::CrateType::Dylib | config::CrateType::Rlib | config::CrateType::ProcMacro => false, + CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => true, + CrateType::Dylib | CrateType::Rlib | CrateType::ProcMacro => false, } } @@ -111,22 +107,19 @@ fn prepare_lto( } let archive = ArchiveRO::open(&path).expect("wanted an rlib"); - let bytecodes = archive + let obj_files = archive .iter() .filter_map(|child| child.ok().and_then(|c| c.name().map(|name| (name, c)))) - .filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); - for (name, data) in bytecodes { - let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_lto_load_upstream_bitcode", name); - info!("adding bytecode {}", name); - let bc_encoded = data.data(); - - let (bc, id) = match DecodedBytecode::new(bc_encoded) { - Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), - Err(e) => Err(diag_handler.fatal(&e)), - }?; - let bc = SerializedModule::FromRlib(bc); - upstream_modules.push((bc, CString::new(id).unwrap())); + .filter(|&(name, _)| looks_like_rust_object_file(name)); + for (name, child) in obj_files { + info!("adding bitcode from {}", name); + match get_bitcode_slice_from_object_data(child.data()) { + Ok(data) => { + let module = SerializedModule::FromRlib(data.to_vec()); + upstream_modules.push((module, CString::new(name).unwrap())); + } + Err(msg) => return Err(diag_handler.fatal(&msg)), + } } } } @@ -134,6 +127,26 @@ fn prepare_lto( Ok((symbol_white_list, upstream_modules)) } +fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], String> { + let mut len = 0; + let data = + unsafe { llvm::LLVMRustGetBitcodeSliceFromObjectData(obj.as_ptr(), obj.len(), &mut len) }; + if !data.is_null() { + assert!(len != 0); + let bc = unsafe { slice::from_raw_parts(data, len) }; + + // `bc` must be a sub-slice of `obj`. + assert!(obj.as_ptr() <= bc.as_ptr()); + assert!(bc[bc.len()..bc.len()].as_ptr() <= obj[obj.len()..obj.len()].as_ptr()); + + Ok(bc) + } else { + assert!(len == 0); + let msg = llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string()); + Err(format!("failed to get bitcode from object file for LTO ({})", msg)) + } +} + /// Performs fat LTO by merging all modules into a single one and returning it /// for further optimization. pub(crate) fn run_fat( @@ -463,15 +476,18 @@ fn thin_lto( // If previous imports have been deleted, or we get an IO error // reading the file storing them, then we'll just use `None` as the // prev_import_map, which will force the code to be recompiled. - let prev = - if path.exists() { ThinLTOImports::load_from_file(&path).ok() } else { None }; - let curr = ThinLTOImports::from_thin_lto_data(data); + let prev = if path.exists() { + ThinLTOImportMaps::load_from_file(&path).ok() + } else { + None + }; + let curr = ThinLTOImportMaps::from_thin_lto_data(data); (Some(path), prev, curr) } else { // If we don't compile incrementally, we don't need to load the // import data from LLVM. assert!(green_modules.is_empty()); - let curr = ThinLTOImports::default(); + let curr = ThinLTOImportMaps::default(); (None, None, curr) }; info!("thin LTO import map loaded"); @@ -497,10 +513,31 @@ fn thin_lto( let module_name = module_name_to_str(module_name); // If (1.) the module hasn't changed, and (2.) none of the modules - // it imports from has changed, *and* (3.) the import-set itself has - // not changed from the previous compile when it was last - // ThinLTO'ed, then we can re-use the post-ThinLTO version of the - // module. Otherwise, freshly perform LTO optimization. + // it imports from have changed, *and* (3.) the import and export + // sets themselves have not changed from the previous compile when + // it was last ThinLTO'ed, then we can re-use the post-ThinLTO + // version of the module. Otherwise, freshly perform LTO + // optimization. + // + // (Note that globally, the export set is just the inverse of the + // import set.) + // + // For further justification of why the above is necessary and sufficient, + // see the LLVM blog post on ThinLTO: + // + // http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html + // + // which states the following: + // + // ```quote + // any particular ThinLTO backend must be redone iff: + // + // 1. The corresponding (primary) module’s bitcode changed + // 2. The list of imports into or exports from the module changed + // 3. The bitcode for any module being imported from has changed + // 4. Any global analysis result affecting either the primary module + // or anything it imports has changed. + // ``` // // This strategy means we can always save the computed imports as // canon: when we reuse the post-ThinLTO version, condition (3.) @@ -509,19 +546,25 @@ fn thin_lto( // version, the current import set *is* the correct one, since we // are doing the ThinLTO in this current compilation cycle.) // - // See rust-lang/rust#59535. + // For more discussion, see rust-lang/rust#59535 (where the import + // issue was discovered) and rust-lang/rust#69798 (where the + // analogous export issue was discovered). if let (Some(prev_import_map), true) = (prev_import_map.as_ref(), green_modules.contains_key(module_name)) { assert!(cgcx.incr_comp_session_dir.is_some()); - let prev_imports = prev_import_map.modules_imported_by(module_name); - let curr_imports = curr_import_map.modules_imported_by(module_name); + let prev_imports = prev_import_map.imports_of(module_name); + let curr_imports = curr_import_map.imports_of(module_name); + let prev_exports = prev_import_map.exports_of(module_name); + let curr_exports = curr_import_map.exports_of(module_name); let imports_all_green = curr_imports .iter() .all(|imported_module| green_modules.contains_key(imported_module)); - - if imports_all_green && equivalent_as_sets(prev_imports, curr_imports) { + if imports_all_green + && equivalent_as_sets(prev_imports, curr_imports) + && equivalent_as_sets(prev_exports, curr_exports) + { let work_product = green_modules[module_name].clone(); copy_jobs.push(work_product); info!(" - {}: re-used", module_name); @@ -881,17 +924,32 @@ pub unsafe fn optimize_thin_module( Ok(module) } +/// Summarizes module import/export relationships used by LLVM's ThinLTO pass. +/// +/// Note that we tend to have two such instances of `ThinLTOImportMaps` in use: +/// one loaded from a file that represents the relationships used during the +/// compilation associated with the incremetnal build artifacts we are +/// attempting to reuse, and another constructed via `from_thin_lto_data`, which +/// captures the relationships of ThinLTO in the current compilation. #[derive(Debug, Default)] -pub struct ThinLTOImports { +pub struct ThinLTOImportMaps { // key = llvm name of importing module, value = list of modules it imports from imports: FxHashMap>, + // key = llvm name of exporting module, value = list of modules it exports to + exports: FxHashMap>, } -impl ThinLTOImports { - fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] { +impl ThinLTOImportMaps { + /// Returns modules imported by `llvm_module_name` during some ThinLTO pass. + fn imports_of(&self, llvm_module_name: &str) -> &[String] { self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[]) } + /// Returns modules exported by `llvm_module_name` during some ThinLTO pass. + fn exports_of(&self, llvm_module_name: &str) -> &[String] { + self.exports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[]) + } + fn save_to_file(&self, path: &Path) -> io::Result<()> { use std::io::Write; let file = File::create(path)?; @@ -906,16 +964,20 @@ impl ThinLTOImports { Ok(()) } - fn load_from_file(path: &Path) -> io::Result { + fn load_from_file(path: &Path) -> io::Result { use std::io::BufRead; let mut imports = FxHashMap::default(); - let mut current_module = None; - let mut current_imports = vec![]; + let mut exports: FxHashMap<_, Vec<_>> = FxHashMap::default(); + let mut current_module: Option = None; + let mut current_imports: Vec = vec![]; let file = File::open(path)?; for line in io::BufReader::new(file).lines() { let line = line?; if line.is_empty() { let importing_module = current_module.take().expect("Importing module not set"); + for imported in ¤t_imports { + exports.entry(imported.clone()).or_default().push(importing_module.clone()); + } imports.insert(importing_module, mem::replace(&mut current_imports, vec![])); } else if line.starts_with(' ') { // Space marks an imported module @@ -927,17 +989,17 @@ impl ThinLTOImports { current_module = Some(line.trim().to_string()); } } - Ok(ThinLTOImports { imports }) + Ok(ThinLTOImportMaps { imports, exports }) } /// Loads the ThinLTO import map from ThinLTOData. - unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImports { + unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImportMaps { unsafe extern "C" fn imported_module_callback( payload: *mut libc::c_void, importing_module_name: *const libc::c_char, imported_module_name: *const libc::c_char, ) { - let map = &mut *(payload as *mut ThinLTOImports); + let map = &mut *(payload as *mut ThinLTOImportMaps); let importing_module_name = CStr::from_ptr(importing_module_name); let importing_module_name = module_name_to_str(&importing_module_name); let imported_module_name = CStr::from_ptr(imported_module_name); @@ -951,8 +1013,18 @@ impl ThinLTOImports { .get_mut(importing_module_name) .unwrap() .push(imported_module_name.to_owned()); + + if !map.exports.contains_key(imported_module_name) { + map.exports.insert(imported_module_name.to_owned(), vec![]); + } + + map.exports + .get_mut(imported_module_name) + .unwrap() + .push(importing_module_name.to_owned()); } - let mut map = ThinLTOImports::default(); + + let mut map = ThinLTOImportMaps::default(); llvm::LLVMRustGetThinLTOModuleImports( data, imported_module_callback, diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 5759eb2991a81..6ed9cd69738cc 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -1,5 +1,4 @@ use crate::attributes; -use crate::back::bytecode; use crate::back::lto::ThinBuffer; use crate::back::profiling::{ selfprofile_after_pass_callback, selfprofile_before_pass_callback, LlvmSelfProfiler, @@ -7,24 +6,25 @@ use crate::back::profiling::{ use crate::base; use crate::common; use crate::consts; -use crate::context::{get_reloc_model, is_pie_binary}; +use crate::context::all_outputs_are_pic_executables; use crate::llvm::{self, DiagnosticInfo, PassManager, SMDiagnostic}; use crate::llvm_util; use crate::type_::Type; use crate::LlvmCodegenBackend; use crate::ModuleLlvm; use log::debug; -use rustc::bug; -use rustc::ty::TyCtxt; -use rustc_codegen_ssa::back::write::{run_assembler, CodegenContext, EmbedBitcode, ModuleConfig}; +use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, RLIB_BYTECODE_EXTENSION}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_errors::{FatalError, Handler}; use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::bug; +use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, Lto, OutputType, Passes, Sanitizer, SwitchWithOptPath}; use rustc_session::Session; +use rustc_target::spec::{CodeModel, RelocModel}; use libc::{c_char, c_int, c_uint, c_void, size_t}; use std::ffi::CString; @@ -35,30 +35,6 @@ use std::slice; use std::str; use std::sync::Arc; -pub const RELOC_MODEL_ARGS: [(&str, llvm::RelocMode); 7] = [ - ("pic", llvm::RelocMode::PIC), - ("static", llvm::RelocMode::Static), - ("default", llvm::RelocMode::Default), - ("dynamic-no-pic", llvm::RelocMode::DynamicNoPic), - ("ropi", llvm::RelocMode::ROPI), - ("rwpi", llvm::RelocMode::RWPI), - ("ropi-rwpi", llvm::RelocMode::ROPI_RWPI), -]; - -pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[ - ("small", llvm::CodeModel::Small), - ("kernel", llvm::CodeModel::Kernel), - ("medium", llvm::CodeModel::Medium), - ("large", llvm::CodeModel::Large), -]; - -pub const TLS_MODEL_ARGS: [(&str, llvm::ThreadLocalMode); 4] = [ - ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), - ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), - ("initial-exec", llvm::ThreadLocalMode::InitialExec), - ("local-exec", llvm::ThreadLocalMode::LocalExec), -]; - pub fn llvm_err(handler: &rustc_errors::Handler, msg: &str) -> FatalError { match llvm::last_error() { Some(err) => handler.fatal(&format!("{}: {}", msg, err)), @@ -84,19 +60,13 @@ pub fn write_output_file( } } -pub fn create_informational_target_machine( - sess: &Session, - find_features: bool, -) -> &'static mut llvm::TargetMachine { - target_machine_factory(sess, config::OptLevel::No, find_features)() +pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm::TargetMachine { + target_machine_factory(sess, config::OptLevel::No)() .unwrap_or_else(|err| llvm_err(sess.diagnostic(), &err).raise()) } -pub fn create_target_machine( - tcx: TyCtxt<'_>, - find_features: bool, -) -> &'static mut llvm::TargetMachine { - target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE), find_features)() +pub fn create_target_machine(tcx: TyCtxt<'_>) -> &'static mut llvm::TargetMachine { + target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))() .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise()) } @@ -126,15 +96,33 @@ fn to_pass_builder_opt_level(cfg: config::OptLevel) -> llvm::PassBuilderOptLevel } } -// If find_features is true this won't access `sess.crate_types` by assuming -// that `is_pie_binary` is false. When we discover LLVM target features -// `sess.crate_types` is uninitialized so we cannot access it. +fn to_llvm_relocation_model(relocation_model: RelocModel) -> llvm::RelocModel { + match relocation_model { + RelocModel::Static => llvm::RelocModel::Static, + RelocModel::Pic => llvm::RelocModel::PIC, + RelocModel::DynamicNoPic => llvm::RelocModel::DynamicNoPic, + RelocModel::Ropi => llvm::RelocModel::ROPI, + RelocModel::Rwpi => llvm::RelocModel::RWPI, + RelocModel::RopiRwpi => llvm::RelocModel::ROPI_RWPI, + } +} + +fn to_llvm_code_model(code_model: Option) -> llvm::CodeModel { + match code_model { + Some(CodeModel::Tiny) => llvm::CodeModel::Tiny, + Some(CodeModel::Small) => llvm::CodeModel::Small, + Some(CodeModel::Kernel) => llvm::CodeModel::Kernel, + Some(CodeModel::Medium) => llvm::CodeModel::Medium, + Some(CodeModel::Large) => llvm::CodeModel::Large, + None => llvm::CodeModel::None, + } +} + pub fn target_machine_factory( sess: &Session, optlvl: config::OptLevel, - find_features: bool, ) -> Arc Result<&'static mut llvm::TargetMachine, String> + Send + Sync> { - let reloc_model = get_reloc_model(sess); + let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); let use_softfp = sess.opts.cg.soft_float; @@ -142,20 +130,7 @@ pub fn target_machine_factory( let ffunction_sections = sess.target.target.options.function_sections; let fdata_sections = ffunction_sections; - let code_model_arg = - sess.opts.cg.code_model.as_ref().or(sess.target.target.options.code_model.as_ref()); - - let code_model = match code_model_arg { - Some(s) => match CODE_GEN_MODEL_ARGS.iter().find(|arg| arg.0 == s) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid code model", code_model_arg)); - sess.abort_if_errors(); - bug!(); - } - }, - None => llvm::CodeModel::None, - }; + let code_model = to_llvm_code_model(sess.code_model()); let features = attributes::llvm_target_features(sess).collect::>(); let mut singlethread = sess.target.target.options.singlethread; @@ -175,12 +150,19 @@ pub fn target_machine_factory( let features = features.join(","); let features = CString::new(features).unwrap(); let abi = SmallCStr::new(&sess.target.target.options.llvm_abiname); - let is_pie_binary = !find_features && is_pie_binary(sess); + let pic_is_pie = all_outputs_are_pic_executables(sess); let trap_unreachable = sess.target.target.options.trap_unreachable; let emit_stack_size_section = sess.opts.debugging_opts.emit_stack_sizes; let asm_comments = sess.asm_comments(); let relax_elf_relocations = sess.target.target.options.relax_elf_relocations; + + let use_init_array = !sess + .opts + .debugging_opts + .use_ctors_section + .unwrap_or(sess.target.target.options.use_ctors_section); + Arc::new(move || { let tm = unsafe { llvm::LLVMRustCreateTargetMachine( @@ -192,7 +174,7 @@ pub fn target_machine_factory( reloc_model, opt_level, use_softfp, - is_pie_binary, + pic_is_pie, ffunction_sections, fdata_sections, trap_unreachable, @@ -200,6 +182,7 @@ pub fn target_machine_factory( asm_comments, emit_stack_size_section, relax_elf_relocations, + use_init_array, ) }; @@ -347,7 +330,7 @@ pub(crate) fn should_use_new_llvm_pass_manager(config: &ModuleConfig) -> bool { } // The new pass manager is disabled by default. - config.new_llvm_pass_manager.unwrap_or(false) + config.new_llvm_pass_manager } pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( @@ -402,6 +385,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( config.vectorize_slp, config.vectorize_loop, config.no_builtins, + config.emit_lifetime_markers, 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()), @@ -651,7 +635,7 @@ pub(crate) unsafe fn codegen( let thin = ThinBuffer::new(llmod); let data = thin.data(); - if config.emit_bc || config.obj_is_bitcode { + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { let _timer = cgcx.prof.generic_activity_with_arg( "LLVM_module_codegen_emit_bitcode", &module.name[..], @@ -662,28 +646,13 @@ pub(crate) unsafe fn codegen( } } - if config.embed_bitcode == EmbedBitcode::Full { + if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { let _timer = cgcx.prof.generic_activity_with_arg( "LLVM_module_codegen_embed_bitcode", &module.name[..], ); - embed_bitcode(cgcx, llcx, llmod, Some(data)); + embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); } - - if config.emit_bc_compressed { - let _timer = cgcx.prof.generic_activity_with_arg( - "LLVM_module_codegen_emit_compressed_bitcode", - &module.name[..], - ); - let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); - let data = bytecode::encode(&module.name, data); - if let Err(e) = fs::write(&dst, data) { - let msg = format!("failed to write bytecode to {}: {}", dst.display(), e); - diag_handler.err(&msg); - } - } - } else if config.embed_bitcode == EmbedBitcode::Marker { - embed_bitcode(cgcx, llcx, llmod, None); } if config.emit_ir { @@ -732,25 +701,28 @@ pub(crate) unsafe fn codegen( })?; } - let config_emit_normal_obj = config.emit_obj && !config.obj_is_bitcode; - - if config.emit_asm || (config_emit_normal_obj && config.no_integrated_as) { + 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); - // We can't use the same module for asm and binary output, because that triggers - // various errors like invalid IR or broken binaries, so we might have to clone the - // module to produce the asm output - let llmod = if config.emit_obj { llvm::LLVMCloneModule(llmod) } else { llmod }; + // We can't use the same module for asm and object code output, + // because that triggers various errors like invalid IR or broken + // binaries. So we must clone the module to produce the asm output + // if we are also producing object code. + let llmod = if let EmitObj::ObjectCode(_) = config.emit_obj { + llvm::LLVMCloneModule(llmod) + } else { + llmod + }; with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile) })?; } - if config_emit_normal_obj { - if !config.no_integrated_as { + match config.emit_obj { + EmitObj::ObjectCode(_) => { let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); @@ -764,41 +736,31 @@ pub(crate) unsafe fn codegen( llvm::FileType::ObjectFile, ) })?; - } else { - let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_module_codegen_asm_to_obj", &module.name[..]); - let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); - run_assembler(cgcx, diag_handler, &assembly, &obj_out); - - if !config.emit_asm && !cgcx.save_temps { - drop(fs::remove_file(&assembly)); - } } - } - if config.obj_is_bitcode { - if config.emit_obj { + EmitObj::Bitcode => { debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); if let Err(e) = link_or_copy(&bc_out, &obj_out) { diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); } - } - if !config.emit_bc { - debug!("removing_bitcode {:?}", bc_out); - if let Err(e) = fs::remove_file(&bc_out) { - diag_handler.err(&format!("failed to remove bitcode: {}", e)); + if !config.emit_bc { + debug!("removing_bitcode {:?}", bc_out); + if let Err(e) = fs::remove_file(&bc_out) { + diag_handler.err(&format!("failed to remove bitcode: {}", e)); + } } } + + EmitObj::None => {} } drop(handlers); } + Ok(module.into_compiled_module( - config.emit_obj, + config.emit_obj != EmitObj::None, config.emit_bc, - config.emit_bc_compressed, &cgcx.output_filenames, )) } @@ -813,8 +775,8 @@ pub(crate) unsafe fn codegen( /// * __LLVM,__cmdline /// /// It appears *both* of these sections are necessary to get the linker to -/// recognize what's going on. For us though we just always throw in an empty -/// cmdline section. +/// recognize what's going on. A suitable cmdline value is taken from the +/// target spec. /// /// Furthermore debug/O1 builds don't actually embed bitcode but rather just /// embed an empty section. @@ -825,9 +787,10 @@ unsafe fn embed_bitcode( cgcx: &CodegenContext, llcx: &llvm::Context, llmod: &llvm::Module, - bitcode: Option<&[u8]>, + cmdline: &str, + bitcode: &[u8], ) { - let llconst = common::bytes_in_context(llcx, bitcode.unwrap_or(&[])); + let llconst = common::bytes_in_context(llcx, bitcode); let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), @@ -836,14 +799,15 @@ unsafe fn embed_bitcode( llvm::LLVMSetInitializer(llglobal, llconst); let is_apple = cgcx.opts.target_triple.triple().contains("-ios") - || cgcx.opts.target_triple.triple().contains("-darwin"); + || cgcx.opts.target_triple.triple().contains("-darwin") + || cgcx.opts.target_triple.triple().contains("-tvos"); let section = if is_apple { "__LLVM,__bitcode\0" } else { ".llvmbc\0" }; llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::LLVMSetGlobalConstant(llglobal, llvm::True); - let llconst = common::bytes_in_context(llcx, &[]); + let llconst = common::bytes_in_context(llcx, cmdline.as_bytes()); let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), @@ -853,6 +817,57 @@ unsafe fn embed_bitcode( let section = if is_apple { "__LLVM,__cmdline\0" } else { ".llvmcmd\0" }; llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); + + // We're adding custom sections to the output object file, but we definitely + // do not want these custom sections to make their way into the final linked + // executable. The purpose of these custom sections is for tooling + // surrounding object files to work with the LLVM IR, if necessary. For + // example rustc's own LTO will look for LLVM IR inside of the object file + // in these sections by default. + // + // To handle this is a bit different depending on the object file format + // used by the backend, broken down into a few different categories: + // + // * Mach-O - this is for macOS. Inspecting the source code for the native + // linker here shows that the `.llvmbc` and `.llvmcmd` sections are + // automatically skipped by the linker. In that case there's nothing extra + // that we need to do here. + // + // * Wasm - the native LLD linker is hard-coded to skip `.llvmbc` and + // `.llvmcmd` sections, so there's nothing extra we need to do. + // + // * COFF - if we don't do anything the linker will by default copy all + // these sections to the output artifact, not what we want! To subvert + // this we want to flag the sections we inserted here as + // `IMAGE_SCN_LNK_REMOVE`. Unfortunately though LLVM has no native way to + // do this. Thankfully though we can do this with some inline assembly, + // which is easy enough to add via module-level global inline asm. + // + // * ELF - this is very similar to COFF above. One difference is that these + // sections are removed from the output linked artifact when + // `--gc-sections` is passed, which we pass by default. If that flag isn't + // passed though then these sections will show up in the final output. + // Additionally the flag that we need to set here is `SHF_EXCLUDE`. + if is_apple + || cgcx.opts.target_triple.triple().starts_with("wasm") + || cgcx.opts.target_triple.triple().starts_with("asmjs") + { + // nothing to do here + } else if cgcx.opts.target_triple.triple().contains("windows") + || cgcx.opts.target_triple.triple().contains("uefi") + { + let asm = " + .section .llvmbc,\"n\" + .section .llvmcmd,\"n\" + "; + llvm::LLVMRustAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len()); + } else { + let asm = " + .section .llvmbc,\"e\" + .section .llvmcmd,\"e\" + "; + llvm::LLVMRustAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len()); + } } pub unsafe fn with_llvm_pmb( @@ -911,10 +926,10 @@ pub unsafe fn with_llvm_pmb( llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25); } (llvm::CodeGenOptLevel::None, ..) => { - llvm::LLVMRustAddAlwaysInlinePass(builder, false); + llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers); } (llvm::CodeGenOptLevel::Less, ..) => { - llvm::LLVMRustAddAlwaysInlinePass(builder, true); + llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers); } (llvm::CodeGenOptLevel::Default, ..) => { llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 1fb1965484af2..e5f73473b72a6 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -23,17 +23,17 @@ use crate::llvm; use crate::metadata; use crate::value::Value; -use rustc::dep_graph; -use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc::middle::cstore::EncodedMetadata; -use rustc::middle::exported_symbols; -use rustc::mir::mono::{Linkage, Visibility}; -use rustc::ty::TyCtxt; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind}; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_middle::dep_graph; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::middle::exported_symbols; +use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::ty::TyCtxt; use rustc_session::config::DebugInfo; use rustc_span::symbol::Symbol; diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index b37d63fce6592..f5ae9824df894 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -7,8 +7,6 @@ use crate::type_of::LayoutLlvmExt; use crate::value::Value; use libc::{c_char, c_uint}; use log::debug; -use rustc::ty::layout::{self, Align, Size, TyLayout}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_codegen_ssa::base::to_immediate; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -18,7 +16,9 @@ use rustc_codegen_ssa::MemFlags; use rustc_data_structures::const_cstr; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_session::config::{self, Sanitizer}; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_target::abi::{self, Align, Size}; use rustc_target::spec::{HasTargetSpec, Target}; use std::borrow::Cow; use std::ffi::CStr; @@ -60,8 +60,8 @@ impl BackendTypes for Builder<'_, 'll, 'tcx> { type DIVariable = as BackendTypes>::DIVariable; } -impl ty::layout::HasDataLayout for Builder<'_, '_, '_> { - fn data_layout(&self) -> &ty::layout::TargetDataLayout { +impl abi::HasDataLayout for Builder<'_, '_, '_> { + fn data_layout(&self) -> &abi::TargetDataLayout { self.cx.data_layout() } } @@ -84,11 +84,11 @@ impl HasTargetSpec for Builder<'_, '_, 'tcx> { } } -impl ty::layout::LayoutOf for Builder<'_, '_, 'tcx> { +impl abi::LayoutOf for Builder<'_, '_, 'tcx> { type Ty = Ty<'tcx>; - type TyLayout = TyLayout<'tcx>; + type TyAndLayout = TyAndLayout<'tcx>; - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { self.cx.layout_of(ty) } } @@ -302,14 +302,14 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { lhs: Self::Value, rhs: Self::Value, ) -> (Self::Value, Self::Value) { - use rustc::ty::{Int, Uint}; use rustc_ast::ast::IntTy::*; use rustc_ast::ast::UintTy::*; + use rustc_middle::ty::{Int, Uint}; let new_kind = match ty.kind { Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.ptr_width)), Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.ptr_width)), - ref t @ Uint(_) | ref t @ Int(_) => t.clone(), + ref t @ (Uint(_) | Int(_)) => t.clone(), _ => panic!("tried to get overflow intrinsic for op applied to non-int type"), }; @@ -435,17 +435,17 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn scalar_load_metadata<'a, 'll, 'tcx>( bx: &mut Builder<'a, 'll, 'tcx>, load: &'ll Value, - scalar: &layout::Scalar, + scalar: &abi::Scalar, ) { let vr = scalar.valid_range.clone(); match scalar.value { - layout::Int(..) => { + abi::Int(..) => { let range = scalar.valid_range_exclusive(bx); if range.start != range.end { bx.range_metadata(load, range); } } - layout::Pointer if vr.start() < vr.end() && !vr.contains(&0) => { + abi::Pointer if vr.start() < vr.end() && !vr.contains(&0) => { bx.nonnull_metadata(load); } _ => {} @@ -465,16 +465,16 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } let llval = const_llval.unwrap_or_else(|| { let load = self.load(place.llval, place.align); - if let layout::Abi::Scalar(ref scalar) = place.layout.abi { + if let abi::Abi::Scalar(ref scalar) = place.layout.abi { scalar_load_metadata(self, load, scalar); } load }); OperandValue::Immediate(to_immediate(self, llval, place.layout)) - } else if let layout::Abi::ScalarPair(ref a, ref b) = place.layout.abi { + } else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi { let b_offset = a.value.size(self).align_to(b.value.align(self).abi); - let mut load = |i, scalar: &layout::Scalar, align| { + let mut load = |i, scalar: &abi::Scalar, align| { let llptr = self.struct_gep(place.llval, i as u64); let load = self.load(llptr, align); scalar_load_metadata(self, load, scalar); @@ -1242,14 +1242,7 @@ impl Builder<'a, 'll, 'tcx> { return; } - let opts = &self.cx.sess().opts; - let emit = match opts.debugging_opts.sanitizer { - // Some sanitizer use lifetime intrinsics. When they are in use, - // emit lifetime intrinsics regardless of optimization level. - Some(Sanitizer::Address) | Some(Sanitizer::Memory) => true, - _ => opts.optimize != config::OptLevel::No, - }; - if !emit { + if !self.cx().sess().emit_lifetime_markers() { return; } diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs index 04d92142266ee..6ad75cff3ddb6 100644 --- a/src/librustc_codegen_llvm/callee.rs +++ b/src/librustc_codegen_llvm/callee.rs @@ -12,8 +12,8 @@ use crate::value::Value; use log::debug; use rustc_codegen_ssa::traits::*; -use rustc::ty::layout::{FnAbiExt, HasTyCtxt}; -use rustc::ty::{Instance, TypeFoldable}; +use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; +use rustc_middle::ty::{Instance, TypeFoldable}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -29,7 +29,7 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value assert!(!instance.substs.needs_infer()); assert!(!instance.substs.has_escaping_bound_vars()); - assert!(!instance.substs.has_param_types()); + assert!(!instance.substs.has_param_types_or_consts()); if let Some(&llfn) = cx.instances.borrow().get(&instance) { return llfn; @@ -78,7 +78,7 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value let llfn = cx.declare_fn(&sym, &fn_abi); debug!("get_fn: not casting pointer!"); - attributes::from_fn_attrs(cx, llfn, instance, &fn_abi); + attributes::from_fn_attrs(cx, llfn, instance); let instance_def_id = instance.def_id(); @@ -116,7 +116,7 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value if cx.tcx.sess.opts.share_generics() { // We are in share_generics mode. - if instance_def_id.is_local() { + if let Some(instance_def_id) = instance_def_id.as_local() { // This is a definition from the current crate. If the // definition is unreachable for downstream crates or // the current crate does not re-export generics, the diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs index f72060868128c..856f989bc10a1 100644 --- a/src/librustc_codegen_llvm/common.rs +++ b/src/librustc_codegen_llvm/common.rs @@ -2,26 +2,24 @@ //! Code that is useful in various codegen modules. -use crate::consts; +use crate::consts::{self, const_alloc_to_llvm}; +pub use crate::context::CodegenCx; use crate::llvm::{self, BasicBlock, Bool, ConstantInt, False, OperandBundleDef, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use log::debug; -use rustc::bug; -use rustc_codegen_ssa::traits::*; - -use crate::consts::const_alloc_to_llvm; -use rustc::mir::interpret::{Allocation, GlobalAlloc, Scalar}; -use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Size, TyLayout}; -use rustc_codegen_ssa::mir::place::PlaceRef; - -use libc::{c_char, c_uint}; use rustc_ast::ast::Mutability; +use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::traits::*; +use rustc_middle::bug; +use rustc_middle::mir::interpret::{Allocation, GlobalAlloc, Scalar}; +use rustc_middle::ty::layout::TyAndLayout; use rustc_span::symbol::Symbol; +use rustc_target::abi::{self, HasDataLayout, LayoutOf, Pointer, Size}; -pub use crate::context::CodegenCx; +use libc::{c_char, c_uint}; +use log::debug; /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". @@ -229,12 +227,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { }) } - fn scalar_to_backend( - &self, - cv: Scalar, - layout: &layout::Scalar, - llty: &'ll Type, - ) -> &'ll Value { + fn scalar_to_backend(&self, cv: Scalar, layout: &abi::Scalar, llty: &'ll Type) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() }; match cv { Scalar::Raw { size: 0, .. } => { @@ -244,16 +237,15 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { Scalar::Raw { data, size } => { assert_eq!(size as u64, layout.value.size(self).bytes()); let llval = self.const_uint_big(self.type_ix(bitsize), data); - if layout.value == layout::Pointer { + if layout.value == Pointer { unsafe { llvm::LLVMConstIntToPtr(llval, llty) } } else { self.const_bitcast(llval, llty) } } Scalar::Ptr(ptr) => { - let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id); - let base_addr = match alloc_kind { - Some(GlobalAlloc::Memory(alloc)) => { + let base_addr = match self.tcx.global_alloc(ptr.alloc_id) { + GlobalAlloc::Memory(alloc) => { let init = const_alloc_to_llvm(self, alloc); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), @@ -264,12 +256,11 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { } value } - Some(GlobalAlloc::Function(fn_instance)) => self.get_fn_addr(fn_instance), - Some(GlobalAlloc::Static(def_id)) => { + GlobalAlloc::Function(fn_instance) => self.get_fn_addr(fn_instance), + GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); self.get_static(def_id) } - None => bug!("missing allocation {:?}", ptr.alloc_id), }; let llval = unsafe { llvm::LLVMConstInBoundsGEP( @@ -278,7 +269,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { 1, ) }; - if layout.value != layout::Pointer { + if layout.value != Pointer { unsafe { llvm::LLVMConstPtrToInt(llval, llty) } } else { self.const_bitcast(llval, llty) @@ -289,7 +280,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn from_const_alloc( &self, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, alloc: &Allocation, offset: Size, ) -> PlaceRef<'tcx, &'ll Value> { diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index 619dee2909281..9d9b53fc4a87c 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -7,19 +7,20 @@ use crate::type_of::LayoutLlvmExt; use crate::value::Value; use libc::c_uint; use log::debug; -use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc::mir::interpret::{read_target_uint, Allocation, ConstValue, ErrorHandled, Pointer}; -use rustc::mir::mono::MonoItem; -use rustc::ty::layout::{self, Align, LayoutOf, Size}; -use rustc::ty::{self, Instance, Ty}; -use rustc::{bug, span_bug}; use rustc_codegen_ssa::traits::*; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::Node; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::mir::interpret::{ + read_target_uint, Allocation, ConstValue, ErrorHandled, Pointer, +}; +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; -use rustc_target::abi::HasDataLayout; +use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size}; use std::ffi::CStr; @@ -54,7 +55,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll as u64; llvals.push(cx.scalar_to_backend( Pointer::new(alloc_id, Size::from_bytes(ptr_offset)).into(), - &layout::Scalar { value: layout::Primitive::Pointer, valid_range: 0..=!0 }, + &Scalar { value: Primitive::Pointer, valid_range: 0..=!0 }, cx.type_i8p(), )); next_offset = offset + pointer_size; @@ -208,8 +209,10 @@ impl CodegenCx<'ll, 'tcx> { debug!("get_static: sym={} instance={:?}", sym, instance); - let g = if let Some(id) = self.tcx.hir().as_local_hir_id(def_id) { + let g = if let Some(def_id) = def_id.as_local() { + let id = self.tcx.hir().as_local_hir_id(def_id); let llty = self.layout_of(ty).llvm_type(self); + // FIXME: refactor this to work without accessing the HIR let (g, attrs) = match self.tcx.hir().get(id) { Node::Item(&hir::Item { attrs, span, kind: hir::ItemKind::Static(..), .. }) => { let sym_str = sym.as_str(); @@ -435,24 +438,21 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { // // We could remove this hack whenever we decide to drop macOS 10.10 support. if self.tcx.sess.target.target.options.is_like_osx { - assert_eq!(alloc.relocations().len(), 0); - - let is_zeroed = { - // Treats undefined bytes as if they were defined with the byte value that - // happens to be currently assigned in mir. This is valid since reading - // undef bytes may yield arbitrary values. - // - // FIXME: ignore undef bytes even with representation `!= 0`. - // - // The `inspect` method is okay here because we checked relocations, and - // because we are doing this access to inspect the final interpreter state - // (not as part of the interpreter execution). - alloc + // The `inspect` method is okay here because we checked relocations, and + // because we are doing this access to inspect the final interpreter state + // (not as part of the interpreter execution). + // + // FIXME: This check requires that the (arbitrary) value of undefined bytes + // happens to be zero. Instead, we should only check the value of defined bytes + // and set all undefined bytes to zero if this allocation is headed for the + // BSS. + let all_bytes_are_zero = alloc.relocations().is_empty() + && alloc .inspect_with_undef_and_ptr_outside_interpreter(0..alloc.len()) .iter() - .all(|b| *b == 0) - }; - let sect_name = if is_zeroed { + .all(|&byte| byte == 0); + + let sect_name = if all_bytes_are_zero { CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0") } else { CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0") diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 92f59feef90fe..01f90cae7a5fc 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -6,29 +6,26 @@ use crate::llvm_util; use crate::type_::Type; use crate::value::Value; -use rustc::bug; -use rustc::dep_graph::DepGraphSafe; -use rustc::mir::mono::CodegenUnit; -use rustc::ty::layout::{ - HasParamEnv, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx, -}; -use rustc::ty::{self, Instance, Ty, TyCtxt}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n; use rustc_data_structures::const_cstr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_session::config::{self, CFGuard, DebugInfo}; +use rustc_middle::bug; +use rustc_middle::mir::mono::CodegenUnit; +use rustc_middle::ty::layout::{HasParamEnv, LayoutError, TyAndLayout}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_session::config::{CFGuard, CrateType, DebugInfo}; use rustc_session::Session; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::Symbol; -use rustc_target::spec::{HasTargetSpec, Target}; +use rustc_target::abi::{HasDataLayout, LayoutOf, PointeeInfo, Size, TargetDataLayout, VariantIdx}; +use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use std::cell::{Cell, RefCell}; use std::ffi::CStr; use std::str; -use std::sync::Arc; /// There is one `CodegenCx` per compilation unit. Each one has its own LLVM /// `llvm::Context` so that several compilation units may be optimized in parallel. @@ -41,7 +38,7 @@ pub struct CodegenCx<'ll, 'tcx> { pub llmod: &'ll llvm::Module, pub llcx: &'ll llvm::Context, - pub codegen_unit: Arc>, + pub codegen_unit: &'tcx CodegenUnit<'tcx>, /// Cache instances of monomorphic and polymorphic items pub instances: RefCell, &'ll Value>>, @@ -52,12 +49,13 @@ pub struct CodegenCx<'ll, 'tcx> { pub const_cstr_cache: RefCell>, /// Reverse-direction for const ptrs cast from globals. - /// Key is a Value holding a *T, - /// Val is a Value holding a *[T]. + /// + /// Key is a Value holding a `*T`, + /// Val is a Value holding a `*[T]`. /// /// Needed because LLVM loses pointer->pointee association /// when we ptrcast, and we have to ptrcast during codegen - /// of a [T] const because we form a slice, a (*T,usize) pair, not + /// of a `[T]` const because we form a slice, a `(*T,usize)` pair, not /// a pointer to an LLVM array type. Similar for trait objects. pub const_unsized: RefCell>, @@ -90,46 +88,24 @@ pub struct CodegenCx<'ll, 'tcx> { local_gen_sym_counter: Cell, } -impl<'ll, 'tcx> DepGraphSafe for CodegenCx<'ll, 'tcx> {} - -pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { - let reloc_model_arg = match sess.opts.cg.relocation_model { - Some(ref s) => &s[..], - None => &sess.target.target.options.relocation_model[..], - }; - - match crate::back::write::RELOC_MODEL_ARGS.iter().find(|&&arg| arg.0 == reloc_model_arg) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid relocation mode", reloc_model_arg)); - sess.abort_if_errors(); - bug!(); - } - } -} - -fn get_tls_model(sess: &Session) -> llvm::ThreadLocalMode { - let tls_model_arg = match sess.opts.debugging_opts.tls_model { - Some(ref s) => &s[..], - None => &sess.target.target.options.tls_model[..], - }; - - match crate::back::write::TLS_MODEL_ARGS.iter().find(|&&arg| arg.0 == tls_model_arg) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid TLS model", tls_model_arg)); - sess.abort_if_errors(); - bug!(); - } +fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { + match tls_model { + TlsModel::GeneralDynamic => llvm::ThreadLocalMode::GeneralDynamic, + TlsModel::LocalDynamic => llvm::ThreadLocalMode::LocalDynamic, + TlsModel::InitialExec => llvm::ThreadLocalMode::InitialExec, + TlsModel::LocalExec => llvm::ThreadLocalMode::LocalExec, } } -fn is_any_library(sess: &Session) -> bool { - sess.crate_types.borrow().iter().any(|ty| *ty != config::CrateType::Executable) -} - -pub fn is_pie_binary(sess: &Session) -> bool { - !is_any_library(sess) && get_reloc_model(sess) == llvm::RelocMode::PIC +/// PIE is potentially more effective than PIC, but can only be used in executables. +/// If all our outputs are executables, then we can relax PIC to PIE when producing object code. +/// If the list of crate types is not yet known we conservatively return `false`. +pub fn all_outputs_are_pic_executables(sess: &Session) -> bool { + sess.relocation_model() == RelocModel::Pic + && sess + .crate_types + .try_get() + .map_or(false, |crate_types| crate_types.iter().all(|ty| *ty == CrateType::Executable)) } fn strip_function_ptr_alignment(data_layout: String) -> String { @@ -162,7 +138,7 @@ pub unsafe fn create_module( // Ensure the data-layout values hardcoded remain the defaults. if sess.target.target.options.is_builtin { - let tm = crate::back::write::create_informational_target_machine(&tcx.sess, false); + let tm = crate::back::write::create_informational_target_machine(tcx.sess); llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); llvm::LLVMRustDisposeTargetMachine(tm); @@ -205,11 +181,11 @@ pub unsafe fn create_module( let llvm_target = SmallCStr::new(&sess.target.target.llvm_target); llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); - if get_reloc_model(sess) == llvm::RelocMode::PIC { + if sess.relocation_model() == RelocModel::Pic { llvm::LLVMRustSetModulePICLevel(llmod); } - if is_pie_binary(sess) { + if all_outputs_are_pic_executables(sess) { llvm::LLVMRustSetModulePIELevel(llmod); } @@ -236,7 +212,7 @@ pub unsafe fn create_module( impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { crate fn new( tcx: TyCtxt<'tcx>, - codegen_unit: Arc>, + codegen_unit: &'tcx CodegenUnit<'tcx>, llvm_module: &'ll crate::ModuleLlvm, ) -> Self { // An interesting part of Windows which MSVC forces our hand on (and @@ -286,7 +262,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let check_overflow = tcx.sess.overflow_checks(); - let tls_model = get_tls_model(&tcx.sess); + let tls_model = to_llvm_tls_model(tcx.sess.tls_model()); let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod()); @@ -381,6 +357,7 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { def_id, tcx.intern_substs(&[]), ) + .unwrap() .unwrap(), ), _ => { @@ -406,8 +383,8 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.check_overflow } - fn codegen_unit(&self) -> &Arc> { - &self.codegen_unit + fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { + self.codegen_unit } fn used_statics(&self) -> &RefCell> { @@ -820,8 +797,8 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { } } -impl ty::layout::HasDataLayout for CodegenCx<'ll, 'tcx> { - fn data_layout(&self) -> &ty::layout::TargetDataLayout { +impl HasDataLayout for CodegenCx<'ll, 'tcx> { + fn data_layout(&self) -> &TargetDataLayout { &self.tcx.data_layout } } @@ -840,13 +817,13 @@ impl ty::layout::HasTyCtxt<'tcx> for CodegenCx<'ll, 'tcx> { impl LayoutOf for CodegenCx<'ll, 'tcx> { type Ty = Ty<'tcx>; - type TyLayout = TyLayout<'tcx>; + type TyAndLayout = TyAndLayout<'tcx>; - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { self.spanned_layout_of(ty, DUMMY_SP) } - fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::TyLayout { + fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::TyAndLayout { self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap_or_else(|e| { if let LayoutError::SizeOverflow(_) = e { self.sess().span_fatal(span, &e.to_string()) diff --git a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs index 13a36c36a309a..7f47b61de3f92 100644 --- a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs +++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs @@ -6,7 +6,7 @@ use rustc_codegen_ssa::traits::*; use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::{DIScope, DISubprogram}; -use rustc::mir::{Body, SourceScope}; +use rustc_middle::mir::{Body, SourceScope}; use rustc_session::config::DebugInfo; use rustc_index::bit_set::BitSet; @@ -76,7 +76,7 @@ fn make_mir_scope( } let loc = cx.lookup_debug_loc(scope_data.span.lo()); - let file_metadata = file_metadata(cx, &loc.file.name, debug_context.defining_crate); + let file_metadata = file_metadata(cx, &loc.file, debug_context.defining_crate); let scope_metadata = unsafe { Some(llvm::LLVMRustDIBuilderCreateLexicalBlock( diff --git a/src/librustc_codegen_llvm/debuginfo/gdb.rs b/src/librustc_codegen_llvm/debuginfo/gdb.rs index dccd287a2e38c..64d4076cbf0db 100644 --- a/src/librustc_codegen_llvm/debuginfo/gdb.rs +++ b/src/librustc_codegen_llvm/debuginfo/gdb.rs @@ -5,8 +5,8 @@ use crate::llvm; use crate::builder::Builder; use crate::common::CodegenCx; use crate::value::Value; -use rustc::bug; use rustc_codegen_ssa::traits::*; +use rustc_middle::bug; use rustc_session::config::DebugInfo; use rustc_ast::attr; diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index d7a0acb133956..fb9a27ed001f4 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -16,22 +16,9 @@ use crate::llvm::debuginfo::{ DIArray, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, }; -use crate::llvm_util; use crate::value::Value; use log::debug; -use rustc::ich::NodeIdHashingMode; -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::mir::interpret::truncate; -use rustc::mir::{self, Field, GeneratorLayout}; -use rustc::ty::layout::{ - self, Align, Integer, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx, -}; -use rustc::ty::subst::{GenericArgKind, SubstsRef}; -use rustc::ty::Instance; -use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; -use rustc::{bug, span_bug}; -use rustc_ast::ast; use rustc_codegen_ssa::traits::*; use rustc_data_structures::const_cstr; use rustc_data_structures::fingerprint::Fingerprint; @@ -41,10 +28,21 @@ use rustc_fs_util::path_to_c_string; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::ich::NodeIdHashingMode; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::interpret::truncate; +use rustc_middle::mir::{self, Field, GeneratorLayout}; +use rustc_middle::ty::layout::{self, IntegerExt, PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::Instance; +use rustc_middle::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::{Interner, Symbol}; -use rustc_span::{self, FileName, Span}; -use rustc_target::abi::HasDataLayout; +use rustc_span::{self, SourceFile, SourceFileHash, Span}; +use rustc_target::abi::{Abi, Align, DiscriminantKind, HasDataLayout, Integer, LayoutOf}; +use rustc_target::abi::{Int, Pointer, F32, F64}; +use rustc_target::abi::{Primitive, Size, VariantIdx, Variants}; use libc::{c_longlong, c_uint}; use std::collections::hash_map::Entry; @@ -94,7 +92,7 @@ pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0; pub const NO_SCOPE_METADATA: Option<&DIScope> = None; #[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] -pub struct UniqueTypeId(ast::Name); +pub struct UniqueTypeId(Symbol); /// The `TypeMap` is where the `CrateDebugContext` holds the type metadata nodes /// created so far. The metadata nodes are indexed by `UniqueTypeId`, and, for @@ -751,14 +749,23 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp metadata } +fn hex_encode(data: &[u8]) -> String { + let mut hex_string = String::with_capacity(data.len() * 2); + for byte in data.iter() { + write!(&mut hex_string, "{:02x}", byte).unwrap(); + } + hex_string +} + pub fn file_metadata( cx: &CodegenCx<'ll, '_>, - file_name: &FileName, + source_file: &SourceFile, defining_crate: CrateNum, ) -> &'ll DIFile { - debug!("file_metadata: file_name: {}, defining_crate: {}", file_name, defining_crate); + debug!("file_metadata: file_name: {}, defining_crate: {}", source_file.name, defining_crate); - let file_name = Some(file_name.to_string()); + let hash = Some(&source_file.src_hash); + let file_name = Some(source_file.name.to_string()); let directory = if defining_crate == LOCAL_CRATE { Some(cx.sess().working_dir.0.to_string_lossy().to_string()) } else { @@ -766,17 +773,18 @@ pub fn file_metadata( // independent of the compiler's working directory one way or another. None }; - file_metadata_raw(cx, file_name, directory) + file_metadata_raw(cx, file_name, directory, hash) } pub fn unknown_file_metadata(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile { - file_metadata_raw(cx, None, None) + file_metadata_raw(cx, None, None, None) } fn file_metadata_raw( cx: &CodegenCx<'ll, '_>, file_name: Option, directory: Option, + hash: Option<&SourceFileHash>, ) -> &'ll DIFile { let key = (file_name, directory); @@ -789,6 +797,17 @@ fn file_metadata_raw( let file_name = file_name.as_deref().unwrap_or(""); let directory = directory.as_deref().unwrap_or(""); + let (hash_kind, hash_value) = match hash { + Some(hash) => { + let kind = match hash.kind { + rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5, + rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1, + }; + (kind, hex_encode(hash.hash_bytes())) + } + None => (llvm::ChecksumKind::None, String::new()), + }; + let file_metadata = unsafe { llvm::LLVMRustDIBuilderCreateFile( DIB(cx), @@ -796,6 +815,9 @@ fn file_metadata_raw( file_name.len(), directory.as_ptr().cast(), directory.len(), + hash_kind, + hash_value.as_ptr().cast(), + hash_value.len(), ) }; @@ -920,6 +942,9 @@ pub fn compile_unit_metadata( name_in_debuginfo.len(), work_dir.as_ptr().cast(), work_dir.len(), + llvm::ChecksumKind::None, + ptr::null(), + 0, ); let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( @@ -1203,7 +1228,7 @@ fn prepare_tuple_metadata( //=----------------------------------------------------------------------------- struct UnionMemberDescriptionFactory<'tcx> { - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, variant: &'tcx ty::VariantDef, span: Span, } @@ -1262,22 +1287,11 @@ fn prepare_union_metadata( // Enums //=----------------------------------------------------------------------------- -/// DWARF variant support is only available starting in LLVM 8. -/// Although the earlier enum debug info output did not work properly -/// in all situations, it is better for the time being to continue to -/// sometimes emit the old style rather than emit something completely -/// useless when rust is compiled against LLVM 6 or older. LLVM 7 -/// contains an early version of the DWARF variant support, and will -/// crash when handling the new debug info format. This function -/// decides which representation will be emitted. +/// DWARF variant support is only available starting in LLVM 8, but +/// on MSVC we have to use the fallback mode, because LLVM doesn't +/// lower variant parts to PDB. fn use_enum_fallback(cx: &CodegenCx<'_, '_>) -> bool { - // On MSVC we have to use the fallback mode, because LLVM doesn't - // lower variant parts to PDB. cx.sess().target.target.options.is_like_msvc - // LLVM version 7 did not release with an important bug fix; - // but the required patch is in the LLVM 8. Rust LLVM reports - // 8 as well. - || llvm_util::get_major_version() < 8 } // FIXME(eddyb) maybe precompute this? Right now it's computed once @@ -1285,7 +1299,7 @@ fn use_enum_fallback(cx: &CodegenCx<'_, '_>) -> bool { fn generator_layout_and_saved_local_names( tcx: TyCtxt<'tcx>, def_id: DefId, -) -> (&'tcx GeneratorLayout<'tcx>, IndexVec>) { +) -> (&'tcx GeneratorLayout<'tcx>, IndexVec>) { let body = tcx.optimized_mir(def_id); let generator_layout = body.generator_layout.as_ref().unwrap(); let mut generator_saved_local_names = IndexVec::from_elem(None, &generator_layout.field_tys); @@ -1325,7 +1339,7 @@ fn generator_layout_and_saved_local_names( /// offset of zero bytes). struct EnumMemberDescriptionFactory<'ll, 'tcx> { enum_type: Ty<'tcx>, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, discriminant_type_metadata: Option<&'ll DIType>, containing_scope: &'ll DIScope, span: Span, @@ -1364,7 +1378,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { }; match self.layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { if let ty::Adt(adt, _) = &self.enum_type.kind { if adt.variants.is_empty() { return vec![]; @@ -1399,8 +1413,8 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { discriminant: None, }] } - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, + Variants::Multiple { + discr_kind: DiscriminantKind::Tag, discr_index, ref variants, .. @@ -1457,9 +1471,9 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { }) .collect() } - layout::Variants::Multiple { + Variants::Multiple { discr_kind: - layout::DiscriminantKind::Niche { ref niche_variants, niche_start, dataful_variant }, + DiscriminantKind::Niche { ref niche_variants, niche_start, dataful_variant }, ref discr, ref variants, discr_index, @@ -1494,7 +1508,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { fn compute_field_path<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, name: &mut String, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, offset: Size, size: Size, ) { @@ -1592,7 +1606,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { // Creates `MemberDescription`s for the fields of a single enum variant. struct VariantMemberDescriptionFactory<'ll, 'tcx> { /// Cloned from the `layout::Struct` describing the variant. - offsets: Vec, + offsets: Vec, args: Vec<(String, Ty<'tcx>)>, discriminant_type_metadata: Option<&'ll DIType>, span: Span, @@ -1641,7 +1655,7 @@ enum VariantInfo<'a, 'tcx> { Generator { substs: SubstsRef<'tcx>, generator_layout: &'tcx GeneratorLayout<'tcx>, - generator_saved_local_names: &'a IndexVec>, + generator_saved_local_names: &'a IndexVec>, variant_index: VariantIdx, }, } @@ -1695,7 +1709,7 @@ impl<'tcx> VariantInfo<'_, 'tcx> { /// `RecursiveTypeDescription`. fn describe_enum_variant( cx: &CodegenCx<'ll, 'tcx>, - layout: layout::TyLayout<'tcx>, + layout: layout::TyAndLayout<'tcx>, variant: VariantInfo<'_, 'tcx>, discriminant_info: EnumDiscriminantInfo<'ll>, containing_scope: &'ll DIScope, @@ -1777,7 +1791,7 @@ fn prepare_enum_metadata( // let file_metadata = unknown_file_metadata(cx); - let discriminant_type_metadata = |discr: layout::Primitive| { + let discriminant_type_metadata = |discr: Primitive| { let enumerators_metadata: Vec<_> = match enum_type.kind { ty::Adt(def, _) => def .discriminants(cx.tcx) @@ -1869,30 +1883,21 @@ fn prepare_enum_metadata( let layout = cx.layout_of(enum_type); - match (&layout.abi, &layout.variants) { - ( - &layout::Abi::Scalar(_), - &layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, - ref discr, - .. - }, - ) => return FinalMetadata(discriminant_type_metadata(discr.value)), - _ => {} + if let ( + &Abi::Scalar(_), + &Variants::Multiple { discr_kind: DiscriminantKind::Tag, ref discr, .. }, + ) = (&layout.abi, &layout.variants) + { + return FinalMetadata(discriminant_type_metadata(discr.value)); } if use_enum_fallback(cx) { let discriminant_type_metadata = match layout.variants { - layout::Variants::Single { .. } - | layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Niche { .. }, - .. - } => None, - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, - ref discr, - .. - } => Some(discriminant_type_metadata(discr.value)), + Variants::Single { .. } + | Variants::Multiple { discr_kind: DiscriminantKind::Niche { .. }, .. } => None, + Variants::Multiple { discr_kind: DiscriminantKind::Tag, ref discr, .. } => { + Some(discriminant_type_metadata(discr.value)) + } }; let enum_metadata = { @@ -1940,10 +1945,10 @@ fn prepare_enum_metadata( }; let discriminator_metadata = match layout.variants { // A single-variant enum has no discriminant. - layout::Variants::Single { .. } => None, + Variants::Single { .. } => None, - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Niche { .. }, + Variants::Multiple { + discr_kind: DiscriminantKind::Niche { .. }, ref discr, discr_index, .. @@ -1953,10 +1958,10 @@ fn prepare_enum_metadata( let align = discr.value.align(cx); let discr_type = match discr.value { - layout::Int(t, _) => t, - layout::F32 => Integer::I32, - layout::F64 => Integer::I64, - layout::Pointer => cx.data_layout().ptr_sized_integer(), + Int(t, _) => t, + F32 => Integer::I32, + F64 => Integer::I64, + Pointer => cx.data_layout().ptr_sized_integer(), } .to_ty(cx.tcx, false); @@ -1978,11 +1983,8 @@ fn prepare_enum_metadata( } } - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, - ref discr, - discr_index, - .. + Variants::Multiple { + discr_kind: DiscriminantKind::Tag, ref discr, discr_index, .. } => { let discr_type = discr.value.to_ty(cx.tcx); let (size, align) = cx.size_and_align_of(discr_type); @@ -2007,8 +2009,8 @@ fn prepare_enum_metadata( }; let mut outer_fields = match layout.variants { - layout::Variants::Single { .. } => vec![], - layout::Variants::Multiple { .. } => { + Variants::Single { .. } => vec![], + Variants::Multiple { .. } => { let tuple_mdf = TupleMemberDescriptionFactory { ty: enum_type, component_types: outer_field_tys, @@ -2315,7 +2317,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global let (file_metadata, line_number) = if !span.is_dummy() { let loc = cx.lookup_debug_loc(span.lo()); - (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line) + (file_metadata(cx, &loc.file, LOCAL_CRATE), loc.line) } else { (unknown_file_metadata(cx), None) }; @@ -2424,6 +2426,6 @@ pub fn extend_scope_to_file( file: &rustc_span::SourceFile, defining_crate: CrateNum, ) -> &'ll DILexicalBlock { - let file_metadata = file_metadata(cx, &file.name, defining_crate); + let file_metadata = file_metadata(cx, &file, defining_crate); unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) } } diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 4b8140263f1f2..8c9a2c09c272c 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -8,35 +8,34 @@ use self::namespace::mangled_name_of_instance; use self::type_names::compute_debuginfo_type_name; use self::utils::{create_DIArray, is_node_local_to_unit, DIB}; +use crate::abi::FnAbi; +use crate::builder::Builder; +use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::{ DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DISPFlags, DIScope, DIType, DIVariable, }; -use rustc::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; - -use crate::abi::FnAbi; -use crate::builder::Builder; -use crate::common::CodegenCx; use crate::value::Value; -use rustc::mir; -use rustc::ty::{self, Instance, ParamEnv, Ty}; + use rustc_codegen_ssa::debuginfo::type_names; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; +use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_index::vec::IndexVec; +use rustc_middle::mir; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, Instance, ParamEnv, Ty}; use rustc_session::config::{self, DebugInfo}; +use rustc_span::symbol::Symbol; +use rustc_span::{self, BytePos, Span}; +use rustc_target::abi::{LayoutOf, Primitive, Size}; use libc::c_uint; use log::debug; -use std::cell::RefCell; - -use rustc::ty::layout::{self, HasTyCtxt, LayoutOf, Size}; -use rustc_ast::ast; -use rustc_codegen_ssa::traits::*; -use rustc_span::symbol::Symbol; -use rustc_span::{self, BytePos, Span}; use smallvec::SmallVec; +use std::cell::RefCell; mod create_scope_map; pub mod gdb; @@ -60,7 +59,7 @@ pub struct CrateDebugContext<'a, 'tcx> { llmod: &'a llvm::Module, builder: &'a mut DIBuilder<'a>, created_files: RefCell, Option), &'a DIFile>>, - created_enum_disr_types: RefCell>, + created_enum_disr_types: RefCell>, type_map: RefCell>, namespace_map: RefCell>, @@ -249,7 +248,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { let def_id = instance.def_id(); let containing_scope = get_containing_scope(self, instance); let loc = self.lookup_debug_loc(span.lo()); - let file_metadata = file_metadata(self, &loc.file.name, def_id.krate); + let file_metadata = file_metadata(self, &loc.file, def_id.krate); let function_type_metadata = unsafe { let fn_signature = get_function_signature(self, fn_abi); @@ -290,7 +289,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { spflags |= DISPFlags::SPFlagOptimized; } if let Some((id, _)) = self.tcx.entry_fn(LOCAL_CRATE) { - if id == def_id { + if id.to_def_id() == def_id { spflags |= DISPFlags::SPFlagMainSubprogram; } } @@ -529,14 +528,14 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn create_dbg_var( &self, dbg_context: &FunctionDebugContext<&'ll DIScope>, - variable_name: ast::Name, + variable_name: Symbol, variable_type: Ty<'tcx>, scope_metadata: &'ll DIScope, variable_kind: VariableKind, span: Span, ) -> &'ll DIVariable { let loc = self.lookup_debug_loc(span.lo()); - let file_metadata = file_metadata(self, &loc.file.name, dbg_context.defining_crate); + let file_metadata = file_metadata(self, &loc.file, dbg_context.defining_crate); let type_metadata = type_metadata(self, variable_type, span); diff --git a/src/librustc_codegen_llvm/debuginfo/namespace.rs b/src/librustc_codegen_llvm/debuginfo/namespace.rs index 55a3540809b48..475dea239a765 100644 --- a/src/librustc_codegen_llvm/debuginfo/namespace.rs +++ b/src/librustc_codegen_llvm/debuginfo/namespace.rs @@ -1,13 +1,13 @@ // Namespace Handling. use super::utils::{debug_context, DIB}; -use rustc::ty::{self, Instance}; +use rustc_middle::ty::{self, Instance}; use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::DIScope; -use rustc::hir::map::DefPathData; use rustc_hir::def_id::DefId; +use rustc_hir::definitions::DefPathData; pub fn mangled_name_of_instance<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, diff --git a/src/librustc_codegen_llvm/debuginfo/utils.rs b/src/librustc_codegen_llvm/debuginfo/utils.rs index b42d760a77345..ee188e69be11f 100644 --- a/src/librustc_codegen_llvm/debuginfo/utils.rs +++ b/src/librustc_codegen_llvm/debuginfo/utils.rs @@ -3,8 +3,8 @@ use super::namespace::item_namespace; use super::CrateDebugContext; -use rustc::ty::DefIdTree; use rustc_hir::def_id::DefId; +use rustc_middle::ty::DefIdTree; use crate::common::CodegenCx; use crate::llvm; diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index 236f5bb1bfdfb..26ab46bde3843 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -19,8 +19,8 @@ use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; use log::debug; -use rustc::ty::Ty; use rustc_codegen_ssa::traits::*; +use rustc_middle::ty::Ty; /// Declare a function. /// diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 95982c860f3c1..1e6d2e3dbb74e 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -2,31 +2,30 @@ use crate::abi::{Abi, FnAbi, LlvmType, PassMode}; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm; -use crate::llvm_util; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; -use rustc::ty::layout::{self, FnAbiExt, HasTyCtxt, LayoutOf, Primitive}; -use rustc::ty::{self, Ty}; -use rustc::{bug, span_bug}; + use rustc_ast::ast; use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh}; -use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; +use rustc_codegen_ssa::common::span_invalid_monomorphization_error; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::glue; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::MemFlags; use rustc_hir as hir; -use rustc_target::abi::HasDataLayout; - -use rustc_codegen_ssa::common::span_invalid_monomorphization_error; -use rustc_codegen_ssa::traits::*; - +use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; +use rustc_middle::ty::{self, Ty}; +use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use rustc_target::abi::{self, HasDataLayout, LayoutOf, Primitive}; +use rustc_target::spec::PanicStrategy; use std::cmp::Ordering; -use std::{i128, iter, u128}; +use std::iter; fn get_simple_intrinsic(cx: &CodegenCx<'ll, '_>, name: &str) -> Option<&'ll Value> { let llvm_name = match name { @@ -145,7 +144,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } "va_arg" => { match fn_abi.ret.layout.abi { - layout::Abi::Scalar(ref scalar) => { + abi::Abi::Scalar(ref scalar) => { match scalar.value { Primitive::Int(..) => { if self.cx().size_of(ret_ty).bytes() < 4 { @@ -189,11 +188,11 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } "size_of" | "pref_align_of" | "min_align_of" | "needs_drop" | "type_id" | "type_name" => { - let ty_name = self + let value = self .tcx .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) .unwrap(); - OperandRef::from_const(self, ty_name, ret_ty).immediate_or_packed_pair(self) + OperandRef::from_const(self, value, ret_ty).immediate_or_packed_pair(self) } // Effectively no-op "forget" => { @@ -462,46 +461,14 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { let is_add = name == "saturating_add"; let lhs = args[0].immediate(); let rhs = args[1].immediate(); - if llvm_util::get_major_version() >= 8 { - let llvm_name = &format!( - "llvm.{}{}.sat.i{}", - if signed { 's' } else { 'u' }, - if is_add { "add" } else { "sub" }, - width - ); - let llfn = self.get_intrinsic(llvm_name); - self.call(llfn, &[lhs, rhs], None) - } else { - let llvm_name = &format!( - "llvm.{}{}.with.overflow.i{}", - if signed { 's' } else { 'u' }, - if is_add { "add" } else { "sub" }, - width - ); - let llfn = self.get_intrinsic(llvm_name); - let pair = self.call(llfn, &[lhs, rhs], None); - let val = self.extract_value(pair, 0); - let overflow = self.extract_value(pair, 1); - let llty = self.type_ix(width); - - let limit = if signed { - let limit_lo = self - .const_uint_big(llty, (i128::MIN >> (128 - width)) as u128); - let limit_hi = self - .const_uint_big(llty, (i128::MAX >> (128 - width)) as u128); - let neg = self.icmp( - IntPredicate::IntSLT, - val, - self.const_uint(llty, 0), - ); - self.select(neg, limit_hi, limit_lo) - } else if is_add { - self.const_uint_big(llty, u128::MAX >> (128 - width)) - } else { - self.const_uint(llty, 0) - }; - self.select(overflow, limit, val) - } + let llvm_name = &format!( + "llvm.{}{}.sat.i{}", + if signed { 's' } else { 'u' }, + if is_add { "add" } else { "sub" }, + width + ); + let llfn = self.get_intrinsic(llvm_name); + self.call(llfn, &[lhs, rhs], None) } _ => bug!(), }, @@ -544,13 +511,13 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } - "float_to_int_approx_unchecked" => { + "float_to_int_unchecked" => { if float_type_width(arg_tys[0]).is_none() { span_invalid_monomorphization_error( tcx.sess, span, &format!( - "invalid monomorphization of `float_to_int_approx_unchecked` \ + "invalid monomorphization of `float_to_int_unchecked` \ intrinsic: expected basic float type, \ found `{}`", arg_tys[0] @@ -571,7 +538,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { tcx.sess, span, &format!( - "invalid monomorphization of `float_to_int_approx_unchecked` \ + "invalid monomorphization of `float_to_int_unchecked` \ intrinsic: expected basic integer type, \ found `{}`", ret_ty @@ -582,7 +549,13 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } - "discriminant_value" => args[0].deref(self.cx()).codegen_get_discr(self, ret_ty), + "discriminant_value" => { + if ret_ty.is_integral() { + args[0].deref(self.cx()).codegen_get_discr(self, ret_ty) + } else { + span_bug!(span, "Invalid discriminant type for `{:?}`", arg_tys[0]) + } + } name if name.starts_with("simd_") => { match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) { @@ -838,7 +811,7 @@ fn try_intrinsic( catch_func: &'ll Value, dest: &'ll Value, ) { - if bx.sess().no_landing_pads() { + if bx.sess().panic_strategy() == PanicStrategy::Abort { bx.call(try_func, &[data], None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. @@ -1172,8 +1145,8 @@ fn generic_simd_intrinsic( let m_len = match in_ty.kind { // Note that this `.unwrap()` crashes for isize/usize, that's sort // of intentional as there's not currently a use case for that. - ty::Int(i) => i.bit_width().unwrap() as u64, - ty::Uint(i) => i.bit_width().unwrap() as u64, + ty::Int(i) => i.bit_width().unwrap(), + ty::Uint(i) => i.bit_width().unwrap(), _ => return_error!("`{}` is not an integral type", in_ty), }; require_simd!(arg_tys[1], "argument"); @@ -1354,20 +1327,18 @@ fn generic_simd_intrinsic( // trailing bits. let expected_int_bits = in_len.max(8); match ret_ty.kind { - ty::Uint(i) if i.bit_width() == Some(expected_int_bits as usize) => (), + ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (), _ => return_error!("bitmask `{}`, expected `u{}`", ret_ty, expected_int_bits), } // Integer vector : let (i_xn, in_elem_bitwidth) = match in_elem.kind { - ty::Int(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits() as _), - ), - ty::Uint(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits() as _), - ), + ty::Int(i) => { + (args[0].immediate(), i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits())) + } + ty::Uint(i) => { + (args[0].immediate(), i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits())) + } _ => return_error!( "vector argument `{}`'s element type `{}`, expected integer element type", in_ty, @@ -1378,22 +1349,22 @@ fn generic_simd_intrinsic( // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position. let shift_indices = vec![ - bx.cx.const_int(bx.type_ix(in_elem_bitwidth as _), (in_elem_bitwidth - 1) as _); + bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); in_len as _ ]; let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice())); // Truncate vector to an - let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len as _)); + let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)); // Bitcast to iN: - let i_ = bx.bitcast(i1xn, bx.type_ix(in_len as _)); + let i_ = bx.bitcast(i1xn, bx.type_ix(in_len)); // Zero-extend iN to the bitmask type: - return Ok(bx.zext(i_, bx.type_ix(expected_int_bits as _))); + return Ok(bx.zext(i_, bx.type_ix(expected_int_bits))); } fn simd_simple_float_intrinsic( name: &str, - in_elem: &::rustc::ty::TyS<'_>, - in_ty: &::rustc::ty::TyS<'_>, + in_elem: &::rustc_middle::ty::TyS<'_>, + in_ty: &::rustc_middle::ty::TyS<'_>, in_len: u64, bx: &mut Builder<'a, 'll, 'tcx>, span: Span, @@ -2099,7 +2070,7 @@ fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, boo match ty.kind { ty::Int(t) => Some(( match t { - ast::IntTy::Isize => cx.tcx.sess.target.ptr_width as u64, + ast::IntTy::Isize => u64::from(cx.tcx.sess.target.ptr_width), ast::IntTy::I8 => 8, ast::IntTy::I16 => 16, ast::IntTy::I32 => 32, @@ -2110,7 +2081,7 @@ fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, boo )), ty::Uint(t) => Some(( match t { - ast::UintTy::Usize => cx.tcx.sess.target.ptr_width as u64, + ast::UintTy::Usize => u64::from(cx.tcx.sess.target.ptr_width), ast::UintTy::U8 => 8, ast::UintTy::U16 => 16, ast::UintTy::U32 => 32, @@ -2127,7 +2098,7 @@ fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, boo // Returns None if the type is not a float fn float_type_width(ty: Ty<'_>) -> Option { match ty.kind { - ty::Float(t) => Some(t.bit_width() as u64), + ty::Float(t) => Some(t.bit_width()), _ => None, } } diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index e36c80e15a5e0..55ee660d9f700 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -11,23 +11,23 @@ #![feature(extern_types)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![feature(trusted_len)] #![recursion_limit = "256"] use back::write::{create_informational_target_machine, create_target_machine}; pub use llvm_util::target_features; -use rustc::dep_graph::{DepGraph, WorkProduct}; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; -use rustc::ty::{self, TyCtxt}; -use rustc::util::common::ErrorReported; use rustc_ast::expand::allocator::AllocatorKind; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::{CodegenResults, CompiledModule}; -use rustc_errors::{FatalError, Handler}; +use rustc_errors::{ErrorReported, FatalError, Handler}; +use rustc_middle::dep_graph::{DepGraph, WorkProduct}; +use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_serialize::json; use rustc_session::config::{self, OptLevel, OutputFilenames, PrintRequest}; use rustc_session::Session; @@ -40,7 +40,6 @@ use std::sync::Arc; mod back { pub mod archive; - pub mod bytecode; pub mod lto; mod profiling; pub mod write; @@ -110,9 +109,8 @@ impl ExtraBackendMethods for LlvmCodegenBackend { &self, sess: &Session, optlvl: OptLevel, - find_features: bool, ) -> Arc Result<&'static mut llvm::TargetMachine, String> + Send + Sync> { - back::write::target_machine_factory(sess, optlvl, find_features) + back::write::target_machine_factory(sess, optlvl) } fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str { llvm_util::target_cpu(sess) @@ -201,21 +199,23 @@ impl CodegenBackend for LlvmCodegenBackend { match req { PrintRequest::RelocationModels => { println!("Available relocation models:"); - for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() { + for name in + &["static", "pic", "dynamic-no-pic", "ropi", "rwpi", "ropi-rwpi", "default"] + { println!(" {}", name); } println!(); } PrintRequest::CodeModels => { println!("Available code models:"); - for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter() { + for name in &["tiny", "small", "kernel", "medium", "large"] { println!(" {}", name); } println!(); } PrintRequest::TlsModels => { println!("Available TLS models:"); - for &(name, _) in back::write::TLS_MODEL_ARGS.iter() { + for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] { println!(" {}", name); } println!(); @@ -351,7 +351,7 @@ impl ModuleLlvm { unsafe { let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; - ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx, false) } + ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx) } } } @@ -359,11 +359,7 @@ impl ModuleLlvm { unsafe { let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; - ModuleLlvm { - llmod_raw, - llcx, - tm: create_informational_target_machine(&tcx.sess, false), - } + ModuleLlvm { llmod_raw, llcx, tm: create_informational_target_machine(tcx.sess) } } } diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 69881e2149bc4..9cb0f0e0c2e67 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -124,6 +124,8 @@ pub enum Attribute { NonLazyBind = 23, OptimizeNone = 24, ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, } /// LLVMIntPredicate @@ -389,10 +391,10 @@ pub enum AsmDialect { } impl AsmDialect { - pub fn from_generic(asm: rustc_ast::ast::AsmDialect) -> Self { + pub fn from_generic(asm: rustc_ast::ast::LlvmAsmDialect) -> Self { match asm { - rustc_ast::ast::AsmDialect::Att => AsmDialect::Att, - rustc_ast::ast::AsmDialect::Intel => AsmDialect::Intel, + rustc_ast::ast::LlvmAsmDialect::Att => AsmDialect::Att, + rustc_ast::ast::LlvmAsmDialect::Intel => AsmDialect::Intel, } } } @@ -445,8 +447,7 @@ pub struct SanitizerOptions { /// LLVMRelocMode #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum RelocMode { - Default, +pub enum RelocModel { Static, PIC, DynamicNoPic, @@ -459,9 +460,7 @@ pub enum RelocMode { #[derive(Copy, Clone)] #[repr(C)] pub enum CodeModel { - // FIXME: figure out if this variant is needed at all. - #[allow(dead_code)] - Other, + Tiny, Small, Kernel, Medium, @@ -546,6 +545,15 @@ pub enum ThreadLocalMode { LocalExec, } +/// LLVMRustChecksumKind +#[derive(Copy, Clone)] +#[repr(C)] +pub enum ChecksumKind { + None, + MD5, + SHA1, +} + extern "C" { type Opaque; } @@ -1640,6 +1648,9 @@ extern "C" { FilenameLen: size_t, Directory: *const c_char, DirectoryLen: size_t, + CSKind: ChecksumKind, + Checksum: *const c_char, + ChecksumLen: size_t, ) -> &'a DIFile; pub fn LLVMRustDIBuilderCreateSubroutineType( @@ -1934,7 +1945,7 @@ extern "C" { Features: *const c_char, Abi: *const c_char, Model: CodeModel, - Reloc: RelocMode, + Reloc: RelocModel, Level: CodeGenOptLevel, UseSoftFP: bool, PositionIndependentExecutable: bool, @@ -1945,6 +1956,7 @@ extern "C" { AsmComments: bool, EmitStackSizeSection: bool, RelaxELFRelocations: bool, + UseInitArray: bool, ) -> Option<&'static mut TargetMachine>; pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine); pub fn LLVMRustAddBuilderLibraryInfo( @@ -1988,6 +2000,7 @@ extern "C" { SLPVectorize: bool, LoopVectorize: bool, DisableSimplifyLibCalls: bool, + EmitLifetimeMarkers: bool, SanitizerOptions: Option<&SanitizerOptions>, PGOGenPath: *const c_char, PGOUsePath: *const c_char, @@ -2126,6 +2139,11 @@ extern "C" { len: usize, Identifier: *const c_char, ) -> Option<&Module>; + pub fn LLVMRustGetBitcodeSliceFromObjectData( + Data: *const u8, + len: usize, + out_len: &mut usize, + ) -> *const u8; pub fn LLVMRustThinLTOGetDICompileUnit( M: &Module, CU1: &mut *mut c_void, diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs index 5e924c9af8481..286d3630181cb 100644 --- a/src/librustc_codegen_llvm/llvm_util.rs +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -1,9 +1,9 @@ use crate::back::write::create_informational_target_machine; use crate::llvm; use libc::c_int; -use rustc::bug; use rustc_data_structures::fx::FxHashSet; use rustc_feature::UnstableFeatures; +use rustc_middle::bug; use rustc_session::config::PrintRequest; use rustc_session::Session; use rustc_span::symbol::sym; @@ -80,21 +80,18 @@ unsafe fn configure_llvm(sess: &Session) { if sess.print_llvm_passes() { add("-debug-pass=Structure", false); } - - if sess.opts.debugging_opts.generate_arange_section { + if !sess.opts.debugging_opts.no_generate_arange_section { add("-generate-arange-section", false); } - if get_major_version() >= 8 { - match sess - .opts - .debugging_opts - .merge_functions - .unwrap_or(sess.target.target.options.merge_functions) - { - MergeFunctions::Disabled | MergeFunctions::Trampolines => {} - MergeFunctions::Aliases => { - add("-mergefunc-use-aliases", false); - } + match sess + .opts + .debugging_opts + .merge_functions + .unwrap_or(sess.target.target.options.merge_functions) + { + MergeFunctions::Disabled | MergeFunctions::Trampolines => {} + MergeFunctions::Aliases => { + add("-mergefunc-use-aliases", false); } } @@ -239,6 +236,15 @@ const POWERPC_WHITELIST: &[(&str, Option)] = &[ const MIPS_WHITELIST: &[(&str, Option)] = &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))]; +const RISCV_WHITELIST: &[(&str, Option)] = &[ + ("m", Some(sym::riscv_target_feature)), + ("a", Some(sym::riscv_target_feature)), + ("c", Some(sym::riscv_target_feature)), + ("f", Some(sym::riscv_target_feature)), + ("d", Some(sym::riscv_target_feature)), + ("e", Some(sym::riscv_target_feature)), +]; + const WASM_WHITELIST: &[(&str, Option)] = &[("simd128", Some(sym::wasm_target_feature)), ("atomics", Some(sym::wasm_target_feature))]; @@ -256,6 +262,7 @@ pub fn all_known_features() -> impl Iterator(sess: &Session, s: &'a str) -> &'a str { } pub fn target_features(sess: &Session) -> Vec { - let target_machine = create_informational_target_machine(sess, true); + let target_machine = create_informational_target_machine(sess); target_feature_whitelist(sess) .iter() .filter_map(|&(feature, gate)| { @@ -300,6 +307,7 @@ pub fn target_feature_whitelist(sess: &Session) -> &'static [(&'static str, Opti "hexagon" => HEXAGON_WHITELIST, "mips" | "mips64" => MIPS_WHITELIST, "powerpc" | "powerpc64" => POWERPC_WHITELIST, + "riscv32" | "riscv64" => RISCV_WHITELIST, "wasm32" => WASM_WHITELIST, _ => &[], } @@ -325,7 +333,7 @@ pub fn print_passes() { pub(crate) fn print(req: PrintRequest, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess, true); + let tm = create_informational_target_machine(sess); unsafe { match req { PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm), diff --git a/src/librustc_codegen_llvm/metadata.rs b/src/librustc_codegen_llvm/metadata.rs index 0f30c2c020de7..6d0612ca075d1 100644 --- a/src/librustc_codegen_llvm/metadata.rs +++ b/src/librustc_codegen_llvm/metadata.rs @@ -1,7 +1,7 @@ use crate::llvm; use crate::llvm::archive_ro::ArchiveRO; use crate::llvm::{mk_section_iter, False, ObjectFile}; -use rustc::middle::cstore::MetadataLoader; +use rustc_middle::middle::cstore::MetadataLoader; use rustc_target::spec::Target; use log::debug; diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs index 97393e69e995f..486ea7f22dfff 100644 --- a/src/librustc_codegen_llvm/mono_item.rs +++ b/src/librustc_codegen_llvm/mono_item.rs @@ -5,13 +5,13 @@ use crate::context::CodegenCx; use crate::llvm; use crate::type_of::LayoutLlvmExt; use log::debug; -use rustc::mir::mono::{Linkage, Visibility}; -use rustc::ty::layout::{FnAbiExt, LayoutOf}; -use rustc::ty::{Instance, TypeFoldable}; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; - -pub use rustc::mir::mono::MonoItem; +pub use rustc_middle::mir::mono::MonoItem; +use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::ty::layout::FnAbiExt; +use rustc_middle::ty::{Instance, TypeFoldable}; +use rustc_target::abi::LayoutOf; impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn predefine_static( @@ -47,7 +47,7 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { visibility: Visibility, symbol_name: &str, ) { - assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types()); + assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types_or_consts()); let fn_abi = FnAbi::of_instance(self, instance, &[]); let lldecl = self.declare_fn(symbol_name, &fn_abi); @@ -77,7 +77,7 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { debug!("predefine_fn: instance = {:?}", instance); - attributes::from_fn_attrs(self, lldecl, instance, &fn_abi); + attributes::from_fn_attrs(self, lldecl, instance); self.instances.borrow_mut().insert(instance, lldecl); } diff --git a/src/librustc_codegen_llvm/type_.rs b/src/librustc_codegen_llvm/type_.rs index 5bc1475df23a7..854eff3173380 100644 --- a/src/librustc_codegen_llvm/type_.rs +++ b/src/librustc_codegen_llvm/type_.rs @@ -1,21 +1,21 @@ pub use crate::llvm::Type; +use crate::abi::{FnAbiLlvmExt, LlvmType}; +use crate::common; use crate::context::CodegenCx; use crate::llvm; use crate::llvm::{Bool, False, True}; -use crate::value::Value; -use rustc::bug; -use rustc_codegen_ssa::traits::*; - -use crate::abi::{FnAbiLlvmExt, LlvmType}; -use crate::common; use crate::type_of::LayoutLlvmExt; -use rustc::ty::layout::{self, Align, Size, TyLayout}; -use rustc::ty::Ty; +use crate::value::Value; use rustc_ast::ast; use rustc_codegen_ssa::common::TypeKind; +use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_middle::bug; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::Ty; use rustc_target::abi::call::{CastTarget, FnAbi, Reg}; +use rustc_target::abi::{Align, Integer, Size}; use std::fmt; use std::ptr; @@ -115,14 +115,14 @@ impl CodegenCx<'ll, 'tcx> { crate fn type_pointee_for_align(&self, align: Align) -> &'ll Type { // FIXME(eddyb) We could find a better approximation if ity.align < align. - let ity = layout::Integer::approximate_align(self, align); + let ity = Integer::approximate_align(self, align); self.type_from_integer(ity) } /// Return a LLVM type that has at most the required alignment, /// and exactly the required size, as a best-effort padding array. crate fn type_padding_filler(&self, size: Size, align: Align) -> &'ll Type { - let unit = layout::Integer::approximate_align(self, align); + let unit = Integer::approximate_align(self, align); let size = size.bytes(); let unit_size = unit.size().bytes(); assert_eq!(size % unit_size, 0); @@ -250,24 +250,24 @@ impl Type { } impl LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { - fn backend_type(&self, layout: TyLayout<'tcx>) -> &'ll Type { + fn backend_type(&self, layout: TyAndLayout<'tcx>) -> &'ll Type { layout.llvm_type(self) } - fn immediate_backend_type(&self, layout: TyLayout<'tcx>) -> &'ll Type { + fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> &'ll Type { layout.immediate_llvm_type(self) } - fn is_backend_immediate(&self, layout: TyLayout<'tcx>) -> bool { + fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool { layout.is_llvm_immediate() } - fn is_backend_scalar_pair(&self, layout: TyLayout<'tcx>) -> bool { + fn is_backend_scalar_pair(&self, layout: TyAndLayout<'tcx>) -> bool { layout.is_llvm_scalar_pair() } - fn backend_field_index(&self, layout: TyLayout<'tcx>, index: usize) -> u64 { + fn backend_field_index(&self, layout: TyAndLayout<'tcx>, index: usize) -> u64 { layout.llvm_field_index(index) } fn scalar_pair_element_backend_type( &self, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, index: usize, immediate: bool, ) -> &'ll Type { diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index 3e6c75e4d6f82..77009aca6d32e 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -2,23 +2,25 @@ use crate::abi::FnAbi; use crate::common::*; use crate::type_::Type; use log::debug; -use rustc::bug; -use rustc::ty::layout::{self, Align, FnAbiExt, LayoutOf, PointeeInfo, Size, TyLayout}; -use rustc::ty::print::obsolete::DefPathBasedNames; -use rustc::ty::{self, Ty, TypeFoldable}; use rustc_codegen_ssa::traits::*; -use rustc_target::abi::TyLayoutMethods; +use rustc_middle::bug; +use rustc_middle::ty::layout::{FnAbiExt, TyAndLayout}; +use rustc_middle::ty::print::obsolete::DefPathBasedNames; +use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_target::abi::{Abi, Align, FieldsShape}; +use rustc_target::abi::{Int, Pointer, F32, F64}; +use rustc_target::abi::{LayoutOf, PointeeInfo, Scalar, Size, TyAndLayoutMethods, Variants}; use std::fmt::Write; fn uncached_llvm_type<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>, - defer: &mut Option<(&'a Type, TyLayout<'tcx>)>, + layout: TyAndLayout<'tcx>, + defer: &mut Option<(&'a Type, TyAndLayout<'tcx>)>, ) -> &'a Type { match layout.abi { - layout::Abi::Scalar(_) => bug!("handled elsewhere"), - layout::Abi::Vector { ref element, count } => { + Abi::Scalar(_) => bug!("handled elsewhere"), + Abi::Vector { ref element, count } => { // LLVM has a separate type for 64-bit SIMD vectors on X86 called // `x86_mmx` which is needed for some SIMD operations. As a bit of a // hack (all SIMD definitions are super unstable anyway) we @@ -37,7 +39,7 @@ fn uncached_llvm_type<'a, 'tcx>( return cx.type_vector(element, count); } } - layout::Abi::ScalarPair(..) => { + Abi::ScalarPair(..) => { return cx.type_struct( &[ layout.scalar_pair_element_llvm_type(cx, 0, false), @@ -46,7 +48,7 @@ fn uncached_llvm_type<'a, 'tcx>( false, ); } - layout::Abi::Uninhabited | layout::Abi::Aggregate { .. } => {} + Abi::Uninhabited | Abi::Aggregate { .. } => {} } let name = match layout.ty.kind { @@ -61,14 +63,14 @@ fn uncached_llvm_type<'a, 'tcx>( let mut name = String::with_capacity(32); let printer = DefPathBasedNames::new(cx.tcx, true, true); printer.push_type_name(layout.ty, &mut name, false); - if let (&ty::Adt(def, _), &layout::Variants::Single { index }) + if let (&ty::Adt(def, _), &Variants::Single { index }) = (&layout.ty.kind, &layout.variants) { if def.is_enum() && !def.variants.is_empty() { write!(&mut name, "::{}", def.variants[index].ident).unwrap(); } } - if let (&ty::Generator(_, substs, _), &layout::Variants::Single { index }) + if let (&ty::Generator(_, substs, _), &Variants::Single { index }) = (&layout.ty.kind, &layout.variants) { write!(&mut name, "::{}", substs.as_generator().variant_name(index)).unwrap(); @@ -79,7 +81,7 @@ fn uncached_llvm_type<'a, 'tcx>( }; match layout.fields { - layout::FieldPlacement::Union(_) => { + FieldsShape::Primitive | FieldsShape::Union(_) => { let fill = cx.type_padding_filler(layout.size, layout.align.abi); let packed = false; match name { @@ -91,10 +93,8 @@ fn uncached_llvm_type<'a, 'tcx>( } } } - layout::FieldPlacement::Array { count, .. } => { - cx.type_array(layout.field(cx, 0).llvm_type(cx), count) - } - layout::FieldPlacement::Arbitrary { .. } => match name { + FieldsShape::Array { count, .. } => cx.type_array(layout.field(cx, 0).llvm_type(cx), count), + FieldsShape::Arbitrary { .. } => match name { None => { let (llfields, packed) = struct_llfields(cx, layout); cx.type_struct(&llfields, packed) @@ -110,7 +110,7 @@ fn uncached_llvm_type<'a, 'tcx>( fn struct_llfields<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> (Vec<&'a Type>, bool) { debug!("struct_llfields: {:#?}", layout); let field_count = layout.fields.count(); @@ -189,7 +189,7 @@ pub trait LayoutLlvmExt<'tcx> { fn scalar_llvm_type_at<'a>( &self, cx: &CodegenCx<'a, 'tcx>, - scalar: &layout::Scalar, + scalar: &Scalar, offset: Size, ) -> &'a Type; fn scalar_pair_element_llvm_type<'a>( @@ -202,26 +202,23 @@ pub trait LayoutLlvmExt<'tcx> { fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option; } -impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { +impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { fn is_llvm_immediate(&self) -> bool { match self.abi { - layout::Abi::Scalar(_) | layout::Abi::Vector { .. } => true, - layout::Abi::ScalarPair(..) => false, - layout::Abi::Uninhabited | layout::Abi::Aggregate { .. } => self.is_zst(), + Abi::Scalar(_) | Abi::Vector { .. } => true, + Abi::ScalarPair(..) => false, + Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(), } } fn is_llvm_scalar_pair(&self) -> bool { match self.abi { - layout::Abi::ScalarPair(..) => true, - layout::Abi::Uninhabited - | layout::Abi::Scalar(_) - | layout::Abi::Vector { .. } - | layout::Abi::Aggregate { .. } => false, + Abi::ScalarPair(..) => true, + Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } | Abi::Aggregate { .. } => false, } } - /// Gets the LLVM type corresponding to a Rust type, i.e., `rustc::ty::Ty`. + /// Gets the LLVM type corresponding to a Rust type, i.e., `rustc_middle::ty::Ty`. /// The pointee type of the pointer in `PlaceRef` is always this type. /// For sized types, it is also the right LLVM type for an `alloca` /// containing a value of that type, and most immediates (except `bool`). @@ -233,7 +230,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { /// of that field's type - this is useful for taking the address of /// that field and ensuring the struct has the right alignment. fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type { - if let layout::Abi::Scalar(ref scalar) = self.abi { + if let Abi::Scalar(ref scalar) = self.abi { // Use a different cache for scalars because pointers to DSTs // can be either fat or thin (data pointers of fat pointers). if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { @@ -255,7 +252,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { // Check the cache. let variant_index = match self.variants { - layout::Variants::Single { index } => Some(index), + Variants::Single { index } => Some(index), _ => None, }; if let Some(&llty) = cx.lltypes.borrow().get(&(self.ty, variant_index)) { @@ -293,7 +290,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { } fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type { - if let layout::Abi::Scalar(ref scalar) = self.abi { + if let Abi::Scalar(ref scalar) = self.abi { if scalar.is_bool() { return cx.type_i1(); } @@ -304,14 +301,14 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { fn scalar_llvm_type_at<'a>( &self, cx: &CodegenCx<'a, 'tcx>, - scalar: &layout::Scalar, + scalar: &Scalar, offset: Size, ) -> &'a Type { match scalar.value { - layout::Int(i, _) => cx.type_from_integer(i), - layout::F32 => cx.type_f32(), - layout::F64 => cx.type_f64(), - layout::Pointer => { + Int(i, _) => cx.type_from_integer(i), + F32 => cx.type_f32(), + F64 => cx.type_f64(), + Pointer => { // If we know the alignment, pick something better than i8. let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) { cx.type_pointee_for_align(pointee.align) @@ -343,8 +340,8 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { } let (a, b) = match self.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("TyLayout::scalar_pair_element_llty({:?}): not applicable", self), + Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self), }; let scalar = [a, b][index]; @@ -365,21 +362,19 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { fn llvm_field_index(&self, index: usize) -> u64 { match self.abi { - layout::Abi::Scalar(_) | layout::Abi::ScalarPair(..) => { - bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + Abi::Scalar(_) | Abi::ScalarPair(..) => { + bug!("TyAndLayout::llvm_field_index({:?}): not applicable", self) } _ => {} } match self.fields { - layout::FieldPlacement::Union(_) => { - bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + FieldsShape::Primitive | FieldsShape::Union(_) => { + bug!("TyAndLayout::llvm_field_index({:?}): not applicable", self) } - layout::FieldPlacement::Array { .. } => index as u64, + FieldsShape::Array { .. } => index as u64, - layout::FieldPlacement::Arbitrary { .. } => { - 1 + (self.fields.memory_index(index) as u64) * 2 - } + FieldsShape::Arbitrary { .. } => 1 + (self.fields.memory_index(index) as u64) * 2, } } diff --git a/src/librustc_codegen_llvm/va_arg.rs b/src/librustc_codegen_llvm/va_arg.rs index a552f2cdb78aa..8bc3579800ea8 100644 --- a/src/librustc_codegen_llvm/va_arg.rs +++ b/src/librustc_codegen_llvm/va_arg.rs @@ -2,12 +2,13 @@ use crate::builder::Builder; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use rustc::ty::layout::{Align, HasDataLayout, HasTyCtxt, LayoutOf, Size}; -use rustc::ty::Ty; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::traits::{ BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods, }; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::Ty; +use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size}; #[allow(dead_code)] fn round_pointer_up_to_alignment( diff --git a/src/librustc_codegen_ssa/Cargo.toml b/src/librustc_codegen_ssa/Cargo.toml index 299f4d2c66998..e8bfc87aef5ea 100644 --- a/src/librustc_codegen_ssa/Cargo.toml +++ b/src/librustc_codegen_ssa/Cargo.toml @@ -15,14 +15,14 @@ cc = "1.0.1" num_cpus = "1.0" memmap = "0.7" log = "0.4.5" -libc = "0.2.44" +libc = "0.2.50" jobserver = "0.1.11" tempfile = "3.1" rustc_serialize = { path = "../libserialize", package = "serialize" } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_apfloat = { path = "../librustc_apfloat" } rustc_attr = { path = "../librustc_attr" } rustc_symbol_mangling = { path = "../librustc_symbol_mangling" } @@ -34,4 +34,3 @@ rustc_incremental = { path = "../librustc_incremental" } rustc_index = { path = "../librustc_index" } rustc_target = { path = "../librustc_target" } rustc_session = { path = "../librustc_session" } -rustc_metadata = { path = "../librustc_metadata" } diff --git a/src/librustc_codegen_ssa/README.md b/src/librustc_codegen_ssa/README.md index 90d991a3a4b25..7b770187b75ae 100644 --- a/src/librustc_codegen_ssa/README.md +++ b/src/librustc_codegen_ssa/README.md @@ -1,3 +1,3 @@ Please read the rustc-dev-guide chapter on [Backend Agnostic Codegen][bac]. -[bac]: https://rustc-dev-guide.rust-lang.org/codegen/backend-agnostic.html +[bac]: https://rustc-dev-guide.rust-lang.org/backend/backend-agnostic.html diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 672b6e4aa4600..aa4b51f1acb09 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1,27 +1,25 @@ -use rustc::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind}; -use rustc::middle::dependency_format::Linkage; use rustc_data_structures::fx::FxHashSet; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; -use rustc_session::config::{ - self, CFGuard, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer, -}; +use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind}; +use rustc_middle::middle::dependency_format::Linkage; +use rustc_session::config::{self, CFGuard, CrateType, DebugInfo}; +use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, Sanitizer}; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; /// For all the linkers we support, and information they might /// need out of the shared crate context before we get rid of it. use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; -use rustc_target::spec::{LinkerFlavor, PanicStrategy, RelroLevel}; +use rustc_target::spec::crt_objects::CrtObjectsFallback; +use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; +use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel}; use super::archive::ArchiveBuilder; use super::command::Command; -use super::linker::Linker; +use super::linker::{self, Linker}; use super::rpath::{self, RPathConfig}; -use crate::{ - looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME, - RLIB_BYTECODE_EXTENSION, -}; +use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME}; use cc::windows_registry; use tempfile::{Builder as TempFileBuilder, TempDir}; @@ -58,7 +56,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen()) && !output_metadata - && crate_type == config::CrateType::Executable + && crate_type == CrateType::Executable { continue; } @@ -85,7 +83,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( if outputs.outputs.should_codegen() { let out_filename = out_filename(sess, crate_type, outputs, crate_name); match crate_type { - config::CrateType::Rlib => { + CrateType::Rlib => { let _timer = sess.timer("link_rlib"); link_rlib::( sess, @@ -96,7 +94,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( ) .build(); } - config::CrateType::Staticlib => { + CrateType::Staticlib => { link_staticlib::(sess, codegen_results, &out_filename, &tmpdir); } _ => { @@ -130,10 +128,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( remove(sess, obj); } } - for obj in codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) - { - remove(sess, obj); - } if let Some(ref metadata_module) = codegen_results.metadata_module { if let Some(ref obj) = metadata_module.object { remove(sess, obj); @@ -143,9 +137,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( if let Some(ref obj) = allocator_module.object { remove(sess, obj); } - if let Some(ref bc) = allocator_module.bytecode_compressed { - remove(sess, bc); - } } } }); @@ -154,7 +145,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( // The third parameter is for env vars, used on windows to set up the // path for MSVC to find its DLLs, and gcc to find its bundled // toolchain -pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) { +fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> Command { let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe"); // If our linker looks like a batch script on Windows then to execute this @@ -182,7 +173,9 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB // To comply with the Windows App Certification Kit, // MSVC needs to link with the Store versions of the runtime libraries (vcruntime, msvcrt, etc). let t = &sess.target.target; - if flavor == LinkerFlavor::Msvc && t.target_vendor == "uwp" { + if (flavor == LinkerFlavor::Msvc || flavor == LinkerFlavor::Lld(LldFlavor::Link)) + && t.target_vendor == "uwp" + { if let Some(ref tool) = msvc_tool { let original_path = tool.path(); if let Some(ref root_lib_path) = original_path.ancestors().nth(4) { @@ -193,6 +186,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB _ => None, }; if let Some(ref a) = arch { + // FIXME: Move this to `fn linker_with_args`. let mut arg = OsString::from("/LIBPATH:"); arg.push(format!("{}\\lib\\{}\\store", root_lib_path.display(), a.to_string())); cmd.arg(&arg); @@ -232,7 +226,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB } cmd.env("PATH", env::join_paths(new_path).unwrap()); - (linker.to_path_buf(), cmd) + cmd } pub fn each_linked_rlib( @@ -243,10 +237,10 @@ pub fn each_linked_rlib( let mut fmts = None; for (ty, list) in info.dependency_formats.iter() { match ty { - config::CrateType::Executable - | config::CrateType::Staticlib - | config::CrateType::Cdylib - | config::CrateType::ProcMacro => { + CrateType::Executable + | CrateType::Staticlib + | CrateType::Cdylib + | CrateType::ProcMacro => { fmts = Some(list); break; } @@ -259,7 +253,7 @@ pub fn each_linked_rlib( }; for &(cnum, ref path) in crates { match fmts.get(cnum.as_usize() - 1) { - Some(&Linkage::NotLinked) | Some(&Linkage::IncludedFromDylib) => continue, + Some(&Linkage::NotLinked | &Linkage::IncludedFromDylib) => continue, Some(_) => {} None => return Err("could not find formats for rlibs".to_string()), } @@ -284,11 +278,7 @@ pub fn each_linked_rlib( /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a /// directory being searched for `extern crate` (observing an incomplete file). /// The returned path is the temporary file containing the complete metadata. -pub fn emit_metadata<'a>( - sess: &'a Session, - metadata: &EncodedMetadata, - tmpdir: &TempDir, -) -> PathBuf { +pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &TempDir) -> PathBuf { let out_filename = tmpdir.path().join(METADATA_FILENAME); let result = fs::write(&out_filename, &metadata.raw_data); @@ -379,14 +369,6 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( // contain the metadata in a separate file. ab.add_file(&emit_metadata(sess, &codegen_results.metadata, tmpdir)); - // For LTO purposes, the bytecode of this library is also inserted - // into the archive. - for bytecode in - codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) - { - ab.add_file(bytecode); - } - // After adding all files to the archive, we need to update the // symbol table of the archive. This currently dies on macOS (see // #11162), and isn't necessary there anyway @@ -480,105 +462,27 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>( // links to all upstream files as well. fn link_natively<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, - crate_type: config::CrateType, + crate_type: CrateType, out_filename: &Path, codegen_results: &CodegenResults, tmpdir: &Path, target_cpu: &str, ) { info!("preparing {:?} to {:?}", crate_type, out_filename); - let (linker, flavor) = linker_and_flavor(sess); - - let any_dynamic_crate = crate_type == config::CrateType::Dylib - || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { - *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) - }); - - // The invocations of cc share some flags across platforms - let (pname, mut cmd) = get_linker(sess, &linker, flavor); - - if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { - cmd.args(args); - } - if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { - if sess.crt_static(Some(crate_type)) { - cmd.args(args); - } - } - if let Some(ref args) = sess.opts.debugging_opts.pre_link_args { - cmd.args(args); - } - cmd.args(&sess.opts.debugging_opts.pre_link_arg); - - if sess.target.target.options.is_like_fuchsia { - let prefix = match sess.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => "asan/", - _ => "", - }; - cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); - } - - let pre_link_objects = if crate_type == config::CrateType::Executable { - &sess.target.target.options.pre_link_objects_exe - } else { - &sess.target.target.options.pre_link_objects_dll - }; - for obj in pre_link_objects { - cmd.arg(get_file_path(sess, obj)); - } - - if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) { - for obj in &sess.target.target.options.pre_link_objects_exe_crt { - cmd.arg(get_file_path(sess, obj)); - } - } + let (linker_path, flavor) = linker_and_flavor(sess); + let mut cmd = linker_with_args::( + &linker_path, + flavor, + sess, + crate_type, + tmpdir, + out_filename, + codegen_results, + target_cpu, + ); - if sess.target.target.options.is_like_emscripten { - cmd.arg("-s"); - cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { - "DISABLE_EXCEPTION_CATCHING=1" - } else { - "DISABLE_EXCEPTION_CATCHING=0" - }); - } + linker::disable_localization(&mut cmd); - { - let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu); - link_sanitizer_runtime(sess, crate_type, &mut *linker); - link_args::( - &mut *linker, - flavor, - sess, - crate_type, - tmpdir, - out_filename, - codegen_results, - ); - cmd = linker.finalize(); - } - if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { - cmd.args(args); - } - if any_dynamic_crate { - if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) { - cmd.args(args); - } - } else { - if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) { - cmd.args(args); - } - } - for obj in &sess.target.target.options.post_link_objects { - cmd.arg(get_file_path(sess, obj)); - } - if sess.crt_static(Some(crate_type)) { - for obj in &sess.target.target.options.post_link_objects_crt { - cmd.arg(get_file_path(sess, obj)); - } - } - if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { - cmd.args(args); - } for &(ref k, ref v) in &sess.target.target.options.link_env { cmd.env(k, v); } @@ -600,7 +504,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( let mut i = 0; loop { i += 1; - prog = sess.time("run_linker", || exec_linker(sess, &mut cmd, out_filename, tmpdir)); + prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, tmpdir)); let output = match prog { Ok(ref output) => output, Err(_) => break, @@ -701,12 +605,61 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( output.extend_from_slice(&prog.stdout); sess.struct_err(&format!( "linking with `{}` failed: {}", - pname.display(), + linker_path.display(), prog.status )) .note(&format!("{:?}", &cmd)) .note(&escape_string(&output)) .emit(); + + // If MSVC's `link.exe` was expected but the return code + // is not a Microsoft LNK error then suggest a way to fix or + // install the Visual Studio build tools. + if let Some(code) = prog.status.code() { + if sess.target.target.options.is_like_msvc + && flavor == LinkerFlavor::Msvc + // Respect the command line override + && sess.opts.cg.linker.is_none() + // Match exactly "link.exe" + && linker_path.to_str() == Some("link.exe") + // All Microsoft `link.exe` linking error codes are + // four digit numbers in the range 1000 to 9999 inclusive + && (code < 1000 || code > 9999) + { + let is_vs_installed = windows_registry::find_vs_version().is_ok(); + let has_linker = windows_registry::find_tool( + &sess.opts.target_triple.triple(), + "link.exe", + ) + .is_some(); + + sess.note_without_error("`link.exe` returned an unexpected error"); + if is_vs_installed && has_linker { + // the linker is broken + sess.note_without_error( + "the Visual Studio build tools may need to be repaired \ + using the Visual Studio installer", + ); + sess.note_without_error( + "or a necessary component may be missing from the \ + \"C++ build tools\" workload", + ); + } else if is_vs_installed { + // the linker is not installed + sess.note_without_error( + "in the Visual Studio installer, ensure the \ + \"C++ build tools\" workload is selected", + ); + } else { + // visual studio is not installed + sess.note_without_error( + "you may need to install Visual Studio build tools with the \ + \"C++ build tools\" workload", + ); + } + } + } + sess.abort_if_errors(); } info!("linker stderr:\n{}", escape_string(&prog.stderr)); @@ -717,9 +670,12 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( let mut linker_error = { if linker_not_found { - sess.struct_err(&format!("linker `{}` not found", pname.display())) + sess.struct_err(&format!("linker `{}` not found", linker_path.display())) } else { - sess.struct_err(&format!("could not exec the linker `{}`", pname.display())) + sess.struct_err(&format!( + "could not exec the linker `{}`", + linker_path.display() + )) } }; @@ -758,13 +714,13 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( } } -fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: &mut dyn Linker) { +fn link_sanitizer_runtime(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) { let sanitizer = match &sess.opts.debugging_opts.sanitizer { Some(s) => s, None => return, }; - if crate_type != config::CrateType::Executable { + if crate_type != CrateType::Executable { return; } @@ -790,7 +746,7 @@ fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: // PR #41352 for details). let libname = format!("rustc{}_rt.{}", channel, name); let rpath = default_tlib.to_str().expect("non-utf8 component in path"); - linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); + linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); linker.link_dylib(Symbol::intern(&libname)); } "x86_64-unknown-linux-gnu" | "x86_64-fuchsia" | "aarch64-fuchsia" => { @@ -820,7 +776,7 @@ pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum)) } -pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { +fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { fn infer_from( sess: &Session, linker: Option, @@ -838,7 +794,19 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { "emcc" } } - LinkerFlavor::Gcc => "cc", + LinkerFlavor::Gcc => { + if cfg!(any(target_os = "solaris", target_os = "illumos")) { + // On historical Solaris systems, "cc" may have + // been Sun Studio, which is not flag-compatible + // with "gcc". This history casts a long shadow, + // and many modern illumos distributions today + // ship GCC as "gcc" without also making it + // available as "cc". + "gcc" + } else { + "cc" + } + } LinkerFlavor::Ld => "ld", LinkerFlavor::Msvc => "link.exe", LinkerFlavor::Lld(_) => "lld", @@ -896,7 +864,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { /// Returns a boolean indicating whether we should preserve the object files on /// the filesystem for their debug information. This is often useful with /// split-dwarf like schemes. -pub fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { +fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { // If the objects don't have debuginfo there's nothing to preserve. if sess.opts.debuginfo == config::DebugInfo::None { return false; @@ -908,7 +876,7 @@ pub fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { .crate_types .borrow() .iter() - .any(|&x| x != config::CrateType::Rlib && x != config::CrateType::Staticlib); + .any(|&x| x != CrateType::Rlib && x != CrateType::Staticlib); if !output_linked { return false; } @@ -924,18 +892,7 @@ pub fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { // *not* running dsymutil then the object files are the only source of truth // for debug information, so we must preserve them. if sess.target.target.options.is_like_osx { - match sess.opts.debugging_opts.run_dsymutil { - // dsymutil is not being run, preserve objects - Some(false) => return true, - - // dsymutil is being run, no need to preserve the objects - Some(true) => return false, - - // The default historical behavior was to always run dsymutil, so - // we're preserving that temporarily, but we're likely to switch the - // default soon. - None => return false, - } + return !sess.opts.debugging_opts.run_dsymutil; } false @@ -950,7 +907,7 @@ enum RlibFlavor { StaticlibBase, } -pub fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { +fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { let lib_args: Vec<_> = all_native_libs .iter() .filter(|l| relevant_lib(sess, l)) @@ -1052,7 +1009,7 @@ fn get_crt_libs_path(sess: &Session) -> Option { } } -pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { +fn get_object_file_path(sess: &Session, name: &str) -> PathBuf { // prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details if sess.target.target.llvm_target.contains("windows-gnu") { if let Some(compiler_libs_path) = get_crt_libs_path(sess) { @@ -1076,9 +1033,9 @@ pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { PathBuf::from(name) } -pub fn exec_linker( +fn exec_linker( sess: &Session, - cmd: &mut Command, + cmd: &Command, out_filename: &Path, tmpdir: &Path, ) -> io::Result { @@ -1224,135 +1181,201 @@ pub fn exec_linker( } } -fn link_args<'a, B: ArchiveBuilder<'a>>( - cmd: &mut dyn Linker, - flavor: LinkerFlavor, - sess: &'a Session, - crate_type: config::CrateType, - tmpdir: &Path, - out_filename: &Path, - codegen_results: &CodegenResults, -) { - // Linker plugins should be specified early in the list of arguments - cmd.linker_plugin_lto(); - - // The default library location, we need this to find the runtime. - // The location of crates will be determined as needed. - let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); - - // target descriptor - let t = &sess.target.target; +fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { + let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) { + (CrateType::Executable, false, RelocModel::Pic) => LinkOutputKind::DynamicPicExe, + (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe, + (CrateType::Executable, true, RelocModel::Pic) => LinkOutputKind::StaticPicExe, + (CrateType::Executable, true, _) => LinkOutputKind::StaticNoPicExe, + (_, true, _) => LinkOutputKind::StaticDylib, + (_, false, _) => LinkOutputKind::DynamicDylib, + }; - // prefer system mingw-w64 libs, see get_crt_libs_path comment for more details - if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") { - if let Some(compiler_libs_path) = get_crt_libs_path(sess) { - cmd.include_path(&compiler_libs_path); - } + // Adjust the output kind to target capabilities. + let pic_exe_supported = sess.target.target.options.position_independent_executables; + let static_pic_exe_supported = false; // FIXME: Add this option to target specs. + let static_dylib_supported = sess.target.target.options.crt_static_allows_dylibs; + match kind { + LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe, + LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe, + LinkOutputKind::StaticDylib if !static_dylib_supported => LinkOutputKind::DynamicDylib, + _ => kind, } +} - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); - - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { - cmd.add_object(obj); +/// Whether we link to our own CRT objects instead of relying on gcc to pull them. +/// We only provide such support for a very limited number of targets. +fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { + match sess.target.target.options.crt_objects_fallback { + // FIXME: Find a better heuristic for "native musl toolchain is available", + // based on host and linker path, for example. + // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). + Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), + // FIXME: Find some heuristic for "native mingw toolchain is available", + // likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429). + Some(CrtObjectsFallback::Mingw) => sess.target.target.target_vendor != "uwp", + // FIXME: Figure out cases in which WASM needs to link with a native toolchain. + Some(CrtObjectsFallback::Wasm) => true, + None => false, } - cmd.output_filename(out_filename); +} - if crate_type == config::CrateType::Executable && sess.target.target.options.is_like_windows { - if let Some(ref s) = codegen_results.windows_subsystem { - cmd.subsystem(s); - } +/// Add pre-link object files defined by the target spec. +fn add_pre_link_objects( + cmd: &mut dyn Linker, + sess: &Session, + link_output_kind: LinkOutputKind, + fallback: bool, +) { + let opts = &sess.target.target.options; + let objects = if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects }; + for obj in objects.get(&link_output_kind).iter().copied().flatten() { + cmd.add_object(&get_object_file_path(sess, obj)); } +} - // If we're building something like a dynamic library then some platforms - // need to make sure that all symbols are exported correctly from the - // dynamic library. - cmd.export_symbols(tmpdir, crate_type); - - // 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 == config::CrateType::Dylib || crate_type == config::CrateType::ProcMacro { - let obj = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - cmd.add_object(obj); - } +/// Add post-link object files defined by the target spec. +fn add_post_link_objects( + cmd: &mut dyn Linker, + sess: &Session, + link_output_kind: LinkOutputKind, + fallback: bool, +) { + let opts = &sess.target.target.options; + let objects = if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; + for obj in objects.get(&link_output_kind).iter().copied().flatten() { + cmd.add_object(&get_object_file_path(sess, obj)); } +} - let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - cmd.add_object(obj); +/// Add arbitrary "pre-link" args defined by the target spec or from command line. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_pre_link_args( + cmd: &mut dyn Linker, + sess: &Session, + flavor: LinkerFlavor, + crate_type: CrateType, +) { + if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { + cmd.args(args); } - - // Try to strip as much out of the generated object by removing unused - // sections if possible. See more comments in linker.rs - if !sess.opts.cg.link_dead_code { - let keep_metadata = crate_type == config::CrateType::Dylib; - cmd.gc_sections(keep_metadata); + if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { + if sess.crt_static(Some(crate_type)) { + cmd.args(args); + } } + cmd.args(&sess.opts.debugging_opts.pre_link_args); +} - let used_link_args = &codegen_results.crate_info.link_args; - - if crate_type == config::CrateType::Executable { - let mut position_independent_executable = false; +/// Add a link script embedded in the target, if applicable. +fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_type: CrateType) { + match (crate_type, &sess.target.target.options.link_script) { + (CrateType::Cdylib | CrateType::Executable, Some(script)) => { + if !sess.target.target.options.linker_is_gnu { + sess.fatal("can only use link script when linking with GNU-like linker"); + } - if t.options.position_independent_executables { - let empty_vec = Vec::new(); - let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); - let more_args = &sess.opts.cg.link_arg; - let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); + let file_name = ["rustc", &sess.target.target.llvm_target, "linkfile.ld"].join("-"); - if is_pic(sess) && !sess.crt_static(Some(crate_type)) && !args.any(|x| *x == "-static") - { - position_independent_executable = true; + let path = tmpdir.join(file_name); + if let Err(e) = fs::write(&path, script) { + sess.fatal(&format!("failed to write link script to {}: {}", path.display(), e)); } - } - if position_independent_executable { - cmd.position_independent_executable(); - } else { - // recent versions of gcc can be configured to generate position - // independent executables by default. We have to pass -no-pie to - // explicitly turn that off. Not applicable to ld. - if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld { - cmd.no_position_independent_executable(); - } + cmd.arg("--script"); + cmd.arg(path); } + _ => {} } +} - let relro_level = match sess.opts.debugging_opts.relro_level { - Some(level) => level, - None => t.options.relro_level, - }; - match relro_level { - RelroLevel::Full => { - cmd.full_relro(); - } - RelroLevel::Partial => { - cmd.partial_relro(); +/// Add arbitrary "user defined" args defined from command line and by `#[link_args]` attributes. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_user_defined_link_args( + cmd: &mut dyn Linker, + sess: &Session, + codegen_results: &CodegenResults, +) { + cmd.args(&sess.opts.cg.link_args); + cmd.args(&*codegen_results.crate_info.link_args); +} + +/// Add arbitrary "late link" args defined by the target spec. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_late_link_args( + cmd: &mut dyn Linker, + sess: &Session, + flavor: LinkerFlavor, + crate_type: CrateType, + codegen_results: &CodegenResults, +) { + if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { + cmd.args(args); + } + let any_dynamic_crate = crate_type == CrateType::Dylib + || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { + *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) + }); + if any_dynamic_crate { + if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) { + cmd.args(args); } - RelroLevel::Off => { - cmd.no_relro(); + } else { + if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) { + cmd.args(args); } - RelroLevel::None => {} } +} - // Pass optimization flags down to the linker. - cmd.optimize(); +/// Add arbitrary "post-link" args defined by the target spec. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { + if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { + cmd.args(args); + } +} - // Pass debuginfo flags down to the linker. - cmd.debuginfo(); +/// Add object files containing code from the current crate. +fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + cmd.add_object(obj); + } +} - // We want to, by default, prevent the compiler from accidentally leaking in - // any system libraries, so we may explicitly ask linkers to not link to any - // libraries by default. Note that this does not happen for windows because - // windows pulls in some large number of libraries and I couldn't quite - // figure out which subset we wanted. - // - // This is all naturally configurable via the standard methods as well. - if !sess.opts.cg.default_linker_libraries.unwrap_or(false) && t.options.no_default_libraries { - cmd.no_default_libraries(); +/// Add object files for allocator code linked once for the whole crate tree. +fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { + if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) { + cmd.add_object(obj); + } +} + +/// Add object files containing metadata for the current crate. +fn add_local_crate_metadata_objects( + cmd: &mut dyn Linker, + crate_type: CrateType, + codegen_results: &CodegenResults, +) { + // 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); + } } +} +/// Link native libraries corresponding to the current crate and all libraries corresponding to +/// all its dependency crates. +/// FIXME: Consider combining this with the functions above adding object files for the local crate. +fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<'a>>( + cmd: &mut dyn Linker, + sess: &'a Session, + crate_type: CrateType, + codegen_results: &CodegenResults, + tmpdir: &Path, +) { // Take careful note of the ordering of the arguments we pass to the linker // here. Linkers will assume that things on the left depend on things to the // right. Things on the right cannot depend on things on the left. This is @@ -1379,26 +1402,52 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( // link line. And finally upstream native libraries can't depend on anything // in this DAG so far because they're only dylibs and dylibs can only depend // on other dylibs (e.g., other native deps). - add_local_native_libraries(cmd, sess, codegen_results); - add_upstream_rust_crates::(cmd, sess, codegen_results, crate_type, tmpdir); - add_upstream_native_libraries(cmd, sess, codegen_results, crate_type); - - // Tell the linker what we're doing. - if crate_type != config::CrateType::Executable { - cmd.build_dylib(out_filename); + // + // If -Zlink-native-libraries=false is set, then the assumption is that an + // external build system already has the native dependencies defined, and it + // will provide them to the linker itself. + if sess.opts.debugging_opts.link_native_libraries { + add_local_native_libraries(cmd, sess, codegen_results); } - if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) { - cmd.build_static_executable(); + add_upstream_rust_crates::(cmd, sess, codegen_results, crate_type, tmpdir); + if sess.opts.debugging_opts.link_native_libraries { + add_upstream_native_libraries(cmd, sess, codegen_results, crate_type); } +} - if sess.opts.cg.profile_generate.enabled() { - cmd.pgo_gen(); +/// Add sysroot and other globally set directories to the directory search list. +fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) { + // Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details. + if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") { + if let Some(compiler_libs_path) = get_crt_libs_path(sess) { + cmd.include_path(&compiler_libs_path); + } } - if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled { - cmd.control_flow_guard(); + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); +} + +/// Add options making relocation sections in the produced ELF files read-only +/// and suppressing lazy binding. +fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) { + match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.target.options.relro_level) { + RelroLevel::Full => cmd.full_relro(), + RelroLevel::Partial => cmd.partial_relro(), + RelroLevel::Off => cmd.no_relro(), + RelroLevel::None => {} } +} +/// Add library search paths used at runtime by dynamic linkers. +fn add_rpath_args( + cmd: &mut dyn Linker, + sess: &Session, + codegen_results: &CodegenResults, + out_filename: &Path, +) { // FIXME (#2397): At some point we want to rpath our guesses as to // where extern libraries might live, based on the // addl_lib_search_paths @@ -1422,14 +1471,191 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( }; cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); } +} - // Finally add all the linker arguments provided on the command line along - // with any #[link_args] attributes found inside the crate - if let Some(ref args) = sess.opts.cg.link_args { - cmd.args(args); +/// Produce the linker command line containing linker path and arguments. +/// `NO-OPT-OUT` marks the arguments that cannot be removed from the command line +/// by the user without creating a custom target specification. +/// `OBJECT-FILES` specify whether the arguments can add object files. +/// `CUSTOMIZATION-POINT` means that arbitrary arguments defined by the user +/// or by the target spec can be inserted here. +/// `AUDIT-ORDER` - need to figure out whether the option is order-dependent or not. +fn linker_with_args<'a, B: ArchiveBuilder<'a>>( + path: &Path, + flavor: LinkerFlavor, + sess: &'a Session, + crate_type: CrateType, + tmpdir: &Path, + out_filename: &Path, + codegen_results: &CodegenResults, + target_cpu: &str, +) -> Command { + let base_cmd = get_linker(sess, path, flavor); + // FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction + // to the linker args construction. + assert!(base_cmd.get_args().is_empty() || sess.target.target.target_vendor == "uwp"); + let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor, target_cpu); + let link_output_kind = link_output_kind(sess, crate_type); + let crt_objects_fallback = crt_objects_fallback(sess, crate_type); + + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + add_pre_link_args(cmd, sess, flavor, crate_type); + + // NO-OPT-OUT + add_link_script(cmd, sess, tmpdir, crate_type); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + if sess.target.target.options.is_like_fuchsia && crate_type == CrateType::Executable { + let prefix = match sess.opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => "asan/", + _ => "", + }; + cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); } - cmd.args(&sess.opts.cg.link_arg); - cmd.args(&used_link_args); + + // NO-OPT-OUT, OBJECT-FILES-NO + if crt_objects_fallback { + cmd.no_crt_objects(); + } + + // NO-OPT-OUT, OBJECT-FILES-YES + add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + if sess.target.target.options.is_like_emscripten { + cmd.arg("-s"); + cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { + "DISABLE_EXCEPTION_CATCHING=1" + } else { + "DISABLE_EXCEPTION_CATCHING=0" + }); + } + + // OBJECT-FILES-YES, AUDIT-ORDER + link_sanitizer_runtime(sess, crate_type, cmd); + + // OBJECT-FILES-NO, AUDIT-ORDER + // Linker plugins should be specified early in the list of arguments + // FIXME: How "early" exactly? + cmd.linker_plugin_lto(); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // FIXME: Order-dependent, at least relatively to other args adding searh directories. + add_library_search_dirs(cmd, sess); + + // OBJECT-FILES-YES + add_local_crate_regular_objects(cmd, codegen_results); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + cmd.output_filename(out_filename); + + // OBJECT-FILES-NO, AUDIT-ORDER + if crate_type == CrateType::Executable && sess.target.target.options.is_like_windows { + if let Some(ref s) = codegen_results.windows_subsystem { + cmd.subsystem(s); + } + } + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // If we're building something like a dynamic library then some platforms + // need to make sure that all symbols are exported correctly from the + // dynamic library. + cmd.export_symbols(tmpdir, crate_type); + + // OBJECT-FILES-YES + add_local_crate_metadata_objects(cmd, crate_type, codegen_results); + + // OBJECT-FILES-YES + add_local_crate_allocator_objects(cmd, codegen_results); + + // OBJECT-FILES-NO, AUDIT-ORDER + // FIXME: Order dependent, applies to the following objects. Where should it be placed? + // Try to strip as much out of the generated object by removing unused + // sections if possible. See more comments in linker.rs + if !sess.opts.cg.link_dead_code { + let keep_metadata = crate_type == CrateType::Dylib; + cmd.gc_sections(keep_metadata); + } + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // FIXME: Support `StaticPicExe` correctly. + match link_output_kind { + LinkOutputKind::DynamicPicExe | LinkOutputKind::StaticPicExe => { + cmd.position_independent_executable() + } + LinkOutputKind::DynamicNoPicExe | LinkOutputKind::StaticNoPicExe => { + cmd.no_position_independent_executable() + } + _ => {} + } + + // OBJECT-FILES-NO, AUDIT-ORDER + add_relro_args(cmd, sess); + + // OBJECT-FILES-NO, AUDIT-ORDER + // Pass optimization flags down to the linker. + cmd.optimize(); + + // OBJECT-FILES-NO, AUDIT-ORDER + // Pass debuginfo and strip flags down to the linker. + cmd.debuginfo(sess.opts.debugging_opts.strip); + + // OBJECT-FILES-NO, AUDIT-ORDER + // We want to prevent the compiler from accidentally leaking in any system libraries, + // so by default we tell linkers not to link to any default libraries. + if !sess.opts.cg.default_linker_libraries && sess.target.target.options.no_default_libraries { + cmd.no_default_libraries(); + } + + // OBJECT-FILES-YES + link_local_crate_native_libs_and_dependent_crate_libs::( + cmd, + sess, + crate_type, + codegen_results, + tmpdir, + ); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // FIXME: Merge with the previous `link_output_kind` match, + // and support `StaticPicExe` and `StaticDylib` correctly. + match link_output_kind { + LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => { + cmd.build_static_executable() + } + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => cmd.build_dylib(out_filename), + _ => {} + } + + // OBJECT-FILES-NO, AUDIT-ORDER + if sess.opts.cg.profile_generate.enabled() { + cmd.pgo_gen(); + } + + // OBJECT-FILES-NO, AUDIT-ORDER + if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled { + cmd.control_flow_guard(); + } + + // OBJECT-FILES-NO, AUDIT-ORDER + add_rpath_args(cmd, sess, codegen_results, out_filename); + + // OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + add_user_defined_link_args(cmd, sess, codegen_results); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + cmd.finalize(); + + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + add_late_link_args(cmd, sess, flavor, crate_type, codegen_results); + + // NO-OPT-OUT, OBJECT-FILES-YES + add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); + + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + add_post_link_args(cmd, sess, flavor); + + cmd.take_cmd() } // # Native library linking @@ -1443,7 +1669,7 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( // Also note that the native libraries linked here are only the ones located // in the current crate. Upstream crates with native library dependencies // may have their native library pulled in above. -pub fn add_local_native_libraries( +fn add_local_native_libraries( cmd: &mut dyn Linker, sess: &Session, codegen_results: &CodegenResults, @@ -1491,7 +1717,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( cmd: &mut dyn Linker, sess: &'a Session, codegen_results: &CodegenResults, - crate_type: config::CrateType, + crate_type: CrateType, tmpdir: &Path, ) { // All of the heavy lifting has previously been accomplished by the @@ -1652,7 +1878,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, codegen_results: &CodegenResults, tmpdir: &Path, - crate_type: config::CrateType, + crate_type: CrateType, cnum: CrateNum, ) { let src = &codegen_results.crate_info.used_crate_source[&cnum]; @@ -1668,7 +1894,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( if (!are_upstream_rust_objects_already_included(sess) || ignored_for_lto(sess, &codegen_results.crate_info, cnum)) - && crate_type != config::CrateType::Dylib + && crate_type != CrateType::Dylib && !skip_native { cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); @@ -1685,7 +1911,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( let mut any_objects = false; for f in archive.src_files() { - if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { + if f == METADATA_FILENAME { archive.remove_file(&f); continue; } @@ -1729,7 +1955,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( // Note, though, that we don't want to include the whole of a // compiler-builtins crate (e.g., compiler-rt) because it'll get // repeatedly linked anyway. - if crate_type == config::CrateType::Dylib + if crate_type == CrateType::Dylib && codegen_results.crate_info.compiler_builtins != Some(cnum) { cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst)); @@ -1773,11 +1999,11 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( // generic function calls a native function, then the generic function must // be instantiated in the target crate, meaning that the native symbol must // also be resolved in the target crate. -pub fn add_upstream_native_libraries( +fn add_upstream_native_libraries( cmd: &mut dyn Linker, sess: &Session, codegen_results: &CodegenResults, - crate_type: config::CrateType, + crate_type: CrateType, ) { // Be sure to use a topological sorting of crates because there may be // interdependencies between native libraries. When passing -nodefaultlibs, @@ -1830,14 +2056,14 @@ pub fn add_upstream_native_libraries( } } -pub fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { +fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { match lib.cfg { Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, None), None => true, } } -pub fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { +fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { match sess.lto() { config::Lto::Fat => true, config::Lto::Thin => { @@ -1848,12 +2074,3 @@ pub fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { config::Lto::No | config::Lto::ThinLocal => false, } } - -fn is_pic(sess: &Session) -> bool { - let reloc_model_arg = match sess.opts.cg.relocation_model { - Some(ref s) => &s[..], - None => &sess.target.target.options.relocation_model[..], - }; - - reloc_model_arg == "pic" -} diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index 6b30ae8559db9..ee5bcf4b9f58b 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -6,18 +6,32 @@ use std::ffi::{OsStr, OsString}; use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufWriter}; +use std::mem; use std::path::{Path, PathBuf}; -use rustc::middle::dependency_format::Linkage; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_middle::middle::dependency_format::Linkage; +use rustc_middle::ty::TyCtxt; use rustc_serialize::{json, Encoder}; -use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel}; +use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{LinkerFlavor, LldFlavor}; +/// Disables non-English messages from localized linkers. +/// Such messages may cause issues with text encoding on Windows (#35785) +/// and prevent inspection of linker output in case of errors, which we occasionally do. +/// This should be acceptable because other messages from rustc are in English anyway, +/// and may also be desirable to improve searchability of the linker diagnostics. +pub fn disable_localization(linker: &mut Command) { + // No harm in setting both env vars simultaneously. + // Unix-style linkers. + linker.env("LC_ALL", "C"); + // MSVC's `link.exe`. + linker.env("VSLANG", "1033"); +} + /// For all the linkers we support, and information they might /// need out of the shared crate context before we get rid of it. #[derive(RustcEncodable, RustcDecodable)] @@ -87,6 +101,7 @@ impl LinkerInfo { /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an /// MSVC linker (e.g., `link.exe`) is being used. pub trait Linker { + fn cmd(&mut self) -> &mut Command; fn link_dylib(&mut self, lib: Symbol); fn link_rust_dylib(&mut self, lib: Symbol, path: &Path); fn link_framework(&mut self, framework: Symbol); @@ -107,18 +122,31 @@ pub trait Linker { fn optimize(&mut self); fn pgo_gen(&mut self); fn control_flow_guard(&mut self); - fn debuginfo(&mut self); + fn debuginfo(&mut self, strip: Strip); + fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); fn build_dylib(&mut self, out_filename: &Path); fn build_static_executable(&mut self); - fn args(&mut self, args: &[String]); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); fn subsystem(&mut self, subsystem: &str); fn group_start(&mut self); fn group_end(&mut self); fn linker_plugin_lto(&mut self); - // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). - fn finalize(&mut self) -> Command; + fn finalize(&mut self); +} + +impl dyn Linker + '_ { + pub fn arg(&mut self, arg: impl AsRef) { + self.cmd().arg(arg); + } + + pub fn args(&mut self, args: impl IntoIterator>) { + self.cmd().args(args); + } + + pub fn take_cmd(&mut self) -> Command { + mem::replace(self.cmd(), Command::new("")) + } } pub struct GccLinker<'a> { @@ -208,6 +236,9 @@ impl<'a> GccLinker<'a> { } impl<'a> Linker for GccLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } fn link_dylib(&mut self, lib: Symbol) { self.hint_dynamic(); self.cmd.arg(format!("-l{}", lib)); @@ -236,7 +267,9 @@ impl<'a> Linker for GccLinker<'a> { self.cmd.arg("-pie"); } fn no_position_independent_executable(&mut self) { - self.cmd.arg("-no-pie"); + if !self.is_ld { + self.cmd.arg("-no-pie"); + } } fn full_relro(&mut self) { self.linker_arg("-zrelro"); @@ -251,9 +284,6 @@ impl<'a> Linker for GccLinker<'a> { fn build_static_executable(&mut self) { self.cmd.arg("-static"); } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { self.hint_dynamic(); @@ -365,15 +395,22 @@ impl<'a> Linker for GccLinker<'a> { self.sess.warn("Windows Control Flow Guard is not supported by this linker."); } - fn debuginfo(&mut self) { - if let DebugInfo::None = self.sess.opts.debuginfo { - // If we are building without debuginfo enabled and we were called with - // `-Zstrip-debuginfo-if-disabled=yes`, tell the linker to strip any debuginfo - // found when linking to get rid of symbols from libstd. - if let Some(true) = self.sess.opts.debugging_opts.strip_debuginfo_if_disabled { - self.linker_arg("-S"); + fn debuginfo(&mut self, strip: Strip) { + match strip { + Strip::None => {} + Strip::Debuginfo => { + self.linker_arg("--strip-debug"); } - }; + Strip::Symbols => { + self.linker_arg("--strip-all"); + } + } + } + + fn no_crt_objects(&mut self) { + if !self.is_ld { + self.cmd.arg("-nostartfiles"); + } } fn no_default_libraries(&mut self) { @@ -505,10 +542,8 @@ impl<'a> Linker for GccLinker<'a> { self.linker_arg(&subsystem); } - fn finalize(&mut self) -> Command { + fn finalize(&mut self) { self.hint_dynamic(); // Reset to default before returning the composed command line. - - ::std::mem::replace(&mut self.cmd, Command::new("")) } fn group_start(&mut self) { @@ -545,15 +580,15 @@ pub struct MsvcLinker<'a> { } impl<'a> Linker for MsvcLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } fn build_dylib(&mut self, out_filename: &Path) { self.cmd.arg("/DLL"); @@ -618,16 +653,12 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn no_crt_objects(&mut self) { + // noop + } + fn no_default_libraries(&mut self) { - // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC - // as there's been trouble in the past of linking the C++ standard - // library required by LLVM. This likely needs to happen one day, but - // in general Windows is also a more controlled environment than - // Unix, so it's not necessarily as critical that this be implemented. - // - // Note that there are also some licensing worries about statically - // linking some libraries which require a specific agreement, so it may - // not ever be possible for us to pass this flag. + self.cmd.arg("/NODEFAULTLIB"); } fn include_path(&mut self, path: &Path) { @@ -669,29 +700,37 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg("/guard:cf"); } - fn debuginfo(&mut self) { - // This will cause the Microsoft linker to generate a PDB file - // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); - - // This will cause the Microsoft linker to embed .natvis info into the PDB file - let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc"); - if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { - for entry in natvis_dir { - match entry { - Ok(entry) => { - let path = entry.path(); - if path.extension() == Some("natvis".as_ref()) { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); + fn debuginfo(&mut self, strip: Strip) { + match strip { + Strip::None => { + // This will cause the Microsoft linker to generate a PDB file + // from the CodeView line tables in the object files. + self.cmd.arg("/DEBUG"); + + // This will cause the Microsoft linker to embed .natvis info into the PDB file + let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc"); + if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { + for entry in natvis_dir { + match entry { + Ok(entry) => { + let path = entry.path(); + if path.extension() == Some("natvis".as_ref()) { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); + } + } + Err(err) => { + self.sess + .warn(&format!("error enumerating natvis directory: {}", err)); + } } } - Err(err) => { - self.sess.warn(&format!("error enumerating natvis directory: {}", err)); - } } } + Strip::Debuginfo | Strip::Symbols => { + self.cmd.arg("/DEBUG:NONE"); + } } } @@ -758,9 +797,7 @@ impl<'a> Linker for MsvcLinker<'a> { } } - fn finalize(&mut self) -> Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) - } + fn finalize(&mut self) {} // MSVC doesn't need group indicators fn group_start(&mut self) {} @@ -778,6 +815,9 @@ pub struct EmLinker<'a> { } impl<'a> Linker for EmLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } @@ -837,10 +877,6 @@ impl<'a> Linker for EmLinker<'a> { // noop } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - fn framework_path(&mut self, _path: &Path) { bug!("frameworks are not supported on Emscripten") } @@ -875,7 +911,7 @@ impl<'a> Linker for EmLinker<'a> { self.sess.warn("Windows Control Flow Guard is not supported by this linker."); } - fn debuginfo(&mut self) { + fn debuginfo(&mut self, _strip: Strip) { // Preserve names or generate source maps depending on debug info self.cmd.arg(match self.sess.opts.debuginfo { DebugInfo::None => "-g0", @@ -884,6 +920,8 @@ impl<'a> Linker for EmLinker<'a> { }); } + fn no_crt_objects(&mut self) {} + fn no_default_libraries(&mut self) { self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); } @@ -928,9 +966,7 @@ impl<'a> Linker for EmLinker<'a> { // noop } - fn finalize(&mut self) -> Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) - } + fn finalize(&mut self) {} // Appears not necessary on Emscripten fn group_start(&mut self) {} @@ -992,6 +1028,10 @@ impl<'a> WasmLd<'a> { } impl<'a> Linker for WasmLd<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + fn link_dylib(&mut self, lib: Symbol) { self.cmd.arg("-l").sym_arg(lib); } @@ -1030,10 +1070,6 @@ impl<'a> Linker for WasmLd<'a> { fn build_static_executable(&mut self) {} - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { self.cmd.arg("-l").sym_arg(lib); } @@ -1069,12 +1105,24 @@ impl<'a> Linker for WasmLd<'a> { fn pgo_gen(&mut self) {} - fn debuginfo(&mut self) {} + fn debuginfo(&mut self, strip: Strip) { + match strip { + Strip::None => {} + Strip::Debuginfo => { + self.cmd.arg("--strip-debug"); + } + Strip::Symbols => { + self.cmd.arg("--strip-all"); + } + } + } fn control_flow_guard(&mut self) { self.sess.warn("Windows Control Flow Guard is not supported by this linker."); } + fn no_crt_objects(&mut self) {} + fn no_default_libraries(&mut self) {} fn build_dylib(&mut self, _out_filename: &Path) { @@ -1098,9 +1146,7 @@ impl<'a> Linker for WasmLd<'a> { fn no_position_independent_executable(&mut self) {} - fn finalize(&mut self) -> Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) - } + fn finalize(&mut self) {} // Not needed for now with LLD fn group_start(&mut self) {} @@ -1130,11 +1176,7 @@ fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { } let formats = tcx.dependency_formats(LOCAL_CRATE); - let deps = formats - .iter() - .filter_map(|(t, list)| if *t == crate_type { Some(list) } else { None }) - .next() - .unwrap(); + let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap(); for (index, dep_format) in deps.iter().enumerate() { let cnum = CrateNum::new(index + 1); @@ -1162,6 +1204,10 @@ pub struct PtxLinker<'a> { } impl<'a> Linker for PtxLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + fn link_rlib(&mut self, path: &Path) { self.cmd.arg("--rlib").arg(path); } @@ -1174,7 +1220,7 @@ impl<'a> Linker for PtxLinker<'a> { self.cmd.arg("-L").arg(path); } - fn debuginfo(&mut self) { + fn debuginfo(&mut self, _strip: Strip) { self.cmd.arg("--debug"); } @@ -1182,10 +1228,6 @@ impl<'a> Linker for PtxLinker<'a> { self.cmd.arg("--bitcode").arg(path); } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - fn optimize(&mut self) { match self.sess.lto() { Lto::Thin | Lto::Fat | Lto::ThinLocal => { @@ -1200,14 +1242,12 @@ impl<'a> Linker for PtxLinker<'a> { self.cmd.arg("-o").arg(path); } - fn finalize(&mut self) -> Command { + fn finalize(&mut self) { // Provide the linker with fallback to internal `target-cpu`. self.cmd.arg("--fallback-arch").arg(match self.sess.opts.cg.target_cpu { Some(ref s) => s, None => &self.sess.target.target.options.cpu, }); - - ::std::mem::replace(&mut self.cmd, Command::new("")) } fn link_dylib(&mut self, _lib: Symbol) { @@ -1248,6 +1288,8 @@ impl<'a> Linker for PtxLinker<'a> { fn pgo_gen(&mut self) {} + fn no_crt_objects(&mut self) {} + fn no_default_libraries(&mut self) {} fn control_flow_guard(&mut self) { diff --git a/src/librustc_codegen_ssa/back/rpath.rs b/src/librustc_codegen_ssa/back/rpath.rs index 0a0e975e5a54c..c02e4f279b1fb 100644 --- a/src/librustc_codegen_ssa/back/rpath.rs +++ b/src/librustc_codegen_ssa/back/rpath.rs @@ -3,8 +3,8 @@ use std::env; use std::fs; use std::path::{Path, PathBuf}; -use rustc::middle::cstore::LibSource; use rustc_hir::def_id::CrateNum; +use rustc_middle::middle::cstore::LibSource; pub struct RPathConfig<'a> { pub used_crates: &'a [(CrateNum, LibSource)], diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs index 8a2503ce16730..c0272e1cd2d6b 100644 --- a/src/librustc_codegen_ssa/back/symbol_export.rs +++ b/src/librustc_codegen_ssa/back/symbol_export.rs @@ -1,12 +1,5 @@ use std::collections::hash_map::Entry::*; -use std::sync::Arc; - -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::middle::exported_symbols::{metadata_symbol_name, ExportedSymbol, SymbolExportLevel}; -use rustc::ty::query::Providers; -use rustc::ty::subst::{GenericArgKind, SubstsRef}; -use rustc::ty::Instance; -use rustc::ty::{SymbolName, TyCtxt}; + use rustc_ast::expand::allocator::ALLOCATOR_METHODS; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -14,23 +7,30 @@ use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::Node; use rustc_index::vec::IndexVec; -use rustc_session::config::{self, Sanitizer}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::exported_symbols::{ + metadata_symbol_name, ExportedSymbol, SymbolExportLevel, +}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::Instance; +use rustc_middle::ty::{SymbolName, TyCtxt}; +use rustc_session::config::{CrateType, Sanitizer}; pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { crates_export_threshold(&tcx.sess.crate_types.borrow()) } -fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel { +fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel { match crate_type { - config::CrateType::Executable - | config::CrateType::Staticlib - | config::CrateType::ProcMacro - | config::CrateType::Cdylib => SymbolExportLevel::C, - config::CrateType::Rlib | config::CrateType::Dylib => SymbolExportLevel::Rust, + CrateType::Executable | CrateType::Staticlib | CrateType::ProcMacro | CrateType::Cdylib => { + SymbolExportLevel::C + } + CrateType::Rlib | CrateType::Dylib => SymbolExportLevel::Rust, } } -pub fn crates_export_threshold(crate_types: &[config::CrateType]) -> SymbolExportLevel { +pub fn crates_export_threshold(crate_types: &[CrateType]) -> SymbolExportLevel { if crate_types .iter() .any(|&crate_type| crate_export_threshold(crate_type) == SymbolExportLevel::Rust) @@ -41,14 +41,11 @@ pub fn crates_export_threshold(crate_types: &[config::CrateType]) -> SymbolExpor } } -fn reachable_non_generics_provider( - tcx: TyCtxt<'_>, - cnum: CrateNum, -) -> &DefIdMap { +fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap { assert_eq!(cnum, LOCAL_CRATE); if !tcx.sess.opts.output_types.should_codegen() { - return tcx.arena.alloc(Default::default()); + return Default::default(); } // Check to see if this crate is a "special runtime crate". These @@ -85,15 +82,17 @@ fn reachable_non_generics_provider( } // Only consider nodes that actually have exported symbols. - Node::Item(&hir::Item { kind: hir::ItemKind::Static(..), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) + Node::Item(&hir::Item { + kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn(..), + .. + }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => { let def_id = tcx.hir().local_def_id(hir_id); let generics = tcx.generics_of(def_id); if !generics.requires_monomorphization(tcx) && // Functions marked with #[inline] are only ever codegened // with "internal" linkage and are never exported. - !Instance::mono(tcx, def_id).def.generates_cgu_internal_copy(tcx) + !Instance::mono(tcx, def_id.to_def_id()).def.generates_cgu_internal_copy(tcx) { Some(def_id) } else { @@ -106,7 +105,7 @@ fn reachable_non_generics_provider( }) .map(|def_id| { let export_level = if special_runtime_crate { - let name = tcx.symbol_name(Instance::mono(tcx, def_id)).name.as_str(); + let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name.as_str(); // We can probably do better here by just ensuring that // it has hidden visibility rather than public // visibility, as this is primarily here to ensure it's @@ -123,14 +122,14 @@ fn reachable_non_generics_provider( SymbolExportLevel::Rust } } else { - symbol_export_level(tcx, def_id) + symbol_export_level(tcx, def_id.to_def_id()) }; debug!( "EXPORTED SYMBOL (local): {} ({:?})", - tcx.symbol_name(Instance::mono(tcx, def_id)), + tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())), export_level ); - (def_id, export_level) + (def_id.to_def_id(), export_level) }) .collect(); @@ -142,7 +141,7 @@ fn reachable_non_generics_provider( reachable_non_generics.insert(id, SymbolExportLevel::C); } - tcx.arena.alloc(reachable_non_generics) + reachable_non_generics } fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: DefId) -> bool { @@ -162,11 +161,11 @@ fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> b fn exported_symbols_provider_local( tcx: TyCtxt<'_>, cnum: CrateNum, -) -> Arc, SymbolExportLevel)>> { +) -> &'tcx [(ExportedSymbol<'_>, SymbolExportLevel)] { assert_eq!(cnum, LOCAL_CRATE); if !tcx.sess.opts.output_types.should_codegen() { - return Arc::new(vec![]); + return &[]; } let mut symbols: Vec<_> = tcx @@ -213,7 +212,7 @@ fn exported_symbols_provider_local( })); } - if tcx.sess.crate_types.borrow().contains(&config::CrateType::Dylib) { + if tcx.sess.crate_types.borrow().contains(&CrateType::Dylib) { let symbol_name = metadata_symbol_name(tcx); let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); @@ -221,8 +220,8 @@ fn exported_symbols_provider_local( } if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() { - use rustc::mir::mono::{Linkage, MonoItem, Visibility}; - use rustc::ty::InstanceDef; + use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; + use rustc_middle::ty::InstanceDef; // Normally, we require that shared monomorphizations are not hidden, // because if we want to re-use a monomorphization from a Rust dylib, it @@ -272,13 +271,13 @@ fn exported_symbols_provider_local( // Sort so we get a stable incr. comp. hash. symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx)); - Arc::new(symbols) + tcx.arena.alloc_from_iter(symbols) } fn upstream_monomorphizations_provider( tcx: TyCtxt<'_>, cnum: CrateNum, -) -> &DefIdMap, CrateNum>> { +) -> DefIdMap, CrateNum>> { debug_assert!(cnum == LOCAL_CRATE); let cnums = tcx.all_crate_nums(LOCAL_CRATE); @@ -335,7 +334,7 @@ fn upstream_monomorphizations_provider( } } - tcx.arena.alloc(instances) + instances } fn upstream_monomorphizations_for_provider( @@ -358,8 +357,8 @@ fn upstream_drop_glue_for_provider<'tcx>( } fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) { - !tcx.reachable_set(LOCAL_CRATE).contains(&hir_id) + if let Some(def_id) = def_id.as_local() { + !tcx.reachable_set(LOCAL_CRATE).contains(&tcx.hir().as_local_hir_id(def_id)) } else { bug!("is_unreachable_local_definition called with non-local DefId: {:?}", def_id) } diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index a4b5acdcd18a2..53dfe7cb74998 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -1,20 +1,14 @@ -use super::command::Command; -use super::link::{self, get_linker, remove}; +use super::link::{self, remove}; use super::linker::LinkerInfo; use super::lto::{self, SerializedModule}; use super::symbol_export::symbol_name_for_instance_in_crate; use crate::{ CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, - RLIB_BYTECODE_EXTENSION, }; use crate::traits::*; use jobserver::{Acquired, Client}; -use rustc::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId}; -use rustc::middle::cstore::EncodedMetadata; -use rustc::middle::exported_symbols::SymbolExportLevel; -use rustc::ty::TyCtxt; use rustc_ast::attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfilerRef; @@ -27,17 +21,20 @@ use rustc_errors::{DiagnosticId, FatalError, Handler, Level}; use rustc_fs_util::link_or_copy; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ - copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, + copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::middle::exported_symbols::SymbolExportLevel; +use rustc_middle::ty::TyCtxt; use rustc_session::cgu_reuse_tracker::CguReuseTracker; -use rustc_session::config::{ - self, Lto, OutputFilenames, OutputType, Passes, Sanitizer, SwitchWithOptPath, -}; +use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; +use rustc_session::config::{Passes, Sanitizer, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::spec::MergeFunctions; +use rustc_target::spec::{MergeFunctions, PanicStrategy}; use std::any::Any; use std::fs; @@ -51,11 +48,27 @@ use std::thread; const PRE_LTO_BC_EXT: &str = "pre-lto.bc"; -/// The kind of bitcode to embed in object files. -#[derive(PartialEq)] -pub enum EmbedBitcode { +/// What kind of object file to emit. +#[derive(Clone, Copy, PartialEq)] +pub enum EmitObj { + // No object file. + None, + + // Just uncompressed llvm bitcode. Provides easy compatibility with + // emscripten's ecc compiler, when used as the linker. + Bitcode, + + // Object code, possibly augmented with a bitcode section. + ObjectCode(BitcodeSection), +} + +/// What kind of llvm bitcode section to embed in an object file. +#[derive(Clone, Copy, PartialEq)] +pub enum BitcodeSection { + // No bitcode section. None, - Marker, + + // A full, uncompressed bitcode section. Full, } @@ -81,10 +94,11 @@ pub struct ModuleConfig { pub emit_pre_lto_bc: bool, pub emit_no_opt_bc: bool, pub emit_bc: bool, - pub emit_bc_compressed: bool, pub emit_ir: bool, pub emit_asm: bool, - pub emit_obj: bool, + pub emit_obj: EmitObj, + pub bc_cmdline: String, + // Miscellaneous flags. These are mostly copied from command-line // options. pub verify_llvm_ir: bool, @@ -95,117 +109,153 @@ pub struct ModuleConfig { pub vectorize_slp: bool, pub merge_functions: bool, pub inline_threshold: Option, - pub new_llvm_pass_manager: Option, - // Instead of creating an object file by doing LLVM codegen, just - // make the object file bitcode. Provides easy compatibility with - // emscripten's ecc compiler, when used as the linker. - pub obj_is_bitcode: bool, - pub no_integrated_as: bool, - pub embed_bitcode: EmbedBitcode, + pub new_llvm_pass_manager: bool, + pub emit_lifetime_markers: bool, } impl ModuleConfig { - fn new(passes: Vec) -> ModuleConfig { - ModuleConfig { - passes, - opt_level: None, - opt_size: None, - - pgo_gen: SwitchWithOptPath::Disabled, - pgo_use: None, - - sanitizer: None, - sanitizer_recover: Default::default(), - sanitizer_memory_track_origins: 0, - - emit_no_opt_bc: false, - emit_pre_lto_bc: false, - emit_bc: false, - emit_bc_compressed: false, - emit_ir: false, - emit_asm: false, - emit_obj: false, - obj_is_bitcode: false, - embed_bitcode: EmbedBitcode::None, - no_integrated_as: false, - - verify_llvm_ir: false, - no_prepopulate_passes: false, - no_builtins: false, - time_module: true, - vectorize_loop: false, - vectorize_slp: false, - merge_functions: false, - inline_threshold: None, - new_llvm_pass_manager: None, + fn new( + kind: ModuleKind, + sess: &Session, + no_builtins: bool, + is_compiler_builtins: bool, + ) -> ModuleConfig { + // If it's a regular module, use `$regular`, otherwise use `$other`. + // `$regular` and `$other` are evaluated lazily. + macro_rules! if_regular { + ($regular: expr, $other: expr) => { + if let ModuleKind::Regular = kind { $regular } else { $other } + }; } - } - fn set_flags(&mut self, sess: &Session, no_builtins: bool) { - self.verify_llvm_ir = sess.verify_llvm_ir(); - self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; - self.no_builtins = no_builtins || sess.target.target.options.no_builtins; - self.inline_threshold = sess.opts.cg.inline_threshold; - self.new_llvm_pass_manager = sess.opts.debugging_opts.new_llvm_pass_manager; - self.obj_is_bitcode = - sess.target.target.options.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled(); - self.embed_bitcode = - if sess.target.target.options.embed_bitcode || sess.opts.debugging_opts.embed_bitcode { - match sess.opts.optimize { - config::OptLevel::No | config::OptLevel::Less => EmbedBitcode::Marker, - _ => EmbedBitcode::Full, - } - } else { - EmbedBitcode::None + let opt_level_and_size = if_regular!(Some(sess.opts.optimize), None); + + let save_temps = sess.opts.cg.save_temps; + + let should_emit_obj = sess.opts.output_types.contains_key(&OutputType::Exe) + || match kind { + ModuleKind::Regular => sess.opts.output_types.contains_key(&OutputType::Object), + ModuleKind::Allocator => false, + ModuleKind::Metadata => sess.opts.output_types.contains_key(&OutputType::Metadata), }; - // Copy what clang does by turning on loop vectorization at O2 and - // slp vectorization at O3. Otherwise configure other optimization aspects - // of this pass manager builder. - self.vectorize_loop = !sess.opts.cg.no_vectorize_loops - && (sess.opts.optimize == config::OptLevel::Default - || sess.opts.optimize == config::OptLevel::Aggressive); - - self.vectorize_slp = - !sess.opts.cg.no_vectorize_slp && sess.opts.optimize == config::OptLevel::Aggressive; - - // Some targets (namely, NVPTX) interact badly with the MergeFunctions - // pass. This is because MergeFunctions can generate new function calls - // which may interfere with the target calling convention; e.g. for the - // NVPTX target, PTX kernels should not call other PTX kernels. - // MergeFunctions can also be configured to generate aliases instead, - // but aliases are not supported by some backends (again, NVPTX). - // Therefore, allow targets to opt out of the MergeFunctions pass, - // but otherwise keep the pass enabled (at O2 and O3) since it can be - // useful for reducing code size. - self.merge_functions = match sess - .opts - .debugging_opts - .merge_functions - .unwrap_or(sess.target.target.options.merge_functions) + let emit_obj = if !should_emit_obj { + EmitObj::None + } else if sess.target.target.options.obj_is_bitcode + || sess.opts.cg.linker_plugin_lto.enabled() { - MergeFunctions::Disabled => false, - MergeFunctions::Trampolines | MergeFunctions::Aliases => { - sess.opts.optimize == config::OptLevel::Default - || sess.opts.optimize == config::OptLevel::Aggressive - } + EmitObj::Bitcode + } else if need_bitcode_in_object(sess) { + EmitObj::ObjectCode(BitcodeSection::Full) + } else { + EmitObj::ObjectCode(BitcodeSection::None) }; + + ModuleConfig { + passes: if_regular!( + { + let mut passes = sess.opts.cg.passes.clone(); + // compiler_builtins overrides the codegen-units settings, + // which is incompatible with -Zprofile which requires that + // only a single codegen unit is used per crate. + if sess.opts.debugging_opts.profile && !is_compiler_builtins { + passes.push("insert-gcov-profiling".to_owned()); + } + passes + }, + vec![] + ), + + opt_level: opt_level_and_size, + opt_size: opt_level_and_size, + + pgo_gen: if_regular!( + sess.opts.cg.profile_generate.clone(), + SwitchWithOptPath::Disabled + ), + pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None), + + sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer.clone(), None), + sanitizer_recover: if_regular!( + sess.opts.debugging_opts.sanitizer_recover.clone(), + vec![] + ), + sanitizer_memory_track_origins: if_regular!( + sess.opts.debugging_opts.sanitizer_memory_track_origins, + 0 + ), + + emit_pre_lto_bc: if_regular!( + save_temps || need_pre_lto_bitcode_for_incr_comp(sess), + false + ), + emit_no_opt_bc: if_regular!(save_temps, false), + emit_bc: if_regular!( + save_temps || sess.opts.output_types.contains_key(&OutputType::Bitcode), + save_temps + ), + emit_ir: if_regular!( + sess.opts.output_types.contains_key(&OutputType::LlvmAssembly), + false + ), + emit_asm: if_regular!( + sess.opts.output_types.contains_key(&OutputType::Assembly), + false + ), + emit_obj, + bc_cmdline: sess.target.target.options.bitcode_llvm_cmdline.clone(), + + verify_llvm_ir: sess.verify_llvm_ir(), + no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes, + no_builtins: no_builtins || sess.target.target.options.no_builtins, + + // Exclude metadata and allocator modules from time_passes output, + // since they throw off the "LLVM passes" measurement. + time_module: if_regular!(true, false), + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3. + vectorize_loop: !sess.opts.cg.no_vectorize_loops + && (sess.opts.optimize == config::OptLevel::Default + || sess.opts.optimize == config::OptLevel::Aggressive), + vectorize_slp: !sess.opts.cg.no_vectorize_slp + && sess.opts.optimize == config::OptLevel::Aggressive, + + // Some targets (namely, NVPTX) interact badly with the + // MergeFunctions pass. This is because MergeFunctions can generate + // new function calls which may interfere with the target calling + // convention; e.g. for the NVPTX target, PTX kernels should not + // call other PTX kernels. MergeFunctions can also be configured to + // generate aliases instead, but aliases are not supported by some + // backends (again, NVPTX). Therefore, allow targets to opt out of + // the MergeFunctions pass, but otherwise keep the pass enabled (at + // O2 and O3) since it can be useful for reducing code size. + merge_functions: match sess + .opts + .debugging_opts + .merge_functions + .unwrap_or(sess.target.target.options.merge_functions) + { + MergeFunctions::Disabled => false, + MergeFunctions::Trampolines | MergeFunctions::Aliases => { + sess.opts.optimize == config::OptLevel::Default + || sess.opts.optimize == config::OptLevel::Aggressive + } + }, + + inline_threshold: sess.opts.cg.inline_threshold, + new_llvm_pass_manager: sess.opts.debugging_opts.new_llvm_pass_manager, + emit_lifetime_markers: sess.emit_lifetime_markers(), + } } pub fn bitcode_needed(&self) -> bool { self.emit_bc - || self.obj_is_bitcode - || self.emit_bc_compressed - || self.embed_bitcode == EmbedBitcode::Full + || self.emit_obj == EmitObj::Bitcode + || self.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) } } -/// Assembler name and command used by codegen when no_integrated_as is enabled -pub struct AssemblerCommand { - name: PathBuf, - cmd: Command, -} - // HACK(eddyb) work around `#[derive]` producing wrong bounds for `Clone`. pub struct TargetMachineFactory( pub Arc Result + Send + Sync>, @@ -231,7 +281,7 @@ pub struct CodegenContext { pub fewer_names: bool, pub exported_symbols: Option>, pub opts: Arc, - pub crate_types: Vec, + pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, pub output_filenames: Arc, pub regular_module_config: Arc, @@ -258,8 +308,6 @@ pub struct CodegenContext { pub cgu_reuse_tracker: CguReuseTracker, // Channel back to the main control thread to send messages to pub coordinator_send: Sender>, - // The assembler command if no_integrated_as option is enabled, None otherwise - pub assembler_cmd: Option>, } impl CodegenContext { @@ -318,9 +366,12 @@ pub struct CompiledModules { pub allocator_module: Option, } -fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { - sess.crate_types.borrow().contains(&config::CrateType::Rlib) - && sess.opts.output_types.contains_key(&OutputType::Exe) +fn need_bitcode_in_object(sess: &Session) -> bool { + let requested_for_rlib = sess.opts.cg.embed_bitcode + && sess.crate_types.borrow().contains(&CrateType::Rlib) + && sess.opts.output_types.contains_key(&OutputType::Exe); + let forced_by_target = sess.target.target.options.forces_embed_bitcode; + requested_for_rlib || forced_by_target } fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { @@ -346,6 +397,8 @@ pub fn start_async_codegen( let crate_name = tcx.crate_name(LOCAL_CRATE); let crate_hash = tcx.crate_hash(LOCAL_CRATE); let no_builtins = attr::contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins); + let is_compiler_builtins = + attr::contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins); let subsystem = attr::first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem); let windows_subsystem = subsystem.map(|subsystem| { @@ -362,88 +415,12 @@ pub fn start_async_codegen( let linker_info = LinkerInfo::new(tcx); let crate_info = CrateInfo::new(tcx); - // Figure out what we actually need to build. - let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone()); - let mut metadata_config = ModuleConfig::new(vec![]); - let mut allocator_config = ModuleConfig::new(vec![]); - - if sess.opts.debugging_opts.profile { - modules_config.passes.push("insert-gcov-profiling".to_owned()) - } - - modules_config.pgo_gen = sess.opts.cg.profile_generate.clone(); - modules_config.pgo_use = sess.opts.cg.profile_use.clone(); - modules_config.sanitizer = sess.opts.debugging_opts.sanitizer.clone(); - modules_config.sanitizer_recover = sess.opts.debugging_opts.sanitizer_recover.clone(); - modules_config.sanitizer_memory_track_origins = - sess.opts.debugging_opts.sanitizer_memory_track_origins; - modules_config.opt_level = Some(sess.opts.optimize); - modules_config.opt_size = Some(sess.opts.optimize); - - // Save all versions of the bytecode if we're saving our temporaries. - if sess.opts.cg.save_temps { - modules_config.emit_no_opt_bc = true; - modules_config.emit_pre_lto_bc = true; - modules_config.emit_bc = true; - metadata_config.emit_bc = true; - allocator_config.emit_bc = true; - } - - // Emit compressed bitcode files for the crate if we're emitting an rlib. - // Whenever an rlib is created, the bitcode is inserted into the archive in - // order to allow LTO against it. - if need_crate_bitcode_for_rlib(sess) { - modules_config.emit_bc_compressed = true; - allocator_config.emit_bc_compressed = true; - } - - modules_config.emit_pre_lto_bc = need_pre_lto_bitcode_for_incr_comp(sess); - - modules_config.no_integrated_as = - tcx.sess.opts.cg.no_integrated_as || tcx.sess.target.target.options.no_integrated_as; - - for output_type in sess.opts.output_types.keys() { - match *output_type { - OutputType::Bitcode => { - modules_config.emit_bc = true; - } - OutputType::LlvmAssembly => { - modules_config.emit_ir = true; - } - OutputType::Assembly => { - modules_config.emit_asm = true; - // If we're not using the LLVM assembler, this function - // could be invoked specially with output_type_assembly, so - // in this case we still want the metadata object file. - if !sess.opts.output_types.contains_key(&OutputType::Assembly) { - metadata_config.emit_obj = true; - allocator_config.emit_obj = true; - } - } - OutputType::Object => { - modules_config.emit_obj = true; - } - OutputType::Metadata => { - metadata_config.emit_obj = true; - } - OutputType::Exe => { - modules_config.emit_obj = true; - metadata_config.emit_obj = true; - allocator_config.emit_obj = true; - } - OutputType::Mir => {} - OutputType::DepInfo => {} - } - } - - modules_config.set_flags(sess, no_builtins); - metadata_config.set_flags(sess, no_builtins); - allocator_config.set_flags(sess, no_builtins); - - // Exclude metadata and allocator modules from time_passes output, since - // they throw off the "LLVM passes" measurement. - metadata_config.time_module = false; - allocator_config.time_module = false; + let regular_config = + ModuleConfig::new(ModuleKind::Regular, sess, no_builtins, is_compiler_builtins); + let metadata_config = + ModuleConfig::new(ModuleKind::Metadata, sess, no_builtins, is_compiler_builtins); + let allocator_config = + ModuleConfig::new(ModuleKind::Allocator, sess, no_builtins, is_compiler_builtins); let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (codegen_worker_send, codegen_worker_receive) = channel(); @@ -457,7 +434,7 @@ pub fn start_async_codegen( coordinator_receive, total_cgus, sess.jobserver.clone(), - Arc::new(modules_config), + Arc::new(regular_config), Arc::new(metadata_config), Arc::new(allocator_config), coordinator_send.clone(), @@ -490,23 +467,13 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( return work_products; } - let _timer = sess.timer("incr_comp_copy_cgu_workproducts"); + let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir"); for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { - let mut files = vec![]; - - if let Some(ref path) = module.object { - files.push((WorkProductFileKind::Object, path.clone())); - } - if let Some(ref path) = module.bytecode { - files.push((WorkProductFileKind::Bytecode, path.clone())); - } - if let Some(ref path) = module.bytecode_compressed { - files.push((WorkProductFileKind::BytecodeCompressed, path.clone())); - } + let path = module.object.as_ref().map(|path| path.clone()); if let Some((id, product)) = - copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files) + copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, &path) { work_products.insert(id, product); } @@ -740,38 +707,34 @@ fn execute_work_item( } // Actual LTO type we end up choosing based on multiple factors. -enum ComputedLtoType { +pub enum ComputedLtoType { No, Thin, Fat, } -fn execute_optimize_work_item( - cgcx: &CodegenContext, - module: ModuleCodegen, - module_config: &ModuleConfig, -) -> Result, FatalError> { - let diag_handler = cgcx.create_diag_handler(); - - unsafe { - B::optimize(cgcx, &diag_handler, &module, module_config)?; +pub fn compute_per_cgu_lto_type( + sess_lto: &Lto, + opts: &config::Options, + sess_crate_types: &[CrateType], + module_kind: ModuleKind, +) -> ComputedLtoType { + // Metadata modules never participate in LTO regardless of the lto + // settings. + if module_kind == ModuleKind::Metadata { + return ComputedLtoType::No; } - // After we've done the initial round of optimizations we need to - // decide whether to synchronously codegen this module or ship it - // back to the coordinator thread for further LTO processing (which - // has to wait for all the initial modules to be optimized). - // If the linker does LTO, we don't have to do it. Note that we // keep doing full LTO, if it is requested, as not to break the // assumption that the output will be a single module. - let linker_does_lto = cgcx.opts.cg.linker_plugin_lto.enabled(); + let linker_does_lto = opts.cg.linker_plugin_lto.enabled(); // When we're automatically doing ThinLTO for multi-codegen-unit // builds we don't actually want to LTO the allocator modules if // it shows up. This is due to various linker shenanigans that // we'll encounter later. - let is_allocator = module.kind == ModuleKind::Allocator; + let is_allocator = module_kind == ModuleKind::Allocator; // We ignore a request for full crate grath LTO if the cate type // is only an rlib, as there is no full crate graph to process, @@ -781,20 +744,33 @@ fn execute_optimize_work_item( // require LTO so the request for LTO is always unconditionally // passed down to the backend, but we don't actually want to do // anything about it yet until we've got a final product. - let is_rlib = cgcx.crate_types.len() == 1 && cgcx.crate_types[0] == config::CrateType::Rlib; + let is_rlib = sess_crate_types.len() == 1 && sess_crate_types[0] == CrateType::Rlib; - // Metadata modules never participate in LTO regardless of the lto - // settings. - let lto_type = if module.kind == ModuleKind::Metadata { - ComputedLtoType::No - } else { - match cgcx.lto { - Lto::ThinLocal if !linker_does_lto && !is_allocator => ComputedLtoType::Thin, - Lto::Thin if !linker_does_lto && !is_rlib => ComputedLtoType::Thin, - Lto::Fat if !is_rlib => ComputedLtoType::Fat, - _ => ComputedLtoType::No, - } - }; + match sess_lto { + Lto::ThinLocal if !linker_does_lto && !is_allocator => ComputedLtoType::Thin, + Lto::Thin if !linker_does_lto && !is_rlib => ComputedLtoType::Thin, + Lto::Fat if !is_rlib => ComputedLtoType::Fat, + _ => ComputedLtoType::No, + } +} + +fn execute_optimize_work_item( + cgcx: &CodegenContext, + module: ModuleCodegen, + module_config: &ModuleConfig, +) -> Result, FatalError> { + let diag_handler = cgcx.create_diag_handler(); + + unsafe { + B::optimize(cgcx, &diag_handler, &module, module_config)?; + } + + // After we've done the initial round of optimizations we need to + // decide whether to synchronously codegen this module or ship it + // back to the coordinator thread for further LTO processing (which + // has to wait for all the initial modules to be optimized). + + let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types, module.kind); // If we're doing some form of incremental LTO then we need to be sure to // save our module to disk first. @@ -839,29 +815,9 @@ fn execute_copy_from_cache_work_item( ) -> Result, FatalError> { let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); let mut object = None; - let mut bytecode = None; - let mut bytecode_compressed = None; - for (kind, saved_file) in &module.source.saved_files { - let obj_out = match kind { - WorkProductFileKind::Object => { - let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)); - object = Some(path.clone()); - path - } - WorkProductFileKind::Bytecode => { - let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(&module.name)); - bytecode = Some(path.clone()); - path - } - WorkProductFileKind::BytecodeCompressed => { - let path = cgcx - .output_filenames - .temp_path(OutputType::Bitcode, Some(&module.name)) - .with_extension(RLIB_BYTECODE_EXTENSION); - bytecode_compressed = Some(path.clone()); - path - } - }; + if let Some(saved_file) = module.source.saved_file { + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)); + object = Some(obj_out.clone()); let source_file = in_incr_comp_dir(&incr_comp_session_dir, &saved_file); debug!( "copying pre-existing module `{}` from {:?} to {}", @@ -880,16 +836,13 @@ fn execute_copy_from_cache_work_item( } } - assert_eq!(object.is_some(), module_config.emit_obj); - assert_eq!(bytecode.is_some(), module_config.emit_bc); - assert_eq!(bytecode_compressed.is_some(), module_config.emit_bc_compressed); + assert_eq!(object.is_some(), module_config.emit_obj != EmitObj::None); Ok(WorkItemResult::Compiled(CompiledModule { name: module.name, kind: ModuleKind::Regular, object, - bytecode, - bytecode_compressed, + bytecode: None, })) } @@ -957,7 +910,7 @@ fn start_executing_work( coordinator_receive: Receiver>, total_cgus: usize, jobserver: Client, - modules_config: Arc, + regular_config: Arc, metadata_config: Arc, allocator_config: Arc, tx_to_llvm_workers: Sender>, @@ -1014,17 +967,6 @@ fn start_executing_work( each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); - let assembler_cmd = if modules_config.no_integrated_as { - // HACK: currently we use linker (gcc) as our assembler - let (linker, flavor) = link::linker_and_flavor(sess); - - let (name, mut cmd) = get_linker(sess, &linker, flavor); - cmd.args(&sess.target.target.options.asm_args); - Some(Arc::new(AssemblerCommand { name, cmd })) - } else { - None - }; - let ol = if tcx.sess.opts.debugging_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { @@ -1038,7 +980,7 @@ fn start_executing_work( crate_types: sess.crate_types.borrow().clone(), each_linked_rlib_for_lto, lto: sess.lto(), - no_landing_pads: sess.no_landing_pads(), + no_landing_pads: sess.panic_strategy() == PanicStrategy::Abort, fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, opts: Arc::new(sess.opts.clone()), @@ -1051,16 +993,15 @@ fn start_executing_work( coordinator_send, diag_emitter: shared_emitter.clone(), output_filenames: tcx.output_filenames(LOCAL_CRATE), - regular_module_config: modules_config, + regular_module_config: regular_config, metadata_module_config: metadata_config, allocator_module_config: allocator_config, - tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol, false)), + tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)), total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), target_arch: tcx.sess.target.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, - assembler_cmd, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1538,7 +1479,7 @@ fn start_executing_work( } } -pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX; +pub const CODEGEN_WORKER_ID: usize = usize::MAX; /// `FatalError` is explicitly not `Send`. #[must_use] @@ -1594,44 +1535,6 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem }); } -pub fn run_assembler( - cgcx: &CodegenContext, - handler: &Handler, - assembly: &Path, - object: &Path, -) { - let assembler = cgcx.assembler_cmd.as_ref().expect("cgcx.assembler_cmd is missing?"); - - let pname = &assembler.name; - let mut cmd = assembler.cmd.clone(); - cmd.arg("-c").arg("-o").arg(object).arg(assembly); - debug!("{:?}", cmd); - - match cmd.output() { - Ok(prog) => { - if !prog.status.success() { - let mut note = prog.stderr.clone(); - note.extend_from_slice(&prog.stdout); - - handler - .struct_err(&format!( - "linking with `{}` failed: {}", - pname.display(), - prog.status - )) - .note(&format!("{:?}", &cmd)) - .note(str::from_utf8(¬e[..]).unwrap()) - .emit(); - handler.abort_if_errors(); - } - } - Err(e) => { - handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e)); - handler.abort_if_errors(); - } - } -} - enum SharedEmitterMessage { Diagnostic(Diagnostic), InlineAsmError(u32, String), @@ -1895,7 +1798,7 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool { ); tcx.sess.target.target.options.is_like_msvc && - tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateType::Rlib) && + tcx.sess.crate_types.borrow().iter().any(|ct| *ct == CrateType::Rlib) && // ThinLTO can't handle this workaround in all cases, so we don't // emit the `__imp_` symbols. Instead we make them unnecessary by disallowing // dynamic linking when linker plugin LTO is enabled. diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 834702a30099c..29398db6ff8a9 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -14,8 +14,8 @@ //! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`. use crate::back::write::{ - start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, - submit_pre_lto_module_to_llvm, OngoingCodegen, + compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, + submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, }; use crate::common::{IntPredicate, RealPredicate, TypeKind}; use crate::meth; @@ -25,28 +25,29 @@ use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{CachedModuleCodegen, CrateInfo, MemFlags, ModuleCodegen, ModuleKind}; -use rustc::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc::middle::cstore::EncodedMetadata; -use rustc::middle::cstore::{self, LinkagePreference}; -use rustc::middle::lang_items; -use rustc::middle::lang_items::StartFnLangItem; -use rustc::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; -use rustc::ty::layout::{self, Align, HasTyCtxt, LayoutOf, TyLayout, VariantIdx}; -use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; -use rustc::ty::query::Providers; -use rustc::ty::{self, Instance, Ty, TyCtxt}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::print_time_passes_entry; use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator}; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::lang_items::StartFnLangItem; use rustc_index::vec::Idx; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::middle::cstore::{self, LinkagePreference}; +use rustc_middle::middle::lang_items; +use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; +use rustc_middle::ty::layout::{self, HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::cgu_reuse_tracker::CguReuse; -use rustc_session::config::{self, EntryFnType, Lto}; +use rustc_session::config::{self, EntryFnType}; use rustc_session::Session; use rustc_span::Span; use rustc_symbol_mangling::test as symbol_names_test; +use rustc_target::abi::{Abi, Align, LayoutOf, Scalar, VariantIdx}; use std::cmp; use std::ops::{Deref, DerefMut}; @@ -181,8 +182,7 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> (Bx::Value, Bx::Value) { debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); match (&src_ty.kind, &dst_ty.kind) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) - | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { assert!(bx.cx().type_is_sized(a)); let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b))); @@ -231,9 +231,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let src_ty = src.layout.ty; let dst_ty = dst.layout.ty; match (&src_ty.kind, &dst_ty.kind) { - (&ty::Ref(..), &ty::Ref(..)) - | (&ty::Ref(..), &ty::RawPtr(..)) - | (&ty::RawPtr(..), &ty::RawPtr(..)) => { + (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => { let (base, info) = match bx.load_operand(src).val { OperandValue::Pair(base, info) => { // fat-ptr to fat-ptr unsize preserves the vtable @@ -341,9 +339,9 @@ pub fn from_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( pub fn to_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, val: Bx::Value, - layout: layout::TyLayout<'_>, + layout: layout::TyAndLayout<'_>, ) -> Bx::Value { - if let layout::Abi::Scalar(ref scalar) = layout.abi { + if let Abi::Scalar(ref scalar) = layout.abi { return to_immediate_scalar(bx, val, scalar); } val @@ -352,7 +350,7 @@ pub fn to_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( pub fn to_immediate_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, val: Bx::Value, - scalar: &layout::Scalar, + scalar: &Scalar, ) -> Bx::Value { if scalar.is_bool() { return bx.trunc(val, bx.cx().type_i1()); @@ -366,7 +364,7 @@ pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( dst_align: Align, src: Bx::Value, src_align: Align, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, flags: MemFlags, ) { let size = layout.size.bytes(); @@ -399,7 +397,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( None => return None, }; - let instance = Instance::mono(cx.tcx(), main_def_id); + let instance = Instance::mono(cx.tcx(), main_def_id.to_def_id()); if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) { // We want to create the wrapper in the same codegen unit as Rust's main @@ -418,7 +416,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, sp: Span, rust_main: Bx::Value, - rust_main_def_id: DefId, + rust_main_def_id: LocalDefId, use_start_lang_item: bool, ) -> Bx::Function { // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, @@ -467,6 +465,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( start_def_id, cx.tcx().intern_substs(&[main_ret_ty.into()]), ) + .unwrap() .unwrap(), ); ( @@ -506,7 +505,7 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX; +pub const CODEGEN_WORKER_ID: usize = usize::MAX; pub fn codegen_crate( backend: B, @@ -532,7 +531,6 @@ pub fn codegen_crate( // Run the monomorphization collector and partition the collected items into // codegen units. let codegen_units = tcx.collect_and_partition_mono_items(LOCAL_CRATE).1; - let codegen_units = (*codegen_units).clone(); // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -540,8 +538,8 @@ pub fn codegen_crate( // lead to having to re-execute compile_codegen_unit, possibly // unnecessarily. if tcx.dep_graph.is_fully_enabled() { - for cgu in &codegen_units { - tcx.codegen_unit(cgu.name()); + for cgu in codegen_units { + tcx.ensure().codegen_unit(cgu.name()); } } @@ -557,7 +555,7 @@ pub fn codegen_crate( // one instead. If nothing exists then it's our job to generate the // allocator! let any_dynamic_crate = tcx.dependency_formats(LOCAL_CRATE).iter().any(|(_, list)| { - use rustc::middle::dependency_format::Linkage; + use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); let allocator_module = if any_dynamic_crate { @@ -602,7 +600,7 @@ pub fn codegen_crate( // We sort the codegen units by size. This way we can schedule work for LLVM // a bit more efficiently. let codegen_units = { - let mut codegen_units = codegen_units; + let mut codegen_units = codegen_units.iter().collect::>(); codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); codegen_units }; @@ -910,7 +908,7 @@ pub fn provide_both(providers: &mut Providers<'_>) { .map(|id| &module_map[&id]) .flat_map(|module| module.foreign_items.iter().cloned()) .collect(); - tcx.arena.alloc(dllimports) + dllimports }; providers.is_dllimport_foreign_item = @@ -943,8 +941,18 @@ fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguR ); if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { - // We can re-use either the pre- or the post-thinlto state - if tcx.sess.lto() != Lto::No { CguReuse::PreLto } else { CguReuse::PostLto } + // We can re-use either the pre- or the post-thinlto state. If no LTO is + // being performed then we can use post-LTO artifacts, otherwise we must + // reuse pre-LTO artifacts + match compute_per_cgu_lto_type( + &tcx.sess.lto(), + &tcx.sess.opts, + &tcx.sess.crate_types.borrow(), + ModuleKind::Regular, + ) { + ComputedLtoType::No => CguReuse::PostLto, + _ => CguReuse::PreLto, + } } else { CguReuse::No } diff --git a/src/librustc_codegen_ssa/common.rs b/src/librustc_codegen_ssa/common.rs index 147b23995023f..0d0321ec4ae5e 100644 --- a/src/librustc_codegen_ssa/common.rs +++ b/src/librustc_codegen_ssa/common.rs @@ -1,17 +1,16 @@ #![allow(non_camel_case_types, non_snake_case)] -use rustc::ty::{Ty, TyCtxt}; use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_session::Session; use rustc_span::Span; use crate::base; -use crate::traits::*; -use rustc::middle::lang_items::LangItem; -use rustc_hir::def_id::DefId; - use crate::traits::BuilderMethods; -use rustc_hir as hir; +use crate::traits::*; pub enum IntPredicate { IntEQ, diff --git a/src/librustc_codegen_ssa/debuginfo/type_names.rs b/src/librustc_codegen_ssa/debuginfo/type_names.rs index 8dd35208bf69a..1d8730db54602 100644 --- a/src/librustc_codegen_ssa/debuginfo/type_names.rs +++ b/src/librustc_codegen_ssa/debuginfo/type_names.rs @@ -1,9 +1,9 @@ // Type Names for Debug Info. -use rustc::ty::{self, subst::SubstsRef, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, subst::SubstsRef, Ty, TyCtxt}; // Compute the name of the type as it should be stored in debuginfo. Does not do // any caching, i.e., calling the function twice with the same type will also do @@ -198,7 +198,6 @@ pub fn push_debuginfo_type_name<'tcx>( ty::Error | ty::Infer(_) | ty::Placeholder(..) - | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::Bound(..) | ty::Opaque(..) diff --git a/src/librustc_codegen_ssa/glue.rs b/src/librustc_codegen_ssa/glue.rs index a43e84272868c..5b086bc43ff35 100644 --- a/src/librustc_codegen_ssa/glue.rs +++ b/src/librustc_codegen_ssa/glue.rs @@ -5,7 +5,7 @@ use crate::common::IntPredicate; use crate::meth; use crate::traits::*; -use rustc::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty}; pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, diff --git a/src/librustc_codegen_ssa/lib.rs b/src/librustc_codegen_ssa/lib.rs index d2afbdc23e1fd..7dc09b595c36e 100644 --- a/src/librustc_codegen_ssa/lib.rs +++ b/src/librustc_codegen_ssa/lib.rs @@ -4,6 +4,7 @@ #![feature(try_blocks)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![feature(trusted_len)] #![feature(associated_type_bounds)] #![recursion_limit = "256"] @@ -15,17 +16,17 @@ #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; -use rustc::dep_graph::WorkProduct; -use rustc::middle::cstore::{CrateSource, LibSource, NativeLibrary}; -use rustc::middle::dependency_format::Dependencies; -use rustc::middle::lang_items::LangItem; -use rustc::ty::query::Providers; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::CrateNum; +use rustc_hir::LangItem; +use rustc_middle::dep_graph::WorkProduct; +use rustc_middle::middle::cstore::{CrateSource, LibSource, NativeLibrary}; +use rustc_middle::middle::dependency_format::Dependencies; +use rustc_middle::ty::query::Providers; use rustc_session::config::{OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_span::symbol::Symbol; use std::path::{Path, PathBuf}; @@ -54,31 +55,18 @@ pub struct ModuleCodegen { // FIXME(eddyb) maybe include the crate name in this? pub const METADATA_FILENAME: &str = "lib.rmeta"; -pub const RLIB_BYTECODE_EXTENSION: &str = "bc.z"; impl ModuleCodegen { pub fn into_compiled_module( self, emit_obj: bool, emit_bc: bool, - emit_bc_compressed: bool, outputs: &OutputFilenames, ) -> CompiledModule { let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name))); let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name))); - let bytecode_compressed = emit_bc_compressed.then(|| { - outputs - .temp_path(OutputType::Bitcode, Some(&self.name)) - .with_extension(RLIB_BYTECODE_EXTENSION) - }); - - CompiledModule { - name: self.name.clone(), - kind: self.kind, - object, - bytecode, - bytecode_compressed, - } + + CompiledModule { name: self.name.clone(), kind: self.kind, object, bytecode } } } @@ -88,7 +76,6 @@ pub struct CompiledModule { pub kind: ModuleKind, pub object: Option, pub bytecode: Option, - pub bytecode_compressed: Option, } pub struct CachedModuleCodegen { @@ -144,7 +131,7 @@ pub struct CodegenResults { pub allocator_module: Option, pub metadata_module: Option, pub crate_hash: Svh, - pub metadata: rustc::middle::cstore::EncodedMetadata, + pub metadata: rustc_middle::middle::cstore::EncodedMetadata, pub windows_subsystem: Option, pub linker_info: back::linker::LinkerInfo, pub crate_info: CrateInfo, diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index c36ef9b480cf0..199dd8c7df42f 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -1,6 +1,6 @@ use crate::traits::*; -use rustc::ty::{self, Instance, Ty}; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_target::abi::call::FnAbi; #[derive(Copy, Clone, Debug)] diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs index 04680a1751734..5e3a37e20bd4f 100644 --- a/src/librustc_codegen_ssa/mir/analyze.rs +++ b/src/librustc_codegen_ssa/mir/analyze.rs @@ -3,16 +3,17 @@ use super::FunctionCx; use crate::traits::*; -use rustc::mir::traversal; -use rustc::mir::visit::{ - MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor, -}; -use rustc::mir::{self, Location, TerminatorKind}; -use rustc::ty; -use rustc::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_data_structures::graph::dominators::Dominators; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::traversal; +use rustc_middle::mir::visit::{ + MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor, +}; +use rustc_middle::mir::{self, Location, TerminatorKind}; +use rustc_middle::ty; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_target::abi::LayoutOf; pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx: &FunctionCx<'a, 'tcx, Bx>, @@ -20,7 +21,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let mir = fx.mir; let mut analyzer = LocalAnalyzer::new(fx); - analyzer.visit_body(mir); + analyzer.visit_body(&mir); for (local, decl) in mir.local_decls.iter_enumerated() { let ty = fx.monomorphize(&decl.ty); @@ -112,13 +113,14 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { // Allow uses of projections that are ZSTs or from scalar fields. let is_consume = match context { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true, + PlaceContext::NonMutatingUse( + NonMutatingUseContext::Copy | NonMutatingUseContext::Move, + ) => true, _ => false, }; if is_consume { let base_ty = - mir::Place::ty_from(place_ref.local, proj_base, *self.fx.mir, cx.tcx()); + mir::Place::ty_from(place_ref.local, proj_base, self.fx.mir, cx.tcx()); let base_ty = self.fx.monomorphize(&base_ty); // ZSTs don't require any actual memory access. @@ -202,7 +204,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { }; } - self.visit_place_base(&place_ref.local, context, location); + self.visit_local(&place_ref.local, context, location); self.visit_projection(place_ref.local, place_ref.projection, context, location); } } @@ -267,14 +269,16 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> fn visit_local(&mut self, &local: &mir::Local, context: PlaceContext, location: Location) { match context { - PlaceContext::MutatingUse(MutatingUseContext::Call) => { + PlaceContext::MutatingUse(MutatingUseContext::Call) + | PlaceContext::MutatingUse(MutatingUseContext::Yield) => { self.assign(local, location); } PlaceContext::NonUse(_) | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {} - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => { + PlaceContext::NonMutatingUse( + NonMutatingUseContext::Copy | NonMutatingUseContext::Move, + ) => { // Reads from uninitialized variables (e.g., in dead code, after // optimizations) require locals to be in (uninitialized) memory. // N.B., there can be uninitialized reads of a local visited after @@ -290,17 +294,21 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> } } - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) - | PlaceContext::MutatingUse(MutatingUseContext::Store) - | PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) - | PlaceContext::MutatingUse(MutatingUseContext::AddressOf) - | PlaceContext::MutatingUse(MutatingUseContext::Projection) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => { + PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::AsmOutput + | MutatingUseContext::Borrow + | MutatingUseContext::AddressOf + | MutatingUseContext::Projection, + ) + | PlaceContext::NonMutatingUse( + NonMutatingUseContext::Inspect + | NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::UniqueBorrow + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::AddressOf + | NonMutatingUseContext::Projection, + ) => { self.not_ssa(local); } @@ -350,7 +358,8 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec { /* nothing to do */ } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { /* nothing to do */ } TerminatorKind::Call { cleanup: unwind, .. } | TerminatorKind::Assert { cleanup: unwind, .. } | TerminatorKind::DropAndReplace { unwind, .. } diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 8433f79302077..b487ed8dea8b6 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -9,14 +9,17 @@ use crate::meth; use crate::traits::*; use crate::MemFlags; -use rustc::middle::lang_items; -use rustc::mir; -use rustc::mir::AssertKind; -use rustc::ty::layout::{self, FnAbiExt, HasTyCtxt, LayoutOf}; -use rustc::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_ast::ast; +use rustc_hir::lang_items; use rustc_index::vec::Idx; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::AssertKind; +use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; +use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; use rustc_span::{source_map::Span, symbol::Symbol}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use rustc_target::abi::{self, LayoutOf}; use rustc_target::spec::abi::Abi; use std::borrow::Cow; @@ -110,7 +113,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, cleanup: Option, ) { - if let Some(cleanup) = cleanup { + // If there is a cleanup block and the function we're calling can unwind, then + // do an invoke, otherwise do a call. + if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) { let ret_bx = if let Some((_, target)) = destination { fx.blocks[target] } else { @@ -149,7 +154,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { // a loop. fn maybe_sideeffect>( &self, - mir: mir::ReadOnlyBodyAndCache<'tcx, 'tcx>, + mir: &'tcx mir::Body<'tcx>, bx: &mut Bx, targets: &[mir::BasicBlock], ) { @@ -299,11 +304,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &mut self, helper: TerminatorCodegenHelper<'tcx>, mut bx: Bx, - location: &mir::Place<'tcx>, + location: mir::Place<'tcx>, target: mir::BasicBlock, unwind: Option, ) { - let ty = location.ty(*self.mir, bx.tcx()).ty; + let ty = location.ty(self.mir, bx.tcx()).ty; let ty = self.monomorphize(&ty); let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty); @@ -534,6 +539,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::FnDef(def_id, substs) => ( Some( ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs) + .unwrap() .unwrap(), ), None, @@ -568,7 +574,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let extra_args = extra_args .iter() .map(|op_arg| { - let op_ty = op_arg.ty(*self.mir, bx.tcx()); + let op_ty = op_arg.ty(self.mir, bx.tcx()); self.monomorphize(&op_ty) }) .collect::>(); @@ -580,7 +586,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if intrinsic == Some("transmute") { if let Some(destination_ref) = destination.as_ref() { - let &(ref dest, target) = destination_ref; + let &(dest, target) = destination_ref; self.codegen_transmute(&mut bx, &args[0], dest); helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); @@ -591,7 +597,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // we can do what we like. Here, we declare that transmuting // into an uninhabited type is impossible, so anything following // it must be unreachable. - assert_eq!(fn_abi.ret.layout.abi, layout::Abi::Uninhabited); + assert_eq!(fn_abi.ret.layout.abi, abi::Abi::Uninhabited); bx.unreachable(); } return; @@ -619,7 +625,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut llargs = Vec::with_capacity(arg_count); // Prepare the return value destination - let ret_dest = if let Some((ref dest, _)) = *destination { + let ret_dest = if let Some((dest, _)) = *destination { let is_intrinsic = intrinsic.is_some(); self.make_return_dest(&mut bx, dest, &fn_abi.ret, &mut llargs, is_intrinsic) } else { @@ -816,6 +822,123 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cleanup, ); } + + fn codegen_asm_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + template: &[ast::InlineAsmTemplatePiece], + operands: &[mir::InlineAsmOperand<'tcx>], + options: ast::InlineAsmOptions, + destination: Option, + ) { + let span = terminator.source_info.span; + + let operands: Vec<_> = operands + .iter() + .map(|op| match *op { + mir::InlineAsmOperand::In { reg, ref value } => { + let value = self.codegen_operand(&mut bx, value); + InlineAsmOperandRef::In { reg, value } + } + mir::InlineAsmOperand::Out { reg, late, ref place } => { + let place = place.map(|place| self.codegen_place(&mut bx, place.as_ref())); + InlineAsmOperandRef::Out { reg, late, place } + } + mir::InlineAsmOperand::InOut { reg, late, ref in_value, ref out_place } => { + let in_value = self.codegen_operand(&mut bx, in_value); + let out_place = + out_place.map(|out_place| self.codegen_place(&mut bx, out_place.as_ref())); + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } + } + mir::InlineAsmOperand::Const { ref value } => { + if let mir::Operand::Constant(constant) = value { + let const_value = self + .eval_mir_constant(constant) + .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); + let ty = constant.literal.ty; + let size = bx.layout_of(ty).size; + let scalar = match const_value { + // Promoted constants are evaluated into a ByRef instead of a Scalar, + // but we want the scalar value here. + ConstValue::ByRef { alloc, offset } => { + let ptr = Pointer::new(AllocId(0), offset); + alloc + .read_scalar(&bx, ptr, size) + .and_then(|s| s.not_undef()) + .unwrap_or_else(|e| { + bx.tcx().sess.span_err( + span, + &format!("Could not evaluate asm const: {}", e), + ); + + // We are erroring out, just emit a dummy constant. + Scalar::from_u64(0) + }) + } + _ => span_bug!(span, "expected ByRef for promoted asm const"), + }; + let value = scalar.assert_bits(size); + let string = match ty.kind { + ty::Uint(_) => value.to_string(), + ty::Int(int_ty) => { + match int_ty.normalize(bx.tcx().sess.target.ptr_width) { + ast::IntTy::I8 => (value as i8).to_string(), + ast::IntTy::I16 => (value as i16).to_string(), + ast::IntTy::I32 => (value as i32).to_string(), + ast::IntTy::I64 => (value as i64).to_string(), + ast::IntTy::I128 => (value as i128).to_string(), + ast::IntTy::Isize => unreachable!(), + } + } + ty::Float(ast::FloatTy::F32) => { + f32::from_bits(value as u32).to_string() + } + ty::Float(ast::FloatTy::F64) => { + f64::from_bits(value as u64).to_string() + } + _ => span_bug!(span, "asm const has bad type {}", ty), + }; + InlineAsmOperandRef::Const { string } + } else { + span_bug!(span, "asm const is not a constant"); + } + } + mir::InlineAsmOperand::SymFn { ref value } => { + let literal = self.monomorphize(&value.literal); + if let ty::FnDef(def_id, substs) = literal.ty.kind { + let instance = ty::Instance::resolve( + bx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap() + .unwrap(); + InlineAsmOperandRef::SymFn { instance } + } else { + span_bug!(span, "invalid type for asm sym (fn)"); + } + } + mir::InlineAsmOperand::SymStatic { ref value } => { + if let Some(def_id) = value.check_static_ptr(bx.tcx()) { + InlineAsmOperandRef::SymStatic { def_id } + } else { + span_bug!(span, "invalid type for asm sym (static)"); + } + } + }) + .collect(); + + bx.codegen_inline_asm(template, &operands, options, span); + + if let Some(target) = destination { + helper.funclet_br(self, &mut bx, target); + } else { + bx.unreachable(); + } + } } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -873,7 +996,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); } - mir::TerminatorKind::Drop { ref location, target, unwind } => { + mir::TerminatorKind::Drop { location, target, unwind } => { self.codegen_drop_terminator(helper, bx, location, target, unwind); } @@ -910,6 +1033,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::TerminatorKind::FalseEdges { .. } | mir::TerminatorKind::FalseUnwind { .. } => { bug!("borrowck false edges in codegen") } + + mir::TerminatorKind::InlineAsm { template, ref operands, options, destination } => { + self.codegen_asm_terminator( + helper, + bx, + terminator, + template, + operands, + options, + destination, + ); + } } } @@ -994,7 +1129,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // the load would just produce `OperandValue::Ref` instead // of the `OperandValue::Immediate` we need for the call. llval = bx.load(llval, align); - if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if let abi::Abi::Scalar(ref scalar) = arg.layout.abi { if scalar.is_bool() { bx.range_metadata(llval, 0..2); } @@ -1123,7 +1258,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn make_return_dest( &mut self, bx: &mut Bx, - dest: &mir::Place<'tcx>, + dest: mir::Place<'tcx>, fn_ret: &ArgAbi<'tcx, Ty<'tcx>>, llargs: &mut Vec, is_intrinsic: bool, @@ -1184,7 +1319,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn codegen_transmute(&mut self, bx: &mut Bx, src: &mir::Operand<'tcx>, dst: &mir::Place<'tcx>) { + fn codegen_transmute(&mut self, bx: &mut Bx, src: &mir::Operand<'tcx>, dst: mir::Place<'tcx>) { if let Some(index) = dst.as_local() { match self.locals[index] { LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), diff --git a/src/librustc_codegen_ssa/mir/constant.rs b/src/librustc_codegen_ssa/mir/constant.rs index 74a9646a0c0af..d2629b771c2af 100644 --- a/src/librustc_codegen_ssa/mir/constant.rs +++ b/src/librustc_codegen_ssa/mir/constant.rs @@ -1,11 +1,12 @@ use crate::mir::operand::OperandRef; use crate::traits::*; -use rustc::mir; -use rustc::mir::interpret::{ConstValue, ErrorHandled}; -use rustc::ty::layout::{self, HasTyCtxt}; -use rustc::ty::{self, Ty}; use rustc_index::vec::Idx; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; +use rustc_target::abi::Abi; use super::FunctionCx; @@ -15,25 +16,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx: &mut Bx, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { - match constant.literal.val { - // Special case unevaluated statics, because statics have an identity and thus should - // use `get_static` to get at their id. - // FIXME(oli-obk): can we unify this somehow, maybe by making const eval of statics - // always produce `&STATIC`. This may also simplify how const eval works with statics. - ty::ConstKind::Unevaluated(def_id, substs, None) if self.cx.tcx().is_static(def_id) => { - assert!(substs.is_empty(), "we don't support generic statics yet"); - let static_ = bx.get_static(def_id); - // we treat operands referring to statics as if they were `&STATIC` instead - let ptr_ty = self.cx.tcx().mk_mut_ptr(self.monomorphize(&constant.literal.ty)); - let layout = bx.layout_of(ptr_ty); - Ok(OperandRef::from_immediate_or_packed_pair(bx, static_, layout)) - } - _ => { - let val = self.eval_mir_constant(constant)?; - let ty = self.monomorphize(&constant.literal.ty); - Ok(OperandRef::from_const(bx, val, ty)) - } - } + let val = self.eval_mir_constant(constant)?; + let ty = self.monomorphize(&constant.literal.ty); + Ok(OperandRef::from_const(bx, val, ty)) } pub fn eval_mir_constant( @@ -87,7 +72,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(prim) = field.try_to_scalar() { let layout = bx.layout_of(field_ty); let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, + Abi::Scalar(ref x) => x, _ => bug!("from_const: invalid ByVal layout: {:#?}", layout), }; bx.scalar_to_backend(prim, scalar, bx.immediate_backend_type(layout)) diff --git a/src/librustc_codegen_ssa/mir/debuginfo.rs b/src/librustc_codegen_ssa/mir/debuginfo.rs index cbe5f51115246..d166a27b5a982 100644 --- a/src/librustc_codegen_ssa/mir/debuginfo.rs +++ b/src/librustc_codegen_ssa/mir/debuginfo.rs @@ -1,13 +1,12 @@ use crate::traits::*; -use rustc::mir; -use rustc::ty; -use rustc::ty::layout::{LayoutOf, Size}; use rustc_hir::def_id::CrateNum; use rustc_index::vec::IndexVec; +use rustc_middle::mir; +use rustc_middle::ty; use rustc_session::config::DebugInfo; - use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; +use rustc_target::abi::{LayoutOf, Size}; use super::operand::OperandValue; use super::place::PlaceRef; @@ -116,7 +115,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; // FIXME(eddyb) maybe name the return place as `_0` or `return`? - if local == mir::RETURN_PLACE { + if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable() + { return; } diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 000db0155ada3..00b4bf96afa59 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -1,8 +1,10 @@ use crate::base; use crate::traits::*; -use rustc::mir; -use rustc::ty::layout::{FnAbiExt, HasTyCtxt, TyLayout}; -use rustc::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_errors::ErrorReported; +use rustc_middle::mir; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; use rustc_target::abi::call::{FnAbi, PassMode}; use std::iter; @@ -13,7 +15,7 @@ use rustc_index::vec::IndexVec; use self::analyze::CleanupKind; use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo}; use self::place::PlaceRef; -use rustc::mir::traversal; +use rustc_middle::mir::traversal; use self::operand::{OperandRef, OperandValue}; @@ -21,7 +23,7 @@ use self::operand::{OperandRef, OperandValue}; pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { instance: Instance<'tcx>, - mir: mir::ReadOnlyBodyAndCache<'tcx, 'tcx>, + mir: &'tcx mir::Body<'tcx>, debug_context: Option>, @@ -114,7 +116,7 @@ enum LocalRef<'tcx, V> { impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> { fn new_operand>( bx: &mut Bx, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> LocalRef<'tcx, V> { if layout.is_zst() { // Zero-size temporaries aren't always initialized, which @@ -155,7 +157,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let cleanup_kinds = analyze::cleanup_kinds(&mir); // Allocate a `Block` for every basic block, except // the start block, if nothing loops back to it. - let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty(); + let reentrant_start_block = !mir.predecessors()[mir::START_BLOCK].is_empty(); let block_bxs: IndexVec = mir .basic_blocks() .indices() @@ -169,7 +171,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( .collect(); let (landing_pads, funclets) = create_funclets(&mir, &mut bx, &cleanup_kinds, &block_bxs); - let mir_body: &mir::Body<'_> = *mir; let mut fx = FunctionCx { instance, mir, @@ -190,6 +191,18 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(); + for const_ in &mir.required_consts { + if let Err(err) = fx.eval_mir_constant(const_) { + match err { + // errored or at least linted + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} + ErrorHandled::TooGeneric => { + span_bug!(const_.span, "codgen encountered polymorphic constant: {:?}", err) + } + } + } + } + let memory_locals = analyze::non_ssa_locals(&fx); // Allocate variable and temp allocas @@ -197,7 +210,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let args = arg_local_refs(&mut bx, &mut fx, &memory_locals); let mut allocate_local = |local| { - let decl = &mir_body.local_decls[local]; + let decl = &mir.local_decls[local]; let layout = bx.layout_of(fx.monomorphize(&decl.ty)); assert!(!layout.ty.has_erasable_regions()); @@ -223,7 +236,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let retptr = allocate_local(mir::RETURN_PLACE); iter::once(retptr) .chain(args.into_iter()) - .chain(mir_body.vars_and_temps_iter().map(allocate_local)) + .chain(mir.vars_and_temps_iter().map(allocate_local)) .collect() }; @@ -235,8 +248,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.br(fx.blocks[mir::START_BLOCK]); } - let rpo = traversal::reverse_postorder(&mir_body); - let mut visited = BitSet::new_empty(mir_body.basic_blocks().len()); + let rpo = traversal::reverse_postorder(&mir); + let mut visited = BitSet::new_empty(mir.basic_blocks().len()); // Codegen the body of each block using reverse postorder for (bb, _) in rpo { @@ -246,7 +259,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Remove blocks that haven't been visited, or have no // predecessors. - for bb in mir_body.basic_blocks().indices() { + for bb in mir.basic_blocks().indices() { // Unreachable block if !visited.contains(bb.index()) { debug!("codegen_mir: block {:?} was not visited", bb); diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs index 1e1fede2588df..937c7457c63bb 100644 --- a/src/librustc_codegen_ssa/mir/operand.rs +++ b/src/librustc_codegen_ssa/mir/operand.rs @@ -6,10 +6,12 @@ use crate::glue; use crate::traits::*; use crate::MemFlags; -use rustc::mir; -use rustc::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar}; -use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; -use rustc::ty::Ty; +use rustc_errors::ErrorReported; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar}; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::Ty; +use rustc_target::abi::{Abi, Align, LayoutOf, Size}; use std::fmt; @@ -43,7 +45,7 @@ pub struct OperandRef<'tcx, V> { pub val: OperandValue, // The layout of value, based on its Rust type. - pub layout: TyLayout<'tcx>, + pub layout: TyAndLayout<'tcx>, } impl fmt::Debug for OperandRef<'tcx, V> { @@ -55,7 +57,7 @@ impl fmt::Debug for OperandRef<'tcx, V> { impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { pub fn new_zst>( bx: &mut Bx, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> OperandRef<'tcx, V> { assert!(layout.is_zst()); OperandRef { @@ -78,7 +80,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let val = match val { ConstValue::Scalar(x) => { let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, + Abi::Scalar(ref x) => x, _ => bug!("from_const: invalid ByVal layout: {:#?}", layout), }; let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout)); @@ -86,12 +88,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } ConstValue::Slice { data, start, end } => { let a_scalar = match layout.abi { - layout::Abi::ScalarPair(ref a, _) => a, + Abi::ScalarPair(ref a, _) => a, _ => bug!("from_const: invalid ScalarPair layout: {:#?}", layout), }; let a = Scalar::from(Pointer::new( - bx.tcx().alloc_map.lock().create_memory_alloc(data), - Size::from_bytes(start as u64), + bx.tcx().create_memory_alloc(data), + Size::from_bytes(start), )); let a_llval = bx.scalar_to_backend( a, @@ -159,9 +161,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { pub fn from_immediate_or_packed_pair>( bx: &mut Bx, llval: V, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> Self { - let val = if let layout::Abi::ScalarPair(ref a, ref b) = layout.abi { + let val = if let Abi::ScalarPair(ref a, ref b) = layout.abi { debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", llval, layout); // Deconstruct the immediate aggregate. @@ -191,7 +193,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } // Newtype of a scalar, scalar pair or vector. - (OperandValue::Immediate(_), _) | (OperandValue::Pair(..), _) + (OperandValue::Immediate(_) | OperandValue::Pair(..), _) if field.size == self.layout.size => { assert_eq!(offset.bytes(), 0); @@ -199,7 +201,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } // Extract a scalar component from a pair. - (OperandValue::Pair(a_llval, b_llval), &layout::Abi::ScalarPair(ref a, ref b)) => { + (OperandValue::Pair(a_llval, b_llval), &Abi::ScalarPair(ref a, ref b)) => { if offset.bytes() == 0 { assert_eq!(field.size, a.value.size(bx.cx())); OperandValue::Immediate(a_llval) @@ -211,7 +213,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } // `#[repr(simd)]` types are also immediate. - (OperandValue::Immediate(llval), &layout::Abi::Vector { .. }) => { + (OperandValue::Immediate(llval), &Abi::Vector { .. }) => { OperandValue::Immediate(bx.extract_element(llval, bx.cx().const_usize(i as u64))) } @@ -305,7 +307,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { } OperandValue::Pair(a, b) => { let (a_scalar, b_scalar) = match dest.layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), + Abi::ScalarPair(ref a, ref b) => (a, b), _ => bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout), }; let b_offset = a_scalar.value.size(bx).align_to(b_scalar.value.align(bx).abi); @@ -446,8 +448,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|err| { match err { // errored or at least linted - ErrorHandled::Reported => {} - ErrorHandled::TooGeneric => bug!("codgen encountered polymorphic constant"), + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} + ErrorHandled::TooGeneric => { + bug!("codegen encountered polymorphic constant") + } } // Allow RalfJ to sleep soundly knowing that even refactorings that remove // the above error (or silence it under some conditions) will not cause UB. diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index 2eba88c6b5f31..aaba2ec1362ac 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -6,10 +6,12 @@ use crate::glue; use crate::traits::*; use crate::MemFlags; -use rustc::mir; -use rustc::mir::tcx::PlaceTy; -use rustc::ty::layout::{self, Align, HasTyCtxt, LayoutOf, TyLayout, VariantIdx}; -use rustc::ty::{self, Ty}; +use rustc_middle::mir; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::{self, Ty}; +use rustc_target::abi::{Abi, Align, DiscriminantKind, FieldsShape, Int}; +use rustc_target::abi::{LayoutOf, VariantIdx, Variants}; #[derive(Copy, Clone, Debug)] pub struct PlaceRef<'tcx, V> { @@ -20,19 +22,23 @@ pub struct PlaceRef<'tcx, V> { pub llextra: Option, /// The monomorphized type of this place, including variant information. - pub layout: TyLayout<'tcx>, + pub layout: TyAndLayout<'tcx>, /// The alignment we know for this place. pub align: Align, } impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { - pub fn new_sized(llval: V, layout: TyLayout<'tcx>) -> PlaceRef<'tcx, V> { + pub fn new_sized(llval: V, layout: TyAndLayout<'tcx>) -> PlaceRef<'tcx, V> { assert!(!layout.is_unsized()); PlaceRef { llval, llextra: None, layout, align: layout.align.abi } } - pub fn new_sized_aligned(llval: V, layout: TyLayout<'tcx>, align: Align) -> PlaceRef<'tcx, V> { + pub fn new_sized_aligned( + llval: V, + layout: TyAndLayout<'tcx>, + align: Align, + ) -> PlaceRef<'tcx, V> { assert!(!layout.is_unsized()); PlaceRef { llval, llextra: None, layout, align } } @@ -41,7 +47,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). pub fn alloca>( bx: &mut Bx, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> Self { assert!(!layout.is_unsized(), "tried to statically allocate unsized place"); let tmp = bx.alloca(bx.cx().backend_type(layout), layout.align.abi); @@ -53,7 +59,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). pub fn alloca_unsized_indirect>( bx: &mut Bx, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> Self { assert!(layout.is_unsized(), "tried to allocate indirect place for sized values"); let ptr_ty = bx.cx().tcx().mk_mut_ptr(layout.ty); @@ -62,7 +68,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } pub fn len>(&self, cx: &Cx) -> V { - if let layout::FieldPlacement::Array { count, .. } = self.layout.fields { + if let FieldsShape::Array { count, .. } = self.layout.fields { if self.layout.is_unsized() { assert_eq!(count, 0); self.llextra.unwrap() @@ -90,7 +96,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // Unions and newtypes only use an offset of 0. let llval = if offset.bytes() == 0 { self.llval - } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { + } else if let Abi::ScalarPair(ref a, ref b) = self.layout.abi { // Offsets have to match either first or second field. assert_eq!(offset, a.value.size(bx.cx()).align_to(b.value.align(bx.cx()).abi)); bx.struct_gep(self.llval, 1) @@ -194,7 +200,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { return bx.cx().const_undef(cast_to); } let (discr_scalar, discr_kind, discr_index) = match self.layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { let discr_val = self .layout .ty @@ -202,7 +208,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { .map_or(index.as_u32() as u128, |discr| discr.val); return bx.cx().const_uint_big(cast_to, discr_val); } - layout::Variants::Multiple { ref discr, ref discr_kind, discr_index, .. } => { + Variants::Multiple { ref discr, ref discr_kind, discr_index, .. } => { (discr, discr_kind, discr_index) } }; @@ -213,22 +219,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // Decode the discriminant (specifically if it's niche-encoded). match *discr_kind { - layout::DiscriminantKind::Tag => { + DiscriminantKind::Tag => { let signed = match discr_scalar.value { // We use `i1` for bytes that are always `0` or `1`, // e.g., `#[repr(i8)] enum E { A, B }`, but we can't // let LLVM interpret the `i1` as signed, because // then `i1 1` (i.e., `E::B`) is effectively `i8 -1`. - layout::Int(_, signed) => !discr_scalar.is_bool() && signed, + Int(_, signed) => !discr_scalar.is_bool() && signed, _ => false, }; bx.intcast(encoded_discr.immediate(), cast_to, signed) } - layout::DiscriminantKind::Niche { - dataful_variant, - ref niche_variants, - niche_start, - } => { + DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start } => { // Rebase from niche values to discriminants, and check // whether the result is in range for the niche variants. let niche_llty = bx.cx().immediate_backend_type(encoded_discr.layout); @@ -307,14 +309,10 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { return; } match self.layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { assert_eq!(index, variant_index); } - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, - discr_index, - .. - } => { + Variants::Multiple { discr_kind: DiscriminantKind::Tag, discr_index, .. } => { let ptr = self.project_field(bx, discr_index); let to = self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val; @@ -324,9 +322,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { ptr.align, ); } - layout::Variants::Multiple { + Variants::Multiple { discr_kind: - layout::DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, + DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, discr_index, .. } => { @@ -499,7 +497,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn monomorphized_place_ty(&self, place_ref: mir::PlaceRef<'tcx>) -> Ty<'tcx> { let tcx = self.cx.tcx(); - let place_ty = mir::Place::ty_from(place_ref.local, place_ref.projection, *self.mir, tcx); + let place_ty = mir::Place::ty_from(place_ref.local, place_ref.projection, self.mir, tcx); self.monomorphize(&place_ty.ty) } } diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 880bce7fde487..bb532abd84bde 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -7,16 +7,15 @@ use crate::common::{self, IntPredicate, RealPredicate}; use crate::traits::*; use crate::MemFlags; -use rustc::middle::lang_items::ExchangeMallocFnLangItem; -use rustc::mir; -use rustc::ty::cast::{CastTy, IntTy}; -use rustc::ty::layout::{self, HasTyCtxt, LayoutOf}; -use rustc::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_apfloat::{ieee, Float, Round, Status}; +use rustc_hir::lang_items::ExchangeMallocFnLangItem; +use rustc_middle::mir; +use rustc_middle::ty::cast::{CastTy, IntTy}; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::sym; - -use std::{i128, u128}; +use rustc_target::abi::{Abi, Int, LayoutOf, Variants}; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn codegen_rvalue( @@ -278,8 +277,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("unexpected non-pair operand"); } } - mir::CastKind::Pointer(PointerCast::MutToConstPointer) - | mir::CastKind::Pointer(PointerCast::ArrayToPointer) + mir::CastKind::Pointer( + PointerCast::MutToConstPointer | PointerCast::ArrayToPointer, + ) | mir::CastKind::Misc => { assert!(bx.cx().is_backend_immediate(cast)); let ll_t_out = bx.cx().immediate_backend_type(cast); @@ -292,7 +292,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast"); let ll_t_in = bx.cx().immediate_backend_type(operand.layout); match operand.layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { if let Some(discr) = operand.layout.ty.discriminant_for_variant(bx.tcx(), index) { @@ -311,13 +311,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ); } } - layout::Variants::Multiple { .. } => {} + Variants::Multiple { .. } => {} } let llval = operand.immediate(); let mut signed = false; - if let layout::Abi::Scalar(ref scalar) = operand.layout.abi { - if let layout::Int(_, s) = scalar.value { + if let Abi::Scalar(ref scalar) = operand.layout.abi { + if let Int(_, s) = scalar.value { // We use `i1` for bytes that are always `0` or `1`, // e.g., `#[repr(i8)] enum E { A, B }`, but we can't // let LLVM interpret the `i1` as signed, because @@ -359,10 +359,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.uitofp(llval, ll_t_out) } } - (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FnPtr, CastTy::Ptr(_)) => { + (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => { bx.pointercast(llval, ll_t_out) } - (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { + (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { bx.ptrtoint(llval, ll_t_out) } (CastTy::Int(_), CastTy::Ptr(_)) => { @@ -383,7 +383,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (bx, OperandRef { val, layout: cast }) } - mir::Rvalue::Ref(_, bk, ref place) => { + mir::Rvalue::Ref(_, bk, place) => { let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { tcx.mk_ref( tcx.lifetimes.re_erased, @@ -393,14 +393,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_place_to_pointer(bx, place, mk_ref) } - mir::Rvalue::AddressOf(mutability, ref place) => { + mir::Rvalue::AddressOf(mutability, place) => { let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { tcx.mk_ptr(ty::TypeAndMut { ty, mutbl: mutability }) }; self.codegen_place_to_pointer(bx, place, mk_ptr) } - mir::Rvalue::Len(ref place) => { + mir::Rvalue::Len(place) => { let size = self.evaluate_array_len(&mut bx, place); let operand = OperandRef { val: OperandValue::Immediate(size), @@ -473,7 +473,7 @@ 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 = rvalue.ty(self.mir, bx.tcx()); let discr = self .codegen_place(&mut bx, place.as_ref()) .codegen_get_discr(&mut bx, discr_ty); @@ -529,7 +529,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { // According to `rvalue_creates_operand`, only ZST // aggregate rvalues are allowed to be operands. - let ty = rvalue.ty(*self.mir, self.cx.tcx()); + let ty = rvalue.ty(self.mir, self.cx.tcx()); let operand = OperandRef::new_zst(&mut bx, self.cx.layout_of(self.monomorphize(&ty))); (bx, operand) @@ -537,7 +537,7 @@ 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 { + 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() { @@ -557,7 +557,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn codegen_place_to_pointer( &mut self, mut bx: Bx, - place: &mir::Place<'tcx>, + place: mir::Place<'tcx>, mk_ptr_ty: impl FnOnce(TyCtxt<'tcx>, Ty<'tcx>) -> Ty<'tcx>, ) -> (Bx, OperandRef<'tcx, Bx::Value>) { let cg_place = self.codegen_place(&mut bx, place.as_ref()); @@ -749,7 +749,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { - let ty = rvalue.ty(*self.mir, self.cx.tcx()); + let ty = rvalue.ty(self.mir, self.cx.tcx()); let ty = self.monomorphize(&ty); self.cx.spanned_layout_of(ty, span).is_zst() } @@ -768,7 +768,7 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> Bx::Value { let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - if !bx.cx().sess().opts.debugging_opts.saturating_float_casts { + if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { return fptosui_result; } diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/src/librustc_codegen_ssa/mir/statement.rs index e68b41ad18879..ddd7447406c48 100644 --- a/src/librustc_codegen_ssa/mir/statement.rs +++ b/src/librustc_codegen_ssa/mir/statement.rs @@ -1,5 +1,5 @@ -use rustc::mir; use rustc_errors::struct_span_err; +use rustc_middle::mir; use super::FunctionCx; use super::LocalRef; @@ -66,7 +66,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } bx } - mir::StatementKind::InlineAsm(ref asm) => { + mir::StatementKind::LlvmInlineAsm(ref asm) => { let outputs = asm .outputs .iter() @@ -93,7 +93,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ); if input_vals.len() == asm.inputs.len() { - let res = bx.codegen_inline_asm( + let res = bx.codegen_llvm_inline_asm( &asm.asm, outputs, input_vals, diff --git a/src/librustc_codegen_ssa/mono_item.rs b/src/librustc_codegen_ssa/mono_item.rs index ae211cad62de4..5994ef2be5467 100644 --- a/src/librustc_codegen_ssa/mono_item.rs +++ b/src/librustc_codegen_ssa/mono_item.rs @@ -1,10 +1,10 @@ use crate::base; use crate::traits::*; -use rustc::mir::mono::{Linkage, Visibility}; -use rustc::ty::layout::HasTyCtxt; use rustc_hir as hir; +use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::ty::layout::HasTyCtxt; -use rustc::mir::mono::MonoItem; +use rustc_middle::mir::mono::MonoItem; pub trait MonoItemExt<'a, 'tcx> { fn define>(&self, cx: &'a Bx::CodegenCx); diff --git a/src/librustc_codegen_ssa/traits/abi.rs b/src/librustc_codegen_ssa/traits/abi.rs index c564552fcb438..dd8495850bd54 100644 --- a/src/librustc_codegen_ssa/traits/abi.rs +++ b/src/librustc_codegen_ssa/traits/abi.rs @@ -1,5 +1,5 @@ use super::BackendTypes; -use rustc::ty::Ty; +use rustc_middle::ty::Ty; use rustc_target::abi::call::FnAbi; pub trait AbiBuilderMethods<'tcx>: BackendTypes { diff --git a/src/librustc_codegen_ssa/traits/asm.rs b/src/librustc_codegen_ssa/traits/asm.rs index d31b063232c85..0abfdfde7801b 100644 --- a/src/librustc_codegen_ssa/traits/asm.rs +++ b/src/librustc_codegen_ssa/traits/asm.rs @@ -1,17 +1,59 @@ use super::BackendTypes; +use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; -use rustc_hir::{GlobalAsm, InlineAsmInner}; +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_hir::def_id::DefId; +use rustc_hir::{GlobalAsm, LlvmInlineAsmInner}; +use rustc_middle::ty::Instance; use rustc_span::Span; +use rustc_target::asm::InlineAsmRegOrRegClass; + +#[derive(Debug)] +pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { + In { + reg: InlineAsmRegOrRegClass, + value: OperandRef<'tcx, B::Value>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + place: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_value: OperandRef<'tcx, B::Value>, + out_place: Option>, + }, + Const { + string: String, + }, + SymFn { + instance: Instance<'tcx>, + }, + SymStatic { + def_id: DefId, + }, +} pub trait AsmBuilderMethods<'tcx>: BackendTypes { /// Take an inline assembly expression and splat it out via LLVM - fn codegen_inline_asm( + fn codegen_llvm_inline_asm( &mut self, - ia: &InlineAsmInner, + ia: &LlvmInlineAsmInner, outputs: Vec>, inputs: Vec, span: Span, ) -> bool; + + /// Take an inline assembly expression and splat it out via LLVM + fn codegen_inline_asm( + &mut self, + template: &[InlineAsmTemplatePiece], + operands: &[InlineAsmOperandRef<'tcx, Self>], + options: InlineAsmOptions, + span: Span, + ); } pub trait AsmMethods { diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs index 4e861f45ff7a5..6cbb47efa99f2 100644 --- a/src/librustc_codegen_ssa/traits/backend.rs +++ b/src/librustc_codegen_ssa/traits/backend.rs @@ -2,18 +2,19 @@ use super::write::WriteBackendMethods; use super::CodegenObject; use crate::ModuleCodegen; -use rustc::dep_graph::DepGraph; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; -use rustc::ty::layout::{HasTyCtxt, LayoutOf, TyLayout}; -use rustc::ty::query::Providers; -use rustc::ty::{Ty, TyCtxt}; -use rustc::util::common::ErrorReported; use rustc_ast::expand::allocator::AllocatorKind; +use rustc_errors::ErrorReported; +use rustc_middle::dep_graph::DepGraph; +use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_session::{ config::{self, OutputFilenames, PrintRequest}, Session, }; use rustc_span::symbol::Symbol; +use rustc_target::abi::LayoutOf; pub use rustc_data_structures::sync::MetadataRef; @@ -35,12 +36,12 @@ pub trait BackendTypes { } pub trait Backend<'tcx>: - Sized + BackendTypes + HasTyCtxt<'tcx> + LayoutOf, TyLayout = TyLayout<'tcx>> + Sized + BackendTypes + HasTyCtxt<'tcx> + LayoutOf, TyAndLayout = TyAndLayout<'tcx>> { } impl<'tcx, T> Backend<'tcx> for T where - Self: BackendTypes + HasTyCtxt<'tcx> + LayoutOf, TyLayout = TyLayout<'tcx>> + Self: BackendTypes + HasTyCtxt<'tcx> + LayoutOf, TyAndLayout = TyAndLayout<'tcx>> { } @@ -109,14 +110,10 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se tcx: TyCtxt<'_>, cgu_name: Symbol, ) -> (ModuleCodegen, u64); - // If find_features is true this won't access `sess.crate_types` by assuming - // that `is_pie_binary` is false. When we discover LLVM target features - // `sess.crate_types` is uninitialized so we cannot access it. fn target_machine_factory( &self, sess: &Session, opt_level: config::OptLevel, - find_features: bool, ) -> Arc Result + Send + Sync>; fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str; } diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index b3c830eef8698..caba7ebef593b 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -12,8 +12,9 @@ use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use crate::MemFlags; -use rustc::ty::layout::{Align, HasParamEnv, Size}; -use rustc::ty::Ty; +use rustc_middle::ty::layout::HasParamEnv; +use rustc_middle::ty::Ty; +use rustc_target::abi::{Align, Size}; use rustc_target::spec::HasTargetSpec; use std::iter::TrustedLen; diff --git a/src/librustc_codegen_ssa/traits/consts.rs b/src/librustc_codegen_ssa/traits/consts.rs index 608fa5ef71e03..6b58dea794bcb 100644 --- a/src/librustc_codegen_ssa/traits/consts.rs +++ b/src/librustc_codegen_ssa/traits/consts.rs @@ -1,9 +1,9 @@ use super::BackendTypes; use crate::mir::place::PlaceRef; -use rustc::mir::interpret::Allocation; -use rustc::mir::interpret::Scalar; -use rustc::ty::layout; +use rustc_middle::mir::interpret::{Allocation, Scalar}; +use rustc_middle::ty::layout::TyAndLayout; use rustc_span::Symbol; +use rustc_target::abi::{self, Size}; pub trait ConstMethods<'tcx>: BackendTypes { // Constant constructors @@ -26,17 +26,12 @@ pub trait ConstMethods<'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 scalar_to_backend( - &self, - cv: Scalar, - layout: &layout::Scalar, - llty: Self::Type, - ) -> Self::Value; + fn scalar_to_backend(&self, cv: Scalar, layout: &abi::Scalar, llty: Self::Type) -> Self::Value; fn from_const_alloc( &self, - layout: layout::TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, alloc: &Allocation, - offset: layout::Size, + offset: Size, ) -> PlaceRef<'tcx, Self::Value>; fn const_ptrcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value; diff --git a/src/librustc_codegen_ssa/traits/debuginfo.rs b/src/librustc_codegen_ssa/traits/debuginfo.rs index 14c5a1b8ee9b3..1ee0f489ffc10 100644 --- a/src/librustc_codegen_ssa/traits/debuginfo.rs +++ b/src/librustc_codegen_ssa/traits/debuginfo.rs @@ -1,12 +1,11 @@ use super::BackendTypes; use crate::mir::debuginfo::{FunctionDebugContext, VariableKind}; -use rustc::mir; -use rustc::ty::layout::Size; -use rustc::ty::{Instance, Ty}; -use rustc_ast::ast::Name; use rustc_hir::def_id::CrateNum; -use rustc_span::{SourceFile, Span}; +use rustc_middle::mir; +use rustc_middle::ty::{Instance, Ty}; +use rustc_span::{SourceFile, Span, Symbol}; use rustc_target::abi::call::FnAbi; +use rustc_target::abi::Size; pub trait DebugInfoMethods<'tcx>: BackendTypes { fn create_vtable_metadata(&self, ty: Ty<'tcx>, vtable: Self::Value); @@ -36,7 +35,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes { fn create_dbg_var( &self, dbg_context: &FunctionDebugContext, - variable_name: Name, + variable_name: Symbol, variable_type: Ty<'tcx>, scope_metadata: Self::DIScope, variable_kind: VariableKind, diff --git a/src/librustc_codegen_ssa/traits/declare.rs b/src/librustc_codegen_ssa/traits/declare.rs index c2ab5f50c6f7e..690aacd20566b 100644 --- a/src/librustc_codegen_ssa/traits/declare.rs +++ b/src/librustc_codegen_ssa/traits/declare.rs @@ -1,7 +1,7 @@ use super::BackendTypes; -use rustc::mir::mono::{Linkage, Visibility}; -use rustc::ty::{Instance, Ty}; use rustc_hir::def_id::DefId; +use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::ty::{Instance, Ty}; use rustc_target::abi::call::FnAbi; pub trait DeclareMethods<'tcx>: BackendTypes { @@ -31,7 +31,7 @@ pub trait DeclareMethods<'tcx>: BackendTypes { /// Use this function when you intend to define a global. This function will /// return `None` if the name already has a definition associated with it. In that /// case an error should be reported to the user, because it usually happens due - /// to user’s fault (e.g., misuse of #[no_mangle] or #[export_name] attributes). + /// to user’s fault (e.g., misuse of `#[no_mangle]` or `#[export_name]` attributes). fn define_global(&self, name: &str, ty: Self::Type) -> Option; /// Declare a private global diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs index 482a76932ae42..9d48e233de655 100644 --- a/src/librustc_codegen_ssa/traits/intrinsic.rs +++ b/src/librustc_codegen_ssa/traits/intrinsic.rs @@ -1,6 +1,6 @@ use super::BackendTypes; use crate::mir::operand::OperandRef; -use rustc::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_target::abi::call::FnAbi; diff --git a/src/librustc_codegen_ssa/traits/misc.rs b/src/librustc_codegen_ssa/traits/misc.rs index 6863b474cb6de..fc57a9a80b261 100644 --- a/src/librustc_codegen_ssa/traits/misc.rs +++ b/src/librustc_codegen_ssa/traits/misc.rs @@ -1,10 +1,9 @@ use super::BackendTypes; -use rustc::mir::mono::CodegenUnit; -use rustc::ty::{self, Instance, Ty}; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::mono::CodegenUnit; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::Session; use std::cell::RefCell; -use std::sync::Arc; pub trait MiscMethods<'tcx>: BackendTypes { fn vtables( @@ -15,7 +14,7 @@ pub trait MiscMethods<'tcx>: BackendTypes { fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value; fn eh_personality(&self) -> Self::Value; fn sess(&self) -> &Session; - fn codegen_unit(&self) -> &Arc>; + fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx>; fn used_statics(&self) -> &RefCell>; fn set_frame_pointer_elimination(&self, llfn: Self::Function); fn apply_target_cpu_attr(&self, llfn: Self::Function); diff --git a/src/librustc_codegen_ssa/traits/mod.rs b/src/librustc_codegen_ssa/traits/mod.rs index 1bc9f297ea1b1..6b782731d535c 100644 --- a/src/librustc_codegen_ssa/traits/mod.rs +++ b/src/librustc_codegen_ssa/traits/mod.rs @@ -28,7 +28,7 @@ mod type_; mod write; pub use self::abi::AbiBuilderMethods; -pub use self::asm::{AsmBuilderMethods, AsmMethods}; +pub use self::asm::{AsmBuilderMethods, AsmMethods, InlineAsmOperandRef}; pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; @@ -42,7 +42,7 @@ pub use self::type_::{ }; pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; -use rustc::ty::layout::{HasParamEnv, HasTyCtxt}; +use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt}; use rustc_target::spec::HasTargetSpec; use std::fmt; diff --git a/src/librustc_codegen_ssa/traits/statics.rs b/src/librustc_codegen_ssa/traits/statics.rs index 40c9dde98c628..a6462b358347b 100644 --- a/src/librustc_codegen_ssa/traits/statics.rs +++ b/src/librustc_codegen_ssa/traits/statics.rs @@ -1,6 +1,6 @@ use super::BackendTypes; -use rustc::ty::layout::Align; use rustc_hir::def_id::DefId; +use rustc_target::abi::Align; pub trait StaticMethods: BackendTypes { fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; diff --git a/src/librustc_codegen_ssa/traits/type_.rs b/src/librustc_codegen_ssa/traits/type_.rs index 0d644e6dd30b3..703479b74bef8 100644 --- a/src/librustc_codegen_ssa/traits/type_.rs +++ b/src/librustc_codegen_ssa/traits/type_.rs @@ -3,10 +3,11 @@ use super::Backend; use super::HasCodegen; use crate::common::TypeKind; use crate::mir::place::PlaceRef; -use rustc::ty::layout::{self, TyLayout}; -use rustc::ty::{self, Ty}; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{self, Ty}; use rustc_span::DUMMY_SP; use rustc_target::abi::call::{ArgAbi, CastTarget, FnAbi, Reg}; +use rustc_target::abi::Integer; // This depends on `Backend` and not `BackendTypes`, because consumers will probably want to use // `LayoutOf` or `HasTyCtxt`. This way, they don't have to add a constraint on it themselves. @@ -53,8 +54,8 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { } } - fn type_from_integer(&self, i: layout::Integer) -> Self::Type { - use rustc::ty::layout::Integer::*; + fn type_from_integer(&self, i: Integer) -> Self::Type { + use Integer::*; match i { I8 => self.type_i8(), I16 => self.type_i16(), @@ -94,17 +95,17 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { impl DerivedTypeMethods<'tcx> for T where Self: BaseTypeMethods<'tcx> + MiscMethods<'tcx> {} pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { - fn backend_type(&self, layout: TyLayout<'tcx>) -> Self::Type; + fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type; fn cast_backend_type(&self, ty: &CastTarget) -> Self::Type; fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type; fn reg_backend_type(&self, ty: &Reg) -> Self::Type; - fn immediate_backend_type(&self, layout: TyLayout<'tcx>) -> Self::Type; - fn is_backend_immediate(&self, layout: TyLayout<'tcx>) -> bool; - fn is_backend_scalar_pair(&self, layout: TyLayout<'tcx>) -> bool; - fn backend_field_index(&self, layout: TyLayout<'tcx>, index: usize) -> u64; + fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type; + fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool; + fn is_backend_scalar_pair(&self, layout: TyAndLayout<'tcx>) -> bool; + fn backend_field_index(&self, layout: TyAndLayout<'tcx>, index: usize) -> u64; fn scalar_pair_element_backend_type( &self, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, index: usize, immediate: bool, ) -> Self::Type; diff --git a/src/librustc_codegen_ssa/traits/write.rs b/src/librustc_codegen_ssa/traits/write.rs index 382dc14e789ce..27d52e9b9c53e 100644 --- a/src/librustc_codegen_ssa/traits/write.rs +++ b/src/librustc_codegen_ssa/traits/write.rs @@ -2,8 +2,8 @@ use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; use crate::{CompiledModule, ModuleCodegen}; -use rustc::dep_graph::WorkProduct; use rustc_errors::{FatalError, Handler}; +use rustc_middle::dep_graph::WorkProduct; pub trait WriteBackendMethods: 'static + Sized + Clone { type Module: Send + Sync; diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index fb4f818c4b249..81ad032967bfd 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" doctest = false [dependencies] -ena = "0.13.1" +ena = "0.14" indexmap = "1" log = "0.4" jobserver_crate = { version = "0.1.13", package = "jobserver" } @@ -18,7 +18,7 @@ lazy_static = "1" rustc_serialize = { path = "../libserialize", package = "serialize" } graphviz = { path = "../libgraphviz" } cfg-if = "0.1.2" -crossbeam-utils = { version = "0.6.5", features = ["nightly"] } +crossbeam-utils = { version = "0.7", features = ["nightly"] } stable_deref_trait = "1.0.0" rayon = { version = "0.3.0", package = "rustc-rayon" } rayon-core = { version = "0.3.0", package = "rustc-rayon-core" } @@ -27,9 +27,11 @@ smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_index = { path = "../librustc_index", package = "rustc_index" } bitflags = "1.2.1" measureme = "0.7.1" +libc = "0.2" +stacker = "0.1.9" [dependencies.parking_lot] -version = "0.9" +version = "0.10" features = ["nightly"] [target.'cfg(windows)'.dependencies] diff --git a/src/librustc_data_structures/box_region.rs b/src/librustc_data_structures/box_region.rs index edeb4f83c7d7e..eb6f4e8213ec7 100644 --- a/src/librustc_data_structures/box_region.rs +++ b/src/librustc_data_structures/box_region.rs @@ -1,4 +1,15 @@ -use std::cell::Cell; +//! This module provides a way to deal with self-referential data. +//! +//! The main idea is to allocate such data in a generator frame and then +//! give access to it by executing user-provided closures inside that generator. +//! The module provides a safe abstraction for the latter task. +//! +//! The interface consists of two exported macros meant to be used together: +//! * `declare_box_region_type` wraps a generator inside a struct with `access` +//! method which accepts closures. +//! * `box_region_allow_access` is a helper which should be called inside +//! a generator to actually execute those closures. + use std::marker::PhantomData; use std::ops::{Generator, GeneratorState}; use std::pin::Pin; @@ -14,24 +25,23 @@ impl AccessAction { #[derive(Copy, Clone)] pub enum Action { + Initial, Access(AccessAction), Complete, } -thread_local!(pub static BOX_REGION_ARG: Cell = Cell::new(Action::Complete)); - pub struct PinnedGenerator { - generator: Pin, Return = R>>>, + generator: Pin, Return = R>>>, } impl PinnedGenerator { - pub fn new, Return = R> + 'static>( + pub fn new, Return = R> + 'static>( generator: T, ) -> (I, Self) { let mut result = PinnedGenerator { generator: Box::pin(generator) }; // Run it to the first yield to set it up - let init = match Pin::new(&mut result.generator).resume(()) { + let init = match Pin::new(&mut result.generator).resume(Action::Initial) { GeneratorState::Yielded(YieldType::Initial(y)) => y, _ => panic!(), }; @@ -40,21 +50,17 @@ impl PinnedGenerator { } pub unsafe fn access(&mut self, closure: *mut dyn FnMut()) { - BOX_REGION_ARG.with(|i| { - i.set(Action::Access(AccessAction(closure))); - }); - - // Call the generator, which in turn will call the closure in BOX_REGION_ARG - if let GeneratorState::Complete(_) = Pin::new(&mut self.generator).resume(()) { + // Call the generator, which in turn will call the closure + if let GeneratorState::Complete(_) = + Pin::new(&mut self.generator).resume(Action::Access(AccessAction(closure))) + { panic!() } } pub fn complete(&mut self) -> R { // Tell the generator we want it to complete, consuming it and yielding a result - BOX_REGION_ARG.with(|i| i.set(Action::Complete)); - - let result = Pin::new(&mut self.generator).resume(()); + let result = Pin::new(&mut self.generator).resume(Action::Complete); if let GeneratorState::Complete(r) = result { r } else { panic!() } } } @@ -89,7 +95,7 @@ macro_rules! declare_box_region_type { >); impl $name { - fn new + 'static>( + fn new + 'static>( generator: T ) -> ($reti, Self) { let (initial, pinned) = $crate::box_region::PinnedGenerator::new(generator); @@ -98,7 +104,7 @@ macro_rules! declare_box_region_type { $v fn access FnOnce($($args,)*) -> R, R>(&mut self, f: F) -> R { // Turn the FnOnce closure into *mut dyn FnMut() - // so we can pass it in to the generator using the BOX_REGION_ARG thread local + // so we can pass it in to the generator let mut r = None; let mut f = Some(f); let mut_f: &mut dyn for<$($lifetimes)*> FnMut(($($args,)*)) = @@ -140,9 +146,9 @@ macro_rules! declare_box_region_type { #[macro_export] #[allow_internal_unstable(fn_traits)] macro_rules! box_region_allow_access { - (for($($lifetimes:tt)*), ($($args:ty),*), ($($exprs:expr),*) ) => { + (for($($lifetimes:tt)*), ($($args:ty),*), ($($exprs:expr),*), $action:ident) => { loop { - match $crate::box_region::BOX_REGION_ARG.with(|i| i.get()) { + match $action { $crate::box_region::Action::Access(accessor) => { let accessor: &mut dyn for<$($lifetimes)*> FnMut($($args),*) = unsafe { ::std::mem::transmute(accessor.get()) @@ -152,10 +158,11 @@ macro_rules! box_region_allow_access { let marker = $crate::box_region::Marker::< for<$($lifetimes)*> fn(($($args,)*)) >::new(); - yield $crate::box_region::YieldType::Accessor(marker) + $action = yield $crate::box_region::YieldType::Accessor(marker); }; } $crate::box_region::Action::Complete => break, + $crate::box_region::Action::Initial => panic!("unexpected box_region action: Initial"), } } } diff --git a/src/librustc_data_structures/graph/implementation/mod.rs b/src/librustc_data_structures/graph/implementation/mod.rs index f705c2f0b75f3..1aa7ac024d94e 100644 --- a/src/librustc_data_structures/graph/implementation/mod.rs +++ b/src/librustc_data_structures/graph/implementation/mod.rs @@ -23,7 +23,6 @@ use crate::snapshot_vec::{SnapshotVec, SnapshotVecDelegate}; use rustc_index::bit_set::BitSet; use std::fmt::Debug; -use std::usize; #[cfg(test)] mod tests; diff --git a/src/librustc_data_structures/graph/iterate/mod.rs b/src/librustc_data_structures/graph/iterate/mod.rs index d9d4c7e321fb5..64ff6130ddffb 100644 --- a/src/librustc_data_structures/graph/iterate/mod.rs +++ b/src/librustc_data_structures/graph/iterate/mod.rs @@ -209,7 +209,9 @@ where // schedule its successors for examination. self.stack.push(Event { node, becomes: Settled }); for succ in self.graph.successors(node) { - self.stack.push(Event { node: succ, becomes: Visited }); + if !visitor.ignore_edge(node, succ) { + self.stack.push(Event { node: succ, becomes: Visited }); + } } } } @@ -255,16 +257,21 @@ where /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms fn node_examined( &mut self, - _target: G::Node, + _node: G::Node, _prior_status: Option, ) -> ControlFlow { ControlFlow::Continue } /// Called after all nodes reachable from this one have been examined. - fn node_settled(&mut self, _target: G::Node) -> ControlFlow { + fn node_settled(&mut self, _node: G::Node) -> ControlFlow { ControlFlow::Continue } + + /// Behave as if no edges exist from `source` to `target`. + fn ignore_edge(&mut self, _source: G::Node, _target: G::Node) -> bool { + false + } } /// This `TriColorVisitor` looks for back edges in a graph, which indicate that a cycle exists. diff --git a/src/librustc_data_structures/graph/scc/mod.rs b/src/librustc_data_structures/graph/scc/mod.rs index 7ecf3e3cb8d5d..57eaf56f268f8 100644 --- a/src/librustc_data_structures/graph/scc/mod.rs +++ b/src/librustc_data_structures/graph/scc/mod.rs @@ -47,6 +47,11 @@ impl Sccs { } /// Returns an iterator over the SCCs in the graph. + /// + /// The SCCs will be iterated in **dependency order** (or **post order**), + /// meaning that if `S1 -> S2`, we will visit `S2` first and `S1` after. + /// This is convenient when the edges represent dependencies: when you visit + /// `S1`, the value for `S2` will already have been computed. pub fn all_sccs(&self) -> impl Iterator { (0..self.scc_data.len()).map(S::new) } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index f9f8ff5303e10..7ee60176dbead 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -12,7 +12,7 @@ #![feature(generators)] #![feature(generator_trait)] #![feature(fn_traits)] -#![feature(specialization)] +#![feature(min_specialization)] #![feature(optin_builtin_traits)] #![feature(nll)] #![feature(allow_internal_unstable)] @@ -26,13 +26,9 @@ #[macro_use] extern crate log; -#[cfg(unix)] -extern crate libc; #[macro_use] extern crate cfg_if; -pub use rustc_serialize::hex::ToHex; - #[inline(never)] #[cold] pub fn cold_path R, R>(f: F) -> R { @@ -69,6 +65,7 @@ pub mod fx; pub mod graph; pub mod jobserver; pub mod macros; +pub mod map_in_place; pub mod obligation_forest; pub mod owning_ref; pub mod ptr_key; @@ -83,10 +80,12 @@ pub mod stable_set; #[macro_use] pub mod stable_hasher; pub mod sharded; +pub mod stack; pub mod sync; pub mod thin_vec; pub mod tiny_list; pub mod transitive_relation; +pub use ena::undo_log; pub use ena::unify; mod atomic_ref; pub mod fingerprint; @@ -114,6 +113,6 @@ impl Drop for OnDrop { } } -// See comments in src/librustc/lib.rs +// See comments in src/librustc_middle/lib.rs #[doc(hidden)] pub fn __noop_fix_for_27438() {} diff --git a/src/librustc_ast/util/map_in_place.rs b/src/librustc_data_structures/map_in_place.rs similarity index 98% rename from src/librustc_ast/util/map_in_place.rs rename to src/librustc_data_structures/map_in_place.rs index a237a6e6162c0..5dd9fc6e8bc08 100644 --- a/src/librustc_ast/util/map_in_place.rs +++ b/src/librustc_data_structures/map_in_place.rs @@ -1,5 +1,3 @@ -// FIXME(Centril): Move to rustc_data_structures. - use smallvec::{Array, SmallVec}; use std::ptr; diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index 6711a49b2b7c1..197169b7036e0 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -173,7 +173,7 @@ struct Node { /// must all be in a non-pending state. dependents: Vec, - /// If true, dependents[0] points to a "parent" node, which requires + /// If true, `dependents[0]` points to a "parent" node, which requires /// special treatment upon error but is otherwise treated the same. /// (It would be more idiomatic to store the parent node in a separate /// `Option` field, but that slows down the common case of diff --git a/src/librustc_data_structures/profiling.rs b/src/librustc_data_structures/profiling.rs index a7cdc48d60342..07d16c6483ec7 100644 --- a/src/librustc_data_structures/profiling.rs +++ b/src/librustc_data_structures/profiling.rs @@ -93,17 +93,21 @@ use std::path::Path; use std::process; use std::sync::Arc; use std::time::{Duration, Instant}; -use std::u32; use measureme::{EventId, EventIdBuilder, SerializableString, StringId}; use parking_lot::RwLock; -/// MmapSerializatioSink is faster on macOS and Linux -/// but FileSerializationSink is faster on Windows -#[cfg(not(windows))] -type SerializationSink = measureme::MmapSerializationSink; -#[cfg(windows)] -type SerializationSink = measureme::FileSerializationSink; +cfg_if! { + if #[cfg(any(windows, target_os = "wasi"))] { + /// FileSerializationSink is faster on Windows + type SerializationSink = measureme::FileSerializationSink; + } else if #[cfg(target_arch = "wasm32")] { + type SerializationSink = measureme::ByteVecSink; + } else { + /// MmapSerializatioSink is faster on macOS and Linux + type SerializationSink = measureme::MmapSerializationSink; + } +} type Profiler = measureme::Profiler; @@ -603,31 +607,37 @@ pub fn duration_to_secs_str(dur: std::time::Duration) -> String { } // Memory reporting -#[cfg(unix)] -fn get_resident() -> Option { - let field = 1; - let contents = fs::read("/proc/self/statm").ok()?; - let contents = String::from_utf8(contents).ok()?; - let s = contents.split_whitespace().nth(field)?; - let npages = s.parse::().ok()?; - Some(npages * 4096) -} - -#[cfg(windows)] -fn get_resident() -> Option { - use std::mem::{self, MaybeUninit}; - use winapi::shared::minwindef::DWORD; - use winapi::um::processthreadsapi::GetCurrentProcess; - use winapi::um::psapi::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}; - - let mut pmc = MaybeUninit::::uninit(); - match unsafe { - GetProcessMemoryInfo(GetCurrentProcess(), pmc.as_mut_ptr(), mem::size_of_val(&pmc) as DWORD) - } { - 0 => None, - _ => { - let pmc = unsafe { pmc.assume_init() }; - Some(pmc.WorkingSetSize as usize) +cfg_if! { + if #[cfg(windows)] { + fn get_resident() -> Option { + use std::mem::{self, MaybeUninit}; + use winapi::shared::minwindef::DWORD; + use winapi::um::processthreadsapi::GetCurrentProcess; + use winapi::um::psapi::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}; + + let mut pmc = MaybeUninit::::uninit(); + match unsafe { + GetProcessMemoryInfo(GetCurrentProcess(), pmc.as_mut_ptr(), mem::size_of_val(&pmc) as DWORD) + } { + 0 => None, + _ => { + let pmc = unsafe { pmc.assume_init() }; + Some(pmc.WorkingSetSize as usize) + } + } + } + } else if #[cfg(unix)] { + fn get_resident() -> Option { + let field = 1; + let contents = fs::read("/proc/self/statm").ok()?; + let contents = String::from_utf8(contents).ok()?; + let s = contents.split_whitespace().nth(field)?; + let npages = s.parse::().ok()?; + Some(npages * 4096) + } + } else { + fn get_resident() -> Option { + None } } } diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index b71163a8f9433..52865f55f786b 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -1,77 +1,75 @@ use crate::fx::FxHashMap; +use crate::undo_log::{Rollback, Snapshots, UndoLogs, VecLog}; +use std::borrow::{Borrow, BorrowMut}; use std::hash::Hash; -use std::mem; +use std::marker::PhantomData; use std::ops; +pub use crate::undo_log::Snapshot; + #[cfg(test)] mod tests; -pub struct SnapshotMap -where - K: Clone + Eq, -{ - map: FxHashMap, - undo_log: Vec>, - num_open_snapshots: usize, +pub type SnapshotMapStorage = SnapshotMap, ()>; +pub type SnapshotMapRef<'a, K, V, L> = SnapshotMap, &'a mut L>; + +pub struct SnapshotMap, L = VecLog>> { + map: M, + undo_log: L, + _marker: PhantomData<(K, V)>, } // HACK(eddyb) manual impl avoids `Default` bounds on `K` and `V`. -impl Default for SnapshotMap +impl Default for SnapshotMap where - K: Hash + Clone + Eq, + M: Default, + L: Default, { fn default() -> Self { - SnapshotMap { map: Default::default(), undo_log: Default::default(), num_open_snapshots: 0 } + SnapshotMap { map: Default::default(), undo_log: Default::default(), _marker: PhantomData } } } -pub struct Snapshot { - len: usize, -} - -enum UndoLog { +pub enum UndoLog { Inserted(K), Overwrite(K, V), Purged, } -impl SnapshotMap +impl SnapshotMap { + pub fn with_log(&mut self, undo_log: L2) -> SnapshotMap { + SnapshotMap { map: &mut self.map, undo_log, _marker: PhantomData } + } +} + +impl SnapshotMap where K: Hash + Clone + Eq, + M: BorrowMut> + Borrow>, + L: UndoLogs>, { pub fn clear(&mut self) { - self.map.clear(); + self.map.borrow_mut().clear(); self.undo_log.clear(); - self.num_open_snapshots = 0; - } - - fn in_snapshot(&self) -> bool { - self.num_open_snapshots > 0 } pub fn insert(&mut self, key: K, value: V) -> bool { - match self.map.insert(key.clone(), value) { + match self.map.borrow_mut().insert(key.clone(), value) { None => { - if self.in_snapshot() { - self.undo_log.push(UndoLog::Inserted(key)); - } + self.undo_log.push(UndoLog::Inserted(key)); true } Some(old_value) => { - if self.in_snapshot() { - self.undo_log.push(UndoLog::Overwrite(key, old_value)); - } + self.undo_log.push(UndoLog::Overwrite(key, old_value)); false } } } pub fn remove(&mut self, key: K) -> bool { - match self.map.remove(&key) { + match self.map.borrow_mut().remove(&key) { Some(old_value) => { - if self.in_snapshot() { - self.undo_log.push(UndoLog::Overwrite(key, old_value)); - } + self.undo_log.push(UndoLog::Overwrite(key, old_value)); true } None => false, @@ -79,83 +77,64 @@ where } pub fn get(&self, key: &K) -> Option<&V> { - self.map.get(key) + self.map.borrow().get(key) } +} +impl SnapshotMap +where + K: Hash + Clone + Eq, +{ pub fn snapshot(&mut self) -> Snapshot { - let len = self.undo_log.len(); - self.num_open_snapshots += 1; - Snapshot { len } - } - - fn assert_open_snapshot(&self, snapshot: &Snapshot) { - assert!(self.undo_log.len() >= snapshot.len); - assert!(self.num_open_snapshots > 0); + self.undo_log.start_snapshot() } pub fn commit(&mut self, snapshot: Snapshot) { - self.assert_open_snapshot(&snapshot); - if self.num_open_snapshots == 1 { - // The root snapshot. It's safe to clear the undo log because - // there's no snapshot further out that we might need to roll back - // to. - assert!(snapshot.len == 0); - self.undo_log.clear(); - } - - self.num_open_snapshots -= 1; + self.undo_log.commit(snapshot) } - pub fn partial_rollback(&mut self, snapshot: &Snapshot, should_revert_key: &F) - where - F: Fn(&K) -> bool, - { - self.assert_open_snapshot(snapshot); - for i in (snapshot.len..self.undo_log.len()).rev() { - let reverse = match self.undo_log[i] { - UndoLog::Purged => false, - UndoLog::Inserted(ref k) => should_revert_key(k), - UndoLog::Overwrite(ref k, _) => should_revert_key(k), - }; - - if reverse { - let entry = mem::replace(&mut self.undo_log[i], UndoLog::Purged); - self.reverse(entry); - } - } + pub fn rollback_to(&mut self, snapshot: Snapshot) { + let map = &mut self.map; + self.undo_log.rollback_to(|| map, snapshot) } +} - pub fn rollback_to(&mut self, snapshot: Snapshot) { - self.assert_open_snapshot(&snapshot); - while self.undo_log.len() > snapshot.len { - let entry = self.undo_log.pop().unwrap(); - self.reverse(entry); - } +impl<'k, K, V, M, L> ops::Index<&'k K> for SnapshotMap +where + K: Hash + Clone + Eq, + M: Borrow>, +{ + type Output = V; + fn index(&self, key: &'k K) -> &V { + &self.map.borrow()[key] + } +} - self.num_open_snapshots -= 1; +impl Rollback> for SnapshotMap +where + K: Eq + Hash, + M: Rollback>, +{ + fn reverse(&mut self, undo: UndoLog) { + self.map.reverse(undo) } +} - fn reverse(&mut self, entry: UndoLog) { - match entry { +impl Rollback> for FxHashMap +where + K: Eq + Hash, +{ + fn reverse(&mut self, undo: UndoLog) { + match undo { UndoLog::Inserted(key) => { - self.map.remove(&key); + self.remove(&key); } UndoLog::Overwrite(key, old_value) => { - self.map.insert(key, old_value); + self.insert(key, old_value); } UndoLog::Purged => {} } } } - -impl<'k, K, V> ops::Index<&'k K> for SnapshotMap -where - K: Hash + Clone + Eq, -{ - type Output = V; - fn index(&self, key: &'k K) -> &V { - &self.map[key] - } -} diff --git a/src/librustc_data_structures/sorted_map.rs b/src/librustc_data_structures/sorted_map.rs index 8c42b74b85aef..652268dcee884 100644 --- a/src/librustc_data_structures/sorted_map.rs +++ b/src/librustc_data_structures/sorted_map.rs @@ -110,13 +110,13 @@ impl SortedMap { /// Iterate over the keys, sorted #[inline] - pub fn keys(&self) -> impl Iterator + ExactSizeIterator { + pub fn keys(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator { self.data.iter().map(|&(ref k, _)| k) } /// Iterate over values, sorted by key #[inline] - pub fn values(&self) -> impl Iterator + ExactSizeIterator { + pub fn values(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator { self.data.iter().map(|&(_, ref v)| v) } diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index a98e77cebd88a..97b02eaef35d0 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -210,6 +210,12 @@ impl HashStable for ::std::num::NonZeroU32 { } } +impl HashStable for ::std::num::NonZeroUsize { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.get().hash_stable(ctx, hasher) + } +} + impl HashStable for f32 { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let val: u32 = unsafe { ::std::mem::transmute(*self) }; diff --git a/src/librustc_data_structures/stack.rs b/src/librustc_data_structures/stack.rs new file mode 100644 index 0000000000000..a4964b7aa0cc8 --- /dev/null +++ b/src/librustc_data_structures/stack.rs @@ -0,0 +1,17 @@ +// This is the amount of bytes that need to be left on the stack before increasing the size. +// It must be at least as large as the stack required by any code that does not call +// `ensure_sufficient_stack`. +const RED_ZONE: usize = 100 * 1024; // 100k + +// Only the first stack that is pushed, grows exponentially (2^n * STACK_PER_RECURSION) from then +// on. This flag has performance relevant characteristics. Don't set it too high. +const STACK_PER_RECURSION: usize = 1 * 1024 * 1024; // 1MB + +/// Grows the stack on demand to prevent stack overflow. Call this in strategic locations +/// to "break up" recursive calls. E.g. almost any call to `visit_expr` or equivalent can benefit +/// from this. +/// +/// Should not be sprinkled around carelessly, as it causes a little bit of overhead. +pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { + stacker::maybe_grow(RED_ZONE, STACK_PER_RECURSION, f) +} diff --git a/src/librustc_data_structures/tiny_list.rs b/src/librustc_data_structures/tiny_list.rs index 78cbc1240b182..e94a0c6eb5943 100644 --- a/src/librustc_data_structures/tiny_list.rs +++ b/src/librustc_data_structures/tiny_list.rs @@ -52,7 +52,7 @@ impl TinyList { if &e.data == data { return true; } - elem = e.next.as_ref().map(|e| &**e); + elem = e.next.as_deref(); } false } @@ -62,7 +62,7 @@ impl TinyList { let (mut elem, mut count) = (self.head.as_ref(), 0); while let Some(ref e) = elem { count += 1; - elem = e.next.as_ref().map(|e| &**e); + elem = e.next.as_deref(); } count } diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index 16f2e740104ca..de503fe8228aa 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -289,7 +289,7 @@ impl TransitiveRelation { /// /// - A != B /// - A R B is true - /// - for each i, j: B[i] R B[j] does not hold + /// - for each i, j: `B[i]` R `B[j]` does not hold /// /// The intuition is that this moves "one step up" through a lattice /// (where the relation is encoding the `<=` relation for the lattice). diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index aec10ee5ef537..cfd103aed3240 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -11,9 +11,10 @@ crate-type = ["dylib"] [dependencies] lazy_static = "1.0" +libc = "0.2" log = "0.4" env_logger = { version = "0.7", default-features = false } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_target = { path = "../librustc_target" } rustc_lint = { path = "../librustc_lint" } @@ -21,6 +22,7 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_feature = { path = "../librustc_feature" } rustc_hir = { path = "../librustc_hir" } +rustc_hir_pretty = { path = "../librustc_hir_pretty" } rustc_metadata = { path = "../librustc_metadata" } rustc_mir = { path = "../librustc_mir" } rustc_parse = { path = "../librustc_parse" } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 9097a72f36f49..6847b175e60eb 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -8,9 +8,6 @@ #![feature(nll)] #![recursion_limit = "256"] -pub extern crate getopts; -#[cfg(unix)] -extern crate libc; #[macro_use] extern crate log; #[macro_use] @@ -18,28 +15,26 @@ extern crate lazy_static; pub extern crate rustc_plugin_impl as plugin; -use rustc::middle::cstore::MetadataLoader; -use rustc::ty::TyCtxt; -use rustc::util::common::ErrorReported; use rustc_ast::ast; use rustc_codegen_ssa::{traits::CodegenBackend, CodegenResults}; use rustc_data_structures::profiling::print_time_passes_entry; use rustc_data_structures::sync::SeqCst; -use rustc_errors::{ - registry::{InvalidErrorCode, Registry}, - PResult, -}; +use rustc_errors::registry::{InvalidErrorCode, Registry}; +use rustc_errors::{ErrorReported, PResult}; use rustc_feature::{find_gated_cfg, UnstableFeatures}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_interface::util::{collect_crate_types, get_builtin_codegen_backend}; use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; use rustc_metadata::locator; +use rustc_middle::middle::cstore::MetadataLoader; +use rustc_middle::ty::TyCtxt; use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; use rustc_serialize::json::{self, ToJson}; use rustc_session::config::nightly_options; use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest}; +use rustc_session::getopts; use rustc_session::lint::{Lint, LintId}; use rustc_session::{config, DiagnosticOutput, Session}; use rustc_session::{early_error, early_warn}; @@ -139,7 +134,6 @@ pub fn diagnostics_registry() -> Registry { } // Parse args and run the compiler. This is the primary entry point for rustc. -// See comments on CompilerCalls below for details about the callbacks argument. // The FileLoader provides a way to load files from sources other than the file system. pub fn run_compiler( at_args: &[String], @@ -624,15 +618,15 @@ impl RustcDefaultCalls { ) -> Compilation { let r = matches.opt_strs("Z"); if r.iter().any(|s| *s == "ls") { - match input { - &Input::File(ref ifile) => { + match *input { + Input::File(ref ifile) => { let path = &(*ifile); let mut v = Vec::new(); locator::list_file_metadata(&sess.target.target, path, metadata_loader, &mut v) .unwrap(); println!("{}", String::from_utf8(v).unwrap()); } - &Input::Str { .. } => { + Input::Str { .. } => { early_error(ErrorOutputType::default(), "cannot list metadata for stdin"); } } @@ -961,32 +955,17 @@ fn describe_codegen_flags() { fn print_flag_list( cmdline_opt: &str, - flag_list: &[(&'static str, T, Option<&'static str>, &'static str)], + flag_list: &[(&'static str, T, &'static str, &'static str)], ) { - let max_len = flag_list - .iter() - .map(|&(name, _, opt_type_desc, _)| { - let extra_len = match opt_type_desc { - Some(..) => 4, - None => 0, - }; - name.chars().count() + extra_len - }) - .max() - .unwrap_or(0); + let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0); - for &(name, _, opt_type_desc, desc) in flag_list { - let (width, extra) = match opt_type_desc { - Some(..) => (max_len - 4, "=val"), - None => (max_len, ""), - }; + for &(name, _, _, desc) in flag_list { println!( - " {} {:>width$}{} -- {}", + " {} {:>width$}=val -- {}", cmdline_opt, name.replace("_", "-"), - extra, desc, - width = width + width = max_len ); } } @@ -1159,6 +1138,16 @@ pub fn catch_fatal_errors R, R>(f: F) -> Result }) } +/// Variant of `catch_fatal_errors` for the `interface::Result` return type +/// that also computes the exit code. +pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { + let result = catch_fatal_errors(f).and_then(|result| result); + match result { + Ok(()) => EXIT_SUCCESS, + Err(_) => EXIT_FAILURE, + } +} + lazy_static! { static ref DEFAULT_HOOK: Box) + Sync + Send + 'static> = { let hook = panic::take_hook(); @@ -1249,12 +1238,12 @@ pub fn init_rustc_env_logger() { env_logger::init_from_env("RUSTC_LOG"); } -pub fn main() { +pub fn main() -> ! { let start = Instant::now(); init_rustc_env_logger(); let mut callbacks = TimePassesCallbacks::default(); install_ice_hook(); - let result = catch_fatal_errors(|| { + let exit_code = catch_with_exit_code(|| { let args = env::args_os() .enumerate() .map(|(i, arg)| { @@ -1267,13 +1256,8 @@ pub fn main() { }) .collect::>(); run_compiler(&args, &mut callbacks, None, None) - }) - .and_then(|result| result); - let exit_code = match result { - Ok(_) => EXIT_SUCCESS, - Err(_) => EXIT_FAILURE, - }; + }); // The extra `\t` is necessary to align this label with the others. print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed()); - process::exit(exit_code); + process::exit(exit_code) } diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 1e5cc55a82853..a443b8f464f1c 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -1,16 +1,17 @@ //! The various pretty-printing routines. -use rustc::hir::map as hir_map; -use rustc::ty::{self, TyCtxt}; -use rustc::util::common::ErrorReported; use rustc_ast::ast; use rustc_ast_pretty::pprust; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::print as pprust_hir; +use rustc_hir_pretty as pprust_hir; +use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::{self, TyCtxt}; use rustc_mir::util::{write_mir_graphviz, write_mir_pretty}; use rustc_session::config::{Input, PpMode, PpSourceMode}; use rustc_session::Session; +use rustc_span::symbol::Ident; use rustc_span::FileName; use std::cell::Cell; @@ -155,7 +156,7 @@ impl<'hir> pprust::PpAnn for NoAnn<'hir> {} impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { if let Some(tcx) = self.tcx { - pprust_hir::PpAnn::nested(&tcx.hir(), state, nested) + pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested) } } } @@ -177,9 +178,8 @@ impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> { impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> { fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) { - match node { - pprust::AnnNode::Expr(_) => s.popen(), - _ => {} + if let pprust::AnnNode::Expr(_) = node { + s.popen(); } } fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) { @@ -228,13 +228,12 @@ impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> { impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { if let Some(ref tcx) = self.tcx { - pprust_hir::PpAnn::nested(&tcx.hir(), state, nested) + pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested) } } fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { - match node { - pprust_hir::AnnNode::Expr(_) => s.popen(), - _ => {} + if let pprust_hir::AnnNode::Expr(_) = node { + s.popen(); } } fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { @@ -286,7 +285,7 @@ impl<'a> PrinterSupport for HygieneAnnotation<'a> { impl<'a> pprust::PpAnn for HygieneAnnotation<'a> { fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) { match node { - pprust::AnnNode::Ident(&ast::Ident { name, span }) => { + pprust::AnnNode::Ident(&Ident { name, span }) => { s.s.space(); s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt())) } @@ -324,7 +323,7 @@ impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> { } fn node_path(&self, id: hir::HirId) -> Option { - Some(self.tcx.def_path_str(self.tcx.hir().local_def_id(id))) + Some(self.tcx.def_path_str(self.tcx.hir().local_def_id(id).to_def_id())) } } @@ -334,25 +333,22 @@ impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> { if let pprust_hir::Nested::Body(id) = nested { self.tables.set(self.tcx.body_tables(id)); } - pprust_hir::PpAnn::nested(&self.tcx.hir(), state, nested); + let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>); + pprust_hir::PpAnn::nested(pp_ann, state, nested); self.tables.set(old_tables); } fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { - match node { - pprust_hir::AnnNode::Expr(_) => s.popen(), - _ => {} + if let pprust_hir::AnnNode::Expr(_) = node { + s.popen(); } } fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { - match node { - pprust_hir::AnnNode::Expr(expr) => { - s.s.space(); - s.s.word("as"); - s.s.space(); - s.s.word(self.tables.get().expr_ty(expr).to_string()); - s.pclose(); - } - _ => {} + if let pprust_hir::AnnNode::Expr(expr) = node { + s.s.space(); + s.s.word("as"); + s.s.space(); + s.s.word(self.tables.get().expr_ty(expr).to_string()); + s.pclose(); } } } diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index 33bfaddc39c9d..5865042859dca 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -97,7 +97,6 @@ E0184: include_str!("./error_codes/E0184.md"), E0185: include_str!("./error_codes/E0185.md"), E0186: include_str!("./error_codes/E0186.md"), E0191: include_str!("./error_codes/E0191.md"), -E0192: include_str!("./error_codes/E0192.md"), E0193: include_str!("./error_codes/E0193.md"), E0195: include_str!("./error_codes/E0195.md"), E0197: include_str!("./error_codes/E0197.md"), @@ -120,6 +119,8 @@ E0222: include_str!("./error_codes/E0222.md"), E0223: include_str!("./error_codes/E0223.md"), E0224: include_str!("./error_codes/E0224.md"), E0225: include_str!("./error_codes/E0225.md"), +E0226: include_str!("./error_codes/E0226.md"), +E0228: include_str!("./error_codes/E0228.md"), E0229: include_str!("./error_codes/E0229.md"), E0230: include_str!("./error_codes/E0230.md"), E0231: include_str!("./error_codes/E0231.md"), @@ -281,6 +282,7 @@ E0535: include_str!("./error_codes/E0535.md"), E0536: include_str!("./error_codes/E0536.md"), E0537: include_str!("./error_codes/E0537.md"), E0538: include_str!("./error_codes/E0538.md"), +E0539: include_str!("./error_codes/E0539.md"), E0541: include_str!("./error_codes/E0541.md"), E0550: include_str!("./error_codes/E0550.md"), E0551: include_str!("./error_codes/E0551.md"), @@ -366,6 +368,7 @@ E0644: include_str!("./error_codes/E0644.md"), E0646: include_str!("./error_codes/E0646.md"), E0647: include_str!("./error_codes/E0647.md"), E0648: include_str!("./error_codes/E0648.md"), +E0657: include_str!("./error_codes/E0657.md"), E0658: include_str!("./error_codes/E0658.md"), E0659: include_str!("./error_codes/E0659.md"), E0660: include_str!("./error_codes/E0660.md"), @@ -385,14 +388,18 @@ E0691: include_str!("./error_codes/E0691.md"), E0692: include_str!("./error_codes/E0692.md"), E0693: include_str!("./error_codes/E0693.md"), E0695: include_str!("./error_codes/E0695.md"), +E0696: include_str!("./error_codes/E0696.md"), E0697: include_str!("./error_codes/E0697.md"), E0698: include_str!("./error_codes/E0698.md"), E0699: include_str!("./error_codes/E0699.md"), E0700: include_str!("./error_codes/E0700.md"), E0701: include_str!("./error_codes/E0701.md"), +E0703: include_str!("./error_codes/E0703.md"), E0704: include_str!("./error_codes/E0704.md"), E0705: include_str!("./error_codes/E0705.md"), E0706: include_str!("./error_codes/E0706.md"), +E0708: include_str!("./error_codes/E0708.md"), +E0710: include_str!("./error_codes/E0710.md"), E0712: include_str!("./error_codes/E0712.md"), E0713: include_str!("./error_codes/E0713.md"), E0714: include_str!("./error_codes/E0714.md"), @@ -414,7 +421,6 @@ E0734: include_str!("./error_codes/E0734.md"), E0735: include_str!("./error_codes/E0735.md"), E0736: include_str!("./error_codes/E0736.md"), E0737: include_str!("./error_codes/E0737.md"), -E0738: include_str!("./error_codes/E0738.md"), E0739: include_str!("./error_codes/E0739.md"), E0740: include_str!("./error_codes/E0740.md"), E0741: include_str!("./error_codes/E0741.md"), @@ -425,6 +431,12 @@ E0745: include_str!("./error_codes/E0745.md"), E0746: include_str!("./error_codes/E0746.md"), E0747: include_str!("./error_codes/E0747.md"), E0748: include_str!("./error_codes/E0748.md"), +E0749: include_str!("./error_codes/E0749.md"), +E0750: include_str!("./error_codes/E0750.md"), +E0751: include_str!("./error_codes/E0751.md"), +E0752: include_str!("./error_codes/E0752.md"), +E0753: include_str!("./error_codes/E0753.md"), +E0754: include_str!("./error_codes/E0754.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -459,6 +471,7 @@ E0748: include_str!("./error_codes/E0748.md"), // E0188, // can not cast an immutable reference to a mutable pointer // E0189, // deprecated: can only cast a boxed pointer to a boxed object // E0190, // deprecated: can only cast a &-pointer to an &-object +// E0192, // negative impl only applicable to auto traits // E0194, // merged into E0403 // E0196, // cannot determine a type for this closure E0208, @@ -470,9 +483,7 @@ E0748: include_str!("./error_codes/E0748.md"), // E0217, // ambiguous associated type, defined in multiple supertraits // E0218, // no associated type defined // E0219, // associated type defined in higher-ranked supertrait - E0226, // only a single explicit lifetime bound is permitted E0227, // ambiguous lifetime bound, explicit lifetime bound required - E0228, // explicit lifetime bound required // E0233, // E0234, // E0235, // structure constructor specifies a structure of type but @@ -537,7 +548,7 @@ E0748: include_str!("./error_codes/E0748.md"), // E0467, removed // E0470, removed // E0471, // constant evaluation error (in pattern) - E0472, // asm! is unsupported on this target + E0472, // llvm_asm! is unsupported on this target E0473, // dereference of reference outside its lifetime E0474, // captured variable `..` does not outlive the enclosing closure E0475, // index of slice outside its lifetime @@ -561,7 +572,6 @@ E0748: include_str!("./error_codes/E0748.md"), E0521, // borrowed data escapes outside of closure E0523, // E0526, // shuffle indices are not constant - E0539, // incorrect meta item E0540, // multiple rustc_deprecated attributes E0542, // missing 'since' E0543, // missing 'reason' @@ -592,23 +602,21 @@ E0748: include_str!("./error_codes/E0748.md"), // used in argument position E0640, // infer outlives requirements // E0645, // trait aliases not finished - E0657, // `impl Trait` can only capture lifetimes bound at the fn level E0667, // `impl Trait` in projections E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders // E0694, // an unknown tool name found in scoped attributes - E0696, // `continue` pointing to a labeled block // E0702, // replaced with a generic attribute input check - E0703, // invalid ABI // E0707, // multiple elided lifetimes used in arguments of `async fn` - E0708, // `async` non-`move` closures with parameters are not currently - // supported // E0709, // multiple different lifetimes used in arguments of `async fn` - E0710, // an unknown tool name found in scoped lint E0711, // a feature has been declared with conflicting stability attributes E0717, // rustc_promotable without stability attribute // E0721, // `await` keyword E0722, // Malformed `#[optimize]` attribute E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions E0726, // non-explicit (not `'_`) elided lifetime in unsupported position +// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. + E0755, // `#[ffi_pure]` is only allowed on foreign functions + E0756, // `#[ffi_const]` is only allowed on foreign functions + E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]` } diff --git a/src/librustc_error_codes/error_codes/E0060.md b/src/librustc_error_codes/error_codes/E0060.md index 7a07b8e7ed669..e6906d72367d8 100644 --- a/src/librustc_error_codes/error_codes/E0060.md +++ b/src/librustc_error_codes/error_codes/E0060.md @@ -2,12 +2,14 @@ External C functions are allowed to be variadic. However, a variadic function takes a minimum number of arguments. For example, consider C's variadic `printf` function: -``` +```compile_fail,E0060 use std::os::raw::{c_char, c_int}; extern "C" { fn printf(_: *const c_char, ...) -> c_int; } + +unsafe { printf(); } // error! ``` Using this declaration, it must be called with at least one argument, so diff --git a/src/librustc_error_codes/error_codes/E0130.md b/src/librustc_error_codes/error_codes/E0130.md index 539049edb33b7..a270feaf58c17 100644 --- a/src/librustc_error_codes/error_codes/E0130.md +++ b/src/librustc_error_codes/error_codes/E0130.md @@ -2,7 +2,7 @@ A pattern was declared as an argument in a foreign function declaration. Erroneous code example: -```compile_fail +```compile_fail,E0130 extern { fn foo((a, b): (u32, u32)); // error: patterns aren't allowed in foreign // function declarations diff --git a/src/librustc_error_codes/error_codes/E0152.md b/src/librustc_error_codes/error_codes/E0152.md index 602dcb6e2c5b7..120c96b421104 100644 --- a/src/librustc_error_codes/error_codes/E0152.md +++ b/src/librustc_error_codes/error_codes/E0152.md @@ -5,8 +5,8 @@ Erroneous code example: ```compile_fail,E0152 #![feature(lang_items)] -#[lang = "arc"] -struct Foo; // error: duplicate lang item found: `arc` +#[lang = "owned_box"] +struct Foo; // error: duplicate lang item found: `owned_box` ``` Lang items are already implemented in the standard library. Unless you are diff --git a/src/librustc_error_codes/error_codes/E0198.md b/src/librustc_error_codes/error_codes/E0198.md index 687214a205096..90f1e54287496 100644 --- a/src/librustc_error_codes/error_codes/E0198.md +++ b/src/librustc_error_codes/error_codes/E0198.md @@ -2,7 +2,7 @@ A negative implementation was marked as unsafe. Erroneous code example: -```compile_fail +```compile_fail,E0198 struct Foo; unsafe impl !Clone for Foo { } // error! diff --git a/src/librustc_error_codes/error_codes/E0202.md b/src/librustc_error_codes/error_codes/E0202.md index b20d338c5fd99..afc61ec2e48ff 100644 --- a/src/librustc_error_codes/error_codes/E0202.md +++ b/src/librustc_error_codes/error_codes/E0202.md @@ -1,5 +1,15 @@ Inherent associated types were part of [RFC 195] but are not yet implemented. See [the tracking issue][iss8995] for the status of this implementation. +Erroneous code example: + +```compile_fail,E0202 +struct Foo; + +impl Foo { + type Bar = isize; // error! +} +``` + [RFC 195]: https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md [iss8995]: https://github.com/rust-lang/rust/issues/8995 diff --git a/src/librustc_error_codes/error_codes/E0226.md b/src/librustc_error_codes/error_codes/E0226.md new file mode 100644 index 0000000000000..4e65132ff0d69 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0226.md @@ -0,0 +1,21 @@ +More than one explicit lifetime bound was used on a trait object. + +Example of erroneous code: + +```compile_fail,E0226 +trait Foo {} + +type T<'a, 'b> = dyn Foo + 'a + 'b; // error: Trait object `arg` has two + // lifetime bound, 'a and 'b. +``` + +Here `T` is a trait object with two explicit lifetime bounds, 'a and 'b. + +Only a single explicit lifetime bound is permitted on trait objects. +To fix this error, consider removing one of the lifetime bounds: + +``` +trait Foo {} + +type T<'a> = dyn Foo + 'a; +``` diff --git a/src/librustc_error_codes/error_codes/E0228.md b/src/librustc_error_codes/error_codes/E0228.md new file mode 100644 index 0000000000000..3443a5ae8638c --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0228.md @@ -0,0 +1,40 @@ +The lifetime bound for this object type cannot be deduced from context and must +be specified. + +Erroneous code example: + +```compile_fail,E0228 +trait Trait { } + +struct TwoBounds<'a, 'b, T: Sized + 'a + 'b> { + x: &'a i32, + y: &'b i32, + z: T, +} + +type Foo<'a, 'b> = TwoBounds<'a, 'b, dyn Trait>; +``` + +When a trait object is used as a type argument of a generic type, Rust will try +to infer its lifetime if unspecified. However, this isn't possible when the +containing type has more than one lifetime bound. + +The above example can be resolved by either reducing the number of lifetime +bounds to one or by making the trait object lifetime explicit, like so: + +``` +trait Trait { } + +struct TwoBounds<'a, 'b, T: Sized + 'a + 'b> { + x: &'a i32, + y: &'b i32, + z: T, +} + +type Foo<'a, 'b> = TwoBounds<'a, 'b, dyn Trait + 'b>; +``` + +For more information, see [RFC 599] and its amendment [RFC 1156]. + +[RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md +[RFC 1156]: https://github.com/rust-lang/rfcs/blob/master/text/1156-adjust-default-object-bounds.md diff --git a/src/librustc_error_codes/error_codes/E0230.md b/src/librustc_error_codes/error_codes/E0230.md index 9dbcb8e010b1c..cfb72e74319c1 100644 --- a/src/librustc_error_codes/error_codes/E0230.md +++ b/src/librustc_error_codes/error_codes/E0230.md @@ -3,15 +3,11 @@ message for when a particular trait isn't implemented on a type placed in a position that needs that trait. For example, when the following code is compiled: -```compile_fail +```compile_fail,E0230 #![feature(rustc_attrs)] -fn foo>(x: T){} - -#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { /* ... */ } - -foo(true); // `bool` does not implement `Index` +#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{B}>`"] // error +trait BadAnnotation {} ``` There will be an error about `bool` not implementing `Index`, followed by a diff --git a/src/librustc_error_codes/error_codes/E0231.md b/src/librustc_error_codes/error_codes/E0231.md index 4f80da54540b1..23a0a88ecdd9b 100644 --- a/src/librustc_error_codes/error_codes/E0231.md +++ b/src/librustc_error_codes/error_codes/E0231.md @@ -3,15 +3,11 @@ message for when a particular trait isn't implemented on a type placed in a position that needs that trait. For example, when the following code is compiled: -```compile_fail +```compile_fail,E0231 #![feature(rustc_attrs)] -fn foo>(x: T){} - -#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { /* ... */ } - -foo(true); // `bool` does not implement `Index` +#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{}>`"] // error! +trait BadAnnotation {} ``` there will be an error about `bool` not implementing `Index`, followed by a diff --git a/src/librustc_error_codes/error_codes/E0232.md b/src/librustc_error_codes/error_codes/E0232.md index 07a031488d091..b310caefa6e31 100644 --- a/src/librustc_error_codes/error_codes/E0232.md +++ b/src/librustc_error_codes/error_codes/E0232.md @@ -3,15 +3,11 @@ message for when a particular trait isn't implemented on a type placed in a position that needs that trait. For example, when the following code is compiled: -```compile_fail +```compile_fail,E0232 #![feature(rustc_attrs)] -fn foo>(x: T){} - -#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { /* ... */ } - -foo(true); // `bool` does not implement `Index` +#[rustc_on_unimplemented(lorem="")] // error! +trait BadAnnotation {} ``` there will be an error about `bool` not implementing `Index`, followed by a diff --git a/src/librustc_error_codes/error_codes/E0264.md b/src/librustc_error_codes/error_codes/E0264.md index 708eac8837a7c..e2a27f7b10672 100644 --- a/src/librustc_error_codes/error_codes/E0264.md +++ b/src/librustc_error_codes/error_codes/E0264.md @@ -12,7 +12,7 @@ extern "C" { ``` A list of available external lang items is available in -`src/librustc/middle/weak_lang_items.rs`. Example: +`src/librustc_middle/middle/weak_lang_items.rs`. Example: ``` #![feature(lang_items)] diff --git a/src/librustc_error_codes/error_codes/E0281.md b/src/librustc_error_codes/error_codes/E0281.md index 1a9796d2790ea..1d7904b67ddb4 100644 --- a/src/librustc_error_codes/error_codes/E0281.md +++ b/src/librustc_error_codes/error_codes/E0281.md @@ -4,7 +4,7 @@ You tried to supply a type which doesn't implement some trait in a location which expected that trait. This error typically occurs when working with `Fn`-based types. Erroneous code example: -```compile-fail +```compile_fail fn foo(x: F) { } fn main() { diff --git a/src/librustc_error_codes/error_codes/E0307.md b/src/librustc_error_codes/error_codes/E0307.md index 52707b93acc86..0d29d56ea1a75 100644 --- a/src/librustc_error_codes/error_codes/E0307.md +++ b/src/librustc_error_codes/error_codes/E0307.md @@ -64,7 +64,7 @@ impl Trait for Foo { } ``` -The nightly feature [Arbintrary self types][AST] extends the accepted +The nightly feature [Arbitrary self types][AST] extends the accepted set of receiver types to also include any type that can dereference to `Self`: diff --git a/src/librustc_error_codes/error_codes/E0308.md b/src/librustc_error_codes/error_codes/E0308.md index b2c8437049001..e2c40f03019c1 100644 --- a/src/librustc_error_codes/error_codes/E0308.md +++ b/src/librustc_error_codes/error_codes/E0308.md @@ -12,8 +12,7 @@ let x: i32 = "I am not a number!"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. diff --git a/src/librustc_error_codes/error_codes/E0364.md b/src/librustc_error_codes/error_codes/E0364.md index ec1fb911f3cc2..d01fb0c9c4229 100644 --- a/src/librustc_error_codes/error_codes/E0364.md +++ b/src/librustc_error_codes/error_codes/E0364.md @@ -3,27 +3,27 @@ attempted to `pub use` a type or value that was not itself public. Erroneous code example: -```compile_fail -mod foo { - const X: u32 = 1; -} - -pub use foo::X; +```compile_fail,E0364 +mod a { + fn foo() {} -fn main() {} + mod a { + pub use super::foo; // error! + } +} ``` The solution to this problem is to ensure that the items that you are re-exporting are themselves marked with `pub`: ``` -mod foo { - pub const X: u32 = 1; -} - -pub use foo::X; +mod a { + pub fn foo() {} // ok! -fn main() {} + mod a { + pub use super::foo; + } +} ``` See the [Use Declarations][use-declarations] section of the reference for diff --git a/src/librustc_error_codes/error_codes/E0378.md b/src/librustc_error_codes/error_codes/E0378.md index 7f4374738de28..c6fe997f3dcfc 100644 --- a/src/librustc_error_codes/error_codes/E0378.md +++ b/src/librustc_error_codes/error_codes/E0378.md @@ -3,7 +3,7 @@ or a newtype wrapper around a pointer. Erroneous code example: -```compile-fail,E0378 +```compile_fail,E0378 #![feature(dispatch_from_dyn)] use std::ops::DispatchFromDyn; diff --git a/src/librustc_error_codes/error_codes/E0404.md b/src/librustc_error_codes/error_codes/E0404.md index 201107c05a02c..1360cc99afcc4 100644 --- a/src/librustc_error_codes/error_codes/E0404.md +++ b/src/librustc_error_codes/error_codes/E0404.md @@ -1,5 +1,5 @@ -You tried to use something which is not a trait in a trait position, such as -a bound or `impl`. +A type that is not a trait was used in a trait position, such as a bound +or `impl`. Erroneous code example: @@ -18,8 +18,8 @@ struct Foo; fn bar(t: T) {} // error: `Foo` is not a trait ``` -Please verify that you didn't misspell the trait's name or otherwise use the -wrong identifier. Example: +Please verify that the trait's name was not misspelled or that the right +identifier was used. Example: ``` trait Foo { @@ -32,7 +32,7 @@ impl Foo for Bar { // ok! } ``` -or +or: ``` trait Foo { diff --git a/src/librustc_error_codes/error_codes/E0436.md b/src/librustc_error_codes/error_codes/E0436.md index b1afd9e960337..48ecc49e92f54 100644 --- a/src/librustc_error_codes/error_codes/E0436.md +++ b/src/librustc_error_codes/error_codes/E0436.md @@ -1,5 +1,4 @@ -The functional record update syntax is only allowed for structs. (Struct-like -enum variants don't qualify, for example.) +The functional record update syntax was used on something other than a struct. Erroneous code example: @@ -24,7 +23,9 @@ fn one_up_competitor(competitor_frequency: PublicationFrequency) } ``` -Rewrite the expression without functional record update syntax: +The functional record update syntax is only allowed for structs (struct-like +enum variants don't qualify, for example). To fix the previous code, rewrite the +expression without functional record update syntax: ``` enum PublicationFrequency { diff --git a/src/librustc_error_codes/error_codes/E0454.md b/src/librustc_error_codes/error_codes/E0454.md index 80eb91e43d16b..23ca6c7824df1 100644 --- a/src/librustc_error_codes/error_codes/E0454.md +++ b/src/librustc_error_codes/error_codes/E0454.md @@ -1,4 +1,6 @@ -A link name was given with an empty name. Erroneous code example: +A link name was given with an empty name. + +Erroneous code example: ```compile_fail,E0454 #[link(name = "")] extern {} diff --git a/src/librustc_error_codes/error_codes/E0458.md b/src/librustc_error_codes/error_codes/E0458.md index 5996f190b34f0..075226ac98b9d 100644 --- a/src/librustc_error_codes/error_codes/E0458.md +++ b/src/librustc_error_codes/error_codes/E0458.md @@ -1,4 +1,6 @@ -An unknown "kind" was specified for a link attribute. Erroneous code example: +An unknown "kind" was specified for a link attribute. + +Erroneous code example: ```compile_fail,E0458 #[link(kind = "wonderful_unicorn")] extern {} diff --git a/src/librustc_error_codes/error_codes/E0459.md b/src/librustc_error_codes/error_codes/E0459.md index 580cbf1e1c6ed..6f75f2a99a563 100644 --- a/src/librustc_error_codes/error_codes/E0459.md +++ b/src/librustc_error_codes/error_codes/E0459.md @@ -1,4 +1,6 @@ -A link was used without a name parameter. Erroneous code example: +A link was used without a name parameter. + +Erroneous code example: ```compile_fail,E0459 #[link(kind = "dylib")] extern {} diff --git a/src/librustc_error_codes/error_codes/E0463.md b/src/librustc_error_codes/error_codes/E0463.md index 41add698cdc90..e46938c607d34 100644 --- a/src/librustc_error_codes/error_codes/E0463.md +++ b/src/librustc_error_codes/error_codes/E0463.md @@ -1,4 +1,6 @@ -A plugin/crate was declared but cannot be found. Erroneous code example: +A plugin/crate was declared but cannot be found. + +Erroneous code example: ```compile_fail,E0463 #![feature(plugin)] diff --git a/src/librustc_error_codes/error_codes/E0466.md b/src/librustc_error_codes/error_codes/E0466.md index 443b7bae1345e..7aefedbc0875d 100644 --- a/src/librustc_error_codes/error_codes/E0466.md +++ b/src/librustc_error_codes/error_codes/E0466.md @@ -1,4 +1,4 @@ -Macro import declarations were malformed. +Macro import declaration was malformed. Erroneous code examples: diff --git a/src/librustc_error_codes/error_codes/E0468.md b/src/librustc_error_codes/error_codes/E0468.md index 73e3ebb949ac0..cf8664718fa63 100644 --- a/src/librustc_error_codes/error_codes/E0468.md +++ b/src/librustc_error_codes/error_codes/E0468.md @@ -1,4 +1,4 @@ -A non-root module attempts to import macros from another crate. +A non-root module tried to import macros from another crate. Example of erroneous code: @@ -17,7 +17,7 @@ Either move the macro import to crate root or do without the foreign macros. This will work: ``` -#[macro_use(debug_assert)] +#[macro_use(debug_assert)] // ok! extern crate core; mod foo { diff --git a/src/librustc_error_codes/error_codes/E0501.md b/src/librustc_error_codes/error_codes/E0501.md index f5aa17a809467..ffdbc443905ae 100644 --- a/src/librustc_error_codes/error_codes/E0501.md +++ b/src/librustc_error_codes/error_codes/E0501.md @@ -1,12 +1,4 @@ -This error indicates that a mutable variable is being used while it is still -captured by a closure. Because the closure has borrowed the variable, it is not -available for use until the closure goes out of scope. - -Note that a capture will either move or borrow a variable, but in this -situation, the closure is borrowing the variable. Take a look at the chapter -on [Capturing][capturing] in Rust By Example for more information. - -[capturing]: https://doc.rust-lang.org/stable/rust-by-example/fn/closures/capture.html +A mutable variable is used but it is already captured by a closure. Erroneous code example: @@ -29,6 +21,16 @@ fn foo(a: &mut i32) { } ``` +This error indicates that a mutable variable is used while it is still captured +by a closure. Because the closure has borrowed the variable, it is not available +until the closure goes out of scope. + +Note that a capture will either move or borrow a variable, but in this +situation, the closure is borrowing the variable. Take a look at the chapter +on [Capturing][capturing] in Rust By Example for more information. + +[capturing]: https://doc.rust-lang.org/stable/rust-by-example/fn/closures/capture.html + To fix this error, you can finish using the closure before using the captured variable: diff --git a/src/librustc_error_codes/error_codes/E0502.md b/src/librustc_error_codes/error_codes/E0502.md index f15c05d558d85..b90c59f580737 100644 --- a/src/librustc_error_codes/error_codes/E0502.md +++ b/src/librustc_error_codes/error_codes/E0502.md @@ -1,5 +1,4 @@ -This error indicates that you are trying to borrow a variable as mutable when it -has already been borrowed as immutable. +A variable already borrowed as immutable was borrowed as mutable. Erroneous code example: diff --git a/src/librustc_error_codes/error_codes/E0506.md b/src/librustc_error_codes/error_codes/E0506.md index 9b8e316487203..c312a0460e3a1 100644 --- a/src/librustc_error_codes/error_codes/E0506.md +++ b/src/librustc_error_codes/error_codes/E0506.md @@ -1,4 +1,4 @@ -This error occurs when an attempt is made to assign to a borrowed value. +An attempt was made to assign to a borrowed value. Erroneous code example: @@ -7,14 +7,12 @@ struct FancyNum { num: u8, } -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - let fancy_ref = &fancy_num; - fancy_num = FancyNum { num: 6 }; - // error: cannot assign to `fancy_num` because it is borrowed +let mut fancy_num = FancyNum { num: 5 }; +let fancy_ref = &fancy_num; +fancy_num = FancyNum { num: 6 }; +// error: cannot assign to `fancy_num` because it is borrowed - println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num); -} +println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num); ``` Because `fancy_ref` still holds a reference to `fancy_num`, `fancy_num` can't @@ -27,13 +25,11 @@ struct FancyNum { num: u8, } -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - let moved_num = fancy_num; - fancy_num = FancyNum { num: 6 }; +let mut fancy_num = FancyNum { num: 5 }; +let moved_num = fancy_num; +fancy_num = FancyNum { num: 6 }; - println!("Num: {}, Moved num: {}", fancy_num.num, moved_num.num); -} +println!("Num: {}, Moved num: {}", fancy_num.num, moved_num.num); ``` If the value has to be borrowed, try limiting the lifetime of the borrow using @@ -44,18 +40,16 @@ struct FancyNum { num: u8, } -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - - { - let fancy_ref = &fancy_num; - println!("Ref: {}", fancy_ref.num); - } +let mut fancy_num = FancyNum { num: 5 }; - // Works because `fancy_ref` is no longer in scope - fancy_num = FancyNum { num: 6 }; - println!("Num: {}", fancy_num.num); +{ + let fancy_ref = &fancy_num; + println!("Ref: {}", fancy_ref.num); } + +// Works because `fancy_ref` is no longer in scope +fancy_num = FancyNum { num: 6 }; +println!("Num: {}", fancy_num.num); ``` Or by moving the reference into a function: @@ -65,17 +59,15 @@ struct FancyNum { num: u8, } -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - - print_fancy_ref(&fancy_num); - - // Works because function borrow has ended - fancy_num = FancyNum { num: 6 }; - println!("Num: {}", fancy_num.num); -} - fn print_fancy_ref(fancy_ref: &FancyNum){ println!("Ref: {}", fancy_ref.num); } + +let mut fancy_num = FancyNum { num: 5 }; + +print_fancy_ref(&fancy_num); + +// Works because function borrow has ended +fancy_num = FancyNum { num: 6 }; +println!("Num: {}", fancy_num.num); ``` diff --git a/src/librustc_error_codes/error_codes/E0507.md b/src/librustc_error_codes/error_codes/E0507.md index 1e3457e96c5f8..254751fc45e30 100644 --- a/src/librustc_error_codes/error_codes/E0507.md +++ b/src/librustc_error_codes/error_codes/E0507.md @@ -1,9 +1,4 @@ -You tried to move out of a value which was borrowed. - -This can also happen when using a type implementing `Fn` or `FnMut`, as neither -allows moving out of them (they usually represent closures which can be called -more than once). Much of the text following applies equally well to non-`FnOnce` -closure bodies. +A borrowed value was moved out. Erroneous code example: @@ -32,6 +27,11 @@ you have three choices: * Somehow reclaim the ownership. * Implement the `Copy` trait on the type. +This can also happen when using a type implementing `Fn` or `FnMut`, as neither +allows moving out of them (they usually represent closures which can be called +more than once). Much of the text following applies equally well to non-`FnOnce` +closure bodies. + Examples: ``` diff --git a/src/librustc_error_codes/error_codes/E0510.md b/src/librustc_error_codes/error_codes/E0510.md index d5be417888b54..e045e04bdbe11 100644 --- a/src/librustc_error_codes/error_codes/E0510.md +++ b/src/librustc_error_codes/error_codes/E0510.md @@ -1,16 +1,29 @@ -Cannot mutate place in this match guard. +The matched value was assigned in a match guard. -When matching on a variable it cannot be mutated in the match guards, as this -could cause the match to be non-exhaustive: +Erroneous code example: ```compile_fail,E0510 let mut x = Some(0); match x { - None => (), - Some(_) if { x = None; false } => (), - Some(v) => (), // No longer matches + None => {} + Some(_) if { x = None; false } => {} // error! + Some(_) => {} } ``` +When matching on a variable it cannot be mutated in the match guards, as this +could cause the match to be non-exhaustive. + Here executing `x = None` would modify the value being matched and require us -to go "back in time" to the `None` arm. +to go "back in time" to the `None` arm. To fix it, change the value in the match +arm: + +``` +let mut x = Some(0); +match x { + None => {} + Some(_) => { + x = None; // ok! + } +} +``` diff --git a/src/librustc_error_codes/error_codes/E0511.md b/src/librustc_error_codes/error_codes/E0511.md index 4f6644f358291..5351a685eb512 100644 --- a/src/librustc_error_codes/error_codes/E0511.md +++ b/src/librustc_error_codes/error_codes/E0511.md @@ -1,5 +1,6 @@ -Invalid monomorphization of an intrinsic function was used. Erroneous code -example: +Invalid monomorphization of an intrinsic function was used. + +Erroneous code example: ```compile_fail,E0511 #![feature(platform_intrinsics)] diff --git a/src/librustc_error_codes/error_codes/E0512.md b/src/librustc_error_codes/error_codes/E0512.md index c13c0511c7575..00c0961228513 100644 --- a/src/librustc_error_codes/error_codes/E0512.md +++ b/src/librustc_error_codes/error_codes/E0512.md @@ -1,5 +1,6 @@ -Transmute with two differently sized types was attempted. Erroneous code -example: +Transmute with two differently sized types was attempted. + +Erroneous code example: ```compile_fail,E0512 fn takes_u8(_: u8) {} diff --git a/src/librustc_error_codes/error_codes/E0515.md b/src/librustc_error_codes/error_codes/E0515.md index 9580b6f92acd1..0f4fbf672239c 100644 --- a/src/librustc_error_codes/error_codes/E0515.md +++ b/src/librustc_error_codes/error_codes/E0515.md @@ -1,7 +1,4 @@ -Cannot return value that references local variable - -Local variables, function parameters and temporaries are all dropped before the -end of the function body. So a reference to them cannot be returned. +A reference to a local variable was returned. Erroneous code example: @@ -20,6 +17,9 @@ 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. + Consider returning an owned value instead: ``` diff --git a/src/librustc_error_codes/error_codes/E0516.md b/src/librustc_error_codes/error_codes/E0516.md index 815d760901300..935c31bbab98a 100644 --- a/src/librustc_error_codes/error_codes/E0516.md +++ b/src/librustc_error_codes/error_codes/E0516.md @@ -1,4 +1,5 @@ The `typeof` keyword is currently reserved but unimplemented. + Erroneous code example: ```compile_fail,E0516 diff --git a/src/librustc_error_codes/error_codes/E0517.md b/src/librustc_error_codes/error_codes/E0517.md index f738d33560a02..ae802245bd1d7 100644 --- a/src/librustc_error_codes/error_codes/E0517.md +++ b/src/librustc_error_codes/error_codes/E0517.md @@ -1,5 +1,4 @@ -This error indicates that a `#[repr(..)]` attribute was placed on an -unsupported item. +A `#[repr(..)]` attribute was placed on an unsupported item. Examples of erroneous code: diff --git a/src/librustc_error_codes/error_codes/E0518.md b/src/librustc_error_codes/error_codes/E0518.md index 1af9a3735fe1a..f04329bc4e618 100644 --- a/src/librustc_error_codes/error_codes/E0518.md +++ b/src/librustc_error_codes/error_codes/E0518.md @@ -1,7 +1,7 @@ -This error indicates that an `#[inline(..)]` attribute was incorrectly placed -on something other than a function or method. +An `#[inline(..)]` attribute was incorrectly placed on something other than a +function or method. -Examples of erroneous code: +Example of erroneous code: ```compile_fail,E0518 #[inline(always)] diff --git a/src/librustc_error_codes/error_codes/E0520.md b/src/librustc_error_codes/error_codes/E0520.md index e8a2b4da08054..f9d7e02e5c893 100644 --- a/src/librustc_error_codes/error_codes/E0520.md +++ b/src/librustc_error_codes/error_codes/E0520.md @@ -1,5 +1,7 @@ A non-default implementation was already made on this type so it cannot be -specialized further. Erroneous code example: +specialized further. + +Erroneous code example: ```compile_fail,E0520 #![feature(specialization)] diff --git a/src/librustc_error_codes/error_codes/E0522.md b/src/librustc_error_codes/error_codes/E0522.md index e4756c384c495..83272314a8708 100644 --- a/src/librustc_error_codes/error_codes/E0522.md +++ b/src/librustc_error_codes/error_codes/E0522.md @@ -1,7 +1,5 @@ -The lang attribute is intended for marking special items that are built-in to -Rust itself. This includes special traits (like `Copy` and `Sized`) that affect -how the compiler behaves, as well as special functions that may be automatically -invoked (such as the handler for out-of-bounds accesses when indexing a slice). +The lang attribute was used in an invalid context. + Erroneous code example: ```compile_fail,E0522 @@ -12,3 +10,8 @@ fn cookie() -> ! { // error: definition of an unknown language item: `cookie` loop {} } ``` + +The lang attribute is intended for marking special items that are built-in to +Rust itself. This includes special traits (like `Copy` and `Sized`) that affect +how the compiler behaves, as well as special functions that may be automatically +invoked (such as the handler for out-of-bounds accesses when indexing a slice). diff --git a/src/librustc_error_codes/error_codes/E0539.md b/src/librustc_error_codes/error_codes/E0539.md new file mode 100644 index 0000000000000..df2d7d910bb36 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0539.md @@ -0,0 +1,48 @@ +An invalid meta-item was used inside an attribute. + +Erroneous code example: + +```compile_fail,E0539 +#![feature(staged_api)] +#![stable(since = "1.0.0", feature = "test")] + +#[rustc_deprecated(reason)] // error! +#[unstable(feature = "deprecated_fn", issue = "123")] +fn deprecated() {} + +#[unstable(feature = "unstable_struct", issue)] // error! +struct Unstable; + +#[rustc_const_unstable(feature)] // error! +const fn unstable_fn() {} + +#[stable(feature = "stable_struct", since)] // error! +struct Stable; + +#[rustc_const_stable(feature)] // error! +const fn stable_fn() {} +``` + +Meta items are the key-value pairs inside of an attribute. +To fix these issues you need to give required key-value pairs. + +``` +#![feature(staged_api)] +#![stable(since = "1.0.0", feature = "test")] + +#[rustc_deprecated(since = "1.39.0", reason = "reason")] // ok! +#[unstable(feature = "deprecated_fn", issue = "123")] +fn deprecated() {} + +#[unstable(feature = "unstable_struct", issue = "123")] // ok! +struct Unstable; + +#[rustc_const_unstable(feature = "unstable_fn", issue = "124")] // ok! +const fn unstable_fn() {} + +#[stable(feature = "stable_struct", since = "1.39.0")] // ok! +struct Stable; + +#[rustc_const_stable(feature = "stable_fn", since = "1.39.0")] // ok! +const fn stable_fn() {} +``` diff --git a/src/librustc_error_codes/error_codes/E0554.md b/src/librustc_error_codes/error_codes/E0554.md index e25212983ebcc..e55fa4c6ede8b 100644 --- a/src/librustc_error_codes/error_codes/E0554.md +++ b/src/librustc_error_codes/error_codes/E0554.md @@ -1,7 +1,7 @@ Feature attributes are only allowed on the nightly release channel. Stable or beta compilers will not comply. -Example of erroneous code (on a stable compiler): +Erroneous code example: ```ignore (depends on release channel) #![feature(non_ascii_idents)] // error: `#![feature]` may not be used on the diff --git a/src/librustc_error_codes/error_codes/E0567.md b/src/librustc_error_codes/error_codes/E0567.md index ec1ed03c126e1..05cf8fed03160 100644 --- a/src/librustc_error_codes/error_codes/E0567.md +++ b/src/librustc_error_codes/error_codes/E0567.md @@ -6,8 +6,7 @@ Erroneous code example: #![feature(optin_builtin_traits)] auto trait Generic {} // error! - -fn main() {} +# fn main() {} ``` Since an auto trait is implemented on all existing types, the @@ -20,6 +19,5 @@ To fix this issue, just remove the generics: #![feature(optin_builtin_traits)] auto trait Generic {} // ok! - -fn main() {} +# fn main() {} ``` diff --git a/src/librustc_error_codes/error_codes/E0569.md b/src/librustc_error_codes/error_codes/E0569.md index 4cba0cf9c96ba..2ca2b57ecace5 100644 --- a/src/librustc_error_codes/error_codes/E0569.md +++ b/src/librustc_error_codes/error_codes/E0569.md @@ -1,5 +1,5 @@ If an impl has a generic parameter with the `#[may_dangle]` attribute, then -that impl must be declared as an `unsafe impl. +that impl must be declared as an `unsafe impl`. Erroneous code example: diff --git a/src/librustc_error_codes/error_codes/E0571.md b/src/librustc_error_codes/error_codes/E0571.md index c2a3a8d758881..eadae05aa304c 100644 --- a/src/librustc_error_codes/error_codes/E0571.md +++ b/src/librustc_error_codes/error_codes/E0571.md @@ -7,7 +7,7 @@ Example of erroneous code: # fn satisfied(n: usize) -> bool { n % 23 == 0 } let result = while true { if satisfied(i) { - break 2*i; // error: `break` with value from a `while` loop + break 2 * i; // error: `break` with value from a `while` loop } i += 1; }; @@ -22,9 +22,9 @@ Make sure `break value;` statements only occur in `loop` loops: ``` # let mut i = 1; # fn satisfied(n: usize) -> bool { n % 23 == 0 } -let result = loop { // ok! +let result = loop { // This is now a "loop" loop. if satisfied(i) { - break 2*i; + break 2 * i; // ok! } i += 1; }; diff --git a/src/librustc_error_codes/error_codes/E0579.md b/src/librustc_error_codes/error_codes/E0579.md index 225e27f0cab83..f554242a3d466 100644 --- a/src/librustc_error_codes/error_codes/E0579.md +++ b/src/librustc_error_codes/error_codes/E0579.md @@ -1,7 +1,4 @@ -When matching against an exclusive range, the compiler verifies that the range -is non-empty. Exclusive range patterns include the start point but not the end -point, so this is equivalent to requiring the start of the range to be less -than the end of the range. +A lower range wasn't less than the upper range. Erroneous code example: @@ -17,3 +14,8 @@ fn main() { } } ``` + +When matching against an exclusive range, the compiler verifies that the range +is non-empty. Exclusive range patterns include the start point but not the end +point, so this is equivalent to requiring the start of the range to be less +than the end of the range. diff --git a/src/librustc_error_codes/error_codes/E0581.md b/src/librustc_error_codes/error_codes/E0581.md index 947ec255a9db8..89f6e3269ec36 100644 --- a/src/librustc_error_codes/error_codes/E0581.md +++ b/src/librustc_error_codes/error_codes/E0581.md @@ -1,4 +1,4 @@ -In a `fn` type, a lifetime appears only in the return type, +In a `fn` type, a lifetime appears only in the return type and not in the arguments types. Erroneous code example: @@ -10,8 +10,11 @@ fn main() { } ``` -To fix this issue, either use the lifetime in the arguments, or use -`'static`. Example: +The problem here is that the lifetime isn't contrained by any of the arguments, +making it impossible to determine how long it's supposed to live. + +To fix this issue, either use the lifetime in the arguments, or use the +`'static` lifetime. Example: ``` fn main() { diff --git a/src/librustc_error_codes/error_codes/E0582.md b/src/librustc_error_codes/error_codes/E0582.md index c0cf44852c4ef..e50cc60ea3302 100644 --- a/src/librustc_error_codes/error_codes/E0582.md +++ b/src/librustc_error_codes/error_codes/E0582.md @@ -1,5 +1,5 @@ -A lifetime appears only in an associated-type binding, -and not in the input types to the trait. +A lifetime is only present in an associated-type binding, and not in the input +types to the trait. Erroneous code example: diff --git a/src/librustc_error_codes/error_codes/E0589.md b/src/librustc_error_codes/error_codes/E0589.md index 5e15a875b0ba7..8a4f8d2172585 100644 --- a/src/librustc_error_codes/error_codes/E0589.md +++ b/src/librustc_error_codes/error_codes/E0589.md @@ -1,6 +1,8 @@ The value of `N` that was specified for `repr(align(N))` was not a power of two, or was greater than 2^29. +Erroneous code example: + ```compile_fail,E0589 #[repr(align(15))] // error: invalid `repr(align)` attribute: not a power of two enum Foo { diff --git a/src/librustc_error_codes/error_codes/E0590.md b/src/librustc_error_codes/error_codes/E0590.md index 8032e45903ce2..df7aa4f0a1e8d 100644 --- a/src/librustc_error_codes/error_codes/E0590.md +++ b/src/librustc_error_codes/error_codes/E0590.md @@ -3,7 +3,7 @@ Example of erroneous code: -```compile_fail +```compile_fail,E0590 while break {} ``` diff --git a/src/librustc_error_codes/error_codes/E0639.md b/src/librustc_error_codes/error_codes/E0639.md index c2d9662337fa0..4646e37fb7526 100644 --- a/src/librustc_error_codes/error_codes/E0639.md +++ b/src/librustc_error_codes/error_codes/E0639.md @@ -3,5 +3,17 @@ instantiated from outside of the defining crate as it has been marked as `non_exhaustive` and as such more fields/variants may be added in future that could cause adverse side effects for this code. +Erroneous code example: + +```ignore (it only works cross-crate) +#[non_exhaustive] +pub struct NormalStruct { + pub first_field: u16, + pub second_field: u16, +} + +let ns = NormalStruct { first_field: 640, second_field: 480 }; // error! +``` + It is recommended that you look for a `new` function or equivalent in the crate's documentation. diff --git a/src/librustc_error_codes/error_codes/E0644.md b/src/librustc_error_codes/error_codes/E0644.md index 7a653bd2264fd..277643dfb1ab7 100644 --- a/src/librustc_error_codes/error_codes/E0644.md +++ b/src/librustc_error_codes/error_codes/E0644.md @@ -2,17 +2,17 @@ A closure or generator was constructed that references its own type. Erroneous example: -```compile-fail,E0644 +```compile_fail,E0644 fn fix(f: &F) where F: Fn(&F) { - f(&f); + f(&f); } fn main() { - fix(&|y| { - // Here, when `x` is called, the parameter `y` is equal to `x`. - }); + fix(&|y| { + // Here, when `x` is called, the parameter `y` is equal to `x`. + }); } ``` diff --git a/src/librustc_error_codes/error_codes/E0657.md b/src/librustc_error_codes/error_codes/E0657.md new file mode 100644 index 0000000000000..7fe48c5114790 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0657.md @@ -0,0 +1,57 @@ +A lifetime bound on a trait implementation was captured at an incorrect place. + +Erroneous code example: + +```compile_fail,E0657 +trait Id {} +trait Lt<'a> {} + +impl<'a> Lt<'a> for () {} +impl Id for T {} + +fn free_fn_capture_hrtb_in_impl_trait() + -> Box Id>> // error! +{ + Box::new(()) +} + +struct Foo; +impl Foo { + fn impl_fn_capture_hrtb_in_impl_trait() + -> Box Id>> // error! + { + Box::new(()) + } +} +``` + +Here, you have used the inappropriate lifetime in the `impl Trait`, +The `impl Trait` can only capture lifetimes bound at the fn or impl +level. + +To fix this we have to define the lifetime at the function or impl +level and use that lifetime in the `impl Trait`. For example you can +define the lifetime at the function: + +``` +trait Id {} +trait Lt<'a> {} + +impl<'a> Lt<'a> for () {} +impl Id for T {} + +fn free_fn_capture_hrtb_in_impl_trait<'b>() + -> Box Id>> // ok! +{ + Box::new(()) +} + +struct Foo; +impl Foo { + fn impl_fn_capture_hrtb_in_impl_trait<'b>() + -> Box Id>> // ok! + { + Box::new(()) + } +} +``` diff --git a/src/librustc_error_codes/error_codes/E0658.md b/src/librustc_error_codes/error_codes/E0658.md index 44a38cee23dfd..d821b9027f136 100644 --- a/src/librustc_error_codes/error_codes/E0658.md +++ b/src/librustc_error_codes/error_codes/E0658.md @@ -2,7 +2,7 @@ An unstable feature was used. Erroneous code example: -```compile_fail,E658 +```compile_fail,E0658 #[repr(u128)] // error: use of unstable library feature 'repr128' enum Foo { Bar(u64), diff --git a/src/librustc_error_codes/error_codes/E0660.md b/src/librustc_error_codes/error_codes/E0660.md index 189459175dca2..fccd1b96f60db 100644 --- a/src/librustc_error_codes/error_codes/E0660.md +++ b/src/librustc_error_codes/error_codes/E0660.md @@ -1,12 +1,12 @@ -The argument to the `asm` macro is not well-formed. +The argument to the `llvm_asm` macro is not well-formed. Erroneous code example: ```compile_fail,E0660 -asm!("nop" "nop"); +llvm_asm!("nop" "nop"); ``` Considering that this would be a long explanation, we instead recommend you -take a look at the [`asm`] chapter of the Unstable book: +take a look at the [`llvm_asm`] chapter of the Unstable book: -[asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/asm.html +[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/src/librustc_error_codes/error_codes/E0661.md b/src/librustc_error_codes/error_codes/E0661.md index b171ada620516..f1debee7a18f1 100644 --- a/src/librustc_error_codes/error_codes/E0661.md +++ b/src/librustc_error_codes/error_codes/E0661.md @@ -1,13 +1,13 @@ -An invalid syntax was passed to the second argument of an `asm` macro line. +An invalid syntax was passed to the second argument of an `llvm_asm` macro line. Erroneous code example: ```compile_fail,E0661 let a; -asm!("nop" : "r"(a)); +llvm_asm!("nop" : "r"(a)); ``` Considering that this would be a long explanation, we instead recommend you -take a look at the [`asm`] chapter of the Unstable book: +take a look at the [`llvm_asm`] chapter of the Unstable book: -[asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/asm.html +[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/src/librustc_error_codes/error_codes/E0662.md b/src/librustc_error_codes/error_codes/E0662.md index c15e736610b5e..d4765f078b0e6 100644 --- a/src/librustc_error_codes/error_codes/E0662.md +++ b/src/librustc_error_codes/error_codes/E0662.md @@ -1,15 +1,16 @@ -An invalid input operand constraint was passed to the `asm` macro (third line). +An invalid input operand constraint was passed to the `llvm_asm` macro +(third line). Erroneous code example: ```compile_fail,E0662 -asm!("xor %eax, %eax" - : - : "=test"("a") - ); +llvm_asm!("xor %eax, %eax" + : + : "=test"("a") + ); ``` Considering that this would be a long explanation, we instead recommend you -take a look at the [`asm`] chapter of the Unstable book: +take a look at the [`llvm_asm`] chapter of the Unstable book: -[asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/asm.html +[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/src/librustc_error_codes/error_codes/E0663.md b/src/librustc_error_codes/error_codes/E0663.md index bd450230ec3f2..d5a85b275db63 100644 --- a/src/librustc_error_codes/error_codes/E0663.md +++ b/src/librustc_error_codes/error_codes/E0663.md @@ -1,15 +1,16 @@ -An invalid input operand constraint was passed to the `asm` macro (third line). +An invalid input operand constraint was passed to the `llvm_asm` macro +(third line). Erroneous code example: ```compile_fail,E0663 -asm!("xor %eax, %eax" - : - : "+test"("a") - ); +llvm_asm!("xor %eax, %eax" + : + : "+test"("a") + ); ``` Considering that this would be a long explanation, we instead recommend you -take a look at the [`asm`] chapter of the Unstable book: +take a look at the [`llvm_asm`] chapter of the Unstable book: -[asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/asm.html +[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/src/librustc_error_codes/error_codes/E0664.md b/src/librustc_error_codes/error_codes/E0664.md index 768ffdf2de93b..ce9c9491df3d7 100644 --- a/src/librustc_error_codes/error_codes/E0664.md +++ b/src/librustc_error_codes/error_codes/E0664.md @@ -1,16 +1,16 @@ -A clobber was surrounded by braces in the `asm` macro. +A clobber was surrounded by braces in the `llvm_asm` macro. Erroneous code example: ```compile_fail,E0664 -asm!("mov $$0x200, %eax" - : - : - : "{eax}" - ); +llvm_asm!("mov $$0x200, %eax" + : + : + : "{eax}" + ); ``` Considering that this would be a long explanation, we instead recommend you -take a look at the [`asm`] chapter of the Unstable book: +take a look at the [`llvm_asm`] chapter of the Unstable book: -[asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/asm.html +[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/src/librustc_error_codes/error_codes/E0668.md b/src/librustc_error_codes/error_codes/E0668.md index f5d26244fb961..3b43a1bcae9e6 100644 --- a/src/librustc_error_codes/error_codes/E0668.md +++ b/src/librustc_error_codes/error_codes/E0668.md @@ -7,12 +7,12 @@ assembly call. In particular, it can happen if you forgot the closing bracket of a register constraint (see issue #51430): ```compile_fail,E0668 -#![feature(asm)] +#![feature(llvm_asm)] fn main() { let rax: u64; unsafe { - asm!("" :"={rax"(rax)); + llvm_asm!("" :"={rax"(rax)); println!("Accumulator is: {}", rax); } } diff --git a/src/librustc_error_codes/error_codes/E0669.md b/src/librustc_error_codes/error_codes/E0669.md index 39de01eebe825..f078c441b3421 100644 --- a/src/librustc_error_codes/error_codes/E0669.md +++ b/src/librustc_error_codes/error_codes/E0669.md @@ -1,5 +1,17 @@ Cannot convert inline assembly operand to a single LLVM value. +Erroneous code example: + +```compile_fail,E0669 +#![feature(llvm_asm)] + +fn main() { + unsafe { + llvm_asm!("" :: "r"("")); // error! + } +} +``` + This error usually happens when trying to pass in a value to an input inline assembly operand that is actually a pair of values. In particular, this can happen when trying to pass in a slice, for instance a `&str`. In Rust, these diff --git a/src/librustc_error_codes/error_codes/E0690.md b/src/librustc_error_codes/error_codes/E0690.md index 3a4d1c56fad0c..1673456580a85 100644 --- a/src/librustc_error_codes/error_codes/E0690.md +++ b/src/librustc_error_codes/error_codes/E0690.md @@ -14,7 +14,7 @@ struct LengthWithUnit { // error: transparent struct needs exactly one Because transparent structs are represented exactly like one of their fields at run time, said field must be uniquely determined. If there is no field, or if there are multiple fields, it is not clear how the struct should be represented. -Note that fields of zero-typed types (e.g., `PhantomData`) can also exist +Note that fields of zero-sized types (e.g., `PhantomData`) can also exist alongside the field that contains the actual data, they do not count for this error. When generic types are involved (as in the above example), an error is reported because the type parameter could be non-zero-sized. diff --git a/src/librustc_error_codes/error_codes/E0696.md b/src/librustc_error_codes/error_codes/E0696.md new file mode 100644 index 0000000000000..fc32d1cc5f798 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0696.md @@ -0,0 +1,49 @@ +A function is using `continue` keyword incorrectly. + +Erroneous code example: + +```compile_fail,E0696 +fn continue_simple() { + 'b: { + continue; // error! + } +} +fn continue_labeled() { + 'b: { + continue 'b; // error! + } +} +fn continue_crossing() { + loop { + 'b: { + continue; // error! + } + } +} +``` + +Here we have used the `continue` keyword incorrectly. As we +have seen above that `continue` pointing to a labeled block. + +To fix this we have to use the labeled block properly. +For example: + +``` +fn continue_simple() { + 'b: loop { + continue ; // ok! + } +} +fn continue_labeled() { + 'b: loop { + continue 'b; // ok! + } +} +fn continue_crossing() { + loop { + 'b: loop { + continue; // ok! + } + } +} +``` diff --git a/src/librustc_error_codes/error_codes/E0698.md b/src/librustc_error_codes/error_codes/E0698.md index d0fcec3990241..3ba992a8476ed 100644 --- a/src/librustc_error_codes/error_codes/E0698.md +++ b/src/librustc_error_codes/error_codes/E0698.md @@ -3,7 +3,7 @@ generator can be constructed. Erroneous code example: -```edition2018,compile-fail,E0698 +```edition2018,compile_fail,E0698 async fn bar() -> () {} async fn foo() { diff --git a/src/librustc_error_codes/error_codes/E0700.md b/src/librustc_error_codes/error_codes/E0700.md index 41ac4d948c22c..b1eb8b66ad682 100644 --- a/src/librustc_error_codes/error_codes/E0700.md +++ b/src/librustc_error_codes/error_codes/E0700.md @@ -3,7 +3,7 @@ appear within the `impl Trait` itself. Erroneous code example: -```compile-fail,E0700 +```compile_fail,E0700 use std::cell::Cell; trait Trait<'a> { } diff --git a/src/librustc_error_codes/error_codes/E0703.md b/src/librustc_error_codes/error_codes/E0703.md new file mode 100644 index 0000000000000..b42677d52cbb5 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0703.md @@ -0,0 +1,17 @@ +Invalid ABI (Application Binary Interface) used in the code. + +Erroneous code example: + +```compile_fail,E0703 +extern "invalid" fn foo() {} // error! +# fn main() {} +``` + +At present few predefined ABI's (like Rust, C, system, etc.) can be +used in Rust. Verify that the ABI is predefined. For example you can +replace the given ABI from 'Rust'. + +``` +extern "Rust" fn foo() {} // ok! +# fn main() { } +``` diff --git a/src/librustc_error_codes/error_codes/E0708.md b/src/librustc_error_codes/error_codes/E0708.md new file mode 100644 index 0000000000000..9287fc803d1de --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0708.md @@ -0,0 +1,26 @@ +`async` non-`move` closures with parameters are currently not supported. + +Erroneous code example: + +```compile_fail,edition2018,E0708 +#![feature(async_closure)] + +fn main() { + let add_one = async |num: u8| { // error! + num + 1 + }; +} +``` + +`async` with non-move is currently not supported with the current +version, you can use successfully by using move: + +```edition2018 +#![feature(async_closure)] + +fn main() { + let add_one = async move |num: u8| { // ok! + num + 1 + }; +} +``` diff --git a/src/librustc_error_codes/error_codes/E0710.md b/src/librustc_error_codes/error_codes/E0710.md new file mode 100644 index 0000000000000..d9cefe2a6da72 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0710.md @@ -0,0 +1,34 @@ +An unknown tool name found in scoped lint + +Erroneous code examples: + +```compile_fail,E0710 +#[allow(clipp::filter_map)] // error!` +fn main() { + // business logic +} +``` + +```compile_fail,E0710 +#[warn(clipp::filter_map)] // error!` +fn main() { + // business logic +} +``` + +Please verify you didn't misspell the tool's name or that you didn't +forget to import it in you project: + +``` +#[allow(clippy::filter_map)] // ok! +fn main() { + // business logic +} +``` + +``` +#[warn(clippy::filter_map)] // ok! +fn main() { + // business logic +} +``` diff --git a/src/librustc_error_codes/error_codes/E0714.md b/src/librustc_error_codes/error_codes/E0714.md index e8707f3e39039..45d1cafa69062 100644 --- a/src/librustc_error_codes/error_codes/E0714.md +++ b/src/librustc_error_codes/error_codes/E0714.md @@ -1,5 +1,19 @@ A `#[marker]` trait contained an associated item. +Erroneous code example: + +```compile_fail,E0714 +#![feature(marker_trait_attr)] +#![feature(associated_type_defaults)] + +#[marker] +trait MarkerConst { + const N: usize; // error! +} + +fn main() {} +``` + The items of marker traits cannot be overridden, so there's no need to have them when they cannot be changed per-type anyway. If you wanted them for ergonomic reasons, consider making an extension trait instead. diff --git a/src/librustc_error_codes/error_codes/E0715.md b/src/librustc_error_codes/error_codes/E0715.md index 9e20e81368338..8f0022d942547 100644 --- a/src/librustc_error_codes/error_codes/E0715.md +++ b/src/librustc_error_codes/error_codes/E0715.md @@ -1,5 +1,24 @@ An `impl` for a `#[marker]` trait tried to override an associated item. +Erroneous code example: + +```compile_fail,E0715 +#![feature(marker_trait_attr)] + +#[marker] +trait Marker { + const N: usize = 0; + fn do_something() {} +} + +struct OverrideConst; +impl Marker for OverrideConst { // error! + const N: usize = 1; +} + +fn main() {} +``` + Because marker traits are allowed to have multiple implementations for the same type, it's not allowed to override anything in those implementations, as it would be ambiguous which override should actually be used. diff --git a/src/librustc_error_codes/error_codes/E0718.md b/src/librustc_error_codes/error_codes/E0718.md index 8548ff0c15011..e7ae51ca58835 100644 --- a/src/librustc_error_codes/error_codes/E0718.md +++ b/src/librustc_error_codes/error_codes/E0718.md @@ -6,6 +6,6 @@ Examples of erroneous code: ```compile_fail,E0718 #![feature(lang_items)] -#[lang = "arc"] +#[lang = "owned_box"] static X: u32 = 42; ``` diff --git a/src/librustc_error_codes/error_codes/E0727.md b/src/librustc_error_codes/error_codes/E0727.md index 528807ee9afe2..be1b68e645d01 100644 --- a/src/librustc_error_codes/error_codes/E0727.md +++ b/src/librustc_error_codes/error_codes/E0727.md @@ -2,14 +2,16 @@ A `yield` clause was used in an `async` context. Example of erroneous code: -```compile_fail +```compile_fail,E0727,edition2018 #![feature(generators)] -let generator = || { - async { - yield; - } -}; +fn main() { + let generator = || { + async { + yield; + } + }; +} ``` Here, the `yield` keyword is used in an `async` block, @@ -17,10 +19,12 @@ which is not yet supported. To fix this error, you have to move `yield` out of the `async` block: -``` +```edition2018 #![feature(generators)] -let generator = || { - yield; -}; +fn main() { + let generator = || { + yield; + }; +} ``` diff --git a/src/librustc_error_codes/error_codes/E0730.md b/src/librustc_error_codes/error_codes/E0730.md index bf1f72be32589..c2a71ca5669a1 100644 --- a/src/librustc_error_codes/error_codes/E0730.md +++ b/src/librustc_error_codes/error_codes/E0730.md @@ -7,8 +7,8 @@ Example of erroneous code: fn is_123(x: [u32; N]) -> bool { match x { - [1, 2, 3] => true, // error: cannot pattern-match on an - // array without a fixed length + [1, 2, ..] => true, // error: cannot pattern-match on an + // array without a fixed length _ => false } } diff --git a/src/librustc_error_codes/error_codes/E0732.md b/src/librustc_error_codes/error_codes/E0732.md index 03137590e883f..7347e6654c5b3 100644 --- a/src/librustc_error_codes/error_codes/E0732.md +++ b/src/librustc_error_codes/error_codes/E0732.md @@ -1,5 +1,18 @@ An `enum` with a discriminant must specify a `#[repr(inttype)]`. +Erroneous code example: + +```compile_fail,E0732 +#![feature(arbitrary_enum_discriminant)] + +enum Enum { // error! + Unit = 1, + Tuple() = 2, + Struct{} = 3, +} +# fn main() {} +``` + A `#[repr(inttype)]` must be provided on an `enum` if it has a non-unit variant with a discriminant, or where there are both unit variants with discriminants and non-unit variants. This restriction ensures that there @@ -23,7 +36,9 @@ fn discriminant(v : &Enum) -> u8 { unsafe { *(v as *const Enum as *const u8) } } -assert_eq!(3, discriminant(&Enum::Unit)); -assert_eq!(2, discriminant(&Enum::Tuple(5))); -assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11})); +fn main() { + assert_eq!(3, discriminant(&Enum::Unit)); + assert_eq!(2, discriminant(&Enum::Tuple(5))); + assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11})); +} ``` diff --git a/src/librustc_error_codes/error_codes/E0738.md b/src/librustc_error_codes/error_codes/E0738.md deleted file mode 100644 index 8f31b701e495e..0000000000000 --- a/src/librustc_error_codes/error_codes/E0738.md +++ /dev/null @@ -1,11 +0,0 @@ -`#[track_caller]` cannot be used to annotate foreign functions. - -Erroneous example: - -```compile_fail,E0738 -#![feature(track_caller)] -extern "Rust" { - #[track_caller] - fn bar(); -} -``` diff --git a/src/librustc_error_codes/error_codes/E0740.md b/src/librustc_error_codes/error_codes/E0740.md index a9eb5f2790d47..3777678518964 100644 --- a/src/librustc_error_codes/error_codes/E0740.md +++ b/src/librustc_error_codes/error_codes/E0740.md @@ -1 +1,16 @@ A `union` cannot have fields with destructors. + +Erroneous code example: + +```compile_fail,E0740 +union Test { + a: A, // error! +} + +#[derive(Debug)] +struct A(i32); + +impl Drop for A { + fn drop(&mut self) { println!("A"); } +} +``` diff --git a/src/librustc_error_codes/error_codes/E0741.md b/src/librustc_error_codes/error_codes/E0741.md index 804260809e914..0a8650282a374 100644 --- a/src/librustc_error_codes/error_codes/E0741.md +++ b/src/librustc_error_codes/error_codes/E0741.md @@ -1,4 +1,4 @@ -Only `structural_match` types (that is, types that derive `PartialEq` and `Eq`) +Only structural-match types (that is, types that derive `PartialEq` and `Eq`) may be used as the types of const generic parameters. ```compile_fail,E0741 diff --git a/src/librustc_error_codes/error_codes/E0744.md b/src/librustc_error_codes/error_codes/E0744.md index 602fbc50a7153..56b947a8282ed 100644 --- a/src/librustc_error_codes/error_codes/E0744.md +++ b/src/librustc_error_codes/error_codes/E0744.md @@ -3,16 +3,13 @@ Control-flow expressions are not allowed inside a const context. At the moment, `if` and `match`, as well as the looping constructs `for`, `while`, and `loop`, are forbidden inside a `const`, `static`, or `const fn`. -```compile_fail,E0658 +```compile_fail,E0744 const _: i32 = { let mut x = 0; - loop { - x += 1; - if x == 4 { - break; - } + + for i in 0..4 { // error! + x += i; } - x }; ``` diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md index 16b2722f0eac2..305667e58f8fb 100644 --- a/src/librustc_error_codes/error_codes/E0746.md +++ b/src/librustc_error_codes/error_codes/E0746.md @@ -2,8 +2,7 @@ Return types cannot be `dyn Trait`s as they must be `Sized`. Erroneous code example: -```compile_fail,E0277 -# // FIXME: after E0746 is in beta, change the above +```compile_fail,E0746 trait T { fn bar(&self); } diff --git a/src/librustc_error_codes/error_codes/E0749.md b/src/librustc_error_codes/error_codes/E0749.md new file mode 100644 index 0000000000000..9eb8ee4e3fdf7 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0749.md @@ -0,0 +1,4 @@ +Negative impls are not allowed to have any items. Negative impls +declare that a trait is **not** implemented (and never will be) and +hence there is no need to specify the values for trait methods or +other items. diff --git a/src/librustc_error_codes/error_codes/E0750.md b/src/librustc_error_codes/error_codes/E0750.md new file mode 100644 index 0000000000000..e0cf56f716f9d --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0750.md @@ -0,0 +1,4 @@ +Negative impls cannot be default impls. A default impl supplies +default values for the items within to be used by other impls, whereas +a negative impl declares that there are no other impls. These don't +make sense to combine. diff --git a/src/librustc_error_codes/error_codes/E0751.md b/src/librustc_error_codes/error_codes/E0751.md new file mode 100644 index 0000000000000..809b888d92ac3 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0751.md @@ -0,0 +1,12 @@ +There are both a positive and negative trait implementation for the same type. + +Erroneous code example: + +```compile_fail,E0751 +trait MyTrait {} +impl MyTrait for i32 { } +impl !MyTrait for i32 { } +``` + +Negative implementations are a promise that the trait will never be +implemented for the given types. diff --git a/src/librustc_error_codes/error_codes/E0752.md b/src/librustc_error_codes/error_codes/E0752.md new file mode 100644 index 0000000000000..86945f83b5524 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0752.md @@ -0,0 +1,11 @@ +`fn main()` or the specified start function is not allowed to be +async. You might be seeing this error because your async runtime +library is not set up correctly. + +Erroneous code example: + +```compile_fail,E0752 +async fn main() -> Result { + Ok(1) +} +``` diff --git a/src/librustc_error_codes/error_codes/E0753.md b/src/librustc_error_codes/error_codes/E0753.md new file mode 100644 index 0000000000000..a69da964aee39 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0753.md @@ -0,0 +1,31 @@ +An inner doc comment was used in an invalid context. + +Erroneous code example: + +```compile_fail,E0753 +fn foo() {} +//! foo +// ^ error! +fn main() {} +``` + +Inner document can only be used before items. For example: + +``` +//! A working comment applied to the module! +fn foo() { + //! Another working comment! +} +fn main() {} +``` + +In case you want to document the item following the doc comment, you might want +to use outer doc comment: + +``` +/// I am an outer doc comment +#[doc = "I am also an outer doc comment!"] +fn foo() { + // ... +} +``` diff --git a/src/librustc_error_codes/error_codes/E0754.md b/src/librustc_error_codes/error_codes/E0754.md new file mode 100644 index 0000000000000..abdc01ed21ab7 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0754.md @@ -0,0 +1,33 @@ +An non-ascii identifier was used in an invalid context. + +Erroneous code example: + +```compile_fail,E0754 +# #![feature(non_ascii_idents)] + +mod řųśť; +// ^ error! +fn main() {} +``` + +```compile_fail,E0754 +# #![feature(non_ascii_idents)] + +#[no_mangle] +fn řųśť() {} +// ^ error! +fn main() {} +``` + +Non-ascii can be used as module names if it is inline +or a #\[path\] attribute is specified. For example: + +``` +# #![feature(non_ascii_idents)] + +mod řųśť { + const IS_GREAT: bool = true; +} + +fn main() {} +``` diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index fffae0bfd24d9..2dbd9f4e52fad 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -315,6 +315,20 @@ impl<'a> DiagnosticBuilder<'a> { self } + pub fn span_suggestion_verbose( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.0.allow_suggestions { + return self; + } + self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability); + self + } + pub fn span_suggestion_hidden( &mut self, sp: Span, diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 94053b98cd75b..b22da86c09187 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -285,21 +285,18 @@ pub trait Emitter { let has_macro_spans = iter::once(&*span) .chain(children.iter().map(|child| &child.span)) .flat_map(|span| span.primary_spans()) - .copied() - .flat_map(|sp| { - sp.macro_backtrace().filter_map(|expn_data| { - match expn_data.kind { - ExpnKind::Root => None, + .flat_map(|sp| sp.macro_backtrace()) + .find_map(|expn_data| { + match expn_data.kind { + ExpnKind::Root => None, - // Skip past non-macro entries, just in case there - // are some which do actually involve macros. - ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, + // Skip past non-macro entries, just in case there + // are some which do actually involve macros. + ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, - ExpnKind::Macro(macro_kind, _) => Some(macro_kind), - } - }) - }) - .next(); + ExpnKind::Macro(macro_kind, _) => Some(macro_kind), + } + }); if !backtrace { self.fix_multispans_in_extern_macros(source_map, span, children); @@ -1361,7 +1358,7 @@ impl EmitterWriter { let mut multilines = FxHashMap::default(); // Get the left-side margin to remove it - let mut whitespace_margin = std::usize::MAX; + let mut whitespace_margin = usize::MAX; for line_idx in 0..annotated_file.lines.len() { let file = annotated_file.file.clone(); let line = &annotated_file.lines[line_idx]; @@ -1373,19 +1370,19 @@ impl EmitterWriter { } } } - if whitespace_margin == std::usize::MAX { + if whitespace_margin == usize::MAX { whitespace_margin = 0; } // Left-most column any visible span points at. - let mut span_left_margin = std::usize::MAX; + 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); span_left_margin = min(span_left_margin, ann.end_col); } } - if span_left_margin == std::usize::MAX { + if span_left_margin == usize::MAX { span_left_margin = 0; } @@ -1421,7 +1418,7 @@ impl EmitterWriter { } else { termize::dimensions() .map(|(w, _)| w.saturating_sub(code_offset)) - .unwrap_or(std::usize::MAX) + .unwrap_or(usize::MAX) }; let margin = Margin::new( @@ -1719,7 +1716,7 @@ impl EmitterWriter { if !self.short_message { for child in children { let span = child.render_span.as_ref().unwrap_or(&child.span); - match self.emit_message_default( + if let Err(err) = self.emit_message_default( &span, &child.styled_message(), &None, @@ -1727,15 +1724,14 @@ impl EmitterWriter { max_line_num_len, true, ) { - Err(e) => panic!("failed to emit error: {}", e), - _ => (), + panic!("failed to emit error: {}", err); } } for sugg in suggestions { if sugg.style == SuggestionStyle::CompletelyHidden { // do not display this suggestion, it is meant only for tools } else if sugg.style == SuggestionStyle::HideCodeAlways { - match self.emit_message_default( + if let Err(e) = self.emit_message_default( &MultiSpan::new(), &[(sugg.msg.to_owned(), Style::HeaderMsg)], &None, @@ -1743,16 +1739,13 @@ impl EmitterWriter { max_line_num_len, true, ) { - Err(e) => panic!("failed to emit error: {}", e), - _ => (), - } - } else { - match self.emit_suggestion_default(sugg, &Level::Help, max_line_num_len) - { - Err(e) => panic!("failed to emit error: {}", e), - _ => (), + panic!("failed to emit error: {}", e); } - } + } else if let Err(e) = + self.emit_suggestion_default(sugg, &Level::Help, max_line_num_len) + { + panic!("failed to emit error: {}", e); + }; } } } @@ -1762,10 +1755,11 @@ impl EmitterWriter { let mut dst = self.dst.writable(); match writeln!(dst) { Err(e) => panic!("failed to emit error: {}", e), - _ => match dst.flush() { - Err(e) => panic!("failed to emit error: {}", e), - _ => (), - }, + _ => { + if let Err(e) = dst.flush() { + panic!("failed to emit error: {}", e) + } + } } } } @@ -2008,7 +2002,7 @@ fn emit_to_destination( let _buffer_lock = lock::acquire_global_lock("rustc_errors"); for (pos, line) in rendered_buffer.iter().enumerate() { for part in line { - dst.apply_style(lvl.clone(), part.style)?; + dst.apply_style(*lvl, part.style)?; write!(dst, "{}", part.text)?; dst.reset()?; } @@ -2149,11 +2143,8 @@ impl<'a> Write for WritableDst<'a> { impl<'a> Drop for WritableDst<'a> { fn drop(&mut self) { - match *self { - WritableDst::Buffered(ref mut dst, ref mut buf) => { - drop(dst.print(buf)); - } - _ => {} + if let WritableDst::Buffered(ref mut dst, ref mut buf) = self { + drop(dst.print(buf)); } } } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index a21314afb1e3b..e4a560e434aaa 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -231,7 +231,10 @@ impl CodeSuggestion { } } if let Some(cur_line) = sf.get_line(cur_lo.line - 1) { - let end = std::cmp::min(cur_line.len(), cur_lo.col.to_usize()); + let end = match cur_line.char_indices().nth(cur_lo.col.to_usize()) { + Some((i, _)) => i, + None => cur_line.len(), + }; buf.push_str(&cur_line[..end]); } } @@ -312,6 +315,9 @@ struct HandlerInner { /// The stashed diagnostics count towards the total error count. /// When `.abort_if_errors()` is called, these are also emitted. stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, + + /// The warning count, used for a recap upon finishing + deduplicated_warn_count: usize, } /// A key denoting where from a diagnostic was stashed. @@ -414,6 +420,7 @@ impl Handler { flags, err_count: 0, deduplicated_err_count: 0, + deduplicated_warn_count: 0, emitter, delayed_span_bugs: Vec::new(), taught_diagnostics: Default::default(), @@ -425,7 +432,7 @@ impl Handler { } // This is here to not allow mutation of flags; - // as of this writing it's only used in tests in librustc. + // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { self.flags.can_emit_warnings } @@ -439,6 +446,7 @@ impl Handler { let mut inner = self.inner.borrow_mut(); inner.err_count = 0; inner.deduplicated_err_count = 0; + inner.deduplicated_warn_count = 0; // actually free the underlying memory (which `clear` would not do) inner.delayed_span_bugs = Default::default(); @@ -745,6 +753,8 @@ impl HandlerInner { self.emitter.emit_diagnostic(diagnostic); if diagnostic.is_error() { self.deduplicated_err_count += 1; + } else if diagnostic.level == Warning { + self.deduplicated_warn_count += 1; } } if diagnostic.is_error() { @@ -763,8 +773,13 @@ impl HandlerInner { fn print_error_count(&mut self, registry: &Registry) { self.emit_stashed_diagnostics(); - let s = match self.deduplicated_err_count { - 0 => return, + let warnings = match self.deduplicated_warn_count { + 0 => String::new(), + 1 => "1 warning emitted".to_string(), + count => format!("{} warnings emitted", count), + }; + let errors = match self.deduplicated_err_count { + 0 => String::new(), 1 => "aborting due to previous error".to_string(), count => format!("aborting due to {} previous errors", count), }; @@ -772,7 +787,16 @@ impl HandlerInner { return; } - let _ = self.fatal(&s); + match (errors.len(), warnings.len()) { + (0, 0) => return, + (0, _) => self.emit_diagnostic(&Diagnostic::new(Level::Warning, &warnings)), + (_, 0) => { + let _ = self.fatal(&errors); + } + (_, _) => { + let _ = self.fatal(&format!("{}; {}", &errors, &warnings)); + } + } let can_show_explain = self.emitter.should_show_explain(); let are_there_diagnostics = !self.emitted_diagnostic_codes.is_empty(); @@ -802,13 +826,13 @@ impl HandlerInner { )); self.failure(&format!( "For more information about an error, try \ - `rustc --explain {}`.", + `rustc --explain {}`.", &error_codes[0] )); } else { self.failure(&format!( "For more information about this error, try \ - `rustc --explain {}`.", + `rustc --explain {}`.", &error_codes[0] )); } @@ -845,7 +869,10 @@ impl HandlerInner { } fn delay_span_bug(&mut self, sp: impl Into, msg: &str) { - if self.treat_err_as_bug() { + // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before + // incrementing `err_count` by one, so we need to +1 the comparing. + // FIXME: Would be nice to increment err_count in a more coherent way. + if self.flags.treat_err_as_bug.map(|c| self.err_count() + 1 >= c).unwrap_or(false) { // FIXME: don't abort here if report_delayed_bugs is off self.span_bug(sp, msg); } diff --git a/src/librustc_errors/registry.rs b/src/librustc_errors/registry.rs index 32700c6500bc8..b1d770d5bd523 100644 --- a/src/librustc_errors/registry.rs +++ b/src/librustc_errors/registry.rs @@ -10,12 +10,12 @@ pub struct Registry { impl Registry { pub fn new(long_descriptions: &[(&'static str, Option<&'static str>)]) -> Registry { - Registry { long_descriptions: long_descriptions.iter().cloned().collect() } + Registry { long_descriptions: long_descriptions.iter().copied().collect() } } /// This will panic if an invalid error code is passed in pub fn find_description(&self, code: &str) -> Option<&'static str> { - self.try_find_description(code).unwrap() + self.long_descriptions[code] } /// Returns `InvalidErrorCode` if the code requested does not exist in the /// registry. Otherwise, returns an `Option` where `None` means the error @@ -24,9 +24,6 @@ impl Registry { &self, code: &str, ) -> Result, InvalidErrorCode> { - if !self.long_descriptions.contains_key(code) { - return Err(InvalidErrorCode); - } - Ok(*self.long_descriptions.get(code).unwrap()) + self.long_descriptions.get(code).copied().ok_or(InvalidErrorCode) } } diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs index 59c1a5468f100..fe5bf6f82c6d3 100644 --- a/src/librustc_expand/base.rs +++ b/src/librustc_expand/base.rs @@ -1,7 +1,7 @@ use crate::expand::{self, AstFragment, Invocation}; use crate::module::DirectoryOwnership; -use rustc_ast::ast::{self, Attribute, Name, NodeId, PatKind}; +use rustc_ast::ast::{self, Attribute, NodeId, PatKind}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token; @@ -796,7 +796,7 @@ impl SyntaxExtension { span: Span, helper_attrs: Vec, edition: Edition, - name: Name, + name: Symbol, attrs: &[ast::Attribute], ) -> SyntaxExtension { let allow_internal_unstable = attr::allow_internal_unstable(&attrs, &sess.span_diagnostic) @@ -885,7 +885,7 @@ pub trait Resolver { fn resolve_dollar_crates(&mut self); fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment); - fn register_builtin_macro(&mut self, ident: ast::Ident, ext: SyntaxExtension); + fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension); fn expansion_for_ast_pass( &mut self, @@ -913,7 +913,7 @@ pub trait Resolver { #[derive(Clone)] pub struct ModuleData { - pub mod_path: Vec, + pub mod_path: Vec, pub directory: PathBuf, } @@ -1052,16 +1052,16 @@ impl<'a> ExtCtxt<'a> { pub fn set_trace_macros(&mut self, x: bool) { self.ecfg.trace_mac = x } - pub fn ident_of(&self, st: &str, sp: Span) -> ast::Ident { - ast::Ident::from_str_and_span(st, sp) + pub fn ident_of(&self, st: &str, sp: Span) -> Ident { + Ident::from_str_and_span(st, sp) } - pub fn std_path(&self, components: &[Symbol]) -> Vec { + pub fn std_path(&self, components: &[Symbol]) -> Vec { let def_site = self.with_def_site_ctxt(DUMMY_SP); iter::once(Ident::new(kw::DollarCrate, def_site)) .chain(components.iter().map(|&s| Ident::with_dummy_span(s))) .collect() } - pub fn name_of(&self, st: &str) -> ast::Name { + pub fn name_of(&self, st: &str) -> Symbol { Symbol::intern(st) } diff --git a/src/librustc_expand/build.rs b/src/librustc_expand/build.rs index 48ceaf5fccd8e..be2c52a85eb2a 100644 --- a/src/librustc_expand/build.rs +++ b/src/librustc_expand/build.rs @@ -1,28 +1,28 @@ use crate::base::ExtCtxt; -use rustc_ast::ast::{self, AttrVec, BlockCheckMode, Expr, Ident, PatKind, UnOp}; +use rustc_ast::ast::{self, AttrVec, BlockCheckMode, Expr, PatKind, UnOp}; use rustc_ast::attr; use rustc_ast::ptr::P; use rustc_span::source_map::{respan, Spanned}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; impl<'a> ExtCtxt<'a> { - pub fn path(&self, span: Span, strs: Vec) -> ast::Path { + pub fn path(&self, span: Span, strs: Vec) -> ast::Path { self.path_all(span, false, strs, vec![]) } - pub fn path_ident(&self, span: Span, id: ast::Ident) -> ast::Path { + pub fn path_ident(&self, span: Span, id: Ident) -> ast::Path { self.path(span, vec![id]) } - pub fn path_global(&self, span: Span, strs: Vec) -> ast::Path { + pub fn path_global(&self, span: Span, strs: Vec) -> ast::Path { self.path_all(span, true, strs, vec![]) } pub fn path_all( &self, span: Span, global: bool, - mut idents: Vec, + mut idents: Vec, args: Vec, ) -> ast::Path { assert!(!idents.is_empty()); @@ -36,7 +36,8 @@ impl<'a> ExtCtxt<'a> { idents.into_iter().map(|ident| ast::PathSegment::from_ident(ident.with_span_pos(span))), ); let args = if !args.is_empty() { - ast::AngleBracketedArgs { args, constraints: Vec::new(), span }.into() + let args = args.into_iter().map(ast::AngleBracketedArg::Arg).collect(); + ast::AngleBracketedArgs { args, span }.into() } else { None }; @@ -62,7 +63,7 @@ impl<'a> ExtCtxt<'a> { // Might need to take bounds as an argument in the future, if you ever want // to generate a bounded existential trait type. - pub fn ty_ident(&self, span: Span, ident: ast::Ident) -> P { + pub fn ty_ident(&self, span: Span, ident: Ident) -> P { self.ty_path(self.path_ident(span, ident)) } @@ -73,7 +74,7 @@ impl<'a> ExtCtxt<'a> { } } - pub fn const_ident(&self, span: Span, ident: ast::Ident) -> ast::AnonConst { + pub fn const_ident(&self, span: Span, ident: Ident) -> ast::AnonConst { self.anon_const(span, ast::ExprKind::Path(None, self.path_ident(span, ident))) } @@ -94,7 +95,7 @@ impl<'a> ExtCtxt<'a> { pub fn typaram( &self, span: Span, - ident: ast::Ident, + ident: Ident, attrs: Vec, bounds: ast::GenericBounds, default: Option>, @@ -128,14 +129,14 @@ impl<'a> ExtCtxt<'a> { ) } - pub fn lifetime(&self, span: Span, ident: ast::Ident) -> ast::Lifetime { + pub fn lifetime(&self, span: Span, ident: Ident) -> ast::Lifetime { ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) } } pub fn lifetime_def( &self, span: Span, - ident: ast::Ident, + ident: Ident, attrs: Vec, bounds: ast::GenericBounds, ) -> ast::GenericParam { @@ -154,13 +155,7 @@ impl<'a> ExtCtxt<'a> { ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) } } - pub fn stmt_let( - &self, - sp: Span, - mutbl: bool, - ident: ast::Ident, - ex: P, - ) -> ast::Stmt { + pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P) -> ast::Stmt { let pat = if mutbl { let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut); self.pat_ident_binding_mode(sp, ident, binding_mode) @@ -217,7 +212,7 @@ impl<'a> ExtCtxt<'a> { self.expr(path.span, ast::ExprKind::Path(None, path)) } - pub fn expr_ident(&self, span: Span, id: ast::Ident) -> P { + pub fn expr_ident(&self, span: Span, id: Ident) -> P { self.expr_path(self.path_ident(span, id)) } pub fn expr_self(&self, span: Span) -> P { @@ -250,18 +245,13 @@ impl<'a> ExtCtxt<'a> { ) -> P { self.expr(span, ast::ExprKind::Call(expr, args)) } - pub fn expr_call_ident( - &self, - span: Span, - id: ast::Ident, - args: Vec>, - ) -> P { + pub fn expr_call_ident(&self, span: Span, id: Ident, args: Vec>) -> P { self.expr(span, ast::ExprKind::Call(self.expr_ident(span, id), args)) } pub fn expr_call_global( &self, sp: Span, - fn_path: Vec, + fn_path: Vec, args: Vec>, ) -> P { let pathexpr = self.expr_path(self.path_global(sp, fn_path)); @@ -271,7 +261,7 @@ impl<'a> ExtCtxt<'a> { &self, span: Span, expr: P, - ident: ast::Ident, + ident: Ident, mut args: Vec>, ) -> P { args.insert(0, expr); @@ -303,7 +293,7 @@ impl<'a> ExtCtxt<'a> { pub fn expr_struct_ident( &self, span: Span, - id: ast::Ident, + id: Ident, fields: Vec, ) -> P { self.expr_struct(span, self.path_ident(span, id), fields) @@ -404,7 +394,7 @@ impl<'a> ExtCtxt<'a> { pub fn pat_lit(&self, span: Span, expr: P) -> P { self.pat(span, PatKind::Lit(expr)) } - pub fn pat_ident(&self, span: Span, ident: ast::Ident) -> P { + pub fn pat_ident(&self, span: Span, ident: Ident) -> P { let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Not); self.pat_ident_binding_mode(span, ident, binding_mode) } @@ -412,7 +402,7 @@ impl<'a> ExtCtxt<'a> { pub fn pat_ident_binding_mode( &self, span: Span, - ident: ast::Ident, + ident: Ident, bm: ast::BindingMode, ) -> P { let pat = PatKind::Ident(bm, ident.with_span_pos(span), None); @@ -516,7 +506,7 @@ impl<'a> ExtCtxt<'a> { ) } - pub fn lambda(&self, span: Span, ids: Vec, body: P) -> P { + pub fn lambda(&self, span: Span, ids: Vec, body: P) -> P { let fn_decl = self.fn_decl( ids.iter().map(|id| self.param(span, *id, self.ty(span, ast::TyKind::Infer))).collect(), ast::FnRetTy::Default(span), @@ -543,20 +533,15 @@ impl<'a> ExtCtxt<'a> { self.lambda(span, Vec::new(), body) } - pub fn lambda1(&self, span: Span, body: P, ident: ast::Ident) -> P { + pub fn lambda1(&self, span: Span, body: P, ident: Ident) -> P { self.lambda(span, vec![ident], body) } - pub fn lambda_stmts_1( - &self, - span: Span, - stmts: Vec, - ident: ast::Ident, - ) -> P { + pub fn lambda_stmts_1(&self, span: Span, stmts: Vec, ident: Ident) -> P { self.lambda1(span, self.expr_block(self.block(span, stmts)), ident) } - pub fn param(&self, span: Span, ident: ast::Ident, ty: P) -> ast::Param { + pub fn param(&self, span: Span, ident: Ident, ty: P) -> ast::Param { let arg_pat = self.pat_ident(span, ident); ast::Param { attrs: AttrVec::default(), @@ -652,7 +637,7 @@ impl<'a> ExtCtxt<'a> { attr::mk_attr_outer(mi) } - pub fn meta_word(&self, sp: Span, w: ast::Name) -> ast::MetaItem { + pub fn meta_word(&self, sp: Span, w: Symbol) -> ast::MetaItem { attr::mk_word_item(Ident::new(w, sp)) } } diff --git a/src/librustc_expand/config.rs b/src/librustc_expand/config.rs index 72c09f35dfa55..d79dabb509267 100644 --- a/src/librustc_expand/config.rs +++ b/src/librustc_expand/config.rs @@ -4,9 +4,9 @@ use rustc_ast::ast::{self, AttrItem, Attribute, MetaItem}; use rustc_ast::attr::HasAttrs; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::util::map_in_place::MapInPlace; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::map_in_place::MapInPlace; use rustc_errors::{error_code, struct_span_err, Applicability, Handler}; use rustc_feature::{Feature, Features, State as FeatureState}; use rustc_feature::{ diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs index 51208906c2f2d..485c5147d2c26 100644 --- a/src/librustc_expand/expand.rs +++ b/src/librustc_expand/expand.rs @@ -7,16 +7,16 @@ use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwne use crate::placeholders::{placeholder, PlaceholderExpander}; use crate::proc_macro::collect_derives; -use rustc_ast::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path}; +use rustc_ast::ast::{self, AttrItem, Block, LitKind, NodeId, PatKind, Path}; use rustc_ast::ast::{ItemKind, MacArgs, MacStmtStyle, StmtKind}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::util::map_in_place::MapInPlace; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; +use rustc_data_structures::map_in_place::MapInPlace; use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; use rustc_parse::parser::Parser; @@ -25,7 +25,7 @@ use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::{feature_err, ParseSess}; use rustc_span::source_map::respan; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{FileName, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; @@ -507,9 +507,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { expanded_fragments.push(Vec::new()); } expanded_fragments[depth - 1].push((expn_id, expanded_fragment)); - if !self.cx.ecfg.single_step { - invocations.extend(new_invocations.into_iter().rev()); - } + invocations.extend(new_invocations.into_iter().rev()); } self.cx.current_expansion = orig_expansion_data; @@ -681,7 +679,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ExpandResult::Ready(match invoc.kind { InvocationKind::Bang { mac, .. } => match ext { SyntaxExtensionKind::Bang(expander) => { - self.gate_proc_macro_expansion_kind(span, fragment_kind); let tok_result = match expander.expand(self.cx, span, mac.args.inner_tokens()) { Err(_) => return ExpandResult::Ready(fragment_kind.dummy(span)), Ok(ts) => ts, @@ -848,36 +845,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } - fn gate_proc_macro_expansion_kind(&self, span: Span, kind: AstFragmentKind) { - let kind = match kind { - AstFragmentKind::Expr | AstFragmentKind::OptExpr => "expressions", - AstFragmentKind::Pat => "patterns", - AstFragmentKind::Stmts => "statements", - AstFragmentKind::Ty - | AstFragmentKind::Items - | AstFragmentKind::TraitItems - | AstFragmentKind::ImplItems - | AstFragmentKind::ForeignItems => return, - AstFragmentKind::Arms - | AstFragmentKind::Fields - | AstFragmentKind::FieldPats - | AstFragmentKind::GenericParams - | AstFragmentKind::Params - | AstFragmentKind::StructFields - | AstFragmentKind::Variants => panic!("unexpected AST fragment kind"), - }; - if self.cx.ecfg.proc_macro_hygiene() { - return; - } - feature_err( - self.cx.parse_sess, - sym::proc_macro_hygiene, - span, - &format!("procedural macros cannot be expanded to {}", kind), - ) - .emit(); - } - fn parse_ast_fragment( &mut self, toks: TokenStream, @@ -1174,10 +1141,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { // ignore derives so they remain unused let (attr, after_derive) = self.classify_nonitem(&mut expr); - if attr.is_some() { + if let Some(ref attr_value) = attr { // Collect the invoc regardless of whether or not attributes are permitted here // expansion will eat the attribute so it won't error later. - attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); + self.cfg.maybe_emit_expr_attr_err(attr_value); // AstFragmentKind::Expr requires the macro to emit an expression. return self @@ -1324,8 +1291,8 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { // Ignore derives so they remain unused. let (attr, after_derive) = self.classify_nonitem(&mut expr); - if attr.is_some() { - attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); + if let Some(ref attr_value) = attr { + self.cfg.maybe_emit_expr_attr_err(attr_value); return self .collect_attr( @@ -1819,7 +1786,6 @@ pub struct ExpansionConfig<'feat> { pub recursion_limit: usize, pub trace_mac: bool, pub should_test: bool, // If false, strip `#[test]` nodes - pub single_step: bool, pub keep_macs: bool, } @@ -1831,7 +1797,6 @@ impl<'feat> ExpansionConfig<'feat> { recursion_limit: 1024, trace_mac: false, should_test: false, - single_step: false, keep_macs: false, } } diff --git a/src/librustc_expand/lib.rs b/src/librustc_expand/lib.rs index 876a26de3fb7e..1e3ab2c5ec2db 100644 --- a/src/librustc_expand/lib.rs +++ b/src/librustc_expand/lib.rs @@ -2,6 +2,7 @@ #![feature(cow_is_borrowed)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(or_patterns)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] #![feature(proc_macro_span)] diff --git a/src/librustc_expand/mbe.rs b/src/librustc_expand/mbe.rs index 57957a1fa8bf9..a728261d711a7 100644 --- a/src/librustc_expand/mbe.rs +++ b/src/librustc_expand/mbe.rs @@ -9,10 +9,10 @@ crate mod macro_rules; crate mod quoted; crate mod transcribe; -use rustc_ast::ast; use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::DelimSpan; +use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_data_structures::sync::Lrc; @@ -82,13 +82,9 @@ enum TokenTree { /// A kleene-style repetition sequence Sequence(DelimSpan, Lrc), /// e.g., `$var` - MetaVar(Span, ast::Ident), + MetaVar(Span, Ident), /// e.g., `$var:expr`. This is only used in the left hand side of MBE macros. - MetaVarDecl( - Span, - ast::Ident, /* name to bind */ - ast::Ident, /* kind of nonterminal */ - ), + MetaVarDecl(Span, Ident /* name to bind */, Ident /* kind of nonterminal */), } impl TokenTree { diff --git a/src/librustc_expand/mbe/macro_parser.rs b/src/librustc_expand/mbe/macro_parser.rs index e868b7e36aac5..0cf092d912bcf 100644 --- a/src/librustc_expand/mbe/macro_parser.rs +++ b/src/librustc_expand/mbe/macro_parser.rs @@ -76,7 +76,6 @@ use TokenTreeOrTokenTreeSlice::*; use crate::mbe::{self, TokenTree}; -use rustc_ast::ast::Name; use rustc_ast::ptr::P; use rustc_ast::token::{self, DocComment, Nonterminal, Token}; use rustc_ast_pretty::pprust; @@ -588,8 +587,10 @@ fn inner_parse_loop<'root, 'tt>( // // At the beginning of the loop, if we reach the end of the delimited submatcher, // we pop the stack to backtrack out of the descent. - seq @ TokenTree::Delimited(..) - | seq @ TokenTree::Token(Token { kind: DocComment(..), .. }) => { + seq + @ + (TokenTree::Delimited(..) + | TokenTree::Token(Token { kind: DocComment(..), .. })) => { let lower_elts = mem::replace(&mut item.top_elts, Tt(seq)); let idx = item.idx; item.stack.push(MatcherTtFrame { elts: lower_elts, idx }); @@ -764,11 +765,11 @@ fn get_macro_ident(token: &Token) -> Option<(Ident, bool)> { /// /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that /// token. Be conservative (return true) if not sure. -fn may_begin_with(token: &Token, name: Name) -> bool { +fn may_begin_with(token: &Token, name: Symbol) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. fn may_be_ident(nt: &token::Nonterminal) -> bool { match *nt { - token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) => false, + token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_) => false, _ => true, } } diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs index 859362b5e29d3..ecadf320f87c4 100644 --- a/src/librustc_expand/mbe/macro_rules.rs +++ b/src/librustc_expand/mbe/macro_rules.rs @@ -21,7 +21,7 @@ use rustc_parse::parser::Parser; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{kw, sym, MacroRulesNormalizedIdent, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent, Symbol}; use rustc_span::Span; use log::debug; @@ -39,7 +39,7 @@ crate struct ParserAnyMacro<'a> { /// Span of the expansion site of the macro this parser is for site_span: Span, /// The ident of the macro we're parsing - macro_ident: ast::Ident, + macro_ident: Ident, arm_span: Span, } @@ -86,8 +86,9 @@ fn suggest_slice_pat(e: &mut DiagnosticBuilder<'_>, site_span: Span, parser: &Pa fn emit_frag_parse_err( mut e: DiagnosticBuilder<'_>, parser: &Parser<'_>, + orig_parser: &mut Parser<'_>, site_span: Span, - macro_ident: ast::Ident, + macro_ident: Ident, arm_span: Span, kind: AstFragmentKind, ) { @@ -118,6 +119,21 @@ fn emit_frag_parse_err( AstFragmentKind::Pat if macro_ident.name == sym::vec => { suggest_slice_pat(&mut e, site_span, parser); } + // Try a statement if an expression is wanted but failed and suggest adding `;` to call. + AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) { + Err(mut err) => err.cancel(), + Ok(_) => { + e.note( + "the macro call doesn't expand to an expression, but it can expand to a statement", + ); + e.span_suggestion_verbose( + site_span.shrink_to_hi(), + "add `;` to interpret the expansion as a statement", + ";".to_string(), + Applicability::MaybeIncorrect, + ); + } + }, _ => annotate_err_with_kind(&mut e, kind, site_span), }; e.emit(); @@ -126,10 +142,11 @@ fn emit_frag_parse_err( impl<'a> ParserAnyMacro<'a> { crate fn make(mut self: Box>, kind: AstFragmentKind) -> AstFragment { let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self; + let snapshot = &mut parser.clone(); let fragment = match parse_ast_fragment(parser, kind) { Ok(f) => f, Err(err) => { - emit_frag_parse_err(err, parser, site_span, macro_ident, arm_span, kind); + emit_frag_parse_err(err, parser, snapshot, site_span, macro_ident, arm_span, kind); return kind.dummy(site_span); } }; @@ -149,7 +166,7 @@ impl<'a> ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { - name: ast::Ident, + name: Ident, span: Span, transparency: Transparency, lhses: Vec, @@ -198,7 +215,7 @@ fn generic_extension<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, def_span: Span, - name: ast::Ident, + name: Ident, transparency: Transparency, arg: TokenStream, lhses: &[mbe::TokenTree], @@ -326,7 +343,7 @@ fn generic_extension<'cx>( let mut err = cx.struct_span_err(span, &parse_failure_msg(&token)); err.span_label(span, label); if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { - err.span_label(cx.source_map().def_span(def_span), "when calling this macro"); + err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); } // Check whether there's a missing comma in this macro call, like `println!("{}" a);` @@ -337,20 +354,19 @@ fn generic_extension<'cx>( mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], _ => continue, }; - match parse_tt(&mut Cow::Borrowed(&parser_from_cx(sess, arg.clone())), lhs_tt) { - Success(_) => { - if comma_span.is_dummy() { - err.note("you might be missing a comma"); - } else { - err.span_suggestion_short( - comma_span, - "missing comma here", - ", ".to_string(), - Applicability::MachineApplicable, - ); - } + if let Success(_) = + parse_tt(&mut Cow::Borrowed(&parser_from_cx(sess, arg.clone())), lhs_tt) + { + if comma_span.is_dummy() { + err.note("you might be missing a comma"); + } else { + err.span_suggestion_short( + comma_span, + "missing comma here", + ", ".to_string(), + Applicability::MachineApplicable, + ); } - _ => {} } } } @@ -384,9 +400,9 @@ pub fn compile_declarative_macro( }; let diag = &sess.span_diagnostic; - let lhs_nm = ast::Ident::new(sym::lhs, def.span); - let rhs_nm = ast::Ident::new(sym::rhs, def.span); - let tt_spec = ast::Ident::new(sym::tt, def.span); + let lhs_nm = Ident::new(sym::lhs, def.span); + let rhs_nm = Ident::new(sym::rhs, def.span); + let tt_spec = Ident::new(sym::tt, def.span); // Parse the macro_rules! invocation let (macro_rules, body) = match &def.kind { diff --git a/src/librustc_expand/mbe/quoted.rs b/src/librustc_expand/mbe/quoted.rs index b19fae6559704..3295f5b392d6d 100644 --- a/src/librustc_expand/mbe/quoted.rs +++ b/src/librustc_expand/mbe/quoted.rs @@ -1,12 +1,11 @@ use crate::mbe::macro_parser; use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree}; -use rustc_ast::ast; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; use rustc_ast_pretty::pprust; use rustc_session::parse::ParseSess; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; @@ -67,7 +66,7 @@ pub(super) fn parse( tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp), }; sess.missing_fragment_specifiers.borrow_mut().insert(span); - result.push(TokenTree::MetaVarDecl(span, ident, ast::Ident::invalid())); + result.push(TokenTree::MetaVarDecl(span, ident, Ident::invalid())); } // Not a metavar or no matchers allowed, so just return the tree @@ -145,7 +144,7 @@ fn parse_tree( let msg = format!("expected identifier, found `{}`", pprust::token_to_string(&token),); sess.span_diagnostic.span_err(token.span, &msg); - TokenTree::MetaVar(token.span, ast::Ident::invalid()) + TokenTree::MetaVar(token.span, Ident::invalid()) } // There are no more tokens. Just return the `$` we already have. diff --git a/src/librustc_expand/module.rs b/src/librustc_expand/module.rs index aad92a09743b3..9bb2b57b7f591 100644 --- a/src/librustc_expand/module.rs +++ b/src/librustc_expand/module.rs @@ -1,10 +1,10 @@ -use rustc_ast::ast::{self, Attribute, Ident, Mod}; +use rustc_ast::ast::{Attribute, Mod}; use rustc_ast::{attr, token}; use rustc_errors::{struct_span_err, PResult}; use rustc_parse::new_parser_from_file; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FileName, Span}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident}; use std::path::{self, Path, PathBuf}; @@ -18,7 +18,7 @@ pub struct Directory { pub enum DirectoryOwnership { Owned { // None if `mod.rs`, `Some("foo")` if we're in `foo.rs`. - relative: Option, + relative: Option, }, UnownedViaBlock, UnownedViaMod, @@ -40,7 +40,7 @@ pub struct ModulePathSuccess { crate fn parse_external_mod( sess: &ParseSess, - id: ast::Ident, + id: Ident, span: Span, // The span to blame on errors. Directory { mut ownership, path }: Directory, attrs: &mut Vec, @@ -125,7 +125,7 @@ crate fn push_directory( fn submod_path<'a>( sess: &'a ParseSess, - id: ast::Ident, + id: Ident, span: Span, attrs: &[Attribute], ownership: DirectoryOwnership, @@ -236,9 +236,9 @@ pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option( sess: &'a ParseSess, - id: ast::Ident, + id: Ident, span: Span, - relative: Option, + relative: Option, dir_path: &Path, ) -> ModulePath<'a> { // If we're in a foo.rs file instead of a mod.rs file, diff --git a/src/librustc_expand/mut_visit/tests.rs b/src/librustc_expand/mut_visit/tests.rs index 70fb8975d4d08..48da1a3ccc420 100644 --- a/src/librustc_expand/mut_visit/tests.rs +++ b/src/librustc_expand/mut_visit/tests.rs @@ -1,9 +1,10 @@ use crate::tests::{matches_codepattern, string_to_crate}; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::with_default_globals; use rustc_ast_pretty::pprust; +use rustc_span::symbol::Ident; // This version doesn't care about getting comments or doc-strings in. fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) { @@ -14,7 +15,7 @@ fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) { struct ToZzIdentMutVisitor; impl MutVisitor for ToZzIdentMutVisitor { - fn visit_ident(&mut self, ident: &mut ast::Ident) { + fn visit_ident(&mut self, ident: &mut Ident) { *ident = Ident::from_str("zz"); } fn visit_mac(&mut self, mac: &mut ast::MacCall) { diff --git a/src/librustc_expand/parse/lexer/tests.rs b/src/librustc_expand/parse/lexer/tests.rs index 2cb6267e0f6aa..2932475430bbf 100644 --- a/src/librustc_expand/parse/lexer/tests.rs +++ b/src/librustc_expand/parse/lexer/tests.rs @@ -50,13 +50,13 @@ fn t1() { assert_eq!(string_reader.next_token(), token::Whitespace); // Read another token. let tok3 = string_reader.next_token(); - assert_eq!(string_reader.pos.clone(), BytePos(28)); + assert_eq!(string_reader.pos(), BytePos(28)); let tok4 = Token::new(mk_ident("main"), Span::with_root_ctxt(BytePos(24), BytePos(28))); assert_eq!(tok3.kind, tok4.kind); assert_eq!(tok3.span, tok4.span); assert_eq!(string_reader.next_token(), token::OpenDelim(token::Paren)); - assert_eq!(string_reader.pos.clone(), BytePos(29)) + assert_eq!(string_reader.pos(), BytePos(29)) }) } diff --git a/src/librustc_expand/parse/tests.rs b/src/librustc_expand/parse/tests.rs index 4add896258fa8..437f6e62d7d33 100644 --- a/src/librustc_expand/parse/tests.rs +++ b/src/librustc_expand/parse/tests.rs @@ -1,6 +1,6 @@ use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse}; -use rustc_ast::ast::{self, Name, PatKind}; +use rustc_ast::ast::{self, PatKind}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; @@ -100,12 +100,12 @@ fn string_to_tts_1() { let expected = TokenStream::new(vec![ TokenTree::token(token::Ident(kw::Fn, false), sp(0, 2)).into(), - TokenTree::token(token::Ident(Name::intern("a"), false), sp(3, 4)).into(), + TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(3, 4)).into(), TokenTree::Delimited( DelimSpan::from_pair(sp(5, 6), sp(13, 14)), token::DelimToken::Paren, TokenStream::new(vec![ - TokenTree::token(token::Ident(Name::intern("b"), false), sp(6, 7)).into(), + TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(6, 7)).into(), TokenTree::token(token::Colon, sp(8, 9)).into(), TokenTree::token(token::Ident(sym::i32, false), sp(10, 13)).into(), ]) @@ -116,7 +116,7 @@ fn string_to_tts_1() { DelimSpan::from_pair(sp(15, 16), sp(20, 21)), token::DelimToken::Brace, TokenStream::new(vec![ - TokenTree::token(token::Ident(Name::intern("b"), false), sp(17, 18)).into(), + TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(17, 18)).into(), TokenTree::token(token::Semi, sp(18, 19)).into(), ]) .into(), diff --git a/src/librustc_expand/placeholders.rs b/src/librustc_expand/placeholders.rs index e1781f8636e58..23f7a5b28fe80 100644 --- a/src/librustc_expand/placeholders.rs +++ b/src/librustc_expand/placeholders.rs @@ -5,6 +5,7 @@ use rustc_ast::ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_span::source_map::{dummy_spanned, DUMMY_SP}; +use rustc_span::symbol::Ident; use smallvec::{smallvec, SmallVec}; @@ -23,7 +24,7 @@ pub fn placeholder( } } - let ident = ast::Ident::invalid(); + let ident = Ident::invalid(); let attrs = Vec::new(); let vis = vis.unwrap_or_else(|| dummy_spanned(ast::VisibilityKind::Inherited)); let span = DUMMY_SP; diff --git a/src/librustc_expand/proc_macro_server.rs b/src/librustc_expand/proc_macro_server.rs index 5f21ff503d59e..b9693c2c86278 100644 --- a/src/librustc_expand/proc_macro_server.rs +++ b/src/librustc_expand/proc_macro_server.rs @@ -10,7 +10,7 @@ use rustc_errors::Diagnostic; use rustc_parse::lexer::nfc_normalize; use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{self, kw, sym, Symbol}; use rustc_span::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span}; use pm::bridge::{server, TokenTree}; @@ -141,10 +141,10 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> SingleQuote => op!('\''), Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), - Ident(name, is_raw) => tt!(Ident::new(name, is_raw)), + Ident(name, is_raw) => tt!(Ident::new(sess, name, is_raw)), Lifetime(name) => { - let ident = ast::Ident::new(name, span).without_first_quote(); - stack.push(tt!(Ident::new(ident.name, false))); + let ident = symbol::Ident::new(name, span).without_first_quote(); + stack.push(tt!(Ident::new(sess, ident.name, false))); tt!(Punct::new('\'', true)) } Literal(lit) => tt!(Literal { lit }), @@ -322,7 +322,7 @@ impl Ident { false } } - fn new(sym: Symbol, is_raw: bool, span: Span) -> Ident { + fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident { let sym = nfc_normalize(&sym.as_str()); let string = sym.as_str(); if !Self::is_valid(&string) { @@ -331,6 +331,7 @@ impl Ident { if is_raw && !sym.can_be_raw() { panic!("`{}` cannot be a raw identifier", string); } + sess.symbol_gallery.insert(sym, span); Ident { sym, is_raw, span } } fn dollar_crate(span: Span) -> Ident { @@ -495,7 +496,7 @@ impl server::Punct for Rustc<'_> { impl server::Ident for Rustc<'_> { fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - Ident::new(Symbol::intern(string), is_raw, span) + Ident::new(self.sess, Symbol::intern(string), is_raw, span) } fn span(&mut self, ident: Self::Ident) -> Self::Span { ident.span @@ -506,9 +507,14 @@ impl server::Ident for Rustc<'_> { } impl server::Literal for Rustc<'_> { - // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. - fn debug(&mut self, literal: &Self::Literal) -> String { - format!("{:?}", literal) + fn debug_kind(&mut self, literal: &Self::Literal) -> String { + format!("{:?}", literal.lit.kind) + } + fn symbol(&mut self, literal: &Self::Literal) -> String { + literal.lit.symbol.to_string() + } + fn suffix(&mut self, literal: &Self::Literal) -> Option { + literal.lit.suffix.as_ref().map(Symbol::to_string) } fn integer(&mut self, n: &str) -> Self::Literal { self.lit(token::Integer, Symbol::intern(n), None) diff --git a/src/librustc_expand/tokenstream/tests.rs b/src/librustc_expand/tokenstream/tests.rs index db329f22d146f..caaa08df49981 100644 --- a/src/librustc_expand/tokenstream/tests.rs +++ b/src/librustc_expand/tokenstream/tests.rs @@ -1,10 +1,9 @@ use crate::tests::string_to_stream; -use rustc_ast::ast::Name; use rustc_ast::token; use rustc_ast::tokenstream::{TokenStream, TokenStreamBuilder, TokenTree}; use rustc_ast::with_default_globals; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol}; use smallvec::smallvec; fn string_to_ts(string: &str) -> TokenStream { @@ -87,7 +86,7 @@ fn test_is_empty() { with_default_globals(|| { let test0: TokenStream = Vec::::new().into_iter().collect(); let test1: TokenStream = - TokenTree::token(token::Ident(Name::intern("a"), false), sp(0, 1)).into(); + TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(0, 1)).into(); let test2 = string_to_ts("foo(bar::baz)"); assert_eq!(test0.is_empty(), true); diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 9f1fee8fc09d8..90b2380d86450 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -173,6 +173,8 @@ declare_features! ( // no-tracking-issue-end /// Allows using `#[structural_match]` which indicates that a type is structurally matchable. + /// FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library + /// feature with the same name exists. (active, structural_match, "1.8.0", Some(31434), None), /// Allows using the `may_dangle` attribute (RFC 1327). @@ -236,6 +238,7 @@ declare_features! ( (active, movbe_target_feature, "1.34.0", Some(44839), None), (active, rtm_target_feature, "1.35.0", Some(44839), None), (active, f16c_target_feature, "1.36.0", Some(44839), None), + (active, riscv_target_feature, "1.45.0", Some(44839), None), // ------------------------------------------------------------------------- // feature-group-end: actual feature gates (target features) @@ -428,8 +431,7 @@ declare_features! ( /// Allows `#[marker]` on certain traits allowing overlapping implementations. (active, marker_trait_attr, "1.30.0", Some(29864), None), - /// Allows macro invocations on modules expressions and statements and - /// procedural macros to expand to non-items. + /// Allows macro attributes on expressions, statements and non-inline modules. (active, proc_macro_hygiene, "1.30.0", Some(54727), None), /// Allows unsized rvalues at arguments and parameters. @@ -554,6 +556,21 @@ declare_features! ( // Allows limiting the evaluation steps of const expressions (active, const_eval_limit, "1.43.0", Some(67217), None), + /// Allow negative trait implementations. + (active, negative_impls, "1.44.0", Some(68318), None), + + /// Allows the use of `#[target_feature]` on safe functions. + (active, target_feature_11, "1.45.0", Some(69098), None), + + /// Allow conditional compilation depending on rust version + (active, cfg_version, "1.45.0", Some(64796), None), + + /// Allows the use of `#[ffi_pure]` on foreign functions. + (active, ffi_pure, "1.45.0", Some(58329), None), + + /// Allows the use of `#[ffi_const]` on foreign functions. + (active, ffi_const, "1.45.0", Some(58328), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs index eaebdd9fb95a2..44971a98cc32f 100644 --- a/src/librustc_feature/builtin_attrs.rs +++ b/src/librustc_feature/builtin_attrs.rs @@ -26,6 +26,7 @@ const GATED_CFGS: &[GatedCfg] = &[ (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)), + (sym::version, sym::cfg_version, cfg_fn!(cfg_version)), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. @@ -220,7 +221,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ABI, linking, symbols, and FFI ungated!( link, Whitelisted, - template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ cfg = "...""#), + template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), ), ungated!(link_name, Whitelisted, template!(NameValueStr: "name")), ungated!(no_link, Normal, template!(Word)), @@ -330,6 +331,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!(ffi_returns_twice, Whitelisted, template!(Word), experimental!(ffi_returns_twice)), + gated!(ffi_pure, Whitelisted, template!(Word), experimental!(ffi_pure)), + gated!(ffi_const, Whitelisted, template!(Word), experimental!(ffi_const)), gated!(track_caller, Whitelisted, template!(Word), experimental!(track_caller)), gated!( register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), @@ -376,11 +379,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== gated!(fundamental, Whitelisted, template!(Word), experimental!(fundamental)), - gated!( - // RFC #1445. - structural_match, Whitelisted, template!(Word), - "the semantics of constant patterns is not yet settled", - ), gated!( may_dangle, Normal, template!(Word), dropck_eyepatch, "`may_dangle` has unstable semantics and may be removed in the future", diff --git a/src/librustc_hir/Cargo.toml b/src/librustc_hir/Cargo.toml index b3682ea5a807c..811440fdeb987 100644 --- a/src/librustc_hir/Cargo.toml +++ b/src/librustc_hir/Cargo.toml @@ -10,13 +10,11 @@ path = "lib.rs" doctest = false [dependencies] -rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_target = { path = "../librustc_target" } rustc_macros = { path = "../librustc_macros" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_index = { path = "../librustc_index" } rustc_span = { path = "../librustc_span" } -rustc_errors = { path = "../librustc_errors" } rustc_serialize = { path = "../libserialize", package = "serialize" } rustc_ast = { path = "../librustc_ast" } lazy_static = "1" diff --git a/src/librustc_hir/arena.rs b/src/librustc_hir/arena.rs index 978565a4c39d7..6ba396666070a 100644 --- a/src/librustc_hir/arena.rs +++ b/src/librustc_hir/arena.rs @@ -5,8 +5,8 @@ /// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is /// faster and more memory efficient if there is lots of allocations. /// -/// Specifying the `decode` modifier will add decode impls for &T and &[T] where T is the type -/// listed. These impls will appear in the implement_ty_decoder! macro. +/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]`, +/// where `T` is the type listed. These impls will appear in the implement_ty_decoder! macro. #[macro_export] macro_rules! arena_types { ($macro:path, $args:tt, $tcx:lifetime) => ( @@ -14,6 +14,8 @@ macro_rules! arena_types { // HIR types [few] hir_krate: rustc_hir::Crate<$tcx>, [] arm: rustc_hir::Arm<$tcx>, + [] asm_operand: rustc_hir::InlineAsmOperand<$tcx>, + [] asm_template: rustc_ast::ast::InlineAsmTemplatePiece, [] attribute: rustc_ast::ast::Attribute, [] block: rustc_hir::Block<$tcx>, [] bare_fn_ty: rustc_hir::BareFnTy<$tcx>, @@ -28,7 +30,8 @@ macro_rules! arena_types { [] fn_decl: rustc_hir::FnDecl<$tcx>, [] foreign_item: rustc_hir::ForeignItem<$tcx>, [] impl_item_ref: rustc_hir::ImplItemRef<$tcx>, - [] inline_asm: rustc_hir::InlineAsm<$tcx>, + [few] inline_asm: rustc_hir::InlineAsm<$tcx>, + [few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>, [] local: rustc_hir::Local<$tcx>, [few] macro_def: rustc_hir::MacroDef<$tcx>, [] param: rustc_hir::Param<$tcx>, diff --git a/src/librustc_hir/def.rs b/src/librustc_hir/def.rs index 3334cc32a52f7..88049f85f45e4 100644 --- a/src/librustc_hir/def.rs +++ b/src/librustc_hir/def.rs @@ -77,6 +77,18 @@ pub enum DefKind { // Macro namespace Macro(MacroKind), + + // Not namespaced (or they are, but we don't treat them so) + ExternCrate, + Use, + ForeignMod, + AnonConst, + Field, + LifetimeParam, + GlobalAsm, + Impl, + Closure, + Generator, } impl DefKind { @@ -113,6 +125,16 @@ impl DefKind { DefKind::TyParam => "type parameter", DefKind::ConstParam => "const parameter", DefKind::Macro(macro_kind) => macro_kind.descr(), + DefKind::LifetimeParam => "lifetime parameter", + DefKind::Use => "import", + DefKind::ForeignMod => "foreign module", + DefKind::AnonConst => "constant expression", + DefKind::Field => "field", + DefKind::Impl => "implementation", + DefKind::Closure => "closure", + DefKind::Generator => "generator", + DefKind::ExternCrate => "extern crate", + DefKind::GlobalAsm => "global assembly block", } } @@ -124,7 +146,10 @@ impl DefKind { | DefKind::AssocOpaqueTy | DefKind::AssocFn | DefKind::Enum - | DefKind::OpaqueTy => "an", + | DefKind::OpaqueTy + | DefKind::Impl + | DefKind::Use + | DefKind::ExternCrate => "an", DefKind::Macro(macro_kind) => macro_kind.article(), _ => "a", } @@ -155,6 +180,18 @@ impl DefKind { | DefKind::AssocConst => ns == Namespace::ValueNS, DefKind::Macro(..) => ns == Namespace::MacroNS, + + // Not namespaced. + DefKind::AnonConst + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::ExternCrate + | DefKind::Closure + | DefKind::Generator + | DefKind::Use + | DefKind::ForeignMod + | DefKind::GlobalAsm + | DefKind::Impl => false, } } } diff --git a/src/librustc_hir/definitions.rs b/src/librustc_hir/definitions.rs index 3b86dd42a68b9..30cddac6aac91 100644 --- a/src/librustc_hir/definitions.rs +++ b/src/librustc_hir/definitions.rs @@ -7,7 +7,6 @@ pub use crate::def_id::DefPathHash; use crate::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use crate::hir; -use crate::hir_id::DUMMY_HIR_ID; use rustc_ast::ast; use rustc_ast::crate_disambiguator::CrateDisambiguator; @@ -87,7 +86,7 @@ pub struct Definitions { node_id_to_def_id: FxHashMap, def_id_to_node_id: IndexVec, - pub(super) node_id_to_hir_id: IndexVec, + pub(super) node_id_to_hir_id: IndexVec>, /// The reverse mapping of `node_id_to_hir_id`. pub(super) hir_id_to_node_id: FxHashMap, @@ -247,7 +246,7 @@ impl DefPath { let mut opt_delimiter = None; for component in &self.data { - opt_delimiter.map(|d| s.push(d)); + s.extend(opt_delimiter); opt_delimiter = Some('-'); if component.disambiguator == 0 { write!(s, "{}", component.data.as_symbol()).unwrap(); @@ -326,10 +325,9 @@ impl Definitions { self.node_id_to_def_id.get(&node).copied() } - // FIXME(eddyb) this function can and should return `LocalDefId`. #[inline] - pub fn local_def_id(&self, node: ast::NodeId) -> DefId { - self.opt_local_def_id(node).unwrap().to_def_id() + pub fn local_def_id(&self, node: ast::NodeId) -> LocalDefId { + self.opt_local_def_id(node).unwrap() } #[inline] @@ -344,29 +342,33 @@ impl Definitions { } #[inline] - pub fn as_local_hir_id(&self, def_id: DefId) -> Option { - if let Some(def_id) = def_id.as_local() { - let hir_id = self.local_def_id_to_hir_id(def_id); - if hir_id != DUMMY_HIR_ID { Some(hir_id) } else { None } - } else { - None - } + pub fn as_local_hir_id(&self, def_id: LocalDefId) -> hir::HirId { + self.local_def_id_to_hir_id(def_id) } - // FIXME(eddyb) rename to `hir_id_to_node_id`. #[inline] - pub fn hir_to_node_id(&self, hir_id: hir::HirId) -> ast::NodeId { + pub fn hir_id_to_node_id(&self, hir_id: hir::HirId) -> ast::NodeId { self.hir_id_to_node_id[&hir_id] } - // FIXME(eddyb) rename to `node_id_to_hir_id`. #[inline] - pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId { + pub fn node_id_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId { + self.node_id_to_hir_id[node_id].unwrap() + } + + #[inline] + pub fn opt_node_id_to_hir_id(&self, node_id: ast::NodeId) -> Option { self.node_id_to_hir_id[node_id] } #[inline] pub fn local_def_id_to_hir_id(&self, id: LocalDefId) -> hir::HirId { + let node_id = self.def_id_to_node_id[id]; + self.node_id_to_hir_id[node_id].unwrap() + } + + #[inline] + pub fn opt_local_def_id_to_hir_id(&self, id: LocalDefId) -> Option { let node_id = self.def_id_to_node_id[id]; self.node_id_to_hir_id[node_id] } @@ -473,7 +475,10 @@ impl Definitions { /// Initializes the `ast::NodeId` to `HirId` mapping once it has been generated during /// AST to HIR lowering. - pub fn init_node_id_to_hir_id_mapping(&mut self, mapping: IndexVec) { + pub fn init_node_id_to_hir_id_mapping( + &mut self, + mapping: IndexVec>, + ) { assert!( self.node_id_to_hir_id.is_empty(), "trying to initialize `NodeId` -> `HirId` mapping twice" @@ -484,7 +489,7 @@ impl Definitions { self.hir_id_to_node_id = self .node_id_to_hir_id .iter_enumerated() - .map(|(node_id, &hir_id)| (hir_id, node_id)) + .filter_map(|(node_id, &hir_id)| hir_id.map(|hir_id| (hir_id, node_id))) .collect(); } diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index bb864edc99969..ef398ab25d3fb 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2,25 +2,21 @@ use crate::def::{DefKind, Namespace, Res}; use crate::def_id::DefId; crate use crate::hir_id::HirId; use crate::itemlikevisit; -use crate::print; -crate use BlockCheckMode::*; -crate use FnRetTy::*; -crate use UnsafeSource::*; - -use rustc_ast::ast::{self, AsmDialect, CrateSugar, Ident, Name}; +use rustc_ast::ast::{self, CrateSugar, LlvmAsmDialect}; use rustc_ast::ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy}; pub use rustc_ast::ast::{BorrowKind, ImplPolarity, IsAuto}; pub use rustc_ast::ast::{CaptureBy, Movability, Mutability}; +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_ast::node_id::NodeMap; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; -use rustc_errors::FatalError; use rustc_macros::HashStable_Generic; use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::spec::abi::Abi; use smallvec::SmallVec; @@ -169,12 +165,7 @@ impl fmt::Display for Lifetime { impl fmt::Debug for Lifetime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "lifetime({}: {})", - self.hir_id, - print::to_string(print::NO_ANN, |s| s.print_lifetime(self)) - ) + write!(f, "lifetime({}: {})", self.hir_id, self.name.ident()) } } @@ -191,7 +182,7 @@ impl Lifetime { /// A `Path` is essentially Rust's notion of a name; for instance, /// `std::cmp::PartialEq`. It's represented as a sequence of identifiers, /// along with a bunch of supporting information. -#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] pub struct Path<'hir> { pub span: Span, /// The resolution for the path. @@ -206,18 +197,6 @@ impl Path<'_> { } } -impl fmt::Debug for Path<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "path({})", self) - } -} - -impl fmt::Display for Path<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", print::to_string(print::NO_ANN, |s| s.print_path(self, false))) - } -} - /// A segment of a path: an identifier, an optional lifetime, and a set of /// types. #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] @@ -388,9 +367,9 @@ pub enum GenericBound<'hir> { } impl GenericBound<'_> { - pub fn trait_def_id(&self) -> Option { + pub fn trait_ref(&self) -> Option<&TraitRef<'_>> { match self { - GenericBound::Trait(data, _) => Some(data.trait_ref.trait_def_id()), + GenericBound::Trait(data, _) => Some(&data.trait_ref), _ => None, } } @@ -758,7 +737,7 @@ pub struct Block<'hir> { pub targeted_by_break: bool, } -#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] pub struct Pat<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, @@ -766,17 +745,6 @@ pub struct Pat<'hir> { pub span: Span, } -impl fmt::Debug for Pat<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "pat({}: {})", - self.hir_id, - print::to_string(print::NO_ANN, |s| s.print_pat(self)) - ) - } -} - impl Pat<'_> { // FIXME(#19596) this is a workaround, but there should be a better way fn walk_short_(&self, it: &mut impl FnMut(&Pat<'_>) -> bool) -> bool { @@ -1118,26 +1086,15 @@ impl UnOp { } /// A statement. -#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] pub struct Stmt<'hir> { pub hir_id: HirId, pub kind: StmtKind<'hir>, pub span: Span, } -impl fmt::Debug for Stmt<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "stmt({}: {})", - self.hir_id, - print::to_string(print::NO_ANN, |s| s.print_stmt(self)) - ) - } -} - /// The contents of a statement. -#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] pub enum StmtKind<'hir> { /// A local (`let`) binding. Local(&'hir Local<'hir>), @@ -1336,6 +1293,53 @@ impl BodyOwnerKind { } } +/// The kind of an item that requires const-checking. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ConstContext { + /// A `const fn`. + ConstFn, + + /// A `static` or `static mut`. + Static(Mutability), + + /// A `const`, associated `const`, or other const context. + /// + /// Other contexts include: + /// - Array length expressions + /// - Enum discriminants + /// - Const generics + /// + /// For the most part, other contexts are treated just like a regular `const`, so they are + /// lumped into the same category. + Const, +} + +impl ConstContext { + /// A description of this const context that can appear between backticks in an error message. + /// + /// E.g. `const` or `static mut`. + pub fn keyword_name(self) -> &'static str { + match self { + Self::Const => "const", + Self::Static(Mutability::Not) => "static", + Self::Static(Mutability::Mut) => "static mut", + Self::ConstFn => "const fn", + } + } +} + +/// A colloquial, trivially pluralizable description of this const context for use in error +/// messages. +impl fmt::Display for ConstContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Const => write!(f, "constant"), + Self::Static(_) => write!(f, "static"), + Self::ConstFn => write!(f, "constant function"), + } + } +} + /// A literal. pub type Lit = Spanned; @@ -1351,7 +1355,7 @@ pub struct AnonConst { } /// An expression. -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Debug, RustcEncodable, RustcDecodable)] pub struct Expr<'hir> { pub hir_id: HirId, pub kind: ExprKind<'hir>, @@ -1390,6 +1394,7 @@ impl Expr<'_> { ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, + ExprKind::LlvmInlineAsm(..) => ExprPrecedence::InlineAsm, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, ExprKind::Yield(..) => ExprPrecedence::Yield, @@ -1445,6 +1450,7 @@ impl Expr<'_> { | ExprKind::Loop(..) | ExprKind::Assign(..) | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::Lit(_) | ExprKind::Unary(..) @@ -1472,17 +1478,6 @@ impl Expr<'_> { } } -impl fmt::Debug for Expr<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "expr({}: {})", - self.hir_id, - print::to_string(print::NO_ANN, |s| s.print_expr(self)) - ) - } -} - /// Checks if the specified expression is a built-in range literal. /// (See: `LoweringContext::lower_expr()`). /// @@ -1633,6 +1628,8 @@ pub enum ExprKind<'hir> { /// Inline assembly (from `asm!`), with its outputs and inputs. InlineAsm(&'hir InlineAsm<'hir>), + /// Inline assembly (from `llvm_asm!`), with its outputs and inputs. + LlvmInlineAsm(&'hir LlvmInlineAsm<'hir>), /// A struct or struct-like variant literal expression. /// @@ -1657,7 +1654,7 @@ pub enum ExprKind<'hir> { /// /// To resolve the path to a `DefId`, call [`qpath_res`]. /// -/// [`qpath_res`]: ../ty/struct.TypeckTables.html#method.qpath_res +/// [`qpath_res`]: ../rustc_middle/ty/struct.TypeckTables.html#method.qpath_res #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] pub enum QPath<'hir> { /// Path to a definition, optionally "fully-qualified" with a `Self` @@ -1792,15 +1789,24 @@ pub struct Destination { #[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] pub enum YieldSource { /// An `.await`. - Await, + Await { expr: Option }, /// A plain `yield`. Yield, } +impl YieldSource { + pub fn is_await(&self) -> bool { + match self { + YieldSource::Await { .. } => true, + YieldSource::Yield => false, + } + } +} + impl fmt::Display for YieldSource { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { - YieldSource::Await => "`await`", + YieldSource::Await { .. } => "`await`", YieldSource::Yield => "`yield`", }) } @@ -1811,7 +1817,7 @@ impl From for YieldSource { match kind { // Guess based on the kind of the current generator. GeneratorKind::Gen => Self::Yield, - GeneratorKind::Async(_) => Self::Await, + GeneratorKind::Async(_) => Self::Await { expr: None }, } } } @@ -1965,19 +1971,13 @@ impl TypeBinding<'_> { } } -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Debug, RustcEncodable, RustcDecodable)] pub struct Ty<'hir> { pub hir_id: HirId, pub kind: TyKind<'hir>, pub span: Span, } -impl fmt::Debug for Ty<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "type({})", print::to_string(print::NO_ANN, |s| s.print_type(self))) - } -} - /// Not represented directly in the AST; referred to by name through a `ty_path`. #[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(HashStable_Generic)] @@ -2060,8 +2060,57 @@ pub enum TyKind<'hir> { Err, } +#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +pub enum InlineAsmOperand<'hir> { + In { + reg: InlineAsmRegOrRegClass, + expr: Expr<'hir>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: Expr<'hir>, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_expr: Expr<'hir>, + out_expr: Option>, + }, + Const { + expr: Expr<'hir>, + }, + Sym { + expr: Expr<'hir>, + }, +} + +impl<'hir> InlineAsmOperand<'hir> { + pub fn reg(&self) -> Option { + match *self { + Self::In { reg, .. } + | Self::Out { reg, .. } + | Self::InOut { reg, .. } + | Self::SplitInOut { reg, .. } => Some(reg), + Self::Const { .. } | Self::Sym { .. } => None, + } + } +} + +#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +pub struct InlineAsm<'hir> { + pub template: &'hir [InlineAsmTemplatePiece], + pub operands: &'hir [InlineAsmOperand<'hir>], + pub options: InlineAsmOptions, +} + #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic, PartialEq)] -pub struct InlineAsmOutput { +pub struct LlvmInlineAsmOutput { pub constraint: Symbol, pub is_rw: bool, pub is_indirect: bool, @@ -2071,20 +2120,20 @@ pub struct InlineAsmOutput { // NOTE(eddyb) This is used within MIR as well, so unlike the rest of the HIR, // it needs to be `Clone` and use plain `Vec` instead of arena-allocated slice. #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic, PartialEq)] -pub struct InlineAsmInner { +pub struct LlvmInlineAsmInner { pub asm: Symbol, pub asm_str_style: StrStyle, - pub outputs: Vec, + pub outputs: Vec, pub inputs: Vec, pub clobbers: Vec, pub volatile: bool, pub alignstack: bool, - pub dialect: AsmDialect, + pub dialect: LlvmAsmDialect, } #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] -pub struct InlineAsm<'hir> { - pub inner: InlineAsmInner, +pub struct LlvmInlineAsm<'hir> { + pub inner: LlvmInlineAsmInner, pub outputs_exprs: &'hir [Expr<'hir>], pub inputs_exprs: &'hir [Expr<'hir>], } @@ -2103,7 +2152,7 @@ pub struct Param<'hir> { pub struct FnDecl<'hir> { /// The types of the function's parameters. /// - /// Additional argument data is stored in the function's [body](Body::parameters). + /// Additional argument data is stored in the function's [body](Body::params). pub inputs: &'hir [Ty<'hir>], pub output: FnRetTy<'hir>, pub c_variadic: bool, @@ -2182,15 +2231,6 @@ pub enum FnRetTy<'hir> { Return(&'hir Ty<'hir>), } -impl fmt::Display for FnRetTy<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Return(ref ty) => print::to_string(print::NO_ANN, |s| s.print_type(ty)).fmt(f), - Self::DefaultReturn(_) => "()".fmt(f), - } - } -} - impl FnRetTy<'_> { pub fn span(&self) -> Span { match *self { @@ -2274,13 +2314,10 @@ pub struct TraitRef<'hir> { impl TraitRef<'_> { /// Gets the `DefId` of the referenced trait. It _must_ actually be a trait or trait alias. - pub fn trait_def_id(&self) -> DefId { + pub fn trait_def_id(&self) -> Option { match self.path.res { - Res::Def(DefKind::Trait, did) => did, - Res::Def(DefKind::TraitAlias, did) => did, - Res::Err => { - FatalError.raise(); - } + Res::Def(DefKind::Trait | DefKind::TraitAlias, did) => Some(did), + Res::Err => None, _ => unreachable!(), } } @@ -2461,7 +2498,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), /// `use foo::bar::*;` or `use foo::bar::baz as quux;` /// @@ -2517,27 +2554,6 @@ pub enum ItemKind<'hir> { } impl ItemKind<'_> { - pub fn descr(&self) -> &str { - match *self { - ItemKind::ExternCrate(..) => "extern crate", - ItemKind::Use(..) => "`use` import", - ItemKind::Static(..) => "static item", - ItemKind::Const(..) => "constant item", - ItemKind::Fn(..) => "function", - ItemKind::Mod(..) => "module", - ItemKind::ForeignMod(..) => "extern block", - ItemKind::GlobalAsm(..) => "global asm item", - ItemKind::TyAlias(..) => "type alias", - ItemKind::OpaqueTy(..) => "opaque type", - ItemKind::Enum(..) => "enum", - ItemKind::Struct(..) => "struct", - ItemKind::Union(..) => "union", - ItemKind::Trait(..) => "trait", - ItemKind::TraitAlias(..) => "trait alias", - ItemKind::Impl { .. } => "implementation", - } - } - pub fn generics(&self) -> Option<&Generics<'_>> { Some(match *self { ItemKind::Fn(_, ref generics, _) @@ -2589,7 +2605,7 @@ pub struct ImplItemRef<'hir> { #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] pub enum AssocItemKind { Const, - Method { has_self: bool }, + Fn { has_self: bool }, Type, OpaqueTy, } @@ -2616,16 +2632,6 @@ pub enum ForeignItemKind<'hir> { Type, } -impl ForeignItemKind<'hir> { - pub fn descriptive_variant(&self) -> &str { - match *self { - ForeignItemKind::Fn(..) => "foreign function", - ForeignItemKind::Static(..) => "foreign static item", - ForeignItemKind::Type => "foreign type", - } - } -} - /// A variable captured by a closure. #[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable_Generic)] pub struct Upvar { @@ -2660,7 +2666,7 @@ pub type TraitMap = NodeMap>>; // Map from the NodeId of a glob import to a list of items which are actually // imported. -pub type GlobMap = NodeMap>; +pub type GlobMap = NodeMap>; #[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum Node<'hir> { @@ -2722,8 +2728,42 @@ impl Node<'_> { match self { Node::TraitItem(TraitItem { generics, .. }) | Node::ImplItem(ImplItem { generics, .. }) - | Node::Item(Item { kind: ItemKind::Fn(_, generics, _), .. }) => Some(generics), + | Node::Item(Item { + kind: + ItemKind::Trait(_, _, generics, ..) + | ItemKind::Impl { generics, .. } + | ItemKind::Fn(_, generics, _), + .. + }) => Some(generics), _ => None, } } + + pub fn hir_id(&self) -> Option { + match self { + Node::Item(Item { hir_id, .. }) + | Node::ForeignItem(ForeignItem { hir_id, .. }) + | Node::TraitItem(TraitItem { hir_id, .. }) + | Node::ImplItem(ImplItem { hir_id, .. }) + | Node::Field(StructField { hir_id, .. }) + | Node::AnonConst(AnonConst { hir_id, .. }) + | Node::Expr(Expr { hir_id, .. }) + | Node::Stmt(Stmt { hir_id, .. }) + | Node::Ty(Ty { hir_id, .. }) + | Node::Binding(Pat { hir_id, .. }) + | Node::Pat(Pat { hir_id, .. }) + | Node::Arm(Arm { hir_id, .. }) + | Node::Block(Block { hir_id, .. }) + | Node::Local(Local { hir_id, .. }) + | Node::MacroDef(MacroDef { hir_id, .. }) + | Node::Lifetime(Lifetime { hir_id, .. }) + | Node::Param(Param { hir_id, .. }) + | Node::GenericParam(GenericParam { hir_id, .. }) => Some(*hir_id), + Node::TraitRef(TraitRef { hir_ref_id, .. }) => Some(*hir_ref_id), + Node::PathSegment(PathSegment { hir_id, .. }) => *hir_id, + Node::Variant(Variant { id, .. }) => Some(*id), + Node::Ctor(variant) => variant.ctor_hir_id(), + Node::Crate(_) | Node::Visibility(_) => None, + } + } } diff --git a/src/librustc_hir/hir_id.rs b/src/librustc_hir/hir_id.rs index 1c7987e965f85..d782c3dd70a2c 100644 --- a/src/librustc_hir/hir_id.rs +++ b/src/librustc_hir/hir_id.rs @@ -45,7 +45,4 @@ pub const CRATE_HIR_ID: HirId = HirId { local_id: ItemLocalId::from_u32(0), }; -pub const DUMMY_HIR_ID: HirId = - HirId { owner: LocalDefId { local_def_index: CRATE_DEF_INDEX }, local_id: DUMMY_ITEM_LOCAL_ID }; - pub const DUMMY_ITEM_LOCAL_ID: ItemLocalId = ItemLocalId::MAX; diff --git a/src/librustc_hir/intravisit.rs b/src/librustc_hir/intravisit.rs index 11749cf996b44..97601a3e1ac7b 100644 --- a/src/librustc_hir/intravisit.rs +++ b/src/librustc_hir/intravisit.rs @@ -34,8 +34,9 @@ use crate::hir::*; use crate::hir_id::CRATE_HIR_ID; use crate::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor}; -use rustc_ast::ast::{Attribute, Ident, Label, Name}; +use rustc_ast::ast::{Attribute, Label}; use rustc_ast::walk_list; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; pub struct DeepVisitor<'v, V> { @@ -119,8 +120,10 @@ impl<'a> FnKind<'a> { } } -/// An abstract representation of the HIR `rustc::hir::map::Map`. +/// An abstract representation of the HIR `rustc_middle::hir::map::Map`. pub trait Map<'hir> { + /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. + fn find(&self, hir_id: HirId) -> Option>; fn body(&self, id: BodyId) -> &'hir Body<'hir>; fn item(&self, id: HirId) -> &'hir Item<'hir>; fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir>; @@ -132,6 +135,9 @@ pub trait Map<'hir> { pub struct ErasedMap<'hir>(&'hir dyn Map<'hir>); impl<'hir> Map<'hir> for ErasedMap<'hir> { + fn find(&self, _: HirId) -> Option> { + None + } fn body(&self, id: BodyId) -> &'hir Body<'hir> { self.0.body(id) } @@ -312,7 +318,7 @@ pub trait Visitor<'v>: Sized { fn visit_id(&mut self, _hir_id: HirId) { // Nothing to do. } - fn visit_name(&mut self, _span: Span, _name: Name) { + fn visit_name(&mut self, _span: Span, _name: Symbol) { // Nothing to do. } fn visit_ident(&mut self, ident: Ident) { @@ -390,7 +396,7 @@ pub trait Visitor<'v>: Sized { fn visit_variant_data( &mut self, s: &'v VariantData<'v>, - _: Name, + _: Symbol, _: &'v Generics<'v>, _parent_id: HirId, _: Span, @@ -1152,6 +1158,27 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) walk_list!(visitor, visit_expr, optional_expression); } ExprKind::InlineAsm(ref asm) => { + for op in asm.operands { + match op { + InlineAsmOperand::In { expr, .. } + | InlineAsmOperand::InOut { expr, .. } + | InlineAsmOperand::Const { expr, .. } + | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr), + InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + visitor.visit_expr(expr); + } + } + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + visitor.visit_expr(in_expr); + if let Some(out_expr) = out_expr { + visitor.visit_expr(out_expr); + } + } + } + } + } + ExprKind::LlvmInlineAsm(ref asm) => { walk_list!(visitor, visit_expr, asm.outputs_exprs); walk_list!(visitor, visit_expr, asm.inputs_exprs); } diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 89457009a8bfa..04fe3b60b6a87 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -135,6 +135,8 @@ language_item_table! { SliceU8AllocImplItem, "slice_u8_alloc", slice_u8_alloc_impl, Target::Impl; ConstPtrImplItem, "const_ptr", const_ptr_impl, Target::Impl; MutPtrImplItem, "mut_ptr", mut_ptr_impl, Target::Impl; + ConstSlicePtrImplItem, "const_slice_ptr", const_slice_ptr_impl, Target::Impl; + MutSlicePtrImplItem, "mut_slice_ptr", mut_slice_ptr_impl, Target::Impl; I8ImplItem, "i8", i8_impl, Target::Impl; I16ImplItem, "i16", i16_impl, Target::Impl; I32ImplItem, "i32", i32_impl, Target::Impl; @@ -161,6 +163,7 @@ language_item_table! { CopyTraitLangItem, "copy", copy_trait, Target::Trait; CloneTraitLangItem, "clone", clone_trait, Target::Trait; SyncTraitLangItem, "sync", sync_trait, Target::Trait; + DiscriminantKindTraitLangItem,"discriminant_kind", discriminant_kind_trait, Target::Trait; FreezeTraitLangItem, "freeze", freeze_trait, Target::Trait; DropTraitLangItem, "drop", drop_trait, Target::Trait; @@ -254,7 +257,4 @@ language_item_table! { AlignOffsetLangItem, "align_offset", align_offset_fn, Target::Fn; TerminationTraitLangItem, "termination", termination, Target::Trait; - - Arc, "arc", arc, Target::Struct; - Rc, "rc", rc, Target::Struct; } diff --git a/src/librustc_hir/lib.rs b/src/librustc_hir/lib.rs index fbb3d6b2af37c..b51c0a6e98840 100644 --- a/src/librustc_hir/lib.rs +++ b/src/librustc_hir/lib.rs @@ -7,7 +7,8 @@ #![feature(const_fn)] // For the unsizing cast on `&[]` #![feature(const_panic)] #![feature(in_band_lifetimes)] -#![feature(specialization)] +#![feature(or_patterns)] +#![feature(min_specialization)] #![recursion_limit = "256"] #[macro_use] @@ -23,7 +24,6 @@ pub mod intravisit; pub mod itemlikevisit; pub mod lang_items; pub mod pat_util; -pub mod print; mod stable_hash_impls; mod target; pub mod weak_lang_items; diff --git a/src/librustc_hir/pat_util.rs b/src/librustc_hir/pat_util.rs index 0b9ffdf21fe8e..2f1b5da8e13a0 100644 --- a/src/librustc_hir/pat_util.rs +++ b/src/librustc_hir/pat_util.rs @@ -1,7 +1,7 @@ use crate::def::{CtorOf, DefKind, Res}; use crate::def_id::DefId; use crate::hir::{self, HirId, PatKind}; -use rustc_ast::ast; +use rustc_span::symbol::Ident; use rustc_span::Span; use std::iter::{Enumerate, ExactSizeIterator}; @@ -62,8 +62,9 @@ impl hir::Pat<'_> { match self.kind { PatKind::Lit(_) | PatKind::Range(..) - | PatKind::Path(hir::QPath::Resolved(Some(..), _)) - | PatKind::Path(hir::QPath::TypeRelative(..)) => true, + | PatKind::Path(hir::QPath::Resolved(Some(..), _) | hir::QPath::TypeRelative(..)) => { + true + } PatKind::Path(hir::QPath::Resolved(_, ref path)) | PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) @@ -78,7 +79,7 @@ impl hir::Pat<'_> { /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` - pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident)) { + pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) { self.walk_always(|p| { if let PatKind::Binding(binding_mode, _, ident, _) = p.kind { f(binding_mode, p.hir_id, p.span, ident); @@ -92,7 +93,7 @@ impl hir::Pat<'_> { /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited. pub fn each_binding_or_first( &self, - f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident), + f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident), ) { self.walk(|p| match &p.kind { PatKind::Or(ps) => { @@ -139,10 +140,14 @@ impl hir::Pat<'_> { satisfies } - pub fn simple_ident(&self) -> Option { + pub fn simple_ident(&self) -> Option { match self.kind { - PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) - | PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, None) => Some(ident), + PatKind::Binding( + hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, + _, + ident, + None, + ) => Some(ident), _ => None, } } @@ -155,8 +160,8 @@ impl hir::Pat<'_> { PatKind::Path(hir::QPath::Resolved(_, path)) | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => { - if let Res::Def(DefKind::Variant, id) - | Res::Def(DefKind::Ctor(CtorOf::Variant, ..), id) = path.res + if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) = + path.res { variants.push(id); } diff --git a/src/librustc_hir/print.rs b/src/librustc_hir/print.rs deleted file mode 100644 index cd16e451f1db8..0000000000000 --- a/src/librustc_hir/print.rs +++ /dev/null @@ -1,2336 +0,0 @@ -use rustc_ast::ast; -use rustc_ast::util::parser::{self, AssocOp, Fixity}; -use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; -use rustc_ast_pretty::pp::{self, Breaks}; -use rustc_ast_pretty::pprust::{Comments, PrintState}; -use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::symbol::{kw, IdentPrinter}; -use rustc_span::{self, BytePos, FileName}; -use rustc_target::spec::abi::Abi; - -use crate::hir; -use crate::hir::{GenericArg, GenericParam, GenericParamKind, Node}; -use crate::hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier}; - -use std::borrow::Cow; -use std::cell::Cell; -use std::vec; - -pub enum AnnNode<'a> { - Name(&'a ast::Name), - Block(&'a hir::Block<'a>), - Item(&'a hir::Item<'a>), - SubItem(hir::HirId), - Expr(&'a hir::Expr<'a>), - Pat(&'a hir::Pat<'a>), - Arm(&'a hir::Arm<'a>), -} - -pub enum Nested { - Item(hir::ItemId), - TraitItem(hir::TraitItemId), - ImplItem(hir::ImplItemId), - Body(hir::BodyId), - BodyParamPat(hir::BodyId, usize), -} - -pub trait PpAnn { - fn nested(&self, _state: &mut State<'_>, _nested: Nested) {} - fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {} - fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {} - fn try_fetch_item(&self, _: hir::HirId) -> Option<&hir::Item<'_>> { - None - } -} - -pub struct NoAnn; -impl PpAnn for NoAnn {} -pub const NO_ANN: &dyn PpAnn = &NoAnn; - -impl PpAnn for hir::Crate<'a> { - fn try_fetch_item(&self, item: hir::HirId) -> Option<&hir::Item<'_>> { - Some(self.item(item)) - } - fn nested(&self, state: &mut State<'_>, nested: Nested) { - match nested { - Nested::Item(id) => state.print_item(self.item(id.id)), - Nested::TraitItem(id) => state.print_trait_item(self.trait_item(id)), - Nested::ImplItem(id) => state.print_impl_item(self.impl_item(id)), - Nested::Body(id) => state.print_expr(&self.body(id).value), - Nested::BodyParamPat(id, i) => state.print_pat(&self.body(id).params[i].pat), - } - } -} - -pub struct State<'a> { - pub s: pp::Printer, - comments: Option>, - ann: &'a (dyn PpAnn + 'a), -} - -impl<'a> State<'a> { - pub fn print_node(&mut self, node: Node<'_>) { - match node { - Node::Param(a) => self.print_param(&a), - Node::Item(a) => self.print_item(&a), - Node::ForeignItem(a) => self.print_foreign_item(&a), - Node::TraitItem(a) => self.print_trait_item(a), - Node::ImplItem(a) => self.print_impl_item(a), - Node::Variant(a) => self.print_variant(&a), - Node::AnonConst(a) => self.print_anon_const(&a), - Node::Expr(a) => self.print_expr(&a), - Node::Stmt(a) => self.print_stmt(&a), - Node::PathSegment(a) => self.print_path_segment(&a), - Node::Ty(a) => self.print_type(&a), - Node::TraitRef(a) => self.print_trait_ref(&a), - Node::Binding(a) | Node::Pat(a) => self.print_pat(&a), - Node::Arm(a) => self.print_arm(&a), - Node::Block(a) => { - // Containing cbox, will be closed by print-block at `}`. - self.cbox(INDENT_UNIT); - // Head-ibox, will be closed by print-block after `{`. - self.ibox(0); - self.print_block(&a) - } - Node::Lifetime(a) => self.print_lifetime(&a), - Node::Visibility(a) => self.print_visibility(&a), - Node::GenericParam(_) => panic!("cannot print Node::GenericParam"), - Node::Field(_) => panic!("cannot print StructField"), - // These cases do not carry enough information in the - // `hir_map` to reconstruct their full structure for pretty - // printing. - Node::Ctor(..) => panic!("cannot print isolated Ctor"), - Node::Local(a) => self.print_local_decl(&a), - Node::MacroDef(_) => panic!("cannot print MacroDef"), - Node::Crate(..) => panic!("cannot print Crate"), - } - } -} - -impl std::ops::Deref for State<'_> { - type Target = pp::Printer; - fn deref(&self) -> &Self::Target { - &self.s - } -} - -impl std::ops::DerefMut for State<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.s - } -} - -impl<'a> PrintState<'a> for State<'a> { - fn comments(&mut self) -> &mut Option> { - &mut self.comments - } - - fn print_ident(&mut self, ident: ast::Ident) { - self.s.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string()); - self.ann.post(self, AnnNode::Name(&ident.name)) - } - - fn print_generic_args(&mut self, _: &ast::GenericArgs, _colons_before_params: bool) { - panic!("AST generic args printed by HIR pretty-printer"); - } -} - -pub const INDENT_UNIT: usize = 4; - -/// Requires you to pass an input filename and reader so that -/// it can scan the input text for comments to copy forward. -pub fn print_crate<'a>( - sm: &'a SourceMap, - krate: &hir::Crate<'_>, - filename: FileName, - input: String, - ann: &'a dyn PpAnn, -) -> String { - let mut s = State::new_from_input(sm, filename, input, ann); - - // When printing the AST, we sometimes need to inject `#[no_std]` here. - // Since you can't compile the HIR, it's not necessary. - - s.print_mod(&krate.item.module, &krate.item.attrs); - s.print_remaining_comments(); - s.s.eof() -} - -impl<'a> State<'a> { - pub fn new_from_input( - sm: &'a SourceMap, - filename: FileName, - input: String, - ann: &'a dyn PpAnn, - ) -> State<'a> { - State { s: pp::mk_printer(), comments: Some(Comments::new(sm, filename, input)), ann } - } -} - -pub fn to_string(ann: &dyn PpAnn, f: F) -> String -where - F: FnOnce(&mut State<'_>), -{ - let mut printer = State { s: pp::mk_printer(), comments: None, ann }; - f(&mut printer); - printer.s.eof() -} - -pub fn visibility_qualified>>(vis: &hir::Visibility<'_>, w: S) -> String { - to_string(NO_ANN, |s| { - s.print_visibility(vis); - s.s.word(w) - }) -} - -impl<'a> State<'a> { - pub fn cbox(&mut self, u: usize) { - self.s.cbox(u); - } - - pub fn nbsp(&mut self) { - self.s.word(" ") - } - - pub fn word_nbsp>>(&mut self, w: S) { - self.s.word(w); - self.nbsp() - } - - pub fn head>>(&mut self, w: S) { - let w = w.into(); - // outer-box is consistent - self.cbox(INDENT_UNIT); - // head-box is inconsistent - self.ibox(w.len() + 1); - // keyword that starts the head - if !w.is_empty() { - self.word_nbsp(w); - } - } - - pub fn bopen(&mut self) { - self.s.word("{"); - self.end(); // close the head-box - } - - pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) { - self.maybe_print_comment(span.hi()); - self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize)); - self.s.word("}"); - if close_box { - self.end(); // close the outer-box - } - } - - pub fn bclose(&mut self, span: rustc_span::Span) { - self.bclose_maybe_open(span, true) - } - - pub fn space_if_not_bol(&mut self) { - if !self.s.is_beginning_of_line() { - self.s.space(); - } - } - - pub fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { - if !self.s.is_beginning_of_line() { - self.s.break_offset(n, off) - } else { - if off != 0 && self.s.last_token().is_hardbreak_tok() { - // We do something pretty sketchy here: tuck the nonzero - // offset-adjustment we were going to deposit along with the - // break into the previous hardbreak. - self.s.replace_last_token(pp::Printer::hardbreak_tok_offset(off)); - } - } - } - - // Synthesizes a comment that was not textually present in the original source - // file. - pub fn synth_comment(&mut self, text: String) { - self.s.word("/*"); - self.s.space(); - self.s.word(text); - self.s.space(); - self.s.word("*/") - } - - pub fn commasep_cmnt(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) - where - F: FnMut(&mut State<'_>, &T), - G: FnMut(&T) -> rustc_span::Span, - { - self.rbox(0, b); - let len = elts.len(); - let mut i = 0; - for elt in elts { - self.maybe_print_comment(get_span(elt).hi()); - op(self, elt); - i += 1; - if i < len { - self.s.word(","); - self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi())); - self.space_if_not_bol(); - } - } - self.end(); - } - - pub fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) { - self.commasep_cmnt(b, exprs, |s, e| s.print_expr(&e), |e| e.span) - } - - pub fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) { - self.print_inner_attributes(attrs); - for &item_id in _mod.item_ids { - self.ann.nested(self, Nested::Item(item_id)); - } - } - - pub fn print_foreign_mod(&mut self, nmod: &hir::ForeignMod<'_>, attrs: &[ast::Attribute]) { - self.print_inner_attributes(attrs); - for item in nmod.items { - self.print_foreign_item(item); - } - } - - pub fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) { - if !lifetime.is_elided() { - self.print_lifetime(lifetime); - self.nbsp(); - } - } - - pub fn print_type(&mut self, ty: &hir::Ty<'_>) { - self.maybe_print_comment(ty.span.lo()); - self.ibox(0); - match ty.kind { - hir::TyKind::Slice(ref ty) => { - self.s.word("["); - self.print_type(&ty); - self.s.word("]"); - } - hir::TyKind::Ptr(ref mt) => { - self.s.word("*"); - self.print_mt(mt, true); - } - hir::TyKind::Rptr(ref lifetime, ref mt) => { - self.s.word("&"); - self.print_opt_lifetime(lifetime); - self.print_mt(mt, false); - } - hir::TyKind::Never => { - self.s.word("!"); - } - hir::TyKind::Tup(ref elts) => { - self.popen(); - self.commasep(Inconsistent, &elts[..], |s, ty| s.print_type(&ty)); - if elts.len() == 1 { - self.s.word(","); - } - self.pclose(); - } - hir::TyKind::BareFn(ref f) => { - self.print_ty_fn( - f.abi, - f.unsafety, - &f.decl, - None, - &f.generic_params, - &f.param_names[..], - ); - } - hir::TyKind::Def(..) => {} - hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), - hir::TyKind::TraitObject(bounds, ref lifetime) => { - let mut first = true; - for bound in bounds { - if first { - first = false; - } else { - self.nbsp(); - self.word_space("+"); - } - self.print_poly_trait_ref(bound); - } - if !lifetime.is_elided() { - self.nbsp(); - self.word_space("+"); - self.print_lifetime(lifetime); - } - } - hir::TyKind::Array(ref ty, ref length) => { - self.s.word("["); - self.print_type(&ty); - self.s.word("; "); - self.print_anon_const(length); - self.s.word("]"); - } - hir::TyKind::Typeof(ref e) => { - self.s.word("typeof("); - self.print_anon_const(e); - self.s.word(")"); - } - hir::TyKind::Infer => { - self.s.word("_"); - } - hir::TyKind::Err => { - self.popen(); - self.s.word("/*ERROR*/"); - self.pclose(); - } - } - self.end() - } - - pub fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(item.span.lo()); - self.print_outer_attributes(&item.attrs); - match item.kind { - hir::ForeignItemKind::Fn(ref decl, ref arg_names, ref generics) => { - self.head(""); - self.print_fn( - decl, - hir::FnHeader { - unsafety: hir::Unsafety::Normal, - constness: hir::Constness::NotConst, - abi: Abi::Rust, - asyncness: hir::IsAsync::NotAsync, - }, - Some(item.ident.name), - generics, - &item.vis, - arg_names, - None, - ); - self.end(); // end head-ibox - self.s.word(";"); - self.end() // end the outer fn box - } - hir::ForeignItemKind::Static(ref t, m) => { - self.head(visibility_qualified(&item.vis, "static")); - if m == hir::Mutability::Mut { - self.word_space("mut"); - } - self.print_ident(item.ident); - self.word_space(":"); - self.print_type(&t); - self.s.word(";"); - self.end(); // end the head-ibox - self.end() // end the outer cbox - } - hir::ForeignItemKind::Type => { - self.head(visibility_qualified(&item.vis, "type")); - self.print_ident(item.ident); - self.s.word(";"); - self.end(); // end the head-ibox - self.end() // end the outer cbox - } - } - } - - fn print_associated_const( - &mut self, - ident: ast::Ident, - ty: &hir::Ty<'_>, - default: Option, - vis: &hir::Visibility<'_>, - ) { - self.s.word(visibility_qualified(vis, "")); - self.word_space("const"); - self.print_ident(ident); - self.word_space(":"); - self.print_type(ty); - if let Some(expr) = default { - self.s.space(); - self.word_space("="); - self.ann.nested(self, Nested::Body(expr)); - } - self.s.word(";") - } - - fn print_associated_type( - &mut self, - ident: ast::Ident, - generics: &hir::Generics<'_>, - bounds: Option>, - ty: Option<&hir::Ty<'_>>, - ) { - self.word_space("type"); - self.print_ident(ident); - self.print_generic_params(&generics.params); - if let Some(bounds) = bounds { - self.print_bounds(":", bounds); - } - self.print_where_clause(&generics.where_clause); - if let Some(ty) = ty { - self.s.space(); - self.word_space("="); - self.print_type(ty); - } - self.s.word(";") - } - - fn print_item_type( - &mut self, - item: &hir::Item<'_>, - generics: &hir::Generics<'_>, - inner: impl Fn(&mut Self), - ) { - self.head(visibility_qualified(&item.vis, "type")); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - self.end(); // end the inner ibox - - self.print_where_clause(&generics.where_clause); - self.s.space(); - inner(self); - self.s.word(";"); - self.end(); // end the outer ibox - } - - /// Pretty-print an item - pub fn print_item(&mut self, item: &hir::Item<'_>) { - 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 { - hir::ItemKind::ExternCrate(orig_name) => { - self.head(visibility_qualified(&item.vis, "extern crate")); - if let Some(orig_name) = orig_name { - self.print_name(orig_name); - self.s.space(); - self.s.word("as"); - self.s.space(); - } - self.print_ident(item.ident); - self.s.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - hir::ItemKind::Use(ref path, kind) => { - self.head(visibility_qualified(&item.vis, "use")); - self.print_path(path, false); - - match kind { - hir::UseKind::Single => { - if path.segments.last().unwrap().ident != item.ident { - self.s.space(); - self.word_space("as"); - self.print_ident(item.ident); - } - self.s.word(";"); - } - hir::UseKind::Glob => self.s.word("::*;"), - hir::UseKind::ListStem => self.s.word("::{};"), - } - self.end(); // end inner head-block - self.end(); // end outer head-block - } - hir::ItemKind::Static(ref ty, m, expr) => { - self.head(visibility_qualified(&item.vis, "static")); - if m == hir::Mutability::Mut { - self.word_space("mut"); - } - self.print_ident(item.ident); - self.word_space(":"); - self.print_type(&ty); - self.s.space(); - self.end(); // end the head-ibox - - self.word_space("="); - self.ann.nested(self, Nested::Body(expr)); - self.s.word(";"); - self.end(); // end the outer cbox - } - hir::ItemKind::Const(ref ty, expr) => { - self.head(visibility_qualified(&item.vis, "const")); - self.print_ident(item.ident); - self.word_space(":"); - self.print_type(&ty); - self.s.space(); - self.end(); // end the head-ibox - - self.word_space("="); - self.ann.nested(self, Nested::Body(expr)); - self.s.word(";"); - self.end(); // end the outer cbox - } - hir::ItemKind::Fn(ref sig, ref param_names, body) => { - self.head(""); - self.print_fn( - &sig.decl, - sig.header, - Some(item.ident.name), - param_names, - &item.vis, - &[], - Some(body), - ); - self.s.word(" "); - self.end(); // need to close a box - self.end(); // need to close a box - self.ann.nested(self, Nested::Body(body)); - } - hir::ItemKind::Mod(ref _mod) => { - self.head(visibility_qualified(&item.vis, "mod")); - self.print_ident(item.ident); - self.nbsp(); - self.bopen(); - self.print_mod(_mod, &item.attrs); - self.bclose(item.span); - } - hir::ItemKind::ForeignMod(ref nmod) => { - self.head("extern"); - self.word_nbsp(nmod.abi.to_string()); - self.bopen(); - self.print_foreign_mod(nmod, &item.attrs); - self.bclose(item.span); - } - hir::ItemKind::GlobalAsm(ref ga) => { - self.head(visibility_qualified(&item.vis, "global asm")); - self.s.word(ga.asm.to_string()); - self.end() - } - hir::ItemKind::TyAlias(ref ty, ref generics) => { - self.print_item_type(item, &generics, |state| { - state.word_space("="); - state.print_type(&ty); - }); - } - hir::ItemKind::OpaqueTy(ref opaque_ty) => { - self.print_item_type(item, &opaque_ty.generics, |state| { - let mut real_bounds = Vec::with_capacity(opaque_ty.bounds.len()); - for b in opaque_ty.bounds.iter() { - if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { - state.s.space(); - state.word_space("for ?"); - state.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b); - } - } - state.print_bounds("= impl", real_bounds); - }); - } - hir::ItemKind::Enum(ref enum_definition, ref params) => { - self.print_enum_def(enum_definition, params, item.ident.name, item.span, &item.vis); - } - hir::ItemKind::Struct(ref struct_def, ref generics) => { - self.head(visibility_qualified(&item.vis, "struct")); - self.print_struct(struct_def, generics, item.ident.name, item.span, true); - } - hir::ItemKind::Union(ref struct_def, ref generics) => { - self.head(visibility_qualified(&item.vis, "union")); - self.print_struct(struct_def, generics, item.ident.name, item.span, true); - } - hir::ItemKind::Impl { - unsafety, - polarity, - defaultness, - constness, - defaultness_span: _, - ref generics, - ref of_trait, - ref self_ty, - items, - } => { - self.head(""); - self.print_visibility(&item.vis); - self.print_defaultness(defaultness); - self.print_unsafety(unsafety); - self.word_nbsp("impl"); - - if !generics.params.is_empty() { - self.print_generic_params(&generics.params); - self.s.space(); - } - - if constness == hir::Constness::Const { - self.word_nbsp("const"); - } - - if let hir::ImplPolarity::Negative(_) = polarity { - self.s.word("!"); - } - - if let Some(ref t) = of_trait { - self.print_trait_ref(t); - self.s.space(); - self.word_space("for"); - } - - self.print_type(&self_ty); - self.print_where_clause(&generics.where_clause); - - self.s.space(); - self.bopen(); - self.print_inner_attributes(&item.attrs); - for impl_item in items { - self.ann.nested(self, Nested::ImplItem(impl_item.id)); - } - self.bclose(item.span); - } - hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, trait_items) => { - self.head(""); - self.print_visibility(&item.vis); - self.print_is_auto(is_auto); - self.print_unsafety(unsafety); - self.word_nbsp("trait"); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { - self.s.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b); - } - } - self.print_bounds(":", real_bounds); - self.print_where_clause(&generics.where_clause); - self.s.word(" "); - self.bopen(); - for trait_item in trait_items { - self.ann.nested(self, Nested::TraitItem(trait_item.id)); - } - self.bclose(item.span); - } - hir::ItemKind::TraitAlias(ref generics, ref bounds) => { - self.head(""); - self.print_visibility(&item.vis); - self.word_nbsp("trait"); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - // FIXME(durka) this seems to be some quite outdated syntax - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { - self.s.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b); - } - } - self.nbsp(); - self.print_bounds("=", real_bounds); - self.print_where_clause(&generics.where_clause); - self.s.word(";"); - } - } - self.ann.post(self, AnnNode::Item(item)) - } - - pub fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) { - self.print_path(&t.path, false) - } - - fn print_formal_generic_params(&mut self, generic_params: &[hir::GenericParam<'_>]) { - if !generic_params.is_empty() { - self.s.word("for"); - self.print_generic_params(generic_params); - self.nbsp(); - } - } - - fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) { - self.print_formal_generic_params(&t.bound_generic_params); - self.print_trait_ref(&t.trait_ref) - } - - pub fn print_enum_def( - &mut self, - enum_definition: &hir::EnumDef<'_>, - generics: &hir::Generics<'_>, - name: ast::Name, - span: rustc_span::Span, - visibility: &hir::Visibility<'_>, - ) { - self.head(visibility_qualified(visibility, "enum")); - self.print_name(name); - self.print_generic_params(&generics.params); - self.print_where_clause(&generics.where_clause); - self.s.space(); - self.print_variants(&enum_definition.variants, span) - } - - pub fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) { - self.bopen(); - for v in variants { - self.space_if_not_bol(); - self.maybe_print_comment(v.span.lo()); - self.print_outer_attributes(&v.attrs); - self.ibox(INDENT_UNIT); - self.print_variant(v); - self.s.word(","); - self.end(); - self.maybe_print_trailing_comment(v.span, None); - } - self.bclose(span) - } - - pub fn print_visibility(&mut self, vis: &hir::Visibility<'_>) { - match vis.node { - hir::VisibilityKind::Public => self.word_nbsp("pub"), - hir::VisibilityKind::Crate(ast::CrateSugar::JustCrate) => self.word_nbsp("crate"), - hir::VisibilityKind::Crate(ast::CrateSugar::PubCrate) => self.word_nbsp("pub(crate)"), - hir::VisibilityKind::Restricted { ref path, .. } => { - self.s.word("pub("); - if path.segments.len() == 1 && path.segments[0].ident.name == kw::Super { - // Special case: `super` can print like `pub(super)`. - self.s.word("super"); - } else { - // Everything else requires `in` at present. - self.word_nbsp("in"); - self.print_path(path, false); - } - self.word_nbsp(")"); - } - hir::VisibilityKind::Inherited => (), - } - } - - pub fn print_defaultness(&mut self, defaultness: hir::Defaultness) { - match defaultness { - hir::Defaultness::Default { .. } => self.word_nbsp("default"), - hir::Defaultness::Final => (), - } - } - - pub fn print_struct( - &mut self, - struct_def: &hir::VariantData<'_>, - generics: &hir::Generics<'_>, - name: ast::Name, - span: rustc_span::Span, - print_finalizer: bool, - ) { - self.print_name(name); - self.print_generic_params(&generics.params); - match struct_def { - hir::VariantData::Tuple(..) | hir::VariantData::Unit(..) => { - if let hir::VariantData::Tuple(..) = struct_def { - self.popen(); - self.commasep(Inconsistent, struct_def.fields(), |s, field| { - s.maybe_print_comment(field.span.lo()); - s.print_outer_attributes(&field.attrs); - s.print_visibility(&field.vis); - s.print_type(&field.ty) - }); - self.pclose(); - } - self.print_where_clause(&generics.where_clause); - if print_finalizer { - self.s.word(";"); - } - self.end(); - self.end() // close the outer-box - } - hir::VariantData::Struct(..) => { - self.print_where_clause(&generics.where_clause); - self.nbsp(); - self.bopen(); - 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_outer_attributes(&field.attrs); - self.print_visibility(&field.vis); - self.print_ident(field.ident); - self.word_nbsp(":"); - self.print_type(&field.ty); - self.s.word(","); - } - - self.bclose(span) - } - } - } - - pub fn print_variant(&mut self, v: &hir::Variant<'_>) { - self.head(""); - let generics = hir::Generics::empty(); - self.print_struct(&v.data, &generics, v.ident.name, v.span, false); - if let Some(ref d) = v.disr_expr { - self.s.space(); - self.word_space("="); - self.print_anon_const(d); - } - } - pub fn print_method_sig( - &mut self, - ident: ast::Ident, - m: &hir::FnSig<'_>, - generics: &hir::Generics<'_>, - vis: &hir::Visibility<'_>, - arg_names: &[ast::Ident], - body_id: Option, - ) { - self.print_fn(&m.decl, m.header, Some(ident.name), generics, vis, arg_names, body_id) - } - - pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) { - self.ann.pre(self, AnnNode::SubItem(ti.hir_id)); - self.hardbreak_if_not_bol(); - self.maybe_print_comment(ti.span.lo()); - self.print_outer_attributes(&ti.attrs); - match ti.kind { - hir::TraitItemKind::Const(ref ty, default) => { - let vis = - Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }; - self.print_associated_const(ti.ident, &ty, default, &vis); - } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref arg_names)) => { - let vis = - Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }; - self.print_method_sig(ti.ident, sig, &ti.generics, &vis, arg_names, None); - self.s.word(";"); - } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - let vis = - Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }; - self.head(""); - self.print_method_sig(ti.ident, sig, &ti.generics, &vis, &[], Some(body)); - self.nbsp(); - self.end(); // need to close a box - self.end(); // need to close a box - self.ann.nested(self, Nested::Body(body)); - } - hir::TraitItemKind::Type(ref bounds, ref default) => { - self.print_associated_type( - ti.ident, - &ti.generics, - Some(bounds), - default.as_ref().map(|ty| &**ty), - ); - } - } - self.ann.post(self, AnnNode::SubItem(ti.hir_id)) - } - - pub fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) { - self.ann.pre(self, AnnNode::SubItem(ii.hir_id)); - self.hardbreak_if_not_bol(); - self.maybe_print_comment(ii.span.lo()); - self.print_outer_attributes(&ii.attrs); - self.print_defaultness(ii.defaultness); - - match ii.kind { - hir::ImplItemKind::Const(ref ty, expr) => { - self.print_associated_const(ii.ident, &ty, Some(expr), &ii.vis); - } - hir::ImplItemKind::Fn(ref sig, body) => { - self.head(""); - self.print_method_sig(ii.ident, sig, &ii.generics, &ii.vis, &[], Some(body)); - self.nbsp(); - self.end(); // need to close a box - self.end(); // need to close a box - self.ann.nested(self, Nested::Body(body)); - } - hir::ImplItemKind::TyAlias(ref ty) => { - self.print_associated_type(ii.ident, &ii.generics, None, Some(ty)); - } - hir::ImplItemKind::OpaqueTy(bounds) => { - self.word_space("type"); - self.print_ident(ii.ident); - self.print_bounds("= impl", bounds); - self.s.word(";"); - } - } - self.ann.post(self, AnnNode::SubItem(ii.hir_id)) - } - - pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) { - self.space_if_not_bol(); - self.ibox(INDENT_UNIT); - self.word_nbsp("let"); - - self.ibox(INDENT_UNIT); - decl(self); - self.end(); - - if let Some(ref init) = init { - self.nbsp(); - self.word_space("="); - self.print_expr(&init); - } - self.end() - } - - pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) { - self.maybe_print_comment(st.span.lo()); - match st.kind { - hir::StmtKind::Local(ref loc) => { - self.print_local(loc.init.as_deref(), |this| this.print_local_decl(&loc)); - } - hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), - hir::StmtKind::Expr(ref expr) => { - self.space_if_not_bol(); - self.print_expr(&expr); - } - hir::StmtKind::Semi(ref expr) => { - self.space_if_not_bol(); - self.print_expr(&expr); - self.s.word(";"); - } - } - if stmt_ends_with_semi(&st.kind) { - self.s.word(";"); - } - self.maybe_print_trailing_comment(st.span, None) - } - - pub fn print_block(&mut self, blk: &hir::Block<'_>) { - self.print_block_with_attrs(blk, &[]) - } - - pub fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) { - self.print_block_maybe_unclosed(blk, &[], false) - } - - pub fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) { - self.print_block_maybe_unclosed(blk, attrs, true) - } - - pub fn print_block_maybe_unclosed( - &mut self, - blk: &hir::Block<'_>, - attrs: &[ast::Attribute], - close_box: bool, - ) { - match blk.rules { - hir::UnsafeBlock(..) => self.word_space("unsafe"), - hir::PushUnsafeBlock(..) => self.word_space("push_unsafe"), - hir::PopUnsafeBlock(..) => self.word_space("pop_unsafe"), - hir::DefaultBlock => (), - } - self.maybe_print_comment(blk.span.lo()); - self.ann.pre(self, AnnNode::Block(blk)); - self.bopen(); - - self.print_inner_attributes(attrs); - - for st in blk.stmts { - self.print_stmt(st); - } - if let Some(ref expr) = blk.expr { - self.space_if_not_bol(); - self.print_expr(&expr); - self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi())); - } - self.bclose_maybe_open(blk.span, close_box); - self.ann.post(self, AnnNode::Block(blk)) - } - - pub fn print_anon_const(&mut self, constant: &hir::AnonConst) { - self.ann.nested(self, Nested::Body(constant.body)) - } - - fn print_call_post(&mut self, args: &[hir::Expr<'_>]) { - self.popen(); - self.commasep_exprs(Inconsistent, args); - self.pclose() - } - - pub fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) { - let needs_par = expr.precedence().order() < prec; - if needs_par { - self.popen(); - } - self.print_expr(expr); - if needs_par { - self.pclose(); - } - } - - /// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in - /// `if cond { ... }`. - pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) { - let needs_par = match expr.kind { - // These cases need parens due to the parse error observed in #26461: `if return {}` - // parses as the erroneous construct `if (return {})`, not `if (return) {}`. - hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) => true, - - _ => contains_exterior_struct_lit(expr), - }; - - if needs_par { - self.popen(); - } - self.print_expr(expr); - if needs_par { - self.pclose(); - } - } - - fn print_expr_vec(&mut self, exprs: &[hir::Expr<'_>]) { - self.ibox(INDENT_UNIT); - self.s.word("["); - self.commasep_exprs(Inconsistent, exprs); - self.s.word("]"); - self.end() - } - - fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::AnonConst) { - self.ibox(INDENT_UNIT); - self.s.word("["); - self.print_expr(element); - self.word_space(";"); - self.print_anon_const(count); - self.s.word("]"); - self.end() - } - - fn print_expr_struct( - &mut self, - qpath: &hir::QPath<'_>, - fields: &[hir::Field<'_>], - wth: &Option<&'hir hir::Expr<'_>>, - ) { - self.print_qpath(qpath, true); - self.s.word("{"); - self.commasep_cmnt( - Consistent, - &fields[..], - |s, field| { - s.ibox(INDENT_UNIT); - if !field.is_shorthand { - s.print_ident(field.ident); - s.word_space(":"); - } - s.print_expr(&field.expr); - s.end() - }, - |f| f.span, - ); - match *wth { - Some(ref expr) => { - self.ibox(INDENT_UNIT); - if !fields.is_empty() { - self.s.word(","); - self.s.space(); - } - self.s.word(".."); - self.print_expr(&expr); - self.end(); - } - _ => { - if !fields.is_empty() { - self.s.word(",") - } - } - } - self.s.word("}"); - } - - fn print_expr_tup(&mut self, exprs: &[hir::Expr<'_>]) { - self.popen(); - self.commasep_exprs(Inconsistent, exprs); - if exprs.len() == 1 { - self.s.word(","); - } - self.pclose() - } - - fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let prec = match func.kind { - hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_POSTFIX, - }; - - self.print_expr_maybe_paren(func, prec); - self.print_call_post(args) - } - - fn print_expr_method_call(&mut self, segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>]) { - let base_args = &args[1..]; - self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX); - self.s.word("."); - self.print_ident(segment.ident); - - let generic_args = segment.generic_args(); - if !generic_args.args.is_empty() || !generic_args.bindings.is_empty() { - self.print_generic_args(generic_args, segment.infer_args, true); - } - - self.print_call_post(base_args) - } - - fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { - let assoc_op = bin_op_to_assoc_op(op.node); - let prec = assoc_op.precedence() as i8; - let fixity = assoc_op.fixity(); - - let (left_prec, right_prec) = match fixity { - Fixity::Left => (prec, prec + 1), - Fixity::Right => (prec + 1, prec), - Fixity::None => (prec + 1, prec + 1), - }; - - let left_prec = match (&lhs.kind, op.node) { - // 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. - (&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt) - | (&hir::ExprKind::Cast { .. }, hir::BinOpKind::Shl) => parser::PREC_FORCE_PAREN, - _ => left_prec, - }; - - self.print_expr_maybe_paren(lhs, left_prec); - self.s.space(); - self.word_space(op.node.as_str()); - self.print_expr_maybe_paren(rhs, right_prec) - } - - fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { - self.s.word(op.as_str()); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) - } - - fn print_expr_addr_of( - &mut self, - kind: hir::BorrowKind, - mutability: hir::Mutability, - expr: &hir::Expr<'_>, - ) { - self.s.word("&"); - match kind { - hir::BorrowKind::Ref => self.print_mutability(mutability, false), - hir::BorrowKind::Raw => { - self.word_nbsp("raw"); - self.print_mutability(mutability, true); - } - } - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) - } - - fn print_literal(&mut self, lit: &hir::Lit) { - self.maybe_print_comment(lit.span.lo()); - self.word(lit.node.to_lit_token().to_string()) - } - - pub fn print_expr(&mut self, expr: &hir::Expr<'_>) { - self.maybe_print_comment(expr.span.lo()); - self.print_outer_attributes(&expr.attrs); - self.ibox(INDENT_UNIT); - self.ann.pre(self, AnnNode::Expr(expr)); - match expr.kind { - hir::ExprKind::Box(ref expr) => { - self.word_space("box"); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); - } - hir::ExprKind::Array(ref exprs) => { - self.print_expr_vec(exprs); - } - hir::ExprKind::Repeat(ref element, ref count) => { - self.print_expr_repeat(&element, count); - } - hir::ExprKind::Struct(ref qpath, fields, ref wth) => { - self.print_expr_struct(qpath, fields, wth); - } - hir::ExprKind::Tup(ref exprs) => { - self.print_expr_tup(exprs); - } - hir::ExprKind::Call(ref func, ref args) => { - self.print_expr_call(&func, args); - } - hir::ExprKind::MethodCall(ref segment, _, ref args) => { - self.print_expr_method_call(segment, args); - } - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { - self.print_expr_binary(op, &lhs, &rhs); - } - hir::ExprKind::Unary(op, ref expr) => { - self.print_expr_unary(op, &expr); - } - hir::ExprKind::AddrOf(k, m, ref expr) => { - self.print_expr_addr_of(k, m, &expr); - } - hir::ExprKind::Lit(ref lit) => { - self.print_literal(&lit); - } - hir::ExprKind::Cast(ref expr, ref ty) => { - let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren(&expr, prec); - self.s.space(); - self.word_space("as"); - self.print_type(&ty); - } - hir::ExprKind::Type(ref expr, ref ty) => { - let prec = AssocOp::Colon.precedence() as i8; - self.print_expr_maybe_paren(&expr, prec); - self.word_space(":"); - self.print_type(&ty); - } - hir::ExprKind::DropTemps(ref init) => { - // Print `{`: - self.cbox(INDENT_UNIT); - self.ibox(0); - self.bopen(); - - // Print `let _t = $init;`: - let temp = ast::Ident::from_str("_t"); - self.print_local(Some(init), |this| this.print_ident(temp)); - self.s.word(";"); - - // Print `_t`: - self.space_if_not_bol(); - self.print_ident(temp); - - // Print `}`: - self.bclose_maybe_open(expr.span, true); - } - hir::ExprKind::Loop(ref blk, opt_label, _) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - self.head("loop"); - self.s.space(); - self.print_block(&blk); - } - hir::ExprKind::Match(ref expr, arms, _) => { - self.cbox(INDENT_UNIT); - self.ibox(INDENT_UNIT); - self.word_nbsp("match"); - self.print_expr_as_cond(&expr); - self.s.space(); - self.bopen(); - for arm in arms { - self.print_arm(arm); - } - self.bclose(expr.span); - } - hir::ExprKind::Closure(capture_clause, ref decl, body, _fn_decl_span, _gen) => { - self.print_capture_clause(capture_clause); - - self.print_closure_params(&decl, body); - self.s.space(); - - // 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(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - // containing cbox, will be closed by print-block at `}` - self.cbox(INDENT_UNIT); - // head-box, will be closed by print-block after `{` - self.ibox(0); - self.print_block(&blk); - } - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(&lhs, prec + 1); - self.s.space(); - self.word_space("="); - self.print_expr_maybe_paren(&rhs, prec); - } - hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(&lhs, prec + 1); - self.s.space(); - self.s.word(op.node.as_str()); - self.word_space("="); - self.print_expr_maybe_paren(&rhs, prec); - } - hir::ExprKind::Field(ref expr, ident) => { - self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); - self.s.word("."); - self.print_ident(ident); - } - hir::ExprKind::Index(ref expr, ref index) => { - self.print_expr_maybe_paren(&expr, parser::PREC_POSTFIX); - self.s.word("["); - self.print_expr(&index); - self.s.word("]"); - } - hir::ExprKind::Path(ref qpath) => self.print_qpath(qpath, true), - hir::ExprKind::Break(destination, ref opt_expr) => { - self.s.word("break"); - self.s.space(); - if let Some(label) = destination.label { - self.print_ident(label.ident); - self.s.space(); - } - if let Some(ref expr) = *opt_expr { - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - self.s.space(); - } - } - hir::ExprKind::Continue(destination) => { - self.s.word("continue"); - self.s.space(); - if let Some(label) = destination.label { - self.print_ident(label.ident); - self.s.space() - } - } - hir::ExprKind::Ret(ref result) => { - self.s.word("return"); - if let Some(ref expr) = *result { - self.s.word(" "); - self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); - } - } - hir::ExprKind::InlineAsm(ref a) => { - let i = &a.inner; - self.s.word("asm!"); - self.popen(); - self.print_string(&i.asm.as_str(), i.asm_str_style); - self.word_space(":"); - - let mut out_idx = 0; - self.commasep(Inconsistent, &i.outputs, |s, out| { - let constraint = out.constraint.as_str(); - let mut ch = constraint.chars(); - match ch.next() { - Some('=') if out.is_rw => { - s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked) - } - _ => s.print_string(&constraint, ast::StrStyle::Cooked), - } - s.popen(); - s.print_expr(&a.outputs_exprs[out_idx]); - s.pclose(); - out_idx += 1; - }); - self.s.space(); - self.word_space(":"); - - let mut in_idx = 0; - self.commasep(Inconsistent, &i.inputs, |s, co| { - s.print_string(&co.as_str(), ast::StrStyle::Cooked); - s.popen(); - s.print_expr(&a.inputs_exprs[in_idx]); - s.pclose(); - in_idx += 1; - }); - self.s.space(); - self.word_space(":"); - - self.commasep(Inconsistent, &i.clobbers, |s, co| { - s.print_string(&co.as_str(), ast::StrStyle::Cooked); - }); - - let mut options = vec![]; - if i.volatile { - options.push("volatile"); - } - if i.alignstack { - options.push("alignstack"); - } - if i.dialect == ast::AsmDialect::Intel { - options.push("intel"); - } - - if !options.is_empty() { - self.s.space(); - self.word_space(":"); - self.commasep(Inconsistent, &options, |s, &co| { - s.print_string(co, ast::StrStyle::Cooked); - }); - } - - self.pclose(); - } - hir::ExprKind::Yield(ref expr, _) => { - self.word_space("yield"); - self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); - } - hir::ExprKind::Err => { - self.popen(); - self.s.word("/*ERROR*/"); - self.pclose(); - } - } - self.ann.post(self, AnnNode::Expr(expr)); - self.end() - } - - pub fn print_local_decl(&mut self, loc: &hir::Local<'_>) { - self.print_pat(&loc.pat); - if let Some(ref ty) = loc.ty { - self.word_space(":"); - self.print_type(&ty); - } - } - - pub fn print_usize(&mut self, i: usize) { - self.s.word(i.to_string()) - } - - pub fn print_name(&mut self, name: ast::Name) { - self.print_ident(ast::Ident::with_dummy_span(name)) - } - - pub fn print_for_decl(&mut self, loc: &hir::Local<'_>, coll: &hir::Expr<'_>) { - self.print_local_decl(loc); - self.s.space(); - self.word_space("in"); - self.print_expr(coll) - } - - pub fn print_path(&mut self, path: &hir::Path<'_>, colons_before_params: bool) { - self.maybe_print_comment(path.span.lo()); - - for (i, segment) in path.segments.iter().enumerate() { - if i > 0 { - self.s.word("::") - } - if segment.ident.name != kw::PathRoot { - self.print_ident(segment.ident); - self.print_generic_args( - segment.generic_args(), - segment.infer_args, - colons_before_params, - ); - } - } - } - - pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) { - if segment.ident.name != kw::PathRoot { - self.print_ident(segment.ident); - self.print_generic_args(segment.generic_args(), segment.infer_args, false); - } - } - - pub fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) { - match *qpath { - hir::QPath::Resolved(None, ref path) => self.print_path(path, colons_before_params), - hir::QPath::Resolved(Some(ref qself), ref path) => { - self.s.word("<"); - self.print_type(qself); - self.s.space(); - self.word_space("as"); - - for (i, segment) in path.segments[..path.segments.len() - 1].iter().enumerate() { - if i > 0 { - self.s.word("::") - } - if segment.ident.name != kw::PathRoot { - self.print_ident(segment.ident); - self.print_generic_args( - segment.generic_args(), - segment.infer_args, - colons_before_params, - ); - } - } - - self.s.word(">"); - self.s.word("::"); - let item_segment = path.segments.last().unwrap(); - self.print_ident(item_segment.ident); - self.print_generic_args( - item_segment.generic_args(), - item_segment.infer_args, - colons_before_params, - ) - } - hir::QPath::TypeRelative(ref qself, ref item_segment) => { - // If we've got a compound-qualified-path, let's push an additional pair of angle - // brackets, so that we pretty-print `<::C>` as `::C`, instead of just - // `A::B::C` (since the latter could be ambiguous to the user) - if let hir::TyKind::Path(hir::QPath::Resolved(None, _)) = &qself.kind { - self.print_type(qself); - } else { - self.s.word("<"); - self.print_type(qself); - self.s.word(">"); - } - - self.s.word("::"); - self.print_ident(item_segment.ident); - self.print_generic_args( - item_segment.generic_args(), - item_segment.infer_args, - colons_before_params, - ) - } - } - } - - fn print_generic_args( - &mut self, - generic_args: &hir::GenericArgs<'_>, - infer_args: bool, - colons_before_params: bool, - ) { - if generic_args.parenthesized { - self.s.word("("); - self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(&ty)); - self.s.word(")"); - - self.space_if_not_bol(); - self.word_space("->"); - self.print_type(generic_args.bindings[0].ty()); - } else { - let start = if colons_before_params { "::<" } else { "<" }; - let empty = Cell::new(true); - let start_or_comma = |this: &mut Self| { - if empty.get() { - empty.set(false); - this.s.word(start) - } else { - this.word_space(",") - } - }; - - let mut nonelided_generic_args: bool = false; - let elide_lifetimes = generic_args.args.iter().all(|arg| match arg { - GenericArg::Lifetime(lt) => lt.is_elided(), - _ => { - nonelided_generic_args = true; - true - } - }); - - if nonelided_generic_args { - start_or_comma(self); - self.commasep( - Inconsistent, - &generic_args.args, - |s, generic_arg| match generic_arg { - GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), - GenericArg::Lifetime(_) => {} - GenericArg::Type(ty) => s.print_type(ty), - GenericArg::Const(ct) => s.print_anon_const(&ct.value), - }, - ); - } - - // FIXME(eddyb): this would leak into error messages (e.g., - // "non-exhaustive patterns: `Some::<..>(_)` not covered"). - if infer_args && false { - start_or_comma(self); - self.s.word(".."); - } - - for binding in generic_args.bindings.iter() { - start_or_comma(self); - self.print_ident(binding.ident); - self.s.space(); - match generic_args.bindings[0].kind { - hir::TypeBindingKind::Equality { ref ty } => { - self.word_space("="); - self.print_type(ty); - } - hir::TypeBindingKind::Constraint { bounds } => { - self.print_bounds(":", bounds); - } - } - } - - if !empty.get() { - self.s.word(">") - } - } - } - - pub 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 - match pat.kind { - PatKind::Wild => self.s.word("_"), - PatKind::Binding(binding_mode, _, ident, ref sub) => { - match binding_mode { - hir::BindingAnnotation::Ref => { - self.word_nbsp("ref"); - self.print_mutability(hir::Mutability::Not, false); - } - hir::BindingAnnotation::RefMut => { - self.word_nbsp("ref"); - self.print_mutability(hir::Mutability::Mut, false); - } - hir::BindingAnnotation::Unannotated => {} - hir::BindingAnnotation::Mutable => { - self.word_nbsp("mut"); - } - } - self.print_ident(ident); - if let Some(ref p) = *sub { - self.s.word("@"); - self.print_pat(&p); - } - } - PatKind::TupleStruct(ref qpath, ref elts, ddpos) => { - self.print_qpath(qpath, true); - self.popen(); - if let Some(ddpos) = ddpos { - self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)); - if ddpos != 0 { - self.word_space(","); - } - self.s.word(".."); - if ddpos != elts.len() { - self.s.word(","); - self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)); - } - } else { - self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)); - } - self.pclose(); - } - PatKind::Path(ref qpath) => { - self.print_qpath(qpath, true); - } - PatKind::Struct(ref qpath, ref fields, etc) => { - self.print_qpath(qpath, true); - self.nbsp(); - self.word_space("{"); - self.commasep_cmnt( - Consistent, - &fields[..], - |s, f| { - s.cbox(INDENT_UNIT); - if !f.is_shorthand { - s.print_ident(f.ident); - s.word_nbsp(":"); - } - s.print_pat(&f.pat); - s.end() - }, - |f| f.pat.span, - ); - if etc { - if !fields.is_empty() { - self.word_space(","); - } - self.s.word(".."); - } - self.s.space(); - self.s.word("}"); - } - PatKind::Or(ref pats) => { - self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(&p)); - } - PatKind::Tuple(ref elts, ddpos) => { - self.popen(); - if let Some(ddpos) = ddpos { - self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)); - if ddpos != 0 { - self.word_space(","); - } - self.s.word(".."); - if ddpos != elts.len() { - self.s.word(","); - self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)); - } - } else { - self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)); - if elts.len() == 1 { - self.s.word(","); - } - } - self.pclose(); - } - PatKind::Box(ref inner) => { - let is_range_inner = match inner.kind { - PatKind::Range(..) => true, - _ => false, - }; - self.s.word("box "); - if is_range_inner { - self.popen(); - } - self.print_pat(&inner); - if is_range_inner { - self.pclose(); - } - } - PatKind::Ref(ref inner, mutbl) => { - let is_range_inner = match inner.kind { - PatKind::Range(..) => true, - _ => false, - }; - self.s.word("&"); - self.s.word(mutbl.prefix_str()); - if is_range_inner { - self.popen(); - } - self.print_pat(&inner); - if is_range_inner { - self.pclose(); - } - } - PatKind::Lit(ref e) => self.print_expr(&e), - PatKind::Range(ref begin, ref end, ref end_kind) => { - if let Some(expr) = begin { - self.print_expr(expr); - self.s.space(); - } - match *end_kind { - RangeEnd::Included => self.s.word("..."), - RangeEnd::Excluded => self.s.word(".."), - } - if let Some(expr) = end { - self.print_expr(expr); - } - } - PatKind::Slice(ref before, ref slice, ref after) => { - self.s.word("["); - self.commasep(Inconsistent, &before[..], |s, p| s.print_pat(&p)); - if let Some(ref p) = *slice { - if !before.is_empty() { - self.word_space(","); - } - if let PatKind::Wild = p.kind { - // Print nothing. - } else { - self.print_pat(&p); - } - self.s.word(".."); - if !after.is_empty() { - self.word_space(","); - } - } - self.commasep(Inconsistent, &after[..], |s, p| s.print_pat(&p)); - self.s.word("]"); - } - } - self.ann.post(self, AnnNode::Pat(pat)) - } - - pub fn print_param(&mut self, arg: &hir::Param<'_>) { - self.print_outer_attributes(&arg.attrs); - self.print_pat(&arg.pat); - } - - pub fn print_arm(&mut self, arm: &hir::Arm<'_>) { - // I have no idea why this check is necessary, but here it - // is :( - if arm.attrs.is_empty() { - self.s.space(); - } - self.cbox(INDENT_UNIT); - self.ann.pre(self, AnnNode::Arm(arm)); - self.ibox(0); - self.print_outer_attributes(&arm.attrs); - self.print_pat(&arm.pat); - self.s.space(); - if let Some(ref g) = arm.guard { - match g { - hir::Guard::If(e) => { - self.word_space("if"); - self.print_expr(&e); - self.s.space(); - } - } - } - self.word_space("=>"); - - match arm.body.kind { - hir::ExprKind::Block(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - // the block will close the pattern's ibox - self.print_block_unclosed(&blk); - - // If it is a user-provided unsafe block, print a comma after it - if let hir::UnsafeBlock(hir::UserProvided) = blk.rules { - self.s.word(","); - } - } - _ => { - self.end(); // close the ibox for the pattern - self.print_expr(&arm.body); - self.s.word(","); - } - } - self.ann.post(self, AnnNode::Arm(arm)); - self.end() // close enclosing cbox - } - - pub fn print_fn( - &mut self, - decl: &hir::FnDecl<'_>, - header: hir::FnHeader, - name: Option, - generics: &hir::Generics<'_>, - vis: &hir::Visibility<'_>, - arg_names: &[ast::Ident], - body_id: Option, - ) { - self.print_fn_header_info(header, vis); - - if let Some(name) = name { - self.nbsp(); - self.print_name(name); - } - self.print_generic_params(&generics.params); - - self.popen(); - let mut i = 0; - // Make sure we aren't supplied *both* `arg_names` and `body_id`. - assert!(arg_names.is_empty() || body_id.is_none()); - self.commasep(Inconsistent, &decl.inputs, |s, ty| { - s.ibox(INDENT_UNIT); - if let Some(arg_name) = arg_names.get(i) { - s.s.word(arg_name.to_string()); - s.s.word(":"); - s.s.space(); - } else if let Some(body_id) = body_id { - s.ann.nested(s, Nested::BodyParamPat(body_id, i)); - s.s.word(":"); - s.s.space(); - } - i += 1; - s.print_type(ty); - s.end() - }); - if decl.c_variadic { - self.s.word(", ..."); - } - self.pclose(); - - self.print_fn_output(decl); - self.print_where_clause(&generics.where_clause) - } - - fn print_closure_params(&mut self, decl: &hir::FnDecl<'_>, body_id: hir::BodyId) { - self.s.word("|"); - let mut i = 0; - self.commasep(Inconsistent, &decl.inputs, |s, ty| { - s.ibox(INDENT_UNIT); - - s.ann.nested(s, Nested::BodyParamPat(body_id, i)); - i += 1; - - if let hir::TyKind::Infer = ty.kind { - // Print nothing. - } else { - s.s.word(":"); - s.s.space(); - s.print_type(ty); - } - s.end(); - }); - self.s.word("|"); - - if let hir::DefaultReturn(..) = decl.output { - return; - } - - self.space_if_not_bol(); - self.word_space("->"); - match decl.output { - hir::Return(ref ty) => { - self.print_type(&ty); - self.maybe_print_comment(ty.span.lo()) - } - hir::DefaultReturn(..) => unreachable!(), - } - } - - pub fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) { - match capture_clause { - hir::CaptureBy::Value => self.word_space("move"), - hir::CaptureBy::Ref => {} - } - } - - pub fn print_bounds<'b>( - &mut self, - prefix: &'static str, - bounds: impl IntoIterator>, - ) { - let mut first = true; - for bound in bounds { - if first { - self.s.word(prefix); - } - if !(first && prefix.is_empty()) { - self.nbsp(); - } - if first { - first = false; - } else { - self.word_space("+"); - } - - match bound { - GenericBound::Trait(tref, modifier) => { - if modifier == &TraitBoundModifier::Maybe { - self.s.word("?"); - } - self.print_poly_trait_ref(tref); - } - GenericBound::Outlives(lt) => { - self.print_lifetime(lt); - } - } - } - } - - pub fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) { - if !generic_params.is_empty() { - self.s.word("<"); - - self.commasep(Inconsistent, generic_params, |s, param| s.print_generic_param(param)); - - self.s.word(">"); - } - } - - pub fn print_generic_param(&mut self, param: &GenericParam<'_>) { - if let GenericParamKind::Const { .. } = param.kind { - self.word_space("const"); - } - - self.print_ident(param.name.ident()); - - match param.kind { - GenericParamKind::Lifetime { .. } => { - let mut sep = ":"; - for bound in param.bounds { - match bound { - GenericBound::Outlives(ref lt) => { - self.s.word(sep); - self.print_lifetime(lt); - sep = "+"; - } - _ => panic!(), - } - } - } - GenericParamKind::Type { ref default, .. } => { - self.print_bounds(":", param.bounds); - match default { - Some(default) => { - self.s.space(); - self.word_space("="); - self.print_type(&default) - } - _ => {} - } - } - GenericParamKind::Const { ref ty } => { - self.word_space(":"); - self.print_type(ty) - } - } - } - - pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) { - self.print_ident(lifetime.name.ident()) - } - - pub fn print_where_clause(&mut self, where_clause: &hir::WhereClause<'_>) { - if where_clause.predicates.is_empty() { - return; - } - - self.s.space(); - self.word_space("where"); - - for (i, predicate) in where_clause.predicates.iter().enumerate() { - if i != 0 { - self.word_space(","); - } - - match predicate { - &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - ref bound_generic_params, - ref bounded_ty, - bounds, - .. - }) => { - self.print_formal_generic_params(bound_generic_params); - self.print_type(&bounded_ty); - self.print_bounds(":", bounds); - } - &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - ref lifetime, - ref bounds, - .. - }) => { - self.print_lifetime(lifetime); - self.s.word(":"); - - for (i, bound) in bounds.iter().enumerate() { - match bound { - GenericBound::Outlives(lt) => { - self.print_lifetime(lt); - } - _ => panic!(), - } - - if i != 0 { - self.s.word(":"); - } - } - } - &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - ref lhs_ty, - ref rhs_ty, - .. - }) => { - self.print_type(lhs_ty); - self.s.space(); - self.word_space("="); - self.print_type(rhs_ty); - } - } - } - } - - pub fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) { - match mutbl { - hir::Mutability::Mut => self.word_nbsp("mut"), - hir::Mutability::Not => { - if print_const { - self.word_nbsp("const") - } - } - } - } - - pub fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) { - self.print_mutability(mt.mutbl, print_const); - self.print_type(&mt.ty) - } - - pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) { - if let hir::DefaultReturn(..) = decl.output { - return; - } - - self.space_if_not_bol(); - self.ibox(INDENT_UNIT); - self.word_space("->"); - match decl.output { - hir::DefaultReturn(..) => unreachable!(), - hir::Return(ref ty) => self.print_type(&ty), - } - self.end(); - - match decl.output { - hir::Return(ref output) => self.maybe_print_comment(output.span.lo()), - _ => {} - } - } - - pub fn print_ty_fn( - &mut self, - abi: Abi, - unsafety: hir::Unsafety, - decl: &hir::FnDecl<'_>, - name: Option, - generic_params: &[hir::GenericParam<'_>], - arg_names: &[ast::Ident], - ) { - self.ibox(INDENT_UNIT); - if !generic_params.is_empty() { - self.s.word("for"); - self.print_generic_params(generic_params); - } - let generics = hir::Generics { - params: &[], - where_clause: hir::WhereClause { predicates: &[], span: rustc_span::DUMMY_SP }, - span: rustc_span::DUMMY_SP, - }; - self.print_fn( - decl, - hir::FnHeader { - unsafety, - abi, - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - }, - name, - &generics, - &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }, - arg_names, - None, - ); - self.end(); - } - - pub fn maybe_print_trailing_comment( - &mut self, - span: rustc_span::Span, - next_pos: Option, - ) { - if let Some(cmnts) = self.comments() { - if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) { - self.print_comment(&cmnt); - } - } - } - - pub fn print_remaining_comments(&mut self) { - // If there aren't any remaining comments, then we need to manually - // make sure there is a line break at the end. - if self.next_comment().is_none() { - self.s.hardbreak(); - } - while let Some(ref cmnt) = self.next_comment() { - self.print_comment(cmnt) - } - } - - pub fn print_opt_abi_and_extern_if_nondefault(&mut self, opt_abi: Option) { - match opt_abi { - Some(Abi::Rust) => {} - Some(abi) => { - self.word_nbsp("extern"); - self.word_nbsp(abi.to_string()) - } - None => {} - } - } - - pub fn print_extern_opt_abi(&mut self, opt_abi: Option) { - match opt_abi { - Some(abi) => { - self.word_nbsp("extern"); - self.word_nbsp(abi.to_string()) - } - None => {} - } - } - - pub fn print_fn_header_info(&mut self, header: hir::FnHeader, vis: &hir::Visibility<'_>) { - self.s.word(visibility_qualified(vis, "")); - - match header.constness { - hir::Constness::NotConst => {} - hir::Constness::Const => self.word_nbsp("const"), - } - - match header.asyncness { - hir::IsAsync::NotAsync => {} - hir::IsAsync::Async => self.word_nbsp("async"), - } - - self.print_unsafety(header.unsafety); - - if header.abi != Abi::Rust { - self.word_nbsp("extern"); - self.word_nbsp(header.abi.to_string()); - } - - self.s.word("fn") - } - - pub fn print_unsafety(&mut self, s: hir::Unsafety) { - match s { - hir::Unsafety::Normal => {} - hir::Unsafety::Unsafe => self.word_nbsp("unsafe"), - } - } - - pub fn print_is_auto(&mut self, s: hir::IsAuto) { - match s { - hir::IsAuto::Yes => self.word_nbsp("auto"), - hir::IsAuto::No => {} - } - } -} - -/// Does this expression require a semicolon to be treated -/// as a statement? The negation of this: 'can this expression -/// be used as a statement without a semicolon' -- is used -/// as an early-bail-out in the parser so that, for instance, -/// if true {...} else {...} -/// |x| 5 -/// isn't parsed as (if true {...} else {...} | x) | 5 -// -// Duplicated from `parse::classify`, but adapted for the HIR. -fn expr_requires_semi_to_be_stmt(e: &hir::Expr<'_>) -> bool { - match e.kind { - hir::ExprKind::Match(..) | hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) => false, - _ => true, - } -} - -/// This statement requires a semicolon after it. -/// note that in one case (stmt_semi), we've already -/// seen the semicolon, and thus don't need another. -fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool { - match *stmt { - hir::StmtKind::Local(_) => true, - hir::StmtKind::Item(_) => false, - hir::StmtKind::Expr(ref e) => expr_requires_semi_to_be_stmt(&e), - hir::StmtKind::Semi(..) => false, - } -} - -fn bin_op_to_assoc_op(op: hir::BinOpKind) -> AssocOp { - use crate::hir::BinOpKind::*; - match op { - Add => AssocOp::Add, - Sub => AssocOp::Subtract, - Mul => AssocOp::Multiply, - Div => AssocOp::Divide, - Rem => AssocOp::Modulus, - - And => AssocOp::LAnd, - Or => AssocOp::LOr, - - BitXor => AssocOp::BitXor, - BitAnd => AssocOp::BitAnd, - BitOr => AssocOp::BitOr, - Shl => AssocOp::ShiftLeft, - Shr => AssocOp::ShiftRight, - - Eq => AssocOp::Equal, - Lt => AssocOp::Less, - Le => AssocOp::LessEqual, - Ne => AssocOp::NotEqual, - Ge => AssocOp::GreaterEqual, - Gt => AssocOp::Greater, - } -} - -/// Expressions that syntactically contain an "exterior" struct literal, i.e., not surrounded by any -/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and -/// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. -fn contains_exterior_struct_lit(value: &hir::Expr<'_>) -> bool { - match value.kind { - hir::ExprKind::Struct(..) => true, - - hir::ExprKind::Assign(ref lhs, ref rhs, _) - | hir::ExprKind::AssignOp(_, ref lhs, ref rhs) - | hir::ExprKind::Binary(_, ref lhs, ref rhs) => { - // `X { y: 1 } + X { y: 2 }` - contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) - } - hir::ExprKind::Unary(_, ref x) - | hir::ExprKind::Cast(ref x, _) - | hir::ExprKind::Type(ref x, _) - | hir::ExprKind::Field(ref x, _) - | hir::ExprKind::Index(ref x, _) => { - // `&X { y: 1 }, X { y: 1 }.y` - contains_exterior_struct_lit(&x) - } - - hir::ExprKind::MethodCall(.., ref exprs) => { - // `X { y: 1 }.bar(...)` - contains_exterior_struct_lit(&exprs[0]) - } - - _ => false, - } -} diff --git a/src/librustc_hir/stable_hash_impls.rs b/src/librustc_hir/stable_hash_impls.rs index 996b310896904..1d3f44a08993a 100644 --- a/src/librustc_hir/stable_hash_impls.rs +++ b/src/librustc_hir/stable_hash_impls.rs @@ -9,7 +9,7 @@ use rustc_span::def_id::{DefPathHash, LocalDefId}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc. +/// instead of implementing everything in librustc_middle. pub trait HashStableContext: rustc_ast::HashStableContext + rustc_target::HashStableContext { diff --git a/src/librustc_hir_pretty/Cargo.toml b/src/librustc_hir_pretty/Cargo.toml new file mode 100644 index 0000000000000..ccd3e9b6e43c3 --- /dev/null +++ b/src/librustc_hir_pretty/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_hir_pretty" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_hir_pretty" +path = "lib.rs" +doctest = false + +[dependencies] +rustc_ast_pretty = { path = "../librustc_ast_pretty" } +rustc_hir = { path = "../librustc_hir" } +rustc_target = { path = "../librustc_target" } +rustc_span = { path = "../librustc_span" } +rustc_ast = { path = "../librustc_ast" } diff --git a/src/librustc_hir_pretty/lib.rs b/src/librustc_hir_pretty/lib.rs new file mode 100644 index 0000000000000..8eb19cbb65a0a --- /dev/null +++ b/src/librustc_hir_pretty/lib.rs @@ -0,0 +1,2455 @@ +#![feature(or_patterns)] +#![recursion_limit = "256"] + +use rustc_ast::ast; +use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; +use rustc_ast_pretty::pp::{self, Breaks}; +use rustc_ast_pretty::pprust::{Comments, PrintState}; +use rustc_hir as hir; +use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node}; +use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier}; +use rustc_span::source_map::{SourceMap, Spanned}; +use rustc_span::symbol::{kw, Ident, IdentPrinter, Symbol}; +use rustc_span::{self, BytePos, FileName}; +use rustc_target::spec::abi::Abi; + +use std::borrow::Cow; +use std::cell::Cell; +use std::vec; + +pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: hir::HirId) -> String { + to_string(&map, |s| s.print_node(map.find(hir_id).unwrap())) +} + +pub enum AnnNode<'a> { + Name(&'a Symbol), + Block(&'a hir::Block<'a>), + Item(&'a hir::Item<'a>), + SubItem(hir::HirId), + Expr(&'a hir::Expr<'a>), + Pat(&'a hir::Pat<'a>), + Arm(&'a hir::Arm<'a>), +} + +pub enum Nested { + Item(hir::ItemId), + TraitItem(hir::TraitItemId), + ImplItem(hir::ImplItemId), + Body(hir::BodyId), + BodyParamPat(hir::BodyId, usize), +} + +pub trait PpAnn { + fn nested(&self, _state: &mut State<'_>, _nested: Nested) {} + fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {} + fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {} + fn try_fetch_item(&self, _: hir::HirId) -> Option<&hir::Item<'_>> { + None + } +} + +pub struct NoAnn; +impl PpAnn for NoAnn {} +pub const NO_ANN: &dyn PpAnn = &NoAnn; + +impl PpAnn for hir::Crate<'_> { + fn try_fetch_item(&self, item: hir::HirId) -> Option<&hir::Item<'_>> { + Some(self.item(item)) + } + fn nested(&self, state: &mut State<'_>, nested: Nested) { + match nested { + Nested::Item(id) => state.print_item(self.item(id.id)), + Nested::TraitItem(id) => state.print_trait_item(self.trait_item(id)), + Nested::ImplItem(id) => state.print_impl_item(self.impl_item(id)), + Nested::Body(id) => state.print_expr(&self.body(id).value), + Nested::BodyParamPat(id, i) => state.print_pat(&self.body(id).params[i].pat), + } + } +} + +/// Identical to the `PpAnn` implementation for `hir::Crate`, +/// except it avoids creating a dependency on the whole crate. +impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> { + fn nested(&self, state: &mut State<'_>, nested: Nested) { + match nested { + Nested::Item(id) => state.print_item(self.item(id.id)), + Nested::TraitItem(id) => state.print_trait_item(self.trait_item(id)), + Nested::ImplItem(id) => state.print_impl_item(self.impl_item(id)), + Nested::Body(id) => state.print_expr(&self.body(id).value), + Nested::BodyParamPat(id, i) => state.print_pat(&self.body(id).params[i].pat), + } + } +} + +pub struct State<'a> { + pub s: pp::Printer, + comments: Option>, + ann: &'a (dyn PpAnn + 'a), +} + +impl<'a> State<'a> { + pub fn print_node(&mut self, node: Node<'_>) { + match node { + Node::Param(a) => self.print_param(&a), + Node::Item(a) => self.print_item(&a), + Node::ForeignItem(a) => self.print_foreign_item(&a), + Node::TraitItem(a) => self.print_trait_item(a), + Node::ImplItem(a) => self.print_impl_item(a), + Node::Variant(a) => self.print_variant(&a), + Node::AnonConst(a) => self.print_anon_const(&a), + Node::Expr(a) => self.print_expr(&a), + Node::Stmt(a) => self.print_stmt(&a), + Node::PathSegment(a) => self.print_path_segment(&a), + Node::Ty(a) => self.print_type(&a), + Node::TraitRef(a) => self.print_trait_ref(&a), + Node::Binding(a) | Node::Pat(a) => self.print_pat(&a), + Node::Arm(a) => self.print_arm(&a), + Node::Block(a) => { + // Containing cbox, will be closed by print-block at `}`. + self.cbox(INDENT_UNIT); + // Head-ibox, will be closed by print-block after `{`. + self.ibox(0); + self.print_block(&a) + } + Node::Lifetime(a) => self.print_lifetime(&a), + Node::Visibility(a) => self.print_visibility(&a), + Node::GenericParam(_) => panic!("cannot print Node::GenericParam"), + Node::Field(_) => panic!("cannot print StructField"), + // These cases do not carry enough information in the + // `hir_map` to reconstruct their full structure for pretty + // printing. + Node::Ctor(..) => panic!("cannot print isolated Ctor"), + Node::Local(a) => self.print_local_decl(&a), + Node::MacroDef(_) => panic!("cannot print MacroDef"), + Node::Crate(..) => panic!("cannot print Crate"), + } + } +} + +impl std::ops::Deref for State<'_> { + type Target = pp::Printer; + fn deref(&self) -> &Self::Target { + &self.s + } +} + +impl std::ops::DerefMut for State<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.s + } +} + +impl<'a> PrintState<'a> for State<'a> { + fn comments(&mut self) -> &mut Option> { + &mut self.comments + } + + fn print_ident(&mut self, ident: Ident) { + self.s.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string()); + self.ann.post(self, AnnNode::Name(&ident.name)) + } + + fn print_generic_args(&mut self, _: &ast::GenericArgs, _colons_before_params: bool) { + panic!("AST generic args printed by HIR pretty-printer"); + } +} + +pub const INDENT_UNIT: usize = 4; + +/// Requires you to pass an input filename and reader so that +/// it can scan the input text for comments to copy forward. +pub fn print_crate<'a>( + sm: &'a SourceMap, + krate: &hir::Crate<'_>, + filename: FileName, + input: String, + ann: &'a dyn PpAnn, +) -> String { + let mut s = State::new_from_input(sm, filename, input, ann); + + // When printing the AST, we sometimes need to inject `#[no_std]` here. + // Since you can't compile the HIR, it's not necessary. + + s.print_mod(&krate.item.module, &krate.item.attrs); + s.print_remaining_comments(); + s.s.eof() +} + +impl<'a> State<'a> { + pub fn new_from_input( + sm: &'a SourceMap, + filename: FileName, + input: String, + ann: &'a dyn PpAnn, + ) -> State<'a> { + State { s: pp::mk_printer(), comments: Some(Comments::new(sm, filename, input)), ann } + } +} + +pub fn to_string(ann: &dyn PpAnn, f: F) -> String +where + F: FnOnce(&mut State<'_>), +{ + let mut printer = State { s: pp::mk_printer(), comments: None, ann }; + f(&mut printer); + printer.s.eof() +} + +pub fn visibility_qualified>>(vis: &hir::Visibility<'_>, w: S) -> String { + to_string(NO_ANN, |s| { + s.print_visibility(vis); + s.s.word(w) + }) +} + +impl<'a> State<'a> { + pub fn cbox(&mut self, u: usize) { + self.s.cbox(u); + } + + pub fn nbsp(&mut self) { + self.s.word(" ") + } + + pub fn word_nbsp>>(&mut self, w: S) { + self.s.word(w); + self.nbsp() + } + + pub fn head>>(&mut self, w: S) { + let w = w.into(); + // outer-box is consistent + self.cbox(INDENT_UNIT); + // head-box is inconsistent + self.ibox(w.len() + 1); + // keyword that starts the head + if !w.is_empty() { + self.word_nbsp(w); + } + } + + pub fn bopen(&mut self) { + self.s.word("{"); + self.end(); // close the head-box + } + + pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) { + self.maybe_print_comment(span.hi()); + self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize)); + self.s.word("}"); + if close_box { + self.end(); // close the outer-box + } + } + + pub fn bclose(&mut self, span: rustc_span::Span) { + self.bclose_maybe_open(span, true) + } + + pub fn space_if_not_bol(&mut self) { + if !self.s.is_beginning_of_line() { + self.s.space(); + } + } + + pub fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { + if !self.s.is_beginning_of_line() { + self.s.break_offset(n, off) + } else { + if off != 0 && self.s.last_token().is_hardbreak_tok() { + // We do something pretty sketchy here: tuck the nonzero + // offset-adjustment we were going to deposit along with the + // break into the previous hardbreak. + self.s.replace_last_token(pp::Printer::hardbreak_tok_offset(off)); + } + } + } + + // Synthesizes a comment that was not textually present in the original source + // file. + pub fn synth_comment(&mut self, text: String) { + self.s.word("/*"); + self.s.space(); + self.s.word(text); + self.s.space(); + self.s.word("*/") + } + + pub fn commasep_cmnt(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) + where + F: FnMut(&mut State<'_>, &T), + G: FnMut(&T) -> rustc_span::Span, + { + self.rbox(0, b); + let len = elts.len(); + let mut i = 0; + for elt in elts { + self.maybe_print_comment(get_span(elt).hi()); + op(self, elt); + i += 1; + if i < len { + self.s.word(","); + self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi())); + self.space_if_not_bol(); + } + } + self.end(); + } + + pub fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) { + self.commasep_cmnt(b, exprs, |s, e| s.print_expr(&e), |e| e.span) + } + + pub fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) { + self.print_inner_attributes(attrs); + for &item_id in _mod.item_ids { + self.ann.nested(self, Nested::Item(item_id)); + } + } + + pub fn print_foreign_mod(&mut self, nmod: &hir::ForeignMod<'_>, attrs: &[ast::Attribute]) { + self.print_inner_attributes(attrs); + for item in nmod.items { + self.print_foreign_item(item); + } + } + + pub fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) { + if !lifetime.is_elided() { + self.print_lifetime(lifetime); + self.nbsp(); + } + } + + pub fn print_type(&mut self, ty: &hir::Ty<'_>) { + self.maybe_print_comment(ty.span.lo()); + self.ibox(0); + match ty.kind { + hir::TyKind::Slice(ref ty) => { + self.s.word("["); + self.print_type(&ty); + self.s.word("]"); + } + hir::TyKind::Ptr(ref mt) => { + self.s.word("*"); + self.print_mt(mt, true); + } + hir::TyKind::Rptr(ref lifetime, ref mt) => { + self.s.word("&"); + self.print_opt_lifetime(lifetime); + self.print_mt(mt, false); + } + hir::TyKind::Never => { + self.s.word("!"); + } + hir::TyKind::Tup(ref elts) => { + self.popen(); + self.commasep(Inconsistent, &elts[..], |s, ty| s.print_type(&ty)); + if elts.len() == 1 { + self.s.word(","); + } + self.pclose(); + } + hir::TyKind::BareFn(ref f) => { + self.print_ty_fn( + f.abi, + f.unsafety, + &f.decl, + None, + &f.generic_params, + &f.param_names[..], + ); + } + hir::TyKind::Def(..) => {} + hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), + hir::TyKind::TraitObject(bounds, ref lifetime) => { + let mut first = true; + for bound in bounds { + if first { + first = false; + } else { + self.nbsp(); + self.word_space("+"); + } + self.print_poly_trait_ref(bound); + } + if !lifetime.is_elided() { + self.nbsp(); + self.word_space("+"); + self.print_lifetime(lifetime); + } + } + hir::TyKind::Array(ref ty, ref length) => { + self.s.word("["); + self.print_type(&ty); + self.s.word("; "); + self.print_anon_const(length); + self.s.word("]"); + } + hir::TyKind::Typeof(ref e) => { + self.s.word("typeof("); + self.print_anon_const(e); + self.s.word(")"); + } + hir::TyKind::Infer => { + self.s.word("_"); + } + hir::TyKind::Err => { + self.popen(); + self.s.word("/*ERROR*/"); + self.pclose(); + } + } + self.end() + } + + pub fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(item.span.lo()); + self.print_outer_attributes(&item.attrs); + match item.kind { + hir::ForeignItemKind::Fn(ref decl, ref arg_names, ref generics) => { + self.head(""); + self.print_fn( + decl, + hir::FnHeader { + unsafety: hir::Unsafety::Normal, + constness: hir::Constness::NotConst, + abi: Abi::Rust, + asyncness: hir::IsAsync::NotAsync, + }, + Some(item.ident.name), + generics, + &item.vis, + arg_names, + None, + ); + self.end(); // end head-ibox + self.s.word(";"); + self.end() // end the outer fn box + } + hir::ForeignItemKind::Static(ref t, m) => { + self.head(visibility_qualified(&item.vis, "static")); + if m == hir::Mutability::Mut { + self.word_space("mut"); + } + self.print_ident(item.ident); + self.word_space(":"); + self.print_type(&t); + self.s.word(";"); + self.end(); // end the head-ibox + self.end() // end the outer cbox + } + hir::ForeignItemKind::Type => { + self.head(visibility_qualified(&item.vis, "type")); + self.print_ident(item.ident); + self.s.word(";"); + self.end(); // end the head-ibox + self.end() // end the outer cbox + } + } + } + + fn print_associated_const( + &mut self, + ident: Ident, + ty: &hir::Ty<'_>, + default: Option, + vis: &hir::Visibility<'_>, + ) { + self.s.word(visibility_qualified(vis, "")); + self.word_space("const"); + self.print_ident(ident); + self.word_space(":"); + self.print_type(ty); + if let Some(expr) = default { + self.s.space(); + self.word_space("="); + self.ann.nested(self, Nested::Body(expr)); + } + self.s.word(";") + } + + fn print_associated_type( + &mut self, + ident: Ident, + generics: &hir::Generics<'_>, + bounds: Option>, + ty: Option<&hir::Ty<'_>>, + ) { + self.word_space("type"); + self.print_ident(ident); + self.print_generic_params(&generics.params); + if let Some(bounds) = bounds { + self.print_bounds(":", bounds); + } + self.print_where_clause(&generics.where_clause); + if let Some(ty) = ty { + self.s.space(); + self.word_space("="); + self.print_type(ty); + } + self.s.word(";") + } + + fn print_item_type( + &mut self, + item: &hir::Item<'_>, + generics: &hir::Generics<'_>, + inner: impl Fn(&mut Self), + ) { + self.head(visibility_qualified(&item.vis, "type")); + self.print_ident(item.ident); + self.print_generic_params(&generics.params); + self.end(); // end the inner ibox + + self.print_where_clause(&generics.where_clause); + self.s.space(); + inner(self); + self.s.word(";"); + self.end(); // end the outer ibox + } + + /// Pretty-print an item + pub fn print_item(&mut self, item: &hir::Item<'_>) { + 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 { + hir::ItemKind::ExternCrate(orig_name) => { + self.head(visibility_qualified(&item.vis, "extern crate")); + if let Some(orig_name) = orig_name { + self.print_name(orig_name); + self.s.space(); + self.s.word("as"); + self.s.space(); + } + self.print_ident(item.ident); + self.s.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + hir::ItemKind::Use(ref path, kind) => { + self.head(visibility_qualified(&item.vis, "use")); + self.print_path(path, false); + + match kind { + hir::UseKind::Single => { + if path.segments.last().unwrap().ident != item.ident { + self.s.space(); + self.word_space("as"); + self.print_ident(item.ident); + } + self.s.word(";"); + } + hir::UseKind::Glob => self.s.word("::*;"), + hir::UseKind::ListStem => self.s.word("::{};"), + } + self.end(); // end inner head-block + self.end(); // end outer head-block + } + hir::ItemKind::Static(ref ty, m, expr) => { + self.head(visibility_qualified(&item.vis, "static")); + if m == hir::Mutability::Mut { + self.word_space("mut"); + } + self.print_ident(item.ident); + self.word_space(":"); + self.print_type(&ty); + self.s.space(); + self.end(); // end the head-ibox + + self.word_space("="); + self.ann.nested(self, Nested::Body(expr)); + self.s.word(";"); + self.end(); // end the outer cbox + } + hir::ItemKind::Const(ref ty, expr) => { + self.head(visibility_qualified(&item.vis, "const")); + self.print_ident(item.ident); + self.word_space(":"); + self.print_type(&ty); + self.s.space(); + self.end(); // end the head-ibox + + self.word_space("="); + self.ann.nested(self, Nested::Body(expr)); + self.s.word(";"); + self.end(); // end the outer cbox + } + hir::ItemKind::Fn(ref sig, ref param_names, body) => { + self.head(""); + self.print_fn( + &sig.decl, + sig.header, + Some(item.ident.name), + param_names, + &item.vis, + &[], + Some(body), + ); + self.s.word(" "); + self.end(); // need to close a box + self.end(); // need to close a box + self.ann.nested(self, Nested::Body(body)); + } + hir::ItemKind::Mod(ref _mod) => { + self.head(visibility_qualified(&item.vis, "mod")); + self.print_ident(item.ident); + self.nbsp(); + self.bopen(); + self.print_mod(_mod, &item.attrs); + self.bclose(item.span); + } + hir::ItemKind::ForeignMod(ref nmod) => { + self.head("extern"); + self.word_nbsp(nmod.abi.to_string()); + self.bopen(); + self.print_foreign_mod(nmod, &item.attrs); + self.bclose(item.span); + } + hir::ItemKind::GlobalAsm(ref ga) => { + self.head(visibility_qualified(&item.vis, "global asm")); + self.s.word(ga.asm.to_string()); + self.end() + } + hir::ItemKind::TyAlias(ref ty, ref generics) => { + self.print_item_type(item, &generics, |state| { + state.word_space("="); + state.print_type(&ty); + }); + } + hir::ItemKind::OpaqueTy(ref opaque_ty) => { + self.print_item_type(item, &opaque_ty.generics, |state| { + let mut real_bounds = Vec::with_capacity(opaque_ty.bounds.len()); + for b in opaque_ty.bounds.iter() { + if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { + state.s.space(); + state.word_space("for ?"); + state.print_trait_ref(&ptr.trait_ref); + } else { + real_bounds.push(b); + } + } + state.print_bounds("= impl", real_bounds); + }); + } + hir::ItemKind::Enum(ref enum_definition, ref params) => { + self.print_enum_def(enum_definition, params, item.ident.name, item.span, &item.vis); + } + hir::ItemKind::Struct(ref struct_def, ref generics) => { + self.head(visibility_qualified(&item.vis, "struct")); + self.print_struct(struct_def, generics, item.ident.name, item.span, true); + } + hir::ItemKind::Union(ref struct_def, ref generics) => { + self.head(visibility_qualified(&item.vis, "union")); + self.print_struct(struct_def, generics, item.ident.name, item.span, true); + } + hir::ItemKind::Impl { + unsafety, + polarity, + defaultness, + constness, + defaultness_span: _, + ref generics, + ref of_trait, + ref self_ty, + items, + } => { + self.head(""); + self.print_visibility(&item.vis); + self.print_defaultness(defaultness); + self.print_unsafety(unsafety); + self.word_nbsp("impl"); + + if !generics.params.is_empty() { + self.print_generic_params(&generics.params); + self.s.space(); + } + + if constness == hir::Constness::Const { + self.word_nbsp("const"); + } + + if let hir::ImplPolarity::Negative(_) = polarity { + self.s.word("!"); + } + + if let Some(ref t) = of_trait { + self.print_trait_ref(t); + self.s.space(); + self.word_space("for"); + } + + self.print_type(&self_ty); + self.print_where_clause(&generics.where_clause); + + self.s.space(); + self.bopen(); + self.print_inner_attributes(&item.attrs); + for impl_item in items { + self.ann.nested(self, Nested::ImplItem(impl_item.id)); + } + self.bclose(item.span); + } + hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, trait_items) => { + self.head(""); + self.print_visibility(&item.vis); + self.print_is_auto(is_auto); + self.print_unsafety(unsafety); + self.word_nbsp("trait"); + self.print_ident(item.ident); + self.print_generic_params(&generics.params); + let mut real_bounds = Vec::with_capacity(bounds.len()); + for b in bounds.iter() { + if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { + self.s.space(); + self.word_space("for ?"); + self.print_trait_ref(&ptr.trait_ref); + } else { + real_bounds.push(b); + } + } + self.print_bounds(":", real_bounds); + self.print_where_clause(&generics.where_clause); + self.s.word(" "); + self.bopen(); + for trait_item in trait_items { + self.ann.nested(self, Nested::TraitItem(trait_item.id)); + } + self.bclose(item.span); + } + hir::ItemKind::TraitAlias(ref generics, ref bounds) => { + self.head(""); + self.print_visibility(&item.vis); + self.word_nbsp("trait"); + self.print_ident(item.ident); + self.print_generic_params(&generics.params); + let mut real_bounds = Vec::with_capacity(bounds.len()); + // FIXME(durka) this seems to be some quite outdated syntax + for b in bounds.iter() { + if let GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = *b { + self.s.space(); + self.word_space("for ?"); + self.print_trait_ref(&ptr.trait_ref); + } else { + real_bounds.push(b); + } + } + self.nbsp(); + self.print_bounds("=", real_bounds); + self.print_where_clause(&generics.where_clause); + self.s.word(";"); + } + } + self.ann.post(self, AnnNode::Item(item)) + } + + pub fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) { + self.print_path(&t.path, false) + } + + fn print_formal_generic_params(&mut self, generic_params: &[hir::GenericParam<'_>]) { + if !generic_params.is_empty() { + self.s.word("for"); + self.print_generic_params(generic_params); + self.nbsp(); + } + } + + fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) { + self.print_formal_generic_params(&t.bound_generic_params); + self.print_trait_ref(&t.trait_ref) + } + + pub fn print_enum_def( + &mut self, + enum_definition: &hir::EnumDef<'_>, + generics: &hir::Generics<'_>, + name: Symbol, + span: rustc_span::Span, + visibility: &hir::Visibility<'_>, + ) { + self.head(visibility_qualified(visibility, "enum")); + self.print_name(name); + self.print_generic_params(&generics.params); + self.print_where_clause(&generics.where_clause); + self.s.space(); + self.print_variants(&enum_definition.variants, span) + } + + pub fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) { + self.bopen(); + for v in variants { + self.space_if_not_bol(); + self.maybe_print_comment(v.span.lo()); + self.print_outer_attributes(&v.attrs); + self.ibox(INDENT_UNIT); + self.print_variant(v); + self.s.word(","); + self.end(); + self.maybe_print_trailing_comment(v.span, None); + } + self.bclose(span) + } + + pub fn print_visibility(&mut self, vis: &hir::Visibility<'_>) { + match vis.node { + hir::VisibilityKind::Public => self.word_nbsp("pub"), + hir::VisibilityKind::Crate(ast::CrateSugar::JustCrate) => self.word_nbsp("crate"), + hir::VisibilityKind::Crate(ast::CrateSugar::PubCrate) => self.word_nbsp("pub(crate)"), + hir::VisibilityKind::Restricted { ref path, .. } => { + self.s.word("pub("); + if path.segments.len() == 1 && path.segments[0].ident.name == kw::Super { + // Special case: `super` can print like `pub(super)`. + self.s.word("super"); + } else { + // Everything else requires `in` at present. + self.word_nbsp("in"); + self.print_path(path, false); + } + self.word_nbsp(")"); + } + hir::VisibilityKind::Inherited => (), + } + } + + pub fn print_defaultness(&mut self, defaultness: hir::Defaultness) { + match defaultness { + hir::Defaultness::Default { .. } => self.word_nbsp("default"), + hir::Defaultness::Final => (), + } + } + + pub fn print_struct( + &mut self, + struct_def: &hir::VariantData<'_>, + generics: &hir::Generics<'_>, + name: Symbol, + span: rustc_span::Span, + print_finalizer: bool, + ) { + self.print_name(name); + self.print_generic_params(&generics.params); + match struct_def { + hir::VariantData::Tuple(..) | hir::VariantData::Unit(..) => { + if let hir::VariantData::Tuple(..) = struct_def { + self.popen(); + self.commasep(Inconsistent, struct_def.fields(), |s, field| { + s.maybe_print_comment(field.span.lo()); + s.print_outer_attributes(&field.attrs); + s.print_visibility(&field.vis); + s.print_type(&field.ty) + }); + self.pclose(); + } + self.print_where_clause(&generics.where_clause); + if print_finalizer { + self.s.word(";"); + } + self.end(); + self.end() // close the outer-box + } + hir::VariantData::Struct(..) => { + self.print_where_clause(&generics.where_clause); + self.nbsp(); + self.bopen(); + 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_outer_attributes(&field.attrs); + self.print_visibility(&field.vis); + self.print_ident(field.ident); + self.word_nbsp(":"); + self.print_type(&field.ty); + self.s.word(","); + } + + self.bclose(span) + } + } + } + + pub fn print_variant(&mut self, v: &hir::Variant<'_>) { + self.head(""); + let generics = hir::Generics::empty(); + self.print_struct(&v.data, &generics, v.ident.name, v.span, false); + if let Some(ref d) = v.disr_expr { + self.s.space(); + self.word_space("="); + self.print_anon_const(d); + } + } + pub fn print_method_sig( + &mut self, + ident: Ident, + m: &hir::FnSig<'_>, + generics: &hir::Generics<'_>, + vis: &hir::Visibility<'_>, + arg_names: &[Ident], + body_id: Option, + ) { + self.print_fn(&m.decl, m.header, Some(ident.name), generics, vis, arg_names, body_id) + } + + pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) { + self.ann.pre(self, AnnNode::SubItem(ti.hir_id)); + self.hardbreak_if_not_bol(); + self.maybe_print_comment(ti.span.lo()); + self.print_outer_attributes(&ti.attrs); + match ti.kind { + hir::TraitItemKind::Const(ref ty, default) => { + let vis = + Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }; + self.print_associated_const(ti.ident, &ty, default, &vis); + } + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref arg_names)) => { + let vis = + Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }; + self.print_method_sig(ti.ident, sig, &ti.generics, &vis, arg_names, None); + self.s.word(";"); + } + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + let vis = + Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }; + self.head(""); + self.print_method_sig(ti.ident, sig, &ti.generics, &vis, &[], Some(body)); + self.nbsp(); + self.end(); // need to close a box + self.end(); // need to close a box + self.ann.nested(self, Nested::Body(body)); + } + hir::TraitItemKind::Type(ref bounds, ref default) => { + self.print_associated_type( + ti.ident, + &ti.generics, + Some(bounds), + default.as_ref().map(|ty| &**ty), + ); + } + } + self.ann.post(self, AnnNode::SubItem(ti.hir_id)) + } + + pub fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) { + self.ann.pre(self, AnnNode::SubItem(ii.hir_id)); + self.hardbreak_if_not_bol(); + self.maybe_print_comment(ii.span.lo()); + self.print_outer_attributes(&ii.attrs); + self.print_defaultness(ii.defaultness); + + match ii.kind { + hir::ImplItemKind::Const(ref ty, expr) => { + self.print_associated_const(ii.ident, &ty, Some(expr), &ii.vis); + } + hir::ImplItemKind::Fn(ref sig, body) => { + self.head(""); + self.print_method_sig(ii.ident, sig, &ii.generics, &ii.vis, &[], Some(body)); + self.nbsp(); + self.end(); // need to close a box + self.end(); // need to close a box + self.ann.nested(self, Nested::Body(body)); + } + hir::ImplItemKind::TyAlias(ref ty) => { + self.print_associated_type(ii.ident, &ii.generics, None, Some(ty)); + } + hir::ImplItemKind::OpaqueTy(bounds) => { + self.word_space("type"); + self.print_ident(ii.ident); + self.print_bounds("= impl", bounds); + self.s.word(";"); + } + } + self.ann.post(self, AnnNode::SubItem(ii.hir_id)) + } + + pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) { + self.space_if_not_bol(); + self.ibox(INDENT_UNIT); + self.word_nbsp("let"); + + self.ibox(INDENT_UNIT); + decl(self); + self.end(); + + if let Some(ref init) = init { + self.nbsp(); + self.word_space("="); + self.print_expr(&init); + } + self.end() + } + + pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) { + self.maybe_print_comment(st.span.lo()); + match st.kind { + hir::StmtKind::Local(ref loc) => { + self.print_local(loc.init.as_deref(), |this| this.print_local_decl(&loc)); + } + hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), + hir::StmtKind::Expr(ref expr) => { + self.space_if_not_bol(); + self.print_expr(&expr); + } + hir::StmtKind::Semi(ref expr) => { + self.space_if_not_bol(); + self.print_expr(&expr); + self.s.word(";"); + } + } + if stmt_ends_with_semi(&st.kind) { + self.s.word(";"); + } + self.maybe_print_trailing_comment(st.span, None) + } + + pub fn print_block(&mut self, blk: &hir::Block<'_>) { + self.print_block_with_attrs(blk, &[]) + } + + pub fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) { + self.print_block_maybe_unclosed(blk, &[], false) + } + + pub fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) { + self.print_block_maybe_unclosed(blk, attrs, true) + } + + pub fn print_block_maybe_unclosed( + &mut self, + blk: &hir::Block<'_>, + attrs: &[ast::Attribute], + close_box: bool, + ) { + match blk.rules { + hir::BlockCheckMode::UnsafeBlock(..) => self.word_space("unsafe"), + hir::BlockCheckMode::PushUnsafeBlock(..) => self.word_space("push_unsafe"), + hir::BlockCheckMode::PopUnsafeBlock(..) => self.word_space("pop_unsafe"), + hir::BlockCheckMode::DefaultBlock => (), + } + self.maybe_print_comment(blk.span.lo()); + self.ann.pre(self, AnnNode::Block(blk)); + self.bopen(); + + self.print_inner_attributes(attrs); + + for st in blk.stmts { + self.print_stmt(st); + } + if let Some(ref expr) = blk.expr { + self.space_if_not_bol(); + self.print_expr(&expr); + self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi())); + } + self.bclose_maybe_open(blk.span, close_box); + self.ann.post(self, AnnNode::Block(blk)) + } + + pub fn print_anon_const(&mut self, constant: &hir::AnonConst) { + self.ann.nested(self, Nested::Body(constant.body)) + } + + fn print_call_post(&mut self, args: &[hir::Expr<'_>]) { + self.popen(); + self.commasep_exprs(Inconsistent, args); + self.pclose() + } + + pub fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) { + let needs_par = expr.precedence().order() < prec; + if needs_par { + self.popen(); + } + self.print_expr(expr); + if needs_par { + self.pclose(); + } + } + + /// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in + /// `if cond { ... }`. + pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) { + let needs_par = match expr.kind { + // These cases need parens due to the parse error observed in #26461: `if return {}` + // parses as the erroneous construct `if (return {})`, not `if (return) {}`. + hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) => true, + + _ => contains_exterior_struct_lit(expr), + }; + + if needs_par { + self.popen(); + } + self.print_expr(expr); + if needs_par { + self.pclose(); + } + } + + fn print_expr_vec(&mut self, exprs: &[hir::Expr<'_>]) { + self.ibox(INDENT_UNIT); + self.s.word("["); + self.commasep_exprs(Inconsistent, exprs); + self.s.word("]"); + self.end() + } + + fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::AnonConst) { + self.ibox(INDENT_UNIT); + self.s.word("["); + self.print_expr(element); + self.word_space(";"); + self.print_anon_const(count); + self.s.word("]"); + self.end() + } + + fn print_expr_struct( + &mut self, + qpath: &hir::QPath<'_>, + fields: &[hir::Field<'_>], + wth: &Option<&hir::Expr<'_>>, + ) { + self.print_qpath(qpath, true); + self.s.word("{"); + self.commasep_cmnt( + Consistent, + &fields[..], + |s, field| { + s.ibox(INDENT_UNIT); + if !field.is_shorthand { + s.print_ident(field.ident); + s.word_space(":"); + } + s.print_expr(&field.expr); + s.end() + }, + |f| f.span, + ); + match *wth { + Some(ref expr) => { + self.ibox(INDENT_UNIT); + if !fields.is_empty() { + self.s.word(","); + self.s.space(); + } + self.s.word(".."); + self.print_expr(&expr); + self.end(); + } + _ => { + if !fields.is_empty() { + self.s.word(",") + } + } + } + self.s.word("}"); + } + + fn print_expr_tup(&mut self, exprs: &[hir::Expr<'_>]) { + self.popen(); + self.commasep_exprs(Inconsistent, exprs); + if exprs.len() == 1 { + self.s.word(","); + } + self.pclose() + } + + fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let prec = match func.kind { + hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, + _ => parser::PREC_POSTFIX, + }; + + self.print_expr_maybe_paren(func, prec); + self.print_call_post(args) + } + + fn print_expr_method_call(&mut self, segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>]) { + let base_args = &args[1..]; + self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX); + self.s.word("."); + self.print_ident(segment.ident); + + let generic_args = segment.generic_args(); + if !generic_args.args.is_empty() || !generic_args.bindings.is_empty() { + self.print_generic_args(generic_args, segment.infer_args, true); + } + + self.print_call_post(base_args) + } + + fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { + let assoc_op = bin_op_to_assoc_op(op.node); + let prec = assoc_op.precedence() as i8; + let fixity = assoc_op.fixity(); + + let (left_prec, right_prec) = match fixity { + Fixity::Left => (prec, prec + 1), + Fixity::Right => (prec + 1, prec), + Fixity::None => (prec + 1, prec + 1), + }; + + let left_prec = match (&lhs.kind, op.node) { + // 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. + (&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => { + parser::PREC_FORCE_PAREN + } + _ => left_prec, + }; + + self.print_expr_maybe_paren(lhs, left_prec); + self.s.space(); + self.word_space(op.node.as_str()); + self.print_expr_maybe_paren(rhs, right_prec) + } + + fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { + self.s.word(op.as_str()); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + } + + fn print_expr_addr_of( + &mut self, + kind: hir::BorrowKind, + mutability: hir::Mutability, + expr: &hir::Expr<'_>, + ) { + self.s.word("&"); + match kind { + hir::BorrowKind::Ref => self.print_mutability(mutability, false), + hir::BorrowKind::Raw => { + self.word_nbsp("raw"); + self.print_mutability(mutability, true); + } + } + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + } + + fn print_literal(&mut self, lit: &hir::Lit) { + self.maybe_print_comment(lit.span.lo()); + self.word(lit.node.to_lit_token().to_string()) + } + + pub fn print_expr(&mut self, expr: &hir::Expr<'_>) { + self.maybe_print_comment(expr.span.lo()); + self.print_outer_attributes(&expr.attrs); + self.ibox(INDENT_UNIT); + self.ann.pre(self, AnnNode::Expr(expr)); + match expr.kind { + hir::ExprKind::Box(ref expr) => { + self.word_space("box"); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); + } + hir::ExprKind::Array(ref exprs) => { + self.print_expr_vec(exprs); + } + hir::ExprKind::Repeat(ref element, ref count) => { + self.print_expr_repeat(&element, count); + } + hir::ExprKind::Struct(ref qpath, fields, ref wth) => { + self.print_expr_struct(qpath, fields, wth); + } + hir::ExprKind::Tup(ref exprs) => { + self.print_expr_tup(exprs); + } + hir::ExprKind::Call(ref func, ref args) => { + self.print_expr_call(&func, args); + } + hir::ExprKind::MethodCall(ref segment, _, ref args) => { + self.print_expr_method_call(segment, args); + } + hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + self.print_expr_binary(op, &lhs, &rhs); + } + hir::ExprKind::Unary(op, ref expr) => { + self.print_expr_unary(op, &expr); + } + hir::ExprKind::AddrOf(k, m, ref expr) => { + self.print_expr_addr_of(k, m, &expr); + } + hir::ExprKind::Lit(ref lit) => { + self.print_literal(&lit); + } + hir::ExprKind::Cast(ref expr, ref ty) => { + let prec = AssocOp::As.precedence() as i8; + self.print_expr_maybe_paren(&expr, prec); + self.s.space(); + self.word_space("as"); + self.print_type(&ty); + } + hir::ExprKind::Type(ref expr, ref ty) => { + let prec = AssocOp::Colon.precedence() as i8; + self.print_expr_maybe_paren(&expr, prec); + self.word_space(":"); + self.print_type(&ty); + } + hir::ExprKind::DropTemps(ref init) => { + // Print `{`: + self.cbox(INDENT_UNIT); + self.ibox(0); + self.bopen(); + + // Print `let _t = $init;`: + let temp = Ident::from_str("_t"); + self.print_local(Some(init), |this| this.print_ident(temp)); + self.s.word(";"); + + // Print `_t`: + self.space_if_not_bol(); + self.print_ident(temp); + + // Print `}`: + self.bclose_maybe_open(expr.span, true); + } + hir::ExprKind::Loop(ref blk, opt_label, _) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + self.head("loop"); + self.s.space(); + self.print_block(&blk); + } + hir::ExprKind::Match(ref expr, arms, _) => { + self.cbox(INDENT_UNIT); + self.ibox(INDENT_UNIT); + self.word_nbsp("match"); + self.print_expr_as_cond(&expr); + self.s.space(); + self.bopen(); + for arm in arms { + self.print_arm(arm); + } + self.bclose(expr.span); + } + hir::ExprKind::Closure(capture_clause, ref decl, body, _fn_decl_span, _gen) => { + self.print_capture_clause(capture_clause); + + self.print_closure_params(&decl, body); + self.s.space(); + + // 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(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + // containing cbox, will be closed by print-block at `}` + self.cbox(INDENT_UNIT); + // head-box, will be closed by print-block after `{` + self.ibox(0); + self.print_block(&blk); + } + hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + let prec = AssocOp::Assign.precedence() as i8; + self.print_expr_maybe_paren(&lhs, prec + 1); + self.s.space(); + self.word_space("="); + self.print_expr_maybe_paren(&rhs, prec); + } + hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => { + let prec = AssocOp::Assign.precedence() as i8; + self.print_expr_maybe_paren(&lhs, prec + 1); + self.s.space(); + self.s.word(op.node.as_str()); + self.word_space("="); + self.print_expr_maybe_paren(&rhs, prec); + } + hir::ExprKind::Field(ref expr, ident) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); + self.s.word("."); + self.print_ident(ident); + } + hir::ExprKind::Index(ref expr, ref index) => { + self.print_expr_maybe_paren(&expr, parser::PREC_POSTFIX); + self.s.word("["); + self.print_expr(&index); + self.s.word("]"); + } + hir::ExprKind::Path(ref qpath) => self.print_qpath(qpath, true), + hir::ExprKind::Break(destination, ref opt_expr) => { + self.s.word("break"); + self.s.space(); + if let Some(label) = destination.label { + self.print_ident(label.ident); + self.s.space(); + } + if let Some(ref expr) = *opt_expr { + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + self.s.space(); + } + } + hir::ExprKind::Continue(destination) => { + self.s.word("continue"); + self.s.space(); + if let Some(label) = destination.label { + self.print_ident(label.ident); + self.s.space() + } + } + hir::ExprKind::Ret(ref result) => { + self.s.word("return"); + if let Some(ref expr) = *result { + self.s.word(" "); + self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); + } + } + hir::ExprKind::InlineAsm(ref a) => { + enum AsmArg<'a> { + Template(String), + Operand(&'a hir::InlineAsmOperand<'a>), + Options(ast::InlineAsmOptions), + } + + let mut args = vec![]; + args.push(AsmArg::Template(ast::InlineAsmTemplatePiece::to_string(&a.template))); + args.extend(a.operands.iter().map(|o| AsmArg::Operand(o))); + if !a.options.is_empty() { + args.push(AsmArg::Options(a.options)); + } + + self.word("asm!"); + self.popen(); + self.commasep(Consistent, &args, |s, arg| match arg { + AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked), + AsmArg::Operand(op) => match op { + hir::InlineAsmOperand::In { reg, expr } => { + s.word("in"); + s.popen(); + s.word(format!("{}", reg)); + s.pclose(); + s.space(); + s.print_expr(expr); + } + hir::InlineAsmOperand::Out { reg, late, expr } => { + s.word(if *late { "lateout" } else { "out" }); + s.popen(); + s.word(format!("{}", reg)); + s.pclose(); + s.space(); + match expr { + Some(expr) => s.print_expr(expr), + None => s.word("_"), + } + } + hir::InlineAsmOperand::InOut { reg, late, expr } => { + s.word(if *late { "inlateout" } else { "inout" }); + s.popen(); + s.word(format!("{}", reg)); + s.pclose(); + s.space(); + s.print_expr(expr); + } + hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { + s.word(if *late { "inlateout" } else { "inout" }); + s.popen(); + s.word(format!("{}", reg)); + s.pclose(); + s.space(); + s.print_expr(in_expr); + s.space(); + s.word_space("=>"); + match out_expr { + Some(out_expr) => s.print_expr(out_expr), + None => s.word("_"), + } + } + hir::InlineAsmOperand::Const { expr } => { + s.word("const"); + s.space(); + s.print_expr(expr); + } + hir::InlineAsmOperand::Sym { expr } => { + s.word("sym"); + s.space(); + s.print_expr(expr); + } + }, + AsmArg::Options(opts) => { + s.word("options"); + s.popen(); + let mut options = vec![]; + if opts.contains(ast::InlineAsmOptions::PURE) { + options.push("pure"); + } + if opts.contains(ast::InlineAsmOptions::NOMEM) { + options.push("nomem"); + } + if opts.contains(ast::InlineAsmOptions::READONLY) { + options.push("readonly"); + } + if opts.contains(ast::InlineAsmOptions::PRESERVES_FLAGS) { + options.push("preserves_flags"); + } + if opts.contains(ast::InlineAsmOptions::NORETURN) { + options.push("noreturn"); + } + if opts.contains(ast::InlineAsmOptions::NOSTACK) { + options.push("nostack"); + } + if opts.contains(ast::InlineAsmOptions::ATT_SYNTAX) { + options.push("att_syntax"); + } + s.commasep(Inconsistent, &options, |s, &opt| { + s.word(opt); + }); + s.pclose(); + } + }); + self.pclose(); + } + hir::ExprKind::LlvmInlineAsm(ref a) => { + let i = &a.inner; + self.s.word("llvm_asm!"); + self.popen(); + self.print_string(&i.asm.as_str(), i.asm_str_style); + self.word_space(":"); + + let mut out_idx = 0; + self.commasep(Inconsistent, &i.outputs, |s, out| { + let constraint = out.constraint.as_str(); + let mut ch = constraint.chars(); + match ch.next() { + Some('=') if out.is_rw => { + s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked) + } + _ => s.print_string(&constraint, ast::StrStyle::Cooked), + } + s.popen(); + s.print_expr(&a.outputs_exprs[out_idx]); + s.pclose(); + out_idx += 1; + }); + self.s.space(); + self.word_space(":"); + + let mut in_idx = 0; + self.commasep(Inconsistent, &i.inputs, |s, co| { + s.print_string(&co.as_str(), ast::StrStyle::Cooked); + s.popen(); + s.print_expr(&a.inputs_exprs[in_idx]); + s.pclose(); + in_idx += 1; + }); + self.s.space(); + self.word_space(":"); + + self.commasep(Inconsistent, &i.clobbers, |s, co| { + s.print_string(&co.as_str(), ast::StrStyle::Cooked); + }); + + let mut options = vec![]; + if i.volatile { + options.push("volatile"); + } + if i.alignstack { + options.push("alignstack"); + } + if i.dialect == ast::LlvmAsmDialect::Intel { + options.push("intel"); + } + + if !options.is_empty() { + self.s.space(); + self.word_space(":"); + self.commasep(Inconsistent, &options, |s, &co| { + s.print_string(co, ast::StrStyle::Cooked); + }); + } + + self.pclose(); + } + hir::ExprKind::Yield(ref expr, _) => { + self.word_space("yield"); + self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); + } + hir::ExprKind::Err => { + self.popen(); + self.s.word("/*ERROR*/"); + self.pclose(); + } + } + self.ann.post(self, AnnNode::Expr(expr)); + self.end() + } + + pub fn print_local_decl(&mut self, loc: &hir::Local<'_>) { + self.print_pat(&loc.pat); + if let Some(ref ty) = loc.ty { + self.word_space(":"); + self.print_type(&ty); + } + } + + pub fn print_usize(&mut self, i: usize) { + self.s.word(i.to_string()) + } + + pub fn print_name(&mut self, name: Symbol) { + self.print_ident(Ident::with_dummy_span(name)) + } + + pub fn print_for_decl(&mut self, loc: &hir::Local<'_>, coll: &hir::Expr<'_>) { + self.print_local_decl(loc); + self.s.space(); + self.word_space("in"); + self.print_expr(coll) + } + + pub fn print_path(&mut self, path: &hir::Path<'_>, colons_before_params: bool) { + self.maybe_print_comment(path.span.lo()); + + for (i, segment) in path.segments.iter().enumerate() { + if i > 0 { + self.s.word("::") + } + if segment.ident.name != kw::PathRoot { + self.print_ident(segment.ident); + self.print_generic_args( + segment.generic_args(), + segment.infer_args, + colons_before_params, + ); + } + } + } + + pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) { + if segment.ident.name != kw::PathRoot { + self.print_ident(segment.ident); + self.print_generic_args(segment.generic_args(), segment.infer_args, false); + } + } + + pub fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) { + match *qpath { + hir::QPath::Resolved(None, ref path) => self.print_path(path, colons_before_params), + hir::QPath::Resolved(Some(ref qself), ref path) => { + self.s.word("<"); + self.print_type(qself); + self.s.space(); + self.word_space("as"); + + for (i, segment) in path.segments[..path.segments.len() - 1].iter().enumerate() { + if i > 0 { + self.s.word("::") + } + if segment.ident.name != kw::PathRoot { + self.print_ident(segment.ident); + self.print_generic_args( + segment.generic_args(), + segment.infer_args, + colons_before_params, + ); + } + } + + self.s.word(">"); + self.s.word("::"); + let item_segment = path.segments.last().unwrap(); + self.print_ident(item_segment.ident); + self.print_generic_args( + item_segment.generic_args(), + item_segment.infer_args, + colons_before_params, + ) + } + hir::QPath::TypeRelative(ref qself, ref item_segment) => { + // If we've got a compound-qualified-path, let's push an additional pair of angle + // brackets, so that we pretty-print `<::C>` as `::C`, instead of just + // `A::B::C` (since the latter could be ambiguous to the user) + if let hir::TyKind::Path(hir::QPath::Resolved(None, _)) = &qself.kind { + self.print_type(qself); + } else { + self.s.word("<"); + self.print_type(qself); + self.s.word(">"); + } + + self.s.word("::"); + self.print_ident(item_segment.ident); + self.print_generic_args( + item_segment.generic_args(), + item_segment.infer_args, + colons_before_params, + ) + } + } + } + + fn print_generic_args( + &mut self, + generic_args: &hir::GenericArgs<'_>, + infer_args: bool, + colons_before_params: bool, + ) { + if generic_args.parenthesized { + self.s.word("("); + self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(&ty)); + self.s.word(")"); + + self.space_if_not_bol(); + self.word_space("->"); + self.print_type(generic_args.bindings[0].ty()); + } else { + let start = if colons_before_params { "::<" } else { "<" }; + let empty = Cell::new(true); + let start_or_comma = |this: &mut Self| { + if empty.get() { + empty.set(false); + this.s.word(start) + } else { + this.word_space(",") + } + }; + + let mut nonelided_generic_args: bool = false; + let elide_lifetimes = generic_args.args.iter().all(|arg| match arg { + GenericArg::Lifetime(lt) => lt.is_elided(), + _ => { + nonelided_generic_args = true; + true + } + }); + + if nonelided_generic_args { + start_or_comma(self); + self.commasep( + Inconsistent, + &generic_args.args, + |s, generic_arg| match generic_arg { + GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), + GenericArg::Lifetime(_) => {} + GenericArg::Type(ty) => s.print_type(ty), + GenericArg::Const(ct) => s.print_anon_const(&ct.value), + }, + ); + } + + // FIXME(eddyb): this would leak into error messages (e.g., + // "non-exhaustive patterns: `Some::<..>(_)` not covered"). + if infer_args && false { + start_or_comma(self); + self.s.word(".."); + } + + for binding in generic_args.bindings.iter() { + start_or_comma(self); + self.print_ident(binding.ident); + self.s.space(); + match generic_args.bindings[0].kind { + hir::TypeBindingKind::Equality { ref ty } => { + self.word_space("="); + self.print_type(ty); + } + hir::TypeBindingKind::Constraint { bounds } => { + self.print_bounds(":", bounds); + } + } + } + + if !empty.get() { + self.s.word(">") + } + } + } + + pub 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 + match pat.kind { + PatKind::Wild => self.s.word("_"), + PatKind::Binding(binding_mode, _, ident, ref sub) => { + match binding_mode { + hir::BindingAnnotation::Ref => { + self.word_nbsp("ref"); + self.print_mutability(hir::Mutability::Not, false); + } + hir::BindingAnnotation::RefMut => { + self.word_nbsp("ref"); + self.print_mutability(hir::Mutability::Mut, false); + } + hir::BindingAnnotation::Unannotated => {} + hir::BindingAnnotation::Mutable => { + self.word_nbsp("mut"); + } + } + self.print_ident(ident); + if let Some(ref p) = *sub { + self.s.word("@"); + self.print_pat(&p); + } + } + PatKind::TupleStruct(ref qpath, ref elts, ddpos) => { + self.print_qpath(qpath, true); + self.popen(); + if let Some(ddpos) = ddpos { + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)); + if ddpos != 0 { + self.word_space(","); + } + self.s.word(".."); + if ddpos != elts.len() { + self.s.word(","); + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)); + } + } else { + self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)); + } + self.pclose(); + } + PatKind::Path(ref qpath) => { + self.print_qpath(qpath, true); + } + PatKind::Struct(ref qpath, ref fields, etc) => { + self.print_qpath(qpath, true); + self.nbsp(); + self.word_space("{"); + self.commasep_cmnt( + Consistent, + &fields[..], + |s, f| { + s.cbox(INDENT_UNIT); + if !f.is_shorthand { + s.print_ident(f.ident); + s.word_nbsp(":"); + } + s.print_pat(&f.pat); + s.end() + }, + |f| f.pat.span, + ); + if etc { + if !fields.is_empty() { + self.word_space(","); + } + self.s.word(".."); + } + self.s.space(); + self.s.word("}"); + } + PatKind::Or(ref pats) => { + self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(&p)); + } + PatKind::Tuple(ref elts, ddpos) => { + self.popen(); + if let Some(ddpos) = ddpos { + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)); + if ddpos != 0 { + self.word_space(","); + } + self.s.word(".."); + if ddpos != elts.len() { + self.s.word(","); + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)); + } + } else { + self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)); + if elts.len() == 1 { + self.s.word(","); + } + } + self.pclose(); + } + PatKind::Box(ref inner) => { + let is_range_inner = match inner.kind { + PatKind::Range(..) => true, + _ => false, + }; + self.s.word("box "); + if is_range_inner { + self.popen(); + } + self.print_pat(&inner); + if is_range_inner { + self.pclose(); + } + } + PatKind::Ref(ref inner, mutbl) => { + let is_range_inner = match inner.kind { + PatKind::Range(..) => true, + _ => false, + }; + self.s.word("&"); + self.s.word(mutbl.prefix_str()); + if is_range_inner { + self.popen(); + } + self.print_pat(&inner); + if is_range_inner { + self.pclose(); + } + } + PatKind::Lit(ref e) => self.print_expr(&e), + PatKind::Range(ref begin, ref end, ref end_kind) => { + if let Some(expr) = begin { + self.print_expr(expr); + self.s.space(); + } + match *end_kind { + RangeEnd::Included => self.s.word("..."), + RangeEnd::Excluded => self.s.word(".."), + } + if let Some(expr) = end { + self.print_expr(expr); + } + } + PatKind::Slice(ref before, ref slice, ref after) => { + self.s.word("["); + self.commasep(Inconsistent, &before[..], |s, p| s.print_pat(&p)); + if let Some(ref p) = *slice { + if !before.is_empty() { + self.word_space(","); + } + if let PatKind::Wild = p.kind { + // Print nothing. + } else { + self.print_pat(&p); + } + self.s.word(".."); + if !after.is_empty() { + self.word_space(","); + } + } + self.commasep(Inconsistent, &after[..], |s, p| s.print_pat(&p)); + self.s.word("]"); + } + } + self.ann.post(self, AnnNode::Pat(pat)) + } + + pub fn print_param(&mut self, arg: &hir::Param<'_>) { + self.print_outer_attributes(&arg.attrs); + self.print_pat(&arg.pat); + } + + pub fn print_arm(&mut self, arm: &hir::Arm<'_>) { + // I have no idea why this check is necessary, but here it + // is :( + if arm.attrs.is_empty() { + self.s.space(); + } + self.cbox(INDENT_UNIT); + self.ann.pre(self, AnnNode::Arm(arm)); + self.ibox(0); + self.print_outer_attributes(&arm.attrs); + self.print_pat(&arm.pat); + self.s.space(); + if let Some(ref g) = arm.guard { + match g { + hir::Guard::If(e) => { + self.word_space("if"); + self.print_expr(&e); + self.s.space(); + } + } + } + self.word_space("=>"); + + match arm.body.kind { + hir::ExprKind::Block(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + // the block will close the pattern's ibox + self.print_block_unclosed(&blk); + + // If it is a user-provided unsafe block, print a comma after it + if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = blk.rules + { + self.s.word(","); + } + } + _ => { + self.end(); // close the ibox for the pattern + self.print_expr(&arm.body); + self.s.word(","); + } + } + self.ann.post(self, AnnNode::Arm(arm)); + self.end() // close enclosing cbox + } + + pub fn print_fn( + &mut self, + decl: &hir::FnDecl<'_>, + header: hir::FnHeader, + name: Option, + generics: &hir::Generics<'_>, + vis: &hir::Visibility<'_>, + arg_names: &[Ident], + body_id: Option, + ) { + self.print_fn_header_info(header, vis); + + if let Some(name) = name { + self.nbsp(); + self.print_name(name); + } + self.print_generic_params(&generics.params); + + self.popen(); + let mut i = 0; + // Make sure we aren't supplied *both* `arg_names` and `body_id`. + assert!(arg_names.is_empty() || body_id.is_none()); + self.commasep(Inconsistent, &decl.inputs, |s, ty| { + s.ibox(INDENT_UNIT); + if let Some(arg_name) = arg_names.get(i) { + s.s.word(arg_name.to_string()); + s.s.word(":"); + s.s.space(); + } else if let Some(body_id) = body_id { + s.ann.nested(s, Nested::BodyParamPat(body_id, i)); + s.s.word(":"); + s.s.space(); + } + i += 1; + s.print_type(ty); + s.end() + }); + if decl.c_variadic { + self.s.word(", ..."); + } + self.pclose(); + + self.print_fn_output(decl); + self.print_where_clause(&generics.where_clause) + } + + fn print_closure_params(&mut self, decl: &hir::FnDecl<'_>, body_id: hir::BodyId) { + self.s.word("|"); + let mut i = 0; + self.commasep(Inconsistent, &decl.inputs, |s, ty| { + s.ibox(INDENT_UNIT); + + s.ann.nested(s, Nested::BodyParamPat(body_id, i)); + i += 1; + + if let hir::TyKind::Infer = ty.kind { + // Print nothing. + } else { + s.s.word(":"); + s.s.space(); + s.print_type(ty); + } + s.end(); + }); + self.s.word("|"); + + if let hir::FnRetTy::DefaultReturn(..) = decl.output { + return; + } + + self.space_if_not_bol(); + self.word_space("->"); + match decl.output { + hir::FnRetTy::Return(ref ty) => { + self.print_type(&ty); + self.maybe_print_comment(ty.span.lo()) + } + hir::FnRetTy::DefaultReturn(..) => unreachable!(), + } + } + + pub fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) { + match capture_clause { + hir::CaptureBy::Value => self.word_space("move"), + hir::CaptureBy::Ref => {} + } + } + + pub fn print_bounds<'b>( + &mut self, + prefix: &'static str, + bounds: impl IntoIterator>, + ) { + let mut first = true; + for bound in bounds { + if first { + self.s.word(prefix); + } + if !(first && prefix.is_empty()) { + self.nbsp(); + } + if first { + first = false; + } else { + self.word_space("+"); + } + + match bound { + GenericBound::Trait(tref, modifier) => { + if modifier == &TraitBoundModifier::Maybe { + self.s.word("?"); + } + self.print_poly_trait_ref(tref); + } + GenericBound::Outlives(lt) => { + self.print_lifetime(lt); + } + } + } + } + + pub fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) { + if !generic_params.is_empty() { + self.s.word("<"); + + self.commasep(Inconsistent, generic_params, |s, param| s.print_generic_param(param)); + + self.s.word(">"); + } + } + + pub fn print_generic_param(&mut self, param: &GenericParam<'_>) { + if let GenericParamKind::Const { .. } = param.kind { + self.word_space("const"); + } + + self.print_ident(param.name.ident()); + + match param.kind { + GenericParamKind::Lifetime { .. } => { + let mut sep = ":"; + for bound in param.bounds { + match bound { + GenericBound::Outlives(ref lt) => { + self.s.word(sep); + self.print_lifetime(lt); + sep = "+"; + } + _ => panic!(), + } + } + } + GenericParamKind::Type { ref default, .. } => { + self.print_bounds(":", param.bounds); + if let Some(default) = default { + self.s.space(); + self.word_space("="); + self.print_type(&default) + } + } + GenericParamKind::Const { ref ty } => { + self.word_space(":"); + self.print_type(ty) + } + } + } + + pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) { + self.print_ident(lifetime.name.ident()) + } + + pub fn print_where_clause(&mut self, where_clause: &hir::WhereClause<'_>) { + if where_clause.predicates.is_empty() { + return; + } + + self.s.space(); + self.word_space("where"); + + for (i, predicate) in where_clause.predicates.iter().enumerate() { + if i != 0 { + self.word_space(","); + } + + match predicate { + &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + ref bound_generic_params, + ref bounded_ty, + bounds, + .. + }) => { + self.print_formal_generic_params(bound_generic_params); + self.print_type(&bounded_ty); + self.print_bounds(":", bounds); + } + &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + ref lifetime, + ref bounds, + .. + }) => { + self.print_lifetime(lifetime); + self.s.word(":"); + + for (i, bound) in bounds.iter().enumerate() { + match bound { + GenericBound::Outlives(lt) => { + self.print_lifetime(lt); + } + _ => panic!(), + } + + if i != 0 { + self.s.word(":"); + } + } + } + &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + ref lhs_ty, + ref rhs_ty, + .. + }) => { + self.print_type(lhs_ty); + self.s.space(); + self.word_space("="); + self.print_type(rhs_ty); + } + } + } + } + + pub fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) { + match mutbl { + hir::Mutability::Mut => self.word_nbsp("mut"), + hir::Mutability::Not => { + if print_const { + self.word_nbsp("const") + } + } + } + } + + pub fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) { + self.print_mutability(mt.mutbl, print_const); + self.print_type(&mt.ty) + } + + pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) { + if let hir::FnRetTy::DefaultReturn(..) = decl.output { + return; + } + + self.space_if_not_bol(); + self.ibox(INDENT_UNIT); + self.word_space("->"); + match decl.output { + hir::FnRetTy::DefaultReturn(..) => unreachable!(), + hir::FnRetTy::Return(ref ty) => self.print_type(&ty), + } + self.end(); + + if let hir::FnRetTy::Return(ref output) = decl.output { + self.maybe_print_comment(output.span.lo()) + } + } + + pub fn print_ty_fn( + &mut self, + abi: Abi, + unsafety: hir::Unsafety, + decl: &hir::FnDecl<'_>, + name: Option, + generic_params: &[hir::GenericParam<'_>], + arg_names: &[Ident], + ) { + self.ibox(INDENT_UNIT); + if !generic_params.is_empty() { + self.s.word("for"); + self.print_generic_params(generic_params); + } + let generics = hir::Generics { + params: &[], + where_clause: hir::WhereClause { predicates: &[], span: rustc_span::DUMMY_SP }, + span: rustc_span::DUMMY_SP, + }; + self.print_fn( + decl, + hir::FnHeader { + unsafety, + abi, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + }, + name, + &generics, + &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited }, + arg_names, + None, + ); + self.end(); + } + + pub fn maybe_print_trailing_comment( + &mut self, + span: rustc_span::Span, + next_pos: Option, + ) { + if let Some(cmnts) = self.comments() { + if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) { + self.print_comment(&cmnt); + } + } + } + + pub fn print_remaining_comments(&mut self) { + // If there aren't any remaining comments, then we need to manually + // make sure there is a line break at the end. + if self.next_comment().is_none() { + self.s.hardbreak(); + } + while let Some(ref cmnt) = self.next_comment() { + self.print_comment(cmnt) + } + } + + pub fn print_opt_abi_and_extern_if_nondefault(&mut self, opt_abi: Option) { + match opt_abi { + Some(Abi::Rust) => {} + Some(abi) => { + self.word_nbsp("extern"); + self.word_nbsp(abi.to_string()) + } + None => {} + } + } + + pub fn print_extern_opt_abi(&mut self, opt_abi: Option) { + if let Some(abi) = opt_abi { + self.word_nbsp("extern"); + self.word_nbsp(abi.to_string()) + } + } + + pub fn print_fn_header_info(&mut self, header: hir::FnHeader, vis: &hir::Visibility<'_>) { + self.s.word(visibility_qualified(vis, "")); + + match header.constness { + hir::Constness::NotConst => {} + hir::Constness::Const => self.word_nbsp("const"), + } + + match header.asyncness { + hir::IsAsync::NotAsync => {} + hir::IsAsync::Async => self.word_nbsp("async"), + } + + self.print_unsafety(header.unsafety); + + if header.abi != Abi::Rust { + self.word_nbsp("extern"); + self.word_nbsp(header.abi.to_string()); + } + + self.s.word("fn") + } + + pub fn print_unsafety(&mut self, s: hir::Unsafety) { + match s { + hir::Unsafety::Normal => {} + hir::Unsafety::Unsafe => self.word_nbsp("unsafe"), + } + } + + pub fn print_is_auto(&mut self, s: hir::IsAuto) { + match s { + hir::IsAuto::Yes => self.word_nbsp("auto"), + hir::IsAuto::No => {} + } + } +} + +/// Does this expression require a semicolon to be treated +/// as a statement? The negation of this: 'can this expression +/// be used as a statement without a semicolon' -- is used +/// as an early-bail-out in the parser so that, for instance, +/// if true {...} else {...} +/// |x| 5 +/// isn't parsed as (if true {...} else {...} | x) | 5 +// +// Duplicated from `parse::classify`, but adapted for the HIR. +fn expr_requires_semi_to_be_stmt(e: &hir::Expr<'_>) -> bool { + match e.kind { + hir::ExprKind::Match(..) | hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) => false, + _ => true, + } +} + +/// This statement requires a semicolon after it. +/// note that in one case (stmt_semi), we've already +/// seen the semicolon, and thus don't need another. +fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool { + match *stmt { + hir::StmtKind::Local(_) => true, + hir::StmtKind::Item(_) => false, + hir::StmtKind::Expr(ref e) => expr_requires_semi_to_be_stmt(&e), + hir::StmtKind::Semi(..) => false, + } +} + +fn bin_op_to_assoc_op(op: hir::BinOpKind) -> AssocOp { + use crate::hir::BinOpKind::*; + match op { + Add => AssocOp::Add, + Sub => AssocOp::Subtract, + Mul => AssocOp::Multiply, + Div => AssocOp::Divide, + Rem => AssocOp::Modulus, + + And => AssocOp::LAnd, + Or => AssocOp::LOr, + + BitXor => AssocOp::BitXor, + BitAnd => AssocOp::BitAnd, + BitOr => AssocOp::BitOr, + Shl => AssocOp::ShiftLeft, + Shr => AssocOp::ShiftRight, + + Eq => AssocOp::Equal, + Lt => AssocOp::Less, + Le => AssocOp::LessEqual, + Ne => AssocOp::NotEqual, + Ge => AssocOp::GreaterEqual, + Gt => AssocOp::Greater, + } +} + +/// Expressions that syntactically contain an "exterior" struct literal, i.e., not surrounded by any +/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and +/// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. +fn contains_exterior_struct_lit(value: &hir::Expr<'_>) -> bool { + match value.kind { + hir::ExprKind::Struct(..) => true, + + hir::ExprKind::Assign(ref lhs, ref rhs, _) + | hir::ExprKind::AssignOp(_, ref lhs, ref rhs) + | hir::ExprKind::Binary(_, ref lhs, ref rhs) => { + // `X { y: 1 } + X { y: 2 }` + contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) + } + hir::ExprKind::Unary(_, ref x) + | hir::ExprKind::Cast(ref x, _) + | hir::ExprKind::Type(ref x, _) + | hir::ExprKind::Field(ref x, _) + | hir::ExprKind::Index(ref x, _) => { + // `&X { y: 1 }, X { y: 1 }.y` + contains_exterior_struct_lit(&x) + } + + hir::ExprKind::MethodCall(.., ref exprs) => { + // `X { y: 1 }.bar(...)` + contains_exterior_struct_lit(&exprs[0]) + } + + _ => false, + } +} diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml index eddfb81ea8b0a..5caf1d411e637 100644 --- a/src/librustc_incremental/Cargo.toml +++ b/src/librustc_incremental/Cargo.toml @@ -13,7 +13,7 @@ doctest = false graphviz = { path = "../libgraphviz" } log = "0.4" rand = "0.7" -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_hir = { path = "../librustc_hir" } rustc_serialize = { path = "../libserialize", package = "serialize" } diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs index 305e0fcc383ad..807ae586348e9 100644 --- a/src/librustc_incremental/assert_dep_graph.rs +++ b/src/librustc_incremental/assert_dep_graph.rs @@ -34,17 +34,17 @@ //! ``` use graphviz as dot; -use rustc::dep_graph::debug::{DepNodeFilter, EdgeFilter}; -use rustc::dep_graph::{DepGraphQuery, DepKind, DepNode, DepNodeExt}; -use rustc::hir::map::Map; -use rustc::ty::TyCtxt; use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_span::symbol::sym; +use rustc_middle::dep_graph::debug::{DepNodeFilter, EdgeFilter}; +use rustc_middle::dep_graph::{DepGraphQuery, DepKind, DepNode, DepNodeExt}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use std::env; @@ -89,7 +89,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) { } type Sources = Vec<(Span, DefId, DepNode)>; -type Targets = Vec<(Span, ast::Name, hir::HirId, DepNode)>; +type Targets = Vec<(Span, Symbol, hir::HirId, DepNode)>; struct IfThisChanged<'tcx> { tcx: TyCtxt<'tcx>, @@ -98,7 +98,7 @@ struct IfThisChanged<'tcx> { } impl IfThisChanged<'tcx> { - fn argument(&self, attr: &ast::Attribute) -> Option { + fn argument(&self, attr: &ast::Attribute) -> Option { let mut value = None; for list_item in attr.meta_item_list().unwrap_or_default() { match list_item.ident() { @@ -115,7 +115,7 @@ impl IfThisChanged<'tcx> { fn process_attrs(&mut self, hir_id: hir::HirId, attrs: &[ast::Attribute]) { let def_id = self.tcx.hir().local_def_id(hir_id); - let def_path_hash = self.tcx.def_path_hash(def_id); + let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id()); for attr in attrs { if attr.check_name(sym::rustc_if_this_changed) { let dep_node_interned = self.argument(attr); @@ -131,7 +131,7 @@ impl IfThisChanged<'tcx> { } }, }; - self.if_this_changed.push((attr.span, def_id, dep_node)); + self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node)); } else if attr.check_name(sym::rustc_then_this_would_need) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs index c5446116f4c50..eee6e73ed1073 100644 --- a/src/librustc_incremental/assert_module_sources.rs +++ b/src/librustc_incremental/assert_module_sources.rs @@ -21,10 +21,10 @@ //! allows for doing a more fine-grained check to see if pre- or post-lto data //! was re-used. -use rustc::mir::mono::CodegenUnitNameBuilder; -use rustc::ty::TyCtxt; use rustc_ast::ast; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::mir::mono::CodegenUnitNameBuilder; +use rustc_middle::ty::TyCtxt; use rustc_session::cgu_reuse_tracker::*; use rustc_span::symbol::{sym, Symbol}; use std::collections::BTreeSet; @@ -147,7 +147,7 @@ impl AssertModuleSource<'tcx> { ); } - fn field(&self, attr: &ast::Attribute, name: Symbol) -> ast::Name { + fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol { for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(name) { if let Some(value) = item.value_str() { diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index ca824fde7efc1..7fd4b3c2554f3 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -6,7 +6,7 @@ #![recursion_limit = "256"] #[macro_use] -extern crate rustc; +extern crate rustc_middle; #[macro_use] extern crate log; @@ -15,7 +15,7 @@ pub mod assert_module_sources; mod persist; pub use assert_dep_graph::assert_dep_graph; -pub use persist::copy_cgu_workproducts_to_incr_comp_cache_dir; +pub use persist::copy_cgu_workproduct_to_incr_comp_cache_dir; pub use persist::delete_workproduct_files; pub use persist::dep_graph_tcx_init; pub use persist::finalize_session_directory; diff --git a/src/librustc_incremental/persist/data.rs b/src/librustc_incremental/persist/data.rs index 49b4bb061141c..ea0fd4eb7ee73 100644 --- a/src/librustc_incremental/persist/data.rs +++ b/src/librustc_incremental/persist/data.rs @@ -1,6 +1,6 @@ //! The data that we will serialize and deserialize. -use rustc::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct SerializedWorkProduct { diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index 9ddd238afff2b..9bf992537dfaf 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -13,9 +13,6 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. -use rustc::dep_graph::{label_strs, DepNode, DepNodeExt}; -use rustc::hir::map::Map; -use rustc::ty::TyCtxt; use rustc_ast::ast::{self, Attribute, NestedMetaItem}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashSet; @@ -25,6 +22,9 @@ use rustc_hir::intravisit; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Node as HirNode; use rustc_hir::{ImplItemKind, ItemKind as HirItem, TraitItemKind}; +use rustc_middle::dep_graph::{label_strs, DepNode, DepNodeExt}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use std::iter::FromIterator; @@ -306,7 +306,7 @@ impl DirtyCleanVisitor<'tcx> { // michaelwoerister and vitiral came up with a possible solution, // to just do this before every query // ``` - // ::rustc::ty::query::plumbing::force_from_dep_node(tcx, dep_node) + // ::rustc_middle::ty::query::plumbing::force_from_dep_node(tcx, dep_node) // ``` // // However, this did not seem to work effectively and more bugs were hit. @@ -434,16 +434,16 @@ impl DirtyCleanVisitor<'tcx> { fn check_item(&mut self, item_id: hir::HirId, item_span: Span) { let def_id = self.tcx.hir().local_def_id(item_id); - for attr in self.tcx.get_attrs(def_id).iter() { + for attr in self.tcx.get_attrs(def_id.to_def_id()).iter() { let assertion = match self.assertion_maybe(item_id, attr) { Some(a) => a, None => continue, }; self.checked_attrs.insert(attr.id); - for dep_node in self.dep_nodes(&assertion.clean, def_id) { + for dep_node in self.dep_nodes(&assertion.clean, def_id.to_def_id()) { self.assert_clean(item_span, dep_node); } - for dep_node in self.dep_nodes(&assertion.dirty, def_id) { + for dep_node in self.dep_nodes(&assertion.dirty, def_id.to_def_id()) { self.assert_dirty(item_span, dep_node); } } @@ -499,7 +499,7 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { } } -fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> ast::Name { +fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> Symbol { if let Some(value) = item.value_str() { value } else { diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 8a11586250dec..966faa9639d72 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -1,9 +1,9 @@ //! Code to save/load the dep-graph from files. -use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; -use rustc::ty::query::OnDiskCache; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; +use rustc_middle::ty::query::OnDiskCache; +use rustc_middle::ty::TyCtxt; use rustc_serialize::opaque::Decoder; use rustc_serialize::Decodable as RustcDecodable; use rustc_session::Session; @@ -134,7 +134,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { for swp in work_products { let mut all_files_exist = true; - for &(_, ref file_name) in swp.work_product.saved_files.iter() { + if let Some(ref file_name) = swp.work_product.saved_file { let path = in_incr_comp_dir_sess(sess, file_name); if !path.exists() { all_files_exist = false; @@ -195,7 +195,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { } pub fn load_query_result_cache(sess: &Session) -> OnDiskCache<'_> { - if sess.opts.incremental.is_none() || !sess.opts.debugging_opts.incremental_queries { + if sess.opts.incremental.is_none() { return OnDiskCache::new_empty(sess.source_map()); } diff --git a/src/librustc_incremental/persist/mod.rs b/src/librustc_incremental/persist/mod.rs index 6a03a01430b0f..7bc3b47e15adf 100644 --- a/src/librustc_incremental/persist/mod.rs +++ b/src/librustc_incremental/persist/mod.rs @@ -21,5 +21,5 @@ pub use load::LoadResult; pub use load::{load_dep_graph, DepGraphFuture}; pub use save::save_dep_graph; pub use save::save_work_product_index; -pub use work_product::copy_cgu_workproducts_to_incr_comp_cache_dir; +pub use work_product::copy_cgu_workproduct_to_incr_comp_cache_dir; pub use work_product::delete_workproduct_files; diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index ba586d0cfba04..c43d4ad4049c9 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -1,7 +1,7 @@ -use rustc::dep_graph::{DepGraph, DepKind, WorkProduct, WorkProductId}; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::join; +use rustc_middle::dep_graph::{DepGraph, DepKind, WorkProduct, WorkProductId}; +use rustc_middle::ty::TyCtxt; use rustc_serialize::opaque::Encoder; use rustc_serialize::Encodable as RustcEncodable; use rustc_session::Session; @@ -31,11 +31,9 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { join( move || { - if tcx.sess.opts.debugging_opts.incremental_queries { - sess.time("incr_comp_persist_result_cache", || { - save_in(sess, query_cache_path, |e| encode_query_cache(tcx, e)); - }); - } + sess.time("incr_comp_persist_result_cache", || { + save_in(sess, query_cache_path, |e| encode_query_cache(tcx, e)); + }); }, || { sess.time("incr_comp_persist_dep_graph", || { @@ -76,8 +74,8 @@ pub fn save_work_product_index( if !new_work_products.contains_key(id) { work_product::delete_workproduct_files(sess, wp); debug_assert!( - wp.saved_files.iter().all(|&(_, ref file_name)| { - !in_incr_comp_dir_sess(sess, file_name).exists() + wp.saved_file.as_ref().map_or(true, |file_name| { + !in_incr_comp_dir_sess(sess, &file_name).exists() }) ); } @@ -87,7 +85,7 @@ pub fn save_work_product_index( debug_assert!({ new_work_products .iter() - .flat_map(|(_, wp)| wp.saved_files.iter().map(|&(_, ref name)| name)) + .flat_map(|(_, wp)| wp.saved_file.iter()) .map(|name| in_incr_comp_dir_sess(sess, name)) .all(|path| path.exists()) }); diff --git a/src/librustc_incremental/persist/work_product.rs b/src/librustc_incremental/persist/work_product.rs index aa3588b284b28..19d64bda56d6d 100644 --- a/src/librustc_incremental/persist/work_product.rs +++ b/src/librustc_incremental/persist/work_product.rs @@ -1,54 +1,47 @@ //! This module contains files for saving intermediate work-products. use crate::persist::fs::*; -use rustc::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId}; use rustc_fs_util::link_or_copy; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; use std::fs as std_fs; use std::path::PathBuf; -pub fn copy_cgu_workproducts_to_incr_comp_cache_dir( +pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( sess: &Session, cgu_name: &str, - files: &[(WorkProductFileKind, PathBuf)], + path: &Option, ) -> Option<(WorkProductId, WorkProduct)> { - debug!("copy_cgu_workproducts_to_incr_comp_cache_dir({:?},{:?})", cgu_name, files); + debug!("copy_cgu_workproduct_to_incr_comp_cache_dir({:?},{:?})", cgu_name, path); sess.opts.incremental.as_ref()?; - let saved_files = files - .iter() - .map(|&(kind, ref path)| { - let extension = match kind { - WorkProductFileKind::Object => "o", - WorkProductFileKind::Bytecode => "bc", - WorkProductFileKind::BytecodeCompressed => "bc.z", - }; - let file_name = format!("{}.{}", cgu_name, extension); - let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); - match link_or_copy(path, &path_in_incr_dir) { - Ok(_) => Some((kind, file_name)), - Err(err) => { - sess.warn(&format!( - "error copying object file `{}` \ - to incremental directory as `{}`: {}", - path.display(), - path_in_incr_dir.display(), - err - )); - None - } + let saved_file = if let Some(path) = path { + let file_name = format!("{}.o", cgu_name); + let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); + match link_or_copy(path, &path_in_incr_dir) { + Ok(_) => Some(file_name), + Err(err) => { + sess.warn(&format!( + "error copying object file `{}` to incremental directory as `{}`: {}", + path.display(), + path_in_incr_dir.display(), + err + )); + return None; } - }) - .collect::>>()?; + } + } else { + None + }; - let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_files }; + let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_file }; let work_product_id = WorkProductId::from_cgu_name(cgu_name); Some((work_product_id, work_product)) } pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - for &(_, ref file_name) in &work_product.saved_files { + if let Some(ref file_name) = work_product.saved_file { let path = in_incr_comp_dir_sess(sess, file_name); match std_fs::remove_file(&path) { Ok(()) => {} diff --git a/src/librustc_index/bit_set.rs b/src/librustc_index/bit_set.rs index 2a1a56076728e..46c38840516e2 100644 --- a/src/librustc_index/bit_set.rs +++ b/src/librustc_index/bit_set.rs @@ -307,7 +307,7 @@ impl<'a, T: Idx> BitIter<'a, T> { // additional state about whether we have started. BitIter { word: 0, - offset: std::usize::MAX - (WORD_BITS - 1), + offset: usize::MAX - (WORD_BITS - 1), iter: words.iter(), marker: PhantomData, } diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs index d8c67f6210c75..67dcea58cf82b 100644 --- a/src/librustc_index/vec.rs +++ b/src/librustc_index/vec.rs @@ -7,7 +7,6 @@ use std::iter::{self, FromIterator}; use std::marker::PhantomData; use std::ops::{Index, IndexMut, Range, RangeBounds}; use std::slice; -use std::u32; use std::vec; /// Represents some newtyped `usize` wrapper. @@ -66,7 +65,7 @@ impl Idx for u32 { /// `u32::MAX`. You can also customize things like the `Debug` impl, /// what traits are derived, and so forth via the macro. #[macro_export] -#[allow_internal_unstable(step_trait, rustc_attrs)] +#[allow_internal_unstable(step_trait, step_trait_ext, rustc_attrs)] macro_rules! newtype_index { // ---- public rules ---- @@ -182,7 +181,7 @@ macro_rules! newtype_index { } } - impl ::std::iter::Step for $type { + unsafe impl ::std::iter::Step for $type { #[inline] fn steps_between(start: &Self, end: &Self) -> Option { ::steps_between( @@ -192,33 +191,13 @@ macro_rules! newtype_index { } #[inline] - fn replace_one(&mut self) -> Self { - ::std::mem::replace(self, Self::from_u32(1)) + fn forward_checked(start: Self, u: usize) -> Option { + Self::index(start).checked_add(u).map(Self::from_usize) } #[inline] - fn replace_zero(&mut self) -> Self { - ::std::mem::replace(self, Self::from_u32(0)) - } - - #[inline] - fn add_one(&self) -> Self { - Self::from_usize(Self::index(*self) + 1) - } - - #[inline] - fn sub_one(&self) -> Self { - Self::from_usize(Self::index(*self) - 1) - } - - #[inline] - fn add_usize(&self, u: usize) -> Option { - Self::index(*self).checked_add(u).map(Self::from_usize) - } - - #[inline] - fn sub_usize(&self, u: usize) -> Option { - Self::index(*self).checked_sub(u).map(Self::from_usize) + fn backward_checked(start: Self, u: usize) -> Option { + Self::index(start).checked_sub(u).map(Self::from_usize) } } diff --git a/src/librustc_infer/Cargo.toml b/src/librustc_infer/Cargo.toml index 9ecd056430c20..fa8e5a2ab78b4 100644 --- a/src/librustc_infer/Cargo.toml +++ b/src/librustc_infer/Cargo.toml @@ -12,13 +12,14 @@ doctest = false [dependencies] graphviz = { path = "../libgraphviz" } log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_hir = { path = "../librustc_hir" } rustc_index = { path = "../librustc_index" } rustc_macros = { path = "../librustc_macros" } rustc_session = { path = "../librustc_session" } +rustc_serialize = { path = "../libserialize", package = "serialize" } rustc_span = { path = "../librustc_span" } rustc_target = { path = "../librustc_target" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_infer/infer/at.rs b/src/librustc_infer/infer/at.rs index 04f5b03c0e15c..d44b8f554143c 100644 --- a/src/librustc_infer/infer/at.rs +++ b/src/librustc_infer/infer/at.rs @@ -27,8 +27,8 @@ use super::*; -use rustc::ty::relate::{Relate, TypeRelation}; -use rustc::ty::Const; +use rustc_middle::ty::relate::{Relate, TypeRelation}; +use rustc_middle::ty::Const; pub struct At<'a, 'tcx> { pub infcx: &'a InferCtxt<'a, 'tcx>, @@ -186,7 +186,6 @@ impl<'a, 'tcx> At<'a, 'tcx> { impl<'a, 'tcx> Trace<'a, 'tcx> { /// Makes `a <: b` where `a` may or may not be expected (if /// `a_is_expected` is true, then `a` is expected). - /// Makes `expected <: actual`. pub fn sub(self, a: &T, b: &T) -> InferResult<'tcx, ()> where T: Relate<'tcx>, diff --git a/src/librustc_infer/infer/canonical/canonicalizer.rs b/src/librustc_infer/infer/canonical/canonicalizer.rs index 4d9a81d440695..5551b56ab797b 100644 --- a/src/librustc_infer/infer/canonical/canonicalizer.rs +++ b/src/librustc_infer/infer/canonical/canonicalizer.rs @@ -3,17 +3,17 @@ //! For an overview of what canonicalization is and how it fits into //! rustc, check out the [chapter in the rustc dev guide][c]. //! -//! [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::canonical::{ Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized, OriginalQueryValues, }; use crate::infer::InferCtxt; -use rustc::ty::flags::FlagComputation; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::subst::GenericArg; -use rustc::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags}; +use rustc_middle::ty::flags::FlagComputation; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags}; use std::sync::atomic::Ordering; use rustc_data_structures::fx::FxHashMap; @@ -35,7 +35,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// To get a good understanding of what is happening here, check /// out the [chapter in the rustc dev guide][c]. /// - /// [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html#canonicalizing-the-query + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query pub fn canonicalize_query( &self, value: &V, @@ -79,7 +79,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// To get a good understanding of what is happening here, check /// out the [chapter in the rustc dev guide][c]. /// - /// [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html#canonicalizing-the-query-result + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result pub fn canonicalize_response(&self, value: &V) -> Canonicalized<'tcx, V> where V: TypeFoldable<'tcx>, @@ -353,8 +353,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `TyVar(vid)` is unresolved, track its universe index in the canonicalized // result. Err(mut ui) => { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; + if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } self.canonicalize_ty_var( CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), @@ -375,9 +377,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { t, ), - ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("encountered a fresh type during canonicalization") } @@ -415,7 +415,6 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { | ty::Never | ty::Tuple(..) | ty::Projection(..) - | ty::UnnormalizedProjection(..) | ty::Foreign(..) | ty::Param(..) | ty::Opaque(..) => { @@ -441,8 +440,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `ConstVar(vid)` is unresolved, track its universe index in the // canonicalized result Err(mut ui) => { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; + if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } return self.canonicalize_const_var( CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, ct, @@ -488,12 +489,12 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { V: TypeFoldable<'tcx>, { let needs_canonical_flags = if canonicalize_region_mode.any() { - TypeFlags::KEEP_IN_LOCAL_TCX | + TypeFlags::NEEDS_INFER | TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER } else { - TypeFlags::KEEP_IN_LOCAL_TCX + TypeFlags::NEEDS_INFER | TypeFlags::HAS_RE_PLACEHOLDER | TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER @@ -524,7 +525,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { // Once we have canonicalized `out_value`, it should not // contain anything that ties it to this inference context // anymore, so it should live in the global arena. - debug_assert!(!out_value.has_type_flags(TypeFlags::KEEP_IN_LOCAL_TCX)); + debug_assert!(!out_value.needs_infer()); let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables); diff --git a/src/librustc_infer/infer/canonical/mod.rs b/src/librustc_infer/infer/canonical/mod.rs index 0e9593e8ea6b0..7f58b29a73f36 100644 --- a/src/librustc_infer/infer/canonical/mod.rs +++ b/src/librustc_infer/infer/canonical/mod.rs @@ -19,17 +19,17 @@ //! For a more detailed look at what is happening here, check //! out the [chapter in the rustc dev guide][c]. //! -//! [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind}; use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::GenericArg; -use rustc::ty::{self, BoundVar, List}; use rustc_index::vec::IndexVec; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundVar, List}; use rustc_span::source_map::Span; -pub use rustc::infer::canonical::*; +pub use rustc_middle::infer::canonical::*; use substitute::CanonicalExt; mod canonicalizer; diff --git a/src/librustc_infer/infer/canonical/query_response.rs b/src/librustc_infer/infer/canonical/query_response.rs index 9322df4823511..c7a7cf89b4f1b 100644 --- a/src/librustc_infer/infer/canonical/query_response.rs +++ b/src/librustc_infer/infer/canonical/query_response.rs @@ -5,7 +5,7 @@ //! For an overview of what canonicaliation is and how it fits into //! rustc, check out the [chapter in the rustc dev guide][c]. //! -//! [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::canonical::substitute::{substitute_value, CanonicalExt}; use crate::infer::canonical::{ @@ -16,16 +16,16 @@ use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelating use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::{InferCtxt, InferOk, InferResult, NLLRegionVariableOrigin}; use crate::traits::query::{Fallible, NoSolution}; -use crate::traits::{DomainGoal, TraitEngine}; +use crate::traits::TraitEngine; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; -use rustc::arena::ArenaAllocatable; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::relate::TypeRelation; -use rustc::ty::subst::{GenericArg, GenericArgKind}; -use rustc::ty::{self, BoundVar, Ty, TyCtxt}; use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_index::vec::IndexVec; +use rustc_middle::arena::ArenaAllocatable; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, BoundVar, Const, Ty, TyCtxt}; use std::fmt::Debug; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { @@ -154,7 +154,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// To get a good understanding of what is happening here, check /// out the [chapter in the rustc dev guide][c]. /// - /// [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html#processing-the-canonicalized-query-result + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#processing-the-canonicalized-query-result pub fn instantiate_query_response_and_region_obligations( &self, cause: &ObligationCause<'tcx>, @@ -671,8 +671,11 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { }); } - fn push_domain_goal(&mut self, _: DomainGoal<'tcx>) { - bug!("should never be invoked with eager normalization") + fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) { + span_bug!( + self.cause.span(self.infcx.tcx), + "lazy_normalization_consts: unreachable `const_equate`" + ); } fn normalization() -> NormalizationStrategy { diff --git a/src/librustc_infer/infer/canonical/substitute.rs b/src/librustc_infer/infer/canonical/substitute.rs index afef32c1ffebb..65791f6fc6523 100644 --- a/src/librustc_infer/infer/canonical/substitute.rs +++ b/src/librustc_infer/infer/canonical/substitute.rs @@ -4,12 +4,12 @@ //! For an overview of what canonicalization is and how it fits into //! rustc, check out the [chapter in the rustc dev guide][c]. //! -//! [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::GenericArgKind; -use rustc::ty::{self, TyCtxt}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, TyCtxt}; pub(super) trait CanonicalExt<'tcx, V> { /// Instantiate the wrapped value, replacing each canonical value diff --git a/src/librustc_infer/infer/combine.rs b/src/librustc_infer/infer/combine.rs index 0f5d4d30a2385..3467457b44997 100644 --- a/src/librustc_infer/infer/combine.rs +++ b/src/librustc_infer/infer/combine.rs @@ -34,13 +34,13 @@ use super::{InferCtxt, MiscVariable, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; -use rustc::ty::error::TypeError; -use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, InferConst, Ty, TyCtxt}; -use rustc::ty::{IntType, UintType}; use rustc_ast::ast; use rustc_hir::def_id::DefId; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{IntType, UintType}; use rustc_span::{Span, DUMMY_SP}; #[derive(Clone)] @@ -76,7 +76,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { self.inner .borrow_mut() - .int_unification_table + .int_unification_table() .unify_var_var(a_id, b_id) .map_err(|e| int_unification_error(a_is_expected, e))?; Ok(a) @@ -98,7 +98,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => { self.inner .borrow_mut() - .float_unification_table + .float_unification_table() .unify_var_var(a_id, b_id) .map_err(|e| float_unification_error(relation.a_is_expected(), e))?; Ok(a) @@ -126,15 +126,15 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { b: &'tcx ty::Const<'tcx>, ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> where - R: TypeRelation<'tcx>, + R: ConstEquateRelation<'tcx>, { debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); if a == b { return Ok(a); } - let a = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table, a); - let b = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table, b); + let a = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), a); + let b = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), b); let a_is_expected = relation.a_is_expected(); @@ -145,7 +145,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) => { self.inner .borrow_mut() - .const_unification_table + .const_unification_table() .unify_var_var(a_vid, b_vid) .map_err(|e| const_unification_error(a_is_expected, e))?; return Ok(a); @@ -164,7 +164,22 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { (_, ty::ConstKind::Infer(InferConst::Var(vid))) => { return self.unify_const_variable(!a_is_expected, vid, a); } - + (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => { + // FIXME(#59490): Need to remove the leak check to accomodate + // escaping bound variables here. + if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { + relation.const_equate_obligation(a, b); + } + return Ok(b); + } + (_, ty::ConstKind::Unevaluated(..)) if self.tcx.lazy_normalization() => { + // FIXME(#59490): Need to remove the leak check to accomodate + // escaping bound variables here. + if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { + relation.const_equate_obligation(a, b); + } + return Ok(a); + } _ => {} } @@ -179,7 +194,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { self.inner .borrow_mut() - .const_unification_table + .const_unification_table() .unify_var_value( vid, ConstVarValue { @@ -202,7 +217,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) -> RelateResult<'tcx, Ty<'tcx>> { self.inner .borrow_mut() - .int_unification_table + .int_unification_table() .unify_var_value(vid, Some(val)) .map_err(|e| int_unification_error(vid_is_expected, e))?; match val { @@ -219,7 +234,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) -> RelateResult<'tcx, Ty<'tcx>> { self.inner .borrow_mut() - .float_unification_table + .float_unification_table() .unify_var_value(vid, Some(ty::FloatVarValue(val))) .map_err(|e| float_unification_error(vid_is_expected, e))?; Ok(self.tcx.mk_mach_float(val)) @@ -266,7 +281,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { use self::RelationDir::*; // Get the actual variable that b_vid has been inferred to - debug_assert!(self.infcx.inner.borrow_mut().type_variables.probe(b_vid).is_unknown()); + debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown()); debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid); @@ -286,7 +301,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { "instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})", a_ty, dir, b_vid, b_ty ); - self.infcx.inner.borrow_mut().type_variables.instantiate(b_vid, b_ty); + self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty); if needs_wf { self.obligations.push(Obligation::new( @@ -344,7 +359,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { debug!("generalize: ambient_variance = {:?}", ambient_variance); - let for_universe = match self.infcx.inner.borrow_mut().type_variables.probe(for_vid) { + let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) { v @ TypeVariableValue::Known { .. } => { panic!("instantiating {:?} which has a known value {:?}", for_vid, v,) } @@ -356,7 +371,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { let mut generalize = Generalizer { infcx: self.infcx, span: self.trace.cause.span, - for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables.sub_root_var(for_vid), + for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), for_universe, ambient_variance, needs_wf: false, @@ -375,6 +390,20 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { debug!("generalize: success {{ {:?}, {:?} }}", ty, needs_wf); Ok(Generalization { ty, needs_wf }) } + + pub fn add_const_equate_obligation( + &mut self, + a_is_expected: bool, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) { + let predicate = if a_is_expected { + ty::Predicate::ConstEquate(a, b) + } else { + ty::Predicate::ConstEquate(b, a) + }; + self.obligations.push(Obligation::new(self.trace.cause.clone(), self.param_env, predicate)); + } } struct Generalizer<'cx, 'tcx> { @@ -508,14 +537,14 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { // us from creating infinitely sized types. match t.kind { ty::Infer(ty::TyVar(vid)) => { - let vid = self.infcx.inner.borrow_mut().type_variables.root_var(vid); - let sub_vid = self.infcx.inner.borrow_mut().type_variables.sub_root_var(vid); + let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid); + let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid); if sub_vid == self.for_vid_sub_root { // If sub-roots are equal, then `for_vid` and // `vid` are related via subtyping. Err(TypeError::CyclicTy(self.root_ty)) } else { - let probe = self.infcx.inner.borrow_mut().type_variables.probe(vid); + let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid); match probe { TypeVariableValue::Known { value: u } => { debug!("generalize: known value {:?}", u); @@ -542,12 +571,13 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } let origin = - *self.infcx.inner.borrow_mut().type_variables.var_origin(vid); - let new_var_id = self.infcx.inner.borrow_mut().type_variables.new_var( - self.for_universe, - false, - origin, - ); + *self.infcx.inner.borrow_mut().type_variables().var_origin(vid); + let new_var_id = self + .infcx + .inner + .borrow_mut() + .type_variables() + .new_var(self.for_universe, false, origin); let u = self.tcx().mk_ty_var(new_var_id); debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); Ok(u) @@ -555,7 +585,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } } - ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { // No matter what mode we are in, // integer/floating-point types must be equal to be // relatable. @@ -618,7 +648,8 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { match c.val { ty::ConstKind::Infer(InferConst::Var(vid)) => { - let variable_table = &mut self.infcx.inner.borrow_mut().const_unification_table; + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); let var_value = variable_table.probe_value(vid); match var_value.val { ConstVariableValue::Known { value: u } => self.relate(&u, &u), @@ -635,11 +666,19 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } } + ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c), _ => relate::super_relate_consts(self, c, c), } } } +pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> { + /// Register an obligation that both constants must be equal to each other. + /// + /// If they aren't equal then the relation doesn't hold. + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>); +} + pub trait RelateResultCompare<'tcx, T> { fn compare(&self, t: T, f: F) -> RelateResult<'tcx, T> where diff --git a/src/librustc_infer/infer/equate.rs b/src/librustc_infer/infer/equate.rs index 8f8fc4f137b73..e3cafb82719dd 100644 --- a/src/librustc_infer/infer/equate.rs +++ b/src/librustc_infer/infer/equate.rs @@ -1,10 +1,10 @@ -use super::combine::{CombineFields, RelationDir}; +use super::combine::{CombineFields, ConstEquateRelation, RelationDir}; use super::Subtype; -use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc::ty::subst::SubstsRef; -use rustc::ty::TyVar; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_hir::def_id::DefId; @@ -72,14 +72,14 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> { } let infcx = self.fields.infcx; - let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b); + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b); match (&a.kind, &b.kind) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { - infcx.inner.borrow_mut().type_variables.equate(a_id, b_id); + infcx.inner.borrow_mut().type_variables().equate(a_id, b_id); } (&ty::Infer(TyVar(a_id)), _) => { @@ -140,3 +140,9 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> { } } } + +impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index a2ae4f53fb8f2..a8d6c01785ff9 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -55,20 +55,19 @@ use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, }; -use rustc::hir::map; -use rustc::middle::region; -use rustc::ty::error::TypeError; -use rustc::ty::{ - self, - subst::{Subst, SubstsRef}, - Region, Ty, TyCtxt, TypeFoldable, -}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err}; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::Node; +use rustc_middle::middle::region; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::{ + self, + subst::{Subst, SubstsRef}, + Region, Ty, TyCtxt, TypeFoldable, +}; use rustc_span::{DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::{cmp, fmt}; @@ -94,7 +93,8 @@ pub(super) fn note_and_explain_region( let unknown_scope = || format!("{}unknown scope: {:?}{}. Please report a bug.", prefix, scope, suffix); let span = scope.span(tcx, region_scope_tree); - let tag = match tcx.hir().find(scope.hir_id(region_scope_tree)) { + let hir_id = scope.hir_id(region_scope_tree); + let tag = match hir_id.and_then(|hir_id| tcx.hir().find(hir_id)) { Some(Node::Block(_)) => "block", Some(Node::Expr(expr)) => match expr.kind { hir::ExprKind::Call(..) => "call", @@ -191,9 +191,9 @@ fn msg_span_from_early_bound_and_free_regions( let sm = tcx.sess.source_map(); let scope = region.free_region_binding_scope(tcx); - let node = tcx.hir().as_local_hir_id(scope).unwrap_or(hir::DUMMY_HIR_ID); + let node = tcx.hir().as_local_hir_id(scope.expect_local()); let tag = match tcx.hir().find(node) { - Some(Node::Block(_)) | Some(Node::Expr(_)) => "body", + Some(Node::Block(_) | Node::Expr(_)) => "body", Some(Node::Item(it)) => item_scope_tag(&it), Some(Node::TraitItem(it)) => trait_item_scope_tag(&it), Some(Node::ImplItem(it)) => impl_item_scope_tag(&it), @@ -201,7 +201,7 @@ fn msg_span_from_early_bound_and_free_regions( }; let (prefix, span) = match *region { ty::ReEarlyBound(ref br) => { - let mut sp = sm.def_span(tcx.hir().span(node)); + let mut sp = sm.guess_head_span(tcx.hir().span(node)); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) { @@ -210,7 +210,7 @@ fn msg_span_from_early_bound_and_free_regions( (format!("the lifetime `{}` as defined on", br.name), sp) } ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegion::BrNamed(_, name), .. }) => { - let mut sp = sm.def_span(tcx.hir().span(node)); + let mut sp = sm.guess_head_span(tcx.hir().span(node)); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) { @@ -224,7 +224,7 @@ fn msg_span_from_early_bound_and_free_regions( } _ => ( format!("the lifetime `{}` as defined on", region), - sm.def_span(tcx.hir().span(node)), + sm.guess_head_span(tcx.hir().span(node)), ), }, _ => bug!(), @@ -304,8 +304,8 @@ pub fn unexpected_hidden_region_diagnostic( // down this path which gives a decent human readable // explanation. // - // (*) if not, the `tainted_by_errors` flag would be set to - // true in any case, so we wouldn't be here at all. + // (*) if not, the `tainted_by_errors` field would be set to + // `Some(ErrorReported)` in any case, so we wouldn't be here at all. note_and_explain_free_region( tcx, &mut err, @@ -549,7 +549,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { terr: &TypeError<'tcx>, ) { use hir::def_id::CrateNum; - use map::DisambiguatedDefPathData; + use rustc_hir::definitions::DisambiguatedDefPathData; use ty::print::Printer; use ty::subst::GenericArg; @@ -755,7 +755,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, ObligationCauseCode::IfExpression(box IfExpressionCause { then, outer, semicolon }) => { err.span_label(then, "expected because of this"); - outer.map(|sp| err.span_label(sp, "`if` and `else` have incompatible types")); + if let Some(sp) = outer { + err.span_label(sp, "`if` and `else` have incompatible types"); + } if let Some(sp) = semicolon { err.span_suggestion_short( sp, @@ -841,7 +843,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// For the following code: /// - /// ```norun + /// ```no_run /// let x: Foo> = foo::>(); /// ``` /// @@ -871,7 +873,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return Some(()); } if let &ty::Adt(def, _) = &ta.kind { - let path_ = self.tcx.def_path_str(def.did.clone()); + let path_ = self.tcx.def_path_str(def.did); if path_ == other_path { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); return Some(()); @@ -1058,13 +1060,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match (&a.kind, &b.kind) { (a, b) if *a == *b => true, (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) - | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Int(_)) - | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) + | ( + &ty::Infer(ty::InferTy::IntVar(_)), + &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ) | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) - | (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Float(_)) - | (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Infer(ty::InferTy::FloatVar(_))) => { - true - } + | ( + &ty::Infer(ty::InferTy::FloatVar(_)), + &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ) => true, _ => false, } } @@ -1091,8 +1095,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let sub_no_defaults_1 = self.strip_generic_default_params(def1.did, sub1); let sub_no_defaults_2 = self.strip_generic_default_params(def2.did, sub2); let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); - let path1 = self.tcx.def_path_str(def1.did.clone()); - let path2 = self.tcx.def_path_str(def2.did.clone()); + let path1 = self.tcx.def_path_str(def1.did); + let path2 = self.tcx.def_path_str(def2.did); if def1.did == def2.did { // Easy case. Replace same types with `_` to shorten the output and highlight // the differing ones. @@ -1384,16 +1388,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { terr: &TypeError<'tcx>, ) { let span = cause.span(self.tcx); + debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr); // For some types of errors, expected-found does not make // sense, so just ignore the values we were given. - match terr { - TypeError::CyclicTy(_) => { - values = None; - } - _ => {} + if let TypeError::CyclicTy(_) = terr { + values = None; } - struct OpaqueTypesVisitor<'tcx> { types: FxHashMap>, expected: FxHashMap>, @@ -1599,11 +1600,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id }) }); self.check_and_note_conflicting_crates(diag, terr); - self.tcx.note_and_explain_type_err(diag, terr, span, body_owner_def_id); + self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id()); // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, &cause, exp_found); + self.note_error_origin(diag, cause, exp_found); } /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, @@ -1614,60 +1615,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: &ty::error::ExpectedFound>, diag: &mut DiagnosticBuilder<'tcx>, ) { - match (&exp_found.expected.kind, &exp_found.found.kind) { - (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) => { - if let ty::Adt(found_def, found_substs) = found_ty.kind { - let path_str = format!("{:?}", exp_def); - if exp_def == &found_def { - let opt_msg = "you can convert from `&Option` to `Option<&T>` using \ + if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) = + (&exp_found.expected.kind, &exp_found.found.kind) + { + if let ty::Adt(found_def, found_substs) = found_ty.kind { + let path_str = format!("{:?}", exp_def); + if exp_def == &found_def { + let opt_msg = "you can convert from `&Option` to `Option<&T>` using \ `.as_ref()`"; - let result_msg = "you can convert from `&Result` to \ + let result_msg = "you can convert from `&Result` to \ `Result<&T, &E>` using `.as_ref()`"; - let have_as_ref = &[ - ("std::option::Option", opt_msg), - ("core::option::Option", opt_msg), - ("std::result::Result", result_msg), - ("core::result::Result", result_msg), - ]; - if let Some(msg) = have_as_ref - .iter() - .filter_map( - |(path, msg)| if &path_str == path { Some(msg) } else { None }, - ) - .next() - { - let mut show_suggestion = true; - for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) { - match exp_ty.kind { - ty::Ref(_, exp_ty, _) => { - match (&exp_ty.kind, &found_ty.kind) { - (_, ty::Param(_)) - | (_, ty::Infer(_)) - | (ty::Param(_), _) - | (ty::Infer(_), _) => {} - _ if ty::TyS::same_type(exp_ty, found_ty) => {} - _ => show_suggestion = false, - }; - } - ty::Param(_) | ty::Infer(_) => {} - _ => show_suggestion = false, + let have_as_ref = &[ + ("std::option::Option", opt_msg), + ("core::option::Option", opt_msg), + ("std::result::Result", result_msg), + ("core::result::Result", result_msg), + ]; + if let Some(msg) = have_as_ref + .iter() + .find_map(|(path, msg)| (&path_str == path).then_some(msg)) + { + let mut show_suggestion = true; + for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) { + match exp_ty.kind { + ty::Ref(_, exp_ty, _) => { + match (&exp_ty.kind, &found_ty.kind) { + (_, ty::Param(_)) + | (_, ty::Infer(_)) + | (ty::Param(_), _) + | (ty::Infer(_), _) => {} + _ if ty::TyS::same_type(exp_ty, found_ty) => {} + _ => show_suggestion = false, + }; } + ty::Param(_) | ty::Infer(_) => {} + _ => show_suggestion = false, } - if let (Ok(snippet), true) = - (self.tcx.sess.source_map().span_to_snippet(span), show_suggestion) - { - diag.span_suggestion( - span, - msg, - format!("{}.as_ref()", snippet), - Applicability::MachineApplicable, - ); - } + } + if let (Ok(snippet), true) = + (self.tcx.sess.source_map().span_to_snippet(span), show_suggestion) + { + diag.span_suggestion( + span, + msg, + format!("{}.as_ref()", snippet), + Applicability::MachineApplicable, + ); } } } } - _ => {} } } @@ -1787,10 +1784,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if !(generics.has_self && param.index == 0) { let type_param = generics.type_param(param, self.tcx); let hir = &self.tcx.hir(); - hir.as_local_hir_id(type_param.def_id).map(|id| { + type_param.def_id.as_local().map(|def_id| { // Get the `hir::Param` to verify whether it already has any bounds. // We do this to avoid suggesting code that ends up as `T: 'a'b`, // instead we suggest `T: 'a + 'b` in that case. + let id = hir.as_local_hir_id(def_id); let mut has_bounds = false; if let Node::GenericParam(param) = hir.get(id) { has_bounds = !param.bounds.is_empty(); @@ -1956,42 +1954,41 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "...", ); - match (&sup_origin, &sub_origin) { - (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) => { - debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); - debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); - debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); - debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); - debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); - debug!("report_sub_sup_conflict: sup_trace={:?}", sup_trace); - debug!("report_sub_sup_conflict: sub_trace={:?}", sub_trace); - debug!("report_sub_sup_conflict: sup_trace.values={:?}", sup_trace.values); - debug!("report_sub_sup_conflict: sub_trace.values={:?}", sub_trace.values); - - if let (Some((sup_expected, sup_found)), Some((sub_expected, sub_found))) = - (self.values_str(&sup_trace.values), self.values_str(&sub_trace.values)) - { - if sub_expected == sup_expected && sub_found == sup_found { - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "...but the lifetime must also be valid for ", - sub_region, - "...", - ); - err.span_note( - sup_trace.cause.span, - &format!("...so that the {}", sup_trace.cause.as_requirement_str()), - ); + if let (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) = + (&sup_origin, &sub_origin) + { + debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); + debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); + debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); + debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); + debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); + debug!("report_sub_sup_conflict: sup_trace={:?}", sup_trace); + debug!("report_sub_sup_conflict: sub_trace={:?}", sub_trace); + debug!("report_sub_sup_conflict: sup_trace.values={:?}", sup_trace.values); + debug!("report_sub_sup_conflict: sub_trace.values={:?}", sub_trace.values); + + if let (Some((sup_expected, sup_found)), Some((sub_expected, sub_found))) = + (self.values_str(&sup_trace.values), self.values_str(&sub_trace.values)) + { + if sub_expected == sup_expected && sub_found == sup_found { + note_and_explain_region( + self.tcx, + region_scope_tree, + &mut err, + "...but the lifetime must also be valid for ", + sub_region, + "...", + ); + err.span_note( + sup_trace.cause.span, + &format!("...so that the {}", sup_trace.cause.as_requirement_str()), + ); - err.note_expected_found(&"", sup_expected, &"", sup_found); - err.emit(); - return; - } + err.note_expected_found(&"", sup_expected, &"", sup_found); + err.emit(); + return; } } - _ => {} } self.note_region_origin(&mut err, &sup_origin); diff --git a/src/librustc_infer/infer/error_reporting/need_type_info.rs b/src/librustc_infer/infer/error_reporting/need_type_info.rs index fcbe938a8c7ac..dfc7177921d31 100644 --- a/src/librustc_infer/infer/error_reporting/need_type_info.rs +++ b/src/librustc_infer/infer/error_reporting/need_type_info.rs @@ -1,58 +1,69 @@ use crate::infer::type_variable::TypeVariableOriginKind; use crate::infer::InferCtxt; -use rustc::hir::map::Map; -use rustc::ty::print::Print; -use rustc::ty::{self, DefIdTree, Infer, Ty, TyVar}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::print::Print; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::kw; use rustc_span::Span; use std::borrow::Cow; -struct FindLocalByTypeVisitor<'a, 'tcx> { +struct FindHirNodeVisitor<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, - target_ty: Ty<'tcx>, - hir_map: Map<'tcx>, + target: GenericArg<'tcx>, + target_span: Span, + found_node_ty: Option>, found_local_pattern: Option<&'tcx Pat<'tcx>>, found_arg_pattern: Option<&'tcx Pat<'tcx>>, - found_ty: Option>, - found_closure: Option<&'tcx ExprKind<'tcx>>, + found_closure: Option<&'tcx Expr<'tcx>>, found_method_call: Option<&'tcx Expr<'tcx>>, + found_exact_method_call: Option<&'tcx Expr<'tcx>>, } -impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> { - fn new(infcx: &'a InferCtxt<'a, 'tcx>, target_ty: Ty<'tcx>, hir_map: Map<'tcx>) -> Self { +impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { + fn new(infcx: &'a InferCtxt<'a, 'tcx>, target: GenericArg<'tcx>, target_span: Span) -> Self { Self { infcx, - target_ty, - hir_map, + target, + target_span, + found_node_ty: None, found_local_pattern: None, found_arg_pattern: None, - found_ty: None, found_closure: None, found_method_call: None, + found_exact_method_call: None, } } - fn node_matches_type(&mut self, hir_id: HirId) -> Option> { + fn node_ty_contains_target(&mut self, hir_id: HirId) -> Option> { let ty_opt = self.infcx.in_progress_tables.and_then(|tables| tables.borrow().node_type_opt(hir_id)); match ty_opt { Some(ty) => { let ty = self.infcx.resolve_vars_if_possible(&ty); - if ty.walk().any(|inner_ty| { - inner_ty == self.target_ty - || match (&inner_ty.kind, &self.target_ty.kind) { - (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => self - .infcx - .inner - .borrow_mut() - .type_variables - .sub_unified(a_vid, b_vid), + if ty.walk().any(|inner| { + inner == self.target + || match (inner.unpack(), self.target.unpack()) { + (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { + match (&inner_ty.kind, &target_ty.kind) { + ( + &ty::Infer(ty::TyVar(a_vid)), + &ty::Infer(ty::TyVar(b_vid)), + ) => self + .infcx + .inner + .borrow_mut() + .type_variables() + .sub_unified(a_vid, b_vid), + _ => false, + } + } _ => false, } }) { @@ -66,36 +77,50 @@ impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.hir_map) + NestedVisitorMap::OnlyBodies(self.infcx.tcx.hir()) } fn visit_local(&mut self, local: &'tcx Local<'tcx>) { - if let (None, Some(ty)) = (self.found_local_pattern, self.node_matches_type(local.hir_id)) { + if let (None, Some(ty)) = + (self.found_local_pattern, self.node_ty_contains_target(local.hir_id)) + { self.found_local_pattern = Some(&*local.pat); - self.found_ty = Some(ty); + self.found_node_ty = Some(ty); } intravisit::walk_local(self, local); } fn visit_body(&mut self, body: &'tcx Body<'tcx>) { for param in body.params { - if let (None, Some(ty)) = (self.found_arg_pattern, self.node_matches_type(param.hir_id)) + if let (None, Some(ty)) = + (self.found_arg_pattern, self.node_ty_contains_target(param.hir_id)) { self.found_arg_pattern = Some(&*param.pat); - self.found_ty = Some(ty); + self.found_node_ty = Some(ty); } } intravisit::walk_body(self, body); } fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if self.node_matches_type(expr.hir_id).is_some() { + if let ExprKind::MethodCall(_, call_span, exprs) = expr.kind { + if call_span == self.target_span + && Some(self.target) + == self.infcx.in_progress_tables.and_then(|tables| { + tables.borrow().node_type_opt(exprs.first().unwrap().hir_id).map(Into::into) + }) + { + self.found_exact_method_call = Some(&expr); + return; + } + } + if self.node_ty_contains_target(expr.hir_id).is_some() { match expr.kind { - ExprKind::Closure(..) => self.found_closure = Some(&expr.kind), + ExprKind::Closure(..) => self.found_closure = Some(&expr), ExprKind::MethodCall(..) => self.found_method_call = Some(&expr), _ => {} } @@ -147,8 +172,19 @@ fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String { } pub enum TypeAnnotationNeeded { + /// ```compile_fail,E0282 + /// let x = "hello".chars().rev().collect(); + /// ``` E0282, + /// An implementation cannot be chosen unambiguously because of lack of information. + /// ```compile_fail,E0283 + /// let _ = Default::default(); + /// ``` E0283, + /// ```compile_fail,E0284 + /// let mut d: u64 = 2; + /// d = d % 1u32.into(); + /// ``` E0284, } @@ -169,7 +205,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { highlight: Option, ) -> (String, Option, Cow<'static, str>, Option, Option<&'static str>) { if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind { - let ty_vars = &self.inner.borrow().type_variables; + let mut inner = self.inner.borrow_mut(); + let ty_vars = &inner.type_variables(); let var_origin = ty_vars.var_origin(ty_vid); if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind { let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id)); @@ -182,12 +219,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .get_opt_name() .map(|parent_symbol| parent_symbol.to_string()); - let type_parent_desc = self - .tcx - .def_kind(parent_def_id) - .map(|parent_def_kind| parent_def_kind.descr(parent_def_id)); - - (parent_name, type_parent_desc) + (parent_name, Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id))) } else { (None, None) }; @@ -213,6 +245,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { (s, None, ty.prefix_string(), None, None) } + // FIXME(eddyb) generalize all of this to handle `ty::Const` inference variables as well. pub fn need_type_info_err( &self, body_id: Option, @@ -223,11 +256,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(&ty); let (name, name_sp, descr, parent_name, parent_descr) = self.extract_type_name(&ty, None); - let mut local_visitor = FindLocalByTypeVisitor::new(&self, ty, self.tcx.hir()); + let mut local_visitor = FindHirNodeVisitor::new(&self, ty.into(), span); let ty_to_string = |ty: Ty<'tcx>| -> String { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); - let ty_vars = &self.inner.borrow().type_variables; + let mut inner = self.inner.borrow_mut(); + let ty_vars = inner.type_variables(); let getter = move |ty_vid| { let var_origin = ty_vars.var_origin(ty_vid); if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind { @@ -236,7 +270,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { None }; printer.name_resolver = Some(Box::new(&getter)); - let _ = ty.print(printer); + let _ = if let ty::FnDef(..) = ty.kind { + // We don't want the regular output for `fn`s because it includes its path in + // invalid pseudo-syntax, we want the `fn`-pointer output instead. + ty.fn_sig(self.tcx).print(printer) + } else { + ty.print(printer) + }; s }; @@ -276,14 +316,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) }; - let ty_msg = match local_visitor.found_ty { - Some(ty::TyS { kind: ty::Closure(_, substs), .. }) => { + let ty_msg = match (local_visitor.found_node_ty, local_visitor.found_exact_method_call) { + (_, Some(_)) => String::new(), + (Some(ty::TyS { kind: ty::Closure(_, substs), .. }), _) => { let fn_sig = substs.as_closure().sig(); let args = closure_args(&fn_sig); let ret = fn_sig.output().skip_binder().to_string(); format!(" for the closure `fn({}) -> {}`", args, ret) } - Some(ty) if is_named_and_not_impl_trait(ty) => { + (Some(ty), _) if is_named_and_not_impl_trait(ty) => { let ty = ty_to_string(ty); format!(" for `{}`", ty) } @@ -310,28 +351,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { error_code, ); - let suffix = match local_visitor.found_ty { + let suffix = match local_visitor.found_node_ty { Some(ty::TyS { kind: ty::Closure(_, substs), .. }) => { let fn_sig = substs.as_closure().sig(); let ret = fn_sig.output().skip_binder().to_string(); - if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure { - if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) { - closure_return_type_suggestion( - span, - &mut err, - &decl.output, - &body, - &descr, - &name, - &ret, - parent_name, - parent_descr, - ); - // We don't want to give the other suggestions when the problem is the - // closure return type. - return err; - } + let closure_decl_and_body_id = + local_visitor.found_closure.and_then(|closure| match &closure.kind { + ExprKind::Closure(_, decl, body_id, ..) => Some((decl, *body_id)), + _ => None, + }); + + if let Some((decl, body_id)) = closure_decl_and_body_id { + closure_return_type_suggestion( + span, + &mut err, + &decl.output, + self.tcx.hir().body(body_id), + &descr, + &name, + &ret, + parent_name, + parent_descr, + ); + // We don't want to give the other suggestions when the problem is the + // closure return type. + return err; } // This shouldn't be reachable, but just in case we leave a reasonable fallback. @@ -355,7 +400,37 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => "a type".to_string(), }; - if let Some(pattern) = local_visitor.found_arg_pattern { + if let Some(e) = local_visitor.found_exact_method_call { + if let ExprKind::MethodCall(segment, ..) = &e.kind { + // Suggest specifying type params or point out the return type of the call: + // + // error[E0282]: type annotations needed + // --> $DIR/type-annotations-needed-expr.rs:2:39 + // | + // LL | let _ = x.into_iter().sum() as f64; + // | ^^^ + // | | + // | cannot infer type for `S` + // | help: consider specifying the type argument in + // | the method call: `sum::` + // | + // = note: type must be known at this point + // + // or + // + // error[E0282]: type annotations needed + // --> $DIR/issue-65611.rs:59:20 + // | + // LL | let x = buffer.last().unwrap().0.clone(); + // | -------^^^^-- + // | | | + // | | cannot infer type for `T` + // | this method call resolves to `std::option::Option<&T>` + // | + // = note: type must be known at this point + self.annotate_method_call(segment, e, &mut err); + } + } else if let Some(pattern) = local_visitor.found_arg_pattern { // We don't want to show the default label for closures. // // So, before clearing, the output would look something like this: @@ -454,6 +529,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err } + // FIXME(const_generics): We should either try and merge this with `need_type_info_err` + // or improve the errors created here. + // + // Unlike for type inference variables, we don't yet store the origin of const inference variables. + // This is needed for to get a more relevant error span. + pub fn need_type_info_err_const( + &self, + body_id: Option, + span: Span, + ct: &'tcx ty::Const<'tcx>, + error_code: TypeAnnotationNeeded, + ) -> DiagnosticBuilder<'tcx> { + let mut local_visitor = FindHirNodeVisitor::new(&self, ct.into(), span); + if let Some(body_id) = body_id { + let expr = self.tcx.hir().expect_expr(body_id.hir_id); + local_visitor.visit_expr(expr); + } + + let error_code = error_code.into(); + let mut err = self.tcx.sess.struct_span_err_with_code( + local_visitor.target_span, + &format!("type annotations needed"), + error_code, + ); + + err.note("unable to infer the value of a const parameter"); + + err + } + /// If the `FnSig` for the method call can be found and type arguments are identified as /// needed, suggest annotating the call, otherwise point out the resulting type of the call. fn annotate_method_call( @@ -490,7 +595,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let output = bound_output.skip_binder(); err.span_label(e.span, &format!("this method call resolves to `{:?}`", output)); let kind = &output.kind; - if let ty::Projection(proj) | ty::UnnormalizedProjection(proj) = kind { + if let ty::Projection(proj) = kind { if let Some(span) = self.tcx.hir().span_if_local(proj.item_def_id) { err.span_label(span, &format!("`{:?}` defined here", output)); } diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs index 689323ce48346..d206a30d526cb 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -5,9 +5,8 @@ use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::SubregionOrigin; -use rustc::util::common::ErrorReported; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, ErrorReported}; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when both the concerned regions are anonymous. diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs index 15acf632b2c98..de71363cbde5c 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -1,10 +1,10 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use rustc::hir::map::Map; -use rustc::middle::resolve_lifetime as rl; -use rustc::ty::{self, Region, TyCtxt}; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::resolve_lifetime as rl; +use rustc_middle::ty::{self, Region, TyCtxt}; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// This function calls the `visit_ty` method for the parameters @@ -29,7 +29,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ) -> Option<(&hir::Ty<'_>, &hir::FnDecl<'_>)> { if let Some(anon_reg) = self.tcx().is_suitable_region(region) { let def_id = anon_reg.def_id; - if let Some(hir_id) = self.tcx().hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx().hir().as_local_hir_id(def_id); let fndecl = match self.tcx().hir().get(hir_id) { Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref m, ..), .. }) | Node::TraitItem(&hir::TraitItem { @@ -46,8 +47,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { return fndecl .inputs .iter() - .filter_map(|arg| self.find_component_for_bound_region(arg, br)) - .next() + .find_map(|arg| self.find_component_for_bound_region(arg, br)) .map(|ty| (ty, &**fndecl)); } } @@ -164,12 +164,17 @@ impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { } } - (Some(rl::Region::Static), _) - | (Some(rl::Region::Free(_, _)), _) - | (Some(rl::Region::EarlyBound(_, _, _)), _) - | (Some(rl::Region::LateBound(_, _, _)), _) - | (Some(rl::Region::LateBoundAnon(_, _)), _) - | (None, _) => { + ( + Some( + rl::Region::Static + | rl::Region::Free(_, _) + | rl::Region::EarlyBound(_, _, _) + | rl::Region::LateBound(_, _, _) + | rl::Region::LateBoundAnon(_, _), + ) + | None, + _, + ) => { debug!("no arg found"); } } @@ -244,12 +249,17 @@ impl Visitor<'tcx> for TyPathVisitor<'tcx> { } } - (Some(rl::Region::Static), _) - | (Some(rl::Region::EarlyBound(_, _, _)), _) - | (Some(rl::Region::LateBound(_, _, _)), _) - | (Some(rl::Region::LateBoundAnon(_, _)), _) - | (Some(rl::Region::Free(_, _)), _) - | (None, _) => { + ( + Some( + rl::Region::Static + | rl::Region::EarlyBound(_, _, _) + | rl::Region::LateBound(_, _, _) + | rl::Region::LateBoundAnon(_, _) + | rl::Region::Free(_, _), + ) + | None, + _, + ) => { debug!("no arg found"); } } diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs index 2357ee689d59e..2aed3d9a469fb 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs @@ -1,9 +1,8 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::lexical_region_resolve::RegionResolutionError::*; use crate::infer::InferCtxt; -use rustc::ty::{self, TyCtxt}; -use rustc::util::common::ErrorReported; -use rustc_errors::DiagnosticBuilder; +use rustc_errors::{DiagnosticBuilder, ErrorReported}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::source_map::Span; mod different_lifetimes; diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 02ce357967c18..b85a4cae2e470 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -1,9 +1,9 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where one region is named and the other is anonymous. use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use rustc::ty; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir::{FnRetTy, TyKind}; +use rustc_middle::ty; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// When given a `ConcreteFailure` for a function with parameters containing a named region and diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/outlives_closure.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/outlives_closure.rs index d88e6555af93e..fc858a497597e 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/outlives_closure.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/outlives_closure.rs @@ -4,9 +4,9 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError::SubSupConflict; use crate::infer::SubregionOrigin; -use rustc::ty::RegionKind; -use rustc::util::common::ErrorReported; +use rustc_errors::ErrorReported; use rustc_hir::{Expr, ExprKind::Closure, Node}; +use rustc_middle::ty::RegionKind; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when binding escapes a closure. @@ -46,7 +46,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ) = (&sub_origin, sup_region) { let hir = &self.tcx().hir(); - if let Some(hir_id) = hir.as_local_hir_id(free_region.scope) { + if let Some(def_id) = free_region.scope.as_local() { + let hir_id = hir.as_local_hir_id(def_id); if let Node::Expr(Expr { kind: Closure(_, _, _, closure_span, None), .. }) = hir.get(hir_id) { diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/placeholder_error.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/placeholder_error.rs index 57313dbab420f..2187064ec5ecf 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -3,13 +3,13 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::ValuePairs; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCause, ObligationCauseCode}; -use rustc::ty::error::ExpectedFound; -use rustc::ty::print::{FmtPrinter, Print, RegionHighlightMode}; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, TyCtxt}; use rustc_errors::DiagnosticBuilder; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, TyCtxt}; use std::fmt::{self, Write}; diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 655e28bbd3d92..7f3ec852e41de 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -3,9 +3,8 @@ use crate::infer::error_reporting::msg_span_from_free_region; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use rustc::ty::{BoundRegion, FreeRegion, RegionKind}; -use rustc::util::common::ErrorReported; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, ErrorReported}; +use rustc_middle::ty::{BoundRegion, FreeRegion, RegionKind}; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the return type is a static impl Trait. diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index f8cab9f84c841..695f3e47fb5d7 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -4,8 +4,8 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{Subtype, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; -use rustc::ty::Ty; -use rustc::util::common::ErrorReported; +use rustc_errors::ErrorReported; +use rustc_middle::ty::Ty; use rustc_span::Span; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -22,26 +22,25 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { _sup, ) = error.clone() { - match (&sup_origin, &sub_origin) { - (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) => { - if let ( - ValuePairs::Types(sub_expected_found), - ValuePairs::Types(sup_expected_found), - CompareImplMethodObligation { trait_item_def_id, .. }, - ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code) - { - if sup_expected_found == sub_expected_found { - self.emit_err( - var_origin.span(), - sub_expected_found.expected, - sub_expected_found.found, - self.tcx().def_span(*trait_item_def_id), - ); - return Some(ErrorReported); - } + if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = + (&sup_origin, &sub_origin) + { + if let ( + ValuePairs::Types(sub_expected_found), + ValuePairs::Types(sup_expected_found), + CompareImplMethodObligation { trait_item_def_id, .. }, + ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code) + { + if sup_expected_found == sub_expected_found { + self.emit_err( + var_origin.span(), + sub_expected_found.expected, + sub_expected_found.found, + self.tcx().def_span(*trait_item_def_id), + ); + return Some(ErrorReported); } } - _ => {} } } } diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs index de72c276595f7..22b130cdf5ffe 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs @@ -2,9 +2,9 @@ //! anonymous regions. use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use rustc::ty::{self, DefIdTree, Region, Ty}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, DefIdTree, Region, Ty}; use rustc_span::Span; // The struct contains the information about the anonymous region @@ -51,44 +51,40 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }; let hir = &self.tcx().hir(); - let hir_id = hir.as_local_hir_id(id)?; + let hir_id = hir.as_local_hir_id(id.as_local()?); let body_id = hir.maybe_body_owned_by(hir_id)?; let body = hir.body(body_id); let owner_id = hir.body_owner(body_id); let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); let poly_fn_sig = self.tcx().fn_sig(id); let fn_sig = self.tcx().liberate_late_bound_regions(id, &poly_fn_sig); - body.params - .iter() - .enumerate() - .filter_map(|(index, param)| { - // May return None; sometimes the tables are not yet populated. - let ty = fn_sig.inputs()[index]; - let mut found_anon_region = false; - let new_param_ty = self.tcx().fold_regions(&ty, &mut false, |r, _| { - if *r == *anon_region { - found_anon_region = true; - replace_region - } else { - r - } - }); - if found_anon_region { - let ty_hir_id = fn_decl.inputs[index].hir_id; - let param_ty_span = hir.span(ty_hir_id); - let is_first = index == 0; - Some(AnonymousParamInfo { - param, - param_ty: new_param_ty, - param_ty_span, - bound_region, - is_first, - }) + body.params.iter().enumerate().find_map(|(index, param)| { + // May return None; sometimes the tables are not yet populated. + let ty = fn_sig.inputs()[index]; + let mut found_anon_region = false; + let new_param_ty = self.tcx().fold_regions(&ty, &mut false, |r, _| { + if *r == *anon_region { + found_anon_region = true; + replace_region } else { - None + r } - }) - .next() + }); + if found_anon_region { + let ty_hir_id = fn_decl.inputs[index].hir_id; + let param_ty_span = hir.span(ty_hir_id); + let is_first = index == 0; + Some(AnonymousParamInfo { + param, + param_ty: new_param_ty, + param_ty_span, + bound_region, + is_first, + }) + } else { + None + } + }) } // Here, we check for the case where the anonymous region @@ -118,7 +114,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // enable E0621 for it. pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: DefId) -> bool { is_first - && self.tcx().opt_associated_item(scope_def_id).map(|i| i.method_has_self_argument) + && self.tcx().opt_associated_item(scope_def_id).map(|i| i.fn_has_self_parameter) == Some(true) } } diff --git a/src/librustc_infer/infer/error_reporting/note.rs b/src/librustc_infer/infer/error_reporting/note.rs index 5c0caa48d0e77..81f37831af208 100644 --- a/src/librustc_infer/infer/error_reporting/note.rs +++ b/src/librustc_infer/infer/error_reporting/note.rs @@ -1,9 +1,9 @@ use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt}; use crate::infer::{self, InferCtxt, SubregionOrigin}; -use rustc::middle::region; -use rustc::ty::error::TypeError; -use rustc::ty::{self, Region}; use rustc_errors::{struct_span_err, DiagnosticBuilder}; +use rustc_middle::middle::region; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::{self, Region}; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub(super) fn note_region_origin( diff --git a/src/librustc_infer/infer/free_regions.rs b/src/librustc_infer/infer/free_regions.rs new file mode 100644 index 0000000000000..e31c524c19710 --- /dev/null +++ b/src/librustc_infer/infer/free_regions.rs @@ -0,0 +1,175 @@ +//! This module handles the relationships between "free regions", i.e., lifetime parameters. +//! Ordinarily, free regions are unrelated to one another, but they can be related via implied +//! or explicit bounds. In that case, we track the bounds using the `TransitiveRelation` type, +//! and use that to decide when one free region outlives another, and so forth. + +use rustc_data_structures::transitive_relation::TransitiveRelation; +use rustc_hir::def_id::DefId; +use rustc_middle::middle::region; +use rustc_middle::ty::{self, Lift, Region, TyCtxt}; + +/// Combines a `region::ScopeTree` (which governs relationships between +/// scopes) and a `FreeRegionMap` (which governs relationships between +/// free regions) to yield a complete relation between concrete +/// regions. +/// +/// This stuff is a bit convoluted and should be refactored, but as we +/// transition to NLL, it'll all go away anyhow. +pub struct RegionRelations<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + + /// The context used to fetch the region maps. + pub context: DefId, + + /// The region maps for the given context. + pub region_scope_tree: &'a region::ScopeTree, + + /// Free-region relationships. + pub free_regions: &'a FreeRegionMap<'tcx>, +} + +impl<'a, 'tcx> RegionRelations<'a, 'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + context: DefId, + region_scope_tree: &'a region::ScopeTree, + free_regions: &'a FreeRegionMap<'tcx>, + ) -> Self { + Self { tcx, context, region_scope_tree, free_regions } + } + + pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { + self.free_regions.lub_free_regions(self.tcx, r_a, r_b) + } +} + +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default, HashStable)] +pub struct FreeRegionMap<'tcx> { + // Stores the relation `a < b`, where `a` and `b` are regions. + // + // Invariant: only free regions like `'x` or `'static` are stored + // in this relation, not scopes. + relation: TransitiveRelation>, +} + +impl<'tcx> FreeRegionMap<'tcx> { + pub fn elements(&self) -> impl Iterator> { + self.relation.elements() + } + + pub fn is_empty(&self) -> bool { + self.relation.is_empty() + } + + // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. + // (with the exception that `'static: 'x` is not notable) + pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { + debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); + if self.is_free_or_static(sub) && self.is_free(sup) { + self.relation.add(sub, sup) + } + } + + /// Tests whether `r_a <= r_b`. + /// + /// Both regions must meet `is_free_or_static`. + /// + /// Subtle: one tricky case that this code gets correct is as + /// follows. If we know that `r_b: 'static`, then this function + /// will return true, even though we don't know anything that + /// directly relates `r_a` and `r_b`. + /// + /// Also available through the `FreeRegionRelations` trait below. + pub fn sub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>, + ) -> bool { + assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b)); + let re_static = tcx.lifetimes.re_static; + if self.check_relation(re_static, r_b) { + // `'a <= 'static` is always true, and not stored in the + // relation explicitly, so check if `'b` is `'static` (or + // equivalent to it) + true + } else { + self.check_relation(r_a, r_b) + } + } + + /// Check whether `r_a <= r_b` is found in the relation. + fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { + r_a == r_b || self.relation.contains(&r_a, &r_b) + } + + /// True for free regions other than `'static`. + pub fn is_free(&self, r: Region<'_>) -> bool { + match *r { + ty::ReEarlyBound(_) | ty::ReFree(_) => true, + _ => false, + } + } + + /// True if `r` is a free region or static of the sort that this + /// free region map can be used with. + pub fn is_free_or_static(&self, r: Region<'_>) -> bool { + match *r { + ty::ReStatic => true, + _ => self.is_free(r), + } + } + + /// Computes the least-upper-bound of two free regions. In some + /// cases, this is more conservative than necessary, in order to + /// avoid making arbitrary choices. See + /// `TransitiveRelation::postdom_upper_bound` for more details. + pub fn lub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>, + ) -> Region<'tcx> { + debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); + assert!(self.is_free(r_a)); + assert!(self.is_free(r_b)); + let result = if r_a == r_b { + r_a + } else { + match self.relation.postdom_upper_bound(&r_a, &r_b) { + None => tcx.lifetimes.re_static, + Some(r) => *r, + } + }; + debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); + result + } +} + +/// The NLL region handling code represents free region relations in a +/// slightly different way; this trait allows functions to be abstract +/// over which version is in use. +pub trait FreeRegionRelations<'tcx> { + /// Tests whether `r_a <= r_b`. Both must be free regions or + /// `'static`. + fn sub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + shorter: ty::Region<'tcx>, + longer: ty::Region<'tcx>, + ) -> bool; +} + +impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> { + fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { + // invoke the "inherent method" + self.sub_free_regions(tcx, r_a, r_b) + } +} + +impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { + type Lifted = FreeRegionMap<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + self.relation.maybe_map(|&fr| tcx.lift(&fr)).map(|relation| FreeRegionMap { relation }) + } +} diff --git a/src/librustc_infer/infer/freshen.rs b/src/librustc_infer/infer/freshen.rs index fa28cf5b45464..c9ed687eaf256 100644 --- a/src/librustc_infer/infer/freshen.rs +++ b/src/librustc_infer/infer/freshen.rs @@ -31,8 +31,8 @@ //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type //! inferencer knows "so far". -use rustc::ty::fold::TypeFolder; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; @@ -147,7 +147,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { match t.kind { ty::Infer(ty::TyVar(v)) => { - let opt_ty = self.infcx.inner.borrow_mut().type_variables.probe(v).known(); + let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy) } @@ -155,7 +155,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { self.infcx .inner .borrow_mut() - .int_unification_table + .int_unification_table() .probe_value(v) .map(|v| v.to_type(tcx)), ty::IntVar(v), @@ -166,16 +166,14 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { self.infcx .inner .borrow_mut() - .float_unification_table + .float_unification_table() .probe_value(v) .map(|v| v.to_type(tcx)), ty::FloatVar(v), ty::FreshFloatTy, ), - ty::Infer(ty::FreshTy(ct)) - | ty::Infer(ty::FreshIntTy(ct)) - | ty::Infer(ty::FreshFloatTy(ct)) => { + ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => { if ct >= self.ty_freshen_count { bug!( "Encountered a freshend type with id {} \ @@ -206,7 +204,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::Never | ty::Tuple(..) | ty::Projection(..) - | ty::UnnormalizedProjection(..) | ty::Foreign(..) | ty::Param(..) | ty::Closure(..) @@ -224,7 +221,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { .infcx .inner .borrow_mut() - .const_unification_table + .const_unification_table() .probe_value(v) .val .known(); @@ -251,7 +248,10 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { bug!("unexpected const {:?}", ct) } - ty::ConstKind::Param(_) | ty::ConstKind::Value(_) | ty::ConstKind::Unevaluated(..) => {} + ty::ConstKind::Param(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Error => {} } ct.super_fold_with(self) diff --git a/src/librustc_infer/infer/fudge.rs b/src/librustc_infer/infer/fudge.rs index 16bf0f3d1c682..c6651108df53d 100644 --- a/src/librustc_infer/infer/fudge.rs +++ b/src/librustc_infer/infer/fudge.rs @@ -1,20 +1,32 @@ -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; use super::type_variable::TypeVariableOrigin; use super::InferCtxt; -use super::{ConstVariableOrigin, RegionVariableOrigin}; +use super::{ConstVariableOrigin, RegionVariableOrigin, UnificationTable}; +use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::unify as ut; use ut::UnifyKey; use std::ops::Range; +fn vars_since_snapshot<'tcx, T>( + table: &mut UnificationTable<'_, 'tcx, T>, + snapshot_var_len: usize, +) -> Range +where + T: UnifyKey, + super::UndoLog<'tcx>: From>>, +{ + T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32) +} + fn const_vars_since_snapshot<'tcx>( - table: &mut ut::UnificationTable>>, - snapshot: &ut::Snapshot>>, + table: &mut UnificationTable<'_, 'tcx, ConstVid<'tcx>>, + snapshot_var_len: usize, ) -> (Range>, Vec) { - let range = table.vars_since_snapshot(snapshot); + let range = vars_since_snapshot(table, snapshot_var_len); ( range.start..range.end, (range.start.index..range.end.index) @@ -23,7 +35,26 @@ fn const_vars_since_snapshot<'tcx>( ) } +struct VariableLengths { + type_var_len: usize, + const_var_len: usize, + int_var_len: usize, + float_var_len: usize, + region_constraints_len: usize, +} + impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + type_var_len: inner.type_variables().num_vars(), + const_var_len: inner.const_unification_table().len(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + } + } + /// This rather funky routine is used while processing expected /// types. What happens here is that we want to propagate a /// coercion through the return type of a fn to its @@ -70,7 +101,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { debug!("fudge_inference_if_ok()"); - let (mut fudger, value) = self.probe(|snapshot| { + let variable_lengths = self.variable_lengths(); + let (mut fudger, value) = self.probe(|_| { match f() { Ok(value) => { let value = self.resolve_vars_if_possible(&value); @@ -83,17 +115,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let mut inner = self.inner.borrow_mut(); let type_vars = - inner.type_variables.vars_since_snapshot(&snapshot.type_snapshot); - let int_vars = - inner.int_unification_table.vars_since_snapshot(&snapshot.int_snapshot); - let float_vars = - inner.float_unification_table.vars_since_snapshot(&snapshot.float_snapshot); + inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); + let int_vars = vars_since_snapshot( + &mut inner.int_unification_table(), + variable_lengths.int_var_len, + ); + let float_vars = vars_since_snapshot( + &mut inner.float_unification_table(), + variable_lengths.float_var_len, + ); let region_vars = inner .unwrap_region_constraints() - .vars_since_snapshot(&snapshot.region_constraints_snapshot); + .vars_since_snapshot(variable_lengths.region_constraints_len); let const_vars = const_vars_since_snapshot( - &mut inner.const_unification_table, - &snapshot.const_snapshot, + &mut inner.const_unification_table(), + variable_lengths.const_var_len, ); let fudger = InferenceFudger { @@ -161,7 +197,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> { // that it is unbound, so we can just return // it. debug_assert!( - self.infcx.inner.borrow_mut().type_variables.probe(vid).is_unknown() + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() ); ty } diff --git a/src/librustc_infer/infer/glb.rs b/src/librustc_infer/infer/glb.rs index 8b26bcef57304..ec219a95b9441 100644 --- a/src/librustc_infer/infer/glb.rs +++ b/src/librustc_infer/infer/glb.rs @@ -3,9 +3,10 @@ use super::lattice::{self, LatticeDir}; use super::InferCtxt; use super::Subtype; +use crate::infer::combine::ConstEquateRelation; use crate::traits::ObligationCause; -use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt}; /// "Greatest lower bound" (common subtype) pub struct Glb<'combine, 'infcx, 'tcx> { @@ -116,3 +117,9 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, Ok(()) } } + +impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} diff --git a/src/librustc_infer/infer/higher_ranked/mod.rs b/src/librustc_infer/infer/higher_ranked/mod.rs index 6a5a1c46d4caf..ef18918c1772f 100644 --- a/src/librustc_infer/infer/higher_ranked/mod.rs +++ b/src/librustc_infer/infer/higher_ranked/mod.rs @@ -5,8 +5,8 @@ use super::combine::CombineFields; use super::{HigherRankedType, InferCtxt, PlaceholderMap}; use crate::infer::CombinedSnapshot; -use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc::ty::{self, Binder, TypeFoldable}; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Binder, TypeFoldable}; impl<'a, 'tcx> CombineFields<'a, 'tcx> { pub fn higher_ranked_sub( diff --git a/src/librustc_infer/infer/lattice.rs b/src/librustc_infer/infer/lattice.rs index 42f9b3ab7709e..1bf43e74dcd84 100644 --- a/src/librustc_infer/infer/lattice.rs +++ b/src/librustc_infer/infer/lattice.rs @@ -23,9 +23,9 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::InferCtxt; use crate::traits::ObligationCause; -use rustc::ty::relate::{RelateResult, TypeRelation}; -use rustc::ty::TyVar; -use rustc::ty::{self, Ty}; +use rustc_middle::ty::relate::{RelateResult, TypeRelation}; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty}; pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> { fn infcx(&self) -> &'f InferCtxt<'f, 'tcx>; @@ -56,8 +56,8 @@ where } let infcx = this.infcx(); - let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b); + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); match (&a.kind, &b.kind) { // If one side is known to be a variable and one is not, // create a variable (`v`) to represent the LUB. Make sure to diff --git a/src/librustc_infer/infer/lexical_region_resolve/graphviz.rs b/src/librustc_infer/infer/lexical_region_resolve/graphviz.rs index eb52f10e40893..5d3e8f440d6fd 100644 --- a/src/librustc_infer/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc_infer/infer/lexical_region_resolve/graphviz.rs @@ -1,5 +1,5 @@ //! This module provides linkage between libgraphviz traits and -//! `rustc::middle::typeck::infer::region_constraints`, generating a +//! `rustc_trait_selection::infer::region_constraints`, generating a //! rendering of the graph represented by the list of `Constraint` //! instances (which make up the edges of the graph), as well as the //! origin for each constraint (which are attached to the labels on @@ -10,12 +10,12 @@ use graphviz as dot; use super::Constraint; use crate::infer::region_constraints::RegionConstraintData; +use crate::infer::RegionRelations; use crate::infer::SubregionOrigin; -use rustc::middle::free_region::RegionRelations; -use rustc::middle::region; -use rustc::ty; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::DefIndex; +use rustc_middle::middle::region; +use rustc_middle::ty; use std::borrow::Cow; use std::collections::btree_map::BTreeMap; diff --git a/src/librustc_infer/infer/lexical_region_resolve/mod.rs b/src/librustc_infer/infer/lexical_region_resolve/mod.rs index d81c7454a0fe5..33a80fb747101 100644 --- a/src/librustc_infer/infer/lexical_region_resolve/mod.rs +++ b/src/librustc_infer/infer/lexical_region_resolve/mod.rs @@ -6,20 +6,20 @@ use crate::infer::region_constraints::MemberConstraint; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::region_constraints::VarInfos; use crate::infer::region_constraints::VerifyBound; +use crate::infer::RegionRelations; use crate::infer::RegionVariableOrigin; use crate::infer::RegionckMode; use crate::infer::SubregionOrigin; -use rustc::middle::free_region::RegionRelations; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; -use rustc::ty::{ReLateBound, RePlaceholder, ReScope, ReVar}; -use rustc::ty::{Region, RegionVid}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::implementation::{ Direction, Graph, NodeIndex, INCOMING, OUTGOING, }; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use rustc_middle::ty::{ReLateBound, RePlaceholder, ReScope, ReVar}; +use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; use std::fmt; @@ -325,8 +325,21 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - debug!("enforce_member_constraint: final least choice = {:?}", least_choice); - if least_choice != member_lower_bound { + // (#72087) Different `ty::Regions` can be known to be equal, for + // example, we know that `'a` and `'static` are equal in a function + // with a parameter of type `&'static &'a ()`. + // + // When we have two equal regions like this `expansion` will use + // `lub_concrete_regions` to pick a canonical representative. The same + // choice is needed here so that we don't end up in a cycle of + // `expansion` changing the region one way and the code here changing + // it back. + let lub = self.lub_concrete_regions(least_choice, member_lower_bound); + debug!( + "enforce_member_constraint: final least choice = {:?}\nlub = {:?}", + least_choice, lub + ); + if lub != member_lower_bound { *var_values.value_mut(member_vid) = VarValue::Value(least_choice); true } else { @@ -512,12 +525,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.tcx().lifetimes.re_static } - (&ReEmpty(_), r @ ReEarlyBound(_)) - | (r @ ReEarlyBound(_), &ReEmpty(_)) - | (&ReEmpty(_), r @ ReFree(_)) - | (r @ ReFree(_), &ReEmpty(_)) - | (&ReEmpty(_), r @ ReScope(_)) - | (r @ ReScope(_), &ReEmpty(_)) => { + (&ReEmpty(_), r @ (ReEarlyBound(_) | ReFree(_) | ReScope(_))) + | (r @ (ReEarlyBound(_) | ReFree(_) | ReScope(_)), &ReEmpty(_)) => { // All empty regions are less than early-bound, free, // and scope regions. r @@ -542,10 +551,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - (&ReEarlyBound(_), &ReScope(s_id)) - | (&ReScope(s_id), &ReEarlyBound(_)) - | (&ReFree(_), &ReScope(s_id)) - | (&ReScope(s_id), &ReFree(_)) => { + (&ReEarlyBound(_) | &ReFree(_), &ReScope(s_id)) + | (&ReScope(s_id), &ReEarlyBound(_) | &ReFree(_)) => { // A "free" region can be interpreted as "some region // at least as big as fr.scope". So, we can // reasonably compare free regions and scopes: @@ -584,10 +591,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.tcx().mk_region(ReScope(lub)) } - (&ReEarlyBound(_), &ReEarlyBound(_)) - | (&ReFree(_), &ReEarlyBound(_)) - | (&ReEarlyBound(_), &ReFree(_)) - | (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b), + (&ReEarlyBound(_) | &ReFree(_), &ReEarlyBound(_) | &ReFree(_)) => { + self.region_rels.lub_free_regions(a, b) + } // For these types, we cannot define any additional // relationship: diff --git a/src/librustc_infer/infer/lub.rs b/src/librustc_infer/infer/lub.rs index 20ddeec68503a..a0453db2cb499 100644 --- a/src/librustc_infer/infer/lub.rs +++ b/src/librustc_infer/infer/lub.rs @@ -3,9 +3,10 @@ use super::lattice::{self, LatticeDir}; use super::InferCtxt; use super::Subtype; +use crate::infer::combine::ConstEquateRelation; use crate::traits::ObligationCause; -use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt}; /// "Least upper bound" (common supertype) pub struct Lub<'combine, 'infcx, 'tcx> { @@ -100,6 +101,12 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> { } } +impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} + impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> { self.fields.infcx diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs index dc494b8e41370..9c81a1153958b 100644 --- a/src/librustc_infer/infer/mod.rs +++ b/src/librustc_infer/infer/mod.rs @@ -6,30 +6,31 @@ pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; +pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; + use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; -use rustc::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc::infer::unify_key::{ConstVarValue, ConstVariableValue}; -use rustc::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; -use rustc::middle::free_region::RegionRelations; -use rustc::middle::region; -use rustc::mir; -use rustc::mir::interpret::ConstEvalResult; -use rustc::traits::select; -use rustc::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::relate::RelateResult; -use rustc::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; -pub use rustc::ty::IntVarValue; -use rustc::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; -use rustc::ty::{ConstVid, FloatVid, IntVid, TyVid}; -use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::unify as ut; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; +use rustc_middle::middle::region; +use rustc_middle::mir; +use rustc_middle::mir::interpret::ConstEvalResult; +use rustc_middle::traits::select; +use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::relate::RelateResult; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; +pub use rustc_middle::ty::IntVarValue; +use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; +use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid}; use rustc_session::config::BorrowckMode; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -39,10 +40,13 @@ use std::collections::BTreeMap; use std::fmt; use self::combine::CombineFields; +use self::free_regions::RegionRelations; use self::lexical_region_resolve::LexicalRegionResolutions; use self::outlives::env::OutlivesEnvironment; use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound}; -use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; +use self::region_constraints::{ + RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, +}; use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; @@ -50,6 +54,7 @@ pub mod canonical; mod combine; mod equate; pub mod error_reporting; +pub mod free_regions; mod freshen; mod fudge; mod glb; @@ -63,9 +68,10 @@ pub mod region_constraints; pub mod resolve; mod sub; pub mod type_variable; +mod undo_log; use crate::infer::canonical::OriginalQueryValues; -pub use rustc::infer::unify_key; +pub use rustc_middle::infer::unify_key; #[must_use] #[derive(Debug)] @@ -79,6 +85,10 @@ pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult<'tcx, T> = Result>; // "fixup result" +pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable< + ut::InPlace, &'a mut InferCtxtUndoLogs<'tcx>>, +>; + /// How we should handle region solving. /// /// This is used so that the region values inferred by HIR region solving are @@ -135,28 +145,28 @@ pub struct InferCtxtInner<'tcx> { /// Cache for projections. This cache is snapshotted along with the infcx. /// /// Public so that `traits::project` can use it. - pub projection_cache: traits::ProjectionCache<'tcx>, + pub projection_cache: traits::ProjectionCacheStorage<'tcx>, /// We instantiate `UnificationTable` with `bounds` because the types /// that might instantiate a general type variable have an order, /// represented by its upper and lower bounds. - type_variables: type_variable::TypeVariableTable<'tcx>, + type_variable_storage: type_variable::TypeVariableStorage<'tcx>, /// Map from const parameter variable to the kind of const it represents. - const_unification_table: ut::UnificationTable>>, + const_unification_storage: ut::UnificationTableStorage>, /// Map from integral variable to the kind of integer it represents. - int_unification_table: ut::UnificationTable>, + int_unification_storage: ut::UnificationTableStorage, /// Map from floating variable to the kind of float it represents. - float_unification_table: ut::UnificationTable>, + float_unification_storage: ut::UnificationTableStorage, /// Tracks the set of region variables and the constraints between them. /// This is initially `Some(_)` but when /// `resolve_regions_and_report_errors` is invoked, this gets set to `None` /// -- further attempts to perform unification, etc., may fail if new /// region constraints would've been added. - region_constraints: Option>, + region_constraint_storage: Option>, /// A set of constraints that regionck must validate. Each /// constraint has the form `T:'a`, meaning "some type `T` must @@ -189,24 +199,78 @@ pub struct InferCtxtInner<'tcx> { /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. - pub region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>, + region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>, + + undo_log: InferCtxtUndoLogs<'tcx>, } impl<'tcx> InferCtxtInner<'tcx> { fn new() -> InferCtxtInner<'tcx> { InferCtxtInner { projection_cache: Default::default(), - type_variables: type_variable::TypeVariableTable::new(), - const_unification_table: ut::UnificationTable::new(), - int_unification_table: ut::UnificationTable::new(), - float_unification_table: ut::UnificationTable::new(), - region_constraints: Some(RegionConstraintCollector::new()), + type_variable_storage: type_variable::TypeVariableStorage::new(), + undo_log: InferCtxtUndoLogs::default(), + const_unification_storage: ut::UnificationTableStorage::new(), + int_unification_storage: ut::UnificationTableStorage::new(), + float_unification_storage: ut::UnificationTableStorage::new(), + region_constraint_storage: Some(RegionConstraintStorage::new()), region_obligations: vec![], } } - pub fn unwrap_region_constraints(&mut self) -> &mut RegionConstraintCollector<'tcx> { - self.region_constraints.as_mut().expect("region constraints already solved") + pub fn region_obligations(&self) -> &[(hir::HirId, RegionObligation<'tcx>)] { + &self.region_obligations + } + + pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> { + self.projection_cache.with_log(&mut self.undo_log) + } + + fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> { + self.type_variable_storage.with_log(&mut self.undo_log) + } + + fn int_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::IntVid, + &mut ut::UnificationStorage, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.int_unification_storage.with_log(&mut self.undo_log) + } + + fn float_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::FloatVid, + &mut ut::UnificationStorage, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.float_unification_storage.with_log(&mut self.undo_log) + } + + fn const_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::ConstVid<'tcx>, + &mut ut::UnificationStorage>, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.const_unification_storage.with_log(&mut self.undo_log) + } + + pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> { + self.region_constraint_storage + .as_mut() + .expect("region constraints already solved") + .with_log(&mut self.undo_log) } } @@ -392,7 +456,7 @@ pub enum SubregionOrigin<'tcx> { /// the containing trait. CompareImplMethodObligation { span: Span, - item_name: ast::Name, + item_name: Symbol, impl_item_def_id: DefId, trait_item_def_id: DefId, }, @@ -454,7 +518,7 @@ pub enum RegionVariableOrigin { UpvarRegion(ty::UpvarId, Span), - BoundRegionInCoherence(ast::Name), + BoundRegionInCoherence(Symbol), /// This origin is used for the inference variables that we create /// during NLL region processing. @@ -472,6 +536,9 @@ pub enum NLLRegionVariableOrigin { /// from a `for<'a> T` binder). Meant to represent "any region". Placeholder(ty::PlaceholderRegion), + /// The variable we create to represent `'empty(U0)`. + RootEmptyRegion, + Existential { /// If this is true, then this variable was created to represent a lifetime /// bound in a `for` binder. For example, it might have been created to @@ -493,6 +560,7 @@ impl NLLRegionVariableOrigin { NLLRegionVariableOrigin::FreeRegion => true, NLLRegionVariableOrigin::Placeholder(..) => true, NLLRegionVariableOrigin::Existential { .. } => false, + NLLRegionVariableOrigin::RootEmptyRegion => false, } } @@ -638,16 +706,10 @@ impl<'tcx> InferOk<'tcx, ()> { #[must_use = "once you start a snapshot, you should always consume it"] pub struct CombinedSnapshot<'a, 'tcx> { - projection_cache_snapshot: traits::ProjectionCacheSnapshot, - type_snapshot: type_variable::Snapshot<'tcx>, - const_snapshot: ut::Snapshot>>, - int_snapshot: ut::Snapshot>, - float_snapshot: ut::Snapshot>, + undo_snapshot: Snapshot<'tcx>, region_constraints_snapshot: RegionSnapshot, - region_obligations_snapshot: usize, universe: ty::UniverseIndex, was_in_snapshot: bool, - was_skip_leak_check: bool, _in_progress_tables: Option>>, } @@ -662,7 +724,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool { match ty.kind { - ty::Infer(ty::TyVar(vid)) => self.inner.borrow().type_variables.var_diverges(vid), + ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid), _ => false, } } @@ -672,18 +734,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric { - use rustc::ty::error::UnconstrainedNumeric::Neither; - use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; + use rustc_middle::ty::error::UnconstrainedNumeric::Neither; + use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; match ty.kind { ty::Infer(ty::IntVar(vid)) => { - if self.inner.borrow_mut().int_unification_table.probe_value(vid).is_some() { + if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() { Neither } else { UnconstrainedInt } } ty::Infer(ty::FloatVar(vid)) => { - if self.inner.borrow_mut().float_unification_table.probe_value(vid).is_some() { + if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() { Neither } else { UnconstrainedFloat @@ -698,21 +760,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // FIXME(const_generics): should there be an equivalent function for const variables? let mut vars: Vec> = inner - .type_variables + .type_variables() .unsolved_variables() .into_iter() .map(|t| self.tcx.mk_ty_var(t)) .collect(); vars.extend( - (0..inner.int_unification_table.len()) + (0..inner.int_unification_table().len()) .map(|i| ty::IntVid { index: i as u32 }) - .filter(|&vid| inner.int_unification_table.probe_value(vid).is_none()) + .filter(|&vid| inner.int_unification_table().probe_value(vid).is_none()) .map(|v| self.tcx.mk_int_var(v)), ); vars.extend( - (0..inner.float_unification_table.len()) + (0..inner.float_unification_table().len()) .map(|i| ty::FloatVid { index: i as u32 }) - .filter(|&vid| inner.float_unification_table.probe_value(vid).is_none()) + .filter(|&vid| inner.float_unification_table().probe_value(vid).is_none()) .map(|v| self.tcx.mk_float_var(v)), ); vars @@ -764,17 +826,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let in_snapshot = self.in_snapshot.replace(true); let mut inner = self.inner.borrow_mut(); + CombinedSnapshot { - projection_cache_snapshot: inner.projection_cache.snapshot(), - type_snapshot: inner.type_variables.snapshot(), - const_snapshot: inner.const_unification_table.snapshot(), - int_snapshot: inner.int_unification_table.snapshot(), - float_snapshot: inner.float_unification_table.snapshot(), + undo_snapshot: inner.undo_log.start_snapshot(), region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), - region_obligations_snapshot: inner.region_obligations.len(), universe: self.universe(), was_in_snapshot: in_snapshot, - was_skip_leak_check: self.skip_leak_check.get(), // Borrow tables "in progress" (i.e., during typeck) // to ban writes from within a snapshot to them. _in_progress_tables: self.in_progress_tables.map(|tables| tables.borrow()), @@ -784,59 +841,34 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) { debug!("rollback_to(cause={})", cause); let CombinedSnapshot { - projection_cache_snapshot, - type_snapshot, - const_snapshot, - int_snapshot, - float_snapshot, + undo_snapshot, region_constraints_snapshot, - region_obligations_snapshot, universe, was_in_snapshot, - was_skip_leak_check, _in_progress_tables, } = snapshot; self.in_snapshot.set(was_in_snapshot); self.universe.set(universe); - self.skip_leak_check.set(was_skip_leak_check); let mut inner = self.inner.borrow_mut(); - inner.projection_cache.rollback_to(projection_cache_snapshot); - inner.type_variables.rollback_to(type_snapshot); - inner.const_unification_table.rollback_to(const_snapshot); - inner.int_unification_table.rollback_to(int_snapshot); - inner.float_unification_table.rollback_to(float_snapshot); + inner.rollback_to(undo_snapshot); inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot); - inner.region_obligations.truncate(region_obligations_snapshot); } fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) { debug!("commit_from()"); let CombinedSnapshot { - projection_cache_snapshot, - type_snapshot, - const_snapshot, - int_snapshot, - float_snapshot, - region_constraints_snapshot, - region_obligations_snapshot: _, + undo_snapshot, + region_constraints_snapshot: _, universe: _, was_in_snapshot, - was_skip_leak_check, _in_progress_tables, } = snapshot; self.in_snapshot.set(was_in_snapshot); - self.skip_leak_check.set(was_skip_leak_check); - let mut inner = self.inner.borrow_mut(); - inner.projection_cache.commit(projection_cache_snapshot); - inner.type_variables.commit(type_snapshot); - inner.const_unification_table.commit(const_snapshot); - inner.int_unification_table.commit(int_snapshot); - inner.float_unification_table.commit(float_snapshot); - inner.unwrap_region_constraints().commit(region_constraints_snapshot); + self.inner.borrow_mut().commit(undo_snapshot); } /// Executes `f` and commit the bindings. @@ -890,10 +922,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { debug!("probe()"); let snapshot = self.start_snapshot(); - let skip_leak_check = should_skip || self.skip_leak_check.get(); - self.skip_leak_check.set(skip_leak_check); + let was_skip_leak_check = self.skip_leak_check.get(); + if should_skip { + self.skip_leak_check.set(true); + } let r = f(&snapshot); self.rollback_to("probe", snapshot); + self.skip_leak_check.set(was_skip_leak_check); r } @@ -909,7 +944,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.inner .borrow_mut() .unwrap_region_constraints() - .region_constraints_added_in_snapshot(&snapshot.region_constraints_snapshot) + .region_constraints_added_in_snapshot(&snapshot.undo_snapshot) } pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) { @@ -1027,7 +1062,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid { - self.inner.borrow_mut().type_variables.new_var(self.universe(), diverging, origin) + self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin) } pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { @@ -1039,7 +1074,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: TypeVariableOrigin, universe: ty::UniverseIndex, ) -> Ty<'tcx> { - let vid = self.inner.borrow_mut().type_variables.new_var(universe, false, origin); + let vid = self.inner.borrow_mut().type_variables().new_var(universe, false, origin); self.tcx.mk_ty_var(vid) } @@ -1064,20 +1099,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let vid = self .inner .borrow_mut() - .const_unification_table + .const_unification_table() .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } }); self.tcx.mk_const_var(vid, ty) } pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> { - self.inner.borrow_mut().const_unification_table.new_key(ConstVarValue { + self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe: self.universe() }, }) } fn next_int_var_id(&self) -> IntVid { - self.inner.borrow_mut().int_unification_table.new_key(None) + self.inner.borrow_mut().int_unification_table().new_key(None) } pub fn next_int_var(&self) -> Ty<'tcx> { @@ -1085,7 +1120,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } fn next_float_var_id(&self) -> FloatVid { - self.inner.borrow_mut().float_unification_table.new_key(None) + self.inner.borrow_mut().float_unification_table().new_key(None) } pub fn next_float_var(&self) -> Ty<'tcx> { @@ -1156,7 +1191,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // used in a path such as `Foo::::new()` will // use an inference variable for `C` with `[T, U]` // as the substitutions for the default, `(T, U)`. - let ty_var_id = self.inner.borrow_mut().type_variables.new_var( + let ty_var_id = self.inner.borrow_mut().type_variables().new_var( self.universe(), false, TypeVariableOrigin { @@ -1176,7 +1211,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span, }; let const_var_id = - self.inner.borrow_mut().const_unification_table.new_key(ConstVarValue { + self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe: self.universe() }, }); @@ -1229,18 +1264,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { outlives_env: &OutlivesEnvironment<'tcx>, mode: RegionckMode, ) { - assert!( - self.is_tainted_by_errors() || self.inner.borrow().region_obligations.is_empty(), - "region_obligations not empty: {:#?}", - self.inner.borrow().region_obligations - ); - let (var_infos, data) = self - .inner - .borrow_mut() - .region_constraints - .take() - .expect("regions already resolved") - .into_infos_and_data(); + let (var_infos, data) = { + let mut inner = self.inner.borrow_mut(); + let inner = &mut *inner; + assert!( + self.is_tainted_by_errors() || inner.region_obligations.is_empty(), + "region_obligations not empty: {:#?}", + inner.region_obligations + ); + inner + .region_constraint_storage + .take() + .expect("regions already resolved") + .with_log(&mut inner.undo_log) + .into_infos_and_data() + }; let region_rels = &RegionRelations::new( self.tcx, @@ -1301,12 +1339,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// called. This is used only during NLL processing to "hand off" ownership /// of the set of region variables into the NLL region context. pub fn take_region_var_origins(&self) -> VarInfos { - let (var_infos, data) = self - .inner - .borrow_mut() - .region_constraints + let mut inner = self.inner.borrow_mut(); + let (var_infos, data) = inner + .region_constraint_storage .take() .expect("regions already resolved") + .with_log(&mut inner.undo_log) .into_infos_and_data(); assert!(data.is_empty()); var_infos @@ -1330,7 +1368,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn probe_ty_var(&self, vid: TyVid) -> Result, ty::UniverseIndex> { use self::type_variable::TypeVariableValue; - match self.inner.borrow_mut().type_variables.probe(vid) { + match self.inner.borrow_mut().type_variables().probe(vid) { TypeVariableValue::Known { value } => Ok(value), TypeVariableValue::Unknown { universe } => Err(universe), } @@ -1352,7 +1390,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid { - self.inner.borrow_mut().type_variables.root_var(var) + self.inner.borrow_mut().type_variables().root_var(var) } /// Where possible, replaces type/const variables in @@ -1390,7 +1428,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, vid: ty::ConstVid<'tcx>, ) -> Result<&'tcx ty::Const<'tcx>, ty::UniverseIndex> { - match self.inner.borrow_mut().const_unification_table.probe_value(vid).val { + match self.inner.borrow_mut().const_unification_table().probe_value(vid).val { ConstVariableValue::Known { value } => Ok(value), ConstVariableValue::Unknown { universe } => Err(universe), } @@ -1452,6 +1490,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.report_and_explain_type_error(trace, &err) } + pub fn report_mismatched_consts( + &self, + cause: &ObligationCause<'tcx>, + expected: &'tcx ty::Const<'tcx>, + actual: &'tcx ty::Const<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + let trace = TypeTrace::consts(cause, true, expected, actual); + self.report_and_explain_type_error(trace, &err) + } + pub fn replace_bound_vars_with_fresh_vars( &self, span: Span, @@ -1477,7 +1526,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.replace_bound_vars(value, fld_r, fld_t, fld_c) } - /// See the [`region_constraints::verify_generic_bound`] method. + /// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method. pub fn verify_generic_bound( &self, origin: SubregionOrigin<'tcx>, @@ -1508,7 +1557,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn clear_caches(&self) { self.selection_cache.clear(); self.evaluation_cache.clear(); - self.inner.borrow_mut().projection_cache.clear(); + self.inner.borrow_mut().projection_cache().clear(); } fn universe(&self) -> ty::UniverseIndex { @@ -1571,14 +1620,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // // Note: if these two lines are combined into one we get // dynamic borrow errors on `self.inner`. - let known = self.inner.borrow_mut().type_variables.probe(v).known(); + let known = self.inner.borrow_mut().type_variables().probe(v).known(); known.map(|t| self.shallow_resolve_ty(t)).unwrap_or(typ) } ty::Infer(ty::IntVar(v)) => self .inner .borrow_mut() - .int_unification_table + .int_unification_table() .probe_value(v) .map(|v| v.to_type(self.tcx)) .unwrap_or(typ), @@ -1586,7 +1635,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ty::Infer(ty::FloatVar(v)) => self .inner .borrow_mut() - .float_unification_table + .float_unification_table() .probe_value(v) .map(|v| v.to_type(self.tcx)) .unwrap_or(typ), @@ -1606,13 +1655,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// having to resort to storing full `GenericArg`s in `stalled_on`. #[inline(always)] pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar<'tcx>) -> bool { + let mut inner = self.inner.borrow_mut(); match infer_var { TyOrConstInferVar::Ty(v) => { use self::type_variable::TypeVariableValue; // If `inlined_probe` returns a `Known` value, it never equals // `ty::Infer(ty::TyVar(v))`. - match self.inner.borrow_mut().type_variables.inlined_probe(v) { + match inner.type_variables().inlined_probe(v) { TypeVariableValue::Unknown { .. } => false, TypeVariableValue::Known { .. } => true, } @@ -1622,7 +1672,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // If `inlined_probe_value` returns a value it's always a // `ty::Int(_)` or `ty::UInt(_)`, which never matches a // `ty::Infer(_)`. - self.inner.borrow_mut().int_unification_table.inlined_probe_value(v).is_some() + inner.int_unification_table().inlined_probe_value(v).is_some() } TyOrConstInferVar::TyFloat(v) => { @@ -1630,7 +1680,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // `ty::Float(_)`, which never matches a `ty::Infer(_)`. // // Not `inlined_probe_value(v)` because this call site is colder. - self.inner.borrow_mut().float_unification_table.probe_value(v).is_some() + inner.float_unification_table().probe_value(v).is_some() } TyOrConstInferVar::Const(v) => { @@ -1638,7 +1688,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // `ty::ConstKind::Infer(ty::InferConst::Var(v))`. // // Not `inlined_probe_value(v)` because this call site is colder. - match self.inner.borrow_mut().const_unification_table.probe_value(v).val { + match inner.const_unification_table().probe_value(v).val { ConstVariableValue::Unknown { .. } => false, ConstVariableValue::Known { .. } => true, } @@ -1713,7 +1763,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { self.infcx .inner .borrow_mut() - .const_unification_table + .const_unification_table() .probe_value(*vid) .val .known() @@ -1738,6 +1788,15 @@ impl<'tcx> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) } } + pub fn consts( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } + } + pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> { TypeTrace { cause: ObligationCause::dummy(), diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs index c194e968013eb..8de8925100608 100644 --- a/src/librustc_infer/infer/nll_relate/mod.rs +++ b/src/librustc_infer/infer/nll_relate/mod.rs @@ -21,15 +21,15 @@ //! thing we relate in chalk are basically domain goals and their //! constituents) +use crate::infer::combine::ConstEquateRelation; use crate::infer::InferCtxt; use crate::infer::{ConstVarValue, ConstVariableValue}; -use crate::traits::DomainGoal; -use rustc::ty::error::TypeError; -use rustc::ty::fold::{TypeFoldable, TypeVisitor}; -use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc::ty::subst::GenericArg; -use rustc::ty::{self, InferConst, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor}; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; use std::fmt::Debug; #[derive(PartialEq)] @@ -78,9 +78,7 @@ pub trait TypeRelatingDelegate<'tcx> { /// delegate. fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>); - /// Push a domain goal that will need to be proved for the two types to - /// be related. Used for lazy normalization. - fn push_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>); + fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>); /// Creates a new universe index. Used when instantiating placeholders. fn create_next_universe(&mut self) -> ty::UniverseIndex; @@ -265,7 +263,6 @@ where value_ty: Ty<'tcx>, ) -> Ty<'tcx> { use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; - use crate::traits::WhereClause; use rustc_span::DUMMY_SP; match value_ty.kind { @@ -279,12 +276,7 @@ where var } - _ => { - let projection = ty::ProjectionPredicate { projection_ty, ty: value_ty }; - self.delegate - .push_domain_goal(DomainGoal::Holds(WhereClause::ProjectionEq(projection))); - value_ty - } + _ => bug!("should never be invoked with eager normalization"), } } @@ -322,7 +314,7 @@ where match value_ty.kind { ty::Infer(ty::TyVar(value_vid)) => { // Two type variables: just equate them. - self.infcx.inner.borrow_mut().type_variables.equate(vid, value_vid); + self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); return Ok(value_ty); } @@ -343,7 +335,7 @@ where assert!(!generalized_ty.has_infer_types_or_consts()); } - self.infcx.inner.borrow_mut().type_variables.instantiate(vid, generalized_ty); + self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); // The generalized values we extract from `canonical_var_values` have // been fully instantiated and hence the set of scopes we have @@ -373,7 +365,7 @@ where delegate: &mut self.delegate, first_free_index: ty::INNERMOST, ambient_variance: self.ambient_variance, - for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables.sub_root_var(for_vid), + for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), universe, }; @@ -668,7 +660,7 @@ where // Reset the ambient variance to covariant. This is needed // to correctly handle cases like // - // for<'a> fn(&'a u32, &'a u3) == for<'b, 'c> fn(&'b u32, &'c u32) + // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) // // Somewhat surprisingly, these two types are actually // **equal**, even though the one on the right looks more @@ -726,6 +718,15 @@ where } } +impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + self.delegate.const_equate(a, b); + } +} + /// When we encounter a binder like `for<..> fn(..)`, we actually have /// to walk the `fn` value to find all the values bound by the `for` /// (these are not explicitly present in the ty representation right @@ -870,7 +871,8 @@ where } ty::Infer(ty::TyVar(vid)) => { - let variables = &mut self.infcx.inner.borrow_mut().type_variables; + let mut inner = self.infcx.inner.borrow_mut(); + let variables = &mut inner.type_variables(); let vid = variables.root_var(vid); let sub_vid = variables.sub_root_var(vid); if sub_vid == self.for_vid_sub_root { @@ -904,7 +906,7 @@ where } } - ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { // No matter what mode we are in, // integer/floating-point types must be equal to be // relatable. @@ -972,7 +974,8 @@ where bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); } ty::ConstKind::Infer(InferConst::Var(vid)) => { - let variable_table = &mut self.infcx.inner.borrow_mut().const_unification_table; + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); let var_value = variable_table.probe_value(vid); match var_value.val.known() { Some(u) => self.relate(&u, &u), @@ -985,6 +988,7 @@ where } } } + ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a), _ => relate::super_relate_consts(self, a, a), } } diff --git a/src/librustc_infer/infer/outlives/env.rs b/src/librustc_infer/infer/outlives/env.rs index 6c1e86bf408b0..1a9e20e79fe1e 100644 --- a/src/librustc_infer/infer/outlives/env.rs +++ b/src/librustc_infer/infer/outlives/env.rs @@ -1,9 +1,9 @@ +use crate::infer::free_regions::FreeRegionMap; use crate::infer::{GenericKind, InferCtxt}; use crate::traits::query::OutlivesBound; -use rustc::ty; -use rustc::ty::free_region_map::FreeRegionMap; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_middle::ty; use super::explicit_outlives_bounds; @@ -168,8 +168,10 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { for outlives_bound in outlives_bounds { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { - OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) - | OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { + OutlivesBound::RegionSubRegion( + r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)), + &ty::ReVar(vid_b), + ) => { infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); } OutlivesBound::RegionSubParam(r_a, param_b) => { diff --git a/src/librustc_infer/infer/outlives/mod.rs b/src/librustc_infer/infer/outlives/mod.rs index 75cf742de31a7..289457e2bd0c2 100644 --- a/src/librustc_infer/infer/outlives/mod.rs +++ b/src/librustc_infer/infer/outlives/mod.rs @@ -4,8 +4,8 @@ pub mod env; pub mod obligations; pub mod verify; -use rustc::traits::query::OutlivesBound; -use rustc::ty; +use rustc_middle::traits::query::OutlivesBound; +use rustc_middle::ty; pub fn explicit_outlives_bounds<'tcx>( param_env: ty::ParamEnv<'tcx>, @@ -19,7 +19,8 @@ pub fn explicit_outlives_bounds<'tcx>( | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => None, ty::Predicate::RegionOutlives(ref data) => data .no_bound_vars() .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)), diff --git a/src/librustc_infer/infer/outlives/obligations.rs b/src/librustc_infer/infer/outlives/obligations.rs index e3790b027348e..48f6d937f2f7a 100644 --- a/src/librustc_infer/infer/outlives/obligations.rs +++ b/src/librustc_infer/infer/outlives/obligations.rs @@ -61,13 +61,16 @@ use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; -use crate::infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use crate::infer::{ + self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound, +}; use crate::traits::ObligationCause; -use rustc::ty::outlives::Component; -use rustc::ty::subst::GenericArgKind; -use rustc::ty::{self, Region, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::outlives::Component; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::undo_log::UndoLogs; use rustc_hir as hir; use smallvec::smallvec; @@ -84,7 +87,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { ) { debug!("register_region_obligation(body_id={:?}, obligation={:?})", body_id, obligation); - self.inner.borrow_mut().region_obligations.push((body_id, obligation)); + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushRegionObligation); + inner.region_obligations.push((body_id, obligation)); } pub fn register_region_obligation_with_cause( @@ -452,7 +457,7 @@ where // even though a satisfactory solution exists. let generic = GenericKind::Projection(projection_ty); let verify_bound = self.verify_bound.generic_bound(generic); - self.delegate.push_verify(origin, generic.clone(), region, verify_bound); + self.delegate.push_verify(origin, generic, region, verify_bound); } } diff --git a/src/librustc_infer/infer/outlives/verify.rs b/src/librustc_infer/infer/outlives/verify.rs index 08f73d2c9d2a8..5020dc4132cc3 100644 --- a/src/librustc_infer/infer/outlives/verify.rs +++ b/src/librustc_infer/infer/outlives/verify.rs @@ -1,12 +1,10 @@ use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::{GenericKind, VerifyBound}; use crate::traits; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; - -use smallvec::smallvec; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst}; +use rustc_middle::ty::{self, Ty, TyCtxt}; /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` /// obligation into a series of `'a: 'b` constraints and "verifys", as @@ -44,7 +42,32 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { match ty.kind { ty::Param(p) => self.param_bound(p), ty::Projection(data) => self.projection_bound(data), - _ => self.recursive_type_bound(ty), + ty::FnDef(_, substs) => { + // HACK(eddyb) ignore lifetimes found shallowly in `substs`. + // This is inconsistent with `ty::Adt` (including all substs), + // but consistent with previous (accidental) behavior. + // See https://github.com/rust-lang/rust/issues/70917 + // for further background and discussion. + let mut bounds = substs + .iter() + .filter_map(|&child| match child.unpack() { + GenericArgKind::Type(ty) => Some(self.type_bound(ty)), + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Const(_) => Some(self.recursive_bound(child)), + }) + .filter(|bound| { + // Remove bounds that must hold, since they are not interesting. + !bound.must_hold() + }); + + match (bounds.next(), bounds.next()) { + (Some(first), None) => first, + (first, second) => VerifyBound::AllBounds( + first.into_iter().chain(second).chain(bounds).collect(), + ), + } + } + _ => self.recursive_bound(ty.into()), } } @@ -144,25 +167,33 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // see the extensive comment in projection_must_outlive let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_type_bound(ty); + let recursive_bound = self.recursive_bound(ty.into()); VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) } - fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - let mut bounds = ty.walk_shallow().map(|subty| self.type_bound(subty)).collect::>(); - - let mut regions = smallvec![]; - ty.push_regions(&mut regions); - regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push(VerifyBound::AllBounds( - regions.into_iter().map(|r| VerifyBound::OutlivedBy(r)).collect(), - )); - - // remove bounds that must hold, since they are not interesting - bounds.retain(|b| !b.must_hold()); + fn recursive_bound(&self, parent: GenericArg<'tcx>) -> VerifyBound<'tcx> { + let mut bounds = parent + .walk_shallow() + .filter_map(|child| match child.unpack() { + GenericArgKind::Type(ty) => Some(self.type_bound(ty)), + GenericArgKind::Lifetime(lt) => { + // Ignore late-bound regions. + if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None } + } + GenericArgKind::Const(_) => Some(self.recursive_bound(child)), + }) + .filter(|bound| { + // Remove bounds that must hold, since they are not interesting. + !bound.must_hold() + }); - if bounds.len() == 1 { bounds.pop().unwrap() } else { VerifyBound::AllBounds(bounds) } + match (bounds.next(), bounds.next()) { + (Some(first), None) => first, + (first, second) => { + VerifyBound::AllBounds(first.into_iter().chain(second).chain(bounds).collect()) + } + } } /// Searches the environment for where-clauses like `G: 'a` where @@ -192,7 +223,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // like `T` and `T::Item`. It may not work as well for things // like `>::Item`. let c_b = self.param_env.caller_bounds; - let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b); + let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b.into_iter()); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -284,13 +315,12 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { let tcx = self.tcx; let assoc_item = tcx.associated_item(assoc_item_def_id); let trait_def_id = assoc_item.container.assert_trait(); - let trait_predicates = - tcx.predicates_of(trait_def_id).predicates.iter().map(|(p, _)| *p).collect(); + let trait_predicates = tcx.predicates_of(trait_def_id).predicates.iter().map(|(p, _)| *p); let identity_substs = InternalSubsts::identity_for_item(tcx, assoc_item_def_id); let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); self.collect_outlives_from_predicate_list( move |ty| ty == identity_proj, - traits::elaborate_predicates(tcx, trait_predicates), + traits::elaborate_predicates(tcx, trait_predicates).map(|o| o.predicate), ) .map(|b| b.1) } @@ -304,10 +334,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn collect_outlives_from_predicate_list( &self, compare_ty: impl Fn(Ty<'tcx>) -> bool, - predicates: impl IntoIterator>>, + predicates: impl Iterator>>, ) -> impl Iterator, ty::Region<'tcx>>> { predicates - .into_iter() .filter_map(|p| p.as_ref().to_opt_type_outlives()) .filter_map(|p| p.no_bound_vars()) .filter(move |p| compare_ty(p.0)) diff --git a/src/librustc_infer/infer/region_constraints/leak_check.rs b/src/librustc_infer/infer/region_constraints/leak_check.rs index 6ebe3f5759760..473550d5433df 100644 --- a/src/librustc_infer/infer/region_constraints/leak_check.rs +++ b/src/librustc_infer/infer/region_constraints/leak_check.rs @@ -1,9 +1,10 @@ use super::*; use crate::infer::{CombinedSnapshot, PlaceholderMap}; -use rustc::ty::error::TypeError; -use rustc::ty::relate::RelateResult; +use rustc_data_structures::undo_log::UndoLogs; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::RelateResult; -impl<'tcx> RegionConstraintCollector<'tcx> { +impl<'tcx> RegionConstraintCollector<'_, 'tcx> { /// Searches region constraints created since `snapshot` that /// affect one of the placeholders in `placeholder_map`, returning /// an error if any of the placeholders are related to another @@ -31,7 +32,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { ) -> RelateResult<'tcx, ()> { debug!("leak_check(placeholders={:?})", placeholder_map); - assert!(self.in_snapshot()); + assert!(UndoLogs::>::in_snapshot(&self.undo_log)); // Go through each placeholder that we created. for &placeholder_region in placeholder_map.values() { @@ -45,7 +46,11 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // in some way. This means any region that either outlives // or is outlived by a placeholder. let mut taint_set = TaintSet::new(TaintDirections::both(), placeholder_region); - taint_set.fixed_point(tcx, &self.undo_log, &self.data.verifys); + taint_set.fixed_point( + tcx, + self.undo_log.region_constraints(), + &self.storage.data.verifys, + ); let tainted_regions = taint_set.into_set(); // Report an error if two placeholders in the same universe @@ -88,19 +93,21 @@ impl<'tcx> TaintSet<'tcx> { TaintSet { directions, regions } } - fn fixed_point( + fn fixed_point<'a>( &mut self, tcx: TyCtxt<'tcx>, - undo_log: &[UndoLog<'tcx>], + undo_log: impl IntoIterator> + Clone, verifys: &[Verify<'tcx>], - ) { + ) where + 'tcx: 'a, + { let mut prev_len = 0; while prev_len < self.len() { debug!("tainted: prev_len = {:?} new_len = {:?}", prev_len, self.len()); prev_len = self.len(); - for undo_entry in undo_log { + for undo_entry in undo_log.clone() { match undo_entry { &AddConstraint(Constraint::VarSubVar(a, b)) => { self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); diff --git a/src/librustc_infer/infer/region_constraints/mod.rs b/src/librustc_infer/infer/region_constraints/mod.rs index 72637f4544a35..0c9f002a2a21d 100644 --- a/src/librustc_infer/infer/region_constraints/mod.rs +++ b/src/librustc_infer/infer/region_constraints/mod.rs @@ -4,17 +4,21 @@ use self::CombineMapType::*; use self::UndoLog::*; use super::unify_key; -use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; +use super::{ + InferCtxtUndoLogs, MiscVariable, RegionVariableOrigin, Rollback, Snapshot, SubregionOrigin, +}; -use rustc::ty::ReStatic; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::{ReLateBound, ReVar}; -use rustc::ty::{Region, RegionVid}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; +use rustc_data_structures::unify::UnifyKey; use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; +use rustc_middle::ty::ReStatic; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ReLateBound, ReVar}; +use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; use std::collections::BTreeMap; @@ -23,10 +27,10 @@ use std::{cmp, fmt, mem}; mod leak_check; -pub use rustc::infer::MemberConstraint; +pub use rustc_middle::infer::MemberConstraint; #[derive(Default)] -pub struct RegionConstraintCollector<'tcx> { +pub struct RegionConstraintStorage<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. var_infos: IndexVec, @@ -42,20 +46,6 @@ pub struct RegionConstraintCollector<'tcx> { /// exist). This prevents us from making many such regions. glbs: CombineMap<'tcx>, - /// The undo log records actions that might later be undone. - /// - /// Note: `num_open_snapshots` is used to track if we are actively - /// snapshotting. When the `start_snapshot()` method is called, we - /// increment `num_open_snapshots` to indicate that we are now actively - /// snapshotting. The reason for this is that otherwise we end up adding - /// entries for things like the lower bound on a variable and so forth, - /// which can never be rolled back. - undo_log: Vec>, - - /// The number of open snapshots, i.e., those that haven't been committed or - /// rolled back. - num_open_snapshots: usize, - /// When we add a R1 == R2 constriant, we currently add (a) edges /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this /// table. You can then call `opportunistic_resolve_var` early @@ -64,13 +54,31 @@ pub struct RegionConstraintCollector<'tcx> { /// is iterating to a fixed point, because otherwise we sometimes /// would wind up with a fresh stream of region variables that /// have been equated but appear distinct. - unification_table: ut::UnificationTable>, + pub(super) unification_table: ut::UnificationTableStorage, /// a flag set to true when we perform any unifications; this is used /// to micro-optimize `take_and_reset_data` any_unifications: bool, } +pub struct RegionConstraintCollector<'a, 'tcx> { + storage: &'a mut RegionConstraintStorage<'tcx>, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + +impl std::ops::Deref for RegionConstraintCollector<'_, 'tcx> { + type Target = RegionConstraintStorage<'tcx>; + fn deref(&self) -> &RegionConstraintStorage<'tcx> { + self.storage + } +} + +impl std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> { + fn deref_mut(&mut self) -> &mut RegionConstraintStorage<'tcx> { + self.storage + } +} + pub type VarInfos = IndexVec; /// The full set of region constraints gathered up by the collector. @@ -147,11 +155,6 @@ impl Constraint<'_> { } } -/// `VerifyGenericBound(T, _, R, RS)`: the parameter type `T` (or -/// associated type) must outlive the region `R`. `T` is known to -/// outlive `RS`. Therefore, verify that `R <= RS[i]` for some -/// `i`. Inference variables may be involved (but this verification -/// step doesn't influence inference). #[derive(Debug, Clone)] pub struct Verify<'tcx> { pub kind: GenericKind<'tcx>, @@ -263,13 +266,13 @@ pub enum VerifyBound<'tcx> { } #[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct TwoRegions<'tcx> { +pub(crate) struct TwoRegions<'tcx> { a: Region<'tcx>, b: Region<'tcx>, } #[derive(Copy, Clone, PartialEq)] -enum UndoLog<'tcx> { +pub(crate) enum UndoLog<'tcx> { /// We added `RegionVid`. AddVar(RegionVid), @@ -295,7 +298,7 @@ enum UndoLog<'tcx> { } #[derive(Copy, Clone, PartialEq)] -enum CombineMapType { +pub(crate) enum CombineMapType { Lub, Glb, } @@ -309,8 +312,6 @@ pub struct RegionVariableInfo { } pub struct RegionSnapshot { - length: usize, - region_snapshot: ut::Snapshot>, any_unifications: bool, } @@ -339,11 +340,48 @@ impl TaintDirections { } } -impl<'tcx> RegionConstraintCollector<'tcx> { +impl<'tcx> RegionConstraintStorage<'tcx> { pub fn new() -> Self { Self::default() } + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> RegionConstraintCollector<'a, 'tcx> { + RegionConstraintCollector { storage: self, undo_log } + } + + fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) { + match undo_entry { + Purged => { + // nothing to do here + } + AddVar(vid) => { + self.var_infos.pop().unwrap(); + assert_eq!(self.var_infos.len(), vid.index() as usize); + } + AddConstraint(ref constraint) => { + self.data.constraints.remove(constraint); + } + AddVerify(index) => { + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); + } + AddGiven(sub, sup) => { + self.data.givens.remove(&(sub, sup)); + } + AddCombination(Glb, ref regions) => { + self.glbs.remove(regions); + } + AddCombination(Lub, ref regions) => { + self.lubs.remove(regions); + } + } + } +} + +impl<'tcx> RegionConstraintCollector<'_, 'tcx> { pub fn num_region_vars(&self) -> usize { self.var_infos.len() } @@ -356,8 +394,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> { /// /// Not legal during a snapshot. pub fn into_infos_and_data(self) -> (VarInfos, RegionConstraintData<'tcx>) { - assert!(!self.in_snapshot()); - (self.var_infos, self.data) + assert!(!UndoLogs::>::in_snapshot(&self.undo_log)); + (mem::take(&mut self.storage.var_infos), mem::take(&mut self.storage.data)) } /// Takes (and clears) the current set of constraints. Note that @@ -373,21 +411,19 @@ impl<'tcx> RegionConstraintCollector<'tcx> { /// /// Not legal during a snapshot. pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { - assert!(!self.in_snapshot()); + assert!(!UndoLogs::>::in_snapshot(&self.undo_log)); // If you add a new field to `RegionConstraintCollector`, you // should think carefully about whether it needs to be cleared // or updated in some way. - let RegionConstraintCollector { + let RegionConstraintStorage { var_infos: _, data, lubs, glbs, - undo_log: _, - num_open_snapshots: _, - unification_table, + unification_table: _, any_unifications, - } = self; + } = self.storage; // Clear the tables of (lubs, glbs), so that we will create // fresh regions if we do a LUB operation. As it happens, @@ -396,102 +432,35 @@ impl<'tcx> RegionConstraintCollector<'tcx> { lubs.clear(); glbs.clear(); + let data = mem::take(data); + // Clear all unifications and recreate the variables a "now // un-unified" state. Note that when we unify `a` and `b`, we // also insert `a <= b` and a `b <= a` edges, so the // `RegionConstraintData` contains the relationship here. if *any_unifications { - unification_table.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid }); *any_unifications = false; + self.unification_table() + .reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid }); } - mem::take(data) + data } pub fn data(&self) -> &RegionConstraintData<'tcx> { &self.data } - fn in_snapshot(&self) -> bool { - self.num_open_snapshots > 0 - } - pub fn start_snapshot(&mut self) -> RegionSnapshot { - let length = self.undo_log.len(); - debug!("RegionConstraintCollector: start_snapshot({})", length); - self.num_open_snapshots += 1; - RegionSnapshot { - length, - region_snapshot: self.unification_table.snapshot(), - any_unifications: self.any_unifications, - } - } - - fn assert_open_snapshot(&self, snapshot: &RegionSnapshot) { - assert!(self.undo_log.len() >= snapshot.length); - assert!(self.num_open_snapshots > 0); - } - - pub fn commit(&mut self, snapshot: RegionSnapshot) { - debug!("RegionConstraintCollector: commit({})", snapshot.length); - self.assert_open_snapshot(&snapshot); - - if self.num_open_snapshots == 1 { - // The root snapshot. It's safe to clear the undo log because - // there's no snapshot further out that we might need to roll back - // to. - assert!(snapshot.length == 0); - self.undo_log.clear(); - } - - self.num_open_snapshots -= 1; - - self.unification_table.commit(snapshot.region_snapshot); + debug!("RegionConstraintCollector: start_snapshot"); + RegionSnapshot { any_unifications: self.any_unifications } } pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); - self.assert_open_snapshot(&snapshot); - - while self.undo_log.len() > snapshot.length { - let undo_entry = self.undo_log.pop().unwrap(); - self.rollback_undo_entry(undo_entry); - } - - self.num_open_snapshots -= 1; - - self.unification_table.rollback_to(snapshot.region_snapshot); self.any_unifications = snapshot.any_unifications; } - fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) { - match undo_entry { - Purged => { - // nothing to do here - } - AddVar(vid) => { - self.var_infos.pop().unwrap(); - assert_eq!(self.var_infos.len(), vid.index() as usize); - } - AddConstraint(ref constraint) => { - self.data.constraints.remove(constraint); - } - AddVerify(index) => { - self.data.verifys.pop(); - assert_eq!(self.data.verifys.len(), index); - } - AddGiven(sub, sup) => { - self.data.givens.remove(&(sub, sup)); - } - AddCombination(Glb, ref regions) => { - self.glbs.remove(regions); - } - AddCombination(Lub, ref regions) => { - self.lubs.remove(regions); - } - } - } - pub fn new_region_var( &mut self, universe: ty::UniverseIndex, @@ -499,11 +468,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> { ) -> RegionVid { let vid = self.var_infos.push(RegionVariableInfo { origin, universe }); - let u_vid = self.unification_table.new_key(unify_key::RegionVidKey { min_vid: vid }); + let u_vid = self.unification_table().new_key(unify_key::RegionVidKey { min_vid: vid }); assert_eq!(vid, u_vid); - if self.in_snapshot() { - self.undo_log.push(AddVar(vid)); - } + self.undo_log.push(AddVar(vid)); debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin); vid } @@ -525,19 +492,29 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn pop_placeholders(&mut self, placeholders: &FxHashSet>) { debug!("pop_placeholders(placeholders={:?})", placeholders); - assert!(self.in_snapshot()); + assert!(UndoLogs::>::in_snapshot(&self.undo_log)); let constraints_to_kill: Vec = self .undo_log .iter() .enumerate() .rev() - .filter(|&(_, undo_entry)| kill_constraint(placeholders, undo_entry)) + .filter(|&(_, undo_entry)| match undo_entry { + super::UndoLog::RegionConstraintCollector(undo_entry) => { + kill_constraint(placeholders, undo_entry) + } + _ => false, + }) .map(|(index, _)| index) .collect(); for index in constraints_to_kill { - let undo_entry = mem::replace(&mut self.undo_log[index], Purged); + let undo_entry = match &mut self.undo_log[index] { + super::UndoLog::RegionConstraintCollector(undo_entry) => { + mem::replace(undo_entry, Purged) + } + _ => unreachable!(), + }; self.rollback_undo_entry(undo_entry); } @@ -571,12 +548,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // never overwrite an existing (constraint, origin) - only insert one if it isn't // present in the map yet. This prevents origins from outside the snapshot being // replaced with "less informative" origins e.g., during calls to `can_eq` - let in_snapshot = self.in_snapshot(); let undo_log = &mut self.undo_log; - self.data.constraints.entry(constraint).or_insert_with(|| { - if in_snapshot { - undo_log.push(AddConstraint(constraint)); - } + self.storage.data.constraints.entry(constraint).or_insert_with(|| { + undo_log.push(AddConstraint(constraint)); origin }); } @@ -594,9 +568,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { let index = self.data.verifys.len(); self.data.verifys.push(verify); - if self.in_snapshot() { - self.undo_log.push(AddVerify(index)); - } + self.undo_log.push(AddVerify(index)); } pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { @@ -604,9 +576,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { if self.data.givens.insert((sub, sup)) { debug!("add_given({:?} <= {:?})", sub, sup); - if self.in_snapshot() { - self.undo_log.push(AddGiven(sub, sup)); - } + self.undo_log.push(AddGiven(sub, sup)); } } @@ -624,7 +594,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { debug!("make_eqregion: uniying {:?} with {:?}", sub, sup); - self.unification_table.union(sub, sup); + self.unification_table().union(sub, sup); self.any_unifications = true; } } @@ -687,7 +657,6 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } - /// See [`Verify::VerifyGenericBound`]. pub fn verify_generic_bound( &mut self, origin: SubregionOrigin<'tcx>, @@ -747,7 +716,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { tcx: TyCtxt<'tcx>, rid: RegionVid, ) -> ty::Region<'tcx> { - let vid = self.unification_table.probe_value(rid).min_vid; + let vid = self.unification_table().probe_value(rid).min_vid; tcx.mk_region(ty::ReVar(vid)) } @@ -775,9 +744,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { let c_universe = cmp::max(a_universe, b_universe); let c = self.new_region_var(c_universe, MiscVariable(origin.span())); self.combine_map(t).insert(vars, c); - if self.in_snapshot() { - self.undo_log.push(AddCombination(t, vars)); - } + self.undo_log.push(AddCombination(t, vars)); let new_r = tcx.mk_region(ReVar(c)); for &old_r in &[a, b] { match t { @@ -805,9 +772,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn vars_since_snapshot( &self, - mark: &RegionSnapshot, + value_count: usize, ) -> (Range, Vec) { - let range = self.unification_table.vars_since_snapshot(&mark.region_snapshot); + let range = RegionVid::from_index(value_count as u32) + ..RegionVid::from_index(self.unification_table.len() as u32); ( range.clone(), (range.start.index()..range.end.index()) @@ -817,9 +785,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } /// See [`RegionInference::region_constraints_added_in_snapshot`]. - pub fn region_constraints_added_in_snapshot(&self, mark: &RegionSnapshot) -> Option { - self.undo_log[mark.length..] - .iter() + pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Option { + self.undo_log + .region_constraints_in_snapshot(mark) .map(|&elt| match elt { AddConstraint(constraint) => Some(constraint.involves_placeholders()), _ => None, @@ -827,11 +795,15 @@ impl<'tcx> RegionConstraintCollector<'tcx> { .max() .unwrap_or(None) } + + fn unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, ty::RegionVid> { + ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) + } } impl fmt::Debug for RegionSnapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RegionSnapshot(length={})", self.length) + write!(f, "RegionSnapshot") } } @@ -916,3 +888,9 @@ impl<'tcx> RegionConstraintData<'tcx> { && givens.is_empty() } } + +impl<'tcx> Rollback> for RegionConstraintStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + self.rollback_undo_entry(undo) + } +} diff --git a/src/librustc_infer/infer/resolve.rs b/src/librustc_infer/infer/resolve.rs index ce0f2f40894f9..e28cf49c7f253 100644 --- a/src/librustc_infer/infer/resolve.rs +++ b/src/librustc_infer/infer/resolve.rs @@ -1,7 +1,7 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::{FixupError, FixupResult, InferCtxt, Span}; -use rustc::ty::fold::{TypeFolder, TypeVisitor}; -use rustc::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::fold::{TypeFolder, TypeVisitor}; +use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; /////////////////////////////////////////////////////////////////////////// // OPPORTUNISTIC VAR RESOLVER @@ -123,7 +123,8 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { // Since we called `shallow_resolve` above, this must // be an (as yet...) unresolved inference variable. let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty { - let ty_vars = &self.infcx.inner.borrow().type_variables; + let mut inner = self.infcx.inner.borrow_mut(); + let ty_vars = &inner.type_variables(); if let TypeVariableOrigin { kind: TypeVariableOriginKind::TypeParameterDefinition(_, _), span, @@ -181,10 +182,8 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.needs_infer() && !ty::keep_local(&t) { + if !t.needs_infer() { t // micro-optimize -- if there is nothing in this type that this fold affects... - // ^ we need to have the `keep_local` check to un-default - // defaulted tuples. } else { let t = self.infcx.shallow_resolve(t); match t.kind { @@ -222,16 +221,14 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { } fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if !c.needs_infer() && !ty::keep_local(&c) { + if !c.needs_infer() { c // micro-optimize -- if there is nothing in this const that this fold affects... - // ^ we need to have the `keep_local` check to un-default - // defaulted tuples. } else { let c = self.infcx.shallow_resolve(c); match c.val { ty::ConstKind::Infer(InferConst::Var(vid)) => { self.err = Some(FixupError::UnresolvedConst(vid)); - return self.tcx().consts.err; + return self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty: c.ty }); } ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("Unexpected const in full const resolver: {:?}", c); diff --git a/src/librustc_infer/infer/sub.rs b/src/librustc_infer/infer/sub.rs index f6fc38b535887..1ec67ef2efa9d 100644 --- a/src/librustc_infer/infer/sub.rs +++ b/src/librustc_infer/infer/sub.rs @@ -1,11 +1,12 @@ use super::combine::{CombineFields, RelationDir}; use super::SubregionOrigin; +use crate::infer::combine::ConstEquateRelation; use crate::traits::Obligation; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; -use rustc::ty::TyVar; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty, TyCtxt}; use std::mem; /// Ensures `a` is made a subtype of `b`. Returns `a` on success. @@ -80,8 +81,8 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { } let infcx = self.fields.infcx; - let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b); + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); match (&a.kind, &b.kind) { (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => { // Shouldn't have any LBR here, so we can safely put @@ -95,7 +96,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { // have to record in the `type_variables` tracker that // the two variables are equal modulo subtyping, which // is important to the occurs check later on. - infcx.inner.borrow_mut().type_variables.sub(a_vid, b_vid); + infcx.inner.borrow_mut().type_variables().sub(a_vid, b_vid); self.fields.obligations.push(Obligation::new( self.fields.trace.cause.clone(), self.fields.param_env, @@ -169,3 +170,9 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { self.fields.higher_ranked_sub(a, b, self.a_is_expected) } } + +impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} diff --git a/src/librustc_infer/infer/type_variable.rs b/src/librustc_infer/infer/type_variable.rs index 53bc70a5344ec..f68692391a288 100644 --- a/src/librustc_infer/infer/type_variable.rs +++ b/src/librustc_infer/infer/type_variable.rs @@ -1,22 +1,70 @@ -use rustc::ty::{self, Ty, TyVid}; use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, Ty, TyVid}; use rustc_span::symbol::Symbol; use rustc_span::Span; +use crate::infer::InferCtxtUndoLogs; + use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::unify as ut; use std::cmp; use std::marker::PhantomData; use std::ops::Range; -use std::u32; -pub struct TypeVariableTable<'tcx> { - values: sv::SnapshotVec, +use rustc_data_structures::undo_log::{Rollback, UndoLogs}; + +/// Represents a single undo-able action that affects a type inference variable. +pub(crate) enum UndoLog<'tcx> { + EqRelation(sv::UndoLog>>), + SubRelation(sv::UndoLog>), + Values(sv::UndoLog), +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>>) -> Self { + UndoLog::EqRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>) -> Self { + UndoLog::SubRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From> for UndoLog<'tcx> { + fn from(l: sv::UndoLog) -> Self { + UndoLog::Values(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From for UndoLog<'tcx> { + fn from(l: Instantiate) -> Self { + UndoLog::Values(sv::UndoLog::Other(l)) + } +} + +impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo), + UndoLog::Values(undo) => self.values.reverse(undo), + } + } +} + +pub struct TypeVariableStorage<'tcx> { + values: sv::SnapshotVecStorage, /// Two variables are unified in `eq_relations` when we have a /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. - eq_relations: ut::UnificationTable>>, + eq_relations: ut::UnificationTableStorage>, /// Two variables are unified in `sub_relations` when we have a /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second @@ -35,7 +83,17 @@ pub struct TypeVariableTable<'tcx> { /// This is reasonable because, in Rust, subtypes have the same /// "skeleton" and hence there is no possible type such that /// (e.g.) `Box <: ?3` for any `?3`. - sub_relations: ut::UnificationTable>, + sub_relations: ut::UnificationTableStorage, +} + +pub struct TypeVariableTable<'a, 'tcx> { + values: &'a mut sv::SnapshotVecStorage, + + eq_relations: &'a mut ut::UnificationTableStorage>, + + sub_relations: &'a mut ut::UnificationTableStorage, + + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, } #[derive(Copy, Clone, Debug)] @@ -63,7 +121,7 @@ pub enum TypeVariableOriginKind { LatticeVariable, } -struct TypeVariableData { +pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, diverging: bool, } @@ -92,27 +150,31 @@ impl<'tcx> TypeVariableValue<'tcx> { } } -pub struct Snapshot<'tcx> { - snapshot: sv::Snapshot, - eq_snapshot: ut::Snapshot>>, - sub_snapshot: ut::Snapshot>, -} - -struct Instantiate { +pub(crate) struct Instantiate { vid: ty::TyVid, } -struct Delegate; +pub(crate) struct Delegate; -impl<'tcx> TypeVariableTable<'tcx> { - pub fn new() -> TypeVariableTable<'tcx> { - TypeVariableTable { - values: sv::SnapshotVec::new(), - eq_relations: ut::UnificationTable::new(), - sub_relations: ut::UnificationTable::new(), +impl<'tcx> TypeVariableStorage<'tcx> { + pub fn new() -> TypeVariableStorage<'tcx> { + TypeVariableStorage { + values: sv::SnapshotVecStorage::new(), + eq_relations: ut::UnificationTableStorage::new(), + sub_relations: ut::UnificationTableStorage::new(), } } + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> TypeVariableTable<'a, 'tcx> { + let TypeVariableStorage { values, eq_relations, sub_relations } = self; + TypeVariableTable { values, eq_relations, sub_relations, undo_log } + } +} + +impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Returns the diverges flag given when `vid` was created. /// /// Note that this function does not return care whether @@ -135,8 +197,8 @@ impl<'tcx> TypeVariableTable<'tcx> { pub fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); - self.eq_relations.union(a, b); - self.sub_relations.union(a, b); + self.eq_relations().union(a, b); + self.sub_relations().union(a, b); } /// Records that `a <: b`, depending on `dir`. @@ -145,7 +207,7 @@ impl<'tcx> TypeVariableTable<'tcx> { pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); - self.sub_relations.union(a, b); + self.sub_relations().union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -155,18 +217,18 @@ impl<'tcx> TypeVariableTable<'tcx> { let vid = self.root_var(vid); debug_assert!(self.probe(vid).is_unknown()); debug_assert!( - self.eq_relations.probe_value(vid).is_unknown(), + self.eq_relations().probe_value(vid).is_unknown(), "instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}", vid, ty, - self.eq_relations.probe_value(vid) + self.eq_relations().probe_value(vid) ); - self.eq_relations.union_value(vid, TypeVariableValue::Known { value: ty }); + self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty }); // Hack: we only need this so that `types_escaping_snapshot` // can see what has been unified; see the Delegate impl for // more details. - self.values.record(Instantiate { vid }); + self.undo_log.push(Instantiate { vid }); } /// Creates a new type variable. @@ -185,12 +247,12 @@ impl<'tcx> TypeVariableTable<'tcx> { diverging: bool, origin: TypeVariableOrigin, ) -> ty::TyVid { - let eq_key = self.eq_relations.new_key(TypeVariableValue::Unknown { universe }); + let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); - let sub_key = self.sub_relations.new_key(()); + let sub_key = self.sub_relations().new_key(()); assert_eq!(eq_key.vid, sub_key); - let index = self.values.push(TypeVariableData { origin, diverging }); + let index = self.values().push(TypeVariableData { origin, diverging }); assert_eq!(eq_key.vid.index, index as u32); debug!( @@ -212,7 +274,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// algorithm), so `root_var(a) == root_var(b)` implies that `a == /// b` (transitively). pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { - self.eq_relations.find(vid).vid + self.eq_relations().find(vid).vid } /// Returns the "root" variable of `vid` in the `sub_relations` @@ -223,7 +285,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// /// exists X. (a <: X || X <: a) && (b <: X || X <: b) pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { - self.sub_relations.find(vid) + self.sub_relations().find(vid) } /// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some @@ -241,7 +303,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// An always-inlined variant of `probe`, for very hot call sites. #[inline(always)] pub fn inlined_probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { - self.eq_relations.inlined_probe_value(vid) + self.eq_relations().inlined_probe_value(vid) } /// If `t` is a type-inference variable, and it has been @@ -257,56 +319,29 @@ impl<'tcx> TypeVariableTable<'tcx> { } } - /// Creates a snapshot of the type variable state. This snapshot - /// must later be committed (`commit()`) or rolled back - /// (`rollback_to()`). Nested snapshots are permitted, but must - /// be processed in a stack-like fashion. - pub fn snapshot(&mut self) -> Snapshot<'tcx> { - Snapshot { - snapshot: self.values.start_snapshot(), - eq_snapshot: self.eq_relations.snapshot(), - sub_snapshot: self.sub_relations.snapshot(), - } + fn values( + &mut self, + ) -> sv::SnapshotVec, &mut InferCtxtUndoLogs<'tcx>> { + self.values.with_log(self.undo_log) } - /// Undoes all changes since the snapshot was created. Any - /// snapshots created since that point must already have been - /// committed or rolled back. - pub fn rollback_to(&mut self, s: Snapshot<'tcx>) { - debug!("rollback_to{:?}", { - for action in self.values.actions_since_snapshot(&s.snapshot) { - if let sv::UndoLog::NewElem(index) = *action { - debug!("inference variable _#{}t popped", index) - } - } - }); - - let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; - self.values.rollback_to(snapshot); - self.eq_relations.rollback_to(eq_snapshot); - self.sub_relations.rollback_to(sub_snapshot); + fn eq_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidEqKey<'tcx>> { + self.eq_relations.with_log(self.undo_log) } - /// Commits all changes since the snapshot was created, making - /// them permanent (unless this snapshot was created within - /// another snapshot). Any snapshots created since that point - /// must already have been committed or rolled back. - pub fn commit(&mut self, s: Snapshot<'tcx>) { - let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; - self.values.commit(snapshot); - self.eq_relations.commit(eq_snapshot); - self.sub_relations.commit(sub_snapshot); + fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> { + self.sub_relations.with_log(self.undo_log) } /// Returns a range of the type variables created during the snapshot. pub fn vars_since_snapshot( &mut self, - s: &Snapshot<'tcx>, + value_count: usize, ) -> (Range, Vec) { - let range = self.eq_relations.vars_since_snapshot(&s.eq_snapshot); + let range = TyVid { index: value_count as u32 }..TyVid { index: self.num_vars() as u32 }; ( - range.start.vid..range.end.vid, - (range.start.vid.index..range.end.vid.index) + range.start..range.end, + (range.start.index..range.end.index) .map(|index| self.values.get(index as usize).origin) .collect(), ) @@ -318,14 +353,15 @@ impl<'tcx> TypeVariableTable<'tcx> { /// a type variable `V0`, then we started the snapshot, then we /// created a type variable `V1`, unified `V0` with `T0`, and /// unified `V1` with `T1`, this function would return `{T0}`. - pub fn types_escaping_snapshot(&mut self, s: &Snapshot<'tcx>) -> Vec> { + pub fn types_escaping_snapshot(&mut self, s: &super::Snapshot<'tcx>) -> Vec> { let mut new_elem_threshold = u32::MAX; let mut escaping_types = Vec::new(); - let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot); + let actions_since_snapshot = self.undo_log.actions_since_snapshot(s); debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len()); - for action in actions_since_snapshot { - match *action { - sv::UndoLog::NewElem(index) => { + for i in 0..actions_since_snapshot.len() { + let actions_since_snapshot = self.undo_log.actions_since_snapshot(s); + match actions_since_snapshot[i] { + super::UndoLog::TypeVariables(UndoLog::Values(sv::UndoLog::NewElem(index))) => { // if any new variables were created during the // snapshot, remember the lower index (which will // always be the first one we see). Note that this @@ -335,11 +371,17 @@ impl<'tcx> TypeVariableTable<'tcx> { debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold); } - sv::UndoLog::Other(Instantiate { vid, .. }) => { + super::UndoLog::TypeVariables(UndoLog::Values(sv::UndoLog::Other( + Instantiate { vid, .. }, + ))) => { if vid.index < new_elem_threshold { // quick check to see if this variable was // created since the snapshot started or not. - let escaping_type = match self.eq_relations.probe_value(vid) { + let mut eq_relations = ut::UnificationTable::with_log( + &mut *self.eq_relations, + &mut *self.undo_log, + ); + let escaping_type = match eq_relations.probe_value(vid) { TypeVariableValue::Unknown { .. } => bug!(), TypeVariableValue::Known { value } => value, }; @@ -396,7 +438,7 @@ impl sv::SnapshotVecDelegate for Delegate { /// for the `eq_relations`; they carry a `TypeVariableValue` along /// with them. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -struct TyVidEqKey<'tcx> { +pub(crate) struct TyVidEqKey<'tcx> { vid: ty::TyVid, // in the table, we map each ty-vid to one of these: diff --git a/src/librustc_infer/infer/undo_log.rs b/src/librustc_infer/infer/undo_log.rs new file mode 100644 index 0000000000000..56cb182dbf0f9 --- /dev/null +++ b/src/librustc_infer/infer/undo_log.rs @@ -0,0 +1,217 @@ +use std::marker::PhantomData; + +use rustc_data_structures::snapshot_vec as sv; +use rustc_data_structures::undo_log::{Rollback, UndoLogs}; +use rustc_data_structures::unify as ut; +use rustc_middle::ty; + +use crate::{ + infer::{region_constraints, type_variable, InferCtxtInner}, + traits, +}; + +pub struct Snapshot<'tcx> { + pub(crate) undo_len: usize, + _marker: PhantomData<&'tcx ()>, +} + +/// Records the 'undo' data fora single operation that affects some form of inference variable. +pub(crate) enum UndoLog<'tcx> { + TypeVariables(type_variable::UndoLog<'tcx>), + ConstUnificationTable(sv::UndoLog>>), + IntUnificationTable(sv::UndoLog>), + FloatUnificationTable(sv::UndoLog>), + RegionConstraintCollector(region_constraints::UndoLog<'tcx>), + RegionUnificationTable(sv::UndoLog>), + ProjectionCache(traits::UndoLog<'tcx>), + PushRegionObligation, +} + +macro_rules! impl_from { + ($($ctor: ident ($ty: ty),)*) => { + $( + impl<'tcx> From<$ty> for UndoLog<'tcx> { + fn from(x: $ty) -> Self { + UndoLog::$ctor(x.into()) + } + } + )* + } +} + +// Upcast from a single kind of "undoable action" to the general enum +impl_from! { + RegionConstraintCollector(region_constraints::UndoLog<'tcx>), + TypeVariables(type_variable::UndoLog<'tcx>), + + TypeVariables(sv::UndoLog>>), + TypeVariables(sv::UndoLog>), + TypeVariables(sv::UndoLog), + TypeVariables(type_variable::Instantiate), + + IntUnificationTable(sv::UndoLog>), + + FloatUnificationTable(sv::UndoLog>), + + ConstUnificationTable(sv::UndoLog>>), + + RegionUnificationTable(sv::UndoLog>), + ProjectionCache(traits::UndoLog<'tcx>), +} + +/// The Rollback trait defines how to rollback a particular action. +impl<'tcx> Rollback> for InferCtxtInner<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), + UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), + UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), + UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo), + UndoLog::RegionConstraintCollector(undo) => { + self.region_constraint_storage.as_mut().unwrap().reverse(undo) + } + UndoLog::RegionUnificationTable(undo) => { + self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) + } + UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo), + UndoLog::PushRegionObligation => { + self.region_obligations.pop(); + } + } + } +} + +/// The combined undo log for all the various unification tables. For each change to the storage +/// for any kind of inference variable, we record an UndoLog entry in the vector here. +pub(crate) struct InferCtxtUndoLogs<'tcx> { + logs: Vec>, + num_open_snapshots: usize, +} + +impl Default for InferCtxtUndoLogs<'_> { + fn default() -> Self { + Self { logs: Default::default(), num_open_snapshots: Default::default() } + } +} + +/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any +/// action that is convertable into a UndoLog (per the From impls above). +impl<'tcx, T> UndoLogs for InferCtxtUndoLogs<'tcx> +where + UndoLog<'tcx>: From, +{ + fn num_open_snapshots(&self) -> usize { + self.num_open_snapshots + } + + fn push(&mut self, undo: T) { + if self.in_snapshot() { + self.logs.push(undo.into()) + } + } + + fn clear(&mut self) { + self.logs.clear(); + self.num_open_snapshots = 0; + } + + fn extend(&mut self, undos: J) + where + Self: Sized, + J: IntoIterator, + { + if self.in_snapshot() { + self.logs.extend(undos.into_iter().map(UndoLog::from)) + } + } +} + +impl<'tcx> InferCtxtInner<'tcx> { + pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) { + debug!("rollback_to({})", snapshot.undo_len); + self.undo_log.assert_open_snapshot(&snapshot); + + while self.undo_log.logs.len() > snapshot.undo_len { + let undo = self.undo_log.logs.pop().unwrap(); + self.reverse(undo); + } + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } + + pub fn commit(&mut self, snapshot: Snapshot<'tcx>) { + debug!("commit({})", snapshot.undo_len); + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } +} + +impl<'tcx> InferCtxtUndoLogs<'tcx> { + pub fn actions_since_snapshot(&self, snapshot: &Snapshot<'tcx>) -> &[UndoLog<'tcx>] { + &self.logs[snapshot.undo_len..] + } + + pub fn start_snapshot(&mut self) -> Snapshot<'tcx> { + self.num_open_snapshots += 1; + Snapshot { undo_len: self.logs.len(), _marker: PhantomData } + } + + pub(crate) fn region_constraints_in_snapshot( + &self, + s: &Snapshot<'tcx>, + ) -> impl Iterator> + Clone { + self.logs[s.undo_len..].iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + pub(crate) fn region_constraints( + &self, + ) -> impl Iterator> + Clone { + self.logs.iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) { + // Failures here may indicate a failure to follow a stack discipline. + assert!(self.logs.len() >= snapshot.undo_len); + assert!(self.num_open_snapshots > 0); + } + + pub(crate) fn iter(&self) -> std::slice::Iter<'_, UndoLog<'tcx>> { + self.logs.iter() + } +} + +impl<'tcx> std::ops::Index for InferCtxtUndoLogs<'tcx> { + type Output = UndoLog<'tcx>; + + fn index(&self, key: usize) -> &Self::Output { + &self.logs[key] + } +} + +impl<'tcx> std::ops::IndexMut for InferCtxtUndoLogs<'tcx> { + fn index_mut(&mut self, key: usize) -> &mut Self::Output { + &mut self.logs[key] + } +} diff --git a/src/librustc_infer/lib.rs b/src/librustc_infer/lib.rs index cb8ae8c592b22..28d42cea6d300 100644 --- a/src/librustc_infer/lib.rs +++ b/src/librustc_infer/lib.rs @@ -17,6 +17,7 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(never_type)] +#![feature(or_patterns)] #![feature(range_is_empty)] #![feature(in_band_lifetimes)] #![feature(crate_visibility_modifier)] @@ -30,7 +31,7 @@ extern crate rustc_data_structures; #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; pub mod infer; pub mod traits; diff --git a/src/librustc_infer/traits/engine.rs b/src/librustc_infer/traits/engine.rs index 9ad722342a19e..a95257ba6820a 100644 --- a/src/librustc_infer/traits/engine.rs +++ b/src/librustc_infer/traits/engine.rs @@ -1,7 +1,7 @@ use crate::infer::InferCtxt; use crate::traits::Obligation; -use rustc::ty::{self, ToPredicate, Ty, WithConstness}; use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness}; use super::FulfillmentError; use super::{ObligationCause, PredicateObligation}; diff --git a/src/librustc_infer/traits/error_reporting/mod.rs b/src/librustc_infer/traits/error_reporting/mod.rs index 8943ce4e6c505..f873358ff9fdd 100644 --- a/src/librustc_infer/traits/error_reporting/mod.rs +++ b/src/librustc_infer/traits/error_reporting/mod.rs @@ -1,12 +1,12 @@ use super::ObjectSafetyViolation; use crate::infer::InferCtxt; -use rustc::ty::TyCtxt; -use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::Symbol; use rustc_span::Span; use std::fmt; @@ -14,18 +14,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_extra_impl_obligation( &self, error_span: Span, - item_name: ast::Name, + item_name: Symbol, _impl_item_def_id: DefId, trait_item_def_id: DefId, requirement: &dyn fmt::Display, ) -> DiagnosticBuilder<'tcx> { let msg = "impl has stricter requirements than trait"; - let sp = self.tcx.sess.source_map().def_span(error_span); + let sp = self.tcx.sess.source_map().guess_head_span(error_span); let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg); if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) { - let span = self.tcx.sess.source_map().def_span(trait_item_span); + let span = self.tcx.sess.source_map().guess_head_span(trait_item_span); err.span_label(span, format!("definition of `{}` from trait", item_name)); } @@ -39,14 +39,14 @@ pub fn report_object_safety_error( tcx: TyCtxt<'tcx>, span: Span, trait_def_id: DefId, - violations: Vec, + violations: &[ObjectSafetyViolation], ) -> DiagnosticBuilder<'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), _ => None, }); - let span = tcx.sess.source_map().def_span(span); + let span = tcx.sess.source_map().guess_head_span(span); let mut err = struct_span_err!( tcx.sess, span, diff --git a/src/librustc_infer/traits/mod.rs b/src/librustc_infer/traits/mod.rs index 1c0785497be22..8d95904b355da 100644 --- a/src/librustc_infer/traits/mod.rs +++ b/src/librustc_infer/traits/mod.rs @@ -1,16 +1,16 @@ -//! Trait Resolution. See the [rustc guide] for more information on how this works. +//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works. //! -//! [rustc guide]: https://rust-lang.github.io/rustc-guide/traits/resolution.html +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html mod engine; pub mod error_reporting; mod project; mod structural_impls; -mod util; +pub mod util; -use rustc::ty::error::{ExpectedFound, TypeError}; -use rustc::ty::{self, Ty}; use rustc_hir as hir; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::{self, Const, Ty}; use rustc_span::Span; pub use self::FulfillmentErrorCode::*; @@ -20,13 +20,14 @@ pub use self::Vtable::*; pub use self::engine::{TraitEngine, TraitEngineExt}; pub use self::project::MismatchedProjectionTypes; +pub(crate) use self::project::UndoLog; pub use self::project::{ Normalized, NormalizedTy, ProjectionCache, ProjectionCacheEntry, ProjectionCacheKey, - ProjectionCacheSnapshot, Reveal, + ProjectionCacheStorage, Reveal, }; crate use self::util::elaborate_predicates; -pub use rustc::traits::*; +pub use rustc_middle::traits::*; /// An `Obligation` represents some trait reference (e.g., `int: Eq`) for /// which the vtable must be found. The process of finding a vtable is @@ -80,6 +81,7 @@ pub enum FulfillmentErrorCode<'tcx> { CodeSelectionError(SelectionError<'tcx>), CodeProjectionError(MismatchedProjectionTypes<'tcx>), CodeSubtypeError(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate + CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>), CodeAmbiguity, } diff --git a/src/librustc_infer/traits/project.rs b/src/librustc_infer/traits/project.rs index 06870ccc7dd5e..f0d21a7d022da 100644 --- a/src/librustc_infer/traits/project.rs +++ b/src/librustc_infer/traits/project.rs @@ -2,11 +2,18 @@ use super::PredicateObligation; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, Ty}; -use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; +use crate::infer::InferCtxtUndoLogs; -pub use rustc::traits::Reveal; +use rustc_data_structures::{ + snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage}, + undo_log::Rollback, +}; +use rustc_middle::ty::{self, Ty}; + +pub use rustc_middle::traits::Reveal; + +pub(crate) type UndoLog<'tcx> = + snapshot_map::UndoLog, ProjectionCacheEntry<'tcx>>; #[derive(Clone)] pub struct MismatchedProjectionTypes<'tcx> { @@ -58,9 +65,14 @@ impl<'tcx, T> Normalized<'tcx, T> { // // FIXME: we probably also want some sort of cross-infcx cache here to // reduce the amount of duplication. Let's see what we get with the Chalk reforms. +pub struct ProjectionCache<'a, 'tcx> { + map: &'a mut SnapshotMapStorage, ProjectionCacheEntry<'tcx>>, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + #[derive(Default)] -pub struct ProjectionCache<'tcx> { - map: SnapshotMap, ProjectionCacheEntry<'tcx>>, +pub struct ProjectionCacheStorage<'tcx> { + map: SnapshotMapStorage, ProjectionCacheEntry<'tcx>>, } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -82,30 +94,29 @@ pub enum ProjectionCacheEntry<'tcx> { NormalizedTy(NormalizedTy<'tcx>), } -// N.B., intentionally not Clone -pub struct ProjectionCacheSnapshot { - snapshot: Snapshot, -} - -impl<'tcx> ProjectionCache<'tcx> { - pub fn clear(&mut self) { - self.map.clear(); - } - - pub fn snapshot(&mut self) -> ProjectionCacheSnapshot { - ProjectionCacheSnapshot { snapshot: self.map.snapshot() } - } - - pub fn rollback_to(&mut self, snapshot: ProjectionCacheSnapshot) { - self.map.rollback_to(snapshot.snapshot); +impl<'tcx> ProjectionCacheStorage<'tcx> { + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> ProjectionCache<'a, 'tcx> { + ProjectionCache { map: &mut self.map, undo_log } } +} - pub fn rollback_placeholder(&mut self, snapshot: &ProjectionCacheSnapshot) { - self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_placeholders()); +impl<'tcx> ProjectionCache<'_, 'tcx> { + fn map( + &mut self, + ) -> SnapshotMapRef< + '_, + ProjectionCacheKey<'tcx>, + ProjectionCacheEntry<'tcx>, + InferCtxtUndoLogs<'tcx>, + > { + self.map.with_log(self.undo_log) } - pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { - self.map.commit(snapshot.snapshot); + pub fn clear(&mut self) { + self.map().clear(); } /// Try to start normalize `key`; returns an error if @@ -115,11 +126,12 @@ impl<'tcx> ProjectionCache<'tcx> { &mut self, key: ProjectionCacheKey<'tcx>, ) -> Result<(), ProjectionCacheEntry<'tcx>> { - if let Some(entry) = self.map.get(&key) { + let mut map = self.map(); + if let Some(entry) = map.get(&key) { return Err(entry.clone()); } - self.map.insert(key, ProjectionCacheEntry::InProgress); + map.insert(key, ProjectionCacheEntry::InProgress); Ok(()) } @@ -129,7 +141,7 @@ impl<'tcx> ProjectionCache<'tcx> { "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", key, value ); - let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value)); + let fresh_key = self.map().insert(key, ProjectionCacheEntry::NormalizedTy(value)); assert!(!fresh_key, "never started projecting `{:?}`", key); } @@ -138,7 +150,8 @@ impl<'tcx> ProjectionCache<'tcx> { /// snapshot - if the snapshot is rolled back, the obligations will be /// marked as incomplete again). pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) { - let ty = match self.map.get(&key) { + let mut map = self.map(); + let ty = match map.get(&key) { Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => { debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty); ty.value @@ -151,7 +164,7 @@ impl<'tcx> ProjectionCache<'tcx> { } }; - self.map.insert( + map.insert( key, ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }), ); @@ -163,7 +176,7 @@ impl<'tcx> ProjectionCache<'tcx> { // We want to insert `ty` with no obligations. If the existing value // already has no obligations (as is common) we don't insert anything. if !ty.obligations.is_empty() { - self.map.insert( + self.map().insert( key, ProjectionCacheEntry::NormalizedTy(Normalized { value: ty.value, @@ -178,14 +191,20 @@ impl<'tcx> ProjectionCache<'tcx> { /// type information (in which case, the "fully resolved" key will /// be different). pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { - let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous); + let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous); assert!(!fresh, "never started projecting `{:?}`", key); } /// Indicates that trying to normalize `key` resulted in /// error. pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) { - let fresh = self.map.insert(key, ProjectionCacheEntry::Error); + let fresh = self.map().insert(key, ProjectionCacheEntry::Error); assert!(!fresh, "never started projecting `{:?}`", key); } } + +impl<'tcx> Rollback> for ProjectionCacheStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + self.map.reverse(undo); + } +} diff --git a/src/librustc_infer/traits/structural_impls.rs b/src/librustc_infer/traits/structural_impls.rs index 6630f664f96e4..c48e58c04824e 100644 --- a/src/librustc_infer/traits/structural_impls.rs +++ b/src/librustc_infer/traits/structural_impls.rs @@ -1,7 +1,7 @@ use crate::traits; use crate::traits::project::Normalized; -use rustc::ty; -use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use rustc_middle::ty; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use std::fmt; @@ -41,6 +41,9 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { super::CodeSubtypeError(ref a, ref b) => { write!(f, "CodeSubtypeError({:?}, {:?})", a, b) } + super::CodeConstEquateError(ref a, ref b) => { + write!(f, "CodeConstEquateError({:?}, {:?})", a, b) + } super::CodeAmbiguity => write!(f, "Ambiguity"), } } diff --git a/src/librustc_infer/traits/util.rs b/src/librustc_infer/traits/util.rs index 90f3cb1d24ccc..ee903b676bae9 100644 --- a/src/librustc_infer/traits/util.rs +++ b/src/librustc_infer/traits/util.rs @@ -1,10 +1,15 @@ use smallvec::smallvec; -use rustc::ty::outlives::Component; -use rustc::ty::{self, ToPolyTraitRef, TyCtxt}; +use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use rustc_data_structures::fx::FxHashSet; +use rustc_middle::ty::outlives::Component; +use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, TyCtxt, WithConstness}; +use rustc_span::Span; -fn anonymize_predicate<'tcx>(tcx: TyCtxt<'tcx>, pred: &ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { +pub fn anonymize_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + pred: &ty::Predicate<'tcx>, +) -> ty::Predicate<'tcx> { match *pred { ty::Predicate::Trait(ref data, constness) => { ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data), constness) @@ -37,6 +42,8 @@ fn anonymize_predicate<'tcx>(tcx: TyCtxt<'tcx>, pred: &ty::Predicate<'tcx>) -> t ty::Predicate::ConstEvaluatable(def_id, substs) => { ty::Predicate::ConstEvaluatable(def_id, substs) } + + ty::Predicate::ConstEquate(c1, c2) => ty::Predicate::ConstEquate(c1, c2), } } @@ -84,41 +91,81 @@ impl>> Extend for PredicateSet<'tcx> { /// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that /// `T: Foo`, then we know that `T: 'static`. pub struct Elaborator<'tcx> { - stack: Vec>, + stack: Vec>, visited: PredicateSet<'tcx>, } +pub fn elaborate_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Elaborator<'tcx> { + elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate())) +} + +pub fn elaborate_trait_refs<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl Iterator>, +) -> Elaborator<'tcx> { + let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate()); + elaborate_predicates(tcx, predicates) +} + pub fn elaborate_predicates<'tcx>( tcx: TyCtxt<'tcx>, - mut predicates: Vec>, + predicates: impl Iterator>, +) -> Elaborator<'tcx> { + let obligations = predicates.map(|predicate| predicate_obligation(predicate, None)).collect(); + elaborate_obligations(tcx, obligations) +} + +pub fn elaborate_obligations<'tcx>( + tcx: TyCtxt<'tcx>, + mut obligations: Vec>, ) -> Elaborator<'tcx> { let mut visited = PredicateSet::new(tcx); - predicates.retain(|pred| visited.insert(pred)); - Elaborator { stack: predicates, visited } + obligations.retain(|obligation| visited.insert(&obligation.predicate)); + Elaborator { stack: obligations, visited } +} + +fn predicate_obligation<'tcx>( + predicate: ty::Predicate<'tcx>, + span: Option, +) -> PredicateObligation<'tcx> { + let mut cause = ObligationCause::dummy(); + if let Some(span) = span { + cause.span = span; + } + Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } } impl Elaborator<'tcx> { - fn elaborate(&mut self, predicate: &ty::Predicate<'tcx>) { + pub fn filter_to_traits(self) -> FilterToTraits { + FilterToTraits::new(self) + } + + fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { let tcx = self.visited.tcx; - match *predicate { + match obligation.predicate { ty::Predicate::Trait(ref data, _) => { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); - let predicates = predicates - .predicates - .iter() - .map(|(pred, _)| pred.subst_supertrait(tcx, &data.to_poly_trait_ref())); - debug!("super_predicates: data={:?} predicates={:?}", data, predicates.clone()); + let obligations = predicates.predicates.iter().map(|(pred, span)| { + predicate_obligation( + pred.subst_supertrait(tcx, &data.to_poly_trait_ref()), + Some(*span), + ) + }); + debug!("super_predicates: data={:?}", data); // Only keep those bounds that we haven't already seen. // This is necessary to prevent infinite recursion in some // cases. One common case is when people define // `trait Sized: Sized { }` rather than `trait Sized { }`. let visited = &mut self.visited; - let predicates = predicates.filter(|pred| visited.insert(pred)); + let obligations = obligations.filter(|o| visited.insert(&o.predicate)); - self.stack.extend(predicates); + self.stack.extend(obligations); } ty::Predicate::WellFormed(..) => { // Currently, we do not elaborate WF predicates, @@ -142,6 +189,10 @@ impl Elaborator<'tcx> { // Currently, we do not elaborate const-evaluatable // predicates. } + ty::Predicate::ConstEquate(..) => { + // Currently, we do not elaborate const-equate + // predicates. + } ty::Predicate::RegionOutlives(..) => { // Nothing to elaborate from `'a: 'b`. } @@ -199,7 +250,8 @@ impl Elaborator<'tcx> { None } }) - .filter(|p| visited.insert(p)), + .filter(|p| visited.insert(p)) + .map(|p| predicate_obligation(p, None)), ); } } @@ -207,19 +259,73 @@ impl Elaborator<'tcx> { } impl Iterator for Elaborator<'tcx> { - type Item = ty::Predicate<'tcx>; + type Item = PredicateObligation<'tcx>; fn size_hint(&self) -> (usize, Option) { (self.stack.len(), None) } - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { // Extract next item from top-most stack frame, if any. - if let Some(pred) = self.stack.pop() { - self.elaborate(&pred); - Some(pred) + if let Some(obligation) = self.stack.pop() { + self.elaborate(&obligation); + Some(obligation) } else { None } } } + +/////////////////////////////////////////////////////////////////////////// +// Supertrait iterator +/////////////////////////////////////////////////////////////////////////// + +pub type Supertraits<'tcx> = FilterToTraits>; + +pub fn supertraits<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Supertraits<'tcx> { + elaborate_trait_ref(tcx, trait_ref).filter_to_traits() +} + +pub fn transitive_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: impl Iterator>, +) -> Supertraits<'tcx> { + elaborate_trait_refs(tcx, bounds).filter_to_traits() +} + +/////////////////////////////////////////////////////////////////////////// +// Other +/////////////////////////////////////////////////////////////////////////// + +/// A filter around an iterator of predicates that makes it yield up +/// just trait references. +pub struct FilterToTraits { + base_iterator: I, +} + +impl FilterToTraits { + fn new(base: I) -> FilterToTraits { + FilterToTraits { base_iterator: base } + } +} + +impl<'tcx, I: Iterator>> Iterator for FilterToTraits { + type Item = ty::PolyTraitRef<'tcx>; + + fn next(&mut self) -> Option> { + while let Some(obligation) = self.base_iterator.next() { + if let ty::Predicate::Trait(data, _) = obligation.predicate { + return Some(data.to_poly_trait_ref()); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.base_iterator.size_hint(); + (0, upper) + } +} diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index 2e055ff183f2c..2963eb29bc170 100644 --- a/src/librustc_interface/Cargo.toml +++ b/src/librustc_interface/Cargo.toml @@ -10,6 +10,7 @@ path = "lib.rs" doctest = false [dependencies] +libc = "0.2" log = "0.4" rayon = { version = "0.3.0", package = "rustc-rayon" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } @@ -21,7 +22,7 @@ rustc_parse = { path = "../librustc_parse" } rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } rustc_serialize = { path = "../libserialize", package = "serialize" } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_ast_lowering = { path = "../librustc_ast_lowering" } rustc_ast_passes = { path = "../librustc_ast_passes" } rustc_incremental = { path = "../librustc_incremental" } @@ -31,7 +32,6 @@ rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } rustc_symbol_mangling = { path = "../librustc_symbol_mangling" } rustc_codegen_llvm = { path = "../librustc_codegen_llvm", optional = true } rustc_hir = { path = "../librustc_hir" } -rustc_infer = { path = "../librustc_infer" } rustc_metadata = { path = "../librustc_metadata" } rustc_mir = { path = "../librustc_mir" } rustc_mir_build = { path = "../librustc_mir_build" } diff --git a/src/librustc_interface/callbacks.rs b/src/librustc_interface/callbacks.rs index 0345b82d3bbc6..913c67d045e59 100644 --- a/src/librustc_interface/callbacks.rs +++ b/src/librustc_interface/callbacks.rs @@ -1,6 +1,6 @@ //! Throughout the compiler tree, there are several places which want to have //! access to state or queries while being inside crates that are dependencies -//! of librustc. To facilitate this, we have the +//! of librustc_middle. To facilitate this, we have the //! `rustc_data_structures::AtomicRef` type, which allows us to setup a global //! static which can then be set in this file at program startup. //! @@ -9,12 +9,12 @@ //! The functions in this file should fall back to the default set in their //! origin crate when the `TyCtxt` is not present in TLS. -use rustc::ty::tls; use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS}; +use rustc_middle::ty::tls; use std::fmt; /// This is a callback from librustc_ast as it cannot access the implicit state -/// in librustc otherwise. +/// in librustc_middle otherwise. fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { tls::with_opt(|tcx| { if let Some(tcx) = tcx { @@ -26,7 +26,7 @@ fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result } /// This is a callback from librustc_ast as it cannot access the implicit state -/// in librustc otherwise. It is used to when diagnostic messages are +/// in librustc_middle otherwise. It is used to when diagnostic messages are /// emitted and stores them in the current query, if there is one. fn track_diagnostic(diagnostic: &Diagnostic) { tls::with_context_opt(|icx| { @@ -40,7 +40,7 @@ fn track_diagnostic(diagnostic: &Diagnostic) { } /// This is a callback from librustc_hir as it cannot access the implicit state -/// in librustc otherwise. +/// in librustc_middle otherwise. fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "DefId({}:{}", def_id.krate, def_id.index.index())?; tls::with_opt(|opt_tcx| { @@ -58,5 +58,4 @@ pub fn setup_callbacks() { rustc_span::SPAN_DEBUG.swap(&(span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); TRACK_DIAGNOSTICS.swap(&(track_diagnostic as fn(&_))); - rustc::ty::RESOLVE_INSTANCE.swap(&(rustc_ty::instance::resolve_instance as _)); } diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs index 65a7a48d440bd..55f825e150e5e 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs @@ -1,8 +1,6 @@ pub use crate::passes::BoxedResolver; use crate::util; -use rustc::ty; -use rustc::util::common::ErrorReported; use rustc_ast::ast::{self, MetaItemKind}; use rustc_ast::token; use rustc_codegen_ssa::traits::CodegenBackend; @@ -10,7 +8,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::OnDrop; use rustc_errors::registry::Registry; +use rustc_errors::ErrorReported; use rustc_lint::LintStore; +use rustc_middle::ty; use rustc_parse::new_parser_from_source_str; use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames}; use rustc_session::early_error; @@ -193,7 +193,7 @@ pub fn run_compiler_in_existing_thread_pool( let r = { let _sess_abort_error = OnDrop(|| { - compiler.sess.diagnostic().print_error_count(registry); + compiler.sess.finish_diagnostics(registry); }); f(&compiler) diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs index ba1e2216ca805..0650d09003486 100644 --- a/src/librustc_interface/lib.rs +++ b/src/librustc_interface/lib.rs @@ -6,9 +6,6 @@ #![feature(generators)] #![recursion_limit = "256"] -#[cfg(unix)] -extern crate libc; - mod callbacks; pub mod interface; mod passes; diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 60bb4a661fd01..801c8e9329b24 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -3,34 +3,31 @@ use crate::proc_macro_decls; use crate::util; use log::{info, log_enabled, warn}; -use rustc::arena::Arena; -use rustc::dep_graph::DepGraph; -use rustc::hir::map::Definitions; -use rustc::middle; -use rustc::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; -use rustc::ty::steal::Steal; -use rustc::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; -use rustc::util::common::ErrorReported; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::{self, ast, visit}; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::sync::{par_iter, Lrc, Once, ParallelIterator, WorkerLocal}; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; -use rustc_errors::PResult; +use rustc_errors::{ErrorReported, PResult}; use rustc_expand::base::ExtCtxt; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::definitions::Definitions; use rustc_hir::Crate; use rustc_lint::LintStore; +use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::DepGraph; +use rustc_middle::middle; +use rustc_middle::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; +use rustc_middle::ty::steal::Steal; +use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; use rustc_mir as mir; use rustc_mir_build as mir_build; use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str}; use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::{Resolver, ResolverArenas}; -use rustc_session::config::{ - self, CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode, -}; +use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode}; use rustc_session::lint; use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; @@ -110,7 +107,8 @@ pub fn configure_and_expand( // its contents but the results of name resolution on those contents. Hopefully we'll push // this back at some point. let crate_name = crate_name.to_string(); - let (result, resolver) = BoxedResolver::new(static move || { + let (result, resolver) = BoxedResolver::new(static move |mut action| { + let _ = action; let sess = &*sess; let resolver_arenas = Resolver::arenas(); let res = configure_and_expand_inner( @@ -127,11 +125,11 @@ pub fn configure_and_expand( panic!() } Ok((krate, resolver)) => { - yield BoxedResolver::initial_yield(Ok(krate)); + action = yield BoxedResolver::initial_yield(Ok(krate)); resolver } }; - box_region_allow_access!(for(), (&mut Resolver<'_>), (&mut resolver)); + box_region_allow_access!(for(), (&mut Resolver<'_>), (&mut resolver), action); resolver.into_outputs() }); result.map(|k| (k, resolver)) @@ -361,7 +359,7 @@ fn configure_and_expand_inner<'a>( }); let crate_types = sess.crate_types.borrow(); - let is_proc_macro_crate = crate_types.contains(&config::CrateType::ProcMacro); + let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); // For backwards compatibility, we don't try to run proc macro injection // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being @@ -678,7 +676,7 @@ pub fn default_provide(providers: &mut ty::query::Providers<'_>) { providers.analysis = analysis; proc_macro_decls::provide(providers); plugin::build::provide(providers); - rustc::hir::provide(providers); + rustc_middle::hir::provide(providers); mir::provide(providers); mir_build::provide(providers); rustc_privacy::provide(providers); @@ -711,7 +709,7 @@ impl<'tcx> QueryContext<'tcx> { } pub fn print_stats(&mut self) { - self.enter(|tcx| ty::query::print_stats(tcx)) + self.enter(ty::query::print_stats) } } @@ -776,7 +774,7 @@ pub fn create_global_ctxt<'tcx>( fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { assert_eq!(cnum, LOCAL_CRATE); - rustc::hir::map::check_crate(tcx); + rustc_passes::hir_id_validator::check_crate(tcx); let sess = tcx.sess; let mut entry_point = None; @@ -813,7 +811,7 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { { sess.time("match_checking", || { tcx.par_body_owners(|def_id| { - tcx.ensure().check_match(def_id); + tcx.ensure().check_match(def_id.to_def_id()); }); }); }, @@ -838,13 +836,9 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { tcx.par_body_owners(|def_id| tcx.ensure().mir_borrowck(def_id)); }); - sess.time("dumping_chalk_like_clauses", || { - rustc_traits::lowering::dump_program_clauses(tcx); - }); - sess.time("MIR_effect_checking", || { for def_id in tcx.body_owners() { - mir::transform::check_unsafety::check_unsafety(tcx, def_id) + mir::transform::check_unsafety::check_unsafety(tcx, def_id.to_def_id()) } }); diff --git a/src/librustc_interface/proc_macro_decls.rs b/src/librustc_interface/proc_macro_decls.rs index 076a8cef92cbc..c74cba81ca907 100644 --- a/src/librustc_interface/proc_macro_decls.rs +++ b/src/librustc_interface/proc_macro_decls.rs @@ -1,9 +1,9 @@ -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::attr; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; pub fn find(tcx: TyCtxt<'_>) -> Option { @@ -16,7 +16,7 @@ fn proc_macro_decls_static(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option { let mut finder = Finder { decls: None }; tcx.hir().krate().visit_all_item_likes(&mut finder); - finder.decls.map(|id| tcx.hir().local_def_id(id)) + finder.decls.map(|id| tcx.hir().local_def_id(id).to_def_id()) } struct Finder { diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs index b0eeb57173fa3..94cd4bcd4c626 100644 --- a/src/librustc_interface/queries.rs +++ b/src/librustc_interface/queries.rs @@ -1,18 +1,18 @@ use crate::interface::{Compiler, Result}; use crate::passes::{self, BoxedResolver, QueryContext}; -use rustc::arena::Arena; -use rustc::dep_graph::DepGraph; -use rustc::ty::steal::Steal; -use rustc::ty::{GlobalCtxt, ResolverOutputs, TyCtxt}; -use rustc::util::common::ErrorReported; use rustc_ast::{self, ast}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::sync::{Lrc, Once, WorkerLocal}; +use rustc_errors::ErrorReported; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::Crate; use rustc_incremental::DepGraphFuture; use rustc_lint::LintStore; +use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::DepGraph; +use rustc_middle::ty::steal::Steal; +use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt}; use rustc_session::config::{OutputFilenames, OutputType}; use rustc_session::{output::find_crate_name, Session}; use rustc_span::symbol::sym; @@ -137,7 +137,7 @@ impl<'tcx> Queries<'tcx> { let result = passes::register_plugins( self.session(), &*self.codegen_backend().metadata_loader(), - self.compiler.register_lints.as_ref().map(|p| &**p).unwrap_or_else(|| empty), + self.compiler.register_lints.as_deref().unwrap_or_else(|| empty), krate, &crate_name, ); @@ -293,7 +293,7 @@ impl<'tcx> Queries<'tcx> { _ => return, }; - let attrs = &*tcx.get_attrs(def_id); + let attrs = &*tcx.get_attrs(def_id.to_def_id()); let attrs = attrs.iter().filter(|attr| attr.check_name(sym::rustc_error)); for attr in attrs { match attr.meta_item_list() { diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index db5ada9291435..5e17660f4c60e 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -1,20 +1,22 @@ -extern crate getopts; - use crate::interface::parse_cfgspecs; -use rustc::middle::cstore; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; +use rustc_middle::middle::cstore; +use rustc_session::config::Strip; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; -use rustc_session::config::{ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; -use rustc_session::config::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; +use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; +use rustc_session::config::{Externs, OutputType, OutputTypes, Sanitizer, SymbolManglingVersion}; +use rustc_session::getopts; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_session::{build_session, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; -use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel}; +use rustc_span::SourceFileHashAlgorithm; +use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; +use rustc_target::spec::{RelocModel, RelroLevel, TlsModel}; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; use std::path::PathBuf; @@ -375,141 +377,66 @@ fn test_codegen_options_tracking_hash() { let reference = Options::default(); let mut opts = Options::default(); - // Make sure the changing an [UNTRACKED] option leaves the hash unchanged - opts.cg.ar = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.linker = Some(PathBuf::from("linker")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.link_args = Some(vec![String::from("abc"), String::from("def")]); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.link_dead_code = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.rpath = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.extra_filename = String::from("extra-filename"); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.codegen_units = Some(42); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.remark = Passes::Some(vec![String::from("pass1"), String::from("pass2")]); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.save_temps = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.incremental = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - // Make sure changing a [TRACKED] option changes the hash - opts = reference.clone(); - opts.cg.lto = LtoCli::Fat; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.target_cpu = Some(String::from("abc")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.target_feature = String::from("all the features, all of them"); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.passes = vec![String::from("1"), String::from("2")]; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.llvm_args = vec![String::from("1"), String::from("2")]; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.overflow_checks = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_prepopulate_passes = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_vectorize_loops = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_vectorize_slp = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.soft_float = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.prefer_dynamic = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_integrated_as = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_redzone = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.relocation_model = Some(String::from("relocation model")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.code_model = Some(String::from("code model")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.tls_model = Some(String::from("tls model")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.profile_generate = SwitchWithOptPath::Enabled(None); - assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.profile_use = Some(PathBuf::from("abc")); - assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.metadata = vec![String::from("A"), String::from("B")]; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.debuginfo = Some(0xdeadbeef); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.debuginfo = Some(0xba5eba11); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.force_frame_pointers = Some(false); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.debug_assertions = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + macro_rules! untracked { + ($name: ident, $non_default_value: expr) => { + opts.cg.$name = $non_default_value; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } - opts = reference.clone(); - opts.cg.inline_threshold = Some(0xf007ba11); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. + // This list is in alphabetical order. + untracked!(ar, String::from("abc")); + untracked!(codegen_units, Some(42)); + untracked!(default_linker_libraries, true); + untracked!(extra_filename, String::from("extra-filename")); + untracked!(incremental, Some(String::from("abc"))); + // `link_arg` is omitted because it just forwards to `link_args`. + untracked!(link_args, vec![String::from("abc"), String::from("def")]); + untracked!(link_dead_code, true); + untracked!(linker, Some(PathBuf::from("linker"))); + untracked!(linker_flavor, Some(LinkerFlavor::Gcc)); + untracked!(no_stack_check, true); + untracked!(remark, Passes::Some(vec![String::from("pass1"), String::from("pass2")])); + untracked!(rpath, true); + untracked!(save_temps, true); + + macro_rules! tracked { + ($name: ident, $non_default_value: expr) => { + opts = reference.clone(); + opts.cg.$name = $non_default_value; + assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } - opts = reference.clone(); - opts.cg.panic = Some(PanicStrategy::Abort); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.linker_plugin_lto = LinkerPluginLto::LinkerPluginAuto; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + // Make sure that changing a [TRACKED] option changes the hash. + // This list is in alphabetical order. + tracked!(code_model, Some(CodeModel::Large)); + tracked!(debug_assertions, Some(true)); + tracked!(debuginfo, 0xdeadbeef); + tracked!(embed_bitcode, false); + tracked!(force_frame_pointers, Some(false)); + tracked!(force_unwind_tables, Some(true)); + tracked!(inline_threshold, Some(0xf007ba11)); + tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); + tracked!(llvm_args, vec![String::from("1"), String::from("2")]); + tracked!(lto, LtoCli::Fat); + tracked!(metadata, vec![String::from("A"), String::from("B")]); + tracked!(no_prepopulate_passes, true); + tracked!(no_redzone, Some(true)); + tracked!(no_vectorize_loops, true); + tracked!(no_vectorize_slp, true); + tracked!(opt_level, "3".to_string()); + tracked!(overflow_checks, Some(true)); + tracked!(panic, Some(PanicStrategy::Abort)); + tracked!(passes, vec![String::from("1"), String::from("2")]); + tracked!(prefer_dynamic, true); + tracked!(profile_generate, SwitchWithOptPath::Enabled(None)); + tracked!(profile_use, Some(PathBuf::from("abc"))); + tracked!(relocation_model, Some(RelocModel::Pic)); + tracked!(soft_float, true); + tracked!(target_cpu, Some(String::from("abc"))); + tracked!(target_feature, String::from("all the features, all of them")); } #[test] @@ -517,116 +444,136 @@ fn test_debugging_options_tracking_hash() { let reference = Options::default(); let mut opts = Options::default(); - // Make sure the changing an [UNTRACKED] option leaves the hash unchanged - opts.debugging_opts.verbose = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.time_passes = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.time_llvm_passes = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.input_stats = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.borrowck_stats = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.meta_stats = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_link_args = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_llvm_passes = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.ast_json = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.ast_json_noexpand = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.ls = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.save_analysis = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_region_graph = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.parse_only = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.incremental = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_dep_graph = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.query_dep_graph = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.no_analysis = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.unstable_options = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.trace_macros = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.keep_hygiene_data = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.keep_ast = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_mono_items = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_mir = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_mir_dir = String::from("abc"); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_mir_graphviz = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - // Make sure changing a [TRACKED] option changes the hash - opts = reference.clone(); - opts.debugging_opts.asm_comments = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.verify_llvm_ir = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.no_landing_pads = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.fewer_names = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.no_codegen = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.treat_err_as_bug = Some(1); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.report_delayed_bugs = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.force_overflow_checks = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.show_span = Some(String::from("abc")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.mir_opt_level = 3; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.relro_level = Some(RelroLevel::Full); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.merge_functions = Some(MergeFunctions::Disabled); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.allow_features = Some(vec![String::from("lang_items")]); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + macro_rules! untracked { + ($name: ident, $non_default_value: expr) => { + opts.debugging_opts.$name = $non_default_value; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } + + // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. + // This list is in alphabetical order. + untracked!(ast_json, true); + untracked!(ast_json_noexpand, true); + untracked!(borrowck, String::from("other")); + untracked!(borrowck_stats, true); + untracked!(control_flow_guard, CFGuard::Checks); + untracked!(deduplicate_diagnostics, true); + untracked!(dep_tasks, true); + untracked!(dont_buffer_diagnostics, true); + untracked!(dump_dep_graph, true); + untracked!(dump_mir, Some(String::from("abc"))); + untracked!(dump_mir_dataflow, true); + untracked!(dump_mir_dir, String::from("abc")); + untracked!(dump_mir_exclude_pass_number, true); + untracked!(dump_mir_graphviz, true); + untracked!(emit_stack_sizes, true); + untracked!(hir_stats, true); + untracked!(identify_regions, true); + untracked!(incremental_ignore_spans, true); + untracked!(incremental_info, true); + untracked!(incremental_verify_ich, true); + untracked!(input_stats, true); + untracked!(keep_hygiene_data, true); + untracked!(link_native_libraries, false); + untracked!(llvm_time_trace, true); + untracked!(ls, true); + untracked!(macro_backtrace, true); + untracked!(meta_stats, true); + untracked!(nll_facts, true); + untracked!(no_analysis, true); + untracked!(no_interleave_lints, true); + untracked!(no_leak_check, true); + untracked!(no_parallel_llvm, true); + untracked!(parse_only, true); + untracked!(perf_stats, true); + untracked!(polonius, true); + // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. + untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); + untracked!(print_link_args, true); + untracked!(print_llvm_passes, true); + untracked!(print_mono_items, Some(String::from("abc"))); + untracked!(print_region_graph, true); + untracked!(print_type_sizes, true); + untracked!(query_dep_graph, true); + untracked!(query_stats, true); + untracked!(save_analysis, true); + untracked!(self_profile, SwitchWithOptPath::Enabled(None)); + untracked!(self_profile_events, Some(vec![String::new()])); + untracked!(span_free_formats, true); + untracked!(strip, Strip::None); + untracked!(terminal_width, Some(80)); + untracked!(threads, 99); + untracked!(time, true); + untracked!(time_llvm_passes, true); + untracked!(time_passes, true); + untracked!(trace_macros, true); + untracked!(ui_testing, true); + untracked!(unpretty, Some("expanded".to_string())); + untracked!(unstable_options, true); + untracked!(verbose, true); + + macro_rules! tracked { + ($name: ident, $non_default_value: expr) => { + opts = reference.clone(); + opts.debugging_opts.$name = $non_default_value; + assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } + + // Make sure that changing a [TRACKED] option changes the hash. + // This list is in alphabetical order. + tracked!(allow_features, Some(vec![String::from("lang_items")])); + tracked!(always_encode_mir, true); + tracked!(asm_comments, true); + tracked!(binary_dep_depinfo, true); + tracked!(chalk, true); + tracked!(codegen_backend, Some("abc".to_string())); + tracked!(crate_attr, vec!["abc".to_string()]); + tracked!(debug_macros, true); + tracked!(dep_info_omit_d_target, true); + tracked!(dual_proc_macros, true); + tracked!(fewer_names, true); + tracked!(force_overflow_checks, Some(true)); + tracked!(force_unstable_if_unmarked, true); + tracked!(fuel, Some(("abc".to_string(), 99))); + tracked!(human_readable_cgu_names, true); + tracked!(inline_in_all_cgus, Some(true)); + tracked!(insert_sideeffect, true); + tracked!(instrument_mcount, true); + tracked!(link_only, true); + tracked!(merge_functions, Some(MergeFunctions::Disabled)); + tracked!(mir_emit_retag, true); + tracked!(mir_opt_level, 3); + tracked!(mutable_noalias, true); + tracked!(new_llvm_pass_manager, true); + tracked!(no_codegen, true); + tracked!(no_generate_arange_section, true); + tracked!(no_link, true); + tracked!(no_profiler_runtime, true); + tracked!(osx_rpath_install_name, true); + tracked!(panic_abort_tests, true); + tracked!(plt, Some(true)); + tracked!(print_fuel, Some("abc".to_string())); + tracked!(profile, true); + tracked!(relro_level, Some(RelroLevel::Full)); + tracked!(report_delayed_bugs, true); + tracked!(run_dsymutil, false); + tracked!(sanitizer, Some(Sanitizer::Address)); + tracked!(sanitizer_memory_track_origins, 2); + tracked!(sanitizer_recover, vec![Sanitizer::Address]); + tracked!(saturating_float_casts, Some(true)); + tracked!(share_generics, Some(true)); + tracked!(show_span, Some(String::from("abc"))); + tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); + tracked!(symbol_mangling_version, SymbolManglingVersion::V0); + tracked!(teach, true); + tracked!(thinlto, Some(true)); + tracked!(tls_model, Some(TlsModel::GeneralDynamic)); + tracked!(treat_err_as_bug, Some(1)); + tracked!(unleash_the_miri_inside_of_you, true); + tracked!(use_ctors_section, Some(true)); + tracked!(verify_llvm_ir, true); } #[test] diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index c6f2d1b82fcf4..5a76802014e0d 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -1,5 +1,4 @@ use log::info; -use rustc::ty; use rustc_ast::ast::{AttrVec, BlockCheckMode}; use rustc_ast::mut_visit::{visit_clobber, MutVisitor, *}; use rustc_ast::ptr::P; @@ -14,15 +13,17 @@ use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::registry::Registry; use rustc_metadata::dynamic_lib::DynamicLibrary; +use rustc_middle::ty; use rustc_resolve::{self, Resolver}; use rustc_session as session; +use rustc_session::config::{self, CrateType}; use rustc_session::config::{ErrorOutputType, Input, OutputFilenames}; use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; use rustc_session::parse::CrateConfig; use rustc_session::CrateDisambiguator; -use rustc_session::{config, early_error, filesearch, output, DiagnosticOutput, Session}; +use rustc_session::{early_error, filesearch, output, DiagnosticOutput, Session}; use rustc_span::edition::Edition; -use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMap}; +use rustc_span::source_map::{FileLoader, SourceMap}; use rustc_span::symbol::{sym, Symbol}; use smallvec::SmallVec; use std::env; @@ -41,14 +42,17 @@ use std::{panic, thread}; /// features is available on the target machine, by querying LLVM. pub fn add_configuration( cfg: &mut CrateConfig, - sess: &Session, + sess: &mut Session, codegen_backend: &dyn CodegenBackend, ) { let tf = sym::target_feature; - cfg.extend(codegen_backend.target_features(sess).into_iter().map(|feat| (tf, Some(feat)))); + let target_features = codegen_backend.target_features(sess); + sess.target_features.extend(target_features.iter().cloned()); + + cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat)))); - if sess.crt_static_feature(None) { + if sess.crt_static(None) { cfg.insert((tf, Some(Symbol::intern("crt-static")))); } } @@ -62,34 +66,25 @@ pub fn create_session( lint_caps: FxHashMap, descriptions: Registry, ) -> (Lrc, Lrc>, Lrc) { - let loader = file_loader.unwrap_or(box RealFileLoader); - let source_map = Lrc::new(SourceMap::with_file_loader(loader, sopts.file_path_mapping())); - let mut sess = session::build_session_with_source_map( + let (mut sess, source_map) = session::build_session_with_source_map( sopts, input_path, descriptions, - source_map.clone(), diagnostic_output, lint_caps, + file_loader, ); let codegen_backend = get_codegen_backend(&sess); let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg)); - add_configuration(&mut cfg, &sess, &*codegen_backend); + add_configuration(&mut cfg, &mut sess, &*codegen_backend); sess.parse_sess.config = cfg; (Lrc::new(sess), Lrc::new(codegen_backend), source_map) } -// Temporarily have stack size set to 32MB to deal with various crates with long method -// chains or deep syntax trees, except when on Haiku. -// FIXME(oli-obk): get https://github.com/rust-lang/rust/pull/55617 the finish line -#[cfg(not(target_os = "haiku"))] -const STACK_SIZE: usize = 32 * 1024 * 1024; - -#[cfg(target_os = "haiku")] -const STACK_SIZE: usize = 16 * 1024 * 1024; +const STACK_SIZE: usize = 8 * 1024 * 1024; fn get_stack_size() -> Option { // FIXME: Hacks on hacks. If the env is trying to override the stack size @@ -207,7 +202,7 @@ pub fn spawn_thread_pool R + Send, R: Send>( } fn load_backend_from_dylib(path: &Path) -> fn() -> Box { - let lib = DynamicLibrary::open(Some(path)).unwrap_or_else(|err| { + let lib = DynamicLibrary::open(path).unwrap_or_else(|err| { let err = format!("couldn't load codegen backend {:?}: {:?}", path, err); early_error(ErrorOutputType::default(), &err); }); @@ -269,17 +264,14 @@ pub fn rustc_path<'a>() -> Option<&'a Path> { } fn get_rustc_path_inner(bin_path: &str) -> Option { - sysroot_candidates() - .iter() - .filter_map(|sysroot| { - let candidate = sysroot.join(bin_path).join(if cfg!(target_os = "windows") { - "rustc.exe" - } else { - "rustc" - }); - candidate.exists().then_some(candidate) - }) - .next() + sysroot_candidates().iter().find_map(|sysroot| { + let candidate = sysroot.join(bin_path).join(if cfg!(target_os = "windows") { + "rustc.exe" + } else { + "rustc" + }); + candidate.exists().then_some(candidate) + }) } fn sysroot_candidates() -> Vec { @@ -414,7 +406,7 @@ pub(crate) fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguat // Also incorporate crate type, so that we don't get symbol conflicts when // linking against a library of the same name, if this is an executable. - let is_exe = session.crate_types.borrow().contains(&config::CrateType::Executable); + let is_exe = session.crate_types.borrow().contains(&CrateType::Executable); hasher.write(if is_exe { b"exe" } else { b"lib" }); CrateDisambiguator::from(hasher.finish::()) @@ -462,23 +454,23 @@ pub(crate) fn check_attr_crate_type(attrs: &[ast::Attribute], lint_buffer: &mut } } -const CRATE_TYPES: &[(Symbol, config::CrateType)] = &[ - (sym::rlib, config::CrateType::Rlib), - (sym::dylib, config::CrateType::Dylib), - (sym::cdylib, config::CrateType::Cdylib), +const CRATE_TYPES: &[(Symbol, CrateType)] = &[ + (sym::rlib, CrateType::Rlib), + (sym::dylib, CrateType::Dylib), + (sym::cdylib, CrateType::Cdylib), (sym::lib, config::default_lib_output()), - (sym::staticlib, config::CrateType::Staticlib), - (sym::proc_dash_macro, config::CrateType::ProcMacro), - (sym::bin, config::CrateType::Executable), + (sym::staticlib, CrateType::Staticlib), + (sym::proc_dash_macro, CrateType::ProcMacro), + (sym::bin, CrateType::Executable), ]; -fn categorize_crate_type(s: Symbol) -> Option { +fn categorize_crate_type(s: Symbol) -> Option { Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) } -pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { +pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { // Unconditionally collect crate types from attributes to make them used - let attr_types: Vec = attrs + let attr_types: Vec = attrs .iter() .filter_map(|a| { if a.check_name(sym::crate_type) { @@ -495,7 +487,7 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec ReplaceBodyWithLoop<'a, 'b> { | ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) | ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), - ast::TyKind::Path(_, ref path) => path.segments.iter().any(|seg| { - match seg.args.as_ref().map(|generic_arg| &**generic_arg) { + ast::TyKind::Path(_, ref path) => { + path.segments.iter().any(|seg| match seg.args.as_deref() { None => false, Some(&ast::GenericArgs::AngleBracketed(ref data)) => { - let types = data.args.iter().filter_map(|arg| match arg { - ast::GenericArg::Type(ty) => Some(ty), - _ => None, - }); - any_involves_impl_trait(types) - || data.constraints.iter().any(|c| match c.kind { + data.args.iter().any(|arg| match arg { + ast::AngleBracketedArg::Arg(arg) => match arg { + ast::GenericArg::Type(ty) => involves_impl_trait(ty), + ast::GenericArg::Lifetime(_) + | ast::GenericArg::Const(_) => false, + }, + ast::AngleBracketedArg::Constraint(c) => match c.kind { ast::AssocTyConstraintKind::Bound { .. } => true, ast::AssocTyConstraintKind::Equality { ref ty } => { involves_impl_trait(ty) } - }) + }, + }) } Some(&ast::GenericArgs::Parenthesized(ref data)) => { any_involves_impl_trait(data.inputs.iter()) || ReplaceBodyWithLoop::should_ignore_fn(&data.output) } - } - }), + }) + } _ => false, } } diff --git a/src/librustc_lexer/src/lib.rs b/src/librustc_lexer/src/lib.rs index d3ac58a49c8d5..e44feee96607a 100644 --- a/src/librustc_lexer/src/lib.rs +++ b/src/librustc_lexer/src/lib.rs @@ -1,5 +1,11 @@ //! Low-level Rust lexer. //! +//! The idea with `librustc_lexer` is to make a reusable library, +//! by separating out pure lexing and rustc-specific concerns, like spans, +//! error reporting an interning. So, rustc_lexer operates directly on `&str`, +//! produces simple tokens which are a pair of type-tag and a bit of original text, +//! and does not report errors, instead storing them as flags on the token. +//! //! Tokens produced by this lexer are not yet ready for parsing the Rust syntax, //! for that see `librustc_parse::lexer`, which converts this basic token stream //! into wide tokens used by actual parser. @@ -17,9 +23,13 @@ mod cursor; pub mod unescape; +#[cfg(test)] +mod tests; + use self::LiteralKind::*; use self::TokenKind::*; use crate::cursor::{Cursor, EOF_CHAR}; +use std::convert::TryInto; /// Parsed token. /// It doesn't contain information about data that has been parsed, @@ -132,9 +142,84 @@ pub enum LiteralKind { /// "b"abc"", "b"abc" ByteStr { terminated: bool }, /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a" - RawStr { n_hashes: usize, started: bool, terminated: bool }, + RawStr(UnvalidatedRawStr), /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a" - RawByteStr { n_hashes: usize, started: bool, terminated: bool }, + RawByteStr(UnvalidatedRawStr), +} + +/// Represents something that looks like a raw string, but may have some +/// problems. Use `.validate()` to convert it into something +/// usable. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct UnvalidatedRawStr { + /// The prefix (`r###"`) is valid + valid_start: bool, + + /// The postfix (`"###`) is valid + valid_end: bool, + + /// The number of leading `#` + n_start_hashes: usize, + /// The number of trailing `#`. `n_end_hashes` <= `n_start_hashes` + n_end_hashes: usize, + /// The offset starting at `r` or `br` where the user may have intended to end the string. + /// Currently, it is the longest sequence of pattern `"#+"`. + possible_terminator_offset: Option, +} + +/// Error produced validating a raw string. Represents cases like: +/// - `r##~"abcde"##`: `LexRawStrError::InvalidStarter` +/// - `r###"abcde"##`: `LexRawStrError::NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)` +/// - Too many `#`s (>65536): `TooManyDelimiters` +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum LexRawStrError { + /// Non `#` characters exist between `r` and `"` eg. `r#~"..` + InvalidStarter, + /// The string was never terminated. `possible_terminator_offset` is the number of characters after `r` or `br` where they + /// may have intended to terminate it. + NoTerminator { expected: usize, found: usize, possible_terminator_offset: Option }, + /// More than 65536 `#`s exist. + TooManyDelimiters, +} + +/// Raw String that contains a valid prefix (`#+"`) and postfix (`"#+`) where +/// there are a matching number of `#` characters in both. Note that this will +/// not consume extra trailing `#` characters: `r###"abcde"####` is lexed as a +/// `ValidatedRawString { n_hashes: 3 }` followed by a `#` token. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub struct ValidatedRawStr { + n_hashes: u16, +} + +impl ValidatedRawStr { + pub fn num_hashes(&self) -> u16 { + self.n_hashes + } +} + +impl UnvalidatedRawStr { + pub fn validate(self) -> Result { + if !self.valid_start { + return Err(LexRawStrError::InvalidStarter); + } + + // Only up to 65535 `#`s are allowed in raw strings + let n_start_safe: u16 = + self.n_start_hashes.try_into().map_err(|_| LexRawStrError::TooManyDelimiters)?; + + if self.n_start_hashes > self.n_end_hashes || !self.valid_end { + Err(LexRawStrError::NoTerminator { + expected: self.n_start_hashes, + found: self.n_end_hashes, + possible_terminator_offset: self.possible_terminator_offset, + }) + } else { + // Since the lexer should never produce a literal with n_end > n_start, if n_start <= n_end, + // they must be equal. + debug_assert_eq!(self.n_start_hashes, self.n_end_hashes); + Ok(ValidatedRawStr { n_hashes: n_start_safe }) + } + } } /// Base of numeric literal encoding according to its prefix. @@ -209,7 +294,7 @@ pub fn is_whitespace(c: char) -> bool { // Dedicated whitespace characters from Unicode | '\u{2028}' // LINE SEPARATOR | '\u{2029}' // PARAGRAPH SEPARATOR - => true, + => true, _ => false, } } @@ -258,12 +343,12 @@ impl Cursor<'_> { 'r' => match (self.first(), self.second()) { ('#', c1) if is_id_start(c1) => self.raw_ident(), ('#', _) | ('"', _) => { - let (n_hashes, started, terminated) = self.raw_double_quoted_string(); + let raw_str_i = self.raw_double_quoted_string(1); let suffix_start = self.len_consumed(); - if terminated { + if raw_str_i.n_end_hashes == raw_str_i.n_start_hashes { self.eat_literal_suffix(); } - let kind = RawStr { n_hashes, started, terminated }; + let kind = RawStr(raw_str_i); Literal { kind, suffix_start } } _ => self.ident(), @@ -293,12 +378,14 @@ impl Cursor<'_> { } ('r', '"') | ('r', '#') => { self.bump(); - let (n_hashes, started, terminated) = self.raw_double_quoted_string(); + let raw_str_i = self.raw_double_quoted_string(2); let suffix_start = self.len_consumed(); + let terminated = raw_str_i.n_start_hashes == raw_str_i.n_end_hashes; if terminated { self.eat_literal_suffix(); } - let kind = RawByteStr { n_hashes, started, terminated }; + + let kind = RawByteStr(raw_str_i); Literal { kind, suffix_start } } _ => self.ident(), @@ -594,29 +681,43 @@ impl Cursor<'_> { false } - /// Eats the double-quoted string and returns a tuple of - /// (amount of the '#' symbols, raw string started, raw string terminated) - fn raw_double_quoted_string(&mut self) -> (usize, bool, bool) { + /// Eats the double-quoted string and returns an `UnvalidatedRawStr`. + fn raw_double_quoted_string(&mut self, prefix_len: usize) -> UnvalidatedRawStr { debug_assert!(self.prev() == 'r'); - let mut started: bool = false; - let mut finished: bool = false; + let mut valid_start: bool = false; + let start_pos = self.len_consumed(); + let (mut possible_terminator_offset, mut max_hashes) = (None, 0); // Count opening '#' symbols. - let n_hashes = self.eat_while(|c| c == '#'); + let n_start_hashes = self.eat_while(|c| c == '#'); // Check that string is started. match self.bump() { - Some('"') => started = true, - _ => return (n_hashes, started, finished), + Some('"') => valid_start = true, + _ => { + return UnvalidatedRawStr { + valid_start, + valid_end: false, + n_start_hashes, + n_end_hashes: 0, + possible_terminator_offset, + }; + } } // Skip the string contents and on each '#' character met, check if this is // a raw string termination. - while !finished { + loop { self.eat_while(|c| c != '"'); if self.is_eof() { - return (n_hashes, started, finished); + return UnvalidatedRawStr { + valid_start, + valid_end: false, + n_start_hashes, + n_end_hashes: max_hashes, + possible_terminator_offset, + }; } // Eat closing double quote. @@ -624,7 +725,10 @@ impl Cursor<'_> { // Check that amount of closing '#' symbols // is equal to the amount of opening ones. - let mut hashes_left = n_hashes; + // Note that this will not consume extra trailing `#` characters: + // `r###"abcde"####` is lexed as a `LexedRawString { n_hashes: 3 }` + // followed by a `#` token. + let mut hashes_left = n_start_hashes; let is_closing_hash = |c| { if c == '#' && hashes_left != 0 { hashes_left -= 1; @@ -633,10 +737,24 @@ impl Cursor<'_> { false } }; - finished = self.eat_while(is_closing_hash) == n_hashes; + let n_end_hashes = self.eat_while(is_closing_hash); + + if n_end_hashes == n_start_hashes { + return UnvalidatedRawStr { + valid_start, + valid_end: true, + n_start_hashes, + n_end_hashes, + possible_terminator_offset: None, + }; + } else if n_end_hashes > max_hashes { + // Keep track of possible terminators to give a hint about + // where there might be a missing terminator + possible_terminator_offset = + Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len); + max_hashes = n_end_hashes; + } } - - (n_hashes, started, finished) } fn eat_decimal_digits(&mut self) -> bool { diff --git a/src/librustc_lexer/src/tests.rs b/src/librustc_lexer/src/tests.rs new file mode 100644 index 0000000000000..06fc159fe2516 --- /dev/null +++ b/src/librustc_lexer/src/tests.rs @@ -0,0 +1,148 @@ +#[cfg(test)] +mod tests { + use crate::*; + + fn check_raw_str( + s: &str, + expected: UnvalidatedRawStr, + validated: Result, + ) { + let s = &format!("r{}", s); + let mut cursor = Cursor::new(s); + cursor.bump(); + let tok = cursor.raw_double_quoted_string(0); + assert_eq!(tok, expected); + assert_eq!(tok.validate(), validated); + } + + #[test] + fn test_naked_raw_str() { + check_raw_str( + r#""abc""#, + UnvalidatedRawStr { + n_start_hashes: 0, + n_end_hashes: 0, + valid_start: true, + valid_end: true, + possible_terminator_offset: None, + }, + Ok(ValidatedRawStr { n_hashes: 0 }), + ); + } + + #[test] + fn test_raw_no_start() { + check_raw_str( + r##""abc"#"##, + UnvalidatedRawStr { + n_start_hashes: 0, + n_end_hashes: 0, + valid_start: true, + valid_end: true, + possible_terminator_offset: None, + }, + Ok(ValidatedRawStr { n_hashes: 0 }), + ); + } + + #[test] + fn test_too_many_terminators() { + // this error is handled in the parser later + check_raw_str( + r###"#"abc"##"###, + UnvalidatedRawStr { + n_start_hashes: 1, + n_end_hashes: 1, + valid_end: true, + valid_start: true, + possible_terminator_offset: None, + }, + Ok(ValidatedRawStr { n_hashes: 1 }), + ); + } + + #[test] + fn test_unterminated() { + check_raw_str( + r#"#"abc"#, + UnvalidatedRawStr { + n_start_hashes: 1, + n_end_hashes: 0, + valid_end: false, + valid_start: true, + possible_terminator_offset: None, + }, + Err(LexRawStrError::NoTerminator { + expected: 1, + found: 0, + possible_terminator_offset: None, + }), + ); + check_raw_str( + r###"##"abc"#"###, + UnvalidatedRawStr { + n_start_hashes: 2, + n_end_hashes: 1, + valid_start: true, + valid_end: false, + possible_terminator_offset: Some(7), + }, + Err(LexRawStrError::NoTerminator { + expected: 2, + found: 1, + possible_terminator_offset: Some(7), + }), + ); + // We're looking for "# not just any # + check_raw_str( + r###"##"abc#"###, + UnvalidatedRawStr { + n_start_hashes: 2, + n_end_hashes: 0, + valid_start: true, + valid_end: false, + possible_terminator_offset: None, + }, + Err(LexRawStrError::NoTerminator { + expected: 2, + found: 0, + possible_terminator_offset: None, + }), + ) + } + + #[test] + fn test_invalid_start() { + check_raw_str( + r##"#~"abc"#"##, + UnvalidatedRawStr { + n_start_hashes: 1, + n_end_hashes: 0, + valid_start: false, + valid_end: false, + possible_terminator_offset: None, + }, + Err(LexRawStrError::InvalidStarter), + ); + } + + #[test] + fn test_unterminated_no_pound() { + // https://github.com/rust-lang/rust/issues/70677 + check_raw_str( + r#"""#, + UnvalidatedRawStr { + n_start_hashes: 0, + n_end_hashes: 0, + valid_start: true, + valid_end: false, + possible_terminator_offset: None, + }, + Err(LexRawStrError::NoTerminator { + expected: 0, + found: 0, + possible_terminator_offset: None, + }), + ); + } +} diff --git a/src/librustc_lexer/src/unescape.rs b/src/librustc_lexer/src/unescape.rs index c51bf735fa727..2a9e1b7cbc346 100644 --- a/src/librustc_lexer/src/unescape.rs +++ b/src/librustc_lexer/src/unescape.rs @@ -58,69 +58,57 @@ pub enum EscapeError { NonAsciiCharInByteString, } -/// Takes a contents of a char literal (without quotes), and returns an -/// unescaped char or an error -pub fn unescape_char(literal_text: &str) -> Result { - let mut chars = literal_text.chars(); - unescape_char_or_byte(&mut chars, Mode::Char) - .map_err(|err| (literal_text.len() - chars.as_str().len(), err)) -} - -/// Takes a contents of a byte literal (without quotes), and returns an -/// unescaped byte or an error. -pub fn unescape_byte(literal_text: &str) -> Result { - let mut chars = literal_text.chars(); - unescape_char_or_byte(&mut chars, Mode::Byte) - .map(byte_from_char) - .map_err(|err| (literal_text.len() - chars.as_str().len(), err)) -} - -/// Takes a contents of a string literal (without quotes) and produces a +/// Takes a contents of a literal (without quotes) and produces a /// sequence of escaped characters or errors. /// Values are returned through invoking of the provided callback. -pub fn unescape_str(literal_text: &str, callback: &mut F) +pub fn unescape_literal(literal_text: &str, mode: Mode, callback: &mut F) where F: FnMut(Range, Result), { - unescape_str_or_byte_str(literal_text, Mode::Str, callback) + match mode { + Mode::Char | Mode::Byte => { + let mut chars = literal_text.chars(); + let result = unescape_char_or_byte(&mut chars, mode); + // The Chars iterator moved forward. + callback(0..(literal_text.len() - chars.as_str().len()), result); + } + Mode::Str | Mode::ByteStr => unescape_str_or_byte_str(literal_text, mode, callback), + // NOTE: Raw strings do not perform any explicit character escaping, here we + // only translate CRLF to LF and produce errors on bare CR. + Mode::RawStr | Mode::RawByteStr => { + unescape_raw_str_or_byte_str(literal_text, mode, callback) + } + } } -/// Takes a contents of a byte string literal (without quotes) and produces a -/// sequence of bytes or errors. +/// Takes a contents of a byte, byte string or raw byte string (without quotes) +/// and produces a sequence of bytes or errors. /// Values are returned through invoking of the provided callback. -pub fn unescape_byte_str(literal_text: &str, callback: &mut F) +pub fn unescape_byte_literal(literal_text: &str, mode: Mode, callback: &mut F) where F: FnMut(Range, Result), { - unescape_str_or_byte_str(literal_text, Mode::ByteStr, &mut |range, char| { - callback(range, char.map(byte_from_char)) + assert!(mode.is_bytes()); + unescape_literal(literal_text, mode, &mut |range, result| { + callback(range, result.map(byte_from_char)); }) } -/// Takes a contents of a raw string literal (without quotes) and produces a -/// sequence of characters or errors. -/// Values are returned through invoking of the provided callback. -/// NOTE: Raw strings do not perform any explicit character escaping, here we -/// only translate CRLF to LF and produce errors on bare CR. -pub fn unescape_raw_str(literal_text: &str, callback: &mut F) -where - F: FnMut(Range, Result), -{ - unescape_raw_str_or_byte_str(literal_text, Mode::Str, callback) +/// Takes a contents of a char literal (without quotes), and returns an +/// unescaped char or an error +pub fn unescape_char(literal_text: &str) -> Result { + let mut chars = literal_text.chars(); + unescape_char_or_byte(&mut chars, Mode::Char) + .map_err(|err| (literal_text.len() - chars.as_str().len(), err)) } -/// Takes a contents of a raw byte string literal (without quotes) and produces a -/// sequence of bytes or errors. -/// Values are returned through invoking of the provided callback. -/// NOTE: Raw strings do not perform any explicit character escaping, here we -/// only translate CRLF to LF and produce errors on bare CR. -pub fn unescape_raw_byte_str(literal_text: &str, callback: &mut F) -where - F: FnMut(Range, Result), -{ - unescape_raw_str_or_byte_str(literal_text, Mode::ByteStr, &mut |range, char| { - callback(range, char.map(byte_from_char)) - }) +/// Takes a contents of a byte literal (without quotes), and returns an +/// unescaped byte or an error. +pub fn unescape_byte(literal_text: &str) -> Result { + let mut chars = literal_text.chars(); + unescape_char_or_byte(&mut chars, Mode::Byte) + .map(byte_from_char) + .map_err(|err| (literal_text.len() - chars.as_str().len(), err)) } /// What kind of literal do we parse. @@ -130,13 +118,15 @@ pub enum Mode { Str, Byte, ByteStr, + RawStr, + RawByteStr, } impl Mode { pub fn in_single_quotes(self) -> bool { match self { Mode::Char | Mode::Byte => true, - Mode::Str | Mode::ByteStr => false, + Mode::Str | Mode::ByteStr | Mode::RawStr | Mode::RawByteStr => false, } } @@ -146,8 +136,8 @@ impl Mode { pub fn is_bytes(self) -> bool { match self { - Mode::Byte | Mode::ByteStr => true, - Mode::Char | Mode::Str => false, + Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true, + Mode::Char | Mode::Str | Mode::RawStr => false, } } } @@ -345,7 +335,7 @@ where fn byte_from_char(c: char) -> u8 { let res = c as u32; - assert!(res <= u8::max_value() as u32, "guaranteed because of Mode::Byte(Str)"); + assert!(res <= u8::max_value() as u32, "guaranteed because of Mode::ByteStr"); res as u8 } diff --git a/src/librustc_lexer/src/unescape/tests.rs b/src/librustc_lexer/src/unescape/tests.rs index e7b1ff6479d88..f2b751a78f27f 100644 --- a/src/librustc_lexer/src/unescape/tests.rs +++ b/src/librustc_lexer/src/unescape/tests.rs @@ -102,7 +102,7 @@ fn test_unescape_char_good() { fn test_unescape_str_good() { fn check(literal_text: &str, expected: &str) { let mut buf = Ok(String::with_capacity(literal_text.len())); - unescape_str(literal_text, &mut |range, c| { + unescape_literal(literal_text, Mode::Str, &mut |range, c| { if let Ok(b) = &mut buf { match c { Ok(c) => b.push(c), @@ -222,7 +222,7 @@ fn test_unescape_byte_good() { fn test_unescape_byte_str_good() { fn check(literal_text: &str, expected: &[u8]) { let mut buf = Ok(Vec::with_capacity(literal_text.len())); - unescape_byte_str(literal_text, &mut |range, c| { + unescape_byte_literal(literal_text, Mode::ByteStr, &mut |range, c| { if let Ok(b) = &mut buf { match c { Ok(c) => b.push(c), @@ -246,7 +246,7 @@ fn test_unescape_byte_str_good() { fn test_unescape_raw_str() { fn check(literal: &str, expected: &[(Range, Result)]) { let mut unescaped = Vec::with_capacity(literal.len()); - unescape_raw_str(literal, &mut |range, res| unescaped.push((range, res))); + unescape_literal(literal, Mode::RawStr, &mut |range, res| unescaped.push((range, res))); assert_eq!(unescaped, expected); } @@ -258,7 +258,9 @@ fn test_unescape_raw_str() { fn test_unescape_raw_byte_str() { fn check(literal: &str, expected: &[(Range, Result)]) { let mut unescaped = Vec::with_capacity(literal.len()); - unescape_raw_byte_str(literal, &mut |range, res| unescaped.push((range, res))); + unescape_byte_literal(literal, Mode::RawByteStr, &mut |range, res| { + unescaped.push((range, res)) + }); assert_eq!(unescaped, expected); } diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 9785af5eab2fd..ada6f2a9381dc 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -10,8 +10,8 @@ path = "lib.rs" [dependencies] log = "0.4" -unicode-security = "0.0.2" -rustc = { path = "../librustc" } +unicode-security = "0.0.3" +rustc_middle = { path = "../librustc_middle" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } rustc_errors = { path = "../librustc_errors" } @@ -23,5 +23,4 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_feature = { path = "../librustc_feature" } rustc_index = { path = "../librustc_index" } rustc_session = { path = "../librustc_session" } -rustc_infer = { path = "../librustc_infer" } rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_lint/array_into_iter.rs b/src/librustc_lint/array_into_iter.rs index a91d735622f4e..3eb587c016acb 100644 --- a/src/librustc_lint/array_into_iter.rs +++ b/src/librustc_lint/array_into_iter.rs @@ -1,8 +1,8 @@ use crate::{LateContext, LateLintPass, LintContext}; -use rustc::ty; -use rustc::ty::adjustment::{Adjust, Adjustment}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_middle::ty; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::lint::FutureIncompatibleInfo; use rustc_span::symbol::sym; diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 66d9fe7e14988..bca91fb7b5d16 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1,9 +1,8 @@ //! Lints in the Rust compiler. //! //! This contains lints which can feasibly be implemented as their own -//! AST visitor. Also see `rustc::lint::builtin`, which contains the -//! definitions of lints that are emitted directly inside the main -//! compiler. +//! AST visitor. Also see `rustc_session::lint::builtin`, which contains the +//! definitions of lints that are emitted directly inside the main compiler. //! //! To add a new lint to rustc, declare it here using `declare_lint!()`. //! Then add code to emit the new lint in the appropriate circumstances. @@ -11,7 +10,7 @@ //! new `LintPass`, or using `Session::add_lint` elsewhere in the //! compiler. Only do the latter if the check can't be written cleanly as a //! `LintPass` (also, note that such lints will need to be defined in -//! `rustc::lint::builtin`, not here). +//! `rustc_session::lint::builtin`, not here). //! //! If you define a new `EarlyLintPass`, you will also need to add it to the //! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in @@ -22,8 +21,6 @@ //! `late_lint_methods!` invocation in `lib.rs`. use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc::lint::LintDiagnosticBuilder; -use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt}; use rustc_ast::ast::{self, Expr}; use rustc_ast::attr::{self, HasAttrs}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; @@ -31,18 +28,22 @@ use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast_pretty::pprust::{self, expr_to_string}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_feature::Stability; use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType}; +use rustc_feature::{GateIssue, Stability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{GenericParamKind, PatKind}; use rustc_hir::{HirIdSet, Node}; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::FutureIncompatibleInfo; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Span}; +use rustc_target::abi::VariantIdx; use rustc_trait_selection::traits::misc::can_type_implement_copy; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -50,7 +51,7 @@ use crate::nonstandard_style::{method_context, MethodLateContext}; use log::debug; use std::fmt::Write; -// hardwired lints from librustc +// hardwired lints from librustc_middle pub use rustc_session::lint::builtin::*; declare_lint! { @@ -76,7 +77,7 @@ impl EarlyLintPass for WhileTrue { if let ast::LitKind::Bool(true) = lit.kind { if !lit.span.from_expansion() { let msg = "denote infinite loops with `loop { ... }`"; - let condition_span = cx.sess.source_map().def_span(e.span); + let condition_span = cx.sess.source_map().guess_head_span(e.span); cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { lint.build(msg) .span_suggestion_short( @@ -104,11 +105,13 @@ declare_lint_pass!(BoxPointers => [BOX_POINTERS]); impl BoxPointers { fn check_heap_type(&self, cx: &LateContext<'_, '_>, span: Span, ty: Ty<'_>) { - for leaf_ty in ty.walk() { - if leaf_ty.is_box() { - cx.struct_span_lint(BOX_POINTERS, span, |lint| { - lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit() - }); + for leaf in ty.walk() { + if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { + if leaf_ty.is_box() { + cx.struct_span_lint(BOX_POINTERS, span, |lint| { + lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit() + }); + } } } } @@ -374,9 +377,13 @@ impl MissingDoc { let has_doc = attrs.iter().any(|a| has_doc(a)); if !has_doc { - cx.struct_span_lint(MISSING_DOCS, cx.tcx.sess.source_map().def_span(sp), |lint| { - lint.build(&format!("missing documentation for {} {}", article, desc)).emit() - }); + cx.struct_span_lint( + MISSING_DOCS, + cx.tcx.sess.source_map().guess_head_span(sp), + |lint| { + lint.build(&format!("missing documentation for {} {}", article, desc)).emit() + }, + ); } } } @@ -406,7 +413,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { if !has_doc { cx.struct_span_lint( MISSING_DOCS, - cx.tcx.sess.source_map().def_span(macro_def.span), + cx.tcx.sess.source_map().guess_head_span(macro_def.span), |lint| lint.build("missing documentation for macro").emit(), ); } @@ -429,7 +436,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { // If the trait is private, add the impl items to `private_traits` so they don't get // reported for missing docs. let real_trait = trait_ref.path.res.def_id(); - if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(real_trait) { + if let Some(def_id) = real_trait.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(def_id); if let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) { if let hir::VisibilityKind::Inherited = item.vis.node { for impl_item_ref in items { @@ -454,7 +462,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { }; let def_id = cx.tcx.hir().local_def_id(it.hir_id); - let (article, desc) = cx.tcx.article_and_description(def_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, article, desc); } @@ -465,7 +473,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { } let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); - let (article, desc) = cx.tcx.article_and_description(def_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); self.check_missing_docs_attrs( cx, @@ -484,7 +492,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { } let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); - let (article, desc) = cx.tcx.article_and_description(def_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); self.check_missing_docs_attrs( cx, Some(impl_item.hir_id), @@ -602,8 +610,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations { let mut impls = HirIdSet::default(); cx.tcx.for_each_impl(debug, |d| { if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { - if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(ty_def.did) { - impls.insert(hir_id); + if let Some(def_id) = ty_def.did.as_local() { + impls.insert(cx.tcx.hir().as_local_hir_id(def_id)); } } }); @@ -642,41 +650,35 @@ declare_lint_pass!( impl EarlyLintPass for AnonymousParameters { fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { - match it.kind { - ast::AssocItemKind::Fn(_, ref sig, _, _) => { - for arg in sig.decl.inputs.iter() { - match arg.pat.kind { - ast::PatKind::Ident(_, ident, None) => { - if ident.name == kw::Invalid { - cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { - let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); - - let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { - (snip.as_str(), Applicability::MachineApplicable) - } else { - ("", Applicability::HasPlaceholders) - }; + if let ast::AssocItemKind::Fn(_, ref sig, _, _) = it.kind { + for arg in sig.decl.inputs.iter() { + if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { + if ident.name == kw::Invalid { + cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { + let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); + + let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { + (snip.as_str(), Applicability::MachineApplicable) + } else { + ("", Applicability::HasPlaceholders) + }; - lint.build( - "anonymous parameters are deprecated and will be \ + lint.build( + "anonymous parameters are deprecated and will be \ removed in the next edition.", - ) - .span_suggestion( - arg.pat.span, - "try naming the parameter or explicitly \ + ) + .span_suggestion( + arg.pat.span, + "try naming the parameter or explicitly \ ignoring it", - format!("_: {}", ty_snip), - appl, - ) - .emit(); - }) - } - } - _ => (), + format!("_: {}", ty_snip), + appl, + ) + .emit(); + }) } } } - _ => (), } } } @@ -884,18 +886,14 @@ declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { use rustc_target::spec::abi::Abi::RustIntrinsic; - - match get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind)) { - Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) => { - if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { - let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \ + if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) = + get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind)) + { + if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { + let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \ consider instead using an UnsafeCell"; - cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| { - lint.build(msg).emit() - }); - } + cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| lint.build(msg).emit()); } - _ => (), } fn get_transmute_from_to<'a, 'tcx>( @@ -978,7 +976,7 @@ impl UnreachablePub { if span.from_expansion() { applicability = Applicability::MaybeIncorrect; } - let def_span = cx.tcx.sess.source_map().def_span(span); + let def_span = cx.tcx.sess.source_map().guess_head_span(span); cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| { let mut err = lint.build(&format!("unreachable `pub` {}", what)); let replacement = if cx.tcx.features().crate_visibility_modifier { @@ -1169,7 +1167,7 @@ declare_lint_pass!( ); fn check_const(cx: &LateContext<'_, '_>, body_id: hir::BodyId) { - let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); // trigger the query once for all constants since that will already report the errors // FIXME: Use ensure here let _ = cx.tcx.const_eval_poly(def_id); @@ -1203,8 +1201,8 @@ declare_lint_pass!( impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc::ty::fold::TypeFoldable; - use rustc::ty::Predicate::*; + use rustc_middle::ty::fold::TypeFoldable; + use rustc_middle::ty::Predicate::*; if cx.tcx.features().trivial_bounds { let def_id = cx.tcx.hir().local_def_id(item.hir_id); @@ -1223,7 +1221,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints { ObjectSafe(..) | ClosureKind(..) | Subtype(..) | - ConstEvaluatable(..) => continue, + ConstEvaluatable(..) | + ConstEquate(..) => continue, }; if predicate.is_global() { cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { @@ -1357,7 +1356,7 @@ declare_lint! { } pub struct UnnameableTestItems { - boundary: hir::HirId, // HirId of the item under which things are not nameable + boundary: Option, // HirId of the item under which things are not nameable items_nameable: bool, } @@ -1365,7 +1364,7 @@ impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]); impl UnnameableTestItems { pub fn new() -> Self { - Self { boundary: hir::DUMMY_HIR_ID, items_nameable: true } + Self { boundary: None, items_nameable: true } } } @@ -1375,7 +1374,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems { if let hir::ItemKind::Mod(..) = it.kind { } else { self.items_nameable = false; - self.boundary = it.hir_id; + self.boundary = Some(it.hir_id); } return; } @@ -1388,7 +1387,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems { } fn check_item_post(&mut self, _cx: &LateContext<'_, '_>, it: &hir::Item<'_>) { - if !self.items_nameable && self.boundary == it.hir_id { + if !self.items_nameable && self.boundary == Some(it.hir_id) { self.items_nameable = true; } } @@ -1430,7 +1429,7 @@ impl KeywordIdents { &mut self, cx: &EarlyContext<'_>, UnderMacro(under_macro): UnderMacro, - ident: ast::Ident, + ident: Ident, ) { let next_edition = match cx.sess.edition() { Edition::Edition2015 => { @@ -1484,7 +1483,7 @@ impl EarlyLintPass for KeywordIdents { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { self.check_tokens(cx, mac.args.inner_tokens()); } - fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) { + fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { self.check_ident_token(cx, UnderMacro(false), ident); } } @@ -1534,7 +1533,8 @@ impl ExplicitOutlivesRequirements { inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], ty_generics: &'tcx ty::Generics, ) -> Vec> { - let index = ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id)]; + let index = + ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id).to_def_id()]; match param.kind { hir::GenericParamKind::Lifetime { .. } => { @@ -1554,7 +1554,7 @@ impl ExplicitOutlivesRequirements { inferred_outlives: &[ty::Region<'tcx>], infer_static: bool, ) -> Vec<(usize, Span)> { - use rustc::middle::resolve_lifetime::Region; + use rustc_middle::middle::resolve_lifetime::Region; bounds .iter() @@ -1637,7 +1637,7 @@ impl ExplicitOutlivesRequirements { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements { fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { - use rustc::middle::resolve_lifetime::Region; + use rustc_middle::middle::resolve_lifetime::Region; let infer_static = cx.tcx.features().infer_static_outlives_requirements; let def_id = cx.tcx.hir().local_def_id(item.hir_id); @@ -1818,13 +1818,21 @@ impl EarlyLintPass for IncompleteFeatures { .map(|(name, span, _)| (name, span)) .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) .filter(|(name, _)| rustc_feature::INCOMPLETE_FEATURES.iter().any(|f| name == &f)) - .for_each(|(name, &span)| { + .for_each(|(&name, &span)| { cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| { - lint.build(&format!( - "the feature `{}` is incomplete and may cause the compiler to crash", + let mut builder = lint.build(&format!( + "the feature `{}` is incomplete and may not be safe to use \ + and/or cause compiler crashes", name, - )) - .emit() + )); + if let Some(n) = rustc_feature::find_feature_issue(name, GateIssue::Language) { + builder.note(&format!( + "see issue #{} \ + for more information", + n, n, + )); + } + builder.emit(); }) }); } @@ -1922,7 +1930,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { ty: Ty<'tcx>, init: InitKind, ) -> Option { - use rustc::ty::TyKind::*; + use rustc_middle::ty::TyKind::*; match ty.kind { // Primitive types that don't like 0 as a value. Ref(..) => Some(("references must be non-null".to_string(), None)), diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs index 257b179d6ba6d..e5d3227d5afd4 100644 --- a/src/librustc_lint/context.rs +++ b/src/librustc_lint/context.rs @@ -18,11 +18,6 @@ use self::TargetLint::*; use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -use rustc::lint::LintDiagnosticBuilder; -use rustc::middle::privacy::AccessLevels; -use rustc::middle::stability; -use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout}; -use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; use rustc_ast::ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; @@ -31,10 +26,16 @@ use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::stability; +use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; +use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::Session; use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP}; +use rustc_target::abi::LayoutOf; use std::slice; @@ -565,7 +566,7 @@ pub trait LintContext: Sized { stability::deprecation_suggestion(&mut db, suggestion, span) } BuiltinLintDiagnostics::UnusedDocComment(span) => { - db.span_label(span, "rustdoc does not generate documentation for macros"); + db.span_label(span, "rustdoc does not generate documentation for macro invocations"); db.help("to document an item produced by a macro, \ the macro must produce the documentation as part of its expansion"); } @@ -787,9 +788,8 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> { let mut path = print_prefix(self)?; // Skip `::{{constructor}}` on tuple/unit structs. - match disambiguated_data.data { - DefPathData::Ctor => return Ok(path), - _ => {} + if let DefPathData::Ctor = disambiguated_data.data { + return Ok(path); } path.push(disambiguated_data.data.as_symbol()); @@ -811,9 +811,9 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> { impl<'a, 'tcx> LayoutOf for LateContext<'a, 'tcx> { type Ty = Ty<'tcx>; - type TyLayout = Result, LayoutError<'tcx>>; + type TyAndLayout = Result, LayoutError<'tcx>>; - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { self.tcx.layout_of(self.param_env.and(ty)) } } diff --git a/src/librustc_lint/early.rs b/src/librustc_lint/early.rs index 34da29c974777..06987ffa3d569 100644 --- a/src/librustc_lint/early.rs +++ b/src/librustc_lint/early.rs @@ -20,6 +20,7 @@ use rustc_ast::ast; use rustc_ast::visit as ast_visit; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; +use rustc_span::symbol::Ident; use rustc_span::Span; use log::debug; @@ -104,6 +105,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> run_early_pass!(self, check_pat_post, p); } + fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { + run_early_pass!(self, check_anon_const, c); + ast_visit::walk_anon_const(self, c); + } + fn visit_expr(&mut self, e: &'a ast::Expr) { self.with_lint_attrs(e.id, &e.attrs, |cx| { run_early_pass!(cx, check_expr, e); @@ -154,7 +160,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::walk_ty(self, t); } - fn visit_ident(&mut self, ident: ast::Ident) { + fn visit_ident(&mut self, ident: Ident) { run_early_pass!(self, check_ident, ident); } diff --git a/src/librustc_lint/internal.rs b/src/librustc_lint/internal.rs index d8c685f2b22ff..12b7459e88dc3 100644 --- a/src/librustc_lint/internal.rs +++ b/src/librustc_lint/internal.rs @@ -2,13 +2,13 @@ //! Clippy. use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_ast::ast::{Ident, Item, ItemKind}; +use rustc_ast::ast::{Item, ItemKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; declare_tool_lint! { pub rustc::DEFAULT_HASH_TYPES, @@ -175,18 +175,15 @@ fn lint_ty_kind_usage(cx: &LateContext<'_, '_>, segment: &PathSegment<'_>) -> bo } fn is_ty_or_ty_ctxt(cx: &LateContext<'_, '_>, ty: &Ty<'_>) -> Option { - match &ty.kind { - TyKind::Path(qpath) => { - if let QPath::Resolved(_, path) = qpath { - let did = path.res.opt_def_id()?; - if cx.tcx.is_diagnostic_item(sym::Ty, did) { - return Some(format!("Ty{}", gen_args(path.segments.last().unwrap()))); - } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) { - return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap()))); - } + if let TyKind::Path(qpath) = &ty.kind { + if let QPath::Resolved(_, path) = qpath { + let did = path.res.opt_def_id()?; + if cx.tcx.is_diagnostic_item(sym::Ty, did) { + return Some(format!("Ty{}", gen_args(path.segments.last().unwrap()))); + } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) { + return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap()))); } } - _ => {} } None diff --git a/src/librustc_lint/late.rs b/src/librustc_lint/late.rs index d2cc551060357..c8f827b1f5ced 100644 --- a/src/librustc_lint/late.rs +++ b/src/librustc_lint/late.rs @@ -15,16 +15,17 @@ //! for all lint attributes. use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; -use rustc::hir::map::Map; -use rustc::ty::{self, TyCtxt}; use rustc_ast::ast; use rustc_ast::walk_list; use rustc_data_structures::sync::{join, par_iter, ParallelIterator}; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit as hir_visit; use rustc_hir::intravisit::Visitor; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::LintPass; +use rustc_span::symbol::Symbol; use rustc_span::Span; use log::debug; @@ -192,7 +193,7 @@ impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> hir_visit::Visitor<'tcx> fn visit_variant_data( &mut self, s: &'tcx hir::VariantData<'tcx>, - _: ast::Name, + _: Symbol, _: &'tcx hir::Generics<'tcx>, _: hir::HirId, _: Span, @@ -227,7 +228,7 @@ impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> hir_visit::Visitor<'tcx> hir_visit::walk_ty(self, t); } - fn visit_name(&mut self, sp: Span, name: ast::Name) { + fn visit_name(&mut self, sp: Span, name: Symbol) { lint_callback!(self, check_name, sp, name); } @@ -353,7 +354,7 @@ crate::late_lint_methods!(late_lint_pass_impl, [], ['tcx]); fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>( tcx: TyCtxt<'tcx>, - module_def_id: DefId, + module_def_id: LocalDefId, pass: T, ) { let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); @@ -364,7 +365,7 @@ fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>( param_env: ty::ParamEnv::empty(), access_levels, lint_store: unerased_lint_store(tcx), - last_node_with_lint_attrs: tcx.hir().as_local_hir_id(module_def_id).unwrap(), + last_node_with_lint_attrs: tcx.hir().as_local_hir_id(module_def_id), generics: None, only_module: true, }; @@ -382,7 +383,7 @@ fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>( pub fn late_lint_mod<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>( tcx: TyCtxt<'tcx>, - module_def_id: DefId, + module_def_id: LocalDefId, builtin_lints: T, ) { if tcx.sess.opts.debugging_opts.no_interleave_lints { diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 2062f9499aeb9..274e57ae64cac 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -1,10 +1,5 @@ use crate::context::{CheckLintNameResult, LintStore}; use crate::late::unerased_lint_store; -use rustc::hir::map::Map; -use rustc::lint::LintDiagnosticBuilder; -use rustc::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::ast; use rustc_ast::attr; use rustc_ast::unwrap_or; @@ -14,6 +9,11 @@ use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::{intravisit, HirId}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_session::lint::{builtin, Level, Lint}; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -22,7 +22,7 @@ use rustc_span::symbol::{sym, Symbol}; use std::cmp; -fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap { +fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> LintLevelMap { assert_eq!(cnum, LOCAL_CRATE); let store = unerased_lint_store(tcx); let levels = LintLevelsBuilder::new(tcx.sess, false, &store); @@ -37,7 +37,7 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap { intravisit::walk_crate(&mut builder, krate); builder.levels.pop(push); - tcx.arena.alloc(builder.levels.build_map()) + builder.levels.build_map() } pub struct LintLevelsBuilder<'s> { @@ -103,8 +103,8 @@ impl<'s> LintLevelsBuilder<'s> { /// * It'll validate all lint-related attributes in `attrs` /// * It'll mark all lint-related attributes as used /// * Lint levels will be updated based on the attributes provided - /// * Lint attributes are validated, e.g., a #[forbid] can't be switched to - /// #[allow] + /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to + /// `#[allow]` /// /// Don't forget to call `pop`! pub fn push(&mut self, attrs: &[ast::Attribute], store: &LintStore) -> BuilderPush { @@ -388,6 +388,11 @@ impl<'s> LintLevelsBuilder<'s> { self.cur = push.prev; } + /// Find the lint level for a lint. + pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintSource) { + self.sets.get_lint_level(lint, self.cur, None, self.sess) + } + /// Used to emit a lint-related diagnostic based on the current state of /// this lint context. pub fn struct_lint( @@ -396,7 +401,7 @@ impl<'s> LintLevelsBuilder<'s> { span: Option, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), ) { - let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess); + let (level, src) = self.lint_level(lint); struct_lint_level(self.sess, lint, level, src, span, decorate) } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 825ac04bc0920..b791d313fc4f4 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -6,9 +6,9 @@ //! other phases of the compiler, which are generally required to hold in order //! to compile the program at all. //! -//! Most lints can be written as `LintPass` instances. These run after +//! Most lints can be written as [LintPass] instances. These run after //! all other analyses. The `LintPass`es built into rustc are defined -//! within `rustc_session::lint::builtin`, +//! within [rustc_session::lint::builtin], //! which has further comments on how to add such a lint. //! rustc can also load user-defined lint plugins via the plugin mechanism. //! @@ -19,7 +19,7 @@ //! example) requires more effort. See `emit_lint` and `GatherNodeLevels` //! in `context.rs`. //! -//! Some code also exists in `rustc_session::lint`, `rustc::lint`. +//! Some code also exists in [rustc_session::lint], [rustc_middle::lint]. //! //! ## Note //! @@ -32,10 +32,11 @@ #![feature(crate_visibility_modifier)] #![feature(never_type)] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] -extern crate rustc; +extern crate rustc_middle; #[macro_use] extern crate rustc_session; @@ -53,15 +54,17 @@ mod redundant_semicolon; mod types; mod unused; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::ast; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS, - INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, + INTRA_DOC_LINK_RESOLUTION_FAILURE, INVALID_CODEBLOCK_ATTRIBUTE, MISSING_DOC_CODE_EXAMPLES, + PRIVATE_DOC_TESTS, }; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; use array_into_iter::ArrayIntoIter; @@ -88,7 +91,7 @@ pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { lint_mod, ..*providers }; } -fn lint_mod(tcx: TyCtxt<'_>, module_def_id: DefId) { +fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); } @@ -104,6 +107,7 @@ macro_rules! early_lint_passes { $args, [ UnusedParens: UnusedParens, + UnusedBraces: UnusedBraces, UnusedImportBraces: UnusedImportBraces, UnsafeCode: UnsafeCode, AnonymousParameters: AnonymousParameters, @@ -275,6 +279,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { UNUSED_FEATURES, UNUSED_LABELS, UNUSED_PARENS, + UNUSED_BRACES, REDUNDANT_SEMICOLONS ); @@ -296,6 +301,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { add_lint_group!( "rustdoc", INTRA_DOC_LINK_RESOLUTION_FAILURE, + INVALID_CODEBLOCK_ATTRIBUTE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS ); diff --git a/src/librustc_lint/non_ascii_idents.rs b/src/librustc_lint/non_ascii_idents.rs index 470fac2675bac..ad02b2637d22f 100644 --- a/src/librustc_lint/non_ascii_idents.rs +++ b/src/librustc_lint/non_ascii_idents.rs @@ -1,5 +1,9 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; use rustc_ast::ast; +use rustc_data_structures::fx::FxHashMap; +use rustc_span::symbol::{Ident, SymbolStr}; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; declare_lint! { pub NON_ASCII_IDENTS, @@ -13,10 +17,145 @@ declare_lint! { "detects uncommon Unicode codepoints in identifiers" } -declare_lint_pass!(NonAsciiIdents => [NON_ASCII_IDENTS, UNCOMMON_CODEPOINTS]); +// FIXME: Change this to warn. +declare_lint! { + pub CONFUSABLE_IDENTS, + Allow, + "detects visually confusable pairs between identifiers" +} + +declare_lint_pass!(NonAsciiIdents => [NON_ASCII_IDENTS, UNCOMMON_CODEPOINTS, CONFUSABLE_IDENTS]); + +enum CowBoxSymStr { + Interned(SymbolStr), + Owned(Box), +} + +impl Deref for CowBoxSymStr { + type Target = str; + + fn deref(&self) -> &str { + match self { + CowBoxSymStr::Interned(interned) => interned, + CowBoxSymStr::Owned(ref owned) => owned, + } + } +} + +impl Hash for CowBoxSymStr { + #[inline] + fn hash(&self, state: &mut H) { + Hash::hash(&**self, state) + } +} + +impl PartialEq for CowBoxSymStr { + #[inline] + fn eq(&self, other: &CowBoxSymStr) -> bool { + PartialEq::eq(&**self, &**other) + } +} + +impl Eq for CowBoxSymStr {} + +fn calc_skeleton(symbol_str: SymbolStr, buffer: &'_ mut String) -> CowBoxSymStr { + use std::mem::swap; + use unicode_security::confusable_detection::skeleton; + buffer.clear(); + buffer.extend(skeleton(&symbol_str)); + if symbol_str == *buffer { + CowBoxSymStr::Interned(symbol_str) + } else { + let mut owned = String::new(); + swap(buffer, &mut owned); + CowBoxSymStr::Owned(owned.into_boxed_str()) + } +} + +fn is_in_ascii_confusable_closure(c: char) -> bool { + // FIXME: move this table to `unicode_security` crate. + // data here corresponds to Unicode 13. + const ASCII_CONFUSABLE_CLOSURE: &[(u64, u64)] = &[(0x00, 0x7f), (0xba, 0xba), (0x2080, 0x2080)]; + let c = c as u64; + for &(range_start, range_end) in ASCII_CONFUSABLE_CLOSURE { + if c >= range_start && c <= range_end { + return true; + } + } + false +} + +fn is_in_ascii_confusable_closure_relevant_list(c: char) -> bool { + // FIXME: move this table to `unicode_security` crate. + // data here corresponds to Unicode 13. + const ASCII_CONFUSABLE_CLOSURE_RELEVANT_LIST: &[u64] = &[ + 0x22, 0x25, 0x27, 0x2f, 0x30, 0x31, 0x49, 0x4f, 0x60, 0x6c, 0x6d, 0x6e, 0x72, 0x7c, 0xba, + 0x2080, + ]; + let c = c as u64; + for &item in ASCII_CONFUSABLE_CLOSURE_RELEVANT_LIST { + if c == item { + return true; + } + } + false +} impl EarlyLintPass for NonAsciiIdents { - fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) { + fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + use rustc_session::lint::Level; + if cx.builder.lint_level(CONFUSABLE_IDENTS).0 == Level::Allow { + return; + } + let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock(); + let mut symbol_strs_and_spans = Vec::with_capacity(symbols.len()); + let mut in_fast_path = true; + for (symbol, sp) in symbols.iter() { + // fast path + let symbol_str = symbol.as_str(); + if !symbol_str.chars().all(is_in_ascii_confusable_closure) { + // fallback to slow path. + symbol_strs_and_spans.clear(); + in_fast_path = false; + break; + } + if symbol_str.chars().any(is_in_ascii_confusable_closure_relevant_list) { + symbol_strs_and_spans.push((symbol_str, *sp)); + } + } + if !in_fast_path { + // slow path + for (symbol, sp) in symbols.iter() { + let symbol_str = symbol.as_str(); + symbol_strs_and_spans.push((symbol_str, *sp)); + } + } + drop(symbols); + symbol_strs_and_spans.sort_by_key(|x| x.0.clone()); + let mut skeleton_map = + FxHashMap::with_capacity_and_hasher(symbol_strs_and_spans.len(), Default::default()); + let mut str_buf = String::new(); + for (symbol_str, sp) in symbol_strs_and_spans { + let skeleton = calc_skeleton(symbol_str.clone(), &mut str_buf); + skeleton_map + .entry(skeleton) + .and_modify(|(existing_symbolstr, existing_span)| { + cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| { + lint.build(&format!( + "identifier pair considered confusable between `{}` and `{}`", + existing_symbolstr, symbol_str + )) + .span_label( + *existing_span, + "this is where the previous identifier occurred", + ) + .emit(); + }); + }) + .or_insert((symbol_str, sp)); + } + } + fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { use unicode_security::GeneralSecurityProfile; let name_str = ident.name.as_str(); if name_str.is_ascii() { diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs index afab55358d938..052b461039b23 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/src/librustc_lint/nonstandard_style.rs @@ -1,5 +1,4 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc::ty; use rustc_ast::ast; use rustc_attr as attr; use rustc_errors::Applicability; @@ -7,6 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; use rustc_hir::{GenericParamKind, PatKind}; +use rustc_middle::ty; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, BytePos, Span}; use rustc_target::spec::abi::Abi; diff --git a/src/librustc_lint/passes.rs b/src/librustc_lint/passes.rs index ace154714458e..04a398a29ba7b 100644 --- a/src/librustc_lint/passes.rs +++ b/src/librustc_lint/passes.rs @@ -5,6 +5,7 @@ use rustc_data_structures::sync; use rustc_hir as hir; use rustc_session::lint::builtin::HardwiredLints; use rustc_session::lint::LintPass; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; #[macro_export] @@ -14,7 +15,7 @@ macro_rules! late_lint_methods { fn check_param(a: &$hir hir::Param<$hir>); fn check_body(a: &$hir hir::Body<$hir>); fn check_body_post(a: &$hir hir::Body<$hir>); - fn check_name(a: Span, b: ast::Name); + fn check_name(a: Span, b: Symbol); fn check_crate(a: &$hir hir::Crate<$hir>); fn check_crate_post(a: &$hir hir::Crate<$hir>); fn check_mod(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId); @@ -155,7 +156,7 @@ macro_rules! early_lint_methods { ($macro:path, $args:tt) => ( $macro!($args, [ fn check_param(a: &ast::Param); - fn check_ident(a: ast::Ident); + fn check_ident(a: Ident); fn check_crate(a: &ast::Crate); fn check_crate_post(a: &ast::Crate); fn check_mod(a: &ast::Mod, b: Span, c: ast::NodeId); @@ -170,6 +171,7 @@ macro_rules! early_lint_methods { fn check_stmt(a: &ast::Stmt); fn check_arm(a: &ast::Arm); fn check_pat(a: &ast::Pat); + fn check_anon_const(a: &ast::AnonConst); fn check_pat_post(a: &ast::Pat); fn check_expr(a: &ast::Expr); fn check_expr_post(a: &ast::Expr); diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index fcd50001cb3a9..703c2a7a443a9 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -1,10 +1,6 @@ #![allow(non_snake_case)] use crate::{LateContext, LateLintPass, LintContext}; -use rustc::mir::interpret::{sign_extend, truncate}; -use rustc::ty::layout::{self, IntegerExt, LayoutOf, SizeSkeleton, VariantIdx}; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc_ast::ast; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; @@ -13,14 +9,18 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{is_range_literal, ExprKind, Node}; use rustc_index::vec::Idx; +use rustc_middle::mir::interpret::{sign_extend, truncate}; +use rustc_middle::ty::layout::{IntegerExt, SizeSkeleton}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_target::abi::{DiscriminantKind, Integer, LayoutOf, VariantIdx, Variants}; use rustc_target::spec::abi::Abi; use log::debug; use std::cmp; -use std::{f32, f64, i16, i32, i64, i8, u16, u32, u64, u8}; declare_lint! { UNUSED_COMPARISONS, @@ -43,14 +43,14 @@ declare_lint! { #[derive(Copy, Clone)] pub struct TypeLimits { /// Id of the last visited negated expression - negated_expr_id: hir::HirId, + negated_expr_id: Option, } impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]); impl TypeLimits { pub fn new() -> TypeLimits { - TypeLimits { negated_expr_id: hir::DUMMY_HIR_ID } + TypeLimits { negated_expr_id: None } } } @@ -134,7 +134,7 @@ fn get_bin_hex_repr(cx: &LateContext<'_, '_>, lit: &hir::Lit) -> Option if firstch == '0' { match src.chars().nth(1) { - Some('x') | Some('b') => return Some(src), + Some('x' | 'b') => return Some(src), _ => return None, } } @@ -150,7 +150,7 @@ fn report_bin_hex_error( val: u128, negative: bool, ) { - let size = layout::Integer::from_attr(&cx.tcx, ty).size(); + let size = Integer::from_attr(&cx.tcx, ty).size(); cx.struct_span_lint(OVERFLOWING_LITERALS, expr.span, |lint| { let (t, actually) = match ty { attr::IntType::SignedInt(t) => { @@ -244,7 +244,7 @@ fn lint_int_literal<'a, 'tcx>( let int_type = t.normalize(cx.sess().target.ptr_width); let (min, max) = int_ty_range(int_type); let max = max as u128; - let negative = type_limits.negated_expr_id == e.hir_id; + let negative = type_limits.negated_expr_id == Some(e.hir_id); // Detect literal value out of range [min, max] inclusive // avoiding use of -min to prevent overflow/panic @@ -356,8 +356,7 @@ fn lint_literal<'a, 'tcx>( match cx.tables.node_type(e.hir_id).kind { ty::Int(t) => { match lit.node { - ast::LitKind::Int(v, ast::LitIntType::Signed(_)) - | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => { + ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { lint_int_literal(cx, type_limits, e, lit, t, v) } _ => bug!(), @@ -397,8 +396,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { match e.kind { hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => { // propagate negation, if the negation itself isn't negated - if self.negated_expr_id != e.hir_id { - self.negated_expr_id = expr.hir_id; + if self.negated_expr_id != Some(e.hir_id) { + self.negated_expr_id = Some(expr.hir_id); } } hir::ExprKind::Binary(binop, ref l, ref r) => { @@ -455,8 +454,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { let (min, max) = int_ty_range(int_ty); let lit_val: i128 = match lit.kind { hir::ExprKind::Lit(ref li) => match li.node { - ast::LitKind::Int(v, ast::LitIntType::Signed(_)) - | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i128, + ast::LitKind::Int( + v, + ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed, + ) => v as i128, _ => return true, }, _ => bug!(), @@ -887,7 +888,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { | ty::Generator(..) | ty::GeneratorWitness(..) | ty::Placeholder(..) - | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::Opaque(..) | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), @@ -919,7 +919,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - use crate::rustc::ty::TypeFoldable; + use rustc_middle::ty::TypeFoldable; struct ProhibitOpaqueTypes<'tcx> { ty: Option>, @@ -1030,12 +1030,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { let ty = cx.tcx.erase_regions(&t); let layout = match cx.layout_of(ty) { Ok(layout) => layout, - Err(ty::layout::LayoutError::Unknown(_)) - | Err(ty::layout::LayoutError::SizeOverflow(_)) => return, + Err( + ty::layout::LayoutError::Unknown(_) | ty::layout::LayoutError::SizeOverflow(_), + ) => return, }; let (variants, tag) = match layout.variants { - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, + Variants::Multiple { + discr_kind: DiscriminantKind::Tag, ref discr, ref variants, .. diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index b5826d6a5efa6..c24079a6e4be2 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -1,7 +1,7 @@ +use crate::Lint; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc::ty::adjustment; -use rustc::ty::{self, Ty}; use rustc_ast::ast; +use rustc_ast::ast::{ExprKind, StmtKind}; use rustc_ast::attr; use rustc_ast::util::parser; use rustc_ast_pretty::pprust; @@ -11,10 +11,12 @@ use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_middle::ty::adjustment; +use rustc_middle::ty::{self, Ty}; use rustc_session::lint::builtin::UNUSED_ATTRIBUTES; use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, sym}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, DUMMY_SP}; use log::debug; @@ -54,9 +56,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { match callee.kind { hir::ExprKind::Path(ref qpath) => { match cx.tables.qpath_res(qpath, callee.hir_id) { - Res::Def(DefKind::Fn, def_id) | Res::Def(DefKind::AssocFn, def_id) => { - Some(def_id) - } + Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), // `Res::Local` if it was a closure, for which we // do not currently support must-use linting _ => None, @@ -285,12 +285,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { let attr_info = attr.ident().and_then(|ident| self.builtin_attributes.get(&ident.name)); if let Some(&&(name, ty, ..)) = attr_info { - match ty { - AttributeType::Whitelisted => { - debug!("{:?} is Whitelisted", name); - return; - } - _ => (), + if let AttributeType::Whitelisted = ty { + debug!("{:?} is Whitelisted", name); + return; } } @@ -318,115 +315,135 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { } } -declare_lint! { - pub(super) UNUSED_PARENS, - Warn, - "`if`, `match`, `while` and `return` do not need parentheses" +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum UnusedDelimsCtx { + FunctionArg, + MethodArg, + AssignedValue, + IfCond, + WhileCond, + ForIterExpr, + MatchScrutineeExpr, + ReturnValue, + BlockRetValue, + LetScrutineeExpr, + ArrayLenExpr, + AnonConst, } -declare_lint_pass!(UnusedParens => [UNUSED_PARENS]); - -impl UnusedParens { - fn is_expr_parens_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool { - followed_by_block - && match inner.kind { - ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true, - _ => parser::contains_exterior_struct_lit(&inner), - } +impl From for &'static str { + fn from(ctx: UnusedDelimsCtx) -> &'static str { + match ctx { + UnusedDelimsCtx::FunctionArg => "function argument", + UnusedDelimsCtx::MethodArg => "method argument", + UnusedDelimsCtx::AssignedValue => "assigned value", + UnusedDelimsCtx::IfCond => "`if` condition", + UnusedDelimsCtx::WhileCond => "`while` condition", + UnusedDelimsCtx::ForIterExpr => "`for` iterator expression", + UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression", + UnusedDelimsCtx::ReturnValue => "`return` value", + UnusedDelimsCtx::BlockRetValue => "block return value", + UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression", + UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression", + } } +} - fn check_unused_parens_expr( +/// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication. +trait UnusedDelimLint { + const DELIM_STR: &'static str; + + /// Due to `ref` pattern, there can be a difference between using + /// `{ expr }` and `expr` in pattern-matching contexts. This means + /// that we should only lint `unused_parens` and not `unused_braces` + /// in this case. + /// + /// ```rust + /// let mut a = 7; + /// let ref b = { a }; // We actually borrow a copy of `a` here. + /// a += 1; // By mutating `a` we invalidate any borrows of `a`. + /// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here. + /// ``` + const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool; + + // this cannot be a constant is it refers to a static. + fn lint(&self) -> &'static Lint; + + fn check_unused_delims_expr( &self, cx: &EarlyContext<'_>, value: &ast::Expr, - msg: &str, + ctx: UnusedDelimsCtx, followed_by_block: bool, left_pos: Option, right_pos: Option, - ) { - match value.kind { - ast::ExprKind::Paren(ref inner) => { - if !Self::is_expr_parens_necessary(inner, followed_by_block) - && value.attrs.is_empty() - && !value.span.from_expansion() - { - let expr_text = - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { - snippet - } else { - pprust::expr_to_string(value) - }; - let keep_space = ( - left_pos.map(|s| s >= value.span.lo()).unwrap_or(false), - right_pos.map(|s| s <= value.span.hi()).unwrap_or(false), - ); - Self::remove_outer_parens(cx, value.span, &expr_text, msg, keep_space); + ); + + fn is_expr_delims_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool { + // Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }` + let lhs_needs_parens = { + let mut innermost = inner; + loop { + if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind { + innermost = lhs; + if !rustc_ast::util::classify::expr_requires_semi_to_be_stmt(innermost) { + break true; + } + } else { + break false; } } - ast::ExprKind::Let(_, ref expr) => { - // FIXME(#60336): Properly handle `let true = (false && true)` - // actually needing the parenthesis. - self.check_unused_parens_expr( - cx, - expr, - "`let` head expression", - followed_by_block, - None, - None, - ); - } - _ => {} - } + }; + + lhs_needs_parens + || (followed_by_block + && match inner.kind { + ExprKind::Ret(_) | ExprKind::Break(..) => true, + _ => parser::contains_exterior_struct_lit(&inner), + }) } - fn check_unused_parens_pat( + fn emit_unused_delims_expr( &self, cx: &EarlyContext<'_>, - value: &ast::Pat, - avoid_or: bool, - avoid_mut: bool, + value: &ast::Expr, + ctx: UnusedDelimsCtx, + left_pos: Option, + right_pos: Option, ) { - use ast::{BindingMode, Mutability, PatKind}; - - if let PatKind::Paren(inner) = &value.kind { - match inner.kind { - // The lint visitor will visit each subpattern of `p`. We do not want to lint - // any range pattern no matter where it occurs in the pattern. For something like - // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume - // that if there are unnecessary parens they serve a purpose of readability. - PatKind::Range(..) => return, - // Avoid `p0 | .. | pn` if we should. - PatKind::Or(..) if avoid_or => return, - // Avoid `mut x` and `mut x @ p` if we should: - PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ..) if avoid_mut => return, - // Otherwise proceed with linting. - _ => {} - } - - let pattern_text = - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { - snippet - } else { - pprust::pat_to_string(value) - }; - Self::remove_outer_parens(cx, value.span, &pattern_text, "pattern", (false, false)); - } + let expr_text = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { + snippet + } else { + pprust::expr_to_string(value) + }; + let keep_space = ( + left_pos.map(|s| s >= value.span.lo()).unwrap_or(false), + right_pos.map(|s| s <= value.span.hi()).unwrap_or(false), + ); + self.emit_unused_delims(cx, value.span, &expr_text, ctx.into(), keep_space); } - fn remove_outer_parens( + fn emit_unused_delims( + &self, cx: &EarlyContext<'_>, span: Span, pattern: &str, msg: &str, keep_space: (bool, bool), ) { - cx.struct_span_lint(UNUSED_PARENS, span, |lint| { - let span_msg = format!("unnecessary parentheses around {}", msg); + // FIXME(flip1995): Quick and dirty fix for #70814. This should be fixed in rustdoc + // properly. + if span == DUMMY_SP { + return; + } + + cx.struct_span_lint(self.lint(), span, |lint| { + let span_msg = format!("unnecessary {} around {}", Self::DELIM_STR, msg); let mut err = lint.build(&span_msg); let mut ate_left_paren = false; let mut ate_right_paren = false; let parens_removed = pattern.trim_matches(|c| match c { - '(' => { + '(' | '{' => { if ate_left_paren { false } else { @@ -434,7 +451,7 @@ impl UnusedParens { true } } - ')' => { + ')' | '}' => { if ate_right_paren { false } else { @@ -460,61 +477,54 @@ impl UnusedParens { replace }; - err.span_suggestion_short( - span, - "remove these parentheses", - replace, - Applicability::MachineApplicable, - ); + let suggestion = format!("remove these {}", Self::DELIM_STR); + + err.span_suggestion_short(span, &suggestion, replace, Applicability::MachineApplicable); err.emit(); }); } -} -impl EarlyLintPass for UnusedParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { use rustc_ast::ast::ExprKind::*; - let (value, msg, followed_by_block, left_pos, right_pos) = match e.kind { - Let(ref pat, ..) => { - self.check_unused_parens_pat(cx, pat, false, false); - return; - } - - If(ref cond, ref block, ..) => { + let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind { + // Do not lint `unused_braces` in `if let` expressions. + If(ref cond, ref block, ..) + if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => + { let left = e.span.lo() + rustc_span::BytePos(2); let right = block.span.lo(); - (cond, "`if` condition", true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right)) } While(ref cond, ref block, ..) => { let left = e.span.lo() + rustc_span::BytePos(5); let right = block.span.lo(); - (cond, "`while` condition", true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right)) } - ForLoop(ref pat, ref cond, ref block, ..) => { - self.check_unused_parens_pat(cx, pat, false, false); - (cond, "`for` head expression", true, None, Some(block.span.lo())) + ForLoop(_, ref cond, ref block, ..) => { + (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo())) } - Match(ref head, _) => { + Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { let left = e.span.lo() + rustc_span::BytePos(5); - (head, "`match` head expression", true, Some(left), None) + (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None) } Ret(Some(ref value)) => { let left = e.span.lo() + rustc_span::BytePos(3); - (value, "`return` value", false, Some(left), None) + (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None) } - Assign(_, ref value, _) => (value, "assigned value", false, None, None), - AssignOp(.., ref value) => (value, "assigned value", false, None, None), + Assign(_, ref value, _) | AssignOp(.., ref value) => { + (value, UnusedDelimsCtx::AssignedValue, false, None, None) + } // either function/method call, or something this lint doesn't care about ref call_or_other => { - let (args_to_check, call_kind) = match *call_or_other { - Call(_, ref args) => (&args[..], "function"), - // first "argument" is self (which sometimes needs parens) - MethodCall(_, ref args) => (&args[1..], "method"), + let (args_to_check, ctx) = match *call_or_other { + Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg), + // first "argument" is self (which sometimes needs delims) + MethodCall(_, ref args) => (&args[1..], UnusedDelimsCtx::MethodArg), // actual catch-all arm _ => { return; @@ -527,14 +537,154 @@ impl EarlyLintPass for UnusedParens { if e.span.ctxt().outer_expn_data().call_site.from_expansion() { return; } - let msg = format!("{} argument", call_kind); for arg in args_to_check { - self.check_unused_parens_expr(cx, arg, &msg, false, None, None); + self.check_unused_delims_expr(cx, arg, ctx, false, None, None); } return; } }; - self.check_unused_parens_expr(cx, &value, msg, followed_by_block, left_pos, right_pos); + self.check_unused_delims_expr(cx, &value, ctx, followed_by_block, left_pos, right_pos); + } + + fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { + match s.kind { + StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { + if let Some(ref value) = local.init { + self.check_unused_delims_expr( + cx, + &value, + UnusedDelimsCtx::AssignedValue, + false, + None, + None, + ); + } + } + StmtKind::Expr(ref expr) => { + self.check_unused_delims_expr( + cx, + &expr, + UnusedDelimsCtx::BlockRetValue, + false, + None, + None, + ); + } + _ => {} + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + use ast::ItemKind::*; + + if let Const(.., Some(expr)) | Static(.., Some(expr)) = &item.kind { + self.check_unused_delims_expr( + cx, + expr, + UnusedDelimsCtx::AssignedValue, + false, + None, + None, + ); + } + } +} + +declare_lint! { + pub(super) UNUSED_PARENS, + Warn, + "`if`, `match`, `while` and `return` do not need parentheses" +} + +declare_lint_pass!(UnusedParens => [UNUSED_PARENS]); + +impl UnusedDelimLint for UnusedParens { + const DELIM_STR: &'static str = "parentheses"; + + const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true; + + fn lint(&self) -> &'static Lint { + UNUSED_PARENS + } + + fn check_unused_delims_expr( + &self, + cx: &EarlyContext<'_>, + value: &ast::Expr, + ctx: UnusedDelimsCtx, + followed_by_block: bool, + left_pos: Option, + right_pos: Option, + ) { + match value.kind { + ast::ExprKind::Paren(ref inner) => { + if !Self::is_expr_delims_necessary(inner, followed_by_block) + && value.attrs.is_empty() + && !value.span.from_expansion() + { + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + } + } + ast::ExprKind::Let(_, ref expr) => { + // FIXME(#60336): Properly handle `let true = (false && true)` + // actually needing the parenthesis. + self.check_unused_delims_expr( + cx, + expr, + UnusedDelimsCtx::LetScrutineeExpr, + followed_by_block, + None, + None, + ); + } + _ => {} + } + } +} + +impl UnusedParens { + fn check_unused_parens_pat( + &self, + cx: &EarlyContext<'_>, + value: &ast::Pat, + avoid_or: bool, + avoid_mut: bool, + ) { + use ast::{BindingMode, Mutability, PatKind}; + + if let PatKind::Paren(inner) = &value.kind { + match inner.kind { + // The lint visitor will visit each subpattern of `p`. We do not want to lint + // any range pattern no matter where it occurs in the pattern. For something like + // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume + // that if there are unnecessary parens they serve a purpose of readability. + PatKind::Range(..) => return, + // Avoid `p0 | .. | pn` if we should. + PatKind::Or(..) if avoid_or => return, + // Avoid `mut x` and `mut x @ p` if we should: + PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ..) if avoid_mut => return, + // Otherwise proceed with linting. + _ => {} + } + + let pattern_text = + if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) { + snippet + } else { + pprust::pat_to_string(value) + }; + self.emit_unused_delims(cx, value.span, &pattern_text, "pattern", (false, false)); + } + } +} + +impl EarlyLintPass for UnusedParens { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ExprKind::Let(ref pat, ..) | ExprKind::ForLoop(ref pat, ..) = e.kind { + self.check_unused_parens_pat(cx, pat, false, false); + } + + ::check_expr(self, cx, e) } fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { @@ -559,22 +709,16 @@ impl EarlyLintPass for UnusedParens { } } - fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { - use ast::StmtKind::*; - - match s.kind { - Local(ref local) => { - self.check_unused_parens_pat(cx, &local.pat, false, false); + fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) { + self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None); + } - if let Some(ref value) = local.init { - self.check_unused_parens_expr(cx, &value, "assigned value", false, None, None); - } - } - Expr(ref expr) => { - self.check_unused_parens_expr(cx, &expr, "block return value", false, None, None); - } - _ => {} + fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { + if let StmtKind::Local(ref local) = s.kind { + self.check_unused_parens_pat(cx, &local.pat, false, false); } + + ::check_stmt(self, cx, s) } fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) { @@ -590,6 +734,16 @@ impl EarlyLintPass for UnusedParens { match &r.kind { &ast::TyKind::TraitObject(..) => {} &ast::TyKind::ImplTrait(_, ref bounds) if bounds.len() > 1 => {} + &ast::TyKind::Array(_, ref len) => { + self.check_unused_delims_expr( + cx, + &len.value, + UnusedDelimsCtx::ArrayLenExpr, + false, + None, + None, + ); + } _ => { let pattern_text = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(ty.span) { @@ -598,21 +752,138 @@ impl EarlyLintPass for UnusedParens { pprust::ty_to_string(ty) }; - Self::remove_outer_parens(cx, ty.span, &pattern_text, "type", (false, false)); + self.emit_unused_delims(cx, ty.span, &pattern_text, "type", (false, false)); } } } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - use ast::ItemKind::*; + ::check_item(self, cx, item) + } +} - if let Const(.., Some(expr)) | Static(.., Some(expr)) = &item.kind { - self.check_unused_parens_expr(cx, expr, "assigned value", false, None, None); +declare_lint! { + pub(super) UNUSED_BRACES, + Warn, + "unnecessary braces around an expression" +} + +declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]); + +impl UnusedDelimLint for UnusedBraces { + const DELIM_STR: &'static str = "braces"; + + const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false; + + fn lint(&self) -> &'static Lint { + UNUSED_BRACES + } + + fn check_unused_delims_expr( + &self, + cx: &EarlyContext<'_>, + value: &ast::Expr, + ctx: UnusedDelimsCtx, + followed_by_block: bool, + left_pos: Option, + right_pos: Option, + ) { + match value.kind { + ast::ExprKind::Block(ref inner, None) + if inner.rules == ast::BlockCheckMode::Default => + { + // emit a warning under the following conditions: + // + // - the block does not have a label + // - the block is not `unsafe` + // - the block contains exactly one expression (do not lint `{ expr; }`) + // - `followed_by_block` is true and the internal expr may contain a `{` + // - the block is not multiline (do not lint multiline match arms) + // ``` + // match expr { + // Pattern => { + // somewhat_long_expression + // } + // // ... + // } + // ``` + // - the block has no attribute and was not created inside a macro + // - if the block is an `anon_const`, the inner expr must be a literal + // (do not lint `struct A; let _: A<{ 2 + 3 }>;`) + // + // FIXME(const_generics): handle paths when #67075 is fixed. + if let [stmt] = inner.stmts.as_slice() { + if let ast::StmtKind::Expr(ref expr) = stmt.kind { + if !Self::is_expr_delims_necessary(expr, followed_by_block) + && (ctx != UnusedDelimsCtx::AnonConst + || matches!(expr.kind, ast::ExprKind::Lit(_))) + // array length expressions are checked during `check_anon_const` and `check_ty`, + // once as `ArrayLenExpr` and once as `AnonConst`. + // + // As we do not want to lint this twice, we do not emit an error for + // `ArrayLenExpr` if `AnonConst` would do the same. + && (ctx != UnusedDelimsCtx::ArrayLenExpr + || !matches!(expr.kind, ast::ExprKind::Lit(_))) + && !cx.sess().source_map().is_multiline(value.span) + && value.attrs.is_empty() + && !value.span.from_expansion() + { + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + } + } + } + } + ast::ExprKind::Let(_, ref expr) => { + // FIXME(#60336): Properly handle `let true = (false && true)` + // actually needing the parenthesis. + self.check_unused_delims_expr( + cx, + expr, + UnusedDelimsCtx::LetScrutineeExpr, + followed_by_block, + None, + None, + ); + } + _ => {} } } } +impl EarlyLintPass for UnusedBraces { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + ::check_expr(self, cx, e) + } + + fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) { + self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None); + } + + fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { + ::check_stmt(self, cx, s) + } + + fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { + if let &ast::TyKind::Paren(ref r) = &ty.kind { + if let ast::TyKind::Array(_, ref len) = r.kind { + self.check_unused_delims_expr( + cx, + &len.value, + UnusedDelimsCtx::ArrayLenExpr, + false, + None, + None, + ); + } + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + ::check_item(self, cx, item) + } +} + declare_lint! { UNUSED_IMPORT_BRACES, Allow, diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs index fcaeaf2e4b07d..e97fa4345fe7c 100644 --- a/src/librustc_llvm/build.rs +++ b/src/librustc_llvm/build.rs @@ -15,27 +15,37 @@ fn detect_llvm_link() -> (&'static str, &'static str) { } fn main() { + println!("cargo:rerun-if-env-changed=RUST_CHECK"); if env::var_os("RUST_CHECK").is_some() { // If we're just running `check`, there's no need for LLVM to be built. - println!("cargo:rerun-if-env-changed=RUST_CHECK"); return; } build_helper::restore_library_path(); let target = env::var("TARGET").expect("TARGET was not set"); - let llvm_config = env::var_os("LLVM_CONFIG").map(PathBuf::from).unwrap_or_else(|| { - if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) { - let to_test = - dir.parent().unwrap().parent().unwrap().join(&target).join("llvm/bin/llvm-config"); - if Command::new(&to_test).output().is_ok() { - return to_test; + let llvm_config = + env::var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { + if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) { + let to_test = dir + .parent() + .unwrap() + .parent() + .unwrap() + .join(&target) + .join("llvm/bin/llvm-config"); + if Command::new(&to_test).output().is_ok() { + return Some(to_test); + } } - } - PathBuf::from("llvm-config") - }); + None + }); + + if let Some(llvm_config) = &llvm_config { + println!("cargo:rerun-if-changed={}", llvm_config.display()); + } + let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); - println!("cargo:rerun-if-changed={}", llvm_config.display()); println!("cargo:rerun-if-env-changed=LLVM_CONFIG"); // Test whether we're cross-compiling LLVM. This is a pretty rare case diff --git a/src/librustc_macros/src/hash_stable.rs b/src/librustc_macros/src/hash_stable.rs index dc6ae961e5a6e..c955c13778276 100644 --- a/src/librustc_macros/src/hash_stable.rs +++ b/src/librustc_macros/src/hash_stable.rs @@ -115,13 +115,13 @@ pub fn hash_stable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::To s.bound_impl( quote!( ::rustc_data_structures::stable_hasher::HashStable< - ::rustc::ich::StableHashingContext<'__ctx>, + ::rustc_middle::ich::StableHashingContext<'__ctx>, > ), quote! { fn hash_stable( &self, - __hcx: &mut ::rustc::ich::StableHashingContext<'__ctx>, + __hcx: &mut ::rustc_middle::ich::StableHashingContext<'__ctx>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { #discriminant match *self { #body } diff --git a/src/librustc_macros/src/lift.rs b/src/librustc_macros/src/lift.rs index a246b34b2c295..4bf4ce00a4d38 100644 --- a/src/librustc_macros/src/lift.rs +++ b/src/librustc_macros/src/lift.rs @@ -39,11 +39,11 @@ pub fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStre s.add_impl_generic(newtcx); s.bound_impl( - quote!(::rustc::ty::Lift<'__lifted>), + quote!(::rustc_middle::ty::Lift<'__lifted>), quote! { type Lifted = #lifted; - fn lift_to_tcx(&self, __tcx: ::rustc::ty::TyCtxt<'__lifted>) -> Option<#lifted> { + fn lift_to_tcx(&self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> { Some(match *self { #body }) } }, diff --git a/src/librustc_macros/src/query.rs b/src/librustc_macros/src/query.rs index e7005f2f5ba77..5f5bae66cfc65 100644 --- a/src/librustc_macros/src/query.rs +++ b/src/librustc_macros/src/query.rs @@ -107,7 +107,9 @@ impl Parse for QueryModifier { let block = input.parse()?; Ok(QueryModifier::LoadCached(tcx, id, block)) } else if modifier == "storage" { - let ty = input.parse()?; + let args; + parenthesized!(args in input); + let ty = args.parse()?; Ok(QueryModifier::Storage(ty)) } else if modifier == "fatal_cycle" { Ok(QueryModifier::FatalCycle) @@ -356,12 +358,14 @@ fn add_query_description_impl( quote! { #t } }) .unwrap_or(quote! { _ }); + // expr is a `Block`, meaning that `{ #expr }` gets expanded + // to `{ { stmts... } }`, which triggers the `unused_braces` lint. quote! { #[inline] - #[allow(unused_variables)] + #[allow(unused_variables, unused_braces)] fn cache_on_disk( #tcx: TyCtxt<'tcx>, - #key: Self::Key, + #key: &Self::Key, #value: Option<&Self::Value> ) -> bool { #expr @@ -380,7 +384,7 @@ fn add_query_description_impl( quote! { #[allow(unused_variables)] fn describe( - #tcx: TyCtxt<'_>, + #tcx: TyCtxt<'tcx>, #key: #arg, ) -> Cow<'static, str> { format!(#desc).into() @@ -393,7 +397,7 @@ fn add_query_description_impl( let desc = desc.unwrap_or(quote! {}); impls.extend(quote! { - impl<'tcx> QueryDescription<'tcx> for queries::#name<'tcx> { + impl<'tcx> QueryDescription> for queries::#name<'tcx> { #desc #cache } @@ -429,7 +433,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { }); try_load_from_on_disk_cache_stream.extend(quote! { - ::rustc::dep_graph::DepKind::#name => { + ::rustc_middle::dep_graph::DepKind::#name => { if <#arg as DepNodeParams>>::CAN_RECONSTRUCT_QUERY_KEY { debug_assert!($tcx.dep_graph .node_color($dep_node) @@ -437,7 +441,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { .unwrap_or(false)); let key = <#arg as DepNodeParams>>::recover($tcx, $dep_node).unwrap(); - if queries::#name::cache_on_disk($tcx, key, None) { + if queries::#name::cache_on_disk($tcx, &key, None) { let _ = $tcx.#name(key); } } @@ -486,10 +490,11 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { // Add a match arm to force the query given the dep node dep_node_force_stream.extend(quote! { - ::rustc::dep_graph::DepKind::#name => { + ::rustc_middle::dep_graph::DepKind::#name => { if <#arg as DepNodeParams>>::CAN_RECONSTRUCT_QUERY_KEY { if let Some(key) = <#arg as DepNodeParams>>::recover($tcx, $dep_node) { - $tcx.force_query::>( + force_query::, _>( + $tcx, key, DUMMY_SP, *$dep_node @@ -509,7 +514,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { } dep_node_force_stream.extend(quote! { - ::rustc::dep_graph::DepKind::Null => { + ::rustc_middle::dep_graph::DepKind::Null => { bug!("Cannot force dep node: {:?}", $dep_node) } }); diff --git a/src/librustc_macros/src/type_foldable.rs b/src/librustc_macros/src/type_foldable.rs index 687401e33449b..6931e6552add2 100644 --- a/src/librustc_macros/src/type_foldable.rs +++ b/src/librustc_macros/src/type_foldable.rs @@ -11,25 +11,25 @@ pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:: vi.construct(|_, index| { let bind = &bindings[index]; quote! { - ::rustc::ty::fold::TypeFoldable::fold_with(#bind, __folder) + ::rustc_middle::ty::fold::TypeFoldable::fold_with(#bind, __folder) } }) }); let body_visit = s.fold(false, |acc, bind| { - quote! { #acc || ::rustc::ty::fold::TypeFoldable::visit_with(#bind, __folder) } + quote! { #acc || ::rustc_middle::ty::fold::TypeFoldable::visit_with(#bind, __folder) } }); s.bound_impl( - quote!(::rustc::ty::fold::TypeFoldable<'tcx>), + quote!(::rustc_middle::ty::fold::TypeFoldable<'tcx>), quote! { - fn super_fold_with<__F: ::rustc::ty::fold::TypeFolder<'tcx>>( + fn super_fold_with<__F: ::rustc_middle::ty::fold::TypeFolder<'tcx>>( &self, __folder: &mut __F ) -> Self { match *self { #body_fold } } - fn super_visit_with<__F: ::rustc::ty::fold::TypeVisitor<'tcx>>( + fn super_visit_with<__F: ::rustc_middle::ty::fold::TypeVisitor<'tcx>>( &self, __folder: &mut __F ) -> bool { diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml index 088cba83ef998..b03e884cdaf7a 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -11,14 +11,16 @@ doctest = false [dependencies] flate2 = "1.0" +libc = "0.2" log = "0.4" memmap = "0.7" smallvec = { version = "1.0", features = ["union", "may_dangle"] } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_attr = { path = "../librustc_attr" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_hir = { path = "../librustc_hir" } +rustc_hir_pretty = { path = "../librustc_hir_pretty" } rustc_target = { path = "../librustc_target" } rustc_index = { path = "../librustc_index" } rustc_serialize = { path = "../libserialize", package = "serialize" } diff --git a/src/librustc_metadata/build.rs b/src/librustc_metadata/build.rs index d230ba91039ad..7d5c58ecea2a1 100644 --- a/src/librustc_metadata/build.rs +++ b/src/librustc_metadata/build.rs @@ -1,4 +1,5 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=CFG_VERSION"); + println!("cargo:rerun-if-env-changed=CFG_VIRTUAL_RUST_SOURCE_BASE_DIR"); } diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 1f551583b0c86..a9e7a9f35dc36 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -3,10 +3,6 @@ use crate::locator::{CrateLocator, CratePaths}; use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; -use rustc::hir::map::Definitions; -use rustc::middle::cstore::DepKind; -use rustc::middle::cstore::{CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn}; -use rustc::ty::TyCtxt; use rustc_ast::expand::allocator::{global_allocator_spans, AllocatorKind}; use rustc_ast::{ast, attr}; use rustc_data_structures::svh::Svh; @@ -14,8 +10,14 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; use rustc_expand::base::SyntaxExtension; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::definitions::Definitions; use rustc_index::vec::IndexVec; -use rustc_session::config; +use rustc_middle::middle::cstore::DepKind; +use rustc_middle::middle::cstore::{ + CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn, +}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{self, CrateType}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; use rustc_session::{CrateDisambiguator, Session}; @@ -99,9 +101,15 @@ fn dump_crates(cstore: &CStore) { info!(" hash: {}", data.hash()); info!(" reqd: {:?}", data.dep_kind()); let CrateSource { dylib, rlib, rmeta } = data.source(); - dylib.as_ref().map(|dl| info!(" dylib: {}", dl.0.display())); - rlib.as_ref().map(|rl| info!(" rlib: {}", rl.0.display())); - rmeta.as_ref().map(|rl| info!(" rmeta: {}", rl.0.display())); + if let Some(dylib) = dylib { + info!(" dylib: {}", dylib.0.display()); + } + if let Some(rlib) = rlib { + info!(" rlib: {}", rlib.0.display()); + } + if let Some(rmeta) = rmeta { + info!(" rmeta: {}", rmeta.0.display()); + } }); } @@ -583,7 +591,7 @@ impl<'a> CrateLoader<'a> { // Make sure the path contains a / or the linker will search for it. let path = env::current_dir().unwrap().join(path); - let lib = match DynamicLibrary::open(Some(&path)) { + let lib = match DynamicLibrary::open(&path) { Ok(lib) => lib, Err(err) => self.sess.span_fatal(span, &err), }; @@ -607,8 +615,7 @@ impl<'a> CrateLoader<'a> { fn inject_panic_runtime(&mut self, krate: &ast::Crate) { // If we're only compiling an rlib, then there's no need to select a // panic runtime, so we just skip this section entirely. - let any_non_rlib = - self.sess.crate_types.borrow().iter().any(|ct| *ct != config::CrateType::Rlib); + let any_non_rlib = self.sess.crate_types.borrow().iter().any(|ct| *ct != CrateType::Rlib); if !any_non_rlib { info!("panic runtime injection skipped, only generating rlib"); return; @@ -684,7 +691,9 @@ impl<'a> CrateLoader<'a> { } fn inject_profiler_runtime(&mut self) { - if self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled() { + if (self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled()) + && !self.sess.opts.debugging_opts.no_profiler_runtime + { info!("loading profiler"); let name = Symbol::intern("profiler_builtins"); @@ -726,7 +735,7 @@ impl<'a> CrateLoader<'a> { // if our compilation session actually needs an allocator based on what // we're emitting. let all_rlib = self.sess.crate_types.borrow().iter().all(|ct| match *ct { - config::CrateType::Rlib => true, + CrateType::Rlib => true, _ => false, }); if all_rlib { diff --git a/src/librustc_metadata/dependency_format.rs b/src/librustc_metadata/dependency_format.rs index 4cfaf03b7a5f6..0876cd1e63835 100644 --- a/src/librustc_metadata/dependency_format.rs +++ b/src/librustc_metadata/dependency_format.rs @@ -53,13 +53,13 @@ use crate::creader::CStore; -use rustc::middle::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; -use rustc::middle::cstore::{self, DepKind}; -use rustc::middle::dependency_format::{Dependencies, DependencyList, Linkage}; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::CrateNum; -use rustc_session::config; +use rustc_middle::middle::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; +use rustc_middle::middle::cstore::{self, DepKind}; +use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::CrateType; use rustc_target::spec::PanicStrategy; crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { @@ -75,7 +75,7 @@ crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { .collect::>() } -fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList { +fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let sess = &tcx.sess; if !sess.opts.output_types.should_codegen() { @@ -90,29 +90,25 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList { // Treat cdylibs similarly. If `-C prefer-dynamic` is set, the caller may // be code-size conscious, but without it, it makes sense to statically // link a cdylib. - config::CrateType::Dylib | config::CrateType::Cdylib if !sess.opts.cg.prefer_dynamic => { - Linkage::Static - } - config::CrateType::Dylib | config::CrateType::Cdylib => Linkage::Dynamic, + CrateType::Dylib | CrateType::Cdylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, + CrateType::Dylib | CrateType::Cdylib => Linkage::Dynamic, // If the global prefer_dynamic switch is turned off, or the final // executable will be statically linked, prefer static crate linkage. - config::CrateType::Executable - if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => - { + CrateType::Executable if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => { Linkage::Static } - config::CrateType::Executable => Linkage::Dynamic, + CrateType::Executable => Linkage::Dynamic, // proc-macro crates are mostly cdylibs, but we also need metadata. - config::CrateType::ProcMacro => Linkage::Static, + 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. - config::CrateType::Rlib => Linkage::NotLinked, + CrateType::Rlib => Linkage::NotLinked, // staticlibs must have all static dependencies. - config::CrateType::Staticlib => Linkage::Static, + CrateType::Staticlib => Linkage::Static, }; if preferred_linkage == Linkage::NotLinked { @@ -129,8 +125,8 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList { // Staticlibs and static executables must have all static dependencies. // If any are not found, generate some nice pretty errors. - if ty == config::CrateType::Staticlib - || (ty == config::CrateType::Executable + if ty == CrateType::Staticlib + || (ty == CrateType::Executable && sess.crt_static(Some(ty)) && !sess.target.target.options.crt_static_allows_dylibs) { diff --git a/src/librustc_metadata/dynamic_lib.rs b/src/librustc_metadata/dynamic_lib.rs index 3e78a5852354f..ce19240a009d0 100644 --- a/src/librustc_metadata/dynamic_lib.rs +++ b/src/librustc_metadata/dynamic_lib.rs @@ -16,10 +16,9 @@ impl Drop for DynamicLibrary { } impl DynamicLibrary { - /// Lazily open a dynamic library. When passed None it gives a - /// handle to the calling process - pub fn open(filename: Option<&Path>) -> Result { - let maybe_library = dl::open(filename.map(|path| path.as_os_str())); + /// Lazily open a dynamic library. + pub fn open(filename: &Path) -> Result { + let maybe_library = dl::open(filename.as_os_str()); // The dynamic library must not be constructed if there is // an error opening the library so the destructor does not @@ -57,24 +56,13 @@ mod dl { use std::ptr; use std::str; - pub(super) fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> { + pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> { check_for_errors_in(|| unsafe { - match filename { - Some(filename) => open_external(filename), - None => open_internal(), - } + let s = CString::new(filename.as_bytes()).unwrap(); + libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8 }) } - unsafe fn open_external(filename: &OsStr) -> *mut u8 { - let s = CString::new(filename.as_bytes()).unwrap(); - libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8 - } - - unsafe fn open_internal() -> *mut u8 { - libc::dlopen(ptr::null(), libc::RTLD_LAZY) as *mut u8 - } - fn check_for_errors_in(f: F) -> Result where F: FnOnce() -> T, @@ -124,10 +112,10 @@ mod dl { use winapi::shared::minwindef::HMODULE; use winapi::um::errhandlingapi::SetThreadErrorMode; - use winapi::um::libloaderapi::{FreeLibrary, GetModuleHandleExW, GetProcAddress, LoadLibraryW}; + use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryW}; use winapi::um::winbase::SEM_FAILCRITICALERRORS; - pub(super) fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> { + pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> { // disable "dll load failed" error dialog. let prev_error_mode = unsafe { let new_error_mode = SEM_FAILCRITICALERRORS; @@ -139,22 +127,9 @@ mod dl { prev_error_mode }; - let result = match filename { - Some(filename) => { - let filename_str: Vec<_> = filename.encode_wide().chain(Some(0)).collect(); - let result = unsafe { LoadLibraryW(filename_str.as_ptr()) } as *mut u8; - ptr_result(result) - } - None => { - let mut handle = ptr::null_mut(); - let succeeded = unsafe { GetModuleHandleExW(0, ptr::null(), &mut handle) }; - if succeeded == 0 { - Err(io::Error::last_os_error().to_string()) - } else { - Ok(handle as *mut u8) - } - } - }; + let filename_str: Vec<_> = filename.encode_wide().chain(Some(0)).collect(); + let result = unsafe { LoadLibraryW(filename_str.as_ptr()) } as *mut u8; + let result = ptr_result(result); unsafe { SetThreadErrorMode(prev_error_mode, ptr::null_mut()); diff --git a/src/librustc_metadata/dynamic_lib/tests.rs b/src/librustc_metadata/dynamic_lib/tests.rs index cbf2b181e3c2a..7090bbf61c794 100644 --- a/src/librustc_metadata/dynamic_lib/tests.rs +++ b/src/librustc_metadata/dynamic_lib/tests.rs @@ -1,32 +1,4 @@ use super::*; -use std::mem; - -#[test] -fn test_loading_atoi() { - if cfg!(windows) { - return; - } - - // The C library does not need to be loaded since it is already linked in - let lib = match DynamicLibrary::open(None) { - Err(error) => panic!("Could not load self as module: {}", error), - Ok(lib) => lib, - }; - - let atoi: extern "C" fn(*const libc::c_char) -> libc::c_int = unsafe { - match lib.symbol("atoi") { - Err(error) => panic!("Could not load function atoi: {}", error), - Ok(atoi) => mem::transmute::<*mut u8, _>(atoi), - } - }; - - let argument = CString::new("1383428980").unwrap(); - let expected_result = 0x52757374; - let result = atoi(argument.as_ptr()); - if result != expected_result { - panic!("atoi({:?}) != {} but equaled {} instead", argument, expected_result, result) - } -} #[test] fn test_errors_do_not_crash() { @@ -39,7 +11,7 @@ fn test_errors_do_not_crash() { // Open /dev/null as a library to get an error, and make sure // that only causes an error, and not a crash. let path = Path::new("/dev/null"); - match DynamicLibrary::open(Some(&path)) { + match DynamicLibrary::open(&path) { Err(_) => {} Ok(_) => panic!("Successfully opened the empty library."), } diff --git a/src/librustc_metadata/foreign_modules.rs b/src/librustc_metadata/foreign_modules.rs index 60b8239a82155..8675197656a48 100644 --- a/src/librustc_metadata/foreign_modules.rs +++ b/src/librustc_metadata/foreign_modules.rs @@ -1,7 +1,7 @@ -use rustc::middle::cstore::ForeignModule; -use rustc::ty::TyCtxt; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::middle::cstore::ForeignModule; +use rustc_middle::ty::TyCtxt; crate fn collect(tcx: TyCtxt<'_>) -> Vec { let mut collector = Collector { tcx, modules: Vec::new() }; @@ -22,9 +22,11 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { }; let foreign_items = - fm.items.iter().map(|it| self.tcx.hir().local_def_id(it.hir_id)).collect(); - self.modules - .push(ForeignModule { foreign_items, def_id: self.tcx.hir().local_def_id(it.hir_id) }); + fm.items.iter().map(|it| self.tcx.hir().local_def_id(it.hir_id).to_def_id()).collect(); + self.modules.push(ForeignModule { + foreign_items, + def_id: self.tcx.hir().local_def_id(it.hir_id).to_def_id(), + }); } fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem<'tcx>) {} diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 2993aed2f8ab4..2a2169880a54e 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -5,16 +5,16 @@ #![feature(drain_filter)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![feature(proc_macro_internals)] -#![feature(specialization)] +#![feature(specialization)] // FIXME: min_specialization ICEs #![feature(stmt_expr_attributes)] #![recursion_limit = "256"] -extern crate libc; extern crate proc_macro; #[macro_use] -extern crate rustc; +extern crate rustc_middle; #[macro_use] extern crate rustc_data_structures; diff --git a/src/librustc_metadata/link_args.rs b/src/librustc_metadata/link_args.rs index 56b26efe5bf1e..2dd4a9c9dbcb1 100644 --- a/src/librustc_metadata/link_args.rs +++ b/src/librustc_metadata/link_args.rs @@ -1,6 +1,6 @@ -use rustc::ty::TyCtxt; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index c1a95c094b10c..f78f3c5d8d40f 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -215,14 +215,15 @@ use crate::creader::Library; use crate::rmeta::{rustc_version, MetadataBlob, METADATA_HEADER}; -use rustc::middle::cstore::{CrateSource, MetadataLoader}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; use rustc_errors::{struct_span_err, DiagnosticBuilder}; +use rustc_middle::middle::cstore::{CrateSource, MetadataLoader}; +use rustc_session::config::{self, CrateType}; use rustc_session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; use rustc_session::search_paths::PathKind; -use rustc_session::{config, CrateDisambiguator, Session}; +use rustc_session::{CrateDisambiguator, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::{Target, TargetTriple}; @@ -543,8 +544,8 @@ impl<'a> CrateLocator<'a> { // of the crate id (path/name/id). // // The goal of this step is to look at as little metadata as possible. - self.filesearch.search(|path, kind| { - let file = match path.file_name().and_then(|s| s.to_str()) { + self.filesearch.search(|spf, kind| { + let file = match &spf.file_name_str { None => return FileDoesntMatch, Some(file) => file, }; @@ -556,20 +557,18 @@ impl<'a> CrateLocator<'a> { (&file[(dylib_prefix.len())..(file.len() - dypair.1.len())], CrateFlavor::Dylib) } else { if file.starts_with(&staticlib_prefix) && file.ends_with(&staticpair.1) { - staticlibs.push(CrateMismatch { - path: path.to_path_buf(), - got: "static".to_string(), - }); + staticlibs + .push(CrateMismatch { path: spf.path.clone(), got: "static".to_string() }); } return FileDoesntMatch; }; - info!("lib candidate: {}", path.display()); + info!("lib candidate: {}", spf.path.display()); let hash_str = hash.to_string(); let slot = candidates.entry(hash_str).or_default(); let (ref mut rlibs, ref mut rmetas, ref mut dylibs) = *slot; - fs::canonicalize(path) + fs::canonicalize(&spf.path) .map(|p| { if seen_paths.contains(&p) { return FileDoesntMatch; @@ -671,7 +670,7 @@ impl<'a> CrateLocator<'a> { // The all loop is because `--crate-type=rlib --crate-type=rlib` is // legal and produces both inside this type. - let is_rlib = self.sess.crate_types.borrow().iter().all(|c| *c == config::CrateType::Rlib); + let is_rlib = self.sess.crate_types.borrow().iter().all(|c| *c == CrateType::Rlib); let needs_object_code = self.sess.opts.output_types.should_codegen(); // If we're producing an rlib, then we don't need object code. // Or, if we're not producing object code, then we don't need it either diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index 19d2d620f58a7..51c9950a5dfed 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -1,10 +1,10 @@ -use rustc::middle::cstore::{self, NativeLibrary}; -use rustc::ty::TyCtxt; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::middle::cstore::{self, NativeLibrary}; +use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::source_map::Span; @@ -51,7 +51,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { name: None, kind: cstore::NativeUnknown, cfg: None, - foreign_module: Some(self.tcx.hir().local_def_id(it.hir_id)), + foreign_module: Some(self.tcx.hir().local_def_id(it.hir_id).to_def_id()), wasm_import_module: None, }; let mut kind_specified = false; @@ -162,8 +162,13 @@ impl Collector<'tcx> { } } if lib.cfg.is_some() && !self.tcx.features().link_cfg { - feature_err(&self.tcx.sess.parse_sess, sym::link_cfg, span.unwrap(), "is unstable") - .emit(); + feature_err( + &self.tcx.sess.parse_sess, + sym::link_cfg, + span.unwrap(), + "kind=\"link_cfg\" is unstable", + ) + .emit(); } if lib.kind == cstore::NativeStaticNobundle && !self.tcx.features().static_nobundle { feature_err( diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 4520df588996e..32149c0afd597 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -4,18 +4,7 @@ use crate::creader::CrateMetadataRef; use crate::rmeta::table::{FixedSizeEncoding, Table}; use crate::rmeta::*; -use rustc::dep_graph::{self, DepNode, DepNodeExt, DepNodeIndex}; -use rustc::hir::exports::Export; -use rustc::middle::cstore::{CrateSource, ExternCrate}; -use rustc::middle::cstore::{ForeignModule, LinkagePreference, NativeLibrary}; -use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; -use rustc::middle::lang_items; -use rustc::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use rustc::mir::{self, interpret, BodyAndCache, Promoted}; -use rustc::ty::codec::TyDecoder; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::util::common::record_time; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_attr as attr; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; @@ -29,11 +18,22 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::DefPathTable; use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; +use rustc_hir::lang_items; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::dep_graph::{self, DepNode, DepNodeExt, DepNodeIndex}; +use rustc_middle::hir::exports::Export; +use rustc_middle::middle::cstore::{CrateSource, ExternCrate}; +use rustc_middle::middle::cstore::{ForeignModule, LinkagePreference, NativeLibrary}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; +use rustc_middle::mir::{self, interpret, Body, Promoted}; +use rustc_middle::ty::codec::TyDecoder; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::util::common::record_time; use rustc_serialize::{opaque, Decodable, Decoder, SpecializedDecoder}; use rustc_session::Session; -use rustc_span::source_map::{self, respan, Spanned}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::source_map::{respan, Spanned}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{self, hygiene::MacroKind, BytePos, Pos, Span, DUMMY_SP}; use log::debug; @@ -41,7 +41,7 @@ use proc_macro::bridge::client::ProcMacro; use std::io; use std::mem; use std::num::NonZeroUsize; -use std::u32; +use std::path::Path; pub use cstore_impl::{provide, provide_extern}; @@ -427,7 +427,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { // we can call `imported_source_files` for the proper crate, and binary search // through the returned slice using our span. let imported_source_files = if tag == TAG_VALID_SPAN_LOCAL { - self.cdata().imported_source_files(sess.source_map()) + self.cdata().imported_source_files(sess) } else { // FIXME: We don't decode dependencies of proc-macros. // Remove this once #69976 is merged @@ -457,7 +457,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { self.last_source_file_index = 0; let foreign_data = self.cdata().cstore.get_crate_data(cnum); - foreign_data.imported_source_files(sess.source_map()) + foreign_data.imported_source_files(sess) }; let source_file = { @@ -562,8 +562,8 @@ impl MetadataBlob { } impl EntryKind { - fn def_kind(&self) -> Option { - Some(match *self { + fn def_kind(&self) -> DefKind { + match *self { EntryKind::Const(..) => DefKind::Const, EntryKind::AssocConst(..) => DefKind::AssocConst, EntryKind::ImmStatic @@ -587,14 +587,13 @@ impl EntryKind { EntryKind::Enum(..) => DefKind::Enum, EntryKind::MacroDef(_) => DefKind::Macro(MacroKind::Bang), EntryKind::ForeignType => DefKind::ForeignTy, - - EntryKind::ForeignMod - | EntryKind::GlobalAsm - | EntryKind::Impl(_) - | EntryKind::Field - | EntryKind::Generator(_) - | EntryKind::Closure => return None, - }) + EntryKind::Impl(_) => DefKind::Impl, + EntryKind::Closure => DefKind::Closure, + EntryKind::ForeignMod => DefKind::ForeignMod, + EntryKind::GlobalAsm => DefKind::GlobalAsm, + EntryKind::Field => DefKind::Field, + EntryKind::Generator(_) => DefKind::Generator, + } } } @@ -633,7 +632,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn maybe_kind(&self, item_id: DefIndex) -> Option { - self.root.per_def.kind.get(self, item_id).map(|k| k.decode(self)) + self.root.tables.kind.get(self, item_id).map(|k| k.decode(self)) } fn kind(&self, item_id: DefIndex) -> EntryKind { @@ -665,7 +664,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .expect("no name in item_ident"); let span = self .root - .per_def + .tables .ident_span .get(self, item_index) .map(|data| data.decode((self, sess))) @@ -679,16 +678,16 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn def_kind(&self, index: DefIndex) -> Option { + fn def_kind(&self, index: DefIndex) -> DefKind { if !self.is_proc_macro(index) { self.kind(index).def_kind() } else { - Some(DefKind::Macro(macro_kind(self.raw_proc_macro(index)))) + DefKind::Macro(macro_kind(self.raw_proc_macro(index))) } } fn get_span(&self, index: DefIndex, sess: &Session) -> Span { - self.root.per_def.span.get(self, index).unwrap().decode((self, sess)) + self.root.tables.span.get(self, index).unwrap().decode((self, sess)) } fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension { @@ -781,7 +780,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ctor_did, data.discr, self.root - .per_def + .tables .children .get(self, index) .unwrap_or(Lazy::empty()) @@ -812,7 +811,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let variants = if let ty::AdtKind::Enum = adt_kind { self.root - .per_def + .tables .children .get(self, item_id) .unwrap_or(Lazy::empty()) @@ -831,7 +830,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> ty::GenericPredicates<'tcx> { - self.root.per_def.explicit_predicates.get(self, item_id).unwrap().decode((self, tcx)) + self.root.tables.explicit_predicates.get(self, item_id).unwrap().decode((self, tcx)) } fn get_inferred_outlives( @@ -840,7 +839,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { tcx: TyCtxt<'tcx>, ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { self.root - .per_def + .tables .inferred_outlives .get(self, item_id) .map(|predicates| predicates.decode((self, tcx))) @@ -852,31 +851,31 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> ty::GenericPredicates<'tcx> { - self.root.per_def.super_predicates.get(self, item_id).unwrap().decode((self, tcx)) + self.root.tables.super_predicates.get(self, item_id).unwrap().decode((self, tcx)) } fn get_generics(&self, item_id: DefIndex, sess: &Session) -> ty::Generics { - self.root.per_def.generics.get(self, item_id).unwrap().decode((self, sess)) + self.root.tables.generics.get(self, item_id).unwrap().decode((self, sess)) } fn get_type(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - self.root.per_def.ty.get(self, id).unwrap().decode((self, tcx)) + self.root.tables.ty.get(self, id).unwrap().decode((self, tcx)) } fn get_stability(&self, id: DefIndex) -> Option { match self.is_proc_macro(id) { true => self.root.proc_macro_stability, - false => self.root.per_def.stability.get(self, id).map(|stab| stab.decode(self)), + false => self.root.tables.stability.get(self, id).map(|stab| stab.decode(self)), } } fn get_const_stability(&self, id: DefIndex) -> Option { - self.root.per_def.const_stability.get(self, id).map(|stab| stab.decode(self)) + self.root.tables.const_stability.get(self, id).map(|stab| stab.decode(self)) } fn get_deprecation(&self, id: DefIndex) -> Option { self.root - .per_def + .tables .deprecation .get(self, id) .filter(|_| !self.is_proc_macro(id)) @@ -886,7 +885,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { fn get_visibility(&self, id: DefIndex) -> ty::Visibility { match self.is_proc_macro(id) { true => ty::Visibility::Public, - false => self.root.per_def.visibility.get(self, id).unwrap().decode(self), + false => self.root.tables.visibility.get(self, id).unwrap().decode(self), } } @@ -914,11 +913,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_impl_trait(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Option> { - self.root.per_def.impl_trait_ref.get(self, id).map(|tr| tr.decode((self, tcx))) + self.root.tables.impl_trait_ref.get(self, id).map(|tr| tr.decode((self, tcx))) } /// Iterates over all the stability attributes in the given crate. - fn get_lib_features(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(ast::Name, Option)] { + fn get_lib_features(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option)] { // FIXME: For a proc macro crate, not sure whether we should return the "host" // features or an empty Vec. Both don't cause ICEs. tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) @@ -940,8 +939,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } /// Iterates over the diagnostic items in the given crate. - fn get_diagnostic_items(&self, tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap { - tcx.arena.alloc(if self.root.is_proc_macro_crate() { + fn get_diagnostic_items(&self) -> FxHashMap { + if self.root.is_proc_macro_crate() { // Proc macro crates do not export any diagnostic-items to the target. Default::default() } else { @@ -950,7 +949,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode(self) .map(|(name, def_index)| (name, self.local_def_id(def_index))) .collect() - }) + } } /// Iterates over each child of the given item. @@ -984,7 +983,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // Iterate over all children. let macros_only = self.dep_kind.lock().macros_only(); - let children = self.root.per_def.children.get(self, id).unwrap_or(Lazy::empty()); + let children = self.root.tables.children.get(self, id).unwrap_or(Lazy::empty()); for child_index in children.decode((self, sess)) { if macros_only { continue; @@ -1004,25 +1003,24 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { EntryKind::ForeignMod => { let child_children = self .root - .per_def + .tables .children .get(self, child_index) .unwrap_or(Lazy::empty()); for child_index in child_children.decode((self, sess)) { - if let Some(kind) = self.def_kind(child_index) { - callback(Export { - res: Res::Def(kind, self.local_def_id(child_index)), - ident: self.item_ident(child_index, sess), - vis: self.get_visibility(child_index), - span: self - .root - .per_def - .span - .get(self, child_index) - .unwrap() - .decode((self, sess)), - }); - } + let kind = self.def_kind(child_index); + callback(Export { + res: Res::Def(kind, self.local_def_id(child_index)), + ident: self.item_ident(child_index, sess), + vis: self.get_visibility(child_index), + span: self + .root + .tables + .span + .get(self, child_index) + .unwrap() + .decode((self, sess)), + }); } continue; } @@ -1033,10 +1031,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let def_key = self.def_key(child_index); let span = self.get_span(child_index, sess); - if let (Some(kind), true) = ( - self.def_kind(child_index), - def_key.disambiguated_data.data.get_opt_name().is_some(), - ) { + if def_key.disambiguated_data.data.get_opt_name().is_some() { + let kind = self.def_kind(child_index); let ident = self.item_ident(child_index, sess); let vis = self.get_visibility(child_index); let def_id = self.local_def_id(child_index); @@ -1096,51 +1092,44 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn is_item_mir_available(&self, id: DefIndex) -> bool { - !self.is_proc_macro(id) && self.root.per_def.mir.get(self, id).is_some() + !self.is_proc_macro(id) && self.root.tables.mir.get(self, id).is_some() } - fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> BodyAndCache<'tcx> { - let mut cache = self - .root - .per_def + fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { + self.root + .tables .mir .get(self, id) .filter(|_| !self.is_proc_macro(id)) .unwrap_or_else(|| { bug!("get_optimized_mir: missing MIR for `{:?}`", self.local_def_id(id)) }) - .decode((self, tcx)); - cache.ensure_predecessors(); - cache + .decode((self, tcx)) } - fn get_promoted_mir( - &self, - tcx: TyCtxt<'tcx>, - id: DefIndex, - ) -> IndexVec> { - let mut cache = self - .root - .per_def + fn get_promoted_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> IndexVec> { + self.root + .tables .promoted_mir .get(self, id) .filter(|_| !self.is_proc_macro(id)) .unwrap_or_else(|| { bug!("get_promoted_mir: missing MIR for `{:?}`", self.local_def_id(id)) }) - .decode((self, tcx)); - for body in cache.iter_mut() { - body.ensure_predecessors(); - } - cache + .decode((self, tcx)) } fn mir_const_qualif(&self, id: DefIndex) -> mir::ConstQualifs { match self.kind(id) { EntryKind::Const(qualif, _) - | EntryKind::AssocConst(AssocContainer::ImplDefault, qualif, _) - | EntryKind::AssocConst(AssocContainer::ImplFinal, qualif, _) => qualif, - _ => bug!(), + | EntryKind::AssocConst( + AssocContainer::ImplDefault + | AssocContainer::ImplFinal + | AssocContainer::TraitWithDefault, + qualif, + _, + ) => qualif, + _ => bug!("mir_const_qualif: unexpected kind"), } } @@ -1153,7 +1142,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { EntryKind::AssocConst(container, _, _) => (ty::AssocKind::Const, container, false), EntryKind::AssocFn(data) => { let data = data.decode(self); - (ty::AssocKind::Method, data.container, data.has_self) + (ty::AssocKind::Fn, data.container, data.has_self) } EntryKind::AssocType(container) => (ty::AssocKind::Type, container, false), EntryKind::AssocOpaqueTy(container) => (ty::AssocKind::OpaqueTy, container, false), @@ -1167,12 +1156,12 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { defaultness: container.defaultness(), def_id: self.local_def_id(id), container: container.with_def_id(parent), - method_has_self_argument: has_self, + fn_has_self_parameter: has_self, } } fn get_item_variances(&self, id: DefIndex) -> Vec { - self.root.per_def.variances.get(self, id).unwrap_or(Lazy::empty()).decode(self).collect() + self.root.tables.variances.get(self, id).unwrap_or(Lazy::empty()).decode(self).collect() } fn get_ctor_kind(&self, node_id: DefIndex) -> CtorKind { @@ -1196,7 +1185,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_item_attrs(&self, node_id: DefIndex, sess: &Session) -> Lrc<[ast::Attribute]> { + fn get_item_attrs(&self, node_id: DefIndex, sess: &Session) -> Vec { // The attributes for a tuple struct/variant are attached to the definition, not the ctor; // we assume that someone passing in a tuple struct ctor is actually wanting to // look at the definition @@ -1207,20 +1196,18 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { node_id }; - Lrc::from( - self.root - .per_def - .attributes - .get(self, item_id) - .unwrap_or(Lazy::empty()) - .decode((self, sess)) - .collect::>(), - ) + self.root + .tables + .attributes + .get(self, item_id) + .unwrap_or(Lazy::empty()) + .decode((self, sess)) + .collect::>() } - fn get_struct_field_names(&self, id: DefIndex, sess: &Session) -> Vec> { + fn get_struct_field_names(&self, id: DefIndex, sess: &Session) -> Vec> { self.root - .per_def + .tables .children .get(self, id) .unwrap_or(Lazy::empty()) @@ -1236,7 +1223,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) -> &'tcx [DefId] { tcx.arena.alloc_from_iter( self.root - .per_def + .tables .inherent_impls .get(self, id) .unwrap_or(Lazy::empty()) @@ -1330,25 +1317,25 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_fn_param_names(&self, id: DefIndex) -> Vec { + fn get_fn_param_names(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [Symbol] { let param_names = match self.kind(id) { EntryKind::Fn(data) | EntryKind::ForeignFn(data) => data.decode(self).param_names, EntryKind::AssocFn(data) => data.decode(self).fn_data.param_names, _ => Lazy::empty(), }; - param_names.decode(self).collect() + tcx.arena.alloc_from_iter(param_names.decode(self)) } fn exported_symbols( &self, tcx: TyCtxt<'tcx>, - ) -> Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)> { + ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { if self.root.is_proc_macro_crate() { // If this crate is a custom derive crate, then we're not even going to // link those in so we skip those crates. - vec![] + &[] } else { - self.root.exported_symbols.decode((self, tcx)).collect() + tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx))) } } @@ -1416,7 +1403,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn fn_sig(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { - self.root.per_def.fn_sig.get(self, id).unwrap().decode((self, tcx)) + self.root.tables.fn_sig.get(self, id).unwrap().decode((self, tcx)) } #[inline] @@ -1460,10 +1447,45 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { /// /// Proc macro crates don't currently export spans, so this function does not have /// to work for them. - fn imported_source_files( - &self, - local_source_map: &source_map::SourceMap, - ) -> &'a [ImportedSourceFile] { + fn imported_source_files(&self, sess: &Session) -> &'a [ImportedSourceFile] { + // Translate the virtual `/rustc/$hash` prefix back to a real directory + // that should hold actual sources, where possible. + let virtual_rust_source_base_dir = option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR") + .map(Path::new) + .filter(|_| { + // Only spend time on further checks if we have what to translate *to*. + sess.real_rust_source_base_dir.is_some() + }) + .filter(|virtual_dir| { + // Don't translate away `/rustc/$hash` if we're still remapping to it, + // since that means we're still building `std`/`rustc` that need it, + // and we don't want the real path to leak into codegen/debuginfo. + !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir) + }); + let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { + debug!( + "try_to_translate_virtual_to_real(name={:?}): \ + virtual_rust_source_base_dir={:?}, real_rust_source_base_dir={:?}", + name, virtual_rust_source_base_dir, sess.real_rust_source_base_dir, + ); + + if let Some(virtual_dir) = virtual_rust_source_base_dir { + if let Some(real_dir) = &sess.real_rust_source_base_dir { + if let rustc_span::FileName::Real(path) = name { + if let Ok(rest) = path.strip_prefix(virtual_dir) { + let new_path = real_dir.join(rest); + debug!( + "try_to_translate_virtual_to_real: `{}` -> `{}`", + path.display(), + new_path.display(), + ); + *path = new_path; + } + } + } + } + }; + self.cdata.source_map_import_info.init_locking(|| { let external_source_map = self.root.source_map.decode(self); @@ -1472,7 +1494,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // We can't reuse an existing SourceFile, so allocate a new one // containing the information we need. let rustc_span::SourceFile { - name, + mut name, name_was_remapped, src_hash, start_pos, @@ -1485,6 +1507,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .. } = source_file_to_import; + // If this file's path has been remapped to `/rustc/$hash`, + // we might be able to reverse that (also see comments above, + // on `try_to_translate_virtual_to_real`). + // FIXME(eddyb) we could check `name_was_remapped` here, + // but in practice it seems to be always `false`. + try_to_translate_virtual_to_real(&mut name); + let source_length = (end_pos - start_pos).to_usize(); // Translate line-start positions and multibyte character @@ -1505,7 +1534,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { np.pos = np.pos - start_pos; } - let local_version = local_source_map.new_imported_source_file( + let local_version = sess.source_map().new_imported_source_file( name, name_was_remapped, src_hash, diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs index b9f1dd1663eee..b18272675c0b2 100644 --- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs +++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs @@ -4,13 +4,6 @@ use crate::link_args; use crate::native_libs; use crate::rmeta::{self, encoder}; -use rustc::hir::exports::Export; -use rustc::middle::cstore::{CrateSource, CrateStore, EncodedMetadata, NativeLibraryKind}; -use rustc::middle::exported_symbols::ExportedSymbol; -use rustc::middle::stability::DeprecationEntry; -use rustc::ty::query::Providers; -use rustc::ty::query::QueryConfig; -use rustc::ty::{self, TyCtxt}; use rustc_ast::ast; use rustc_ast::attr; use rustc_ast::expand::allocator::AllocatorKind; @@ -19,14 +12,20 @@ use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::DefPathTable; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; +use rustc_middle::hir::exports::Export; +use rustc_middle::middle::cstore::{CrateSource, CrateStore, EncodedMetadata, NativeLibraryKind}; +use rustc_middle::middle::exported_symbols::ExportedSymbol; +use rustc_middle::middle::stability::DeprecationEntry; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::query::QueryConfig; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::{CrateDisambiguator, Session}; use rustc_span::source_map::{self, Span, Spanned}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_data_structures::sync::Lrc; use smallvec::SmallVec; use std::any::Any; -use std::sync::Arc; macro_rules! provide { (<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident, @@ -37,7 +36,7 @@ macro_rules! provide { $(fn $name<$lt: $lt, T: IntoArgs>( $tcx: TyCtxt<$lt>, def_id_arg: T, - ) -> as QueryConfig<$lt>>::Value { + ) -> as QueryConfig>>::Value { let _prof_timer = $tcx.prof.generic_activity("metadata_decode_entry"); @@ -89,15 +88,11 @@ impl IntoArgs for (CrateNum, DefId) { provide! { <'tcx> tcx, def_id, other, cdata, type_of => { cdata.get_type(def_id.index, tcx) } - generics_of => { - tcx.arena.alloc(cdata.get_generics(def_id.index, tcx.sess)) - } + generics_of => { cdata.get_generics(def_id.index, tcx.sess) } explicit_predicates_of => { cdata.get_explicit_predicates(def_id.index, tcx) } inferred_outlives_of => { cdata.get_inferred_outlives(def_id.index, tcx) } super_predicates_of => { cdata.get_super_predicates(def_id.index, tcx) } - trait_def => { - tcx.arena.alloc(cdata.get_trait_def(def_id.index, tcx.sess)) - } + trait_def => { cdata.get_trait_def(def_id.index, tcx.sess) } adt_def => { cdata.get_adt_def(def_id.index, tcx) } adt_destructor => { let _ = cdata; @@ -118,8 +113,8 @@ provide! { <'tcx> tcx, def_id, other, cdata, bug!("coerce_unsized_info: `{:?}` is missing its info", def_id); }) } - optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) } - promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } + optimized_mir => { cdata.get_optimized_mir(tcx, def_id.index) } + promoted_mir => { cdata.get_promoted_mir(tcx, def_id.index) } mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } fn_sig => { cdata.fn_sig(def_id.index, tcx) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } @@ -139,12 +134,14 @@ provide! { <'tcx> tcx, def_id, other, cdata, lookup_deprecation_entry => { cdata.get_deprecation(def_id.index).map(DeprecationEntry::external) } - item_attrs => { cdata.get_item_attrs(def_id.index, tcx.sess) } + item_attrs => { tcx.arena.alloc_from_iter( + cdata.get_item_attrs(def_id.index, tcx.sess).into_iter() + ) } // FIXME(#38501) We've skipped a `read` on the `hir_owner_nodes` of // a `fn` when encoding, so the dep-tracking wouldn't work. // This is only used by rustdoc anyway, which shouldn't have // incremental recompilation ever enabled. - fn_arg_names => { cdata.get_fn_param_names(def_id.index) } + fn_arg_names => { cdata.get_fn_param_names(tcx, def_id.index) } rendered_const => { cdata.get_rendered_const(def_id.index) } impl_parent => { cdata.get_parent_impl(def_id.index) } trait_of_item => { cdata.get_trait_of_item(def_id.index) } @@ -177,7 +174,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, }) .collect(); - tcx.arena.alloc(reachable_non_generics) + reachable_non_generics } native_libraries => { Lrc::new(cdata.get_native_libraries(tcx.sess)) } foreign_modules => { cdata.get_foreign_modules(tcx) } @@ -219,7 +216,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } defined_lib_features => { cdata.get_lib_features(tcx) } defined_lang_items => { cdata.get_lang_items(tcx) } - diagnostic_items => { cdata.get_diagnostic_items(tcx) } + diagnostic_items => { cdata.get_diagnostic_items() } missing_lang_items => { cdata.get_missing_lang_items(tcx) } missing_extern_crate_item => { @@ -239,7 +236,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, // to block export of generics from dylibs, but we must fix // rust-lang/rust#65890 before we can do that robustly. - Arc::new(syms) + syms } } @@ -249,14 +246,11 @@ pub fn provide(providers: &mut Providers<'_>) { // resolve! Does this work? Unsure! That's what the issue is about *providers = Providers { is_dllimport_foreign_item: |tcx, id| match tcx.native_library_kind(id) { - Some(NativeLibraryKind::NativeUnknown) | Some(NativeLibraryKind::NativeRawDylib) => { - true - } + Some(NativeLibraryKind::NativeUnknown | NativeLibraryKind::NativeRawDylib) => true, _ => false, }, is_statically_included_foreign_item: |tcx, id| match tcx.native_library_kind(id) { - Some(NativeLibraryKind::NativeStatic) - | Some(NativeLibraryKind::NativeStaticNobundle) => true, + Some(NativeLibraryKind::NativeStatic | NativeLibraryKind::NativeStaticNobundle) => true, _ => false, }, native_library_kind: |tcx, id| { @@ -365,7 +359,7 @@ pub fn provide(providers: &mut Providers<'_>) { } } - tcx.arena.alloc(visible_parent_map) + visible_parent_map }, dependency_formats: |tcx, cnum| { @@ -425,7 +419,7 @@ impl CStore { .disambiguated_data .data .get_opt_name() - .map(ast::Ident::with_dummy_span) // FIXME: cross-crate hygiene + .map(Ident::with_dummy_span) // FIXME: cross-crate hygiene .expect("no name in load_macro"); LoadedMacro::MacroDef( @@ -433,7 +427,7 @@ impl CStore { ident, id: ast::DUMMY_NODE_ID, span, - attrs: attrs.iter().cloned().collect(), + attrs: attrs.to_vec(), kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)), vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), tokens: None, diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index 9718d19281620..2589e162dffe2 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -2,46 +2,48 @@ use crate::rmeta::table::FixedSizeEncoding; use crate::rmeta::*; use log::{debug, trace}; -use rustc::hir::map::Map; -use rustc::middle::cstore::{EncodedMetadata, ForeignModule, LinkagePreference, NativeLibrary}; -use rustc::middle::dependency_format::Linkage; -use rustc::middle::exported_symbols::{metadata_symbol_name, ExportedSymbol, SymbolExportLevel}; -use rustc::middle::lang_items; -use rustc::mir::{self, interpret}; -use rustc::traits::specialization_graph; -use rustc::ty::codec::{self as ty_codec, TyEncoder}; -use rustc::ty::layout::VariantIdx; -use rustc::ty::{self, SymbolName, Ty, TyCtxt}; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_ast::attr; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{join, Lrc}; use rustc_hir as hir; use rustc_hir::def::CtorKind; -use rustc_hir::def_id::DefIdSet; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::DefPathTable; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor}; +use rustc_hir::lang_items; use rustc_hir::{AnonConst, GenericParamKind}; use rustc_index::vec::Idx; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::cstore::{ + EncodedMetadata, ForeignModule, LinkagePreference, NativeLibrary, +}; +use rustc_middle::middle::dependency_format::Linkage; +use rustc_middle::middle::exported_symbols::{ + metadata_symbol_name, ExportedSymbol, SymbolExportLevel, +}; +use rustc_middle::mir::{self, interpret}; +use rustc_middle::traits::specialization_graph; +use rustc_middle::ty::codec::{self as ty_codec, TyEncoder}; +use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; use rustc_serialize::{opaque, Encodable, Encoder, SpecializedEncoder}; -use rustc_session::config::{self, CrateType}; +use rustc_session::config::CrateType; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, ExternalSource, FileName, SourceFile, Span}; +use rustc_target::abi::VariantIdx; use std::hash::Hash; use std::num::NonZeroUsize; use std::path::Path; -use std::u32; struct EncodeContext<'tcx> { opaque: opaque::Encoder, tcx: TyCtxt<'tcx>, - per_def: PerDefTableBuilders<'tcx>, + tables: TableBuilders<'tcx>, lazy_state: LazyState, type_shorthands: FxHashMap, usize>, @@ -65,6 +67,7 @@ macro_rules! encoder_methods { impl<'tcx> Encoder for EncodeContext<'tcx> { type Error = ::Error; + #[inline] fn emit_unit(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -493,8 +496,8 @@ impl<'tcx> EncodeContext<'tcx> { }; i = self.position(); - let per_def = self.per_def.encode(&mut self.opaque); - let per_def_bytes = self.position() - i; + let tables = self.tables.encode(&mut self.opaque); + let tables_bytes = self.position() - i; // Encode the proc macro data i = self.position(); @@ -556,7 +559,7 @@ impl<'tcx> EncodeContext<'tcx> { impls, exported_symbols, interpret_alloc_index, - per_def, + tables, }); let total_bytes = self.position(); @@ -581,7 +584,7 @@ impl<'tcx> EncodeContext<'tcx> { println!(" def-path table bytes: {}", def_path_table_bytes); println!(" proc-macro-data-bytes: {}", proc_macro_data_bytes); println!(" item bytes: {}", item_bytes); - println!(" per-def table bytes: {}", per_def_bytes); + println!(" table bytes: {}", tables_bytes); println!(" zero bytes: {}", zero_bytes); println!(" total bytes: {}", total_bytes); } @@ -593,17 +596,16 @@ impl<'tcx> EncodeContext<'tcx> { impl EncodeContext<'tcx> { fn encode_variances_of(&mut self, def_id: DefId) { debug!("EncodeContext::encode_variances_of({:?})", def_id); - record!(self.per_def.variances[def_id] <- &self.tcx.variances_of(def_id)[..]); + record!(self.tables.variances[def_id] <- &self.tcx.variances_of(def_id)[..]); } fn encode_item_type(&mut self, def_id: DefId) { debug!("EncodeContext::encode_item_type({:?})", def_id); - record!(self.per_def.ty[def_id] <- self.tcx.type_of(def_id)); + record!(self.tables.ty[def_id] <- self.tcx.type_of(def_id)); } - fn encode_enum_variant_info(&mut self, enum_did: DefId, index: VariantIdx) { + fn encode_enum_variant_info(&mut self, def: &ty::AdtDef, index: VariantIdx) { let tcx = self.tcx; - let def = tcx.adt_def(enum_did); let variant = &def.variants[index]; let def_id = variant.def_id; debug!("EncodeContext::encode_enum_variant_info({:?})", def_id); @@ -614,15 +616,15 @@ impl EncodeContext<'tcx> { ctor: variant.ctor_def_id.map(|did| did.index), }; - let enum_id = tcx.hir().as_local_hir_id(enum_did).unwrap(); + let enum_id = tcx.hir().as_local_hir_id(def.did.expect_local()); let enum_vis = &tcx.hir().expect_item(enum_id).vis; - record!(self.per_def.kind[def_id] <- EntryKind::Variant(self.lazy(data))); - record!(self.per_def.visibility[def_id] <- + record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data))); + record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(enum_vis, enum_id, self.tcx)); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.per_def.attributes[def_id] <- &self.tcx.get_attrs(def_id)[..]); - record!(self.per_def.children[def_id] <- variant.fields.iter().map(|f| { + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.attributes[def_id] <- &self.tcx.get_attrs(def_id)[..]); + record!(self.tables.children[def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); f.did.index })); @@ -633,7 +635,7 @@ impl EncodeContext<'tcx> { if variant.ctor_kind == CtorKind::Fn { // FIXME(eddyb) encode signature only in `encode_enum_variant_ctor`. if let Some(ctor_def_id) = variant.ctor_def_id { - record!(self.per_def.fn_sig[def_id] <- tcx.fn_sig(ctor_def_id)); + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(ctor_def_id)); } // FIXME(eddyb) is this ever used? self.encode_variances_of(def_id); @@ -641,13 +643,12 @@ impl EncodeContext<'tcx> { self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); + self.encode_optimized_mir(def_id.expect_local()); + self.encode_promoted_mir(def_id.expect_local()); } - fn encode_enum_variant_ctor(&mut self, enum_did: DefId, index: VariantIdx) { + fn encode_enum_variant_ctor(&mut self, def: &ty::AdtDef, index: VariantIdx) { let tcx = self.tcx; - let def = tcx.adt_def(enum_did); let variant = &def.variants[index]; let def_id = variant.ctor_def_id.unwrap(); debug!("EncodeContext::encode_enum_variant_ctor({:?})", def_id); @@ -661,28 +662,28 @@ impl EncodeContext<'tcx> { // Variant constructors have the same visibility as the parent enums, unless marked as // non-exhaustive, in which case they are lowered to `pub(crate)`. - let enum_id = tcx.hir().as_local_hir_id(enum_did).unwrap(); + let enum_id = tcx.hir().as_local_hir_id(def.did.expect_local()); let enum_vis = &tcx.hir().expect_item(enum_id).vis; let mut ctor_vis = ty::Visibility::from_hir(enum_vis, enum_id, tcx); if variant.is_field_list_non_exhaustive() && ctor_vis == ty::Visibility::Public { ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); } - record!(self.per_def.kind[def_id] <- EntryKind::Variant(self.lazy(data))); - record!(self.per_def.visibility[def_id] <- ctor_vis); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data))); + record!(self.tables.visibility[def_id] <- ctor_vis); + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); self.encode_stability(def_id); self.encode_deprecation(def_id); self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { - record!(self.per_def.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); self.encode_variances_of(def_id); } self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); + self.encode_optimized_mir(def_id.expect_local()); + self.encode_promoted_mir(def_id.expect_local()); } fn encode_info_for_mod( @@ -693,7 +694,7 @@ impl EncodeContext<'tcx> { vis: &hir::Visibility<'_>, ) { let tcx = self.tcx; - let def_id = tcx.hir().local_def_id(id); + let def_id = tcx.hir().local_def_id(id).to_def_id(); debug!("EncodeContext::encode_info_for_mod({:?})", def_id); let data = ModData { @@ -703,32 +704,37 @@ impl EncodeContext<'tcx> { }, }; - record!(self.per_def.kind[def_id] <- EntryKind::Mod(self.lazy(data))); - record!(self.per_def.visibility[def_id] <- ty::Visibility::from_hir(vis, id, self.tcx)); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.per_def.attributes[def_id] <- attrs); - record!(self.per_def.children[def_id] <- md.item_ids.iter().map(|item_id| { - tcx.hir().local_def_id(item_id.id).index + record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); + record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(vis, id, self.tcx)); + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.attributes[def_id] <- attrs); + record!(self.tables.children[def_id] <- md.item_ids.iter().map(|item_id| { + tcx.hir().local_def_id(item_id.id).local_def_index })); self.encode_stability(def_id); self.encode_deprecation(def_id); } - fn encode_field(&mut self, adt_def_id: DefId, variant_index: VariantIdx, field_index: usize) { + fn encode_field( + &mut self, + adt_def: &ty::AdtDef, + variant_index: VariantIdx, + field_index: usize, + ) { let tcx = self.tcx; - let variant = &tcx.adt_def(adt_def_id).variants[variant_index]; + let variant = &adt_def.variants[variant_index]; let field = &variant.fields[field_index]; let def_id = field.did; debug!("EncodeContext::encode_field({:?})", def_id); - let variant_id = tcx.hir().as_local_hir_id(variant.def_id).unwrap(); + let variant_id = tcx.hir().as_local_hir_id(variant.def_id.expect_local()); let variant_data = tcx.hir().expect_variant_data(variant_id); - record!(self.per_def.kind[def_id] <- EntryKind::Field); - record!(self.per_def.visibility[def_id] <- field.vis); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.per_def.attributes[def_id] <- variant_data.fields()[field_index].attrs); + record!(self.tables.kind[def_id] <- EntryKind::Field); + record!(self.tables.visibility[def_id] <- field.vis); + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.attributes[def_id] <- variant_data.fields()[field_index].attrs); self.encode_ident_span(def_id, field.ident); self.encode_stability(def_id); self.encode_deprecation(def_id); @@ -738,10 +744,9 @@ impl EncodeContext<'tcx> { self.encode_inferred_outlives(def_id); } - fn encode_struct_ctor(&mut self, adt_def_id: DefId, def_id: DefId) { + fn encode_struct_ctor(&mut self, adt_def: &ty::AdtDef, def_id: DefId) { debug!("EncodeContext::encode_struct_ctor({:?})", def_id); let tcx = self.tcx; - let adt_def = tcx.adt_def(adt_def_id); let variant = adt_def.non_enum_variant(); let data = VariantData { @@ -750,7 +755,7 @@ impl EncodeContext<'tcx> { ctor: Some(def_id.index), }; - let struct_id = tcx.hir().as_local_hir_id(adt_def_id).unwrap(); + let struct_id = tcx.hir().as_local_hir_id(adt_def.did.expect_local()); let struct_vis = &tcx.hir().expect_item(struct_id).vis; let mut ctor_vis = ty::Visibility::from_hir(struct_vis, struct_id, tcx); for field in &variant.fields { @@ -767,31 +772,31 @@ impl EncodeContext<'tcx> { ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); } - record!(self.per_def.kind[def_id] <- EntryKind::Struct(self.lazy(data), adt_def.repr)); - record!(self.per_def.visibility[def_id] <- ctor_vis); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data), adt_def.repr)); + record!(self.tables.visibility[def_id] <- ctor_vis); + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); self.encode_stability(def_id); self.encode_deprecation(def_id); self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { - record!(self.per_def.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); self.encode_variances_of(def_id); } self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); + self.encode_optimized_mir(def_id.expect_local()); + self.encode_promoted_mir(def_id.expect_local()); } fn encode_generics(&mut self, def_id: DefId) { debug!("EncodeContext::encode_generics({:?})", def_id); - record!(self.per_def.generics[def_id] <- self.tcx.generics_of(def_id)); + record!(self.tables.generics[def_id] <- self.tcx.generics_of(def_id)); } fn encode_explicit_predicates(&mut self, def_id: DefId) { debug!("EncodeContext::encode_explicit_predicates({:?})", def_id); - record!(self.per_def.explicit_predicates[def_id] <- + record!(self.tables.explicit_predicates[def_id] <- self.tcx.explicit_predicates_of(def_id)); } @@ -799,20 +804,20 @@ impl EncodeContext<'tcx> { debug!("EncodeContext::encode_inferred_outlives({:?})", def_id); let inferred_outlives = self.tcx.inferred_outlives_of(def_id); if !inferred_outlives.is_empty() { - record!(self.per_def.inferred_outlives[def_id] <- inferred_outlives); + record!(self.tables.inferred_outlives[def_id] <- inferred_outlives); } } fn encode_super_predicates(&mut self, def_id: DefId) { debug!("EncodeContext::encode_super_predicates({:?})", def_id); - record!(self.per_def.super_predicates[def_id] <- self.tcx.super_predicates_of(def_id)); + record!(self.tables.super_predicates[def_id] <- self.tcx.super_predicates_of(def_id)); } fn encode_info_for_trait_item(&mut self, def_id: DefId) { debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id); let tcx = self.tcx; - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let ast_item = tcx.hir().expect_trait_item(hir_id); let trait_item = tcx.associated_item(def_id); @@ -822,10 +827,12 @@ impl EncodeContext<'tcx> { hir::Defaultness::Final => span_bug!(ast_item.span, "traits cannot have final items"), }; - record!(self.per_def.kind[def_id] <- match trait_item.kind { + record!(self.tables.kind[def_id] <- match trait_item.kind { ty::AssocKind::Const => { - let rendered = - hir::print::to_string(&self.tcx.hir(), |s| s.print_trait_item(ast_item)); + let rendered = rustc_hir_pretty::to_string( + &(&self.tcx.hir() as &dyn intravisit::Map<'_>), + |s| s.print_trait_item(ast_item) + ); let rendered_const = self.lazy(RenderedConst(rendered)); EntryKind::AssocConst( @@ -834,7 +841,7 @@ impl EncodeContext<'tcx> { rendered_const, ) } - ty::AssocKind::Method => { + ty::AssocKind::Fn => { let fn_data = if let hir::TraitItemKind::Fn(m_sig, m) = &ast_item.kind { let param_names = match *m { hir::TraitFn::Required(ref names) => { @@ -855,21 +862,21 @@ impl EncodeContext<'tcx> { EntryKind::AssocFn(self.lazy(AssocFnData { fn_data, container, - has_self: trait_item.method_has_self_argument, + has_self: trait_item.fn_has_self_parameter, })) } ty::AssocKind::Type => EntryKind::AssocType(container), ty::AssocKind::OpaqueTy => span_bug!(ast_item.span, "opaque type in trait"), }); - record!(self.per_def.visibility[def_id] <- trait_item.vis); - record!(self.per_def.span[def_id] <- ast_item.span); - record!(self.per_def.attributes[def_id] <- ast_item.attrs); + record!(self.tables.visibility[def_id] <- trait_item.vis); + record!(self.tables.span[def_id] <- ast_item.span); + record!(self.tables.attributes[def_id] <- ast_item.attrs); self.encode_ident_span(def_id, ast_item.ident); self.encode_stability(def_id); self.encode_const_stability(def_id); self.encode_deprecation(def_id); match trait_item.kind { - ty::AssocKind::Const | ty::AssocKind::Method => { + ty::AssocKind::Const | ty::AssocKind::Fn => { self.encode_item_type(def_id); } ty::AssocKind::Type => { @@ -879,8 +886,8 @@ impl EncodeContext<'tcx> { } ty::AssocKind::OpaqueTy => unreachable!(), } - if trait_item.kind == ty::AssocKind::Method { - record!(self.per_def.fn_sig[def_id] <- tcx.fn_sig(def_id)); + if trait_item.kind == ty::AssocKind::Fn { + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); self.encode_variances_of(def_id); } self.encode_generics(def_id); @@ -888,8 +895,8 @@ impl EncodeContext<'tcx> { self.encode_inferred_outlives(def_id); // This should be kept in sync with `PrefetchVisitor.visit_trait_item`. - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); + self.encode_optimized_mir(def_id.expect_local()); + self.encode_promoted_mir(def_id.expect_local()); } fn metadata_output_only(&self) -> bool { @@ -901,7 +908,7 @@ impl EncodeContext<'tcx> { debug!("EncodeContext::encode_info_for_impl_item({:?})", def_id); let tcx = self.tcx; - let hir_id = self.tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = self.tcx.hir().as_local_hir_id(def_id.expect_local()); let ast_item = self.tcx.hir().expect_impl_item(hir_id); let impl_item = self.tcx.associated_item(def_id); @@ -913,7 +920,7 @@ impl EncodeContext<'tcx> { } }; - record!(self.per_def.kind[def_id] <- match impl_item.kind { + record!(self.tables.kind[def_id] <- match impl_item.kind { ty::AssocKind::Const => { if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind { let qualifs = self.tcx.at(ast_item.span).mir_const_qualif(def_id); @@ -926,7 +933,7 @@ impl EncodeContext<'tcx> { bug!() } } - ty::AssocKind::Method => { + ty::AssocKind::Fn => { let fn_data = if let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind { FnData { asyncness: sig.header.asyncness, @@ -939,22 +946,22 @@ impl EncodeContext<'tcx> { EntryKind::AssocFn(self.lazy(AssocFnData { fn_data, container, - has_self: impl_item.method_has_self_argument, + has_self: impl_item.fn_has_self_parameter, })) } ty::AssocKind::OpaqueTy => EntryKind::AssocOpaqueTy(container), ty::AssocKind::Type => EntryKind::AssocType(container) }); - record!(self.per_def.visibility[def_id] <- impl_item.vis); - record!(self.per_def.span[def_id] <- ast_item.span); - record!(self.per_def.attributes[def_id] <- ast_item.attrs); + record!(self.tables.visibility[def_id] <- impl_item.vis); + record!(self.tables.span[def_id] <- ast_item.span); + record!(self.tables.attributes[def_id] <- ast_item.attrs); self.encode_ident_span(def_id, impl_item.ident); self.encode_stability(def_id); self.encode_const_stability(def_id); self.encode_deprecation(def_id); self.encode_item_type(def_id); - if impl_item.kind == ty::AssocKind::Method { - record!(self.per_def.fn_sig[def_id] <- tcx.fn_sig(def_id)); + if impl_item.kind == ty::AssocKind::Fn { + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); self.encode_variances_of(def_id); } self.encode_generics(def_id); @@ -977,12 +984,12 @@ impl EncodeContext<'tcx> { hir::ImplItemKind::OpaqueTy(..) | hir::ImplItemKind::TyAlias(..) => false, }; if mir { - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); + self.encode_optimized_mir(def_id.expect_local()); + self.encode_promoted_mir(def_id.expect_local()); } } - fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[ast::Name]> { + fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Symbol]> { self.tcx.dep_graph.with_ignore(|| { let body = self.tcx.hir().body(body_id); self.lazy(body.params.iter().map(|arg| match arg.pat.kind { @@ -992,21 +999,21 @@ impl EncodeContext<'tcx> { }) } - fn encode_fn_param_names(&mut self, param_names: &[ast::Ident]) -> Lazy<[ast::Name]> { + fn encode_fn_param_names(&mut self, param_names: &[Ident]) -> Lazy<[Symbol]> { self.lazy(param_names.iter().map(|ident| ident.name)) } - fn encode_optimized_mir(&mut self, def_id: DefId) { + fn encode_optimized_mir(&mut self, def_id: LocalDefId) { debug!("EntryBuilder::encode_mir({:?})", def_id); if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) { - record!(self.per_def.mir[def_id] <- self.tcx.optimized_mir(def_id)); + record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id)); } } - fn encode_promoted_mir(&mut self, def_id: DefId) { + fn encode_promoted_mir(&mut self, def_id: LocalDefId) { debug!("EncodeContext::encode_promoted_mir({:?})", def_id); if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) { - record!(self.per_def.promoted_mir[def_id] <- self.tcx.promoted_mir(def_id)); + record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id)); } } @@ -1015,7 +1022,7 @@ impl EncodeContext<'tcx> { debug!("EncodeContext::encode_inherent_implementations({:?})", def_id); let implementations = self.tcx.inherent_impls(def_id); if !implementations.is_empty() { - record!(self.per_def.inherent_impls[def_id] <- implementations.iter().map(|&def_id| { + record!(self.tables.inherent_impls[def_id] <- implementations.iter().map(|&def_id| { assert!(def_id.is_local()); def_id.index })); @@ -1025,27 +1032,30 @@ impl EncodeContext<'tcx> { fn encode_stability(&mut self, def_id: DefId) { debug!("EncodeContext::encode_stability({:?})", def_id); if let Some(stab) = self.tcx.lookup_stability(def_id) { - record!(self.per_def.stability[def_id] <- stab) + record!(self.tables.stability[def_id] <- stab) } } fn encode_const_stability(&mut self, def_id: DefId) { debug!("EncodeContext::encode_const_stability({:?})", def_id); if let Some(stab) = self.tcx.lookup_const_stability(def_id) { - record!(self.per_def.const_stability[def_id] <- stab) + record!(self.tables.const_stability[def_id] <- stab) } } fn encode_deprecation(&mut self, def_id: DefId) { debug!("EncodeContext::encode_deprecation({:?})", def_id); if let Some(depr) = self.tcx.lookup_deprecation(def_id) { - record!(self.per_def.deprecation[def_id] <- depr); + record!(self.tables.deprecation[def_id] <- depr); } } fn encode_rendered_const_for_body(&mut self, body_id: hir::BodyId) -> Lazy { - let body = self.tcx.hir().body(body_id); - let rendered = hir::print::to_string(&self.tcx.hir(), |s| s.print_expr(&body.value)); + let hir = self.tcx.hir(); + let body = hir.body(body_id); + let rendered = rustc_hir_pretty::to_string(&(&hir as &dyn intravisit::Map<'_>), |s| { + s.print_expr(&body.value) + }); let rendered_const = &RenderedConst(rendered); self.lazy(rendered_const) } @@ -1057,7 +1067,7 @@ impl EncodeContext<'tcx> { self.encode_ident_span(def_id, item.ident); - record!(self.per_def.kind[def_id] <- match item.kind { + record!(self.tables.kind[def_id] <- match item.kind { hir::ItemKind::Static(_, hir::Mutability::Mut, _) => EntryKind::MutStatic, hir::ItemKind::Static(_, hir::Mutability::Not, _) => EntryKind::ImmStatic, hir::ItemKind::Const(_, body_id) => { @@ -1092,7 +1102,7 @@ impl EncodeContext<'tcx> { // for methods, write all the stuff get_trait_method // needs to know let ctor = struct_def.ctor_hir_id().map(|ctor_hir_id| { - self.tcx.hir().local_def_id(ctor_hir_id).index + self.tcx.hir().local_def_id(ctor_hir_id).local_def_index }); EntryKind::Struct(self.lazy(VariantData { @@ -1163,26 +1173,26 @@ impl EncodeContext<'tcx> { hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => bug!("cannot encode info for item {:?}", item), }); - record!(self.per_def.visibility[def_id] <- + record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(&item.vis, item.hir_id, tcx)); - record!(self.per_def.span[def_id] <- item.span); - record!(self.per_def.attributes[def_id] <- item.attrs); + record!(self.tables.span[def_id] <- item.span); + record!(self.tables.attributes[def_id] <- item.attrs); // FIXME(eddyb) there should be a nicer way to do this. match item.kind { - hir::ItemKind::ForeignMod(ref fm) => record!(self.per_def.children[def_id] <- + hir::ItemKind::ForeignMod(ref fm) => record!(self.tables.children[def_id] <- fm.items .iter() .map(|foreign_item| tcx.hir().local_def_id( - foreign_item.hir_id).index) + foreign_item.hir_id).local_def_index) ), - hir::ItemKind::Enum(..) => record!(self.per_def.children[def_id] <- + hir::ItemKind::Enum(..) => record!(self.tables.children[def_id] <- self.tcx.adt_def(def_id).variants.iter().map(|v| { assert!(v.def_id.is_local()); v.def_id.index }) ), hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => { - record!(self.per_def.children[def_id] <- + record!(self.tables.children[def_id] <- self.tcx.adt_def(def_id).non_enum_variant().fields.iter().map(|f| { assert!(f.did.is_local()); f.did.index @@ -1191,7 +1201,7 @@ impl EncodeContext<'tcx> { } hir::ItemKind::Impl { .. } | hir::ItemKind::Trait(..) => { let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id); - record!(self.per_def.children[def_id] <- + record!(self.tables.children[def_id] <- associated_item_def_ids.iter().map(|&def_id| { assert!(def_id.is_local()); def_id.index @@ -1216,11 +1226,11 @@ impl EncodeContext<'tcx> { _ => {} } if let hir::ItemKind::Fn(..) = item.kind { - record!(self.per_def.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } if let hir::ItemKind::Impl { .. } = item.kind { if let Some(trait_ref) = self.tcx.impl_trait_ref(def_id) { - record!(self.per_def.impl_trait_ref[def_id] <- trait_ref); + record!(self.tables.impl_trait_ref[def_id] <- trait_ref); } } self.encode_inherent_implementations(def_id); @@ -1271,41 +1281,41 @@ impl EncodeContext<'tcx> { _ => false, }; if mir { - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); + self.encode_optimized_mir(def_id.expect_local()); + self.encode_promoted_mir(def_id.expect_local()); } } /// Serialize the text of exported macros fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) { - let def_id = self.tcx.hir().local_def_id(macro_def.hir_id); - record!(self.per_def.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone()))); - record!(self.per_def.visibility[def_id] <- ty::Visibility::Public); - record!(self.per_def.span[def_id] <- macro_def.span); - record!(self.per_def.attributes[def_id] <- macro_def.attrs); + let def_id = self.tcx.hir().local_def_id(macro_def.hir_id).to_def_id(); + record!(self.tables.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone()))); + record!(self.tables.visibility[def_id] <- ty::Visibility::Public); + record!(self.tables.span[def_id] <- macro_def.span); + record!(self.tables.attributes[def_id] <- macro_def.attrs); self.encode_ident_span(def_id, macro_def.ident); self.encode_stability(def_id); self.encode_deprecation(def_id); } fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) { - record!(self.per_def.kind[def_id] <- kind); - record!(self.per_def.visibility[def_id] <- ty::Visibility::Public); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.kind[def_id] <- kind); + record!(self.tables.visibility[def_id] <- ty::Visibility::Public); + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); if encode_type { self.encode_item_type(def_id); } } - fn encode_info_for_closure(&mut self, def_id: DefId) { + fn encode_info_for_closure(&mut self, def_id: LocalDefId) { debug!("EncodeContext::encode_info_for_closure({:?})", def_id); // NOTE(eddyb) `tcx.type_of(def_id)` isn't used because it's fully generic, // including on the signature, which is inferred in `typeck_tables_of. - let hir_id = self.tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = self.tcx.hir().as_local_hir_id(def_id); let ty = self.tcx.typeck_tables_of(def_id).node_type(hir_id); - record!(self.per_def.kind[def_id] <- match ty.kind { + record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind { ty::Generator(..) => { let data = self.tcx.generator_kind(def_id).unwrap(); EntryKind::Generator(data) @@ -1315,32 +1325,32 @@ impl EncodeContext<'tcx> { _ => bug!("closure that is neither generator nor closure"), }); - record!(self.per_def.visibility[def_id] <- ty::Visibility::Public); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.per_def.attributes[def_id] <- &self.tcx.get_attrs(def_id)[..]); - self.encode_item_type(def_id); + record!(self.tables.visibility[def_id.to_def_id()] <- ty::Visibility::Public); + record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); + record!(self.tables.attributes[def_id.to_def_id()] <- &self.tcx.get_attrs(def_id.to_def_id())[..]); + self.encode_item_type(def_id.to_def_id()); if let ty::Closure(def_id, substs) = ty.kind { - record!(self.per_def.fn_sig[def_id] <- substs.as_closure().sig()); + record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig()); } - self.encode_generics(def_id); + self.encode_generics(def_id.to_def_id()); self.encode_optimized_mir(def_id); self.encode_promoted_mir(def_id); } - fn encode_info_for_anon_const(&mut self, def_id: DefId) { + fn encode_info_for_anon_const(&mut self, def_id: LocalDefId) { debug!("EncodeContext::encode_info_for_anon_const({:?})", def_id); - let id = self.tcx.hir().as_local_hir_id(def_id).unwrap(); + let id = self.tcx.hir().as_local_hir_id(def_id); let body_id = self.tcx.hir().body_owned_by(id); let const_data = self.encode_rendered_const_for_body(body_id); let qualifs = self.tcx.mir_const_qualif(def_id); - record!(self.per_def.kind[def_id] <- EntryKind::Const(qualifs, const_data)); - record!(self.per_def.visibility[def_id] <- ty::Visibility::Public); - record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id)); - self.encode_item_type(def_id); - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); + record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::Const(qualifs, const_data)); + record!(self.tables.visibility[def_id.to_def_id()] <- ty::Visibility::Public); + record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); + self.encode_item_type(def_id.to_def_id()); + self.encode_generics(def_id.to_def_id()); + self.encode_explicit_predicates(def_id.to_def_id()); + self.encode_inferred_outlives(def_id.to_def_id()); self.encode_optimized_mir(def_id); self.encode_promoted_mir(def_id); } @@ -1400,7 +1410,7 @@ impl EncodeContext<'tcx> { self.lazy(deps.iter().map(|&(_, ref dep)| dep)) } - fn encode_lib_features(&mut self) -> Lazy<[(ast::Name, Option)]> { + fn encode_lib_features(&mut self) -> Lazy<[(Symbol, Option)]> { let tcx = self.tcx; let lib_features = tcx.lib_features(); self.lazy(lib_features.to_vec()) @@ -1489,7 +1499,7 @@ impl EncodeContext<'tcx> { fn encode_dylib_dependency_formats(&mut self) -> Lazy<[Option]> { let formats = self.tcx.dependency_formats(LOCAL_CRATE); for (ty, arr) in formats.iter() { - if *ty != config::CrateType::Dylib { + if *ty != CrateType::Dylib { continue; } return self.lazy(arr.iter().map(|slot| match *slot { @@ -1507,7 +1517,7 @@ impl EncodeContext<'tcx> { debug!("EncodeContext::encode_info_for_foreign_item({:?})", def_id); - record!(self.per_def.kind[def_id] <- match nitem.kind { + record!(self.tables.kind[def_id] <- match nitem.kind { hir::ForeignItemKind::Fn(_, ref names, _) => { let data = FnData { asyncness: hir::IsAsync::NotAsync, @@ -1524,17 +1534,17 @@ impl EncodeContext<'tcx> { hir::ForeignItemKind::Static(_, hir::Mutability::Not) => EntryKind::ForeignImmStatic, hir::ForeignItemKind::Type => EntryKind::ForeignType, }); - record!(self.per_def.visibility[def_id] <- + record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(&nitem.vis, nitem.hir_id, self.tcx)); - record!(self.per_def.span[def_id] <- nitem.span); - record!(self.per_def.attributes[def_id] <- nitem.attrs); + record!(self.tables.span[def_id] <- nitem.span); + record!(self.tables.attributes[def_id] <- nitem.attrs); self.encode_ident_span(def_id, nitem.ident); self.encode_stability(def_id); self.encode_const_stability(def_id); self.encode_deprecation(def_id); self.encode_item_type(def_id); if let hir::ForeignItemKind::Fn(..) = nitem.kind { - record!(self.per_def.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); self.encode_variances_of(def_id); } self.encode_generics(def_id); @@ -1564,14 +1574,14 @@ impl Visitor<'tcx> for EncodeContext<'tcx> { let def_id = self.tcx.hir().local_def_id(item.hir_id); match item.kind { hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => {} // ignore these - _ => self.encode_info_for_item(def_id, item), + _ => self.encode_info_for_item(def_id.to_def_id(), item), } self.encode_addl_info_for_item(item); } fn visit_foreign_item(&mut self, ni: &'tcx hir::ForeignItem<'tcx>) { intravisit::walk_foreign_item(self, ni); let def_id = self.tcx.hir().local_def_id(ni.hir_id); - self.encode_info_for_foreign_item(def_id, ni); + self.encode_info_for_foreign_item(def_id.to_def_id(), ni); } fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { intravisit::walk_generics(self, generics); @@ -1583,13 +1593,10 @@ impl Visitor<'tcx> for EncodeContext<'tcx> { } impl EncodeContext<'tcx> { - fn encode_fields(&mut self, adt_def_id: DefId) { - let def = self.tcx.adt_def(adt_def_id); - for (variant_index, variant) in def.variants.iter_enumerated() { + fn encode_fields(&mut self, adt_def: &ty::AdtDef) { + for (variant_index, variant) in adt_def.variants.iter_enumerated() { for (field_index, _field) in variant.fields.iter().enumerate() { - // FIXME(eddyb) `adt_def_id` is leftover from incremental isolation, - // pass `def`, `variant` or `field` instead. - self.encode_field(adt_def_id, variant_index, field_index); + self.encode_field(adt_def, variant_index, field_index); } } } @@ -1601,30 +1608,31 @@ impl EncodeContext<'tcx> { GenericParamKind::Lifetime { .. } => continue, GenericParamKind::Type { ref default, .. } => { self.encode_info_for_generic_param( - def_id, + def_id.to_def_id(), EntryKind::TypeParam, default.is_some(), ); } GenericParamKind::Const { .. } => { - self.encode_info_for_generic_param(def_id, EntryKind::ConstParam, true); + self.encode_info_for_generic_param( + def_id.to_def_id(), + EntryKind::ConstParam, + true, + ); } } } } fn encode_info_for_expr(&mut self, expr: &hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Closure(..) => { - let def_id = self.tcx.hir().local_def_id(expr.hir_id); - self.encode_info_for_closure(def_id); - } - _ => {} + if let hir::ExprKind::Closure(..) = expr.kind { + let def_id = self.tcx.hir().local_def_id(expr.hir_id); + self.encode_info_for_closure(def_id); } } fn encode_ident_span(&mut self, def_id: DefId, ident: Ident) { - record!(self.per_def.ident_span[def_id] <- ident.span); + record!(self.tables.ident_span[def_id] <- ident.span); } /// In some cases, along with the item itself, we also @@ -1648,40 +1656,40 @@ impl EncodeContext<'tcx> { // no sub-item recording needed in these cases } hir::ItemKind::Enum(..) => { - self.encode_fields(def_id); + let def = self.tcx.adt_def(def_id.to_def_id()); + self.encode_fields(def); - let def = self.tcx.adt_def(def_id); for (i, variant) in def.variants.iter_enumerated() { - // FIXME(eddyb) `def_id` is leftover from incremental isolation, - // pass `def` or `variant` instead. - self.encode_enum_variant_info(def_id, i); + self.encode_enum_variant_info(def, i); - // FIXME(eddyb) `def_id` is leftover from incremental isolation, - // pass `def`, `variant` or `ctor_def_id` instead. if let Some(_ctor_def_id) = variant.ctor_def_id { - self.encode_enum_variant_ctor(def_id, i); + self.encode_enum_variant_ctor(def, i); } } } hir::ItemKind::Struct(ref struct_def, _) => { - self.encode_fields(def_id); + let def = self.tcx.adt_def(def_id.to_def_id()); + self.encode_fields(def); // If the struct has a constructor, encode it. if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id); - self.encode_struct_ctor(def_id, ctor_def_id); + self.encode_struct_ctor(def, ctor_def_id.to_def_id()); } } hir::ItemKind::Union(..) => { - self.encode_fields(def_id); + let def = self.tcx.adt_def(def_id.to_def_id()); + self.encode_fields(def); } hir::ItemKind::Impl { .. } => { - for &trait_item_def_id in self.tcx.associated_item_def_ids(def_id).iter() { + for &trait_item_def_id in + self.tcx.associated_item_def_ids(def_id.to_def_id()).iter() + { self.encode_info_for_impl_item(trait_item_def_id); } } hir::ItemKind::Trait(..) => { - for &item_def_id in self.tcx.associated_item_def_ids(def_id).iter() { + for &item_def_id in self.tcx.associated_item_def_ids(def_id.to_def_id()).iter() { self.encode_info_for_trait_item(item_def_id); } } @@ -1698,8 +1706,8 @@ impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { if let hir::ItemKind::Impl { .. } = item.kind { let impl_id = self.tcx.hir().local_def_id(item.hir_id); - if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_id) { - self.impls.entry(trait_ref.def_id).or_default().push(impl_id.index); + if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_id.to_def_id()) { + self.impls.entry(trait_ref.def_id).or_default().push(impl_id.local_def_index); } } } @@ -1715,14 +1723,14 @@ impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> { /// Only a subset of the queries are actually prefetched to keep this code smaller. struct PrefetchVisitor<'tcx> { tcx: TyCtxt<'tcx>, - mir_keys: &'tcx DefIdSet, + mir_keys: &'tcx FxHashSet, } impl<'tcx> PrefetchVisitor<'tcx> { - fn prefetch_mir(&self, def_id: DefId) { + fn prefetch_mir(&self, def_id: LocalDefId) { if self.mir_keys.contains(&def_id) { - self.tcx.optimized_mir(def_id); - self.tcx.promoted_mir(def_id); + self.tcx.ensure().optimized_mir(def_id); + self.tcx.ensure().promoted_mir(def_id); } } } @@ -1737,9 +1745,9 @@ impl<'tcx, 'v> ParItemLikeVisitor<'v> for PrefetchVisitor<'tcx> { } hir::ItemKind::Fn(ref sig, ..) => { let def_id = tcx.hir().local_def_id(item.hir_id); - let generics = tcx.generics_of(def_id); + let generics = tcx.generics_of(def_id.to_def_id()); let needs_inline = generics.requires_monomorphization(tcx) - || tcx.codegen_fn_attrs(def_id).requests_inline(); + || tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline(); if needs_inline || sig.header.constness == hir::Constness::Const { self.prefetch_mir(def_id) } @@ -1762,9 +1770,9 @@ impl<'tcx, 'v> ParItemLikeVisitor<'v> for PrefetchVisitor<'tcx> { } hir::ImplItemKind::Fn(ref sig, _) => { let def_id = tcx.hir().local_def_id(impl_item.hir_id); - let generics = tcx.generics_of(def_id); + let generics = tcx.generics_of(def_id.to_def_id()); let needs_inline = generics.requires_monomorphization(tcx) - || tcx.codegen_fn_attrs(def_id).requests_inline(); + || tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline(); let is_const_fn = sig.header.constness == hir::Constness::Const; if needs_inline || is_const_fn { self.prefetch_mir(def_id) @@ -1840,7 +1848,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { let mut ecx = EncodeContext { opaque: encoder, tcx, - per_def: Default::default(), + tables: Default::default(), lazy_state: LazyState::NoNode, type_shorthands: Default::default(), predicate_shorthands: Default::default(), diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 3a4214e916acb..669307612055a 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -1,13 +1,6 @@ use decoder::Metadata; use table::{Table, TableBuilder}; -use rustc::hir::exports::Export; -use rustc::hir::map; -use rustc::middle::cstore::{DepKind, ForeignModule, LinkagePreference, NativeLibrary}; -use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; -use rustc::middle::lang_items; -use rustc::mir; -use rustc::ty::{self, ReprOptions, Ty}; use rustc_ast::ast::{self, MacroDef}; use rustc_attr as attr; use rustc_data_structures::svh::Svh; @@ -15,7 +8,13 @@ use rustc_data_structures::sync::MetadataRef; use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, DefIndex}; +use rustc_hir::lang_items; use rustc_index::vec::IndexVec; +use rustc_middle::hir::exports::Export; +use rustc_middle::middle::cstore::{DepKind, ForeignModule, LinkagePreference, NativeLibrary}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use rustc_middle::mir; +use rustc_middle::ty::{self, ReprOptions, Ty}; use rustc_serialize::opaque::Encoder; use rustc_session::config::SymbolManglingVersion; use rustc_session::CrateDisambiguator; @@ -194,11 +193,11 @@ crate struct CrateRoot<'tcx> { native_libraries: Lazy<[NativeLibrary]>, foreign_modules: Lazy<[ForeignModule]>, source_map: Lazy<[rustc_span::SourceFile]>, - def_path_table: Lazy, + def_path_table: Lazy, impls: Lazy<[TraitImpls]>, interpret_alloc_index: Lazy<[u32]>, - per_def: LazyPerDefTables<'tcx>, + tables: LazyTables<'tcx>, /// The DefIndex's of any proc macros declared by this crate. proc_macro_data: Option>, @@ -216,7 +215,7 @@ crate struct CrateRoot<'tcx> { #[derive(RustcEncodable, RustcDecodable)] crate struct CrateDep { - pub name: ast::Name, + pub name: Symbol, pub hash: Svh, pub host_hash: Option, pub kind: DepKind, @@ -229,22 +228,22 @@ crate struct TraitImpls { impls: Lazy<[DefIndex]>, } -/// Define `LazyPerDefTables` and `PerDefTableBuilders` at the same time. -macro_rules! define_per_def_tables { +/// Define `LazyTables` and `TableBuilders` at the same time. +macro_rules! define_tables { ($($name:ident: Table),+ $(,)?) => { #[derive(RustcEncodable, RustcDecodable)] - crate struct LazyPerDefTables<'tcx> { + crate struct LazyTables<'tcx> { $($name: Lazy!(Table)),+ } #[derive(Default)] - struct PerDefTableBuilders<'tcx> { + struct TableBuilders<'tcx> { $($name: TableBuilder),+ } - impl PerDefTableBuilders<'tcx> { - fn encode(&self, buf: &mut Encoder) -> LazyPerDefTables<'tcx> { - LazyPerDefTables { + impl TableBuilders<'tcx> { + fn encode(&self, buf: &mut Encoder) -> LazyTables<'tcx> { + LazyTables { $($name: self.$name.encode(buf)),+ } } @@ -252,7 +251,7 @@ macro_rules! define_per_def_tables { } } -define_per_def_tables! { +define_tables! { kind: Table>, visibility: Table>, span: Table>, @@ -276,8 +275,8 @@ define_per_def_tables! { // Also, as an optimization, a missing entry indicates an empty `&[]`. inferred_outlives: Table, Span)])>, super_predicates: Table)>, - mir: Table)>, - promoted_mir: Table>)>, + mir: Table)>, + promoted_mir: Table>)>, } #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] @@ -328,7 +327,7 @@ struct ModData { struct FnData { asyncness: hir::IsAsync, constness: hir::Constness, - param_names: Lazy<[ast::Name]>, + param_names: Lazy<[Symbol]>, } #[derive(RustcEncodable, RustcDecodable)] diff --git a/src/librustc_metadata/rmeta/table.rs b/src/librustc_metadata/rmeta/table.rs index 752caff754eeb..bacb5a345fca9 100644 --- a/src/librustc_metadata/rmeta/table.rs +++ b/src/librustc_metadata/rmeta/table.rs @@ -42,10 +42,7 @@ macro_rules! fixed_size_encoding_byte_len_and_defaults { // but slicing `[u8]` with `i * N..` is optimized worse, due to the // possibility of `i * N` overflowing, than indexing `[[u8; N]]`. let b = unsafe { - std::slice::from_raw_parts( - b.as_ptr() as *const [u8; BYTE_LEN], - b.len() / BYTE_LEN, - ) + std::slice::from_raw_parts(b.as_ptr() as *const [u8; BYTE_LEN], b.len() / BYTE_LEN) }; b.get(i).map(|b| FixedSizeEncoding::from_bytes(b)) } @@ -61,7 +58,7 @@ macro_rules! fixed_size_encoding_byte_len_and_defaults { }; self.write_to_bytes(&mut b[i]); } - } + }; } impl FixedSizeEncoding for u32 { diff --git a/src/librustc_middle/Cargo.toml b/src/librustc_middle/Cargo.toml new file mode 100644 index 0000000000000..0bb32438b7276 --- /dev/null +++ b/src/librustc_middle/Cargo.toml @@ -0,0 +1,36 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_middle" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_middle" +path = "lib.rs" +doctest = false + +[dependencies] +arena = { path = "../libarena" } +bitflags = "1.2.1" +scoped-tls = "1.0" +log = { version = "0.4", features = ["release_max_level_info", "std"] } +rustc-rayon-core = "0.3.0" +polonius-engine = "0.12.0" +rustc_apfloat = { path = "../librustc_apfloat" } +rustc_attr = { path = "../librustc_attr" } +rustc_feature = { path = "../librustc_feature" } +rustc_hir = { path = "../librustc_hir" } +rustc_target = { path = "../librustc_target" } +rustc_macros = { path = "../librustc_macros" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_query_system = { path = "../librustc_query_system" } +rustc_errors = { path = "../librustc_errors" } +rustc_index = { path = "../librustc_index" } +rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_ast = { path = "../librustc_ast" } +rustc_span = { path = "../librustc_span" } +byteorder = { version = "1.3" } +chalk-ir = "0.10.0" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +measureme = "0.7.1" +rustc_session = { path = "../librustc_session" } diff --git a/src/librustc/README.md b/src/librustc_middle/README.md similarity index 100% rename from src/librustc/README.md rename to src/librustc_middle/README.md diff --git a/src/librustc_middle/arena.rs b/src/librustc_middle/arena.rs new file mode 100644 index 0000000000000..a97db3134dc9e --- /dev/null +++ b/src/librustc_middle/arena.rs @@ -0,0 +1,83 @@ +/// This declares a list of types which can be allocated by `Arena`. +/// +/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. +/// This is faster and more memory efficient if there's only a few allocations of the type. +/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is +/// faster and more memory efficient if there is lots of allocations. +/// +/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type +/// listed. These impls will appear in the implement_ty_decoder! macro. +#[macro_export] +macro_rules! arena_types { + ($macro:path, $args:tt, $tcx:lifetime) => ( + $macro!($args, [ + [] layouts: rustc_target::abi::Layout, + // AdtDef are interned and compared by address + [] adt_def: rustc_middle::ty::AdtDef, + [decode] tables: rustc_middle::ty::TypeckTables<$tcx>, + [] const_allocs: rustc_middle::mir::interpret::Allocation, + // Required for the incremental on-disk cache + [few, decode] mir_keys: rustc_hir::def_id::DefIdSet, + [] region_scope_tree: rustc_middle::middle::region::ScopeTree, + [] dropck_outlives: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::DropckOutlivesResult<'tcx> + > + >, + [] normalize_projection_ty: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::NormalizationResult<'tcx> + > + >, + [] implied_outlives_bounds: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + Vec> + > + >, + [] type_op_subtype: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, ()> + >, + [] type_op_normalize_poly_fn_sig: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::PolyFnSig<'tcx>> + >, + [] type_op_normalize_fn_sig: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::FnSig<'tcx>> + >, + [] type_op_normalize_predicate: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Predicate<'tcx>> + >, + [] type_op_normalize_ty: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Ty<'tcx>> + >, + [few] all_traits: Vec, + [few] privacy_access_levels: rustc_middle::middle::privacy::AccessLevels, + [few] foreign_module: rustc_middle::middle::cstore::ForeignModule, + [few] foreign_modules: Vec, + [] upvars: rustc_data_structures::fx::FxIndexMap, + [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, + [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<$tcx>, + [] attribute: rustc_ast::ast::Attribute, + [] name_set: rustc_data_structures::fx::FxHashSet, + [] hir_id_set: rustc_hir::HirIdSet, + + // Interned types + [] tys: rustc_middle::ty::TyS<$tcx>, + + // HIR query types + [few] indexed_hir: rustc_middle::hir::map::IndexedHir<$tcx>, + [few] hir_definitions: rustc_hir::definitions::Definitions, + [] hir_owner: rustc_middle::hir::Owner<$tcx>, + [] hir_owner_nodes: rustc_middle::hir::OwnerNodes<$tcx>, + ], $tcx); + ) +} + +arena_types!(arena::declare_arena, [], 'tcx); diff --git a/src/librustc/benches/lib.rs b/src/librustc_middle/benches/lib.rs similarity index 100% rename from src/librustc/benches/lib.rs rename to src/librustc_middle/benches/lib.rs diff --git a/src/librustc/build.rs b/src/librustc_middle/build.rs similarity index 100% rename from src/librustc/build.rs rename to src/librustc_middle/build.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc_middle/dep_graph/dep_node.rs similarity index 92% rename from src/librustc/dep_graph/dep_node.rs rename to src/librustc_middle/dep_graph/dep_node.rs index fdcc1a0db0538..3303790088010 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc_middle/dep_graph/dep_node.rs @@ -49,8 +49,6 @@ //! user of the `DepNode` API of having to know how to compute the expected //! fingerprint for a given set of node parameters. -use crate::hir::map::DefPathHash; -use crate::ich::Fingerprint; use crate::mir; use crate::mir::interpret::{GlobalId, LitToConstInput}; use crate::traits; @@ -59,10 +57,12 @@ use crate::traits::query::{ CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, }; -use crate::ty::subst::SubstsRef; +use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::definitions::DefPathHash; use rustc_hir::HirId; use rustc_span::symbol::Symbol; use std::hash::Hash; @@ -183,31 +183,10 @@ macro_rules! define_dep_nodes { // tuple args $({ erase!($tuple_arg_ty); - let hash = DepNodeParams::to_fingerprint(&arg, _tcx); - let dep_node = DepNode { - kind: DepKind::$variant, - hash - }; - - #[cfg(debug_assertions)] - { - if !dep_node.kind.can_reconstruct_query_key() && - (_tcx.sess.opts.debugging_opts.incremental_info || - _tcx.sess.opts.debugging_opts.query_dep_graph) - { - _tcx.dep_graph.register_dep_node_debug_str(dep_node, || { - arg.to_debug_str(_tcx) - }); - } - } - - return dep_node; + return DepNode::construct(_tcx, DepKind::$variant, &arg) })* - DepNode { - kind: DepKind::$variant, - hash: Fingerprint::ZERO, - } + return DepNode::construct(_tcx, DepKind::$variant, &()) } )* } diff --git a/src/librustc_middle/dep_graph/mod.rs b/src/librustc_middle/dep_graph/mod.rs new file mode 100644 index 0000000000000..682b335c5d071 --- /dev/null +++ b/src/librustc_middle/dep_graph/mod.rs @@ -0,0 +1,190 @@ +use crate::ich::StableHashingContext; +use crate::ty::query::try_load_from_on_disk_cache; +use crate::ty::{self, TyCtxt}; +use rustc_data_structures::profiling::SelfProfilerRef; +use rustc_data_structures::sync::Lock; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::Diagnostic; +use rustc_hir::def_id::LocalDefId; + +mod dep_node; + +pub(crate) use rustc_query_system::dep_graph::DepNodeParams; +pub use rustc_query_system::dep_graph::{ + debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, + WorkProduct, WorkProductId, +}; + +pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt}; + +pub type DepGraph = rustc_query_system::dep_graph::DepGraph; +pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps; +pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery; +pub type PreviousDepGraph = rustc_query_system::dep_graph::PreviousDepGraph; +pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph; + +impl rustc_query_system::dep_graph::DepKind for DepKind { + const NULL: Self = DepKind::Null; + + fn is_eval_always(&self) -> bool { + DepKind::is_eval_always(self) + } + + fn has_params(&self) -> bool { + DepKind::has_params(self) + } + + fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", node.kind)?; + + if !node.kind.has_params() && !node.kind.is_anon() { + return Ok(()); + } + + write!(f, "(")?; + + ty::tls::with_opt(|opt_tcx| { + if let Some(tcx) = opt_tcx { + if let Some(def_id) = node.extract_def_id(tcx) { + write!(f, "{}", tcx.def_path_debug_str(def_id))?; + } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*node) { + write!(f, "{}", s)?; + } else { + write!(f, "{}", node.hash)?; + } + } else { + write!(f, "{}", node.hash)?; + } + Ok(()) + })?; + + write!(f, ")") + } + + fn with_deps(task_deps: Option<&Lock>, op: OP) -> R + where + OP: FnOnce() -> R, + { + ty::tls::with_context(|icx| { + let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; + + ty::tls::enter_context(&icx, |_| op()) + }) + } + + fn read_deps(op: OP) + where + OP: for<'a> FnOnce(Option<&'a Lock>), + { + ty::tls::with_context_opt(|icx| { + let icx = if let Some(icx) = icx { icx } else { return }; + op(icx.task_deps) + }) + } + + fn can_reconstruct_query_key(&self) -> bool { + DepKind::can_reconstruct_query_key(self) + } +} + +impl<'tcx> DepContext for TyCtxt<'tcx> { + type DepKind = DepKind; + type StableHashingContext = StableHashingContext<'tcx>; + + fn create_stable_hashing_context(&self) -> Self::StableHashingContext { + TyCtxt::create_stable_hashing_context(*self) + } + + fn debug_dep_tasks(&self) -> bool { + self.sess.opts.debugging_opts.dep_tasks + } + fn debug_dep_node(&self) -> bool { + self.sess.opts.debugging_opts.incremental_info + || self.sess.opts.debugging_opts.query_dep_graph + } + + fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool { + // FIXME: This match is just a workaround for incremental bugs and should + // be removed. https://github.com/rust-lang/rust/issues/62649 is one such + // bug that must be fixed before removing this. + match dep_node.kind { + DepKind::hir_owner | DepKind::hir_owner_nodes | DepKind::CrateMetadata => { + if let Some(def_id) = dep_node.extract_def_id(*self) { + if def_id_corresponds_to_hir_dep_node(*self, def_id.expect_local()) { + if dep_node.kind == DepKind::CrateMetadata { + // The `DefPath` has corresponding node, + // and that node should have been marked + // either red or green in `data.colors`. + bug!( + "DepNode {:?} should have been \ + pre-marked as red or green but wasn't.", + dep_node + ); + } + } else { + // This `DefPath` does not have a + // corresponding `DepNode` (e.g. a + // struct field), and the ` DefPath` + // collided with the `DefPath` of a + // proper item that existed in the + // previous compilation session. + // + // Since the given `DefPath` does not + // denote the item that previously + // existed, we just fail to mark green. + return false; + } + } else { + // If the node does not exist anymore, we + // just fail to mark green. + return false; + } + } + _ => { + // For other kinds of nodes it's OK to be + // forced. + } + } + + debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); + ty::query::force_from_dep_node(*self, dep_node) + } + + fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.sess.has_errors_or_delayed_span_bugs() + } + + fn diagnostic(&self) -> &rustc_errors::Handler { + self.sess.diagnostic() + } + + // Interactions with on_disk_cache + fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { + try_load_from_on_disk_cache(*self, dep_node) + } + + fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec { + self.queries.on_disk_cache.load_diagnostics(*self, prev_dep_node_index) + } + + fn store_diagnostics(&self, dep_node_index: DepNodeIndex, diagnostics: ThinVec) { + self.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics) + } + + fn store_diagnostics_for_anon_node( + &self, + dep_node_index: DepNodeIndex, + diagnostics: ThinVec, + ) { + self.queries.on_disk_cache.store_diagnostics_for_anon_node(dep_node_index, diagnostics) + } + + fn profiler(&self) -> &SelfProfilerRef { + &self.prof + } +} + +fn def_id_corresponds_to_hir_dep_node(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let hir_id = tcx.hir().as_local_hir_id(def_id); + def_id == hir_id.owner +} diff --git a/src/librustc/hir/exports.rs b/src/librustc_middle/hir/exports.rs similarity index 94% rename from src/librustc/hir/exports.rs rename to src/librustc_middle/hir/exports.rs index 4c144a54d6345..83baf6cc43345 100644 --- a/src/librustc/hir/exports.rs +++ b/src/librustc_middle/hir/exports.rs @@ -1,9 +1,9 @@ use crate::ty; -use rustc_ast::ast; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; use rustc_macros::HashStable; +use rustc_span::symbol::Ident; use rustc_span::Span; use std::fmt::Debug; @@ -15,7 +15,7 @@ pub type ExportMap = DefIdMap>>; #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct Export { /// The name of the target. - pub ident: ast::Ident, + pub ident: Ident, /// The resolution of the target. pub res: Res, /// The span of the target. diff --git a/src/librustc_middle/hir/map/blocks.rs b/src/librustc_middle/hir/map/blocks.rs new file mode 100644 index 0000000000000..a2e4372f017ce --- /dev/null +++ b/src/librustc_middle/hir/map/blocks.rs @@ -0,0 +1,263 @@ +//! This module provides a simplified abstraction for working with +//! code blocks identified by their integer `NodeId`. In particular, +//! it captures a common set of attributes that all "function-like +//! things" (represented by `FnLike` instances) share. For example, +//! all `FnLike` instances have a type signature (be it explicit or +//! inferred). And all `FnLike` instances have a body, i.e., the code +//! that is run when the function-like thing it represents is invoked. +//! +//! With the above abstraction in place, one can treat the program +//! text as a collection of blocks of code (and most such blocks are +//! nested within a uniquely determined `FnLike`), and users can ask +//! for the `Code` associated with a particular NodeId. + +use crate::hir::map::Map; +use rustc_ast::ast::Attribute; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Expr, FnDecl, Node}; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +/// An FnLikeNode is a Node that is like a fn, in that it has a decl +/// and a body (as well as a NodeId, a span, etc). +/// +/// More specifically, it is one of either: +/// +/// - A function item, +/// - A closure expr (i.e., an ExprKind::Closure), or +/// - The default implementation for a trait method. +/// +/// To construct one, use the `Code::from_node` function. +#[derive(Copy, Clone, Debug)] +pub struct FnLikeNode<'a> { + node: Node<'a>, +} + +/// MaybeFnLike wraps a method that indicates if an object +/// corresponds to some FnLikeNode. +trait MaybeFnLike { + fn is_fn_like(&self) -> bool; +} + +impl MaybeFnLike for hir::Item<'_> { + fn is_fn_like(&self) -> bool { + match self.kind { + hir::ItemKind::Fn(..) => true, + _ => false, + } + } +} + +impl MaybeFnLike for hir::ImplItem<'_> { + fn is_fn_like(&self) -> bool { + match self.kind { + hir::ImplItemKind::Fn(..) => true, + _ => false, + } + } +} + +impl MaybeFnLike for hir::TraitItem<'_> { + fn is_fn_like(&self) -> bool { + match self.kind { + hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => true, + _ => false, + } + } +} + +impl MaybeFnLike for hir::Expr<'_> { + fn is_fn_like(&self) -> bool { + match self.kind { + hir::ExprKind::Closure(..) => true, + _ => false, + } + } +} + +/// Carries either an FnLikeNode or a Expr, as these are the two +/// constructs that correspond to "code" (as in, something from which +/// we can construct a control-flow graph). +#[derive(Copy, Clone)] +pub enum Code<'a> { + FnLike(FnLikeNode<'a>), + Expr(&'a Expr<'a>), +} + +impl<'a> Code<'a> { + pub fn id(&self) -> hir::HirId { + match *self { + Code::FnLike(node) => node.id(), + Code::Expr(block) => block.hir_id, + } + } + + /// Attempts to construct a Code from presumed FnLike or Expr node input. + pub fn from_node(map: &Map<'a>, id: hir::HirId) -> Option> { + match map.get(id) { + Node::Block(_) => { + // Use the parent, hopefully an expression node. + Code::from_node(map, map.get_parent_node(id)) + } + Node::Expr(expr) => Some(Code::Expr(expr)), + node => FnLikeNode::from_node(node).map(Code::FnLike), + } + } +} + +/// These are all the components one can extract from a fn item for +/// use when implementing FnLikeNode operations. +struct ItemFnParts<'a> { + ident: Ident, + decl: &'a hir::FnDecl<'a>, + header: hir::FnHeader, + vis: &'a hir::Visibility<'a>, + generics: &'a hir::Generics<'a>, + body: hir::BodyId, + id: hir::HirId, + span: Span, + attrs: &'a [Attribute], +} + +/// These are all the components one can extract from a closure expr +/// for use when implementing FnLikeNode operations. +struct ClosureParts<'a> { + decl: &'a FnDecl<'a>, + body: hir::BodyId, + id: hir::HirId, + span: Span, + attrs: &'a [Attribute], +} + +impl<'a> ClosureParts<'a> { + fn new( + d: &'a FnDecl<'a>, + b: hir::BodyId, + id: hir::HirId, + s: Span, + attrs: &'a [Attribute], + ) -> Self { + ClosureParts { decl: d, body: b, id, span: s, attrs } + } +} + +impl<'a> FnLikeNode<'a> { + /// Attempts to construct a FnLikeNode from presumed FnLike node input. + pub fn from_node(node: Node<'_>) -> Option> { + let fn_like = match node { + Node::Item(item) => item.is_fn_like(), + Node::TraitItem(tm) => tm.is_fn_like(), + Node::ImplItem(it) => it.is_fn_like(), + Node::Expr(e) => e.is_fn_like(), + _ => false, + }; + fn_like.then_some(FnLikeNode { node }) + } + + pub fn body(self) -> hir::BodyId { + self.handle( + |i: ItemFnParts<'a>| i.body, + |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _, _| body, + |c: ClosureParts<'a>| c.body, + ) + } + + pub fn decl(self) -> &'a FnDecl<'a> { + self.handle( + |i: ItemFnParts<'a>| &*i.decl, + |_, _, sig: &'a hir::FnSig<'a>, _, _, _, _| &sig.decl, + |c: ClosureParts<'a>| c.decl, + ) + } + + pub fn span(self) -> Span { + self.handle( + |i: ItemFnParts<'_>| i.span, + |_, _, _: &'a hir::FnSig<'a>, _, _, span, _| span, + |c: ClosureParts<'_>| c.span, + ) + } + + pub fn id(self) -> hir::HirId { + self.handle( + |i: ItemFnParts<'_>| i.id, + |id, _, _: &'a hir::FnSig<'a>, _, _, _, _| id, + |c: ClosureParts<'_>| c.id, + ) + } + + pub fn constness(self) -> hir::Constness { + self.kind().header().map_or(hir::Constness::NotConst, |header| header.constness) + } + + pub fn asyncness(self) -> hir::IsAsync { + self.kind().header().map_or(hir::IsAsync::NotAsync, |header| header.asyncness) + } + + pub fn unsafety(self) -> hir::Unsafety { + self.kind().header().map_or(hir::Unsafety::Normal, |header| header.unsafety) + } + + pub fn kind(self) -> FnKind<'a> { + let item = |p: ItemFnParts<'a>| -> FnKind<'a> { + FnKind::ItemFn(p.ident, p.generics, p.header, p.vis, p.attrs) + }; + let closure = |c: ClosureParts<'a>| FnKind::Closure(c.attrs); + let method = |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _, attrs| { + FnKind::Method(ident, sig, vis, attrs) + }; + self.handle(item, method, closure) + } + + fn handle(self, item_fn: I, method: M, closure: C) -> A + where + I: FnOnce(ItemFnParts<'a>) -> A, + M: FnOnce( + hir::HirId, + Ident, + &'a hir::FnSig<'a>, + Option<&'a hir::Visibility<'a>>, + hir::BodyId, + Span, + &'a [Attribute], + ) -> A, + C: FnOnce(ClosureParts<'a>) -> A, + { + match self.node { + Node::Item(i) => match i.kind { + hir::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts { + id: i.hir_id, + ident: i.ident, + decl: &sig.decl, + body: block, + vis: &i.vis, + span: i.span, + attrs: &i.attrs, + header: sig.header, + generics, + }), + _ => bug!("item FnLikeNode that is not fn-like"), + }, + Node::TraitItem(ti) => match ti.kind { + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + method(ti.hir_id, ti.ident, sig, None, body, ti.span, &ti.attrs) + } + _ => bug!("trait method FnLikeNode that is not fn-like"), + }, + Node::ImplItem(ii) => match ii.kind { + hir::ImplItemKind::Fn(ref sig, body) => { + method(ii.hir_id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs) + } + _ => bug!("impl method FnLikeNode that is not fn-like"), + }, + Node::Expr(e) => match e.kind { + hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => { + closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs)) + } + _ => bug!("expr FnLikeNode that is not fn-like"), + }, + _ => bug!("other FnLikeNode that is not fn-like"), + } + } +} diff --git a/src/librustc/hir/map/collector.rs b/src/librustc_middle/hir/map/collector.rs similarity index 96% rename from src/librustc/hir/map/collector.rs rename to src/librustc_middle/hir/map/collector.rs index c3bc9490d294e..2906da437abac 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc_middle/hir/map/collector.rs @@ -1,5 +1,4 @@ use crate::arena::Arena; -use crate::hir::map::definitions::{self, DefPathHash}; use crate::hir::map::{Entry, HirOwnerData, Map}; use crate::hir::{Owner, OwnerNodes, ParentedNode}; use crate::ich::StableHashingContext; @@ -11,6 +10,7 @@ use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_INDEX; use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::{self, DefPathHash}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::*; use rustc_index::vec::{Idx, IndexVec}; @@ -241,8 +241,8 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { // Make sure that the DepNode of some node coincides with the HirId // owner of that node. if cfg!(debug_assertions) { - let node_id = self.definitions.hir_to_node_id(hir_id); - assert_eq!(self.definitions.node_to_hir_id(node_id), hir_id); + let node_id = self.definitions.hir_id_to_node_id(hir_id); + assert_eq!(self.definitions.node_id_to_hir_id(node_id), hir_id); if hir_id.owner != self.current_dep_node_owner { let node_str = match self.definitions.opt_local_def_id(node_id) { @@ -250,23 +250,16 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { None => format!("{:?}", node), }; - let forgot_str = if hir_id == hir::DUMMY_HIR_ID { - format!("\nMaybe you forgot to lower the node id {:?}?", node_id) - } else { - String::new() - }; - span_bug!( span, "inconsistent DepNode at `{:?}` for `{}`: \ - current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?}){}", + current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})", self.source_map.span_to_string(span), node_str, self.definitions.def_path(self.current_dep_node_owner).to_string_no_crate(), self.current_dep_node_owner, self.definitions.def_path(hir_id.owner).to_string_no_crate(), hir_id.owner, - forgot_str, ) } } @@ -342,7 +335,9 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { debug!("visit_item: {:?}", i); debug_assert_eq!( i.hir_id.owner, - self.definitions.opt_local_def_id(self.definitions.hir_to_node_id(i.hir_id)).unwrap() + self.definitions + .opt_local_def_id(self.definitions.hir_id_to_node_id(i.hir_id)) + .unwrap() ); self.with_dep_node_owner(i.hir_id.owner, i, |this, hash| { this.insert_with_hash(i.span, i.hir_id, Node::Item(i), hash); @@ -374,7 +369,9 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) { debug_assert_eq!( ti.hir_id.owner, - self.definitions.opt_local_def_id(self.definitions.hir_to_node_id(ti.hir_id)).unwrap() + self.definitions + .opt_local_def_id(self.definitions.hir_id_to_node_id(ti.hir_id)) + .unwrap() ); self.with_dep_node_owner(ti.hir_id.owner, ti, |this, hash| { this.insert_with_hash(ti.span, ti.hir_id, Node::TraitItem(ti), hash); @@ -388,7 +385,9 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) { debug_assert_eq!( ii.hir_id.owner, - self.definitions.opt_local_def_id(self.definitions.hir_to_node_id(ii.hir_id)).unwrap() + self.definitions + .opt_local_def_id(self.definitions.hir_id_to_node_id(ii.hir_id)) + .unwrap() ); self.with_dep_node_owner(ii.hir_id.owner, ii, |this, hash| { this.insert_with_hash(ii.span, ii.hir_id, Node::ImplItem(ii), hash); diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs new file mode 100644 index 0000000000000..b823516d64f3b --- /dev/null +++ b/src/librustc_middle/hir/map/mod.rs @@ -0,0 +1,1110 @@ +use self::collector::NodeCollector; + +use crate::hir::{Owner, OwnerNodes}; +use crate::ty::query::Providers; +use crate::ty::TyCtxt; +use rustc_ast::ast::{self, NodeId}; +use rustc_data_structures::svh::Svh; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::{DefKey, DefPath, Definitions}; +use rustc_hir::intravisit; +use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::*; +use rustc_index::vec::IndexVec; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{kw, Symbol}; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; + +pub mod blocks; +mod collector; + +/// Represents an entry and its parent `HirId`. +#[derive(Copy, Clone, Debug)] +pub struct Entry<'hir> { + parent: HirId, + node: Node<'hir>, +} + +impl<'hir> Entry<'hir> { + fn parent_node(self) -> Option { + match self.node { + Node::Crate(_) | Node::MacroDef(_) => None, + _ => Some(self.parent), + } + } +} + +fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { + match node { + Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl), + Node::Expr(Expr { kind: ExprKind::Closure(_, fn_decl, ..), .. }) => Some(fn_decl), + _ => None, + } +} + +fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { + match &node { + Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(sig), + _ => None, + } +} + +fn associated_body<'hir>(node: Node<'hir>) -> Option { + match node { + Node::Item(Item { + kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), + .. + }) + | Node::TraitItem(TraitItem { + kind: + TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)), + .. + }) + | Node::ImplItem(ImplItem { + kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), + .. + }) + | Node::Expr(Expr { kind: ExprKind::Closure(.., body, _, _), .. }) => Some(*body), + + Node::AnonConst(constant) => Some(constant.body), + + _ => None, + } +} + +fn is_body_owner<'hir>(node: Node<'hir>, hir_id: HirId) -> bool { + match associated_body(node) { + Some(b) => b.hir_id == hir_id, + None => false, + } +} + +pub(super) struct HirOwnerData<'hir> { + pub(super) signature: Option<&'hir Owner<'hir>>, + pub(super) with_bodies: Option<&'hir mut OwnerNodes<'hir>>, +} + +pub struct IndexedHir<'hir> { + /// The SVH of the local crate. + pub crate_hash: Svh, + + pub(super) map: IndexVec>, +} + +#[derive(Copy, Clone)] +pub struct Map<'hir> { + pub(super) tcx: TyCtxt<'hir>, +} + +/// An iterator that walks up the ancestor tree of a given `HirId`. +/// Constructed using `tcx.hir().parent_iter(hir_id)`. +pub struct ParentHirIterator<'map, 'hir> { + current_id: HirId, + map: &'map Map<'hir>, +} + +impl<'hir> Iterator for ParentHirIterator<'_, 'hir> { + type Item = (HirId, Node<'hir>); + + fn next(&mut self) -> Option { + if self.current_id == CRATE_HIR_ID { + return None; + } + loop { + // There are nodes that do not have entries, so we need to skip them. + let parent_id = self.map.get_parent_node(self.current_id); + + if parent_id == self.current_id { + self.current_id = CRATE_HIR_ID; + return None; + } + + self.current_id = parent_id; + if let Some(entry) = self.map.find_entry(parent_id) { + return Some((parent_id, entry.node)); + } + // If this `HirId` doesn't have an `Entry`, skip it and look for its `parent_id`. + } + } +} + +impl<'hir> Map<'hir> { + pub fn krate(&self) -> &'hir Crate<'hir> { + self.tcx.hir_crate(LOCAL_CRATE) + } + + #[inline] + pub fn definitions(&self) -> &'hir Definitions { + &self.tcx.definitions + } + + pub fn def_key(&self, def_id: LocalDefId) -> DefKey { + self.tcx.definitions.def_key(def_id) + } + + pub fn def_path_from_hir_id(&self, id: HirId) -> Option { + self.opt_local_def_id(id).map(|def_id| self.def_path(def_id)) + } + + pub fn def_path(&self, def_id: LocalDefId) -> DefPath { + self.tcx.definitions.def_path(def_id) + } + + #[inline] + pub fn local_def_id_from_node_id(&self, node: NodeId) -> LocalDefId { + self.opt_local_def_id_from_node_id(node).unwrap_or_else(|| { + let hir_id = self.node_id_to_hir_id(node); + bug!( + "local_def_id_from_node_id: no entry for `{}`, which has a map of `{:?}`", + node, + self.find_entry(hir_id) + ) + }) + } + + // FIXME(eddyb) this function can and should return `LocalDefId`. + #[inline] + pub fn local_def_id(&self, hir_id: HirId) -> LocalDefId { + self.opt_local_def_id(hir_id).unwrap_or_else(|| { + bug!( + "local_def_id: no entry for `{:?}`, which has a map of `{:?}`", + hir_id, + self.find_entry(hir_id) + ) + }) + } + + #[inline] + pub fn opt_local_def_id(&self, hir_id: HirId) -> Option { + let node_id = self.hir_id_to_node_id(hir_id); + self.opt_local_def_id_from_node_id(node_id) + } + + #[inline] + pub fn opt_local_def_id_from_node_id(&self, node: NodeId) -> Option { + self.tcx.definitions.opt_local_def_id(node) + } + + #[inline] + pub fn as_local_node_id(&self, def_id: DefId) -> Option { + self.tcx.definitions.as_local_node_id(def_id) + } + + #[inline] + pub fn as_local_hir_id(&self, def_id: LocalDefId) -> HirId { + self.tcx.definitions.as_local_hir_id(def_id) + } + + #[inline] + pub fn hir_id_to_node_id(&self, hir_id: HirId) -> NodeId { + self.tcx.definitions.hir_id_to_node_id(hir_id) + } + + #[inline] + pub fn node_id_to_hir_id(&self, node_id: NodeId) -> HirId { + self.tcx.definitions.node_id_to_hir_id(node_id) + } + + #[inline] + pub fn opt_node_id_to_hir_id(&self, node_id: NodeId) -> Option { + self.tcx.definitions.opt_node_id_to_hir_id(node_id) + } + + #[inline] + pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { + self.tcx.definitions.local_def_id_to_hir_id(def_id) + } + + #[inline] + pub fn opt_local_def_id_to_hir_id(&self, def_id: LocalDefId) -> Option { + self.tcx.definitions.opt_local_def_id_to_hir_id(def_id) + } + + pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind { + // FIXME(eddyb) support `find` on the crate root. + if local_def_id.to_def_id().index == CRATE_DEF_INDEX { + return DefKind::Mod; + } + + let hir_id = self.local_def_id_to_hir_id(local_def_id); + match self.get(hir_id) { + Node::Item(item) => match item.kind { + ItemKind::Static(..) => DefKind::Static, + ItemKind::Const(..) => DefKind::Const, + ItemKind::Fn(..) => DefKind::Fn, + ItemKind::Mod(..) => DefKind::Mod, + ItemKind::OpaqueTy(..) => DefKind::OpaqueTy, + ItemKind::TyAlias(..) => DefKind::TyAlias, + ItemKind::Enum(..) => DefKind::Enum, + ItemKind::Struct(..) => DefKind::Struct, + ItemKind::Union(..) => DefKind::Union, + ItemKind::Trait(..) => DefKind::Trait, + ItemKind::TraitAlias(..) => DefKind::TraitAlias, + ItemKind::ExternCrate(_) => DefKind::ExternCrate, + ItemKind::Use(..) => DefKind::Use, + ItemKind::ForeignMod(..) => DefKind::ForeignMod, + ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, + ItemKind::Impl { .. } => DefKind::Impl, + }, + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Fn(..) => DefKind::Fn, + ForeignItemKind::Static(..) => DefKind::Static, + ForeignItemKind::Type => DefKind::ForeignTy, + }, + Node::TraitItem(item) => match item.kind { + TraitItemKind::Const(..) => DefKind::AssocConst, + TraitItemKind::Fn(..) => DefKind::AssocFn, + TraitItemKind::Type(..) => DefKind::AssocTy, + }, + Node::ImplItem(item) => match item.kind { + ImplItemKind::Const(..) => DefKind::AssocConst, + ImplItemKind::Fn(..) => DefKind::AssocFn, + ImplItemKind::TyAlias(..) => DefKind::AssocTy, + ImplItemKind::OpaqueTy(..) => DefKind::AssocOpaqueTy, + }, + Node::Variant(_) => DefKind::Variant, + Node::Ctor(variant_data) => { + // FIXME(eddyb) is this even possible, if we have a `Node::Ctor`? + assert_ne!(variant_data.ctor_hir_id(), None); + + let ctor_of = match self.find(self.get_parent_node(hir_id)) { + Some(Node::Item(..)) => def::CtorOf::Struct, + Some(Node::Variant(..)) => def::CtorOf::Variant, + _ => unreachable!(), + }; + DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data)) + } + Node::AnonConst(_) => DefKind::AnonConst, + Node::Field(_) => DefKind::Field, + Node::Expr(expr) => match expr.kind { + ExprKind::Closure(.., None) => DefKind::Closure, + ExprKind::Closure(.., Some(_)) => DefKind::Generator, + _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), + }, + Node::MacroDef(_) => DefKind::Macro(MacroKind::Bang), + Node::GenericParam(param) => match param.kind { + GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, + GenericParamKind::Type { .. } => DefKind::TyParam, + GenericParamKind::Const { .. } => DefKind::ConstParam, + }, + Node::Stmt(_) + | Node::PathSegment(_) + | Node::Ty(_) + | Node::TraitRef(_) + | Node::Pat(_) + | Node::Binding(_) + | Node::Local(_) + | Node::Param(_) + | Node::Arm(_) + | Node::Lifetime(_) + | Node::Visibility(_) + | Node::Block(_) + | Node::Crate(_) => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), + } + } + + fn find_entry(&self, id: HirId) -> Option> { + if id.local_id == ItemLocalId::from_u32(0) { + let owner = self.tcx.hir_owner(id.owner); + owner.map(|owner| Entry { parent: owner.parent, node: owner.node }) + } else { + let owner = self.tcx.hir_owner_nodes(id.owner); + owner.and_then(|owner| { + let node = owner.nodes[id.local_id].as_ref(); + // FIXME(eddyb) use a single generic type insted of having both + // `Entry` and `ParentedNode`, which are effectively the same. + // Alternatively, rewrite code using `Entry` to use `ParentedNode`. + node.map(|node| Entry { + parent: HirId { owner: id.owner, local_id: node.parent }, + node: node.node, + }) + }) + } + } + + fn get_entry(&self, id: HirId) -> Entry<'hir> { + self.find_entry(id).unwrap() + } + + pub fn item(&self, id: HirId) -> &'hir Item<'hir> { + match self.find(id).unwrap() { + Node::Item(item) => item, + _ => bug!(), + } + } + + pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + match self.find(id.hir_id).unwrap() { + Node::TraitItem(item) => item, + _ => bug!(), + } + } + + pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + match self.find(id.hir_id).unwrap() { + Node::ImplItem(item) => item, + _ => bug!(), + } + } + + pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { + self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies.get(&id.hir_id.local_id).unwrap() + } + + pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { + if let Some(node) = self.find(hir_id) { + fn_decl(node) + } else { + bug!("no node for hir_id `{}`", hir_id) + } + } + + pub fn fn_sig_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { + if let Some(node) = self.find(hir_id) { + fn_sig(node) + } else { + bug!("no node for hir_id `{}`", hir_id) + } + } + + /// Returns the `HirId` that corresponds to the definition of + /// which this is the body of, i.e., a `fn`, `const` or `static` + /// item (possibly associated), a closure, or a `hir::AnonConst`. + pub fn body_owner(&self, BodyId { hir_id }: BodyId) -> HirId { + let parent = self.get_parent_node(hir_id); + assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id))); + parent + } + + pub fn body_owner_def_id(&self, id: BodyId) -> LocalDefId { + self.local_def_id(self.body_owner(id)) + } + + /// Given a `HirId`, returns the `BodyId` associated with it, + /// if the node is a body owner, otherwise returns `None`. + pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { + self.find(hir_id).map(associated_body).flatten() + } + + /// Given a body owner's id, returns the `BodyId` associated with it. + pub fn body_owned_by(&self, id: HirId) -> BodyId { + self.maybe_body_owned_by(id).unwrap_or_else(|| { + span_bug!( + self.span(id), + "body_owned_by: {} has no associated body", + self.node_to_string(id) + ); + }) + } + + /// Returns the `BodyOwnerKind` of this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. + pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { + match self.get(id) { + Node::Item(&Item { kind: ItemKind::Const(..), .. }) + | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) + | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), .. }) + | Node::AnonConst(_) => BodyOwnerKind::Const, + Node::Ctor(..) + | Node::Item(&Item { kind: ItemKind::Fn(..), .. }) + | Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) + | Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(..), .. }) => BodyOwnerKind::Fn, + Node::Item(&Item { kind: ItemKind::Static(_, m, _), .. }) => BodyOwnerKind::Static(m), + Node::Expr(&Expr { kind: ExprKind::Closure(..), .. }) => BodyOwnerKind::Closure, + node => bug!("{:#?} is not a body node", node), + } + } + + /// Returns the `ConstContext` of the body associated with this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. + pub fn body_const_context(&self, did: LocalDefId) -> Option { + let hir_id = self.local_def_id_to_hir_id(did); + let ccx = match self.body_owner_kind(hir_id) { + BodyOwnerKind::Const => ConstContext::Const, + BodyOwnerKind::Static(mt) => ConstContext::Static(mt), + + BodyOwnerKind::Fn if self.tcx.is_constructor(did.to_def_id()) => return None, + BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(did.to_def_id()) => ConstContext::ConstFn, + BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None, + }; + + Some(ccx) + } + + pub fn ty_param_owner(&self, id: HirId) -> HirId { + match self.get(id) { + Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => id, + Node::GenericParam(_) => self.get_parent_node(id), + _ => bug!("ty_param_owner: {} not a type parameter", self.node_to_string(id)), + } + } + + pub fn ty_param_name(&self, id: HirId) -> Symbol { + match self.get(id) { + Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => { + kw::SelfUpper + } + Node::GenericParam(param) => param.name.ident().name, + _ => bug!("ty_param_name: {} not a type parameter", self.node_to_string(id)), + } + } + + pub fn trait_impls(&self, trait_did: DefId) -> &'hir [HirId] { + self.tcx.all_local_trait_impls(LOCAL_CRATE).get(&trait_did).map_or(&[], |xs| &xs[..]) + } + + /// Gets the attributes on the crate. This is preferable to + /// invoking `krate.attrs` because it registers a tighter + /// dep-graph access. + pub fn krate_attrs(&self) -> &'hir [ast::Attribute] { + match self.get_entry(CRATE_HIR_ID).node { + Node::Crate(item) => item.attrs, + _ => bug!(), + } + } + + pub fn get_module(&self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) { + let hir_id = self.as_local_hir_id(module); + match self.get_entry(hir_id).node { + Node::Item(&Item { span, kind: ItemKind::Mod(ref m), .. }) => (m, span, hir_id), + Node::Crate(item) => (&item.module, item.span, hir_id), + node => panic!("not a module: {:?}", node), + } + } + + pub fn visit_item_likes_in_module(&self, module: DefId, visitor: &mut V) + where + V: ItemLikeVisitor<'hir>, + { + let module = self.tcx.hir_module_items(module.expect_local()); + + for id in &module.items { + visitor.visit_item(self.expect_item(*id)); + } + + for id in &module.trait_items { + visitor.visit_trait_item(self.expect_trait_item(id.hir_id)); + } + + for id in &module.impl_items { + visitor.visit_impl_item(self.expect_impl_item(id.hir_id)); + } + } + + /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. + pub fn get(&self, id: HirId) -> Node<'hir> { + self.find(id).unwrap_or_else(|| bug!("couldn't find hir id {} in the HIR map", id)) + } + + pub fn get_if_local(&self, id: DefId) -> Option> { + id.as_local().map(|id| self.get(self.as_local_hir_id(id))) + } + + pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics<'hir>> { + self.get_if_local(id).and_then(|node| match &node { + Node::ImplItem(impl_item) => Some(&impl_item.generics), + Node::TraitItem(trait_item) => Some(&trait_item.generics), + Node::Item(Item { + kind: + ItemKind::Fn(_, generics, _) + | ItemKind::TyAlias(_, generics) + | ItemKind::Enum(_, generics) + | ItemKind::Struct(_, generics) + | ItemKind::Union(_, generics) + | ItemKind::Trait(_, _, generics, ..) + | ItemKind::TraitAlias(generics, _) + | ItemKind::Impl { generics, .. }, + .. + }) => Some(generics), + _ => None, + }) + } + + /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. + pub fn find(&self, hir_id: HirId) -> Option> { + self.find_entry(hir_id).and_then(|entry| { + if let Node::Crate(..) = entry.node { None } else { Some(entry.node) } + }) + } + + /// Similar to `get_parent`; returns the parent HIR Id, or just `hir_id` if there + /// is no parent. Note that the parent may be `CRATE_HIR_ID`, which is not itself + /// present in the map, so passing the return value of `get_parent_node` to + /// `get` may in fact panic. + /// This function returns the immediate parent in the HIR, whereas `get_parent` + /// returns the enclosing item. Note that this might not be the actual parent + /// node in the HIR -- some kinds of nodes are not in the map and these will + /// never appear as the parent node. Thus, you can always walk the parent nodes + /// from a node to the root of the HIR (unless you get back the same ID here, + /// which can happen if the ID is not in the map itself or is just weird). + pub fn get_parent_node(&self, hir_id: HirId) -> HirId { + self.get_entry(hir_id).parent_node().unwrap_or(hir_id) + } + + /// Returns an iterator for the nodes in the ancestor tree of the `current_id` + /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`. + pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> { + ParentHirIterator { current_id, map: self } + } + + /// Checks if the node is an argument. An argument is a local variable whose + /// immediate parent is an item or a closure. + pub fn is_argument(&self, id: HirId) -> bool { + match self.find(id) { + Some(Node::Binding(_)) => (), + _ => return false, + } + match self.find(self.get_parent_node(id)) { + Some( + Node::Item(_) + | Node::TraitItem(_) + | Node::ImplItem(_) + | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), + ) => true, + _ => false, + } + } + + /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. + /// Used exclusively for diagnostics, to avoid suggestion function calls. + pub fn is_const_context(&self, hir_id: HirId) -> bool { + let parent_id = self.get_parent_item(hir_id); + match self.get(parent_id) { + Node::Item(&Item { kind: ItemKind::Const(..) | ItemKind::Static(..), .. }) + | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) + | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), .. }) + | Node::AnonConst(_) => true, + Node::Item(&Item { kind: ItemKind::Fn(ref sig, ..), .. }) => { + sig.header.constness == Constness::Const + } + _ => false, + } + } + + /// Whether `hir_id` corresponds to a `mod` or a crate. + pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { + match self.get_entry(hir_id).node { + Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) => true, + _ => false, + } + } + + /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a + /// `while` or `loop` before reaching it, as block tail returns are not + /// available in them. + /// + /// ``` + /// fn foo(x: usize) -> bool { + /// if x == 1 { + /// true // If `get_return_block` gets passed the `id` corresponding + /// } else { // to this, it will return `foo`'s `HirId`. + /// false + /// } + /// } + /// ``` + /// + /// ``` + /// fn foo(x: usize) -> bool { + /// loop { + /// true // If `get_return_block` gets passed the `id` corresponding + /// } // to this, it will return `None`. + /// false + /// } + /// ``` + pub fn get_return_block(&self, id: HirId) -> Option { + let mut iter = self.parent_iter(id).peekable(); + let mut ignore_tail = false; + if let Some(entry) = self.find_entry(id) { + if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = entry.node { + // When dealing with `return` statements, we don't care about climbing only tail + // expressions. + ignore_tail = true; + } + } + while let Some((hir_id, node)) = iter.next() { + if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { + match next_node { + Node::Block(Block { expr: None, .. }) => return None, + // The current node is not the tail expression of its parent. + Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None, + _ => {} + } + } + match node { + Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }) + | Node::ImplItem(_) => return Some(hir_id), + // Ignore `return`s on the first iteration + Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) + | Node::Local(_) => { + return None; + } + _ => {} + } + } + None + } + + /// Retrieves the `HirId` for `id`'s parent item, or `id` itself if no + /// parent item is in this map. The "parent item" is the closest parent node + /// in the HIR which is recorded by the map and is an item, either an item + /// in a module, trait, or impl. + pub fn get_parent_item(&self, hir_id: HirId) -> HirId { + for (hir_id, node) in self.parent_iter(hir_id) { + match node { + Node::Crate(_) + | Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::ImplItem(_) => return hir_id, + _ => {} + } + } + hir_id + } + + /// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no + /// module parent is in this map. + pub(super) fn get_module_parent_node(&self, hir_id: HirId) -> HirId { + for (hir_id, node) in self.parent_iter(hir_id) { + if let Node::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { + return hir_id; + } + } + CRATE_HIR_ID + } + + /// When on a match arm tail expression or on a match arm, give back the enclosing `match` + /// expression. + /// + /// Used by error reporting when there's a type error in a match arm caused by the `match` + /// expression needing to be unit. + pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> { + for (_, node) in self.parent_iter(hir_id) { + match node { + Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::ImplItem(_) + | Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break, + Node::Expr(expr @ Expr { kind: ExprKind::Match(..), .. }) => return Some(expr), + _ => {} + } + } + None + } + + /// Returns the nearest enclosing scope. A scope is roughly an item or block. + pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option { + for (hir_id, node) in self.parent_iter(hir_id) { + if let Node::Item(Item { + kind: + ItemKind::Fn(..) + | ItemKind::Mod(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Trait(..) + | ItemKind::Impl { .. }, + .. + }) + | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(..), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(..), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(..), .. }) + | Node::Block(_) = node + { + return Some(hir_id); + } + } + None + } + + /// Returns the defining scope for an opaque type definition. + pub fn get_defining_scope(&self, id: HirId) -> HirId { + let mut scope = id; + loop { + scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); + if scope == CRATE_HIR_ID { + return CRATE_HIR_ID; + } + match self.get(scope) { + Node::Item(Item { + kind: ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }), + .. + }) + | Node::Block(_) => {} + _ => break, + } + } + scope + } + + pub fn get_parent_did(&self, id: HirId) -> LocalDefId { + self.local_def_id(self.get_parent_item(id)) + } + + pub fn get_foreign_abi(&self, hir_id: HirId) -> Abi { + let parent = self.get_parent_item(hir_id); + if let Some(entry) = self.find_entry(parent) { + if let Entry { + node: Node::Item(Item { kind: ItemKind::ForeignMod(ref nm), .. }), .. + } = entry + { + return nm.abi; + } + } + bug!("expected foreign mod or inlined parent, found {}", self.node_to_string(parent)) + } + + pub fn expect_item(&self, id: HirId) -> &'hir Item<'hir> { + match self.find(id) { + Some(Node::Item(item)) => item, + _ => bug!("expected item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_impl_item(&self, id: HirId) -> &'hir ImplItem<'hir> { + match self.find(id) { + Some(Node::ImplItem(item)) => item, + _ => bug!("expected impl item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_trait_item(&self, id: HirId) -> &'hir TraitItem<'hir> { + match self.find(id) { + Some(Node::TraitItem(item)) => item, + _ => bug!("expected trait item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_variant_data(&self, id: HirId) -> &'hir VariantData<'hir> { + match self.find(id) { + Some( + Node::Ctor(vd) + | Node::Item(Item { kind: ItemKind::Struct(vd, _) | ItemKind::Union(vd, _), .. }), + ) => vd, + Some(Node::Variant(variant)) => &variant.data, + _ => bug!("expected struct or variant, found {}", self.node_to_string(id)), + } + } + + pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> { + match self.find(id) { + Some(Node::Variant(variant)) => variant, + _ => bug!("expected variant, found {}", self.node_to_string(id)), + } + } + + pub fn expect_foreign_item(&self, id: HirId) -> &'hir ForeignItem<'hir> { + match self.find(id) { + Some(Node::ForeignItem(item)) => item, + _ => bug!("expected foreign item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> { + match self.find(id) { + Some(Node::Expr(expr)) => expr, + _ => bug!("expected expr, found {}", self.node_to_string(id)), + } + } + + pub fn opt_name(&self, id: HirId) -> Option { + Some(match self.get(id) { + Node::Item(i) => i.ident.name, + Node::ForeignItem(fi) => fi.ident.name, + Node::ImplItem(ii) => ii.ident.name, + Node::TraitItem(ti) => ti.ident.name, + Node::Variant(v) => v.ident.name, + Node::Field(f) => f.ident.name, + Node::Lifetime(lt) => lt.name.ident().name, + Node::GenericParam(param) => param.name.ident().name, + Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name, + Node::Ctor(..) => self.name(self.get_parent_item(id)), + _ => return None, + }) + } + + pub fn name(&self, id: HirId) -> Symbol { + match self.opt_name(id) { + Some(name) => name, + None => bug!("no name for {}", self.node_to_string(id)), + } + } + + /// Given a node ID, gets a list of attributes associated with the AST + /// corresponding to the node-ID. + pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { + let attrs = match self.find_entry(id).map(|entry| entry.node) { + Some(Node::Param(a)) => Some(&a.attrs[..]), + Some(Node::Local(l)) => Some(&l.attrs[..]), + Some(Node::Item(i)) => Some(&i.attrs[..]), + Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]), + Some(Node::TraitItem(ref ti)) => Some(&ti.attrs[..]), + Some(Node::ImplItem(ref ii)) => Some(&ii.attrs[..]), + Some(Node::Variant(ref v)) => Some(&v.attrs[..]), + Some(Node::Field(ref f)) => Some(&f.attrs[..]), + Some(Node::Expr(ref e)) => Some(&*e.attrs), + Some(Node::Stmt(ref s)) => Some(s.kind.attrs()), + Some(Node::Arm(ref a)) => Some(&*a.attrs), + Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), + // Unit/tuple structs/variants take the attributes straight from + // the struct/variant definition. + Some(Node::Ctor(..)) => return self.attrs(self.get_parent_item(id)), + Some(Node::Crate(item)) => Some(&item.attrs[..]), + _ => None, + }; + attrs.unwrap_or(&[]) + } + + pub fn span(&self, hir_id: HirId) -> Span { + match self.find_entry(hir_id).map(|entry| entry.node) { + Some(Node::Param(param)) => param.span, + Some(Node::Item(item)) => item.span, + Some(Node::ForeignItem(foreign_item)) => foreign_item.span, + Some(Node::TraitItem(trait_method)) => trait_method.span, + Some(Node::ImplItem(impl_item)) => impl_item.span, + Some(Node::Variant(variant)) => variant.span, + Some(Node::Field(field)) => field.span, + Some(Node::AnonConst(constant)) => self.body(constant.body).value.span, + Some(Node::Expr(expr)) => expr.span, + Some(Node::Stmt(stmt)) => stmt.span, + Some(Node::PathSegment(seg)) => seg.ident.span, + Some(Node::Ty(ty)) => ty.span, + Some(Node::TraitRef(tr)) => tr.path.span, + Some(Node::Binding(pat)) => pat.span, + Some(Node::Pat(pat)) => pat.span, + Some(Node::Arm(arm)) => arm.span, + Some(Node::Block(block)) => block.span, + Some(Node::Ctor(..)) => match self.find(self.get_parent_node(hir_id)) { + Some(Node::Item(item)) => item.span, + Some(Node::Variant(variant)) => variant.span, + _ => unreachable!(), + }, + Some(Node::Lifetime(lifetime)) => lifetime.span, + Some(Node::GenericParam(param)) => param.span, + Some(Node::Visibility(&Spanned { + node: VisibilityKind::Restricted { ref path, .. }, + .. + })) => path.span, + Some(Node::Visibility(v)) => bug!("unexpected Visibility {:?}", v), + Some(Node::Local(local)) => local.span, + Some(Node::MacroDef(macro_def)) => macro_def.span, + Some(Node::Crate(item)) => item.span, + None => bug!("hir::map::Map::span: id not in map: {:?}", hir_id), + } + } + + pub fn span_if_local(&self, id: DefId) -> Option { + id.as_local().map(|id| self.span(self.as_local_hir_id(id))) + } + + pub fn res_span(&self, res: Res) -> Option { + match res { + Res::Err => None, + Res::Local(id) => Some(self.span(id)), + res => self.span_if_local(res.opt_def_id()?), + } + } + + /// Get a representation of this `id` for debugging purposes. + /// NOTE: Do NOT use this in diagnostics! + pub fn node_to_string(&self, id: HirId) -> String { + hir_id_to_string(self, id) + } +} + +impl<'hir> intravisit::Map<'hir> for Map<'hir> { + fn find(&self, hir_id: HirId) -> Option> { + self.find(hir_id) + } + + fn body(&self, id: BodyId) -> &'hir Body<'hir> { + self.body(id) + } + + fn item(&self, id: HirId) -> &'hir Item<'hir> { + self.item(id) + } + + fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + self.trait_item(id) + } + + fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + self.impl_item(id) + } +} + +trait Named { + fn name(&self) -> Symbol; +} + +impl Named for Spanned { + fn name(&self) -> Symbol { + self.node.name() + } +} + +impl Named for Item<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for ForeignItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for Variant<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for StructField<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for TraitItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for ImplItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} + +pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx IndexedHir<'tcx> { + assert_eq!(cnum, LOCAL_CRATE); + + let _prof_timer = tcx.sess.prof.generic_activity("build_hir_map"); + + let (map, crate_hash) = { + let hcx = tcx.create_stable_hashing_context(); + + let mut collector = + NodeCollector::root(tcx.sess, &**tcx.arena, tcx.untracked_crate, &tcx.definitions, hcx); + intravisit::walk_crate(&mut collector, tcx.untracked_crate); + + let crate_disambiguator = tcx.sess.local_crate_disambiguator(); + let cmdline_args = tcx.sess.opts.dep_tracking_hash(); + collector.finalize_and_compute_crate_hash(crate_disambiguator, &*tcx.cstore, cmdline_args) + }; + + tcx.arena.alloc(IndexedHir { crate_hash, map }) +} + +fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String { + let id_str = format!(" (hir_id={})", id); + + let path_str = || { + // This functionality is used for debugging, try to use `TyCtxt` to get + // the user-friendly path, otherwise fall back to stringifying `DefPath`. + crate::ty::tls::with_opt(|tcx| { + if let Some(tcx) = tcx { + let def_id = map.local_def_id(id); + tcx.def_path_str(def_id.to_def_id()) + } else if let Some(path) = map.def_path_from_hir_id(id) { + path.data + .into_iter() + .map(|elem| elem.data.to_string()) + .collect::>() + .join("::") + } else { + String::from("") + } + }) + }; + + let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default(); + let node_str = |prefix| format!("{} {}{}", prefix, span_str(), id_str); + + match map.find(id) { + Some(Node::Item(item)) => { + let item_str = match item.kind { + ItemKind::ExternCrate(..) => "extern crate", + ItemKind::Use(..) => "use", + ItemKind::Static(..) => "static", + ItemKind::Const(..) => "const", + ItemKind::Fn(..) => "fn", + ItemKind::Mod(..) => "mod", + ItemKind::ForeignMod(..) => "foreign mod", + ItemKind::GlobalAsm(..) => "global asm", + ItemKind::TyAlias(..) => "ty", + ItemKind::OpaqueTy(..) => "opaque type", + ItemKind::Enum(..) => "enum", + ItemKind::Struct(..) => "struct", + ItemKind::Union(..) => "union", + ItemKind::Trait(..) => "trait", + ItemKind::TraitAlias(..) => "trait alias", + ItemKind::Impl { .. } => "impl", + }; + format!("{} {}{}", item_str, path_str(), id_str) + } + Some(Node::ForeignItem(_)) => format!("foreign item {}{}", path_str(), id_str), + Some(Node::ImplItem(ii)) => match ii.kind { + ImplItemKind::Const(..) => { + format!("assoc const {} in {}{}", ii.ident, path_str(), id_str) + } + ImplItemKind::Fn(..) => format!("method {} in {}{}", ii.ident, path_str(), id_str), + ImplItemKind::TyAlias(_) => { + format!("assoc type {} in {}{}", ii.ident, path_str(), id_str) + } + ImplItemKind::OpaqueTy(_) => { + format!("assoc opaque type {} in {}{}", ii.ident, path_str(), id_str) + } + }, + Some(Node::TraitItem(ti)) => { + let kind = match ti.kind { + TraitItemKind::Const(..) => "assoc constant", + TraitItemKind::Fn(..) => "trait method", + TraitItemKind::Type(..) => "assoc type", + }; + + format!("{} {} in {}{}", kind, ti.ident, path_str(), id_str) + } + Some(Node::Variant(ref variant)) => { + format!("variant {} in {}{}", variant.ident, path_str(), id_str) + } + Some(Node::Field(ref field)) => { + format!("field {} in {}{}", field.ident, path_str(), id_str) + } + Some(Node::AnonConst(_)) => node_str("const"), + Some(Node::Expr(_)) => node_str("expr"), + Some(Node::Stmt(_)) => node_str("stmt"), + Some(Node::PathSegment(_)) => node_str("path segment"), + Some(Node::Ty(_)) => node_str("type"), + Some(Node::TraitRef(_)) => node_str("trait ref"), + Some(Node::Binding(_)) => node_str("local"), + Some(Node::Pat(_)) => node_str("pat"), + Some(Node::Param(_)) => node_str("param"), + Some(Node::Arm(_)) => node_str("arm"), + Some(Node::Block(_)) => node_str("block"), + Some(Node::Local(_)) => node_str("local"), + Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str), + Some(Node::Lifetime(_)) => node_str("lifetime"), + Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str), + Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str), + Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str), + Some(Node::Crate(..)) => String::from("root_crate"), + None => format!("unknown node{}", id_str), + } +} + +pub fn provide(providers: &mut Providers<'_>) { + providers.def_kind = |tcx, def_id| tcx.hir().def_kind(def_id.expect_local()); +} diff --git a/src/librustc_middle/hir/mod.rs b/src/librustc_middle/hir/mod.rs new file mode 100644 index 0000000000000..1e3676496ce39 --- /dev/null +++ b/src/librustc_middle/hir/mod.rs @@ -0,0 +1,83 @@ +//! HIR datatypes. See the [rustc dev guide] for more info. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html + +pub mod exports; +pub mod map; + +use crate::ich::StableHashingContext; +use crate::ty::query::Providers; +use crate::ty::TyCtxt; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::Body; +use rustc_hir::HirId; +use rustc_hir::ItemLocalId; +use rustc_hir::Node; +use rustc_index::vec::IndexVec; + +pub struct Owner<'tcx> { + parent: HirId, + node: Node<'tcx>, +} + +impl<'a, 'tcx> HashStable> for Owner<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let Owner { parent, node } = self; + hcx.while_hashing_hir_bodies(false, |hcx| { + parent.hash_stable(hcx, hasher); + node.hash_stable(hcx, hasher); + }); + } +} + +#[derive(Clone)] +pub struct ParentedNode<'tcx> { + parent: ItemLocalId, + node: Node<'tcx>, +} + +pub struct OwnerNodes<'tcx> { + hash: Fingerprint, + nodes: IndexVec>>, + bodies: FxHashMap>, +} + +impl<'a, 'tcx> HashStable> for OwnerNodes<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + // We ignore the `nodes` and `bodies` fields since these refer to information included in + // `hash` which is hashed in the collector and used for the crate hash. + let OwnerNodes { hash, nodes: _, bodies: _ } = *self; + hash.hash_stable(hcx, hasher); + } +} + +impl<'tcx> TyCtxt<'tcx> { + #[inline(always)] + pub fn hir(self) -> map::Map<'tcx> { + map::Map { tcx: self } + } + + pub fn parent_module(self, id: HirId) -> LocalDefId { + self.parent_module_from_def_id(id.owner) + } +} + +pub fn provide(providers: &mut Providers<'_>) { + providers.parent_module_from_def_id = |tcx, id| { + let hir = tcx.hir(); + hir.local_def_id(hir.get_module_parent_node(hir.as_local_hir_id(id))) + }; + providers.hir_crate = |tcx, _| tcx.untracked_crate; + providers.index_hir = map::index_hir; + providers.hir_module_items = |tcx, id| { + let hir = tcx.hir(); + let module = hir.as_local_hir_id(id); + &tcx.untracked_crate.modules[&module] + }; + providers.hir_owner = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].signature; + providers.hir_owner_nodes = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].with_bodies.as_deref(); + map::provide(providers); +} diff --git a/src/librustc/ich/hcx.rs b/src/librustc_middle/ich/hcx.rs similarity index 95% rename from src/librustc/ich/hcx.rs rename to src/librustc_middle/ich/hcx.rs index ba3763f6ee0bd..69b4adb3a0e1d 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc_middle/ich/hcx.rs @@ -1,5 +1,4 @@ -use crate::hir::map::definitions::{DefPathHash, Definitions}; -use crate::ich::{self, CachingSourceMapView}; +use crate::ich; use crate::middle::cstore::CrateStore; use crate::ty::{fast_reject, TyCtxt}; @@ -9,10 +8,11 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::definitions::{DefPathHash, Definitions}; use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, SourceFile}; +use rustc_span::{BytePos, CachingSourceMapView, SourceFile}; use smallvec::SmallVec; use std::cmp::Ord; @@ -135,11 +135,6 @@ impl<'a> StableHashingContext<'a> { self.definitions.def_path_hash(def_id) } - #[inline] - pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId { - self.definitions.node_to_hir_id(node_id) - } - #[inline] pub fn hash_bodies(&self) -> bool { self.hash_bodies @@ -194,8 +189,6 @@ impl<'a> StableHashingContextProvider<'a> for StableHashingContext<'a> { } } -impl<'a> crate::dep_graph::DepGraphSafe for StableHashingContext<'a> {} - impl<'a> HashStable> for ast::NodeId { fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) { panic!("Node IDs should not appear in incremental state"); diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc_middle/ich/impls_hir.rs similarity index 98% rename from src/librustc/ich/impls_hir.rs rename to src/librustc_middle/ich/impls_hir.rs index 1722b29266ad2..f668cc99754f4 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc_middle/ich/impls_hir.rs @@ -1,12 +1,13 @@ //! This module contains `HashStable` implementations for various HIR data //! types in no particular order. -use crate::hir::map::DefPathHash; -use crate::ich::{Fingerprint, NodeIdHashingMode, StableHashingContext}; +use crate::ich::{NodeIdHashingMode, StableHashingContext}; use rustc_attr as attr; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::definitions::DefPathHash; use smallvec::SmallVec; use std::mem; diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc_middle/ich/impls_syntax.rs similarity index 99% rename from src/librustc/ich/impls_syntax.rs rename to src/librustc_middle/ich/impls_syntax.rs index c5a4b53b10df8..300aac19e51b0 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc_middle/ich/impls_syntax.rs @@ -61,7 +61,7 @@ impl<'a> HashStable> for SourceFile { cnum, // Do not hash the source as it is not encoded src: _, - src_hash, + ref src_hash, external_src: _, start_pos, end_pos: _, diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc_middle/ich/impls_ty.rs similarity index 96% rename from src/librustc/ich/impls_ty.rs rename to src/librustc_middle/ich/impls_ty.rs index 433076bb8342c..377c8661cbd41 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc_middle/ich/impls_ty.rs @@ -1,10 +1,11 @@ //! This module contains `HashStable` implementations for various data types -//! from rustc::ty in no particular order. +//! from `rustc_middle::ty` in no particular order. -use crate::ich::{Fingerprint, NodeIdHashingMode, StableHashingContext}; +use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::middle::region; use crate::mir; use crate::ty; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use std::cell::RefCell; @@ -135,8 +136,7 @@ impl<'a> HashStable> for mir::interpret::AllocId { ty::tls::with_opt(|tcx| { trace!("hashing {:?}", *self); let tcx = tcx.expect("can't hash AllocIds during hir lowering"); - let alloc_kind = tcx.alloc_map.lock().get(*self); - alloc_kind.hash_stable(hcx, hasher); + tcx.get_global_alloc(*self).hash_stable(hcx, hasher); }); } } diff --git a/src/librustc_middle/ich/mod.rs b/src/librustc_middle/ich/mod.rs new file mode 100644 index 0000000000000..c8fb2bf39cc80 --- /dev/null +++ b/src/librustc_middle/ich/mod.rs @@ -0,0 +1,23 @@ +//! ICH - Incremental Compilation Hash + +pub use self::hcx::{ + hash_stable_trait_impls, NodeIdHashingMode, StableHashingContext, StableHashingContextProvider, +}; +use rustc_span::symbol::{sym, Symbol}; + +mod hcx; + +mod impls_hir; +mod impls_syntax; +mod impls_ty; + +pub const IGNORED_ATTRIBUTES: &[Symbol] = &[ + sym::cfg, + sym::rustc_if_this_changed, + sym::rustc_then_this_would_need, + sym::rustc_dirty, + sym::rustc_clean, + sym::rustc_partition_reused, + sym::rustc_partition_codegened, + sym::rustc_expected_cgu_reuse, +]; diff --git a/src/librustc/infer/canonical.rs b/src/librustc_middle/infer/canonical.rs similarity index 99% rename from src/librustc/infer/canonical.rs rename to src/librustc_middle/infer/canonical.rs index 5f7e8c849653c..9433d282ad297 100644 --- a/src/librustc/infer/canonical.rs +++ b/src/librustc_middle/infer/canonical.rs @@ -19,7 +19,7 @@ //! For a more detailed look at what is happening here, check //! out the [chapter in the rustc dev guide][c]. //! -//! [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::MemberConstraint; use crate::ty::subst::GenericArg; diff --git a/src/librustc/infer/mod.rs b/src/librustc_middle/infer/mod.rs similarity index 100% rename from src/librustc/infer/mod.rs rename to src/librustc_middle/infer/mod.rs diff --git a/src/librustc/infer/unify_key.rs b/src/librustc_middle/infer/unify_key.rs similarity index 92% rename from src/librustc/infer/unify_key.rs rename to src/librustc_middle/infer/unify_key.rs index e205453a48c53..2580ac6bebd86 100644 --- a/src/librustc/infer/unify_key.rs +++ b/src/librustc_middle/infer/unify_key.rs @@ -1,6 +1,9 @@ use crate::ty::{self, FloatVarValue, InferConst, IntVarValue, Ty, TyCtxt}; -use rustc_data_structures::unify::InPlace; -use rustc_data_structures::unify::{EqUnifyValue, NoError, UnificationTable, UnifyKey, UnifyValue}; +use rustc_data_structures::snapshot_vec; +use rustc_data_structures::undo_log::UndoLogs; +use rustc_data_structures::unify::{ + self, EqUnifyValue, InPlace, NoError, UnificationTable, UnifyKey, UnifyValue, +}; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; @@ -212,10 +215,14 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { impl<'tcx> EqUnifyValue for &'tcx ty::Const<'tcx> {} -pub fn replace_if_possible( - table: &mut UnificationTable>>, +pub fn replace_if_possible( + table: &mut UnificationTable, V, L>>, c: &'tcx ty::Const<'tcx>, -) -> &'tcx ty::Const<'tcx> { +) -> &'tcx ty::Const<'tcx> +where + V: snapshot_vec::VecLike>>, + L: UndoLogs>>>, +{ if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = c { match table.probe_value(*vid).val.known() { Some(c) => c, diff --git a/src/librustc_middle/lib.rs b/src/librustc_middle/lib.rs new file mode 100644 index 0000000000000..7c433574d1843 --- /dev/null +++ b/src/librustc_middle/lib.rs @@ -0,0 +1,99 @@ +//! The "main crate" of the Rust compiler. This crate contains common +//! type definitions that are used by the other crates in the rustc +//! "family". Some prominent examples (note that each of these modules +//! has their own README with further details). +//! +//! - **HIR.** The "high-level (H) intermediate representation (IR)" is +//! defined in the `hir` module. +//! - **MIR.** The "mid-level (M) intermediate representation (IR)" is +//! defined in the `mir` module. This module contains only the +//! *definition* of the MIR; the passes that transform and operate +//! on MIR are found in `librustc_mir` crate. +//! - **Types.** The internal representation of types used in rustc is +//! defined in the `ty` module. This includes the **type context** +//! (or `tcx`), which is the central context during most of +//! compilation, containing the interners and other things. +//! +//! For more information about how rustc works, see the [rustc dev guide]. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(backtrace)] +#![feature(bool_to_option)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(const_if_match)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(const_transmute)] +#![feature(core_intrinsics)] +#![feature(discriminant_kind)] +#![feature(drain_filter)] +#![feature(never_type)] +#![feature(exhaustive_patterns)] +#![feature(marker_trait_attr)] +#![feature(extern_types)] +#![feature(nll)] +#![feature(option_expect_none)] +#![feature(or_patterns)] +#![feature(range_is_empty)] +#![feature(specialization)] // FIXME: min_specialization does not work +#![feature(track_caller)] +#![feature(trusted_len)] +#![feature(vec_remove_item)] +#![feature(stmt_expr_attributes)] +#![feature(test)] +#![feature(in_band_lifetimes)] +#![feature(crate_visibility_modifier)] +#![feature(associated_type_bounds)] +#![feature(rustc_attrs)] +#![feature(hash_raw_entry)] +#![feature(int_error_matching)] +#![recursion_limit = "512"] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate scoped_tls; +#[macro_use] +extern crate rustc_macros; +#[macro_use] +extern crate rustc_data_structures; +#[macro_use] +extern crate log; +#[macro_use] +extern crate smallvec; + +#[cfg(test)] +mod tests; + +#[macro_use] +mod macros; + +#[macro_use] +pub mod query; + +#[macro_use] +pub mod arena; +pub mod dep_graph; +pub mod hir; +pub mod ich; +pub mod infer; +pub mod lint; +pub mod middle; +pub mod mir; +pub mod traits; +pub mod ty; + +pub mod util { + pub mod bug; + pub mod common; +} + +// Allows macros to refer to this crate as `::rustc_middle` +extern crate self as rustc_middle; diff --git a/src/librustc_middle/lint.rs b/src/librustc_middle/lint.rs new file mode 100644 index 0000000000000..27239b4ad2e78 --- /dev/null +++ b/src/librustc_middle/lint.rs @@ -0,0 +1,350 @@ +use std::cmp; + +use crate::ich::StableHashingContext; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_errors::{DiagnosticBuilder, DiagnosticId}; +use rustc_hir::HirId; +use rustc_session::lint::{builtin, Level, Lint, LintId}; +use rustc_session::{DiagnosticMessageId, Session}; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan}; +use rustc_span::{Span, Symbol}; + +/// How a lint level was set. +#[derive(Clone, Copy, PartialEq, Eq, HashStable)] +pub enum LintSource { + /// Lint is at the default level as declared + /// in rustc or a plugin. + Default, + + /// Lint level was set by an attribute. + Node(Symbol, Span, Option /* RFC 2383 reason */), + + /// Lint level was set by a command-line flag. + CommandLine(Symbol), +} + +pub type LevelSource = (Level, LintSource); + +pub struct LintLevelSets { + pub list: Vec, + pub lint_cap: Level, +} + +pub enum LintSet { + CommandLine { + // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which + // flag. + specs: FxHashMap, + }, + + Node { + specs: FxHashMap, + parent: u32, + }, +} + +impl LintLevelSets { + pub fn new() -> Self { + LintLevelSets { list: Vec::new(), lint_cap: Level::Forbid } + } + + pub fn get_lint_level( + &self, + lint: &'static Lint, + idx: u32, + aux: Option<&FxHashMap>, + sess: &Session, + ) -> LevelSource { + let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux); + + // If `level` is none then we actually assume the default level for this + // lint. + let mut level = level.unwrap_or_else(|| lint.default_level(sess.edition())); + + // 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 + // `allow(warnings)` in scope then we want to respect that instead. + if level == Level::Warn { + let (warnings_level, warnings_src) = + self.get_lint_id_level(LintId::of(builtin::WARNINGS), idx, aux); + if let Some(configured_warning_level) = warnings_level { + if configured_warning_level != Level::Warn { + level = configured_warning_level; + src = warnings_src; + } + } + } + + // Ensure that we never exceed the `--cap-lints` argument. + level = cmp::min(level, self.lint_cap); + + if let Some(driver_level) = sess.driver_lint_caps.get(&LintId::of(lint)) { + // Ensure that we never exceed driver level. + level = cmp::min(*driver_level, level); + } + + (level, src) + } + + pub fn get_lint_id_level( + &self, + id: LintId, + mut idx: u32, + aux: Option<&FxHashMap>, + ) -> (Option, LintSource) { + if let Some(specs) = aux { + if let Some(&(level, src)) = specs.get(&id) { + return (Some(level), src); + } + } + loop { + match self.list[idx as usize] { + LintSet::CommandLine { ref specs } => { + if let Some(&(level, src)) = specs.get(&id) { + return (Some(level), src); + } + return (None, LintSource::Default); + } + LintSet::Node { ref specs, parent } => { + if let Some(&(level, src)) = specs.get(&id) { + return (Some(level), src); + } + idx = parent; + } + } + } + } +} + +pub struct LintLevelMap { + pub sets: LintLevelSets, + pub id_to_set: FxHashMap, +} + +impl LintLevelMap { + /// If the `id` was previously registered with `register_id` when building + /// this `LintLevelMap` this returns the corresponding lint level and source + /// of the lint level for the lint provided. + /// + /// If the `id` was not previously registered, returns `None`. If `None` is + /// returned then the parent of `id` should be acquired and this function + /// should be called again. + pub fn level_and_source( + &self, + lint: &'static Lint, + id: HirId, + session: &Session, + ) -> Option { + self.id_to_set.get(&id).map(|idx| self.sets.get_lint_level(lint, *idx, None, session)) + } +} + +impl<'a> HashStable> for LintLevelMap { + #[inline] + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let LintLevelMap { ref sets, ref id_to_set } = *self; + + id_to_set.hash_stable(hcx, hasher); + + let LintLevelSets { ref list, lint_cap } = *sets; + + lint_cap.hash_stable(hcx, hasher); + + hcx.while_hashing_spans(true, |hcx| { + list.len().hash_stable(hcx, hasher); + + // We are working under the assumption here that the list of + // lint-sets is built in a deterministic order. + for lint_set in list { + ::std::mem::discriminant(lint_set).hash_stable(hcx, hasher); + + match *lint_set { + LintSet::CommandLine { ref specs } => { + specs.hash_stable(hcx, hasher); + } + LintSet::Node { ref specs, parent } => { + specs.hash_stable(hcx, hasher); + parent.hash_stable(hcx, hasher); + } + } + } + }) + } +} + +pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>); + +impl<'a> LintDiagnosticBuilder<'a> { + /// Return the inner DiagnosticBuilder, first setting the primary message to `msg`. + pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> { + self.0.set_primary_message(msg); + self.0 + } + + /// Create a LintDiagnosticBuilder from some existing DiagnosticBuilder. + pub fn new(err: DiagnosticBuilder<'a>) -> LintDiagnosticBuilder<'a> { + LintDiagnosticBuilder(err) + } +} + +pub fn struct_lint_level<'s, 'd>( + sess: &'s Session, + lint: &'static Lint, + level: Level, + src: LintSource, + span: Option, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + 'd, +) { + // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to + // the "real" work. + fn struct_lint_level_impl( + sess: &'s Session, + lint: &'static Lint, + level: Level, + src: LintSource, + span: Option, + decorate: Box FnOnce(LintDiagnosticBuilder<'b>) + 'd>, + ) { + let mut err = match (level, span) { + (Level::Allow, _) => { + return; + } + (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""), + (Level::Warn, None) => sess.struct_warn(""), + (Level::Deny | Level::Forbid, Some(span)) => sess.struct_span_err(span, ""), + (Level::Deny | Level::Forbid, None) => sess.struct_err(""), + }; + + // Check for future incompatibility lints and issue a stronger warning. + let lint_id = LintId::of(lint); + let future_incompatible = lint.future_incompatible; + + // 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 + // can do about it. We probably want to skip the lint entirely. + if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { + // Any suggestions made here are likely to be incorrect, so anything we + // emit shouldn't be automatically fixed by rustfix. + err.allow_suggestions(false); + + // If this is a future incompatible lint it'll become a hard error, so + // we have to emit *something*. Also allow lints to whitelist themselves + // on a case-by-case basis for emission in a foreign macro. + if future_incompatible.is_none() && !lint.report_in_external_macro { + err.cancel(); + // Don't continue further, since we don't want to have + // `diag_span_note_once` called for a diagnostic that isn't emitted. + return; + } + } + + let name = lint.name_lower(); + match src { + LintSource::Default => { + sess.diag_note_once( + &mut err, + DiagnosticMessageId::from(lint), + &format!("`#[{}({})]` on by default", level.as_str(), name), + ); + } + LintSource::CommandLine(lint_flag_val) => { + let flag = match level { + Level::Warn => "-W", + Level::Deny => "-D", + Level::Forbid => "-F", + Level::Allow => panic!(), + }; + let hyphen_case_lint_name = name.replace("_", "-"); + if lint_flag_val.as_str() == name { + sess.diag_note_once( + &mut err, + DiagnosticMessageId::from(lint), + &format!( + "requested on the command line with `{} {}`", + flag, hyphen_case_lint_name + ), + ); + } else { + let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-"); + sess.diag_note_once( + &mut err, + DiagnosticMessageId::from(lint), + &format!( + "`{} {}` implied by `{} {}`", + flag, hyphen_case_lint_name, flag, hyphen_case_flag_val + ), + ); + } + } + LintSource::Node(lint_attr_name, src, reason) => { + if let Some(rationale) = reason { + err.note(&rationale.as_str()); + } + sess.diag_span_note_once( + &mut err, + DiagnosticMessageId::from(lint), + src, + "the lint level is defined here", + ); + if lint_attr_name.as_str() != name { + let level_str = level.as_str(); + sess.diag_note_once( + &mut err, + DiagnosticMessageId::from(lint), + &format!( + "`#[{}({})]` implied by `#[{}({})]`", + level_str, name, level_str, lint_attr_name + ), + ); + } + } + } + + err.code(DiagnosticId::Lint(name)); + + if let Some(future_incompatible) = future_incompatible { + const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error"; + + let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) { + "once this method is added to the standard library, \ + the ambiguity may cause an error or change in behavior!" + .to_owned() + } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) { + "this borrowing pattern was not meant to be accepted, \ + and may become a hard error in the future" + .to_owned() + } else if let Some(edition) = future_incompatible.edition { + format!("{} in the {} edition!", STANDARD_MESSAGE, edition) + } else { + format!("{} in a future release!", STANDARD_MESSAGE) + }; + let citation = format!("for more information, see {}", future_incompatible.reference); + err.warn(&explanation); + err.note(&citation); + } + + // Finally, run `decorate`. This function is also responsible for emitting the diagnostic. + decorate(LintDiagnosticBuilder::new(err)); + } + struct_lint_level_impl(sess, lint, level, src, span, Box::new(decorate)) +} + +/// Returns whether `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. +pub fn in_external_macro(sess: &Session, span: Span) -> bool { + let expn_data = span.ctxt().outer_expn_data(); + match expn_data.kind { + ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => 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() || sess.source_map().is_imported(expn_data.def_site) + } + ExpnKind::Macro(..) => true, // definitely a plugin + } +} diff --git a/src/librustc/macros.rs b/src/librustc_middle/macros.rs similarity index 100% rename from src/librustc/macros.rs rename to src/librustc_middle/macros.rs diff --git a/src/librustc/middle/codegen_fn_attrs.rs b/src/librustc_middle/middle/codegen_fn_attrs.rs similarity index 93% rename from src/librustc/middle/codegen_fn_attrs.rs rename to src/librustc_middle/middle/codegen_fn_attrs.rs index 61b25cc486446..c480944069efb 100644 --- a/src/librustc/middle/codegen_fn_attrs.rs +++ b/src/librustc_middle/middle/codegen_fn_attrs.rs @@ -77,6 +77,12 @@ bitflags! { const NO_SANITIZE_THREAD = 1 << 14; /// All `#[no_sanitize(...)]` attributes. const NO_SANITIZE_ANY = Self::NO_SANITIZE_ADDRESS.bits | Self::NO_SANITIZE_MEMORY.bits | Self::NO_SANITIZE_THREAD.bits; + /// #[ffi_pure]: applies clang's `pure` attribute to a foreign function + /// declaration. + const FFI_PURE = 1 << 15; + /// #[ffi_const]: applies clang's `const` attribute to a foreign function + /// declaration. + const FFI_CONST = 1 << 16; } } @@ -114,7 +120,7 @@ impl CodegenFnAttrs { || match self.linkage { // These are private, so make sure we don't try to consider // them external. - None | Some(Linkage::Internal) | Some(Linkage::Private) => false, + None | Some(Linkage::Internal | Linkage::Private) => false, Some(_) => true, } } diff --git a/src/librustc/middle/cstore.rs b/src/librustc_middle/middle/cstore.rs similarity index 99% rename from src/librustc/middle/cstore.rs rename to src/librustc_middle/middle/cstore.rs index 3a9d3c56dcead..012390e8f74b8 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc_middle/middle/cstore.rs @@ -4,7 +4,6 @@ pub use self::NativeLibraryKind::*; -use crate::hir::map::definitions::{DefKey, DefPath, DefPathHash, DefPathTable}; use crate::ty::TyCtxt; use rustc_ast::ast; @@ -12,6 +11,7 @@ use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, MetadataRef}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, DefPathTable}; use rustc_macros::HashStable; use rustc_session::search_paths::PathKind; pub use rustc_session::utils::NativeLibraryKind; diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc_middle/middle/dependency_format.rs similarity index 90% rename from src/librustc/middle/dependency_format.rs rename to src/librustc_middle/middle/dependency_format.rs index ee5f822d3134c..16ce315368a05 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc_middle/middle/dependency_format.rs @@ -4,7 +4,7 @@ //! For all the gory details, see the provider of the `dependency_formats` //! query. -use rustc_session::config; +use rustc_session::config::CrateType; /// A list of dependencies for a certain crate type. /// @@ -17,7 +17,7 @@ pub type DependencyList = Vec; /// A mapping of all required dependencies for a particular flavor of output. /// /// This is local to the tcx, and is generally relevant to one session. -pub type Dependencies = Vec<(config::CrateType, DependencyList)>; +pub type Dependencies = Vec<(CrateType, DependencyList)>; #[derive(Copy, Clone, PartialEq, Debug, HashStable, RustcEncodable, RustcDecodable)] pub enum Linkage { diff --git a/src/librustc/middle/exported_symbols.rs b/src/librustc_middle/middle/exported_symbols.rs similarity index 100% rename from src/librustc/middle/exported_symbols.rs rename to src/librustc_middle/middle/exported_symbols.rs diff --git a/src/librustc/middle/lang_items.rs b/src/librustc_middle/middle/lang_items.rs similarity index 94% rename from src/librustc/middle/lang_items.rs rename to src/librustc_middle/middle/lang_items.rs index 36560371587a5..0f98c338c16b1 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc_middle/middle/lang_items.rs @@ -7,17 +7,13 @@ //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. //! * Functions called by the compiler itself. -pub use self::LangItem::*; - use crate::ty::{self, TyCtxt}; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; use rustc_span::Span; use rustc_target::spec::PanicStrategy; -pub use rustc_hir::weak_lang_items::link_name; -pub use rustc_hir::{LangItem, LanguageItems}; - impl<'tcx> TyCtxt<'tcx> { /// Returns the `DefId` for a given `LangItem`. /// If not found, fatally aborts compilation. diff --git a/src/librustc/middle/limits.rs b/src/librustc_middle/middle/limits.rs similarity index 99% rename from src/librustc/middle/limits.rs rename to src/librustc_middle/middle/limits.rs index 6de53c703e165..c43c22cd61ba6 100644 --- a/src/librustc/middle/limits.rs +++ b/src/librustc_middle/middle/limits.rs @@ -5,7 +5,7 @@ //! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass //! just peeks and looks for that attribute. -use rustc::bug; +use crate::bug; use rustc_ast::ast; use rustc_data_structures::sync::Once; use rustc_session::Session; diff --git a/src/librustc_middle/middle/mod.rs b/src/librustc_middle/middle/mod.rs new file mode 100644 index 0000000000000..9bc9ca6707afe --- /dev/null +++ b/src/librustc_middle/middle/mod.rs @@ -0,0 +1,34 @@ +pub mod codegen_fn_attrs; +pub mod cstore; +pub mod dependency_format; +pub mod exported_symbols; +pub mod lang_items; +pub mod lib_features { + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + use rustc_span::symbol::Symbol; + + #[derive(HashStable)] + pub struct LibFeatures { + // A map from feature to stabilisation version. + pub stable: FxHashMap, + pub unstable: FxHashSet, + } + + impl LibFeatures { + pub fn to_vec(&self) -> Vec<(Symbol, Option)> { + let mut all_features: Vec<_> = self + .stable + .iter() + .map(|(f, s)| (*f, Some(*s))) + .chain(self.unstable.iter().map(|f| (*f, None))) + .collect(); + all_features.sort_unstable_by_key(|f| f.0.as_str()); + all_features + } + } +} +pub mod limits; +pub mod privacy; +pub mod region; +pub mod resolve_lifetime; +pub mod stability; diff --git a/src/librustc/middle/privacy.rs b/src/librustc_middle/middle/privacy.rs similarity index 100% rename from src/librustc/middle/privacy.rs rename to src/librustc_middle/middle/privacy.rs diff --git a/src/librustc/middle/region.rs b/src/librustc_middle/middle/region.rs similarity index 97% rename from src/librustc/middle/region.rs rename to src/librustc_middle/middle/region.rs index 73251ee2680df..f02d8fe8ad601 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc_middle/middle/region.rs @@ -4,7 +4,7 @@ //! For more information about how MIR-based region-checking works, //! see the [rustc dev guide]. //! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/borrowck.html +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::ty::{self, DefIdTree, TyCtxt}; @@ -159,21 +159,20 @@ impl Scope { self.id } - pub fn hir_id(&self, scope_tree: &ScopeTree) -> hir::HirId { - match scope_tree.root_body { - Some(hir_id) => hir::HirId { owner: hir_id.owner, local_id: self.item_local_id() }, - None => hir::DUMMY_HIR_ID, - } + pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option { + scope_tree + .root_body + .map(|hir_id| hir::HirId { owner: hir_id.owner, local_id: self.item_local_id() }) } /// Returns the span of this `Scope`. Note that in general the /// returned span may not correspond to the span of any `NodeId` in /// the AST. pub fn span(&self, tcx: TyCtxt<'_>, scope_tree: &ScopeTree) -> Span { - let hir_id = self.hir_id(scope_tree); - if hir_id == hir::DUMMY_HIR_ID { - return DUMMY_SP; - } + let hir_id = match self.hir_id(scope_tree) { + Some(hir_id) => hir_id, + None => return DUMMY_SP, + }; let span = tcx.hir().span(hir_id); if let ScopeData::Remainder(first_statement_index) = self.data { if let Node::Block(ref blk) = tcx.hir().get(hir_id) { @@ -182,7 +181,7 @@ impl Scope { // `blk`; reuse span of `blk` and shift `lo` // forward to end of indexed statement. // - // (This is the special case aluded to in the + // (This is the special case alluded to in the // doc-comment for this method) let stmt_span = blk.stmts[first_statement_index.index()].span; @@ -293,7 +292,7 @@ pub struct ScopeTree { /// /// Then: /// 1. From the ordering guarantee of HIR visitors (see - /// `rustc::hir::intravisit`), `D` does not dominate `U`. + /// `rustc_hir::intravisit`), `D` does not dominate `U`. /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because /// we might visit `U` without ever getting to `D`). /// 3. However, we guarantee that at each HIR point, each @@ -555,7 +554,7 @@ impl<'tcx> ScopeTree { pub fn early_free_scope(&self, tcx: TyCtxt<'tcx>, br: &ty::EarlyBoundRegion) -> Scope { let param_owner = tcx.parent(br.def_id).unwrap(); - let param_owner_id = tcx.hir().as_local_hir_id(param_owner).unwrap(); + let param_owner_id = tcx.hir().as_local_hir_id(param_owner.expect_local()); let scope = tcx .hir() .maybe_body_owned_by(param_owner_id) @@ -596,7 +595,7 @@ impl<'tcx> ScopeTree { // on the same function that they ended up being freed in. assert_eq!(param_owner, fr.scope); - let param_owner_id = tcx.hir().as_local_hir_id(param_owner).unwrap(); + let param_owner_id = tcx.hir().as_local_hir_id(param_owner.expect_local()); let body_id = tcx.hir().body_owned_by(param_owner_id); Scope { id: tcx.hir().body(body_id).value.hir_id.local_id, data: ScopeData::CallSite } } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc_middle/middle/resolve_lifetime.rs similarity index 100% rename from src/librustc/middle/resolve_lifetime.rs rename to src/librustc_middle/middle/resolve_lifetime.rs diff --git a/src/librustc/middle/stability.rs b/src/librustc_middle/middle/stability.rs similarity index 95% rename from src/librustc/middle/stability.rs rename to src/librustc_middle/middle/stability.rs index 46525bdedad35..54c05bca3bd2b 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc_middle/middle/stability.rs @@ -215,7 +215,6 @@ fn late_report_deprecation( suggestion: Option, lint: &'static Lint, span: Span, - def_id: DefId, hir_id: HirId, ) { if span.in_derive_expansion() { @@ -229,9 +228,6 @@ fn late_report_deprecation( } diag.emit() }); - if hir_id == hir::DUMMY_HIR_ID { - span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id); - } } /// Result of `TyCtxt::eval_stability`. @@ -250,7 +246,7 @@ pub enum EvalResult { fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool { // Check if `def_id` is a trait method. match tcx.def_kind(def_id) { - Some(DefKind::AssocFn) | Some(DefKind::AssocTy) | Some(DefKind::AssocConst) => { + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { if let ty::TraitContainer(trait_def_id) = tcx.associated_item(def_id).container { // Trait methods do not declare visibility (even // for visibility info in cstore). Use containing @@ -290,13 +286,13 @@ impl<'tcx> TyCtxt<'tcx> { if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id)); let skip = self - .lookup_deprecation_entry(parent_def_id) + .lookup_deprecation_entry(parent_def_id.to_def_id()) .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); if !skip { let (message, lint) = deprecation_message(&depr_entry.attr, &self.def_path_str(def_id)); - late_report_deprecation(self, &message, None, lint, span, def_id, id); + late_report_deprecation(self, &message, None, lint, span, id); } }; } @@ -319,15 +315,7 @@ impl<'tcx> TyCtxt<'tcx> { if let Some(depr) = &stability.rustc_depr { let (message, lint) = rustc_deprecation_message(depr, &self.def_path_str(def_id)); - late_report_deprecation( - self, - &message, - depr.suggestion, - lint, - span, - def_id, - id, - ); + late_report_deprecation(self, &message, depr.suggestion, lint, span, id); } } } diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc_middle/mir/interpret/allocation.rs similarity index 87% rename from src/librustc/mir/interpret/allocation.rs rename to src/librustc_middle/mir/interpret/allocation.rs index 946b6add40a7e..2b6cf224e2c1b 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc_middle/mir/interpret/allocation.rs @@ -1,20 +1,18 @@ //! The virtual memory representation of the MIR interpreter. -use super::{ - read_target_uint, write_target_uint, AllocId, InterpResult, Pointer, Scalar, ScalarMaybeUndef, -}; - -use crate::ty::layout::{Align, Size}; - -use rustc_ast::ast::Mutability; -use rustc_data_structures::sorted_map::SortedMap; -use rustc_target::abi::HasDataLayout; use std::borrow::Cow; +use std::convert::TryFrom; use std::iter; use std::ops::{Deref, DerefMut, Range}; -// NOTE: When adding new fields, make sure to adjust the `Snapshot` impl in -// `src/librustc_mir/interpret/snapshot.rs`. +use rustc_ast::ast::Mutability; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_target::abi::{Align, HasDataLayout, Size}; + +use super::{ + read_target_uint, write_target_uint, AllocId, InterpResult, Pointer, Scalar, ScalarMaybeUninit, +}; + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] #[derive(HashStable)] pub struct Allocation { @@ -27,7 +25,7 @@ pub struct Allocation { /// at the given offset. relocations: Relocations, /// Denotes which part of this allocation is initialized. - undef_mask: UndefMask, + init_mask: InitMask, /// The size of the allocation. Currently, must always equal `bytes.len()`. pub size: Size, /// The alignment of the allocation to detect unaligned reads. @@ -90,11 +88,11 @@ impl Allocation { /// Creates a read-only allocation initialized by the given bytes pub fn from_bytes<'a>(slice: impl Into>, align: Align) -> Self { let bytes = slice.into().into_owned(); - let size = Size::from_bytes(bytes.len() as u64); + let size = Size::from_bytes(bytes.len()); Self { bytes, relocations: Relocations::new(), - undef_mask: UndefMask::new(size, true), + init_mask: InitMask::new(size, true), size, align, mutability: Mutability::Not, @@ -107,11 +105,10 @@ impl Allocation { } pub fn undef(size: Size, align: Align) -> Self { - assert_eq!(size.bytes() as usize as u64, size.bytes()); Allocation { - bytes: vec![0; size.bytes() as usize], + bytes: vec![0; size.bytes_usize()], relocations: Relocations::new(), - undef_mask: UndefMask::new(size, false), + init_mask: InitMask::new(size, false), size, align, mutability: Mutability::Mut, @@ -141,7 +138,7 @@ impl Allocation<(), ()> { }) .collect(), ), - undef_mask: self.undef_mask, + init_mask: self.init_mask, align: self.align, mutability: self.mutability, extra, @@ -152,7 +149,7 @@ impl Allocation<(), ()> { /// Raw accessors. Provide access to otherwise private bytes. impl Allocation { pub fn len(&self) -> usize { - self.size.bytes() as usize + self.size.bytes_usize() } /// Looks at a slice which may describe undefined bytes or describe a relocation. This differs @@ -163,9 +160,9 @@ impl Allocation { &self.bytes[range] } - /// Returns the undef mask. - pub fn undef_mask(&self) -> &UndefMask { - &self.undef_mask + /// Returns the mask indicating which bytes are initialized. + pub fn init_mask(&self) -> &InitMask { + &self.init_mask } /// Returns the relocation list. @@ -183,12 +180,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { #[inline] fn check_bounds(&self, offset: Size, size: Size) -> Range { let end = offset + size; // This does overflow checking. - assert_eq!( - end.bytes() as usize as u64, - end.bytes(), - "cannot handle this access on this host architecture" - ); - let end = end.bytes() as usize; + let end = usize::try_from(end.bytes()).expect("access too big for this host architecture"); assert!( end <= self.len(), "Out-of-bounds access at offset {}, size {} in allocation of size {}", @@ -196,7 +188,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { size.bytes(), self.len() ); - (offset.bytes() as usize)..end + offset.bytes_usize()..end } /// The last argument controls whether we error out when there are undefined @@ -294,11 +286,10 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { cx: &impl HasDataLayout, ptr: Pointer, ) -> InterpResult<'tcx, &[u8]> { - assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); - let offset = ptr.offset.bytes() as usize; + let offset = ptr.offset.bytes_usize(); Ok(match self.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { - let size_with_null = Size::from_bytes((size + 1) as u64); + let size_with_null = Size::from_bytes(size) + Size::from_bytes(1); // Go through `get_bytes` for checks and AllocationExtra hooks. // We read the null, so we include it in the request, but we want it removed // from the result, so we do subslicing. @@ -343,7 +334,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { let (lower, upper) = src.size_hint(); let len = upper.expect("can only write bounded iterators"); assert_eq!(lower, len, "can only write iterators with a precise length"); - let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(len as u64))?; + let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(len))?; // `zip` would stop when the first iterator ends; we want to definitely // cover all of `bytes`. for dest in bytes { @@ -367,15 +358,15 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { cx: &impl HasDataLayout, ptr: Pointer, size: Size, - ) -> InterpResult<'tcx, ScalarMaybeUndef> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { // `get_bytes_unchecked` tests relocation edges. let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; - // Undef check happens *after* we established that the alignment is correct. + // Uninit check happens *after* we established that the alignment is correct. // We must not return `Ok()` for unaligned pointers! - if self.check_defined(ptr, size).is_err() { - // This inflates undefined bytes to the entire scalar, even if only a few - // bytes are undefined. - return Ok(ScalarMaybeUndef::Undef); + if self.is_defined(ptr, size).is_err() { + // This inflates uninitialized bytes to the entire scalar, even if only a few + // bytes are uninitialized. + return Ok(ScalarMaybeUninit::Uninit); } // Now we do the actual reading. let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap(); @@ -384,16 +375,13 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { // *Now*, we better make sure that the inside is free of relocations too. self.check_relocations(cx, ptr, size)?; } else { - match self.relocations.get(&ptr.offset) { - Some(&(tag, alloc_id)) => { - let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits as u64), tag); - return Ok(ScalarMaybeUndef::Scalar(ptr.into())); - } - None => {} + if let Some(&(tag, alloc_id)) = self.relocations.get(&ptr.offset) { + let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits), tag); + return Ok(ScalarMaybeUninit::Scalar(ptr.into())); } } // We don't. Just return the bits. - Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size))) + Ok(ScalarMaybeUninit::Scalar(Scalar::from_uint(bits, size))) } /// Reads a pointer-sized scalar. @@ -404,7 +392,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &self, cx: &impl HasDataLayout, ptr: Pointer, - ) -> InterpResult<'tcx, ScalarMaybeUndef> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { self.read_scalar(cx, ptr, cx.data_layout().pointer_size) } @@ -421,19 +409,19 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &mut self, cx: &impl HasDataLayout, ptr: Pointer, - val: ScalarMaybeUndef, + val: ScalarMaybeUninit, type_size: Size, ) -> InterpResult<'tcx> { let val = match val { - ScalarMaybeUndef::Scalar(scalar) => scalar, - ScalarMaybeUndef::Undef => { + ScalarMaybeUninit::Scalar(scalar) => scalar, + ScalarMaybeUninit::Uninit => { self.mark_definedness(ptr, type_size, false); return Ok(()); } }; let bytes = match val.to_bits_or_ptr(type_size, cx) { - Err(val) => val.offset.bytes() as u128, + Err(val) => u128::from(val.offset.bytes()), Ok(data) => data, }; @@ -442,11 +430,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also write a relocation. - match val { - Scalar::Ptr(val) => { - self.relocations.insert(ptr.offset, (val.tag, val.alloc_id)); - } - _ => {} + if let Scalar::Ptr(val) = val { + self.relocations.insert(ptr.offset, (val.tag, val.alloc_id)); } Ok(()) @@ -460,7 +445,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &mut self, cx: &impl HasDataLayout, ptr: Pointer, - val: ScalarMaybeUndef, + val: ScalarMaybeUninit, ) -> InterpResult<'tcx> { let ptr_size = cx.data_layout().pointer_size; self.write_scalar(cx, ptr, val, ptr_size) @@ -524,15 +509,15 @@ impl<'tcx, Tag: Copy, Extra> Allocation { ) }; let start = ptr.offset; - let end = start + size; + let end = start + size; // `Size` addition // Mark parts of the outermost relocations as undefined if they partially fall outside the // given range. if first < start { - self.undef_mask.set_range(first, start, false); + self.init_mask.set_range(first, start, false); } if last > end { - self.undef_mask.set_range(end, last, false); + self.init_mask.set_range(end, last, false); } // Forget all the relocations. @@ -557,21 +542,27 @@ impl<'tcx, Tag: Copy, Extra> Allocation { } /// Undefined bytes. -impl<'tcx, Tag, Extra> Allocation { +impl<'tcx, Tag: Copy, Extra> Allocation { + /// Checks whether the given range is entirely defined. + /// + /// Returns `Ok(())` if it's defined. Otherwise returns the index of the byte + /// at which the first undefined access begins. + fn is_defined(&self, ptr: Pointer, size: Size) -> Result<(), Size> { + self.init_mask.is_range_initialized(ptr.offset, ptr.offset + size) // `Size` addition + } + /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes` /// error which will report the first byte which is undefined. - #[inline] fn check_defined(&self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { - self.undef_mask - .is_range_defined(ptr.offset, ptr.offset + size) - .or_else(|idx| throw_ub!(InvalidUndefBytes(Some(Pointer::new(ptr.alloc_id, idx))))) + self.is_defined(ptr, size) + .or_else(|idx| throw_ub!(InvalidUninitBytes(Some(Pointer::new(ptr.alloc_id, idx))))) } pub fn mark_definedness(&mut self, ptr: Pointer, size: Size, new_state: bool) { if size.bytes() == 0 { return; } - self.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); + self.init_mask.set_range(ptr.offset, ptr.offset + size, new_state); } } @@ -610,13 +601,13 @@ impl Allocation { // where each element toggles the state. let mut ranges = smallvec::SmallVec::<[u64; 1]>::new(); - let initial = self.undef_mask.get(src.offset); + let initial = self.init_mask.get(src.offset); let mut cur_len = 1; let mut cur = initial; for i in 1..size.bytes() { // FIXME: optimize to bitshift the current undef block's bits and read the top bit. - if self.undef_mask.get(src.offset + Size::from_bytes(i)) == cur { + if self.init_mask.get(src.offset + Size::from_bytes(i)) == cur { cur_len += 1; } else { ranges.push(cur_len); @@ -641,9 +632,9 @@ impl Allocation { // An optimization where we can just overwrite an entire range of definedness bits if // they are going to be uniformly `1` or `0`. if defined.ranges.len() <= 1 { - self.undef_mask.set_range_inbounds( + self.init_mask.set_range_inbounds( dest.offset, - dest.offset + size * repeat, + dest.offset + size * repeat, // `Size` operations defined.initial, ); return; @@ -656,7 +647,7 @@ impl Allocation { for range in &defined.ranges { let old_j = j; j += range; - self.undef_mask.set_range_inbounds( + self.init_mask.set_range_inbounds( Size::from_bytes(old_j), Size::from_bytes(j), cur, @@ -721,10 +712,10 @@ impl Allocation { for i in 0..length { new_relocations.extend(relocations.iter().map(|&(offset, reloc)| { // compute offset for current repetition - let dest_offset = dest.offset + (i * size); + let dest_offset = dest.offset + size * i; // `Size` operations ( // shift offsets from source allocation to destination allocation - offset + dest_offset - src.offset, + (offset + dest_offset) - src.offset, // `Size` operations reloc, ) })); @@ -748,29 +739,29 @@ impl Allocation { type Block = u64; /// A bitmask where each bit refers to the byte with the same index. If the bit is `true`, the byte -/// is defined. If it is `false` the byte is undefined. +/// is initialized. If it is `false` the byte is uninitialized. #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] #[derive(HashStable)] -pub struct UndefMask { +pub struct InitMask { blocks: Vec, len: Size, } -impl UndefMask { +impl InitMask { pub const BLOCK_SIZE: u64 = 64; pub fn new(size: Size, state: bool) -> Self { - let mut m = UndefMask { blocks: vec![], len: Size::ZERO }; + let mut m = InitMask { blocks: vec![], len: Size::ZERO }; m.grow(size, state); m } - /// Checks whether the range `start..end` (end-exclusive) is entirely defined. + /// Checks whether the range `start..end` (end-exclusive) is entirely initialized. /// - /// Returns `Ok(())` if it's defined. Otherwise returns the index of the byte - /// at which the first undefined access begins. + /// Returns `Ok(())` if it's initialized. Otherwise returns the index of the byte + /// at which the first uninitialized access begins. #[inline] - pub fn is_range_defined(&self, start: Size, end: Size) -> Result<(), Size> { + pub fn is_range_initialized(&self, start: Size, end: Size) -> Result<(), Size> { if end > self.len { return Err(self.len); } @@ -861,27 +852,25 @@ impl UndefMask { if amount.bytes() == 0 { return; } - let unused_trailing_bits = self.blocks.len() as u64 * Self::BLOCK_SIZE - self.len.bytes(); + let unused_trailing_bits = + u64::try_from(self.blocks.len()).unwrap() * Self::BLOCK_SIZE - self.len.bytes(); if amount.bytes() > unused_trailing_bits { let additional_blocks = amount.bytes() / Self::BLOCK_SIZE + 1; - assert_eq!(additional_blocks as usize as u64, additional_blocks); self.blocks.extend( // FIXME(oli-obk): optimize this by repeating `new_state as Block`. - iter::repeat(0).take(additional_blocks as usize), + iter::repeat(0).take(usize::try_from(additional_blocks).unwrap()), ); } let start = self.len; self.len += amount; - self.set_range_inbounds(start, start + amount, new_state); + self.set_range_inbounds(start, start + amount, new_state); // `Size` operation } } #[inline] fn bit_index(bits: Size) -> (usize, usize) { let bits = bits.bytes(); - let a = bits / UndefMask::BLOCK_SIZE; - let b = bits % UndefMask::BLOCK_SIZE; - assert_eq!(a as usize as u64, a); - assert_eq!(b as usize as u64, b); - (a as usize, b as usize) + let a = bits / InitMask::BLOCK_SIZE; + let b = bits % InitMask::BLOCK_SIZE; + (usize::try_from(a).unwrap(), usize::try_from(b).unwrap()) } diff --git a/src/librustc_middle/mir/interpret/error.rs b/src/librustc_middle/mir/interpret/error.rs new file mode 100644 index 0000000000000..06fe3793b2383 --- /dev/null +++ b/src/librustc_middle/mir/interpret/error.rs @@ -0,0 +1,611 @@ +use super::{AllocId, Pointer, RawConst, ScalarMaybeUninit}; + +use crate::mir::interpret::ConstValue; +use crate::ty::layout::LayoutError; +use crate::ty::query::TyCtxtAt; +use crate::ty::{self, layout, tls, FnSig, Ty}; + +use rustc_data_structures::sync::Lock; +use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::definitions::DefPathData; +use rustc_macros::HashStable; +use rustc_session::CtfeBacktrace; +use rustc_span::{def_id::DefId, Pos, Span}; +use rustc_target::abi::{Align, Size}; +use std::{any::Any, backtrace::Backtrace, fmt, mem}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)] +pub enum ErrorHandled { + /// Already reported an error for this evaluation, and the compilation is + /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`. + Reported(ErrorReported), + /// Already emitted a lint for this evaluation. + Linted, + /// Don't emit an error, the evaluation failed because the MIR was generic + /// and the substs didn't fully monomorphize it. + TooGeneric, +} + +CloneTypeFoldableImpls! { + ErrorHandled, +} + +pub type ConstEvalRawResult<'tcx> = Result, ErrorHandled>; +pub type ConstEvalResult<'tcx> = Result, ErrorHandled>; + +#[derive(Debug)] +pub struct ConstEvalErr<'tcx> { + pub span: Span, + pub error: crate::mir::interpret::InterpError<'tcx>, + pub stacktrace: Vec>, +} + +#[derive(Debug)] +pub struct FrameInfo<'tcx> { + pub instance: ty::Instance<'tcx>, + pub span: Span, + pub lint_root: Option, +} + +impl<'tcx> fmt::Display for FrameInfo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data + == DefPathData::ClosureExpr + { + write!(f, "inside closure")?; + } else { + write!(f, "inside `{}`", self.instance)?; + } + if !self.span.is_dummy() { + let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo()); + write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?; + } + Ok(()) + }) + } +} + +impl<'tcx> ConstEvalErr<'tcx> { + pub fn struct_error( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + emit: impl FnOnce(DiagnosticBuilder<'_>), + ) -> ErrorHandled { + self.struct_generic(tcx, message, emit, None) + } + + pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { + self.struct_error(tcx, message, |mut e| e.emit()) + } + + pub fn report_as_lint( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + lint_root: hir::HirId, + span: Option, + ) -> ErrorHandled { + self.struct_generic( + tcx, + message, + |mut lint: DiagnosticBuilder<'_>| { + // Apply the span. + if let Some(span) = span { + let primary_spans = lint.span.primary_spans().to_vec(); + // point at the actual error as the primary span + lint.replace_span_with(span); + // point to the `const` statement as a secondary span + // they don't have any label + for sp in primary_spans { + if sp != span { + lint.span_label(sp, ""); + } + } + } + lint.emit(); + }, + Some(lint_root), + ) + } + + /// Create a diagnostic for this const eval error. + /// + /// Sets the message passed in via `message` and adds span labels with detailed error + /// information before handing control back to `emit` to do any final processing. + /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit` + /// function to dispose of the diagnostic properly. + /// + /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. + /// (Except that for some errors, we ignore all that -- see `must_error` below.) + fn struct_generic( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + emit: impl FnOnce(DiagnosticBuilder<'_>), + lint_root: Option, + ) -> ErrorHandled { + let must_error = match self.error { + err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { + return ErrorHandled::TooGeneric; + } + err_inval!(TypeckError(error_reported)) => { + return ErrorHandled::Reported(error_reported); + } + // We must *always* hard error on these, even if the caller wants just a lint. + err_inval!(Layout(LayoutError::SizeOverflow(_))) => true, + _ => false, + }; + trace!("reporting const eval failure at {:?}", self.span); + + let err_msg = match &self.error { + InterpError::MachineStop(msg) => { + // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`). + // Should be turned into a string by now. + msg.downcast_ref::().expect("invalid MachineStop payload").clone() + } + err => err.to_string(), + }; + + let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option| { + if let Some(span_msg) = span_msg { + err.span_label(self.span, span_msg); + } + // Add spans for the stacktrace. Don't print a single-line backtrace though. + if self.stacktrace.len() > 1 { + for frame_info in &self.stacktrace { + err.span_label(frame_info.span, frame_info.to_string()); + } + } + // Let the caller finish the job. + emit(err) + }; + + if must_error { + // The `message` makes little sense here, this is a more serious error than the + // caller thinks anyway. + // See . + finish(struct_error(tcx, &err_msg), None); + ErrorHandled::Reported(ErrorReported) + } else { + // Regular case. + if let Some(lint_root) = lint_root { + // Report as lint. + let hir_id = self + .stacktrace + .iter() + .rev() + .find_map(|frame| frame.lint_root) + .unwrap_or(lint_root); + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::CONST_ERR, + hir_id, + tcx.span, + |lint| finish(lint.build(message), Some(err_msg)), + ); + ErrorHandled::Linted + } else { + // Report as hard error. + finish(struct_error(tcx, message), Some(err_msg)); + ErrorHandled::Reported(ErrorReported) + } + } + } +} + +pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> { + struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) +} + +/// Packages the kind of error we got from the const code interpreter +/// up with a Rust-level backtrace of where the error occurred. +/// Thsese should always be constructed by calling `.into()` on +/// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*` +/// macros for this. +#[derive(Debug)] +pub struct InterpErrorInfo<'tcx> { + pub kind: InterpError<'tcx>, + backtrace: Option>, +} + +impl fmt::Display for InterpErrorInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.kind) + } +} + +impl InterpErrorInfo<'_> { + pub fn print_backtrace(&self) { + if let Some(backtrace) = self.backtrace.as_ref() { + print_backtrace(backtrace); + } + } +} + +fn print_backtrace(backtrace: &Backtrace) { + eprintln!("\n\nAn error occurred in miri:\n{}", backtrace); +} + +impl From for InterpErrorInfo<'_> { + fn from(err: ErrorHandled) -> Self { + match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + err_inval!(ReferencedConstant) + } + ErrorHandled::TooGeneric => err_inval!(TooGeneric), + } + .into() + } +} + +impl<'tcx> From> for InterpErrorInfo<'tcx> { + fn from(kind: InterpError<'tcx>) -> Self { + let capture_backtrace = tls::with_context_opt(|ctxt| { + if let Some(ctxt) = ctxt { + *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace) + } else { + CtfeBacktrace::Disabled + } + }); + + let backtrace = match capture_backtrace { + CtfeBacktrace::Disabled => None, + CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())), + CtfeBacktrace::Immediate => { + // Print it now. + let backtrace = Backtrace::force_capture(); + print_backtrace(&backtrace); + None + } + }; + + InterpErrorInfo { kind, backtrace } + } +} + +/// Error information for when the program we executed turned out not to actually be a valid +/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp +/// where we work on generic code or execution does not have all information available. +pub enum InvalidProgramInfo<'tcx> { + /// Resolution can fail if we are in a too generic context. + TooGeneric, + /// Cannot compute this constant because it depends on another one + /// which already produced an error. + ReferencedConstant, + /// Abort in case type errors are reached. + TypeckError(ErrorReported), + /// An error occurred during layout computation. + Layout(layout::LayoutError<'tcx>), + /// An invalid transmute happened. + TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), +} + +impl fmt::Display for InvalidProgramInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InvalidProgramInfo::*; + match self { + TooGeneric => write!(f, "encountered overly generic constant"), + ReferencedConstant => write!(f, "referenced constant has errors"), + TypeckError(ErrorReported) => { + write!(f, "encountered constants with type errors, stopping evaluation") + } + Layout(ref err) => write!(f, "{}", err), + TransmuteSizeDiff(from_ty, to_ty) => write!( + f, + "transmuting `{}` to `{}` is not possible, because these types do not have the same size", + from_ty, to_ty + ), + } + } +} + +/// Details of why a pointer had to be in-bounds. +#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] +pub enum CheckInAllocMsg { + MemoryAccessTest, + NullPointerTest, + PointerArithmeticTest, + InboundsTest, +} + +impl fmt::Display for CheckInAllocMsg { + /// When this is printed as an error the context looks like this + /// "{test name} failed: pointer must be in-bounds at offset..." + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match *self { + CheckInAllocMsg::MemoryAccessTest => "memory access", + CheckInAllocMsg::NullPointerTest => "NULL pointer test", + CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic", + CheckInAllocMsg::InboundsTest => "inbounds test", + } + ) + } +} + +/// Error information for when the program caused Undefined Behavior. +pub enum UndefinedBehaviorInfo<'tcx> { + /// Free-form case. Only for errors that are never caught! + Ub(String), + /// Unreachable code was executed. + Unreachable, + /// A slice/array index projection went out-of-bounds. + BoundsCheckFailed { + len: u64, + index: u64, + }, + /// Something was divided by 0 (x / 0). + DivisionByZero, + /// Something was "remainded" by 0 (x % 0). + RemainderByZero, + /// Overflowing inbounds pointer arithmetic. + PointerArithOverflow, + /// Invalid metadata in a wide pointer (using `str` to avoid allocations). + InvalidMeta(&'static str), + /// Invalid drop function in vtable. + InvalidDropFn(FnSig<'tcx>), + /// Reading a C string that does not end within its allocation. + UnterminatedCString(Pointer), + /// Dereferencing a dangling pointer after it got freed. + PointerUseAfterFree(AllocId), + /// Used a pointer outside the bounds it is valid for. + PointerOutOfBounds { + ptr: Pointer, + msg: CheckInAllocMsg, + allocation_size: Size, + }, + /// Using an integer as a pointer in the wrong way. + DanglingIntPointer(u64, CheckInAllocMsg), + /// Used a pointer with bad alignment. + AlignmentCheckFailed { + required: Align, + has: Align, + }, + /// Writing to read-only memory. + WriteToReadOnly(AllocId), + // Trying to access the data behind a function pointer. + DerefFunctionPointer(AllocId), + /// The value validity check found a problem. + /// Should only be thrown by `validity.rs` and always point out which part of the value + /// is the problem. + ValidationFailure(String), + /// Using a non-boolean `u8` as bool. + InvalidBool(u8), + /// Using a non-character `u32` as character. + InvalidChar(u32), + /// An enum discriminant was set to a value which was outside the range of valid values. + InvalidDiscriminant(ScalarMaybeUninit), + /// Using a pointer-not-to-a-function as function pointer. + InvalidFunctionPointer(Pointer), + /// Using a string that is not valid UTF-8, + InvalidStr(std::str::Utf8Error), + /// Using uninitialized data where it is not allowed. + InvalidUninitBytes(Option), + /// Working with a local that is not currently live. + DeadLocal, + /// Data size is not equal to target size. + ScalarSizeMismatch { + target_size: u64, + data_size: u64, + }, +} + +impl fmt::Display for UndefinedBehaviorInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UndefinedBehaviorInfo::*; + match self { + Ub(msg) => write!(f, "{}", msg), + Unreachable => write!(f, "entering unreachable code"), + BoundsCheckFailed { ref len, ref index } => { + write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index) + } + DivisionByZero => write!(f, "dividing by zero"), + RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), + PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), + InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), + InvalidDropFn(sig) => write!( + f, + "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type", + sig + ), + UnterminatedCString(p) => write!( + f, + "reading a null-terminated string starting at {} with no null found before end of allocation", + p, + ), + PointerUseAfterFree(a) => { + write!(f, "pointer to {} was dereferenced after this allocation got freed", a) + } + PointerOutOfBounds { ptr, msg, allocation_size } => write!( + f, + "{} failed: pointer must be in-bounds at offset {}, \ + but is outside bounds of {} which has size {}", + msg, + ptr.offset.bytes(), + ptr.alloc_id, + allocation_size.bytes() + ), + DanglingIntPointer(_, CheckInAllocMsg::NullPointerTest) => { + write!(f, "NULL pointer is not allowed for this operation") + } + DanglingIntPointer(i, msg) => { + write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i) + } + AlignmentCheckFailed { required, has } => write!( + f, + "accessing memory with alignment {}, but alignment {} is required", + has.bytes(), + required.bytes() + ), + WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a), + DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a), + ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), + InvalidBool(b) => { + write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b) + } + InvalidChar(c) => { + write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c) + } + InvalidDiscriminant(val) => write!(f, "enum value has invalid discriminant: {}", val), + InvalidFunctionPointer(p) => { + write!(f, "using {} as function pointer but it does not point to a function", p) + } + InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err), + InvalidUninitBytes(Some(p)) => write!( + f, + "reading uninitialized memory at {}, but this operation requires initialized memory", + p + ), + InvalidUninitBytes(None) => write!( + f, + "using uninitialized data, but this operation requires initialized memory" + ), + DeadLocal => write!(f, "accessing a dead local variable"), + ScalarSizeMismatch { target_size, data_size } => write!( + f, + "scalar size mismatch: expected {} bytes but got {} bytes instead", + target_size, data_size + ), + } + } +} + +/// Error information for when the program did something that might (or might not) be correct +/// to do according to the Rust spec, but due to limitations in the interpreter, the +/// operation could not be carried out. These limitations can differ between CTFE and the +/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses. +pub enum UnsupportedOpInfo { + /// Free-form case. Only for errors that are never caught! + Unsupported(String), + /// Accessing an unsupported foreign static. + ReadForeignStatic(DefId), + /// Could not find MIR for a function. + NoMirFor(DefId), + /// Encountered a pointer where we needed raw bytes. + ReadPointerAsBytes, + // + // The variants below are only reachable from CTFE/const prop, miri will never emit them. + // + /// Encountered raw bytes where we needed a pointer. + ReadBytesAsPointer, +} + +impl fmt::Display for UnsupportedOpInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UnsupportedOpInfo::*; + match self { + Unsupported(ref msg) => write!(f, "{}", msg), + ReadForeignStatic(did) => { + write!(f, "cannot read from foreign (extern) static {:?}", did) + } + NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did), + ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",), + ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), + } + } +} + +/// Error information for when the program exhausted the resources granted to it +/// by the interpreter. +pub enum ResourceExhaustionInfo { + /// The stack grew too big. + StackFrameLimitReached, + /// The program ran for too long. + /// + /// The exact limit is set by the `const_eval_limit` attribute. + StepLimitReached, +} + +impl fmt::Display for ResourceExhaustionInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use ResourceExhaustionInfo::*; + match self { + StackFrameLimitReached => { + write!(f, "reached the configured maximum number of stack frames") + } + StepLimitReached => { + write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)") + } + } + } +} + +/// A trait to work around not having trait object upcasting. +pub trait AsAny: Any { + fn as_any(&self) -> &dyn Any; +} +impl AsAny for T { + #[inline(always)] + fn as_any(&self) -> &dyn Any { + self + } +} + +/// A trait for machine-specific errors (or other "machine stop" conditions). +pub trait MachineStopType: AsAny + fmt::Display + Send {} +impl MachineStopType for String {} + +impl dyn MachineStopType { + #[inline(always)] + pub fn downcast_ref(&self) -> Option<&T> { + self.as_any().downcast_ref() + } +} + +pub enum InterpError<'tcx> { + /// The program caused undefined behavior. + UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), + /// The program did something the interpreter does not support (some of these *might* be UB + /// but the interpreter is not sure). + Unsupported(UnsupportedOpInfo), + /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). + InvalidProgram(InvalidProgramInfo<'tcx>), + /// The program exhausted the interpreter's resources (stack/heap too big, + /// execution takes too long, ...). + ResourceExhaustion(ResourceExhaustionInfo), + /// Stop execution for a machine-controlled reason. This is never raised by + /// the core engine itself. + MachineStop(Box), +} + +pub type InterpResult<'tcx, T = ()> = Result>; + +impl fmt::Display for InterpError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InterpError::*; + match *self { + Unsupported(ref msg) => write!(f, "{}", msg), + InvalidProgram(ref msg) => write!(f, "{}", msg), + UndefinedBehavior(ref msg) => write!(f, "{}", msg), + ResourceExhaustion(ref msg) => write!(f, "{}", msg), + MachineStop(ref msg) => write!(f, "{}", msg), + } + } +} + +// Forward `Debug` to `Display`, so it does not look awful. +impl fmt::Debug for InterpError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl InterpError<'_> { + /// Some errors allocate to be created as they contain free-form strings. + /// And sometimes we want to be sure that did not happen as it is a + /// waste of resources. + pub fn allocates(&self) -> bool { + match self { + // Zero-sized boxes do not allocate. + InterpError::MachineStop(b) => mem::size_of_val::(&**b) > 0, + InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) => true, + _ => false, + } + } +} diff --git a/src/librustc_middle/mir/interpret/mod.rs b/src/librustc_middle/mir/interpret/mod.rs new file mode 100644 index 0000000000000..71adb2fa477ad --- /dev/null +++ b/src/librustc_middle/mir/interpret/mod.rs @@ -0,0 +1,600 @@ +//! An interpreter for MIR used in CTFE and by miri. + +#[macro_export] +macro_rules! err_unsup { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::Unsupported( + $crate::mir::interpret::UnsupportedOpInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_unsup_format { + ($($tt:tt)*) => { err_unsup!(Unsupported(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! err_inval { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::InvalidProgram( + $crate::mir::interpret::InvalidProgramInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_ub { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::UndefinedBehavior( + $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_ub_format { + ($($tt:tt)*) => { err_ub!(Ub(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! err_exhaust { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::ResourceExhaustion( + $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_machine_stop { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) + }; +} + +// In the `throw_*` macros, avoid `return` to make them work with `try {}`. +#[macro_export] +macro_rules! throw_unsup { + ($($tt:tt)*) => { Err::(err_unsup!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_unsup_format { + ($($tt:tt)*) => { throw_unsup!(Unsupported(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! throw_inval { + ($($tt:tt)*) => { Err::(err_inval!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_ub { + ($($tt:tt)*) => { Err::(err_ub!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_ub_format { + ($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! throw_exhaust { + ($($tt:tt)*) => { Err::(err_exhaust!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_machine_stop { + ($($tt:tt)*) => { Err::(err_machine_stop!($($tt)*))? }; +} + +mod allocation; +mod error; +mod pointer; +mod queries; +mod value; + +use std::convert::TryFrom; +use std::fmt; +use std::io; +use std::num::NonZeroU32; +use std::sync::atomic::{AtomicU32, Ordering}; + +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{HashMapExt, Lock}; +use rustc_data_structures::tiny_list::TinyList; +use rustc_hir::def_id::DefId; +use rustc_macros::HashStable; +use rustc_serialize::{Decodable, Encodable, Encoder}; +use rustc_target::abi::{Endian, Size}; + +use crate::mir; +use crate::ty::codec::TyDecoder; +use crate::ty::subst::GenericArgKind; +use crate::ty::{self, Instance, Ty, TyCtxt}; + +pub use self::error::{ + struct_error, CheckInAllocMsg, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled, + FrameInfo, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, + ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, +}; + +pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit}; + +pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations}; + +pub use self::pointer::{Pointer, PointerArithmetic}; + +/// Uniquely identifies one of the following: +/// - A constant +/// - A static +/// - A const fn where all arguments (if any) are zero-sized types +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, Lift)] +pub struct GlobalId<'tcx> { + /// For a constant or static, the `Instance` of the item itself. + /// For a promoted global, the `Instance` of the function they belong to. + pub instance: ty::Instance<'tcx>, + + /// The index for promoted globals within their function's `mir::Body`. + pub promoted: Option, +} + +/// Input argument for `tcx.lit_to_const`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)] +pub struct LitToConstInput<'tcx> { + /// The absolute value of the resultant constant. + pub lit: &'tcx LitKind, + /// The type of the constant. + pub ty: Ty<'tcx>, + /// If the constant is negative. + pub neg: bool, +} + +/// Error type for `tcx.lit_to_const`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] +pub enum LitToConstError { + /// The literal's inferred type did not match the expected `ty` in the input. + /// This is used for graceful error handling (`delay_span_bug`) in + /// type checking (`Const::from_anon_const`). + TypeError, + UnparseableFloat, + Reported, +} + +#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct AllocId(pub u64); + +// We want the `Debug` output to be readable as it is used by `derive(Debug)` for +// all the Miri types. +impl fmt::Debug for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) } + } +} + +impl fmt::Display for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl rustc_serialize::UseSpecializedEncodable for AllocId {} +impl rustc_serialize::UseSpecializedDecodable for AllocId {} + +#[derive(RustcDecodable, RustcEncodable)] +enum AllocDiscriminant { + Alloc, + Fn, + Static, +} + +pub fn specialized_encode_alloc_id<'tcx, E: Encoder>( + encoder: &mut E, + tcx: TyCtxt<'tcx>, + alloc_id: AllocId, +) -> Result<(), E::Error> { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + AllocDiscriminant::Alloc.encode(encoder)?; + alloc.encode(encoder)?; + } + GlobalAlloc::Function(fn_instance) => { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + AllocDiscriminant::Fn.encode(encoder)?; + fn_instance.encode(encoder)?; + } + GlobalAlloc::Static(did) => { + // References to statics doesn't need to know about their allocations, + // just about its `DefId`. + AllocDiscriminant::Static.encode(encoder)?; + did.encode(encoder)?; + } + } + Ok(()) +} + +// Used to avoid infinite recursion when decoding cyclic allocations. +type DecodingSessionId = NonZeroU32; + +#[derive(Clone)] +enum State { + Empty, + InProgressNonAlloc(TinyList), + InProgress(TinyList, AllocId), + Done(AllocId), +} + +pub struct AllocDecodingState { + // For each `AllocId`, we keep track of which decoding state it's currently in. + decoding_state: Vec>, + // The offsets of each allocation in the data stream. + data_offsets: Vec, +} + +impl AllocDecodingState { + pub fn new_decoding_session(&self) -> AllocDecodingSession<'_> { + static DECODER_SESSION_ID: AtomicU32 = AtomicU32::new(0); + let counter = DECODER_SESSION_ID.fetch_add(1, Ordering::SeqCst); + + // Make sure this is never zero. + let session_id = DecodingSessionId::new((counter & 0x7FFFFFFF) + 1).unwrap(); + + AllocDecodingSession { state: self, session_id } + } + + pub fn new(data_offsets: Vec) -> Self { + let decoding_state = vec![Lock::new(State::Empty); data_offsets.len()]; + + Self { decoding_state, data_offsets } + } +} + +#[derive(Copy, Clone)] +pub struct AllocDecodingSession<'s> { + state: &'s AllocDecodingState, + session_id: DecodingSessionId, +} + +impl<'s> AllocDecodingSession<'s> { + /// Decodes an `AllocId` in a thread-safe way. + pub fn decode_alloc_id(&self, decoder: &mut D) -> Result + where + D: TyDecoder<'tcx>, + { + // Read the index of the allocation. + let idx = usize::try_from(decoder.read_u32()?).unwrap(); + let pos = usize::try_from(self.state.data_offsets[idx]).unwrap(); + + // Decode the `AllocDiscriminant` now so that we know if we have to reserve an + // `AllocId`. + let (alloc_kind, pos) = decoder.with_position(pos, |decoder| { + let alloc_kind = AllocDiscriminant::decode(decoder)?; + Ok((alloc_kind, decoder.position())) + })?; + + // Check the decoding state to see if it's already decoded or if we should + // decode it here. + let alloc_id = { + let mut entry = self.state.decoding_state[idx].lock(); + + match *entry { + State::Done(alloc_id) => { + return Ok(alloc_id); + } + ref mut entry @ State::Empty => { + // We are allowed to decode. + match alloc_kind { + AllocDiscriminant::Alloc => { + // If this is an allocation, we need to reserve an + // `AllocId` so we can decode cyclic graphs. + let alloc_id = decoder.tcx().reserve_alloc_id(); + *entry = + State::InProgress(TinyList::new_single(self.session_id), alloc_id); + Some(alloc_id) + } + AllocDiscriminant::Fn | AllocDiscriminant::Static => { + // Fns and statics cannot be cyclic, and their `AllocId` + // is determined later by interning. + *entry = + State::InProgressNonAlloc(TinyList::new_single(self.session_id)); + None + } + } + } + State::InProgressNonAlloc(ref mut sessions) => { + if sessions.contains(&self.session_id) { + bug!("this should be unreachable"); + } else { + // Start decoding concurrently. + sessions.insert(self.session_id); + None + } + } + State::InProgress(ref mut sessions, alloc_id) => { + if sessions.contains(&self.session_id) { + // Don't recurse. + return Ok(alloc_id); + } else { + // Start decoding concurrently. + sessions.insert(self.session_id); + Some(alloc_id) + } + } + } + }; + + // Now decode the actual data. + let alloc_id = decoder.with_position(pos, |decoder| { + match alloc_kind { + AllocDiscriminant::Alloc => { + let alloc = <&'tcx Allocation as Decodable>::decode(decoder)?; + // We already have a reserved `AllocId`. + let alloc_id = alloc_id.unwrap(); + trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc); + decoder.tcx().set_alloc_id_same_memory(alloc_id, alloc); + Ok(alloc_id) + } + AllocDiscriminant::Fn => { + assert!(alloc_id.is_none()); + trace!("creating fn alloc ID"); + let instance = ty::Instance::decode(decoder)?; + trace!("decoded fn alloc instance: {:?}", instance); + let alloc_id = decoder.tcx().create_fn_alloc(instance); + Ok(alloc_id) + } + AllocDiscriminant::Static => { + assert!(alloc_id.is_none()); + trace!("creating extern static alloc ID"); + let did = DefId::decode(decoder)?; + trace!("decoded static def-ID: {:?}", did); + let alloc_id = decoder.tcx().create_static_alloc(did); + Ok(alloc_id) + } + } + })?; + + self.state.decoding_state[idx].with_lock(|entry| { + *entry = State::Done(alloc_id); + }); + + Ok(alloc_id) + } +} + +/// An allocation in the global (tcx-managed) memory can be either a function pointer, +/// a static, or a "real" allocation with some data in it. +#[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable, HashStable)] +pub enum GlobalAlloc<'tcx> { + /// The alloc ID is used as a function pointer. + Function(Instance<'tcx>), + /// The alloc ID points to a "lazy" static variable that did not get computed (yet). + /// This is also used to break the cycle in recursive statics. + Static(DefId), + /// The alloc ID points to memory. + Memory(&'tcx Allocation), +} + +impl GlobalAlloc<'tcx> { + /// Panics if the `GlobalAlloc` does not refer to an `GlobalAlloc::Memory` + #[track_caller] + #[inline] + pub fn unwrap_memory(&self) -> &'tcx Allocation { + match *self { + GlobalAlloc::Memory(mem) => mem, + _ => bug!("expected memory, got {:?}", self), + } + } + + /// Panics if the `GlobalAlloc` is not `GlobalAlloc::Function` + #[track_caller] + #[inline] + pub fn unwrap_fn(&self) -> Instance<'tcx> { + match *self { + GlobalAlloc::Function(instance) => instance, + _ => bug!("expected function, got {:?}", self), + } + } +} + +crate struct AllocMap<'tcx> { + /// Maps `AllocId`s to their corresponding allocations. + alloc_map: FxHashMap>, + + /// Used to ensure that statics and functions only get one associated `AllocId`. + /// Should never contain a `GlobalAlloc::Memory`! + // + // FIXME: Should we just have two separate dedup maps for statics and functions each? + dedup: FxHashMap, AllocId>, + + /// The `AllocId` to assign to the next requested ID. + /// Always incremented; never gets smaller. + next_id: AllocId, +} + +impl<'tcx> AllocMap<'tcx> { + crate fn new() -> Self { + AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(0) } + } + fn reserve(&mut self) -> AllocId { + let next = self.next_id; + self.next_id.0 = self.next_id.0.checked_add(1).expect( + "You overflowed a u64 by incrementing by 1... \ + You've just earned yourself a free drink if we ever meet. \ + Seriously, how did you do that?!", + ); + next + } +} + +impl<'tcx> TyCtxt<'tcx> { + /// Obtains a new allocation ID that can be referenced but does not + /// yet have an allocation backing it. + /// + /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such + /// an `AllocId` from a query. + pub fn reserve_alloc_id(&self) -> AllocId { + self.alloc_map.lock().reserve() + } + + /// Reserves a new ID *if* this allocation has not been dedup-reserved before. + /// Should only be used for function pointers and statics, we don't want + /// to dedup IDs for "real" memory! + fn reserve_and_set_dedup(&self, alloc: GlobalAlloc<'tcx>) -> AllocId { + let mut alloc_map = self.alloc_map.lock(); + match alloc { + GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} + GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), + } + if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { + return alloc_id; + } + let id = alloc_map.reserve(); + debug!("creating alloc {:?} with id {}", alloc, id); + alloc_map.alloc_map.insert(id, alloc.clone()); + alloc_map.dedup.insert(alloc, id); + id + } + + /// Generates an `AllocId` for a static or return a cached one in case this function has been + /// called on the same static before. + pub fn create_static_alloc(&self, static_id: DefId) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::Static(static_id)) + } + + /// Generates an `AllocId` for a function. Depending on the function type, + /// this might get deduplicated or assigned a new ID each time. + pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> AllocId { + // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated + // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be + // duplicated across crates. + // We thus generate a new `AllocId` for every mention of a function. This means that + // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. + // However, formatting code relies on function identity (see #58320), so we only do + // this for generic functions. Lifetime parameters are ignored. + let is_generic = instance.substs.into_iter().any(|kind| match kind.unpack() { + GenericArgKind::Lifetime(_) => false, + _ => true, + }); + if is_generic { + // Get a fresh ID. + let mut alloc_map = self.alloc_map.lock(); + let id = alloc_map.reserve(); + alloc_map.alloc_map.insert(id, GlobalAlloc::Function(instance)); + id + } else { + // Deduplicate. + self.reserve_and_set_dedup(GlobalAlloc::Function(instance)) + } + } + + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical + /// `Allocation` with a different `AllocId`. + /// Statics with identical content will still point to the same `Allocation`, i.e., + /// their data will be deduplicated through `Allocation` interning -- but they + /// are different places in memory and as such need different IDs. + pub fn create_memory_alloc(&self, mem: &'tcx Allocation) -> AllocId { + let id = self.reserve_alloc_id(); + self.set_alloc_id_memory(id, mem); + id + } + + /// Returns `None` in case the `AllocId` is dangling. An `InterpretCx` can still have a + /// local `Allocation` for that `AllocId`, but having such an `AllocId` in a constant is + /// illegal and will likely ICE. + /// This function exists to allow const eval to detect the difference between evaluation- + /// local dangling pointers and allocations in constants/statics. + #[inline] + pub fn get_global_alloc(&self, id: AllocId) -> Option> { + self.alloc_map.lock().alloc_map.get(&id).cloned() + } + + #[inline] + #[track_caller] + /// Panics in case the `AllocId` is dangling. Since that is impossible for `AllocId`s in + /// constants (as all constants must pass interning and validation that check for dangling + /// ids), this function is frequently used throughout rustc, but should not be used within + /// the miri engine. + pub fn global_alloc(&self, id: AllocId) -> GlobalAlloc<'tcx> { + match self.get_global_alloc(id) { + Some(alloc) => alloc, + None => bug!("could not find allocation for {}", id), + } + } + + /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to + /// call this function twice, even with the same `Allocation` will ICE the compiler. + pub fn set_alloc_id_memory(&self, id: AllocId, mem: &'tcx Allocation) { + if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) { + bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old); + } + } + + /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called + /// twice for the same `(AllocId, Allocation)` pair. + fn set_alloc_id_same_memory(&self, id: AllocId, mem: &'tcx Allocation) { + self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Methods to access integers in the target endianness +//////////////////////////////////////////////////////////////////////////////// + +#[inline] +pub fn write_target_uint( + endianness: Endian, + mut target: &mut [u8], + data: u128, +) -> Result<(), io::Error> { + let len = target.len(); + match endianness { + Endian::Little => target.write_uint128::(data, len), + Endian::Big => target.write_uint128::(data, len), + } +} + +#[inline] +pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result { + match endianness { + Endian::Little => source.read_uint128::(source.len()), + Endian::Big => source.read_uint128::(source.len()), + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Methods to facilitate working with signed integers stored in a u128 +//////////////////////////////////////////////////////////////////////////////// + +/// Truncates `value` to `size` bits and then sign-extend it to 128 bits +/// (i.e., if it is negative, fill with 1's on the left). +#[inline] +pub fn sign_extend(value: u128, size: Size) -> u128 { + let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + // Sign-extend it. + let shift = 128 - size; + // Shift the unsigned value to the left, then shift back to the right as signed + // (essentially fills with FF on the left). + (((value << shift) as i128) >> shift) as u128 +} + +/// Truncates `value` to `size` bits. +#[inline] +pub fn truncate(value: u128, size: Size) -> u128 { + let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + let shift = 128 - size; + // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). + (value << shift) >> shift +} diff --git a/src/librustc_middle/mir/interpret/pointer.rs b/src/librustc_middle/mir/interpret/pointer.rs new file mode 100644 index 0000000000000..70cc546199b79 --- /dev/null +++ b/src/librustc_middle/mir/interpret/pointer.rs @@ -0,0 +1,200 @@ +use super::{AllocId, InterpResult}; + +use rustc_macros::HashStable; +use rustc_target::abi::{HasDataLayout, Size}; + +use std::convert::TryFrom; +use std::fmt; + +//////////////////////////////////////////////////////////////////////////////// +// Pointer arithmetic +//////////////////////////////////////////////////////////////////////////////// + +pub trait PointerArithmetic: HasDataLayout { + // These are not supposed to be overridden. + + #[inline(always)] + fn pointer_size(&self) -> Size { + self.data_layout().pointer_size + } + + #[inline] + fn machine_usize_max(&self) -> u64 { + let max_usize_plus_1 = 1u128 << self.pointer_size().bits(); + u64::try_from(max_usize_plus_1 - 1).unwrap() + } + + #[inline] + fn machine_isize_max(&self) -> i64 { + let max_isize_plus_1 = 1u128 << (self.pointer_size().bits() - 1); + i64::try_from(max_isize_plus_1 - 1).unwrap() + } + + /// Helper function: truncate given value-"overflowed flag" pair to pointer size and + /// update "overflowed flag" if there was an overflow. + /// This should be called by all the other methods before returning! + #[inline] + fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) { + let val = u128::from(val); + let max_ptr_plus_1 = 1u128 << self.pointer_size().bits(); + (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1) + } + + #[inline] + fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { + let res = val.overflowing_add(i); + self.truncate_to_ptr(res) + } + + #[inline] + fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) { + if i < 0 { + // Trickery to ensure that `i64::MIN` works fine: compute `n = -i`. + // This formula only works for true negative values; it overflows for zero! + let n = u64::MAX - (i as u64) + 1; + let res = val.overflowing_sub(n); + self.truncate_to_ptr(res) + } else { + // `i >= 0`, so the cast is safe. + self.overflowing_offset(val, i as u64) + } + } + + #[inline] + fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> { + let (res, over) = self.overflowing_offset(val, i); + if over { throw_ub!(PointerArithOverflow) } else { Ok(res) } + } + + #[inline] + fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> { + let (res, over) = self.overflowing_signed_offset(val, i); + if over { throw_ub!(PointerArithOverflow) } else { Ok(res) } + } +} + +impl PointerArithmetic for T {} + +/// Represents a pointer in the Miri engine. +/// +/// `Pointer` is generic over the `Tag` associated with each pointer, +/// which is used to do provenance tracking during execution. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] +#[derive(HashStable)] +pub struct Pointer { + pub alloc_id: AllocId, + pub offset: Size, + pub tag: Tag, +} + +static_assert_size!(Pointer, 16); + +/// Print the address of a pointer (without the tag) +fn print_ptr_addr(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Forward `alternate` flag to `alloc_id` printing. + if f.alternate() { + write!(f, "{:#?}", ptr.alloc_id)?; + } else { + write!(f, "{:?}", ptr.alloc_id)?; + } + // Print offset only if it is non-zero. + if ptr.offset.bytes() > 0 { + write!(f, "+0x{:x}", ptr.offset.bytes())?; + } + Ok(()) +} + +// We want the `Debug` output to be readable as it is used by `derive(Debug)` for +// all the Miri types. +// We have to use `Debug` output for the tag, because `()` does not implement +// `Display` so we cannot specialize that. +impl fmt::Debug for Pointer { + default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + print_ptr_addr(self, f)?; + write!(f, "[{:?}]", self.tag) + } +} +// Specialization for no tag +impl fmt::Debug for Pointer<()> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + print_ptr_addr(self, f) + } +} + +impl fmt::Display for Pointer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +/// Produces a `Pointer` that points to the beginning of the `Allocation`. +impl From for Pointer { + #[inline(always)] + fn from(alloc_id: AllocId) -> Self { + Pointer::new(alloc_id, Size::ZERO) + } +} + +impl Pointer<()> { + #[inline(always)] + pub fn new(alloc_id: AllocId, offset: Size) -> Self { + Pointer { alloc_id, offset, tag: () } + } + + #[inline(always)] + pub fn with_tag(self, tag: Tag) -> Pointer { + Pointer::new_with_tag(self.alloc_id, self.offset, tag) + } +} + +impl<'tcx, Tag> Pointer { + #[inline(always)] + pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self { + Pointer { alloc_id, offset, tag } + } + + #[inline] + pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { + Ok(Pointer::new_with_tag( + self.alloc_id, + Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?), + self.tag, + )) + } + + #[inline] + pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) { + let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes()); + (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over) + } + + #[inline(always)] + pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self { + self.overflowing_offset(i, cx).0 + } + + #[inline] + pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { + Ok(Pointer::new_with_tag( + self.alloc_id, + Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?), + self.tag, + )) + } + + #[inline] + pub fn overflowing_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> (Self, bool) { + let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i); + (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over) + } + + #[inline(always)] + pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self { + self.overflowing_signed_offset(i, cx).0 + } + + #[inline(always)] + pub fn erase_tag(self) -> Pointer { + Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () } + } +} diff --git a/src/librustc/mir/interpret/queries.rs b/src/librustc_middle/mir/interpret/queries.rs similarity index 88% rename from src/librustc/mir/interpret/queries.rs rename to src/librustc_middle/mir/interpret/queries.rs index 46bf1d9695796..a7953f0f900fb 100644 --- a/src/librustc/mir/interpret/queries.rs +++ b/src/librustc_middle/mir/interpret/queries.rs @@ -39,12 +39,13 @@ impl<'tcx> TyCtxt<'tcx> { promoted: Option, span: Option, ) -> ConstEvalResult<'tcx> { - let instance = ty::Instance::resolve(self, param_env, def_id, substs); - if let Some(instance) = instance { - let cid = GlobalId { instance, promoted }; - self.const_eval_global_id(param_env, cid, span) - } else { - Err(ErrorHandled::TooGeneric) + match ty::Instance::resolve(self, param_env, def_id, substs) { + Ok(Some(instance)) => { + let cid = GlobalId { instance, promoted }; + self.const_eval_global_id(param_env, cid, span) + } + Ok(None) => Err(ErrorHandled::TooGeneric), + Err(error_reported) => Err(ErrorHandled::Reported(error_reported)), } } diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc_middle/mir/interpret/value.rs similarity index 80% rename from src/librustc/mir/interpret/value.rs rename to src/librustc_middle/mir/interpret/value.rs index 59e6b1b0c3788..0e913ff58bb4a 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc_middle/mir/interpret/value.rs @@ -1,14 +1,14 @@ +use std::convert::TryFrom; +use std::fmt; + use rustc_apfloat::{ ieee::{Double, Single}, Float, }; use rustc_macros::HashStable; -use std::fmt; +use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; -use crate::ty::{ - layout::{HasDataLayout, Size}, - ParamEnv, Ty, TyCtxt, -}; +use crate::ty::{ParamEnv, Ty, TyCtxt}; use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}; @@ -28,7 +28,7 @@ pub struct RawConst<'tcx> { pub enum ConstValue<'tcx> { /// Used only for types with `layout::abi::Scalar` ABI and ZSTs. /// - /// Not using the enum `Value` to encode that this must not be `Undef`. + /// Not using the enum `Value` to encode that this must not be `Uninit`. Scalar(Scalar), /// Used only for `&[u8]` and `&str` @@ -89,7 +89,7 @@ impl<'tcx> ConstValue<'tcx> { /// of a simple value or a pointer into another `Allocation` #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] #[derive(HashStable)] -pub enum Scalar { +pub enum Scalar { /// The raw bytes of a simple value. Raw { /// The first `size` bytes of `data` are the value. @@ -101,13 +101,15 @@ pub enum Scalar { /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the /// relocation and its associated offset together as a `Pointer` here. - Ptr(Pointer), + Ptr(Pointer), } #[cfg(target_arch = "x86_64")] static_assert_size!(Scalar, 24); -impl fmt::Debug for Scalar { +// We want the `Debug` output to be readable as it is used by `derive(Debug)` for +// all the Miri types. +impl fmt::Debug for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr) => write!(f, "{:?}", ptr), @@ -125,11 +127,11 @@ impl fmt::Debug for Scalar { } } -impl fmt::Display for Scalar { +impl fmt::Display for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Scalar::Ptr(_) => write!(f, "a pointer"), - Scalar::Raw { data, .. } => write!(f, "{}", data), + Scalar::Ptr(ptr) => write!(f, "pointer to {}", ptr), + Scalar::Raw { .. } => fmt::Debug::fmt(self, f), } } } @@ -156,7 +158,7 @@ impl Scalar<()> { #[inline(always)] fn check_data(data: u128, size: u8) { debug_assert_eq!( - truncate(data, Size::from_bytes(size as u64)), + truncate(data, Size::from_bytes(u64::from(size))), data, "Scalar value {:#x} exceeds size of {} bytes", data, @@ -189,7 +191,7 @@ impl<'tcx, Tag> Scalar { } #[inline] - pub fn ptr_null(cx: &impl HasDataLayout) -> Self { + pub fn null_ptr(cx: &impl HasDataLayout) -> Self { Scalar::Raw { data: 0, size: cx.data_layout().pointer_size.bytes() as u8 } } @@ -198,55 +200,54 @@ impl<'tcx, Tag> Scalar { Scalar::Raw { data: 0, size: 0 } } - #[inline] - pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - let dl = cx.data_layout(); + #[inline(always)] + fn ptr_op( + self, + dl: &TargetDataLayout, + f_int: impl FnOnce(u64) -> InterpResult<'tcx, u64>, + f_ptr: impl FnOnce(Pointer) -> InterpResult<'tcx, Pointer>, + ) -> InterpResult<'tcx, Self> { match self { Scalar::Raw { data, size } => { - assert_eq!(size as u64, dl.pointer_size.bytes()); - Ok(Scalar::Raw { data: dl.offset(data as u64, i.bytes())? as u128, size }) + assert_eq!(u64::from(size), dl.pointer_size.bytes()); + Ok(Scalar::Raw { data: u128::from(f_int(u64::try_from(data).unwrap())?), size }) } - Scalar::Ptr(ptr) => ptr.offset(i, dl).map(Scalar::Ptr), + Scalar::Ptr(ptr) => Ok(Scalar::Ptr(f_ptr(ptr)?)), } } + #[inline] + pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { + let dl = cx.data_layout(); + self.ptr_op(dl, |int| dl.offset(int, i.bytes()), |ptr| ptr.offset(i, dl)) + } + #[inline] pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self { let dl = cx.data_layout(); - match self { - Scalar::Raw { data, size } => { - assert_eq!(size as u64, dl.pointer_size.bytes()); - Scalar::Raw { data: dl.overflowing_offset(data as u64, i.bytes()).0 as u128, size } - } - Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_offset(i, dl)), - } + self.ptr_op( + dl, + |int| Ok(dl.overflowing_offset(int, i.bytes()).0), + |ptr| Ok(ptr.wrapping_offset(i, dl)), + ) + .unwrap() } #[inline] pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { let dl = cx.data_layout(); - match self { - Scalar::Raw { data, size } => { - assert_eq!(size as u64, dl.pointer_size().bytes()); - Ok(Scalar::Raw { data: dl.signed_offset(data as u64, i)? as u128, size }) - } - Scalar::Ptr(ptr) => ptr.signed_offset(i, dl).map(Scalar::Ptr), - } + self.ptr_op(dl, |int| dl.signed_offset(int, i), |ptr| ptr.signed_offset(i, dl)) } #[inline] pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self { let dl = cx.data_layout(); - match self { - Scalar::Raw { data, size } => { - assert_eq!(size as u64, dl.pointer_size.bytes()); - Scalar::Raw { - data: dl.overflowing_signed_offset(data as u64, i128::from(i)).0 as u128, - size, - } - } - Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_signed_offset(i, dl)), - } + self.ptr_op( + dl, + |int| Ok(dl.overflowing_signed_offset(int, i).0), + |ptr| Ok(ptr.wrapping_signed_offset(i, dl)), + ) + .unwrap() } #[inline] @@ -281,25 +282,25 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn from_u8(i: u8) -> Self { // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i as u128, size: 1 } + Scalar::Raw { data: i.into(), size: 1 } } #[inline] pub fn from_u16(i: u16) -> Self { // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i as u128, size: 2 } + Scalar::Raw { data: i.into(), size: 2 } } #[inline] pub fn from_u32(i: u32) -> Self { // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i as u128, size: 4 } + Scalar::Raw { data: i.into(), size: 4 } } #[inline] pub fn from_u64(i: u64) -> Self { // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i as u128, size: 8 } + Scalar::Raw { data: i.into(), size: 8 } } #[inline] @@ -376,7 +377,7 @@ impl<'tcx, Tag> Scalar { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); match self { Scalar::Raw { data, size } => { - assert_eq!(target_size.bytes(), size as u64); + assert_eq!(target_size.bytes(), u64::from(size)); Scalar::check_data(data, size); Ok(data) } @@ -394,7 +395,12 @@ impl<'tcx, Tag> Scalar { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); match self { Scalar::Raw { data, size } => { - assert_eq!(target_size.bytes(), size as u64); + if target_size.bytes() != u64::from(size) { + throw_ub!(ScalarSizeMismatch { + target_size: target_size.bytes(), + data_size: u64::from(size), + }); + } Scalar::check_data(data, size); Ok(data) } @@ -458,27 +464,27 @@ impl<'tcx, Tag> Scalar { /// Converts the scalar to produce an `u8`. Fails if the scalar is a pointer. pub fn to_u8(self) -> InterpResult<'static, u8> { - self.to_unsigned_with_bit_width(8).map(|v| v as u8) + self.to_unsigned_with_bit_width(8).map(|v| u8::try_from(v).unwrap()) } /// Converts the scalar to produce an `u16`. Fails if the scalar is a pointer. pub fn to_u16(self) -> InterpResult<'static, u16> { - self.to_unsigned_with_bit_width(16).map(|v| v as u16) + self.to_unsigned_with_bit_width(16).map(|v| u16::try_from(v).unwrap()) } /// Converts the scalar to produce an `u32`. Fails if the scalar is a pointer. pub fn to_u32(self) -> InterpResult<'static, u32> { - self.to_unsigned_with_bit_width(32).map(|v| v as u32) + self.to_unsigned_with_bit_width(32).map(|v| u32::try_from(v).unwrap()) } /// Converts the scalar to produce an `u64`. Fails if the scalar is a pointer. pub fn to_u64(self) -> InterpResult<'static, u64> { - self.to_unsigned_with_bit_width(64).map(|v| v as u64) + self.to_unsigned_with_bit_width(64).map(|v| u64::try_from(v).unwrap()) } pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'static, u64> { let b = self.to_bits(cx.data_layout().pointer_size)?; - Ok(b as u64) + Ok(u64::try_from(b).unwrap()) } #[inline] @@ -490,41 +496,41 @@ impl<'tcx, Tag> Scalar { /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer. pub fn to_i8(self) -> InterpResult<'static, i8> { - self.to_signed_with_bit_width(8).map(|v| v as i8) + self.to_signed_with_bit_width(8).map(|v| i8::try_from(v).unwrap()) } /// Converts the scalar to produce an `i16`. Fails if the scalar is a pointer. pub fn to_i16(self) -> InterpResult<'static, i16> { - self.to_signed_with_bit_width(16).map(|v| v as i16) + self.to_signed_with_bit_width(16).map(|v| i16::try_from(v).unwrap()) } /// Converts the scalar to produce an `i32`. Fails if the scalar is a pointer. pub fn to_i32(self) -> InterpResult<'static, i32> { - self.to_signed_with_bit_width(32).map(|v| v as i32) + self.to_signed_with_bit_width(32).map(|v| i32::try_from(v).unwrap()) } /// Converts the scalar to produce an `i64`. Fails if the scalar is a pointer. pub fn to_i64(self) -> InterpResult<'static, i64> { - self.to_signed_with_bit_width(64).map(|v| v as i64) + self.to_signed_with_bit_width(64).map(|v| i64::try_from(v).unwrap()) } pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'static, i64> { let sz = cx.data_layout().pointer_size; let b = self.to_bits(sz)?; let b = sign_extend(b, sz) as i128; - Ok(b as i64) + Ok(i64::try_from(b).unwrap()) } #[inline] pub fn to_f32(self) -> InterpResult<'static, Single> { // Going through `u32` to check size and truncation. - Ok(Single::from_bits(self.to_u32()? as u128)) + Ok(Single::from_bits(self.to_u32()?.into())) } #[inline] pub fn to_f64(self) -> InterpResult<'static, Double> { // Going through `u64` to check size and truncation. - Ok(Double::from_bits(self.to_u64()? as u128)) + Ok(Double::from_bits(self.to_u64()?.into())) } } @@ -536,60 +542,62 @@ impl From> for Scalar { } #[derive(Clone, Copy, Eq, PartialEq, RustcEncodable, RustcDecodable, HashStable, Hash)] -pub enum ScalarMaybeUndef { - Scalar(Scalar), - Undef, +pub enum ScalarMaybeUninit { + Scalar(Scalar), + Uninit, } -impl From> for ScalarMaybeUndef { +impl From> for ScalarMaybeUninit { #[inline(always)] fn from(s: Scalar) -> Self { - ScalarMaybeUndef::Scalar(s) + ScalarMaybeUninit::Scalar(s) } } -impl From> for ScalarMaybeUndef { +impl From> for ScalarMaybeUninit { #[inline(always)] fn from(s: Pointer) -> Self { - ScalarMaybeUndef::Scalar(s.into()) + ScalarMaybeUninit::Scalar(s.into()) } } -impl fmt::Debug for ScalarMaybeUndef { +// We want the `Debug` output to be readable as it is used by `derive(Debug)` for +// all the Miri types. +impl fmt::Debug for ScalarMaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ScalarMaybeUndef::Undef => write!(f, "Undef"), - ScalarMaybeUndef::Scalar(s) => write!(f, "{:?}", s), + ScalarMaybeUninit::Uninit => write!(f, ""), + ScalarMaybeUninit::Scalar(s) => write!(f, "{:?}", s), } } } -impl fmt::Display for ScalarMaybeUndef { +impl fmt::Display for ScalarMaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ScalarMaybeUndef::Undef => write!(f, "uninitialized bytes"), - ScalarMaybeUndef::Scalar(s) => write!(f, "{}", s), + ScalarMaybeUninit::Uninit => write!(f, "uninitialized bytes"), + ScalarMaybeUninit::Scalar(s) => write!(f, "{}", s), } } } -impl<'tcx, Tag> ScalarMaybeUndef { +impl<'tcx, Tag> ScalarMaybeUninit { /// Erase the tag from the scalar, if any. /// /// Used by error reporting code to avoid having the error type depend on `Tag`. #[inline] - pub fn erase_tag(self) -> ScalarMaybeUndef { + pub fn erase_tag(self) -> ScalarMaybeUninit { match self { - ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.erase_tag()), - ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef, + ScalarMaybeUninit::Scalar(s) => ScalarMaybeUninit::Scalar(s.erase_tag()), + ScalarMaybeUninit::Uninit => ScalarMaybeUninit::Uninit, } } #[inline] pub fn not_undef(self) -> InterpResult<'static, Scalar> { match self { - ScalarMaybeUndef::Scalar(scalar) => Ok(scalar), - ScalarMaybeUndef::Undef => throw_ub!(InvalidUndefBytes(None)), + ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), + ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), } } @@ -671,8 +679,8 @@ pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> data.get_bytes( cx, // invent a pointer, only the offset is relevant anyway - Pointer::new(AllocId(0), Size::from_bytes(start as u64)), - Size::from_bytes(len as u64), + Pointer::new(AllocId(0), Size::from_bytes(start)), + Size::from_bytes(len), ) .unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err)) } else { diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs new file mode 100644 index 0000000000000..8247338ae0fad --- /dev/null +++ b/src/librustc_middle/mir/mod.rs @@ -0,0 +1,2840 @@ +//! MIR datatypes and passes. See the [rustc dev guide] for more info. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html + +use crate::mir::interpret::{GlobalAlloc, Scalar}; +use crate::mir::visit::MirVisitable; +use crate::ty::adjustment::PointerCast; +use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::subst::{Subst, SubstsRef}; +use crate::ty::{ + self, AdtDef, CanonicalUserTypeAnnotations, List, Region, Ty, TyCtxt, UserTypeAnnotationIndex, +}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, Namespace}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self, GeneratorKind}; +use rustc_target::abi::VariantIdx; + +use polonius_engine::Atom; +pub use rustc_ast::ast::Mutability; +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph::dominators::{dominators, Dominators}; +use rustc_data_structures::graph::{self, GraphSuccessors}; +use rustc_index::bit_set::BitMatrix; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_macros::HashStable; +use rustc_serialize::{Decodable, Encodable}; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::asm::InlineAsmRegOrRegClass; +use std::borrow::Cow; +use std::fmt::{self, Debug, Display, Formatter, Write}; +use std::ops::{Index, IndexMut}; +use std::slice; +use std::{iter, mem, option}; + +use self::predecessors::{PredecessorCache, Predecessors}; +pub use self::query::*; + +pub mod interpret; +pub mod mono; +mod predecessors; +mod query; +pub mod tcx; +pub mod traversal; +mod type_foldable; +pub mod visit; + +/// Types for locals +type LocalDecls<'tcx> = IndexVec>; + +pub trait HasLocalDecls<'tcx> { + fn local_decls(&self) -> &LocalDecls<'tcx>; +} + +impl<'tcx> HasLocalDecls<'tcx> for LocalDecls<'tcx> { + fn local_decls(&self) -> &LocalDecls<'tcx> { + self + } +} + +impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> { + fn local_decls(&self) -> &LocalDecls<'tcx> { + &self.local_decls + } +} + +/// The various "big phases" that MIR goes through. +/// +/// Warning: ordering of variants is significant. +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(HashStable)] +pub enum MirPhase { + Build = 0, + Const = 1, + Validated = 2, + Optimized = 3, +} + +impl MirPhase { + /// Gets the index of the current MirPhase within the set of all `MirPhase`s. + pub fn phase_index(&self) -> usize { + *self as usize + } +} + +/// The lowered representation of a single function. +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable, TypeFoldable)] +pub struct Body<'tcx> { + /// A list of basic blocks. References to basic block use a newtyped index type `BasicBlock` + /// that indexes into this vector. + basic_blocks: IndexVec>, + + /// Records how far through the "desugaring and optimization" process this particular + /// MIR has traversed. This is particularly useful when inlining, since in that context + /// we instantiate the promoted constants and add them to our promoted vector -- but those + /// promoted items have already been optimized, whereas ours have not. This field allows + /// us to see the difference and forego optimization on the inlined promoted items. + pub phase: MirPhase, + + /// A list of source scopes; these are referenced by statements + /// and used for debuginfo. Indexed by a `SourceScope`. + pub source_scopes: IndexVec, + + /// The yield type of the function, if it is a generator. + pub yield_ty: Option>, + + /// Generator drop glue. + pub generator_drop: Option>>, + + /// The layout of a generator. Produced by the state transformation. + pub generator_layout: Option>, + + /// If this is a generator then record the type of source expression that caused this generator + /// to be created. + pub generator_kind: Option, + + /// Declarations of locals. + /// + /// The first local is the return value pointer, followed by `arg_count` + /// locals for the function arguments, followed by any user-declared + /// variables and temporaries. + pub local_decls: LocalDecls<'tcx>, + + /// User type annotations. + pub user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, + + /// The number of arguments this function takes. + /// + /// Starting at local 1, `arg_count` locals will be provided by the caller + /// and can be assumed to be initialized. + /// + /// If this MIR was built for a constant, this will be 0. + pub arg_count: usize, + + /// Mark an argument local (which must be a tuple) as getting passed as + /// its individual components at the LLVM level. + /// + /// This is used for the "rust-call" ABI. + pub spread_arg: Option, + + /// Debug information pertaining to user variables, including captures. + pub var_debug_info: Vec>, + + /// Mark this MIR of a const context other than const functions as having converted a `&&` or + /// `||` expression into `&` or `|` respectively. This is problematic because if we ever stop + /// this conversion from happening and use short circuiting, we will cause the following code + /// to change the value of `x`: `let mut x = 42; false && { x = 55; true };` + /// + /// List of places where control flow was destroyed. Used for error reporting. + pub control_flow_destroyed: Vec<(Span, String)>, + + /// A span representing this MIR, for error reporting. + pub span: Span, + + /// Constants that are required to evaluate successfully for this MIR to be well-formed. + /// We hold in this field all the constants we are not able to evaluate yet. + pub required_consts: Vec>, + + /// The user may be writing e.g. `&[(SOME_CELL, 42)][i].1` and this would get promoted, because + /// we'd statically know that no thing with interior mutability will ever be available to the + /// user without some serious unsafe code. Now this means that our promoted is actually + /// `&[(SOME_CELL, 42)]` and the MIR using it will do the `&promoted[i].1` projection because the + /// index may be a runtime value. Such a promoted value is illegal because it has reachable + /// interior mutability. This flag just makes this situation very obvious where the previous + /// implementation without the flag hid this situation silently. + /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. + pub ignore_interior_mut_in_const_validation: bool, + + predecessor_cache: PredecessorCache, +} + +impl<'tcx> Body<'tcx> { + pub fn new( + basic_blocks: IndexVec>, + source_scopes: IndexVec, + local_decls: LocalDecls<'tcx>, + user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, + arg_count: usize, + var_debug_info: Vec>, + span: Span, + control_flow_destroyed: Vec<(Span, String)>, + generator_kind: Option, + ) -> Self { + // We need `arg_count` locals, and one for the return place. + assert!( + local_decls.len() > arg_count, + "expected at least {} locals, got {}", + arg_count + 1, + local_decls.len() + ); + + Body { + phase: MirPhase::Build, + basic_blocks, + source_scopes, + yield_ty: None, + generator_drop: None, + generator_layout: None, + generator_kind, + local_decls, + user_type_annotations, + arg_count, + spread_arg: None, + var_debug_info, + span, + required_consts: Vec::new(), + ignore_interior_mut_in_const_validation: false, + control_flow_destroyed, + predecessor_cache: PredecessorCache::new(), + } + } + + /// Returns a partially initialized MIR body containing only a list of basic blocks. + /// + /// The returned MIR contains no `LocalDecl`s (even for the return place) or source scopes. It + /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different + /// crate. + pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { + Body { + phase: MirPhase::Build, + basic_blocks, + source_scopes: IndexVec::new(), + yield_ty: None, + generator_drop: None, + generator_layout: None, + local_decls: IndexVec::new(), + user_type_annotations: IndexVec::new(), + arg_count: 0, + spread_arg: None, + span: DUMMY_SP, + required_consts: Vec::new(), + control_flow_destroyed: Vec::new(), + generator_kind: None, + var_debug_info: Vec::new(), + ignore_interior_mut_in_const_validation: false, + predecessor_cache: PredecessorCache::new(), + } + } + + #[inline] + pub fn basic_blocks(&self) -> &IndexVec> { + &self.basic_blocks + } + + #[inline] + pub fn basic_blocks_mut(&mut self) -> &mut IndexVec> { + // Because the user could mutate basic block terminators via this reference, we need to + // invalidate the predecessor cache. + // + // FIXME: Use a finer-grained API for this, so only transformations that alter terminators + // invalidate the predecessor cache. + self.predecessor_cache.invalidate(); + &mut self.basic_blocks + } + + #[inline] + pub fn basic_blocks_and_local_decls_mut( + &mut self, + ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { + self.predecessor_cache.invalidate(); + (&mut self.basic_blocks, &mut self.local_decls) + } + + /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the + /// `START_BLOCK`. + pub fn is_cfg_cyclic(&self) -> bool { + graph::is_cyclic(self) + } + + #[inline] + pub fn local_kind(&self, local: Local) -> LocalKind { + let index = local.as_usize(); + if index == 0 { + debug_assert!( + self.local_decls[local].mutability == Mutability::Mut, + "return place should be mutable" + ); + + LocalKind::ReturnPointer + } else if index < self.arg_count + 1 { + LocalKind::Arg + } else if self.local_decls[local].is_user_variable() { + LocalKind::Var + } else { + LocalKind::Temp + } + } + + /// Returns an iterator over all temporaries. + #[inline] + pub fn temps_iter<'a>(&'a self) -> impl Iterator + 'a { + (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + if self.local_decls[local].is_user_variable() { None } else { Some(local) } + }) + } + + /// Returns an iterator over all user-declared locals. + #[inline] + pub fn vars_iter<'a>(&'a self) -> impl Iterator + 'a { + (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + self.local_decls[local].is_user_variable().then_some(local) + }) + } + + /// Returns an iterator over all user-declared mutable locals. + #[inline] + pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator + 'a { + (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + let decl = &self.local_decls[local]; + if decl.is_user_variable() && decl.mutability == Mutability::Mut { + Some(local) + } else { + None + } + }) + } + + /// Returns an iterator over all user-declared mutable arguments and locals. + #[inline] + pub fn mut_vars_and_args_iter<'a>(&'a self) -> impl Iterator + 'a { + (1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + let decl = &self.local_decls[local]; + if (decl.is_user_variable() || index < self.arg_count + 1) + && decl.mutability == Mutability::Mut + { + Some(local) + } else { + None + } + }) + } + + /// Returns an iterator over all function arguments. + #[inline] + pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator { + let arg_count = self.arg_count; + (1..arg_count + 1).map(Local::new) + } + + /// Returns an iterator over all user-defined variables and compiler-generated temporaries (all + /// locals that are neither arguments nor the return place). + #[inline] + pub fn vars_and_temps_iter(&self) -> impl Iterator + ExactSizeIterator { + let arg_count = self.arg_count; + let local_count = self.local_decls.len(); + (arg_count + 1..local_count).map(Local::new) + } + + /// Changes a statement to a nop. This is both faster than deleting instructions and avoids + /// invalidating statement indices in `Location`s. + pub fn make_statement_nop(&mut self, location: Location) { + let block = &mut self.basic_blocks[location.block]; + debug_assert!(location.statement_index < block.statements.len()); + block.statements[location.statement_index].make_nop() + } + + /// Returns the source info associated with `location`. + pub fn source_info(&self, location: Location) -> &SourceInfo { + let block = &self[location.block]; + let stmts = &block.statements; + let idx = location.statement_index; + if idx < stmts.len() { + &stmts[idx].source_info + } else { + assert_eq!(idx, stmts.len()); + &block.terminator().source_info + } + } + + /// Checks if `sub` is a sub scope of `sup` + pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool { + while sub != sup { + match self.source_scopes[sub].parent_scope { + None => return false, + Some(p) => sub = p, + } + } + true + } + + /// Returns the return type; it always return first element from `local_decls` array. + #[inline] + pub fn return_ty(&self) -> Ty<'tcx> { + self.local_decls[RETURN_PLACE].ty + } + + /// Gets the location of the terminator for the given block. + #[inline] + pub fn terminator_loc(&self, bb: BasicBlock) -> Location { + Location { block: bb, statement_index: self[bb].statements.len() } + } + + #[inline] + pub fn predecessors(&self) -> impl std::ops::Deref + '_ { + self.predecessor_cache.compute(&self.basic_blocks) + } + + #[inline] + pub fn dominators(&self) -> Dominators { + dominators(self) + } +} + +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum Safety { + Safe, + /// Unsafe because of a PushUnsafeBlock + BuiltinUnsafe, + /// Unsafe because of an unsafe fn + FnUnsafe, + /// Unsafe because of an `unsafe` block + ExplicitUnsafe(hir::HirId), +} + +impl<'tcx> Index for Body<'tcx> { + type Output = BasicBlockData<'tcx>; + + #[inline] + fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { + &self.basic_blocks()[index] + } +} + +impl<'tcx> IndexMut for Body<'tcx> { + #[inline] + fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { + &mut self.basic_blocks_mut()[index] + } +} + +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)] +pub enum ClearCrossCrate { + Clear, + Set(T), +} + +impl ClearCrossCrate { + pub fn as_ref(&self) -> ClearCrossCrate<&T> { + match self { + ClearCrossCrate::Clear => ClearCrossCrate::Clear, + ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v), + } + } + + pub fn assert_crate_local(self) -> T { + match self { + ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"), + ClearCrossCrate::Set(v) => v, + } + } +} + +impl rustc_serialize::UseSpecializedEncodable for ClearCrossCrate {} +impl rustc_serialize::UseSpecializedDecodable for ClearCrossCrate {} + +/// Grouped information about the source code origin of a MIR entity. +/// Intended to be inspected by diagnostics and debuginfo. +/// Most passes can work with it as a whole, within a single function. +// The unofficial Cranelift backend, at least as of #65828, needs `SourceInfo` to implement `Eq` and +// `Hash`. Please ping @bjorn3 if removing them. +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)] +pub struct SourceInfo { + /// The source span for the AST pertaining to this MIR entity. + pub span: Span, + + /// The source scope, keeping track of which bindings can be + /// seen by debuginfo, active lint levels, `unsafe {...}`, etc. + pub scope: SourceScope, +} + +impl SourceInfo { + #[inline] + pub fn outermost(span: Span) -> Self { + SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Borrow kinds + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + Shared, + + /// The immediately borrowed place must be immutable, but projections from + /// it don't need to be. For example, a shallow borrow of `a.b` doesn't + /// conflict with a mutable borrow of `a.b.c`. + /// + /// This is used when lowering matches: when matching on a place we want to + /// ensure that place have the same value from the start of the match until + /// an arm is selected. This prevents this code from compiling: + /// + /// let mut x = &Some(0); + /// match *x { + /// None => (), + /// Some(_) if { x = &None; false } => (), + /// Some(_) => (), + /// } + /// + /// This can't be a shared borrow because mutably borrowing (*x as Some).0 + /// should not prevent `if let None = x { ... }`, for example, because the + /// mutating `(*x as Some).0` can't affect the discriminant of `x`. + /// We can also report errors with this kind of borrow differently. + Shallow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when the closure is + /// borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate an `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + Unique, + + /// Data is mutable and not aliasable. + Mut { + /// `true` if this borrow arose from method-call auto-ref + /// (i.e., `adjustment::Adjust::Borrow`). + allow_two_phase_borrow: bool, + }, +} + +impl BorrowKind { + pub fn allows_two_phase_borrow(&self) -> bool { + match *self { + BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false, + BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow, + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Variables and temps + +rustc_index::newtype_index! { + pub struct Local { + derive [HashStable] + DEBUG_FORMAT = "_{}", + const RETURN_PLACE = 0, + } +} + +impl Atom for Local { + fn index(self) -> usize { + Idx::index(self) + } +} + +/// Classifies locals into categories. See `Body::local_kind`. +#[derive(PartialEq, Eq, Debug, HashStable)] +pub enum LocalKind { + /// User-declared variable binding. + Var, + /// Compiler-introduced temporary. + Temp, + /// Function argument. + Arg, + /// Location of function's return value. + ReturnPointer, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct VarBindingForm<'tcx> { + /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? + pub binding_mode: ty::BindingMode, + /// If an explicit type was provided for this variable binding, + /// this holds the source Span of that type. + /// + /// NOTE: if you want to change this to a `HirId`, be wary that + /// doing so breaks incremental compilation (as of this writing), + /// while a `Span` does not cause our tests to fail. + pub opt_ty_info: Option, + /// Place of the RHS of the =, or the subject of the `match` where this + /// variable is initialized. None in the case of `let PATTERN;`. + /// Some((None, ..)) in the case of and `let [mut] x = ...` because + /// (a) the right-hand side isn't evaluated as a place expression. + /// (b) it gives a way to separate this case from the remaining cases + /// for diagnostics. + pub opt_match_place: Option<(Option>, Span)>, + /// The span of the pattern in which this variable was bound. + pub pat_span: Span, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum BindingForm<'tcx> { + /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. + Var(VarBindingForm<'tcx>), + /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit. + ImplicitSelf(ImplicitSelfKind), + /// Reference used in a guard expression to ensure immutability. + RefForGuard, +} + +/// Represents what type of implicit self a function has, if any. +#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum ImplicitSelfKind { + /// Represents a `fn x(self);`. + Imm, + /// Represents a `fn x(mut self);`. + Mut, + /// Represents a `fn x(&self);`. + ImmRef, + /// Represents a `fn x(&mut self);`. + MutRef, + /// Represents when a function does not have a self argument or + /// when a function has a `self: X` argument. + None, +} + +CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, } + +mod binding_form_impl { + use crate::ich::StableHashingContext; + use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + + impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + use super::BindingForm::*; + ::std::mem::discriminant(self).hash_stable(hcx, hasher); + + match self { + Var(binding) => binding.hash_stable(hcx, hasher), + ImplicitSelf(kind) => kind.hash_stable(hcx, hasher), + RefForGuard => (), + } + } + } +} + +/// `BlockTailInfo` is attached to the `LocalDecl` for temporaries +/// created during evaluation of expressions in a block tail +/// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`. +/// +/// It is used to improve diagnostics when such temporaries are +/// involved in borrow_check errors, e.g., explanations of where the +/// temporaries come from, when their destructors are run, and/or how +/// one might revise the code to satisfy the borrow checker's rules. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct BlockTailInfo { + /// If `true`, then the value resulting from evaluating this tail + /// expression is ignored by the block's expression context. + /// + /// Examples include `{ ...; tail };` and `let _ = { ...; tail };` + /// but not e.g., `let _x = { ...; tail };` + pub tail_result_is_ignored: bool, + + /// `Span` of the tail expression. + pub span: Span, +} + +/// A MIR local. +/// +/// This can be a binding declared by the user, a temporary inserted by the compiler, a function +/// argument, or the return place. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct LocalDecl<'tcx> { + /// Whether this is a mutable minding (i.e., `let x` or `let mut x`). + /// + /// Temporaries and the return place are always mutable. + pub mutability: Mutability, + + // FIXME(matthewjasper) Don't store in this in `Body` + pub local_info: Option>>, + + /// `true` if this is an internal local. + /// + /// These locals are not based on types in the source code and are only used + /// for a few desugarings at the moment. + /// + /// The generator transformation will sanity check the locals which are live + /// across a suspension point against the type components of the generator + /// which type checking knows are live across a suspension point. We need to + /// flag drop flags to avoid triggering this check as they are introduced + /// after typeck. + /// + /// Unsafety checking will also ignore dereferences of these locals, + /// so they can be used for raw pointers only used in a desugaring. + /// + /// This should be sound because the drop flags are fully algebraic, and + /// therefore don't affect the OIBIT or outlives properties of the + /// generator. + pub internal: bool, + + /// If this local is a temporary and `is_block_tail` is `Some`, + /// then it is a temporary created for evaluation of some + /// subexpression of some block's tail expression (with no + /// intervening statement context). + // FIXME(matthewjasper) Don't store in this in `Body` + pub is_block_tail: Option, + + /// The type of this local. + pub ty: Ty<'tcx>, + + /// If the user manually ascribed a type to this variable, + /// e.g., via `let x: T`, then we carry that type here. The MIR + /// borrow checker needs this information since it can affect + /// region inference. + // FIXME(matthewjasper) Don't store in this in `Body` + pub user_ty: Option>, + + /// The *syntactic* (i.e., not visibility) source scope the local is defined + /// in. If the local was defined in a let-statement, this + /// is *within* the let-statement, rather than outside + /// of it. + /// + /// This is needed because the visibility source scope of locals within + /// a let-statement is weird. + /// + /// The reason is that we want the local to be *within* the let-statement + /// for lint purposes, but we want the local to be *after* the let-statement + /// for names-in-scope purposes. + /// + /// That's it, if we have a let-statement like the one in this + /// function: + /// + /// ``` + /// fn foo(x: &str) { + /// #[allow(unused_mut)] + /// let mut x: u32 = { // <- one unused mut + /// let mut y: u32 = x.parse().unwrap(); + /// y + 2 + /// }; + /// drop(x); + /// } + /// ``` + /// + /// Then, from a lint point of view, the declaration of `x: u32` + /// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the + /// lint scopes are the same as the AST/HIR nesting. + /// + /// However, from a name lookup point of view, the scopes look more like + /// as if the let-statements were `match` expressions: + /// + /// ``` + /// fn foo(x: &str) { + /// match { + /// match x.parse().unwrap() { + /// y => y + 2 + /// } + /// } { + /// x => drop(x) + /// }; + /// } + /// ``` + /// + /// We care about the name-lookup scopes for debuginfo - if the + /// debuginfo instruction pointer is at the call to `x.parse()`, we + /// want `x` to refer to `x: &str`, but if it is at the call to + /// `drop(x)`, we want it to refer to `x: u32`. + /// + /// To allow both uses to work, we need to have more than a single scope + /// for a local. We have the `source_info.scope` represent the "syntactic" + /// lint scope (with a variable being under its let block) while the + /// `var_debug_info.source_info.scope` represents the "local variable" + /// scope (where the "rest" of a block is under all prior let-statements). + /// + /// The end result looks like this: + /// + /// ```text + /// ROOT SCOPE + /// │{ argument x: &str } + /// │ + /// │ │{ #[allow(unused_mut)] } // This is actually split into 2 scopes + /// │ │ // in practice because I'm lazy. + /// │ │ + /// │ │← x.source_info.scope + /// │ │← `x.parse().unwrap()` + /// │ │ + /// │ │ │← y.source_info.scope + /// │ │ + /// │ │ │{ let y: u32 } + /// │ │ │ + /// │ │ │← y.var_debug_info.source_info.scope + /// │ │ │← `y + 2` + /// │ + /// │ │{ let x: u32 } + /// │ │← x.var_debug_info.source_info.scope + /// │ │← `drop(x)` // This accesses `x: u32`. + /// ``` + pub source_info: SourceInfo, +} + +// `LocalDecl` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(LocalDecl<'_>, 56); + +/// Extra information about a some locals that's used for diagnostics. (Not +/// used for non-StaticRef temporaries, the return place, or anonymous function +/// parameters.) +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub enum LocalInfo<'tcx> { + /// A user-defined local variable or function parameter + /// + /// The `BindingForm` is solely used for local diagnostics when generating + /// warnings/errors when compiling the current crate, and therefore it need + /// not be visible across crates. + User(ClearCrossCrate>), + /// A temporary created that references the static with the given `DefId`. + StaticRef { def_id: DefId, is_thread_local: bool }, +} + +impl<'tcx> LocalDecl<'tcx> { + /// Returns `true` only if local is a binding that can itself be + /// made mutable via the addition of the `mut` keyword, namely + /// something like the occurrences of `x` in: + /// - `fn foo(x: Type) { ... }`, + /// - `let x = ...`, + /// - or `match ... { C(x) => ... }` + pub fn can_be_made_mutable(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + })))) => true, + + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( + ImplicitSelfKind::Imm, + )))) => true, + + _ => false, + } + } + + /// Returns `true` if local is definitely not a `ref ident` or + /// `ref mut ident` binding. (Such bindings cannot be made into + /// mutable bindings, but the inverse does not necessarily hold). + pub fn is_nonref_binding(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + })))) => true, + + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_)))) => true, + + _ => false, + } + } + + /// Returns `true` if this variable is a named variable or function + /// parameter declared by the user. + #[inline] + pub fn is_user_variable(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(_)) => true, + _ => false, + } + } + + /// Returns `true` if this is a reference to a variable bound in a `match` + /// expression that is used to access said variable for the guard of the + /// match arm. + pub fn is_ref_for_guard(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) => true, + _ => false, + } + } + + /// Returns `Some` if this is a reference to a static item that is used to + /// access that static + pub fn is_ref_to_static(&self) -> bool { + match self.local_info { + Some(box LocalInfo::StaticRef { .. }) => true, + _ => false, + } + } + + /// Returns `Some` if this is a reference to a static item that is used to + /// access that static + pub fn is_ref_to_thread_local(&self) -> bool { + match self.local_info { + Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local, + _ => false, + } + } + + /// Returns `true` is the local is from a compiler desugaring, e.g., + /// `__next` from a `for` loop. + #[inline] + pub fn from_compiler_desugaring(&self) -> bool { + self.source_info.span.desugaring_kind().is_some() + } + + /// Creates a new `LocalDecl` for a temporary: mutable, non-internal. + #[inline] + pub fn new(ty: Ty<'tcx>, span: Span) -> Self { + Self::with_source_info(ty, SourceInfo::outermost(span)) + } + + /// Like `LocalDecl::new`, but takes a `SourceInfo` instead of a `Span`. + #[inline] + pub fn with_source_info(ty: Ty<'tcx>, source_info: SourceInfo) -> Self { + LocalDecl { + mutability: Mutability::Mut, + local_info: None, + internal: false, + is_block_tail: None, + ty, + user_ty: None, + source_info, + } + } + + /// Converts `self` into same `LocalDecl` except tagged as internal. + #[inline] + pub fn internal(mut self) -> Self { + self.internal = true; + self + } + + /// Converts `self` into same `LocalDecl` except tagged as immutable. + #[inline] + pub fn immutable(mut self) -> Self { + self.mutability = Mutability::Not; + self + } + + /// Converts `self` into same `LocalDecl` except tagged as internal temporary. + #[inline] + pub fn block_tail(mut self, info: BlockTailInfo) -> Self { + assert!(self.is_block_tail.is_none()); + self.is_block_tail = Some(info); + self + } +} + +/// Debug information pertaining to a user variable. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VarDebugInfo<'tcx> { + pub name: Symbol, + + /// Source info of the user variable, including the scope + /// within which the variable is visible (to debuginfo) + /// (see `LocalDecl`'s `source_info` field for more details). + pub source_info: SourceInfo, + + /// Where the data for this user variable is to be found. + /// NOTE(eddyb) There's an unenforced invariant that this `Place` is + /// based on a `Local`, not a `Static`, and contains no indexing. + pub place: Place<'tcx>, +} + +/////////////////////////////////////////////////////////////////////////// +// BasicBlock + +rustc_index::newtype_index! { + pub struct BasicBlock { + derive [HashStable] + DEBUG_FORMAT = "bb{}", + const START_BLOCK = 0, + } +} + +impl BasicBlock { + pub fn start_location(self) -> Location { + Location { block: self, statement_index: 0 } + } +} + +/////////////////////////////////////////////////////////////////////////// +// BasicBlockData and Terminator + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct BasicBlockData<'tcx> { + /// List of statements in this block. + pub statements: Vec>, + + /// Terminator for this block. + /// + /// N.B., this should generally ONLY be `None` during construction. + /// Therefore, you should generally access it via the + /// `terminator()` or `terminator_mut()` methods. The only + /// exception is that certain passes, such as `simplify_cfg`, swap + /// out the terminator temporarily with `None` while they continue + /// to recurse over the set of basic blocks. + pub terminator: Option>, + + /// If true, this block lies on an unwind path. This is used + /// during codegen where distinct kinds of basic blocks may be + /// generated (particularly for MSVC cleanup). Unwind blocks must + /// only branch to other unwind blocks. + pub is_cleanup: bool, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct Terminator<'tcx> { + pub source_info: SourceInfo, + pub kind: TerminatorKind<'tcx>, +} + +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] +pub enum TerminatorKind<'tcx> { + /// Block should have one successor in the graph; we jump there. + Goto { target: BasicBlock }, + + /// Operand evaluates to an integer; jump depending on its value + /// to one of the targets, and otherwise fallback to `otherwise`. + SwitchInt { + /// The discriminant value being tested. + discr: Operand<'tcx>, + + /// The type of value being tested. + switch_ty: Ty<'tcx>, + + /// Possible values. The locations to branch to in each case + /// are found in the corresponding indices from the `targets` vector. + values: Cow<'tcx, [u128]>, + + /// Possible branch sites. The last element of this vector is used + /// for the otherwise branch, so targets.len() == values.len() + 1 + /// should hold. + // + // This invariant is quite non-obvious and also could be improved. + // One way to make this invariant is to have something like this instead: + // + // branches: Vec<(ConstInt, BasicBlock)>, + // otherwise: Option // exhaustive if None + // + // However we’ve decided to keep this as-is until we figure a case + // where some other approach seems to be strictly better than other. + targets: Vec, + }, + + /// Indicates that the landing pad is finished and unwinding should + /// continue. Emitted by `build::scope::diverge_cleanup`. + Resume, + + /// Indicates that the landing pad is finished and that the process + /// should abort. Used to prevent unwinding for foreign items. + Abort, + + /// Indicates a normal return. The return place should have + /// been filled in by now. This should occur at most once. + Return, + + /// Indicates a terminator that can never be reached. + Unreachable, + + /// Drop the `Place`. + Drop { location: Place<'tcx>, target: BasicBlock, unwind: Option }, + + /// Drop the `Place` and assign the new value over it. This ensures + /// that the assignment to `P` occurs *even if* the destructor for + /// place unwinds. Its semantics are best explained by the + /// elaboration: + /// + /// ``` + /// BB0 { + /// DropAndReplace(P <- V, goto BB1, unwind BB2) + /// } + /// ``` + /// + /// becomes + /// + /// ``` + /// BB0 { + /// Drop(P, goto BB1, unwind BB2) + /// } + /// BB1 { + /// // P is now uninitialized + /// P <- V + /// } + /// BB2 { + /// // P is now uninitialized -- its dtor panicked + /// P <- V + /// } + /// ``` + DropAndReplace { + location: Place<'tcx>, + value: Operand<'tcx>, + target: BasicBlock, + unwind: Option, + }, + + /// Block ends with a call of a converging function. + Call { + /// The function that’s being called. + func: Operand<'tcx>, + /// Arguments the function is called with. + /// These are owned by the callee, which is free to modify them. + /// This allows the memory occupied by "by-value" arguments to be + /// reused across function calls without duplicating the contents. + args: Vec>, + /// Destination for the return value. If some, the call is converging. + destination: Option<(Place<'tcx>, BasicBlock)>, + /// Cleanups to be done if the call unwinds. + cleanup: Option, + /// `true` if this is from a call in HIR rather than from an overloaded + /// operator. True for overloaded function call. + from_hir_call: bool, + }, + + /// Jump to the target if the condition has the expected value, + /// otherwise panic with a message and a cleanup target. + Assert { + cond: Operand<'tcx>, + expected: bool, + msg: AssertMessage<'tcx>, + target: BasicBlock, + cleanup: Option, + }, + + /// A suspend point. + Yield { + /// The value to return. + value: Operand<'tcx>, + /// Where to resume to. + resume: BasicBlock, + /// The place to store the resume argument in. + resume_arg: Place<'tcx>, + /// Cleanup to be done if the generator is dropped at this suspend point. + drop: Option, + }, + + /// Indicates the end of the dropping of a generator. + GeneratorDrop, + + /// A block where control flow only ever takes one real path, but borrowck + /// needs to be more conservative. + FalseEdges { + /// The target normal control flow will take. + real_target: BasicBlock, + /// A block control flow could conceptually jump to, but won't in + /// practice. + imaginary_target: BasicBlock, + }, + /// A terminator for blocks that only take one path in reality, but where we + /// reserve the right to unwind in borrowck, even if it won't happen in practice. + /// This can arise in infinite loops with no function calls for example. + FalseUnwind { + /// The target normal control flow will take. + real_target: BasicBlock, + /// The imaginary cleanup block link. This particular path will never be taken + /// in practice, but in order to avoid fragility we want to always + /// consider it in borrowck. We don't want to accept programs which + /// pass borrowck only when `panic=abort` or some assertions are disabled + /// due to release vs. debug mode builds. This needs to be an `Option` because + /// of the `remove_noop_landing_pads` and `no_landing_pads` passes. + unwind: Option, + }, + + /// Block ends with an inline assembly block. This is a terminator since + /// inline assembly is allowed to diverge. + InlineAsm { + /// The template for the inline assembly, with placeholders. + template: &'tcx [InlineAsmTemplatePiece], + + /// The operands for the inline assembly, as `Operand`s or `Place`s. + operands: Vec>, + + /// Miscellaneous options for the inline assembly. + options: InlineAsmOptions, + + /// Destination block after the inline assembly returns, unless it is + /// diverging (InlineAsmOptions::NORETURN). + destination: Option, + }, +} + +/// Information about an assertion failure. +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] +pub enum AssertKind { + BoundsCheck { len: O, index: O }, + Overflow(BinOp), + OverflowNeg, + DivisionByZero, + RemainderByZero, + ResumedAfterReturn(GeneratorKind), + ResumedAfterPanic(GeneratorKind), +} + +#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub enum InlineAsmOperand<'tcx> { + In { + reg: InlineAsmRegOrRegClass, + value: Operand<'tcx>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + place: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_value: Operand<'tcx>, + out_place: Option>, + }, + Const { + value: Operand<'tcx>, + }, + SymFn { + value: Box>, + }, + SymStatic { + value: Box>, + }, +} + +/// Type for MIR `Assert` terminator error messages. +pub type AssertMessage<'tcx> = AssertKind>; + +pub type Successors<'a> = + iter::Chain, slice::Iter<'a, BasicBlock>>; +pub type SuccessorsMut<'a> = + iter::Chain, slice::IterMut<'a, BasicBlock>>; + +impl<'tcx> Terminator<'tcx> { + pub fn successors(&self) -> Successors<'_> { + self.kind.successors() + } + + pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + self.kind.successors_mut() + } + + pub fn unwind(&self) -> Option<&Option> { + self.kind.unwind() + } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + self.kind.unwind_mut() + } +} + +impl<'tcx> TerminatorKind<'tcx> { + pub fn if_( + tcx: TyCtxt<'tcx>, + cond: Operand<'tcx>, + t: BasicBlock, + f: BasicBlock, + ) -> TerminatorKind<'tcx> { + static BOOL_SWITCH_FALSE: &[u128] = &[0]; + TerminatorKind::SwitchInt { + discr: cond, + switch_ty: tcx.types.bool, + values: From::from(BOOL_SWITCH_FALSE), + targets: vec![f, t], + } + } + + pub fn successors(&self) -> Successors<'_> { + use self::TerminatorKind::*; + match *self { + Resume + | Abort + | GeneratorDrop + | Return + | Unreachable + | Call { destination: None, cleanup: None, .. } + | InlineAsm { destination: None, .. } => None.into_iter().chain(&[]), + Goto { target: ref t } + | Call { destination: None, cleanup: Some(ref t), .. } + | Call { destination: Some((_, ref t)), cleanup: None, .. } + | Yield { resume: ref t, drop: None, .. } + | DropAndReplace { target: ref t, unwind: None, .. } + | Drop { target: ref t, unwind: None, .. } + | Assert { target: ref t, cleanup: None, .. } + | FalseUnwind { real_target: ref t, unwind: None } + | InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]), + Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. } + | Yield { resume: ref t, drop: Some(ref u), .. } + | DropAndReplace { target: ref t, unwind: Some(ref u), .. } + | Drop { target: ref t, unwind: Some(ref u), .. } + | Assert { target: ref t, cleanup: Some(ref u), .. } + | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => { + Some(t).into_iter().chain(slice::from_ref(u)) + } + SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]), + FalseEdges { ref real_target, ref imaginary_target } => { + Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)) + } + } + } + + pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + use self::TerminatorKind::*; + match *self { + Resume + | Abort + | GeneratorDrop + | Return + | Unreachable + | Call { destination: None, cleanup: None, .. } + | InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []), + Goto { target: ref mut t } + | Call { destination: None, cleanup: Some(ref mut t), .. } + | Call { destination: Some((_, ref mut t)), cleanup: None, .. } + | Yield { resume: ref mut t, drop: None, .. } + | DropAndReplace { target: ref mut t, unwind: None, .. } + | Drop { target: ref mut t, unwind: None, .. } + | Assert { target: ref mut t, cleanup: None, .. } + | FalseUnwind { real_target: ref mut t, unwind: None } + | InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []), + Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. } + | Yield { resume: ref mut t, drop: Some(ref mut u), .. } + | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. } + | Drop { target: ref mut t, unwind: Some(ref mut u), .. } + | Assert { target: ref mut t, cleanup: Some(ref mut u), .. } + | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => { + Some(t).into_iter().chain(slice::from_mut(u)) + } + SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]), + FalseEdges { ref mut real_target, ref mut imaginary_target } => { + Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) + } + } + } + + pub fn unwind(&self) -> Option<&Option> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::Yield { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::InlineAsm { .. } => None, + TerminatorKind::Call { cleanup: ref unwind, .. } + | TerminatorKind::Assert { cleanup: ref unwind, .. } + | TerminatorKind::DropAndReplace { ref unwind, .. } + | TerminatorKind::Drop { ref unwind, .. } + | TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind), + } + } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::Yield { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::InlineAsm { .. } => None, + TerminatorKind::Call { cleanup: ref mut unwind, .. } + | TerminatorKind::Assert { cleanup: ref mut unwind, .. } + | TerminatorKind::DropAndReplace { ref mut unwind, .. } + | TerminatorKind::Drop { ref mut unwind, .. } + | TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind), + } + } +} + +impl<'tcx> BasicBlockData<'tcx> { + pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { + BasicBlockData { statements: vec![], terminator, is_cleanup: false } + } + + /// Accessor for terminator. + /// + /// Terminator may not be None after construction of the basic block is complete. This accessor + /// provides a convenience way to reach the terminator. + pub fn terminator(&self) -> &Terminator<'tcx> { + self.terminator.as_ref().expect("invalid terminator state") + } + + pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> { + self.terminator.as_mut().expect("invalid terminator state") + } + + pub fn retain_statements(&mut self, mut f: F) + where + F: FnMut(&mut Statement<'_>) -> bool, + { + for s in &mut self.statements { + if !f(s) { + s.make_nop(); + } + } + } + + pub fn expand_statements(&mut self, mut f: F) + where + F: FnMut(&mut Statement<'tcx>) -> Option, + I: iter::TrustedLen>, + { + // Gather all the iterators we'll need to splice in, and their positions. + let mut splices: Vec<(usize, I)> = vec![]; + let mut extra_stmts = 0; + for (i, s) in self.statements.iter_mut().enumerate() { + if let Some(mut new_stmts) = f(s) { + if let Some(first) = new_stmts.next() { + // We can already store the first new statement. + *s = first; + + // Save the other statements for optimized splicing. + let remaining = new_stmts.size_hint().0; + if remaining > 0 { + splices.push((i + 1 + extra_stmts, new_stmts)); + extra_stmts += remaining; + } + } else { + s.make_nop(); + } + } + } + + // Splice in the new statements, from the end of the block. + // FIXME(eddyb) This could be more efficient with a "gap buffer" + // where a range of elements ("gap") is left uninitialized, with + // splicing adding new elements to the end of that gap and moving + // existing elements from before the gap to the end of the gap. + // For now, this is safe code, emulating a gap but initializing it. + let mut gap = self.statements.len()..self.statements.len() + extra_stmts; + self.statements.resize( + gap.end, + Statement { source_info: SourceInfo::outermost(DUMMY_SP), kind: StatementKind::Nop }, + ); + for (splice_start, new_stmts) in splices.into_iter().rev() { + let splice_end = splice_start + new_stmts.size_hint().0; + while gap.end > splice_end { + gap.start -= 1; + gap.end -= 1; + self.statements.swap(gap.start, gap.end); + } + self.statements.splice(splice_start..splice_end, new_stmts); + gap.end = splice_start; + } + } + + pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { + if index < self.statements.len() { &self.statements[index] } else { &self.terminator } + } +} + +impl AssertKind { + /// Getting a description does not require `O` to be printable, and does not + /// require allocation. + /// The caller is expected to handle `BoundsCheck` separately. + pub fn description(&self) -> &'static str { + use AssertKind::*; + match self { + Overflow(BinOp::Add) => "attempt to add with overflow", + Overflow(BinOp::Sub) => "attempt to subtract with overflow", + Overflow(BinOp::Mul) => "attempt to multiply with overflow", + Overflow(BinOp::Div) => "attempt to divide with overflow", + Overflow(BinOp::Rem) => "attempt to calculate the remainder with overflow", + OverflowNeg => "attempt to negate with overflow", + Overflow(BinOp::Shr) => "attempt to shift right with overflow", + Overflow(BinOp::Shl) => "attempt to shift left with overflow", + Overflow(op) => bug!("{:?} cannot overflow", op), + DivisionByZero => "attempt to divide by zero", + RemainderByZero => "attempt to calculate the remainder with a divisor of zero", + ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion", + ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion", + ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking", + ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking", + BoundsCheck { .. } => bug!("Unexpected AssertKind"), + } + } + + /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing. + fn fmt_assert_args(&self, f: &mut W) -> fmt::Result + where + O: Debug, + { + match self { + AssertKind::BoundsCheck { ref len, ref index } => write!( + f, + "\"index out of bounds: the len is {{}} but the index is {{}}\", {:?}, {:?}", + len, index + ), + _ => write!(f, "\"{}\"", self.description()), + } + } +} + +impl fmt::Debug for AssertKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use AssertKind::*; + match self { + BoundsCheck { ref len, ref index } => { + write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index) + } + _ => write!(f, "{}", self.description()), + } + } +} + +impl<'tcx> Debug for TerminatorKind<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + self.fmt_head(fmt)?; + let successor_count = self.successors().count(); + let labels = self.fmt_successor_labels(); + assert_eq!(successor_count, labels.len()); + + match successor_count { + 0 => Ok(()), + + 1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()), + + _ => { + write!(fmt, " -> [")?; + for (i, target) in self.successors().enumerate() { + if i > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{}: {:?}", labels[i], target)?; + } + write!(fmt, "]") + } + } + } +} + +impl<'tcx> TerminatorKind<'tcx> { + /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the + /// successor basic block, if any. The only information not included is the list of possible + /// successors, which may be rendered differently between the text and the graphviz format. + pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { + use self::TerminatorKind::*; + match self { + Goto { .. } => write!(fmt, "goto"), + SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr), + Return => write!(fmt, "return"), + GeneratorDrop => write!(fmt, "generator_drop"), + Resume => write!(fmt, "resume"), + Abort => write!(fmt, "abort"), + Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value), + Unreachable => write!(fmt, "unreachable"), + Drop { location, .. } => write!(fmt, "drop({:?})", location), + DropAndReplace { location, value, .. } => { + write!(fmt, "replace({:?} <- {:?})", location, value) + } + Call { func, args, destination, .. } => { + if let Some((destination, _)) = destination { + write!(fmt, "{:?} = ", destination)?; + } + write!(fmt, "{:?}(", func)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{:?}", arg)?; + } + write!(fmt, ")") + } + Assert { cond, expected, msg, .. } => { + write!(fmt, "assert(")?; + if !expected { + write!(fmt, "!")?; + } + write!(fmt, "{:?}, ", cond)?; + msg.fmt_assert_args(fmt)?; + write!(fmt, ")") + } + FalseEdges { .. } => write!(fmt, "falseEdges"), + FalseUnwind { .. } => write!(fmt, "falseUnwind"), + InlineAsm { template, ref operands, options, destination: _ } => { + write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?; + for op in operands { + write!(fmt, ", ")?; + let print_late = |&late| if late { "late" } else { "" }; + match op { + InlineAsmOperand::In { reg, value } => { + write!(fmt, "in({}) {:?}", reg, value)?; + } + InlineAsmOperand::Out { reg, late, place: Some(place) } => { + write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?; + } + InlineAsmOperand::Out { reg, late, place: None } => { + write!(fmt, "{}out({}) _", print_late(late), reg)?; + } + InlineAsmOperand::InOut { + reg, + late, + in_value, + out_place: Some(out_place), + } => { + write!( + fmt, + "in{}out({}) {:?} => {:?}", + print_late(late), + reg, + in_value, + out_place + )?; + } + InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => { + write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?; + } + InlineAsmOperand::Const { value } => { + write!(fmt, "const {:?}", value)?; + } + InlineAsmOperand::SymFn { value } + | InlineAsmOperand::SymStatic { value } => { + write!(fmt, "sym {:?}", value)?; + } + } + } + write!(fmt, ", options({:?}))", options) + } + } + } + + /// Returns the list of labels for the edges to the successor basic blocks. + pub fn fmt_successor_labels(&self) -> Vec> { + use self::TerminatorKind::*; + match *self { + Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], + Goto { .. } => vec!["".into()], + SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| { + let param_env = ty::ParamEnv::empty(); + let switch_ty = tcx.lift(&switch_ty).unwrap(); + let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; + values + .iter() + .map(|&u| { + ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) + .to_string() + .into() + }) + .chain(iter::once("otherwise".into())) + .collect() + }), + Call { destination: Some(_), cleanup: Some(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()], + Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()], + Call { destination: None, cleanup: None, .. } => vec![], + Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()], + Yield { drop: None, .. } => vec!["resume".into()], + DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => { + vec!["return".into()] + } + DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Assert { cleanup: None, .. } => vec!["".into()], + Assert { .. } => vec!["success".into(), "unwind".into()], + FalseEdges { .. } => vec!["real".into(), "imaginary".into()], + FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()], + FalseUnwind { unwind: None, .. } => vec!["real".into()], + InlineAsm { destination: Some(_), .. } => vec!["".into()], + InlineAsm { destination: None, .. } => vec![], + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Statements + +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct Statement<'tcx> { + pub source_info: SourceInfo, + pub kind: StatementKind<'tcx>, +} + +// `Statement` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(Statement<'_>, 32); + +impl Statement<'_> { + /// Changes a statement to a nop. This is both faster than deleting instructions and avoids + /// invalidating statement indices in `Location`s. + pub fn make_nop(&mut self) { + self.kind = StatementKind::Nop + } + + /// Changes a statement to a nop and returns the original statement. + pub fn replace_nop(&mut self) -> Self { + Statement { + source_info: self.source_info, + kind: mem::replace(&mut self.kind, StatementKind::Nop), + } + } +} + +#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub enum StatementKind<'tcx> { + /// Write the RHS Rvalue to the LHS Place. + Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), + + /// This represents all the reading that a pattern match may do + /// (e.g., inspecting constants and discriminant values), and the + /// kind of pattern it comes from. This is in order to adapt potential + /// error messages to these specific patterns. + /// + /// Note that this also is emitted for regular `let` bindings to ensure that locals that are + /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;` + FakeRead(FakeReadCause, Box>), + + /// Write the discriminant for a variant to the enum Place. + SetDiscriminant { place: Box>, variant_index: VariantIdx }, + + /// Start a live range for the storage of the local. + StorageLive(Local), + + /// End the current live range for the storage of the local. + StorageDead(Local), + + /// Executes a piece of inline Assembly. Stored in a Box to keep the size + /// of `StatementKind` low. + LlvmInlineAsm(Box>), + + /// Retag references in the given place, ensuring they got fresh tags. This is + /// part of the Stacked Borrows model. These statements are currently only interpreted + /// by miri and only generated when "-Z mir-emit-retag" is passed. + /// See + /// for more details. + Retag(RetagKind, Box>), + + /// Encodes a user's type ascription. These need to be preserved + /// intact so that NLL can respect them. For example: + /// + /// let a: T = y; + /// + /// The effect of this annotation is to relate the type `T_y` of the place `y` + /// to the user-given type `T`. The effect depends on the specified variance: + /// + /// - `Covariant` -- requires that `T_y <: T` + /// - `Contravariant` -- requires that `T_y :> T` + /// - `Invariant` -- requires that `T_y == T` + /// - `Bivariant` -- no effect + AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), + + /// No-op. Useful for deleting instructions without affecting statement indices. + Nop, +} + +/// Describes what kind of retag is to be performed. +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, HashStable)] +pub enum RetagKind { + /// The initial retag when entering a function. + FnEntry, + /// Retag preparing for a two-phase borrow. + TwoPhase, + /// Retagging raw pointers. + Raw, + /// A "normal" retag. + Default, +} + +/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable, PartialEq)] +pub enum FakeReadCause { + /// Inject a fake read of the borrowed input at the end of each guards + /// code. + /// + /// This should ensure that you cannot change the variant for an enum while + /// you are in the midst of matching on it. + ForMatchGuard, + + /// `let x: !; match x {}` doesn't generate any read of x so we need to + /// generate a read of x to check that it is initialized and safe. + ForMatchedPlace, + + /// A fake read of the RefWithinGuard version of a bind-by-value variable + /// in a match guard to ensure that it's value hasn't change by the time + /// we create the OutsideGuard version. + ForGuardBinding, + + /// Officially, the semantics of + /// + /// `let pattern = ;` + /// + /// is that `` is evaluated into a temporary and then this temporary is + /// into the pattern. + /// + /// However, if we see the simple pattern `let var = `, we optimize this to + /// evaluate `` directly into the variable `var`. This is mostly unobservable, + /// but in some cases it can affect the borrow checker, as in #53695. + /// Therefore, we insert a "fake read" here to ensure that we get + /// appropriate errors. + ForLet, + + /// If we have an index expression like + /// + /// (*x)[1][{ x = y; 4}] + /// + /// then the first bounds check is invalidated when we evaluate the second + /// index expression. Thus we create a fake borrow of `x` across the second + /// indexer, which will cause a borrow check error. + ForIndex, +} + +#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct LlvmInlineAsm<'tcx> { + pub asm: hir::LlvmInlineAsmInner, + pub outputs: Box<[Place<'tcx>]>, + pub inputs: Box<[(Span, Operand<'tcx>)]>, +} + +impl Debug for Statement<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::StatementKind::*; + match self.kind { + Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv), + FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place), + Retag(ref kind, ref place) => write!( + fmt, + "Retag({}{:?})", + match kind { + RetagKind::FnEntry => "[fn entry] ", + RetagKind::TwoPhase => "[2phase] ", + RetagKind::Raw => "[raw] ", + RetagKind::Default => "", + }, + place, + ), + StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place), + StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place), + SetDiscriminant { ref place, variant_index } => { + write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) + } + LlvmInlineAsm(ref asm) => { + write!(fmt, "llvm_asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs) + } + AscribeUserType(box (ref place, ref c_ty), ref variance) => { + write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) + } + Nop => write!(fmt, "nop"), + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Places + +/// A path to a value; something that can be evaluated without +/// changing or disturbing program state. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, HashStable)] +pub struct Place<'tcx> { + pub local: Local, + + /// projection out of a place (access a field, deref a pointer, etc) + pub projection: &'tcx List>, +} + +impl<'tcx> rustc_serialize::UseSpecializedDecodable for Place<'tcx> {} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(RustcEncodable, RustcDecodable, HashStable)] +pub enum ProjectionElem { + Deref, + Field(Field, T), + Index(V), + + /// These indices are generated by slice patterns. Easiest to explain + /// by example: + /// + /// ``` + /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, + /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, + /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, + /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, + /// ``` + ConstantIndex { + /// index or -index (in Python terms), depending on from_end + offset: u32, + /// The thing being indexed must be at least this long. For arrays this + /// is always the exact length. + min_length: u32, + /// Counting backwards from end? This is always false when indexing an + /// array. + from_end: bool, + }, + + /// These indices are generated by slice patterns. + /// + /// If `from_end` is true `slice[from..slice.len() - to]`. + /// Otherwise `array[from..to]`. + Subslice { + from: u32, + to: u32, + /// Whether `to` counts from the start or end of the array/slice. + /// For `PlaceElem`s this is `true` if and only if the base is a slice. + /// For `ProjectionKind`, this can also be `true` for arrays. + from_end: bool, + }, + + /// "Downcast" to a variant of an ADT. Currently, we only introduce + /// this for ADTs with more than one variant. It may be better to + /// just introduce it always, or always for enums. + /// + /// The included Symbol is the name of the variant, used for printing MIR. + Downcast(Option, VariantIdx), +} + +impl ProjectionElem { + /// Returns `true` if the target of this projection may refer to a different region of memory + /// than the base. + fn is_indirect(&self) -> bool { + match self { + Self::Deref => true, + + Self::Field(_, _) + | Self::Index(_) + | Self::ConstantIndex { .. } + | Self::Subslice { .. } + | Self::Downcast(_, _) => false, + } + } +} + +/// Alias for projections as they appear in places, where the base is a place +/// and the index is a local. +pub type PlaceElem<'tcx> = ProjectionElem>; + +impl<'tcx> Copy for PlaceElem<'tcx> {} + +// At least on 64 bit systems, `PlaceElem` should not be larger than two pointers. +#[cfg(target_arch = "x86_64")] +static_assert_size!(PlaceElem<'_>, 16); + +/// Alias for projections as they appear in `UserTypeProjection`, where we +/// need neither the `V` parameter for `Index` nor the `T` for `Field`. +pub type ProjectionKind = ProjectionElem<(), ()>; + +rustc_index::newtype_index! { + pub struct Field { + derive [HashStable] + DEBUG_FORMAT = "field[{}]" + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PlaceRef<'tcx> { + pub local: Local, + pub projection: &'tcx [PlaceElem<'tcx>], +} + +impl<'tcx> Place<'tcx> { + // FIXME change this to a const fn by also making List::empty a const fn. + pub fn return_place() -> Place<'tcx> { + Place { local: RETURN_PLACE, projection: List::empty() } + } + + /// Returns `true` if this `Place` contains a `Deref` projection. + /// + /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the + /// same region of memory as its base. + pub fn is_indirect(&self) -> bool { + self.projection.iter().any(|elem| elem.is_indirect()) + } + + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or + /// a single deref of a local. + // + // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + pub fn local_or_deref_local(&self) -> Option { + match self.as_ref() { + PlaceRef { local, projection: [] } + | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + _ => None, + } + } + + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + pub fn as_local(&self) -> Option { + self.as_ref().as_local() + } + + pub fn as_ref(&self) -> PlaceRef<'tcx> { + PlaceRef { local: self.local, projection: &self.projection } + } +} + +impl From for Place<'_> { + fn from(local: Local) -> Self { + Place { local, projection: List::empty() } + } +} + +impl<'tcx> PlaceRef<'tcx> { + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or + /// a single deref of a local. + // + // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + pub fn local_or_deref_local(&self) -> Option { + match *self { + PlaceRef { local, projection: [] } + | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + _ => None, + } + } + + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + pub fn as_local(&self) -> Option { + match *self { + PlaceRef { local, projection: [] } => Some(local), + _ => None, + } + } +} + +impl Debug for Place<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + for elem in self.projection.iter().rev() { + match elem { + ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { + write!(fmt, "(").unwrap(); + } + ProjectionElem::Deref => { + write!(fmt, "(*").unwrap(); + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => {} + } + } + + write!(fmt, "{:?}", self.local)?; + + for elem in self.projection.iter() { + match elem { + ProjectionElem::Downcast(Some(name), _index) => { + write!(fmt, " as {})", name)?; + } + ProjectionElem::Downcast(None, index) => { + write!(fmt, " as variant#{:?})", index)?; + } + ProjectionElem::Deref => { + write!(fmt, ")")?; + } + ProjectionElem::Field(field, ty) => { + write!(fmt, ".{:?}: {:?})", field.index(), ty)?; + } + ProjectionElem::Index(ref index) => { + write!(fmt, "[{:?}]", index)?; + } + ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => { + write!(fmt, "[{:?} of {:?}]", offset, min_length)?; + } + ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => { + write!(fmt, "[-{:?} of {:?}]", offset, min_length)?; + } + ProjectionElem::Subslice { from, to, from_end: true } if *to == 0 => { + write!(fmt, "[{:?}:]", from)?; + } + ProjectionElem::Subslice { from, to, from_end: true } if *from == 0 => { + write!(fmt, "[:-{:?}]", to)?; + } + ProjectionElem::Subslice { from, to, from_end: true } => { + write!(fmt, "[{:?}:-{:?}]", from, to)?; + } + ProjectionElem::Subslice { from, to, from_end: false } => { + write!(fmt, "[{:?}..{:?}]", from, to)?; + } + } + } + + Ok(()) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Scopes + +rustc_index::newtype_index! { + pub struct SourceScope { + derive [HashStable] + DEBUG_FORMAT = "scope[{}]", + const OUTERMOST_SOURCE_SCOPE = 0, + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct SourceScopeData { + pub span: Span, + pub parent_scope: Option, + + /// Crate-local information for this source scope, that can't (and + /// needn't) be tracked across crates. + pub local_data: ClearCrossCrate, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct SourceScopeLocalData { + /// An `HirId` with lint levels equivalent to this scope's lint levels. + pub lint_root: hir::HirId, + /// The unsafe block that contains this node. + pub safety: Safety, +} + +/////////////////////////////////////////////////////////////////////////// +// Operands + +/// These are values that can appear inside an rvalue. They are intentionally +/// limited to prevent rvalues from being nested in one another. +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub enum Operand<'tcx> { + /// Copy: The value must be available for use afterwards. + /// + /// This implies that the type of the place must be `Copy`; this is true + /// by construction during build, but also checked by the MIR type checker. + Copy(Place<'tcx>), + + /// Move: The value (including old borrows of it) will not be used again. + /// + /// Safe for values of all types (modulo future developments towards `?Move`). + /// Correct usage patterns are enforced by the borrow checker for safe code. + /// `Copy` may be converted to `Move` to enable "last-use" optimizations. + Move(Place<'tcx>), + + /// Synthesizes a constant value. + Constant(Box>), +} + +impl<'tcx> Debug for Operand<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::Operand::*; + match *self { + Constant(ref a) => write!(fmt, "{:?}", a), + Copy(ref place) => write!(fmt, "{:?}", place), + Move(ref place) => write!(fmt, "move {:?}", place), + } + } +} + +impl<'tcx> Operand<'tcx> { + /// Convenience helper to make a constant that refers to the fn + /// with given `DefId` and substs. Since this is used to synthesize + /// MIR, assumes `user_ty` is None. + pub fn function_handle( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + span: Span, + ) -> Self { + let ty = tcx.type_of(def_id).subst(tcx, substs); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::zero_sized(tcx, ty), + }) + } + + pub fn to_copy(&self) -> Self { + match *self { + Operand::Copy(_) | Operand::Constant(_) => self.clone(), + Operand::Move(place) => Operand::Copy(place), + } + } + + /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a + /// constant. + pub fn place(&self) -> Option> { + match self { + Operand::Copy(place) | Operand::Move(place) => Some(*place), + Operand::Constant(_) => None, + } + } +} + +/////////////////////////////////////////////////////////////////////////// +/// Rvalues + +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] +pub enum Rvalue<'tcx> { + /// x (either a move or copy, depending on type of x) + Use(Operand<'tcx>), + + /// [x; 32] + Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>), + + /// &x or &mut x + Ref(Region<'tcx>, BorrowKind, Place<'tcx>), + + /// Create a raw pointer to the given place + /// Can be generated by raw address of expressions (`&raw const x`), + /// or when casting a reference to a raw pointer. + AddressOf(Mutability, Place<'tcx>), + + /// length of a `[X]` or `[X;n]` value + Len(Place<'tcx>), + + Cast(CastKind, Operand<'tcx>, Ty<'tcx>), + + BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + + NullaryOp(NullOp, Ty<'tcx>), + UnaryOp(UnOp, Operand<'tcx>), + + /// Read the discriminant of an ADT. + /// + /// Undefined (i.e., no effort is made to make it defined, but there’s no reason why it cannot + /// be defined to return, say, a 0) if ADT is not an enum. + Discriminant(Place<'tcx>), + + /// Creates an aggregate value, like a tuple or struct. This is + /// only needed because we want to distinguish `dest = Foo { x: + /// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case + /// that `Foo` has a destructor. These rvalues can be optimized + /// away after type-checking and before lowering. + Aggregate(Box>, Vec>), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum CastKind { + Misc, + Pointer(PointerCast), +} + +#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum AggregateKind<'tcx> { + /// The type is of the element + Array(Ty<'tcx>), + Tuple, + + /// The second field is the variant index. It's equal to 0 for struct + /// and union expressions. The fourth field is + /// active field number and is present only for union expressions + /// -- e.g., for a union expression `SomeUnion { c: .. }`, the + /// active field index would identity the field `c` + Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option, Option), + + Closure(DefId, SubstsRef<'tcx>), + Generator(DefId, SubstsRef<'tcx>, hir::Movability), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum BinOp { + /// The `+` operator (addition) + Add, + /// The `-` operator (subtraction) + Sub, + /// The `*` operator (multiplication) + Mul, + /// The `/` operator (division) + Div, + /// The `%` operator (modulus) + Rem, + /// The `^` operator (bitwise xor) + BitXor, + /// The `&` operator (bitwise and) + BitAnd, + /// The `|` operator (bitwise or) + BitOr, + /// The `<<` operator (shift left) + Shl, + /// The `>>` operator (shift right) + Shr, + /// The `==` operator (equality) + Eq, + /// The `<` operator (less than) + Lt, + /// The `<=` operator (less than or equal to) + Le, + /// The `!=` operator (not equal to) + Ne, + /// The `>=` operator (greater than or equal to) + Ge, + /// The `>` operator (greater than) + Gt, + /// The `ptr.offset` operator + Offset, +} + +impl BinOp { + pub fn is_checkable(self) -> bool { + use self::BinOp::*; + match self { + Add | Sub | Mul | Shl | Shr => true, + _ => false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum NullOp { + /// Returns the size of a value of that type + SizeOf, + /// Creates a new uninitialized box for a value of that type + Box, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum UnOp { + /// The `!` operator for logical inversion + Not, + /// The `-` operator for negation + Neg, +} + +impl<'tcx> Debug for Rvalue<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::Rvalue::*; + + match *self { + Use(ref place) => write!(fmt, "{:?}", place), + Repeat(ref a, ref b) => { + write!(fmt, "[{:?}; ", a)?; + pretty_print_const(b, fmt, false)?; + write!(fmt, "]") + } + Len(ref a) => write!(fmt, "Len({:?})", a), + Cast(ref kind, ref place, ref ty) => { + write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) + } + BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), + CheckedBinaryOp(ref op, ref a, ref b) => { + write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) + } + UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), + Discriminant(ref place) => write!(fmt, "discriminant({:?})", place), + NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t), + Ref(region, borrow_kind, ref place) => { + let kind_str = match borrow_kind { + BorrowKind::Shared => "", + BorrowKind::Shallow => "shallow ", + BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ", + }; + + // When printing regions, add trailing space if necessary. + let print_region = ty::tls::with(|tcx| { + tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions + }); + let region = if print_region { + let mut region = region.to_string(); + if !region.is_empty() { + region.push(' '); + } + region + } else { + // Do not even print 'static + String::new() + }; + write!(fmt, "&{}{}{:?}", region, kind_str, place) + } + + AddressOf(mutability, ref place) => { + let kind_str = match mutability { + Mutability::Mut => "mut", + Mutability::Not => "const", + }; + + write!(fmt, "&raw {} {:?}", kind_str, place) + } + + Aggregate(ref kind, ref places) => { + let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| { + let mut tuple_fmt = fmt.debug_tuple(name); + for place in places { + tuple_fmt.field(place); + } + tuple_fmt.finish() + }; + + match **kind { + AggregateKind::Array(_) => write!(fmt, "{:?}", places), + + AggregateKind::Tuple => { + if places.is_empty() { + write!(fmt, "()") + } else { + fmt_tuple(fmt, "") + } + } + + AggregateKind::Adt(adt_def, variant, substs, _user_ty, _) => { + let variant_def = &adt_def.variants[variant]; + + let name = ty::tls::with(|tcx| { + let mut name = String::new(); + let substs = tcx.lift(&substs).expect("could not lift for printing"); + FmtPrinter::new(tcx, &mut name, Namespace::ValueNS) + .print_def_path(variant_def.def_id, substs)?; + Ok(name) + })?; + + match variant_def.ctor_kind { + CtorKind::Const => fmt.write_str(&name), + CtorKind::Fn => fmt_tuple(fmt, &name), + CtorKind::Fictive => { + let mut struct_fmt = fmt.debug_struct(&name); + for (field, place) in variant_def.fields.iter().zip(places) { + struct_fmt.field(&field.ident.as_str(), place); + } + struct_fmt.finish() + } + } + } + + AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| { + if let Some(def_id) = def_id.as_local() { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let name = if tcx.sess.opts.debugging_opts.span_free_formats { + let substs = tcx.lift(&substs).unwrap(); + format!( + "[closure@{}]", + tcx.def_path_str_with_substs(def_id.to_def_id(), substs), + ) + } else { + format!("[closure@{:?}]", tcx.hir().span(hir_id)) + }; + let mut struct_fmt = fmt.debug_struct(&name); + + if let Some(upvars) = tcx.upvars(def_id) { + for (&var_id, place) in upvars.keys().zip(places) { + let var_name = tcx.hir().name(var_id); + struct_fmt.field(&var_name.as_str(), place); + } + } + + struct_fmt.finish() + } else { + write!(fmt, "[closure]") + } + }), + + AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| { + if let Some(def_id) = def_id.as_local() { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let name = format!("[generator@{:?}]", tcx.hir().span(hir_id)); + let mut struct_fmt = fmt.debug_struct(&name); + + if let Some(upvars) = tcx.upvars(def_id) { + for (&var_id, place) in upvars.keys().zip(places) { + let var_name = tcx.hir().name(var_id); + struct_fmt.field(&var_name.as_str(), place); + } + } + + struct_fmt.finish() + } else { + write!(fmt, "[generator]") + } + }), + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +/// Constants +/// +/// Two constants are equal if they are the same constant. Note that +/// this does not necessarily mean that they are "==" in Rust -- in +/// particular one must be wary of `NaN`! + +#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub struct Constant<'tcx> { + pub span: Span, + + /// Optional user-given type: for something like + /// `collect::>`, this would be present and would + /// indicate that `Vec<_>` was explicitly specified. + /// + /// Needed for NLL to impose user-given type constraints. + pub user_ty: Option, + + pub literal: &'tcx ty::Const<'tcx>, +} + +impl Constant<'tcx> { + pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { + match self.literal.val.try_to_scalar() { + Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) { + GlobalAlloc::Static(def_id) => Some(def_id), + _ => None, + }, + _ => None, + } + } +} + +/// A collection of projections into user types. +/// +/// They are projections because a binding can occur a part of a +/// parent pattern that has been ascribed a type. +/// +/// Its a collection because there can be multiple type ascriptions on +/// the path from the root of the pattern down to the binding itself. +/// +/// An example: +/// +/// ```rust +/// struct S<'a>((i32, &'a str), String); +/// let S((_, w): (i32, &'static str), _): S = ...; +/// // ------ ^^^^^^^^^^^^^^^^^^^ (1) +/// // --------------------------------- ^ (2) +/// ``` +/// +/// The highlights labelled `(1)` show the subpattern `(_, w)` being +/// ascribed the type `(i32, &'static str)`. +/// +/// The highlights labelled `(2)` show the whole pattern being +/// ascribed the type `S`. +/// +/// In this example, when we descend to `w`, we will have built up the +/// following two projected types: +/// +/// * base: `S`, projection: `(base.0).1` +/// * base: `(i32, &'static str)`, projection: `base.1` +/// +/// The first will lead to the constraint `w: &'1 str` (for some +/// inferred region `'1`). The second will lead to the constraint `w: +/// &'static str`. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct UserTypeProjections { + pub contents: Vec<(UserTypeProjection, Span)>, +} + +impl<'tcx> UserTypeProjections { + pub fn none() -> Self { + UserTypeProjections { contents: vec![] } + } + + pub fn is_empty(&self) -> bool { + self.contents.is_empty() + } + + pub fn from_projections(projs: impl Iterator) -> Self { + UserTypeProjections { contents: projs.collect() } + } + + pub fn projections_and_spans( + &self, + ) -> impl Iterator + ExactSizeIterator { + self.contents.iter() + } + + pub fn projections(&self) -> impl Iterator + ExactSizeIterator { + self.contents.iter().map(|&(ref user_type, _span)| user_type) + } + + pub fn push_projection(mut self, user_ty: &UserTypeProjection, span: Span) -> Self { + self.contents.push((user_ty.clone(), span)); + self + } + + fn map_projections( + mut self, + mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection, + ) -> Self { + self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect(); + self + } + + pub fn index(self) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.index()) + } + + pub fn subslice(self, from: u32, to: u32) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to)) + } + + pub fn deref(self) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.deref()) + } + + pub fn leaf(self, field: Field) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field)) + } + + pub fn variant(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx, field: Field) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field)) + } +} + +/// Encodes the effect of a user-supplied type annotation on the +/// subcomponents of a pattern. The effect is determined by applying the +/// given list of proejctions to some underlying base type. Often, +/// the projection element list `projs` is empty, in which case this +/// directly encodes a type in `base`. But in the case of complex patterns with +/// subpatterns and bindings, we want to apply only a *part* of the type to a variable, +/// in which case the `projs` vector is used. +/// +/// Examples: +/// +/// * `let x: T = ...` -- here, the `projs` vector is empty. +/// +/// * `let (x, _): T = ...` -- here, the `projs` vector would contain +/// `field[0]` (aka `.0`), indicating that the type of `s` is +/// determined by finding the type of the `.0` field from `T`. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, PartialEq)] +pub struct UserTypeProjection { + pub base: UserTypeAnnotationIndex, + pub projs: Vec, +} + +impl Copy for ProjectionKind {} + +impl UserTypeProjection { + pub(crate) fn index(mut self) -> Self { + self.projs.push(ProjectionElem::Index(())); + self + } + + pub(crate) fn subslice(mut self, from: u32, to: u32) -> Self { + self.projs.push(ProjectionElem::Subslice { from, to, from_end: true }); + self + } + + pub(crate) fn deref(mut self) -> Self { + self.projs.push(ProjectionElem::Deref); + self + } + + pub(crate) fn leaf(mut self, field: Field) -> Self { + self.projs.push(ProjectionElem::Field(field, ())); + self + } + + pub(crate) fn variant( + mut self, + adt_def: &AdtDef, + variant_index: VariantIdx, + field: Field, + ) -> Self { + self.projs.push(ProjectionElem::Downcast( + Some(adt_def.variants[variant_index].ident.name), + variant_index, + )); + self.projs.push(ProjectionElem::Field(field, ())); + self + } +} + +CloneTypeFoldableAndLiftImpls! { ProjectionKind, } + +impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::mir::ProjectionElem::*; + + let base = self.base.fold_with(folder); + let projs: Vec<_> = self + .projs + .iter() + .map(|&elem| match elem { + Deref => Deref, + Field(f, ()) => Field(f, ()), + Index(()) => Index(()), + Downcast(symbol, variantidx) => Downcast(symbol, variantidx), + ConstantIndex { offset, min_length, from_end } => { + ConstantIndex { offset, min_length, from_end } + } + Subslice { from, to, from_end } => Subslice { from, to, from_end }, + }) + .collect(); + + UserTypeProjection { base, projs } + } + + fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + self.base.visit_with(visitor) + // Note: there's nothing in `self.proj` to visit. + } +} + +rustc_index::newtype_index! { + pub struct Promoted { + derive [HashStable] + DEBUG_FORMAT = "promoted[{}]" + } +} + +impl<'tcx> Debug for Constant<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + write!(fmt, "{}", self) + } +} + +impl<'tcx> Display for Constant<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + write!(fmt, "const ")?; + pretty_print_const(self.literal, fmt, true) + } +} + +fn pretty_print_const( + c: &ty::Const<'tcx>, + fmt: &mut Formatter<'_>, + print_types: bool, +) -> fmt::Result { + use crate::ty::print::PrettyPrinter; + ty::tls::with(|tcx| { + let literal = tcx.lift(&c).unwrap(); + let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); + cx.print_alloc_ids = true; + cx.pretty_print_const(literal, print_types)?; + Ok(()) + }) +} + +impl<'tcx> graph::DirectedGraph for Body<'tcx> { + type Node = BasicBlock; +} + +impl<'tcx> graph::WithNumNodes for Body<'tcx> { + #[inline] + fn num_nodes(&self) -> usize { + self.basic_blocks.len() + } +} + +impl<'tcx> graph::WithStartNode for Body<'tcx> { + #[inline] + fn start_node(&self) -> Self::Node { + START_BLOCK + } +} + +impl<'tcx> graph::WithSuccessors for Body<'tcx> { + #[inline] + fn successors(&self, node: Self::Node) -> >::Iter { + self.basic_blocks[node].terminator().successors().cloned() + } +} + +impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { + type Item = BasicBlock; + type Iter = iter::Cloned>; +} + +impl graph::GraphPredecessors<'graph> for Body<'tcx> { + type Item = BasicBlock; + type Iter = smallvec::IntoIter<[BasicBlock; 4]>; +} + +impl graph::WithPredecessors for Body<'tcx> { + #[inline] + fn predecessors(&self, node: Self::Node) -> >::Iter { + self.predecessors()[node].clone().into_iter() + } +} + +/// `Location` represents the position of the start of the statement; or, if +/// `statement_index` equals the number of statements, then the start of the +/// terminator. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, HashStable)] +pub struct Location { + /// The block that the location is within. + pub block: BasicBlock, + + pub statement_index: usize, +} + +impl fmt::Debug for Location { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{:?}[{}]", self.block, self.statement_index) + } +} + +impl Location { + pub const START: Location = Location { block: START_BLOCK, statement_index: 0 }; + + /// Returns the location immediately after this one within the enclosing block. + /// + /// Note that if this location represents a terminator, then the + /// resulting location would be out of bounds and invalid. + pub fn successor_within_block(&self) -> Location { + Location { block: self.block, statement_index: self.statement_index + 1 } + } + + /// Returns `true` if `other` is earlier in the control flow graph than `self`. + pub fn is_predecessor_of<'tcx>(&self, other: Location, body: &Body<'tcx>) -> bool { + // If we are in the same block as the other location and are an earlier statement + // then we are a predecessor of `other`. + if self.block == other.block && self.statement_index < other.statement_index { + return true; + } + + let predecessors = body.predecessors(); + + // If we're in another block, then we want to check that block is a predecessor of `other`. + let mut queue: Vec = predecessors[other.block].to_vec(); + let mut visited = FxHashSet::default(); + + while let Some(block) = queue.pop() { + // If we haven't visited this block before, then make sure we visit it's predecessors. + if visited.insert(block) { + queue.extend(predecessors[block].iter().cloned()); + } else { + continue; + } + + // If we found the block that `self` is in, then we are a predecessor of `other` (since + // we found that block by looking at the predecessors of `other`). + if self.block == block { + return true; + } + } + + false + } + + pub fn dominates(&self, other: Location, dominators: &Dominators) -> bool { + if self.block == other.block { + self.statement_index <= other.statement_index + } else { + dominators.is_dominated_by(other.block, self.block) + } + } +} diff --git a/src/librustc/mir/mono.rs b/src/librustc_middle/mir/mono.rs similarity index 94% rename from src/librustc/mir/mono.rs rename to src/librustc_middle/mir/mono.rs index 27d82d25803c7..c889dbc0a4498 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -1,12 +1,13 @@ use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId}; -use crate::ich::{Fingerprint, NodeIdHashingMode, StableHashingContext}; +use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::ty::print::obsolete::DefPathBasedNames; use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt}; use rustc_attr::InlineAttr; use rustc_data_structures::base_n; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::HirId; use rustc_session::config::OptLevel; use rustc_span::source_map::Span; @@ -94,7 +95,7 @@ impl<'tcx> MonoItem<'tcx> { // linkage, then we'll be creating a globally shared version. if self.explicit_linkage(tcx).is_some() || !instance.def.generates_cgu_internal_copy(tcx) - || Some(instance.def_id()) == entry_def_id + || Some(instance.def_id()) == entry_def_id.map(LocalDefId::to_def_id) { return InstantiationMode::GloballyShared { may_conflict: false }; } @@ -196,8 +197,12 @@ impl<'tcx> MonoItem<'tcx> { pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option { match *self { - MonoItem::Fn(Instance { def, .. }) => tcx.hir().as_local_hir_id(def.def_id()), - MonoItem::Static(def_id) => tcx.hir().as_local_hir_id(def_id), + MonoItem::Fn(Instance { def, .. }) => { + def.def_id().as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + } + MonoItem::Static(def_id) => { + def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + } MonoItem::GlobalAsm(hir_id) => Some(hir_id), } .map(|hir_id| tcx.hir().span(hir_id)) @@ -234,6 +239,9 @@ pub struct CodegenUnit<'tcx> { size_estimate: Option, } +/// Specifies the linkage type for a `MonoItem`. +/// +/// See https://llvm.org/docs/LangRef.html#linkage-types for more details about these variants. #[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] pub enum Linkage { External, @@ -338,7 +346,9 @@ impl<'tcx> CodegenUnit<'tcx> { // instances into account. The others don't matter for // the codegen tests and can even make item order // unstable. - InstanceDef::Item(def_id) => tcx.hir().as_local_hir_id(def_id), + InstanceDef::Item(def_id) => { + def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + } InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) @@ -349,7 +359,9 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceDef::CloneShim(..) => None, } } - MonoItem::Static(def_id) => tcx.hir().as_local_hir_id(def_id), + MonoItem::Static(def_id) => { + def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + } MonoItem::GlobalAsm(hir_id) => Some(hir_id), }, item.symbol_name(tcx), diff --git a/src/librustc_middle/mir/predecessors.rs b/src/librustc_middle/mir/predecessors.rs new file mode 100644 index 0000000000000..9508365886aa7 --- /dev/null +++ b/src/librustc_middle/mir/predecessors.rs @@ -0,0 +1,84 @@ +//! Lazily compute the reverse control-flow graph for the MIR. + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{Lock, Lrc}; +use rustc_index::vec::IndexVec; +use rustc_serialize as serialize; +use smallvec::SmallVec; + +use crate::mir::{BasicBlock, BasicBlockData}; + +// Typically 95%+ of basic blocks have 4 or fewer predecessors. +pub type Predecessors = IndexVec>; + +#[derive(Clone, Debug)] +pub(super) struct PredecessorCache { + cache: Lock>>, +} + +impl PredecessorCache { + #[inline] + pub(super) fn new() -> Self { + PredecessorCache { cache: Lock::new(None) } + } + + /// Invalidates the predecessor cache. + /// + /// Invalidating the predecessor cache requires mutating the MIR, which in turn requires a + /// unique reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all + /// callers of `invalidate` have a unique reference to the MIR and thus to the predecessor + /// cache. This means we don't actually need to take a lock when `invalidate` is called. + #[inline] + pub(super) fn invalidate(&mut self) { + *self.cache.get_mut() = None; + } + + /// Returns a ref-counted smart pointer containing the predecessor graph for this MIR. + /// + /// We use ref-counting instead of a mapped `LockGuard` here to ensure that the lock for + /// `cache` is only held inside this function. As long as no other locks are taken while + /// computing the predecessor graph, deadlock is impossible. + #[inline] + pub(super) fn compute( + &self, + basic_blocks: &IndexVec>, + ) -> Lrc { + Lrc::clone(self.cache.lock().get_or_insert_with(|| { + let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks); + for (bb, data) in basic_blocks.iter_enumerated() { + if let Some(term) = &data.terminator { + for &succ in term.successors() { + preds[succ].push(bb); + } + } + } + + Lrc::new(preds) + })) + } +} + +impl serialize::Encodable for PredecessorCache { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + serialize::Encodable::encode(&(), s) + } +} + +impl serialize::Decodable for PredecessorCache { + #[inline] + fn decode(d: &mut D) -> Result { + serialize::Decodable::decode(d).map(|_v: ()| Self::new()) + } +} + +impl HashStable for PredecessorCache { + #[inline] + fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) { + // do nothing + } +} + +CloneTypeFoldableAndLiftImpls! { + PredecessorCache, +} diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs new file mode 100644 index 0000000000000..63b8d8c8da782 --- /dev/null +++ b/src/librustc_middle/mir/query.rs @@ -0,0 +1,229 @@ +//! Values computed by queries that use MIR. + +use crate::ty::{self, Ty}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lrc; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::BitMatrix; +use rustc_index::vec::IndexVec; +use rustc_span::{Span, Symbol}; +use rustc_target::abi::VariantIdx; +use smallvec::SmallVec; + +use super::{Field, SourceInfo}; + +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub enum UnsafetyViolationKind { + General, + /// Permitted both in `const fn`s and regular `fn`s. + GeneralAndConstFn, + BorrowPacked(hir::HirId), +} + +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub struct UnsafetyViolation { + pub source_info: SourceInfo, + pub description: Symbol, + pub details: Symbol, + pub kind: UnsafetyViolationKind, +} + +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] +pub struct UnsafetyCheckResult { + /// Violations that are propagated *upwards* from this function. + pub violations: Lrc<[UnsafetyViolation]>, + /// `unsafe` blocks in this function, along with whether they are used. This is + /// used for the "unused_unsafe" lint. + pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>, +} + +rustc_index::newtype_index! { + pub struct GeneratorSavedLocal { + derive [HashStable] + DEBUG_FORMAT = "_{}", + } +} + +/// The layout of generator state. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct GeneratorLayout<'tcx> { + /// The type of every local stored inside the generator. + pub field_tys: IndexVec>, + + /// Which of the above fields are in each variant. Note that one field may + /// be stored in multiple variants. + pub variant_fields: IndexVec>, + + /// Which saved locals are storage-live at the same time. Locals that do not + /// have conflicts with each other are allowed to overlap in the computed + /// layout. + pub storage_conflicts: BitMatrix, +} + +#[derive(Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct BorrowCheckResult<'tcx> { + /// All the opaque types that are restricted to concrete types + /// by this function. Unlike the value in `TypeckTables`, this has + /// unerased regions. + pub concrete_opaque_types: FxHashMap>, + pub closure_requirements: Option>, + pub used_mut_upvars: SmallVec<[Field; 8]>, +} + +/// The result of the `mir_const_qualif` query. +/// +/// Each field corresponds to an implementer of the `Qualif` trait in +/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each +/// `Qualif`. +#[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)] +pub struct ConstQualifs { + pub has_mut_interior: bool, + pub needs_drop: bool, + pub custom_eq: bool, +} + +/// After we borrow check a closure, we are left with various +/// requirements that we have inferred between the free regions that +/// appear in the closure's signature or on its field types. These +/// requirements are then verified and proved by the closure's +/// creating function. This struct encodes those requirements. +/// +/// The requirements are listed as being between various `RegionVid`. The 0th +/// region refers to `'static`; subsequent region vids refer to the free +/// regions that appear in the closure (or generator's) type, in order of +/// appearance. (This numbering is actually defined by the `UniversalRegions` +/// struct in the NLL region checker. See for example +/// `UniversalRegions::closure_mapping`.) Note the free regions in the +/// closure's signature and captures are erased. +/// +/// Example: If type check produces a closure with the closure substs: +/// +/// ```text +/// ClosureSubsts = [ +/// 'a, // From the parent. +/// 'b, +/// i8, // the "closure kind" +/// for<'x> fn(&' &'x u32) -> &'x u32, // the "closure signature" +/// &' String, // some upvar +/// ] +/// ``` +/// +/// We would "renumber" each free region to a unique vid, as follows: +/// +/// ```text +/// ClosureSubsts = [ +/// '1, // From the parent. +/// '2, +/// i8, // the "closure kind" +/// for<'x> fn(&'3 &'x u32) -> &'x u32, // the "closure signature" +/// &'4 String, // some upvar +/// ] +/// ``` +/// +/// Now the code might impose a requirement like `'1: '2`. When an +/// instance of the closure is created, the corresponding free regions +/// can be extracted from its type and constrained to have the given +/// outlives relationship. +/// +/// In some cases, we have to record outlives requirements between types and +/// regions as well. In that case, if those types include any regions, those +/// regions are recorded using their external names (`ReStatic`, +/// `ReEarlyBound`, `ReFree`). We use these because in a query response we +/// cannot use `ReVar` (which is what we use internally within the rest of the +/// NLL code). +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct ClosureRegionRequirements<'tcx> { + /// The number of external regions defined on the closure. In our + /// example above, it would be 3 -- one for `'static`, then `'1` + /// and `'2`. This is just used for a sanity check later on, to + /// make sure that the number of regions we see at the callsite + /// matches. + pub num_external_vids: usize, + + /// Requirements between the various free regions defined in + /// indices. + pub outlives_requirements: Vec>, +} + +/// Indicates an outlives-constraint between a type or between two +/// free regions declared on the closure. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct ClosureOutlivesRequirement<'tcx> { + // This region or type ... + pub subject: ClosureOutlivesSubject<'tcx>, + + // ... must outlive this one. + pub outlived_free_region: ty::RegionVid, + + // If not, report an error here ... + pub blame_span: Span, + + // ... due to this reason. + pub category: ConstraintCategory, +} + +/// Outlives-constraints can be categorized to determine whether and why they +/// are interesting (for error reporting). Order of variants indicates sort +/// order of the category, thereby influencing diagnostic output. +/// +/// See also `rustc_mir::borrow_check::constraints`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(RustcEncodable, RustcDecodable, HashStable)] +pub enum ConstraintCategory { + Return, + Yield, + UseAsConst, + UseAsStatic, + TypeAnnotation, + Cast, + + /// A constraint that came from checking the body of a closure. + /// + /// We try to get the category that the closure used when reporting this. + ClosureBounds, + CallArgument, + CopyBound, + SizedBound, + Assignment, + OpaqueType, + + /// A "boring" constraint (caused by the given location) is one that + /// the user probably doesn't want to see described in diagnostics, + /// because it is kind of an artifact of the type system setup. + /// Example: `x = Foo { field: y }` technically creates + /// intermediate regions representing the "type of `Foo { field: y + /// }`", and data flows from `y` into those variables, but they + /// are not very interesting. The assignment into `x` on the other + /// hand might be. + Boring, + // Boring and applicable everywhere. + BoringNoLocation, + + /// A constraint that doesn't correspond to anything the user sees. + Internal, +} + +/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing +/// that must outlive some region. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum ClosureOutlivesSubject<'tcx> { + /// Subject is a type, typically a type parameter, but could also + /// be a projection. Indicates a requirement like `T: 'a` being + /// passed to the caller, where the type here is `T`. + /// + /// The type here is guaranteed not to contain any free regions at + /// present. + Ty(Ty<'tcx>), + + /// Subject is a free region from the closure. Indicates a requirement + /// like `'a: 'b` being passed to the caller; the region here is `'a`. + Region(ty::RegionVid), +} + +/// The constituent parts of an ADT or array. +#[derive(Copy, Clone, Debug, HashStable)] +pub struct DestructuredConst<'tcx> { + pub variant: VariantIdx, + pub fields: &'tcx [&'tcx ty::Const<'tcx>], +} diff --git a/src/librustc/mir/tcx.rs b/src/librustc_middle/mir/tcx.rs similarity index 98% rename from src/librustc/mir/tcx.rs rename to src/librustc_middle/mir/tcx.rs index feb6631926712..17edd9f4cb643 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc_middle/mir/tcx.rs @@ -4,11 +4,11 @@ */ use crate::mir::*; -use crate::ty::layout::VariantIdx; use crate::ty::subst::Subst; use crate::ty::util::IntTypeExt; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; +use rustc_target::abi::VariantIdx; #[derive(Copy, Clone, Debug, TypeFoldable)] pub struct PlaceTy<'tcx> { @@ -173,9 +173,7 @@ impl<'tcx> Rvalue<'tcx> { let ty = op.ty(tcx, lhs_ty, rhs_ty); tcx.intern_tup(&[ty, tcx.types.bool]) } - Rvalue::UnaryOp(UnOp::Not, ref operand) | Rvalue::UnaryOp(UnOp::Neg, ref operand) => { - operand.ty(local_decls, tcx) - } + Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), Rvalue::Discriminant(ref place) => { let ty = place.ty(local_decls, tcx).ty; match ty.kind { diff --git a/src/librustc/mir/traversal.rs b/src/librustc_middle/mir/traversal.rs similarity index 100% rename from src/librustc/mir/traversal.rs rename to src/librustc_middle/mir/traversal.rs diff --git a/src/librustc/mir/type_foldable.rs b/src/librustc_middle/mir/type_foldable.rs similarity index 97% rename from src/librustc/mir/type_foldable.rs rename to src/librustc_middle/mir/type_foldable.rs index 9520f081b6bfb..bb7001c1207bf 100644 --- a/src/librustc/mir/type_foldable.rs +++ b/src/librustc_middle/mir/type_foldable.rs @@ -78,6 +78,9 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { FalseEdges { real_target, imaginary_target } } FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind }, + InlineAsm { template, ref operands, options, destination } => { + InlineAsm { template, operands: operands.fold_with(folder), options, destination } + } }; Terminator { source_info: self.source_info, kind } } @@ -120,6 +123,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { false } } + InlineAsm { ref operands, .. } => operands.visit_with(visitor), Goto { .. } | Resume | Abort diff --git a/src/librustc/mir/visit.rs b/src/librustc_middle/mir/visit.rs similarity index 86% rename from src/librustc/mir/visit.rs rename to src/librustc_middle/mir/visit.rs index 2aca6f684f4fe..02164244771c9 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc_middle/mir/visit.rs @@ -65,15 +65,6 @@ use rustc_span::Span; // variant argument) that does not require visiting, as in // `is_cleanup` above. -macro_rules! body_cache_type { - (mut $a:lifetime, $tcx:lifetime) => { - &mut BodyAndCache<$tcx> - }; - ($a:lifetime, $tcx:lifetime) => { - ReadOnlyBodyAndCache<$a, $tcx> - }; -} - macro_rules! make_mir_visitor { ($visitor_trait_name:ident, $($mutability:ident)?) => { pub trait $visitor_trait_name<'tcx> { @@ -82,7 +73,7 @@ macro_rules! make_mir_visitor { fn visit_body( &mut self, - body: body_cache_type!($($mutability)? '_, 'tcx) + body: &$($mutability)? Body<'tcx>, ) { self.super_body(body); } @@ -163,13 +154,6 @@ macro_rules! make_mir_visitor { self.super_place(place, context, location); } - fn visit_place_base(&mut self, - local: & $($mutability)? Local, - context: PlaceContext, - location: Location) { - self.super_place_base(local, context, location); - } - visit_place_fns!($($mutability)?); fn visit_constant(&mut self, @@ -254,14 +238,14 @@ macro_rules! make_mir_visitor { fn super_body( &mut self, - $($mutability)? body: body_cache_type!($($mutability)? '_, 'tcx) + body: &$($mutability)? Body<'tcx>, ) { let span = body.span; if let Some(yield_ty) = &$($mutability)? body.yield_ty { - self.visit_ty(yield_ty, TyContext::YieldTy(SourceInfo { - span, - scope: OUTERMOST_SOURCE_SCOPE, - })); + self.visit_ty( + yield_ty, + TyContext::YieldTy(SourceInfo::outermost(span)) + ); } // for best performance, we want to use an iterator rather @@ -275,15 +259,14 @@ macro_rules! make_mir_visitor { self.visit_basic_block_data(bb, data); } - let body: & $($mutability)? Body<'_> = & $($mutability)? body; for scope in &$($mutability)? body.source_scopes { self.visit_source_scope_data(scope); } - self.visit_ty(&$($mutability)? body.return_ty(), TyContext::ReturnTy(SourceInfo { - span: body.span, - scope: OUTERMOST_SOURCE_SCOPE, - })); + self.visit_ty( + &$($mutability)? body.return_ty(), + TyContext::ReturnTy(SourceInfo::outermost(body.span)) + ); for local in body.local_decls.indices() { self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); @@ -305,6 +288,11 @@ macro_rules! make_mir_visitor { } self.visit_span(&$($mutability)? body.span); + + for const_ in &$($mutability)? body.required_consts { + let location = START_BLOCK.start_location(); + self.visit_constant(const_, location); + } } fn super_basic_block_data(&mut self, @@ -385,7 +373,7 @@ macro_rules! make_mir_visitor { location ); } - StatementKind::InlineAsm(asm) => { + StatementKind::LlvmInlineAsm(asm) => { for output in & $($mutability)? asm.outputs[..] { self.visit_place( output, @@ -439,13 +427,29 @@ macro_rules! make_mir_visitor { TerminatorKind::Goto { .. } | TerminatorKind::Resume | TerminatorKind::Abort | - TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | TerminatorKind::FalseEdges { .. } | TerminatorKind::FalseUnwind { .. } => { } + TerminatorKind::Return => { + // `return` logically moves from the return place `_0`. Note that the place + // cannot be changed by any visitor, though. + let $($mutability)? local = RETURN_PLACE; + self.visit_local( + & $($mutability)? local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), + source_location, + ); + + assert_eq!( + local, + RETURN_PLACE, + "`MutVisitor` tried to mutate return place of `return` terminator" + ); + } + TerminatorKind::SwitchInt { discr, switch_ty, @@ -522,11 +526,49 @@ macro_rules! make_mir_visitor { self.visit_operand(value, source_location); self.visit_place( resume_arg, - PlaceContext::MutatingUse(MutatingUseContext::Store), + PlaceContext::MutatingUse(MutatingUseContext::Yield), source_location, ); } + TerminatorKind::InlineAsm { + template: _, + operands, + options: _, + destination: _, + } => { + for op in operands { + match op { + InlineAsmOperand::In { value, .. } + | InlineAsmOperand::Const { value } => { + self.visit_operand(value, source_location); + } + InlineAsmOperand::Out { place, .. } => { + if let Some(place) = place { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + source_location, + ); + } + } + InlineAsmOperand::InOut { in_value, out_place, .. } => { + self.visit_operand(in_value, source_location); + if let Some(out_place) = out_place { + self.visit_place( + out_place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + source_location, + ); + } + } + InlineAsmOperand::SymFn { value } + | InlineAsmOperand::SymStatic { value } => { + self.visit_constant(value, source_location); + } + } + } + } } } @@ -710,13 +752,6 @@ macro_rules! make_mir_visitor { ); } - fn super_place_base(&mut self, - local: & $($mutability)? Local, - context: PlaceContext, - location: Location) { - self.visit_local(local, context, location); - } - fn super_local_decl(&mut self, local: Local, local_decl: & $($mutability)? LocalDecl<'tcx>) { @@ -734,8 +769,10 @@ macro_rules! make_mir_visitor { local, source_info: *source_info, }); - for (user_ty, _) in & $($mutability)? user_ty.contents { - self.visit_user_type_projection(user_ty); + if let Some(user_ty) = user_ty { + for (user_ty, _) in & $($mutability)? user_ty.contents { + self.visit_user_type_projection(user_ty); + } } self.visit_source_info(source_info); } @@ -819,10 +856,14 @@ macro_rules! make_mir_visitor { fn visit_location( &mut self, - body: body_cache_type!($($mutability)? '_, 'tcx), + body: &$($mutability)? Body<'tcx>, location: Location ) { - let basic_block = & $($mutability)? body[location.block]; + macro_rules! basic_blocks { + (mut) => (body.basic_blocks_mut()); + () => (body.basic_blocks()); + }; + let basic_block = & $($mutability)? basic_blocks!($($mutability)?)[location.block]; if basic_block.statements.len() == location.statement_index { if let Some(ref $($mutability)? terminator) = basic_block.terminator { self.visit_terminator(terminator, location) @@ -838,7 +879,7 @@ macro_rules! make_mir_visitor { } macro_rules! visit_place_fns { - (mut) => ( + (mut) => { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; fn super_place( @@ -847,9 +888,9 @@ macro_rules! visit_place_fns { context: PlaceContext, location: Location, ) { - self.visit_place_base(&mut place.local, context, location); + self.visit_local(&mut place.local, context, location); - if let Some(new_projection) = self.process_projection(&place.projection) { + if let Some(new_projection) = self.process_projection(&place.projection, location) { place.projection = self.tcx().intern_place_elems(&new_projection); } } @@ -857,12 +898,13 @@ macro_rules! visit_place_fns { fn process_projection( &mut self, projection: &'a [PlaceElem<'tcx>], + location: Location, ) -> Option>> { let mut projection = Cow::Borrowed(projection); for i in 0..projection.len() { if let Some(elem) = projection.get(i) { - if let Some(elem) = self.process_projection_elem(elem) { + if let Some(elem) = self.process_projection_elem(elem, location) { // This converts the borrowed projection into `Cow::Owned(_)` and returns a // clone of the projection so we can mutate and reintern later. let vec = projection.to_mut(); @@ -879,13 +921,30 @@ macro_rules! visit_place_fns { fn process_projection_elem( &mut self, - _elem: &PlaceElem<'tcx>, + elem: &PlaceElem<'tcx>, + location: Location, ) -> Option> { - None + match elem { + PlaceElem::Index(local) => { + let mut new_local = *local; + self.visit_local( + &mut new_local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location, + ); + + if new_local == *local { None } else { Some(PlaceElem::Index(new_local)) } + } + PlaceElem::Deref + | PlaceElem::Field(..) + | PlaceElem::ConstantIndex { .. } + | PlaceElem::Subslice { .. } + | PlaceElem::Downcast(..) => None, + } } - ); + }; - () => ( + () => { fn visit_projection( &mut self, local: Local, @@ -907,12 +966,7 @@ macro_rules! visit_place_fns { self.super_projection_elem(local, proj_base, elem, context, location); } - fn super_place( - &mut self, - place: &Place<'tcx>, - context: PlaceContext, - location: Location, - ) { + fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { let mut context = context; if !place.projection.is_empty() { @@ -923,12 +977,9 @@ macro_rules! visit_place_fns { }; } - self.visit_place_base(&place.local, context, location); + self.visit_local(&place.local, context, location); - self.visit_projection(place.local, - &place.projection, - context, - location); + self.visit_projection(place.local, &place.projection, context, location); } fn super_projection( @@ -961,19 +1012,16 @@ macro_rules! visit_place_fns { self.visit_local( local, PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), - location + location, ); } - ProjectionElem::Deref | - ProjectionElem::Subslice { from: _, to: _, from_end: _ } | - ProjectionElem::ConstantIndex { offset: _, - min_length: _, - from_end: _ } | - ProjectionElem::Downcast(_, _) => { - } + ProjectionElem::Deref + | ProjectionElem::Subslice { from: _, to: _, from_end: _ } + | ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } + | ProjectionElem::Downcast(_, _) => {} } } - ); + }; } make_mir_visitor!(Visitor,); @@ -1060,6 +1108,8 @@ pub enum MutatingUseContext { AsmOutput, /// Destination of a call. Call, + /// Destination of a yield. + Yield, /// Being dropped. Drop, /// Mutable borrow. @@ -1108,9 +1158,11 @@ impl PlaceContext { /// Returns `true` if this place context represents a borrow. pub fn is_borrow(&self) -> bool { match *self { - PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) + PlaceContext::NonMutatingUse( + NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::UniqueBorrow, + ) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => true, _ => false, } @@ -1119,8 +1171,7 @@ impl PlaceContext { /// Returns `true` if this place context represents a storage live or storage dead marker. pub fn is_storage_marker(&self) -> bool { match *self { - PlaceContext::NonUse(NonUseContext::StorageLive) - | PlaceContext::NonUse(NonUseContext::StorageDead) => true, + PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => true, _ => false, } } @@ -1168,9 +1219,11 @@ impl PlaceContext { /// Returns `true` if this place context represents an assignment statement. pub fn is_place_assignment(&self) -> bool { match *self { - PlaceContext::MutatingUse(MutatingUseContext::Store) - | PlaceContext::MutatingUse(MutatingUseContext::Call) - | PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) => true, + PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::Call + | MutatingUseContext::AsmOutput, + ) => true, _ => false, } } diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs new file mode 100644 index 0000000000000..13cf9a934b72c --- /dev/null +++ b/src/librustc_middle/query/mod.rs @@ -0,0 +1,1300 @@ +use crate::dep_graph::SerializedDepNodeIndex; +use crate::mir; +use crate::mir::interpret::{GlobalId, LitToConstInput}; +use crate::traits; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, +}; +use crate::ty::query::queries; +use crate::ty::query::QueryDescription; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; + +use rustc_span::symbol::Symbol; +use std::borrow::Cow; + +fn describe_as_module(def_id: DefId, tcx: TyCtxt<'_>) -> String { + if def_id.is_top_level_module() { + "top-level module".to_string() + } else { + format!("module `{}`", tcx.def_path_str(def_id)) + } +} + +// Each of these queries corresponds to a function pointer field in the +// `Providers` struct for requesting a value of that type, and a method +// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way +// which memoizes and does dep-graph tracking, wrapping around the actual +// `Providers` that the driver creates (using several `rustc_*` crates). +// +// The result type of each query must implement `Clone`, and additionally +// `ty::query::values::Value`, which produces an appropriate placeholder +// (error) value if the query resulted in a query cycle. +// Queries marked with `fatal_cycle` do not need the latter implementation, +// as they will raise an fatal error on query cycles instead. +rustc_queries! { + Other { + query trigger_delay_span_bug(key: DefId) -> () { + desc { "trigger a delay span bug" } + } + } + + Other { + // Represents crate as a whole (as distinct from the top-level crate module). + // If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), + // we will have to assume that any change means that you need to be recompiled. + // This is because the `hir_crate` query gives you access to all other items. + // To avoid this fate, do not call `tcx.hir().krate()`; instead, + // prefer wrappers like `tcx.visit_all_items_in_krate()`. + query hir_crate(key: CrateNum) -> &'tcx Crate<'tcx> { + eval_always + no_hash + desc { "get the crate HIR" } + } + + // The indexed HIR. This can be conveniently accessed by `tcx.hir()`. + // Avoid calling this query directly. + query index_hir(_: CrateNum) -> &'tcx map::IndexedHir<'tcx> { + eval_always + no_hash + desc { "index HIR" } + } + + // The items in a module. + // + // This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. + // Avoid calling this query directly. + query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems { + eval_always + desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } + } + + // Gives access to the HIR node for the HIR owner `key`. + // + // This can be conveniently accessed by methods on `tcx.hir()`. + // Avoid calling this query directly. + query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> { + eval_always + desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } + } + + // Gives access to the HIR nodes and bodies inside the HIR owner `key`. + // + // This can be conveniently accessed by methods on `tcx.hir()`. + // Avoid calling this query directly. + query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> { + eval_always + desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } + } + + /// Records the type of every item. + query type_of(key: DefId) -> Ty<'tcx> { + cache_on_disk_if { key.is_local() } + } + + query analysis(key: CrateNum) -> Result<(), ErrorReported> { + eval_always + desc { "running analysis passes on this crate" } + } + + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its + /// associated generics. + query generics_of(key: DefId) -> ty::Generics { + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + load_cached(tcx, id) { + let generics: Option = tcx.queries.on_disk_cache + .try_load_query_result(tcx, id); + generics + } + } + + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the + /// predicates (where-clauses) that must be proven true in order + /// to reference it. This is almost always the "predicates query" + /// that you want. + /// + /// `predicates_of` builds on `predicates_defined_on` -- in fact, + /// it is almost always the same as that query, except for the + /// case of traits. For traits, `predicates_of` contains + /// an additional `Self: Trait<...>` predicate that users don't + /// actually write. This reflects the fact that to invoke the + /// trait (e.g., via `Default::default`) you must supply types + /// that actually implement the trait. (However, this extra + /// predicate gets in the way of some checks, which are intended + /// to operate over only the actual where-clauses written by the + /// user.) + query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + cache_on_disk_if { key.is_local() } + } + + query native_libraries(_: CrateNum) -> Lrc> { + desc { "looking up the native libraries of a linked crate" } + } + + query lint_levels(_: CrateNum) -> LintLevelMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "computing the lint levels for items in this crate" } + } + + query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { + eval_always + desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } + } + } + + Codegen { + query is_panic_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_panic_runtime" } + } + } + + Codegen { + /// Set of all the `DefId`s in this crate that have MIR associated with + /// them. This includes all the body owners, but also things like struct + /// constructors. + query mir_keys(_: CrateNum) -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "getting a list of all mir_keys" } + } + + /// Maps DefId's that have an associated `mir::Body` to the result + /// of the MIR const-checking pass. This is the set of qualifs in + /// the final value of a `const`. + query mir_const_qualif(key: DefId) -> mir::ConstQualifs { + desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + /// Fetch the MIR for a given `DefId` right after it's built - this includes + /// unreachable code. + query mir_built(_: LocalDefId) -> Steal> { + storage(ArenaCacheSelector<'tcx>) + desc { "building MIR for" } + } + + /// Fetch the MIR for a given `DefId` up till the point where it is + /// ready for const evaluation. + /// + /// See the README for the `mir` module for details. + query mir_const(_: DefId) -> Steal> { + storage(ArenaCacheSelector<'tcx>) + no_hash + } + + query mir_validated(key: LocalDefId) -> + ( + Steal>, + Steal>> + ) { + storage(ArenaCacheSelector<'tcx>) + no_hash + desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } + } + + /// MIR after our optimization passes have run. This is MIR that is ready + /// for codegen. This is also the only query that can fetch non-local MIR, at present. + query optimized_mir(key: DefId) -> mir::Body<'tcx> { + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + + query promoted_mir(key: DefId) -> IndexVec> { + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + } + + TypeChecking { + // Erases regions from `ty` to yield a new type. + // Normally you would just use `tcx.erase_regions(&value)`, + // however, which uses this query as a kind of cache. + query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { + // This query is not expected to have input -- as a result, it + // is not a good candidates for "replay" because it is essentially a + // pure function of its input (and hence the expectation is that + // no caller would be green **apart** from just these + // queries). Making it anonymous avoids hashing the result, which + // may save a bit of time. + anon + desc { "erasing regions from `{:?}`", ty } + } + } + + Linking { + query wasm_import_module_map(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "wasm import module map" } + } + } + + Other { + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the + /// predicates (where-clauses) directly defined on it. This is + /// equal to the `explicit_predicates_of` predicates plus the + /// `inferred_outlives_of` predicates. + query predicates_defined_on(_: DefId) -> ty::GenericPredicates<'tcx> {} + + /// Returns the predicates written explicitly by the user. + query explicit_predicates_of(_: DefId) -> ty::GenericPredicates<'tcx> {} + + /// Returns the inferred outlives predicates (e.g., for `struct + /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). + query inferred_outlives_of(_: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] {} + + /// Maps from the `DefId` of a trait to the list of + /// super-predicates. This is a subset of the full list of + /// predicates. We store these in a separate map because we must + /// evaluate them even during type conversion, often before the + /// full predicates are available (note that supertraits have + /// additional acyclicity requirements). + query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } + } + + /// To avoid cycles within the predicates of a single item we compute + /// per-type-parameter predicates for resolving `T::AssocTy`. + query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the bounds for type parameter `{}`", { + let id = tcx.hir().as_local_hir_id(key.1); + tcx.hir().ty_param_name(id) + }} + } + + query trait_def(_: DefId) -> ty::TraitDef { + storage(ArenaCacheSelector<'tcx>) + } + query adt_def(_: DefId) -> &'tcx ty::AdtDef { + } + query adt_destructor(_: DefId) -> Option {} + + // The cycle error here should be reported as an error by `check_representable`. + // We consider the type as Sized in the meanwhile to avoid + // further errors (done in impl Value for AdtSizedConstraint). + // Use `cycle_delay_bug` to delay the cycle error here to be emitted later + // in case we accidentally otherwise don't emit an error. + query adt_sized_constraint( + _: DefId + ) -> AdtSizedConstraint<'tcx> { + cycle_delay_bug + } + + query adt_dtorck_constraint( + _: DefId + ) -> Result, NoSolution> {} + + /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate + /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might + /// not have the feature gate active). + /// + /// **Do not call this function manually.** It is only meant to cache the base data for the + /// `is_const_fn` function. + query is_const_fn_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if this is a const `impl`. **Do not call this function manually.** + /// + /// This query caches the base data for the `is_const_impl` helper function, which also + /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). + query is_const_impl_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } + } + + query asyncness(key: DefId) -> hir::IsAsync { + desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if calls to the function may be promoted. + /// + /// This is either because the function is e.g., a tuple-struct or tuple-variant + /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should + /// be removed in the future in favour of some form of check which figures out whether the + /// function does not inspect the bits of any of its arguments (so is essentially just a + /// constructor function). + query is_promotable_const_fn(_: DefId) -> bool {} + + query const_fn_is_allowed_fn_ptr(_: DefId) -> bool {} + + /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). + query is_foreign_item(_: DefId) -> bool {} + + /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. + query static_mutability(_: DefId) -> Option {} + + /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. + query generator_kind(_: DefId) -> Option {} + + /// Gets a map with the variance of every item; use `item_variance` instead. + query crate_variances(_: CrateNum) -> ty::CrateVariancesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the variances for items in this crate" } + } + + /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. + query variances_of(_: DefId) -> &'tcx [ty::Variance] {} + } + + TypeChecking { + /// Maps from thee `DefId` of a type to its (inferred) outlives. + query inferred_outlives_crate(_: CrateNum) + -> ty::CratePredicatesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the inferred outlives predicates for items in this crate" } + } + } + + Other { + /// Maps from an impl/trait `DefId to a list of the `DefId`s of its items. + query associated_item_def_ids(_: DefId) -> &'tcx [DefId] {} + + /// Maps from a trait item to the trait item "descriptor". + query associated_item(_: DefId) -> ty::AssocItem { + storage(ArenaCacheSelector<'tcx>) + } + + /// Collects the associated items defined on a trait or impl. + query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } + } + + query impl_trait_ref(_: DefId) -> Option> {} + query impl_polarity(_: DefId) -> ty::ImplPolarity {} + + query issue33140_self_ty(_: DefId) -> Option> {} + } + + TypeChecking { + /// Maps a `DefId` of a type to a list of its inherent impls. + /// Contains implementations of methods that are inherent to a type. + /// Methods in these implementations don't need to be exported. + query inherent_impls(_: DefId) -> &'tcx [DefId] { + eval_always + } + } + + TypeChecking { + /// The result of unsafety-checking this `DefId`. + query unsafety_check_result(key: LocalDefId) -> mir::UnsafetyCheckResult { + desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + storage(ArenaCacheSelector<'tcx>) + } + + /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error + query unsafe_derive_on_repr_packed(_: DefId) -> () {} + + /// The signature of functions and closures. + query fn_sig(_: DefId) -> ty::PolyFnSig<'tcx> {} + } + + Other { + query lint_mod(key: LocalDefId) -> () { + desc { |tcx| "linting {}", describe_as_module(key.to_def_id(), tcx) } + } + + /// Checks the attributes in the module. + query check_mod_attrs(key: DefId) -> () { + desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } + } + + query check_mod_unstable_api_usage(key: DefId) -> () { + desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } + } + + /// Checks the const bodies in the module for illegal operations (e.g. `if` or `loop`). + query check_mod_const_bodies(key: DefId) -> () { + desc { |tcx| "checking consts in {}", describe_as_module(key, tcx) } + } + + /// Checks the loops in the module. + query check_mod_loops(key: DefId) -> () { + desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } + } + + query check_mod_item_types(key: DefId) -> () { + desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) } + } + + query check_mod_privacy(key: LocalDefId) -> () { + desc { |tcx| "checking privacy in {}", describe_as_module(key.to_def_id(), tcx) } + } + + query check_mod_intrinsics(key: DefId) -> () { + desc { |tcx| "checking intrinsics in {}", describe_as_module(key, tcx) } + } + + query check_mod_liveness(key: DefId) -> () { + desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) } + } + + query check_mod_impl_wf(key: DefId) -> () { + desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } + } + + query collect_mod_item_types(key: DefId) -> () { + desc { |tcx| "collecting item types in {}", describe_as_module(key, tcx) } + } + + /// Caches `CoerceUnsized` kinds for impls on custom types. + query coerce_unsized_info(_: DefId) + -> ty::adjustment::CoerceUnsizedInfo {} + } + + TypeChecking { + query typeck_item_bodies(_: CrateNum) -> () { + desc { "type-checking all item bodies" } + } + + query typeck_tables_of(key: LocalDefId) -> &'tcx ty::TypeckTables<'tcx> { + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + query diagnostic_only_typeck_tables_of(key: LocalDefId) -> &'tcx ty::TypeckTables<'tcx> { + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + load_cached(tcx, id) { + let typeck_tables: Option> = tcx + .queries.on_disk_cache + .try_load_query_result(tcx, id); + + typeck_tables.map(|x| &*tcx.arena.alloc(x)) + } + } + } + + Other { + query used_trait_imports(key: LocalDefId) -> &'tcx DefIdSet { + desc { |tcx| "used_trait_imports `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + } + + TypeChecking { + query has_typeck_tables(_: DefId) -> bool {} + + query coherent_trait(def_id: DefId) -> () { + desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } + } + } + + BorrowChecking { + /// Borrow-checks the function body. If this is a closure, returns + /// additional requirements that the closure's creator must verify. + query mir_borrowck(key: LocalDefId) -> mir::BorrowCheckResult<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if(tcx, opt_result) { + tcx.is_closure(key.to_def_id()) + || opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty()) + } + } + } + + TypeChecking { + /// Gets a complete map from all types to their inherent impls. + /// Not meant to be used directly outside of coherence. + /// (Defined only for `LOCAL_CRATE`.) + query crate_inherent_impls(k: CrateNum) + -> CrateInherentImpls { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "all inherent impls defined in crate `{:?}`", k } + } + + /// Checks all types in the crate for overlap in their inherent impls. Reports errors. + /// Not meant to be used directly outside of coherence. + /// (Defined only for `LOCAL_CRATE`.) + query crate_inherent_impls_overlap_check(_: CrateNum) + -> () { + eval_always + desc { "check for overlap between inherent impls defined in this crate" } + } + } + + Other { + /// Evaluates a constant without running sanity checks. + /// + /// **Do not use this** outside const eval. Const eval uses this to break query cycles + /// during validation. Please add a comment to every use site explaining why using + /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable + /// form to be used outside of const eval. + query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> ConstEvalRawResult<'tcx> { + desc { |tcx| + "const-evaluating `{}`", + tcx.def_path_str(key.value.instance.def.def_id()) + } + } + + /// Results of evaluating const items or constants embedded in + /// other items (such as enum variant explicit discriminants). + /// + /// In contrast to `const_eval_raw` this performs some validation on the constant, and + /// returns a proper constant that is usable by the rest of the compiler. + /// + /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, + /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. + query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> ConstEvalResult<'tcx> { + desc { |tcx| + "const-evaluating + checking `{}`", + tcx.def_path_str(key.value.instance.def.def_id()) + } + cache_on_disk_if(_, opt_result) { + // Only store results without errors + opt_result.map_or(true, |r| r.is_ok()) + } + } + + /// Extracts a field of a (variant of a) const. + query const_field( + key: ty::ParamEnvAnd<'tcx, (&'tcx ty::Const<'tcx>, mir::Field)> + ) -> ConstValue<'tcx> { + desc { "extract field of const" } + } + + /// Destructure a constant ADT or array into its variant index and its + /// field values. + query destructure_const( + key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> + ) -> mir::DestructuredConst<'tcx> { + desc { "destructure constant" } + } + + query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { + desc { "get a &core::panic::Location referring to a span" } + } + + query lit_to_const( + key: LitToConstInput<'tcx> + ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { + desc { "converting literal to const" } + } + } + + TypeChecking { + query check_match(key: DefId) { + cache_on_disk_if { key.is_local() } + } + + /// Performs part of the privacy check and computes "access levels". + query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels { + eval_always + desc { "privacy access levels" } + } + query check_private_in_public(_: CrateNum) -> () { + eval_always + desc { "checking for private elements in public interfaces" } + } + } + + Other { + query reachable_set(_: CrateNum) -> &'tcx HirIdSet { + desc { "reachability" } + } + + /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; + /// in the case of closures, this will be redirected to the enclosing function. + query region_scope_tree(_: DefId) -> &'tcx region::ScopeTree {} + + query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } + } + + /// The `symbol_name` query provides the symbol name for calling a + /// given instance from the local crate. In particular, it will also + /// look up the correct symbol name of instances from upstream crates. + query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName { + desc { "computing the symbol for `{}`", key } + cache_on_disk_if { true } + } + + query def_kind(_: DefId) -> DefKind {} + query def_span(_: DefId) -> Span { + // FIXME(mw): DefSpans are not really inputs since they are derived from + // HIR. But at the moment HIR hashing still contains some hacks that allow + // to make type debuginfo to be source location independent. Declaring + // DefSpan an input makes sure that changes to these are always detected + // regardless of HIR hashing. + eval_always + } + query lookup_stability(_: DefId) -> Option<&'tcx attr::Stability> {} + query lookup_const_stability(_: DefId) -> Option<&'tcx attr::ConstStability> {} + query lookup_deprecation_entry(_: DefId) -> Option {} + query item_attrs(_: DefId) -> &'tcx [ast::Attribute] {} + } + + Codegen { + query codegen_fn_attrs(_: DefId) -> CodegenFnAttrs { + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { true } + } + } + + Other { + query fn_arg_names(_: DefId) -> &'tcx [Symbol] {} + /// Gets the rendered value of the specified constant or associated constant. + /// Used by rustdoc. + query rendered_const(_: DefId) -> String {} + query impl_parent(_: DefId) -> Option {} + } + + TypeChecking { + query trait_of_item(_: DefId) -> Option {} + } + + Codegen { + query is_mir_available(key: DefId) -> bool { + desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } + } + } + + Other { + query vtable_methods(key: ty::PolyTraitRef<'tcx>) + -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { + desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } + } + } + + Codegen { + query codegen_fulfill_obligation( + key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) + ) -> Result, ErrorReported> { + cache_on_disk_if { true } + desc { |tcx| + "checking if `{}` fulfills its obligations", + tcx.def_path_str(key.1.def_id()) + } + } + } + + TypeChecking { + query all_local_trait_impls(key: CrateNum) -> &'tcx BTreeMap> { + desc { "local trait impls" } + } + query trait_impls_of(key: DefId) -> ty::trait_def::TraitImpls { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) } + } + query specialization_graph_of(key: DefId) -> specialization_graph::Graph { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + query object_safety_violations(key: DefId) -> &'tcx [traits::ObjectSafetyViolation] { + desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) } + } + + /// Gets the ParameterEnvironment for a given item; this environment + /// will be in "user-facing" mode, meaning that it is suitabe for + /// type-checking etc, and it does not normalize specializable + /// associated types. This is almost always what you want, + /// unless you are doing MIR optimizations, in which case you + /// might want to use `reveal_all()` method to change modes. + query param_env(_: DefId) -> ty::ParamEnv<'tcx> {} + + /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, + /// `ty.is_copy()`, etc, since that will prune the environment where possible. + query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Copy`", env.value } + } + /// Query backing `TyS::is_sized`. + query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Sized`", env.value } + } + /// Query backing `TyS::is_freeze`. + query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is freeze", env.value } + } + /// Query backing `TyS::needs_drop`. + query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs drop", env.value } + } + + /// A list of types where the ADT requires drop if and only if any of + /// those types require drop. If the ADT is known to always need drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_drop_tys(_: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + cache_on_disk_if { true } + } + + query layout_raw( + env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { + desc { "computing layout of `{}`", env.value } + } + } + + Other { + query dylib_dependency_formats(_: CrateNum) + -> &'tcx [(CrateNum, LinkagePreference)] { + desc { "dylib dependency formats of crate" } + } + + query dependency_formats(_: CrateNum) + -> Lrc + { + desc { "get the linkage format of all dependencies" } + } + } + + Codegen { + query is_compiler_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_compiler_builtins" } + } + query has_global_allocator(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_global_allocator" } + } + query has_panic_handler(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_panic_handler" } + } + query is_profiler_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "query a crate is `#![profiler_runtime]`" } + } + query panic_strategy(_: CrateNum) -> PanicStrategy { + fatal_cycle + desc { "query a crate's configured panic strategy" } + } + query is_no_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "test whether a crate has `#![no_builtins]`" } + } + query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { + fatal_cycle + desc { "query a crate's symbol mangling version" } + } + + query extern_crate(_: DefId) -> Option<&'tcx ExternCrate> { + eval_always + desc { "getting crate's ExternCrateData" } + } + } + + TypeChecking { + query specializes(_: (DefId, DefId)) -> bool { + desc { "computing whether impls specialize one another" } + } + query in_scope_traits_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + eval_always + desc { "traits in scope at a block" } + } + } + + Other { + query module_exports(_: DefId) -> Option<&'tcx [Export]> { + eval_always + } + } + + TypeChecking { + query impl_defaultness(_: DefId) -> hir::Defaultness {} + + query check_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } + } + query check_trait_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } + } + query check_impl_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } + } + } + + Linking { + // The `DefId`s of all non-generic functions and statics in the given crate + // that can be reached from outside the crate. + // + // We expect this items to be available for being linked to. + // + // This query can also be called for `LOCAL_CRATE`. In this case it will + // compute which items will be reachable to other crates, taking into account + // the kind of crate that is currently compiled. Crates with only a + // C interface have fewer reachable things. + // + // Does not include external symbols that don't have a corresponding DefId, + // like the compiler-generated `main` function and so on. + query reachable_non_generics(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "looking up the exported symbols of a crate" } + } + query is_reachable_non_generic(_: DefId) -> bool {} + query is_unreachable_local_definition(_: DefId) -> bool {} + } + + Codegen { + /// The entire set of monomorphizations the local crate can safely link + /// to because they are exported from upstream crates. Do not depend on + /// this directly, as its value changes anytime a monomorphization gets + /// added or removed in any upstream crate. Instead use the narrower + /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even + /// better, `Instance::upstream_monomorphization()`. + query upstream_monomorphizations( + k: CrateNum + ) -> DefIdMap, CrateNum>> { + storage(ArenaCacheSelector<'tcx>) + desc { "collecting available upstream monomorphizations `{:?}`", k } + } + + /// Returns the set of upstream monomorphizations available for the + /// generic function identified by the given `def_id`. The query makes + /// sure to make a stable selection if the same monomorphization is + /// available in multiple upstream crates. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + query upstream_monomorphizations_for(_: DefId) + -> Option<&'tcx FxHashMap, CrateNum>> {} + + /// Returns the upstream crate that exports drop-glue for the given + /// type (`substs` is expected to be a single-item list containing the + /// type one wants drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order to + /// increase dep-tracking granularity. Otherwise adding or removing any + /// type with drop-glue in any upstream crate would invalidate all + /// functions calling drop-glue of an upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option { + desc { "available upstream drop-glue for `{:?}`", substs } + } + } + + Other { + query foreign_modules(_: CrateNum) -> &'tcx [ForeignModule] { + desc { "looking up the foreign modules of a linked crate" } + } + + /// Identifies the entry-point (e.g., the `main` function) for a given + /// crate, returning `None` if there is no entry point (such as for library crates). + query entry_fn(_: CrateNum) -> Option<(LocalDefId, EntryFnType)> { + desc { "looking up the entry function of a crate" } + } + query plugin_registrar_fn(_: CrateNum) -> Option { + desc { "looking up the plugin registrar for a crate" } + } + query proc_macro_decls_static(_: CrateNum) -> Option { + desc { "looking up the derive registrar for a crate" } + } + query crate_disambiguator(_: CrateNum) -> CrateDisambiguator { + eval_always + desc { "looking up the disambiguator a crate" } + } + query crate_hash(_: CrateNum) -> Svh { + eval_always + desc { "looking up the hash a crate" } + } + query crate_host_hash(_: CrateNum) -> Option { + eval_always + desc { "looking up the hash of a host version of a crate" } + } + query original_crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "looking up the original name a crate" } + } + query extra_filename(_: CrateNum) -> String { + eval_always + desc { "looking up the extra filename for a crate" } + } + } + + TypeChecking { + query implementations_of_trait(_: (CrateNum, DefId)) + -> &'tcx [DefId] { + desc { "looking up implementations of a trait in a crate" } + } + query all_trait_implementations(_: CrateNum) + -> &'tcx [DefId] { + desc { "looking up all (?) trait implementations" } + } + } + + Other { + query dllimport_foreign_items(_: CrateNum) + -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "dllimport_foreign_items" } + } + query is_dllimport_foreign_item(_: DefId) -> bool {} + query is_statically_included_foreign_item(_: DefId) -> bool {} + query native_library_kind(_: DefId) + -> Option {} + } + + Linking { + query link_args(_: CrateNum) -> Lrc> { + eval_always + desc { "looking up link arguments for a crate" } + } + } + + BorrowChecking { + /// Lifetime resolution. See `middle::resolve_lifetimes`. + query resolve_lifetimes(_: CrateNum) -> ResolveLifetimes { + storage(ArenaCacheSelector<'tcx>) + desc { "resolving lifetimes" } + } + query named_region_map(_: LocalDefId) -> + Option<&'tcx FxHashMap> { + desc { "looking up a named region" } + } + query is_late_bound_map(_: LocalDefId) -> + Option<&'tcx FxHashSet> { + desc { "testing if a region is late bound" } + } + query object_lifetime_defaults_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + desc { "looking up lifetime defaults for a region" } + } + } + + TypeChecking { + query visibility(_: DefId) -> ty::Visibility {} + } + + Other { + query dep_kind(_: CrateNum) -> DepKind { + eval_always + desc { "fetching what a dependency looks like" } + } + query crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "fetching what a crate is named" } + } + query item_children(_: DefId) -> &'tcx [Export] {} + query extern_mod_stmt_cnum(_: DefId) -> Option {} + + query get_lib_features(_: CrateNum) -> LibFeatures { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lib features map" } + } + query defined_lib_features(_: CrateNum) + -> &'tcx [(Symbol, Option)] { + desc { "calculating the lib features defined in a crate" } + } + /// Returns the lang items defined in another crate by loading it from metadata. + // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid + // of that argument? + query get_lang_items(_: CrateNum) -> LanguageItems { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lang items map" } + } + + /// Returns all diagnostic items defined in all crates. + query all_diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the diagnostic items map" } + } + + /// Returns the lang items defined in another crate by loading it from metadata. + query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { + desc { "calculating the lang items defined in a crate" } + } + + /// Returns the diagnostic items defined in a crate. + query diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the diagnostic items map in a crate" } + } + + query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { + desc { "calculating the missing lang items in a crate" } + } + query visible_parent_map(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the visible parent map" } + } + query missing_extern_crate_item(_: CrateNum) -> bool { + eval_always + desc { "seeing if we're missing an `extern crate` item for this crate" } + } + query used_crate_source(_: CrateNum) -> Lrc { + eval_always + desc { "looking at the source for a crate" } + } + query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "generating a postorder list of CrateNums" } + } + + query upvars(_: DefId) -> Option<&'tcx FxIndexMap> { + eval_always + } + query maybe_unused_trait_import(def_id: LocalDefId) -> bool { + eval_always + desc { |tcx| "maybe_unused_trait_import for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + query maybe_unused_extern_crates(_: CrateNum) + -> &'tcx [(DefId, Span)] { + eval_always + desc { "looking up all possibly unused extern crates" } + } + query names_imported_by_glob_use(def_id: LocalDefId) + -> &'tcx FxHashSet { + eval_always + desc { |tcx| "names_imported_by_glob_use for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + + query stability_index(_: CrateNum) -> stability::Index<'tcx> { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the stability index for the local crate" } + } + query all_crate_nums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "fetching all foreign CrateNum instances" } + } + + /// A vector of every trait accessible in the whole crate + /// (i.e., including those from subcrates). This is used only for + /// error reporting. + query all_traits(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all foreign and local traits" } + } + } + + Linking { + /// The list of symbols exported from the given crate. + /// + /// - All names contained in `exported_symbols(cnum)` are guaranteed to + /// correspond to a publicly visible symbol in `cnum` machine code. + /// - The `exported_symbols` sets of different crates do not intersect. + query exported_symbols(_: CrateNum) + -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { + desc { "exported_symbols" } + } + } + + Codegen { + query collect_and_partition_mono_items(_: CrateNum) + -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + eval_always + desc { "collect_and_partition_mono_items" } + } + query is_codegened_item(_: DefId) -> bool {} + query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { + desc { "codegen_unit" } + } + query backend_optimization_level(_: CrateNum) -> OptLevel { + desc { "optimization level used by backend" } + } + } + + Other { + query output_filenames(_: CrateNum) -> Arc { + eval_always + desc { "output_filenames" } + } + } + + TypeChecking { + /// Do not call this query directly: invoke `normalize` instead. + query normalize_projection_ty( + goal: CanonicalProjectionGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: invoke `normalize_erasing_regions` instead. + query normalize_generic_arg_after_erasing_regions( + goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> + ) -> GenericArg<'tcx> { + desc { "normalizing `{}`", goal.value } + } + + query implied_outlives_bounds( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, + > { + desc { "computing implied outlives bounds for `{:?}`", goal } + } + + /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. + query dropck_outlives( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, + NoSolution, + > { + desc { "computing dropck types for `{:?}`", goal } + } + + /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or + /// `infcx.predicate_must_hold()` instead. + query evaluate_obligation( + goal: CanonicalPredicateGoal<'tcx> + ) -> Result { + desc { "evaluating trait selection obligation `{}`", goal.value.value } + } + + query evaluate_goal( + goal: traits::ChalkCanonicalGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution + > { + desc { "evaluating trait selection obligation `{}`", goal.value } + } + + query type_implements_trait( + key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, ) + ) -> bool { + desc { "evaluating `type_implements_trait` `{:?}`", key } + } + + /// Do not call this query directly: part of the `Eq` type-op + query type_op_ascribe_user_type( + goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Eq` type-op + query type_op_eq( + goal: CanonicalTypeOpEqGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_eq` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Subtype` type-op + query type_op_subtype( + goal: CanonicalTypeOpSubtypeGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_subtype` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `ProvePredicate` type-op + query type_op_prove_predicate( + goal: CanonicalTypeOpProvePredicateGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_prove_predicate` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_ty( + goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_predicate( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_poly_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { + desc { |tcx| + "testing substituted normalized predicates:`{}`", + tcx.def_path_str(key.0) + } + } + + query method_autoderef_steps( + goal: CanonicalTyGoal<'tcx> + ) -> MethodAutoderefStepsResult<'tcx> { + desc { "computing autoderef types for `{:?}`", goal } + } + } + + Other { + query target_features_whitelist(_: CrateNum) -> FxHashMap> { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "looking up the whitelist of target features" } + } + + // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. + query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) + -> usize { + desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } + } + + query features_query(_: CrateNum) -> &'tcx rustc_feature::Features { + eval_always + desc { "looking up enabled feature gates" } + } + + /// Attempt to resolve the given `DefId` to an `Instance`, for the + /// given generics args (`SubstsRef`), returning one of: + /// * `Ok(Some(instance))` on success + /// * `Ok(None)` when the `SubstsRef` are still too generic, + /// and therefore don't allow finding the final `Instance` + /// * `Err(ErrorReported)` when the `Instance` resolution process + /// couldn't complete due to errors elsewhere - this is distinct + /// from `Ok(None)` to avoid misleading diagnostics when an error + /// has already been/will be emitted, for the original cause + query resolve_instance( + key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)> + ) -> Result>, ErrorReported> { + desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } + } + } +} diff --git a/src/librustc_middle/tests.rs b/src/librustc_middle/tests.rs new file mode 100644 index 0000000000000..757e0bd3bfbf9 --- /dev/null +++ b/src/librustc_middle/tests.rs @@ -0,0 +1,13 @@ +use super::*; + +// FIXME(#27438): right now the unit tests of librustc_middle don't refer to any actual +// functions generated in librustc_data_structures (all +// references are through generic functions), but statics are +// referenced from time to time. Due to this bug we won't +// actually correctly link in the statics unless we also +// reference a function, so be sure to reference a dummy +// function. +#[test] +fn noop() { + rustc_data_structures::__noop_fix_for_27438(); +} diff --git a/src/librustc_middle/traits/chalk.rs b/src/librustc_middle/traits/chalk.rs new file mode 100644 index 0000000000000..b963af96f5027 --- /dev/null +++ b/src/librustc_middle/traits/chalk.rs @@ -0,0 +1,366 @@ +//! Types required for Chalk-related queries +//! +//! The primary purpose of this file is defining an implementation for the +//! `chalk_ir::interner::Interner` trait. The primary purpose of this trait, as +//! its name suggest, is to provide an abstraction boundary for creating +//! interned Chalk types. + +use chalk_ir::{GoalData, Parameter}; + +use rustc_middle::mir::Mutability; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +use rustc_hir::def_id::DefId; + +use smallvec::SmallVec; + +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; + +/// Since Chalk doesn't have full support for all Rust builtin types yet, we +/// need to use an enum here, rather than just `DefId`. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum RustDefId { + Adt(DefId), + Str, + Never, + Slice, + Array, + Ref(Mutability), + RawPtr, + + Trait(DefId), + + Impl(DefId), + + FnDef(DefId), + + AssocTy(DefId), +} + +#[derive(Copy, Clone)] +pub struct RustInterner<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + +/// We don't ever actually need this. It's only required for derives. +impl<'tcx> Hash for RustInterner<'tcx> { + fn hash(&self, _state: &mut H) {} +} + +/// We don't ever actually need this. It's only required for derives. +impl<'tcx> Ord for RustInterner<'tcx> { + fn cmp(&self, _other: &Self) -> Ordering { + Ordering::Equal + } +} + +/// We don't ever actually need this. It's only required for derives. +impl<'tcx> PartialOrd for RustInterner<'tcx> { + fn partial_cmp(&self, _other: &Self) -> Option { + None + } +} + +/// We don't ever actually need this. It's only required for derives. +impl<'tcx> PartialEq for RustInterner<'tcx> { + fn eq(&self, _other: &Self) -> bool { + false + } +} + +/// We don't ever actually need this. It's only required for derives. +impl<'tcx> Eq for RustInterner<'tcx> {} + +impl fmt::Debug for RustInterner<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RustInterner") + } +} + +// Right now, there is no interning at all. I was running into problems with +// adding interning in `ty/context.rs` for Chalk types with +// `parallel-compiler = true`. -jackh726 +impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { + type InternedType = Box>; + type InternedLifetime = Box>; + type InternedParameter = Box>; + type InternedGoal = Box>; + type InternedGoals = Vec>; + type InternedSubstitution = Vec>; + type InternedProgramClause = Box>; + type InternedProgramClauses = Vec>; + type InternedQuantifiedWhereClauses = Vec>; + type InternedParameterKinds = Vec>; + type InternedCanonicalVarKinds = Vec>; + type DefId = RustDefId; + type Identifier = (); + + fn debug_program_clause_implication( + pci: &chalk_ir::ProgramClauseImplication, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + let mut write = || { + write!(fmt, "{:?}", pci.consequence)?; + + let conditions = pci.conditions.interned(); + + let conds = conditions.len(); + if conds == 0 { + return Ok(()); + } + + write!(fmt, " :- ")?; + for cond in &conditions[..conds - 1] { + write!(fmt, "{:?}, ", cond)?; + } + write!(fmt, "{:?}", conditions[conds - 1])?; + Ok(()) + }; + Some(write()) + } + + fn debug_application_ty( + application_ty: &chalk_ir::ApplicationTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + let chalk_ir::ApplicationTy { name, substitution } = application_ty; + Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned()))) + } + + fn debug_substitution( + substitution: &chalk_ir::Substitution, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "{:?}", substitution.interned())) + } + + fn debug_separator_trait_ref( + separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Self>, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + let substitution = &separator_trait_ref.trait_ref.substitution; + let parameters = substitution.interned(); + Some(write!( + fmt, + "{:?}{}{:?}{:?}", + parameters[0], + separator_trait_ref.separator, + separator_trait_ref.trait_ref.trait_id, + chalk_ir::debug::Angle(¶meters[1..]) + )) + } + + fn debug_quantified_where_clauses( + clauses: &chalk_ir::QuantifiedWhereClauses, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "{:?}", clauses.interned())) + } + + fn debug_alias( + alias_ty: &chalk_ir::AliasTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + match alias_ty { + chalk_ir::AliasTy::Projection(projection_ty) => { + Self::debug_projection_ty(projection_ty, fmt) + } + chalk_ir::AliasTy::Opaque(opaque_ty) => Self::debug_opaque_ty(opaque_ty, fmt), + } + } + + fn debug_projection_ty( + projection_ty: &chalk_ir::ProjectionTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!( + fmt, + "projection: {:?} {:?}", + projection_ty.associated_ty_id, projection_ty.substitution, + )) + } + + fn debug_opaque_ty( + opaque_ty: &chalk_ir::OpaqueTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id)) + } + + fn intern_ty(&self, ty: chalk_ir::TyData) -> Self::InternedType { + Box::new(ty) + } + + fn ty_data<'a>(&self, ty: &'a Self::InternedType) -> &'a chalk_ir::TyData { + ty + } + + fn intern_lifetime(&self, lifetime: chalk_ir::LifetimeData) -> Self::InternedLifetime { + Box::new(lifetime) + } + + fn lifetime_data<'a>( + &self, + lifetime: &'a Self::InternedLifetime, + ) -> &'a chalk_ir::LifetimeData { + &lifetime + } + + fn intern_parameter( + &self, + parameter: chalk_ir::ParameterData, + ) -> Self::InternedParameter { + Box::new(parameter) + } + + fn parameter_data<'a>( + &self, + parameter: &'a Self::InternedParameter, + ) -> &'a chalk_ir::ParameterData { + ¶meter + } + + fn intern_goal(&self, goal: GoalData) -> Self::InternedGoal { + Box::new(goal) + } + + fn goal_data<'a>(&self, goal: &'a Self::InternedGoal) -> &'a GoalData { + &goal + } + + fn intern_goals( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn goals_data<'a>(&self, goals: &'a Self::InternedGoals) -> &'a [chalk_ir::Goal] { + goals + } + + fn intern_substitution( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn substitution_data<'a>( + &self, + substitution: &'a Self::InternedSubstitution, + ) -> &'a [Parameter] { + substitution + } + + fn intern_program_clause( + &self, + data: chalk_ir::ProgramClauseData, + ) -> Self::InternedProgramClause { + Box::new(data) + } + + fn program_clause_data<'a>( + &self, + clause: &'a Self::InternedProgramClause, + ) -> &'a chalk_ir::ProgramClauseData { + &clause + } + + fn intern_program_clauses( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn program_clauses_data<'a>( + &self, + clauses: &'a Self::InternedProgramClauses, + ) -> &'a [chalk_ir::ProgramClause] { + clauses + } + + fn intern_quantified_where_clauses( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn quantified_where_clauses_data<'a>( + &self, + clauses: &'a Self::InternedQuantifiedWhereClauses, + ) -> &'a [chalk_ir::QuantifiedWhereClause] { + clauses + } + + fn intern_parameter_kinds( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn parameter_kinds_data<'a>( + &self, + parameter_kinds: &'a Self::InternedParameterKinds, + ) -> &'a [chalk_ir::ParameterKind<()>] { + parameter_kinds + } + + fn intern_canonical_var_kinds( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn canonical_var_kinds_data<'a>( + &self, + canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, + ) -> &'a [chalk_ir::ParameterKind] { + canonical_var_kinds + } +} + +impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> { + type Interner = Self; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] +pub enum ChalkEnvironmentClause<'tcx> { + /// A normal rust `ty::Predicate` in the environment. + Predicate(ty::Predicate<'tcx>), + /// A special clause in the environment that gets lowered to + /// `chalk_ir::FromEnv::Ty`. + TypeFromEnv(Ty<'tcx>), +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let v = self.iter().map(|t| t.fold_with(folder)).collect::>(); + folder.tcx().intern_chalk_environment_clause_list(&v) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} +/// We have to elaborate the environment of a chalk goal *before* +/// canonicalization. This type wraps the predicate and the elaborated +/// environment. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] +pub struct ChalkEnvironmentAndGoal<'tcx> { + pub environment: &'tcx ty::List>, + pub goal: ty::Predicate<'tcx>, +} + +impl<'tcx> fmt::Display for ChalkEnvironmentAndGoal<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "environment: {:?}, goal: {}", self.environment, self.goal) + } +} diff --git a/src/librustc_middle/traits/mod.rs b/src/librustc_middle/traits/mod.rs new file mode 100644 index 0000000000000..9afab5a4d2fe9 --- /dev/null +++ b/src/librustc_middle/traits/mod.rs @@ -0,0 +1,709 @@ +//! Trait Resolution. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +mod chalk; +pub mod query; +pub mod select; +pub mod specialization_graph; +mod structural_impls; + +use crate::infer::canonical::Canonical; +use crate::mir::interpret::ErrorHandled; +use crate::ty::subst::SubstsRef; +use crate::ty::{self, AdtKind, Ty, TyCtxt}; + +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use smallvec::SmallVec; + +use std::borrow::Cow; +use std::fmt::Debug; +use std::rc::Rc; + +pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; + +pub type ChalkCanonicalGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>; + +pub use self::ObligationCauseCode::*; +pub use self::SelectionError::*; +pub use self::Vtable::*; + +pub use self::chalk::{ + ChalkEnvironmentAndGoal, ChalkEnvironmentClause, RustDefId as ChalkRustDefId, + RustInterner as ChalkRustInterner, +}; + +/// Depending on the stage of compilation, we want projection to be +/// more or less conservative. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] +pub enum Reveal { + /// At type-checking time, we refuse to project any associated + /// type that is marked `default`. Non-`default` ("final") types + /// are always projected. This is necessary in general for + /// soundness of specialization. However, we *could* allow + /// projections in fully-monomorphic cases. We choose not to, + /// because we prefer for `default type` to force the type + /// definition to be treated abstractly by any consumers of the + /// impl. Concretely, that means that the following example will + /// fail to compile: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// default type Output = bool; + /// } + /// + /// fn main() { + /// let <() as Assoc>::Output = true; + /// } + /// ``` + UserFacing, + + /// At codegen time, all monomorphic projections will succeed. + /// Also, `impl Trait` is normalized to the concrete type, + /// which has to be already collected by type-checking. + /// + /// NOTE: as `impl Trait`'s concrete type should *never* + /// be observable directly by the user, `Reveal::All` + /// should not be used by checks which may expose + /// type equality or type contents to the user. + /// There are some exceptions, e.g., around OIBITS and + /// transmute-checking, which expose some details, but + /// not the whole concrete type of the `impl Trait`. + All, +} + +/// The reason why we incurred this obligation; used for error reporting. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ObligationCause<'tcx> { + pub span: Span, + + /// The ID of the fn body that triggered this obligation. This is + /// used for region obligations to determine the precise + /// environment in which the region obligation should be evaluated + /// (in particular, closures can add new assumptions). See the + /// field `region_obligations` of the `FulfillmentContext` for more + /// information. + pub body_id: hir::HirId, + + pub code: ObligationCauseCode<'tcx>, +} + +impl<'tcx> ObligationCause<'tcx> { + #[inline] + pub fn new( + span: Span, + body_id: hir::HirId, + code: ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + ObligationCause { span, body_id, code } + } + + pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> { + ObligationCause { span, body_id, code: MiscObligation } + } + + pub fn dummy() -> ObligationCause<'tcx> { + ObligationCause { span: DUMMY_SP, body_id: hir::CRATE_HIR_ID, code: MiscObligation } + } + + pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { + match self.code { + ObligationCauseCode::CompareImplMethodObligation { .. } + | ObligationCauseCode::MainFunctionType + | ObligationCauseCode::StartFunctionType => { + tcx.sess.source_map().guess_head_span(self.span) + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + arm_span, + .. + }) => arm_span, + _ => self.span, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ObligationCauseCode<'tcx> { + /// Not well classified or should be obvious from the span. + MiscObligation, + + /// A slice or array is WF only if `T: Sized`. + SliceOrArrayElem, + + /// A tuple is WF only if its middle elements are `Sized`. + TupleElem, + + /// This is the trait reference from the given projection. + ProjectionWf(ty::ProjectionTy<'tcx>), + + /// In an impl of trait `X` for type `Y`, type `Y` must + /// also implement all supertraits of `X`. + ItemObligation(DefId), + + /// Like `ItemObligation`, but with extra detail on the source of the obligation. + BindingObligation(DefId, Span), + + /// A type like `&'a T` is WF only if `T: 'a`. + ReferenceOutlivesReferent(Ty<'tcx>), + + /// A type like `Box + 'b>` is WF only if `'b: 'a`. + ObjectTypeBound(Ty<'tcx>, ty::Region<'tcx>), + + /// Obligation incurred due to an object cast. + ObjectCastObligation(/* Object type */ Ty<'tcx>), + + /// Obligation incurred due to a coercion. + Coercion { + source: Ty<'tcx>, + target: Ty<'tcx>, + }, + + /// Various cases where expressions must be `Sized` / `Copy` / etc. + /// `L = X` implies that `L` is `Sized`. + AssignmentLhsSized, + /// `(x1, .., xn)` must be `Sized`. + TupleInitializerSized, + /// `S { ... }` must be `Sized`. + StructInitializerSized, + /// Type of each variable must be `Sized`. + VariableType(hir::HirId), + /// Argument type must be `Sized`. + SizedArgumentType, + /// Return type must be `Sized`. + SizedReturnType, + /// Yield type must be `Sized`. + SizedYieldType, + /// Inline asm operand type must be `Sized`. + InlineAsmSized, + /// `[T, ..n]` implies that `T` must be `Copy`. + /// If `true`, suggest `const_in_array_repeat_expressions` feature flag. + RepeatVec(bool), + + /// Types of fields (other than the last, except for packed structs) in a struct must be sized. + FieldSized { + adt_kind: AdtKind, + last: bool, + }, + + /// Constant expressions must be sized. + ConstSized, + + /// `static` items must have `Sync` type. + SharedStatic, + + BuiltinDerivedObligation(DerivedObligationCause<'tcx>), + + ImplDerivedObligation(DerivedObligationCause<'tcx>), + + DerivedObligation(DerivedObligationCause<'tcx>), + + /// Error derived when matching traits/impls; see ObligationCause for more details + CompareImplConstObligation, + + /// Error derived when matching traits/impls; see ObligationCause for more details + CompareImplMethodObligation { + item_name: Symbol, + impl_item_def_id: DefId, + trait_item_def_id: DefId, + }, + + /// Error derived when matching traits/impls; see ObligationCause for more details + CompareImplTypeObligation { + item_name: Symbol, + impl_item_def_id: DefId, + trait_item_def_id: DefId, + }, + + /// Checking that this expression can be assigned where it needs to be + // FIXME(eddyb) #11161 is the original Expr required? + ExprAssignable, + + /// Computing common supertype in the arms of a match expression + MatchExpressionArm(Box>), + + /// Type error arising from type checking a pattern against an expected type. + Pattern { + /// The span of the scrutinee or type expression which caused the `root_ty` type. + span: Option, + /// The root expected type induced by a scrutinee or type expression. + root_ty: Ty<'tcx>, + /// Whether the `Span` came from an expression or a type expression. + origin_expr: bool, + }, + + /// Constants in patterns must have `Structural` type. + ConstPatternStructural, + + /// Computing common supertype in an if expression + IfExpression(Box), + + /// Computing common supertype of an if expression with no else counter-part + IfExpressionWithNoElse, + + /// `main` has wrong type + MainFunctionType, + + /// `start` has wrong type + StartFunctionType, + + /// Intrinsic has wrong type + IntrinsicType, + + /// Method receiver + MethodReceiver, + + /// `return` with no expression + ReturnNoExpression, + + /// `return` with an expression + ReturnValue(hir::HirId), + + /// Return type of this function + ReturnType, + + /// Block implicit return + BlockTailExpression(hir::HirId), + + /// #[feature(trivial_bounds)] is not enabled + TrivialBound, +} + +impl ObligationCauseCode<'_> { + // Return the base obligation, ignoring derived obligations. + pub fn peel_derives(&self) -> &Self { + let mut base_cause = self; + while let BuiltinDerivedObligation(cause) + | ImplDerivedObligation(cause) + | DerivedObligation(cause) = base_cause + { + base_cause = &cause.parent_code; + } + base_cause + } +} + +// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(ObligationCauseCode<'_>, 32); + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct MatchExpressionArmCause<'tcx> { + pub arm_span: Span, + pub source: hir::MatchSource, + pub prior_arms: Vec, + pub last_ty: Ty<'tcx>, + pub scrut_hir_id: hir::HirId, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct IfExpressionCause { + pub then: Span, + pub outer: Option, + pub semicolon: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct DerivedObligationCause<'tcx> { + /// The trait reference of the parent obligation that led to the + /// current obligation. Note that only trait obligations lead to + /// derived obligations, so we just store the trait reference here + /// directly. + pub parent_trait_ref: ty::PolyTraitRef<'tcx>, + + /// The parent trait had this cause. + pub parent_code: Rc>, +} + +#[derive(Clone, Debug, TypeFoldable)] +pub enum SelectionError<'tcx> { + Unimplemented, + OutputTypeParameterMismatch( + ty::PolyTraitRef<'tcx>, + ty::PolyTraitRef<'tcx>, + ty::error::TypeError<'tcx>, + ), + TraitNotObjectSafe(DefId), + ConstEvalFailure(ErrorHandled), + Overflow, +} + +/// When performing resolution, it is typically the case that there +/// can be one of three outcomes: +/// +/// - `Ok(Some(r))`: success occurred with result `r` +/// - `Ok(None)`: could not definitely determine anything, usually due +/// to inconclusive type inference. +/// - `Err(e)`: error `e` occurred +pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; + +/// Given the successful resolution of an obligation, the `Vtable` +/// indicates where the vtable comes from. Note that while we call this +/// a "vtable", it does not necessarily indicate dynamic dispatch at +/// runtime. `Vtable` instances just tell the compiler where to find +/// methods, but in generic code those methods are typically statically +/// dispatched -- only when an object is constructed is a `Vtable` +/// instance reified into an actual vtable. +/// +/// For example, the vtable may be tied to a specific impl (case A), +/// or it may be relative to some bound that is in scope (case B). +/// +/// ``` +/// impl Clone for Option { ... } // Impl_1 +/// impl Clone for Box { ... } // Impl_2 +/// impl Clone for int { ... } // Impl_3 +/// +/// fn foo(concrete: Option>, +/// param: T, +/// mixed: Option) { +/// +/// // Case A: Vtable points at a specific impl. Only possible when +/// // type is concretely known. If the impl itself has bounded +/// // type parameters, Vtable will carry resolutions for those as well: +/// concrete.clone(); // Vtable(Impl_1, [Vtable(Impl_2, [Vtable(Impl_3)])]) +/// +/// // Case B: Vtable must be provided by caller. This applies when +/// // type is a type parameter. +/// param.clone(); // VtableParam +/// +/// // Case C: A mix of cases A and B. +/// mixed.clone(); // Vtable(Impl_1, [VtableParam]) +/// } +/// ``` +/// +/// ### The type parameter `N` +/// +/// See explanation on `VtableImplData`. +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub enum Vtable<'tcx, N> { + /// Vtable identifying a particular impl. + VtableImpl(VtableImplData<'tcx, N>), + + /// Vtable for auto trait implementations. + /// This carries the information and nested obligations with regards + /// to an auto implementation for a trait `Trait`. The nested obligations + /// ensure the trait implementation holds for all the constituent types. + VtableAutoImpl(VtableAutoImplData), + + /// Successful resolution to an obligation provided by the caller + /// for some type parameter. The `Vec` represents the + /// obligations incurred from normalizing the where-clause (if + /// any). + VtableParam(Vec), + + /// Virtual calls through an object. + VtableObject(VtableObjectData<'tcx, N>), + + /// Successful resolution for a builtin trait. + VtableBuiltin(VtableBuiltinData), + + /// Vtable automatically generated for a closure. The `DefId` is the ID + /// of the closure expression. This is a `VtableImpl` in spirit, but the + /// impl is generated by the compiler and does not appear in the source. + VtableClosure(VtableClosureData<'tcx, N>), + + /// Same as above, but for a function pointer type with the given signature. + VtableFnPointer(VtableFnPointerData<'tcx, N>), + + /// Vtable for a builtin `DeterminantKind` trait implementation. + VtableDiscriminantKind(VtableDiscriminantKindData), + + /// Vtable automatically generated for a generator. + VtableGenerator(VtableGeneratorData<'tcx, N>), + + /// Vtable for a trait alias. + VtableTraitAlias(VtableTraitAliasData<'tcx, N>), +} + +impl<'tcx, N> Vtable<'tcx, N> { + pub fn nested_obligations(self) -> Vec { + match self { + VtableImpl(i) => i.nested, + VtableParam(n) => n, + VtableBuiltin(i) => i.nested, + VtableAutoImpl(d) => d.nested, + VtableClosure(c) => c.nested, + VtableGenerator(c) => c.nested, + VtableObject(d) => d.nested, + VtableFnPointer(d) => d.nested, + VtableDiscriminantKind(VtableDiscriminantKindData) => Vec::new(), + VtableTraitAlias(d) => d.nested, + } + } + + pub fn borrow_nested_obligations(&self) -> &[N] { + match &self { + VtableImpl(i) => &i.nested[..], + VtableParam(n) => &n[..], + VtableBuiltin(i) => &i.nested[..], + VtableAutoImpl(d) => &d.nested[..], + VtableClosure(c) => &c.nested[..], + VtableGenerator(c) => &c.nested[..], + VtableObject(d) => &d.nested[..], + VtableFnPointer(d) => &d.nested[..], + VtableDiscriminantKind(VtableDiscriminantKindData) => &[], + VtableTraitAlias(d) => &d.nested[..], + } + } + + pub fn map(self, f: F) -> Vtable<'tcx, M> + where + F: FnMut(N) -> M, + { + match self { + VtableImpl(i) => VtableImpl(VtableImplData { + impl_def_id: i.impl_def_id, + substs: i.substs, + nested: i.nested.into_iter().map(f).collect(), + }), + VtableParam(n) => VtableParam(n.into_iter().map(f).collect()), + VtableBuiltin(i) => { + VtableBuiltin(VtableBuiltinData { nested: i.nested.into_iter().map(f).collect() }) + } + VtableObject(o) => VtableObject(VtableObjectData { + upcast_trait_ref: o.upcast_trait_ref, + vtable_base: o.vtable_base, + nested: o.nested.into_iter().map(f).collect(), + }), + VtableAutoImpl(d) => VtableAutoImpl(VtableAutoImplData { + trait_def_id: d.trait_def_id, + nested: d.nested.into_iter().map(f).collect(), + }), + VtableClosure(c) => VtableClosure(VtableClosureData { + closure_def_id: c.closure_def_id, + substs: c.substs, + nested: c.nested.into_iter().map(f).collect(), + }), + VtableGenerator(c) => VtableGenerator(VtableGeneratorData { + generator_def_id: c.generator_def_id, + substs: c.substs, + nested: c.nested.into_iter().map(f).collect(), + }), + VtableFnPointer(p) => VtableFnPointer(VtableFnPointerData { + fn_ty: p.fn_ty, + nested: p.nested.into_iter().map(f).collect(), + }), + VtableDiscriminantKind(VtableDiscriminantKindData) => { + VtableDiscriminantKind(VtableDiscriminantKindData) + } + VtableTraitAlias(d) => VtableTraitAlias(VtableTraitAliasData { + alias_def_id: d.alias_def_id, + substs: d.substs, + nested: d.nested.into_iter().map(f).collect(), + }), + } + } +} + +/// Identifies a particular impl in the source, along with a set of +/// substitutions from the impl's type/lifetime parameters. The +/// `nested` vector corresponds to the nested obligations attached to +/// the impl's type parameters. +/// +/// The type parameter `N` indicates the type used for "nested +/// obligations" that are required by the impl. During type-check, this +/// is `Obligation`, as one might expect. During codegen, however, this +/// is `()`, because codegen only requires a shallow resolution of an +/// impl, and nested obligations are satisfied later. +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableImplData<'tcx, N> { + pub impl_def_id: DefId, + pub substs: SubstsRef<'tcx>, + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableGeneratorData<'tcx, N> { + pub generator_def_id: DefId, + pub substs: SubstsRef<'tcx>, + /// Nested obligations. This can be non-empty if the generator + /// signature contains associated types. + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableClosureData<'tcx, N> { + pub closure_def_id: DefId, + pub substs: SubstsRef<'tcx>, + /// Nested obligations. This can be non-empty if the closure + /// signature contains associated types. + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableAutoImplData { + pub trait_def_id: DefId, + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableBuiltinData { + pub nested: Vec, +} + +/// A vtable for some object-safe trait `Foo` automatically derived +/// for the object type `Foo`. +#[derive(PartialEq, Eq, Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableObjectData<'tcx, N> { + /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. + pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, + + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits; this is the start of + /// `upcast_trait_ref`'s methods in that vtable. + pub vtable_base: usize, + + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableFnPointerData<'tcx, N> { + pub fn_ty: Ty<'tcx>, + pub nested: Vec, +} + +// FIXME(@lcnr): This should be refactored and merged with other builtin vtables. +#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableDiscriminantKindData; + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VtableTraitAliasData<'tcx, N> { + pub alias_def_id: DefId, + pub substs: SubstsRef<'tcx>, + pub nested: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum ObjectSafetyViolation { + /// `Self: Sized` declared on the trait. + SizedSelf(SmallVec<[Span; 1]>), + + /// Supertrait reference references `Self` an in illegal location + /// (e.g., `trait Foo : Bar`). + SupertraitSelf(SmallVec<[Span; 1]>), + + /// Method has something illegal. + Method(Symbol, MethodViolationCode, Span), + + /// Associated const. + AssocConst(Symbol, Span), +} + +impl ObjectSafetyViolation { + pub fn error_msg(&self) -> Cow<'static, str> { + match *self { + ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), + ObjectSafetyViolation::SupertraitSelf(ref spans) => { + if spans.iter().any(|sp| *sp != DUMMY_SP) { + "it uses `Self` as a type parameter in this".into() + } else { + "it cannot use `Self` as a type parameter in a supertrait or `where`-clause" + .into() + } + } + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { + format!("associated function `{}` has no `self` parameter", name).into() + } + ObjectSafetyViolation::Method( + name, + MethodViolationCode::ReferencesSelfInput(_), + DUMMY_SP, + ) => format!("method `{}` references the `Self` type in its parameters", name).into(), + ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfInput(_), _) => { + format!("method `{}` references the `Self` type in this parameter", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfOutput, _) => { + format!("method `{}` references the `Self` type in its return type", name).into() + } + ObjectSafetyViolation::Method( + name, + MethodViolationCode::WhereClauseReferencesSelf, + _, + ) => { + format!("method `{}` references the `Self` type in its `where` clause", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::Generic, _) => { + format!("method `{}` has generic type parameters", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::UndispatchableReceiver, _) => { + format!("method `{}`'s `self` parameter cannot be dispatched on", name).into() + } + ObjectSafetyViolation::AssocConst(name, DUMMY_SP) => { + format!("it contains associated `const` `{}`", name).into() + } + ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(), + } + } + + pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> { + Some(match *self { + ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => { + return None; + } + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => ( + format!( + "consider turning `{}` into a method by giving it a `&self` argument or \ + constraining it so it does not apply to trait objects", + name + ), + sugg.map(|(sugg, sp)| (sugg.to_string(), sp)), + ), + ObjectSafetyViolation::Method( + name, + MethodViolationCode::UndispatchableReceiver, + span, + ) => ( + format!("consider changing method `{}`'s `self` parameter to be `&self`", name), + Some(("&Self".to_string(), span)), + ), + ObjectSafetyViolation::AssocConst(name, _) + | ObjectSafetyViolation::Method(name, ..) => { + (format!("consider moving `{}` to another trait", name), None) + } + }) + } + + pub fn spans(&self) -> SmallVec<[Span; 1]> { + // When `span` comes from a separate crate, it'll be `DUMMY_SP`. Treat it as `None` so + // diagnostics use a `note` instead of a `span_label`. + match self { + ObjectSafetyViolation::SupertraitSelf(spans) + | ObjectSafetyViolation::SizedSelf(spans) => spans.clone(), + ObjectSafetyViolation::AssocConst(_, span) + | ObjectSafetyViolation::Method(_, _, span) + if *span != DUMMY_SP => + { + smallvec![*span] + } + _ => smallvec![], + } + } +} + +/// Reasons a method might not be object-safe. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum MethodViolationCode { + /// e.g., `fn foo()` + StaticMethod(Option<(&'static str, Span)>), + + /// e.g., `fn foo(&self, x: Self)` + ReferencesSelfInput(usize), + + /// e.g., `fn foo(&self) -> Self` + ReferencesSelfOutput, + + /// e.g., `fn foo(&self) where Self: Clone` + WhereClauseReferencesSelf, + + /// e.g., `fn foo()` + Generic, + + /// the method's receiver (`self` argument) can't be dispatched on + UndispatchableReceiver, +} diff --git a/src/librustc_middle/traits/query.rs b/src/librustc_middle/traits/query.rs new file mode 100644 index 0000000000000..e030125b5b15c --- /dev/null +++ b/src/librustc_middle/traits/query.rs @@ -0,0 +1,330 @@ +//! Experimental types for the trait query interface. The methods +//! defined in this module are all based on **canonicalization**, +//! which makes a canonical query by replacing unbound inference +//! variables and regions, so that results can be reused more broadly. +//! The providers for the queries defined here can be found in +//! `librustc_traits`. + +use crate::ich::StableHashingContext; +use crate::infer::canonical::{Canonical, QueryResponse}; +use crate::ty::error::TypeError; +use crate::ty::subst::GenericArg; +use crate::ty::{self, Ty, TyCtxt}; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lrc; +use rustc_errors::struct_span_err; +use rustc_span::source_map::Span; +use std::iter::FromIterator; +use std::mem; + +pub mod type_op { + use crate::ty::fold::TypeFoldable; + use crate::ty::subst::UserSubsts; + use crate::ty::{Predicate, Ty}; + use rustc_hir::def_id::DefId; + use std::fmt; + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct AscribeUserType<'tcx> { + pub mir_ty: Ty<'tcx>, + pub def_id: DefId, + pub user_substs: UserSubsts<'tcx>, + } + + impl<'tcx> AscribeUserType<'tcx> { + pub fn new(mir_ty: Ty<'tcx>, def_id: DefId, user_substs: UserSubsts<'tcx>) -> Self { + Self { mir_ty, def_id, user_substs } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct Eq<'tcx> { + pub a: Ty<'tcx>, + pub b: Ty<'tcx>, + } + + impl<'tcx> Eq<'tcx> { + pub fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + Self { a, b } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct Subtype<'tcx> { + pub sub: Ty<'tcx>, + pub sup: Ty<'tcx>, + } + + impl<'tcx> Subtype<'tcx> { + pub fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + Self { sub, sup } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct ProvePredicate<'tcx> { + pub predicate: Predicate<'tcx>, + } + + impl<'tcx> ProvePredicate<'tcx> { + pub fn new(predicate: Predicate<'tcx>) -> Self { + ProvePredicate { predicate } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct Normalize { + pub value: T, + } + + impl<'tcx, T> Normalize + where + T: fmt::Debug + TypeFoldable<'tcx>, + { + pub fn new(value: T) -> Self { + Self { value } + } + } +} + +pub type CanonicalProjectionGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; + +pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; + +pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; + +pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; + +pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; + +pub type CanonicalTypeOpSubtypeGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; + +pub type CanonicalTypeOpProvePredicateGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; + +pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; + +#[derive(Clone, Debug, HashStable)] +pub struct NoSolution; + +pub type Fallible = Result; + +impl<'tcx> From> for NoSolution { + fn from(_: TypeError<'tcx>) -> NoSolution { + NoSolution + } +} + +#[derive(Clone, Debug, Default, HashStable, TypeFoldable, Lift)] +pub struct DropckOutlivesResult<'tcx> { + pub kinds: Vec>, + pub overflows: Vec>, +} + +impl<'tcx> DropckOutlivesResult<'tcx> { + pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { + if let Some(overflow_ty) = self.overflows.iter().next() { + let mut err = struct_span_err!( + tcx.sess, + span, + E0320, + "overflow while adding drop-check rules for {}", + ty, + ); + err.note(&format!("overflowed on {}", overflow_ty)); + err.emit(); + } + } + + pub fn into_kinds_reporting_overflows( + self, + tcx: TyCtxt<'tcx>, + span: Span, + ty: Ty<'tcx>, + ) -> Vec> { + self.report_overflows(tcx, span, ty); + let DropckOutlivesResult { kinds, overflows: _ } = self; + kinds + } +} + +/// A set of constraints that need to be satisfied in order for +/// a type to be valid for destruction. +#[derive(Clone, Debug, HashStable)] +pub struct DtorckConstraint<'tcx> { + /// Types that are required to be alive in order for this + /// type to be valid for destruction. + pub outlives: Vec>, + + /// Types that could not be resolved: projections and params. + pub dtorck_types: Vec>, + + /// If, during the computation of the dtorck constraint, we + /// overflow, that gets recorded here. The caller is expected to + /// report an error. + pub overflows: Vec>, +} + +impl<'tcx> DtorckConstraint<'tcx> { + pub fn empty() -> DtorckConstraint<'tcx> { + DtorckConstraint { outlives: vec![], dtorck_types: vec![], overflows: vec![] } + } +} + +impl<'tcx> FromIterator> for DtorckConstraint<'tcx> { + fn from_iter>>(iter: I) -> Self { + let mut result = Self::empty(); + + for DtorckConstraint { outlives, dtorck_types, overflows } in iter { + result.outlives.extend(outlives); + result.dtorck_types.extend(dtorck_types); + result.overflows.extend(overflows); + } + + result + } +} + +/// This returns true if the type `ty` is "trivial" for +/// dropck-outlives -- that is, if it doesn't require any types to +/// outlive. This is similar but not *quite* the same as the +/// `needs_drop` test in the compiler already -- that is, for every +/// type T for which this function return true, needs-drop would +/// return `false`. But the reverse does not hold: in particular, +/// `needs_drop` returns false for `PhantomData`, but it is not +/// trivial for dropck-outlives. +/// +/// Note also that `needs_drop` requires a "global" type (i.e., one +/// with erased regions), but this function does not. +pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind { + // None of these types have a destructor and hence they do not + // require anything in particular to outlive the dtor's + // execution. + ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + | ty::Bool + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Never + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Char + | ty::GeneratorWitness(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Str + | ty::Foreign(..) + | ty::Error => true, + + // [T; N] and [T] have same properties as T. + ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), + + // (T1..Tn) and closures have same properties as T1..Tn -- + // check if *any* of those are trivial. + ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), + ty::Closure(_, ref substs) => { + substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) + } + + ty::Adt(def, _) => { + if Some(def.did) == tcx.lang_items().manually_drop() { + // `ManuallyDrop` never has a dtor. + true + } else { + // Other types might. Moreover, PhantomData doesn't + // have a dtor, but it is considered to own its + // content, so it is non-trivial. Unions can have `impl Drop`, + // and hence are non-trivial as well. + false + } + } + + // The following *might* require a destructor: needs deeper inspection. + ty::Dynamic(..) + | ty::Projection(..) + | ty::Param(_) + | ty::Opaque(..) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Bound(..) + | ty::Generator(..) => false, + } +} + +#[derive(Debug, HashStable)] +pub struct CandidateStep<'tcx> { + pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + pub autoderefs: usize, + /// `true` if the type results from a dereference of a raw pointer. + /// when assembling candidates, we include these steps, but not when + /// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods + /// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then + /// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't. + pub from_unsafe_deref: bool, + pub unsize: bool, +} + +#[derive(Clone, Debug, HashStable)] +pub struct MethodAutoderefStepsResult<'tcx> { + /// The valid autoderef steps that could be find. + pub steps: Lrc>>, + /// If Some(T), a type autoderef reported an error on. + pub opt_bad_ty: Option>>, + /// If `true`, `steps` has been truncated due to reaching the + /// recursion limit. + pub reached_recursion_limit: bool, +} + +#[derive(Debug, HashStable)] +pub struct MethodAutoderefBadTy<'tcx> { + pub reached_raw_pointer: bool, + pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, +} + +/// Result from the `normalize_projection_ty` query. +#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] +pub struct NormalizationResult<'tcx> { + /// Result of normalization. + pub normalized_ty: Ty<'tcx>, +} + +/// Outlives bounds are relationships between generic parameters, +/// whether they both be regions (`'a: 'b`) or whether types are +/// involved (`T: 'a`). These relationships can be extracted from the +/// full set of predicates we understand or also from types (in which +/// case they are called implied bounds). They are fed to the +/// `OutlivesEnv` which in turn is supplied to the region checker and +/// other parts of the inference system. +#[derive(Clone, Debug, TypeFoldable, Lift)] +pub enum OutlivesBound<'tcx> { + RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), + RegionSubParam(ty::Region<'tcx>, ty::ParamTy), + RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), +} + +impl<'a, 'tcx> HashStable> for OutlivesBound<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + match *self { + OutlivesBound::RegionSubRegion(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + OutlivesBound::RegionSubParam(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + OutlivesBound::RegionSubProjection(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + } + } +} diff --git a/src/librustc/traits/select.rs b/src/librustc_middle/traits/select.rs similarity index 94% rename from src/librustc/traits/select.rs rename to src/librustc_middle/traits/select.rs index d316d7659e222..a12f5910b14b9 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc_middle/traits/select.rs @@ -34,7 +34,7 @@ impl<'tcx> SelectionCache<'tcx> { /// clauses, and so forth that might resolve an obligation. Sometimes /// we'll be able to say definitively that (e.g.) an impl does not /// apply to the obligation: perhaps it is defined for `usize` but the -/// obligation is for `int`. In that case, we drop the impl out of the +/// obligation is for `i32`. In that case, we drop the impl out of the /// list. But the other cases are considered *candidates*. /// /// For selection to succeed, there must be exactly one matching @@ -54,12 +54,14 @@ impl<'tcx> SelectionCache<'tcx> { /// will always be satisfied) picking the blanket impl will be wrong /// for at least *some* substitutions. To make this concrete, if we have /// -/// trait AsDebug { type Out : fmt::Debug; fn debug(self) -> Self::Out; } -/// impl AsDebug for T { -/// type Out = T; -/// fn debug(self) -> fmt::Debug { self } -/// } -/// fn foo(t: T) { println!("{:?}", ::debug(t)); } +/// ```rust, ignore +/// trait AsDebug { type Out: fmt::Debug; fn debug(self) -> Self::Out; } +/// impl AsDebug for T { +/// type Out = T; +/// fn debug(self) -> fmt::Debug { self } +/// } +/// fn foo(t: T) { println!("{:?}", ::debug(t)); } +/// ``` /// /// we can't just use the impl to resolve the `` obligation /// -- a type from another crate (that doesn't implement `fmt::Debug`) could @@ -79,14 +81,16 @@ impl<'tcx> SelectionCache<'tcx> { /// inference variables. The can lead to inference making "leaps of logic", /// for example in this situation: /// -/// pub trait Foo { fn foo(&self) -> T; } -/// impl Foo<()> for T { fn foo(&self) { } } -/// impl Foo for bool { fn foo(&self) -> bool { *self } } +/// ```rust, ignore +/// pub trait Foo { fn foo(&self) -> T; } +/// impl Foo<()> for T { fn foo(&self) { } } +/// impl Foo for bool { fn foo(&self) -> bool { *self } } /// -/// pub fn foo(t: T) where T: Foo { -/// println!("{:?}", >::foo(&t)); -/// } -/// fn main() { foo(false); } +/// pub fn foo(t: T) where T: Foo { +/// println!("{:?}", >::foo(&t)); +/// } +/// fn main() { foo(false); } +/// ``` /// /// Here the obligation `>` can be matched by both the blanket /// impl and the where-clause. We select the where-clause and unify `$0=bool`, @@ -128,6 +132,9 @@ pub enum SelectionCandidate<'tcx> { /// types generated for a fn pointer type (e.g., `fn(int) -> int`) FnPointerCandidate, + /// Builtin implementation of `DiscriminantKind`. + DiscriminantKindCandidate, + TraitAliasCandidate(DefId), ObjectCandidate, diff --git a/src/librustc_middle/traits/specialization_graph.rs b/src/librustc_middle/traits/specialization_graph.rs new file mode 100644 index 0000000000000..4f02aaa96acd3 --- /dev/null +++ b/src/librustc_middle/traits/specialization_graph.rs @@ -0,0 +1,259 @@ +use crate::ich::{self, StableHashingContext}; +use crate::ty::fast_reject::SimplifiedType; +use crate::ty::{self, TyCtxt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_errors::ErrorReported; +use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_span::symbol::Ident; + +/// A per-trait graph of impls in specialization order. At the moment, this +/// graph forms a tree rooted with the trait itself, with all other nodes +/// representing impls, and parent-child relationships representing +/// specializations. +/// +/// The graph provides two key services: +/// +/// - Construction. This implicitly checks for overlapping impls (i.e., impls +/// that overlap but where neither specializes the other -- an artifact of the +/// simple "chain" rule. +/// +/// - Parent extraction. In particular, the graph can give you the *immediate* +/// parents of a given specializing impl, which is needed for extracting +/// default items amongst other things. In the simple "chain" rule, every impl +/// has at most one parent. +#[derive(RustcEncodable, RustcDecodable, HashStable)] +pub struct Graph { + /// All impls have a parent; the "root" impls have as their parent the `def_id` + /// of the trait. + pub parent: DefIdMap, + + /// The "root" impls are found by looking up the trait's def_id. + pub children: DefIdMap, + + /// Whether an error was emitted while constructing the graph. + pub has_errored: bool, +} + +impl Graph { + pub fn new() -> Graph { + Graph { parent: Default::default(), children: Default::default(), has_errored: false } + } + + /// The parent of a given impl, which is the `DefId` of the trait when the + /// impl is a "specialization root". + pub fn parent(&self, child: DefId) -> DefId { + *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {:?}", child)) + } +} + +/// Children of a given impl, grouped into blanket/non-blanket varieties as is +/// done in `TraitDef`. +#[derive(Default, RustcEncodable, RustcDecodable)] +pub struct Children { + // Impls of a trait (or specializations of a given impl). To allow for + // quicker lookup, the impls are indexed by a simplified version of their + // `Self` type: impls with a simplifiable `Self` are stored in + // `nonblanket_impls` keyed by it, while all other impls are stored in + // `blanket_impls`. + // + // A similar division is used within `TraitDef`, but the lists there collect + // together *all* the impls for a trait, and are populated prior to building + // the specialization graph. + /// Impls of the trait. + pub nonblanket_impls: FxHashMap>, + + /// Blanket impls associated with the trait. + pub blanket_impls: Vec, +} + +/// A node in the specialization graph is either an impl or a trait +/// definition; either can serve as a source of item definitions. +/// There is always exactly one trait definition node: the root. +#[derive(Debug, Copy, Clone)] +pub enum Node { + Impl(DefId), + Trait(DefId), +} + +impl<'tcx> Node { + pub fn is_from_trait(&self) -> bool { + match *self { + Node::Trait(..) => true, + _ => false, + } + } + + /// Iterate over the items defined directly by the given (impl or trait) node. + pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator { + tcx.associated_items(self.def_id()).in_definition_order() + } + + /// Finds an associated item defined in this node. + /// + /// If this returns `None`, the item can potentially still be found in + /// parents of this node. + pub fn item( + &self, + tcx: TyCtxt<'tcx>, + trait_item_name: Ident, + trait_item_kind: ty::AssocKind, + trait_def_id: DefId, + ) -> Option { + use crate::ty::AssocKind::*; + + tcx.associated_items(self.def_id()) + .filter_by_name_unhygienic(trait_item_name.name) + .find(move |impl_item| { + match (trait_item_kind, impl_item.kind) { + | (Const, Const) + | (Fn, Fn) + | (Type, Type) + | (Type, OpaqueTy) // assoc. types can be made opaque in impls + => tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id), + + | (Const, _) + | (Fn, _) + | (Type, _) + | (OpaqueTy, _) + => false, + } + }) + .copied() + } + + pub fn def_id(&self) -> DefId { + match *self { + Node::Impl(did) => did, + Node::Trait(did) => did, + } + } +} + +#[derive(Copy, Clone)] +pub struct Ancestors<'tcx> { + trait_def_id: DefId, + specialization_graph: &'tcx Graph, + current_source: Option, +} + +impl Iterator for Ancestors<'_> { + type Item = Node; + fn next(&mut self) -> Option { + let cur = self.current_source.take(); + if let Some(Node::Impl(cur_impl)) = cur { + let parent = self.specialization_graph.parent(cur_impl); + + self.current_source = if parent == self.trait_def_id { + Some(Node::Trait(parent)) + } else { + Some(Node::Impl(parent)) + }; + } + cur + } +} + +/// Information about the most specialized definition of an associated item. +pub struct LeafDef { + /// The associated item described by this `LeafDef`. + pub item: ty::AssocItem, + + /// The node in the specialization graph containing the definition of `item`. + pub defining_node: Node, + + /// The "top-most" (ie. least specialized) specialization graph node that finalized the + /// definition of `item`. + /// + /// Example: + /// + /// ``` + /// trait Tr { + /// fn assoc(&self); + /// } + /// + /// impl Tr for T { + /// default fn assoc(&self) {} + /// } + /// + /// impl Tr for u8 {} + /// ``` + /// + /// If we start the leaf definition search at `impl Tr for u8`, that impl will be the + /// `finalizing_node`, while `defining_node` will be the generic impl. + /// + /// If the leaf definition search is started at the generic impl, `finalizing_node` will be + /// `None`, since the most specialized impl we found still allows overriding the method + /// (doesn't finalize it). + pub finalizing_node: Option, +} + +impl LeafDef { + /// Returns whether this definition is known to not be further specializable. + pub fn is_final(&self) -> bool { + self.finalizing_node.is_some() + } +} + +impl<'tcx> Ancestors<'tcx> { + /// Finds the bottom-most (ie. most specialized) definition of an associated + /// item. + pub fn leaf_def( + mut self, + tcx: TyCtxt<'tcx>, + trait_item_name: Ident, + trait_item_kind: ty::AssocKind, + ) -> Option { + let trait_def_id = self.trait_def_id; + let mut finalizing_node = None; + + self.find_map(|node| { + if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) { + if finalizing_node.is_none() { + let is_specializable = item.defaultness.is_default() + || tcx.impl_defaultness(node.def_id()).is_default(); + + if !is_specializable { + finalizing_node = Some(node); + } + } + + Some(LeafDef { item, defining_node: node, finalizing_node }) + } else { + // Item not mentioned. This "finalizes" any defaulted item provided by an ancestor. + finalizing_node = Some(node); + None + } + }) + } +} + +/// Walk up the specialization ancestors of a given impl, starting with that +/// impl itself. +/// +/// Returns `Err` if an error was reported while building the specialization +/// graph. +pub fn ancestors( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + start_from_impl: DefId, +) -> Result, ErrorReported> { + let specialization_graph = tcx.specialization_graph_of(trait_def_id); + if specialization_graph.has_errored { + Err(ErrorReported) + } else { + Ok(Ancestors { + trait_def_id, + specialization_graph, + current_source: Some(Node::Impl(start_from_impl)), + }) + } +} + +impl<'a> HashStable> for Children { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let Children { ref nonblanket_impls, ref blanket_impls } = *self; + + ich::hash_stable_trait_impls(hcx, hasher, blanket_impls, nonblanket_impls); + } +} diff --git a/src/librustc_middle/traits/structural_impls.rs b/src/librustc_middle/traits/structural_impls.rs new file mode 100644 index 0000000000000..74f7441529499 --- /dev/null +++ b/src/librustc_middle/traits/structural_impls.rs @@ -0,0 +1,308 @@ +use crate::traits; +use crate::ty::{Lift, TyCtxt}; + +use std::fmt; +use std::rc::Rc; + +// Structural impls for the structs in `traits`. + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::Vtable<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + super::VtableImpl(ref v) => write!(f, "{:?}", v), + + super::VtableAutoImpl(ref t) => write!(f, "{:?}", t), + + super::VtableClosure(ref d) => write!(f, "{:?}", d), + + super::VtableGenerator(ref d) => write!(f, "{:?}", d), + + super::VtableFnPointer(ref d) => write!(f, "VtableFnPointer({:?})", d), + + super::VtableDiscriminantKind(ref d) => write!(f, "{:?}", d), + + super::VtableObject(ref d) => write!(f, "{:?}", d), + + super::VtableParam(ref n) => write!(f, "VtableParam({:?})", n), + + super::VtableBuiltin(ref d) => write!(f, "{:?}", d), + + super::VtableTraitAlias(ref d) => write!(f, "{:?}", d), + } + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableImplData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "VtableImplData(impl_def_id={:?}, substs={:?}, nested={:?})", + self.impl_def_id, self.substs, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableGeneratorData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "VtableGeneratorData(generator_def_id={:?}, substs={:?}, nested={:?})", + self.generator_def_id, self.substs, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableClosureData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "VtableClosureData(closure_def_id={:?}, substs={:?}, nested={:?})", + self.closure_def_id, self.substs, self.nested + ) + } +} + +impl fmt::Debug for traits::VtableBuiltinData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "VtableBuiltinData(nested={:?})", self.nested) + } +} + +impl fmt::Debug for traits::VtableAutoImplData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "VtableAutoImplData(trait_def_id={:?}, nested={:?})", + self.trait_def_id, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableObjectData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "VtableObjectData(upcast={:?}, vtable_base={}, nested={:?})", + self.upcast_trait_ref, self.vtable_base, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableFnPointerData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "VtableFnPointerData(fn_ty={:?}, nested={:?})", self.fn_ty, self.nested) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableTraitAliasData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "VtableTraitAlias(alias_def_id={:?}, substs={:?}, nested={:?})", + self.alias_def_id, self.substs, self.nested + ) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Lift implementations + +impl<'a, 'tcx> Lift<'tcx> for traits::SelectionError<'a> { + type Lifted = traits::SelectionError<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + super::Unimplemented => Some(super::Unimplemented), + super::OutputTypeParameterMismatch(a, b, ref err) => { + tcx.lift(&(a, b)).and_then(|(a, b)| { + tcx.lift(err).map(|err| super::OutputTypeParameterMismatch(a, b, err)) + }) + } + super::TraitNotObjectSafe(def_id) => Some(super::TraitNotObjectSafe(def_id)), + super::ConstEvalFailure(err) => Some(super::ConstEvalFailure(err)), + super::Overflow => Some(super::Overflow), + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { + type Lifted = traits::ObligationCauseCode<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + super::ReturnNoExpression => Some(super::ReturnNoExpression), + super::MiscObligation => Some(super::MiscObligation), + super::SliceOrArrayElem => Some(super::SliceOrArrayElem), + super::TupleElem => Some(super::TupleElem), + super::ProjectionWf(proj) => tcx.lift(&proj).map(super::ProjectionWf), + super::ItemObligation(def_id) => Some(super::ItemObligation(def_id)), + super::BindingObligation(def_id, span) => Some(super::BindingObligation(def_id, span)), + super::ReferenceOutlivesReferent(ty) => { + tcx.lift(&ty).map(super::ReferenceOutlivesReferent) + } + super::ObjectTypeBound(ty, r) => { + tcx.lift(&ty).and_then(|ty| tcx.lift(&r).map(|r| super::ObjectTypeBound(ty, r))) + } + super::ObjectCastObligation(ty) => tcx.lift(&ty).map(super::ObjectCastObligation), + super::Coercion { source, target } => { + Some(super::Coercion { source: tcx.lift(&source)?, target: tcx.lift(&target)? }) + } + super::AssignmentLhsSized => Some(super::AssignmentLhsSized), + super::TupleInitializerSized => Some(super::TupleInitializerSized), + super::StructInitializerSized => Some(super::StructInitializerSized), + super::VariableType(id) => Some(super::VariableType(id)), + super::ReturnValue(id) => Some(super::ReturnValue(id)), + super::ReturnType => Some(super::ReturnType), + super::SizedArgumentType => Some(super::SizedArgumentType), + super::SizedReturnType => Some(super::SizedReturnType), + super::SizedYieldType => Some(super::SizedYieldType), + super::InlineAsmSized => Some(super::InlineAsmSized), + super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)), + super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }), + super::ConstSized => Some(super::ConstSized), + super::ConstPatternStructural => Some(super::ConstPatternStructural), + super::SharedStatic => Some(super::SharedStatic), + super::BuiltinDerivedObligation(ref cause) => { + tcx.lift(cause).map(super::BuiltinDerivedObligation) + } + super::ImplDerivedObligation(ref cause) => { + tcx.lift(cause).map(super::ImplDerivedObligation) + } + super::DerivedObligation(ref cause) => tcx.lift(cause).map(super::DerivedObligation), + super::CompareImplConstObligation => Some(super::CompareImplConstObligation), + super::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } => Some(super::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + }), + super::CompareImplTypeObligation { item_name, impl_item_def_id, trait_item_def_id } => { + Some(super::CompareImplTypeObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + }) + } + super::ExprAssignable => Some(super::ExprAssignable), + super::MatchExpressionArm(box super::MatchExpressionArmCause { + arm_span, + source, + ref prior_arms, + last_ty, + scrut_hir_id, + }) => tcx.lift(&last_ty).map(|last_ty| { + super::MatchExpressionArm(box super::MatchExpressionArmCause { + arm_span, + source, + prior_arms: prior_arms.clone(), + last_ty, + scrut_hir_id, + }) + }), + super::Pattern { span, root_ty, origin_expr } => { + tcx.lift(&root_ty).map(|root_ty| super::Pattern { span, root_ty, origin_expr }) + } + super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }) => { + Some(super::IfExpression(box super::IfExpressionCause { then, outer, semicolon })) + } + super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse), + super::MainFunctionType => Some(super::MainFunctionType), + super::StartFunctionType => Some(super::StartFunctionType), + super::IntrinsicType => Some(super::IntrinsicType), + super::MethodReceiver => Some(super::MethodReceiver), + super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)), + super::TrivialBound => Some(super::TrivialBound), + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for traits::DerivedObligationCause<'a> { + type Lifted = traits::DerivedObligationCause<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.parent_trait_ref).and_then(|trait_ref| { + tcx.lift(&*self.parent_code).map(|code| traits::DerivedObligationCause { + parent_trait_ref: trait_ref, + parent_code: Rc::new(code), + }) + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCause<'a> { + type Lifted = traits::ObligationCause<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.code).map(|code| traits::ObligationCause { + span: self.span, + body_id: self.body_id, + code, + }) + } +} + +// For codegen only. +impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { + type Lifted = traits::Vtable<'tcx, ()>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match self.clone() { + traits::VtableImpl(traits::VtableImplData { impl_def_id, substs, nested }) => { + tcx.lift(&substs).map(|substs| { + traits::VtableImpl(traits::VtableImplData { impl_def_id, substs, nested }) + }) + } + traits::VtableAutoImpl(t) => Some(traits::VtableAutoImpl(t)), + traits::VtableGenerator(traits::VtableGeneratorData { + generator_def_id, + substs, + nested, + }) => tcx.lift(&substs).map(|substs| { + traits::VtableGenerator(traits::VtableGeneratorData { + generator_def_id, + substs, + nested, + }) + }), + traits::VtableClosure(traits::VtableClosureData { closure_def_id, substs, nested }) => { + tcx.lift(&substs).map(|substs| { + traits::VtableClosure(traits::VtableClosureData { + closure_def_id, + substs, + nested, + }) + }) + } + traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, nested }) => { + tcx.lift(&fn_ty).map(|fn_ty| { + traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, nested }) + }) + } + traits::VtableDiscriminantKind(traits::VtableDiscriminantKindData) => { + Some(traits::VtableDiscriminantKind(traits::VtableDiscriminantKindData)) + } + traits::VtableParam(n) => Some(traits::VtableParam(n)), + traits::VtableBuiltin(n) => Some(traits::VtableBuiltin(n)), + traits::VtableObject(traits::VtableObjectData { + upcast_trait_ref, + vtable_base, + nested, + }) => tcx.lift(&upcast_trait_ref).map(|trait_ref| { + traits::VtableObject(traits::VtableObjectData { + upcast_trait_ref: trait_ref, + vtable_base, + nested, + }) + }), + traits::VtableTraitAlias(traits::VtableTraitAliasData { + alias_def_id, + substs, + nested, + }) => tcx.lift(&substs).map(|substs| { + traits::VtableTraitAlias(traits::VtableTraitAliasData { + alias_def_id, + substs, + nested, + }) + }), + } + } +} diff --git a/src/librustc/ty/_match.rs b/src/librustc_middle/ty/_match.rs similarity index 94% rename from src/librustc/ty/_match.rs rename to src/librustc_middle/ty/_match.rs index 35f8eb20475c7..02abe868f3943 100644 --- a/src/librustc/ty/_match.rs +++ b/src/librustc_middle/ty/_match.rs @@ -68,9 +68,12 @@ impl TypeRelation<'tcx> for Match<'tcx> { } match (&a.kind, &b.kind) { - (_, &ty::Infer(ty::FreshTy(_))) - | (_, &ty::Infer(ty::FreshIntTy(_))) - | (_, &ty::Infer(ty::FreshFloatTy(_))) => Ok(a), + ( + _, + &ty::Infer(ty::FreshTy(_)) + | &ty::Infer(ty::FreshIntTy(_)) + | &ty::Infer(ty::FreshFloatTy(_)), + ) => Ok(a), (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { Err(TypeError::Sorts(relate::expected_found(self, &a, &b))) diff --git a/src/librustc/ty/adjustment.rs b/src/librustc_middle/ty/adjustment.rs similarity index 95% rename from src/librustc/ty/adjustment.rs rename to src/librustc_middle/ty/adjustment.rs index 851bffc2065c9..52ebcd63e7cda 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc_middle/ty/adjustment.rs @@ -2,6 +2,7 @@ use crate::ty::subst::SubstsRef; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::{DerefMutTraitLangItem, DerefTraitLangItem}; use rustc_macros::HashStable; #[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] @@ -117,13 +118,13 @@ pub struct OverloadedDeref<'tcx> { impl<'tcx> OverloadedDeref<'tcx> { pub fn method_call(&self, tcx: TyCtxt<'tcx>, source: Ty<'tcx>) -> (DefId, SubstsRef<'tcx>) { let trait_def_id = match self.mutbl { - hir::Mutability::Not => tcx.lang_items().deref_trait(), - hir::Mutability::Mut => tcx.lang_items().deref_mut_trait(), + hir::Mutability::Not => tcx.require_lang_item(DerefTraitLangItem, None), + hir::Mutability::Mut => tcx.require_lang_item(DerefMutTraitLangItem, None), }; let method_def_id = tcx - .associated_items(trait_def_id.unwrap()) + .associated_items(trait_def_id) .in_definition_order() - .find(|m| m.kind == ty::AssocKind::Method) + .find(|m| m.kind == ty::AssocKind::Fn) .unwrap() .def_id; (method_def_id, tcx.mk_substs_trait(source, &[])) diff --git a/src/librustc/ty/binding.rs b/src/librustc_middle/ty/binding.rs similarity index 100% rename from src/librustc/ty/binding.rs rename to src/librustc_middle/ty/binding.rs diff --git a/src/librustc/ty/cast.rs b/src/librustc_middle/ty/cast.rs similarity index 100% rename from src/librustc/ty/cast.rs rename to src/librustc_middle/ty/cast.rs diff --git a/src/librustc/ty/codec.rs b/src/librustc_middle/ty/codec.rs similarity index 98% rename from src/librustc/ty/codec.rs rename to src/librustc_middle/ty/codec.rs index cbbc937ed7d31..1cd4af45f2956 100644 --- a/src/librustc/ty/codec.rs +++ b/src/librustc_middle/ty/codec.rs @@ -15,8 +15,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; use rustc_span::Span; +use std::convert::{TryFrom, TryInto}; use std::hash::Hash; use std::intrinsics; +use std::marker::DiscriminantKind; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -60,6 +62,7 @@ where E: TyEncoder, M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, T: EncodableWithShorthand, + ::Discriminant: Ord + TryFrom, { let existing_shorthand = cache(encoder).get(value).cloned(); if let Some(shorthand) = existing_shorthand { @@ -75,7 +78,8 @@ where // The shorthand encoding uses the same usize as the // discriminant, with an offset so they can't conflict. let discriminant = intrinsics::discriminant_value(variant); - assert!(discriminant < SHORTHAND_OFFSET as u64); + assert!(discriminant < SHORTHAND_OFFSET.try_into().ok().unwrap()); + let shorthand = start + SHORTHAND_OFFSET; // Get the number of bits that leb128 could fit diff --git a/src/librustc/ty/context.rs b/src/librustc_middle/ty/context.rs similarity index 93% rename from src/librustc/ty/context.rs rename to src/librustc_middle/ty/context.rs index 75842fd554941..d2e53facf5e0a 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -4,26 +4,18 @@ use crate::arena::Arena; use crate::dep_graph::DepGraph; use crate::dep_graph::{self, DepConstructor}; use crate::hir::exports::Export; -use crate::hir::map as hir_map; -use crate::hir::map::definitions::Definitions; -use crate::hir::map::{DefPathData, DefPathHash}; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; +use crate::lint::LintDiagnosticBuilder; use crate::lint::{struct_lint_level, LintSource}; use crate::middle; use crate::middle::cstore::CrateStoreDyn; use crate::middle::cstore::EncodedMetadata; -use crate::middle::lang_items; -use crate::middle::lang_items::PanicLocationLangItem; use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault}; use crate::middle::stability; use crate::mir::interpret::{Allocation, ConstValue, Scalar}; -use crate::mir::{ - interpret, BodyAndCache, Field, Local, Place, PlaceElem, ProjectionKind, Promoted, -}; +use crate::mir::{interpret, Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted}; use crate::traits; -use crate::traits::{Clause, Clauses, Goal, GoalKind, Goals}; -use crate::ty::layout::{LayoutDetails, TargetDataLayout, VariantIdx}; use crate::ty::query; use crate::ty::steal::Steal; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; @@ -40,8 +32,6 @@ use crate::ty::{ConstVid, FloatVar, FloatVid, IntVar, IntVid, TyVar, TyVid}; use crate::ty::{ExistentialPredicate, InferTy, ParamTy, PolyFnSig, Predicate, ProjectionTy}; use crate::ty::{InferConst, ParamConst}; use crate::ty::{List, TyKind, TyS}; -use crate::util::common::ErrorReported; -use rustc::lint::LintDiagnosticBuilder; use rustc_ast::ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::node_id::NodeMap; @@ -53,20 +43,24 @@ use rustc_data_structures::stable_hasher::{ hash_stable_hashmap, HashStable, StableHasher, StableVec, }; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::{DefPathHash, Definitions}; +use rustc_hir::lang_items; +use rustc_hir::lang_items::PanicLocationLangItem; use rustc_hir::{HirId, Node, TraitCandidate}; use rustc_hir::{ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet}; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; -use rustc_session::config::CrateType; -use rustc_session::config::{BorrowckMode, OutputFilenames}; +use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; use rustc_session::lint::{Level, Lint}; use rustc_session::Session; use rustc_span::source_map::MultiSpan; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; +use rustc_target::abi::{Layout, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; use smallvec::SmallVec; @@ -96,12 +90,11 @@ pub struct CtxtInterners<'tcx> { region: InternedSet<'tcx, RegionKind>, existential_predicates: InternedSet<'tcx, List>>, predicates: InternedSet<'tcx, List>>, - clauses: InternedSet<'tcx, List>>, - goal: InternedSet<'tcx, GoalKind<'tcx>>, - goal_list: InternedSet<'tcx, List>>, projs: InternedSet<'tcx, List>, place_elems: InternedSet<'tcx, List>>, const_: InternedSet<'tcx, Const<'tcx>>, + + chalk_environment_clause_list: InternedSet<'tcx, List>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -115,12 +108,11 @@ impl<'tcx> CtxtInterners<'tcx> { existential_predicates: Default::default(), canonical_var_infos: Default::default(), predicates: Default::default(), - clauses: Default::default(), - goal: Default::default(), - goal_list: Default::default(), projs: Default::default(), place_elems: Default::default(), const_: Default::default(), + + chalk_environment_clause_list: Default::default(), } } @@ -184,7 +176,7 @@ pub struct CommonLifetimes<'tcx> { } pub struct CommonConsts<'tcx> { - pub err: &'tcx Const<'tcx>, + pub unit: &'tcx Const<'tcx>, } pub struct LocalTableInContext<'a, V> { @@ -300,14 +292,14 @@ pub struct ResolvedOpaqueTy<'tcx> { /// /// ```ignore (pseudo-Rust) /// async move { -/// let x: T = ...; +/// let x: T = expr; /// foo.await /// ... /// } /// ``` /// -/// Here, we would store the type `T`, the span of the value `x`, and the "scope-span" for -/// the scope that contains `x`. +/// Here, we would store the type `T`, the span of the value `x`, the "scope-span" for +/// the scope that contains `x`, the expr `T` evaluated from, and the span of `foo.await`. #[derive(RustcEncodable, RustcDecodable, Clone, Debug, Eq, Hash, PartialEq, HashStable)] pub struct GeneratorInteriorTypeCause<'tcx> { /// Type of the captured binding. @@ -316,6 +308,8 @@ pub struct GeneratorInteriorTypeCause<'tcx> { pub span: Span, /// Span of the scope of the captured binding. pub scope_span: Option, + /// Span of `.await` or `yield` expression. + pub yield_span: Span, /// Expr which the type evaluated from. pub expr: Option, } @@ -386,7 +380,7 @@ pub struct TypeckTables<'tcx> { /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. - closure_kind_origins: ItemLocalMap<(Span, ast::Name)>, + closure_kind_origins: ItemLocalMap<(Span, Symbol)>, /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions @@ -412,8 +406,8 @@ pub struct TypeckTables<'tcx> { pub used_trait_imports: Lrc, /// If any errors occurred while type-checking this body, - /// this field will be set to `true`. - pub tainted_by_errors: bool, + /// this field will be set to `Some(ErrorReported)`. + pub tainted_by_errors: Option, /// All the opaque types that are restricted to concrete types /// by this function. @@ -449,7 +443,7 @@ impl<'tcx> TypeckTables<'tcx> { fru_field_types: Default::default(), coercion_casts: Default::default(), used_trait_imports: Lrc::new(Default::default()), - tainted_by_errors: false, + tainted_by_errors: None, concrete_opaque_types: Default::default(), upvar_list: Default::default(), generator_interior_types: Default::default(), @@ -631,11 +625,11 @@ impl<'tcx> TypeckTables<'tcx> { self.upvar_capture_map[&upvar_id] } - pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, ast::Name)> { + pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, Symbol)> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins } } - pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, ast::Name)> { + pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins } } @@ -860,9 +854,9 @@ impl<'tcx> CommonConsts<'tcx> { let mk_const = |c| interners.const_.intern(c, |c| Interned(interners.arena.alloc(c))).0; CommonConsts { - err: mk_const(ty::Const { + unit: mk_const(ty::Const { val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::zst())), - ty: types.err, + ty: types.unit, }), } } @@ -947,14 +941,14 @@ pub struct GlobalCtxt<'tcx> { pub queries: query::Queries<'tcx>, - maybe_unused_trait_imports: FxHashSet, + maybe_unused_trait_imports: FxHashSet, maybe_unused_extern_crates: Vec<(DefId, Span)>, /// A map of glob use to a set of names it actually imports. Currently only /// used in save-analysis. - glob_map: FxHashMap>, + glob_map: FxHashMap>, /// Extern prelude entries. The value is `true` if the entry was introduced /// via `extern crate` item and not `--extern` option or compiler built-in. - pub extern_prelude: FxHashMap, + pub extern_prelude: FxHashMap, // Internal cache for metadata decoding. No need to track deps on this. pub rcache: Lock>>, @@ -984,30 +978,24 @@ pub struct GlobalCtxt<'tcx> { /// Stores the value of constants (and deduplicates the actual memory) allocation_interner: ShardedHashMap<&'tcx Allocation, ()>, - pub alloc_map: Lock>, + /// Stores memory for globals (statics/consts). + pub(crate) alloc_map: Lock>, - layout_interner: ShardedHashMap<&'tcx LayoutDetails, ()>, + layout_interner: ShardedHashMap<&'tcx Layout, ()>, output_filenames: Arc, } impl<'tcx> TyCtxt<'tcx> { - pub fn alloc_steal_mir(self, mir: BodyAndCache<'tcx>) -> &'tcx Steal> { - self.arena.alloc(Steal::new(mir)) + pub fn alloc_steal_mir(self, mir: Body<'tcx>) -> Steal> { + Steal::new(mir) } pub fn alloc_steal_promoted( self, - promoted: IndexVec>, - ) -> &'tcx Steal>> { - self.arena.alloc(Steal::new(promoted)) - } - - pub fn intern_promoted( - self, - promoted: IndexVec>, - ) -> &'tcx IndexVec> { - self.arena.alloc(promoted) + promoted: IndexVec>, + ) -> Steal>> { + Steal::new(promoted) } pub fn alloc_adt_def( @@ -1017,8 +1005,7 @@ impl<'tcx> TyCtxt<'tcx> { variants: IndexVec, repr: ReprOptions, ) -> &'tcx ty::AdtDef { - let def = ty::AdtDef::new(self, did, kind, variants, repr); - self.arena.alloc(def) + self.arena.alloc(ty::AdtDef::new(self, did, kind, variants, repr)) } pub fn intern_const_alloc(self, alloc: Allocation) -> &'tcx Allocation { @@ -1030,7 +1017,7 @@ impl<'tcx> TyCtxt<'tcx> { // Create an allocation that just contains these bytes. let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes); let alloc = self.intern_const_alloc(alloc); - self.alloc_map.lock().create_memory_alloc(alloc) + self.create_memory_alloc(alloc) } pub fn intern_stability(self, stab: attr::Stability) -> &'tcx attr::Stability { @@ -1041,7 +1028,7 @@ impl<'tcx> TyCtxt<'tcx> { self.const_stability_interner.intern(stab, |stab| self.arena.alloc(stab)) } - pub fn intern_layout(self, layout: LayoutDetails) -> &'tcx LayoutDetails { + pub fn intern_layout(self, layout: Layout) -> &'tcx Layout { self.layout_interner.intern(layout, |layout| self.arena.alloc(layout)) } @@ -1127,11 +1114,11 @@ impl<'tcx> TyCtxt<'tcx> { let mut trait_map: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); for (k, v) in resolutions.trait_map { - let hir_id = definitions.node_to_hir_id(k); + let hir_id = definitions.node_id_to_hir_id(k); let map = trait_map.entry(hir_id.owner).or_default(); let v = v .into_iter() - .map(|tc| tc.map_import_ids(|id| definitions.node_to_hir_id(id))) + .map(|tc| tc.map_import_ids(|id| definitions.node_id_to_hir_id(id))) .collect(); map.insert(hir_id.local_id, StableVec::new(v)); } @@ -1155,7 +1142,7 @@ impl<'tcx> TyCtxt<'tcx> { .map(|(k, v)| { let exports: Vec<_> = v .into_iter() - .map(|e| e.map_id(|id| definitions.node_to_hir_id(id))) + .map(|e| e.map_id(|id| definitions.node_id_to_hir_id(id))) .collect(); (k, exports) }) @@ -1168,7 +1155,7 @@ impl<'tcx> TyCtxt<'tcx> { maybe_unused_extern_crates: resolutions .maybe_unused_extern_crates .into_iter() - .map(|(id, sp)| (definitions.local_def_id(id), sp)) + .map(|(id, sp)| (definitions.local_def_id(id).to_def_id(), sp)) .collect(), glob_map: resolutions .glob_map @@ -1204,7 +1191,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Obtain all lang items of this crate and all dependencies (recursively) - pub fn lang_items(self) -> &'tcx middle::lang_items::LanguageItems { + pub fn lang_items(self) -> &'tcx rustc_hir::lang_items::LanguageItems { self.get_lang_items(LOCAL_CRATE) } @@ -1235,7 +1222,7 @@ impl<'tcx> TyCtxt<'tcx> { self.features_query(LOCAL_CRATE) } - pub fn def_key(self, id: DefId) -> hir_map::DefKey { + pub fn def_key(self, id: DefId) -> rustc_hir::definitions::DefKey { if let Some(id) = id.as_local() { self.hir().def_key(id) } else { self.cstore.def_key(id) } } @@ -1244,7 +1231,7 @@ impl<'tcx> TyCtxt<'tcx> { /// /// Note that if `id` is not local to this crate, the result will /// be a non-local `DefPath`. - pub fn def_path(self, id: DefId) -> hir_map::DefPath { + pub fn def_path(self, id: DefId) -> rustc_hir::definitions::DefPath { if let Some(id) = id.as_local() { self.hir().def_path(id) } else { @@ -1259,7 +1246,7 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn def_path_hash(self, def_id: DefId) -> hir_map::DefPathHash { + pub fn def_path_hash(self, def_id: DefId) -> rustc_hir::definitions::DefPathHash { if let Some(def_id) = def_id.as_local() { self.definitions.def_path_hash(def_id) } else { @@ -1349,7 +1336,7 @@ impl<'tcx> TyCtxt<'tcx> { /// What mode(s) of borrowck should we run? AST? MIR? both? /// (Also considers the `#![feature(nll)]` setting.) - pub fn borrowck_mode(&self) -> BorrowckMode { + pub fn borrowck_mode(self) -> BorrowckMode { // Here are the main constraints we need to deal with: // // 1. An opts.borrowck_mode of `BorrowckMode::Migrate` is @@ -1379,6 +1366,13 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.opts.borrowck_mode } + /// If `true`, we should use lazy normalization for constants, otherwise + /// we still evaluate them eagerly. + #[inline] + pub fn lazy_normalization(self) -> bool { + self.features().const_generics + } + #[inline] pub fn local_crate_exports_generics(self) -> bool { debug_assert!(self.sess.opts.share_generics()); @@ -1411,9 +1405,9 @@ impl<'tcx> TyCtxt<'tcx> { _ => return None, // not a free region }; - let hir_id = self.hir().as_local_hir_id(suitable_region_binding_scope).unwrap(); + let hir_id = self.hir().as_local_hir_id(suitable_region_binding_scope.expect_local()); let is_impl_item = match self.hir().find(hir_id) { - Some(Node::Item(..)) | Some(Node::TraitItem(..)) => false, + Some(Node::Item(..) | Node::TraitItem(..)) => false, Some(Node::ImplItem(..)) => { self.is_bound_region_in_impl_item(suitable_region_binding_scope) } @@ -1429,7 +1423,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn return_type_impl_trait(&self, scope_def_id: DefId) -> Option<(Ty<'tcx>, Span)> { // HACK: `type_of_def_id()` will fail on these (#55796), so return `None`. - let hir_id = self.hir().as_local_hir_id(scope_def_id).unwrap(); + let hir_id = self.hir().as_local_hir_id(scope_def_id.expect_local()); match self.hir().get(hir_id) { Node::Item(item) => { match item.kind { @@ -1490,21 +1484,13 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns a displayable description and article for the given `def_id` (e.g. `("a", "struct")`). pub fn article_and_description(&self, def_id: DefId) -> (&'static str, &'static str) { - self.def_kind(def_id) - .map(|def_kind| (def_kind.article(), def_kind.descr(def_id))) - .unwrap_or_else(|| match self.def_key(def_id).disambiguated_data.data { - DefPathData::ClosureExpr => match self.generator_kind(def_id) { - None => ("a", "closure"), - Some(rustc_hir::GeneratorKind::Async(..)) => ("an", "async closure"), - Some(rustc_hir::GeneratorKind::Gen) => ("a", "generator"), - }, - DefPathData::LifetimeNs(..) => ("a", "lifetime"), - DefPathData::Impl => ("an", "implementation"), - DefPathData::TypeNs(..) | DefPathData::ValueNs(..) | DefPathData::MacroNs(..) => { - unreachable!() - } - _ => bug!("article_and_description called on def_id {:?}", def_id), - }) + match self.def_kind(def_id) { + DefKind::Generator => match self.generator_kind(def_id).unwrap() { + rustc_hir::GeneratorKind::Async(..) => ("an", "async closure"), + rustc_hir::GeneratorKind::Gen => ("a", "generator"), + }, + def_kind => (def_kind.article(), def_kind.descr(def_id)), + } } } @@ -1587,11 +1573,8 @@ macro_rules! nop_list_lift { nop_lift! {type_; Ty<'a> => Ty<'tcx>} nop_lift! {region; Region<'a> => Region<'tcx>} -nop_lift! {goal; Goal<'a> => Goal<'tcx>} nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>} -nop_list_lift! {goal_list; Goal<'a> => Goal<'tcx>} -nop_list_lift! {clauses; Clause<'a> => Clause<'tcx>} nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>} nop_list_lift! {existential_predicates; ExistentialPredicate<'a> => ExistentialPredicate<'tcx>} nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>} @@ -1604,7 +1587,7 @@ nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>} pub mod tls { use super::{ptr_eq, GlobalCtxt, TyCtxt}; - use crate::dep_graph::TaskDeps; + use crate::dep_graph::{DepKind, TaskDeps}; use crate::ty::query; use rustc_data_structures::sync::{self, Lock}; use rustc_data_structures::thin_vec::ThinVec; @@ -1631,7 +1614,7 @@ pub mod tls { /// The current query job, if any. This is updated by `JobOwner::start` in /// `ty::query::plumbing` when executing a query. - pub query: Option, + 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. @@ -1899,7 +1882,6 @@ impl<'tcx> TyCtxt<'tcx> { Bound, Param, Infer, - UnnormalizedProjection, Projection, Opaque, Foreign @@ -2002,12 +1984,6 @@ impl<'tcx> Borrow for Interned<'tcx, RegionKind> { } } -impl<'tcx> Borrow> for Interned<'tcx, GoalKind<'tcx>> { - fn borrow<'a>(&'a self) -> &'a GoalKind<'tcx> { - &self.0 - } -} - impl<'tcx> Borrow<[ExistentialPredicate<'tcx>]> for Interned<'tcx, List>> { @@ -2028,14 +2004,10 @@ impl<'tcx> Borrow> for Interned<'tcx, Const<'tcx>> { } } -impl<'tcx> Borrow<[Clause<'tcx>]> for Interned<'tcx, List>> { - fn borrow<'a>(&'a self) -> &'a [Clause<'tcx>] { - &self.0[..] - } -} - -impl<'tcx> Borrow<[Goal<'tcx>]> for Interned<'tcx, List>> { - fn borrow<'a>(&'a self) -> &'a [Goal<'tcx>] { +impl<'tcx> Borrow<[traits::ChalkEnvironmentClause<'tcx>]> + for Interned<'tcx, List>> +{ + fn borrow<'a>(&'a self) -> &'a [traits::ChalkEnvironmentClause<'tcx>] { &self.0[..] } } @@ -2066,15 +2038,7 @@ macro_rules! direct_interners { } } -pub fn keep_local<'tcx, T: ty::TypeFoldable<'tcx>>(x: &T) -> bool { - x.has_type_flags(ty::TypeFlags::KEEP_IN_LOCAL_TCX) -} - -direct_interners!( - region: mk_region(RegionKind), - goal: mk_goal(GoalKind<'tcx>), - const_: mk_const(Const<'tcx>) -); +direct_interners!(region: mk_region(RegionKind), const_: mk_const(Const<'tcx>)); macro_rules! slice_interners { ($($field:ident: $method:ident($ty:ty)),+) => ( @@ -2094,10 +2058,10 @@ slice_interners!( canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo), existential_predicates: _intern_existential_predicates(ExistentialPredicate<'tcx>), predicates: _intern_predicates(Predicate<'tcx>), - clauses: _intern_clauses(Clause<'tcx>), - goal_list: _intern_goals(Goal<'tcx>), projs: _intern_projs(ProjectionKind), - place_elems: _intern_place_elems(PlaceElem<'tcx>) + place_elems: _intern_place_elems(PlaceElem<'tcx>), + chalk_environment_clause_list: + _intern_chalk_environment_clause_list(traits::ChalkEnvironmentClause<'tcx>) ); impl<'tcx> TyCtxt<'tcx> { @@ -2109,24 +2073,25 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig })) } - /// Given a closure signature `sig`, returns an equivalent `fn` - /// type with the same signature. Detuples and so forth -- so - /// e.g., if we have a sig with `Fn<(u32, i32)>` then you would get - /// a `fn(u32, i32)`. - /// `unsafety` determines the unsafety of the `fn` type. If you pass + /// Given a closure signature, returns an equivalent fn signature. Detuples + /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then + /// you would get a `fn(u32, i32)`. + /// `unsafety` determines the unsafety of the fn signature. If you pass /// `hir::Unsafety::Unsafe` in the previous example, then you would get /// an `unsafe fn (u32, i32)`. /// It cannot convert a closure that requires unsafe. - pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>, unsafety: hir::Unsafety) -> Ty<'tcx> { - let converted_sig = sig.map_bound(|s| { + pub fn signature_unclosure( + self, + sig: PolyFnSig<'tcx>, + unsafety: hir::Unsafety, + ) -> PolyFnSig<'tcx> { + sig.map_bound(|s| { let params_iter = match s.inputs()[0].kind { ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()), _ => bug!(), }; self.mk_fn_sig(params_iter, s.output(), s.c_variadic, unsafety, abi::Abi::Rust) - }); - - self.mk_fn_ptr(converted_sig) + }) } #[allow(rustc::usage_of_ty_tykind)] @@ -2214,6 +2179,12 @@ impl<'tcx> TyCtxt<'tcx> { Some(self.mk_generic_adt(def_id, ty)) } + #[inline] + pub fn mk_diagnostic_item(self, ty: Ty<'tcx>, name: Symbol) -> Option> { + let def_id = self.get_diagnostic_item(name)?; + Some(self.mk_generic_adt(def_id, ty)) + } + #[inline] pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> { let def_id = self.require_lang_item(lang_items::MaybeUninitLangItem, None); @@ -2477,12 +2448,11 @@ impl<'tcx> TyCtxt<'tcx> { if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) } } - pub fn intern_clauses(self, ts: &[Clause<'tcx>]) -> Clauses<'tcx> { - if ts.is_empty() { List::empty() } else { self._intern_clauses(ts) } - } - - pub fn intern_goals(self, ts: &[Goal<'tcx>]) -> Goals<'tcx> { - if ts.is_empty() { List::empty() } else { self._intern_goals(ts) } + pub fn intern_chalk_environment_clause_list( + self, + ts: &[traits::ChalkEnvironmentClause<'tcx>], + ) -> &'tcx List> { + if ts.is_empty() { List::empty() } else { self._intern_chalk_environment_clause_list(ts) } } pub fn mk_fn_sig( @@ -2542,12 +2512,16 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned())) } - pub fn mk_clauses], Clauses<'tcx>>>(self, iter: I) -> I::Output { - iter.intern_with(|xs| self.intern_clauses(xs)) - } - - pub fn mk_goals], Goals<'tcx>>>(self, iter: I) -> I::Output { - iter.intern_with(|xs| self.intern_goals(xs)) + pub fn mk_chalk_environment_clause_list< + I: InternAs< + [traits::ChalkEnvironmentClause<'tcx>], + &'tcx List>, + >, + >( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_chalk_environment_clause_list(xs)) } /// Walks upwards from `id` to find a node which might change lint levels with attributes. @@ -2720,10 +2694,8 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { assert_eq!(cnum, LOCAL_CRATE); &tcx.maybe_unused_extern_crates[..] }; - providers.names_imported_by_glob_use = |tcx, id| { - assert_eq!(id.krate, LOCAL_CRATE); - Lrc::new(tcx.glob_map.get(&id).cloned().unwrap_or_default()) - }; + providers.names_imported_by_glob_use = + |tcx, id| tcx.arena.alloc(tcx.glob_map.get(&id).cloned().unwrap_or_default()); providers.lookup_stability = |tcx, id| { let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); @@ -2751,7 +2723,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { }; providers.features_query = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - tcx.arena.alloc(tcx.sess.features_untracked().clone()) + tcx.sess.features_untracked() }; providers.is_panic_runtime = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); diff --git a/src/librustc_middle/ty/diagnostics.rs b/src/librustc_middle/ty/diagnostics.rs new file mode 100644 index 0000000000000..613d66d59c55b --- /dev/null +++ b/src/librustc_middle/ty/diagnostics.rs @@ -0,0 +1,251 @@ +//! Diagnostics related methods for `TyS`. + +use crate::ty::sty::InferTy; +use crate::ty::TyKind::*; +use crate::ty::{TyCtxt, TyS}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; +use rustc_span::{BytePos, Span}; + +impl<'tcx> TyS<'tcx> { + /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. + pub fn is_primitive_ty(&self) -> bool { + match self.kind { + Bool + | Char + | Str + | Int(_) + | Uint(_) + | Float(_) + | Infer( + InferTy::IntVar(_) + | InferTy::FloatVar(_) + | InferTy::FreshIntTy(_) + | InferTy::FreshFloatTy(_), + ) => true, + _ => false, + } + } + + /// Whether the type is succinctly representable as a type instead of just referred to with a + /// description in error messages. This is used in the main error message. + pub fn is_simple_ty(&self) -> bool { + match self.kind { + Bool + | Char + | Str + | Int(_) + | Uint(_) + | Float(_) + | Infer( + InferTy::IntVar(_) + | InferTy::FloatVar(_) + | InferTy::FreshIntTy(_) + | InferTy::FreshFloatTy(_), + ) => true, + Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(), + Tuple(tys) if tys.is_empty() => true, + _ => false, + } + } + + /// Whether the type is succinctly representable as a type instead of just referred to with a + /// description in error messages. This is used in the primary span label. Beyond what + /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to + /// ADTs with no type arguments. + pub fn is_simple_text(&self) -> bool { + match self.kind { + Adt(_, substs) => substs.types().next().is_none(), + Ref(_, ty, _) => ty.is_simple_text(), + _ => self.is_simple_ty(), + } + } + + /// Whether the type can be safely suggested during error recovery. + pub fn is_suggestable(&self) -> bool { + match self.kind { + Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) + | Projection(..) => false, + _ => true, + } + } +} + +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_param( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut DiagnosticBuilder<'_>, + param_name: &str, + constraint: &str, + def_id: Option, +) -> bool { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + + let param = if let Some(param) = param { + param + } else { + return false; + }; + + const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; + let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); + let msg_restrict_type_further = + format!("consider further restricting type parameter `{}`", param_name); + + if def_id == tcx.lang_items().sized_trait() { + // Type parameters are already `Sized` by default. + err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); + return true; + } + let mut suggest_restrict = |span| { + err.span_suggestion_verbose( + span, + MSG_RESTRICT_BOUND_FURTHER, + format!(" + {}", constraint), + Applicability::MachineApplicable, + ); + }; + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + suggest_restrict(param.span.shrink_to_hi()); + return true; + } + + if generics.where_clause.predicates.is_empty() + // Given `trait Base: Super` where `T: Copy`, suggest restricting in the + // `where` clause instead of `trait Base: Super`. + && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + { + if let Some(bounds_span) = param.bounds_span() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + suggest_restrict(bounds_span.shrink_to_hi()); + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + err.span_suggestion_verbose( + param.span.shrink_to_hi(), + &msg_restrict_type, + format!(": {}", constraint), + Applicability::MachineApplicable, + ); + } + + true + } else { + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, bounded_ty, .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } + } + } + } + } + + let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place(); + // Account for `fn foo(t: T) where T: Foo,` so we don't suggest two trailing commas. + let mut trailing_comma = false; + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) { + trailing_comma = snippet.ends_with(','); + } + let where_clause_span = if trailing_comma { + let hi = where_clause_span.hi(); + Span::new(hi - BytePos(1), hi, where_clause_span.ctxt()) + } else { + where_clause_span.shrink_to_hi() + }; + + match ¶m_spans[..] { + &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + err.span_suggestion_verbose( + where_clause_span, + &msg_restrict_type_further, + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } + } + + true + } +} diff --git a/src/librustc/ty/erase_regions.rs b/src/librustc_middle/ty/erase_regions.rs similarity index 95% rename from src/librustc/ty/erase_regions.rs rename to src/librustc_middle/ty/erase_regions.rs index 4bf08096edeb8..ba165925b6474 100644 --- a/src/librustc/ty/erase_regions.rs +++ b/src/librustc_middle/ty/erase_regions.rs @@ -40,7 +40,7 @@ impl TypeFolder<'tcx> for RegionEraserVisitor<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_local_value() { ty.super_fold_with(self) } else { self.tcx.erase_regions_ty(ty) } + if ty.needs_infer() { ty.super_fold_with(self) } else { self.tcx.erase_regions_ty(ty) } } fn fold_binder(&mut self, t: &ty::Binder) -> ty::Binder diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs new file mode 100644 index 0000000000000..cf63a659e6c0f --- /dev/null +++ b/src/librustc_middle/ty/error.rs @@ -0,0 +1,906 @@ +use crate::traits::{ObligationCause, ObligationCauseCode}; +use crate::ty::diagnostics::suggest_constraining_type_param; +use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt}; +use rustc_ast::ast; +use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; +use rustc_errors::{pluralize, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{BytePos, MultiSpan, Span}; +use rustc_target::spec::abi; + +use std::borrow::Cow; +use std::fmt; +use std::ops::Deref; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] +pub struct ExpectedFound { + pub expected: T, + pub found: T, +} + +impl ExpectedFound { + pub fn new(a_is_expected: bool, a: T, b: T) -> Self { + if a_is_expected { + ExpectedFound { expected: a, found: b } + } else { + ExpectedFound { expected: b, found: a } + } + } +} + +// Data structures used in type unification +#[derive(Clone, Debug, TypeFoldable)] +pub enum TypeError<'tcx> { + Mismatch, + UnsafetyMismatch(ExpectedFound), + AbiMismatch(ExpectedFound), + Mutability, + TupleSize(ExpectedFound), + FixedArraySize(ExpectedFound), + ArgCount, + + RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), + RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>), + RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>), + RegionsPlaceholderMismatch, + + Sorts(ExpectedFound>), + IntMismatch(ExpectedFound), + FloatMismatch(ExpectedFound), + Traits(ExpectedFound), + VariadicMismatch(ExpectedFound), + + /// Instantiating a type variable with the given type would have + /// created a cycle (because it appears somewhere within that + /// type). + CyclicTy(Ty<'tcx>), + ProjectionMismatched(ExpectedFound), + ProjectionBoundsLength(ExpectedFound), + ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), + ObjectUnsafeCoercion(DefId), + ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), + + IntrinsicCast, + /// Safe `#[target_feature]` functions are not assignable to safe function pointers. + TargetFeatureCast(DefId), +} + +pub enum UnconstrainedNumeric { + UnconstrainedFloat, + UnconstrainedInt, + Neither, +} + +/// Explains the source of a type err in a short, human readable way. This is meant to be placed +/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` +/// afterwards to present additional details, particularly when it comes to lifetime-related +/// errors. +impl<'tcx> fmt::Display for TypeError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use self::TypeError::*; + fn report_maybe_different( + f: &mut fmt::Formatter<'_>, + expected: &str, + found: &str, + ) -> fmt::Result { + // A naive approach to making sure that we're not reporting silly errors such as: + // (expected closure, found closure). + if expected == found { + write!(f, "expected {}, found a different {}", expected, found) + } else { + write!(f, "expected {}, found {}", expected, found) + } + } + + let br_string = |br: ty::BoundRegion| match br { + ty::BrNamed(_, name) => format!(" {}", name), + _ => String::new(), + }; + + match *self { + CyclicTy(_) => write!(f, "cyclic type of infinite size"), + Mismatch => write!(f, "types differ"), + UnsafetyMismatch(values) => { + write!(f, "expected {} fn, found {} fn", values.expected, values.found) + } + AbiMismatch(values) => { + write!(f, "expected {} fn, found {} fn", values.expected, values.found) + } + Mutability => write!(f, "types differ in mutability"), + TupleSize(values) => write!( + f, + "expected a tuple with {} element{}, \ + found one with {} element{}", + values.expected, + pluralize!(values.expected), + values.found, + pluralize!(values.found) + ), + FixedArraySize(values) => write!( + f, + "expected an array with a fixed size of {} element{}, \ + found one with {} element{}", + values.expected, + pluralize!(values.expected), + values.found, + pluralize!(values.found) + ), + ArgCount => write!(f, "incorrect number of function parameters"), + RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), + RegionsInsufficientlyPolymorphic(br, _) => write!( + f, + "expected bound lifetime parameter{}, found concrete lifetime", + br_string(br) + ), + RegionsOverlyPolymorphic(br, _) => write!( + f, + "expected concrete lifetime, found bound lifetime parameter{}", + br_string(br) + ), + RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), + Sorts(values) => ty::tls::with(|tcx| { + report_maybe_different( + f, + &values.expected.sort_string(tcx), + &values.found.sort_string(tcx), + ) + }), + Traits(values) => ty::tls::with(|tcx| { + report_maybe_different( + f, + &format!("trait `{}`", tcx.def_path_str(values.expected)), + &format!("trait `{}`", tcx.def_path_str(values.found)), + ) + }), + IntMismatch(ref values) => { + write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) + } + FloatMismatch(ref values) => { + write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) + } + VariadicMismatch(ref values) => write!( + f, + "expected {} fn, found {} function", + if values.expected { "variadic" } else { "non-variadic" }, + if values.found { "variadic" } else { "non-variadic" } + ), + ProjectionMismatched(ref values) => ty::tls::with(|tcx| { + write!( + f, + "expected {}, found {}", + tcx.def_path_str(values.expected), + tcx.def_path_str(values.found) + ) + }), + ProjectionBoundsLength(ref values) => write!( + f, + "expected {} associated type binding{}, found {}", + values.expected, + pluralize!(values.expected), + values.found + ), + ExistentialMismatch(ref values) => report_maybe_different( + f, + &format!("trait `{}`", values.expected), + &format!("trait `{}`", values.found), + ), + ConstMismatch(ref values) => { + write!(f, "expected `{}`, found `{}`", values.expected, values.found) + } + IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"), + TargetFeatureCast(_) => write!( + f, + "cannot coerce functions with `#[target_feature]` to safe function pointers" + ), + ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"), + } + } +} + +impl<'tcx> TypeError<'tcx> { + pub fn must_include_note(&self) -> bool { + use self::TypeError::*; + match self { + CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) + | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) + | TargetFeatureCast(_) => false, + + Mutability + | TupleSize(_) + | ArgCount + | RegionsDoesNotOutlive(..) + | RegionsInsufficientlyPolymorphic(..) + | RegionsOverlyPolymorphic(..) + | RegionsPlaceholderMismatch + | Traits(_) + | ProjectionMismatched(_) + | ProjectionBoundsLength(_) + | ExistentialMismatch(_) + | ConstMismatch(_) + | IntrinsicCast + | ObjectUnsafeCoercion(_) => true, + } + } +} + +impl<'tcx> ty::TyS<'tcx> { + pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { + match self.kind { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { + format!("`{}`", self).into() + } + ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(), + + ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), + ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), + ty::Array(t, n) => { + let n = tcx.lift(&n).unwrap(); + match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { + _ if t.is_simple_ty() => format!("array `{}`", self).into(), + Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(), + None => "array".into(), + } + } + ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(), + ty::Slice(_) => "slice".into(), + ty::RawPtr(_) => "*-ptr".into(), + ty::Ref(_, ty, mutbl) => { + let tymut = ty::TypeAndMut { ty, mutbl }; + let tymut_string = tymut.to_string(); + if tymut_string != "_" + && (ty.is_simple_text() || tymut_string.len() < "mutable reference".len()) + { + format!("`&{}`", tymut_string).into() + } else { + // Unknown type name, it's long or has type arguments + match mutbl { + hir::Mutability::Mut => "mutable reference", + _ => "reference", + } + .into() + } + } + ty::FnDef(..) => "fn item".into(), + ty::FnPtr(_) => "fn pointer".into(), + ty::Dynamic(ref inner, ..) => { + if let Some(principal) = inner.principal() { + format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() + } else { + "trait object".into() + } + } + ty::Closure(..) => "closure".into(), + ty::Generator(..) => "generator".into(), + ty::GeneratorWitness(..) => "generator witness".into(), + ty::Tuple(..) => "tuple".into(), + ty::Infer(ty::TyVar(_)) => "inferred type".into(), + ty::Infer(ty::IntVar(_)) => "integer".into(), + ty::Infer(ty::FloatVar(_)) => "floating-point number".into(), + ty::Placeholder(..) => "placeholder type".into(), + ty::Bound(..) => "bound type".into(), + ty::Infer(ty::FreshTy(_)) => "fresh type".into(), + ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), + ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), + ty::Projection(_) => "associated type".into(), + ty::Param(p) => format!("type parameter `{}`", p).into(), + ty::Opaque(..) => "opaque type".into(), + ty::Error => "type error".into(), + } + } + + pub fn prefix_string(&self) -> Cow<'static, str> { + match self.kind { + ty::Infer(_) + | ty::Error + | ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Never => "type".into(), + ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(), + ty::Adt(def, _) => def.descr().into(), + ty::Foreign(_) => "extern type".into(), + ty::Array(..) => "array".into(), + ty::Slice(_) => "slice".into(), + ty::RawPtr(_) => "raw pointer".into(), + ty::Ref(.., mutbl) => match mutbl { + hir::Mutability::Mut => "mutable reference", + _ => "reference", + } + .into(), + ty::FnDef(..) => "fn item".into(), + ty::FnPtr(_) => "fn pointer".into(), + ty::Dynamic(..) => "trait object".into(), + ty::Closure(..) => "closure".into(), + ty::Generator(..) => "generator".into(), + ty::GeneratorWitness(..) => "generator witness".into(), + ty::Tuple(..) => "tuple".into(), + ty::Placeholder(..) => "higher-ranked type".into(), + ty::Bound(..) => "bound type variable".into(), + ty::Projection(_) => "associated type".into(), + ty::Param(_) => "type parameter".into(), + ty::Opaque(..) => "opaque type".into(), + } + } +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn note_and_explain_type_err( + self, + db: &mut DiagnosticBuilder<'_>, + err: &TypeError<'tcx>, + cause: &ObligationCause<'tcx>, + sp: Span, + body_owner_def_id: DefId, + ) { + use self::TypeError::*; + debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); + match err { + Sorts(values) => { + let expected_str = values.expected.sort_string(self); + let found_str = values.found.sort_string(self); + if expected_str == found_str && expected_str == "closure" { + db.note("no two closures, even if identical, have the same type"); + db.help("consider boxing your closure and/or using it as a trait object"); + } + if expected_str == found_str && expected_str == "opaque type" { + // Issue #63167 + db.note("distinct uses of `impl Trait` result in different opaque types"); + let e_str = values.expected.to_string(); + let f_str = values.found.to_string(); + if e_str == f_str && &e_str == "impl std::future::Future" { + // FIXME: use non-string based check. + db.help( + "if both `Future`s have the same `Output` type, consider \ + `.await`ing on both of them", + ); + } + } + match (&values.expected.kind, &values.found.kind) { + (ty::Float(_), ty::Infer(ty::IntVar(_))) => { + if let Ok( + // Issue #53280 + snippet, + ) = self.sess.source_map().span_to_snippet(sp) + { + if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { + db.span_suggestion( + sp, + "use a float literal", + format!("{}.0", snippet), + MachineApplicable, + ); + } + } + } + (ty::Param(expected), ty::Param(found)) => { + let generics = self.generics_of(body_owner_def_id); + let e_span = self.def_span(generics.type_param(expected, self).def_id); + if !sp.contains(e_span) { + db.span_label(e_span, "expected type parameter"); + } + let f_span = self.def_span(generics.type_param(found, self).def_id); + if !sp.contains(f_span) { + db.span_label(f_span, "found type parameter"); + } + db.note( + "a type parameter was expected, but a different one was found; \ + you might be missing a type parameter or trait bound", + ); + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Projection(_), ty::Projection(_)) => { + db.note("an associated type was expected, but a different one was found"); + } + (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + let hir = self.hir(); + let mut note = true; + if let Some(generics) = generics + .type_param(p, self) + .def_id + .as_local() + .map(|id| hir.as_local_hir_id(id)) + .and_then(|id| self.hir().find(self.hir().get_parent_node(id))) + .as_ref() + .and_then(|node| node.generics()) + { + // Synthesize the associated type restriction `Add`. + // FIXME: extract this logic for use in other diagnostics. + let trait_ref = proj.trait_ref(self); + let path = + self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); + let item_name = self.item_name(proj.item_def_id); + let path = if path.ends_with('>') { + format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p) + } else { + format!("{}<{} = {}>", path, item_name, p) + }; + note = !suggest_constraining_type_param( + self, + generics, + db, + &format!("{}", proj.self_ty()), + &path, + None, + ); + } + if note { + db.note("you might be missing a type parameter or trait bound"); + } + } + (ty::Param(p), ty::Dynamic(..) | ty::Opaque(..)) + | (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + db.help("type parameters must be constrained to match other types"); + if self.sess.teach(&db.get_code().unwrap()) { + db.help( + "given a type parameter `T` and a method `foo`: +``` +trait Trait { fn foo(&self) -> T; } +``` +the only ways to implement method `foo` are: +- constrain `T` with an explicit type: +``` +impl Trait for X { + fn foo(&self) -> String { String::new() } +} +``` +- add a trait bound to `T` and call a method on that trait that returns `Self`: +``` +impl Trait for X { + fn foo(&self) -> T { ::default() } +} +``` +- change `foo` to return an argument of type `T`: +``` +impl Trait for X { + fn foo(&self, x: T) -> T { x } +} +```", + ); + } + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Param(p), _) | (_, ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + } + (ty::Projection(proj_ty), _) => { + self.expected_projection( + db, + proj_ty, + values, + body_owner_def_id, + &cause.code, + ); + } + (_, ty::Projection(proj_ty)) => { + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.found, values.expected, + ); + if !self.suggest_constraint( + db, + &msg, + body_owner_def_id, + proj_ty, + values.expected, + ) { + db.help(&msg); + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + } + _ => {} + } + debug!( + "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", + values.expected, values.expected.kind, values.found, values.found.kind, + ); + } + CyclicTy(ty) => { + // Watch out for various cases of cyclic types and try to explain. + if ty.is_closure() || ty.is_generator() { + db.note( + "closures cannot capture themselves or take themselves as argument;\n\ + this error may be the result of a recent compiler bug-fix,\n\ + see issue #46062 \n\ + for more information", + ); + } + } + TargetFeatureCast(def_id) => { + let attrs = self.get_attrs(*def_id); + let target_spans = attrs + .deref() + .iter() + .filter(|attr| attr.has_name(sym::target_feature)) + .map(|attr| attr.span); + db.note( + "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" + ); + db.span_labels(target_spans, "`#[target_feature]` added here"); + } + _ => {} + } + } + + fn suggest_constraint( + &self, + db: &mut DiagnosticBuilder<'_>, + msg: &str, + body_owner_def_id: DefId, + proj_ty: &ty::ProjectionTy<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let assoc = self.associated_item(proj_ty.item_def_id); + let trait_ref = proj_ty.trait_ref(*self); + if let Some(item) = self.hir().get_if_local(body_owner_def_id) { + if let Some(hir_generics) = item.generics() { + // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. + // This will also work for `impl Trait`. + let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind { + let generics = self.generics_of(body_owner_def_id); + generics.type_param(¶m_ty, *self).def_id + } else { + return false; + }; + + // First look in the `where` clause, as this might be + // `fn foo(x: T) where T: Trait`. + for predicate in hir_generics.where_clause.predicates { + if let hir::WherePredicate::BoundPredicate(pred) = predicate { + if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = + pred.bounded_ty.kind + { + if path.res.opt_def_id() == Some(def_id) { + // This predicate is binding type param `A` in `::Foo` to + // something, potentially `T`. + } else { + continue; + } + } else { + continue; + } + + if self.constrain_generic_bound_associated_type_structured_suggestion( + db, + &trait_ref, + pred.bounds, + &assoc, + ty, + msg, + ) { + return true; + } + } + } + for param in hir_generics.params { + if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id()) + == Some(def_id) + { + // This is type param `A` in `::Foo`. + return self.constrain_generic_bound_associated_type_structured_suggestion( + db, + &trait_ref, + param.bounds, + &assoc, + ty, + msg, + ); + } + } + } + } + false + } + + /// An associated type was expected and a different type was found. + /// + /// We perform a few different checks to see what we can suggest: + /// + /// - In the current item, look for associated functions that return the expected type and + /// suggest calling them. (Not a structured suggestion.) + /// - If any of the item's generic bounds can be constrained, we suggest constraining the + /// associated type to the found type. + /// - If the associated type has a default type and was expected inside of a `trait`, we + /// mention that this is disallowed. + /// - If all other things fail, and the error is not because of a mismatch between the `trait` + /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc + /// fn that returns the type. + fn expected_projection( + &self, + db: &mut DiagnosticBuilder<'_>, + proj_ty: &ty::ProjectionTy<'tcx>, + values: &ExpectedFound>, + body_owner_def_id: DefId, + cause_code: &ObligationCauseCode<'_>, + ) { + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.expected, values.found + ); + let body_owner = self.hir().get_if_local(body_owner_def_id); + let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); + + // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. + let callable_scope = match body_owner { + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), + ) => true, + _ => false, + }; + let impl_comparison = matches!( + cause_code, + ObligationCauseCode::CompareImplMethodObligation { .. } + | ObligationCauseCode::CompareImplTypeObligation { .. } + | ObligationCauseCode::CompareImplConstObligation + ); + let assoc = self.associated_item(proj_ty.item_def_id); + if !callable_scope || impl_comparison { + // We do not want to suggest calling functions when the reason of the + // type error is a comparison of an `impl` with its `trait` or when the + // scope is outside of a `Body`. + } else { + // If we find a suitable associated function that returns the expected type, we don't + // want the more general suggestion later in this method about "consider constraining + // the associated type or calling a method that returns the associated type". + let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( + db, + assoc.container.id(), + current_method_ident, + proj_ty.item_def_id, + values.expected, + ); + // Possibly suggest constraining the associated type to conform to the + // found type. + if self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found) + || point_at_assoc_fn + { + return; + } + } + + if let ty::Opaque(def_id, _) = proj_ty.self_ty().kind { + // When the expected `impl Trait` is not defined in the current item, it will come from + // a return type. This can occur when dealing with `TryStream` (#71035). + if self.constrain_associated_type_structured_suggestion( + db, + self.def_span(def_id), + &assoc, + values.found, + &msg, + ) { + return; + } + } + + if self.point_at_associated_type(db, body_owner_def_id, values.found) { + return; + } + + if !impl_comparison { + // Generic suggestion when we can't be more specific. + if callable_scope { + db.help(&format!("{} or calling a method that returns `{}`", msg, values.expected)); + } else { + db.help(&msg); + } + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + if self.sess.teach(&db.get_code().unwrap()) { + db.help( + "given an associated type `T` and a method `foo`: +``` +trait Trait { +type T; +fn foo(&self) -> Self::T; +} +``` +the only way of implementing method `foo` is to constrain `T` with an explicit associated type: +``` +impl Trait for X { +type T = String; +fn foo(&self) -> Self::T { String::new() } +} +```", + ); + } + } + + fn point_at_methods_that_satisfy_associated_type( + &self, + db: &mut DiagnosticBuilder<'_>, + assoc_container_id: DefId, + current_method_ident: Option, + proj_ty_item_def_id: DefId, + expected: Ty<'tcx>, + ) -> bool { + let items = self.associated_items(assoc_container_id); + // Find all the methods in the trait that could be called to construct the + // expected associated type. + // FIXME: consider suggesting the use of associated `const`s. + let methods: Vec<(Span, String)> = items + .items + .iter() + .filter(|(name, item)| { + ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident + }) + .filter_map(|(_, item)| { + let method = self.fn_sig(item.def_id); + match method.output().skip_binder().kind { + ty::Projection(ty::ProjectionTy { item_def_id, .. }) + if item_def_id == proj_ty_item_def_id => + { + Some(( + self.sess.source_map().guess_head_span(self.def_span(item.def_id)), + format!("consider calling `{}`", self.def_path_str(item.def_id)), + )) + } + _ => None, + } + }) + .collect(); + if !methods.is_empty() { + // Use a single `help:` to show all the methods in the trait that can + // be used to construct the expected associated type. + let mut span: MultiSpan = + methods.iter().map(|(sp, _)| *sp).collect::>().into(); + let msg = format!( + "{some} method{s} {are} available that return{r} `{ty}`", + some = if methods.len() == 1 { "a" } else { "some" }, + s = pluralize!(methods.len()), + are = if methods.len() == 1 { "is" } else { "are" }, + r = if methods.len() == 1 { "s" } else { "" }, + ty = expected + ); + for (sp, label) in methods.into_iter() { + span.push_span_label(sp, label); + } + db.span_help(span, &msg); + return true; + } + false + } + + fn point_at_associated_type( + &self, + db: &mut DiagnosticBuilder<'_>, + body_owner_def_id: DefId, + found: Ty<'tcx>, + ) -> bool { + let hir_id = match body_owner_def_id.as_local().map(|id| self.hir().as_local_hir_id(id)) { + Some(hir_id) => hir_id, + None => return false, + }; + // When `body_owner` is an `impl` or `trait` item, look in its associated types for + // `expected` and point at it. + let parent_id = self.hir().get_parent_item(hir_id); + let item = self.hir().find(parent_id); + debug!("expected_projection parent item {:?}", item); + match item { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { + // FIXME: account for `#![feature(specialization)]` + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + if let hir::Defaultness::Default { has_value: true } = + item.defaultness + { + db.span_label( + item.span, + "associated type defaults can't be assumed inside the \ + trait defining them", + ); + } else { + db.span_label(item.span, "expected this associated type"); + } + return true; + } + } + _ => {} + } + } + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { items, .. }, .. + })) => { + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + db.span_label(item.span, "expected this associated type"); + return true; + } + } + _ => {} + } + } + } + _ => {} + } + false + } + + /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` + /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. + fn constrain_generic_bound_associated_type_structured_suggestion( + &self, + db: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::TraitRef<'tcx>, + bounds: hir::GenericBounds<'_>, + assoc: &ty::AssocItem, + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. + bounds.iter().any(|bound| match bound { + hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { + // Relate the type param against `T` in `::Foo`. + ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) + && self.constrain_associated_type_structured_suggestion( + db, ptr.span, assoc, ty, msg, + ) + } + _ => false, + }) + } + + /// Given a span corresponding to a bound, provide a structured suggestion to set an + /// associated type to a given type `ty`. + fn constrain_associated_type_structured_suggestion( + &self, + db: &mut DiagnosticBuilder<'_>, + span: Span, + assoc: &ty::AssocItem, + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + if let Ok(has_params) = + self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) + { + let (span, sugg) = if has_params { + let pos = span.hi() - BytePos(1); + let span = Span::new(pos, pos, span.ctxt()); + (span, format!(", {} = {}", assoc.ident, ty)) + } else { + (span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty)) + }; + db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); + return true; + } + false + } +} diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc_middle/ty/fast_reject.rs similarity index 98% rename from src/librustc/ty/fast_reject.rs rename to src/librustc_middle/ty/fast_reject.rs index 2a937d6581d6a..16d8e37940763 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc_middle/ty/fast_reject.rs @@ -90,7 +90,6 @@ pub fn simplify_type( ty::Never => Some(NeverSimplifiedType), ty::Tuple(ref tys) => Some(TupleSimplifiedType(tys.len())), ty::FnPtr(ref f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())), - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), ty::Projection(_) | ty::Param(_) => { if can_simplify_params { // In normalized types, projections don't unify with diff --git a/src/librustc/ty/flags.rs b/src/librustc_middle/ty/flags.rs similarity index 86% rename from src/librustc/ty/flags.rs rename to src/librustc_middle/ty/flags.rs index 5243e1fbf579b..042ffc4d1e550 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc_middle/ty/flags.rs @@ -1,4 +1,4 @@ -use crate::ty::subst::{GenericArgKind, SubstsRef}; +use crate::ty::subst::{GenericArg, GenericArgKind}; use crate::ty::{self, InferConst, Ty, TypeFlags}; #[derive(Debug)] @@ -28,7 +28,7 @@ impl FlagComputation { } fn add_flags(&mut self, flags: TypeFlags) { - self.flags = self.flags | (flags & TypeFlags::NOMINAL_FLAGS); + self.flags = self.flags | flags; } /// indicates that `self` refers to something at binding level `binder` @@ -70,17 +70,11 @@ impl FlagComputation { | &ty::Str | &ty::Foreign(..) => {} - // You might think that we could just return Error for - // any type containing Error as a component, and get - // rid of the TypeFlags::HAS_TY_ERR flag -- likewise for ty_bot (with - // the exception of function types that return bot). - // But doing so caused sporadic memory corruption, and - // neither I (tjc) nor nmatsakis could figure out why, - // so we're doing it this way. - &ty::Error => self.add_flags(TypeFlags::HAS_TY_ERR), + &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::Generator(_, ref substs, _) => { @@ -99,19 +93,21 @@ impl FlagComputation { &ty::Bound(debruijn, _) => { self.add_binder(debruijn); + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } &ty::Placeholder(..) => { self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } &ty::Infer(infer) => { - self.add_flags(TypeFlags::HAS_TY_INFER); + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); match infer { ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {} ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) => { - self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX) + self.add_flags(TypeFlags::HAS_TY_INFER) } } } @@ -125,11 +121,6 @@ impl FlagComputation { self.add_projection_ty(data); } - &ty::UnnormalizedProjection(ref data) => { - self.add_flags(TypeFlags::HAS_TY_PROJECTION); - self.add_projection_ty(data); - } - &ty::Opaque(_, substs) => { self.add_flags(TypeFlags::HAS_TY_OPAQUE); self.add_substs(substs); @@ -217,20 +208,26 @@ impl FlagComputation { self.add_flags(TypeFlags::HAS_CT_PROJECTION); } ty::ConstKind::Infer(infer) => { - self.add_flags(TypeFlags::HAS_CT_INFER); + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); match infer { InferConst::Fresh(_) => {} - InferConst::Var(_) => self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX), + InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER), } } - ty::ConstKind::Bound(debruijn, _) => self.add_binder(debruijn), + ty::ConstKind::Bound(debruijn, _) => { + self.add_binder(debruijn); + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); + } 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(_) => {} + ty::ConstKind::Error => self.add_flags(TypeFlags::HAS_ERROR), } } @@ -243,7 +240,7 @@ impl FlagComputation { self.add_substs(projection_ty.substs); } - fn add_substs(&mut self, substs: SubstsRef<'_>) { + fn add_substs(&mut self, substs: &[GenericArg<'_>]) { for kind in substs { match kind.unpack() { GenericArgKind::Type(ty) => self.add_ty(ty), diff --git a/src/librustc/ty/fold.rs b/src/librustc_middle/ty/fold.rs similarity index 97% rename from src/librustc/ty/fold.rs rename to src/librustc_middle/ty/fold.rs index 4adca6c7d9772..248dd00ef47be 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc_middle/ty/fold.rs @@ -82,9 +82,9 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } fn references_error(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_ERR) + self.has_type_flags(TypeFlags::HAS_ERROR) } - fn has_param_types(&self) -> bool { + fn has_param_types_or_consts(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM) } fn has_infer_types(&self) -> bool { @@ -96,9 +96,6 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn has_infer_consts(&self) -> bool { self.has_type_flags(TypeFlags::HAS_CT_INFER) } - fn has_local_value(&self) -> bool { - self.has_type_flags(TypeFlags::KEEP_IN_LOCAL_TCX) - } fn needs_infer(&self) -> bool { self.has_type_flags(TypeFlags::NEEDS_INFER) } @@ -142,6 +139,13 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND) } + /// Indicates whether this value still has parameters/placeholders/inference variables + /// which could be replaced later, in a way that would change the results of `impl` + /// specialization. + fn still_further_specializable(&self) -> bool { + self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) + } + /// A visitor that does not recurse into types, works like `fn walk_shallow` in `Ty`. fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> bool) -> bool { pub struct Visitor(F); @@ -256,20 +260,6 @@ where // Region folder impl<'tcx> TyCtxt<'tcx> { - /// Collects the free and escaping regions in `value` into `region_set`. Returns - /// whether any late-bound regions were skipped - pub fn collect_regions(self, value: &T, region_set: &mut FxHashSet>) -> bool - where - T: TypeFoldable<'tcx>, - { - let mut have_bound_regions = false; - self.fold_regions(value, &mut have_bound_regions, |r, d| { - region_set.insert(self.mk_region(r.shifted_out_to_binder(d))); - r - }); - have_bound_regions - } - /// Folds the escaping and free regions in `value` using `f`, and /// sets `skipped_regions` to true if any late-bound region was found /// and skipped. @@ -978,17 +968,27 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { // ignore the inputs to a projection, as they may not appear // in the normalized form if self.just_constrained { - match t.kind { - ty::Projection(..) | ty::Opaque(..) => { - return false; - } - _ => {} + if let ty::Projection(..) | ty::Opaque(..) = t.kind { + return false; } } t.super_visit_with(self) } + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + // if we are only looking for "constrained" region, we have to + // ignore the inputs of an unevaluated const, as they may not appear + // in the normalized form + if self.just_constrained { + if let ty::ConstKind::Unevaluated(..) = c.val { + return false; + } + } + + c.super_visit_with(self) + } + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { if let ty::ReLateBound(debruijn, br) = *r { if debruijn == self.current_index { diff --git a/src/librustc/ty/inhabitedness/def_id_forest.rs b/src/librustc_middle/ty/inhabitedness/def_id_forest.rs similarity index 98% rename from src/librustc/ty/inhabitedness/def_id_forest.rs rename to src/librustc_middle/ty/inhabitedness/def_id_forest.rs index 14ead77653c32..ee6b06a1cc803 100644 --- a/src/librustc/ty/inhabitedness/def_id_forest.rs +++ b/src/librustc_middle/ty/inhabitedness/def_id_forest.rs @@ -32,7 +32,7 @@ impl<'tcx> DefIdForest { #[inline] pub fn full(tcx: TyCtxt<'tcx>) -> DefIdForest { let crate_id = tcx.hir().local_def_id(CRATE_HIR_ID); - DefIdForest::from_id(crate_id) + DefIdForest::from_id(crate_id.to_def_id()) } /// Creates a forest containing a `DefId` and all its descendants. diff --git a/src/librustc_middle/ty/inhabitedness/mod.rs b/src/librustc_middle/ty/inhabitedness/mod.rs new file mode 100644 index 0000000000000..d1b5eed921bb3 --- /dev/null +++ b/src/librustc_middle/ty/inhabitedness/mod.rs @@ -0,0 +1,228 @@ +pub use self::def_id_forest::DefIdForest; + +use crate::ty; +use crate::ty::context::TyCtxt; +use crate::ty::TyKind::*; +use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef}; +use crate::ty::{AdtKind, Visibility}; +use crate::ty::{DefId, SubstsRef}; +use rustc_data_structures::stack::ensure_sufficient_stack; + +mod def_id_forest; + +// The methods in this module calculate `DefIdForest`s of modules in which a +// `AdtDef`/`VariantDef`/`FieldDef` is visibly uninhabited. +// +// # Example +// ```rust +// enum Void {} +// mod a { +// pub mod b { +// pub struct SecretlyUninhabited { +// _priv: !, +// } +// } +// } +// +// mod c { +// pub struct AlsoSecretlyUninhabited { +// _priv: Void, +// } +// mod d { +// } +// } +// +// struct Foo { +// x: a::b::SecretlyUninhabited, +// y: c::AlsoSecretlyUninhabited, +// } +// ``` +// In this code, the type `Foo` will only be visibly uninhabited inside the +// modules `b`, `c` and `d`. Calling `uninhabited_from` on `Foo` or its `AdtDef` will +// return the forest of modules {`b`, `c`->`d`} (represented in a `DefIdForest` by the +// set {`b`, `c`}). +// +// We need this information for pattern-matching on `Foo` or types that contain +// `Foo`. +// +// # Example +// ```rust +// let foo_result: Result = ... ; +// let Ok(t) = foo_result; +// ``` +// This code should only compile in modules where the uninhabitedness of `Foo` is +// visible. + +impl<'tcx> TyCtxt<'tcx> { + /// Checks whether a type is visibly uninhabited from a particular module. + /// + /// # Example + /// ```rust + /// enum Void {} + /// mod a { + /// pub mod b { + /// pub struct SecretlyUninhabited { + /// _priv: !, + /// } + /// } + /// } + /// + /// mod c { + /// pub struct AlsoSecretlyUninhabited { + /// _priv: Void, + /// } + /// mod d { + /// } + /// } + /// + /// struct Foo { + /// x: a::b::SecretlyUninhabited, + /// y: c::AlsoSecretlyUninhabited, + /// } + /// ``` + /// In this code, the type `Foo` will only be visibly uninhabited inside the + /// modules b, c and d. This effects pattern-matching on `Foo` or types that + /// contain `Foo`. + /// + /// # Example + /// ```rust + /// let foo_result: Result = ... ; + /// let Ok(t) = foo_result; + /// ``` + /// This code should only compile in modules where the uninhabitedness of Foo is + /// visible. + pub fn is_ty_uninhabited_from( + self, + module: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + // To check whether this type is uninhabited at all (not just from the + // given node), you could check whether the forest is empty. + // ``` + // forest.is_empty() + // ``` + ty.uninhabited_from(self, param_env).contains(self, module) + } + + pub fn is_ty_uninhabited_from_any_module( + self, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + !ty.uninhabited_from(self, param_env).is_empty() + } +} + +impl<'tcx> AdtDef { + /// Calculates the forest of `DefId`s from which this ADT is visibly uninhabited. + fn uninhabited_from( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> DefIdForest { + // Non-exhaustive ADTs from other crates are always considered inhabited. + if self.is_variant_list_non_exhaustive() && !self.did.is_local() { + DefIdForest::empty() + } else { + DefIdForest::intersection( + tcx, + self.variants + .iter() + .map(|v| v.uninhabited_from(tcx, substs, self.adt_kind(), param_env)), + ) + } + } +} + +impl<'tcx> VariantDef { + /// Calculates the forest of `DefId`s from which this variant is visibly uninhabited. + pub fn uninhabited_from( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + adt_kind: AdtKind, + param_env: ty::ParamEnv<'tcx>, + ) -> DefIdForest { + let is_enum = match adt_kind { + // For now, `union`s are never considered uninhabited. + // The precise semantics of inhabitedness with respect to unions is currently undecided. + AdtKind::Union => return DefIdForest::empty(), + AdtKind::Enum => true, + AdtKind::Struct => false, + }; + // Non-exhaustive variants from other crates are always considered inhabited. + if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { + DefIdForest::empty() + } else { + DefIdForest::union( + tcx, + self.fields.iter().map(|f| f.uninhabited_from(tcx, substs, is_enum, param_env)), + ) + } + } +} + +impl<'tcx> FieldDef { + /// Calculates the forest of `DefId`s from which this field is visibly uninhabited. + fn uninhabited_from( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + is_enum: bool, + param_env: ty::ParamEnv<'tcx>, + ) -> DefIdForest { + let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx, param_env); + // FIXME(canndrew): Currently enum fields are (incorrectly) stored with + // `Visibility::Invisible` so we need to override `self.vis` if we're + // dealing with an enum. + if is_enum { + data_uninhabitedness() + } else { + match self.vis { + Visibility::Invisible => DefIdForest::empty(), + Visibility::Restricted(from) => { + let forest = DefIdForest::from_id(from); + let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness())); + DefIdForest::intersection(tcx, iter) + } + Visibility::Public => data_uninhabitedness(), + } + } + } +} + +impl<'tcx> TyS<'tcx> { + /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. + fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest { + match self.kind { + Adt(def, substs) => { + ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env)) + } + + Never => DefIdForest::full(tcx), + + Tuple(ref tys) => DefIdForest::union( + tcx, + tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)), + ), + + Array(ty, len) => match len.try_eval_usize(tcx, param_env) { + // If the array is definitely non-empty, it's uninhabited if + // the type of its elements is uninhabited. + Some(n) if n != 0 => ty.uninhabited_from(tcx, param_env), + _ => DefIdForest::empty(), + }, + + // References to uninitialised memory is valid for any type, including + // uninhabited types, in unsafe code, so we treat all references as + // inhabited. + // The precise semantics of inhabitedness with respect to references is currently + // undecided. + Ref(..) => DefIdForest::empty(), + + _ => DefIdForest::empty(), + } + } +} diff --git a/src/librustc/ty/instance.rs b/src/librustc_middle/ty/instance.rs similarity index 91% rename from src/librustc/ty/instance.rs rename to src/librustc_middle/ty/instance.rs index ac49feaf8c2bf..1ce079821a22e 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc_middle/ty/instance.rs @@ -1,10 +1,10 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::middle::lang_items::DropInPlaceFnLangItem; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable}; -use rustc_data_structures::AtomicRef; +use rustc_errors::ErrorReported; use rustc_hir::def::Namespace; use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::lang_items::{DropInPlaceFnLangItem, FnOnceTraitLangItem}; use rustc_macros::HashStable; use std::fmt; @@ -92,7 +92,7 @@ impl<'tcx> Instance<'tcx> { // There shouldn't be any params - if there are, then // Instance.ty_env should have been used to provide the proper // ParamEnv - if self.substs.has_param_types() { + if self.substs.has_param_types_or_consts() { bug!("Instance.ty called for type {:?} with params in substs: {:?}", ty, self.substs); } tcx.subst_and_normalize_erasing_regions(self.substs, ty::ParamEnv::reveal_all(), &ty) @@ -166,7 +166,7 @@ impl<'tcx> InstanceDef<'tcx> { /// Note that this is only a hint. See the documentation for /// `generates_cgu_internal_copy` for more information. pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool { - use crate::hir::map::DefPathData; + use rustc_hir::definitions::DefPathData; let def_id = match *self { ty::InstanceDef::Item(def_id) => def_id, ty::InstanceDef::DropGlue(_, Some(_)) => return false, @@ -269,27 +269,41 @@ impl<'tcx> Instance<'tcx> { /// this is used to find the precise code that will run for a trait method invocation, /// if known. /// - /// Returns `None` if we cannot resolve `Instance` to a specific instance. + /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance. /// For example, in a context like this, /// /// ``` /// fn foo(t: T) { ... } /// ``` /// - /// trying to resolve `Debug::fmt` applied to `T` will yield `None`, because we do not + /// trying to resolve `Debug::fmt` applied to `T` will yield `Ok(None)`, because we do not /// know what code ought to run. (Note that this setting is also affected by the /// `RevealMode` in the parameter environment.) /// /// Presuming that coherence and type-check have succeeded, if this method is invoked /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return - /// `Some`. + /// `Ok(Some(instance))`. + /// + /// Returns `Err(ErrorReported)` when the `Instance` resolution process + /// couldn't complete due to errors elsewhere - this is distinct + /// from `Ok(None)` to avoid misleading diagnostics when an error + /// has already been/will be emitted, for the original cause pub fn resolve( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>, - ) -> Option> { - (*RESOLVE_INSTANCE)(tcx, param_env, def_id, substs) + ) -> Result>, ErrorReported> { + // All regions in the result of this query are erased, so it's + // fine to erase all of the input regions. + + // HACK(eddyb) erase regions in `substs` first, so that `param_env.and(...)` + // below is more likely to ignore the bounds in scope (e.g. if the only + // generic parameters mentioned by `substs` were lifetime ones). + let substs = tcx.erase_regions(&substs); + + // FIXME(eddyb) should this always use `param_env.with_reveal_all()`? + tcx.resolve_instance(tcx.erase_regions(¶m_env.and((def_id, substs)))) } pub fn resolve_for_fn_ptr( @@ -299,7 +313,7 @@ impl<'tcx> Instance<'tcx> { substs: SubstsRef<'tcx>, ) -> Option> { debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); - Instance::resolve(tcx, param_env, def_id, substs).map(|mut resolved| { + Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| { match resolved.def { InstanceDef::Item(def_id) if resolved.def.requires_caller_location(tcx) => { debug!(" => fn pointer created for function with #[track_caller]"); @@ -331,7 +345,7 @@ impl<'tcx> Instance<'tcx> { debug!(" => associated item with unsizeable self: Self"); Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) } else { - Instance::resolve(tcx, param_env, def_id, substs) + Instance::resolve(tcx, param_env, def_id, substs).ok().flatten() } } @@ -352,7 +366,7 @@ impl<'tcx> Instance<'tcx> { pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { let def_id = tcx.require_lang_item(DropInPlaceFnLangItem, None); let substs = tcx.intern_substs(&[ty.into()]); - Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap() + Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() } pub fn fn_once_adapter_instance( @@ -361,11 +375,11 @@ impl<'tcx> Instance<'tcx> { substs: ty::SubstsRef<'tcx>, ) -> Instance<'tcx> { debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, substs); - let fn_once = tcx.lang_items().fn_once_trait().unwrap(); + let fn_once = tcx.require_lang_item(FnOnceTraitLangItem, None); let call_once = tcx .associated_items(fn_once) .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Method) + .find(|it| it.kind == ty::AssocKind::Fn) .unwrap() .def_id; let def = ty::InstanceDef::ClosureOnceShim { call_once }; @@ -425,8 +439,7 @@ fn needs_fn_once_adapter_shim( // basically the same thing, so we can just return llfn. Ok(false) } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) - | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + (ty::ClosureKind::Fn | ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut // self, ...)`. We want a `fn(self, ...)`. We can produce // this by doing something like: @@ -437,24 +450,6 @@ fn needs_fn_once_adapter_shim( // These are both the same at codegen time. Ok(true) } - (ty::ClosureKind::FnMut, _) | (ty::ClosureKind::FnOnce, _) => Err(()), + (ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()), } } - -fn resolve_instance_default( - _tcx: TyCtxt<'tcx>, - _param_env: ty::ParamEnv<'tcx>, - _def_id: DefId, - _substs: SubstsRef<'tcx>, -) -> Option> { - unimplemented!() -} - -pub static RESOLVE_INSTANCE: AtomicRef< - for<'tcx> fn( - TyCtxt<'tcx>, - ty::ParamEnv<'tcx>, - DefId, - SubstsRef<'tcx>, - ) -> Option>, -> = AtomicRef::new(&(resolve_instance_default as _)); diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs new file mode 100644 index 0000000000000..2d49d85c4df54 --- /dev/null +++ b/src/librustc_middle/ty/layout.rs @@ -0,0 +1,2773 @@ +use crate::ich::StableHashingContext; +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; +use crate::ty::subst::Subst; +use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable}; + +use rustc_ast::ast::{self, IntTy, UintTy}; +use rustc_attr as attr; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir as hir; +use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::DUMMY_SP; +use rustc_target::abi::call::{ + ArgAbi, ArgAttribute, ArgAttributes, Conv, FnAbi, PassMode, Reg, RegKind, +}; +use rustc_target::abi::*; +use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy}; + +use std::cmp; +use std::fmt; +use std::iter; +use std::mem; +use std::num::NonZeroUsize; +use std::ops::Bound; + +pub trait IntegerExt { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>; + fn from_attr(cx: &C, ity: attr::IntType) -> Integer; + fn repr_discr<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool); +} + +impl IntegerExt for Integer { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> { + match (*self, signed) { + (I8, false) => tcx.types.u8, + (I16, false) => tcx.types.u16, + (I32, false) => tcx.types.u32, + (I64, false) => tcx.types.u64, + (I128, false) => tcx.types.u128, + (I8, true) => tcx.types.i8, + (I16, true) => tcx.types.i16, + (I32, true) => tcx.types.i32, + (I64, true) => tcx.types.i64, + (I128, true) => tcx.types.i128, + } + } + + /// Gets the Integer type from an attr::IntType. + fn from_attr(cx: &C, ity: attr::IntType) -> Integer { + let dl = cx.data_layout(); + + match ity { + attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8, + attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16, + attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32, + attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64, + attr::SignedInt(IntTy::I128) | attr::UnsignedInt(UintTy::U128) => I128, + attr::SignedInt(IntTy::Isize) | attr::UnsignedInt(UintTy::Usize) => { + dl.ptr_sized_integer() + } + } + } + + /// Finds the appropriate Integer type and signedness for the given + /// signed discriminant range and `#[repr]` attribute. + /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but + /// that shouldn't affect anything, other than maybe debuginfo. + fn repr_discr<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool) { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); + let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + let mut min_from_extern = None; + let min_default = I8; + + if let Some(ity) = repr.int { + let discr = Integer::from_attr(&tcx, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + bug!( + "Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum `{}", + ty + ) + } + return (discr, ity.is_signed()); + } + + if repr.c() { + match &tcx.sess.target.target.arch[..] { + // WARNING: the ARM EABI has two variants; the one corresponding + // to `at_least == I32` appears to be used on Linux and NetBSD, + // but some systems may use the variant corresponding to no + // lower bound. However, we don't run on those yet...? + "arm" => min_from_extern = Some(I32), + _ => min_from_extern = Some(I32), + } + } + + let at_least = min_from_extern.unwrap_or(min_default); + + // If there are no negative values, we can use the unsigned fit. + if min >= 0 { + (cmp::max(unsigned_fit, at_least), false) + } else { + (cmp::max(signed_fit, at_least), true) + } + } +} + +pub trait PrimitiveExt { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; + fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; +} + +impl PrimitiveExt for Primitive { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + Int(i, signed) => i.to_ty(tcx, signed), + F32 => tcx.types.f32, + F64 => tcx.types.f64, + Pointer => tcx.mk_mut_ptr(tcx.mk_unit()), + } + } + + /// Return an *integer* type matching this primitive. + /// Useful in particular when dealing with enum discriminants. + fn to_int_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + Int(i, signed) => i.to_ty(tcx, signed), + Pointer => tcx.types.usize, + F32 | F64 => bug!("floats do not have an int type"), + } + } +} + +/// The first half of a fat pointer. +/// +/// - For a trait object, this is the address of the box. +/// - For a slice, this is the base address. +pub const FAT_PTR_ADDR: usize = 0; + +/// The second half of a fat pointer. +/// +/// - For a trait object, this is the address of the vtable. +/// - For a slice, this is the length. +pub const FAT_PTR_EXTRA: usize = 1; + +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum LayoutError<'tcx> { + Unknown(Ty<'tcx>), + SizeOverflow(Ty<'tcx>), +} + +impl<'tcx> fmt::Display for LayoutError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + LayoutError::Unknown(ty) => write!(f, "the type `{:?}` has an unknown layout", ty), + LayoutError::SizeOverflow(ty) => { + write!(f, "the type `{:?}` is too big for the current architecture", ty) + } + } + } +} + +fn layout_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Result<&'tcx Layout, LayoutError<'tcx>> { + ty::tls::with_related_context(tcx, move |icx| { + let rec_limit = *tcx.sess.recursion_limit.get(); + let (param_env, ty) = query.into_parts(); + + if icx.layout_depth > rec_limit { + tcx.sess.fatal(&format!("overflow representing the type `{}`", ty)); + } + + // Update the ImplicitCtxt to increase the layout_depth + let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() }; + + ty::tls::enter_context(&icx, |_| { + let cx = LayoutCx { tcx, param_env }; + let layout = cx.layout_raw_uncached(ty); + // Type-level uninhabitedness should always imply ABI uninhabitedness. + if let Ok(layout) = layout { + if ty.conservative_is_privately_uninhabited(tcx) { + assert!(layout.abi.is_uninhabited()); + } + } + layout + }) + }) +} + +pub fn provide(providers: &mut ty::query::Providers<'_>) { + *providers = ty::query::Providers { layout_raw, ..*providers }; +} + +pub struct LayoutCx<'tcx, C> { + pub tcx: C, + pub param_env: ty::ParamEnv<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +enum StructKind { + /// A tuple, closure, or univariant which cannot be coerced to unsized. + AlwaysSized, + /// A univariant, the last field of which may be coerced to unsized. + MaybeUnsized, + /// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag). + Prefixed(Size, Align), +} + +// Invert a bijective mapping, i.e. `invert(map)[y] = x` if `map[x] = y`. +// This is used to go between `memory_index` (source field order to memory order) +// and `inverse_memory_index` (memory order to source field order). +// See also `FieldsShape::Arbitrary::memory_index` for more details. +// FIXME(eddyb) build a better abstraction for permutations, if possible. +fn invert_mapping(map: &[u32]) -> Vec { + let mut inverse = vec![0; map.len()]; + for i in 0..map.len() { + inverse[map[i] as usize] = i as u32; + } + inverse +} + +impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { + fn scalar_pair(&self, a: Scalar, b: Scalar) -> Layout { + let dl = self.data_layout(); + let b_align = b.value.align(dl); + let align = a.value.align(dl).max(b_align).max(dl.aggregate_align); + let b_offset = a.value.size(dl).align_to(b_align.abi); + let size = (b_offset + b.value.size(dl)).align_to(align.abi); + + // HACK(nox): We iter on `b` and then `a` because `max_by_key` + // returns the last maximum. + let largest_niche = Niche::from_scalar(dl, b_offset, b.clone()) + .into_iter() + .chain(Niche::from_scalar(dl, Size::ZERO, a.clone())) + .max_by_key(|niche| niche.available(dl)); + + Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { + offsets: vec![Size::ZERO, b_offset], + memory_index: vec![0, 1], + }, + abi: Abi::ScalarPair(a, b), + largest_niche, + align, + size, + } + } + + fn univariant_uninterned( + &self, + ty: Ty<'tcx>, + fields: &[TyAndLayout<'_>], + repr: &ReprOptions, + kind: StructKind, + ) -> Result> { + let dl = self.data_layout(); + let pack = repr.pack; + if pack.is_some() && repr.align.is_some() { + bug!("struct cannot be packed and aligned"); + } + + let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; + + let mut inverse_memory_index: Vec = (0..fields.len() as u32).collect(); + + let optimize = !repr.inhibit_struct_field_reordering_opt(); + if optimize { + let end = + if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; + let optimizing = &mut inverse_memory_index[..end]; + let field_align = |f: &TyAndLayout<'_>| { + if let Some(pack) = pack { f.align.abi.min(pack) } else { f.align.abi } + }; + match kind { + StructKind::AlwaysSized | StructKind::MaybeUnsized => { + optimizing.sort_by_key(|&x| { + // Place ZSTs first to avoid "interesting offsets", + // especially with only one or two non-ZST fields. + let f = &fields[x as usize]; + (!f.is_zst(), cmp::Reverse(field_align(f))) + }); + } + StructKind::Prefixed(..) => { + // Sort in ascending alignment so that the layout stay optimal + // regardless of the prefix + optimizing.sort_by_key(|&x| field_align(&fields[x as usize])); + } + } + } + + // inverse_memory_index holds field indices by increasing memory offset. + // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. + // We now write field offsets to the corresponding offset slot; + // field 5 with offset 0 puts 0 in offsets[5]. + // At the bottom of this function, we invert `inverse_memory_index` to + // produce `memory_index` (see `invert_mapping`). + + let mut sized = true; + let mut offsets = vec![Size::ZERO; fields.len()]; + let mut offset = Size::ZERO; + let mut largest_niche = None; + let mut largest_niche_available = 0; + + if let StructKind::Prefixed(prefix_size, prefix_align) = kind { + let prefix_align = + if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align }; + align = align.max(AbiAndPrefAlign::new(prefix_align)); + offset = prefix_size.align_to(prefix_align); + } + + for &i in &inverse_memory_index { + let field = fields[i as usize]; + if !sized { + bug!("univariant: field #{} of `{}` comes after unsized field", offsets.len(), ty); + } + + if field.is_unsized() { + sized = false; + } + + // Invariant: offset < dl.obj_size_bound() <= 1<<61 + let field_align = if let Some(pack) = pack { + field.align.min(AbiAndPrefAlign::new(pack)) + } else { + field.align + }; + offset = offset.align_to(field_align.abi); + align = align.max(field_align); + + debug!("univariant offset: {:?} field: {:#?}", offset, field); + offsets[i as usize] = offset; + + if !repr.hide_niche() { + if let Some(mut niche) = field.largest_niche.clone() { + let available = niche.available(dl); + if available > largest_niche_available { + largest_niche_available = available; + niche.offset += offset; + largest_niche = Some(niche); + } + } + } + + offset = offset.checked_add(field.size, dl).ok_or(LayoutError::SizeOverflow(ty))?; + } + + if let Some(repr_align) = repr.align { + align = align.max(AbiAndPrefAlign::new(repr_align)); + } + + debug!("univariant min_size: {:?}", offset); + let min_size = offset; + + // As stated above, inverse_memory_index holds field indices by increasing offset. + // This makes it an already-sorted view of the offsets vec. + // To invert it, consider: + // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. + // Field 5 would be the first element, so memory_index is i: + // Note: if we didn't optimize, it's already right. + + let memory_index = + if optimize { invert_mapping(&inverse_memory_index) } else { inverse_memory_index }; + + let size = min_size.align_to(align.abi); + let mut abi = Abi::Aggregate { sized }; + + // Unpack newtype ABIs and find scalar pairs. + if sized && size.bytes() > 0 { + // All other fields must be ZSTs, and we need them to all start at 0. + let mut zst_offsets = offsets.iter().enumerate().filter(|&(i, _)| fields[i].is_zst()); + if zst_offsets.all(|(_, o)| o.bytes() == 0) { + let mut non_zst_fields = fields.iter().enumerate().filter(|&(_, f)| !f.is_zst()); + + match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) { + // We have exactly one non-ZST field. + (Some((i, field)), None, None) => { + // Field fills the struct and it has a scalar or scalar pair ABI. + if offsets[i].bytes() == 0 + && align.abi == field.align.abi + && size == field.size + { + match field.abi { + // For plain scalars, or vectors of them, we can't unpack + // newtypes for `#[repr(C)]`, as that affects C ABIs. + Abi::Scalar(_) | Abi::Vector { .. } if optimize => { + abi = field.abi.clone(); + } + // But scalar pairs are Rust-specific and get + // treated as aggregates by C ABIs anyway. + Abi::ScalarPair(..) => { + abi = field.abi.clone(); + } + _ => {} + } + } + } + + // Two non-ZST fields, and they're both scalars. + ( + Some(( + i, + &TyAndLayout { + layout: &Layout { abi: Abi::Scalar(ref a), .. }, .. + }, + )), + Some(( + j, + &TyAndLayout { + layout: &Layout { abi: Abi::Scalar(ref b), .. }, .. + }, + )), + None, + ) => { + // Order by the memory placement, not source order. + let ((i, a), (j, b)) = if offsets[i] < offsets[j] { + ((i, a), (j, b)) + } else { + ((j, b), (i, a)) + }; + let pair = self.scalar_pair(a.clone(), b.clone()); + let pair_offsets = match pair.fields { + FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + assert_eq!(memory_index, &[0, 1]); + offsets + } + _ => bug!(), + }; + if offsets[i] == pair_offsets[0] + && offsets[j] == pair_offsets[1] + && align == pair.align + && size == pair.size + { + // We can use `ScalarPair` only when it matches our + // already computed layout (including `#[repr(C)]`). + abi = pair.abi; + } + } + + _ => {} + } + } + } + + if sized && fields.iter().any(|f| f.abi.is_uninhabited()) { + abi = Abi::Uninhabited; + } + + Ok(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { offsets, memory_index }, + abi, + largest_niche, + align, + size, + }) + } + + fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { + let tcx = self.tcx; + let param_env = self.param_env; + let dl = self.data_layout(); + let scalar_unit = |value: Primitive| { + let bits = value.size(dl).bits(); + assert!(bits <= 128); + Scalar { value, valid_range: 0..=(!0 >> (128 - bits)) } + }; + let scalar = |value: Primitive| tcx.intern_layout(Layout::scalar(self, scalar_unit(value))); + + let univariant = |fields: &[TyAndLayout<'_>], repr: &ReprOptions, kind| { + Ok(tcx.intern_layout(self.univariant_uninterned(ty, fields, repr, kind)?)) + }; + debug_assert!(!ty.has_infer_types_or_consts()); + + Ok(match ty.kind { + // Basic scalars. + ty::Bool => tcx.intern_layout(Layout::scalar( + self, + Scalar { value: Int(I8, false), valid_range: 0..=1 }, + )), + ty::Char => tcx.intern_layout(Layout::scalar( + self, + Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF }, + )), + ty::Int(ity) => scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true)), + ty::Uint(ity) => scalar(Int(Integer::from_attr(dl, attr::UnsignedInt(ity)), false)), + ty::Float(fty) => scalar(match fty { + ast::FloatTy::F32 => F32, + ast::FloatTy::F64 => F64, + }), + ty::FnPtr(_) => { + let mut ptr = scalar_unit(Pointer); + ptr.valid_range = 1..=*ptr.valid_range.end(); + tcx.intern_layout(Layout::scalar(self, ptr)) + } + + // The never type. + ty::Never => tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Primitive, + abi: Abi::Uninhabited, + largest_niche: None, + align: dl.i8_align, + size: Size::ZERO, + }), + + // Potentially-fat pointers. + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let mut data_ptr = scalar_unit(Pointer); + if !ty.is_unsafe_ptr() { + data_ptr.valid_range = 1..=*data_ptr.valid_range.end(); + } + + let pointee = tcx.normalize_erasing_regions(param_env, pointee); + if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { + return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr))); + } + + let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + let metadata = match unsized_part.kind { + ty::Foreign(..) => { + return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr))); + } + ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), + ty::Dynamic(..) => { + let mut vtable = scalar_unit(Pointer); + vtable.valid_range = 1..=*vtable.valid_range.end(); + vtable + } + _ => return Err(LayoutError::Unknown(unsized_part)), + }; + + // Effectively a (ptr, meta) tuple. + tcx.intern_layout(self.scalar_pair(data_ptr, metadata)) + } + + // Arrays and slices. + ty::Array(element, mut count) => { + if count.has_projections() { + count = tcx.normalize_erasing_regions(param_env, count); + if count.has_projections() { + return Err(LayoutError::Unknown(ty)); + } + } + + let count = count.try_eval_usize(tcx, param_env).ok_or(LayoutError::Unknown(ty))?; + let element = self.layout_of(element)?; + let size = + element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; + + let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) { + Abi::Uninhabited + } else { + Abi::Aggregate { sized: true } + }; + + let largest_niche = if count != 0 { element.largest_niche.clone() } else { None }; + + tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: element.size, count }, + abi, + largest_niche, + align: element.align, + size, + }) + } + ty::Slice(element) => { + let element = self.layout_of(element)?; + tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: element.size, count: 0 }, + abi: Abi::Aggregate { sized: false }, + largest_niche: None, + align: element.align, + size: Size::ZERO, + }) + } + ty::Str => tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, + abi: Abi::Aggregate { sized: false }, + largest_niche: None, + align: dl.i8_align, + size: Size::ZERO, + }), + + // Odd unit types. + ty::FnDef(..) => univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?, + ty::Dynamic(..) | ty::Foreign(..) => { + let mut unit = self.univariant_uninterned( + ty, + &[], + &ReprOptions::default(), + StructKind::AlwaysSized, + )?; + match unit.abi { + Abi::Aggregate { ref mut sized } => *sized = false, + _ => bug!(), + } + tcx.intern_layout(unit) + } + + ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, substs)?, + + ty::Closure(_, ref substs) => { + let tys = substs.as_closure().upvar_tys(); + univariant( + &tys.map(|ty| self.layout_of(ty)).collect::, _>>()?, + &ReprOptions::default(), + StructKind::AlwaysSized, + )? + } + + ty::Tuple(tys) => { + let kind = + if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; + + univariant( + &tys.iter() + .map(|k| self.layout_of(k.expect_ty())) + .collect::, _>>()?, + &ReprOptions::default(), + kind, + )? + } + + // SIMD vector types. + ty::Adt(def, ..) if def.repr.simd() => { + let element = self.layout_of(ty.simd_type(tcx))?; + let count = ty.simd_size(tcx); + assert!(count > 0); + let scalar = match element.abi { + Abi::Scalar(ref scalar) => scalar.clone(), + _ => { + tcx.sess.fatal(&format!( + "monomorphising SIMD type `{}` with \ + a non-machine element type `{}`", + ty, element.ty + )); + } + }; + let size = + element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; + let align = dl.vector_align(size); + let size = size.align_to(align.abi); + + tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: element.size, count }, + abi: Abi::Vector { element: scalar, count }, + largest_niche: element.largest_niche.clone(), + size, + align, + }) + } + + // ADTs. + ty::Adt(def, substs) => { + // Cache the field layouts. + let variants = def + .variants + .iter() + .map(|v| { + v.fields + .iter() + .map(|field| self.layout_of(field.ty(tcx, substs))) + .collect::, _>>() + }) + .collect::, _>>()?; + + if def.is_union() { + if def.repr.pack.is_some() && def.repr.align.is_some() { + bug!("union cannot be packed and aligned"); + } + + let mut align = + if def.repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; + + if let Some(repr_align) = def.repr.align { + align = align.max(AbiAndPrefAlign::new(repr_align)); + } + + let optimize = !def.repr.inhibit_union_abi_opt(); + let mut size = Size::ZERO; + let mut abi = Abi::Aggregate { sized: true }; + let index = VariantIdx::new(0); + for field in &variants[index] { + assert!(!field.is_unsized()); + align = align.max(field.align); + + // If all non-ZST fields have the same ABI, forward this ABI + if optimize && !field.is_zst() { + // Normalize scalar_unit to the maximal valid range + let field_abi = match &field.abi { + Abi::Scalar(x) => Abi::Scalar(scalar_unit(x.value)), + Abi::ScalarPair(x, y) => { + Abi::ScalarPair(scalar_unit(x.value), scalar_unit(y.value)) + } + Abi::Vector { element: x, count } => { + Abi::Vector { element: scalar_unit(x.value), count: *count } + } + Abi::Uninhabited | Abi::Aggregate { .. } => { + Abi::Aggregate { sized: true } + } + }; + + if size == Size::ZERO { + // first non ZST: initialize 'abi' + abi = field_abi; + } else if abi != field_abi { + // different fields have different ABI: reset to Aggregate + abi = Abi::Aggregate { sized: true }; + } + } + + size = cmp::max(size, field.size); + } + + if let Some(pack) = def.repr.pack { + align = align.min(AbiAndPrefAlign::new(pack)); + } + + return Ok(tcx.intern_layout(Layout { + variants: Variants::Single { index }, + fields: FieldsShape::Union( + NonZeroUsize::new(variants[index].len()) + .ok_or(LayoutError::Unknown(ty))?, + ), + abi, + largest_niche: None, + align, + size: size.align_to(align.abi), + })); + } + + // A variant is absent if it's uninhabited and only has ZST fields. + // Present uninhabited variants only require space for their fields, + // but *not* an encoding of the discriminant (e.g., a tag value). + // See issue #49298 for more details on the need to leave space + // for non-ZST uninhabited data (mostly partial initialization). + let absent = |fields: &[TyAndLayout<'_>]| { + let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited()); + let is_zst = fields.iter().all(|f| f.is_zst()); + uninhabited && is_zst + }; + let (present_first, present_second) = { + let mut present_variants = variants + .iter_enumerated() + .filter_map(|(i, v)| if absent(v) { None } else { Some(i) }); + (present_variants.next(), present_variants.next()) + }; + let present_first = match present_first { + present_first @ Some(_) => present_first, + // Uninhabited because it has no variants, or only absent ones. + None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)), + // If it's a struct, still compute a layout so that we can still compute the + // field offsets. + None => Some(VariantIdx::new(0)), + }; + + let is_struct = !def.is_enum() || + // Only one variant is present. + (present_second.is_none() && + // Representation optimizations are allowed. + !def.repr.inhibit_enum_layout_opt()); + if is_struct { + // Struct, or univariant enum equivalent to a struct. + // (Typechecking will reject discriminant-sizing attrs.) + + let v = present_first.unwrap(); + let kind = if def.is_enum() || variants[v].is_empty() { + StructKind::AlwaysSized + } else { + let param_env = tcx.param_env(def.did); + let last_field = def.variants[v].fields.last().unwrap(); + let always_sized = + tcx.type_of(last_field.did).is_sized(tcx.at(DUMMY_SP), param_env); + if !always_sized { + StructKind::MaybeUnsized + } else { + StructKind::AlwaysSized + } + }; + + let mut st = self.univariant_uninterned(ty, &variants[v], &def.repr, kind)?; + st.variants = Variants::Single { index: v }; + let (start, end) = self.tcx.layout_scalar_valid_range(def.did); + match st.abi { + Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => { + // the asserts ensure that we are not using the + // `#[rustc_layout_scalar_valid_range(n)]` + // attribute to widen the range of anything as that would probably + // result in UB somewhere + // FIXME(eddyb) the asserts are probably not needed, + // as larger validity ranges would result in missed + // optimizations, *not* wrongly assuming the inner + // value is valid. e.g. unions enlarge validity ranges, + // because the values may be uninitialized. + if let Bound::Included(start) = start { + // FIXME(eddyb) this might be incorrect - it doesn't + // account for wrap-around (end < start) ranges. + assert!(*scalar.valid_range.start() <= start); + scalar.valid_range = start..=*scalar.valid_range.end(); + } + if let Bound::Included(end) = end { + // FIXME(eddyb) this might be incorrect - it doesn't + // account for wrap-around (end < start) ranges. + assert!(*scalar.valid_range.end() >= end); + scalar.valid_range = *scalar.valid_range.start()..=end; + } + + // Update `largest_niche` if we have introduced a larger niche. + let niche = if def.repr.hide_niche() { + None + } else { + Niche::from_scalar(dl, Size::ZERO, scalar.clone()) + }; + if let Some(niche) = niche { + match &st.largest_niche { + Some(largest_niche) => { + // Replace the existing niche even if they're equal, + // because this one is at a lower offset. + if largest_niche.available(dl) <= niche.available(dl) { + st.largest_niche = Some(niche); + } + } + None => st.largest_niche = Some(niche), + } + } + } + _ => assert!( + start == Bound::Unbounded && end == Bound::Unbounded, + "nonscalar layout for layout_scalar_valid_range type {:?}: {:#?}", + def, + st, + ), + } + + return Ok(tcx.intern_layout(st)); + } + + // At this point, we have handled all unions and + // structs. (We have also handled univariant enums + // that allow representation optimization.) + assert!(def.is_enum()); + + // The current code for niche-filling relies on variant indices + // instead of actual discriminants, so dataful enums with + // explicit discriminants (RFC #2363) would misbehave. + let no_explicit_discriminants = def + .variants + .iter_enumerated() + .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32())); + + // Niche-filling enum optimization. + if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants { + let mut dataful_variant = None; + let mut niche_variants = VariantIdx::MAX..=VariantIdx::new(0); + + // Find one non-ZST variant. + 'variants: for (v, fields) in variants.iter_enumerated() { + if absent(fields) { + continue 'variants; + } + for f in fields { + if !f.is_zst() { + if dataful_variant.is_none() { + dataful_variant = Some(v); + continue 'variants; + } else { + dataful_variant = None; + break 'variants; + } + } + } + niche_variants = *niche_variants.start().min(&v)..=v; + } + + if niche_variants.start() > niche_variants.end() { + dataful_variant = None; + } + + if let Some(i) = dataful_variant { + let count = (niche_variants.end().as_u32() + - niche_variants.start().as_u32() + + 1) as u128; + + // Find the field with the largest niche + let niche_candidate = variants[i] + .iter() + .enumerate() + .filter_map(|(j, &field)| Some((j, field.largest_niche.as_ref()?))) + .max_by_key(|(_, niche)| niche.available(dl)); + + if let Some((field_index, niche, (niche_start, niche_scalar))) = + niche_candidate.and_then(|(field_index, niche)| { + Some((field_index, niche, niche.reserve(self, count)?)) + }) + { + let mut align = dl.aggregate_align; + let st = variants + .iter_enumerated() + .map(|(j, v)| { + let mut st = self.univariant_uninterned( + ty, + v, + &def.repr, + StructKind::AlwaysSized, + )?; + st.variants = Variants::Single { index: j }; + + align = align.max(st.align); + + Ok(st) + }) + .collect::, _>>()?; + + let offset = st[i].fields.offset(field_index) + niche.offset; + let size = st[i].size; + + let abi = if st.iter().all(|v| v.abi.is_uninhabited()) { + Abi::Uninhabited + } else { + match st[i].abi { + Abi::Scalar(_) => Abi::Scalar(niche_scalar.clone()), + Abi::ScalarPair(ref first, ref second) => { + // We need to use scalar_unit to reset the + // valid range to the maximal one for that + // primitive, because only the niche is + // guaranteed to be initialised, not the + // other primitive. + if offset.bytes() == 0 { + Abi::ScalarPair( + niche_scalar.clone(), + scalar_unit(second.value), + ) + } else { + Abi::ScalarPair( + scalar_unit(first.value), + niche_scalar.clone(), + ) + } + } + _ => Abi::Aggregate { sized: true }, + } + }; + + let largest_niche = + Niche::from_scalar(dl, offset, niche_scalar.clone()); + + return Ok(tcx.intern_layout(Layout { + variants: Variants::Multiple { + discr: niche_scalar, + discr_kind: DiscriminantKind::Niche { + dataful_variant: i, + niche_variants, + niche_start, + }, + discr_index: 0, + variants: st, + }, + fields: FieldsShape::Arbitrary { + offsets: vec![offset], + memory_index: vec![0], + }, + abi, + largest_niche, + size, + align, + })); + } + } + } + + let (mut min, mut max) = (i128::MAX, i128::MIN); + let discr_type = def.repr.discr_type(); + let bits = Integer::from_attr(self, discr_type).size().bits(); + for (i, discr) in def.discriminants(tcx) { + if variants[i].iter().any(|f| f.abi.is_uninhabited()) { + continue; + } + let mut x = discr.val as i128; + if discr_type.is_signed() { + // sign extend the raw representation to be an i128 + x = (x << (128 - bits)) >> (128 - bits); + } + if x < min { + min = x; + } + if x > max { + max = x; + } + } + // We might have no inhabited variants, so pretend there's at least one. + if (min, max) == (i128::MAX, i128::MIN) { + min = 0; + max = 0; + } + assert!(min <= max, "discriminant range is {}...{}", min, max); + let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); + + let mut align = dl.aggregate_align; + let mut size = Size::ZERO; + + // We're interested in the smallest alignment, so start large. + let mut start_align = Align::from_bytes(256).unwrap(); + assert_eq!(Integer::for_align(dl, start_align), None); + + // repr(C) on an enum tells us to make a (tag, union) layout, + // so we need to grow the prefix alignment to be at least + // the alignment of the union. (This value is used both for + // determining the alignment of the overall enum, and the + // determining the alignment of the payload after the tag.) + let mut prefix_align = min_ity.align(dl).abi; + if def.repr.c() { + for fields in &variants { + for field in fields { + prefix_align = prefix_align.max(field.align.abi); + } + } + } + + // Create the set of structs that represent each variant. + let mut layout_variants = variants + .iter_enumerated() + .map(|(i, field_layouts)| { + let mut st = self.univariant_uninterned( + ty, + &field_layouts, + &def.repr, + StructKind::Prefixed(min_ity.size(), prefix_align), + )?; + st.variants = Variants::Single { index: i }; + // Find the first field we can't move later + // to make room for a larger discriminant. + for field in + st.fields.index_by_increasing_offset().map(|j| field_layouts[j]) + { + if !field.is_zst() || field.align.abi.bytes() != 1 { + start_align = start_align.min(field.align.abi); + break; + } + } + size = cmp::max(size, st.size); + align = align.max(st.align); + Ok(st) + }) + .collect::, _>>()?; + + // Align the maximum variant size to the largest alignment. + size = size.align_to(align.abi); + + if size.bytes() >= dl.obj_size_bound() { + return Err(LayoutError::SizeOverflow(ty)); + } + + let typeck_ity = Integer::from_attr(dl, def.repr.discr_type()); + if typeck_ity < min_ity { + // It is a bug if Layout decided on a greater discriminant size than typeck for + // some reason at this point (based on values discriminant can take on). Mostly + // because this discriminant will be loaded, and then stored into variable of + // type calculated by typeck. Consider such case (a bug): typeck decided on + // byte-sized discriminant, but layout thinks we need a 16-bit to store all + // discriminant values. That would be a bug, because then, in codegen, in order + // to store this 16-bit discriminant into 8-bit sized temporary some of the + // space necessary to represent would have to be discarded (or layout is wrong + // on thinking it needs 16 bits) + bug!( + "layout decided on a larger discriminant type ({:?}) than typeck ({:?})", + min_ity, + typeck_ity + ); + // However, it is fine to make discr type however large (as an optimisation) + // after this point – we’ll just truncate the value we load in codegen. + } + + // Check to see if we should use a different type for the + // discriminant. We can safely use a type with the same size + // as the alignment of the first field of each variant. + // We increase the size of the discriminant to avoid LLVM copying + // padding when it doesn't need to. This normally causes unaligned + // load/stores and excessive memcpy/memset operations. By using a + // bigger integer size, LLVM can be sure about its contents and + // won't be so conservative. + + // Use the initial field alignment + let mut ity = if def.repr.c() || def.repr.int.is_some() { + min_ity + } else { + Integer::for_align(dl, start_align).unwrap_or(min_ity) + }; + + // If the alignment is not larger than the chosen discriminant size, + // don't use the alignment as the final size. + if ity <= min_ity { + ity = min_ity; + } else { + // Patch up the variants' first few fields. + let old_ity_size = min_ity.size(); + let new_ity_size = ity.size(); + for variant in &mut layout_variants { + match variant.fields { + FieldsShape::Arbitrary { ref mut offsets, .. } => { + for i in offsets { + if *i <= old_ity_size { + assert_eq!(*i, old_ity_size); + *i = new_ity_size; + } + } + // We might be making the struct larger. + if variant.size <= old_ity_size { + variant.size = new_ity_size; + } + } + _ => bug!(), + } + } + } + + let tag_mask = !0u128 >> (128 - ity.size().bits()); + let tag = Scalar { + value: Int(ity, signed), + valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask), + }; + let mut abi = Abi::Aggregate { sized: true }; + if tag.value.size(dl) == size { + abi = Abi::Scalar(tag.clone()); + } else { + // Try to use a ScalarPair for all tagged enums. + let mut common_prim = None; + for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) { + let offsets = match layout_variant.fields { + FieldsShape::Arbitrary { ref offsets, .. } => offsets, + _ => bug!(), + }; + let mut fields = + field_layouts.iter().zip(offsets).filter(|p| !p.0.is_zst()); + let (field, offset) = match (fields.next(), fields.next()) { + (None, None) => continue, + (Some(pair), None) => pair, + _ => { + common_prim = None; + break; + } + }; + let prim = match field.abi { + Abi::Scalar(ref scalar) => scalar.value, + _ => { + common_prim = None; + break; + } + }; + if let Some(pair) = common_prim { + // This is pretty conservative. We could go fancier + // by conflating things like i32 and u32, or even + // realising that (u8, u8) could just cohabit with + // u16 or even u32. + if pair != (prim, offset) { + common_prim = None; + break; + } + } else { + common_prim = Some((prim, offset)); + } + } + if let Some((prim, offset)) = common_prim { + let pair = self.scalar_pair(tag.clone(), scalar_unit(prim)); + let pair_offsets = match pair.fields { + FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + assert_eq!(memory_index, &[0, 1]); + offsets + } + _ => bug!(), + }; + if pair_offsets[0] == Size::ZERO + && pair_offsets[1] == *offset + && align == pair.align + && size == pair.size + { + // We can use `ScalarPair` only when it matches our + // already computed layout (including `#[repr(C)]`). + abi = pair.abi; + } + } + } + + if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { + abi = Abi::Uninhabited; + } + + let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone()); + + tcx.intern_layout(Layout { + variants: Variants::Multiple { + discr: tag, + discr_kind: DiscriminantKind::Tag, + discr_index: 0, + variants: layout_variants, + }, + fields: FieldsShape::Arbitrary { + offsets: vec![Size::ZERO], + memory_index: vec![0], + }, + largest_niche, + abi, + align, + size, + }) + } + + // Types with no meaningful known layout. + ty::Projection(_) | ty::Opaque(..) => { + let normalized = tcx.normalize_erasing_regions(param_env, ty); + if ty == normalized { + return Err(LayoutError::Unknown(ty)); + } + tcx.layout_raw(param_env.and(normalized))? + } + + ty::Bound(..) | ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { + bug!("Layout::compute: unexpected type `{}`", ty) + } + + ty::Param(_) | ty::Error => { + return Err(LayoutError::Unknown(ty)); + } + }) + } +} + +/// Overlap eligibility and variant assignment for each GeneratorSavedLocal. +#[derive(Clone, Debug, PartialEq)] +enum SavedLocalEligibility { + Unassigned, + Assigned(VariantIdx), + // FIXME: Use newtype_index so we aren't wasting bytes + Ineligible(Option), +} + +// When laying out generators, we divide our saved local fields into two +// categories: overlap-eligible and overlap-ineligible. +// +// Those fields which are ineligible for overlap go in a "prefix" at the +// beginning of the layout, and always have space reserved for them. +// +// Overlap-eligible fields are only assigned to one variant, so we lay +// those fields out for each variant and put them right after the +// prefix. +// +// Finally, in the layout details, we point to the fields from the +// variants they are assigned to. It is possible for some fields to be +// included in multiple variants. No field ever "moves around" in the +// layout; its offset is always the same. +// +// Also included in the layout are the upvars and the discriminant. +// These are included as fields on the "outer" layout; they are not part +// of any variant. +impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { + /// Compute the eligibility and assignment of each local. + fn generator_saved_local_eligibility( + &self, + info: &GeneratorLayout<'tcx>, + ) -> (BitSet, IndexVec) { + use SavedLocalEligibility::*; + + let mut assignments: IndexVec = + IndexVec::from_elem_n(Unassigned, info.field_tys.len()); + + // The saved locals not eligible for overlap. These will get + // "promoted" to the prefix of our generator. + let mut ineligible_locals = BitSet::new_empty(info.field_tys.len()); + + // Figure out which of our saved locals are fields in only + // one variant. The rest are deemed ineligible for overlap. + for (variant_index, fields) in info.variant_fields.iter_enumerated() { + for local in fields { + match assignments[*local] { + Unassigned => { + assignments[*local] = Assigned(variant_index); + } + Assigned(idx) => { + // We've already seen this local at another suspension + // point, so it is no longer a candidate. + trace!( + "removing local {:?} in >1 variant ({:?}, {:?})", + local, + variant_index, + idx + ); + ineligible_locals.insert(*local); + assignments[*local] = Ineligible(None); + } + Ineligible(_) => {} + } + } + } + + // Next, check every pair of eligible locals to see if they + // conflict. + for local_a in info.storage_conflicts.rows() { + let conflicts_a = info.storage_conflicts.count(local_a); + if ineligible_locals.contains(local_a) { + continue; + } + + for local_b in info.storage_conflicts.iter(local_a) { + // local_a and local_b are storage live at the same time, therefore they + // cannot overlap in the generator layout. The only way to guarantee + // this is if they are in the same variant, or one is ineligible + // (which means it is stored in every variant). + if ineligible_locals.contains(local_b) + || assignments[local_a] == assignments[local_b] + { + continue; + } + + // If they conflict, we will choose one to make ineligible. + // This is not always optimal; it's just a greedy heuristic that + // seems to produce good results most of the time. + let conflicts_b = info.storage_conflicts.count(local_b); + let (remove, other) = + if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) }; + ineligible_locals.insert(remove); + assignments[remove] = Ineligible(None); + trace!("removing local {:?} due to conflict with {:?}", remove, other); + } + } + + // Count the number of variants in use. If only one of them, then it is + // impossible to overlap any locals in our layout. In this case it's + // always better to make the remaining locals ineligible, so we can + // lay them out with the other locals in the prefix and eliminate + // unnecessary padding bytes. + { + let mut used_variants = BitSet::new_empty(info.variant_fields.len()); + for assignment in &assignments { + if let Assigned(idx) = assignment { + used_variants.insert(*idx); + } + } + if used_variants.count() < 2 { + for assignment in assignments.iter_mut() { + *assignment = Ineligible(None); + } + ineligible_locals.insert_all(); + } + } + + // Write down the order of our locals that will be promoted to the prefix. + { + for (idx, local) in ineligible_locals.iter().enumerate() { + assignments[local] = Ineligible(Some(idx as u32)); + } + } + debug!("generator saved local assignments: {:?}", assignments); + + (ineligible_locals, assignments) + } + + /// Compute the full generator layout. + fn generator_layout( + &self, + ty: Ty<'tcx>, + def_id: hir::def_id::DefId, + substs: SubstsRef<'tcx>, + ) -> Result<&'tcx Layout, LayoutError<'tcx>> { + use SavedLocalEligibility::*; + let tcx = self.tcx; + + let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs); + + let info = tcx.generator_layout(def_id); + let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info); + + // Build a prefix layout, including "promoting" all ineligible + // locals as part of the prefix. We compute the layout of all of + // these fields at once to get optimal packing. + let discr_index = substs.as_generator().prefix_tys().count(); + + // `info.variant_fields` already accounts for the reserved variants, so no need to add them. + let max_discr = (info.variant_fields.len() - 1) as u128; + let discr_int = Integer::fit_unsigned(max_discr); + let discr_int_ty = discr_int.to_ty(tcx, false); + let discr = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr }; + let discr_layout = self.tcx.intern_layout(Layout::scalar(self, discr.clone())); + let discr_layout = TyAndLayout { ty: discr_int_ty, layout: discr_layout }; + + let promoted_layouts = ineligible_locals + .iter() + .map(|local| subst_field(info.field_tys[local])) + .map(|ty| tcx.mk_maybe_uninit(ty)) + .map(|ty| self.layout_of(ty)); + let prefix_layouts = substs + .as_generator() + .prefix_tys() + .map(|ty| self.layout_of(ty)) + .chain(iter::once(Ok(discr_layout))) + .chain(promoted_layouts) + .collect::, _>>()?; + let prefix = self.univariant_uninterned( + ty, + &prefix_layouts, + &ReprOptions::default(), + StructKind::AlwaysSized, + )?; + + let (prefix_size, prefix_align) = (prefix.size, prefix.align); + + // Split the prefix layout into the "outer" fields (upvars and + // discriminant) and the "promoted" fields. Promoted fields will + // get included in each variant that requested them in + // GeneratorLayout. + debug!("prefix = {:#?}", prefix); + let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields { + FieldsShape::Arbitrary { mut offsets, memory_index } => { + let mut inverse_memory_index = invert_mapping(&memory_index); + + // "a" (`0..b_start`) and "b" (`b_start..`) correspond to + // "outer" and "promoted" fields respectively. + let b_start = (discr_index + 1) as u32; + let offsets_b = offsets.split_off(b_start as usize); + let offsets_a = offsets; + + // Disentangle the "a" and "b" components of `inverse_memory_index` + // by preserving the order but keeping only one disjoint "half" each. + // FIXME(eddyb) build a better abstraction for permutations, if possible. + let inverse_memory_index_b: Vec<_> = + inverse_memory_index.iter().filter_map(|&i| i.checked_sub(b_start)).collect(); + inverse_memory_index.retain(|&i| i < b_start); + let inverse_memory_index_a = inverse_memory_index; + + // Since `inverse_memory_index_{a,b}` each only refer to their + // respective fields, they can be safely inverted + let memory_index_a = invert_mapping(&inverse_memory_index_a); + let memory_index_b = invert_mapping(&inverse_memory_index_b); + + let outer_fields = + FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a }; + (outer_fields, offsets_b, memory_index_b) + } + _ => bug!(), + }; + + let mut size = prefix.size; + let mut align = prefix.align; + let variants = info + .variant_fields + .iter_enumerated() + .map(|(index, variant_fields)| { + // Only include overlap-eligible fields when we compute our variant layout. + let variant_only_tys = variant_fields + .iter() + .filter(|local| match assignments[**local] { + Unassigned => bug!(), + Assigned(v) if v == index => true, + Assigned(_) => bug!("assignment does not match variant"), + Ineligible(_) => false, + }) + .map(|local| subst_field(info.field_tys[*local])); + + let mut variant = self.univariant_uninterned( + ty, + &variant_only_tys + .map(|ty| self.layout_of(ty)) + .collect::, _>>()?, + &ReprOptions::default(), + StructKind::Prefixed(prefix_size, prefix_align.abi), + )?; + variant.variants = Variants::Single { index }; + + let (offsets, memory_index) = match variant.fields { + FieldsShape::Arbitrary { offsets, memory_index } => (offsets, memory_index), + _ => bug!(), + }; + + // Now, stitch the promoted and variant-only fields back together in + // the order they are mentioned by our GeneratorLayout. + // Because we only use some subset (that can differ between variants) + // of the promoted fields, we can't just pick those elements of the + // `promoted_memory_index` (as we'd end up with gaps). + // So instead, we build an "inverse memory_index", as if all of the + // promoted fields were being used, but leave the elements not in the + // subset as `INVALID_FIELD_IDX`, which we can filter out later to + // obtain a valid (bijective) mapping. + const INVALID_FIELD_IDX: u32 = !0; + let mut combined_inverse_memory_index = + vec![INVALID_FIELD_IDX; promoted_memory_index.len() + memory_index.len()]; + let mut offsets_and_memory_index = offsets.into_iter().zip(memory_index); + let combined_offsets = variant_fields + .iter() + .enumerate() + .map(|(i, local)| { + let (offset, memory_index) = match assignments[*local] { + Unassigned => bug!(), + Assigned(_) => { + let (offset, memory_index) = + offsets_and_memory_index.next().unwrap(); + (offset, promoted_memory_index.len() as u32 + memory_index) + } + Ineligible(field_idx) => { + let field_idx = field_idx.unwrap() as usize; + (promoted_offsets[field_idx], promoted_memory_index[field_idx]) + } + }; + combined_inverse_memory_index[memory_index as usize] = i as u32; + offset + }) + .collect(); + + // Remove the unused slots and invert the mapping to obtain the + // combined `memory_index` (also see previous comment). + combined_inverse_memory_index.retain(|&i| i != INVALID_FIELD_IDX); + let combined_memory_index = invert_mapping(&combined_inverse_memory_index); + + variant.fields = FieldsShape::Arbitrary { + offsets: combined_offsets, + memory_index: combined_memory_index, + }; + + size = size.max(variant.size); + align = align.max(variant.align); + Ok(variant) + }) + .collect::, _>>()?; + + size = size.align_to(align.abi); + + let abi = if prefix.abi.is_uninhabited() || variants.iter().all(|v| v.abi.is_uninhabited()) + { + Abi::Uninhabited + } else { + Abi::Aggregate { sized: true } + }; + + let layout = tcx.intern_layout(Layout { + variants: Variants::Multiple { + discr, + discr_kind: DiscriminantKind::Tag, + discr_index, + variants, + }, + fields: outer_fields, + abi, + largest_niche: prefix.largest_niche, + size, + align, + }); + debug!("generator layout ({:?}): {:#?}", ty, layout); + Ok(layout) + } + + /// This is invoked by the `layout_raw` query to record the final + /// layout of each type. + #[inline(always)] + fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) { + // If we are running with `-Zprint-type-sizes`, maybe record layouts + // for dumping later. + if self.tcx.sess.opts.debugging_opts.print_type_sizes { + self.record_layout_for_printing_outlined(layout) + } + } + + fn record_layout_for_printing_outlined(&self, layout: TyAndLayout<'tcx>) { + // Ignore layouts that are done with non-empty environments or + // non-monomorphic layouts, as the user only wants to see the stuff + // resulting from the final codegen session. + if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds.is_empty() { + return; + } + + // (delay format until we actually need it) + let record = |kind, packed, opt_discr_size, variants| { + let type_desc = format!("{:?}", layout.ty); + self.tcx.sess.code_stats.record_type_size( + kind, + type_desc, + layout.align.abi, + layout.size, + packed, + opt_discr_size, + variants, + ); + }; + + let adt_def = match layout.ty.kind { + ty::Adt(ref adt_def, _) => { + debug!("print-type-size t: `{:?}` process adt", layout.ty); + adt_def + } + + ty::Closure(..) => { + debug!("print-type-size t: `{:?}` record closure", layout.ty); + record(DataTypeKind::Closure, false, None, vec![]); + return; + } + + _ => { + debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); + return; + } + }; + + let adt_kind = adt_def.adt_kind(); + let adt_packed = adt_def.repr.pack.is_some(); + + let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { + let mut min_size = Size::ZERO; + let field_info: Vec<_> = flds + .iter() + .enumerate() + .map(|(i, &name)| match layout.field(self, i) { + Err(err) => { + bug!("no layout found for field {}: `{:?}`", name, err); + } + Ok(field_layout) => { + let offset = layout.fields.offset(i); + let field_end = offset + field_layout.size; + if min_size < field_end { + min_size = field_end; + } + FieldInfo { + name: name.to_string(), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + } + }) + .collect(); + + VariantInfo { + name: n.map(|n| n.to_string()), + kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, + align: layout.align.abi.bytes(), + size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, + fields: field_info, + } + }; + + match layout.variants { + Variants::Single { index } => { + debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variants[index].ident); + if !adt_def.variants.is_empty() { + let variant_def = &adt_def.variants[index]; + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.ident.name).collect(); + record( + adt_kind.into(), + adt_packed, + None, + vec![build_variant_info(Some(variant_def.ident), &fields, layout)], + ); + } else { + // (This case arises for *empty* enums; so give it + // zero variants.) + record(adt_kind.into(), adt_packed, None, vec![]); + } + } + + Variants::Multiple { ref discr, ref discr_kind, .. } => { + debug!( + "print-type-size `{:#?}` adt general variants def {}", + layout.ty, + adt_def.variants.len() + ); + let variant_infos: Vec<_> = adt_def + .variants + .iter_enumerated() + .map(|(i, variant_def)| { + let fields: Vec<_> = + variant_def.fields.iter().map(|f| f.ident.name).collect(); + build_variant_info( + Some(variant_def.ident), + &fields, + layout.for_variant(self, i), + ) + }) + .collect(); + record( + adt_kind.into(), + adt_packed, + match discr_kind { + DiscriminantKind::Tag => Some(discr.value.size(self)), + _ => None, + }, + variant_infos, + ); + } + } + } +} + +/// Type size "skeleton", i.e., the only information determining a type's size. +/// While this is conservative, (aside from constant sizes, only pointers, +/// newtypes thereof and null pointer optimized enums are allowed), it is +/// enough to statically check common use cases of transmute. +#[derive(Copy, Clone, Debug)] +pub enum SizeSkeleton<'tcx> { + /// Any statically computable Layout. + Known(Size), + + /// A potentially-fat pointer. + Pointer { + /// If true, this pointer is never null. + non_zero: bool, + /// The type which determines the unsized metadata, if any, + /// of this pointer. Either a type parameter or a projection + /// depending on one, with regions erased. + tail: Ty<'tcx>, + }, +} + +impl<'tcx> SizeSkeleton<'tcx> { + pub fn compute( + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result, LayoutError<'tcx>> { + debug_assert!(!ty.has_infer_types_or_consts()); + + // First try computing a static layout. + let err = match tcx.layout_of(param_env.and(ty)) { + Ok(layout) => { + return Ok(SizeSkeleton::Known(layout.size)); + } + Err(err) => err, + }; + + match ty.kind { + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let non_zero = !ty.is_unsafe_ptr(); + let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + match tail.kind { + ty::Param(_) | ty::Projection(_) => { + debug_assert!(tail.has_param_types_or_consts()); + Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(&tail) }) + } + _ => bug!( + "SizeSkeleton::compute({}): layout errored ({}), yet \ + tail `{}` is not a type parameter or a projection", + ty, + err, + tail + ), + } + } + + ty::Adt(def, substs) => { + // Only newtypes and enums w/ nullable pointer optimization. + if def.is_union() || def.variants.is_empty() || def.variants.len() > 2 { + return Err(err); + } + + // Get a zero-sized variant or a pointer newtype. + let zero_or_ptr_variant = |i| { + let i = VariantIdx::new(i); + let fields = def.variants[i] + .fields + .iter() + .map(|field| SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)); + let mut ptr = None; + for field in fields { + let field = field?; + match field { + SizeSkeleton::Known(size) => { + if size.bytes() > 0 { + return Err(err); + } + } + SizeSkeleton::Pointer { .. } => { + if ptr.is_some() { + return Err(err); + } + ptr = Some(field); + } + } + } + Ok(ptr) + }; + + let v0 = zero_or_ptr_variant(0)?; + // Newtype. + if def.variants.len() == 1 { + if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { + return Ok(SizeSkeleton::Pointer { + non_zero: non_zero + || match tcx.layout_scalar_valid_range(def.did) { + (Bound::Included(start), Bound::Unbounded) => start > 0, + (Bound::Included(start), Bound::Included(end)) => { + 0 < start && start < end + } + _ => false, + }, + tail, + }); + } else { + return Err(err); + } + } + + let v1 = zero_or_ptr_variant(1)?; + // Nullable pointer enum optimization. + match (v0, v1) { + (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) + | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => { + Ok(SizeSkeleton::Pointer { non_zero: false, tail }) + } + _ => Err(err), + } + } + + ty::Projection(_) | ty::Opaque(..) => { + let normalized = tcx.normalize_erasing_regions(param_env, ty); + if ty == normalized { + Err(err) + } else { + SizeSkeleton::compute(normalized, tcx, param_env) + } + } + + _ => Err(err), + } + } + + pub fn same_size(self, other: SizeSkeleton<'_>) -> bool { + match (self, other) { + (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b, + (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { + a == b + } + _ => false, + } + } +} + +pub trait HasTyCtxt<'tcx>: HasDataLayout { + fn tcx(&self) -> TyCtxt<'tcx>; +} + +pub trait HasParamEnv<'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx>; +} + +impl<'tcx> HasDataLayout for TyCtxt<'tcx> { + fn data_layout(&self) -> &TargetDataLayout { + &self.data_layout + } +} + +impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + *self + } +} + +impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> { + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } +} + +impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> { + fn data_layout(&self) -> &TargetDataLayout { + self.tcx.data_layout() + } +} + +impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx.tcx() + } +} + +pub type TyAndLayout<'tcx> = ::rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; + +impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { + type Ty = Ty<'tcx>; + type TyAndLayout = Result, LayoutError<'tcx>>; + + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + let param_env = self.param_env.with_reveal_all(); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); + let layout = self.tcx.layout_raw(param_env.and(ty))?; + let layout = TyAndLayout { ty, layout }; + + // N.B., this recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admittedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + self.record_layout_for_printing(layout); + + Ok(layout) + } +} + +impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { + type Ty = Ty<'tcx>; + type TyAndLayout = Result, LayoutError<'tcx>>; + + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + let param_env = self.param_env.with_reveal_all(); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); + let layout = self.tcx.layout_raw(param_env.and(ty))?; + let layout = TyAndLayout { ty, layout }; + + // N.B., this recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admittedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + let cx = LayoutCx { tcx: *self.tcx, param_env: self.param_env }; + cx.record_layout_for_printing(layout); + + Ok(layout) + } +} + +// Helper (inherent) `layout_of` methods to avoid pushing `LayoutCx` to users. +impl TyCtxt<'tcx> { + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] + pub fn layout_of( + self, + param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ) -> Result, LayoutError<'tcx>> { + let cx = LayoutCx { tcx: self, param_env: param_env_and_ty.param_env }; + cx.layout_of(param_env_and_ty.value) + } +} + +impl ty::query::TyCtxtAt<'tcx> { + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] + pub fn layout_of( + self, + param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ) -> Result, LayoutError<'tcx>> { + let cx = LayoutCx { tcx: self.at(self.span), param_env: param_env_and_ty.param_env }; + cx.layout_of(param_env_and_ty.value) + } +} + +impl<'tcx, C> TyAndLayoutMethods<'tcx, C> for Ty<'tcx> +where + C: LayoutOf, TyAndLayout: MaybeResult>> + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + fn for_variant( + this: TyAndLayout<'tcx>, + cx: &C, + variant_index: VariantIdx, + ) -> TyAndLayout<'tcx> { + let layout = match this.variants { + Variants::Single { index } + // If all variants but one are uninhabited, the variant layout is the enum layout. + if index == variant_index && + // Don't confuse variants of uninhabited enums with the enum itself. + // For more details see https://github.com/rust-lang/rust/issues/69763. + this.fields != FieldsShape::Primitive => + { + this.layout + } + + Variants::Single { index } => { + // Deny calling for_variant more than once for non-Single enums. + if let Ok(original_layout) = cx.layout_of(this.ty).to_result() { + assert_eq!(original_layout.variants, Variants::Single { index }); + } + + let fields = match this.ty.kind { + ty::Adt(def, _) => def.variants[variant_index].fields.len(), + _ => bug!(), + }; + let tcx = cx.tcx(); + tcx.intern_layout(Layout { + variants: Variants::Single { index: variant_index }, + fields: match NonZeroUsize::new(fields) { + Some(fields) => FieldsShape::Union(fields), + None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] }, + }, + abi: Abi::Uninhabited, + largest_niche: None, + align: tcx.data_layout.i8_align, + size: Size::ZERO, + }) + } + + Variants::Multiple { ref variants, .. } => &variants[variant_index], + }; + + assert_eq!(layout.variants, Variants::Single { index: variant_index }); + + TyAndLayout { ty: this.ty, layout } + } + + fn field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> C::TyAndLayout { + let tcx = cx.tcx(); + let discr_layout = |discr: &Scalar| -> C::TyAndLayout { + let layout = Layout::scalar(cx, discr.clone()); + MaybeResult::from(Ok(TyAndLayout { + layout: tcx.intern_layout(layout), + ty: discr.value.to_ty(tcx), + })) + }; + + cx.layout_of(match this.ty.kind { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::FnPtr(_) + | ty::Never + | ty::FnDef(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this), + + // Potentially-fat pointers. + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + assert!(i < this.fields.count()); + + // Reuse the fat `*T` type as its own thin pointer data field. + // This provides information about, e.g., DST struct pointees + // (which may have no non-DST form), and will work as long + // as the `Abi` or `FieldsShape` is checked by users. + if i == 0 { + let nil = tcx.mk_unit(); + let ptr_ty = if this.ty.is_unsafe_ptr() { + tcx.mk_mut_ptr(nil) + } else { + tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) + }; + return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map( + |mut ptr_layout| { + ptr_layout.ty = this.ty; + ptr_layout + }, + )); + } + + match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind { + ty::Slice(_) | ty::Str => tcx.types.usize, + ty::Dynamic(_, _) => { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) + /* FIXME: use actual fn pointers + Warning: naively computing the number of entries in the + vtable by counting the methods on the trait + methods on + all parent traits does not work, because some methods can + be not object safe and thus excluded from the vtable. + Increase this counter if you tried to implement this but + failed to do it without duplicating a lot of code from + other places in the compiler: 2 + tcx.mk_tup(&[ + tcx.mk_array(tcx.types.usize, 3), + tcx.mk_array(Option), + ]) + */ + } + _ => bug!("TyAndLayout::field_type({:?}): not applicable", this), + } + } + + // Arrays and slices. + ty::Array(element, _) | ty::Slice(element) => element, + ty::Str => tcx.types.u8, + + // Tuples, generators and closures. + ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().nth(i).unwrap(), + + ty::Generator(def_id, ref substs, _) => match this.variants { + Variants::Single { index } => substs + .as_generator() + .state_tys(def_id, tcx) + .nth(index.as_usize()) + .unwrap() + .nth(i) + .unwrap(), + Variants::Multiple { ref discr, discr_index, .. } => { + if i == discr_index { + return discr_layout(discr); + } + substs.as_generator().prefix_tys().nth(i).unwrap() + } + }, + + ty::Tuple(tys) => tys[i].expect_ty(), + + // SIMD vector types. + ty::Adt(def, ..) if def.repr.simd() => this.ty.simd_type(tcx), + + // ADTs. + ty::Adt(def, substs) => { + match this.variants { + Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs), + + // Discriminant field for enums (where applicable). + Variants::Multiple { ref discr, .. } => { + assert_eq!(i, 0); + return discr_layout(discr); + } + } + } + + ty::Projection(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Opaque(..) + | ty::Param(_) + | ty::Infer(_) + | ty::Error => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), + }) + } + + fn pointee_info_at(this: TyAndLayout<'tcx>, cx: &C, offset: Size) -> Option { + match this.ty.kind { + ty::RawPtr(mt) if offset.bytes() == 0 => { + cx.layout_of(mt.ty).to_result().ok().map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: None, + }) + } + + ty::Ref(_, ty, mt) if offset.bytes() == 0 => { + let tcx = cx.tcx(); + let is_freeze = ty.is_freeze(tcx, cx.param_env(), DUMMY_SP); + let kind = match mt { + hir::Mutability::Not => { + if is_freeze { + PointerKind::Frozen + } else { + PointerKind::Shared + } + } + hir::Mutability::Mut => { + // Previously we would only emit noalias annotations for LLVM >= 6 or in + // panic=abort mode. That was deemed right, as prior versions had many bugs + // in conjunction with unwinding, but later versions didn’t seem to have + // said issues. See issue #31681. + // + // Alas, later on we encountered a case where noalias would generate wrong + // code altogether even with recent versions of LLVM in *safe* code with no + // unwinding involved. See #54462. + // + // For now, do not enable mutable_noalias by default at all, while the + // issue is being figured out. + if tcx.sess.opts.debugging_opts.mutable_noalias { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } + } + }; + + cx.layout_of(ty).to_result().ok().map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: Some(kind), + }) + } + + _ => { + let mut data_variant = match this.variants { + // Within the discriminant field, only the niche itself is + // always initialized, so we only check for a pointer at its + // offset. + // + // If the niche is a pointer, it's either valid (according + // to its type), or null (which the niche field's scalar + // validity range encodes). This allows using + // `dereferenceable_or_null` for e.g., `Option<&T>`, and + // this will continue to work as long as we don't start + // using more niches than just null (e.g., the first page of + // the address space, or unaligned pointers). + Variants::Multiple { + discr_kind: DiscriminantKind::Niche { dataful_variant, .. }, + discr_index, + .. + } if this.fields.offset(discr_index) == offset => { + Some(this.for_variant(cx, dataful_variant)) + } + _ => Some(this), + }; + + if let Some(variant) = data_variant { + // We're not interested in any unions. + if let FieldsShape::Union(_) = variant.fields { + data_variant = None; + } + } + + let mut result = None; + + if let Some(variant) = data_variant { + let ptr_end = offset + Pointer.size(cx); + for i in 0..variant.fields.count() { + let field_start = variant.fields.offset(i); + if field_start <= offset { + let field = variant.field(cx, i); + result = field.to_result().ok().and_then(|field| { + if ptr_end <= field_start + field.size { + // We found the right field, look inside it. + field.pointee_info_at(cx, offset - field_start) + } else { + None + } + }); + if result.is_some() { + break; + } + } + } + } + + // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. + if let Some(ref mut pointee) = result { + if let ty::Adt(def, _) = this.ty.kind { + if def.is_box() && offset.bytes() == 0 { + pointee.safe = Some(PointerKind::UniqueOwned); + } + } + } + + result + } + } + } +} + +impl<'a, 'tcx> HashStable> for LayoutError<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + use crate::ty::layout::LayoutError::*; + mem::discriminant(self).hash_stable(hcx, hasher); + + match *self { + Unknown(t) | SizeOverflow(t) => t.hash_stable(hcx, hasher), + } + } +} + +impl<'tcx> ty::Instance<'tcx> { + // NOTE(eddyb) this is private to avoid using it from outside of + // `FnAbi::of_instance` - any other uses are either too high-level + // for `Instance` (e.g. typeck would use `Ty::fn_sig` instead), + // or should go through `FnAbi` instead, to avoid losing any + // adjustments `FnAbi::of_instance` might be performing. + fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { + let ty = self.monomorphic_ty(tcx); + match ty.kind { + ty::FnDef(..) | + // Shims currently have type FnPtr. Not sure this should remain. + ty::FnPtr(_) => { + let mut sig = ty.fn_sig(tcx); + if let ty::InstanceDef::VtableShim(..) = self.def { + // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. + sig = sig.map_bound(|mut sig| { + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]); + sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output); + sig + }); + } + sig + } + ty::Closure(def_id, substs) => { + let sig = substs.as_closure().sig(); + + let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); + sig.map_bound(|sig| tcx.mk_fn_sig( + iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.c_variadic, + sig.unsafety, + sig.abi + )) + } + ty::Generator(_, substs, _) => { + let sig = substs.as_generator().poly_sig(); + + let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); + + let pin_did = tcx.require_lang_item(PinTypeLangItem, None); + let pin_adt_ref = tcx.adt_def(pin_did); + let pin_substs = tcx.intern_substs(&[env_ty.into()]); + let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs); + + sig.map_bound(|sig| { + let state_did = tcx.require_lang_item(GeneratorStateLangItem, None); + let state_adt_ref = tcx.adt_def(state_did); + let state_substs = tcx.intern_substs(&[ + sig.yield_ty.into(), + sig.return_ty.into(), + ]); + let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); + + tcx.mk_fn_sig( + [env_ty, sig.resume_ty].iter(), + &ret_ty, + false, + hir::Unsafety::Normal, + rustc_target::spec::abi::Abi::Rust + ) + }) + } + _ => bug!("unexpected type {:?} in Instance::fn_sig", ty) + } + } +} + +pub trait FnAbiExt<'tcx, C> +where + C: LayoutOf, TyAndLayout = TyAndLayout<'tcx>> + + HasDataLayout + + HasTargetSpec + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. + /// + /// NB: this doesn't handle virtual calls - those should use `FnAbi::of_instance` + /// instead, where the instance is a `InstanceDef::Virtual`. + fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for + /// direct calls to an `fn`. + /// + /// NB: that includes virtual calls, which are represented by "direct calls" + /// to a `InstanceDef::Virtual` instance (of `::fn`). + fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; + + fn new_internal( + cx: &C, + sig: ty::PolyFnSig<'tcx>, + extra_args: &[Ty<'tcx>], + caller_location: Option>, + codegen_fn_attr_flags: CodegenFnAttrFlags, + mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, + ) -> Self; + fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi); +} + +fn fn_can_unwind( + panic_strategy: PanicStrategy, + codegen_fn_attr_flags: CodegenFnAttrFlags, + call_conv: Conv, +) -> bool { + if panic_strategy != PanicStrategy::Unwind { + // In panic=abort mode we assume nothing can unwind anywhere, so + // optimize based on this! + false + } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::UNWIND) { + // If a specific #[unwind] attribute is present, use that. + true + } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { + // Special attribute for allocator functions, which can't unwind. + false + } else { + if call_conv == Conv::Rust { + // Any Rust method (or `extern "Rust" fn` or `extern + // "rust-call" fn`) is explicitly allowed to unwind + // (unless it has no-unwind attribute, handled above). + true + } else { + // Anything else is either: + // + // 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or + // + // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`). + // + // Foreign items (case 1) are assumed to not unwind; it is + // UB otherwise. (At least for now; see also + // rust-lang/rust#63909 and Rust RFC 2753.) + // + // Items defined in Rust with non-Rust ABIs (case 2) are also + // not supposed to unwind. Whether this should be enforced + // (versus stating it is UB) and *how* it would be enforced + // is currently under discussion; see rust-lang/rust#58794. + // + // In either case, we mark item as explicitly nounwind. + false + } + } +} + +impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>> +where + C: LayoutOf, TyAndLayout = TyAndLayout<'tcx>> + + HasDataLayout + + HasTargetSpec + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { + // Assume that fn pointers may always unwind + let codegen_fn_attr_flags = CodegenFnAttrFlags::UNWIND; + + call::FnAbi::new_internal(cx, sig, extra_args, None, codegen_fn_attr_flags, |ty, _| { + ArgAbi::new(cx.layout_of(ty)) + }) + } + + fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { + let sig = instance.fn_sig_for_fn_abi(cx.tcx()); + + let caller_location = if instance.def.requires_caller_location(cx.tcx()) { + Some(cx.tcx().caller_location_ty()) + } else { + None + }; + + let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()).flags; + + call::FnAbi::new_internal(cx, sig, extra_args, caller_location, attrs, |ty, arg_idx| { + let mut layout = cx.layout_of(ty); + // Don't pass the vtable, it's not an argument of the virtual fn. + // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` + // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen + if let (ty::InstanceDef::Virtual(..), Some(0)) = (&instance.def, arg_idx) { + let fat_pointer_ty = if layout.is_unsized() { + // unsized `self` is passed as a pointer to `self` + // FIXME (mikeyhew) change this to use &own if it is ever added to the language + cx.tcx().mk_mut_ptr(layout.ty) + } else { + match layout.abi { + Abi::ScalarPair(..) => (), + _ => bug!("receiver type has unsupported layout: {:?}", layout), + } + + // In the case of Rc, we need to explicitly pass a *mut RcBox + // with a Scalar (not ScalarPair) ABI. This is a hack that is understood + // elsewhere in the compiler as a method on a `dyn Trait`. + // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we + // get a built-in pointer type + let mut fat_pointer_layout = layout; + 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() + && !fat_pointer_layout.ty.is_region_ptr() + { + for i in 0..fat_pointer_layout.fields.count() { + let field_layout = fat_pointer_layout.field(cx, i); + + if !field_layout.is_zst() { + fat_pointer_layout = field_layout; + continue 'descend_newtypes; + } + } + + bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout); + } + + fat_pointer_layout.ty + }; + + // we now have a type like `*mut RcBox` + // change its layout to that of `*mut ()`, a thin pointer, but keep the same type + // this is understood as a special case elsewhere in the compiler + let unit_pointer_ty = cx.tcx().mk_mut_ptr(cx.tcx().mk_unit()); + layout = cx.layout_of(unit_pointer_ty); + layout.ty = fat_pointer_ty; + } + ArgAbi::new(layout) + }) + } + + fn new_internal( + cx: &C, + sig: ty::PolyFnSig<'tcx>, + extra_args: &[Ty<'tcx>], + caller_location: Option>, + codegen_fn_attr_flags: CodegenFnAttrFlags, + mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, + ) -> Self { + debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args); + + let sig = cx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + + use rustc_target::spec::abi::Abi::*; + let conv = match cx.tcx().sess.target.target.adjust_abi(sig.abi) { + RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust, + + // It's the ABI's job to select this, not ours. + System => bug!("system abi should be selected elsewhere"), + EfiApi => bug!("eficall abi should be selected elsewhere"), + + Stdcall => Conv::X86Stdcall, + Fastcall => Conv::X86Fastcall, + Vectorcall => Conv::X86VectorCall, + Thiscall => Conv::X86ThisCall, + C => Conv::C, + Unadjusted => Conv::C, + Win64 => Conv::X86_64Win64, + SysV64 => Conv::X86_64SysV, + Aapcs => Conv::ArmAapcs, + PtxKernel => Conv::PtxKernel, + Msp430Interrupt => Conv::Msp430Intr, + X86Interrupt => Conv::X86Intr, + AmdGpuKernel => Conv::AmdGpuKernel, + + // These API constants ought to be more specific... + Cdecl => Conv::C, + }; + + let mut inputs = sig.inputs(); + let extra_args = if sig.abi == RustCall { + assert!(!sig.c_variadic && extra_args.is_empty()); + + if let Some(input) = sig.inputs().last() { + if let ty::Tuple(tupled_arguments) = input.kind { + inputs = &sig.inputs()[0..sig.inputs().len() - 1]; + tupled_arguments.iter().map(|k| k.expect_ty()).collect() + } else { + bug!( + "argument to function with \"rust-call\" ABI \ + is not a tuple" + ); + } + } else { + bug!( + "argument to function with \"rust-call\" ABI \ + is not a tuple" + ); + } + } else { + assert!(sig.c_variadic || extra_args.is_empty()); + extra_args.to_vec() + }; + + let target = &cx.tcx().sess.target.target; + let target_env_gnu_like = matches!(&target.target_env[..], "gnu" | "musl"); + let win_x64_gnu = + target.target_os == "windows" && target.arch == "x86_64" && target.target_env == "gnu"; + let linux_s390x_gnu_like = + target.target_os == "linux" && target.arch == "s390x" && target_env_gnu_like; + let linux_sparc64_gnu_like = + target.target_os == "linux" && target.arch == "sparc64" && target_env_gnu_like; + let linux_powerpc_gnu_like = + target.target_os == "linux" && target.arch == "powerpc" && target_env_gnu_like; + let rust_abi = match sig.abi { + RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, + _ => false, + }; + + // Handle safe Rust thin and fat pointers. + let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, + scalar: &Scalar, + layout: TyAndLayout<'tcx>, + offset: Size, + is_return: bool| { + // Booleans are always an i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.set(ArgAttribute::ZExt); + return; + } + + // Only pointer types handled below. + if scalar.value != Pointer { + return; + } + + if scalar.valid_range.start() < scalar.valid_range.end() { + if *scalar.valid_range.start() > 0 { + attrs.set(ArgAttribute::NonNull); + } + } + + if let Some(pointee) = layout.pointee_info_at(cx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_align = Some(pointee.align); + + // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable + // for the entire duration of the function as they can be deallocated + // at any time. Set their valid size to 0. + attrs.pointee_size = match kind { + PointerKind::UniqueOwned => Size::ZERO, + _ => pointee.size, + }; + + // `Box` pointer parameters never alias because ownership is transferred + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + let no_alias = match kind { + PointerKind::Shared => false, + PointerKind::UniqueOwned => true, + PointerKind::Frozen | PointerKind::UniqueBorrowed => !is_return, + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } + + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } + } + } + }; + + let arg_of = |ty: Ty<'tcx>, arg_idx: Option| { + let is_return = arg_idx.is_none(); + let mut arg = mk_arg_type(ty, arg_idx); + if arg.layout.is_zst() { + // For some forsaken reason, x86_64-pc-windows-gnu + // doesn't ignore zero-sized struct arguments. + // The same is true for {s390x,sparc64,powerpc}-unknown-linux-{gnu,musl}. + if is_return + || rust_abi + || (!win_x64_gnu + && !linux_s390x_gnu_like + && !linux_sparc64_gnu_like + && !linux_powerpc_gnu_like) + { + arg.mode = PassMode::Ignore; + } + } + + // FIXME(eddyb) other ABIs don't have logic for scalar pairs. + if !is_return && rust_abi { + if let Abi::ScalarPair(ref a, ref b) = arg.layout.abi { + let mut a_attrs = ArgAttributes::new(); + let mut b_attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut a_attrs, a, arg.layout, Size::ZERO, false); + adjust_for_rust_scalar( + &mut b_attrs, + b, + arg.layout, + a.value.size(cx).align_to(b.value.align(cx).abi), + false, + ); + arg.mode = PassMode::Pair(a_attrs, b_attrs); + return arg; + } + } + + if let Abi::Scalar(ref scalar) = arg.layout.abi { + if let PassMode::Direct(ref mut attrs) = arg.mode { + adjust_for_rust_scalar(attrs, scalar, arg.layout, Size::ZERO, is_return); + } + } + + arg + }; + + let mut fn_abi = FnAbi { + ret: arg_of(sig.output(), None), + args: inputs + .iter() + .cloned() + .chain(extra_args) + .chain(caller_location) + .enumerate() + .map(|(i, ty)| arg_of(ty, Some(i))) + .collect(), + c_variadic: sig.c_variadic, + fixed_count: inputs.len(), + conv, + can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), + }; + fn_abi.adjust_for_abi(cx, sig.abi); + fn_abi + } + + fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi) { + if abi == SpecAbi::Unadjusted { + return; + } + + if abi == SpecAbi::Rust + || abi == SpecAbi::RustCall + || abi == SpecAbi::RustIntrinsic + || abi == SpecAbi::PlatformIntrinsic + { + let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { + if arg.is_ignore() { + return; + } + + match arg.layout.abi { + Abi::Aggregate { .. } => {} + + // This is a fun case! The gist of what this is doing is + // that we want callers and callees to always agree on the + // ABI of how they pass SIMD arguments. If we were to *not* + // make these arguments indirect then they'd be immediates + // in LLVM, which means that they'd used whatever the + // appropriate ABI is for the callee and the caller. That + // means, for example, if the caller doesn't have AVX + // enabled but the callee does, then passing an AVX argument + // across this boundary would cause corrupt data to show up. + // + // This problem is fixed by unconditionally passing SIMD + // arguments through memory between callers and callees + // which should get them all to agree on ABI regardless of + // target feature sets. Some more information about this + // issue can be found in #44367. + // + // Note that the platform intrinsic ABI is exempt here as + // that's how we connect up to LLVM and it's unstable + // anyway, we control all calls to it in libstd. + Abi::Vector { .. } + if abi != SpecAbi::PlatformIntrinsic + && cx.tcx().sess.target.target.options.simd_types_indirect => + { + arg.make_indirect(); + return; + } + + _ => return, + } + + let size = arg.layout.size; + if arg.layout.is_unsized() || size > Pointer.size(cx) { + arg.make_indirect(); + } else { + // We want to pass small aggregates as immediates, but using + // a LLVM aggregate type for this leads to bad optimizations, + // so we pick an appropriately sized integer type instead. + arg.cast_to(Reg { kind: RegKind::Integer, size }); + } + }; + fixup(&mut self.ret); + for arg in &mut self.args { + fixup(arg); + } + if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); + } + return; + } + + if let Err(msg) = self.adjust_for_cabi(cx, abi) { + cx.tcx().sess.fatal(&msg); + } + } +} diff --git a/src/librustc_middle/ty/list.rs b/src/librustc_middle/ty/list.rs new file mode 100644 index 0000000000000..6427c547a8f29 --- /dev/null +++ b/src/librustc_middle/ty/list.rs @@ -0,0 +1,149 @@ +use crate::arena::Arena; + +use rustc_serialize::{Encodable, Encoder}; + +use std::cmp::{self, Ordering}; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::mem; +use std::ops::Deref; +use std::ptr; +use std::slice; + +extern "C" { + /// A dummy type used to force `List` to be unsized while not requiring references to it be wide + /// pointers. + type OpaqueListContents; +} + +/// A wrapper for slices with the additional invariant +/// that the slice is interned and no other slice with +/// the same contents can exist in the same context. +/// This means we can use pointer for both +/// equality comparisons and hashing. +/// Note: `Slice` was already taken by the `Ty`. +#[repr(C)] +pub struct List { + len: usize, + data: [T; 0], + opaque: OpaqueListContents, +} + +unsafe impl Sync for List {} + +impl List { + #[inline] + pub(super) fn from_arena<'tcx>(arena: &'tcx Arena<'tcx>, slice: &[T]) -> &'tcx List { + assert!(!mem::needs_drop::()); + assert!(mem::size_of::() != 0); + assert!(!slice.is_empty()); + + // Align up the size of the len (usize) field + let align = mem::align_of::(); + let align_mask = align - 1; + let offset = mem::size_of::(); + let offset = (offset + align_mask) & !align_mask; + + let size = offset + slice.len() * mem::size_of::(); + + let mem = arena + .dropless + .alloc_raw(size, cmp::max(mem::align_of::(), mem::align_of::())); + unsafe { + let result = &mut *(mem.as_mut_ptr() as *mut List); + // Write the length + result.len = slice.len(); + + // Write the elements + let arena_slice = slice::from_raw_parts_mut(result.data.as_mut_ptr(), result.len); + arena_slice.copy_from_slice(slice); + + result + } + } +} + +impl fmt::Debug for List { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl Encodable for List { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} + +impl Ord for List +where + T: Ord, +{ + fn cmp(&self, other: &List) -> Ordering { + if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) } + } +} + +impl PartialOrd for List +where + T: PartialOrd, +{ + fn partial_cmp(&self, other: &List) -> Option { + if self == other { + Some(Ordering::Equal) + } else { + <[T] as PartialOrd>::partial_cmp(&**self, &**other) + } + } +} + +impl PartialEq for List { + #[inline] + fn eq(&self, other: &List) -> bool { + ptr::eq(self, other) + } +} +impl Eq for List {} + +impl Hash for List { + #[inline] + fn hash(&self, s: &mut H) { + (self as *const List).hash(s) + } +} + +impl Deref for List { + type Target = [T]; + #[inline(always)] + fn deref(&self) -> &[T] { + self.as_ref() + } +} + +impl AsRef<[T]> for List { + #[inline(always)] + fn as_ref(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.data.as_ptr(), self.len) } + } +} + +impl<'a, T> IntoIterator for &'a List { + type Item = &'a T; + type IntoIter = <&'a [T] as IntoIterator>::IntoIter; + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + self[..].iter() + } +} + +impl List { + #[inline(always)] + pub fn empty<'a>() -> &'a List { + #[repr(align(64), C)] + struct EmptySlice([u8; 64]); + static EMPTY_SLICE: EmptySlice = EmptySlice([0; 64]); + assert!(mem::align_of::() <= 64); + unsafe { &*(&EMPTY_SLICE as *const _ as *const List) } + } +} diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs new file mode 100644 index 0000000000000..36bc44f5e5032 --- /dev/null +++ b/src/librustc_middle/ty/mod.rs @@ -0,0 +1,2858 @@ +pub use self::fold::{TypeFoldable, TypeVisitor}; +pub use self::AssocItemContainer::*; +pub use self::BorrowKind::*; +pub use self::IntVarValue::*; +pub use self::Variance::*; + +use crate::hir::exports::ExportMap; +use crate::ich::StableHashingContext; +use crate::infer::canonical::Canonical; +use crate::middle::cstore::CrateStoreDyn; +use crate::middle::resolve_lifetime::ObjectLifetimeDefault; +use crate::mir::interpret::ErrorHandled; +use crate::mir::Body; +use crate::mir::GeneratorLayout; +use crate::traits::{self, Reveal}; +use crate::ty; +use crate::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use crate::ty::util::{Discr, IntTypeExt}; +use rustc_ast::ast; +use rustc_ast::node_id::{NodeId, NodeMap, NodeSet}; +use rustc_attr as attr; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::sorted_map::SortedIndexMultiMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{self, par_iter, ParallelIterator}; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::lang_items::{FnMutTraitLangItem, FnOnceTraitLangItem, FnTraitLangItem}; +use rustc_hir::{Constness, GlobMap, Node, TraitMap}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_macros::HashStable; +use rustc_serialize::{self, Encodable, Encoder}; +use rustc_session::DataTypeKind; +use rustc_span::hygiene::ExpnId; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::Span; +use rustc_target::abi::{Align, VariantIdx}; + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::ops::Range; +use std::ptr; + +pub use self::sty::BoundRegion::*; +pub use self::sty::InferTy::*; +pub use self::sty::RegionKind; +pub use self::sty::RegionKind::*; +pub use self::sty::TyKind::*; +pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNERMOST}; +pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; +pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; +pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; +pub use self::sty::{Const, ConstKind, ExistentialProjection, PolyExistentialProjection}; +pub use self::sty::{ConstVid, FloatVid, IntVid, RegionVid, TyVid}; +pub use self::sty::{ExistentialPredicate, InferConst, InferTy, ParamConst, ParamTy, ProjectionTy}; +pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; +pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; +pub use crate::ty::diagnostics::*; + +pub use self::binding::BindingMode; +pub use self::binding::BindingMode::*; + +pub use self::context::{tls, FreeRegionInfo, TyCtxt}; +pub use self::context::{ + CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, ResolvedOpaqueTy, + UserType, UserTypeAnnotationIndex, +}; +pub use self::context::{ + CtxtInterners, GeneratorInteriorTypeCause, GlobalCtxt, Lift, TypeckTables, +}; + +pub use self::instance::{Instance, InstanceDef}; + +pub use self::list::List; + +pub use self::trait_def::TraitDef; + +pub use self::query::queries; + +pub mod adjustment; +pub mod binding; +pub mod cast; +#[macro_use] +pub mod codec; +pub mod _match; +mod erase_regions; +pub mod error; +pub mod fast_reject; +pub mod flags; +pub mod fold; +pub mod inhabitedness; +pub mod layout; +pub mod normalize_erasing_regions; +pub mod outlives; +pub mod print; +pub mod query; +pub mod relate; +pub mod steal; +pub mod subst; +pub mod trait_def; +pub mod util; +pub mod walk; + +mod context; +mod diagnostics; +mod instance; +mod list; +mod structural_impls; +mod sty; + +// Data types + +pub struct ResolverOutputs { + pub definitions: rustc_hir::definitions::Definitions, + pub cstore: Box, + pub extern_crate_map: NodeMap, + pub trait_map: TraitMap, + pub maybe_unused_trait_imports: NodeSet, + pub maybe_unused_extern_crates: Vec<(NodeId, Span)>, + pub export_map: ExportMap, + pub glob_map: GlobMap, + /// Extern prelude entries. The value is `true` if the entry was introduced + /// via `extern crate` item and not `--extern` option or compiler built-in. + pub extern_prelude: FxHashMap, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)] +pub enum AssocItemContainer { + TraitContainer(DefId), + ImplContainer(DefId), +} + +impl AssocItemContainer { + /// Asserts that this is the `DefId` of an associated item declared + /// in a trait, and returns the trait `DefId`. + pub fn assert_trait(&self) -> DefId { + match *self { + TraitContainer(id) => id, + _ => bug!("associated item has wrong container type: {:?}", self), + } + } + + pub fn id(&self) -> DefId { + match *self { + TraitContainer(id) => id, + ImplContainer(id) => id, + } + } +} + +/// The "header" of an impl is everything outside the body: a Self type, a trait +/// ref (in the case of a trait impl), and a set of predicates (from the +/// bounds / where-clauses). +#[derive(Clone, Debug, TypeFoldable)] +pub struct ImplHeader<'tcx> { + pub impl_def_id: DefId, + pub self_ty: Ty<'tcx>, + pub trait_ref: Option>, + pub predicates: Vec>, +} + +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub enum ImplPolarity { + /// `impl Trait for Type` + Positive, + /// `impl !Trait for Type` + Negative, + /// `#[rustc_reservation_impl] impl Trait for Type` + /// + /// This is a "stability hack", not a real Rust feature. + /// See #64631 for details. + Reservation, +} + +#[derive(Copy, Clone, Debug, PartialEq, HashStable)] +pub struct AssocItem { + pub def_id: DefId, + #[stable_hasher(project(name))] + pub ident: Ident, + pub kind: AssocKind, + pub vis: Visibility, + pub defaultness: hir::Defaultness, + pub container: AssocItemContainer, + + /// Whether this is a method with an explicit self + /// as its first parameter, allowing method calls. + pub fn_has_self_parameter: bool, +} + +#[derive(Copy, Clone, PartialEq, Debug, HashStable)] +pub enum AssocKind { + Const, + Fn, + OpaqueTy, + Type, +} + +impl AssocKind { + pub fn namespace(&self) -> Namespace { + match *self { + ty::AssocKind::OpaqueTy | 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::OpaqueTy => DefKind::AssocOpaqueTy, + } + } +} + +impl AssocItem { + /// Tests whether the associated item admits a non-trivial implementation + /// for ! + pub fn relevant_for_never(&self) -> bool { + match self.kind { + AssocKind::OpaqueTy | AssocKind::Const | AssocKind::Type => true, + // FIXME(canndrew): Be more thorough here, check if any argument is uninhabited. + AssocKind::Fn => !self.fn_has_self_parameter, + } + } + + pub fn signature(&self, tcx: TyCtxt<'_>) -> String { + match self.kind { + ty::AssocKind::Fn => { + // We skip the binder here because the binder would deanonymize all + // late-bound regions, and we don't want method signatures to show up + // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound + // regions just fine, showing `fn(&MyType)`. + tcx.fn_sig(self.def_id).skip_binder().to_string() + } + ty::AssocKind::Type => format!("type {};", self.ident), + // FIXME(type_alias_impl_trait): we should print bounds here too. + ty::AssocKind::OpaqueTy => format!("type {};", self.ident), + ty::AssocKind::Const => { + format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) + } + } + } +} + +/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. +/// +/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since +/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is +/// done only on items with the same name. +#[derive(Debug, Clone, PartialEq, HashStable)] +pub struct AssociatedItems<'tcx> { + items: SortedIndexMultiMap, +} + +impl<'tcx> AssociatedItems<'tcx> { + /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. + pub fn new(items_in_def_order: impl IntoIterator) -> Self { + let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); + AssociatedItems { items } + } + + /// Returns a slice of associated items in the order they were defined. + /// + /// New code should avoid relying on definition order. If you need a particular associated item + /// for a known trait, make that trait a lang item instead of indexing this array. + pub fn in_definition_order(&self) -> impl '_ + Iterator { + self.items.iter().map(|(_, v)| *v) + } + + /// Returns an iterator over all associated items with the given name, ignoring hygiene. + pub fn filter_by_name_unhygienic( + &self, + name: Symbol, + ) -> impl '_ + Iterator { + self.items.get_by_key(&name).copied() + } + + /// Returns an iterator over all associated items with the given name. + /// + /// Multiple items may have the same name if they are in different `Namespace`s. For example, + /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` + /// methods below if you know which item you are looking for. + pub fn filter_by_name( + &'a self, + tcx: TyCtxt<'a>, + ident: Ident, + parent_def_id: DefId, + ) -> impl 'a + Iterator { + self.filter_by_name_unhygienic(ident.name) + .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name and `AssocKind`, if one exists. + pub fn find_by_name_and_kind( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + kind: AssocKind, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind == kind) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name in the given `Namespace`, if one exists. + pub fn find_by_name_and_namespace( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + ns: Namespace, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind.namespace() == ns) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable, HashStable)] +pub enum Visibility { + /// Visible everywhere (including in other crates). + Public, + /// Visible only in the given crate-local module. + Restricted(DefId), + /// Not visible anywhere in the local crate. This is the visibility of private external items. + Invisible, +} + +pub trait DefIdTree: Copy { + fn parent(self, id: DefId) -> Option; + + fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { + if descendant.krate != ancestor.krate { + return false; + } + + while descendant != ancestor { + match self.parent(descendant) { + Some(parent) => descendant = parent, + None => return false, + } + } + true + } +} + +impl<'tcx> DefIdTree for TyCtxt<'tcx> { + fn parent(self, id: DefId) -> Option { + self.def_key(id).parent.map(|index| DefId { index, ..id }) + } +} + +impl Visibility { + pub fn from_hir(visibility: &hir::Visibility<'_>, id: hir::HirId, tcx: TyCtxt<'_>) -> Self { + match visibility.node { + hir::VisibilityKind::Public => Visibility::Public, + hir::VisibilityKind::Crate(_) => Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)), + hir::VisibilityKind::Restricted { ref path, .. } => match path.res { + // If there is no resolution, `resolve` will have already reported an error, so + // assume that the visibility is public to avoid reporting more privacy errors. + Res::Err => Visibility::Public, + def => Visibility::Restricted(def.def_id()), + }, + hir::VisibilityKind::Inherited => { + Visibility::Restricted(tcx.parent_module(id).to_def_id()) + } + } + } + + /// Returns `true` if an item with this visibility is accessible from the given block. + pub fn is_accessible_from(self, module: DefId, tree: T) -> bool { + let restriction = match self { + // Public items are visible everywhere. + Visibility::Public => return true, + // Private items from other crates are visible nowhere. + Visibility::Invisible => return false, + // Restricted items are visible in an arbitrary local module. + Visibility::Restricted(other) if other.krate != module.krate => return false, + Visibility::Restricted(module) => module, + }; + + tree.is_descendant_of(module, restriction) + } + + /// Returns `true` if this visibility is at least as accessible as the given visibility + pub fn is_at_least(self, vis: Visibility, tree: T) -> bool { + let vis_restriction = match vis { + Visibility::Public => return self == Visibility::Public, + Visibility::Invisible => return true, + Visibility::Restricted(module) => module, + }; + + self.is_accessible_from(vis_restriction, tree) + } + + // Returns `true` if this item is visible anywhere in the local crate. + pub fn is_visible_locally(self) -> bool { + match self { + Visibility::Public => true, + Visibility::Restricted(def_id) => def_id.is_local(), + Visibility::Invisible => false, + } + } +} + +#[derive(Copy, Clone, PartialEq, RustcDecodable, RustcEncodable, HashStable)] +pub enum Variance { + Covariant, // T <: T iff A <: B -- e.g., function return type + Invariant, // T <: T iff B == A -- e.g., type of mutable cell + Contravariant, // T <: T iff B <: A -- e.g., function param type + Bivariant, // T <: T -- e.g., unused type parameter +} + +/// The crate variances map is computed during typeck and contains the +/// variance of every item in the local crate. You should not use it +/// directly, because to do so will make your pass dependent on the +/// HIR of every item in the local crate. Instead, use +/// `tcx.variances_of()` to get the variance for a *particular* +/// item. +#[derive(HashStable)] +pub struct CrateVariancesMap<'tcx> { + /// For each item with generics, maps to a vector of the variance + /// of its generics. If an item has no generics, it will have no + /// entry. + pub variances: FxHashMap, +} + +impl Variance { + /// `a.xform(b)` combines the variance of a context with the + /// variance of a type with the following meaning. If we are in a + /// context with variance `a`, and we encounter a type argument in + /// a position with variance `b`, then `a.xform(b)` is the new + /// variance with which the argument appears. + /// + /// Example 1: + /// + /// *mut Vec + /// + /// Here, the "ambient" variance starts as covariant. `*mut T` is + /// invariant with respect to `T`, so the variance in which the + /// `Vec` appears is `Covariant.xform(Invariant)`, which + /// yields `Invariant`. Now, the type `Vec` is covariant with + /// respect to its type argument `T`, and hence the variance of + /// the `i32` here is `Invariant.xform(Covariant)`, which results + /// (again) in `Invariant`. + /// + /// Example 2: + /// + /// fn(*const Vec, *mut Vec` appears is + /// `Contravariant.xform(Covariant)` or `Contravariant`. The same + /// is true for its `i32` argument. In the `*mut T` case, the + /// variance of `Vec` is `Contravariant.xform(Invariant)`, + /// and hence the outermost type is `Invariant` with respect to + /// `Vec` (and its `i32` argument). + /// + /// Source: Figure 1 of "Taming the Wildcards: + /// Combining Definition- and Use-Site Variance" published in PLDI'11. + pub fn xform(self, v: ty::Variance) -> ty::Variance { + match (self, v) { + // Figure 1, column 1. + (ty::Covariant, ty::Covariant) => ty::Covariant, + (ty::Covariant, ty::Contravariant) => ty::Contravariant, + (ty::Covariant, ty::Invariant) => ty::Invariant, + (ty::Covariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 2. + (ty::Contravariant, ty::Covariant) => ty::Contravariant, + (ty::Contravariant, ty::Contravariant) => ty::Covariant, + (ty::Contravariant, ty::Invariant) => ty::Invariant, + (ty::Contravariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 3. + (ty::Invariant, _) => ty::Invariant, + + // Figure 1, column 4. + (ty::Bivariant, _) => ty::Bivariant, + } + } +} + +// Contains information needed to resolve types and (in the future) look up +// the types of AST nodes. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct CReaderCacheKey { + pub cnum: CrateNum, + pub pos: usize, +} + +bitflags! { + /// Flags that we track on types. These flags are propagated upwards + /// through the type during type construction, so that we can quickly check + /// whether the type has various kinds of types in it without recursing + /// over the type itself. + pub struct TypeFlags: u32 { + // Does this have parameters? Used to determine whether substitution is + // required. + /// Does this have [Param]? + const HAS_TY_PARAM = 1 << 0; + /// Does this have [ReEarlyBound]? + const HAS_RE_PARAM = 1 << 1; + /// Does this have [ConstKind::Param]? + const HAS_CT_PARAM = 1 << 2; + + const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_RE_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits; + + /// Does this have [Infer]? + const HAS_TY_INFER = 1 << 3; + /// Does this have [ReVar]? + const HAS_RE_INFER = 1 << 4; + /// Does this have [ConstKind::Infer]? + const HAS_CT_INFER = 1 << 5; + + /// Does this have inference variables? Used to determine whether + /// inference is required. + const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_RE_INFER.bits + | TypeFlags::HAS_CT_INFER.bits; + + /// Does this have [Placeholder]? + const HAS_TY_PLACEHOLDER = 1 << 6; + /// Does this have [RePlaceholder]? + const HAS_RE_PLACEHOLDER = 1 << 7; + /// Does this have [ConstKind::Placeholder]? + const HAS_CT_PLACEHOLDER = 1 << 8; + + /// `true` if there are "names" of regions and so forth + /// that are local to a particular fn/inferctxt + const HAS_FREE_LOCAL_REGIONS = 1 << 9; + + /// `true` if there are "names" of types and regions and so forth + /// that are local to a particular fn + const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits + | TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_CT_INFER.bits + | TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits + | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; + + /// Does this have [Projection]? + const HAS_TY_PROJECTION = 1 << 10; + /// Does this have [Opaque]? + const HAS_TY_OPAQUE = 1 << 11; + /// Does this have [ConstKind::Unevaluated]? + const HAS_CT_PROJECTION = 1 << 12; + + /// Could this type be normalized further? + const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits + | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_CT_PROJECTION.bits; + + /// Is an error type/const reachable? + const HAS_ERROR = 1 << 13; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but [ReLateBound] and [ReErased]. + const HAS_FREE_REGIONS = 1 << 14; + + /// Does this have any [ReLateBound] regions? Used to check + /// if a global bound is safe to evaluate. + const HAS_RE_LATE_BOUND = 1 << 15; + + /// Does this have any [ReErased] regions? + const HAS_RE_ERASED = 1 << 16; + + /// Does this value have parameters/placeholders/inference variables which could be + /// replaced later, in a way that would change the results of `impl` specialization? + const STILL_FURTHER_SPECIALIZABLE = 1 << 17; + } +} + +#[allow(rustc::usage_of_ty_tykind)] +pub struct TyS<'tcx> { + pub kind: TyKind<'tcx>, + pub flags: TypeFlags, + + /// This is a kind of confusing thing: it stores the smallest + /// binder such that + /// + /// (a) the binder itself captures nothing but + /// (b) all the late-bound things within the type are captured + /// by some sub-binder. + /// + /// So, for a type without any late-bound things, like `u32`, this + /// will be *innermost*, because that is the innermost binder that + /// captures nothing. But for a type `&'D u32`, where `'D` is a + /// late-bound region with De Bruijn index `D`, this would be `D + 1` + /// -- the binder itself does not capture `D`, but `D` is captured + /// by an inner binder. + /// + /// We call this concept an "exclusive" binder `D` because all + /// De Bruijn indices within the type are contained within `0..D` + /// (exclusive). + outer_exclusive_binder: ty::DebruijnIndex, +} + +// `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(TyS<'_>, 32); + +impl<'tcx> Ord for TyS<'tcx> { + fn cmp(&self, other: &TyS<'tcx>) -> Ordering { + self.kind.cmp(&other.kind) + } +} + +impl<'tcx> PartialOrd for TyS<'tcx> { + fn partial_cmp(&self, other: &TyS<'tcx>) -> Option { + Some(self.kind.cmp(&other.kind)) + } +} + +impl<'tcx> PartialEq for TyS<'tcx> { + #[inline] + fn eq(&self, other: &TyS<'tcx>) -> bool { + ptr::eq(self, other) + } +} +impl<'tcx> Eq for TyS<'tcx> {} + +impl<'tcx> Hash for TyS<'tcx> { + fn hash(&self, s: &mut H) { + (self as *const TyS<'_>).hash(s) + } +} + +impl<'a, 'tcx> HashStable> for ty::TyS<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ty::TyS { + ref kind, + + // The other fields just provide fast access to information that is + // also contained in `kind`, so no need to hash them. + flags: _, + + outer_exclusive_binder: _, + } = *self; + + kind.hash_stable(hcx, hasher); + } +} + +#[rustc_diagnostic_item = "Ty"] +pub type Ty<'tcx> = &'tcx TyS<'tcx>; + +impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {} +impl<'tcx> rustc_serialize::UseSpecializedDecodable for Ty<'tcx> {} +impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx List> {} + +pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +pub struct UpvarPath { + pub hir_id: hir::HirId, +} + +/// Upvars do not get their own `NodeId`. Instead, we use the pair of +/// the original var ID (that is, the root variable that is referenced +/// by the upvar) and the ID of the closure expression. +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +pub struct UpvarId { + pub var_path: UpvarPath, + pub closure_expr_id: LocalDefId, +} + +#[derive(Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + ImmBorrow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when the closure + /// is borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate a `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + UniqueImmBorrow, + + /// Data is mutable and not aliasable. + MutBorrow, +} + +/// Information describing the capture of an upvar. This is computed +/// during `typeck`, specifically by `regionck`. +#[derive(PartialEq, Clone, Debug, Copy, RustcEncodable, RustcDecodable, HashStable)] +pub enum UpvarCapture<'tcx> { + /// Upvar is captured by value. This is always true when the + /// closure is labeled `move`, but can also be true in other cases + /// depending on inference. + ByValue, + + /// Upvar is captured by reference. + ByRef(UpvarBorrow<'tcx>), +} + +#[derive(PartialEq, Clone, Copy, RustcEncodable, RustcDecodable, HashStable)] +pub struct UpvarBorrow<'tcx> { + /// The kind of borrow: by-ref upvars have access to shared + /// immutable borrows, which are not part of the normal language + /// syntax. + pub kind: BorrowKind, + + /// Region of the resulting reference. + pub region: ty::Region<'tcx>, +} + +pub type UpvarListMap = FxHashMap>; +pub type UpvarCaptureMap<'tcx> = FxHashMap>; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum IntVarValue { + IntType(ast::IntTy), + UintType(ast::UintTy), +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct FloatVarValue(pub ast::FloatTy); + +impl ty::EarlyBoundRegion { + pub fn to_bound_region(&self) -> ty::BoundRegion { + ty::BoundRegion::BrNamed(self.def_id, self.name) + } + + /// Does this early bound region have a name? Early bound regions normally + /// always have names except when using anonymous lifetimes (`'_`). + pub fn has_name(&self) -> bool { + self.name != kw::UnderscoreLifetime + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum GenericParamDefKind { + Lifetime, + Type { + has_default: bool, + object_lifetime_default: ObjectLifetimeDefault, + synthetic: Option, + }, + Const, +} + +impl GenericParamDefKind { + pub fn descr(&self) -> &'static str { + match self { + GenericParamDefKind::Lifetime => "lifetime", + GenericParamDefKind::Type { .. } => "type", + GenericParamDefKind::Const => "constant", + } + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct GenericParamDef { + pub name: Symbol, + pub def_id: DefId, + pub index: u32, + + /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute + /// on generic parameter `'a`/`T`, asserts data behind the parameter + /// `'a`/`T` won't be accessed during the parent type's `Drop` impl. + pub pure_wrt_drop: bool, + + pub kind: GenericParamDefKind, +} + +impl GenericParamDef { + pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { + if let GenericParamDefKind::Lifetime = self.kind { + ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } + } else { + bug!("cannot convert a non-lifetime parameter def to an early bound region") + } + } + + pub fn to_bound_region(&self) -> ty::BoundRegion { + if let GenericParamDefKind::Lifetime = self.kind { + self.to_early_bound_region_data().to_bound_region() + } else { + bug!("cannot convert a non-lifetime parameter def to an early bound region") + } + } +} + +#[derive(Default)] +pub struct GenericParamCount { + pub lifetimes: usize, + pub types: usize, + pub consts: usize, +} + +/// Information about the formal type/lifetime parameters associated +/// with an item or method. Analogous to `hir::Generics`. +/// +/// The ordering of parameters is the same as in `Subst` (excluding child generics): +/// `Self` (optionally), `Lifetime` params..., `Type` params... +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct Generics { + pub parent: Option, + pub parent_count: usize, + pub params: Vec, + + /// Reverse map to the `index` field of each `GenericParamDef`. + #[stable_hasher(ignore)] + pub param_def_id_to_index: FxHashMap, + + pub has_self: bool, + pub has_late_bound_regions: Option, +} + +impl<'tcx> Generics { + pub fn count(&self) -> usize { + self.parent_count + self.params.len() + } + + pub fn own_counts(&self) -> GenericParamCount { + // We could cache this as a property of `GenericParamCount`, but + // the aim is to refactor this away entirely eventually and the + // presence of this method will be a constant reminder. + let mut own_counts: GenericParamCount = Default::default(); + + for param in &self.params { + match param.kind { + GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, + GenericParamDefKind::Type { .. } => own_counts.types += 1, + GenericParamDefKind::Const => own_counts.consts += 1, + }; + } + + own_counts + } + + pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { + if self.own_requires_monomorphization() { + return true; + } + + if let Some(parent_def_id) = self.parent { + let parent = tcx.generics_of(parent_def_id); + parent.requires_monomorphization(tcx) + } else { + false + } + } + + pub fn own_requires_monomorphization(&self) -> bool { + for param in &self.params { + match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => return true, + GenericParamDefKind::Lifetime => {} + } + } + false + } + + pub fn param_at(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + if let Some(index) = param_index.checked_sub(self.parent_count) { + &self.params[index] + } else { + tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) + .param_at(param_index, tcx) + } + } + + pub fn region_param( + &'tcx self, + param: &EarlyBoundRegion, + tcx: TyCtxt<'tcx>, + ) -> &'tcx GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Lifetime => param, + _ => bug!("expected lifetime parameter, but found another generic parameter"), + } + } + + /// Returns the `GenericParamDef` associated with this `ParamTy`. + pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Type { .. } => param, + _ => bug!("expected type parameter, but found another generic parameter"), + } + } + + /// Returns the `ConstParameterDef` associated with this `ParamConst`. + pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Const => param, + _ => bug!("expected const parameter, but found another generic parameter"), + } + } +} + +/// Bounds on generics. +#[derive(Copy, Clone, Default, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct GenericPredicates<'tcx> { + pub parent: Option, + pub predicates: &'tcx [(Predicate<'tcx>, Span)], +} + +impl<'tcx> GenericPredicates<'tcx> { + pub fn instantiate( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + let mut instantiated = InstantiatedPredicates::empty(); + self.instantiate_into(tcx, &mut instantiated, substs); + instantiated + } + + pub fn instantiate_own( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + InstantiatedPredicates { + predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(), + spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), + } + } + + fn instantiate_into( + &self, + tcx: TyCtxt<'tcx>, + instantiated: &mut InstantiatedPredicates<'tcx>, + substs: SubstsRef<'tcx>, + ) { + if let Some(def_id) = self.parent { + tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs); + } + instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs))); + instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); + } + + pub fn instantiate_identity(&self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { + let mut instantiated = InstantiatedPredicates::empty(); + self.instantiate_identity_into(tcx, &mut instantiated); + instantiated + } + + fn instantiate_identity_into( + &self, + tcx: TyCtxt<'tcx>, + instantiated: &mut InstantiatedPredicates<'tcx>, + ) { + if let Some(def_id) = self.parent { + tcx.predicates_of(def_id).instantiate_identity_into(tcx, instantiated); + } + instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); + instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); + } + + pub fn instantiate_supertrait( + &self, + tcx: TyCtxt<'tcx>, + poly_trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + assert_eq!(self.parent, None); + InstantiatedPredicates { + predicates: self + .predicates + .iter() + .map(|(pred, _)| pred.subst_supertrait(tcx, poly_trait_ref)) + .collect(), + spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub enum Predicate<'tcx> { + /// Corresponds to `where Foo: Bar`. `Foo` here would be + /// the `Self` type of the trait reference and `A`, `B`, and `C` + /// would be the type parameters. + /// + /// A trait predicate will have `Constness::Const` if it originates + /// from a bound on a `const fn` without the `?const` opt-out (e.g., + /// `const fn foobar() {}`). + Trait(PolyTraitPredicate<'tcx>, Constness), + + /// `where 'a: 'b` + RegionOutlives(PolyRegionOutlivesPredicate<'tcx>), + + /// `where T: 'a` + TypeOutlives(PolyTypeOutlivesPredicate<'tcx>), + + /// `where ::Name == X`, approximately. + /// See the `ProjectionPredicate` struct for details. + Projection(PolyProjectionPredicate<'tcx>), + + /// No syntax: `T` well-formed. + WellFormed(Ty<'tcx>), + + /// Trait must be object-safe. + ObjectSafe(DefId), + + /// No direct syntax. May be thought of as `where T: FnFoo<...>` + /// for some substitutions `...` and `T` being a closure type. + /// Satisfied (or refuted) once we know the closure's kind. + ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind), + + /// `T1 <: T2` + Subtype(PolySubtypePredicate<'tcx>), + + /// Constant initializer must evaluate successfully. + ConstEvaluatable(DefId, SubstsRef<'tcx>), + + /// Constants must be equal. The first component is the const that is expected. + ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>), +} + +/// The crate outlives map is computed during typeck and contains the +/// outlives of every item in the local crate. You should not use it +/// directly, because to do so will make your pass dependent on the +/// HIR of every item in the local crate. Instead, use +/// `tcx.inferred_outlives_of()` to get the outlives for a *particular* +/// item. +#[derive(HashStable)] +pub struct CratePredicatesMap<'tcx> { + /// For each struct with outlive bounds, maps to a vector of the + /// predicate of its outlive bounds. If an item has no outlives + /// bounds, it will have no entry. + pub predicates: FxHashMap, Span)]>, +} + +impl<'tcx> AsRef> for Predicate<'tcx> { + fn as_ref(&self) -> &Predicate<'tcx> { + self + } +} + +impl<'tcx> Predicate<'tcx> { + /// Performs a substitution suitable for going from a + /// poly-trait-ref to supertraits that must hold if that + /// poly-trait-ref holds. This is slightly different from a normal + /// substitution in terms of what happens with bound regions. See + /// lengthy comment below for details. + pub fn subst_supertrait( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> ty::Predicate<'tcx> { + // The interaction between HRTB and supertraits is not entirely + // obvious. Let me walk you (and myself) through an example. + // + // Let's start with an easy case. Consider two traits: + // + // trait Foo<'a>: Bar<'a,'a> { } + // trait Bar<'b,'c> { } + // + // Now, if we have a trait reference `for<'x> T: Foo<'x>`, then + // we can deduce that `for<'x> T: Bar<'x,'x>`. Basically, if we + // knew that `Foo<'x>` (for any 'x) then we also know that + // `Bar<'x,'x>` (for any 'x). This more-or-less falls out from + // normal substitution. + // + // In terms of why this is sound, the idea is that whenever there + // is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>` + // holds. So if there is an impl of `T:Foo<'a>` that applies to + // all `'a`, then we must know that `T:Bar<'a,'a>` holds for all + // `'a`. + // + // Another example to be careful of is this: + // + // trait Foo1<'a>: for<'b> Bar1<'a,'b> { } + // trait Bar1<'b,'c> { } + // + // Here, if we have `for<'x> T: Foo1<'x>`, then what do we know? + // The answer is that we know `for<'x,'b> T: Bar1<'x,'b>`. The + // reason is similar to the previous example: any impl of + // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So + // basically we would want to collapse the bound lifetimes from + // the input (`trait_ref`) and the supertraits. + // + // To achieve this in practice is fairly straightforward. Let's + // consider the more complicated scenario: + // + // - We start out with `for<'x> T: Foo1<'x>`. In this case, `'x` + // has a De Bruijn index of 1. We want to produce `for<'x,'b> T: Bar1<'x,'b>`, + // where both `'x` and `'b` would have a DB index of 1. + // The substitution from the input trait-ref is therefore going to be + // `'a => 'x` (where `'x` has a DB index of 1). + // - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an + // early-bound parameter and `'b' is a late-bound parameter with a + // DB index of 1. + // - If we replace `'a` with `'x` from the input, it too will have + // a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>` + // just as we wanted. + // + // There is only one catch. If we just apply the substitution `'a + // => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will + // adjust the DB index because we substituting into a binder (it + // tries to be so smart...) resulting in `for<'x> for<'b> + // Bar1<'x,'b>` (we have no syntax for this, so use your + // imagination). Basically the 'x will have DB index of 2 and 'b + // will have DB index of 1. Not quite what we want. So we apply + // the substitution to the *contents* of the trait reference, + // rather than the trait reference itself (put another way, the + // substitution code expects equal binding levels in the values + // from the substitution and the value being substituted into, and + // this trick achieves that). + + let substs = &trait_ref.skip_binder().substs; + match *self { + Predicate::Trait(ref binder, constness) => { + Predicate::Trait(binder.map_bound(|data| data.subst(tcx, substs)), constness) + } + Predicate::Subtype(ref binder) => { + Predicate::Subtype(binder.map_bound(|data| data.subst(tcx, substs))) + } + Predicate::RegionOutlives(ref binder) => { + Predicate::RegionOutlives(binder.map_bound(|data| data.subst(tcx, substs))) + } + Predicate::TypeOutlives(ref binder) => { + Predicate::TypeOutlives(binder.map_bound(|data| data.subst(tcx, substs))) + } + Predicate::Projection(ref binder) => { + Predicate::Projection(binder.map_bound(|data| data.subst(tcx, substs))) + } + Predicate::WellFormed(data) => Predicate::WellFormed(data.subst(tcx, substs)), + Predicate::ObjectSafe(trait_def_id) => Predicate::ObjectSafe(trait_def_id), + Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + Predicate::ClosureKind(closure_def_id, closure_substs.subst(tcx, substs), kind) + } + Predicate::ConstEvaluatable(def_id, const_substs) => { + Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)) + } + Predicate::ConstEquate(c1, c2) => { + Predicate::ConstEquate(c1.subst(tcx, substs), c2.subst(tcx, substs)) + } + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct TraitPredicate<'tcx> { + pub trait_ref: TraitRef<'tcx>, +} + +pub type PolyTraitPredicate<'tcx> = ty::Binder>; + +impl<'tcx> TraitPredicate<'tcx> { + pub fn def_id(&self) -> DefId { + self.trait_ref.def_id + } + + pub fn self_ty(&self) -> Ty<'tcx> { + self.trait_ref.self_ty() + } +} + +impl<'tcx> PolyTraitPredicate<'tcx> { + pub fn def_id(&self) -> DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().def_id() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct OutlivesPredicate(pub A, pub B); // `A: B` +pub type PolyOutlivesPredicate = ty::Binder>; +pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; +pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; +pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder>; +pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder>; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct SubtypePredicate<'tcx> { + pub a_is_expected: bool, + pub a: Ty<'tcx>, + pub b: Ty<'tcx>, +} +pub type PolySubtypePredicate<'tcx> = ty::Binder>; + +/// This kind of predicate has no *direct* correspondent in the +/// syntax, but it roughly corresponds to the syntactic forms: +/// +/// 1. `T: TraitRef<..., Item = Type>` +/// 2. `>::Item == Type` (NYI) +/// +/// In particular, form #1 is "desugared" to the combination of a +/// normal trait predicate (`T: TraitRef<...>`) and one of these +/// predicates. Form #2 is a broader form in that it also permits +/// equality between arbitrary types. Processing an instance of +/// Form #2 eventually yields one of these `ProjectionPredicate` +/// instances to normalize the LHS. +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct ProjectionPredicate<'tcx> { + pub projection_ty: ProjectionTy<'tcx>, + pub ty: Ty<'tcx>, +} + +pub type PolyProjectionPredicate<'tcx> = Binder>; + +impl<'tcx> PolyProjectionPredicate<'tcx> { + /// Returns the `DefId` of the associated item being projected. + pub fn item_def_id(&self) -> DefId { + self.skip_binder().projection_ty.item_def_id + } + + #[inline] + pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { + // Note: unlike with `TraitRef::to_poly_trait_ref()`, + // `self.0.trait_ref` is permitted to have escaping regions. + // This is because here `self` has a `Binder` and so does our + // return value, so we are preserving the number of binding + // levels. + self.map_bound(|predicate| predicate.projection_ty.trait_ref(tcx)) + } + + pub fn ty(&self) -> Binder> { + self.map_bound(|predicate| predicate.ty) + } + + /// The `DefId` of the `TraitItem` for the associated type. + /// + /// Note that this is not the `DefId` of the `TraitRef` containing this + /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. + pub fn projection_def_id(&self) -> DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().projection_ty.item_def_id + } +} + +pub trait ToPolyTraitRef<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; +} + +impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { + ty::Binder::dummy(*self) + } +} + +impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { + self.map_bound_ref(|trait_pred| trait_pred.trait_ref) + } +} + +pub trait ToPredicate<'tcx> { + fn to_predicate(&self) -> Predicate<'tcx>; +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { + fn to_predicate(&self) -> Predicate<'tcx> { + ty::Predicate::Trait( + ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.value }), + self.constness, + ) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&TraitRef<'tcx>> { + fn to_predicate(&self) -> Predicate<'tcx> { + ty::Predicate::Trait( + ty::Binder::dummy(ty::TraitPredicate { trait_ref: *self.value }), + self.constness, + ) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { + fn to_predicate(&self) -> Predicate<'tcx> { + ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&PolyTraitRef<'tcx>> { + fn to_predicate(&self) -> Predicate<'tcx> { + ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { + fn to_predicate(&self) -> Predicate<'tcx> { + Predicate::RegionOutlives(*self) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { + fn to_predicate(&self) -> Predicate<'tcx> { + Predicate::TypeOutlives(*self) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { + fn to_predicate(&self) -> Predicate<'tcx> { + Predicate::Projection(*self) + } +} + +impl<'tcx> Predicate<'tcx> { + pub fn to_opt_poly_trait_ref(&self) -> Option> { + match *self { + Predicate::Trait(ref t, _) => Some(t.to_poly_trait_ref()), + Predicate::Projection(..) + | Predicate::Subtype(..) + | Predicate::RegionOutlives(..) + | Predicate::WellFormed(..) + | Predicate::ObjectSafe(..) + | Predicate::ClosureKind(..) + | Predicate::TypeOutlives(..) + | Predicate::ConstEvaluatable(..) + | Predicate::ConstEquate(..) => None, + } + } + + pub fn to_opt_type_outlives(&self) -> Option> { + match *self { + Predicate::TypeOutlives(data) => Some(data), + Predicate::Trait(..) + | Predicate::Projection(..) + | Predicate::Subtype(..) + | Predicate::RegionOutlives(..) + | Predicate::WellFormed(..) + | Predicate::ObjectSafe(..) + | Predicate::ClosureKind(..) + | Predicate::ConstEvaluatable(..) + | Predicate::ConstEquate(..) => None, + } + } +} + +/// Represents the bounds declared on a particular set of type +/// parameters. Should eventually be generalized into a flag list of +/// where-clauses. You can obtain a `InstantiatedPredicates` list from a +/// `GenericPredicates` by using the `instantiate` method. Note that this method +/// reflects an important semantic invariant of `InstantiatedPredicates`: while +/// the `GenericPredicates` are expressed in terms of the bound type +/// parameters of the impl/trait/whatever, an `InstantiatedPredicates` instance +/// represented a set of bounds for some particular instantiation, +/// meaning that the generic parameters have been substituted with +/// their values. +/// +/// Example: +/// +/// struct Foo> { ... } +/// +/// Here, the `GenericPredicates` for `Foo` would contain a list of bounds like +/// `[[], [U:Bar]]`. Now if there were some particular reference +/// like `Foo`, then the `InstantiatedPredicates` would be `[[], +/// [usize:Bar]]`. +#[derive(Clone, Debug, TypeFoldable)] +pub struct InstantiatedPredicates<'tcx> { + pub predicates: Vec>, + pub spans: Vec, +} + +impl<'tcx> InstantiatedPredicates<'tcx> { + pub fn empty() -> InstantiatedPredicates<'tcx> { + InstantiatedPredicates { predicates: vec![], spans: vec![] } + } + + pub fn is_empty(&self) -> bool { + self.predicates.is_empty() + } +} + +rustc_index::newtype_index! { + /// "Universes" are used during type- and trait-checking in the + /// presence of `for<..>` binders to control what sets of names are + /// visible. Universes are arranged into a tree: the root universe + /// contains names that are always visible. Each child then adds a new + /// set of names that are visible, in addition to those of its parent. + /// We say that the child universe "extends" the parent universe with + /// new names. + /// + /// To make this more concrete, consider this program: + /// + /// ``` + /// struct Foo { } + /// fn bar(x: T) { + /// let y: for<'a> fn(&'a u8, Foo) = ...; + /// } + /// ``` + /// + /// The struct name `Foo` is in the root universe U0. But the type + /// parameter `T`, introduced on `bar`, is in an extended universe U1 + /// -- i.e., within `bar`, we can name both `T` and `Foo`, but outside + /// of `bar`, we cannot name `T`. Then, within the type of `y`, the + /// region `'a` is in a universe U2 that extends U1, because we can + /// name it inside the fn type but not outside. + /// + /// Universes are used to do type- and trait-checking around these + /// "forall" binders (also called **universal quantification**). The + /// idea is that when, in the body of `bar`, we refer to `T` as a + /// type, we aren't referring to any type in particular, but rather a + /// kind of "fresh" type that is distinct from all other types we have + /// actually declared. This is called a **placeholder** type, and we + /// use universes to talk about this. In other words, a type name in + /// universe 0 always corresponds to some "ground" type that the user + /// declared, but a type name in a non-zero universe is a placeholder + /// type -- an idealized representative of "types in general" that we + /// use for checking generic functions. + pub struct UniverseIndex { + derive [HashStable] + DEBUG_FORMAT = "U{}", + } +} + +impl UniverseIndex { + pub const ROOT: UniverseIndex = UniverseIndex::from_u32(0); + + /// Returns the "next" universe index in order -- this new index + /// is considered to extend all previous universes. This + /// corresponds to entering a `forall` quantifier. So, for + /// example, suppose we have this type in universe `U`: + /// + /// ``` + /// for<'a> fn(&'a u32) + /// ``` + /// + /// Once we "enter" into this `for<'a>` quantifier, we are in a + /// new universe that extends `U` -- in this new universe, we can + /// name the region `'a`, but that region was not nameable from + /// `U` because it was not in scope there. + pub fn next_universe(self) -> UniverseIndex { + UniverseIndex::from_u32(self.private.checked_add(1).unwrap()) + } + + /// Returns `true` if `self` can name a name from `other` -- in other words, + /// if the set of names in `self` is a superset of those in + /// `other` (`self >= other`). + pub fn can_name(self, other: UniverseIndex) -> bool { + self.private >= other.private + } + + /// Returns `true` if `self` cannot name some names from `other` -- in other + /// words, if the set of names in `self` is a strict subset of + /// those in `other` (`self < other`). + pub fn cannot_name(self, other: UniverseIndex) -> bool { + self.private < other.private + } +} + +/// The "placeholder index" fully defines a placeholder region. +/// Placeholder regions are identified by both a **universe** as well +/// as a "bound-region" within that universe. The `bound_region` is +/// basically a name -- distinct bound regions within the same +/// universe are just two regions with an unknown relationship to one +/// another. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, PartialOrd, Ord)] +pub struct Placeholder { + pub universe: UniverseIndex, + pub name: T, +} + +impl<'a, T> HashStable> for Placeholder +where + T: HashStable>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + self.universe.hash_stable(hcx, hasher); + self.name.hash_stable(hcx, hasher); + } +} + +pub type PlaceholderRegion = Placeholder; + +pub type PlaceholderType = Placeholder; + +pub type PlaceholderConst = Placeholder; + +/// When type checking, we use the `ParamEnv` to track +/// details about the set of where-clauses that are in scope at this +/// particular point. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeFoldable)] +pub struct ParamEnv<'tcx> { + /// `Obligation`s that the caller must satisfy. This is basically + /// the set of bounds on the in-scope type parameters, translated + /// into `Obligation`s, and elaborated and normalized. + pub caller_bounds: &'tcx List>, + + /// Typically, this is `Reveal::UserFacing`, but during codegen we + /// want `Reveal::All` -- note that this is always paired with an + /// empty environment. To get that, use `ParamEnv::reveal()`. + pub reveal: traits::Reveal, + + /// If this `ParamEnv` comes from a call to `tcx.param_env(def_id)`, + /// register that `def_id` (useful for transitioning to the chalk trait + /// solver). + pub def_id: Option, +} + +impl<'tcx> ParamEnv<'tcx> { + /// Construct a trait environment suitable for contexts where + /// there are no where-clauses in scope. Hidden types (like `impl + /// Trait`) are left hidden, so this is suitable for ordinary + /// type-checking. + #[inline] + pub fn empty() -> Self { + Self::new(List::empty(), Reveal::UserFacing, None) + } + + /// Construct a trait environment with no where-clauses in scope + /// where the values of all `impl Trait` and other hidden types + /// are revealed. This is suitable for monomorphized, post-typeck + /// environments like codegen or doing optimizations. + /// + /// N.B., if you want to have predicates in scope, use `ParamEnv::new`, + /// or invoke `param_env.with_reveal_all()`. + #[inline] + pub fn reveal_all() -> Self { + Self::new(List::empty(), Reveal::All, None) + } + + /// Construct a trait environment with the given set of predicates. + #[inline] + pub fn new( + caller_bounds: &'tcx List>, + reveal: Reveal, + def_id: Option, + ) -> Self { + ty::ParamEnv { caller_bounds, reveal, def_id } + } + + /// Returns a new parameter environment with the same clauses, but + /// which "reveals" the true results of projections in all cases + /// (even for associated types that are specializable). This is + /// the desired behavior during codegen and certain other special + /// contexts; normally though we want to use `Reveal::UserFacing`, + /// which is the default. + pub fn with_reveal_all(self) -> Self { + ty::ParamEnv { reveal: Reveal::All, ..self } + } + + /// Returns this same environment but with no caller bounds. + pub fn without_caller_bounds(self) -> Self { + ty::ParamEnv { caller_bounds: List::empty(), ..self } + } + + /// Creates a suitable environment in which to perform trait + /// queries on the given value. When type-checking, this is simply + /// the pair of the environment plus value. But when reveal is set to + /// All, then if `value` does not reference any type parameters, we will + /// pair it with the empty environment. This improves caching and is generally + /// invisible. + /// + /// N.B., we preserve the environment when type-checking because it + /// is possible for the user to have wacky where-clauses like + /// `where Box: Copy`, which are clearly never + /// satisfiable. We generally want to behave as if they were true, + /// although the surrounding function is never reachable. + pub fn and>(self, value: T) -> ParamEnvAnd<'tcx, T> { + match self.reveal { + Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, + + Reveal::All => { + if value.is_global() { + ParamEnvAnd { param_env: self.without_caller_bounds(), value } + } else { + ParamEnvAnd { param_env: self, value } + } + } + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstnessAnd { + pub constness: Constness, + pub value: T, +} + +// FIXME(ecstaticmorse): Audit all occurrences of `without_const().to_predicate()` to ensure that +// the constness of trait bounds is being propagated correctly. +pub trait WithConstness: Sized { + #[inline] + fn with_constness(self, constness: Constness) -> ConstnessAnd { + ConstnessAnd { constness, value: self } + } + + #[inline] + fn with_const(self) -> ConstnessAnd { + self.with_constness(Constness::Const) + } + + #[inline] + fn without_const(self) -> ConstnessAnd { + self.with_constness(Constness::NotConst) + } +} + +impl WithConstness for T {} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)] +pub struct ParamEnvAnd<'tcx, T> { + pub param_env: ParamEnv<'tcx>, + pub value: T, +} + +impl<'tcx, T> ParamEnvAnd<'tcx, T> { + pub fn into_parts(self) -> (ParamEnv<'tcx>, T) { + (self.param_env, self.value) + } +} + +impl<'a, 'tcx, T> HashStable> for ParamEnvAnd<'tcx, T> +where + T: HashStable>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ParamEnvAnd { ref param_env, ref value } = *self; + + param_env.hash_stable(hcx, hasher); + value.hash_stable(hcx, hasher); + } +} + +#[derive(Copy, Clone, Debug, HashStable)] +pub struct Destructor { + /// The `DefId` of the destructor method + pub did: DefId, +} + +bitflags! { + #[derive(HashStable)] + pub struct AdtFlags: u32 { + const NO_ADT_FLAGS = 0; + /// Indicates whether the ADT is an enum. + const IS_ENUM = 1 << 0; + /// Indicates whether the ADT is a union. + const IS_UNION = 1 << 1; + /// Indicates whether the ADT is a struct. + const IS_STRUCT = 1 << 2; + /// Indicates whether the ADT is a struct and has a constructor. + const HAS_CTOR = 1 << 3; + /// Indicates whether the type is `PhantomData`. + const IS_PHANTOM_DATA = 1 << 4; + /// Indicates whether the type has a `#[fundamental]` attribute. + const IS_FUNDAMENTAL = 1 << 5; + /// Indicates whether the type is `Box`. + const IS_BOX = 1 << 6; + /// Indicates whether the type is `ManuallyDrop`. + const IS_MANUALLY_DROP = 1 << 7; + /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. + /// (i.e., this flag is never set unless this ADT is an enum). + const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; + } +} + +bitflags! { + #[derive(HashStable)] + pub struct VariantFlags: u32 { + const NO_VARIANT_FLAGS = 0; + /// Indicates whether the field list of this variant is `#[non_exhaustive]`. + const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0; + } +} + +/// Definition of a variant -- a struct's fields or a enum variant. +#[derive(Debug, HashStable)] +pub struct VariantDef { + /// `DefId` that identifies the variant itself. + /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. + pub def_id: DefId, + /// `DefId` that identifies the variant's constructor. + /// If this variant is a struct variant, then this is `None`. + pub ctor_def_id: Option, + /// Variant or struct name. + #[stable_hasher(project(name))] + pub ident: Ident, + /// Discriminant of this variant. + pub discr: VariantDiscr, + /// Fields of this variant. + pub fields: Vec, + /// Type of constructor of variant. + pub ctor_kind: CtorKind, + /// Flags of the variant (e.g. is field list non-exhaustive)? + flags: VariantFlags, + /// Variant is obtained as part of recovering from a syntactic error. + /// May be incomplete or bogus. + pub recovered: bool, +} + +impl<'tcx> VariantDef { + /// Creates a new `VariantDef`. + /// + /// `variant_did` is the `DefId` that identifies the enum variant (if this `VariantDef` + /// represents an enum variant). + /// + /// `ctor_did` is the `DefId` that identifies the constructor of unit or + /// tuple-variants/structs. If this is a `struct`-variant then this should be `None`. + /// + /// `parent_did` is the `DefId` of the `AdtDef` representing the enum or struct that + /// owns this variant. It is used for checking if a struct has `#[non_exhaustive]` w/out having + /// to go through the redirect of checking the ctor's attributes - but compiling a small crate + /// requires loading the `AdtDef`s for all the structs in the universe (e.g., coherence for any + /// built-in trait), and we do not want to load attributes twice. + /// + /// If someone speeds up attribute loading to not be a performance concern, they can + /// remove this hack and use the constructor `DefId` everywhere. + pub fn new( + tcx: TyCtxt<'tcx>, + ident: Ident, + variant_did: Option, + ctor_def_id: Option, + discr: VariantDiscr, + fields: Vec, + ctor_kind: CtorKind, + adt_kind: AdtKind, + parent_did: DefId, + recovered: bool, + ) -> Self { + debug!( + "VariantDef::new(ident = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, + fields = {:?}, ctor_kind = {:?}, adt_kind = {:?}, parent_did = {:?})", + ident, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, + ); + + let mut flags = VariantFlags::NO_VARIANT_FLAGS; + if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive) { + debug!("found non-exhaustive field list for {:?}", parent_did); + flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; + } else if let Some(variant_did) = variant_did { + if tcx.has_attr(variant_did, sym::non_exhaustive) { + debug!("found non-exhaustive field list for {:?}", variant_did); + flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; + } + } + + VariantDef { + def_id: variant_did.unwrap_or(parent_did), + ctor_def_id, + ident, + discr, + fields, + ctor_kind, + flags, + recovered, + } + } + + /// Is this field list non-exhaustive? + #[inline] + pub fn is_field_list_non_exhaustive(&self) -> bool { + self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum VariantDiscr { + /// Explicit value for this variant, i.e., `X = 123`. + /// The `DefId` corresponds to the embedded constant. + Explicit(DefId), + + /// The previous variant's discriminant plus one. + /// For efficiency reasons, the distance from the + /// last `Explicit` discriminant is being stored, + /// or `0` for the first variant, if it has none. + Relative(u32), +} + +#[derive(Debug, HashStable)] +pub struct FieldDef { + pub did: DefId, + #[stable_hasher(project(name))] + pub ident: Ident, + pub vis: Visibility, +} + +/// The definition of a user-defined type, e.g., a `struct`, `enum`, or `union`. +/// +/// These are all interned (by `intern_adt_def`) into the `adt_defs` table. +/// +/// The initialism *ADT* stands for an [*algebraic data type (ADT)*][adt]. +/// This is slightly wrong because `union`s are not ADTs. +/// Moreover, Rust only allows recursive data types through indirection. +/// +/// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type +pub struct AdtDef { + /// The `DefId` of the struct, enum or union item. + pub did: DefId, + /// Variants of the ADT. If this is a struct or union, then there will be a single variant. + pub variants: IndexVec, + /// Flags of the ADT (e.g., is this a struct? is this non-exhaustive?). + flags: AdtFlags, + /// Repr options provided by the user. + pub repr: ReprOptions, +} + +impl PartialOrd for AdtDef { + fn partial_cmp(&self, other: &AdtDef) -> Option { + Some(self.cmp(&other)) + } +} + +/// There should be only one AdtDef for each `did`, therefore +/// it is fine to implement `Ord` only based on `did`. +impl Ord for AdtDef { + fn cmp(&self, other: &AdtDef) -> Ordering { + self.did.cmp(&other.did) + } +} + +impl PartialEq for AdtDef { + // `AdtDef`s are always interned, and this is part of `TyS` equality. + #[inline] + fn eq(&self, other: &Self) -> bool { + ptr::eq(self, other) + } +} + +impl Eq for AdtDef {} + +impl Hash for AdtDef { + #[inline] + fn hash(&self, s: &mut H) { + (self as *const AdtDef).hash(s) + } +} + +impl<'tcx> rustc_serialize::UseSpecializedEncodable for &'tcx AdtDef { + fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + self.did.encode(s) + } +} + +impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx AdtDef {} + +impl<'a> HashStable> for AdtDef { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + thread_local! { + static CACHE: RefCell> = Default::default(); + } + + let hash: Fingerprint = CACHE.with(|cache| { + let addr = self as *const AdtDef as usize; + *cache.borrow_mut().entry(addr).or_insert_with(|| { + let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; + + let mut hasher = StableHasher::new(); + did.hash_stable(hcx, &mut hasher); + variants.hash_stable(hcx, &mut hasher); + flags.hash_stable(hcx, &mut hasher); + repr.hash_stable(hcx, &mut hasher); + + hasher.finish() + }) + }); + + hash.hash_stable(hcx, hasher); + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum AdtKind { + Struct, + Union, + Enum, +} + +impl Into for AdtKind { + fn into(self) -> DataTypeKind { + match self { + AdtKind::Struct => DataTypeKind::Struct, + AdtKind::Union => DataTypeKind::Union, + AdtKind::Enum => DataTypeKind::Enum, + } + } +} + +bitflags! { + #[derive(RustcEncodable, RustcDecodable, Default, HashStable)] + pub struct ReprFlags: u8 { + const IS_C = 1 << 0; + const IS_SIMD = 1 << 1; + const IS_TRANSPARENT = 1 << 2; + // Internal only for now. If true, don't reorder fields. + const IS_LINEAR = 1 << 3; + // If true, don't expose any niche to type's context. + const HIDE_NICHE = 1 << 4; + // Any of these flags being set prevent field reordering optimisation. + const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits | + ReprFlags::IS_SIMD.bits | + ReprFlags::IS_LINEAR.bits; + } +} + +/// Represents the repr options provided by the user, +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Default, HashStable)] +pub struct ReprOptions { + pub int: Option, + pub align: Option, + pub pack: Option, + pub flags: ReprFlags, +} + +impl ReprOptions { + pub fn new(tcx: TyCtxt<'_>, did: DefId) -> ReprOptions { + let mut flags = ReprFlags::empty(); + let mut size = None; + let mut max_align: Option = None; + let mut min_pack: Option = None; + for attr in tcx.get_attrs(did).iter() { + for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) { + flags.insert(match r { + attr::ReprC => ReprFlags::IS_C, + attr::ReprPacked(pack) => { + let pack = Align::from_bytes(pack as u64).unwrap(); + min_pack = Some(if let Some(min_pack) = min_pack { + min_pack.min(pack) + } else { + pack + }); + ReprFlags::empty() + } + attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, + attr::ReprNoNiche => ReprFlags::HIDE_NICHE, + attr::ReprSimd => ReprFlags::IS_SIMD, + attr::ReprInt(i) => { + size = Some(i); + ReprFlags::empty() + } + attr::ReprAlign(align) => { + max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); + ReprFlags::empty() + } + }); + } + } + + // This is here instead of layout because the choice must make it into metadata. + if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.def_path_str(did))) { + flags.insert(ReprFlags::IS_LINEAR); + } + ReprOptions { int: size, align: max_align, pack: min_pack, flags } + } + + #[inline] + pub fn simd(&self) -> bool { + self.flags.contains(ReprFlags::IS_SIMD) + } + #[inline] + pub fn c(&self) -> bool { + self.flags.contains(ReprFlags::IS_C) + } + #[inline] + pub fn packed(&self) -> bool { + self.pack.is_some() + } + #[inline] + pub fn transparent(&self) -> bool { + self.flags.contains(ReprFlags::IS_TRANSPARENT) + } + #[inline] + pub fn linear(&self) -> bool { + self.flags.contains(ReprFlags::IS_LINEAR) + } + #[inline] + pub fn hide_niche(&self) -> bool { + self.flags.contains(ReprFlags::HIDE_NICHE) + } + + pub fn discr_type(&self) -> attr::IntType { + self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize)) + } + + /// Returns `true` if this `#[repr()]` should inhabit "smart enum + /// layout" optimizations, such as representing `Foo<&T>` as a + /// single pointer. + pub fn inhibit_enum_layout_opt(&self) -> bool { + self.c() || self.int.is_some() + } + + /// Returns `true` if this `#[repr()]` should inhibit struct field reordering + /// optimizations, such as with `repr(C)`, `repr(packed(1))`, or `repr()`. + pub fn inhibit_struct_field_reordering_opt(&self) -> bool { + if let Some(pack) = self.pack { + if pack.bytes() == 1 { + return true; + } + } + self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() + } + + /// Returns `true` if this `#[repr()]` should inhibit union ABI optimisations. + pub fn inhibit_union_abi_opt(&self) -> bool { + self.c() + } +} + +impl<'tcx> AdtDef { + /// Creates a new `AdtDef`. + fn new( + tcx: TyCtxt<'_>, + did: DefId, + kind: AdtKind, + variants: IndexVec, + repr: ReprOptions, + ) -> Self { + debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); + let mut flags = AdtFlags::NO_ADT_FLAGS; + + if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { + debug!("found non-exhaustive variant list for {:?}", did); + flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; + } + + flags |= match kind { + AdtKind::Enum => AdtFlags::IS_ENUM, + AdtKind::Union => AdtFlags::IS_UNION, + AdtKind::Struct => AdtFlags::IS_STRUCT, + }; + + if kind == AdtKind::Struct && variants[VariantIdx::new(0)].ctor_def_id.is_some() { + flags |= AdtFlags::HAS_CTOR; + } + + let attrs = tcx.get_attrs(did); + if attr::contains_name(&attrs, sym::fundamental) { + flags |= AdtFlags::IS_FUNDAMENTAL; + } + if Some(did) == tcx.lang_items().phantom_data() { + flags |= AdtFlags::IS_PHANTOM_DATA; + } + if Some(did) == tcx.lang_items().owned_box() { + flags |= AdtFlags::IS_BOX; + } + if Some(did) == tcx.lang_items().manually_drop() { + flags |= AdtFlags::IS_MANUALLY_DROP; + } + + AdtDef { did, variants, flags, repr } + } + + /// Returns `true` if this is a struct. + #[inline] + pub fn is_struct(&self) -> bool { + self.flags.contains(AdtFlags::IS_STRUCT) + } + + /// Returns `true` if this is a union. + #[inline] + pub fn is_union(&self) -> bool { + self.flags.contains(AdtFlags::IS_UNION) + } + + /// Returns `true` if this is a enum. + #[inline] + pub fn is_enum(&self) -> bool { + self.flags.contains(AdtFlags::IS_ENUM) + } + + /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. + #[inline] + pub fn is_variant_list_non_exhaustive(&self) -> bool { + self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) + } + + /// Returns the kind of the ADT. + #[inline] + pub fn adt_kind(&self) -> AdtKind { + if self.is_enum() { + AdtKind::Enum + } else if self.is_union() { + AdtKind::Union + } else { + AdtKind::Struct + } + } + + /// Returns a description of this abstract data type. + pub fn descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "enum", + } + } + + /// Returns a description of a variant of this abstract data type. + #[inline] + pub fn variant_descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "variant", + } + } + + /// If this function returns `true`, it implies that `is_struct` must return `true`. + #[inline] + pub fn has_ctor(&self) -> bool { + self.flags.contains(AdtFlags::HAS_CTOR) + } + + /// Returns `true` if this type is `#[fundamental]` for the purposes + /// of coherence checking. + #[inline] + pub fn is_fundamental(&self) -> bool { + self.flags.contains(AdtFlags::IS_FUNDAMENTAL) + } + + /// Returns `true` if this is `PhantomData`. + #[inline] + pub fn is_phantom_data(&self) -> bool { + self.flags.contains(AdtFlags::IS_PHANTOM_DATA) + } + + /// Returns `true` if this is Box. + #[inline] + pub fn is_box(&self) -> bool { + self.flags.contains(AdtFlags::IS_BOX) + } + + /// Returns `true` if this is `ManuallyDrop`. + #[inline] + pub fn is_manually_drop(&self) -> bool { + self.flags.contains(AdtFlags::IS_MANUALLY_DROP) + } + + /// Returns `true` if this type has a destructor. + pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { + self.destructor(tcx).is_some() + } + + /// Asserts this is a struct or union and returns its unique variant. + pub fn non_enum_variant(&self) -> &VariantDef { + assert!(self.is_struct() || self.is_union()); + &self.variants[VariantIdx::new(0)] + } + + #[inline] + pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { + tcx.predicates_of(self.did) + } + + /// Returns an iterator over all fields contained + /// by this ADT. + #[inline] + pub fn all_fields(&self) -> impl Iterator + Clone { + self.variants.iter().flat_map(|v| v.fields.iter()) + } + + pub fn is_payloadfree(&self) -> bool { + !self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty()) + } + + /// Return a `VariantDef` given a variant id. + pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { + self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") + } + + /// Return a `VariantDef` given a constructor id. + pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef { + self.variants + .iter() + .find(|v| v.ctor_def_id == Some(cid)) + .expect("variant_with_ctor_id: unknown variant") + } + + /// Return the index of `VariantDef` given a variant id. + pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.def_id == vid) + .expect("variant_index_with_id: unknown variant") + .0 + } + + /// Return the index of `VariantDef` given a constructor id. + pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.ctor_def_id == Some(cid)) + .expect("variant_index_with_ctor_id: unknown variant") + .0 + } + + pub fn variant_of_res(&self, res: Res) -> &VariantDef { + match res { + Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid), + Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), + Res::Def(DefKind::Struct, _) + | Res::Def(DefKind::Union, _) + | Res::Def(DefKind::TyAlias, _) + | Res::Def(DefKind::AssocTy, _) + | Res::SelfTy(..) + | Res::SelfCtor(..) => self.non_enum_variant(), + _ => bug!("unexpected res {:?} in variant_of_res", res), + } + } + + #[inline] + pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { + let param_env = tcx.param_env(expr_did); + let repr_type = self.repr.discr_type(); + match tcx.const_eval_poly(expr_did) { + Ok(val) => { + let ty = repr_type.to_ty(tcx); + if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { + trace!("discriminants: {} ({:?})", b, repr_type); + Some(Discr { val: b, ty }) + } else { + info!("invalid enum discriminant: {:#?}", val); + crate::mir::interpret::struct_error( + tcx.at(tcx.def_span(expr_did)), + "constant evaluation of enum discriminant resulted in non-integer", + ) + .emit(); + None + } + } + Err(err) => { + let msg = match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + "enum discriminant evaluation failed" + } + ErrorHandled::TooGeneric => "enum discriminant depends on generics", + }; + tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg); + None + } + } + } + + #[inline] + pub fn discriminants( + &'tcx self, + tcx: TyCtxt<'tcx>, + ) -> impl Iterator)> + Captures<'tcx> { + let repr_type = self.repr.discr_type(); + let initial = repr_type.initial_discriminant(tcx); + let mut prev_discr = None::>; + self.variants.iter_enumerated().map(move |(i, v)| { + let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); + if let VariantDiscr::Explicit(expr_did) = v.discr { + if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { + discr = new_discr; + } + } + prev_discr = Some(discr); + + (i, discr) + }) + } + + #[inline] + pub fn variant_range(&self) -> Range { + VariantIdx::new(0)..VariantIdx::new(self.variants.len()) + } + + /// Computes the discriminant value used by a specific variant. + /// Unlike `discriminants`, this is (amortized) constant-time, + /// only doing at most one query for evaluating an explicit + /// discriminant (the last one before the requested variant), + /// assuming there are no constant-evaluation errors there. + #[inline] + pub fn discriminant_for_variant( + &self, + tcx: TyCtxt<'tcx>, + variant_index: VariantIdx, + ) -> Discr<'tcx> { + let (val, offset) = self.discriminant_def_for_variant(variant_index); + let explicit_value = val + .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) + .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); + explicit_value.checked_add(tcx, offset as u128).0 + } + + /// Yields a `DefId` for the discriminant and an offset to add to it + /// Alternatively, if there is no explicit discriminant, returns the + /// inferred discriminant directly. + pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option, u32) { + let mut explicit_index = variant_index.as_u32(); + let expr_did; + loop { + match self.variants[VariantIdx::from_u32(explicit_index)].discr { + ty::VariantDiscr::Relative(0) => { + expr_did = None; + break; + } + ty::VariantDiscr::Relative(distance) => { + explicit_index -= distance; + } + ty::VariantDiscr::Explicit(did) => { + expr_did = Some(did); + break; + } + } + } + (expr_did, variant_index.as_u32() - explicit_index) + } + + pub fn destructor(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.adt_destructor(self.did) + } + + /// Returns a list of types such that `Self: Sized` if and only + /// if that type is `Sized`, or `TyErr` if this type is recursive. + /// + /// Oddly enough, checking that the sized-constraint is `Sized` is + /// actually more expressive than checking all members: + /// the `Sized` trait is inductive, so an associated type that references + /// `Self` would prevent its containing ADT from being `Sized`. + /// + /// Due to normalization being eager, this applies even if + /// the associated type is behind a pointer (e.g., issue #31299). + pub fn sized_constraint(&self, tcx: TyCtxt<'tcx>) -> &'tcx [Ty<'tcx>] { + tcx.adt_sized_constraint(self.did).0 + } +} + +impl<'tcx> FieldDef { + /// Returns the type of this field. The `subst` is typically obtained + /// via the second field of `TyKind::AdtDef`. + pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { + tcx.type_of(self.did).subst(tcx, subst) + } +} + +/// Represents the various closure traits in the language. This +/// will determine the type of the environment (`self`, in the +/// desugaring) argument that the closure expects. +/// +/// You can get the environment type of a closure using +/// `tcx.closure_env_ty()`. +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] +pub enum ClosureKind { + // Warning: Ordering is significant here! The ordering is chosen + // because the trait Fn is a subtrait of FnMut and so in turn, and + // hence we order it so that Fn < FnMut < FnOnce. + Fn, + FnMut, + FnOnce, +} + +impl<'tcx> ClosureKind { + // This is the initial value used when doing upvar inference. + pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; + + pub fn trait_did(&self, tcx: TyCtxt<'tcx>) -> DefId { + match *self { + ClosureKind::Fn => tcx.require_lang_item(FnTraitLangItem, None), + ClosureKind::FnMut => tcx.require_lang_item(FnMutTraitLangItem, None), + ClosureKind::FnOnce => tcx.require_lang_item(FnOnceTraitLangItem, None), + } + } + + /// Returns `true` if this a type that impls this closure kind + /// must also implement `other`. + pub fn extends(self, other: ty::ClosureKind) -> bool { + match (self, other) { + (ClosureKind::Fn, ClosureKind::Fn) => true, + (ClosureKind::Fn, ClosureKind::FnMut) => true, + (ClosureKind::Fn, ClosureKind::FnOnce) => true, + (ClosureKind::FnMut, ClosureKind::FnMut) => true, + (ClosureKind::FnMut, ClosureKind::FnOnce) => true, + (ClosureKind::FnOnce, ClosureKind::FnOnce) => true, + _ => false, + } + } + + /// Returns the representative scalar type for this closure kind. + /// See `TyS::to_opt_closure_kind` for more details. + pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match self { + ty::ClosureKind::Fn => tcx.types.i8, + ty::ClosureKind::FnMut => tcx.types.i16, + ty::ClosureKind::FnOnce => tcx.types.i32, + } + } +} + +impl BorrowKind { + pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { + match m { + hir::Mutability::Mut => MutBorrow, + hir::Mutability::Not => ImmBorrow, + } + } + + /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow + /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a + /// mutability that is stronger than necessary so that it at least *would permit* the borrow in + /// question. + pub fn to_mutbl_lossy(self) -> hir::Mutability { + match self { + MutBorrow => hir::Mutability::Mut, + ImmBorrow => hir::Mutability::Not, + + // We have no type corresponding to a unique imm borrow, so + // use `&mut`. It gives all the capabilities of an `&uniq` + // and hence is a safe "over approximation". + UniqueImmBorrow => hir::Mutability::Mut, + } + } + + pub fn to_user_str(&self) -> &'static str { + match *self { + MutBorrow => "mutable", + ImmBorrow => "immutable", + UniqueImmBorrow => "uniquely immutable", + } + } +} + +pub type Attributes<'tcx> = &'tcx [ast::Attribute]; + +#[derive(Debug, PartialEq, Eq)] +pub enum ImplOverlapKind { + /// These impls are always allowed to overlap. + Permitted { + /// Whether or not the impl is permitted due to the trait being a `#[marker]` trait + marker: bool, + }, + /// These impls are allowed to overlap, but that raises + /// an issue #33140 future-compatibility warning. + /// + /// Some background: in Rust 1.0, the trait-object types `Send + Sync` (today's + /// `dyn Send + Sync`) and `Sync + Send` (now `dyn Sync + Send`) were different. + /// + /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied + /// that difference, making what reduces to the following set of impls: + /// + /// ``` + /// trait Trait {} + /// impl Trait for dyn Send + Sync {} + /// impl Trait for dyn Sync + Send {} + /// ``` + /// + /// Obviously, once we made these types be identical, that code causes a coherence + /// error and a fairly big headache for us. However, luckily for us, the trait + /// `Trait` used in this case is basically a marker trait, and therefore having + /// overlapping impls for it is sound. + /// + /// To handle this, we basically regard the trait as a marker trait, with an additional + /// future-compatibility warning. To avoid accidentally "stabilizing" this feature, + /// it has the following restrictions: + /// + /// 1. The trait must indeed be a marker-like trait (i.e., no items), and must be + /// positive impls. + /// 2. The trait-ref of both impls must be equal. + /// 3. The trait-ref of both impls must be a trait object type consisting only of + /// marker traits. + /// 4. Neither of the impls can have any where-clauses. + /// + /// Once `traitobject` 0.1.0 is no longer an active concern, this hack can be removed. + Issue33140, +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn body_tables(self, body: hir::BodyId) -> &'tcx TypeckTables<'tcx> { + self.typeck_tables_of(self.hir().body_owner_def_id(body)) + } + + /// Returns an iterator of the `DefId`s for all body-owners in this + /// crate. If you would prefer to iterate over the bodies + /// themselves, you can do `self.hir().krate().body_ids.iter()`. + pub fn body_owners(self) -> impl Iterator + Captures<'tcx> + 'tcx { + self.hir() + .krate() + .body_ids + .iter() + .map(move |&body_id| self.hir().body_owner_def_id(body_id)) + } + + pub fn par_body_owners(self, f: F) { + par_iter(&self.hir().krate().body_ids) + .for_each(|&body_id| f(self.hir().body_owner_def_id(body_id))); + } + + pub fn provided_trait_methods(self, id: DefId) -> impl 'tcx + Iterator { + self.associated_items(id) + .in_definition_order() + .filter(|item| item.kind == AssocKind::Fn && item.defaultness.has_value()) + } + + pub fn trait_relevant_for_never(self, did: DefId) -> bool { + self.associated_items(did).in_definition_order().any(|item| item.relevant_for_never()) + } + + pub fn opt_item_name(self, def_id: DefId) -> Option { + def_id + .as_local() + .and_then(|def_id| self.hir().get(self.hir().as_local_hir_id(def_id)).ident()) + } + + pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> { + let is_associated_item = if let Some(def_id) = def_id.as_local() { + match self.hir().get(self.hir().as_local_hir_id(def_id)) { + Node::TraitItem(_) | Node::ImplItem(_) => true, + _ => false, + } + } else { + match self.def_kind(def_id) { + DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => true, + _ => false, + } + }; + + is_associated_item.then(|| self.associated_item(def_id)) + } + + pub fn field_index(self, hir_id: hir::HirId, tables: &TypeckTables<'_>) -> usize { + tables.field_indices().get(hir_id).cloned().expect("no index for a field") + } + + pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option { + variant.fields.iter().position(|field| self.hygienic_eq(ident, field.ident, variant.def_id)) + } + + /// Returns `true` if the impls are the same polarity and the trait either + /// has no items or is annotated `#[marker]` and prevents item overrides. + pub fn impls_are_allowed_to_overlap( + self, + def_id1: DefId, + def_id2: DefId, + ) -> Option { + // If either trait impl references an error, they're allowed to overlap, + // as one of them essentially doesn't exist. + if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error()) + || self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error()) + { + return Some(ImplOverlapKind::Permitted { marker: false }); + } + + match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) { + (ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => { + // `#[rustc_reservation_impl]` impls don't overlap with anything + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)", + def_id1, def_id2 + ); + return Some(ImplOverlapKind::Permitted { marker: false }); + } + (ImplPolarity::Positive, ImplPolarity::Negative) + | (ImplPolarity::Negative, ImplPolarity::Positive) => { + // `impl AutoTrait for Type` + `impl !AutoTrait for Type` + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)", + def_id1, def_id2 + ); + return None; + } + (ImplPolarity::Positive, ImplPolarity::Positive) + | (ImplPolarity::Negative, ImplPolarity::Negative) => {} + }; + + let is_marker_overlap = { + let is_marker_impl = |def_id: DefId| -> bool { + let trait_ref = self.impl_trait_ref(def_id); + trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker) + }; + is_marker_impl(def_id1) && is_marker_impl(def_id2) + }; + + if is_marker_overlap { + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)", + def_id1, def_id2 + ); + Some(ImplOverlapKind::Permitted { marker: true }) + } else { + if let Some(self_ty1) = self.issue33140_self_ty(def_id1) { + if let Some(self_ty2) = self.issue33140_self_ty(def_id2) { + if self_ty1 == self_ty2 { + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK", + def_id1, def_id2 + ); + return Some(ImplOverlapKind::Issue33140); + } else { + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) - found {:?} != {:?}", + def_id1, def_id2, self_ty1, self_ty2 + ); + } + } + } + + debug!("impls_are_allowed_to_overlap({:?}, {:?}) = None", def_id1, def_id2); + None + } + } + + /// Returns `ty::VariantDef` if `res` refers to a struct, + /// or variant or their constructors, panics otherwise. + pub fn expect_variant_res(self, res: Res) -> &'tcx VariantDef { + match res { + Res::Def(DefKind::Variant, did) => { + let enum_did = self.parent(did).unwrap(); + self.adt_def(enum_did).variant_with_id(did) + } + Res::Def(DefKind::Struct | DefKind::Union, did) => self.adt_def(did).non_enum_variant(), + Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_did) => { + let variant_did = self.parent(variant_ctor_did).unwrap(); + let enum_did = self.parent(variant_did).unwrap(); + self.adt_def(enum_did).variant_with_ctor_id(variant_ctor_did) + } + Res::Def(DefKind::Ctor(CtorOf::Struct, ..), ctor_did) => { + let struct_did = self.parent(ctor_did).expect("struct ctor has no parent"); + self.adt_def(struct_did).non_enum_variant() + } + _ => bug!("expect_variant_res used with unexpected res {:?}", res), + } + } + + pub fn item_name(self, id: DefId) -> Symbol { + if id.index == CRATE_DEF_INDEX { + self.original_crate_name(id.krate) + } else { + let def_key = self.def_key(id); + match def_key.disambiguated_data.data { + // The name of a constructor is that of its parent. + rustc_hir::definitions::DefPathData::Ctor => { + self.item_name(DefId { krate: id.krate, index: def_key.parent.unwrap() }) + } + _ => def_key.disambiguated_data.data.get_opt_name().unwrap_or_else(|| { + bug!("item_name: no name for {:?}", self.def_path(id)); + }), + } + } + } + + /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair. + pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> { + match instance { + ty::InstanceDef::Item(did) => self.optimized_mir(did), + ty::InstanceDef::VtableShim(..) + | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::FnPtrShim(..) + | ty::InstanceDef::Virtual(..) + | ty::InstanceDef::ClosureOnceShim { .. } + | ty::InstanceDef::DropGlue(..) + | ty::InstanceDef::CloneShim(..) => self.mir_shims(instance), + } + } + + /// Gets the attributes of a definition. + pub fn get_attrs(self, did: DefId) -> Attributes<'tcx> { + if let Some(did) = did.as_local() { + self.hir().attrs(self.hir().as_local_hir_id(did)) + } else { + self.item_attrs(did) + } + } + + /// Determines whether an item is annotated with an attribute. + pub fn has_attr(self, did: DefId, attr: Symbol) -> bool { + attr::contains_name(&self.get_attrs(did), attr) + } + + /// Returns `true` if this is an `auto trait`. + pub fn trait_is_auto(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).has_auto_impl + } + + pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> { + self.optimized_mir(def_id).generator_layout.as_ref().unwrap() + } + + /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements. + /// If it implements no trait, returns `None`. + pub fn trait_id_of_impl(self, def_id: DefId) -> Option { + self.impl_trait_ref(def_id).map(|tr| tr.def_id) + } + + /// If the given defid describes a method belonging to an impl, returns the + /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. + pub fn impl_of_method(self, def_id: DefId) -> Option { + self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container { + TraitContainer(_) => None, + ImplContainer(def_id) => Some(def_id), + }) + } + + /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` + /// with the name of the crate containing the impl. + pub fn span_of_impl(self, impl_did: DefId) -> Result { + if let Some(impl_did) = impl_did.as_local() { + let hir_id = self.hir().as_local_hir_id(impl_did); + Ok(self.hir().span(hir_id)) + } else { + Err(self.crate_name(impl_did.krate)) + } + } + + /// Hygienically compares a use-site name (`use_name`) for a field or an associated item with + /// its supposed definition name (`def_name`). The method also needs `DefId` of the supposed + /// definition's parent/scope to perform comparison. + pub fn hygienic_eq(self, use_name: Ident, def_name: Ident, def_parent_def_id: DefId) -> bool { + // We could use `Ident::eq` here, but we deliberately don't. The name + // comparison fails frequently, and we want to avoid the expensive + // `normalize_to_macros_2_0()` calls required for the span comparison whenever possible. + use_name.name == def_name.name + && use_name + .span + .ctxt() + .hygienic_eq(def_name.span.ctxt(), self.expansion_that_defined(def_parent_def_id)) + } + + fn expansion_that_defined(self, scope: DefId) -> ExpnId { + match scope.as_local() { + Some(scope) => self.hir().definitions().expansion_that_defined(scope), + None => ExpnId::root(), + } + } + + pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { + ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)); + ident + } + + pub fn adjust_ident_and_get_scope( + self, + mut ident: Ident, + scope: DefId, + block: hir::HirId, + ) -> (Ident, DefId) { + let scope = + match ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)) + { + Some(actual_expansion) => { + self.hir().definitions().parent_module_of_macro_def(actual_expansion) + } + None => self.parent_module(block).to_def_id(), + }; + (ident, scope) + } + + pub fn is_object_safe(self, key: DefId) -> bool { + self.object_safety_violations(key).is_empty() + } +} + +#[derive(Clone, HashStable)] +pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); + +/// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition. +pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + if let Some(def_id) = def_id.as_local() { + if let Node::Item(item) = tcx.hir().get(tcx.hir().as_local_hir_id(def_id)) { + if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { + return opaque_ty.impl_trait_fn; + } + } + } + None +} + +pub fn provide(providers: &mut ty::query::Providers<'_>) { + context::provide(providers); + erase_regions::provide(providers); + layout::provide(providers); + super::util::bug::provide(providers); + *providers = ty::query::Providers { + trait_impls_of: trait_def::trait_impls_of_provider, + all_local_trait_impls: trait_def::all_local_trait_impls, + ..*providers + }; +} + +/// A map for the local crate mapping each type to a vector of its +/// inherent impls. This is not meant to be used outside of coherence; +/// rather, you should request the vector for a specific type via +/// `tcx.inherent_impls(def_id)` so as to minimize your dependencies +/// (constructing this map requires touching the entire crate). +#[derive(Clone, Debug, Default, HashStable)] +pub struct CrateInherentImpls { + pub inherent_impls: DefIdMap>, +} + +#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub struct SymbolName { + // FIXME: we don't rely on interning or equality here - better have + // this be a `&'tcx str`. + pub name: Symbol, +} + +impl SymbolName { + pub fn new(name: &str) -> SymbolName { + SymbolName { name: Symbol::intern(name) } + } +} + +impl PartialOrd for SymbolName { + fn partial_cmp(&self, other: &SymbolName) -> Option { + self.name.as_str().partial_cmp(&other.name.as_str()) + } +} + +/// Ordering must use the chars to ensure reproducible builds. +impl Ord for SymbolName { + fn cmp(&self, other: &SymbolName) -> Ordering { + self.name.as_str().cmp(&other.name.as_str()) + } +} + +impl fmt::Display for SymbolName { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.name, fmt) + } +} + +impl fmt::Debug for SymbolName { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.name, fmt) + } +} diff --git a/src/librustc/ty/normalize_erasing_regions.rs b/src/librustc_middle/ty/normalize_erasing_regions.rs similarity index 86% rename from src/librustc/ty/normalize_erasing_regions.rs rename to src/librustc_middle/ty/normalize_erasing_regions.rs index cbaabd8e1f137..2f0a57c59eb14 100644 --- a/src/librustc/ty/normalize_erasing_regions.rs +++ b/src/librustc_middle/ty/normalize_erasing_regions.rs @@ -4,8 +4,8 @@ //! //! The methods in this file use a `TypeFolder` to recursively process //! contents, invoking the underlying -//! `normalize_ty_after_erasing_regions` query for each type found -//! within. (This underlying query is what is cached.) +//! `normalize_generic_arg_after_erasing_regions` query for each type +//! or constant found within. (This underlying query is what is cached.) use crate::ty::fold::{TypeFoldable, TypeFolder}; use crate::ty::subst::{Subst, SubstsRef}; @@ -94,6 +94,12 @@ impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.tcx.normalize_ty_after_erasing_regions(self.param_env.and(ty)) + let arg = self.param_env.and(ty.into()); + self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_ty() + } + + fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + let arg = self.param_env.and(c.into()); + self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_const() } } diff --git a/src/librustc_middle/ty/outlives.rs b/src/librustc_middle/ty/outlives.rs new file mode 100644 index 0000000000000..3e6a12df6887d --- /dev/null +++ b/src/librustc_middle/ty/outlives.rs @@ -0,0 +1,206 @@ +// The outlines relation `T: 'a` or `'a: 'b`. This code frequently +// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that +// RFC for reference. + +use crate::ty::subst::{GenericArg, GenericArgKind}; +use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; +use smallvec::SmallVec; + +#[derive(Debug)] +pub enum Component<'tcx> { + Region(ty::Region<'tcx>), + Param(ty::ParamTy), + UnresolvedInferenceVariable(ty::InferTy), + + // Projections like `T::Foo` are tricky because a constraint like + // `T::Foo: 'a` can be satisfied in so many ways. There may be a + // where-clause that says `T::Foo: 'a`, or the defining trait may + // include a bound like `type Foo: 'static`, or -- in the most + // conservative way -- we can prove that `T: 'a` (more generally, + // that all components in the projection outlive `'a`). This code + // is not in a position to judge which is the best technique, so + // we just product the projection as a component and leave it to + // the consumer to decide (but see `EscapingProjection` below). + Projection(ty::ProjectionTy<'tcx>), + + // In the case where a projection has escaping regions -- meaning + // regions bound within the type itself -- we always use + // the most conservative rule, which requires that all components + // outlive the bound. So for example if we had a type like this: + // + // for<'a> Trait1< >::Foo > + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // + // then the inner projection (underlined) has an escaping region + // `'a`. We consider that outer trait `'c` to meet a bound if `'b` + // outlives `'b: 'c`, and we don't consider whether the trait + // declares that `Foo: 'static` etc. Therefore, we just return the + // free components of such a projection (in this case, `'b`). + // + // However, in the future, we may want to get smarter, and + // actually return a "higher-ranked projection" here. Therefore, + // we mark that these components are part of an escaping + // projection, so that implied bounds code can avoid relying on + // them. This gives us room to improve the regionck reasoning in + // the future without breaking backwards compat. + EscapingProjection(Vec>), +} + +impl<'tcx> TyCtxt<'tcx> { + /// Push onto `out` all the things that must outlive `'a` for the condition + /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. + pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { + compute_components(self, ty0, out); + debug!("components({:?}) = {:?}", ty0, out); + } +} + +fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { + // Descend through the types, looking for the various "base" + // components and collecting them into `out`. This is not written + // with `collect()` because of the need to sometimes skip subtrees + // in the `subtys` iterator (e.g., when encountering a + // projection). + match ty.kind { + ty::FnDef(_, substs) => { + // HACK(eddyb) ignore lifetimes found shallowly in `substs`. + // This is inconsistent with `ty::Adt` (including all substs) + // and with `ty::Closure` (ignoring all substs other than + // upvars, of which a `ty::FnDef` doesn't have any), but + // consistent with previous (accidental) behavior. + // See https://github.com/rust-lang/rust/issues/70917 + // for further background and discussion. + for &child in substs { + match child.unpack() { + GenericArgKind::Type(ty) => { + compute_components(tcx, ty, out); + } + GenericArgKind::Lifetime(_) => {} + GenericArgKind::Const(_) => { + compute_components_recursive(tcx, child, out); + } + } + } + } + + ty::Array(element, _) => { + // Don't look into the len const as it doesn't affect regions + compute_components(tcx, element, out); + } + + ty::Closure(_, ref substs) => { + for upvar_ty in substs.as_closure().upvar_tys() { + compute_components(tcx, upvar_ty, out); + } + } + + ty::Generator(_, ref substs, _) => { + // Same as the closure case + for upvar_ty in substs.as_generator().upvar_tys() { + compute_components(tcx, upvar_ty, out); + } + + // We ignore regions in the generator interior as we don't + // want these to affect region inference + } + + // All regions are bound inside a witness + ty::GeneratorWitness(..) => (), + + // OutlivesTypeParameterEnv -- the actual checking that `X:'a` + // is implied by the environment is done in regionck. + ty::Param(p) => { + out.push(Component::Param(p)); + } + + // For projections, we prefer to generate an obligation like + // `>::Foo: 'a`, because this gives the + // regionck more ways to prove that it holds. However, + // regionck is not (at least currently) prepared to deal with + // higher-ranked regions that may appear in the + // trait-ref. Therefore, if we see any higher-ranke regions, + // we simply fallback to the most restrictive rule, which + // requires that `Pi: 'a` for all `i`. + ty::Projection(ref data) => { + if !data.has_escaping_bound_vars() { + // best case: no escaping regions, so push the + // projection and skip the subtree (thus generating no + // constraints for Pi). This defers the choice between + // the rules OutlivesProjectionEnv, + // OutlivesProjectionTraitDef, and + // OutlivesProjectionComponents to regionck. + out.push(Component::Projection(*data)); + } else { + // fallback case: hard code + // OutlivesProjectionComponents. Continue walking + // through and constrain Pi. + let mut subcomponents = smallvec![]; + compute_components_recursive(tcx, ty.into(), &mut subcomponents); + out.push(Component::EscapingProjection(subcomponents.into_iter().collect())); + } + } + + // We assume that inference variables are fully resolved. + // So, if we encounter an inference variable, just record + // the unresolved variable as a component. + ty::Infer(infer_ty) => { + out.push(Component::UnresolvedInferenceVariable(infer_ty)); + } + + // Most types do not introduce any region binders, nor + // involve any other subtle cases, and so the WF relation + // simply constraints any regions referenced directly by + // the type and then visits the types that are lexically + // contained within. (The comments refer to relevant rules + // from RFC1214.) + ty::Bool | // OutlivesScalar + ty::Char | // OutlivesScalar + ty::Int(..) | // OutlivesScalar + ty::Uint(..) | // OutlivesScalar + ty::Float(..) | // OutlivesScalar + ty::Never | // ... + ty::Adt(..) | // OutlivesNominalType + ty::Opaque(..) | // OutlivesNominalType (ish) + ty::Foreign(..) | // OutlivesNominalType + ty::Str | // OutlivesScalar (ish) + ty::Slice(..) | // ... + ty::RawPtr(..) | // ... + ty::Ref(..) | // OutlivesReference + ty::Tuple(..) | // ... + ty::FnPtr(_) | // OutlivesFunction (*) + ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) + ty::Placeholder(..) | + ty::Bound(..) | + ty::Error => { + // (*) Function pointers and trait objects are both binders. + // In the RFC, this means we would add the bound regions to + // the "bound regions list". In our representation, no such + // list is maintained explicitly, because bound regions + // themselves can be readily identified. + compute_components_recursive(tcx, ty.into(), out); + } + } +} + +fn compute_components_recursive( + tcx: TyCtxt<'tcx>, + parent: GenericArg<'tcx>, + out: &mut SmallVec<[Component<'tcx>; 4]>, +) { + for child in parent.walk_shallow() { + match child.unpack() { + GenericArgKind::Type(ty) => { + compute_components(tcx, ty, out); + } + GenericArgKind::Lifetime(lt) => { + // Ignore late-bound regions. + if !lt.is_late_bound() { + out.push(Component::Region(lt)); + } + } + GenericArgKind::Const(_) => { + compute_components_recursive(tcx, child, out); + } + } + } +} diff --git a/src/librustc_middle/ty/print/mod.rs b/src/librustc_middle/ty/print/mod.rs new file mode 100644 index 0000000000000..69b36980bd73c --- /dev/null +++ b/src/librustc_middle/ty/print/mod.rs @@ -0,0 +1,346 @@ +use crate::ty::subst::{GenericArg, Subst}; +use crate::ty::{self, DefIdTree, Ty, TyCtxt}; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; + +// `pretty` is a separate module only for organization. +mod pretty; +pub use self::pretty::*; + +pub mod obsolete; + +// FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`. +#[allow(unused_lifetimes)] +pub trait Print<'tcx, P> { + type Output; + type Error; + + fn print(&self, cx: P) -> Result; +} + +/// Interface for outputting user-facing "type-system entities" +/// (paths, types, lifetimes, constants, etc.) as a side-effect +/// (e.g. formatting, like `PrettyPrinter` implementors do) or by +/// constructing some alternative representation (e.g. an AST), +/// which the associated types allow passing through the methods. +/// +/// For pretty-printing/formatting in particular, see `PrettyPrinter`. +// +// FIXME(eddyb) find a better name; this is more general than "printing". +pub trait Printer<'tcx>: Sized { + type Error; + + type Path; + type Region; + type Type; + type DynExistential; + type Const; + + fn tcx(&'a self) -> TyCtxt<'tcx>; + + fn print_def_path( + self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + self.default_print_def_path(def_id, substs) + } + + fn print_impl_path( + self, + impl_def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self.default_print_impl_path(impl_def_id, substs, self_ty, trait_ref) + } + + fn print_region(self, region: ty::Region<'_>) -> Result; + + fn print_type(self, ty: Ty<'tcx>) -> Result; + + fn print_dyn_existential( + self, + predicates: &'tcx ty::List>, + ) -> Result; + + fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result; + + fn path_crate(self, cnum: CrateNum) -> Result; + + fn path_qualified( + self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result; + + fn path_append_impl( + self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result; + + fn path_append( + self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result; + + fn path_generic_args( + self, + print_prefix: impl FnOnce(Self) -> Result, + args: &[GenericArg<'tcx>], + ) -> Result; + + // Defaults (should not be overridden): + + fn default_print_def_path( + self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs); + let key = self.tcx().def_key(def_id); + debug!("default_print_def_path: key={:?}", key); + + match key.disambiguated_data.data { + DefPathData::CrateRoot => { + assert!(key.parent.is_none()); + self.path_crate(def_id.krate) + } + + DefPathData::Impl => { + let generics = self.tcx().generics_of(def_id); + let mut self_ty = self.tcx().type_of(def_id); + let mut impl_trait_ref = self.tcx().impl_trait_ref(def_id); + if substs.len() >= generics.count() { + self_ty = self_ty.subst(self.tcx(), substs); + impl_trait_ref = impl_trait_ref.subst(self.tcx(), substs); + } + self.print_impl_path(def_id, substs, self_ty, impl_trait_ref) + } + + _ => { + let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; + + let mut parent_substs = substs; + let mut trait_qualify_parent = false; + if !substs.is_empty() { + let generics = self.tcx().generics_of(def_id); + parent_substs = &substs[..generics.parent_count.min(substs.len())]; + + match key.disambiguated_data.data { + // Closures' own generics are only captures, don't print them. + DefPathData::ClosureExpr => {} + + // If we have any generic arguments to print, we do that + // on top of the same path, but without its own generics. + _ => { + if !generics.params.is_empty() && substs.len() >= generics.count() { + let args = self.generic_args_to_print(generics, substs); + return self.path_generic_args( + |cx| cx.print_def_path(def_id, parent_substs), + args, + ); + } + } + } + + // FIXME(eddyb) try to move this into the parent's printing + // logic, instead of doing it when printing the child. + trait_qualify_parent = generics.has_self + && generics.parent == Some(parent_def_id) + && parent_substs.len() == generics.parent_count + && self.tcx().generics_of(parent_def_id).parent_count == 0; + } + + self.path_append( + |cx: Self| { + if trait_qualify_parent { + let trait_ref = ty::TraitRef::new( + parent_def_id, + cx.tcx().intern_substs(parent_substs), + ); + cx.path_qualified(trait_ref.self_ty(), Some(trait_ref)) + } else { + cx.print_def_path(parent_def_id, parent_substs) + } + }, + &key.disambiguated_data, + ) + } + } + } + + fn generic_args_to_print( + &self, + generics: &'tcx ty::Generics, + substs: &'tcx [GenericArg<'tcx>], + ) -> &'tcx [GenericArg<'tcx>] { + let mut own_params = generics.parent_count..generics.count(); + + // Don't print args for `Self` parameters (of traits). + if generics.has_self && own_params.start == 0 { + own_params.start = 1; + } + + // Don't print args that are the defaults of their respective parameters. + own_params.end -= generics + .params + .iter() + .rev() + .take_while(|param| { + match param.kind { + ty::GenericParamDefKind::Lifetime => false, + ty::GenericParamDefKind::Type { has_default, .. } => { + has_default + && substs[param.index as usize] + == GenericArg::from( + self.tcx().type_of(param.def_id).subst(self.tcx(), substs), + ) + } + ty::GenericParamDefKind::Const => false, // FIXME(const_generics:defaults) + } + }) + .count(); + + &substs[own_params] + } + + fn default_print_impl_path( + self, + impl_def_id: DefId, + _substs: &'tcx [GenericArg<'tcx>], + self_ty: Ty<'tcx>, + impl_trait_ref: Option>, + ) -> Result { + debug!( + "default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}", + impl_def_id, self_ty, impl_trait_ref + ); + + let key = self.tcx().def_key(impl_def_id); + let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; + + // Decide whether to print the parent path for the impl. + // Logically, since impls are global, it's never needed, but + // users may find it useful. Currently, we omit the parent if + // the impl is either in the same module as the self-type or + // as the trait. + let in_self_mod = match characteristic_def_id_of_type(self_ty) { + None => false, + Some(ty_def_id) => self.tcx().parent(ty_def_id) == Some(parent_def_id), + }; + let in_trait_mod = match impl_trait_ref { + None => false, + Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == Some(parent_def_id), + }; + + if !in_self_mod && !in_trait_mod { + // If the impl is not co-located with either self-type or + // trait-type, then fallback to a format that identifies + // the module more clearly. + self.path_append_impl( + |cx| cx.print_def_path(parent_def_id, &[]), + &key.disambiguated_data, + self_ty, + impl_trait_ref, + ) + } else { + // Otherwise, try to give a good form that would be valid language + // syntax. Preferably using associated item notation. + self.path_qualified(self_ty, impl_trait_ref) + } + } +} + +/// As a heuristic, when we see an impl, if we see that the +/// 'self type' is a type defined in the same module as the impl, +/// we can omit including the path to the impl itself. This +/// function tries to find a "characteristic `DefId`" for a +/// type. It's just a heuristic so it makes some questionable +/// decisions and we may want to adjust it later. +pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { + match ty.kind { + ty::Adt(adt_def, _) => Some(adt_def.did), + + ty::Dynamic(data, ..) => data.principal_def_id(), + + ty::Array(subty, _) | ty::Slice(subty) => characteristic_def_id_of_type(subty), + + ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty), + + ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty), + + ty::Tuple(ref tys) => { + tys.iter().find_map(|ty| characteristic_def_id_of_type(ty.expect_ty())) + } + + ty::FnDef(def_id, _) + | ty::Closure(def_id, _) + | ty::Generator(def_id, _, _) + | ty::Foreign(def_id) => Some(def_id), + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Str + | ty::FnPtr(_) + | ty::Projection(_) + | ty::Placeholder(..) + | ty::Param(_) + | ty::Opaque(..) + | ty::Infer(_) + | ty::Bound(..) + | ty::Error + | ty::GeneratorWitness(..) + | ty::Never + | ty::Float(_) => None, + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { + type Output = P::Region; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_region(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'_> { + type Output = P::Region; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_region(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { + type Output = P::Type; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_type(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List> { + type Output = P::DynExistential; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_dyn_existential(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::Const<'tcx> { + type Output = P::Const; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_const(self) + } +} diff --git a/src/librustc/ty/print/obsolete.rs b/src/librustc_middle/ty/print/obsolete.rs similarity index 98% rename from src/librustc/ty/print/obsolete.rs rename to src/librustc_middle/ty/print/obsolete.rs index 7605d44c7f30f..41a6cd5466f5e 100644 --- a/src/librustc/ty/print/obsolete.rs +++ b/src/librustc_middle/ty/print/obsolete.rs @@ -5,9 +5,9 @@ //! Note: A lot of this could looks very similar to what's already in `ty::print`. //! FIXME(eddyb) implement a custom `PrettyPrinter` for this. -use rustc::bug; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Const, Instance, Ty, TyCtxt}; +use crate::bug; +use crate::ty::subst::SubstsRef; +use crate::ty::{self, Const, Instance, Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use std::fmt::Write; @@ -148,7 +148,6 @@ impl DefPathBasedNames<'tcx> { | ty::Bound(..) | ty::Infer(_) | ty::Placeholder(..) - | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::Param(_) | ty::GeneratorWitness(_) diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc_middle/ty/print/pretty.rs similarity index 92% rename from src/librustc/ty/print/pretty.rs rename to src/librustc_middle/ty/print/pretty.rs index 58bff2f13eb70..2502a4a13a8f0 100644 --- a/src/librustc/ty/print/pretty.rs +++ b/src/librustc_middle/ty/print/pretty.rs @@ -1,19 +1,19 @@ -use crate::hir::map::{DefPathData, DisambiguatedDefPathData}; use crate::middle::cstore::{ExternCrate, ExternCrateSource}; use crate::middle::region; use crate::mir::interpret::{sign_extend, truncate, AllocId, ConstValue, Pointer, Scalar}; -use crate::ty::layout::{Integer, IntegerExt, Size}; +use crate::ty::layout::IntegerExt; use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; use crate::ty::{self, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; - use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; use rustc_ast::ast; use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_span::symbol::{kw, Symbol}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Namespace}; +use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_target::abi::{Integer, Size}; use rustc_target::spec::abi::Abi; use std::cell::Cell; @@ -150,13 +150,10 @@ impl RegionHighlightMode { /// Returns `Some(n)` with the number to use for the given region, if any. fn region_highlighted(&self, region: ty::Region<'_>) -> Option { - self.highlight_regions - .iter() - .filter_map(|h| match h { - Some((r, n)) if r == region => Some(*n), - _ => None, - }) - .next() + self.highlight_regions.iter().find_map(|h| match h { + Some((r, n)) if r == region => Some(*n), + _ => None, + }) } /// Highlight the given bound region. @@ -319,18 +316,15 @@ pub trait PrettyPrinter<'tcx>: debug!("try_print_visible_def_path: cur_def_key={:?}", cur_def_key); // For a constructor, we want the name of its parent rather than . - match cur_def_key.disambiguated_data.data { - DefPathData::Ctor => { - let parent = DefId { - krate: def_id.krate, - index: cur_def_key - .parent - .expect("`DefPathData::Ctor` / `VariantData` missing a parent"), - }; + if let DefPathData::Ctor = cur_def_key.disambiguated_data.data { + let parent = DefId { + krate: def_id.krate, + index: cur_def_key + .parent + .expect("`DefPathData::Ctor` / `VariantData` missing a parent"), + }; - cur_def_key = self.tcx().def_key(parent); - } - _ => {} + cur_def_key = self.tcx().def_key(parent); } let visible_parent = match visible_parent_map.get(&def_id).cloned() { @@ -501,16 +495,9 @@ pub trait PrettyPrinter<'tcx>: } ty::Never => p!(write("!")), ty::Tuple(ref tys) => { - p!(write("(")); - let mut tys = tys.iter(); - if let Some(&ty) = tys.next() { - p!(print(ty), write(",")); - if let Some(&ty) = tys.next() { - p!(write(" "), print(ty)); - for &ty in tys { - p!(write(", "), print(ty)); - } - } + p!(write("("), comma_sep(tys.iter().copied())); + if tys.len() == 1 { + p!(write(",")); } p!(write(")")) } @@ -553,16 +540,13 @@ pub trait PrettyPrinter<'tcx>: p!(print_def_path(def_id, &[])); } ty::Projection(ref data) => p!(print(data)), - ty::UnnormalizedProjection(ref data) => { - p!(write("Unnormalized("), print(data), write(")")) - } ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), ty::Opaque(def_id, substs) => { // FIXME(eddyb) print this with `print_def_path`. // We use verbose printing in 'NO_QUERIES' mode, to // avoid needing to call `predicates_of`. This should // only affect certain debug messages (e.g. messages printed - // from `rustc::ty` during the computation of `tcx.predicates_of`), + // from `rustc_middle::ty` during the computation of `tcx.predicates_of`), // and should have no effect on any compiler output. if self.tcx().sess.verbose() || NO_QUERIES.with(|q| q.get()) { p!(write("Opaque({:?}, {:?})", def_id, substs)); @@ -573,15 +557,10 @@ pub trait PrettyPrinter<'tcx>: let def_key = self.tcx().def_key(def_id); if let Some(name) = def_key.disambiguated_data.data.get_opt_name() { p!(write("{}", name)); - let mut substs = substs.iter(); // FIXME(eddyb) print this with `print_def_path`. - if let Some(first) = substs.next() { - p!(write("::<")); - p!(print(first)); - for subst in substs { - p!(write(", "), print(subst)); - } - p!(write(">")); + if !substs.is_empty() { + p!(write("::")); + p!(generic_delimiters(|cx| cx.comma_sep(substs.iter().copied()))); } return Ok(self); } @@ -623,7 +602,8 @@ pub trait PrettyPrinter<'tcx>: } // FIXME(eddyb) should use `def_span`. - if let Some(hir_id) = self.tcx().hir().as_local_hir_id(did) { + if let Some(did) = did.as_local() { + let hir_id = self.tcx().hir().as_local_hir_id(did); p!(write("@{:?}", self.tcx().hir().span(hir_id))); if substs.as_generator().is_valid() { @@ -642,9 +622,7 @@ pub trait PrettyPrinter<'tcx>: } } } else { - // Cross-crate closure types should only be - // visible in codegen bug reports, I imagine. - p!(write("@{:?}", did)); + p!(write("@{}", self.tcx().def_path_str(did))); if substs.as_generator().is_valid() { let upvar_tys = substs.as_generator().upvar_tys(); @@ -669,9 +647,10 @@ pub trait PrettyPrinter<'tcx>: p!(write("[closure")); // FIXME(eddyb) should use `def_span`. - if let Some(hir_id) = self.tcx().hir().as_local_hir_id(did) { + if let Some(did) = did.as_local() { + let hir_id = self.tcx().hir().as_local_hir_id(did); if self.tcx().sess.opts.debugging_opts.span_free_formats { - p!(write("@"), print_def_path(did, substs)); + p!(write("@"), print_def_path(did.to_def_id(), substs)); } else { p!(write("@{:?}", self.tcx().hir().span(hir_id))); } @@ -692,9 +671,7 @@ pub trait PrettyPrinter<'tcx>: } } } else { - // Cross-crate closure types should only be - // visible in codegen bug reports, I imagine. - p!(write("@{:?}", did)); + p!(write("@{}", self.tcx().def_path_str(did))); if substs.as_closure().is_valid() { let upvar_tys = substs.as_closure().upvar_tys(); @@ -721,12 +698,14 @@ pub trait PrettyPrinter<'tcx>: if self.tcx().sess.verbose() { p!(write("{:?}", sz)); } else if let ty::ConstKind::Unevaluated(..) = sz.val { - // do not try to evaluate unevaluated constants. If we are const evaluating an + // Do not try to evaluate unevaluated constants. If we are const evaluating an // array length anon const, rustc will (with debug assertions) print the // constant's path. Which will end up here again. p!(write("_")); } else if let Some(n) = sz.val.try_to_bits(self.tcx().data_layout.pointer_size) { p!(write("{}", n)); + } else if let ty::ConstKind::Param(param) = sz.val { + p!(write("{}", param)); } else { p!(write("_")); } @@ -857,16 +836,12 @@ pub trait PrettyPrinter<'tcx>: ) -> Result { define_scoped_cx!(self); - p!(write("(")); - let mut inputs = inputs.iter(); - if let Some(&ty) = inputs.next() { - p!(print(ty)); - for &ty in inputs { - p!(write(", "), print(ty)); - } - if c_variadic { - p!(write(", ...")); + p!(write("("), comma_sep(inputs.iter().copied())); + if c_variadic { + if !inputs.is_empty() { + p!(write(", ")); } + p!(write("...")); } p!(write(")")); if !output.is_unit() { @@ -912,9 +887,9 @@ pub trait PrettyPrinter<'tcx>: p!(write("::{:?}", promoted)); } else { match self.tcx().def_kind(did) { - Some(DefKind::Static) - | Some(DefKind::Const) - | Some(DefKind::AssocConst) => p!(print_value_path(did, substs)), + DefKind::Static | DefKind::Const | DefKind::AssocConst => { + p!(print_value_path(did, substs)) + } _ => { if did.is_local() { let span = self.tcx().def_span(did); @@ -941,6 +916,7 @@ pub trait PrettyPrinter<'tcx>: self.pretty_print_bound_var(debruijn, bound_var)? } ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + ty::ConstKind::Error => p!(write("[const error]")), }; Ok(self) } @@ -979,10 +955,9 @@ pub trait PrettyPrinter<'tcx>: ) => { let byte_str = self .tcx() - .alloc_map - .lock() - .unwrap_memory(ptr.alloc_id) - .get_bytes(&self.tcx(), ptr, Size::from_bytes(*data as u64)) + .global_alloc(ptr.alloc_id) + .unwrap_memory() + .get_bytes(&self.tcx(), ptr, Size::from_bytes(*data)) .unwrap(); p!(pretty_print_byte_str(byte_str)); } @@ -1044,10 +1019,7 @@ pub trait PrettyPrinter<'tcx>: )?; } (Scalar::Ptr(ptr), ty::FnPtr(_)) => { - let instance = { - let alloc_map = self.tcx().alloc_map.lock(); - alloc_map.unwrap_fn(ptr.alloc_id) - }; + let instance = self.tcx().global_alloc(ptr.alloc_id).unwrap_fn(); self = self.typed_value( |this| this.print_value_path(instance.def_id(), instance.substs), |this| this.print_type(ty), @@ -1056,19 +1028,6 @@ pub trait PrettyPrinter<'tcx>: } // For function type zsts just printing the path is enough (Scalar::Raw { size: 0, .. }, ty::FnDef(d, s)) => p!(print_value_path(*d, s)), - // Empty tuples are frequently occurring, so don't print the fallback. - (Scalar::Raw { size: 0, .. }, ty::Tuple(ts)) if ts.is_empty() => p!(write("()")), - // Zero element arrays have a trivial representation. - ( - Scalar::Raw { size: 0, .. }, - ty::Array( - _, - ty::Const { - val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: 0, .. })), - .. - }, - ), - ) => p!(write("[]")), // Nontrivial types with scalar bit representation (Scalar::Raw { data, size }, _) => { let print = |mut this: Self| { @@ -1137,14 +1096,14 @@ pub trait PrettyPrinter<'tcx>: define_scoped_cx!(self); if self.tcx().sess.verbose() { - p!(write("ConstValue({:?}: {:?})", ct, ty)); + p!(write("ConstValue({:?}: ", ct), print(ty), write(")")); return Ok(self); } let u8_type = self.tcx().types.u8; match (ct, &ty.kind) { - (ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty), + // Byte/string slices, printed as (byte) string literals. ( ConstValue::Slice { data, start, end }, ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _), @@ -1170,7 +1129,7 @@ pub trait PrettyPrinter<'tcx>: (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { let n = n.val.try_to_bits(self.tcx().data_layout.pointer_size).unwrap(); // cast is ok because we already checked for pointer size (32 or 64 bit) above - let n = Size::from_bytes(n as u64); + let n = Size::from_bytes(n); let ptr = Pointer::new(AllocId(0), offset); let byte_str = alloc.get_bytes(&self.tcx(), ptr, n).unwrap(); @@ -1178,6 +1137,66 @@ pub trait PrettyPrinter<'tcx>: p!(pretty_print_byte_str(byte_str)); Ok(self) } + + // Aggregates, printed as array/tuple/struct/variant construction syntax. + // + // NB: the `has_param_types_or_consts` check ensures that we can use + // the `destructure_const` query with an empty `ty::ParamEnv` without + // introducing ICEs (e.g. via `layout_of`) from missing bounds. + // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` + // to be able to destructure the tuple into `(0u8, *mut T) + // + // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the + // correct `ty::ParamEnv` to allow printing *all* constant values. + (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { + let contents = self.tcx().destructure_const( + ty::ParamEnv::reveal_all() + .and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })), + ); + let fields = contents.fields.iter().copied(); + + match ty.kind { + ty::Array(..) => { + p!(write("["), comma_sep(fields), write("]")); + } + ty::Tuple(..) => { + p!(write("("), comma_sep(fields)); + if contents.fields.len() == 1 { + p!(write(",")); + } + p!(write(")")); + } + ty::Adt(def, substs) => { + let variant_def = &def.variants[contents.variant]; + p!(print_value_path(variant_def.def_id, substs)); + + match variant_def.ctor_kind { + CtorKind::Const => {} + CtorKind::Fn => { + p!(write("("), comma_sep(fields), write(")")); + } + CtorKind::Fictive => { + p!(write(" {{ ")); + let mut first = true; + for (field_def, field) in variant_def.fields.iter().zip(fields) { + if !first { + p!(write(", ")); + } + p!(write("{}: ", field_def.ident), print(field)); + first = false; + } + p!(write(" }}")); + } + } + } + _ => unreachable!(), + } + + Ok(self) + } + + (ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty), + // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading // their fields instead of just dumping the memory. _ => { @@ -1421,9 +1440,8 @@ impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { self = print_prefix(self)?; // Skip `::{{constructor}}` on tuple/unit structs. - match disambiguated_data.data { - DefPathData::Ctor => return Ok(self), - _ => {} + if let DefPathData::Ctor = disambiguated_data.data { + return Ok(self); } // FIXME(eddyb) `name` should never be empty, but it @@ -1433,7 +1451,7 @@ impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { if !self.empty_path { write!(self, "::")?; } - if ast::Ident::from_str(&name).is_raw_guess() { + if Ident::from_str(&name).is_raw_guess() { write!(self, "r#")?; } write!(self, "{}", name)?; @@ -1785,11 +1803,8 @@ impl FmtPrinter<'_, 'tcx, F> { struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - match *r { - ty::ReLateBound(_, ty::BrNamed(_, name)) => { - self.0.insert(name); - } - _ => {} + if let ty::ReLateBound(_, ty::BrNamed(_, name)) = *r { + self.0.insert(name); } r.super_visit_with(self) } @@ -1920,15 +1935,7 @@ define_print_and_forward_display! { (self, cx): &'tcx ty::List> { - p!(write("{{")); - let mut tys = self.iter(); - if let Some(&ty) = tys.next() { - p!(print(ty)); - for &ty in tys { - p!(write(", "), print(ty)); - } - } - p!(write("}}")) + p!(write("{{"), comma_sep(self.iter().copied()), write("}}")) } ty::TypeAndMut<'tcx> { @@ -2051,6 +2058,13 @@ define_print_and_forward_display! { print_value_path(def_id, substs), write("` can be evaluated")) } + ty::Predicate::ConstEquate(c1, c2) => { + p!(write("the constant `"), + print(c1), + write("` equals `"), + print(c2), + write("`")) + } } } diff --git a/src/librustc/ty/query/README.md b/src/librustc_middle/ty/query/README.md similarity index 100% rename from src/librustc/ty/query/README.md rename to src/librustc_middle/ty/query/README.md diff --git a/src/librustc_middle/ty/query/job.rs b/src/librustc_middle/ty/query/job.rs new file mode 100644 index 0000000000000..5f7a9e81158e0 --- /dev/null +++ b/src/librustc_middle/ty/query/job.rs @@ -0,0 +1,29 @@ +use crate::ty::tls; + +use rustc_query_system::query::deadlock; +use rustc_rayon_core as rayon_core; +use std::thread; + +/// Creates a new thread and forwards information in thread locals to it. +/// The new thread runs the deadlock handler. +/// Must only be called when a deadlock is about to happen. +pub unsafe fn handle_deadlock() { + let registry = rayon_core::Registry::current(); + + let gcx_ptr = tls::GCX_PTR.with(|gcx_ptr| gcx_ptr as *const _); + let gcx_ptr = &*gcx_ptr; + + let rustc_span_globals = + rustc_span::GLOBALS.with(|rustc_span_globals| rustc_span_globals as *const _); + let rustc_span_globals = &*rustc_span_globals; + let syntax_globals = rustc_ast::attr::GLOBALS.with(|syntax_globals| syntax_globals as *const _); + let syntax_globals = &*syntax_globals; + thread::spawn(move || { + tls::GCX_PTR.set(gcx_ptr, || { + rustc_ast::attr::GLOBALS.set(syntax_globals, || { + rustc_span::GLOBALS + .set(rustc_span_globals, || tls::with_global(|tcx| deadlock(tcx, ®istry))) + }); + }) + }); +} diff --git a/src/librustc/ty/query/keys.rs b/src/librustc_middle/ty/query/keys.rs similarity index 90% rename from src/librustc/ty/query/keys.rs rename to src/librustc_middle/ty/query/keys.rs index 6073d3a545f6d..4acf766f033d8 100644 --- a/src/librustc/ty/query/keys.rs +++ b/src/librustc_middle/ty/query/keys.rs @@ -2,12 +2,11 @@ use crate::infer::canonical::Canonical; use crate::mir; -use crate::traits; use crate::ty::fast_reject::SimplifiedType; -use crate::ty::query::caches::DefaultCacheSelector; -use crate::ty::subst::SubstsRef; +use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_query_system::query::DefaultCacheSelector; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; @@ -117,6 +116,17 @@ impl Key for (DefId, DefId) { } } +impl Key for (DefId, LocalDefId) { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + self.0.krate + } + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.1.default_span(tcx) + } +} + impl Key for (CrateNum, DefId) { type CacheSelector = DefaultCacheSelector; @@ -194,6 +204,17 @@ impl<'tcx> Key for ty::PolyTraitRef<'tcx> { } } +impl<'tcx> Key for GenericArg<'tcx> { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for &'tcx ty::Const<'tcx> { type CacheSelector = DefaultCacheSelector; @@ -238,31 +259,32 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> { } } -impl<'tcx> Key for traits::Environment<'tcx> { +impl Key for Symbol { type CacheSelector = DefaultCacheSelector; fn query_crate(&self) -> CrateNum { LOCAL_CRATE } - fn default_span(&self, _: TyCtxt<'_>) -> Span { + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl Key for Symbol { +/// Canonical query goals correspond to abstract trait operations that +/// are not tied to any crate in particular. +impl<'tcx, T> Key for Canonical<'tcx, T> { type CacheSelector = DefaultCacheSelector; fn query_crate(&self) -> CrateNum { LOCAL_CRATE } + 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> Key for Canonical<'tcx, T> { +impl Key for (Symbol, u32, u32) { type CacheSelector = DefaultCacheSelector; fn query_crate(&self) -> CrateNum { @@ -274,7 +296,7 @@ impl<'tcx, T> Key for Canonical<'tcx, T> { } } -impl Key for (Symbol, u32, u32) { +impl<'tcx> Key for (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>) { type CacheSelector = DefaultCacheSelector; fn query_crate(&self) -> CrateNum { diff --git a/src/librustc_middle/ty/query/mod.rs b/src/librustc_middle/ty/query/mod.rs new file mode 100644 index 0000000000000..e1a5a766ca15a --- /dev/null +++ b/src/librustc_middle/ty/query/mod.rs @@ -0,0 +1,218 @@ +use crate::dep_graph::{self, DepNode, DepNodeParams}; +use crate::hir::exports::Export; +use crate::hir::map; +use crate::infer::canonical::{self, Canonical}; +use crate::lint::LintLevelMap; +use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::cstore::{CrateSource, DepKind, NativeLibraryKind}; +use crate::middle::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLibrary}; +use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use crate::middle::lib_features::LibFeatures; +use crate::middle::privacy::AccessLevels; +use crate::middle::region; +use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLifetimes}; +use crate::middle::stability::{self, DeprecationEntry}; +use crate::mir; +use crate::mir::interpret::GlobalId; +use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue}; +use crate::mir::interpret::{LitToConstError, LitToConstInput}; +use crate::mir::mono::CodegenUnit; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, +}; +use crate::traits::query::{ + DropckOutlivesResult, DtorckConstraint, MethodAutoderefStepsResult, NormalizationResult, + OutlivesBound, +}; +use crate::traits::specialization_graph; +use crate::traits::{self, Vtable}; +use crate::ty::steal::Steal; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::util::AlwaysRequiresDrop; +use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::profiling::ProfileCategory::*; +use rustc_data_structures::stable_hasher::StableVec; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; +use rustc_hir::lang_items::{LangItem, LanguageItems}; +use rustc_hir::{Crate, HirIdSet, ItemLocalId, TraitCandidate}; +use rustc_index::vec::IndexVec; +use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; +use rustc_session::CrateDisambiguator; +use rustc_target::spec::PanicStrategy; + +use rustc_ast::ast; +use rustc_attr as attr; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::ops::Deref; +use std::sync::Arc; + +#[macro_use] +mod plumbing; +pub(crate) use rustc_query_system::query::CycleError; +use rustc_query_system::query::*; + +mod stats; +pub use self::stats::print_stats; + +#[cfg(parallel_compiler)] +mod job; +#[cfg(parallel_compiler)] +pub use self::job::handle_deadlock; +pub use rustc_query_system::query::{QueryInfo, QueryJob, QueryJobId}; + +mod keys; +use self::keys::Key; + +mod values; +use self::values::Value; + +use rustc_query_system::query::QueryAccessors; +pub use rustc_query_system::query::QueryConfig; +pub(crate) use rustc_query_system::query::QueryDescription; + +mod on_disk_cache; +pub use self::on_disk_cache::OnDiskCache; + +mod profiling_support; +pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder}; + +// Each of these queries corresponds to a function pointer field in the +// `Providers` struct for requesting a value of that type, and a method +// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way +// which memoizes and does dep-graph tracking, wrapping around the actual +// `Providers` that the driver creates (using several `rustc_*` crates). +// +// The result type of each query must implement `Clone`, and additionally +// `ty::query::values::Value`, which produces an appropriate placeholder +// (error) value if the query resulted in a query cycle. +// Queries marked with `fatal_cycle` do not need the latter implementation, +// as they will raise an fatal error on query cycles instead. + +rustc_query_append! { [define_queries!][<'tcx>] } + +/// The red/green evaluation system will try to mark a specific DepNode in the +/// dependency graph as green by recursively trying to mark the dependencies of +/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` +/// where we don't know if it is red or green and we therefore actually have +/// to recompute its value in order to find out. Since the only piece of +/// information that we have at that point is the `DepNode` we are trying to +/// re-evaluate, we need some way to re-run a query from just that. This is what +/// `force_from_dep_node()` implements. +/// +/// In the general case, a `DepNode` consists of a `DepKind` and an opaque +/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint +/// is usually constructed by computing a stable hash of the query-key that the +/// `DepNode` corresponds to. Consequently, it is not in general possible to go +/// back from hash to query-key (since hash functions are not reversible). For +/// this reason `force_from_dep_node()` is expected to fail from time to time +/// because we just cannot find out, from the `DepNode` alone, what the +/// corresponding query-key is and therefore cannot re-run the query. +/// +/// The system deals with this case letting `try_mark_green` fail which forces +/// the root query to be re-evaluated. +/// +/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. +/// Fortunately, we can use some contextual information that will allow us to +/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we +/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a +/// valid `DefPathHash`. Since we also always build a huge table that maps every +/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have +/// everything we need to re-run the query. +/// +/// Take the `mir_validated` query as an example. Like many other queries, it +/// just has a single parameter: the `DefId` of the item it will compute the +/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` +/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` +/// is actually a `DefPathHash`, and can therefore just look up the corresponding +/// `DefId` in `tcx.def_path_hash_to_def_id`. +/// +/// When you implement a new query, it will likely have a corresponding new +/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As +/// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, +/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just +/// add it to the "We don't have enough information to reconstruct..." group in +/// the match below. +pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool { + // We must avoid ever having to call `force_from_dep_node()` for a + // `DepNode::codegen_unit`: + // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!( + dep_node.kind != crate::dep_graph::DepKind::codegen_unit, + "calling force_from_dep_node() on DepKind::codegen_unit" + ); + + if !dep_node.kind.can_reconstruct_query_key() { + return false; + } + + rustc_dep_node_force!([dep_node, tcx] + // These are inputs that are expected to be pre-allocated and that + // should therefore always be red or green already. + crate::dep_graph::DepKind::CrateMetadata | + + // These are anonymous nodes. + crate::dep_graph::DepKind::TraitSelect | + + // We don't have enough information to reconstruct the query key of + // these. + crate::dep_graph::DepKind::CompileCodegenUnit => { + bug!("force_from_dep_node: encountered {:?}", dep_node) + } + ); + + false +} + +pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { + rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx) +} + +mod sealed { + use super::{DefId, LocalDefId}; + + /// An analogue of the `Into` trait that's intended only for query paramaters. + /// + /// This exists to allow queries to accept either `DefId` or `LocalDefId` while requiring that the + /// user call `to_def_id` to convert between them everywhere else. + pub trait IntoQueryParam

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

IntoQueryParam

for P { + #[inline(always)] + fn into_query_param(self) -> P { + self + } + } + + impl IntoQueryParam for LocalDefId { + #[inline(always)] + fn into_query_param(self) -> DefId { + self.to_def_id() + } + } +} + +use sealed::IntoQueryParam; diff --git a/src/librustc/ty/query/on_disk_cache.rs b/src/librustc_middle/ty/query/on_disk_cache.rs similarity index 99% rename from src/librustc/ty/query/on_disk_cache.rs rename to src/librustc_middle/ty/query/on_disk_cache.rs index 781abea75d9f7..71c2c24cc0a9f 100644 --- a/src/librustc/ty/query/on_disk_cache.rs +++ b/src/librustc_middle/ty/query/on_disk_cache.rs @@ -1,17 +1,16 @@ use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; -use crate::hir::map::definitions::DefPathHash; -use crate::ich::{CachingSourceMapView, Fingerprint}; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use crate::mir::{self, interpret}; use crate::ty::codec::{self as ty_codec, TyDecoder, TyEncoder}; use crate::ty::context::TyCtxt; use crate::ty::{self, Ty}; -use rustc_ast::ast::Ident; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, Once}; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathHash; use rustc_index::vec::{Idx, IndexVec}; use rustc_serialize::{ opaque, Decodable, Decoder, Encodable, Encoder, SpecializedDecoder, SpecializedEncoder, @@ -20,6 +19,8 @@ use rustc_serialize::{ use rustc_session::{CrateDisambiguator, Session}; use rustc_span::hygiene::{ExpnId, SyntaxContext}; use rustc_span::source_map::{SourceMap, StableSourceFileId}; +use rustc_span::symbol::Ident; +use rustc_span::CachingSourceMapView; use rustc_span::{BytePos, SourceFile, Span, DUMMY_SP}; use std::mem; @@ -921,6 +922,7 @@ where { type Error = E::Error; + #[inline] fn emit_unit(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -993,7 +995,8 @@ fn encode_query_results<'a, 'tcx, Q, E>( query_result_index: &mut EncodedQueryResultIndex, ) -> Result<(), E::Error> where - Q: super::config::QueryDescription<'tcx, Value: Encodable>, + Q: super::QueryDescription>, + Q::Value: Encodable, E: 'a + TyEncoder, { let _timer = tcx @@ -1006,7 +1009,7 @@ where state.iter_results(|results| { for (key, value, dep_node) in results { - if Q::cache_on_disk(tcx, key.clone(), Some(&value)) { + if Q::cache_on_disk(tcx, &key, Some(&value)) { let dep_node = SerializedDepNodeIndex::new(dep_node.index()); // Record position of the cache entry. diff --git a/src/librustc_middle/ty/query/plumbing.rs b/src/librustc_middle/ty/query/plumbing.rs new file mode 100644 index 0000000000000..c711de9476e9d --- /dev/null +++ b/src/librustc_middle/ty/query/plumbing.rs @@ -0,0 +1,556 @@ +//! The implementation of the query system itself. This defines the macros that +//! generate the actual methods on tcx which find and execute the provider, +//! manage the caches, and so forth. + +use crate::dep_graph::DepGraph; +use crate::ty::query::Query; +use crate::ty::tls::{self, ImplicitCtxt}; +use crate::ty::{self, TyCtxt}; +use rustc_query_system::query::QueryContext; +use rustc_query_system::query::{CycleError, QueryJobId, QueryJobInfo}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lock; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, Handler, Level}; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +impl QueryContext for TyCtxt<'tcx> { + type Query = Query<'tcx>; + + fn incremental_verify_ich(&self) -> bool { + self.sess.opts.debugging_opts.incremental_verify_ich + } + fn verbose(&self) -> bool { + self.sess.verbose() + } + + fn def_path_str(&self, def_id: DefId) -> String { + TyCtxt::def_path_str(*self, def_id) + } + + fn dep_graph(&self) -> &DepGraph { + &self.dep_graph + } + + fn current_query_job(&self) -> Option> { + tls::with_related_context(*self, |icx| icx.query) + } + + fn try_collect_active_jobs( + &self, + ) -> Option, QueryJobInfo>> { + self.queries.try_collect_active_jobs() + } + + /// Executes a job by changing the `ImplicitCtxt` to point to the + /// new query job while it executes. It returns the diagnostics + /// captured during execution and the actual result. + #[inline(always)] + fn start_query( + &self, + token: QueryJobId, + diagnostics: Option<&Lock>>, + compute: impl FnOnce(Self) -> R, + ) -> R { + // The `TyCtxt` stored in TLS has the same global interner lifetime + // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes + // when accessing the `ImplicitCtxt`. + tls::with_related_context(*self, move |current_icx| { + // Update the `ImplicitCtxt` to point to our new query job. + let new_icx = ImplicitCtxt { + tcx: *self, + query: Some(token), + diagnostics, + layout_depth: current_icx.layout_depth, + task_deps: current_icx.task_deps, + }; + + // Use the `ImplicitCtxt` while we execute the query. + tls::enter_context(&new_icx, |_| { + rustc_data_structures::stack::ensure_sufficient_stack(|| compute(*self)) + }) + }) + } +} + +impl<'tcx> TyCtxt<'tcx> { + #[inline(never)] + #[cold] + pub(super) fn report_cycle( + self, + CycleError { usage, cycle: stack }: CycleError>, + ) -> DiagnosticBuilder<'tcx> { + assert!(!stack.is_empty()); + + let fix_span = |span: Span, query: &Query<'tcx>| { + self.sess.source_map().guess_head_span(query.default_span(self, span)) + }; + + // Disable naming impls with types in this path, since that + // sometimes cycles itself, leading to extra cycle errors. + // (And cycle errors around impls tend to occur during the + // collect/coherence phases anyhow.) + ty::print::with_forced_impl_filename_line(|| { + let span = fix_span(stack[1 % stack.len()].span, &stack[0].query); + let mut err = struct_span_err!( + self.sess, + span, + E0391, + "cycle detected when {}", + stack[0].query.describe(self) + ); + + for i in 1..stack.len() { + let query = &stack[i].query; + let span = fix_span(stack[(i + 1) % stack.len()].span, query); + err.span_note(span, &format!("...which requires {}...", query.describe(self))); + } + + err.note(&format!( + "...which again requires {}, completing the cycle", + stack[0].query.describe(self) + )); + + if let Some((span, query)) = usage { + err.span_note( + fix_span(span, &query), + &format!("cycle used when {}", query.describe(self)), + ); + } + + err + }) + } + + pub fn try_print_query_stack(handler: &Handler) { + eprintln!("query stack during panic:"); + + // Be careful reyling on global state here: this code is called from + // a panic hook, which means that the global `Handler` may be in a weird + // state if it was responsible for triggering the panic. + ty::tls::with_context_opt(|icx| { + if let Some(icx) = icx { + let query_map = icx.tcx.queries.try_collect_active_jobs(); + + let mut current_query = icx.query; + let mut i = 0; + + while let Some(query) = current_query { + let query_info = + if let Some(info) = query_map.as_ref().and_then(|map| map.get(&query)) { + info + } else { + break; + }; + let mut diag = Diagnostic::new( + Level::FailureNote, + &format!( + "#{} [{}] {}", + i, + query_info.info.query.name(), + query_info.info.query.describe(icx.tcx) + ), + ); + diag.span = + icx.tcx.sess.source_map().guess_head_span(query_info.info.span).into(); + handler.force_print_diagnostic(diag); + + current_query = query_info.job.parent; + i += 1; + } + } + }); + + eprintln!("end of query stack"); + } +} + +macro_rules! handle_cycle_error { + ([][$tcx: expr, $error:expr]) => {{ + $tcx.report_cycle($error).emit(); + Value::from_cycle_error($tcx) + }}; + ([fatal_cycle $($rest:tt)*][$tcx:expr, $error:expr]) => {{ + $tcx.report_cycle($error).emit(); + $tcx.sess.abort_if_errors(); + unreachable!() + }}; + ([cycle_delay_bug $($rest:tt)*][$tcx:expr, $error:expr]) => {{ + $tcx.report_cycle($error).delay_as_bug(); + Value::from_cycle_error($tcx) + }}; + ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => { + handle_cycle_error!([$($($modifiers)*)*][$($args)*]) + }; +} + +macro_rules! is_anon { + ([]) => {{ + false + }}; + ([anon $($rest:tt)*]) => {{ + true + }}; + ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*]) => { + is_anon!([$($($modifiers)*)*]) + }; +} + +macro_rules! is_eval_always { + ([]) => {{ + false + }}; + ([eval_always $($rest:tt)*]) => {{ + true + }}; + ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*]) => { + is_eval_always!([$($($modifiers)*)*]) + }; +} + +macro_rules! query_storage { + ([][$K:ty, $V:ty]) => { + <<$K as Key>::CacheSelector as CacheSelector<$K, $V>>::Cache + }; + ([storage($ty:ty) $($rest:tt)*][$K:ty, $V:ty]) => { + <$ty as CacheSelector<$K, $V>>::Cache + }; + ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => { + query_storage!([$($($modifiers)*)*][$($args)*]) + }; +} + +macro_rules! hash_result { + ([][$hcx:expr, $result:expr]) => {{ + dep_graph::hash_result($hcx, &$result) + }}; + ([no_hash $($rest:tt)*][$hcx:expr, $result:expr]) => {{ + None + }}; + ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => { + hash_result!([$($($modifiers)*)*][$($args)*]) + }; +} + +macro_rules! define_queries { + (<$tcx:tt> $($category:tt { + $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)* + },)*) => { + define_queries_inner! { <$tcx> + $($( $(#[$attr])* category<$category> [$($modifiers)*] fn $name: $node($($K)*) -> $V,)*)* + } + } +} + +macro_rules! query_helper_param_ty { + (DefId) => { impl IntoQueryParam }; + ($K:ty) => { $K }; +} + +macro_rules! define_queries_inner { + (<$tcx:tt> + $($(#[$attr:meta])* category<$category:tt> + [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*) => { + + use std::mem; + use crate::{ + rustc_data_structures::stable_hasher::HashStable, + rustc_data_structures::stable_hasher::StableHasher, + ich::StableHashingContext + }; + use rustc_data_structures::profiling::ProfileCategory; + + define_queries_struct! { + tcx: $tcx, + input: ($(([$($modifiers)*] [$($attr)*] [$name]))*) + } + + #[allow(nonstandard_style)] + #[derive(Clone, Debug)] + pub enum Query<$tcx> { + $($(#[$attr])* $name($($K)*)),* + } + + impl<$tcx> Query<$tcx> { + pub fn name(&self) -> &'static str { + match *self { + $(Query::$name(_) => stringify!($name),)* + } + } + + pub fn describe(&self, tcx: TyCtxt<$tcx>) -> Cow<'static, str> { + let (r, name) = match *self { + $(Query::$name(key) => { + (queries::$name::describe(tcx, key), stringify!($name)) + })* + }; + if tcx.sess.verbose() { + format!("{} [{}]", r, name).into() + } else { + r + } + } + + // FIXME(eddyb) Get more valid `Span`s on queries. + pub fn default_span(&self, tcx: TyCtxt<$tcx>, span: Span) -> Span { + if !span.is_dummy() { + return span; + } + // The `def_span` query is used to calculate `default_span`, + // so exit to avoid infinite recursion. + if let Query::def_span(..) = *self { + return span + } + match *self { + $(Query::$name(key) => key.default_span(tcx),)* + } + } + } + + impl<'a, $tcx> HashStable> for Query<$tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + match *self { + $(Query::$name(key) => key.hash_stable(hcx, hasher),)* + } + } + } + + pub mod queries { + use std::marker::PhantomData; + + $(#[allow(nonstandard_style)] + pub struct $name<$tcx> { + data: PhantomData<&$tcx ()> + })* + } + + $(impl<$tcx> QueryConfig> for queries::$name<$tcx> { + type Key = $($K)*; + type Value = $V; + type Stored = < + query_storage!([$($modifiers)*][$($K)*, $V]) + as QueryStorage + >::Stored; + const NAME: &'static str = stringify!($name); + const CATEGORY: ProfileCategory = $category; + } + + impl<$tcx> QueryAccessors> for queries::$name<$tcx> { + const ANON: bool = is_anon!([$($modifiers)*]); + const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]); + const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$node; + + type Cache = query_storage!([$($modifiers)*][$($K)*, $V]); + + #[inline(always)] + fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState, Self::Cache> { + &tcx.queries.$name + } + + #[inline] + fn compute(tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value { + let provider = tcx.queries.providers.get(key.query_crate()) + // HACK(eddyb) it's possible crates may be loaded after + // the query engine is created, and because crate loading + // is not yet integrated with the query engine, such crates + // would be missing appropriate entries in `providers`. + .unwrap_or(&tcx.queries.fallback_extern_providers) + .$name; + provider(tcx, key) + } + + fn hash_result( + _hcx: &mut StableHashingContext<'_>, + _result: &Self::Value + ) -> Option { + hash_result!([$($modifiers)*][_hcx, _result]) + } + + fn handle_cycle_error( + tcx: TyCtxt<'tcx>, + error: CycleError> + ) -> Self::Value { + handle_cycle_error!([$($modifiers)*][tcx, error]) + } + })* + + #[derive(Copy, Clone)] + pub struct TyCtxtEnsure<'tcx> { + pub tcx: TyCtxt<'tcx>, + } + + impl TyCtxtEnsure<$tcx> { + $($(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) { + ensure_query::, _>(self.tcx, key.into_query_param()) + })* + } + + #[derive(Copy, Clone)] + pub struct TyCtxtAt<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub span: Span, + } + + impl Deref for TyCtxtAt<'tcx> { + type Target = TyCtxt<'tcx>; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.tcx + } + } + + impl TyCtxt<$tcx> { + /// Returns a transparent wrapper for `TyCtxt`, which ensures queries + /// are executed instead of just returning their results. + #[inline(always)] + pub fn ensure(self) -> TyCtxtEnsure<$tcx> { + TyCtxtEnsure { + tcx: self, + } + } + + /// Returns a transparent wrapper for `TyCtxt` which uses + /// `span` as the location of queries performed through it. + #[inline(always)] + pub fn at(self, span: Span) -> TyCtxtAt<$tcx> { + TyCtxtAt { + tcx: self, + span + } + } + + $($(#[$attr])* + #[inline(always)] + #[must_use] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) + -> as QueryConfig>>::Stored + { + self.at(DUMMY_SP).$name(key.into_query_param()) + })* + + /// All self-profiling events generated by the query engine use + /// virtual `StringId`s for their `event_id`. This method makes all + /// those virtual `StringId`s point to actual strings. + /// + /// If we are recording only summary data, the ids will point to + /// just the query names. If we are recording query keys too, we + /// allocate the corresponding strings here. + pub fn alloc_self_profile_query_strings(self) { + use crate::ty::query::profiling_support::{ + alloc_self_profile_query_strings_for_query_cache, + QueryKeyStringCache, + }; + + if !self.prof.enabled() { + return; + } + + let mut string_cache = QueryKeyStringCache::new(); + + $({ + alloc_self_profile_query_strings_for_query_cache( + self, + stringify!($name), + &self.queries.$name, + &mut string_cache, + ); + })* + } + } + + impl TyCtxtAt<$tcx> { + $($(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) + -> as QueryConfig>>::Stored + { + get_query::, _>(self.tcx, self.span, key.into_query_param()) + })* + } + + define_provider_struct! { + tcx: $tcx, + input: ($(([$($modifiers)*] [$name] [$($K)*] [$V]))*) + } + + impl<$tcx> Copy for Providers<$tcx> {} + impl<$tcx> Clone for Providers<$tcx> { + fn clone(&self) -> Self { *self } + } + } +} + +macro_rules! define_queries_struct { + (tcx: $tcx:tt, + input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { + pub struct Queries<$tcx> { + /// This provides access to the incrimental comilation on-disk cache for query results. + /// Do not access this directly. It is only meant to be used by + /// `DepGraph::try_mark_green()` and the query infrastructure. + pub(crate) on_disk_cache: OnDiskCache<'tcx>, + + providers: IndexVec>, + fallback_extern_providers: Box>, + + $($(#[$attr])* $name: QueryState< + TyCtxt<$tcx>, + as QueryAccessors>>::Cache, + >,)* + } + + impl<$tcx> Queries<$tcx> { + pub(crate) fn new( + providers: IndexVec>, + fallback_extern_providers: Providers<$tcx>, + on_disk_cache: OnDiskCache<'tcx>, + ) -> Self { + Queries { + providers, + fallback_extern_providers: Box::new(fallback_extern_providers), + on_disk_cache, + $($name: Default::default()),* + } + } + + pub(crate) fn try_collect_active_jobs( + &self + ) -> Option, QueryJobInfo>>> { + let mut jobs = FxHashMap::default(); + + $( + self.$name.try_collect_active_jobs( + as QueryAccessors>>::DEP_KIND, + Query::$name, + &mut jobs, + )?; + )* + + Some(jobs) + } + } + }; +} + +macro_rules! define_provider_struct { + (tcx: $tcx:tt, + input: ($(([$($modifiers:tt)*] [$name:ident] [$K:ty] [$R:ty]))*)) => { + pub struct Providers<$tcx> { + $(pub $name: fn(TyCtxt<$tcx>, $K) -> $R,)* + } + + impl<$tcx> Default for Providers<$tcx> { + fn default() -> Self { + $(fn $name<$tcx>(_: TyCtxt<$tcx>, key: $K) -> $R { + bug!("`tcx.{}({:?})` unsupported by its crate", + stringify!($name), key); + })* + Providers { $($name),* } + } + } + }; +} diff --git a/src/librustc/ty/query/profiling_support.rs b/src/librustc_middle/ty/query/profiling_support.rs similarity index 97% rename from src/librustc/ty/query/profiling_support.rs rename to src/librustc_middle/ty/query/profiling_support.rs index 58ace917786cf..e0d3e764dad83 100644 --- a/src/librustc/ty/query/profiling_support.rs +++ b/src/librustc_middle/ty/query/profiling_support.rs @@ -1,11 +1,11 @@ -use crate::hir::map::definitions::DefPathData; use crate::ty::context::TyCtxt; -use crate::ty::query::caches::QueryCache; -use crate::ty::query::plumbing::QueryState; use measureme::{StringComponent, StringId}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfiler; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathData; +use rustc_query_system::query::QueryCache; +use rustc_query_system::query::QueryState; use std::fmt::Debug; use std::io::Write; @@ -160,7 +160,7 @@ where pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( tcx: TyCtxt<'tcx>, query_name: &'static str, - query_state: &QueryState<'tcx, C>, + query_state: &QueryState, C>, string_cache: &mut QueryKeyStringCache, ) where C: QueryCache, diff --git a/src/librustc/ty/query/stats.rs b/src/librustc_middle/ty/query/stats.rs similarity index 91% rename from src/librustc/ty/query/stats.rs rename to src/librustc_middle/ty/query/stats.rs index 527bb46c90888..b496bf839ab9e 100644 --- a/src/librustc/ty/query/stats.rs +++ b/src/librustc_middle/ty/query/stats.rs @@ -1,9 +1,9 @@ -use crate::ty::query::caches::QueryCache; -use crate::ty::query::config::QueryAccessors; -use crate::ty::query::plumbing::QueryState; use crate::ty::query::queries; use crate::ty::TyCtxt; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_query_system::query::QueryCache; +use rustc_query_system::query::QueryState; +use rustc_query_system::query::{QueryAccessors, QueryContext}; use std::any::type_name; use std::mem; @@ -38,7 +38,10 @@ struct QueryStats { local_def_id_keys: Option, } -fn stats<'tcx, C: QueryCache>(name: &'static str, map: &QueryState<'tcx, C>) -> QueryStats { +fn stats( + name: &'static str, + map: &QueryState, +) -> QueryStats { let mut stats = QueryStats { name, #[cfg(debug_assertions)] @@ -124,7 +127,8 @@ macro_rules! print_stats { $($( queries.push(stats::< - as QueryAccessors<'_>>::Cache, + TyCtxt<'_>, + as QueryAccessors>>::Cache, >( stringify!($name), &tcx.queries.$name, diff --git a/src/librustc/ty/query/values.rs b/src/librustc_middle/ty/query/values.rs similarity index 100% rename from src/librustc/ty/query/values.rs rename to src/librustc_middle/ty/query/values.rs diff --git a/src/librustc/ty/relate.rs b/src/librustc_middle/ty/relate.rs similarity index 75% rename from src/librustc/ty/relate.rs rename to src/librustc_middle/ty/relate.rs index 872e06e1176dc..594ffbcd83613 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc_middle/ty/relate.rs @@ -5,12 +5,12 @@ //! subtyping, type equality, etc. use crate::mir::interpret::{get_slice_bytes, ConstValue}; -use crate::traits; use crate::ty::error::{ExpectedFound, TypeError}; use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_hir as ast; use rustc_hir::def_id::DefId; +use rustc_span::DUMMY_SP; use rustc_target::spec::abi; use std::iter; use std::rc::Rc; @@ -431,6 +431,9 @@ pub fn super_relate_tys>( let t = relation.relate(&a_t, &b_t)?; match relation.relate(&sz_a, &sz_b) { Ok(sz) => Ok(tcx.mk_ty(ty::Array(t, sz))), + // FIXME(#72219) Implement improved diagnostics for mismatched array + // length? + Err(err) if relation.tcx().lazy_normalization() => Err(err), Err(err) => { // Check whether the lengths are both concrete/known values, // but are unequal, for better diagnostics. @@ -477,11 +480,6 @@ pub fn super_relate_tys>( Ok(tcx.mk_fn_ptr(fty)) } - (ty::UnnormalizedProjection(a_data), ty::UnnormalizedProjection(b_data)) => { - let projection_ty = relation.relate(a_data, b_data)?; - Ok(tcx.mk_ty(ty::UnnormalizedProjection(projection_ty))) - } - // these two are already handled downstream in case of lazy normalization (ty::Projection(a_data), ty::Projection(b_data)) => { let projection_ty = relation.relate(a_data, b_data)?; @@ -507,23 +505,36 @@ pub fn super_relate_consts>( a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>, ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); let tcx = relation.tcx(); let eagerly_eval = |x: &'tcx ty::Const<'tcx>| { - if !x.val.has_local_value() { + // FIXME(eddyb) this doesn't account for lifetime inference variables + // being erased by `eval`, *nor* for the polymorphic aspect of `eval`. + // That is, we could always use `eval` and it will just return the + // old value back if it doesn't succeed. + if !x.val.needs_infer() { return x.eval(tcx, relation.param_env()).val; } x.val }; + // FIXME(eddyb) doesn't look like everything below checks that `a.ty == b.ty`. + // We could probably always assert it early, as `const` generic parameters + // are not allowed to depend on other generic parameters, i.e. are concrete. + // (although there could be normalization differences) + // Currently, the values that can be unified are primitive types, // and those that derive both `PartialEq` and `Eq`, corresponding - // to `structural_match` types. + // to structural-match types. let new_const_val = match (eagerly_eval(a), eagerly_eval(b)) { (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { // The caller should handle these cases! bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b) } + + (ty::ConstKind::Error, _) | (_, ty::ConstKind::Error) => Ok(ty::ConstKind::Error), + (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { return Ok(a); } @@ -536,9 +547,8 @@ pub fn super_relate_consts>( if a_val == b_val { Ok(ConstValue::Scalar(a_val)) } else if let ty::FnPtr(_) = a.ty.kind { - let alloc_map = tcx.alloc_map.lock(); - let a_instance = alloc_map.unwrap_fn(a_val.assert_ptr().alloc_id); - let b_instance = alloc_map.unwrap_fn(b_val.assert_ptr().alloc_id); + let a_instance = tcx.global_alloc(a_val.assert_ptr().alloc_id).unwrap_fn(); + let b_instance = tcx.global_alloc(b_val.assert_ptr().alloc_id).unwrap_fn(); if a_instance == b_instance { Ok(ConstValue::Scalar(a_val)) } else { @@ -549,7 +559,7 @@ pub fn super_relate_consts>( } } - (a_val @ ConstValue::Slice { .. }, b_val @ ConstValue::Slice { .. }) => { + (ConstValue::Slice { .. }, ConstValue::Slice { .. }) => { let a_bytes = get_slice_bytes(&tcx, a_val); let b_bytes = get_slice_bytes(&tcx, b_val); if a_bytes == b_bytes { @@ -559,7 +569,37 @@ pub fn super_relate_consts>( } } - // FIXME(const_generics): handle `ConstValue::ByRef`. + (ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => { + match a.ty.kind { + ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { + let a_destructured = tcx.destructure_const(relation.param_env().and(a)); + let b_destructured = tcx.destructure_const(relation.param_env().and(b)); + + // Both the variant and each field have to be equal. + if a_destructured.variant == b_destructured.variant { + for (a_field, b_field) in + a_destructured.fields.iter().zip(b_destructured.fields.iter()) + { + relation.consts(a_field, b_field)?; + } + + Ok(a_val) + } else { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } + // FIXME(const_generics): There are probably some `TyKind`s + // which should be handled here. + _ => { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("unexpected consts: a: {:?}, b: {:?}", a, b), + ); + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } + } + _ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))), }; @@ -742,229 +782,6 @@ impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> { } } -impl<'tcx> Relate<'tcx> for traits::WhereClause<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::WhereClause<'tcx>, - b: &traits::WhereClause<'tcx>, - ) -> RelateResult<'tcx, traits::WhereClause<'tcx>> { - use crate::traits::WhereClause::*; - match (a, b) { - (Implemented(a_pred), Implemented(b_pred)) => { - Ok(Implemented(relation.relate(a_pred, b_pred)?)) - } - - (ProjectionEq(a_pred), ProjectionEq(b_pred)) => { - Ok(ProjectionEq(relation.relate(a_pred, b_pred)?)) - } - - (RegionOutlives(a_pred), RegionOutlives(b_pred)) => { - Ok(RegionOutlives(ty::OutlivesPredicate( - relation.relate(&a_pred.0, &b_pred.0)?, - relation.relate(&a_pred.1, &b_pred.1)?, - ))) - } - - (TypeOutlives(a_pred), TypeOutlives(b_pred)) => { - Ok(TypeOutlives(ty::OutlivesPredicate( - relation.relate(&a_pred.0, &b_pred.0)?, - relation.relate(&a_pred.1, &b_pred.1)?, - ))) - } - - _ => Err(TypeError::Mismatch), - } - } -} - -impl<'tcx> Relate<'tcx> for traits::WellFormed<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::WellFormed<'tcx>, - b: &traits::WellFormed<'tcx>, - ) -> RelateResult<'tcx, traits::WellFormed<'tcx>> { - use crate::traits::WellFormed::*; - match (a, b) { - (Trait(a_pred), Trait(b_pred)) => Ok(Trait(relation.relate(a_pred, b_pred)?)), - (Ty(a_ty), Ty(b_ty)) => Ok(Ty(relation.relate(a_ty, b_ty)?)), - _ => Err(TypeError::Mismatch), - } - } -} - -impl<'tcx> Relate<'tcx> for traits::FromEnv<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::FromEnv<'tcx>, - b: &traits::FromEnv<'tcx>, - ) -> RelateResult<'tcx, traits::FromEnv<'tcx>> { - use crate::traits::FromEnv::*; - match (a, b) { - (Trait(a_pred), Trait(b_pred)) => Ok(Trait(relation.relate(a_pred, b_pred)?)), - (Ty(a_ty), Ty(b_ty)) => Ok(Ty(relation.relate(a_ty, b_ty)?)), - _ => Err(TypeError::Mismatch), - } - } -} - -impl<'tcx> Relate<'tcx> for traits::DomainGoal<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::DomainGoal<'tcx>, - b: &traits::DomainGoal<'tcx>, - ) -> RelateResult<'tcx, traits::DomainGoal<'tcx>> { - use crate::traits::DomainGoal::*; - match (a, b) { - (Holds(a_wc), Holds(b_wc)) => Ok(Holds(relation.relate(a_wc, b_wc)?)), - (WellFormed(a_wf), WellFormed(b_wf)) => Ok(WellFormed(relation.relate(a_wf, b_wf)?)), - (FromEnv(a_fe), FromEnv(b_fe)) => Ok(FromEnv(relation.relate(a_fe, b_fe)?)), - - (Normalize(a_pred), Normalize(b_pred)) => { - Ok(Normalize(relation.relate(a_pred, b_pred)?)) - } - - _ => Err(TypeError::Mismatch), - } - } -} - -impl<'tcx> Relate<'tcx> for traits::Goal<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::Goal<'tcx>, - b: &traits::Goal<'tcx>, - ) -> RelateResult<'tcx, traits::Goal<'tcx>> { - use crate::traits::GoalKind::*; - match (a, b) { - (Implies(a_clauses, a_goal), Implies(b_clauses, b_goal)) => { - let clauses = relation.relate(a_clauses, b_clauses)?; - let goal = relation.relate(a_goal, b_goal)?; - Ok(relation.tcx().mk_goal(Implies(clauses, goal))) - } - - (And(a_left, a_right), And(b_left, b_right)) => { - let left = relation.relate(a_left, b_left)?; - let right = relation.relate(a_right, b_right)?; - Ok(relation.tcx().mk_goal(And(left, right))) - } - - (Not(a_goal), Not(b_goal)) => { - let goal = relation.relate(a_goal, b_goal)?; - Ok(relation.tcx().mk_goal(Not(goal))) - } - - (DomainGoal(a_goal), DomainGoal(b_goal)) => { - let goal = relation.relate(a_goal, b_goal)?; - Ok(relation.tcx().mk_goal(DomainGoal(goal))) - } - - (Quantified(a_qkind, a_goal), Quantified(b_qkind, b_goal)) if a_qkind == b_qkind => { - let goal = relation.relate(a_goal, b_goal)?; - Ok(relation.tcx().mk_goal(Quantified(*a_qkind, goal))) - } - - (CannotProve, CannotProve) => Ok(*a), - - _ => Err(TypeError::Mismatch), - } - } -} - -impl<'tcx> Relate<'tcx> for traits::Goals<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::Goals<'tcx>, - b: &traits::Goals<'tcx>, - ) -> RelateResult<'tcx, traits::Goals<'tcx>> { - if a.len() != b.len() { - return Err(TypeError::Mismatch); - } - - let tcx = relation.tcx(); - let goals = a.iter().zip(b.iter()).map(|(a, b)| relation.relate(a, b)); - Ok(tcx.mk_goals(goals)?) - } -} - -impl<'tcx> Relate<'tcx> for traits::Clause<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::Clause<'tcx>, - b: &traits::Clause<'tcx>, - ) -> RelateResult<'tcx, traits::Clause<'tcx>> { - use crate::traits::Clause::*; - match (a, b) { - (Implies(a_clause), Implies(b_clause)) => { - let clause = relation.relate(a_clause, b_clause)?; - Ok(Implies(clause)) - } - - (ForAll(a_clause), ForAll(b_clause)) => { - let clause = relation.relate(a_clause, b_clause)?; - Ok(ForAll(clause)) - } - - _ => Err(TypeError::Mismatch), - } - } -} - -impl<'tcx> Relate<'tcx> for traits::Clauses<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::Clauses<'tcx>, - b: &traits::Clauses<'tcx>, - ) -> RelateResult<'tcx, traits::Clauses<'tcx>> { - if a.len() != b.len() { - return Err(TypeError::Mismatch); - } - - let tcx = relation.tcx(); - let clauses = a.iter().zip(b.iter()).map(|(a, b)| relation.relate(a, b)); - Ok(tcx.mk_clauses(clauses)?) - } -} - -impl<'tcx> Relate<'tcx> for traits::ProgramClause<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::ProgramClause<'tcx>, - b: &traits::ProgramClause<'tcx>, - ) -> RelateResult<'tcx, traits::ProgramClause<'tcx>> { - Ok(traits::ProgramClause { - goal: relation.relate(&a.goal, &b.goal)?, - hypotheses: relation.relate(&a.hypotheses, &b.hypotheses)?, - category: traits::ProgramClauseCategory::Other, - }) - } -} - -impl<'tcx> Relate<'tcx> for traits::Environment<'tcx> { - fn relate>( - relation: &mut R, - a: &traits::Environment<'tcx>, - b: &traits::Environment<'tcx>, - ) -> RelateResult<'tcx, traits::Environment<'tcx>> { - Ok(traits::Environment { clauses: relation.relate(&a.clauses, &b.clauses)? }) - } -} - -impl<'tcx, G> Relate<'tcx> for traits::InEnvironment<'tcx, G> -where - G: Relate<'tcx>, -{ - fn relate>( - relation: &mut R, - a: &traits::InEnvironment<'tcx, G>, - b: &traits::InEnvironment<'tcx, G>, - ) -> RelateResult<'tcx, traits::InEnvironment<'tcx, G>> { - Ok(traits::InEnvironment { - environment: relation.relate(&a.environment, &b.environment)?, - goal: relation.relate(&a.goal, &b.goal)?, - }) - } -} - /////////////////////////////////////////////////////////////////////////// // Error handling diff --git a/src/librustc/ty/steal.rs b/src/librustc_middle/ty/steal.rs similarity index 100% rename from src/librustc/ty/steal.rs rename to src/librustc_middle/ty/steal.rs diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs new file mode 100644 index 0000000000000..c23a351ac515c --- /dev/null +++ b/src/librustc_middle/ty/structural_impls.rs @@ -0,0 +1,1087 @@ +//! This module contains implements of the `Lift` and `TypeFoldable` +//! traits for various types in the Rust compiler. Most are written by +//! hand, though we've recently added some macros and proc-macros to help with the tedium. + +use crate::mir::interpret; +use crate::mir::ProjectionKind; +use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::{self, InferConst, Lift, Ty, TyCtxt}; +use rustc_hir as hir; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::CRATE_DEF_INDEX; +use rustc_index::vec::{Idx, IndexVec}; + +use smallvec::SmallVec; +use std::fmt; +use std::rc::Rc; +use std::sync::Arc; + +impl fmt::Debug for ty::TraitDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])?; + Ok(()) + }) + } +} + +impl fmt::Debug for ty::AdtDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])?; + Ok(()) + }) + } +} + +impl fmt::Debug for ty::UpvarId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = ty::tls::with(|tcx| tcx.hir().name(self.var_path.hir_id)); + write!(f, "UpvarId({:?};`{}`;{:?})", self.var_path.hir_id, name, self.closure_expr_id) + } +} + +impl fmt::Debug for ty::UpvarBorrow<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region) + } +} + +impl fmt::Debug for ty::ExistentialTraitRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for ty::adjustment::Adjustment<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?} -> {}", self.kind, self.target) + } +} + +impl fmt::Debug for ty::BoundRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::BrAnon(n) => write!(f, "BrAnon({:?})", n), + ty::BrNamed(did, name) => { + if did.index == CRATE_DEF_INDEX { + write!(f, "BrNamed({})", name) + } else { + write!(f, "BrNamed({:?}, {})", did, name) + } + } + ty::BrEnv => write!(f, "BrEnv"), + } + } +} + +impl fmt::Debug for ty::RegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::ReEarlyBound(ref data) => write!(f, "ReEarlyBound({}, {})", data.index, data.name), + + ty::ReLateBound(binder_id, ref bound_region) => { + write!(f, "ReLateBound({:?}, {:?})", binder_id, bound_region) + } + + ty::ReFree(ref fr) => fr.fmt(f), + + ty::ReScope(id) => write!(f, "ReScope({:?})", id), + + ty::ReStatic => write!(f, "ReStatic"), + + ty::ReVar(ref vid) => vid.fmt(f), + + ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder), + + ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui), + + ty::ReErased => write!(f, "ReErased"), + } + } +} + +impl fmt::Debug for ty::FreeRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ReFree({:?}, {:?})", self.scope, self.bound_region) + } +} + +impl fmt::Debug for ty::Variance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + ty::Covariant => "+", + ty::Contravariant => "-", + ty::Invariant => "o", + ty::Bivariant => "*", + }) + } +} + +impl fmt::Debug for ty::FnSig<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output()) + } +} + +impl fmt::Debug for ty::TyVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}t", self.index) + } +} + +impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}c", self.index) + } +} + +impl fmt::Debug for ty::IntVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}i", self.index) + } +} + +impl fmt::Debug for ty::FloatVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}f", self.index) + } +} + +impl fmt::Debug for ty::RegionVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "'_#{}r", self.index()) + } +} + +impl fmt::Debug for ty::InferTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::TyVar(ref v) => v.fmt(f), + ty::IntVar(ref v) => v.fmt(f), + ty::FloatVar(ref v) => v.fmt(f), + ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), + ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), + ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), + } + } +} + +impl fmt::Debug for ty::IntVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::IntType(ref v) => v.fmt(f), + ty::UintType(ref v) => v.fmt(f), + } + } +} + +impl fmt::Debug for ty::FloatVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Debug for ty::TraitRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for Ty<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for ty::ParamTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/#{}", self.name, self.index) + } +} + +impl fmt::Debug for ty::ParamConst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/#{}", self.name, self.index) + } +} + +impl fmt::Debug for ty::TraitPredicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraitPredicate({:?})", self.trait_ref) + } +} + +impl fmt::Debug for ty::ProjectionPredicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.ty) + } +} + +impl fmt::Debug for ty::Predicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::Predicate::Trait(ref a, constness) => { + if let hir::Constness::Const = constness { + write!(f, "const ")?; + } + a.fmt(f) + } + ty::Predicate::Subtype(ref pair) => pair.fmt(f), + ty::Predicate::RegionOutlives(ref pair) => pair.fmt(f), + ty::Predicate::TypeOutlives(ref pair) => pair.fmt(f), + ty::Predicate::Projection(ref pair) => pair.fmt(f), + ty::Predicate::WellFormed(ty) => write!(f, "WellFormed({:?})", ty), + ty::Predicate::ObjectSafe(trait_def_id) => write!(f, "ObjectSafe({:?})", trait_def_id), + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) + } + ty::Predicate::ConstEvaluatable(def_id, substs) => { + write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) + } + ty::Predicate::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Atomic structs +// +// For things that don't carry any arena-allocated data (and are +// copy...), just add them to this list. + +CloneTypeFoldableAndLiftImpls! { + (), + bool, + usize, + ::rustc_target::abi::VariantIdx, + u64, + String, + crate::middle::region::Scope, + ::rustc_ast::ast::FloatTy, + ::rustc_ast::ast::InlineAsmOptions, + ::rustc_ast::ast::InlineAsmTemplatePiece, + ::rustc_ast::ast::NodeId, + ::rustc_span::symbol::Symbol, + ::rustc_hir::def::Res, + ::rustc_hir::def_id::DefId, + ::rustc_hir::LlvmInlineAsmInner, + ::rustc_hir::MatchSource, + ::rustc_hir::Mutability, + ::rustc_hir::Unsafety, + ::rustc_target::asm::InlineAsmRegOrRegClass, + ::rustc_target::spec::abi::Abi, + crate::mir::Local, + crate::mir::Promoted, + crate::traits::Reveal, + crate::ty::adjustment::AutoBorrowMutability, + crate::ty::AdtKind, + // Including `BoundRegion` is a *bit* dubious, but direct + // references to bound region appear in `ty::Error`, and aren't + // really meant to be folded. In general, we can only fold a fully + // general `Region`. + crate::ty::BoundRegion, + crate::ty::Placeholder, + crate::ty::ClosureKind, + crate::ty::FreeRegion, + crate::ty::InferTy, + crate::ty::IntVarValue, + crate::ty::ParamConst, + crate::ty::ParamTy, + crate::ty::adjustment::PointerCast, + crate::ty::RegionVid, + crate::ty::UniverseIndex, + crate::ty::Variance, + ::rustc_span::Span, +} + +/////////////////////////////////////////////////////////////////////////// +// Lift implementations + +// FIXME(eddyb) replace all the uses of `Option::map` with `?`. +impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) { + type Lifted = (A::Lifted, B::Lifted); + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.0).and_then(|a| tcx.lift(&self.1).map(|b| (a, b))) + } +} + +impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) { + type Lifted = (A::Lifted, B::Lifted, C::Lifted); + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.0) + .and_then(|a| tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { + type Lifted = Option; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + Some(ref x) => tcx.lift(x).map(Some), + None => Some(None), + } + } +} + +impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result { + type Lifted = Result; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + Ok(ref x) => tcx.lift(x).map(Ok), + Err(ref e) => tcx.lift(e).map(Err), + } + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { + type Lifted = Box; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Box::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { + type Lifted = Rc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Rc::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { + type Lifted = Arc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Arc::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { + type Lifted = Vec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + // type annotation needed to inform `projection_must_outlive` + let mut result: Vec<>::Lifted> = Vec::with_capacity(self.len()); + for x in self { + if let Some(value) = tcx.lift(x) { + result.push(value); + } else { + return None; + } + } + Some(result) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { + type Lifted = Vec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self[..]) + } +} + +impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { + type Lifted = IndexVec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + self.iter().map(|e| tcx.lift(e)).collect() + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> { + type Lifted = ty::TraitRef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::TraitRef { def_id: self.def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> { + type Lifted = ty::ExistentialTraitRef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ExistentialTraitRef { def_id: self.def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { + type Lifted = ty::ExistentialPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match self { + ty::ExistentialPredicate::Trait(x) => tcx.lift(x).map(ty::ExistentialPredicate::Trait), + ty::ExistentialPredicate::Projection(x) => { + tcx.lift(x).map(ty::ExistentialPredicate::Projection) + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + Some(ty::ExistentialPredicate::AutoTrait(*def_id)) + } + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { + type Lifted = ty::TraitPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitPredicate { trait_ref }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { + type Lifted = ty::SubtypePredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&(self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a, + b, + }) + } +} + +impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate { + type Lifted = ty::OutlivesPredicate; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&(self.0, self.1)).map(|(a, b)| ty::OutlivesPredicate(a, b)) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionTy<'a> { + type Lifted = ty::ProjectionTy<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&self.substs) + .map(|substs| ty::ProjectionTy { item_def_id: self.item_def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionPredicate<'a> { + type Lifted = ty::ProjectionPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&(self.projection_ty, self.ty)) + .map(|(projection_ty, ty)| ty::ProjectionPredicate { projection_ty, ty }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { + type Lifted = ty::ExistentialProjection<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ExistentialProjection { + substs, + ty: tcx.lift(&self.ty).expect("type must lift when substs do"), + item_def_id: self.item_def_id, + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> { + type Lifted = ty::Predicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::Predicate::Trait(ref binder, constness) => { + tcx.lift(binder).map(|binder| ty::Predicate::Trait(binder, constness)) + } + ty::Predicate::Subtype(ref binder) => tcx.lift(binder).map(ty::Predicate::Subtype), + ty::Predicate::RegionOutlives(ref binder) => { + tcx.lift(binder).map(ty::Predicate::RegionOutlives) + } + ty::Predicate::TypeOutlives(ref binder) => { + tcx.lift(binder).map(ty::Predicate::TypeOutlives) + } + ty::Predicate::Projection(ref binder) => { + tcx.lift(binder).map(ty::Predicate::Projection) + } + ty::Predicate::WellFormed(ty) => tcx.lift(&ty).map(ty::Predicate::WellFormed), + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + tcx.lift(&closure_substs).map(|closure_substs| { + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) + }) + } + ty::Predicate::ObjectSafe(trait_def_id) => { + Some(ty::Predicate::ObjectSafe(trait_def_id)) + } + ty::Predicate::ConstEvaluatable(def_id, substs) => { + tcx.lift(&substs).map(|substs| ty::Predicate::ConstEvaluatable(def_id, substs)) + } + ty::Predicate::ConstEquate(c1, c2) => { + tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::Predicate::ConstEquate(c1, c2)) + } + } + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder { + type Lifted = ty::Binder; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.skip_binder()).map(ty::Binder::bind) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { + type Lifted = ty::ParamEnv<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.caller_bounds).map(|caller_bounds| ty::ParamEnv { + reveal: self.reveal, + caller_bounds, + def_id: self.def_id, + }) + } +} + +impl<'a, 'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::ParamEnvAnd<'a, T> { + type Lifted = ty::ParamEnvAnd<'tcx, T::Lifted>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.param_env).and_then(|param_env| { + tcx.lift(&self.value).map(|value| ty::ParamEnvAnd { param_env, value }) + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ClosureSubsts<'a> { + type Lifted = ty::ClosureSubsts<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ClosureSubsts { substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorSubsts<'a> { + type Lifted = ty::GeneratorSubsts<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::GeneratorSubsts { substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> { + type Lifted = ty::adjustment::Adjustment<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.kind).and_then(|kind| { + tcx.lift(&self.target).map(|target| ty::adjustment::Adjustment { kind, target }) + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { + type Lifted = ty::adjustment::Adjust<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::adjustment::Adjust::NeverToAny => Some(ty::adjustment::Adjust::NeverToAny), + ty::adjustment::Adjust::Pointer(ptr) => Some(ty::adjustment::Adjust::Pointer(ptr)), + ty::adjustment::Adjust::Deref(ref overloaded) => { + tcx.lift(overloaded).map(ty::adjustment::Adjust::Deref) + } + ty::adjustment::Adjust::Borrow(ref autoref) => { + tcx.lift(autoref).map(ty::adjustment::Adjust::Borrow) + } + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { + type Lifted = ty::adjustment::OverloadedDeref<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.region) + .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> { + type Lifted = ty::adjustment::AutoBorrow<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::adjustment::AutoBorrow::Ref(r, m) => { + tcx.lift(&r).map(|r| ty::adjustment::AutoBorrow::Ref(r, m)) + } + ty::adjustment::AutoBorrow::RawPtr(m) => Some(ty::adjustment::AutoBorrow::RawPtr(m)), + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> { + type Lifted = ty::GenSig<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&(self.resume_ty, self.yield_ty, self.return_ty)) + .map(|(resume_ty, yield_ty, return_ty)| ty::GenSig { resume_ty, yield_ty, return_ty }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> { + type Lifted = ty::FnSig<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.inputs_and_output).map(|x| ty::FnSig { + inputs_and_output: x, + c_variadic: self.c_variadic, + unsafety: self.unsafety, + abi: self.abi, + }) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::error::ExpectedFound { + type Lifted = ty::error::ExpectedFound; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.expected).and_then(|expected| { + tcx.lift(&self.found).map(|found| ty::error::ExpectedFound { expected, found }) + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { + type Lifted = ty::error::TypeError<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + use crate::ty::error::TypeError::*; + + Some(match *self { + Mismatch => Mismatch, + UnsafetyMismatch(x) => UnsafetyMismatch(x), + AbiMismatch(x) => AbiMismatch(x), + Mutability => Mutability, + TupleSize(x) => TupleSize(x), + FixedArraySize(x) => FixedArraySize(x), + ArgCount => ArgCount, + RegionsDoesNotOutlive(a, b) => { + return tcx.lift(&(a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); + } + RegionsInsufficientlyPolymorphic(a, b) => { + return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b)); + } + RegionsOverlyPolymorphic(a, b) => { + return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b)); + } + RegionsPlaceholderMismatch => RegionsPlaceholderMismatch, + IntMismatch(x) => IntMismatch(x), + FloatMismatch(x) => FloatMismatch(x), + Traits(x) => Traits(x), + VariadicMismatch(x) => VariadicMismatch(x), + CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)), + ProjectionMismatched(x) => ProjectionMismatched(x), + ProjectionBoundsLength(x) => ProjectionBoundsLength(x), + Sorts(ref x) => return tcx.lift(x).map(Sorts), + ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), + ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch), + IntrinsicCast => IntrinsicCast, + TargetFeatureCast(ref x) => TargetFeatureCast(*x), + ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion), + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { + type Lifted = ty::InstanceDef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), + ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), + ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), + ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), + ty::InstanceDef::FnPtrShim(def_id, ref ty) => { + Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) + } + ty::InstanceDef::Virtual(def_id, n) => Some(ty::InstanceDef::Virtual(def_id, n)), + ty::InstanceDef::ClosureOnceShim { call_once } => { + Some(ty::InstanceDef::ClosureOnceShim { call_once }) + } + ty::InstanceDef::DropGlue(def_id, ref ty) => { + Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)) + } + ty::InstanceDef::CloneShim(def_id, ref ty) => { + Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)) + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// TypeFoldable implementations. +// +// Ideally, each type should invoke `folder.fold_foo(self)` and +// nothing else. In some cases, though, we haven't gotten around to +// adding methods on the `folder` yet, and thus the folding is +// hard-coded here. This is less-flexible, because folders cannot +// override the behavior, but there are a lot of random types and one +// can easily refactor the folding into the TypeFolder trait as +// needed. + +/// AdtDefs are basically the same as a DefId. +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } +} + +impl<'tcx, T: TypeFoldable<'tcx>, U: TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { + fn super_fold_with>(&self, folder: &mut F) -> (T, U) { + (self.0.fold_with(folder), self.1.fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) || self.1.visit_with(visitor) + } +} + +EnumTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for Option { + (Some)(a), + (None), + } where T: TypeFoldable<'tcx> +} + +EnumTypeFoldableImpl! { + impl<'tcx, T, E> TypeFoldable<'tcx> for Result { + (Ok)(a), + (Err)(a), + } where T: TypeFoldable<'tcx>, E: TypeFoldable<'tcx>, +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Rc::new((**self).fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Arc::new((**self).fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let content: T = (**self).fold_with(folder); + box content + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect() + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect::>().into_boxed_slice() + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.map_bound_ref(|ty| ty.fold_with(folder)) + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_binder(self) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.skip_binder().visit_with(visitor) + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_binder(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_existential_predicates(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|p| p.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_type_list(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_projs(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::ty::InstanceDef::*; + Self { + substs: self.substs.fold_with(folder), + def: match self.def { + Item(did) => Item(did.fold_with(folder)), + VtableShim(did) => VtableShim(did.fold_with(folder)), + ReifyShim(did) => ReifyShim(did.fold_with(folder)), + Intrinsic(did) => Intrinsic(did.fold_with(folder)), + FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), + Virtual(did, i) => Virtual(did.fold_with(folder), i), + ClosureOnceShim { call_once } => { + ClosureOnceShim { call_once: call_once.fold_with(folder) } + } + DropGlue(did, ty) => DropGlue(did.fold_with(folder), ty.fold_with(folder)), + CloneShim(did, ty) => CloneShim(did.fold_with(folder), ty.fold_with(folder)), + }, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + use crate::ty::InstanceDef::*; + self.substs.visit_with(visitor) + || match self.def { + Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { + did.visit_with(visitor) + } + FnPtrShim(did, ty) | CloneShim(did, ty) => { + did.visit_with(visitor) || ty.visit_with(visitor) + } + DropGlue(did, ty) => did.visit_with(visitor) || ty.visit_with(visitor), + ClosureOnceShim { call_once } => call_once.visit_with(visitor), + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Self { instance: self.instance.fold_with(folder), promoted: self.promoted } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.instance.visit_with(visitor) + } +} + +impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let kind = match self.kind { + ty::RawPtr(tm) => ty::RawPtr(tm.fold_with(folder)), + ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), + ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), + ty::Adt(tid, substs) => ty::Adt(tid, substs.fold_with(folder)), + ty::Dynamic(ref trait_ty, ref region) => { + ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) + } + ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), + ty::FnDef(def_id, substs) => ty::FnDef(def_id, substs.fold_with(folder)), + ty::FnPtr(f) => ty::FnPtr(f.fold_with(folder)), + ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl), + ty::Generator(did, substs, movability) => { + ty::Generator(did, substs.fold_with(folder), movability) + } + ty::GeneratorWitness(types) => ty::GeneratorWitness(types.fold_with(folder)), + ty::Closure(did, substs) => ty::Closure(did, substs.fold_with(folder)), + ty::Projection(ref data) => ty::Projection(data.fold_with(folder)), + ty::Opaque(did, substs) => ty::Opaque(did, substs.fold_with(folder)), + + ty::Bool + | ty::Char + | ty::Str + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Error + | ty::Infer(_) + | ty::Param(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Never + | ty::Foreign(..) => return self, + }; + + if self.kind == kind { self } else { folder.tcx().mk_ty(kind) } + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_ty(*self) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match self.kind { + ty::RawPtr(ref tm) => tm.visit_with(visitor), + ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor), + ty::Slice(typ) => typ.visit_with(visitor), + ty::Adt(_, substs) => substs.visit_with(visitor), + ty::Dynamic(ref trait_ty, ref reg) => { + trait_ty.visit_with(visitor) || reg.visit_with(visitor) + } + ty::Tuple(ts) => ts.visit_with(visitor), + ty::FnDef(_, substs) => substs.visit_with(visitor), + ty::FnPtr(ref f) => f.visit_with(visitor), + ty::Ref(r, ty, _) => r.visit_with(visitor) || ty.visit_with(visitor), + ty::Generator(_did, ref substs, _) => substs.visit_with(visitor), + ty::GeneratorWitness(ref types) => types.visit_with(visitor), + ty::Closure(_did, ref substs) => substs.visit_with(visitor), + ty::Projection(ref data) => data.visit_with(visitor), + ty::Opaque(_, ref substs) => substs.visit_with(visitor), + + ty::Bool + | ty::Char + | ty::Str + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Error + | ty::Infer(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Param(..) + | ty::Never + | ty::Foreign(..) => false, + } + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_ty(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_region(*self) + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_region(*self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_predicates(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|p| p.visit_with(visitor)) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|x| x.fold_with(folder)).collect() + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let ty = self.ty.fold_with(folder); + let val = self.val.fold_with(folder); + folder.tcx().mk_const(ty::Const { ty, val }) + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_const(*self) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.ty.visit_with(visitor) || self.val.visit_with(visitor) + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_const(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)), + ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)), + ty::ConstKind::Unevaluated(did, substs, promoted) => { + ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted) + } + ty::ConstKind::Value(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) + | ty::ConstKind::Error => *self, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + ty::ConstKind::Infer(ic) => ic.visit_with(visitor), + ty::ConstKind::Param(p) => p.visit_with(visitor), + ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor), + ty::ConstKind::Value(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Error => false, + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } +} + +// Does the equivalent of +// ``` +// let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); +// folder.tcx().intern_*(&v) +// ``` +fn fold_list<'tcx, F, T>( + list: &'tcx ty::List, + folder: &mut F, + intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> &'tcx ty::List, +) -> &'tcx ty::List +where + F: TypeFolder<'tcx>, + T: TypeFoldable<'tcx> + PartialEq + Copy, +{ + let mut iter = list.iter(); + // Look for the first element that changed + if let Some((i, new_t)) = iter.by_ref().enumerate().find_map(|(i, t)| { + let new_t = t.fold_with(folder); + if new_t == *t { None } else { Some((i, new_t)) } + }) { + // An element changed, prepare to intern the resulting list + let mut new_list = SmallVec::<[_; 8]>::with_capacity(list.len()); + new_list.extend_from_slice(&list[..i]); + new_list.push(new_t); + new_list.extend(iter.map(|t| t.fold_with(folder))); + intern(folder.tcx(), &new_list) + } else { + list + } +} diff --git a/src/librustc/ty/sty.rs b/src/librustc_middle/ty/sty.rs similarity index 94% rename from src/librustc/ty/sty.rs rename to src/librustc_middle/ty/sty.rs index d440e84e15c45..2ad673b2c1943 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -10,22 +10,22 @@ use crate::middle::region; use crate::mir::interpret::ConstValue; use crate::mir::interpret::{LitToConstInput, Scalar}; use crate::mir::Promoted; -use crate::ty::layout::VariantIdx; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; use crate::ty::{ self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable, WithConstness, }; use crate::ty::{List, ParamEnv, ParamEnvAnd, TyS}; use polonius_engine::Atom; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_data_structures::captures::Captures; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::vec::Idx; use rustc_macros::HashStable; -use rustc_span::symbol::{kw, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_target::abi::{Size, VariantIdx}; use rustc_target::spec::abi; -use smallvec::SmallVec; use std::borrow::Cow; use std::cmp::Ordering; use std::marker::PhantomData; @@ -181,11 +181,6 @@ pub enum TyKind<'tcx> { /// `>::N`. Projection(ProjectionTy<'tcx>), - /// A placeholder type used when we do not have enough information - /// to normalize the projection of an associated type to an - /// existing concrete type. Currently only used with chalk-engine. - UnnormalizedProjection(ProjectionTy<'tcx>), - /// Opaque (`impl Trait`) type found in a return type. /// The `DefId` comes either from /// * the `impl Trait` ast::Ty node, @@ -755,14 +750,6 @@ impl<'tcx> TraitRef<'tcx> { self.substs.type_at(0) } - pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator> + 'a { - // Select only the "input types" from a trait-reference. For - // now this is all the types that appear in the - // trait-reference, but it should eventually exclude - // associated types. - self.substs.types() - } - pub fn from_method( tcx: TyCtxt<'tcx>, trait_id: DefId, @@ -806,14 +793,6 @@ pub struct ExistentialTraitRef<'tcx> { } impl<'tcx> ExistentialTraitRef<'tcx> { - pub fn input_types<'b>(&'b self) -> impl DoubleEndedIterator> + 'b { - // Select only the "input types" from a trait-reference. For - // now this is all the types that appear in the - // trait-reference, but it should eventually exclude - // associated types. - self.substs.types() - } - pub fn erase_self_ty( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, @@ -1238,7 +1217,7 @@ pub type Region<'tcx> = &'tcx RegionKind; /// /// The scope regions are related to one another based on the AST /// structure. (See `RegionRelations` type, and particularly the -/// `rustc::middle::region::ScopeTree`.) +/// `rustc_middle::middle::region::ScopeTree`.) /// /// Note that inference variables and bound regions are not included /// in this diagram. In the case of inference variables, they should @@ -1622,17 +1601,19 @@ impl RegionKind { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; - flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; + flags = flags | TypeFlags::STILL_FURTHER_SPECIALIZABLE; } ty::RePlaceholder(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PLACEHOLDER; + flags = flags | TypeFlags::STILL_FURTHER_SPECIALIZABLE; } ty::ReEarlyBound(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PARAM; + flags = flags | TypeFlags::STILL_FURTHER_SPECIALIZABLE; } ty::ReFree { .. } | ty::ReScope { .. } => { flags = flags | TypeFlags::HAS_FREE_REGIONS; @@ -1879,24 +1860,6 @@ impl<'tcx> TyS<'tcx> { self.is_region_ptr() || self.is_unsafe_ptr() || self.is_fn_ptr() } - /// Returns `true` if this type is an `Arc`. - #[inline] - pub fn is_arc(&self) -> bool { - match self.kind { - Adt(def, _) => def.is_arc(), - _ => false, - } - } - - /// Returns `true` if this type is an `Rc`. - #[inline] - pub fn is_rc(&self) -> bool { - match self.kind { - Adt(def, _) => def.is_rc(), - _ => false, - } - } - #[inline] pub fn is_box(&self) -> bool { match self.kind { @@ -1919,8 +1882,15 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_scalar(&self) -> bool { match self.kind { - Bool | Char | Int(_) | Float(_) | Uint(_) | Infer(IntVar(_)) | Infer(FloatVar(_)) - | FnDef(..) | FnPtr(_) | RawPtr(_) => true, + Bool + | Char + | Int(_) + | Float(_) + | Uint(_) + | Infer(IntVar(_) | FloatVar(_)) + | FnDef(..) + | FnPtr(_) + | RawPtr(_) => true, _ => false, } } @@ -2149,31 +2119,6 @@ impl<'tcx> TyS<'tcx> { } } - /// Pushes onto `out` the regions directly referenced from this type (but not - /// types reachable from this type via `walk_tys`). This ignores late-bound - /// regions binders. - pub fn push_regions(&self, out: &mut SmallVec<[ty::Region<'tcx>; 4]>) { - match self.kind { - Ref(region, _, _) => { - out.push(region); - } - Dynamic(ref obj, region) => { - out.push(region); - if let Some(principal) = obj.principal() { - out.extend(principal.skip_binder().substs.regions()); - } - } - Adt(_, substs) | Opaque(_, substs) => out.extend(substs.regions()), - Closure(_, ref substs) | Generator(_, ref substs, _) => out.extend(substs.regions()), - Projection(ref data) | UnnormalizedProjection(ref data) => { - out.extend(data.substs.regions()) - } - FnDef(..) | FnPtr(_) | GeneratorWitness(..) | Bool | Char | Int(_) | Uint(_) - | Float(_) | Str | Array(..) | Slice(_) | RawPtr(_) | Never | Tuple(..) - | Foreign(..) | Param(_) | Bound(..) | Placeholder(..) | Infer(_) | Error => {} - } - } - /// When we create a closure, we record its kind (i.e., what trait /// it implements) into its `ClosureSubsts` using a type /// parameter. This is kind of a phantom type, except that the @@ -2211,8 +2156,7 @@ impl<'tcx> TyS<'tcx> { /// `false` means nothing -- could be sized, might not be. pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool { match self.kind { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) | ty::Bool @@ -2237,15 +2181,11 @@ impl<'tcx> TyS<'tcx> { ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => false, - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - ty::Infer(ty::TyVar(_)) => false, ty::Bound(..) | ty::Placeholder(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("`is_trivially_sized` applied to unexpected type: {:?}", self) } } @@ -2319,11 +2259,12 @@ impl<'tcx> Const<'tcx> { ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { // Find the name and index of the const parameter by indexing the generics of // the parent item and construct a `ParamConst`. - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let item_id = tcx.hir().get_parent_node(hir_id); let item_def_id = tcx.hir().local_def_id(item_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id)]; + let generics = tcx.generics_of(item_def_id.to_def_id()); + let index = + generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()]; let name = tcx.hir().name(hir_id); ty::ConstKind::Param(ty::ParamConst::new(index, name)) } @@ -2397,43 +2338,45 @@ impl<'tcx> Const<'tcx> { /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the /// unevaluated constant. pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> { - let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs, promoted| { + if let ConstKind::Unevaluated(did, substs, promoted) = self.val { + use crate::mir::interpret::ErrorHandled; + let param_env_and_substs = param_env.with_reveal_all().and(substs); - // Avoid querying `tcx.const_eval(...)` with any e.g. inference vars. - if param_env_and_substs.has_local_value() { - return None; - } + // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` + // also does later, but we want to do it before checking for + // inference variables. + let param_env_and_substs = tcx.erase_regions(¶m_env_and_substs); + + // HACK(eddyb) when the query key would contain inference variables, + // attempt using identity substs and `ParamEnv` instead, that will succeed + // when the expression doesn't depend on any parameters. + // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that + // we can call `infcx.const_eval_resolve` which handles inference variables. + let param_env_and_substs = if param_env_and_substs.needs_infer() { + tcx.param_env(did).and(InternalSubsts::identity_for_item(tcx, did)) + } else { + param_env_and_substs + }; + // FIXME(eddyb) maybe the `const_eval_*` methods should take + // `ty::ParamEnvAnd` instead of having them separate. let (param_env, substs) = param_env_and_substs.into_parts(); - // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - tcx.const_eval_resolve(param_env, did, substs, promoted, None) - .ok() - .map(|val| Const::from_value(tcx, val, self.ty)) - }; - - match self.val { - ConstKind::Unevaluated(did, substs, promoted) => { - // HACK(eddyb) when substs contain e.g. inference variables, - // attempt using identity substs instead, that will succeed - // when the expression doesn't depend on any parameters. - // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that - // we can call `infcx.const_eval_resolve` which handles inference variables. - if substs.has_local_value() { - let identity_substs = InternalSubsts::identity_for_item(tcx, did); - // The `ParamEnv` needs to match the `identity_substs`. - let identity_param_env = tcx.param_env(did); - match try_const_eval(did, identity_param_env, identity_substs, promoted) { - Some(ct) => ct.subst(tcx, substs), - None => self, - } - } else { - try_const_eval(did, param_env, substs, promoted).unwrap_or(self) + match tcx.const_eval_resolve(param_env, did, substs, promoted, None) { + // NOTE(eddyb) `val` contains no lifetimes/types/consts, + // and we use the original type, so nothing from `substs` + // (which may be identity substs, see above), + // can leak through `val` into the const we return. + Ok(val) => Const::from_value(tcx, val, self.ty), + Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => self, + Err(ErrorHandled::Reported(ErrorReported)) => { + tcx.mk_const(ty::Const { val: ty::ConstKind::Error, ty: self.ty }) } } - _ => self, + } else { + self } } @@ -2489,6 +2432,10 @@ pub enum ConstKind<'tcx> { /// Used to hold computed value. Value(ConstValue<'tcx>), + + /// A placeholder for a const which could not be computed; this is + /// propagated to avoid useless error messages. + Error, } #[cfg(target_arch = "x86_64")] @@ -2501,7 +2448,7 @@ impl<'tcx> ConstKind<'tcx> { } #[inline] - pub fn try_to_bits(&self, size: ty::layout::Size) -> Option { + pub fn try_to_bits(&self, size: Size) -> Option { if let ConstKind::Value(val) = self { val.try_to_bits(size) } else { None } } } diff --git a/src/librustc/ty/subst.rs b/src/librustc_middle/ty/subst.rs similarity index 94% rename from src/librustc/ty/subst.rs rename to src/librustc_middle/ty/subst.rs index a3acc14856e1f..4d73f8f91ad2e 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc_middle/ty/subst.rs @@ -128,6 +128,14 @@ impl<'tcx> GenericArg<'tcx> { _ => bug!("expected a type, but found another kind"), } } + + /// Unpack the `GenericArg` as a const when it is known certainly to be a const. + pub fn expect_const(self) -> &'tcx ty::Const<'tcx> { + match self.unpack() { + GenericArgKind::Const(c) => c, + _ => bug!("expected a const, but found another kind"), + } + } } impl<'a, 'tcx> Lift<'tcx> for GenericArg<'a> { @@ -199,38 +207,6 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { Self::for_item(tcx, def_id, |param, _| tcx.mk_param_from_def(param)) } - /// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked - /// var bound at index `0`. For types, we use a `BoundVar` index equal to - /// the type parameter index. For regions, we use the `BoundRegion::BrNamed` - /// variant (which has a `DefId`). - pub fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { - Self::for_item(tcx, def_id, |param, _| match param.kind { - ty::GenericParamDefKind::Type { .. } => tcx - .mk_ty(ty::Bound( - ty::INNERMOST, - ty::BoundTy { - var: ty::BoundVar::from(param.index), - kind: ty::BoundTyKind::Param(param.name), - }, - )) - .into(), - - ty::GenericParamDefKind::Lifetime => tcx - .mk_region(ty::RegionKind::ReLateBound( - ty::INNERMOST, - ty::BoundRegion::BrNamed(param.def_id, param.name), - )) - .into(), - - ty::GenericParamDefKind::Const => tcx - .mk_const(ty::Const { - val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), - ty: tcx.type_of(param.def_id), - }) - .into(), - }) - } - /// Creates a `InternalSubsts` for generic parameter definitions, /// by calling closures to obtain each kind. /// The closures get to observe the `InternalSubsts` as they're diff --git a/src/librustc/ty/trait_def.rs b/src/librustc_middle/ty/trait_def.rs similarity index 94% rename from src/librustc/ty/trait_def.rs rename to src/librustc_middle/ty/trait_def.rs index b0287be65294f..8f125098ee684 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc_middle/ty/trait_def.rs @@ -1,4 +1,3 @@ -use crate::hir::map::DefPathHash; use crate::ich::{self, StableHashingContext}; use crate::traits::specialization_graph; use crate::ty::fast_reject; @@ -6,6 +5,7 @@ use crate::ty::fold::TypeFoldable; use crate::ty::{Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::definitions::DefPathHash; use rustc_hir::HirId; use rustc_data_structures::fx::FxHashMap; @@ -168,15 +168,10 @@ impl<'tcx> TyCtxt<'tcx> { } /// Returns a vector containing all impls - pub fn all_impls(self, def_id: DefId) -> Vec { - let impls = self.trait_impls_of(def_id); + pub fn all_impls(self, def_id: DefId) -> impl Iterator + 'tcx { + let TraitImpls { blanket_impls, non_blanket_impls } = self.trait_impls_of(def_id); - impls - .blanket_impls - .iter() - .chain(impls.non_blanket_impls.values().flatten()) - .cloned() - .collect() + blanket_impls.iter().chain(non_blanket_impls.iter().map(|(_, v)| v).flatten()).cloned() } } @@ -189,11 +184,11 @@ pub(super) fn all_local_trait_impls<'tcx>( } // Query provider for `trait_impls_of`. -pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> &TraitImpls { +pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> TraitImpls { let mut impls = TraitImpls::default(); { - let mut add_impl = |impl_def_id| { + let mut add_impl = |impl_def_id: DefId| { let impl_self_ty = tcx.type_of(impl_def_id); if impl_def_id.is_local() && impl_self_ty.references_error() { return; @@ -217,11 +212,11 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> &Trai } for &hir_id in tcx.hir().trait_impls(trait_id) { - add_impl(tcx.hir().local_def_id(hir_id)); + add_impl(tcx.hir().local_def_id(hir_id).to_def_id()); } } - tcx.arena.alloc(impls) + impls } impl<'a> HashStable> for TraitImpls { diff --git a/src/librustc/ty/util.rs b/src/librustc_middle/ty/util.rs similarity index 97% rename from src/librustc/ty/util.rs rename to src/librustc_middle/ty/util.rs index 1f512f1dde7d6..f9c10488ffbc0 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc_middle/ty/util.rs @@ -1,25 +1,25 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. -use crate::hir::map::DefPathData; use crate::ich::NodeIdHashingMode; +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir::interpret::{sign_extend, truncate}; -use crate::ty::layout::{Integer, IntegerExt, Size}; +use crate::ty::layout::IntegerExt; use crate::ty::query::TyCtxtAt; use crate::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef}; use crate::ty::TyKind::*; use crate::ty::{self, DefIdTree, GenericParamDefKind, Ty, TyCtxt, TypeFoldable}; -use crate::util::common::ErrorReported; use rustc_apfloat::Float as _; use rustc_ast::ast; use rustc_attr::{self as attr, SignedInt, UnsignedInt}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_span::Span; -use rustc_target::abi::TargetDataLayout; +use rustc_target::abi::{Integer, Size, TargetDataLayout}; use smallvec::SmallVec; use std::{cmp, fmt}; @@ -316,10 +316,8 @@ impl<'tcx> TyCtxt<'tcx> { break; } } - (ty::Projection(_), _) - | (ty::Opaque(..), _) - | (_, ty::Projection(_)) - | (_, ty::Opaque(..)) => { + (ty::Projection(_) | ty::Opaque(..), _) + | (_, ty::Projection(_) | ty::Opaque(..)) => { // If either side is a projection, attempt to // progress via normalization. (Should be safe to // apply to both sides as normalization is @@ -448,24 +446,24 @@ impl<'tcx> TyCtxt<'tcx> { /// those are not yet phased out). The parent of the closure's /// `DefId` will also be the context where it appears. pub fn is_closure(self, def_id: DefId) -> bool { - self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr + matches!(self.def_kind(def_id), DefKind::Closure | DefKind::Generator) } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). pub fn is_trait(self, def_id: DefId) -> bool { - self.def_kind(def_id) == Some(DefKind::Trait) + self.def_kind(def_id) == DefKind::Trait } /// Returns `true` if `def_id` refers to a trait alias (i.e., `trait Foo = ...;`), /// and `false` otherwise. pub fn is_trait_alias(self, def_id: DefId) -> bool { - self.def_kind(def_id) == Some(DefKind::TraitAlias) + self.def_kind(def_id) == DefKind::TraitAlias } /// Returns `true` if this `DefId` refers to the implicit constructor for /// a tuple struct like `struct Foo(u32)`, and `false` otherwise. pub fn is_constructor(self, def_id: DefId) -> bool { - self.def_key(def_id).disambiguated_data.data == DefPathData::Ctor + matches!(self.def_kind(def_id), DefKind::Ctor(..)) } /// Given the def-ID of a fn or closure, returns the def-ID of @@ -531,6 +529,11 @@ impl<'tcx> TyCtxt<'tcx> { self.static_mutability(def_id).is_some() } + /// Returns `true` if this is a `static` item with the `#[thread_local]` attribute. + pub fn is_thread_local_static(&self, def_id: DefId) -> bool { + self.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) + } + /// Returns `true` if the node pointed to by `def_id` is a mutable `static` item. pub fn is_mutable_static(&self, def_id: DefId) -> bool { self.static_mutability(def_id) == Some(hir::Mutability::Mut) @@ -742,8 +745,7 @@ impl<'tcx> ty::TyS<'tcx> { | ty::Opaque(..) | ty::Param(_) | ty::Placeholder(_) - | ty::Projection(_) - | ty::UnnormalizedProjection(_) => false, + | ty::Projection(_) => false, } } @@ -1047,10 +1049,7 @@ pub fn needs_drop_components( // Foreign types can never have destructors. ty::Foreign(..) => Ok(SmallVec::new()), - // Pessimistically assume that all generators will require destructors - // as we don't know if a destructor is a noop or not until after the MIR - // state transformation pass. - ty::Generator(..) | ty::Dynamic(..) | ty::Error => Err(AlwaysRequiresDrop), + ty::Dynamic(..) | ty::Error => Err(AlwaysRequiresDrop), ty::Slice(ty) => needs_drop_components(ty, target_layout), ty::Array(elem_ty, size) => { @@ -1077,13 +1076,13 @@ pub fn needs_drop_components( // These require checking for `Copy` bounds or `Adt` destructors. ty::Adt(..) | ty::Projection(..) - | ty::UnnormalizedProjection(..) | ty::Param(_) | ty::Bound(..) | ty::Placeholder(..) | ty::Opaque(..) | ty::Infer(_) - | ty::Closure(..) => Ok(smallvec![ty]), + | ty::Closure(..) + | ty::Generator(..) => Ok(smallvec![ty]), } } diff --git a/src/librustc_middle/ty/walk.rs b/src/librustc_middle/ty/walk.rs new file mode 100644 index 0000000000000..0093c60d7689b --- /dev/null +++ b/src/librustc_middle/ty/walk.rs @@ -0,0 +1,182 @@ +//! An iterator over the type substructure. +//! WARNING: this does not keep track of the region depth. + +use crate::ty; +use crate::ty::subst::{GenericArg, GenericArgKind}; +use smallvec::{self, SmallVec}; + +// 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, +} + +impl<'tcx> TypeWalker<'tcx> { + pub fn new(root: GenericArg<'tcx>) -> TypeWalker<'tcx> { + TypeWalker { stack: smallvec![root], last_subtree: 1 } + } + + /// Skips the subtree corresponding to the last type + /// returned by `next()`. + /// + /// Example: Imagine you are walking `Foo, usize>`. + /// + /// ``` + /// let mut iter: TypeWalker = ...; + /// iter.next(); // yields Foo + /// iter.next(); // yields Bar + /// iter.skip_current_subtree(); // skips int + /// 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); + let next = self.stack.pop()?; + self.last_subtree = self.stack.len(); + push_inner(&mut self.stack, next); + debug!("next: stack={:?}", self.stack); + Some(next) + } +} + +impl 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: + /// + /// ```notrust + /// isize => { isize } + /// Foo> => { Foo>, Bar, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(self) -> TypeWalker<'tcx> { + TypeWalker::new(self) + } + + /// Iterator that walks the immediate children of `self`. Hence + /// `Foo, u32>` yields the sequence `[Bar, u32]` + /// (but not `i32`, like `walk`). + pub fn walk_shallow(self) -> impl Iterator> { + let mut stack = SmallVec::new(); + push_inner(&mut stack, self); + stack.into_iter() + } +} + +impl<'tcx> super::TyS<'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: + /// + /// ```notrust + /// isize => { isize } + /// Foo> => { Foo>, Bar, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(&'tcx 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::Array(ty, len) => { + stack.push(len.into()); + stack.push(ty.into()); + } + ty::Slice(ty) => { + stack.push(ty.into()); + } + ty::RawPtr(mt) => { + stack.push(mt.ty.into()); + } + ty::Ref(lt, ty, _) => { + stack.push(ty.into()); + stack.push(lt.into()); + } + ty::Projection(data) => { + stack.extend(data.substs.iter().copied().rev()); + } + ty::Dynamic(obj, lt) => { + stack.push(lt.into()); + stack.extend(obj.iter().rev().flat_map(|predicate| { + let (substs, opt_ty) = match *predicate.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => (tr.substs, None), + ty::ExistentialPredicate::Projection(p) => (p.substs, Some(p.ty)), + ty::ExistentialPredicate::AutoTrait(_) => + // Empty iterator + { + (ty::InternalSubsts::empty(), None) + } + }; + + substs.iter().copied().rev().chain(opt_ty.map(|ty| ty.into())) + })); + } + ty::Adt(_, substs) + | ty::Opaque(_, substs) + | ty::Closure(_, substs) + | ty::Generator(_, substs, _) + | ty::Tuple(substs) + | ty::FnDef(_, substs) => { + stack.extend(substs.iter().copied().rev()); + } + ty::GeneratorWitness(ts) => { + stack.extend(ts.skip_binder().iter().cloned().rev().map(|ty| ty.into())); + } + ty::FnPtr(sig) => { + stack.push(sig.skip_binder().output().into()); + stack.extend(sig.skip_binder().inputs().iter().cloned().rev().map(|ty| ty.into())); + } + }, + GenericArgKind::Lifetime(_) => {} + GenericArgKind::Const(parent_ct) => { + stack.push(parent_ct.ty.into()); + match parent_ct.val { + ty::ConstKind::Infer(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Value(_) + | ty::ConstKind::Error => {} + + ty::ConstKind::Unevaluated(_, substs, _) => { + stack.extend(substs.iter().copied().rev()); + } + } + } + } +} diff --git a/src/librustc/util/bug.rs b/src/librustc_middle/util/bug.rs similarity index 100% rename from src/librustc/util/bug.rs rename to src/librustc_middle/util/bug.rs diff --git a/src/librustc/util/common.rs b/src/librustc_middle/util/common.rs similarity index 97% rename from src/librustc/util/common.rs rename to src/librustc_middle/util/common.rs index 19b43bfd16241..1e09702bf27ca 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc_middle/util/common.rs @@ -8,8 +8,6 @@ use std::time::{Duration, Instant}; #[cfg(test)] mod tests; -pub use rustc_errors::ErrorReported; - pub fn to_readable_str(mut val: usize) -> String { let mut groups = vec![]; loop { diff --git a/src/librustc/util/common/tests.rs b/src/librustc_middle/util/common/tests.rs similarity index 100% rename from src/librustc/util/common/tests.rs rename to src/librustc_middle/util/common/tests.rs diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 256a80076b923..d922a83232901 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -16,8 +16,7 @@ itertools = "0.8" log = "0.4" log_settings = "0.1.1" polonius-engine = "0.12.0" -rustc = { path = "../librustc" } -rustc_ast_pretty = { path = "../librustc_ast_pretty" } +rustc_middle = { path = "../librustc_middle" } rustc_attr = { path = "../librustc_attr" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index 9f4f0ce5620b5..ef9af7bace96f 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/src/librustc_mir/borrow_check/borrow_set.rs @@ -3,13 +3,13 @@ use crate::borrow_check::path_utils::allow_two_phase_borrow; use crate::borrow_check::place_ext::PlaceExt; use crate::dataflow::indexes::BorrowIndex; use crate::dataflow::move_paths::MoveData; -use rustc::mir::traversal; -use rustc::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; -use rustc::mir::{self, Body, Local, Location, ReadOnlyBodyAndCache}; -use rustc::ty::{RegionVid, TyCtxt}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; +use rustc_middle::mir::traversal; +use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{self, Body, Local, Location}; +use rustc_middle::ty::{RegionVid, TyCtxt}; use std::fmt; use std::ops::Index; @@ -90,7 +90,7 @@ crate enum LocalsStateAtExit { impl LocalsStateAtExit { fn build( locals_are_invalidated_at_exit: bool, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, move_data: &MoveData<'tcx>, ) -> Self { struct HasStorageDead(BitSet); @@ -107,7 +107,7 @@ impl LocalsStateAtExit { LocalsStateAtExit::AllAreInvalidated } else { let mut has_storage_dead = HasStorageDead(BitSet::new_empty(body.local_decls.len())); - has_storage_dead.visit_body(body); + has_storage_dead.visit_body(&body); let mut has_storage_dead_or_moved = has_storage_dead.0; for move_out in &move_data.moves { if let Some(index) = move_data.base_local(move_out.path) { @@ -122,7 +122,7 @@ impl LocalsStateAtExit { impl<'tcx> BorrowSet<'tcx> { pub fn build( tcx: TyCtxt<'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, locals_are_invalidated_at_exit: bool, move_data: &MoveData<'tcx>, ) -> Self { @@ -206,7 +206,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { let idx = self.idx_vec.push(borrow); self.location_map.insert(location, idx); - self.insert_as_pending_if_two_phase(location, &assigned_place, kind, idx); + self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); self.local_map.entry(borrowed_place.local).or_default().insert(idx); } diff --git a/src/librustc_mir/borrow_check/constraint_generation.rs b/src/librustc_mir/borrow_check/constraint_generation.rs index 46cfe0897a9ac..e0420d974fbdf 100644 --- a/src/librustc_mir/borrow_check/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/constraint_generation.rs @@ -1,13 +1,13 @@ -use rustc::mir::visit::TyContext; -use rustc::mir::visit::Visitor; -use rustc::mir::{ +use rustc_infer::infer::InferCtxt; +use rustc_middle::mir::visit::TyContext; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{ BasicBlock, BasicBlockData, Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection, }; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, RegionVid, Ty}; -use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, RegionVid, Ty}; use crate::borrow_check::{ borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, nll::ToRegionVid, @@ -114,7 +114,7 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { // When we see `X = ...`, then kill borrows of // `(*X).foo` and so forth. - self.record_killed_borrows_for_place(place, location); + self.record_killed_borrows_for_place(*place, location); self.super_assign(place, rvalue, location); } @@ -139,7 +139,7 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { // A `Call` terminator's return value can be a local which has borrows, // so we need to record those as `killed` as well. - if let TerminatorKind::Call { ref destination, .. } = terminator.kind { + if let TerminatorKind::Call { destination, .. } = terminator.kind { if let Some((place, _)) = destination { self.record_killed_borrows_for_place(place, location); } @@ -177,7 +177,7 @@ impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> { /// When recording facts for Polonius, records the borrows on the specified place /// as `killed`. For example, when assigning to a local, or on a call's return destination. - fn record_killed_borrows_for_place(&mut self, place: &Place<'tcx>, location: Location) { + fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) { if let Some(all_facts) = self.all_facts { let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); @@ -217,7 +217,7 @@ impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> { let places_conflict = places_conflict::places_conflict( self.infcx.tcx, self.body, - &self.borrow_set.borrows[borrow_index].borrowed_place, + self.borrow_set.borrows[borrow_index].borrowed_place, place, places_conflict::PlaceConflictBias::NoOverlap, ); diff --git a/src/librustc_mir/borrow_check/constraints/graph.rs b/src/librustc_mir/borrow_check/constraints/graph.rs index c60a11e348d70..f3f6b8c10da7c 100644 --- a/src/librustc_mir/borrow_check/constraints/graph.rs +++ b/src/librustc_mir/borrow_check/constraints/graph.rs @@ -1,7 +1,7 @@ -use rustc::mir::ConstraintCategory; -use rustc::ty::RegionVid; use rustc_data_structures::graph; use rustc_index::vec::IndexVec; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::ty::RegionVid; use rustc_span::DUMMY_SP; use crate::borrow_check::{ diff --git a/src/librustc_mir/borrow_check/constraints/mod.rs b/src/librustc_mir/borrow_check/constraints/mod.rs index ef70b127ac5bd..3772b7d8f986d 100644 --- a/src/librustc_mir/borrow_check/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/constraints/mod.rs @@ -1,7 +1,7 @@ -use rustc::mir::ConstraintCategory; -use rustc::ty::RegionVid; use rustc_data_structures::graph::scc::Sccs; use rustc_index::vec::IndexVec; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::ty::RegionVid; use std::fmt; use std::ops::Index; diff --git a/src/librustc_mir/borrow_check/def_use.rs b/src/librustc_mir/borrow_check/def_use.rs new file mode 100644 index 0000000000000..689ec249a2fb4 --- /dev/null +++ b/src/librustc_mir/borrow_check/def_use.rs @@ -0,0 +1,78 @@ +use rustc_middle::mir::visit::{ + MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, +}; + +#[derive(Eq, PartialEq, Clone)] +pub enum DefUse { + Def, + Use, + Drop, +} + +pub fn categorize(context: PlaceContext) -> Option { + match context { + /////////////////////////////////////////////////////////////////////////// + // DEFS + + PlaceContext::MutatingUse(MutatingUseContext::Store) | + + // This is potentially both a def and a use... + PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) | + + // We let Call define the result in both the success and + // unwind cases. This is not really correct, however it + // does not seem to be observable due to the way that we + // generate MIR. To do things properly, we would apply + // the def in call only to the input from the success + // path and not the unwind path. -nmatsakis + PlaceContext::MutatingUse(MutatingUseContext::Call) | + PlaceContext::MutatingUse(MutatingUseContext::Yield) | + + // Storage live and storage dead aren't proper defines, but we can ignore + // values that come before them. + PlaceContext::NonUse(NonUseContext::StorageLive) | + PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def), + + /////////////////////////////////////////////////////////////////////////// + // REGULAR USES + // + // These are uses that occur *outside* of a drop. For the + // purposes of NLL, these are special in that **all** the + // lifetimes appearing in the variable must be live for each regular use. + + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) | + PlaceContext::MutatingUse(MutatingUseContext::Projection) | + + // Borrows only consider their local used at the point of the borrow. + // This won't affect the results since we use this analysis for generators + // and we only care about the result at suspension points. Borrows cannot + // cross suspension points so this behavior is unproblematic. + PlaceContext::MutatingUse(MutatingUseContext::Borrow) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) | + + PlaceContext::MutatingUse(MutatingUseContext::AddressOf) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) | + PlaceContext::NonUse(NonUseContext::AscribeUserTy) | + PlaceContext::MutatingUse(MutatingUseContext::Retag) => + Some(DefUse::Use), + + /////////////////////////////////////////////////////////////////////////// + // DROP USES + // + // These are uses that occur in a DROP (a MIR drop, not a + // call to `std::mem::drop()`). For the purposes of NLL, + // uses in drop are special because `#[may_dangle]` + // attributes can affect whether lifetimes must be live. + + PlaceContext::MutatingUse(MutatingUseContext::Drop) => + Some(DefUse::Drop), + + // Debug info is neither def nor use. + PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None, + } +} diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index 27a8616939704..5f1c0911da2bf 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -1,18 +1,18 @@ -use rustc::mir::{ - self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, - FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, - ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm, -}; -use rustc::ty::{self, Ty}; +use either::Either; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; use rustc_index::vec::Idx; +use rustc_middle::mir::{ + self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, + FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, + ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm, +}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; use rustc_span::source_map::DesugaringKind; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::suggest_constraining_type_param; use crate::dataflow::drop_flag_effects; use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex}; @@ -186,12 +186,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } let ty = - Place::ty_from(used_place.local, used_place.projection, *self.body, self.infcx.tcx) + Place::ty_from(used_place.local, used_place.projection, self.body, self.infcx.tcx) .ty; let needs_note = match ty.kind { ty::Closure(id, _) => { - let tables = self.infcx.tcx.typeck_tables_of(id); - let hir_id = self.infcx.tcx.hir().as_local_hir_id(id).unwrap(); + let tables = self.infcx.tcx.typeck_tables_of(id.expect_local()); + let hir_id = self.infcx.tcx.hir().as_local_hir_id(id.expect_local()); tables.closure_kind_origins().get(hir_id).is_none() } @@ -202,7 +202,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mpi = self.move_data.moves[move_out_indices[0]].path; let place = &self.move_data.move_paths[mpi].place; - let ty = place.ty(*self.body, self.infcx.tcx).ty; + let ty = place.ty(self.body, self.infcx.tcx).ty; let opt_name = self.describe_place_with_options(place.as_ref(), IncludingDowncast(true)); let note_msg = match opt_name { @@ -222,8 +222,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &mut err, ¶m.name.as_str(), "Copy", - tcx.sess.source_map(), - span, None, ); } @@ -249,21 +247,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { pub(in crate::borrow_check) fn report_move_out_while_borrowed( &mut self, location: Location, - (place, span): (&Place<'tcx>, Span), + (place, span): (Place<'tcx>, Span), borrow: &BorrowData<'tcx>, ) { debug!( "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}", location, place, span, borrow ); - let value_msg = match self.describe_place(place.as_ref()) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - let borrow_msg = match self.describe_place(borrow.borrowed_place.as_ref()) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; + let value_msg = self.describe_any_place(place.as_ref()); + let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref()); let borrow_spans = self.retrieve_borrow_spans(borrow); let borrow_span = borrow_spans.args_or_use(); @@ -271,10 +263,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let move_spans = self.move_spans(place.as_ref(), location); let span = move_spans.args_or_use(); - let mut err = self.cannot_move_when_borrowed( - span, - &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()), - ); + let mut err = + self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref())); err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg)); err.span_label(span, format!("move out of {} occurs here", value_msg)); @@ -301,7 +291,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { pub(in crate::borrow_check) fn report_use_while_mutably_borrowed( &mut self, location: Location, - (place, _span): (&Place<'tcx>, Span), + (place, _span): (Place<'tcx>, Span), borrow: &BorrowData<'tcx>, ) -> DiagnosticBuilder<'cx> { let borrow_spans = self.retrieve_borrow_spans(borrow); @@ -314,16 +304,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut err = self.cannot_use_when_mutably_borrowed( span, - &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()), + &self.describe_any_place(place.as_ref()), borrow_span, - &self.describe_place(borrow.borrowed_place.as_ref()).unwrap_or_else(|| "_".to_owned()), + &self.describe_any_place(borrow.borrowed_place.as_ref()), ); borrow_spans.var_span_label(&mut err, { let place = &borrow.borrowed_place; - let desc_place = self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()); - - format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()) + let desc_place = self.describe_any_place(place.as_ref()); + format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe()) }); self.explain_why_borrow_contains_point(location, borrow, None) @@ -341,7 +330,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { pub(in crate::borrow_check) fn report_conflicting_borrow( &mut self, location: Location, - (place, span): (&Place<'tcx>, Span), + (place, span): (Place<'tcx>, Span), gen_borrow_kind: BorrowKind, issued_borrow: &BorrowData<'tcx>, ) -> DiagnosticBuilder<'cx> { @@ -358,7 +347,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; let (desc_place, msg_place, msg_borrow, union_type_name) = - self.describe_place_for_conflicting_borrow(place, &issued_borrow.borrowed_place); + self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place); let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None); let second_borrow_desc = if explanation.is_explained() { "second " } else { "" }; @@ -407,8 +396,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); self.suggest_split_at_mut_if_applicable( &mut err, - &place, - &issued_borrow.borrowed_place, + place, + issued_borrow.borrowed_place, ); err } @@ -418,10 +407,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None) } - (BorrowKind::Mut { .. }, BorrowKind::Shallow) - | (BorrowKind::Unique, BorrowKind::Shallow) => { + (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => { if let Some(immutable_section_description) = - self.classify_immutable_section(&issued_borrow.assigned_place) + self.classify_immutable_section(issued_borrow.assigned_place) { let mut err = self.cannot_mutate_in_immutable_section( span, @@ -433,7 +421,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_spans.var_span_label( &mut err, format!( - "borrow occurs due to use of `{}`{}", + "borrow occurs due to use of {}{}", desc_place, borrow_spans.describe(), ), @@ -500,27 +488,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) } - (BorrowKind::Shared, BorrowKind::Shared) - | (BorrowKind::Shared, BorrowKind::Shallow) - | (BorrowKind::Shallow, BorrowKind::Mut { .. }) - | (BorrowKind::Shallow, BorrowKind::Unique) - | (BorrowKind::Shallow, BorrowKind::Shared) - | (BorrowKind::Shallow, BorrowKind::Shallow) => unreachable!(), + (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow) + | ( + BorrowKind::Shallow, + BorrowKind::Mut { .. } + | BorrowKind::Unique + | BorrowKind::Shared + | BorrowKind::Shallow, + ) => unreachable!(), }; if issued_spans == borrow_spans { borrow_spans.var_span_label( &mut err, - format!("borrows occur due to use of `{}`{}", desc_place, borrow_spans.describe()), + format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe()), ); } else { let borrow_place = &issued_borrow.borrowed_place; - let borrow_place_desc = - self.describe_place(borrow_place.as_ref()).unwrap_or_else(|| "_".to_owned()); + let borrow_place_desc = self.describe_any_place(borrow_place.as_ref()); issued_spans.var_span_label( &mut err, format!( - "first borrow occurs due to use of `{}`{}", + "first borrow occurs due to use of {}{}", borrow_place_desc, issued_spans.describe(), ), @@ -529,7 +518,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_spans.var_span_label( &mut err, format!( - "second borrow occurs due to use of `{}`{}", + "second borrow occurs due to use of {}{}", desc_place, borrow_spans.describe(), ), @@ -538,7 +527,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if union_type_name != "" { err.note(&format!( - "`{}` is a field of the union `{}`, so it overlaps the field `{}`", + "{} is a field of the union `{}`, so it overlaps the field {}", msg_place, union_type_name, msg_borrow, )); } @@ -558,17 +547,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_split_at_mut_if_applicable( &self, err: &mut DiagnosticBuilder<'_>, - place: &Place<'tcx>, - borrowed_place: &Place<'tcx>, + place: Place<'tcx>, + borrowed_place: Place<'tcx>, ) { - match (&place.projection[..], &borrowed_place.projection[..]) { - ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) => { - err.help( - "consider using `.split_at_mut(position)` or similar method to obtain \ + if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) = + (&place.projection[..], &borrowed_place.projection[..]) + { + err.help( + "consider using `.split_at_mut(position)` or similar method to obtain \ two mutable non-overlapping sub-slices", - ); - } - _ => {} + ); } } @@ -593,20 +581,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// /// This is used when creating error messages like below: /// - /// > cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as - /// > mutable (via `a.u.s.b`) [E0502] + /// ```text + /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as + /// mutable (via `a.u.s.b`) [E0502] + /// ``` pub(in crate::borrow_check) fn describe_place_for_conflicting_borrow( &self, - first_borrowed_place: &Place<'tcx>, - second_borrowed_place: &Place<'tcx>, + first_borrowed_place: Place<'tcx>, + second_borrowed_place: Place<'tcx>, ) -> (String, String, String, String) { // Define a small closure that we can use to check if the type of a place // is a union. let union_ty = |place_base, place_projection| { - let ty = Place::ty_from(place_base, place_projection, *self.body, self.infcx.tcx).ty; + let ty = Place::ty_from(place_base, place_projection, self.body, self.infcx.tcx).ty; ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty) }; - let describe_place = |place| self.describe_place(place).unwrap_or_else(|| "_".to_owned()); // Start with an empty tuple, so we can use the functions on `Option` to reduce some // code duplication (particularly around returning an empty description in the failure @@ -629,13 +618,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { cursor = proj_base; match elem { - ProjectionElem::Field(field, _) - if union_ty(*local, proj_base).is_some() => - { - return Some(( - PlaceRef { local: *local, projection: proj_base }, - field, - )); + ProjectionElem::Field(field, _) if union_ty(local, proj_base).is_some() => { + return Some((PlaceRef { local, projection: proj_base }, field)); } _ => {} } @@ -645,30 +629,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .and_then(|(target_base, target_field)| { // With the place of a union and a field access into it, we traverse the second // borrowed place and look for a access to a different field of the same union. - let Place { local, projection } = second_borrowed_place; + let Place { local, ref projection } = second_borrowed_place; let mut cursor = &projection[..]; while let [proj_base @ .., elem] = cursor { cursor = proj_base; if let ProjectionElem::Field(field, _) = elem { - if let Some(union_ty) = union_ty(*local, proj_base) { + if let Some(union_ty) = union_ty(local, proj_base) { if field != target_field - && *local == target_base.local + && local == target_base.local && proj_base == target_base.projection { - // FIXME when we avoid clone reuse describe_place closure - let describe_base_place = self - .describe_place(PlaceRef { - local: *local, - projection: proj_base, - }) - .unwrap_or_else(|| "_".to_owned()); - return Some(( - describe_base_place, - describe_place(first_borrowed_place.as_ref()), - describe_place(second_borrowed_place.as_ref()), + self.describe_any_place(PlaceRef { + local, + projection: proj_base, + }), + self.describe_any_place(first_borrowed_place.as_ref()), + self.describe_any_place(second_borrowed_place.as_ref()), union_ty.to_string(), )); } @@ -681,7 +660,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If we didn't find a field access into a union, or both places match, then // only return the description of the first place. ( - describe_place(first_borrowed_place.as_ref()), + self.describe_any_place(first_borrowed_place.as_ref()), "".to_string(), "".to_string(), "".to_string(), @@ -701,7 +680,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &mut self, location: Location, borrow: &BorrowData<'tcx>, - place_span: (&Place<'tcx>, Span), + place_span: (Place<'tcx>, Span), kind: Option, ) { debug!( @@ -784,47 +763,26 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ( Some(ref name), BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::Return, - from_closure: false, - ref region_name, - span, - .. - }, - ) - | ( - Some(ref name), - BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::CallArgument, + category: + category + @ + (ConstraintCategory::Return + | ConstraintCategory::CallArgument + | ConstraintCategory::OpaqueType), from_closure: false, ref region_name, span, .. }, - ) if borrow_spans.for_closure() => self.report_escaping_closure_capture( - borrow_spans, - borrow_span, - region_name, - category, - span, - &format!("`{}`", name), - ), - ( - Some(ref name), - BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::OpaqueType, - from_closure: false, - ref region_name, + ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self + .report_escaping_closure_capture( + borrow_spans, + borrow_span, + region_name, + category, span, - .. - }, - ) if borrow_spans.for_generator() => self.report_escaping_closure_capture( - borrow_spans, - borrow_span, - region_name, - category, - span, - &format!("`{}`", name), - ), + &format!("`{}`", name), + ), ( ref name, BorrowExplanation::MustBeValidFor { @@ -907,7 +865,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("`{}` would have to be valid for `{}`...", name, region_name), ); - if let Some(fn_hir_id) = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id) { + if let Some(def_id) = self.mir_def_id.as_local() { + let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id); err.span_label( drop_span, format!( @@ -922,7 +881,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { match &self .infcx .tcx - .typeck_tables_of(self.mir_def_id) + .typeck_tables_of(def_id) .node_type(fn_hir_id) .kind { @@ -986,7 +945,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &mut self, location: Location, borrow: &BorrowData<'tcx>, - (place, drop_span): (&Place<'tcx>, Span), + (place, drop_span): (Place<'tcx>, Span), kind: Option, dropped_ty: Ty<'tcx>, ) { @@ -1211,7 +1170,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> DiagnosticBuilder<'cx> { let tcx = self.infcx.tcx; let args_span = use_span.args_or_use(); - let mut err = self.cannot_capture_in_long_lived_closure(args_span, captured_var, var_span); let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) { Ok(mut string) => { @@ -1237,6 +1195,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }, None => "closure", }; + + let mut err = + self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span); err.span_suggestion( args_span, &format!( @@ -1249,8 +1210,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); let msg = match category { - ConstraintCategory::Return => "closure is returned here".to_string(), - ConstraintCategory::OpaqueType => "generator is returned here".to_string(), + ConstraintCategory::Return | ConstraintCategory::OpaqueType => { + format!("{} is returned here", kind) + } ConstraintCategory::CallArgument => { fr_name.highlight_region_name(&mut err); format!("function requires argument type to outlive `{}`", fr_name) @@ -1303,8 +1265,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec { + fn predecessor_locations( + body: &'a mir::Body<'tcx>, + location: Location, + ) -> impl Iterator + 'a { + if location.statement_index == 0 { + let predecessors = body.predecessors()[location.block].to_vec(); + Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb))) + } else { + Either::Right(std::iter::once(Location { + statement_index: location.statement_index - 1, + ..location + })) + } + } + let mut stack = Vec::new(); - stack.extend(self.body.predecessor_locations(location).map(|predecessor| { + stack.extend(predecessor_locations(self.body, location).map(|predecessor| { let is_back_edge = location.dominates(predecessor, &self.dominators); (predecessor, is_back_edge) })); @@ -1386,7 +1363,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { continue 'dfs; } - stack.extend(self.body.predecessor_locations(location).map(|predecessor| { + stack.extend(predecessor_locations(self.body, location).map(|predecessor| { let back_edge = location.dominates(predecessor, &self.dominators); (predecessor, is_back_edge || back_edge) })); @@ -1398,18 +1375,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { pub(in crate::borrow_check) fn report_illegal_mutation_of_borrowed( &mut self, location: Location, - (place, span): (&Place<'tcx>, Span), + (place, span): (Place<'tcx>, Span), loan: &BorrowData<'tcx>, ) { let loan_spans = self.retrieve_borrow_spans(loan); let loan_span = loan_spans.args_or_use(); + let descr_place = self.describe_any_place(place.as_ref()); if loan.kind == BorrowKind::Shallow { - if let Some(section) = self.classify_immutable_section(&loan.assigned_place) { + if let Some(section) = self.classify_immutable_section(loan.assigned_place) { let mut err = self.cannot_mutate_in_immutable_section( span, loan_span, - &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()), + &descr_place, section, "assign", ); @@ -1424,11 +1402,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - let mut err = self.cannot_assign_to_borrowed( - span, - loan_span, - &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()), - ); + let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place); loan_spans .var_span_label(&mut err, format!("borrow occurs due to use{}", loan_spans.describe())); @@ -1454,9 +1428,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { pub(in crate::borrow_check) fn report_illegal_reassignment( &mut self, _location: Location, - (place, span): (&Place<'tcx>, Span), + (place, span): (Place<'tcx>, Span), assigned_span: Span, - err_place: &Place<'tcx>, + err_place: Place<'tcx>, ) { let (from_arg, local_decl, local_name) = match err_place.as_local() { Some(local) => ( @@ -1471,26 +1445,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // PATTERN;) then make the error refer to that local, rather than the // place being assigned later. let (place_description, assigned_span) = match local_decl { - Some(LocalDecl { local_info: LocalInfo::User(ClearCrossCrate::Clear), .. }) - | Some(LocalDecl { + Some(LocalDecl { local_info: - LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - opt_match_place: None, - .. - }))), + Some(box LocalInfo::User( + ClearCrossCrate::Clear + | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + opt_match_place: None, + .. + })), + )) + | Some(box LocalInfo::StaticRef { .. }) + | None, .. }) - | Some(LocalDecl { local_info: LocalInfo::StaticRef { .. }, .. }) - | Some(LocalDecl { local_info: LocalInfo::Other, .. }) - | None => (self.describe_place(place.as_ref()), assigned_span), - Some(decl) => (self.describe_place(err_place.as_ref()), decl.source_info.span), + | None => (self.describe_any_place(place.as_ref()), assigned_span), + Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span), }; - let mut err = self.cannot_reassign_immutable( - span, - place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"), - from_arg, - ); + let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg); let msg = if from_arg { "cannot assign to immutable argument" } else { @@ -1498,11 +1470,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; if span != assigned_span { if !from_arg { - let value_msg = match place_description { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - err.span_label(assigned_span, format!("first assignment to {}", value_msg)); + err.span_label(assigned_span, format!("first assignment to {}", place_description)); } } if let Some(decl) = local_decl { @@ -1536,7 +1504,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { StorageDeadOrDrop::LocalStorageDead | StorageDeadOrDrop::BoxedStorageDead => { assert!( - Place::ty_from(place.local, proj_base, *self.body, tcx).ty.is_box(), + Place::ty_from(place.local, proj_base, self.body, tcx).ty.is_box(), "Drop of value behind a reference or raw pointer" ); StorageDeadOrDrop::BoxedStorageDead @@ -1544,7 +1512,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { StorageDeadOrDrop::Destructor(_) => base_access, }, ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { - let base_ty = Place::ty_from(place.local, proj_base, *self.body, tcx).ty; + let base_ty = Place::ty_from(place.local, proj_base, self.body, tcx).ty; match base_ty.kind { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor @@ -1569,17 +1537,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } /// Describe the reason for the fake borrow that was assigned to `place`. - fn classify_immutable_section(&self, place: &Place<'tcx>) -> Option<&'static str> { - use rustc::mir::visit::Visitor; - struct FakeReadCauseFinder<'a, 'tcx> { - place: &'a Place<'tcx>, + fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> { + use rustc_middle::mir::visit::Visitor; + struct FakeReadCauseFinder<'tcx> { + place: Place<'tcx>, cause: Option, } - impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { match statement { - Statement { kind: StatementKind::FakeRead(cause, box ref place), .. } - if *place == *self.place => + Statement { kind: StatementKind::FakeRead(cause, box place), .. } + if *place == self.place => { self.cause = Some(*cause); } @@ -1588,7 +1556,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } let mut visitor = FakeReadCauseFinder { place, cause: None }; - visitor.visit_body(self.body); + visitor.visit_body(&self.body); match visitor.cause { Some(FakeReadCause::ForMatchGuard) => Some("match guard"), Some(FakeReadCause::ForIndex) => Some("indexing expression"), @@ -1816,7 +1784,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option> { debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); let is_closure = self.infcx.tcx.is_closure(did); - let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(did)?; + let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(did.as_local()?); let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?; // We need to work out which arguments to highlight. We do this by looking @@ -1850,7 +1818,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { if let ty::Ref(argument_region, _, _) = argument.kind { if argument_region == return_region { - // Need to use the `rustc::ty` types to compare against the + // Need to use the `rustc_middle::ty` types to compare against the // `return_region`. Then use the `rustc_hir` type to get only // the lifetime span. if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind { diff --git a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs index 6475677988fbb..5253acbba7f1c 100644 --- a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs +++ b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs @@ -2,16 +2,16 @@ use std::collections::VecDeque; -use rustc::mir::{ - Body, CastKind, ConstraintCategory, FakeReadCause, Local, Location, Operand, Place, Rvalue, - Statement, StatementKind, TerminatorKind, -}; -use rustc::ty::adjustment::PointerCast; -use rustc::ty::{self, RegionVid, TyCtxt}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_index::vec::IndexVec; use rustc_infer::infer::NLLRegionVariableOrigin; +use rustc_middle::mir::{ + Body, CastKind, ConstraintCategory, FakeReadCause, Local, Location, Operand, Place, Rvalue, + Statement, StatementKind, TerminatorKind, +}; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -156,23 +156,32 @@ impl BorrowExplanation { err.span_label(body.source_info(drop_loc).span, message); if let Some(info) = &local_decl.is_block_tail { - // FIXME: use span_suggestion instead, highlighting the - // whole block tail expression. - let msg = if info.tail_result_is_ignored { - "The temporary is part of an expression at the end of a block. \ - Consider adding semicolon after the expression so its temporaries \ - are dropped sooner, before the local variables declared by the \ - block are dropped." + if info.tail_result_is_ignored { + err.span_suggestion_verbose( + info.span.shrink_to_hi(), + "consider adding semicolon after the expression so its \ + temporaries are dropped sooner, before the local variables \ + declared by the block are dropped", + ";".to_string(), + Applicability::MaybeIncorrect, + ); } else { - "The temporary is part of an expression at the end of a block. \ - Consider forcing this temporary to be dropped sooner, before \ - the block's local variables are dropped. \ - For example, you could save the expression's value in a new \ - local variable `x` and then make `x` be the expression \ - at the end of the block." + err.note( + "the temporary is part of an expression at the end of a \ + block;\nconsider forcing this temporary to be dropped sooner, \ + before the block's local variables are dropped", + ); + err.multipart_suggestion( + "for example, you could save the expression's value in a new \ + local variable `x` and then make `x` be the expression at the \ + end of the block", + vec![ + (info.span.shrink_to_lo(), "let x = ".to_string()), + (info.span.shrink_to_hi(), "; x".to_string()), + ], + Applicability::MaybeIncorrect, + ); }; - - err.note(msg); } } } @@ -208,48 +217,34 @@ impl BorrowExplanation { ); }; - self.add_lifetime_bound_suggestion_to_diagnostic( - tcx, - err, - &category, - span, - region_name, - ); + self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); } _ => {} } } - pub(in crate::borrow_check) fn add_lifetime_bound_suggestion_to_diagnostic<'tcx>( + pub(in crate::borrow_check) fn add_lifetime_bound_suggestion_to_diagnostic( &self, - tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>, category: &ConstraintCategory, span: Span, region_name: &RegionName, ) { - match category { - ConstraintCategory::OpaqueType => { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { - let suggestable_name = if region_name.was_named() { - region_name.to_string() - } else { - "'_".to_string() - }; + if let ConstraintCategory::OpaqueType = category { + let suggestable_name = + if region_name.was_named() { region_name.to_string() } else { "'_".to_string() }; + + let msg = format!( + "you can add a bound to the {}to make it last less than `'static` and match `{}`", + category.description(), + region_name, + ); - err.span_suggestion( - span, - &format!( - "you can add a bound to the {}to make it last less than \ - `'static` and match `{}`", - category.description(), - region_name, - ), - format!("{} + {}", snippet, suggestable_name), - Applicability::Unspecified, - ); - } - } - _ => {} + err.span_suggestion_verbose( + span.shrink_to_hi(), + &msg, + format!(" + {}", suggestable_name), + Applicability::Unspecified, + ); } } } @@ -289,7 +284,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, location: Location, borrow: &BorrowData<'tcx>, - kind_place: Option<(WriteKind, &Place<'tcx>)>, + kind_place: Option<(WriteKind, Place<'tcx>)>, ) -> BorrowExplanation { debug!( "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})", diff --git a/src/librustc_mir/borrow_check/diagnostics/find_use.rs b/src/librustc_mir/borrow_check/diagnostics/find_use.rs index ca4141d5fa51c..8d8cdfb52934c 100644 --- a/src/librustc_mir/borrow_check/diagnostics/find_use.rs +++ b/src/librustc_mir/borrow_check/diagnostics/find_use.rs @@ -2,14 +2,14 @@ use std::collections::VecDeque; use std::rc::Rc; use crate::borrow_check::{ + def_use::{self, DefUse}, nll::ToRegionVid, region_infer::{Cause, RegionInferenceContext}, }; -use crate::util::liveness::{self, DefUse}; -use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor}; -use rustc::mir::{Body, Local, Location}; -use rustc::ty::{RegionVid, TyCtxt}; use rustc_data_structures::fx::FxHashSet; +use rustc_middle::mir::visit::{MirVisitable, PlaceContext, Visitor}; +use rustc_middle::mir::{Body, Local, Location}; +use rustc_middle::ty::{RegionVid, TyCtxt}; crate fn find<'tcx>( body: &Body<'tcx>, @@ -117,7 +117,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for DefUseVisitor<'cx, 'tcx> { }); if found_it { - self.def_use_result = match liveness::categorize(context) { + self.def_use_result = match def_use::categorize(context) { Some(DefUse::Def) => Some(DefUseResult::Def), Some(DefUse::Use) => Some(DefUseResult::UseLive { local }), Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }), diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs index 7110a4a3058a6..c218e3906fff2 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mod.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs @@ -1,18 +1,18 @@ //! Borrow checker diagnostics. -use rustc::mir::{ - AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place, - PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, -}; -use rustc::ty::layout::VariantIdx; -use rustc::ty::print::Print; -use rustc::ty::{self, DefIdTree, Ty, TyCtxt}; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_hir::GeneratorKind; -use rustc_span::Span; +use rustc_middle::mir::{ + AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place, + PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, +}; +use rustc_middle::ty::print::Print; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_span::{symbol::sym, Span}; +use rustc_target::abi::VariantIdx; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -97,7 +97,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind { - let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap(); + let did = did.expect_local(); + let hir_id = self.infcx.tcx.hir().as_local_hir_id(did); if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id) @@ -119,7 +120,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Check if we are just moving a closure after it has been invoked. if let Some(target) = target { if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind { - let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap(); + let did = did.expect_local(); + let hir_id = self.infcx.tcx.hir().as_local_hir_id(did); if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id) @@ -137,8 +139,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - /// End-user visible description of `place` if one can be found. If the - /// place is a temporary for instance, None will be returned. + /// End-user visible description of `place` if one can be found. + /// If the place is a temporary for instance, `"value"` will be returned. + pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String { + match self.describe_place(place_ref) { + Some(mut descr) => { + // Surround descr with `backticks`. + descr.reserve(2); + descr.insert_str(0, "`"); + descr.push_str("`"); + descr + } + None => "value".to_string(), + } + } + + /// End-user visible description of `place` if one can be found. + /// If the place is a temporary for instance, None will be returned. pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option { self.describe_place_with_options(place_ref, IncludingDowncast(false)) } @@ -185,7 +202,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.body.local_decls[local].is_ref_to_static() => { let local_info = &self.body.local_decls[local].local_info; - if let LocalInfo::StaticRef { def_id, .. } = *local_info { + if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { buf.push_str(&self.infcx.tcx.item_name(def_id).as_str()); } else { unreachable!(); @@ -316,8 +333,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ProjectionElem::Downcast(_, variant_index) => { let base_ty = - Place::ty_from(place.local, place.projection, *self.body, self.infcx.tcx) - .ty; + Place::ty_from(place.local, place.projection, self.body, self.infcx.tcx).ty; self.describe_field_from_ty(&base_ty, field, Some(*variant_index)) } ProjectionElem::Field(_, field_type) => { @@ -434,7 +450,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }) = bbd.terminator { if let Some(source) = - BorrowedContentSource::from_call(func.ty(*self.body, tcx), tcx) + BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx) { return source; } @@ -447,7 +463,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If we didn't find an overloaded deref or index, then assume it's a // built in deref and check the type of the base. - let base_ty = Place::ty_from(deref_base.local, deref_base.projection, *self.body, tcx).ty; + let base_ty = Place::ty_from(deref_base.local, deref_base.projection, self.body, tcx).ty; if base_ty.is_unsafe_ptr() { BorrowedContentSource::DerefRawPointer } else if base_ty.is_mutable_ptr() { @@ -469,9 +485,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // this by hooking into the pretty printer and telling it to label the // lifetimes without names with the value `'0`. match ty.kind { - ty::Ref(ty::RegionKind::ReLateBound(_, br), _, _) - | ty::Ref( - ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), + ty::Ref( + ty::RegionKind::ReLateBound(_, br) + | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), _, _, ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter), @@ -617,20 +633,20 @@ pub(super) enum BorrowedContentSource<'tcx> { } impl BorrowedContentSource<'tcx> { - pub(super) fn describe_for_unnamed_place(&self) -> String { + pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String { match *self { BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(), BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(), BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(), - BorrowedContentSource::OverloadedDeref(ty) => { - if ty.is_rc() { + BorrowedContentSource::OverloadedDeref(ty) => match ty.kind { + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { "an `Rc`".to_string() - } else if ty.is_arc() { + } + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { "an `Arc`".to_string() - } else { - format!("dereference of `{}`", ty) } - } + _ => format!("dereference of `{}`", ty), + }, BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty), } } @@ -647,22 +663,22 @@ impl BorrowedContentSource<'tcx> { } } - pub(super) fn describe_for_immutable_place(&self) -> String { + pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String { match *self { BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(), BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(), BorrowedContentSource::DerefMutableRef => { bug!("describe_for_immutable_place: DerefMutableRef isn't immutable") } - BorrowedContentSource::OverloadedDeref(ty) => { - if ty.is_rc() { + BorrowedContentSource::OverloadedDeref(ty) => match ty.kind { + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { "an `Rc`".to_string() - } else if ty.is_arc() { + } + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { "an `Arc`".to_string() - } else { - format!("a dereference of `{}`", ty) } - } + _ => format!("a dereference of `{}`", ty), + }, BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty), } } @@ -789,7 +805,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "closure_span: def_id={:?} target_place={:?} places={:?}", def_id, target_place, places ); - let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?; + let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id.as_local()?); let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { diff --git a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs index 83bc9849cafe9..67254811ec52a 100644 --- a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs @@ -1,6 +1,6 @@ -use rustc::mir::*; -use rustc::ty; use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_middle::mir::*; +use rustc_middle::ty; use rustc_span::source_map::DesugaringKind; use rustc_span::{Span, Symbol}; @@ -103,21 +103,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // // opt_match_place is None for let [mut] x = ... statements, // whether or not the right-hand side is a place expression - if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( VarBindingForm { - opt_match_place: Some((ref opt_match_place, match_span)), + opt_match_place: Some((opt_match_place, match_span)), binding_mode: _, opt_ty_info: _, pat_span: _, }, - ))) = local_decl.local_info + )))) = local_decl.local_info { let stmt_source_info = self.body.source_info(location); self.append_binding_error( grouped_errors, kind, original_path, - move_from, + *move_from, local, opt_match_place, match_span, @@ -143,16 +143,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { grouped_errors: &mut Vec>, kind: IllegalMoveOriginKind<'tcx>, original_path: Place<'tcx>, - move_from: &Place<'tcx>, + move_from: Place<'tcx>, bind_to: Local, - match_place: &Option>, + match_place: Option>, match_span: Span, statement_span: Span, ) { debug!("append_binding_error(match_place={:?}, match_span={:?})", match_place, match_span); let from_simple_let = match_place.is_none(); - let match_place = match_place.as_ref().unwrap_or(move_from); + let match_place = match_place.unwrap_or(move_from); match self.move_data.rev_lookup.find(match_place.as_ref()) { // Error with the match place @@ -178,7 +178,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; grouped_errors.push(GroupedMoveError::MovesFromPlace { span, - move_from: *match_place, + move_from, original_path, kind, binds_to, @@ -223,14 +223,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let (span, use_spans, original_path, kind): ( Span, Option, - &Place<'tcx>, + Place<'tcx>, &IllegalMoveOriginKind<'_>, ) = match error { - GroupedMoveError::MovesFromPlace { span, ref original_path, ref kind, .. } - | GroupedMoveError::MovesFromValue { span, ref original_path, ref kind, .. } => { + GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. } + | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => { (span, None, original_path, kind) } - GroupedMoveError::OtherIllegalMove { use_spans, ref original_path, ref kind } => { + GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => { (use_spans.args_or_use(), Some(use_spans), original_path, kind) } }; @@ -247,7 +247,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { IllegalMoveOriginKind::BorrowedContent { target_place } => self .report_cannot_move_from_borrowed_content( original_path, - target_place, + *target_place, span, use_spans, ), @@ -268,18 +268,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn report_cannot_move_from_static( &mut self, - place: &Place<'tcx>, + place: Place<'tcx>, span: Span, ) -> DiagnosticBuilder<'a> { let description = if place.projection.len() == 1 { - format!("static item `{}`", self.describe_place(place.as_ref()).unwrap()) + format!("static item {}", self.describe_any_place(place.as_ref())) } else { let base_static = PlaceRef { local: place.local, projection: &[ProjectionElem::Deref] }; format!( - "`{:?}` as `{:?}` is a static item", - self.describe_place(place.as_ref()).unwrap(), - self.describe_place(base_static).unwrap(), + "{} as {} is a static item", + self.describe_any_place(place.as_ref()), + self.describe_any_place(base_static), ) }; @@ -288,15 +288,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn report_cannot_move_from_borrowed_content( &mut self, - move_place: &Place<'tcx>, - deref_target_place: &Place<'tcx>, + move_place: Place<'tcx>, + deref_target_place: Place<'tcx>, span: Span, use_spans: Option, ) -> DiagnosticBuilder<'a> { // Inspect the type of the content behind the // borrow to provide feedback about why this // was a move rather than a copy. - let ty = deref_target_place.ty(*self.body, self.infcx.tcx).ty; + let ty = deref_target_place.ty(self.body, self.infcx.tcx).ty; let upvar_field = self .prefixes(move_place.as_ref(), PrefixSet::All) .find_map(|p| self.is_upvar_field_projection(p)); @@ -349,16 +349,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let upvar_name = upvar.name; let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id); - let place_name = self.describe_place(move_place.as_ref()).unwrap(); + let place_name = self.describe_any_place(move_place.as_ref()); - let place_description = if self - .is_upvar_field_projection(move_place.as_ref()) - .is_some() - { - format!("`{}`, a {}", place_name, capture_description) - } else { - format!("`{}`, as `{}` is a {}", place_name, upvar_name, capture_description,) - }; + let place_description = + if self.is_upvar_field_projection(move_place.as_ref()).is_some() { + format!("{}, a {}", place_name, capture_description) + } else { + format!("{}, as `{}` is a {}", place_name, upvar_name, capture_description) + }; debug!( "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}", @@ -379,12 +377,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { span, &format!("`{}` which is behind a {}", place_desc, source_desc), ), - (_, _) => self.cannot_move_out_of(span, &source.describe_for_unnamed_place()), + (_, _) => self.cannot_move_out_of( + span, + &source.describe_for_unnamed_place(self.infcx.tcx), + ), } } }; if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) { - let def_id = match move_place.ty(*self.body, self.infcx.tcx).ty.kind { + let def_id = match move_place.ty(self.body, self.infcx.tcx).ty.kind { ty::Adt(self_def, _) => self_def.did, ty::Foreign(def_id) | ty::FnDef(def_id, _) @@ -440,7 +441,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } if binds_to.is_empty() { - let place_ty = move_from.ty(*self.body, self.infcx.tcx).ty; + let place_ty = move_from.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(move_from.as_ref()) { Some(desc) => format!("`{}`", desc), None => "value".to_string(), @@ -463,7 +464,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // No binding. Nothing to suggest. GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { let span = use_spans.var_or_use(); - let place_ty = original_path.ty(*self.body, self.infcx.tcx).ty; + let place_ty = original_path.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(original_path.as_ref()) { Some(desc) => format!("`{}`", desc), None => "value".to_string(), @@ -481,10 +482,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut suggestions: Vec<(Span, &str, String)> = Vec::new(); for local in binds_to { let bind_to = &self.body.local_decls[*local]; - if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - pat_span, - .. - }))) = bind_to.local_info + if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { pat_span, .. }, + )))) = bind_to.local_info { if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span) { diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs index ee654431d8892..402eac47c462b 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs @@ -1,9 +1,9 @@ -use rustc::mir::{self, ClearCrossCrate, Local, LocalInfo, Location}; -use rustc::mir::{Mutability, Place, PlaceRef, ProjectionElem}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::Node; use rustc_index::vec::Idx; +use rustc_middle::mir::{self, ClearCrossCrate, Local, LocalInfo, Location}; +use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::kw; use rustc_span::Span; @@ -22,7 +22,7 @@ pub(crate) enum AccessKind { impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { pub(crate) fn report_mutability_error( &mut self, - access_place: &Place<'tcx>, + access_place: Place<'tcx>, span: Span, the_place_err: PlaceRef<'tcx>, error_access: AccessKind, @@ -58,7 +58,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], } => { debug_assert!(is_closure_or_generator( - Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty + Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); item_msg = format!("`{}`", access_place_desc.unwrap()); @@ -85,7 +85,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } else { item_msg = format!("`{}`", access_place_desc.unwrap()); let local_info = &self.body.local_decls[local].local_info; - if let LocalInfo::StaticRef { def_id, .. } = *local_info { + if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { let static_name = &self.infcx.tcx.item_name(def_id); reason = format!(", as `{}` is an immutable static item", static_name); } else { @@ -104,7 +104,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Place::ty_from( the_place_err.local, the_place_err.projection, - *self.body, + self.body, self.infcx.tcx ) .ty @@ -120,7 +120,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { local: the_place_err.local, projection: proj_base, }); - let pointer_type = source.describe_for_immutable_place(); + let pointer_type = source.describe_for_immutable_place(self.infcx.tcx); opt_source = Some(source); if let Some(desc) = access_place_desc { item_msg = format!("`{}`", desc); @@ -137,12 +137,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - PlaceRef { local: _, projection: [.., ProjectionElem::Index(_)] } - | PlaceRef { local: _, projection: [.., ProjectionElem::ConstantIndex { .. }] } - | PlaceRef { local: _, projection: [.., ProjectionElem::Subslice { .. }] } - | PlaceRef { local: _, projection: [.., ProjectionElem::Downcast(..)] } => { - bug!("Unexpected immutable place.") - } + PlaceRef { + local: _, + projection: + [.., ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..)], + } => bug!("Unexpected immutable place."), } debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason); @@ -169,9 +171,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { borrow_spans.var_span_label( &mut err, format!( - "mutable borrow occurs due to use of `{}` in closure", - // always Some() if the message is printed. - self.describe_place(access_place.as_ref()).unwrap_or_default(), + "mutable borrow occurs due to use of {} in closure", + self.describe_any_place(access_place.as_ref()), ), ); borrow_span @@ -196,7 +197,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some((span, message)) = annotate_struct_field( self.infcx.tcx, - Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty, + Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty, field, ) { err.span_suggestion( @@ -215,9 +216,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .local_decls .get(local) .map(|local_decl| { - if let LocalInfo::User(ClearCrossCrate::Set( + if let Some(box LocalInfo::User(ClearCrossCrate::Set( mir::BindingForm::ImplicitSelf(kind), - )) = local_decl.local_info + ))) = local_decl.local_info { // Check if the user variable is a `&mut self` and we can therefore // suggest removing the `&mut`. @@ -272,7 +273,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], } => { debug_assert!(is_closure_or_generator( - Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty + Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); err.span_label(span, format!("cannot {ACT}", ACT = act)); @@ -339,8 +340,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { match self.local_names[local] { Some(name) if !local_decl.from_compiler_desugaring() => { - let label = match local_decl.local_info { - LocalInfo::User(ClearCrossCrate::Set( + let label = match local_decl.local_info.as_ref().unwrap() { + box LocalInfo::User(ClearCrossCrate::Set( mir::BindingForm::ImplicitSelf(_), )) => { let (span, suggestion) = @@ -348,7 +349,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some((true, span, suggestion)) } - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( + box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( mir::VarBindingForm { binding_mode: ty::BindingMode::BindByValue(_), opt_ty_info, @@ -380,14 +381,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.infcx.tcx, local_decl, opt_assignment_rhs_span, - opt_ty_info, + *opt_ty_info, ); Some((true, span, suggestion)) } } } - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( + box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( mir::VarBindingForm { binding_mode: ty::BindingMode::BindByReference(_), .. @@ -398,7 +399,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .map(|replacement| (true, pattern_span, replacement)) } - LocalInfo::User(ClearCrossCrate::Clear) => { + box LocalInfo::User(ClearCrossCrate::Clear) => { bug!("saw cleared local state") } @@ -491,69 +492,67 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.span_label(sp, format!("cannot {}", act)); let hir = self.infcx.tcx.hir(); - let closure_id = hir.as_local_hir_id(self.mir_def_id).unwrap(); + let closure_id = hir.as_local_hir_id(self.mir_def_id.expect_local()); let fn_call_id = hir.get_parent_node(closure_id); let node = hir.get(fn_call_id); let item_id = hir.get_parent_item(fn_call_id); let mut look_at_return = true; // If we can detect the expression to be an `fn` call where the closure was an argument, // we point at the `fn` definition argument... - match node { - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) => { - let arg_pos = args - .iter() - .enumerate() - .filter(|(_, arg)| arg.span == self.body.span) - .map(|(pos, _)| pos) - .next(); - let def_id = hir.local_def_id(item_id); - let tables = self.infcx.tcx.typeck_tables_of(def_id); - if let Some(ty::FnDef(def_id, _)) = - tables.node_type_opt(func.hir_id).as_ref().map(|ty| &ty.kind) - { - let arg = match hir.get_if_local(*def_id) { - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(sig, ..), - .. - })) - | Some(hir::Node::TraitItem(hir::TraitItem { + if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node { + let arg_pos = args + .iter() + .enumerate() + .filter(|(_, arg)| arg.span == self.body.span) + .map(|(pos, _)| pos) + .next(); + let def_id = hir.local_def_id(item_id); + let tables = self.infcx.tcx.typeck_tables_of(def_id); + if let Some(ty::FnDef(def_id, _)) = + tables.node_type_opt(func.hir_id).as_ref().map(|ty| &ty.kind) + { + let arg = match hir.get_if_local(*def_id) { + Some( + hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Fn(sig, ..), .. + }) + | hir::Node::TraitItem(hir::TraitItem { ident, kind: hir::TraitItemKind::Fn(sig, _), .. - })) - | Some(hir::Node::ImplItem(hir::ImplItem { + }) + | hir::Node::ImplItem(hir::ImplItem { ident, kind: hir::ImplItemKind::Fn(sig, _), .. - })) => Some( - arg_pos - .and_then(|pos| { - sig.decl.inputs.get( - pos + if sig.decl.implicit_self.has_implicit_self() { - 1 - } else { - 0 - }, - ) - }) - .map(|arg| arg.span) - .unwrap_or(ident.span), - ), - _ => None, - }; - if let Some(span) = arg { - err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); - err.span_label(func.span, "expects `Fn` instead of `FnMut`"); - if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) { - err.span_label(self.body.span, "in this closure"); - } - look_at_return = false; + }), + ) => Some( + arg_pos + .and_then(|pos| { + sig.decl.inputs.get( + pos + if sig.decl.implicit_self.has_implicit_self() { + 1 + } else { + 0 + }, + ) + }) + .map(|arg| arg.span) + .unwrap_or(ident.span), + ), + _ => None, + }; + if let Some(span) = arg { + err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); + err.span_label(func.span, "expects `Fn` instead of `FnMut`"); + if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) { + err.span_label(self.body.span, "in this closure"); } + look_at_return = false; } } - _ => {} } + if look_at_return && hir.get_return_block(closure_id).is_some() { // ...otherwise we are probably in the tail expression of the function, point at the // return type. @@ -692,7 +691,7 @@ fn annotate_struct_field( if let ty::Adt(def, _) = ty.kind { let field = def.all_fields().nth(field.index())?; // Use the HIR types to construct the diagnostic message. - let hir_id = tcx.hir().as_local_hir_id(field.did)?; + let hir_id = tcx.hir().as_local_hir_id(field.did.as_local()?); let node = tcx.hir().find(hir_id)?; // Now we're dealing with the actual struct that we're going to suggest a change to, // we can expect a field that is an immutable reference to a type. diff --git a/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs b/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs index ee9489078bdb9..dd970d800fba0 100644 --- a/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs +++ b/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs @@ -4,9 +4,9 @@ use std::collections::BTreeMap; use log::debug; -use rustc::ty::RegionVid; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagnosticBuilder; +use rustc_middle::ty::RegionVid; use smallvec::SmallVec; diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs index 93800d2a2b38d..98c0542f9c0dc 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -1,12 +1,12 @@ //! Error reporting machinery for lifetime errors. -use rustc::mir::ConstraintCategory; -use rustc::ty::{self, RegionVid, Ty}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_infer::infer::{ error_reporting::nice_region_error::NiceRegionError, error_reporting::unexpected_hidden_region_diagnostic, NLLRegionVariableOrigin, }; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_span::symbol::kw; use rustc_span::Span; diff --git a/src/librustc_mir/borrow_check/diagnostics/region_name.rs b/src/librustc_mir/borrow_check/diagnostics/region_name.rs index 6756f476f6155..37e2e0475048d 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_name.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_name.rs @@ -1,11 +1,11 @@ use std::fmt::{self, Display}; -use rustc::ty::print::RegionHighlightMode; -use rustc::ty::subst::{GenericArgKind, SubstsRef}; -use rustc::ty::{self, RegionVid, Ty}; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; +use rustc_middle::ty::print::RegionHighlightMode; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_span::symbol::kw; use rustc_span::{symbol::Symbol, Span, DUMMY_SP}; @@ -148,7 +148,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// /// This function would create a label like this: /// - /// ``` + /// ```text /// | fn foo(x: &u32) { .. } /// ------- fully elaborated type of `x` is `&'1 u32` /// ``` @@ -237,12 +237,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } ty::BoundRegion::BrEnv => { - let mir_hir_id = self - .infcx - .tcx - .hir() - .as_local_hir_id(self.mir_def_id) - .expect("non-local mir"); + let mir_hir_id = + self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id.expect_local()); let def_ty = self.regioncx.universal_regions().defining_ty; if let DefiningTy::Closure(_, substs) = def_ty { @@ -300,7 +296,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// elaborated type, returning something like `'1`. Result looks /// like: /// - /// ``` + /// ```text /// | fn foo(x: &u32) { .. } /// ------- fully elaborated type of `x` is `&'1 u32` /// ``` @@ -328,7 +324,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { argument_ty: Ty<'tcx>, argument_index: usize, ) -> Option { - let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id)?; + let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id.as_local()?); let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?; let argument_hir_ty: &hir::Ty<'_> = fn_decl.inputs.get(argument_index)?; match argument_hir_ty.kind { @@ -347,7 +343,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// that has no type annotation. /// For example, we might produce an annotation like this: /// - /// ``` + /// ```text /// | foo(|a, b| b) /// | - - /// | | | @@ -396,7 +392,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// that contains the anonymous reference we want to give a name /// to. For example, we might produce an annotation like this: /// - /// ``` + /// ```text /// | fn a(items: &[T]) -> Box> { /// | - let's call the lifetime of this reference `'1` /// ``` @@ -577,9 +573,12 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // to search anything here. } - (GenericArgKind::Lifetime(_), _) - | (GenericArgKind::Type(_), _) - | (GenericArgKind::Const(_), _) => { + ( + GenericArgKind::Lifetime(_) + | GenericArgKind::Type(_) + | GenericArgKind::Const(_), + _, + ) => { // I *think* that HIR lowering should ensure this // doesn't happen, even in erroneous // programs. Else we should use delay-span-bug. @@ -600,7 +599,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// fully elaborated type, returning something like `'1`. Result /// looks like: /// - /// ``` + /// ```text /// | let x = Some(&22); /// - fully elaborated type of `x` is `Option<&'1 u32>` /// ``` @@ -636,7 +635,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_type_name(&return_ty, Some(highlight)).0; - let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id).expect("non-local mir"); + let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id.expect_local()); let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) { hir::Node::Expr(hir::Expr { @@ -688,7 +687,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_type_name(&yield_ty, Some(highlight)).0; - let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id).expect("non-local mir"); + let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id.expect_local()); let yield_span = match tcx.hir().get(mir_hir_id) { hir::Node::Expr(hir::Expr { diff --git a/src/librustc_mir/borrow_check/diagnostics/var_name.rs b/src/librustc_mir/borrow_check/diagnostics/var_name.rs index c4933bedc2271..a850b85e9bbae 100644 --- a/src/librustc_mir/borrow_check/diagnostics/var_name.rs +++ b/src/librustc_mir/borrow_check/diagnostics/var_name.rs @@ -1,8 +1,8 @@ use crate::borrow_check::Upvar; use crate::borrow_check::{nll::ToRegionVid, region_infer::RegionInferenceContext}; -use rustc::mir::{Body, Local}; -use rustc::ty::{RegionVid, TyCtxt}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::{Body, Local}; +use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; diff --git a/src/librustc_mir/borrow_check/facts.rs b/src/librustc_mir/borrow_check/facts.rs index cd8139b17b48d..6d6b94ecf6440 100644 --- a/src/librustc_mir/borrow_check/facts.rs +++ b/src/librustc_mir/borrow_check/facts.rs @@ -2,9 +2,9 @@ use crate::borrow_check::location::{LocationIndex, LocationTable}; use crate::dataflow::indexes::{BorrowIndex, MovePathIndex}; use polonius_engine::AllFacts as PoloniusFacts; use polonius_engine::Atom; -use rustc::mir::Local; -use rustc::ty::{RegionVid, TyCtxt}; use rustc_index::vec::Idx; +use rustc_middle::mir::Local; +use rustc_middle::ty::{RegionVid, TyCtxt}; use std::error::Error; use std::fmt::Debug; use std::fs::{self, File}; diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/src/librustc_mir/borrow_check/invalidation.rs index d33639aa9d9a0..178e3db17cd32 100644 --- a/src/librustc_mir/borrow_check/invalidation.rs +++ b/src/librustc_mir/borrow_check/invalidation.rs @@ -1,10 +1,10 @@ -use rustc::mir::visit::Visitor; -use rustc::mir::TerminatorKind; -use rustc::mir::{BasicBlock, Body, Location, Place, ReadOnlyBodyAndCache, Rvalue}; -use rustc::mir::{BorrowKind, Mutability, Operand}; -use rustc::mir::{Statement, StatementKind}; -use rustc::ty::TyCtxt; use rustc_data_structures::graph::dominators::Dominators; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{BasicBlock, Body, Location, Place, Rvalue}; +use rustc_middle::mir::{BorrowKind, Mutability, Operand}; +use rustc_middle::mir::{InlineAsmOperand, TerminatorKind}; +use rustc_middle::mir::{Statement, StatementKind}; +use rustc_middle::ty::TyCtxt; use crate::dataflow::indexes::BorrowIndex; @@ -18,7 +18,7 @@ pub(super) fn generate_invalidates<'tcx>( tcx: TyCtxt<'tcx>, all_facts: &mut Option, location_table: &LocationTable, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, ) { if all_facts.is_none() { @@ -56,33 +56,33 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { self.check_activations(location); - match statement.kind { - StatementKind::Assign(box (ref lhs, ref rhs)) => { + match &statement.kind { + StatementKind::Assign(box (lhs, rhs)) => { self.consume_rvalue(location, rhs); - self.mutate_place(location, lhs, Shallow(None), JustWrite); + self.mutate_place(location, *lhs, Shallow(None), JustWrite); } StatementKind::FakeRead(_, _) => { // Only relevant for initialized/liveness/safety checks. } - StatementKind::SetDiscriminant { ref place, variant_index: _ } => { - self.mutate_place(location, place, Shallow(None), JustWrite); + StatementKind::SetDiscriminant { place, variant_index: _ } => { + self.mutate_place(location, **place, Shallow(None), JustWrite); } - StatementKind::InlineAsm(ref asm) => { + StatementKind::LlvmInlineAsm(asm) => { for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) { if o.is_indirect { // FIXME(eddyb) indirect inline asm outputs should // be encoded through MIR place derefs instead. self.access_place( location, - output, + *output, (Deep, Read(ReadKind::Copy)), LocalMutationIsAllowed::No, ); } else { self.mutate_place( location, - output, + *output, if o.is_rw { Deep } else { Shallow(None) }, if o.is_rw { WriteAndRead } else { JustWrite }, ); @@ -102,7 +102,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { StatementKind::StorageDead(local) => { self.access_place( location, - &Place::from(local), + Place::from(*local), (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, ); @@ -119,27 +119,27 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { self.consume_operand(location, discr); } - TerminatorKind::Drop { location: ref drop_place, target: _, unwind: _ } => { + TerminatorKind::Drop { location: drop_place, target: _, unwind: _ } => { self.access_place( location, - drop_place, + *drop_place, (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, ); } TerminatorKind::DropAndReplace { - location: ref drop_place, + location: drop_place, value: ref new_value, target: _, unwind: _, } => { - self.mutate_place(location, drop_place, Deep, JustWrite); + self.mutate_place(location, *drop_place, Deep, JustWrite); self.consume_operand(location, new_value); } TerminatorKind::Call { ref func, ref args, - ref destination, + destination, cleanup: _, from_hir_call: _, } => { @@ -147,13 +147,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { for arg in args { self.consume_operand(location, arg); } - if let Some((ref dest, _ /*bb*/)) = *destination { - self.mutate_place(location, dest, Deep, JustWrite); + if let Some((dest, _ /*bb*/)) = destination { + self.mutate_place(location, *dest, Deep, JustWrite); } } TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { self.consume_operand(location, cond); - use rustc::mir::AssertKind; + use rustc_middle::mir::AssertKind; if let AssertKind::BoundsCheck { ref len, ref index } = *msg { self.consume_operand(location, len); self.consume_operand(location, index); @@ -166,23 +166,46 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { let borrow_set = self.borrow_set.clone(); let resume = self.location_table.start_index(resume.start_location()); for i in borrow_set.borrows.indices() { - if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) { + if borrow_of_local_data(borrow_set.borrows[i].borrowed_place) { self.all_facts.invalidates.push((resume, i)); } } - self.mutate_place(location, resume_arg, Deep, JustWrite); + self.mutate_place(location, *resume_arg, Deep, JustWrite); } TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { // Invalidate all borrows of local places let borrow_set = self.borrow_set.clone(); let start = self.location_table.start_index(location); for i in borrow_set.borrows.indices() { - if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) { + if borrow_of_local_data(borrow_set.borrows[i].borrowed_place) { self.all_facts.invalidates.push((start, i)); } } } + TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + for op in operands { + match *op { + InlineAsmOperand::In { reg: _, ref value } + | InlineAsmOperand::Const { ref value } => { + self.consume_operand(location, value); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let Some(place) = place { + self.mutate_place(location, place, Shallow(None), JustWrite); + } + } + InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { + self.consume_operand(location, in_value); + if let Some(out_place) = out_place { + self.mutate_place(location, out_place, Shallow(None), JustWrite); + } + } + InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { value: _ } => {} + } + } + } TerminatorKind::Goto { target: _ } | TerminatorKind::Abort | TerminatorKind::Unreachable @@ -201,7 +224,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { fn mutate_place( &mut self, location: Location, - place: &Place<'tcx>, + place: Place<'tcx>, kind: AccessDepth, _mode: MutateMode, ) { @@ -216,7 +239,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { /// Simulates consumption of an operand. fn consume_operand(&mut self, location: Location, operand: &Operand<'tcx>) { match *operand { - Operand::Copy(ref place) => { + Operand::Copy(place) => { self.access_place( location, place, @@ -224,7 +247,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { LocalMutationIsAllowed::No, ); } - Operand::Move(ref place) => { + Operand::Move(place) => { self.access_place( location, place, @@ -239,7 +262,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { // Simulates consumption of an rvalue fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) { match *rvalue { - Rvalue::Ref(_ /*rgn*/, bk, ref place) => { + Rvalue::Ref(_ /*rgn*/, bk, place) => { let access_kind = match bk { BorrowKind::Shallow => { (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk))) @@ -258,7 +281,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } - Rvalue::AddressOf(mutability, ref place) => { + Rvalue::AddressOf(mutability, place) => { let access_kind = match mutability { Mutability::Mut => ( Deep, @@ -279,7 +302,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { self.consume_operand(location, operand) } - Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => { + Rvalue::Len(place) | Rvalue::Discriminant(place) => { let af = match *rvalue { Rvalue::Len(..) => Some(ArtificialField::ArrayLength), Rvalue::Discriminant(..) => None, @@ -313,7 +336,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { fn access_place( &mut self, location: Location, - place: &Place<'tcx>, + place: Place<'tcx>, kind: (AccessDepth, ReadOrWrite), _is_local_mutation_allowed: LocalMutationIsAllowed, ) { @@ -325,7 +348,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { fn check_access_for_conflict( &mut self, location: Location, - place: &Place<'tcx>, + place: Place<'tcx>, sd: AccessDepth, rw: ReadOrWrite, ) { @@ -359,14 +382,15 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { // have already taken the reservation } - (Read(_), BorrowKind::Shallow) - | (Read(_), BorrowKind::Shared) - | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Unique) - | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => { + (Read(_), BorrowKind::Shallow | BorrowKind::Shared) + | ( + Read(ReadKind::Borrow(BorrowKind::Shallow)), + BorrowKind::Unique | BorrowKind::Mut { .. }, + ) => { // Reads don't invalidate shared or shallow borrows } - (Read(_), BorrowKind::Unique) | (Read(_), BorrowKind::Mut { .. }) => { + (Read(_), BorrowKind::Unique | BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(&this.dominators, borrow, location) { // If the borrow isn't active yet, reads don't invalidate it @@ -379,7 +403,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { this.generate_invalidates(borrow_index, location); } - (Reservation(_), _) | (Activation(_, _), _) | (Write(_), _) => { + (Reservation(_) | Activation(_, _) | Write(_), _) => { // unique or mutable borrows are invalidated by writes. // Reservations count as writes since we need to check // that activating the borrow will be OK @@ -413,7 +437,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { self.access_place( location, - &borrow.borrowed_place, + borrow.borrowed_place, (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)), LocalMutationIsAllowed::No, ); diff --git a/src/librustc_mir/borrow_check/location.rs b/src/librustc_mir/borrow_check/location.rs index 7bc746b181d81..375ff72679f8f 100644 --- a/src/librustc_mir/borrow_check/location.rs +++ b/src/librustc_mir/borrow_check/location.rs @@ -1,5 +1,5 @@ -use rustc::mir::{BasicBlock, Body, Location}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::{BasicBlock, Body, Location}; /// Maps between a MIR Location, which identifies a particular /// statement within a basic block, to a "rich location", which diff --git a/src/librustc_mir/borrow_check/member_constraints.rs b/src/librustc_mir/borrow_check/member_constraints.rs index 4323e2db84476..d4baa5d809a22 100644 --- a/src/librustc_mir/borrow_check/member_constraints.rs +++ b/src/librustc_mir/borrow_check/member_constraints.rs @@ -1,8 +1,8 @@ -use crate::rustc::ty::{self, Ty}; -use rustc::infer::MemberConstraint; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; +use rustc_middle::infer::MemberConstraint; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use std::hash::Hash; use std::ops::Index; @@ -71,7 +71,7 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { /// Pushes a member constraint into the set. /// /// The input member constraint `m_c` is in the form produced by - /// the the `rustc::infer` code. + /// the the `rustc_middle::infer` code. /// /// The `to_region_vid` callback fn is used to convert the regions /// within into `RegionVid` format -- it typically consults the diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 6c1901455fda9..a0c1d96bb4743 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1,25 +1,27 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. -use rustc::mir::{ - read_only, traversal, Body, BodyAndCache, ClearCrossCrate, Local, Location, Mutability, - Operand, Place, PlaceElem, PlaceRef, ReadOnlyBodyAndCache, -}; -use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; -use rustc::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; -use rustc::mir::{Terminator, TerminatorKind}; -use rustc::ty::query::Providers; -use rustc::ty::{self, RegionVid, TyCtxt}; -use rustc_ast::ast::Name; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::dominators::Dominators; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; -use rustc_hir::{def_id::DefId, HirId, Node}; +use rustc_hir::{ + def_id::{DefId, LocalDefId}, + HirId, Node, +}; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::mir::{ + traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, + PlaceRef, +}; +use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; +use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; +use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{Span, Symbol, DUMMY_SP}; use either::Either; use smallvec::SmallVec; @@ -29,13 +31,13 @@ use std::mem; use std::rc::Rc; use crate::dataflow; -use crate::dataflow::generic::{Analysis, BorrowckFlowState as Flows, BorrowckResults}; +use crate::dataflow::impls::{ + Borrows, EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, +}; use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex}; use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError}; -use crate::dataflow::Borrows; -use crate::dataflow::EverInitializedPlaces; use crate::dataflow::MoveDataParamEnv; -use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; +use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults}; use crate::transform::MirSource; use self::diagnostics::{AccessKind, RegionName}; @@ -48,6 +50,7 @@ use self::path_utils::*; mod borrow_set; mod constraint_generation; mod constraints; +mod def_use; mod diagnostics; mod facts; mod invalidation; @@ -73,7 +76,7 @@ crate use region_infer::RegionInferenceContext; // FIXME(eddyb) perhaps move this somewhere more centrally. #[derive(Debug)] crate struct Upvar { - name: Name, + name: Symbol, var_hir_id: HirId, @@ -89,9 +92,9 @@ pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { mir_borrowck, ..*providers }; } -fn mir_borrowck(tcx: TyCtxt<'_>, def_id: DefId) -> &BorrowCheckResult<'_> { +fn mir_borrowck(tcx: TyCtxt<'_>, def_id: LocalDefId) -> BorrowCheckResult<'_> { let (input_body, promoted) = tcx.mir_validated(def_id); - debug!("run query mir_borrowck: {}", tcx.def_path_str(def_id)); + debug!("run query mir_borrowck: {}", tcx.def_path_str(def_id.to_def_id())); let opt_closure_req = tcx.infer_ctxt().enter(|infcx| { let input_body: &Body<'_> = &input_body.borrow(); @@ -100,20 +103,20 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def_id: DefId) -> &BorrowCheckResult<'_> { }); debug!("mir_borrowck done"); - tcx.arena.alloc(opt_closure_req) + opt_closure_req } fn do_mir_borrowck<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, input_body: &Body<'tcx>, - input_promoted: &IndexVec>, - def_id: DefId, + input_promoted: &IndexVec>, + def_id: LocalDefId, ) -> BorrowCheckResult<'tcx> { debug!("do_mir_borrowck(def_id = {:?})", def_id); let tcx = infcx.tcx; let param_env = tcx.param_env(def_id); - let id = tcx.hir().as_local_hir_id(def_id).expect("do_mir_borrowck: non-local DefId"); + let id = tcx.hir().as_local_hir_id(def_id); let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); for var_debug_info in &input_body.var_debug_info { @@ -135,12 +138,12 @@ fn do_mir_borrowck<'a, 'tcx>( // Gather the upvars of a closure, if any. let tables = tcx.typeck_tables_of(def_id); - if tables.tainted_by_errors { + if let Some(ErrorReported) = tables.tainted_by_errors { infcx.set_tainted_by_errors(); } let upvars: Vec<_> = tables .upvar_list - .get(&def_id) + .get(&def_id.to_def_id()) .into_iter() .flat_map(|v| v.values()) .map(|upvar_id| { @@ -168,27 +171,28 @@ fn do_mir_borrowck<'a, 'tcx>( // requires first making our own copy of the MIR. This copy will // be modified (in place) to contain non-lexical lifetimes. It // will have a lifetime tied to the inference context. - let body_clone: Body<'tcx> = input_body.clone(); + let mut body = input_body.clone(); let mut promoted = input_promoted.clone(); - let mut body = BodyAndCache::new(body_clone); let free_regions = - nll::replace_regions_in_mir(infcx, def_id, param_env, &mut body, &mut promoted); - let body = read_only!(body); // no further changes - let promoted: IndexVec<_, _> = promoted.iter_mut().map(|body| read_only!(body)).collect(); + nll::replace_regions_in_mir(infcx, def_id.to_def_id(), param_env, &mut body, &mut promoted); + let body = &body; // no further changes let location_table = &LocationTable::new(&body); let mut errors_buffer = Vec::new(); - let (move_data, move_errors): (MoveData<'tcx>, Option, MoveError<'tcx>)>>) = + let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) = match MoveData::gather_moves(&body, tcx, param_env) { - Ok(move_data) => (move_data, None), - Err((move_data, move_errors)) => (move_data, Some(move_errors)), + Ok(move_data) => (move_data, Vec::new()), + Err((move_data, move_errors)) => (move_data, move_errors), }; + let promoted_errors = promoted + .iter_enumerated() + .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env))); let mdpe = MoveDataParamEnv { move_data, param_env }; let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def_id) + .into_engine(tcx, &body, def_id.to_def_id()) .iterate_to_fixpoint() .into_results_cursor(&body); @@ -218,14 +222,20 @@ fn do_mir_borrowck<'a, 'tcx>( // Dump MIR results into a file, if that is enabled. This let us // write unit-tests, as well as helping with debugging. - nll::dump_mir_results(infcx, MirSource::item(def_id), &body, ®ioncx, &opt_closure_req); + nll::dump_mir_results( + infcx, + MirSource::item(def_id.to_def_id()), + &body, + ®ioncx, + &opt_closure_req, + ); // We also have a `#[rustc_regions]` annotation that causes us to dump // information. nll::dump_annotation( infcx, &body, - def_id, + def_id.to_def_id(), ®ioncx, &opt_closure_req, &opaque_type_values, @@ -240,13 +250,13 @@ fn do_mir_borrowck<'a, 'tcx>( let regioncx = Rc::new(regioncx); let flow_borrows = Borrows::new(tcx, &body, regioncx.clone(), &borrow_set) - .into_engine(tcx, &body, def_id) + .into_engine(tcx, &body, def_id.to_def_id()) .iterate_to_fixpoint(); let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def_id) + .into_engine(tcx, &body, def_id.to_def_id()) .iterate_to_fixpoint(); let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def_id) + .into_engine(tcx, &body, def_id.to_def_id()) .iterate_to_fixpoint(); let movable_generator = match tcx.hir().get(id) { @@ -257,12 +267,47 @@ fn do_mir_borrowck<'a, 'tcx>( _ => true, }; + for (idx, move_data_results) in promoted_errors { + let promoted_body = &promoted[idx]; + let dominators = promoted_body.dominators(); + + if let Err((move_data, move_errors)) = move_data_results { + let mut promoted_mbcx = MirBorrowckCtxt { + infcx, + body: promoted_body, + mir_def_id: def_id.to_def_id(), + move_data: &move_data, + location_table: &LocationTable::new(promoted_body), + movable_generator, + locals_are_invalidated_at_exit, + access_place_error_reported: Default::default(), + reservation_error_reported: Default::default(), + reservation_warnings: Default::default(), + move_error_reported: BTreeMap::new(), + uninitialized_error_reported: Default::default(), + errors_buffer, + regioncx: regioncx.clone(), + used_mut: Default::default(), + used_mut_upvars: SmallVec::new(), + borrow_set: borrow_set.clone(), + dominators, + upvars: Vec::new(), + local_names: IndexVec::from_elem(None, &promoted_body.local_decls), + region_names: RefCell::default(), + next_region_name: RefCell::new(1), + polonius_output: None, + }; + promoted_mbcx.report_move_errors(move_errors); + errors_buffer = promoted_mbcx.errors_buffer; + }; + } + let dominators = body.dominators(); let mut mbcx = MirBorrowckCtxt { infcx, body, - mir_def_id: def_id, + mir_def_id: def_id.to_def_id(), move_data: &mdpe.move_data, location_table, movable_generator, @@ -294,13 +339,11 @@ fn do_mir_borrowck<'a, 'tcx>( borrows: flow_borrows, }; - if let Some(errors) = move_errors { - mbcx.report_move_errors(errors); - } + mbcx.report_move_errors(move_errors); - dataflow::generic::visit_results( - &*body, - traversal::reverse_postorder(&*body).map(|(bb, _)| bb), + dataflow::visit_results( + &body, + traversal::reverse_postorder(&body).map(|(bb, _)| bb), &results, &mut mbcx, ); @@ -308,8 +351,7 @@ fn do_mir_borrowck<'a, 'tcx>( // Convert any reservation warnings into lints. let reservation_warnings = mem::take(&mut mbcx.reservation_warnings); for (_, (place, span, location, bk, borrow)) in reservation_warnings { - let mut initial_diag = - mbcx.report_conflicting_borrow(location, (&place, span), bk, &borrow); + let mut initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow); let scope = mbcx.body.source_info(location).scope; let lint_root = match &mbcx.body.source_scopes[scope].local_data { @@ -416,7 +458,7 @@ fn do_mir_borrowck<'a, 'tcx>( crate struct MirBorrowckCtxt<'cx, 'tcx> { crate infcx: &'cx InferCtxt<'cx, 'tcx>, - body: ReadOnlyBodyAndCache<'cx, 'tcx>, + body: &'cx Body<'tcx>, mir_def_id: DefId, move_data: &'cx MoveData<'tcx>, @@ -491,7 +533,7 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> { upvars: Vec, /// Names of local (user) variables (extracted from `var_debug_info`). - local_names: IndexVec>, + local_names: IndexVec>, /// Record the region names generated for each region in the given /// MIR def so that we can reuse them later in help/error messages. @@ -509,10 +551,10 @@ crate struct MirBorrowckCtxt<'cx, '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<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> { +impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> { type FlowState = Flows<'cx, 'tcx>; - fn visit_statement( + fn visit_statement_before_primary_effect( &mut self, flow_state: &Flows<'cx, 'tcx>, stmt: &'cx Statement<'tcx>, @@ -523,11 +565,11 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt self.check_activations(location, span, flow_state); - match stmt.kind { - StatementKind::Assign(box (ref lhs, ref rhs)) => { + match &stmt.kind { + StatementKind::Assign(box (lhs, ref rhs)) => { self.consume_rvalue(location, (rhs, span), flow_state); - self.mutate_place(location, (lhs, span), Shallow(None), JustWrite, flow_state); + self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state); } StatementKind::FakeRead(_, box ref place) => { // Read for match doesn't access any memory and is used to @@ -547,17 +589,17 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt flow_state, ); } - StatementKind::SetDiscriminant { ref place, variant_index: _ } => { - self.mutate_place(location, (place, span), Shallow(None), JustWrite, flow_state); + StatementKind::SetDiscriminant { place, variant_index: _ } => { + self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state); } - StatementKind::InlineAsm(ref asm) => { + StatementKind::LlvmInlineAsm(ref asm) => { for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) { if o.is_indirect { // FIXME(eddyb) indirect inline asm outputs should // be encoded through MIR place derefs instead. self.access_place( location, - (output, o.span), + (*output, o.span), (Deep, Read(ReadKind::Copy)), LocalMutationIsAllowed::No, flow_state, @@ -571,7 +613,7 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt } else { self.mutate_place( location, - (output, o.span), + (*output, o.span), if o.is_rw { Deep } else { Shallow(None) }, if o.is_rw { WriteAndRead } else { JustWrite }, flow_state, @@ -592,7 +634,7 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt StatementKind::StorageDead(local) => { self.access_place( location, - (&Place::from(local), span), + (Place::from(*local), span), (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, flow_state, @@ -601,7 +643,7 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt } } - fn visit_terminator( + fn visit_terminator_before_primary_effect( &mut self, flow_state: &Flows<'cx, 'tcx>, term: &'cx Terminator<'tcx>, @@ -620,7 +662,7 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt let tcx = self.infcx.tcx; // Compute the type with accurate region information. - let drop_place_ty = drop_place.ty(*self.body, self.infcx.tcx); + let drop_place_ty = drop_place.ty(self.body, self.infcx.tcx); // Erase the regions. let drop_place_ty = self.infcx.tcx.erase_regions(&drop_place_ty).ty; @@ -638,14 +680,14 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt self.access_place( loc, - (drop_place, span), + (*drop_place, span), (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, flow_state, ); } TerminatorKind::DropAndReplace { - location: ref drop_place, + location: drop_place, value: ref new_value, target: _, unwind: _, @@ -664,24 +706,60 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt for arg in args { self.consume_operand(loc, (arg, span), flow_state); } - if let Some((ref dest, _ /*bb*/)) = *destination { + if let Some((dest, _ /*bb*/)) = *destination { self.mutate_place(loc, (dest, span), Deep, JustWrite, flow_state); } } TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { self.consume_operand(loc, (cond, span), flow_state); - use rustc::mir::AssertKind; + use rustc_middle::mir::AssertKind; if let AssertKind::BoundsCheck { ref len, ref index } = *msg { self.consume_operand(loc, (len, span), flow_state); self.consume_operand(loc, (index, span), flow_state); } } - TerminatorKind::Yield { ref value, resume: _, ref resume_arg, drop: _ } => { + TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => { self.consume_operand(loc, (value, span), flow_state); self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state); } + TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + for op in operands { + match *op { + InlineAsmOperand::In { reg: _, ref value } + | InlineAsmOperand::Const { ref value } => { + self.consume_operand(loc, (value, span), flow_state); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let Some(place) = place { + self.mutate_place( + loc, + (place, span), + Shallow(None), + JustWrite, + flow_state, + ); + } + } + InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { + self.consume_operand(loc, (in_value, span), flow_state); + if let Some(out_place) = out_place { + self.mutate_place( + loc, + (out_place, span), + Shallow(None), + JustWrite, + flow_state, + ); + } + } + InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { value: _ } => {} + } + } + } + TerminatorKind::Goto { target: _ } | TerminatorKind::Abort | TerminatorKind::Unreachable @@ -695,7 +773,7 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt } } - fn visit_terminator_exit( + fn visit_terminator_after_primary_effect( &mut self, flow_state: &Flows<'cx, 'tcx>, term: &'cx Terminator<'tcx>, @@ -736,7 +814,8 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } | TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Unreachable => {} + | TerminatorKind::Unreachable + | TerminatorKind::InlineAsm { .. } => {} } } } @@ -872,7 +951,7 @@ impl InitializationRequiringAction { impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn body(&self) -> &'cx Body<'tcx> { - *self.body + self.body } /// Checks an access to the given place to see if it is allowed. Examines the set of borrows @@ -884,7 +963,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn access_place( &mut self, location: Location, - place_span: (&Place<'tcx>, Span), + place_span: (Place<'tcx>, Span), kind: (AccessDepth, ReadOrWrite), is_local_mutation_allowed: LocalMutationIsAllowed, flow_state: &Flows<'cx, 'tcx>, @@ -905,7 +984,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Check is_empty() first because it's the common case, and doing that // way we avoid the clone() call. if !self.access_place_error_reported.is_empty() - && self.access_place_error_reported.contains(&(*place_span.0, place_span.1)) + && self.access_place_error_reported.contains(&(place_span.0, place_span.1)) { debug!( "access_place: suppressing error place_span=`{:?}` kind=`{:?}`", @@ -933,14 +1012,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if conflict_error || mutability_error { debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind); - self.access_place_error_reported.insert((*place_span.0, place_span.1)); + self.access_place_error_reported.insert((place_span.0, place_span.1)); } } fn check_access_for_conflict( &mut self, location: Location, - place_span: (&Place<'tcx>, Span), + place_span: (Place<'tcx>, Span), sd: AccessDepth, rw: ReadOrWrite, flow_state: &Flows<'cx, 'tcx>, @@ -953,7 +1032,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut error_reported = false; let tcx = self.infcx.tcx; let body = self.body; - let body: &Body<'_> = &body; let borrow_set = self.borrow_set.clone(); // Use polonius output if it has been enabled. @@ -992,19 +1070,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Control::Continue } - (Read(_), BorrowKind::Shared) - | (Read(_), BorrowKind::Shallow) - | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Unique) - | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => { - Control::Continue - } + (Read(_), BorrowKind::Shared | BorrowKind::Shallow) + | ( + Read(ReadKind::Borrow(BorrowKind::Shallow)), + BorrowKind::Unique | BorrowKind::Mut { .. }, + ) => Control::Continue, (Write(WriteKind::Move), BorrowKind::Shallow) => { // Handled by initialization checks. Control::Continue } - (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => { + (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(&this.dominators, borrow, location) { assert!(allow_two_phase_borrow(borrow.kind)); @@ -1025,12 +1102,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Control::Break } - (Reservation(WriteKind::MutableBorrow(bk)), BorrowKind::Shallow) - | (Reservation(WriteKind::MutableBorrow(bk)), BorrowKind::Shared) - if { - tcx.migrate_borrowck() - && this.borrow_set.location_map.contains_key(&location) - } => + ( + Reservation(WriteKind::MutableBorrow(bk)), + BorrowKind::Shallow | BorrowKind::Shared, + ) if { + tcx.migrate_borrowck() && this.borrow_set.location_map.contains_key(&location) + } => { let bi = this.borrow_set.location_map[&location]; debug!( @@ -1043,13 +1120,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // these sepately so that we only emit a warning if borrow // checking was otherwise successful. this.reservation_warnings - .insert(bi, (*place_span.0, place_span.1, location, bk, borrow.clone())); + .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone())); // Don't suppress actual errors. Control::Continue } - (Reservation(kind), _) | (Activation(kind, _), _) | (Write(kind), _) => { + (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { match rw { Reservation(..) => { debug!( @@ -1057,7 +1134,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place: {:?}", place_span.0 ); - this.reservation_error_reported.insert(place_span.0.clone()); + this.reservation_error_reported.insert(place_span.0); } Activation(_, activating) => { debug!( @@ -1100,7 +1177,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn mutate_place( &mut self, location: Location, - place_span: (&'cx Place<'tcx>, Span), + place_span: (Place<'tcx>, Span), kind: AccessDepth, mode: MutateMode, flow_state: &Flows<'cx, 'tcx>, @@ -1150,7 +1227,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { flow_state: &Flows<'cx, 'tcx>, ) { match *rvalue { - Rvalue::Ref(_ /*rgn*/, bk, ref place) => { + Rvalue::Ref(_ /*rgn*/, bk, place) => { let access_kind = match bk { BorrowKind::Shallow => { (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk))) @@ -1188,7 +1265,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - Rvalue::AddressOf(mutability, ref place) => { + Rvalue::AddressOf(mutability, place) => { let access_kind = match mutability { Mutability::Mut => ( Deep, @@ -1222,7 +1299,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.consume_operand(location, (operand, span), flow_state) } - Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => { + Rvalue::Len(place) | Rvalue::Discriminant(place) => { let af = match *rvalue { Rvalue::Len(..) => Some(ArtificialField::ArrayLength), Rvalue::Discriminant(..) => None, @@ -1264,7 +1341,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { match **aggregate_kind { AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => { let BorrowCheckResult { used_mut_upvars, .. } = - self.infcx.tcx.mir_borrowck(def_id); + self.infcx.tcx.mir_borrowck(def_id.expect_local()); debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars); for field in used_mut_upvars { self.propagate_closure_used_mut_upvar(&operands[field.index()]); @@ -1283,7 +1360,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) { - let propagate_closure_used_mut_place = |this: &mut Self, place: &Place<'tcx>| { + let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| { if !place.projection.is_empty() { if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { this.used_mut_upvars.push(field); @@ -1297,7 +1374,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // captures of a closure are copied/moved directly // when generating MIR. match *operand { - Operand::Move(ref place) | Operand::Copy(ref place) => { + Operand::Move(place) | Operand::Copy(place) => { match place.as_local() { Some(local) if !self.body.local_decls[local].is_user_variable() => { if self.body.local_decls[local].ty.is_mutable_ptr() { @@ -1336,8 +1413,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let stmt = &bbd.statements[loc.statement_index]; debug!("temporary assigned in: stmt={:?}", stmt); - if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, ref source))) = - stmt.kind + if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind { propagate_closure_used_mut_place(self, source); } else { @@ -1361,7 +1437,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { flow_state: &Flows<'cx, 'tcx>, ) { match *operand { - Operand::Copy(ref place) => { + Operand::Copy(place) => { // copy of place: check if this is "copy of frozen path" // (FIXME: see check_loans.rs) self.access_place( @@ -1380,7 +1456,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { flow_state, ); } - Operand::Move(ref place) => { + Operand::Move(place) => { // move of place: check if this is move of already borrowed path self.access_place( location, @@ -1411,7 +1487,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { span: Span, ) { debug!("check_for_invalidation_at_exit({:?})", borrow); - let place = &borrow.borrowed_place; + let place = borrow.borrowed_place; let mut root_place = PlaceRef { local: place.local, projection: &[] }; // FIXME(nll-rfc#40): do more precise destructor tracking here. For now @@ -1465,7 +1541,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) { debug!("check_for_local_borrow({:?})", borrow); - if borrow_of_local_data(&borrow.borrowed_place) { + if borrow_of_local_data(borrow.borrowed_place) { let err = self.cannot_borrow_across_generator_yield( self.retrieve_borrow_spans(borrow).var_or_use(), yield_span, @@ -1491,7 +1567,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.access_place( location, - (&borrow.borrowed_place, span), + (borrow.borrowed_place, span), (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)), LocalMutationIsAllowed::No, flow_state, @@ -1506,7 +1582,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &mut self, location: Location, local: Local, - place_span: (&Place<'tcx>, Span), + place_span: (Place<'tcx>, Span), flow_state: &Flows<'cx, 'tcx>, ) { debug!("check_if_reassignment_to_immutable_state({:?})", local); @@ -1730,7 +1806,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn check_if_assigned_path_is_moved( &mut self, location: Location, - (place, span): (&'cx Place<'tcx>, Span), + (place, span): (Place<'tcx>, Span), flow_state: &Flows<'cx, 'tcx>, ) { debug!("check_if_assigned_path_is_moved place: {:?}", place); @@ -1903,7 +1979,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// Returns `true` if an error is reported. fn check_access_permissions( &mut self, - (place, span): (&Place<'tcx>, Span), + (place, span): (Place<'tcx>, Span), kind: ReadOrWrite, is_local_mutation_allowed: LocalMutationIsAllowed, flow_state: &Flows<'cx, 'tcx>, @@ -1918,10 +1994,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let the_place_err; match kind { - Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Unique)) - | Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Mut { .. })) - | Write(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Unique)) - | Write(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Mut { .. })) => { + Reservation(WriteKind::MutableBorrow( + borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }), + )) + | Write(WriteKind::MutableBorrow( + borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }), + )) => { let is_local_mutation_allowed = match borrow_kind { BorrowKind::Unique => LocalMutationIsAllowed::Yes, BorrowKind::Mut { .. } => is_local_mutation_allowed, @@ -1951,14 +2029,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - Reservation(WriteKind::Move) - | Write(WriteKind::Move) - | Reservation(WriteKind::StorageDeadOrDrop) - | Reservation(WriteKind::MutableBorrow(BorrowKind::Shared)) - | Reservation(WriteKind::MutableBorrow(BorrowKind::Shallow)) - | Write(WriteKind::StorageDeadOrDrop) - | Write(WriteKind::MutableBorrow(BorrowKind::Shared)) - | Write(WriteKind::MutableBorrow(BorrowKind::Shallow)) => { + Reservation( + WriteKind::Move + | WriteKind::StorageDeadOrDrop + | WriteKind::MutableBorrow(BorrowKind::Shared) + | WriteKind::MutableBorrow(BorrowKind::Shallow), + ) + | Write( + WriteKind::Move + | WriteKind::StorageDeadOrDrop + | WriteKind::MutableBorrow(BorrowKind::Shared) + | WriteKind::MutableBorrow(BorrowKind::Shallow), + ) => { if let (Err(_), true) = ( self.is_mutable(place.as_ref(), is_local_mutation_allowed), self.errors_buffer.is_empty(), @@ -1982,11 +2064,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // permission checks are done at Reservation point. return false; } - Read(ReadKind::Borrow(BorrowKind::Unique)) - | Read(ReadKind::Borrow(BorrowKind::Mut { .. })) - | Read(ReadKind::Borrow(BorrowKind::Shared)) - | Read(ReadKind::Borrow(BorrowKind::Shallow)) - | Read(ReadKind::Copy) => { + Read( + ReadKind::Borrow( + BorrowKind::Unique + | BorrowKind::Mut { .. } + | BorrowKind::Shared + | BorrowKind::Shallow, + ) + | ReadKind::Copy, + ) => { // Access authorized return false; } @@ -2154,10 +2240,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { upvar, is_local_mutation_allowed, place ); match (upvar.mutability, is_local_mutation_allowed) { - (Mutability::Not, LocalMutationIsAllowed::No) - | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => { - Err(place) - } + ( + Mutability::Not, + LocalMutationIsAllowed::No + | LocalMutationIsAllowed::ExceptUpvars, + ) => Err(place), (Mutability::Not, LocalMutationIsAllowed::Yes) | (Mutability::Mut, _) => { // Subtle: this is an upvar diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index 077ed49ed2cac..b820b79c47fe8 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/src/librustc_mir/borrow_check/nll.rs @@ -1,15 +1,15 @@ //! The entry point of the NLL borrow checker. -use rustc::mir::{ - BasicBlock, Body, BodyAndCache, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, - Location, Promoted, ReadOnlyBodyAndCache, -}; -use rustc::ty::{self, RegionKind, RegionVid}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Diagnostic; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; +use rustc_middle::mir::{ + BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, + Promoted, +}; +use rustc_middle::ty::{self, RegionKind, RegionVid}; use rustc_span::symbol::sym; use std::env; use std::fmt::Debug; @@ -21,9 +21,9 @@ use std::str::FromStr; use self::mir_util::PassWhere; use polonius_engine::{Algorithm, Output}; -use crate::dataflow::generic::ResultsCursor; +use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::move_paths::{InitKind, InitLocation, MoveData}; -use crate::dataflow::MaybeInitializedPlaces; +use crate::dataflow::ResultsCursor; use crate::transform::MirSource; use crate::util as mir_util; use crate::util::pretty; @@ -60,13 +60,13 @@ pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, def_id: DefId, param_env: ty::ParamEnv<'tcx>, - body: &mut BodyAndCache<'tcx>, - promoted: &mut IndexVec>, + body: &mut Body<'tcx>, + promoted: &mut IndexVec>, ) -> UniversalRegions<'tcx> { debug!("replace_regions_in_mir(def_id={:?})", def_id); // Compute named region information. This also renumbers the inputs/outputs. - let universal_regions = UniversalRegions::new(infcx, def_id, param_env); + let universal_regions = UniversalRegions::new(infcx, def_id.expect_local(), param_env); // Replace all remaining regions with fresh inference variables. renumber::renumber_mir(infcx, body, promoted); @@ -157,10 +157,10 @@ fn populate_polonius_move_facts( /// This may result in errors being reported. pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, - def_id: DefId, + def_id: LocalDefId, universal_regions: UniversalRegions<'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, - promoted: &IndexVec>, + body: &Body<'tcx>, + promoted: &IndexVec>, location_table: &LocationTable, param_env: ty::ParamEnv<'tcx>, flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, @@ -272,7 +272,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( // Dump facts if requested. let polonius_output = all_facts.and_then(|all_facts| { if infcx.tcx.sess.opts.debugging_opts.nll_facts { - let def_path = infcx.tcx.def_path(def_id); + let def_path = infcx.tcx.def_path(def_id.to_def_id()); let dir_path = PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate()); all_facts.write_to_dir(dir_path, location_table).unwrap(); @@ -292,7 +292,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( // Solve the region constraints. let (closure_region_requirements, nll_errors) = - regioncx.solve(infcx, &body, def_id, polonius_output.clone()); + regioncx.solve(infcx, &body, def_id.to_def_id(), polonius_output.clone()); if !nll_errors.is_empty() { // Suppress unhelpful extra errors in `infer_opaque_types`. @@ -317,7 +317,7 @@ pub(super) fn dump_mir_results<'a, 'tcx>( regioncx: &RegionInferenceContext<'_>, closure_region_requirements: &Option>, ) { - if !mir_util::dump_enabled(infcx.tcx, "nll", source) { + if !mir_util::dump_enabled(infcx.tcx, "nll", source.def_id()) { return; } diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index deec6f386ffb3..f5238e7b7bedc 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -2,10 +2,10 @@ use crate::borrow_check::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation} use crate::borrow_check::places_conflict; use crate::borrow_check::AccessDepth; use crate::dataflow::indexes::BorrowIndex; -use rustc::mir::BorrowKind; -use rustc::mir::{BasicBlock, Body, Location, Place}; -use rustc::ty::TyCtxt; use rustc_data_structures::graph::dominators::Dominators; +use rustc_middle::mir::BorrowKind; +use rustc_middle::mir::{BasicBlock, Body, Location, Place}; +use rustc_middle::ty::TyCtxt; /// Returns `true` if the borrow represented by `kind` is /// allowed to be split into separate Reservation and @@ -27,7 +27,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, _location: Location, - access_place: (AccessDepth, &Place<'tcx>), + access_place: (AccessDepth, Place<'tcx>), borrow_set: &BorrowSet<'tcx>, candidates: I, mut op: F, @@ -48,7 +48,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( if places_conflict::borrow_conflicts_with_place( tcx, body, - &borrowed.borrowed_place, + borrowed.borrowed_place, borrowed.kind, place.as_ref(), access, @@ -130,7 +130,7 @@ pub(super) fn is_active<'tcx>( /// Determines if a given borrow is borrowing local data /// This is called for all Yield expressions on movable generators -pub(super) fn borrow_of_local_data(place: &Place<'_>) -> bool { +pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool { // Reborrow of already borrowed data is ignored // Any errors will be caught on the initial borrow !place.is_indirect() diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index 01c44d0d905b6..e53f50326d3d2 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -1,8 +1,8 @@ use crate::borrow_check::borrow_set::LocalsStateAtExit; -use rustc::mir::ProjectionElem; -use rustc::mir::{Body, Mutability, Place}; -use rustc::ty::{self, TyCtxt}; use rustc_hir as hir; +use rustc_middle::mir::ProjectionElem; +use rustc_middle::mir::{Body, Mutability, Place}; +use rustc_middle::ty::{self, TyCtxt}; /// Extension methods for the `Place` type. crate trait PlaceExt<'tcx> { diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 767ffa50fedb4..45f77af4aba40 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -1,9 +1,9 @@ use crate::borrow_check::ArtificialField; use crate::borrow_check::Overlap; use crate::borrow_check::{AccessDepth, Deep, Shallow}; -use rustc::mir::{Body, BorrowKind, Local, Place, PlaceElem, PlaceRef, ProjectionElem}; -use rustc::ty::{self, TyCtxt}; use rustc_hir as hir; +use rustc_middle::mir::{Body, BorrowKind, Local, Place, PlaceElem, PlaceRef, ProjectionElem}; +use rustc_middle::ty::{self, TyCtxt}; use std::cmp::max; /// When checking if a place conflicts with another place, this enum is used to influence decisions @@ -24,8 +24,8 @@ crate enum PlaceConflictBias { crate fn places_conflict<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - borrow_place: &Place<'tcx>, - access_place: &Place<'tcx>, + borrow_place: Place<'tcx>, + access_place: Place<'tcx>, bias: PlaceConflictBias, ) -> bool { borrow_conflicts_with_place( @@ -46,7 +46,7 @@ crate fn places_conflict<'tcx>( pub(super) fn borrow_conflicts_with_place<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - borrow_place: &Place<'tcx>, + borrow_place: Place<'tcx>, borrow_kind: BorrowKind, access_place: PlaceRef<'tcx>, access: AccessDepth, @@ -71,7 +71,7 @@ pub(super) fn borrow_conflicts_with_place<'tcx>( fn place_components_conflict<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - borrow_place: &Place<'tcx>, + borrow_place: Place<'tcx>, borrow_kind: BorrowKind, access_place: PlaceRef<'tcx>, access: AccessDepth, @@ -377,11 +377,16 @@ fn place_projection_conflict<'tcx>( Overlap::Disjoint } } - (ProjectionElem::Index(..), ProjectionElem::Index(..)) - | (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) - | (ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) - | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) - | (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => { + ( + ProjectionElem::Index(..), + ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. }, + ) + | ( + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. }, + ProjectionElem::Index(..), + ) => { // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint // (if the indexes differ) or equal (if they are the same). match bias { @@ -519,12 +524,15 @@ fn place_projection_conflict<'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-SLICE-SUBSLICES"); Overlap::EqualOrDisjoint } - (ProjectionElem::Deref, _) - | (ProjectionElem::Field(..), _) - | (ProjectionElem::Index(..), _) - | (ProjectionElem::ConstantIndex { .. }, _) - | (ProjectionElem::Subslice { .. }, _) - | (ProjectionElem::Downcast(..), _) => bug!( + ( + ProjectionElem::Deref + | ProjectionElem::Field(..) + | ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..), + _, + ) => bug!( "mismatched projections in place_element_conflict: {:?} and {:?}", pi1_elem, pi2_elem diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs index c64e8c363af54..a2475e0ff29fe 100644 --- a/src/librustc_mir/borrow_check/prefixes.rs +++ b/src/librustc_mir/borrow_check/prefixes.rs @@ -9,9 +9,9 @@ use super::MirBorrowckCtxt; -use rustc::mir::{Place, PlaceRef, ProjectionElem, ReadOnlyBodyAndCache}; -use rustc::ty::{self, TyCtxt}; use rustc_hir as hir; +use rustc_middle::mir::{Body, Place, PlaceRef, ProjectionElem}; +use rustc_middle::ty::{self, TyCtxt}; pub trait IsPrefixOf<'tcx> { fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool; @@ -26,7 +26,7 @@ impl<'tcx> IsPrefixOf<'tcx> for PlaceRef<'tcx> { } pub(super) struct Prefixes<'cx, 'tcx> { - body: ReadOnlyBodyAndCache<'cx, 'tcx>, + body: &'cx Body<'tcx>, tcx: TyCtxt<'tcx>, kind: PrefixSet, next: Option>, @@ -120,7 +120,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // derefs, except we stop at the deref of a shared // reference. - let ty = Place::ty_from(cursor.local, proj_base, *self.body, self.tcx).ty; + let ty = Place::ty_from(cursor.local, proj_base, self.body, self.tcx).ty; match ty.kind { ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => { // don't continue traversing over derefs of raw pointers or shared diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index c8b0e59ebb117..fe11384380086 100644 --- a/src/librustc_mir/borrow_check/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/region_infer/mod.rs @@ -1,21 +1,20 @@ use std::collections::VecDeque; use std::rc::Rc; -use rustc::mir::{ - Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - ConstraintCategory, Local, Location, -}; -use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::binary_search_util; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::scc::Sccs; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::canonical::QueryOutlivesConstraint; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; +use rustc_middle::mir::{ + Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, + ConstraintCategory, Local, Location, +}; +use rustc_middle::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; use crate::borrow_check::{ @@ -202,7 +201,7 @@ pub(crate) enum Cause { /// /// For more information about this translation, see /// `InferCtxt::process_registered_region_obligations` and -/// `InferCtxt::type_must_outlive` in `rustc::infer::outlives`. +/// `InferCtxt::type_must_outlive` in `rustc_infer::infer::InferCtxt`. #[derive(Clone, Debug)] pub struct TypeTest<'tcx> { /// The type `T` that must outlive the region. @@ -315,16 +314,81 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// SCC could have as well. This implies that the SCC must have /// the minimum, or narrowest, universe. fn compute_scc_universes( - constraints_scc: &Sccs, + constraint_sccs: &Sccs, definitions: &IndexVec>, ) -> IndexVec { - let num_sccs = constraints_scc.num_sccs(); + let num_sccs = constraint_sccs.num_sccs(); let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs); + debug!("compute_scc_universes()"); + + // For each region R in universe U, ensure that the universe for the SCC + // that contains R is "no bigger" than U. This effectively sets the universe + // for each SCC to be the minimum of the regions within. for (region_vid, region_definition) in definitions.iter_enumerated() { - let scc = constraints_scc.scc(region_vid); + let scc = constraint_sccs.scc(region_vid); let scc_universe = &mut scc_universes[scc]; - *scc_universe = ::std::cmp::min(*scc_universe, region_definition.universe); + let scc_min = std::cmp::min(region_definition.universe, *scc_universe); + if scc_min != *scc_universe { + *scc_universe = scc_min; + debug!( + "compute_scc_universes: lowered universe of {scc:?} to {scc_min:?} \ + because it contains {region_vid:?} in {region_universe:?}", + scc = scc, + scc_min = scc_min, + region_vid = region_vid, + region_universe = region_definition.universe, + ); + } + } + + // Walk each SCC `A` and `B` such that `A: B` + // and ensure that universe(A) can see universe(B). + // + // This serves to enforce the 'empty/placeholder' hierarchy + // (described in more detail on `RegionKind`): + // + // ``` + // static -----+ + // | | + // empty(U0) placeholder(U1) + // | / + // empty(U1) + // ``` + // + // In particular, imagine we have variables R0 in U0 and R1 + // created in U1, and constraints like this; + // + // ``` + // R1: !1 // R1 outlives the placeholder in U1 + // R1: R0 // R1 outlives R0 + // ``` + // + // Here, we wish for R1 to be `'static`, because it + // cannot outlive `placeholder(U1)` and `empty(U0)` any other way. + // + // Thanks to this loop, what happens is that the `R1: R0` + // constraint lowers the universe of `R1` to `U0`, which in turn + // means that the `R1: !1` constraint will (later) cause + // `R1` to become `'static`. + for scc_a in constraint_sccs.all_sccs() { + for &scc_b in constraint_sccs.successors(scc_a) { + let scc_universe_a = scc_universes[scc_a]; + let scc_universe_b = scc_universes[scc_b]; + let scc_universe_min = std::cmp::min(scc_universe_a, scc_universe_b); + if scc_universe_a != scc_universe_min { + scc_universes[scc_a] = scc_universe_min; + + debug!( + "compute_scc_universes: lowered universe of {scc_a:?} to {scc_universe_min:?} \ + because {scc_a:?}: {scc_b:?} and {scc_b:?} is in universe {scc_universe_b:?}", + scc_a = scc_a, + scc_b = scc_b, + scc_universe_min = scc_universe_min, + scc_universe_b = scc_universe_b + ); + } + } } debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes); @@ -416,7 +480,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - NLLRegionVariableOrigin::Existential { .. } => { + NLLRegionVariableOrigin::RootEmptyRegion + | NLLRegionVariableOrigin::Existential { .. } => { // For existential, regions, nothing to do. } } @@ -495,7 +560,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // to store those. Otherwise, we'll pass in `None` to the // functions below, which will trigger them to report errors // eagerly. - let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(|| vec![]); + let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(Vec::new); self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer); @@ -550,9 +615,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // SCC. For each SCC, we visit its successors and compute // their values, then we union all those values to get our // own. - let visited = &mut BitSet::new_empty(self.constraint_sccs.num_sccs()); - for scc_index in self.constraint_sccs.all_sccs() { - self.propagate_constraint_sccs_if_new(scc_index, visited); + let constraint_sccs = self.constraint_sccs.clone(); + for scc in constraint_sccs.all_sccs() { + self.compute_value_for_scc(scc); } // Sort the applied member constraints so we can binary search @@ -560,37 +625,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc); } - /// Computes the value of the SCC `scc_a` if it has not already - /// been computed. The `visited` parameter is a bitset - #[inline] - fn propagate_constraint_sccs_if_new( - &mut self, - scc_a: ConstraintSccIndex, - visited: &mut BitSet, - ) { - if visited.insert(scc_a) { - self.propagate_constraint_sccs_new(scc_a, visited); - } - } - /// Computes the value of the SCC `scc_a`, which has not yet been - /// computed. This works by first computing all successors of the - /// SCC (if they haven't been computed already) and then unioning - /// together their elements. - fn propagate_constraint_sccs_new( - &mut self, - scc_a: ConstraintSccIndex, - visited: &mut BitSet, - ) { + /// computed, by unioning the values of its successors. + /// Assumes that all successors have been computed already + /// (which is assured by iterating over SCCs in dependency order). + fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) { let constraint_sccs = self.constraint_sccs.clone(); // Walk each SCC `B` such that `A: B`... for &scc_b in constraint_sccs.successors(scc_a) { debug!("propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", scc_a, scc_b); - // ...compute the value of `B`... - self.propagate_constraint_sccs_if_new(scc_b, visited); - // ...and add elements from `B` into `A`. One complication // arises because of universes: If `B` contains something // that `A` cannot name, then `A` can only contain `B` if @@ -995,15 +1040,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.definitions[upper_bound].external_name.unwrap_or(r) } else { // In the case of a failure, use a `ReVar` result. This will - // cause the `has_local_value` later on to return `None`. + // cause the `needs_infer` later on to return `None`. r } }); debug!("try_promote_type_test_subject: folded ty = {:?}", ty); - // `has_local_value` will only be true if we failed to promote some region. - if ty.has_local_value() { + // `needs_infer` will only be true if we failed to promote some region. + if ty.needs_infer() { return None; } @@ -1258,7 +1303,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_bound_universal_region(fr, placeholder, errors_buffer); } - NLLRegionVariableOrigin::Existential { .. } => { + NLLRegionVariableOrigin::RootEmptyRegion + | NLLRegionVariableOrigin::Existential { .. } => { // nothing to check here } } @@ -1360,7 +1406,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_bound_universal_region(fr, placeholder, errors_buffer); } - NLLRegionVariableOrigin::Existential { .. } => { + NLLRegionVariableOrigin::RootEmptyRegion + | NLLRegionVariableOrigin::Existential { .. } => { // nothing to check here } } @@ -1633,9 +1680,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { universe1.cannot_name(placeholder.universe) } - NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { .. } => { - false - } + NLLRegionVariableOrigin::RootEmptyRegion + | NLLRegionVariableOrigin::FreeRegion + | NLLRegionVariableOrigin::Existential { .. } => false, } } @@ -1773,6 +1820,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Finds some region R such that `fr1: R` and `R` is live at `elem`. crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); + debug!("find_sub_region_live_at: {:?} is in scc {:?}", fr1, self.constraint_sccs.scc(fr1)); + debug!( + "find_sub_region_live_at: {:?} is in universe {:?}", + fr1, + self.scc_universes[self.constraint_sccs.scc(fr1)] + ); self.find_constraint_paths_between_regions(fr1, |r| { // First look for some `r` such that `fr1: r` and `r` is live at `elem` debug!( @@ -1794,13 +1847,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { .or_else(|| { // If we fail to find THAT, it may be that `fr1` is a // placeholder that cannot "fit" into its SCC. In that - // case, there should be some `r` where `fr1: r`, both - // `fr1` and `r` are in the same SCC, and `fr1` is a + // case, there should be some `r` where `fr1: r` and `fr1` is a // placeholder that `r` cannot name. We can blame that // edge. + // + // Remember that if `R1: R2`, then the universe of R1 + // must be able to name the universe of R2, because R2 will + // be at least `'empty(Universe(R2))`, and `R1` must be at + // larger than that. self.find_constraint_paths_between_regions(fr1, |r| { - self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r) - && self.cannot_name_placeholder(r, fr1) + self.cannot_name_placeholder(r, fr1) }) }) .map(|(_path, r)| r) @@ -1815,11 +1871,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { RegionElement::PlaceholderRegion(error_placeholder) => self .definitions .iter_enumerated() - .filter_map(|(r, definition)| match definition.origin { + .find_map(|(r, definition)| match definition.origin { NLLRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), _ => None, }) - .next() .unwrap(), } } @@ -1945,7 +2000,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let blame_source = match from_region_origin { NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { from_forall: false } => true, - NLLRegionVariableOrigin::Placeholder(_) + NLLRegionVariableOrigin::RootEmptyRegion + | NLLRegionVariableOrigin::Placeholder(_) | NLLRegionVariableOrigin::Existential { from_forall: true } => false, }; diff --git a/src/librustc_mir/borrow_check/region_infer/opaque_types.rs b/src/librustc_mir/borrow_check/region_infer/opaque_types.rs index 49b4943732847..7e352bfba77bc 100644 --- a/src/librustc_mir/borrow_check/region_infer/opaque_types.rs +++ b/src/librustc_mir/borrow_check/region_infer/opaque_types.rs @@ -1,7 +1,7 @@ -use rustc::ty::{self, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::Span; use rustc_trait_selection::opaque_types::InferCtxtExt; diff --git a/src/librustc_mir/borrow_check/region_infer/reverse_sccs.rs b/src/librustc_mir/borrow_check/region_infer/reverse_sccs.rs index ff19ac5f21a36..5d345a6e63d6b 100644 --- a/src/librustc_mir/borrow_check/region_infer/reverse_sccs.rs +++ b/src/librustc_mir/borrow_check/region_infer/reverse_sccs.rs @@ -1,10 +1,10 @@ use crate::borrow_check::constraints::ConstraintSccIndex; use crate::borrow_check::RegionInferenceContext; use itertools::Itertools; -use rustc::ty::RegionVid; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_data_structures::graph::WithSuccessors; +use rustc_middle::ty::RegionVid; use std::ops::Range; use std::rc::Rc; diff --git a/src/librustc_mir/borrow_check/region_infer/values.rs b/src/librustc_mir/borrow_check/region_infer/values.rs index 675463cb1c1f9..6cd814962c613 100644 --- a/src/librustc_mir/borrow_check/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/region_infer/values.rs @@ -1,9 +1,9 @@ -use rustc::mir::{BasicBlock, Body, Location, ReadOnlyBodyAndCache}; -use rustc::ty::{self, RegionVid}; use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix}; use rustc_index::vec::Idx; use rustc_index::vec::IndexVec; +use rustc_middle::mir::{BasicBlock, Body, Location}; +use rustc_middle::ty::{self, RegionVid}; use std::fmt::Debug; use std::rc::Rc; @@ -80,7 +80,7 @@ impl RegionValueElements { /// Pushes all predecessors of `index` onto `stack`. crate fn push_predecessors( &self, - body: ReadOnlyBodyAndCache<'_, '_>, + body: &Body<'_>, index: PointIndex, stack: &mut Vec, ) { @@ -89,7 +89,7 @@ impl RegionValueElements { // If this is a basic block head, then the predecessors are // the terminators of other basic blocks stack.extend( - body.predecessors_for(block) + body.predecessors()[block] .iter() .map(|&pred_bb| body.terminator_loc(pred_bb)) .map(|pred_loc| self.point_from_location(pred_loc)), diff --git a/src/librustc_mir/borrow_check/renumber.rs b/src/librustc_mir/borrow_check/renumber.rs index a63d18c27f119..5956896881941 100644 --- a/src/librustc_mir/borrow_check/renumber.rs +++ b/src/librustc_mir/borrow_check/renumber.rs @@ -1,16 +1,16 @@ -use rustc::mir::visit::{MutVisitor, TyContext}; -use rustc::mir::{BodyAndCache, Location, PlaceElem, Promoted}; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; +use rustc_middle::mir::visit::{MutVisitor, TyContext}; +use rustc_middle::mir::{Body, Location, PlaceElem, Promoted}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; /// Replaces all free regions appearing in the MIR with fresh /// inference variables, returning the number of variables created. pub fn renumber_mir<'tcx>( infcx: &InferCtxt<'_, 'tcx>, - body: &mut BodyAndCache<'tcx>, - promoted: &mut IndexVec>, + body: &mut Body<'tcx>, + promoted: &mut IndexVec>, ) { debug!("renumber_mir()"); debug!("renumber_mir: body.arg_count={:?}", body.arg_count); @@ -64,7 +64,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'tcx> { debug!("visit_ty: ty={:?}", ty); } - fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option> { + fn process_projection_elem( + &mut self, + elem: &PlaceElem<'tcx>, + _: Location, + ) -> Option> { if let PlaceElem::Field(field, ty) = elem { let new_ty = self.renumber_regions(ty); diff --git a/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs index 576759c2a3574..711271a63fbff 100644 --- a/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs @@ -1,12 +1,12 @@ -use rustc::mir::ConstraintCategory; -use rustc::ty::subst::GenericArgKind; -use rustc::ty::{self, TyCtxt}; use rustc_infer::infer::canonical::QueryOutlivesConstraint; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::DUMMY_SP; use crate::borrow_check::{ @@ -160,10 +160,6 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) { - // FIXME -- this is not the fix I would prefer - if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a { - return; - } let b = self.to_region_vid(b); let a = self.to_region_vid(a); self.add_outlives(b, a); @@ -176,10 +172,6 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' a: ty::Region<'tcx>, bound: VerifyBound<'tcx>, ) { - // FIXME: I'd prefer if NLL had a notion of empty - if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a { - return; - } let type_test = self.verify_to_type_test(kind, a, bound); self.add_type_test(type_test); } diff --git a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs index 86951f93f0e7e..f97dff146450c 100644 --- a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs @@ -1,13 +1,13 @@ -use rustc::mir::ConstraintCategory; -use rustc::traits::query::OutlivesBound; -use rustc::ty::free_region_map::FreeRegionRelations; -use rustc::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::free_regions::FreeRegionRelations; use rustc_infer::infer::outlives; use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::traits::query::OutlivesBound; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; diff --git a/src/librustc_mir/borrow_check/type_check/input_output.rs b/src/librustc_mir/borrow_check/type_check/input_output.rs index f2194c77c8991..edd2dc3c2de55 100644 --- a/src/librustc_mir/borrow_check/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/type_check/input_output.rs @@ -7,9 +7,9 @@ //! `RETURN_PLACE` the MIR arguments) are always fully normalized (and //! contain revealed `impl Trait` values). -use rustc::mir::*; -use rustc::ty::Ty; use rustc_infer::infer::LateBoundRegionConversionTime; +use rustc_middle::mir::*; +use rustc_middle::ty::Ty; use rustc_index::vec::Idx; use rustc_span::Span; @@ -33,35 +33,37 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // // e.g., `|x: FxHashMap<_, &'static u32>| ...` let user_provided_sig; - if !self.tcx().is_closure(self.mir_def_id) { + if !self.tcx().is_closure(self.mir_def_id.to_def_id()) { user_provided_sig = None; } else { let typeck_tables = self.tcx().typeck_tables_of(self.mir_def_id); - user_provided_sig = match typeck_tables.user_provided_sigs.get(&self.mir_def_id) { - None => None, - Some(user_provided_poly_sig) => { - // Instantiate the canonicalized variables from - // user-provided signature (e.g., the `_` in the code - // above) with fresh variables. - let (poly_sig, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars( - body.span, - &user_provided_poly_sig, - ); - - // Replace the bound items in the fn sig with fresh - // variables, so that they represent the view from - // "inside" the closure. - Some( - self.infcx - .replace_bound_vars_with_fresh_vars( + user_provided_sig = + match typeck_tables.user_provided_sigs.get(&self.mir_def_id.to_def_id()) { + None => None, + Some(user_provided_poly_sig) => { + // Instantiate the canonicalized variables from + // user-provided signature (e.g., the `_` in the code + // above) with fresh variables. + let (poly_sig, _) = + self.infcx.instantiate_canonical_with_fresh_inference_vars( body.span, - LateBoundRegionConversionTime::FnCall, - &poly_sig, - ) - .0, - ) + &user_provided_poly_sig, + ); + + // Replace the bound items in the fn sig with fresh + // variables, so that they represent the view from + // "inside" the closure. + Some( + self.infcx + .replace_bound_vars_with_fresh_vars( + body.span, + LateBoundRegionConversionTime::FnCall, + &poly_sig, + ) + .0, + ) + } } - } }; debug!( @@ -120,7 +122,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Err(terr) = self.eq_opaque_type_and_type( mir_output_ty, normalized_output_ty, - self.mir_def_id, + self.mir_def_id.to_def_id(), Locations::All(output_span), ConstraintCategory::BoringNoLocation, ) { @@ -143,7 +145,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Err(err) = self.eq_opaque_type_and_type( mir_output_ty, user_provided_output_ty, - self.mir_def_id, + self.mir_def_id.to_def_id(), Locations::All(output_span), ConstraintCategory::BoringNoLocation, ) { diff --git a/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs b/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs index 8155aa0ee000a..995e3a60a0c76 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs @@ -1,10 +1,9 @@ -use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc::mir::{Local, Location, ReadOnlyBodyAndCache}; use rustc_data_structures::vec_linked_list as vll; use rustc_index::vec::IndexVec; +use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::{Body, Local, Location}; -use crate::util::liveness::{categorize, DefUse}; - +use crate::borrow_check::def_use::{self, DefUse}; use crate::borrow_check::region_infer::values::{PointIndex, RegionValueElements}; /// A map that cross references each local with the locations where it @@ -62,7 +61,7 @@ impl LocalUseMap { crate fn build( live_locals: &Vec, elements: &RegionValueElements, - body: ReadOnlyBodyAndCache<'_, '_>, + body: &Body<'_>, ) -> Self { let nones = IndexVec::from_elem_n(None, body.local_decls.len()); let mut local_use_map = LocalUseMap { @@ -81,7 +80,7 @@ impl LocalUseMap { live_locals.iter().for_each(|&local| locals_with_use_data[local] = true); LocalUseMapBuild { local_use_map: &mut local_use_map, elements, locals_with_use_data } - .visit_body(body); + .visit_body(&body); local_use_map } @@ -160,7 +159,7 @@ impl LocalUseMapBuild<'_> { impl Visitor<'tcx> for LocalUseMapBuild<'_> { fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) { if self.locals_with_use_data[local] { - match categorize(context) { + match def_use::categorize(context) { Some(DefUse::Def) => self.insert_def(local, location), Some(DefUse::Use) => self.insert_use(local, location), Some(DefUse::Drop) => self.insert_drop(local, location), diff --git a/src/librustc_mir/borrow_check/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/type_check/liveness/mod.rs index cdf962ee31a6e..bddcd34ed3e47 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/mod.rs @@ -1,11 +1,11 @@ -use rustc::mir::{Body, Local, ReadOnlyBodyAndCache}; -use rustc::ty::{RegionVid, TyCtxt}; use rustc_data_structures::fx::FxHashSet; +use rustc_middle::mir::{Body, Local}; +use rustc_middle::ty::{RegionVid, TyCtxt}; use std::rc::Rc; -use crate::dataflow::generic::ResultsCursor; +use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::move_paths::MoveData; -use crate::dataflow::MaybeInitializedPlaces; +use crate::dataflow::ResultsCursor; use crate::borrow_check::{ constraints::OutlivesConstraintSet, @@ -32,7 +32,7 @@ mod trace; /// performed before pub(super) fn generate<'mir, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, elements: &Rc, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, diff --git a/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs b/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs index 407e0628b6eb8..d285098c52ad2 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs @@ -1,10 +1,10 @@ +use crate::borrow_check::def_use::{self, DefUse}; use crate::borrow_check::location::{LocationIndex, LocationTable}; use crate::dataflow::indexes::MovePathIndex; use crate::dataflow::move_paths::{LookupResult, MoveData}; -use crate::util::liveness::{categorize, DefUse}; -use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; -use rustc::mir::{Local, Location, Place, ReadOnlyBodyAndCache}; -use rustc::ty::subst::GenericArg; +use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{Body, Local, Location, Place}; +use rustc_middle::ty::subst::GenericArg; use super::TypeChecker; @@ -43,7 +43,7 @@ impl UseFactsExtractor<'_> { fn insert_path_access(&mut self, path: MovePathIndex, location: Location) { debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location); - self.path_accessed_at_base.push((path, self.location_table.start_index(location))); + self.path_accessed_at_base.push((path, self.location_to_index(location))); } fn place_to_mpi(&self, place: &Place<'_>) -> Option { @@ -56,7 +56,7 @@ impl UseFactsExtractor<'_> { impl Visitor<'tcx> for UseFactsExtractor<'_> { fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) { - match categorize(context) { + match def_use::categorize(context) { Some(DefUse::Def) => self.insert_def(local, location), Some(DefUse::Use) => self.insert_use(local, location), Some(DefUse::Drop) => self.insert_drop_use(local, location), @@ -85,7 +85,7 @@ impl Visitor<'tcx> for UseFactsExtractor<'_> { pub(super) fn populate_access_facts( typeck: &mut TypeChecker<'_, 'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, location_table: &LocationTable, move_data: &MoveData<'_>, dropped_at: &mut Vec<(Local, Location)>, @@ -101,7 +101,7 @@ pub(super) fn populate_access_facts( location_table, move_data, }; - extractor.visit_body(body); + extractor.visit_body(&body); facts.var_dropped_at.extend( dropped_at.iter().map(|&(local, location)| (local, location_table.mid_index(location))), diff --git a/src/librustc_mir/borrow_check/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/type_check/liveness/trace.rs index 0c49ee44f9a50..f04736e04a053 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/trace.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/trace.rs @@ -1,17 +1,17 @@ -use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, ReadOnlyBodyAndCache}; -use rustc::ty::{Ty, TypeFoldable}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::bit_set::HybridBitSet; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; +use rustc_middle::ty::{Ty, TypeFoldable}; use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult; use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; use rustc_trait_selection::traits::query::type_op::TypeOp; use std::rc::Rc; -use crate::dataflow::generic::ResultsCursor; +use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::indexes::MovePathIndex; use crate::dataflow::move_paths::{HasMoveData, MoveData}; -use crate::dataflow::MaybeInitializedPlaces; +use crate::dataflow::ResultsCursor; use crate::borrow_check::{ region_infer::values::{self, PointIndex, RegionValueElements}, @@ -37,7 +37,7 @@ use crate::borrow_check::{ /// this respects `#[may_dangle]` annotations). pub(super) fn trace( typeck: &mut TypeChecker<'_, 'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, elements: &Rc, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, @@ -76,7 +76,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { elements: &'me RegionValueElements, /// MIR we are analyzing. - body: ReadOnlyBodyAndCache<'me, 'tcx>, + body: &'me Body<'tcx>, /// Mapping to/from the various indices used for initialization tracking. move_data: &'me MoveData<'tcx>, @@ -303,7 +303,7 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> { } let body = self.cx.body; - for &pred_block in body.predecessors_for(block).iter() { + for &pred_block in body.predecessors()[block].iter() { debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,); // Check whether the variable is (at least partially) @@ -408,7 +408,7 @@ impl 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(self.body.terminator_loc(block)); + self.flow_inits.seek_before_primary_effect(self.body.terminator_loc(block)); self.initialized_at_curr_loc(mpi) } @@ -418,7 +418,7 @@ impl 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(self.body.terminator_loc(block)); + self.flow_inits.seek_after_primary_effect(self.body.terminator_loc(block)); self.initialized_at_curr_loc(mpi) } diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index f94160cc08a19..08aa434010710 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -5,24 +5,12 @@ use std::{fmt, iter, mem}; use either::Either; -use rustc::mir::tcx::PlaceTy; -use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; -use rustc::mir::AssertKind; -use rustc::mir::*; -use rustc::ty::adjustment::PointerCast; -use rustc::ty::cast::CastTy; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::layout::VariantIdx; -use rustc::ty::subst::{GenericArgKind, Subst, SubstsRef, UserSubsts}; -use rustc::ty::{ - self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, RegionVid, ToPolyTraitRef, Ty, - TyCtxt, UserType, UserTypeAnnotationIndex, -}; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::{CoerceUnsizedTraitLangItem, CopyTraitLangItem, SizedTraitLangItem}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; @@ -30,7 +18,20 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::{ InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin, }; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::AssertKind; +use rustc_middle::mir::*; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::cast::CastTy; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef, UserSubsts}; +use rustc_middle::ty::{ + self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, RegionVid, ToPolyTraitRef, Ty, + TyCtxt, UserType, UserTypeAnnotationIndex, +}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::VariantIdx; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::opaque_types::{GenerateMemberConstraints, InferCtxtExt}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; @@ -39,10 +40,13 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::{Fallible, NoSolution}; use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations}; -use crate::dataflow::generic::ResultsCursor; +use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::move_paths::MoveData; -use crate::dataflow::MaybeInitializedPlaces; -use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute; +use crate::dataflow::ResultsCursor; +use crate::transform::{ + check_consts::ConstCx, + promote_consts::should_suggest_const_in_array_repeat_expressions_attribute, +}; use crate::borrow_check::{ borrow_set::BorrowSet, @@ -105,26 +109,22 @@ mod relate_tys; /// /// - `infcx` -- inference context to use /// - `param_env` -- parameter environment to use for trait solving -/// - `mir` -- MIR to type-check -/// - `mir_def_id` -- DefId from which the MIR is derived (must be local) -/// - `region_bound_pairs` -- the implied outlives obligations between type parameters -/// and lifetimes (e.g., `&'a T` implies `T: 'a`) -/// - `implicit_region_bound` -- a region which all generic parameters are assumed -/// to outlive; should represent the fn body -/// - `input_tys` -- fully liberated, but **not** normalized, expected types of the arguments; -/// the types of the input parameters found in the MIR itself will be equated with these -/// - `output_ty` -- fully liberated, but **not** normalized, expected return type; -/// the type for the RETURN_PLACE will be equated with this -/// - `liveness` -- results of a liveness computation on the MIR; used to create liveness -/// constraints for the regions in the types of variables +/// - `body` -- MIR body to type-check +/// - `promoted` -- map of promoted constants within `body` +/// - `mir_def_id` -- `LocalDefId` from which the MIR is derived +/// - `universal_regions` -- the universal regions from `body`s function signature +/// - `location_table` -- MIR location map of `body` +/// - `borrow_set` -- information about borrows occurring in `body` +/// - `all_facts` -- when using Polonius, this is the generated set of Polonius facts /// - `flow_inits` -- results of a maybe-init dataflow analysis /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis +/// - `elements` -- MIR region map pub(crate) fn type_check<'mir, 'tcx>( infcx: &InferCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, - promoted: &IndexVec>, - mir_def_id: DefId, + body: &Body<'tcx>, + promoted: &IndexVec>, + mir_def_id: LocalDefId, universal_regions: &Rc>, location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, @@ -188,10 +188,10 @@ pub(crate) fn type_check<'mir, 'tcx>( fn type_check_internal<'a, 'tcx, R>( infcx: &'a InferCtxt<'a, 'tcx>, - mir_def_id: DefId, + mir_def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, - body: ReadOnlyBodyAndCache<'a, 'tcx>, - promoted: &'a IndexVec>, + body: &'a Body<'tcx>, + promoted: &'a IndexVec>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, @@ -200,7 +200,7 @@ fn type_check_internal<'a, 'tcx, R>( ) -> R { let mut checker = TypeChecker::new( infcx, - *body, + body, mir_def_id, param_env, region_bound_pairs, @@ -209,8 +209,8 @@ fn type_check_internal<'a, 'tcx, R>( universal_region_relations, ); let errors_reported = { - let mut verifier = TypeVerifier::new(&mut checker, *body, promoted); - verifier.visit_body(body); + let mut verifier = TypeVerifier::new(&mut checker, body, promoted); + verifier.visit_body(&body); verifier.errors_reported }; @@ -266,9 +266,9 @@ enum FieldAccessError { struct TypeVerifier<'a, 'b, 'tcx> { cx: &'a mut TypeChecker<'b, 'tcx>, body: &'b Body<'tcx>, - promoted: &'b IndexVec>, + promoted: &'b IndexVec>, last_span: Span, - mir_def_id: DefId, + mir_def_id: LocalDefId, errors_reported: bool, } @@ -320,7 +320,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.literal.val { if let Some(promoted) = promoted { let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, - promoted: &ReadOnlyBodyAndCache<'_, 'tcx>, + promoted: &Body<'tcx>, ty, san_ty| { if let Err(terr) = verifier.cx.eq_types( @@ -341,11 +341,11 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { }; if !self.errors_reported { - let promoted_body = self.promoted[promoted]; + let promoted_body = &self.promoted[promoted]; self.sanitize_promoted(promoted_body, location); let promoted_ty = promoted_body.return_ty(); - check_err(self, &promoted_body, ty, promoted_ty); + check_err(self, promoted_body, ty, promoted_ty); } } else { if let Err(terr) = self.cx.fully_perform_op( @@ -402,40 +402,43 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { self.super_local_decl(local, local_decl); self.sanitize_type(local_decl, local_decl.ty); - for (user_ty, span) in local_decl.user_ty.projections_and_spans() { - let ty = if !local_decl.is_nonref_binding() { - // If we have a binding of the form `let ref x: T = ..` then remove the outermost - // reference so we can check the type annotation for the remaining type. - if let ty::Ref(_, rty, _) = local_decl.ty.kind { - rty + if let Some(user_ty) = &local_decl.user_ty { + for (user_ty, span) in user_ty.projections_and_spans() { + let ty = if !local_decl.is_nonref_binding() { + // If we have a binding of the form `let ref x: T = ..` + // then remove the outermost reference so we can check the + // type annotation for the remaining type. + if let ty::Ref(_, rty, _) = local_decl.ty.kind { + rty + } else { + bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); + } } else { - bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); - } - } else { - local_decl.ty - }; + local_decl.ty + }; - if let Err(terr) = self.cx.relate_type_and_user_type( - ty, - ty::Variance::Invariant, - user_ty, - Locations::All(*span), - ConstraintCategory::TypeAnnotation, - ) { - span_mirbug!( - self, - local, - "bad user type on variable {:?}: {:?} != {:?} ({:?})", - local, - local_decl.ty, - local_decl.user_ty, - terr, - ); + if let Err(terr) = self.cx.relate_type_and_user_type( + ty, + ty::Variance::Invariant, + user_ty, + Locations::All(*span), + ConstraintCategory::TypeAnnotation, + ) { + span_mirbug!( + self, + local, + "bad user type on variable {:?}: {:?} != {:?} ({:?})", + local, + local_decl.ty, + local_decl.user_ty, + terr, + ); + } } } } - fn visit_body(&mut self, body: ReadOnlyBodyAndCache<'_, 'tcx>) { + fn visit_body(&mut self, body: &Body<'tcx>) { self.sanitize_type(&"return type", body.return_ty()); for local_decl in &body.local_decls { self.sanitize_type(local_decl, local_decl.ty); @@ -451,7 +454,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { fn new( cx: &'a mut TypeChecker<'b, 'tcx>, body: &'b Body<'tcx>, - promoted: &'b IndexVec>, + promoted: &'b IndexVec>, ) -> Self { TypeVerifier { body, @@ -500,7 +503,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { let tcx = self.tcx(); let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().copy_trait().unwrap(), + def_id: tcx.require_lang_item(CopyTraitLangItem, Some(self.last_span)), substs: tcx.mk_substs_trait(place_ty.ty, &[]), }; @@ -525,16 +528,12 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { place_ty } - fn sanitize_promoted( - &mut self, - promoted_body: ReadOnlyBodyAndCache<'b, 'tcx>, - location: Location, - ) { + fn sanitize_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) { // Determine the constraints from the promoted MIR by running the type // checker on the promoted MIR, then transfer the constraints back to // the main MIR, changing the locations to the provided location. - let parent_body = mem::replace(&mut self.body, *promoted_body); + 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. @@ -563,7 +562,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { swap_constraints(self); - self.visit_body(promoted_body); + self.visit_body(&promoted_body); if !self.errors_reported { // if verifier failed, don't do further checks to avoid ICEs @@ -690,6 +689,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let fty = self.sanitize_type(place, fty); match self.field_ty(place, base, field, location) { Ok(ty) => { + let ty = self.cx.normalize(ty, location); if let Err(terr) = self.cx.eq_types( ty, fty, @@ -812,7 +812,7 @@ struct TypeChecker<'a, 'tcx> { /// User type annotations are shared between the main MIR and the MIR of /// all of the promoted items. user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, - mir_def_id: DefId, + mir_def_id: LocalDefId, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, @@ -960,7 +960,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn new( infcx: &'a InferCtxt<'a, 'tcx>, body: &'a Body<'tcx>, - mir_def_id: DefId, + mir_def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, @@ -1053,7 +1053,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// regions which are extracted and stored as having occurred at /// `locations`. /// - /// **Any `rustc::infer` operations that might generate region + /// **Any `rustc_infer::infer` operations that might generate region /// constraints should occur within this method so that those /// constraints can be properly localized!** fn fully_perform_op( @@ -1139,7 +1139,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // When you have `let x: impl Foo = ...` in a closure, // the resulting inferend values are stored with the // def-id of the base function. - let parent_def_id = self.tcx().closure_base_def_id(self.mir_def_id); + let parent_def_id = self.tcx().closure_base_def_id(self.mir_def_id.to_def_id()); return self.eq_opaque_type_and_type(sub, sup, parent_def_id, locations, category); } else { return Err(terr); @@ -1233,7 +1233,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tcx = infcx.tcx; let param_env = self.param_env; let body = self.body; - let concrete_opaque_types = &tcx.typeck_tables_of(anon_owner_def_id).concrete_opaque_types; + let concrete_opaque_types = + &tcx.typeck_tables_of(anon_owner_def_id.expect_local()).concrete_opaque_types; let mut opaque_type_values = Vec::new(); debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id); @@ -1396,12 +1397,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.infcx.tcx } - fn check_stmt( - &mut self, - body: ReadOnlyBodyAndCache<'_, 'tcx>, - stmt: &Statement<'tcx>, - location: Location, - ) { + fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) { debug!("check_stmt: {:?}", stmt); let tcx = self.tcx(); match stmt.kind { @@ -1433,9 +1429,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { _ => ConstraintCategory::Assignment, }; - let place_ty = place.ty(*body, tcx).ty; + let place_ty = place.ty(body, tcx).ty; let place_ty = self.normalize(place_ty, location); - let rv_ty = rv.ty(*body, tcx); + let rv_ty = rv.ty(body, tcx); let rv_ty = self.normalize(rv_ty, location); if let Err(terr) = self.sub_types_or_anon(rv_ty, place_ty, location.to_locations(), category) @@ -1473,7 +1469,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_rvalue(body, rv, location); if !self.tcx().features().unsized_locals { let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().sized_trait().unwrap(), + def_id: tcx.require_lang_item(SizedTraitLangItem, Some(self.last_span)), substs: tcx.mk_substs_trait(place_ty, &[]), }; self.prove_trait_ref( @@ -1484,7 +1480,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } StatementKind::SetDiscriminant { ref place, variant_index } => { - let place_type = place.ty(*body, tcx).ty; + let place_type = place.ty(body, tcx).ty; let adt = match place_type.kind { ty::Adt(adt, _) if adt.is_enum() => adt, _ => { @@ -1506,7 +1502,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }; } StatementKind::AscribeUserType(box (ref place, ref projection), variance) => { - let place_ty = place.ty(*body, tcx).ty; + let place_ty = place.ty(body, tcx).ty; if let Err(terr) = self.relate_type_and_user_type( place_ty, variance, @@ -1529,7 +1525,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::InlineAsm { .. } + | StatementKind::LlvmInlineAsm { .. } | StatementKind::Retag { .. } | StatementKind::Nop => {} } @@ -1552,7 +1548,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdges { .. } - | TerminatorKind::FalseUnwind { .. } => { + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { // no checks needed for these } @@ -1760,6 +1757,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { let op_arg_ty = op_arg.ty(body, self.tcx()); + let op_arg_ty = self.normalize(op_arg_ty, term_location); let category = if from_hir_call { ConstraintCategory::CallArgument } else { @@ -1858,6 +1856,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.assert_iscleanup(body, block_data, unwind, true); } } + TerminatorKind::InlineAsm { ref destination, .. } => { + if let &Some(target) = destination { + self.assert_iscleanup(body, block_data, target, is_cleanup); + } + } } } @@ -1972,12 +1975,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - fn check_rvalue( - &mut self, - body: ReadOnlyBodyAndCache<'_, 'tcx>, - rvalue: &Rvalue<'tcx>, - location: Location, - ) { + fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { let tcx = self.tcx(); match rvalue { @@ -1995,16 +1993,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // While this is located in `nll::typeck` this error is not an NLL error, it's // a required check to make sure that repeated elements implement `Copy`. let span = body.source_info(location).span; - let ty = operand.ty(*body, tcx); + let ty = operand.ty(body, tcx); if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { + let ccx = ConstCx::new_with_param_env( + tcx, + self.mir_def_id, + body, + self.param_env, + ); // To determine if `const_in_array_repeat_expressions` feature gate should // be mentioned, need to check if the rvalue is promotable. let should_suggest = should_suggest_const_in_array_repeat_expressions_attribute( - tcx, - self.mir_def_id, - body, - operand, + &ccx, operand, ); debug!("check_rvalue: should_suggest={:?}", should_suggest); @@ -2012,16 +2013,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { &traits::Obligation::new( ObligationCause::new( span, - self.tcx() - .hir() - .local_def_id_to_hir_id(self.mir_def_id.expect_local()), + self.tcx().hir().local_def_id_to_hir_id(self.mir_def_id), traits::ObligationCauseCode::RepeatVec(should_suggest), ), self.param_env, ty::Predicate::Trait( ty::Binder::bind(ty::TraitPredicate { trait_ref: ty::TraitRef::new( - self.tcx().lang_items().copy_trait().unwrap(), + self.tcx().require_lang_item( + CopyTraitLangItem, + Some(self.last_span), + ), tcx.mk_substs_trait(ty, &[]), ), }), @@ -2045,7 +2047,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().sized_trait().unwrap(), + def_id: tcx.require_lang_item(SizedTraitLangItem, Some(self.last_span)), substs: tcx.mk_substs_trait(ty, &[]), }; @@ -2059,7 +2061,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Rvalue::Cast(cast_kind, op, ty) => { match cast_kind { CastKind::Pointer(PointerCast::ReifyFnPointer) => { - let fn_sig = op.ty(*body, tcx).fn_sig(tcx); + let fn_sig = op.ty(body, tcx).fn_sig(tcx); // The type that we see in the fcx is like // `foo::<'a, 'b>`, where `foo` is the path to a @@ -2088,11 +2090,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => { - let sig = match op.ty(*body, tcx).kind { + let sig = match op.ty(body, tcx).kind { ty::Closure(_, substs) => substs.as_closure().sig(), _ => bug!(), }; - let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety); + let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety)); if let Err(terr) = self.eq_types( ty_fn_ptr_from, @@ -2112,7 +2114,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Pointer(PointerCast::UnsafeFnPointer) => { - let fn_sig = op.ty(*body, tcx).fn_sig(tcx); + let fn_sig = op.ty(body, tcx).fn_sig(tcx); // The type that we see in the fcx is like // `foo::<'a, 'b>`, where `foo` is the path to a @@ -2143,8 +2145,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { CastKind::Pointer(PointerCast::Unsize) => { let &ty = ty; let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().coerce_unsized_trait().unwrap(), - substs: tcx.mk_substs_trait(op.ty(*body, tcx), &[ty.into()]), + def_id: tcx.require_lang_item( + CoerceUnsizedTraitLangItem, + Some(self.last_span), + ), + substs: tcx.mk_substs_trait(op.ty(body, tcx), &[ty.into()]), }; self.prove_trait_ref( @@ -2155,7 +2160,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Pointer(PointerCast::MutToConstPointer) => { - let ty_from = match op.ty(*body, tcx).kind { + let ty_from = match op.ty(body, tcx).kind { ty::RawPtr(ty::TypeAndMut { ty: ty_from, mutbl: hir::Mutability::Mut, @@ -2203,7 +2208,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Pointer(PointerCast::ArrayToPointer) => { - let ty_from = op.ty(*body, tcx); + let ty_from = op.ty(body, tcx); let opt_ty_elem = match ty_from.kind { ty::RawPtr(ty::TypeAndMut { @@ -2263,27 +2268,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Misc => { - let ty_from = op.ty(*body, tcx); + let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(ty); match (cast_ty_from, cast_ty_to) { (None, _) - | (_, None) - | (_, Some(CastTy::FnPtr)) + | (_, None | Some(CastTy::FnPtr)) | (Some(CastTy::Float), Some(CastTy::Ptr(_))) - | (Some(CastTy::Ptr(_)), Some(CastTy::Float)) - | (Some(CastTy::FnPtr), Some(CastTy::Float)) => { + | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Float)) => { span_mirbug!(self, rvalue, "Invalid cast {:?} -> {:?}", ty_from, ty,) } - (Some(CastTy::Int(_)), Some(CastTy::Int(_))) - | (Some(CastTy::Float), Some(CastTy::Int(_))) - | (Some(CastTy::Int(_)), Some(CastTy::Float)) - | (Some(CastTy::Float), Some(CastTy::Float)) - | (Some(CastTy::Ptr(_)), Some(CastTy::Int(_))) - | (Some(CastTy::FnPtr), Some(CastTy::Int(_))) - | (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) - | (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) - | (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => (), + ( + Some(CastTy::Int(_)), + Some(CastTy::Int(_) | CastTy::Float | CastTy::Ptr(_)), + ) + | (Some(CastTy::Float), Some(CastTy::Int(_) | CastTy::Float)) + | (Some(CastTy::Ptr(_)), Some(CastTy::Int(_) | CastTy::Ptr(_))) + | (Some(CastTy::FnPtr), Some(CastTy::Int(_) | CastTy::Ptr(_))) => (), } } } @@ -2293,43 +2294,60 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.add_reborrow_constraint(&body, location, region, borrowed_place); } - Rvalue::BinaryOp(BinOp::Eq, left, right) - | Rvalue::BinaryOp(BinOp::Ne, left, right) - | Rvalue::BinaryOp(BinOp::Lt, left, right) - | Rvalue::BinaryOp(BinOp::Le, left, right) - | Rvalue::BinaryOp(BinOp::Gt, left, right) - | Rvalue::BinaryOp(BinOp::Ge, left, right) => { - let ty_left = left.ty(*body, tcx); - if let ty::RawPtr(_) | ty::FnPtr(_) = ty_left.kind { - let ty_right = right.ty(*body, tcx); - let common_ty = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: body.source_info(location).span, - }); - self.sub_types( - common_ty, - ty_left, - location.to_locations(), - ConstraintCategory::Boring, - ) - .unwrap_or_else(|err| { - bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) - }); - if let Err(terr) = self.sub_types( - common_ty, - ty_right, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!( - self, - rvalue, - "unexpected comparison types {:?} and {:?} yields {:?}", + Rvalue::BinaryOp( + BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, + left, + right, + ) => { + let ty_left = left.ty(body, tcx); + match ty_left.kind { + // Types with regions are comparable if they have a common super-type. + ty::RawPtr(_) | ty::FnPtr(_) => { + let ty_right = right.ty(body, tcx); + let common_ty = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: body.source_info(location).span, + }); + self.relate_types( + common_ty, + ty::Variance::Contravariant, ty_left, - ty_right, - terr + location.to_locations(), + ConstraintCategory::Boring, ) + .unwrap_or_else(|err| { + bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) + }); + if let Err(terr) = self.relate_types( + common_ty, + ty::Variance::Contravariant, + ty_right, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!( + self, + rvalue, + "unexpected comparison types {:?} and {:?} yields {:?}", + ty_left, + ty_right, + terr + ) + } } + // For types with no regions we can just check that the + // both operands have the same type. + ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) + if ty_left == right.ty(body, tcx) => {} + // Other types are compared by trait methods, not by + // `Rvalue::BinaryOp`. + _ => span_mirbug!( + self, + rvalue, + "unexpected comparison types {:?} and {:?}", + ty_left, + right.ty(body, tcx) + ), } } @@ -2402,6 +2420,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; let operand_ty = operand.ty(body, tcx); + let operand_ty = self.normalize(operand_ty, location); if let Err(terr) = self.sub_types( operand_ty, @@ -2579,7 +2598,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // clauses on the struct. AggregateKind::Closure(def_id, substs) | AggregateKind::Generator(def_id, substs, _) => { - self.prove_closure_bounds(tcx, *def_id, substs, location) + self.prove_closure_bounds(tcx, def_id.expect_local(), substs, location) } AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(), @@ -2594,14 +2613,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn prove_closure_bounds( &mut self, tcx: TyCtxt<'tcx>, - def_id: DefId, + def_id: LocalDefId, substs: SubstsRef<'tcx>, location: Location, ) -> ty::InstantiatedPredicates<'tcx> { if let Some(ref closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements { let closure_constraints = QueryRegionConstraints { - outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs), + outlives: closure_region_requirements.apply_requirements( + tcx, + def_id.to_def_id(), + substs, + ), // Presently, closures never propagate member // constraints to their parents -- they are enforced @@ -2715,7 +2738,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }) } - fn typeck_mir(&mut self, body: ReadOnlyBodyAndCache<'_, 'tcx>) { + fn typeck_mir(&mut self, body: &Body<'tcx>) { self.last_span = body.span; debug!("run_on_mir: {:?}", body.span); diff --git a/src/librustc_mir/borrow_check/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/type_check/relate_tys.rs index ebaafd4026270..7ff12820db815 100644 --- a/src/librustc_mir/borrow_check/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/type_check/relate_tys.rs @@ -1,10 +1,9 @@ -use rustc::mir::ConstraintCategory; -use rustc::ty::relate::TypeRelation; -use rustc::ty::{self, Ty}; use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::{self, Const, Ty}; use rustc_trait_selection::traits::query::Fallible; -use rustc_trait_selection::traits::DomainGoal; use crate::borrow_check::constraints::OutlivesConstraint; use crate::borrow_check::type_check::{BorrowCheckContext, Locations}; @@ -100,9 +99,9 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> { } } - fn push_domain_goal(&mut self, _: DomainGoal<'tcx>) { - bug!("should never be invoked with eager normalization") - } + // We don't have to worry about the equality of consts during borrow checking + // as consts always have a static lifetime. + fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {} fn normalization() -> NormalizationStrategy { NormalizationStrategy::Eager diff --git a/src/librustc_mir/borrow_check/universal_regions.rs b/src/librustc_mir/borrow_check/universal_regions.rs index 825931cf8922f..3003f4639d9fa 100644 --- a/src/librustc_mir/borrow_check/universal_regions.rs +++ b/src/librustc_mir/borrow_check/universal_regions.rs @@ -13,17 +13,17 @@ //! just returns them for other code to use. use either::Either; -use rustc::middle::lang_items; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items; use rustc_hir::{BodyOwnerKind, HirId}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; use std::iter; use crate::borrow_check::nll::ToRegionVid; @@ -54,6 +54,13 @@ pub struct UniversalRegions<'tcx> { /// The total number of universal region variables instantiated. num_universals: usize, + /// A special region variable created for the `'empty(U0)` region. + /// Note that this is **not** a "universal" region, as it doesn't + /// represent a universally bound placeholder or any such thing. + /// But we do create it here in this type because it's a useful region + /// to have around in a few limited cases. + pub root_empty: RegionVid, + /// The "defining" type for this function, with all universal /// regions instantiated. For a closure or generator, this is the /// closure type, but for a top-level function it's the `FnDef`. @@ -220,12 +227,13 @@ impl<'tcx> UniversalRegions<'tcx> { /// known between those regions. pub fn new( infcx: &InferCtxt<'_, 'tcx>, - mir_def_id: DefId, + mir_def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, ) -> Self { let tcx = infcx.tcx; - let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).unwrap(); - UniversalRegionsBuilder { infcx, mir_def_id, mir_hir_id, param_env }.build() + let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id); + UniversalRegionsBuilder { infcx, mir_def_id: mir_def_id.to_def_id(), mir_hir_id, param_env } + .build() } /// Given a reference to a closure type, extracts all the values @@ -316,7 +324,11 @@ impl<'tcx> UniversalRegions<'tcx> { /// See `UniversalRegionIndices::to_region_vid`. pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { - self.indices.to_region_vid(r) + if let ty::ReEmpty(ty::UniverseIndex::ROOT) = r { + self.root_empty + } else { + self.indices.to_region_vid(r) + } } /// As part of the NLL unit tests, you can annotate a function with @@ -472,10 +484,16 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { _ => None, }; + let root_empty = self + .infcx + .next_nll_region_var(NLLRegionVariableOrigin::RootEmptyRegion) + .to_region_vid(); + UniversalRegions { indices, fr_static, fr_fn_body, + root_empty, first_extern_index, first_local_index, num_universals, @@ -497,7 +515,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let defining_ty = if self.mir_def_id == closure_base_def_id { tcx.type_of(closure_base_def_id) } else { - let tables = tcx.typeck_tables_of(self.mir_def_id); + let tables = tcx.typeck_tables_of(self.mir_def_id.expect_local()); tables.node_type(self.mir_hir_id) }; @@ -777,7 +795,7 @@ fn for_each_late_bound_region_defined_on<'tcx>( let region_def_id = tcx.hir().local_def_id(hir_id); let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion { scope: fn_def_id, - bound_region: ty::BoundRegion::BrNamed(region_def_id, name), + bound_region: ty::BoundRegion::BrNamed(region_def_id.to_def_id(), name), })); f(liberated_region); } diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs index 5e4eebb771f21..2da72f3bcc517 100644 --- a/src/librustc_mir/borrow_check/used_muts.rs +++ b/src/librustc_mir/borrow_check/used_muts.rs @@ -1,5 +1,5 @@ -use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc::mir::{Local, Location, Place, Statement, StatementKind, TerminatorKind}; +use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::{Local, Location, Place, Statement, StatementKind, TerminatorKind}; use rustc_data_structures::fx::FxHashSet; @@ -32,7 +32,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { never_initialized_mut_locals: &mut never_initialized_mut_locals, mbcx: self, }; - visitor.visit_body(visitor.mbcx.body); + visitor.visit_body(&visitor.mbcx.body); } // Take the union of the existed `used_mut` set with those variables we've found were @@ -51,7 +51,7 @@ struct GatherUsedMutsVisitor<'visit, 'cx, 'tcx> { } impl GatherUsedMutsVisitor<'_, '_, '_> { - fn remove_never_initialized_mut_locals(&mut self, into: &Place<'_>) { + fn remove_never_initialized_mut_locals(&mut self, into: Place<'_>) { // Remove any locals that we found were initialized from the // `never_initialized_mut_locals` set. At the end, the only remaining locals will // be those that were never initialized - we will consider those as being used as @@ -66,26 +66,23 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc debug!("visit_terminator_kind: kind={:?}", kind); match &kind { TerminatorKind::Call { destination: Some((into, _)), .. } => { - self.remove_never_initialized_mut_locals(&into); + self.remove_never_initialized_mut_locals(*into); } TerminatorKind::DropAndReplace { location, .. } => { - self.remove_never_initialized_mut_locals(&location); + self.remove_never_initialized_mut_locals(*location); } _ => {} } } fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) { - match &statement.kind { - StatementKind::Assign(box (into, _)) => { - debug!( - "visit_statement: statement={:?} local={:?} \ + if let StatementKind::Assign(box (into, _)) = &statement.kind { + debug!( + "visit_statement: statement={:?} local={:?} \ never_initialized_mut_locals={:?}", - statement, into.local, self.never_initialized_mut_locals - ); - self.remove_never_initialized_mut_locals(into); - } - _ => {} + statement, into.local, self.never_initialized_mut_locals + ); + self.remove_never_initialized_mut_locals(*into); } } diff --git a/src/librustc_mir/const_eval/error.rs b/src/librustc_mir/const_eval/error.rs index dc23eba643e3e..b165a69433db1 100644 --- a/src/librustc_mir/const_eval/error.rs +++ b/src/librustc_mir/const_eval/error.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::fmt; -use rustc::mir::AssertKind; +use rustc_middle::mir::AssertKind; use rustc_span::Symbol; use super::InterpCx; @@ -12,12 +12,13 @@ use crate::interpret::{ConstEvalErr, InterpErrorInfo, Machine}; pub enum ConstEvalErrKind { NeedsRfc(String), ConstAccessesStatic, + ModifiedGlobal, AssertFailure(AssertKind), Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, } // The errors become `MachineStop` with plain strings when being raised. -// `ConstEvalErr` (in `librustc/mir/interpret/error.rs`) knows to +// `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to // handle these. impl<'tcx> Into> for ConstEvalErrKind { fn into(self) -> InterpErrorInfo<'tcx> { @@ -33,6 +34,9 @@ impl fmt::Display for ConstEvalErrKind { write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) } ConstAccessesStatic => write!(f, "constant accesses static"), + ModifiedGlobal => { + write!(f, "modifying a static's initial value from another static's initializer") + } AssertFailure(ref msg) => write!(f, "{:?}", msg), Panic { msg, line, col, file } => { write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col) @@ -46,11 +50,11 @@ impl Error for ConstEvalErrKind {} /// Turn an interpreter error into something to report to the user. /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace. /// Should be called only if the error is actually going to to be reported! -pub fn error_to_const_error<'mir, 'tcx, M: Machine<'mir, 'tcx>>( +pub fn error_to_const_error<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>( ecx: &InterpCx<'mir, 'tcx, M>, - mut error: InterpErrorInfo<'tcx>, + error: InterpErrorInfo<'tcx>, ) -> ConstEvalErr<'tcx> { error.print_backtrace(); - let stacktrace = ecx.generate_stacktrace(None); + let stacktrace = ecx.generate_stacktrace(); ConstEvalErr { error: error.kind, stacktrace, span: ecx.tcx.span } } diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index ffbff00cf3760..0637ebf959e5a 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -1,16 +1,17 @@ use super::{error_to_const_error, CompileTimeEvalContext, CompileTimeInterpreter, MemoryExtra}; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ - intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, ImmTy, Immediate, InternKind, + intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar, - ScalarMaybeUndef, StackPopCleanup, + ScalarMaybeUninit, StackPopCleanup, }; -use rustc::mir; -use rustc::mir::interpret::{ConstEvalErr, ErrorHandled}; -use rustc::traits::Reveal; -use rustc::ty::{self, layout, layout::LayoutOf, subst::Subst, TyCtxt}; use rustc_hir::def::DefKind; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{ConstEvalErr, ErrorHandled}; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::{self, subst::Subst, TyCtxt}; use rustc_span::source_map::Span; +use rustc_target::abi::{Abi, LayoutOf}; use std::convert::TryInto; pub fn note_on_undefined_behavior_error() -> &'static str { @@ -46,7 +47,6 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.push_stack_frame( cid.instance, - body.span, body, Some(ret.into()), StackPopCleanup::None { cleanup: false }, @@ -66,7 +66,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( intern_kind, ret, body.ignore_interior_mut_in_const_validation, - )?; + ); debug!("eval_body_using_ecx done: {:?}", *ret); Ok(ret) @@ -100,14 +100,14 @@ pub(super) fn op_to_const<'tcx>( ) -> ConstValue<'tcx> { // We do not have value optimizations for everything. // Only scalars and slices, since they are very common. - // Note that further down we turn scalars of undefined bits back to `ByRef`. These can result + // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result // from scalar unions that are initialized with one of their zero sized variants. We could - // instead allow `ConstValue::Scalar` to store `ScalarMaybeUndef`, but that would affect all + // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all // the usual cases of extracting e.g. a `usize`, without there being a real use case for the // `Undef` situation. let try_as_immediate = match op.layout.abi { - layout::Abi::Scalar(..) => true, - layout::Abi::ScalarPair(..) => match op.layout.ty.kind { + Abi::Scalar(..) => true, + Abi::ScalarPair(..) => match op.layout.ty.kind { ty::Ref(_, inner, _) => match inner.kind { ty::Slice(elem) => elem == ecx.tcx.types.u8, ty::Str => true, @@ -130,7 +130,7 @@ pub(super) fn op_to_const<'tcx>( let to_const_value = |mplace: MPlaceTy<'_>| match mplace.ptr { Scalar::Ptr(ptr) => { - let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id); + let alloc = ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(); ConstValue::ByRef { alloc, offset: ptr.offset } } Scalar::Raw { data, .. } => { @@ -147,25 +147,28 @@ pub(super) fn op_to_const<'tcx>( match immediate { Ok(mplace) => to_const_value(mplace), // see comment on `let try_as_immediate` above - Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x { - ScalarMaybeUndef::Scalar(s) => ConstValue::Scalar(s), - ScalarMaybeUndef::Undef => to_const_value(op.assert_mem_place(ecx)), + Err(imm) => match *imm { + Immediate::Scalar(x) => match x { + ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s), + ScalarMaybeUninit::Uninit => to_const_value(op.assert_mem_place(ecx)), + }, + Immediate::ScalarPair(a, b) => { + let (data, start) = match a.not_undef().unwrap() { + Scalar::Ptr(ptr) => { + (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes()) + } + Scalar::Raw { .. } => ( + ecx.tcx + .intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])), + 0, + ), + }; + let len = b.to_machine_usize(&ecx.tcx.tcx).unwrap(); + let start = start.try_into().unwrap(); + let len: usize = len.try_into().unwrap(); + ConstValue::Slice { data, start, end: start + len } + } }, - Err(ImmTy { imm: Immediate::ScalarPair(a, b), .. }) => { - let (data, start) = match a.not_undef().unwrap() { - Scalar::Ptr(ptr) => { - (ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id), ptr.offset.bytes()) - } - Scalar::Raw { .. } => ( - ecx.tcx.intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])), - 0, - ), - }; - let len = b.to_machine_usize(&ecx.tcx.tcx).unwrap(); - let start = start.try_into().unwrap(); - let len: usize = len.try_into().unwrap(); - ConstValue::Slice { data, start, end: start + len } - } } } @@ -173,7 +176,7 @@ fn validate_and_turn_into_const<'tcx>( tcx: TyCtxt<'tcx>, constant: RawConst<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> { +) -> ::rustc_middle::mir::interpret::ConstEvalResult<'tcx> { let cid = key.value; let def_id = cid.instance.def.def_id(); let is_static = tcx.is_static(def_id); @@ -190,7 +193,7 @@ fn validate_and_turn_into_const<'tcx>( mplace.into(), path, &mut ref_tracking, - /*may_ref_to_static*/ is_static, + /*may_ref_to_static*/ ecx.memory.extra.can_access_statics, )?; } } @@ -200,7 +203,7 @@ fn validate_and_turn_into_const<'tcx>( if is_static || cid.promoted.is_some() { let ptr = mplace.ptr.assert_ptr(); Ok(ConstValue::ByRef { - alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id), + alloc: ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), offset: ptr.offset, }) } else { @@ -210,20 +213,17 @@ fn validate_and_turn_into_const<'tcx>( val.map_err(|error| { let err = error_to_const_error(&ecx, error); - match err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| { + err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| { diag.note(note_on_undefined_behavior_error()); diag.emit(); - }) { - Ok(_) => ErrorHandled::Reported, - Err(err) => err, - } + }) }) } pub fn const_eval_validated_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> { +) -> ::rustc_middle::mir::interpret::ConstEvalResult<'tcx> { // see comment in const_eval_raw_provider for what we're doing here if key.param_env.reveal == Reveal::All { let mut key = key; @@ -257,7 +257,7 @@ pub fn const_eval_validated_provider<'tcx>( pub fn const_eval_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc::mir::interpret::ConstEvalRawResult<'tcx> { +) -> ::rustc_middle::mir::interpret::ConstEvalRawResult<'tcx> { // Because the constant is computed twice (once per value of `Reveal`), we are at risk of // reporting the same error twice here. To resolve this, we check whether we can evaluate the // constant in the more restrictive `Reveal::UserFacing`, which most likely already was @@ -289,11 +289,12 @@ pub fn const_eval_raw_provider<'tcx>( let cid = key.value; let def_id = cid.instance.def.def_id(); - if def_id.is_local() - && tcx.has_typeck_tables(def_id) - && tcx.typeck_tables_of(def_id).tainted_by_errors - { - return Err(ErrorHandled::Reported); + if let Some(def_id) = def_id.as_local() { + if tcx.has_typeck_tables(def_id) { + if let Some(error_reported) = tcx.typeck_tables_of(def_id).tainted_by_errors { + return Err(ErrorHandled::Reported(error_reported)); + } + } } let is_static = tcx.is_static(def_id); @@ -307,7 +308,7 @@ pub fn const_eval_raw_provider<'tcx>( ); let res = ecx.load_mir(cid.instance.def, cid.promoted); - res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, *body)) + res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) .and_then(|place| { Ok(RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty }) }) @@ -342,8 +343,8 @@ pub fn const_eval_raw_provider<'tcx>( // because any code that existed before validation could not have failed // validation thus preventing such a hard error from being a backwards // compatibility hazard - Some(DefKind::Const) | Some(DefKind::AssocConst) => { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + DefKind::Const | DefKind::AssocConst => { + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); err.report_as_lint( tcx.at(tcx.def_span(def_id)), "any use of this value will cause an error", @@ -366,7 +367,7 @@ pub fn const_eval_raw_provider<'tcx>( err.report_as_lint( tcx.at(span), "reaching this expression at runtime will panic or abort", - tcx.hir().as_local_hir_id(def_id).unwrap(), + tcx.hir().as_local_hir_id(def_id.expect_local()), Some(err.span), ) } diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index 470e4e7ed25c1..74f8a1cb6d124 100644 --- a/src/librustc_mir/const_eval/fn_queries.rs +++ b/src/librustc_mir/const_eval/fn_queries.rs @@ -1,9 +1,9 @@ -use rustc::hir::map::blocks::FnLikeNode; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_attr as attr; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_target::spec::abi::Abi; @@ -84,23 +84,26 @@ pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { let parent_id = tcx.hir().get_parent_did(hir_id); - if !parent_id.is_top_level_module() { - is_const_impl_raw(tcx, parent_id.expect_local()) - } else { - false - } + if !parent_id.is_top_level_module() { is_const_impl_raw(tcx, parent_id) } else { false } } /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether /// said intrinsic is on the whitelist for being const callable. fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let hir_id = - tcx.hir().as_local_hir_id(def_id).expect("Non-local call to local provider is_const_fn"); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); - if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) { - whitelisted + if let hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) = + node + { + // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other + // foreign items cannot be evaluated at compile-time. + if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = tcx.hir().get_foreign_abi(hir_id) { + tcx.lookup_const_stability(def_id).is_some() + } else { + false + } } else if let Some(fn_like) = FnLikeNode::from_node(node) { if fn_like.constness() == hir::Constness::Const { return true; @@ -116,21 +119,6 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } } -/// Const evaluability whitelist is here to check evaluability at the -/// top level beforehand. -fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - if tcx.is_closure(def_id) { - return None; - } - - match tcx.fn_sig(def_id).abi() { - Abi::RustIntrinsic | Abi::PlatformIntrinsic => { - Some(tcx.lookup_const_stability(def_id).is_some()) - } - _ => None, - } -} - /// Checks whether the given item is an `impl` that has a `const` modifier. fn is_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs index ab88a92ea7bc2..7c1ab261eb9c4 100644 --- a/src/librustc_mir/const_eval/machine.rs +++ b/src/librustc_mir/const_eval/machine.rs @@ -1,24 +1,25 @@ -use rustc::mir; -use rustc::ty::layout::HasTyCtxt; -use rustc::ty::{self, Ty}; -use std::borrow::{Borrow, Cow}; +use rustc_middle::mir; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{self, Ty}; +use std::borrow::Borrow; use std::collections::hash_map::Entry; use std::hash::Hash; use rustc_data_structures::fx::FxHashMap; -use rustc::mir::AssertMessage; -use rustc_span::source_map::Span; +use rustc_ast::ast::Mutability; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::AssertMessage; use rustc_span::symbol::Symbol; use crate::interpret::{ - self, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, OpTy, - PlaceTy, Pointer, Scalar, + self, compile_time_machine, AllocId, Allocation, Frame, GlobalId, ImmTy, InterpCx, + InterpResult, Memory, OpTy, PlaceTy, Pointer, Scalar, }; use super::error::*; -impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> { +impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { /// Evaluate a const function where all arguments (if any) are zero-sized types. /// The evaluation is memoized thanks to the query system. /// @@ -63,7 +64,6 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> { /// If this returns successfully (`Ok`), the function should just be evaluated normally. fn hook_panic_fn( &mut self, - span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], ) -> InterpResult<'tcx> { @@ -76,7 +76,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> { let msg_place = self.deref_operand(args[0])?; let msg = Symbol::intern(self.read_str(msg_place)?); - let span = self.find_closest_untracked_caller_location().unwrap_or(span); + let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()) } else { @@ -86,23 +86,31 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> { } /// Extra machine state for CTFE, and the Machine instance -pub struct CompileTimeInterpreter { +pub struct CompileTimeInterpreter<'mir, 'tcx> { /// For now, the number of terminators that can be evaluated before we throw a resource /// exhuastion error. /// /// Setting this to `0` disables the limit and allows the interpreter to run forever. pub steps_remaining: usize, + + /// The virtual call stack. + pub(crate) stack: Vec>, } #[derive(Copy, Clone, Debug)] pub struct MemoryExtra { - /// Whether this machine may read from statics + /// We need to make sure consts never point to anything mutable, even recursively. That is + /// relied on for pattern matching on consts with references. + /// To achieve this, two pieces have to work together: + /// * Interning makes everything outside of statics immutable. + /// * Pointers to allocations inside of statics can never leak outside, to a non-static global. + /// This boolean here controls the second part. pub(super) can_access_statics: bool, } -impl CompileTimeInterpreter { +impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { pub(super) fn new(const_eval_limit: usize) -> Self { - CompileTimeInterpreter { steps_remaining: const_eval_limit } + CompileTimeInterpreter { steps_remaining: const_eval_limit, stack: Vec::new() } } } @@ -156,7 +164,8 @@ impl interpret::AllocMap for FxHashMap { } } -crate type CompileTimeEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, CompileTimeInterpreter>; +crate type CompileTimeEvalContext<'mir, 'tcx> = + InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>; impl interpret::MayLeak for ! { #[inline(always)] @@ -166,31 +175,13 @@ impl interpret::MayLeak for ! { } } -impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { - type MemoryKinds = !; - type PointerTag = (); - type ExtraFnVal = !; +impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> { + compile_time_machine!(<'mir, 'tcx>); - type FrameExtra = (); type MemoryExtra = MemoryExtra; - type AllocExtra = (); - - type MemoryMap = FxHashMap, Allocation)>; - - const STATIC_KIND: Option = None; // no copying of statics allowed - - // We do not check for alignment to avoid having to carry an `Align` - // in `ConstValue::ByRef`. - const CHECK_ALIGN: bool = false; - - #[inline(always)] - fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { - false // for now, we don't enforce validity - } fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, - span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, @@ -212,7 +203,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { } else { // Some functions we support even if they are non-const -- but avoid testing // that for const fn! - ecx.hook_panic_fn(span, instance, args)?; + ecx.hook_panic_fn(instance, args)?; // We certainly do *not* want to actually call the fn // though, so be sure we return here. throw_unsup_format!("calling non-const function `{}`", instance) @@ -220,7 +211,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { } // This is a const fn. Call it. Ok(Some(match ecx.load_mir(instance.def, None) { - Ok(body) => *body, + Ok(body) => body, Err(err) => { if let err_unsup!(NoMirFor(did)) = err.kind { let path = ecx.tcx.def_path_str(did); @@ -235,25 +226,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { })) } - fn call_extra_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - fn_val: !, - _args: &[OpTy<'tcx>], - _ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, - _unwind: Option, - ) -> InterpResult<'tcx> { - match fn_val {} - } - fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, - span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, _unwind: Option, ) -> InterpResult<'tcx> { - if ecx.emulate_intrinsic(span, instance, args, ret)? { + if ecx.emulate_intrinsic(instance, args, ret)? { return Ok(()); } // An intrinsic that we do not support @@ -266,7 +246,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { msg: &AssertMessage<'tcx>, _unwind: Option, ) -> InterpResult<'tcx> { - use rustc::mir::AssertKind::*; + use rustc_middle::mir::AssertKind::*; // Convert `AssertKind` to `AssertKind`. let err = match msg { BoundsCheck { ref len, ref index } => { @@ -305,20 +285,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into()) } - #[inline(always)] - fn init_allocation_extra<'b>( - _memory_extra: &MemoryExtra, - _id: AllocId, - alloc: Cow<'b, Allocation>, - _kind: Option>, - ) -> (Cow<'b, Allocation>, Self::PointerTag) { - // We do not use a tag so we can just cheaply forward the allocation - (alloc, ()) - } - - #[inline(always)] - fn tag_static_base_pointer(_memory_extra: &MemoryExtra, _id: AllocId) -> Self::PointerTag {} - fn box_alloc( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _dest: PlaceTy<'tcx>, @@ -341,18 +307,54 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { } #[inline(always)] - fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + fn stack( + ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + &ecx.machine.stack } - fn before_access_static( + #[inline(always)] + fn stack_mut( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec> { + &mut ecx.machine.stack + } + + fn before_access_global( memory_extra: &MemoryExtra, - _allocation: &Allocation, + alloc_id: AllocId, + allocation: &Allocation, + static_def_id: Option, + is_write: bool, ) -> InterpResult<'tcx> { - if memory_extra.can_access_statics { - Ok(()) + if is_write { + // Write access. These are never allowed, but we give a targeted error message. + if allocation.mutability == Mutability::Not { + Err(err_ub!(WriteToReadOnly(alloc_id)).into()) + } else { + Err(ConstEvalErrKind::ModifiedGlobal.into()) + } } else { - Err(ConstEvalErrKind::ConstAccessesStatic.into()) + // Read access. These are usually allowed, with some exceptions. + if memory_extra.can_access_statics { + // Machine configuration allows us read from anything (e.g., `static` initializer). + Ok(()) + } else if static_def_id.is_some() { + // Machine configuration does not allow us to read statics + // (e.g., `const` initializer). + // See const_eval::machine::MemoryExtra::can_access_statics for why + // this check is so important: if we could read statics, we could read pointers + // to mutable allocations *inside* statics. These allocations are not themselves + // statics, so pointers to them can get around the check in `validity.rs`. + Err(ConstEvalErrKind::ConstAccessesStatic.into()) + } else { + // Immutable global, this read is fine. + // But make sure we never accept a read from something mutable, that would be + // unsound. The reason is that as the content of this allocation may be different + // now and at run-time, so if we permit reading now we might return the wrong value. + assert_eq!(allocation.mutability, Mutability::Not); + Ok(()) + } } } } diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/const_eval/mod.rs index 605091d6c7d41..7f557e340bbc8 100644 --- a/src/librustc_mir/const_eval/mod.rs +++ b/src/librustc_mir/const_eval/mod.rs @@ -1,9 +1,11 @@ // Not in interpret to make sure we do not use private implementation details -use rustc::mir; -use rustc::ty::layout::VariantIdx; -use rustc::ty::{self, TyCtxt}; +use std::convert::TryFrom; + +use rustc_middle::mir; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; +use rustc_target::abi::VariantIdx; use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx}; @@ -37,7 +39,7 @@ pub(crate) fn const_field<'tcx>( Some(variant) => ecx.operand_downcast(op, variant).unwrap(), }; // then project - let field = ecx.operand_field(down, field.index() as u64).unwrap(); + let field = ecx.operand_field(down, field.index()).unwrap(); // and finally move back to the const world, always normalizing because // this is not called for statics. op_to_const(&ecx, field) @@ -51,7 +53,7 @@ pub(crate) fn const_caller_location( let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false); let loc_place = ecx.alloc_caller_location(file, line, col); - intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false).unwrap(); + intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false); ConstValue::Scalar(loc_place.ptr) } @@ -68,10 +70,11 @@ pub(crate) fn destructure_const<'tcx>( let variant = ecx.read_discriminant(op).unwrap().1; + // We go to `usize` as we cannot allocate anything bigger anyway. let field_count = match val.ty.kind { - ty::Array(_, len) => len.eval_usize(tcx, param_env), - ty::Adt(def, _) => def.variants[variant].fields.len() as u64, - ty::Tuple(substs) => substs.len() as u64, + ty::Array(_, len) => usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), + ty::Adt(def, _) => def.variants[variant].fields.len(), + ty::Tuple(substs) => substs.len(), _ => bug!("cannot destructure constant {:?}", val), }; diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/dataflow/at_location.rs deleted file mode 100644 index e4eb8506846c0..0000000000000 --- a/src/librustc_mir/dataflow/at_location.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! A nice wrapper to consume dataflow results at several CFG -//! locations. - -use rustc::mir::{BasicBlock, Location}; -use rustc_index::bit_set::{BitIter, BitSet, HybridBitSet}; - -use crate::dataflow::{BitDenotation, DataflowResults, GenKillSet}; - -use std::borrow::Borrow; -use std::iter; - -/// A trait for "cartesian products" of multiple FlowAtLocation. -/// -/// There's probably a way to auto-impl this, but I think -/// it is cleaner to have manual visitor impls. -pub trait FlowsAtLocation { - /// Reset the state bitvector to represent the entry to block `bb`. - fn reset_to_entry_of(&mut self, bb: BasicBlock); - - /// Reset the state bitvector to represent the exit of the - /// terminator of block `bb`. - /// - /// **Important:** In the case of a `Call` terminator, these - /// effects do *not* include the result of storing the destination - /// of the call, since that is edge-dependent (in other words, the - /// effects don't apply to the unwind edge). - fn reset_to_exit_of(&mut self, bb: BasicBlock); - - /// Builds gen and kill sets for statement at `loc`. - /// - /// Note that invoking this method alone does not change the - /// `curr_state` -- you must invoke `apply_local_effect` - /// afterwards. - fn reconstruct_statement_effect(&mut self, loc: Location); - - /// Builds gen and kill sets for terminator for `loc`. - /// - /// Note that invoking this method alone does not change the - /// `curr_state` -- you must invoke `apply_local_effect` - /// afterwards. - fn reconstruct_terminator_effect(&mut self, loc: Location); - - /// Apply current gen + kill sets to `flow_state`. - /// - /// (`loc` parameters can be ignored if desired by - /// client. For the terminator, the `stmt_idx` will be the number - /// of statements in the block.) - fn apply_local_effect(&mut self, loc: Location); -} - -/// Represents the state of dataflow at a particular -/// CFG location, both before and after it is -/// executed. -/// -/// Data flow results are typically computed only as basic block -/// boundaries. A `FlowInProgress` allows you to reconstruct the -/// effects at any point in the control-flow graph by starting with -/// the state at the start of the basic block (`reset_to_entry_of`) -/// and then replaying the effects of statements and terminators -/// (e.g., via `reconstruct_statement_effect` and -/// `reconstruct_terminator_effect`; don't forget to call -/// `apply_local_effect`). -pub struct FlowAtLocation<'tcx, BD, DR = DataflowResults<'tcx, BD>> -where - BD: BitDenotation<'tcx>, - DR: Borrow>, -{ - base_results: DR, - curr_state: BitSet, - stmt_trans: GenKillSet, -} - -impl<'tcx, BD, DR> FlowAtLocation<'tcx, BD, DR> -where - BD: BitDenotation<'tcx>, - DR: Borrow>, -{ - /// Iterate over each bit set in the current state. - pub fn each_state_bit(&self, f: F) - where - F: FnMut(BD::Idx), - { - self.curr_state.iter().for_each(f) - } - - /// Iterate over each `gen` bit in the current effect (invoke - /// `reconstruct_statement_effect` or - /// `reconstruct_terminator_effect` first). - pub fn each_gen_bit(&self, f: F) - where - F: FnMut(BD::Idx), - { - self.stmt_trans.gen_set.iter().for_each(f) - } - - pub fn new(results: DR) -> Self { - let bits_per_block = results.borrow().sets().bits_per_block(); - let curr_state = BitSet::new_empty(bits_per_block); - let stmt_trans = GenKillSet::from_elem(HybridBitSet::new_empty(bits_per_block)); - FlowAtLocation { base_results: results, curr_state, stmt_trans } - } - - /// Access the underlying operator. - pub fn operator(&self) -> &BD { - self.base_results.borrow().operator() - } - - pub fn contains(&self, x: BD::Idx) -> bool { - self.curr_state.contains(x) - } - - /// Returns an iterator over the elements present in the current state. - pub fn iter_incoming(&self) -> iter::Peekable> { - self.curr_state.iter().peekable() - } - - /// Creates a clone of the current state and applies the local - /// effects to the clone (leaving the state of self intact). - /// Invokes `f` with an iterator over the resulting state. - pub fn with_iter_outgoing(&self, f: F) - where - F: FnOnce(BitIter<'_, BD::Idx>), - { - let mut curr_state = self.curr_state.clone(); - self.stmt_trans.apply(&mut curr_state); - f(curr_state.iter()); - } - - /// Returns a bitset of the elements present in the current state. - pub fn as_dense(&self) -> &BitSet { - &self.curr_state - } -} - -impl<'tcx, BD, DR> FlowsAtLocation for FlowAtLocation<'tcx, BD, DR> -where - BD: BitDenotation<'tcx>, - DR: Borrow>, -{ - fn reset_to_entry_of(&mut self, bb: BasicBlock) { - self.curr_state.overwrite(self.base_results.borrow().sets().entry_set_for(bb.index())); - } - - fn reset_to_exit_of(&mut self, bb: BasicBlock) { - self.reset_to_entry_of(bb); - let trans = self.base_results.borrow().sets().trans_for(bb.index()); - trans.apply(&mut self.curr_state) - } - - fn reconstruct_statement_effect(&mut self, loc: Location) { - self.stmt_trans.clear(); - self.base_results.borrow().operator().before_statement_effect(&mut self.stmt_trans, loc); - self.stmt_trans.apply(&mut self.curr_state); - - self.base_results.borrow().operator().statement_effect(&mut self.stmt_trans, loc); - } - - fn reconstruct_terminator_effect(&mut self, loc: Location) { - self.stmt_trans.clear(); - self.base_results.borrow().operator().before_terminator_effect(&mut self.stmt_trans, loc); - self.stmt_trans.apply(&mut self.curr_state); - - self.base_results.borrow().operator().terminator_effect(&mut self.stmt_trans, loc); - } - - fn apply_local_effect(&mut self, _loc: Location) { - self.stmt_trans.apply(&mut self.curr_state) - } -} diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index 720b17e7ff85b..91b342ae5c36a 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -1,6 +1,6 @@ use crate::util::elaborate_drops::DropFlagState; -use rustc::mir::{self, Body, Location}; -use rustc::ty::{self, TyCtxt}; +use rustc_middle::mir::{self, Body, Location}; +use rustc_middle::ty::{self, TyCtxt}; use super::indexes::MovePathIndex; use super::move_paths::{InitKind, LookupResult, MoveData}; @@ -49,7 +49,7 @@ where fn place_contents_drop_state_cannot_differ<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - place: &mir::Place<'tcx>, + place: mir::Place<'tcx>, ) -> bool { let ty = place.ty(body, tcx).ty; match ty.kind { @@ -110,7 +110,7 @@ pub(crate) fn on_all_children_bits<'tcx, F>( move_data: &MoveData<'tcx>, path: MovePathIndex, ) -> bool { - place_contents_drop_state_cannot_differ(tcx, body, &move_data.move_paths[path].place) + place_contents_drop_state_cannot_differ(tcx, body, move_data.move_paths[path].place) } fn on_all_children_bits<'tcx, F>( diff --git a/src/librustc_mir/dataflow/framework/cursor.rs b/src/librustc_mir/dataflow/framework/cursor.rs new file mode 100644 index 0000000000000..2ae353adfc7f3 --- /dev/null +++ b/src/librustc_mir/dataflow/framework/cursor.rs @@ -0,0 +1,212 @@ +//! Random access inspection of the results of a dataflow analysis. + +use std::borrow::Borrow; +use std::cmp::Ordering; + +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::{self, BasicBlock, Location}; + +use super::{Analysis, Direction, Effect, EffectIndex, Results}; + +/// A `ResultsCursor` that borrows the underlying `Results`. +pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>; + +/// Allows random access inspection of the results of a dataflow analysis. +/// +/// This cursor only has linear performance within a basic block when its statements are visited in +/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are +/// visited in *reverse* order—performance will be quadratic in the number of statements in the +/// block. The order in which basic blocks are inspected has no impact on performance. +/// +/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The +/// type of ownership is determined by `R` (see `ResultsRefCursor` above). +pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>> +where + A: Analysis<'tcx>, +{ + body: &'mir mir::Body<'tcx>, + results: R, + state: BitSet, + + pos: CursorPosition, + + /// Indicates that `state` has been modified with a custom effect. + /// + /// When this flag is set, we need to reset to an entry set before doing a seek. + state_needs_reset: bool, +} + +impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx>, + R: Borrow>, +{ + /// Returns a new cursor that can inspect `results`. + pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { + let bits_per_block = results.borrow().entry_set_for_block(mir::START_BLOCK).domain_size(); + + ResultsCursor { + body, + results, + + // Initialize to an empty `BitSet` and set `state_needs_reset` to tell the cursor that + // it needs to reset to block entry before the first seek. The cursor position is + // immaterial. + state_needs_reset: true, + state: BitSet::new_empty(bits_per_block), + pos: CursorPosition::block_entry(mir::START_BLOCK), + } + } + + pub fn body(&self) -> &'mir mir::Body<'tcx> { + self.body + } + + /// Returns the `Analysis` used to generate the underlying results. + pub fn analysis(&self) -> &A { + &self.results.borrow().analysis + } + + /// Returns the dataflow state at the current location. + pub fn get(&self) -> &BitSet { + &self.state + } + + /// Returns `true` if the dataflow state at the current location contains the given element. + /// + /// Shorthand for `self.get().contains(elem)` + pub fn contains(&self, elem: A::Idx) -> bool { + self.state.contains(elem) + } + + /// Resets the cursor to hold the entry set for the given basic block. + /// + /// For forward dataflow analyses, this is the dataflow state prior to the first statement. + /// + /// For backward dataflow analyses, this is the dataflow state after the terminator. + pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) { + self.state.overwrite(&self.results.borrow().entry_set_for_block(block)); + self.pos = CursorPosition::block_entry(block); + self.state_needs_reset = false; + } + + /// Resets the cursor to hold the state prior to the first statement in a basic block. + /// + /// For forward analyses, this is the entry set for the given block. + /// + /// For backward analyses, this is the state that will be propagated to its + /// predecessors (ignoring edge-specific effects). + pub fn seek_to_block_start(&mut self, block: BasicBlock) { + if A::Direction::is_forward() { + self.seek_to_block_entry(block) + } else { + self.seek_after(Location { block, statement_index: 0 }, Effect::Primary) + } + } + + /// Resets the cursor to hold the state after the terminator in a basic block. + /// + /// For backward analyses, this is the entry set for the given block. + /// + /// For forward analyses, this is the state that will be propagated to its + /// successors (ignoring edge-specific effects). + pub fn seek_to_block_end(&mut self, block: BasicBlock) { + if A::Direction::is_backward() { + self.seek_to_block_entry(block) + } else { + self.seek_after(self.body.terminator_loc(block), Effect::Primary) + } + } + + /// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is + /// applied. + /// + /// The "before" effect at the target location *will be* applied. + pub fn seek_before_primary_effect(&mut self, target: Location) { + self.seek_after(target, Effect::Before) + } + + /// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is + /// applied. + /// + /// The "before" effect at the target location will be applied as well. + pub fn seek_after_primary_effect(&mut self, target: Location) { + self.seek_after(target, Effect::Primary) + } + + fn seek_after(&mut self, target: Location, effect: Effect) { + assert!(target <= self.body.terminator_loc(target.block)); + + // Reset to the entry of the target block if any of the following are true: + // - A custom effect has been applied to the cursor state. + // - We are in a different block than the target. + // - We are in the same block but have advanced past the target effect. + if self.state_needs_reset || self.pos.block != target.block { + self.seek_to_block_entry(target.block); + } else if let Some(curr_effect) = self.pos.curr_effect_index { + let mut ord = curr_effect.statement_index.cmp(&target.statement_index); + if A::Direction::is_backward() { + ord = ord.reverse() + } + + match ord.then_with(|| curr_effect.effect.cmp(&effect)) { + Ordering::Equal => return, + Ordering::Greater => self.seek_to_block_entry(target.block), + Ordering::Less => {} + } + } + + // At this point, the cursor is in the same block as the target location at an earlier + // statement. + debug_assert_eq!(target.block, self.pos.block); + + let block_data = &self.body[target.block]; + let next_effect = if A::Direction::is_forward() { + #[rustfmt::skip] + self.pos.curr_effect_index.map_or_else( + || Effect::Before.at_index(0), + EffectIndex::next_in_forward_order, + ) + } else { + self.pos.curr_effect_index.map_or_else( + || Effect::Before.at_index(block_data.statements.len()), + EffectIndex::next_in_backward_order, + ) + }; + + let analysis = &self.results.borrow().analysis; + let target_effect_index = effect.at_index(target.statement_index); + + A::Direction::apply_effects_in_range( + analysis, + &mut self.state, + target.block, + block_data, + next_effect..=target_effect_index, + ); + + self.pos = + CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) }; + } + + /// Applies `f` to the cursor's internal state. + /// + /// This can be used, e.g., to apply the call return effect directly to the cursor without + /// creating an extra copy of the dataflow state. + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut BitSet)) { + f(&self.results.borrow().analysis, &mut self.state); + self.state_needs_reset = true; + } +} + +#[derive(Clone, Copy, Debug)] +struct CursorPosition { + block: BasicBlock, + curr_effect_index: Option, +} + +impl CursorPosition { + fn block_entry(block: BasicBlock) -> CursorPosition { + CursorPosition { block, curr_effect_index: None } + } +} diff --git a/src/librustc_mir/dataflow/framework/direction.rs b/src/librustc_mir/dataflow/framework/direction.rs new file mode 100644 index 0000000000000..97b14ea771b2f --- /dev/null +++ b/src/librustc_mir/dataflow/framework/direction.rs @@ -0,0 +1,576 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::ty::{self, TyCtxt}; +use std::ops::RangeInclusive; + +use super::visitor::{ResultsVisitable, ResultsVisitor}; +use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet}; + +pub trait Direction { + fn is_forward() -> bool; + + fn is_backward() -> bool { + !Self::is_forward() + } + + /// Applies all effects between the given `EffectIndex`s. + /// + /// `effects.start()` must precede or equal `effects.end()` in this direction. + fn apply_effects_in_range( + analysis: &A, + state: &mut BitSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + effects: RangeInclusive, + ) where + A: Analysis<'tcx>; + + fn apply_effects_in_block( + analysis: &A, + state: &mut BitSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + ) where + A: Analysis<'tcx>; + + fn gen_kill_effects_in_block( + analysis: &A, + trans: &mut GenKillSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + ) where + A: GenKillAnalysis<'tcx>; + + fn visit_results_in_block( + state: &mut F, + block: BasicBlock, + block_data: &'mir mir::BasicBlockData<'tcx>, + results: &R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + ) where + R: ResultsVisitable<'tcx, FlowState = F>; + + fn join_state_into_successors_of( + analysis: &A, + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + dead_unwinds: Option<&BitSet>, + exit_state: &mut BitSet, + block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>), + propagate: impl FnMut(BasicBlock, &BitSet), + ) where + A: Analysis<'tcx>; +} + +/// Dataflow that runs from the exit of a block (the terminator), to its entry (the first statement). +pub struct Backward; + +impl Direction for Backward { + fn is_forward() -> bool { + false + } + + fn apply_effects_in_block( + analysis: &A, + state: &mut BitSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + ) where + A: Analysis<'tcx>, + { + let terminator = block_data.terminator(); + let location = Location { block, statement_index: block_data.statements.len() }; + analysis.apply_before_terminator_effect(state, terminator, location); + analysis.apply_terminator_effect(state, terminator, location); + + for (statement_index, statement) in block_data.statements.iter().enumerate().rev() { + let location = Location { block, statement_index }; + analysis.apply_before_statement_effect(state, statement, location); + analysis.apply_statement_effect(state, statement, location); + } + } + + fn gen_kill_effects_in_block( + analysis: &A, + trans: &mut GenKillSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + ) where + A: GenKillAnalysis<'tcx>, + { + let terminator = block_data.terminator(); + let location = Location { block, statement_index: block_data.statements.len() }; + analysis.before_terminator_effect(trans, terminator, location); + analysis.terminator_effect(trans, terminator, location); + + for (statement_index, statement) in block_data.statements.iter().enumerate().rev() { + let location = Location { block, statement_index }; + analysis.before_statement_effect(trans, statement, location); + analysis.statement_effect(trans, statement, location); + } + } + + fn apply_effects_in_range( + analysis: &A, + state: &mut BitSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + effects: RangeInclusive, + ) where + A: Analysis<'tcx>, + { + let (from, to) = (*effects.start(), *effects.end()); + let terminator_index = block_data.statements.len(); + + assert!(from.statement_index <= terminator_index); + assert!(!to.precedes_in_backward_order(from)); + + // Handle the statement (or terminator) at `from`. + + let next_effect = match from.effect { + // If we need to apply the terminator effect in all or in part, do so now. + _ if from.statement_index == terminator_index => { + let location = Location { block, statement_index: from.statement_index }; + let terminator = block_data.terminator(); + + if from.effect == Effect::Before { + analysis.apply_before_terminator_effect(state, terminator, location); + if to == Effect::Before.at_index(terminator_index) { + return; + } + } + + analysis.apply_terminator_effect(state, terminator, location); + if to == Effect::Primary.at_index(terminator_index) { + return; + } + + // If `from.statement_index` is `0`, we will have hit one of the earlier comparisons + // with `to`. + from.statement_index - 1 + } + + Effect::Primary => { + let location = Location { block, statement_index: from.statement_index }; + let statement = &block_data.statements[from.statement_index]; + + analysis.apply_statement_effect(state, statement, location); + if to == Effect::Primary.at_index(from.statement_index) { + return; + } + + from.statement_index - 1 + } + + Effect::Before => from.statement_index, + }; + + // Handle all statements between `first_unapplied_idx` and `to.statement_index`. + + for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) { + let location = Location { block, statement_index }; + let statement = &block_data.statements[statement_index]; + analysis.apply_before_statement_effect(state, statement, location); + analysis.apply_statement_effect(state, statement, location); + } + + // Handle the statement at `to`. + + let location = Location { block, statement_index: to.statement_index }; + let statement = &block_data.statements[to.statement_index]; + analysis.apply_before_statement_effect(state, statement, location); + + if to.effect == Effect::Before { + return; + } + + analysis.apply_statement_effect(state, statement, location); + } + + fn visit_results_in_block( + state: &mut F, + block: BasicBlock, + block_data: &'mir mir::BasicBlockData<'tcx>, + results: &R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + ) where + R: ResultsVisitable<'tcx, FlowState = F>, + { + results.reset_to_block_entry(state, block); + + vis.visit_block_end(&state, block_data, block); + + // Terminator + let loc = Location { block, statement_index: block_data.statements.len() }; + let term = block_data.terminator(); + results.reconstruct_before_terminator_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(state, term, loc); + results.reconstruct_terminator_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(state, term, loc); + + for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { + let loc = Location { block, statement_index }; + results.reconstruct_before_statement_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(state, stmt, loc); + results.reconstruct_statement_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(state, stmt, loc); + } + + vis.visit_block_start(state, block_data, block); + } + + fn join_state_into_successors_of( + analysis: &A, + _tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + dead_unwinds: Option<&BitSet>, + exit_state: &mut BitSet, + (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), + mut propagate: impl FnMut(BasicBlock, &BitSet), + ) where + A: Analysis<'tcx>, + { + for pred in body.predecessors()[bb].iter().copied() { + match body[pred].terminator().kind { + // Apply terminator-specific edge effects. + // + // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally. + mir::TerminatorKind::Call { + destination: Some((return_place, dest)), + ref func, + ref args, + .. + } if dest == bb => { + let mut tmp = exit_state.clone(); + analysis.apply_call_return_effect(&mut tmp, pred, func, args, return_place); + propagate(pred, &tmp); + } + + mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => { + let mut tmp = exit_state.clone(); + analysis.apply_yield_resume_effect(&mut tmp, resume, resume_arg); + propagate(pred, &tmp); + } + + // Ignore dead unwinds. + mir::TerminatorKind::Call { cleanup: Some(unwind), .. } + | mir::TerminatorKind::Assert { cleanup: Some(unwind), .. } + | mir::TerminatorKind::Drop { unwind: Some(unwind), .. } + | mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. } + | mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. } + if unwind == bb => + { + if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { + propagate(pred, exit_state); + } + } + + _ => propagate(pred, exit_state), + } + } + } +} + +/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator). +pub struct Forward; + +impl Direction for Forward { + fn is_forward() -> bool { + true + } + + fn apply_effects_in_block( + analysis: &A, + state: &mut BitSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + ) where + A: Analysis<'tcx>, + { + for (statement_index, statement) in block_data.statements.iter().enumerate() { + let location = Location { block, statement_index }; + analysis.apply_before_statement_effect(state, statement, location); + analysis.apply_statement_effect(state, statement, location); + } + + let terminator = block_data.terminator(); + let location = Location { block, statement_index: block_data.statements.len() }; + analysis.apply_before_terminator_effect(state, terminator, location); + analysis.apply_terminator_effect(state, terminator, location); + } + + fn gen_kill_effects_in_block( + analysis: &A, + trans: &mut GenKillSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + ) where + A: GenKillAnalysis<'tcx>, + { + for (statement_index, statement) in block_data.statements.iter().enumerate() { + let location = Location { block, statement_index }; + analysis.before_statement_effect(trans, statement, location); + analysis.statement_effect(trans, statement, location); + } + + let terminator = block_data.terminator(); + let location = Location { block, statement_index: block_data.statements.len() }; + analysis.before_terminator_effect(trans, terminator, location); + analysis.terminator_effect(trans, terminator, location); + } + + fn apply_effects_in_range( + analysis: &A, + state: &mut BitSet, + block: BasicBlock, + block_data: &mir::BasicBlockData<'tcx>, + effects: RangeInclusive, + ) where + A: Analysis<'tcx>, + { + let (from, to) = (*effects.start(), *effects.end()); + let terminator_index = block_data.statements.len(); + + assert!(to.statement_index <= terminator_index); + assert!(!to.precedes_in_forward_order(from)); + + // If we have applied the before affect of the statement or terminator at `from` but not its + // after effect, do so now and start the loop below from the next statement. + + let first_unapplied_index = match from.effect { + Effect::Before => from.statement_index, + + Effect::Primary if from.statement_index == terminator_index => { + debug_assert_eq!(from, to); + + let location = Location { block, statement_index: terminator_index }; + let terminator = block_data.terminator(); + analysis.apply_terminator_effect(state, terminator, location); + return; + } + + Effect::Primary => { + let location = Location { block, statement_index: from.statement_index }; + let statement = &block_data.statements[from.statement_index]; + analysis.apply_statement_effect(state, statement, location); + + // If we only needed to apply the after effect of the statement at `idx`, we are done. + if from == to { + return; + } + + from.statement_index + 1 + } + }; + + // Handle all statements between `from` and `to` whose effects must be applied in full. + + for statement_index in first_unapplied_index..to.statement_index { + let location = Location { block, statement_index }; + let statement = &block_data.statements[statement_index]; + analysis.apply_before_statement_effect(state, statement, location); + analysis.apply_statement_effect(state, statement, location); + } + + // Handle the statement or terminator at `to`. + + let location = Location { block, statement_index: to.statement_index }; + if to.statement_index == terminator_index { + let terminator = block_data.terminator(); + analysis.apply_before_terminator_effect(state, terminator, location); + + if to.effect == Effect::Primary { + analysis.apply_terminator_effect(state, terminator, location); + } + } else { + let statement = &block_data.statements[to.statement_index]; + analysis.apply_before_statement_effect(state, statement, location); + + if to.effect == Effect::Primary { + analysis.apply_statement_effect(state, statement, location); + } + } + } + + fn visit_results_in_block( + state: &mut F, + block: BasicBlock, + block_data: &'mir mir::BasicBlockData<'tcx>, + results: &R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + ) where + R: ResultsVisitable<'tcx, FlowState = F>, + { + results.reset_to_block_entry(state, block); + + vis.visit_block_start(state, block_data, block); + + for (statement_index, stmt) in block_data.statements.iter().enumerate() { + let loc = Location { block, statement_index }; + results.reconstruct_before_statement_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(state, stmt, loc); + results.reconstruct_statement_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(state, stmt, loc); + } + + let loc = Location { block, statement_index: block_data.statements.len() }; + let term = block_data.terminator(); + results.reconstruct_before_terminator_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(state, term, loc); + results.reconstruct_terminator_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(state, term, loc); + + vis.visit_block_end(state, block_data, block); + } + + fn join_state_into_successors_of( + analysis: &A, + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + dead_unwinds: Option<&BitSet>, + exit_state: &mut BitSet, + (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), + mut propagate: impl FnMut(BasicBlock, &BitSet), + ) where + A: Analysis<'tcx>, + { + use mir::TerminatorKind::*; + match bb_data.terminator().kind { + Return | Resume | Abort | GeneratorDrop | Unreachable => {} + + Goto { target } => propagate(target, exit_state), + + Assert { target, cleanup: unwind, expected: _, msg: _, cond: _ } + | Drop { target, unwind, location: _ } + | DropAndReplace { target, unwind, value: _, location: _ } + | FalseUnwind { real_target: target, unwind } => { + if let Some(unwind) = unwind { + if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { + propagate(unwind, exit_state); + } + } + + propagate(target, exit_state); + } + + FalseEdges { real_target, imaginary_target } => { + propagate(real_target, exit_state); + propagate(imaginary_target, exit_state); + } + + Yield { resume: target, drop, resume_arg, value: _ } => { + if let Some(drop) = drop { + propagate(drop, exit_state); + } + + analysis.apply_yield_resume_effect(exit_state, target, resume_arg); + propagate(target, exit_state); + } + + Call { cleanup, destination, ref func, ref args, from_hir_call: _ } => { + if let Some(unwind) = cleanup { + if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { + propagate(unwind, exit_state); + } + } + + if let Some((dest_place, target)) = destination { + // N.B.: This must be done *last*, otherwise the unwind path will see the call + // return effect. + analysis.apply_call_return_effect(exit_state, bb, func, args, dest_place); + propagate(target, exit_state); + } + } + + InlineAsm { template: _, operands: _, options: _, destination } => { + if let Some(target) = destination { + propagate(target, exit_state); + } + } + + SwitchInt { ref targets, ref values, ref discr, switch_ty: _ } => { + let enum_ = discr + .place() + .and_then(|discr| switch_on_enum_discriminant(tcx, &body, bb_data, discr)); + match enum_ { + // If this is a switch on an enum discriminant, a custom effect may be applied + // along each outgoing edge. + Some((enum_place, enum_def)) => { + // MIR building adds discriminants to the `values` array in the same order as they + // are yielded by `AdtDef::discriminants`. We rely on this to match each + // discriminant in `values` to its corresponding variant in linear time. + let mut tmp = BitSet::new_empty(exit_state.domain_size()); + let mut discriminants = enum_def.discriminants(tcx); + for (value, target) in values.iter().zip(targets.iter().copied()) { + let (variant_idx, _) = + discriminants.find(|&(_, discr)| discr.val == *value).expect( + "Order of `AdtDef::discriminants` differed \ + from that of `SwitchInt::values`", + ); + + tmp.overwrite(exit_state); + analysis.apply_discriminant_switch_effect( + &mut tmp, + bb, + enum_place, + enum_def, + variant_idx, + ); + propagate(target, &tmp); + } + + // Move out of `tmp` so we don't accidentally use it below. + std::mem::drop(tmp); + + // Propagate dataflow state along the "otherwise" edge. + let otherwise = targets.last().copied().unwrap(); + propagate(otherwise, exit_state) + } + + // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same + // exit state. + None => { + for target in targets.iter().copied() { + propagate(target, exit_state); + } + } + } + } + } + } +} + +/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is +/// an enum discriminant. +/// +/// We expect such blocks to have a call to `discriminant` as their last statement like so: +/// _42 = discriminant(_1) +/// SwitchInt(_42, ..) +/// +/// If the basic block matches this pattern, this function returns the place corresponding to the +/// enum (`_1` in the example above) as well as the `AdtDef` of that enum. +fn switch_on_enum_discriminant( + tcx: TyCtxt<'tcx>, + body: &'mir mir::Body<'tcx>, + block: &'mir mir::BasicBlockData<'tcx>, + switch_on: mir::Place<'tcx>, +) -> Option<(mir::Place<'tcx>, &'tcx ty::AdtDef)> { + match block.statements.last().map(|stmt| &stmt.kind) { + Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))) + if *lhs == switch_on => + { + match &discriminated.ty(body, tcx).ty.kind { + ty::Adt(def, _) => Some((*discriminated, def)), + + // `Rvalue::Discriminant` is also used to get the active yield point for a + // generator, but we do not need edge-specific effects in that case. This may + // change in the future. + ty::Generator(..) => None, + + t => bug!("`discriminant` called on unexpected type {:?}", t), + } + } + + _ => None, + } +} diff --git a/src/librustc_mir/dataflow/framework/engine.rs b/src/librustc_mir/dataflow/framework/engine.rs new file mode 100644 index 0000000000000..32e569fdc3589 --- /dev/null +++ b/src/librustc_mir/dataflow/framework/engine.rs @@ -0,0 +1,410 @@ +//! A solver for dataflow problems. + +use std::ffi::OsString; +use std::fs; +use std::path::PathBuf; + +use rustc_ast::ast; +use rustc_data_structures::work_queue::WorkQueue; +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::{self, traversal, BasicBlock}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::symbol::{sym, Symbol}; + +use super::graphviz; +use super::{ + visit_results, Analysis, Direction, GenKillAnalysis, GenKillSet, ResultsCursor, ResultsVisitor, +}; +use crate::util::pretty::dump_enabled; + +/// A dataflow analysis that has converged to fixpoint. +pub struct Results<'tcx, A> +where + A: Analysis<'tcx>, +{ + pub analysis: A, + pub(super) entry_sets: IndexVec>, +} + +impl Results<'tcx, A> +where + A: Analysis<'tcx>, +{ + /// Creates a `ResultsCursor` that can inspect these `Results`. + pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> { + ResultsCursor::new(body, self) + } + + /// Gets the dataflow state for the given block. + pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet { + &self.entry_sets[block] + } + + pub fn visit_with( + &self, + body: &'mir mir::Body<'tcx>, + blocks: impl IntoIterator, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet>, + ) { + visit_results(body, blocks, self, vis) + } + + pub fn visit_in_rpo_with( + &self, + body: &'mir mir::Body<'tcx>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet>, + ) { + let blocks = mir::traversal::reverse_postorder(body); + visit_results(body, blocks.map(|(bb, _)| bb), self, vis) + } +} + +/// A solver for dataflow problems. +pub struct Engine<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + bits_per_block: usize, + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + def_id: DefId, + dead_unwinds: Option<&'a BitSet>, + entry_sets: IndexVec>, + analysis: A, + + /// Cached, cumulative transfer functions for each block. + trans_for_block: Option>>, +} + +impl Engine<'a, 'tcx, A> +where + A: GenKillAnalysis<'tcx>, +{ + /// Creates a new `Engine` to solve a gen-kill dataflow problem. + pub fn new_gen_kill( + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + def_id: DefId, + analysis: A, + ) -> Self { + // If there are no back-edges in the control-flow graph, we only ever need to apply the + // transfer function for each block exactly once (assuming that we process blocks in RPO). + // + // In this case, there's no need to compute the block transfer functions ahead of time. + if !body.is_cfg_cyclic() { + return Self::new(tcx, body, def_id, analysis, None); + } + + // Otherwise, compute and store the cumulative transfer function for each block. + + let bits_per_block = analysis.bits_per_block(body); + let mut trans_for_block = + IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks()); + + for (block, block_data) in body.basic_blocks().iter_enumerated() { + let trans = &mut trans_for_block[block]; + A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data); + } + + Self::new(tcx, body, def_id, analysis, Some(trans_for_block)) + } +} + +impl Engine<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer + /// function. + /// + /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for + /// better performance. + pub fn new_generic( + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + def_id: DefId, + analysis: A, + ) -> Self { + Self::new(tcx, body, def_id, analysis, None) + } + + fn new( + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + def_id: DefId, + analysis: A, + trans_for_block: Option>>, + ) -> Self { + let bits_per_block = analysis.bits_per_block(body); + + let bottom_value_set = if A::BOTTOM_VALUE { + BitSet::new_filled(bits_per_block) + } else { + BitSet::new_empty(bits_per_block) + }; + + let mut entry_sets = IndexVec::from_elem(bottom_value_set.clone(), body.basic_blocks()); + analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); + + if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value_set { + bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); + } + + Engine { + analysis, + bits_per_block, + tcx, + body, + def_id, + dead_unwinds: None, + entry_sets, + trans_for_block, + } + } + + /// Signals that we do not want dataflow state to propagate across unwind edges for these + /// `BasicBlock`s. + /// + /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually + /// unwind during execution. Otherwise, your dataflow results will not be correct. + pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet) -> Self { + self.dead_unwinds = Some(dead_unwinds); + self + } + + /// Computes the fixpoint for this dataflow problem and returns it. + pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> { + let Engine { + analysis, + bits_per_block, + body, + dead_unwinds, + def_id, + mut entry_sets, + tcx, + trans_for_block, + .. + } = self; + + let mut dirty_queue: WorkQueue = + WorkQueue::with_none(body.basic_blocks().len()); + + if A::Direction::is_forward() { + for (bb, _) in traversal::reverse_postorder(body) { + dirty_queue.insert(bb); + } + } else { + // Reverse post-order on the reverse CFG may generate a better iteration order for + // backward dataflow analyses, but probably not enough to matter. + for (bb, _) in traversal::postorder(body) { + dirty_queue.insert(bb); + } + } + + // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will + // be processed after the ones added above. + // + // FIXME(ecstaticmorse): Is this actually necessary? In principle, we shouldn't need to + // know the dataflow state in unreachable basic blocks. + for bb in body.basic_blocks().indices() { + dirty_queue.insert(bb); + } + + let mut state = BitSet::new_empty(bits_per_block); + while let Some(bb) = dirty_queue.pop() { + let bb_data = &body[bb]; + + // Apply the block transfer function, using the cached one if it exists. + state.overwrite(&entry_sets[bb]); + match &trans_for_block { + Some(trans_for_block) => trans_for_block[bb].apply(&mut state), + None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), + } + + A::Direction::join_state_into_successors_of( + &analysis, + tcx, + body, + dead_unwinds, + &mut state, + (bb, bb_data), + |target: BasicBlock, state: &BitSet| { + let set_changed = analysis.join(&mut entry_sets[target], state); + if set_changed { + dirty_queue.insert(target); + } + }, + ); + } + + let results = Results { analysis, entry_sets }; + + let res = write_graphviz_results(tcx, def_id, &body, &results, trans_for_block); + if let Err(e) = res { + warn!("Failed to write graphviz dataflow results: {}", e); + } + + results + } +} + +// Graphviz + +/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via +/// `rustc_mir` attributes. +fn write_graphviz_results( + tcx: TyCtxt<'tcx>, + def_id: DefId, + body: &mir::Body<'tcx>, + results: &Results<'tcx, A>, + block_transfer_functions: Option>>, +) -> std::io::Result<()> +where + A: Analysis<'tcx>, +{ + let attrs = match RustcMirAttrs::parse(tcx, def_id) { + Ok(attrs) => attrs, + + // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` + Err(()) => return Ok(()), + }; + + let path = match attrs.output_path(A::NAME) { + Some(path) => path, + + None if tcx.sess.opts.debugging_opts.dump_mir_dataflow + && dump_enabled(tcx, A::NAME, def_id) => + { + let mut path = PathBuf::from(&tcx.sess.opts.debugging_opts.dump_mir_dir); + + let item_name = ty::print::with_forced_impl_filename_line(|| { + tcx.def_path(def_id).to_filename_friendly_no_crate() + }); + path.push(format!("rustc.{}.{}.dot", item_name, A::NAME)); + path + } + + None => return Ok(()), + }; + + let bits_per_block = results.analysis.bits_per_block(body); + + let mut formatter: Box> = match attrs.formatter { + Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)), + Some(sym::gen_kill) => { + if let Some(trans_for_block) = block_transfer_functions { + Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block)) + } else { + Box::new(graphviz::SimpleDiff::new(body, &results)) + } + } + + // Default to the `SimpleDiff` output style. + _ => Box::new(graphviz::SimpleDiff::new(body, &results)), + }; + + debug!("printing dataflow results for {:?} to {}", def_id, path.display()); + let mut buf = Vec::new(); + + let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter); + dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?; + + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + fs::write(&path, buf)?; + + Ok(()) +} + +#[derive(Default)] +struct RustcMirAttrs { + basename_and_suffix: Option, + formatter: Option, +} + +impl RustcMirAttrs { + fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result { + let attrs = tcx.get_attrs(def_id); + + let mut result = Ok(()); + let mut ret = RustcMirAttrs::default(); + + let rustc_mir_attrs = attrs + .iter() + .filter(|attr| attr.check_name(sym::rustc_mir)) + .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); + + for attr in rustc_mir_attrs { + let attr_result = if attr.check_name(sym::borrowck_graphviz_postflow) { + 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.sess.span_err(attr.span(), "path must end in a filename"); + Err(()) + } + } + }) + } else if attr.check_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.sess.span_err(attr.span(), "unknown formatter"); + Err(()) + } + }) + } else { + Ok(()) + }; + + result = result.and(attr_result); + } + + result.map(|()| ret) + } + + fn set_field( + field: &mut Option, + tcx: TyCtxt<'tcx>, + attr: &ast::NestedMetaItem, + mapper: impl FnOnce(Symbol) -> Result, + ) -> Result<(), ()> { + if field.is_some() { + tcx.sess + .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty())); + + return Err(()); + } + + if let Some(s) = attr.value_str() { + *field = Some(mapper(s)?); + Ok(()) + } else { + tcx.sess + .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty())); + Err(()) + } + } + + /// Returns the path where dataflow results should be written, or `None` + /// `borrowck_graphviz_postflow` was not specified. + /// + /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`: + /// + /// "path/suffix.dot" -> "path/analysis_name_suffix.dot" + fn output_path(&self, analysis_name: &str) -> Option { + let mut ret = self.basename_and_suffix.as_ref().cloned()?; + let suffix = ret.file_name().unwrap(); // Checked when parsing attrs + + let mut file_name: OsString = analysis_name.into(); + file_name.push("_"); + file_name.push(suffix); + ret.set_file_name(file_name); + + Some(ret) + } +} diff --git a/src/librustc_mir/dataflow/framework/graphviz.rs b/src/librustc_mir/dataflow/framework/graphviz.rs new file mode 100644 index 0000000000000..e3ba26eaf8b37 --- /dev/null +++ b/src/librustc_mir/dataflow/framework/graphviz.rs @@ -0,0 +1,739 @@ +//! A helpful diagram for debugging dataflow problems. + +use std::cell::RefCell; +use std::{io, ops, str}; + +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::{self, BasicBlock, Body, Location}; + +use super::{Analysis, Direction, GenKillSet, Results, ResultsRefCursor}; +use crate::util::graphviz_safe_def_name; + +pub struct Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + body: &'a Body<'tcx>, + def_id: DefId, + + // This must be behind a `RefCell` because `dot::Labeller` takes `&self`. + block_formatter: RefCell>, +} + +impl Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + pub fn new( + body: &'a Body<'tcx>, + def_id: DefId, + results: &'a Results<'tcx, A>, + state_formatter: &'a mut dyn StateFormatter<'tcx, A>, + ) -> Self { + let block_formatter = BlockFormatter { + bg: Background::Light, + results: ResultsRefCursor::new(body, results), + state_formatter, + }; + + Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) } + } +} + +/// A pair of a basic block and an index into that basic blocks `successors`. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct CfgEdge { + source: BasicBlock, + index: usize, +} + +fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec { + body[bb] + .terminator() + .successors() + .enumerate() + .map(|(index, _)| CfgEdge { source: bb, index }) + .collect() +} + +impl dot::Labeller<'_> for Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + type Node = BasicBlock; + type Edge = CfgEdge; + + fn graph_id(&self) -> dot::Id<'_> { + let name = graphviz_safe_def_name(self.def_id); + dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() + } + + fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { + dot::Id::new(format!("bb_{}", n.index())).unwrap() + } + + fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { + let mut label = Vec::new(); + self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap(); + dot::LabelText::html(String::from_utf8(label).unwrap()) + } + + fn node_shape(&self, _n: &Self::Node) -> Option> { + Some(dot::LabelText::label("none")) + } + + fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> { + let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index]; + dot::LabelText::label(label.clone()) + } +} + +impl dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + type Node = BasicBlock; + type Edge = CfgEdge; + + fn nodes(&self) -> dot::Nodes<'_, Self::Node> { + self.body.basic_blocks().indices().collect::>().into() + } + + fn edges(&self) -> dot::Edges<'_, Self::Edge> { + self.body + .basic_blocks() + .indices() + .flat_map(|bb| dataflow_successors(self.body, bb)) + .collect::>() + .into() + } + + fn source(&self, edge: &Self::Edge) -> Self::Node { + edge.source + } + + fn target(&self, edge: &Self::Edge) -> Self::Node { + self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap() + } +} + +struct BlockFormatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + results: ResultsRefCursor<'a, 'a, 'tcx, A>, + bg: Background, + state_formatter: &'a mut dyn StateFormatter<'tcx, A>, +} + +impl BlockFormatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + const HEADER_COLOR: &'static str = "#a0a0a0"; + + fn num_state_columns(&self) -> usize { + std::cmp::max(1, self.state_formatter.column_names().len()) + } + + fn toggle_background(&mut self) -> Background { + let bg = self.bg; + self.bg = !bg; + bg + } + + fn write_node_label( + &mut self, + w: &mut impl io::Write, + body: &'a Body<'tcx>, + block: BasicBlock, + ) -> io::Result<()> { + // Sample output: + // +-+-----------------------------------------------+ + // A | bb4 | + // +-+----------------------------------+------------+ + // B | MIR | STATE | + // +-+----------------------------------+------------+ + // C | | (on entry) | {_0,_2,_3} | + // +-+----------------------------------+------------+ + // D |0| StorageLive(_7) | | + // +-+----------------------------------+------------+ + // |1| StorageLive(_8) | | + // +-+----------------------------------+------------+ + // |2| _8 = &mut _1 | +_8 | + // +-+----------------------------------+------------+ + // E |T| _4 = const Foo::twiddle(move _2) | -_2 | + // +-+----------------------------------+------------+ + // F | | (on unwind) | {_0,_3,_8} | + // +-+----------------------------------+------------+ + // | | (on successful return) | +_4 | + // +-+----------------------------------+------------+ + + // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their + // children. This is because `xdot` seemed to have a hard time correctly propagating + // attributes. Make sure to test the output before trying to remove the redundancy. + // Notably, `align` was found to have no effect when applied only to . + + let table_fmt = concat!( + " border=\"1\"", + " cellborder=\"1\"", + " cellspacing=\"0\"", + " cellpadding=\"3\"", + " sides=\"rb\"", + ); + write!(w, r#""#, fmt = table_fmt)?; + + // A + B: Block header + if self.state_formatter.column_names().is_empty() { + self.write_block_header_simple(w, block)?; + } else { + self.write_block_header_with_state_columns(w, block)?; + } + + // C: State at start of block + self.bg = Background::Light; + self.results.seek_to_block_start(block); + let block_entry_state = self.results.get().clone(); + + self.write_row_with_full_state(w, "", "(on start)")?; + + // D: Statement transfer functions + for (i, statement) in body[block].statements.iter().enumerate() { + let location = Location { block, statement_index: i }; + let statement_str = format!("{:?}", statement); + self.write_row_for_location(w, &i.to_string(), &statement_str, location)?; + } + + // E: Terminator transfer function + let terminator = body[block].terminator(); + let terminator_loc = body.terminator_loc(block); + let mut terminator_str = String::new(); + terminator.kind.fmt_head(&mut terminator_str).unwrap(); + + self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?; + + // F: State at end of block + + // Write the full dataflow state immediately after the terminator if it differs from the + // state at block entry. + self.results.seek_to_block_end(block); + if self.results.get() != &block_entry_state || A::Direction::is_backward() { + let after_terminator_name = match terminator.kind { + mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)", + _ => "(on end)", + }; + + self.write_row_with_full_state(w, "", after_terminator_name)?; + } + + // Write any changes caused by terminator-specific effects + let num_state_columns = self.num_state_columns(); + match terminator.kind { + mir::TerminatorKind::Call { + destination: Some((return_place, _)), + ref func, + ref args, + .. + } => { + self.write_row(w, "", "(on successful return)", |this, w, fmt| { + write!( + w, + r#"") + })?; + } + + mir::TerminatorKind::Yield { resume, resume_arg, .. } => { + self.write_row(w, "", "(on yield resume)", |this, w, fmt| { + write!( + w, + r#"") + })?; + } + + _ => {} + }; + + write!(w, "
"#, + colspan = num_state_columns, + fmt = fmt, + )?; + + let state_on_unwind = this.results.get().clone(); + this.results.apply_custom_effect(|analysis, state| { + analysis.apply_call_return_effect(state, block, func, args, return_place); + }); + + write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?; + write!(w, ""#, + colspan = num_state_columns, + fmt = fmt, + )?; + + let state_on_generator_drop = this.results.get().clone(); + this.results.apply_custom_effect(|analysis, state| { + analysis.apply_yield_resume_effect(state, resume, resume_arg); + }); + + write_diff( + w, + this.results.analysis(), + &state_on_generator_drop, + this.results.get(), + )?; + write!(w, "
") + } + + fn write_block_header_simple( + &mut self, + w: &mut impl io::Write, + block: BasicBlock, + ) -> io::Result<()> { + // +-------------------------------------------------+ + // A | bb4 | + // +-----------------------------------+-------------+ + // B | MIR | STATE | + // +-+---------------------------------+-------------+ + // | | ... | | + + // A + write!( + w, + concat!("", r#"bb{block_id}"#, "",), + block_id = block.index(), + )?; + + // B + write!( + w, + concat!( + "", + r#"MIR"#, + r#"STATE"#, + "", + ), + fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR), + ) + } + + fn write_block_header_with_state_columns( + &mut self, + w: &mut impl io::Write, + block: BasicBlock, + ) -> io::Result<()> { + // +------------------------------------+-------------+ + // A | bb4 | STATE | + // +------------------------------------+------+------+ + // B | MIR | GEN | KILL | + // +-+----------------------------------+------+------+ + // | | ... | | | + + let state_column_names = self.state_formatter.column_names(); + + // A + write!( + w, + concat!( + "", + r#"bb{block_id}"#, + r#"STATE"#, + "", + ), + fmt = "sides=\"tl\"", + num_state_cols = state_column_names.len(), + block_id = block.index(), + )?; + + // B + let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR); + write!(w, concat!("", r#"MIR"#,), fmt = fmt,)?; + + for name in state_column_names { + write!(w, "{name}", fmt = fmt, name = name)?; + } + + write!(w, "") + } + + /// Write a row with the given index and MIR, using the function argument to fill in the + /// "STATE" column(s). + fn write_row( + &mut self, + w: &mut W, + i: &str, + mir: &str, + f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>, + ) -> io::Result<()> { + let bg = self.toggle_background(); + let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" }; + + let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr()); + + write!( + w, + concat!( + "", + r#"{i}"#, + r#"{mir}"#, + ), + i = i, + fmt = fmt, + mir = dot::escape_html(mir), + )?; + + f(self, w, &fmt)?; + write!(w, "") + } + + fn write_row_with_full_state( + &mut self, + w: &mut impl io::Write, + i: &str, + mir: &str, + ) -> io::Result<()> { + self.write_row(w, i, mir, |this, w, fmt| { + let state = this.results.get(); + let analysis = this.results.analysis(); + + write!( + w, + r#"{{"#, + colspan = this.num_state_columns(), + fmt = fmt, + )?; + pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?; + write!(w, "}}") + }) + } + + fn write_row_for_location( + &mut self, + w: &mut impl io::Write, + i: &str, + mir: &str, + location: Location, + ) -> io::Result<()> { + self.write_row(w, i, mir, |this, w, fmt| { + this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location) + }) + } +} + +/// Controls what gets printed under the `STATE` header. +pub trait StateFormatter<'tcx, A> +where + A: Analysis<'tcx>, +{ + /// The columns that will get printed under `STATE`. + fn column_names(&self) -> &[&str]; + + fn write_state_for_location( + &mut self, + w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()>; +} + +/// Prints a single column containing the state vector immediately *after* each statement. +pub struct SimpleDiff<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + prev_state: ResultsRefCursor<'a, 'a, 'tcx, A>, +} + +impl
SimpleDiff<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>) -> Self { + SimpleDiff { prev_state: ResultsRefCursor::new(body, results) } + } +} + +impl StateFormatter<'tcx, A> for SimpleDiff<'_, 'tcx, A> +where + A: Analysis<'tcx>, +{ + fn column_names(&self) -> &[&str] { + &[] + } + + fn write_state_for_location( + &mut self, + mut w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()> { + if A::Direction::is_forward() { + if location.statement_index == 0 { + self.prev_state.seek_to_block_start(location.block); + } else { + self.prev_state.seek_after_primary_effect(Location { + statement_index: location.statement_index - 1, + ..location + }); + } + } else { + if location == results.body().terminator_loc(location.block) { + self.prev_state.seek_to_block_end(location.block); + } else { + self.prev_state.seek_after_primary_effect(location.successor_within_block()); + } + } + + write!(w, r#""#, fmt = fmt)?; + results.seek_after_primary_effect(location); + let curr_state = results.get(); + write_diff(&mut w, results.analysis(), self.prev_state.get(), curr_state)?; + write!(w, "") + } +} + +/// Prints two state columns, one containing only the "before" effect of each statement and one +/// containing the full effect. +pub struct TwoPhaseDiff { + prev_state: BitSet, + prev_loc: Location, +} + +impl TwoPhaseDiff { + pub fn new(bits_per_block: usize) -> Self { + TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START } + } +} + +impl StateFormatter<'tcx, A> for TwoPhaseDiff +where + A: Analysis<'tcx>, +{ + fn column_names(&self) -> &[&str] { + &["BEFORE", " AFTER"] + } + + fn write_state_for_location( + &mut self, + mut w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()> { + if location.statement_index == 0 { + results.seek_to_block_entry(location.block); + self.prev_state.overwrite(results.get()); + } else { + // Ensure that we are visiting statements in order, so `prev_state` is correct. + assert_eq!(self.prev_loc.successor_within_block(), location); + } + + self.prev_loc = location; + + // Before + + write!(w, r#""#, fmt = fmt)?; + results.seek_before_primary_effect(location); + let curr_state = results.get(); + write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; + self.prev_state.overwrite(curr_state); + write!(w, "")?; + + // After + + write!(w, r#""#, fmt = fmt)?; + results.seek_after_primary_effect(location); + let curr_state = results.get(); + write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; + self.prev_state.overwrite(curr_state); + write!(w, "") + } +} + +/// Prints the gen/kill set for the entire block. +pub struct BlockTransferFunc<'a, 'tcx, T: Idx> { + body: &'a mir::Body<'tcx>, + trans_for_block: IndexVec>, +} + +impl BlockTransferFunc<'mir, 'tcx, T> { + pub fn new( + body: &'mir mir::Body<'tcx>, + trans_for_block: IndexVec>, + ) -> Self { + BlockTransferFunc { body, trans_for_block } + } +} + +impl StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx> +where + A: Analysis<'tcx>, +{ + fn column_names(&self) -> &[&str] { + &["GEN", "KILL"] + } + + fn write_state_for_location( + &mut self, + mut w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()> { + // Only print a single row. + if location.statement_index != 0 { + return Ok(()); + } + + let block_trans = &self.trans_for_block[location.block]; + let rowspan = self.body.basic_blocks()[location.block].statements.len(); + + for set in &[&block_trans.gen, &block_trans.kill] { + write!( + w, + r#""#, + fmt = fmt, + rowspan = rowspan + )?; + + pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?; + write!(w, "")?; + } + + Ok(()) + } +} + +/// Writes two lines, one containing the added bits and one the removed bits. +fn write_diff>( + w: &mut impl io::Write, + analysis: &A, + from: &BitSet, + to: &BitSet, +) -> io::Result<()> { + assert_eq!(from.domain_size(), to.domain_size()); + let len = from.domain_size(); + + let mut set = HybridBitSet::new_empty(len); + let mut clear = HybridBitSet::new_empty(len); + + // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets. + for i in (0..len).map(A::Idx::new) { + match (from.contains(i), to.contains(i)) { + (false, true) => set.insert(i), + (true, false) => clear.insert(i), + _ => continue, + }; + } + + if !set.is_empty() { + write!(w, r#"+"#)?; + pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?; + write!(w, r#""#)?; + } + + if !set.is_empty() && !clear.is_empty() { + write!(w, "{}", BR_LEFT)?; + } + + if !clear.is_empty() { + write!(w, r#"-"#)?; + pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?; + write!(w, r#""#)?; + } + + Ok(()) +} + +const BR_LEFT: &str = r#"
"#; +const BR_LEFT_SPACE: &str = r#"
"#; + +/// Line break policy that breaks at 40 characters and starts the next line with a single space. +const LIMIT_30_ALIGN_1: Option = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }); + +struct LineBreak { + sequence: &'static str, + limit: usize, +} + +/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given +/// separator (`sep`). +/// +/// Optionally, it will break lines using the given character sequence (usually `
`) and +/// character limit. +fn pretty_print_state_elems
( + w: &mut impl io::Write, + analysis: &A, + elems: impl Iterator, + sep: &str, + line_break: Option, +) -> io::Result +where + A: Analysis<'tcx>, +{ + let sep_width = sep.chars().count(); + + let mut buf = Vec::new(); + + let mut first = true; + let mut curr_line_width = 0; + let mut line_break_inserted = false; + + for idx in elems { + buf.clear(); + analysis.pretty_print_idx(&mut buf, idx)?; + let idx_str = + str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8"); + let escaped = dot::escape_html(idx_str); + let escaped_width = escaped.chars().count(); + + if first { + first = false; + } else { + write!(w, "{}", sep)?; + curr_line_width += sep_width; + + if let Some(line_break) = &line_break { + if curr_line_width + sep_width + escaped_width > line_break.limit { + write!(w, "{}", line_break.sequence)?; + line_break_inserted = true; + curr_line_width = 0; + } + } + } + + write!(w, "{}", escaped)?; + curr_line_width += escaped_width; + } + + Ok(line_break_inserted) +} + +/// The background color used for zebra-striping the table. +#[derive(Clone, Copy)] +enum Background { + Light, + Dark, +} + +impl Background { + fn attr(self) -> &'static str { + match self { + Self::Dark => "bgcolor=\"#f0f0f0\"", + Self::Light => "", + } + } +} + +impl ops::Not for Background { + type Output = Self; + + fn not(self) -> Self { + match self { + Self::Light => Self::Dark, + Self::Dark => Self::Light, + } + } +} diff --git a/src/librustc_mir/dataflow/framework/mod.rs b/src/librustc_mir/dataflow/framework/mod.rs new file mode 100644 index 0000000000000..a21bbacb46766 --- /dev/null +++ b/src/librustc_mir/dataflow/framework/mod.rs @@ -0,0 +1,556 @@ +//! A framework that can express both [gen-kill] and generic dataflow problems. +//! +//! To actually use this framework, you must implement either the `Analysis` or the +//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill +//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The +//! `impls` module contains several examples of gen/kill dataflow analyses. +//! +//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait, +//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the +//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use +//! `visit_results`. The following example uses the `ResultsCursor` approach. +//! +//! ```ignore(cross-crate-imports) +//! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available. +//! +//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) { +//! let analysis = MyAnalysis::new() +//! .into_engine(tcx, body, did) +//! .iterate_to_fixpoint() +//! .into_results_cursor(body); +//! +//! // Print the dataflow state *after* each statement in the start block. +//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() { +//! cursor.seek_after(Location { block: START_BLOCK, statement_index }); +//! let state = cursor.get(); +//! println!("{:?}", state); +//! } +//! } +//! ``` +//! +//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems + +use std::cmp::Ordering; +use std::io; + +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_index::vec::Idx; +use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_target::abi::VariantIdx; + +mod cursor; +mod direction; +mod engine; +mod graphviz; +mod visitor; + +pub use self::cursor::{ResultsCursor, ResultsRefCursor}; +pub use self::direction::{Backward, Direction, Forward}; +pub use self::engine::{Engine, Results}; +pub use self::visitor::{visit_results, ResultsVisitor}; +pub use self::visitor::{BorrowckFlowState, BorrowckResults}; + +/// Parameterization for the precise form of data flow that is used. +/// +/// `BottomValue` determines whether the initial entry set for each basic block is empty or full. +/// This also determines the semantics of the lattice `join` operator used to merge dataflow +/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed +/// point. +/// +/// This means, for propagation across the graph, that you either want to start at all-zeroes and +/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect +/// as your merge when propagating. +pub trait BottomValue { + /// Specifies the initial value for each bit in the entry set for each basic block. + const BOTTOM_VALUE: bool; + + /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed. + /// + /// It is almost certainly wrong to override this, since it automatically applies + /// * `inout_set & in_set` if `BOTTOM_VALUE == true` + /// * `inout_set | in_set` if `BOTTOM_VALUE == false` + /// + /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks. + /// For clarity, the above statement again from a different perspective: + /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is + /// `!BOTTOM_VALUE`. + /// + /// There are situations where you want the opposite behaviour: propagate only if *all* + /// predecessor blocks's value is `!BOTTOM_VALUE`. + /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This + /// means that all code paths leading to the location must have set the bit, instead of any + /// code path leading there. + /// + /// If you want this kind of "definitely set" analysis, you need to + /// 1. Invert `BOTTOM_VALUE` + /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE` + /// 3. Override `join` to do the opposite from what it's doing now. + #[inline] + fn join(&self, inout_set: &mut BitSet, in_set: &BitSet) -> bool { + if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) } + } +} + +/// Define the domain of a dataflow problem. +/// +/// This trait specifies the lattice on which this analysis operates. For now, this must be a +/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet` +/// and referred to as the state vector. +/// +/// This trait also defines the initial value for the dataflow state upon entry to the +/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging. +pub trait AnalysisDomain<'tcx>: BottomValue { + /// The type of the elements in the state vector. + type Idx: Idx; + + /// The direction of this analyis. Either `Forward` or `Backward`. + type Direction: Direction = Forward; + + /// A descriptive name for this analysis. Used only for debugging. + /// + /// This name should be brief and contain no spaces, periods or other characters that are not + /// suitable as part of a filename. + const NAME: &'static str; + + /// The size of the state vector. + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize; + + /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow + /// analysis. + /// + /// For backward analyses, initial state besides the bottom value is not yet supported. Trying + /// to mutate the initial state will result in a panic. + // + // FIXME: For backward dataflow analyses, the initial state should be applied to every basic + // block where control flow could exit the MIR body (e.g., those terminated with `return` or + // `resume`). It's not obvious how to handle `yield` points in generators, however. + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet); + + /// Prints an element in the state vector for debugging. + fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> { + write!(w, "{:?}", idx) + } +} + +/// A dataflow problem with an arbitrarily complex transfer function. +pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { + /// Updates the current dataflow state with the effect of evaluating a statement. + fn apply_statement_effect( + &self, + state: &mut BitSet, + statement: &mir::Statement<'tcx>, + location: Location, + ); + + /// Updates the current dataflow state with an effect that occurs immediately *before* the + /// given statement. + /// + /// This method is useful if the consumer of the results of this analysis needs only to observe + /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, + /// analyses should not implement this without implementing `apply_statement_effect`. + fn apply_before_statement_effect( + &self, + _state: &mut BitSet, + _statement: &mir::Statement<'tcx>, + _location: Location, + ) { + } + + /// Updates the current dataflow state with the effect of evaluating a terminator. + /// + /// The effect of a successful return from a `Call` terminator should **not** be accounted for + /// in this function. That should go in `apply_call_return_effect`. For example, in the + /// `InitializedPlaces` analyses, the return place for a function call is not marked as + /// initialized here. + fn apply_terminator_effect( + &self, + state: &mut BitSet, + terminator: &mir::Terminator<'tcx>, + location: Location, + ); + + /// Updates the current dataflow state with an effect that occurs immediately *before* the + /// given terminator. + /// + /// This method is useful if the consumer of the results of this analysis needs only to observe + /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, + /// analyses should not implement this without implementing `apply_terminator_effect`. + fn apply_before_terminator_effect( + &self, + _state: &mut BitSet, + _terminator: &mir::Terminator<'tcx>, + _location: Location, + ) { + } + + /// Updates the current dataflow state with the effect of a successful return from a `Call` + /// terminator. + /// + /// This is separate from `apply_terminator_effect` to properly track state across unwind + /// edges. + fn apply_call_return_effect( + &self, + state: &mut BitSet, + block: BasicBlock, + func: &mir::Operand<'tcx>, + args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, + ); + + /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator. + /// + /// This is similar to `apply_call_return_effect` in that it only takes place after the + /// generator is resumed, not when it is dropped. + /// + /// By default, no effects happen. + fn apply_yield_resume_effect( + &self, + _state: &mut BitSet, + _resume_block: BasicBlock, + _resume_place: mir::Place<'tcx>, + ) { + } + + /// Updates the current dataflow state with the effect of taking a particular branch in a + /// `SwitchInt` terminator. + /// + /// Much like `apply_call_return_effect`, this effect is only propagated along a single + /// outgoing edge from this basic block. + /// + /// FIXME: This class of effects is not supported for backward dataflow analyses. + fn apply_discriminant_switch_effect( + &self, + _state: &mut BitSet, + _block: BasicBlock, + _enum_place: mir::Place<'tcx>, + _adt: &ty::AdtDef, + _variant: VariantIdx, + ) { + } + + /// Creates an `Engine` to find the fixpoint for this dataflow problem. + /// + /// You shouldn't need to override this outside this module, since the combination of the + /// default impl and the one for all `A: GenKillAnalysis` will do the right thing. + /// Its purpose is to enable method chaining like so: + /// + /// ```ignore(cross-crate-imports) + /// let results = MyAnalysis::new(tcx, body) + /// .into_engine(tcx, body, def_id) + /// .iterate_to_fixpoint() + /// .into_results_cursor(body); + /// ``` + fn into_engine( + self, + tcx: TyCtxt<'tcx>, + body: &'mir mir::Body<'tcx>, + def_id: DefId, + ) -> Engine<'mir, 'tcx, Self> + where + Self: Sized, + { + Engine::new_generic(tcx, body, def_id, self) + } +} + +/// A gen/kill dataflow problem. +/// +/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only +/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer +/// functions for each statement in this way, the transfer function for an entire basic block can +/// be computed efficiently. +/// +/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`. +pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { + /// See `Analysis::apply_statement_effect`. + fn statement_effect( + &self, + trans: &mut impl GenKill, + statement: &mir::Statement<'tcx>, + location: Location, + ); + + /// See `Analysis::apply_before_statement_effect`. + fn before_statement_effect( + &self, + _trans: &mut impl GenKill, + _statement: &mir::Statement<'tcx>, + _location: Location, + ) { + } + + /// See `Analysis::apply_terminator_effect`. + fn terminator_effect( + &self, + trans: &mut impl GenKill, + terminator: &mir::Terminator<'tcx>, + location: Location, + ); + + /// See `Analysis::apply_before_terminator_effect`. + fn before_terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _location: Location, + ) { + } + + /// See `Analysis::apply_call_return_effect`. + fn call_return_effect( + &self, + trans: &mut impl GenKill, + block: BasicBlock, + func: &mir::Operand<'tcx>, + args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, + ); + + /// See `Analysis::apply_yield_resume_effect`. + fn yield_resume_effect( + &self, + _trans: &mut impl GenKill, + _resume_block: BasicBlock, + _resume_place: mir::Place<'tcx>, + ) { + } + + /// See `Analysis::apply_discriminant_switch_effect`. + fn discriminant_switch_effect( + &self, + _state: &mut impl GenKill, + _block: BasicBlock, + _enum_place: mir::Place<'tcx>, + _adt: &ty::AdtDef, + _variant: VariantIdx, + ) { + } +} + +impl Analysis<'tcx> for A +where + A: GenKillAnalysis<'tcx>, +{ + fn apply_statement_effect( + &self, + state: &mut BitSet, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + self.statement_effect(state, statement, location); + } + + fn apply_before_statement_effect( + &self, + state: &mut BitSet, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + self.before_statement_effect(state, statement, location); + } + + fn apply_terminator_effect( + &self, + state: &mut BitSet, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + self.terminator_effect(state, terminator, location); + } + + fn apply_before_terminator_effect( + &self, + state: &mut BitSet, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + self.before_terminator_effect(state, terminator, location); + } + + fn apply_call_return_effect( + &self, + state: &mut BitSet, + block: BasicBlock, + func: &mir::Operand<'tcx>, + args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, + ) { + self.call_return_effect(state, block, func, args, return_place); + } + + fn apply_yield_resume_effect( + &self, + state: &mut BitSet, + resume_block: BasicBlock, + resume_place: mir::Place<'tcx>, + ) { + self.yield_resume_effect(state, resume_block, resume_place); + } + + fn apply_discriminant_switch_effect( + &self, + state: &mut BitSet, + block: BasicBlock, + enum_place: mir::Place<'tcx>, + adt: &ty::AdtDef, + variant: VariantIdx, + ) { + self.discriminant_switch_effect(state, block, enum_place, adt, variant); + } + + fn into_engine( + self, + tcx: TyCtxt<'tcx>, + body: &'mir mir::Body<'tcx>, + def_id: DefId, + ) -> Engine<'mir, 'tcx, Self> + where + Self: Sized, + { + Engine::new_gen_kill(tcx, body, def_id, self) + } +} + +/// The legal operations for a transfer function in a gen/kill problem. +/// +/// This abstraction exists because there are two different contexts in which we call the methods in +/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently +/// applied multiple times, such as when computing the cumulative transfer function for each block. +/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes, +/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the +/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to +/// building up a `GenKillSet` and then throwing it away. +pub trait GenKill { + /// Inserts `elem` into the state vector. + fn gen(&mut self, elem: T); + + /// Removes `elem` from the state vector. + fn kill(&mut self, elem: T); + + /// Calls `gen` for each element in `elems`. + fn gen_all(&mut self, elems: impl IntoIterator) { + for elem in elems { + self.gen(elem); + } + } + + /// Calls `kill` for each element in `elems`. + fn kill_all(&mut self, elems: impl IntoIterator) { + for elem in elems { + self.kill(elem); + } + } +} + +/// Stores a transfer function for a gen/kill problem. +/// +/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be +/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for +/// the same element, the most recent one takes precedence. +#[derive(Clone)] +pub struct GenKillSet { + gen: HybridBitSet, + kill: HybridBitSet, +} + +impl GenKillSet { + /// Creates a new transfer function that will leave the dataflow state unchanged. + pub fn identity(universe: usize) -> Self { + GenKillSet { + gen: HybridBitSet::new_empty(universe), + kill: HybridBitSet::new_empty(universe), + } + } + + /// Applies this transfer function to the given state vector. + pub fn apply(&self, state: &mut BitSet) { + state.union(&self.gen); + state.subtract(&self.kill); + } +} + +impl GenKill for GenKillSet { + fn gen(&mut self, elem: T) { + self.gen.insert(elem); + self.kill.remove(elem); + } + + fn kill(&mut self, elem: T) { + self.kill.insert(elem); + self.gen.remove(elem); + } +} + +impl GenKill for BitSet { + fn gen(&mut self, elem: T) { + self.insert(elem); + } + + fn kill(&mut self, elem: T) { + self.remove(elem); + } +} + +// NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Effect { + /// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or + /// terminator). + Before, + + /// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator). + Primary, +} + +impl Effect { + pub const fn at_index(self, statement_index: usize) -> EffectIndex { + EffectIndex { effect: self, statement_index } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EffectIndex { + statement_index: usize, + effect: Effect, +} + +impl EffectIndex { + fn next_in_forward_order(self) -> Self { + match self.effect { + Effect::Before => Effect::Primary.at_index(self.statement_index), + Effect::Primary => Effect::Before.at_index(self.statement_index + 1), + } + } + + fn next_in_backward_order(self) -> Self { + match self.effect { + Effect::Before => Effect::Primary.at_index(self.statement_index), + Effect::Primary => Effect::Before.at_index(self.statement_index - 1), + } + } + + /// Returns `true` if the effect at `self` should be applied eariler than the effect at `other` + /// in forward order. + fn precedes_in_forward_order(self, other: Self) -> bool { + let ord = self + .statement_index + .cmp(&other.statement_index) + .then_with(|| self.effect.cmp(&other.effect)); + ord == Ordering::Less + } + + /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` + /// in backward order. + fn precedes_in_backward_order(self, other: Self) -> bool { + let ord = other + .statement_index + .cmp(&self.statement_index) + .then_with(|| self.effect.cmp(&other.effect)); + ord == Ordering::Less + } +} + +#[cfg(test)] +mod tests; diff --git a/src/librustc_mir/dataflow/framework/tests.rs b/src/librustc_mir/dataflow/framework/tests.rs new file mode 100644 index 0000000000000..3ed0a9594e7d5 --- /dev/null +++ b/src/librustc_mir/dataflow/framework/tests.rs @@ -0,0 +1,323 @@ +//! A test for the logic that updates the state in a `ResultsCursor` during seek. + +use std::marker::PhantomData; + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::ty; +use rustc_span::DUMMY_SP; + +use super::*; +use crate::dataflow::BottomValue; + +/// Creates a `mir::Body` with a few disconnected basic blocks. +/// +/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not +/// important. +fn mock_body() -> mir::Body<'static> { + let source_info = mir::SourceInfo::outermost(DUMMY_SP); + + let mut blocks = IndexVec::new(); + let mut block = |n, kind| { + let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop }; + + blocks.push(mir::BasicBlockData { + statements: std::iter::repeat(&nop).cloned().take(n).collect(), + terminator: Some(mir::Terminator { source_info, kind }), + is_cleanup: false, + }) + }; + + let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() }; + + block(4, mir::TerminatorKind::Return); + block(1, mir::TerminatorKind::Return); + block( + 2, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: vec![], + destination: Some((dummy_place.clone(), mir::START_BLOCK)), + cleanup: None, + from_hir_call: false, + }, + ); + block(3, mir::TerminatorKind::Return); + block(0, mir::TerminatorKind::Return); + block( + 4, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: vec![], + destination: Some((dummy_place.clone(), mir::START_BLOCK)), + cleanup: None, + from_hir_call: false, + }, + ); + + mir::Body::new_cfg_only(blocks) +} + +/// A dataflow analysis whose state is unique at every possible `SeekTarget`. +/// +/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and +/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is +/// *globally* unique (see `mock_entry_set`). +/// +/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each +/// location ("+x" indicates that "x" is added to the state). +/// +/// | Location | Before | After | +/// |------------------------|-------------------|--------| +/// | (on_entry) | {102} || +/// | statement 0 | +0 | +1 | +/// | statement 1 | +2 | +3 | +/// | `Call` terminator | +4 | +5 | +/// | (on unwind) | {102,0,1,2,3,4,5} || +/// +/// The `102` in the block's entry set is derived from the basic block index and ensures that the +/// expected state is unique across all basic blocks. Remember, it is generated by +/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint. +struct MockAnalysis<'tcx, D> { + body: &'tcx mir::Body<'tcx>, + dir: PhantomData, +} + +impl MockAnalysis<'tcx, D> { + const BASIC_BLOCK_OFFSET: usize = 100; + + /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to + /// avoid colliding with the statement/terminator effects. + fn mock_entry_set(&self, bb: BasicBlock) -> BitSet { + let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); + ret + } + + fn mock_entry_sets(&self) -> IndexVec> { + let empty = BitSet::new_empty(self.bits_per_block(self.body)); + let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks()); + + for (bb, _) in self.body.basic_blocks().iter_enumerated() { + ret[bb] = self.mock_entry_set(bb); + } + + ret + } + + /// Returns the index that should be added to the dataflow state at the given target. + fn effect(&self, loc: EffectIndex) -> usize { + let idx = match loc.effect { + Effect::Before => loc.statement_index * 2, + Effect::Primary => loc.statement_index * 2 + 1, + }; + + assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block"); + idx + } + + /// Returns the expected state at the given `SeekTarget`. + /// + /// This is the union of index of the target basic block, the index assigned to the + /// target statement or terminator, and the indices of all preceding statements in the target + /// basic block. + /// + /// For example, the expected state when calling + /// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })` + /// would be `[102, 0, 1, 2, 3, 4]`. + fn expected_state_at_target(&self, target: SeekTarget) -> BitSet { + let block = target.block(); + let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); + + let target = match target { + SeekTarget::BlockEntry { .. } => return ret, + SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index), + SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), + }; + + let mut pos = if D::is_forward() { + Effect::Before.at_index(0) + } else { + Effect::Before.at_index(self.body[block].statements.len()) + }; + + loop { + ret.insert(self.effect(pos)); + + if pos == target { + return ret; + } + + if D::is_forward() { + pos = pos.next_in_forward_order(); + } else { + pos = pos.next_in_backward_order(); + } + } + } +} + +impl BottomValue for MockAnalysis<'tcx, D> { + const BOTTOM_VALUE: bool = false; +} + +impl AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { + type Idx = usize; + type Direction = D; + + const NAME: &'static str = "mock"; + + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { + Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len() + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { + unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); + } +} + +impl Analysis<'tcx> for MockAnalysis<'tcx, D> { + fn apply_statement_effect( + &self, + state: &mut BitSet, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_before_statement_effect( + &self, + state: &mut BitSet, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Before.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_terminator_effect( + &self, + state: &mut BitSet, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_before_terminator_effect( + &self, + state: &mut BitSet, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Before.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_call_return_effect( + &self, + _state: &mut BitSet, + _block: BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + _return_place: mir::Place<'tcx>, + ) { + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum SeekTarget { + BlockEntry(BasicBlock), + Before(Location), + After(Location), +} + +impl SeekTarget { + fn block(&self) -> BasicBlock { + use SeekTarget::*; + + match *self { + BlockEntry(block) => block, + Before(loc) | After(loc) => loc.block, + } + } + + /// An iterator over all possible `SeekTarget`s in a given block in order, starting with + /// `BlockEntry`. + fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator { + let statements_and_terminator = (0..=body[block].statements.len()) + .flat_map(|i| (0..2).map(move |j| (i, j))) + .map(move |(i, kind)| { + let loc = Location { block, statement_index: i }; + match kind { + 0 => SeekTarget::Before(loc), + 1 => SeekTarget::After(loc), + _ => unreachable!(), + } + }); + + std::iter::once(SeekTarget::BlockEntry(block)).chain(statements_and_terminator) + } +} + +fn test_cursor(analysis: MockAnalysis<'tcx, D>) { + let body = analysis.body; + + let mut cursor = + Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + + let every_target = || { + body.basic_blocks() + .iter_enumerated() + .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb)) + }; + + let mut seek_to_target = |targ| { + use SeekTarget::*; + + match targ { + BlockEntry(block) => cursor.seek_to_block_entry(block), + Before(loc) => cursor.seek_before_primary_effect(loc), + After(loc) => cursor.seek_after_primary_effect(loc), + } + + assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ)); + }; + + // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`. + // + // By resetting the cursor to `from` each time it changes, we end up checking some edges twice. + // What we really want is an Eulerian cycle for the complete digraph over all possible + // `SeekTarget`s, but it's not worth spending the time to compute it. + for from in every_target() { + seek_to_target(from); + + for to in every_target() { + dbg!(from); + dbg!(to); + seek_to_target(to); + seek_to_target(from); + } + } +} + +#[test] +fn backward_cursor() { + let body = mock_body(); + let body = &body; + let analysis = MockAnalysis { body, dir: PhantomData:: }; + test_cursor(analysis) +} + +#[test] +fn forward_cursor() { + let body = mock_body(); + let body = &body; + let analysis = MockAnalysis { body, dir: PhantomData:: }; + test_cursor(analysis) +} diff --git a/src/librustc_mir/dataflow/generic/visitor.rs b/src/librustc_mir/dataflow/framework/visitor.rs similarity index 83% rename from src/librustc_mir/dataflow/generic/visitor.rs rename to src/librustc_mir/dataflow/framework/visitor.rs index 6e1513bcd1dd0..0df9322e7fe08 100644 --- a/src/librustc_mir/dataflow/generic/visitor.rs +++ b/src/librustc_mir/dataflow/framework/visitor.rs @@ -1,50 +1,41 @@ -use rustc::mir::{self, BasicBlock, Location}; use rustc_index::bit_set::BitSet; +use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Results}; +use super::{Analysis, Direction, Results}; use crate::dataflow::impls::{borrows::Borrows, EverInitializedPlaces, MaybeUninitializedPlaces}; /// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the /// dataflow state at that location. -pub fn visit_results( +pub fn visit_results( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - results: &impl ResultsVisitable<'tcx, FlowState = F>, + results: &V, vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, -) { +) where + V: ResultsVisitable<'tcx, FlowState = F>, +{ let mut state = results.new_flow_state(body); for block in blocks { let block_data = &body[block]; - results.reset_to_block_start(&mut state, block); - - for (statement_index, stmt) in block_data.statements.iter().enumerate() { - let loc = Location { block, statement_index }; - - results.reconstruct_before_statement_effect(&mut state, stmt, loc); - vis.visit_statement(&state, stmt, loc); - - results.reconstruct_statement_effect(&mut state, stmt, loc); - vis.visit_statement_exit(&state, stmt, loc); - } - - let loc = body.terminator_loc(block); - let term = block_data.terminator(); - - results.reconstruct_before_terminator_effect(&mut state, term, loc); - vis.visit_terminator(&state, term, loc); - - results.reconstruct_terminator_effect(&mut state, term, loc); - vis.visit_terminator_exit(&state, term, loc); + V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); } } pub trait ResultsVisitor<'mir, 'tcx> { type FlowState; + fn visit_block_start( + &mut self, + _state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + } + /// Called with the `before_statement_effect` of the given statement applied to `state` but not /// its `statement_effect`. - fn visit_statement( + fn visit_statement_before_primary_effect( &mut self, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, @@ -54,7 +45,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// Called with both the `before_statement_effect` and the `statement_effect` of the given /// statement applied to `state`. - fn visit_statement_exit( + fn visit_statement_after_primary_effect( &mut self, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, @@ -64,7 +55,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// Called with the `before_terminator_effect` of the given terminator applied to `state` but not /// its `terminator_effect`. - fn visit_terminator( + fn visit_terminator_before_primary_effect( &mut self, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, @@ -76,13 +67,21 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// terminator applied to `state`. /// /// The `call_return_effect` (if one exists) will *not* be applied to `state`. - fn visit_terminator_exit( + fn visit_terminator_after_primary_effect( &mut self, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, ) { } + + fn visit_block_end( + &mut self, + _state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + } } /// Things that can be visited by a `ResultsVisitor`. @@ -90,15 +89,16 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// This trait exists so that we can visit the results of multiple dataflow analyses simultaneously. /// DO NOT IMPLEMENT MANUALLY. Instead, use the `impl_visitable` macro below. pub trait ResultsVisitable<'tcx> { + type Direction: Direction; type FlowState; /// Creates an empty `FlowState` to hold the transient state for these dataflow results. /// - /// The value of the newly created `FlowState` will be overwritten by `reset_to_block_start` + /// The value of the newly created `FlowState` will be overwritten by `reset_to_block_entry` /// before it can be observed by a `ResultsVisitor`. fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState; - fn reset_to_block_start(&self, state: &mut Self::FlowState, block: BasicBlock); + fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock); fn reconstruct_before_statement_effect( &self, @@ -135,11 +135,13 @@ where { type FlowState = BitSet; + type Direction = A::Direction; + fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { BitSet::new_empty(self.analysis.bits_per_block(body)) } - fn reset_to_block_start(&self, state: &mut Self::FlowState, block: BasicBlock) { + fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { state.overwrite(&self.entry_set_for_block(block)); } @@ -204,10 +206,11 @@ macro_rules! impl_visitable { ( $( $T:ident { $( $field:ident : $A:ident ),* $(,)? } )* ) => { $( - impl<'tcx, $($A),*> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*> + impl<'tcx, $($A),*, D: Direction> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*> where - $( $A: Analysis<'tcx>, )* + $( $A: Analysis<'tcx, Direction = D>, )* { + type Direction = D; type FlowState = $T<$( BitSet<$A::Idx> ),*>; fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { @@ -216,12 +219,12 @@ macro_rules! impl_visitable { } } - fn reset_to_block_start( + fn reset_to_block_entry( &self, state: &mut Self::FlowState, block: BasicBlock, ) { - $( state.$field.overwrite(&self.$field.entry_sets[block]); )* + $( state.$field.overwrite(&self.$field.entry_set_for_block(block)); )* } fn reconstruct_before_statement_effect( diff --git a/src/librustc_mir/dataflow/generic/cursor.rs b/src/librustc_mir/dataflow/generic/cursor.rs deleted file mode 100644 index 170157aca5ddd..0000000000000 --- a/src/librustc_mir/dataflow/generic/cursor.rs +++ /dev/null @@ -1,280 +0,0 @@ -//! Random access inspection of the results of a dataflow analysis. - -use std::borrow::Borrow; - -use rustc::mir::{self, BasicBlock, Location, TerminatorKind}; -use rustc_index::bit_set::BitSet; - -use super::{Analysis, Results}; - -/// A `ResultsCursor` that borrows the underlying `Results`. -pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>; - -/// Allows random access inspection of the results of a dataflow analysis. -/// -/// This cursor only has linear performance within a basic block when its statements are visited in -/// order. In the worst case—when statements are visited in *reverse* order—performance will be -/// quadratic in the number of statements in the block. The order in which basic blocks are -/// inspected has no impact on performance. -/// -/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The -/// type of ownership is determined by `R` (see `ResultsRefCursor` above). -pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>> -where - A: Analysis<'tcx>, -{ - body: &'mir mir::Body<'tcx>, - results: R, - state: BitSet, - - pos: CursorPosition, - - /// When this flag is set, the cursor is pointing at a `Call` or `Yield` terminator whose call - /// return or resume effect has been applied to `state`. - /// - /// This flag helps to ensure that multiple calls to `seek_after_assume_success` with the - /// same target will result in exactly one invocation of `apply_call_return_effect`. It is - /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a - /// terminator will always require a cursor reset. - success_effect_applied: bool, -} - -impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> -where - A: Analysis<'tcx>, - R: Borrow>, -{ - /// Returns a new cursor for `results` that points to the start of the `START_BLOCK`. - pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { - ResultsCursor { - body, - pos: CursorPosition::BlockStart(mir::START_BLOCK), - state: results.borrow().entry_sets[mir::START_BLOCK].clone(), - success_effect_applied: false, - results, - } - } - - /// Returns the `Analysis` used to generate the underlying results. - pub fn analysis(&self) -> &A { - &self.results.borrow().analysis - } - - /// Returns the dataflow state at the current location. - pub fn get(&self) -> &BitSet { - &self.state - } - - /// Returns `true` if the dataflow state at the current location contains the given element. - /// - /// Shorthand for `self.get().contains(elem)` - pub fn contains(&self, elem: A::Idx) -> bool { - self.state.contains(elem) - } - - /// Resets the cursor to the start of the given basic block. - pub fn seek_to_block_start(&mut self, block: BasicBlock) { - self.state.overwrite(&self.results.borrow().entry_sets[block]); - self.pos = CursorPosition::BlockStart(block); - self.success_effect_applied = false; - } - - /// Advances the cursor to hold all effects up to and including to the "before" effect of the - /// statement (or terminator) at the given location. - /// - /// If you wish to observe the full effect of a statement or terminator, not just the "before" - /// effect, use `seek_after` or `seek_after_assume_success`. - pub fn seek_before(&mut self, target: Location) { - assert!(target <= self.body.terminator_loc(target.block)); - self.seek_(target, false); - } - - /// Advances the cursor to hold the full effect of all statements (and possibly closing - /// terminators) up to and including the `target`. - /// - /// If the `target` is a `Call` terminator, any call return effect for that terminator will - /// **not** be observed. Use `seek_after_assume_success` if you wish to observe the call - /// return effect. - pub fn seek_after(&mut self, target: Location) { - assert!(target <= self.body.terminator_loc(target.block)); - - // If we have already applied the call return effect, we are currently pointing at a `Call` - // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo" - // the call return effect. - if self.success_effect_applied { - self.seek_to_block_start(target.block); - } - - self.seek_(target, true); - } - - /// Advances the cursor to hold all effects up to and including of the statement (or - /// terminator) at the given location. - /// - /// If the `target` is a `Call` or `Yield` terminator, any call return or resume effect for that - /// terminator will be observed. Use `seek_after` if you do **not** wish to observe the - /// "success" effect. - pub fn seek_after_assume_success(&mut self, target: Location) { - let terminator_loc = self.body.terminator_loc(target.block); - assert!(target.statement_index <= terminator_loc.statement_index); - - self.seek_(target, true); - - if target != terminator_loc || self.success_effect_applied { - return; - } - - // Apply the effect of the "success" path of the terminator. - - self.success_effect_applied = true; - let terminator = self.body.basic_blocks()[target.block].terminator(); - match &terminator.kind { - TerminatorKind::Call { destination: Some((return_place, _)), func, args, .. } => { - self.results.borrow().analysis.apply_call_return_effect( - &mut self.state, - target.block, - func, - args, - return_place, - ); - } - TerminatorKind::Yield { resume, resume_arg, .. } => { - self.results.borrow().analysis.apply_yield_resume_effect( - &mut self.state, - *resume, - resume_arg, - ); - } - _ => {} - } - } - - fn seek_(&mut self, target: Location, apply_after_effect_at_target: bool) { - use CursorPosition::*; - - match self.pos { - // Return early if we are already at the target location. - Before(curr) if curr == target && !apply_after_effect_at_target => return, - After(curr) if curr == target && apply_after_effect_at_target => return, - - // Otherwise, we must reset to the start of the target block if... - - // we are in a different block entirely. - BlockStart(block) | Before(Location { block, .. }) | After(Location { block, .. }) - if block != target.block => - { - self.seek_to_block_start(target.block) - } - - // we are in the same block but have advanced past the target statement. - Before(curr) | After(curr) if curr.statement_index > target.statement_index => { - self.seek_to_block_start(target.block) - } - - // we have already applied the entire effect of a statement but only wish to observe - // its "before" effect. - After(curr) - if curr.statement_index == target.statement_index - && !apply_after_effect_at_target => - { - self.seek_to_block_start(target.block) - } - - // N.B., `success_effect_applied` is checked in `seek_after`, not here. - _ => (), - } - - let analysis = &self.results.borrow().analysis; - let block_data = &self.body.basic_blocks()[target.block]; - - // At this point, the cursor is in the same block as the target location at an earlier - // statement. - debug_assert_eq!(target.block, self.pos.block()); - - // Find the first statement whose transfer function has not yet been applied. - let first_unapplied_statement = match self.pos { - BlockStart(_) => 0, - After(Location { statement_index, .. }) => statement_index + 1, - - // If we have only applied the "before" effect for the current statement, apply the - // remainder before continuing. - Before(curr) => { - if curr.statement_index == block_data.statements.len() { - let terminator = block_data.terminator(); - analysis.apply_terminator_effect(&mut self.state, terminator, curr); - } else { - let statement = &block_data.statements[curr.statement_index]; - analysis.apply_statement_effect(&mut self.state, statement, curr); - } - - // If all we needed to do was go from `Before` to `After` in the same statement, - // we are now done. - if curr.statement_index == target.statement_index { - debug_assert!(apply_after_effect_at_target); - self.pos = After(target); - return; - } - - curr.statement_index + 1 - } - }; - - // We have now applied all effects prior to `first_unapplied_statement`. - - // Apply the effects of all statements before `target`. - let mut location = Location { block: target.block, statement_index: 0 }; - for statement_index in first_unapplied_statement..target.statement_index { - location.statement_index = statement_index; - let statement = &block_data.statements[statement_index]; - analysis.apply_before_statement_effect(&mut self.state, statement, location); - analysis.apply_statement_effect(&mut self.state, statement, location); - } - - // Apply the effect of the statement (or terminator) at `target`. - location.statement_index = target.statement_index; - if target.statement_index == block_data.statements.len() { - let terminator = &block_data.terminator(); - analysis.apply_before_terminator_effect(&mut self.state, terminator, location); - - if apply_after_effect_at_target { - analysis.apply_terminator_effect(&mut self.state, terminator, location); - self.pos = After(target); - } else { - self.pos = Before(target); - } - } else { - let statement = &block_data.statements[target.statement_index]; - analysis.apply_before_statement_effect(&mut self.state, statement, location); - - if apply_after_effect_at_target { - analysis.apply_statement_effect(&mut self.state, statement, location); - self.pos = After(target) - } else { - self.pos = Before(target); - } - } - } -} - -#[derive(Clone, Copy, Debug)] -enum CursorPosition { - /// No effects within this block have been applied. - BlockStart(BasicBlock), - - /// Only the "before" effect of the statement (or terminator) at this location has been - /// applied (along with the effects of all previous statements). - Before(Location), - - /// The effects of all statements up to and including the one at this location have been - /// applied. - After(Location), -} - -impl CursorPosition { - fn block(&self) -> BasicBlock { - match *self { - Self::BlockStart(block) => block, - Self::Before(loc) | Self::After(loc) => loc.block, - } - } -} diff --git a/src/librustc_mir/dataflow/generic/engine.rs b/src/librustc_mir/dataflow/generic/engine.rs deleted file mode 100644 index d32072125b3b9..0000000000000 --- a/src/librustc_mir/dataflow/generic/engine.rs +++ /dev/null @@ -1,525 +0,0 @@ -//! A solver for dataflow problems. - -use std::ffi::OsString; -use std::fs; -use std::path::PathBuf; - -use rustc::mir::{self, traversal, BasicBlock, Location}; -use rustc::ty::{self, TyCtxt}; -use rustc_ast::ast; -use rustc_data_structures::work_queue::WorkQueue; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; -use rustc_span::symbol::{sym, Symbol}; - -use super::graphviz; -use super::{Analysis, GenKillAnalysis, GenKillSet, Results}; - -/// A solver for dataflow problems. -pub struct Engine<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - bits_per_block: usize, - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - def_id: DefId, - dead_unwinds: Option<&'a BitSet>, - entry_sets: IndexVec>, - analysis: A, - - /// Cached, cumulative transfer functions for each block. - trans_for_block: Option>>, -} - -impl Engine<'a, 'tcx, A> -where - A: GenKillAnalysis<'tcx>, -{ - /// Creates a new `Engine` to solve a gen-kill dataflow problem. - pub fn new_gen_kill( - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - def_id: DefId, - analysis: A, - ) -> Self { - // If there are no back-edges in the control-flow graph, we only ever need to apply the - // transfer function for each block exactly once (assuming that we process blocks in RPO). - // - // In this case, there's no need to compute the block transfer functions ahead of time. - if !body.is_cfg_cyclic() { - return Self::new(tcx, body, def_id, analysis, None); - } - - // Otherwise, compute and store the cumulative transfer function for each block. - - let bits_per_block = analysis.bits_per_block(body); - let mut trans_for_block = - IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks()); - - for (block, block_data) in body.basic_blocks().iter_enumerated() { - let trans = &mut trans_for_block[block]; - - for (i, statement) in block_data.statements.iter().enumerate() { - let loc = Location { block, statement_index: i }; - analysis.before_statement_effect(trans, statement, loc); - analysis.statement_effect(trans, statement, loc); - } - - let terminator = block_data.terminator(); - let loc = Location { block, statement_index: block_data.statements.len() }; - analysis.before_terminator_effect(trans, terminator, loc); - analysis.terminator_effect(trans, terminator, loc); - } - - Self::new(tcx, body, def_id, analysis, Some(trans_for_block)) - } -} - -impl Engine<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer - /// function. - /// - /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for - /// better performance. - pub fn new_generic( - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - def_id: DefId, - analysis: A, - ) -> Self { - Self::new(tcx, body, def_id, analysis, None) - } - - fn new( - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - def_id: DefId, - analysis: A, - trans_for_block: Option>>, - ) -> Self { - let bits_per_block = analysis.bits_per_block(body); - - let bottom_value_set = if A::BOTTOM_VALUE { - BitSet::new_filled(bits_per_block) - } else { - BitSet::new_empty(bits_per_block) - }; - - let mut entry_sets = IndexVec::from_elem(bottom_value_set, body.basic_blocks()); - analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); - - Engine { - analysis, - bits_per_block, - tcx, - body, - def_id, - dead_unwinds: None, - entry_sets, - trans_for_block, - } - } - - /// Signals that we do not want dataflow state to propagate across unwind edges for these - /// `BasicBlock`s. - /// - /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually - /// unwind during execution. Otherwise, your dataflow results will not be correct. - pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet) -> Self { - self.dead_unwinds = Some(dead_unwinds); - self - } - - /// Computes the fixpoint for this dataflow problem and returns it. - pub fn iterate_to_fixpoint(mut self) -> Results<'tcx, A> { - let mut temp_state = BitSet::new_empty(self.bits_per_block); - - let mut dirty_queue: WorkQueue = - WorkQueue::with_none(self.body.basic_blocks().len()); - - for (bb, _) in traversal::reverse_postorder(self.body) { - dirty_queue.insert(bb); - } - - // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will - // be processed after the ones added above. - for bb in self.body.basic_blocks().indices() { - dirty_queue.insert(bb); - } - - while let Some(bb) = dirty_queue.pop() { - let bb_data = &self.body[bb]; - let on_entry = &self.entry_sets[bb]; - - temp_state.overwrite(on_entry); - self.apply_whole_block_effect(&mut temp_state, bb, bb_data); - - self.propagate_bits_into_graph_successors_of( - &mut temp_state, - (bb, bb_data), - &mut dirty_queue, - ); - } - - let Engine { tcx, body, def_id, trans_for_block, entry_sets, analysis, .. } = self; - let results = Results { analysis, entry_sets }; - - let res = write_graphviz_results(tcx, def_id, body, &results, trans_for_block); - if let Err(e) = res { - warn!("Failed to write graphviz dataflow results: {}", e); - } - - results - } - - /// Applies the cumulative effect of an entire block, excluding the call return effect if one - /// exists. - fn apply_whole_block_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - ) { - // Use the cached block transfer function if available. - if let Some(trans_for_block) = &self.trans_for_block { - trans_for_block[block].apply(state); - return; - } - - // Otherwise apply effects one-by-one. - - for (statement_index, statement) in block_data.statements.iter().enumerate() { - let location = Location { block, statement_index }; - self.analysis.apply_before_statement_effect(state, statement, location); - self.analysis.apply_statement_effect(state, statement, location); - } - - let terminator = block_data.terminator(); - let location = Location { block, statement_index: block_data.statements.len() }; - self.analysis.apply_before_terminator_effect(state, terminator, location); - self.analysis.apply_terminator_effect(state, terminator, location); - } - - fn propagate_bits_into_graph_successors_of( - &mut self, - in_out: &mut BitSet, - (bb, bb_data): (BasicBlock, &'a mir::BasicBlockData<'tcx>), - dirty_list: &mut WorkQueue, - ) { - use mir::TerminatorKind::*; - - match bb_data.terminator().kind { - Return | Resume | Abort | GeneratorDrop | Unreachable => {} - - Goto { target } - | Assert { target, cleanup: None, .. } - | Drop { target, location: _, unwind: None } - | DropAndReplace { target, value: _, location: _, unwind: None } => { - self.propagate_bits_into_entry_set_for(in_out, target, dirty_list) - } - - Yield { resume: target, drop, resume_arg, .. } => { - if let Some(drop) = drop { - self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list); - } - - self.analysis.apply_yield_resume_effect(in_out, target, &resume_arg); - self.propagate_bits_into_entry_set_for(in_out, target, dirty_list); - } - - Assert { target, cleanup: Some(unwind), .. } - | Drop { target, location: _, unwind: Some(unwind) } - | DropAndReplace { target, value: _, location: _, unwind: Some(unwind) } => { - self.propagate_bits_into_entry_set_for(in_out, target, dirty_list); - if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) { - self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list); - } - } - - SwitchInt { ref targets, ref values, ref discr, .. } => { - let Engine { tcx, body, .. } = *self; - let enum_ = discr - .place() - .and_then(|discr| switch_on_enum_discriminant(tcx, body, bb_data, discr)); - match enum_ { - // If this is a switch on an enum discriminant, a custom effect may be applied - // along each outgoing edge. - Some((enum_place, enum_def)) => { - self.propagate_bits_into_enum_discriminant_switch_successors( - in_out, bb, enum_def, enum_place, dirty_list, &*values, &*targets, - ); - } - - // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same - // exit state. - None => { - for target in targets.iter().copied() { - self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list); - } - } - } - } - - Call { cleanup, ref destination, ref func, ref args, .. } => { - if let Some(unwind) = cleanup { - if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) { - self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list); - } - } - - if let Some((ref dest_place, dest_bb)) = *destination { - // N.B.: This must be done *last*, otherwise the unwind path will see the call - // return effect. - self.analysis.apply_call_return_effect(in_out, bb, func, args, dest_place); - self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list); - } - } - - FalseEdges { real_target, imaginary_target } => { - self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list); - self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list); - } - - FalseUnwind { real_target, unwind } => { - self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list); - if let Some(unwind) = unwind { - if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) { - self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list); - } - } - } - } - } - - fn propagate_bits_into_entry_set_for( - &mut self, - in_out: &BitSet, - bb: BasicBlock, - dirty_queue: &mut WorkQueue, - ) { - let entry_set = &mut self.entry_sets[bb]; - let set_changed = self.analysis.join(entry_set, &in_out); - if set_changed { - dirty_queue.insert(bb); - } - } - - fn propagate_bits_into_enum_discriminant_switch_successors( - &mut self, - in_out: &mut BitSet, - bb: BasicBlock, - enum_def: &'tcx ty::AdtDef, - enum_place: &mir::Place<'tcx>, - dirty_list: &mut WorkQueue, - values: &[u128], - targets: &[BasicBlock], - ) { - // MIR building adds discriminants to the `values` array in the same order as they - // are yielded by `AdtDef::discriminants`. We rely on this to match each - // discriminant in `values` to its corresponding variant in linear time. - let mut tmp = BitSet::new_empty(in_out.domain_size()); - let mut discriminants = enum_def.discriminants(self.tcx); - for (value, target) in values.iter().zip(targets.iter().copied()) { - let (variant_idx, _) = discriminants.find(|&(_, discr)| discr.val == *value).expect( - "Order of `AdtDef::discriminants` differed from that of `SwitchInt::values`", - ); - - tmp.overwrite(in_out); - self.analysis.apply_discriminant_switch_effect( - &mut tmp, - bb, - enum_place, - enum_def, - variant_idx, - ); - self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list); - } - - std::mem::drop(tmp); - - // Propagate dataflow state along the "otherwise" edge. - let otherwise = targets.last().copied().unwrap(); - self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list); - } -} - -/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is -/// an enum discriminant. -/// -/// We expect such blocks to have a call to `discriminant` as their last statement like so: -/// _42 = discriminant(_1) -/// SwitchInt(_42, ..) -/// -/// If the basic block matches this pattern, this function returns the place corresponding to the -/// enum (`_1` in the example above) as well as the `AdtDef` of that enum. -fn switch_on_enum_discriminant( - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - block: &'mir mir::BasicBlockData<'tcx>, - switch_on: &mir::Place<'tcx>, -) -> Option<(&'mir mir::Place<'tcx>, &'tcx ty::AdtDef)> { - match block.statements.last().map(|stmt| &stmt.kind) { - Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))) - if lhs == switch_on => - { - match &discriminated.ty(body, tcx).ty.kind { - ty::Adt(def, _) => Some((discriminated, def)), - - // `Rvalue::Discriminant` is also used to get the active yield point for a - // generator, but we do not need edge-specific effects in that case. This may - // change in the future. - ty::Generator(..) => None, - - t => bug!("`discriminant` called on unexpected type {:?}", t), - } - } - - _ => None, - } -} - -// Graphviz - -/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via -/// `rustc_mir` attributes. -fn write_graphviz_results( - tcx: TyCtxt<'tcx>, - def_id: DefId, - body: &mir::Body<'tcx>, - results: &Results<'tcx, A>, - block_transfer_functions: Option>>, -) -> std::io::Result<()> -where - A: Analysis<'tcx>, -{ - let attrs = match RustcMirAttrs::parse(tcx, def_id) { - Ok(attrs) => attrs, - - // Invalid `rustc_mir` attrs will be reported using `span_err`. - Err(()) => return Ok(()), - }; - - let path = match attrs.output_path(A::NAME) { - Some(path) => path, - None => return Ok(()), - }; - - let bits_per_block = results.analysis.bits_per_block(body); - - let mut formatter: Box> = match attrs.formatter { - Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)), - Some(sym::gen_kill) => { - if let Some(trans_for_block) = block_transfer_functions { - Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block)) - } else { - Box::new(graphviz::SimpleDiff::new(bits_per_block)) - } - } - - // Default to the `SimpleDiff` output style. - _ => Box::new(graphviz::SimpleDiff::new(bits_per_block)), - }; - - debug!("printing dataflow results for {:?} to {}", def_id, path.display()); - let mut buf = Vec::new(); - - let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter); - dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?; - fs::write(&path, buf)?; - Ok(()) -} - -#[derive(Default)] -struct RustcMirAttrs { - basename_and_suffix: Option, - formatter: Option, -} - -impl RustcMirAttrs { - fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result { - let attrs = tcx.get_attrs(def_id); - - let mut result = Ok(()); - let mut ret = RustcMirAttrs::default(); - - let rustc_mir_attrs = attrs - .iter() - .filter(|attr| attr.check_name(sym::rustc_mir)) - .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); - - for attr in rustc_mir_attrs { - let attr_result = if attr.check_name(sym::borrowck_graphviz_postflow) { - 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.sess.span_err(attr.span(), "path must end in a filename"); - Err(()) - } - } - }) - } else if attr.check_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.sess.span_err(attr.span(), "unknown formatter"); - Err(()) - } - }) - } else { - Ok(()) - }; - - result = result.and(attr_result); - } - - result.map(|()| ret) - } - - fn set_field( - field: &mut Option, - tcx: TyCtxt<'tcx>, - attr: &ast::NestedMetaItem, - mapper: impl FnOnce(Symbol) -> Result, - ) -> Result<(), ()> { - if field.is_some() { - tcx.sess - .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty())); - - return Err(()); - } - - if let Some(s) = attr.value_str() { - *field = Some(mapper(s)?); - Ok(()) - } else { - tcx.sess - .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty())); - Err(()) - } - } - - /// Returns the path where dataflow results should be written, or `None` - /// `borrowck_graphviz_postflow` was not specified. - /// - /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`: - /// - /// "path/suffix.dot" -> "path/analysis_name_suffix.dot" - fn output_path(&self, analysis_name: &str) -> Option { - let mut ret = self.basename_and_suffix.as_ref().cloned()?; - let suffix = ret.file_name().unwrap(); // Checked when parsing attrs - - let mut file_name: OsString = analysis_name.into(); - file_name.push("_"); - file_name.push(suffix); - ret.set_file_name(file_name); - - Some(ret) - } -} diff --git a/src/librustc_mir/dataflow/generic/graphviz.rs b/src/librustc_mir/dataflow/generic/graphviz.rs deleted file mode 100644 index c15f2a726ee70..0000000000000 --- a/src/librustc_mir/dataflow/generic/graphviz.rs +++ /dev/null @@ -1,696 +0,0 @@ -//! A helpful diagram for debugging dataflow problems. - -use std::cell::RefCell; -use std::{io, ops, str}; - -use rustc::mir::{self, BasicBlock, Body, Location}; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::{Idx, IndexVec}; - -use super::{Analysis, GenKillSet, Results, ResultsRefCursor}; -use crate::util::graphviz_safe_def_name; - -pub struct Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - body: &'a Body<'tcx>, - def_id: DefId, - - // This must be behind a `RefCell` because `dot::Labeller` takes `&self`. - block_formatter: RefCell>, -} - -impl Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - pub fn new( - body: &'a Body<'tcx>, - def_id: DefId, - results: &'a Results<'tcx, A>, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, - ) -> Self { - let block_formatter = BlockFormatter { - bg: Background::Light, - results: ResultsRefCursor::new(body, results), - state_formatter, - }; - - Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) } - } -} - -/// A pair of a basic block and an index into that basic blocks `successors`. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct CfgEdge { - source: BasicBlock, - index: usize, -} - -fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec { - body[bb] - .terminator() - .successors() - .enumerate() - .map(|(index, _)| CfgEdge { source: bb, index }) - .collect() -} - -impl dot::Labeller<'_> for Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - type Node = BasicBlock; - type Edge = CfgEdge; - - fn graph_id(&self) -> dot::Id<'_> { - let name = graphviz_safe_def_name(self.def_id); - dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() - } - - fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { - dot::Id::new(format!("bb_{}", n.index())).unwrap() - } - - fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { - let mut label = Vec::new(); - self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap(); - dot::LabelText::html(String::from_utf8(label).unwrap()) - } - - fn node_shape(&self, _n: &Self::Node) -> Option> { - Some(dot::LabelText::label("none")) - } - - fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> { - let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index]; - dot::LabelText::label(label.clone()) - } -} - -impl dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - type Node = BasicBlock; - type Edge = CfgEdge; - - fn nodes(&self) -> dot::Nodes<'_, Self::Node> { - self.body.basic_blocks().indices().collect::>().into() - } - - fn edges(&self) -> dot::Edges<'_, Self::Edge> { - self.body - .basic_blocks() - .indices() - .flat_map(|bb| outgoing_edges(self.body, bb)) - .collect::>() - .into() - } - - fn source(&self, edge: &Self::Edge) -> Self::Node { - edge.source - } - - fn target(&self, edge: &Self::Edge) -> Self::Node { - self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap() - } -} - -struct BlockFormatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - results: ResultsRefCursor<'a, 'a, 'tcx, A>, - bg: Background, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, -} - -impl BlockFormatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - const HEADER_COLOR: &'static str = "#a0a0a0"; - - fn num_state_columns(&self) -> usize { - std::cmp::max(1, self.state_formatter.column_names().len()) - } - - fn toggle_background(&mut self) -> Background { - let bg = self.bg; - self.bg = !bg; - bg - } - - fn write_node_label( - &mut self, - w: &mut impl io::Write, - body: &'a Body<'tcx>, - block: BasicBlock, - ) -> io::Result<()> { - // Sample output: - // +-+-----------------------------------------------+ - // A | bb4 | - // +-+----------------------------------+------------+ - // B | MIR | STATE | - // +-+----------------------------------+------------+ - // C | | (on entry) | {_0,_2,_3} | - // +-+----------------------------------+------------+ - // D |0| StorageLive(_7) | | - // +-+----------------------------------+------------+ - // |1| StorageLive(_8) | | - // +-+----------------------------------+------------+ - // |2| _8 = &mut _1 | +_8 | - // +-+----------------------------------+------------+ - // E |T| _4 = const Foo::twiddle(move _2) | -_2 | - // +-+----------------------------------+------------+ - // F | | (on unwind) | {_0,_3,_8} | - // +-+----------------------------------+------------+ - // | | (on successful return) | +_4 | - // +-+----------------------------------+------------+ - - // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their - // children. This is because `xdot` seemed to have a hard time correctly propagating - // attributes. Make sure to test the output before trying to remove the redundancy. - // Notably, `align` was found to have no effect when applied only to . - - let table_fmt = concat!( - " border=\"1\"", - " cellborder=\"1\"", - " cellspacing=\"0\"", - " cellpadding=\"3\"", - " sides=\"rb\"", - ); - write!(w, r#""#, fmt = table_fmt)?; - - // A + B: Block header - if self.state_formatter.column_names().is_empty() { - self.write_block_header_simple(w, block)?; - } else { - self.write_block_header_with_state_columns(w, block)?; - } - - // C: Entry state - self.bg = Background::Light; - self.results.seek_to_block_start(block); - let block_entry_state = self.results.get().clone(); - - self.write_row_with_full_state(w, "", "(on entry)")?; - - // D: Statement transfer functions - for (i, statement) in body[block].statements.iter().enumerate() { - let location = Location { block, statement_index: i }; - let statement_str = format!("{:?}", statement); - self.write_row_for_location(w, &i.to_string(), &statement_str, location)?; - } - - // E: Terminator transfer function - let terminator = body[block].terminator(); - let terminator_loc = body.terminator_loc(block); - let mut terminator_str = String::new(); - terminator.kind.fmt_head(&mut terminator_str).unwrap(); - - self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?; - - // F: Exit state - - // Write the full dataflow state immediately after the terminator if it differs from the - // state at block entry. - self.results.seek_after(terminator_loc); - if self.results.get() != &block_entry_state { - let after_terminator_name = match terminator.kind { - mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)", - _ => "(on exit)", - }; - - self.write_row_with_full_state(w, "", after_terminator_name)?; - } - - // Write any changes caused by terminator-specific effects - match terminator.kind { - mir::TerminatorKind::Call { destination: Some(_), .. } => { - let num_state_columns = self.num_state_columns(); - self.write_row(w, "", "(on successful return)", |this, w, fmt| { - write!( - w, - r#"") - })?; - } - - _ => {} - }; - - write!(w, "
"#, - colspan = num_state_columns, - fmt = fmt, - )?; - - let state_on_unwind = this.results.get().clone(); - this.results.seek_after_assume_success(terminator_loc); - write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?; - - write!(w, "
") - } - - fn write_block_header_simple( - &mut self, - w: &mut impl io::Write, - block: BasicBlock, - ) -> io::Result<()> { - // +-------------------------------------------------+ - // A | bb4 | - // +-----------------------------------+-------------+ - // B | MIR | STATE | - // +-+---------------------------------+-------------+ - // | | ... | | - - // A - write!( - w, - concat!("", r#"bb{block_id}"#, "",), - block_id = block.index(), - )?; - - // B - write!( - w, - concat!( - "", - r#"MIR"#, - r#"STATE"#, - "", - ), - fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR), - ) - } - - fn write_block_header_with_state_columns( - &mut self, - w: &mut impl io::Write, - block: BasicBlock, - ) -> io::Result<()> { - // +------------------------------------+-------------+ - // A | bb4 | STATE | - // +------------------------------------+------+------+ - // B | MIR | GEN | KILL | - // +-+----------------------------------+------+------+ - // | | ... | | | - - let state_column_names = self.state_formatter.column_names(); - - // A - write!( - w, - concat!( - "", - r#"bb{block_id}"#, - r#"STATE"#, - "", - ), - fmt = "sides=\"tl\"", - num_state_cols = state_column_names.len(), - block_id = block.index(), - )?; - - // B - let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR); - write!(w, concat!("", r#"MIR"#,), fmt = fmt,)?; - - for name in state_column_names { - write!(w, "{name}", fmt = fmt, name = name)?; - } - - write!(w, "") - } - - /// Write a row with the given index and MIR, using the function argument to fill in the - /// "STATE" column(s). - fn write_row( - &mut self, - w: &mut W, - i: &str, - mir: &str, - f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>, - ) -> io::Result<()> { - let bg = self.toggle_background(); - let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" }; - - let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr()); - - write!( - w, - concat!( - "", - r#"{i}"#, - r#"{mir}"#, - ), - i = i, - fmt = fmt, - mir = dot::escape_html(mir), - )?; - - f(self, w, &fmt)?; - write!(w, "") - } - - fn write_row_with_full_state( - &mut self, - w: &mut impl io::Write, - i: &str, - mir: &str, - ) -> io::Result<()> { - self.write_row(w, i, mir, |this, w, fmt| { - let state = this.results.get(); - let analysis = this.results.analysis(); - - write!( - w, - r#"{{"#, - colspan = this.num_state_columns(), - fmt = fmt, - )?; - pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, "}}") - }) - } - - fn write_row_for_location( - &mut self, - w: &mut impl io::Write, - i: &str, - mir: &str, - location: Location, - ) -> io::Result<()> { - self.write_row(w, i, mir, |this, w, fmt| { - this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location) - }) - } -} - -/// Controls what gets printed under the `STATE` header. -pub trait StateFormatter<'tcx, A> -where - A: Analysis<'tcx>, -{ - /// The columns that will get printed under `STATE`. - fn column_names(&self) -> &[&str]; - - fn write_state_for_location( - &mut self, - w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()>; -} - -/// Prints a single column containing the state vector immediately *after* each statement. -pub struct SimpleDiff { - prev_state: BitSet, - prev_loc: Location, -} - -impl SimpleDiff { - pub fn new(bits_per_block: usize) -> Self { - SimpleDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START } - } -} - -impl
StateFormatter<'tcx, A> for SimpleDiff -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &[] - } - - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - if location.statement_index == 0 { - results.seek_to_block_start(location.block); - self.prev_state.overwrite(results.get()); - } else { - // Ensure that we are visiting statements in order, so `prev_state` is correct. - assert_eq!(self.prev_loc.successor_within_block(), location); - } - - self.prev_loc = location; - write!(w, r#""#, fmt = fmt)?; - results.seek_after(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "") - } -} - -/// Prints two state columns, one containing only the "before" effect of each statement and one -/// containing the full effect. -pub struct TwoPhaseDiff { - prev_state: BitSet, - prev_loc: Location, -} - -impl TwoPhaseDiff { - pub fn new(bits_per_block: usize) -> Self { - TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START } - } -} - -impl StateFormatter<'tcx, A> for TwoPhaseDiff -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &["BEFORE", " AFTER"] - } - - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - if location.statement_index == 0 { - results.seek_to_block_start(location.block); - self.prev_state.overwrite(results.get()); - } else { - // Ensure that we are visiting statements in order, so `prev_state` is correct. - assert_eq!(self.prev_loc.successor_within_block(), location); - } - - self.prev_loc = location; - - // Before - - write!(w, r#""#, fmt = fmt)?; - results.seek_before(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "")?; - - // After - - write!(w, r#""#, fmt = fmt)?; - results.seek_after(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "") - } -} - -/// Prints the gen/kill set for the entire block. -pub struct BlockTransferFunc<'a, 'tcx, T: Idx> { - body: &'a mir::Body<'tcx>, - trans_for_block: IndexVec>, -} - -impl BlockTransferFunc<'mir, 'tcx, T> { - pub fn new( - body: &'mir mir::Body<'tcx>, - trans_for_block: IndexVec>, - ) -> Self { - BlockTransferFunc { body, trans_for_block } - } -} - -impl StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx> -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &["GEN", "KILL"] - } - - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - // Only print a single row. - if location.statement_index != 0 { - return Ok(()); - } - - let block_trans = &self.trans_for_block[location.block]; - let rowspan = self.body.basic_blocks()[location.block].statements.len(); - - for set in &[&block_trans.gen, &block_trans.kill] { - write!( - w, - r#""#, - fmt = fmt, - rowspan = rowspan - )?; - - pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?; - write!(w, "")?; - } - - Ok(()) - } -} - -/// Writes two lines, one containing the added bits and one the removed bits. -fn write_diff>( - w: &mut impl io::Write, - analysis: &A, - from: &BitSet, - to: &BitSet, -) -> io::Result<()> { - assert_eq!(from.domain_size(), to.domain_size()); - let len = from.domain_size(); - - let mut set = HybridBitSet::new_empty(len); - let mut clear = HybridBitSet::new_empty(len); - - // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets. - for i in (0..len).map(A::Idx::new) { - match (from.contains(i), to.contains(i)) { - (false, true) => set.insert(i), - (true, false) => clear.insert(i), - _ => continue, - }; - } - - if !set.is_empty() { - write!(w, r#"+"#)?; - pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#""#)?; - } - - if !set.is_empty() && !clear.is_empty() { - write!(w, "{}", BR_LEFT)?; - } - - if !clear.is_empty() { - write!(w, r#"-"#)?; - pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#""#)?; - } - - Ok(()) -} - -const BR_LEFT: &str = r#"
"#; -const BR_LEFT_SPACE: &str = r#"
"#; - -/// Line break policy that breaks at 40 characters and starts the next line with a single space. -const LIMIT_30_ALIGN_1: Option = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }); - -struct LineBreak { - sequence: &'static str, - limit: usize, -} - -/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given -/// separator (`sep`). -/// -/// Optionally, it will break lines using the given character sequence (usually `
`) and -/// character limit. -fn pretty_print_state_elems
( - w: &mut impl io::Write, - analysis: &A, - elems: impl Iterator, - sep: &str, - line_break: Option, -) -> io::Result -where - A: Analysis<'tcx>, -{ - let sep_width = sep.chars().count(); - - let mut buf = Vec::new(); - - let mut first = true; - let mut curr_line_width = 0; - let mut line_break_inserted = false; - - for idx in elems { - buf.clear(); - analysis.pretty_print_idx(&mut buf, idx)?; - let idx_str = - str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8"); - let escaped = dot::escape_html(idx_str); - let escaped_width = escaped.chars().count(); - - if first { - first = false; - } else { - write!(w, "{}", sep)?; - curr_line_width += sep_width; - - if let Some(line_break) = &line_break { - if curr_line_width + sep_width + escaped_width > line_break.limit { - write!(w, "{}", line_break.sequence)?; - line_break_inserted = true; - curr_line_width = 0; - } - } - } - - write!(w, "{}", escaped)?; - curr_line_width += escaped_width; - } - - Ok(line_break_inserted) -} - -/// The background color used for zebra-striping the table. -#[derive(Clone, Copy)] -enum Background { - Light, - Dark, -} - -impl Background { - fn attr(self) -> &'static str { - match self { - Self::Dark => "bgcolor=\"#f0f0f0\"", - Self::Light => "", - } - } -} - -impl ops::Not for Background { - type Output = Self; - - fn not(self) -> Self { - match self { - Self::Light => Self::Dark, - Self::Dark => Self::Light, - } - } -} diff --git a/src/librustc_mir/dataflow/generic/mod.rs b/src/librustc_mir/dataflow/generic/mod.rs deleted file mode 100644 index fb4b7b9c5be31..0000000000000 --- a/src/librustc_mir/dataflow/generic/mod.rs +++ /dev/null @@ -1,487 +0,0 @@ -//! A framework that can express both [gen-kill] and generic dataflow problems. -//! -//! There is another interface for dataflow in the compiler in `librustc_mir/dataflow/mod.rs`. The -//! interface in this module will eventually [replace that one][design-meeting]. -//! -//! To actually use this framework, you must implement either the `Analysis` or the `GenKillAnalysis` -//! trait. If your transfer function can be expressed with only gen/kill operations, prefer -//! `GenKillAnalysis` since it will run faster while iterating to fixpoint. Create an `Engine` using -//! the appropriate constructor and call `iterate_to_fixpoint`. You can use a `ResultsCursor` to -//! inspect the fixpoint solution to your dataflow problem. -//! -//! ```ignore(cross-crate-imports) -//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) { -//! let analysis = MyAnalysis::new(); -//! -//! // If `MyAnalysis` implements `GenKillAnalysis`. -//! let results = Engine::new_gen_kill(tcx, body, did, analysis).iterate_to_fixpoint(); -//! -//! // If `MyAnalysis` implements `Analysis`. -//! // let results = Engine::new_generic(tcx, body, did, analysis).iterate_to_fixpoint(); -//! -//! let mut cursor = ResultsCursor::new(body, results); -//! -//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() { -//! cursor.seek_after(Location { block: START_BLOCK, statement_index }); -//! let state = cursor.get(); -//! println!("{:?}", state); -//! } -//! } -//! ``` -//! -//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems -//! [design-meeting]https://github.com/rust-lang/compiler-team/issues/202 - -use std::io; - -use rustc::mir::{self, BasicBlock, Location}; -use rustc::ty::layout::VariantIdx; -use rustc::ty::{self, TyCtxt}; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::{Idx, IndexVec}; - -use crate::dataflow::BottomValue; - -mod cursor; -mod engine; -mod graphviz; -mod visitor; - -pub use self::cursor::{ResultsCursor, ResultsRefCursor}; -pub use self::engine::Engine; -pub use self::visitor::{visit_results, ResultsVisitor}; -pub use self::visitor::{BorrowckFlowState, BorrowckResults}; - -/// A dataflow analysis that has converged to fixpoint. -pub struct Results<'tcx, A> -where - A: Analysis<'tcx>, -{ - pub analysis: A, - entry_sets: IndexVec>, -} - -impl Results<'tcx, A> -where - A: Analysis<'tcx>, -{ - /// Creates a `ResultsCursor` that can inspect these `Results`. - pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> { - ResultsCursor::new(body, self) - } - - /// Gets the entry set for the given block. - pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet { - &self.entry_sets[block] - } - - pub fn visit_with( - &self, - body: &'mir mir::Body<'tcx>, - blocks: impl IntoIterator, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet>, - ) { - visit_results(body, blocks, self, vis) - } - - pub fn visit_in_rpo_with( - &self, - body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet>, - ) { - let blocks = mir::traversal::reverse_postorder(body); - visit_results(body, blocks.map(|(bb, _)| bb), self, vis) - } -} - -/// Define the domain of a dataflow problem. -/// -/// This trait specifies the lattice on which this analysis operates. For now, this must be a -/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet` -/// and referred to as the state vector. -/// -/// This trait also defines the initial value for the dataflow state upon entry to the -/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging. -pub trait AnalysisDomain<'tcx>: BottomValue { - /// The type of the elements in the state vector. - type Idx: Idx; - - /// A descriptive name for this analysis. Used only for debugging. - /// - /// This name should be brief and contain no spaces, periods or other characters that are not - /// suitable as part of a filename. - const NAME: &'static str; - - /// The size of the state vector. - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize; - - /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow - /// analysis. - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet); - - /// Prints an element in the state vector for debugging. - fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> { - write!(w, "{:?}", idx) - } -} - -/// A dataflow problem with an arbitrarily complex transfer function. -pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { - /// Updates the current dataflow state with the effect of evaluating a statement. - fn apply_statement_effect( - &self, - state: &mut BitSet, - statement: &mir::Statement<'tcx>, - location: Location, - ); - - /// Updates the current dataflow state with an effect that occurs immediately *before* the - /// given statement. - /// - /// This method is useful if the consumer of the results of this analysis needs only to observe - /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without implementing `apply_statement_effect`. - fn apply_before_statement_effect( - &self, - _state: &mut BitSet, - _statement: &mir::Statement<'tcx>, - _location: Location, - ) { - } - - /// Updates the current dataflow state with the effect of evaluating a terminator. - /// - /// The effect of a successful return from a `Call` terminator should **not** be accounted for - /// in this function. That should go in `apply_call_return_effect`. For example, in the - /// `InitializedPlaces` analyses, the return place for a function call is not marked as - /// initialized here. - fn apply_terminator_effect( - &self, - state: &mut BitSet, - terminator: &mir::Terminator<'tcx>, - location: Location, - ); - - /// Updates the current dataflow state with an effect that occurs immediately *before* the - /// given terminator. - /// - /// This method is useful if the consumer of the results of this analysis needs only to observe - /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without implementing `apply_terminator_effect`. - fn apply_before_terminator_effect( - &self, - _state: &mut BitSet, - _terminator: &mir::Terminator<'tcx>, - _location: Location, - ) { - } - - /// Updates the current dataflow state with the effect of a successful return from a `Call` - /// terminator. - /// - /// This is separate from `apply_terminator_effect` to properly track state across unwind - /// edges. - fn apply_call_return_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], - return_place: &mir::Place<'tcx>, - ); - - /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator. - /// - /// This is similar to `apply_call_return_effect` in that it only takes place after the - /// generator is resumed, not when it is dropped. - /// - /// By default, no effects happen. - fn apply_yield_resume_effect( - &self, - _state: &mut BitSet, - _resume_block: BasicBlock, - _resume_place: &mir::Place<'tcx>, - ) { - } - - /// Updates the current dataflow state with the effect of taking a particular branch in a - /// `SwitchInt` terminator. - /// - /// Much like `apply_call_return_effect`, this effect is only propagated along a single - /// outgoing edge from this basic block. - fn apply_discriminant_switch_effect( - &self, - _state: &mut BitSet, - _block: BasicBlock, - _enum_place: &mir::Place<'tcx>, - _adt: &ty::AdtDef, - _variant: VariantIdx, - ) { - } - - /// Creates an `Engine` to find the fixpoint for this dataflow problem. - /// - /// You shouldn't need to override this outside this module, since the combination of the - /// default impl and the one for all `A: GenKillAnalysis` will do the right thing. - /// Its purpose is to enable method chaining like so: - /// - /// ```ignore(cross-crate-imports) - /// let results = MyAnalysis::new(tcx, body) - /// .into_engine(tcx, body, def_id) - /// .iterate_to_fixpoint() - /// .into_results_cursor(body); - /// ``` - fn into_engine( - self, - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - def_id: DefId, - ) -> Engine<'mir, 'tcx, Self> - where - Self: Sized, - { - Engine::new_generic(tcx, body, def_id, self) - } -} - -/// A gen/kill dataflow problem. -/// -/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only -/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer -/// functions for each statement in this way, the transfer function for an entire basic block can -/// be computed efficiently. -/// -/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`. -pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { - /// See `Analysis::apply_statement_effect`. - fn statement_effect( - &self, - trans: &mut impl GenKill, - statement: &mir::Statement<'tcx>, - location: Location, - ); - - /// See `Analysis::apply_before_statement_effect`. - fn before_statement_effect( - &self, - _trans: &mut impl GenKill, - _statement: &mir::Statement<'tcx>, - _location: Location, - ) { - } - - /// See `Analysis::apply_terminator_effect`. - fn terminator_effect( - &self, - trans: &mut impl GenKill, - terminator: &mir::Terminator<'tcx>, - location: Location, - ); - - /// See `Analysis::apply_before_terminator_effect`. - fn before_terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _location: Location, - ) { - } - - /// See `Analysis::apply_call_return_effect`. - fn call_return_effect( - &self, - trans: &mut impl GenKill, - block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], - return_place: &mir::Place<'tcx>, - ); - - /// See `Analysis::apply_yield_resume_effect`. - fn yield_resume_effect( - &self, - _trans: &mut BitSet, - _resume_block: BasicBlock, - _resume_place: &mir::Place<'tcx>, - ) { - } - - /// See `Analysis::apply_discriminant_switch_effect`. - fn discriminant_switch_effect( - &self, - _state: &mut impl GenKill, - _block: BasicBlock, - _enum_place: &mir::Place<'tcx>, - _adt: &ty::AdtDef, - _variant: VariantIdx, - ) { - } -} - -impl Analysis<'tcx> for A -where - A: GenKillAnalysis<'tcx>, -{ - fn apply_statement_effect( - &self, - state: &mut BitSet, - statement: &mir::Statement<'tcx>, - location: Location, - ) { - self.statement_effect(state, statement, location); - } - - fn apply_before_statement_effect( - &self, - state: &mut BitSet, - statement: &mir::Statement<'tcx>, - location: Location, - ) { - self.before_statement_effect(state, statement, location); - } - - fn apply_terminator_effect( - &self, - state: &mut BitSet, - terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - self.terminator_effect(state, terminator, location); - } - - fn apply_before_terminator_effect( - &self, - state: &mut BitSet, - terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - self.before_terminator_effect(state, terminator, location); - } - - fn apply_call_return_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], - return_place: &mir::Place<'tcx>, - ) { - self.call_return_effect(state, block, func, args, return_place); - } - - fn apply_yield_resume_effect( - &self, - state: &mut BitSet, - resume_block: BasicBlock, - resume_place: &mir::Place<'tcx>, - ) { - self.yield_resume_effect(state, resume_block, resume_place); - } - - fn apply_discriminant_switch_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - enum_place: &mir::Place<'tcx>, - adt: &ty::AdtDef, - variant: VariantIdx, - ) { - self.discriminant_switch_effect(state, block, enum_place, adt, variant); - } - - fn into_engine( - self, - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - def_id: DefId, - ) -> Engine<'mir, 'tcx, Self> - where - Self: Sized, - { - Engine::new_gen_kill(tcx, body, def_id, self) - } -} - -/// The legal operations for a transfer function in a gen/kill problem. -/// -/// This abstraction exists because there are two different contexts in which we call the methods in -/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently -/// applied multiple times, such as when computing the cumulative transfer function for each block. -/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes, -/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the -/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to -/// building up a `GenKillSet` and then throwing it away. -pub trait GenKill { - /// Inserts `elem` into the state vector. - fn gen(&mut self, elem: T); - - /// Removes `elem` from the state vector. - fn kill(&mut self, elem: T); - - /// Calls `gen` for each element in `elems`. - fn gen_all(&mut self, elems: impl IntoIterator) { - for elem in elems { - self.gen(elem); - } - } - - /// Calls `kill` for each element in `elems`. - fn kill_all(&mut self, elems: impl IntoIterator) { - for elem in elems { - self.kill(elem); - } - } -} - -/// Stores a transfer function for a gen/kill problem. -/// -/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be -/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for -/// the same element, the most recent one takes precedence. -#[derive(Clone)] -pub struct GenKillSet { - gen: HybridBitSet, - kill: HybridBitSet, -} - -impl GenKillSet { - /// Creates a new transfer function that will leave the dataflow state unchanged. - pub fn identity(universe: usize) -> Self { - GenKillSet { - gen: HybridBitSet::new_empty(universe), - kill: HybridBitSet::new_empty(universe), - } - } - - /// Applies this transfer function to the given state vector. - pub fn apply(&self, state: &mut BitSet) { - state.union(&self.gen); - state.subtract(&self.kill); - } -} - -impl GenKill for GenKillSet { - fn gen(&mut self, elem: T) { - self.gen.insert(elem); - self.kill.remove(elem); - } - - fn kill(&mut self, elem: T) { - self.kill.insert(elem); - self.gen.remove(elem); - } -} - -impl GenKill for BitSet { - fn gen(&mut self, elem: T) { - self.insert(elem); - } - - fn kill(&mut self, elem: T) { - self.remove(elem); - } -} - -#[cfg(test)] -mod tests; diff --git a/src/librustc_mir/dataflow/generic/tests.rs b/src/librustc_mir/dataflow/generic/tests.rs deleted file mode 100644 index 8f07a10e1b01c..0000000000000 --- a/src/librustc_mir/dataflow/generic/tests.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! A test for the logic that updates the state in a `ResultsCursor` during seek. - -use rustc::mir::{self, BasicBlock, Location}; -use rustc::ty; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; -use rustc_span::DUMMY_SP; - -use super::*; -use crate::dataflow::BottomValue; - -/// Returns `true` if the given location points to a `Call` terminator that can return -/// successfully. -fn is_call_terminator_non_diverging(body: &mir::Body<'_>, loc: Location) -> bool { - loc == body.terminator_loc(loc.block) - && matches!( - body[loc.block].terminator().kind, - mir::TerminatorKind::Call { destination: Some(_), .. } - ) -} - -/// Creates a `mir::Body` with a few disconnected basic blocks. -/// -/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not -/// important. -fn mock_body() -> mir::Body<'static> { - let source_info = mir::SourceInfo { scope: mir::OUTERMOST_SOURCE_SCOPE, span: DUMMY_SP }; - - let mut blocks = IndexVec::new(); - let mut block = |n, kind| { - let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop }; - - blocks.push(mir::BasicBlockData { - statements: std::iter::repeat(&nop).cloned().take(n).collect(), - terminator: Some(mir::Terminator { source_info, kind }), - is_cleanup: false, - }) - }; - - let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() }; - - block(4, mir::TerminatorKind::Return); - block(1, mir::TerminatorKind::Return); - block( - 2, - mir::TerminatorKind::Call { - func: mir::Operand::Copy(dummy_place.clone()), - args: vec![], - destination: Some((dummy_place.clone(), mir::START_BLOCK)), - cleanup: None, - from_hir_call: false, - }, - ); - block(3, mir::TerminatorKind::Return); - block(0, mir::TerminatorKind::Return); - block( - 4, - mir::TerminatorKind::Call { - func: mir::Operand::Copy(dummy_place.clone()), - args: vec![], - destination: Some((dummy_place.clone(), mir::START_BLOCK)), - cleanup: None, - from_hir_call: false, - }, - ); - - mir::Body::new_cfg_only(blocks) -} - -/// A dataflow analysis whose state is unique at every possible `SeekTarget`. -/// -/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and -/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is -/// *globally* unique (see `mock_entry_set`). -/// -/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each -/// location ("+x" indicates that "x" is added to the state). -/// -/// | Location | Before | After | -/// |------------------------|-------------------|--------| -/// | (on_entry) | {102} || -/// | Statement 0 | +0 | +1 | -/// | statement 1 | +2 | +3 | -/// | `Call` terminator | +4 | +5 | -/// | (on unwind) | {102,0,1,2,3,4,5} || -/// | (on successful return) | +6 || -/// -/// The `102` in the block's entry set is derived from the basic block index and ensures that the -/// expected state is unique across all basic blocks. Remember, it is generated by -/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint. -struct MockAnalysis<'tcx> { - body: &'tcx mir::Body<'tcx>, -} - -impl MockAnalysis<'tcx> { - const BASIC_BLOCK_OFFSET: usize = 100; - - /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to - /// avoid colliding with the statement/terminator effects. - fn mock_entry_set(&self, bb: BasicBlock) -> BitSet { - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); - ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); - ret - } - - fn mock_entry_sets(&self) -> IndexVec> { - let empty = BitSet::new_empty(self.bits_per_block(self.body)); - let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks()); - - for (bb, _) in self.body.basic_blocks().iter_enumerated() { - ret[bb] = self.mock_entry_set(bb); - } - - ret - } - - /// Returns the index that should be added to the dataflow state at the given target. - /// - /// This index is only unique within a given basic block. `SeekAfter` and - /// `SeekAfterAssumeCallReturns` have the same effect unless `target` is a `Call` terminator. - fn effect_at_target(&self, target: SeekTarget) -> Option { - use SeekTarget::*; - - let idx = match target { - BlockStart(_) => return None, - - AfterAssumeCallReturns(loc) if is_call_terminator_non_diverging(self.body, loc) => { - loc.statement_index * 2 + 2 - } - - Before(loc) => loc.statement_index * 2, - After(loc) | AfterAssumeCallReturns(loc) => loc.statement_index * 2 + 1, - }; - - assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block"); - Some(idx) - } - - /// Returns the expected state at the given `SeekTarget`. - /// - /// This is the union of index of the target basic block, the index assigned to the - /// target statement or terminator, and the indices of all preceding statements in the target - /// basic block. - /// - /// For example, the expected state when calling - /// `seek_before(Location { block: 2, statement_index: 2 })` would be `[102, 0, 1, 2, 3, 4]`. - fn expected_state_at_target(&self, target: SeekTarget) -> BitSet { - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); - ret.insert(Self::BASIC_BLOCK_OFFSET + target.block().index()); - - if let Some(target_effect) = self.effect_at_target(target) { - for i in 0..=target_effect { - ret.insert(i); - } - } - - ret - } -} - -impl BottomValue for MockAnalysis<'tcx> { - const BOTTOM_VALUE: bool = false; -} - -impl AnalysisDomain<'tcx> for MockAnalysis<'tcx> { - type Idx = usize; - - const NAME: &'static str = "mock"; - - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len() - } - - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { - unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); - } -} - -impl Analysis<'tcx> for MockAnalysis<'tcx> { - fn apply_statement_effect( - &self, - state: &mut BitSet, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - let idx = self.effect_at_target(SeekTarget::After(location)).unwrap(); - assert!(state.insert(idx)); - } - - fn apply_before_statement_effect( - &self, - state: &mut BitSet, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap(); - assert!(state.insert(idx)); - } - - fn apply_terminator_effect( - &self, - state: &mut BitSet, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - let idx = self.effect_at_target(SeekTarget::After(location)).unwrap(); - assert!(state.insert(idx)); - } - - fn apply_before_terminator_effect( - &self, - state: &mut BitSet, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap(); - assert!(state.insert(idx)); - } - - fn apply_call_return_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - _return_place: &mir::Place<'tcx>, - ) { - let location = self.body.terminator_loc(block); - let idx = self.effect_at_target(SeekTarget::AfterAssumeCallReturns(location)).unwrap(); - assert!(state.insert(idx)); - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum SeekTarget { - BlockStart(BasicBlock), - Before(Location), - After(Location), - AfterAssumeCallReturns(Location), -} - -impl SeekTarget { - fn block(&self) -> BasicBlock { - use SeekTarget::*; - - match *self { - BlockStart(block) => block, - Before(loc) | After(loc) | AfterAssumeCallReturns(loc) => loc.block, - } - } - - /// An iterator over all possible `SeekTarget`s in a given block in order, starting with - /// `BlockStart`. - /// - /// This includes both `After` and `AfterAssumeCallReturns` for every `Location`. - fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator { - let statements_and_terminator = (0..=body[block].statements.len()) - .flat_map(|i| (0..3).map(move |j| (i, j))) - .map(move |(i, kind)| { - let loc = Location { block, statement_index: i }; - match kind { - 0 => SeekTarget::Before(loc), - 1 => SeekTarget::After(loc), - 2 => SeekTarget::AfterAssumeCallReturns(loc), - _ => unreachable!(), - } - }); - - std::iter::once(SeekTarget::BlockStart(block)).chain(statements_and_terminator) - } -} - -#[test] -fn cursor_seek() { - let body = mock_body(); - let body = &body; - let analysis = MockAnalysis { body }; - - let mut cursor = - Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); - - // Sanity check: the mock call return effect is unique and actually being applied. - let call_terminator_loc = Location { block: BasicBlock::from_usize(2), statement_index: 2 }; - assert!(is_call_terminator_non_diverging(body, call_terminator_loc)); - - let call_return_effect = cursor - .analysis() - .effect_at_target(SeekTarget::AfterAssumeCallReturns(call_terminator_loc)) - .unwrap(); - assert_ne!( - call_return_effect, - cursor.analysis().effect_at_target(SeekTarget::After(call_terminator_loc)).unwrap() - ); - - cursor.seek_after(call_terminator_loc); - assert!(!cursor.get().contains(call_return_effect)); - cursor.seek_after_assume_success(call_terminator_loc); - assert!(cursor.get().contains(call_return_effect)); - - let every_target = || { - body.basic_blocks() - .iter_enumerated() - .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb)) - }; - - let mut seek_to_target = |targ| { - use SeekTarget::*; - - match targ { - BlockStart(block) => cursor.seek_to_block_start(block), - Before(loc) => cursor.seek_before(loc), - After(loc) => cursor.seek_after(loc), - AfterAssumeCallReturns(loc) => cursor.seek_after_assume_success(loc), - } - - assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ)); - }; - - // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`. - // - // By resetting the cursor to `from` each time it changes, we end up checking some edges twice. - // What we really want is an Eulerian cycle for the complete digraph over all possible - // `SeekTarget`s, but it's not worth spending the time to compute it. - for from in every_target() { - seek_to_target(from); - - for to in every_target() { - seek_to_target(to); - seek_to_target(from); - } - } -} diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/dataflow/graphviz.rs deleted file mode 100644 index a9ef7ef6c528a..0000000000000 --- a/src/librustc_mir/dataflow/graphviz.rs +++ /dev/null @@ -1,277 +0,0 @@ -//! Hook into libgraphviz for rendering dataflow graphs for MIR. - -use rustc::mir::{BasicBlock, Body}; -use rustc_hir::def_id::DefId; - -use std::fs; -use std::io; -use std::marker::PhantomData; -use std::path::Path; - -use crate::util::graphviz_safe_def_name; - -use super::DataflowBuilder; -use super::DebugFormatted; -use super::{BitDenotation, DataflowState}; - -pub trait MirWithFlowState<'tcx> { - type BD: BitDenotation<'tcx>; - fn def_id(&self) -> DefId; - fn body(&self) -> &Body<'tcx>; - fn flow_state(&self) -> &DataflowState<'tcx, Self::BD>; -} - -impl<'a, 'tcx, BD> MirWithFlowState<'tcx> for DataflowBuilder<'a, 'tcx, BD> -where - BD: BitDenotation<'tcx>, -{ - type BD = BD; - fn def_id(&self) -> DefId { - self.def_id - } - fn body(&self) -> &Body<'tcx> { - self.flow_state.body() - } - fn flow_state(&self) -> &DataflowState<'tcx, Self::BD> { - &self.flow_state.flow_state - } -} - -struct Graph<'a, 'tcx, MWF, P> -where - MWF: MirWithFlowState<'tcx>, -{ - mbcx: &'a MWF, - phantom: PhantomData<&'tcx ()>, - render_idx: P, -} - -pub(crate) fn print_borrowck_graph_to<'a, 'tcx, BD, P>( - mbcx: &DataflowBuilder<'a, 'tcx, BD>, - path: &Path, - render_idx: P, -) -> io::Result<()> -where - BD: BitDenotation<'tcx>, - P: Fn(&BD, BD::Idx) -> DebugFormatted, -{ - let g = Graph { mbcx, phantom: PhantomData, render_idx }; - let mut v = Vec::new(); - dot::render(&g, &mut v)?; - debug!("print_borrowck_graph_to path: {} def_id: {:?}", path.display(), mbcx.def_id); - fs::write(path, v) -} - -pub type Node = BasicBlock; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Edge { - source: BasicBlock, - index: usize, -} - -fn outgoing(body: &Body<'_>, bb: BasicBlock) -> Vec { - (0..body[bb].terminator().successors().count()) - .map(|index| Edge { source: bb, index }) - .collect() -} - -impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> -where - MWF: MirWithFlowState<'tcx>, - P: Fn(&MWF::BD, >::Idx) -> DebugFormatted, -{ - type Node = Node; - type Edge = Edge; - fn graph_id(&self) -> dot::Id<'_> { - let name = graphviz_safe_def_name(self.mbcx.def_id()); - dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() - } - - fn node_id(&self, n: &Node) -> dot::Id<'_> { - dot::Id::new(format!("bb_{}", n.index())).unwrap() - } - - fn node_label(&self, n: &Node) -> dot::LabelText<'_> { - // Node label is something like this: - // +---------+----------------------------------+------------------+------------------+ - // | ENTRY | MIR | GEN | KILL | - // +---------+----------------------------------+------------------+------------------+ - // | | 0: StorageLive(_7) | bb3[2]: reserved | bb2[0]: reserved | - // | | 1: StorageLive(_8) | bb3[2]: active | bb2[0]: active | - // | | 2: _8 = &mut _1 | | bb4[2]: reserved | - // | | | | bb4[2]: active | - // | | | | bb9[0]: reserved | - // | | | | bb9[0]: active | - // | | | | bb10[0]: reserved| - // | | | | bb10[0]: active | - // | | | | bb11[0]: reserved| - // | | | | bb11[0]: active | - // +---------+----------------------------------+------------------+------------------+ - // | [00-00] | _7 = const Foo::twiddle(move _8) | [0c-00] | [f3-0f] | - // +---------+----------------------------------+------------------+------------------+ - let mut v = Vec::new(); - self.node_label_internal(n, &mut v, *n, self.mbcx.body()).unwrap(); - dot::LabelText::html(String::from_utf8(v).unwrap()) - } - - fn node_shape(&self, _n: &Node) -> Option> { - Some(dot::LabelText::label("none")) - } - - fn edge_label(&'a self, e: &Edge) -> dot::LabelText<'a> { - let term = self.mbcx.body()[e.source].terminator(); - let label = &term.kind.fmt_successor_labels()[e.index]; - dot::LabelText::label(label.clone()) - } -} - -impl<'a, 'tcx, MWF, P> Graph<'a, 'tcx, MWF, P> -where - MWF: MirWithFlowState<'tcx>, - P: Fn(&MWF::BD, >::Idx) -> DebugFormatted, -{ - /// Generate the node label - fn node_label_internal( - &self, - n: &Node, - w: &mut W, - block: BasicBlock, - body: &Body<'_>, - ) -> io::Result<()> { - // Header rows - const HDRS: [&str; 4] = ["ENTRY", "MIR", "BLOCK GENS", "BLOCK KILLS"]; - const HDR_FMT: &str = "bgcolor=\"grey\""; - write!(w, "")?; - for hdr in &HDRS { - write!(w, "", HDR_FMT, hdr)?; - } - write!(w, "")?; - - // Data row - self.node_label_verbose_row(n, w, block, body)?; - self.node_label_final_row(n, w, block, body)?; - write!(w, "
", HDRS.len())?; - write!(w, "{:?}", block.index())?; - write!(w, "
{}
")?; - - Ok(()) - } - - /// Builds the verbose row: full MIR data, and detailed gen/kill/entry sets. - fn node_label_verbose_row( - &self, - n: &Node, - w: &mut W, - block: BasicBlock, - body: &Body<'_>, - ) -> io::Result<()> { - let i = n.index(); - - macro_rules! dump_set_for { - ($set:ident, $interpret:ident) => { - write!(w, "")?; - - let flow = self.mbcx.flow_state(); - let entry_interp = - flow.$interpret(&flow.operator, flow.sets.$set(i), &self.render_idx); - for e in &entry_interp { - write!(w, "{:?}
", e)?; - } - write!(w, "")?; - }; - } - - write!(w, "")?; - // Entry - dump_set_for!(entry_set_for, interpret_set); - - // MIR statements - write!(w, "")?; - { - let data = &body[block]; - for (i, statement) in data.statements.iter().enumerate() { - write!( - w, - "{}
", - dot::escape_html(&format!("{:3}: {:?}", i, statement)) - )?; - } - } - write!(w, "")?; - - // Gen - dump_set_for!(gen_set_for, interpret_hybrid_set); - - // Kill - dump_set_for!(kill_set_for, interpret_hybrid_set); - - write!(w, "")?; - - Ok(()) - } - - /// Builds the summary row: terminator, gen/kill/entry bit sets. - fn node_label_final_row( - &self, - n: &Node, - w: &mut W, - block: BasicBlock, - body: &Body<'_>, - ) -> io::Result<()> { - let i = n.index(); - - let flow = self.mbcx.flow_state(); - - write!(w, "")?; - - // Entry - let set = flow.sets.entry_set_for(i); - write!(w, "{:?}", dot::escape_html(&set.to_string()))?; - - // Terminator - write!(w, "")?; - { - let data = &body[block]; - let mut terminator_head = String::new(); - data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); - write!(w, "{}", dot::escape_html(&terminator_head))?; - } - write!(w, "")?; - - // Gen/Kill - let trans = flow.sets.trans_for(i); - write!(w, "{:?}", dot::escape_html(&format!("{:?}", trans.gen_set)))?; - write!(w, "{:?}", dot::escape_html(&format!("{:?}", trans.kill_set)))?; - - write!(w, "")?; - - Ok(()) - } -} - -impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P> -where - MWF: MirWithFlowState<'tcx>, -{ - type Node = Node; - type Edge = Edge; - fn nodes(&self) -> dot::Nodes<'_, Node> { - self.mbcx.body().basic_blocks().indices().collect::>().into() - } - - fn edges(&self) -> dot::Edges<'_, Edge> { - let body = self.mbcx.body(); - - body.basic_blocks().indices().flat_map(|bb| outgoing(body, bb)).collect::>().into() - } - - fn source(&self, edge: &Edge) -> Node { - edge.source - } - - fn target(&self, edge: &Edge) -> Node { - let body = self.mbcx.body(); - *body[edge.source].terminator().successors().nth(edge.index).unwrap() - } -} diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index 95a676c0892c5..f929b2ddde0ba 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs @@ -1,9 +1,9 @@ pub use super::*; -use crate::dataflow::generic::{AnalysisDomain, GenKill, GenKillAnalysis}; -use rustc::mir::visit::Visitor; -use rustc::mir::*; -use rustc::ty::{ParamEnv, TyCtxt}; +use crate::dataflow::{AnalysisDomain, GenKill, GenKillAnalysis}; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::{ParamEnv, TyCtxt}; use rustc_span::DUMMY_SP; pub type MaybeMutBorrowedLocals<'mir, 'tcx> = MaybeBorrowedLocals>; @@ -123,7 +123,7 @@ where _block: mir::BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - _dest_place: &mir::Place<'tcx>, + _dest_place: mir::Place<'tcx>, ) { } } @@ -160,13 +160,13 @@ where match rvalue { mir::Rvalue::AddressOf(mt, borrowed_place) => { - if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, borrowed_place) { + if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, *borrowed_place) { self.trans.gen(borrowed_place.local); } } mir::Rvalue::Ref(_, kind, borrowed_place) => { - if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, borrowed_place) { + if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, *borrowed_place) { self.trans.gen(borrowed_place.local); } } @@ -203,6 +203,7 @@ where | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } + | TerminatorKind::InlineAsm { .. } | TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } @@ -230,7 +231,7 @@ impl MutBorrow<'mir, 'tcx> { /// below. See [rust-lang/unsafe-code-guidelines#134]. /// /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134 - fn shared_borrow_allows_mutation(&self, place: &Place<'tcx>) -> bool { + fn shared_borrow_allows_mutation(&self, place: Place<'tcx>) -> bool { !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) } } @@ -238,17 +239,17 @@ impl MutBorrow<'mir, 'tcx> { pub trait BorrowAnalysisKind<'tcx> { const ANALYSIS_NAME: &'static str; - fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool; - fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool; + fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool; + fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool; } impl BorrowAnalysisKind<'tcx> for AnyBorrow { const ANALYSIS_NAME: &'static str = "maybe_borrowed_locals"; - fn in_ref(&self, _: mir::BorrowKind, _: &Place<'_>) -> bool { + fn in_ref(&self, _: mir::BorrowKind, _: Place<'_>) -> bool { true } - fn in_address_of(&self, _: Mutability, _: &Place<'_>) -> bool { + fn in_address_of(&self, _: Mutability, _: Place<'_>) -> bool { true } } @@ -256,7 +257,7 @@ impl BorrowAnalysisKind<'tcx> for AnyBorrow { impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> { const ANALYSIS_NAME: &'static str = "maybe_mut_borrowed_locals"; - fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool { + fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool { match kind { mir::BorrowKind::Mut { .. } => true, mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => { @@ -265,7 +266,7 @@ impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> { } } - fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool { + fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool { match mt { Mutability::Mut => true, Mutability::Not => self.shared_borrow_allows_mutation(place), diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index a7c0efd63b1a8..dfca270396de9 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -1,6 +1,6 @@ -use rustc::mir::{self, Body, Location, Place}; -use rustc::ty::RegionVid; -use rustc::ty::TyCtxt; +use rustc_middle::mir::{self, Body, Location, Place}; +use rustc_middle::ty::RegionVid; +use rustc_middle::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::BitSet; @@ -8,8 +8,8 @@ use rustc_index::bit_set::BitSet; use crate::borrow_check::{ places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid, }; -use crate::dataflow::generic::{self, GenKill}; use crate::dataflow::BottomValue; +use crate::dataflow::{self, GenKill}; use std::rc::Rc; @@ -187,7 +187,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } /// Kill any borrows that conflict with `place`. - fn kill_borrows_on_place(&self, trans: &mut impl GenKill, place: &Place<'tcx>) { + fn kill_borrows_on_place(&self, trans: &mut impl GenKill, place: Place<'tcx>) { debug!("kill_borrows_on_place: place={:?}", place); let other_borrows_of_local = self @@ -216,7 +216,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { places_conflict( self.tcx, self.body, - &self.borrow_set.borrows[i].borrowed_place, + self.borrow_set.borrows[i].borrowed_place, place, PlaceConflictBias::NoOverlap, ) @@ -226,7 +226,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } } -impl<'tcx> generic::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { +impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { type Idx = BorrowIndex; const NAME: &'static str = "borrows"; @@ -245,7 +245,7 @@ impl<'tcx> generic::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { } } -impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { +impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { fn before_statement_effect( &self, trans: &mut impl GenKill, @@ -262,8 +262,8 @@ impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { location: Location, ) { match stmt.kind { - mir::StatementKind::Assign(box (ref lhs, ref rhs)) => { - if let mir::Rvalue::Ref(_, _, ref place) = *rhs { + mir::StatementKind::Assign(box (lhs, ref rhs)) => { + if let mir::Rvalue::Ref(_, _, place) = *rhs { if place.ignore_borrow( self.tcx, self.body, @@ -286,13 +286,13 @@ impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { mir::StatementKind::StorageDead(local) => { // Make sure there are no remaining borrows for locals that // are gone out of scope. - self.kill_borrows_on_place(trans, &Place::from(local)); + self.kill_borrows_on_place(trans, Place::from(local)); } - mir::StatementKind::InlineAsm(ref asm) => { + mir::StatementKind::LlvmInlineAsm(ref asm) => { for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) { if !kind.is_indirect && !kind.is_rw { - self.kill_borrows_on_place(trans, output); + self.kill_borrows_on_place(trans, *output); } } } @@ -317,10 +317,19 @@ impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { fn terminator_effect( &self, - _: &mut impl GenKill, - _: &mir::Terminator<'tcx>, - _: Location, + trans: &mut impl GenKill, + teminator: &mir::Terminator<'tcx>, + _location: Location, ) { + if let mir::TerminatorKind::InlineAsm { operands, .. } = &teminator.kind { + for op in operands { + if let mir::InlineAsmOperand::Out { place: Some(place), .. } + | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op + { + self.kill_borrows_on_place(trans, place); + } + } + } } fn call_return_effect( @@ -329,7 +338,7 @@ impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { _block: mir::BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - _dest_place: &mir::Place<'tcx>, + _dest_place: mir::Place<'tcx>, ) { } } diff --git a/src/librustc_mir/dataflow/impls/liveness.rs b/src/librustc_mir/dataflow/impls/liveness.rs new file mode 100644 index 0000000000000..d24faacd3779e --- /dev/null +++ b/src/librustc_mir/dataflow/impls/liveness.rs @@ -0,0 +1,144 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{self, Local, Location}; + +use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis}; + +/// A [live-variable dataflow analysis][liveness]. +/// +/// This analysis considers references as being used only at the point of the +/// borrow. In other words, this analysis does not track uses because of references that already +/// exist. See [this `mir-datalow` test][flow-test] for an example. You almost never want to use +/// this analysis without also looking at the results of [`MaybeBorrowedLocals`]. +/// +/// [`MaybeBorrowedLocals`]: ../struct.MaybeBorrowedLocals.html +/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs +/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis +pub struct MaybeLiveLocals; + +impl MaybeLiveLocals { + fn transfer_function(&self, trans: &'a mut T) -> TransferFunction<'a, T> { + TransferFunction(trans) + } +} + +impl BottomValue for MaybeLiveLocals { + // bottom = not live + const BOTTOM_VALUE: bool = false; +} + +impl AnalysisDomain<'tcx> for MaybeLiveLocals { + type Idx = Local; + type Direction = Backward; + + const NAME: &'static str = "liveness"; + + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { + body.local_decls.len() + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { + // No variables are live until we observe a use + } +} + +impl GenKillAnalysis<'tcx> for MaybeLiveLocals { + fn statement_effect( + &self, + trans: &mut impl GenKill, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + self.transfer_function(trans).visit_statement(statement, location); + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + self.transfer_function(trans).visit_terminator(terminator, location); + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + dest_place: mir::Place<'tcx>, + ) { + if let Some(local) = dest_place.as_local() { + trans.kill(local); + } + } + + fn yield_resume_effect( + &self, + trans: &mut impl GenKill, + _resume_block: mir::BasicBlock, + resume_place: mir::Place<'tcx>, + ) { + if let Some(local) = resume_place.as_local() { + trans.kill(local); + } + } +} + +struct TransferFunction<'a, T>(&'a mut T); + +impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> +where + T: GenKill, +{ + fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { + match DefUse::for_place(context) { + Some(DefUse::Def) => self.0.kill(local), + Some(DefUse::Use) => self.0.gen(local), + _ => {} + } + } +} + +#[derive(Eq, PartialEq, Clone)] +enum DefUse { + Def, + Use, +} + +impl DefUse { + fn for_place(context: PlaceContext) -> Option { + match context { + PlaceContext::NonUse(_) => None, + + PlaceContext::MutatingUse(MutatingUseContext::Store) => Some(DefUse::Def), + + // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the + // destination place for a `Call` return or `Yield` resume respectively. Since this is + // only a `Def` when the function returns succesfully, we handle this case separately + // in `call_return_effect` above. + PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None, + + // All other contexts are uses... + PlaceContext::MutatingUse( + MutatingUseContext::AddressOf + | MutatingUseContext::AsmOutput + | MutatingUseContext::Borrow + | MutatingUseContext::Drop + | MutatingUseContext::Projection + | MutatingUseContext::Retag, + ) + | PlaceContext::NonMutatingUse( + NonMutatingUseContext::AddressOf + | NonMutatingUseContext::Copy + | NonMutatingUseContext::Inspect + | NonMutatingUseContext::Move + | NonMutatingUseContext::Projection + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::UniqueBorrow, + ) => Some(DefUse::Use), + } + } +} diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 87d8e9e411c6f..e199a174efbc3 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -2,19 +2,18 @@ //! bitvectors attached to each basic block, represented via a //! zero-sized structure. -use rustc::mir::{self, Body, Location}; -use rustc::ty::layout::VariantIdx; -use rustc::ty::{self, TyCtxt}; use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; +use rustc_middle::mir::{self, Body, Location}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_target::abi::VariantIdx; use super::MoveDataParamEnv; use crate::util::elaborate_drops::DropFlagState; -use super::generic::{AnalysisDomain, GenKill, GenKillAnalysis}; use super::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; -use super::BottomValue; +use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis}; use super::drop_flag_effects_for_function_entry; use super::drop_flag_effects_for_location; @@ -22,12 +21,14 @@ use super::on_lookup_result_bits; use crate::dataflow::drop_flag_effects; mod borrowed_locals; +pub(super) mod borrows; +mod liveness; mod storage_liveness; -pub use self::borrowed_locals::*; -pub use self::storage_liveness::*; - -pub(super) mod borrows; +pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals}; +pub use self::borrows::Borrows; +pub use self::liveness::MaybeLiveLocals; +pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; /// `MaybeInitializedPlaces` tracks all places that might be /// initialized upon reaching a particular point in the control flow @@ -324,7 +325,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { _block: mir::BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - dest_place: &mir::Place<'tcx>, + dest_place: mir::Place<'tcx>, ) { // when a call returns successfully, that means we need to set // the bits for that dest_place to 1 (initialized). @@ -343,7 +344,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { &self, trans: &mut impl GenKill, _block: mir::BasicBlock, - enum_place: &mir::Place<'tcx>, + enum_place: mir::Place<'tcx>, _adt: &ty::AdtDef, variant: VariantIdx, ) { @@ -426,7 +427,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { _block: mir::BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - dest_place: &mir::Place<'tcx>, + dest_place: mir::Place<'tcx>, ) { // when a call returns successfully, that means we need to set // the bits for that dest_place to 0 (initialized). @@ -495,7 +496,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { _block: mir::BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - dest_place: &mir::Place<'tcx>, + dest_place: mir::Place<'tcx>, ) { // when a call returns successfully, that means we need to set // the bits for that dest_place to 1 (initialized). @@ -545,18 +546,15 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { ); trans.gen_all(init_loc_map[location].iter().copied()); - match stmt.kind { - mir::StatementKind::StorageDead(local) => { - // End inits for StorageDead, so that an immutable variable can - // be reinitialized on the next iteration of the loop. - let move_path_index = rev_lookup.find_local(local); - debug!( - "stmt {:?} at loc {:?} clears the ever initialized status of {:?}", - stmt, location, &init_path_map[move_path_index] - ); - trans.kill_all(init_path_map[move_path_index].iter().copied()); - } - _ => {} + if let mir::StatementKind::StorageDead(local) = stmt.kind { + // End inits for StorageDead, so that an immutable variable can + // be reinitialized on the next iteration of the loop. + let move_path_index = rev_lookup.find_local(local); + debug!( + "stmt {:?} at loc {:?} clears the ever initialized status of {:?}", + stmt, location, &init_path_map[move_path_index] + ); + trans.kill_all(init_path_map[move_path_index].iter().copied()); } } @@ -589,7 +587,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { block: mir::BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - _dest_place: &mir::Place<'tcx>, + _dest_place: mir::Place<'tcx>, ) { let move_data = self.move_data(); let init_loc_map = &move_data.init_loc_map; diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs index 5341d661b1db6..bbc4942030ef7 100644 --- a/src/librustc_mir/dataflow/impls/storage_liveness.rs +++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs @@ -1,13 +1,22 @@ pub use super::*; -use crate::dataflow::generic::{self as dataflow, GenKill, Results, ResultsRefCursor}; use crate::dataflow::BottomValue; -use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; -use rustc::mir::*; +use crate::dataflow::{self, GenKill, Results, ResultsRefCursor}; +use crate::util::storage::AlwaysLiveLocals; +use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; use std::cell::RefCell; -#[derive(Copy, Clone)] -pub struct MaybeStorageLive; +#[derive(Clone)] +pub struct MaybeStorageLive { + always_live_locals: AlwaysLiveLocals, +} + +impl MaybeStorageLive { + pub fn new(always_live_locals: AlwaysLiveLocals) -> Self { + MaybeStorageLive { always_live_locals } + } +} impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { type Idx = Local; @@ -19,9 +28,12 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { } fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet) { - // The resume argument is live on function entry (we don't care about - // the `self` argument) - for arg in body.args_iter().skip(1) { + assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); + for local in self.always_live_locals.iter() { + on_entry.insert(local); + } + + for arg in body.args_iter() { on_entry.insert(arg); } } @@ -56,7 +68,7 @@ impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive { _block: BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - _return_place: &mir::Place<'tcx>, + _return_place: mir::Place<'tcx>, ) { // Nothing to do when a call returns successfully } @@ -72,18 +84,18 @@ type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorro /// Dataflow analysis that determines whether each local requires storage at a /// given location; i.e. whether its storage can go away without being observed. pub struct MaybeRequiresStorage<'mir, 'tcx> { - body: ReadOnlyBodyAndCache<'mir, 'tcx>, + body: &'mir Body<'tcx>, borrowed_locals: RefCell>, } impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { pub fn new( - body: ReadOnlyBodyAndCache<'mir, 'tcx>, + body: &'mir Body<'tcx>, borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>, ) -> Self { MaybeRequiresStorage { body, - borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)), + borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)), } } } @@ -124,7 +136,7 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, | StatementKind::SetDiscriminant { box place, .. } => { trans.gen(place.local); } - StatementKind::InlineAsm(asm) => { + StatementKind::LlvmInlineAsm(asm) => { for place in &*asm.outputs { trans.gen(place.local); } @@ -171,6 +183,23 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, // place to have storage *before* the yield, only after. TerminatorKind::Yield { .. } => {} + TerminatorKind::InlineAsm { operands, .. } => { + for op in operands { + match op { + InlineAsmOperand::Out { place, .. } + | InlineAsmOperand::InOut { out_place: place, .. } => { + if let Some(place) = place { + trans.gen(place.local); + } + } + InlineAsmOperand::In { .. } + | InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::SymStatic { .. } => {} + } + } + } + // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. TerminatorKind::Call { destination: None, .. } @@ -216,6 +245,7 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } + | TerminatorKind::InlineAsm { .. } | TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } @@ -231,16 +261,16 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, _block: BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - return_place: &mir::Place<'tcx>, + return_place: mir::Place<'tcx>, ) { trans.gen(return_place.local); } fn yield_resume_effect( &self, - trans: &mut BitSet, + trans: &mut impl GenKill, _resume_block: BasicBlock, - resume_place: &mir::Place<'tcx>, + resume_place: mir::Place<'tcx>, ) { trans.gen(resume_place.local); } @@ -250,7 +280,7 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. fn check_for_move(&self, trans: &mut impl GenKill, loc: Location) { let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals }; - visitor.visit_location(self.body, loc); + visitor.visit_location(&self.body, loc); } } @@ -271,7 +301,7 @@ where fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) { if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context { let mut borrowed_locals = self.borrowed_locals.borrow_mut(); - borrowed_locals.seek_before(loc); + borrowed_locals.seek_before_primary_effect(loc); if !borrowed_locals.contains(*local) { self.trans.kill(*local); } diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index c98a5e84729ab..ae1328dbd12c7 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -1,36 +1,19 @@ -use rustc::mir::traversal; -use rustc::mir::{self, BasicBlock, BasicBlockData, Body, Location, Statement, Terminator}; -use rustc::ty::{self, TyCtxt}; use rustc_ast::ast::{self, MetaItem}; -use rustc_ast_pretty::pprust; -use rustc_data_structures::work_queue::WorkQueue; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::Idx; -use rustc_session::Session; +use rustc_middle::ty; use rustc_span::symbol::{sym, Symbol}; -use std::borrow::Borrow; -use std::fmt; -use std::io; -use std::path::PathBuf; - -pub use self::at_location::{FlowAtLocation, FlowsAtLocation}; pub(crate) use self::drop_flag_effects::*; -pub use self::impls::borrows::Borrows; -pub use self::impls::DefinitelyInitializedPlaces; -pub use self::impls::EverInitializedPlaces; -pub use self::impls::{MaybeBorrowedLocals, MaybeMutBorrowedLocals}; -pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; -pub use self::impls::{MaybeRequiresStorage, MaybeStorageLive}; +pub use self::framework::{ + visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, BorrowckResults, + BottomValue, Engine, Forward, GenKill, GenKillAnalysis, Results, ResultsCursor, + ResultsRefCursor, ResultsVisitor, +}; use self::move_paths::MoveData; -mod at_location; pub mod drop_flag_effects; -pub mod generic; -mod graphviz; -mod impls; +mod framework; +pub mod impls; pub mod move_paths; pub(crate) mod indexes { @@ -40,74 +23,9 @@ pub(crate) mod indexes { }; } -pub(crate) struct DataflowBuilder<'a, 'tcx, BD> -where - BD: BitDenotation<'tcx>, -{ - def_id: DefId, - flow_state: DataflowAnalysis<'a, 'tcx, BD>, - print_preflow_to: Option, - print_postflow_to: Option, -} - -/// `DebugFormatted` encapsulates the "{:?}" rendering of some -/// arbitrary value. This way: you pay cost of allocating an extra -/// string (as well as that of rendering up-front); in exchange, you -/// don't have to hand over ownership of your value or deal with -/// borrowing it. -pub struct DebugFormatted(String); - -impl DebugFormatted { - pub fn new(input: &dyn fmt::Debug) -> DebugFormatted { - DebugFormatted(format!("{:?}", input)) - } -} - -impl fmt::Debug for DebugFormatted { - fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(w, "{}", self.0) - } -} - -pub trait Dataflow<'tcx, BD: BitDenotation<'tcx>> { - /// Sets up and runs the dataflow problem, using `p` to render results if - /// implementation so chooses. - fn dataflow

(&mut self, p: P) - where - P: Fn(&BD, BD::Idx) -> DebugFormatted, - { - let _ = p; // default implementation does not instrument process. - self.build_sets(); - self.propagate(); - } - - /// Sets up the entry, gen, and kill sets for this instance of a dataflow problem. - fn build_sets(&mut self); - - /// Finds a fixed-point solution to this instance of a dataflow problem. - fn propagate(&mut self); -} - -impl<'a, 'tcx, BD> Dataflow<'tcx, BD> for DataflowBuilder<'a, 'tcx, BD> -where - BD: BitDenotation<'tcx>, -{ - fn dataflow

(&mut self, p: P) - where - P: Fn(&BD, BD::Idx) -> DebugFormatted, - { - self.flow_state.build_sets(); - self.pre_dataflow_instrumentation(|c, i| p(c, i)).unwrap(); - self.flow_state.propagate(); - self.post_dataflow_instrumentation(|c, i| p(c, i)).unwrap(); - } - - fn build_sets(&mut self) { - self.flow_state.build_sets(); - } - fn propagate(&mut self) { - self.flow_state.propagate(); - } +pub struct MoveDataParamEnv<'tcx> { + pub(crate) move_data: MoveData<'tcx>, + pub(crate) param_env: ty::ParamEnv<'tcx>, } pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Option { @@ -124,811 +42,3 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Opti } None } - -pub struct MoveDataParamEnv<'tcx> { - pub(crate) move_data: MoveData<'tcx>, - pub(crate) param_env: ty::ParamEnv<'tcx>, -} - -pub fn do_dataflow<'a, 'tcx, BD, P>( - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - def_id: DefId, - attributes: &[ast::Attribute], - dead_unwinds: &BitSet, - bd: BD, - p: P, -) -> DataflowResults<'tcx, BD> -where - BD: BitDenotation<'tcx>, - P: Fn(&BD, BD::Idx) -> DebugFormatted, -{ - let flow_state = DataflowAnalysis::new(body, dead_unwinds, bd); - flow_state.run(tcx, def_id, attributes, p) -} - -impl<'a, 'tcx, BD> DataflowAnalysis<'a, 'tcx, BD> -where - BD: BitDenotation<'tcx>, -{ - pub(crate) fn run

( - self, - tcx: TyCtxt<'tcx>, - def_id: DefId, - attributes: &[ast::Attribute], - p: P, - ) -> DataflowResults<'tcx, BD> - where - P: Fn(&BD, BD::Idx) -> DebugFormatted, - { - let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option { - if let Some(item) = has_rustc_mir_with(attrs, name) { - if let Some(s) = item.value_str() { - return Some(s.to_string()); - } else { - let path = pprust::path_to_string(&item.path); - sess.span_err(item.span, &format!("{} attribute requires a path", path)); - return None; - } - } - None - }; - - let print_preflow_to = name_found(tcx.sess, attributes, sym::borrowck_graphviz_preflow); - let print_postflow_to = name_found(tcx.sess, attributes, sym::borrowck_graphviz_postflow); - - let mut mbcx = - DataflowBuilder { def_id, print_preflow_to, print_postflow_to, flow_state: self }; - - mbcx.dataflow(p); - mbcx.flow_state.results() - } -} - -struct PropagationContext<'b, 'a, 'tcx, O> -where - O: BitDenotation<'tcx>, -{ - builder: &'b mut DataflowAnalysis<'a, 'tcx, O>, -} - -impl<'a, 'tcx, BD> DataflowAnalysis<'a, 'tcx, BD> -where - BD: BitDenotation<'tcx>, -{ - fn propagate(&mut self) { - let mut temp = BitSet::new_empty(self.flow_state.sets.bits_per_block); - let mut propcx = PropagationContext { builder: self }; - propcx.walk_cfg(&mut temp); - } - - fn build_sets(&mut self) { - // Build the transfer function for each block. - for (bb, data) in self.body.basic_blocks().iter_enumerated() { - let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data; - - let trans = self.flow_state.sets.trans_mut_for(bb.index()); - for j_stmt in 0..statements.len() { - let location = Location { block: bb, statement_index: j_stmt }; - self.flow_state.operator.before_statement_effect(trans, location); - self.flow_state.operator.statement_effect(trans, location); - } - - if terminator.is_some() { - let location = Location { block: bb, statement_index: statements.len() }; - self.flow_state.operator.before_terminator_effect(trans, location); - self.flow_state.operator.terminator_effect(trans, location); - } - } - - // Initialize the flow state at entry to the start block. - let on_entry = self.flow_state.sets.entry_set_mut_for(mir::START_BLOCK.index()); - self.flow_state.operator.start_block_effect(on_entry); - } -} - -impl<'b, 'a, 'tcx, BD> PropagationContext<'b, 'a, 'tcx, BD> -where - BD: BitDenotation<'tcx>, -{ - fn walk_cfg(&mut self, in_out: &mut BitSet) { - let body = self.builder.body; - - // Initialize the dirty queue in reverse post-order. This makes it more likely that the - // entry state for each basic block will have the effects of its predecessors applied - // before it is processed. In fact, for CFGs without back edges, this guarantees that - // dataflow will converge in exactly `N` iterations, where `N` is the number of basic - // blocks. - let mut dirty_queue: WorkQueue = - WorkQueue::with_none(body.basic_blocks().len()); - for (bb, _) in traversal::reverse_postorder(body) { - dirty_queue.insert(bb); - } - - // Add blocks which are not reachable from START_BLOCK to the work queue. These blocks will - // be processed after the ones added above. - for bb in body.basic_blocks().indices() { - dirty_queue.insert(bb); - } - - while let Some(bb) = dirty_queue.pop() { - let (on_entry, trans) = self.builder.flow_state.sets.get_mut(bb.index()); - debug_assert!(in_out.words().len() == on_entry.words().len()); - in_out.overwrite(on_entry); - trans.apply(in_out); - - let bb_data = &body[bb]; - self.builder.propagate_bits_into_graph_successors_of( - in_out, - (bb, bb_data), - &mut dirty_queue, - ); - } - } -} - -fn dataflow_path(context: &str, path: &str) -> PathBuf { - let mut path = PathBuf::from(path); - let new_file_name = { - let orig_file_name = path.file_name().unwrap().to_str().unwrap(); - format!("{}_{}", context, orig_file_name) - }; - path.set_file_name(new_file_name); - path -} - -impl<'a, 'tcx, BD> DataflowBuilder<'a, 'tcx, BD> -where - BD: BitDenotation<'tcx>, -{ - fn pre_dataflow_instrumentation

(&self, p: P) -> io::Result<()> - where - P: Fn(&BD, BD::Idx) -> DebugFormatted, - { - if let Some(ref path_str) = self.print_preflow_to { - let path = dataflow_path(BD::name(), path_str); - graphviz::print_borrowck_graph_to(self, &path, p) - } else { - Ok(()) - } - } - - fn post_dataflow_instrumentation

(&self, p: P) -> io::Result<()> - where - P: Fn(&BD, BD::Idx) -> DebugFormatted, - { - if let Some(ref path_str) = self.print_postflow_to { - let path = dataflow_path(BD::name(), path_str); - graphviz::print_borrowck_graph_to(self, &path, p) - } else { - Ok(()) - } - } -} - -/// DataflowResultsConsumer abstracts over walking the MIR with some -/// already constructed dataflow results. -/// -/// It abstracts over the FlowState and also completely hides the -/// underlying flow analysis results, because it needs to handle cases -/// where we are combining the results of *multiple* flow analyses -/// (e.g., borrows + inits + uninits). -pub(crate) trait DataflowResultsConsumer<'a, 'tcx: 'a> { - type FlowState: FlowsAtLocation; - - // Observation Hooks: override (at least one of) these to get analysis feedback. - fn visit_block_entry(&mut self, _bb: BasicBlock, _flow_state: &Self::FlowState) {} - - fn visit_statement_entry( - &mut self, - _loc: Location, - _stmt: &'a Statement<'tcx>, - _flow_state: &Self::FlowState, - ) { - } - - fn visit_terminator_entry( - &mut self, - _loc: Location, - _term: &'a Terminator<'tcx>, - _flow_state: &Self::FlowState, - ) { - } - - // Main entry point: this drives the processing of results. - - fn analyze_results(&mut self, flow_uninit: &mut Self::FlowState) { - let flow = flow_uninit; - for (bb, _) in traversal::reverse_postorder(self.body()) { - flow.reset_to_entry_of(bb); - self.process_basic_block(bb, flow); - } - } - - fn process_basic_block(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) { - self.visit_block_entry(bb, flow_state); - - let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = self.body()[bb]; - let mut location = Location { block: bb, statement_index: 0 }; - for stmt in statements.iter() { - flow_state.reconstruct_statement_effect(location); - self.visit_statement_entry(location, stmt, flow_state); - flow_state.apply_local_effect(location); - location.statement_index += 1; - } - - if let Some(ref term) = *terminator { - flow_state.reconstruct_terminator_effect(location); - self.visit_terminator_entry(location, term, flow_state); - - // We don't need to apply the effect of the terminator, - // since we are only visiting dataflow state on control - // flow entry to the various nodes. (But we still need to - // reconstruct the effect, because the visit method might - // inspect it.) - } - } - - // Delegated Hooks: Provide access to the MIR and process the flow state. - - fn body(&self) -> &'a Body<'tcx>; -} - -/// Allows iterating dataflow results in a flexible and reasonably fast way. -pub struct DataflowResultsCursor<'mir, 'tcx, BD, DR = DataflowResults<'tcx, BD>> -where - BD: BitDenotation<'tcx>, - DR: Borrow>, -{ - flow_state: FlowAtLocation<'tcx, BD, DR>, - - // The statement (or terminator) whose effect has been reconstructed in - // flow_state. - curr_loc: Option, - - body: &'mir Body<'tcx>, -} - -pub type DataflowResultsRefCursor<'mir, 'tcx, BD> = - DataflowResultsCursor<'mir, 'tcx, BD, &'mir DataflowResults<'tcx, BD>>; - -impl<'mir, 'tcx, BD, DR> DataflowResultsCursor<'mir, 'tcx, BD, DR> -where - BD: BitDenotation<'tcx>, - DR: Borrow>, -{ - pub fn new(result: DR, body: &'mir Body<'tcx>) -> Self { - DataflowResultsCursor { flow_state: FlowAtLocation::new(result), curr_loc: None, body } - } - - /// Seek to the given location in MIR. This method is fast if you are - /// traversing your MIR statements in order. - /// - /// After calling `seek`, the current state will reflect all effects up to - /// and including the `before_statement_effect` of the statement at location - /// `loc`. The `statement_effect` of the statement at `loc` will be - /// available as the current effect (see e.g. `each_gen_bit`). - /// - /// If `loc.statement_index` equals the number of statements in the block, - /// we will reconstruct the terminator effect in the same way as described - /// above. - pub fn seek(&mut self, loc: Location) { - if self.curr_loc.map(|cur| loc == cur).unwrap_or(false) { - return; - } - - let start_index; - let should_reset = match self.curr_loc { - None => true, - Some(cur) if loc.block != cur.block || loc.statement_index < cur.statement_index => { - true - } - _ => false, - }; - if should_reset { - self.flow_state.reset_to_entry_of(loc.block); - start_index = 0; - } else { - let curr_loc = self.curr_loc.unwrap(); - start_index = curr_loc.statement_index; - // Apply the effect from the last seek to the current state. - self.flow_state.apply_local_effect(curr_loc); - } - - for stmt in start_index..loc.statement_index { - let mut stmt_loc = loc; - stmt_loc.statement_index = stmt; - self.flow_state.reconstruct_statement_effect(stmt_loc); - self.flow_state.apply_local_effect(stmt_loc); - } - - if loc.statement_index == self.body[loc.block].statements.len() { - self.flow_state.reconstruct_terminator_effect(loc); - } else { - self.flow_state.reconstruct_statement_effect(loc); - } - self.curr_loc = Some(loc); - } - - /// Return whether the current state contains bit `x`. - pub fn contains(&self, x: BD::Idx) -> bool { - self.flow_state.contains(x) - } - - /// Iterate over each `gen` bit in the current effect (invoke `seek` first). - pub fn each_gen_bit(&self, f: F) - where - F: FnMut(BD::Idx), - { - self.flow_state.each_gen_bit(f) - } - - pub fn get(&self) -> &BitSet { - self.flow_state.as_dense() - } -} - -pub struct DataflowAnalysis<'a, 'tcx, O> -where - O: BitDenotation<'tcx>, -{ - flow_state: DataflowState<'tcx, O>, - dead_unwinds: &'a BitSet, - body: &'a Body<'tcx>, -} - -impl<'a, 'tcx, O> DataflowAnalysis<'a, 'tcx, O> -where - O: BitDenotation<'tcx>, -{ - pub fn results(self) -> DataflowResults<'tcx, O> { - DataflowResults(self.flow_state) - } - - pub fn body(&self) -> &'a Body<'tcx> { - self.body - } -} - -pub struct DataflowResults<'tcx, O>(pub(crate) DataflowState<'tcx, O>) -where - O: BitDenotation<'tcx>; - -impl<'tcx, O: BitDenotation<'tcx>> DataflowResults<'tcx, O> { - pub fn sets(&self) -> &AllSets { - &self.0.sets - } - - pub fn operator(&self) -> &O { - &self.0.operator - } -} - -/// State of a dataflow analysis; couples a collection of bit sets -/// with operator used to initialize and merge bits during analysis. -pub struct DataflowState<'tcx, O: BitDenotation<'tcx>> { - /// All the sets for the analysis. (Factored into its - /// own structure so that we can borrow it mutably - /// on its own separate from other fields.) - pub sets: AllSets, - - /// operator used to initialize, combine, and interpret bits. - pub(crate) operator: O, -} - -impl<'tcx, O: BitDenotation<'tcx>> DataflowState<'tcx, O> { - pub(crate) fn interpret_set<'c, P>( - &self, - o: &'c O, - set: &BitSet, - render_idx: &P, - ) -> Vec - where - P: Fn(&O, O::Idx) -> DebugFormatted, - { - set.iter().map(|i| render_idx(o, i)).collect() - } - - pub(crate) fn interpret_hybrid_set<'c, P>( - &self, - o: &'c O, - set: &HybridBitSet, - render_idx: &P, - ) -> Vec - where - P: Fn(&O, O::Idx) -> DebugFormatted, - { - set.iter().map(|i| render_idx(o, i)).collect() - } -} - -/// A 2-tuple representing the "gen" and "kill" bitsets during -/// dataflow analysis. -/// -/// It is best to ensure that the intersection of `gen_set` and -/// `kill_set` is empty; otherwise the results of the dataflow will -/// have a hidden dependency on what order the bits are generated and -/// killed during the iteration. (This is such a good idea that the -/// `fn gen` and `fn kill` methods that set their state enforce this -/// for you.) -#[derive(Debug, Clone, Copy)] -pub struct GenKill { - pub(crate) gen_set: T, - pub(crate) kill_set: T, -} - -pub type GenKillSet = GenKill>; - -impl GenKill { - /// Creates a new tuple where `gen_set == kill_set == elem`. - pub(crate) fn from_elem(elem: T) -> Self - where - T: Clone, - { - GenKill { gen_set: elem.clone(), kill_set: elem } - } -} - -impl GenKillSet { - pub fn clear(&mut self) { - self.gen_set.clear(); - self.kill_set.clear(); - } - - pub fn gen(&mut self, e: E) { - self.gen_set.insert(e); - self.kill_set.remove(e); - } - - pub fn gen_all(&mut self, i: impl IntoIterator>) { - for j in i { - self.gen(*j.borrow()); - } - } - - pub fn kill(&mut self, e: E) { - self.gen_set.remove(e); - self.kill_set.insert(e); - } - - pub fn kill_all(&mut self, i: impl IntoIterator>) { - for j in i { - self.kill(*j.borrow()); - } - } - - /// Computes `(set ∪ gen) - kill` and assigns the result to `set`. - pub(crate) fn apply(&self, set: &mut BitSet) { - set.union(&self.gen_set); - set.subtract(&self.kill_set); - } -} - -#[derive(Debug)] -pub struct AllSets { - /// Analysis bitwidth for each block. - bits_per_block: usize, - - /// For each block, bits valid on entry to the block. - on_entry: Vec>, - - /// The transfer function of each block expressed as the set of bits - /// generated and killed by executing the statements + terminator in the - /// block -- with one caveat. In particular, for *call terminators*, the - /// effect of storing the destination is not included, since that only takes - /// effect on the **success** edge (and not the unwind edge). - trans: Vec>, -} - -impl AllSets { - pub fn bits_per_block(&self) -> usize { - self.bits_per_block - } - - pub fn get_mut(&mut self, block_idx: usize) -> (&mut BitSet, &mut GenKillSet) { - (&mut self.on_entry[block_idx], &mut self.trans[block_idx]) - } - - pub fn trans_for(&self, block_idx: usize) -> &GenKillSet { - &self.trans[block_idx] - } - pub fn trans_mut_for(&mut self, block_idx: usize) -> &mut GenKillSet { - &mut self.trans[block_idx] - } - pub fn entry_set_for(&self, block_idx: usize) -> &BitSet { - &self.on_entry[block_idx] - } - pub fn entry_set_mut_for(&mut self, block_idx: usize) -> &mut BitSet { - &mut self.on_entry[block_idx] - } - pub fn gen_set_for(&self, block_idx: usize) -> &HybridBitSet { - &self.trans_for(block_idx).gen_set - } - pub fn kill_set_for(&self, block_idx: usize) -> &HybridBitSet { - &self.trans_for(block_idx).kill_set - } -} - -/// Parameterization for the precise form of data flow that is used. -/// -/// `BottomValue` determines whether the initial entry set for each basic block is empty or full. -/// This also determines the semantics of the lattice `join` operator used to merge dataflow -/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed -/// point. -/// -/// This means, for propagation across the graph, that you either want to start at all-zeroes and -/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect -/// as your merge when propagating. -pub trait BottomValue { - /// Specifies the initial value for each bit in the entry set for each basic block. - const BOTTOM_VALUE: bool; - - /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed. - /// - /// It is almost certainly wrong to override this, since it automatically applies - /// * `inout_set & in_set` if `BOTTOM_VALUE == true` - /// * `inout_set | in_set` if `BOTTOM_VALUE == false` - /// - /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks. - /// For clarity, the above statement again from a different perspective: - /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is - /// `!BOTTOM_VALUE`. - /// - /// There are situations where you want the opposite behaviour: propagate only if *all* - /// predecessor blocks's value is `!BOTTOM_VALUE`. - /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This - /// means that all code paths leading to the location must have set the bit, instead of any - /// code path leading there. - /// - /// If you want this kind of "definitely set" analysis, you need to - /// 1. Invert `BOTTOM_VALUE` - /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE` - /// 3. Override `join` to do the opposite from what it's doing now. - #[inline] - fn join(&self, inout_set: &mut BitSet, in_set: &BitSet) -> bool { - if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) } - } -} - -/// A specific flavor of dataflow analysis. -/// -/// To run a dataflow analysis, one sets up an initial state for the -/// `START_BLOCK` via `start_block_effect` and a transfer function (`trans`) -/// for each block individually. The entry set for all other basic blocks is -/// initialized to `Self::BOTTOM_VALUE`. The dataflow analysis then -/// iteratively modifies the various entry sets (but leaves the the transfer -/// function unchanged). `BottomValue::join` is used to merge the bitsets from -/// two blocks (e.g. when two blocks' terminator jumps to a single block, that -/// target block's state is the merged state of both incoming blocks). -pub trait BitDenotation<'tcx>: BottomValue { - /// Specifies what index type is used to access the bitvector. - type Idx: Idx; - - /// A name describing the dataflow analysis that this - /// `BitDenotation` is supporting. The name should be something - /// suitable for plugging in as part of a filename (i.e., avoid - /// space-characters or other things that tend to look bad on a - /// file system, like slashes or periods). It is also better for - /// the name to be reasonably short, again because it will be - /// plugged into a filename. - fn name() -> &'static str; - - /// Size of each bitvector allocated for each block in the analysis. - fn bits_per_block(&self) -> usize; - - /// Mutates the entry set according to the effects that - /// have been established *prior* to entering the start - /// block. This can't access the gen/kill sets, because - /// these won't be accounted for correctly. - /// - /// (For example, establishing the call arguments.) - fn start_block_effect(&self, entry_set: &mut BitSet); - - /// Similar to `statement_effect`, except it applies - /// *just before* the statement rather than *just after* it. - /// - /// This matters for "dataflow at location" APIs, because the - /// before-statement effect is visible while visiting the - /// statement, while the after-statement effect only becomes - /// visible at the next statement. - /// - /// Both the before-statement and after-statement effects are - /// applied, in that order, before moving for the next - /// statement. - fn before_statement_effect(&self, _trans: &mut GenKillSet, _location: Location) {} - - /// Mutates the block-sets (the flow sets for the given - /// basic block) according to the effects of evaluating statement. - /// - /// This is used, in particular, for building up the - /// "transfer-function" representing the overall-effect of the - /// block, represented via GEN and KILL sets. - /// - /// The statement is identified as `bb_data[idx_stmt]`, where - /// `bb_data` is the sequence of statements identified by `bb` in - /// the MIR. - fn statement_effect(&self, trans: &mut GenKillSet, location: Location); - - /// Similar to `terminator_effect`, except it applies - /// *just before* the terminator rather than *just after* it. - /// - /// This matters for "dataflow at location" APIs, because the - /// before-terminator effect is visible while visiting the - /// terminator, while the after-terminator effect only becomes - /// visible at the terminator's successors. - /// - /// Both the before-terminator and after-terminator effects are - /// applied, in that order, before moving for the next - /// terminator. - fn before_terminator_effect(&self, _trans: &mut GenKillSet, _location: Location) {} - - /// Mutates the block-sets (the flow sets for the given - /// basic block) according to the effects of evaluating - /// the terminator. - /// - /// This is used, in particular, for building up the - /// "transfer-function" representing the overall-effect of the - /// block, represented via GEN and KILL sets. - /// - /// The effects applied here cannot depend on which branch the - /// terminator took. - fn terminator_effect(&self, trans: &mut GenKillSet, location: Location); - - /// Mutates the block-sets according to the (flow-dependent) - /// effect of a successful return from a Call terminator. - /// - /// If basic-block BB_x ends with a call-instruction that, upon - /// successful return, flows to BB_y, then this method will be - /// called on the exit flow-state of BB_x in order to set up the - /// entry flow-state of BB_y. - /// - /// This is used, in particular, as a special case during the - /// "propagate" loop where all of the basic blocks are repeatedly - /// visited. Since the effects of a Call terminator are - /// flow-dependent, the current MIR cannot encode them via just - /// GEN and KILL sets attached to the block, and so instead we add - /// this extra machinery to represent the flow-dependent effect. - // - // FIXME: right now this is a bit of a wart in the API. It might - // be better to represent this as an additional gen- and - // kill-sets associated with each edge coming out of the basic - // block. - fn propagate_call_return( - &self, - in_out: &mut BitSet, - call_bb: mir::BasicBlock, - dest_bb: mir::BasicBlock, - dest_place: &mir::Place<'tcx>, - ); -} - -impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D> -where - D: BitDenotation<'tcx>, -{ - pub fn new( - body: &'a Body<'tcx>, - dead_unwinds: &'a BitSet, - denotation: D, - ) -> Self { - let bits_per_block = denotation.bits_per_block(); - let num_blocks = body.basic_blocks().len(); - - let on_entry = if D::BOTTOM_VALUE { - vec![BitSet::new_filled(bits_per_block); num_blocks] - } else { - vec![BitSet::new_empty(bits_per_block); num_blocks] - }; - let nop = GenKill::from_elem(HybridBitSet::new_empty(bits_per_block)); - - DataflowAnalysis { - body, - dead_unwinds, - flow_state: DataflowState { - sets: AllSets { bits_per_block, on_entry, trans: vec![nop; num_blocks] }, - operator: denotation, - }, - } - } -} - -impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D> -where - D: BitDenotation<'tcx>, -{ - /// Propagates the bits of `in_out` into all the successors of `bb`, - /// using bitwise operator denoted by `self.operator`. - /// - /// For most blocks, this is entirely uniform. However, for blocks - /// that end with a call terminator, the effect of the call on the - /// dataflow state may depend on whether the call returned - /// successfully or unwound. - /// - /// To reflect this, the `propagate_call_return` method of the - /// `BitDenotation` mutates `in_out` when propagating `in_out` via - /// a call terminator; such mutation is performed *last*, to - /// ensure its side-effects do not leak elsewhere (e.g., into - /// unwind target). - fn propagate_bits_into_graph_successors_of( - &mut self, - in_out: &mut BitSet, - (bb, bb_data): (mir::BasicBlock, &mir::BasicBlockData<'tcx>), - dirty_list: &mut WorkQueue, - ) { - match bb_data.terminator().kind { - mir::TerminatorKind::Return - | mir::TerminatorKind::Resume - | mir::TerminatorKind::Abort - | mir::TerminatorKind::GeneratorDrop - | mir::TerminatorKind::Unreachable => {} - mir::TerminatorKind::Goto { target } - | mir::TerminatorKind::Assert { target, cleanup: None, .. } - | mir::TerminatorKind::Yield { resume: target, drop: None, .. } - | mir::TerminatorKind::Drop { target, location: _, unwind: None } - | mir::TerminatorKind::DropAndReplace { target, value: _, location: _, unwind: None } => - { - self.propagate_bits_into_entry_set_for(in_out, target, dirty_list); - } - mir::TerminatorKind::Yield { resume: target, drop: Some(drop), .. } => { - self.propagate_bits_into_entry_set_for(in_out, target, dirty_list); - self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list); - } - mir::TerminatorKind::Assert { target, cleanup: Some(unwind), .. } - | mir::TerminatorKind::Drop { target, location: _, unwind: Some(unwind) } - | mir::TerminatorKind::DropAndReplace { - target, - value: _, - location: _, - unwind: Some(unwind), - } => { - self.propagate_bits_into_entry_set_for(in_out, target, dirty_list); - if !self.dead_unwinds.contains(bb) { - self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list); - } - } - mir::TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets { - self.propagate_bits_into_entry_set_for(in_out, *target, dirty_list); - } - } - mir::TerminatorKind::Call { cleanup, ref destination, .. } => { - if let Some(unwind) = cleanup { - if !self.dead_unwinds.contains(bb) { - self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list); - } - } - if let Some((ref dest_place, dest_bb)) = *destination { - // N.B.: This must be done *last*, after all other - // propagation, as documented in comment above. - self.flow_state.operator.propagate_call_return(in_out, bb, dest_bb, dest_place); - self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list); - } - } - mir::TerminatorKind::FalseEdges { real_target, imaginary_target } => { - self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list); - self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list); - } - mir::TerminatorKind::FalseUnwind { real_target, unwind } => { - self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list); - if let Some(unwind) = unwind { - if !self.dead_unwinds.contains(bb) { - self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list); - } - } - } - } - } - - fn propagate_bits_into_entry_set_for( - &mut self, - in_out: &BitSet, - bb: mir::BasicBlock, - dirty_queue: &mut WorkQueue, - ) { - let entry_set = self.flow_state.sets.entry_set_mut_for(bb.index()); - let set_changed = self.flow_state.operator.join(entry_set, &in_out); - if set_changed { - dirty_queue.insert(bb); - } - } -} diff --git a/src/librustc_mir/dataflow/move_paths/abs_domain.rs b/src/librustc_mir/dataflow/move_paths/abs_domain.rs index 0ecf22ae23361..28936274baafa 100644 --- a/src/librustc_mir/dataflow/move_paths/abs_domain.rs +++ b/src/librustc_mir/dataflow/move_paths/abs_domain.rs @@ -11,8 +11,8 @@ //! `a[x]` would still overlap them both. But that is not this //! representation does today.) -use rustc::mir::{Local, Operand, PlaceElem, ProjectionElem}; -use rustc::ty::Ty; +use rustc_middle::mir::{Local, Operand, PlaceElem, ProjectionElem}; +use rustc_middle::ty::Ty; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct AbstractOperand; diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 276aabec13da0..0f2760b3f3b4e 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -1,7 +1,7 @@ -use rustc::mir::tcx::RvalueInitializationState; -use rustc::mir::*; -use rustc::ty::{self, TyCtxt}; use rustc_index::vec::IndexVec; +use rustc_middle::mir::tcx::RvalueInitializationState; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; use smallvec::{smallvec, SmallVec}; use std::convert::TryInto; @@ -94,7 +94,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { /// problematic for borrowck. /// /// Maybe we should have separate "borrowck" and "moveck" modes. - fn move_path_for(&mut self, place: &Place<'tcx>) -> Result> { + fn move_path_for(&mut self, place: Place<'tcx>) -> Result> { debug!("lookup({:?})", place); let mut base = self.builder.data.rev_lookup.locals[place.local]; @@ -144,15 +144,16 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { }, )); } - ty::Array(..) => match elem { - ProjectionElem::Index(..) => { + + ty::Array(..) => { + if let ProjectionElem::Index(..) = elem { return Err(MoveError::cannot_move_out_of( self.loc, InteriorOfSliceOrArray { ty: place_ty, is_index: true }, )); } - _ => {} - }, + } + _ => {} }; @@ -194,7 +195,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { }) } - fn create_move_path(&mut self, place: &Place<'tcx>) { + fn create_move_path(&mut self, place: Place<'tcx>) { // This is an non-moving access (such as an overwrite or // drop), so this not being a valid move path is OK. let _ = self.move_path_for(place); @@ -278,24 +279,24 @@ struct Gatherer<'b, 'a, 'tcx> { impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn gather_statement(&mut self, stmt: &Statement<'tcx>) { - match stmt.kind { - StatementKind::Assign(box (ref place, ref rval)) => { - self.create_move_path(place); + match &stmt.kind { + StatementKind::Assign(box (place, rval)) => { + self.create_move_path(*place); if let RvalueInitializationState::Shallow = rval.initialization_state() { // Box starts out uninitialized - need to create a separate // move-path for the interior so it will be separate from // the exterior. - self.create_move_path(&self.builder.tcx.mk_place_deref(place.clone())); + self.create_move_path(self.builder.tcx.mk_place_deref(*place)); self.gather_init(place.as_ref(), InitKind::Shallow); } else { self.gather_init(place.as_ref(), InitKind::Deep); } self.gather_rvalue(rval); } - StatementKind::FakeRead(_, ref place) => { - self.create_move_path(place); + StatementKind::FakeRead(_, place) => { + self.create_move_path(**place); } - StatementKind::InlineAsm(ref asm) => { + StatementKind::LlvmInlineAsm(ref asm) => { for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) { if !kind.is_indirect { self.gather_init(output.as_ref(), InitKind::Deep); @@ -307,7 +308,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } StatementKind::StorageLive(_) => {} StatementKind::StorageDead(local) => { - self.gather_move(&Place::from(local)); + self.gather_move(Place::from(*local)); } StatementKind::SetDiscriminant { .. } => { span_bug!( @@ -360,17 +361,18 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn gather_terminator(&mut self, term: &Terminator<'tcx>) { match term.kind { TerminatorKind::Goto { target: _ } + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseUnwind { .. } + // In some sense returning moves the return place into the current + // call's destination, however, since there are no statements after + // this that could possibly access the return place, this doesn't + // need recording. + | TerminatorKind::Return | TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdges { .. } - | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Unreachable => {} - TerminatorKind::Return => { - self.gather_move(&Place::return_place()); - } - TerminatorKind::Assert { ref cond, .. } => { self.gather_operand(cond); } @@ -379,16 +381,16 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { self.gather_operand(discr); } - TerminatorKind::Yield { ref value, resume_arg: ref place, .. } => { + TerminatorKind::Yield { ref value, resume_arg: place, .. } => { self.gather_operand(value); self.create_move_path(place); self.gather_init(place.as_ref(), InitKind::Deep); } - TerminatorKind::Drop { ref location, target: _, unwind: _ } => { + TerminatorKind::Drop { location, target: _, unwind: _ } => { self.gather_move(location); } - TerminatorKind::DropAndReplace { ref location, ref value, .. } => { + TerminatorKind::DropAndReplace { location, ref value, .. } => { self.create_move_path(location); self.gather_operand(value); self.gather_init(location.as_ref(), InitKind::Deep); @@ -404,25 +406,50 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { for arg in args { self.gather_operand(arg); } - if let Some((ref destination, _bb)) = *destination { + if let Some((destination, _bb)) = *destination { self.create_move_path(destination); self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); } } + TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + for op in operands { + match *op { + InlineAsmOperand::In { reg: _, ref value } + | InlineAsmOperand::Const { ref value } => { + self.gather_operand(value); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let Some(place) = place { + self.create_move_path(place); + self.gather_init(place.as_ref(), InitKind::Deep); + } + } + InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { + self.gather_operand(in_value); + if let Some(out_place) = out_place { + self.create_move_path(out_place); + self.gather_init(out_place.as_ref(), InitKind::Deep); + } + } + InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { value: _ } => {} + } + } + } } } fn gather_operand(&mut self, operand: &Operand<'tcx>) { match *operand { Operand::Constant(..) | Operand::Copy(..) => {} // not-a-move - Operand::Move(ref place) => { + Operand::Move(place) => { // a move self.gather_move(place); } } } - fn gather_move(&mut self, place: &Place<'tcx>) { + fn gather_move(&mut self, place: Place<'tcx>) { debug!("gather_move({:?}, {:?})", self.loc, place); if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] = @@ -433,7 +460,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // are disjoint, which is expected by drop elaboration. let base_place = Place { local: place.local, projection: self.builder.tcx.intern_place_elems(base) }; - let base_path = match self.move_path_for(&base_place) { + let base_path = match self.move_path_for(base_place) { Ok(path) => path, Err(MoveError::UnionMove { path }) => { self.record_move(place, path); @@ -457,22 +484,21 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { for offset in from..to { let elem = ProjectionElem::ConstantIndex { offset, min_length: len, from_end: false }; - let path = self.add_move_path(base_path, &elem, |tcx| { - tcx.mk_place_elem(base_place.clone(), elem) - }); + let path = + self.add_move_path(base_path, &elem, |tcx| tcx.mk_place_elem(base_place, elem)); self.record_move(place, path); } } else { match self.move_path_for(place) { Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path), Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((*place, error)); + self.builder.errors.push((place, error)); } }; } } - fn record_move(&mut self, place: &Place<'tcx>, path: MovePathIndex) { + fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) { let move_out = self.builder.data.moves.push(MoveOut { path, source: self.loc }); debug!( "gather_move({:?}, {:?}): adding move {:?} of {:?}", diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 593952bfa7c80..d66d2625d78ea 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -1,8 +1,8 @@ use core::slice::Iter; -use rustc::mir::*; -use rustc::ty::{ParamEnv, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::{Enumerated, IndexVec}; +use rustc_middle::mir::*; +use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_span::Span; use smallvec::SmallVec; diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 5c70b28a56786..0e01652bc9002 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -1,33 +1,31 @@ -use rustc::ty::adjustment::PointerCast; -use rustc::ty::layout::{self, Size, TyLayout}; -use rustc::ty::{self, Ty, TypeAndMut, TypeFoldable}; -use rustc_ast::ast::FloatTy; -use rustc_span::symbol::sym; -use rustc_target::abi::LayoutOf; +use std::convert::TryFrom; -use rustc::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; -use rustc::mir::CastKind; +use super::{FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy}; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::{Float, FloatConvert}; +use rustc_ast::ast::FloatTy; +use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; +use rustc_middle::mir::CastKind; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; +use rustc_span::symbol::sym; +use rustc_target::abi::{LayoutOf, Size, Variants}; -use super::{FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy}; - -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn cast( &mut self, src: OpTy<'tcx, M::PointerTag>, kind: CastKind, dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - use rustc::mir::CastKind::*; + use rustc_middle::mir::CastKind::*; match kind { Pointer(PointerCast::Unsize) => { self.unsize_into(src, dest)?; } - Misc - | Pointer(PointerCast::MutToConstPointer) - | Pointer(PointerCast::ArrayToPointer) => { + Misc | Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => { let src = self.read_immediate(src)?; let res = self.cast_immediate(src, dest.layout)?; self.write_immediate(res, dest)?; @@ -100,9 +98,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn cast_immediate( &self, src: ImmTy<'tcx, M::PointerTag>, - dest_layout: TyLayout<'tcx>, + dest_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Immediate> { - use rustc::ty::TyKind::*; + use rustc_middle::ty::TyKind::*; trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, dest_layout.ty); match src.layout.ty.kind { @@ -132,7 +130,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Handle cast from a univariant (ZST) enum. match src.layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { assert!(src.layout.is_zst()); let discr_layout = self.layout_of(discr.ty)?; @@ -141,7 +139,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .into()); } } - layout::Variants::Multiple { .. } => {} + Variants::Multiple { .. } => {} } // Handle casting the metadata away from a fat pointer. @@ -181,14 +179,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn cast_from_int_like( &self, v: u128, // raw bits - src_layout: TyLayout<'tcx>, - dest_layout: TyLayout<'tcx>, + src_layout: TyAndLayout<'tcx>, + dest_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Scalar> { // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.abi.is_signed(); let v = if signed { self.sign_extend(v, src_layout) } else { v }; trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty); - use rustc::ty::TyKind::*; + use rustc_middle::ty::TyKind::*; match dest_layout.ty.kind { Int(_) | Uint(_) | RawPtr(_) => { let v = self.truncate(v, dest_layout); @@ -206,8 +204,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Char => { // `u8` to `char` cast - assert_eq!(v as u8 as u128, v); - Ok(Scalar::from_uint(v, Size::from_bytes(4))) + Ok(Scalar::from_u32(u8::try_from(v).unwrap().into())) } // Casts to bool are not permitted by rustc, no need to handle them here. @@ -223,20 +220,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { where F: Float + Into> + FloatConvert + FloatConvert, { - use rustc::ty::TyKind::*; + use rustc_middle::ty::TyKind::*; match dest_ty.kind { // float -> uint Uint(t) => { - let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize); - let v = f.to_u128(width).value; + let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits()); + // `to_u128` is a saturating cast, which is what we need + // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). + let v = f.to_u128(usize::try_from(width).unwrap()).value; // This should already fit the bit width - Ok(Scalar::from_uint(v, Size::from_bits(width as u64))) + Ok(Scalar::from_uint(v, Size::from_bits(width))) } // float -> int Int(t) => { - let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize); - let v = f.to_i128(width).value; - Ok(Scalar::from_int(v, Size::from_bits(width as u64))) + let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits()); + // `to_i128` is a saturating cast, which is what we need + // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). + let v = f.to_i128(usize::try_from(width).unwrap()).value; + Ok(Scalar::from_int(v, Size::from_bits(width))) } // float -> f32 Float(FloatTy::F32) => Ok(Scalar::from_f32(f.convert(&mut false).value)), @@ -296,8 +297,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx> { trace!("Unsizing {:?} into {:?}", src, dest); match (&src.layout.ty.kind, &dest.layout.ty.kind) { - (&ty::Ref(_, s, _), &ty::Ref(_, d, _)) - | (&ty::Ref(_, s, _), &ty::RawPtr(TypeAndMut { ty: d, .. })) + (&ty::Ref(_, s, _), &ty::Ref(_, d, _) | &ty::RawPtr(TypeAndMut { ty: d, .. })) | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: d, .. })) => { self.unsize_into_ptr(src, dest, s, d) } @@ -319,11 +319,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Example: `Arc` -> `Arc` // here we need to increase the size of every &T thin ptr field to a fat ptr for i in 0..src.layout.fields.count() { - let dst_field = self.place_field(dest, i as u64)?; + let dst_field = self.place_field(dest, i)?; if dst_field.layout.is_zst() { continue; } - let src_field = self.operand_field(src, i as u64)?; + let src_field = self.operand_field(src, i)?; if src_field.layout.ty == dst_field.layout.ty { self.copy_op(src_field, dst_field)?; } else { diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index ac593d0845a7d..eba4dd336ade2 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -2,30 +2,34 @@ use std::cell::Cell; use std::fmt::Write; use std::mem; -use rustc::ich::StableHashingContext; -use rustc::mir; -use rustc::mir::interpret::{ - sign_extend, truncate, AllocId, FrameInfo, GlobalId, InterpResult, Pointer, Scalar, -}; -use rustc::ty::layout::{self, Align, HasDataLayout, LayoutOf, Size, TyLayout}; -use rustc::ty::query::TyCtxtAt; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; use rustc_macros::HashStable; -use rustc_span::source_map::{self, Span, DUMMY_SP}; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{ + sign_extend, truncate, FrameInfo, GlobalId, InterpResult, Pointer, Scalar, +}; +use rustc_middle::ty::layout::{self, TyAndLayout}; +use rustc_middle::ty::{ + self, fold::BottomUpFolder, query::TyCtxtAt, subst::SubstsRef, Ty, TyCtxt, TypeFoldable, +}; +use rustc_span::{source_map::DUMMY_SP, Span}; +use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout}; use super::{ Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, OpTy, Operand, Place, PlaceTy, - ScalarMaybeUndef, StackPopJump, + ScalarMaybeUninit, StackPopJump, }; +use crate::util::storage::AlwaysLiveLocals; pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. + /// + /// Note: the stack is provided by the machine. pub machine: M, /// The results of the type checker, from rustc. @@ -37,9 +41,6 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// The virtual memory system. pub memory: Memory<'mir, 'tcx, M>, - /// The virtual call stack. - pub(crate) stack: Vec>, - /// A cache for deduplicating vtables pub(super) vtables: FxHashMap<(Ty<'tcx>, Option>), Pointer>, @@ -57,9 +58,6 @@ pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> { /// The def_id and substs of the current function. pub instance: ty::Instance<'tcx>, - /// The span of the call site. - pub span: source_map::Span, - /// Extra data for the machine. pub extra: Extra, @@ -83,14 +81,9 @@ pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// - /// The block that is currently executed (or will be executed after the above call stacks - /// return). /// If this is `None`, we are unwinding and this function doesn't need any clean-up. /// Just continue the same as with `Resume`. - pub block: Option, - - /// The index of the currently evaluated statement. - pub stmt: usize, + pub loc: Option, } #[derive(Clone, Eq, PartialEq, Debug, HashStable)] // Miri debug-prints these @@ -110,16 +103,16 @@ pub enum StackPopCleanup { /// State of a local variable including a memoized layout #[derive(Clone, PartialEq, Eq, HashStable)] -pub struct LocalState<'tcx, Tag = (), Id = AllocId> { - pub value: LocalValue, +pub struct LocalState<'tcx, Tag = ()> { + pub value: LocalValue, /// Don't modify if `Some`, this is only used to prevent computing the layout twice #[stable_hasher(ignore)] - pub layout: Cell>>, + pub layout: Cell>>, } /// Current value of a local variable #[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these -pub enum LocalValue { +pub enum LocalValue { /// This local is not currently alive, and cannot be used at all. Dead, /// This local is alive but not yet initialized. It can be written to @@ -132,7 +125,7 @@ pub enum LocalValue { /// This is an optimization over just always having a pointer here; /// we can thus avoid doing an allocation when the local just stores /// immediate values *and* never has its address taken. - Live(Operand), + Live(Operand), } impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { @@ -154,8 +147,24 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { match self.value { LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)), - ref mut local @ LocalValue::Live(Operand::Immediate(_)) - | ref mut local @ LocalValue::Uninitialized => Ok(Ok(local)), + ref mut + local @ (LocalValue::Live(Operand::Immediate(_)) | LocalValue::Uninitialized) => { + Ok(Ok(local)) + } + } + } +} + +impl<'mir, 'tcx, Tag> Frame<'mir, 'tcx, Tag> { + pub fn with_extra(self, extra: Extra) -> Frame<'mir, 'tcx, Tag, Extra> { + Frame { + body: self.body, + instance: self.instance, + return_to_block: self.return_to_block, + return_place: self.return_place, + locals: self.locals, + loc: self.loc, + extra, } } } @@ -163,10 +172,10 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> { /// Return the `SourceInfo` of the current instruction. pub fn current_source_info(&self) -> Option { - self.block.map(|block| { - let block = &self.body.basic_blocks()[block]; - if self.stmt < block.statements.len() { - block.statements[self.stmt].source_info + self.loc.map(|loc| { + let block = &self.body.basic_blocks()[loc.block]; + if loc.statement_index < block.statements.len() { + block.statements[loc.statement_index].source_info } else { block.terminator().source_info } @@ -176,7 +185,7 @@ impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> { impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { #[inline] - fn data_layout(&self) -> &layout::TargetDataLayout { + fn data_layout(&self) -> &TargetDataLayout { &self.tcx.data_layout } } @@ -202,17 +211,86 @@ where impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for InterpCx<'mir, 'tcx, M> { type Ty = Ty<'tcx>; - type TyLayout = InterpResult<'tcx, TyLayout<'tcx>>; + type TyAndLayout = InterpResult<'tcx, TyAndLayout<'tcx>>; #[inline] - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { self.tcx .layout_of(self.param_env.and(ty)) .map_err(|layout| err_inval!(Layout(layout)).into()) } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +/// Test if it is valid for a MIR assignment to assign `src`-typed place to `dest`-typed value. +/// This test should be symmetric, as it is primarily about layout compatibility. +pub(super) fn mir_assign_valid_types<'tcx>( + tcx: TyCtxt<'tcx>, + src: TyAndLayout<'tcx>, + dest: TyAndLayout<'tcx>, +) -> bool { + if src.ty == dest.ty { + // Equal types, all is good. + return true; + } + if src.layout != dest.layout { + // Layout differs, definitely not equal. + // We do this here because Miri would *do the wrong thing* if we allowed layout-changing + // assignments. + return false; + } + + // Type-changing assignments can happen for (at least) two reasons: + // 1. `&mut T` -> `&T` gets optimized from a reborrow to a mere assignment. + // 2. Subtyping is used. While all normal lifetimes are erased, higher-ranked types + // with their late-bound lifetimes are still around and can lead to type differences. + // Normalize both of them away. + let normalize = |ty: Ty<'tcx>| { + ty.fold_with(&mut BottomUpFolder { + tcx, + // Normalize all references to immutable. + ty_op: |ty| match ty.kind { + ty::Ref(_, pointee, _) => tcx.mk_imm_ref(tcx.lifetimes.re_erased, pointee), + _ => ty, + }, + // We just erase all late-bound lifetimes, but this is not fully correct (FIXME): + // lifetimes in invariant positions could matter (e.g. through associated types). + // We rely on the fact that layout was confirmed to be equal above. + lt_op: |_| tcx.lifetimes.re_erased, + // Leave consts unchanged. + ct_op: |ct| ct, + }) + }; + normalize(src.ty) == normalize(dest.ty) +} + +/// Use the already known layout if given (but sanity check in debug mode), +/// or compute the layout. +#[cfg_attr(not(debug_assertions), inline(always))] +pub(super) fn from_known_layout<'tcx>( + tcx: TyCtxtAt<'tcx>, + known_layout: Option>, + compute: impl FnOnce() -> InterpResult<'tcx, TyAndLayout<'tcx>>, +) -> InterpResult<'tcx, TyAndLayout<'tcx>> { + match known_layout { + None => compute(), + Some(known_layout) => { + if cfg!(debug_assertions) { + let check_layout = compute()?; + if !mir_assign_valid_types(tcx.tcx, check_layout, known_layout) { + span_bug!( + tcx.span, + "expected type differs from actual type.\nexpected: {:?}\nactual: {:?}", + known_layout.ty, + check_layout.ty, + ); + } + } + Ok(known_layout) + } + } +} + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn new( tcx: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -224,11 +302,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { tcx, param_env, memory: Memory::new(tcx, memory_extra), - stack: Vec::new(), vtables: FxHashMap::default(), } } + #[inline(always)] + pub fn set_span(&mut self, span: Span) { + self.tcx.span = span; + self.memory.tcx.span = span; + } + #[inline(always)] pub fn force_ptr( &self, @@ -253,29 +336,37 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This represents a *direct* access to that memory, as opposed to access /// through a pointer that was created by the program. #[inline(always)] - pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer { - self.memory.tag_static_base_pointer(ptr) + pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer { + self.memory.tag_global_base_pointer(ptr) + } + + #[inline(always)] + pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] { + M::stack(self) } #[inline(always)] - pub fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] { - &self.stack + pub(crate) fn stack_mut( + &mut self, + ) -> &mut Vec> { + M::stack_mut(self) } #[inline(always)] - pub fn cur_frame(&self) -> usize { - assert!(!self.stack.is_empty()); - self.stack.len() - 1 + pub fn frame_idx(&self) -> usize { + let stack = self.stack(); + assert!(!stack.is_empty()); + stack.len() - 1 } #[inline(always)] pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { - self.stack.last().expect("no call frames exist") + self.stack().last().expect("no call frames exist") } #[inline(always)] pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { - self.stack.last_mut().expect("no call frames exist") + self.stack_mut().last_mut().expect("no call frames exist") } #[inline(always)] @@ -284,13 +375,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub fn sign_extend(&self, value: u128, ty: TyLayout<'_>) -> u128 { + pub fn sign_extend(&self, value: u128, ty: TyAndLayout<'_>) -> u128 { assert!(ty.abi.is_signed()); sign_extend(value, ty.size) } #[inline(always)] - pub fn truncate(&self, value: u128, ty: TyLayout<'_>) -> u128 { + pub fn truncate(&self, value: u128, ty: TyAndLayout<'_>) -> u128 { truncate(value, ty.size) } @@ -308,23 +399,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, instance: ty::InstanceDef<'tcx>, promoted: Option, - ) -> InterpResult<'tcx, mir::ReadOnlyBodyAndCache<'tcx, 'tcx>> { + ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { // do not continue if typeck errors occurred (can only occur in local crate) let did = instance.def_id(); - if did.is_local() - && self.tcx.has_typeck_tables(did) - && self.tcx.typeck_tables_of(did).tainted_by_errors - { - throw_inval!(TypeckError) + if let Some(did) = did.as_local() { + if self.tcx.has_typeck_tables(did) { + if let Some(error_reported) = self.tcx.typeck_tables_of(did).tainted_by_errors { + throw_inval!(TypeckError(error_reported)) + } + } } trace!("load mir(instance={:?}, promoted={:?})", instance, promoted); if let Some(promoted) = promoted { - return Ok(self.tcx.promoted_mir(did)[promoted].unwrap_read_only()); + return Ok(&self.tcx.promoted_mir(did)[promoted]); } match instance { ty::InstanceDef::Item(def_id) => { if self.tcx.is_mir_available(did) { - Ok(self.tcx.optimized_mir(did).unwrap_read_only()) + Ok(self.tcx.optimized_mir(did)) } else { throw_unsup!(NoMirFor(def_id)) } @@ -365,21 +457,26 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("resolve: {:?}, {:#?}", def_id, substs); trace!("param_env: {:#?}", self.param_env); trace!("substs: {:#?}", substs); - ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs) - .ok_or_else(|| err_inval!(TooGeneric).into()) + match ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs) { + Ok(Some(instance)) => Ok(instance), + Ok(None) => throw_inval!(TooGeneric), + + // FIXME(eddyb) this could be a bit more specific than `TypeckError`. + Err(error_reported) => throw_inval!(TypeckError(error_reported)), + } } pub fn layout_of_local( &self, frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, local: mir::Local, - layout: Option>, - ) -> InterpResult<'tcx, TyLayout<'tcx>> { + layout: Option>, + ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { // `const_prop` runs into this with an invalid (empty) frame, so we // have to support that case (mostly by skipping all caching). match frame.locals.get(local).and_then(|state| state.layout.get()) { None => { - let layout = crate::interpret::operand::from_known_layout(layout, || { + let layout = from_known_layout(self.tcx, layout, || { let local_ty = frame.body.local_decls[local].ty; let local_ty = self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty); @@ -401,7 +498,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(super) fn size_and_align_of( &self, metadata: MemPlaceMeta, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { if !layout.is_unsized() { return Ok(Some((layout.size, layout.align.abi))); @@ -413,6 +510,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // and it also rounds up to alignment, which we want to avoid, // as the unsized field's alignment could be smaller. assert!(!layout.ty.is_simd()); + assert!(layout.fields.count() > 0); trace!("DST layout: {:?}", layout); let sized_size = layout.fields.offset(layout.fields.count() - 1); @@ -452,7 +550,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // here. But this is where the add would go.) // Return the sum of sizes and max of aligns. - let size = sized_size + unsized_size; + let size = sized_size + unsized_size; // `Size` addition // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). @@ -501,66 +599,59 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn push_stack_frame( &mut self, instance: ty::Instance<'tcx>, - span: Span, body: &'mir mir::Body<'tcx>, return_place: Option>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { - if !self.stack.is_empty() { - info!("PAUSING({}) {}", self.cur_frame(), self.frame().instance); + if !self.stack().is_empty() { + info!("PAUSING({}) {}", self.frame_idx(), self.frame().instance); } ::log_settings::settings().indentation += 1; // first push a stack frame so we have access to the local substs - let extra = M::stack_push(self)?; - self.stack.push(Frame { + let pre_frame = Frame { body, - block: Some(mir::START_BLOCK), + loc: Some(mir::Location::START), return_to_block, return_place, // empty local array, we fill it in below, after we are inside the stack frame and // all methods actually know about the frame locals: IndexVec::new(), - span, instance, - stmt: 0, - extra, - }); - - // don't allocate at all for trivial constants - if body.local_decls.len() > 1 { - // Locals are initially uninitialized. - let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) }; - let mut locals = IndexVec::from_elem(dummy, &body.local_decls); - // Return place is handled specially by the `eval_place` functions, and the - // entry in `locals` should never be used. Make it dead, to be sure. - locals[mir::RETURN_PLACE].value = LocalValue::Dead; - // Now mark those locals as dead that we do not want to initialize - match self.tcx.def_kind(instance.def_id()) { - // statics and constants don't have `Storage*` statements, no need to look for them - Some(DefKind::Static) | Some(DefKind::Const) | Some(DefKind::AssocConst) => {} - _ => { - trace!("push_stack_frame: {:?}: num_bbs: {}", span, body.basic_blocks().len()); - for block in body.basic_blocks() { - for stmt in block.statements.iter() { - use rustc::mir::StatementKind::{StorageDead, StorageLive}; - match stmt.kind { - StorageLive(local) | StorageDead(local) => { - locals[local].value = LocalValue::Dead; - } - _ => {} - } - } + extra: (), + }; + let frame = M::init_frame_extra(self, pre_frame)?; + self.stack_mut().push(frame); + + // Locals are initially uninitialized. + let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) }; + let mut locals = IndexVec::from_elem(dummy, &body.local_decls); + + // Now mark those locals as dead that we do not want to initialize + match self.tcx.def_kind(instance.def_id()) { + // statics and constants don't have `Storage*` statements, no need to look for them + // + // FIXME: The above is likely untrue. See + // . Is it + // okay to ignore `StorageDead`/`StorageLive` annotations during CTFE? + DefKind::Static | DefKind::Const | DefKind::AssocConst => {} + _ => { + // Mark locals that use `Storage*` annotations as dead on function entry. + let always_live = AlwaysLiveLocals::new(self.body()); + for local in locals.indices() { + if !always_live.contains(local) { + locals[local].value = LocalValue::Dead; } } } - // done - self.frame_mut().locals = locals; } + // done + self.frame_mut().locals = locals; - info!("ENTERING({}) {}", self.cur_frame(), self.frame().instance); + M::after_stack_push(self)?; + info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance); - if self.stack.len() > *self.tcx.sess.recursion_limit.get() { + if self.stack().len() > *self.tcx.sess.recursion_limit.get() { throw_exhaust!(StackFrameLimitReached) } else { Ok(()) @@ -570,9 +661,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Jump to the given block. #[inline] pub fn go_to_block(&mut self, target: mir::BasicBlock) { - let frame = self.frame_mut(); - frame.block = Some(target); - frame.stmt = 0; + self.frame_mut().loc = Some(mir::Location { block: target, statement_index: 0 }); } /// *Return* to the given `target` basic block. @@ -594,9 +683,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// If `target` is `None`, that indicates the function does not need cleanup during /// unwinding, and we will just keep propagating that upwards. pub fn unwind_to_block(&mut self, target: Option) { - let frame = self.frame_mut(); - frame.block = target; - frame.stmt = 0; + self.frame_mut().loc = target.map(|block| mir::Location { block, statement_index: 0 }); } /// Pops the current frame from the stack, deallocating the @@ -615,7 +702,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> { info!( "LEAVING({}) {} (unwinding = {})", - self.cur_frame(), + self.frame_idx(), self.frame().instance, unwinding ); @@ -623,14 +710,26 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Sanity check `unwinding`. assert_eq!( unwinding, - match self.frame().block { + match self.frame().loc { None => true, - Some(block) => self.body().basic_blocks()[block].is_cleanup, + Some(loc) => self.body().basic_blocks()[loc.block].is_cleanup, } ); ::log_settings::settings().indentation -= 1; - let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); + let frame = + self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + + if !unwinding { + // Copy the return value to the caller's stack frame. + if let Some(return_place) = frame.return_place { + let op = self.access_local(&frame, mir::RETURN_PLACE, None)?; + self.copy_op_transmute(op, return_place)?; + self.dump_place(*return_place); + } else { + throw_ub!(Unreachable); + } + } // Now where do we jump next? @@ -645,7 +744,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; if !cleanup { - assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked"); + assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); assert!(next_block.is_none(), "tried to skip cleanup when we have a next block!"); assert!(!unwinding, "tried to skip cleanup during unwinding"); // Leak the locals, skip validation, skip machine hook. @@ -653,50 +752,31 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Cleanup: deallocate all locals that are backed by an allocation. - for local in frame.locals { + for local in &frame.locals { self.deallocate_local(local.value)?; } - if M::stack_pop(self, frame.extra, unwinding)? == StackPopJump::NoJump { + if M::after_stack_pop(self, frame, unwinding)? == StackPopJump::NoJump { // The hook already did everything. // We want to skip the `info!` below, hence early return. return Ok(()); } - // Normal return. + // Normal return, figure out where to jump. if unwinding { // Follow the unwind edge. let unwind = next_block.expect("Encountered StackPopCleanup::None when unwinding!"); self.unwind_to_block(unwind); } else { // Follow the normal return edge. - // Validate the return value. Do this after deallocating so that we catch dangling - // references. - if let Some(return_place) = frame.return_place { - if M::enforce_validity(self) { - // Data got changed, better make sure it matches the type! - // It is still possible that the return place held invalid data while - // the function is running, but that's okay because nobody could have - // accessed that same data from the "outside" to observe any broken - // invariant -- that is, unless a function somehow has a ptr to - // its return place... but the way MIR is currently generated, the - // return place is always a local and then this cannot happen. - self.validate_operand(self.place_to_op(return_place)?)?; - } - } else { - // Uh, that shouldn't happen... the function did not intend to return - throw_ub!(Unreachable); - } - - // Jump to new block -- *after* validation so that the spans make more sense. if let Some(ret) = next_block { self.return_to_block(ret)?; } } - if !self.stack.is_empty() { + if !self.stack().is_empty() { info!( "CONTINUING({}) {} (unwinding = {})", - self.cur_frame(), + self.frame_idx(), self.frame().instance, unwinding ); @@ -791,6 +871,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Our result will later be validated anyway, and there seems no good reason // to have to fail early here. This is also more consistent with // `Memory::get_static_alloc` which has to use `const_eval_raw` to avoid cycles. + // FIXME: We can hit delay_span_bug if this is an invalid const, interning finds + // that problem, but we never run validation to show an error. Can we ensure + // this does not happen? let val = self.tcx.const_eval_raw(param_env.and(gid))?; self.raw_const_to_mplace(val) } @@ -804,12 +887,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Place::Local { frame, local } => { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); - if frame != self.cur_frame() { - write!(msg, " ({} frames up)", self.cur_frame() - frame).unwrap(); + if frame != self.frame_idx() { + write!(msg, " ({} frames up)", self.frame_idx() - frame).unwrap(); } write!(msg, ":").unwrap(); - match self.stack[frame].locals[local].value { + match self.stack()[frame].locals[local].value { LocalValue::Dead => write!(msg, " is dead").unwrap(), LocalValue::Uninitialized => write!(msg, " is uninitialized").unwrap(), LocalValue::Live(Operand::Indirect(mplace)) => match mplace.ptr { @@ -830,16 +913,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }, LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { write!(msg, " {:?}", val).unwrap(); - if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = val { + if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val { allocs.push(ptr.alloc_id); } } LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { write!(msg, " ({:?}, {:?})", val1, val2).unwrap(); - if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = val1 { + if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val1 { allocs.push(ptr.alloc_id); } - if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = val2 { + if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val2 { allocs.push(ptr.alloc_id); } } @@ -858,33 +941,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - pub fn generate_stacktrace(&self, explicit_span: Option) -> Vec> { - let mut last_span = None; + pub fn generate_stacktrace(&self) -> Vec> { let mut frames = Vec::new(); for frame in self.stack().iter().rev() { - // make sure we don't emit frames that are duplicates of the previous - if explicit_span == Some(frame.span) { - last_span = Some(frame.span); - continue; - } - if let Some(last) = last_span { - if last == frame.span { - continue; - } - } else { - last_span = Some(frame.span); - } - - let lint_root = frame.current_source_info().and_then(|source_info| { + let source_info = frame.current_source_info(); + let lint_root = source_info.and_then(|source_info| { match &frame.body.source_scopes[source_info.scope].local_data { mir::ClearCrossCrate::Set(data) => Some(data.lint_root), mir::ClearCrossCrate::Clear => None, } }); + let span = source_info.map_or(DUMMY_SP, |source_info| source_info.span); - frames.push(FrameInfo { call_site: frame.span, instance: frame.instance, lint_root }); + frames.push(FrameInfo { span, instance: frame.instance, lint_root }); } - trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span); + trace!("generate stacktrace: {:#?}", frames); frames } } @@ -896,14 +967,14 @@ where Tag: HashStable>, { fn hash_stable(&self, hcx: &mut StableHashingContext<'ctx>, hasher: &mut StableHasher) { - self.body.hash_stable(hcx, hasher); - self.instance.hash_stable(hcx, hasher); - self.span.hash_stable(hcx, hasher); - self.return_to_block.hash_stable(hcx, hasher); - self.return_place.as_ref().map(|r| &**r).hash_stable(hcx, hasher); - self.locals.hash_stable(hcx, hasher); - self.block.hash_stable(hcx, hasher); - self.stmt.hash_stable(hcx, hasher); - self.extra.hash_stable(hcx, hasher); + // Exhaustive match on fields to make sure we forget no field. + let Frame { body, instance, return_to_block, return_place, locals, loc, extra } = self; + body.hash_stable(hcx, hasher); + instance.hash_stable(hcx, hasher); + return_to_block.hash_stable(hcx, hasher); + return_place.as_ref().map(|r| &**r).hash_stable(hcx, hasher); + locals.hash_stable(hcx, hasher); + loc.hash_stable(hcx, hasher); + extra.hash_stable(hcx, hasher); } } diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 90b8a4932991e..02a7f24a1e351 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -4,10 +4,10 @@ //! memory, we need to extract all memory allocations to the global memory pool so they stay around. use super::validity::RefTracking; -use rustc::mir::interpret::{ErrorHandled, InterpResult}; -use rustc::ty::{self, Ty}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; +use rustc_middle::mir::interpret::InterpResult; +use rustc_middle::ty::{self, query::TyCtxtAt, Ty}; use rustc_ast::ast::Mutability; @@ -16,7 +16,7 @@ use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, Scalar pub trait CompileTimeMachine<'mir, 'tcx> = Machine< 'mir, 'tcx, - MemoryKinds = !, + MemoryKind = !, PointerTag = (), ExtraFnVal = !, FrameExtra = (), @@ -28,43 +28,45 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> { /// The ectx from which we intern. ecx: &'rt mut InterpCx<'mir, 'tcx, M>, /// Previously encountered safe references. - ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>, + ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, InternMode)>, /// A list of all encountered allocations. After type-based interning, we traverse this list to /// also intern allocations that are only referenced by a raw pointer or inside a union. leftover_allocations: &'rt mut FxHashSet, - /// The root node of the value that we're looking at. This field is never mutated and only used + /// The root kind of the value that we're looking at. This field is never mutated and only used /// for sanity assertions that will ICE when `const_qualif` screws up. mode: InternMode, - /// This field stores the mutability of the value *currently* being checked. - /// When encountering a mutable reference, we determine the pointee mutability - /// taking into account the mutability of the context: `& &mut i32` is entirely immutable, - /// despite the nested mutable reference! - /// The field gets updated when an `UnsafeCell` is encountered. - mutability: Mutability, + /// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect + /// the intern mode of references we encounter. + inside_unsafe_cell: bool, /// This flag is to avoid triggering UnsafeCells are not allowed behind references in constants /// for promoteds. /// It's a copy of `mir::Body`'s ignore_interior_mut_in_const_validation field - ignore_interior_mut_in_const_validation: bool, + ignore_interior_mut_in_const: bool, } #[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] enum InternMode { - /// Mutable references must in fact be immutable due to their surrounding immutability in a - /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut` - /// that will actually be treated as mutable. - Static, - /// UnsafeCell is OK in the value of a constant: `const FOO = Cell::new(0)` creates - /// a new cell every time it is used. + /// A static and its current mutability. Below shared references inside a `static mut`, + /// this is *immutable*, and below mutable references inside an `UnsafeCell`, this + /// is *mutable*. + Static(hir::Mutability), + /// The "base value" of a const, which can have `UnsafeCell` (as in `const FOO: Cell`), + /// but that interior mutability is simply ignored. ConstBase, - /// `UnsafeCell` ICEs. - Const, + /// The "inner values" of a const with references, where `UnsafeCell` is an error. + ConstInner, } /// Signalling data structure to ensure we don't recurse /// into the memory of other constants or statics struct IsStaticOrFn; +fn mutable_memory_in_const(tcx: TyCtxtAt<'_>, kind: &str) { + // FIXME: show this in validation instead so we can point at where in the value the error is? + tcx.sess.span_err(tcx.span, &format!("mutable memory ({}) is not allowed in constant", kind)); +} + /// Intern an allocation without looking at its children. /// `mode` is the mode of the environment where we found this pointer. /// `mutablity` is the mutability of the place to be interned; even if that says @@ -74,12 +76,11 @@ struct IsStaticOrFn; fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( ecx: &'rt mut InterpCx<'mir, 'tcx, M>, leftover_allocations: &'rt mut FxHashSet, - mode: InternMode, alloc_id: AllocId, - mutability: Mutability, + mode: InternMode, ty: Option>, -) -> InterpResult<'tcx, Option> { - trace!("InternVisitor::intern {:?} with {:?}", alloc_id, mutability,); +) -> Option { + trace!("intern_shallow {:?} with {:?}", alloc_id, mode); // remove allocation let tcx = ecx.tcx; let (kind, mut alloc) = match ecx.memory.alloc_map.remove(&alloc_id) { @@ -88,14 +89,15 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( // Pointer not found in local memory map. It is either a pointer to the global // map, or dangling. // If the pointer is dangling (neither in local nor global memory), we leave it - // to validation to error. The `delay_span_bug` ensures that we don't forget such - // a check in validation. - if tcx.alloc_map.lock().get(alloc_id).is_none() { + // to validation to error -- it has the much better error messages, pointing out where + // in the value the dangling reference lies. + // The `delay_span_bug` ensures that we don't forget such a check in validation. + if tcx.get_global_alloc(alloc_id).is_none() { tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer"); } // treat dangling pointers like other statics // just to stop trying to recurse into them - return Ok(Some(IsStaticOrFn)); + return Some(IsStaticOrFn); } }; // This match is just a canary for future changes to `MemoryKind`, which most likely need @@ -104,51 +106,51 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( MemoryKind::Stack | MemoryKind::Vtable | MemoryKind::CallerLocation => {} } // Set allocation mutability as appropriate. This is used by LLVM to put things into - // read-only memory, and also by Miri when evluating other constants/statics that + // read-only memory, and also by Miri when evaluating other globals that // access this one. - if mode == InternMode::Static { - // When `ty` is `None`, we assume no interior mutability. + if let InternMode::Static(mutability) = mode { + // For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume + // no interior mutability. let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx.tcx, ecx.param_env, ecx.tcx.span)); // For statics, allocation mutability is the combination of the place mutability and // the type mutability. // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere. - if mutability == Mutability::Not && frozen { + let immutable = mutability == Mutability::Not && frozen; + if immutable { alloc.mutability = Mutability::Not; } else { // Just making sure we are not "upgrading" an immutable allocation to mutable. assert_eq!(alloc.mutability, Mutability::Mut); } } else { - // We *could* be non-frozen at `ConstBase`, for constants like `Cell::new(0)`. - // But we still intern that as immutable as the memory cannot be changed once the - // initial value was computed. - // Constants are never mutable. - assert_eq!( - mutability, - Mutability::Not, - "Something went very wrong: mutability requested for a constant" - ); + // No matter what, *constants are never mutable*. Mutating them is UB. + // See const_eval::machine::MemoryExtra::can_access_statics for why + // immutability is so important. + + // There are no sensible checks we can do here; grep for `mutable_memory_in_const` to + // find the checks we are doing elsewhere to avoid even getting here for memory + // that "wants" to be mutable. alloc.mutability = Mutability::Not; }; // link the alloc id to the actual allocation let alloc = tcx.intern_const_alloc(alloc); leftover_allocations.extend(alloc.relocations().iter().map(|&(_, ((), reloc))| reloc)); - tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc); - Ok(None) + tcx.set_alloc_id_memory(alloc_id, alloc); + None } impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> InternVisitor<'rt, 'mir, 'tcx, M> { fn intern_shallow( &mut self, alloc_id: AllocId, - mutability: Mutability, + mode: InternMode, ty: Option>, - ) -> InterpResult<'tcx, Option> { - intern_shallow(self.ecx, self.leftover_allocations, self.mode, alloc_id, mutability, ty) + ) -> Option { + intern_shallow(self.ecx, self.leftover_allocations, alloc_id, mode, ty) } } -impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> +impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> for InternVisitor<'rt, 'mir, 'tcx, M> { type V = MPlaceTy<'tcx>; @@ -165,22 +167,22 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx ) -> InterpResult<'tcx> { if let Some(def) = mplace.layout.ty.ty_adt_def() { if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() { + if self.mode == InternMode::ConstInner && !self.ignore_interior_mut_in_const { + // We do not actually make this memory mutable. But in case the user + // *expected* it to be mutable, make sure we error. This is just a + // sanity check to prevent users from accidentally exploiting the UB + // they caused. It also helps us to find cases where const-checking + // failed to prevent an `UnsafeCell` (but as `ignore_interior_mut_in_const` + // shows that part is not airtight). + mutable_memory_in_const(self.ecx.tcx, "`UnsafeCell`"); + } // We are crossing over an `UnsafeCell`, we can mutate again. This means that // References we encounter inside here are interned as pointing to mutable // allocations. - let old = std::mem::replace(&mut self.mutability, Mutability::Mut); - if !self.ignore_interior_mut_in_const_validation { - assert_ne!( - self.mode, - InternMode::Const, - "UnsafeCells are not allowed behind references in constants. This should \ - have been prevented statically by const qualification. If this were \ - allowed one would be able to change a constant at one use site and other \ - use sites could observe that mutation.", - ); - } + // Remember the `old` value to handle nested `UnsafeCell`. + let old = std::mem::replace(&mut self.inside_unsafe_cell, true); let walked = self.walk_aggregate(mplace, fields); - self.mutability = old; + self.inside_unsafe_cell = old; return walked; } } @@ -190,77 +192,92 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx fn visit_value(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> { // Handle Reference types, as these are the only relocations supported by const eval. // Raw pointers (and boxes) are handled by the `leftover_relocations` logic. + let tcx = self.ecx.tcx; let ty = mplace.layout.ty; - if let ty::Ref(_, referenced_ty, mutability) = ty.kind { + if let ty::Ref(_, referenced_ty, ref_mutability) = ty.kind { let value = self.ecx.read_immediate(mplace.into())?; let mplace = self.ecx.ref_to_mplace(value)?; + assert_eq!(mplace.layout.ty, referenced_ty); // Handle trait object vtables. if let ty::Dynamic(..) = - self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind + tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind { - // Validation has already errored on an invalid vtable pointer so we can safely not - // do anything if this is not a real pointer. + // Validation will error (with a better message) on an invalid vtable pointer + // so we can safely not do anything if this is not a real pointer. if let Scalar::Ptr(vtable) = mplace.meta.unwrap_meta() { - // Explicitly choose `Immutable` here, since vtables are immutable, even + // Explicitly choose const mode here, since vtables are immutable, even // if the reference of the fat pointer is mutable. - self.intern_shallow(vtable.alloc_id, Mutability::Not, None)?; + self.intern_shallow(vtable.alloc_id, InternMode::ConstInner, None); } else { - self.ecx().tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - "vtables pointers cannot be integer pointers", - ); + // Let validation show the error message, but make sure it *does* error. + tcx.sess + .delay_span_bug(tcx.span, "vtables pointers cannot be integer pointers"); } } // Check if we have encountered this pointer+layout combination before. // Only recurse for allocation-backed pointers. if let Scalar::Ptr(ptr) = mplace.ptr { - // We do not have any `frozen` logic here, because it's essentially equivalent to - // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze", - // and we check that in `visit_aggregate`. - // This is not an inherent limitation, but one that we know to be true, because - // const qualification enforces it. We can lift it in the future. - match (self.mode, mutability) { - // immutable references are fine everywhere - (_, hir::Mutability::Not) => {} - // all is "good and well" in the unsoundness of `static mut` + // Compute the mode with which we intern this. + let ref_mode = match self.mode { + InternMode::Static(mutbl) => { + // In statics, merge outer mutability with reference mutability and + // take into account whether we are in an `UnsafeCell`. - // mutable references are ok in `static`. Either they are treated as immutable - // because they are behind an immutable one, or they are behind an `UnsafeCell` - // and thus ok. - (InternMode::Static, hir::Mutability::Mut) => {} - // we statically prevent `&mut T` via `const_qualif` and double check this here - (InternMode::ConstBase, hir::Mutability::Mut) - | (InternMode::Const, hir::Mutability::Mut) => match referenced_ty.kind { - ty::Array(_, n) - if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {} - ty::Slice(_) - if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? == 0 => {} - _ => bug!("const qualif failed to prevent mutable references"), - }, - } - // Compute the mutability with which we'll start visiting the allocation. This is - // what gets changed when we encounter an `UnsafeCell`. - // - // The only way a mutable reference actually works as a mutable reference is - // by being in a `static mut` directly or behind another mutable reference. - // If there's an immutable reference or we are inside a static, then our - // mutable reference is equivalent to an immutable one. As an example: - // `&&mut Foo` is semantically equivalent to `&&Foo` - let mutability = self.mutability.and(mutability); - // Recursing behind references changes the intern mode for constants in order to - // cause assertions to trigger if we encounter any `UnsafeCell`s. - let mode = match self.mode { - InternMode::ConstBase => InternMode::Const, - other => other, + // The only way a mutable reference actually works as a mutable reference is + // by being in a `static mut` directly or behind another mutable reference. + // If there's an immutable reference or we are inside a `static`, then our + // mutable reference is equivalent to an immutable one. As an example: + // `&&mut Foo` is semantically equivalent to `&&Foo` + match ref_mutability { + _ if self.inside_unsafe_cell => { + // Inside an `UnsafeCell` is like inside a `static mut`, the "outer" + // mutability does not matter. + InternMode::Static(ref_mutability) + } + Mutability::Not => { + // A shared reference, things become immutable. + // We do *not* consier `freeze` here -- that is done more precisely + // when traversing the referenced data (by tracking `UnsafeCell`). + InternMode::Static(Mutability::Not) + } + Mutability::Mut => { + // Mutable reference. + InternMode::Static(mutbl) + } + } + } + InternMode::ConstBase | InternMode::ConstInner => { + // Ignore `UnsafeCell`, everything is immutable. Do some sanity checking + // for mutable references that we encounter -- they must all be ZST. + // This helps to prevent users from accidentally exploiting UB that they + // caused (by somehow getting a mutable reference in a `const`). + if ref_mutability == Mutability::Mut { + match referenced_ty.kind { + ty::Array(_, n) + if n.eval_usize(tcx.tcx, self.ecx.param_env) == 0 => {} + ty::Slice(_) + if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? + == 0 => {} + _ => mutable_memory_in_const(tcx, "`&mut`"), + } + } else { + // A shared reference. We cannot check `freeze` here due to references + // like `&dyn Trait` that are actually immutable. We do check for + // concrete `UnsafeCell` when traversing the pointee though (if it is + // a new allocation, not yet interned). + } + // Go on with the "inner" rules. + InternMode::ConstInner + } }; - match self.intern_shallow(ptr.alloc_id, mutability, Some(mplace.layout.ty))? { + match self.intern_shallow(ptr.alloc_id, ref_mode, Some(referenced_ty)) { // No need to recurse, these are interned already and statics may have // cycles, so we don't want to recurse there Some(IsStaticOrFn) => {} // intern everything referenced by this value. The mutability is taken from the // reference. It is checked above that mutable references only happen in // `static mut` - None => self.ref_tracking.track((mplace, mutability, mode), || ()), + None => self.ref_tracking.track((mplace, ref_mode), || ()), } } Ok(()) @@ -271,6 +288,7 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx } } +#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] pub enum InternKind { /// The `mutability` of the static, ignoring the type which may have interior mutability. Static(hir::Mutability), @@ -279,66 +297,78 @@ pub enum InternKind { ConstProp, } +/// Intern `ret` and everything it references. +/// +/// This *cannot raise an interpreter error*. Doing so is left to validation, which +/// tracks where in the value we are and thus can show much better error messages. +/// Any errors here would anyway be turned into `const_err` lints, whereas validation failures +/// are hard errors. pub fn intern_const_alloc_recursive>( ecx: &mut InterpCx<'mir, 'tcx, M>, intern_kind: InternKind, ret: MPlaceTy<'tcx>, - ignore_interior_mut_in_const_validation: bool, -) -> InterpResult<'tcx> { + ignore_interior_mut_in_const: bool, +) where + 'tcx: 'mir, +{ let tcx = ecx.tcx; - let (base_mutability, base_intern_mode) = match intern_kind { - // `static mut` doesn't care about interior mutability, it's mutable anyway - InternKind::Static(mutbl) => (mutbl, InternMode::Static), + let base_intern_mode = match intern_kind { + InternKind::Static(mutbl) => InternMode::Static(mutbl), // FIXME: what about array lengths, array initializers? - InternKind::Constant | InternKind::ConstProp => (Mutability::Not, InternMode::ConstBase), - InternKind::Promoted => (Mutability::Not, InternMode::ConstBase), + InternKind::Constant | InternKind::ConstProp | InternKind::Promoted => { + InternMode::ConstBase + } }; // Type based interning. - // `ref_tracking` tracks typed references we have seen and still need to crawl for + // `ref_tracking` tracks typed references we have already interned and still need to crawl for // more typed information inside them. // `leftover_allocations` collects *all* allocations we see, because some might not // be available in a typed way. They get interned at the end. - let mut ref_tracking = RefTracking::new((ret, base_mutability, base_intern_mode)); + let mut ref_tracking = RefTracking::empty(); let leftover_allocations = &mut FxHashSet::default(); // start with the outermost allocation intern_shallow( ecx, leftover_allocations, - base_intern_mode, // The outermost allocation must exist, because we allocated it with // `Memory::allocate`. ret.ptr.assert_ptr().alloc_id, - base_mutability, + base_intern_mode, Some(ret.layout.ty), - )?; + ); + + ref_tracking.track((ret, base_intern_mode), || ()); - while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() { - let interned = InternVisitor { + while let Some(((mplace, mode), _)) = ref_tracking.todo.pop() { + let res = InternVisitor { ref_tracking: &mut ref_tracking, ecx, mode, leftover_allocations, - mutability, - ignore_interior_mut_in_const_validation, + ignore_interior_mut_in_const, + inside_unsafe_cell: false, } .visit_value(mplace); - if let Err(error) = interned { - // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have - // to read enum discriminants in order to find references in enum variant fields. - if let err_ub!(ValidationFailure(_)) = error.kind { - let err = crate::const_eval::error_to_const_error(&ecx, error); - match err.struct_error( - ecx.tcx, - "it is undefined behavior to use this value", - |mut diag| { - diag.note(crate::const_eval::note_on_undefined_behavior_error()); - diag.emit(); - }, - ) { - Ok(()) | Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {} - } + // We deliberately *ignore* interpreter errors here. When there is a problem, the remaining + // references are "leftover"-interned, and later validation will show a proper error + // and point at the right part of the value causing the problem. + match res { + Ok(()) => {} + Err(error) => { + ecx.tcx.sess.delay_span_bug( + ecx.tcx.span, + "error during interning should later cause validation failure", + ); + // Some errors shouldn't come up because creating them causes + // an allocation, which we should avoid. When that happens, + // dedicated error variants should be introduced instead. + assert!( + !error.kind.allocates(), + "interning encountered allocating error: {}", + error + ); } } } @@ -359,43 +389,44 @@ pub fn intern_const_alloc_recursive>( InternKind::Static(_) => {} // Raw pointers in promoteds may only point to immutable things so we mark // everything as immutable. - // It is UB to mutate through a raw pointer obtained via an immutable reference. + // It is UB to mutate through a raw pointer obtained via an immutable reference: // Since all references and pointers inside a promoted must by their very definition // be created from an immutable reference (and promotion also excludes interior // mutability), mutating through them would be UB. // There's no way we can check whether the user is using raw pointers correctly, // so all we can do is mark this as immutable here. InternKind::Promoted => { + // See const_eval::machine::MemoryExtra::can_access_statics for why + // immutability is so important. alloc.mutability = Mutability::Not; } InternKind::Constant | InternKind::ConstProp => { - // If it's a constant, it *must* be immutable. - // We cannot have mutable memory inside a constant. - // We use `delay_span_bug` here, because this can be reached in the presence - // of fancy transmutes. - if alloc.mutability == Mutability::Mut { - // For better errors later, mark the allocation as immutable - // (on top of the delayed ICE). - alloc.mutability = Mutability::Not; - ecx.tcx.sess.delay_span_bug(ecx.tcx.span, "mutable allocation in constant"); - } + // If it's a constant, we should not have any "leftovers" as everything + // is tracked by const-checking. + // FIXME: downgrade this to a warning? It rejects some legitimate consts, + // such as `const CONST_RAW: *const Vec = &Vec::new() as *const _;`. + ecx.tcx + .sess + .span_err(ecx.tcx.span, "untyped pointers are not allowed in constant"); + // For better errors later, mark the allocation as immutable. + alloc.mutability = Mutability::Not; } } let alloc = tcx.intern_const_alloc(alloc); - tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc); + tcx.set_alloc_id_memory(alloc_id, alloc); for &(_, ((), reloc)) in alloc.relocations().iter() { if leftover_allocations.insert(reloc) { todo.push(reloc); } } } else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) { - // dangling pointer - throw_ub_format!("encountered dangling pointer in final constant") - } else if ecx.tcx.alloc_map.lock().get(alloc_id).is_none() { - // We have hit an `AllocId` that is neither in local or global memory and isn't marked - // as dangling by local memory. + // Codegen does not like dangling pointers, and generally `tcx` assumes that + // all allocations referenced anywhere actually exist. So, make sure we error here. + ecx.tcx.sess.span_err(ecx.tcx.span, "encountered dangling pointer in final constant"); + } else if ecx.tcx.get_global_alloc(alloc_id).is_none() { + // We have hit an `AllocId` that is neither in local or global memory and isn't + // marked as dangling by local memory. That should be impossible. span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id); } } - Ok(()) } diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 03aedad0d988d..42b969c99917f 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -2,18 +2,17 @@ //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. -use rustc::mir::{ +use rustc_hir::def_id::DefId; +use rustc_middle::mir::{ self, interpret::{ConstValue, GlobalId, InterpResult, Scalar}, BinOp, }; -use rustc::ty; -use rustc::ty::layout::{LayoutOf, Primitive, Size}; -use rustc::ty::subst::SubstsRef; -use rustc::ty::TyCtxt; -use rustc_hir::def_id::DefId; +use rustc_middle::ty; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::Span; +use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size}; use super::{ImmTy, InterpCx, Machine, OpTy, PlaceTy}; @@ -29,11 +28,11 @@ fn numeric_intrinsic<'tcx, Tag>( Primitive::Int(integer, _) => integer.size(), _ => bug!("invalid `{}` argument: {:?}", name, bits), }; - let extra = 128 - size.bits() as u128; + let extra = 128 - u128::from(size.bits()); let bits_out = match name { - sym::ctpop => bits.count_ones() as u128, - sym::ctlz => bits.leading_zeros() as u128 - extra, - sym::cttz => (bits << extra).trailing_zeros() as u128 - extra, + sym::ctpop => u128::from(bits.count_ones()), + sym::ctlz => u128::from(bits.leading_zeros()) - extra, + sym::cttz => u128::from((bits << extra).trailing_zeros()) - extra, sym::bswap => (bits << extra).swap_bytes(), sym::bitreverse => (bits << extra).reverse_bits(), _ => bug!("not a numeric intrinsic: {}", name), @@ -72,11 +71,10 @@ crate fn eval_nullary_intrinsic<'tcx>( }) } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Returns `true` if emulation happened. pub fn emulate_intrinsic( &mut self, - span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, M::PointerTag>], ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>, @@ -96,10 +94,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Keep the patterns in this match ordered the same as the list in - // `src/librustc/ty/constness.rs` + // `src/librustc_middle/ty/constness.rs` match intrinsic_name { sym::caller_location => { - let span = self.find_closest_untracked_caller_location().unwrap_or(span); + let span = self.find_closest_untracked_caller_location(); let location = self.alloc_caller_location_for_span(span); self.write_scalar(location.ptr, dest)?; } @@ -116,7 +114,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::needs_drop => self.tcx.types.bool, sym::type_id => self.tcx.types.u64, sym::type_name => self.tcx.mk_static_str(), - _ => span_bug!(span, "Already checked for nullary intrinsics"), + _ => bug!("already checked for nullary intrinsics"), }; let val = self.const_eval(gid, ty)?; self.copy_op(val, dest)?; @@ -134,7 +132,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = self.read_scalar(args[0])?.not_undef()?; let bits = self.force_bits(val, layout_of.size)?; let kind = match layout_of.abi { - ty::layout::Abi::Scalar(ref scalar) => scalar.value, + Abi::Scalar(ref scalar) => scalar.value, _ => bug!("{} called on invalid type {:?}", intrinsic_name, ty), }; let (nonzero, intrinsic_name) = match intrinsic_name { @@ -220,7 +218,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::discriminant_value => { let place = self.deref_operand(args[0])?; let discr_val = self.read_discriminant(place.into())?.0; - self.write_scalar(Scalar::from_uint(discr_val, dest.layout.size), dest)?; + let scalar = match dest.layout.ty.kind { + ty::Int(_) => Scalar::from_int(discr_val as i128, dest.layout.size), + ty::Uint(_) => Scalar::from_uint(discr_val, dest.layout.size), + _ => bug!("invalid `discriminant_value` return layout: {:?}", dest.layout), + }; + self.write_scalar(scalar, dest)?; } sym::unchecked_shl | sym::unchecked_shr @@ -261,7 +264,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val_bits = self.force_bits(val, layout.size)?; let raw_shift = self.read_scalar(args[1])?.not_undef()?; let raw_shift_bits = self.force_bits(raw_shift, layout.size)?; - let width_bits = layout.size.bits() as u128; + let width_bits = u128::from(layout.size.bits()); let shift_bits = raw_shift_bits % width_bits; let inv_shift_bits = (width_bits - shift_bits) % width_bits; let result_bits = if intrinsic_name == sym::rotate_left { @@ -275,7 +278,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::ptr_offset_from => { - let isize_layout = self.layout_of(self.tcx.types.isize)?; let a = self.read_immediate(args[0])?.to_scalar()?; let b = self.read_immediate(args[1])?.to_scalar()?; @@ -292,7 +294,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let a = a.to_machine_usize(self)?; let b = b.to_machine_usize(self)?; if a == b && a != 0 { - self.write_scalar(Scalar::from_int(0, isize_layout.size), dest)?; + self.write_scalar(Scalar::from_machine_isize(0, self), dest)?; true } else { false @@ -312,6 +314,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); } let usize_layout = self.layout_of(self.tcx.types.usize)?; + let isize_layout = self.layout_of(self.tcx.types.isize)?; let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout); let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout); let (val, _overflowed, _ty) = @@ -350,8 +353,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); for i in 0..len { - let place = self.place_field(dest, i)?; - let value = if i == index { elem } else { self.operand_field(input, i)? }; + let place = self.place_index(dest, i)?; + let value = if i == index { elem } else { self.operand_index(input, i)? }; self.copy_op(value, place)?; } } @@ -370,7 +373,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { "Return type `{}` must match vector element type `{}`", dest.layout.ty, e_ty ); - self.copy_op(self.operand_field(args[0], index)?, dest)?; + self.copy_op(self.operand_index(args[0], index)?, dest)?; } _ => return Ok(false), } diff --git a/src/librustc_mir/interpret/intrinsics/caller_location.rs b/src/librustc_mir/interpret/intrinsics/caller_location.rs index dc2b0e1b983dc..91b046d7bb264 100644 --- a/src/librustc_mir/interpret/intrinsics/caller_location.rs +++ b/src/librustc_mir/interpret/intrinsics/caller_location.rs @@ -1,5 +1,7 @@ -use rustc::middle::lang_items::PanicLocationLangItem; -use rustc::ty::subst::Subst; +use std::convert::TryFrom; + +use rustc_hir::lang_items::PanicLocationLangItem; +use rustc_middle::ty::subst::Subst; use rustc_span::{Span, Symbol}; use rustc_target::abi::LayoutOf; @@ -8,20 +10,23 @@ use crate::interpret::{ MPlaceTy, MemoryKind, Scalar, }; -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. If the first frame found lacks `#[track_caller]`, then - /// `None` is returned and the callsite of the function invocation itself should be used. - crate fn find_closest_untracked_caller_location(&self) -> Option { - let mut caller_span = None; - for next_caller in self.stack.iter().rev() { - if !next_caller.instance.def.requires_caller_location(*self.tcx) { - return caller_span; - } - caller_span = Some(next_caller.span); - } - - caller_span + /// frame which is not `#[track_caller]`. + crate fn find_closest_untracked_caller_location(&self) -> Span { + self.stack() + .iter() + .rev() + // Find first non-`#[track_caller]` frame. + .find(|frame| !frame.instance.def.requires_caller_location(*self.tcx)) + // Assert that there is always such a frame. + .unwrap() + .current_source_info() + // Assert that the frame we look at is actually executing code currently + // (`current_source_info` is None when we are unwinding and the frame does + // not require cleanup). + .unwrap() + .span } /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. @@ -59,8 +64,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); ( Symbol::intern(&caller.file.name.to_string()), - caller.line as u32, - caller.col_display as u32 + 1, + u32::try_from(caller.line).unwrap(), + u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), ) } diff --git a/src/librustc_mir/interpret/intrinsics/type_name.rs b/src/librustc_mir/interpret/intrinsics/type_name.rs index 162387308040d..71cca725982f5 100644 --- a/src/librustc_mir/interpret/intrinsics/type_name.rs +++ b/src/librustc_mir/interpret/intrinsics/type_name.rs @@ -1,12 +1,12 @@ -use rustc::hir::map::{DefPathData, DisambiguatedDefPathData}; -use rustc::mir::interpret::Allocation; -use rustc::ty::{ +use rustc_hir::def_id::CrateNum; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::mir::interpret::Allocation; +use rustc_middle::ty::{ self, print::{PrettyPrinter, Print, Printer}, subst::{GenericArg, GenericArgKind}, Ty, TyCtxt, }; -use rustc_hir::def_id::CrateNum; use std::fmt::Write; struct AbsolutePathPrinter<'tcx> { @@ -60,7 +60,6 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::FnDef(def_id, substs) | ty::Opaque(def_id, substs) | ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) - | ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | ty::Closure(def_id, substs) | ty::Generator(def_id, substs, _) => self.print_def_path(def_id, substs), ty::Foreign(def_id) => self.print_def_path(def_id, &[]), @@ -129,9 +128,8 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { self = print_prefix(self)?; // Skip `::{{constructor}}` on tuple/unit structs. - match disambiguated_data.data { - DefPathData::Ctor => return Ok(self), - _ => {} + if disambiguated_data.data == DefPathData::Ctor { + return Ok(self); } self.path.push_str("::"); diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 88cb74ebf8c98..39b0218c5d73f 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -5,13 +5,13 @@ use std::borrow::{Borrow, Cow}; use std::hash::Hash; -use rustc::mir; -use rustc::ty::{self, Ty}; -use rustc_span::Span; +use rustc_middle::mir; +use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::DefId; use super::{ - AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, - OpTy, Operand, PlaceTy, Pointer, Scalar, + AllocId, Allocation, AllocationExtra, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult, + Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar, }; /// Data returned by Machine::stack_pop, @@ -51,7 +51,7 @@ pub trait AllocMap { where K: Borrow; - /// Returns data based the keys and values in the map. + /// Returns data based on the keys and values in the map. fn filter_map_collect(&self, f: impl FnMut(&K, &V) -> Option) -> Vec; /// Returns a reference to entry `k`. If no such entry exists, call @@ -79,11 +79,13 @@ pub trait AllocMap { /// and some use case dependent behaviour can instead be applied. pub trait Machine<'mir, 'tcx>: Sized { /// Additional memory kinds a machine wishes to distinguish from the builtin ones - type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static; + type MemoryKind: ::std::fmt::Debug + ::std::fmt::Display + MayLeak + Eq + 'static; /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows" /// . /// The `default()` is used for pointers to consts, statics, vtables and functions. + /// The `Debug` formatting is used for displaying pointers; we cannot use `Display` + /// as `()` does not implement that, but it should be "nice" output. type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static; /// Machines can define extra (non-instance) things that represent values of function pointers. @@ -105,19 +107,20 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Memory's allocation map type MemoryMap: AllocMap< AllocId, - (MemoryKind, Allocation), + (MemoryKind, Allocation), > + Default + Clone; - /// The memory kind to use for copied statics -- or None if statics should not be mutated - /// and thus any such attempt will cause a `ModifiedStatic` error to be raised. + /// The memory kind to use for copied global memory (held in `tcx`) -- + /// or None if such memory should not be mutated and thus any such attempt will cause + /// a `ModifiedStatic` error to be raised. /// Statics are copied under two circumstances: When they are mutated, and when - /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation + /// `tag_allocation` (see below) returns an owned allocation /// that is added to the memory so that the work is not done twice. - const STATIC_KIND: Option; + const GLOBAL_KIND: Option; /// Whether memory accesses should be alignment-checked. - const CHECK_ALIGN: bool; + fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool; /// Whether to enforce the validity invariant fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; @@ -134,7 +137,6 @@ pub trait Machine<'mir, 'tcx>: Sized { /// was used. fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, - span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Self::PointerTag>], ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, @@ -155,7 +157,6 @@ pub trait Machine<'mir, 'tcx>: Sized { /// responsibility to advance the instruction pointer as appropriate. fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, - span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Self::PointerTag>], ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, @@ -207,11 +208,15 @@ pub trait Machine<'mir, 'tcx>: Sized { Ok(()) } - /// Called before a `Static` value is accessed. + /// Called before a global allocation is accessed. + /// `def_id` is `Some` if this is the "lazy" allocation of a static. #[inline] - fn before_access_static( + fn before_access_global( _memory_extra: &Self::MemoryExtra, + _alloc_id: AllocId, _allocation: &Allocation, + _static_def_id: Option, + _is_write: bool, ) -> InterpResult<'tcx> { Ok(()) } @@ -227,14 +232,42 @@ pub trait Machine<'mir, 'tcx>: Sized { id } + /// Called when converting a `ty::Const` to an operand (in + /// `eval_const_to_op`). + /// + /// Miri uses this callback for creating per thread allocations for thread + /// locals. In Rust, one way of creating a thread local is by marking a + /// static with `#[thread_local]`. On supported platforms this gets + /// translated to a LLVM thread local for which LLVM automatically ensures + /// that each thread gets its own copy. Since LLVM automatically handles + /// thread locals, the Rust compiler just treats thread local statics as + /// regular statics even though accessing a thread local static should be an + /// effectful computation that depends on the current thread. The long term + /// plan is to change MIR to make accesses to thread locals explicit + /// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685 + /// is not fixed, our current workaround in Miri is to use this function to + /// make per-thread copies of thread locals. Please note that we cannot make + /// these copies in `canonical_alloc_id` because that is too late: for + /// example, if one created a pointer in thread `t1` to a thread local and + /// sent it to another thread `t2`, resolving the access in + /// `canonical_alloc_id` would result in pointer pointing to `t2`'s thread + /// local and not `t1` as it should. + #[inline] + fn adjust_global_const( + _ecx: &InterpCx<'mir, 'tcx, Self>, + val: mir::interpret::ConstValue<'tcx>, + ) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> { + Ok(val) + } + /// Called to initialize the "extra" state of an allocation and make the pointers /// it contains (in relocations) tagged. The way we construct allocations is /// to always first construct it without extra and then add the extra. /// This keeps uniform code paths for handling both allocations created by CTFE - /// for statics, and allocations created by Miri during evaluation. + /// for globals, and allocations created by Miri during evaluation. /// /// `kind` is the kind of the allocation being tagged; it can be `None` when - /// it's a static and `STATIC_KIND` is `None`. + /// it's a global and `GLOBAL_KIND` is `None`. /// /// This should avoid copying if no work has to be done! If this returns an owned /// allocation (because a copy had to be done to add tags or metadata), machine memory will @@ -243,20 +276,28 @@ pub trait Machine<'mir, 'tcx>: Sized { /// /// Also return the "base" tag to use for this allocation: the one that is used for direct /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent - /// with `tag_static_base_pointer`. + /// with `tag_global_base_pointer`. fn init_allocation_extra<'b>( memory_extra: &Self::MemoryExtra, id: AllocId, alloc: Cow<'b, Allocation>, - kind: Option>, + kind: Option>, ) -> (Cow<'b, Allocation>, Self::PointerTag); - /// Return the "base" tag for the given *static* allocation: the one that is used for direct - /// accesses to this static/const/fn allocation. If `id` is not a static allocation, + /// Called to notify the machine before a deallocation occurs. + fn before_deallocation( + _memory_extra: &mut Self::MemoryExtra, + _id: AllocId, + ) -> InterpResult<'tcx> { + Ok(()) + } + + /// Return the "base" tag for the given *global* allocation: the one that is used for direct + /// accesses to this static/const/fn allocation. If `id` is not a global allocation, /// this will return an unusable tag (i.e., accesses will be UB)! /// /// Expects `id` to be already canonical, if needed. - fn tag_static_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; + fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; /// Executes a retagging operation #[inline] @@ -268,13 +309,31 @@ pub trait Machine<'mir, 'tcx>: Sized { Ok(()) } - /// Called immediately before a new stack frame got pushed. - fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>; + /// Called immediately before a new stack frame gets pushed. + fn init_frame_extra( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + frame: Frame<'mir, 'tcx, Self::PointerTag>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>; + + /// Borrow the current thread's stack. + fn stack( + ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>]; + + /// Mutably borrow the current thread's stack. + fn stack_mut( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec>; - /// Called immediately after a stack frame gets popped - fn stack_pop( + /// Called immediately after a stack frame got pushed and its locals got initialized. + fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + Ok(()) + } + + /// Called immediately after a stack frame got popped, but before jumping back to the caller. + fn after_stack_pop( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _extra: Self::FrameExtra, + _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, _unwinding: bool, ) -> InterpResult<'tcx, StackPopJump> { // By default, we do not support unwinding from panics @@ -287,7 +346,7 @@ pub trait Machine<'mir, 'tcx>: Sized { ) -> InterpResult<'tcx, Pointer> { Err((if int == 0 { // This is UB, seriously. - err_ub!(InvalidIntPointerUsage(0)) + err_ub!(DanglingIntPointer(0, CheckInAllocMsg::InboundsTest)) } else { // This is just something we cannot support during const-eval. err_unsup!(ReadBytesAsPointer) @@ -300,3 +359,67 @@ pub trait Machine<'mir, 'tcx>: Sized { _ptr: Pointer, ) -> InterpResult<'tcx, u64>; } + +// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines +// (CTFE and ConstProp) use the same instance. Here, we share that code. +pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { + type PointerTag = (); + type ExtraFnVal = !; + + type MemoryKind = !; + type MemoryMap = rustc_data_structures::fx::FxHashMap, Allocation)>; + const GLOBAL_KIND: Option = None; // no copying of globals from `tcx` to machine memory + + type AllocExtra = (); + type FrameExtra = (); + + #[inline(always)] + fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool { + // We do not check for alignment to avoid having to carry an `Align` + // in `ConstValue::ByRef`. + false + } + + #[inline(always)] + fn enforce_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + false // for now, we don't enforce validity + } + + #[inline(always)] + fn call_extra_fn( + _ecx: &mut InterpCx<$mir, $tcx, Self>, + fn_val: !, + _args: &[OpTy<$tcx>], + _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>, + _unwind: Option, + ) -> InterpResult<$tcx> { + match fn_val {} + } + + #[inline(always)] + fn init_allocation_extra<'b>( + _memory_extra: &Self::MemoryExtra, + _id: AllocId, + alloc: Cow<'b, Allocation>, + _kind: Option>, + ) -> (Cow<'b, Allocation>, Self::PointerTag) { + // We do not use a tag so we can just cheaply forward the allocation + (alloc, ()) + } + + #[inline(always)] + fn tag_global_base_pointer( + _memory_extra: &Self::MemoryExtra, + _id: AllocId, + ) -> Self::PointerTag { + () + } + + #[inline(always)] + fn init_frame_extra( + _ecx: &mut InterpCx<$mir, $tcx, Self>, + frame: Frame<$mir, $tcx>, + ) -> InterpResult<$tcx, Frame<$mir, $tcx>> { + Ok(frame) + } +} diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 277a77af3fd56..61c365644c7f2 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -8,18 +8,20 @@ use std::borrow::Cow; use std::collections::VecDeque; +use std::convert::TryFrom; +use std::fmt; use std::ptr; -use rustc::ty::layout::{Align, HasDataLayout, Size, TargetDataLayout}; -use rustc::ty::{self, query::TyCtxtAt, Instance, ParamEnv}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - use rustc_ast::ast::Mutability; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_middle::ty::{self, query::TyCtxtAt, Instance, ParamEnv}; +use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout}; use super::{ - AllocId, AllocMap, Allocation, AllocationExtra, CheckInAllocMsg, ErrorHandled, GlobalAlloc, - GlobalId, InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Scalar, + AllocId, AllocMap, Allocation, AllocationExtra, CheckInAllocMsg, GlobalAlloc, GlobalId, + InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Scalar, }; +use crate::util::pretty; #[derive(Debug, PartialEq, Copy, Clone)] pub enum MemoryKind { @@ -45,6 +47,17 @@ impl MayLeak for MemoryKind { } } +impl fmt::Display for MemoryKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MemoryKind::Stack => write!(f, "stack variable"), + MemoryKind::Vtable => write!(f, "vtable"), + MemoryKind::CallerLocation => write!(f, "caller location"), + MemoryKind::Machine(m) => write!(f, "{}", m), + } + } +} + /// Used by `get_size_and_align` to indicate whether the allocation needs to be live. #[derive(Debug, Copy, Clone)] pub enum AllocCheck { @@ -80,12 +93,12 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// Allocations local to this instance of the miri engine. The kind /// helps ensure that the same mechanism is used for allocation and /// deallocation. When an allocation is not found here, it is a - /// static and looked up in the `tcx` for read access. Some machines may - /// have to mutate this map even on a read-only access to a static (because + /// global and looked up in the `tcx` for read access. Some machines may + /// have to mutate this map even on a read-only access to a global (because /// they do pointer provenance tracking and the allocations in `tcx` have /// the wrong type), so we let the machine override this type. - /// Either way, if the machine allows writing to a static, doing so will - /// create a copy of the static allocation here. + /// Either way, if the machine allows writing to a global, doing so will + /// create a copy of the global allocation here. // FIXME: this should not be public, but interning currently needs access to it pub(super) alloc_map: M::MemoryMap, @@ -130,9 +143,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// This represents a *direct* access to that memory, as opposed to access /// through a pointer that was created by the program. #[inline] - pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer { + pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer { let id = M::canonical_alloc_id(self, ptr.alloc_id); - ptr.with_tag(M::tag_static_base_pointer(&self.extra, id)) + ptr.with_tag(M::tag_global_base_pointer(&self.extra, id)) } pub fn create_fn_alloc( @@ -140,32 +153,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { fn_val: FnVal<'tcx, M::ExtraFnVal>, ) -> Pointer { let id = match fn_val { - FnVal::Instance(instance) => self.tcx.alloc_map.lock().create_fn_alloc(instance), + FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance), FnVal::Other(extra) => { // FIXME(RalfJung): Should we have a cache here? - let id = self.tcx.alloc_map.lock().reserve(); + let id = self.tcx.reserve_alloc_id(); let old = self.extra_fn_ptr_map.insert(id, extra); assert!(old.is_none()); id } }; - self.tag_static_base_pointer(Pointer::from(id)) + self.tag_global_base_pointer(Pointer::from(id)) } pub fn allocate( &mut self, size: Size, align: Align, - kind: MemoryKind, + kind: MemoryKind, ) -> Pointer { let alloc = Allocation::undef(size, align); self.allocate_with(alloc, kind) } - pub fn allocate_static_bytes( + pub fn allocate_bytes( &mut self, bytes: &[u8], - kind: MemoryKind, + kind: MemoryKind, ) -> Pointer { let alloc = Allocation::from_byte_aligned_bytes(bytes); self.allocate_with(alloc, kind) @@ -174,13 +187,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn allocate_with( &mut self, alloc: Allocation, - kind: MemoryKind, + kind: MemoryKind, ) -> Pointer { - let id = self.tcx.alloc_map.lock().reserve(); + let id = self.tcx.reserve_alloc_id(); debug_assert_ne!( Some(kind), - M::STATIC_KIND.map(MemoryKind::Machine), - "dynamically allocating static memory" + M::GLOBAL_KIND.map(MemoryKind::Machine), + "dynamically allocating global memory" ); let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind)); self.alloc_map.insert(id, (kind, alloc.into_owned())); @@ -193,7 +206,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { old_size_and_align: Option<(Size, Align)>, new_size: Size, new_align: Align, - kind: MemoryKind, + kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { if ptr.offset.bytes() != 0 { throw_ub_format!( @@ -215,9 +228,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Ok(new_ptr) } - /// Deallocate a local, or do nothing if that local has been made into a static + /// Deallocate a local, or do nothing if that local has been made into a global. pub fn deallocate_local(&mut self, ptr: Pointer) -> InterpResult<'tcx> { - // The allocation might be already removed by static interning. + // The allocation might be already removed by global interning. // This can only really happen in the CTFE instance, not in miri. if self.alloc_map.contains_key(&ptr.alloc_id) { self.deallocate(ptr, None, MemoryKind::Stack) @@ -230,7 +243,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { &mut self, ptr: Pointer, old_size_and_align: Option<(Size, Align)>, - kind: MemoryKind, + kind: MemoryKind, ) -> InterpResult<'tcx> { trace!("deallocating: {}", ptr.alloc_id); @@ -241,13 +254,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ); } + M::before_deallocation(&mut self.extra, ptr.alloc_id)?; + let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) { Some(alloc) => alloc, None => { - // Deallocating static memory -- always an error - return Err(match self.tcx.alloc_map.lock().get(ptr.alloc_id) { + // Deallocating global memory -- always an error + return Err(match self.tcx.get_global_alloc(ptr.alloc_id) { Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"), - Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => { + Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_format!("deallocating static memory") } None => err_ub!(PointerUseAfterFree(ptr.alloc_id)), @@ -258,7 +273,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if alloc_kind != kind { throw_ub_format!( - "deallocating `{:?}` memory using `{:?}` deallocation operation", + "deallocating {} memory using {} deallocation operation", alloc_kind, kind ); @@ -308,12 +323,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { size: Size, align: Align, ) -> InterpResult<'tcx, Option>> { - let align = M::CHECK_ALIGN.then_some(align); + let align = M::enforce_alignment(&self.extra).then_some(align); self.check_ptr_access_align(sptr, size, align, CheckInAllocMsg::MemoryAccessTest) } /// Like `check_ptr_access`, but *definitely* checks alignment when `align` - /// is `Some` (overriding `M::CHECK_ALIGN`). Also lets the caller control + /// is `Some` (overriding `M::enforce_alignment`). Also lets the caller control /// the error message for the out-of-bounds case. pub fn check_ptr_access_align( &self, @@ -346,11 +361,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { }; Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) { Ok(bits) => { - let bits = bits as u64; // it's ptr-sized + let bits = u64::try_from(bits).unwrap(); // it's ptr-sized assert!(size.bytes() == 0); // Must be non-NULL. if bits == 0 { - throw_ub!(InvalidIntPointerUsage(0)) + throw_ub!(DanglingIntPointer(0, msg)) } // Must be aligned. if let Some(align) = align { @@ -403,74 +418,75 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// Allocation accessors impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { - /// Helper function to obtain the global (tcx) allocation for a static. + /// Helper function to obtain a global (tcx) allocation. /// This attempts to return a reference to an existing allocation if /// one can be found in `tcx`. That, however, is only possible if `tcx` and /// this machine use the same pointer tag, so it is indirected through /// `M::tag_allocation`. - /// - /// Notice that every static has two `AllocId` that will resolve to the same - /// thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, - /// and the other one is maps to `GlobalAlloc::Memory`, this is returned by - /// `const_eval_raw` and it is the "resolved" ID. - /// The resolved ID is never used by the interpreted progrma, it is hidden. - /// The `GlobalAlloc::Memory` branch here is still reachable though; when a static - /// contains a reference to memory that was created during its evaluation (i.e., not to - /// another static), those inner references only exist in "resolved" form. - /// - /// Assumes `id` is already canonical. - fn get_static_alloc( + fn get_global_alloc( memory_extra: &M::MemoryExtra, tcx: TyCtxtAt<'tcx>, id: AllocId, + is_write: bool, ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> { - let alloc = tcx.alloc_map.lock().get(id); - let alloc = match alloc { - Some(GlobalAlloc::Memory(mem)) => Cow::Borrowed(mem), + let (alloc, def_id) = match tcx.get_global_alloc(id) { + Some(GlobalAlloc::Memory(mem)) => { + // Memory of a constant or promoted or anonymous memory referenced by a static. + (mem, None) + } Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { - // We got a "lazy" static that has not been computed yet. + // Notice that every static has two `AllocId` that will resolve to the same + // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, + // and the other one is maps to `GlobalAlloc::Memory`, this is returned by + // `const_eval_raw` and it is the "resolved" ID. + // The resolved ID is never used by the interpreted program, it is hidden. + // This is relied upon for soundness of const-patterns; a pointer to the resolved + // ID would "sidestep" the checks that make sure consts do not point to statics! + // The `GlobalAlloc::Memory` branch here is still reachable though; when a static + // contains a reference to memory that was created during its evaluation (i.e., not + // to another static), those inner references only exist in "resolved" form. + // + // Assumes `id` is already canonical. if tcx.is_foreign_item(def_id) { - trace!("get_static_alloc: foreign item {:?}", def_id); + trace!("get_global_alloc: foreign item {:?}", def_id); throw_unsup!(ReadForeignStatic(def_id)) } - trace!("get_static_alloc: Need to compute {:?}", def_id); + trace!("get_global_alloc: Need to compute {:?}", def_id); let instance = Instance::mono(tcx.tcx, def_id); let gid = GlobalId { instance, promoted: None }; - // use the raw query here to break validation cycles. Later uses of the static - // will call the full query anyway + // Use the raw query here to break validation cycles. Later uses of the static + // will call the full query anyway. let raw_const = tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| { // no need to report anything, the const_eval call takes care of that // for statics assert!(tcx.is_static(def_id)); - match err { - ErrorHandled::Reported => err_inval!(ReferencedConstant), - ErrorHandled::TooGeneric => err_inval!(TooGeneric), - } + err })?; // Make sure we use the ID of the resolved memory, not the lazy one! let id = raw_const.alloc_id; - let allocation = tcx.alloc_map.lock().unwrap_memory(id); + let allocation = tcx.global_alloc(id).unwrap_memory(); - M::before_access_static(memory_extra, allocation)?; - Cow::Borrowed(allocation) + (allocation, Some(def_id)) } }; + M::before_access_global(memory_extra, id, alloc, def_id, is_write)?; + let alloc = Cow::Borrowed(alloc); // We got tcx memory. Let the machine initialize its "extra" stuff. let (alloc, tag) = M::init_allocation_extra( memory_extra, id, // always use the ID we got as input, not the "hidden" one. alloc, - M::STATIC_KIND.map(MemoryKind::Machine), + M::GLOBAL_KIND.map(MemoryKind::Machine), ); - debug_assert_eq!(tag, M::tag_static_base_pointer(memory_extra, id)); + debug_assert_eq!(tag, M::tag_global_base_pointer(memory_extra, id)); Ok(alloc) } /// Gives raw access to the `Allocation`, without bounds or alignment checks. - /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCtx` instead! + /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCx` instead! pub fn get_raw( &self, id: AllocId, @@ -478,10 +494,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let id = M::canonical_alloc_id(self, id); // The error type of the inner closure here is somewhat funny. We have two // ways of "erroring": An actual error, or because we got a reference from - // `get_static_alloc` that we can actually use directly without inserting anything anywhere. + // `get_global_alloc` that we can actually use directly without inserting anything anywhere. // So the error type is `InterpResult<'tcx, &Allocation>`. let a = self.alloc_map.get_or(id, || { - let alloc = Self::get_static_alloc(&self.extra, self.tcx, id).map_err(Err)?; + let alloc = Self::get_global_alloc(&self.extra, self.tcx, id, /*is_write*/ false) + .map_err(Err)?; match alloc { Cow::Borrowed(alloc) => { // We got a ref, cheaply return that as an "error" so that the @@ -490,8 +507,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } Cow::Owned(alloc) => { // Need to put it into the map and return a ref to that - let kind = M::STATIC_KIND.expect( - "I got an owned allocation that I have to copy but the machine does \ + let kind = M::GLOBAL_KIND.expect( + "I got a global allocation that I have to copy but the machine does \ not expect that to happen", ); Ok((MemoryKind::Machine(kind), alloc)) @@ -506,7 +523,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } /// Gives raw mutable access to the `Allocation`, without bounds or alignment checks. - /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCtx` instead! + /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCx` instead! pub fn get_raw_mut( &mut self, id: AllocId, @@ -515,16 +532,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let tcx = self.tcx; let memory_extra = &self.extra; let a = self.alloc_map.get_mut_or(id, || { - // Need to make a copy, even if `get_static_alloc` is able + // Need to make a copy, even if `get_global_alloc` is able // to give us a cheap reference. - let alloc = Self::get_static_alloc(memory_extra, tcx, id)?; + let alloc = Self::get_global_alloc(memory_extra, tcx, id, /*is_write*/ true)?; if alloc.mutability == Mutability::Not { throw_ub!(WriteToReadOnly(id)) } - match M::STATIC_KIND { - Some(kind) => Ok((MemoryKind::Machine(kind), alloc.into_owned())), - None => throw_unsup!(ModifiedStatic), - } + let kind = M::GLOBAL_KIND.expect( + "I got a global allocation that I have to copy but the machine does \ + not expect that to happen", + ); + Ok((MemoryKind::Machine(kind), alloc.into_owned())) }); // Unpack the error type manually because type inference doesn't // work otherwise (and we cannot help it because `impl Trait`) @@ -553,7 +571,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // # Regular allocations // Don't use `self.get_raw` here as that will // a) cause cycles in case `id` refers to a static - // b) duplicate a static's allocation in miri + // b) duplicate a global's allocation in miri if let Some((_, alloc)) = self.alloc_map.get(id) { return Ok((alloc.size, alloc.align)); } @@ -572,8 +590,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // # Statics // Can't do this in the match argument, we may get cycle errors since the lock would // be held throughout the match. - let alloc = self.tcx.alloc_map.lock().get(id); - match alloc { + match self.tcx.get_global_alloc(id) { Some(GlobalAlloc::Static(did)) => { // Use size and align of the type. let ty = self.tcx.type_of(did); @@ -608,7 +625,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if let Some(extra) = self.extra_fn_ptr_map.get(&id) { Some(FnVal::Other(*extra)) } else { - match self.tcx.alloc_map.lock().get(id) { + match self.tcx.get_global_alloc(id) { Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)), _ => None, } @@ -639,128 +656,90 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { self.dump_allocs(vec![id]); } - fn dump_alloc_helper( - &self, - allocs_seen: &mut FxHashSet, - allocs_to_print: &mut VecDeque, - mut msg: String, - alloc: &Allocation, - extra: String, - ) { - use std::fmt::Write; - - let prefix_len = msg.len(); - let mut relocations = vec![]; - - for i in 0..alloc.size.bytes() { - let i = Size::from_bytes(i); - if let Some(&(_, target_id)) = alloc.relocations().get(&i) { - if allocs_seen.insert(target_id) { - allocs_to_print.push_back(target_id); - } - relocations.push((i, target_id)); - } - if alloc.undef_mask().is_range_defined(i, i + Size::from_bytes(1)).is_ok() { - // this `as usize` is fine, since `i` came from a `usize` - let i = i.bytes() as usize; - - // Checked definedness (and thus range) and relocations. This access also doesn't - // influence interpreter execution but is only for debugging. - let bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(i..i + 1); - write!(msg, "{:02x} ", bytes[0]).unwrap(); - } else { - msg.push_str("__ "); - } - } - - eprintln!( - "{}({} bytes, alignment {}){}", - msg, - alloc.size.bytes(), - alloc.align.bytes(), - extra - ); - - if !relocations.is_empty() { - msg.clear(); - write!(msg, "{:1$}", "", prefix_len).unwrap(); // Print spaces. - let mut pos = Size::ZERO; - let relocation_width = (self.pointer_size().bytes() - 1) * 3; - for (i, target_id) in relocations { - // this `as usize` is fine, since we can't print more chars than `usize::MAX` - write!(msg, "{:1$}", "", ((i - pos) * 3).bytes() as usize).unwrap(); - let target = format!("({})", target_id); - // this `as usize` is fine, since we can't print more chars than `usize::MAX` - write!(msg, "└{0:─^1$}┘ ", target, relocation_width as usize).unwrap(); - pos = i + self.pointer_size(); - } - eprintln!("{}", msg); - } - } - /// Print a list of allocations and all allocations they point to, recursively. /// This prints directly to stderr, ignoring RUSTC_LOG! It is up to the caller to /// control for this. pub fn dump_allocs(&self, mut allocs: Vec) { + // Cannot be a closure because it is generic in `Tag`, `Extra`. + fn write_allocation_track_relocs<'tcx, Tag: Copy + fmt::Debug, Extra>( + tcx: TyCtxtAt<'tcx>, + allocs_to_print: &mut VecDeque, + alloc: &Allocation, + ) { + for &(_, target_id) in alloc.relocations().values() { + allocs_to_print.push_back(target_id); + } + pretty::write_allocation(tcx.tcx, alloc, &mut std::io::stderr()).unwrap(); + } + allocs.sort(); allocs.dedup(); let mut allocs_to_print = VecDeque::from(allocs); - let mut allocs_seen = FxHashSet::default(); + // `allocs_printed` contains all allocations that we have already printed. + let mut allocs_printed = FxHashSet::default(); while let Some(id) = allocs_to_print.pop_front() { - let msg = format!("Alloc {:<5} ", format!("{}:", id)); - - // normal alloc? - match self.alloc_map.get_or(id, || Err(())) { - Ok((kind, alloc)) => { - let extra = match kind { - MemoryKind::Stack => " (stack)".to_owned(), - MemoryKind::Vtable => " (vtable)".to_owned(), - MemoryKind::CallerLocation => " (caller_location)".to_owned(), - MemoryKind::Machine(m) => format!(" ({:?})", m), - }; - self.dump_alloc_helper( - &mut allocs_seen, - &mut allocs_to_print, - msg, - alloc, - extra, - ); + if !allocs_printed.insert(id) { + // Already printed, so skip this. + continue; + } + + eprint!("{}", id); + match self.alloc_map.get(id) { + Some(&(kind, ref alloc)) => { + // normal alloc + eprint!(" ({}, ", kind); + write_allocation_track_relocs(self.tcx, &mut allocs_to_print, alloc); } - Err(()) => { - // static alloc? - match self.tcx.alloc_map.lock().get(id) { + None => { + // global alloc + match self.tcx.get_global_alloc(id) { Some(GlobalAlloc::Memory(alloc)) => { - self.dump_alloc_helper( - &mut allocs_seen, - &mut allocs_to_print, - msg, - alloc, - " (immutable)".to_owned(), - ); + eprint!(" (unchanged global, "); + write_allocation_track_relocs(self.tcx, &mut allocs_to_print, alloc); } Some(GlobalAlloc::Function(func)) => { - eprintln!("{} {}", msg, func); + eprint!(" (fn: {})", func); } Some(GlobalAlloc::Static(did)) => { - eprintln!("{} {:?}", msg, did); + eprint!(" (static: {})", self.tcx.def_path_str(did)); } None => { - eprintln!("{} (deallocated)", msg); + eprint!(" (deallocated)"); } } } - }; + } + eprintln!(); } } pub fn leak_report(&self) -> usize { - let leaks: Vec<_> = self - .alloc_map - .filter_map_collect(|&id, &(kind, _)| if kind.may_leak() { None } else { Some(id) }); + // Collect the set of allocations that are *reachable* from `Global` allocations. + let reachable = { + let mut reachable = FxHashSet::default(); + let global_kind = M::GLOBAL_KIND.map(MemoryKind::Machine); + let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| { + if Some(kind) == global_kind { Some(id) } else { None } + }); + while let Some(id) = todo.pop() { + if reachable.insert(id) { + // This is a new allocation, add its relocations to `todo`. + if let Some((_, alloc)) = self.alloc_map.get(id) { + todo.extend(alloc.relocations().values().map(|&(_, target_id)| target_id)); + } + } + } + reachable + }; + + // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking. + let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| { + if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) } + }); let n = leaks.len(); if n > 0 { - eprintln!("### LEAK REPORT ###"); + eprintln!("The following memory was leaked:"); self.dump_allocs(leaks); } n @@ -828,17 +807,57 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ptr: Scalar, src: impl IntoIterator, ) -> InterpResult<'tcx> { - let src = src.into_iter(); - let size = Size::from_bytes(src.size_hint().0 as u64); - // `write_bytes` checks that this lower bound matches the upper bound matches reality. + let mut src = src.into_iter(); + let size = Size::from_bytes(src.size_hint().0); + // `write_bytes` checks that this lower bound `size` matches the upper bound and reality. let ptr = match self.check_ptr_access(ptr, size, Align::from_bytes(1).unwrap())? { Some(ptr) => ptr, - None => return Ok(()), // zero-sized access + None => { + // zero-sized access + src.next().expect_none("iterator said it was empty but returned an element"); + return Ok(()); + } }; let tcx = self.tcx.tcx; self.get_raw_mut(ptr.alloc_id)?.write_bytes(&tcx, ptr, src) } + /// Writes the given stream of u16s into memory. + /// + /// Performs appropriate bounds checks. + pub fn write_u16s( + &mut self, + ptr: Scalar, + src: impl IntoIterator, + ) -> InterpResult<'tcx> { + let mut src = src.into_iter(); + let (lower, upper) = src.size_hint(); + let len = upper.expect("can only write bounded iterators"); + assert_eq!(lower, len, "can only write iterators with a precise length"); + + let size = Size::from_bytes(lower); + let ptr = match self.check_ptr_access(ptr, size, Align::from_bytes(2).unwrap())? { + Some(ptr) => ptr, + None => { + // zero-sized access + src.next().expect_none("iterator said it was empty but returned an element"); + return Ok(()); + } + }; + let tcx = self.tcx.tcx; + let allocation = self.get_raw_mut(ptr.alloc_id)?; + + for idx in 0..len { + let val = Scalar::from_u16( + src.next().expect("iterator was shorter than it said it would be"), + ); + let offset_ptr = ptr.offset(Size::from_bytes(idx) * 2, &tcx)?; // `Size` multiplication + allocation.write_scalar(&tcx, offset_ptr, val.into(), Size::from_bytes(2))?; + } + src.next().expect_none("iterator was longer than it said it would be"); + Ok(()) + } + /// Expects the caller to have checked bounds and alignment. pub fn copy( &mut self, @@ -869,14 +888,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let tcx = self.tcx.tcx; - // The bits have to be saved locally before writing to dest in case src and dest overlap. - assert_eq!(size.bytes() as usize as u64, size.bytes()); - // This checks relocation edges on the src. let src_bytes = self.get_raw(src.alloc_id)?.get_bytes_with_undef_and_ptr(&tcx, src, size)?.as_ptr(); let dest_bytes = - self.get_raw_mut(dest.alloc_id)?.get_bytes_mut(&tcx, dest, size * length)?; + self.get_raw_mut(dest.alloc_id)?.get_bytes_mut(&tcx, dest, size * length)?; // `Size` multiplication // If `dest_bytes` is empty we just optimize to not run anything for zsts. // See #67539 @@ -897,7 +913,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // touched if the bytes stay undef for the whole interpreter execution. On contemporary // operating system this can avoid physically allocating the page. let dest_alloc = self.get_raw_mut(dest.alloc_id)?; - dest_alloc.mark_definedness(dest, size * length, false); + dest_alloc.mark_definedness(dest, size * length, false); // `Size` multiplication dest_alloc.mark_relocation_range(relocations); return Ok(()); } @@ -908,9 +924,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // The pointers above remain valid even if the `HashMap` table is moved around because they // point into the `Vec` storing the bytes. unsafe { - assert_eq!(size.bytes() as usize as u64, size.bytes()); if src.alloc_id == dest.alloc_id { if nonoverlapping { + // `Size` additions if (src.offset <= dest.offset && src.offset + size > dest.offset) || (dest.offset <= src.offset && dest.offset + size > src.offset) { @@ -921,16 +937,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { for i in 0..length { ptr::copy( src_bytes, - dest_bytes.offset((size.bytes() * i) as isize), - size.bytes() as usize, + dest_bytes.add((size * i).bytes_usize()), // `Size` multiplication + size.bytes_usize(), ); } } else { for i in 0..length { ptr::copy_nonoverlapping( src_bytes, - dest_bytes.offset((size.bytes() * i) as isize), - size.bytes() as usize, + dest_bytes.add((size * i).bytes_usize()), // `Size` multiplication + size.bytes_usize(), ); } } @@ -970,7 +986,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ) -> InterpResult<'tcx, u128> { match scalar.to_bits_or_ptr(size, self) { Ok(bits) => Ok(bits), - Err(ptr) => Ok(M::ptr_to_int(&self, ptr)? as u128), + Err(ptr) => Ok(M::ptr_to_int(&self, ptr)?.into()), } } } diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 3063a99886b7b..d46010d98a5aa 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -15,22 +15,16 @@ mod traits; mod validity; mod visitor; -pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here +pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here pub use self::eval_context::{Frame, InterpCx, LocalState, LocalValue, StackPopCleanup}; - -pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; - +pub use self::intern::{intern_const_alloc_recursive, InternKind}; +pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; pub use self::memory::{AllocCheck, FnVal, Memory, MemoryKind}; - -pub use self::machine::{AllocMap, Machine, MayLeak, StackPopJump}; - -pub use self::operand::{ImmTy, Immediate, OpTy, Operand, ScalarMaybeUndef}; - -pub use self::visitor::{MutValueVisitor, ValueVisitor}; - +pub use self::operand::{ImmTy, Immediate, OpTy, Operand}; +pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; pub use self::validity::RefTracking; - -pub use self::intern::{intern_const_alloc_recursive, InternKind}; +pub use self::visitor::{MutValueVisitor, ValueVisitor}; crate use self::intrinsics::eval_nullary_intrinsic; +use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 4c82172ae4517..a3caa2048a1e7 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -1,22 +1,23 @@ //! Functions concerning immediate values and operands, and reading from operands. //! All high-level functions to read from memory work on operands as sources. -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; +use std::fmt::Write; -use super::{InterpCx, MPlaceTy, Machine, MemPlace, Place, PlaceTy}; -pub use rustc::mir::interpret::ScalarMaybeUndef; -use rustc::mir::interpret::{ - sign_extend, truncate, AllocId, ConstValue, GlobalId, InterpResult, Pointer, Scalar, -}; -use rustc::ty::layout::{ - self, HasDataLayout, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx, -}; -use rustc::ty::print::{FmtPrinter, PrettyPrinter, Printer}; -use rustc::ty::Ty; -use rustc::{mir, ty}; +use rustc_errors::ErrorReported; use rustc_hir::def::Namespace; use rustc_macros::HashStable; -use std::fmt::Write; +use rustc_middle::ty::layout::{IntegerExt, PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; +use rustc_middle::ty::Ty; +use rustc_middle::{mir, ty}; +use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, Integer, LayoutOf, Size}; +use rustc_target::abi::{VariantIdx, Variants}; + +use super::{ + from_known_layout, sign_extend, truncate, ConstValue, GlobalId, InterpCx, InterpResult, + MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, +}; /// An `Immediate` represents a single immediate self-contained Rust value. /// @@ -26,14 +27,14 @@ use std::fmt::Write; /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// defined on `Immediate`, and do not have to work with a `Place`. #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] -pub enum Immediate { - Scalar(ScalarMaybeUndef), - ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef), +pub enum Immediate { + Scalar(ScalarMaybeUninit), + ScalarPair(ScalarMaybeUninit, ScalarMaybeUninit), } -impl From> for Immediate { +impl From> for Immediate { #[inline(always)] - fn from(val: ScalarMaybeUndef) -> Self { + fn from(val: ScalarMaybeUninit) -> Self { Immediate::Scalar(val) } } @@ -54,10 +55,7 @@ impl From> for Immediate { impl<'tcx, Tag> Immediate { pub fn new_slice(val: Scalar, len: u64, cx: &impl HasDataLayout) -> Self { - Immediate::ScalarPair( - val.into(), - Scalar::from_uint(len, cx.data_layout().pointer_size).into(), - ) + Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) } pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self { @@ -65,7 +63,7 @@ impl<'tcx, Tag> Immediate { } #[inline] - pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef { + pub fn to_scalar_or_undef(self) -> ScalarMaybeUninit { match self { Immediate::Scalar(val) => val, Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"), @@ -90,8 +88,8 @@ impl<'tcx, Tag> Immediate { // as input for binary and cast operations. #[derive(Copy, Clone, Debug)] pub struct ImmTy<'tcx, Tag = ()> { - pub(crate) imm: Immediate, - pub layout: TyLayout<'tcx>, + imm: Immediate, + pub layout: TyAndLayout<'tcx>, } impl std::fmt::Display for ImmTy<'tcx, Tag> { @@ -99,14 +97,14 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { /// Helper function for printing a scalar to a FmtPrinter fn p<'a, 'tcx, F: std::fmt::Write, Tag>( cx: FmtPrinter<'a, 'tcx, F>, - s: ScalarMaybeUndef, + s: ScalarMaybeUninit, ty: Ty<'tcx>, ) -> Result, std::fmt::Error> { match s { - ScalarMaybeUndef::Scalar(s) => { + ScalarMaybeUninit::Scalar(s) => { cx.pretty_print_const_scalar(s.erase_tag(), ty, true) } - ScalarMaybeUndef::Undef => cx.typed_value( + ScalarMaybeUninit::Uninit => cx.typed_value( |mut this| { this.write_str("{undef ")?; Ok(this) @@ -124,11 +122,11 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { p(cx, s, ty)?; return Ok(()); } - write!(f, "{:?}: {}", s.erase_tag(), self.layout.ty) + write!(f, "{}: {}", s.erase_tag(), self.layout.ty) } Immediate::ScalarPair(a, b) => { // FIXME(oli-obk): at least print tuples and slices nicely - write!(f, "({:?}, {:?}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,) + write!(f, "({}, {}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,) } } }) @@ -147,15 +145,15 @@ impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> { /// or still in memory. The latter is an optimization, to delay reading that chunk of /// memory and to avoid having to store arbitrary-sized data here. #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] -pub enum Operand { - Immediate(Immediate), - Indirect(MemPlace), +pub enum Operand { + Immediate(Immediate), + Indirect(MemPlace), } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct OpTy<'tcx, Tag = ()> { op: Operand, // Keep this private; it helps enforce invariants. - pub layout: TyLayout<'tcx>, + pub layout: TyAndLayout<'tcx>, } impl<'tcx, Tag> ::std::ops::Deref for OpTy<'tcx, Tag> { @@ -182,54 +180,36 @@ impl<'tcx, Tag> From> for OpTy<'tcx, Tag> { impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { #[inline] - pub fn from_scalar(val: Scalar, layout: TyLayout<'tcx>) -> Self { + pub fn from_scalar(val: Scalar, layout: TyAndLayout<'tcx>) -> Self { ImmTy { imm: val.into(), layout } } #[inline] - pub fn try_from_uint(i: impl Into, layout: TyLayout<'tcx>) -> Option { + pub fn from_immediate(imm: Immediate, layout: TyAndLayout<'tcx>) -> Self { + ImmTy { imm, layout } + } + + #[inline] + pub fn try_from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) } #[inline] - pub fn from_uint(i: impl Into, layout: TyLayout<'tcx>) -> Self { + pub fn from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_uint(i, layout.size), layout) } #[inline] - pub fn try_from_int(i: impl Into, layout: TyLayout<'tcx>) -> Option { + pub fn try_from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout)) } #[inline] - pub fn from_int(i: impl Into, layout: TyLayout<'tcx>) -> Self { + pub fn from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_int(i, layout.size), layout) } } -// Use the existing layout if given (but sanity check in debug mode), -// or compute the layout. -#[inline(always)] -pub(super) fn from_known_layout<'tcx>( - layout: Option>, - compute: impl FnOnce() -> InterpResult<'tcx, TyLayout<'tcx>>, -) -> InterpResult<'tcx, TyLayout<'tcx>> { - match layout { - None => compute(), - Some(layout) => { - if cfg!(debug_assertions) { - let layout2 = compute()?; - assert_eq!( - layout.details, layout2.details, - "mismatch in layout of supposedly equal-layout types {:?} and {:?}", - layout.ty, layout2.ty - ); - } - Ok(layout) - } - } -} - -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST. /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. #[inline] @@ -260,6 +240,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { { Some(ptr) => ptr, None => { + if let Scalar::Ptr(ptr) = mplace.ptr { + // We may be reading from a static. + // In order to ensure that `static FOO: Type = FOO;` causes a cycle error + // instead of magically pulling *any* ZST value from the ether, we need to + // actually access the referenced allocation. + self.memory.get_raw(ptr.alloc_id)?; + } return Ok(Some(ImmTy { // zero-sized type imm: Scalar::zst().into(), @@ -268,16 +255,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } }; + let alloc = self.memory.get_raw(ptr.alloc_id)?; + match mplace.layout.abi { - layout::Abi::Scalar(..) => { - let scalar = self.memory.get_raw(ptr.alloc_id)?.read_scalar( - self, - ptr, - mplace.layout.size, - )?; + Abi::Scalar(..) => { + let scalar = alloc.read_scalar(self, ptr, mplace.layout.size)?; Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })) } - layout::Abi::ScalarPair(ref a, ref b) => { + Abi::ScalarPair(ref a, ref b) => { // We checked `ptr_align` above, so all fields will have the alignment they need. // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. @@ -287,8 +272,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let b_offset = a_size.align_to(b.align(self).abi); assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields let b_ptr = ptr.offset(b_offset, self)?; - let a_val = self.memory.get_raw(ptr.alloc_id)?.read_scalar(self, a_ptr, a_size)?; - let b_val = self.memory.get_raw(ptr.alloc_id)?.read_scalar(self, b_ptr, b_size)?; + let a_val = alloc.read_scalar(self, a_ptr, a_size)?; + let b_val = alloc.read_scalar(self, b_ptr, b_size)?; Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })) } _ => Ok(None), @@ -334,16 +319,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn read_scalar( &self, op: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ScalarMaybeUndef> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { Ok(self.read_immediate(op)?.to_scalar_or_undef()) } // Turn the wide MPlace into a string (must already be dereferenced!) pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; - let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?; - let str = ::std::str::from_utf8(bytes) - .map_err(|err| err_ub_format!("this string is not valid UTF-8: {}", err))?; + let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?; + let str = ::std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; Ok(str) } @@ -351,7 +335,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn operand_field( &self, op: OpTy<'tcx, M::PointerTag>, - field: u64, + field: usize, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let base = match op.try_as_mplace(self) { Ok(mplace) => { @@ -362,7 +346,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Err(value) => value, }; - let field = field.try_into().unwrap(); let field_layout = op.layout.field(self, field)?; if field_layout.is_zst() { let immediate = Scalar::zst().into(); @@ -384,6 +367,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }) } + pub fn operand_index( + &self, + op: OpTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + if let Ok(index) = usize::try_from(index) { + // We can just treat this as a field. + self.operand_field(op, index) + } else { + // Indexing into a big array. This must be an mplace. + let mplace = op.assert_mem_place(self); + Ok(self.mplace_index(mplace, index)?.into()) + } + } + pub fn operand_downcast( &self, op: OpTy<'tcx, M::PointerTag>, @@ -404,9 +402,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { base: OpTy<'tcx, M::PointerTag>, proj_elem: &mir::PlaceElem<'tcx>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc::mir::ProjectionElem::*; + use rustc_middle::mir::ProjectionElem::*; Ok(match *proj_elem { - Field(field, _) => self.operand_field(base, field.index() as u64)?, + Field(field, _) => self.operand_field(base, field.index())?, Downcast(_, variant) => self.operand_downcast(base, variant)?, Deref => self.deref_operand(base)?.into(), Subslice { .. } | ConstantIndex { .. } | Index(_) => { @@ -423,9 +421,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, frame: &super::Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, local: mir::Local, - layout: Option>, + layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - assert_ne!(local, mir::RETURN_PLACE); let layout = self.layout_of_local(frame, local, layout)?; let op = if layout.is_zst() { // Do not read from ZST, they might not be initialized @@ -436,7 +433,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(OpTy { op, layout }) } - /// Every place can be read from, so we can turn them into an operand + /// Every place can be read from, so we can turn them into an operand. + /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this + /// will never actually read from memory. #[inline(always)] pub fn place_to_op( &self, @@ -444,7 +443,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let op = match *place { Place::Ptr(mplace) => Operand::Indirect(mplace), - Place::Local { frame, local } => *self.access_local(&self.stack[frame], local, None)?, + Place::Local { frame, local } => { + *self.access_local(&self.stack()[frame], local, None)? + } }; Ok(OpTy { op, layout: place.layout }) } @@ -453,19 +454,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // avoid allocations. pub fn eval_place_to_op( &self, - place: &mir::Place<'tcx>, - layout: Option>, + place: mir::Place<'tcx>, + layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let base_op = match place.local { - mir::RETURN_PLACE => throw_ub!(ReadFromReturnPlace), - local => { - // Do not use the layout passed in as argument if the base we are looking at - // here is not the entire place. - let layout = if place.projection.is_empty() { layout } else { None }; - - self.access_local(self.frame(), local, layout)? - } - }; + // Do not use the layout passed in as argument if the base we are looking at + // here is not the entire place. + let layout = if place.projection.is_empty() { layout } else { None }; + + let base_op = self.access_local(self.frame(), place.local, layout)?; let op = place .projection @@ -482,12 +478,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn eval_operand( &self, mir_op: &mir::Operand<'tcx>, - layout: Option>, + layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc::mir::Operand::*; + use rustc_middle::mir::Operand::*; let op = match *mir_op { // FIXME: do some more logic on `move` to invalidate the old location - Copy(ref place) | Move(ref place) => self.eval_place_to_op(place, layout)?, + Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?, Constant(ref constant) => { let val = @@ -514,15 +510,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { crate fn eval_const_to_op( &self, val: &ty::Const<'tcx>, - layout: Option>, + layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let tag_scalar = |scalar| match scalar { - Scalar::Ptr(ptr) => Scalar::Ptr(self.tag_static_base_pointer(ptr)), + Scalar::Ptr(ptr) => Scalar::Ptr(self.tag_global_base_pointer(ptr)), Scalar::Raw { data, size } => Scalar::Raw { data, size }, }; // Early-return cases. let val_val = match val.val { ty::ConstKind::Param(_) => throw_inval!(TooGeneric), + ty::ConstKind::Error => throw_inval!(TypeckError(ErrorReported)), ty::ConstKind::Unevaluated(def_id, substs, promoted) => { let instance = self.resolve(def_id, substs)?; // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation. @@ -531,6 +528,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // potentially requiring the current static to be evaluated again. This is not a // problem here, because we are building an operand which means an actual read is // happening. + // + // The machine callback `adjust_global_const` below is guaranteed to + // be called for all constants because `const_eval` calls + // `eval_const_to_op` recursively. return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?); } ty::ConstKind::Infer(..) @@ -540,14 +541,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } ty::ConstKind::Value(val_val) => val_val, }; + // This call allows the machine to create fresh allocation ids for + // thread-local statics (see the `adjust_global_const` function + // documentation). + let val_val = M::adjust_global_const(self, val_val)?; // Other cases need layout. - let layout = from_known_layout(layout, || self.layout_of(val.ty))?; + let layout = from_known_layout(self.tcx, layout, || self.layout_of(val.ty))?; let op = match val_val { ConstValue::ByRef { alloc, offset } => { - let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc); + let id = self.tcx.create_memory_alloc(alloc); // We rely on mutability being set correctly in that allocation to prevent writes // where none should happen. - let ptr = self.tag_static_base_pointer(Pointer::new(id, offset)); + let ptr = self.tag_global_base_pointer(Pointer::new(id, offset)); Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi)) } ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x).into()), @@ -555,12 +560,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We rely on mutability being set correctly in `data` to prevent writes // where none should happen. let ptr = Pointer::new( - self.tcx.alloc_map.lock().create_memory_alloc(data), - Size::from_bytes(start as u64), // offset: `start` + self.tcx.create_memory_alloc(data), + Size::from_bytes(start), // offset: `start` ); Operand::Immediate(Immediate::new_slice( - self.tag_static_base_pointer(ptr).into(), - (end - start) as u64, // len: `end - start` + self.tag_global_base_pointer(ptr).into(), + u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start` self, )) } @@ -576,30 +581,27 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("read_discriminant_value {:#?}", rval.layout); let (discr_layout, discr_kind, discr_index) = match rval.layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { let discr_val = rval .layout .ty .discriminant_for_variant(*self.tcx, index) - .map_or(index.as_u32() as u128, |discr| discr.val); + .map_or(u128::from(index.as_u32()), |discr| discr.val); return Ok((discr_val, index)); } - layout::Variants::Multiple { - discr: ref discr_layout, - ref discr_kind, - discr_index, - .. - } => (discr_layout, discr_kind, discr_index), + Variants::Multiple { discr: ref discr_layout, ref discr_kind, discr_index, .. } => { + (discr_layout, discr_kind, discr_index) + } }; // read raw discriminant value - let discr_op = self.operand_field(rval, discr_index as u64)?; + let discr_op = self.operand_field(rval, discr_index)?; let discr_val = self.read_immediate(discr_op)?; let raw_discr = discr_val.to_scalar_or_undef(); trace!("discr value: {:?}", raw_discr); // post-process Ok(match *discr_kind { - layout::DiscriminantKind::Tag => { + DiscriminantKind::Tag => { let bits_discr = raw_discr .not_undef() .and_then(|raw_discr| self.force_bits(raw_discr, discr_val.layout.size)) @@ -607,7 +609,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let real_discr = if discr_val.layout.abi.is_signed() { // going from layout tag type to typeck discriminant type // requires first sign extending with the discriminant layout - let sexted = sign_extend(bits_discr, discr_val.layout.size) as i128; + let sexted = sign_extend(bits_discr, discr_val.layout.size); // and then zeroing with the typeck discriminant type let discr_ty = rval .layout @@ -616,9 +618,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .expect("tagged layout corresponds to adt") .repr .discr_type(); - let size = layout::Integer::from_attr(self, discr_ty).size(); - let truncatee = sexted as u128; - truncate(truncatee, size) + let size = Integer::from_attr(self, discr_ty).size(); + truncate(sexted, size) } else { bits_discr }; @@ -638,16 +639,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .ok_or_else(|| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?; (real_discr, index.0) } - layout::DiscriminantKind::Niche { - dataful_variant, - ref niche_variants, - niche_start, - } => { + DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start } => { let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); let raw_discr = raw_discr .not_undef() - .map_err(|_| err_ub!(InvalidDiscriminant(ScalarMaybeUndef::Undef)))?; + .map_err(|_| err_ub!(InvalidDiscriminant(ScalarMaybeUninit::Uninit)))?; match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) { Err(ptr) => { // The niche must be just 0 (which an inbounds pointer value never is) @@ -657,7 +654,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if !ptr_valid { throw_ub!(InvalidDiscriminant(raw_discr.erase_tag().into())) } - (dataful_variant.as_u32() as u128, dataful_variant) + (u128::from(dataful_variant.as_u32()), dataful_variant) } Ok(raw_discr) => { // We need to use machine arithmetic to get the relative variant idx: @@ -686,7 +683,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .expect("tagged layout for non adt") .variants .len(); - assert!((variant_index as usize) < variants_len); + assert!(usize::try_from(variant_index).unwrap() < variants_len); (u128::from(variant_index), VariantIdx::from_u32(variant_index)) } else { (u128::from(dataful_variant.as_u32()), dataful_variant) diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 76a5aecb9db62..d651267f82b79 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -1,16 +1,15 @@ -use rustc::mir; -use rustc::mir::interpret::{InterpResult, Scalar}; -use rustc::ty::{ - self, - layout::{LayoutOf, TyLayout}, - Ty, -}; +use std::convert::TryFrom; + use rustc_apfloat::Float; use rustc_ast::ast::FloatTy; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{InterpResult, Scalar}; +use rustc_middle::ty::{self, layout::TyAndLayout, Ty}; +use rustc_target::abi::LayoutOf; use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}; -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Applies the binary operation `op` to the two operands and writes a tuple of the result /// and a boolean signifying the potential overflow to the destination. pub fn binop_with_overflow( @@ -46,14 +45,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn binary_char_op( &self, bin_op: mir::BinOp, l: char, r: char, ) -> (Scalar, bool, Ty<'tcx>) { - use rustc::mir::BinOp::*; + use rustc_middle::mir::BinOp::*; let res = match bin_op { Eq => l == r, @@ -73,7 +72,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { l: bool, r: bool, ) -> (Scalar, bool, Ty<'tcx>) { - use rustc::mir::BinOp::*; + use rustc_middle::mir::BinOp::*; let res = match bin_op { Eq => l == r, @@ -97,7 +96,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { l: F, r: F, ) -> (Scalar, bool, Ty<'tcx>) { - use rustc::mir::BinOp::*; + use rustc_middle::mir::BinOp::*; let (val, ty) = match bin_op { Eq => (Scalar::from_bool(l == r), self.tcx.types.bool), @@ -121,37 +120,36 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bin_op: mir::BinOp, // passing in raw bits l: u128, - left_layout: TyLayout<'tcx>, + left_layout: TyAndLayout<'tcx>, r: u128, - right_layout: TyLayout<'tcx>, + right_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { - use rustc::mir::BinOp::*; + use rustc_middle::mir::BinOp::*; // Shift ops can have an RHS with a different numeric type. if bin_op == Shl || bin_op == Shr { let signed = left_layout.abi.is_signed(); - let mut oflo = (r as u32 as u128) != r; - let mut r = r as u32; - let size = left_layout.size; - oflo |= r >= size.bits() as u32; - r %= size.bits() as u32; + let size = u128::from(left_layout.size.bits()); + let overflow = r >= size; + let r = r % size; // mask to type size + let r = u32::try_from(r).unwrap(); // we masked so this will always fit let result = if signed { let l = self.sign_extend(l, left_layout) as i128; let result = match bin_op { - Shl => l << r, - Shr => l >> r, + Shl => l.checked_shl(r).unwrap(), + Shr => l.checked_shr(r).unwrap(), _ => bug!("it has already been checked that this is a shift op"), }; result as u128 } else { match bin_op { - Shl => l << r, - Shr => l >> r, + Shl => l.checked_shl(r).unwrap(), + Shr => l.checked_shr(r).unwrap(), _ => bug!("it has already been checked that this is a shift op"), } }; let truncated = self.truncate(result, left_layout); - return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty)); + return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty)); } // For the remaining ops, the types must be the same on both sides @@ -193,21 +191,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => None, }; if let Some(op) = op { - let l128 = self.sign_extend(l, left_layout) as i128; let r = self.sign_extend(r, right_layout) as i128; // We need a special check for overflowing remainder: // "int_min % -1" overflows and returns 0, but after casting things to a larger int // type it does *not* overflow nor give an unrepresentable result! - match bin_op { - Rem => { - if r == -1 && l == (1 << (size.bits() - 1)) { - return Ok((Scalar::from_int(0, size), true, left_layout.ty)); - } + if bin_op == Rem { + if r == -1 && l == (1 << (size.bits() - 1)) { + return Ok((Scalar::from_int(0, size), true, left_layout.ty)); } - _ => {} } + let l = self.sign_extend(l, left_layout) as i128; - let (result, oflo) = op(l128, r); + let (result, oflo) = op(l, r); // This may be out-of-bounds for the result type, so we have to truncate ourselves. // If that truncation loses any information, we have an overflow. let result = result as u128; @@ -361,7 +356,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { un_op: mir::UnOp, val: ImmTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { - use rustc::mir::UnOp::*; + use rustc_middle::mir::UnOp::*; let layout = val.layout; let val = val.to_scalar()?; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 027e33abc7bb1..6dadb8e4c67f4 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -5,36 +5,35 @@ use std::convert::TryFrom; use std::hash::Hash; -use rustc::mir; -use rustc::mir::interpret::truncate; -use rustc::ty::layout::{ - self, Align, HasDataLayout, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx, -}; -use rustc::ty::{self, Ty}; use rustc_macros::HashStable; +use rustc_middle::mir; +use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::{self, Ty}; +use rustc_target::abi::{Abi, Align, DiscriminantKind, FieldsShape}; +use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants}; use super::{ - AllocId, AllocMap, Allocation, AllocationExtra, ImmTy, Immediate, InterpCx, InterpResult, - LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, PointerArithmetic, RawConst, Scalar, - ScalarMaybeUndef, + mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ImmTy, + Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, + PointerArithmetic, RawConst, Scalar, ScalarMaybeUninit, }; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] /// Information required for the sound usage of a `MemPlace`. -pub enum MemPlaceMeta { +pub enum MemPlaceMeta { /// The unsized payload (e.g. length for slices or vtable pointer for trait objects). - Meta(Scalar), + Meta(Scalar), /// `Sized` types or unsized `extern type` None, /// The address of this place may not be taken. This protects the `MemPlace` from coming from - /// a ZST Operand with a backing allocation and being converted to an integer address. This + /// a ZST Operand without a backing allocation and being converted to an integer address. This /// should be impossible, because you can't take the address of an operand, but this is a second /// protection layer ensuring that we don't mess up. Poison, } -impl MemPlaceMeta { - pub fn unwrap_meta(self) -> Scalar { +impl MemPlaceMeta { + pub fn unwrap_meta(self) -> Scalar { match self { Self::Meta(s) => s, Self::None | Self::Poison => { @@ -48,9 +47,7 @@ impl MemPlaceMeta { Self::None | Self::Poison => false, } } -} -impl MemPlaceMeta { pub fn erase_tag(self) -> MemPlaceMeta<()> { match self { Self::Meta(s) => MemPlaceMeta::Meta(s.erase_tag()), @@ -61,22 +58,22 @@ impl MemPlaceMeta { } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] -pub struct MemPlace { +pub struct MemPlace { /// A place may have an integral pointer for ZSTs, and since it might /// be turned back into a reference before ever being dereferenced. /// However, it may never be undef. - pub ptr: Scalar, + pub ptr: Scalar, pub align: Align, /// Metadata for unsized places. Interpretation is up to the type. /// Must not be present for sized types, but can be missing for unsized types /// (e.g., `extern type`). - pub meta: MemPlaceMeta, + pub meta: MemPlaceMeta, } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] -pub enum Place { +pub enum Place { /// A place referring to a value allocated in the `Memory` system. - Ptr(MemPlace), + Ptr(MemPlace), /// To support alloc-free locals, we are able to write directly to a local. /// (Without that optimization, we'd just always be a `MemPlace`.) @@ -86,7 +83,7 @@ pub enum Place { #[derive(Copy, Clone, Debug)] pub struct PlaceTy<'tcx, Tag = ()> { place: Place, // Keep this private; it helps enforce invariants. - pub layout: TyLayout<'tcx>, + pub layout: TyAndLayout<'tcx>, } impl<'tcx, Tag> ::std::ops::Deref for PlaceTy<'tcx, Tag> { @@ -101,7 +98,7 @@ impl<'tcx, Tag> ::std::ops::Deref for PlaceTy<'tcx, Tag> { #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub struct MPlaceTy<'tcx, Tag = ()> { mplace: MemPlace, - pub layout: TyLayout<'tcx>, + pub layout: TyAndLayout<'tcx>, } impl<'tcx, Tag> ::std::ops::Deref for MPlaceTy<'tcx, Tag> { @@ -136,12 +133,6 @@ impl MemPlace { MemPlace { ptr, align, meta: MemPlaceMeta::None } } - /// Produces a Place that will error if attempted to be read from or written to - #[inline(always)] - fn null(cx: &impl HasDataLayout) -> Self { - Self::from_scalar_ptr(Scalar::ptr_null(cx), Align::from_bytes(1).unwrap()) - } - #[inline(always)] pub fn from_ptr(ptr: Pointer, align: Align) -> Self { Self::from_scalar_ptr(ptr.into(), align) @@ -178,9 +169,9 @@ impl MemPlace { impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { /// Produces a MemPlace that works for ZST but nothing else #[inline] - pub fn dangling(layout: TyLayout<'tcx>, cx: &impl HasDataLayout) -> Self { + pub fn dangling(layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { let align = layout.align.abi; - let ptr = Scalar::from_uint(align.bytes(), cx.pointer_size()); + let ptr = Scalar::from_machine_usize(align.bytes(), cx); // `Poison` this to make sure that the pointer value `ptr` is never observable by the program. MPlaceTy { mplace: MemPlace { ptr, align, meta: MemPlaceMeta::Poison }, layout } } @@ -196,14 +187,14 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { self, offset: Size, meta: MemPlaceMeta, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { Ok(MPlaceTy { mplace: self.mplace.offset(offset, meta, cx)?, layout }) } #[inline] - fn from_aligned_ptr(ptr: Pointer, layout: TyLayout<'tcx>) -> Self { + fn from_aligned_ptr(ptr: Pointer, layout: TyAndLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } } @@ -219,7 +210,7 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { // Go through the layout. There are lots of types that support a length, // e.g., SIMD types. match self.layout.fields { - layout::FieldPlacement::Array { count, .. } => Ok(count), + FieldsShape::Array { count, .. } => Ok(count), _ => bug!("len not supported on sized type {:?}", self.layout.ty), } } @@ -248,7 +239,7 @@ impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { Operand::Immediate(_) if self.layout.is_zst() => { Ok(MPlaceTy::dangling(self.layout, cx)) } - Operand::Immediate(imm) => Err(ImmTy { imm, layout: self.layout }), + Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)), } } @@ -261,12 +252,6 @@ impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { } impl Place { - /// Produces a Place that will error if attempted to be read from or written to - #[inline(always)] - fn null(cx: &impl HasDataLayout) -> Self { - Place::Ptr(MemPlace::null(cx)) - } - #[inline] pub fn assert_mem_place(self) -> MemPlace { match self { @@ -284,13 +269,13 @@ impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> { } // separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385 -impl<'mir, 'tcx, Tag, M> InterpCx<'mir, 'tcx, M> +impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> where // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static, M: Machine<'mir, 'tcx, PointerTag = Tag>, // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 - M::MemoryMap: AllocMap, Allocation)>, + M::MemoryMap: AllocMap, Allocation)>, M::AllocExtra: AllocationExtra, { /// Take a value, which represents a (thin or wide) reference, and make it a place. @@ -334,7 +319,7 @@ where let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); let place = self.ref_to_mplace(val)?; - self.mplace_access_checked(place) + self.mplace_access_checked(place, None) } /// Check if the given place is good for memory access with the given @@ -359,15 +344,20 @@ where /// Return the "access-checked" version of this `MPlace`, where for non-ZST /// this is definitely a `Pointer`. + /// + /// `force_align` must only be used when correct alignment does not matter, + /// like in Stacked Borrows. pub fn mplace_access_checked( &self, mut place: MPlaceTy<'tcx, M::PointerTag>, + force_align: Option, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let (size, align) = self .size_and_align_of_mplace(place)? .unwrap_or((place.layout.size, place.layout.align.abi)); assert!(place.mplace.align <= align, "dynamic alignment less strict than static one?"); - place.mplace.align = align; // maximally strict checking + // Check (stricter) dynamic alignment, unless forced otherwise. + place.mplace.align = force_align.unwrap_or(align); // When dereferencing a pointer, it must be non-NULL, aligned, and live. if let Some(ptr) = self.check_mplace_access(place, Some(size))? { place.mplace.ptr = ptr.into(); @@ -385,43 +375,20 @@ where Ok(place) } - /// Offset a pointer to project to a field. Unlike `place_field`, this is always - /// possible without allocating, so it can take `&self`. Also return the field's layout. + /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is + /// always possible without allocating, so it can take `&self`. Also return the field's layout. /// This supports both struct and array fields. + /// + /// This also works for arrays, but then the `usize` index type is restricting. + /// For indexing into arrays, use `mplace_index`. #[inline(always)] pub fn mplace_field( &self, base: MPlaceTy<'tcx, M::PointerTag>, - field: u64, + field: usize, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // Not using the layout method because we want to compute on u64 - let offset = match base.layout.fields { - layout::FieldPlacement::Arbitrary { ref offsets, .. } => { - offsets[usize::try_from(field).unwrap()] - } - layout::FieldPlacement::Array { stride, .. } => { - let len = base.len(self)?; - if field >= len { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len, index: field }); - } - stride * field - } - layout::FieldPlacement::Union(count) => { - assert!( - field < count as u64, - "Tried to access field {} of union {:#?} with {} fields", - field, - base.layout, - count - ); - // Offset is always 0 - Size::from_bytes(0) - } - }; - // the only way conversion can fail if is this is an array (otherwise we already panicked - // above). In that case, all fields are equal. - let field_layout = base.layout.field(self, usize::try_from(field).unwrap_or(0))?; + let offset = base.layout.fields.offset(field); + let field_layout = base.layout.field(self, field)?; // Offset may need adjustment for unsized fields. let (meta, offset) = if field_layout.is_unsized() { @@ -451,6 +418,32 @@ where base.offset(offset, meta, field_layout, self) } + /// Index into an array. + #[inline(always)] + pub fn mplace_index( + &self, + base: MPlaceTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + // Not using the layout method because we want to compute on u64 + match base.layout.fields { + FieldsShape::Array { stride, .. } => { + let len = base.len(self)?; + if index >= len { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len, index }); + } + let offset = stride * index; // `Size` multiplication + // All fields have the same layout. + let field_layout = base.layout.field(self, 0)?; + + assert!(!field_layout.is_unsized()); + base.offset(offset, MemPlaceMeta::None, field_layout, self) + } + _ => bug!("`mplace_index` called on non-array type {:?}", base.layout.ty), + } + } + // Iterates over all fields of an array. Much more efficient than doing the // same by repeatedly calling `mplace_array`. pub(super) fn mplace_array_fields( @@ -460,12 +453,13 @@ where { let len = base.len(self)?; // also asserts that we have a type where this makes sense let stride = match base.layout.fields { - layout::FieldPlacement::Array { stride, .. } => stride, + FieldsShape::Array { stride, .. } => stride, _ => bug!("mplace_array_fields: expected an array layout"), }; let layout = base.layout.field(self, 0)?; let dl = &self.tcx.data_layout; - Ok((0..len).map(move |i| base.offset(i * stride, MemPlaceMeta::None, layout, dl))) + // `Size` multiplication + Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) } fn mplace_subslice( @@ -477,11 +471,11 @@ where ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let len = base.len(self)?; // also asserts that we have a type where this makes sense let actual_to = if from_end { - if from + to > len { + if from.checked_add(to).map_or(true, |to| to > len) { // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: len as u64, index: from as u64 + to as u64 }); + throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); } - len - to + len.checked_sub(to).unwrap() } else { to }; @@ -489,18 +483,18 @@ where // Not using layout method because that works with usize, and does not work with slices // (that have count 0 in their layout). let from_offset = match base.layout.fields { - layout::FieldPlacement::Array { stride, .. } => stride * from, + FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked _ => bug!("Unexpected layout of index access: {:#?}", base.layout), }; // Compute meta and new layout - let inner_len = actual_to - from; + let inner_len = actual_to.checked_sub(from).unwrap(); let (meta, ty) = match base.layout.ty.kind { // It is not nice to match on the type, but that seems to be the only way to // implement this. ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(inner, inner_len)), ty::Slice(..) => { - let len = Scalar::from_uint(inner_len, self.pointer_size()); + let len = Scalar::from_machine_usize(inner_len, self); (MemPlaceMeta::Meta(len), base.layout.ty) } _ => bug!("cannot subslice non-array type: `{:?}`", base.layout.ty), @@ -525,9 +519,9 @@ where base: MPlaceTy<'tcx, M::PointerTag>, proj_elem: &mir::PlaceElem<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - use rustc::mir::ProjectionElem::*; + use rustc_middle::mir::ProjectionElem::*; Ok(match *proj_elem { - Field(field, _) => self.mplace_field(base, field.index() as u64)?, + Field(field, _) => self.mplace_field(base, field.index())?, Downcast(_, variant) => self.mplace_downcast(base, variant)?, Deref => self.deref_operand(base.into())?, @@ -535,26 +529,29 @@ where let layout = self.layout_of(self.tcx.types.usize)?; let n = self.access_local(self.frame(), local, Some(layout))?; let n = self.read_scalar(n)?; - let n = self.force_bits(n.not_undef()?, self.tcx.data_layout.pointer_size)?; - self.mplace_field(base, u64::try_from(n).unwrap())? + let n = u64::try_from( + self.force_bits(n.not_undef()?, self.tcx.data_layout.pointer_size)?, + ) + .unwrap(); + self.mplace_index(base, n)? } ConstantIndex { offset, min_length, from_end } => { let n = base.len(self)?; - if n < min_length as u64 { + if n < u64::from(min_length) { // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: min_length as u64, index: n as u64 }); + throw_ub!(BoundsCheckFailed { len: min_length.into(), index: n }); } let index = if from_end { - assert!(0 < offset && offset - 1 < min_length); - n - u64::from(offset) + assert!(0 < offset && offset <= min_length); + n.checked_sub(u64::from(offset)).unwrap() } else { assert!(offset < min_length); u64::from(offset) }; - self.mplace_field(base, index)? + self.mplace_index(base, index)? } Subslice { from, to, from_end } => { @@ -570,7 +567,7 @@ where pub fn place_field( &mut self, base: PlaceTy<'tcx, M::PointerTag>, - field: u64, + field: usize, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { // FIXME: We could try to be smarter and avoid allocation for fields that span the // entire place. @@ -578,6 +575,15 @@ where Ok(self.mplace_field(mplace, field)?.into()) } + pub fn place_index( + &mut self, + base: PlaceTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + let mplace = self.force_allocation(base)?; + Ok(self.mplace_index(mplace, index)?.into()) + } + pub fn place_downcast( &self, base: PlaceTy<'tcx, M::PointerTag>, @@ -601,9 +607,9 @@ where base: PlaceTy<'tcx, M::PointerTag>, proj_elem: &mir::ProjectionElem>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - use rustc::mir::ProjectionElem::*; + use rustc_middle::mir::ProjectionElem::*; Ok(match *proj_elem { - Field(field, _) => self.place_field(base, field.index() as u64)?, + Field(field, _) => self.place_field(base, field.index())?, Downcast(_, variant) => self.place_downcast(base, variant)?, Deref => self.deref_operand(self.place_to_op(base)?)?.into(), // For the other variants, we have to force an allocation. @@ -619,37 +625,12 @@ where /// place; for reading, a more efficient alternative is `eval_place_for_read`. pub fn eval_place( &mut self, - place: &mir::Place<'tcx>, + place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mut place_ty = match place.local { - mir::RETURN_PLACE => { - // `return_place` has the *caller* layout, but we want to use our - // `layout to verify our assumption. The caller will validate - // their layout on return. - PlaceTy { - place: match self.frame().return_place { - Some(p) => *p, - // Even if we don't have a return place, we sometimes need to - // create this place, but any attempt to read from / write to it - // (even a ZST read/write) needs to error, so let us make this - // a NULL place. - // - // FIXME: Ideally we'd make sure that the place projections also - // bail out. - None => Place::null(&*self), - }, - layout: self.layout_of( - self.subst_from_current_frame_and_normalize_erasing_regions( - self.frame().body.return_ty(), - ), - )?, - } - } - local => PlaceTy { - // This works even for dead/uninitialized locals; we check further when writing - place: Place::Local { frame: self.cur_frame(), local }, - layout: self.layout_of_local(self.frame(), local, None)?, - }, + let mut place_ty = PlaceTy { + // This works even for dead/uninitialized locals; we check further when writing + place: Place::Local { frame: self.frame_idx(), local: place.local }, + layout: self.layout_of_local(self.frame(), place.local, None)?, }; for elem in place.projection.iter() { @@ -664,7 +645,7 @@ where #[inline(always)] pub fn write_scalar( &mut self, - val: impl Into>, + val: impl Into>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { self.write_immediate(Immediate::Scalar(val.into()), dest) @@ -716,19 +697,19 @@ where // This is a very common path, avoid some checks in release mode assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); match src { - Immediate::Scalar(ScalarMaybeUndef::Scalar(Scalar::Ptr(_))) => assert_eq!( + Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(_))) => assert_eq!( self.pointer_size(), dest.layout.size, "Size mismatch when writing pointer" ), - Immediate::Scalar(ScalarMaybeUndef::Scalar(Scalar::Raw { size, .. })) => { + Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Raw { size, .. })) => { assert_eq!( - Size::from_bytes(size.into()), + Size::from_bytes(size), dest.layout.size, "Size mismatch when writing bits" ) } - Immediate::Scalar(ScalarMaybeUndef::Undef) => {} // undef can have any size + Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // undef can have any size Immediate::ScalarPair(_, _) => { // FIXME: Can we check anything here? } @@ -740,7 +721,7 @@ where // but not factored as a separate function. let mplace = match dest.place { Place::Local { frame, local } => { - match self.stack[frame].locals[local].access_mut()? { + match self.stack_mut()[frame].locals[local].access_mut()? { Ok(local) => { // Local can be updated in-place. *local = LocalValue::Live(Operand::Immediate(src)); @@ -786,7 +767,7 @@ where match value { Immediate::Scalar(scalar) => { match dest.layout.abi { - layout::Abi::Scalar(_) => {} // fine + Abi::Scalar(_) => {} // fine _ => { bug!("write_immediate_to_mplace: invalid Scalar layout: {:#?}", dest.layout) } @@ -803,7 +784,7 @@ where // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. let (a, b) = match dest.layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value), + Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value), _ => bug!( "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", dest.layout @@ -852,12 +833,14 @@ where ) -> InterpResult<'tcx> { // We do NOT compare the types for equality, because well-typed code can // actually "transmute" `&mut T` to `&T` in an assignment without a cast. - assert!( - src.layout.details == dest.layout.details, - "Layout mismatch when copying!\nsrc: {:#?}\ndest: {:#?}", - src, - dest - ); + if !mir_assign_valid_types(self.tcx.tcx, src.layout, dest.layout) { + span_bug!( + self.tcx.span, + "type mismatch when copying!\nsrc: {:?},\ndest: {:?}", + src.layout.ty, + dest.layout.ty, + ); + } // Let us see if the layout is simple so we take a shortcut, avoid force_allocation. let src = match self.try_read_immediate(src)? { @@ -907,7 +890,7 @@ where src: OpTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - if src.layout.details == dest.layout.details { + if mir_assign_valid_types(self.tcx.tcx, src.layout, dest.layout) { // Fast path: Just use normal `copy_op` return self.copy_op(src, dest); } @@ -968,14 +951,15 @@ where ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option)> { let (mplace, size) = match place.place { Place::Local { frame, local } => { - match self.stack[frame].locals[local].access_mut()? { + match self.stack_mut()[frame].locals[local].access_mut()? { Ok(&mut local_val) => { // We need to make an allocation. // We need the layout of the local. We can NOT use the layout we got, // that might e.g., be an inner field of a struct with `Scalar` layout, // that has different alignment than the outer field. - let local_layout = self.layout_of_local(&self.stack[frame], local, None)?; + let local_layout = + self.layout_of_local(&self.stack()[frame], local, None)?; // We also need to support unsized types, and hence cannot use `allocate`. let (size, align) = self .size_and_align_of(meta, local_layout)? @@ -991,7 +975,7 @@ where } // Now we can call `access_mut` again, asserting it goes well, // and actually overwrite things. - *self.stack[frame].locals[local].access_mut().unwrap().unwrap() = + *self.stack_mut()[frame].locals[local].access_mut().unwrap().unwrap() = LocalValue::Live(Operand::Indirect(mplace)); (mplace, Some(size)) } @@ -1014,8 +998,8 @@ where pub fn allocate( &mut self, - layout: TyLayout<'tcx>, - kind: MemoryKind, + layout: TyAndLayout<'tcx>, + kind: MemoryKind, ) -> MPlaceTy<'tcx, M::PointerTag> { let ptr = self.memory.allocate(layout.size, layout.align.abi, kind); MPlaceTy::from_aligned_ptr(ptr, layout) @@ -1025,10 +1009,10 @@ where pub fn allocate_str( &mut self, str: &str, - kind: MemoryKind, + kind: MemoryKind, ) -> MPlaceTy<'tcx, M::PointerTag> { - let ptr = self.memory.allocate_static_bytes(str.as_bytes(), kind); - let meta = Scalar::from_uint(str.len() as u128, self.pointer_size()); + let ptr = self.memory.allocate_bytes(str.as_bytes(), kind); + let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self); let mplace = MemPlace { ptr: ptr.into(), align: Align::from_bytes(1).unwrap(), @@ -1051,17 +1035,17 @@ where } match dest.layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { assert_eq!(index, variant_index); } - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, + Variants::Multiple { + discr_kind: DiscriminantKind::Tag, discr: ref discr_layout, discr_index, .. } => { // No need to validate that the discriminant here because the - // `TyLayout::for_variant()` call earlier already checks the variant is valid. + // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. let discr_val = dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val; @@ -1072,18 +1056,18 @@ where let size = discr_layout.value.size(self); let discr_val = truncate(discr_val, size); - let discr_dest = self.place_field(dest, discr_index as u64)?; + let discr_dest = self.place_field(dest, discr_index)?; self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?; } - layout::Variants::Multiple { + Variants::Multiple { discr_kind: - layout::DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, + DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, discr: ref discr_layout, discr_index, .. } => { // No need to validate that the discriminant here because the - // `TyLayout::for_variant()` call earlier already checks the variant is valid. + // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. if variant_index != dataful_variant { let variants_start = niche_variants.start().as_u32(); @@ -1103,7 +1087,7 @@ where niche_start_val, )?; // Write result. - let niche_dest = self.place_field(dest, discr_index as u64)?; + let niche_dest = self.place_field(dest, discr_index)?; self.write_immediate(*discr_val, niche_dest)?; } } @@ -1117,8 +1101,8 @@ where raw: RawConst<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { // This must be an allocation in `tcx` - assert!(self.tcx.alloc_map.lock().get(raw.alloc_id).is_some()); - let ptr = self.tag_static_base_pointer(Pointer::from(raw.alloc_id)); + let _ = self.tcx.global_alloc(raw.alloc_id); + let ptr = self.tag_global_base_pointer(Pointer::from(raw.alloc_id)); let layout = self.layout_of(raw.ty)?; Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index cb11df18378d9..bb4c0156c88cf 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -2,9 +2,9 @@ //! //! The main entry point is the `step` method. -use rustc::mir; -use rustc::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; -use rustc::ty::layout::LayoutOf; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{InterpResult, Scalar}; +use rustc_target::abi::LayoutOf; use super::{InterpCx, Machine}; @@ -12,7 +12,7 @@ use super::{InterpCx, Machine}; /// same type as the result. #[inline] fn binop_left_homogeneous(op: mir::BinOp) -> bool { - use rustc::mir::BinOp::*; + use rustc_middle::mir::BinOp::*; match op { Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true, Eq | Ne | Lt | Le | Gt | Ge => false, @@ -22,14 +22,14 @@ fn binop_left_homogeneous(op: mir::BinOp) -> bool { /// same type as the LHS. #[inline] fn binop_right_homogeneous(op: mir::BinOp) -> bool { - use rustc::mir::BinOp::*; + use rustc_middle::mir::BinOp::*; match op { Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true, Offset | Shl | Shr => false, } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn run(&mut self) -> InterpResult<'tcx> { while self.step()? {} Ok(()) @@ -42,12 +42,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This is marked `#inline(always)` to work around adverserial codegen when `opt-level = 3` #[inline(always)] pub fn step(&mut self) -> InterpResult<'tcx, bool> { - if self.stack.is_empty() { + if self.stack().is_empty() { return Ok(false); } - let block = match self.frame().block { - Some(block) => block, + let loc = match self.frame().loc { + Some(loc) => loc, None => { // We are unwinding and this fn has no cleanup code. // Just go on unwinding. @@ -56,14 +56,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return Ok(true); } }; - let stmt_id = self.frame().stmt; - let body = self.body(); - let basic_block = &body.basic_blocks()[block]; + let basic_block = &self.body().basic_blocks()[loc.block]; - let old_frames = self.cur_frame(); + let old_frames = self.frame_idx(); - if let Some(stmt) = basic_block.statements.get(stmt_id) { - assert_eq!(old_frames, self.cur_frame()); + if let Some(stmt) = basic_block.statements.get(loc.statement_index) { + assert_eq!(old_frames, self.frame_idx()); self.statement(stmt)?; return Ok(true); } @@ -71,39 +69,38 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::before_terminator(self)?; let terminator = basic_block.terminator(); - assert_eq!(old_frames, self.cur_frame()); + assert_eq!(old_frames, self.frame_idx()); self.terminator(terminator)?; Ok(true) } fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { info!("{:?}", stmt); + self.set_span(stmt.source_info.span); - use rustc::mir::StatementKind::*; + use rustc_middle::mir::StatementKind::*; // Some statements (e.g., box) push new stack frames. // We have to record the stack frame number *before* executing the statement. - let frame_idx = self.cur_frame(); - self.tcx.span = stmt.source_info.span; - self.memory.tcx.span = stmt.source_info.span; + let frame_idx = self.frame_idx(); - match stmt.kind { - Assign(box (ref place, ref rvalue)) => self.eval_rvalue_into_place(rvalue, place)?, + match &stmt.kind { + Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?, - SetDiscriminant { ref place, variant_index } => { - let dest = self.eval_place(place)?; - self.write_discriminant_index(variant_index, dest)?; + SetDiscriminant { place, variant_index } => { + let dest = self.eval_place(**place)?; + self.write_discriminant_index(*variant_index, dest)?; } // Mark locals as alive StorageLive(local) => { - let old_val = self.storage_live(local)?; + let old_val = self.storage_live(*local)?; self.deallocate_local(old_val)?; } // Mark locals as dead StorageDead(local) => { - let old_val = self.storage_dead(local); + let old_val = self.storage_dead(*local); self.deallocate_local(old_val)?; } @@ -112,9 +109,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { FakeRead(..) => {} // Stacked Borrows. - Retag(kind, ref place) => { - let dest = self.eval_place(place)?; - M::retag(self, kind, dest)?; + Retag(kind, place) => { + let dest = self.eval_place(**place)?; + M::retag(self, *kind, dest)?; } // Statements we do not track. @@ -124,10 +121,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // size of MIR constantly. Nop => {} - InlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"), + LlvmInlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"), } - self.stack[frame_idx].stmt += 1; + self.stack_mut()[frame_idx].loc.as_mut().unwrap().statement_index += 1; Ok(()) } @@ -138,11 +135,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn eval_rvalue_into_place( &mut self, rvalue: &mir::Rvalue<'tcx>, - place: &mir::Place<'tcx>, + place: mir::Place<'tcx>, ) -> InterpResult<'tcx> { let dest = self.eval_place(place)?; - use rustc::mir::Rvalue::*; + use rustc_middle::mir::Rvalue::*; match *rvalue { Use(ref operand) => { // Avoid recomputing the layout @@ -192,7 +189,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Ignore zero-sized fields. if !op.layout.is_zst() { let field_index = active_field_index.unwrap_or(i); - let field_dest = self.place_field(dest, field_index as u64)?; + let field_dest = self.place_field(dest, field_index)?; self.copy_op(op, field_dest)?; } } @@ -224,16 +221,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - Len(ref place) => { + Len(place) => { // FIXME(CTFE): don't allow computing the length of arrays in const eval let src = self.eval_place(place)?; let mplace = self.force_allocation(src)?; let len = mplace.len(self)?; - let size = self.pointer_size(); - self.write_scalar(Scalar::from_uint(len, size), dest)?; + self.write_scalar(Scalar::from_machine_usize(len, self), dest)?; } - AddressOf(_, ref place) | Ref(_, _, ref place) => { + AddressOf(_, place) | Ref(_, _, place) => { let src = self.eval_place(place)?; let place = self.force_allocation(src)?; if place.layout.size.bytes() > 0 { @@ -254,8 +250,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { !layout.is_unsized(), "SizeOf nullary MIR operator called for unsized type" ); - let size = self.pointer_size(); - self.write_scalar(Scalar::from_uint(layout.size.bytes(), size), dest)?; + self.write_scalar(Scalar::from_machine_usize(layout.size.bytes(), self), dest)?; } Cast(kind, ref operand, _) => { @@ -263,7 +258,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.cast(src, kind, dest)?; } - Discriminant(ref place) => { + Discriminant(place) => { let op = self.eval_place_to_op(place, None)?; let discr_val = self.read_discriminant(op)?.0; let size = dest.layout.size; @@ -278,18 +273,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { info!("{:?}", terminator.kind); - self.tcx.span = terminator.source_info.span; - self.memory.tcx.span = terminator.source_info.span; - - let old_stack = self.cur_frame(); - let old_bb = self.frame().block; + self.set_span(terminator.source_info.span); self.eval_terminator(terminator)?; - if !self.stack.is_empty() { - // This should change *something* - assert!(self.cur_frame() != old_stack || self.frame().block != old_bb); - if let Some(block) = self.frame().block { - info!("// executing {:?}", block); + if !self.stack().is_empty() { + if let Some(loc) = self.frame().loc { + info!("// executing {:?}", loc.block); } } Ok(()) diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 6b0bbe4f6e0bb..b048240ca8dc1 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -1,24 +1,24 @@ use std::borrow::Cow; +use std::convert::TryFrom; -use rustc::ty::layout::{self, LayoutOf, TyLayout}; -use rustc::ty::Instance; -use rustc::{mir, ty}; -use rustc_span::source_map::Span; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::Instance; +use rustc_middle::{mir, ty}; +use rustc_target::abi::{self, LayoutOf as _}; use rustc_target::spec::abi::Abi; use super::{ FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, StackPopCleanup, }; -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(super) fn eval_terminator( &mut self, terminator: &mir::Terminator<'tcx>, ) -> InterpResult<'tcx> { - use rustc::mir::TerminatorKind::*; + use rustc_middle::mir::TerminatorKind::*; match terminator.kind { Return => { - self.frame().return_place.map(|r| self.dump_place(*r)); self.pop_stack_frame(/* unwinding */ false)? } @@ -29,6 +29,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("SwitchInt({:?})", *discr); // Branch to the `otherwise` case by default, if no match is found. + assert!(!targets.is_empty()); let mut target_block = targets[targets.len() - 1]; for (index, &const_int) in values.iter().enumerate() { @@ -49,7 +50,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.go_to_block(target_block); } - Call { ref func, ref args, ref destination, ref cleanup, .. } => { + Call { ref func, ref args, destination, ref cleanup, .. } => { + let old_stack = self.frame_idx(); + let old_loc = self.frame().loc; let func = self.eval_operand(func, None)?; let (fn_val, abi) = match func.layout.ty.kind { ty::FnPtr(sig) => { @@ -62,31 +65,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let sig = func.layout.ty.fn_sig(*self.tcx); (FnVal::Instance(self.resolve(def_id, substs)?), sig.abi()) } - _ => bug!("invalid callee of type {:?}", func.layout.ty), + _ => span_bug!( + terminator.source_info.span, + "invalid callee of type {:?}", + func.layout.ty + ), }; let args = self.eval_operands(args)?; let ret = match destination { - Some((dest, ret)) => Some((self.eval_place(dest)?, *ret)), + Some((dest, ret)) => Some((self.eval_place(dest)?, ret)), None => None, }; - self.eval_fn_call( - fn_val, - terminator.source_info.span, - abi, - &args[..], - ret, - *cleanup, - )?; + self.eval_fn_call(fn_val, abi, &args[..], ret, *cleanup)?; + // Sanity-check that `eval_fn_call` either pushed a new frame or + // did a jump to another block. + if self.frame_idx() == old_stack && self.frame().loc == old_loc { + span_bug!(terminator.source_info.span, "evaluating this call made no progress"); + } } - Drop { ref location, target, unwind } => { - // FIXME(CTFE): forbid drop in const eval + Drop { location, target, unwind } => { let place = self.eval_place(location)?; let ty = place.layout.ty; trace!("TerminatorKind::drop: {:?}, type {}", location, ty); let instance = Instance::resolve_drop_in_place(*self.tcx, ty); - self.drop_in_place(place, instance, terminator.source_info.span, target, unwind)?; + self.drop_in_place(place, instance, target, unwind)?; } Assert { ref cond, expected, ref msg, target, cleanup } => { @@ -122,9 +126,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | FalseEdges { .. } | FalseUnwind { .. } | Yield { .. } - | GeneratorDrop => { - bug!("{:#?} should have been eliminated by MIR pass", terminator.kind) - } + | GeneratorDrop => span_bug!( + terminator.source_info.span, + "{:#?} should have been eliminated by MIR pass", + terminator.kind + ), + + // Inline assembly can't be interpreted. + InlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"), } Ok(()) @@ -132,8 +141,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn check_argument_compat( rust_abi: bool, - caller: TyLayout<'tcx>, - callee: TyLayout<'tcx>, + caller: TyAndLayout<'tcx>, + callee: TyAndLayout<'tcx>, ) -> bool { if caller.ty == callee.ty { // No question @@ -148,12 +157,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Different valid ranges are okay (once we enforce validity, // that will take care to make it UB to leave the range, just // like for transmute). - (layout::Abi::Scalar(ref caller), layout::Abi::Scalar(ref callee)) => { + (abi::Abi::Scalar(ref caller), abi::Abi::Scalar(ref callee)) => { caller.value == callee.value } ( - layout::Abi::ScalarPair(ref caller1, ref caller2), - layout::Abi::ScalarPair(ref callee1, ref callee2), + abi::Abi::ScalarPair(ref caller1, ref caller2), + abi::Abi::ScalarPair(ref callee1, ref callee2), ) => caller1.value == callee1.value && caller2.value == callee2.value, // Be conservative _ => false, @@ -194,7 +203,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn eval_fn_call( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, - span: Span, caller_abi: Abi, args: &[OpTy<'tcx, M::PointerTag>], ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>, @@ -240,7 +248,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match instance.def { ty::InstanceDef::Intrinsic(..) => { assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic); - M::call_intrinsic(self, span, instance, args, ret, unwind) + M::call_intrinsic(self, instance, args, ret, unwind) } ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) @@ -250,14 +258,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::Item(_) => { // We need MIR for this fn - let body = match M::find_mir_or_eval_fn(self, span, instance, args, ret, unwind)? { + let body = match M::find_mir_or_eval_fn(self, instance, args, ret, unwind)? { Some(body) => body, None => return Ok(()), }; self.push_stack_frame( instance, - span, body, ret.map(|p| p.0), StackPopCleanup::Goto { ret: ret.map(|p| p.1), unwind }, @@ -307,7 +314,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .map(|&a| Ok(a)) .chain( (0..untuple_arg.layout.fields.count()) - .map(|i| self.operand_field(untuple_arg, i as u64)), + .map(|i| self.operand_field(untuple_arg, i)), ) .collect::>>>( )?, @@ -326,11 +333,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `pass_argument` would be the loop body. It takes care to // not advance `caller_iter` for ZSTs. for local in body.args_iter() { - let dest = self.eval_place(&mir::Place::from(local))?; + let dest = self.eval_place(mir::Place::from(local))?; if Some(local) == body.spread_arg { // Must be a tuple for i in 0..dest.layout.fields.count() { - let dest = self.place_field(dest, i as u64)?; + let dest = self.place_field(dest, i)?; self.pass_argument(rust_abi, &mut caller_iter, dest)?; } } else { @@ -344,7 +351,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Don't forget to check the return type! if let Some((caller_ret, _)) = ret { - let callee_ret = self.eval_place(&mir::Place::return_place())?; + let callee_ret = self.eval_place(mir::Place::return_place())?; if !Self::check_argument_compat( rust_abi, caller_ret.layout, @@ -367,7 +374,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; match res { Err(err) => { - self.stack.pop(); + self.stack_mut().pop(); Err(err) } Ok(()) => Ok(()), @@ -392,7 +399,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Find and consult vtable let vtable = receiver_place.vtable(); - let drop_fn = self.get_vtable_slot(vtable, idx)?; + let drop_fn = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?; // `*mut receiver_place.layout.ty` is almost the layout that we // want for args[0]: We have to project to field 0 because we want @@ -402,10 +409,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let this_receiver_ptr = self.layout_of(receiver_ptr_ty)?.field(self, 0)?; // Adjust receiver argument. args[0] = - OpTy::from(ImmTy { layout: this_receiver_ptr, imm: receiver_place.ptr.into() }); + OpTy::from(ImmTy::from_immediate(receiver_place.ptr.into(), this_receiver_ptr)); trace!("Patched self operand to {:#?}", args[0]); // recurse with concrete function - self.eval_fn_call(drop_fn, span, caller_abi, &args, ret, unwind) + self.eval_fn_call(drop_fn, caller_abi, &args, ret, unwind) } } } @@ -414,7 +421,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, place: PlaceTy<'tcx, M::PointerTag>, instance: ty::Instance<'tcx>, - span: Span, target: mir::BasicBlock, unwind: Option, ) -> InterpResult<'tcx> { @@ -432,17 +438,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => (instance, place), }; - let arg = ImmTy { - imm: place.to_ref(), - layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, - }; + let arg = ImmTy::from_immediate( + place.to_ref(), + self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, + ); let ty = self.tcx.mk_unit(); // return type is () let dest = MPlaceTy::dangling(self.layout_of(ty)?, self); self.eval_fn_call( FnVal::Instance(instance), - span, Abi::Rust, &[arg.into()], Some((dest.into(), target)), diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index efbbca534856a..b9f9d37df7645 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -1,10 +1,12 @@ -use super::{FnVal, InterpCx, Machine, MemoryKind}; +use std::convert::TryFrom; + +use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar}; +use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size}; -use rustc::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar}; -use rustc::ty::layout::{Align, HasDataLayout, LayoutOf, Size}; -use rustc::ty::{self, Instance, Ty, TypeFoldable}; +use super::{FnVal, InterpCx, Machine, MemoryKind}; -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// @@ -54,7 +56,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `get_vtable` in `rust_codegen_llvm/meth.rs`. // ///////////////////////////////////////////////////////////////////////////////////////// let vtable = self.memory.allocate( - ptr_size * (3 + methods.len() as u64), + ptr_size * u64::try_from(methods.len()).unwrap().checked_add(3).unwrap(), ptr_align, MemoryKind::Vtable, ); @@ -103,11 +105,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn get_vtable_slot( &self, vtable: Scalar, - idx: usize, + idx: u64, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { let ptr_size = self.pointer_size(); // Skip over the 'drop_ptr', 'size', and 'align' fields. - let vtable_slot = vtable.ptr_offset(ptr_size * (idx as u64 + 3), self)?; + let vtable_slot = vtable.ptr_offset(ptr_size * idx.checked_add(3).unwrap(), self)?; let vtable_slot = self .memory .check_ptr_access(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)? @@ -145,14 +147,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // The drop function takes `*mut T` where `T` is the type being dropped, so get that. let args = fn_sig.inputs(); if args.len() != 1 { - throw_ub_format!("drop fn should have 1 argument, but signature is {:?}", fn_sig); + throw_ub!(InvalidDropFn(fn_sig)); } - let ty = args[0] - .builtin_deref(true) - .ok_or_else(|| { - err_ub_format!("drop fn argument type {} is not a pointer type", args[0]) - })? - .ty; + let ty = args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidDropFn(fn_sig)))?.ty; Ok((drop_instance, ty)) } @@ -169,10 +166,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .expect("cannot be a ZST"); let alloc = self.memory.get_raw(vtable.alloc_id)?; let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)?.not_undef()?; - let size = self.force_bits(size, pointer_size)? as u64; + let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap(); let align = alloc.read_ptr_sized(self, vtable.offset(pointer_size * 2, self)?)?.not_undef()?; - let align = self.force_bits(align, pointer_size)? as u64; + let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap(); if size >= self.tcx.data_layout().obj_size_bound() { throw_ub_format!( diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 6f9543bf95a3b..f6020641d3e28 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -4,14 +4,18 @@ //! That's useful because it means other passes (e.g. promotion) can rely on `const`s //! to be const-safe. +use std::convert::TryFrom; use std::fmt::Write; +use std::num::NonZeroUsize; use std::ops::RangeInclusive; -use rustc::ty; -use rustc::ty::layout::{self, LayoutOf, TyLayout, VariantIdx}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; +use rustc_middle::mir::interpret::{InterpError, InterpErrorInfo}; +use rustc_middle::ty; +use rustc_middle::ty::layout::TyAndLayout; use rustc_span::symbol::{sym, Symbol}; +use rustc_target::abi::{Abi, LayoutOf, Scalar, Size, VariantIdx, Variants}; use std::hash::Hash; @@ -21,43 +25,68 @@ use super::{ }; macro_rules! throw_validation_failure { - ($what:expr, $where:expr, $details:expr) => {{ - let mut msg = format!("encountered {}", $what); - let where_ = &$where; - if !where_.is_empty() { - msg.push_str(" at "); - write_path(&mut msg, where_); - } - write!(&mut msg, ", but expected {}", $details).unwrap(); - throw_ub!(ValidationFailure(msg)) - }}; - ($what:expr, $where:expr) => {{ - let mut msg = format!("encountered {}", $what); + ($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{ + let mut msg = String::new(); + msg.push_str("encountered "); + write!(&mut msg, $($what_fmt),+).unwrap(); let where_ = &$where; if !where_.is_empty() { msg.push_str(" at "); write_path(&mut msg, where_); } + $( + msg.push_str(", but expected "); + write!(&mut msg, $($expected_fmt),+).unwrap(); + )? throw_ub!(ValidationFailure(msg)) }}; } +/// If $e throws an error matching the pattern, throw a validation failure. +/// Other errors are passed back to the caller, unchanged -- and if they reach the root of +/// the visitor, we make sure only validation errors and `InvalidProgram` errors are left. +/// This lets you use the patterns as a kind of validation whitelist, asserting which errors +/// can possibly happen: +/// +/// ``` +/// let v = try_validation!(some_fn(), some_path, { +/// Foo | Bar | Baz => { "some failure" }, +/// }); +/// ``` +/// +/// An additional expected parameter can also be added to the failure message: +/// +/// ``` +/// let v = try_validation!(some_fn(), some_path, { +/// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" }, +/// }); +/// ``` +/// +/// An additional nicety is that both parameters actually take format args, so you can just write +/// the format string in directly: +/// +/// ``` +/// let v = try_validation!(some_fn(), some_path, { +/// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value }, +/// }); +/// ``` +/// macro_rules! try_validation { - ($e:expr, $what:expr, $where:expr, $details:expr) => {{ + ($e:expr, $where:expr, + $( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)? + ) => {{ match $e { Ok(x) => x, - // We re-throw the error, so we are okay with allocation: - // this can only slow down builds that fail anyway. - Err(_) => throw_validation_failure!($what, $where, $details), - } - }}; - - ($e:expr, $what:expr, $where:expr) => {{ - match $e { - Ok(x) => x, - // We re-throw the error, so we are okay with allocation: - // this can only slow down builds that fail anyway. - Err(_) => throw_validation_failure!($what, $where), + // We catch the error and turn it into a validation failure. We are okay with + // allocation here as this can only slow down builds that fail anyway. + $( $( Err(InterpErrorInfo { kind: $p, .. }) )|+ => + throw_validation_failure!( + $where, + { $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )? + ), + )+ + #[allow(unreachable_patterns)] + Err(e) => Err::(e)?, } }}; } @@ -175,11 +204,11 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { ecx: &'rt InterpCx<'mir, 'tcx, M>, } -impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> { - fn aggregate_field_path_elem(&mut self, layout: TyLayout<'tcx>, field: usize) -> PathElem { +impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> { + fn aggregate_field_path_elem(&mut self, layout: TyAndLayout<'tcx>, field: usize) -> PathElem { // First, check if we are projecting to a variant. match layout.variants { - layout::Variants::Multiple { discr_index, .. } => { + Variants::Multiple { discr_index, .. } => { if discr_index == field { return match layout.ty.kind { ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, @@ -188,7 +217,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M }; } } - layout::Variants::Single { .. } => {} + Variants::Single { .. } => {} } // Now we know we are projecting to a field, so figure out which one. @@ -196,9 +225,9 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M // generators and closures. ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { let mut name = None; - if def_id.is_local() { + if let Some(def_id) = def_id.as_local() { let tables = self.ecx.tcx.typeck_tables_of(def_id); - if let Some(upvars) = tables.upvar_list.get(&def_id) { + if let Some(upvars) = tables.upvar_list.get(&def_id.to_def_id()) { // Sometimes the index is beyond the number of upvars (seen // for a generator). if let Some((&var_hir_id, _)) = upvars.get_index(field) { @@ -225,11 +254,11 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M ty::Adt(def, ..) if def.is_enum() => { // we might be projecting *to* a variant, or to a field *in* a variant. match layout.variants { - layout::Variants::Single { index } => { + Variants::Single { index } => { // Inside a variant PathElem::Field(def.variants[index].fields[field].ident.name) } - layout::Variants::Multiple { .. } => bug!("we handled variants above"), + Variants::Multiple { .. } => bug!("we handled variants above"), } } @@ -265,38 +294,52 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M fn check_wide_ptr_meta( &mut self, meta: MemPlaceMeta, - pointee: TyLayout<'tcx>, + pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); match tail.kind { ty::Dynamic(..) => { let vtable = meta.unwrap_meta(); + // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. try_validation!( - self.ecx.memory.check_ptr_access( + self.ecx.memory.check_ptr_access_align( vtable, 3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align - self.ecx.tcx.data_layout.pointer_align.abi, + Some(self.ecx.tcx.data_layout.pointer_align.abi), + CheckInAllocMsg::InboundsTest, ), - "dangling or unaligned vtable pointer in wide pointer or too small vtable", - self.path + self.path, + err_ub!(DanglingIntPointer(..)) | + err_ub!(PointerUseAfterFree(..)) | + err_unsup!(ReadBytesAsPointer) => + { "dangling vtable pointer in wide pointer" }, + err_ub!(AlignmentCheckFailed { .. }) => + { "unaligned vtable pointer in wide pointer" }, + err_ub!(PointerOutOfBounds { .. }) => + { "too small vtable" }, ); try_validation!( self.ecx.read_drop_type_from_vtable(vtable), - "invalid drop fn in vtable", - self.path + self.path, + err_ub!(DanglingIntPointer(..)) | + err_ub!(InvalidFunctionPointer(..)) | + err_unsup!(ReadBytesAsPointer) => + { "invalid drop function pointer in vtable (not pointing to a function)" }, + err_ub!(InvalidDropFn(..)) => + { "invalid drop function pointer in vtable (function has incompatible signature)" }, ); try_validation!( self.ecx.read_size_and_align_from_vtable(vtable), - "invalid size or align in vtable", - self.path + self.path, + err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" }, ); // FIXME: More checks for the vtable. } ty::Slice(..) | ty::Str => { let _len = try_validation!( meta.unwrap_meta().to_machine_usize(self.ecx), - "non-integer slice length in wide pointer", - self.path + self.path, + err_unsup!(ReadPointerAsBytes) => { "non-integer slice length in wide pointer" }, ); // We do not check that `len * elem_size <= isize::MAX`: // that is only required for references, and there it falls out of the @@ -320,96 +363,81 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M let value = self.ecx.read_immediate(value)?; // Handle wide pointers. // Check metadata early, for better diagnostics - let place = try_validation!(self.ecx.ref_to_mplace(value), "undefined pointer", self.path); + let place = try_validation!( + self.ecx.ref_to_mplace(value), + self.path, + err_ub!(InvalidUninitBytes(..)) => { "uninitialized {}", kind }, + ); if place.layout.is_unsized() { self.check_wide_ptr_meta(place.meta, place.layout)?; } // Make sure this is dereferenceable and all. - let size_and_align = match self.ecx.size_and_align_of(place.meta, place.layout) { - Ok(res) => res, - Err(err) => match err.kind { - err_ub!(InvalidMeta(msg)) => throw_validation_failure!( - format_args!("invalid {} metadata: {}", kind, msg), - self.path - ), - _ => bug!("Unexpected error during ptr size_and_align_of: {}", err), - }, - }; + let size_and_align = try_validation!( + self.ecx.size_and_align_of(place.meta, place.layout), + self.path, + err_ub!(InvalidMeta(msg)) => { "invalid {} metadata: {}", kind, msg }, + ); let (size, align) = size_and_align // for the purpose of validity, consider foreign types to have // alignment and size determined by the layout (size will be 0, // alignment should take attributes into account). .unwrap_or_else(|| (place.layout.size, place.layout.align.abi)); - let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align( - place.ptr, - size, - Some(align), - CheckInAllocMsg::InboundsTest, - ) { - Ok(ptr) => ptr, - Err(err) => { - info!( - "{:?} did not pass access check for size {:?}, align {:?}", - place.ptr, size, align - ); - match err.kind { - err_ub!(InvalidIntPointerUsage(0)) => { - throw_validation_failure!(format_args!("a NULL {}", kind), self.path) - } - err_ub!(InvalidIntPointerUsage(i)) => throw_validation_failure!( - format_args!("a {} to unallocated address {}", kind, i), - self.path - ), - err_ub!(AlignmentCheckFailed { required, has }) => throw_validation_failure!( - format_args!( - "an unaligned {} (required {} byte alignment but found {})", - kind, - required.bytes(), - has.bytes() - ), - self.path - ), - err_unsup!(ReadBytesAsPointer) => throw_validation_failure!( - format_args!("a dangling {} (created from integer)", kind), - self.path - ), - err_ub!(PointerOutOfBounds { .. }) => throw_validation_failure!( - format_args!( - "a dangling {} (going beyond the bounds of its allocation)", - kind - ), - self.path - ), - // This cannot happen during const-eval (because interning already detects - // dangling pointers), but it can happen in Miri. - err_ub!(PointerUseAfterFree(_)) => throw_validation_failure!( - format_args!("a dangling {} (use-after-free)", kind), - self.path - ), - _ => bug!("Unexpected error during ptr inbounds test: {}", err), - } - } - }; + // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. + let ptr: Option<_> = try_validation!( + self.ecx.memory.check_ptr_access_align( + place.ptr, + size, + Some(align), + CheckInAllocMsg::InboundsTest, + ), + self.path, + err_ub!(AlignmentCheckFailed { required, has }) => + { + "an unaligned {} (required {} byte alignment but found {})", + kind, + required.bytes(), + has.bytes() + }, + err_ub!(DanglingIntPointer(0, _)) => + { "a NULL {}", kind }, + err_ub!(DanglingIntPointer(i, _)) => + { "a dangling {} (address 0x{:x} is unallocated)", kind, i }, + err_ub!(PointerOutOfBounds { .. }) => + { "a dangling {} (going beyond the bounds of its allocation)", kind }, + err_unsup!(ReadBytesAsPointer) => + { "a dangling {} (created from integer)", kind }, + // This cannot happen during const-eval (because interning already detects + // dangling pointers), but it can happen in Miri. + err_ub!(PointerUseAfterFree(..)) => + { "a dangling {} (use-after-free)", kind }, + ); // Recursive checking if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts { if let Some(ptr) = ptr { // not a ZST // Skip validation entirely for some external statics - let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id); + let alloc_kind = self.ecx.tcx.get_global_alloc(ptr.alloc_id); if let Some(GlobalAlloc::Static(did)) = alloc_kind { + // See const_eval::machine::MemoryExtra::can_access_statics for why + // this check is so important. + // This check is reachable when the const just referenced the static, + // but never read it (so we never entered `before_access_global`). + // We also need to do it here instead of going on to avoid running + // into the `before_access_global` check during validation. + if !self.may_ref_to_static && self.ecx.tcx.is_static(did) { + throw_validation_failure!(self.path, + { "a {} pointing to a static variable", kind } + ); + } // `extern static` cannot be validated as they have no body. // FIXME: Statics from other crates are also skipped. // They might be checked at a different type, but for now we - // want to avoid recursing too deeply. This is not sound! + // want to avoid recursing too deeply. We might miss const-invalid data, + // but things are still sound otherwise (in particular re: consts + // referring to statics). if !did.is_local() || self.ecx.tcx.is_foreign_item(did) { return Ok(()); } - if !self.may_ref_to_static && self.ecx.tcx.is_static(did) { - throw_validation_failure!( - format_args!("a {} pointing to a static variable", kind), - self.path - ); - } } } // Proceed recursively even for ZST, no reason to skip them! @@ -445,12 +473,20 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M match ty.kind { ty::Bool => { let value = self.ecx.read_scalar(value)?; - try_validation!(value.to_bool(), value, self.path, "a boolean"); + try_validation!( + value.to_bool(), + self.path, + err_ub!(InvalidBool(..)) => { "{}", value } expected { "a boolean" }, + ); Ok(true) } ty::Char => { let value = self.ecx.read_scalar(value)?; - try_validation!(value.to_char(), value, self.path, "a valid unicode codepoint"); + try_validation!( + value.to_char(), + self.path, + err_ub!(InvalidChar(..)) => { "{}", value } expected { "a valid unicode codepoint" }, + ); Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { @@ -461,10 +497,8 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous let is_bits = value.not_undef().map_or(false, |v| v.is_bits()); if !is_bits { - throw_validation_failure!( - value, - self.path, - "initialized plain (non-pointer) bytes" + throw_validation_failure!(self.path, + { "{}", value } expected { "initialized plain (non-pointer) bytes" } ) } } else { @@ -475,11 +509,12 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M } ty::RawPtr(..) => { // We are conservative with undef for integers, but try to - // actually enforce our current rules for raw pointers. + // actually enforce the strict rules for raw pointers (mostly because + // that lets us re-use `ref_to_mplace`). let place = try_validation!( self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), - "undefined pointer", - self.path + self.path, + err_ub!(InvalidUninitBytes(..)) => { "uninitialized raw pointer" }, ); if place.layout.is_unsized() { self.check_wide_ptr_meta(place.meta, place.layout)?; @@ -498,14 +533,16 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M let value = self.ecx.read_scalar(value)?; let _fn = try_validation!( value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)), - value, self.path, - "a function pointer" + err_ub!(DanglingIntPointer(..)) | + err_ub!(InvalidFunctionPointer(..)) | + err_unsup!(ReadBytesAsPointer) => + { "{}", value } expected { "a function pointer" }, ); // FIXME: Check if the signature matches Ok(true) } - ty::Never => throw_validation_failure!("a value of the never type `!`", self.path), + ty::Never => throw_validation_failure!(self.path, { "a value of the never type `!`" }), ty::Foreign(..) | ty::FnDef(..) => { // Nothing to check. Ok(true) @@ -529,7 +566,6 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M | ty::Bound(..) | ty::Param(..) | ty::Opaque(..) - | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty), } @@ -538,7 +574,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M fn visit_scalar( &mut self, op: OpTy<'tcx, M::PointerTag>, - scalar_layout: &layout::Scalar, + scalar_layout: &Scalar, ) -> InterpResult<'tcx> { let value = self.ecx.read_scalar(op)?; let valid_range = &scalar_layout.valid_range; @@ -555,35 +591,33 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M // At least one value is excluded. Get the bits. let value = try_validation!( value.not_undef(), - value, self.path, - format_args!("something {}", wrapping_range_format(valid_range, max_hi),) + err_ub!(InvalidUninitBytes(..)) => { "{}", value } + expected { "something {}", wrapping_range_format(valid_range, max_hi) }, ); let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) { Err(ptr) => { if lo == 1 && hi == max_hi { // Only NULL is the niche. So make sure the ptr is NOT NULL. if self.ecx.memory.ptr_may_be_null(ptr) { - throw_validation_failure!( - "a potentially NULL pointer", - self.path, - format_args!( + throw_validation_failure!(self.path, + { "a potentially NULL pointer" } + expected { "something that cannot possibly fail to be {}", wrapping_range_format(valid_range, max_hi) - ) + } ) } return Ok(()); } else { // Conservatively, we reject, because the pointer *could* have a bad // value. - throw_validation_failure!( - "a pointer", - self.path, - format_args!( + throw_validation_failure!(self.path, + { "a pointer" } + expected { "something that cannot possibly fail to be {}", wrapping_range_format(valid_range, max_hi) - ) + } ) } } @@ -593,16 +627,15 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M if wrapping_range_contains(&valid_range, bits) { Ok(()) } else { - throw_validation_failure!( - bits, - self.path, - format_args!("something {}", wrapping_range_format(valid_range, max_hi)) + throw_validation_failure!(self.path, + { "{}", bits } + expected { "something {}", wrapping_range_format(valid_range, max_hi) } ) } } } -impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> +impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> for ValidityVisitor<'rt, 'mir, 'tcx, M> { type V = OpTy<'tcx, M::PointerTag>; @@ -640,10 +673,11 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } #[inline(always)] - fn visit_union(&mut self, op: OpTy<'tcx, M::PointerTag>, fields: usize) -> InterpResult<'tcx> { - // Empty unions are not accepted by rustc. But uninhabited enums - // claim to be unions, so allow them, too. - assert!(op.layout.abi.is_uninhabited() || fields > 0); + fn visit_union( + &mut self, + _op: OpTy<'tcx, M::PointerTag>, + _fields: NonZeroUsize, + ) -> InterpResult<'tcx> { Ok(()) } @@ -659,19 +693,14 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> assert!(op.layout.ty.builtin_deref(true).is_none()); // Recursively walk the type. Translate some possible errors to something nicer. - match self.walk_value(op) { - Ok(()) => {} - Err(err) => match err.kind { - err_ub!(InvalidDiscriminant(val)) => { - throw_validation_failure!(val, self.path, "a valid enum discriminant") - } - err_unsup!(ReadPointerAsBytes) => { - throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes") - } - // Propagate upwards (that will also check for unexpected errors). - _ => return Err(err), - }, - } + try_validation!( + self.walk_value(op), + self.path, + err_ub!(InvalidDiscriminant(val)) => + { "{}", val } expected { "a valid enum discriminant" }, + err_unsup!(ReadPointerAsBytes) => + { "a pointer" } expected { "plain (non-pointer) bytes" }, + ); // *After* all of this, check the ABI. We need to check the ABI to handle // types like `NonNull` where the `Scalar` info is more restrictive than what @@ -684,22 +713,21 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // scalars, we do the same check on every "level" (e.g., first we check // MyNewtype and then the scalar in there). match op.layout.abi { - layout::Abi::Uninhabited => { - throw_validation_failure!( - format_args!("a value of uninhabited type {:?}", op.layout.ty), - self.path + Abi::Uninhabited => { + throw_validation_failure!(self.path, + { "a value of uninhabited type {:?}", op.layout.ty } ); } - layout::Abi::Scalar(ref scalar_layout) => { + Abi::Scalar(ref scalar_layout) => { self.visit_scalar(op, scalar_layout)?; } - layout::Abi::ScalarPair { .. } | layout::Abi::Vector { .. } => { + Abi::ScalarPair { .. } | Abi::Vector { .. } => { // These have fields that we already visited above, so we already checked // all their scalar-level restrictions. // There is also no equivalent to `rustc_layout_scalar_valid_range_start` // that would make skipping them here an issue. } - layout::Abi::Aggregate { .. } => { + Abi::Aggregate { .. } => { // Nothing to do. } } @@ -715,10 +743,11 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> match op.layout.ty.kind { ty::Str => { let mplace = op.assert_mem_place(self.ecx); // strings are never immediate + let len = mplace.len(self.ecx)?; try_validation!( - self.ecx.read_str(mplace), - "uninitialized or non-UTF-8 data in str", - self.path + self.ecx.memory.read_bytes(mplace.ptr, Size::from_bytes(len)), + self.path, + err_ub!(InvalidUninitBytes(..)) => { "uninitialized data in `str`" }, ); } ty::Array(tys, ..) | ty::Slice(tys) @@ -746,7 +775,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } // This is the element type size. let layout = self.ecx.layout_of(tys)?; - // This is the size in bytes of the whole array. + // This is the size in bytes of the whole array. (This checks for overflow.) let size = layout.size * len; // Size is not 0, get a pointer. let ptr = self.ecx.force_ptr(mplace.ptr)?; @@ -771,18 +800,20 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> Ok(()) => {} // Some error happened, try to provide a more detailed description. Err(err) => { - // For some errors we might be able to provide extra information + // For some errors we might be able to provide extra information. + // (This custom logic does not fit the `try_validation!` macro.) match err.kind { - err_ub!(InvalidUndefBytes(Some(ptr))) => { - // Some byte was undefined, determine which + err_ub!(InvalidUninitBytes(Some(ptr))) => { + // Some byte was uninitialized, determine which // element that byte belongs to so we can // provide an index. - let i = (ptr.offset.bytes() / layout.size.bytes()) as usize; + let i = usize::try_from(ptr.offset.bytes() / layout.size.bytes()) + .unwrap(); self.path.push(PathElem::ArrayElem(i)); - throw_validation_failure!("undefined bytes", self.path) + throw_validation_failure!(self.path, { "uninitialized bytes" }) } - // Other errors shouldn't be possible + // Propagate upwards (that will also check for unexpected errors). _ => return Err(err), } } @@ -803,7 +834,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn validate_operand_internal( &self, op: OpTy<'tcx, M::PointerTag>, @@ -825,10 +856,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Run it. match visitor.visit_value(op) { Ok(()) => Ok(()), - // We should only get validation errors here. Avoid other errors as - // those do not show *where* in the value the issue lies. + // Pass through validation failures. Err(err) if matches!(err.kind, err_ub!(ValidationFailure { .. })) => Err(err), - Err(err) => bug!("Unexpected error during validation: {}", err), + // Also pass through InvalidProgram, those just indicate that we could not + // validate and each caller will know best what to do with them. + Err(err) if matches!(err.kind, InterpError::InvalidProgram(_)) => Err(err), + // Avoid other errors as those do not show *where* in the value the issue lies. + Err(err) => { + err.print_backtrace(); + bug!("Unexpected error during validation: {}", err); + } } } diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs index 8808fc70cf76b..903aa377a3d7d 100644 --- a/src/librustc_mir/interpret/visitor.rs +++ b/src/librustc_mir/interpret/visitor.rs @@ -1,9 +1,12 @@ //! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound //! types until we arrive at the leaves, with custom handling for primitive types. -use rustc::mir::interpret::InterpResult; -use rustc::ty; -use rustc::ty::layout::{self, TyLayout, VariantIdx}; +use rustc_middle::mir::interpret::InterpResult; +use rustc_middle::ty; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; + +use std::num::NonZeroUsize; use super::{InterpCx, MPlaceTy, Machine, OpTy}; @@ -12,7 +15,7 @@ use super::{InterpCx, MPlaceTy, Machine, OpTy}; // that's just more convenient to work with (avoids repeating all the `Machine` bounds). pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { /// Gets this value's layout. - fn layout(&self) -> TyLayout<'tcx>; + fn layout(&self) -> TyAndLayout<'tcx>; /// Makes this into an `OpTy`. fn to_op(self, ecx: &InterpCx<'mir, 'tcx, M>) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; @@ -28,14 +31,15 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { ) -> InterpResult<'tcx, Self>; /// Projects to the n-th field. - fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: u64) -> InterpResult<'tcx, Self>; + fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: usize) + -> InterpResult<'tcx, Self>; } // Operands and memory-places are both values. // Places in general are not due to `place_field` having to do `force_allocation`. -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { #[inline(always)] - fn layout(&self) -> TyLayout<'tcx> { + fn layout(&self) -> TyAndLayout<'tcx> { self.layout } @@ -62,14 +66,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M:: } #[inline(always)] - fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: u64) -> InterpResult<'tcx, Self> { + fn project_field( + self, + ecx: &InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { ecx.operand_field(self, field) } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for MPlaceTy<'tcx, M::PointerTag> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> + for MPlaceTy<'tcx, M::PointerTag> +{ #[inline(always)] - fn layout(&self) -> TyLayout<'tcx> { + fn layout(&self) -> TyAndLayout<'tcx> { self.layout } @@ -96,7 +106,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for MPlaceTy<'tcx, } #[inline(always)] - fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: u64) -> InterpResult<'tcx, Self> { + fn project_field( + self, + ecx: &InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { ecx.mplace_field(self, field) } } @@ -120,7 +134,7 @@ macro_rules! make_value_visitor { } /// Visits the given value as a union. No automatic recursion can happen here. #[inline(always)] - fn visit_union(&mut self, _v: Self::V, _fields: usize) -> InterpResult<'tcx> + fn visit_union(&mut self, _v: Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> { Ok(()) } @@ -198,20 +212,21 @@ macro_rules! make_value_visitor { // Visit the fields of this value. match v.layout().fields { - layout::FieldPlacement::Union(fields) => { + FieldsShape::Primitive => {}, + FieldsShape::Union(fields) => { self.visit_union(v, fields)?; }, - layout::FieldPlacement::Arbitrary { ref offsets, .. } => { + FieldsShape::Arbitrary { ref offsets, .. } => { // FIXME: We collect in a vec because otherwise there are lifetime // errors: Projecting to a field needs access to `ecx`. let fields: Vec> = (0..offsets.len()).map(|i| { - v.project_field(self.ecx(), i as u64) + v.project_field(self.ecx(), i) }) .collect(); self.visit_aggregate(v, fields.into_iter())?; }, - layout::FieldPlacement::Array { .. } => { + FieldsShape::Array { .. } => { // Let's get an mplace first. let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); // Now we can go over all the fields. @@ -228,7 +243,7 @@ macro_rules! make_value_visitor { match v.layout().variants { // If this is a multi-variant layout, find the right variant and proceed // with *its* fields. - layout::Variants::Multiple { .. } => { + Variants::Multiple { .. } => { let op = v.to_op(self.ecx())?; let idx = self.ecx().read_discriminant(op)?.1; let inner = v.project_downcast(self.ecx(), idx)?; @@ -237,7 +252,7 @@ macro_rules! make_value_visitor { self.visit_variant(v, idx, inner) } // For single-variant layouts, we already did anything there is to do. - layout::Variants::Single { .. } => Ok(()) + Variants::Single { .. } => Ok(()) } } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 7d3aba3ff034e..785c6c21d7443 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -9,27 +9,32 @@ Rust MIR: a lowered representation of Rust. #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] -#![feature(const_if_match)] #![feature(const_fn)] +#![feature(const_if_match)] +#![feature(const_loop)] #![feature(const_panic)] #![feature(crate_visibility_modifier)] +#![feature(decl_macro)] #![feature(drain_filter)] #![feature(exhaustive_patterns)] #![feature(iter_order_by)] #![feature(never_type)] -#![feature(specialization)] +#![feature(min_specialization)] #![feature(trusted_len)] #![feature(try_blocks)] #![feature(associated_type_bounds)] +#![feature(associated_type_defaults)] #![feature(range_is_empty)] #![feature(stmt_expr_attributes)] #![feature(trait_alias)] +#![feature(option_expect_none)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; mod borrow_check; pub mod const_eval; @@ -40,7 +45,7 @@ mod shim; pub mod transform; pub mod util; -use rustc::ty::query::Providers; +use rustc_middle::ty::query::Providers; pub fn provide(providers: &mut Providers<'_>) { borrow_check::provide(providers); diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index cbd19f080eb9e..925b8d329668f 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -176,23 +176,24 @@ use crate::monomorphize; -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::middle::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem}; -use rustc::mir::interpret::{AllocId, ConstValue}; -use rustc::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar}; -use rustc::mir::mono::{InstantiationMode, MonoItem}; -use rustc::mir::visit::Visitor as MirVisitor; -use rustc::mir::{self, Local, Location}; -use rustc::ty::adjustment::{CustomCoerceUnsized, PointerCast}; -use rustc::ty::print::obsolete::DefPathBasedNames; -use rustc::ty::subst::InternalSubsts; -use rustc::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator}; +use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, DefIdMap, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem}; use rustc_index::bit_set::GrowableBitSet; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::interpret::{AllocId, ConstValue}; +use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar}; +use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::mir::visit::Visitor as MirVisitor; +use rustc_middle::mir::{self, Local, Location}; +use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; +use rustc_middle::ty::print::obsolete::DefPathBasedNames; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; +use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_session::config::EntryFnType; use smallvec::SmallVec; use std::iter; @@ -335,7 +336,7 @@ fn collect_items_rec<'tcx>( recursion_depths: &mut DefIdMap, inlining_map: MTRef<'_, MTLock>>, ) { - if !visited.lock_mut().insert(starting_point.clone()) { + if !visited.lock_mut().insert(starting_point) { // We've been here already, no need to search again. return; } @@ -368,7 +369,9 @@ fn collect_items_rec<'tcx>( recursion_depth_reset = Some(check_recursion_limit(tcx, instance, recursion_depths)); check_type_length_limit(tcx, instance); - collect_neighbours(tcx, instance, &mut neighbors); + rustc_data_structures::stack::ensure_sufficient_stack(|| { + collect_neighbours(tcx, instance, &mut neighbors); + }); } MonoItem::GlobalAsm(..) => { recursion_depth_reset = None; @@ -429,7 +432,8 @@ fn check_recursion_limit<'tcx>( // infinite expansion. if adjusted_recursion_depth > *tcx.sess.recursion_limit.get() { let error = format!("reached the recursion limit while instantiating `{}`", instance); - if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = tcx.hir().as_local_hir_id(def_id); tcx.sess.span_fatal(tcx.hir().span(hir_id), &error); } else { tcx.sess.fatal(&error); @@ -442,9 +446,16 @@ fn check_recursion_limit<'tcx>( } fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { - let type_length = instance.substs.types().flat_map(|ty| ty.walk()).count(); - let const_length = instance.substs.consts().flat_map(|ct| ct.ty.walk()).count(); - debug!(" => type length={}, const length={}", type_length, const_length); + let type_length = instance + .substs + .iter() + .flat_map(|&arg| arg.walk()) + .filter(|arg| match arg.unpack() { + GenericArgKind::Type(_) | GenericArgKind::Const(_) => true, + GenericArgKind::Lifetime(_) => false, + }) + .count(); + debug!(" => type length={}", type_length); // Rust code can easily create exponentially-long types using only a // polynomial recursion depth. Even with the default recursion @@ -453,11 +464,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { // // Bail out in these cases to avoid that bad user experience. let type_length_limit = *tcx.sess.type_length_limit.get(); - // We include the const length in the type length, as it's better - // to be overly conservative. - // FIXME(const_generics): we should instead uniformly walk through `substs`, - // ignoring lifetimes. - if type_length + const_length > type_length_limit { + if type_length > type_length_limit { // The instance name is already known to be too long for rustc. // Show only the first and last 32 characters to avoid blasting // the user's terminal with thousands of lines of type-name. @@ -573,10 +580,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { } mir::Rvalue::NullaryOp(mir::NullOp::Box, _) => { let tcx = self.tcx; - let exchange_malloc_fn_def_id = tcx - .lang_items() - .require(ExchangeMallocFnLangItem) - .unwrap_or_else(|e| tcx.sess.fatal(&e)); + let exchange_malloc_fn_def_id = + tcx.require_lang_item(ExchangeMallocFnLangItem, None); let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); if should_monomorphize_locally(tcx, &instance) { self.output.push(create_fn_mono_item(instance)); @@ -599,7 +604,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { ty::ConstKind::Unevaluated(def_id, substs, promoted) => { match self.tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) { Ok(val) => collect_const_value(self.tcx, val, self.output), - Err(ErrorHandled::Reported) => {} + Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => {} Err(ErrorHandled::TooGeneric) => span_bug!( self.tcx.def_span(def_id), "collection encountered polymorphic constant", @@ -634,7 +639,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { | mir::TerminatorKind::Abort | mir::TerminatorKind::Return | mir::TerminatorKind::Unreachable - | mir::TerminatorKind::Assert { .. } => {} + | mir::TerminatorKind::Assert { .. } + | mir::TerminatorKind::InlineAsm { .. } => {} mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } | mir::TerminatorKind::FalseEdges { .. } @@ -644,7 +650,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.super_terminator_kind(kind, location); } - fn visit_place_base( + fn visit_local( &mut self, _place_local: &Local, _context: mir::visit::PlaceContext, @@ -670,9 +676,12 @@ fn visit_fn_use<'tcx>( output: &mut Vec>, ) { if let ty::FnDef(def_id, substs) = ty.kind { - let resolver = - if is_direct_call { ty::Instance::resolve } else { ty::Instance::resolve_for_fn_ptr }; - let instance = resolver(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap(); + let instance = if is_direct_call { + ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() + } else { + ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, substs) + .unwrap() + }; visit_instance_use(tcx, instance, is_direct_call, output); } } @@ -818,8 +827,7 @@ fn find_vtable_types_for_unsizing<'tcx>( }; match (&source_ty.kind, &target_ty.kind) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) - | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { ptr_vtable(a, b) } @@ -912,7 +920,7 @@ struct RootCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, mode: MonoItemCollectionMode, output: &'a mut Vec>, - entry_fn: Option<(DefId, EntryFnType)>, + entry_fn: Option<(LocalDefId, EntryFnType)>, } impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { @@ -946,8 +954,8 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { def_id_to_string(self.tcx, def_id) ); - let ty = - Instance::new(def_id, InternalSubsts::empty()).monomorphic_ty(self.tcx); + let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty()) + .monomorphic_ty(self.tcx); visit_drop_use(self.tcx, ty, true, self.output); } } @@ -962,7 +970,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { hir::ItemKind::Static(..) => { let def_id = self.tcx.hir().local_def_id(item.hir_id); debug!("RootCollector: ItemKind::Static({})", def_id_to_string(self.tcx, def_id)); - self.output.push(MonoItem::Static(def_id)); + self.output.push(MonoItem::Static(def_id.to_def_id())); } hir::ItemKind::Const(..) => { // const items only generate mono items if they are @@ -971,7 +979,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { // but even just declaring them must collect the items they refer to let def_id = self.tcx.hir().local_def_id(item.hir_id); - if let Ok(val) = self.tcx.const_eval_poly(def_id) { + if let Ok(val) = self.tcx.const_eval_poly(def_id.to_def_id()) { collect_const_value(self.tcx, val, &mut self.output); } } @@ -988,18 +996,15 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { } fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) { - match ii.kind { - hir::ImplItemKind::Fn(hir::FnSig { .. }, _) => { - let def_id = self.tcx.hir().local_def_id(ii.hir_id); - self.push_if_root(def_id); - } - _ => { /* nothing to do here */ } + if let hir::ImplItemKind::Fn(hir::FnSig { .. }, _) = ii.kind { + let def_id = self.tcx.hir().local_def_id(ii.hir_id); + self.push_if_root(def_id); } } } impl RootCollector<'_, 'v> { - fn is_root(&self, def_id: DefId) -> bool { + fn is_root(&self, def_id: LocalDefId) -> bool { !item_requires_monomorphization(self.tcx, def_id) && match self.mode { MonoItemCollectionMode::Eager => true, @@ -1017,11 +1022,11 @@ impl RootCollector<'_, 'v> { /// If `def_id` represents a root, pushes it onto the list of /// outputs. (Note that all roots must be monomorphic.) - fn push_if_root(&mut self, def_id: DefId) { + fn push_if_root(&mut self, def_id: LocalDefId) { if self.is_root(def_id) { debug!("RootCollector::push_if_root: found root def_id={:?}", def_id); - let instance = Instance::mono(self.tcx, def_id); + let instance = Instance::mono(self.tcx, def_id.to_def_id()); self.output.push(create_fn_mono_item(instance)); } } @@ -1056,13 +1061,14 @@ impl RootCollector<'_, 'v> { start_def_id, self.tcx.intern_substs(&[main_ret_ty.into()]), ) + .unwrap() .unwrap(); self.output.push(create_fn_mono_item(start_instance)); } } -fn item_requires_monomorphization(tcx: TyCtxt<'_>, def_id: DefId) -> bool { +fn item_requires_monomorphization(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let generics = tcx.generics_of(def_id); generics.requires_monomorphization(tcx) } @@ -1111,8 +1117,9 @@ fn create_mono_items_for_default_impls<'tcx>( trait_ref.substs[param.index as usize] } }); - let instance = - ty::Instance::resolve(tcx, param_env, method.def_id, substs).unwrap(); + let instance = ty::Instance::resolve(tcx, param_env, method.def_id, substs) + .unwrap() + .unwrap(); let mono_item = create_fn_mono_item(instance); if mono_item.is_instantiable(tcx) && should_monomorphize_locally(tcx, &instance) @@ -1128,28 +1135,28 @@ fn create_mono_items_for_default_impls<'tcx>( /// Scans the miri alloc in order to find function calls, closures, and drop-glue. fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut Vec>) { - let alloc_kind = tcx.alloc_map.lock().get(alloc_id); - match alloc_kind { - Some(GlobalAlloc::Static(def_id)) => { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Static(def_id) => { let instance = Instance::mono(tcx, def_id); if should_monomorphize_locally(tcx, &instance) { trace!("collecting static {:?}", def_id); output.push(MonoItem::Static(def_id)); } } - Some(GlobalAlloc::Memory(alloc)) => { + GlobalAlloc::Memory(alloc) => { trace!("collecting {:?} with {:#?}", alloc_id, alloc); for &((), inner) in alloc.relocations().values() { - collect_miri(tcx, inner, output); + rustc_data_structures::stack::ensure_sufficient_stack(|| { + collect_miri(tcx, inner, output); + }); } } - Some(GlobalAlloc::Function(fn_instance)) => { + GlobalAlloc::Function(fn_instance) => { if should_monomorphize_locally(tcx, &fn_instance) { trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); output.push(create_fn_mono_item(fn_instance)); } } - None => bug!("alloc id without corresponding allocation: {}", alloc_id), } } @@ -1162,13 +1169,13 @@ fn collect_neighbours<'tcx>( debug!("collect_neighbours: {:?}", instance.def_id()); let body = tcx.instance_mir(instance.def); - MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(body); + MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body); } -fn def_id_to_string(tcx: TyCtxt<'_>, def_id: DefId) -> String { +fn def_id_to_string(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { let mut output = String::new(); let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_def_path(def_id, &mut output); + printer.push_def_path(def_id.to_def_id(), &mut output); output } diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 7177bf726d403..28edd87a3add5 100644 --- a/src/librustc_mir/monomorphize/mod.rs +++ b/src/librustc_mir/monomorphize/mod.rs @@ -1,6 +1,8 @@ -use rustc::traits; -use rustc::ty::adjustment::CustomCoerceUnsized; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::traits; +use rustc_middle::ty::adjustment::CustomCoerceUnsized; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +use rustc_hir::lang_items::CoerceUnsizedTraitLangItem; pub mod collector; pub mod partitioning; @@ -10,7 +12,7 @@ pub fn custom_coerce_unsize_info<'tcx>( source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, ) -> CustomCoerceUnsized { - let def_id = tcx.lang_items().coerce_unsized_trait().unwrap(); + let def_id = tcx.require_lang_item(CoerceUnsizedTraitLangItem, None); let trait_ref = ty::Binder::bind(ty::TraitRef { def_id, @@ -18,7 +20,7 @@ pub fn custom_coerce_unsize_info<'tcx>( }); match tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) { - Some(traits::VtableImpl(traits::VtableImplData { impl_def_id, .. })) => { + Ok(traits::VtableImpl(traits::VtableImplData { impl_def_id, .. })) => { tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() } vtable => { diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 9b81d69ce694c..db1ea72c0a531 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -94,32 +94,23 @@ use std::cmp; use std::collections::hash_map::Entry; -use std::sync::Arc; - -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::middle::exported_symbols::SymbolExportLevel; -use rustc::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility}; -use rustc::mir::mono::{InstantiationMode, MonoItem}; -use rustc::ty::print::characteristic_def_id_of_type; -use rustc::ty::query::Providers; -use rustc::ty::{self, DefIdTree, InstanceDef, TyCtxt}; + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_span::symbol::Symbol; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::exported_symbols::SymbolExportLevel; +use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility}; +use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::ty::print::characteristic_def_id_of_type; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, DefIdTree, InstanceDef, TyCtxt}; +use rustc_span::symbol::{Symbol, SymbolStr}; use crate::monomorphize::collector::InliningMap; use crate::monomorphize::collector::{self, MonoItemCollectionMode}; -pub enum PartitioningStrategy { - /// Generates one codegen unit per source-level module. - PerModule, - - /// Partition the whole crate into a fixed number of codegen units. - FixedUnitCount(usize), -} - // Anything we can't find a proper codegen unit for goes into this. fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol { name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu")) @@ -128,7 +119,7 @@ fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol { pub fn partition<'tcx, I>( tcx: TyCtxt<'tcx>, mono_items: I, - strategy: PartitioningStrategy, + max_cgu_count: usize, inlining_map: &InliningMap<'tcx>, ) -> Vec> where @@ -148,11 +139,10 @@ where debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter()); - // If the partitioning should produce a fixed count of codegen units, merge - // until that count is reached. - if let PartitioningStrategy::FixedUnitCount(count) = strategy { + // Merge until we have at most `max_cgu_count` codegen units. + { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus"); - merge_codegen_units(tcx, &mut initial_partitioning, count); + merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count); debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); } @@ -316,7 +306,7 @@ fn mono_item_visibility( let def_id = tcx.hir().local_def_id(*hir_id); return if tcx.is_reachable_non_generic(def_id) { *can_be_internalized = false; - default_visibility(tcx, def_id, false) + default_visibility(tcx, def_id.to_def_id(), false) } else { Visibility::Hidden }; @@ -464,11 +454,18 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit fn merge_codegen_units<'tcx>( tcx: TyCtxt<'tcx>, initial_partitioning: &mut PreInliningPartitioning<'tcx>, - target_cgu_count: usize, + mut target_cgu_count: usize, ) { assert!(target_cgu_count >= 1); let codegen_units = &mut initial_partitioning.codegen_units; + if tcx.is_compiler_builtins(LOCAL_CRATE) { + // Compiler builtins require some degree of control over how mono items + // are partitioned into compilation units. Provide it by keeping the + // original partitioning when compiling the compiler builtins crate. + target_cgu_count = codegen_units.len(); + } + // Note that at this point in time the `codegen_units` here may not be in a // deterministic order (but we know they're deterministically the same set). // We want this merging to produce a deterministic ordering of codegen units @@ -480,6 +477,10 @@ fn merge_codegen_units<'tcx>( // the stable sort below will keep everything nice and deterministic. codegen_units.sort_by_cached_key(|cgu| cgu.name().as_str()); + // This map keeps track of what got merged into what. + let mut cgu_contents: FxHashMap> = + codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name().as_str()])).collect(); + // Merge the two smallest codegen units until the target size is reached. while codegen_units.len() > target_cgu_count { // Sort small cgus to the back @@ -487,20 +488,67 @@ fn merge_codegen_units<'tcx>( let mut smallest = codegen_units.pop().unwrap(); let second_smallest = codegen_units.last_mut().unwrap(); + // Move the mono-items from `smallest` to `second_smallest` second_smallest.modify_size_estimate(smallest.size_estimate()); for (k, v) in smallest.items_mut().drain() { second_smallest.items_mut().insert(k, v); } + + // Record that `second_smallest` now contains all the stuff that was in + // `smallest` before. + let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap(); + cgu_contents.get_mut(&second_smallest.name()).unwrap().extend(consumed_cgu_names.drain(..)); + debug!( - "CodegenUnit {} merged in to CodegenUnit {}", + "CodegenUnit {} merged into CodegenUnit {}", smallest.name(), second_smallest.name() ); } let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); - for (index, cgu) in codegen_units.iter_mut().enumerate() { - cgu.set_name(numbered_codegen_unit_name(cgu_name_builder, index)); + + if tcx.sess.opts.incremental.is_some() { + // If we are doing incremental compilation, we want CGU names to + // reflect the path of the source level module they correspond to. + // For CGUs that contain the code of multiple modules because of the + // merging done above, we use a concatenation of the names of + // all contained CGUs. + let new_cgu_names: FxHashMap = cgu_contents + .into_iter() + // This `filter` makes sure we only update the name of CGUs that + // were actually modified by merging. + .filter(|(_, cgu_contents)| cgu_contents.len() > 1) + .map(|(current_cgu_name, cgu_contents)| { + let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| &s[..]).collect(); + + // Sort the names, so things are deterministic and easy to + // predict. + cgu_contents.sort(); + + (current_cgu_name, cgu_contents.join("--")) + }) + .collect(); + + for cgu in codegen_units.iter_mut() { + if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) { + if tcx.sess.opts.debugging_opts.human_readable_cgu_names { + cgu.set_name(Symbol::intern(&new_cgu_name)); + } else { + // If we don't require CGU names to be human-readable, we + // use a fixed length hash of the composite CGU name + // instead. + let new_cgu_name = CodegenUnit::mangle_name(&new_cgu_name); + cgu.set_name(Symbol::intern(&new_cgu_name)); + } + } + } + } else { + // If we are compiling non-incrementally we just generate simple CGU + // names containing an index. + for (index, cgu) in codegen_units.iter_mut().enumerate() { + cgu.set_name(numbered_codegen_unit_name(cgu_name_builder, index)); + } } } @@ -707,7 +755,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( Some(def_id) } MonoItem::Static(def_id) => Some(def_id), - MonoItem::GlobalAsm(hir_id) => Some(tcx.hir().local_def_id(hir_id)), + MonoItem::GlobalAsm(hir_id) => Some(tcx.hir().local_def_id(hir_id).to_def_id()), } } @@ -731,7 +779,7 @@ fn compute_codegen_unit_name( cgu_def_id = Some(DefId { krate: def_id.krate, index: CRATE_DEF_INDEX }); } break; - } else if tcx.def_kind(current_def_id) == Some(DefKind::Mod) { + } else if tcx.def_kind(current_def_id) == DefKind::Mod { if cgu_def_id.is_none() { cgu_def_id = Some(current_def_id); } @@ -841,7 +889,7 @@ where fn collect_and_partition_mono_items( tcx: TyCtxt<'_>, cnum: CrateNum, -) -> (Arc, Arc>>>) { +) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'_>]) { assert_eq!(cnum, LOCAL_CRATE); let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items { @@ -879,16 +927,12 @@ fn collect_and_partition_mono_items( let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { sync::join( || { - let strategy = if tcx.sess.opts.incremental.is_some() { - PartitioningStrategy::PerModule - } else { - PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) - }; - - partition(tcx, items.iter().cloned(), strategy, &inlining_map) - .into_iter() - .map(Arc::new) - .collect::>() + &*tcx.arena.alloc_from_iter(partition( + tcx, + items.iter().cloned(), + tcx.sess.codegen_units(), + &inlining_map, + )) }, || assert_symbols_are_distinct(tcx, items.iter()), ) @@ -906,7 +950,7 @@ fn collect_and_partition_mono_items( if tcx.sess.opts.debugging_opts.print_mono_items.is_some() { let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default(); - for cgu in &codegen_units { + for cgu in codegen_units { for (&mono_item, &linkage) in cgu.items() { item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage)); } @@ -954,7 +998,7 @@ fn collect_and_partition_mono_items( } } - (Arc::new(mono_items), Arc::new(codegen_units)) + (tcx.arena.alloc(mono_items), codegen_units) } pub fn provide(providers: &mut Providers<'_>) { @@ -969,7 +1013,6 @@ pub fn provide(providers: &mut Providers<'_>) { let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); all.iter() .find(|cgu| cgu.name() == name) - .cloned() .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name)) }; } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index eeb9d5f5a1fcc..e3982c654d5fa 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -1,10 +1,11 @@ -use rustc::mir::*; -use rustc::ty::layout::VariantIdx; -use rustc::ty::query::Providers; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::FnMutTraitLangItem; +use rustc_middle::mir::*; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_target::abi::VariantIdx; use rustc_index::vec::{Idx, IndexVec}; @@ -26,7 +27,7 @@ pub fn provide(providers: &mut Providers<'_>) { providers.mir_shims = make_shim; } -fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx BodyAndCache<'tcx> { +fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'tcx> { debug!("make_shim({:?})", instance); let mut result = match instance { @@ -47,7 +48,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx let trait_ = tcx.trait_of_item(def_id).unwrap(); let adjustment = match tcx.fn_trait_kind_from_lang_item(trait_) { Some(ty::ClosureKind::FnOnce) => Adjustment::Identity, - Some(ty::ClosureKind::FnMut) | Some(ty::ClosureKind::Fn) => Adjustment::Deref, + Some(ty::ClosureKind::FnMut | ty::ClosureKind::Fn) => Adjustment::Deref, None => bug!("fn pointer {:?} is not an fn", ty), }; // HACK: we need the "real" argument types for the MIR, @@ -70,11 +71,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx build_call_shim(tcx, instance, None, CallKind::Direct(def_id), None) } ty::InstanceDef::ClosureOnceShim { call_once: _ } => { - let fn_mut = tcx.lang_items().fn_mut_trait().unwrap(); + let fn_mut = tcx.require_lang_item(FnMutTraitLangItem, None); let call_mut = tcx .associated_items(fn_mut) .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Method) + .find(|it| it.kind == ty::AssocKind::Fn) .unwrap() .def_id; @@ -117,19 +118,18 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx instance, None, MirPhase::Const, - &[ + &[&[ &add_moves_for_packed_drops::AddMovesForPackedDrops, &no_landing_pads::NoLandingPads::new(tcx), &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::new("make_shim"), &add_call_guards::CriticalCallEdges, - ], + ]], ); debug!("make_shim({:?}) = {:?}", instance, result); - result.ensure_predecessors(); - tcx.arena.alloc(result) + result } #[derive(Copy, Clone, Debug, PartialEq)] @@ -146,33 +146,16 @@ enum CallKind { Direct(DefId), } -fn temp_decl(mutability: Mutability, ty: Ty<'_>, span: Span) -> LocalDecl<'_> { - let source_info = SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span }; - LocalDecl { - mutability, - ty, - user_ty: UserTypeProjections::none(), - source_info, - internal: false, - local_info: LocalInfo::Other, - is_block_tail: None, - } -} - fn local_decls_for_sig<'tcx>( sig: &ty::FnSig<'tcx>, span: Span, ) -> IndexVec> { - iter::once(temp_decl(Mutability::Mut, sig.output(), span)) - .chain(sig.inputs().iter().map(|ity| temp_decl(Mutability::Not, ity, span))) + iter::once(LocalDecl::new(sig.output(), span)) + .chain(sig.inputs().iter().map(|ity| LocalDecl::new(ity, span).immutable())) .collect() } -fn build_drop_shim<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - ty: Option>, -) -> BodyAndCache<'tcx> { +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); // Check if this is a generator, if so, return the drop glue for it @@ -190,7 +173,7 @@ fn build_drop_shim<'tcx>( let sig = tcx.erase_late_bound_regions(&sig); let span = tcx.def_span(def_id); - let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }; + let source_info = SourceInfo::outermost(span); let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); @@ -204,9 +187,7 @@ fn build_drop_shim<'tcx>( block(&mut blocks, TerminatorKind::Goto { target: return_block }); block(&mut blocks, TerminatorKind::Return); - let body = new_body(blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); - - let mut body = BodyAndCache::new(body); + let mut body = new_body(blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); if let Some(..) = ty { // The first argument (index 0), but add 1 for the return value. @@ -230,7 +211,7 @@ fn build_drop_shim<'tcx>( elaborate_drops::elaborate_drop( &mut elaborator, source_info, - &dropee, + dropee, (), return_block, elaborate_drops::Unwind::To(resume_block), @@ -296,7 +277,18 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle { - if let DropFlagMode::Shallow = mode { DropStyle::Static } else { DropStyle::Open } + match mode { + DropFlagMode::Shallow => { + // Drops for the contained fields are "shallow" and "static" - they will simply call + // the field's own drop glue. + DropStyle::Static + } + DropFlagMode::Deep => { + // The top-level drop is "deep" and "open" - it will be elaborated to a drop ladder + // dropping each field contained in the value. + DropStyle::Open + } + } } fn get_drop_flag(&mut self, _path: Self::Path) -> Option> { @@ -320,11 +312,7 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } /// Builds a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`. -fn build_clone_shim<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - self_ty: Ty<'tcx>, -) -> BodyAndCache<'tcx> { +fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> { debug!("build_clone_shim(def_id={:?})", def_id); let param_env = tcx.param_env(def_id); @@ -348,7 +336,7 @@ fn build_clone_shim<'tcx>( _ => bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty), }; - BodyAndCache::new(builder.into_mir()) + builder.into_mir() } struct CloneShimBuilder<'tcx> { @@ -385,7 +373,7 @@ impl CloneShimBuilder<'tcx> { } fn source_info(&self) -> SourceInfo { - SourceInfo { span: self.span, scope: OUTERMOST_SOURCE_SCOPE } + SourceInfo::outermost(self.span) } fn block( @@ -425,7 +413,11 @@ impl CloneShimBuilder<'tcx> { fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> { let span = self.span; - Place::from(self.local_decls.push(temp_decl(mutability, ty, span))) + let mut local = LocalDecl::new(ty, span); + if mutability == Mutability::Not { + local = local.immutable(); + } + Place::from(self.local_decls.push(local)) } fn make_clone_call( @@ -509,7 +501,7 @@ impl CloneShimBuilder<'tcx> { let tcx = self.tcx; let span = self.span; - let beg = self.local_decls.push(temp_decl(Mutability::Mut, tcx.types.usize, span)); + let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span)); let end = self.make_place(Mutability::Not, tcx.types.usize); // BB #0 @@ -538,7 +530,7 @@ impl CloneShimBuilder<'tcx> { // BB #2 // `dest[i] = Clone::clone(src[beg])`; // Goto #3 if ok, #5 if unwinding happens. - let dest_field = self.tcx.mk_place_index(dest.clone(), beg); + let dest_field = self.tcx.mk_place_index(dest, beg); let src_field = self.tcx.mk_place_index(src, beg); self.make_clone_call(dest_field, src_field, ty, BasicBlock::new(3), BasicBlock::new(5)); @@ -564,7 +556,7 @@ impl CloneShimBuilder<'tcx> { // `let mut beg = 0;` // goto #6; let end = beg; - let beg = self.local_decls.push(temp_decl(Mutability::Mut, tcx.types.usize, span)); + let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span)); let init = self.make_statement(StatementKind::Assign(box ( Place::from(beg), Rvalue::Use(Operand::Constant(self.make_usize(0))), @@ -620,9 +612,9 @@ impl CloneShimBuilder<'tcx> { let mut previous_field = None; for (i, ity) in tys.enumerate() { let field = Field::new(i); - let src_field = self.tcx.mk_place_field(src.clone(), field, ity); + let src_field = self.tcx.mk_place_field(src, field, ity); - let dest_field = self.tcx.mk_place_field(dest.clone(), field, ity); + let dest_field = self.tcx.mk_place_field(dest, field, ity); // #(2i + 1) is the cleanup block for the previous clone operation let cleanup_block = self.block_index_offset(1); @@ -633,7 +625,7 @@ impl CloneShimBuilder<'tcx> { // BB #(2i) // `dest.i = Clone::clone(&src.i);` // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens. - self.make_clone_call(dest_field.clone(), src_field, ity, next_block, cleanup_block); + self.make_clone_call(dest_field, src_field, ity, next_block, cleanup_block); // BB #(2i + 1) (cleanup) if let Some((previous_field, previous_cleanup)) = previous_field.take() { @@ -671,7 +663,7 @@ fn build_call_shim<'tcx>( rcvr_adjustment: Option, call_kind: CallKind, untuple_args: Option<&[Ty<'tcx>]>, -) -> BodyAndCache<'tcx> { +) -> Body<'tcx> { debug!( "build_call_shim(instance={:?}, rcvr_adjustment={:?}, \ call_kind={:?}, untuple_args={:?})", @@ -698,7 +690,7 @@ fn build_call_shim<'tcx>( debug!("build_call_shim: sig={:?}", sig); let mut local_decls = local_decls_for_sig(&sig, span); - let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }; + let source_info = SourceInfo::outermost(span); let rcvr_place = || { assert!(rcvr_adjustment.is_some()); @@ -712,14 +704,16 @@ fn build_call_shim<'tcx>( Adjustment::DerefMove => Operand::Move(tcx.mk_place_deref(rcvr_place())), Adjustment::RefMut => { // let rcvr = &mut rcvr; - let ref_rcvr = local_decls.push(temp_decl( - Mutability::Not, - tcx.mk_ref( - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty: sig.inputs()[0], mutbl: hir::Mutability::Mut }, - ), - span, - )); + let ref_rcvr = local_decls.push( + LocalDecl::new( + tcx.mk_ref( + tcx.lifetimes.re_erased, + ty::TypeAndMut { ty: sig.inputs()[0], mutbl: hir::Mutability::Mut }, + ), + span, + ) + .immutable(), + ); let borrow_kind = BorrowKind::Mut { allow_two_phase_borrow: false }; statements.push(Statement { source_info, @@ -835,10 +829,11 @@ fn build_call_shim<'tcx>( if let Abi::RustCall = sig.abi { body.spread_arg = Some(Local::new(sig.inputs().len())); } - BodyAndCache::new(body) + + body } -pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &BodyAndCache<'_> { +pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { debug_assert!(tcx.is_constructor(ctor_id)); let span = @@ -859,7 +854,7 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &BodyAndCache<'_> { let local_decls = local_decls_for_sig(&sig, span); - let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }; + let source_info = SourceInfo::outermost(span); let variant_index = if adt_def.is_enum() { adt_def.variant_index_with_ctor_id(ctor_id) @@ -905,7 +900,5 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &BodyAndCache<'_> { |_, _| Ok(()), ); - let mut body = BodyAndCache::new(body); - body.ensure_predecessors(); - tcx.arena.alloc(body) + body } diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs index c979b569ec5c7..33859115359e0 100644 --- a/src/librustc_mir/transform/add_call_guards.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -1,7 +1,7 @@ use crate::transform::{MirPass, MirSource}; -use rustc::mir::*; -use rustc::ty::TyCtxt; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; #[derive(PartialEq)] pub enum AddCallGuards { @@ -31,13 +31,13 @@ pub use self::AddCallGuards::*; */ impl<'tcx> MirPass<'tcx> for AddCallGuards { - fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { self.add_call_guards(body); } } impl AddCallGuards { - pub fn add_call_guards(&self, body: &mut BodyAndCache<'_>) { + pub fn add_call_guards(&self, body: &mut Body<'_>) { let pred_count: IndexVec<_, _> = body.predecessors().iter().map(|ps| ps.len()).collect(); // We need a place to store the new blocks generated diff --git a/src/librustc_mir/transform/add_moves_for_packed_drops.rs b/src/librustc_mir/transform/add_moves_for_packed_drops.rs index 38db9e5195937..39ce2340aed21 100644 --- a/src/librustc_mir/transform/add_moves_for_packed_drops.rs +++ b/src/librustc_mir/transform/add_moves_for_packed_drops.rs @@ -1,6 +1,6 @@ -use rustc::mir::*; -use rustc::ty::TyCtxt; use rustc_hir::def_id::DefId; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; use crate::transform::{MirPass, MirSource}; use crate::util; @@ -40,17 +40,13 @@ use crate::util::patch::MirPatch; pub struct AddMovesForPackedDrops; impl<'tcx> MirPass<'tcx> for AddMovesForPackedDrops { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { debug!("add_moves_for_packed_drops({:?} @ {:?})", src, body.span); add_moves_for_packed_drops(tcx, body, src.def_id()); } } -pub fn add_moves_for_packed_drops<'tcx>( - tcx: TyCtxt<'tcx>, - body: &mut BodyAndCache<'tcx>, - def_id: DefId, -) { +pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, def_id: DefId) { let patch = add_moves_for_packed_drops_patch(tcx, body, def_id); patch.apply(body); } @@ -68,7 +64,7 @@ fn add_moves_for_packed_drops_patch<'tcx>( let terminator = data.terminator(); match terminator.kind { - TerminatorKind::Drop { ref location, .. } + TerminatorKind::Drop { location, .. } if util::is_disaligned(tcx, body, param_env, location) => { add_move_for_packed_drop(tcx, body, &mut patch, terminator, loc, data.is_cleanup); diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs index aa9cad7ffc19f..baa3e5e1581c5 100644 --- a/src/librustc_mir/transform/add_retag.rs +++ b/src/librustc_mir/transform/add_retag.rs @@ -5,8 +5,8 @@ //! normal MIR semantics. use crate::transform::{MirPass, MirSource}; -use rustc::mir::*; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, Ty, TyCtxt}; pub struct AddRetag; @@ -58,10 +58,14 @@ fn may_be_reference(ty: Ty<'tcx>) -> bool { } impl<'tcx> MirPass<'tcx> for AddRetag { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { if !tcx.sess.opts.debugging_opts.mir_emit_retag { return; } + + // We need an `AllCallEdges` pass before we can do any work. + super::add_call_guards::AllCallEdges.run_pass(tcx, src, body); + let (span, arg_count) = (body.span, body.arg_count); let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let needs_retag = |place: &Place<'tcx>| { @@ -73,11 +77,9 @@ impl<'tcx> MirPass<'tcx> for AddRetag { // PART 1 // Retag arguments at the beginning of the start block. { - let source_info = SourceInfo { - scope: OUTERMOST_SOURCE_SCOPE, - span, // FIXME: Consider using just the span covering the function - // argument declaration. - }; + // FIXME: Consider using just the span covering the function + // argument declaration. + let source_info = SourceInfo::outermost(span); // Gather all arguments, skip return value. let places = local_decls .iter_enumerated() diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index b7383663932a4..7c439f80ef6ad 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -4,109 +4,52 @@ //! 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::mir; -use rustc::ty::{self, TyCtxt}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; - -use std::fmt; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::mir; +use rustc_middle::ty::{self, TyCtxt}; pub use self::qualifs::Qualif; -pub mod ops; +mod ops; pub mod qualifs; mod resolver; pub mod validation; /// Information about the item currently being const-checked, as well as a reference to the global /// context. -pub struct Item<'mir, 'tcx> { - pub body: mir::ReadOnlyBodyAndCache<'mir, 'tcx>, +pub struct ConstCx<'mir, 'tcx> { + pub body: &'mir mir::Body<'tcx>, pub tcx: TyCtxt<'tcx>, pub def_id: DefId, pub param_env: ty::ParamEnv<'tcx>, - pub const_kind: Option, + pub const_kind: Option, } -impl Item<'mir, 'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - def_id: DefId, - body: mir::ReadOnlyBodyAndCache<'mir, 'tcx>, - ) -> Self { +impl ConstCx<'mir, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'mir mir::Body<'tcx>) -> Self { let param_env = tcx.param_env(def_id); - let const_kind = ConstKind::for_item(tcx, def_id); + Self::new_with_param_env(tcx, def_id, body, param_env) + } - Item { body, tcx, def_id, param_env, const_kind } + pub fn new_with_param_env( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + body: &'mir mir::Body<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Self { + let const_kind = tcx.hir().body_const_context(def_id); + ConstCx { body, tcx, def_id: def_id.to_def_id(), param_env, const_kind } } /// Returns the kind of const context this `Item` represents (`const`, `static`, etc.). /// /// Panics if this `Item` is not const. - pub fn const_kind(&self) -> ConstKind { + pub fn const_kind(&self) -> hir::ConstContext { self.const_kind.expect("`const_kind` must not be called on a non-const fn") } } -/// The kinds of items which require compile-time evaluation. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ConstKind { - /// A `static` item. - Static, - /// A `static mut` item. - StaticMut, - /// A `const fn` item. - ConstFn, - /// A `const` item or an anonymous constant (e.g. in array lengths). - Const, -} - -impl ConstKind { - /// Returns the validation mode for the item with the given `DefId`, or `None` if this item - /// does not require validation (e.g. a non-const `fn`). - pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option { - use hir::BodyOwnerKind as HirKind; - - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - - let mode = match tcx.hir().body_owner_kind(hir_id) { - HirKind::Closure => return None, - - // Note: this is deliberately checking for `is_const_fn_raw`, as the `is_const_fn` - // checks take into account the `rustc_const_unstable` attribute combined with enabled - // feature gates. Otherwise, const qualification would _not check_ whether this - // function body follows the `const fn` rules, as an unstable `const fn` would - // be considered "not const". More details are available in issue #67053. - HirKind::Fn if tcx.is_const_fn_raw(def_id) => ConstKind::ConstFn, - HirKind::Fn => return None, - - HirKind::Const => ConstKind::Const, - - HirKind::Static(hir::Mutability::Not) => ConstKind::Static, - HirKind::Static(hir::Mutability::Mut) => ConstKind::StaticMut, - }; - - Some(mode) - } - - pub fn is_static(self) -> bool { - match self { - ConstKind::Static | ConstKind::StaticMut => true, - ConstKind::ConstFn | ConstKind::Const => false, - } - } -} - -impl fmt::Display for ConstKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ConstKind::Const => write!(f, "constant"), - ConstKind::Static | ConstKind::StaticMut => write!(f, "static"), - ConstKind::ConstFn => write!(f, "constant function"), - } - } -} - /// Returns `true` if this `DefId` points to one of the official `panic` lang items. pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn() diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index af7af7388bd28..28743ee8e3636 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -1,13 +1,14 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use rustc_errors::struct_span_err; +use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use super::{ConstKind, Item}; +use super::ConstCx; /// An operation that is not *always* allowed in a const context. pub trait NonConstOp: std::fmt::Debug { @@ -27,19 +28,22 @@ pub trait NonConstOp: std::fmt::Debug { /// /// By default, it returns `true` if and only if this operation has a corresponding feature /// gate and that gate is enabled. - fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool { - Self::feature_gate().map_or(false, |gate| item.tcx.features().enabled(gate)) + fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { + Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate)) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let mut err = struct_span_err!( - item.tcx.sess, + ccx.tcx.sess, span, E0019, "{} contains unimplemented expression type", - item.const_kind() + ccx.const_kind() ); - if item.tcx.sess.teach(&err.get_code().unwrap()) { + if let Some(feat) = Self::feature_gate() { + err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat)); + } + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "A function call isn't allowed in the const's initialization expression \ because the expression's value must be known at compile-time.", @@ -53,22 +57,13 @@ pub trait NonConstOp: std::fmt::Debug { } } -/// A `Downcast` projection. -#[derive(Debug)] -pub struct Downcast; -impl NonConstOp for Downcast { - fn feature_gate() -> Option { - Some(sym::const_if_match) - } -} - /// A function call where the callee is a pointer. #[derive(Debug)] pub struct FnCallIndirect; impl NonConstOp for FnCallIndirect { - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let mut err = - item.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn"); + ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn"); err.emit(); } } @@ -77,41 +72,31 @@ impl NonConstOp for FnCallIndirect { #[derive(Debug)] pub struct FnCallNonConst(pub DefId); impl NonConstOp for FnCallNonConst { - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let mut err = struct_span_err!( - item.tcx.sess, + ccx.tcx.sess, span, E0015, "calls in {}s are limited to constant functions, \ tuple structs and tuple variants", - item.const_kind(), + ccx.const_kind(), ); err.emit(); } } -/// A function call where the callee is not a function definition or function pointer, e.g. a -/// closure. -/// -/// This can be subdivided in the future to produce a better error message. -#[derive(Debug)] -pub struct FnCallOther; -impl NonConstOp for FnCallOther { - const IS_SUPPORTED_IN_MIRI: bool = false; -} - /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function. /// /// Contains the name of the feature that would allow the use of this function. #[derive(Debug)] pub struct FnCallUnstable(pub DefId, pub Symbol); impl NonConstOp for FnCallUnstable { - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let FnCallUnstable(def_id, feature) = *self; - let mut err = item.tcx.sess.struct_span_err( + let mut err = ccx.tcx.sess.struct_span_err( span, - &format!("`{}` is not yet stable as a const fn", item.tcx.def_path_str(def_id)), + &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)), ); if nightly_options::is_nightly_build() { err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature)); @@ -123,18 +108,16 @@ impl NonConstOp for FnCallUnstable { #[derive(Debug)] pub struct HeapAllocation; impl NonConstOp for HeapAllocation { - const IS_SUPPORTED_IN_MIRI: bool = false; - - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let mut err = struct_span_err!( - item.tcx.sess, + ccx.tcx.sess, span, E0010, "allocations are not allowed in {}s", - item.const_kind() + ccx.const_kind() ); - err.span_label(span, format!("allocation not allowed in {}s", item.const_kind())); - if item.tcx.sess.teach(&err.get_code().unwrap()) { + err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind())); + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "The value of statics and constants must be known at compile time, \ and they live for the entire lifetime of a program. Creating a boxed \ @@ -153,23 +136,27 @@ impl NonConstOp for IfOrMatch { Some(sym::const_if_match) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { // This should be caught by the HIR const-checker. - item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context"); + ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context"); } } +#[derive(Debug)] +pub struct InlineAsm; +impl NonConstOp for InlineAsm {} + #[derive(Debug)] pub struct LiveDrop; impl NonConstOp for LiveDrop { - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { struct_span_err!( - item.tcx.sess, + ccx.tcx.sess, span, E0493, "destructors cannot be evaluated at compile-time" ) - .span_label(span, format!("{}s cannot evaluate destructors", item.const_kind())) + .span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind())) .emit(); } } @@ -181,18 +168,18 @@ impl NonConstOp for Loop { Some(sym::const_loop) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { // This should be caught by the HIR const-checker. - item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context"); + ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context"); } } #[derive(Debug)] pub struct CellBorrow; impl NonConstOp for CellBorrow { - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { struct_span_err!( - item.tcx.sess, + ccx.tcx.sess, span, E0492, "cannot borrow a constant which may contain \ @@ -209,19 +196,19 @@ impl NonConstOp for MutBorrow { Some(sym::const_mut_refs) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let mut err = feature_err( - &item.tcx.sess.parse_sess, + &ccx.tcx.sess.parse_sess, sym::const_mut_refs, span, &format!( "references in {}s may only refer \ to immutable values", - item.const_kind() + ccx.const_kind() ), ); - err.span_label(span, format!("{}s require immutable values", item.const_kind())); - if item.tcx.sess.teach(&err.get_code().unwrap()) { + err.span_label(span, format!("{}s require immutable values", ccx.const_kind())); + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "References in statics and constants may only refer \ to immutable values.\n\n\ @@ -244,12 +231,12 @@ impl NonConstOp for MutAddressOf { Some(sym::const_mut_refs) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { feature_err( - &item.tcx.sess.parse_sess, + &ccx.tcx.sess.parse_sess, sym::const_mut_refs, span, - &format!("`&raw mut` is not allowed in {}s", item.const_kind()), + &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()), ) .emit(); } @@ -270,12 +257,12 @@ impl NonConstOp for Panic { Some(sym::const_panic) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { feature_err( - &item.tcx.sess.parse_sess, + &ccx.tcx.sess.parse_sess, sym::const_panic, span, - &format!("panicking in {}s is unstable", item.const_kind()), + &format!("panicking in {}s is unstable", ccx.const_kind()), ) .emit(); } @@ -288,12 +275,12 @@ impl NonConstOp for RawPtrComparison { Some(sym::const_compare_raw_pointers) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { feature_err( - &item.tcx.sess.parse_sess, + &ccx.tcx.sess.parse_sess, sym::const_compare_raw_pointers, span, - &format!("comparing raw pointers inside {}", item.const_kind()), + &format!("comparing raw pointers inside {}", ccx.const_kind()), ) .emit(); } @@ -306,12 +293,12 @@ impl NonConstOp for RawPtrDeref { Some(sym::const_raw_ptr_deref) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { feature_err( - &item.tcx.sess.parse_sess, + &ccx.tcx.sess.parse_sess, sym::const_raw_ptr_deref, span, - &format!("dereferencing raw pointers in {}s is unstable", item.const_kind(),), + &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),), ) .emit(); } @@ -324,12 +311,12 @@ impl NonConstOp for RawPtrToIntCast { Some(sym::const_raw_ptr_to_usize_cast) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { feature_err( - &item.tcx.sess.parse_sess, + &ccx.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast, span, - &format!("casting pointers to integers in {}s is unstable", item.const_kind(),), + &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),), ) .emit(); } @@ -339,22 +326,22 @@ impl NonConstOp for RawPtrToIntCast { #[derive(Debug)] pub struct StaticAccess; impl NonConstOp for StaticAccess { - fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool { - item.const_kind().is_static() + fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { + matches!(ccx.const_kind(), hir::ConstContext::Static(_)) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let mut err = struct_span_err!( - item.tcx.sess, + ccx.tcx.sess, span, E0013, "{}s cannot refer to statics", - item.const_kind() + ccx.const_kind() ); err.help( "consider extracting the value of the `static` to a `const`, and referring to that", ); - if item.tcx.sess.teach(&err.get_code().unwrap()) { + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "`static` and `const` variables can refer to other `const` variables. \ A `const` variable, however, cannot refer to a `static` variable.", @@ -371,9 +358,9 @@ pub struct ThreadLocalAccess; impl NonConstOp for ThreadLocalAccess { const IS_SUPPORTED_IN_MIRI: bool = false; - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { struct_span_err!( - item.tcx.sess, + ccx.tcx.sess, span, E0625, "thread-local statics cannot be \ @@ -386,19 +373,19 @@ impl NonConstOp for ThreadLocalAccess { #[derive(Debug)] pub struct UnionAccess; impl NonConstOp for UnionAccess { - fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool { + fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { // Union accesses are stable in all contexts except `const fn`. - item.const_kind() != ConstKind::ConstFn - || item.tcx.features().enabled(Self::feature_gate().unwrap()) + ccx.const_kind() != hir::ConstContext::ConstFn + || ccx.tcx.features().enabled(Self::feature_gate().unwrap()) } fn feature_gate() -> Option { Some(sym::const_fn_union) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { feature_err( - &item.tcx.sess.parse_sess, + &ccx.tcx.sess.parse_sess, sym::const_fn_union, span, "unions in const fn are unstable", diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index 9359ec16533a5..fc6860b40e8d2 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -2,16 +2,19 @@ //! //! See the `Qualif` trait for more info. -use rustc::mir::*; -use rustc::ty::{self, AdtDef, Ty}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits; -use super::Item as ConstCx; +use super::ConstCx; pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs { ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), + custom_eq: CustomEq::in_any_value_of_ty(cx, ty), } } @@ -53,7 +56,11 @@ pub trait Qualif { /// with a custom `Drop` impl is inherently `NeedsDrop`. /// /// Returning `true` for `in_adt_inherently` but `false` for `in_any_value_of_ty` is unsound. - fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool; + fn in_adt_inherently( + cx: &ConstCx<'_, 'tcx>, + adt: &'tcx AdtDef, + substs: SubstsRef<'tcx>, + ) -> bool; } /// Constant containing interior mutability (`UnsafeCell`). @@ -74,7 +81,7 @@ impl Qualif for HasMutInterior { !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) } - fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool { + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently. // It arises structurally for all other types. Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type() @@ -99,11 +106,44 @@ impl Qualif for NeedsDrop { ty.needs_drop(cx.tcx, cx.param_env) } - fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool { + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { adt.has_dtor(cx.tcx) } } +/// A constant that cannot be used as part of a pattern in a `match` expression. +pub struct CustomEq; + +impl Qualif for CustomEq { + const ANALYSIS_NAME: &'static str = "flow_custom_eq"; + + fn in_qualifs(qualifs: &ConstQualifs) -> bool { + qualifs.custom_eq + } + + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { + // If *any* component of a composite data type does not implement `Structural{Partial,}Eq`, + // we know that at least some values of that type are not structural-match. I say "some" + // because that component may be part of an enum variant (e.g., + // `Option::::Some`), in which case some values of this type may be + // structural-match (`Option::None`). + let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id.as_local().unwrap()); + traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some() + } + + fn in_adt_inherently( + cx: &ConstCx<'_, 'tcx>, + adt: &'tcx AdtDef, + substs: SubstsRef<'tcx>, + ) -> bool { + let ty = cx.tcx.mk_ty(ty::Adt(adt, substs)); + let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id.as_local().unwrap()); + cx.tcx + .infer_ctxt() + .enter(|infcx| !traits::type_marked_structural(id, cx.body.span, &infcx, ty)) + } +} + // FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return. /// Returns `true` if this `Rvalue` contains qualif `Q`. @@ -113,7 +153,7 @@ where F: FnMut(Local) -> bool, { match rvalue { - Rvalue::NullaryOp(..) => Q::in_any_value_of_ty(cx, rvalue.ty(*cx.body, cx.tcx)), + Rvalue::NullaryOp(..) => Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)), Rvalue::Discriminant(place) | Rvalue::Len(place) => { in_place::(cx, in_local, place.as_ref()) @@ -131,7 +171,7 @@ where Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { // Special-case reborrows to be more like a copy of the reference. if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, *cx.body, cx.tcx).ty; + let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty; if let ty::Ref(..) = base_ty.kind { return in_place::( cx, @@ -147,8 +187,8 @@ where Rvalue::Aggregate(kind, operands) => { // Return early if we know that the struct or enum being constructed is always // qualified. - if let AggregateKind::Adt(def, ..) = **kind { - if Q::in_adt_inherently(cx, def) { + if let AggregateKind::Adt(def, _, substs, ..) = **kind { + if Q::in_adt_inherently(cx, def, substs) { return true; } } @@ -178,7 +218,7 @@ where | ProjectionElem::Index(_) => {} } - let base_ty = Place::ty_from(place.local, proj_base, *cx.body, cx.tcx); + let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx); let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty; if !Q::in_any_value_of_ty(cx, proj_ty) { return false; diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs index e42f64b5c7384..a81d7a23be2fb 100644 --- a/src/librustc_mir/transform/check_consts/resolver.rs +++ b/src/librustc_mir/transform/check_consts/resolver.rs @@ -2,14 +2,14 @@ //! //! This contains the dataflow analysis used to track `Qualif`s on complex control-flow graphs. -use rustc::mir::visit::Visitor; -use rustc::mir::{self, BasicBlock, Local, Location}; use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{self, BasicBlock, Local, Location}; use std::marker::PhantomData; -use super::{qualifs, Item, Qualif}; -use crate::dataflow::{self as old_dataflow, generic as dataflow}; +use super::{qualifs, ConstCx, Qualif}; +use crate::dataflow; /// A `Visitor` that propagates qualifs between locals. This defines the transfer function of /// `FlowSensitiveAnalysis`. @@ -18,7 +18,7 @@ use crate::dataflow::{self as old_dataflow, generic as dataflow}; /// the `MaybeMutBorrowedLocals` dataflow pass to see if a `Local` may have become qualified via /// an indirect assignment or function call. struct TransferFunction<'a, 'mir, 'tcx, Q> { - item: &'a Item<'mir, 'tcx>, + ccx: &'a ConstCx<'mir, 'tcx>, qualifs_per_local: &'a mut BitSet, _qualif: PhantomData, @@ -28,16 +28,16 @@ impl TransferFunction<'a, 'mir, 'tcx, Q> where Q: Qualif, { - fn new(item: &'a Item<'mir, 'tcx>, qualifs_per_local: &'a mut BitSet) -> Self { - TransferFunction { item, qualifs_per_local, _qualif: PhantomData } + fn new(ccx: &'a ConstCx<'mir, 'tcx>, qualifs_per_local: &'a mut BitSet) -> Self { + TransferFunction { ccx, qualifs_per_local, _qualif: PhantomData } } fn initialize_state(&mut self) { self.qualifs_per_local.clear(); - for arg in self.item.body.args_iter() { - let arg_ty = self.item.body.local_decls[arg].ty; - if Q::in_any_value_of_ty(self.item, arg_ty) { + for arg in self.ccx.body.args_iter() { + let arg_ty = self.ccx.body.local_decls[arg].ty; + if Q::in_any_value_of_ty(self.ccx, arg_ty) { self.qualifs_per_local.insert(arg); } } @@ -68,15 +68,15 @@ where _block: BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], - return_place: &mir::Place<'tcx>, + return_place: mir::Place<'tcx>, ) { // We cannot reason about another function's internals, so use conservative type-based // qualification for the result of a function call. - let return_ty = return_place.ty(*self.item.body, self.item.tcx).ty; - let qualif = Q::in_any_value_of_ty(self.item, return_ty); + let return_ty = return_place.ty(self.ccx.body, self.ccx.tcx).ty; + let qualif = Q::in_any_value_of_ty(self.ccx, return_ty); if !return_place.is_indirect() { - self.assign_qualif_direct(return_place, qualif); + self.assign_qualif_direct(&return_place, qualif); } } } @@ -108,7 +108,7 @@ where location: Location, ) { let qualif = qualifs::in_rvalue::( - self.item, + self.ccx, &mut |l| self.qualifs_per_local.contains(l), rvalue, ); @@ -127,7 +127,7 @@ where if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind { let qualif = qualifs::in_operand::( - self.item, + self.ccx, &mut |l| self.qualifs_per_local.contains(l), value, ); @@ -145,7 +145,7 @@ where /// The dataflow analysis used to propagate qualifs on arbitrary CFGs. pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> { - item: &'a Item<'mir, 'tcx>, + ccx: &'a ConstCx<'mir, 'tcx>, _qualif: PhantomData, } @@ -153,19 +153,19 @@ impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> where Q: Qualif, { - pub(super) fn new(_: Q, item: &'a Item<'mir, 'tcx>) -> Self { - FlowSensitiveAnalysis { item, _qualif: PhantomData } + pub(super) fn new(_: Q, ccx: &'a ConstCx<'mir, 'tcx>) -> Self { + FlowSensitiveAnalysis { ccx, _qualif: PhantomData } } fn transfer_function( &self, state: &'a mut BitSet, ) -> TransferFunction<'a, 'mir, 'tcx, Q> { - TransferFunction::::new(self.item, state) + TransferFunction::::new(self.ccx, state) } } -impl old_dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> { +impl dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> { const BOTTOM_VALUE: bool = false; } @@ -214,7 +214,7 @@ where block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], - return_place: &mir::Place<'tcx>, + return_place: mir::Place<'tcx>, ) { self.transfer_function(state).apply_call_return_effect(block, func, args, return_place) } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index be461c0e03d86..987c9e24fc3c3 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -1,15 +1,13 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. -use rustc::middle::lang_items; -use rustc::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; -use rustc::mir::*; -use rustc::ty::cast::CastTy; -use rustc::ty::{self, Instance, InstanceDef, TyCtxt}; use rustc_errors::struct_span_err; +use rustc_hir::{self as hir, lang_items}; use rustc_hir::{def_id::DefId, HirId}; -use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; -use rustc_span::symbol::sym; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::cast::CastTy; +use rustc_middle::ty::{self, Instance, InstanceDef, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, TraitEngine}; @@ -18,85 +16,115 @@ use std::borrow::Cow; use std::ops::Deref; use super::ops::{self, NonConstOp}; -use super::qualifs::{self, HasMutInterior, NeedsDrop}; +use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{is_lang_panic_fn, ConstKind, Item, Qualif}; +use super::{is_lang_panic_fn, ConstCx, Qualif}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; -use crate::dataflow::generic::{self as dataflow, Analysis}; -use crate::dataflow::MaybeMutBorrowedLocals; +use crate::dataflow::impls::MaybeMutBorrowedLocals; +use crate::dataflow::{self, Analysis}; // We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated // through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals` // kills locals upon `StorageDead` because a local will never be used after a `StorageDead`. -pub type IndirectlyMutableResults<'mir, 'tcx> = +type IndirectlyMutableResults<'mir, 'tcx> = dataflow::ResultsCursor<'mir, 'tcx, MaybeMutBorrowedLocals<'mir, 'tcx>>; -struct QualifCursor<'a, 'mir, 'tcx, Q: Qualif> { - cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>, - in_any_value_of_ty: BitSet, -} - -impl QualifCursor<'a, 'mir, 'tcx, Q> { - pub fn new(q: Q, item: &'a Item<'mir, 'tcx>) -> Self { - let cursor = FlowSensitiveAnalysis::new(q, item) - .into_engine(item.tcx, &item.body, item.def_id) - .iterate_to_fixpoint() - .into_results_cursor(*item.body); - - let mut in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len()); - for (local, decl) in item.body.local_decls.iter_enumerated() { - if Q::in_any_value_of_ty(item, decl.ty) { - in_any_value_of_ty.insert(local); - } - } - - QualifCursor { cursor, in_any_value_of_ty } - } -} +type QualifResults<'mir, 'tcx, Q> = + dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; -pub struct Qualifs<'a, 'mir, 'tcx> { - has_mut_interior: QualifCursor<'a, 'mir, 'tcx, HasMutInterior>, - needs_drop: QualifCursor<'a, 'mir, 'tcx, NeedsDrop>, - indirectly_mutable: IndirectlyMutableResults<'mir, 'tcx>, +#[derive(Default)] +pub struct Qualifs<'mir, 'tcx> { + has_mut_interior: Option>, + needs_drop: Option>, + indirectly_mutable: Option>, } -impl Qualifs<'a, 'mir, 'tcx> { - fn indirectly_mutable(&mut self, local: Local, location: Location) -> bool { - self.indirectly_mutable.seek_before(location); - self.indirectly_mutable.get().contains(local) +impl Qualifs<'mir, 'tcx> { + fn indirectly_mutable( + &mut self, + ccx: &'mir ConstCx<'mir, 'tcx>, + local: Local, + location: Location, + ) -> bool { + let indirectly_mutable = self.indirectly_mutable.get_or_insert_with(|| { + let ConstCx { tcx, body, def_id, param_env, .. } = *ccx; + + // We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not + // allowed in a const. + // + // FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this + // without breaking stable code? + MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env) + .unsound_ignore_borrow_on_drop() + .into_engine(tcx, &body, def_id) + .iterate_to_fixpoint() + .into_results_cursor(&body) + }); + + indirectly_mutable.seek_before_primary_effect(location); + indirectly_mutable.get().contains(local) } /// Returns `true` if `local` is `NeedsDrop` at the given `Location`. /// /// Only updates the cursor if absolutely necessary - fn needs_drop(&mut self, local: Local, location: Location) -> bool { - if !self.needs_drop.in_any_value_of_ty.contains(local) { + fn needs_drop( + &mut self, + ccx: &'mir ConstCx<'mir, 'tcx>, + local: Local, + location: Location, + ) -> bool { + let ty = ccx.body.local_decls[local].ty; + if !NeedsDrop::in_any_value_of_ty(ccx, ty) { return false; } - self.needs_drop.cursor.seek_before(location); - self.needs_drop.cursor.get().contains(local) || self.indirectly_mutable(local, location) + let needs_drop = self.needs_drop.get_or_insert_with(|| { + let ConstCx { tcx, body, def_id, .. } = *ccx; + + FlowSensitiveAnalysis::new(NeedsDrop, ccx) + .into_engine(tcx, &body, def_id) + .iterate_to_fixpoint() + .into_results_cursor(&body) + }); + + needs_drop.seek_before_primary_effect(location); + needs_drop.get().contains(local) || self.indirectly_mutable(ccx, local, location) } /// Returns `true` if `local` is `HasMutInterior` at the given `Location`. /// /// Only updates the cursor if absolutely necessary. - fn has_mut_interior(&mut self, local: Local, location: Location) -> bool { - if !self.has_mut_interior.in_any_value_of_ty.contains(local) { + fn has_mut_interior( + &mut self, + ccx: &'mir ConstCx<'mir, 'tcx>, + local: Local, + location: Location, + ) -> bool { + let ty = ccx.body.local_decls[local].ty; + if !HasMutInterior::in_any_value_of_ty(ccx, ty) { return false; } - self.has_mut_interior.cursor.seek_before(location); - self.has_mut_interior.cursor.get().contains(local) - || self.indirectly_mutable(local, location) + let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| { + let ConstCx { tcx, body, def_id, .. } = *ccx; + + FlowSensitiveAnalysis::new(HasMutInterior, ccx) + .into_engine(tcx, &body, def_id) + .iterate_to_fixpoint() + .into_results_cursor(&body) + }); + + has_mut_interior.seek_before_primary_effect(location); + has_mut_interior.get().contains(local) || self.indirectly_mutable(ccx, local, location) } - fn in_return_place(&mut self, item: &Item<'_, 'tcx>) -> ConstQualifs { + fn in_return_place(&mut self, ccx: &'mir ConstCx<'mir, 'tcx>) -> ConstQualifs { // Find the `Return` terminator if one exists. // // If no `Return` terminator exists, this MIR is divergent. Just return the conservative // qualifs for the return type. - let return_block = item + let return_block = ccx .body .basic_blocks() .iter_enumerated() @@ -107,62 +135,66 @@ impl Qualifs<'a, 'mir, 'tcx> { .map(|(bb, _)| bb); let return_block = match return_block { - None => return qualifs::in_any_value_of_ty(item, item.body.return_ty()), + None => return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty()), Some(bb) => bb, }; - let return_loc = item.body.terminator_loc(return_block); + let return_loc = ccx.body.terminator_loc(return_block); + + let custom_eq = match ccx.const_kind() { + // We don't care whether a `const fn` returns a value that is not structurally + // matchable. Functions calls are opaque and always use type-based qualification, so + // this value should never be used. + hir::ConstContext::ConstFn => true, + + // If we know that all values of the return type are structurally matchable, there's no + // need to run dataflow. + _ if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => false, + + hir::ConstContext::Const | hir::ConstContext::Static(_) => { + let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx) + .into_engine(ccx.tcx, &ccx.body, ccx.def_id) + .iterate_to_fixpoint() + .into_results_cursor(&ccx.body); + + cursor.seek_after_primary_effect(return_loc); + cursor.contains(RETURN_PLACE) + } + }; ConstQualifs { - needs_drop: self.needs_drop(RETURN_PLACE, return_loc), - has_mut_interior: self.has_mut_interior(RETURN_PLACE, return_loc), + needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc), + has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc), + custom_eq, } } } -pub struct Validator<'a, 'mir, 'tcx> { - item: &'a Item<'mir, 'tcx>, - qualifs: Qualifs<'a, 'mir, 'tcx>, +pub struct Validator<'mir, 'tcx> { + ccx: &'mir ConstCx<'mir, 'tcx>, + qualifs: Qualifs<'mir, 'tcx>, /// The span of the current statement. span: Span, } -impl Deref for Validator<'_, 'mir, 'tcx> { - type Target = Item<'mir, 'tcx>; +impl Deref for Validator<'mir, 'tcx> { + type Target = ConstCx<'mir, 'tcx>; fn deref(&self) -> &Self::Target { - &self.item + &self.ccx } } -impl Validator<'a, 'mir, 'tcx> { - pub fn new(item: &'a Item<'mir, 'tcx>) -> Self { - let Item { tcx, body, def_id, param_env, .. } = *item; - - let needs_drop = QualifCursor::new(NeedsDrop, item); - let has_mut_interior = QualifCursor::new(HasMutInterior, item); - - // We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not - // allowed in a const. - // - // FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this - // without breaking stable code? - let indirectly_mutable = MaybeMutBorrowedLocals::mut_borrows_only(tcx, *body, param_env) - .unsound_ignore_borrow_on_drop() - .into_engine(tcx, *body, def_id) - .iterate_to_fixpoint() - .into_results_cursor(*body); - - let qualifs = Qualifs { needs_drop, has_mut_interior, indirectly_mutable }; - - Validator { span: item.body.span, item, qualifs } +impl Validator<'mir, 'tcx> { + pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self { + Validator { span: ccx.body.span, ccx, qualifs: Default::default() } } pub fn check_body(&mut self) { - let Item { tcx, body, def_id, const_kind, .. } = *self.item; + let ConstCx { tcx, body, def_id, const_kind, .. } = *self.ccx; - let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn) + let use_min_const_fn_checks = (const_kind == Some(hir::ConstContext::ConstFn) && crate::const_eval::is_min_const_fn(tcx, def_id)) && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; @@ -175,7 +207,7 @@ impl Validator<'a, 'mir, 'tcx> { } } - check_short_circuiting_in_const_local(self.item); + check_short_circuiting_in_const_local(self.ccx); if body.is_cfg_cyclic() { // We can't provide a good span for the error here, but this should be caught by the @@ -183,20 +215,21 @@ impl Validator<'a, 'mir, 'tcx> { self.check_op_spanned(ops::Loop, body.span); } - self.visit_body(body); + self.visit_body(&body); // Ensure that the end result is `Sync` in a non-thread local `static`. - let should_check_for_sync = - const_kind == Some(ConstKind::Static) && !tcx.has_attr(def_id, sym::thread_local); + let should_check_for_sync = const_kind + == Some(hir::ConstContext::Static(hir::Mutability::Not)) + && !tcx.is_thread_local_static(def_id); if should_check_for_sync { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); check_return_ty_is_sync(tcx, &body, hir_id); } } pub fn qualifs_in_return_place(&mut self) -> ConstQualifs { - self.qualifs.in_return_place(self.item) + self.qualifs.in_return_place(self.ccx) } /// Emits an error at the given `span` if an expression cannot be evaluated in the current @@ -205,18 +238,18 @@ impl Validator<'a, 'mir, 'tcx> { where O: NonConstOp, { - trace!("check_op: op={:?}", op); + debug!("check_op: op={:?}", op); if op.is_allowed_in_item(self) { return; } - // If an operation is supported in miri (and is not already controlled by a feature gate) it - // can be turned on with `-Zunleash-the-miri-inside-of-you`. - let is_unleashable = O::IS_SUPPORTED_IN_MIRI && O::feature_gate().is_none(); + // If an operation is supported in miri it can be turned on with + // `-Zunleash-the-miri-inside-of-you`. + let is_unleashable = O::IS_SUPPORTED_IN_MIRI; if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { - self.tcx.sess.span_warn(span, "skipping const checks"); + self.tcx.sess.miri_unleashed_feature(span, O::feature_gate()); return; } @@ -230,8 +263,7 @@ impl Validator<'a, 'mir, 'tcx> { } fn check_static(&mut self, def_id: DefId, span: Span) { - let is_thread_local = self.tcx.has_attr(def_id, sym::thread_local); - if is_thread_local { + if self.tcx.is_thread_local_static(def_id) { self.check_op_spanned(ops::ThreadLocalAccess, span) } else { self.check_op_spanned(ops::StaticAccess, span) @@ -239,7 +271,7 @@ impl Validator<'a, 'mir, 'tcx> { } } -impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { +impl Visitor<'tcx> for Validator<'mir, 'tcx> { fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &BasicBlockData<'tcx>) { trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup); @@ -260,8 +292,8 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { // Special-case reborrows to be more like a copy of a reference. match *rvalue { - Rvalue::Ref(_, kind, ref place) => { - if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, *self.body, place) { + Rvalue::Ref(_, kind, place) => { + if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) { let ctx = match kind { BorrowKind::Shared => { PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) @@ -276,20 +308,20 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { PlaceContext::MutatingUse(MutatingUseContext::Borrow) } }; - self.visit_place_base(&place.local, ctx, location); + self.visit_local(&place.local, ctx, location); self.visit_projection(place.local, reborrowed_proj, ctx, location); return; } } - Rvalue::AddressOf(mutbl, ref place) => { - if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, *self.body, place) { + Rvalue::AddressOf(mutbl, place) => { + if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) { let ctx = match mutbl { Mutability::Not => { PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) } Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::AddressOf), }; - self.visit_place_base(&place.local, ctx, location); + self.visit_local(&place.local, ctx, location); self.visit_projection(place.local, reborrowed_proj, ctx, location); return; } @@ -313,10 +345,12 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place) | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => { - let ty = place.ty(*self.body, self.tcx).ty; + let ty = place.ty(self.body, self.tcx).ty; let is_allowed = match ty.kind { // Inside a `static mut`, `&mut [...]` is allowed. - ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut => { + ty::Array(..) | ty::Slice(_) + if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) => + { true } @@ -341,12 +375,11 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { Rvalue::AddressOf(Mutability::Mut, _) => self.check_op(ops::MutAddressOf), - Rvalue::Ref(_, BorrowKind::Shared, ref place) - | Rvalue::Ref(_, BorrowKind::Shallow, ref place) + Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place) | Rvalue::AddressOf(Mutability::Not, ref place) => { let borrowed_place_has_mut_interior = qualifs::in_place::( - &self.item, - &mut |local| self.qualifs.has_mut_interior(local, location), + &self.ccx, + &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location), place.as_ref(), ); @@ -356,19 +389,17 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { } Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { - let operand_ty = operand.ty(*self.body, self.tcx); + let operand_ty = operand.ty(self.body, self.tcx); let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - if let (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) = - (cast_in, cast_out) - { + if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { self.check_op(ops::RawPtrToIntCast); } } Rvalue::BinaryOp(op, ref lhs, _) => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(*self.body, self.tcx).kind { + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { assert!( op == BinOp::Eq || op == BinOp::Ne @@ -389,16 +420,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { } } - fn visit_place_base(&mut self, place_local: &Local, context: PlaceContext, location: Location) { - trace!( - "visit_place_base: place_local={:?} context={:?} location={:?}", - place_local, - context, - location, - ); - self.super_place_base(place_local, context, location); - } - fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { self.super_operand(op, location); if let Operand::Constant(c) = op { @@ -429,12 +450,12 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { match elem { ProjectionElem::Deref => { - let base_ty = Place::ty_from(place_local, proj_base, *self.body, self.tcx).ty; + let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty; if let ty::RawPtr(_) = base_ty.kind { if proj_base.is_empty() { if let (local, []) = (place_local, proj_base) { let decl = &self.body.local_decls[local]; - if let LocalInfo::StaticRef { def_id, .. } = decl.local_info { + if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { let span = decl.source_info.span; self.check_static(def_id, span); return; @@ -450,10 +471,11 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { } ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Field(..) | ProjectionElem::Index(_) => { - let base_ty = Place::ty_from(place_local, proj_base, *self.body, self.tcx).ty; + let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty; match base_ty.ty_adt_def() { Some(def) if def.is_union() => { self.check_op(ops::UnionAccess); @@ -462,10 +484,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { _ => {} } } - - ProjectionElem::Downcast(..) => { - self.check_op(ops::Downcast); - } } } @@ -481,27 +499,37 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } => { self.super_statement(statement, location); } - StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { + + StatementKind::FakeRead( + FakeReadCause::ForMatchedPlace + | FakeReadCause::ForMatchGuard + | FakeReadCause::ForGuardBinding, + _, + ) => { + self.super_statement(statement, location); self.check_op(ops::IfOrMatch); } - // FIXME(eddyb) should these really do nothing? - StatementKind::FakeRead(..) + StatementKind::LlvmInlineAsm { .. } => { + self.super_statement(statement, location); + self.check_op(ops::InlineAsm); + } + + StatementKind::FakeRead(FakeReadCause::ForLet | FakeReadCause::ForIndex, _) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) - | StatementKind::InlineAsm { .. } | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) | StatementKind::Nop => {} } } - fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) { - trace!("visit_terminator_kind: kind={:?} location={:?}", kind, location); - self.super_terminator_kind(kind, location); + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + trace!("visit_terminator: terminator={:?} location={:?}", terminator, location); + self.super_terminator(terminator, location); - match kind { + match &terminator.kind { TerminatorKind::Call { func, .. } => { - let fn_ty = func.ty(*self.body, self.tcx); + let fn_ty = func.ty(self.body, self.tcx); let (def_id, substs) = match fn_ty.kind { ty::FnDef(def_id, substs) => (def_id, substs), @@ -511,8 +539,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { return; } _ => { - self.check_op(ops::FnCallOther); - return; + span_bug!(terminator.source_info.span, "invalid callee of type {:?}", fn_ty) } }; @@ -526,7 +553,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { if self.tcx.features().const_trait_impl { let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs); debug!("Resolving ({:?}) -> {:?}", def_id, instance); - if let Some(func) = instance { + if let Ok(Some(func)) = instance { if let InstanceDef::Item(def_id) = func.def { if is_const_fn(self.tcx, def_id) { return; @@ -557,7 +584,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { // Check to see if the type of this place can ever have a drop impl. If not, this // `Drop` terminator is frivolous. let ty_needs_drop = - dropped_place.ty(*self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env); + dropped_place.ty(self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env); if !ty_needs_drop { return; @@ -566,7 +593,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { let needs_drop = if let Some(local) = dropped_place.as_local() { // Use the span where the local was declared as the span of the drop error. err_span = self.body.local_decls[local].source_info.span; - self.qualifs.needs_drop(local, location) + self.qualifs.needs_drop(self.ccx, local, location) } else { true }; @@ -576,7 +603,23 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { } } - _ => {} + TerminatorKind::InlineAsm { .. } => { + self.check_op(ops::InlineAsm); + } + + // FIXME: Some of these are only caught by `min_const_fn`, but should error here + // instead. + TerminatorKind::Abort + | TerminatorKind::Assert { .. } + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Return + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Unreachable + | TerminatorKind::Yield { .. } => {} } } } @@ -591,8 +634,8 @@ fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) .emit(); } -fn check_short_circuiting_in_const_local(item: &Item<'_, 'tcx>) { - let body = item.body; +fn check_short_circuiting_in_const_local(ccx: &ConstCx<'_, 'tcx>) { + let body = ccx.body; if body.control_flow_destroyed.is_empty() { return; @@ -601,12 +644,12 @@ fn check_short_circuiting_in_const_local(item: &Item<'_, 'tcx>) { let mut locals = body.vars_iter(); if let Some(local) = locals.next() { let span = body.local_decls[local].source_info.span; - let mut error = item.tcx.sess.struct_span_err( + let mut error = ccx.tcx.sess.struct_span_err( span, &format!( "new features like let bindings are not permitted in {}s \ which also use short circuiting operators", - item.const_kind(), + ccx.const_kind(), ), ); for (span, kind) in body.control_flow_destroyed.iter() { @@ -645,7 +688,7 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) fn place_as_reborrow( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - place: &'a Place<'tcx>, + place: Place<'tcx>, ) -> Option<&'a [PlaceElem<'tcx>]> { place.projection.split_last().and_then(|(outermost, inner)| { if outermost != &ProjectionElem::Deref { diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 437a154a9b80f..9bcb45f6493d1 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -1,14 +1,14 @@ -use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; -use rustc::mir::*; -use rustc::ty::cast::CastTy; -use rustc::ty::query::Providers; -use rustc::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit; use rustc_hir::Node; +use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::cast::CastTy; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE}; use rustc_span::symbol::{sym, Symbol}; @@ -19,6 +19,7 @@ use crate::util; pub struct UnsafetyChecker<'a, 'tcx> { body: &'a Body<'tcx>, + body_did: LocalDefId, const_context: bool, min_const_fn: bool, violations: Vec, @@ -35,6 +36,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { const_context: bool, min_const_fn: bool, body: &'a Body<'tcx>, + body_did: LocalDefId, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Self { @@ -44,10 +46,11 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { } Self { body, + body_did, const_context, min_const_fn, violations: vec![], - source_info: SourceInfo { span: body.span, scope: OUTERMOST_SOURCE_SCOPE }, + source_info: SourceInfo::outermost(body.span), tcx, param_env, used_unsafe: Default::default(), @@ -87,7 +90,17 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { UnsafetyViolationKind::GeneralAndConstFn, ) } + + if let ty::FnDef(func_id, _) = func_ty.kind { + self.check_target_features(func_id); + } } + + TerminatorKind::InlineAsm { .. } => self.require_unsafe( + "use of inline assembly", + "inline assembly is entirely unchecked and can cause undefined behavior", + UnsafetyViolationKind::General, + ), } self.super_terminator(terminator, location); } @@ -106,7 +119,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { // safe (at least as emitted during MIR construction) } - StatementKind::InlineAsm { .. } => self.require_unsafe( + StatementKind::LlvmInlineAsm { .. } => self.require_unsafe( "use of inline assembly", "inline assembly is entirely unchecked and can cause undefined behavior", UnsafetyViolationKind::General, @@ -132,7 +145,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => { let UnsafetyCheckResult { violations, unsafe_blocks } = - self.tcx.unsafety_check_result(def_id); + self.tcx.unsafety_check_result(def_id.expect_local()); self.register_violations(&violations, &unsafe_blocks); } }, @@ -146,7 +159,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); match (cast_in, cast_out) { - (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { + (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { self.require_unsafe( "cast of pointer to int", "casting pointers to integers in constants", @@ -177,41 +190,41 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { - // prevent + // On types with `scalar_valid_range`, prevent // * `&mut x.field` // * `x.field = y;` // * `&x.field` if `field`'s type has interior mutability // because either of these would allow modifying the layout constrained field and // insert values that violate the layout constraints. if context.is_mutating_use() || context.is_borrow() { - self.check_mut_borrowing_layout_constrained_field(place, context.is_mutating_use()); + self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use()); + } + + if context.is_borrow() { + if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { + let source_info = self.source_info; + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + self.require_unsafe( + "borrow of packed field", + "fields of packed structs might be misaligned: dereferencing a \ + misaligned pointer or even just creating a misaligned reference \ + is undefined behavior", + UnsafetyViolationKind::BorrowPacked(lint_root), + ); + } } for (i, elem) in place.projection.iter().enumerate() { let proj_base = &place.projection[..i]; - - if context.is_borrow() { - if util::is_disaligned(self.tcx, self.body, self.param_env, place) { - let source_info = self.source_info; - let lint_root = self.body.source_scopes[source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; - self.require_unsafe( - "borrow of packed field", - "fields of packed structs might be misaligned: dereferencing a \ - misaligned pointer or even just creating a misaligned reference \ - is undefined behavior", - UnsafetyViolationKind::BorrowPacked(lint_root), - ); - } - } let old_source_info = self.source_info; if let [] = proj_base { let decl = &self.body.local_decls[place.local]; if decl.internal { - if let LocalInfo::StaticRef { def_id, .. } = decl.local_info { + if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { if self.tcx.is_mutable_static(def_id) { self.require_unsafe( "use of mutable static", @@ -382,7 +395,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { } fn check_mut_borrowing_layout_constrained_field( &mut self, - place: &Place<'tcx>, + place: Place<'tcx>, is_mut_use: bool, ) { let mut cursor = place.projection.as_ref(); @@ -396,48 +409,62 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { ProjectionElem::Field(..) => { let ty = Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty; - match ty.kind { - ty::Adt(def, _) => match self.tcx.layout_scalar_valid_range(def.did) { - (Bound::Unbounded, Bound::Unbounded) => {} - _ => { - let (description, details) = if is_mut_use { - ( - "mutation of layout constrained field", - "mutating layout constrained fields cannot statically be \ + if let ty::Adt(def, _) = ty.kind { + if self.tcx.layout_scalar_valid_range(def.did) + != (Bound::Unbounded, Bound::Unbounded) + { + let (description, details) = if is_mut_use { + ( + "mutation of layout constrained field", + "mutating layout constrained fields cannot statically be \ checked for valid values", - ) + ) - // Check `is_freeze` as late as possible to avoid cycle errors - // with opaque types. - } else if !place.ty(self.body, self.tcx).ty.is_freeze( - self.tcx, - self.param_env, - self.source_info.span, - ) { - ( - "borrow of layout constrained field with interior \ + // Check `is_freeze` as late as possible to avoid cycle errors + // with opaque types. + } else if !place.ty(self.body, self.tcx).ty.is_freeze( + self.tcx, + self.param_env, + self.source_info.span, + ) { + ( + "borrow of layout constrained field with interior \ mutability", - "references to fields of layout constrained fields \ + "references to fields of layout constrained fields \ lose the constraints. Coupled with interior mutability, \ the field can be changed to invalid values", - ) - } else { - continue; - }; - self.require_unsafe( - description, - details, - UnsafetyViolationKind::GeneralAndConstFn, - ); - } - }, - _ => {} + ) + } else { + continue; + }; + self.require_unsafe( + description, + details, + UnsafetyViolationKind::GeneralAndConstFn, + ); + } } } _ => {} } } } + + /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether + /// the called function has target features the calling function hasn't. + fn check_target_features(&mut self, func_did: DefId) { + let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; + let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features; + + // Is `callee_features` a subset of `calling_features`? + if !callee_features.iter().all(|feature| self_features.contains(feature)) { + self.require_unsafe( + "call to function with `#[target_feature]`", + "can only be called if the required target features are available", + UnsafetyViolationKind::GeneralAndConstFn, + ) + } + } } pub(crate) fn provide(providers: &mut Providers<'_>) { @@ -467,12 +494,11 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> { fn check_unused_unsafe( tcx: TyCtxt<'_>, - def_id: DefId, + def_id: LocalDefId, used_unsafe: &FxHashSet, unsafe_blocks: &mut Vec<(hir::HirId, bool)>, ) { - let body_id = - tcx.hir().as_local_hir_id(def_id).and_then(|hir_id| tcx.hir().maybe_body_owned_by(hir_id)); + let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().as_local_hir_id(def_id)); let body_id = match body_id { Some(body) => body, @@ -488,7 +514,7 @@ fn check_unused_unsafe( intravisit::Visitor::visit_body(&mut visitor, body); } -fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult { +fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: LocalDefId) -> UnsafetyCheckResult { debug!("unsafety_violations({:?})", def_id); // N.B., this borrow is valid because all the consumers of @@ -497,17 +523,17 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult let param_env = tcx.param_env(def_id); - let id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let id = tcx.hir().as_local_hir_id(def_id); let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) { hir::BodyOwnerKind::Closure => (false, false), - hir::BodyOwnerKind::Fn => (is_const_fn(tcx, def_id), is_min_const_fn(tcx, def_id)), + hir::BodyOwnerKind::Fn => { + (is_const_fn(tcx, def_id.to_def_id()), is_min_const_fn(tcx, def_id.to_def_id())) + } hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false), }; - let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env); - // mir_built ensures that body has a computed cache, so we don't (and can't) attempt to - // recompute it here. - let body = body.unwrap_read_only(); - checker.visit_body(body); + let mut checker = + UnsafetyChecker::new(const_context, min_const_fn, body, def_id, tcx, param_env); + checker.visit_body(&body); check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks); UnsafetyCheckResult { @@ -517,10 +543,7 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult } fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) { - let lint_hir_id = tcx - .hir() - .as_local_hir_id(def_id) - .unwrap_or_else(|| bug!("checking unsafety for non-local def id {:?}", def_id)); + let lint_hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| { // FIXME: when we make this a hard error, this should have its @@ -565,14 +588,14 @@ fn is_enclosed( } fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet, id: hir::HirId) { - let span = tcx.sess.source_map().def_span(tcx.hir().span(id)); + let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id)); tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| { let msg = "unnecessary `unsafe` block"; let mut db = lint.build(msg); db.span_label(span, msg); if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { db.span_label( - tcx.sess.source_map().def_span(tcx.hir().span(id)), + tcx.sess.source_map().guess_head_span(tcx.hir().span(id)), format!("because it's nested under this `unsafe` {}", kind), ); } @@ -604,7 +627,8 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { return; } - let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id); + let UnsafetyCheckResult { violations, unsafe_blocks } = + tcx.unsafety_check_result(def_id.expect_local()); for &UnsafetyViolation { source_info, description, details, kind } in violations.iter() { // Report an error. @@ -623,7 +647,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { } UnsafetyViolationKind::BorrowPacked(lint_hir_id) => { if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id) { - tcx.unsafe_derive_on_repr_packed(impl_def_id); + tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id); } else { tcx.struct_span_lint_hir( SAFE_PACKED_BORROWS, @@ -643,13 +667,19 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { } } - let mut unsafe_blocks: Vec<_> = unsafe_blocks.iter().collect(); - unsafe_blocks.sort_by_cached_key(|(hir_id, _)| tcx.hir().hir_to_node_id(*hir_id)); - let used_unsafe: FxHashSet<_> = - unsafe_blocks.iter().flat_map(|&&(id, used)| used.then_some(id)).collect(); - for &(block_id, is_used) in unsafe_blocks { - if !is_used { - report_unused_unsafe(tcx, &used_unsafe, block_id); + let (mut unsafe_used, mut unsafe_unused): (FxHashSet<_>, Vec<_>) = Default::default(); + for &(block_id, is_used) in unsafe_blocks.iter() { + if is_used { + unsafe_used.insert(block_id); + } else { + unsafe_unused.push(block_id); } } + // The unused unsafe blocks might not be in source order; sort them so that the unused unsafe + // error messages are properly aligned and the issue-45107 and lint-unused-unsafe tests pass. + unsafe_unused.sort_by_cached_key(|hir_id| tcx.hir().span(*hir_id)); + + for &block_id in &unsafe_unused { + report_unused_unsafe(tcx, &unsafe_used, block_id); + } } diff --git a/src/librustc_mir/transform/cleanup_post_borrowck.rs b/src/librustc_mir/transform/cleanup_post_borrowck.rs index 3d219ac2c01ec..e80da4f756c64 100644 --- a/src/librustc_mir/transform/cleanup_post_borrowck.rs +++ b/src/librustc_mir/transform/cleanup_post_borrowck.rs @@ -7,20 +7,22 @@ //! //! The `CleanFakeReadsAndBorrows` "pass" is actually implemented as two //! traversals (aka visits) of the input MIR. The first traversal, -//! [`DeleteAndRecordFakeReads`], deletes the fake reads and finds the -//! temporaries read by [`ForMatchGuard`] reads, and [`DeleteFakeBorrows`] +//! `DeleteAndRecordFakeReads`, deletes the fake reads and finds the +//! temporaries read by [`ForMatchGuard`] reads, and `DeleteFakeBorrows` //! deletes the initialization of those temporaries. //! -//! [`AscribeUserType`]: rustc::mir::StatementKind::AscribeUserType -//! [`Shallow`]: rustc::mir::BorrowKind::Shallow -//! [`FakeRead`]: rustc::mir::StatementKind::FakeRead -//! [`Nop`]: rustc::mir::StatementKind::Nop +//! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType +//! [`Shallow`]: rustc_middle::mir::BorrowKind::Shallow +//! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead +//! [`Assign`]: rustc_middle::mir::StatementKind::Assign +//! [`ForMatchGuard`]: rustc_middle::mir::FakeReadCause::ForMatchGuard +//! [`Nop`]: rustc_middle::mir::StatementKind::Nop use crate::transform::{MirPass, MirSource}; -use rustc::mir::visit::MutVisitor; -use rustc::mir::{BodyAndCache, BorrowKind, Location, Rvalue}; -use rustc::mir::{Statement, StatementKind}; -use rustc::ty::TyCtxt; +use rustc_middle::mir::visit::MutVisitor; +use rustc_middle::mir::{Body, BorrowKind, Location, Rvalue}; +use rustc_middle::mir::{Statement, StatementKind}; +use rustc_middle::ty::TyCtxt; pub struct CleanupNonCodegenStatements; @@ -29,7 +31,7 @@ pub struct DeleteNonCodegenStatements<'tcx> { } impl<'tcx> MirPass<'tcx> for CleanupNonCodegenStatements { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { let mut delete = DeleteNonCodegenStatements { tcx }; delete.visit_body(body); body.user_type_annotations.raw.clear(); diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 548c637efb260..f69343f4d7500 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -1,38 +1,35 @@ //! Propagates constants for early reporting of statically known //! assertion failures -use std::borrow::Cow; use std::cell::Cell; -use rustc::mir::interpret::{InterpResult, Scalar}; -use rustc::mir::visit::{ - MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, -}; -use rustc::mir::{ - read_only, AggregateKind, AssertKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate, - Constant, Local, LocalDecl, LocalKind, Location, Operand, Place, ReadOnlyBodyAndCache, Rvalue, - SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, - UnOp, RETURN_PLACE, -}; -use rustc::ty::layout::{ - HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout, -}; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable}; use rustc_ast::ast::Mutability; -use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::HirId; +use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; +use rustc_middle::mir::interpret::{InterpResult, Scalar}; +use rustc_middle::mir::visit::{ + MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, +}; +use rustc_middle::mir::{ + AggregateKind, AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, + LocalDecl, LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, + SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, +}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout}; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint; -use rustc_span::Span; +use rustc_span::{def_id::DefId, Span}; +use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout}; use rustc_trait_selection::traits; use crate::const_eval::error_to_const_error; use crate::interpret::{ - self, intern_const_alloc_recursive, AllocId, Allocation, Frame, ImmTy, Immediate, InternKind, - InterpCx, LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, - Pointer, ScalarMaybeUndef, StackPopCleanup, + self, compile_time_machine, intern_const_alloc_recursive, AllocId, Allocation, Frame, ImmTy, + Immediate, InternKind, InterpCx, LocalState, LocalValue, Memory, MemoryKind, OpTy, + Operand as InterpOperand, PlaceTy, Pointer, ScalarMaybeUninit, StackPopCleanup, }; use crate::transform::{MirPass, MirSource}; @@ -46,13 +43,13 @@ macro_rules! throw_machine_stop_str { // We make a new local type for it. The type itself does not carry any information, // but its vtable (for the `MachineStopType` trait) does. struct Zst; - // Debug-printing this type shows the desired string. - impl std::fmt::Debug for Zst { + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, $($tt)*) } } - impl rustc::mir::interpret::MachineStopType for Zst {} + impl rustc_middle::mir::interpret::MachineStopType for Zst {} throw_machine_stop!(Zst) }}; } @@ -60,23 +57,17 @@ macro_rules! throw_machine_stop_str { pub struct ConstProp; impl<'tcx> MirPass<'tcx> for ConstProp { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { // will be evaluated by miri and produce its errors there if source.promoted.is_some() { return; } - use rustc::hir::map::blocks::FnLikeNode; - let hir_id = tcx - .hir() - .as_local_hir_id(source.def_id()) - .expect("Non-local call to local provider is_const_fn"); + use rustc_middle::hir::map::blocks::FnLikeNode; + let hir_id = tcx.hir().as_local_hir_id(source.def_id().expect_local()); let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); - let is_assoc_const = match tcx.def_kind(source.def_id()) { - Some(DefKind::AssocConst) => true, - _ => false, - }; + let is_assoc_const = tcx.def_kind(source.def_id()) == DefKind::AssocConst; // Only run const prop on functions, methods, closures and associated constants if !is_fn_like && !is_assoc_const { @@ -123,11 +114,10 @@ impl<'tcx> MirPass<'tcx> for ConstProp { .predicates_of(source.def_id()) .predicates .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }) - .collect(); + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); if !traits::normalize_and_test_predicates( tcx, - traits::elaborate_predicates(tcx, predicates).collect(), + traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(), ) { trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", source.def_id()); return; @@ -151,39 +141,31 @@ impl<'tcx> MirPass<'tcx> for ConstProp { // constants, instead of just checking for const-folding succeeding. // That would require an uniform one-def no-mutation analysis // and RPO (or recursing when needing the value of a local). - let mut optimization_finder = - ConstPropagator::new(read_only!(body), dummy_body, tcx, source); + let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx, source); optimization_finder.visit_body(body); trace!("ConstProp done for {:?}", source.def_id()); } } -struct ConstPropMachine; - -impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { - type MemoryKinds = !; - type PointerTag = (); - type ExtraFnVal = !; - - type FrameExtra = (); - type MemoryExtra = (); - type AllocExtra = (); - - type MemoryMap = FxHashMap, Allocation)>; +struct ConstPropMachine<'mir, 'tcx> { + /// The virtual call stack. + stack: Vec>, +} - const STATIC_KIND: Option = None; +impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> { + fn new() -> Self { + Self { stack: Vec::new() } + } +} - const CHECK_ALIGN: bool = false; +impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> { + compile_time_machine!(<'mir, 'tcx>); - #[inline(always)] - fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { - false - } + type MemoryExtra = (); fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _span: Span, _instance: ty::Instance<'tcx>, _args: &[OpTy<'tcx>], _ret: Option<(PlaceTy<'tcx>, BasicBlock)>, @@ -192,19 +174,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { Ok(None) } - fn call_extra_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - fn_val: !, - _args: &[OpTy<'tcx>], - _ret: Option<(PlaceTy<'tcx>, BasicBlock)>, - _unwind: Option, - ) -> InterpResult<'tcx> { - match fn_val {} - } - fn call_intrinsic( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _span: Span, _instance: ty::Instance<'tcx>, _args: &[OpTy<'tcx>], _ret: Option<(PlaceTy<'tcx>, BasicBlock)>, @@ -215,8 +186,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { fn assert_panic( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &rustc::mir::AssertMessage<'tcx>, - _unwind: Option, + _msg: &rustc_middle::mir::AssertMessage<'tcx>, + _unwind: Option, ) -> InterpResult<'tcx> { bug!("panics terminators are not evaluated in ConstProp") } @@ -235,20 +206,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") } - #[inline(always)] - fn init_allocation_extra<'b>( - _memory_extra: &(), - _id: AllocId, - alloc: Cow<'b, Allocation>, - _kind: Option>, - ) -> (Cow<'b, Allocation>, Self::PointerTag) { - // We do not use a tag so we can just cheaply forward the allocation - (alloc, ()) - } - - #[inline(always)] - fn tag_static_base_pointer(_memory_extra: &(), _id: AllocId) -> Self::PointerTag {} - fn box_alloc( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _dest: PlaceTy<'tcx>, @@ -270,28 +227,43 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { l.access() } - fn before_access_static( + fn before_access_global( _memory_extra: &(), + _alloc_id: AllocId, allocation: &Allocation, + _static_def_id: Option, + is_write: bool, ) -> InterpResult<'tcx> { - // if the static allocation is mutable or if it has relocations (it may be legal to mutate - // the memory behind that in the future), then we can't const prop it - if allocation.mutability == Mutability::Mut || allocation.relocations().len() > 0 { - throw_machine_stop_str!("can't eval mutable statics in ConstProp") + if is_write { + throw_machine_stop_str!("can't write to global"); + } + // If the static allocation is mutable, then we can't const prop it as its content + // might be different at runtime. + if allocation.mutability == Mutability::Mut { + throw_machine_stop_str!("can't access mutable globals in ConstProp"); } Ok(()) } #[inline(always)] - fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + fn stack( + ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + &ecx.machine.stack + } + + #[inline(always)] + fn stack_mut( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec> { + &mut ecx.machine.stack } } /// Finds optimization opportunities on the MIR. struct ConstPropagator<'mir, 'tcx> { - ecx: InterpCx<'mir, 'tcx, ConstPropMachine>, + ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, tcx: TyCtxt<'tcx>, can_const_prop: IndexVec, param_env: ParamEnv<'tcx>, @@ -299,17 +271,18 @@ struct ConstPropagator<'mir, 'tcx> { // by accessing them through `ecx` instead. source_scopes: IndexVec, local_decls: IndexVec>, - ret: Option>, // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store // the last known `SourceInfo` here and just keep revisiting it. source_info: Option, + // Locals we need to forget at the end of the current block + locals_of_current_block: BitSet, } impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> { type Ty = Ty<'tcx>; - type TyLayout = Result, LayoutError<'tcx>>; + type TyAndLayout = Result, LayoutError<'tcx>>; - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { self.tcx.layout_of(self.param_env.and(ty)) } } @@ -330,7 +303,7 @@ impl<'mir, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> { impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { fn new( - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, dummy_body: &'mir Body<'tcx>, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, @@ -340,7 +313,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let param_env = tcx.param_env(def_id).with_reveal_all(); let span = tcx.def_span(def_id); - let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ()); + let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine::new(), ()); let can_const_prop = CanConstProp::check(body); let ret = ecx @@ -355,7 +328,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ecx.push_stack_frame( Instance::new(def_id, substs), - span, dummy_body, ret.map(Into::into), StackPopCleanup::None { cleanup: false }, @@ -372,26 +344,26 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { source_scopes: body.source_scopes.clone(), //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it local_decls: body.local_decls.clone(), - ret: ret.map(Into::into), source_info: None, + locals_of_current_block: BitSet::new_empty(body.local_decls.len()), } } - fn get_const(&self, local: Local) -> Option> { - if local == RETURN_PLACE { - // Try to read the return place as an immediate so that if it is representable as a - // scalar, we can handle it as such, but otherwise, just return the value as is. - return match self.ret.map(|ret| self.ecx.try_read_immediate(ret)) { - Some(Ok(Ok(imm))) => Some(imm.into()), - _ => self.ret, - }; - } + fn get_const(&self, place: Place<'tcx>) -> Option> { + let op = self.ecx.eval_place_to_op(place, None).ok(); - self.ecx.access_local(self.ecx.frame(), local, None).ok() + // Try to read the local as an immediate so that if it is representable as a scalar, we can + // handle it as such, but otherwise, just return the value as is. + match op.map(|ret| self.ecx.try_read_immediate(ret)) { + Some(Ok(Ok(imm))) => Some(imm.into()), + _ => op, + } } - fn remove_const(&mut self, local: Local) { - self.ecx.frame_mut().locals[local] = + /// Remove `local` from the pool of `Locals`. Allows writing to them, + /// but not reading from them anymore. + fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) { + ecx.frame_mut().locals[local] = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) }; } @@ -422,9 +394,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } + /// Returns the value, if any, of evaluating `c`. fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option> { - self.ecx.tcx.span = c.span; - // FIXME we need to revisit this for #67176 if c.needs_subst() { return None; @@ -433,6 +404,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { match self.ecx.eval_const_to_op(c.literal, None) { Ok(op) => Some(op), Err(error) => { + // Make sure errors point at the constant. + self.ecx.set_span(c.span); let err = error_to_const_error(&self.ecx, error); if let Some(lint_root) = self.lint_root(source_info) { let lint_only = match c.literal.val { @@ -462,15 +435,18 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - fn eval_place(&mut self, place: &Place<'tcx>) -> Option> { + /// Returns the value, if any, of evaluating `place`. + fn eval_place(&mut self, place: Place<'tcx>) -> Option> { trace!("eval_place(place={:?})", place); self.use_ecx(|this| this.ecx.eval_place_to_op(place, None)) } + /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant` + /// or `eval_place`, depending on the variant of `Operand` used. fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option> { match *op { Operand::Constant(ref c) => self.eval_constant(c, source_info), - Operand::Move(ref place) | Operand::Copy(ref place) => self.eval_place(place), + Operand::Move(place) | Operand::Copy(place) => self.eval_place(place), } } @@ -564,20 +540,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { fn const_prop( &mut self, rvalue: &Rvalue<'tcx>, - place_layout: TyLayout<'tcx>, + place_layout: TyAndLayout<'tcx>, source_info: SourceInfo, - place: &Place<'tcx>, + place: Place<'tcx>, ) -> Option<()> { // #66397: Don't try to eval into large places as that can cause an OOM if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) { return None; } - // FIXME we need to revisit this for #67176 - if rvalue.needs_subst() { - return None; - } - // Perform any special handling for specific Rvalue types. // Generally, checks here fall into one of two categories: // 1. Additional checking to provide useful lints to the user @@ -618,6 +589,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { _ => {} } + // FIXME we need to revisit this for #67176 + if rvalue.needs_subst() { + return None; + } + self.use_ecx(|this| { trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place); this.ecx.eval_rvalue_into_place(rvalue, place)?; @@ -625,6 +601,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { }) } + /// Creates a new `Operand::Constant` from a `Scalar` value fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> { Operand::Constant(Box::new(Constant { span, @@ -639,6 +616,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { value: OpTy<'tcx>, source_info: SourceInfo, ) { + if let Rvalue::Use(Operand::Constant(c)) = rval { + if !matches!(c.literal.val, ConstKind::Unevaluated(..)) { + trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); + return; + } + } + trace!("attepting to replace {:?} with {:?}", rval, value); if let Err(e) = self.ecx.const_validate_operand( value, @@ -656,7 +640,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { if let Some(Ok(imm)) = imm { match *imm { - interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => { + interpret::Immediate::Scalar(ScalarMaybeUninit::Scalar(scalar)) => { *rval = Rvalue::Use(self.operand_from_scalar( scalar, value.layout.ty, @@ -664,12 +648,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { )); } Immediate::ScalarPair( - ScalarMaybeUndef::Scalar(one), - ScalarMaybeUndef::Scalar(two), + ScalarMaybeUninit::Scalar(one), + ScalarMaybeUninit::Scalar(two), ) => { // Found a value represented as a pair. For now only do cont-prop if type of // Rvalue is also a pair with two scalars. The more general case is more // complicated to implement so we'll do it later. + // FIXME: implement the general case stated above ^. let ty = &value.layout.ty.kind; // Only do it for tuples if let ty::Tuple(substs) = ty { @@ -679,7 +664,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let ty1 = substs[0].expect_ty(); let ty2 = substs[1].expect_ty(); let ty_is_scalar = |ty| { - this.ecx.layout_of(ty).ok().map(|ty| ty.details.abi.is_scalar()) + this.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar()) == Some(true) }; if ty_is_scalar(ty1) && ty_is_scalar(ty2) { @@ -706,6 +691,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } + /// Returns `true` if and only if this `op` should be const-propagated into. fn should_const_prop(&mut self, op: OpTy<'tcx>) -> bool { let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level; @@ -714,17 +700,16 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } match *op { - interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Scalar(s))) => { + interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUninit::Scalar(s))) => { s.is_bits() } interpret::Operand::Immediate(Immediate::ScalarPair( - ScalarMaybeUndef::Scalar(l), - ScalarMaybeUndef::Scalar(r), + ScalarMaybeUninit::Scalar(l), + ScalarMaybeUninit::Scalar(r), )) => l.is_bits() && r.is_bits(), interpret::Operand::Indirect(_) if mir_opt_level >= 2 => { let mplace = op.assert_mem_place(&self.ecx); - intern_const_alloc_recursive(&mut self.ecx, InternKind::ConstProp, mplace, false) - .expect("failed to intern alloc"); + intern_const_alloc_recursive(&mut self.ecx, InternKind::ConstProp, mplace, false); true } _ => false, @@ -737,6 +722,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { enum ConstPropMode { /// The `Local` can be propagated into and reads of this `Local` can also be propagated. FullConstProp, + /// The `Local` can only be propagated into and from its own block. + OnlyInsideOwnBlock, /// The `Local` can be propagated into but reads cannot be propagated. OnlyPropagateInto, /// No propagation is allowed at all. @@ -745,48 +732,75 @@ enum ConstPropMode { struct CanConstProp { can_const_prop: IndexVec, - // false at the beginning, once set, there are not allowed to be any more assignments - found_assignment: IndexVec, + // False at the beginning. Once set, no more assignments are allowed to that local. + found_assignment: BitSet, + // Cache of locals' information + local_kinds: IndexVec, } impl CanConstProp { - /// returns true if `local` can be propagated - fn check(body: ReadOnlyBodyAndCache<'_, '_>) -> IndexVec { + /// Returns true if `local` can be propagated + fn check(body: &Body<'_>) -> IndexVec { let mut cpv = CanConstProp { can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), - found_assignment: IndexVec::from_elem(false, &body.local_decls), + found_assignment: BitSet::new_empty(body.local_decls.len()), + local_kinds: IndexVec::from_fn_n( + |local| body.local_kind(local), + body.local_decls.len(), + ), }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { - // cannot use args at all - // cannot use locals because if x < y { y - x } else { x - y } would + // Cannot use args at all + // Cannot use locals because if x < y { y - x } else { x - y } would // lint for x != y // FIXME(oli-obk): lint variables until they are used in a condition // FIXME(oli-obk): lint if return value is constant - let local_kind = body.local_kind(local); - - if local_kind == LocalKind::Arg || local_kind == LocalKind::Var { + if cpv.local_kinds[local] == LocalKind::Arg { *val = ConstPropMode::OnlyPropagateInto; - trace!("local {:?} can't be const propagated because it's not a temporary", local); + trace!( + "local {:?} can't be const propagated because it's a function argument", + local + ); + } else if cpv.local_kinds[local] == LocalKind::Var { + *val = ConstPropMode::OnlyInsideOwnBlock; + trace!( + "local {:?} will only be propagated inside its block, because it's a user variable", + local + ); } } - cpv.visit_body(body); + cpv.visit_body(&body); cpv.can_const_prop } } impl<'tcx> Visitor<'tcx> for CanConstProp { fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { - use rustc::mir::visit::PlaceContext::*; + use rustc_middle::mir::visit::PlaceContext::*; match context { - // Constants must have at most one write - // FIXME(oli-obk): we could be more powerful here, if the multiple writes - // only occur in independent execution paths - MutatingUse(MutatingUseContext::Store) => { - if self.found_assignment[local] { - trace!("local {:?} can't be propagated because of multiple assignments", local); - self.can_const_prop[local] = ConstPropMode::NoPropagation; - } else { - self.found_assignment[local] = true + // Projections are fine, because `&mut foo.x` will be caught by + // `MutatingUseContext::Borrow` elsewhere. + MutatingUse(MutatingUseContext::Projection) + // These are just stores, where the storing is not propagatable, but there may be later + // mutations of the same local via `Store` + | MutatingUse(MutatingUseContext::Call) + // Actual store that can possibly even propagate a value + | MutatingUse(MutatingUseContext::Store) => { + if !self.found_assignment.insert(local) { + match &mut self.can_const_prop[local] { + // If the local can only get propagated in its own block, then we don't have + // to worry about multiple assignments, as we'll nuke the const state at the + // end of the block anyway, and inside the block we overwrite previous + // states as applicable. + ConstPropMode::OnlyInsideOwnBlock => {} + other => { + trace!( + "local {:?} can't be propagated because of multiple assignments", + local, + ); + *other = ConstPropMode::NoPropagation; + } + } } } // Reading constants is allowed an arbitrary number of times @@ -794,9 +808,22 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { | NonMutatingUse(NonMutatingUseContext::Move) | NonMutatingUse(NonMutatingUseContext::Inspect) | NonMutatingUse(NonMutatingUseContext::Projection) - | MutatingUse(MutatingUseContext::Projection) | NonUse(_) => {} - _ => { + + // These could be propagated with a smarter analysis or just some careful thinking about + // whether they'd be fine right now. + MutatingUse(MutatingUseContext::AsmOutput) + | MutatingUse(MutatingUseContext::Yield) + | MutatingUse(MutatingUseContext::Drop) + | MutatingUse(MutatingUseContext::Retag) + // These can't ever be propagated under any scheme, as we can't reason about indirect + // mutation. + | NonMutatingUse(NonMutatingUseContext::SharedBorrow) + | NonMutatingUse(NonMutatingUseContext::ShallowBorrow) + | NonMutatingUse(NonMutatingUseContext::UniqueBorrow) + | NonMutatingUse(NonMutatingUseContext::AddressOf) + | MutatingUse(MutatingUseContext::Borrow) + | MutatingUse(MutatingUseContext::AddressOf) => { trace!("local {:?} can't be propagaged because it's used: {:?}", local, context); self.can_const_prop[local] = ConstPropMode::NoPropagation; } @@ -809,6 +836,12 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { self.tcx } + fn visit_body(&mut self, body: &mut Body<'tcx>) { + for (bb, data) in body.basic_blocks_mut().iter_enumerated_mut() { + self.visit_basic_block_data(bb, data); + } + } + fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) { trace!("visit_constant: {:?}", constant); self.super_constant(constant, location); @@ -818,34 +851,60 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { trace!("visit_statement: {:?}", statement); let source_info = statement.source_info; + self.ecx.set_span(source_info.span); self.source_info = Some(source_info); - if let StatementKind::Assign(box (ref place, ref mut rval)) = statement.kind { + if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind { let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty; if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) { - if let Some(local) = place.as_local() { - let can_const_prop = self.can_const_prop[local]; - if let Some(()) = self.const_prop(rval, place_layout, source_info, place) { - if can_const_prop == ConstPropMode::FullConstProp - || can_const_prop == ConstPropMode::OnlyPropagateInto - { - if let Some(value) = self.get_const(local) { - if self.should_const_prop(value) { - trace!("replacing {:?} with {:?}", rval, value); - self.replace_with_const(rval, value, statement.source_info); - - if can_const_prop == ConstPropMode::FullConstProp { - trace!("propagated into {:?}", local); - } + let can_const_prop = self.can_const_prop[place.local]; + if let Some(()) = self.const_prop(rval, place_layout, source_info, place) { + if can_const_prop != ConstPropMode::NoPropagation { + // This will return None for variables that are from other blocks, + // so it should be okay to propagate from here on down. + if let Some(value) = self.get_const(place) { + if self.should_const_prop(value) { + trace!("replacing {:?} with {:?}", rval, value); + self.replace_with_const(rval, value, statement.source_info); + if can_const_prop == ConstPropMode::FullConstProp + || can_const_prop == ConstPropMode::OnlyInsideOwnBlock + { + trace!("propagated into {:?}", place); } } + if can_const_prop == ConstPropMode::OnlyInsideOwnBlock { + trace!( + "found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}", + place.local + ); + self.locals_of_current_block.insert(place.local); + } } } - if self.can_const_prop[local] != ConstPropMode::FullConstProp { - trace!("can't propagate into {:?}", local); - if local != RETURN_PLACE { - self.remove_const(local); + if can_const_prop == ConstPropMode::OnlyPropagateInto + || can_const_prop == ConstPropMode::NoPropagation + { + trace!("can't propagate into {:?}", place); + if place.local != RETURN_PLACE { + Self::remove_const(&mut self.ecx, place.local); } } + } else { + // Const prop failed, so erase the destination, ensuring that whatever happens + // from here on, does not know about the previous value. + // This is important in case we have + // ```rust + // let mut x = 42; + // x = SOME_MUTABLE_STATIC; + // // x must now be undefined + // ``` + // FIXME: we overzealously erase the entire local, because that's easier to + // implement. + trace!( + "propagation into {:?} failed. + Nuking the entire site from orbit, it's the only way to be sure", + place, + ); + Self::remove_const(&mut self.ecx, place.local); } } } else { @@ -868,20 +927,21 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { let source_info = terminator.source_info; + self.ecx.set_span(source_info.span); self.source_info = Some(source_info); self.super_terminator(terminator, location); match &mut terminator.kind { TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => { if let Some(value) = self.eval_operand(&cond, source_info) { trace!("assertion on {:?} should be {:?}", value, expected); - let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected)); + let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected)); let value_const = self.ecx.read_scalar(value).unwrap(); if expected != value_const { - // poison all places this operand references so that further code + // Poison all places this operand references so that further code // doesn't use the invalid value match cond { Operand::Move(ref place) | Operand::Copy(ref place) => { - self.remove_const(place.local); + Self::remove_const(&mut self.ecx, place.local); } Operand::Constant(_) => {} } @@ -921,7 +981,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { ); } else { if self.should_const_prop(value) { - if let ScalarMaybeUndef::Scalar(scalar) = value_const { + if let ScalarMaybeUninit::Scalar(scalar) = value_const { *cond = self.operand_from_scalar( scalar, self.tcx.types.bool, @@ -935,7 +995,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => { if let Some(value) = self.eval_operand(&discr, source_info) { if self.should_const_prop(value) { - if let ScalarMaybeUndef::Scalar(scalar) = + if let ScalarMaybeUninit::Scalar(scalar) = self.ecx.read_scalar(value).unwrap() { *discr = self.operand_from_scalar(scalar, switch_ty, source_info.span); @@ -943,7 +1003,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { } } } - //none of these have Operands to const-propagate + // None of these have Operands to const-propagate TerminatorKind::Goto { .. } | TerminatorKind::Resume | TerminatorKind::Abort @@ -954,9 +1014,63 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { | TerminatorKind::Yield { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::FalseEdges { .. } - | TerminatorKind::FalseUnwind { .. } => {} - //FIXME(wesleywiser) Call does have Operands that could be const-propagated - TerminatorKind::Call { .. } => {} + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} + // Every argument in our function calls can be const propagated. + TerminatorKind::Call { ref mut args, .. } => { + let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level; + // Constant Propagation into function call arguments is gated + // under mir-opt-level 2, because LLVM codegen gives performance + // regressions with it. + if mir_opt_level >= 2 { + for opr in args { + /* + The following code would appear to be incomplete, because + the function `Operand::place()` returns `None` if the + `Operand` is of the variant `Operand::Constant`. In this + context however, that variant will never appear. This is why: + + When constructing the MIR, all function call arguments are + copied into `Locals` of `LocalKind::Temp`. At least, all arguments + that are not unsized (Less than 0.1% are unsized. See #71170 + to learn more about those). + + This means that, conversely, all `Operands` found as function call + arguments are of the variant `Operand::Copy`. This allows us to + simplify our handling of `Operands` in this case. + */ + if let Some(l) = opr.place() { + if let Some(value) = self.get_const(l) { + if self.should_const_prop(value) { + // FIXME(felix91gr): this code only handles `Scalar` cases. + // For now, we're not handling `ScalarPair` cases because + // doing so here would require a lot of code duplication. + // We should hopefully generalize `Operand` handling into a fn, + // and use it to do const-prop here and everywhere else + // where it makes sense. + if let interpret::Operand::Immediate( + interpret::Immediate::Scalar(ScalarMaybeUninit::Scalar( + scalar, + )), + ) = *value + { + *opr = self.operand_from_scalar( + scalar, + value.layout.ty, + source_info.span, + ); + } + } + } + } + } + } + } + } + // We remove all Locals which are restricted in propagation to their containing blocks. + for local in self.locals_of_current_block.iter() { + Self::remove_const(&mut self.ecx, local); } + self.locals_of_current_block.clear(); } } diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index b8ca823a985ba..ba406c72df848 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -21,17 +21,16 @@ use crate::transform::{MirPass, MirSource}; use crate::util::def_use::DefUseAnalysis; -use rustc::mir::visit::MutVisitor; -use rustc::mir::{ - read_only, Body, BodyAndCache, Constant, Local, LocalKind, Location, Operand, Place, Rvalue, - StatementKind, +use rustc_middle::mir::visit::MutVisitor; +use rustc_middle::mir::{ + Body, Constant, Local, LocalKind, Location, Operand, Place, Rvalue, StatementKind, }; -use rustc::ty::TyCtxt; +use rustc_middle::ty::TyCtxt; pub struct CopyPropagation; impl<'tcx> MirPass<'tcx> for CopyPropagation { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { // We only run when the MIR optimization level is > 1. // This avoids a slow pass, and messing up debug info. if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { @@ -40,10 +39,10 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation { let mut def_use_analysis = DefUseAnalysis::new(body); loop { - def_use_analysis.analyze(read_only!(body)); + def_use_analysis.analyze(body); if eliminate_self_assignments(body, &def_use_analysis) { - def_use_analysis.analyze(read_only!(body)); + def_use_analysis.analyze(body); } let mut changed = false; @@ -74,7 +73,12 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation { } // Conservatively gives up if the dest is an argument, // because there may be uses of the original argument value. - if body.local_kind(dest_local) == LocalKind::Arg { + // Also gives up on the return place, as we cannot propagate into its implicit + // use by `return`. + if matches!( + body.local_kind(dest_local), + LocalKind::Arg | LocalKind::ReturnPointer + ) { debug!(" Can't copy-propagate local: dest {:?} (argument)", dest_local); continue; } @@ -97,9 +101,8 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation { if let Some(local) = place.as_local() { if local == dest_local { let maybe_action = match operand { - Operand::Copy(ref src_place) - | Operand::Move(ref src_place) => { - Action::local_copy(&body, &def_use_analysis, src_place) + Operand::Copy(src_place) | Operand::Move(src_place) => { + Action::local_copy(&body, &def_use_analysis, *src_place) } Operand::Constant(ref src_constant) => { Action::constant(src_constant) @@ -157,8 +160,10 @@ fn eliminate_self_assignments(body: &mut Body<'_>, def_use_analysis: &DefUseAnal let location = def.location; if let Some(stmt) = body[location.block].statements.get(location.statement_index) { match &stmt.kind { - StatementKind::Assign(box (place, Rvalue::Use(Operand::Copy(src_place)))) - | StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(src_place)))) => { + StatementKind::Assign(box ( + place, + Rvalue::Use(Operand::Copy(src_place) | Operand::Move(src_place)), + )) => { if let (Some(local), Some(src_local)) = (place.as_local(), src_place.as_local()) { @@ -195,7 +200,7 @@ impl<'tcx> Action<'tcx> { fn local_copy( body: &Body<'tcx>, def_use_analysis: &DefUseAnalysis, - src_place: &Place<'tcx>, + src_place: Place<'tcx>, ) -> Option> { // The source must be a local. let src_local = if let Some(local) = src_place.as_local() { @@ -246,12 +251,12 @@ impl<'tcx> Action<'tcx> { } fn constant(src_constant: &Constant<'tcx>) -> Option> { - Some(Action::PropagateConstant((*src_constant).clone())) + Some(Action::PropagateConstant(*src_constant)) } fn perform( self, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, def_use_analysis: &DefUseAnalysis, dest_local: Local, location: Location, @@ -371,7 +376,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstantPropagationVisitor<'tcx> { _ => return, } - *operand = Operand::Constant(box self.constant.clone()); + *operand = Operand::Constant(box self.constant); self.uses_replaced += 1 } } diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index 9f8964ee5a0c4..2de701284e3f5 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -1,12 +1,12 @@ use crate::transform::{MirPass, MirSource}; use crate::util::expand_aggregate; -use rustc::mir::*; -use rustc::ty::TyCtxt; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; pub struct Deaggregator; impl<'tcx> MirPass<'tcx> for Deaggregator { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &*local_decls; for bb in basic_blocks { diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index 795bcb57d0678..5ce6f4fa7414e 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -7,8 +7,8 @@ use std::io; use crate::transform::{MirPass, MirSource}; use crate::util as mir_util; -use rustc::mir::{Body, BodyAndCache}; -use rustc::ty::TyCtxt; +use rustc_middle::mir::Body; +use rustc_middle::ty::TyCtxt; use rustc_session::config::{OutputFilenames, OutputType}; pub struct Marker(pub &'static str); @@ -18,13 +18,7 @@ impl<'tcx> MirPass<'tcx> for Marker { Cow::Borrowed(self.0) } - fn run_pass( - &self, - _tcx: TyCtxt<'tcx>, - _source: MirSource<'tcx>, - _body: &mut BodyAndCache<'tcx>, - ) { - } + fn run_pass(&self, _tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, _body: &mut Body<'tcx>) {} } pub struct Disambiguator { @@ -46,7 +40,7 @@ pub fn on_mir_pass<'tcx>( body: &Body<'tcx>, is_after: bool, ) { - if mir_util::dump_enabled(tcx, pass_name, source) { + if mir_util::dump_enabled(tcx, pass_name, source.def_id()) { mir_util::dump_mir( tcx, Some(pass_num), diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 5d02074aaaa6b..e379e5ee656b7 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -1,27 +1,27 @@ use crate::dataflow; -use crate::dataflow::generic::{Analysis, ResultsCursor}; +use crate::dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; use crate::dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; use crate::dataflow::on_lookup_result_bits; use crate::dataflow::MoveDataParamEnv; use crate::dataflow::{on_all_children_bits, on_all_drop_children_bits}; -use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; +use crate::dataflow::{Analysis, ResultsCursor}; use crate::transform::{MirPass, MirSource}; use crate::util::elaborate_drops::{elaborate_drop, DropFlagState, Unwind}; use crate::util::elaborate_drops::{DropElaborator, DropFlagMode, DropStyle}; use crate::util::patch::MirPatch; -use rustc::mir::*; -use rustc::ty::layout::VariantIdx; -use rustc::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_index::bit_set::BitSet; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Span; +use rustc_target::abi::VariantIdx; use std::fmt; pub struct ElaborateDrops; impl<'tcx> MirPass<'tcx> for ElaborateDrops { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { debug!("elaborate_drops({:?} @ {:?})", src, body.span); let def_id = src.def_id(); @@ -101,7 +101,7 @@ fn find_dead_unwinds<'tcx>( } }; - flow_inits.seek_before(body.terminator_loc(bb)); + flow_inits.seek_before_primary_effect(body.terminator_loc(bb)); debug!( "find_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}", bb, @@ -131,8 +131,8 @@ struct InitializationData<'mir, 'tcx> { impl InitializationData<'_, '_> { fn seek_before(&mut self, loc: Location) { - self.inits.seek_before(loc); - self.uninits.seek_before(loc); + self.inits.seek_before_primary_effect(loc); + self.uninits.seek_before_primary_effect(loc); } fn maybe_live_dead(&self, path: MovePathIndex) -> (bool, bool) { @@ -346,7 +346,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let resume_block = self.patch.resume_block(); match terminator.kind { - TerminatorKind::Drop { ref location, target, unwind } => { + TerminatorKind::Drop { location, target, unwind } => { self.init_data.seek_before(loc); match self.move_data().rev_lookup.find(location.as_ref()) { LookupResult::Exact(path) => elaborate_drop( @@ -371,7 +371,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } } } - TerminatorKind::DropAndReplace { ref location, ref value, target, unwind } => { + TerminatorKind::DropAndReplace { location, ref value, target, unwind } => { assert!(!data.is_cleanup); self.elaborate_replace(loc, location, value, target, unwind); @@ -396,7 +396,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn elaborate_replace( &mut self, loc: Location, - location: &Place<'tcx>, + location: Place<'tcx>, value: &Operand<'tcx>, target: BasicBlock, unwind: Option, @@ -407,7 +407,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { assert!(!data.is_cleanup, "DropAndReplace in unwind path not supported"); let assign = Statement { - kind: StatementKind::Assign(box (*location, Rvalue::Use(value.clone()))), + kind: StatementKind::Assign(box (location, Rvalue::Use(value.clone()))), source_info: terminator.source_info, }; @@ -459,7 +459,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent); self.patch.patch_terminator( bb, - TerminatorKind::Drop { location: *location, target, unwind: Some(unwind) }, + TerminatorKind::Drop { location, target, unwind: Some(unwind) }, ); } } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 349cda831053f..4bf2adcd450c0 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -49,24 +49,28 @@ //! For generators with state 1 (returned) and state 2 (poisoned) it does nothing. //! Otherwise it drops all the values in scope at the last suspension point. -use crate::dataflow::generic::{self as dataflow, Analysis}; -use crate::dataflow::{MaybeBorrowedLocals, MaybeRequiresStorage, MaybeStorageLive}; +use crate::dataflow::impls::{ + MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive, +}; +use crate::dataflow::{self, Analysis}; use crate::transform::no_landing_pads::no_landing_pads; use crate::transform::simplify; use crate::transform::{MirPass, MirSource}; use crate::util::dump_mir; -use crate::util::liveness; -use rustc::mir::visit::{MutVisitor, PlaceContext, Visitor}; -use rustc::mir::*; -use rustc::ty::layout::VariantIdx; -use rustc::ty::subst::SubstsRef; -use rustc::ty::GeneratorSubsts; -use rustc::ty::{self, AdtDef, Ty, TyCtxt}; +use crate::util::storage; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem}; use rustc_index::bit_set::{BitMatrix, BitSet}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext}; +use rustc_middle::mir::*; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::GeneratorSubsts; +use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; +use rustc_target::spec::PanicStrategy; use std::borrow::Cow; use std::iter; @@ -89,10 +93,13 @@ impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor<'tcx> { } } - fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option> { - match elem { - PlaceElem::Index(local) if *local == self.from => Some(PlaceElem::Index(self.to)), - _ => None, + fn visit_terminator_kind(&mut self, kind: &mut TerminatorKind<'tcx>, location: Location) { + match kind { + TerminatorKind::Return => { + // Do not replace the implicit `_0` access here, as that's not possible. The + // transform already handles `return` correctly. + } + _ => self.super_terminator_kind(kind, location), } } } @@ -121,7 +128,7 @@ impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor<'tcx> { self.tcx, ); } else { - self.visit_place_base(&mut place.local, context, location); + self.visit_local(&mut place.local, context, location); for elem in place.projection.iter() { if let PlaceElem::Index(local) = elem { @@ -160,7 +167,7 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> { self.tcx, ); } else { - self.visit_place_base(&mut place.local, context, location); + self.visit_local(&mut place.local, context, location); for elem in place.projection.iter() { if let PlaceElem::Index(local) = elem { @@ -200,7 +207,7 @@ struct SuspensionPoint<'tcx> { /// Which block to jump to if the generator is dropped in this state. drop: Option, /// Set of locals that have live storage while at this suspension point. - storage_liveness: liveness::LiveVarSet, + storage_liveness: BitSet, } struct TransformVisitor<'tcx> { @@ -216,12 +223,14 @@ struct TransformVisitor<'tcx> { remap: FxHashMap, VariantIdx, usize)>, // A map from a suspension point in a block to the locals which have live storage at that point - // FIXME(eddyb) This should use `IndexVec>`. - storage_liveness: FxHashMap, + storage_liveness: IndexVec>>, // A list of suspension points, generated during the transform suspension_points: Vec>, + // The set of locals that have no `StorageLive`/`StorageDead` annotations. + always_live_locals: storage::AlwaysLiveLocals, + // The original RETURN_PLACE local new_ret_local: Local, } @@ -257,13 +266,13 @@ impl TransformVisitor<'tcx> { // Create a statement which reads the discriminant into a temporary fn get_discr(&self, body: &mut Body<'tcx>) -> (Statement<'tcx>, Place<'tcx>) { - let temp_decl = LocalDecl::new_internal(self.tcx.types.isize, body.span); + let temp_decl = LocalDecl::new(self.tcx.types.isize, body.span).internal(); let local_decls_len = body.local_decls.push(temp_decl); let temp = Place::from(local_decls_len); let self_place = Place::from(SELF_ARG); let assign = Statement { - source_info: source_info(body), + source_info: SourceInfo::outermost(body.span), kind: StatementKind::Assign(box (temp, Rvalue::Discriminant(self_place))), }; (assign, temp) @@ -341,7 +350,7 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> { resume, resume_arg, drop, - storage_liveness: self.storage_liveness.get(&block).unwrap().clone(), + storage_liveness: self.storage_liveness[block].clone().unwrap(), }); VariantIdx::new(state) @@ -357,7 +366,7 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> { } } -fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyAndCache<'tcx>) { +fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let gen_ty = body.local_decls.raw[1].ty; let ref_gen_ty = @@ -370,10 +379,10 @@ fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Bo DerefArgVisitor { tcx }.visit_body(body); } -fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyAndCache<'tcx>) { +fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let ref_gen_ty = body.local_decls.raw[1].ty; - let pin_did = tcx.lang_items().pin_type().unwrap(); + let pin_did = tcx.require_lang_item(PinTypeLangItem, Some(body.span)); let pin_adt_ref = tcx.adt_def(pin_did); let substs = tcx.intern_substs(&[ref_gen_ty.into()]); let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs); @@ -394,21 +403,11 @@ fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body fn replace_local<'tcx>( local: Local, ty: Ty<'tcx>, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, ) -> Local { - let source_info = source_info(body); - let new_decl = LocalDecl { - mutability: Mutability::Mut, - ty, - user_ty: UserTypeProjections::none(), - source_info, - internal: false, - is_block_tail: None, - local_info: LocalInfo::Other, - }; - let new_local = Local::new(body.local_decls.len()); - body.local_decls.push(new_decl); + let new_decl = LocalDecl::new(ty, body.span); + let new_local = body.local_decls.push(new_decl); body.local_decls.swap(local, new_local); RenameLocalVisitor { from: local, to: new_local, tcx }.visit_body(body); @@ -416,26 +415,13 @@ fn replace_local<'tcx>( new_local } -struct StorageIgnored(liveness::LiveVarSet); - -impl<'tcx> Visitor<'tcx> for StorageIgnored { - fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) { - match statement.kind { - StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { - self.0.remove(l); - } - _ => (), - } - } -} - struct LivenessInfo { /// Which locals are live across any suspension point. /// /// GeneratorSavedLocal is indexed in terms of the elements in this set; /// i.e. GeneratorSavedLocal::new(1) corresponds to the second local /// included in this set. - live_locals: liveness::LiveVarSet, + live_locals: BitSet, /// The set of saved locals live at each suspension point. live_locals_at_suspension_points: Vec>, @@ -447,13 +433,14 @@ struct LivenessInfo { /// For every suspending block, the locals which are storage-live across /// that suspension point. - storage_liveness: FxHashMap, + storage_liveness: IndexVec>>, } fn locals_live_across_suspend_points( tcx: TyCtxt<'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + body: &Body<'tcx>, source: MirSource<'tcx>, + always_live_locals: &storage::AlwaysLiveLocals, movable: bool, ) -> LivenessInfo { let def_id = source.def_id(); @@ -461,16 +448,11 @@ fn locals_live_across_suspend_points( // Calculate when MIR locals have live storage. This gives us an upper bound of their // lifetimes. - let mut storage_live = MaybeStorageLive + let mut storage_live = MaybeStorageLive::new(always_live_locals.clone()) .into_engine(tcx, body_ref, def_id) .iterate_to_fixpoint() .into_results_cursor(body_ref); - // Find the MIR locals which do not use StorageLive/StorageDead statements. - // The storage of these locals are always live. - let mut ignored = StorageIgnored(BitSet::new_filled(body.local_decls.len())); - ignored.visit_body(body); - // Calculate the MIR locals which have been previously // borrowed (even if they are still active). let borrowed_locals_results = @@ -488,17 +470,22 @@ fn locals_live_across_suspend_points( dataflow::ResultsCursor::new(body_ref, &requires_storage_results); // Calculate the liveness of MIR locals ignoring borrows. - let mut live_locals = liveness::LiveVarSet::new_empty(body.local_decls.len()); - let mut liveness = liveness::liveness_of_locals(body); - liveness::dump_mir(tcx, "generator_liveness", source, body_ref, &liveness); + let mut liveness = MaybeLiveLocals + .into_engine(tcx, body_ref, def_id) + .iterate_to_fixpoint() + .into_results_cursor(body_ref); - let mut storage_liveness_map = FxHashMap::default(); + let mut storage_liveness_map = IndexVec::from_elem(None, body.basic_blocks()); let mut live_locals_at_suspension_points = Vec::new(); + let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len()); for (block, data) in body.basic_blocks().iter_enumerated() { if let TerminatorKind::Yield { .. } = data.terminator().kind { let loc = Location { block, statement_index: data.statements.len() }; + liveness.seek_to_block_end(block); + let mut live_locals = liveness.get().clone(); + if !movable { // The `liveness` variable contains the liveness of MIR locals ignoring borrows. // This is correct for movable generators since borrows cannot live across @@ -510,52 +497,51 @@ fn locals_live_across_suspend_points( // 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(loc); - liveness.outs[block].union(borrowed_locals_cursor.get()); + borrowed_locals_cursor.seek_before_primary_effect(loc); + live_locals.union(borrowed_locals_cursor.get()); } - storage_live.seek_before(loc); - let storage_liveness = storage_live.get(); - // Store the storage liveness for later use so we can restore the state // after a suspension point - storage_liveness_map.insert(block, storage_liveness.clone()); - - requires_storage_cursor.seek_before(loc); - let storage_required = requires_storage_cursor.get().clone(); + storage_live.seek_before_primary_effect(loc); + storage_liveness_map[block] = Some(storage_live.get().clone()); // Locals live are live at this point only if they are used across // suspension points (the `liveness` variable) // and their storage is required (the `storage_required` variable) - let mut live_locals_here = storage_required; - live_locals_here.intersect(&liveness.outs[block]); + requires_storage_cursor.seek_before_primary_effect(loc); + live_locals.intersect(requires_storage_cursor.get()); // The generator argument is ignored. - live_locals_here.remove(SELF_ARG); + live_locals.remove(SELF_ARG); - debug!("loc = {:?}, live_locals_here = {:?}", loc, live_locals_here); + debug!("loc = {:?}, live_locals = {:?}", loc, live_locals); // Add the locals live at this suspension point to the set of locals which live across // any suspension points - live_locals.union(&live_locals_here); + live_locals_at_any_suspension_point.union(&live_locals); - live_locals_at_suspension_points.push(live_locals_here); + live_locals_at_suspension_points.push(live_locals); } } - debug!("live_locals = {:?}", live_locals); + debug!("live_locals_anywhere = {:?}", live_locals_at_any_suspension_point); // Renumber our liveness_map bitsets to include only the locals we are // saving. let live_locals_at_suspension_points = live_locals_at_suspension_points .iter() - .map(|live_here| renumber_bitset(&live_here, &live_locals)) + .map(|live_here| renumber_bitset(&live_here, &live_locals_at_any_suspension_point)) .collect(); - let storage_conflicts = - compute_storage_conflicts(body_ref, &live_locals, &ignored, requires_storage_results); + let storage_conflicts = compute_storage_conflicts( + body_ref, + &live_locals_at_any_suspension_point, + always_live_locals.clone(), + requires_storage_results, + ); LivenessInfo { - live_locals, + live_locals: live_locals_at_any_suspension_point, live_locals_at_suspension_points, storage_conflicts, storage_liveness: storage_liveness_map, @@ -569,7 +555,7 @@ fn locals_live_across_suspend_points( /// `[0, 1, 2]`. Thus, if `input = [3, 5]` we would return `[1, 2]`. fn renumber_bitset( input: &BitSet, - stored_locals: &liveness::LiveVarSet, + stored_locals: &BitSet, ) -> BitSet { assert!(stored_locals.superset(&input), "{:?} not a superset of {:?}", stored_locals, input); let mut out = BitSet::new_empty(stored_locals.count()); @@ -589,19 +575,19 @@ fn renumber_bitset( /// computation; see `GeneratorLayout` for more. fn compute_storage_conflicts( body: &'mir Body<'tcx>, - stored_locals: &liveness::LiveVarSet, - ignored: &StorageIgnored, + stored_locals: &BitSet, + always_live_locals: storage::AlwaysLiveLocals, requires_storage: dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, ) -> BitMatrix { - assert_eq!(body.local_decls.len(), ignored.0.domain_size()); assert_eq!(body.local_decls.len(), stored_locals.domain_size()); + debug!("compute_storage_conflicts({:?})", body.span); - debug!("ignored = {:?}", ignored.0); + debug!("always_live = {:?}", always_live_locals); - // Storage ignored locals are not eligible for overlap, since their storage - // is always live. - let mut ineligible_locals = ignored.0.clone(); - ineligible_locals.intersect(&stored_locals); + // Locals that are always live or ones that need to be stored across + // suspension points are not eligible for overlap. + let mut ineligible_locals = always_live_locals.into_inner(); + ineligible_locals.intersect(stored_locals); // Compute the storage conflicts for all eligible locals. let mut visitor = StorageConflictVisitor { @@ -644,7 +630,7 @@ fn compute_storage_conflicts( struct StorageConflictVisitor<'mir, 'tcx, 's> { body: &'mir Body<'tcx>, - stored_locals: &'s liveness::LiveVarSet, + stored_locals: &'s BitSet, // FIXME(tmandry): Consider using sparse bitsets here once we have good // benchmarks for generators. local_conflicts: BitMatrix, @@ -653,7 +639,7 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> { impl dataflow::ResultsVisitor<'mir, 'tcx> for StorageConflictVisitor<'mir, 'tcx, '_> { type FlowState = BitSet; - fn visit_statement( + fn visit_statement_before_primary_effect( &mut self, state: &Self::FlowState, _statement: &'mir Statement<'tcx>, @@ -662,7 +648,7 @@ impl dataflow::ResultsVisitor<'mir, 'tcx> for StorageConflictVisitor<'mir, 'tcx, self.apply_state(state, loc); } - fn visit_terminator( + fn visit_terminator_before_primary_effect( &mut self, state: &Self::FlowState, _terminator: &'mir Terminator<'tcx>, @@ -675,10 +661,9 @@ impl dataflow::ResultsVisitor<'mir, 'tcx> for StorageConflictVisitor<'mir, 'tcx, impl<'body, 'tcx, 's> StorageConflictVisitor<'body, 'tcx, 's> { fn apply_state(&mut self, flow_state: &BitSet, loc: Location) { // Ignore unreachable blocks. - match self.body.basic_blocks()[loc.block].terminator().kind { - TerminatorKind::Unreachable => return, - _ => (), - }; + if self.body.basic_blocks()[loc.block].terminator().kind == TerminatorKind::Unreachable { + return; + } let mut eligible_storage_live = flow_state.clone(); eligible_storage_live.intersect(&self.stored_locals); @@ -698,12 +683,13 @@ fn compute_layout<'tcx>( source: MirSource<'tcx>, upvars: &Vec>, interior: Ty<'tcx>, + always_live_locals: &storage::AlwaysLiveLocals, movable: bool, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, ) -> ( FxHashMap, VariantIdx, usize)>, GeneratorLayout<'tcx>, - FxHashMap, + IndexVec>>, ) { // Use a liveness analysis to compute locals which are live across a suspension point let LivenessInfo { @@ -711,7 +697,7 @@ fn compute_layout<'tcx>( live_locals_at_suspension_points, storage_conflicts, storage_liveness, - } = locals_live_across_suspend_points(tcx, read_only!(body), source, movable); + } = locals_live_across_suspend_points(tcx, body, source, always_live_locals, movable); // Erase regions from the types passed in from typeck so we can compare them with // MIR types @@ -721,15 +707,18 @@ fn compute_layout<'tcx>( _ => bug!(), }; + let param_env = tcx.param_env(source.def_id()); + for (local, decl) in body.local_decls.iter_enumerated() { // Ignore locals which are internal or not live if !live_locals.contains(local) || decl.internal { continue; } + let decl_ty = tcx.normalize_erasing_regions(param_env, decl.ty); // Sanity check that typeck knows about the type of locals which are // live across a suspension point - if !allowed.contains(&decl.ty) && !allowed_upvars.contains(&decl.ty) { + if !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) { span_bug!( body.span, "Broken MIR: generator contains type {} in MIR, \ @@ -783,7 +772,7 @@ fn compute_layout<'tcx>( /// /// After this function, the former entry point of the function will be bb1. fn insert_switch<'tcx>( - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, cases: Vec<(usize, BasicBlock)>, transform: &TransformVisitor<'tcx>, default: TerminatorKind<'tcx>, @@ -797,7 +786,7 @@ fn insert_switch<'tcx>( targets: cases.iter().map(|&(_, d)| d).chain(iter::once(default_block)).collect(), }; - let source_info = source_info(body); + let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().raw.insert( 0, BasicBlockData { @@ -814,11 +803,7 @@ fn insert_switch<'tcx>( } } -fn elaborate_generator_drops<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - body: &mut BodyAndCache<'tcx>, -) { +fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut Body<'tcx>) { use crate::shim::DropShimElaborator; use crate::util::elaborate_drops::{elaborate_drop, Unwind}; use crate::util::patch::MirPatch; @@ -854,7 +839,7 @@ fn elaborate_generator_drops<'tcx>( elaborate_drop( &mut elaborator, *source_info, - &Place::from(SELF_ARG), + Place::from(SELF_ARG), (), *target, unwind, @@ -869,13 +854,13 @@ fn create_generator_drop_shim<'tcx>( transform: &TransformVisitor<'tcx>, source: MirSource<'tcx>, gen_ty: Ty<'tcx>, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, drop_clean: BasicBlock, -) -> BodyAndCache<'tcx> { +) -> Body<'tcx> { let mut body = body.clone(); body.arg_count = 1; // make sure the resume argument is not included here - let source_info = source_info(&body); + let source_info = SourceInfo::outermost(body.span); let mut cases = create_cases(&mut body, transform, Operation::Drop); @@ -894,28 +879,15 @@ fn create_generator_drop_shim<'tcx>( } // Replace the return variable - body.local_decls[RETURN_PLACE] = LocalDecl { - mutability: Mutability::Mut, - ty: tcx.mk_unit(), - user_ty: UserTypeProjections::none(), - source_info, - internal: false, - is_block_tail: None, - local_info: LocalInfo::Other, - }; + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.mk_unit(), source_info); make_generator_state_argument_indirect(tcx, &mut body); // Change the generator argument from &mut to *mut - body.local_decls[SELF_ARG] = LocalDecl { - mutability: Mutability::Mut, - ty: tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }), - user_ty: UserTypeProjections::none(), + body.local_decls[SELF_ARG] = LocalDecl::with_source_info( + tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }), source_info, - internal: false, - is_block_tail: None, - local_info: LocalInfo::Other, - }; + ); if tcx.sess.opts.debugging_opts.mir_emit_retag { // Alias tracking must know we changed the type body.basic_blocks_mut()[START_BLOCK].statements.insert( @@ -938,23 +910,18 @@ fn create_generator_drop_shim<'tcx>( body } -fn insert_term_block<'tcx>( - body: &mut BodyAndCache<'tcx>, - kind: TerminatorKind<'tcx>, -) -> BasicBlock { - let term_block = BasicBlock::new(body.basic_blocks().len()); - let source_info = source_info(body); +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, - }); - term_block + }) } fn insert_panic_block<'tcx>( tcx: TyCtxt<'tcx>, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, message: AssertMessage<'tcx>, ) -> BasicBlock { let assert_block = BasicBlock::new(body.basic_blocks().len()); @@ -970,7 +937,7 @@ fn insert_panic_block<'tcx>( cleanup: None, }; - let source_info = source_info(body); + let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().push(BasicBlockData { statements: Vec::new(), terminator: Some(Terminator { source_info, kind: term }), @@ -999,7 +966,7 @@ fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { // Nothing can unwind when landing pads are off. - if tcx.sess.no_landing_pads() { + if tcx.sess.panic_strategy() == PanicStrategy::Abort { return false; } @@ -1014,7 +981,8 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop | TerminatorKind::FalseEdges { .. } - | TerminatorKind::FalseUnwind { .. } => {} + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} // Resume will *continue* unwinding, but if there's no other unwinding terminator it // will never be reached. @@ -1040,16 +1008,15 @@ fn create_generator_resume_function<'tcx>( tcx: TyCtxt<'tcx>, transform: TransformVisitor<'tcx>, source: MirSource<'tcx>, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, can_return: bool, ) { let can_unwind = can_unwind(tcx, body); // Poison the generator when it unwinds if can_unwind { - let poison_block = BasicBlock::new(body.basic_blocks().len()); - let source_info = source_info(body); - body.basic_blocks_mut().push(BasicBlockData { + let source_info = SourceInfo::outermost(body.span); + let poison_block = body.basic_blocks_mut().push(BasicBlockData { statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)], terminator: Some(Terminator { source_info, kind: TerminatorKind::Resume }), is_cleanup: true, @@ -1079,7 +1046,7 @@ fn create_generator_resume_function<'tcx>( let mut cases = create_cases(body, &transform, Operation::Resume); - use rustc::mir::AssertKind::{ResumedAfterPanic, ResumedAfterReturn}; + use rustc_middle::mir::AssertKind::{ResumedAfterPanic, ResumedAfterReturn}; // Jump to the entry point on the unresumed cases.insert(0, (UNRESUMED, BasicBlock::new(0))); @@ -1115,28 +1082,22 @@ fn create_generator_resume_function<'tcx>( dump_mir(tcx, None, "generator_resume", &0, source, body, |_, _| Ok(())); } -fn source_info(body: &Body<'_>) -> SourceInfo { - SourceInfo { span: body.span, scope: OUTERMOST_SOURCE_SCOPE } -} - -fn insert_clean_drop(body: &mut BodyAndCache<'_>) -> BasicBlock { +fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock { let return_block = insert_term_block(body, TerminatorKind::Return); - // Create a block to destroy an unresumed generators. This can only destroy upvars. - let drop_clean = BasicBlock::new(body.basic_blocks().len()); let term = TerminatorKind::Drop { location: Place::from(SELF_ARG), target: return_block, unwind: None, }; - let source_info = source_info(body); + let source_info = SourceInfo::outermost(body.span); + + // Create a block to destroy an unresumed generators. This can only destroy upvars. body.basic_blocks_mut().push(BasicBlockData { statements: Vec::new(), terminator: Some(Terminator { source_info, kind: term }), is_cleanup: false, - }); - - drop_clean + }) } /// An operation that can be performed on a generator. @@ -1156,11 +1117,11 @@ impl Operation { } fn create_cases<'tcx>( - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, transform: &TransformVisitor<'tcx>, operation: Operation, ) -> Vec<(usize, BasicBlock)> { - let source_info = source_info(body); + let source_info = SourceInfo::outermost(body.span); transform .suspension_points @@ -1168,7 +1129,6 @@ fn create_cases<'tcx>( .filter_map(|point| { // Find the target for this suspension point, if applicable operation.target_block(point).map(|target| { - let block = BasicBlock::new(body.basic_blocks().len()); let mut statements = Vec::new(); // Create StorageLive instructions for locals with live storage @@ -1181,7 +1141,10 @@ fn create_cases<'tcx>( } let l = Local::new(i); - if point.storage_liveness.contains(l) && !transform.remap.contains_key(&l) { + let needs_storage_live = point.storage_liveness.contains(l) + && !transform.remap.contains_key(&l) + && !transform.always_live_locals.contains(l); + if needs_storage_live { statements .push(Statement { source_info, kind: StatementKind::StorageLive(l) }); } @@ -1200,7 +1163,7 @@ fn create_cases<'tcx>( } // Then jump to the real target - body.basic_blocks_mut().push(BasicBlockData { + let block = body.basic_blocks_mut().push(BasicBlockData { statements, terminator: Some(Terminator { source_info, @@ -1216,7 +1179,7 @@ fn create_cases<'tcx>( } impl<'tcx> MirPass<'tcx> for StateTransform { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { let yield_ty = if let Some(yield_ty) = body.yield_ty { yield_ty } else { @@ -1246,7 +1209,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { }; // Compute GeneratorState - let state_did = tcx.lang_items().gen_state().unwrap(); + let state_did = tcx.require_lang_item(GeneratorStateLangItem, None); let state_adt_ref = tcx.adt_def(state_did); let state_substs = tcx.intern_substs(&[yield_ty.into(), body.return_ty().into()]); let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); @@ -1264,7 +1227,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { replace_local(resume_local, body.local_decls[resume_local].ty, body, tcx); // When first entering the generator, move the resume argument into its new local. - let source_info = source_info(body); + let source_info = SourceInfo::outermost(body.span); let stmts = &mut body.basic_blocks_mut()[BasicBlock::new(0)].statements; stmts.insert( 0, @@ -1277,11 +1240,13 @@ impl<'tcx> MirPass<'tcx> for StateTransform { }, ); + let always_live_locals = storage::AlwaysLiveLocals::new(&body); + // Extract locals which are live across suspension point into `layout` // `remap` gives a mapping from local indices onto generator struct indices // `storage_liveness` tells us which locals have live storage at suspension points let (remap, layout, storage_liveness) = - compute_layout(tcx, source, &upvars, interior, movable, body); + compute_layout(tcx, source, &upvars, interior, &always_live_locals, movable, body); let can_return = can_return(tcx, body); @@ -1295,6 +1260,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { state_substs, remap, storage_liveness, + always_live_locals, suspension_points: Vec::new(), new_ret_local, discr_ty, diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 16c32e138fdf0..35d55c4cb9b6d 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -1,14 +1,14 @@ //! Inlining pass for MIR functions -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::mir::visit::*; -use rustc::mir::*; -use rustc::ty::subst::{Subst, SubstsRef}; -use rustc::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_attr as attr; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::visit::*; +use rustc_middle::mir::*; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_session::config::Sanitizer; use rustc_target::spec::abi::Abi; @@ -38,7 +38,7 @@ struct CallSite<'tcx> { } impl<'tcx> MirPass<'tcx> for Inline { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { Inliner { tcx, source }.run_pass(body); } @@ -51,7 +51,7 @@ struct Inliner<'tcx> { } impl Inliner<'tcx> { - fn run_pass(&self, caller_body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, caller_body: &mut Body<'tcx>) { // Keep a queue of callsites to try inlining on. We take // advantage of the fact that queries detect cycles here to // allow us to try and fetch the fully optimized MIR of a @@ -69,7 +69,7 @@ impl Inliner<'tcx> { let param_env = self.tcx.param_env(self.source.def_id()).with_reveal_all(); // Only do inlining into fn bodies. - let id = self.tcx.hir().as_local_hir_id(self.source.def_id()).unwrap(); + let id = self.tcx.hir().as_local_hir_id(self.source.def_id().expect_local()); if self.tcx.hir().body_owner_kind(id).is_fn_or_closure() && self.source.promoted.is_none() { for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated() { if let Some(callsite) = @@ -94,17 +94,15 @@ impl Inliner<'tcx> { continue; } - let self_node_id = self.tcx.hir().as_local_node_id(self.source.def_id()).unwrap(); - let callee_node_id = self.tcx.hir().as_local_node_id(callsite.callee); - - let callee_body = if let Some(callee_node_id) = callee_node_id { + let callee_body = if let Some(callee_def_id) = callsite.callee.as_local() { + let callee_hir_id = self.tcx.hir().as_local_hir_id(callee_def_id); + let self_hir_id = + self.tcx.hir().as_local_hir_id(self.source.def_id().expect_local()); // Avoid a cycle here by only using `optimized_mir` only if we have - // a lower node id than the callee. This ensures that the callee will + // a lower `HirId` than the callee. This ensures that the callee will // not inline us. This trick only works without incremental compilation. // So don't do it if that is enabled. - if !self.tcx.dep_graph.is_fully_enabled() - && self_node_id.as_u32() < callee_node_id.as_u32() - { + if !self.tcx.dep_graph.is_fully_enabled() && self_hir_id < callee_hir_id { self.tcx.optimized_mir(callsite.callee) } else { continue; @@ -125,6 +123,16 @@ impl Inliner<'tcx> { continue; }; + // Copy only unevaluated constants from the callee_body into the caller_body. + // Although we are only pushing `ConstKind::Unevaluated` consts to + // `required_consts`, here we may not only have `ConstKind::Unevaluated` + // because we are calling `subst_and_normalize_erasing_regions`. + caller_body.required_consts.extend( + callee_body.required_consts.iter().copied().filter(|&constant| { + matches!(constant.literal.val, ConstKind::Unevaluated(_, _, _)) + }), + ); + let start = caller_body.basic_blocks().len(); debug!("attempting to inline callsite {:?} - body={:?}", callsite, callee_body); if !self.inline_call(callsite, caller_body, callee_body) { @@ -178,7 +186,8 @@ impl Inliner<'tcx> { let terminator = bb_data.terminator(); if let TerminatorKind::Call { func: ref op, .. } = terminator.kind { if let ty::FnDef(callee_def_id, substs) = op.ty(caller_body, self.tcx).kind { - let instance = Instance::resolve(self.tcx, param_env, callee_def_id, substs)?; + let instance = + Instance::resolve(self.tcx, param_env, callee_def_id, substs).ok().flatten()?; if let InstanceDef::Virtual(..) = instance.def { return None; @@ -406,8 +415,8 @@ impl Inliner<'tcx> { fn inline_call( &self, callsite: CallSite<'tcx>, - caller_body: &mut BodyAndCache<'tcx>, - mut callee_body: BodyAndCache<'tcx>, + caller_body: &mut Body<'tcx>, + mut callee_body: Body<'tcx>, ) -> bool { let terminator = caller_body[callsite.bb].terminator.take().unwrap(); match terminator.kind { @@ -450,7 +459,7 @@ impl Inliner<'tcx> { // Place could result in two different locations if `f` // writes to `i`. To prevent this we need to create a temporary // borrow of the place and pass the destination as `*temp` instead. - fn dest_needs_borrow(place: &Place<'_>) -> bool { + fn dest_needs_borrow(place: Place<'_>) -> bool { for elem in place.projection.iter() { match elem { ProjectionElem::Deref | ProjectionElem::Index(_) => return true, @@ -461,7 +470,7 @@ impl Inliner<'tcx> { false } - let dest = if dest_needs_borrow(&destination.0) { + let dest = if dest_needs_borrow(destination.0) { debug!("creating temp for return destination"); let dest = Rvalue::Ref( self.tcx.lifetimes.re_erased, @@ -469,9 +478,9 @@ impl Inliner<'tcx> { destination.0, ); - let ty = dest.ty(&**caller_body, self.tcx); + let ty = dest.ty(caller_body, self.tcx); - let temp = LocalDecl::new_temp(ty, callsite.location.span); + let temp = LocalDecl::new(ty, callsite.location.span); let tmp = caller_body.local_decls.push(temp); let tmp = Place::from(tmp); @@ -535,7 +544,7 @@ impl Inliner<'tcx> { &self, args: Vec>, callsite: &CallSite<'tcx>, - caller_body: &mut BodyAndCache<'tcx>, + caller_body: &mut Body<'tcx>, ) -> Vec { let tcx = self.tcx; @@ -569,7 +578,7 @@ impl Inliner<'tcx> { assert!(args.next().is_none()); let tuple = Place::from(tuple); - let tuple_tys = if let ty::Tuple(s) = tuple.ty(&**caller_body, tcx).ty.kind { + let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind { s } else { bug!("Closure arguments are not passed as a tuple"); @@ -582,7 +591,7 @@ impl Inliner<'tcx> { let tuple_tmp_args = tuple_tys.iter().enumerate().map(|(i, ty)| { // This is e.g., `tuple_tmp.0` in our example above. let tuple_field = - Operand::Move(tcx.mk_place_field(tuple.clone(), Field::new(i), ty.expect_ty())); + Operand::Move(tcx.mk_place_field(tuple, Field::new(i), ty.expect_ty())); // Spill to a local to make e.g., `tmp0`. self.create_temp_if_necessary(tuple_field, callsite, caller_body) @@ -602,7 +611,7 @@ impl Inliner<'tcx> { &self, arg: Operand<'tcx>, callsite: &CallSite<'tcx>, - caller_body: &mut BodyAndCache<'tcx>, + caller_body: &mut Body<'tcx>, ) -> Local { // FIXME: Analysis of the usage of the arguments to avoid // unnecessary temporaries. @@ -620,9 +629,9 @@ impl Inliner<'tcx> { // Otherwise, create a temporary for the arg let arg = Rvalue::Use(arg); - let ty = arg.ty(&**caller_body, self.tcx); + let ty = arg.ty(caller_body, self.tcx); - let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span); + let arg_tmp = LocalDecl::new(ty, callsite.location.span); let arg_tmp = caller_body.local_decls.push(arg_tmp); let stmt = Statement { @@ -706,18 +715,6 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { self.super_place(place, context, location) } - fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option> { - if let PlaceElem::Index(local) = elem { - let new_local = self.make_integrate_local(*local); - - if new_local != *local { - return Some(PlaceElem::Index(new_local)); - } - } - - None - } - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { self.in_cleanup_block = data.is_cleanup; self.super_basic_block_data(block, data); @@ -735,7 +732,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } fn visit_terminator_kind(&mut self, kind: &mut TerminatorKind<'tcx>, loc: Location) { - self.super_terminator_kind(kind, loc); + // Don't try to modify the implicit `_0` access on return (`return` terminators are + // replaced down below anyways). + if !matches!(kind, TerminatorKind::Return) { + self.super_terminator_kind(kind, loc); + } match *kind { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => bug!(), @@ -799,6 +800,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { { bug!("False unwinds should have been removed before inlining") } + TerminatorKind::InlineAsm { ref mut destination, .. } => { + if let Some(ref mut tgt) = *destination { + *tgt = self.update_target(*tgt); + } + } } } diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 48b4d00a2e9b6..dee37f767e979 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -1,32 +1,25 @@ //! Performs various peephole optimizations. use crate::transform::{MirPass, MirSource}; -use rustc::mir::visit::{MutVisitor, Visitor}; -use rustc::mir::{ - read_only, Body, BodyAndCache, Constant, Local, Location, Operand, Place, PlaceRef, - ProjectionElem, Rvalue, -}; -use rustc::ty::{self, TyCtxt}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::vec::Idx; +use rustc_middle::mir::visit::{MutVisitor, Visitor}; +use rustc_middle::mir::{ + Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, +}; +use rustc_middle::ty::{self, TyCtxt}; use std::mem; pub struct InstCombine; impl<'tcx> MirPass<'tcx> for InstCombine { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { - // We only run when optimizing MIR (at any level). - if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { - return; - } - + fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. // `Place::ty()`). let optimizations = { - let read_only_cache = read_only!(body); let mut optimization_finder = OptimizationFinder::new(body, tcx); - optimization_finder.visit_body(read_only_cache); + optimization_finder.visit_body(body); optimization_finder.optimizations }; diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 50868434baa32..0551ed5a15ddb 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,14 +1,16 @@ use crate::{shim, util}; -use rustc::mir::{BodyAndCache, ConstQualifs, MirPhase, Promoted}; -use rustc::ty::query::Providers; -use rustc::ty::steal::Steal; -use rustc::ty::{InstanceDef, TyCtxt, TypeFoldable}; -use rustc_ast::ast; +use required_consts::RequiredConstsVisitor; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_index::vec::IndexVec; -use rustc_span::Span; +use rustc_middle::mir::visit::Visitor as _; +use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::steal::Steal; +use rustc_middle::ty::{InstanceDef, TyCtxt, TypeFoldable}; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; pub mod add_call_guards; @@ -26,9 +28,11 @@ pub mod generator; pub mod inline; pub mod instcombine; pub mod no_landing_pads; +pub mod nrvo; pub mod promote_consts; pub mod qualify_min_const_fn; pub mod remove_noop_landing_pads; +pub mod required_consts; pub mod rustc_peek; pub mod simplify; pub mod simplify_branches; @@ -51,15 +55,15 @@ pub(crate) fn provide(providers: &mut Providers<'_>) { } fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.mir_keys(def_id.krate).contains(&def_id) + tcx.mir_keys(def_id.krate).contains(&def_id.expect_local()) } /// Finds the full set of `DefId`s within the current crate that have /// MIR associated with them. -fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> &DefIdSet { +fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> FxHashSet { assert_eq!(krate, LOCAL_CRATE); - let mut set = DefIdSet::default(); + let mut set = FxHashSet::default(); // All body-owners have MIR associated with them. set.extend(tcx.body_owners()); @@ -68,13 +72,13 @@ fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> &DefIdSet { // they don't have a BodyId, so we need to build them separately. struct GatherCtors<'a, 'tcx> { tcx: TyCtxt<'tcx>, - set: &'a mut DefIdSet, + set: &'a mut FxHashSet, } impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> { fn visit_variant_data( &mut self, v: &'tcx hir::VariantData<'tcx>, - _: ast::Name, + _: Symbol, _: &'tcx hir::Generics<'tcx>, _: hir::HirId, _: Span, @@ -93,7 +97,7 @@ fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> &DefIdSet { .krate() .visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor()); - tcx.arena.alloc(set) + set } /// Where a specific `mir::Body` comes from. @@ -131,16 +135,16 @@ pub trait MirPass<'tcx> { default_name::() } - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>); + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>); } pub fn run_passes( tcx: TyCtxt<'tcx>, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, instance: InstanceDef<'tcx>, promoted: Option, mir_phase: MirPhase, - passes: &[&dyn MirPass<'tcx>], + passes: &[&[&dyn MirPass<'tcx>]], ) { let phase_index = mir_phase.phase_index(); @@ -168,15 +172,17 @@ pub fn run_passes( index += 1; }; - for pass in passes { - run_pass(*pass); + for pass_group in passes { + for pass in *pass_group { + run_pass(*pass); + } } body.phase = mir_phase; } fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { - let const_kind = check_consts::ConstKind::for_item(tcx, def_id); + let const_kind = tcx.hir().body_const_context(def_id.expect_local()); // No need to const-check a non-const `fn`. if const_kind.is_none() { @@ -194,15 +200,10 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { return Default::default(); } - let item = check_consts::Item { - body: body.unwrap_read_only(), - tcx, - def_id, - const_kind, - param_env: tcx.param_env(def_id), - }; + let ccx = + check_consts::ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) }; - let mut validator = check_consts::validation::Validator::new(&item); + let mut validator = check_consts::validation::Validator::new(&ccx); validator.check_body(); // We return the qualifs in the return place for every MIR body, even though it is only used @@ -210,51 +211,62 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { validator.qualifs_in_return_place() } -fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal> { +fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> Steal> { + let def_id = def_id.expect_local(); + // Unsafety check uses the raw mir, so make sure it is run let _ = tcx.unsafety_check_result(def_id); let mut body = tcx.mir_built(def_id).steal(); - util::dump_mir(tcx, None, "mir_map", &0, MirSource::item(def_id), &body, |_, _| Ok(())); + util::dump_mir(tcx, None, "mir_map", &0, MirSource::item(def_id.to_def_id()), &body, |_, _| { + Ok(()) + }); run_passes( tcx, &mut body, - InstanceDef::Item(def_id), + InstanceDef::Item(def_id.to_def_id()), None, MirPhase::Const, - &[ + &[&[ // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, - ], + ]], ); - body.ensure_predecessors(); tcx.alloc_steal_mir(body) } fn mir_validated( tcx: TyCtxt<'tcx>, - def_id: DefId, -) -> (&'tcx Steal>, &'tcx Steal>>) { + def_id: LocalDefId, +) -> (Steal>, Steal>>) { // Ensure that we compute the `mir_const_qualif` for constants at // this point, before we steal the mir-const result. - let _ = tcx.mir_const_qualif(def_id); + let _ = tcx.mir_const_qualif(def_id.to_def_id()); + + let mut body = tcx.mir_const(def_id.to_def_id()).steal(); + + let mut required_consts = Vec::new(); + let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); + for (bb, bb_data) in traversal::reverse_postorder(&body) { + required_consts_visitor.visit_basic_block_data(bb, bb_data); + } + body.required_consts = required_consts; - let mut body = tcx.mir_const(def_id).steal(); let promote_pass = promote_consts::PromoteTemps::default(); run_passes( tcx, &mut body, - InstanceDef::Item(def_id), + InstanceDef::Item(def_id.to_def_id()), None, MirPhase::Validated, - &[ + &[&[ // What we need to run borrowck etc. &promote_pass, &simplify::SimplifyCfg::new("qualify-consts"), - ], + ]], ); let promoted = promote_pass.promoted_fragments.into_inner(); @@ -263,65 +275,93 @@ fn mir_validated( fn run_optimization_passes<'tcx>( tcx: TyCtxt<'tcx>, - body: &mut BodyAndCache<'tcx>, - def_id: DefId, + body: &mut Body<'tcx>, + def_id: LocalDefId, promoted: Option, ) { + let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[ + // Remove all things only needed by analysis + &no_landing_pads::NoLandingPads::new(tcx), + &simplify_branches::SimplifyBranches::new("initial"), + &remove_noop_landing_pads::RemoveNoopLandingPads, + &cleanup_post_borrowck::CleanupNonCodegenStatements, + &simplify::SimplifyCfg::new("early-opt"), + // These next passes must be executed together + &add_call_guards::CriticalCallEdges, + &elaborate_drops::ElaborateDrops, + &no_landing_pads::NoLandingPads::new(tcx), + // AddMovesForPackedDrops needs to run after drop + // elaboration. + &add_moves_for_packed_drops::AddMovesForPackedDrops, + // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, + // but before optimizations begin. + &add_retag::AddRetag, + &simplify::SimplifyCfg::new("elaborate-drops"), + // No lifetime analysis based on borrowing can be done from here on out. + ]; + + let optimizations: &[&dyn MirPass<'tcx>] = &[ + &unreachable_prop::UnreachablePropagation, + &uninhabited_enum_branching::UninhabitedEnumBranching, + &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"), + &inline::Inline, + // Lowering generator control-flow and variables has to happen before we do anything else + // to them. We do this inside the "optimizations" block so that it can benefit from + // optimizations that run before, that might be harder to do on the state machine than MIR + // with async primitives. + &generator::StateTransform, + &instcombine::InstCombine, + &const_prop::ConstProp, + &simplify_branches::SimplifyBranches::new("after-const-prop"), + // Run deaggregation here because: + // 1. Some codegen backends require it + // 2. It creates additional possibilities for some MIR optimizations to trigger + // FIXME(#70073): Why is this done here and not in `post_borrowck_cleanup`? + &deaggregator::Deaggregator, + &simplify_try::SimplifyArmIdentity, + &simplify_try::SimplifyBranchSame, + ©_prop::CopyPropagation, + &simplify_branches::SimplifyBranches::new("after-copy-prop"), + &remove_noop_landing_pads::RemoveNoopLandingPads, + &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"), + &simplify::SimplifyCfg::new("final"), + &nrvo::RenameReturnPlace, + &simplify::SimplifyLocals, + ]; + + let no_optimizations: &[&dyn MirPass<'tcx>] = &[ + // Even if we don't do optimizations, we still have to lower generators for codegen. + &generator::StateTransform, + // FIXME(#70073): This pass is responsible for both optimization as well as some lints. + &const_prop::ConstProp, + // Even if we don't do optimizations, still run deaggregation because some backends assume + // that deaggregation always occurs. + &deaggregator::Deaggregator, + ]; + + let pre_codegen_cleanup: &[&dyn MirPass<'tcx>] = &[ + &add_call_guards::CriticalCallEdges, + // Dump the end result for testing and debugging purposes. + &dump_mir::Marker("PreCodegen"), + ]; + + let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level; + run_passes( tcx, body, - InstanceDef::Item(def_id), + InstanceDef::Item(def_id.to_def_id()), promoted, MirPhase::Optimized, &[ - // Remove all things only needed by analysis - &no_landing_pads::NoLandingPads::new(tcx), - &simplify_branches::SimplifyBranches::new("initial"), - &remove_noop_landing_pads::RemoveNoopLandingPads, - &cleanup_post_borrowck::CleanupNonCodegenStatements, - &simplify::SimplifyCfg::new("early-opt"), - // These next passes must be executed together - &add_call_guards::CriticalCallEdges, - &elaborate_drops::ElaborateDrops, - &no_landing_pads::NoLandingPads::new(tcx), - // AddMovesForPackedDrops needs to run after drop - // elaboration. - &add_moves_for_packed_drops::AddMovesForPackedDrops, - // AddRetag needs to run after ElaborateDrops, and it needs - // an AllCallEdges pass right before it. Otherwise it should - // run fairly late, but before optimizations begin. - &add_call_guards::AllCallEdges, - &add_retag::AddRetag, - &simplify::SimplifyCfg::new("elaborate-drops"), - // No lifetime analysis based on borrowing can be done from here on out. - - // Optimizations begin. - &unreachable_prop::UnreachablePropagation, - &uninhabited_enum_branching::UninhabitedEnumBranching, - &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"), - &inline::Inline, - // Lowering generator control-flow and variables - // has to happen before we do anything else to them. - &generator::StateTransform, - &instcombine::InstCombine, - &const_prop::ConstProp, - &simplify_branches::SimplifyBranches::new("after-const-prop"), - &deaggregator::Deaggregator, - ©_prop::CopyPropagation, - &simplify_branches::SimplifyBranches::new("after-copy-prop"), - &remove_noop_landing_pads::RemoveNoopLandingPads, - &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"), - &simplify_try::SimplifyArmIdentity, - &simplify_try::SimplifyBranchSame, - &simplify::SimplifyCfg::new("final"), - &simplify::SimplifyLocals, - &add_call_guards::CriticalCallEdges, - &dump_mir::Marker("PreCodegen"), + post_borrowck_cleanup, + if mir_opt_level > 0 { optimizations } else { no_optimizations }, + pre_codegen_cleanup, ], ); } -fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &BodyAndCache<'_> { +fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> { if tcx.is_constructor(def_id) { // There's no reason to run all of the MIR passes on constructors when // we can just output the MIR we want directly. This also saves const @@ -330,6 +370,8 @@ fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &BodyAndCache<'_> { return shim::build_adt_ctor(tcx, def_id); } + let def_id = def_id.expect_local(); + // (Mir-)Borrowck uses `mir_validated`, so we have to force it to // execute before we can steal. tcx.ensure().mir_borrowck(def_id); @@ -337,28 +379,28 @@ fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &BodyAndCache<'_> { let (body, _) = tcx.mir_validated(def_id); let mut body = body.steal(); run_optimization_passes(tcx, &mut body, def_id, None); - body.ensure_predecessors(); debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); - tcx.arena.alloc(body) + body } -fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &IndexVec> { +fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> IndexVec> { if tcx.is_constructor(def_id) { - return tcx.intern_promoted(IndexVec::new()); + return IndexVec::new(); } + let def_id = def_id.expect_local(); + tcx.ensure().mir_borrowck(def_id); let (_, promoted) = tcx.mir_validated(def_id); let mut promoted = promoted.steal(); for (p, mut body) in promoted.iter_enumerated_mut() { run_optimization_passes(tcx, &mut body, def_id, Some(p)); - body.ensure_predecessors(); } debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR"); - tcx.intern_promoted(promoted) + promoted } diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index e17c733e8bb5f..3bffafa1b2f9c 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -2,9 +2,10 @@ //! specified. use crate::transform::{MirPass, MirSource}; -use rustc::mir::visit::MutVisitor; -use rustc::mir::*; -use rustc::ty::TyCtxt; +use rustc_middle::mir::visit::MutVisitor; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_target::spec::PanicStrategy; pub struct NoLandingPads<'tcx> { tcx: TyCtxt<'tcx>, @@ -17,13 +18,13 @@ impl<'tcx> NoLandingPads<'tcx> { } impl<'tcx> MirPass<'tcx> for NoLandingPads<'tcx> { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { no_landing_pads(tcx, body) } } -pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyAndCache<'tcx>) { - if tcx.sess.no_landing_pads() { +pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.panic_strategy() == PanicStrategy::Abort { NoLandingPads::new(tcx).visit_body(body); } } diff --git a/src/librustc_mir/transform/nrvo.rs b/src/librustc_mir/transform/nrvo.rs new file mode 100644 index 0000000000000..941ffa94aa857 --- /dev/null +++ b/src/librustc_mir/transform/nrvo.rs @@ -0,0 +1,238 @@ +use rustc_hir::Mutability; +use rustc_index::bit_set::HybridBitSet; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::{self, BasicBlock, Local, Location}; +use rustc_middle::ty::TyCtxt; + +use crate::transform::{MirPass, MirSource}; + +/// This pass looks for MIR that always copies the same local into the return place and eliminates +/// the copy by renaming all uses of that local to `_0`. +/// +/// This allows LLVM to perform an optimization similar to the named return value optimization +/// (NRVO) that is guaranteed in C++. This avoids a stack allocation and `memcpy` for the +/// relatively common pattern of allocating a buffer on the stack, mutating it, and returning it by +/// value like so: +/// +/// ```rust +/// fn foo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { +/// let mut buf = [0; 1024]; +/// init(&mut buf); +/// buf +/// } +/// ``` +/// +/// For now, this pass is very simple and only capable of eliminating a single copy. A more general +/// version of copy propagation, such as the one based on non-overlapping live ranges in [#47954] and +/// [#71003], could yield even more benefits. +/// +/// [#47954]: https://github.com/rust-lang/rust/pull/47954 +/// [#71003]: https://github.com/rust-lang/rust/pull/71003 +pub struct RenameReturnPlace; + +impl<'tcx> MirPass<'tcx> for RenameReturnPlace { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut mir::Body<'tcx>) { + if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { + return; + } + + let returned_local = match local_eligible_for_nrvo(body) { + Some(l) => l, + None => { + debug!("`{:?}` was ineligible for NRVO", src.def_id()); + return; + } + }; + + // Sometimes, the return place is assigned a local of a different but coercable type, for + // example `&T` instead of `&mut T`. Overwriting the `LocalInfo` for the return place would + // result in it having an incorrect type. Although this doesn't seem to cause a problem in + // codegen, bail out anyways since it happens so rarely. + let ret_ty = body.local_decls[mir::RETURN_PLACE].ty; + let assigned_ty = body.local_decls[returned_local].ty; + if ret_ty != assigned_ty { + debug!("`{:?}` was eligible for NRVO but for type mismatch", src.def_id()); + debug!("typeof(_0) != typeof({:?}); {:?} != {:?}", returned_local, ret_ty, assigned_ty); + return; + } + + debug!( + "`{:?}` was eligible for NRVO, making {:?} the return place", + src.def_id(), + returned_local + ); + + RenameToReturnPlace { tcx, to_rename: returned_local }.visit_body(body); + + // Clean up the `NOP`s we inserted for statements made useless by our renaming. + for block_data in body.basic_blocks_mut() { + block_data.statements.retain(|stmt| stmt.kind != mir::StatementKind::Nop); + } + + // Overwrite the debuginfo of `_0` with that of the renamed local. + let (renamed_decl, ret_decl) = + body.local_decls.pick2_mut(returned_local, mir::RETURN_PLACE); + ret_decl.clone_from(renamed_decl); + + // The return place is always mutable. + ret_decl.mutability = Mutability::Mut; + } +} + +/// MIR that is eligible for the NRVO must fulfill two conditions: +/// 1. The return place must not be read prior to the `Return` terminator. +/// 2. A simple assignment of a whole local to the return place (e.g., `_0 = _1`) must be the +/// only definition of the return place reaching the `Return` terminator. +/// +/// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned +/// to the return place along all possible paths through the control-flow graph. +fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option { + if IsReturnPlaceRead::run(body) { + return None; + } + + let mut copied_to_return_place = None; + for block in body.basic_blocks().indices() { + // Look for blocks with a `Return` terminator. + if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) { + continue; + } + + // Look for an assignment of a single local to the return place prior to the `Return`. + let returned_local = find_local_assigned_to_return_place(block, body)?; + match body.local_kind(returned_local) { + // FIXME: Can we do this for arguments as well? + mir::LocalKind::Arg => return None, + + mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"), + mir::LocalKind::Var | mir::LocalKind::Temp => {} + } + + // If multiple different locals are copied to the return place. We can't pick a + // single one to rename. + if copied_to_return_place.map_or(false, |old| old != returned_local) { + return None; + } + + copied_to_return_place = Some(returned_local); + } + + return copied_to_return_place; +} + +fn find_local_assigned_to_return_place( + start: BasicBlock, + body: &mut mir::Body<'_>, +) -> Option { + let mut block = start; + let mut seen = HybridBitSet::new_empty(body.basic_blocks().len()); + + // Iterate as long as `block` has exactly one predecessor that we have not yet visited. + while seen.insert(block) { + trace!("Looking for assignments to `_0` in {:?}", block); + + let local = body[block].statements.iter().rev().find_map(as_local_assigned_to_return_place); + if local.is_some() { + return local; + } + + match body.predecessors()[block].as_slice() { + &[pred] => block = pred, + _ => return None, + } + } + + return None; +} + +// If this statement is an assignment of an unprojected local to the return place, +// return that local. +fn as_local_assigned_to_return_place(stmt: &mir::Statement<'_>) -> Option { + if let mir::StatementKind::Assign(box (lhs, rhs)) = &stmt.kind { + if lhs.as_local() == Some(mir::RETURN_PLACE) { + if let mir::Rvalue::Use(mir::Operand::Copy(rhs) | mir::Operand::Move(rhs)) = rhs { + return rhs.as_local(); + } + } + } + + None +} + +struct RenameToReturnPlace<'tcx> { + to_rename: Local, + tcx: TyCtxt<'tcx>, +} + +/// Replaces all uses of `self.to_rename` with `_0`. +impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_statement(&mut self, stmt: &mut mir::Statement<'tcx>, loc: Location) { + // Remove assignments of the local being replaced to the return place, since it is now the + // return place: + // _0 = _1 + if as_local_assigned_to_return_place(stmt) == Some(self.to_rename) { + stmt.kind = mir::StatementKind::Nop; + return; + } + + // Remove storage annotations for the local being replaced: + // StorageLive(_1) + if let mir::StatementKind::StorageLive(local) | mir::StatementKind::StorageDead(local) = + stmt.kind + { + if local == self.to_rename { + stmt.kind = mir::StatementKind::Nop; + return; + } + } + + self.super_statement(stmt, loc) + } + + fn visit_terminator(&mut self, terminator: &mut mir::Terminator<'tcx>, loc: Location) { + // Ignore the implicit "use" of the return place in a `Return` statement. + if let mir::TerminatorKind::Return = terminator.kind { + return; + } + + self.super_terminator(terminator, loc); + } + + fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { + assert_ne!(*l, mir::RETURN_PLACE); + if *l == self.to_rename { + *l = mir::RETURN_PLACE; + } + } +} + +struct IsReturnPlaceRead(bool); + +impl IsReturnPlaceRead { + fn run(body: &mir::Body<'_>) -> bool { + let mut vis = IsReturnPlaceRead(false); + vis.visit_body(body); + vis.0 + } +} + +impl Visitor<'tcx> for IsReturnPlaceRead { + fn visit_local(&mut self, &l: &Local, ctxt: PlaceContext, _: Location) { + if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() { + self.0 = true; + } + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, loc: Location) { + // Ignore the implicit "use" of the return place in a `Return` statement. + if let mir::TerminatorKind::Return = terminator.kind { + return; + } + + self.super_terminator(terminator, loc); + } +} diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index a27c80d91cfd0..13a8b9a1000c9 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -12,14 +12,15 @@ //! initialization and can otherwise silence errors, if //! move analysis runs after promotion on broken MIR. -use rustc::mir::traversal::ReversePostorder; -use rustc::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; -use rustc::mir::*; -use rustc::ty::cast::CastTy; -use rustc::ty::subst::InternalSubsts; -use rustc::ty::{self, List, TyCtxt, TypeFoldable}; use rustc_ast::ast::LitKind; +use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::mir::traversal::ReversePostorder; +use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::cast::CastTy; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable}; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; @@ -27,10 +28,10 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_target::spec::abi::Abi; use std::cell::Cell; -use std::{cmp, iter, mem, usize}; +use std::{cmp, iter, mem}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; -use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstKind, Item}; +use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx}; use crate::transform::{MirPass, MirSource}; /// A `MirPass` for promotion. @@ -42,11 +43,11 @@ use crate::transform::{MirPass, MirSource}; /// newly created `Constant`. #[derive(Default)] pub struct PromoteTemps<'tcx> { - pub promoted_fragments: Cell>>, + pub promoted_fragments: Cell>>, } impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { // There's not really any point in promoting errorful MIR. // // This does not include MIR that failed const-checking, which we still try to promote. @@ -62,10 +63,10 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { let def_id = src.def_id(); let mut rpo = traversal::reverse_postorder(body); - let (temps, all_candidates) = collect_temps_and_candidates(tcx, body, &mut rpo); + let ccx = ConstCx::new(tcx, def_id.expect_local(), body); + let (temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo); - let promotable_candidates = - validate_candidates(tcx, read_only!(body), def_id, &temps, &all_candidates); + let promotable_candidates = validate_candidates(&ccx, &temps, &all_candidates); let promoted = promote_candidates(def_id, body, tcx, temps, promotable_candidates); self.promoted_fragments.set(promoted); @@ -112,6 +113,9 @@ pub enum Candidate { /// the attribute currently provides the semantic requirement that arguments /// must be constant. Argument { bb: BasicBlock, index: usize }, + + /// `const` operand in asm!. + InlineAsm { bb: BasicBlock, index: usize }, } impl Candidate { @@ -119,7 +123,7 @@ impl Candidate { fn forces_explicit_promotion(&self) -> bool { match self { Candidate::Ref(_) | Candidate::Repeat(_) => false, - Candidate::Argument { .. } => true, + Candidate::Argument { .. } | Candidate::InlineAsm { .. } => true, } } } @@ -140,8 +144,7 @@ fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { } struct Collector<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, + ccx: &'a ConstCx<'a, 'tcx>, temps: IndexVec, candidates: Vec, span: Span, @@ -151,7 +154,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { fn visit_local(&mut self, &index: &Local, context: PlaceContext, location: Location) { debug!("visit_local: index={:?} context={:?} location={:?}", index, context, location); // We're only interested in temporaries and the return place - match self.body.local_kind(index) { + match self.ccx.body.local_kind(index) { LocalKind::Temp | LocalKind::ReturnPointer => {} LocalKind::Arg | LocalKind::Var => return, } @@ -204,7 +207,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { Rvalue::Ref(..) => { self.candidates.push(Candidate::Ref(location)); } - Rvalue::Repeat(..) if self.tcx.features().const_in_array_repeat_expressions => { + Rvalue::Repeat(..) if self.ccx.tcx.features().const_in_array_repeat_expressions => { // FIXME(#49147) only promote the element when it isn't `Copy` // (so that code that can copy it at runtime is unaffected). self.candidates.push(Candidate::Repeat(location)); @@ -216,25 +219,39 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) { self.super_terminator_kind(kind, location); - if let TerminatorKind::Call { ref func, .. } = *kind { - if let ty::FnDef(def_id, _) = func.ty(self.body, self.tcx).kind { - let fn_sig = self.tcx.fn_sig(def_id); - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { - let name = self.tcx.item_name(def_id); - // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles. - if name.as_str().starts_with("simd_shuffle") { - self.candidates.push(Candidate::Argument { bb: location.block, index: 2 }); + match *kind { + TerminatorKind::Call { ref func, .. } => { + if let ty::FnDef(def_id, _) = func.ty(self.ccx.body, self.ccx.tcx).kind { + let fn_sig = self.ccx.tcx.fn_sig(def_id); + if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { + let name = self.ccx.tcx.item_name(def_id); + // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles. + if name.as_str().starts_with("simd_shuffle") { + self.candidates + .push(Candidate::Argument { bb: location.block, index: 2 }); + + return; // Don't double count `simd_shuffle` candidates + } + } - return; // Don't double count `simd_shuffle` candidates + if let Some(constant_args) = args_required_const(self.ccx.tcx, def_id) { + for index in constant_args { + self.candidates.push(Candidate::Argument { bb: location.block, index }); + } } } - - if let Some(constant_args) = args_required_const(self.tcx, def_id) { - for index in constant_args { - self.candidates.push(Candidate::Argument { bb: location.block, index }); + } + TerminatorKind::InlineAsm { ref operands, .. } => { + for (index, op) in operands.iter().enumerate() { + match op { + InlineAsmOperand::Const { .. } => { + self.candidates.push(Candidate::InlineAsm { bb: location.block, index }) + } + _ => {} } } } + _ => {} } } @@ -244,16 +261,14 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { } pub fn collect_temps_and_candidates( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, + ccx: &ConstCx<'mir, 'tcx>, rpo: &mut ReversePostorder<'_, 'tcx>, ) -> (IndexVec, Vec) { let mut collector = Collector { - tcx, - body, - temps: IndexVec::from_elem(TempState::Undefined, &body.local_decls), + temps: IndexVec::from_elem(TempState::Undefined, &ccx.body.local_decls), candidates: vec![], - span: body.span, + span: ccx.body.span, + ccx, }; for (bb, data) in rpo { collector.visit_basic_block_data(bb, data); @@ -265,7 +280,7 @@ pub fn collect_temps_and_candidates( /// /// This wraps an `Item`, and has access to all fields of that `Item` via `Deref` coercion. struct Validator<'a, 'tcx> { - item: Item<'a, 'tcx>, + ccx: &'a ConstCx<'a, 'tcx>, temps: &'a IndexVec, /// Explicit promotion happens e.g. for constant arguments declared via @@ -278,10 +293,10 @@ struct Validator<'a, 'tcx> { } impl std::ops::Deref for Validator<'a, 'tcx> { - type Target = Item<'a, 'tcx>; + type Target = ConstCx<'a, 'tcx>; fn deref(&self) -> &Self::Target { - &self.item + &self.ccx } } @@ -329,7 +344,7 @@ impl<'tcx> Validator<'_, 'tcx> { // FIXME(eddyb) this is probably excessive, with // the exception of `union` member accesses. let ty = - Place::ty_from(place.local, proj_base, *self.body, self.tcx) + Place::ty_from(place.local, proj_base, self.body, self.tcx) .projection_ty(self.tcx, elem) .ty; if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) { @@ -350,12 +365,14 @@ impl<'tcx> Validator<'_, 'tcx> { } if let BorrowKind::Mut { .. } = kind { - let ty = place.ty(*self.body, self.tcx).ty; + let ty = place.ty(self.body, self.tcx).ty; // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(ConstKind::StaticMut) { + if self.const_kind + == Some(hir::ConstContext::Static(hir::Mutability::Mut)) + { // Inside a `static mut`, &mut [...] is also allowed. match ty.kind { ty::Array(..) | ty::Slice(_) => {} @@ -402,6 +419,18 @@ impl<'tcx> Validator<'_, 'tcx> { _ => bug!(), } } + Candidate::InlineAsm { bb, index } => { + assert!(self.explicit); + + let terminator = self.body[bb].terminator(); + match &terminator.kind { + TerminatorKind::InlineAsm { operands, .. } => match &operands[index] { + InlineAsmOperand::Const { value } => self.validate_operand(value), + _ => bug!(), + }, + _ => bug!(), + } + } } } @@ -414,7 +443,7 @@ impl<'tcx> Validator<'_, 'tcx> { let statement = &self.body[loc.block].statements[loc.statement_index]; match &statement.kind { StatementKind::Assign(box (_, rhs)) => qualifs::in_rvalue::( - &self.item, + &self.ccx, &mut |l| self.qualif_local::(l), rhs, ), @@ -431,7 +460,7 @@ impl<'tcx> Validator<'_, 'tcx> { match &terminator.kind { TerminatorKind::Call { .. } => { let return_ty = self.body.local_decls[local].ty; - Q::in_any_value_of_ty(&self.item, return_ty) + Q::in_any_value_of_ty(&self.ccx, return_ty) } kind => { span_bug!(terminator.source_info.span, "{:?} not promotable", kind); @@ -494,7 +523,7 @@ impl<'tcx> Validator<'_, 'tcx> { ProjectionElem::Field(..) => { if self.const_kind.is_none() { let base_ty = - Place::ty_from(place.local, proj_base, *self.body, self.tcx).ty; + Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; if let Some(def) = base_ty.ty_adt_def() { // No promotion of union field accesses. if def.is_union() { @@ -520,12 +549,12 @@ impl<'tcx> Validator<'_, 'tcx> { if let Some(def_id) = c.check_static_ptr(self.tcx) { // Only allow statics (not consts) to refer to other statics. // FIXME(eddyb) does this matter at all for promotion? - let is_static = self.const_kind.map_or(false, |k| k.is_static()); + let is_static = matches!(self.const_kind, Some(hir::ConstContext::Static(_))); if !is_static { return Err(Unpromotable); } - let is_thread_local = self.tcx.has_attr(def_id, sym::thread_local); + let is_thread_local = self.tcx.is_thread_local_static(def_id); if is_thread_local { return Err(Unpromotable); } @@ -539,11 +568,11 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match *rvalue { Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => { - let operand_ty = operand.ty(*self.body, self.tcx); + let operand_ty = operand.ty(self.body, self.tcx); let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); match (cast_in, cast_out) { - (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { + (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { // in normal functions, mark such casts as not promotable return Err(Unpromotable); } @@ -552,7 +581,7 @@ impl<'tcx> Validator<'_, 'tcx> { } Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(*self.body, self.tcx).kind { + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { assert!( op == BinOp::Eq || op == BinOp::Ne @@ -592,7 +621,7 @@ impl<'tcx> Validator<'_, 'tcx> { // Raw reborrows can come from reference to pointer coercions, // so are allowed. if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, *self.body, self.tcx).ty; + let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind { return self.validate_place(PlaceRef { local: place.local, @@ -605,12 +634,12 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::Ref(_, kind, place) => { if let BorrowKind::Mut { .. } = kind { - let ty = place.ty(*self.body, self.tcx).ty; + let ty = place.ty(self.body, self.tcx).ty; // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(ConstKind::StaticMut) { + if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) { // Inside a `static mut`, &mut [...] is also allowed. match ty.kind { ty::Array(..) | ty::Slice(_) => {} @@ -631,7 +660,7 @@ impl<'tcx> Validator<'_, 'tcx> { // Special-case reborrows to be more like a copy of the reference. let mut place = place.as_ref(); if let [proj_base @ .., ProjectionElem::Deref] = &place.projection { - let base_ty = Place::ty_from(place.local, proj_base, *self.body, self.tcx).ty; + let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind { place = PlaceRef { local: place.local, projection: proj_base }; } @@ -650,7 +679,7 @@ impl<'tcx> Validator<'_, 'tcx> { while let [proj_base @ .., elem] = place_projection { // FIXME(eddyb) this is probably excessive, with // the exception of `union` member accesses. - let ty = Place::ty_from(place.local, proj_base, *self.body, self.tcx) + let ty = Place::ty_from(place.local, proj_base, self.body, self.tcx) .projection_ty(self.tcx, elem) .ty; if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) { @@ -683,7 +712,7 @@ impl<'tcx> Validator<'_, 'tcx> { callee: &Operand<'tcx>, args: &[Operand<'tcx>], ) -> Result<(), Unpromotable> { - let fn_ty = callee.ty(*self.body, self.tcx); + let fn_ty = callee.ty(self.body, self.tcx); if !self.explicit && self.const_kind.is_none() { if let ty::FnDef(def_id, _) = fn_ty.kind { @@ -718,13 +747,11 @@ impl<'tcx> Validator<'_, 'tcx> { // FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`. pub fn validate_candidates( - tcx: TyCtxt<'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, - def_id: DefId, + ccx: &ConstCx<'_, '_>, temps: &IndexVec, candidates: &[Candidate], ) -> Vec { - let mut validator = Validator { item: Item::new(tcx, def_id, body), temps, explicit: false }; + let mut validator = Validator { ccx, temps, explicit: false }; candidates .iter() @@ -736,11 +763,25 @@ pub fn validate_candidates( // and `#[rustc_args_required_const]` arguments here. let is_promotable = validator.validate_candidate(candidate).is_ok(); + + // If we use explicit validation, we carry the risk of turning a legitimate run-time + // operation into a failing compile-time operation. Make sure that does not happen + // by asserting that there is no possible run-time behavior here in case promotion + // fails. + if validator.explicit && !is_promotable { + ccx.tcx.sess.delay_span_bug( + ccx.body.span, + "Explicit promotion requested, but failed to promote", + ); + } + match candidate { - Candidate::Argument { bb, index } if !is_promotable => { - let span = body[bb].terminator().source_info.span; + Candidate::Argument { bb, index } | Candidate::InlineAsm { bb, index } + if !is_promotable => + { + let span = ccx.body[bb].terminator().source_info.span; let msg = format!("argument {} is required to be a constant", index + 1); - tcx.sess.span_err(span, &msg); + ccx.tcx.sess.span_err(span, &msg); } _ => (), } @@ -752,8 +793,8 @@ pub fn validate_candidates( struct Promoter<'a, 'tcx> { tcx: TyCtxt<'tcx>, - source: &'a mut BodyAndCache<'tcx>, - promoted: BodyAndCache<'tcx>, + source: &'a mut Body<'tcx>, + promoted: Body<'tcx>, temps: &'a mut IndexVec, extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>, @@ -768,7 +809,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { self.promoted.basic_blocks_mut().push(BasicBlockData { statements: vec![], terminator: Some(Terminator { - source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }, + source_info: SourceInfo::outermost(span), kind: TerminatorKind::Return, }), is_cleanup: false, @@ -779,7 +820,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let last = self.promoted.basic_blocks().last().unwrap(); let data = &mut self.promoted[last]; data.statements.push(Statement { - source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }, + source_info: SourceInfo::outermost(span), kind: StatementKind::Assign(box (Place::from(dest), rvalue)), }); } @@ -808,7 +849,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { } let num_stmts = self.source[loc.block].statements.len(); - let new_temp = self.promoted.local_decls.push(LocalDecl::new_temp( + let new_temp = self.promoted.local_decls.push(LocalDecl::new( self.source.local_decls[temp].ty, self.source.local_decls[temp].source_info.span, )); @@ -835,7 +876,11 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { if self.keep_original { rhs.clone() } else { - let unit = Rvalue::Aggregate(box AggregateKind::Tuple, vec![]); + let unit = Rvalue::Use(Operand::Constant(box Constant { + span: statement.source_info.span, + user_ty: None, + literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit), + })); mem::replace(rhs, unit) }, statement.source_info, @@ -897,14 +942,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { def_id: DefId, candidate: Candidate, next_promoted_id: usize, - ) -> Option> { + ) -> Option> { let mut rvalue = { 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_return_place(ty, span); + promoted.local_decls[RETURN_PLACE] = LocalDecl::new(ty, span); Operand::Constant(Box::new(Constant { span, @@ -952,7 +997,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { // Create a temp to hold the promoted reference. // This is because `*r` requires `r` to be a local, // otherwise we would use the `promoted` directly. - let mut promoted_ref = LocalDecl::new_temp(ref_ty, span); + let mut promoted_ref = LocalDecl::new(ref_ty, span); promoted_ref.source_info = statement.source_info; let promoted_ref = local_decls.push(promoted_ref); assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref); @@ -1010,6 +1055,24 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { _ => bug!(), } } + Candidate::InlineAsm { bb, index } => { + let terminator = blocks[bb].terminator_mut(); + match terminator.kind { + TerminatorKind::InlineAsm { ref mut operands, .. } => { + match &mut operands[index] { + InlineAsmOperand::Const { ref mut value } => { + let ty = value.ty(local_decls, self.tcx); + let span = terminator.source_info.span; + + Rvalue::Use(mem::replace(value, promoted_operand(ty, span))) + } + _ => bug!(), + } + } + + _ => bug!(), + } + } } }; @@ -1036,24 +1099,15 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { *local = self.promote_temp(*local); } } - - fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option> { - match elem { - PlaceElem::Index(local) if self.is_temp_kind(*local) => { - Some(PlaceElem::Index(self.promote_temp(*local))) - } - _ => None, - } - } } pub fn promote_candidates<'tcx>( def_id: DefId, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, mut temps: IndexVec, candidates: Vec, -) -> IndexVec> { +) -> IndexVec> { // Visit candidates in reverse, in case they're nested. debug!("promote_candidates({:?})", candidates); @@ -1064,24 +1118,22 @@ pub fn promote_candidates<'tcx>( match candidate { Candidate::Repeat(Location { block, statement_index }) | Candidate::Ref(Location { block, statement_index }) => { - match &body[block].statements[statement_index].kind { - StatementKind::Assign(box (place, _)) => { - if let Some(local) = place.as_local() { - if temps[local] == TempState::PromotedOut { - // Already promoted. - continue; - } + if let StatementKind::Assign(box (place, _)) = + &body[block].statements[statement_index].kind + { + if let Some(local) = place.as_local() { + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; } } - _ => {} } } - Candidate::Argument { .. } => {} + Candidate::Argument { .. } | Candidate::InlineAsm { .. } => {} } // Declare return place local so that `mir::Body::new` doesn't complain. - let initial_locals = - iter::once(LocalDecl::new_return_place(tcx.types.never, body.span)).collect(); + let initial_locals = iter::once(LocalDecl::new(tcx.types.never, body.span)).collect(); let mut promoted = Body::new( IndexVec::new(), @@ -1099,7 +1151,7 @@ pub fn promote_candidates<'tcx>( promoted.ignore_interior_mut_in_const_validation = true; let promoter = Promoter { - promoted: BodyAndCache::new(promoted), + promoted, tcx, source: body, temps: &mut temps, @@ -1137,15 +1189,12 @@ pub fn promote_candidates<'tcx>( _ => true, }); let terminator = block.terminator_mut(); - match &terminator.kind { - TerminatorKind::Drop { location: place, target, .. } => { - if let Some(index) = place.as_local() { - if promoted(index) { - terminator.kind = TerminatorKind::Goto { target: *target }; - } + if let TerminatorKind::Drop { location: place, target, .. } = &terminator.kind { + if let Some(index) = place.as_local() { + if promoted(index) { + terminator.kind = TerminatorKind::Goto { target: *target }; } } - _ => {} } } @@ -1157,22 +1206,19 @@ pub fn promote_candidates<'tcx>( /// Feature attribute should be suggested if `operand` can be promoted and the feature is not /// enabled. crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>( - tcx: TyCtxt<'tcx>, - mir_def_id: DefId, - body: ReadOnlyBodyAndCache<'_, 'tcx>, + ccx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>, ) -> bool { - let mut rpo = traversal::reverse_postorder(&body); - let (temps, _) = collect_temps_and_candidates(tcx, &body, &mut rpo); - let validator = - Validator { item: Item::new(tcx, mir_def_id, body), temps: &temps, explicit: false }; + let mut rpo = traversal::reverse_postorder(&ccx.body); + let (temps, _) = collect_temps_and_candidates(&ccx, &mut rpo); + let validator = Validator { ccx, temps: &temps, explicit: false }; let should_promote = validator.validate_operand(operand).is_ok(); - let feature_flag = tcx.features().const_in_array_repeat_expressions; + let feature_flag = validator.ccx.tcx.features().const_in_array_repeat_expressions; debug!( - "should_suggest_const_in_array_repeat_expressions_flag: mir_def_id={:?} \ + "should_suggest_const_in_array_repeat_expressions_flag: def_id={:?} \ should_promote={:?} feature_flag={:?}", - mir_def_id, should_promote, feature_flag + validator.ccx.def_id, should_promote, feature_flag ); should_promote && !feature_flag } diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index 5a99ee27301ad..0750284889391 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -1,8 +1,9 @@ -use rustc::mir::*; -use rustc::ty::{self, adjustment::PointerCast, Predicate, Ty, TyCtxt}; use rustc_attr as attr; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::mir::*; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, adjustment::PointerCast, Predicate, Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use std::borrow::Cow; @@ -12,7 +13,7 @@ type McfResult = Result<(), (Span, Cow<'static, str>)>; pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult { // Prevent const trait methods from being annotated as `stable`. if tcx.features().staged_api { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) { return Err((body.span, "trait methods cannot be stable const fn".into())); } @@ -27,7 +28,8 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - | Predicate::TypeOutlives(_) | Predicate::WellFormed(_) | Predicate::Projection(_) - | Predicate::ConstEvaluatable(..) => continue, + | Predicate::ConstEvaluatable(..) + | Predicate::ConstEquate(..) => continue, Predicate::ObjectSafe(_) => { bug!("object safe predicate on function: {:#?}", predicate) } @@ -92,7 +94,15 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - } fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult { - for ty in ty.walk() { + for arg in ty.walk() { + let ty = match arg.unpack() { + GenericArgKind::Type(ty) => ty, + + // No constraints on lifetimes or constants, except potentially + // constants' types, but `walk` will get to them as well. + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, + }; + match ty.kind { ty::Ref(_, _, hir::Mutability::Mut) => { if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) { @@ -150,27 +160,32 @@ fn check_rvalue( Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) - | Rvalue::AddressOf(_, place) => check_place(tcx, place, span, def_id, body), + | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, def_id, body), Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { - use rustc::ty::cast::CastTy; + use rustc_middle::ty::cast::CastTy; let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); match (cast_in, cast_out) { - (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { + (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { Err((span, "casting pointers to ints is unstable in const fn".into())) } _ => check_operand(tcx, operand, span, def_id, body), } } - Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) - | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, _) => { - check_operand(tcx, operand, span, def_id, body) - } - Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) - | Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) - | Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => { - Err((span, "function pointer casts are not allowed in const fn".into())) - } + Rvalue::Cast( + CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), + operand, + _, + ) => check_operand(tcx, operand, span, def_id, body), + Rvalue::Cast( + CastKind::Pointer( + PointerCast::UnsafeFnPointer + | PointerCast::ClosureFnPointer(_) + | PointerCast::ReifyFnPointer, + ), + _, + _, + ) => Err((span, "function pointer casts are not allowed in const fn".into())), Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => { Err((span, "unsizing casts are not allowed in const fn".into())) } @@ -215,7 +230,7 @@ fn check_statement( let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, place, span, def_id, body)?; + check_place(tcx, *place, span, def_id, body)?; check_rvalue(tcx, body, def_id, rval, span) } @@ -225,12 +240,14 @@ fn check_statement( Err((span, "loops and conditional expressions are not stable in const fn".into())) } - StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body), + StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, def_id, body), // just an assignment - StatementKind::SetDiscriminant { place, .. } => check_place(tcx, place, span, def_id, body), + StatementKind::SetDiscriminant { place, .. } => { + check_place(tcx, **place, span, def_id, body) + } - StatementKind::InlineAsm { .. } => { + StatementKind::LlvmInlineAsm { .. } => { Err((span, "cannot use inline assembly in const fn".into())) } @@ -251,7 +268,7 @@ fn check_operand( body: &Body<'tcx>, ) -> McfResult { match operand { - Operand::Move(place) | Operand::Copy(place) => check_place(tcx, place, span, def_id, body), + Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, def_id, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { Some(_) => Err((span, "cannot access `static` items in const fn".into())), None => Ok(()), @@ -261,7 +278,7 @@ fn check_operand( fn check_place( tcx: TyCtxt<'tcx>, - place: &Place<'tcx>, + place: Place<'tcx>, span: Span, def_id: DefId, body: &Body<'tcx>, @@ -270,11 +287,6 @@ fn check_place( while let &[ref proj_base @ .., elem] = cursor { cursor = proj_base; match elem { - ProjectionElem::Downcast(..) if !feature_allowed(tcx, def_id, sym::const_if_match) => { - return Err((span, "`match` or `if let` in `const fn` is unstable".into())); - } - ProjectionElem::Downcast(_symbol, _variant_index) => {} - ProjectionElem::Field(..) => { let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty; if let Some(def) = base_ty.ty_adt_def() { @@ -287,6 +299,7 @@ fn check_place( } } ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref | ProjectionElem::Index(_) => {} @@ -328,11 +341,12 @@ fn check_terminator( | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } | TerminatorKind::Return - | TerminatorKind::Resume => Ok(()), + | TerminatorKind::Resume + | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { location, .. } => check_place(tcx, location, span, def_id, body), + TerminatorKind::Drop { location, .. } => check_place(tcx, *location, span, def_id, body), TerminatorKind::DropAndReplace { location, value, .. } => { - check_place(tcx, location, span, def_id, body)?; + check_place(tcx, *location, span, def_id, body)?; check_operand(tcx, value, span, def_id, body) } @@ -344,12 +358,7 @@ fn check_terminator( check_operand(tcx, discr, span, def_id, body) } - // FIXME(ecstaticmorse): We probably want to allow `Unreachable` unconditionally. - TerminatorKind::Unreachable if feature_allowed(tcx, def_id, sym::const_if_match) => Ok(()), - - TerminatorKind::Abort | TerminatorKind::Unreachable => { - Err((span, "const fn with unreachable code is not stable".into())) - } + TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn generators are unstable".into())) } @@ -383,5 +392,9 @@ fn check_terminator( TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => { check_operand(tcx, cond, span, def_id, body) } + + TerminatorKind::InlineAsm { .. } => { + Err((span, "cannot use inline assembly in const fn".into())) + } } } diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index 7ac9eccf46f56..69c0163b649d6 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -1,16 +1,17 @@ use crate::transform::{MirPass, MirSource}; use crate::util::patch::MirPatch; -use rustc::mir::*; -use rustc::ty::TyCtxt; use rustc_index::bit_set::BitSet; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_target::spec::PanicStrategy; /// A pass that removes noop landing pads and replaces jumps to them with /// `None`. This is important because otherwise LLVM generates terrible /// code for these. pub struct RemoveNoopLandingPads; -pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyAndCache<'tcx>) { - if tcx.sess.no_landing_pads() { +pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.panic_strategy() == PanicStrategy::Abort { return; } debug!("remove_noop_landing_pads({:?})", body); @@ -19,7 +20,7 @@ pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyAndCache } impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { remove_noop_landing_pads(tcx, body); } } @@ -52,7 +53,7 @@ impl RemoveNoopLandingPads { StatementKind::Assign { .. } | StatementKind::SetDiscriminant { .. } - | StatementKind::InlineAsm { .. } + | StatementKind::LlvmInlineAsm { .. } | StatementKind::Retag { .. } => { return false; } @@ -76,11 +77,12 @@ impl RemoveNoopLandingPads { | TerminatorKind::Call { .. } | TerminatorKind::Assert { .. } | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::Drop { .. } => false, + | TerminatorKind::Drop { .. } + | TerminatorKind::InlineAsm { .. } => false, } } - fn remove_nop_landing_pads(&self, body: &mut BodyAndCache<'_>) { + fn remove_nop_landing_pads(&self, body: &mut Body<'_>) { // make sure there's a single resume block let resume_block = { let patch = MirPatch::new(body); @@ -107,16 +109,13 @@ impl RemoveNoopLandingPads { } } - match body[bb].terminator_mut().unwind_mut() { - Some(unwind) => { - if *unwind == Some(resume_block) { - debug!(" removing noop landing pad"); - jumps_folded -= 1; - landing_pads_removed += 1; - *unwind = None; - } + if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { + if *unwind == Some(resume_block) { + debug!(" removing noop landing pad"); + jumps_folded -= 1; + landing_pads_removed += 1; + *unwind = None; } - _ => {} } let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); diff --git a/src/librustc_mir/transform/required_consts.rs b/src/librustc_mir/transform/required_consts.rs new file mode 100644 index 0000000000000..a63ab30a68fa2 --- /dev/null +++ b/src/librustc_mir/transform/required_consts.rs @@ -0,0 +1,23 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Constant, Location}; +use rustc_middle::ty::ConstKind; + +pub struct RequiredConstsVisitor<'a, 'tcx> { + required_consts: &'a mut Vec>, +} + +impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { + pub fn new(required_consts: &'a mut Vec>) -> Self { + RequiredConstsVisitor { required_consts } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> { + fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) { + let const_kind = constant.literal.val; + + if let ConstKind::Unevaluated(_, _, _) = const_kind { + self.required_consts.push(*constant); + } + } +} diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 22ac3410a75ab..5eb374e7ee2f1 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -4,24 +4,24 @@ use rustc_span::Span; use rustc_target::spec::abi::Abi; use crate::transform::{MirPass, MirSource}; -use rustc::mir::{self, Body, BodyAndCache, Local, Location}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; +use rustc_middle::mir::{self, Body, Local, Location}; +use rustc_middle::ty::{self, Ty, TyCtxt}; -use crate::dataflow::generic::{Analysis, Results, ResultsCursor}; +use crate::dataflow::impls::{ + DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeMutBorrowedLocals, + MaybeUninitializedPlaces, +}; use crate::dataflow::move_paths::{HasMoveData, MoveData}; use crate::dataflow::move_paths::{LookupResult, MovePathIndex}; -use crate::dataflow::MaybeMutBorrowedLocals; use crate::dataflow::MoveDataParamEnv; -use crate::dataflow::{ - DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, -}; +use crate::dataflow::{Analysis, Results, ResultsCursor}; pub struct SanityCheck; impl<'tcx> MirPass<'tcx> for SanityCheck { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { use crate::dataflow::has_rustc_mir_with; let def_id = src.def_id(); if !tcx.has_attr(def_id, sym::rustc_mir) { @@ -36,31 +36,45 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); let mdpe = MoveDataParamEnv { move_data, param_env }; - let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) - .into_engine(tcx, body, def_id) - .iterate_to_fixpoint(); - let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe) - .into_engine(tcx, body, def_id) - .iterate_to_fixpoint(); - let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe) - .into_engine(tcx, body, def_id) - .iterate_to_fixpoint(); - let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env) - .into_engine(tcx, body, def_id) - .iterate_to_fixpoint(); - if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() { + let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) + .into_engine(tcx, body, def_id) + .iterate_to_fixpoint(); + sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits); } + if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() { + let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe) + .into_engine(tcx, body, def_id) + .iterate_to_fixpoint(); + sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits); } + if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() { + let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe) + .into_engine(tcx, body, def_id) + .iterate_to_fixpoint(); + sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits); } + if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() { + let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env) + .into_engine(tcx, body, def_id) + .iterate_to_fixpoint(); + sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed); } + + if has_rustc_mir_with(&attributes, sym::rustc_peek_liveness).is_some() { + let flow_liveness = + MaybeLiveLocals.into_engine(tcx, body, def_id).iterate_to_fixpoint(); + + sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_liveness); + } + if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() { tcx.sess.fatal("stop_after_dataflow ended compilation"); } @@ -113,8 +127,7 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>( .statements .iter() .enumerate() - .filter_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval))) - .next() + .find_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval))) .expect( "call to rustc_peek should be preceded by \ assignment to temporary holding its argument", @@ -122,12 +135,14 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>( match (call.kind, peek_rval) { (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place)) - | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Move(place))) - | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Copy(place))) => { + | ( + PeekCallKind::ByVal, + mir::Rvalue::Use(mir::Operand::Move(place) | mir::Operand::Copy(place)), + ) => { let loc = Location { block: bb, statement_index }; - cursor.seek_before(loc); + cursor.seek_before_primary_effect(loc); let state = cursor.get(); - results.analysis.peek_at(tcx, place, state, call); + results.analysis.peek_at(tcx, *place, state, call); } _ => { @@ -231,7 +246,7 @@ pub trait RustcPeekAt<'tcx>: Analysis<'tcx> { fn peek_at( &self, tcx: TyCtxt<'tcx>, - place: &mir::Place<'tcx>, + place: mir::Place<'tcx>, flow_state: &BitSet, call: PeekCall, ); @@ -244,7 +259,7 @@ where fn peek_at( &self, tcx: TyCtxt<'tcx>, - place: &mir::Place<'tcx>, + place: mir::Place<'tcx>, flow_state: &BitSet, call: PeekCall, ) { @@ -268,7 +283,29 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeMutBorrowedLocals<'_, 'tcx> { fn peek_at( &self, tcx: TyCtxt<'tcx>, - place: &mir::Place<'tcx>, + place: mir::Place<'tcx>, + flow_state: &BitSet, + call: PeekCall, + ) { + warn!("peek_at: place={:?}", place); + let local = if let Some(l) = place.as_local() { + l + } else { + tcx.sess.span_err(call.span, "rustc_peek: argument was not a local"); + return; + }; + + if !flow_state.contains(local) { + tcx.sess.span_err(call.span, "rustc_peek: bit not set"); + } + } +} + +impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals { + fn peek_at( + &self, + tcx: TyCtxt<'tcx>, + place: mir::Place<'tcx>, flow_state: &BitSet, call: PeekCall, ) { diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 4c54a46642f11..491c37cbe06e8 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -28,11 +28,11 @@ //! return. use crate::transform::{MirPass, MirSource}; -use rustc::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; -use rustc::mir::*; -use rustc::ty::{self, TyCtxt}; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; use std::borrow::Cow; pub struct SimplifyCfg { @@ -45,7 +45,7 @@ impl SimplifyCfg { } } -pub fn simplify_cfg(body: &mut BodyAndCache<'_>) { +pub fn simplify_cfg(body: &mut Body<'_>) { CfgSimplifier::new(body).simplify(); remove_dead_blocks(body); @@ -58,7 +58,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { Cow::Borrowed(&self.label) } - fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body); simplify_cfg(body); } @@ -70,7 +70,7 @@ pub struct CfgSimplifier<'a, 'tcx> { } impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { - pub fn new(body: &'a mut BodyAndCache<'tcx>) -> Self { + pub fn new(body: &'a mut Body<'tcx>) -> Self { let mut pred_count = IndexVec::from_elem(0u32, body.basic_blocks()); // we can't use mir.predecessors() here because that counts @@ -272,7 +272,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { } } -pub fn remove_dead_blocks(body: &mut BodyAndCache<'_>) { +pub fn remove_dead_blocks(body: &mut Body<'_>) { let mut seen = BitSet::new_empty(body.basic_blocks().len()); for (bb, _) in traversal::preorder(body) { seen.insert(bb.index()); @@ -304,51 +304,83 @@ pub fn remove_dead_blocks(body: &mut BodyAndCache<'_>) { pub struct SimplifyLocals; impl<'tcx> MirPass<'tcx> for SimplifyLocals { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { trace!("running SimplifyLocals on {:?}", source); - let locals = { - let read_only_cache = read_only!(body); - let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()), body }; - marker.visit_body(read_only_cache); - // Return pointer and arguments are always live - marker.locals.insert(RETURN_PLACE); - for arg in body.args_iter() { - marker.locals.insert(arg); - } - marker.locals + // First, we're going to get a count of *actual* uses for every `Local`. + // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored. + let mut used_locals = { + let mut marker = DeclMarker::new(body); + marker.visit_body(&body); + + marker.local_counts }; - let map = make_local_map(&mut body.local_decls, locals); - // Update references to all vars and tmps now - LocalUpdater { map, tcx }.visit_body(body); - body.local_decls.shrink_to_fit(); + let arg_count = body.arg_count; + + // Next, we're going to remove any `Local` with zero actual uses. When we remove those + // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals` + // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from + // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a + // fixedpoint where there are no more unused locals. + loop { + let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx); + remove_statements.visit_body(body); + + if !remove_statements.modified { + break; + } + } + + // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s. + let map = make_local_map(&mut body.local_decls, used_locals, arg_count); + + // Only bother running the `LocalUpdater` if we actually found locals to remove. + if map.iter().any(Option::is_none) { + // Update references to all vars and tmps now + let mut updater = LocalUpdater { map, tcx }; + updater.visit_body(body); + + body.local_decls.shrink_to_fit(); + } } } /// Construct the mapping while swapping out unused stuff out from the `vec`. fn make_local_map( - vec: &mut IndexVec, - mask: BitSet, + local_decls: &mut IndexVec, + used_locals: IndexVec, + arg_count: usize, ) -> IndexVec> { - let mut map: IndexVec> = IndexVec::from_elem(None, &*vec); + let mut map: IndexVec> = IndexVec::from_elem(None, &*local_decls); let mut used = Local::new(0); - for alive_index in mask.iter() { + for (alive_index, count) in used_locals.iter_enumerated() { + // The `RETURN_PLACE` and arguments are always live. + if alive_index.as_usize() > arg_count && *count == 0 { + continue; + } + map[alive_index] = Some(used); if alive_index != used { - vec.swap(alive_index, used); + local_decls.swap(alive_index, used); } used.increment_by(1); } - vec.truncate(used.index()); + local_decls.truncate(used.index()); map } struct DeclMarker<'a, 'tcx> { - pub locals: BitSet, + pub local_counts: IndexVec, pub body: &'a Body<'tcx>, } +impl<'a, 'tcx> DeclMarker<'a, 'tcx> { + pub fn new(body: &'a Body<'tcx>) -> Self { + Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body } + } +} + impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) { // Ignore storage markers altogether, they get removed along with their otherwise unused @@ -368,56 +400,133 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { if location.statement_index != block.statements.len() { let stmt = &block.statements[location.statement_index]; - if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(c)))) = - &stmt.kind - { - match c.literal.val { - // Keep assignments from unevaluated constants around, since the evaluation - // may report errors, even if the use of the constant is dead code. - ty::ConstKind::Unevaluated(..) => {} - _ => { - if !p.is_indirect() { - trace!("skipping store of const value {:?} to {:?}", c, p); - return; - } + if let StatementKind::Assign(box (dest, rvalue)) = &stmt.kind { + if !dest.is_indirect() && dest.local == *local { + let can_skip = match rvalue { + Rvalue::Use(_) + | Rvalue::Discriminant(_) + | Rvalue::BinaryOp(_, _, _) + | Rvalue::CheckedBinaryOp(_, _, _) + | Rvalue::Repeat(_, _) + | Rvalue::AddressOf(_, _) + | Rvalue::Len(_) + | Rvalue::UnaryOp(_, _) + | Rvalue::Aggregate(_, _) => true, + + _ => false, + }; + + if can_skip { + trace!("skipping store of {:?} to {:?}", rvalue, dest); + return; } } } } } - self.locals.insert(*local); + self.local_counts[*local] += 1; } } -struct LocalUpdater<'tcx> { - map: IndexVec>, +struct StatementDeclMarker<'a, 'tcx> { + used_locals: &'a mut IndexVec, + statement: &'a Statement<'tcx>, +} + +impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> { + pub fn new( + used_locals: &'a mut IndexVec, + statement: &'a Statement<'tcx>, + ) -> Self { + Self { used_locals, statement } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> { + fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) { + // Skip the lvalue for assignments + if let StatementKind::Assign(box (p, _)) = self.statement.kind { + if p.local == *local && context.is_place_assignment() { + return; + } + } + + let use_count = &mut self.used_locals[*local]; + // If this is the local we're removing... + if *use_count != 0 { + *use_count -= 1; + } + } +} + +struct RemoveStatements<'a, 'tcx> { + used_locals: &'a mut IndexVec, + arg_count: usize, tcx: TyCtxt<'tcx>, + modified: bool, } -impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { +impl<'a, 'tcx> RemoveStatements<'a, 'tcx> { + fn new( + used_locals: &'a mut IndexVec, + arg_count: usize, + tcx: TyCtxt<'tcx>, + ) -> Self { + Self { used_locals, arg_count, tcx, modified: false } + } + + fn keep_local(&self, l: Local) -> bool { + trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]); + l.as_usize() <= self.arg_count || self.used_locals[l] != 0 + } +} + +impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { // Remove unnecessary StorageLive and StorageDead annotations. - data.statements.retain(|stmt| match &stmt.kind { - StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => self.map[*l].is_some(), - StatementKind::Assign(box (place, _)) => self.map[place.local].is_some(), - _ => true, + let mut i = 0usize; + data.statements.retain(|stmt| { + let keep = match &stmt.kind { + StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { + self.keep_local(*l) + } + StatementKind::Assign(box (place, _)) => self.keep_local(place.local), + _ => true, + }; + + if !keep { + trace!("removing statement {:?}", stmt); + self.modified = true; + + let mut visitor = StatementDeclMarker::new(self.used_locals, stmt); + visitor.visit_statement(stmt, Location { block, statement_index: i }); + } + + i += 1; + + keep }); + self.super_basic_block_data(block, data); } +} - fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { - *l = self.map[*l].unwrap(); +struct LocalUpdater<'tcx> { + map: IndexVec>, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx } - fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option> { - match elem { - PlaceElem::Index(local) => Some(PlaceElem::Index(self.map[*local].unwrap())), - _ => None, - } + fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { + *l = self.map[*l].unwrap(); } } diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index c233d0b516e2e..38e7f9d8ae45b 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -1,8 +1,8 @@ //! A pass that simplifies branches when their condition is known. use crate::transform::{MirPass, MirSource}; -use rustc::mir::*; -use rustc::ty::TyCtxt; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; use std::borrow::Cow; @@ -21,7 +21,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches { Cow::Borrowed(&self.label) } - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env(src.def_id()); for block in body.basic_blocks_mut() { let terminator = block.terminator_mut(); diff --git a/src/librustc_mir/transform/simplify_try.rs b/src/librustc_mir/transform/simplify_try.rs index 3f28f033047a2..41ffa6594418f 100644 --- a/src/librustc_mir/transform/simplify_try.rs +++ b/src/librustc_mir/transform/simplify_try.rs @@ -11,9 +11,12 @@ use crate::transform::{simplify, MirPass, MirSource}; use itertools::Itertools as _; -use rustc::mir::*; -use rustc::ty::{Ty, TyCtxt}; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::*; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::VariantIdx; +use std::iter::{Enumerate, Peekable}; +use std::slice::Iter; /// Simplifies arms of form `Variant(x) => Variant(x)` to just a move. /// @@ -21,7 +24,8 @@ use rustc_target::abi::VariantIdx; /// /// ```rust /// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); -/// ((_LOCAL_0 as Variant).FIELD: TY) = move _LOCAL_TMP; +/// _TMP_2 = _LOCAL_TMP; +/// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2; /// discriminant(_LOCAL_0) = VAR_IDX; /// ``` /// @@ -32,50 +36,320 @@ use rustc_target::abi::VariantIdx; /// ``` pub struct SimplifyArmIdentity; +#[derive(Debug)] +struct ArmIdentityInfo<'tcx> { + /// Storage location for the variant's field + local_temp_0: Local, + /// Storage location holding the variant being read from + local_1: Local, + /// The variant field being read from + vf_s0: VarField<'tcx>, + /// Index of the statement which loads the variant being read + get_variant_field_stmt: usize, + + /// Tracks each assignment to a temporary of the variant's field + field_tmp_assignments: Vec<(Local, Local)>, + + /// Storage location holding the variant's field that was read from + local_tmp_s1: Local, + /// Storage location holding the enum that we are writing to + local_0: Local, + /// The variant field being written to + vf_s1: VarField<'tcx>, + + /// Storage location that the discriminant is being written to + set_discr_local: Local, + /// The variant being written + set_discr_var_idx: VariantIdx, + + /// Index of the statement that should be overwritten as a move + stmt_to_overwrite: usize, + /// SourceInfo for the new move + source_info: SourceInfo, + + /// Indices of matching Storage{Live,Dead} statements encountered. + /// (StorageLive index,, StorageDead index, Local) + storage_stmts: Vec<(usize, usize, Local)>, + + /// The statements that should be removed (turned into nops) + stmts_to_remove: Vec, +} + +fn get_arm_identity_info<'a, 'tcx>(stmts: &'a [Statement<'tcx>]) -> Option> { + // This can't possibly match unless there are at least 3 statements in the block + // so fail fast on tiny blocks. + if stmts.len() < 3 { + return None; + } + + let mut tmp_assigns = Vec::new(); + let mut nop_stmts = Vec::new(); + let mut storage_stmts = Vec::new(); + let mut storage_live_stmts = Vec::new(); + let mut storage_dead_stmts = Vec::new(); + + type StmtIter<'a, 'tcx> = Peekable>>>; + + fn is_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool { + matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_)) + } + + /// Eats consecutive Statements which match `test`, performing the specified `action` for each. + /// The iterator `stmt_iter` is not advanced if none were matched. + fn try_eat<'a, 'tcx>( + stmt_iter: &mut StmtIter<'a, 'tcx>, + test: impl Fn(&'a Statement<'tcx>) -> bool, + mut action: impl FnMut(usize, &'a Statement<'tcx>) -> (), + ) { + while stmt_iter.peek().map(|(_, stmt)| test(stmt)).unwrap_or(false) { + let (idx, stmt) = stmt_iter.next().unwrap(); + + action(idx, stmt); + } + } + + /// Eats consecutive `StorageLive` and `StorageDead` Statements. + /// The iterator `stmt_iter` is not advanced if none were found. + fn try_eat_storage_stmts<'a, 'tcx>( + stmt_iter: &mut StmtIter<'a, 'tcx>, + storage_live_stmts: &mut Vec<(usize, Local)>, + storage_dead_stmts: &mut Vec<(usize, Local)>, + ) { + try_eat(stmt_iter, is_storage_stmt, |idx, stmt| { + if let StatementKind::StorageLive(l) = stmt.kind { + storage_live_stmts.push((idx, l)); + } else if let StatementKind::StorageDead(l) = stmt.kind { + storage_dead_stmts.push((idx, l)); + } + }) + } + + fn is_tmp_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool { + use rustc_middle::mir::StatementKind::Assign; + if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind { + place.as_local().is_some() && p.as_local().is_some() + } else { + false + } + } + + /// Eats consecutive `Assign` Statements. + // The iterator `stmt_iter` is not advanced if none were found. + fn try_eat_assign_tmp_stmts<'a, 'tcx>( + stmt_iter: &mut StmtIter<'a, 'tcx>, + tmp_assigns: &mut Vec<(Local, Local)>, + nop_stmts: &mut Vec, + ) { + try_eat(stmt_iter, is_tmp_storage_stmt, |idx, stmt| { + use rustc_middle::mir::StatementKind::Assign; + if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = + &stmt.kind + { + tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap())); + nop_stmts.push(idx); + } + }) + } + + fn find_storage_live_dead_stmts_for_local<'tcx>( + local: Local, + stmts: &[Statement<'tcx>], + ) -> Option<(usize, usize)> { + trace!("looking for {:?}", local); + let mut storage_live_stmt = None; + let mut storage_dead_stmt = None; + for (idx, stmt) in stmts.iter().enumerate() { + if stmt.kind == StatementKind::StorageLive(local) { + storage_live_stmt = Some(idx); + } else if stmt.kind == StatementKind::StorageDead(local) { + storage_dead_stmt = Some(idx); + } + } + + Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX))) + } + + // Try to match the expected MIR structure with the basic block we're processing. + // We want to see something that looks like: + // ``` + // (StorageLive(_) | StorageDead(_));* + // _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); + // (StorageLive(_) | StorageDead(_));* + // (tmp_n+1 = tmp_n);* + // (StorageLive(_) | StorageDead(_));* + // (tmp_n+1 = tmp_n);* + // ((LOCAL_FROM as Variant).FIELD: TY) = move tmp; + // discriminant(LOCAL_FROM) = VariantIdx; + // (StorageLive(_) | StorageDead(_));* + // ``` + let mut stmt_iter = stmts.iter().enumerate().peekable(); + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + let (get_variant_field_stmt, stmt) = stmt_iter.next()?; + let (local_tmp_s0, local_1, vf_s0) = match_get_variant_field(stmt)?; + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); + + let (idx, stmt) = stmt_iter.next()?; + let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?; + nop_stmts.push(idx); + + let (idx, stmt) = stmt_iter.next()?; + let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?; + let discr_stmt_source_info = stmt.source_info; + nop_stmts.push(idx); + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + for (live_idx, live_local) in storage_live_stmts { + if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) { + let (dead_idx, _) = storage_dead_stmts.swap_remove(i); + storage_stmts.push((live_idx, dead_idx, live_local)); + + if live_local == local_tmp_s0 { + nop_stmts.push(get_variant_field_stmt); + } + } + } + + nop_stmts.sort(); + + // Use one of the statements we're going to discard between the point + // where the storage location for the variant field becomes live and + // is killed. + let (live_idx, dead_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?; + let stmt_to_overwrite = + nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < dead_idx); + + Some(ArmIdentityInfo { + local_temp_0: local_tmp_s0, + local_1, + vf_s0, + get_variant_field_stmt, + field_tmp_assignments: tmp_assigns, + local_tmp_s1, + local_0, + vf_s1, + set_discr_local, + set_discr_var_idx, + stmt_to_overwrite: *stmt_to_overwrite?, + source_info: discr_stmt_source_info, + storage_stmts, + stmts_to_remove: nop_stmts, + }) +} + +fn optimization_applies<'tcx>( + opt_info: &ArmIdentityInfo<'tcx>, + local_decls: &IndexVec>, +) -> bool { + trace!("testing if optimization applies..."); + + // FIXME(wesleywiser): possibly relax this restriction? + if opt_info.local_0 == opt_info.local_1 { + trace!("NO: moving into ourselves"); + return false; + } else if opt_info.vf_s0 != opt_info.vf_s1 { + trace!("NO: the field-and-variant information do not match"); + return false; + } else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty { + // FIXME(Centril,oli-obk): possibly relax to same layout? + trace!("NO: source and target locals have different types"); + return false; + } else if (opt_info.local_0, opt_info.vf_s0.var_idx) + != (opt_info.set_discr_local, opt_info.set_discr_var_idx) + { + trace!("NO: the discriminants do not match"); + return false; + } + + // Verify the assigment chain consists of the form b = a; c = b; d = c; etc... + if opt_info.field_tmp_assignments.len() == 0 { + trace!("NO: no assignments found"); + } + let mut last_assigned_to = opt_info.field_tmp_assignments[0].1; + let source_local = last_assigned_to; + for (l, r) in &opt_info.field_tmp_assignments { + if *r != last_assigned_to { + trace!("NO: found unexpected assignment {:?} = {:?}", l, r); + return false; + } + + last_assigned_to = *l; + } + + if source_local != opt_info.local_temp_0 { + trace!( + "NO: start of assignment chain does not match enum variant temp: {:?} != {:?}", + source_local, + opt_info.local_temp_0 + ); + return false; + } else if last_assigned_to != opt_info.local_tmp_s1 { + trace!( + "NO: end of assignemnt chain does not match written enum temp: {:?} != {:?}", + last_assigned_to, + opt_info.local_tmp_s1 + ); + return false; + } + + trace!("SUCCESS: optimization applies!"); + return true; +} + impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { - fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, _: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("running SimplifyArmIdentity on {:?}", source); let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); for bb in basic_blocks { - // Need 3 statements: - let (s0, s1, s2) = match &mut *bb.statements { - [s0, s1, s2] => (s0, s1, s2), - _ => continue, - }; + if let Some(opt_info) = get_arm_identity_info(&bb.statements) { + trace!("got opt_info = {:#?}", opt_info); + if !optimization_applies(&opt_info, local_decls) { + debug!("optimization skipped for {:?}", source); + continue; + } - // Pattern match on the form we want: - let (local_tmp_s0, local_1, vf_s0) = match match_get_variant_field(s0) { - None => continue, - Some(x) => x, - }; - let (local_tmp_s1, local_0, vf_s1) = match match_set_variant_field(s1) { - None => continue, - Some(x) => x, - }; - if local_tmp_s0 != local_tmp_s1 - // Avoid moving into ourselves. - || local_0 == local_1 - // The field-and-variant information match up. - || vf_s0 != vf_s1 - // Source and target locals have the same type. - // FIXME(Centril | oli-obk): possibly relax to same layout? - || local_decls[local_0].ty != local_decls[local_1].ty - // We're setting the discriminant of `local_0` to this variant. - || Some((local_0, vf_s0.var_idx)) != match_set_discr(s2) - { - continue; - } + // Also remove unused Storage{Live,Dead} statements which correspond + // to temps used previously. + for (live_idx, dead_idx, local) in &opt_info.storage_stmts { + // The temporary that we've read the variant field into is scoped to this block, + // so we can remove the assignment. + if *local == opt_info.local_temp_0 { + bb.statements[opt_info.get_variant_field_stmt].make_nop(); + } - // Right shape; transform! - s0.source_info = s2.source_info; - match &mut s0.kind { - StatementKind::Assign(box (place, rvalue)) => { - *place = local_0.into(); - *rvalue = Rvalue::Use(Operand::Move(local_1.into())); + for (left, right) in &opt_info.field_tmp_assignments { + if local == left || local == right { + bb.statements[*live_idx].make_nop(); + bb.statements[*dead_idx].make_nop(); + } + } } - _ => unreachable!(), + + // Right shape; transform + for stmt_idx in opt_info.stmts_to_remove { + bb.statements[stmt_idx].make_nop(); + } + + let stmt = &mut bb.statements[opt_info.stmt_to_overwrite]; + stmt.source_info = opt_info.source_info; + stmt.kind = StatementKind::Assign(box ( + opt_info.local_0.into(), + Rvalue::Use(Operand::Move(opt_info.local_1.into())), + )); + + bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop); + + trace!("block is now {:?}", bb.statements); } - s1.make_nop(); - s2.make_nop(); } } } @@ -87,9 +361,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { fn match_get_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { match &stmt.kind { StatementKind::Assign(box (place_into, rvalue_from)) => match rvalue_from { - Rvalue::Use(Operand::Copy(pf)) | Rvalue::Use(Operand::Move(pf)) => { + Rvalue::Use(Operand::Copy(pf) | Operand::Move(pf)) => { let local_into = place_into.as_local()?; - let (local_from, vf) = match_variant_field_place(&pf)?; + let (local_from, vf) = match_variant_field_place(*pf)?; Some((local_into, local_from, vf)) } _ => None, @@ -107,7 +381,7 @@ fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local StatementKind::Assign(box (place_from, rvalue_into)) => match rvalue_into { Rvalue::Use(Operand::Move(place_into)) => { let local_into = place_into.as_local()?; - let (local_from, vf) = match_variant_field_place(&place_from)?; + let (local_from, vf) = match_variant_field_place(*place_from)?; Some((local_into, local_from, vf)) } _ => None, @@ -129,7 +403,7 @@ fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> } } -#[derive(PartialEq)] +#[derive(PartialEq, Debug)] struct VarField<'tcx> { field: Field, field_ty: Ty<'tcx>, @@ -137,7 +411,7 @@ struct VarField<'tcx> { } /// Match on `((_LOCAL as Variant).FIELD: TY)`. -fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarField<'tcx>)> { +fn match_variant_field_place<'tcx>(place: Place<'tcx>) -> Option<(Local, VarField<'tcx>)> { match place.as_ref() { PlaceRef { local, @@ -153,7 +427,7 @@ fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarFie pub struct SimplifyBranchSame; impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { - fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { let mut did_remove_blocks = false; let bbs = body.basic_blocks_mut(); for bb_idx in bbs.indices() { @@ -172,7 +446,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { // so we cannot assume that the `unreachable` terminator itself is reachable. // FIXME(Centril): use a normalization pass instead of a check. || bb.statements.iter().any(|stmt| match stmt.kind { - StatementKind::InlineAsm(..) => true, + StatementKind::LlvmInlineAsm(..) => true, _ => false, }) }) diff --git a/src/librustc_mir/transform/uninhabited_enum_branching.rs b/src/librustc_mir/transform/uninhabited_enum_branching.rs index bbaa66f5954cb..e3b182b88492f 100644 --- a/src/librustc_mir/transform/uninhabited_enum_branching.rs +++ b/src/librustc_mir/transform/uninhabited_enum_branching.rs @@ -1,12 +1,12 @@ //! A pass that eliminates branches on uninhabited enum variants. use crate::transform::{MirPass, MirSource}; -use rustc::mir::{ - BasicBlock, BasicBlockData, Body, BodyAndCache, Local, Operand, Rvalue, StatementKind, - TerminatorKind, +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, TerminatorKind, }; -use rustc::ty::layout::{Abi, TyLayout, Variants}; -use rustc::ty::{Ty, TyCtxt}; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_target::abi::{Abi, Variants}; pub struct UninhabitedEnumBranching; @@ -49,11 +49,11 @@ fn get_switched_on_type<'tcx>( } fn variant_discriminants<'tcx>( - layout: &TyLayout<'tcx>, + layout: &TyAndLayout<'tcx>, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, ) -> Vec { - match &layout.details.variants { + match &layout.variants { Variants::Single { index } => vec![index.as_u32() as u128], Variants::Multiple { variants, .. } => variants .iter_enumerated() @@ -66,7 +66,7 @@ fn variant_discriminants<'tcx>( } impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { if source.promoted.is_some() { return; } diff --git a/src/librustc_mir/transform/unreachable_prop.rs b/src/librustc_mir/transform/unreachable_prop.rs index 27173e0c171b7..d9f2259030ff5 100644 --- a/src/librustc_mir/transform/unreachable_prop.rs +++ b/src/librustc_mir/transform/unreachable_prop.rs @@ -4,15 +4,15 @@ use crate::transform::simplify; use crate::transform::{MirPass, MirSource}; -use rustc::mir::*; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; use std::borrow::Cow; pub struct UnreachablePropagation; impl MirPass<'_> for UnreachablePropagation { - fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.mir_opt_level < 3 { // Enable only under -Zmir-opt-level=3 as in some cases (check the deeply-nested-opt // perf benchmark) LLVM may spend quite a lot of time optimizing the generated code. @@ -29,7 +29,7 @@ impl MirPass<'_> for UnreachablePropagation { // Accompanying testcases: mir-opt/unreachable_asm.rs and mir-opt/unreachable_asm_2.rs let asm_stmt_in_block = || { bb_data.statements.iter().any(|stmt: &Statement<'_>| match stmt.kind { - StatementKind::InlineAsm(..) => true, + StatementKind::LlvmInlineAsm(..) => true, _ => false, }) }; diff --git a/src/librustc_mir/util/aggregate.rs b/src/librustc_mir/util/aggregate.rs index 927c8f6dfb29b..1a22eee3a0371 100644 --- a/src/librustc_mir/util/aggregate.rs +++ b/src/librustc_mir/util/aggregate.rs @@ -1,7 +1,7 @@ -use rustc::mir::*; -use rustc::ty::layout::VariantIdx; -use rustc::ty::{Ty, TyCtxt}; use rustc_index::vec::Idx; +use rustc_middle::mir::*; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; use std::iter::TrustedLen; @@ -56,7 +56,7 @@ pub fn expand_aggregate<'tcx>( let offset = i as u32; assert_eq!(offset as usize, i); tcx.mk_place_elem( - lhs.clone(), + lhs, ProjectionElem::ConstantIndex { offset, // FIXME(eddyb) `min_length` doesn't appear to be used. @@ -66,7 +66,7 @@ pub fn expand_aggregate<'tcx>( ) } else { let field = Field::new(active_field_index.unwrap_or(i)); - tcx.mk_place_field(lhs.clone(), field, ty) + tcx.mk_place_field(lhs, field, ty) }; Statement { source_info, kind: StatementKind::Assign(box (lhs_field, Rvalue::Use(op))) } }) diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/util/alignment.rs index d7f2abfbe9943..202e5e27f1d94 100644 --- a/src/librustc_mir/util/alignment.rs +++ b/src/librustc_mir/util/alignment.rs @@ -1,5 +1,5 @@ -use rustc::mir::*; -use rustc::ty::{self, TyCtxt}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; /// Returns `true` if this place is allowed to be less aligned /// than its containing struct (because it is within a packed @@ -8,7 +8,7 @@ pub fn is_disaligned<'tcx, L>( tcx: TyCtxt<'tcx>, local_decls: &L, param_env: ty::ParamEnv<'tcx>, - place: &Place<'tcx>, + place: Place<'tcx>, ) -> bool where L: HasLocalDecls<'tcx>, @@ -34,7 +34,7 @@ where } } -fn is_within_packed<'tcx, L>(tcx: TyCtxt<'tcx>, local_decls: &L, place: &Place<'tcx>) -> bool +fn is_within_packed<'tcx, L>(tcx: TyCtxt<'tcx>, local_decls: &L, place: Place<'tcx>) -> bool where L: HasLocalDecls<'tcx>, { diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index d8ee059f1a6b6..f8bb7e7a85d11 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -1,10 +1,10 @@ -use rustc::ty::{self, Ty, TyCtxt}; use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{MultiSpan, Span}; impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { crate fn cannot_move_when_borrowed(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx> { - struct_span_err!(self, span, E0505, "cannot move out of `{}` because it is borrowed", desc,) + struct_span_err!(self, span, E0505, "cannot move out of {} because it is borrowed", desc,) } crate fn cannot_use_when_mutably_borrowed( @@ -18,12 +18,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, span, E0503, - "cannot use `{}` because it was mutably borrowed", + "cannot use {} because it was mutably borrowed", desc, ); - err.span_label(borrow_span, format!("borrow of `{}` occurs here", borrow_desc)); - err.span_label(span, format!("use of borrowed `{}`", borrow_desc)); + err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_desc)); + err.span_label(span, format!("use of borrowed {}", borrow_desc)); err } @@ -53,12 +53,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { old_load_end_span: Option, ) -> DiagnosticBuilder<'cx> { let via = - |msg: &str| if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) }; + |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) }; let mut err = struct_span_err!( self, new_loan_span, E0499, - "cannot borrow `{}`{} as mutable more than once at a time", + "cannot borrow {}{} as mutable more than once at a time", desc, via(opt_via), ); @@ -103,7 +103,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, new_loan_span, E0524, - "two closures require unique access to `{}` at the same time", + "two closures require unique access to {} at the same time", desc, ); if old_loan_span == new_loan_span { @@ -136,7 +136,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, new_loan_span, E0500, - "closure requires unique access to `{}` but {} is already borrowed{}", + "closure requires unique access to {} but {} is already borrowed{}", desc_new, noun_old, old_opt_via, @@ -168,7 +168,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, new_loan_span, E0501, - "cannot borrow `{}`{} as {} because previous closure \ + "cannot borrow {}{} as {} because previous closure \ requires unique access", desc_new, opt_via, @@ -201,13 +201,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { old_load_end_span: Option, ) -> DiagnosticBuilder<'cx> { let via = - |msg: &str| if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) }; + |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) }; let mut err = struct_span_err!( self, span, E0502, - "cannot borrow `{}`{} as {} because {} is also borrowed \ - as {}{}", + "cannot borrow {}{} as {} because {} is also borrowed as {}{}", desc_new, via(msg_new), kind_new, @@ -225,7 +224,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { err.span_label( span, format!( - "{} borrow of `{}` -- which overlaps with `{}` -- occurs here", + "{} borrow of {} -- which overlaps with {} -- occurs here", kind_new, msg_new, msg_old, ), ); @@ -248,12 +247,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, span, E0506, - "cannot assign to `{}` because it is borrowed", + "cannot assign to {} because it is borrowed", desc, ); - err.span_label(borrow_span, format!("borrow of `{}` occurs here", desc)); - err.span_label(span, format!("assignment to borrowed `{}` occurs here", desc)); + err.span_label(borrow_span, format!("borrow of {} occurs here", desc)); + err.span_label(span, format!("assignment to borrowed {} occurs here", desc)); err } @@ -264,7 +263,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { is_arg: bool, ) -> DiagnosticBuilder<'cx> { let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" }; - struct_span_err!(self, span, E0384, "cannot assign {} `{}`", msg, desc,) + struct_span_err!(self, span, E0384, "cannot assign {} {}", msg, desc) } crate fn cannot_assign(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx> { @@ -362,7 +361,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, mutate_span, E0510, - "cannot {} `{}` in {}", + "cannot {} {} in {}", action, immutable_place, immutable_section, @@ -432,6 +431,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { crate fn cannot_capture_in_long_lived_closure( &self, closure_span: Span, + closure_kind: &str, borrowed_path: &str, capture_span: Span, ) -> DiagnosticBuilder<'cx> { @@ -439,9 +439,10 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, closure_span, E0373, - "closure may outlive the current function, \ + "{} may outlive the current function, \ but it borrows {}, \ which is owned by the current function", + closure_kind, borrowed_path, ); err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) diff --git a/src/librustc_mir/util/collect_writes.rs b/src/librustc_mir/util/collect_writes.rs index 6cd2131649673..ecf3b08a96eed 100644 --- a/src/librustc_mir/util/collect_writes.rs +++ b/src/librustc_mir/util/collect_writes.rs @@ -1,7 +1,6 @@ -use rustc::mir::visit::PlaceContext; -use rustc::mir::visit::Visitor; -use rustc::mir::ReadOnlyBodyAndCache; -use rustc::mir::{Local, Location}; +use rustc_middle::mir::visit::PlaceContext; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Body, Local, Location}; crate trait FindAssignments { // Finds all statements that assign directly to local (i.e., X = ...) @@ -9,10 +8,10 @@ crate trait FindAssignments { fn find_assignments(&self, local: Local) -> Vec; } -impl<'a, 'tcx> FindAssignments for ReadOnlyBodyAndCache<'a, 'tcx> { +impl<'tcx> FindAssignments for Body<'tcx> { fn find_assignments(&self, local: Local) -> Vec { let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; - visitor.visit_body(*self); + visitor.visit_body(self); visitor.locations } } diff --git a/src/librustc_mir/util/def_use.rs b/src/librustc_mir/util/def_use.rs index aa9ddbdbda94f..b4448ead8eb81 100644 --- a/src/librustc_mir/util/def_use.rs +++ b/src/librustc_mir/util/def_use.rs @@ -1,11 +1,9 @@ //! Def-use analysis. -use rustc::mir::visit::{MutVisitor, PlaceContext, Visitor}; -use rustc::mir::{ - Body, BodyAndCache, Local, Location, PlaceElem, ReadOnlyBodyAndCache, VarDebugInfo, -}; -use rustc::ty::TyCtxt; use rustc_index::vec::IndexVec; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::{Body, Local, Location, VarDebugInfo}; +use rustc_middle::ty::TyCtxt; use std::mem; pub struct DefUseAnalysis { @@ -30,7 +28,7 @@ impl DefUseAnalysis { DefUseAnalysis { info: IndexVec::from_elem_n(Info::new(), body.local_decls.len()) } } - pub fn analyze(&mut self, body: ReadOnlyBodyAndCache<'_, '_>) { + pub fn analyze(&mut self, body: &Body<'_>) { self.clear(); let mut finder = DefUseFinder { @@ -38,7 +36,7 @@ impl DefUseAnalysis { var_debug_info_index: 0, in_var_debug_info: false, }; - finder.visit_body(body); + finder.visit_body(&body); self.info = finder.info } @@ -55,7 +53,7 @@ impl DefUseAnalysis { fn mutate_defs_and_uses( &self, local: Local, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, new_local: Local, tcx: TyCtxt<'tcx>, ) { @@ -74,7 +72,7 @@ impl DefUseAnalysis { pub fn replace_all_defs_and_uses_with( &self, local: Local, - body: &mut BodyAndCache<'tcx>, + body: &mut Body<'tcx>, new_local: Local, tcx: TyCtxt<'tcx>, ) { @@ -157,13 +155,4 @@ impl MutVisitor<'tcx> for MutateUseVisitor<'tcx> { *local = self.new_local; } } - - fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option> { - match elem { - PlaceElem::Index(local) if *local == self.query => { - Some(PlaceElem::Index(self.new_local)) - } - _ => None, - } - } } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 14895ddfbe44f..ed999c6871bb6 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -1,21 +1,26 @@ use crate::util::patch::MirPatch; -use rustc::middle::lang_items; -use rustc::mir::*; -use rustc::traits::Reveal; -use rustc::ty::layout::VariantIdx; -use rustc::ty::subst::SubstsRef; -use rustc::ty::util::IntTypeExt; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; +use rustc_hir::lang_items::{BoxFreeFnLangItem, DropTraitLangItem}; use rustc_index::vec::Idx; +use rustc_middle::mir::*; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; use std::fmt; use std::convert::TryInto; +/// The value of an inserted drop flag. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum DropFlagState { - Present, // i.e., initialized - Absent, // i.e., deinitialized or "moved" + /// The tracked value is initialized and needs to be dropped when leaving its scope. + Present, + + /// The tracked value is uninitialized or was moved out of and does not need to be dropped when + /// leaving its scope. + Absent, } impl DropFlagState { @@ -27,23 +32,42 @@ impl DropFlagState { } } +/// Describes how/if a value should be dropped. #[derive(Debug)] pub enum DropStyle { + /// The value is already dead at the drop location, no drop will be executed. Dead, + + /// The value is known to always be initialized at the drop location, drop will always be + /// executed. Static, + + /// Whether the value needs to be dropped depends on its drop flag. Conditional, + + /// An "open" drop is one where only the fields of a value are dropped. + /// + /// For example, this happens when moving out of a struct field: The rest of the struct will be + /// dropped in such an "open" drop. It is also used to generate drop glue for the individual + /// components of a value, for example for dropping array elements. Open, } +/// Which drop flags to affect/check with an operation. #[derive(Debug)] pub enum DropFlagMode { + /// Only affect the top-level drop flag, not that of any contained fields. Shallow, + /// Affect all nested drop flags in addition to the top-level one. Deep, } +/// Describes if unwinding is necessary and where to unwind to if a panic occurs. #[derive(Copy, Clone, Debug)] pub enum Unwind { + /// Unwind to this block. To(BasicBlock), + /// Already in an unwind path, any panic will cause an abort. InCleanup, } @@ -74,20 +98,58 @@ impl Unwind { } pub trait DropElaborator<'a, 'tcx>: fmt::Debug { + /// The type representing paths that can be moved out of. + /// + /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to + /// represent such move paths. Sometimes tracking individual move paths is not necessary, in + /// which case this may be set to (for example) `()`. type Path: Copy + fmt::Debug; + // Accessors + fn patch(&mut self) -> &mut MirPatch<'tcx>; fn body(&self) -> &'a Body<'tcx>; fn tcx(&self) -> TyCtxt<'tcx>; fn param_env(&self) -> ty::ParamEnv<'tcx>; + // Drop logic + + /// Returns how `path` should be dropped, given `mode`. fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; + + /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag). fn get_drop_flag(&mut self, path: Self::Path) -> Option>; + + /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`. + /// + /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting + /// additional statements. fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); + // Subpaths + + /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `field` will not get a dedicated drop flag. fn field_subpath(&self, path: Self::Path, field: Field) -> Option; + + /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `*path` will not get a dedicated drop flag. + /// + /// This is only relevant for `Box`, where the contained `T` can be moved out of the box. fn deref_subpath(&self, path: Self::Path) -> Option; + + /// Returns the subpath of downcasting `path` to one of its variants. + /// + /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag. fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option; + + /// Returns the subpath of indexing a fixed-size array `path`. + /// + /// If this returns `None`, elements of `path` will not get a dedicated drop flag. + /// + /// This is only relevant for array patterns, which can move out of individual array elements. fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option; } @@ -100,16 +162,24 @@ where source_info: SourceInfo, - place: &'l Place<'tcx>, + place: Place<'tcx>, path: D::Path, succ: BasicBlock, unwind: Unwind, } +/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. +/// +/// The passed `elaborator` is used to determine what should happen at the drop terminator. It +/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag, +/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped +/// value. +/// +/// When this returns, the MIR patch in the `elaborator` contains the necessary changes. pub fn elaborate_drop<'b, 'tcx, D>( elaborator: &mut D, source_info: SourceInfo, - place: &Place<'tcx>, + place: Place<'tcx>, path: D::Path, succ: BasicBlock, unwind: Unwind, @@ -126,7 +196,7 @@ where D: DropElaborator<'b, 'tcx>, 'tcx: 'b, { - fn place_ty(&self, place: &Place<'tcx>) -> Ty<'tcx> { + fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> { place.ty(self.elaborator.body(), self.tcx()).ty } @@ -163,21 +233,17 @@ where .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); } DropStyle::Static => { - let loc = self.terminator_loc(bb); - self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); self.elaborator.patch().patch_terminator( bb, TerminatorKind::Drop { - location: *self.place, + location: self.place, target: self.succ, unwind: self.unwind.into_option(), }, ); } DropStyle::Conditional => { - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - let drop_bb = self.complete_drop(Some(DropFlagMode::Deep), succ, unwind); + let drop_bb = self.complete_drop(self.succ, self.unwind); self.elaborator .patch() .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); @@ -195,7 +261,7 @@ where /// (the move path is `None` if the field is a rest field). fn move_paths_for_fields( &self, - base_place: &Place<'tcx>, + base_place: Place<'tcx>, variant_path: D::Path, variant: &'tcx ty::VariantDef, substs: SubstsRef<'tcx>, @@ -212,14 +278,14 @@ where assert_eq!(self.elaborator.param_env().reveal, Reveal::All); let field_ty = tcx.normalize_erasing_regions(self.elaborator.param_env(), f.ty(tcx, substs)); - (tcx.mk_place_field(base_place.clone(), field, field_ty), subpath) + (tcx.mk_place_field(base_place, field, field_ty), subpath) }) .collect() } fn drop_subpath( &mut self, - place: &Place<'tcx>, + place: Place<'tcx>, path: Option, succ: BasicBlock, unwind: Unwind, @@ -249,7 +315,7 @@ where // our own drop flag. path: self.path, } - .complete_drop(None, succ, unwind) + .complete_drop(succ, unwind) } } @@ -267,12 +333,10 @@ where ) -> Vec { Some(succ) .into_iter() - .chain(fields.iter().rev().zip(unwind_ladder).map( - |(&(ref place, path), &unwind_succ)| { - succ = self.drop_subpath(place, path, succ, unwind_succ); - succ - }, - )) + .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { + succ = self.drop_subpath(place, path, succ, unwind_succ); + succ + })) .collect() } @@ -280,13 +344,7 @@ where // Clear the "master" drop flag at the end. This is needed // because the "master" drop protects the ADT's discriminant, // which is invalidated after the ADT is dropped. - let (succ, unwind) = (self.succ, self.unwind); // FIXME(#43234) - ( - self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind), - unwind.map(|unwind| { - self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup) - }), - ) + (self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), self.unwind) } /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders @@ -315,7 +373,7 @@ where debug!("drop_ladder({:?}, {:?})", self, fields); let mut fields = fields; - fields.retain(|&(ref place, _)| { + fields.retain(|&(place, _)| { self.place_ty(place).needs_drop(self.tcx(), self.elaborator.param_env()) }); @@ -342,7 +400,7 @@ where .enumerate() .map(|(i, &ty)| { ( - self.tcx().mk_place_field(self.place.clone(), Field::new(i), ty), + self.tcx().mk_place_field(self.place, Field::new(i), ty), self.elaborator.field_subpath(self.path, Field::new(i)), ) }) @@ -355,16 +413,14 @@ where fn open_drop_for_box(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock { debug!("open_drop_for_box({:?}, {:?}, {:?})", self, adt, substs); - let interior = self.tcx().mk_place_deref(self.place.clone()); + let interior = self.tcx().mk_place_deref(self.place); let interior_path = self.elaborator.deref_subpath(self.path); - let succ = self.succ; // FIXME(#43234) - let unwind = self.unwind; - let succ = self.box_free_block(adt, substs, succ, unwind); + let succ = self.box_free_block(adt, substs, self.succ, self.unwind); let unwind_succ = self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup)); - self.drop_subpath(&interior, interior_path, succ, unwind_succ) + self.drop_subpath(interior, interior_path, succ, unwind_succ) } fn open_drop_for_adt(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock { @@ -436,11 +492,10 @@ where if let Some(variant_path) = subpath { let base_place = tcx.mk_place_elem( - self.place.clone(), + self.place, ProjectionElem::Downcast(Some(variant.ident.name), variant_index), ); - let fields = - self.move_paths_for_fields(&base_place, variant_path, &variant, substs); + let fields = self.move_paths_for_fields(base_place, variant_path, &variant, substs); values.push(discr.val); if let Unwind::To(unwind) = unwind { // We can't use the half-ladder from the original @@ -527,9 +582,9 @@ where // way lies only trouble. let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); let discr = Place::from(self.new_temp(discr_ty)); - let discr_rv = Rvalue::Discriminant(*self.place); + let discr_rv = Rvalue::Discriminant(self.place); let switch_block = BasicBlockData { - statements: vec![self.assign(&discr, discr_rv)], + statements: vec![self.assign(discr, discr_rv)], terminator: Some(Terminator { source_info: self.source_info, kind: TerminatorKind::SwitchInt { @@ -548,7 +603,7 @@ where fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { debug!("destructor_call_block({:?}, {:?})", self, succ); let tcx = self.tcx(); - let drop_trait = tcx.lang_items().drop_trait().unwrap(); + let drop_trait = tcx.require_lang_item(DropTraitLangItem, None); let drop_fn = tcx.associated_items(drop_trait).in_definition_order().next().unwrap(); let ty = self.place_ty(self.place); let substs = tcx.mk_substs_trait(ty, &[]); @@ -560,11 +615,11 @@ where let result = BasicBlockData { statements: vec![self.assign( - &Place::from(ref_place), + Place::from(ref_place), Rvalue::Ref( tcx.lifetimes.re_erased, BorrowKind::Mut { allow_two_phase_borrow: false }, - *self.place, + self.place, ), )], terminator: Some(Terminator { @@ -607,7 +662,7 @@ where &mut self, succ: BasicBlock, cur: Local, - length_or_end: &Place<'tcx>, + length_or_end: Place<'tcx>, ety: Ty<'tcx>, unwind: Unwind, ptr_based: bool, @@ -617,7 +672,7 @@ where let tcx = self.tcx(); let ptr_ty = tcx.mk_ptr(ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }); - let ptr = &Place::from(self.new_temp(ptr_ty)); + let ptr = Place::from(self.new_temp(ptr_ty)); let can_go = Place::from(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); @@ -625,13 +680,13 @@ where (Rvalue::Use(copy(cur.into())), Rvalue::BinaryOp(BinOp::Offset, move_(cur.into()), one)) } else { ( - Rvalue::AddressOf(Mutability::Mut, tcx.mk_place_index(self.place.clone(), cur)), + Rvalue::AddressOf(Mutability::Mut, tcx.mk_place_index(self.place, cur)), Rvalue::BinaryOp(BinOp::Add, move_(cur.into()), one), ) }; let drop_block = BasicBlockData { - statements: vec![self.assign(ptr, ptr_next), self.assign(&Place::from(cur), cur_next)], + statements: vec![self.assign(ptr, ptr_next), self.assign(Place::from(cur), cur_next)], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { source_info: self.source_info, @@ -643,8 +698,8 @@ where let loop_block = BasicBlockData { statements: vec![self.assign( - &can_go, - Rvalue::BinaryOp(BinOp::Eq, copy(Place::from(cur)), copy(*length_or_end)), + can_go, + Rvalue::BinaryOp(BinOp::Eq, copy(Place::from(cur)), copy(length_or_end)), )], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { @@ -657,7 +712,7 @@ where self.elaborator.patch().patch_terminator( drop_block, TerminatorKind::Drop { - location: tcx.mk_place_deref(ptr.clone()), + location: tcx.mk_place_deref(ptr), target: loop_block, unwind: unwind.into_option(), }, @@ -685,7 +740,7 @@ where .map(|i| { ( tcx.mk_place_elem( - self.place.clone(), + self.place, ProjectionElem::ConstantIndex { offset: i, min_length: size, @@ -703,16 +758,16 @@ where } } - let move_ = |place: &Place<'tcx>| Operand::Move(*place); - let elem_size = &Place::from(self.new_temp(tcx.types.usize)); - let len = &Place::from(self.new_temp(tcx.types.usize)); + let move_ = |place: Place<'tcx>| Operand::Move(place); + let elem_size = Place::from(self.new_temp(tcx.types.usize)); + let len = Place::from(self.new_temp(tcx.types.usize)); static USIZE_SWITCH_ZERO: &[u128] = &[0]; let base_block = BasicBlockData { statements: vec![ self.assign(elem_size, Rvalue::NullaryOp(NullOp::SizeOf, ety)), - self.assign(len, Rvalue::Len(*self.place)), + self.assign(len, Rvalue::Len(self.place)), ], is_cleanup: self.unwind.is_cleanup(), terminator: Some(Terminator { @@ -722,8 +777,8 @@ where switch_ty: tcx.types.usize, values: From::from(USIZE_SWITCH_ZERO), targets: vec![ - self.drop_loop_pair(ety, false, len.clone()), - self.drop_loop_pair(ety, true, len.clone()), + self.drop_loop_pair(ety, false, len), + self.drop_loop_pair(ety, true, len), ], }, }), @@ -748,10 +803,10 @@ where let length_or_end = if ptr_based { Place::from(self.new_temp(iter_ty)) } else { length }; let unwind = self.unwind.map(|unwind| { - self.drop_loop(unwind, cur, &length_or_end, ety, Unwind::InCleanup, ptr_based) + self.drop_loop(unwind, cur, length_or_end, ety, Unwind::InCleanup, ptr_based) }); - let loop_block = self.drop_loop(self.succ, cur, &length_or_end, ety, unwind, ptr_based); + let loop_block = self.drop_loop(self.succ, cur, length_or_end, ety, unwind, ptr_based); let cur = Place::from(cur); let drop_block_stmts = if ptr_based { @@ -761,17 +816,17 @@ where // cur = tmp as *mut T; // end = Offset(cur, len); vec![ - self.assign(&tmp, Rvalue::AddressOf(Mutability::Mut, *self.place)), - self.assign(&cur, Rvalue::Cast(CastKind::Misc, Operand::Move(tmp), iter_ty)), + self.assign(tmp, Rvalue::AddressOf(Mutability::Mut, self.place)), + self.assign(cur, Rvalue::Cast(CastKind::Misc, Operand::Move(tmp), iter_ty)), self.assign( - &length_or_end, + length_or_end, Rvalue::BinaryOp(BinOp::Offset, Operand::Copy(cur), Operand::Move(length)), ), ] } else { // cur = 0 (length already pushed) let zero = self.constant_usize(0); - vec![self.assign(&cur, Rvalue::Use(zero))] + vec![self.assign(cur, Rvalue::Use(zero))] }; let drop_block = self.elaborator.patch().new_block(BasicBlockData { statements: drop_block_stmts, @@ -823,11 +878,7 @@ where self.open_drop_for_adt(def, substs) } } - ty::Dynamic(..) => { - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) - } + ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), ty::Array(ety, size) => { let size = size.try_eval_usize(self.tcx(), self.elaborator.param_env()); self.open_drop_for_array(ety, size) @@ -838,31 +889,16 @@ where } } - /// Returns a basic block that drop a place using the context - /// and path in `c`. If `mode` is something, also clear `c` - /// according to it. - /// - /// if FLAG(self.path) - /// if let Some(mode) = mode: FLAG(self.path)[mode] = false - /// drop(self.place) - fn complete_drop( - &mut self, - drop_mode: Option, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - debug!("complete_drop({:?},{:?})", self, drop_mode); + fn complete_drop(&mut self, succ: BasicBlock, unwind: Unwind) -> BasicBlock { + debug!("complete_drop(succ={:?}, unwind={:?})", succ, unwind); let drop_block = self.drop_block(succ, unwind); - let drop_block = if let Some(mode) = drop_mode { - self.drop_flag_reset_block(mode, drop_block, unwind) - } else { - drop_block - }; self.drop_flag_test_block(drop_block, succ, unwind) } + /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will + /// also be cleared. fn drop_flag_reset_block( &mut self, mode: DropFlagMode, @@ -871,6 +907,11 @@ where ) -> BasicBlock { debug!("drop_flag_reset_block({:?},{:?})", self, mode); + if unwind.is_cleanup() { + // The drop flag isn't read again on the unwind path, so don't + // bother setting it. + return succ; + } let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); let block_start = Location { block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, mode); @@ -879,13 +920,15 @@ where fn elaborated_drop_block(&mut self) -> BasicBlock { debug!("elaborated_drop_block({:?})", self); - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - let blk = self.drop_block(succ, unwind); + let blk = self.drop_block(self.succ, self.unwind); self.elaborate_drop(blk); blk } + /// Creates a block that frees the backing memory of a `Box` if its drop is required (either + /// statically or by checking its drop flag). + /// + /// The contained value will not be dropped. fn box_free_block( &mut self, adt: &'tcx ty::AdtDef, @@ -897,6 +940,8 @@ where self.drop_flag_test_block(block, target, unwind) } + /// Creates a block that frees the backing memory of a `Box` (without dropping the contained + /// value). fn unelaborated_free_block( &mut self, adt: &'tcx ty::AdtDef, @@ -906,8 +951,7 @@ where ) -> BasicBlock { let tcx = self.tcx(); let unit_temp = Place::from(self.new_temp(tcx.mk_unit())); - let free_func = - tcx.require_lang_item(lang_items::BoxFreeFnLangItem, Some(self.source_info.span)); + let free_func = tcx.require_lang_item(BoxFreeFnLangItem, Some(self.source_info.span)); let args = adt.variants[VariantIdx::new(0)] .fields .iter() @@ -915,7 +959,7 @@ where .map(|(i, f)| { let field = Field::new(i); let field_ty = f.ty(tcx, substs); - Operand::Move(tcx.mk_place_field(self.place.clone(), field, field_ty)) + Operand::Move(tcx.mk_place_field(self.place, field, field_ty)) }) .collect(); @@ -935,7 +979,7 @@ where fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let block = - TerminatorKind::Drop { location: *self.place, target, unwind: unwind.into_option() }; + TerminatorKind::Drop { location: self.place, target, unwind: unwind.into_option() }; self.new_block(unwind, block) } @@ -944,6 +988,11 @@ where self.new_block(unwind, block) } + /// Returns the block to jump to in order to test the drop flag and execute the drop. + /// + /// Depending on the required `DropStyle`, this might be a generated block with an `if` + /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case + /// the drop can be statically determined. fn drop_flag_test_block( &mut self, on_set: BasicBlock, @@ -979,11 +1028,6 @@ where self.elaborator.patch().new_temp(ty, self.source_info.span) } - fn terminator_loc(&mut self, bb: BasicBlock) -> Location { - let body = self.elaborator.body(); - self.elaborator.patch().terminator_loc(body, bb) - } - fn constant_usize(&self, val: u16) -> Operand<'tcx> { Operand::Constant(box Constant { span: self.source_info.span, @@ -992,7 +1036,7 @@ where }) } - fn assign(&self, lhs: &Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { - Statement { source_info: self.source_info, kind: StatementKind::Assign(box (*lhs, rhs)) } + fn assign(&self, lhs: Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { + Statement { source_info: self.source_info, kind: StatementKind::Assign(box (lhs, rhs)) } } } diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs index 8291bc958808d..fb862b926d782 100644 --- a/src/librustc_mir/util/graphviz.rs +++ b/src/librustc_mir/util/graphviz.rs @@ -1,7 +1,7 @@ -use rustc::mir::*; -use rustc::ty::TyCtxt; use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; use std::fmt::Debug; use std::io::{self, Write}; @@ -97,12 +97,17 @@ where write!(w, r#""#)?; // Basic block number at the top. + let (blk, color) = if data.is_cleanup { + (format!("{} (cleanup)", block.index()), "lightblue") + } else { + (format!("{}", block.index()), "gray") + }; write!( w, - r#""#, - attrs = r#"bgcolor="gray" align="center""#, + r#""#, colspan = num_cols, - blk = block.index() + blk = blk, + color = color )?; init(w)?; diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs deleted file mode 100644 index f6c6f55549593..0000000000000 --- a/src/librustc_mir/util/liveness.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! Liveness analysis which computes liveness of MIR local variables at the boundary of basic -//! blocks. -//! -//! This analysis considers references as being used only at the point of the -//! borrow. This means that this does not track uses because of references that -//! already exist: -//! -//! ```rust -//! fn foo() { -//! x = 0; -//! // `x` is live here ... -//! GLOBAL = &x: *const u32; -//! // ... but not here, even while it can be accessed through `GLOBAL`. -//! foo(); -//! x = 1; -//! // `x` is live again here, because it is assigned to `OTHER_GLOBAL`. -//! OTHER_GLOBAL = &x: *const u32; -//! // ... -//! } -//! ``` -//! -//! This means that users of this analysis still have to check whether -//! pre-existing references can be used to access the value (e.g., at movable -//! generator yield points, all pre-existing references are invalidated, so this -//! doesn't matter). - -use crate::transform::MirSource; -use crate::util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; -use rustc::mir::visit::{ - MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor, -}; -use rustc::mir::Local; -use rustc::mir::*; -use rustc::ty::{self, TyCtxt}; -use rustc_data_structures::work_queue::WorkQueue; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; -use std::fs; -use std::io::{self, BufWriter, Write}; -use std::path::{Path, PathBuf}; - -pub type LiveVarSet = BitSet; - -/// This gives the result of the liveness analysis at the boundary of -/// basic blocks. -/// -/// The `V` type defines the set of variables that we computed -/// liveness for. This is often `Local`, in which case we computed -/// liveness for all variables -- but it can also be some other type, -/// which indicates a subset of the variables within the graph. -pub struct LivenessResult { - /// Live variables on exit to each basic block. This is equal to - /// the union of the `ins` for each successor. - pub outs: IndexVec, -} - -/// Computes which local variables are live within the given function -/// `mir`, including drops. -pub fn liveness_of_locals(body: ReadOnlyBodyAndCache<'_, '_>) -> LivenessResult { - let num_live_vars = body.local_decls.len(); - - let def_use: IndexVec<_, DefsUses> = - body.basic_blocks().iter().map(|b| block(b, num_live_vars)).collect(); - - let mut outs: IndexVec<_, LiveVarSet> = - body.basic_blocks().indices().map(|_| LiveVarSet::new_empty(num_live_vars)).collect(); - - let mut bits = LiveVarSet::new_empty(num_live_vars); - - // The dirty queue contains the set of basic blocks whose entry sets have changed since they - // were last processed. At the start of the analysis, we initialize the queue in post-order to - // make it more likely that the entry set for a given basic block will have the effects of all - // its successors in the CFG applied before it is processed. - // - // FIXME(ecstaticmorse): Reverse post-order on the reverse CFG may generate a better iteration - // order when cycles are present, but the overhead of computing the reverse CFG may outweigh - // any benefits. Benchmark this and find out. - let mut dirty_queue: WorkQueue = WorkQueue::with_none(body.basic_blocks().len()); - for (bb, _) in traversal::postorder(&body) { - dirty_queue.insert(bb); - } - - // Add blocks which are not reachable from START_BLOCK to the work queue. These blocks will - // be processed after the ones added above. - for bb in body.basic_blocks().indices() { - dirty_queue.insert(bb); - } - - let predecessors = body.predecessors(); - - while let Some(bb) = dirty_queue.pop() { - // bits = use ∪ (bits - def) - bits.overwrite(&outs[bb]); - def_use[bb].apply(&mut bits); - - // `bits` now contains the live variables on entry. Therefore, - // add `bits` to the `out` set for each predecessor; if those - // bits were not already present, then enqueue the predecessor - // as dirty. - // - // (note that `union` returns true if the `self` set changed) - for &pred_bb in &predecessors[bb] { - if outs[pred_bb].union(&bits) { - dirty_queue.insert(pred_bb); - } - } - } - - LivenessResult { outs } -} - -#[derive(Eq, PartialEq, Clone)] -pub enum DefUse { - Def, - Use, - Drop, -} - -pub fn categorize(context: PlaceContext) -> Option { - match context { - /////////////////////////////////////////////////////////////////////////// - // DEFS - - PlaceContext::MutatingUse(MutatingUseContext::Store) | - - // This is potentially both a def and a use... - PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) | - - // We let Call define the result in both the success and - // unwind cases. This is not really correct, however it - // does not seem to be observable due to the way that we - // generate MIR. To do things properly, we would apply - // the def in call only to the input from the success - // path and not the unwind path. -nmatsakis - PlaceContext::MutatingUse(MutatingUseContext::Call) | - - // Storage live and storage dead aren't proper defines, but we can ignore - // values that come before them. - PlaceContext::NonUse(NonUseContext::StorageLive) | - PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def), - - /////////////////////////////////////////////////////////////////////////// - // REGULAR USES - // - // These are uses that occur *outside* of a drop. For the - // purposes of NLL, these are special in that **all** the - // lifetimes appearing in the variable must be live for each regular use. - - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) | - PlaceContext::MutatingUse(MutatingUseContext::Projection) | - - // Borrows only consider their local used at the point of the borrow. - // This won't affect the results since we use this analysis for generators - // and we only care about the result at suspension points. Borrows cannot - // cross suspension points so this behavior is unproblematic. - PlaceContext::MutatingUse(MutatingUseContext::Borrow) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) | - - PlaceContext::MutatingUse(MutatingUseContext::AddressOf) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) | - PlaceContext::NonUse(NonUseContext::AscribeUserTy) | - PlaceContext::MutatingUse(MutatingUseContext::Retag) => - Some(DefUse::Use), - - /////////////////////////////////////////////////////////////////////////// - // DROP USES - // - // These are uses that occur in a DROP (a MIR drop, not a - // call to `std::mem::drop()`). For the purposes of NLL, - // uses in drop are special because `#[may_dangle]` - // attributes can affect whether lifetimes must be live. - - PlaceContext::MutatingUse(MutatingUseContext::Drop) => - Some(DefUse::Drop), - - // Debug info is neither def nor use. - PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None, - } -} - -struct DefsUsesVisitor { - defs_uses: DefsUses, -} - -#[derive(Eq, PartialEq, Clone)] -struct DefsUses { - defs: LiveVarSet, - uses: LiveVarSet, -} - -impl DefsUses { - fn apply(&self, bits: &mut LiveVarSet) -> bool { - bits.subtract(&self.defs) | bits.union(&self.uses) - } - - fn add_def(&mut self, index: Local) { - // If it was used already in the block, remove that use - // now that we found a definition. - // - // Example: - // - // // Defs = {X}, Uses = {} - // X = 5 - // // Defs = {}, Uses = {X} - // use(X) - self.uses.remove(index); - self.defs.insert(index); - } - - fn add_use(&mut self, index: Local) { - // Inverse of above. - // - // Example: - // - // // Defs = {}, Uses = {X} - // use(X) - // // Defs = {X}, Uses = {} - // X = 5 - // // Defs = {}, Uses = {X} - // use(X) - self.defs.remove(index); - self.uses.insert(index); - } -} - -impl<'tcx> Visitor<'tcx> for DefsUsesVisitor { - fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { - match categorize(context) { - Some(DefUse::Def) => self.defs_uses.add_def(local), - Some(DefUse::Use) | Some(DefUse::Drop) => self.defs_uses.add_use(local), - _ => (), - } - } -} - -fn block(b: &BasicBlockData<'_>, locals: usize) -> DefsUses { - let mut visitor = DefsUsesVisitor { - defs_uses: DefsUses { - defs: LiveVarSet::new_empty(locals), - uses: LiveVarSet::new_empty(locals), - }, - }; - - let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 }; - - // Visit the various parts of the basic block in reverse. If we go - // forward, the logic in `add_def` and `add_use` would be wrong. - visitor.visit_terminator(b.terminator(), dummy_location); - for statement in b.statements.iter().rev() { - visitor.visit_statement(statement, dummy_location); - } - - visitor.defs_uses -} - -pub fn dump_mir<'tcx>( - tcx: TyCtxt<'tcx>, - pass_name: &str, - source: MirSource<'tcx>, - body: &Body<'tcx>, - result: &LivenessResult, -) { - if !dump_enabled(tcx, pass_name, source) { - return; - } - let node_path = ty::print::with_forced_impl_filename_line(|| { - // see notes on #41697 below - tcx.def_path_str(source.def_id()) - }); - dump_matched_mir_node(tcx, pass_name, &node_path, source, body, result); -} - -fn dump_matched_mir_node<'tcx>( - tcx: TyCtxt<'tcx>, - pass_name: &str, - node_path: &str, - source: MirSource<'tcx>, - body: &Body<'tcx>, - result: &LivenessResult, -) { - let mut file_path = PathBuf::new(); - file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); - let item_id = tcx.hir().as_local_hir_id(source.def_id()).unwrap(); - let file_name = format!("rustc.node{}{}-liveness.mir", item_id, pass_name); - file_path.push(&file_name); - let _ = fs::File::create(&file_path).and_then(|file| { - let mut file = BufWriter::new(file); - writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?; - writeln!(file, "// source = {:?}", source)?; - writeln!(file, "// pass_name = {}", pass_name)?; - writeln!(file)?; - write_mir_fn(tcx, source, body, &mut file, result)?; - Ok(()) - }); -} - -pub fn write_mir_fn<'tcx>( - tcx: TyCtxt<'tcx>, - src: MirSource<'tcx>, - body: &Body<'tcx>, - w: &mut dyn Write, - result: &LivenessResult, -) -> io::Result<()> { - write_mir_intro(tcx, src, body, w)?; - for block in body.basic_blocks().indices() { - let print = |w: &mut dyn Write, prefix, result: &IndexVec| { - let live: Vec = - result[block].iter().map(|local| format!("{:?}", local)).collect(); - writeln!(w, "{} {{{}}}", prefix, live.join(", ")) - }; - write_basic_block(tcx, block, body, &mut |_, _| Ok(()), w)?; - print(w, " ", &result.outs)?; - if block.index() + 1 != body.basic_blocks().len() { - writeln!(w)?; - } - } - - writeln!(w, "}}")?; - Ok(()) -} diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index bb9d168d9193a..8bbe207c077ee 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -3,11 +3,11 @@ pub mod borrowck_errors; pub mod def_use; pub mod elaborate_drops; pub mod patch; +pub mod storage; mod alignment; pub mod collect_writes; mod graphviz; -pub mod liveness; pub(crate) mod pretty; pub use self::aggregate::expand_aggregate; diff --git a/src/librustc_mir/util/patch.rs b/src/librustc_mir/util/patch.rs index 473692a43f3e9..6566a996fe442 100644 --- a/src/librustc_mir/util/patch.rs +++ b/src/librustc_mir/util/patch.rs @@ -1,6 +1,6 @@ -use rustc::mir::*; -use rustc::ty::Ty; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::*; +use rustc_middle::ty::Ty; use rustc_span::Span; /// This struct represents a patch to MIR, which can add @@ -50,7 +50,7 @@ impl<'tcx> MirPatch<'tcx> { result.new_block(BasicBlockData { statements: vec![], terminator: Some(Terminator { - source_info: SourceInfo { span: body.span, scope: OUTERMOST_SOURCE_SCOPE }, + source_info: SourceInfo::outermost(body.span), kind: TerminatorKind::Resume, }), is_cleanup: true, @@ -83,14 +83,14 @@ impl<'tcx> MirPatch<'tcx> { pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local { let index = self.next_local; self.next_local += 1; - self.new_locals.push(LocalDecl::new_temp(ty, span)); + self.new_locals.push(LocalDecl::new(ty, span)); Local::new(index as usize) } pub fn new_internal(&mut self, ty: Ty<'tcx>, span: Span) -> Local { let index = self.next_local; self.next_local += 1; - self.new_locals.push(LocalDecl::new_internal(ty, span)); + self.new_locals.push(LocalDecl::new(ty, span).internal()); Local::new(index as usize) } @@ -121,7 +121,7 @@ impl<'tcx> MirPatch<'tcx> { self.make_nop.push(loc); } - pub fn apply(self, body: &mut BodyAndCache<'tcx>) { + pub fn apply(self, body: &mut Body<'tcx>) { debug!("MirPatch: make nops at: {:?}", self.make_nop); for loc in self.make_nop { body.make_statement_nop(loc); diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 4c380d1141806..ff386cb218304 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -1,16 +1,23 @@ +use std::collections::BTreeSet; +use std::fmt::Write as _; +use std::fmt::{Debug, Display}; +use std::fs; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; + use super::graphviz::write_mir_fn_graphviz; use crate::transform::MirSource; -use rustc::mir::visit::Visitor; -use rustc::mir::*; -use rustc::ty::{self, TyCtxt}; +use either::Either; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_index::vec::Idx; -use std::fmt::Display; -use std::fmt::Write as _; -use std::fs; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; +use rustc_middle::mir::interpret::{ + read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, +}; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_target::abi::Size; const INDENT: &str = " "; /// Alignment for lining up comments following MIR statements @@ -73,34 +80,21 @@ pub fn dump_mir<'tcx, F>( ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, { - if !dump_enabled(tcx, pass_name, source) { + if !dump_enabled(tcx, pass_name, source.def_id()) { return; } - let node_path = ty::print::with_forced_impl_filename_line(|| { - // see notes on #41697 below - tcx.def_path_str(source.def_id()) - }); - dump_matched_mir_node( - tcx, - pass_num, - pass_name, - &node_path, - disambiguator, - source, - body, - extra_data, - ); + dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, source, body, extra_data); } -pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, source: MirSource<'tcx>) -> bool { +pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool { let filters = match tcx.sess.opts.debugging_opts.dump_mir { None => return false, Some(ref filters) => filters, }; let node_path = ty::print::with_forced_impl_filename_line(|| { // see notes on #41697 below - tcx.def_path_str(source.def_id()) + tcx.def_path_str(def_id) }); filters.split('|').any(|or_filter| { or_filter.split('&').all(|and_filter| { @@ -117,7 +111,6 @@ fn dump_matched_mir_node<'tcx, F>( tcx: TyCtxt<'tcx>, pass_num: Option<&dyn Display>, pass_name: &str, - node_path: &str, disambiguator: &dyn Display, source: MirSource<'tcx>, body: &Body<'tcx>, @@ -127,10 +120,16 @@ fn dump_matched_mir_node<'tcx, F>( { let _: io::Result<()> = try { let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; - writeln!(file, "// MIR for `{}`", node_path)?; - writeln!(file, "// source = {:?}", source)?; - writeln!(file, "// pass_name = {}", pass_name)?; - writeln!(file, "// disambiguator = {}", disambiguator)?; + let def_path = ty::print::with_forced_impl_filename_line(|| { + // see notes on #41697 above + tcx.def_path_str(source.def_id()) + }); + write!(file, "// MIR for `{}", def_path)?; + match source.promoted { + None => write!(file, "`")?, + Some(promoted) => write!(file, "::{:?}`", promoted)?, + } + writeln!(file, " {} {}", disambiguator, pass_name)?; if let Some(ref layout) = body.generator_layout { writeln!(file, "// generator_layout = {:?}", layout)?; } @@ -256,6 +255,7 @@ pub fn write_mir_pretty<'tcx>( Ok(()) } +/// Write out a human-readable textual representation for the given function. pub fn write_mir_fn<'tcx, F>( tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, @@ -276,6 +276,9 @@ where } writeln!(w, "}}")?; + + write_allocations(tcx, body, w)?; + Ok(()) } @@ -303,9 +306,9 @@ where let indented_body = format!("{0}{0}{1:?};", INDENT, statement); writeln!( w, - "{:A$} // {:?}: {}", + "{:A$} // {}{}", indented_body, - current_location, + if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, comment(tcx, statement.source_info), A = ALIGN, )?; @@ -324,9 +327,9 @@ where let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); writeln!( w, - "{:A$} // {:?}: {}", + "{:A$} // {}{}", indented_terminator, - current_location, + if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, comment(tcx, data.terminator().source_info), A = ALIGN, )?; @@ -391,8 +394,8 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); - match rvalue { - Rvalue::Aggregate(kind, _) => match **kind { + if let Rvalue::Aggregate(kind, _) = rvalue { + match **kind { AggregateKind::Closure(def_id, substs) => { self.push("closure"); self.push(&format!("+ def_id: {:?}", def_id)); @@ -412,9 +415,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { } _ => {} - }, - - _ => {} + } } } } @@ -455,7 +456,7 @@ fn write_scope_tree( )?; } - // Local variable types (including the user's name in a comment). + // Local variable types. for (local, local_decl) in body.local_decls.iter_enumerated() { if (1..body.arg_count + 1).contains(&local.index()) { // Skip over argument locals, they're printed in the signature. @@ -471,8 +472,10 @@ fn write_scope_tree( let mut indented_decl = format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty); - for user_ty in local_decl.user_ty.projections() { - write!(indented_decl, " as {:?}", user_ty).unwrap(); + if let Some(user_ty) = &local_decl.user_ty { + for user_ty in user_ty.projections() { + write!(indented_decl, " as {:?}", user_ty).unwrap(); + } } indented_decl.push_str(";"); @@ -534,6 +537,273 @@ pub fn write_mir_intro<'tcx>( Ok(()) } +/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding +/// allocations. +pub fn write_allocations<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'_>, + w: &mut dyn Write, +) -> io::Result<()> { + fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator + '_ { + alloc.relocations().values().map(|(_, id)| *id) + } + fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator + '_ { + match val { + ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => { + Either::Left(Either::Left(std::iter::once(ptr.alloc_id))) + } + ConstValue::Scalar(interpret::Scalar::Raw { .. }) => { + Either::Left(Either::Right(std::iter::empty())) + } + ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => { + Either::Right(alloc_ids_from_alloc(alloc)) + } + } + } + struct CollectAllocIds(BTreeSet); + impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + if let ty::ConstKind::Value(val) = c.val { + self.0.extend(alloc_ids_from_const(val)); + } + c.super_visit_with(self) + } + } + let mut visitor = CollectAllocIds(Default::default()); + body.visit_with(&mut visitor); + // `seen` contains all seen allocations, including the ones we have *not* printed yet. + // The protocol is to first `insert` into `seen`, and only if that returns `true` + // then push to `todo`. + let mut seen = visitor.0; + let mut todo: Vec<_> = seen.iter().copied().collect(); + while let Some(id) = todo.pop() { + let mut write_allocation_track_relocs = + |w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> { + // `.rev()` because we are popping them from the back of the `todo` vector. + for id in alloc_ids_from_alloc(alloc).rev() { + if seen.insert(id) { + todo.push(id); + } + } + write_allocation(tcx, alloc, w) + }; + write!(w, "\n{}", id)?; + match tcx.get_global_alloc(id) { + // This can't really happen unless there are bugs, but it doesn't cost us anything to + // gracefully handle it and allow buggy rustc to be debugged via allocation printing. + None => write!(w, " (deallocated)")?, + Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?, + Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { + match tcx.const_eval_poly(did) { + Ok(ConstValue::ByRef { alloc, .. }) => { + write!(w, " (static: {}, ", tcx.def_path_str(did))?; + write_allocation_track_relocs(w, alloc)?; + } + Ok(_) => { + span_bug!(tcx.def_span(did), " static item without `ByRef` initializer") + } + Err(_) => write!( + w, + " (static: {}, error during initializer evaluation)", + tcx.def_path_str(did) + )?, + } + } + Some(GlobalAlloc::Static(did)) => { + write!(w, " (extern static: {})", tcx.def_path_str(did))? + } + Some(GlobalAlloc::Memory(alloc)) => { + write!(w, " (")?; + write_allocation_track_relocs(w, alloc)? + } + } + writeln!(w)?; + } + Ok(()) +} + +/// Dumps the size and metadata and content of an allocation to the given writer. +/// The expectation is that the caller first prints other relevant metadata, so the exact +/// format of this function is (*without* leading or trailing newline): +/// ``` +/// size: {}, align: {}) { +/// +/// } +/// ``` +/// +/// The byte format is similar to how hex editors print bytes. Each line starts with the address of +/// the start of the line, followed by all bytes in hex format (space separated). +/// If the allocation is small enough to fit into a single line, no start address is given. +/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control +/// characters or characters whose value is larger than 127) with a `.` +/// This also prints relocations adequately. +pub fn write_allocation( + tcx: TyCtxt<'tcx>, + alloc: &Allocation, + w: &mut dyn Write, +) -> io::Result<()> { + write!(w, "size: {}, align: {})", alloc.size.bytes(), alloc.align.bytes())?; + if alloc.size == Size::ZERO { + // We are done. + return write!(w, " {{}}"); + } + // Write allocation bytes. + writeln!(w, " {{")?; + write_allocation_bytes(tcx, alloc, w, " ")?; + write!(w, "}}")?; + Ok(()) +} + +fn write_allocation_endline(w: &mut dyn Write, ascii: &str) -> io::Result<()> { + for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) { + write!(w, " ")?; + } + writeln!(w, " │ {}", ascii) +} + +/// Number of bytes to print per allocation hex dump line. +const BYTES_PER_LINE: usize = 16; + +/// Prints the line start address and returns the new line start address. +fn write_allocation_newline( + w: &mut dyn Write, + mut line_start: Size, + ascii: &str, + pos_width: usize, + prefix: &str, +) -> io::Result { + write_allocation_endline(w, ascii)?; + line_start += Size::from_bytes(BYTES_PER_LINE); + write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?; + Ok(line_start) +} + +/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there +/// is only one line). Note that your prefix should contain a trailing space as the lines are +/// printed directly after it. +fn write_allocation_bytes( + tcx: TyCtxt<'tcx>, + alloc: &Allocation, + w: &mut dyn Write, + prefix: &str, +) -> io::Result<()> { + let num_lines = alloc.size.bytes_usize().saturating_sub(BYTES_PER_LINE); + // Number of chars needed to represent all line numbers. + let pos_width = format!("{:x}", alloc.size.bytes()).len(); + + if num_lines > 0 { + write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?; + } else { + write!(w, "{}", prefix)?; + } + + let mut i = Size::ZERO; + let mut line_start = Size::ZERO; + + let ptr_size = tcx.data_layout.pointer_size; + + let mut ascii = String::new(); + + let oversized_ptr = |target: &mut String, width| { + if target.len() > width { + write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap(); + } + }; + + while i < alloc.size { + // The line start already has a space. While we could remove that space from the line start + // printing and unconditionally print a space here, that would cause the single-line case + // to have a single space before it, which looks weird. + if i != line_start { + write!(w, " ")?; + } + if let Some(&(tag, target_id)) = alloc.relocations().get(&i) { + // Memory with a relocation must be defined + let j = i.bytes_usize(); + let offset = + alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize()); + let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); + let offset = Size::from_bytes(offset); + let relocation_width = |bytes| bytes * 3; + let ptr = Pointer::new_with_tag(target_id, offset, tag); + let mut target = format!("{:?}", ptr); + if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { + // This is too long, try to save some space. + target = format!("{:#?}", ptr); + } + if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE { + // This branch handles the situation where a relocation starts in the current line + // but ends in the next one. + let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start); + let overflow = ptr_size - remainder; + let remainder_width = relocation_width(remainder.bytes_usize()) - 2; + let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1; + ascii.push('╾'); + for _ in 0..remainder.bytes() - 1 { + ascii.push('─'); + } + if overflow_width > remainder_width && overflow_width >= target.len() { + // The case where the relocation fits into the part in the next line + write!(w, "╾{0:─^1$}", "", remainder_width)?; + line_start = + write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; + ascii.clear(); + write!(w, "{0:─^1$}╼", target, overflow_width)?; + } else { + oversized_ptr(&mut target, remainder_width); + write!(w, "╾{0:─^1$}", target, remainder_width)?; + line_start = + write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; + write!(w, "{0:─^1$}╼", "", overflow_width)?; + ascii.clear(); + } + for _ in 0..overflow.bytes() - 1 { + ascii.push('─'); + } + ascii.push('╼'); + i += ptr_size; + continue; + } else { + // This branch handles a relocation that starts and ends in the current line. + let relocation_width = relocation_width(ptr_size.bytes_usize() - 1); + oversized_ptr(&mut target, relocation_width); + ascii.push('╾'); + write!(w, "╾{0:─^1$}╼", target, relocation_width)?; + for _ in 0..ptr_size.bytes() - 2 { + ascii.push('─'); + } + ascii.push('╼'); + i += ptr_size; + } + } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() { + let j = i.bytes_usize(); + + // Checked definedness (and thus range) and relocations. This access also doesn't + // influence interpreter execution but is only for debugging. + let c = alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + 1)[0]; + write!(w, "{:02x}", c)?; + if c.is_ascii_control() || c >= 0x80 { + ascii.push('.'); + } else { + ascii.push(char::from(c)); + } + i += Size::from_bytes(1); + } else { + write!(w, "__")?; + ascii.push('░'); + i += Size::from_bytes(1); + } + // Print a new line header if the next line still has some bytes to print. + if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size { + line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; + ascii.clear(); + } + } + write_allocation_endline(w, &ascii)?; + + Ok(()) +} + fn write_mir_sig( tcx: TyCtxt<'_>, src: MirSource<'tcx>, @@ -545,17 +815,17 @@ fn write_mir_sig( trace!("write_mir_sig: {:?}", src.instance); let kind = tcx.def_kind(src.def_id()); let is_function = match kind { - Some(DefKind::Fn) | Some(DefKind::AssocFn) | Some(DefKind::Ctor(..)) => true, + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, _ => tcx.is_closure(src.def_id()), }; match (kind, src.promoted) { (_, Some(i)) => write!(w, "{:?} in ", i)?, - (Some(DefKind::Const), _) | (Some(DefKind::AssocConst), _) => write!(w, "const ")?, - (Some(DefKind::Static), _) => { + (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, + (DefKind::Static, _) => { write!(w, "static {}", if tcx.is_mutable_static(src.def_id()) { "mut " } else { "" })? } (_, _) if is_function => write!(w, "fn ")?, - (None, _) => {} // things like anon const, not an item + (DefKind::AnonConst, _) => {} // things like anon const, not an item _ => bug!("Unexpected def kind {:?}", kind), } @@ -606,5 +876,9 @@ fn write_user_type_annotations(body: &Body<'_>, w: &mut dyn Write) -> io::Result } pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { - if let Some(i) = single { vec![i] } else { tcx.mir_keys(LOCAL_CRATE).iter().cloned().collect() } + if let Some(i) = single { + vec![i] + } else { + tcx.mir_keys(LOCAL_CRATE).iter().map(|def_id| def_id.to_def_id()).collect() + } } diff --git a/src/librustc_mir/util/storage.rs b/src/librustc_mir/util/storage.rs new file mode 100644 index 0000000000000..0b7b1c29537f5 --- /dev/null +++ b/src/librustc_mir/util/storage.rs @@ -0,0 +1,47 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{self, Local, Location}; + +/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations. +/// +/// These locals have fixed storage for the duration of the body. +// +// FIXME: Currently, we need to traverse the entire MIR to compute this. We should instead store it +// as a field in the `LocalDecl` for each `Local`. +#[derive(Debug, Clone)] +pub struct AlwaysLiveLocals(BitSet); + +impl AlwaysLiveLocals { + pub fn new(body: &mir::Body<'tcx>) -> Self { + let mut ret = AlwaysLiveLocals(BitSet::new_filled(body.local_decls.len())); + + let mut vis = StorageAnnotationVisitor(&mut ret); + vis.visit_body(body); + + ret + } + + pub fn into_inner(self) -> BitSet { + self.0 + } +} + +impl std::ops::Deref for AlwaysLiveLocals { + type Target = BitSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// Removes locals that have `Storage*` annotations from `AlwaysLiveLocals`. +struct StorageAnnotationVisitor<'a>(&'a mut AlwaysLiveLocals); + +impl Visitor<'tcx> for StorageAnnotationVisitor<'_> { + fn visit_statement(&mut self, statement: &mir::Statement<'tcx>, _location: Location) { + use mir::StatementKind::{StorageDead, StorageLive}; + if let StorageLive(l) | StorageDead(l) = statement.kind { + (self.0).0.remove(l); + } + } +} diff --git a/src/librustc_mir_build/Cargo.toml b/src/librustc_mir_build/Cargo.toml index 96716dbd604d5..4a64cf74787ac 100644 --- a/src/librustc_mir_build/Cargo.toml +++ b/src/librustc_mir_build/Cargo.toml @@ -12,7 +12,7 @@ doctest = false [dependencies] arena = { path = "../libarena" } log = "0.4" -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_apfloat = { path = "../librustc_apfloat" } rustc_attr = { path = "../librustc_attr" } rustc_data_structures = { path = "../librustc_data_structures" } @@ -20,7 +20,6 @@ rustc_index = { path = "../librustc_index" } rustc_errors = { path = "../librustc_errors" } rustc_hir = { path = "../librustc_hir" } rustc_infer = { path = "../librustc_infer" } -rustc_macros = { path = "../librustc_macros" } rustc_serialize = { path = "../libserialize", package = "serialize" } rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } diff --git a/src/librustc_mir_build/build/block.rs b/src/librustc_mir_build/build/block.rs index df5526ad76281..fa783ddcf409a 100644 --- a/src/librustc_mir_build/build/block.rs +++ b/src/librustc_mir_build/build/block.rs @@ -2,14 +2,14 @@ use crate::build::matches::ArmHasGuard; use crate::build::ForGuard::OutsideGuard; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use crate::hair::*; -use rustc::mir::*; use rustc_hir as hir; +use rustc_middle::mir::*; use rustc_span::Span; impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn ast_block( &mut self, - destination: &Place<'tcx>, + destination: Place<'tcx>, block: BasicBlock, ast_block: &'tcx hir::Block<'tcx>, source_info: SourceInfo, @@ -26,14 +26,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| { this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { if targeted_by_break { - // This is a `break`-able block - let exit_block = this.cfg.start_new_block(); - let block_exit = - this.in_breakable_scope(None, exit_block, destination.clone(), |this| { - this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) - }); - this.cfg.goto(unpack!(block_exit), source_info, exit_block); - exit_block.unit() + this.in_breakable_scope(None, destination, span, |this| { + Some(this.ast_block_stmts( + destination, + block, + span, + stmts, + expr, + safety_mode, + )) + }) } else { this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) } @@ -43,7 +45,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn ast_block_stmts( &mut self, - destination: &Place<'tcx>, + destination: Place<'tcx>, mut block: BasicBlock, span: Span, stmts: Vec>, @@ -145,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { })); debug!("ast_block_stmts: pattern={:?}", pattern); - this.visit_bindings( + this.visit_primary_bindings( &pattern, UserTypeProjections::none(), &mut |this, _, _, _, node, span, _, _| { @@ -173,7 +175,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(expr) = expr { let tail_result_is_ignored = destination_ty.is_unit() || this.block_context.currently_ignores_tail_results(); - this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored }); + let span = match expr { + ExprRef::Hair(expr) => expr.span, + ExprRef::Mirror(ref expr) => expr.span, + }; + this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored, span }); unpack!(block = this.into(destination, block, expr)); let popped = this.block_context.pop(); @@ -187,7 +193,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if destination_ty.is_unit() { // We only want to assign an implicit `()` as the return value of the block if the // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.) - this.cfg.push_assign_unit(block, source_info, destination); + this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); } } // Finally, we pop all the let scopes before exiting out from the scope of block diff --git a/src/librustc_mir_build/build/cfg.rs b/src/librustc_mir_build/build/cfg.rs index 883aba18ec547..42e2b242d7726 100644 --- a/src/librustc_mir_build/build/cfg.rs +++ b/src/librustc_mir_build/build/cfg.rs @@ -1,7 +1,8 @@ //! Routines for manipulating the control-flow graph. use crate::build::CFG; -use rustc::mir::*; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; impl<'tcx> CFG<'tcx> { crate fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { @@ -34,12 +35,12 @@ impl<'tcx> CFG<'tcx> { &mut self, block: BasicBlock, source_info: SourceInfo, - place: &Place<'tcx>, + place: Place<'tcx>, rvalue: Rvalue<'tcx>, ) { self.push( block, - Statement { source_info, kind: StatementKind::Assign(box (*place, rvalue)) }, + Statement { source_info, kind: StatementKind::Assign(box (place, rvalue)) }, ); } @@ -47,7 +48,7 @@ impl<'tcx> CFG<'tcx> { &mut self, block: BasicBlock, source_info: SourceInfo, - temp: &Place<'tcx>, + temp: Place<'tcx>, constant: Constant<'tcx>, ) { self.push_assign(block, source_info, temp, Rvalue::Use(Operand::Constant(box constant))); @@ -57,13 +58,18 @@ impl<'tcx> CFG<'tcx> { &mut self, block: BasicBlock, source_info: SourceInfo, - place: &Place<'tcx>, + place: Place<'tcx>, + tcx: TyCtxt<'tcx>, ) { self.push_assign( block, source_info, place, - Rvalue::Aggregate(box AggregateKind::Tuple, vec![]), + Rvalue::Use(Operand::Constant(box Constant { + span: source_info.span, + user_ty: None, + literal: ty::Const::zero_sized(tcx, tcx.types.unit), + })), ); } diff --git a/src/librustc_mir_build/build/expr/as_constant.rs b/src/librustc_mir_build/build/expr/as_constant.rs index e4856262975e7..03ec0b48f8bfe 100644 --- a/src/librustc_mir_build/build/expr/as_constant.rs +++ b/src/librustc_mir_build/build/expr/as_constant.rs @@ -2,8 +2,8 @@ use crate::build::Builder; use crate::hair::*; -use rustc::mir::*; -use rustc::ty::CanonicalUserTypeAnnotation; +use rustc_middle::mir::*; +use rustc_middle::ty::CanonicalUserTypeAnnotation; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, yielding a compile-time constant. Assumes that diff --git a/src/librustc_mir_build/build/expr/as_operand.rs b/src/librustc_mir_build/build/expr/as_operand.rs index efe328d2b3c1e..9a75f3afe8f08 100644 --- a/src/librustc_mir_build/build/expr/as_operand.rs +++ b/src/librustc_mir_build/build/expr/as_operand.rs @@ -3,16 +3,17 @@ use crate::build::expr::category::Category; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::hair::*; -use rustc::middle::region; -use rustc::mir::*; +use rustc_middle::middle::region; +use rustc_middle::mir::*; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Returns an operand suitable for use until the end of the current /// scope expression. /// - /// The operand returned from this function will *not be valid* after - /// an ExprKind::Scope is passed, so please do *not* return it from - /// functions to avoid bad miscompiles. + /// The operand returned from this function will *not be valid* + /// after the current enclosing `ExprKind::Scope` has ended, so + /// please do *not* return it from functions to avoid bad + /// miscompiles. crate fn as_local_operand(&mut self, block: BasicBlock, expr: M) -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>>, @@ -21,6 +22,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.as_operand(block, local_scope, expr) } + /// Returns an operand suitable for use until the end of the current scope expression and + /// suitable also to be passed as function arguments. + /// + /// The operand returned from this function will *not be valid* after an ExprKind::Scope is + /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an + /// operand suitable for use as a call argument. This is almost always equivalent to + /// `as_operand`, except for the particular case of passing values of (potentially) unsized + /// types "by value" (see details below). + /// + /// The operand returned from this function will *not be valid* + /// after the current enclosing `ExprKind::Scope` has ended, so + /// please do *not* return it from functions to avoid bad + /// miscompiles. + /// + /// # Parameters of unsized types + /// + /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a + /// local variable of unsized type. For example, consider this program: + /// + /// ```rust + /// fn foo(p: dyn Debug) { ... } + /// + /// fn bar(box_p: Box) { foo(*p); } + /// ``` + /// + /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so: + /// + /// ```rust + /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call + /// foo(tmp0) + /// ``` + /// + /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is + /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0` + /// that we create *stores the entire box*, and the parameter to the call itself will be + /// `*tmp0`: + /// + /// ```rust + /// let tmp0 = box_p; call foo(*tmp0) + /// ``` + /// + /// This way, the temporary `tmp0` that we create has type `Box`, which is sized. + /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that + /// calls are compiled means that this parameter will be passed "by reference", meaning that we + /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug` + /// value to the stack. + /// + /// See #68034 for more details. + crate fn as_local_call_operand( + &mut self, + block: BasicBlock, + expr: M, + ) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, + { + let local_scope = self.local_scope(); + self.as_call_operand(block, local_scope, expr) + } + /// Compile `expr` into a value that can be used as an operand. /// If `expr` is a place like `x`, this will introduce a /// temporary `tmp = x`, so that we capture the value of `x` at @@ -40,6 +101,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.expr_as_operand(block, scope, expr) } + /// Like `as_local_call_operand`, except that the argument will + /// not be valid once `scope` ends. + fn as_call_operand( + &mut self, + block: BasicBlock, + scope: Option, + expr: M, + ) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, + { + let expr = self.hir.mirror(expr); + self.expr_as_call_operand(block, scope, expr) + } + fn expr_as_operand( &mut self, mut block: BasicBlock, @@ -69,4 +145,54 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } + + fn expr_as_call_operand( + &mut self, + mut block: BasicBlock, + scope: Option, + expr: Expr<'tcx>, + ) -> BlockAnd> { + debug!("expr_as_call_operand(block={:?}, expr={:?})", block, expr); + let this = self; + + if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { + let source_info = this.source_info(expr.span); + let region_scope = (region_scope, source_info); + return this.in_scope(region_scope, lint_level, |this| { + this.as_call_operand(block, scope, value) + }); + } + + let tcx = this.hir.tcx(); + + if tcx.features().unsized_locals { + let ty = expr.ty; + let span = expr.span; + let param_env = this.hir.param_env; + + if !ty.is_sized(tcx.at(span), param_env) { + // !sized means !copy, so this is an unsized move + assert!(!ty.is_copy_modulo_regions(tcx, param_env, span)); + + // As described above, detect the case where we are passing a value of unsized + // type, and that value is coming from the deref of a box. + if let ExprKind::Deref { ref arg } = expr.kind { + let arg = this.hir.mirror(arg.clone()); + + // Generate let tmp0 = arg0 + let operand = unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut)); + + // Return the operand *tmp0 to be used as the call argument + let place = Place { + local: operand, + projection: tcx.intern_place_elems(&[PlaceElem::Deref]), + }; + + return block.and(Operand::Move(place)); + } + } + } + + this.expr_as_operand(block, scope, expr) + } } diff --git a/src/librustc_mir_build/build/expr/as_place.rs b/src/librustc_mir_build/build/expr/as_place.rs index d77cc49c94f67..16bba6ad14780 100644 --- a/src/librustc_mir_build/build/expr/as_place.rs +++ b/src/librustc_mir_build/build/expr/as_place.rs @@ -4,10 +4,10 @@ use crate::build::expr::category::Category; use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::hair::*; -use rustc::middle::region; -use rustc::mir::AssertKind::BoundsCheck; -use rustc::mir::*; -use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance}; +use rustc_middle::middle::region; +use rustc_middle::mir::AssertKind::BoundsCheck; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance}; use rustc_span::Span; use rustc_index::vec::Idx; @@ -256,6 +256,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Literal { .. } | ExprKind::StaticRef { .. } | ExprKind::InlineAsm { .. } + | ExprKind::LlvmInlineAsm { .. } | ExprKind::Yield { .. } | ExprKind::Call { .. } => { // these are not places, so we need to make a temporary. @@ -341,12 +342,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let lt = self.temp(bool_ty, expr_span); // len = len(slice) - self.cfg.push_assign(block, source_info, &len, Rvalue::Len(slice)); + self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice)); // lt = idx < len self.cfg.push_assign( block, source_info, - <, + lt, Rvalue::BinaryOp(BinOp::Lt, Operand::Copy(Place::from(index)), Operand::Copy(len)), ); let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) }; @@ -383,12 +384,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty); let fake_borrow_temp = - self.local_decls.push(LocalDecl::new_temp(fake_borrow_ty, expr_span)); + self.local_decls.push(LocalDecl::new(fake_borrow_ty, expr_span)); let projection = tcx.intern_place_elems(&base_place.projection[..idx]); self.cfg.push_assign( block, source_info, - &fake_borrow_temp.into(), + fake_borrow_temp.into(), Rvalue::Ref( tcx.lifetimes.re_erased, BorrowKind::Shallow, diff --git a/src/librustc_mir_build/build/expr/as_rvalue.rs b/src/librustc_mir_build/build/expr/as_rvalue.rs index dc97f321a36ad..0f15190975087 100644 --- a/src/librustc_mir_build/build/expr/as_rvalue.rs +++ b/src/librustc_mir_build/build/expr/as_rvalue.rs @@ -5,10 +5,10 @@ use rustc_index::vec::Idx; use crate::build::expr::category::{Category, RvalueFunc}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::hair::*; -use rustc::middle::region; -use rustc::mir::AssertKind; -use rustc::mir::*; -use rustc::ty::{self, Ty, UpvarSubsts}; +use rustc_middle::middle::region; +use rustc_middle::mir::AssertKind; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, Ty, UpvarSubsts}; use rustc_span::Span; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -78,7 +78,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign( block, source_info, - &is_min, + is_min, Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval), ); @@ -97,7 +97,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // The `Box` temporary created here is not a part of the HIR, // and therefore is not considered during generator OIBIT // determination. See the comment about `box` at `yield_in_scope`. - let result = this.local_decls.push(LocalDecl::new_internal(expr.ty, expr_span)); + let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span).internal()); this.cfg.push( block, Statement { source_info, kind: StatementKind::StorageLive(result) }, @@ -109,15 +109,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // malloc some memory of suitable type (thus far, uninitialized): let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty); - this.cfg.push_assign(block, source_info, &Place::from(result), box_); + this.cfg.push_assign(block, source_info, Place::from(result), box_); // initialize the box contents: unpack!( - block = this.into( - &this.hir.tcx().mk_place_deref(Place::from(result)), - block, - value - ) + block = + this.into(this.hir.tcx().mk_place_deref(Place::from(result)), block, value) ); block.and(Rvalue::Use(Operand::Move(Place::from(result)))) } @@ -228,7 +225,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { block = unpack!(this.stmt_expr(block, expr, None)); - block.and(this.unit_rvalue()) + block.and(Rvalue::Use(Operand::Constant(box Constant { + span: expr_span, + user_ty: None, + literal: ty::Const::zero_sized(this.hir.tcx(), this.hir.tcx().types.unit), + }))) } ExprKind::Yield { .. } | ExprKind::Literal { .. } @@ -252,6 +253,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Continue { .. } | ExprKind::Return { .. } | ExprKind::InlineAsm { .. } + | ExprKind::LlvmInlineAsm { .. } | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } => { // these do not have corresponding `Rvalue` variants, @@ -284,14 +286,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign( block, source_info, - &result_value, + result_value, Rvalue::CheckedBinaryOp(op, lhs, rhs), ); let val_fld = Field::new(0); let of_fld = Field::new(1); let tcx = self.hir.tcx(); - let val = tcx.mk_place_field(result_value.clone(), val_fld, ty); + let val = tcx.mk_place_field(result_value, val_fld, ty); let of = tcx.mk_place_field(result_value, of_fld, bool_ty); let err = AssertKind::Overflow(op); @@ -317,7 +319,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign( block, source_info, - &is_zero, + is_zero, Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero), ); @@ -338,13 +340,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign( block, source_info, - &is_neg_1, + is_neg_1, Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1), ); self.cfg.push_assign( block, source_info, - &is_min, + is_min, Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min), ); @@ -353,7 +355,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign( block, source_info, - &of, + of, Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min), ); @@ -376,7 +378,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let this = self; let source_info = this.source_info(upvar_span); - let temp = this.local_decls.push(LocalDecl::new_temp(upvar_ty, upvar_span)); + let temp = this.local_decls.push(LocalDecl::new(upvar_ty, upvar_span)); this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) }); @@ -428,7 +430,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign( block, source_info, - &Place::from(temp), + Place::from(temp), Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place), ); diff --git a/src/librustc_mir_build/build/expr/as_temp.rs b/src/librustc_mir_build/build/expr/as_temp.rs index 82183e6c96e9f..d82abd8776719 100644 --- a/src/librustc_mir_build/build/expr/as_temp.rs +++ b/src/librustc_mir_build/build/expr/as_temp.rs @@ -3,10 +3,10 @@ use crate::build::scope::DropKind; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::hair::*; -use rustc::middle::region; -use rustc::mir::*; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; -use rustc_span::symbol::sym; +use rustc_middle::middle::region; +use rustc_middle::mir::*; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr` into a fresh temporary. This is used when building @@ -22,7 +22,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); - self.expr_as_temp(block, temp_lifetime, expr, mutability) + // + // this is the only place in mir building that we need to truly need to worry about + // infinite recursion. Everything else does recurse, too, but it always gets broken up + // at some point by inserting an intermediate temporary + ensure_sufficient_stack(|| self.expr_as_temp(block, temp_lifetime, expr, mutability)) } fn expr_as_temp( @@ -48,7 +52,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_ty = expr.ty; let temp = { - let mut local_decl = LocalDecl::new_temp(expr_ty, expr_span); + let mut local_decl = LocalDecl::new(expr_ty, expr_span); if mutability == Mutability::Not { local_decl = local_decl.immutable(); } @@ -60,13 +64,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { local_decl = local_decl.block_tail(tail_info); } if let ExprKind::StaticRef { def_id, .. } = expr.kind { - let is_thread_local = this.hir.tcx().has_attr(def_id, sym::thread_local); + let is_thread_local = this.hir.tcx().is_thread_local_static(def_id); local_decl.internal = true; - local_decl.local_info = LocalInfo::StaticRef { def_id, is_thread_local }; + local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local }); } this.local_decls.push(local_decl) }; - let temp_place = &Place::from(temp); + let temp_place = Place::from(temp); match expr.kind { // Don't bother with StorageLive and Dead for these temporaries, diff --git a/src/librustc_mir_build/build/expr/category.rs b/src/librustc_mir_build/build/expr/category.rs index cc139dee63f92..59d3003c9f49a 100644 --- a/src/librustc_mir_build/build/expr/category.rs +++ b/src/librustc_mir_build/build/expr/category.rs @@ -51,7 +51,8 @@ impl Category { | ExprKind::Borrow { .. } | ExprKind::AddressOf { .. } | ExprKind::Yield { .. } - | ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)), + | ExprKind::Call { .. } + | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)), ExprKind::Array { .. } | ExprKind::Tuple { .. } @@ -64,7 +65,7 @@ impl Category { | ExprKind::Repeat { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } - | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::LlvmInlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => Some(Category::Constant), diff --git a/src/librustc_mir_build/build/expr/into.rs b/src/librustc_mir_build/build/expr/into.rs index 4583e244f493d..ff3c7ee3ee823 100644 --- a/src/librustc_mir_build/build/expr/into.rs +++ b/src/librustc_mir_build/build/expr/into.rs @@ -3,10 +3,11 @@ use crate::build::expr::category::{Category, RvalueFunc}; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use crate::hair::*; -use rustc::mir::*; -use rustc::ty::{self, CanonicalUserTypeAnnotation}; +use rustc_ast::ast::InlineAsmOptions; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation}; use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; @@ -16,7 +17,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// is assumed to be uninitialized. crate fn into_expr( &mut self, - destination: &Place<'tcx>, + destination: Place<'tcx>, mut block: BasicBlock, expr: Expr<'tcx>, ) -> BlockAnd<()> { @@ -53,7 +54,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::NeverToAny { source } => { let source = this.hir.mirror(source); let is_call = match source.kind { - ExprKind::Call { .. } => true, + ExprKind::Call { .. } | ExprKind::InlineAsm { .. } => true, _ => false, }; @@ -134,37 +135,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // body, even when the exact code in the body cannot unwind let loop_block = this.cfg.start_new_block(); - let exit_block = this.cfg.start_new_block(); // Start the loop. this.cfg.goto(block, source_info, loop_block); - this.in_breakable_scope( - Some(loop_block), - exit_block, - destination.clone(), - move |this| { - // conduct the test, if necessary - let body_block = this.cfg.start_new_block(); - let diverge_cleanup = this.diverge_cleanup(); - this.cfg.terminate( - loop_block, - source_info, - TerminatorKind::FalseUnwind { - real_target: body_block, - unwind: Some(diverge_cleanup), - }, - ); - - // The “return” value of the loop body must always be an unit. We therefore - // introduce a unit temporary as the destination for the loop body. - let tmp = this.get_unit_temp(); - // Execute the body, branching back to the test. - let body_block_end = unpack!(this.into(&tmp, body_block, body)); - this.cfg.goto(body_block_end, source_info, loop_block); - }, - ); - exit_block.unit() + this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| { + // conduct the test, if necessary + let body_block = this.cfg.start_new_block(); + this.cfg.terminate( + loop_block, + source_info, + TerminatorKind::FalseUnwind { real_target: body_block, unwind: None }, + ); + this.diverge_from(loop_block); + + // The “return” value of the loop body must always be an unit. We therefore + // introduce a unit temporary as the destination for the loop body. + let tmp = this.get_unit_temp(); + // Execute the body, branching back to the test. + let body_block_end = unpack!(this.into(tmp, body_block, body)); + this.cfg.goto(body_block_end, source_info, loop_block); + + // Loops are only exited by `break` expressions. + None + }) } ExprKind::Call { ty, fun, args, from_hir_call } => { let intrinsic = match ty.kind { @@ -192,26 +186,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let ptr_ty = ptr.ty; // Create an *internal* temp for the pointer, so that unsafety // checking won't complain about the raw pointer assignment. - let ptr_temp = this.local_decls.push(LocalDecl { - mutability: Mutability::Mut, - ty: ptr_ty, - user_ty: UserTypeProjections::none(), + let ptr_temp = this.local_decls.push(LocalDecl::with_source_info( + ptr_ty, source_info, - internal: true, - local_info: LocalInfo::Other, - is_block_tail: None, - }); + ).internal()); let ptr_temp = Place::from(ptr_temp); - let block = unpack!(this.into(&ptr_temp, block, ptr)); - this.into(&this.hir.tcx().mk_place_deref(ptr_temp), block, val) + let block = unpack!(this.into(ptr_temp, block, ptr)); + this.into(this.hir.tcx().mk_place_deref(ptr_temp), block, val) } else { let args: Vec<_> = args .into_iter() - .map(|arg| unpack!(block = this.as_local_operand(block, arg))) + .map(|arg| unpack!(block = this.as_local_call_operand(block, arg))) .collect(); let success = this.cfg.start_new_block(); - let cleanup = this.diverge_cleanup(); this.record_operands_moved(&args); @@ -221,18 +209,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TerminatorKind::Call { func: fun, args, - cleanup: Some(cleanup), + cleanup: None, // FIXME(varkor): replace this with an uninhabitedness-based check. // This requires getting access to the current module to call // `tcx.is_ty_uninhabited_from`, which is currently tricky to do. destination: if expr.ty.is_never() { None } else { - Some((*destination, success)) + Some((destination, success)) }, from_hir_call, }, ); + this.diverge_from(block); success.unit() } } @@ -278,26 +267,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let field_names = this.hir.all_fields(adt_def, variant_index); - let fields = - if let Some(FruInfo { base, field_types }) = base { - let base = unpack!(block = this.as_place(block, base)); - - // MIR does not natively support FRU, so for each - // base-supplied field, generate an operand that - // reads it from the base. - field_names - .into_iter() - .zip(field_types.into_iter()) - .map(|(n, ty)| match fields_map.get(&n) { - Some(v) => v.clone(), - None => this.consume_by_copy_or_move( - this.hir.tcx().mk_place_field(base.clone(), n, ty), - ), - }) - .collect() - } else { - field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect() - }; + let fields = if let Some(FruInfo { base, field_types }) = base { + let base = unpack!(block = this.as_place(block, base)); + + // MIR does not natively support FRU, so for each + // base-supplied field, generate an operand that + // reads it from the base. + field_names + .into_iter() + .zip(field_types.into_iter()) + .map(|(n, ty)| match fields_map.get(&n) { + Some(v) => v.clone(), + None => this.consume_by_copy_or_move( + this.hir.tcx().mk_place_field(base, n, ty), + ), + }) + .collect() + } else { + field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect() + }; let inferred_ty = expr.ty; let user_ty = user_ty.map(|ty| { @@ -322,16 +310,83 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.unit() } + ExprKind::InlineAsm { template, operands, options } => { + use crate::hair; + use rustc_middle::mir; + let operands = operands + .into_iter() + .map(|op| match op { + hair::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In { + reg, + value: unpack!(block = this.as_local_operand(block, expr)), + }, + hair::InlineAsmOperand::Out { reg, late, expr } => { + mir::InlineAsmOperand::Out { + reg, + late, + place: expr.map(|expr| unpack!(block = this.as_place(block, expr))), + } + } + hair::InlineAsmOperand::InOut { reg, late, expr } => { + let place = unpack!(block = this.as_place(block, expr)); + mir::InlineAsmOperand::InOut { + reg, + late, + // This works because asm operands must be Copy + in_value: Operand::Copy(place), + out_place: Some(place), + } + } + hair::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { + mir::InlineAsmOperand::InOut { + reg, + late, + in_value: unpack!(block = this.as_local_operand(block, in_expr)), + out_place: out_expr.map(|out_expr| { + unpack!(block = this.as_place(block, out_expr)) + }), + } + } + hair::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const { + value: unpack!(block = this.as_local_operand(block, expr)), + }, + hair::InlineAsmOperand::SymFn { expr } => { + mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) } + } + hair::InlineAsmOperand::SymStatic { expr } => { + mir::InlineAsmOperand::SymStatic { value: box this.as_constant(expr) } + } + }) + .collect(); + + let destination = this.cfg.start_new_block(); + + this.cfg.terminate( + block, + source_info, + TerminatorKind::InlineAsm { + template, + operands, + options, + destination: if options.contains(InlineAsmOptions::NORETURN) { + None + } else { + Some(destination) + }, + }, + ); + destination.unit() + } // These cases don't actually need a destination ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::Continue { .. } | ExprKind::Break { .. } - | ExprKind::InlineAsm { .. } + | ExprKind::LlvmInlineAsm { .. } | ExprKind::Return { .. } => { unpack!(block = this.stmt_expr(block, expr, None)); - this.cfg.push_assign_unit(block, source_info, destination); + this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); block.unit() } @@ -354,7 +409,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // value is Sized. Usually, this is caught in type checking, but // in the case of box expr there is no such check. if !destination.projection.is_empty() { - this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span)); + this.local_decls.push(LocalDecl::new(expr.ty, expr.span)); } debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); @@ -369,17 +424,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scope = this.local_scope(); let value = unpack!(block = this.as_operand(block, scope, value)); let resume = this.cfg.start_new_block(); - let cleanup = this.generator_drop_cleanup(); this.cfg.terminate( block, source_info, - TerminatorKind::Yield { - value, - resume, - resume_arg: *destination, - drop: cleanup, - }, + TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None }, ); + this.generator_drop_cleanup(block); resume.unit() } diff --git a/src/librustc_mir_build/build/expr/stmt.rs b/src/librustc_mir_build/build/expr/stmt.rs index 882c5e85bb0ac..49d6ce39ddfa4 100644 --- a/src/librustc_mir_build/build/expr/stmt.rs +++ b/src/librustc_mir_build/build/expr/stmt.rs @@ -1,8 +1,8 @@ use crate::build::scope::BreakableTarget; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use crate::hair::*; -use rustc::middle::region; -use rustc::mir::*; +use rustc_middle::middle::region; +use rustc_middle::mir::*; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Builds a block of MIR statements to evaluate the HAIR `expr`. @@ -50,7 +50,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); let lhs = unpack!(block = this.as_place(block, lhs)); - this.cfg.push_assign(block, source_info, &lhs, rhs); + this.cfg.push_assign(block, source_info, lhs, rhs); } this.block_context.pop(); @@ -82,7 +82,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block = this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs) ); - this.cfg.push_assign(block, source_info, &lhs, result); + this.cfg.push_assign(block, source_info, lhs, result); this.block_context.pop(); block.unit() @@ -96,8 +96,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Return { value } => { this.break_scope(block, value, BreakableTarget::Return, source_info) } - ExprKind::InlineAsm { asm, outputs, inputs } => { - debug!("stmt_expr InlineAsm block_context.push(SubExpr) : {:?}", expr2); + ExprKind::LlvmInlineAsm { asm, outputs, inputs } => { + debug!("stmt_expr LlvmInlineAsm block_context.push(SubExpr) : {:?}", expr2); this.block_context.push(BlockFrame::SubExpr); let outputs = outputs .into_iter() @@ -115,7 +115,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, Statement { source_info, - kind: StatementKind::InlineAsm(box InlineAsm { + kind: StatementKind::LlvmInlineAsm(box LlvmInlineAsm { asm: asm.clone(), outputs, inputs, @@ -151,7 +151,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } this.block_context - .push(BlockFrame::TailExpr { tail_result_is_ignored: true }); + .push(BlockFrame::TailExpr { tail_result_is_ignored: true, span: expr.span }); return Some(expr.span); } } diff --git a/src/librustc_mir_build/build/into.rs b/src/librustc_mir_build/build/into.rs index 1a2a9d2bc05fc..0baa0c833a514 100644 --- a/src/librustc_mir_build/build/into.rs +++ b/src/librustc_mir_build/build/into.rs @@ -6,13 +6,13 @@ use crate::build::{BlockAnd, Builder}; use crate::hair::*; -use rustc::mir::*; +use rustc_middle::mir::*; pub(in crate::build) trait EvalInto<'tcx> { fn eval_into( self, builder: &mut Builder<'_, 'tcx>, - destination: &Place<'tcx>, + destination: Place<'tcx>, block: BasicBlock, ) -> BlockAnd<()>; } @@ -20,7 +20,7 @@ pub(in crate::build) trait EvalInto<'tcx> { impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn into( &mut self, - destination: &Place<'tcx>, + destination: Place<'tcx>, block: BasicBlock, expr: E, ) -> BlockAnd<()> @@ -35,7 +35,7 @@ impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> { fn eval_into( self, builder: &mut Builder<'_, 'tcx>, - destination: &Place<'tcx>, + destination: Place<'tcx>, block: BasicBlock, ) -> BlockAnd<()> { let expr = builder.hir.mirror(self); @@ -47,7 +47,7 @@ impl<'tcx> EvalInto<'tcx> for Expr<'tcx> { fn eval_into( self, builder: &mut Builder<'_, 'tcx>, - destination: &Place<'tcx>, + destination: Place<'tcx>, block: BasicBlock, ) -> BlockAnd<()> { builder.into_expr(destination, block, self) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index e7c7e2b47f28f..1071a4c97df65 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -10,16 +10,16 @@ use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; use crate::hair::{self, *}; -use rustc::middle::region; -use rustc::mir::*; -use rustc::ty::layout::VariantIdx; -use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::HirId; use rustc_index::bit_set::BitSet; +use rustc_middle::middle::region; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc_span::Span; +use rustc_span::symbol::Symbol; +use rustc_target::abi::VariantIdx; use smallvec::{smallvec, SmallVec}; -use rustc_ast::ast::Name; // helper functions, broken out by category: mod simplify; @@ -83,7 +83,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// * From each otherwise block to the next prebinding block. crate fn match_expr( &mut self, - destination: &Place<'tcx>, + destination: Place<'tcx>, span: Span, mut block: BasicBlock, scrutinee: ExprRef<'tcx>, @@ -218,15 +218,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// `outer_source_info` is the SourceInfo for the whole match. fn lower_match_arms( &mut self, - destination: &Place<'tcx>, + destination: Place<'tcx>, scrutinee_place: Place<'tcx>, scrutinee_span: Span, arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>, outer_source_info: SourceInfo, fake_borrow_temps: Vec<(Place<'tcx>, Local)>, ) -> BlockAnd<()> { - let match_scope = self.scopes.topmost(); - let arm_end_blocks: Vec<_> = arm_candidates .into_iter() .map(|(arm, candidate)| { @@ -247,7 +245,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_block = this.bind_pattern( outer_source_info, candidate, - arm.guard.as_ref().map(|g| (g, match_scope)), + arm.guard.as_ref(), &fake_borrow_temps, scrutinee_span, Some(arm.scope), @@ -284,7 +282,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, outer_source_info: SourceInfo, candidate: Candidate<'_, 'tcx>, - guard: Option<(&Guard<'tcx>, region::Scope)>, + guard: Option<&Guard<'tcx>>, fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, arm_scope: Option, @@ -364,7 +362,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); - unpack!(block = self.into(&place, block, initializer)); + unpack!(block = self.into(place, block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let source_info = self.source_info(irrefutable_pat.span); @@ -399,7 +397,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); - unpack!(block = self.into(&place, block, initializer)); + unpack!(block = self.into(place, block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let pattern_source_info = self.source_info(irrefutable_pat.span); @@ -470,9 +468,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for binding in &candidate_ref.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); - if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, - ))) = self.local_decls[local].local_info + )))) = self.local_decls[local].local_info { *match_place = Some(initializer); } else { @@ -511,7 +509,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { debug!("declare_bindings: pattern={:?}", pattern); - self.visit_bindings( + self.visit_primary_bindings( &pattern, UserTypeProjections::none(), &mut |this, mutability, name, mode, var, span, ty, user_ty| { @@ -563,14 +561,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.schedule_drop(span, region_scope, local_id, DropKind::Value); } - pub(super) fn visit_bindings( + /// Visit all of the primary bindings in a patterns, that is, visit the + /// leftmost occurrence of each variable bound in a pattern. A variable + /// will occur more than once in an or-pattern. + pub(super) fn visit_primary_bindings( &mut self, pattern: &Pat<'tcx>, pattern_user_ty: UserTypeProjections, f: &mut impl FnMut( &mut Self, Mutability, - Name, + Symbol, BindingMode, HirId, Span, @@ -578,12 +579,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { UserTypeProjections, ), ) { - debug!("visit_bindings: pattern={:?} pattern_user_ty={:?}", pattern, pattern_user_ty); + debug!( + "visit_primary_bindings: pattern={:?} pattern_user_ty={:?}", + pattern, pattern_user_ty + ); match *pattern.kind { - PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, .. } => { - f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); + PatKind::Binding { + mutability, + name, + mode, + var, + ty, + ref subpattern, + is_primary, + .. + } => { + if is_primary { + f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); + } if let Some(subpattern) = subpattern.as_ref() { - self.visit_bindings(subpattern, pattern_user_ty, f); + self.visit_primary_bindings(subpattern, pattern_user_ty, f); } } @@ -592,20 +607,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let from = u32::try_from(prefix.len()).unwrap(); let to = u32::try_from(suffix.len()).unwrap(); for subpattern in prefix { - self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f); + self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); } for subpattern in slice { - self.visit_bindings(subpattern, pattern_user_ty.clone().subslice(from, to), f); + self.visit_primary_bindings( + subpattern, + pattern_user_ty.clone().subslice(from, to), + f, + ); } for subpattern in suffix { - self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f); + self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); } } PatKind::Constant { .. } | PatKind::Range { .. } | PatKind::Wild => {} PatKind::Deref { ref subpattern } => { - self.visit_bindings(subpattern, pattern_user_ty.deref(), f); + self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f); } PatKind::AscribeUserType { @@ -630,14 +649,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { projs: Vec::new(), }; let subpattern_user_ty = pattern_user_ty.push_projection(&projection, user_ty_span); - self.visit_bindings(subpattern, subpattern_user_ty, f) + self.visit_primary_bindings(subpattern, subpattern_user_ty, f) } PatKind::Leaf { ref subpatterns } => { for subpattern in subpatterns { let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field); - debug!("visit_bindings: subpattern_user_ty={:?}", subpattern_user_ty); - self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f); + debug!("visit_primary_bindings: subpattern_user_ty={:?}", subpattern_user_ty); + self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); } } @@ -645,11 +664,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for subpattern in subpatterns { let subpattern_user_ty = pattern_user_ty.clone().variant(adt_def, variant_index, subpattern.field); - self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f); + self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); } } PatKind::Or { ref pats } => { - self.visit_bindings(&pats[0], pattern_user_ty, f); + // In cases where we recover from errors the primary bindings + // may not all be in the leftmost subpattern. For example in + // `let (x | y) = ...`, the primary binding of `y` occurs in + // the right subpattern + for subpattern in pats { + self.visit_primary_bindings(subpattern, pattern_user_ty.clone(), f); + } } } } @@ -737,7 +762,7 @@ fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( struct Binding<'tcx> { span: Span, source: Place<'tcx>, - name: Name, + name: Symbol, var_id: HirId, var_ty: Ty<'tcx>, mutability: Mutability, @@ -1388,7 +1413,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // Insert a Shallow borrow of any places that is switched on. - fake_borrows.as_mut().map(|fb| fb.insert(match_place)); + if let Some(fb) = fake_borrows { + fb.insert(match_place); + } // perform the test, branching to one of N blocks. For each of // those N possible outcomes, create a (initially empty) @@ -1537,7 +1564,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty; let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty); let fake_borrow_temp = - self.local_decls.push(LocalDecl::new_temp(fake_borrow_ty, temp_span)); + self.local_decls.push(LocalDecl::new(fake_borrow_ty, temp_span)); (matched_place, fake_borrow_temp) }) @@ -1561,7 +1588,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, candidate: Candidate<'pat, 'tcx>, parent_bindings: &[(Vec>, Vec>)], - guard: Option<(&Guard<'tcx>, region::Scope)>, + guard: Option<&Guard<'tcx>>, fake_borrows: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, schedule_drops: bool, @@ -1673,7 +1700,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the reference that we create for the arm. // * So we eagerly create the reference for the arm and then take a // reference to that. - if let Some((guard, region_scope)) = guard { + if let Some(guard) = guard { let tcx = self.hir.tcx(); let bindings = parent_bindings .iter() @@ -1691,7 +1718,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scrutinee_source_info = self.source_info(scrutinee_span); for &(place, temp) in fake_borrows { let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, place); - self.cfg.push_assign(block, scrutinee_source_info, &Place::from(temp), borrow); + self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); } // the block to branch to if the guard fails; if there is no @@ -1717,12 +1744,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unreachable }); let outside_scope = self.cfg.start_new_block(); - self.exit_scope( - source_info.span, - region_scope, - otherwise_post_guard_block, - outside_scope, - ); + self.exit_top_scope(otherwise_post_guard_block, outside_scope, source_info); self.false_edges( outside_scope, otherwise_block, @@ -1858,7 +1880,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match binding.binding_mode { BindingMode::ByValue => { let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); - self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue); + self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } BindingMode::ByRef(borrow_kind) => { let value_for_arm = self.storage_live_binding( @@ -1870,9 +1892,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); - self.cfg.push_assign(block, source_info, &value_for_arm, rvalue); + self.cfg.push_assign(block, source_info, value_for_arm, rvalue); let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); - self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue); + self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } } } @@ -1903,14 +1925,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); } let rvalue = match binding.binding_mode { - BindingMode::ByValue => { - Rvalue::Use(self.consume_by_copy_or_move(binding.source.clone())) - } + BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), BindingMode::ByRef(borrow_kind) => { Rvalue::Ref(re_erased, borrow_kind, binding.source) } }; - self.cfg.push_assign(block, source_info, &local, rvalue); + self.cfg.push_assign(block, source_info, local, rvalue); } } @@ -1924,7 +1944,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, visibility_scope: SourceScope, mutability: Mutability, - name: Name, + name: Symbol, mode: BindingMode, var_id: HirId, var_ty: Ty<'tcx>, @@ -1949,20 +1969,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = LocalDecl::<'tcx> { mutability, ty: var_ty, - user_ty, + user_ty: if user_ty.is_empty() { None } else { Some(box user_ty) }, source_info, internal: false, is_block_tail: None, - local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + local_info: Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { binding_mode, - // hypothetically, `visit_bindings` could try to unzip + // hypothetically, `visit_primary_bindings` could try to unzip // an outermost hir::Ty as we descend, matching up // idents in pat; but complex w/ unclear UI payoff. // Instead, just abandon providing diagnostic info. opt_ty_info: None, opt_match_place, pat_span, - }))), + })))), }; let for_arm_body = self.local_decls.push(local); self.var_debug_info.push(VarDebugInfo { @@ -1976,11 +1996,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // immutable to avoid the unused mut lint. mutability: Mutability::Not, ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty), - user_ty: UserTypeProjections::none(), + user_ty: None, source_info, internal: false, is_block_tail: None, - local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard)), + local_info: Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))), }); self.var_debug_info.push(VarDebugInfo { name, diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs index aea4f5f1b3ac9..09543cd1ce4e6 100644 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ b/src/librustc_mir_build/build/matches/simplify.rs @@ -15,12 +15,13 @@ use crate::build::matches::{Ascription, Binding, Candidate, MatchPair}; use crate::build::Builder; use crate::hair::{self, *}; -use rustc::mir::interpret::truncate; -use rustc::mir::Place; -use rustc::ty; -use rustc::ty::layout::{Integer, IntegerExt, Size}; use rustc_attr::{SignedInt, UnsignedInt}; use rustc_hir::RangeEnd; +use rustc_middle::mir::interpret::truncate; +use rustc_middle::mir::Place; +use rustc_middle::ty; +use rustc_middle::ty::layout::IntegerExt; +use rustc_target::abi::{Integer, Size}; use std::mem; @@ -128,7 +129,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Ok(()) } - PatKind::Binding { name, mutability, mode, var, ty, ref subpattern } => { + PatKind::Binding { name, mutability, mode, var, ty, ref subpattern, is_primary: _ } => { candidate.bindings.push(Binding { name, mutability, diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs index d23a2708dc478..1eab5848e0974 100644 --- a/src/librustc_mir_build/build/matches/test.rs +++ b/src/librustc_mir_build/build/matches/test.rs @@ -9,14 +9,14 @@ use crate::build::matches::{Candidate, MatchPair, Test, TestKind}; use crate::build::Builder; use crate::hair::pattern::compare_const_vals; use crate::hair::*; -use rustc::mir::*; -use rustc::ty::layout::VariantIdx; -use rustc::ty::util::IntTypeExt; -use rustc::ty::{self, adjustment::PointerCast, Ty}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::RangeEnd; use rustc_index::bit_set::BitSet; +use rustc_middle::mir::*; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, adjustment::PointerCast, Ty}; use rustc_span::symbol::sym; +use rustc_target::abi::VariantIdx; use std::cmp::Ordering; @@ -202,7 +202,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let discr_ty = adt_def.repr.discr_type().to_ty(tcx); let discr = self.temp(discr_ty, test.span); - self.cfg.push_assign(block, source_info, &discr, Rvalue::Discriminant(place)); + self.cfg.push_assign(block, source_info, discr, Rvalue::Discriminant(place)); assert_eq!(values.len() + 1, targets.len()); self.cfg.terminate( block, @@ -303,7 +303,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let actual = self.temp(usize_ty, test.span); // actual = len(place) - self.cfg.push_assign(block, source_info, &actual, Rvalue::Len(place)); + self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); // expected = let expected = self.push_usize(block, source_info, len); @@ -342,7 +342,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let result = self.temp(bool_ty, source_info.span); // result = op(left, right) - self.cfg.push_assign(block, source_info, &result, Rvalue::BinaryOp(op, left, right)); + self.cfg.push_assign(block, source_info, result, Rvalue::BinaryOp(op, left, right)); // branch based on result self.cfg.terminate( @@ -362,7 +362,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place: Place<'tcx>, mut ty: Ty<'tcx>, ) { - use rustc::middle::lang_items::EqTraitLangItem; + use rustc_hir::lang_items::EqTraitLangItem; let mut expect = self.literal_operand(source_info.span, value); let mut val = Operand::Copy(place); @@ -394,7 +394,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign( block, source_info, - &temp, + temp, Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), val, ty), ); val = Operand::Move(temp); @@ -404,7 +404,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign( block, source_info, - &slice, + slice, Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), expect, ty), ); expect = Operand::Move(slice); @@ -423,7 +423,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let bool_ty = self.hir.bool_ty(); let eq_result = self.temp(bool_ty, source_info.span); let eq_block = self.cfg.start_new_block(); - let cleanup = self.diverge_cleanup(); self.cfg.terminate( block, source_info, @@ -441,10 +440,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }), args: vec![val, expect], destination: Some((eq_result, eq_block)), - cleanup: Some(cleanup), + cleanup: None, from_hir_call: false, }, ); + self.diverge_from(block); if let [success_block, fail_block] = *make_target_blocks(self) { // check the result @@ -582,7 +582,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // enough elements. Some(1) } - (Ordering::Equal, &Some(_)) | (Ordering::Greater, &Some(_)) => { + (Ordering::Equal | Ordering::Greater, &Some(_)) => { // This can match both if $actual_len = test_len >= pat_len, // and if $actual_len > test_len. We can't advance. None @@ -681,7 +681,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (&TestKind::Range { .. }, _) => None, - (&TestKind::Eq { .. }, _) | (&TestKind::Len { .. }, _) => { + (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => { // These are all binary tests. // // FIXME(#29623) we can be more clever here diff --git a/src/librustc_mir_build/build/matches/util.rs b/src/librustc_mir_build/build/matches/util.rs index def8d1b2fd8ba..a97ddeb060012 100644 --- a/src/librustc_mir_build/build/matches/util.rs +++ b/src/librustc_mir_build/build/matches/util.rs @@ -1,11 +1,10 @@ use crate::build::matches::MatchPair; use crate::build::Builder; use crate::hair::*; -use rustc::mir::*; -use rustc::ty; +use rustc_middle::mir::*; +use rustc_middle::ty; use smallvec::SmallVec; use std::convert::TryInto; -use std::u32; impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn field_match_pairs<'pat>( @@ -16,11 +15,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { subpatterns .iter() .map(|fieldpat| { - let place = self.hir.tcx().mk_place_field( - place.clone(), - fieldpat.field, - fieldpat.pattern.ty, - ); + let place = + self.hir.tcx().mk_place_field(place, fieldpat.field, fieldpat.pattern.ty); MatchPair::new(place, &fieldpat.pattern) }) .collect() @@ -45,14 +41,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { let elem = ProjectionElem::ConstantIndex { offset: idx as u32, min_length, from_end: false }; - let place = tcx.mk_place_elem(place.clone(), elem); + let place = tcx.mk_place_elem(*place, elem); MatchPair::new(place, subpattern) })); if let Some(subslice_pat) = opt_slice { let suffix_len = suffix.len() as u32; let subslice = tcx.mk_place_elem( - place.clone(), + *place, ProjectionElem::Subslice { from: prefix.len() as u32, to: if exact_size { min_length - suffix_len } else { suffix_len }, @@ -69,7 +65,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { min_length, from_end: !exact_size, }; - let place = tcx.mk_place_elem(place.clone(), elem); + let place = tcx.mk_place_elem(*place, elem); MatchPair::new(place, subpattern) })); } diff --git a/src/librustc_mir_build/build/misc.rs b/src/librustc_mir_build/build/misc.rs index 3d5145b6960f5..e8933ff8aa749 100644 --- a/src/librustc_mir_build/build/misc.rs +++ b/src/librustc_mir_build/build/misc.rs @@ -3,9 +3,9 @@ use crate::build::Builder; -use rustc::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty}; -use rustc::mir::*; +use rustc_middle::mir::*; use rustc_span::{Span, DUMMY_SP}; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -15,7 +15,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// N.B., **No cleanup is scheduled for this temporary.** You should /// call `schedule_drop` once the temporary is initialized. crate fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> { - let temp = self.local_decls.push(LocalDecl::new_temp(ty, span)); + let temp = self.local_decls.push(LocalDecl::new(ty, span)); let place = Place::from(temp); debug!("temp: created temp {:?} with type {:?}", place, self.local_decls[temp].ty); place @@ -32,10 +32,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Operand::Constant(constant) } - crate fn unit_rvalue(&mut self) -> Rvalue<'tcx> { - Rvalue::Aggregate(box AggregateKind::Tuple, vec![]) - } - // Returns a zero literal operand for the appropriate type, works for // bool, char and integers. crate fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { @@ -55,7 +51,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign_constant( block, source_info, - &temp, + temp, Constant { span: source_info.span, user_ty: None, diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs index a7ec4f501ae76..b30b57ea9217a 100644 --- a/src/librustc_mir_build/build/mod.rs +++ b/src/librustc_mir_build/build/mod.rs @@ -2,32 +2,32 @@ use crate::build; use crate::build::scope::DropKind; use crate::hair::cx::Cx; use crate::hair::{BindingMode, LintLevel, PatKind}; -use rustc::middle::lang_items; -use rustc::middle::region; -use rustc::mir::*; -use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_attr::{self as attr, UnwindAttr}; +use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items; use rustc_hir::{GeneratorKind, HirIdMap, Node}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::middle::region; +use rustc_middle::mir::*; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; use rustc_target::spec::PanicStrategy; -use std::u32; use super::lints; -crate fn mir_built(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::steal::Steal> { +crate fn mir_built(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::steal::Steal> { tcx.alloc_steal_mir(mir_build(tcx, def_id)) } /// Construct the MIR for a given `DefId`. -fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { - let id = tcx.hir().as_local_hir_id(def_id).unwrap(); +fn mir_build(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Body<'_> { + let id = tcx.hir().as_local_hir_id(def_id); // Figure out what primary body this item has. let (body_id, return_ty_span) = match tcx.hir().get(id) { @@ -46,8 +46,10 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { kind: hir::TraitItemKind::Fn(hir::FnSig { decl, .. }, hir::TraitFn::Provided(body_id)), .. }) => (*body_id, decl.output.span()), - Node::Item(hir::Item { kind: hir::ItemKind::Static(ty, _, body_id), .. }) - | Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, body_id), .. }) + Node::Item(hir::Item { + kind: hir::ItemKind::Static(ty, _, body_id) | hir::ItemKind::Const(ty, body_id), + .. + }) | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(ty, body_id), .. }) | Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Const(ty, Some(body_id)), @@ -60,7 +62,7 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { tcx.infer_ctxt().enter(|infcx| { let cx = Cx::new(&infcx, id); - let body = if cx.tables().tainted_by_errors { + let body = if let Some(ErrorReported) = cx.tables().tainted_by_errors { build::construct_error(cx, body_id) } else if cx.body_owner_kind.is_fn_or_closure() { // fetch the fully liberated fn signature (that is, all bound @@ -181,9 +183,6 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { lints::check(tcx, &body, def_id); - let mut body = BodyAndCache::new(body); - body.ensure_predecessors(); - // The borrow checker will replace all the regions here with its own // inference variables. There's no point having non-erased regions here. // The exception is `body.user_type_annotations`, which is used unmodified @@ -243,6 +242,9 @@ enum BlockFrame { /// /// Example: `let _ = { STMT_1; EXPR };` tail_result_is_ignored: bool, + + /// `Span` of the tail expression. + span: Span, }, /// Generic mark meaning that the block occurred as a subexpression @@ -325,11 +327,6 @@ struct Builder<'a, 'tcx> { var_debug_info: Vec>, - /// Cached block with the `RESUME` terminator; this is created - /// when first set of cleanups are built. - cached_resume_block: Option, - /// Cached block with the `RETURN` terminator. - cached_return_block: Option, /// Cached block with the `UNREACHABLE` terminator. cached_unreachable_block: Option, } @@ -370,8 +367,8 @@ impl BlockContext { match bf { BlockFrame::SubExpr => continue, BlockFrame::Statement { .. } => break, - &BlockFrame::TailExpr { tail_result_is_ignored } => { - return Some(BlockTailInfo { tail_result_is_ignored }); + &BlockFrame::TailExpr { tail_result_is_ignored, span } => { + return Some(BlockTailInfo { tail_result_is_ignored, span }); } } } @@ -394,8 +391,10 @@ impl BlockContext { Some(BlockFrame::SubExpr) => false, // otherwise: use accumulated is_ignored state. - Some(BlockFrame::TailExpr { tail_result_is_ignored: ignored }) - | Some(BlockFrame::Statement { ignores_expr_result: ignored }) => *ignored, + Some( + BlockFrame::TailExpr { tail_result_is_ignored: ignored, .. } + | BlockFrame::Statement { ignores_expr_result: ignored }, + ) => *ignored, } } } @@ -522,9 +521,9 @@ macro_rules! unpack { }}; } -fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: DefId, _abi: Abi) -> bool { +fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool { // Validate `#[unwind]` syntax regardless of platform-specific panic strategy. - let attrs = &tcx.get_attrs(fn_def_id); + let attrs = &tcx.get_attrs(fn_def_id.to_def_id()); let unwind_attr = attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs); // We never unwind, so it's not relevant to stop an unwind. @@ -532,11 +531,6 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: DefId, _abi: Abi) -> bool { return false; } - // We cannot add landing pads, so don't add one. - if tcx.sess.no_landing_pads() { - return false; - } - // This is a special case: some functions have a C abi but are meant to // unwind anyway. Don't stop them. match unwind_attr { @@ -591,50 +585,34 @@ where region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite }; let arg_scope = region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::Arguments }; - let mut block = START_BLOCK; let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); - unpack!( - block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { - if should_abort_on_panic(tcx, fn_def_id, abi) { - builder.schedule_abort(); - } - - let arg_scope_s = (arg_scope, source_info); - // `return_block` is called when we evaluate a `return` expression, so - // we just use `START_BLOCK` here. - unpack!( - block = builder.in_breakable_scope( - None, - START_BLOCK, - Place::return_place(), - |builder| { - builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { - builder.args_and_body( - block, - fn_def_id, - &arguments, - arg_scope, - &body.value, - ) - }) - }, - ) - ); - // Attribute epilogue to function's closing brace - let fn_end = span.shrink_to_hi(); - let source_info = builder.source_info(fn_end); - let return_block = builder.return_block(); - builder.cfg.goto(block, source_info, return_block); - builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); - // Attribute any unreachable codepaths to the function's closing brace - if let Some(unreachable_block) = builder.cached_unreachable_block { - builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); - } - return_block.unit() - }) - ); - assert_eq!(block, builder.return_block()); + unpack!(builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { + let arg_scope_s = (arg_scope, source_info); + // Attribute epilogue to function's closing brace + let fn_end = span.shrink_to_hi(); + let return_block = + unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| { + Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { + builder.args_and_body( + START_BLOCK, + fn_def_id.to_def_id(), + &arguments, + arg_scope, + &body.value, + ) + })) + })); + let source_info = builder.source_info(fn_end); + builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); + let should_abort = should_abort_on_panic(tcx, fn_def_id, abi); + builder.build_drop_trees(should_abort); + // Attribute any unreachable codepaths to the function's closing brace + if let Some(unreachable_block) = builder.cached_unreachable_block { + builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); + } + return_block.unit() + })); let spread_arg = if abi == Abi::RustCall { // RustCall pseudo-ABI untuples the last argument. @@ -642,7 +620,7 @@ where } else { None }; - debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id)); + debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id.to_def_id())); let mut body = builder.finish(); body.spread_arg = spread_arg; @@ -663,13 +641,12 @@ fn construct_const<'a, 'tcx>( let mut block = START_BLOCK; let ast_expr = &tcx.hir().body(body_id).value; let expr = builder.hir.mirror(ast_expr); - unpack!(block = builder.into_expr(&Place::return_place(), block, expr)); + unpack!(block = builder.into_expr(Place::return_place(), block, expr)); let source_info = builder.source_info(span); builder.cfg.terminate(block, source_info, TerminatorKind::Return); - // Constants can't `return` so a return block should not be created. - assert_eq!(builder.cached_return_block, None); + builder.build_drop_trees(false); // Constants may be match expressions in which case an unreachable block may // be created, so terminate it properly. @@ -709,15 +686,7 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t // Some MIR passes will expect the number of parameters to match the // function declaration. for _ in 0..num_params { - builder.local_decls.push(LocalDecl { - mutability: Mutability::Mut, - ty, - user_ty: UserTypeProjections::none(), - source_info, - internal: false, - local_info: LocalInfo::Other, - is_block_tail: None, - }); + builder.local_decls.push(LocalDecl::with_source_info(ty, source_info)); } builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); let mut body = builder.finish(); @@ -744,24 +713,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn_span: span, arg_count, generator_kind, - scopes: Default::default(), + scopes: scope::Scopes::new(), block_context: BlockContext::new(), source_scopes: IndexVec::new(), source_scope: OUTERMOST_SOURCE_SCOPE, guard_context: vec![], push_unsafe_count: 0, unpushed_unsafe: safety, - local_decls: IndexVec::from_elem_n( - LocalDecl::new_return_place(return_ty, return_span), - 1, - ), + local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1), canonical_user_type_annotations: IndexVec::new(), upvar_mutbls: vec![], var_indices: Default::default(), unit_temp: None, var_debug_info: vec![], - cached_resume_block: None, - cached_return_block: None, cached_unreachable_block: None, }; @@ -805,19 +769,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> BlockAnd<()> { // Allocate locals for the function arguments for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() { - let source_info = SourceInfo { - scope: OUTERMOST_SOURCE_SCOPE, - span: arg_opt.map_or(self.fn_span, |arg| arg.pat.span), - }; - let arg_local = self.local_decls.push(LocalDecl { - mutability: Mutability::Mut, - ty, - user_ty: UserTypeProjections::none(), - source_info, - internal: false, - local_info: LocalInfo::Other, - is_block_tail: None, - }); + let source_info = + SourceInfo::outermost(arg_opt.map_or(self.fn_span, |arg| arg.pat.span)); + let arg_local = self.local_decls.push(LocalDecl::with_source_info(ty, source_info)); // If this is a simple binding pattern, give debuginfo a nice name. if let Some(arg) = arg_opt { @@ -886,10 +840,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, - source_info: SourceInfo { - scope: OUTERMOST_SOURCE_SCOPE, - span: tcx_hir.span(var_id), - }, + source_info: SourceInfo::outermost(tcx_hir.span(var_id)), place: Place { local: closure_env_arg, projection: tcx.intern_place_elems(&projs), @@ -934,17 +885,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.local_decls[local].mutability = mutability; self.local_decls[local].source_info.scope = self.source_scope; self.local_decls[local].local_info = if let Some(kind) = self_binding { - LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(*kind))) + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(*kind)))) } else { let binding_mode = ty::BindingMode::BindByValue(mutability); - LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( VarBindingForm { binding_mode, opt_ty_info, opt_match_place: Some((Some(place), span)), pat_span: span, }, - ))) + )))) }; self.var_indices.insert(var, LocalsForNode::One(local)); } @@ -969,7 +920,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let body = self.hir.mirror(ast_body); - self.into(&Place::return_place(), block, body) + self.into(Place::return_place(), block, body) } fn set_correct_source_scope_for_arg( @@ -1006,17 +957,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } - - fn return_block(&mut self) -> BasicBlock { - match self.cached_return_block { - Some(rb) => rb, - None => { - let rb = self.cfg.start_new_block(); - self.cached_return_block = Some(rb); - rb - } - } - } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_mir_build/build/scope.rs b/src/librustc_mir_build/build/scope.rs index a63ac06ec3fe9..868fb69abe80c 100644 --- a/src/librustc_mir_build/build/scope.rs +++ b/src/librustc_mir_build/build/scope.rs @@ -6,30 +6,31 @@ contents, and then pop it off. Every scope is named by a ### SEME Regions -When pushing a new scope, we record the current point in the graph (a +When pushing a new [Scope], we record the current point in the graph (a basic block); this marks the entry to the scope. We then generate more stuff in the control-flow graph. Whenever the scope is exited, either via a `break` or `return` or just by fallthrough, that marks an exit from the scope. Each lexical scope thus corresponds to a single-entry, multiple-exit (SEME) region in the control-flow graph. -For now, we keep a mapping from each `region::Scope` to its -corresponding SEME region for later reference (see caveat in next -paragraph). This is because region scopes are tied to -them. Eventually, when we shift to non-lexical lifetimes, there should -be no need to remember this mapping. +For now, we record the `region::Scope` to each SEME region for later reference +(see caveat in next paragraph). This is because destruction scopes are tied to +them. This may change in the future so that MIR lowering determines its own +destruction scopes. ### Not so SEME Regions In the course of building matches, it sometimes happens that certain code (namely guards) gets executed multiple times. This means that the scope lexical scope may in fact correspond to multiple, disjoint SEME regions. So in fact our -mapping is from one scope to a vector of SEME regions. +mapping is from one scope to a vector of SEME regions. Since the SEME regions +are disjoint, the mapping is still one-to-one for the set of SEME regions that +we're currently in. -Also in matches, the scopes assigned to arms are not even SEME regions! Each -arm has a single region with one entry for each pattern. We manually +Also in matches, the scopes assigned to arms are not always even SEME regions! +Each arm has a single region with one entry for each pattern. We manually manipulate the scheduled drops in this scope to avoid dropping things multiple -times, although drop elaboration would clean this up for value drops. +times. ### Drops @@ -60,38 +61,48 @@ that for now); any later drops would also drop `y`. There are numerous "normal" ways to early exit a scope: `break`, `continue`, `return` (panics are handled separately). Whenever an -early exit occurs, the method `exit_scope` is called. It is given the +early exit occurs, the method `break_scope` is called. It is given the current point in execution where the early exit occurs, as well as the scope you want to branch to (note that all early exits from to some -other enclosing scope). `exit_scope` will record this exit point and -also add all drops. +other enclosing scope). `break_scope` will record the set of drops currently +scheduled in a [DropTree]. Later, before `in_breakable_scope` exits, the drops +will be added to the CFG. -Panics are handled in a similar fashion, except that a panic always -returns out to the `DIVERGE_BLOCK`. To trigger a panic, simply call -`panic(p)` with the current point `p`. Or else you can call -`diverge_cleanup`, which will produce a block that you can branch to -which does the appropriate cleanup and then diverges. `panic(p)` -simply calls `diverge_cleanup()` and adds an edge from `p` to the -result. +Panics are handled in a similar fashion, except that the drops are added to the +MIR once the rest of the function has finished being lowered. If a terminator +can panic, call `diverge_from(block)` with the block containing the terminator +`block`. -### Loop scopes +### Breakable scopes In addition to the normal scope stack, we track a loop scope stack -that contains only loops. It tracks where a `break` and `continue` -should go to. +that contains only loops and breakable blocks. It tracks where a `break`, +`continue` or `return` should go to. */ use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG}; use crate::hair::{Expr, ExprRef, LintLevel}; -use rustc::middle::region; -use rustc::mir::*; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::GeneratorKind; +use rustc_index::vec::IndexVec; +use rustc_middle::middle::region; +use rustc_middle::mir::*; use rustc_span::{Span, DUMMY_SP}; -use std::collections::hash_map::Entry; -use std::mem; + +#[derive(Debug)] +pub struct Scopes<'tcx> { + scopes: Vec, + /// The current set of breakable scopes. See module comment for more details. + breakable_scopes: Vec>, + + /// Drops that need to be done on unwind paths. See the comment on + /// [DropTree] for more details. + unwind_drops: DropTree, + + /// Drops that need to be done on paths to the `GeneratorDrop` terminator. + generator_drops: DropTree, +} #[derive(Debug)] struct Scope { @@ -112,73 +123,45 @@ struct Scope { moved_locals: Vec, - /// The cache for drop chain on “normal” exit into a particular BasicBlock. - cached_exits: FxHashMap<(BasicBlock, region::Scope), BasicBlock>, + /// The drop index that will drop everything in and below this scope on an + /// unwind path. + cached_unwind_block: Option, - /// The cache for drop chain on "generator drop" exit. - cached_generator_drop: Option, - - /// The cache for drop chain on "unwind" exit. - cached_unwind: CachedBlock, + /// The drop index that will drop everything in and below this scope on a + /// generator drop path. + cached_generator_drop_block: Option, } -#[derive(Debug, Default)] -crate struct Scopes<'tcx> { - scopes: Vec, - /// The current set of breakable scopes. See module comment for more details. - breakable_scopes: Vec>, -} - -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] struct DropData { - /// span where drop obligation was incurred (typically where place was declared) - span: Span, + /// The `Span` where drop obligation was incurred (typically where place was + /// declared) + source_info: SourceInfo, /// local to drop local: Local, /// Whether this is a value Drop or a StorageDead. kind: DropKind, - - /// The cached blocks for unwinds. - cached_block: CachedBlock, -} - -#[derive(Debug, Default, Clone, Copy)] -struct CachedBlock { - /// The cached block for the cleanups-on-diverge path. This block - /// contains code to run the current drop and all the preceding - /// drops (i.e., those having lower index in Drop’s Scope drop - /// array) - unwind: Option, - - /// The cached block for unwinds during cleanups-on-generator-drop path - /// - /// This is split from the standard unwind path here to prevent drop - /// elaboration from creating drop flags that would have to be captured - /// by the generator. I'm not sure how important this optimization is, - /// but it is here. - generator_drop: Option, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum DropKind { Value, Storage, } -#[derive(Clone, Debug)] +#[derive(Debug)] struct BreakableScope<'tcx> { /// Region scope of the loop region_scope: region::Scope, - /// Where the body of the loop begins. `None` if block - continue_block: Option, - /// Block to branch into when the loop or block terminates (either by being - /// `break`-en out from, or by having its condition to become false) - break_block: BasicBlock, /// The destination of the loop/block expression itself (i.e., where to put - /// the result of a `break` expression) + /// the result of a `break` or `return` expression) break_destination: Place<'tcx>, + /// Drops that happen on the `break`/`return` path. + break_drops: DropTree, + /// Drops that happen on the `continue` path. + continue_drops: Option, } /// The target of an expression that breaks out of a scope @@ -189,61 +172,33 @@ crate enum BreakableTarget { Return, } -impl CachedBlock { - fn invalidate(&mut self) { - *self = CachedBlock::default(); - } +rustc_index::newtype_index! { + struct DropIdx { .. } +} - fn get(&self, generator_drop: bool) -> Option { - if generator_drop { self.generator_drop } else { self.unwind } - } +const ROOT_NODE: DropIdx = DropIdx::from_u32(0); - fn ref_mut(&mut self, generator_drop: bool) -> &mut Option { - if generator_drop { &mut self.generator_drop } else { &mut self.unwind } - } +/// A tree of drops that we have deferred lowering. It's used for: +/// +/// * Drops on unwind paths +/// * Drops on generator drop paths (when a suspended generator is dropped) +/// * Drops on return and loop exit paths +/// +/// Once no more nodes could be added to the tree, we lower it to MIR in one go +/// in `build_drop_tree`. +#[derive(Debug)] +struct DropTree { + /// Drops in the tree. + drops: IndexVec, + /// Map for finding the inverse of the `next_drop` relation: + /// + /// `previous_drops[(drops[i].1, drops[i].0.local, drops[i].0.kind] == i` + previous_drops: FxHashMap<(DropIdx, Local, DropKind), DropIdx>, + /// Edges into the `DropTree` that need to be added once it's lowered. + entry_points: Vec<(DropIdx, BasicBlock)>, } impl Scope { - /// Invalidates all the cached blocks in the scope. - /// - /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a - /// larger extent of code. - /// - /// `storage_only` controls whether to invalidate only drop paths that run `StorageDead`. - /// `this_scope_only` controls whether to invalidate only drop paths that refer to the current - /// top-of-scope (as opposed to dependent scopes). - fn invalidate_cache( - &mut self, - storage_only: bool, - generator_kind: Option, - this_scope_only: bool, - ) { - // FIXME: maybe do shared caching of `cached_exits` etc. to handle functions - // with lots of `try!`? - - // cached exits drop storage and refer to the top-of-scope - self.cached_exits.clear(); - - // the current generator drop and unwind refer to top-of-scope - self.cached_generator_drop = None; - - let ignore_unwinds = storage_only && generator_kind.is_none(); - if !ignore_unwinds { - self.cached_unwind.invalidate(); - } - - if !ignore_unwinds && !this_scope_only { - for drop_data in &mut self.drops { - drop_data.cached_block.invalidate(); - } - } - } - - /// Given a span and this scope's source scope, make a SourceInfo. - fn source_info(&self, span: Span) -> SourceInfo { - SourceInfo { span, scope: self.source_scope } - } - /// Whether there's anything to do for the cleanup path, that is, /// when unwinding through this scope. This includes destructors, /// but not StorageDead statements, which don't get emitted at all @@ -261,109 +216,222 @@ impl Scope { DropKind::Storage => false, }) } + + fn invalidate_cache(&mut self) { + self.cached_unwind_block = None; + self.cached_generator_drop_block = None; + } } -impl<'tcx> Scopes<'tcx> { - fn len(&self) -> usize { - self.scopes.len() +/// A trait that determined how [DropTree::build_mir] creates its blocks and +/// links to any entry nodes. +trait DropTreeBuilder<'tcx> { + /// Create a new block for the tree. This should call either + /// `cfg.start_new_block()` or `cfg.start_new_cleanup_block()`. + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock; + + /// Links a block outside the drop tree, `from`, to the block `to` inside + /// the drop tree. + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock); +} + +impl DropTree { + fn new() -> Self { + // The root node of the tree doesn't represent a drop, but instead + // represents the block in the tree that should be jumped to once all + // of the required drops have been performed. + let fake_source_info = SourceInfo::outermost(DUMMY_SP); + let fake_data = + DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage }; + let drop_idx = DropIdx::MAX; + let drops = IndexVec::from_elem_n((fake_data, drop_idx), 1); + Self { drops, entry_points: Vec::new(), previous_drops: FxHashMap::default() } } - fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) { - debug!("push_scope({:?})", region_scope); - self.scopes.push(Scope { - source_scope: vis_scope, - region_scope: region_scope.0, - region_scope_span: region_scope.1.span, - drops: vec![], - moved_locals: vec![], - cached_generator_drop: None, - cached_exits: Default::default(), - cached_unwind: CachedBlock::default(), - }); + fn add_drop(&mut self, drop: DropData, next: DropIdx) -> DropIdx { + let drops = &mut self.drops; + *self + .previous_drops + .entry((next, drop.local, drop.kind)) + .or_insert_with(|| drops.push((drop, next))) } - fn pop_scope( - &mut self, - region_scope: (region::Scope, SourceInfo), - ) -> (Scope, Option) { - let scope = self.scopes.pop().unwrap(); - assert_eq!(scope.region_scope, region_scope.0); - let unwind_to = - self.scopes.last().and_then(|next_scope| next_scope.cached_unwind.get(false)); - (scope, unwind_to) + fn add_entry(&mut self, from: BasicBlock, to: DropIdx) { + debug_assert!(to < self.drops.next_index()); + self.entry_points.push((to, from)); } - fn may_panic(&self, scope_count: usize) -> bool { - let len = self.len(); - self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup()) + /// 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>, + ) { + debug!("DropTree::build_mir(drops = {:#?})", self); + assert_eq!(blocks.len(), self.drops.len()); + + self.assign_blocks::(cfg, blocks); + self.link_blocks(cfg, blocks) } - /// Finds the breakable scope for a given label. This is used for - /// resolving `return`, `break` and `continue`. - fn find_breakable_scope( - &self, - span: Span, - target: BreakableTarget, - ) -> (BasicBlock, region::Scope, Option>) { - let get_scope = |scope: region::Scope| { - // find the loop-scope by its `region::Scope`. - self.breakable_scopes - .iter() - .rfind(|breakable_scope| breakable_scope.region_scope == scope) - .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found")) - }; - match target { - BreakableTarget::Return => { - let scope = &self.breakable_scopes[0]; - if scope.break_destination != Place::return_place() { - span_bug!(span, "`return` in item with no return scope"); + /// 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>, + ) { + // 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. + #[derive(Clone, Copy)] + enum Block { + // This drop is unreachable + None, + // This drop is only reachable through the `StorageDead` with the + // specified index. + Shares(DropIdx), + // This drop has more than one way of being reached, or it is + // branched to from outside the tree, or its predecessor is a + // `Value` drop. + Own, + } + + let mut needs_block = IndexVec::from_elem(Block::None, &self.drops); + if blocks[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. + needs_block[ROOT_NODE] = Block::Own; + } + + // Sort so that we only need to check the last value. + let entry_points = &mut self.entry_points; + entry_points.sort(); + + for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() { + if entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) { + let block = *blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg)); + needs_block[drop_idx] = Block::Own; + while entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) { + let entry_block = entry_points.pop().unwrap().1; + T::add_entry(cfg, entry_block, block); } - (scope.break_block, scope.region_scope, Some(scope.break_destination)) } - BreakableTarget::Break(scope) => { - let scope = get_scope(scope); - (scope.break_block, scope.region_scope, Some(scope.break_destination)) + match needs_block[drop_idx] { + Block::None => continue, + Block::Own => { + blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg)); + } + Block::Shares(pred) => { + blocks[drop_idx] = blocks[pred]; + } } - BreakableTarget::Continue(scope) => { - let scope = get_scope(scope); - let continue_block = scope - .continue_block - .unwrap_or_else(|| span_bug!(span, "missing `continue` block")); - (continue_block, scope.region_scope, None) + if let DropKind::Value = drop_data.0.kind { + needs_block[drop_data.1] = Block::Own; + } else { + if drop_idx != ROOT_NODE { + match &mut needs_block[drop_data.1] { + pred @ Block::None => *pred = Block::Shares(drop_idx), + pred @ Block::Shares(_) => *pred = Block::Own, + Block::Own => (), + } + } } } + + debug!("assign_blocks: blocks = {:#?}", blocks); + assert!(entry_points.is_empty()); } - fn num_scopes_above(&self, region_scope: region::Scope, span: Span) -> usize { - let scope_count = self - .scopes - .iter() - .rev() - .position(|scope| scope.region_scope == region_scope) - .unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope)); - let len = self.len(); - assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes"); - scope_count + fn link_blocks<'tcx>( + &self, + cfg: &mut CFG<'tcx>, + blocks: &IndexVec>, + ) { + for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() { + let block = if let Some(block) = blocks[drop_idx] { + block + } else { + continue; + }; + match drop_data.0.kind { + DropKind::Value => { + let terminator = TerminatorKind::Drop { + target: blocks[drop_data.1].unwrap(), + // The caller will handle this if needed. + unwind: None, + location: drop_data.0.local.into(), + }; + cfg.terminate(block, drop_data.0.source_info, terminator); + } + // Root nodes don't correspond to a drop. + DropKind::Storage if drop_idx == ROOT_NODE => {} + DropKind::Storage => { + let stmt = Statement { + source_info: drop_data.0.source_info, + kind: StatementKind::StorageDead(drop_data.0.local), + }; + cfg.push(block, stmt); + let target = blocks[drop_data.1].unwrap(); + if target != block { + // Diagnostics don't use this `Span` but debuginfo + // might. Since we don't want breakpoints to be placed + // here, especially when this is on an unwind path, we + // use `DUMMY_SP`. + let source_info = SourceInfo { span: DUMMY_SP, ..drop_data.0.source_info }; + let terminator = TerminatorKind::Goto { target }; + cfg.terminate(block, source_info, terminator); + } + } + } + } } +} - fn iter_mut(&mut self) -> impl DoubleEndedIterator + '_ { - self.scopes.iter_mut().rev() +impl<'tcx> Scopes<'tcx> { + pub(crate) fn new() -> Self { + Self { + scopes: Vec::new(), + breakable_scopes: Vec::new(), + unwind_drops: DropTree::new(), + generator_drops: DropTree::new(), + } } - fn top_scopes(&mut self, count: usize) -> impl DoubleEndedIterator + '_ { - let len = self.len(); - self.scopes[len - count..].iter_mut() + fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) { + debug!("push_scope({:?})", region_scope); + self.scopes.push(Scope { + source_scope: vis_scope, + region_scope: region_scope.0, + region_scope_span: region_scope.1.span, + drops: vec![], + moved_locals: vec![], + cached_unwind_block: None, + cached_generator_drop_block: None, + }); + } + + fn pop_scope(&mut self, region_scope: (region::Scope, SourceInfo)) -> Scope { + let scope = self.scopes.pop().unwrap(); + assert_eq!(scope.region_scope, region_scope.0); + scope + } + + fn scope_index(&self, region_scope: region::Scope, span: Span) -> usize { + self.scopes + .iter() + .rposition(|scope| scope.region_scope == region_scope) + .unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope)) } /// Returns the topmost active scope, which is known to be alive until /// the next scope expression. - pub(super) fn topmost(&self) -> region::Scope { + fn topmost(&self) -> region::Scope { self.scopes.last().expect("topmost_scope: no scopes present").region_scope } - - fn source_info(&self, index: usize, span: Span) -> SourceInfo { - self.scopes[self.len() - index].source_info(span) - } } impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -371,28 +439,50 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ========================== // Start a breakable scope, which tracks where `continue`, `break` and // `return` should branch to. - crate fn in_breakable_scope( + crate fn in_breakable_scope( &mut self, loop_block: Option, - break_block: BasicBlock, break_destination: Place<'tcx>, + span: Span, f: F, - ) -> R + ) -> BlockAnd<()> where - F: FnOnce(&mut Builder<'a, 'tcx>) -> R, + F: FnOnce(&mut Builder<'a, 'tcx>) -> Option>, { let region_scope = self.scopes.topmost(); let scope = BreakableScope { region_scope, - continue_block: loop_block, - break_block, break_destination, + break_drops: DropTree::new(), + continue_drops: loop_block.map(|_| DropTree::new()), }; self.scopes.breakable_scopes.push(scope); - let res = f(self); + let normal_exit_block = f(self); let breakable_scope = self.scopes.breakable_scopes.pop().unwrap(); assert!(breakable_scope.region_scope == region_scope); - res + let break_block = self.build_exit_tree(breakable_scope.break_drops, None); + breakable_scope.continue_drops.map(|drops| { + self.build_exit_tree(drops, loop_block); + }); + match (normal_exit_block, break_block) { + (Some(block), None) | (None, Some(block)) => block, + (None, None) => self.cfg.start_new_block().unit(), + (Some(normal_block), Some(exit_block)) => { + let target = self.cfg.start_new_block(); + let source_info = self.source_info(span); + self.cfg.terminate( + unpack!(normal_block), + source_info, + TerminatorKind::Goto { target }, + ); + self.cfg.terminate( + unpack!(exit_block), + source_info, + TerminatorKind::Goto { target }, + ); + target.unit() + } + } } crate fn in_opt_scope( @@ -476,183 +566,114 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mut block: BasicBlock, ) -> BlockAnd<()> { debug!("pop_scope({:?}, {:?})", region_scope, block); - // If we are emitting a `drop` statement, we need to have the cached - // diverge cleanup pads ready in case that drop panics. - if self.scopes.may_panic(1) { - self.diverge_cleanup(); - } - let (scope, unwind_to) = self.scopes.pop_scope(region_scope); - let unwind_to = unwind_to.unwrap_or_else(|| self.resume_block()); - unpack!( - block = build_scope_drops( - &mut self.cfg, - self.generator_kind, - &scope, - block, - unwind_to, - self.arg_count, - false, // not generator - false, // not unwind path - ) - ); + block = self.leave_top_scope(block); + + self.scopes.pop_scope(region_scope); block.unit() } + /// Sets up the drops for breaking from `block` to `target`. crate fn break_scope( &mut self, mut block: BasicBlock, value: Option>, - scope: BreakableTarget, + target: BreakableTarget, source_info: SourceInfo, ) -> BlockAnd<()> { - let (mut target_block, region_scope, destination) = - self.scopes.find_breakable_scope(source_info.span, scope); - if let BreakableTarget::Return = scope { - // We call this now, rather than when we start lowering the - // function so that the return block doesn't precede the entire - // rest of the CFG. Some passes and LLVM prefer blocks to be in - // approximately CFG order. - target_block = self.return_block(); - } + let span = source_info.span; + + let get_scope_index = |scope: region::Scope| { + // find the loop-scope by its `region::Scope`. + self.scopes + .breakable_scopes + .iter() + .rposition(|breakable_scope| breakable_scope.region_scope == scope) + .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found")) + }; + let (break_index, destination) = match target { + BreakableTarget::Return => { + let scope = &self.scopes.breakable_scopes[0]; + if scope.break_destination != Place::return_place() { + span_bug!(span, "`return` in item with no return scope"); + } + (0, Some(scope.break_destination)) + } + BreakableTarget::Break(scope) => { + let break_index = get_scope_index(scope); + let scope = &self.scopes.breakable_scopes[break_index]; + (break_index, Some(scope.break_destination)) + } + BreakableTarget::Continue(scope) => { + let break_index = get_scope_index(scope); + (break_index, None) + } + }; + if let Some(destination) = destination { if let Some(value) = value { debug!("stmt_expr Break val block_context.push(SubExpr)"); self.block_context.push(BlockFrame::SubExpr); - unpack!(block = self.into(&destination, block, value)); + unpack!(block = self.into(destination, block, value)); self.block_context.pop(); } else { - self.cfg.push_assign_unit(block, source_info, &destination) + self.cfg.push_assign_unit(block, source_info, destination, self.hir.tcx()) } } else { assert!(value.is_none(), "`return` and `break` should have a destination"); } - self.exit_scope(source_info.span, region_scope, block, target_block); + + let region_scope = self.scopes.breakable_scopes[break_index].region_scope; + let scope_index = self.scopes.scope_index(region_scope, span); + let drops = if destination.is_some() { + &mut self.scopes.breakable_scopes[break_index].break_drops + } else { + self.scopes.breakable_scopes[break_index].continue_drops.as_mut().unwrap() + }; + let mut drop_idx = ROOT_NODE; + for scope in &self.scopes.scopes[scope_index + 1..] { + for drop in &scope.drops { + drop_idx = drops.add_drop(*drop, drop_idx); + } + } + drops.add_entry(block, drop_idx); + + // `build_drop_tree` doesn't have access to our source_info, so we + // create a dummy terminator now. `TerminatorKind::Resume` is used + // because MIR type checking will panic if it hasn't been overwritten. + self.cfg.terminate(block, source_info, TerminatorKind::Resume); + self.cfg.start_new_block().unit() } - /// Branch out of `block` to `target`, exiting all scopes up to - /// and including `region_scope`. This will insert whatever drops are - /// needed. See module comment for details. - crate fn exit_scope( + crate fn exit_top_scope( &mut self, - span: Span, - region_scope: region::Scope, mut block: BasicBlock, target: BasicBlock, + source_info: SourceInfo, ) { - debug!( - "exit_scope(region_scope={:?}, block={:?}, target={:?})", - region_scope, block, target - ); - let scope_count = self.scopes.num_scopes_above(region_scope, span); + block = self.leave_top_scope(block); + self.cfg.terminate(block, source_info, TerminatorKind::Goto { target }); + } + 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. - let may_panic = self.scopes.may_panic(scope_count); - if may_panic { - self.diverge_cleanup(); - } - - let mut scopes = self.scopes.top_scopes(scope_count + 1).rev(); - let mut scope = scopes.next().unwrap(); - for next_scope in scopes { - if scope.drops.is_empty() { - scope = next_scope; - continue; - } - let source_info = scope.source_info(span); - block = match scope.cached_exits.entry((target, region_scope)) { - Entry::Occupied(e) => { - self.cfg.goto(block, source_info, *e.get()); - return; - } - Entry::Vacant(v) => { - let b = self.cfg.start_new_block(); - self.cfg.goto(block, source_info, b); - v.insert(b); - b - } - }; - - let unwind_to = next_scope.cached_unwind.get(false).unwrap_or_else(|| { - debug_assert!(!may_panic, "cached block not present?"); - START_BLOCK - }); - - unpack!( - block = build_scope_drops( - &mut self.cfg, - self.generator_kind, - scope, - block, - unwind_to, - self.arg_count, - false, // not generator - false, // not unwind path - ) - ); - - scope = next_scope; - } - - self.cfg.goto(block, self.scopes.source_info(scope_count, span), target); - } - - /// Creates a path that performs all required cleanup for dropping a generator. - /// - /// This path terminates in GeneratorDrop. Returns the start of the path. - /// None indicates there’s no cleanup to do at this point. - crate fn generator_drop_cleanup(&mut self) -> Option { - // Fill in the cache for unwinds - self.diverge_cleanup_gen(true); - - let src_info = self.scopes.source_info(self.scopes.len(), self.fn_span); - let resume_block = self.resume_block(); - let mut scopes = self.scopes.iter_mut().peekable(); - let mut block = self.cfg.start_new_block(); - let result = block; - - while let Some(scope) = scopes.next() { - block = if let Some(b) = scope.cached_generator_drop { - self.cfg.goto(block, src_info, b); - return Some(result); - } else { - let b = self.cfg.start_new_block(); - scope.cached_generator_drop = Some(b); - self.cfg.goto(block, src_info, b); - b - }; - - let unwind_to = scopes - .peek() - .as_ref() - .map(|scope| { - scope - .cached_unwind - .get(true) - .unwrap_or_else(|| span_bug!(src_info.span, "cached block not present?")) - }) - .unwrap_or(resume_block); - - unpack!( - block = build_scope_drops( - &mut self.cfg, - self.generator_kind, - scope, - block, - unwind_to, - self.arg_count, - true, // is generator - true, // is cached path - ) - ); - } - - self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop); - - Some(result) + let needs_cleanup = self.scopes.scopes.last().map_or(false, |scope| scope.needs_cleanup()); + let is_generator = self.generator_kind.is_some(); + 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"); + unpack!(build_scope_drops( + &mut self.cfg, + &mut self.scopes.unwind_drops, + scope, + block, + unwind_to, + is_generator && needs_cleanup, + self.arg_count, + )) } /// Creates a new source scope, nested in the current one. @@ -728,15 +749,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - // Schedule an abort block - this is used for some ABIs that cannot unwind - crate fn schedule_abort(&mut self) -> BasicBlock { - let source_info = self.scopes.source_info(self.scopes.len(), self.fn_span); - let abortblk = self.cfg.start_new_cleanup_block(); - self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort); - self.cached_resume_block = Some(abortblk); - abortblk - } - // Scheduling drops // ================ crate fn schedule_drop_storage_and_value( @@ -749,11 +761,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.schedule_drop(span, region_scope, local, DropKind::Value); } - /// Indicates that `place` should be dropped on exit from - /// `region_scope`. + /// Indicates that `place` should be dropped on exit from `region_scope`. /// - /// When called with `DropKind::Storage`, `place` should be a local - /// with an index higher than the current `self.arg_count`. + /// When called with `DropKind::Storage`, `place` shouldn't be the return + /// place, or a function parameter. crate fn schedule_drop( &mut self, span: Span, @@ -781,70 +792,74 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }; - for scope in self.scopes.iter_mut() { - let this_scope = scope.region_scope == region_scope; - // When building drops, we try to cache chains of drops in such a way so these drops - // could be reused by the drops which would branch into the cached (already built) - // blocks. This, however, means that whenever we add a drop into a scope which already - // had some blocks built (and thus, cached) for it, we must invalidate all caches which - // might branch into the scope which had a drop just added to it. This is necessary, - // because otherwise some other code might use the cache to branch into already built - // chain of drops, essentially ignoring the newly added drop. - // - // For example consider there’s two scopes with a drop in each. These are built and - // thus the caches are filled: - // - // +--------------------------------------------------------+ - // | +---------------------------------+ | - // | | +--------+ +-------------+ | +---------------+ | - // | | | return | <-+ | drop(outer) | <-+ | drop(middle) | | - // | | +--------+ +-------------+ | +---------------+ | - // | +------------|outer_scope cache|--+ | - // +------------------------------|middle_scope cache|------+ - // - // Now, a new, inner-most scope is added along with a new drop into both inner-most and - // outer-most scopes: - // - // +------------------------------------------------------------+ - // | +----------------------------------+ | - // | | +--------+ +-------------+ | +---------------+ | +-------------+ - // | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) | - // | | +--------+ | | drop(outer) | | +---------------+ | +-------------+ - // | | +-+ +-------------+ | | - // | +---|invalid outer_scope cache|----+ | - // +----=----------------|invalid middle_scope cache|-----------+ - // - // If, when adding `drop(new)` we do not invalidate the cached blocks for both - // outer_scope and middle_scope, then, when building drops for the inner (right-most) - // scope, the old, cached blocks, without `drop(new)` will get used, producing the - // wrong results. - // - // The cache and its invalidation for unwind branch is somewhat special. The cache is - // per-drop, rather than per scope, which has a several different implications. Adding - // a new drop into a scope will not invalidate cached blocks of the prior drops in the - // scope. That is true, because none of the already existing drops will have an edge - // into a block with the newly added drop. - // - // Note that this code iterates scopes from the inner-most to the outer-most, - // invalidating caches of each scope visited. This way bare minimum of the - // caches gets invalidated. i.e., if a new drop is added into the middle scope, the - // cache of outer scope stays intact. - scope.invalidate_cache(!needs_drop, self.generator_kind, this_scope); - if this_scope { + // When building drops, we try to cache chains of drops to reduce the + // number of `DropTree::add_drop` calls. This, however, means that + // whenever we add a drop into a scope which already had some entries + // in the drop tree built (and thus, cached) for it, we must invalidate + // all caches which might branch into the scope which had a drop just + // added to it. This is necessary, because otherwise some other code + // might use the cache to branch into already built chain of drops, + // essentially ignoring the newly added drop. + // + // For example consider there’s two scopes with a drop in each. These + // are built and thus the caches are filled: + // + // +--------------------------------------------------------+ + // | +---------------------------------+ | + // | | +--------+ +-------------+ | +---------------+ | + // | | | return | <-+ | drop(outer) | <-+ | drop(middle) | | + // | | +--------+ +-------------+ | +---------------+ | + // | +------------|outer_scope cache|--+ | + // +------------------------------|middle_scope cache|------+ + // + // Now, a new, inner-most scope is added along with a new drop into + // both inner-most and outer-most scopes: + // + // +------------------------------------------------------------+ + // | +----------------------------------+ | + // | | +--------+ +-------------+ | +---------------+ | +-------------+ + // | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) | + // | | +--------+ | | drop(outer) | | +---------------+ | +-------------+ + // | | +-+ +-------------+ | | + // | +---|invalid outer_scope cache|----+ | + // +----=----------------|invalid middle_scope cache|-----------+ + // + // If, when adding `drop(new)` we do not invalidate the cached blocks for both + // outer_scope and middle_scope, then, when building drops for the inner (right-most) + // scope, the old, cached blocks, without `drop(new)` will get used, producing the + // wrong results. + // + // Note that this code iterates scopes from the inner-most to the outer-most, + // invalidating caches of each scope visited. This way bare minimum of the + // caches gets invalidated. i.e., if a new drop is added into the middle scope, the + // cache of outer scope stays intact. + // + // Since we only cache drops for the unwind path and the generator drop + // path, we only need to invalidate the cache for drops that happen on + // the unwind or generator drop paths. This means that for + // non-generators we don't need to invalidate caches for `DropKind::Storage`. + let invalidate_caches = needs_drop || self.generator_kind.is_some(); + for scope in self.scopes.scopes.iter_mut().rev() { + if invalidate_caches { + scope.invalidate_cache(); + } + + if scope.region_scope == region_scope { let region_scope_span = region_scope.span(self.hir.tcx(), &self.hir.region_scope_tree); // Attribute scope exit drops to scope's closing brace. let scope_end = self.hir.tcx().sess.source_map().end_point(region_scope_span); scope.drops.push(DropData { - span: scope_end, + source_info: SourceInfo { span: scope_end, scope: scope.source_scope }, local, kind: drop_kind, - cached_block: CachedBlock::default(), }); + return; } } + span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local); } @@ -892,9 +907,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } Some(local_scope) => self + .scopes .scopes .iter_mut() - .find(|scope| scope.region_scope == local_scope) + .rfind(|scope| scope.region_scope == local_scope) .unwrap_or_else(|| bug!("scope {:?} not found in scope list!", local_scope)), }; @@ -944,13 +960,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Manually drop the condition on both branches. let top_scope = self.scopes.scopes.last_mut().unwrap(); let top_drop_data = top_scope.drops.pop().unwrap(); + if self.generator_kind.is_some() { + top_scope.invalidate_cache(); + } match top_drop_data.kind { DropKind::Value { .. } => { bug!("Drop scheduled on top of condition variable") } DropKind::Storage => { - let source_info = top_scope.source_info(top_drop_data.span); + let source_info = top_drop_data.source_info; let local = top_drop_data.local; assert_eq!(local, cond_temp, "Drop scheduled on top of condition"); self.cfg.push( @@ -963,8 +982,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } } - - top_scope.invalidate_cache(true, self.generator_kind, true); } else { bug!("Expected as_local_operand to produce a temporary"); } @@ -974,62 +991,86 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (true_block, false_block) } - /// Creates a path that performs all required cleanup for unwinding. - /// - /// This path terminates in Resume. Returns the start of the path. - /// See module comment for more details. - crate fn diverge_cleanup(&mut self) -> BasicBlock { - self.diverge_cleanup_gen(false) - } - - fn resume_block(&mut self) -> BasicBlock { - if let Some(target) = self.cached_resume_block { - target - } else { - let resumeblk = self.cfg.start_new_cleanup_block(); - self.cfg.terminate( - resumeblk, - SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span: self.fn_span }, - TerminatorKind::Resume, - ); - self.cached_resume_block = Some(resumeblk); - resumeblk + /// Returns the [DropIdx] for the innermost drop if the function unwound at + /// this point. The `DropIdx` will be created if it doesn't already exist. + fn diverge_cleanup(&mut self) -> DropIdx { + let is_generator = self.generator_kind.is_some(); + let (uncached_scope, mut cached_drop) = self + .scopes + .scopes + .iter() + .enumerate() + .rev() + .find_map(|(scope_idx, scope)| { + scope.cached_unwind_block.map(|cached_block| (scope_idx + 1, cached_block)) + }) + .unwrap_or((0, ROOT_NODE)); + + for scope in &mut self.scopes.scopes[uncached_scope..] { + for drop in &scope.drops { + if is_generator || drop.kind == DropKind::Value { + cached_drop = self.scopes.unwind_drops.add_drop(*drop, cached_drop); + } + } + scope.cached_unwind_block = Some(cached_drop); } + + cached_drop } - fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock { - // Build up the drops in **reverse** order. The end result will - // look like: - // - // scopes[n] -> scopes[n-1] -> ... -> scopes[0] - // - // However, we build this in **reverse order**. That is, we - // process scopes[0], then scopes[1], etc, pointing each one at - // the result generates from the one before. Along the way, we - // store caches. If everything is cached, we'll just walk right - // to left reading the cached results but never created anything. - - // Find the last cached block - debug!("diverge_cleanup_gen(self.scopes = {:?})", self.scopes); - let cached_cleanup = self.scopes.iter_mut().enumerate().find_map(|(idx, ref scope)| { - let cached_block = scope.cached_unwind.get(generator_drop)?; - Some((cached_block, idx)) - }); - let (mut target, first_uncached) = - cached_cleanup.unwrap_or_else(|| (self.resume_block(), self.scopes.len())); + /// Prepares to create a path that performs all required cleanup for a + /// terminator that can unwind at the given basic block. + /// + /// This path terminates in Resume. The path isn't created until after all + /// of the non-unwind paths in this item have been lowered. + crate fn diverge_from(&mut self, start: BasicBlock) { + debug_assert!( + matches!( + self.cfg.block_data(start).terminator().kind, + TerminatorKind::Assert { .. } + | TerminatorKind::Call {..} + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::FalseUnwind { .. } + ), + "diverge_from called on block with terminator that cannot unwind." + ); - for scope in self.scopes.top_scopes(first_uncached) { - target = build_diverge_scope( - &mut self.cfg, - scope.region_scope_span, - scope, - target, - generator_drop, - self.generator_kind, - ); + let next_drop = self.diverge_cleanup(); + self.scopes.unwind_drops.add_entry(start, next_drop); + } + + /// Sets up a path that performs all required cleanup for dropping a + /// generator, starting from the given block that ends in + /// [TerminatorKind::Yield]. + /// + /// This path terminates in GeneratorDrop. + crate fn generator_drop_cleanup(&mut self, yield_block: BasicBlock) { + debug_assert!( + matches!( + self.cfg.block_data(yield_block).terminator().kind, + TerminatorKind::Yield { .. } + ), + "generator_drop_cleanup called on block with non-yield terminator." + ); + let (uncached_scope, mut cached_drop) = self + .scopes + .scopes + .iter() + .enumerate() + .rev() + .find_map(|(scope_idx, scope)| { + scope.cached_generator_drop_block.map(|cached_block| (scope_idx + 1, cached_block)) + }) + .unwrap_or((0, ROOT_NODE)); + + for scope in &mut self.scopes.scopes[uncached_scope..] { + for drop in &scope.drops { + cached_drop = self.scopes.generator_drops.add_drop(*drop, cached_drop); + } + scope.cached_generator_drop_block = Some(cached_drop); } - target + self.scopes.generator_drops.add_entry(yield_block, cached_drop); } /// Utility function for *non*-scope code to build their own drops @@ -1042,21 +1083,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> BlockAnd<()> { let source_info = self.source_info(span); let next_target = self.cfg.start_new_block(); - let diverge_target = self.diverge_cleanup(); + self.cfg.terminate( block, source_info, - TerminatorKind::DropAndReplace { - location, - value, - target: next_target, - unwind: Some(diverge_target), - }, + TerminatorKind::DropAndReplace { location, value, target: next_target, unwind: None }, ); + self.diverge_from(block); + next_target.unit() } - /// Creates an Assert terminator and return the success block. + /// Creates an `Assert` terminator and return the success block. /// If the boolean condition operand is not the expected value, /// a runtime panic will be caused with the given message. crate fn assert( @@ -1068,51 +1106,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, ) -> BasicBlock { let source_info = self.source_info(span); - let success_block = self.cfg.start_new_block(); - let cleanup = self.diverge_cleanup(); self.cfg.terminate( block, source_info, - TerminatorKind::Assert { - cond, - expected, - msg, - target: success_block, - cleanup: Some(cleanup), - }, + TerminatorKind::Assert { cond, expected, msg, target: success_block, cleanup: None }, ); + self.diverge_from(block); success_block } - // `match` arm scopes - // ================== /// Unschedules any drops in the top scope. /// /// This is only needed for `match` arm scopes, because they have one /// entrance per pattern, but only one exit. - pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) { + crate fn clear_top_scope(&mut self, region_scope: region::Scope) { let top_scope = self.scopes.scopes.last_mut().unwrap(); assert_eq!(top_scope.region_scope, region_scope); top_scope.drops.clear(); - top_scope.invalidate_cache(false, self.generator_kind, true); + top_scope.invalidate_cache(); } } -/// Builds drops for pop_scope and exit_scope. +/// Builds drops for `pop_scope` and `leave_top_scope`. fn build_scope_drops<'tcx>( cfg: &mut CFG<'tcx>, - generator_kind: Option, + unwind_drops: &mut DropTree, scope: &Scope, mut block: BasicBlock, - last_unwind_to: BasicBlock, + mut unwind_to: DropIdx, + storage_dead_on_unwind: bool, arg_count: usize, - generator_drop: bool, - is_cached_path: bool, ) -> BlockAnd<()> { debug!("build_scope_drops({:?} -> {:?})", block, scope); @@ -1135,37 +1163,43 @@ fn build_scope_drops<'tcx>( // drops for the unwind path should have already been generated by // `diverge_cleanup_gen`. - for drop_idx in (0..scope.drops.len()).rev() { - let drop_data = &scope.drops[drop_idx]; - let source_info = scope.source_info(drop_data.span); + for drop_data in scope.drops.iter().rev() { + let source_info = drop_data.source_info; let local = drop_data.local; match drop_data.kind { DropKind::Value => { + // `unwind_to` should drop the value that we're about to + // schedule. If dropping this value panics, then we continue + // with the *next* value on the unwind path. + debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local); + debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind); + unwind_to = unwind_drops.drops[unwind_to].1; + // 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 !is_cached_path && scope.moved_locals.iter().any(|&o| o == local) { + if scope.moved_locals.iter().any(|&o| o == local) { continue; } - let unwind_to = get_unwind_to(scope, generator_kind, drop_idx, generator_drop) - .unwrap_or(last_unwind_to); + unwind_drops.add_entry(block, unwind_to); let next = cfg.start_new_block(); cfg.terminate( block, source_info, - TerminatorKind::Drop { - location: local.into(), - target: next, - unwind: Some(unwind_to), - }, + TerminatorKind::Drop { location: local.into(), target: next, unwind: None }, ); block = next; } DropKind::Storage => { + if storage_dead_on_unwind { + debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local); + debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind); + unwind_to = unwind_drops.drops[unwind_to].1; + } // Only temps and vars need their storage dead. assert!(local.index() > arg_count); cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) }); @@ -1175,139 +1209,189 @@ fn build_scope_drops<'tcx>( block.unit() } -fn get_unwind_to( - scope: &Scope, - generator_kind: Option, - unwind_from: usize, - generator_drop: bool, -) -> Option { - for drop_idx in (0..unwind_from).rev() { - let drop_data = &scope.drops[drop_idx]; - match (generator_kind, &drop_data.kind) { - (Some(_), DropKind::Storage) => { - return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| { - span_bug!(drop_data.span, "cached block not present for {:?}", drop_data) - })); - } - (None, DropKind::Value) => { - return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| { - span_bug!(drop_data.span, "cached block not present for {:?}", drop_data) - })); +impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { + /// Build a drop tree for a breakable scope. + /// + /// If `continue_block` is `Some`, then the tree is for `continue` inside a + /// loop. Otherwise this is for `break` or `return`. + fn build_exit_tree( + &mut self, + mut drops: DropTree, + 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); + + // Link the exit drop tree to unwind drop tree. + if drops.drops.iter().any(|(drop, _)| drop.kind == DropKind::Value) { + let unwind_target = self.diverge_cleanup(); + let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1); + for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) { + match drop_data.0.kind { + DropKind::Storage => { + if self.generator_kind.is_some() { + let unwind_drop = self + .scopes + .unwind_drops + .add_drop(drop_data.0, unwind_indices[drop_data.1]); + unwind_indices.push(unwind_drop); + } else { + unwind_indices.push(unwind_indices[drop_data.1]); + } + } + DropKind::Value => { + let unwind_drop = self + .scopes + .unwind_drops + .add_drop(drop_data.0, unwind_indices[drop_data.1]); + self.scopes + .unwind_drops + .add_entry(blocks[drop_idx].unwrap(), unwind_indices[drop_data.1]); + unwind_indices.push(unwind_drop); + } + } } - _ => (), } + blocks[ROOT_NODE].map(BasicBlock::unit) } - None -} -fn build_diverge_scope<'tcx>( - cfg: &mut CFG<'tcx>, - span: Span, - scope: &mut Scope, - mut target: BasicBlock, - generator_drop: bool, - generator_kind: Option, -) -> BasicBlock { - // Build up the drops in **reverse** order. The end result will - // look like: - // - // [drops[n]] -...-> [drops[0]] -> [target] - // - // The code in this function reads from right to left. At each - // point, we check for cached blocks representing the - // remainder. If everything is cached, we'll just walk right to - // left reading the cached results but never create anything. - - let source_scope = scope.source_scope; - let source_info = |span| SourceInfo { span, scope: source_scope }; - - // We keep track of StorageDead statements to prepend to our current block - // and store them here, in reverse order. - let mut storage_deads = vec![]; - - let mut target_built_by_us = false; - - // Build up the drops. Here we iterate the vector in - // *forward* order, so that we generate drops[0] first (right to - // left in diagram above). - debug!("build_diverge_scope({:?})", scope.drops); - for (j, drop_data) in scope.drops.iter_mut().enumerate() { - debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data); - // Only full value drops are emitted in the diverging path, - // not StorageDead, except in the case of generators. + /// Build the unwind and generator drop trees. + crate fn build_drop_trees(&mut self, should_abort: bool) { + if self.generator_kind.is_some() { + self.build_generator_drop_trees(should_abort); + } else { + Self::build_unwind_tree( + &mut self.cfg, + &mut self.scopes.unwind_drops, + self.fn_span, + should_abort, + &mut None, + ); + } + } + + fn build_generator_drop_trees(&mut self, should_abort: bool) { + // Build the drop tree for dropping the generator while it's suspended. + let drops = &mut self.scopes.generator_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); + if let Some(root_block) = blocks[ROOT_NODE] { + cfg.terminate( + root_block, + SourceInfo::outermost(fn_span), + TerminatorKind::GeneratorDrop, + ); + } + + // Build the drop tree for unwinding in the normal control flow paths. + let resume_block = &mut None; + let unwind_drops = &mut self.scopes.unwind_drops; + Self::build_unwind_tree(cfg, unwind_drops, fn_span, should_abort, resume_block); + + // Build the drop tree for unwinding when dropping a suspended + // generator. // - // Note: This may not actually be what we desire (are we - // "freeing" stack storage as we unwind, or merely observing a - // frozen stack)? In particular, the intent may have been to - // match the behavior of clang, but on inspection eddyb says - // this is not what clang does. - match drop_data.kind { - DropKind::Storage if generator_kind.is_some() => { - storage_deads.push(Statement { - source_info: source_info(drop_data.span), - kind: StatementKind::StorageDead(drop_data.local), - }); - if !target_built_by_us { - // We cannot add statements to an existing block, so we create a new - // block for our StorageDead statements. - let block = cfg.start_new_cleanup_block(); - let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope }; - cfg.goto(block, source_info, target); - target = block; - target_built_by_us = true; - } - *drop_data.cached_block.ref_mut(generator_drop) = Some(target); + // This is a different tree to the standard unwind paths here to + // prevent drop elaboration from creating drop flags that would have + // to be captured by the generator. I'm not sure how important this + // optimization is, but it is here. + for (drop_idx, drop_data) in drops.drops.iter_enumerated() { + if let DropKind::Value = drop_data.0.kind { + debug_assert!(drop_data.1 < drops.drops.next_index()); + drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap())); } - DropKind::Storage => {} - DropKind::Value => { - let cached_block = drop_data.cached_block.ref_mut(generator_drop); - target = if let Some(cached_block) = *cached_block { - storage_deads.clear(); - target_built_by_us = false; - cached_block - } else { - push_storage_deads(cfg, target, &mut storage_deads); - let block = cfg.start_new_cleanup_block(); - cfg.terminate( - block, - source_info(drop_data.span), - TerminatorKind::Drop { - location: drop_data.local.into(), - target, - unwind: None, - }, - ); - *cached_block = Some(block); - target_built_by_us = true; - block - }; - } - }; + } + Self::build_unwind_tree(cfg, drops, fn_span, should_abort, resume_block); } - push_storage_deads(cfg, target, &mut storage_deads); - *scope.cached_unwind.ref_mut(generator_drop) = Some(target); - assert!(storage_deads.is_empty()); - debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target); + fn build_unwind_tree( + cfg: &mut CFG<'tcx>, + drops: &mut DropTree, + fn_span: Span, + should_abort: bool, + resume_block: &mut Option, + ) { + let mut blocks = IndexVec::from_elem(None, &drops.drops); + blocks[ROOT_NODE] = *resume_block; + drops.build_mir::(cfg, &mut blocks); + if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) { + // `TerminatorKind::Abort` is used for `#[unwind(aborts)]` + // functions. + let terminator = + if should_abort { TerminatorKind::Abort } else { TerminatorKind::Resume }; + + cfg.terminate(resume, SourceInfo::outermost(fn_span), terminator); + + *resume_block = blocks[ROOT_NODE]; + } + } +} - target +// DropTreeBuilder implementations. + +struct ExitScopes; + +impl<'tcx> DropTreeBuilder<'tcx> for ExitScopes { + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock { + cfg.start_new_block() + } + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) { + cfg.block_data_mut(from).terminator_mut().kind = TerminatorKind::Goto { target: to }; + } } -fn push_storage_deads<'tcx>( - cfg: &mut CFG<'tcx>, - target: BasicBlock, - storage_deads: &mut Vec>, -) { - if storage_deads.is_empty() { - return; +struct GeneratorDrop; + +impl<'tcx> DropTreeBuilder<'tcx> for GeneratorDrop { + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock { + cfg.start_new_block() + } + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) { + let term = cfg.block_data_mut(from).terminator_mut(); + if let TerminatorKind::Yield { ref mut drop, .. } = term.kind { + *drop = Some(to); + } else { + span_bug!( + term.source_info.span, + "cannot enter generator drop tree from {:?}", + term.kind + ) + } + } +} + +struct Unwind; + +impl<'tcx> DropTreeBuilder<'tcx> for Unwind { + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock { + cfg.start_new_cleanup_block() + } + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) { + let term = &mut cfg.block_data_mut(from).terminator_mut(); + match &mut term.kind { + TerminatorKind::Drop { unwind, .. } + | TerminatorKind::DropAndReplace { unwind, .. } + | TerminatorKind::FalseUnwind { unwind, .. } + | TerminatorKind::Call { cleanup: unwind, .. } + | TerminatorKind::Assert { cleanup: unwind, .. } => { + *unwind = Some(to); + } + TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::InlineAsm { .. } => { + span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind) + } + } } - let statements = &mut cfg.block_data_mut(target).statements; - storage_deads.reverse(); - debug!( - "push_storage_deads({:?}), storage_deads={:?}, statements={:?}", - target, storage_deads, statements - ); - storage_deads.append(statements); - mem::swap(statements, storage_deads); - assert!(storage_deads.is_empty()); } diff --git a/src/librustc_mir_build/hair/constant.rs b/src/librustc_mir_build/hair/constant.rs index d5e5398b9d41e..e5af0b5bd6bed 100644 --- a/src/librustc_mir_build/hair/constant.rs +++ b/src/librustc_mir_build/hair/constant.rs @@ -1,9 +1,10 @@ -use rustc::mir::interpret::{ +use rustc_ast::ast; +use rustc_middle::mir::interpret::{ truncate, Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar, }; -use rustc::ty::{self, layout::Size, ParamEnv, TyCtxt, TyS}; -use rustc_ast::ast; +use rustc_middle::ty::{self, ParamEnv, TyCtxt, TyS}; use rustc_span::symbol::Symbol; +use rustc_target::abi::Size; crate fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/src/librustc_mir_build/hair/cx/block.rs b/src/librustc_mir_build/hair/cx/block.rs index 07a9d91cd746d..c7b53024666d9 100644 --- a/src/librustc_mir_build/hair/cx/block.rs +++ b/src/librustc_mir_build/hair/cx/block.rs @@ -2,9 +2,9 @@ use crate::hair::cx::to_ref::ToRef; use crate::hair::cx::Cx; use crate::hair::{self, *}; -use rustc::middle::region; -use rustc::ty; use rustc_hir as hir; +use rustc_middle::middle::region; +use rustc_middle::ty; use rustc_index::vec::Idx; diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs index 9f93b817e38d8..99b59d16029ff 100644 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ b/src/librustc_mir_build/hair/cx/expr.rs @@ -3,14 +3,16 @@ use crate::hair::cx::to_ref::ToRef; use crate::hair::cx::Cx; use crate::hair::util::UserAnnotatedTyHelpers; use crate::hair::*; -use rustc::mir::interpret::Scalar; -use rustc::mir::BorrowKind; -use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast}; -use rustc::ty::subst::{InternalSubsts, SubstsRef}; -use rustc::ty::{self, AdtKind, Ty}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_index::vec::Idx; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::BorrowKind; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast, +}; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::{self, AdtKind, Ty}; use rustc_span::Span; impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr<'tcx> { @@ -399,6 +401,121 @@ fn make_mirror_unadjusted<'a, 'tcx>( } hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm { + template: asm.template, + operands: asm + .operands + .iter() + .map(|op| { + match *op { + hir::InlineAsmOperand::In { reg, ref expr } => { + InlineAsmOperand::In { reg, expr: expr.to_ref() } + } + hir::InlineAsmOperand::Out { reg, late, ref expr } => { + InlineAsmOperand::Out { + reg, + late, + expr: expr.as_ref().map(|expr| expr.to_ref()), + } + } + hir::InlineAsmOperand::InOut { reg, late, ref expr } => { + InlineAsmOperand::InOut { reg, late, expr: expr.to_ref() } + } + hir::InlineAsmOperand::SplitInOut { + reg, + late, + ref in_expr, + ref out_expr, + } => InlineAsmOperand::SplitInOut { + reg, + late, + in_expr: in_expr.to_ref(), + out_expr: out_expr.as_ref().map(|expr| expr.to_ref()), + }, + hir::InlineAsmOperand::Const { ref expr } => { + InlineAsmOperand::Const { expr: expr.to_ref() } + } + hir::InlineAsmOperand::Sym { ref expr } => { + let qpath = match expr.kind { + hir::ExprKind::Path(ref qpath) => qpath, + _ => span_bug!( + expr.span, + "asm `sym` operand should be a path, found {:?}", + expr.kind + ), + }; + let temp_lifetime = + cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let res = cx.tables().qpath_res(qpath, expr.hir_id); + let ty; + match res { + Res::Def(DefKind::Fn, _) | Res::Def(DefKind::AssocFn, _) => { + ty = cx.tables().node_type(expr.hir_id); + let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); + InlineAsmOperand::SymFn { + expr: Expr { + ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx, ty), + user_ty, + }, + } + .to_ref(), + } + } + + Res::Def(DefKind::Static, id) => { + ty = cx.tcx.static_ptr_ty(id); + let ptr = cx.tcx.create_static_alloc(id); + InlineAsmOperand::SymStatic { + expr: Expr { + ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::StaticRef { + literal: ty::Const::from_scalar( + cx.tcx, + Scalar::Ptr(ptr.into()), + ty, + ), + def_id: id, + }, + } + .to_ref(), + } + } + + _ => { + cx.tcx.sess.span_err( + expr.span, + "asm `sym` operand must point to a fn or static", + ); + + // Not a real fn, but we're not reaching codegen anyways... + ty = cx.tcx.types.err; + InlineAsmOperand::SymFn { + expr: Expr { + ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx, ty), + user_ty: None, + }, + } + .to_ref(), + } + } + } + } + } + }) + .collect(), + options: asm.options, + }, + + hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { asm: &asm.inner, outputs: asm.outputs_exprs.to_ref(), inputs: asm.inputs_exprs.to_ref(), @@ -406,7 +523,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( // Now comes the rote stuff: hir::ExprKind::Repeat(ref v, ref count) => { - let count_def_id = cx.tcx.hir().local_def_id(count.hir_id).expect_local(); + let count_def_id = cx.tcx.hir().local_def_id(count.hir_id); let count = ty::Const::from_anon_const(cx.tcx, count_def_id); ExprKind::Repeat { value: v.to_ref(), count } @@ -478,7 +595,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( ) => { let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); let (d, o) = adt_def.discriminant_def_for_variant(idx); - use rustc::ty::util::IntTypeExt; + use rustc_middle::ty::util::IntTypeExt; let ty = adt_def.repr.discr_type(); let ty = ty.to_ty(cx.tcx()); Some((d, o, ty)) @@ -631,7 +748,7 @@ trait ToBorrowKind { impl ToBorrowKind for AutoBorrowMutability { fn to_borrow_kind(&self) -> BorrowKind { - use rustc::ty::adjustment::AllowTwoPhase; + use rustc_middle::ty::adjustment::AllowTwoPhase; match *self { AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut { allow_two_phase_borrow: match allow_two_phase_borrow { @@ -688,12 +805,12 @@ fn convert_path_expr<'a, 'tcx>( } Res::Def(DefKind::ConstParam, def_id) => { - let hir_id = cx.tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = cx.tcx.hir().as_local_hir_id(def_id.expect_local()); let item_id = cx.tcx.hir().get_parent_node(hir_id); let item_def_id = cx.tcx.hir().local_def_id(item_id); let generics = cx.tcx.generics_of(item_def_id); let local_def_id = cx.tcx.hir().local_def_id(hir_id); - let index = generics.param_def_id_to_index[&local_def_id]; + let index = generics.param_def_id_to_index[&local_def_id.to_def_id()]; let name = cx.tcx.hir().name(hir_id); let val = ty::ConstKind::Param(ty::ParamConst::new(index, name)); ExprKind::Literal { @@ -738,7 +855,7 @@ fn convert_path_expr<'a, 'tcx>( // a constant reference (or constant raw pointer for `static mut`) in MIR Res::Def(DefKind::Static, id) => { let ty = cx.tcx.static_ptr_ty(id); - let ptr = cx.tcx.alloc_map.lock().create_static_alloc(id); + let ptr = cx.tcx.create_static_alloc(id); let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); ExprKind::Deref { arg: Expr { @@ -960,7 +1077,7 @@ fn capture_upvar<'tcx>( ) -> ExprRef<'tcx> { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id).expect_local(), + closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id), }; let upvar_capture = cx.tables().upvar_capture(upvar_id); let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); diff --git a/src/librustc_mir_build/hair/cx/mod.rs b/src/librustc_mir_build/hair/cx/mod.rs index 99caa6a0f95b4..46607fd07cdd7 100644 --- a/src/librustc_mir_build/hair/cx/mod.rs +++ b/src/librustc_mir_build/hair/cx/mod.rs @@ -5,12 +5,6 @@ use crate::hair::util::UserAnnotatedTyHelpers; use crate::hair::*; -use rustc::middle::region; -use rustc::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc::ty::layout::VariantIdx; -use rustc::ty::subst::Subst; -use rustc::ty::subst::{GenericArg, InternalSubsts}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_ast::ast; use rustc_ast::attr; use rustc_hir as hir; @@ -18,7 +12,13 @@ use rustc_hir::def_id::DefId; use rustc_hir::Node; use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; +use rustc_middle::middle::region; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; +use rustc_target::abi::VariantIdx; use rustc_trait_selection::infer::InferCtxtExt; #[derive(Clone)] @@ -82,11 +82,11 @@ impl<'a, 'tcx> Cx<'a, 'tcx> { infcx, root_lint_level: src_id, param_env: tcx.param_env(src_def_id), - identity_substs: InternalSubsts::identity_for_item(tcx, src_def_id), + identity_substs: InternalSubsts::identity_for_item(tcx, src_def_id.to_def_id()), region_scope_tree: tcx.region_scope_tree(src_def_id), tables, constness, - body_owner: src_def_id, + body_owner: src_def_id.to_def_id(), body_owner_kind, check_overflow, control_flow_destroyed: Vec::new(), @@ -176,7 +176,7 @@ impl<'a, 'tcx> Cx<'a, 'tcx> { .tcx .associated_items(trait_def_id) .filter_by_name_unhygienic(method_name) - .find(|item| item.kind == ty::AssocKind::Method) + .find(|item| item.kind == ty::AssocKind::Fn) .expect("trait method not found"); let method_ty = self.tcx.type_of(item.def_id); diff --git a/src/librustc_mir_build/hair/mod.rs b/src/librustc_mir_build/hair/mod.rs index 77042240acf32..aba7a7a1b420c 100644 --- a/src/librustc_mir_build/hair/mod.rs +++ b/src/librustc_mir_build/hair/mod.rs @@ -5,16 +5,18 @@ //! structures. use self::cx::Cx; -use rustc::infer::canonical::Canonical; -use rustc::middle::region; -use rustc::mir::{BinOp, BorrowKind, Field, UnOp}; -use rustc::ty::adjustment::PointerCast; -use rustc::ty::layout::VariantIdx; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType}; +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::infer::canonical::Canonical; +use rustc_middle::middle::region; +use rustc_middle::mir::{BinOp, BorrowKind, Field, UnOp}; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType}; use rustc_span::Span; +use rustc_target::abi::VariantIdx; +use rustc_target::asm::InlineAsmRegOrRegClass; crate mod constant; crate mod cx; @@ -278,7 +280,12 @@ crate enum ExprKind<'tcx> { def_id: DefId, }, InlineAsm { - asm: &'tcx hir::InlineAsmInner, + template: &'tcx [InlineAsmTemplatePiece], + operands: Vec>, + options: InlineAsmOptions, + }, + LlvmInlineAsm { + asm: &'tcx hir::LlvmInlineAsmInner, outputs: Vec>, inputs: Vec>, }, @@ -335,6 +342,39 @@ impl<'tcx> ExprRef<'tcx> { } } +#[derive(Clone, Debug)] +crate enum InlineAsmOperand<'tcx> { + In { + reg: InlineAsmRegOrRegClass, + expr: ExprRef<'tcx>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: ExprRef<'tcx>, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_expr: ExprRef<'tcx>, + out_expr: Option>, + }, + Const { + expr: ExprRef<'tcx>, + }, + SymFn { + expr: ExprRef<'tcx>, + }, + SymStatic { + expr: ExprRef<'tcx>, + }, +} + /////////////////////////////////////////////////////////////////////////// // The Mirror trait diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index 89063a4227fa9..626e531c807b7 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -1,5 +1,5 @@ -/// Note: most tests relevant to this file can be found (at the time of writing) -/// in src/tests/ui/pattern/usefulness. +/// Note: most of the tests relevant to this file can be found (at the time of writing) in +/// src/tests/ui/pattern/usefulness. /// /// This file includes the logic for exhaustiveness and usefulness checking for /// pattern-matching. Specifically, given a list of patterns for a type, we can @@ -13,6 +13,8 @@ /// summarise the algorithm here to hopefully save time and be a little clearer /// (without being so rigorous). /// +/// # Premise +/// /// The core of the algorithm revolves about a "usefulness" check. In particular, we /// are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as /// a matrix). `U(P, p)` represents whether, given an existing list of patterns @@ -27,8 +29,51 @@ /// pattern to those that have come before it doesn't increase the number of values /// we're matching). /// +/// # Core concept +/// +/// The idea that powers everything that is done in this file is the following: a value is made +/// from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)` +/// (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the +/// constructor for the number `2`). Fields are just a (possibly empty) list of values. +/// +/// Some of the constructors listed above might feel weird: `None` and `2` don't take any +/// arguments. This is part of what makes constructors so general: we will consider plain values +/// like numbers and string literals to be constructors that take no arguments, also called "0-ary +/// constructors"; they are the simplest case of constructors. This allows us to see any value as +/// made up from a tree of constructors, each having a given number of children. For example: +/// `(None, Ok(0))` is made from 4 different constructors. +/// +/// This idea can be extended to patterns: a pattern captures a set of possible values, and we can +/// describe this set using constructors. For example, `Err(_)` captures all values of the type +/// `Result` that start with the `Err` constructor (for some choice of `T` and `E`). The +/// wildcard `_` captures all values of the given type starting with any of the constructors for +/// that type. +/// +/// We use this to compute whether different patterns might capture a same value. Do the patterns +/// `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern +/// captures only values starting with the `Ok` constructor and the second only values starting +/// with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might, +/// since they both capture values starting with `Some`. To be certain, we need to dig under the +/// `Some` constructor and continue asking the question. This is the main idea behind the +/// exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently +/// figure out if some new pattern might capture a value that hadn't been captured by previous +/// patterns. +/// +/// Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum. +/// Most of the complexity of this file resides in transforming between patterns and +/// (`Constructor`, `Fields`) pairs, handling all the special cases correctly. +/// +/// Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example +/// a value of type `Rc` doesn't fit this idea very well, nor do various other things. +/// However, this idea covers most of the cases that are relevant to exhaustiveness checking. +/// +/// +/// # Algorithm +/// +/// Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`, +/// adding a new pattern `p` will cover previously-uncovered values of the type. /// During the course of the algorithm, the rows of the matrix won't just be individual patterns, -/// but rather partially-deconstructed patterns in the form of a list of patterns. The paper +/// but rather partially-deconstructed patterns in the form of a list of fields. The paper /// calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the /// new pattern `p`. /// @@ -235,16 +280,17 @@ use rustc_index::vec::Idx; use super::{compare_const_vals, PatternFoldable, PatternFolder}; use super::{FieldPat, Pat, PatKind, PatRange}; -use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; -use rustc::mir::Field; -use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; -use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef}; -use rustc::util::common::ErrorReported; use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; +use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::Field; +use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::{Integer, Size, VariantIdx}; use arena::TypedArena; @@ -255,7 +301,6 @@ use std::convert::TryInto; use std::fmt; use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; -use std::u128; crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) @@ -286,7 +331,7 @@ impl<'tcx> LiteralExpander<'tcx> { (ConstValue::Scalar(p), x, y) if x == y => { match p { Scalar::Ptr(p) => { - let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id); + let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory(); ConstValue::ByRef { alloc, offset: p.offset } } Scalar::Raw { .. } => { @@ -305,7 +350,7 @@ impl<'tcx> LiteralExpander<'tcx> { (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { assert_eq!(t, u); ConstValue::Slice { - data: self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id), + data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), start: p.offset.bytes().try_into().unwrap(), end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), } @@ -441,13 +486,11 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { &self, cx: &mut MatchCheckCtxt<'p, 'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &'p [Pat<'tcx>], + ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Option> { - let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); - new_heads.map(|mut new_head| { - new_head.0.extend_from_slice(&self.0[1..]); - new_head - }) + let new_fields = + specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; + Some(new_fields.push_on_patstack(&self.0[1..])) } } @@ -503,7 +546,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { &self, cx: &mut MatchCheckCtxt<'p, 'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &'p [Pat<'tcx>], + ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Matrix<'p, 'tcx> { self.0 .iter() @@ -513,6 +556,8 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } /// Pretty-printer for matrices of patterns, example: +/// +/// ```text /// +++++++++++++++++++++++++++++ /// + _ + [] + /// +++++++++++++++++++++++++++++ @@ -578,22 +623,11 @@ crate struct MatchCheckCtxt<'a, 'tcx> { /// outside it's module and should not be matchable with an empty match /// statement. crate module: DefId, - param_env: ty::ParamEnv<'tcx>, + crate param_env: ty::ParamEnv<'tcx>, crate pattern_arena: &'a TypedArena>, } impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { - crate fn create_and_enter( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - module: DefId, - f: impl FnOnce(MatchCheckCtxt<'_, 'tcx>) -> R, - ) -> R { - let pattern_arena = TypedArena::default(); - - f(MatchCheckCtxt { tcx, param_env, module, pattern_arena: &pattern_arena }) - } - fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) @@ -602,7 +636,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } - // Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind { ty::Adt(def, ..) => { @@ -611,15 +645,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { _ => false, } } - - // Returns whether the given variant is from another crate and has its fields declared - // `#[non_exhaustive]`. - fn is_foreign_non_exhaustive_variant(&self, ty: Ty<'tcx>, variant: &VariantDef) -> bool { - match ty.kind { - ty::Adt(def, ..) => variant.is_field_list_non_exhaustive() && !def.did.is_local(), - _ => false, - } - } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -731,10 +756,17 @@ impl Slice { } } +/// A value can be decomposed into a constructor applied to some fields. This struct represents +/// the constructor. See also `Fields`. +/// +/// `pat_constructor` retrieves the constructor corresponding to a pattern. +/// `specialize_one_pattern` returns the list of fields corresponding to a pattern, given a +/// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and +/// `Fields`. #[derive(Clone, Debug, PartialEq)] enum Constructor<'tcx> { - /// The constructor of all patterns that don't vary by constructor, - /// e.g., struct patterns and fixed-length arrays. + /// The constructor for patterns that have a single constructor, like tuples, struct patterns + /// and fixed-length arrays. Single, /// Enum variants. Variant(DefId), @@ -859,107 +891,10 @@ impl<'tcx> Constructor<'tcx> { } } - /// This returns one wildcard pattern for each argument to this constructor. - /// - /// This must be consistent with `apply`, `specialize_one_pattern`, and `arity`. - fn wildcard_subpatterns<'a>( - &self, - cx: &MatchCheckCtxt<'a, 'tcx>, - ty: Ty<'tcx>, - ) -> Vec> { - debug!("wildcard_subpatterns({:#?}, {:?})", self, ty); - - match self { - Single | Variant(_) => match ty.kind { - ty::Tuple(ref fs) => { - fs.into_iter().map(|t| t.expect_ty()).map(Pat::wildcard_from_ty).collect() - } - ty::Ref(_, rty, _) => vec![Pat::wildcard_from_ty(rty)], - ty::Adt(adt, substs) => { - if adt.is_box() { - // Use T as the sub pattern type of Box. - vec![Pat::wildcard_from_ty(substs.type_at(0))] - } else { - let variant = &adt.variants[self.variant_index_for_adt(cx, adt)]; - let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(ty, variant); - variant - .fields - .iter() - .map(|field| { - let is_visible = adt.is_enum() - || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs)); - match (is_visible, is_non_exhaustive, is_uninhabited) { - // Treat all uninhabited types in non-exhaustive variants as - // `TyErr`. - (_, true, true) => cx.tcx.types.err, - // Treat all non-visible fields as `TyErr`. They can't appear - // in any other pattern from this match (because they are - // private), so their type does not matter - but we don't want - // to know they are uninhabited. - (false, ..) => cx.tcx.types.err, - (true, ..) => { - let ty = field.ty(cx.tcx, substs); - match ty.kind { - // If the field type returned is an array of an unknown - // size return an TyErr. - ty::Array(_, len) - if len - .try_eval_usize(cx.tcx, cx.param_env) - .is_none() => - { - cx.tcx.types.err - } - _ => ty, - } - } - } - }) - .map(Pat::wildcard_from_ty) - .collect() - } - } - _ => vec![], - }, - Slice(_) => match ty.kind { - ty::Slice(ty) | ty::Array(ty, _) => { - let arity = self.arity(cx, ty); - (0..arity).map(|_| Pat::wildcard_from_ty(ty)).collect() - } - _ => bug!("bad slice pattern {:?} {:?}", self, ty), - }, - ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => vec![], - } - } - - /// This computes the arity of a constructor. The arity of a constructor - /// is how many subpattern patterns of that constructor should be expanded to. - /// - /// For instance, a tuple pattern `(_, 42, Some([]))` has the arity of 3. - /// A struct pattern's arity is the number of fields it contains, etc. - /// - /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `apply`. - fn arity<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> u64 { - debug!("Constructor::arity({:#?}, {:?})", self, ty); - match self { - Single | Variant(_) => match ty.kind { - ty::Tuple(ref fs) => fs.len() as u64, - ty::Slice(..) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), - ty::Ref(..) => 1, - ty::Adt(adt, _) => { - adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64 - } - _ => 0, - }, - Slice(slice) => slice.arity(), - ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => 0, - } - } - /// Apply a constructor to a list of patterns, yielding a new pattern. `pats` /// must have as many elements as this constructor's arity. /// - /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `arity`. + /// This is roughly the inverse of `specialize_one_pattern`. /// /// Examples: /// `self`: `Constructor::Single` @@ -971,13 +906,13 @@ impl<'tcx> Constructor<'tcx> { /// `ty`: `Option` /// `pats`: `[false]` /// returns `Some(false)` - fn apply<'a>( + fn apply<'p>( &self, - cx: &MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>, - pats: impl IntoIterator>, + fields: Fields<'p, 'tcx>, ) -> Pat<'tcx> { - let mut subpatterns = pats.into_iter(); + let mut subpatterns = fields.all_patterns(); let pat = match self { Single | Variant(_) => match ty.kind { @@ -1042,8 +977,263 @@ impl<'tcx> Constructor<'tcx> { /// Like `apply`, but where all the subpatterns are wildcards `_`. fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { - let subpatterns = self.wildcard_subpatterns(cx, ty).into_iter().rev(); - self.apply(cx, ty, subpatterns) + self.apply(cx, ty, Fields::wildcards(cx, self, ty)) + } +} + +/// Some fields need to be explicitly hidden away in certain cases; see the comment above the +/// `Fields` struct. This struct represents such a potentially-hidden field. When a field is hidden +/// we still keep its type around. +#[derive(Debug, Copy, Clone)] +enum FilteredField<'p, 'tcx> { + Kept(&'p Pat<'tcx>), + Hidden(Ty<'tcx>), +} + +impl<'p, 'tcx> FilteredField<'p, 'tcx> { + fn kept(self) -> Option<&'p Pat<'tcx>> { + match self { + FilteredField::Kept(p) => Some(p), + FilteredField::Hidden(_) => None, + } + } + + fn to_pattern(self) -> Pat<'tcx> { + match self { + FilteredField::Kept(p) => p.clone(), + FilteredField::Hidden(ty) => Pat::wildcard_from_ty(ty), + } + } +} + +/// A value can be decomposed into a constructor applied to some fields. This struct represents +/// those fields, generalized to allow patterns in each field. See also `Constructor`. +/// +/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is +/// uninhabited. For that, we filter these fields out of the matrix. This is subtle because we +/// still need to have those fields back when going to/from a `Pat`. Most of this is handled +/// automatically in `Fields`, but when constructing or deconstructing `Fields` you need to be +/// careful. As a rule, when going to/from the matrix, use the filtered field list; when going +/// to/from `Pat`, use the full field list. +/// This filtering is uncommon in practice, because uninhabited fields are rarely used, so we avoid +/// it when possible to preserve performance. +#[derive(Debug, Clone)] +enum Fields<'p, 'tcx> { + /// Lists of patterns that don't contain any filtered fields. + /// `Slice` and `Vec` behave the same; the difference is only to avoid allocating and + /// triple-dereferences when possible. Frankly this is premature optimization, I (Nadrieril) + /// have not measured if it really made a difference. + Slice(&'p [Pat<'tcx>]), + Vec(SmallVec<[&'p Pat<'tcx>; 2]>), + /// Patterns where some of the fields need to be hidden. `len` caches the number of non-hidden + /// fields. + Filtered { + fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>, + len: usize, + }, +} + +impl<'p, 'tcx> Fields<'p, 'tcx> { + fn empty() -> Self { + Fields::Slice(&[]) + } + + /// Construct a new `Fields` from the given pattern. Must not be used if the pattern is a field + /// of a struct/tuple/variant. + fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self { + Fields::Slice(std::slice::from_ref(pat)) + } + + /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't + /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. + fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { + Fields::Slice(pats) + } + + /// Convenience; internal use. + fn wildcards_from_tys( + cx: &MatchCheckCtxt<'p, 'tcx>, + tys: impl IntoIterator>, + ) -> Self { + let wilds = tys.into_iter().map(Pat::wildcard_from_ty); + let pats = cx.pattern_arena.alloc_from_iter(wilds); + Fields::Slice(pats) + } + + /// Creates a new list of wildcard fields for a given constructor. + fn wildcards( + cx: &MatchCheckCtxt<'p, 'tcx>, + constructor: &Constructor<'tcx>, + ty: Ty<'tcx>, + ) -> Self { + debug!("Fields::wildcards({:#?}, {:?})", constructor, ty); + let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty)); + + match constructor { + Single | Variant(_) => match ty.kind { + ty::Tuple(ref fs) => { + Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty())) + } + ty::Ref(_, rty, _) => Fields::from_single_pattern(wildcard_from_ty(rty)), + ty::Adt(adt, substs) => { + if adt.is_box() { + // Use T as the sub pattern type of Box. + Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) + } else { + let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; + // Whether we must not match the fields of this variant exhaustively. + let is_non_exhaustive = + variant.is_field_list_non_exhaustive() && !adt.did.is_local(); + let field_tys = variant.fields.iter().map(|field| field.ty(cx.tcx, substs)); + // In the following cases, we don't need to filter out any fields. This is + // the vast majority of real cases, since uninhabited fields are uncommon. + let has_no_hidden_fields = (adt.is_enum() && !is_non_exhaustive) + || !field_tys.clone().any(|ty| cx.is_uninhabited(ty)); + + if has_no_hidden_fields { + Fields::wildcards_from_tys(cx, field_tys) + } else { + let mut len = 0; + let fields = variant + .fields + .iter() + .map(|field| { + let ty = field.ty(cx.tcx, substs); + let is_visible = adt.is_enum() + || field.vis.is_accessible_from(cx.module, cx.tcx); + let is_uninhabited = cx.is_uninhabited(ty); + + // In the cases of either a `#[non_exhaustive]` field list + // or a non-public field, we hide uninhabited fields in + // order not to reveal the uninhabitedness of the whole + // variant. + if is_uninhabited && (!is_visible || is_non_exhaustive) { + FilteredField::Hidden(ty) + } else { + len += 1; + FilteredField::Kept(wildcard_from_ty(ty)) + } + }) + .collect(); + Fields::Filtered { fields, len } + } + } + } + _ => Fields::empty(), + }, + Slice(slice) => match ty.kind { + ty::Slice(ty) | ty::Array(ty, _) => { + let arity = slice.arity(); + Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) + } + _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), + }, + ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), + } + } + + fn len(&self) -> usize { + match self { + Fields::Slice(pats) => pats.len(), + Fields::Vec(pats) => pats.len(), + Fields::Filtered { len, .. } => *len, + } + } + + /// Returns the complete list of patterns, including hidden fields. + fn all_patterns(self) -> impl Iterator> { + let pats: SmallVec<[_; 2]> = match self { + Fields::Slice(pats) => pats.iter().cloned().collect(), + Fields::Vec(pats) => pats.into_iter().cloned().collect(), + Fields::Filtered { fields, .. } => { + // We don't skip any fields here. + fields.into_iter().map(|p| p.to_pattern()).collect() + } + }; + pats.into_iter() + } + + /// Overrides some of the fields with the provided patterns. Exactly like + /// `replace_fields_indexed`, except that it takes `FieldPat`s as input. + fn replace_with_fieldpats( + &self, + new_pats: impl IntoIterator>, + ) -> Self { + self.replace_fields_indexed( + new_pats.into_iter().map(|pat| (pat.field.index(), &pat.pattern)), + ) + } + + /// Overrides some of the fields with the provided patterns. This is used when a pattern + /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start with a + /// `Fields` that is just one wildcard per field of the `Foo` struct, and override the entry + /// corresponding to `field1` with the pattern `Some(_)`. This is also used for slice patterns + /// for the same reason. + fn replace_fields_indexed( + &self, + new_pats: impl IntoIterator)>, + ) -> Self { + let mut fields = self.clone(); + if let Fields::Slice(pats) = fields { + fields = Fields::Vec(pats.iter().collect()); + } + + match &mut fields { + Fields::Vec(pats) => { + for (i, pat) in new_pats { + pats[i] = pat + } + } + Fields::Filtered { fields, .. } => { + for (i, pat) in new_pats { + if let FilteredField::Kept(p) = &mut fields[i] { + *p = pat + } + } + } + Fields::Slice(_) => unreachable!(), + } + fields + } + + /// Replaces contained fields with the given filtered list of patterns, e.g. taken from the + /// matrix. There must be `len()` patterns in `pats`. + fn replace_fields( + &self, + cx: &MatchCheckCtxt<'p, 'tcx>, + pats: impl IntoIterator>, + ) -> Self { + let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats); + + match self { + Fields::Filtered { fields, len } => { + let mut pats = pats.iter(); + let mut fields = fields.clone(); + for f in &mut fields { + if let FilteredField::Kept(p) = f { + // We take one input pattern for each `Kept` field, in order. + *p = pats.next().unwrap(); + } + } + Fields::Filtered { fields, len: *len } + } + _ => Fields::Slice(pats), + } + } + + fn push_on_patstack(self, stack: &[&'p Pat<'tcx>]) -> PatStack<'p, 'tcx> { + let pats: SmallVec<_> = match self { + Fields::Slice(pats) => pats.iter().chain(stack.iter().copied()).collect(), + Fields::Vec(mut pats) => { + pats.extend_from_slice(stack); + pats + } + Fields::Filtered { fields, .. } => { + // We skip hidden fields here + fields.into_iter().filter_map(|p| p.kept()).chain(stack.iter().copied()).collect() + } + }; + PatStack::from_vec(pats) } } @@ -1073,15 +1263,16 @@ impl<'tcx, 'p> Usefulness<'tcx, 'p> { fn apply_constructor( self, - cx: &MatchCheckCtxt<'_, 'tcx>, + cx: &MatchCheckCtxt<'p, 'tcx>, ctor: &Constructor<'tcx>, ty: Ty<'tcx>, + ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Self { match self { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses .into_iter() - .map(|witness| witness.apply_constructor(cx, &ctor, ty)) + .map(|witness| witness.apply_constructor(cx, &ctor, ty, ctor_wild_subpatterns)) .collect(), ), x => x, @@ -1201,17 +1392,19 @@ impl<'tcx> Witness<'tcx> { /// /// left_ty: struct X { a: (bool, &'static str), b: usize} /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } - fn apply_constructor<'a>( + fn apply_constructor<'p>( mut self, - cx: &MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'p, 'tcx>, ctor: &Constructor<'tcx>, ty: Ty<'tcx>, + ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Self { - let arity = ctor.arity(cx, ty); let pat = { - let len = self.0.len() as u64; - let pats = self.0.drain((len - arity) as usize..).rev(); - ctor.apply(cx, ty, pats) + let len = self.0.len(); + let arity = ctor_wild_subpatterns.len(); + let pats = self.0.drain((len - arity)..).rev(); + let fields = ctor_wild_subpatterns.replace_fields(cx, pats); + ctor.apply(cx, ty, fields) }; self.0.push(pat); @@ -1609,22 +1802,23 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { /// to a set of such vectors `m` - this is defined as there being a set of /// inputs that will match `v` but not any of the sets in `m`. /// -/// All the patterns at each column of the `matrix ++ v` matrix must -/// have the same type, except that wildcard (PatKind::Wild) patterns -/// with type `TyErr` are also allowed, even if the "type of the column" -/// is not `TyErr`. That is used to represent private fields, as using their -/// real type would assert that they are inhabited. +/// All the patterns at each column of the `matrix ++ v` matrix must have the same type. /// /// This is used both for reachability checking (if a pattern isn't useful in /// relation to preceding patterns, it is not reachable) and exhaustiveness /// checking (if a wildcard pattern is useful in relation to a matrix, the /// matrix isn't exhaustive). +/// +/// `is_under_guard` is used to inform if the pattern has a guard. If it +/// has one it must not be inserted into the matrix. This shouldn't be +/// relied on for soundness. crate fn is_useful<'p, 'tcx>( cx: &mut MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'p, 'tcx>, witness_preference: WitnessPreference, hir_id: HirId, + is_under_guard: bool, is_top_level: bool, ) -> Usefulness<'tcx, 'p> { let &Matrix(ref rows) = matrix; @@ -1653,7 +1847,7 @@ crate fn is_useful<'p, 'tcx>( let mut unreachable_pats = Vec::new(); let mut any_is_useful = false; for v in vs { - let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, false); + let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); match res { Useful(pats) => { any_is_useful = true; @@ -1664,39 +1858,15 @@ crate fn is_useful<'p, 'tcx>( bug!("Encountered or-pat in `v` during exhaustiveness checking") } } - matrix.push(v); + // If pattern has a guard don't add it to the matrix + if !is_under_guard { + matrix.push(v); + } } return if any_is_useful { Useful(unreachable_pats) } else { NotUseful }; } - let (ty, span) = matrix - .heads() - .map(|r| (r.ty, r.span)) - .find(|(ty, _)| !ty.references_error()) - .unwrap_or((v.head().ty, v.head().span)); - let pcx = PatCtxt { - // TyErr is used to represent the type of wildcard patterns matching - // against inaccessible (private) fields of structs, so that we won't - // be able to observe whether the types of the struct's fields are - // inhabited. - // - // If the field is truly inaccessible, then all the patterns - // matching against it must be wildcard patterns, so its type - // does not matter. - // - // However, if we are matching against non-wildcard patterns, we - // need to know the real type of the field so we can specialize - // against it. This primarily occurs through constants - they - // can include contents for fields that are inaccessible at the - // location of the match. In that case, the field's type is - // inhabited - by the constant - so we can just use it. - // - // FIXME: this might lead to "unstable" behavior with macro hygiene - // introducing uninhabited patterns for inaccessible fields. We - // need to figure out how to model that. - ty, - span, - }; + let pcx = PatCtxt { ty: v.head().ty, span: v.head().span }; debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); @@ -1712,7 +1882,18 @@ crate fn is_useful<'p, 'tcx>( Some(hir_id), ) .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id)) + .map(|c| { + is_useful_specialized( + cx, + matrix, + v, + c, + pcx.ty, + witness_preference, + hir_id, + is_under_guard, + ) + }) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } else { @@ -1746,14 +1927,24 @@ crate fn is_useful<'p, 'tcx>( split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None) .into_iter() .map(|c| { - is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id) + is_useful_specialized( + cx, + matrix, + v, + c, + pcx.ty, + witness_preference, + hir_id, + is_under_guard, + ) }) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } else { let matrix = matrix.specialize_wildcard(); let v = v.to_tail(); - let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id, false); + let usefulness = + is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); // In this case, there's at least one "free" // constructor that is only matched against by @@ -1807,18 +1998,19 @@ fn is_useful_specialized<'p, 'tcx>( matrix: &Matrix<'p, 'tcx>, v: &PatStack<'p, 'tcx>, ctor: Constructor<'tcx>, - lty: Ty<'tcx>, + ty: Ty<'tcx>, witness_preference: WitnessPreference, hir_id: HirId, + is_under_guard: bool, ) -> Usefulness<'tcx, 'p> { - debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); - - let ctor_wild_subpatterns = - cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty)); - let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns); - v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns) - .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, false)) - .map(|u| u.apply_constructor(cx, &ctor, lty)) + debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, ty); + + // We cache the result of `Fields::wildcards` because it is used a lot. + let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); + let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); + v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) + .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) + .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) .unwrap_or(NotUseful) } @@ -1920,8 +2112,8 @@ fn slice_pat_covered_by_const<'tcx>( } (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { assert_eq!(*t, tcx.types.u8); - let ptr = Pointer::new(AllocId(0), Size::from_bytes(start as u64)); - data.get_bytes(&tcx, ptr, Size::from_bytes((end - start) as u64)).unwrap() + let ptr = Pointer::new(AllocId(0), Size::from_bytes(start)); + data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap() } // FIXME(oli-obk): create a way to extract fat pointers from ByRef (_, ty::Slice(_)) => return Ok(false), @@ -1944,15 +2136,12 @@ fn slice_pat_covered_by_const<'tcx>( .zip(prefix) .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) { - match pat.kind { - box PatKind::Constant { value } => { - let b = value.eval_bits(tcx, param_env, pat.ty); - assert_eq!(b as u8 as u128, b); - if b as u8 != *ch { - return Ok(false); - } + if let box PatKind::Constant { value } = pat.kind { + let b = value.eval_bits(tcx, param_env, pat.ty); + assert_eq!(b as u8 as u128, b); + if b as u8 != *ch { + return Ok(false); } - _ => {} } } @@ -2167,8 +2356,8 @@ fn split_grouped_constructors<'p, 'tcx>( let head_ctors = matrix.heads().filter_map(|pat| pat_constructor(tcx, param_env, pat)); for ctor in head_ctors { - match ctor { - Slice(slice) => match slice.pattern_kind() { + if let Slice(slice) = ctor { + match slice.pattern_kind() { FixedLen(len) => { max_fixed_len = cmp::max(max_fixed_len, len); } @@ -2176,8 +2365,7 @@ fn split_grouped_constructors<'p, 'tcx>( max_prefix_len = cmp::max(max_prefix_len, prefix); max_suffix_len = cmp::max(max_suffix_len, suffix); } - }, - _ => {} + } } } @@ -2286,27 +2474,6 @@ fn constructor_covered_by_range<'tcx>( if intersects { Some(()) } else { None } } -fn patterns_for_variant<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - subpatterns: &'p [FieldPat<'tcx>], - ctor_wild_subpatterns: &'p [Pat<'tcx>], - is_non_exhaustive: bool, -) -> PatStack<'p, 'tcx> { - let mut result: SmallVec<_> = ctor_wild_subpatterns.iter().collect(); - - for subpat in subpatterns { - if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) { - result[subpat.field.index()] = &subpat.pattern; - } - } - - debug!( - "patterns_for_variant({:#?}, {:#?}) = {:#?}", - subpatterns, ctor_wild_subpatterns, result - ); - PatStack::from_vec(result) -} - /// This is the main specialization step. It expands the pattern /// into `arity` patterns based on the constructor. For most patterns, the step is trivial, /// for instance tuple patterns are flattened and box patterns expand into their inner pattern. @@ -2316,37 +2483,40 @@ fn patterns_for_variant<'p, 'tcx>( /// different patterns. /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. +/// +/// This is roughly the inverse of `Constructor::apply`. fn specialize_one_pattern<'p, 'tcx>( cx: &mut MatchCheckCtxt<'p, 'tcx>, pat: &'p Pat<'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &'p [Pat<'tcx>], -) -> Option> { + ctor_wild_subpatterns: &Fields<'p, 'tcx>, +) -> Option> { if let NonExhaustive = constructor { // Only a wildcard pattern can match the special extra constructor - return if pat.is_wildcard() { Some(PatStack::default()) } else { None }; + if !pat.is_wildcard() { + return None; + } + return Some(Fields::empty()); } let result = match *pat.kind { PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` - PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.iter().collect()), + PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.clone()), PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let variant = &adt_def.variants[variant_index]; - let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant); - Some(Variant(variant.def_id)) - .filter(|variant_constructor| variant_constructor == constructor) - .map(|_| { - patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, is_non_exhaustive) - }) + if constructor != &Variant(variant.def_id) { + return None; + } + Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) } PatKind::Leaf { ref subpatterns } => { - Some(patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, false)) + Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) } - PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(subpattern)), + PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), PatKind::Constant { value } if constructor.is_slice() => { // We extract an `Option` for the pointer because slices of zero @@ -2359,11 +2529,10 @@ fn specialize_one_pattern<'p, 'tcx>( // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, // the result would be exactly what we early return here. if n == 0 { - if ctor_wild_subpatterns.len() as u64 == 0 { - return Some(PatStack::from_slice(&[])); - } else { + if ctor_wild_subpatterns.len() as u64 != n { return None; } + return Some(Fields::empty()); } match value.val { ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { @@ -2375,7 +2544,7 @@ fn specialize_one_pattern<'p, 'tcx>( ty::Slice(t) => { match value.val { ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { - let offset = Size::from_bytes(start as u64); + let offset = Size::from_bytes(start); let n = (end - start) as u64; (Cow::Borrowed(data), offset, n, t) } @@ -2397,24 +2566,26 @@ fn specialize_one_pattern<'p, 'tcx>( constructor, ), }; - if ctor_wild_subpatterns.len() as u64 == n { - // convert a constant slice/array pattern to a list of patterns. - let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; - let ptr = Pointer::new(AllocId(0), offset); - (0..n) - .map(|i| { - let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; - let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; - let scalar = scalar.not_undef().ok()?; - let value = ty::Const::from_scalar(cx.tcx, scalar, ty); - let pattern = - Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; - Some(&*cx.pattern_arena.alloc(pattern)) - }) - .collect() - } else { - None + if ctor_wild_subpatterns.len() as u64 != n { + return None; + } + + // Convert a constant slice/array pattern to a list of patterns. + let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; + let ptr = Pointer::new(AllocId(0), offset); + let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| { + let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; + let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; + let scalar = scalar.not_undef().ok()?; + let value = ty::Const::from_scalar(cx.tcx, scalar, ty); + let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; + Some(pattern) + })); + // Ensure none of the dereferences failed. + if pats.len() as u64 != n { + return None; } + Some(Fields::from_slice_unfiltered(pats)) } PatKind::Constant { .. } | PatKind::Range { .. } => { @@ -2422,50 +2593,39 @@ fn specialize_one_pattern<'p, 'tcx>( // - Single value: add a row if the pattern contains the constructor. // - Range: add a row if the constructor intersects the pattern. if let IntRange(ctor) = constructor { - match IntRange::from_pat(cx.tcx, cx.param_env, pat) { - Some(pat) => ctor.intersection(cx.tcx, &pat).map(|_| { - // Constructor splitting should ensure that all intersections we encounter - // are actually inclusions. - assert!(ctor.is_subrange(&pat)); - PatStack::default() - }), - _ => None, - } + let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; + ctor.intersection(cx.tcx, &pat)?; + // Constructor splitting should ensure that all intersections we encounter + // are actually inclusions. + assert!(ctor.is_subrange(&pat)); } else { // Fallback for non-ranges and ranges that involve // floating-point numbers, which are not conveniently handled // by `IntRange`. For these cases, the constructor may not be a // range so intersection actually devolves into being covered // by the pattern. - constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat) - .map(|()| PatStack::default()) + constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?; } + Some(Fields::empty()) } PatKind::Array { ref prefix, ref slice, ref suffix } | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { Slice(_) => { + // Number of subpatterns for this pattern let pat_len = prefix.len() + suffix.len(); - if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { - if slice_count == 0 || slice.is_some() { - Some( - prefix - .iter() - .chain( - ctor_wild_subpatterns - .iter() - .skip(prefix.len()) - .take(slice_count) - .chain(suffix.iter()), - ) - .collect(), - ) - } else { - None - } - } else { - None + // Number of subpatterns for this constructor + let arity = ctor_wild_subpatterns.len(); + + if (slice.is_none() && arity != pat_len) || pat_len > arity { + return None; } + + // Replace the prefix and the suffix with the given patterns, leaving wildcards in + // the middle if there was a subslice pattern `..`. + let prefix = prefix.iter().enumerate(); + let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); + Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) } ConstantValue(cv) => { match slice_pat_covered_by_const( @@ -2477,7 +2637,7 @@ fn specialize_one_pattern<'p, 'tcx>( suffix, cx.param_env, ) { - Ok(true) => Some(PatStack::default()), + Ok(true) => Some(Fields::empty()), Ok(false) => None, Err(ErrorReported) => None, } diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs index 9c86669cf9d92..707502640e05c 100644 --- a/src/librustc_mir_build/hair/pattern/check_match.rs +++ b/src/librustc_mir_build/hair/pattern/check_match.rs @@ -1,10 +1,9 @@ use super::_match::Usefulness::*; use super::_match::WitnessPreference::*; use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack}; - use super::{PatCtxt, PatKind, PatternError}; -use rustc::ty::{self, Ty, TyCtxt}; +use arena::TypedArena; use rustc_ast::ast::Mutability; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -12,22 +11,26 @@ use rustc_hir::def::*; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{HirId, Pat}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME; use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS}; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::{sym, Span}; - use std::slice; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { - let body_id = match tcx.hir().as_local_hir_id(def_id) { + let body_id = match def_id.as_local() { None => return, - Some(id) => tcx.hir().body_owned_by(id), + Some(id) => tcx.hir().body_owned_by(tcx.hir().as_local_hir_id(id)), }; - let mut visitor = - MatchVisitor { tcx, tables: tcx.body_tables(body_id), param_env: tcx.param_env(def_id) }; + let mut visitor = MatchVisitor { + tcx, + tables: tcx.body_tables(body_id), + param_env: tcx.param_env(def_id), + pattern_arena: TypedArena::default(), + }; visitor.visit_body(tcx.hir().body(body_id)); } @@ -39,6 +42,7 @@ struct MatchVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, tables: &'a ty::TypeckTables<'tcx>, param_env: ty::ParamEnv<'tcx>, + pattern_arena: TypedArena>, } impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> { @@ -91,14 +95,14 @@ impl PatCtxt<'_, '_> { } PatternError::FloatBug => { // FIXME(#31407) this is only necessary because float parsing is buggy - ::rustc::mir::interpret::struct_error( + ::rustc_middle::mir::interpret::struct_error( self.tcx.at(pat_span), "could not evaluate float literal (see issue #31407)", ) .emit(); } PatternError::NonConstPath(span) => { - ::rustc::mir::interpret::struct_error( + ::rustc_middle::mir::interpret::struct_error( self.tcx.at(span), "runtime values cannot be referenced in patterns", ) @@ -143,9 +147,13 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { (pattern, pattern_ty) } - fn check_in_cx(&self, hir_id: HirId, f: impl FnOnce(MatchCheckCtxt<'_, 'tcx>)) { - let module = self.tcx.parent_module(hir_id); - MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module.to_def_id(), |cx| f(cx)); + fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'_, 'tcx> { + MatchCheckCtxt { + tcx: self.tcx, + param_env: self.param_env, + module: self.tcx.parent_module(hir_id).to_def_id(), + pattern_arena: &self.pattern_arena, + } } fn check_match( @@ -159,90 +167,88 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { self.check_patterns(arm.guard.is_some(), &arm.pat); } - self.check_in_cx(scrut.hir_id, |ref mut cx| { - let mut have_errors = false; + let mut cx = self.new_cx(scrut.hir_id); - let inlined_arms: Vec<_> = arms - .iter() - .map(|hir::Arm { pat, guard, .. }| { - (self.lower_pattern(cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some()) - }) - .collect(); + let mut have_errors = false; - // Bail out early if inlining failed. - if have_errors { - return; - } + let inlined_arms: Vec<_> = arms + .iter() + .map(|hir::Arm { pat, guard, .. }| { + (self.lower_pattern(&mut cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some()) + }) + .collect(); + + // Bail out early if inlining failed. + if have_errors { + return; + } - // Fourth, check for unreachable arms. - let matrix = check_arms(cx, &inlined_arms, source); + // Fourth, check for unreachable arms. + let matrix = check_arms(&mut cx, &inlined_arms, source); - // Fifth, check if the match is exhaustive. - let scrut_ty = self.tables.node_type(scrut.hir_id); - // Note: An empty match isn't the same as an empty matrix for diagnostics purposes, - // since an empty matrix can occur when there are arms, if those arms all have guards. - let is_empty_match = inlined_arms.is_empty(); - check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match); - }) + // Fifth, check if the match is exhaustive. + // Note: An empty match isn't the same as an empty matrix for diagnostics purposes, + // since an empty matrix can occur when there are arms, if those arms all have guards. + let scrut_ty = self.tables.expr_ty_adjusted(scrut); + let is_empty_match = inlined_arms.is_empty(); + check_exhaustive(&mut cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match); } fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option) { - self.check_in_cx(pat.hir_id, |ref mut cx| { - let (pattern, pattern_ty) = self.lower_pattern(cx, pat, &mut false); - let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect(); - - let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) { - Ok(_) => return, - Err(err) => err, - }; - - let joined_patterns = joined_uncovered_patterns(&witnesses); - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0005, - "refutable pattern in {}: {} not covered", - origin, - joined_patterns - ); - let suggest_if_let = match &pat.kind { - hir::PatKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].args.is_none() => - { - const_not_var(&mut err, cx.tcx, pat, path); - false - } - _ => { - err.span_label( - pat.span, - pattern_not_covered_label(&witnesses, &joined_patterns), - ); - true - } - }; + let mut cx = self.new_cx(pat.hir_id); - if let (Some(span), true) = (sp, suggest_if_let) { - err.note( - "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ - an `enum` with only one variant", - ); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - err.span_suggestion( - span, - "you might want to use `if let` to ignore the variant that isn't matched", - format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]), - Applicability::HasPlaceholders, - ); - } - err.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-02-refutability.html", + let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false); + let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect(); + + let witnesses = match check_not_useful(&mut cx, pattern_ty, &pats, pat.hir_id) { + Ok(_) => return, + Err(err) => err, + }; + + let joined_patterns = joined_uncovered_patterns(&witnesses); + let mut err = struct_span_err!( + self.tcx.sess, + pat.span, + E0005, + "refutable pattern in {}: {} not covered", + origin, + joined_patterns + ); + let suggest_if_let = match &pat.kind { + hir::PatKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].args.is_none() => + { + const_not_var(&mut err, cx.tcx, pat, path); + false + } + _ => { + err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns)); + true + } + }; + + if let (Some(span), true) = (sp, suggest_if_let) { + err.note( + "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ + an `enum` with only one variant", + ); + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + err.span_suggestion( + span, + "you might want to use `if let` to ignore the variant that isn't matched", + format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]), + Applicability::HasPlaceholders, ); } + err.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch18-02-refutability.html", + ); + } - adt_defined_here(cx, &mut err, pattern_ty, &witnesses); - err.emit(); - }); + adt_defined_here(&cx, &mut err, pattern_ty, &witnesses); + err.note(&format!("the matched value is of type `{}`", pattern_ty)); + err.emit(); } } @@ -360,7 +366,7 @@ fn check_arms<'p, 'tcx>( let mut catchall = None; for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() { let v = PatStack::from_pattern(pat); - match is_useful(cx, &seen, &v, LeaveOutWitness, id, true) { + match is_useful(cx, &seen, &v, LeaveOutWitness, id, has_guard, true) { NotUseful => { match source { hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(), @@ -410,7 +416,10 @@ fn check_not_useful<'p, 'tcx>( ) -> Result<(), Vec>> { let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty)); let v = PatStack::from_pattern(wild_pattern); - match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) { + + // false is given for `is_under_guard` argument due to the wildcard + // pattern not having a guard + match is_useful(cx, matrix, &v, ConstructWitness, hir_id, false, true) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. UsefulWithWitness(pats) => Err(if pats.is_empty() { bug!("Exhaustiveness check returned no witnesses") @@ -480,6 +489,7 @@ fn check_exhaustive<'p, 'tcx>( "ensure that all possible cases are being handled, \ possibly by adding wildcards or more match arms", ); + err.note(&format!("the matched value is of type `{}`", scrut_ty)); err.emit(); } diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs index ae951e810e32e..28ec2ca13d5af 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -1,8 +1,9 @@ -use rustc::mir::Field; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; +use rustc_hir::lang_items::EqTraitLangItem; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::mir::Field; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::Span; use rustc_trait_selection::traits::predicate_for_trait_def; @@ -22,13 +23,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { cv: &'tcx ty::Const<'tcx>, id: hir::HirId, span: Span, + mir_structural_match_violation: bool, ) -> Pat<'tcx> { debug!("const_to_pat: cv={:#?} id={:?}", cv, id); debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span); self.tcx.infer_ctxt().enter(|infcx| { let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(cv) + convert.to_pat(cv, mir_structural_match_violation) }) } } @@ -81,7 +83,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { traits::type_marked_structural(self.id, self.span, &self.infcx, ty) } - fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> { + fn to_pat( + &mut self, + cv: &'tcx ty::Const<'tcx>, + mir_structural_match_violation: bool, + ) -> Pat<'tcx> { // This method is just a wrapper handling a validity check; the heavy lifting is // performed by the recursive `recur` method, which is not meant to be // invoked except by this method. @@ -100,22 +106,28 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", cv.ty, structural ); + + if structural.is_none() && mir_structural_match_violation { + bug!("MIR const-checker found novel structural match violation"); + } + if let Some(non_sm_ty) = structural { - let adt_def = match non_sm_ty { - traits::NonStructuralMatchTy::Adt(adt_def) => adt_def, + let msg = match non_sm_ty { + traits::NonStructuralMatchTy::Adt(adt_def) => { + let path = self.tcx().def_path_str(adt_def.did); + format!( + "to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + path, path, + ) + } + traits::NonStructuralMatchTy::Dynamic => { + "trait objects cannot be used in patterns".to_string() + } traits::NonStructuralMatchTy::Param => { bug!("use of constant whose type is a parameter inside a pattern") } }; - let path = self.tcx().def_path_str(adt_def.did); - - let make_msg = || -> String { - format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ) - }; // double-check there even *is* a semantic `PartialEq` to dispatch to. // @@ -129,7 +141,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // code at the moment, because types like `for <'a> fn(&'a ())` do // not *yet* implement `PartialEq`. So for now we leave this here. let ty_is_partial_eq: bool = { - let partial_eq_trait_id = self.tcx().lang_items().eq_trait().unwrap(); + let partial_eq_trait_id = + self.tcx().require_lang_item(EqTraitLangItem, Some(self.span)); let obligation: PredicateObligation<'_> = predicate_for_trait_def( self.tcx(), self.param_env, @@ -145,13 +158,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { if !ty_is_partial_eq { // span_fatal avoids ICE from resolution of non-existent method (rare case). - self.tcx().sess.span_fatal(self.span, &make_msg()); - } else { + self.tcx().sess.span_fatal(self.span, &msg); + } else if mir_structural_match_violation { self.tcx().struct_span_lint_hir( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - |lint| lint.build(&make_msg()).emit(), + |lint| lint.build(&msg).emit(), + ); + } else { + debug!( + "`search_for_structural_match_violation` found one, but `CustomEq` was \ + not in the qualifs for that `const`" ); } } diff --git a/src/librustc_mir_build/hair/pattern/mod.rs b/src/librustc_mir_build/hair/pattern/mod.rs index 6786c35629308..e9aa7f597beb6 100644 --- a/src/librustc_mir_build/hair/pattern/mod.rs +++ b/src/librustc_mir_build/hair/pattern/mod.rs @@ -8,14 +8,6 @@ pub(crate) use self::check_match::check_match; use crate::hair::util::UserAnnotatedTyHelpers; -use rustc::mir::interpret::{get_slice_bytes, sign_extend, ConstValue, ErrorHandled}; -use rustc::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc::mir::UserTypeProjection; -use rustc::mir::{BorrowKind, Field, Mutability}; -use rustc::ty::layout::VariantIdx; -use rustc::ty::subst::{GenericArg, SubstsRef}; -use rustc::ty::{self, AdtDef, DefIdTree, Region, Ty, TyCtxt, UserType}; -use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations}; use rustc_ast::ast; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -23,7 +15,17 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::RangeEnd; use rustc_index::vec::Idx; -use rustc_span::{Span, DUMMY_SP}; +use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue}; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::UserTypeProjection; +use rustc_middle::mir::{BorrowKind, Field, Mutability}; +use rustc_middle::ty::subst::{GenericArg, SubstsRef}; +use rustc_middle::ty::{self, AdtDef, DefIdTree, Region, Ty, TyCtxt, UserType}; +use rustc_middle::ty::{ + CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, +}; +use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_target::abi::VariantIdx; use std::cmp::Ordering; use std::fmt; @@ -126,11 +128,14 @@ crate enum PatKind<'tcx> { /// `x`, `ref x`, `x @ P`, etc. Binding { mutability: Mutability, - name: ast::Name, + name: Symbol, mode: BindingMode, var: hir::HirId, ty: Ty<'tcx>, subpattern: Option>, + /// Is this the leftmost occurance of the binding, i.e., is `var` the + /// `HirId` of this pattern? + is_primary: bool, }, /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with @@ -599,6 +604,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { var: id, ty: var_ty, subpattern: self.lower_opt_pattern(sub), + is_primary: id == pat.hir_id, } } @@ -719,14 +725,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } } - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) + Res::Def( + DefKind::Struct + | DefKind::Ctor(CtorOf::Struct, ..) + | DefKind::Union + | DefKind::TyAlias + | DefKind::AssocTy, + _, + ) | Res::SelfTy(..) | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, - _ => { let pattern_error = match res { Res::Def(DefKind::ConstParam, _) => PatternError::ConstParamInPattern(span), @@ -758,69 +766,80 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { let ty = self.tables.node_type(id); let res = self.tables.qpath_res(qpath, id); - let is_associated_const = match res { - Res::Def(DefKind::AssocConst, _) => true, - _ => false, + + let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) }; + + let (def_id, is_associated_const) = match res { + Res::Def(DefKind::Const, def_id) => (def_id, false), + Res::Def(DefKind::AssocConst, def_id) => (def_id, true), + + _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), }; - let kind = match res { - Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { - let substs = self.tables.node_substs(id); - // Use `Reveal::All` here because patterns are always monomorphic even if their function isn't. - match self.tcx.const_eval_resolve( - self.param_env.with_reveal_all(), - def_id, - substs, - None, - Some(span), - ) { - Ok(value) => { - let const_ = - ty::Const::from_value(self.tcx, value, self.tables.node_type(id)); - - let pattern = self.const_to_pat(&const_, id, span); - if !is_associated_const { - return pattern; - } - let user_provided_types = self.tables().user_provided_types(); - return if let Some(u_ty) = user_provided_types.get(id) { - let user_ty = PatTyProj::from_user_type(*u_ty); - Pat { - span, - kind: Box::new(PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - /// Note that use `Contravariant` here. See the - /// `variance` field documentation for details. - variance: ty::Variance::Contravariant, - user_ty, - user_ty_span: span, - }, - }), - ty: const_.ty, - } - } else { - pattern - }; - } - Err(ErrorHandled::TooGeneric) => { - self.errors.push(if is_associated_const { - PatternError::AssocConstInPattern(span) - } else { - PatternError::StaticInPattern(span) - }); - PatKind::Wild - } - Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); - PatKind::Wild - } - } + // Use `Reveal::All` here because patterns are always monomorphic even if their function + // isn't. + let param_env_reveal_all = self.param_env.with_reveal_all(); + let substs = self.tables.node_substs(id); + let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) { + Ok(Some(i)) => i, + Ok(None) => { + self.errors.push(if is_associated_const { + PatternError::AssocConstInPattern(span) + } else { + PatternError::StaticInPattern(span) + }); + + return pat_from_kind(PatKind::Wild); + } + + Err(_) => { + self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + return pat_from_kind(PatKind::Wild); } - _ => self.lower_variant_or_leaf(res, id, span, ty, vec![]), }; - Pat { span, ty, kind: Box::new(kind) } + // `mir_const_qualif` must be called with the `DefId` of the item where the const is + // defined, not where it is declared. The difference is significant for associated + // constants. + let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq; + debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation); + + match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) { + Ok(value) => { + let const_ = ty::Const::from_value(self.tcx, value, self.tables.node_type(id)); + + let pattern = self.const_to_pat(&const_, id, span, mir_structural_match_violation); + + if !is_associated_const { + return pattern; + } + + let user_provided_types = self.tables().user_provided_types(); + if let Some(u_ty) = user_provided_types.get(id) { + let user_ty = PatTyProj::from_user_type(*u_ty); + Pat { + span, + kind: Box::new(PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + /// Note that use `Contravariant` here. See the + /// `variance` field documentation for details. + variance: ty::Variance::Contravariant, + user_ty, + user_ty_span: span, + }, + }), + ty: const_.ty, + } + } else { + pattern + } + } + Err(_) => { + self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + pat_from_kind(PatKind::Wild) + } + } } /// Converts literals, paths and negation of literals to patterns. @@ -845,7 +864,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lit_input = LitToConstInput { lit: &lit.node, ty: self.tables.expr_ty(expr), neg }; match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span).kind, + Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span, false).kind, Err(LitToConstError::UnparseableFloat) => { self.errors.push(PatternError::FloatBug); PatKind::Wild @@ -917,7 +936,7 @@ macro_rules! CloneImpls { } CloneImpls! { <'tcx> - Span, Field, Mutability, ast::Name, hir::HirId, usize, ty::Const<'tcx>, + Span, Field, Mutability, Symbol, hir::HirId, usize, ty::Const<'tcx>, Region<'tcx>, Ty<'tcx>, BindingMode, &'tcx AdtDef, SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, UserTypeProjection, PatTyProj<'tcx> @@ -962,7 +981,7 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { user_ty_span, }, }, - PatKind::Binding { mutability, name, mode, var, ty, ref subpattern } => { + PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, is_primary } => { PatKind::Binding { mutability: mutability.fold_with(folder), name: name.fold_with(folder), @@ -970,6 +989,7 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { var: var.fold_with(folder), ty: ty.fold_with(folder), subpattern: subpattern.fold_with(folder), + is_primary, } } PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { @@ -1044,9 +1064,9 @@ crate fn compare_const_vals<'tcx>( l.partial_cmp(&r) } ty::Int(ity) => { - use rustc::ty::layout::{Integer, IntegerExt}; use rustc_attr::SignedInt; - let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); + use rustc_middle::ty::layout::IntegerExt; + let size = rustc_target::abi::Integer::from_attr(&tcx, SignedInt(ity)).size(); let a = sign_extend(a, size); let b = sign_extend(b, size); Some((a as i128).cmp(&(b as i128))) @@ -1056,18 +1076,15 @@ crate fn compare_const_vals<'tcx>( } if let ty::Str = ty.kind { - match (a.val, b.val) { - ( - ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), - ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), - ) => { - let a_bytes = get_slice_bytes(&tcx, a_val); - let b_bytes = get_slice_bytes(&tcx, b_val); - return from_bool(a_bytes == b_bytes); - } - _ => (), + if let ( + ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), + ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), + ) = (a.val, b.val) + { + let a_bytes = get_slice_bytes(&tcx, a_val); + let b_bytes = get_slice_bytes(&tcx, b_val); + return from_bool(a_bytes == b_bytes); } } - fallback() } diff --git a/src/librustc_mir_build/hair/util.rs b/src/librustc_mir_build/hair/util.rs index c27844ed0d032..0ea0d5d1b0c19 100644 --- a/src/librustc_mir_build/hair/util.rs +++ b/src/librustc_mir_build/hair/util.rs @@ -1,5 +1,5 @@ -use rustc::ty::{self, CanonicalUserType, TyCtxt, UserType}; use rustc_hir as hir; +use rustc_middle::ty::{self, CanonicalUserType, TyCtxt, UserType}; crate trait UserAnnotatedTyHelpers<'tcx> { fn tcx(&self) -> TyCtxt<'tcx>; diff --git a/src/librustc_mir_build/lib.rs b/src/librustc_mir_build/lib.rs index 5a8b5a329634d..c6d65d56c93f0 100644 --- a/src/librustc_mir_build/lib.rs +++ b/src/librustc_mir_build/lib.rs @@ -9,18 +9,19 @@ #![feature(const_panic)] #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; mod build; mod hair; mod lints; -use rustc::ty::query::Providers; +use rustc_middle::ty::query::Providers; pub fn provide(providers: &mut Providers<'_>) { providers.check_match = hair::pattern::check_match; diff --git a/src/librustc_mir_build/lints.rs b/src/librustc_mir_build/lints.rs index 8b1ddf7461a76..dbafc98fb50f1 100644 --- a/src/librustc_mir_build/lints.rs +++ b/src/librustc_mir_build/lints.rs @@ -1,138 +1,161 @@ -use rustc::hir::map::blocks::FnLikeNode; -use rustc::mir::{self, Body, TerminatorKind}; -use rustc::ty::subst::InternalSubsts; -use rustc::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt}; -use rustc_hir::def_id::DefId; +use rustc_data_structures::graph::iterate::{ + ControlFlow, NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, +}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; -use rustc_index::bit_set::BitSet; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind}; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; +use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt}; use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; +use rustc_span::Span; -crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); +crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: LocalDefId) { + let hir_id = tcx.hir().as_local_hir_id(def_id); if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) { - check_fn_for_unconditional_recursion(tcx, fn_like_node.kind(), body, def_id); + if let FnKind::Closure(_) = fn_like_node.kind() { + // closures can't recur, so they don't matter. + return; + } + + // If this is trait/impl method, extract the trait's substs. + let trait_substs = match tcx.opt_associated_item(def_id.to_def_id()) { + Some(AssocItem { + container: AssocItemContainer::TraitContainer(trait_def_id), .. + }) => { + let trait_substs_count = tcx.generics_of(*trait_def_id).count(); + &InternalSubsts::identity_for_item(tcx, def_id.to_def_id())[..trait_substs_count] + } + _ => &[], + }; + + let mut vis = Search { tcx, body, def_id, reachable_recursive_calls: vec![], trait_substs }; + if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) { + return; + } + + vis.reachable_recursive_calls.sort(); + + let hir_id = tcx.hir().as_local_hir_id(def_id); + let sp = tcx.sess.source_map().guess_head_span(tcx.hir().span(hir_id)); + tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| { + let mut db = lint.build("function cannot return without recursing"); + db.span_label(sp, "cannot return without recursing"); + // offer some help to the programmer. + for call_span in vis.reachable_recursive_calls { + db.span_label(call_span, "recursive call site"); + } + db.help("a `loop` may express intention better if this is on purpose"); + db.emit(); + }); } } -fn check_fn_for_unconditional_recursion<'tcx>( +struct NonRecursive; + +struct Search<'mir, 'tcx> { tcx: TyCtxt<'tcx>, - fn_kind: FnKind<'_>, - body: &Body<'tcx>, - def_id: DefId, -) { - if let FnKind::Closure(_) = fn_kind { - // closures can't recur, so they don't matter. - return; - } + body: &'mir Body<'tcx>, + def_id: LocalDefId, + trait_substs: &'tcx [GenericArg<'tcx>], - //FIXME(#54444) rewrite this lint to use the dataflow framework - - // Walk through this function (say `f`) looking to see if - // every possible path references itself, i.e., the function is - // called recursively unconditionally. This is done by trying - // to find a path from the entry node to the exit node that - // *doesn't* call `f` by traversing from the entry while - // pretending that calls of `f` are sinks (i.e., ignoring any - // exit edges from them). - // - // NB. this has an edge case with non-returning statements, - // like `loop {}` or `panic!()`: control flow never reaches - // the exit node through these, so one can have a function - // that never actually calls itself but is still picked up by - // this lint: - // - // fn f(cond: bool) { - // if !cond { panic!() } // could come from `assert!(cond)` - // f(false) - // } - // - // In general, functions of that form may be able to call - // itself a finite number of times and then diverge. The lint - // considers this to be an error for two reasons, (a) it is - // easier to implement, and (b) it seems rare to actually want - // to have behaviour like the above, rather than - // e.g., accidentally recursing after an assert. - - let basic_blocks = body.basic_blocks(); - let mut reachable_without_self_call_queue = vec![mir::START_BLOCK]; - let mut reached_exit_without_self_call = false; - let mut self_call_locations = vec![]; - let mut visited = BitSet::new_empty(basic_blocks.len()); - - let param_env = tcx.param_env(def_id); - let trait_substs_count = match tcx.opt_associated_item(def_id) { - Some(AssocItem { container: AssocItemContainer::TraitContainer(trait_def_id), .. }) => { - tcx.generics_of(trait_def_id).count() + reachable_recursive_calls: Vec, +} + +impl<'mir, 'tcx> Search<'mir, 'tcx> { + /// Returns `true` if `func` refers to the function we are searching in. + fn is_recursive_call(&self, func: &Operand<'tcx>) -> bool { + let Search { tcx, body, def_id, trait_substs, .. } = *self; + let param_env = tcx.param_env(def_id); + + let func_ty = func.ty(body, tcx); + if let ty::FnDef(fn_def_id, substs) = func_ty.kind { + let (call_fn_id, call_substs) = + if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, fn_def_id, substs) { + (instance.def_id(), instance.substs) + } else { + (fn_def_id, substs) + }; + + // FIXME(#57965): Make this work across function boundaries + + // If this is a trait fn, the substs on the trait have to match, or we might be + // calling into an entirely different method (for example, a call from the default + // method in the trait to `>::method`, where `A` and/or `B` are + // specific types). + return call_fn_id == def_id.to_def_id() + && &call_substs[..trait_substs.len()] == trait_substs; } - _ => 0, - }; - let caller_substs = &InternalSubsts::identity_for_item(tcx, def_id)[..trait_substs_count]; - - while let Some(bb) = reachable_without_self_call_queue.pop() { - if !visited.insert(bb) { - //already done - continue; + + false + } +} + +impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { + type BreakVal = NonRecursive; + + fn node_examined( + &mut self, + bb: BasicBlock, + prior_status: Option, + ) -> ControlFlow { + // Back-edge in the CFG (loop). + if let Some(NodeStatus::Visited) = prior_status { + return ControlFlow::Break(NonRecursive); } - let block = &basic_blocks[bb]; - - if let Some(ref terminator) = block.terminator { - match terminator.kind { - TerminatorKind::Call { ref func, .. } => { - let func_ty = func.ty(body, tcx); - - if let ty::FnDef(fn_def_id, substs) = func_ty.kind { - let (call_fn_id, call_substs) = if let Some(instance) = - Instance::resolve(tcx, param_env, fn_def_id, substs) - { - (instance.def_id(), instance.substs) - } else { - (fn_def_id, substs) - }; - - let is_self_call = call_fn_id == def_id - && &call_substs[..caller_substs.len()] == caller_substs; - - if is_self_call { - self_call_locations.push(terminator.source_info); - - //this is a self call so we shouldn't explore - //further down this path - continue; - } - } + match self.body[bb].terminator().kind { + // These terminators return control flow to the caller. + TerminatorKind::Abort + | TerminatorKind::GeneratorDrop + | TerminatorKind::Resume + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive), + + // A diverging InlineAsm is treated as non-recursing + TerminatorKind::InlineAsm { destination, .. } => { + if destination.is_some() { + ControlFlow::Continue + } else { + ControlFlow::Break(NonRecursive) } - TerminatorKind::Abort | TerminatorKind::Return => { - //found a path! - reached_exit_without_self_call = true; - break; - } - _ => {} } - for successor in terminator.successors() { - reachable_without_self_call_queue.push(*successor); - } + // These do not. + TerminatorKind::Assert { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue, } } - // Check the number of self calls because a function that - // doesn't return (e.g., calls a `-> !` function or `loop { /* - // no break */ }`) shouldn't be linted unless it actually - // recurs. - if !reached_exit_without_self_call && !self_call_locations.is_empty() { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - let sp = tcx.sess.source_map().def_span(tcx.hir().span(hir_id)); - tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| { - let mut db = lint.build("function cannot return without recursing"); - db.span_label(sp, "cannot return without recursing"); - // offer some help to the programmer. - for location in &self_call_locations { - db.span_label(location.span, "recursive call site"); + fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow { + // When we examine a node for the last time, remember it if it is a recursive call. + let terminator = self.body[bb].terminator(); + if let TerminatorKind::Call { func, .. } = &terminator.kind { + if self.is_recursive_call(func) { + self.reachable_recursive_calls.push(terminator.source_info.span); } - db.help("a `loop` may express intention better if this is on purpose"); - db.emit(); - }); + } + + ControlFlow::Continue + } + + fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { + // Don't traverse successors of recursive calls or false CFG edges. + match self.body[bb].terminator().kind { + TerminatorKind::Call { ref func, .. } => self.is_recursive_call(func), + + TerminatorKind::FalseUnwind { unwind: Some(imaginary_target), .. } + | TerminatorKind::FalseEdges { imaginary_target, .. } => imaginary_target == target, + + _ => false, + } } } diff --git a/src/librustc_parse/Cargo.toml b/src/librustc_parse/Cargo.toml index b02cabab0a8fc..7164c67880863 100644 --- a/src/librustc_parse/Cargo.toml +++ b/src/librustc_parse/Cargo.toml @@ -13,12 +13,10 @@ doctest = false bitflags = "1.0" log = "0.4" rustc_ast_pretty = { path = "../librustc_ast_pretty" } -rustc_attr = { path = "../librustc_attr" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_feature = { path = "../librustc_feature" } rustc_lexer = { path = "../librustc_lexer" } rustc_errors = { path = "../librustc_errors" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } rustc_ast = { path = "../librustc_ast" } diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs index ac58cbb9e8dae..2b7d5e5adb432 100644 --- a/src/librustc_parse/lexer/mod.rs +++ b/src/librustc_parse/lexer/mod.rs @@ -1,20 +1,21 @@ use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::util::comments; use rustc_data_structures::sync::Lrc; -use rustc_errors::{error_code, DiagnosticBuilder, FatalError}; -use rustc_lexer::unescape; +use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError}; use rustc_lexer::Base; +use rustc_lexer::{unescape, LexRawStrError, UnvalidatedRawStr, ValidatedRawStr}; use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Pos, Span}; use log::debug; use std::char; -use std::convert::TryInto; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; + +use rustc_lexer::unescape::Mode; use unescape_error_reporting::{emit_unescape_error, push_escaped_char}; #[derive(Clone, Debug)] @@ -31,8 +32,7 @@ pub struct StringReader<'a> { /// Initial position, read-only. start_pos: BytePos, /// The absolute offset within the source_map of the current character. - // FIXME(#64197): `pub` is needed by tests for now. - pub pos: BytePos, + pos: BytePos, /// Stop reading src at this index. end_src_index: usize, /// Source text to tokenize. @@ -93,9 +93,6 @@ impl<'a> StringReader<'a> { } /// Returns the next token, including trivia like whitespace or comments. - /// - /// `Err(())` means that some errors were encountered, which can be - /// retrieved using `buffer_fatal_errors`. pub fn next_token(&mut self) -> Token { let start_src_index = self.src_index(self.pos); let text: &str = &self.src[start_src_index..self.end_src_index]; @@ -225,8 +222,9 @@ impl<'a> StringReader<'a> { ident_start = ident_start + BytePos(2); } let sym = nfc_normalize(self.str_from(ident_start)); + let span = self.mk_sp(start, self.pos); + self.sess.symbol_gallery.insert(sym, span); if is_raw_ident { - let span = self.mk_sp(start, self.pos); if !sym.can_be_raw() { self.err_span(span, &format!("`{}` cannot be a raw identifier", sym)); } @@ -328,38 +326,27 @@ impl<'a> StringReader<'a> { suffix_start: BytePos, kind: rustc_lexer::LiteralKind, ) -> (token::LitKind, Symbol) { - match kind { + // prefix means `"` or `br"` or `r###"`, ... + let (lit_kind, mode, prefix_len, postfix_len) = match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { self.fatal_span_(start, suffix_start, "unterminated character literal").raise() } - let content_start = start + BytePos(1); - let content_end = suffix_start - BytePos(1); - self.validate_char_escape(content_start, content_end); - let id = self.symbol_from_to(content_start, content_end); - (token::Char, id) + (token::Char, Mode::Char, 1, 1) // ' ' } rustc_lexer::LiteralKind::Byte { terminated } => { if !terminated { self.fatal_span_(start + BytePos(1), suffix_start, "unterminated byte constant") .raise() } - let content_start = start + BytePos(2); - let content_end = suffix_start - BytePos(1); - self.validate_byte_escape(content_start, content_end); - let id = self.symbol_from_to(content_start, content_end); - (token::Byte, id) + (token::Byte, Mode::Byte, 2, 1) // b' ' } rustc_lexer::LiteralKind::Str { terminated } => { if !terminated { self.fatal_span_(start, suffix_start, "unterminated double quote string") .raise() } - let content_start = start + BytePos(1); - let content_end = suffix_start - BytePos(1); - self.validate_str_escape(content_start, content_end); - let id = self.symbol_from_to(content_start, content_end); - (token::Str, id) + (token::Str, Mode::Str, 1, 1) // " " } rustc_lexer::LiteralKind::ByteStr { terminated } => { if !terminated { @@ -370,50 +357,28 @@ impl<'a> StringReader<'a> { ) .raise() } - let content_start = start + BytePos(2); - let content_end = suffix_start - BytePos(1); - self.validate_byte_str_escape(content_start, content_end); - let id = self.symbol_from_to(content_start, content_end); - (token::ByteStr, id) + (token::ByteStr, Mode::ByteStr, 2, 1) // b" " } - rustc_lexer::LiteralKind::RawStr { n_hashes, started, terminated } => { - if !started { - self.report_non_started_raw_string(start); - } - if !terminated { - self.report_unterminated_raw_string(start, n_hashes) - } - let n_hashes: u16 = self.restrict_n_hashes(start, n_hashes); + rustc_lexer::LiteralKind::RawStr(unvalidated_raw_str) => { + let valid_raw_str = self.validate_and_report_errors(start, unvalidated_raw_str); + let n_hashes = valid_raw_str.num_hashes(); let n = u32::from(n_hashes); - let content_start = start + BytePos(2 + n); - let content_end = suffix_start - BytePos(1 + n); - self.validate_raw_str_escape(content_start, content_end); - let id = self.symbol_from_to(content_start, content_end); - (token::StrRaw(n_hashes), id) + (token::StrRaw(n_hashes), Mode::RawStr, 2 + n, 1 + n) // r##" "## } - rustc_lexer::LiteralKind::RawByteStr { n_hashes, started, terminated } => { - if !started { - self.report_non_started_raw_string(start); - } - if !terminated { - self.report_unterminated_raw_string(start, n_hashes) - } - let n_hashes: u16 = self.restrict_n_hashes(start, n_hashes); + rustc_lexer::LiteralKind::RawByteStr(unvalidated_raw_str) => { + let validated_raw_str = self.validate_and_report_errors(start, unvalidated_raw_str); + let n_hashes = validated_raw_str.num_hashes(); let n = u32::from(n_hashes); - let content_start = start + BytePos(3 + n); - let content_end = suffix_start - BytePos(1 + n); - self.validate_raw_byte_str_escape(content_start, content_end); - let id = self.symbol_from_to(content_start, content_end); - (token::ByteStrRaw(n_hashes), id) + (token::ByteStrRaw(n_hashes), Mode::RawByteStr, 3 + n, 1 + n) // br##" "## } rustc_lexer::LiteralKind::Int { base, empty_int } => { - if empty_int { + return if empty_int { self.err_span_(start, suffix_start, "no valid digits found for number"); (token::Integer, sym::integer(0)) } else { self.validate_int_literal(base, start, suffix_start); (token::Integer, self.symbol_from_to(start, suffix_start)) - } + }; } rustc_lexer::LiteralKind::Float { base, empty_exponent } => { if empty_exponent { @@ -441,9 +406,18 @@ impl<'a> StringReader<'a> { } let id = self.symbol_from_to(start, suffix_start); - (token::Float, id) + return (token::Float, id); } - } + }; + let content_start = start + BytePos(prefix_len); + let content_end = suffix_start - BytePos(postfix_len); + let id = self.symbol_from_to(content_start, content_end); + self.validate_literal_escape(mode, content_start, content_end); + return (lit_kind, id); + } + + pub fn pos(&self) -> BytePos { + self.pos } #[inline] @@ -485,6 +459,26 @@ impl<'a> StringReader<'a> { } } + fn validate_and_report_errors( + &self, + start: BytePos, + unvalidated_raw_str: UnvalidatedRawStr, + ) -> ValidatedRawStr { + match unvalidated_raw_str.validate() { + Err(LexRawStrError::InvalidStarter) => self.report_non_started_raw_string(start), + Err(LexRawStrError::NoTerminator { expected, found, possible_terminator_offset }) => { + self.report_unterminated_raw_string( + start, + expected, + possible_terminator_offset, + found, + ) + } + Err(LexRawStrError::TooManyDelimiters) => self.report_too_many_hashes(start), + Ok(valid) => valid, + } + } + fn report_non_started_raw_string(&self, start: BytePos) -> ! { let bad_char = self.str_from(start).chars().last().unwrap(); self.struct_fatal_span_char( @@ -498,130 +492,70 @@ impl<'a> StringReader<'a> { FatalError.raise() } - fn report_unterminated_raw_string(&self, start: BytePos, n_hashes: usize) -> ! { + fn report_unterminated_raw_string( + &self, + start: BytePos, + n_hashes: usize, + possible_offset: Option, + found_terminators: usize, + ) -> ! { let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code( self.mk_sp(start, start), "unterminated raw string", error_code!(E0748), ); + err.span_label(self.mk_sp(start, start), "unterminated raw string"); if n_hashes > 0 { err.note(&format!( "this raw string should be terminated with `\"{}`", - "#".repeat(n_hashes as usize) + "#".repeat(n_hashes) )); } - err.emit(); - FatalError.raise() - } - - fn restrict_n_hashes(&self, start: BytePos, n_hashes: usize) -> u16 { - match n_hashes.try_into() { - Ok(n_hashes) => n_hashes, - Err(_) => { - self.fatal_span_( - start, - self.pos, - "too many `#` symbols: raw strings may be \ - delimited by up to 65535 `#` symbols", - ) - .raise(); - } + if let Some(possible_offset) = possible_offset { + let lo = start + BytePos(possible_offset as u32); + let hi = lo + BytePos(found_terminators as u32); + let span = self.mk_sp(lo, hi); + err.span_suggestion( + span, + "consider terminating the string here", + "#".repeat(n_hashes), + Applicability::MaybeIncorrect, + ); } - } - fn validate_char_escape(&self, content_start: BytePos, content_end: BytePos) { - let lit = self.str_from_to(content_start, content_end); - if let Err((off, err)) = unescape::unescape_char(lit) { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::Char, - 0..off, - err, - ) - } - } - - fn validate_byte_escape(&self, content_start: BytePos, content_end: BytePos) { - let lit = self.str_from_to(content_start, content_end); - if let Err((off, err)) = unescape::unescape_byte(lit) { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::Byte, - 0..off, - err, - ) - } - } - - fn validate_str_escape(&self, content_start: BytePos, content_end: BytePos) { - let lit = self.str_from_to(content_start, content_end); - unescape::unescape_str(lit, &mut |range, c| { - if let Err(err) = c { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::Str, - range, - err, - ) - } - }) - } - - fn validate_raw_str_escape(&self, content_start: BytePos, content_end: BytePos) { - let lit = self.str_from_to(content_start, content_end); - unescape::unescape_raw_str(lit, &mut |range, c| { - if let Err(err) = c { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::Str, - range, - err, - ) - } - }) + err.emit(); + FatalError.raise() } - fn validate_raw_byte_str_escape(&self, content_start: BytePos, content_end: BytePos) { - let lit = self.str_from_to(content_start, content_end); - unescape::unescape_raw_byte_str(lit, &mut |range, c| { - if let Err(err) = c { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::ByteStr, - range, - err, - ) - } - }) + fn report_too_many_hashes(&self, start: BytePos) -> ! { + self.fatal_span_( + start, + self.pos, + "too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols", + ) + .raise(); } - fn validate_byte_str_escape(&self, content_start: BytePos, content_end: BytePos) { - let lit = self.str_from_to(content_start, content_end); - unescape::unescape_byte_str(lit, &mut |range, c| { - if let Err(err) = c { + fn validate_literal_escape(&self, mode: Mode, content_start: BytePos, content_end: BytePos) { + let lit_content = self.str_from_to(content_start, content_end); + unescape::unescape_literal(lit_content, mode, &mut |range, result| { + // Here we only check for errors. The actual unescaping is done later. + if let Err(err) = result { + let span_with_quotes = + self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)); emit_unescape_error( &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::ByteStr, + lit_content, + span_with_quotes, + mode, range, err, - ) + ); } - }) + }); } fn validate_int_literal(&self, base: Base, content_start: BytePos, content_end: BytePos) { diff --git a/src/librustc_parse/lexer/tokentrees.rs b/src/librustc_parse/lexer/tokentrees.rs index b65b894172844..c08659ec9f648 100644 --- a/src/librustc_parse/lexer/tokentrees.rs +++ b/src/librustc_parse/lexer/tokentrees.rs @@ -1,6 +1,6 @@ use super::{StringReader, UnmatchedBrace}; -use rustc_ast::token::{self, Token}; +use rustc_ast::token::{self, DelimToken, Token}; use rustc_ast::tokenstream::{ DelimSpan, IsJoint::{self, *}, @@ -22,6 +22,7 @@ impl<'a> StringReader<'a> { matching_delim_spans: Vec::new(), last_unclosed_found_span: None, last_delim_empty_block_spans: FxHashMap::default(), + matching_block_spans: Vec::new(), }; let res = tt_reader.parse_all_token_trees(); (res, tt_reader.unmatched_braces) @@ -42,6 +43,9 @@ struct TokenTreesReader<'a> { last_unclosed_found_span: Option, /// Collect empty block spans that might have been auto-inserted by editors. last_delim_empty_block_spans: FxHashMap, + /// Collect the spans of braces (Open, Close). Used only + /// for detecting if blocks are empty and only braces. + matching_block_spans: Vec<(Span, Span)>, } impl<'a> TokenTreesReader<'a> { @@ -77,6 +81,7 @@ impl<'a> TokenTreesReader<'a> { fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> { let sm = self.string_reader.sess.source_map(); + match self.token.kind { token::Eof => { let msg = "this file contains an unclosed delimiter"; @@ -146,6 +151,14 @@ impl<'a> TokenTreesReader<'a> { } } + match (open_brace, delim) { + //only add braces + (DelimToken::Brace, DelimToken::Brace) => { + self.matching_block_spans.push((open_brace_span, close_brace_span)); + } + _ => {} + } + if self.open_braces.is_empty() { // Clear up these spans to avoid suggesting them as we've found // properly matched delimiters so far for an entire block. @@ -164,6 +177,7 @@ impl<'a> TokenTreesReader<'a> { token::CloseDelim(other) => { let mut unclosed_delimiter = None; let mut candidate = None; + if self.last_unclosed_found_span != Some(self.token.span) { // do not complain about the same unclosed delimiter multiple times self.last_unclosed_found_span = Some(self.token.span); @@ -224,12 +238,27 @@ impl<'a> TokenTreesReader<'a> { let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg); - if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { - err.span_label( - span, - "this block is empty, you might have not meant to close it", - ); + // Braces are added at the end, so the last element is the biggest block + if let Some(parent) = self.matching_block_spans.last() { + if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { + // Check if the (empty block) is in the last properly closed block + if (parent.0.to(parent.1)).contains(span) { + err.span_label( + span, + "block is empty, you might have not meant to close it", + ); + } else { + err.span_label(parent.0, "this opening brace..."); + + err.span_label(parent.1, "...matches this closing brace"); + } + } else { + err.span_label(parent.0, "this opening brace..."); + + err.span_label(parent.1, "...matches this closing brace"); + } } + err.span_label(self.token.span, "unexpected closing delimiter"); Err(err) } diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 13fb85db84779..8e2a9513d6b82 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -4,6 +4,7 @@ #![feature(crate_visibility_modifier)] #![feature(bindings_after_at)] #![feature(try_blocks)] +#![feature(or_patterns)] use rustc_ast::ast; use rustc_ast::token::{self, Nonterminal}; diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs index b56dd30739dae..803f14a2a228a 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/src/librustc_parse/parser/attr.rs @@ -4,7 +4,7 @@ use rustc_ast::attr; use rustc_ast::token::{self, Nonterminal}; use rustc_ast::util::comments; use rustc_ast_pretty::pprust; -use rustc_errors::PResult; +use rustc_errors::{error_code, PResult}; use rustc_span::{Span, Symbol}; use log::debug; @@ -50,10 +50,16 @@ impl<'a> Parser<'a> { } else if let token::DocComment(s) = self.token.kind { let attr = self.mk_doc_comment(s); if attr.style != ast::AttrStyle::Outer { - self.struct_span_err(self.token.span, "expected outer doc comment") + self.sess + .span_diagnostic + .struct_span_err_with_code( + self.token.span, + "expected outer doc comment", + error_code!(E0753), + ) .note( "inner doc comments like this (starting with \ - `//!` or `/*!`) can only appear before items", + `//!` or `/*!`) can only appear before items", ) .emit(); } diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 87255386b9e66..93c7faf22a73f 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -1,23 +1,20 @@ use super::ty::AllowPlus; use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; -use rustc_ast::ast::{ - self, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, Param, -}; +use rustc_ast::ast::{self, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Item, Param}; use rustc_ast::ast::{AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind}; use rustc_ast::ptr::P; -use rustc_ast::token::{self, TokenKind}; +use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err}; use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Ident}; use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP}; use log::{debug, trace}; -use std::mem; const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments"; @@ -256,6 +253,10 @@ impl<'a> Parser<'a> { } } + if self.check_too_many_raw_str_terminators(&mut err) { + return Err(err); + } + let sm = self.sess.source_map(); if self.prev_token.span == DUMMY_SP { // Account for macro context where the previous span might not be @@ -283,6 +284,29 @@ impl<'a> Parser<'a> { Err(err) } + fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool { + match (&self.prev_token.kind, &self.token.kind) { + ( + TokenKind::Literal(Lit { + kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes), + .. + }), + TokenKind::Pound, + ) => { + err.set_primary_message("too many `#` when terminating raw string"); + err.span_suggestion( + self.token.span, + "remove the extra `#`", + String::new(), + Applicability::MachineApplicable, + ); + err.note(&format!("the raw string started with {} `#`s", n_hashes)); + true + } + _ => false, + } + } + pub fn maybe_annotate_with_ascription( &mut self, err: &mut DiagnosticBuilder<'_>, @@ -459,48 +483,85 @@ impl<'a> Parser<'a> { err: &mut DiagnosticBuilder<'_>, inner_op: &Expr, outer_op: &Spanned, - ) { + ) -> bool /* advanced the cursor */ { if let ExprKind::Binary(op, ref l1, ref r1) = inner_op.kind { - match (op.node, &outer_op.node) { + if let ExprKind::Field(_, ident) = l1.kind { + if ident.as_str().parse::().is_err() && !matches!(r1.kind, ExprKind::Lit(_)) { + // The parser has encountered `foo.bar y > z` and friends. - (BinOpKind::Gt, AssocOp::Greater) | (BinOpKind::Gt, AssocOp::GreaterEqual) | - (BinOpKind::Ge, AssocOp::GreaterEqual) | (BinOpKind::Ge, AssocOp::Greater) => { + (BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) | + (BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => { let expr_to_str = |e: &Expr| { self.span_to_snippet(e.span) .unwrap_or_else(|_| pprust::expr_to_string(&e)) }; - err.span_suggestion( - inner_op.span.to(outer_op.span), - "split the comparison into two...", - format!( - "{} {} {} && {} {}", - expr_to_str(&l1), - op.node.to_string(), - expr_to_str(&r1), - expr_to_str(&r1), - outer_op.node.to_ast_binop().unwrap().to_string(), - ), - Applicability::MaybeIncorrect, - ); - err.span_suggestion( - inner_op.span.to(outer_op.span), - "...or parenthesize one of the comparisons", - format!( - "({} {} {}) {}", - expr_to_str(&l1), - op.node.to_string(), - expr_to_str(&r1), - outer_op.node.to_ast_binop().unwrap().to_string(), - ), + err.span_suggestion_verbose( + inner_op.span.shrink_to_hi(), + "split the comparison into two", + format!(" && {}", expr_to_str(&r1)), Applicability::MaybeIncorrect, ); + false // Keep the current parse behavior, where the AST is `(x < y) < z`. } - _ => {} - } + // `x == y < z` + (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { + // Consume `z`/outer-op-rhs. + let snapshot = self.clone(); + match self.parse_expr() { + Ok(r2) => { + // We are sure that outer-op-rhs could be consumed, the suggestion is + // likely correct. + enclose(r1.span, r2.span); + true + } + Err(mut expr_err) => { + expr_err.cancel(); + *self = snapshot; + false + } + } + } + // `x > y == z` + (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => { + let snapshot = self.clone(); + // At this point it is always valid to enclose the lhs in parentheses, no + // further checks are necessary. + match self.parse_expr() { + Ok(_) => { + enclose(l1.span, r1.span); + true + } + Err(mut expr_err) => { + expr_err.cancel(); + *self = snapshot; + false + } + } + } + _ => false, + }; } + false } /// Produces an error if comparison operators are chained (RFC #558). @@ -514,11 +575,13 @@ impl<'a> Parser<'a> { /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left /// associative we can infer that we have: /// + /// ```text /// outer_op /// / \ /// inner_op r2 /// / \ /// l1 r1 + /// ``` pub(super) fn check_no_chained_comparison( &mut self, inner_op: &Expr, @@ -534,31 +597,26 @@ impl<'a> Parser<'a> { |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new()))); match inner_op.kind { - ExprKind::Binary(op, _, _) if op.node.is_comparison() => { - // Respan to include both operators. - let op_span = op.span.to(self.prev_token.span); - let mut err = - self.struct_span_err(op_span, "comparison operators cannot be chained"); - - // If it looks like a genuine attempt to chain operators (as opposed to a - // misformatted turbofish, for instance), suggest a correct form. - self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); + ExprKind::Binary(op, ref l1, ref r1) if op.node.is_comparison() => { + let mut err = self.struct_span_err( + vec![op.span, self.prev_token.span], + "comparison operators cannot be chained", + ); let suggest = |err: &mut DiagnosticBuilder<'_>| { err.span_suggestion_verbose( - op_span.shrink_to_lo(), + op.span.shrink_to_lo(), TURBOFISH, "::".to_string(), Applicability::MaybeIncorrect, ); }; - if op.node == BinOpKind::Lt && - outer_op.node == AssocOp::Less || // Include `<` to provide this recommendation - outer_op.node == AssocOp::Greater - // even in a case like the following: + // Include `<` to provide this recommendation even in a case like + // `Foo>>` + if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less + || outer_op.node == AssocOp::Greater { - // Foo>> if outer_op.node == AssocOp::Less { let snapshot = self.clone(); self.bump(); @@ -572,7 +630,7 @@ impl<'a> Parser<'a> { { // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the // parser and bail out. - mem::replace(self, snapshot.clone()); + *self = snapshot.clone(); } } return if token::ModSep == self.token.kind { @@ -597,7 +655,7 @@ impl<'a> Parser<'a> { expr_err.cancel(); // Not entirely sure now, but we bubble the error up with the // suggestion. - mem::replace(self, snapshot); + *self = snapshot; Err(err) } } @@ -617,15 +675,33 @@ impl<'a> Parser<'a> { } } } else { - // All we know is that this is `foo < bar >` and *nothing* else. Try to - // be helpful, but don't attempt to recover. - err.help(TURBOFISH); - err.help("or use `(...)` if you meant to specify fn arguments"); - // These cases cause too many knock-down errors, bail out (#61329). - Err(err) + if !matches!(l1.kind, ExprKind::Lit(_)) + && !matches!(r1.kind, ExprKind::Lit(_)) + { + // All we know is that this is `foo < bar >` and *nothing* else. Try to + // be helpful, but don't attempt to recover. + err.help(TURBOFISH); + err.help("or use `(...)` if you meant to specify fn arguments"); + } + + // If it looks like a genuine attempt to chain operators (as opposed to a + // misformatted turbofish, for instance), suggest a correct form. + if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op) + { + err.emit(); + mk_err_expr(self, inner_op.span.to(self.prev_token.span)) + } else { + // These cases cause too many knock-down errors, bail out (#61329). + Err(err) + } }; } + let recover = + self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); err.emit(); + if recover { + return mk_err_expr(self, inner_op.span.to(self.prev_token.span)); + } } _ => {} } @@ -643,7 +719,7 @@ impl<'a> Parser<'a> { if self.token.kind == token::Eof { // Not entirely sure that what we consumed were fn arguments, rollback. - mem::replace(self, snapshot); + *self = snapshot; Err(()) } else { // 99% certain that the suggestion is correct, continue parsing. @@ -851,7 +927,7 @@ impl<'a> Parser<'a> { return Ok(()); } let sm = self.sess.source_map(); - let msg = format!("expected `;`, found `{}`", super::token_descr(&self.token)); + let msg = format!("expected `;`, found {}", super::token_descr(&self.token)); let appl = Applicability::MachineApplicable; if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { // Likely inside a macro, can't provide meaningful suggestions. @@ -976,6 +1052,39 @@ impl<'a> Parser<'a> { } } + pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P> { + let is_try = self.token.is_keyword(kw::Try); + let is_questionmark = self.look_ahead(1, |t| t == &token::Not); //check for ! + let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(token::Paren)); //check for ( + + if is_try && is_questionmark && is_open { + let lo = self.token.span; + self.bump(); //remove try + self.bump(); //remove ! + let try_span = lo.to(self.token.span); //we take the try!( span + self.bump(); //remove ( + let is_empty = self.token == token::CloseDelim(token::Paren); //check if the block is empty + self.consume_block(token::Paren, ConsumeClosingDelim::No); //eat the block + let hi = self.token.span; + self.bump(); //remove ) + let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro"); + err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated"); + let prefix = if is_empty { "" } else { "alternatively, " }; + if !is_empty { + err.multipart_suggestion( + "you can use the `?` operator instead", + vec![(try_span, "".to_owned()), (hi, "?".to_owned())], + Applicability::MachineApplicable, + ); + } + err.span_suggestion(lo.shrink_to_lo(), &format!("{}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax", prefix), "r#".to_string(), Applicability::MachineApplicable); + err.emit(); + Ok(self.mk_expr_err(lo.to(hi))) + } else { + Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro + } + } + /// Recovers a situation like `for ( $pat in $expr )` /// and suggest writing `for $pat in $expr` instead. /// @@ -1035,7 +1144,7 @@ impl<'a> Parser<'a> { self.look_ahead(2, |t| t.is_ident()) || self.look_ahead(1, |t| t == &token::ModSep) && (self.look_ahead(2, |t| t.is_ident()) || // `foo:bar::baz` - self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::` + self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::` } pub(super) fn recover_seq_parse_error( diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index b205a4b322229..ca497a3b06f4a 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -4,7 +4,7 @@ use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType}; use crate::maybe_recover_from_interpolated_ty_qpath; -use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Ident, Lit, UnOp, DUMMY_NODE_ID}; +use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Lit, UnOp, DUMMY_NODE_ID}; use rustc_ast::ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; use rustc_ast::ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::ptr::P; @@ -13,9 +13,9 @@ use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; use rustc_span::source_map::{self, Span, Spanned}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use std::mem; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression @@ -547,8 +547,7 @@ impl<'a> Parser<'a> { // Rewind to before attempting to parse the type with generics, to recover // from situations like `x as usize < y` in which we first tried to parse // `usize < y` as a type with generic arguments. - let parser_snapshot_after_type = self.clone(); - mem::replace(self, parser_snapshot_before_type); + let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); match self.parse_path(PathStyle::Expr) { Ok(path) => { @@ -560,7 +559,7 @@ impl<'a> Parser<'a> { // example because `parse_ty_no_plus` returns `Err` on keywords, // but `parse_path` returns `Ok` on them due to error recovery. // Return original error and parser state. - mem::replace(self, parser_snapshot_after_type); + *self = parser_snapshot_after_type; return Err(type_err); } }; @@ -601,7 +600,7 @@ impl<'a> Parser<'a> { Err(mut path_err) => { // Couldn't parse as a path, return original error and parser state. path_err.cancel(); - mem::replace(self, parser_snapshot_after_type); + *self = parser_snapshot_after_type; return Err(type_err); } } @@ -638,6 +637,7 @@ impl<'a> Parser<'a> { ExprKind::MethodCall(_, _) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_) => "`.await`", + ExprKind::Err => return Ok(with_postfix), _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), } ); @@ -1005,7 +1005,7 @@ impl<'a> Parser<'a> { let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal), attrs); self.maybe_recover_from_bad_qpath(expr, true) } - None => Err(self.expected_expression_found()), + None => self.try_macro_suggestion(), } } @@ -1068,8 +1068,8 @@ impl<'a> Parser<'a> { } fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let lo = self.token.span; let path = self.parse_path(PathStyle::Expr)?; + let lo = path.span; // `!`, as an operator, is prefix, so we know this isn't that. let (hi, kind) = if self.eat(&token::Not) { @@ -1081,7 +1081,7 @@ impl<'a> Parser<'a> { }; (self.prev_token.span, ExprKind::MacCall(mac)) } else if self.check(&token::OpenDelim(token::Brace)) { - if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) { + if let Some(expr) = self.maybe_parse_struct_expr(&path, &attrs) { return expr; } else { (path.span, ExprKind::Path(None, path)) @@ -1549,6 +1549,11 @@ impl<'a> Parser<'a> { let block = self.parse_block().map_err(|mut err| { if not_block { err.span_label(lo, "this `if` expression has a condition, but no block"); + if let ExprKind::Binary(_, _, ref right) = cond.kind { + if let ExprKind::Block(_, _) = right.kind { + err.help("maybe you forgot the right operand of the condition?"); + } + } } err })?; @@ -1845,11 +1850,9 @@ impl<'a> Parser<'a> { } fn is_try_block(&self) -> bool { - self.token.is_keyword(kw::Try) && - self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) && - self.token.uninterpolated_span().rust_2018() && - // Prevent `while try {} {}`, `if try {} {} else {}`, etc. - !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) + self.token.is_keyword(kw::Try) + && self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) + && self.token.uninterpolated_span().rust_2018() } /// Parses an `async move? {...}` expression. @@ -1892,16 +1895,15 @@ impl<'a> Parser<'a> { fn maybe_parse_struct_expr( &mut self, - lo: Span, path: &ast::Path, attrs: &AttrVec, ) -> Option>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); if struct_allowed || self.is_certainly_not_a_block() { // This is a struct literal, but we don't can't accept them here. - let expr = self.parse_struct_expr(lo, path.clone(), attrs.clone()); + let expr = self.parse_struct_expr(path.clone(), attrs.clone()); if let (Ok(expr), false) = (&expr, struct_allowed) { - self.error_struct_lit_not_allowed_here(lo, expr.span); + self.error_struct_lit_not_allowed_here(path.span, expr.span); } return Some(expr); } @@ -1920,17 +1922,23 @@ impl<'a> Parser<'a> { pub(super) fn parse_struct_expr( &mut self, - lo: Span, pth: ast::Path, mut attrs: AttrVec, ) -> PResult<'a, P> { - let struct_sp = lo.to(self.prev_token.span); self.bump(); let mut fields = Vec::new(); let mut base = None; + let mut recover_async = false; attrs.extend(self.parse_inner_attributes()?); + let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| { + recover_async = true; + e.span_label(span, "`async` blocks are only allowed in the 2018 edition"); + e.help("set `edition = \"2018\"` in `Cargo.toml`"); + e.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + }; + while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { let exp_span = self.prev_token.span; @@ -1949,7 +1957,11 @@ impl<'a> Parser<'a> { let parsed_field = match self.parse_field() { Ok(f) => Some(f), Err(mut e) => { - e.span_label(struct_sp, "while parsing this struct"); + if pth == kw::Async { + async_block_err(&mut e, pth.span); + } else { + e.span_label(pth.span, "while parsing this struct"); + } e.emit(); // If the next token is a comma, then try to parse @@ -1973,15 +1985,19 @@ impl<'a> Parser<'a> { } } Err(mut e) => { - e.span_label(struct_sp, "while parsing this struct"); - if let Some(f) = recovery_field { - fields.push(f); - e.span_suggestion( - self.prev_token.span.shrink_to_hi(), - "try adding a comma", - ",".into(), - Applicability::MachineApplicable, - ); + if pth == kw::Async { + async_block_err(&mut e, pth.span); + } else { + e.span_label(pth.span, "while parsing this struct"); + if let Some(f) = recovery_field { + fields.push(f); + e.span_suggestion( + self.prev_token.span.shrink_to_hi(), + "try adding a comma", + ",".into(), + Applicability::MachineApplicable, + ); + } } e.emit(); self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); @@ -1990,9 +2006,10 @@ impl<'a> Parser<'a> { } } - let span = lo.to(self.token.span); + let span = pth.span.to(self.token.span); self.expect(&token::CloseDelim(token::Brace))?; - Ok(self.mk_expr(span, ExprKind::Struct(pth, fields, base), attrs)) + let expr = if recover_async { ExprKind::Err } else { ExprKind::Struct(pth, fields, base) }; + Ok(self.mk_expr(span, expr, attrs)) } /// Use in case of error after field-looking code: `S { foo: () with a }`. diff --git a/src/librustc_parse/parser/generics.rs b/src/librustc_parse/parser/generics.rs index 3442c5081c18f..8e8f864728ce6 100644 --- a/src/librustc_parse/parser/generics.rs +++ b/src/librustc_parse/parser/generics.rs @@ -8,7 +8,7 @@ use rustc_span::symbol::{kw, sym}; impl<'a> Parser<'a> { /// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. /// - /// ``` + /// ```text /// BOUND = LT_BOUND (e.g., `'a`) /// ``` fn parse_lt_param_bounds(&mut self) -> GenericBounds { @@ -105,7 +105,7 @@ impl<'a> Parser<'a> { } Err(mut err) => { err.cancel(); - std::mem::replace(self, snapshot); + *self = snapshot; break; } } diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 7a4f76804157f..4fe0453e9c87f 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -4,7 +4,7 @@ use super::{FollowedByType, Parser, PathStyle}; use crate::maybe_whole; -use rustc_ast::ast::{self, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID}; +use rustc_ast::ast::{self, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind, Mod}; use rustc_ast::ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind}; @@ -18,7 +18,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, PResult, StashKey}; use rustc_span::edition::Edition; use rustc_span::source_map::{self, Span}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use log::debug; use std::convert::TryFrom; @@ -743,7 +743,7 @@ impl<'a> Parser<'a> { /// Parses a `UseTree`. /// - /// ``` + /// ```text /// USE_TREE = [`::`] `*` | /// [`::`] `{` USE_TREE_LIST `}` | /// PATH `::` `*` | @@ -792,7 +792,7 @@ impl<'a> Parser<'a> { /// Parses a `UseTreeKind::Nested(list)`. /// - /// ``` + /// ```text /// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`] /// ``` fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> { @@ -804,7 +804,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::As) { self.parse_ident_or_underscore().map(Some) } else { Ok(None) } } - fn parse_ident_or_underscore(&mut self) -> PResult<'a, ast::Ident> { + fn parse_ident_or_underscore(&mut self) -> PResult<'a, Ident> { match self.token.ident() { Some((ident @ Ident { name: kw::Underscore, .. }, false)) => { self.bump(); @@ -834,7 +834,7 @@ impl<'a> Parser<'a> { Ok((item_name, ItemKind::ExternCrate(orig_name))) } - fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, ast::Ident> { + fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, Ident> { let error_msg = "crate name using dashes are not valid in `extern crate` statements"; let suggestion_msg = "if the original crate name uses dashes you need to use underscores \ in the code"; @@ -907,9 +907,11 @@ impl<'a> Parser<'a> { } fn error_bad_item_kind(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option { - let span = self.sess.source_map().def_span(span); - let msg = format!("{} is not supported in {}", kind.descr(), ctx); - self.struct_span_err(span, &msg).emit(); + let span = self.sess.source_map().guess_head_span(span); + let descr = kind.descr(); + self.struct_span_err(span, &format!("{} is not supported in {}", descr, ctx)) + .help(&format!("consider moving the {} out to a nearby module scope", descr)) + .emit(); None } @@ -1494,7 +1496,7 @@ impl<'a> Parser<'a> { } /// Is the current token the start of an `FnHeader` / not a valid parse? - fn check_fn_front_matter(&mut self) -> bool { + pub(super) fn check_fn_front_matter(&mut self) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. const QUALS: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern]; @@ -1521,7 +1523,7 @@ impl<'a> Parser<'a> { /// FnQual = "const"? "async"? "unsafe"? Extern? ; /// FnFrontMatter = FnQual? "fn" ; /// ``` - fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { + pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { let constness = self.parse_constness(); let asyncness = self.parse_asyncness(); let unsafety = self.parse_unsafety(); @@ -1548,7 +1550,7 @@ impl<'a> Parser<'a> { if span.rust_2015() { let diag = self.diagnostic(); struct_span_err!(diag, span, E0670, "`async fn` is not permitted in the 2015 edition") - .note("to use `async fn`, switch to Rust 2018") + .span_label(span, "to use `async fn`, switch to Rust 2018") .help("set `edition = \"2018\"` in `Cargo.toml`") .note("for more on editions, read https://doc.rust-lang.org/edition-guide") .emit(); @@ -1648,7 +1650,7 @@ impl<'a> Parser<'a> { // Recover from attempting to parse the argument as a type without pattern. Err(mut err) => { err.cancel(); - mem::replace(self, parser_snapshot_before_ty); + *self = parser_snapshot_before_ty; self.recover_arg_parse()? } } diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs index b987813e38d98..bdb4d7c9df6be 100644 --- a/src/librustc_parse/parser/mod.rs +++ b/src/librustc_parse/parser/mod.rs @@ -14,7 +14,7 @@ use crate::lexer::UnmatchedBrace; use log::debug; use rustc_ast::ast::DUMMY_NODE_ID; -use rustc_ast::ast::{self, AttrStyle, AttrVec, Const, CrateSugar, Extern, Ident, Unsafe}; +use rustc_ast::ast::{self, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; use rustc_ast::ast::{ Async, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind, }; @@ -26,7 +26,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult}; use rustc_session::parse::ParseSess; use rustc_span::source_map::{respan, Span, DUMMY_SP}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use std::{cmp, mem, slice}; @@ -424,11 +424,11 @@ impl<'a> Parser<'a> { } // Public for rustfmt usage. - pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> { + pub fn parse_ident(&mut self) -> PResult<'a, Ident> { self.parse_ident_common(true) } - fn parse_ident_common(&mut self, recover: bool) -> PResult<'a, ast::Ident> { + fn parse_ident_common(&mut self, recover: bool) -> PResult<'a, Ident> { match self.token.ident() { Some((ident, is_raw)) => { if !is_raw && ident.is_reserved() { @@ -1206,8 +1206,8 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec, sess: &Pa *sess.reached_eof.borrow_mut() |= unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none()); for unmatched in unclosed_delims.drain(..) { - make_unclosed_delims_error(unmatched, sess).map(|mut e| { + if let Some(mut e) = make_unclosed_delims_error(unmatched, sess) { e.emit(); - }); + } } } diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs index f7f7ac89a1864..6603d0afc0248 100644 --- a/src/librustc_parse/parser/pat.rs +++ b/src/librustc_parse/parser/pat.rs @@ -1,14 +1,14 @@ use super::{Parser, PathStyle}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ast::{self, AttrVec, Attribute, FieldPat, MacCall, Pat, PatKind, RangeEnd}; -use rustc_ast::ast::{BindingMode, Expr, ExprKind, Ident, Mutability, Path, QSelf, RangeSyntax}; +use rustc_ast::ast::{BindingMode, Expr, ExprKind, Mutability, Path, QSelf, RangeSyntax}; use rustc_ast::mut_visit::{noop_visit_mac, noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult}; use rustc_span::source_map::{respan, Span, Spanned}; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; type Expected = Option<&'static str>; @@ -162,7 +162,7 @@ impl<'a> Parser<'a> { _ => false, }); match (is_end_ahead, &self.token.kind) { - (true, token::BinOp(token::Or)) | (true, token::OrOr) => { + (true, token::BinOp(token::Or) | token::OrOr) => { self.ban_illegal_vert(lo, "trailing", "not allowed in an or-pattern"); self.bump(); true @@ -295,6 +295,8 @@ impl<'a> Parser<'a> { // A rest pattern `..`. self.bump(); // `..` PatKind::Rest + } else if self.check(&token::DotDotDot) && !self.is_pat_range_end_start(1) { + self.recover_dotdotdot_rest_pat(lo) } else if let Some(form) = self.parse_range_end() { self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. } else if self.eat_keyword(kw::Underscore) { @@ -362,6 +364,25 @@ impl<'a> Parser<'a> { Ok(pat) } + /// Recover from a typoed `...` pattern that was encountered + /// Ref: Issue #70388 + fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind { + // A typoed rest pattern `...`. + self.bump(); // `...` + + // The user probably mistook `...` for a rest pattern `..`. + self.struct_span_err(lo, "unexpected `...`") + .span_label(lo, "not a valid pattern") + .span_suggestion_short( + lo, + "for a rest pattern, use `..` instead of `...`", + "..".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + PatKind::Rest + } + /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`. /// /// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs` diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index f88b4fe6ff0a8..5210614548da3 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -1,16 +1,14 @@ use super::ty::{AllowPlus, RecoverQPath}; use super::{Parser, TokenType}; use crate::maybe_whole; -use rustc_ast::ast::{ - self, AngleBracketedArgs, Ident, ParenthesizedArgs, Path, PathSegment, QSelf, -}; -use rustc_ast::ast::{ - AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode, GenericArg, -}; +use rustc_ast::ast::{self, AngleBracketedArg, AngleBracketedArgs, GenericArg, ParenthesizedArgs}; +use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; +use rustc_ast::ast::{Path, PathSegment, QSelf}; +use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; use rustc_errors::{pluralize, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; use log::debug; use std::mem; @@ -218,11 +216,11 @@ impl<'a> Parser<'a> { let lo = self.token.span; let args = if self.eat_lt() { // `<'a, T, A = U>` - let (args, constraints) = - self.parse_generic_args_with_leading_angle_bracket_recovery(style, lo)?; + let args = + self.parse_angle_args_with_leading_angle_bracket_recovery(style, lo)?; self.expect_gt()?; let span = lo.to(self.prev_token.span); - AngleBracketedArgs { args, constraints, span }.into() + AngleBracketedArgs { args, span }.into() } else { // `(T, U) -> R` let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; @@ -251,18 +249,18 @@ impl<'a> Parser<'a> { /// Parses generic args (within a path segment) with recovery for extra leading angle brackets. /// For the purposes of understanding the parsing logic of generic arguments, this function - /// can be thought of being the same as just calling `self.parse_generic_args()` if the source + /// can be thought of being the same as just calling `self.parse_angle_args()` if the source /// had the correct amount of leading angle brackets. /// /// ```ignore (diagnostics) /// bar::<<<::Output>(); /// ^^ help: remove extra angle brackets /// ``` - fn parse_generic_args_with_leading_angle_bracket_recovery( + fn parse_angle_args_with_leading_angle_bracket_recovery( &mut self, style: PathStyle, lo: Span, - ) -> PResult<'a, (Vec, Vec)> { + ) -> PResult<'a, Vec> { // We need to detect whether there are extra leading left angle brackets and produce an // appropriate error and suggestion. This cannot be implemented by looking ahead at // upcoming tokens for a matching `>` character - if there are unmatched `<` tokens @@ -337,8 +335,8 @@ impl<'a> Parser<'a> { let snapshot = if is_first_invocation { Some(self.clone()) } else { None }; debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)"); - match self.parse_generic_args() { - Ok(value) => Ok(value), + match self.parse_angle_args() { + Ok(args) => Ok(args), Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => { // Cancel error from being unable to find `>`. We know the error // must have been this due to a non-zero unmatched angle bracket @@ -381,110 +379,136 @@ impl<'a> Parser<'a> { .emit(); // Try again without unmatched angle bracket characters. - self.parse_generic_args() + self.parse_angle_args() } Err(e) => Err(e), } } - /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, + /// Parses (possibly empty) list of generic arguments / associated item constraints, /// possibly including trailing comma. - fn parse_generic_args(&mut self) -> PResult<'a, (Vec, Vec)> { + fn parse_angle_args(&mut self) -> PResult<'a, Vec> { let mut args = Vec::new(); - let mut constraints = Vec::new(); - let mut misplaced_assoc_ty_constraints: Vec = Vec::new(); - let mut assoc_ty_constraints: Vec = Vec::new(); - - let args_lo = self.token.span; - - loop { - if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { - // Parse lifetime argument. - args.push(GenericArg::Lifetime(self.expect_lifetime())); - misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); - } else if self.check_ident() - && self.look_ahead(1, |t| t == &token::Eq || t == &token::Colon) - { - // Parse associated type constraint. - let lo = self.token.span; - let ident = self.parse_ident()?; - let kind = if self.eat(&token::Eq) { - AssocTyConstraintKind::Equality { ty: self.parse_ty()? } - } else if self.eat(&token::Colon) { - AssocTyConstraintKind::Bound { - bounds: self.parse_generic_bounds(Some(self.prev_token.span))?, - } - } else { - unreachable!(); - }; + while let Some(arg) = self.parse_angle_arg()? { + args.push(arg); + if !self.eat(&token::Comma) { + break; + } + } + Ok(args) + } - let span = lo.to(self.prev_token.span); + /// Parses a single argument in the angle arguments `<...>` of a path segment. + fn parse_angle_arg(&mut self) -> PResult<'a, Option> { + if self.check_ident() && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) + { + // Parse associated type constraint. + let lo = self.token.span; + let ident = self.parse_ident()?; + let kind = if self.eat(&token::Eq) { + let ty = self.parse_assoc_equality_term(ident, self.prev_token.span)?; + AssocTyConstraintKind::Equality { ty } + } else if self.eat(&token::Colon) { + let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; + AssocTyConstraintKind::Bound { bounds } + } else { + unreachable!(); + }; - // Gate associated type bounds, e.g., `Iterator`. - if let AssocTyConstraintKind::Bound { .. } = kind { - self.sess.gated_spans.gate(sym::associated_type_bounds, span); - } + let span = lo.to(self.prev_token.span); - constraints.push(AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }); - assoc_ty_constraints.push(span); - } else if self.check_const_arg() { - // Parse const argument. - let expr = if let token::OpenDelim(token::Brace) = self.token.kind { - self.parse_block_expr( - None, - self.token.span, - BlockCheckMode::Default, - ast::AttrVec::new(), - )? - } else if self.token.is_ident() { - // FIXME(const_generics): to distinguish between idents for types and consts, - // we should introduce a GenericArg::Ident in the AST and distinguish when - // lowering to the HIR. For now, idents for const args are not permitted. - if self.token.is_bool_lit() { - self.parse_literal_maybe_minus()? - } else { - let span = self.token.span; - let msg = "identifiers may currently not be used for const generics"; - self.struct_span_err(span, msg).emit(); - let block = self.mk_block_err(span); - self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new()) - } - } else { - self.parse_literal_maybe_minus()? - }; - let value = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; - args.push(GenericArg::Const(value)); - misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); - } else if self.check_type() { - // Parse type argument. - args.push(GenericArg::Type(self.parse_ty()?)); - misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); - } else { - break; + // Gate associated type bounds, e.g., `Iterator`. + if let AssocTyConstraintKind::Bound { .. } = kind { + self.sess.gated_spans.gate(sym::associated_type_bounds, span); } - if !self.eat(&token::Comma) { - break; - } + let constraint = AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span }; + Ok(Some(AngleBracketedArg::Constraint(constraint))) + } else { + Ok(self.parse_generic_arg()?.map(AngleBracketedArg::Arg)) } + } - // FIXME: we would like to report this in ast_validation instead, but we currently do not - // preserve ordering of generic parameters with respect to associated type binding, so we - // lose that information after parsing. - if !misplaced_assoc_ty_constraints.is_empty() { - let mut err = self.struct_span_err( - args_lo.to(self.prev_token.span), - "associated type bindings must be declared after generic parameters", - ); - for span in misplaced_assoc_ty_constraints { - err.span_label( - span, - "this associated type binding should be moved after the generic parameters", - ); + /// Parse the term to the right of an associated item equality constraint. + /// That is, parse `` in `Item = `. + /// Right now, this only admits types in ``. + fn parse_assoc_equality_term(&mut self, ident: Ident, eq: Span) -> PResult<'a, P> { + let arg = self.parse_generic_arg()?; + let span = ident.span.to(self.prev_token.span); + match arg { + Some(GenericArg::Type(ty)) => return Ok(ty), + Some(GenericArg::Const(expr)) => { + self.struct_span_err(span, "cannot constrain an associated constant to a value") + .span_label(ident.span, "this associated constant...") + .span_label(expr.value.span, "...cannot be constrained to this value") + .emit(); + } + Some(GenericArg::Lifetime(lt)) => { + self.struct_span_err(span, "associated lifetimes are not supported") + .span_label(lt.ident.span, "the lifetime is given here") + .help("if you meant to specify a trait object, write `dyn Trait + 'lifetime`") + .emit(); + } + None => { + let after_eq = eq.shrink_to_hi(); + let before_next = self.token.span.shrink_to_lo(); + self.struct_span_err(after_eq.to(before_next), "missing type to the right of `=`") + .span_suggestion( + self.sess.source_map().next_point(eq).to(before_next), + "to constrain the associated type, add a type after `=`", + " TheType".to_string(), + Applicability::HasPlaceholders, + ) + .span_suggestion( + eq.to(before_next), + &format!("remove the `=` if `{}` is a type", ident), + String::new(), + Applicability::MaybeIncorrect, + ) + .emit(); } - err.emit(); } + Ok(self.mk_ty(span, ast::TyKind::Err)) + } - Ok((args, constraints)) + /// Parse a generic argument in a path segment. + /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. + fn parse_generic_arg(&mut self) -> PResult<'a, Option> { + let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { + // Parse lifetime argument. + GenericArg::Lifetime(self.expect_lifetime()) + } else if self.check_const_arg() { + // Parse const argument. + let expr = if let token::OpenDelim(token::Brace) = self.token.kind { + self.parse_block_expr( + None, + self.token.span, + BlockCheckMode::Default, + ast::AttrVec::new(), + )? + } else if self.token.is_ident() { + // FIXME(const_generics): to distinguish between idents for types and consts, + // we should introduce a GenericArg::Ident in the AST and distinguish when + // lowering to the HIR. For now, idents for const args are not permitted. + if self.token.is_bool_lit() { + self.parse_literal_maybe_minus()? + } else { + let span = self.token.span; + let msg = "identifiers may currently not be used for const generics"; + self.struct_span_err(span, msg).emit(); + let block = self.mk_block_err(span); + self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new()) + } + } else { + self.parse_literal_maybe_minus()? + }; + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: expr }) + } else if self.check_type() { + // Parse type argument. + GenericArg::Type(self.parse_ty()?) + } else { + return Ok(None); + }; + Ok(Some(arg)) } } diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index fddfe48bf8670..849193151c335 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -79,7 +79,7 @@ impl<'a> Parser<'a> { } let expr = if self.check(&token::OpenDelim(token::Brace)) { - self.parse_struct_expr(lo, path, AttrVec::new())? + self.parse_struct_expr(path, AttrVec::new())? } else { let hi = self.prev_token.span; self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) @@ -163,11 +163,11 @@ impl<'a> Parser<'a> { Ok(ty) => (None, Some(ty)), Err(mut err) => { // Rewind to before attempting to parse the type and continue parsing. - let parser_snapshot_after_type = self.clone(); - mem::replace(self, parser_snapshot_before_type); - - let snippet = self.span_to_snippet(pat.span).unwrap(); - err.span_label(pat.span, format!("while parsing the type for `{}`", snippet)); + let parser_snapshot_after_type = + mem::replace(self, parser_snapshot_before_type); + if let Ok(snip) = self.span_to_snippet(pat.span) { + err.span_label(pat.span, format!("while parsing the type for `{}`", snip)); + } (Some((parser_snapshot_after_type, colon_sp, err)), None) } } @@ -201,7 +201,7 @@ impl<'a> Parser<'a> { // Couldn't parse the type nor the initializer, only raise the type error and // return to the parser state before parsing the type as the initializer. // let x: ; - mem::replace(self, snapshot); + *self = snapshot; return Err(ty_err); } (Err(err), None) => { diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs index c21ac8d04f194..a6015504a3287 100644 --- a/src/librustc_parse/parser/ty.rs +++ b/src/librustc_parse/parser/ty.rs @@ -127,16 +127,16 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.token_is_bare_fn_keyword() { + } else if self.check_fn_front_matter() { // Function pointer type - self.parse_ty_bare_fn(Vec::new())? + self.parse_ty_bare_fn(lo, Vec::new())? } else if self.check_keyword(kw::For) { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.token_is_bare_fn_keyword() { - self.parse_ty_bare_fn(lifetime_defs)? + if self.check_fn_front_matter() { + self.parse_ty_bare_fn(lo, lifetime_defs)? } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -291,13 +291,6 @@ impl<'a> Parser<'a> { Ok(TyKind::Typeof(expr)) } - /// Is the current token one of the keywords that signals a bare function type? - fn token_is_bare_fn_keyword(&mut self) -> bool { - self.check_keyword(kw::Fn) - || self.check_keyword(kw::Unsafe) - || self.check_keyword(kw::Extern) - } - /// Parses a function pointer type (`TyKind::BareFn`). /// ``` /// [unsafe] [extern "ABI"] fn (S) -> T @@ -306,12 +299,31 @@ impl<'a> Parser<'a> { /// | | | Return type /// Function Style ABI Parameter types /// ``` - fn parse_ty_bare_fn(&mut self, generic_params: Vec) -> PResult<'a, TyKind> { - let unsafety = self.parse_unsafety(); - let ext = self.parse_extern()?; - self.expect_keyword(kw::Fn)?; + /// We actually parse `FnHeader FnDecl`, but we error on `const` and `async` qualifiers. + fn parse_ty_bare_fn(&mut self, lo: Span, params: Vec) -> PResult<'a, TyKind> { + let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter()?; let decl = self.parse_fn_decl(|_| false, AllowPlus::No)?; - Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params, decl }))) + let whole_span = lo.to(self.prev_token.span); + if let ast::Const::Yes(span) = constness { + self.error_fn_ptr_bad_qualifier(whole_span, span, "const"); + } + if let ast::Async::Yes { span, .. } = asyncness { + self.error_fn_ptr_bad_qualifier(whole_span, span, "async"); + } + Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl }))) + } + + /// Emit an error for the given bad function pointer qualifier. + fn error_fn_ptr_bad_qualifier(&self, span: Span, qual_span: Span, qual: &str) { + self.struct_span_err(span, &format!("an `fn` pointer type cannot be `{}`", qual)) + .span_label(qual_span, format!("`{}` because of this", qual)) + .span_suggestion_short( + qual_span, + &format!("remove the `{}` qualifier", qual), + String::new(), + Applicability::MaybeIncorrect, + ) + .emit(); } /// Parses an `impl B0 + ... + Bn` type. diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index 8acb88f58d379..69048cbf24a30 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -10,14 +10,12 @@ path = "lib.rs" [dependencies] log = "0.4" -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_attr = { path = "../librustc_attr" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } -rustc_feature = { path = "../librustc_feature" } rustc_hir = { path = "../librustc_hir" } rustc_index = { path = "../librustc_index" } -rustc_infer = { path = "../librustc_infer" } rustc_session = { path = "../librustc_session" } rustc_target = { path = "../librustc_target" } rustc_ast = { path = "../librustc_ast" } diff --git a/src/librustc_passes/check_attr.rs b/src/librustc_passes/check_attr.rs index 583e1fdc1f05f..b54731d8881d1 100644 --- a/src/librustc_passes/check_attr.rs +++ b/src/librustc_passes/check_attr.rs @@ -4,9 +4,9 @@ //! conflicts between multiple such attributes attached to the same //! item. -use rustc::hir::map::Map; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_ast::ast::{Attribute, NestedMetaItem}; use rustc_ast::attr; @@ -14,7 +14,6 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::DUMMY_HIR_ID; use rustc_hir::{self, HirId, Item, ItemKind, TraitItem}; use rustc_hir::{MethodKind, Target}; use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES}; @@ -77,8 +76,8 @@ impl CheckAttrVisitor<'tcx> { return; } - if target == Target::Fn { - self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id)); + if matches!(target, Target::Fn | Target::Method(_) | Target::ForeignFn) { + self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id)); } self.check_repr(attrs, span, target, item, hir_id); @@ -90,8 +89,7 @@ impl CheckAttrVisitor<'tcx> { match target { Target::Fn | Target::Closure - | Target::Method(MethodKind::Trait { body: true }) - | Target::Method(MethodKind::Inherent) => true, + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { lint.build("`#[inline]` is ignored on function prototypes").emit() @@ -141,7 +139,7 @@ impl CheckAttrVisitor<'tcx> { target: Target, ) -> bool { match target { - Target::Fn if attr::contains_name(attrs, sym::naked) => { + _ if attr::contains_name(attrs, sym::naked) => { struct_span_err!( self.tcx.sess, *attr_span, @@ -151,17 +149,7 @@ impl CheckAttrVisitor<'tcx> { .emit(); false } - Target::ForeignFn => { - struct_span_err!( - self.tcx.sess, - *attr_span, - E0738, - "`#[track_caller]` is not supported on foreign functions", - ) - .emit(); - false - } - Target::Fn | Target::Method(..) => true, + Target::Fn | Target::Method(..) | Target::ForeignFn => true, _ => { struct_span_err!( self.tcx.sess, @@ -213,8 +201,7 @@ impl CheckAttrVisitor<'tcx> { fn check_target_feature(&self, attr: &Attribute, span: &Span, target: Target) -> bool { match target { Target::Fn - | Target::Method(MethodKind::Trait { body: true }) - | Target::Method(MethodKind::Inherent) => true, + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, _ => { self.tcx .sess @@ -370,7 +357,7 @@ impl CheckAttrVisitor<'tcx> { if let hir::StmtKind::Local(ref l) = stmt.kind { for attr in l.attrs.iter() { if attr.check_name(sym::inline) { - self.check_inline(DUMMY_HIR_ID, attr, &stmt.span, Target::Statement); + self.check_inline(l.hir_id, attr, &stmt.span, Target::Statement); } if attr.check_name(sym::repr) { self.emit_repr_error( @@ -391,7 +378,7 @@ impl CheckAttrVisitor<'tcx> { }; for attr in expr.attrs.iter() { if attr.check_name(sym::inline) { - self.check_inline(DUMMY_HIR_ID, attr, &expr.span, target); + self.check_inline(expr.hir_id, attr, &expr.span, target); } if attr.check_name(sym::repr) { self.emit_repr_error( @@ -402,6 +389,9 @@ impl CheckAttrVisitor<'tcx> { ); } } + if target == Target::Closure { + self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(expr.hir_id)); + } } fn check_used(&self, attrs: &'hir [Attribute], target: Target) { diff --git a/src/librustc_passes/check_const.rs b/src/librustc_passes/check_const.rs index 30737360b9c20..94f9c619a3a26 100644 --- a/src/librustc_passes/check_const.rs +++ b/src/librustc_passes/check_const.rs @@ -7,20 +7,17 @@ //! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips //! through, but errors for structured control flow in a `const` should be emitted here. -use rustc::hir::map::Map; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; -use rustc_ast::ast::Mutability; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::{sym, Span, Symbol}; -use std::fmt; - /// An expression that is not *always* legal in a const context. #[derive(Clone, Copy)] enum NonConstExpr { @@ -52,8 +49,9 @@ impl NonConstExpr { Self::Loop(While) | Self::Loop(WhileLet) - | Self::Match(WhileDesugar) - | Self::Match(WhileLetDesugar) => &[sym::const_loop, sym::const_if_match], + | Self::Match(WhileDesugar | WhileLetDesugar) => { + &[sym::const_loop, sym::const_if_match] + } // A `for` loop's desugaring contains a call to `IntoIterator::into_iter`, // so they are not yet allowed with `#![feature(const_loop)]`. @@ -64,46 +62,6 @@ impl NonConstExpr { } } -#[derive(Copy, Clone)] -enum ConstKind { - Static, - StaticMut, - ConstFn, - Const, - AnonConst, -} - -impl ConstKind { - fn for_body(body: &hir::Body<'_>, hir_map: Map<'_>) -> Option { - let is_const_fn = |id| hir_map.fn_sig_by_hir_id(id).unwrap().header.is_const(); - - let owner = hir_map.body_owner(body.id()); - let const_kind = match hir_map.body_owner_kind(owner) { - hir::BodyOwnerKind::Const => Self::Const, - hir::BodyOwnerKind::Static(Mutability::Mut) => Self::StaticMut, - hir::BodyOwnerKind::Static(Mutability::Not) => Self::Static, - - hir::BodyOwnerKind::Fn if is_const_fn(owner) => Self::ConstFn, - hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => return None, - }; - - Some(const_kind) - } -} - -impl fmt::Display for ConstKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - Self::Static => "static", - Self::StaticMut => "static mut", - Self::Const | Self::AnonConst => "const", - Self::ConstFn => "const fn", - }; - - write!(f, "{}", s) - } -} - fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: DefId) { let mut vis = CheckConstVisitor::new(tcx); tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor()); @@ -116,7 +74,7 @@ pub(crate) fn provide(providers: &mut Providers<'_>) { #[derive(Copy, Clone)] struct CheckConstVisitor<'tcx> { tcx: TyCtxt<'tcx>, - const_kind: Option, + const_kind: Option, } impl<'tcx> CheckConstVisitor<'tcx> { @@ -146,7 +104,8 @@ impl<'tcx> CheckConstVisitor<'tcx> { let const_kind = self .const_kind .expect("`const_check_violated` may only be called inside a const context"); - let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind); + + let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); let required_gates = required_gates.unwrap_or(&[]); let missing_gates: Vec<_> = @@ -190,7 +149,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { } /// Saves the parent `const_kind` before calling `f` and restores it afterwards. - fn recurse_into(&mut self, kind: Option, f: impl FnOnce(&mut Self)) { + fn recurse_into(&mut self, kind: Option, f: impl FnOnce(&mut Self)) { let parent_kind = self.const_kind; self.const_kind = kind; f(self); @@ -206,12 +165,13 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { } fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { - let kind = Some(ConstKind::AnonConst); + let kind = Some(hir::ConstContext::Const); self.recurse_into(kind, |this| intravisit::walk_anon_const(this, anon)); } fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - let kind = ConstKind::for_body(body, self.tcx.hir()); + let owner = self.tcx.hir().body_owner_def_id(body.id()); + let kind = self.tcx.hir().body_const_context(owner); self.recurse_into(kind, |this| intravisit::walk_body(this, body)); } diff --git a/src/librustc_passes/dead.rs b/src/librustc_passes/dead.rs index 4466dea89b0c1..1dcf0e7c7a98a 100644 --- a/src/librustc_passes/dead.rs +++ b/src/librustc_passes/dead.rs @@ -2,10 +2,6 @@ // closely. The idea is that all reachable symbols are live, codes called // from live codes are live, and everything else is dead. -use rustc::hir::map::Map; -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::middle::privacy; -use rustc::ty::{self, DefIdTree, TyCtxt}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -13,10 +9,14 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{Node, PatKind, TyKind}; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::privacy; +use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::lint; use rustc_ast::{ast, attr}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; // Any local node that may call something in its body block should be // explored. For example, if it's a live Node::Item that is a @@ -24,13 +24,15 @@ use rustc_span::symbol::sym; // may need to be marked as live. fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { match tcx.hir().find(hir_id) { - Some(Node::Item(..)) - | Some(Node::ImplItem(..)) - | Some(Node::ForeignItem(..)) - | Some(Node::TraitItem(..)) - | Some(Node::Variant(..)) - | Some(Node::AnonConst(..)) - | Some(Node::Pat(..)) => true, + Some( + Node::Item(..) + | Node::ImplItem(..) + | Node::ForeignItem(..) + | Node::TraitItem(..) + | Node::Variant(..) + | Node::AnonConst(..) + | Node::Pat(..), + ) => true, _ => false, } } @@ -50,7 +52,8 @@ struct MarkSymbolVisitor<'a, 'tcx> { impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn check_def_id(&mut self, def_id: DefId) { - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx.hir().as_local_hir_id(def_id); if should_explore(self.tcx, hir_id) || self.struct_constructors.contains_key(&hir_id) { self.worklist.push(hir_id); } @@ -59,7 +62,8 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } fn insert_def_id(&mut self, def_id: DefId) { - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx.hir().as_local_hir_id(def_id); debug_assert!(!should_explore(self.tcx, hir_id)); self.live_symbols.insert(hir_id); } @@ -67,9 +71,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn handle_res(&mut self, res: Res) { match res { - Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::TyAlias, _) => { + Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, _) => { self.check_def_id(res.def_id()); } _ if self.in_pat => {} @@ -227,7 +229,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { fn visit_variant_data( &mut self, def: &'tcx hir::VariantData<'tcx>, - _: ast::Name, + _: Symbol, _: &hir::Generics<'_>, _: hir::HirId, _: rustc_span::Span, @@ -255,7 +257,9 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { hir::ExprKind::Field(ref lhs, ..) => { self.handle_field_access(&lhs, expr.hir_id); } - hir::ExprKind::Struct(_, ref fields, _) => { + hir::ExprKind::Struct(ref qpath, ref fields, _) => { + let res = self.tables.qpath_res(qpath, expr.hir_id); + self.handle_res(res); if let ty::Adt(ref adt, _) = self.tables.expr_ty(expr).kind { self.mark_as_used_if_union(adt, fields); } @@ -300,12 +304,9 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - match ty.kind { - TyKind::Def(item_id, _) => { - let item = self.tcx.hir().expect_item(item_id.id); - intravisit::walk_item(self, item); - } - _ => (), + if let TyKind::Def(item_id, _) = ty.kind { + let item = self.tcx.hir().expect_item(item_id.id); + intravisit::walk_item(self, item); } intravisit::walk_ty(self, ty); } @@ -451,7 +452,7 @@ fn create_and_seed_worklist<'tcx>( ) .chain( // Seed entry point - tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().as_local_hir_id(def_id).unwrap()), + tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().as_local_hir_id(def_id)), ) .collect::>(); @@ -535,7 +536,8 @@ impl DeadVisitor<'tcx> { let inherent_impls = self.tcx.inherent_impls(def_id); for &impl_did in inherent_impls.iter() { for &item_did in &self.tcx.associated_item_def_ids(impl_did)[..] { - if let Some(item_hir_id) = self.tcx.hir().as_local_hir_id(item_did) { + if let Some(did) = item_did.as_local() { + let item_hir_id = self.tcx.hir().as_local_hir_id(did); if self.live_symbols.contains(&item_hir_id) { return true; } @@ -549,13 +551,14 @@ impl DeadVisitor<'tcx> { &mut self, id: hir::HirId, span: rustc_span::Span, - name: ast::Name, - node_type: &str, + name: Symbol, participle: &str, ) { if !name.as_str().starts_with('_') { self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| { - lint.build(&format!("{} is never {}: `{}`", node_type, participle, name)).emit() + let def_id = self.tcx.hir().local_def_id(id); + let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); + lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit() }); } } @@ -586,11 +589,11 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { // FIXME(66095): Because item.span is annotated with things // like expansion data, and ident.span isn't, we use the // def_span method if it's part of a macro invocation - // (and thus has asource_callee set). + // (and thus has a source_callee set). // We should probably annotate ident.span with the macro // context, but that's a larger change. if item.span.source_callee().is_some() { - self.tcx.sess.source_map().def_span(item.span) + self.tcx.sess.source_map().guess_head_span(item.span) } else { item.ident.span } @@ -601,7 +604,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { hir::ItemKind::Struct(..) => "constructed", // Issue #52325 _ => "used", }; - self.warn_dead_code(item.hir_id, span, item.ident.name, item.kind.descr(), participle); + self.warn_dead_code(item.hir_id, span, item.ident.name, participle); } else { // Only continue if we didn't warn intravisit::walk_item(self, item); @@ -615,13 +618,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { id: hir::HirId, ) { if self.should_warn_about_variant(&variant) { - self.warn_dead_code( - variant.id, - variant.span, - variant.ident.name, - "variant", - "constructed", - ); + self.warn_dead_code(variant.id, variant.span, variant.ident.name, "constructed"); } else { intravisit::walk_variant(self, variant, g, id); } @@ -629,20 +626,14 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem<'tcx>) { if self.should_warn_about_foreign_item(fi) { - self.warn_dead_code( - fi.hir_id, - fi.span, - fi.ident.name, - fi.kind.descriptive_variant(), - "used", - ); + self.warn_dead_code(fi.hir_id, fi.span, fi.ident.name, "used"); } intravisit::walk_foreign_item(self, fi); } fn visit_struct_field(&mut self, field: &'tcx hir::StructField<'tcx>) { if self.should_warn_about_field(&field) { - self.warn_dead_code(field.hir_id, field.span, field.ident.name, "field", "read"); + self.warn_dead_code(field.hir_id, field.span, field.ident.name, "read"); } intravisit::walk_struct_field(self, field); } @@ -655,7 +646,6 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { impl_item.hir_id, impl_item.span, impl_item.ident.name, - "associated const", "used", ); } @@ -663,14 +653,18 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { } hir::ImplItemKind::Fn(_, body_id) => { if !self.symbol_is_live(impl_item.hir_id) { - let span = self.tcx.sess.source_map().def_span(impl_item.span); - self.warn_dead_code( - impl_item.hir_id, - span, - impl_item.ident.name, - "method", - "used", - ); + // FIXME(66095): Because impl_item.span is annotated with things + // like expansion data, and ident.span isn't, we use the + // def_span method if it's part of a macro invocation + // (and thus has a source_callee set). + // We should probably annotate ident.span with the macro + // context, but that's a larger change. + let span = if impl_item.span.source_callee().is_some() { + self.tcx.sess.source_map().guess_head_span(impl_item.span) + } else { + impl_item.ident.span + }; + self.warn_dead_code(impl_item.hir_id, span, impl_item.ident.name, "used"); } self.visit_nested_body(body_id) } diff --git a/src/librustc_passes/diagnostic_items.rs b/src/librustc_passes/diagnostic_items.rs index ae23e9e35f93e..d91f49554ff48 100644 --- a/src/librustc_passes/diagnostic_items.rs +++ b/src/librustc_passes/diagnostic_items.rs @@ -9,13 +9,13 @@ //! //! * Compiler internal types like `Ty` and `TyCtxt` -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; struct DiagnosticItemCollector<'tcx> { @@ -47,7 +47,7 @@ impl<'tcx> DiagnosticItemCollector<'tcx> { if let Some(name) = extract(attrs) { let def_id = self.tcx.hir().local_def_id(hir_id); // insert into our table - collect_item(self.tcx, &mut self.items, name, def_id); + collect_item(self.tcx, &mut self.items, name, def_id.to_def_id()); } } } @@ -93,18 +93,18 @@ fn extract(attrs: &[ast::Attribute]) -> Option { } /// Traverse and collect the diagnostic items in the current -fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap { +fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap { // Initialize the collector. let mut collector = DiagnosticItemCollector::new(tcx); // Collect diagnostic items in this crate. tcx.hir().krate().visit_all_item_likes(&mut collector); - tcx.arena.alloc(collector.items) + collector.items } /// Traverse and collect all the diagnostic items in all crates. -fn collect_all<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap { +fn collect_all<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap { // Initialize the collector. let mut collector = FxHashMap::default(); @@ -115,7 +115,7 @@ fn collect_all<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap { } } - tcx.arena.alloc(collector) + collector } pub fn provide(providers: &mut Providers<'_>) { diff --git a/src/librustc_passes/entry.rs b/src/librustc_passes/entry.rs index 7e0d0bfe9aba4..d2f1d11256bf2 100644 --- a/src/librustc_passes/entry.rs +++ b/src/librustc_passes/entry.rs @@ -1,14 +1,14 @@ -use rustc::hir::map::Map; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::attr; use rustc_ast::entry::EntryPointType; use rustc_errors::struct_span_err; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirId, ImplItem, Item, ItemKind, TraitItem}; -use rustc_session::config::EntryFnType; -use rustc_session::{config, Session}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{CrateType, EntryFnType}; +use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; @@ -34,7 +34,7 @@ struct EntryContext<'a, 'tcx> { impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { fn visit_item(&mut self, item: &'tcx Item<'tcx>) { let def_id = self.map.local_def_id(item.hir_id); - let def_key = self.map.def_key(def_id.expect_local()); + let def_key = self.map.def_key(def_id); let at_root = def_key.parent == Some(CRATE_DEF_INDEX); find_item(item, self, at_root); } @@ -48,11 +48,10 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { } } -fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(DefId, EntryFnType)> { +fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType)> { assert_eq!(cnum, LOCAL_CRATE); - let any_exe = - tcx.sess.crate_types.borrow().iter().any(|ty| *ty == config::CrateType::Executable); + let any_exe = tcx.sess.crate_types.borrow().iter().any(|ty| *ty == CrateType::Executable); if !any_exe { // No need to find a main function. return None; @@ -143,7 +142,10 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { } } -fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) -> Option<(DefId, EntryFnType)> { +fn configure_main( + tcx: TyCtxt<'_>, + visitor: &EntryContext<'_, '_>, +) -> Option<(LocalDefId, EntryFnType)> { if let Some((hir_id, _)) = visitor.start_fn { Some((tcx.hir().local_def_id(hir_id), EntryFnType::Start)) } else if let Some((hir_id, _)) = visitor.attr_main_fn { @@ -211,7 +213,7 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) { err.emit(); } -pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(DefId, EntryFnType)> { +pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(LocalDefId, EntryFnType)> { tcx.entry_fn(LOCAL_CRATE) } diff --git a/src/librustc/hir/map/hir_id_validator.rs b/src/librustc_passes/hir_id_validator.rs similarity index 92% rename from src/librustc/hir/map/hir_id_validator.rs rename to src/librustc_passes/hir_id_validator.rs index e3386a2a910ed..80dfcd9c2417a 100644 --- a/src/librustc/hir/map/hir_id_validator.rs +++ b/src/librustc_passes/hir_id_validator.rs @@ -1,5 +1,3 @@ -use crate::hir::map::Map; -use crate::ty::TyCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator}; use rustc_hir as hir; @@ -7,6 +5,8 @@ use rustc_hir::def_id::{LocalDefId, CRATE_DEF_INDEX}; use rustc_hir::intravisit; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirId, ItemLocalId}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::TyCtxt; pub fn check_crate(tcx: TyCtxt<'_>) { tcx.dep_graph.assert_ignored(); @@ -17,7 +17,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { par_iter(&hir_map.krate().modules).for_each(|(module_id, _)| { let local_def_id = hir_map.local_def_id(*module_id); hir_map.visit_item_likes_in_module( - local_def_id, + local_def_id.to_def_id(), &mut OuterVisitor { hir_map, errors: &errors }, ); }); @@ -79,7 +79,7 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { fn check)>(&mut self, hir_id: HirId, walk: F) { assert!(self.owner.is_none()); - let owner = self.hir_map.local_def_id(hir_id).expect_local(); + let owner = self.hir_map.local_def_id(hir_id); self.owner = Some(owner); walk(self); @@ -143,16 +143,6 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { fn visit_id(&mut self, hir_id: HirId) { let owner = self.owner.expect("no owner"); - if hir_id == hir::DUMMY_HIR_ID { - self.error(|| { - format!( - "HirIdValidator: HirId {:?} is invalid", - self.hir_map.node_to_string(hir_id) - ) - }); - return; - } - if owner != hir_id.owner { self.error(|| { format!( diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs index 8bfe58da78f6e..139ffb9699ad2 100644 --- a/src/librustc_passes/hir_stats.rs +++ b/src/librustc_passes/hir_stats.rs @@ -2,14 +2,14 @@ // pieces of AST and HIR. The resulting numbers are good approximations but not // completely accurate (some things might be counted twice, others missed). -use rustc::hir::map::Map; -use rustc::util::common::to_readable_str; use rustc_ast::ast::{self, AttrId, NodeId}; use rustc_ast::visit as ast_visit; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::intravisit as hir_visit; use rustc_hir::HirId; +use rustc_middle::hir::map::Map; +use rustc_middle::util::common::to_readable_str; use rustc_span::Span; #[derive(Copy, Clone, PartialEq, Eq, Hash)] diff --git a/src/librustc_passes/intrinsicck.rs b/src/librustc_passes/intrinsicck.rs index 1b46aaeefcb18..93344e907c344 100644 --- a/src/librustc_passes/intrinsicck.rs +++ b/src/librustc_passes/intrinsicck.rs @@ -1,13 +1,17 @@ -use rustc::ty::layout::{LayoutError, Pointer, SizeSkeleton, VariantIdx}; -use rustc::ty::query::Providers; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_ast::ast::{FloatTy, InlineAsmTemplatePiece, IntTy, UintTy}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_index::vec::Idx; -use rustc_span::{sym, Span}; +use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::lint; +use rustc_span::{sym, Span, Symbol, DUMMY_SP}; +use rustc_target::abi::{Pointer, VariantIdx}; +use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmType}; use rustc_target::spec::abi::Abi::RustIntrinsic; fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: DefId) { @@ -118,6 +122,271 @@ impl ExprVisitor<'tcx> { } err.emit() } + + fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { + if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) { + return true; + } + if let ty::Foreign(..) = ty.kind { + return true; + } + false + } + + fn check_asm_operand_type( + &self, + idx: usize, + reg: InlineAsmRegOrRegClass, + expr: &hir::Expr<'tcx>, + template: &[InlineAsmTemplatePiece], + tied_input: Option<(&hir::Expr<'tcx>, Option)>, + ) -> Option { + // Check the type against the allowed types for inline asm. + let ty = self.tables.expr_ty_adjusted(expr); + let asm_ty_isize = match self.tcx.sess.target.ptr_width { + 16 => InlineAsmType::I16, + 32 => InlineAsmType::I32, + 64 => InlineAsmType::I64, + _ => unreachable!(), + }; + let asm_ty = match ty.kind { + ty::Never | ty::Error => return None, + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), + ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), + ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), + ty::FnPtr(_) => Some(asm_ty_isize), + ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => { + Some(asm_ty_isize) + } + ty::Adt(adt, substs) if adt.repr.simd() => { + let fields = &adt.non_enum_variant().fields; + let elem_ty = fields[0].ty(self.tcx, substs); + match elem_ty.kind { + ty::Never | ty::Error => return None, + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => { + Some(InlineAsmType::VecI8(fields.len() as u64)) + } + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { + Some(InlineAsmType::VecI16(fields.len() as u64)) + } + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { + Some(InlineAsmType::VecI32(fields.len() as u64)) + } + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { + Some(InlineAsmType::VecI64(fields.len() as u64)) + } + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { + Some(InlineAsmType::VecI128(fields.len() as u64)) + } + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { + Some(match self.tcx.sess.target.ptr_width { + 16 => InlineAsmType::VecI16(fields.len() as u64), + 32 => InlineAsmType::VecI32(fields.len() as u64), + 64 => InlineAsmType::VecI64(fields.len() as u64), + _ => unreachable!(), + }) + } + ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)), + ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)), + _ => None, + } + } + _ => None, + }; + let asm_ty = match asm_ty { + Some(asm_ty) => asm_ty, + None => { + let msg = &format!("cannot use value of type `{}` for inline assembly", ty); + let mut err = self.tcx.sess.struct_span_err(expr.span, msg); + err.note( + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ); + err.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 !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) { + let msg = "arguments for inline assembly must be copyable"; + let mut err = self.tcx.sess.struct_span_err(expr.span, msg); + err.note(&format!("`{}` does not implement the Copy trait", ty)); + err.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 = &format!("incompatible types for asm inout argument"); + let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg); + err.span_label( + in_expr.span, + &format!("type `{}`", self.tables.expr_ty_adjusted(in_expr)), + ); + err.span_label(expr.span, &format!("type `{}`", ty)); + err.note( + "asm inout arguments must have the same type, \ + unless they are both pointers or integers of the same size", + ); + err.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 reg_class = reg.reg_class(); + let supported_tys = reg_class.supported_types(asm_arch); + let feature = match supported_tys.iter().find(|&&(t, _)| t == asm_ty) { + Some((_, feature)) => feature, + None => { + let msg = &format!("type `{}` cannot be used with this register class", ty); + let mut err = self.tcx.sess.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(", "), + )); + 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 in AST + // lowering. While AST lowering 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 in AST lowering to + // verify that the register class is usable at all. + if let Some(feature) = feature { + if !self.tcx.sess.target_features.contains(&Symbol::intern(feature)) { + let msg = &format!("`{}` target feature is not enabled", feature); + let mut err = self.tcx.sess.struct_span_err(expr.span, msg); + err.note(&format!( + "this is required to use type `{}` with register class `{}`", + ty, + reg_class.name(), + )); + err.emit(); + return Some(asm_ty); + } + } + + // Check whether a modifier is suggested for using this type. + if let Some((suggested_modifier, suggested_result)) = + 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 (default_modifier, default_result) = + reg_class.default_modifier(asm_arch).unwrap(); + self.tcx.struct_span_lint_hir( + lint::builtin::ASM_SUB_REGISTER, + expr.hir_id, + spans, + |lint| { + let msg = "formatting may not be suitable for sub-register argument"; + let mut err = lint.build(msg); + err.span_label(expr.span, "for this argument"); + err.help(&format!( + "use the `{}` modifier to have the register formatted as `{}`", + suggested_modifier, suggested_result, + )); + err.help(&format!( + "or use the `{}` modifier to keep the default formatting of `{}`", + default_modifier, default_result, + )); + err.emit(); + }, + ); + } + } + + Some(asm_ty) + } + + fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { + for (idx, op) in asm.operands.iter().enumerate() { + match *op { + hir::InlineAsmOperand::In { reg, ref expr } => { + self.check_asm_operand_type(idx, reg, expr, asm.template, None); + } + hir::InlineAsmOperand::Out { reg, late: _, ref expr } => { + if let Some(expr) = expr { + self.check_asm_operand_type(idx, reg, expr, asm.template, None); + } + } + hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => { + self.check_asm_operand_type(idx, reg, expr, asm.template, None); + } + hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => { + let in_ty = self.check_asm_operand_type(idx, reg, in_expr, asm.template, None); + if let Some(out_expr) = out_expr { + self.check_asm_operand_type( + idx, + reg, + out_expr, + asm.template, + Some((in_expr, in_ty)), + ); + } + } + hir::InlineAsmOperand::Const { ref expr } => { + let ty = self.tables.expr_ty_adjusted(expr); + match ty.kind { + ty::Int(_) | ty::Uint(_) | ty::Float(_) => {} + _ => { + let msg = + "asm `const` arguments must be integer or floating-point values"; + self.tcx.sess.span_err(expr.span, msg); + } + } + } + hir::InlineAsmOperand::Sym { .. } => {} + } + } + } } impl Visitor<'tcx> for ItemVisitor<'tcx> { @@ -130,7 +399,7 @@ impl Visitor<'tcx> for ItemVisitor<'tcx> { fn visit_nested_body(&mut self, body_id: hir::BodyId) { let owner_def_id = self.tcx.hir().body_owner_def_id(body_id); let body = self.tcx.hir().body(body_id); - let param_env = self.tcx.param_env(owner_def_id); + let param_env = self.tcx.param_env(owner_def_id.to_def_id()); let tables = self.tcx.typeck_tables_of(owner_def_id); ExprVisitor { tcx: self.tcx, param_env, tables }.visit_body(body); self.visit_body(body); @@ -145,19 +414,23 @@ impl Visitor<'tcx> for ExprVisitor<'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - let res = if let hir::ExprKind::Path(ref qpath) = expr.kind { - self.tables.qpath_res(qpath, expr.hir_id) - } else { - Res::Err - }; - if let Res::Def(DefKind::Fn, did) = res { - if self.def_id_is_transmute(did) { - let typ = self.tables.node_type(expr.hir_id); - let sig = typ.fn_sig(self.tcx); - let from = sig.inputs().skip_binder()[0]; - let to = *sig.output().skip_binder(); - self.check_transmute(expr.span, from, to); + match expr.kind { + hir::ExprKind::Path(ref qpath) => { + let res = self.tables.qpath_res(qpath, expr.hir_id); + if let Res::Def(DefKind::Fn, did) = res { + if self.def_id_is_transmute(did) { + let typ = self.tables.node_type(expr.hir_id); + let sig = typ.fn_sig(self.tcx); + let from = sig.inputs().skip_binder()[0]; + let to = *sig.output().skip_binder(); + self.check_transmute(expr.span, from, to); + } + } } + + hir::ExprKind::InlineAsm(asm) => self.check_asm(asm), + + _ => {} } intravisit::walk_expr(self, expr); diff --git a/src/librustc_passes/lang_items.rs b/src/librustc_passes/lang_items.rs index 88e92bbdba1aa..779fb8039d157 100644 --- a/src/librustc_passes/lang_items.rs +++ b/src/librustc_passes/lang_items.rs @@ -9,8 +9,8 @@ use crate::weak_lang_items; -use rustc::middle::cstore::ExternCrate; -use rustc::ty::TyCtxt; +use rustc_middle::middle::cstore::ExternCrate; +use rustc_middle::ty::TyCtxt; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -19,7 +19,7 @@ use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items::{extract, ITEM_REFS}; use rustc_hir::{LangItem, LanguageItems, Target}; -use rustc::ty::query::Providers; +use rustc_middle::ty::query::Providers; struct LanguageItemCollector<'tcx> { items: LanguageItems, @@ -34,7 +34,7 @@ impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { // Known lang item with attribute on correct target. Some((item_index, expected_target)) if actual_target == expected_target => { let def_id = self.tcx.hir().local_def_id(item.hir_id); - self.collect_item(item_index, def_id); + self.collect_item(item_index, def_id.to_def_id()); } // Known lang item with attribute on incorrect target. Some((_, expected_target)) => { @@ -169,6 +169,6 @@ fn collect(tcx: TyCtxt<'_>) -> LanguageItems { pub fn provide(providers: &mut Providers<'_>) { providers.get_lang_items = |tcx, id| { assert_eq!(id, LOCAL_CRATE); - tcx.arena.alloc(collect(tcx)) + collect(tcx) }; } diff --git a/src/librustc_passes/layout_test.rs b/src/librustc_passes/layout_test.rs index 9d8b1422bdf24..c0826f8cc605f 100644 --- a/src/librustc_passes/layout_test.rs +++ b/src/librustc_passes/layout_test.rs @@ -1,18 +1,12 @@ -use rustc::ty::layout::HasDataLayout; -use rustc::ty::layout::HasParamEnv; -use rustc::ty::layout::HasTyCtxt; -use rustc::ty::layout::LayoutOf; -use rustc::ty::layout::TargetDataLayout; -use rustc::ty::layout::TyLayout; -use rustc::ty::ParamEnv; -use rustc::ty::Ty; -use rustc::ty::TyCtxt; use rustc_ast::ast::Attribute; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::ItemKind; +use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_span::symbol::sym; +use rustc_target::abi::{HasDataLayout, LayoutOf, TargetDataLayout}; pub fn test_layout(tcx: TyCtxt<'_>) { if tcx.features().rustc_attrs { @@ -33,8 +27,9 @@ impl ItemLikeVisitor<'tcx> for LayoutTest<'tcx> { ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) - | ItemKind::Union(..) => { - for attr in self.tcx.get_attrs(item_def_id).iter() { + | ItemKind::Union(..) + | ItemKind::OpaqueTy(..) => { + for attr in self.tcx.get_attrs(item_def_id.to_def_id()).iter() { if attr.check_name(sym::rustc_layout) { self.dump_layout_of(item_def_id, item, attr); } @@ -49,7 +44,7 @@ impl ItemLikeVisitor<'tcx> for LayoutTest<'tcx> { } impl LayoutTest<'tcx> { - fn dump_layout_of(&self, item_def_id: DefId, item: &hir::Item<'tcx>, attr: &Attribute) { + fn dump_layout_of(&self, item_def_id: LocalDefId, item: &hir::Item<'tcx>, attr: &Attribute) { let tcx = self.tcx; let param_env = self.tcx.param_env(item_def_id); let ty = self.tcx.type_of(item_def_id); @@ -90,7 +85,7 @@ impl LayoutTest<'tcx> { sym::debug => { self.tcx.sess.span_err( item.span, - &format!("layout debugging: {:#?}", *ty_layout), + &format!("layout_of({:?}) = {:#?}", ty, *ty_layout), ); } @@ -118,9 +113,9 @@ struct UnwrapLayoutCx<'tcx> { impl LayoutOf for UnwrapLayoutCx<'tcx> { type Ty = Ty<'tcx>; - type TyLayout = TyLayout<'tcx>; + type TyAndLayout = TyAndLayout<'tcx>; - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { self.tcx.layout_of(self.param_env.and(ty)).unwrap() } } diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index afafbacb8fa88..b55c5ec47eef1 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -7,20 +7,22 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] -extern crate rustc; +extern crate rustc_middle; #[macro_use] extern crate log; -use rustc::ty::query::Providers; +use rustc_middle::ty::query::Providers; mod check_attr; mod check_const; pub mod dead; mod diagnostic_items; pub mod entry; +pub mod hir_id_validator; pub mod hir_stats; mod intrinsicck; mod lang_items; diff --git a/src/librustc_passes/lib_features.rs b/src/librustc_passes/lib_features.rs index 133e30f6ff01a..31c7ba2a4b205 100644 --- a/src/librustc_passes/lib_features.rs +++ b/src/librustc_passes/lib_features.rs @@ -4,14 +4,14 @@ // and `#[unstable (..)]`), but are not declared in one single location // (unlike lang features), which means we need to collect them instead. -use rustc::hir::map::Map; -use rustc::middle::lib_features::LibFeatures; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::ast::{Attribute, MetaItem, MetaItemKind}; use rustc_errors::struct_span_err; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::lib_features::LibFeatures; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; @@ -138,6 +138,6 @@ fn collect(tcx: TyCtxt<'_>) -> LibFeatures { pub fn provide(providers: &mut Providers<'_>) { providers.get_lib_features = |tcx, id| { assert_eq!(id, LOCAL_CRATE); - tcx.arena.alloc(collect(tcx)) + collect(tcx) }; } diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs index 97f6457d39736..21512c566e1c5 100644 --- a/src/librustc_passes/liveness.rs +++ b/src/librustc_passes/liveness.rs @@ -96,26 +96,26 @@ use self::LiveNodeKind::*; use self::VarKind::*; -use rustc::hir::map::Map; -use rustc::ty::query::Providers; -use rustc::ty::{self, TyCtxt}; -use rustc_ast::ast; +use rustc_ast::ast::InlineAsmOptions; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::*; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet, Node}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use std::collections::VecDeque; +use std::fmt; use std::io; use std::io::prelude::*; use std::rc::Rc; -use std::{fmt, u32}; #[derive(Copy, Clone, PartialEq)] struct Variable(u32); @@ -245,13 +245,13 @@ struct CaptureInfo { #[derive(Copy, Clone, Debug)] struct LocalInfo { id: HirId, - name: ast::Name, + name: Symbol, is_shorthand: bool, } #[derive(Copy, Clone, Debug)] enum VarKind { - Param(HirId, ast::Name), + Param(HirId, Symbol), Local(LocalInfo), CleanExit, } @@ -361,7 +361,7 @@ fn visit_fn<'tcx>( // swap in a new set of IR maps for this function body: let def_id = ir.tcx.hir().local_def_id(id); - let mut fn_maps = IrMaps::new(ir.tcx, def_id); + let mut fn_maps = IrMaps::new(ir.tcx, def_id.to_def_id()); // Don't run unused pass for #[derive()] if let FnKind::Method(..) = fk { @@ -496,7 +496,7 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) { } ir.set_captures(expr.hir_id, call_caps); let old_body_owner = ir.body_owner; - ir.body_owner = closure_def_id; + ir.body_owner = closure_def_id.to_def_id(); intravisit::walk_expr(ir, expr); ir.body_owner = old_body_owner; } @@ -533,6 +533,7 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::LlvmInlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Yield(..) | hir::ExprKind::Type(..) @@ -671,7 +672,7 @@ struct Liveness<'a, 'tcx> { } impl<'a, 'tcx> Liveness<'a, 'tcx> { - fn new(ir: &'a mut IrMaps<'tcx>, def_id: DefId) -> Liveness<'a, 'tcx> { + fn new(ir: &'a mut IrMaps<'tcx>, def_id: LocalDefId) -> Liveness<'a, 'tcx> { // Special nodes and variables: // - exit_ln represents the end of the fn, either by return or panic // - implicit_ret_var is a pseudo-variable that represents @@ -903,10 +904,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } fn compute(&mut self, body: &hir::Expr<'_>) -> LiveNode { - debug!( - "compute: using id for body, {}", - self.ir.tcx.hir().hir_to_pretty_string(body.hir_id) - ); + debug!("compute: using id for body, {:?}", body); // the fallthrough exit is only for those cases where we do not // explicitly return: @@ -935,7 +933,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { if blk.targeted_by_break { self.break_ln.insert(blk.hir_id, succ); } - let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ); + let succ = self.propagate_through_opt_expr(blk.expr.as_deref(), succ); blk.stmts.iter().rev().fold(succ, |succ, stmt| self.propagate_through_stmt(stmt, succ)) } @@ -956,7 +954,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // initialization, which is mildly more complex than checking // once at the func header but otherwise equivalent. - let succ = self.propagate_through_opt_expr(local.init.as_ref().map(|e| &**e), succ); + let succ = self.propagate_through_opt_expr(local.init.as_deref(), succ); self.define_bindings_in_pat(&local.pat, succ) } hir::StmtKind::Item(..) => succ, @@ -979,7 +977,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } fn propagate_through_expr(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode { - debug!("propagate_through_expr: {}", self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id)); + debug!("propagate_through_expr: {:?}", expr); match expr.kind { // Interesting cases with control flow or which gen/kill @@ -990,10 +988,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(&e, succ), hir::ExprKind::Closure(..) => { - debug!( - "{} is an ExprKind::Closure", - self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id) - ); + debug!("{:?} is an ExprKind::Closure", expr); // the construction of a closure itself is not important, // but we have to consider the closed over variables. @@ -1184,6 +1179,64 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { | hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(&e, succ), hir::ExprKind::InlineAsm(ref asm) => { + // Handle non-returning asm + let mut succ = if asm.options.contains(InlineAsmOptions::NORETURN) { + self.s.exit_ln + } else { + succ + }; + + // Do a first pass for writing outputs only + for op in asm.operands.iter().rev() { + match op { + hir::InlineAsmOperand::In { .. } + | hir::InlineAsmOperand::Const { .. } + | hir::InlineAsmOperand::Sym { .. } => {} + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + succ = self.write_place(expr, succ, ACC_WRITE); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE); + } + hir::InlineAsmOperand::SplitInOut { out_expr, .. } => { + if let Some(expr) = out_expr { + succ = self.write_place(expr, succ, ACC_WRITE); + } + } + } + } + + // Then do a second pass for inputs + let mut succ = succ; + for op in asm.operands.iter().rev() { + match op { + hir::InlineAsmOperand::In { expr, .. } + | hir::InlineAsmOperand::Const { expr, .. } + | hir::InlineAsmOperand::Sym { expr, .. } => { + succ = self.propagate_through_expr(expr, succ) + } + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + succ = self.propagate_through_place_components(expr, succ); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + succ = self.propagate_through_place_components(expr, succ); + } + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + if let Some(expr) = out_expr { + succ = self.propagate_through_place_components(expr, succ); + } + succ = self.propagate_through_expr(in_expr, succ); + } + } + } + succ + } + + hir::ExprKind::LlvmInlineAsm(ref asm) => { let ia = &asm.inner; let outputs = asm.outputs_exprs; let inputs = asm.inputs_exprs; @@ -1344,11 +1397,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let mut first_merge = true; let ln = self.live_node(expr.hir_id, expr.span); self.init_empty(ln, succ); - debug!( - "propagate_through_loop: using id for loop body {} {}", - expr.hir_id, - self.ir.tcx.hir().hir_to_pretty_string(body.hir_id) - ); + debug!("propagate_through_loop: using id for loop body {} {:?}", expr.hir_id, body); self.break_ln.insert(expr.hir_id, succ); @@ -1409,6 +1458,33 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { } hir::ExprKind::InlineAsm(ref asm) => { + for op in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } + | hir::InlineAsmOperand::Const { expr, .. } + | hir::InlineAsmOperand::Sym { expr, .. } => this.visit_expr(expr), + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + this.check_place(expr); + this.visit_expr(expr); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + this.check_place(expr); + this.visit_expr(expr); + } + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + this.visit_expr(in_expr); + if let Some(out_expr) = out_expr { + this.check_place(out_expr); + this.visit_expr(out_expr); + } + } + } + } + } + + hir::ExprKind::LlvmInlineAsm(ref asm) => { for input in asm.inputs_exprs { this.visit_expr(input); } @@ -1502,28 +1578,33 @@ impl<'tcx> Liveness<'_, 'tcx> { ) { // In an or-pattern, only consider the variable; any later patterns must have the same // bindings, and we also consider the first pattern to be the "authoritative" set of ids. - // However, we should take the spans of variables with the same name from the later + // However, we should take the ids and spans of variables with the same name from the later // patterns so the suggestions to prefix with underscores will apply to those too. - let mut vars: FxIndexMap)> = <_>::default(); + let mut vars: FxIndexMap)> = <_>::default(); pat.each_binding(|_, hir_id, pat_sp, ident| { let ln = entry_ln.unwrap_or_else(|| self.live_node(hir_id, pat_sp)); let var = self.variable(hir_id, ident.span); + let id_and_sp = (hir_id, pat_sp); vars.entry(self.ir.variable_name(var)) - .and_modify(|(.., spans)| spans.push(ident.span)) - .or_insert_with(|| (ln, var, hir_id, vec![ident.span])); + .and_modify(|(.., hir_ids_and_spans)| hir_ids_and_spans.push(id_and_sp)) + .or_insert_with(|| (ln, var, vec![id_and_sp])); }); - for (_, (ln, var, id, spans)) in vars { + for (_, (ln, var, hir_ids_and_spans)) in vars { if self.used_on_entry(ln, var) { + let id = hir_ids_and_spans[0].0; + let spans = hir_ids_and_spans.into_iter().map(|(_, sp)| sp).collect(); on_used_on_entry(spans, id, ln, var); } else { - self.report_unused(spans, id, ln, var); + self.report_unused(hir_ids_and_spans, ln, var); } } } - fn report_unused(&self, spans: Vec, hir_id: HirId, ln: LiveNode, var: Variable) { + fn report_unused(&self, hir_ids_and_spans: Vec<(HirId, Span)>, ln: LiveNode, var: Variable) { + let first_hir_id = hir_ids_and_spans[0].0; + if let Some(name) = self.should_warn(var).filter(|name| name != "self") { // annoying: for parameters in funcs like `fn(x: i32) // {ret}`, there is only one node, so asking about @@ -1534,8 +1615,8 @@ impl<'tcx> Liveness<'_, 'tcx> { if is_assigned { self.ir.tcx.struct_span_lint_hir( lint::builtin::UNUSED_VARIABLES, - hir_id, - spans, + first_hir_id, + hir_ids_and_spans.into_iter().map(|(_, sp)| sp).collect::>(), |lint| { lint.build(&format!("variable `{}` is assigned to, but never used", name)) .note(&format!("consider using `_{}` instead", name)) @@ -1545,31 +1626,49 @@ impl<'tcx> Liveness<'_, 'tcx> { } else { self.ir.tcx.struct_span_lint_hir( lint::builtin::UNUSED_VARIABLES, - hir_id, - spans.clone(), + first_hir_id, + hir_ids_and_spans.iter().map(|(_, sp)| *sp).collect::>(), |lint| { let mut err = lint.build(&format!("unused variable: `{}`", name)); - if self.ir.variable_is_shorthand(var) { - if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) { - // Handle `ref` and `ref mut`. - let spans = spans - .iter() - .map(|_span| (pat.span, format!("{}: _", name))) - .collect(); - - err.multipart_suggestion( - "try ignoring the field", - spans, - Applicability::MachineApplicable, - ); - } + + let (shorthands, non_shorthands): (Vec<_>, Vec<_>) = + hir_ids_and_spans.into_iter().partition(|(hir_id, span)| { + let var = self.variable(*hir_id, *span); + self.ir.variable_is_shorthand(var) + }); + + let mut shorthands = shorthands + .into_iter() + .map(|(_, span)| (span, format!("{}: _", name))) + .collect::>(); + + // If we have both shorthand and non-shorthand, prefer the "try ignoring + // the field" message, and suggest `_` for the non-shorthands. If we only + // have non-shorthand, then prefix with an underscore instead. + if !shorthands.is_empty() { + shorthands.extend( + non_shorthands + .into_iter() + .map(|(_, span)| (span, "_".to_string())) + .collect::>(), + ); + + err.multipart_suggestion( + "try ignoring the field", + shorthands, + Applicability::MachineApplicable, + ); } else { err.multipart_suggestion( "if this is intentional, prefix it with an underscore", - spans.iter().map(|span| (*span, format!("_{}", name))).collect(), + non_shorthands + .into_iter() + .map(|(_, span)| (span, format!("_{}", name))) + .collect::>(), Applicability::MachineApplicable, ); } + err.emit() }, ); diff --git a/src/librustc_passes/loops.rs b/src/librustc_passes/loops.rs index fa2afae469c04..09b3d44020d81 100644 --- a/src/librustc_passes/loops.rs +++ b/src/librustc_passes/loops.rs @@ -1,13 +1,13 @@ use Context::*; -use rustc::hir::map::Map; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Destination, Movability, Node}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::Span; @@ -68,7 +68,9 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { self.with_context(LabeledBlock, |v| v.visit_block(&b)); } hir::ExprKind::Break(label, ref opt_expr) => { - opt_expr.as_ref().map(|e| self.visit_expr(e)); + if let Some(e) = opt_expr { + self.visit_expr(e); + } if self.require_label_in_labeled_block(e.span, &label, "break") { // If we emitted an error about an unlabeled break in a labeled @@ -77,31 +79,31 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { } let loop_id = match label.target_id { - Ok(loop_id) => loop_id, - Err(hir::LoopIdError::OutsideLoopScope) => hir::DUMMY_HIR_ID, + Ok(loop_id) => Some(loop_id), + Err(hir::LoopIdError::OutsideLoopScope) => None, Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { self.emit_unlabled_cf_in_while_condition(e.span, "break"); - hir::DUMMY_HIR_ID + None } - Err(hir::LoopIdError::UnresolvedLabel) => hir::DUMMY_HIR_ID, + Err(hir::LoopIdError::UnresolvedLabel) => None, }; - if loop_id != hir::DUMMY_HIR_ID { + if let Some(loop_id) = loop_id { if let Node::Block(_) = self.hir_map.find(loop_id).unwrap() { return; } } if opt_expr.is_some() { - let loop_kind = if loop_id == hir::DUMMY_HIR_ID { - None - } else { + let loop_kind = if let Some(loop_id) = loop_id { Some(match self.hir_map.expect_expr(loop_id).kind { hir::ExprKind::Loop(_, _, source) => source, ref r => { span_bug!(e.span, "break label resolved to a non-loop: {:?}", r) } }) + } else { + None }; match loop_kind { None | Some(hir::LoopSource::Loop) => (), diff --git a/src/librustc_passes/reachable.rs b/src/librustc_passes/reachable.rs index 1e9c6c91d385f..7c169d6813282 100644 --- a/src/librustc_passes/reachable.rs +++ b/src/librustc_passes/reachable.rs @@ -5,26 +5,25 @@ // makes all other generics or inline functions that it references // reachable as well. -use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc::middle::privacy; -use rustc::ty::query::Providers; -use rustc::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirIdSet, Node}; -use rustc_session::config; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::middle::privacy; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::config::CrateType; use rustc_target::spec::abi::Abi; // Returns true if the given item must be inlined because it may be // monomorphized or it was marked with `#[inline]`. This will only return // true for functions. -fn item_might_be_inlined(tcx: TyCtxt<'tcx>, item: &hir::Item<'_>, attrs: CodegenFnAttrs) -> bool { +fn item_might_be_inlined(tcx: TyCtxt<'tcx>, item: &hir::Item<'_>, attrs: &CodegenFnAttrs) -> bool { if attrs.requests_inline() { return true; } @@ -42,7 +41,7 @@ fn item_might_be_inlined(tcx: TyCtxt<'tcx>, item: &hir::Item<'_>, attrs: Codegen fn method_might_be_inlined( tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>, - impl_src: DefId, + impl_src: LocalDefId, ) -> bool { let codegen_fn_attrs = tcx.codegen_fn_attrs(impl_item.hir_id.owner.to_def_id()); let generics = tcx.generics_of(tcx.hir().local_def_id(impl_item.hir_id)); @@ -54,13 +53,9 @@ fn method_might_be_inlined( return true; } } - if let Some(impl_hir_id) = tcx.hir().as_local_hir_id(impl_src) { - match tcx.hir().find(impl_hir_id) { - Some(Node::Item(item)) => item_might_be_inlined(tcx, &item, codegen_fn_attrs), - Some(..) | None => span_bug!(impl_item.span, "impl did is not an item"), - } - } else { - span_bug!(impl_item.span, "found a foreign impl as a parent of a local method") + match tcx.hir().find(tcx.hir().as_local_hir_id(impl_src)) { + Some(Node::Item(item)) => item_might_be_inlined(tcx, &item, codegen_fn_attrs), + Some(..) | None => span_bug!(impl_item.span, "impl did is not an item"), } } @@ -109,16 +104,16 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { } Some(res) => { if let Some((hir_id, def_id)) = res.opt_def_id().and_then(|def_id| { - self.tcx.hir().as_local_hir_id(def_id).map(|hir_id| (hir_id, def_id)) + def_id.as_local().map(|def_id| (self.tcx.hir().as_local_hir_id(def_id), def_id)) }) { - if self.def_id_represents_local_inlined_item(def_id) { + if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { self.worklist.push(hir_id); } else { match res { // If this path leads to a constant, then we need to // recurse into the constant to continue finding // items that are reachable. - Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { + Res::Def(DefKind::Const | DefKind::AssocConst, _) => { self.worklist.push(hir_id); } @@ -142,8 +137,8 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { // Returns true if the given def ID represents a local item that is // eligible for inlining and false otherwise. fn def_id_represents_local_inlined_item(&self, def_id: DefId) -> bool { - let hir_id = match self.tcx.hir().as_local_hir_id(def_id) { - Some(hir_id) => hir_id, + let hir_id = match def_id.as_local() { + Some(def_id) => self.tcx.hir().as_local_hir_id(def_id), None => { return false; } @@ -175,7 +170,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { // Check the impl. If the generics on the self // type of the impl require inlining, this method // does too. - let impl_hir_id = self.tcx.hir().as_local_hir_id(impl_did).unwrap(); + let impl_hir_id = self.tcx.hir().as_local_hir_id(impl_did); match self.tcx.hir().expect_item(impl_hir_id).kind { hir::ItemKind::Impl { .. } => { let generics = self.tcx.generics_of(impl_did); @@ -362,7 +357,7 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx let tcx = self.tcx; self.worklist.extend( tcx.provided_trait_methods(trait_def_id) - .map(|assoc| tcx.hir().as_local_hir_id(assoc.def_id).unwrap()), + .map(|assoc| tcx.hir().as_local_hir_id(assoc.def_id.expect_local())), ); } } @@ -375,16 +370,15 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx } } -fn reachable_set(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Lrc { +fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, crate_num: CrateNum) -> &'tcx HirIdSet { debug_assert!(crate_num == LOCAL_CRATE); let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); - let any_library = tcx.sess.crate_types.borrow().iter().any(|ty| { - *ty == config::CrateType::Rlib - || *ty == config::CrateType::Dylib - || *ty == config::CrateType::ProcMacro - }); + let any_library = + tcx.sess.crate_types.borrow().iter().any(|ty| { + *ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro + }); let mut reachable_context = ReachableContext { tcx, tables: &ty::TypeckTables::empty(None), @@ -401,7 +395,7 @@ fn reachable_set(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Lrc { reachable_context.worklist.extend(access_levels.map.iter().map(|(id, _)| *id)); for item in tcx.lang_items().items().iter() { if let Some(did) = *item { - if let Some(hir_id) = tcx.hir().as_local_hir_id(did) { + if let Some(hir_id) = did.as_local().map(|did| tcx.hir().as_local_hir_id(did)) { reachable_context.worklist.push(hir_id); } } @@ -421,7 +415,7 @@ fn reachable_set(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Lrc { debug!("Inline reachability shows: {:?}", reachable_context.reachable_symbols); // Return the set of reachable symbols. - Lrc::new(reachable_context.reachable_symbols) + tcx.arena.alloc(reachable_context.reachable_symbols) } pub fn provide(providers: &mut Providers<'_>) { diff --git a/src/librustc_passes/region.rs b/src/librustc_passes/region.rs index e771696a5b6bf..a6fa677cbc0af 100644 --- a/src/librustc_passes/region.rs +++ b/src/librustc_passes/region.rs @@ -6,9 +6,6 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html -use rustc::middle::region::*; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::walk_list; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -16,6 +13,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Arm, Block, Expr, Local, Node, Pat, PatKind, Stmt}; use rustc_index::vec::Idx; +use rustc_middle::middle::region::*; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_span::source_map; use rustc_span::Span; @@ -27,8 +27,8 @@ pub struct Context { /// of the innermost fn body. Each fn forms its own disjoint tree /// in the region hierarchy. These fn bodies are themselves /// arranged into a tree. See the "Modeling closures" section of - /// the README in `infer::region_constraints` for more - /// details. + /// the README in `rustc_trait_selection::infer::region_constraints` + /// for more details. root_id: Option, /// The scope that contains any new variables declared, plus its depth in @@ -565,8 +565,10 @@ fn resolve_local<'tcx>( PatKind::Box(ref subpat) => is_binding_pat(&subpat), PatKind::Ref(_, _) - | PatKind::Binding(hir::BindingAnnotation::Unannotated, ..) - | PatKind::Binding(hir::BindingAnnotation::Mutable, ..) + | PatKind::Binding( + hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, + .., + ) | PatKind::Wild | PatKind::Path(_) | PatKind::Lit(_) @@ -717,9 +719,17 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { self.cx.parent ); + // Save all state that is specific to the outer function + // body. These will be restored once down below, once we've + // 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(body.value.hir_id.local_id); if let Some(root_id) = self.cx.root_id { @@ -771,6 +781,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { self.expr_and_pat_count = outer_ec; self.cx = outer_cx; self.terminating_scopes = outer_ts; + self.pessimistic_yield = outer_pessimistic_yield; } fn visit_arm(&mut self, a: &'tcx Arm<'tcx>) { @@ -786,7 +797,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init.as_ref().map(|e| &**e)); + resolve_local(self, Some(&l.pat), l.init.as_deref()); } } @@ -796,7 +807,7 @@ fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { return tcx.region_scope_tree(closure_base_def_id); } - let id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let id = tcx.hir().as_local_hir_id(def_id.expect_local()); let scope_tree = if let Some(body_id) = tcx.hir().maybe_body_owned_by(id) { let mut visitor = RegionResolutionVisitor { tcx, diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs index 8fa5a4fbc61f4..054748c09fc44 100644 --- a/src/librustc_passes/stability.rs +++ b/src/librustc_passes/stability.rs @@ -1,11 +1,6 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. -use rustc::hir::map::Map; -use rustc::middle::privacy::AccessLevels; -use rustc::middle::stability::{DeprecationEntry, Index}; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::ast::Attribute; use rustc_attr::{self as attr, ConstStability, Stability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -15,6 +10,11 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Generics, HirId, Item, StructField, Variant}; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::stability::{DeprecationEntry, Index}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -337,12 +337,14 @@ struct MissingStabilityAnnotations<'a, 'tcx> { } impl<'a, 'tcx> MissingStabilityAnnotations<'a, 'tcx> { - fn check_missing_stability(&self, hir_id: HirId, span: Span, name: &str) { + fn check_missing_stability(&self, hir_id: HirId, span: Span) { let stab = self.tcx.stability().local_stability(hir_id); let is_error = !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(hir_id); if is_error { - self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", name)); + let def_id = self.tcx.hir().local_def_id(hir_id); + let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); + self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr)); } } } @@ -362,42 +364,42 @@ impl<'a, 'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'a, 'tcx> { // optional. They inherit stability from their parents when unannotated. hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {} - _ => self.check_missing_stability(i.hir_id, i.span, i.kind.descr()), + _ => self.check_missing_stability(i.hir_id, i.span), } intravisit::walk_item(self, i) } fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - self.check_missing_stability(ti.hir_id, ti.span, "item"); + self.check_missing_stability(ti.hir_id, ti.span); intravisit::walk_trait_item(self, ti); } fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id)); if self.tcx.impl_trait_ref(impl_def_id).is_none() { - self.check_missing_stability(ii.hir_id, ii.span, "item"); + self.check_missing_stability(ii.hir_id, ii.span); } intravisit::walk_impl_item(self, ii); } fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { - self.check_missing_stability(var.id, var.span, "variant"); + self.check_missing_stability(var.id, var.span); intravisit::walk_variant(self, var, g, item_id); } fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) { - self.check_missing_stability(s.hir_id, s.span, "field"); + self.check_missing_stability(s.hir_id, s.span); intravisit::walk_struct_field(self, s); } fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { - self.check_missing_stability(i.hir_id, i.span, i.kind.descriptive_variant()); + self.check_missing_stability(i.hir_id, i.span); intravisit::walk_foreign_item(self, i); } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { - self.check_missing_stability(md.hir_id, md.span, "macro"); + self.check_missing_stability(md.hir_id, md.span); } } @@ -438,7 +440,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> { // If the `-Z force-unstable-if-unmarked` flag is passed then we provide // a parent stability annotation which indicates that this is private // with the `rustc_private` feature. This is intended for use when - // compiling librustc crates themselves so we can leverage crates.io + // compiling `librustc_*` crates themselves so we can leverage crates.io // while maintaining the invariant that all sysroot crates are unstable // by default and are unable to be used. if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { @@ -478,7 +480,7 @@ pub(crate) fn provide(providers: &mut Providers<'_>) { *providers = Providers { check_mod_unstable_api_usage, ..*providers }; providers.stability_index = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - tcx.arena.alloc(new_index(tcx)) + new_index(tcx) }; } @@ -585,7 +587,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { if tcx.stability().staged_api[&LOCAL_CRATE] { let krate = tcx.hir().krate(); let mut missing = MissingStabilityAnnotations { tcx, access_levels }; - missing.check_missing_stability(hir::CRATE_HIR_ID, krate.item.span, "crate"); + missing.check_missing_stability(hir::CRATE_HIR_ID, krate.item.span); intravisit::walk_crate(&mut missing, krate); krate.visit_all_item_likes(&mut missing.as_deep_visitor()); } @@ -610,7 +612,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // Warn if the user enables a lib feature multiple times. duplicate_feature_err(tcx.sess, *span, *feature); } - remaining_lib_features.insert(feature, span.clone()); + remaining_lib_features.insert(feature, *span); } // `stdbuild` has special handling for `libc`, so we need to // recognise the feature when building std. diff --git a/src/librustc_passes/upvars.rs b/src/librustc_passes/upvars.rs index 43f39e6c610c8..fb986caa415c9 100644 --- a/src/librustc_passes/upvars.rs +++ b/src/librustc_passes/upvars.rs @@ -1,12 +1,12 @@ //! Upvar (closure capture) collection from cross-body HIR uses of `Res::Local`s. -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{self, HirId}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_span::Span; pub fn provide(providers: &mut Providers<'_>) { @@ -15,7 +15,7 @@ pub fn provide(providers: &mut Providers<'_>) { return None; } - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let body = tcx.hir().body(tcx.hir().maybe_body_owned_by(hir_id)?); let mut local_collector = LocalCollector::default(); diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs index cde489e8d2c1e..8a581626862a2 100644 --- a/src/librustc_passes/weak_lang_items.rs +++ b/src/librustc_passes/weak_lang_items.rs @@ -1,14 +1,14 @@ //! Validity checking for weak lang items -use rustc::middle::lang_items; -use rustc::middle::lang_items::whitelisted; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::lang_items; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; -use rustc_session::config; +use rustc_middle::middle::lang_items::whitelisted; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::CrateType; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -38,12 +38,12 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { // We only need to check for the presence of weak lang items if we're // emitting something that's not an rlib. let needs_check = tcx.sess.crate_types.borrow().iter().any(|kind| match *kind { - config::CrateType::Dylib - | config::CrateType::ProcMacro - | config::CrateType::Cdylib - | config::CrateType::Executable - | config::CrateType::Staticlib => true, - config::CrateType::Rlib => false, + CrateType::Dylib + | CrateType::ProcMacro + | CrateType::Cdylib + | CrateType::Executable + | CrateType::Staticlib => true, + CrateType::Rlib => false, }); if !needs_check { return; diff --git a/src/librustc_plugin_impl/Cargo.toml b/src/librustc_plugin_impl/Cargo.toml index 372d6a534c8d4..38cfbd48de246 100644 --- a/src/librustc_plugin_impl/Cargo.toml +++ b/src/librustc_plugin_impl/Cargo.toml @@ -11,7 +11,7 @@ path = "lib.rs" doctest = false [dependencies] -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_errors = { path = "../librustc_errors" } rustc_hir = { path = "../librustc_hir" } rustc_lint = { path = "../librustc_lint" } diff --git a/src/librustc_plugin_impl/build.rs b/src/librustc_plugin_impl/build.rs index 03e58d758c5b5..34522cfe97f35 100644 --- a/src/librustc_plugin_impl/build.rs +++ b/src/librustc_plugin_impl/build.rs @@ -1,11 +1,11 @@ //! Used by `rustc` when compiling a plugin crate. -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; use rustc_ast::attr; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; use rustc_span::Span; @@ -42,7 +42,7 @@ fn plugin_registrar_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option { 0 => None, 1 => { let (hir_id, _) = finder.registrars.pop().unwrap(); - Some(tcx.hir().local_def_id(hir_id)) + Some(tcx.hir().local_def_id(hir_id).to_def_id()) } _ => { let diagnostic = tcx.sess.diagnostic(); diff --git a/src/librustc_plugin_impl/load.rs b/src/librustc_plugin_impl/load.rs index 0810cf56d1783..c3a6016696888 100644 --- a/src/librustc_plugin_impl/load.rs +++ b/src/librustc_plugin_impl/load.rs @@ -1,12 +1,12 @@ //! Used by `rustc` when loading a plugin. use crate::Registry; -use rustc::middle::cstore::MetadataLoader; -use rustc_ast::ast::{Crate, Ident}; +use rustc_ast::ast::Crate; use rustc_errors::struct_span_err; use rustc_metadata::locator; +use rustc_middle::middle::cstore::MetadataLoader; use rustc_session::Session; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::borrow::ToOwned; @@ -76,7 +76,7 @@ fn dylink_registrar( // Make sure the path contains a / or the linker will search for it. let path = env::current_dir().unwrap().join(&path); - let lib = match DynamicLibrary::open(Some(&path)) { + let lib = match DynamicLibrary::open(&path) { Ok(lib) => lib, // this is fatal: there are almost certainly macros we need // inside this crate, so continue would spew "macro undefined" diff --git a/src/librustc_privacy/Cargo.toml b/src/librustc_privacy/Cargo.toml index 9854e0f6c53f3..6110d2ef7fc9a 100644 --- a/src/librustc_privacy/Cargo.toml +++ b/src/librustc_privacy/Cargo.toml @@ -9,13 +9,12 @@ name = "rustc_privacy" path = "lib.rs" [dependencies] -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_attr = { path = "../librustc_attr" } rustc_errors = { path = "../librustc_errors" } rustc_hir = { path = "../librustc_hir" } rustc_typeck = { path = "../librustc_typeck" } rustc_session = { path = "../librustc_session" } -rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } rustc_data_structures = { path = "../librustc_data_structures" } log = "0.4" diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index d680b9e002678..b474b23ac4f5c 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -1,27 +1,27 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] -use rustc::bug; -use rustc::hir::map::Map; -use rustc::middle::privacy::{AccessLevel, AccessLevels}; -use rustc::ty::fold::TypeVisitor; -use rustc::ty::query::Providers; -use rustc::ty::subst::InternalSubsts; -use rustc::ty::{self, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; -use rustc_ast::ast::Ident; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor}; use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; +use rustc_middle::bug; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; +use rustc_middle::ty::fold::TypeVisitor; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::{self, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use std::marker::PhantomData; @@ -160,7 +160,7 @@ where } } } - ty::Projection(proj) | ty::UnnormalizedProjection(proj) => { + ty::Projection(proj) => { if self.def_id_visitor.skip_assoc_tys() { // Visitors searching for minimal visibility/reachability want to // conservatively approximate associated types like `::Alias` @@ -235,7 +235,7 @@ fn def_id_visibility<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, ) -> (ty::Visibility, Span, &'static str) { - match tcx.hir().as_local_hir_id(def_id) { + match def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) { Some(hir_id) => { let vis = match tcx.hir().get(hir_id) { Node::Item(item) => &item.vis, @@ -248,7 +248,7 @@ fn def_id_visibility<'tcx>( } } Node::TraitItem(..) | Node::Variant(..) => { - return def_id_visibility(tcx, tcx.hir().get_parent_did(hir_id)); + return def_id_visibility(tcx, tcx.hir().get_parent_did(hir_id).to_def_id()); } Node::ImplItem(impl_item) => { match tcx.hir().get(tcx.hir().get_parent_item(hir_id)) { @@ -268,11 +268,11 @@ fn def_id_visibility<'tcx>( Node::Variant(..) => { let parent_did = tcx.hir().local_def_id(parent_hir_id); let (mut ctor_vis, mut span, mut descr) = - def_id_visibility(tcx, parent_did); + def_id_visibility(tcx, parent_did.to_def_id()); - let adt_def = tcx.adt_def(tcx.hir().get_parent_did(hir_id)); + let adt_def = tcx.adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()); let ctor_did = tcx.hir().local_def_id(vdata.ctor_hir_id().unwrap()); - let variant = adt_def.variant_with_ctor_id(ctor_did); + let variant = adt_def.variant_with_ctor_id(ctor_did.to_def_id()); if variant.is_field_list_non_exhaustive() && ctor_vis == ty::Visibility::Public @@ -309,7 +309,8 @@ fn def_id_visibility<'tcx>( // If the structure is marked as non_exhaustive then lower the // visibility to within the crate. if ctor_vis == ty::Visibility::Public { - let adt_def = tcx.adt_def(tcx.hir().get_parent_did(hir_id)); + let adt_def = + tcx.adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()); if adt_def.non_enum_variant().is_field_list_non_exhaustive() { ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); @@ -444,7 +445,8 @@ impl VisibilityLike for Option { const SHALLOW: bool = true; fn new_min(find: &FindMin<'_, '_, Self>, def_id: DefId) -> Self { cmp::min( - if let Some(hir_id) = find.tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = find.tcx.hir().as_local_hir_id(def_id); find.access_levels.map.get(&hir_id).cloned() } else { Self::MAX @@ -513,7 +515,7 @@ impl EmbargoVisitor<'tcx> { ) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { ReachEverythingInTheInterfaceVisitor { access_level: cmp::min(access_level, Some(AccessLevel::Reachable)), - item_def_id: self.tcx.hir().local_def_id(item_id), + item_def_id: self.tcx.hir().local_def_id(item_id).to_def_id(), ev: self, } } @@ -535,18 +537,18 @@ impl EmbargoVisitor<'tcx> { for item_id in module.item_ids { let hir_id = item_id.id; let item_def_id = self.tcx.hir().local_def_id(hir_id); - if let Some(def_kind) = self.tcx.def_kind(item_def_id) { - let item = self.tcx.hir().expect_item(hir_id); - let vis = ty::Visibility::from_hir(&item.vis, hir_id, self.tcx); - self.update_macro_reachable_def(hir_id, def_kind, vis, defining_mod); - } + let def_kind = self.tcx.def_kind(item_def_id); + let item = self.tcx.hir().expect_item(hir_id); + let vis = ty::Visibility::from_hir(&item.vis, hir_id, self.tcx); + self.update_macro_reachable_def(hir_id, def_kind, vis, defining_mod); } if let Some(exports) = self.tcx.module_exports(module_def_id) { for export in exports { if export.vis.is_accessible_from(defining_mod, self.tcx) { if let Res::Def(def_kind, def_id) = export.res { let vis = def_id_visibility(self.tcx, def_id).0; - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx.hir().as_local_hir_id(def_id); self.update_macro_reachable_def(hir_id, def_kind, vis, defining_mod); } } @@ -610,7 +612,7 @@ impl EmbargoVisitor<'tcx> { } // These have type privacy, so are not reachable unless they're - // public + // public, or are not namespaced at all. DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocOpaqueTy @@ -623,7 +625,17 @@ impl EmbargoVisitor<'tcx> { | DefKind::AssocFn | DefKind::Trait | DefKind::TyParam - | DefKind::Variant => (), + | DefKind::Variant + | DefKind::LifetimeParam + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::Field + | DefKind::GlobalAsm + | DefKind::Impl + | DefKind::Closure + | DefKind::Generator => (), } } @@ -653,14 +665,16 @@ impl EmbargoVisitor<'tcx> { // If the module is `self`, i.e. the current crate, // there will be no corresponding item. .filter(|def_id| def_id.index != CRATE_DEF_INDEX || def_id.krate != LOCAL_CRATE) - .and_then(|def_id| self.tcx.hir().as_local_hir_id(def_id)) + .and_then(|def_id| { + def_id.as_local().map(|def_id| self.tcx.hir().as_local_hir_id(def_id)) + }) .map(|module_hir_id| self.tcx.hir().expect_item(module_hir_id)) { if let hir::ItemKind::Mod(m) = &item.kind { for item_id in m.item_ids { let item = self.tcx.hir().expect_item(item_id.id); let def_id = self.tcx.hir().local_def_id(item_id.id); - if !self.tcx.hygienic_eq(segment.ident, item.ident, def_id) { + if !self.tcx.hygienic_eq(segment.ident, item.ident, def_id.to_def_id()) { continue; } if let hir::ItemKind::Use(..) = item.kind { @@ -907,7 +921,8 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { for export in exports.iter() { if export.vis == ty::Visibility::Public { if let Some(def_id) = export.res.opt_def_id() { - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx.hir().as_local_hir_id(def_id); self.update(hir_id, Some(AccessLevel::Exported)); } } @@ -926,8 +941,14 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } let macro_module_def_id = - ty::DefIdTree::parent(self.tcx, self.tcx.hir().local_def_id(md.hir_id)).unwrap(); - let mut module_id = match self.tcx.hir().as_local_hir_id(macro_module_def_id) { + ty::DefIdTree::parent(self.tcx, self.tcx.hir().local_def_id(md.hir_id).to_def_id()) + .unwrap(); + // FIXME(#71104) Should really be using just `as_local_hir_id` but + // some `DefId` do not seem to have a corresponding HirId. + let hir_id = macro_module_def_id + .as_local() + .and_then(|def_id| self.tcx.hir().opt_local_def_id_to_hir_id(def_id)); + let mut module_id = match hir_id { Some(module_id) if self.tcx.hir().is_hir_id_module(module_id) => module_id, // `module_id` doesn't correspond to a `mod`, return early (#63164, #65252). _ => return, @@ -989,10 +1010,11 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { self.ev.tcx } fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) -> bool { - if let Some(hir_id) = self.ev.tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.ev.tcx.hir().as_local_hir_id(def_id); if let ((ty::Visibility::Public, ..), _) | (_, Some(AccessLevel::ReachableFromImplTrait)) = - (def_id_visibility(self.tcx(), def_id), self.access_level) + (def_id_visibility(self.tcx(), def_id.to_def_id()), self.access_level) { self.ev.update(hir_id, self.access_level); } @@ -1011,7 +1033,7 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { struct NamePrivacyVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, tables: &'a ty::TypeckTables<'tcx>, - current_item: hir::HirId, + current_item: Option, empty_tables: &'a ty::TypeckTables<'tcx>, } @@ -1023,12 +1045,19 @@ impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { span: Span, // span of the field pattern, e.g., `x: 0` def: &'tcx ty::AdtDef, // definition of the struct or enum field: &'tcx ty::FieldDef, + in_update_syntax: bool, ) { // definition of the field let ident = Ident::new(kw::Invalid, use_ctxt); - let current_hir = self.current_item; + let current_hir = self.current_item.unwrap(); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did, current_hir).1; if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { + let label = if in_update_syntax { + format!("field `{}` is private", field.ident) + } else { + "private field".to_string() + }; + struct_span_err!( self.tcx.sess, span, @@ -1038,7 +1067,7 @@ impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { def.variant_descr(), self.tcx.def_path_str(def.did) ) - .span_label(span, "private field") + .span_label(span, label) .emit(); } } @@ -1066,7 +1095,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let orig_current_item = mem::replace(&mut self.current_item, item.hir_id); + let orig_current_item = mem::replace(&mut self.current_item, Some(item.hir_id)); let orig_tables = mem::replace(&mut self.tables, item_tables(self.tcx, item.hir_id, self.empty_tables)); intravisit::walk_item(self, item); @@ -1089,52 +1118,46 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - match expr.kind { - hir::ExprKind::Struct(ref qpath, fields, ref base) => { - let res = self.tables.qpath_res(qpath, expr.hir_id); - let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); - let variant = adt.variant_of_res(res); - if let Some(ref base) = *base { - // If the expression uses FRU we need to make sure all the unmentioned fields - // are checked for privacy (RFC 736). Rather than computing the set of - // unmentioned fields, just check them all. - for (vf_index, variant_field) in variant.fields.iter().enumerate() { - let field = fields - .iter() - .find(|f| self.tcx.field_index(f.hir_id, self.tables) == vf_index); - let (use_ctxt, span) = match field { - Some(field) => (field.ident.span, field.span), - None => (base.span, base.span), - }; - self.check_field(use_ctxt, span, adt, variant_field); - } - } else { - for field in fields { - let use_ctxt = field.ident.span; - let index = self.tcx.field_index(field.hir_id, self.tables); - self.check_field(use_ctxt, field.span, adt, &variant.fields[index]); - } + if let hir::ExprKind::Struct(ref qpath, fields, ref base) = expr.kind { + let res = self.tables.qpath_res(qpath, expr.hir_id); + let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); + let variant = adt.variant_of_res(res); + if let Some(ref base) = *base { + // If the expression uses FRU we need to make sure all the unmentioned fields + // are checked for privacy (RFC 736). Rather than computing the set of + // unmentioned fields, just check them all. + for (vf_index, variant_field) in variant.fields.iter().enumerate() { + let field = fields + .iter() + .find(|f| self.tcx.field_index(f.hir_id, self.tables) == vf_index); + let (use_ctxt, span) = match field { + Some(field) => (field.ident.span, field.span), + None => (base.span, base.span), + }; + self.check_field(use_ctxt, span, adt, variant_field, true); + } + } else { + for field in fields { + let use_ctxt = field.ident.span; + let index = self.tcx.field_index(field.hir_id, self.tables); + self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false); } } - _ => {} } intravisit::walk_expr(self, expr); } fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { - match pat.kind { - PatKind::Struct(ref qpath, fields, _) => { - let res = self.tables.qpath_res(qpath, pat.hir_id); - let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap(); - let variant = adt.variant_of_res(res); - for field in fields { - let use_ctxt = field.ident.span; - let index = self.tcx.field_index(field.hir_id, self.tables); - self.check_field(use_ctxt, field.span, adt, &variant.fields[index]); - } + if let PatKind::Struct(ref qpath, fields, _) = pat.kind { + let res = self.tables.qpath_res(qpath, pat.hir_id); + let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap(); + let variant = adt.variant_of_res(res); + for field in fields { + let use_ctxt = field.ident.span; + let index = self.tcx.field_index(field.hir_id, self.tables); + self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false); } - _ => {} } intravisit::walk_pat(self, pat); @@ -1150,7 +1173,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { struct TypePrivacyVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, tables: &'a ty::TypeckTables<'tcx>, - current_item: DefId, + current_item: LocalDefId, in_body: bool, span: Span, empty_tables: &'a ty::TypeckTables<'tcx>, @@ -1158,7 +1181,9 @@ struct TypePrivacyVisitor<'a, 'tcx> { impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { fn item_is_accessible(&self, did: DefId) -> bool { - def_id_visibility(self.tcx, did).0.is_accessible_from(self.current_item, self.tcx) + def_id_visibility(self.tcx, did) + .0 + .is_accessible_from(self.current_item.to_def_id(), self.tcx) } // Take node-id of an expression or pattern and check its type for privacy. @@ -1237,7 +1262,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { if !self.in_body { // Avoid calling `hir_trait_to_predicates` in bodies, it will ICE. // The traits' privacy in bodies is already checked as a part of trait object types. - let bounds = rustc_typeck::hir_trait_to_predicates(self.tcx, trait_ref); + let bounds = rustc_typeck::hir_trait_to_predicates( + self.tcx, + trait_ref, + // NOTE: This isn't really right, but the actual type doesn't matter here. It's + // just required by `ty::TraitRef`. + self.tcx.types.never, + ); for (trait_predicate, _, _) in bounds.trait_bounds { if self.visit_trait(*trait_predicate.skip_binder()) { @@ -1313,14 +1344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { let is_local_static = if let DefKind::Static = kind { def_id.is_local() } else { false }; if !self.item_is_accessible(def_id) && !is_local_static { - let name = match *qpath { - hir::QPath::Resolved(_, ref path) => path.to_string(), - hir::QPath::TypeRelative(_, ref segment) => segment.ident.to_string(), + let sess = self.tcx.sess; + let sm = sess.source_map(); + let name = match qpath { + hir::QPath::Resolved(_, path) => sm.span_to_snippet(path.span).ok(), + hir::QPath::TypeRelative(_, segment) => Some(segment.ident.to_string()), }; let kind = kind.descr(def_id); - self.tcx - .sess - .struct_span_err(span, &format!("{} `{}` is private", kind, name)) + let msg = match name { + Some(name) => format!("{} `{}` is private", kind, name), + None => format!("{} is private", kind), + }; + sess.struct_span_err(span, &msg) .span_label(span, &format!("private {}", kind)) .emit(); return; @@ -1423,10 +1458,10 @@ impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { // A path can only be private if: // it's in this crate... - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(did) { + if let Some(did) = did.as_local() { // .. and it corresponds to a private type in the AST (this returns // `None` for type parameters). - match self.tcx.hir().find(hir_id) { + match self.tcx.hir().find(self.tcx.hir().as_local_hir_id(did)) { Some(Node::Item(ref item)) => !item.vis.node.is_pub(), Some(_) | None => false, } @@ -1544,8 +1579,8 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { |tr| { let did = tr.path.res.def_id(); - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(did) { - self.trait_is_public(hir_id) + if let Some(did) = did.as_local() { + self.trait_is_public(self.tcx.hir().as_local_hir_id(did)) } else { true // external traits must be public } @@ -1636,7 +1671,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { found_pub_static = true; intravisit::walk_impl_item(self, impl_item); } - AssocItemKind::Method { has_self: false } => { + AssocItemKind::Fn { has_self: false } => { found_pub_static = true; intravisit::walk_impl_item(self, impl_item); } @@ -1805,8 +1840,8 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { ); } - let hir_id = match self.tcx.hir().as_local_hir_id(def_id) { - Some(hir_id) => hir_id, + let hir_id = match def_id.as_local() { + Some(def_id) => self.tcx.hir().as_local_hir_id(def_id), None => return false, }; @@ -1896,7 +1931,7 @@ impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { SearchInterfaceForPrivateItemsVisitor { tcx: self.tcx, item_id, - item_def_id: self.tcx.hir().local_def_id(item_id), + item_def_id: self.tcx.hir().local_def_id(item_id).to_def_id(), span: self.tcx.hir().span(item_id), required_visibility, has_pub_restricted: self.has_pub_restricted, @@ -1915,7 +1950,7 @@ impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { let mut check = self.check(hir_id, vis); let (check_ty, is_assoc_ty) = match assoc_item_kind { - AssocItemKind::Const | AssocItemKind::Method { .. } => (true, false), + AssocItemKind::Const | AssocItemKind::Fn { .. } => (true, false), AssocItemKind::Type => (defaultness.has_value(), true), // `ty()` for opaque types is the underlying type, // it's not a part of interface, so we skip it. @@ -2040,14 +2075,14 @@ pub fn provide(providers: &mut Providers<'_>) { }; } -fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: DefId) { +fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let empty_tables = ty::TypeckTables::empty(None); // Check privacy of names not checked in previous compilation stages. let mut visitor = NamePrivacyVisitor { tcx, tables: &empty_tables, - current_item: hir::DUMMY_HIR_ID, + current_item: None, empty_tables: &empty_tables, }; let (module, span, hir_id) = tcx.hir().get_module(module_def_id); diff --git a/src/librustc_query_system/Cargo.toml b/src/librustc_query_system/Cargo.toml index a01bb5e5ea30d..392e19e1f4471 100644 --- a/src/librustc_query_system/Cargo.toml +++ b/src/librustc_query_system/Cargo.toml @@ -10,13 +10,13 @@ path = "lib.rs" doctest = false [dependencies] +arena = { path = "../libarena" } log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc_ast = { path = "../librustc_ast" } +rustc-rayon-core = "0.3.0" rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } rustc_index = { path = "../librustc_index" } -rustc_macros = { path = "../librustc_macros" } rustc_serialize = { path = "../libserialize", package = "serialize" } -parking_lot = "0.9" +rustc_span = { path = "../librustc_span" } +parking_lot = "0.10" smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_query_system/dep_graph/dep_node.rs b/src/librustc_query_system/dep_graph/dep_node.rs index c6fff2f01643a..d8875f8ac64a2 100644 --- a/src/librustc_query_system/dep_graph/dep_node.rs +++ b/src/librustc_query_system/dep_graph/dep_node.rs @@ -26,7 +26,7 @@ //! could not be instantiated because the current compilation session //! contained no `DefId` for thing that had been removed. //! -//! `DepNode` definition happens in `librustc` with the `define_dep_nodes!()` macro. +//! `DepNode` definition happens in `librustc_middle` with the `define_dep_nodes!()` macro. //! This macro defines the `DepKind` enum and a corresponding `DepConstructor` enum. The //! `DepConstructor` enum links a `DepKind` to the parameters that are needed at runtime in order //! to construct a valid `DepNode` fingerprint. @@ -46,7 +46,6 @@ use super::{DepContext, DepKind}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_macros::HashStable_Generic; use std::fmt; use std::hash::Hash; @@ -65,6 +64,24 @@ impl DepNode { debug_assert!(!kind.has_params()); DepNode { kind, hash: Fingerprint::ZERO } } + + pub fn construct(tcx: Ctxt, kind: K, arg: &Key) -> DepNode + where + Ctxt: crate::query::QueryContext, + Key: DepNodeParams, + { + let hash = arg.to_fingerprint(tcx); + let dep_node = DepNode { kind, hash }; + + #[cfg(debug_assertions)] + { + if !kind.can_reconstruct_query_key() && tcx.debug_dep_node() { + tcx.dep_graph().register_dep_node_debug_str(dep_node, || arg.to_debug_str(tcx)); + } + } + + dep_node + } } impl fmt::Debug for DepNode { @@ -121,13 +138,18 @@ where } } +impl DepNodeParams for () { + fn to_fingerprint(&self, _: Ctxt) -> Fingerprint { + Fingerprint::ZERO + } +} + /// A "work product" corresponds to a `.o` (or other) file that we /// save in between runs. These IDs do not have a `DefId` but rather /// some independent path or string that persists between runs without /// the need to be mapped or unmapped. (This ensures we can serialize /// them even in the absence of a tcx.) #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable_Generic)] pub struct WorkProductId { hash: Fingerprint, } @@ -144,3 +166,10 @@ impl WorkProductId { WorkProductId { hash: fingerprint } } } + +impl HashStable for WorkProductId { + #[inline] + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { + self.hash.hash_stable(hcx, hasher) + } +} diff --git a/src/librustc_query_system/dep_graph/graph.rs b/src/librustc_query_system/dep_graph/graph.rs index 7352551559cf4..04a45090b7226 100644 --- a/src/librustc_query_system/dep_graph/graph.rs +++ b/src/librustc_query_system/dep_graph/graph.rs @@ -20,10 +20,8 @@ use std::sync::atomic::Ordering::Relaxed; use super::debug::EdgeFilter; use super::prev::PreviousDepGraph; use super::query::DepGraphQuery; -use super::safe::DepGraphSafe; use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use super::{DepContext, DepKind, DepNode, WorkProductId}; -use crate::{HashStableContext, HashStableContextProvider}; #[derive(Clone)] pub struct DepGraph { @@ -191,18 +189,14 @@ impl DepGraph { /// `arg` parameter. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/incremental-compilation.html - pub fn with_task( + pub fn with_task, A, R>( &self, key: DepNode, - cx: C, + cx: Ctxt, arg: A, - task: fn(C, A) -> R, - hash_result: impl FnOnce(&mut H, &R) -> Option, - ) -> (R, DepNodeIndex) - where - C: DepGraphSafe + HashStableContextProvider, - H: HashStableContext, - { + task: fn(Ctxt, A) -> R, + hash_result: impl FnOnce(&mut Ctxt::StableHashingContext, &R) -> Option, + ) -> (R, DepNodeIndex) { self.with_task_impl( key, cx, @@ -223,13 +217,13 @@ impl DepGraph { ) } - fn with_task_impl( + fn with_task_impl, A, R>( &self, key: DepNode, - cx: C, + cx: Ctxt, arg: A, no_tcx: bool, - task: fn(C, A) -> R, + task: fn(Ctxt, A) -> R, create_task: fn(DepNode) -> Option>, finish_task_and_alloc_depnode: fn( &CurrentDepGraph, @@ -237,12 +231,8 @@ impl DepGraph { Fingerprint, Option>, ) -> DepNodeIndex, - hash_result: impl FnOnce(&mut H, &R) -> Option, - ) -> (R, DepNodeIndex) - where - C: DepGraphSafe + HashStableContextProvider, - H: HashStableContext, - { + hash_result: impl FnOnce(&mut Ctxt::StableHashingContext, &R) -> Option, + ) -> (R, DepNodeIndex) { if let Some(ref data) = self.data { let task_deps = create_task(key).map(Lock::new); @@ -251,7 +241,7 @@ impl DepGraph { // anyway so that // - we make sure that the infrastructure works and // - we can get an idea of the runtime cost. - let mut hcx = cx.get_stable_hashing_context(); + let mut hcx = cx.create_stable_hashing_context(); let result = if no_tcx { task(cx, arg) @@ -268,7 +258,7 @@ impl DepGraph { task_deps.map(|lock| lock.into_inner()), ); - let print_status = cfg!(debug_assertions) && hcx.debug_dep_tasks(); + let print_status = cfg!(debug_assertions) && cx.debug_dep_tasks(); // Determine the color of the new DepNode. if let Some(prev_index) = data.previous.node_to_index_opt(&key) { @@ -335,18 +325,14 @@ impl DepGraph { /// Executes something within an "eval-always" task which is a task /// that runs whenever anything changes. - pub fn with_eval_always_task( + pub fn with_eval_always_task, A, R>( &self, key: DepNode, - cx: C, + cx: Ctxt, arg: A, - task: fn(C, A) -> R, - hash_result: impl FnOnce(&mut H, &R) -> Option, - ) -> (R, DepNodeIndex) - where - C: DepGraphSafe + HashStableContextProvider, - H: HashStableContext, - { + task: fn(Ctxt, A) -> R, + hash_result: impl FnOnce(&mut Ctxt::StableHashingContext, &R) -> Option, + ) -> (R, DepNodeIndex) { self.with_task_impl( key, cx, @@ -874,15 +860,8 @@ impl DepGraph { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct WorkProduct { pub cgu_name: String, - /// Saved files associated with this CGU. - pub saved_files: Vec<(WorkProductFileKind, String)>, -} - -#[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, PartialEq)] -pub enum WorkProductFileKind { - Object, - Bytecode, - BytecodeCompressed, + /// Saved file associated with this CGU. + pub saved_file: Option, } #[derive(Clone)] @@ -1126,6 +1105,7 @@ impl DepNodeColorMap { DepNodeColorMap { values: (0..size).map(|_| AtomicU32::new(COMPRESSED_NONE)).collect() } } + #[inline] fn get(&self, index: SerializedDepNodeIndex) -> Option { match self.values[index].load(Ordering::Acquire) { COMPRESSED_NONE => None, diff --git a/src/librustc_query_system/dep_graph/mod.rs b/src/librustc_query_system/dep_graph/mod.rs index 825b341cd146d..e8d02692f37ba 100644 --- a/src/librustc_query_system/dep_graph/mod.rs +++ b/src/librustc_query_system/dep_graph/mod.rs @@ -3,16 +3,12 @@ mod dep_node; mod graph; mod prev; mod query; -mod safe; mod serialized; pub use dep_node::{DepNode, DepNodeParams, WorkProductId}; -pub use graph::WorkProductFileKind; pub use graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct}; pub use prev::PreviousDepGraph; pub use query::DepGraphQuery; -pub use safe::AssertDepGraphSafe; -pub use safe::DepGraphSafe; pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use rustc_data_structures::profiling::SelfProfilerRef; @@ -25,11 +21,14 @@ use std::hash::Hash; pub trait DepContext: Copy { type DepKind: self::DepKind; - type StableHashingContext: crate::HashStableContext; + type StableHashingContext; /// Create a hashing context for hashing new results. fn create_stable_hashing_context(&self) -> Self::StableHashingContext; + fn debug_dep_tasks(&self) -> bool; + fn debug_dep_node(&self) -> bool; + /// Try to force a dep node to execute and see if it's green. fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool; @@ -48,12 +47,21 @@ pub trait DepContext: Copy { /// Register diagnostics for the given node, for use in next session. fn store_diagnostics(&self, dep_node_index: DepNodeIndex, diagnostics: ThinVec); + /// Register diagnostics for the given node, for use in next session. + fn store_diagnostics_for_anon_node( + &self, + dep_node_index: DepNodeIndex, + diagnostics: ThinVec, + ); + /// Access the profiler. fn profiler(&self) -> &SelfProfilerRef; } /// Describe the different families of dependency nodes. pub trait DepKind: Copy + fmt::Debug + Eq + Ord + Hash { + const NULL: Self; + /// Return whether this kind always require evaluation. fn is_eval_always(&self) -> bool; @@ -69,7 +77,9 @@ pub trait DepKind: Copy + fmt::Debug + Eq + Ord + Hash { OP: FnOnce() -> R; /// Access dependencies from current implicit context. - fn read_deps(op: OP) -> () + fn read_deps(op: OP) where - OP: for<'a> FnOnce(Option<&'a Lock>>) -> (); + OP: for<'a> FnOnce(Option<&'a Lock>>); + + fn can_reconstruct_query_key(&self) -> bool; } diff --git a/src/librustc_query_system/dep_graph/query.rs b/src/librustc_query_system/dep_graph/query.rs index 4a4283b2a0cbb..fb313d2658ff4 100644 --- a/src/librustc_query_system/dep_graph/query.rs +++ b/src/librustc_query_system/dep_graph/query.rs @@ -15,7 +15,7 @@ impl DepGraphQuery { let mut graph = Graph::with_capacity(nodes.len(), edges.len()); let mut indices = FxHashMap::default(); for node in nodes { - indices.insert(node.clone(), graph.add_node(node.clone())); + indices.insert(*node, graph.add_node(*node)); } for &(ref source, ref target) in edges { diff --git a/src/librustc_query_system/dep_graph/safe.rs b/src/librustc_query_system/dep_graph/safe.rs deleted file mode 100644 index 7bba348f8841f..0000000000000 --- a/src/librustc_query_system/dep_graph/safe.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! The `DepGraphSafe` trait - -use rustc_ast::ast::NodeId; -use rustc_hir::def_id::DefId; -use rustc_hir::BodyId; - -/// The `DepGraphSafe` trait is used to specify what kinds of values -/// are safe to "leak" into a task. The idea is that this should be -/// only be implemented for things like the tcx as well as various id -/// types, which will create reads in the dep-graph whenever the trait -/// loads anything that might depend on the input program. -pub trait DepGraphSafe {} - -/// A `BodyId` on its own doesn't give access to any particular state. -/// You must fetch the state from the various maps or generate -/// on-demand queries, all of which create reads. -impl DepGraphSafe for BodyId {} - -/// A `NodeId` on its own doesn't give access to any particular state. -/// You must fetch the state from the various maps or generate -/// on-demand queries, all of which create reads. -impl DepGraphSafe for NodeId {} - -/// A `DefId` on its own doesn't give access to any particular state. -/// You must fetch the state from the various maps or generate -/// on-demand queries, all of which create reads. -impl DepGraphSafe for DefId {} - -/// Tuples make it easy to build up state. -impl DepGraphSafe for (A, B) -where - A: DepGraphSafe, - B: DepGraphSafe, -{ -} - -/// Shared ref to dep-graph-safe stuff should still be dep-graph-safe. -impl<'a, A> DepGraphSafe for &'a A where A: DepGraphSafe {} - -/// Mut ref to dep-graph-safe stuff should still be dep-graph-safe. -impl<'a, A> DepGraphSafe for &'a mut A where A: DepGraphSafe {} - -/// No data here! :) -impl DepGraphSafe for () {} - -/// A convenient override that lets you pass arbitrary state into a -/// task. Every use should be accompanied by a comment explaining why -/// it makes sense (or how it could be refactored away in the future). -pub struct AssertDepGraphSafe(pub T); - -impl DepGraphSafe for AssertDepGraphSafe {} diff --git a/src/librustc_query_system/lib.rs b/src/librustc_query_system/lib.rs index ef4886828c411..8e350d3ba267e 100644 --- a/src/librustc_query_system/lib.rs +++ b/src/librustc_query_system/lib.rs @@ -1,32 +1,17 @@ +#![feature(bool_to_option)] #![feature(const_fn)] #![feature(const_if_match)] #![feature(const_panic)] #![feature(core_intrinsics)] -#![feature(specialization)] +#![feature(hash_raw_entry)] +#![feature(specialization)] // FIXME: min_specialization rejects `default const` #![feature(stmt_expr_attributes)] +#![feature(vec_remove_item)] #[macro_use] extern crate log; +#[macro_use] +extern crate rustc_data_structures; pub mod dep_graph; - -pub trait HashStableContext { - fn debug_dep_tasks(&self) -> bool; -} - -/// Something that can provide a stable hashing context. -pub trait HashStableContextProvider { - fn get_stable_hashing_context(&self) -> Ctxt; -} - -impl> HashStableContextProvider for &T { - fn get_stable_hashing_context(&self) -> Ctxt { - (**self).get_stable_hashing_context() - } -} - -impl> HashStableContextProvider for &mut T { - fn get_stable_hashing_context(&self) -> Ctxt { - (**self).get_stable_hashing_context() - } -} +pub mod query; diff --git a/src/librustc_query_system/query/README.md b/src/librustc_query_system/query/README.md new file mode 100644 index 0000000000000..8ec07b9fdeb78 --- /dev/null +++ b/src/librustc_query_system/query/README.md @@ -0,0 +1,3 @@ +For more information about how the query system works, see the [rustc dev guide]. + +[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/query.html diff --git a/src/librustc_query_system/query/caches.rs b/src/librustc_query_system/query/caches.rs new file mode 100644 index 0000000000000..6a47abc5b4612 --- /dev/null +++ b/src/librustc_query_system/query/caches.rs @@ -0,0 +1,223 @@ +use crate::dep_graph::DepNodeIndex; +use crate::query::plumbing::{QueryLookup, QueryState}; +use crate::query::QueryContext; + +use arena::TypedArena; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sharded::Sharded; +use rustc_data_structures::sync::WorkerLocal; +use std::default::Default; +use std::hash::Hash; +use std::marker::PhantomData; + +pub trait CacheSelector { + type Cache; +} + +pub trait QueryStorage: Default { + type Value; + type Stored: Clone; + + /// Store a value without putting it in the cache. + /// This is meant to be used with cycle errors. + fn store_nocache(&self, value: Self::Value) -> Self::Stored; +} + +pub trait QueryCache: QueryStorage { + type Key: Hash; + type Sharded: Default; + + /// Checks if the query is already computed and in the cache. + /// It returns the shard index and a lock guard to the shard, + /// which will be used if the query is not in the cache and we need + /// to compute it. + fn lookup( + &self, + state: &QueryState, + key: Self::Key, + // `on_hit` can be called while holding a lock to the query state shard. + on_hit: OnHit, + on_miss: OnMiss, + ) -> R + where + OnHit: FnOnce(&Self::Stored, DepNodeIndex) -> R, + OnMiss: FnOnce(Self::Key, QueryLookup<'_, CTX, Self::Key, Self::Sharded>) -> R; + + fn complete( + &self, + tcx: CTX, + lock_sharded_storage: &mut Self::Sharded, + key: Self::Key, + value: Self::Value, + index: DepNodeIndex, + ) -> Self::Stored; + + fn iter( + &self, + shards: &Sharded, + get_shard: impl Fn(&mut L) -> &mut Self::Sharded, + f: impl for<'a> FnOnce( + Box + 'a>, + ) -> R, + ) -> R; +} + +pub struct DefaultCacheSelector; + +impl CacheSelector for DefaultCacheSelector { + type Cache = DefaultCache; +} + +pub struct DefaultCache(PhantomData<(K, V)>); + +impl Default for DefaultCache { + fn default() -> Self { + DefaultCache(PhantomData) + } +} + +impl QueryStorage for DefaultCache { + type Value = V; + type Stored = V; + + #[inline] + fn store_nocache(&self, value: Self::Value) -> Self::Stored { + // We have no dedicated storage + value + } +} + +impl QueryCache for DefaultCache { + type Key = K; + type Sharded = FxHashMap; + + #[inline(always)] + fn lookup( + &self, + state: &QueryState, + key: K, + on_hit: OnHit, + on_miss: OnMiss, + ) -> R + where + OnHit: FnOnce(&V, DepNodeIndex) -> R, + OnMiss: FnOnce(K, QueryLookup<'_, CTX, K, Self::Sharded>) -> R, + { + let mut lookup = state.get_lookup(&key); + let lock = &mut *lookup.lock; + + let result = lock.cache.raw_entry().from_key_hashed_nocheck(lookup.key_hash, &key); + + if let Some((_, value)) = result { on_hit(&value.0, value.1) } else { on_miss(key, lookup) } + } + + #[inline] + fn complete( + &self, + _: CTX, + lock_sharded_storage: &mut Self::Sharded, + key: K, + value: V, + index: DepNodeIndex, + ) -> Self::Stored { + lock_sharded_storage.insert(key, (value.clone(), index)); + value + } + + fn iter( + &self, + shards: &Sharded, + get_shard: impl Fn(&mut L) -> &mut Self::Sharded, + f: impl for<'a> FnOnce(Box + 'a>) -> R, + ) -> R { + let mut shards = shards.lock_shards(); + let mut shards: Vec<_> = shards.iter_mut().map(|shard| get_shard(shard)).collect(); + let results = shards.iter_mut().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1)); + f(Box::new(results)) + } +} + +pub struct ArenaCacheSelector<'tcx>(PhantomData<&'tcx ()>); + +impl<'tcx, K: Eq + Hash, V: 'tcx> CacheSelector for ArenaCacheSelector<'tcx> { + type Cache = ArenaCache<'tcx, K, V>; +} + +pub struct ArenaCache<'tcx, K, V> { + arena: WorkerLocal>, + phantom: PhantomData<(K, &'tcx V)>, +} + +impl<'tcx, K, V> Default for ArenaCache<'tcx, K, V> { + fn default() -> Self { + ArenaCache { arena: WorkerLocal::new(|_| TypedArena::default()), phantom: PhantomData } + } +} + +impl<'tcx, K: Eq + Hash, V: 'tcx> QueryStorage for ArenaCache<'tcx, K, V> { + type Value = V; + type Stored = &'tcx V; + + #[inline] + fn store_nocache(&self, value: Self::Value) -> Self::Stored { + let value = self.arena.alloc((value, DepNodeIndex::INVALID)); + let value = unsafe { &*(&value.0 as *const _) }; + &value + } +} + +impl<'tcx, K: Eq + Hash, V: 'tcx> QueryCache for ArenaCache<'tcx, K, V> { + type Key = K; + type Sharded = FxHashMap; + + #[inline(always)] + fn lookup( + &self, + state: &QueryState, + key: K, + on_hit: OnHit, + on_miss: OnMiss, + ) -> R + where + OnHit: FnOnce(&&'tcx V, DepNodeIndex) -> R, + OnMiss: FnOnce(K, QueryLookup<'_, CTX, K, Self::Sharded>) -> R, + { + let mut lookup = state.get_lookup(&key); + let lock = &mut *lookup.lock; + + let result = lock.cache.raw_entry().from_key_hashed_nocheck(lookup.key_hash, &key); + + if let Some((_, value)) = result { + on_hit(&&value.0, value.1) + } else { + on_miss(key, lookup) + } + } + + #[inline] + fn complete( + &self, + _: CTX, + lock_sharded_storage: &mut Self::Sharded, + key: K, + value: V, + index: DepNodeIndex, + ) -> Self::Stored { + let value = self.arena.alloc((value, index)); + let value = unsafe { &*(value as *const _) }; + lock_sharded_storage.insert(key, value); + &value.0 + } + + fn iter( + &self, + shards: &Sharded, + get_shard: impl Fn(&mut L) -> &mut Self::Sharded, + f: impl for<'a> FnOnce(Box + 'a>) -> R, + ) -> R { + let mut shards = shards.lock_shards(); + let mut shards: Vec<_> = shards.iter_mut().map(|shard| get_shard(shard)).collect(); + let results = shards.iter_mut().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1)); + f(Box::new(results)) + } +} diff --git a/src/librustc_query_system/query/config.rs b/src/librustc_query_system/query/config.rs new file mode 100644 index 0000000000000..f031b54346fa9 --- /dev/null +++ b/src/librustc_query_system/query/config.rs @@ -0,0 +1,156 @@ +//! Query configuration and description traits. + +use crate::dep_graph::DepNode; +use crate::dep_graph::SerializedDepNodeIndex; +use crate::query::caches::QueryCache; +use crate::query::plumbing::CycleError; +use crate::query::{QueryContext, QueryState}; +use rustc_data_structures::profiling::ProfileCategory; +use rustc_span::def_id::DefId; + +use rustc_data_structures::fingerprint::Fingerprint; +use std::borrow::Cow; +use std::fmt::Debug; +use std::hash::Hash; + +// The parameter `CTX` is required in librustc_middle: +// implementations may need to access the `'tcx` lifetime in `CTX = TyCtxt<'tcx>`. +pub trait QueryConfig { + const NAME: &'static str; + const CATEGORY: ProfileCategory; + + type Key: Eq + Hash + Clone + Debug; + type Value; + type Stored: Clone; +} + +pub(crate) struct QueryVtable { + pub anon: bool, + pub dep_kind: CTX::DepKind, + pub eval_always: bool, + + // Don't use this method to compute query results, instead use the methods on TyCtxt + pub compute: fn(CTX, K) -> V, + + pub hash_result: fn(&mut CTX::StableHashingContext, &V) -> Option, + pub handle_cycle_error: fn(CTX, CycleError) -> V, + pub cache_on_disk: fn(CTX, &K, Option<&V>) -> bool, + pub try_load_from_disk: fn(CTX, SerializedDepNodeIndex) -> Option, +} + +impl QueryVtable { + pub(crate) fn to_dep_node(&self, tcx: CTX, key: &K) -> DepNode + where + K: crate::dep_graph::DepNodeParams, + { + DepNode::construct(tcx, self.dep_kind, key) + } + + pub(crate) fn compute(&self, tcx: CTX, key: K) -> V { + (self.compute)(tcx, key) + } + + pub(crate) fn hash_result( + &self, + hcx: &mut CTX::StableHashingContext, + value: &V, + ) -> Option { + (self.hash_result)(hcx, value) + } + + pub(crate) fn handle_cycle_error(&self, tcx: CTX, error: CycleError) -> V { + (self.handle_cycle_error)(tcx, error) + } + + pub(crate) fn cache_on_disk(&self, tcx: CTX, key: &K, value: Option<&V>) -> bool { + (self.cache_on_disk)(tcx, key, value) + } + + pub(crate) fn try_load_from_disk(&self, tcx: CTX, index: SerializedDepNodeIndex) -> Option { + (self.try_load_from_disk)(tcx, index) + } +} + +pub trait QueryAccessors: QueryConfig { + const ANON: bool; + const EVAL_ALWAYS: bool; + const DEP_KIND: CTX::DepKind; + + type Cache: QueryCache; + + // Don't use this method to access query results, instead use the methods on TyCtxt + fn query_state<'a>(tcx: CTX) -> &'a QueryState; + + fn to_dep_node(tcx: CTX, key: &Self::Key) -> DepNode + where + Self::Key: crate::dep_graph::DepNodeParams, + { + DepNode::construct(tcx, Self::DEP_KIND, key) + } + + // Don't use this method to compute query results, instead use the methods on TyCtxt + fn compute(tcx: CTX, key: Self::Key) -> Self::Value; + + fn hash_result( + hcx: &mut CTX::StableHashingContext, + result: &Self::Value, + ) -> Option; + + fn handle_cycle_error(tcx: CTX, error: CycleError) -> Self::Value; +} + +pub trait QueryDescription: QueryAccessors { + fn describe(tcx: CTX, key: Self::Key) -> Cow<'static, str>; + + #[inline] + fn cache_on_disk(_: CTX, _: &Self::Key, _: Option<&Self::Value>) -> bool { + false + } + + fn try_load_from_disk(_: CTX, _: SerializedDepNodeIndex) -> Option { + panic!("QueryDescription::load_from_disk() called for an unsupported query.") + } +} + +pub(crate) trait QueryVtableExt { + const VTABLE: QueryVtable; +} + +impl QueryVtableExt for Q +where + CTX: QueryContext, + Q: QueryDescription, +{ + const VTABLE: QueryVtable = QueryVtable { + anon: Q::ANON, + dep_kind: Q::DEP_KIND, + eval_always: Q::EVAL_ALWAYS, + compute: Q::compute, + hash_result: Q::hash_result, + handle_cycle_error: Q::handle_cycle_error, + cache_on_disk: Q::cache_on_disk, + try_load_from_disk: Q::try_load_from_disk, + }; +} + +impl QueryDescription for M +where + M: QueryAccessors, +{ + default fn describe(tcx: CTX, def_id: DefId) -> Cow<'static, str> { + if !tcx.verbose() { + format!("processing `{}`", tcx.def_path_str(def_id)).into() + } else { + let name = ::std::any::type_name::(); + format!("processing {:?} with query `{}`", def_id, name).into() + } + } + + default fn cache_on_disk(_: CTX, _: &Self::Key, _: Option<&Self::Value>) -> bool { + false + } + + default fn try_load_from_disk(_: CTX, _: SerializedDepNodeIndex) -> Option { + panic!("QueryDescription::load_from_disk() called for an unsupported query.") + } +} diff --git a/src/librustc_query_system/query/job.rs b/src/librustc_query_system/query/job.rs new file mode 100644 index 0000000000000..5150b278a7722 --- /dev/null +++ b/src/librustc_query_system/query/job.rs @@ -0,0 +1,568 @@ +use crate::dep_graph::{DepContext, DepKind}; +use crate::query::plumbing::CycleError; +use crate::query::QueryContext; + +use rustc_data_structures::fx::FxHashMap; +use rustc_span::Span; + +use std::convert::TryFrom; +use std::marker::PhantomData; +use std::num::NonZeroU32; + +#[cfg(parallel_compiler)] +use { + parking_lot::{Condvar, Mutex}, + rustc_data_structures::fx::FxHashSet, + rustc_data_structures::stable_hasher::{HashStable, StableHasher}, + rustc_data_structures::sync::Lock, + rustc_data_structures::sync::Lrc, + rustc_data_structures::{jobserver, OnDrop}, + rustc_rayon_core as rayon_core, + rustc_span::DUMMY_SP, + std::iter::FromIterator, + std::{mem, process}, +}; + +/// Represents a span and a query key. +#[derive(Clone, Debug)] +pub struct QueryInfo { + /// The span corresponding to the reason for which this query was required. + pub span: Span, + pub query: Q, +} + +type QueryMap = FxHashMap::DepKind>, QueryJobInfo>; + +/// A value uniquely identifiying an active query job within a shard in the query cache. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct QueryShardJobId(pub NonZeroU32); + +/// A value uniquely identifiying an active query job. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct QueryJobId { + /// Which job within a shard is this + pub job: QueryShardJobId, + + /// In which shard is this job + pub shard: u16, + + /// What kind of query this job is + pub kind: K, +} + +impl QueryJobId { + pub fn new(job: QueryShardJobId, shard: usize, kind: K) -> Self { + QueryJobId { job, shard: u16::try_from(shard).unwrap(), kind } + } + + fn query>(self, map: &QueryMap) -> CTX::Query { + map.get(&self).unwrap().info.query.clone() + } + + #[cfg(parallel_compiler)] + fn span>(self, map: &QueryMap) -> Span { + map.get(&self).unwrap().job.span + } + + #[cfg(parallel_compiler)] + fn parent>(self, map: &QueryMap) -> Option> { + map.get(&self).unwrap().job.parent + } + + #[cfg(parallel_compiler)] + fn latch<'a, CTX: QueryContext>( + self, + map: &'a QueryMap, + ) -> Option<&'a QueryLatch> { + map.get(&self).unwrap().job.latch.as_ref() + } +} + +pub struct QueryJobInfo { + pub info: QueryInfo, + pub job: QueryJob, +} + +/// Represents an active query job. +#[derive(Clone)] +pub struct QueryJob { + pub id: QueryShardJobId, + + /// The span corresponding to the reason for which this query was required. + pub span: Span, + + /// The parent query job which created this job and is implicitly waiting on it. + pub parent: Option>, + + /// The latch that is used to wait on this job. + #[cfg(parallel_compiler)] + latch: Option>, + + dummy: PhantomData>, +} + +impl QueryJob { + /// Creates a new query job. + pub fn new(id: QueryShardJobId, span: Span, parent: Option>) -> Self { + QueryJob { + id, + span, + parent, + #[cfg(parallel_compiler)] + latch: None, + dummy: PhantomData, + } + } + + #[cfg(parallel_compiler)] + pub(super) fn latch(&mut self, _id: QueryJobId) -> QueryLatch { + if self.latch.is_none() { + self.latch = Some(QueryLatch::new()); + } + self.latch.as_ref().unwrap().clone() + } + + #[cfg(not(parallel_compiler))] + pub(super) fn latch(&mut self, id: QueryJobId) -> QueryLatch { + QueryLatch { id, dummy: PhantomData } + } + + /// Signals to waiters that the query is complete. + /// + /// This does nothing for single threaded rustc, + /// as there are no concurrent jobs which could be waiting on us + pub fn signal_complete(self) { + #[cfg(parallel_compiler)] + { + if let Some(latch) = self.latch { + latch.set(); + } + } + } +} + +#[cfg(not(parallel_compiler))] +#[derive(Clone)] +pub(super) struct QueryLatch { + id: QueryJobId, + dummy: PhantomData, +} + +#[cfg(not(parallel_compiler))] +impl QueryLatch { + pub(super) fn find_cycle_in_stack(&self, tcx: CTX, span: Span) -> CycleError { + let query_map = tcx.try_collect_active_jobs().unwrap(); + + // Get the current executing query (waiter) and find the waitee amongst its parents + let mut current_job = tcx.current_query_job(); + let mut cycle = Vec::new(); + + while let Some(job) = current_job { + let info = query_map.get(&job).unwrap(); + cycle.push(info.info.clone()); + + if job == self.id { + cycle.reverse(); + + // This is the end of the cycle + // The span entry we included was for the usage + // of the cycle itself, and not part of the cycle + // Replace it with the span which caused the cycle to form + cycle[0].span = span; + // Find out why the cycle itself was used + let usage = info + .job + .parent + .as_ref() + .map(|parent| (info.info.span, parent.query(&query_map))); + return CycleError { usage, cycle }; + } + + current_job = info.job.parent; + } + + panic!("did not find a cycle") + } +} + +#[cfg(parallel_compiler)] +struct QueryWaiter { + query: Option>, + condvar: Condvar, + span: Span, + cycle: Lock>>, +} + +#[cfg(parallel_compiler)] +impl QueryWaiter { + fn notify(&self, registry: &rayon_core::Registry) { + rayon_core::mark_unblocked(registry); + self.condvar.notify_one(); + } +} + +#[cfg(parallel_compiler)] +struct QueryLatchInfo { + complete: bool, + waiters: Vec>>, +} + +#[cfg(parallel_compiler)] +#[derive(Clone)] +pub(super) struct QueryLatch { + info: Lrc>>, +} + +#[cfg(parallel_compiler)] +impl QueryLatch { + fn new() -> Self { + QueryLatch { + info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), + } + } +} + +#[cfg(parallel_compiler)] +impl QueryLatch { + /// Awaits for the query job to complete. + pub(super) fn wait_on(&self, tcx: CTX, span: Span) -> Result<(), CycleError> { + let query = tcx.current_query_job(); + let waiter = + Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() }); + self.wait_on_inner(&waiter); + // FIXME: Get rid of this lock. We have ownership of the QueryWaiter + // although another thread may still have a Lrc reference so we cannot + // use Lrc::get_mut + let mut cycle = waiter.cycle.lock(); + match cycle.take() { + None => Ok(()), + Some(cycle) => Err(cycle), + } + } +} + +#[cfg(parallel_compiler)] +impl QueryLatch { + /// Awaits the caller on this latch by blocking the current thread. + fn wait_on_inner(&self, waiter: &Lrc>) { + let mut info = self.info.lock(); + if !info.complete { + // We push the waiter on to the `waiters` list. It can be accessed inside + // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. + // Both of these will remove it from the `waiters` list before resuming + // this thread. + info.waiters.push(waiter.clone()); + + // If this detects a deadlock and the deadlock handler wants to resume this thread + // we have to be in the `wait` call. This is ensured by the deadlock handler + // getting the self.info lock. + rayon_core::mark_blocked(); + jobserver::release_thread(); + waiter.condvar.wait(&mut info); + // Release the lock before we potentially block in `acquire_thread` + mem::drop(info); + jobserver::acquire_thread(); + } + } + + /// Sets the latch and resumes all waiters on it + fn set(&self) { + let mut info = self.info.lock(); + debug_assert!(!info.complete); + info.complete = true; + let registry = rayon_core::Registry::current(); + for waiter in info.waiters.drain(..) { + waiter.notify(®istry); + } + } + + /// Removes a single waiter from the list of waiters. + /// This is used to break query cycles. + fn extract_waiter(&self, waiter: usize) -> Lrc> { + let mut info = self.info.lock(); + debug_assert!(!info.complete); + // Remove the waiter from the list of waiters + info.waiters.remove(waiter) + } +} + +/// A resumable waiter of a query. The usize is the index into waiters in the query's latch +#[cfg(parallel_compiler)] +type Waiter = (QueryJobId, usize); + +/// Visits all the non-resumable and resumable waiters of a query. +/// Only waiters in a query are visited. +/// `visit` is called for every waiter and is passed a query waiting on `query_ref` +/// and a span indicating the reason the query waited on `query_ref`. +/// If `visit` returns Some, this function returns. +/// For visits of non-resumable waiters it returns the return value of `visit`. +/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the +/// required information to resume the waiter. +/// If all `visit` calls returns None, this function also returns None. +#[cfg(parallel_compiler)] +fn visit_waiters( + query_map: &QueryMap, + query: QueryJobId, + mut visit: F, +) -> Option>> +where + F: FnMut(Span, QueryJobId) -> Option>>, +{ + // Visit the parent query which is a non-resumable waiter since it's on the same stack + if let Some(parent) = query.parent(query_map) { + if let Some(cycle) = visit(query.span(query_map), parent) { + return Some(cycle); + } + } + + // Visit the explicit waiters which use condvars and are resumable + if let Some(latch) = query.latch(query_map) { + for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { + if let Some(waiter_query) = waiter.query { + if visit(waiter.span, waiter_query).is_some() { + // Return a value which indicates that this waiter can be resumed + return Some(Some((query, i))); + } + } + } + } + + None +} + +/// Look for query cycles by doing a depth first search starting at `query`. +/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. +/// If a cycle is detected, this initial value is replaced with the span causing +/// the cycle. +#[cfg(parallel_compiler)] +fn cycle_check( + query_map: &QueryMap, + query: QueryJobId, + span: Span, + stack: &mut Vec<(Span, QueryJobId)>, + visited: &mut FxHashSet>, +) -> Option>> { + if !visited.insert(query) { + return if let Some(p) = stack.iter().position(|q| q.1 == query) { + // We detected a query cycle, fix up the initial span and return Some + + // Remove previous stack entries + stack.drain(0..p); + // Replace the span for the first query with the cycle cause + stack[0].0 = span; + Some(None) + } else { + None + }; + } + + // Query marked as visited is added it to the stack + stack.push((span, query)); + + // Visit all the waiters + let r = visit_waiters(query_map, query, |span, successor| { + cycle_check(query_map, successor, span, stack, visited) + }); + + // Remove the entry in our stack if we didn't find a cycle + if r.is_none() { + stack.pop(); + } + + r +} + +/// Finds out if there's a path to the compiler root (aka. code which isn't in a query) +/// from `query` without going through any of the queries in `visited`. +/// This is achieved with a depth first search. +#[cfg(parallel_compiler)] +fn connected_to_root( + query_map: &QueryMap, + query: QueryJobId, + visited: &mut FxHashSet>, +) -> bool { + // We already visited this or we're deliberately ignoring it + if !visited.insert(query) { + return false; + } + + // This query is connected to the root (it has no query parent), return true + if query.parent(query_map).is_none() { + return true; + } + + visit_waiters(query_map, query, |_, successor| { + connected_to_root(query_map, successor, visited).then_some(None) + }) + .is_some() +} + +// Deterministically pick an query from a list +#[cfg(parallel_compiler)] +fn pick_query<'a, CTX, T, F>(query_map: &QueryMap, tcx: CTX, queries: &'a [T], f: F) -> &'a T +where + CTX: QueryContext, + F: Fn(&T) -> (Span, QueryJobId), +{ + // Deterministically pick an entry point + // FIXME: Sort this instead + let mut hcx = tcx.create_stable_hashing_context(); + queries + .iter() + .min_by_key(|v| { + let (span, query) = f(v); + let mut stable_hasher = StableHasher::new(); + query.query(query_map).hash_stable(&mut hcx, &mut stable_hasher); + // Prefer entry points which have valid spans for nicer error messages + // We add an integer to the tuple ensuring that entry points + // with valid spans are picked first + let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; + (span_cmp, stable_hasher.finish::()) + }) + .unwrap() +} + +/// Looks for query cycles starting from the last query in `jobs`. +/// If a cycle is found, all queries in the cycle is removed from `jobs` and +/// the function return true. +/// If a cycle was not found, the starting query is removed from `jobs` and +/// the function returns false. +#[cfg(parallel_compiler)] +fn remove_cycle( + query_map: &QueryMap, + jobs: &mut Vec>, + wakelist: &mut Vec>>, + tcx: CTX, +) -> bool { + let mut visited = FxHashSet::default(); + let mut stack = Vec::new(); + // Look for a cycle starting with the last query in `jobs` + if let Some(waiter) = + cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) + { + // The stack is a vector of pairs of spans and queries; reverse it so that + // the earlier entries require later entries + let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); + + // Shift the spans so that queries are matched with the span for their waitee + spans.rotate_right(1); + + // Zip them back together + let mut stack: Vec<_> = spans.into_iter().zip(queries).collect(); + + // Remove the queries in our cycle from the list of jobs to look at + for r in &stack { + jobs.remove_item(&r.1); + } + + // Find the queries in the cycle which are + // connected to queries outside the cycle + let entry_points = stack + .iter() + .filter_map(|&(span, query)| { + if query.parent(query_map).is_none() { + // This query is connected to the root (it has no query parent) + Some((span, query, None)) + } else { + let mut waiters = Vec::new(); + // Find all the direct waiters who lead to the root + visit_waiters(query_map, query, |span, waiter| { + // Mark all the other queries in the cycle as already visited + let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); + + if connected_to_root(query_map, waiter, &mut visited) { + waiters.push((span, waiter)); + } + + None + }); + if waiters.is_empty() { + None + } else { + // Deterministically pick one of the waiters to show to the user + let waiter = *pick_query(query_map, tcx, &waiters, |s| *s); + Some((span, query, Some(waiter))) + } + } + }) + .collect::, Option<(Span, QueryJobId)>)>>(); + + // Deterministically pick an entry point + let (_, entry_point, usage) = pick_query(query_map, tcx, &entry_points, |e| (e.0, e.1)); + + // Shift the stack so that our entry point is first + let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); + if let Some(pos) = entry_point_pos { + stack.rotate_left(pos); + } + + let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map))); + + // Create the cycle error + let error = CycleError { + usage, + cycle: stack + .iter() + .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) }) + .collect(), + }; + + // We unwrap `waiter` here since there must always be one + // edge which is resumeable / waited using a query latch + let (waitee_query, waiter_idx) = waiter.unwrap(); + + // Extract the waiter we want to resume + let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx); + + // Set the cycle error so it will be picked up when resumed + *waiter.cycle.lock() = Some(error); + + // Put the waiter on the list of things to resume + wakelist.push(waiter); + + true + } else { + false + } +} + +/// Detects query cycles by using depth first search over all active query jobs. +/// If a query cycle is found it will break the cycle by finding an edge which +/// uses a query latch and then resuming that waiter. +/// There may be multiple cycles involved in a deadlock, so this searches +/// all active queries for cycles before finally resuming all the waiters at once. +#[cfg(parallel_compiler)] +pub fn deadlock(tcx: CTX, registry: &rayon_core::Registry) { + let on_panic = OnDrop(|| { + eprintln!("deadlock handler panicked, aborting process"); + process::abort(); + }); + + let mut wakelist = Vec::new(); + let query_map = tcx.try_collect_active_jobs().unwrap(); + let mut jobs: Vec> = query_map.keys().cloned().collect(); + + let mut found_cycle = false; + + while jobs.len() > 0 { + if remove_cycle(&query_map, &mut jobs, &mut wakelist, tcx) { + found_cycle = true; + } + } + + // Check that a cycle was found. It is possible for a deadlock to occur without + // a query cycle if a query which can be waited on uses Rayon to do multithreading + // internally. Such a query (X) may be executing on 2 threads (A and B) and A may + // wait using Rayon on B. Rayon may then switch to executing another query (Y) + // which in turn will wait on X causing a deadlock. We have a false dependency from + // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here + // only considers the true dependency and won't detect a cycle. + assert!(found_cycle); + + // FIXME: Ensure this won't cause a deadlock before we return + for waiter in wakelist.into_iter() { + waiter.notify(registry); + } + + on_panic.disable(); +} diff --git a/src/librustc_query_system/query/mod.rs b/src/librustc_query_system/query/mod.rs new file mode 100644 index 0000000000000..49097725bc9b9 --- /dev/null +++ b/src/librustc_query_system/query/mod.rs @@ -0,0 +1,54 @@ +mod plumbing; +pub use self::plumbing::*; + +mod job; +#[cfg(parallel_compiler)] +pub use self::job::deadlock; +pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo}; + +mod caches; +pub use self::caches::{ + ArenaCacheSelector, CacheSelector, DefaultCacheSelector, QueryCache, QueryStorage, +}; + +mod config; +pub use self::config::{QueryAccessors, QueryConfig, QueryDescription}; + +use crate::dep_graph::{DepContext, DepGraph}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::HashStable; +use rustc_data_structures::sync::Lock; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::Diagnostic; +use rustc_span::def_id::DefId; + +pub trait QueryContext: DepContext { + type Query: Clone + HashStable; + + fn incremental_verify_ich(&self) -> bool; + fn verbose(&self) -> bool; + + /// Get string representation from DefPath. + fn def_path_str(&self, def_id: DefId) -> String; + + /// Access the DepGraph. + fn dep_graph(&self) -> &DepGraph; + + /// Get the query information from the TLS context. + fn current_query_job(&self) -> Option>; + + fn try_collect_active_jobs( + &self, + ) -> Option, QueryJobInfo>>; + + /// Executes a job by changing the `ImplicitCtxt` to point to the + /// new query job while it executes. It returns the diagnostics + /// captured during execution and the actual result. + fn start_query( + &self, + token: QueryJobId, + diagnostics: Option<&Lock>>, + compute: impl FnOnce(Self) -> R, + ) -> R; +} diff --git a/src/librustc_query_system/query/plumbing.rs b/src/librustc_query_system/query/plumbing.rs new file mode 100644 index 0000000000000..cc7d0a1570355 --- /dev/null +++ b/src/librustc_query_system/query/plumbing.rs @@ -0,0 +1,752 @@ +//! The implementation of the query system itself. This defines the macros that +//! generate the actual methods on tcx which find and execute the provider, +//! manage the caches, and so forth. + +use crate::dep_graph::{DepKind, DepNode}; +use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use crate::query::caches::QueryCache; +use crate::query::config::{QueryDescription, QueryVtable, QueryVtableExt}; +use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId}; +use crate::query::QueryContext; + +#[cfg(not(parallel_compiler))] +use rustc_data_structures::cold_path; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHasher}; +use rustc_data_structures::sharded::Sharded; +use rustc_data_structures::sync::{Lock, LockGuard}; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::{Diagnostic, FatalError}; +use rustc_span::source_map::DUMMY_SP; +use rustc_span::Span; +use std::collections::hash_map::Entry; +use std::convert::TryFrom; +use std::fmt::Debug; +use std::hash::{Hash, Hasher}; +use std::mem; +use std::num::NonZeroU32; +use std::ptr; +#[cfg(debug_assertions)] +use std::sync::atomic::{AtomicUsize, Ordering}; + +pub(super) struct QueryStateShard { + pub(super) cache: C, + active: FxHashMap>, + + /// Used to generate unique ids for active jobs. + jobs: u32, +} + +impl Default for QueryStateShard { + fn default() -> QueryStateShard { + QueryStateShard { cache: Default::default(), active: Default::default(), jobs: 0 } + } +} + +pub struct QueryState { + cache: C, + shards: Sharded>, + #[cfg(debug_assertions)] + pub cache_hits: AtomicUsize, +} + +impl QueryState { + #[inline] + pub(super) fn get_lookup<'tcx>( + &'tcx self, + key: &C::Key, + ) -> QueryLookup<'tcx, CTX, C::Key, C::Sharded> { + // We compute the key's hash once and then use it for both the + // shard lookup and the hashmap lookup. This relies on the fact + // that both of them use `FxHasher`. + let mut hasher = FxHasher::default(); + key.hash(&mut hasher); + let key_hash = hasher.finish(); + + let shard = self.shards.get_shard_index_by_hash(key_hash); + let lock = self.shards.get_shard_by_index(shard).lock(); + QueryLookup { key_hash, shard, lock } + } +} + +/// Indicates the state of a query for a given key in a query map. +enum QueryResult { + /// An already executing query. The query job can be used to await for its completion. + Started(QueryJob), + + /// The query panicked. Queries trying to wait on this will raise a fatal error which will + /// silently panic. + Poisoned, +} + +impl QueryState { + #[inline(always)] + pub fn iter_results( + &self, + f: impl for<'a> FnOnce( + Box + 'a>, + ) -> R, + ) -> R { + self.cache.iter(&self.shards, |shard| &mut shard.cache, f) + } + + #[inline(always)] + pub fn all_inactive(&self) -> bool { + let shards = self.shards.lock_shards(); + shards.iter().all(|shard| shard.active.is_empty()) + } + + pub fn try_collect_active_jobs( + &self, + kind: CTX::DepKind, + make_query: fn(C::Key) -> CTX::Query, + jobs: &mut FxHashMap, QueryJobInfo>, + ) -> Option<()> + where + C::Key: Clone, + { + // We use try_lock_shards here since we are called from the + // deadlock handler, and this shouldn't be locked. + let shards = self.shards.try_lock_shards()?; + let shards = shards.iter().enumerate(); + jobs.extend(shards.flat_map(|(shard_id, shard)| { + shard.active.iter().filter_map(move |(k, v)| { + if let QueryResult::Started(ref job) = *v { + let id = + QueryJobId { job: job.id, shard: u16::try_from(shard_id).unwrap(), kind }; + let info = QueryInfo { span: job.span, query: make_query(k.clone()) }; + Some((id, QueryJobInfo { info, job: job.clone() })) + } else { + None + } + }) + })); + + Some(()) + } +} + +impl Default for QueryState { + fn default() -> QueryState { + QueryState { + cache: C::default(), + shards: Default::default(), + #[cfg(debug_assertions)] + cache_hits: AtomicUsize::new(0), + } + } +} + +/// Values used when checking a query cache which can be reused on a cache-miss to execute the query. +pub struct QueryLookup<'tcx, CTX: QueryContext, K, C> { + pub(super) key_hash: u64, + shard: usize, + pub(super) lock: LockGuard<'tcx, QueryStateShard>, +} + +/// A type representing the responsibility to execute the job in the `job` field. +/// This will poison the relevant query if dropped. +struct JobOwner<'tcx, CTX: QueryContext, C> +where + C: QueryCache, + C::Key: Eq + Hash + Clone + Debug, +{ + state: &'tcx QueryState, + key: C::Key, + id: QueryJobId, +} + +impl<'tcx, CTX: QueryContext, C> JobOwner<'tcx, CTX, C> +where + C: QueryCache, + C::Key: Eq + Hash + Clone + Debug, +{ + /// Either gets a `JobOwner` corresponding the query, allowing us to + /// start executing the query, or returns with the result of the query. + /// This function assumes that `try_get_cached` is already called and returned `lookup`. + /// If the query is executing elsewhere, this will wait for it and return the result. + /// If the query panicked, this will silently panic. + /// + /// This function is inlined because that results in a noticeable speed-up + /// for some compile-time benchmarks. + #[inline(always)] + fn try_start<'a, 'b>( + tcx: CTX, + state: &'b QueryState, + span: Span, + key: &C::Key, + mut lookup: QueryLookup<'a, CTX, C::Key, C::Sharded>, + query: &QueryVtable, + ) -> TryGetJob<'b, CTX, C> + where + CTX: QueryContext, + { + let lock = &mut *lookup.lock; + + let (latch, mut _query_blocked_prof_timer) = match lock.active.entry((*key).clone()) { + Entry::Occupied(mut entry) => { + match entry.get_mut() { + QueryResult::Started(job) => { + // For parallel queries, we'll block and wait until the query running + // in another thread has completed. Record how long we wait in the + // self-profiler. + let _query_blocked_prof_timer = if cfg!(parallel_compiler) { + Some(tcx.profiler().query_blocked()) + } else { + None + }; + + // Create the id of the job we're waiting for + let id = QueryJobId::new(job.id, lookup.shard, query.dep_kind); + + (job.latch(id), _query_blocked_prof_timer) + } + QueryResult::Poisoned => FatalError.raise(), + } + } + Entry::Vacant(entry) => { + // No job entry for this query. Return a new one to be started later. + + // Generate an id unique within this shard. + let id = lock.jobs.checked_add(1).unwrap(); + lock.jobs = id; + let id = QueryShardJobId(NonZeroU32::new(id).unwrap()); + + let global_id = QueryJobId::new(id, lookup.shard, query.dep_kind); + + let job = tcx.current_query_job(); + let job = QueryJob::new(id, span, job); + + entry.insert(QueryResult::Started(job)); + + let owner = JobOwner { state, id: global_id, key: (*key).clone() }; + return TryGetJob::NotYetStarted(owner); + } + }; + mem::drop(lookup.lock); + + // If we are single-threaded we know that we have cycle error, + // so we just return the error. + #[cfg(not(parallel_compiler))] + return TryGetJob::Cycle(cold_path(|| { + let value = query.handle_cycle_error(tcx, latch.find_cycle_in_stack(tcx, span)); + state.cache.store_nocache(value) + })); + + // With parallel queries we might just have to wait on some other + // thread. + #[cfg(parallel_compiler)] + { + let result = latch.wait_on(tcx, span); + + if let Err(cycle) = result { + let value = query.handle_cycle_error(tcx, cycle); + let value = state.cache.store_nocache(value); + return TryGetJob::Cycle(value); + } + + let cached = try_get_cached( + tcx, + state, + (*key).clone(), + |value, index| (value.clone(), index), + |_, _| panic!("value must be in cache after waiting"), + ); + + if let Some(prof_timer) = _query_blocked_prof_timer.take() { + prof_timer.finish_with_query_invocation_id(cached.1.into()); + } + + return TryGetJob::JobCompleted(cached); + } + } + + /// Completes the query by updating the query cache with the `result`, + /// signals the waiter and forgets the JobOwner, so it won't poison the query + #[inline(always)] + fn complete(self, tcx: CTX, result: C::Value, dep_node_index: DepNodeIndex) -> C::Stored { + // We can move out of `self` here because we `mem::forget` it below + let key = unsafe { ptr::read(&self.key) }; + let state = self.state; + + // Forget ourself so our destructor won't poison the query + mem::forget(self); + + let (job, result) = { + let mut lock = state.shards.get_shard_by_value(&key).lock(); + let job = match lock.active.remove(&key).unwrap() { + QueryResult::Started(job) => job, + QueryResult::Poisoned => panic!(), + }; + let result = state.cache.complete(tcx, &mut lock.cache, key, result, dep_node_index); + (job, result) + }; + + job.signal_complete(); + result + } +} + +#[inline(always)] +fn with_diagnostics(f: F) -> (R, ThinVec) +where + F: FnOnce(Option<&Lock>>) -> R, +{ + let diagnostics = Lock::new(ThinVec::new()); + let result = f(Some(&diagnostics)); + (result, diagnostics.into_inner()) +} + +impl<'tcx, CTX: QueryContext, C: QueryCache> Drop for JobOwner<'tcx, CTX, C> +where + C::Key: Eq + Hash + Clone + Debug, +{ + #[inline(never)] + #[cold] + fn drop(&mut self) { + // Poison the query so jobs waiting on it panic. + let state = self.state; + let shard = state.shards.get_shard_by_value(&self.key); + let job = { + let mut shard = shard.lock(); + let job = match shard.active.remove(&self.key).unwrap() { + QueryResult::Started(job) => job, + QueryResult::Poisoned => panic!(), + }; + shard.active.insert(self.key.clone(), QueryResult::Poisoned); + job + }; + // Also signal the completion of the job, so waiters + // will continue execution. + job.signal_complete(); + } +} + +#[derive(Clone)] +pub struct CycleError { + /// The query and related span that uses the cycle. + pub usage: Option<(Span, Q)>, + pub cycle: Vec>, +} + +/// The result of `try_start`. +enum TryGetJob<'tcx, CTX: QueryContext, C: QueryCache> +where + C::Key: Eq + Hash + Clone + Debug, +{ + /// The query is not yet started. Contains a guard to the cache eventually used to start it. + NotYetStarted(JobOwner<'tcx, CTX, C>), + + /// The query was already completed. + /// Returns the result of the query and its dep-node index + /// if it succeeded or a cycle error if it failed. + #[cfg(parallel_compiler)] + JobCompleted((C::Stored, DepNodeIndex)), + + /// Trying to execute the query resulted in a cycle. + Cycle(C::Stored), +} + +/// Checks if the query is already computed and in the cache. +/// It returns the shard index and a lock guard to the shard, +/// which will be used if the query is not in the cache and we need +/// to compute it. +#[inline(always)] +fn try_get_cached( + tcx: CTX, + state: &QueryState, + key: C::Key, + // `on_hit` can be called while holding a lock to the query cache + on_hit: OnHit, + on_miss: OnMiss, +) -> R +where + C: QueryCache, + CTX: QueryContext, + OnHit: FnOnce(&C::Stored, DepNodeIndex) -> R, + OnMiss: FnOnce(C::Key, QueryLookup<'_, CTX, C::Key, C::Sharded>) -> R, +{ + state.cache.lookup( + state, + key, + |value, index| { + if unlikely!(tcx.profiler().enabled()) { + tcx.profiler().query_cache_hit(index.into()); + } + #[cfg(debug_assertions)] + { + state.cache_hits.fetch_add(1, Ordering::Relaxed); + } + on_hit(value, index) + }, + on_miss, + ) +} + +#[inline(always)] +fn try_execute_query( + tcx: CTX, + state: &QueryState, + span: Span, + key: C::Key, + lookup: QueryLookup<'_, CTX, C::Key, C::Sharded>, + query: &QueryVtable, +) -> C::Stored +where + C: QueryCache, + C::Key: Eq + Clone + Debug + crate::dep_graph::DepNodeParams, + C::Stored: Clone, + CTX: QueryContext, +{ + let job = match JobOwner::try_start(tcx, state, span, &key, lookup, query) { + TryGetJob::NotYetStarted(job) => job, + TryGetJob::Cycle(result) => return result, + #[cfg(parallel_compiler)] + TryGetJob::JobCompleted((v, index)) => { + tcx.dep_graph().read_index(index); + return v; + } + }; + + // Fast path for when incr. comp. is off. `to_dep_node` is + // expensive for some `DepKind`s. + if !tcx.dep_graph().is_fully_enabled() { + let null_dep_node = DepNode::new_no_params(DepKind::NULL); + return force_query_with_job(tcx, key, job, null_dep_node, query).0; + } + + if query.anon { + let prof_timer = tcx.profiler().query_provider(); + + let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| { + tcx.start_query(job.id, diagnostics, |tcx| { + tcx.dep_graph().with_anon_task(query.dep_kind, || query.compute(tcx, key)) + }) + }); + + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + tcx.dep_graph().read_index(dep_node_index); + + if unlikely!(!diagnostics.is_empty()) { + tcx.store_diagnostics_for_anon_node(dep_node_index, diagnostics); + } + + return job.complete(tcx, result, dep_node_index); + } + + let dep_node = query.to_dep_node(tcx, &key); + + if !query.eval_always { + // The diagnostics for this query will be + // promoted to the current session during + // `try_mark_green()`, so we can ignore them here. + let loaded = tcx.start_query(job.id, None, |tcx| { + let marked = tcx.dep_graph().try_mark_green_and_read(tcx, &dep_node); + marked.map(|(prev_dep_node_index, dep_node_index)| { + ( + load_from_disk_and_cache_in_memory( + tcx, + key.clone(), + prev_dep_node_index, + dep_node_index, + &dep_node, + query, + ), + dep_node_index, + ) + }) + }); + if let Some((result, dep_node_index)) = loaded { + return job.complete(tcx, result, dep_node_index); + } + } + + let (result, dep_node_index) = force_query_with_job(tcx, key, job, dep_node, query); + tcx.dep_graph().read_index(dep_node_index); + result +} + +fn load_from_disk_and_cache_in_memory( + tcx: CTX, + key: K, + prev_dep_node_index: SerializedDepNodeIndex, + dep_node_index: DepNodeIndex, + dep_node: &DepNode, + query: &QueryVtable, +) -> V +where + CTX: QueryContext, +{ + // Note this function can be called concurrently from the same query + // We must ensure that this is handled correctly. + + debug_assert!(tcx.dep_graph().is_green(dep_node)); + + // First we try to load the result from the on-disk cache. + let result = if query.cache_on_disk(tcx, &key, None) { + let prof_timer = tcx.profiler().incr_cache_loading(); + let result = query.try_load_from_disk(tcx, prev_dep_node_index); + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + // We always expect to find a cached result for things that + // can be forced from `DepNode`. + debug_assert!( + !dep_node.kind.can_reconstruct_query_key() || result.is_some(), + "missing on-disk cache entry for {:?}", + dep_node + ); + result + } else { + // Some things are never cached on disk. + None + }; + + let result = if let Some(result) = result { + result + } else { + // We could not load a result from the on-disk cache, so + // recompute. + let prof_timer = tcx.profiler().query_provider(); + + // The dep-graph for this computation is already in-place. + let result = tcx.dep_graph().with_ignore(|| query.compute(tcx, key)); + + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + result + }; + + // If `-Zincremental-verify-ich` is specified, re-hash results from + // the cache and make sure that they have the expected fingerprint. + if unlikely!(tcx.incremental_verify_ich()) { + incremental_verify_ich(tcx, &result, dep_node, dep_node_index, query); + } + + result +} + +#[inline(never)] +#[cold] +fn incremental_verify_ich( + tcx: CTX, + result: &V, + dep_node: &DepNode, + dep_node_index: DepNodeIndex, + query: &QueryVtable, +) where + CTX: QueryContext, +{ + assert!( + Some(tcx.dep_graph().fingerprint_of(dep_node_index)) + == tcx.dep_graph().prev_fingerprint_of(dep_node), + "fingerprint for green query instance not loaded from cache: {:?}", + dep_node, + ); + + debug!("BEGIN verify_ich({:?})", dep_node); + let mut hcx = tcx.create_stable_hashing_context(); + + let new_hash = query.hash_result(&mut hcx, result).unwrap_or(Fingerprint::ZERO); + debug!("END verify_ich({:?})", dep_node); + + let old_hash = tcx.dep_graph().fingerprint_of(dep_node_index); + + assert!(new_hash == old_hash, "found unstable fingerprints for {:?}", dep_node,); +} + +#[inline(always)] +fn force_query_with_job( + tcx: CTX, + key: C::Key, + job: JobOwner<'_, CTX, C>, + dep_node: DepNode, + query: &QueryVtable, +) -> (C::Stored, DepNodeIndex) +where + C: QueryCache, + C::Key: Eq + Clone + Debug, + C::Stored: Clone, + CTX: QueryContext, +{ + // If the following assertion triggers, it can have two reasons: + // 1. Something is wrong with DepNode creation, either here or + // in `DepGraph::try_mark_green()`. + // 2. Two distinct query keys get mapped to the same `DepNode` + // (see for example #48923). + assert!( + !tcx.dep_graph().dep_node_exists(&dep_node), + "forcing query with already existing `DepNode`\n\ + - query-key: {:?}\n\ + - dep-node: {:?}", + key, + dep_node + ); + + let prof_timer = tcx.profiler().query_provider(); + + let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| { + tcx.start_query(job.id, diagnostics, |tcx| { + if query.eval_always { + tcx.dep_graph().with_eval_always_task( + dep_node, + tcx, + key, + query.compute, + query.hash_result, + ) + } else { + tcx.dep_graph().with_task(dep_node, tcx, key, query.compute, query.hash_result) + } + }) + }); + + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + if unlikely!(!diagnostics.is_empty()) { + if dep_node.kind != DepKind::NULL { + tcx.store_diagnostics(dep_node_index, diagnostics); + } + } + + let result = job.complete(tcx, result, dep_node_index); + + (result, dep_node_index) +} + +#[inline(never)] +fn get_query_impl( + tcx: CTX, + state: &QueryState, + span: Span, + key: C::Key, + query: &QueryVtable, +) -> C::Stored +where + CTX: QueryContext, + C: QueryCache, + C::Key: Eq + Clone + crate::dep_graph::DepNodeParams, + C::Stored: Clone, +{ + try_get_cached( + tcx, + state, + key, + |value, index| { + tcx.dep_graph().read_index(index); + value.clone() + }, + |key, lookup| try_execute_query(tcx, state, span, key, lookup, query), + ) +} + +/// Ensure that either this query has all green inputs or been executed. +/// Executing `query::ensure(D)` is considered a read of the dep-node `D`. +/// +/// This function is particularly useful when executing passes for their +/// side-effects -- e.g., in order to report errors for erroneous programs. +/// +/// Note: The optimization is only available during incr. comp. +#[inline(never)] +fn ensure_query_impl( + tcx: CTX, + state: &QueryState, + key: C::Key, + query: &QueryVtable, +) where + C: QueryCache, + C::Key: Eq + Clone + crate::dep_graph::DepNodeParams, + CTX: QueryContext, +{ + if query.eval_always { + let _ = get_query_impl(tcx, state, DUMMY_SP, key, query); + return; + } + + // Ensuring an anonymous query makes no sense + assert!(!query.anon); + + let dep_node = query.to_dep_node(tcx, &key); + + match tcx.dep_graph().try_mark_green_and_read(tcx, &dep_node) { + None => { + // A None return from `try_mark_green_and_read` means that this is either + // a new dep node or that the dep node has already been marked red. + // Either way, we can't call `dep_graph.read()` as we don't have the + // DepNodeIndex. We must invoke the query itself. The performance cost + // this introduces should be negligible as we'll immediately hit the + // in-memory cache, or another query down the line will. + let _ = get_query_impl(tcx, state, DUMMY_SP, key, query); + } + Some((_, dep_node_index)) => { + tcx.profiler().query_cache_hit(dep_node_index.into()); + } + } +} + +#[inline(never)] +fn force_query_impl( + tcx: CTX, + state: &QueryState, + key: C::Key, + span: Span, + dep_node: DepNode, + query: &QueryVtable, +) where + C: QueryCache, + C::Key: Eq + Clone + crate::dep_graph::DepNodeParams, + CTX: QueryContext, +{ + // We may be concurrently trying both execute and force a query. + // Ensure that only one of them runs the query. + + try_get_cached( + tcx, + state, + key, + |_, _| { + // Cache hit, do nothing + }, + |key, lookup| { + let job = match JobOwner::try_start(tcx, state, span, &key, lookup, query) { + TryGetJob::NotYetStarted(job) => job, + TryGetJob::Cycle(_) => return, + #[cfg(parallel_compiler)] + TryGetJob::JobCompleted(_) => return, + }; + force_query_with_job(tcx, key, job, dep_node, query); + }, + ); +} + +#[inline(always)] +pub fn get_query(tcx: CTX, span: Span, key: Q::Key) -> Q::Stored +where + Q: QueryDescription, + Q::Key: crate::dep_graph::DepNodeParams, + CTX: QueryContext, +{ + debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span); + + get_query_impl(tcx, Q::query_state(tcx), span, key, &Q::VTABLE) +} + +#[inline(always)] +pub fn ensure_query(tcx: CTX, key: Q::Key) +where + Q: QueryDescription, + Q::Key: crate::dep_graph::DepNodeParams, + CTX: QueryContext, +{ + ensure_query_impl(tcx, Q::query_state(tcx), key, &Q::VTABLE) +} + +#[inline(always)] +pub fn force_query(tcx: CTX, key: Q::Key, span: Span, dep_node: DepNode) +where + Q: QueryDescription, + Q::Key: crate::dep_graph::DepNodeParams, + CTX: QueryContext, +{ + force_query_impl(tcx, Q::query_state(tcx), key, span, dep_node, &Q::VTABLE) +} diff --git a/src/librustc_resolve/Cargo.toml b/src/librustc_resolve/Cargo.toml index 49f079ad27070..420a82d6d2ced 100644 --- a/src/librustc_resolve/Cargo.toml +++ b/src/librustc_resolve/Cargo.toml @@ -15,7 +15,7 @@ bitflags = "1.2.1" log = "0.4" rustc_ast = { path = "../librustc_ast" } arena = { path = "../libarena" } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_ast_lowering = { path = "../librustc_ast_lowering" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 5408c85a4d006..988ec3d4374e0 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -15,13 +15,8 @@ use crate::{ }; use crate::{Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, Segment, ToNameBinding}; -use rustc::bug; -use rustc::hir::exports::Export; -use rustc::middle::cstore::CrateStore; -use rustc::ty; use rustc_ast::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId}; use rustc_ast::ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind}; -use rustc_ast::ast::{Ident, Name}; use rustc_ast::token::{self, Token}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_attr as attr; @@ -30,11 +25,15 @@ use rustc_errors::{struct_span_err, Applicability}; use rustc_expand::base::SyntaxExtension; use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_metadata::creader::LoadedMacro; +use rustc_middle::bug; +use rustc_middle::hir::exports::Export; +use rustc_middle::middle::cstore::CrateStore; +use rustc_middle::ty; use rustc_span::hygiene::{ExpnId, MacroKind}; use rustc_span::source_map::{respan, Spanned}; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use log::debug; @@ -96,7 +95,7 @@ impl<'a> Resolver<'a> { } crate fn get_module(&mut self, def_id: DefId) -> Module<'a> { - if def_id.krate == LOCAL_CRATE { + if let Some(def_id) = def_id.as_local() { return self.module_map[&def_id]; } @@ -293,7 +292,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.insert_field_names(def_id, field_names); } - fn insert_field_names(&mut self, def_id: DefId, field_names: Vec>) { + fn insert_field_names(&mut self, def_id: DefId, field_names: Vec>) { if !field_names.is_empty() { self.r.field_names.insert(def_id, field_names); } @@ -427,7 +426,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { return; } - // Replace `use foo::self;` with `use foo;` + // Replace `use foo::{ self };` with `use foo;` source = module_path.pop().unwrap(); if rename.is_none() { ident = source.ident; @@ -436,10 +435,33 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } else { // Disallow `self` if source.ident.name == kw::SelfLower { + let parent = module_path.last(); + + let span = match parent { + // only `::self` from `use foo::self as bar` + Some(seg) => seg.ident.span.shrink_to_hi().to(source.ident.span), + None => source.ident.span, + }; + let span_with_rename = match rename { + // only `self as bar` from `use foo::self as bar` + Some(rename) => source.ident.span.to(rename.span), + None => source.ident.span, + }; self.r.report_error( - use_tree.span, - ResolutionError::SelfImportsOnlyAllowedWithin, + span, + ResolutionError::SelfImportsOnlyAllowedWithin { + root: parent.is_none(), + span_with_rename, + }, ); + + // Error recovery: replace `use foo::self;` with `use foo;` + if let Some(parent) = module_path.pop() { + source = parent; + if rename.is_none() { + ident = source.ident; + } + } } // Disallow `use $crate;` @@ -675,12 +697,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ItemKind::Mod(..) => { let def_id = self.r.definitions.local_def_id(item.id); - let module_kind = ModuleKind::Def(DefKind::Mod, def_id, ident.name); + let module_kind = ModuleKind::Def(DefKind::Mod, def_id.to_def_id(), ident.name); let module = self.r.arenas.alloc_module(ModuleData { no_implicit_prelude: parent.no_implicit_prelude || { attr::contains_name(&item.attrs, sym::no_implicit_prelude) }, - ..ModuleData::new(Some(parent), module_kind, def_id, expansion, item.span) + ..ModuleData::new( + Some(parent), + module_kind, + def_id.to_def_id(), + expansion, + item.span, + ) }); self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); self.r.module_map.insert(def_id, module); @@ -691,15 +719,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // These items live in the value namespace. ItemKind::Static(..) => { - let res = Res::Def(DefKind::Static, self.r.definitions.local_def_id(item.id)); + let res = + Res::Def(DefKind::Static, self.r.definitions.local_def_id(item.id).to_def_id()); self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); } ItemKind::Const(..) => { - let res = Res::Def(DefKind::Const, self.r.definitions.local_def_id(item.id)); + let res = + Res::Def(DefKind::Const, self.r.definitions.local_def_id(item.id).to_def_id()); self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); } ItemKind::Fn(..) => { - let res = Res::Def(DefKind::Fn, self.r.definitions.local_def_id(item.id)); + let res = + Res::Def(DefKind::Fn, self.r.definitions.local_def_id(item.id).to_def_id()); self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); // Functions introducing procedural macros reserve a slot @@ -713,12 +744,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { None => DefKind::TyAlias, Some(_) => DefKind::OpaqueTy, }; - let res = Res::Def(def_kind, self.r.definitions.local_def_id(item.id)); + let res = Res::Def(def_kind, self.r.definitions.local_def_id(item.id).to_def_id()); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } ItemKind::Enum(_, _) => { - let def_id = self.r.definitions.local_def_id(item.id); + let def_id = self.r.definitions.local_def_id(item.id).to_def_id(); self.r.variant_vis.insert(def_id, vis); let module_kind = ModuleKind::Def(DefKind::Enum, def_id, ident.name); let module = self.r.new_module( @@ -733,14 +764,17 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::TraitAlias(..) => { - let res = Res::Def(DefKind::TraitAlias, self.r.definitions.local_def_id(item.id)); + let res = Res::Def( + DefKind::TraitAlias, + self.r.definitions.local_def_id(item.id).to_def_id(), + ); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } // These items live in both the type and value namespaces. ItemKind::Struct(ref vdata, _) => { // Define a name in the type namespace. - let def_id = self.r.definitions.local_def_id(item.id); + let def_id = self.r.definitions.local_def_id(item.id).to_def_id(); let res = Res::Def(DefKind::Struct, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); @@ -773,7 +807,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } let ctor_res = Res::Def( DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)), - self.r.definitions.local_def_id(ctor_node_id), + self.r.definitions.local_def_id(ctor_node_id).to_def_id(), ); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion)); self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis)); @@ -781,7 +815,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::Union(ref vdata, _) => { - let def_id = self.r.definitions.local_def_id(item.id); + let def_id = self.r.definitions.local_def_id(item.id).to_def_id(); let res = Res::Def(DefKind::Union, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); @@ -790,7 +824,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::Trait(..) => { - let def_id = self.r.definitions.local_def_id(item.id); + let def_id = self.r.definitions.local_def_id(item.id).to_def_id(); // Add all the items within to a new module. let module_kind = ModuleKind::Def(DefKind::Trait, def_id, ident.name); @@ -815,15 +849,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { /// Constructs the reduced graph for one foreign item. fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem) { let (res, ns) = match item.kind { - ForeignItemKind::Fn(..) => { - (Res::Def(DefKind::Fn, self.r.definitions.local_def_id(item.id)), ValueNS) - } - ForeignItemKind::Static(..) => { - (Res::Def(DefKind::Static, self.r.definitions.local_def_id(item.id)), ValueNS) - } - ForeignItemKind::TyAlias(..) => { - (Res::Def(DefKind::ForeignTy, self.r.definitions.local_def_id(item.id)), TypeNS) - } + ForeignItemKind::Fn(..) => ( + Res::Def(DefKind::Fn, self.r.definitions.local_def_id(item.id).to_def_id()), + ValueNS, + ), + ForeignItemKind::Static(..) => ( + Res::Def(DefKind::Static, self.r.definitions.local_def_id(item.id).to_def_id()), + ValueNS, + ), + ForeignItemKind::TyAlias(..) => ( + Res::Def(DefKind::ForeignTy, self.r.definitions.local_def_id(item.id).to_def_id()), + TypeNS, + ), ForeignItemKind::MacCall(_) => unreachable!(), }; let parent = self.parent_scope.module; @@ -855,9 +892,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let expansion = ExpnId::root(); // FIXME(jseyfried) intercrate hygiene // Record primary definitions. match res { - Res::Def(kind @ DefKind::Mod, def_id) - | Res::Def(kind @ DefKind::Enum, def_id) - | Res::Def(kind @ DefKind::Trait, def_id) => { + Res::Def(kind @ (DefKind::Mod | DefKind::Enum | DefKind::Trait), def_id) => { let module = self.r.new_module( parent, ModuleKind::Def(kind, def_id, ident.name), @@ -867,30 +902,47 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ); self.r.define(parent, ident, TypeNS, (module, vis, span, expansion)); } - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::Variant, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::ForeignTy, _) - | Res::Def(DefKind::OpaqueTy, _) - | Res::Def(DefKind::TraitAlias, _) - | Res::Def(DefKind::AssocTy, _) - | Res::Def(DefKind::AssocOpaqueTy, _) + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Variant + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::OpaqueTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::AssocOpaqueTy, + _, + ) | Res::PrimTy(..) | Res::ToolMod => self.r.define(parent, ident, TypeNS, (res, vis, span, expansion)), - Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Static, _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::Ctor(..), _) => { - self.r.define(parent, ident, ValueNS, (res, vis, span, expansion)) - } + Res::Def( + DefKind::Fn + | DefKind::AssocFn + | DefKind::Static + | DefKind::Const + | DefKind::AssocConst + | DefKind::Ctor(..), + _, + ) => self.r.define(parent, ident, ValueNS, (res, vis, span, expansion)), Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => { self.r.define(parent, ident, MacroNS, (res, vis, span, expansion)) } - Res::Def(DefKind::TyParam, _) - | Res::Def(DefKind::ConstParam, _) + Res::Def( + DefKind::TyParam + | DefKind::ConstParam + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Closure + | DefKind::Impl + | DefKind::Generator, + _, + ) | Res::Local(..) | Res::SelfTy(..) | Res::SelfCtor(..) @@ -899,14 +951,14 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Record some extra data for better diagnostics. let cstore = self.r.cstore(); match res { - Res::Def(DefKind::Struct, def_id) | Res::Def(DefKind::Union, def_id) => { + Res::Def(DefKind::Struct | DefKind::Union, def_id) => { let field_names = cstore.struct_field_names_untracked(def_id, self.r.session); self.insert_field_names(def_id, field_names); } Res::Def(DefKind::AssocFn, def_id) => { if cstore .associated_item_cloned_untracked(def_id, self.r.session) - .method_has_self_argument + .fn_has_self_parameter { self.r.has_self.insert(def_id); } @@ -923,7 +975,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { fn add_macro_use_binding( &mut self, - name: ast::Name, + name: Symbol, binding: &'a NameBinding<'a>, span: Span, allow_shadowing: bool, @@ -1121,7 +1173,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _ => unreachable!(), }; - let def_id = self.r.definitions.local_def_id(item.id); + let def_id = self.r.definitions.local_def_id(item.id).to_def_id(); let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id); self.r.macro_map.insert(def_id, ext); self.r.local_macro_def_scopes.insert(item.id, parent_scope.module); @@ -1251,7 +1303,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { } // Add the item to the trait info. - let item_def_id = self.r.definitions.local_def_id(item.id); + let item_def_id = self.r.definitions.local_def_id(item.id).to_def_id(); let (res, ns) = match item.kind { AssocItemKind::Const(..) => (Res::Def(DefKind::AssocConst, item_def_id), ValueNS), AssocItemKind::Fn(_, ref sig, _, _) => { @@ -1353,7 +1405,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { let ident = variant.ident; // Define a name in the type namespace. - let def_id = self.r.definitions.local_def_id(variant.id); + let def_id = self.r.definitions.local_def_id(variant.id).to_def_id(); let res = Res::Def(DefKind::Variant, def_id); self.r.define(parent, ident, TypeNS, (res, vis, variant.span, expn_id)); @@ -1371,7 +1423,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { // It's ok to use the variant's id as a ctor id since an // error will be reported on any use of such resolution anyway. let ctor_node_id = variant.data.ctor_id().unwrap_or(variant.id); - let ctor_def_id = self.r.definitions.local_def_id(ctor_node_id); + let ctor_def_id = self.r.definitions.local_def_id(ctor_node_id).to_def_id(); let ctor_kind = CtorKind::from_ast(&variant.data); let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id)); diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index 35876176e3e4b..dd286723412dd 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -26,12 +26,12 @@ use crate::imports::ImportKind; use crate::Resolver; -use rustc::ty; use rustc_ast::ast; use rustc_ast::node_id::NodeMap; use rustc_ast::visit::{self, Visitor}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; +use rustc_middle::ty; use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::{MultiSpan, Span, DUMMY_SP}; diff --git a/src/librustc_resolve/def_collector.rs b/src/librustc_resolve/def_collector.rs index 6253f183cc444..71cedb208fcf2 100644 --- a/src/librustc_resolve/def_collector.rs +++ b/src/librustc_resolve/def_collector.rs @@ -2,6 +2,7 @@ use log::debug; use rustc_ast::ast::*; use rustc_ast::token::{self, Token}; use rustc_ast::visit::{self, FnKind}; +use rustc_ast::walk_list; use rustc_expand::expand::AstFragment; use rustc_hir::def_id::LocalDefId; use rustc_hir::definitions::*; @@ -117,10 +118,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { // we must mirror everything that `visit::walk_fn` below does. self.visit_fn_header(&sig.header); visit::walk_fn_decl(self, &sig.decl); - if let Some(body) = body { - let closure_def = self.create_def(closure_id, DefPathData::ClosureExpr, span); - self.with_parent(closure_def, |this| this.visit_block(body)); - } + let closure_def = self.create_def(closure_id, DefPathData::ClosureExpr, span); + self.with_parent(closure_def, |this| walk_list!(this, visit_block, body)); return; } } diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 9cd066b629c07..ea237f1a04f99 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -2,9 +2,7 @@ use std::cmp::Reverse; use std::ptr; use log::debug; -use rustc::bug; -use rustc::ty::{self, DefIdTree}; -use rustc_ast::ast::{self, Ident, Path}; +use rustc_ast::ast::{self, Path}; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -13,10 +11,12 @@ use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_middle::bug; +use rustc_middle::ty::{self, DefIdTree}; use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{kw, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{BytePos, MultiSpan, Span}; use crate::imports::{Import, ImportKind, ImportResolver}; @@ -47,6 +47,7 @@ impl TypoSuggestion { /// A free importable items suggested in case of resolution failure. crate struct ImportSuggestion { pub did: Option, + pub descr: &'static str, pub path: Path, } @@ -300,13 +301,40 @@ impl<'a> Resolver<'a> { } err } - ResolutionError::SelfImportsOnlyAllowedWithin => struct_span_err!( - self.session, - span, - E0429, - "{}", - "`self` imports are only allowed within a { } list" - ), + ResolutionError::SelfImportsOnlyAllowedWithin { root, span_with_rename } => { + let mut err = struct_span_err!( + self.session, + span, + E0429, + "{}", + "`self` imports are only allowed within a { } list" + ); + + // None of the suggestions below would help with a case like `use self`. + if !root { + // use foo::bar::self -> foo::bar + // use foo::bar::self as abc -> foo::bar as abc + err.span_suggestion( + span, + "consider importing the module directly", + "".to_string(), + Applicability::MachineApplicable, + ); + + // use foo::bar::self -> foo::bar::{self} + // use foo::bar::self as abc -> foo::bar::{self as abc} + let braces = vec![ + (span_with_rename.shrink_to_lo(), "{".to_string()), + (span_with_rename.shrink_to_hi(), "}".to_string()), + ]; + err.multipart_suggestion( + "alternatively, use the multi-path `use` syntax to import `self`", + braces, + Applicability::MachineApplicable, + ); + } + err + } ResolutionError::SelfImportCanOnlyAppearOnceInTheList => { let mut err = struct_span_err!( self.session, @@ -652,7 +680,7 @@ impl<'a> Resolver<'a> { Res::Def(DefKind::Ctor(..), did) => this.parent(did), _ => res.opt_def_id(), }; - candidates.push(ImportSuggestion { did, path }); + candidates.push(ImportSuggestion { did, descr: res.descr(), path }); } } } @@ -791,12 +819,12 @@ impl<'a> Resolver<'a> { _ => Some( self.session .source_map() - .def_span(self.cstore().get_span_untracked(def_id, self.session)), + .guess_head_span(self.cstore().get_span_untracked(def_id, self.session)), ), }); if let Some(span) = def_span { err.span_label( - self.session.source_map().def_span(span), + self.session.source_map().guess_head_span(span), &format!( "similarly named {} `{}` defined here", suggestion.res.descr(), @@ -986,7 +1014,7 @@ impl<'a> Resolver<'a> { which = if first { "" } else { " which" }, dots = if next_binding.is_some() { "..." } else { "" }, ); - let def_span = self.session.source_map().def_span(binding.span); + let def_span = self.session.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); if !first && binding.vis == ty::Visibility::Public { note_span.push_span_label(def_span, "consider importing it directly".into()); @@ -1031,7 +1059,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// Suggest a missing `self::` if that resolves to an correct module. /// - /// ``` + /// ```text /// | /// LL | use foo::Bar; /// | ^^^ did you mean `self::foo`? @@ -1051,7 +1079,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// Suggests a missing `crate::` if that resolves to an correct module. /// - /// ``` + /// ```text /// | /// LL | use foo::Bar; /// | ^^^ did you mean `crate::foo`? @@ -1083,7 +1111,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// Suggests a missing `super::` if that resolves to an correct module. /// - /// ``` + /// ```text /// | /// LL | use foo::Bar; /// | ^^^ did you mean `super::foo`? @@ -1103,7 +1131,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// Suggests a missing external crate name if that resolves to an correct module. /// - /// ``` + /// ```text /// | /// LL | use foobar::Baz; /// | ^^^^^^ did you mean `baz::foobar`? @@ -1147,7 +1175,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// Suggests importing a macro from the root of the crate rather than a module within /// the crate. /// - /// ``` + /// ```text /// help: a macro with this name exists at the root of the crate /// | /// LL | use issue_59764::makro; @@ -1445,7 +1473,7 @@ fn find_span_immediately_after_crate_name( crate fn show_candidates( err: &mut DiagnosticBuilder<'_>, // This is `None` if all placement locations are inside expansions - span: Option, + use_placement_span: Option, candidates: &[ImportSuggestion], better: bool, found_use: bool, @@ -1453,6 +1481,7 @@ crate fn show_candidates( if candidates.is_empty() { return; } + // we want consistent results across executions, but candidates are produced // by iterating through a hash map, so make sure they are ordered: let mut path_strings: Vec<_> = @@ -1460,14 +1489,15 @@ crate fn show_candidates( path_strings.sort(); path_strings.dedup(); - let better = if better { "better " } else { "" }; - let msg_diff = match path_strings.len() { - 1 => " is found in another module, you can import it", - _ => "s are found in other modules, you can import them", + let (determiner, kind) = if candidates.len() == 1 { + ("this", candidates[0].descr) + } else { + ("one of these", "items") }; - let msg = format!("possible {}candidate{} into scope", better, msg_diff); + let instead = if better { " instead" } else { "" }; + let msg = format!("consider importing {} {}{}", determiner, kind, instead); - if let Some(span) = span { + if let Some(span) = use_placement_span { for candidate in &mut path_strings { // produce an additional newline to separate the new use statement // from the directly following item. diff --git a/src/librustc_resolve/imports.rs b/src/librustc_resolve/imports.rs index d375ae4a447a3..a1e05d21b58d5 100644 --- a/src/librustc_resolve/imports.rs +++ b/src/librustc_resolve/imports.rs @@ -9,10 +9,7 @@ use crate::{BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; use crate::{CrateLint, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak}; use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding}; -use rustc::hir::exports::Export; -use rustc::ty; -use rustc::{bug, span_bug}; -use rustc_ast::ast::{Ident, Name, NodeId}; +use rustc_ast::ast::NodeId; use rustc_ast::unwrap_or; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashSet; @@ -20,11 +17,14 @@ use rustc_data_structures::ptr_key::PtrKey; use rustc_errors::{pluralize, struct_span_err, Applicability}; use rustc_hir::def::{self, PartialRes}; use rustc_hir::def_id::DefId; +use rustc_middle::hir::exports::Export; +use rustc_middle::ty; +use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::DiagnosticMessageId; use rustc_span::hygiene::ExpnId; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{MultiSpan, Span}; use log::*; @@ -57,7 +57,7 @@ pub enum ImportKind<'a> { // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. }, ExternCrate { - source: Option, + source: Option, target: Ident, }, MacroUse, @@ -1441,7 +1441,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let enum_resolution = resolutions.get(&key).expect("resolution should exist"); let enum_span = enum_resolution.borrow().binding.expect("binding should exist").span; - let enum_def_span = this.session.source_map().def_span(enum_span); + let enum_def_span = this.session.source_map().guess_head_span(enum_span); let enum_def_snippet = this .session .source_map() diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 4a3c9f338d903..e541920e89ed4 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -11,7 +11,6 @@ use crate::{path_names_to_string, BindingError, CrateLint, LexicalScopeBinding}; use crate::{Module, ModuleOrUniformRoot, NameBindingKind, ParentScope, PathResult}; use crate::{ResolutionError, Resolver, Segment, UseError}; -use rustc::{bug, span_bug}; use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::util::lev_distance::find_best_match_for_name; @@ -23,8 +22,9 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::TraitCandidate; +use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use smallvec::{smallvec, SmallVec}; @@ -237,18 +237,21 @@ impl<'a> PathSource<'a> { crate fn is_expected(self, res: Res) -> bool { match self { PathSource::Type => match res { - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::Enum, _) - | Res::Def(DefKind::Trait, _) - | Res::Def(DefKind::TraitAlias, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::OpaqueTy + | DefKind::ForeignTy, + _, + ) | Res::PrimTy(..) - | Res::Def(DefKind::TyParam, _) - | Res::SelfTy(..) - | Res::Def(DefKind::OpaqueTy, _) - | Res::Def(DefKind::ForeignTy, _) => true, + | Res::SelfTy(..) => true, _ => false, }, PathSource::Trait(AliasPossibility::No) => match res { @@ -256,27 +259,29 @@ impl<'a> PathSource<'a> { _ => false, }, PathSource::Trait(AliasPossibility::Maybe) => match res { - Res::Def(DefKind::Trait, _) => true, - Res::Def(DefKind::TraitAlias, _) => true, + Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => true, _ => false, }, PathSource::Expr(..) => match res { - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::Static, _) + Res::Def( + DefKind::Ctor(_, CtorKind::Const | CtorKind::Fn) + | DefKind::Const + | DefKind::Static + | DefKind::Fn + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::ConstParam, + _, + ) | Res::Local(..) - | Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::AssocConst, _) - | Res::SelfCtor(..) - | Res::Def(DefKind::ConstParam, _) => true, + | Res::SelfCtor(..) => true, _ => false, }, PathSource::Pat => match res { - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) + Res::Def( + DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst, + _, + ) | Res::SelfCtor(..) => true, _ => false, }, @@ -285,20 +290,19 @@ impl<'a> PathSource<'a> { _ => false, }, PathSource::Struct => match res { - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::Variant, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Variant + | DefKind::TyAlias + | DefKind::AssocTy, + _, + ) | Res::SelfTy(..) => true, _ => false, }, PathSource::TraitItem(ns) => match res { - Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::AssocFn, _) - if ns == ValueNS => - { - true - } + Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, _ => false, }, @@ -316,8 +320,8 @@ impl<'a> PathSource<'a> { (PathSource::Struct, false) => error_code!(E0422), (PathSource::Expr(..), true) => error_code!(E0423), (PathSource::Expr(..), false) => error_code!(E0425), - (PathSource::Pat, true) | (PathSource::TupleStruct, true) => error_code!(E0532), - (PathSource::Pat, false) | (PathSource::TupleStruct, false) => error_code!(E0531), + (PathSource::Pat | PathSource::TupleStruct, true) => error_code!(E0532), + (PathSource::Pat | PathSource::TupleStruct, false) => error_code!(E0531), (PathSource::TraitItem(..), true) => error_code!(E0575), (PathSource::TraitItem(..), false) => error_code!(E0576), } @@ -459,7 +463,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let rib_kind = match fn_kind { // Bail if there's no body. FnKind::Fn(.., None) => return visit::walk_fn(self, fn_kind, sp), - FnKind::Fn(FnCtxt::Free, ..) | FnKind::Fn(FnCtxt::Foreign, ..) => FnItemRibKind, + FnKind::Fn(FnCtxt::Free | FnCtxt::Foreign, ..) => FnItemRibKind, FnKind::Fn(FnCtxt::Assoc(_), ..) | FnKind::Closure(..) => NormalRibKind, }; let previous_value = replace(&mut self.diagnostic_metadata.current_function, Some(sp)); @@ -745,7 +749,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { debug!("resolve_adt"); self.with_current_self_item(item, |this| { this.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { - let item_def_id = this.r.definitions.local_def_id(item.id); + let item_def_id = this.r.definitions.local_def_id(item.id).to_def_id(); this.with_self_rib(Res::SelfTy(None, Some(item_def_id)), |this| { visit::walk_item(this, item); }); @@ -825,7 +829,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ItemKind::Trait(.., ref generics, ref bounds, ref trait_items) => { // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { - let local_def_id = this.r.definitions.local_def_id(item.id); + let local_def_id = this.r.definitions.local_def_id(item.id).to_def_id(); this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| { this.visit_generics(generics); walk_list!(this, visit_param_bound, bounds); @@ -866,7 +870,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ItemKind::TraitAlias(ref generics, ref bounds) => { // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { - let local_def_id = this.r.definitions.local_def_id(item.id); + let local_def_id = this.r.definitions.local_def_id(item.id).to_def_id(); this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| { this.visit_generics(generics); walk_list!(this, visit_param_bound, bounds); @@ -947,7 +951,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { seen_bindings.entry(ident).or_insert(param.ident.span); // Plain insert (no renaming). - let res = Res::Def(def_kind, self.r.definitions.local_def_id(param.id)); + let res = Res::Def(def_kind, self.r.definitions.local_def_id(param.id).to_def_id()); match param.kind { GenericParamKind::Type { .. } => { @@ -1097,7 +1101,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_self_rib(Res::SelfTy(None, None), |this| { // Resolve the trait reference, if necessary. this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| { - let item_def_id = this.r.definitions.local_def_id(item_id); + let item_def_id = this.r.definitions.local_def_id(item_id).to_def_id(); this.with_self_rib(Res::SelfTy(trait_id, Some(item_def_id)), |this| { if let Some(trait_ref) = opt_trait_reference.as_ref() { // Resolve type arguments in the trait path. @@ -1190,7 +1194,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn check_trait_item(&mut self, ident: Ident, ns: Namespace, span: Span, err: F) where - F: FnOnce(Name, &str) -> ResolutionError<'_>, + F: FnOnce(Symbol, &str) -> ResolutionError<'_>, { // If there is a TraitRef in scope for an impl, then the method must be in the // trait. @@ -1518,11 +1522,20 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ident: Ident, has_sub: bool, ) -> Option { + // An immutable (no `mut`) by-value (no `ref`) binding pattern without + // a sub pattern (no `@ $pat`) is syntactically ambiguous as it could + // also be interpreted as a path to e.g. a constant, variant, etc. + let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Not); + let ls_binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, pat.span)?; let (res, binding) = match ls_binding { - LexicalScopeBinding::Item(binding) if binding.is_ambiguity() => { + LexicalScopeBinding::Item(binding) + if is_syntactic_ambiguity && binding.is_ambiguity() => + { // For ambiguous bindings we don't know all their definitions and cannot check // whether they can be shadowed by fresh bindings or not, so force an error. + // issues/33118#issuecomment-233962221 (see below) still applies here, + // but we have to ignore it for backward compatibility. self.r.record_use(ident, ValueNS, binding, false); return None; } @@ -1530,26 +1543,19 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { LexicalScopeBinding::Res(res) => (res, None), }; - // An immutable (no `mut`) by-value (no `ref`) binding pattern without - // a sub pattern (no `@ $pat`) is syntactically ambiguous as it could - // also be interpreted as a path to e.g. a constant, variant, etc. - let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Not); - match res { - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::ConstParam, _) - if is_syntactic_ambiguity => - { + Res::SelfCtor(_) // See #70549. + | Res::Def( + DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::ConstParam, + _, + ) if is_syntactic_ambiguity => { // Disambiguate in favor of a unit struct/variant or constant pattern. if let Some(binding) = binding { self.r.record_use(ident, ValueNS, binding, false); } Some(res) } - Res::Def(DefKind::Ctor(..), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::Static, _) => { + Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static, _) => { // This is unambiguously a fresh binding, either syntactically // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves // to something unusable as a pattern (e.g., constructor function), @@ -1572,7 +1578,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { _ => span_bug!( ident.span, "unexpected resolution for an identifier in pattern: {:?}", - res + res, ), } } @@ -1908,7 +1914,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if let StmtKind::Item(ref item) = stmt.kind { if let ItemKind::MacroDef(..) = item.kind { num_macro_definition_ribs += 1; - let res = self.r.definitions.local_def_id(item.id); + let res = self.r.definitions.local_def_id(item.id).to_def_id(); self.ribs[ValueNS].push(Rib::new(MacroDefinition(res))); self.label_ribs.push(Rib::new(MacroDefinition(res))); } @@ -1994,7 +2000,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.visit_expr(cond); this.visit_block(then); }); - opt_else.as_ref().map(|expr| self.visit_expr(expr)); + if let Some(expr) = opt_else { + self.visit_expr(expr); + } } ExprKind::Loop(ref block, label) => self.resolve_labeled_block(label, expr.id, &block), @@ -2149,7 +2157,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { return; } match binding.res() { - Res::Def(DefKind::Trait, _) | Res::Def(DefKind::TraitAlias, _) => { + Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => { collected_traits.push((name, binding)) } _ => (), diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index a8d4e7ce5f6ba..dc92b465c2bc1 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -5,7 +5,7 @@ use crate::path_names_to_string; use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; -use rustc_ast::ast::{self, Expr, ExprKind, Ident, Item, ItemKind, NodeId, Path, Ty, TyKind}; +use rustc_ast::ast::{self, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind}; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; @@ -16,7 +16,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::PrimTy; use rustc_session::config::nightly_options; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use log::debug; @@ -123,10 +123,10 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { .map(|snippet| snippet.ends_with(')')) .unwrap_or(false) } - Res::Def(DefKind::Ctor(..), _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) + Res::Def( + DefKind::Ctor(..) | DefKind::AssocFn | DefKind::Const | DefKind::AssocConst, + _, + ) | Res::SelfCtor(_) | Res::PrimTy(_) | Res::Local(_) => true, @@ -151,7 +151,11 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { }; ( format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), - format!("not found in {}", mod_str), + if path_str == "async" && expected.starts_with("struct") { + "`async` blocks are only allowed in the 2018 edition".to_string() + } else { + format!("not found in {}", mod_str) + }, item_span, false, ) @@ -383,7 +387,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { has_self_arg } - fn followed_by_brace(&self, span: Span) -> (bool, Option<(Span, String)>) { + fn followed_by_brace(&self, span: Span) -> (bool, Option) { // HACK(estebank): find a better way to figure out that this was a // parser issue where a struct literal is being used on an expression // where a brace being opened means a block is being started. Look @@ -406,7 +410,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { _ => false, }; // In case this could be a struct literal that needs to be surrounded - // by parenthesis, find the appropriate span. + // by parentheses, find the appropriate span. let mut i = 0; let mut closing_brace = None; loop { @@ -414,10 +418,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { match sm.span_to_snippet(sp) { Ok(ref snippet) => { if snippet == "}" { - let sp = span.to(sp); - if let Ok(snippet) = sm.span_to_snippet(sp) { - closing_brace = Some((sp, snippet)); - } + closing_brace = Some(span.to(sp)); break; } } @@ -479,17 +480,23 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { suggested = path_sep(err, &parent); } PathSource::Expr(None) if followed_by_brace => { - if let Some((sp, snippet)) = closing_brace { - err.span_suggestion( - sp, - "surround the struct literal with parenthesis", - format!("({})", snippet), + if let Some(sp) = closing_brace { + err.multipart_suggestion( + "surround the struct literal with parentheses", + vec![ + (sp.shrink_to_lo(), "(".to_string()), + (sp.shrink_to_hi(), ")".to_string()), + ], Applicability::MaybeIncorrect, ); } else { err.span_label( - span, // Note the parenthesis surrounding the suggestion below - format!("did you mean `({} {{ /* fields */ }})`?", path_str), + span, // Note the parentheses surrounding the suggestion below + format!( + "you might want to surround a struct literal with parentheses: \ + `({} {{ /* fields */ }})`?", + path_str + ), ); } suggested = true; @@ -516,10 +523,16 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { err.note("if you want the `try` keyword, you need to be in the 2018 edition"); } } - (Res::Def(DefKind::TyAlias, _), PathSource::Trait(_)) => { + (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => { err.span_label(span, "type aliases cannot be used as traits"); if nightly_options::is_nightly_build() { - err.note("did you mean to use a trait alias?"); + let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \ + `type` alias"; + if let Some(span) = self.r.definitions.opt_span(def_id) { + err.span_help(span, msg); + } else { + err.help(msg); + } } } (Res::Def(DefKind::Mod, _), PathSource::Expr(Some(parent))) => { @@ -527,8 +540,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { return false; } } - (Res::Def(DefKind::Enum, def_id), PathSource::TupleStruct) - | (Res::Def(DefKind::Enum, def_id), PathSource::Expr(..)) => { + (Res::Def(DefKind::Enum, def_id), PathSource::TupleStruct | PathSource::Expr(..)) => { if let Some(variants) = self.collect_enum_variants(def_id) { if !variants.is_empty() { let msg = if variants.len() == 1 { @@ -563,11 +575,13 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { bad_struct_syntax_suggestion(def_id); } } - (Res::Def(DefKind::Union, def_id), _) - | (Res::Def(DefKind::Variant, def_id), _) - | (Res::Def(DefKind::Ctor(_, CtorKind::Fictive), def_id), _) - if ns == ValueNS => - { + ( + Res::Def( + DefKind::Union | DefKind::Variant | DefKind::Ctor(_, CtorKind::Fictive), + def_id, + ), + _, + ) if ns == ValueNS => { bad_struct_syntax_suggestion(def_id); } (Res::Def(DefKind::Ctor(_, CtorKind::Fn), def_id), _) if ns == ValueNS => { @@ -580,9 +594,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { err.span_label(span, fallback_label); err.note("can't use `Self` as a constructor, you must use the implemented struct"); } - (Res::Def(DefKind::TyAlias, _), _) | (Res::Def(DefKind::AssocTy, _), _) - if ns == ValueNS => - { + (Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => { err.note("can't use a type alias as a constructor"); } _ => return false, @@ -618,7 +630,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { // Look for a field with the same name in the current self_type. if let Some(resolution) = self.r.partial_res_map.get(&node_id) { match resolution.base_res() { - Res::Def(DefKind::Struct, did) | Res::Def(DefKind::Union, did) + Res::Def(DefKind::Struct | DefKind::Union, did) if resolution.unresolved_segments() == 0 => { if let Some(field_names) = self.r.field_names.get(&did) { @@ -865,7 +877,10 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { let module_def_id = module.def_id().unwrap(); if module_def_id == def_id { let path = Path { span: name_binding.span, segments: path_segments }; - result = Some((module, ImportSuggestion { did: Some(def_id), path })); + result = Some(( + module, + ImportSuggestion { did: Some(def_id), descr: "module", path }, + )); } else { // add the module to the lookup if seen_modules.insert(module_def_id) { @@ -1032,104 +1047,128 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { err: &mut DiagnosticBuilder<'_>, span: Span, count: usize, - lifetime_names: &FxHashSet, + lifetime_names: &FxHashSet, params: &[ElisionFailureInfo], ) { - if count > 1 { - err.span_label(span, format!("expected {} lifetime parameters", count)); - } else { - let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok(); - let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| { - err.span_suggestion( - span, - "consider using the named lifetime", - sugg, - Applicability::MaybeIncorrect, - ); - }; - let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| { - err.span_label(span, "expected named lifetime parameter"); + let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok(); - for missing in self.missing_named_lifetime_spots.iter().rev() { - let mut introduce_suggestion = vec![]; - let msg; - let should_break; - introduce_suggestion.push(match missing { - MissingLifetimeSpot::Generics(generics) => { - msg = "consider introducing a named lifetime parameter".to_string(); - should_break = true; - if let Some(param) = generics.params.iter().find(|p| match p.kind { - hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => false, - _ => true, - }) { - (param.span.shrink_to_lo(), "'a, ".to_string()) - } else { - (generics.span, "<'a>".to_string()) - } - } - MissingLifetimeSpot::HigherRanked { span, span_type } => { - msg = format!( - "consider making the {} lifetime-generic with a new `'a` lifetime", - span_type.descr(), - ); - should_break = false; - err.note( - "for more information on higher-ranked polymorphism, visit \ - https://doc.rust-lang.org/nomicon/hrtb.html", - ); - (*span, span_type.suggestion("'a")) - } - }); - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) - { - if snippet.starts_with('&') && !snippet.starts_with("&'") { - introduce_suggestion - .push((param.span, format!("&'a {}", &snippet[1..]))); - } else if snippet.starts_with("&'_ ") { - introduce_suggestion - .push((param.span, format!("&'a {}", &snippet[4..]))); - } + err.span_label( + span, + &format!( + "expected {} lifetime parameter{}", + if count == 1 { "named".to_string() } else { count.to_string() }, + pluralize!(count) + ), + ); + + let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| { + err.span_suggestion_verbose( + span, + &format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()), + sugg, + Applicability::MaybeIncorrect, + ); + }; + let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| { + for missing in self.missing_named_lifetime_spots.iter().rev() { + let mut introduce_suggestion = vec![]; + let msg; + let should_break; + introduce_suggestion.push(match missing { + MissingLifetimeSpot::Generics(generics) => { + msg = "consider introducing a named lifetime parameter".to_string(); + should_break = true; + if let Some(param) = generics.params.iter().find(|p| match p.kind { + hir::GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } => false, + _ => true, + }) { + (param.span.shrink_to_lo(), "'a, ".to_string()) + } else { + (generics.span, "<'a>".to_string()) } } - introduce_suggestion.push((span, sugg.to_string())); - err.multipart_suggestion( - &msg, - introduce_suggestion, - Applicability::MaybeIncorrect, - ); - if should_break { - break; + MissingLifetimeSpot::HigherRanked { span, span_type } => { + msg = format!( + "consider making the {} lifetime-generic with a new `'a` lifetime", + span_type.descr(), + ); + should_break = false; + err.note( + "for more information on higher-ranked polymorphism, visit \ + https://doc.rust-lang.org/nomicon/hrtb.html", + ); + (*span, span_type.suggestion("'a")) + } + }); + for param in params { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { + if snippet.starts_with('&') && !snippet.starts_with("&'") { + introduce_suggestion + .push((param.span, format!("&'a {}", &snippet[1..]))); + } else if snippet.starts_with("&'_ ") { + introduce_suggestion + .push((param.span, format!("&'a {}", &snippet[4..]))); + } } } - }; - - match (lifetime_names.len(), lifetime_names.iter().next(), snippet.as_deref()) { - (1, Some(name), Some("&")) => { - suggest_existing(err, format!("&{} ", name)); - } - (1, Some(name), Some("'_")) => { - suggest_existing(err, name.to_string()); - } - (1, Some(name), Some(snippet)) if !snippet.ends_with('>') => { - suggest_existing(err, format!("{}<{}>", snippet, name)); - } - (0, _, Some("&")) => { - suggest_new(err, "&'a "); - } - (0, _, Some("'_")) => { - suggest_new(err, "'a"); - } - (0, _, Some(snippet)) if !snippet.ends_with('>') => { - suggest_new(err, &format!("{}<'a>", snippet)); + introduce_suggestion.push((span, sugg.to_string())); + err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect); + if should_break { + break; } - _ => { - err.span_label(span, "expected lifetime parameter"); + } + }; + + match (lifetime_names.len(), lifetime_names.iter().next(), snippet.as_deref()) { + (1, Some(name), Some("&")) => { + suggest_existing(err, format!("&{} ", name)); + } + (1, Some(name), Some("'_")) => { + suggest_existing(err, name.to_string()); + } + (1, Some(name), Some("")) => { + suggest_existing(err, format!("{}, ", name).repeat(count)); + } + (1, Some(name), Some(snippet)) if !snippet.ends_with('>') => { + suggest_existing( + err, + format!( + "{}<{}>", + snippet, + std::iter::repeat(name.to_string()) + .take(count) + .collect::>() + .join(", ") + ), + ); + } + (0, _, Some("&")) if count == 1 => { + suggest_new(err, "&'a "); + } + (0, _, Some("'_")) if count == 1 => { + suggest_new(err, "'a"); + } + (0, _, Some(snippet)) if !snippet.ends_with('>') && count == 1 => { + suggest_new(err, &format!("{}<'a>", snippet)); + } + (n, ..) if n > 1 => { + let spans: Vec = lifetime_names.iter().map(|lt| lt.span).collect(); + err.span_note(spans, "these named lifetimes are available to use"); + if Some("") == snippet.as_deref() { + // This happens when we have `Foo` where we point at the space before `T`, + // but this can be confusing so we give a suggestion with placeholders. + err.span_suggestion_verbose( + span, + "consider using one of the available lifetimes here", + "'lifetime, ".repeat(count), + Applicability::HasPlaceholders, + ); } } + _ => {} } } } diff --git a/src/librustc_resolve/late/lifetimes.rs b/src/librustc_resolve/late/lifetimes.rs index bc843fccc4c71..a3fbb28f22a56 100644 --- a/src/librustc_resolve/late/lifetimes.rs +++ b/src/librustc_resolve/late/lifetimes.rs @@ -6,11 +6,6 @@ //! way. Therefore, we break lifetime name resolution into a separate pass. use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot}; -use rustc::hir::map::Map; -use rustc::middle::resolve_lifetime::*; -use rustc::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; -use rustc::{bug, span_bug}; -use rustc_ast::ast; use rustc_ast::attr; use rustc_ast::walk_list; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -21,12 +16,16 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet, LifetimeParamKind}; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::resolve_lifetime::*; +use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::borrow::Cow; use std::cell::Cell; -use std::mem::{replace, take}; +use std::mem::take; use log::debug; @@ -62,7 +61,7 @@ impl RegionExt for Region { let def_id = hir_map.local_def_id(param.hir_id); let origin = LifetimeDefOrigin::from_param(param); debug!("Region::early: index={} def_id={:?}", i, def_id); - (param.name.normalize_to_macros_2_0(), Region::EarlyBound(i, def_id, origin)) + (param.name.normalize_to_macros_2_0(), Region::EarlyBound(i, def_id.to_def_id(), origin)) } fn late(hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region) { @@ -73,7 +72,7 @@ impl RegionExt for Region { "Region::late: param={:?} depth={:?} def_id={:?} origin={:?}", param, depth, def_id, origin, ); - (param.name.normalize_to_macros_2_0(), Region::LateBound(depth, def_id, origin)) + (param.name.normalize_to_macros_2_0(), Region::LateBound(depth, def_id.to_def_id(), origin)) } fn late_anon(index: &Cell) -> Region { @@ -175,7 +174,7 @@ crate struct LifetimeContext<'a, 'tcx> { is_in_fn_syntax: bool, /// List of labels in the function/method currently under analysis. - labels_in_fn: Vec, + labels_in_fn: Vec, /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap>, @@ -294,7 +293,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { /// entire crate. You should not read the result of this query /// directly, but rather use `named_region_map`, `is_late_bound_map`, /// etc. -fn resolve_lifetimes(tcx: TyCtxt<'_>, for_krate: CrateNum) -> &ResolveLifetimes { +fn resolve_lifetimes(tcx: TyCtxt<'_>, for_krate: CrateNum) -> ResolveLifetimes { assert_eq!(for_krate, LOCAL_CRATE); let named_region_map = krate(tcx); @@ -314,7 +313,7 @@ fn resolve_lifetimes(tcx: TyCtxt<'_>, for_krate: CrateNum) -> &ResolveLifetimes map.insert(hir_id.local_id, v); } - tcx.arena.alloc(rl) + rl } fn krate(tcx: TyCtxt<'_>) -> NamedRegionMap { @@ -371,7 +370,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.with(Scope::Body { id: body.id(), s: self.scope }, |_, this| { this.visit_body(body); }); - replace(&mut self.labels_in_fn, saved); + self.labels_in_fn = saved; } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -480,14 +479,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let next_early_index = self.next_early_index(); let was_in_fn_syntax = self.is_in_fn_syntax; self.is_in_fn_syntax = true; - let lifetime_span: Option = c - .generic_params - .iter() - .filter_map(|param| match param.kind { + let lifetime_span: Option = + c.generic_params.iter().rev().find_map(|param| match param.kind { GenericParamKind::Lifetime { .. } => Some(param.span), _ => None, - }) - .last(); + }); let (span, span_type) = if let Some(span) = lifetime_span { (span.shrink_to_hi(), ForLifetimeSpanType::TypeTail) } else { @@ -596,7 +592,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // In the future, this should be fixed and this error should be removed. let def = self.map.defs.get(&lifetime.hir_id).cloned(); if let Some(Region::LateBound(_, def_id, _)) = def { - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx.hir().as_local_hir_id(def_id); // Ensure that the parent of the def is an item, not HRTB let parent_id = self.tcx.hir().get_parent_node(hir_id); let parent_impl_id = hir::ImplItemId { hir_id: parent_id }; @@ -643,13 +640,13 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { if param_name.name == kw::UnderscoreLifetime { // Pick the elided lifetime "definition" if one exists // and use it to make an elision scope. - self.lifetime_uses.insert(def_id.clone(), LifetimeUseSet::Many); + self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); elision = Some(reg); } else { lifetimes.insert(name, reg); } } else { - self.lifetime_uses.insert(def_id.clone(), LifetimeUseSet::Many); + self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); lifetimes.insert(name, reg); } } @@ -1066,7 +1063,7 @@ fn check_mixed_explicit_and_in_band_defs(tcx: TyCtxt<'_>, params: &[hir::Generic } } -fn signal_shadowing_problem(tcx: TyCtxt<'_>, name: ast::Name, orig: Original, shadower: Shadower) { +fn signal_shadowing_problem(tcx: TyCtxt<'_>, name: Symbol, orig: Original, shadower: Shadower) { let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) { // lifetime/lifetime shadowing is an error struct_span_err!( @@ -1104,7 +1101,7 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { struct GatherLabels<'a, 'tcx> { tcx: TyCtxt<'tcx>, scope: ScopeRef<'a>, - labels_in_fn: &'a mut Vec, + labels_in_fn: &'a mut Vec, } let mut gather = @@ -1140,15 +1137,11 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { } } - fn expression_label(ex: &hir::Expr<'_>) -> Option { + fn expression_label(ex: &hir::Expr<'_>) -> Option { if let hir::ExprKind::Loop(_, Some(label), _) = ex.kind { Some(label.ident) } else { None } } - fn check_if_label_shadows_lifetime( - tcx: TyCtxt<'_>, - mut scope: ScopeRef<'_>, - label: ast::Ident, - ) { + fn check_if_label_shadows_lifetime(tcx: TyCtxt<'_>, mut scope: ScopeRef<'_>, label: Ident) { loop { match *scope { Scope::Body { s, .. } @@ -1166,7 +1159,7 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { if let Some(def) = lifetimes.get(&hir::ParamName::Plain(label.normalize_to_macros_2_0())) { - let hir_id = tcx.hir().as_local_hir_id(def.id().unwrap()).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def.id().unwrap().expect_local()); signal_shadowing_problem( tcx, @@ -1278,7 +1271,7 @@ fn object_lifetime_defaults_for_item( _ => continue, }; - if res == Res::Def(DefKind::TyParam, param_def_id) { + if res == Res::Def(DefKind::TyParam, param_def_id.to_def_id()) { add_bounds(&mut set, &data.bounds); } } @@ -1304,7 +1297,11 @@ fn object_lifetime_defaults_for_item( .find(|&(_, (_, lt_name, _))| lt_name == name) .map_or(Set1::Many, |(i, (id, _, origin))| { let def_id = tcx.hir().local_def_id(id); - Set1::One(Region::EarlyBound(i as u32, def_id, origin)) + Set1::One(Region::EarlyBound( + i as u32, + def_id.to_def_id(), + origin, + )) }) } } @@ -1358,11 +1355,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// helper method to determine the span to remove when suggesting the /// deletion of a lifetime - fn lifetime_deletion_span( - &self, - name: ast::Ident, - generics: &hir::Generics<'_>, - ) -> Option { + fn lifetime_deletion_span(&self, name: Ident, generics: &hir::Generics<'_>) -> Option { generics.params.iter().enumerate().find_map(|(i, param)| { if param.name.ident() == name { let mut in_band = false; @@ -1533,7 +1526,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { match lifetimeuseset { Some(LifetimeUseSet::One(lifetime)) => { - let hir_id = self.tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = self.tcx.hir().as_local_hir_id(def_id.expect_local()); debug!("hir id first={:?}", hir_id); if let Some((id, span, name)) = match self.tcx.hir().get(hir_id) { Node::Lifetime(hir_lifetime) => Some(( @@ -1552,9 +1545,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } if let Some(parent_def_id) = self.tcx.parent(def_id) { - if let Some(parent_hir_id) = - self.tcx.hir().as_local_hir_id(parent_def_id) - { + if let Some(def_id) = parent_def_id.as_local() { + let parent_hir_id = self.tcx.hir().as_local_hir_id(def_id); // lifetimes in `derive` expansions don't count (Issue #53738) if self .tcx @@ -1596,7 +1588,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { debug!("not one use lifetime"); } None => { - let hir_id = self.tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = self.tcx.hir().as_local_hir_id(def_id.expect_local()); if let Some((id, span, name)) = match self.tcx.hir().get(hir_id) { Node::Lifetime(hir_lifetime) => Some(( hir_lifetime.hir_id, @@ -1812,7 +1804,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => { let scope = self.tcx.hir().local_def_id(fn_id); - def = Region::Free(scope, def.id().unwrap()); + def = Region::Free(scope.to_def_id(), def.id().unwrap()); } _ => {} } @@ -1835,10 +1827,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } Region::Static - | Region::EarlyBound(_, _, LifetimeDefOrigin::ExplicitOrElided) - | Region::LateBound(_, _, LifetimeDefOrigin::ExplicitOrElided) - | Region::EarlyBound(_, _, LifetimeDefOrigin::Error) - | Region::LateBound(_, _, LifetimeDefOrigin::Error) + | Region::EarlyBound( + _, + _, + LifetimeDefOrigin::ExplicitOrElided | LifetimeDefOrigin::Error, + ) + | Region::LateBound( + _, + _, + LifetimeDefOrigin::ExplicitOrElided | LifetimeDefOrigin::Error, + ) | Region::LateBoundAnon(..) | Region::Free(..) => {} } @@ -1898,15 +1896,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let type_def_id = match res { Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(parent_def_id(self, def_id)), Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(parent_def_id(self, def_id)), - Res::Def(DefKind::Struct, def_id) - | Res::Def(DefKind::Union, def_id) - | Res::Def(DefKind::Enum, def_id) - | Res::Def(DefKind::TyAlias, def_id) - | Res::Def(DefKind::Trait, def_id) - if depth == 0 => - { - Some(def_id) - } + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::TyAlias + | DefKind::Trait, + def_id, + ) if depth == 0 => Some(def_id), _ => None, }; @@ -1946,7 +1943,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }; let map = &self.map; - let unsubst = if let Some(id) = self.tcx.hir().as_local_hir_id(def_id) { + let unsubst = if let Some(def_id) = def_id.as_local() { + let id = self.tcx.hir().as_local_hir_id(def_id); &map.object_lifetime_defaults[&id] } else { let tcx = self.tcx; @@ -2117,7 +2115,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }; let has_self = match assoc_item_kind { - Some(hir::AssocItemKind::Method { has_self }) => has_self, + Some(hir::AssocItemKind::Fn { has_self }) => has_self, _ => false, }; @@ -2149,9 +2147,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // Whitelist the types that unambiguously always // result in the same type constructor being used // (it can't differ between `Self` and `self`). - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::Enum, _) + Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _) | Res::PrimTy(_) => return res == path.res, _ => {} } @@ -2385,52 +2381,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }; let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len()); - let mut add_label = true; if let Some(params) = error { - if lifetime_refs.len() == 1 { - add_label = add_label && self.report_elision_failure(&mut err, params, span); + // If there's no lifetime available, suggest `'static`. + if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() { + lifetime_names.insert(Ident::from_str("'static")); } } - if add_label { - self.add_missing_lifetime_specifiers_label( - &mut err, - span, - lifetime_refs.len(), - &lifetime_names, - error.map(|p| &p[..]).unwrap_or(&[]), - ); - } - + self.add_missing_lifetime_specifiers_label( + &mut err, + span, + lifetime_refs.len(), + &lifetime_names, + error.map(|p| &p[..]).unwrap_or(&[]), + ); err.emit(); } - fn suggest_lifetime(&self, db: &mut DiagnosticBuilder<'_>, span: Span, msg: &str) -> bool { - match self.tcx.sess.source_map().span_to_snippet(span) { - Ok(ref snippet) => { - let (sugg, applicability) = if snippet == "&" { - ("&'static ".to_owned(), Applicability::MachineApplicable) - } else if snippet == "'_" { - ("'static".to_owned(), Applicability::MachineApplicable) - } else { - (format!("{} + 'static", snippet), Applicability::MaybeIncorrect) - }; - db.span_suggestion(span, msg, sugg, applicability); - false - } - Err(_) => { - db.help(msg); - true - } - } - } - fn report_elision_failure( &mut self, db: &mut DiagnosticBuilder<'_>, params: &[ElisionFailureInfo], - span: Span, - ) -> bool { + ) -> bool /* add `'static` lifetime to lifetime list */ { let mut m = String::new(); let len = params.len(); @@ -2479,29 +2451,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { "this function's return type contains a borrowed value, \ but there is no value for it to be borrowed from", ); - self.suggest_lifetime(db, span, "consider giving it a 'static lifetime") + true } else if elided_len == 0 { db.help( "this function's return type contains a borrowed value with \ an elided lifetime, but the lifetime cannot be derived from \ the arguments", ); - let msg = "consider giving it an explicit bounded or 'static lifetime"; - self.suggest_lifetime(db, span, msg) + true } else if elided_len == 1 { db.help(&format!( "this function's return type contains a borrowed value, \ but the signature does not say which {} it is borrowed from", m )); - true + false } else { db.help(&format!( "this function's return type contains a borrowed value, \ but the signature does not say whether it is borrowed from {}", m )); - true + false } } @@ -2654,7 +2625,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Binder { ref lifetimes, s, .. } => { if let Some(&def) = lifetimes.get(¶m.name.normalize_to_macros_2_0()) { - let hir_id = self.tcx.hir().as_local_hir_id(def.id().unwrap()).unwrap(); + let hir_id = + self.tcx.hir().as_local_hir_id(def.id().unwrap().expect_local()); signal_shadowing_problem( self.tcx, @@ -2704,14 +2676,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { - if lifetime_ref.hir_id == hir::DUMMY_HIR_ID { - span_bug!( - lifetime_ref.span, - "lifetime reference not renumbered, \ - probably a bug in rustc_ast::fold" - ); - } - debug!( "insert_lifetime: {} resolved to {:?} span={:?}", self.tcx.hir().node_to_string(lifetime_ref.hir_id), @@ -2852,8 +2816,9 @@ fn insert_late_bound_lifetimes( fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(Some(_), _)) - | hir::TyKind::Path(hir::QPath::TypeRelative(..)) => { + hir::TyKind::Path( + hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..), + ) => { // ignore lifetimes appearing in associated type // projections, as they are not *constrained* // (defined above) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5b112677cf77f..bfb7f081fc333 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -10,19 +10,14 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] pub use rustc_hir::def::{Namespace, PerNS}; use Determinacy::*; -use rustc::hir::exports::ExportMap; -use rustc::hir::map::{DefKey, Definitions}; -use rustc::middle::cstore::{CrateStore, MetadataLoaderDyn}; -use rustc::span_bug; -use rustc::ty::query::Providers; -use rustc::ty::{self, DefIdTree, ResolverOutputs}; -use rustc_ast::ast::{self, FloatTy, Ident, IntTy, Name, NodeId, UintTy}; +use rustc_ast::ast::{self, FloatTy, IntTy, NodeId, UintTy}; use rustc_ast::ast::{Crate, CRATE_NODE_ID}; use rustc_ast::ast::{ItemKind, Path}; use rustc_ast::attr; @@ -37,16 +32,22 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_expand::base::SyntaxExtension; use rustc_hir::def::Namespace::*; use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::definitions::{DefKey, Definitions}; use rustc_hir::PrimTy::{self, Bool, Char, Float, Int, Str, Uint}; use rustc_hir::{GlobMap, TraitMap}; use rustc_metadata::creader::{CStore, CrateLoader}; +use rustc_middle::hir::exports::ExportMap; +use rustc_middle::middle::cstore::{CrateStore, MetadataLoaderDyn}; +use rustc_middle::span_bug; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, DefIdTree, ResolverOutputs}; use rustc_session::lint; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; use rustc_span::hygiene::{ExpnId, ExpnKind, MacroKind, SyntaxContext, Transparency}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use log::debug; @@ -146,7 +147,7 @@ impl<'a> ParentScope<'a> { #[derive(Eq)] struct BindingError { - name: Name, + name: Symbol, origin: BTreeSet, target: BTreeSet, could_be_path: bool, @@ -175,25 +176,25 @@ enum ResolutionError<'a> { GenericParamsFromOuterFunction(Res, HasGenericParams), /// Error E0403: the name is already used for a type or const parameter in this generic /// parameter list. - NameAlreadyUsedInParameterList(Name, Span), + NameAlreadyUsedInParameterList(Symbol, Span), /// Error E0407: method is not a member of trait. - MethodNotMemberOfTrait(Name, &'a str), + MethodNotMemberOfTrait(Symbol, &'a str), /// Error E0437: type is not a member of trait. - TypeNotMemberOfTrait(Name, &'a str), + TypeNotMemberOfTrait(Symbol, &'a str), /// Error E0438: const is not a member of trait. - ConstNotMemberOfTrait(Name, &'a str), + ConstNotMemberOfTrait(Symbol, &'a str), /// Error E0408: variable `{}` is not bound in all patterns. VariableNotBoundInPattern(&'a BindingError), /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm. - VariableBoundWithDifferentMode(Name, Span), + VariableBoundWithDifferentMode(Symbol, Span), /// Error E0415: identifier is bound more than once in this parameter list. IdentifierBoundMoreThanOnceInParameterList(&'a str), /// Error E0416: identifier is bound more than once in the same pattern. IdentifierBoundMoreThanOnceInSamePattern(&'a str), /// Error E0426: use of undeclared label. - UndeclaredLabel(&'a str, Option), + UndeclaredLabel(&'a str, Option), /// Error E0429: `self` imports are only allowed within a `{ }` list. - SelfImportsOnlyAllowedWithin, + SelfImportsOnlyAllowedWithin { root: bool, span_with_rename: Span }, /// Error E0430: `self` import can only appear once in the list. SelfImportCanOnlyAppearOnceInTheList, /// Error E0431: `self` import can only appear in an import list with a non-empty prefix. @@ -205,7 +206,7 @@ enum ResolutionError<'a> { /// Error E0435: attempt to use a non-constant value in a constant. AttemptToUseNonConstantValueInConstant, /// Error E0530: `X` bindings cannot shadow `Y`s. - BindingShadowsSomethingUnacceptable(&'a str, Name, &'a NameBinding<'a>), + BindingShadowsSomethingUnacceptable(&'a str, Symbol, &'a NameBinding<'a>), /// Error E0128: type parameters with a default cannot use forward-declared identifiers. ForwardDeclaredTyParam, // FIXME(const_generics:defaults) /// Error E0735: type parameters with a default cannot use `Self` @@ -405,12 +406,12 @@ enum ModuleKind { /// * A normal module ‒ either `mod from_file;` or `mod from_block { }`. /// * A trait or an enum (it implicitly contains associated types, methods and variant /// constructors). - Def(DefKind, DefId, Name), + Def(DefKind, DefId, Symbol), } impl ModuleKind { /// Get name of the module. - pub fn name(&self) -> Option { + pub fn name(&self) -> Option { match self { ModuleKind::Block(..) => None, ModuleKind::Def(.., name) => Some(*name), @@ -498,7 +499,9 @@ impl<'a> ModuleData<'a> { F: FnMut(&mut R, Ident, Namespace, &'a NameBinding<'a>), { for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() { - name_resolution.borrow().binding.map(|binding| f(resolver, key.ident, key.ns, binding)); + if let Some(binding) = name_resolution.borrow().binding { + f(resolver, key.ident, key.ns, binding); + } } } @@ -533,7 +536,7 @@ impl<'a> ModuleData<'a> { fn nearest_item_scope(&'a self) -> Module<'a> { match self.kind { - ModuleKind::Def(DefKind::Enum, ..) | ModuleKind::Def(DefKind::Trait, ..) => { + ModuleKind::Def(DefKind::Enum | DefKind::Trait, ..) => { self.parent.expect("enum or trait module without a parent") } _ => self, @@ -704,8 +707,10 @@ impl<'a> NameBinding<'a> { fn is_variant(&self) -> bool { match self.kind { - NameBindingKind::Res(Res::Def(DefKind::Variant, _), _) - | NameBindingKind::Res(Res::Def(DefKind::Ctor(CtorOf::Variant, ..), _), _) => true, + NameBindingKind::Res( + Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _), + _, + ) => true, _ => false, } } @@ -740,9 +745,7 @@ impl<'a> NameBinding<'a> { fn is_importable(&self) -> bool { match self.res() { - Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::AssocTy, _) => false, + Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) => false, _ => true, } } @@ -783,7 +786,7 @@ impl<'a> NameBinding<'a> { /// All other types are defined somewhere and possibly imported, but the primitive ones need /// special handling, since they have no place of origin. struct PrimitiveTypeTable { - primitive_types: FxHashMap, + primitive_types: FxHashMap, } impl PrimitiveTypeTable { @@ -835,7 +838,7 @@ pub struct Resolver<'a> { /// Names of fields of an item `DefId` accessible with dot syntax. /// Used for hints during error reporting. - field_names: FxHashMap>>, + field_names: FxHashMap>>, /// All imports known to succeed or fail. determined_imports: Vec<&'a Import<'a>>, @@ -884,7 +887,7 @@ pub struct Resolver<'a> { /// some AST passes can generate identifiers that only resolve to local or /// language items. empty_module: Module<'a>, - module_map: FxHashMap>, + module_map: FxHashMap>, extern_module_map: FxHashMap>, binding_parent_modules: FxHashMap>, Module<'a>>, underscore_disambiguator: u32, @@ -910,11 +913,11 @@ pub struct Resolver<'a> { crate_loader: CrateLoader<'a>, macro_names: FxHashSet, - builtin_macros: FxHashMap, + builtin_macros: FxHashMap, registered_attrs: FxHashSet, registered_tools: FxHashSet, - macro_use_prelude: FxHashMap>, - all_macros: FxHashMap, + macro_use_prelude: FxHashMap>, + all_macros: FxHashMap, macro_map: FxHashMap>, dummy_ext_bang: Lrc, dummy_ext_derive: Lrc, @@ -944,7 +947,7 @@ pub struct Resolver<'a> { helper_attrs: FxHashMap>, /// Avoid duplicated errors for "name already defined". - name_already_seen: FxHashMap, + name_already_seen: FxHashMap, potentially_unused_imports: Vec<&'a Import<'a>>, @@ -953,7 +956,7 @@ pub struct Resolver<'a> { struct_constructors: DefIdMap<(Res, ty::Visibility)>, /// Features enabled for this crate. - active_features: FxHashSet, + active_features: FxHashSet, /// Stores enum visibilities to properly build a reduced graph /// when visiting the correspondent variants. @@ -1041,8 +1044,8 @@ impl rustc_ast_lowering::Resolver for Resolver<'_> { fn resolve_str_path( &mut self, span: Span, - crate_root: Option, - components: &[Name], + crate_root: Option, + components: &[Symbol], ns: Namespace, ) -> (ast::Path, Res) { let root = if crate_root.is_some() { kw::PathRoot } else { kw::Crate }; @@ -1120,7 +1123,7 @@ impl<'a> Resolver<'a> { ) }); let mut module_map = FxHashMap::default(); - module_map.insert(DefId::local(CRATE_DEF_INDEX), graph_root); + module_map.insert(LocalDefId { local_def_index: CRATE_DEF_INDEX }, graph_root); let mut definitions = Definitions::default(); definitions.create_root_def(crate_name, session.local_crate_disambiguator()); @@ -1538,8 +1541,9 @@ impl<'a> Resolver<'a> { let expn_data = expn_id.expn_data(); match expn_data.kind { ExpnKind::Root - | ExpnKind::Macro(MacroKind::Bang, _) - | ExpnKind::Macro(MacroKind::Derive, _) => Scope::DeriveHelpersCompat, + | ExpnKind::Macro(MacroKind::Bang | MacroKind::Derive, _) => { + Scope::DeriveHelpersCompat + } _ => Scope::DeriveHelpers(expn_data.parent), } } @@ -2062,52 +2066,64 @@ impl<'a> Resolver<'a> { }; } - let binding = if let Some(module) = module { - self.resolve_ident_in_module( - module, - ident, - ns, - parent_scope, - record_used, - path_span, - ) - } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) { - let scopes = ScopeSet::All(ns, opt_ns.is_none()); - self.early_resolve_ident_in_lexical_scope( - ident, - scopes, - parent_scope, - record_used, - record_used, - path_span, - ) - } else { - let record_used_id = - if record_used { crate_lint.node_id().or(Some(CRATE_NODE_ID)) } else { None }; - match self.resolve_ident_in_lexical_scope( - ident, - ns, - parent_scope, - record_used_id, - path_span, - &ribs.unwrap()[ns], - ) { - // we found a locally-imported or available item/module - Some(LexicalScopeBinding::Item(binding)) => Ok(binding), - // we found a local variable or type param - Some(LexicalScopeBinding::Res(res)) - if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => - { - record_segment_res(self, res); - return PathResult::NonModule(PartialRes::with_unresolved_segments( - res, - path.len() - 1, - )); + enum FindBindingResult<'a> { + Binding(Result<&'a NameBinding<'a>, Determinacy>), + PathResult(PathResult<'a>), + } + let find_binding_in_ns = |this: &mut Self, ns| { + let binding = if let Some(module) = module { + this.resolve_ident_in_module( + module, + ident, + ns, + parent_scope, + record_used, + path_span, + ) + } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) { + let scopes = ScopeSet::All(ns, opt_ns.is_none()); + this.early_resolve_ident_in_lexical_scope( + ident, + scopes, + parent_scope, + record_used, + record_used, + path_span, + ) + } else { + let record_used_id = if record_used { + crate_lint.node_id().or(Some(CRATE_NODE_ID)) + } else { + None + }; + match this.resolve_ident_in_lexical_scope( + ident, + ns, + parent_scope, + record_used_id, + path_span, + &ribs.unwrap()[ns], + ) { + // we found a locally-imported or available item/module + Some(LexicalScopeBinding::Item(binding)) => Ok(binding), + // we found a local variable or type param + Some(LexicalScopeBinding::Res(res)) + if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => + { + record_segment_res(this, res); + return FindBindingResult::PathResult(PathResult::NonModule( + PartialRes::with_unresolved_segments(res, path.len() - 1), + )); + } + _ => Err(Determinacy::determined(record_used)), } - _ => Err(Determinacy::determined(record_used)), - } + }; + FindBindingResult::Binding(binding) + }; + let binding = match find_binding_in_ns(self, ns) { + FindBindingResult::PathResult(x) => return x, + FindBindingResult::Binding(binding) => binding, }; - match binding { Ok(binding) => { if i == 1 { @@ -2197,7 +2213,33 @@ impl<'a> Resolver<'a> { } else if i == 0 { (format!("use of undeclared type or module `{}`", ident), None) } else { - (format!("could not find `{}` in `{}`", ident, path[i - 1].ident), None) + let mut msg = + format!("could not find `{}` in `{}`", ident, path[i - 1].ident); + if ns == TypeNS || ns == ValueNS { + let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; + if let FindBindingResult::Binding(Ok(binding)) = + find_binding_in_ns(self, ns_to_try) + { + let mut found = |what| { + msg = format!( + "expected {}, found {} `{}` in `{}`", + ns.descr(), + what, + ident, + path[i - 1].ident + ) + }; + if binding.module().is_some() { + found("module") + } else { + match binding.res() { + def::Res::::Def(kind, id) => found(kind.descr(id)), + _ => found(ns_to_try.descr()), + } + } + }; + } + (msg, None) }; return PathResult::Failed { span: ident.span, @@ -2479,8 +2521,7 @@ impl<'a> Resolver<'a> { let (span, found_use) = UsePlacementFinder::check(krate, node_id); if !candidates.is_empty() { diagnostics::show_candidates(&mut err, span, &candidates, better, found_use); - } - if let Some((span, msg, sugg, appl)) = suggestion { + } else if let Some((span, msg, sugg, appl)) = suggestion { err.span_suggestion(span, msg, sugg, appl); } err.emit(); @@ -2501,10 +2542,8 @@ impl<'a> Resolver<'a> { } let container = match parent.kind { - ModuleKind::Def(DefKind::Mod, _, _) => "module", - ModuleKind::Def(DefKind::Trait, _, _) => "trait", + ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id().unwrap()), ModuleKind::Block(..) => "block", - _ => "enum", }; let old_noun = match old_binding.is_import() { @@ -2517,7 +2556,8 @@ impl<'a> Resolver<'a> { false => "defined", }; - let (name, span) = (ident.name, self.session.source_map().def_span(new_binding.span)); + let (name, span) = + (ident.name, self.session.source_map().guess_head_span(new_binding.span)); if let Some(s) = self.name_already_seen.get(&name) { if s == &span { @@ -2558,7 +2598,7 @@ impl<'a> Resolver<'a> { err.span_label(span, format!("`{}` re{} here", name, new_participle)); err.span_label( - self.session.source_map().def_span(old_binding.span), + self.session.source_map().guess_head_span(old_binding.span), format!("previous {} of the {} `{}` here", old_noun, old_kind, name), ); @@ -2629,7 +2669,7 @@ impl<'a> Resolver<'a> { /// This function adds a suggestion to change the binding name of a new import that conflicts /// with an existing import. /// - /// ```ignore (diagnostic) + /// ```text,ignore (diagnostic) /// help: you can use `as` to change the binding name of the import /// | /// LL | use foo::bar as other_bar; @@ -2638,7 +2678,7 @@ impl<'a> Resolver<'a> { fn add_suggestion_for_rename_of_use( &self, err: &mut DiagnosticBuilder<'_>, - name: Name, + name: Symbol, import: &Import<'_>, binding_span: Span, ) { @@ -2806,7 +2846,7 @@ impl<'a> Resolver<'a> { ast::Path { span, segments: iter::once(Ident::with_dummy_span(kw::PathRoot)) - .chain({ path_str.split("::").skip(1).map(Ident::from_str) }) + .chain(path_str.split("::").skip(1).map(Ident::from_str)) .map(|i| self.new_ast_path_segment(i)) .collect(), } @@ -2874,12 +2914,12 @@ impl<'a> Resolver<'a> { } // For rustdoc. - pub fn all_macros(&self) -> &FxHashMap { + pub fn all_macros(&self) -> &FxHashMap { &self.all_macros } } -fn names_to_string(names: &[Name]) -> String { +fn names_to_string(names: &[Symbol]) -> String { let mut result = String::new(); for (i, name) in names.iter().filter(|name| **name != kw::PathRoot).enumerate() { if i > 0 { @@ -2901,14 +2941,14 @@ fn path_names_to_string(path: &Path) -> String { fn module_to_string(module: Module<'_>) -> Option { let mut names = Vec::new(); - fn collect_mod(names: &mut Vec, module: Module<'_>) { + fn collect_mod(names: &mut Vec, module: Module<'_>) { if let ModuleKind::Def(.., name) = module.kind { if let Some(parent) = module.parent { names.push(name); collect_mod(names, parent); } } else { - names.push(Name::intern("")); + names.push(Symbol::intern("")); collect_mod(names, module.parent.unwrap()); } } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 166fc48b44c4c..1b6268dc8cbcf 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -6,9 +6,7 @@ use crate::Namespace::*; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy}; use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; -use rustc::middle::stability; -use rustc::{span_bug, ty}; -use rustc_ast::ast::{self, Ident, NodeId}; +use rustc_ast::ast::{self, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, StabilityLevel}; use rustc_data_structures::fx::FxHashSet; @@ -19,12 +17,13 @@ use rustc_expand::expand::{AstFragment, AstFragmentKind, Invocation, InvocationK use rustc_feature::is_builtin_attr_name; use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id; +use rustc_middle::middle::stability; +use rustc_middle::{span_bug, ty}; use rustc_session::lint::builtin::UNUSED_MACROS; -use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, ExpnData, ExpnId, ExpnKind}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_data_structures::sync::Lrc; @@ -166,7 +165,7 @@ impl<'a> base::Resolver for Resolver<'a> { parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); } - fn register_builtin_macro(&mut self, ident: ast::Ident, ext: SyntaxExtension) { + fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension) { if self.builtin_macros.insert(ident.name, ext).is_some() { self.session .span_err(ident.span, &format!("built-in macro `{}` was already defined", ident)); @@ -191,7 +190,7 @@ impl<'a> base::Resolver for Resolver<'a> { let parent_scope = if let Some(module_id) = parent_module_id { let parent_def_id = self.definitions.local_def_id(module_id); - self.definitions.add_parent_module_of_macro_def(expn_id, parent_def_id); + self.definitions.add_parent_module_of_macro_def(expn_id, parent_def_id.to_def_id()); self.module_map[&parent_def_id] } else { self.definitions.add_parent_module_of_macro_def( @@ -397,20 +396,16 @@ impl<'a> Resolver<'a> { Err(Determinacy::Undetermined) => return Err(Indeterminate), }; - // Report errors and enforce feature gates for the resolved macro. - let features = self.session.features_untracked(); + // Report errors for the resolved macro. for segment in &path.segments { if let Some(args) = &segment.args { self.session.span_err(args.span(), "generic arguments in macro path"); } - if kind == MacroKind::Attr - && !features.rustc_attrs - && segment.ident.as_str().starts_with("rustc") - { - let msg = - "attributes starting with `rustc` are reserved for use by the `rustc` compiler"; - feature_err(&self.session.parse_sess, sym::rustc_attrs, segment.ident.span, msg) - .emit(); + if kind == MacroKind::Attr && segment.ident.as_str().starts_with("rustc") { + self.session.span_err( + segment.ident.span, + "attributes starting with `rustc` are reserved for use by the `rustc` compiler", + ); } } diff --git a/src/librustc_save_analysis/Cargo.toml b/src/librustc_save_analysis/Cargo.toml index de851d9772727..5948c88054ded 100644 --- a/src/librustc_save_analysis/Cargo.toml +++ b/src/librustc_save_analysis/Cargo.toml @@ -10,14 +10,15 @@ path = "lib.rs" [dependencies] log = "0.4" -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } +rustc_ast = { path = "../librustc_ast" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_data_structures = { path = "../librustc_data_structures" } -rustc_session = { path = "../librustc_session" } rustc_hir = { path = "../librustc_hir" } +rustc_hir_pretty = { path = "../librustc_hir_pretty" } rustc_parse = { path = "../librustc_parse" } serde_json = "1" -rustc_ast = { path = "../librustc_ast" } +rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } rls-data = "0.19" rls-span = "0.5" diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index a80c3b72044ef..3dd715f9e3df6 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -13,8 +13,6 @@ //! DumpVisitor walks the AST and processes it, and Dumper is used for //! recording the output. -use rustc::span_bug; -use rustc::ty::{self, DefIdTree, TyCtxt}; use rustc_ast::ast::{self, Attribute, NodeId, PatKind}; use rustc_ast::ptr::P; use rustc_ast::token; @@ -23,9 +21,12 @@ use rustc_ast::walk_list; use rustc_ast_pretty::pprust::{bounds_to_string, generic_params_to_string, ty_to_string}; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind as HirDefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::span_bug; +use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::config::Input; use rustc_span::source_map::{respan, DUMMY_SP}; +use rustc_span::symbol::Ident; use rustc_span::*; use std::env; @@ -103,12 +104,10 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { self.dumper.analysis() } - fn nest_tables(&mut self, item_id: NodeId, f: F) + fn nest_tables(&mut self, item_def_id: LocalDefId, f: F) where F: FnOnce(&mut Self), { - let item_def_id = self.tcx.hir().local_def_id_from_node_id(item_id); - let tables = if self.tcx.has_typeck_tables(item_def_id) { self.tcx.typeck_tables_of(item_def_id) } else { @@ -225,11 +224,14 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { collector.visit_pat(&arg.pat); for (id, ident, ..) in collector.collected_idents { - let hir_id = self.tcx.hir().node_to_hir_id(id); - let typ = match self.save_ctxt.tables.node_type_opt(hir_id) { - Some(s) => s.to_string(), - None => continue, - }; + // FIXME(#71104) Should really be using just `node_id_to_hir_id` but + // some `NodeId` do not seem to have a corresponding HirId. + let hir_id = self.tcx.hir().opt_node_id_to_hir_id(id); + let typ = + match hir_id.and_then(|hir_id| self.save_ctxt.tables.node_type_opt(hir_id)) { + Some(s) => s.to_string(), + None => continue, + }; if !self.span.filter_generated(ident.span) { let id = id_from_node_id(id, &self.save_ctxt); let span = self.span_from_span(ident.span); @@ -261,15 +263,16 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { sig: &'l ast::FnSig, body: Option<&'l ast::Block>, id: ast::NodeId, - ident: ast::Ident, + ident: Ident, generics: &'l ast::Generics, vis: ast::Visibility, span: Span, ) { debug!("process_method: {}:{}", id, ident); - let hir_id = self.tcx.hir().node_to_hir_id(id); - self.nest_tables(id, |v| { + let map = &self.tcx.hir(); + let hir_id = map.node_id_to_hir_id(id); + self.nest_tables(map.local_def_id(hir_id), |v| { if let Some(mut method_data) = v.save_ctxt.get_method_data(id, ident, span) { v.process_formals(&sig.decl.inputs, &method_data.qualname); v.process_generic_params(&generics, &method_data.qualname, id); @@ -292,7 +295,8 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { // start walking from the newly-created definition. match sig.header.asyncness { ast::Async::Yes { return_impl_trait_id, .. } => { - v.nest_tables(return_impl_trait_id, |v| v.visit_ty(ret_ty)) + let hir_id = map.node_id_to_hir_id(return_impl_trait_id); + v.nest_tables(map.local_def_id(hir_id), |v| v.visit_ty(ret_ty)) } _ => v.visit_ty(ret_ty), } @@ -308,7 +312,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { fn process_struct_field_def(&mut self, field: &ast::StructField, parent_id: NodeId) { let field_data = self.save_ctxt.get_field_data(field, parent_id); if let Some(field_data) = field_data { - let hir_id = self.tcx.hir().node_to_hir_id(field.id); + let hir_id = self.tcx.hir().node_id_to_hir_id(field.id); self.dumper.dump_def(&access_from!(self.save_ctxt, field, hir_id), field_data); } } @@ -360,8 +364,9 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { ty_params: &'l ast::Generics, body: Option<&'l ast::Block>, ) { - let hir_id = self.tcx.hir().node_to_hir_id(item.id); - self.nest_tables(item.id, |v| { + let map = &self.tcx.hir(); + let hir_id = map.node_id_to_hir_id(item.id); + self.nest_tables(map.local_def_id(hir_id), |v| { if let Some(fn_data) = v.save_ctxt.get_item_data(item) { down_cast_data!(fn_data, DefData, item.span); v.process_formals(&decl.inputs, &fn_data.qualname); @@ -385,7 +390,8 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { // start walking from the newly-created definition. match header.asyncness { ast::Async::Yes { return_impl_trait_id, .. } => { - v.nest_tables(return_impl_trait_id, |v| v.visit_ty(ret_ty)) + let hir_id = map.node_id_to_hir_id(return_impl_trait_id); + v.nest_tables(map.local_def_id(hir_id), |v| v.visit_ty(ret_ty)) } _ => v.visit_ty(ret_ty), } @@ -402,8 +408,8 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { typ: &'l ast::Ty, expr: Option<&'l ast::Expr>, ) { - let hir_id = self.tcx.hir().node_to_hir_id(item.id); - self.nest_tables(item.id, |v| { + let hir_id = self.tcx.hir().node_id_to_hir_id(item.id); + self.nest_tables(self.tcx.hir().local_def_id(hir_id), |v| { if let Some(var_data) = v.save_ctxt.get_item_data(item) { down_cast_data!(var_data, DefData, item.span); v.dumper.dump_def(&access_from!(v.save_ctxt, item, hir_id), var_data); @@ -416,20 +422,20 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { fn process_assoc_const( &mut self, id: ast::NodeId, - ident: ast::Ident, + ident: Ident, typ: &'l ast::Ty, expr: Option<&'l ast::Expr>, parent_id: DefId, vis: ast::Visibility, attrs: &'l [Attribute], ) { + let hir_id = self.tcx.hir().node_id_to_hir_id(id); let qualname = - format!("::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(id))); + format!("::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id(hir_id).to_def_id())); if !self.span.filter_generated(ident.span) { let sig = sig::assoc_const_signature(id, ident.name, typ, expr, &self.save_ctxt); let span = self.span_from_span(ident.span); - let hir_id = self.tcx.hir().node_to_hir_id(id); self.dumper.dump_def( &access_from_vis!(self.save_ctxt, vis, hir_id), @@ -451,7 +457,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { } // walk type and init value - self.nest_tables(id, |v| { + self.nest_tables(self.tcx.hir().local_def_id(hir_id), |v| { v.visit_ty(typ); if let Some(expr) = expr { v.visit_expr(expr); @@ -468,10 +474,9 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { ) { debug!("process_struct {:?} {:?}", item, item.span); let name = item.ident.to_string(); - let qualname = format!( - "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) - ); + let hir_id = self.tcx.hir().node_id_to_hir_id(item.id); + let qualname = + format!("::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id(hir_id).to_def_id())); let kind = match item.kind { ast::ItemKind::Struct(_, _) => DefKind::Struct, @@ -503,7 +508,6 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { if !self.span.filter_generated(item.ident.span) { let span = self.span_from_span(item.ident.span); - let hir_id = self.tcx.hir().node_to_hir_id(item.id); self.dumper.dump_def( &access_from!(self.save_ctxt, item, hir_id), Def { @@ -523,7 +527,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { ); } - self.nest_tables(item.id, |v| { + self.nest_tables(self.tcx.hir().local_def_id(hir_id), |v| { for field in def.fields() { v.process_struct_field_def(field, item.id); v.visit_ty(&field.ty); @@ -546,7 +550,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { }; down_cast_data!(enum_data, DefData, item.span); - let hir_id = self.tcx.hir().node_to_hir_id(item.id); + let hir_id = self.tcx.hir().node_id_to_hir_id(item.id); let access = access_from!(self.save_ctxt, item, hir_id); for variant in &enum_definition.variants { @@ -663,14 +667,15 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { } let map = &self.tcx.hir(); - self.nest_tables(item.id, |v| { + let hir_id = map.node_id_to_hir_id(item.id); + self.nest_tables(map.local_def_id(hir_id), |v| { v.visit_ty(&typ); if let &Some(ref trait_ref) = trait_ref { v.process_path(trait_ref.ref_id, &trait_ref.path); } v.process_generic_params(generics, "", item.id); for impl_item in impl_items { - v.process_impl_item(impl_item, map.local_def_id_from_node_id(item.id)); + v.process_impl_item(impl_item, map.local_def_id(hir_id).to_def_id()); } }); } @@ -685,7 +690,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { let name = item.ident.to_string(); let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id()) ); let mut val = name.clone(); if !generics.params.is_empty() { @@ -699,7 +704,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { let id = id_from_node_id(item.id, &self.save_ctxt); let span = self.span_from_span(item.ident.span); let children = methods.iter().map(|i| id_from_node_id(i.id, &self.save_ctxt)).collect(); - let hir_id = self.tcx.hir().node_to_hir_id(item.id); + let hir_id = self.tcx.hir().node_id_to_hir_id(item.id); self.dumper.dump_def( &access_from!(self.save_ctxt, item, hir_id), Def { @@ -751,7 +756,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { self.process_generic_params(generics, &qualname, item.id); for method in methods { let map = &self.tcx.hir(); - self.process_trait_item(method, map.local_def_id_from_node_id(item.id)) + self.process_trait_item(method, map.local_def_id_from_node_id(item.id).to_def_id()) } } @@ -759,7 +764,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { fn process_mod(&mut self, item: &ast::Item) { if let Some(mod_data) = self.save_ctxt.get_item_data(item) { down_cast_data!(mod_data, DefData, item.span); - let hir_id = self.tcx.hir().node_to_hir_id(item.id); + let hir_id = self.tcx.hir().node_id_to_hir_id(item.id); self.dumper.dump_def(&access_from!(self.save_ctxt, item, hir_id), mod_data); } } @@ -783,7 +788,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { match **generic_args { ast::GenericArgs::AngleBracketed(ref data) => { for arg in &data.args { - if let ast::GenericArg::Type(ty) = arg { + if let ast::AngleBracketedArg::Arg(ast::GenericArg::Type(ty)) = arg { self.visit_ty(ty); } } @@ -848,10 +853,9 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { if let Some(ref generic_args) = seg.args { if let ast::GenericArgs::AngleBracketed(ref data) = **generic_args { for arg in &data.args { - match arg { - ast::GenericArg::Type(ty) => self.visit_ty(ty), - _ => {} - } + if let ast::AngleBracketedArg::Arg(ast::GenericArg::Type(ty)) = arg { + self.visit_ty(ty) + }; } } } @@ -864,7 +868,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { match p.kind { PatKind::Struct(ref _path, ref fields, _) => { // FIXME do something with _path? - let hir_id = self.tcx.hir().node_to_hir_id(p.id); + let hir_id = self.tcx.hir().node_id_to_hir_id(p.id); let adt = match self.save_ctxt.tables.node_type_opt(hir_id) { Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(), _ => { @@ -903,7 +907,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { for (id, ident, _) in collector.collected_idents { match self.save_ctxt.get_path_res(id) { Res::Local(hir_id) => { - let id = self.tcx.hir().hir_to_node_id(hir_id); + let id = self.tcx.hir().hir_id_to_node_id(hir_id); let typ = self .save_ctxt .tables @@ -936,13 +940,16 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { ); } } - Res::Def(HirDefKind::Ctor(..), _) - | Res::Def(HirDefKind::Const, _) - | Res::Def(HirDefKind::AssocConst, _) - | Res::Def(HirDefKind::Struct, _) - | Res::Def(HirDefKind::Variant, _) - | Res::Def(HirDefKind::TyAlias, _) - | Res::Def(HirDefKind::AssocTy, _) + Res::Def( + HirDefKind::Ctor(..) + | HirDefKind::Const + | HirDefKind::AssocConst + | HirDefKind::Struct + | HirDefKind::Variant + | HirDefKind::TyAlias + | HirDefKind::AssocTy, + _, + ) | Res::SelfTy(..) => { self.dump_path_ref(id, &ast::Path::from_ident(ident)); } @@ -1031,7 +1038,9 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { let name = trait_item.ident.name.to_string(); let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(trait_item.id)) + self.tcx.def_path_str( + self.tcx.hir().local_def_id_from_node_id(trait_item.id).to_def_id() + ) ); if !self.span.filter_generated(trait_item.ident.span) { @@ -1126,7 +1135,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { // The access is calculated using the current tree ID, but with the root tree's visibility // (since nested trees don't have their own visibility). - let hir_id = self.tcx.hir().node_to_hir_id(id); + let hir_id = self.tcx.hir().node_id_to_hir_id(id); let access = access_from!(self.save_ctxt, root_item, hir_id); // The parent `DefId` of a given use tree is always the enclosing item. @@ -1135,7 +1144,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { .tcx .hir() .opt_local_def_id_from_node_id(id) - .and_then(|id| self.save_ctxt.tcx.parent(id)) + .and_then(|id| self.save_ctxt.tcx.parent(id.to_def_id())) .map(id_from_def_id); match use_tree.kind { @@ -1228,8 +1237,10 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { // only get called for the root module of a crate. assert_eq!(id, ast::CRATE_NODE_ID); - let qualname = - format!("::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(id))); + let qualname = format!( + "::{}", + self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(id).to_def_id()) + ); let sm = self.tcx.sess.source_map(); let filename = sm.span_to_filename(span); @@ -1274,7 +1285,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { .tcx .hir() .opt_local_def_id_from_node_id(item.id) - .and_then(|id| self.save_ctxt.tcx.parent(id)) + .and_then(|id| self.save_ctxt.tcx.parent(id.to_def_id())) .map(id_from_def_id); self.dumper.import( &Access { public: false, reachable: false }, @@ -1312,7 +1323,9 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { TyAlias(_, ref ty_params, _, ref ty) => { let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str( + self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id() + ) ); let value = match ty { Some(ty) => ty_to_string(&ty), @@ -1321,7 +1334,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { if !self.span.filter_generated(item.ident.span) { let span = self.span_from_span(item.ident.span); let id = id_from_node_id(item.id, &self.save_ctxt); - let hir_id = self.tcx.hir().node_to_hir_id(item.id); + let hir_id = self.tcx.hir().node_id_to_hir_id(item.id); self.dumper.dump_def( &access_from!(self.save_ctxt, item, hir_id), @@ -1397,7 +1410,10 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { } ast::TyKind::Array(ref element, ref length) => { self.visit_ty(element); - self.nest_tables(length.id, |v| v.visit_expr(&length.value)); + let hir_id = self.tcx.hir().node_id_to_hir_id(length.id); + self.nest_tables(self.tcx.hir().local_def_id(hir_id), |v| { + v.visit_expr(&length.value) + }); } ast::TyKind::ImplTrait(id, ref bounds) => { // FIXME: As of writing, the opaque type lowering introduces @@ -1409,7 +1425,13 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { // bounds... // This will panic if called on return type `impl Trait`, which // we guard against in `process_fn`. - self.nest_tables(id, |v| v.process_bounds(bounds)); + // FIXME(#71104) Should really be using just `node_id_to_hir_id` but + // some `NodeId` do not seem to have a corresponding HirId. + if let Some(hir_id) = self.tcx.hir().opt_node_id_to_hir_id(id) { + self.nest_tables(self.tcx.hir().local_def_id(hir_id), |v| { + v.process_bounds(bounds) + }); + } } _ => visit::walk_ty(self, t), } @@ -1420,7 +1442,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { self.process_macro_use(ex.span); match ex.kind { ast::ExprKind::Struct(ref path, ref fields, ref base) => { - let expr_hir_id = self.save_ctxt.tcx.hir().node_to_hir_id(ex.id); + let expr_hir_id = self.save_ctxt.tcx.hir().node_id_to_hir_id(ex.id); let hir_expr = self.save_ctxt.tcx.hir().expect_expr(expr_hir_id); let adt = match self.save_ctxt.tables.expr_ty_opt(&hir_expr) { Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(), @@ -1429,7 +1451,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { return; } }; - let node_id = self.save_ctxt.tcx.hir().hir_to_node_id(hir_expr.hir_id); + let node_id = self.save_ctxt.tcx.hir().hir_id_to_node_id(hir_expr.hir_id); let res = self.save_ctxt.get_path_res(node_id); self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), base) } @@ -1457,7 +1479,8 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { } // walk the body - self.nest_tables(ex.id, |v| { + let hir_id = self.tcx.hir().node_id_to_hir_id(ex.id); + self.nest_tables(self.tcx.hir().local_def_id(hir_id), |v| { v.process_formals(&decl.inputs, &id); v.visit_expr(body) }); @@ -1474,7 +1497,10 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { } ast::ExprKind::Repeat(ref element, ref count) => { self.visit_expr(element); - self.nest_tables(count.id, |v| v.visit_expr(&count.value)); + let hir_id = self.tcx.hir().node_id_to_hir_id(count.id); + self.nest_tables(self.tcx.hir().local_def_id(hir_id), |v| { + v.visit_expr(&count.value) + }); } // In particular, we take this branch for call and path expressions, // where we'll index the idents involved just by continuing to walk. @@ -1514,7 +1540,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { } fn visit_foreign_item(&mut self, item: &'l ast::ForeignItem) { - let hir_id = self.tcx.hir().node_to_hir_id(item.id); + let hir_id = self.tcx.hir().node_id_to_hir_id(item.id); let access = access_from!(self.save_ctxt, item, hir_id); match item.kind { diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 024633c3b3dec..43fd2b1853011 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -1,5 +1,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] mod dump_visitor; @@ -8,10 +9,6 @@ mod dumper; mod span_utils; mod sig; -use rustc::middle::cstore::ExternCrate; -use rustc::middle::privacy::AccessLevels; -use rustc::ty::{self, DefIdTree, TyCtxt}; -use rustc::{bug, span_bug}; use rustc_ast::ast::{self, Attribute, NodeId, PatKind, DUMMY_NODE_ID}; use rustc_ast::util::comments::strip_doc_comment_decoration; use rustc_ast::visit::{self, Visitor}; @@ -20,9 +17,14 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind as HirDefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::Node; +use rustc_middle::middle::cstore::ExternCrate; +use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, Input, OutputType}; use rustc_session::output::{filename_for_metadata, out_filename}; use rustc_span::source_map::Spanned; +use rustc_span::symbol::Ident; use rustc_span::*; use std::cell::Cell; @@ -130,7 +132,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option { let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id()) ); match item.kind { ast::ForeignItemKind::Fn(_, ref sig, ref generics, _) => { @@ -183,7 +185,9 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { ast::ItemKind::Fn(_, ref sig, .., ref generics, _) => { let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str( + self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id() + ) ); filter!(self.span_utils, item.ident.span); Some(Data::DefData(Def { @@ -204,7 +208,9 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { ast::ItemKind::Static(ref typ, ..) => { let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str( + self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id() + ) ); filter!(self.span_utils, item.ident.span); @@ -230,7 +236,9 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { ast::ItemKind::Const(_, ref typ, _) => { let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str( + self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id() + ) ); filter!(self.span_utils, item.ident.span); @@ -255,7 +263,9 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { ast::ItemKind::Mod(ref m) => { let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str( + self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id() + ) ); let sm = self.tcx.sess.source_map(); @@ -282,7 +292,9 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { let name = item.ident.to_string(); let qualname = format!( "::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) + self.tcx.def_path_str( + self.tcx.hir().local_def_id_from_node_id(item.id).to_def_id() + ) ); filter!(self.span_utils, item.ident.span); let variants_str = @@ -363,11 +375,11 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { let name = ident.to_string(); let qualname = format!( "::{}::{}", - self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(scope)), + self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(scope).to_def_id()), ident ); filter!(self.span_utils, ident.span); - let def_id = self.tcx.hir().local_def_id_from_node_id(field.id); + let def_id = self.tcx.hir().local_def_id_from_node_id(field.id).to_def_id(); let typ = self.tcx.type_of(def_id).to_string(); let id = id_from_node_id(field.id, self); @@ -394,24 +406,25 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { // FIXME would be nice to take a MethodItem here, but the ast provides both // trait and impl flavours, so the caller must do the disassembly. - pub fn get_method_data(&self, id: ast::NodeId, ident: ast::Ident, span: Span) -> Option { + pub fn get_method_data(&self, id: ast::NodeId, ident: Ident, span: Span) -> Option { // The qualname for a method is the trait name or name of the struct in an impl in // which the method is declared in, followed by the method's name. let (qualname, parent_scope, decl_id, docs, attributes) = match self .tcx - .impl_of_method(self.tcx.hir().local_def_id_from_node_id(id)) + .impl_of_method(self.tcx.hir().local_def_id_from_node_id(id).to_def_id()) { Some(impl_id) => match self.tcx.hir().get_if_local(impl_id) { Some(Node::Item(item)) => match item.kind { hir::ItemKind::Impl { ref self_ty, .. } => { + let hir = self.tcx.hir(); + let mut qualname = String::from("<"); - qualname.push_str(&self.tcx.hir().hir_to_pretty_string(self_ty.hir_id)); + qualname.push_str(&rustc_hir_pretty::id_to_string(&hir, self_ty.hir_id)); let trait_id = self.tcx.trait_id_of_impl(impl_id); let mut docs = String::new(); let mut attrs = vec![]; - let hir_id = self.tcx.hir().node_to_hir_id(id); - if let Some(Node::ImplItem(item)) = self.tcx.hir().find(hir_id) { + if let Some(Node::ImplItem(item)) = hir.find(hir.node_id_to_hir_id(id)) { docs = self.docs_for_attrs(&item.attrs); attrs = item.attrs.to_vec(); } @@ -447,11 +460,14 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { ); } }, - None => match self.tcx.trait_of_item(self.tcx.hir().local_def_id_from_node_id(id)) { + None => match self + .tcx + .trait_of_item(self.tcx.hir().local_def_id_from_node_id(id).to_def_id()) + { Some(def_id) => { let mut docs = String::new(); let mut attrs = vec![]; - let hir_id = self.tcx.hir().node_to_hir_id(id); + let hir_id = self.tcx.hir().node_id_to_hir_id(id); if let Some(Node::TraitItem(item)) = self.tcx.hir().find(hir_id) { docs = self.docs_for_attrs(&item.attrs); @@ -510,7 +526,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { } pub fn get_expr_data(&self, expr: &ast::Expr) -> Option { - let expr_hir_id = self.tcx.hir().node_to_hir_id(expr.id); + let expr_hir_id = self.tcx.hir().node_id_to_hir_id(expr.id); let hir_node = self.tcx.hir().expect_expr(expr_hir_id); let ty = self.tables.expr_ty_adjusted_opt(&hir_node); if ty.is_none() || ty.unwrap().kind == ty::Error { @@ -518,7 +534,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { } match expr.kind { ast::ExprKind::Field(ref sub_ex, ident) => { - let sub_ex_hir_id = self.tcx.hir().node_to_hir_id(sub_ex.id); + let sub_ex_hir_id = self.tcx.hir().node_id_to_hir_id(sub_ex.id); let hir_node = match self.tcx.hir().find(sub_ex_hir_id) { Some(Node::Expr(expr)) => expr, _ => { @@ -572,7 +588,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { } } ast::ExprKind::MethodCall(ref seg, ..) => { - let expr_hir_id = self.tcx.hir().definitions().node_to_hir_id(expr.id); + let expr_hir_id = self.tcx.hir().definitions().node_id_to_hir_id(expr.id); let method_id = match self.tables.type_dependent_def_id(expr_hir_id) { Some(id) => id, None => { @@ -590,7 +606,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { Some(Data::RefData(Ref { kind: RefKind::Function, span, - ref_id: def_id.or(decl_id).map(id_from_def_id).unwrap_or_else(|| null_id()), + ref_id: def_id.or(decl_id).map(id_from_def_id).unwrap_or_else(null_id), })) } ast::ExprKind::Path(_, ref path) => { @@ -604,7 +620,11 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { } pub fn get_path_res(&self, id: NodeId) -> Res { - let hir_id = self.tcx.hir().node_to_hir_id(id); + // FIXME(#71104) + let hir_id = match self.tcx.hir().opt_node_id_to_hir_id(id) { + Some(id) => id, + None => return Res::Err, + }; match self.tcx.hir().get(hir_id) { Node::TraitRef(tr) => tr.path.res, @@ -618,7 +638,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { Some(res) if res != Res::Err => res, _ => { let parent_node = self.tcx.hir().get_parent_node(hir_id); - self.get_path_res(self.tcx.hir().hir_to_node_id(parent_node)) + self.get_path_res(self.tcx.hir().hir_id_to_node_id(parent_node)) } }, @@ -627,9 +647,13 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { } Node::Expr(&hir::Expr { kind: hir::ExprKind::Path(ref qpath), .. }) - | Node::Pat(&hir::Pat { kind: hir::PatKind::Path(ref qpath), .. }) - | Node::Pat(&hir::Pat { kind: hir::PatKind::Struct(ref qpath, ..), .. }) - | Node::Pat(&hir::Pat { kind: hir::PatKind::TupleStruct(ref qpath, ..), .. }) + | Node::Pat(&hir::Pat { + kind: + hir::PatKind::Path(ref qpath) + | hir::PatKind::Struct(ref qpath, ..) + | hir::PatKind::TupleStruct(ref qpath, ..), + .. + }) | Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => { self.tables.qpath_res(qpath, hir_id) } @@ -680,25 +704,26 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { Res::Local(id) => Some(Ref { kind: RefKind::Variable, span, - ref_id: id_from_node_id(self.tcx.hir().hir_to_node_id(id), self), + ref_id: id_from_node_id(self.tcx.hir().hir_id_to_node_id(id), self), }), Res::Def(HirDefKind::Trait, def_id) if fn_type(path_seg) => { Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }) } - Res::Def(HirDefKind::Struct, def_id) - | Res::Def(HirDefKind::Variant, def_id) - | Res::Def(HirDefKind::Union, def_id) - | Res::Def(HirDefKind::Enum, def_id) - | Res::Def(HirDefKind::TyAlias, def_id) - | Res::Def(HirDefKind::ForeignTy, def_id) - | Res::Def(HirDefKind::TraitAlias, def_id) - | Res::Def(HirDefKind::AssocOpaqueTy, def_id) - | Res::Def(HirDefKind::AssocTy, def_id) - | Res::Def(HirDefKind::Trait, def_id) - | Res::Def(HirDefKind::OpaqueTy, def_id) - | Res::Def(HirDefKind::TyParam, def_id) => { - Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }) - } + Res::Def( + HirDefKind::Struct + | HirDefKind::Variant + | HirDefKind::Union + | HirDefKind::Enum + | HirDefKind::TyAlias + | HirDefKind::ForeignTy + | HirDefKind::TraitAlias + | HirDefKind::AssocOpaqueTy + | HirDefKind::AssocTy + | HirDefKind::Trait + | HirDefKind::OpaqueTy + | HirDefKind::TyParam, + def_id, + ) => Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }), Res::Def(HirDefKind::ConstParam, def_id) => { Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) }) } @@ -709,12 +734,13 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { let parent_def_id = self.tcx.parent(def_id).unwrap(); Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) }) } - Res::Def(HirDefKind::Static, _) - | Res::Def(HirDefKind::Const, _) - | Res::Def(HirDefKind::AssocConst, _) - | Res::Def(HirDefKind::Ctor(..), _) => { - Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) }) - } + Res::Def( + HirDefKind::Static + | HirDefKind::Const + | HirDefKind::AssocConst + | HirDefKind::Ctor(..), + _, + ) => Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) }), Res::Def(HirDefKind::AssocFn, decl_id) => { let def_id = if decl_id.is_local() { let ti = self.tcx.associated_item(decl_id); @@ -739,9 +765,23 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { Res::Def(HirDefKind::Mod, def_id) => { Some(Ref { kind: RefKind::Mod, span, ref_id: id_from_def_id(def_id) }) } - Res::PrimTy(..) + + Res::Def( + HirDefKind::Macro(..) + | HirDefKind::ExternCrate + | HirDefKind::ForeignMod + | HirDefKind::LifetimeParam + | HirDefKind::AnonConst + | HirDefKind::Use + | HirDefKind::Field + | HirDefKind::GlobalAsm + | HirDefKind::Impl + | HirDefKind::Closure + | HirDefKind::Generator, + _, + ) + | Res::PrimTy(..) | Res::SelfTy(..) - | Res::Def(HirDefKind::Macro(..), _) | Res::ToolMod | Res::NonMacroAttr(..) | Res::SelfCtor(..) @@ -879,7 +919,7 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String { // variables (idents) from patterns. struct PathCollector<'l> { collected_paths: Vec<(NodeId, &'l ast::Path)>, - collected_idents: Vec<(NodeId, ast::Ident, ast::Mutability)>, + collected_idents: Vec<(NodeId, Ident, ast::Mutability)>, } impl<'l> PathCollector<'l> { @@ -1072,7 +1112,7 @@ fn id_from_def_id(id: DefId) -> rls_data::Id { fn id_from_node_id(id: NodeId, scx: &SaveContext<'_, '_>) -> rls_data::Id { let def_id = scx.tcx.hir().opt_local_def_id_from_node_id(id); - def_id.map(id_from_def_id).unwrap_or_else(|| { + def_id.map(|id| id_from_def_id(id.to_def_id())).unwrap_or_else(|| { // Create a *fake* `DefId` out of a `NodeId` by subtracting the `NodeId` // out of the maximum u32 value. This will work unless you have *billions* // of definitions in a single crate (very unlikely to actually happen). diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index 51434e9333049..bda9ff93b02ef 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -32,6 +32,7 @@ use rls_data::{SigElement, Signature}; use rustc_ast::ast::{self, Extern, NodeId}; use rustc_ast_pretty::pprust; use rustc_hir::def::{DefKind, Res}; +use rustc_span::symbol::{Ident, Symbol}; pub fn item_signature(item: &ast::Item, scx: &SaveContext<'_, '_>) -> Option { if !scx.config.signatures { @@ -69,7 +70,7 @@ pub fn variant_signature(variant: &ast::Variant, scx: &SaveContext<'_, '_>) -> O pub fn method_signature( id: NodeId, - ident: ast::Ident, + ident: Ident, generics: &ast::Generics, m: &ast::FnSig, scx: &SaveContext<'_, '_>, @@ -82,7 +83,7 @@ pub fn method_signature( pub fn assoc_const_signature( id: NodeId, - ident: ast::Name, + ident: Symbol, ty: &ast::Ty, default: Option<&ast::Expr>, scx: &SaveContext<'_, '_>, @@ -95,7 +96,7 @@ pub fn assoc_const_signature( pub fn assoc_type_signature( id: NodeId, - ident: ast::Ident, + ident: Ident, bounds: Option<&ast::GenericBounds>, default: Option<&ast::Ty>, scx: &SaveContext<'_, '_>, @@ -561,9 +562,7 @@ impl Sig for ast::Path { refs: vec![], }); } - Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::Variant, _) - | Res::Def(DefKind::Ctor(..), _) => { + Res::Def(DefKind::AssocConst | DefKind::Variant | DefKind::Ctor(..), _) => { let len = self.segments.len(); if len < 2 { return Err("Bad path"); @@ -805,7 +804,7 @@ fn name_and_generics( offset: usize, generics: &ast::Generics, id: NodeId, - name: ast::Ident, + name: Ident, scx: &SaveContext<'_, '_>, ) -> Result { let name = name.to_string(); @@ -823,7 +822,7 @@ fn name_and_generics( fn make_assoc_type_signature( id: NodeId, - ident: ast::Ident, + ident: Ident, bounds: Option<&ast::GenericBounds>, default: Option<&ast::Ty>, scx: &SaveContext<'_, '_>, @@ -855,7 +854,7 @@ fn make_assoc_type_signature( fn make_assoc_const_signature( id: NodeId, - ident: ast::Name, + ident: Symbol, ty: &ast::Ty, default: Option<&ast::Expr>, scx: &SaveContext<'_, '_>, @@ -886,7 +885,7 @@ fn make_assoc_const_signature( fn make_method_signature( id: NodeId, - ident: ast::Ident, + ident: Ident, generics: &ast::Generics, m: &ast::FnSig, scx: &SaveContext<'_, '_>, diff --git a/src/librustc_session/Cargo.toml b/src/librustc_session/Cargo.toml index 3895d0f8061c0..705c115cf3c85 100644 --- a/src/librustc_session/Cargo.toml +++ b/src/librustc_session/Cargo.toml @@ -9,6 +9,7 @@ name = "rustc_session" path = "lib.rs" [dependencies] +getopts = "0.2" log = "0.4" rustc_errors = { path = "../librustc_errors" } rustc_feature = { path = "../librustc_feature" } @@ -16,7 +17,6 @@ rustc_target = { path = "../librustc_target" } rustc_serialize = { path = "../libserialize", package = "serialize" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_span = { path = "../librustc_span" } -rustc_index = { path = "../librustc_index" } rustc_fs_util = { path = "../librustc_fs_util" } num_cpus = "1.0" rustc_ast = { path = "../librustc_ast" } diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs index 6c4d70c09a39a..5f34a39db05ce 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -18,9 +18,10 @@ use rustc_feature::UnstableFeatures; use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST}; use rustc_span::source_map::{FileName, FilePathMapping}; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::SourceFileHashAlgorithm; use rustc_errors::emitter::HumanReadableErrorType; -use rustc_errors::{ColorConfig, FatalError, Handler, HandlerFlags}; +use rustc_errors::{ColorConfig, HandlerFlags}; use std::collections::btree_map::{ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, @@ -68,6 +69,19 @@ impl FromStr for Sanitizer { } } +/// The different settings that the `-Z strip` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum Strip { + /// Do not strip at all. + None, + + /// Strip debuginfo. + Debuginfo, + + /// Strip all symbols. + Symbols, +} + /// The different settings that the `-Z control_flow_guard` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum CFGuard { @@ -616,10 +630,6 @@ impl Options { } impl DebuggingOptions { - pub fn ui_testing(&self) -> bool { - self.ui_testing.unwrap_or(false) - } - pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { HandlerFlags { can_emit_warnings, @@ -627,7 +637,7 @@ impl DebuggingOptions { dont_buffer_diagnostics: self.dont_buffer_diagnostics, report_delayed_bugs: self.report_delayed_bugs, macro_backtrace: self.macro_backtrace, - deduplicate_diagnostics: self.deduplicate_diagnostics.unwrap_or(true), + deduplicate_diagnostics: self.deduplicate_diagnostics, } } } @@ -748,25 +758,30 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo user_cfg } -pub fn build_target_config(opts: &Options, sp: &Handler) -> Config { +pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Config { let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { - sp.struct_fatal(&format!("Error loading target specification: {}", e)) - .help("Use `--print target-list` for a list of built-in targets") - .emit(); - FatalError.raise(); + early_error( + error_format, + &format!( + "Error loading target specification: {}. \ + Use `--print target-list` for a list of built-in targets", + e + ), + ) }); let ptr_width = match &target.target_pointer_width[..] { "16" => 16, "32" => 32, "64" => 64, - w => sp - .fatal(&format!( + w => early_error( + error_format, + &format!( "target specification was invalid: \ unrecognized target-pointer-width {}", w - )) - .raise(), + ), + ), }; Config { target, ptr_width } @@ -1011,7 +1026,15 @@ pub fn get_cmd_lint_options( let mut describe_lints = false; for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] { - for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { + for (passed_arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { + let arg_pos = if let lint::Forbid = level { + // HACK: forbid is always specified last, so it can't be overridden. + // FIXME: remove this once is + // fixed and `forbid` works as expected. + usize::max_value() + } else { + passed_arg_pos + }; if lint_name == "help" { describe_lints = true; } else { @@ -1286,33 +1309,6 @@ fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutp } } -fn select_incremental_path( - debugging_opts: &DebuggingOptions, - cg: &CodegenOptions, - error_format: ErrorOutputType, -) -> Option { - match (&debugging_opts.incremental, &cg.incremental) { - (Some(path1), Some(path2)) => { - if path1 != path2 { - early_error( - error_format, - &format!( - "conflicting paths for `-Z incremental` and \ - `-C incremental` specified: {} versus {}", - path1, path2 - ), - ); - } else { - Some(path1) - } - } - (Some(path), None) => Some(path), - (None, Some(path)) => Some(path), - (None, None) => None, - } - .map(PathBuf::from) -} - fn collect_print_requests( cg: &mut CodegenOptions, dopts: &mut DebuggingOptions, @@ -1328,18 +1324,6 @@ fn collect_print_requests( prints.push(PrintRequest::TargetFeatures); cg.target_feature = String::new(); } - if cg.relocation_model.as_ref().map_or(false, |s| s == "help") { - prints.push(PrintRequest::RelocationModels); - cg.relocation_model = None; - } - if cg.code_model.as_ref().map_or(false, |s| s == "help") { - prints.push(PrintRequest::CodeModels); - cg.code_model = None; - } - if dopts.tls_model.as_ref().map_or(false, |s| s == "help") { - prints.push(PrintRequest::TlsModels); - dopts.tls_model = None; - } prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { "crate-name" => PrintRequest::CrateName, @@ -1408,15 +1392,14 @@ fn parse_opt_level( if max_o > max_c { OptLevel::Default } else { - match cg.opt_level.as_ref().map(String::as_ref) { - None => OptLevel::No, - Some("0") => OptLevel::No, - Some("1") => OptLevel::Less, - Some("2") => OptLevel::Default, - Some("3") => OptLevel::Aggressive, - Some("s") => OptLevel::Size, - Some("z") => OptLevel::SizeMin, - Some(arg) => { + match cg.opt_level.as_ref() { + "0" => OptLevel::No, + "1" => OptLevel::Less, + "2" => OptLevel::Default, + "3" => OptLevel::Aggressive, + "s" => OptLevel::Size, + "z" => OptLevel::SizeMin, + arg => { early_error( error_format, &format!( @@ -1449,10 +1432,10 @@ fn select_debuginfo( DebugInfo::Full } else { match cg.debuginfo { - None | Some(0) => DebugInfo::None, - Some(1) => DebugInfo::Limited, - Some(2) => DebugInfo::Full, - Some(arg) => { + 0 => DebugInfo::None, + 1 => DebugInfo::Limited, + 2 => DebugInfo::Full, + arg => { early_error( error_format, &format!( @@ -1515,10 +1498,10 @@ fn parse_libs( } fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { - match dopts.borrowck.as_ref().map(|s| &s[..]) { - None | Some("migrate") => BorrowckMode::Migrate, - Some("mir") => BorrowckMode::Mir, - Some(m) => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), + match dopts.borrowck.as_ref() { + "migrate" => BorrowckMode::Migrate, + "mir" => BorrowckMode::Mir, + m => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), } } @@ -1668,7 +1651,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let output_types = parse_output_types(&debugging_opts, matches, error_format); let mut cg = build_codegen_options(matches, error_format); - let (disable_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( + let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( &output_types, matches, error_format, @@ -1677,7 +1660,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { check_thread_count(&debugging_opts, error_format); - let incremental = select_incremental_path(&debugging_opts, &cg, error_format); + let incremental = cg.incremental.as_ref().map(PathBuf::from); if debugging_opts.profile && incremental.is_some() { early_error( @@ -1685,6 +1668,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { "can't instrument with gcov profiling when compiling incrementally", ); } + if debugging_opts.profile { + match codegen_units { + Some(1) => {} + None => codegen_units = Some(1), + Some(_) => early_error( + error_format, + "can't instrument with gcov profiling with multiple codegen units", + ), + } + } if cg.profile_generate.enabled() && cg.profile_use.is_some() { early_error( @@ -1693,6 +1686,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } + if !cg.embed_bitcode { + match cg.lto { + LtoCli::No | LtoCli::Unspecified => {} + LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( + error_format, + "options `-C embed-bitcode=no` and `-C lto` are incompatible", + ), + } + } + let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); let cg = cg; @@ -1955,11 +1958,9 @@ impl PpMode { use PpMode::*; use PpSourceMode::*; match *self { - PpmSource(PpmNormal) | PpmSource(PpmEveryBodyLoops) | PpmSource(PpmIdentified) => false, + PpmSource(PpmNormal | PpmEveryBodyLoops | PpmIdentified) => false, - PpmSource(PpmExpanded) - | PpmSource(PpmExpandedIdentified) - | PpmSource(PpmExpandedHygiene) + PpmSource(PpmExpanded | PpmExpandedIdentified | PpmExpandedHygiene) | PpmHir(_) | PpmHirTree(_) | PpmMir @@ -1998,13 +1999,15 @@ impl PpMode { crate mod dep_tracking { use super::{ CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, - OutputTypes, Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion, + OutputTypes, Passes, Sanitizer, SourceFileHashAlgorithm, SwitchWithOptPath, + SymbolManglingVersion, }; use crate::lint; use crate::utils::NativeLibraryKind; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; - use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel, TargetTriple}; + use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; + use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel}; use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; use std::hash::Hash; @@ -2052,6 +2055,9 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); impl_dep_tracking_hash_via_hash!(Option>); impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); @@ -2076,6 +2082,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(LinkerPluginLto); impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_for_sortable_vec_of!(String); impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); diff --git a/src/librustc_session/filesearch.rs b/src/librustc_session/filesearch.rs index 05e6da43ea7a9..e98746231fb30 100644 --- a/src/librustc_session/filesearch.rs +++ b/src/librustc_session/filesearch.rs @@ -7,7 +7,7 @@ use std::env; use std::fs; use std::path::{Path, PathBuf}; -use crate::search_paths::{PathKind, SearchPath}; +use crate::search_paths::{PathKind, SearchPath, SearchPathFile}; use log::debug; use rustc_fs_util::fix_windows_verbatim_for_gcc; @@ -43,28 +43,28 @@ impl<'a> FileSearch<'a> { pub fn search(&self, mut pick: F) where - F: FnMut(&Path, PathKind) -> FileMatch, + F: FnMut(&SearchPathFile, PathKind) -> FileMatch, { for search_path in self.search_paths() { debug!("searching {}", search_path.dir.display()); - fn is_rlib(p: &Path) -> bool { - p.extension() == Some("rlib".as_ref()) + fn is_rlib(spf: &SearchPathFile) -> bool { + if let Some(f) = &spf.file_name_str { f.ends_with(".rlib") } else { false } } // Reading metadata out of rlibs is faster, and if we find both // an rlib and a dylib we only read one of the files of // metadata, so in the name of speed, bring all rlib files to // the front of the search list. - let files1 = search_path.files.iter().filter(|p| is_rlib(p)); - let files2 = search_path.files.iter().filter(|p| !is_rlib(p)); - for path in files1.chain(files2) { - debug!("testing {}", path.display()); - let maybe_picked = pick(path, search_path.kind); + let files1 = search_path.files.iter().filter(|spf| is_rlib(&spf)); + let files2 = search_path.files.iter().filter(|spf| !is_rlib(&spf)); + for spf in files1.chain(files2) { + debug!("testing {}", spf.path.display()); + let maybe_picked = pick(spf, search_path.kind); match maybe_picked { FileMatches => { - debug!("picked {}", path.display()); + debug!("picked {}", spf.path.display()); } FileDoesntMatch => { - debug!("rejected {}", path.display()); + debug!("rejected {}", spf.path.display()); } } } @@ -143,8 +143,8 @@ fn find_libdir(sysroot: &Path) -> Cow<'static, str> { // FIXME: This is a quick hack to make the rustc binary able to locate // Rust libraries in Linux environments where libraries might be installed // to lib64/lib32. This would be more foolproof by basing the sysroot off - // of the directory where librustc is located, rather than where the rustc - // binary is. + // of the directory where `librustc_driver` is located, rather than + // where the rustc binary is. // If --libdir is set during configuration to the value other than // "lib" (i.e., non-default), this value is used (see issue #16552). diff --git a/src/librustc_session/lib.rs b/src/librustc_session/lib.rs index cc4d525d62887..80b693fe1ab60 100644 --- a/src/librustc_session/lib.rs +++ b/src/librustc_session/lib.rs @@ -1,10 +1,5 @@ #![feature(crate_visibility_modifier)] -#![feature(test)] - -// Use the test crate here so we depend on getopts through it. This allow tools to link to both -// librustc_session and libtest. -extern crate getopts; -extern crate test as _; +#![feature(or_patterns)] pub mod cgu_reuse_tracker; pub mod utils; @@ -23,3 +18,5 @@ mod session; pub use session::*; pub mod output; + +pub use getopts; diff --git a/src/librustc_session/lint.rs b/src/librustc_session/lint.rs index 1162cff4e0b2b..b16d513d9239f 100644 --- a/src/librustc_session/lint.rs +++ b/src/librustc_session/lint.rs @@ -195,7 +195,7 @@ pub enum BuiltinLintDiagnostics { } /// Lints that are buffered up early on in the `Session` before the -/// `LintLevels` is calculated. These are later passed to `librustc`. +/// `LintLevels` is calculated. #[derive(PartialEq)] pub struct BufferedEarlyLint { /// The span of code that we are linting on. @@ -207,7 +207,8 @@ pub struct BufferedEarlyLint { /// The `NodeId` of the AST node that generated the lint. pub node_id: NodeId, - /// A lint Id that can be passed to `rustc::lint::Lint::from_parser_lint_id`. + /// A lint Id that can be passed to + /// `rustc_lint::early::EarlyContextAndPass::check_id`. pub lint_id: LintId, /// Customization of the `DiagnosticBuilder<'_>` for the lint. diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index f8a4e024605aa..3d03e46683ed5 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -386,6 +386,18 @@ declare_lint! { "failures in resolving intra-doc link targets" } +declare_lint! { + pub INVALID_CODEBLOCK_ATTRIBUTE, + Warn, + "codeblock attribute looks a lot like a known one" +} + +declare_lint! { + pub MISSING_CRATE_LEVEL_DOCS, + Allow, + "detects crates with no crate-level documentation" +} + declare_lint! { pub MISSING_DOC_CODE_EXAMPLES, Allow, @@ -446,7 +458,7 @@ declare_lint! { pub INDIRECT_STRUCTURAL_MATCH, // defaulting to allow until rust-lang/rust#62614 is fixed. Allow, - "pattern with const indirectly referencing non-`#[structural_match]` type", + "pattern with const indirectly referencing non-structural-match type", @future_incompatible = FutureIncompatibleInfo { reference: "issue #62411 ", edition: None, @@ -496,6 +508,12 @@ declare_lint! { "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", } +declare_lint! { + pub ASM_SUB_REGISTER, + Warn, + "using only a subset of a register for inline asm inputs", +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -547,6 +565,8 @@ declare_lint_pass! { UNSTABLE_NAME_COLLISIONS, IRREFUTABLE_LET_PATTERNS, INTRA_DOC_LINK_RESOLUTION_FAILURE, + INVALID_CODEBLOCK_ATTRIBUTE, + MISSING_CRATE_LEVEL_DOCS, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, WHERE_CLAUSES_OBJECT_SAFETY, @@ -562,6 +582,7 @@ declare_lint_pass! { INDIRECT_STRUCTURAL_MATCH, SOFT_UNSTABLE, INLINE_NO_SANITIZE, + ASM_SUB_REGISTER, ] } diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index a1ecf4e8528be..a8d213a8663c1 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -5,11 +5,12 @@ use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLibraryKind; -use rustc_target::spec::TargetTriple; -use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; +use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; +use rustc_target::spec::{RelocModel, RelroLevel, TargetTriple, TlsModel}; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; +use rustc_span::SourceFileHashAlgorithm; use std::collections::BTreeMap; @@ -180,27 +181,22 @@ macro_rules! options { let value = iter.next(); let option_to_lookup = key.replace("-", "_"); let mut found = false; - for &(candidate, setter, opt_type_desc, _) in $stat { + for &(candidate, setter, type_desc, _) in $stat { if option_to_lookup != candidate { continue } if !setter(&mut op, value) { - match (value, opt_type_desc) { - (Some(..), None) => { - early_error(error_format, &format!("{} option `{}` takes no \ - value", $outputname, key)) - } - (None, Some(type_desc)) => { + match value { + None => { early_error(error_format, &format!("{0} option `{1}` requires \ {2} ({3} {1}=)", $outputname, key, type_desc, $prefix)) } - (Some(value), Some(type_desc)) => { + Some(value) => { early_error(error_format, &format!("incorrect value `{}` for {} \ option `{}` - {} was expected", value, $outputname, key, type_desc)) } - (None, None) => panic!() } } found = true; @@ -230,118 +226,122 @@ macro_rules! options { } pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool; - pub const $stat: &[(&str, $setter_name, Option<&str>, &str)] = + pub const $stat: &[(&str, $setter_name, &str, &str)] = &[ $( (stringify!($opt), $mod_set::$opt, $mod_desc::$parse, $desc) ),* ]; #[allow(non_upper_case_globals, dead_code)] mod $mod_desc { - pub const parse_bool: Option<&str> = None; - pub const parse_opt_bool: Option<&str> = - Some("one of: `y`, `yes`, `on`, `n`, `no`, or `off`"); - pub const parse_string: Option<&str> = Some("a string"); - pub const parse_string_push: Option<&str> = Some("a string"); - pub const parse_pathbuf_push: Option<&str> = Some("a path"); - pub const parse_opt_string: Option<&str> = Some("a string"); - pub const parse_opt_pathbuf: Option<&str> = Some("a path"); - pub const parse_list: Option<&str> = Some("a space-separated list of strings"); - pub const parse_opt_list: Option<&str> = Some("a space-separated list of strings"); - pub const parse_opt_comma_list: Option<&str> = Some("a comma-separated list of strings"); - pub const parse_threads: Option<&str> = Some("a number"); - pub const parse_uint: Option<&str> = Some("a number"); - pub const parse_passes: Option<&str> = - Some("a space-separated list of passes, or `all`"); - pub const parse_opt_uint: Option<&str> = - Some("a number"); - pub const parse_panic_strategy: Option<&str> = - Some("either `unwind` or `abort`"); - pub const parse_relro_level: Option<&str> = - Some("one of: `full`, `partial`, or `off`"); - pub const parse_sanitizer: Option<&str> = - Some("one of: `address`, `leak`, `memory` or `thread`"); - pub const parse_sanitizer_list: Option<&str> = - Some("comma separated list of sanitizers"); - pub const parse_sanitizer_memory_track_origins: Option<&str> = None; - pub const parse_cfguard: Option<&str> = - Some("either `disabled`, `nochecks`, or `checks`"); - pub const parse_linker_flavor: Option<&str> = - Some(::rustc_target::spec::LinkerFlavor::one_of()); - pub const parse_optimization_fuel: Option<&str> = - Some("crate=integer"); - pub const parse_unpretty: Option<&str> = - Some("`string` or `string=string`"); - pub const parse_treat_err_as_bug: Option<&str> = - Some("either no value or a number bigger than 0"); - pub const parse_lto: Option<&str> = - Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \ - `fat`, or omitted"); - pub const parse_linker_plugin_lto: Option<&str> = - Some("either a boolean (`yes`, `no`, `on`, `off`, etc), \ - or the path to the linker plugin"); - pub const parse_switch_with_opt_path: Option<&str> = - Some("an optional path to the profiling data output directory"); - pub const parse_merge_functions: Option<&str> = - Some("one of: `disabled`, `trampolines`, or `aliases`"); - pub const parse_symbol_mangling_version: Option<&str> = - Some("either `legacy` or `v0` (RFC 2603)"); + pub const parse_no_flag: &str = "no value"; + pub const parse_bool: &str = "one of: `y`, `yes`, `on`, `n`, `no`, or `off`"; + pub const parse_opt_bool: &str = parse_bool; + pub const parse_string: &str = "a string"; + pub const parse_opt_string: &str = parse_string; + pub const parse_string_push: &str = parse_string; + pub const parse_opt_pathbuf: &str = "a path"; + pub const parse_pathbuf_push: &str = parse_opt_pathbuf; + pub const parse_list: &str = "a space-separated list of strings"; + pub const parse_opt_list: &str = parse_list; + pub const parse_opt_comma_list: &str = "a comma-separated list of strings"; + pub const parse_uint: &str = "a number"; + pub const parse_opt_uint: &str = parse_uint; + pub const parse_threads: &str = parse_uint; + pub const parse_passes: &str = "a space-separated list of passes, or `all`"; + pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; + pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; + pub const parse_sanitizer: &str = "one of: `address`, `leak`, `memory` or `thread`"; + pub const parse_sanitizer_list: &str = "comma separated list of sanitizers"; + pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; + pub const parse_cfguard: &str = "either `disabled`, `nochecks`, or `checks`"; + pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; + pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of(); + pub const parse_optimization_fuel: &str = "crate=integer"; + pub const parse_unpretty: &str = "`string` or `string=string`"; + pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; + pub const parse_lto: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted"; + pub const parse_linker_plugin_lto: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), or the path to the linker plugin"; + pub const parse_switch_with_opt_path: &str = + "an optional path to the profiling data output directory"; + pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`"; + pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)"; + pub const parse_src_file_hash: &str = "either `md5` or `sha1`"; + pub const parse_relocation_model: &str = + "one of supported relocation models (`rustc --print relocation-models`)"; + pub const parse_code_model: &str = + "one of supported code models (`rustc --print code-models`)"; + pub const parse_tls_model: &str = + "one of supported TLS models (`rustc --print tls-models`)"; + pub const parse_target_feature: &str = parse_string; } #[allow(dead_code)] mod $mod_set { - use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, - SymbolManglingVersion, CFGuard}; - use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; - use std::path::PathBuf; + use super::*; use std::str::FromStr; + // Sometimes different options need to build a common structure. + // That structure can kept in one of the options' fields, the others become dummy. + macro_rules! redirect_field { + ($cg:ident.link_arg) => { $cg.link_args }; + ($cg:ident.pre_link_arg) => { $cg.pre_link_args }; + ($cg:ident.$field:ident) => { $cg.$field }; + } + $( pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool { - $parse(&mut cg.$opt, v) + $parse(&mut redirect_field!(cg.$opt), v) } )* - fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { + /// This is for boolean options that don't take a value and start with + /// `no-`. This style of option is deprecated. + fn parse_no_flag(slot: &mut bool, v: Option<&str>) -> bool { match v { - Some(..) => false, None => { *slot = true; true } + Some(_) => false, } } - fn parse_opt_bool(slot: &mut Option, v: Option<&str>) -> bool { + /// Use this for any boolean option that has a static default. + fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { match v { - Some(s) => { - match s { - "n" | "no" | "off" => { - *slot = Some(false); - } - "y" | "yes" | "on" => { - *slot = Some(true); - } - _ => { return false; } - } + Some("y") | Some("yes") | Some("on") | None => { *slot = true; true } + Some("n") | Some("no") | Some("off") => { *slot = false; true } + _ => false, + } + } - true - }, - None => { *slot = Some(true); true } + /// Use this for any boolean option that lacks a static default. (The + /// actions taken when such an option is not specified will depend on + /// other factors, such as other options, or target options.) + fn parse_opt_bool(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some("y") | Some("yes") | Some("on") | None => { *slot = Some(true); true } + Some("n") | Some("no") | Some("off") => { *slot = Some(false); true } + _ => false, } } - fn parse_opt_string(slot: &mut Option, v: Option<&str>) -> bool { + /// Use this for any string option that has a static default. + fn parse_string(slot: &mut String, v: Option<&str>) -> bool { match v { - Some(s) => { *slot = Some(s.to_string()); true }, + Some(s) => { *slot = s.to_string(); true }, None => false, } } - fn parse_opt_pathbuf(slot: &mut Option, v: Option<&str>) -> bool { + /// Use this for any string option that lacks a static default. + fn parse_opt_string(slot: &mut Option, v: Option<&str>) -> bool { match v { - Some(s) => { *slot = Some(PathBuf::from(s)); true }, + Some(s) => { *slot = Some(s.to_string()); true }, None => false, } } - fn parse_string(slot: &mut String, v: Option<&str>) -> bool { + fn parse_opt_pathbuf(slot: &mut Option, v: Option<&str>) -> bool { match v { - Some(s) => { *slot = s.to_string(); true }, + Some(s) => { *slot = Some(PathBuf::from(s)); true }, None => false, } } @@ -403,6 +403,7 @@ macro_rules! options { } } + /// Use this for any uint option that has a static default. fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool { match v.and_then(|s| s.parse().ok()) { Some(i) => { *slot = i; true }, @@ -410,10 +411,11 @@ macro_rules! options { } } + /// Use this for any uint option that lacks a static default. fn parse_opt_uint(slot: &mut Option, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.parse().ok(); slot.is_some() } - None => { *slot = None; false } + None => false } } @@ -484,21 +486,24 @@ macro_rules! options { } fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool { - match v.map(|s| s.parse()) { - None => { - *slot = 2; - true - } - Some(Ok(i)) if i <= 2 => { - *slot = i; - true - } - _ => { - false - } + match v { + Some("2") | None => { *slot = 2; true } + Some("1") => { *slot = 1; true } + Some("0") => { *slot = 0; true } + Some(_) => false, } } + fn parse_strip(slot: &mut Strip, v: Option<&str>) -> bool { + match v { + Some("none") => *slot = Strip::None, + Some("debuginfo") => *slot = Strip::Debuginfo, + Some("symbols") => *slot = Strip::Symbols, + _ => return false, + } + true + } + fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool { match v { Some("disabled") => *slot = CFGuard::Disabled, @@ -608,6 +613,31 @@ macro_rules! options { true } + fn parse_relocation_model(slot: &mut Option, v: Option<&str>) -> bool { + match v.and_then(|s| RelocModel::from_str(s).ok()) { + Some(relocation_model) => *slot = Some(relocation_model), + None if v == Some("default") => *slot = None, + _ => return false, + } + true + } + + fn parse_code_model(slot: &mut Option, v: Option<&str>) -> bool { + match v.and_then(|s| CodeModel::from_str(s).ok()) { + Some(code_model) => *slot = Some(code_model), + _ => return false, + } + true + } + + fn parse_tls_model(slot: &mut Option, v: Option<&str>) -> bool { + match v.and_then(|s| TlsModel::from_str(s).ok()) { + Some(tls_model) => *slot = Some(tls_model), + _ => return false, + } + true + } + fn parse_symbol_mangling_version( slot: &mut SymbolManglingVersion, v: Option<&str>, @@ -619,217 +649,185 @@ macro_rules! options { }; true } + + fn parse_src_file_hash(slot: &mut Option, v: Option<&str>) -> bool { + match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) { + Some(hash_kind) => *slot = Some(hash_kind), + _ => return false, + } + true + } + + fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool { + match v { + Some(s) => { + if !slot.is_empty() { + slot.push_str(","); + } + slot.push_str(s); + true + } + None => false, + } + } } ) } options! {CodegenOptions, CodegenSetter, basic_codegen_options, build_codegen_options, "C", "codegen", CG_OPTIONS, cg_type_desc, cgsetters, - ar: Option = (None, parse_opt_string, [UNTRACKED], + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs + // - src/doc/rustc/src/codegen-options/index.md + + ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), - linker: Option = (None, parse_opt_pathbuf, [UNTRACKED], - "system linker to link outputs with"), - link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], - "a single extra argument to append to the linker invocation (can be used several times)"), - link_args: Option> = (None, parse_opt_list, [UNTRACKED], - "extra arguments to append to the linker invocation (space separated)"), - link_dead_code: bool = (false, parse_bool, [UNTRACKED], - "don't let linker strip dead code (turning it on can be used for code coverage)"), - lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], - "perform LLVM link-time optimizations"), - target_cpu: Option = (None, parse_opt_string, [TRACKED], - "select target processor (`rustc --print target-cpus` for details)"), - target_feature: String = (String::new(), parse_string, [TRACKED], - "target specific attributes. (`rustc --print target-features` for details). \ - This feature is unsafe."), - passes: Vec = (Vec::new(), parse_list, [TRACKED], - "a list of extra LLVM passes to run (space separated)"), - llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], - "a list of arguments to pass to LLVM (space separated)"), - save_temps: bool = (false, parse_bool, [UNTRACKED], - "save all temporary output files during compilation"), - rpath: bool = (false, parse_bool, [UNTRACKED], - "set rpath values in libs/exes"), - overflow_checks: Option = (None, parse_opt_bool, [TRACKED], - "use overflow checks for integer arithmetic"), - no_prepopulate_passes: bool = (false, parse_bool, [TRACKED], - "don't pre-populate the pass manager with a list of passes"), - no_vectorize_loops: bool = (false, parse_bool, [TRACKED], - "don't run the loop vectorization optimization passes"), - no_vectorize_slp: bool = (false, parse_bool, [TRACKED], - "don't run LLVM's SLP vectorization pass"), - soft_float: bool = (false, parse_bool, [TRACKED], - "use soft float ABI (*eabihf targets only)"), - prefer_dynamic: bool = (false, parse_bool, [TRACKED], - "prefer dynamic linking to static linking"), - no_integrated_as: bool = (false, parse_bool, [TRACKED], - "use an external assembler rather than LLVM's integrated one"), - no_redzone: Option = (None, parse_opt_bool, [TRACKED], - "disable the use of the redzone"), - relocation_model: Option = (None, parse_opt_string, [TRACKED], - "choose the relocation model to use (`rustc --print relocation-models` for details)"), - code_model: Option = (None, parse_opt_string, [TRACKED], + code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), - metadata: Vec = (Vec::new(), parse_list, [TRACKED], - "metadata to mangle symbol names with"), - extra_filename: String = (String::new(), parse_string, [UNTRACKED], - "extra data to put in each output filename"), codegen_units: Option = (None, parse_opt_uint, [UNTRACKED], "divide crate into N units to optimize in parallel"), - remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED], - "print remarks for these optimization passes (space separated, or \"all\")"), - no_stack_check: bool = (false, parse_bool, [UNTRACKED], - "the `--no-stack-check` flag is deprecated and does nothing"), - debuginfo: Option = (None, parse_opt_uint, [TRACKED], - "debug info emission level, 0 = no debug info, 1 = line tables only, \ - 2 = full debug info with variable and type information"), - opt_level: Option = (None, parse_opt_string, [TRACKED], - "optimize with possible levels 0-3, s, or z"), - force_frame_pointers: Option = (None, parse_opt_bool, [TRACKED], - "force use of the frame pointers"), debug_assertions: Option = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(debug_assertions)` directive"), - inline_threshold: Option = (None, parse_opt_uint, [TRACKED], - "set the threshold for inlining a function (default: 225)"), - panic: Option = (None, parse_panic_strategy, - [TRACKED], "panic strategy to compile crate with"), + debuginfo: usize = (0, parse_uint, [TRACKED], + "debug info emission level (0 = no debug info, 1 = line tables only, \ + 2 = full debug info with variable and type information; default: 0)"), + default_linker_libraries: bool = (false, parse_bool, [UNTRACKED], + "allow the linker to link its default libraries (default: no)"), + embed_bitcode: bool = (true, parse_bool, [TRACKED], + "emit bitcode in rlibs (default: yes)"), + extra_filename: String = (String::new(), parse_string, [UNTRACKED], + "extra data to put in each output filename"), + force_frame_pointers: Option = (None, parse_opt_bool, [TRACKED], + "force use of the frame pointers"), + force_unwind_tables: Option = (None, parse_opt_bool, [TRACKED], + "force use of unwind tables"), incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), - default_linker_libraries: Option = (None, parse_opt_bool, [UNTRACKED], - "allow the linker to link its default libraries"), + inline_threshold: Option = (None, parse_opt_uint, [TRACKED], + "set the threshold for inlining a function"), + link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED], + "a single extra argument to append to the linker invocation (can be used several times)"), + link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], + "extra arguments to append to the linker invocation (space separated)"), + link_dead_code: bool = (false, parse_bool, [UNTRACKED], + "keep dead code at link time (useful for code coverage) (default: no)"), + linker: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "system linker to link outputs with"), linker_flavor: Option = (None, parse_linker_flavor, [UNTRACKED], - "linker flavor"), + "linker flavor"), linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled, parse_linker_plugin_lto, [TRACKED], - "generate build artifacts that are compatible with linker-based LTO."), + "generate build artifacts that are compatible with linker-based LTO"), + llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], + "a list of arguments to pass to LLVM (space separated)"), + lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], + "perform LLVM link-time optimizations"), + metadata: Vec = (Vec::new(), parse_list, [TRACKED], + "metadata to mangle symbol names with"), + no_prepopulate_passes: bool = (false, parse_no_flag, [TRACKED], + "give an empty list of passes to the pass manager"), + no_redzone: Option = (None, parse_opt_bool, [TRACKED], + "disable the use of the redzone"), + no_stack_check: bool = (false, parse_no_flag, [UNTRACKED], + "this option is deprecated and does nothing"), + no_vectorize_loops: bool = (false, parse_no_flag, [TRACKED], + "disable loop vectorization optimization passes"), + no_vectorize_slp: bool = (false, parse_no_flag, [TRACKED], + "disable LLVM's SLP vectorization pass"), + opt_level: String = ("0".to_string(), parse_string, [TRACKED], + "optimization level (0-3, s, or z; default: 0)"), + overflow_checks: Option = (None, parse_opt_bool, [TRACKED], + "use overflow checks for integer arithmetic"), + panic: Option = (None, parse_panic_strategy, [TRACKED], + "panic strategy to compile crate with"), + passes: Vec = (Vec::new(), parse_list, [TRACKED], + "a list of extra LLVM passes to run (space separated)"), + prefer_dynamic: bool = (false, parse_bool, [TRACKED], + "prefer dynamic linking to static linking (default: no)"), profile_generate: SwitchWithOptPath = (SwitchWithOptPath::Disabled, parse_switch_with_opt_path, [TRACKED], "compile the program with profiling instrumentation"), profile_use: Option = (None, parse_opt_pathbuf, [TRACKED], "use the given `.profdata` file for profile-guided optimization"), + relocation_model: Option = (None, parse_relocation_model, [TRACKED], + "control generation of position-independent code (PIC) \ + (`rustc --print relocation-models` for details)"), + remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED], + "print remarks for these optimization passes (space separated, or \"all\")"), + rpath: bool = (false, parse_bool, [UNTRACKED], + "set rpath values in libs/exes (default: no)"), + save_temps: bool = (false, parse_bool, [UNTRACKED], + "save all temporary output files during compilation (default: no)"), + soft_float: bool = (false, parse_bool, [TRACKED], + "use soft float ABI (*eabihf targets only) (default: no)"), + target_cpu: Option = (None, parse_opt_string, [TRACKED], + "select target processor (`rustc --print target-cpus` for details)"), + target_feature: String = (String::new(), parse_target_feature, [TRACKED], + "target specific attributes. (`rustc --print target-features` for details). \ + This feature is unsafe."), + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs + // - src/doc/rustc/src/codegen-options/index.md } options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, build_debugging_options, "Z", "debugging", DB_OPTIONS, db_type_desc, dbsetters, - codegen_backend: Option = (None, parse_opt_string, [TRACKED], - "the backend to use"), - verbose: bool = (false, parse_bool, [UNTRACKED], - "in general, enable more debug printouts"), - span_free_formats: bool = (false, parse_bool, [UNTRACKED], - "when debug-printing compiler state, do not include spans"), // o/w tests have closure@path - identify_regions: bool = (false, parse_bool, [UNTRACKED], - "make unnamed regions display as '# (where # is some non-ident unique id)"), - borrowck: Option = (None, parse_opt_string, [UNTRACKED], - "select which borrowck is used (`mir` or `migrate`)"), - time_passes: bool = (false, parse_bool, [UNTRACKED], - "measure time of each rustc pass"), - time: bool = (false, parse_bool, [UNTRACKED], - "measure time of rustc processes"), - time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], - "measure time of each LLVM pass"), - llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], - "generate JSON tracing data file from LLVM data"), - input_stats: bool = (false, parse_bool, [UNTRACKED], - "gather statistics about the input"), + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs + + allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], + "only allow the listed language features to be enabled in code (space separated)"), + always_encode_mir: bool = (false, parse_bool, [TRACKED], + "encode MIR of all functions into the crate metadata (default: no)"), asm_comments: bool = (false, parse_bool, [TRACKED], - "generate comments into the assembly (may change behavior)"), - verify_llvm_ir: bool = (false, parse_bool, [TRACKED], - "verify LLVM IR"), - borrowck_stats: bool = (false, parse_bool, [UNTRACKED], - "gather borrowck statistics"), - no_landing_pads: bool = (false, parse_bool, [TRACKED], - "omit landing pads for unwinding"), - fewer_names: bool = (false, parse_bool, [TRACKED], - "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR)"), - meta_stats: bool = (false, parse_bool, [UNTRACKED], - "gather metadata statistics"), - print_link_args: bool = (false, parse_bool, [UNTRACKED], - "print the arguments passed to the linker"), - print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], - "prints the LLVM optimization passes being run"), + "generate comments into the assembly (may change behavior) (default: no)"), ast_json: bool = (false, parse_bool, [UNTRACKED], - "print the AST as JSON and halt"), - // We default to 1 here since we want to behave like - // a sequential compiler for now. This'll likely be adjusted - // in the future. Note that -Zthreads=0 is the way to get - // the num_cpus behavior. - threads: usize = (1, parse_threads, [UNTRACKED], - "use a thread pool with N threads"), + "print the AST as JSON and halt (default: no)"), ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED], - "print the pre-expansion AST as JSON and halt"), - ls: bool = (false, parse_bool, [UNTRACKED], - "list the symbols defined by a library crate"), - save_analysis: bool = (false, parse_bool, [UNTRACKED], - "write syntax and type analysis (in JSON format) information, in \ - addition to normal output"), - print_region_graph: bool = (false, parse_bool, [UNTRACKED], - "prints region inference graph. \ - Use with RUST_REGION_GRAPH=help for more info"), - parse_only: bool = (false, parse_bool, [UNTRACKED], - "parse only; do not compile, assemble, or link"), - dual_proc_macros: bool = (false, parse_bool, [TRACKED], - "load proc macros for both target and host, but only link to the target"), - no_codegen: bool = (false, parse_bool, [TRACKED], - "run all passes except codegen; no output"), - treat_err_as_bug: Option = (None, parse_treat_err_as_bug, [TRACKED], - "treat error number `val` that occurs as bug"), - report_delayed_bugs: bool = (false, parse_bool, [TRACKED], - "immediately print bugs registered with `delay_span_bug`"), - macro_backtrace: bool = (false, parse_bool, [UNTRACKED], - "show macro backtraces"), - teach: bool = (false, parse_bool, [TRACKED], - "show extended diagnostic help"), - terminal_width: Option = (None, parse_opt_uint, [UNTRACKED], - "set the current terminal width"), - panic_abort_tests: bool = (false, parse_bool, [TRACKED], - "support compiling tests with panic=abort"), + "print the pre-expansion AST as JSON and halt (default: no)"), + binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], + "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ + (default: no)"), + borrowck: String = ("migrate".to_string(), parse_string, [UNTRACKED], + "select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"), + borrowck_stats: bool = (false, parse_bool, [UNTRACKED], + "gather borrowck statistics (default: no)"), + chalk: bool = (false, parse_bool, [TRACKED], + "enable the experimental Chalk-based trait solving engine"), + codegen_backend: Option = (None, parse_opt_string, [TRACKED], + "the backend to use"), + control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [UNTRACKED], + "use Windows Control Flow Guard (`disabled`, `nochecks` or `checks`)"), + crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], + "inject the given attribute in the crate"), + debug_macros: bool = (false, parse_bool, [TRACKED], + "emit line numbers debug info inside macros (default: no)"), + deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], + "deduplicate identical diagnostics (default: yes)"), + dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], + "in dep-info output, omit targets for tracking dependencies of the dep-info files \ + themselves (default: no)"), dep_tasks: bool = (false, parse_bool, [UNTRACKED], - "print tasks that execute and the color their dep node gets (requires debug build)"), - incremental: Option = (None, parse_opt_string, [UNTRACKED], - "enable incremental compilation (experimental)"), - incremental_queries: bool = (true, parse_bool, [UNTRACKED], - "enable incremental compilation support for queries (experimental)"), - incremental_info: bool = (false, parse_bool, [UNTRACKED], - "print high-level information about incremental reuse (or the lack thereof)"), - incremental_dump_hash: bool = (false, parse_bool, [UNTRACKED], - "dump hash information in textual format to stdout"), - incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], - "verify incr. comp. hashes of green query instances"), - incremental_ignore_spans: bool = (false, parse_bool, [UNTRACKED], - "ignore spans during ICH computation -- used for testing"), - instrument_mcount: bool = (false, parse_bool, [TRACKED], - "insert function instrument code for mcount-based tracing"), + "print tasks that execute and the color their dep node gets (requires debug build) \ + (default: no)"), + dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], + "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ + (default: no)"), + dual_proc_macros: bool = (false, parse_bool, [TRACKED], + "load proc macros for both target and host, but only link to the target (default: no)"), dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], - "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), - query_dep_graph: bool = (false, parse_bool, [UNTRACKED], - "enable queries of the dependency graph for regression testing"), - no_analysis: bool = (false, parse_bool, [UNTRACKED], - "parse and expand the source, but run no analysis"), - unstable_options: bool = (false, parse_bool, [UNTRACKED], - "adds unstable command line options to rustc interface"), - force_overflow_checks: Option = (None, parse_opt_bool, [TRACKED], - "force overflow checks on or off"), - trace_macros: bool = (false, parse_bool, [UNTRACKED], - "for every macro invocation, print its name and arguments"), - debug_macros: bool = (false, parse_bool, [TRACKED], - "emit line numbers debug info inside macros"), - generate_arange_section: bool = (true, parse_bool, [TRACKED], - "generate DWARF address ranges for faster lookups"), - keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], - "don't clear the hygiene data after analysis"), - keep_ast: bool = (false, parse_bool, [UNTRACKED], - "keep the AST after lowering it to HIR"), - show_span: Option = (None, parse_opt_string, [TRACKED], - "show spans for compiler debugging (expr|pat|ty)"), - print_type_sizes: bool = (false, parse_bool, [UNTRACKED], - "print layout information for each type encountered"), - print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], - "print the result of the monomorphization collection pass"), - mir_opt_level: usize = (1, parse_uint, [TRACKED], - "set the MIR optimization level (0-3, default: 1)"), - mutable_noalias: Option = (None, parse_opt_bool, [TRACKED], - "emit noalias metadata for mutable references (default: no)"), + "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \ + (default: no)"), dump_mir: Option = (None, parse_opt_string, [UNTRACKED], "dump MIR state to file. `val` is used to select which passes and functions to dump. For example: @@ -837,71 +835,202 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, `foo` matches all passes for functions whose name contains 'foo', `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', `foo | bar` all passes for function names containing 'foo' or 'bar'."), - - dump_mir_dir: String = (String::from("mir_dump"), parse_string, [UNTRACKED], - "the directory the MIR is dumped into"), - dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], - "in addition to `.mir` files, create graphviz `.dot` files"), + dump_mir_dataflow: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files with dataflow results \ + (default: no)"), + dump_mir_dir: String = ("mir_dump".to_string(), parse_string, [UNTRACKED], + "the directory the MIR is dumped into (default: `mir_dump`)"), dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], - "if set, exclude the pass number when dumping MIR (used in tests)"), - mir_emit_retag: bool = (false, parse_bool, [TRACKED], - "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0"), - perf_stats: bool = (false, parse_bool, [UNTRACKED], - "print some performance-related statistics"), - query_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about the query system"), - hir_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about AST and HIR"), - always_encode_mir: bool = (false, parse_bool, [TRACKED], - "encode MIR of all functions into the crate metadata"), - json_rendered: Option = (None, parse_opt_string, [UNTRACKED], - "describes how to render the `rendered` field of json diagnostics"), - unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], - "take the breaks off const evaluation. NOTE: this is unsound"), - osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], - "pass `-install_name @rpath/...` to the macOS linker"), - sanitizer: Option = (None, parse_sanitizer, [TRACKED], - "use a sanitizer"), - sanitizer_recover: Vec = (vec![], parse_sanitizer_list, [TRACKED], - "Enable recovery for selected sanitizers"), - sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], - "Enable origins tracking in MemorySanitizer"), + "exclude the pass number when dumping MIR (used in tests) (default: no)"), + dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), + emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], + "emit a section containing stack size metadata (default: no)"), + fewer_names: bool = (false, parse_bool, [TRACKED], + "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ + (default: no)"), + force_overflow_checks: Option = (None, parse_opt_bool, [TRACKED], + "force overflow checks on or off"), + force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], + "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), - print_fuel: Option = (None, parse_opt_string, [TRACKED], - "make rustc print the total optimization fuel used by a crate"), - force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], - "force all crates to be `rustc_private` unstable"), - pre_link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], + hir_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about AST and HIR (default: no)"), + human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], + "generate human-readable, predictable names for codegen units (default: no)"), + identify_regions: bool = (false, parse_bool, [UNTRACKED], + "display unnamed regions as `'`, using a non-ident unique id (default: no)"), + incremental_ignore_spans: bool = (false, parse_bool, [UNTRACKED], + "ignore spans during ICH computation -- used for testing (default: no)"), + incremental_info: bool = (false, parse_bool, [UNTRACKED], + "print high-level information about incremental reuse (or the lack thereof) \ + (default: no)"), + incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], + "verify incr. comp. hashes of green query instances (default: no)"), + inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], + "control whether `#[inline]` functions are in all CGUs"), + input_stats: bool = (false, parse_bool, [UNTRACKED], + "gather statistics about the input (default: no)"), + insert_sideeffect: bool = (false, parse_bool, [TRACKED], + "fix undefined behavior when a thread doesn't eventually make progress \ + (such as entering an empty infinite loop) by inserting llvm.sideeffect \ + (default: no)"), + instrument_mcount: bool = (false, parse_bool, [TRACKED], + "insert function instrument code for mcount-based tracing (default: no)"), + keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], + "keep hygiene data after analysis (default: no)"), + link_native_libraries: bool = (true, parse_bool, [UNTRACKED], + "link native libraries in the linker invocation (default: yes)"), + link_only: bool = (false, parse_bool, [TRACKED], + "link the `.rlink` file generated by `-Z no-link` (default: no)"), + llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], + "generate JSON tracing data file from LLVM data (default: no)"), + ls: bool = (false, parse_bool, [UNTRACKED], + "list the symbols defined by a library crate (default: no)"), + macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show macro backtraces (default: no)"), + merge_functions: Option = (None, parse_merge_functions, [TRACKED], + "control the operation of the MergeFunctions LLVM pass, taking \ + the same values as the target option of the same name"), + meta_stats: bool = (false, parse_bool, [UNTRACKED], + "gather metadata statistics (default: no)"), + mir_emit_retag: bool = (false, parse_bool, [TRACKED], + "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ + (default: no)"), + mir_opt_level: usize = (1, parse_uint, [TRACKED], + "MIR optimization level (0-3; default: 1)"), + mutable_noalias: bool = (false, parse_bool, [TRACKED], + "emit noalias metadata for mutable references (default: no)"), + new_llvm_pass_manager: bool = (false, parse_bool, [TRACKED], + "use new LLVM pass manager (default: no)"), + nll_facts: bool = (false, parse_bool, [UNTRACKED], + "dump facts from NLL analysis into side files (default: no)"), + no_analysis: bool = (false, parse_no_flag, [UNTRACKED], + "parse and expand the source, but run no analysis"), + no_codegen: bool = (false, parse_no_flag, [TRACKED], + "run all passes except codegen; no output"), + no_generate_arange_section: bool = (false, parse_no_flag, [TRACKED], + "omit DWARF address ranges that give faster lookups"), + no_interleave_lints: bool = (false, parse_no_flag, [UNTRACKED], + "execute lints separately; allows benchmarking individual lints"), + no_leak_check: bool = (false, parse_no_flag, [UNTRACKED], + "disable the 'leak check' for subtyping; unsound, but useful for tests"), + no_link: bool = (false, parse_no_flag, [TRACKED], + "compile without linking"), + no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED], + "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), + no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], + "prevent automatic injection of the profiler_builtins crate"), + osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], + "pass `-install_name @rpath/...` to the macOS linker (default: no)"), + panic_abort_tests: bool = (false, parse_bool, [TRACKED], + "support compiling tests with panic=abort (default: no)"), + parse_only: bool = (false, parse_bool, [UNTRACKED], + "parse only; do not compile, assemble, or link (default: no)"), + perf_stats: bool = (false, parse_bool, [UNTRACKED], + "print some performance-related statistics (default: no)"), + plt: Option = (None, parse_opt_bool, [TRACKED], + "whether to use the PLT when calling into shared libraries; + only has effect for PIC code on systems with ELF binaries + (default: PLT is disabled if full relro is enabled)"), + polonius: bool = (false, parse_bool, [UNTRACKED], + "enable polonius-based borrow-checker (default: no)"), + pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), - pre_link_args: Option> = (None, parse_opt_list, [UNTRACKED], + pre_link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to prepend to the linker invocation (space separated)"), + print_fuel: Option = (None, parse_opt_string, [TRACKED], + "make rustc print the total optimization fuel used by a crate"), + print_link_args: bool = (false, parse_bool, [UNTRACKED], + "print the arguments passed to the linker (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"), + print_region_graph: bool = (false, parse_bool, [UNTRACKED], + "prints region inference graph. \ + Use with RUST_REGION_GRAPH=help for more info (default: no)"), + print_type_sizes: bool = (false, parse_bool, [UNTRACKED], + "print layout information for each type encountered (default: no)"), profile: bool = (false, parse_bool, [TRACKED], - "insert profiling code"), + "insert profiling code (default: no)"), + query_dep_graph: bool = (false, parse_bool, [UNTRACKED], + "enable queries of the dependency graph for regression testing (default: no)"), + query_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about the query system (default: no)"), relro_level: Option = (None, parse_relro_level, [TRACKED], "choose which RELRO level to use"), - nll_facts: bool = (false, parse_bool, [UNTRACKED], - "dump facts from NLL analysis into side files"), - dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], - "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting)."), - polonius: bool = (false, parse_bool, [UNTRACKED], - "enable polonius-based borrow-checker"), - codegen_time_graph: bool = (false, parse_bool, [UNTRACKED], - "generate a graphical HTML report of time spent in codegen and LLVM"), + report_delayed_bugs: bool = (false, parse_bool, [TRACKED], + "immediately print bugs registered with `delay_span_bug` (default: no)"), + // The default historical behavior was to always run dsymutil, so we're + // preserving that temporarily, but we're likely to switch the default + // soon. + run_dsymutil: bool = (true, parse_bool, [TRACKED], + "if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"), + sanitizer: Option = (None, parse_sanitizer, [TRACKED], + "use a sanitizer"), + sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], + "enable origins tracking in MemorySanitizer"), + sanitizer_recover: Vec = (vec![], parse_sanitizer_list, [TRACKED], + "enable recovery for selected sanitizers"), + saturating_float_casts: Option = (None, parse_opt_bool, [TRACKED], + "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ + the max/min integer respectively, and NaN is mapped to 0 (default: yes)"), + save_analysis: bool = (false, parse_bool, [UNTRACKED], + "write syntax and type analysis (in JSON format) information, in \ + addition to normal output (default: no)"), + self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [UNTRACKED], + "run the self profiler and output the raw event data"), + // keep this in sync with the event filter names in librustc_data_structures/profiling.rs + self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], + "specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, query-keys, function-args, args, llvm"), + share_generics: Option = (None, parse_opt_bool, [TRACKED], + "make the current crate share its generic instantiations"), + show_span: Option = (None, parse_opt_string, [TRACKED], + "show spans for compiler debugging (expr|pat|ty)"), + // o/w tests have closure@path + span_free_formats: bool = (false, parse_bool, [UNTRACKED], + "exclude spans when debug-printing compiler state (default: no)"), + src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], + "hash algorithm of source files in debug info (`md5`, or `sha1`)"), + strip: Strip = (Strip::None, parse_strip, [UNTRACKED], + "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), + symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, + parse_symbol_mangling_version, [TRACKED], + "which mangling version to use for symbol names"), + teach: bool = (false, parse_bool, [TRACKED], + "show extended diagnostic help (default: no)"), + terminal_width: Option = (None, parse_opt_uint, [UNTRACKED], + "set the current terminal width"), thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), - inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), - tls_model: Option = (None, parse_opt_string, [TRACKED], + // We default to 1 here since we want to behave like + // a sequential compiler for now. This'll likely be adjusted + // in the future. Note that -Zthreads=0 is the way to get + // the num_cpus behavior. + threads: usize = (1, parse_threads, [UNTRACKED], + "use a thread pool with N threads"), + time: bool = (false, parse_bool, [UNTRACKED], + "measure time of rustc processes (default: no)"), + time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each LLVM pass (default: no)"), + time_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each rustc pass (default: no)"), + tls_model: Option = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), - saturating_float_casts: bool = (false, parse_bool, [TRACKED], - "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ - the max/min integer respectively, and NaN is mapped to 0"), - human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], - "generate human-readable, predictable names for codegen units"), - dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], - "in dep-info output, omit targets for tracking dependencies of the dep-info files \ - themselves"), + trace_macros: bool = (false, parse_bool, [UNTRACKED], + "for every macro invocation, print its name and arguments (default: no)"), + treat_err_as_bug: Option = (None, parse_treat_err_as_bug, [TRACKED], + "treat error number `val` that occurs as bug"), + ui_testing: bool = (false, parse_bool, [UNTRACKED], + "emit compiler diagnostics in a form suitable for UI testing (default: no)"), + unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], + "take the brakes off const evaluation. NOTE: this is unsound (default: no)"), unpretty: Option = (None, parse_unpretty, [UNTRACKED], "present the input source, unstable (and less-pretty) variants; valid types are any of the types for `--pretty`, as well as: @@ -912,60 +1041,17 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, `hir,typed` (HIR with types for each node), `hir-tree` (dump the raw HIR), `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"), - run_dsymutil: Option = (None, parse_opt_bool, [TRACKED], - "run `dsymutil` and delete intermediate object files"), - ui_testing: Option = (None, parse_opt_bool, [UNTRACKED], - "format compiler diagnostics in a way that's better suitable for UI testing"), - embed_bitcode: bool = (false, parse_bool, [TRACKED], - "embed LLVM bitcode in object files"), - strip_debuginfo_if_disabled: Option = (None, parse_opt_bool, [TRACKED], - "tell the linker to strip debuginfo when building without debuginfo enabled."), - share_generics: Option = (None, parse_opt_bool, [TRACKED], - "make the current crate share its generic instantiations"), - no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED], - "don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"), - no_leak_check: bool = (false, parse_bool, [UNTRACKED], - "disables the 'leak check' for subtyping; unsound, but useful for tests"), - no_interleave_lints: bool = (false, parse_bool, [UNTRACKED], - "don't interleave execution of lints; allows benchmarking individual lints"), - crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], - "inject the given attribute in the crate"), - self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, - parse_switch_with_opt_path, [UNTRACKED], - "run the self profiler and output the raw event data"), - // keep this in sync with the event filter names in librustc_data_structures/profiling.rs - self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], - "specifies which kinds of events get recorded by the self profiler; - for example: `-Z self-profile-events=default,query-keys` - all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, query-keys, function-args, args, llvm"), - emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], - "emits a section containing stack size metadata"), - plt: Option = (None, parse_opt_bool, [TRACKED], - "whether to use the PLT when calling into shared libraries; - only has effect for PIC code on systems with ELF binaries - (default: PLT is disabled if full relro is enabled)"), - merge_functions: Option = (None, parse_merge_functions, [TRACKED], - "control the operation of the MergeFunctions LLVM pass, taking - the same values as the target option of the same name"), - allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], - "only allow the listed language features to be enabled in code (space separated)"), - symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, - parse_symbol_mangling_version, [TRACKED], - "which mangling version to use for symbol names"), - binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], - "include artifacts (sysroot, crate dependencies) used during compilation in dep-info"), - insert_sideeffect: bool = (false, parse_bool, [TRACKED], - "fix undefined behavior when a thread doesn't eventually make progress \ - (such as entering an empty infinite loop) by inserting llvm.sideeffect"), - deduplicate_diagnostics: Option = (None, parse_opt_bool, [UNTRACKED], - "deduplicate identical diagnostics"), - control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [UNTRACKED], - "use Windows Control Flow Guard (`disabled`, `nochecks` or `checks`)"), - no_link: bool = (false, parse_bool, [TRACKED], - "compile without linking"), - link_only: bool = (false, parse_bool, [TRACKED], - "link the `.rlink` file generated by `-Z no-link`"), - new_llvm_pass_manager: Option = (None, parse_opt_bool, [TRACKED], - "use new LLVM pass manager"), + unstable_options: bool = (false, parse_bool, [UNTRACKED], + "adds unstable command line options to rustc interface (default: no)"), + use_ctors_section: Option = (None, parse_opt_bool, [TRACKED], + "use legacy .ctors section for initializers rather than .init_array"), + verbose: bool = (false, parse_bool, [UNTRACKED], + "in general, enable more debug printouts (default: no)"), + verify_llvm_ir: bool = (false, parse_bool, [TRACKED], + "verify LLVM IR (default: no)"), + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs } diff --git a/src/librustc_session/output.rs b/src/librustc_session/output.rs index ba3d08cfc7c14..52216188397d7 100644 --- a/src/librustc_session/output.rs +++ b/src/librustc_session/output.rs @@ -1,5 +1,5 @@ //! Related to out filenames of compilation (e.g. save analysis, binaries). -use crate::config::{self, Input, OutputFilenames, OutputType}; +use crate::config::{CrateType, Input, OutputFilenames, OutputType}; use crate::Session; use rustc_ast::{ast, attr}; use rustc_span::symbol::sym; @@ -8,7 +8,7 @@ use std::path::{Path, PathBuf}; pub fn out_filename( sess: &Session, - crate_type: config::CrateType, + crate_type: CrateType, outputs: &OutputFilenames, crate_name: &str, ) -> PathBuf { @@ -146,27 +146,27 @@ pub fn filename_for_metadata( pub fn filename_for_input( sess: &Session, - crate_type: config::CrateType, + crate_type: CrateType, crate_name: &str, outputs: &OutputFilenames, ) -> PathBuf { let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); match crate_type { - config::CrateType::Rlib => outputs.out_directory.join(&format!("lib{}.rlib", libname)), - config::CrateType::Cdylib | config::CrateType::ProcMacro | config::CrateType::Dylib => { + CrateType::Rlib => outputs.out_directory.join(&format!("lib{}.rlib", libname)), + CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => { let (prefix, suffix) = (&sess.target.target.options.dll_prefix, &sess.target.target.options.dll_suffix); outputs.out_directory.join(&format!("{}{}{}", prefix, libname, suffix)) } - config::CrateType::Staticlib => { + CrateType::Staticlib => { let (prefix, suffix) = ( &sess.target.target.options.staticlib_prefix, &sess.target.target.options.staticlib_suffix, ); outputs.out_directory.join(&format!("{}{}{}", prefix, libname, suffix)) } - config::CrateType::Executable => { + CrateType::Executable => { let suffix = &sess.target.target.options.exe_suffix; let out_filename = outputs.path(OutputType::Exe); if suffix.is_empty() { out_filename } else { out_filename.with_extension(&suffix[1..]) } @@ -183,18 +183,18 @@ pub fn filename_for_input( /// way to run iOS binaries anyway without jailbreaking and /// interaction with Rust code through static library is the only /// option for now -pub fn default_output_for_target(sess: &Session) -> config::CrateType { +pub fn default_output_for_target(sess: &Session) -> CrateType { if !sess.target.target.options.executables { - config::CrateType::Staticlib + CrateType::Staticlib } else { - config::CrateType::Executable + CrateType::Executable } } /// Checks if target supports crate_type as output -pub fn invalid_output_for_target(sess: &Session, crate_type: config::CrateType) -> bool { +pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool { match crate_type { - config::CrateType::Cdylib | config::CrateType::Dylib | config::CrateType::ProcMacro => { + CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro => { if !sess.target.target.options.dynamic_linking { return true; } @@ -208,12 +208,12 @@ pub fn invalid_output_for_target(sess: &Session, crate_type: config::CrateType) } if sess.target.target.options.only_cdylib { match crate_type { - config::CrateType::ProcMacro | config::CrateType::Dylib => return true, + CrateType::ProcMacro | CrateType::Dylib => return true, _ => {} } } if !sess.target.target.options.executables { - if crate_type == config::CrateType::Executable { + if crate_type == CrateType::Executable { return true; } } diff --git a/src/librustc_session/parse.rs b/src/librustc_session/parse.rs index 387d35422c43e..69d3e99b7458e 100644 --- a/src/librustc_session/parse.rs +++ b/src/librustc_session/parse.rs @@ -60,6 +60,20 @@ impl GatedSpans { } } +#[derive(Default)] +pub struct SymbolGallery { + /// All symbols occurred and their first occurrance span. + pub symbols: Lock>, +} + +impl SymbolGallery { + /// Insert a symbol and its span into symbol gallery. + /// If the symbol has occurred before, ignore the new occurance. + pub fn insert(&self, symbol: Symbol, span: Span) { + self.symbols.lock().entry(symbol).or_insert(span); + } +} + /// Construct a diagnostic for a language feature error due to the given `span`. /// The `feature`'s `Symbol` is the one you used in `active.rs` and `rustc_span::symbols`. pub fn feature_err<'a>( @@ -118,6 +132,7 @@ pub struct ParseSess { pub ambiguous_block_expr_parse: Lock>, pub injected_crate_name: Once, pub gated_spans: GatedSpans, + pub symbol_gallery: SymbolGallery, /// The parser has reached `Eof` due to an unclosed brace. Used to silence unnecessary errors. pub reached_eof: Lock, } @@ -143,6 +158,7 @@ impl ParseSess { ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), injected_crate_name: Once::new(), gated_spans: GatedSpans::default(), + symbol_gallery: SymbolGallery::default(), reached_eof: Lock::new(false), } } diff --git a/src/librustc_session/search_paths.rs b/src/librustc_session/search_paths.rs index 06f408b4a8d64..4ff06acaa1fd4 100644 --- a/src/librustc_session/search_paths.rs +++ b/src/librustc_session/search_paths.rs @@ -6,7 +6,31 @@ use std::path::{Path, PathBuf}; pub struct SearchPath { pub kind: PathKind, pub dir: PathBuf, - pub files: Vec, + pub files: Vec, +} + +// The obvious implementation of `SearchPath::files` is a `Vec`. But +// it is searched repeatedly by `find_library_crate`, and the searches involve +// checking the prefix and suffix of the filename of each `PathBuf`. This is +// doable, but very slow, because it involves calls to `file_name` and +// `extension` that are themselves slow. +// +// This type augments the `PathBuf` with an `Option` containing the +// `PathBuf`'s filename. The prefix and suffix checking is much faster on the +// `Option` than the `PathBuf`. (It's an `Option` because +// `Path::file_name` can fail; if that happens then all subsequent checking +// will also fail, which is fine.) +#[derive(Clone, Debug)] +pub struct SearchPathFile { + pub path: PathBuf, + pub file_name_str: Option, +} + +impl SearchPathFile { + fn new(path: PathBuf) -> SearchPathFile { + let file_name_str = path.file_name().and_then(|f| f.to_str()).map(|s| s.to_string()); + SearchPathFile { path, file_name_str } + } } #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, RustcEncodable, RustcDecodable)] @@ -60,7 +84,9 @@ impl SearchPath { fn new(kind: PathKind, dir: PathBuf) -> Self { // Get the files within the directory. let files = match std::fs::read_dir(&dir) { - Ok(files) => files.filter_map(|p| p.ok().map(|s| s.path())).collect::>(), + Ok(files) => files + .filter_map(|e| e.ok().map(|e| SearchPathFile::new(e.path()))) + .collect::>(), Err(..) => vec![], }; diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index 80f59aff69137..143401dd3b622 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -1,7 +1,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; +use crate::config::{self, CrateType, OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; use crate::filesearch; use crate::lint; use crate::parse::ParseSess; @@ -18,16 +18,21 @@ use rustc_data_structures::sync::{ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; +use rustc_errors::registry::Registry; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported}; use rustc_span::edition::Edition; -use rustc_span::source_map::{self, MultiSpan, Span}; -use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; +use rustc_span::source_map::{self, FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; +use rustc_span::{SourceFileHashAlgorithm, Symbol}; +use rustc_target::asm::InlineAsmArch; +use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; +use rustc_target::spec::{Target, TargetTriple, TlsModel}; use std::cell::{self, RefCell}; use std::env; use std::io::Write; use std::num::NonZeroU32; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; use std::time::Duration; @@ -72,7 +77,7 @@ pub struct Session { /// (sub)diagnostics that have been set once, but should not be set again, /// in order to avoid redundantly verbose output (Issue #24690, #44953). pub one_time_diagnostics: Lock, String)>>, - pub crate_types: Once>, + pub crate_types: Once>, /// The `crate_disambiguator` is constructed out of all the `-C metadata` /// arguments passed to the compiler. Its value together with the crate-name /// forms a unique global identifier for the crate. It is used to allow @@ -140,6 +145,27 @@ pub struct Session { /// Options range from returning the error without a backtrace to returning an error /// and immediately printing the backtrace to stderr. pub ctfe_backtrace: Lock, + + /// This tracks where `-Zunleash-the-miri-inside-of-you` was used to get around a + /// const check, optionally with the relevant feature gate. We use this to + /// warn about unleashing, but with a single diagnostic instead of dozens that + /// drown everything else in noise. + miri_unleashed_features: Lock)>>, + + /// Base directory containing the `src/` for the Rust standard library, and + /// potentially `rustc` as well, if we can can find it. Right now it's always + /// `$sysroot/lib/rustlib/src/rust` (i.e. the `rustup` `rust-src` component). + /// + /// 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`). + pub real_rust_source_base_dir: Option, + + /// Architecture to use for interpreting asm!. + pub asm_arch: Option, + + /// Set of enabled features for the current target. + pub target_features: FxHashSet, } pub struct PerfStats { @@ -150,7 +176,7 @@ pub struct PerfStats { /// Total number of values canonicalized queries constructed. pub queries_canonicalized: AtomicUsize, /// Number of times this query is invoked. - pub normalize_ty_after_erasing_regions: AtomicUsize, + pub normalize_generic_arg_after_erasing_regions: AtomicUsize, /// Number of times this query is invoked. pub normalize_projection_ty: AtomicUsize, } @@ -179,6 +205,44 @@ impl From<&'static lint::Lint> for DiagnosticMessageId { } impl Session { + pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option) { + self.miri_unleashed_features.lock().push((span, feature_gate)); + } + + fn check_miri_unleashed_features(&self) { + let unleashed_features = self.miri_unleashed_features.lock(); + if !unleashed_features.is_empty() { + let mut must_err = false; + // Create a diagnostic pointing at where things got unleashed. + let mut diag = self.struct_warn("skipping const checks"); + for &(span, feature_gate) in unleashed_features.iter() { + // FIXME: `span_label` doesn't do anything, so we use "help" as a hack. + if let Some(feature_gate) = feature_gate { + diag.span_help(span, &format!("skipping check for `{}` feature", feature_gate)); + // The unleash flag must *not* be used to just "hack around" feature gates. + must_err = true; + } else { + diag.span_help(span, "skipping check that does not even have a feature gate"); + } + } + diag.emit(); + // If we should err, make sure we did. + if must_err && !self.has_errors() { + // We have skipped a feature gate, and not run into other errors... reject. + self.err( + "`-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature \ + gates, except when testing error paths in the CTFE engine", + ); + } + } + } + + /// Invoked all the way at the end to finish off diagnostics printing. + pub fn finish_diagnostics(&self, registry: &Registry) { + self.check_miri_unleashed_features(); + self.diagnostic().print_error_count(registry); + } + pub fn local_crate_disambiguator(&self) -> CrateDisambiguator { *self.crate_disambiguator.get() } @@ -530,9 +594,6 @@ impl Session { self.opts.debugging_opts.fewer_names || !more_names } - pub fn no_landing_pads(&self) -> bool { - self.opts.debugging_opts.no_landing_pads || self.panic_strategy() == PanicStrategy::Abort - } pub fn unstable_options(&self) -> bool { self.opts.debugging_opts.unstable_options } @@ -545,25 +606,20 @@ impl Session { } /// Check whether this compile session and crate type use static crt. - pub fn crt_static(&self, crate_type: Option) -> bool { - // If the target does not opt in to crt-static support, use its default. - if self.target.target.options.crt_static_respected { - self.crt_static_feature(crate_type) - } else { - self.target.target.options.crt_static_default + pub fn crt_static(&self, crate_type: Option) -> bool { + if !self.target.target.options.crt_static_respected { + // If the target does not opt in to crt-static support, use its default. + return self.target.target.options.crt_static_default; } - } - /// Check whether this compile session and crate type use `crt-static` feature. - pub fn crt_static_feature(&self, crate_type: Option) -> bool { let requested_features = self.opts.cg.target_feature.split(','); let found_negative = requested_features.clone().any(|r| r == "-crt-static"); let found_positive = requested_features.clone().any(|r| r == "+crt-static"); if found_positive || found_negative { found_positive - } else if crate_type == Some(config::CrateType::ProcMacro) - || crate_type == None && self.opts.crate_types.contains(&config::CrateType::ProcMacro) + } else if crate_type == Some(CrateType::ProcMacro) + || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) { // FIXME: When crate_type is not available, // we use compiler options to determine the crate_type. @@ -574,6 +630,18 @@ impl Session { } } + pub fn relocation_model(&self) -> RelocModel { + self.opts.cg.relocation_model.unwrap_or(self.target.target.options.relocation_model) + } + + pub fn code_model(&self) -> Option { + self.opts.cg.code_model.or(self.target.target.options.code_model) + } + + pub fn tls_model(&self) -> TlsModel { + self.opts.debugging_opts.tls_model.unwrap_or(self.target.target.options.tls_model) + } + pub fn must_not_eliminate_frame_pointers(&self) -> bool { // "mcount" function relies on stack pointer. // See . @@ -586,6 +654,33 @@ impl Session { } } + pub fn must_emit_unwind_tables(&self) -> bool { + // This is used to control the emission of the `uwtable` attribute on + // LLVM functions. + // + // At the very least, unwind tables are needed when compiling with + // `-C panic=unwind`. + // + // On some targets (including windows), however, exceptions include + // other events such as illegal instructions, segfaults, etc. This means + // that on Windows we end up still needing unwind tables even if the `-C + // panic=abort` flag is passed. + // + // You can also find more info on why Windows needs unwind tables in: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 + // + // If a target requires unwind tables, then they must be emitted. + // Otherwise, we can defer to the `-C force-unwind-tables=` + // value, if it is provided, or disable them, if not. + if self.panic_strategy() == PanicStrategy::Unwind { + true + } else if self.target.target.options.requires_uwtable { + true + } else { + self.opts.cg.force_unwind_tables.unwrap_or(false) + } + } + /// Returns the symbol name for the registrar function, /// given the crate `Svh` and the function `DefIndex`. pub fn generate_plugin_registrar_symbol(&self, disambiguator: CrateDisambiguator) -> String { @@ -707,8 +802,8 @@ impl Session { self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) ); println!( - "normalize_ty_after_erasing_regions: {}", - self.perf_stats.normalize_ty_after_erasing_regions.load(Ordering::Relaxed) + "normalize_generic_arg_after_erasing_regions: {}", + self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) ); println!( "normalize_projection_ty: {}", @@ -726,7 +821,7 @@ impl Session { let mut fuel = self.optimization_fuel.lock(); ret = fuel.remaining != 0; if fuel.remaining == 0 && !fuel.out_of_fuel { - eprintln!("optimization-fuel-exhausted: {}", msg()); + self.warn(&format!("optimization-fuel-exhausted: {}", msg())); fuel.out_of_fuel = true; } else if fuel.remaining > 0 { fuel.remaining -= 1; @@ -758,6 +853,13 @@ impl Session { return n as usize; } + // If incremental compilation is turned on, we default to a high number + // codegen units in order to reduce the "collateral damage" small + // changes cause. + if self.opts.incremental.is_some() { + return 256; + } + // Why is 16 codegen units the default all the time? // // The main reason for enabling multiple codegen units by default is to @@ -847,6 +949,16 @@ impl Session { // then try to skip it where possible. dbg_opts.plt.unwrap_or(needs_plt || !full_relro) } + + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + match self.opts.debugging_opts.sanitizer { + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + Some(Sanitizer::Address | Sanitizer::Memory) => true, + _ => self.opts.optimize != config::OptLevel::No, + } + } } pub fn build_session( @@ -854,16 +966,15 @@ pub fn build_session( local_crate_source_file: Option, registry: rustc_errors::registry::Registry, ) -> Session { - let file_path_mapping = sopts.file_path_mapping(); - build_session_with_source_map( sopts, local_crate_source_file, registry, - Lrc::new(source_map::SourceMap::new(file_path_mapping)), DiagnosticOutput::Default, Default::default(), + None, ) + .0 } fn default_emitter( @@ -883,7 +994,7 @@ fn default_emitter( short, macro_backtrace, ); - Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing())) + Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) } else { let emitter = match dst { None => EmitterWriter::stderr( @@ -904,7 +1015,7 @@ fn default_emitter( macro_backtrace, ), }; - Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing())) + Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) } } (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new( @@ -915,7 +1026,7 @@ fn default_emitter( json_rendered, macro_backtrace, ) - .ui_testing(sopts.debugging_opts.ui_testing()), + .ui_testing(sopts.debugging_opts.ui_testing), ), (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new( JsonEmitter::new( @@ -926,7 +1037,7 @@ fn default_emitter( json_rendered, macro_backtrace, ) - .ui_testing(sopts.debugging_opts.ui_testing()), + .ui_testing(sopts.debugging_opts.ui_testing), ), } } @@ -940,10 +1051,10 @@ pub fn build_session_with_source_map( sopts: config::Options, local_crate_source_file: Option, registry: rustc_errors::registry::Registry, - source_map: Lrc, diagnostics_output: DiagnosticOutput, - lint_caps: FxHashMap, -) -> Session { + driver_lint_caps: FxHashMap, + file_loader: Option>, +) -> (Session, Lrc) { // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed // later via the source code. @@ -961,23 +1072,33 @@ pub fn build_session_with_source_map( DiagnosticOutput::Default => None, DiagnosticOutput::Raw(write) => Some(write), }; + + let target_cfg = config::build_target_config(&sopts, sopts.error_format); + let host_triple = TargetTriple::from_triple(config::host_triple()); + let host = Target::search(&host_triple).unwrap_or_else(|e| { + early_error(sopts.error_format, &format!("Error loading host specification: {}", e)) + }); + + let loader = file_loader.unwrap_or(Box::new(RealFileLoader)); + let hash_kind = sopts.debugging_opts.src_hash_algorithm.unwrap_or_else(|| { + if target_cfg.target.options.is_like_msvc { + SourceFileHashAlgorithm::Sha1 + } else { + SourceFileHashAlgorithm::Md5 + } + }); + let source_map = Lrc::new(SourceMap::with_file_loader_and_hash_kind( + loader, + sopts.file_path_mapping(), + hash_kind, + )); let emitter = default_emitter(&sopts, registry, &source_map, write_dest); - let diagnostic_handler = rustc_errors::Handler::with_emitter_and_flags( + let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( emitter, sopts.debugging_opts.diagnostic_handler_flags(can_emit_warnings), ); - build_session_(sopts, local_crate_source_file, diagnostic_handler, source_map, lint_caps) -} - -fn build_session_( - sopts: config::Options, - local_crate_source_file: Option, - span_diagnostic: rustc_errors::Handler, - source_map: Lrc, - driver_lint_caps: FxHashMap, -) -> Session { let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile { let directory = @@ -999,13 +1120,7 @@ fn build_session_( None }; - let host_triple = TargetTriple::from_triple(config::host_triple()); - let host = Target::search(&host_triple).unwrap_or_else(|e| { - span_diagnostic.fatal(&format!("Error loading host specification: {}", e)).raise() - }); - let target_cfg = config::build_target_config(&sopts, &span_diagnostic); - - let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map); + let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map.clone()); let sysroot = match &sopts.maybe_sysroot { Some(sysroot) => sysroot.clone(), None => filesearch::get_or_default_sysroot(), @@ -1056,6 +1171,32 @@ fn build_session_( _ => CtfeBacktrace::Disabled, }); + // Try to find a directory containing the Rust `src`, for more details see + // the doc comment on the `real_rust_source_base_dir` field. + let real_rust_source_base_dir = { + // This is the location used by the `rust-src` `rustup` component. + let mut candidate = sysroot.join("lib/rustlib/src/rust"); + if let Ok(metadata) = candidate.symlink_metadata() { + // Replace the symlink rustbuild creates, with its destination. + // We could try to use `fs::canonicalize` instead, but that might + // produce unnecessarily verbose path. + if metadata.file_type().is_symlink() { + if let Ok(symlink_dest) = std::fs::read_link(&candidate) { + candidate = symlink_dest; + } + } + } + + // Only use this directory if it has a file we can expect to always find. + if candidate.join("src/libstd/lib.rs").is_file() { Some(candidate) } else { None } + }; + + let asm_arch = if target_cfg.target.options.allow_asm { + InlineAsmArch::from_str(&target_cfg.target.arch).ok() + } else { + None + }; + let sess = Session { target: target_cfg, host, @@ -1080,7 +1221,7 @@ fn build_session_( symbol_hash_time: Lock::new(Duration::from_secs(0)), decode_def_path_tables_time: Lock::new(Duration::from_secs(0)), queries_canonicalized: AtomicUsize::new(0), - normalize_ty_after_erasing_regions: AtomicUsize::new(0), + normalize_generic_arg_after_erasing_regions: AtomicUsize::new(0), normalize_projection_ty: AtomicUsize::new(0), }, code_stats: Default::default(), @@ -1094,11 +1235,15 @@ fn build_session_( confused_type_with_std_module: Lock::new(Default::default()), system_library_path: OneThread::new(RefCell::new(Default::default())), ctfe_backtrace, + miri_unleashed_features: Lock::new(Default::default()), + real_rust_source_base_dir, + asm_arch, + target_features: FxHashSet::default(), }; validate_commandline_args_with_session_available(&sess); - sess + (sess, source_map) } // If it is useful to have a Session available already for validating a @@ -1132,6 +1277,23 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + // Unwind tables cannot be disabled if the target requires them. + if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables { + if sess.panic_strategy() == PanicStrategy::Unwind && !include_uwtables { + sess.err( + "panic=unwind requires unwind tables, they cannot be disabled \ + with `-C force-unwind-tables=no`.", + ); + } + + if sess.target.target.options.requires_uwtable && !include_uwtables { + sess.err( + "target requires unwind tables, they cannot be disabled with \ + `-C force-unwind-tables=no`.", + ); + } + } + // PGO does not work reliably with panic=unwind on Windows. Let's make it // an error to combine the two for now. It always runs into an assertions // if LLVM is built with assertions, but without assertions it sometimes diff --git a/src/librustc_span/Cargo.toml b/src/librustc_span/Cargo.toml index c3fa2331d2b16..1c2721260d69b 100644 --- a/src/librustc_span/Cargo.toml +++ b/src/librustc_span/Cargo.toml @@ -19,3 +19,5 @@ scoped-tls = "1.0" unicode-width = "0.1.4" cfg-if = "0.1.2" log = "0.4" +sha-1 = "0.8" +md-5 = "0.8" diff --git a/src/librustc_span/def_id.rs b/src/librustc_span/def_id.rs index 3551220c5c54a..fad9f2f613012 100644 --- a/src/librustc_span/def_id.rs +++ b/src/librustc_span/def_id.rs @@ -7,7 +7,6 @@ use rustc_macros::HashStable_Generic; use rustc_serialize::{Decoder, Encoder}; use std::borrow::Borrow; use std::fmt; -use std::{u32, u64}; rustc_index::newtype_index! { pub struct CrateId { @@ -225,6 +224,11 @@ impl LocalDefId { pub fn to_def_id(self) -> DefId { DefId { krate: LOCAL_CRATE, index: self.local_def_index } } + + #[inline] + pub fn is_top_level_module(self) -> bool { + self.local_def_index == CRATE_DEF_INDEX + } } impl fmt::Debug for LocalDefId { diff --git a/src/librustc_span/hygiene.rs b/src/librustc_span/hygiene.rs index 0afa2333e0615..23c3dccb130f6 100644 --- a/src/librustc_span/hygiene.rs +++ b/src/librustc_span/hygiene.rs @@ -661,7 +661,7 @@ pub struct ExpnData { /// The span of the macro definition (possibly dummy). /// This span serves only informational purpose and is not used for resolution. pub def_site: Span, - /// List of #[unstable]/feature-gated features that the macro is allowed to use + /// List of `#[unstable]`/feature-gated features that the macro is allowed to use /// internally without forcing the whole crate to opt-in /// to them. pub allow_internal_unstable: Option>, diff --git a/src/librustc_span/lib.rs b/src/librustc_span/lib.rs index 3ebcef947bc37..58cdb87158afe 100644 --- a/src/librustc_span/lib.rs +++ b/src/librustc_span/lib.rs @@ -9,9 +9,14 @@ #![feature(const_if_match)] #![feature(const_fn)] #![feature(const_panic)] +#![feature(negative_impls)] #![feature(nll)] #![feature(optin_builtin_traits)] -#![feature(specialization)] +#![feature(min_specialization)] + +// FIXME(#56935): Work around ICEs during cross-compilation. +#[allow(unused)] +extern crate rustc_macros; use rustc_data_structures::AtomicRef; use rustc_macros::HashStable_Generic; @@ -46,9 +51,14 @@ use std::borrow::Cow; use std::cell::RefCell; use std::cmp::{self, Ordering}; use std::fmt; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::ops::{Add, Sub}; use std::path::PathBuf; +use std::str::FromStr; + +use md5::Md5; +use sha1::Digest; +use sha1::Sha1; #[cfg(test)] mod tests; @@ -651,7 +661,8 @@ impl MultiSpan { MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] } } - pub fn from_spans(vec: Vec) -> MultiSpan { + pub fn from_spans(mut vec: Vec) -> MultiSpan { + vec.sort(); MultiSpan { primary_spans: vec, span_labels: vec![] } } @@ -873,6 +884,70 @@ impl ExternalSource { #[derive(Debug)] pub struct OffsetOverflowError; +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +pub enum SourceFileHashAlgorithm { + Md5, + Sha1, +} + +impl FromStr for SourceFileHashAlgorithm { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "md5" => Ok(SourceFileHashAlgorithm::Md5), + "sha1" => Ok(SourceFileHashAlgorithm::Sha1), + _ => Err(()), + } + } +} + +rustc_data_structures::impl_stable_hash_via_hash!(SourceFileHashAlgorithm); + +/// The hash of the on-disk source file used for debug info. +#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable_Generic)] +pub struct SourceFileHash { + pub kind: SourceFileHashAlgorithm, + value: [u8; 20], +} + +impl SourceFileHash { + pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash { + let mut hash = SourceFileHash { kind, value: Default::default() }; + let len = hash.hash_len(); + let value = &mut hash.value[..len]; + let data = src.as_bytes(); + match kind { + SourceFileHashAlgorithm::Md5 => { + value.copy_from_slice(&Md5::digest(data)); + } + SourceFileHashAlgorithm::Sha1 => { + value.copy_from_slice(&Sha1::digest(data)); + } + } + hash + } + + /// Check if the stored hash matches the hash of the string. + pub fn matches(&self, src: &str) -> bool { + Self::new(self.kind, src) == *self + } + + /// The bytes of the hash. + pub fn hash_bytes(&self) -> &[u8] { + let len = self.hash_len(); + &self.value[..len] + } + + fn hash_len(&self) -> usize { + match self.kind { + SourceFileHashAlgorithm::Md5 => 16, + SourceFileHashAlgorithm::Sha1 => 20, + } + } +} + /// A single source in the `SourceMap`. #[derive(Clone)] pub struct SourceFile { @@ -888,7 +963,7 @@ pub struct SourceFile { /// The complete source code. pub src: Option>, /// The source code's hash. - pub src_hash: u128, + pub src_hash: SourceFileHash, /// The external source code (used for external crates, which will have a `None` /// value as `self.src`. pub external_src: Lock, @@ -986,7 +1061,8 @@ impl Decodable for SourceFile { let name: FileName = d.read_struct_field("name", 0, |d| Decodable::decode(d))?; let name_was_remapped: bool = d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?; - let src_hash: u128 = d.read_struct_field("src_hash", 2, |d| Decodable::decode(d))?; + let src_hash: SourceFileHash = + d.read_struct_field("src_hash", 2, |d| Decodable::decode(d))?; let start_pos: BytePos = d.read_struct_field("start_pos", 3, |d| Decodable::decode(d))?; let end_pos: BytePos = d.read_struct_field("end_pos", 4, |d| Decodable::decode(d))?; @@ -1061,14 +1137,12 @@ impl SourceFile { unmapped_path: FileName, mut src: String, start_pos: BytePos, + hash_kind: SourceFileHashAlgorithm, ) -> Self { + // Compute the file hash before any normalization. + let src_hash = SourceFileHash::new(hash_kind, &src); let normalized_pos = normalize_src(&mut src, start_pos); - let src_hash = { - let mut hasher: StableHasher = StableHasher::new(); - hasher.write(src.as_bytes()); - hasher.finish::() - }; let name_hash = { let mut hasher: StableHasher = StableHasher::new(); name.hash(&mut hasher); @@ -1123,11 +1197,10 @@ impl SourceFile { kind: src_kind @ ExternalSourceKind::AbsentOk, .. } = &mut *external_src { - if let Some(src) = src { - let mut hasher: StableHasher = StableHasher::new(); - hasher.write(src.as_bytes()); - - if hasher.finish::() == self.src_hash { + if let Some(mut src) = src { + // The src_hash needs to be computed on the pre-normalized src. + if self.src_hash.matches(&src) { + normalize_src(&mut src, BytePos::from_usize(0)); *src_kind = ExternalSourceKind::Present(Lrc::new(src)); return true; } @@ -1543,7 +1616,7 @@ fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc. +/// instead of implementing everything in librustc_middle. pub trait HashStableContext { fn hash_spans(&self) -> bool; fn hash_def_id(&mut self, _: DefId, hasher: &mut StableHasher); diff --git a/src/librustc_span/source_map.rs b/src/librustc_span/source_map.rs index fa5baffbe48da..51f5541766305 100644 --- a/src/librustc_span/source_map.rs +++ b/src/librustc_span/source_map.rs @@ -20,7 +20,6 @@ use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; use log::debug; -use std::env; use std::fs; use std::io; @@ -64,9 +63,6 @@ pub trait FileLoader { /// Query the existence of a file. fn file_exists(&self, path: &Path) -> bool; - /// Returns an absolute path to a file, if possible. - fn abs_path(&self, path: &Path) -> Option; - /// Read the contents of an UTF-8 file into memory. fn read_file(&self, path: &Path) -> io::Result; } @@ -79,14 +75,6 @@ impl FileLoader for RealFileLoader { fs::metadata(path).is_ok() } - fn abs_path(&self, path: &Path) -> Option { - if path.is_absolute() { - Some(path.to_path_buf()) - } else { - env::current_dir().ok().map(|cwd| cwd.join(path)) - } - } - fn read_file(&self, path: &Path) -> io::Result { fs::read_to_string(path) } @@ -141,27 +129,31 @@ pub struct SourceMap { // This is used to apply the file path remapping as specified via // `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`. path_mapping: FilePathMapping, + + /// The algorithm used for hashing the contents of each source file. + hash_kind: SourceFileHashAlgorithm, } impl SourceMap { pub fn new(path_mapping: FilePathMapping) -> SourceMap { - SourceMap { - used_address_space: AtomicU32::new(0), - files: Default::default(), - file_loader: Box::new(RealFileLoader), + Self::with_file_loader_and_hash_kind( + Box::new(RealFileLoader), path_mapping, - } + SourceFileHashAlgorithm::Md5, + ) } - pub fn with_file_loader( + pub fn with_file_loader_and_hash_kind( file_loader: Box, path_mapping: FilePathMapping, + hash_kind: SourceFileHashAlgorithm, ) -> SourceMap { SourceMap { used_address_space: AtomicU32::new(0), files: Default::default(), file_loader, path_mapping, + hash_kind, } } @@ -275,6 +267,7 @@ impl SourceMap { unmapped_path, src, Pos::from_usize(start_pos), + self.hash_kind, )); let mut files = self.files.borrow_mut(); @@ -296,7 +289,7 @@ impl SourceMap { &self, filename: FileName, name_was_remapped: bool, - src_hash: u128, + src_hash: SourceFileHash, name_hash: u128, source_len: usize, cnum: CrateNum, @@ -733,7 +726,14 @@ impl SourceMap { } } - pub fn def_span(&self, sp: Span) -> Span { + /// Given a `Span`, return a span ending in the closest `{`. This is useful when you have a + /// `Span` enclosing a whole item but we need to point at only the head (usually the first + /// line) of that item. + /// + /// *Only suitable for diagnostics.* + pub fn guess_head_span(&self, sp: Span) -> Span { + // FIXME: extend the AST items to have a head span, or replace callers with pointing at + // the item's ident when appropriate. self.span_until_char(sp, '{') } @@ -910,14 +910,23 @@ impl SourceMap { pub fn generate_fn_name_span(&self, span: Span) -> Option { let prev_span = self.span_extend_to_prev_str(span, "fn", true); - self.span_to_snippet(prev_span) - .map(|snippet| { - let len = snippet - .find(|c: char| !c.is_alphanumeric() && c != '_') - .expect("no label after fn"); - prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32)) - }) - .ok() + if let Ok(snippet) = self.span_to_snippet(prev_span) { + debug!( + "generate_fn_name_span: span={:?}, prev_span={:?}, snippet={:?}", + span, prev_span, snippet + ); + + if snippet.is_empty() { + return None; + }; + + let len = snippet + .find(|c: char| !c.is_alphanumeric() && c != '_') + .expect("no label after fn"); + Some(prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32))) + } else { + None + } } /// Takes the span of a type parameter in a function signature and try to generate a span for diff --git a/src/librustc_span/source_map/tests.rs b/src/librustc_span/source_map/tests.rs index 79df1884f0db0..b8459eee4ecf0 100644 --- a/src/librustc_span/source_map/tests.rs +++ b/src/librustc_span/source_map/tests.rs @@ -168,6 +168,62 @@ fn span_merging_fail() { assert!(sm.merge_spans(span1, span2).is_none()); } +/// Tests loading an external source file that requires normalization. +#[test] +fn t10() { + let sm = SourceMap::new(FilePathMapping::empty()); + let unnormalized = "first line.\r\nsecond line"; + let normalized = "first line.\nsecond line"; + + let src_file = sm.new_source_file(PathBuf::from("blork.rs").into(), unnormalized.to_string()); + + assert_eq!(src_file.src.as_ref().unwrap().as_ref(), normalized); + assert!( + src_file.src_hash.matches(unnormalized), + "src_hash should use the source before normalization" + ); + + let SourceFile { + name, + name_was_remapped, + src_hash, + start_pos, + end_pos, + lines, + multibyte_chars, + non_narrow_chars, + normalized_pos, + name_hash, + .. + } = (*src_file).clone(); + + let imported_src_file = sm.new_imported_source_file( + name, + name_was_remapped, + src_hash, + name_hash, + (end_pos - start_pos).to_usize(), + CrateNum::new(0), + lines, + multibyte_chars, + non_narrow_chars, + normalized_pos, + start_pos, + end_pos, + ); + + assert!( + imported_src_file.external_src.borrow().get_source().is_none(), + "imported source file should not have source yet" + ); + imported_src_file.add_external_src(|| Some(unnormalized.to_string())); + assert_eq!( + imported_src_file.external_src.borrow().get_source().unwrap().as_ref(), + normalized, + "imported source file should be normalized" + ); +} + /// Returns the span corresponding to the `n`th occurrence of `substring` in `source_text`. trait SourceMapExtension { fn span_substr( diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index dcb4e849a75e8..6a6098710e828 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -144,6 +144,7 @@ symbols! { any, arbitrary_enum_discriminant, arbitrary_self_types, + Arc, Arguments, ArgumentV1, arm_target_feature, @@ -159,6 +160,7 @@ symbols! { attr, attributes, attr_literals, + att_syntax, augmented_assignments, automatically_derived, avx512_target_feature, @@ -191,6 +193,7 @@ symbols! { cfg_target_has_atomic, cfg_target_thread_local, cfg_target_vendor, + cfg_version, char, clippy, clone, @@ -319,6 +322,8 @@ symbols! { f32, f64, feature, + ffi_const, + ffi_pure, ffi_returns_twice, field, field_init_shorthand, @@ -347,6 +352,7 @@ symbols! { generators, generic_associated_types, generic_param_attrs, + get_context, global_allocator, global_asm, globs, @@ -373,6 +379,8 @@ symbols! { if_let, if_while_or_patterns, ignore, + inlateout, + inout, impl_header_lifetime_elision, impl_lint_pass, impl_trait_in_bindings, @@ -408,6 +416,7 @@ symbols! { label_break_value, lang, lang_items, + lateout, let_chains, lhs, lib, @@ -424,6 +433,7 @@ symbols! { LintPass, lint_reasons, literal, + llvm_asm, local_inner_macros, log_syntax, loop_break_value, @@ -473,6 +483,7 @@ symbols! { needs_drop, needs_panic_runtime, negate_unsigned, + negative_impls, never, never_type, never_type_fallback, @@ -490,12 +501,15 @@ symbols! { no_link, no_main, no_mangle, + nomem, non_ascii_idents, None, non_exhaustive, non_modrs_mods, - no_sanitize, + noreturn, no_niche, + no_sanitize, + nostack, no_stack_check, no_start, no_std, @@ -514,11 +528,13 @@ symbols! { option, Option, option_env, + options, opt_out_copy, or, or_patterns, Ord, Ordering, + out, Output, overlapping_marker_traits, packed, @@ -544,13 +560,14 @@ symbols! { plugin, plugin_registrar, plugins, + poll, Poll, - poll_with_context, powerpc_target_feature, precise_pointer_size_matching, pref_align_of, prelude, prelude_import, + preserves_flags, primitive, proc_dash_macro: "proc-macro", proc_macro, @@ -567,6 +584,7 @@ symbols! { profiler_runtime, ptr_offset_from, pub_restricted, + pure, pushpop_unsafe, quad_precision_float, question_mark, @@ -580,6 +598,8 @@ symbols! { raw_dylib, raw_identifiers, raw_ref_op, + Rc, + readonly, Ready, reason, recursion_limit, @@ -601,6 +621,7 @@ symbols! { Result, Return, rhs, + riscv_target_feature, rlib, rotate_left, rotate_right, @@ -650,6 +671,7 @@ symbols! { rustc_partition_reused, rustc_peek, rustc_peek_definite_init, + rustc_peek_liveness, rustc_peek_maybe_init, rustc_peek_maybe_uninit, rustc_peek_indirectly_mutable, @@ -715,8 +737,10 @@ symbols! { sty, sub_with_overflow, suggestion, + sym, sync_trait, target_feature, + target_feature_11, target_has_atomic, target_has_atomic_load_store, target_thread_local, @@ -799,6 +823,7 @@ symbols! { var, vec, Vec, + version, vis, visible_private_types, volatile, @@ -1149,12 +1174,20 @@ impl Interner { } // This module has a very short name because it's used a lot. +/// This module contains all the defined keyword `Symbol`s. +/// +/// Given that `kw` is imported, use them like `kw::keyword_name`. +/// For example `kw::Loop` or `kw::Break`. pub mod kw { use super::Symbol; keywords!(); } // This module has a very short name because it's used a lot. +/// This module contains all the defined non-keyword `Symbol`s. +/// +/// Given that `sym` is imported, use them like `sym::symbol_name`. +/// For example `sym::rustfmt` or `sym::u8`. #[allow(rustc::default_hash_types)] pub mod sym { use super::Symbol; @@ -1169,8 +1202,8 @@ pub mod sym { // have a static symbol and therefore are fast. pub fn integer + Copy + ToString>(n: N) -> Symbol { if let Result::Ok(idx) = n.try_into() { - if let Option::Some(&sym) = digits_array.get(idx) { - return sym; + if let Option::Some(&sym_) = digits_array.get(idx) { + return sym_; } } Symbol::intern(&n.to_string()) diff --git a/src/librustc_symbol_mangling/Cargo.toml b/src/librustc_symbol_mangling/Cargo.toml index 1e4fc8f7e6842..d670ababe9f12 100644 --- a/src/librustc_symbol_mangling/Cargo.toml +++ b/src/librustc_symbol_mangling/Cargo.toml @@ -16,9 +16,8 @@ rustc-demangle = "0.1.16" rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_hir = { path = "../librustc_hir" } rustc_target = { path = "../librustc_target" } rustc_data_structures = { path = "../librustc_data_structures" } -rustc_metadata = { path = "../librustc_metadata" } rustc_session = { path = "../librustc_session" } diff --git a/src/librustc_symbol_mangling/legacy.rs b/src/librustc_symbol_mangling/legacy.rs index 7b082309f34b5..3038b0c6bd7eb 100644 --- a/src/librustc_symbol_mangling/legacy.rs +++ b/src/librustc_symbol_mangling/legacy.rs @@ -1,12 +1,12 @@ -use rustc::hir::map::{DefPathData, DisambiguatedDefPathData}; -use rustc::ich::NodeIdHashingMode; -use rustc::mir::interpret::{ConstValue, Scalar}; -use rustc::ty::print::{PrettyPrinter, Print, Printer}; -use rustc::ty::subst::{GenericArg, GenericArgKind}; -use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; -use rustc::util::common::record_time; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::CrateNum; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::ich::NodeIdHashingMode; +use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::ty::print::{PrettyPrinter, Print, Printer}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::util::common::record_time; use log::debug; @@ -216,7 +216,6 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { ty::FnDef(def_id, substs) | ty::Opaque(def_id, substs) | ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) - | ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | ty::Closure(def_id, substs) | ty::Generator(def_id, substs, _) => self.print_def_path(def_id, substs), _ => self.pretty_print_type(ty), @@ -264,7 +263,6 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { ty::FnDef(..) | ty::Opaque(..) | ty::Projection(_) - | ty::UnnormalizedProjection(_) | ty::Closure(..) | ty::Generator(..) if trait_ref.is_none() => @@ -308,9 +306,8 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { self = print_prefix(self)?; // Skip `::{{constructor}}` on tuple/unit structs. - match disambiguated_data.data { - DefPathData::Ctor => return Ok(self), - _ => {} + if let DefPathData::Ctor = disambiguated_data.data { + return Ok(self); } if self.keep_within_component { diff --git a/src/librustc_symbol_mangling/lib.rs b/src/librustc_symbol_mangling/lib.rs index 26cb341050027..5a1c6491f86d8 100644 --- a/src/librustc_symbol_mangling/lib.rs +++ b/src/librustc_symbol_mangling/lib.rs @@ -90,19 +90,20 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(never_type)] #![feature(nll)] +#![feature(or_patterns)] #![feature(in_band_lifetimes)] #![recursion_limit = "256"] #[macro_use] -extern crate rustc; +extern crate rustc_middle; -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::mir::mono::{InstantiationMode, MonoItem}; -use rustc::ty::query::Providers; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Instance, TyCtxt}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::Node; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::SymbolManglingVersion; use rustc_span::symbol::Symbol; @@ -164,22 +165,18 @@ fn compute_symbol_name( debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs); - let hir_id = tcx.hir().as_local_hir_id(def_id); - - if def_id.is_local() { - if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id) { + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let is_foreign = if let Some(def_id) = def_id.as_local() { + if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id.to_def_id()) { let disambiguator = tcx.sess.local_crate_disambiguator(); return tcx.sess.generate_plugin_registrar_symbol(disambiguator); } - if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id) { + if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id.to_def_id()) { let disambiguator = tcx.sess.local_crate_disambiguator(); return tcx.sess.generate_proc_macro_decls_symbol(disambiguator); } - } - - // FIXME(eddyb) Precompute a custom symbol name based on attributes. - let is_foreign = if let Some(id) = hir_id { - match tcx.hir().get(id) { + let hir_id = tcx.hir().as_local_hir_id(def_id); + match tcx.hir().get(hir_id) { Node::ForeignItem(_) => true, _ => false, } diff --git a/src/librustc_symbol_mangling/test.rs b/src/librustc_symbol_mangling/test.rs index 8f2f2628e7b7f..5175b692e17b6 100644 --- a/src/librustc_symbol_mangling/test.rs +++ b/src/librustc_symbol_mangling/test.rs @@ -4,8 +4,8 @@ //! def-path. This is used for unit testing the code that generates //! paths etc in all kinds of annoying scenarios. -use rustc::ty::{Instance, TyCtxt}; use rustc_hir as hir; +use rustc_middle::ty::{Instance, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; const SYMBOL_NAME: Symbol = sym::rustc_symbol_name; @@ -33,10 +33,10 @@ impl SymbolNamesTest<'tcx> { fn process_attrs(&mut self, hir_id: hir::HirId) { let tcx = self.tcx; let def_id = tcx.hir().local_def_id(hir_id); - for attr in tcx.get_attrs(def_id).iter() { + for attr in tcx.get_attrs(def_id.to_def_id()).iter() { if attr.check_name(SYMBOL_NAME) { // for now, can only use on monomorphic names - let instance = Instance::mono(tcx, def_id); + let instance = Instance::mono(tcx, def_id.to_def_id()); let mangled = self.tcx.symbol_name(instance); tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); if let Ok(demangling) = rustc_demangle::try_demangle(&mangled.name.as_str()) { @@ -44,7 +44,7 @@ impl SymbolNamesTest<'tcx> { tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); } } else if attr.check_name(DEF_PATH) { - let path = tcx.def_path_str(def_id); + let path = tcx.def_path_str(def_id.to_def_id()); tcx.sess.span_err(attr.span, &format!("def-path({})", path)); } diff --git a/src/librustc_symbol_mangling/v0.rs b/src/librustc_symbol_mangling/v0.rs index e22a49061bb19..3b439e09a9d15 100644 --- a/src/librustc_symbol_mangling/v0.rs +++ b/src/librustc_symbol_mangling/v0.rs @@ -1,12 +1,12 @@ -use rustc::hir::map::{DefPathData, DisambiguatedDefPathData}; -use rustc::ty::print::{Print, Printer}; -use rustc::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_ast::ast::{FloatTy, IntTy, UintTy}; use rustc_data_structures::base_n; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::ty::print::{Print, Printer}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_target::spec::abi::Abi; use std::fmt::Write; @@ -153,7 +153,7 @@ impl SymbolMangler<'tcx> { // Write a separating `_` if necessary (leading digit or `_`). match ident.chars().next() { - Some('_') | Some('0'..='9') => { + Some('_' | '0'..='9') => { self.push("_"); } _ => {} @@ -413,7 +413,6 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { | ty::FnDef(def_id, substs) | ty::Opaque(def_id, substs) | ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) - | ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | ty::Closure(def_id, substs) | ty::Generator(def_id, substs, _) => { self = self.print_def_path(def_id, substs)?; diff --git a/src/librustc_target/abi/call/aarch64.rs b/src/librustc_target/abi/call/aarch64.rs index c8bac5aebc634..1ab7722edab98 100644 --- a/src/librustc_target/abi/call/aarch64.rs +++ b/src/librustc_target/abi/call/aarch64.rs @@ -1,10 +1,10 @@ use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { let size = arg.layout.size; @@ -26,8 +26,8 @@ where fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(32); @@ -58,8 +58,8 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !arg.layout.is_aggregate() { arg.extend_integer_width_to(32); @@ -90,8 +90,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !fn_abi.ret.is_ignore() { classify_ret(cx, &mut fn_abi.ret); diff --git a/src/librustc_target/abi/call/amdgpu.rs b/src/librustc_target/abi/call/amdgpu.rs index 704e8b8ffa895..0b4f279fece40 100644 --- a/src/librustc_target/abi/call/amdgpu.rs +++ b/src/librustc_target/abi/call/amdgpu.rs @@ -1,26 +1,26 @@ use crate::abi::call::{ArgAbi, FnAbi}; -use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; fn classify_ret<'a, Ty, C>(_cx: &C, ret: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { ret.extend_integer_width_to(32); } fn classify_arg<'a, Ty, C>(_cx: &C, arg: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { arg.extend_integer_width_to(32); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !fn_abi.ret.is_ignore() { classify_ret(cx, &mut fn_abi.ret); diff --git a/src/librustc_target/abi/call/arm.rs b/src/librustc_target/abi/call/arm.rs index 59ec87e3c9e09..26fed3bae4e48 100644 --- a/src/librustc_target/abi/call/arm.rs +++ b/src/librustc_target/abi/call/arm.rs @@ -1,11 +1,11 @@ use crate::abi::call::{ArgAbi, Conv, FnAbi, Reg, RegKind, Uniform}; -use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; use crate::spec::HasTargetSpec; fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { let size = arg.layout.size; @@ -27,8 +27,8 @@ where fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, vfp: bool) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(32); @@ -60,8 +60,8 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, vfp: bool) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !arg.layout.is_aggregate() { arg.extend_integer_width_to(32); @@ -82,8 +82,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout + HasTargetSpec, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout + HasTargetSpec, { // If this is a target with a hard-float ABI, and the function is not explicitly // `extern "aapcs"`, then we must use the VFP registers for homogeneous aggregates. diff --git a/src/librustc_target/abi/call/mips.rs b/src/librustc_target/abi/call/mips.rs index b332b9afe7f18..733a7328bd3a0 100644 --- a/src/librustc_target/abi/call/mips.rs +++ b/src/librustc_target/abi/call/mips.rs @@ -1,9 +1,9 @@ use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; -use crate::abi::{HasDataLayout, LayoutOf, Size, TyLayoutMethods}; +use crate::abi::{HasDataLayout, LayoutOf, Size, TyAndLayoutMethods}; fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf + HasDataLayout, { if !ret.layout.is_aggregate() { @@ -16,7 +16,7 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size) where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf + HasDataLayout, { let dl = cx.data_layout(); @@ -37,7 +37,7 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>) where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf + HasDataLayout, { let mut offset = Size::ZERO; diff --git a/src/librustc_target/abi/call/mips64.rs b/src/librustc_target/abi/call/mips64.rs index 6f8910a011a32..917dd104d1496 100644 --- a/src/librustc_target/abi/call/mips64.rs +++ b/src/librustc_target/abi/call/mips64.rs @@ -1,5 +1,5 @@ use crate::abi::call::{ArgAbi, ArgAttribute, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; -use crate::abi::{self, HasDataLayout, LayoutOf, Size, TyLayout, TyLayoutMethods}; +use crate::abi::{self, HasDataLayout, LayoutOf, Size, TyAndLayout, TyAndLayoutMethods}; fn extend_integer_width_mips(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips @@ -19,8 +19,8 @@ fn extend_integer_width_mips(arg: &mut ArgAbi<'_, Ty>, bits: u64) { fn float_reg<'a, Ty, C>(cx: &C, ret: &ArgAbi<'a, Ty>, i: usize) -> Option where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { match ret.layout.field(cx, i).abi { abi::Abi::Scalar(ref scalar) => match scalar.value { @@ -34,8 +34,8 @@ where fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !ret.layout.is_aggregate() { extend_integer_width_mips(ret, 64); @@ -49,7 +49,7 @@ where // use of float registers to structures (not unions) containing exactly one or two // float fields. - if let abi::FieldPlacement::Arbitrary { .. } = ret.layout.fields { + if let abi::FieldsShape::Arbitrary { .. } = ret.layout.fields { if ret.layout.fields.count() == 1 { if let Some(reg) = float_reg(cx, ret, 0) { ret.cast_to(reg); @@ -74,8 +74,8 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !arg.layout.is_aggregate() { extend_integer_width_mips(arg, 64); @@ -88,15 +88,16 @@ where let mut prefix_index = 0; match arg.layout.fields { - abi::FieldPlacement::Array { .. } => { + abi::FieldsShape::Primitive => unreachable!(), + abi::FieldsShape::Array { .. } => { // Arrays are passed indirectly arg.make_indirect(); return; } - abi::FieldPlacement::Union(_) => { + abi::FieldsShape::Union(_) => { // Unions and are always treated as a series of 64-bit integer chunks } - abi::FieldPlacement::Arbitrary { .. } => { + abi::FieldsShape::Arbitrary { .. } => { // Structures are split up into a series of 64-bit integer chunks, but any aligned // doubles not part of another aggregate are passed as floats. let mut last_offset = Size::ZERO; @@ -143,8 +144,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !fn_abi.ret.is_ignore() { classify_ret(cx, &mut fn_abi.ret); diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index 77e7ff1e2a738..0303312d057fe 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -1,5 +1,5 @@ -use crate::abi::{self, Abi, Align, FieldPlacement, Size}; -use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{self, Abi, Align, FieldsShape, Size}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; use crate::spec::{self, HasTargetSpec}; mod aarch64; @@ -264,7 +264,7 @@ impl HomogeneousAggregate { } } -impl<'a, Ty> TyLayout<'a, Ty> { +impl<'a, Ty> TyAndLayout<'a, Ty> { fn is_aggregate(&self) -> bool { match self.abi { Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, @@ -284,8 +284,8 @@ impl<'a, Ty> TyLayout<'a, Ty> { /// specific targets. pub fn homogeneous_aggregate(&self, cx: &C) -> Result where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf, { match self.abi { Abi::Uninhabited => Err(Heterogeneous), @@ -308,14 +308,17 @@ impl<'a, Ty> TyLayout<'a, Ty> { } Abi::ScalarPair(..) | Abi::Aggregate { .. } => { - // Helper for computing `homogenous_aggregate`, allowing a custom + // Helper for computing `homogeneous_aggregate`, allowing a custom // starting offset (used below for handling variants). let from_fields_at = |layout: Self, start: Size| -> Result<(HomogeneousAggregate, Size), Heterogeneous> { let is_union = match layout.fields { - FieldPlacement::Array { count, .. } => { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Array { count, .. } => { assert_eq!(start, Size::ZERO); let result = if count > 0 { @@ -325,8 +328,8 @@ impl<'a, Ty> TyLayout<'a, Ty> { }; return Ok((result, layout.size)); } - FieldPlacement::Union(_) => true, - FieldPlacement::Arbitrary { .. } => false, + FieldsShape::Union(_) => true, + FieldsShape::Arbitrary { .. } => false, }; let mut result = HomogeneousAggregate::NoData; @@ -404,7 +407,7 @@ impl<'a, Ty> TyLayout<'a, Ty> { /// or return a value from, a function, under some ABI. #[derive(Debug)] pub struct ArgAbi<'a, Ty> { - pub layout: TyLayout<'a, Ty>, + pub layout: TyAndLayout<'a, Ty>, /// Dummy argument, which is emitted before the real argument. pub pad: Option, @@ -413,7 +416,7 @@ pub struct ArgAbi<'a, Ty> { } impl<'a, Ty> ArgAbi<'a, Ty> { - pub fn new(layout: TyLayout<'a, Ty>) -> Self { + pub fn new(layout: TyAndLayout<'a, Ty>) -> Self { ArgAbi { layout, pad: None, mode: PassMode::Direct(ArgAttributes::new()) } } @@ -546,13 +549,15 @@ pub struct FnAbi<'a, Ty> { pub fixed_count: usize, pub conv: Conv, + + pub can_unwind: bool, } impl<'a, Ty> FnAbi<'a, Ty> { pub fn adjust_for_cabi(&mut self, cx: &C, abi: spec::abi::Abi) -> Result<(), String> where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout + HasTargetSpec, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout + HasTargetSpec, { match &cx.target_spec().arch[..] { "x86" => { diff --git a/src/librustc_target/abi/call/powerpc64.rs b/src/librustc_target/abi/call/powerpc64.rs index 93c4e97de10b9..b740707320f60 100644 --- a/src/librustc_target/abi/call/powerpc64.rs +++ b/src/librustc_target/abi/call/powerpc64.rs @@ -3,7 +3,7 @@ // need to be fixed when PowerPC vector support is added. use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::abi::{Endian, HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{Endian, HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; use crate::spec::HasTargetSpec; #[derive(Debug, Clone, Copy, PartialEq)] @@ -19,8 +19,8 @@ fn is_homogeneous_aggregate<'a, Ty, C>( abi: ABI, ) -> Option where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { // ELFv1 only passes one-member aggregates transparently. @@ -43,8 +43,8 @@ where fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, abi: ABI) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(64); @@ -86,8 +86,8 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi: ABI) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !arg.layout.is_aggregate() { arg.extend_integer_width_to(64); @@ -116,8 +116,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout + HasTargetSpec, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout + HasTargetSpec, { let abi = if cx.target_spec().target_env == "musl" { ELFv2 diff --git a/src/librustc_target/abi/call/riscv.rs b/src/librustc_target/abi/call/riscv.rs index 11d6c4d819107..2e10bed3bd451 100644 --- a/src/librustc_target/abi/call/riscv.rs +++ b/src/librustc_target/abi/call/riscv.rs @@ -6,7 +6,7 @@ use crate::abi::call::{ArgAbi, ArgAttribute, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; use crate::abi::{ - self, Abi, FieldPlacement, HasDataLayout, LayoutOf, Size, TyLayout, TyLayoutMethods, + self, Abi, FieldsShape, HasDataLayout, LayoutOf, Size, TyAndLayout, TyAndLayoutMethods, }; use crate::spec::HasTargetSpec; @@ -36,15 +36,15 @@ fn is_riscv_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { fn should_use_fp_conv_helper<'a, Ty, C>( cx: &C, - arg_layout: &TyLayout<'a, Ty>, + arg_layout: &TyAndLayout<'a, Ty>, xlen: u64, flen: u64, field1_kind: &mut RegPassKind, field2_kind: &mut RegPassKind, ) -> Result<(), CannotUseFpConv> where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf>, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf>, { match arg_layout.abi { Abi::Scalar(ref scalar) => match scalar.value { @@ -87,12 +87,15 @@ where }, Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv), Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields { - FieldPlacement::Union(_) => { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Union(_) => { if !arg_layout.is_zst() { return Err(CannotUseFpConv); } } - FieldPlacement::Array { count, .. } => { + FieldsShape::Array { count, .. } => { for _ in 0..count { let elem_layout = arg_layout.field(cx, 0); should_use_fp_conv_helper( @@ -105,7 +108,7 @@ where )?; } } - FieldPlacement::Arbitrary { .. } => { + FieldsShape::Arbitrary { .. } => { match arg_layout.variants { abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), abi::Variants::Single { .. } => (), @@ -122,13 +125,13 @@ where fn should_use_fp_conv<'a, Ty, C>( cx: &C, - arg: &TyLayout<'a, Ty>, + arg: &TyAndLayout<'a, Ty>, xlen: u64, flen: u64, ) -> Option where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf>, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf>, { let mut field1_kind = RegPassKind::Unknown; let mut field2_kind = RegPassKind::Unknown; @@ -146,8 +149,8 @@ where fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf>, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf>, { if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { match conv { @@ -209,8 +212,8 @@ fn classify_arg<'a, Ty, C>( avail_gprs: &mut u64, avail_fprs: &mut u64, ) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf>, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf>, { if !is_vararg { match should_use_fp_conv(cx, &arg.layout, xlen, flen) { @@ -300,30 +303,25 @@ fn classify_arg<'a, Ty, C>( } fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) { - match arg.layout.abi { - Abi::Scalar(ref scalar) => { - match scalar.value { - abi::Int(i, _) => { - // 32-bit integers are always sign-extended - if i.size().bits() == 32 && xlen > 32 { - if let PassMode::Direct(ref mut attrs) = arg.mode { - attrs.set(ArgAttribute::SExt); - return; - } - } + if let Abi::Scalar(ref scalar) = arg.layout.abi { + if let abi::Int(i, _) = scalar.value { + // 32-bit integers are always sign-extended + if i.size().bits() == 32 && xlen > 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.set(ArgAttribute::SExt); + return; } - _ => (), } } - _ => (), } + arg.extend_integer_width_to(xlen); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout + HasTargetSpec, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout + HasTargetSpec, { let flen = match &cx.target_spec().options.llvm_abiname[..] { "ilp32f" | "lp64f" => 32, diff --git a/src/librustc_target/abi/call/s390x.rs b/src/librustc_target/abi/call/s390x.rs index d4e9511d08775..005dcc62dfdc1 100644 --- a/src/librustc_target/abi/call/s390x.rs +++ b/src/librustc_target/abi/call/s390x.rs @@ -2,11 +2,11 @@ // for a pre-z13 machine or using -mno-vx. use crate::abi::call::{ArgAbi, FnAbi, Reg}; -use crate::abi::{self, HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{self, HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; fn classify_ret<'a, Ty, C>(ret: &mut ArgAbi<'_, Ty>) where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf + HasDataLayout, { if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 { @@ -16,10 +16,10 @@ where } } -fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyLayout<'a, Ty>) -> bool +fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool where - Ty: TyLayoutMethods<'a, C>, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf> + HasDataLayout, { match layout.abi { abi::Abi::Scalar(ref scalar) => scalar.value.is_float(), @@ -36,8 +36,8 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 { arg.extend_integer_width_to(64); @@ -63,8 +63,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !fn_abi.ret.is_ignore() { classify_ret(&mut fn_abi.ret); diff --git a/src/librustc_target/abi/call/sparc.rs b/src/librustc_target/abi/call/sparc.rs index b332b9afe7f18..733a7328bd3a0 100644 --- a/src/librustc_target/abi/call/sparc.rs +++ b/src/librustc_target/abi/call/sparc.rs @@ -1,9 +1,9 @@ use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; -use crate::abi::{HasDataLayout, LayoutOf, Size, TyLayoutMethods}; +use crate::abi::{HasDataLayout, LayoutOf, Size, TyAndLayoutMethods}; fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf + HasDataLayout, { if !ret.layout.is_aggregate() { @@ -16,7 +16,7 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size) where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf + HasDataLayout, { let dl = cx.data_layout(); @@ -37,7 +37,7 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>) where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf + HasDataLayout, { let mut offset = Size::ZERO; diff --git a/src/librustc_target/abi/call/sparc64.rs b/src/librustc_target/abi/call/sparc64.rs index c80f8316feb72..a647675e0735b 100644 --- a/src/librustc_target/abi/call/sparc64.rs +++ b/src/librustc_target/abi/call/sparc64.rs @@ -1,12 +1,12 @@ // FIXME: This needs an audit for correctness and completeness. use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { // Ensure we have at most eight uniquely addressable members. @@ -26,8 +26,8 @@ where fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(64); @@ -52,8 +52,8 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !arg.layout.is_aggregate() { arg.extend_integer_width_to(64); @@ -76,8 +76,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !fn_abi.ret.is_ignore() { classify_ret(cx, &mut fn_abi.ret); diff --git a/src/librustc_target/abi/call/wasm32.rs b/src/librustc_target/abi/call/wasm32.rs index 9aab64ef272b2..510f671a501e1 100644 --- a/src/librustc_target/abi/call/wasm32.rs +++ b/src/librustc_target/abi/call/wasm32.rs @@ -1,10 +1,10 @@ use crate::abi::call::{ArgAbi, FnAbi, Uniform}; -use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if val.layout.is_aggregate() { if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { @@ -20,8 +20,8 @@ where fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { ret.extend_integer_width_to(32); if ret.layout.is_aggregate() { @@ -33,8 +33,8 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { arg.extend_integer_width_to(32); if arg.layout.is_aggregate() { @@ -46,8 +46,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !fn_abi.ret.is_ignore() { classify_ret(cx, &mut fn_abi.ret); diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs index e776a8b3fe4a9..df3dd5d9208d3 100644 --- a/src/librustc_target/abi/call/x86.rs +++ b/src/librustc_target/abi/call/x86.rs @@ -1,5 +1,5 @@ use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind}; -use crate::abi::{self, HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods}; +use crate::abi::{self, HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; use crate::spec::HasTargetSpec; #[derive(PartialEq)] @@ -8,10 +8,10 @@ pub enum Flavor { Fastcall, } -fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyLayout<'a, Ty>) -> bool +fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { match layout.abi { abi::Abi::Scalar(ref scalar) => scalar.value.is_float(), @@ -28,8 +28,8 @@ where pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout + HasTargetSpec, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout + HasTargetSpec, { if !fn_abi.ret.is_ignore() { if fn_abi.ret.layout.is_aggregate() { diff --git a/src/librustc_target/abi/call/x86_64.rs b/src/librustc_target/abi/call/x86_64.rs index 4c192c46786be..5f154dc1bc9f8 100644 --- a/src/librustc_target/abi/call/x86_64.rs +++ b/src/librustc_target/abi/call/x86_64.rs @@ -2,7 +2,7 @@ // https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind}; -use crate::abi::{self, Abi, HasDataLayout, LayoutOf, Size, TyLayout, TyLayoutMethods}; +use crate::abi::{self, Abi, HasDataLayout, LayoutOf, Size, TyAndLayout, TyAndLayoutMethods}; /// Classification of "eightbyte" components. // N.B., the order of the variants is from general to specific, @@ -26,18 +26,18 @@ fn classify_arg<'a, Ty, C>( arg: &ArgAbi<'a, Ty>, ) -> Result<[Option; MAX_EIGHTBYTES], Memory> where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { fn classify<'a, Ty, C>( cx: &C, - layout: TyLayout<'a, Ty>, + layout: TyAndLayout<'a, Ty>, cls: &mut [Option], off: Size, ) -> Result<(), Memory> where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { if !off.is_aligned(layout.align.abi) { if !layout.is_zst() { @@ -172,8 +172,8 @@ const MAX_SSE_REGS: usize = 8; // XMM0-7 pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where - Ty: TyLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, { let mut int_regs = MAX_INT_REGS; let mut sse_regs = MAX_SSE_REGS; diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 635fb80b65980..dcf181cb59f4a 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -3,6 +3,8 @@ pub use Primitive::*; use crate::spec::Target; +use std::convert::{TryFrom, TryInto}; +use std::num::NonZeroUsize; use std::ops::{Add, AddAssign, Deref, Mul, Range, RangeInclusive, Sub}; use rustc_index::vec::{Idx, IndexVec}; @@ -240,17 +242,18 @@ pub struct Size { } impl Size { - pub const ZERO: Size = Self::from_bytes(0); + pub const ZERO: Size = Size { raw: 0 }; #[inline] - pub fn from_bits(bits: u64) -> Size { + pub fn from_bits(bits: impl TryInto) -> Size { + let bits = bits.try_into().ok().unwrap(); // Avoid potential overflow from `bits + 7`. Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8) } #[inline] - pub const fn from_bytes(bytes: u64) -> Size { - Size { raw: bytes } + pub fn from_bytes(bytes: impl TryInto) -> Size { + Size { raw: bytes.try_into().ok().unwrap() } } #[inline] @@ -258,6 +261,11 @@ impl Size { self.raw } + #[inline] + pub fn bytes_usize(self) -> usize { + self.bytes().try_into().unwrap() + } + #[inline] pub fn bits(self) -> u64 { self.bytes().checked_mul(8).unwrap_or_else(|| { @@ -265,6 +273,11 @@ impl Size { }) } + #[inline] + pub fn bits_usize(self) -> usize { + self.bits().try_into().unwrap() + } + #[inline] pub fn align_to(self, align: Align) -> Size { let mask = align.bytes() - 1; @@ -565,7 +578,7 @@ pub struct Scalar { pub value: Primitive, /// Inclusive wrap-around range of valid values, that is, if - /// start > end, it represents `start..=max_value()`, + /// start > end, it represents `start..=MAX`, /// followed by `0..=end`. /// /// That is, for an i8 primitive, a range of `254..=2` means following @@ -606,11 +619,12 @@ impl Scalar { /// Describes how the fields of a type are located in memory. #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum FieldPlacement { +pub enum FieldsShape { + /// Scalar primitives and `!`, which never have fields. + Primitive, + /// All fields start at no offset. The `usize` is the field count. - /// - /// In the case of primitives the number of fields is `0`. - Union(usize), + Union(NonZeroUsize), /// Array/vector-like placement, with all fields of identical types. Array { stride: Size, count: u64 }, @@ -645,38 +659,50 @@ pub enum FieldPlacement { }, } -impl FieldPlacement { +impl FieldsShape { pub fn count(&self) -> usize { match *self { - FieldPlacement::Union(count) => count, - FieldPlacement::Array { count, .. } => { + FieldsShape::Primitive => 0, + FieldsShape::Union(count) => count.get(), + FieldsShape::Array { count, .. } => { let usize_count = count as usize; assert_eq!(usize_count as u64, count); usize_count } - FieldPlacement::Arbitrary { ref offsets, .. } => offsets.len(), + FieldsShape::Arbitrary { ref offsets, .. } => offsets.len(), } } pub fn offset(&self, i: usize) -> Size { match *self { - FieldPlacement::Union(count) => { - assert!(i < count, "tried to access field {} of union with {} fields", i, count); + FieldsShape::Primitive => { + unreachable!("FieldsShape::offset: `Primitive`s have no fields") + } + FieldsShape::Union(count) => { + assert!( + i < count.get(), + "tried to access field {} of union with {} fields", + i, + count + ); Size::ZERO } - FieldPlacement::Array { stride, count } => { - let i = i as u64; + FieldsShape::Array { stride, count } => { + let i = u64::try_from(i).unwrap(); assert!(i < count); stride * i } - FieldPlacement::Arbitrary { ref offsets, .. } => offsets[i], + FieldsShape::Arbitrary { ref offsets, .. } => offsets[i], } } pub fn memory_index(&self, i: usize) -> usize { match *self { - FieldPlacement::Union(_) | FieldPlacement::Array { .. } => i, - FieldPlacement::Arbitrary { ref memory_index, .. } => { + FieldsShape::Primitive => { + unreachable!("FieldsShape::memory_index: `Primitive`s have no fields") + } + FieldsShape::Union(_) | FieldsShape::Array { .. } => i, + FieldsShape::Arbitrary { ref memory_index, .. } => { let r = memory_index[i]; assert_eq!(r as usize as u32, r); r as usize @@ -692,7 +718,7 @@ impl FieldPlacement { let use_small = self.count() <= inverse_small.len(); // We have to write this logic twice in order to keep the array small. - if let FieldPlacement::Arbitrary { ref memory_index, .. } = *self { + if let FieldsShape::Arbitrary { ref memory_index, .. } = *self { if use_small { for i in 0..self.count() { inverse_small[memory_index[i] as usize] = i as u8; @@ -706,8 +732,8 @@ impl FieldPlacement { } (0..self.count()).map(move |i| match *self { - FieldPlacement::Union(_) | FieldPlacement::Array { .. } => i, - FieldPlacement::Arbitrary { .. } => { + FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i, + FieldsShape::Arbitrary { .. } => { if use_small { inverse_small[i] as usize } else { @@ -790,7 +816,7 @@ pub enum Variants { discr: Scalar, discr_kind: DiscriminantKind, discr_index: usize, - variants: IndexVec, + variants: IndexVec, }, } @@ -873,10 +899,9 @@ impl Niche { } #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub struct LayoutDetails { +pub struct Layout { /// Says where the fields are located within the layout. - /// Primitives and uninhabited enums appear as unions without fields. - pub fields: FieldPlacement, + pub fields: FieldsShape, /// Encodes information about multi-variant layouts. /// Even with `Multiple` variants, a layout still has its own fields! Those are then @@ -904,14 +929,14 @@ pub struct LayoutDetails { pub size: Size, } -impl LayoutDetails { +impl Layout { pub fn scalar(cx: &C, scalar: Scalar) -> Self { let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar.clone()); let size = scalar.value.size(cx); let align = scalar.value.align(cx); - LayoutDetails { + Layout { variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Union(0), + fields: FieldsShape::Primitive, abi: Abi::Scalar(scalar), largest_niche, size, @@ -920,38 +945,38 @@ impl LayoutDetails { } } -/// The details of the layout of a type, alongside the type itself. +/// The layout of a type, alongside the type itself. /// Provides various type traversal APIs (e.g., recursing into fields). /// -/// Note that the details are NOT guaranteed to always be identical -/// to those obtained from `layout_of(ty)`, as we need to produce +/// Note that the layout is NOT guaranteed to always be identical +/// to that obtained from `layout_of(ty)`, as we need to produce /// layouts for which Rust types do not exist, such as enum variants /// or synthetic fields of enums (i.e., discriminants) and fat pointers. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct TyLayout<'a, Ty> { +pub struct TyAndLayout<'a, Ty> { pub ty: Ty, - pub details: &'a LayoutDetails, + pub layout: &'a Layout, } -impl<'a, Ty> Deref for TyLayout<'a, Ty> { - type Target = &'a LayoutDetails; - fn deref(&self) -> &&'a LayoutDetails { - &self.details +impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { + type Target = &'a Layout; + fn deref(&self) -> &&'a Layout { + &self.layout } } /// Trait for context types that can compute layouts of things. pub trait LayoutOf { type Ty; - type TyLayout; + type TyAndLayout; - fn layout_of(&self, ty: Self::Ty) -> Self::TyLayout; - fn spanned_layout_of(&self, ty: Self::Ty, _span: Span) -> Self::TyLayout { + fn layout_of(&self, ty: Self::Ty) -> Self::TyAndLayout; + fn spanned_layout_of(&self, ty: Self::Ty, _span: Span) -> Self::TyAndLayout { self.layout_of(ty) } } -/// The `TyLayout` above will always be a `MaybeResult>`. +/// The `TyAndLayout` above will always be a `MaybeResult>`. /// We can't add the bound due to the lifetime, but this trait is still useful when /// writing code that's generic over the `LayoutOf` impl. pub trait MaybeResult { @@ -1005,30 +1030,30 @@ pub struct PointeeInfo { pub safe: Option, } -pub trait TyLayoutMethods<'a, C: LayoutOf>: Sized { +pub trait TyAndLayoutMethods<'a, C: LayoutOf>: Sized { fn for_variant( - this: TyLayout<'a, Self>, + this: TyAndLayout<'a, Self>, cx: &C, variant_index: VariantIdx, - ) -> TyLayout<'a, Self>; - fn field(this: TyLayout<'a, Self>, cx: &C, i: usize) -> C::TyLayout; - fn pointee_info_at(this: TyLayout<'a, Self>, cx: &C, offset: Size) -> Option; + ) -> TyAndLayout<'a, Self>; + fn field(this: TyAndLayout<'a, Self>, cx: &C, i: usize) -> C::TyAndLayout; + fn pointee_info_at(this: TyAndLayout<'a, Self>, cx: &C, offset: Size) -> Option; } -impl<'a, Ty> TyLayout<'a, Ty> { +impl<'a, Ty> TyAndLayout<'a, Ty> { pub fn for_variant(self, cx: &C, variant_index: VariantIdx) -> Self where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf, { Ty::for_variant(self, cx, variant_index) } - /// Callers might want to use `C: LayoutOf>` + /// Callers might want to use `C: LayoutOf>` /// to allow recursion (see `might_permit_zero_init` below for an example). - pub fn field(self, cx: &C, i: usize) -> C::TyLayout + pub fn field(self, cx: &C, i: usize) -> C::TyAndLayout where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf, { Ty::field(self, cx, i) @@ -1036,14 +1061,14 @@ impl<'a, Ty> TyLayout<'a, Ty> { pub fn pointee_info_at(self, cx: &C, offset: Size) -> Option where - Ty: TyLayoutMethods<'a, C>, + Ty: TyAndLayoutMethods<'a, C>, C: LayoutOf, { Ty::pointee_info_at(self, cx, offset) } } -impl<'a, Ty> TyLayout<'a, Ty> { +impl<'a, Ty> TyAndLayout<'a, Ty> { /// Returns `true` if the layout corresponds to an unsized type. pub fn is_unsized(&self) -> bool { self.abi.is_unsized() @@ -1070,8 +1095,8 @@ impl<'a, Ty> TyLayout<'a, Ty> { pub fn might_permit_raw_init(self, cx: &C, zero: bool) -> Result where Self: Copy, - Ty: TyLayoutMethods<'a, C>, - C: LayoutOf> + HasDataLayout, + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf> + HasDataLayout, { let scalar_allows_raw_init = move |s: &Scalar| -> bool { if zero { @@ -1097,7 +1122,7 @@ impl<'a, Ty> TyLayout<'a, Ty> { }; if !valid { // This is definitely not okay. - trace!("might_permit_raw_init({:?}, zero={}): not valid", self.details, zero); + trace!("might_permit_raw_init({:?}, zero={}): not valid", self.layout, zero); return Ok(false); } diff --git a/src/librustc_target/asm/aarch64.rs b/src/librustc_target/asm/aarch64.rs new file mode 100644 index 0000000000000..e7c9edea7653a --- /dev/null +++ b/src/librustc_target/asm/aarch64.rs @@ -0,0 +1,156 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + AArch64 AArch64InlineAsmRegClass { + reg, + vreg, + vreg_low16, + } +} + +impl AArch64InlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::reg => &['w', 'x'], + Self::vreg | Self::vreg_low16 => &['b', 'h', 's', 'd', 'q', 'v'], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::reg => match ty.size().bits() { + 64 => None, + _ => Some(('w', "w0")), + }, + Self::vreg | Self::vreg_low16 => match ty.size().bits() { + 8 => Some(('b', "b0")), + 16 => Some(('h', "h0")), + 32 => Some(('s', "s0")), + 64 => Some(('d', "d0")), + 128 => Some(('q', "q0")), + _ => None, + }, + } + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::reg => Some(('x', "x0")), + Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + } + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg => types! { _: I8, I16, I32, I64, F32, F64; }, + Self::vreg | Self::vreg_low16 => types! { + "fp": I8, I16, I32, I64, F32, F64, + VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1), + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + }, + } + } +} + +def_regs! { + AArch64 AArch64InlineAsmReg AArch64InlineAsmRegClass { + x0: reg = ["x0", "w0"], + x1: reg = ["x1", "w1"], + x2: reg = ["x2", "w2"], + x3: reg = ["x3", "w3"], + x4: reg = ["x4", "w4"], + x5: reg = ["x5", "w5"], + x6: reg = ["x6", "w6"], + x7: reg = ["x7", "w7"], + x8: reg = ["x8", "w8"], + x9: reg = ["x9", "w9"], + x10: reg = ["x10", "w10"], + x11: reg = ["x11", "w11"], + x12: reg = ["x12", "w12"], + x13: reg = ["x13", "w13"], + x14: reg = ["x14", "w14"], + x15: reg = ["x15", "w15"], + x16: reg = ["x16", "w16"], + x17: reg = ["x17", "w17"], + x18: reg = ["x18", "w18"], + x19: reg = ["x19", "w19"], + x20: reg = ["x20", "w20"], + x21: reg = ["x21", "w21"], + x22: reg = ["x22", "w22"], + x23: reg = ["x23", "w23"], + x24: reg = ["x24", "w24"], + x25: reg = ["x25", "w25"], + x26: reg = ["x26", "w26"], + x27: reg = ["x27", "w27"], + x28: reg = ["x28", "w28"], + x30: reg = ["x30", "w30", "lr"], + v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"], + v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"], + v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"], + v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3"], + v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4"], + v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5"], + v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6"], + v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7"], + v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8"], + v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9"], + v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10"], + v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11"], + v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12"], + v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13"], + v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14"], + v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15"], + v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16"], + v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17"], + v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18"], + v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19"], + v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20"], + v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21"], + v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22"], + v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23"], + v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24"], + v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25"], + v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26"], + v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27"], + v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28"], + v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"], + v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"], + v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"], + #error = ["x29", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["sp", "wsp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["xzr", "wzr"] => + "the zero register cannot be used as an operand for inline asm", + } +} + +impl AArch64InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + modifier: Option, + ) -> fmt::Result { + let (prefix, index) = if (self as u32) < Self::v0 as u32 { + (modifier.unwrap_or('x'), self as u32 - Self::x0 as u32) + } else { + (modifier.unwrap_or('v'), self as u32 - Self::v0 as u32) + }; + assert!(index < 32); + write!(out, "{}{}", prefix, index) + } +} diff --git a/src/librustc_target/asm/arm.rs b/src/librustc_target/asm/arm.rs new file mode 100644 index 0000000000000..1798b2a094975 --- /dev/null +++ b/src/librustc_target/asm/arm.rs @@ -0,0 +1,266 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + Arm ArmInlineAsmRegClass { + reg, + reg_thumb, + sreg, + sreg_low16, + dreg, + dreg_low16, + dreg_low8, + qreg, + qreg_low8, + qreg_low4, + } +} + +impl ArmInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::qreg | Self::qreg_low8 | Self::qreg_low4 => &['e', 'f'], + _ => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg | Self::reg_thumb => types! { _: I8, I16, I32, F32; }, + Self::sreg | Self::sreg_low16 => types! { "vfp2": I32, F32; }, + Self::dreg | Self::dreg_low16 | Self::dreg_low8 => types! { + "vfp2": I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2); + }, + Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! { + "neon": VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4); + }, + } + } +} + +def_regs! { + Arm ArmInlineAsmReg ArmInlineAsmRegClass { + r0: reg, reg_thumb = ["r0", "a1"], + r1: reg, reg_thumb = ["r1", "a2"], + r2: reg, reg_thumb = ["r2", "a3"], + r3: reg, reg_thumb = ["r3", "a4"], + r4: reg, reg_thumb = ["r4", "v1"], + r5: reg, reg_thumb = ["r5", "v2"], + r6: reg, reg_thumb = ["r6", "v3"], + r7: reg, reg_thumb = ["r7", "v4"], + r8: reg = ["r8", "v5"], + r9: reg = ["r9", "v6", "rfp"], + r10: reg = ["r10", "sl"], + r12: reg = ["r12", "ip"], + r14: reg = ["r14", "lr"], + s0: sreg, sreg_low16 = ["s0"], + s1: sreg, sreg_low16 = ["s1"], + s2: sreg, sreg_low16 = ["s2"], + s3: sreg, sreg_low16 = ["s3"], + s4: sreg, sreg_low16 = ["s4"], + s5: sreg, sreg_low16 = ["s5"], + s6: sreg, sreg_low16 = ["s6"], + s7: sreg, sreg_low16 = ["s7"], + s8: sreg, sreg_low16 = ["s8"], + s9: sreg, sreg_low16 = ["s9"], + s10: sreg, sreg_low16 = ["s10"], + s11: sreg, sreg_low16 = ["s11"], + s12: sreg, sreg_low16 = ["s12"], + s13: sreg, sreg_low16 = ["s13"], + s14: sreg, sreg_low16 = ["s14"], + s15: sreg, sreg_low16 = ["s15"], + s16: sreg = ["s16"], + s17: sreg = ["s17"], + s18: sreg = ["s18"], + s19: sreg = ["s19"], + s20: sreg = ["s20"], + s21: sreg = ["s21"], + s22: sreg = ["s22"], + s23: sreg = ["s23"], + s24: sreg = ["s24"], + s25: sreg = ["s25"], + s26: sreg = ["s26"], + s27: sreg = ["s27"], + s28: sreg = ["s28"], + s29: sreg = ["s29"], + s30: sreg = ["s30"], + s31: sreg = ["s31"], + d0: dreg, dreg_low16, dreg_low8 = ["d0"], + d1: dreg, dreg_low16, dreg_low8 = ["d1"], + d2: dreg, dreg_low16, dreg_low8 = ["d2"], + d3: dreg, dreg_low16, dreg_low8 = ["d3"], + d4: dreg, dreg_low16, dreg_low8 = ["d4"], + d5: dreg, dreg_low16, dreg_low8 = ["d5"], + d6: dreg, dreg_low16, dreg_low8 = ["d6"], + d7: dreg, dreg_low16, dreg_low8 = ["d7"], + d8: dreg, dreg_low16 = ["d8"], + d9: dreg, dreg_low16 = ["d9"], + d10: dreg, dreg_low16 = ["d10"], + d11: dreg, dreg_low16 = ["d11"], + d12: dreg, dreg_low16 = ["d12"], + d13: dreg, dreg_low16 = ["d13"], + d14: dreg, dreg_low16 = ["d14"], + d15: dreg, dreg_low16 = ["d15"], + d16: dreg = ["d16"], + d17: dreg = ["d17"], + d18: dreg = ["d18"], + d19: dreg = ["d19"], + d20: dreg = ["d20"], + d21: dreg = ["d21"], + d22: dreg = ["d22"], + d23: dreg = ["d23"], + d24: dreg = ["d24"], + d25: dreg = ["d25"], + d26: dreg = ["d26"], + d27: dreg = ["d27"], + d28: dreg = ["d28"], + d29: dreg = ["d29"], + d30: dreg = ["d30"], + d31: dreg = ["d31"], + q0: qreg, qreg_low8, qreg_low4 = ["q0"], + q1: qreg, qreg_low8, qreg_low4 = ["q1"], + q2: qreg, qreg_low8, qreg_low4 = ["q2"], + q3: qreg, qreg_low8, qreg_low4 = ["q3"], + q4: qreg, qreg_low8 = ["q4"], + q5: qreg, qreg_low8 = ["q5"], + q6: qreg, qreg_low8 = ["q6"], + q7: qreg, qreg_low8 = ["q7"], + q8: qreg = ["q8"], + q9: qreg = ["q9"], + q10: qreg = ["q10"], + q11: qreg = ["q11"], + q12: qreg = ["q12"], + q13: qreg = ["q13"], + q14: qreg = ["q14"], + q15: qreg = ["q15"], + #error = ["r11", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["r13", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r15", "pc"] => + "the program pointer cannot be used as an operand for inline asm", + } +} + +impl ArmInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + modifier: Option, + ) -> fmt::Result { + // Only qreg is allowed to have modifiers. This should have been + // validated already by now. + if let Some(modifier) = modifier { + let index = self as u32 - Self::q0 as u32; + assert!(index < 16); + let index = index * 2 + (modifier == 'f') as u32; + write!(out, "d{}", index) + } else { + out.write_str(self.name()) + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(ArmInlineAsmReg)) { + cb(self); + + macro_rules! reg_conflicts { + ( + $( + $q:ident : $d0:ident $d1:ident : $s0:ident $s1:ident $s2:ident $s3:ident + ),*; + $( + $q_high:ident : $d0_high:ident $d1_high:ident + ),*; + ) => { + match self { + $( + Self::$q => { + cb(Self::$d0); + cb(Self::$d1); + cb(Self::$s0); + cb(Self::$s1); + cb(Self::$s2); + cb(Self::$s3); + } + Self::$d0 => { + cb(Self::$q); + cb(Self::$s0); + cb(Self::$s1); + } + Self::$d1 => { + cb(Self::$q); + cb(Self::$s2); + cb(Self::$s3); + } + Self::$s0 | Self::$s1 => { + cb(Self::$q); + cb(Self::$d0); + } + Self::$s2 | Self::$s3 => { + cb(Self::$q); + cb(Self::$d1); + } + )* + $( + Self::$q_high => { + cb(Self::$d0_high); + cb(Self::$d1_high); + } + Self::$d0_high | Self::$d1_high => { + cb(Self::$q_high); + } + )* + _ => {}, + } + }; + } + + // ARM's floating-point register file is interesting in that it can be + // viewed as 16 128-bit registers, 32 64-bit registers or 32 32-bit + // registers. Because these views overlap, the registers of different + // widths will conflict (e.g. d0 overlaps with s0 and s1, and q1 + // overlaps with d2 and d3). + // + // See section E1.3.1 of the ARM Architecture Reference Manual for + // ARMv8-A for more details. + reg_conflicts! { + q0 : d0 d1 : s0 s1 s2 s3, + q1 : d2 d3 : s4 s5 s6 s7, + q2 : d4 d5 : s8 s9 s10 s11, + q3 : d6 d7 : s12 s13 s14 s15, + q4 : d8 d9 : s16 s17 s18 s19, + q5 : d10 d11 : s20 s21 s22 s23, + q6 : d12 d13 : s24 s25 s26 s27, + q7 : d14 d15 : s28 s29 s30 s31; + q8 : d16 d17, + q9 : d18 d19, + q10 : d20 d21, + q11 : d22 d23, + q12 : d24 d25, + q13 : d26 d27, + q14 : d28 d29, + q15 : d30 d31; + } + } +} diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs new file mode 100644 index 0000000000000..05aa85ecb7448 --- /dev/null +++ b/src/librustc_target/asm/mod.rs @@ -0,0 +1,522 @@ +use crate::abi::Size; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; +use std::str::FromStr; + +#[macro_use] +macro_rules! def_reg_class { + ($arch:ident $arch_regclass:ident { + $( + $class:ident, + )* + }) => { + #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_regclass { + $($class,)* + } + + impl $arch_regclass { + pub fn name(self) -> &'static str { + match self { + $(Self::$class => stringify!($class),)* + } + } + + pub fn parse(_arch: super::InlineAsmArch, name: &str) -> Result { + match name { + $( + stringify!($class) => Ok(Self::$class), + )* + _ => Err("unknown register class"), + } + } + } + + pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet, + > { + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + use super::InlineAsmRegClass; + let mut map = FxHashMap::default(); + $( + map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default()); + )* + map + } + } +} + +#[macro_use] +macro_rules! def_regs { + ($arch:ident $arch_reg:ident $arch_regclass:ident { + $( + $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, + )* + $( + #error = [$($bad_reg:literal),+] => $error:literal, + )* + }) => { + #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_reg { + $($reg,)* + } + + impl $arch_reg { + pub fn name(self) -> &'static str { + match self { + $(Self::$reg => $reg_name,)* + } + } + + pub fn reg_class(self) -> $arch_regclass { + match self { + $(Self::$reg => $arch_regclass::$class,)* + } + } + + pub fn parse( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + name: &str, + ) -> Result { + match name { + $( + $($alias)|* | $reg_name => { + $($filter(_arch, &mut _has_feature, false)?;)? + Ok(Self::$reg) + } + )* + $( + $($bad_reg)|* => Err($error), + )* + _ => Err("unknown register"), + } + } + } + + pub(super) fn fill_reg_map( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + map: &mut rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet, + >, + ) { + use super::{InlineAsmReg, InlineAsmRegClass}; + $( + if $($filter(_arch, &mut _has_feature, true).is_ok() &&)? true { + if let Some(set) = map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + $( + if let Some(set) = map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + )* + } + )* + } + } +} + +#[macro_use] +macro_rules! types { + ( + $(_ : $($ty:expr),+;)? + $($feature:literal: $($ty2:expr),+;)* + ) => { + { + use super::InlineAsmType::*; + &[ + $($( + ($ty, None), + )*)? + $($( + ($ty2, Some($feature)), + )*)* + ] + } + }; +} + +mod aarch64; +mod arm; +mod riscv; +mod x86; + +pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; +pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; +pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; +pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; + +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmArch { + X86, + X86_64, + Arm, + AArch64, + RiscV32, + RiscV64, +} + +impl FromStr for InlineAsmArch { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "x86" => Ok(Self::X86), + "x86_64" => Ok(Self::X86_64), + "arm" => Ok(Self::Arm), + "aarch64" => Ok(Self::AArch64), + "riscv32" => Ok(Self::RiscV32), + "riscv64" => Ok(Self::RiscV64), + _ => Err(()), + } + } +} + +#[derive( + Copy, + Clone, + RustcEncodable, + RustcDecodable, + Debug, + Eq, + PartialEq, + Hash, + HashStable_Generic +)] +pub enum InlineAsmReg { + X86(X86InlineAsmReg), + Arm(ArmInlineAsmReg), + AArch64(AArch64InlineAsmReg), + RiscV(RiscVInlineAsmReg), +} + +impl InlineAsmReg { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + } + } + + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()), + Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), + Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), + Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), + } + } + + pub fn parse( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + name: Symbol, + ) -> Result { + // FIXME: use direct symbol comparison for register names + name.with(|name| { + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmReg::parse(arch, has_feature, name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, name)?), + InlineAsmArch::AArch64 => { + Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, name)?) + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, name)?) + } + }) + }) + } + + // NOTE: This function isn't used at the moment, but is needed to support + // falling back to an external assembler. + pub fn emit( + self, + out: &mut dyn fmt::Write, + arch: InlineAsmArch, + modifier: Option, + ) -> fmt::Result { + match self { + Self::X86(r) => r.emit(out, arch, modifier), + Self::Arm(r) => r.emit(out, arch, modifier), + Self::AArch64(r) => r.emit(out, arch, modifier), + Self::RiscV(r) => r.emit(out, arch, modifier), + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { + match self { + Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))), + Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), + Self::AArch64(_) => cb(self), + Self::RiscV(_) => cb(self), + } + } +} + +#[derive( + Copy, + Clone, + RustcEncodable, + RustcDecodable, + Debug, + Eq, + PartialEq, + Hash, + HashStable_Generic +)] +pub enum InlineAsmRegClass { + X86(X86InlineAsmRegClass), + Arm(ArmInlineAsmRegClass), + AArch64(AArch64InlineAsmRegClass), + RiscV(RiscVInlineAsmRegClass), +} + +impl InlineAsmRegClass { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + } + } + + /// Returns a suggested register class to use for this type. This is called + /// after type checking via `supported_types` fails to give a better error + /// message to the user. + pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { + match self { + Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86), + Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), + Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), + Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), + } + } + + /// Returns a suggested template modifier to use for this type and an + /// example of a register named formatted with it. + /// + /// Such suggestions are useful if a type smaller than the full register + /// size is used and a modifier can be used to point to the subregister of + /// the correct size. + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.suggest_modifier(arch, ty), + Self::Arm(r) => r.suggest_modifier(arch, ty), + Self::AArch64(r) => r.suggest_modifier(arch, ty), + Self::RiscV(r) => r.suggest_modifier(arch, ty), + } + } + + /// Returns the default modifier for this register and an example of a + /// register named formatted with it. + /// + /// This is only needed when the register class can suggest a modifier, so + /// that the user can be shown how to get the default behavior without a + /// warning. + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.default_modifier(arch), + Self::Arm(r) => r.default_modifier(arch), + Self::AArch64(r) => r.default_modifier(arch), + Self::RiscV(r) => r.default_modifier(arch), + } + } + + /// Returns a list of supported types for this register class, each with a + /// options target feature required to use this type. + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::X86(r) => r.supported_types(arch), + Self::Arm(r) => r.supported_types(arch), + Self::AArch64(r) => r.supported_types(arch), + Self::RiscV(r) => r.supported_types(arch), + } + } + + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { + // FIXME: use direct symbol comparison for register class names + name.with(|name| { + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::AArch64 => { + Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) + } + }) + }) + } + + /// Returns the list of template modifiers that can be used with this + /// register class. + pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { + match self { + Self::X86(r) => r.valid_modifiers(arch), + Self::Arm(r) => r.valid_modifiers(arch), + Self::AArch64(r) => r.valid_modifiers(arch), + Self::RiscV(r) => r.valid_modifiers(arch), + } + } +} + +#[derive( + Copy, + Clone, + RustcEncodable, + RustcDecodable, + Debug, + Eq, + PartialEq, + Hash, + HashStable_Generic +)] +pub enum InlineAsmRegOrRegClass { + Reg(InlineAsmReg), + RegClass(InlineAsmRegClass), +} + +impl InlineAsmRegOrRegClass { + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::Reg(r) => r.reg_class(), + Self::RegClass(r) => r, + } + } +} + +impl fmt::Display for InlineAsmRegOrRegClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Reg(r) => write!(f, "\"{}\"", r.name()), + Self::RegClass(r) => f.write_str(r.name()), + } + } +} + +/// Set of types which can be used with a particular register class. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum InlineAsmType { + I8, + I16, + I32, + I64, + I128, + F32, + F64, + VecI8(u64), + VecI16(u64), + VecI32(u64), + VecI64(u64), + VecI128(u64), + VecF32(u64), + VecF64(u64), +} + +impl InlineAsmType { + pub fn is_integer(self) -> bool { + match self { + Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 => true, + _ => false, + } + } + + pub fn size(self) -> Size { + Size::from_bytes(match self { + Self::I8 => 1, + Self::I16 => 2, + Self::I32 => 4, + Self::I64 => 8, + Self::I128 => 16, + Self::F32 => 4, + Self::F64 => 8, + Self::VecI8(n) => n * 1, + Self::VecI16(n) => n * 2, + Self::VecI32(n) => n * 4, + Self::VecI64(n) => n * 8, + Self::VecI128(n) => n * 16, + Self::VecF32(n) => n * 4, + Self::VecF64(n) => n * 8, + }) + } +} + +impl fmt::Display for InlineAsmType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::I8 => f.write_str("i8"), + Self::I16 => f.write_str("i16"), + Self::I32 => f.write_str("i32"), + Self::I64 => f.write_str("i64"), + Self::I128 => f.write_str("i128"), + Self::F32 => f.write_str("f32"), + Self::F64 => f.write_str("f64"), + Self::VecI8(n) => write!(f, "i8x{}", n), + Self::VecI16(n) => write!(f, "i16x{}", n), + Self::VecI32(n) => write!(f, "i32x{}", n), + Self::VecI64(n) => write!(f, "i64x{}", n), + Self::VecI128(n) => write!(f, "i128x{}", n), + Self::VecF32(n) => write!(f, "f32x{}", n), + Self::VecF64(n) => write!(f, "f64x{}", n), + } + } +} + +/// Returns the full set of allocatable registers for a given architecture. +/// +/// The registers are structured as a map containing the set of allocatable +/// registers in each register class. A particular register may be allocatable +/// from multiple register classes, in which case it will appear multiple times +/// in the map. +// NOTE: This function isn't used at the moment, but is needed to support +// falling back to an external assembler. +pub fn allocatable_registers( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, +) -> FxHashMap> { + match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + let mut map = x86::regclass_map(); + x86::fill_reg_map(arch, has_feature, &mut map); + map + } + InlineAsmArch::Arm => { + let mut map = arm::regclass_map(); + arm::fill_reg_map(arch, has_feature, &mut map); + map + } + InlineAsmArch::AArch64 => { + let mut map = aarch64::regclass_map(); + aarch64::fill_reg_map(arch, has_feature, &mut map); + map + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + let mut map = riscv::regclass_map(); + riscv::fill_reg_map(arch, has_feature, &mut map); + map + } + } +} diff --git a/src/librustc_target/asm/riscv.rs b/src/librustc_target/asm/riscv.rs new file mode 100644 index 0000000000000..3ff542247ff02 --- /dev/null +++ b/src/librustc_target/asm/riscv.rs @@ -0,0 +1,145 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + RiscV RiscVInlineAsmRegClass { + reg, + freg, + } +} + +impl RiscVInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg => { + if arch == InlineAsmArch::RiscV64 { + types! { _: I8, I16, I32, I64, F32, F64; } + } else { + types! { _: I8, I16, I32, F32; } + } + } + Self::freg => types! { "f": F32; "d": F64; }, + } + } +} + +fn not_e( + _arch: InlineAsmArch, + mut has_feature: impl FnMut(&str) -> bool, + _allocating: bool, +) -> Result<(), &'static str> { + if has_feature("e") { + Err("register can't be used with the `e` target feature") + } else { + Ok(()) + } +} + +def_regs! { + RiscV RiscVInlineAsmReg RiscVInlineAsmRegClass { + x1: reg = ["x1", "ra"], + x5: reg = ["x5", "t0"], + x6: reg = ["x6", "t1"], + x7: reg = ["x7", "t2"], + x9: reg = ["x9", "s1"], + x10: reg = ["x10", "a0"], + x11: reg = ["x11", "a1"], + x12: reg = ["x12", "a2"], + x13: reg = ["x13", "a3"], + x14: reg = ["x14", "a4"], + x15: reg = ["x15", "a5"], + x16: reg = ["x16", "a6"] % not_e, + x17: reg = ["x17", "a7"] % not_e, + x18: reg = ["x18", "s2"] % not_e, + x19: reg = ["x19", "s3"] % not_e, + x20: reg = ["x20", "s4"] % not_e, + x21: reg = ["x21", "s5"] % not_e, + x22: reg = ["x22", "s6"] % not_e, + x23: reg = ["x23", "s7"] % not_e, + x24: reg = ["x24", "s8"] % not_e, + x25: reg = ["x25", "s9"] % not_e, + x26: reg = ["x26", "s10"] % not_e, + x27: reg = ["x27", "s11"] % not_e, + x28: reg = ["x28", "t3"] % not_e, + x29: reg = ["x29", "t4"] % not_e, + x30: reg = ["x30", "t5"] % not_e, + x31: reg = ["x31", "t6"] % not_e, + f0: freg = ["f0", "ft0"], + f1: freg = ["f1", "ft1"], + f2: freg = ["f2", "ft2"], + f3: freg = ["f3", "ft3"], + f4: freg = ["f4", "ft4"], + f5: freg = ["f5", "ft5"], + f6: freg = ["f6", "ft6"], + f7: freg = ["f7", "ft7"], + f8: freg = ["f8", "fs0"], + f9: freg = ["f9", "fs1"], + f10: freg = ["f10", "fa0"], + f11: freg = ["f11", "fa1"], + f12: freg = ["f12", "fa2"], + f13: freg = ["f13", "fa3"], + f14: freg = ["f14", "fa4"], + f15: freg = ["f15", "fa5"], + f16: freg = ["f16", "fa6"], + f17: freg = ["f17", "fa7"], + f18: freg = ["f18", "fs2"], + f19: freg = ["f19", "fs3"], + f20: freg = ["f20", "fs4"], + f21: freg = ["f21", "fs5"], + f22: freg = ["f22", "fs6"], + f23: freg = ["f23", "fs7"], + f24: freg = ["f24", "fs8"], + f25: freg = ["f25", "fs9"], + f26: freg = ["f26", "fs10"], + f27: freg = ["f27", "fs11"], + f28: freg = ["f28", "ft8"], + f29: freg = ["f29", "ft9"], + f30: freg = ["f30", "ft10"], + f31: freg = ["f31", "ft11"], + #error = ["x8", "s0", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["x2", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["x3", "gp"] => + "the global pointer cannot be used as an operand for inline asm", + #error = ["x4", "tp"] => + "the thread pointer cannot be used as an operand for inline asm" , + #error = ["x0", "zero"] => + "the zero register cannot be used as an operand for inline asm", + } +} + +impl RiscVInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/src/librustc_target/asm/x86.rs b/src/librustc_target/asm/x86.rs new file mode 100644 index 0000000000000..ed51b526414d1 --- /dev/null +++ b/src/librustc_target/asm/x86.rs @@ -0,0 +1,424 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + X86 X86InlineAsmRegClass { + reg, + reg_abcd, + reg_byte, + xmm_reg, + ymm_reg, + zmm_reg, + kreg, + } +} + +impl X86InlineAsmRegClass { + pub fn valid_modifiers(self, arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::reg => { + if arch == InlineAsmArch::X86_64 { + &['l', 'x', 'e', 'r'] + } else { + &['x', 'e'] + } + } + Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + &['l', 'h', 'x', 'e', 'r'] + } else { + &['l', 'h', 'x', 'e'] + } + } + Self::reg_byte => &[], + Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'], + Self::kreg => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option { + match self { + Self::reg | Self::reg_abcd if ty.size().bits() == 8 => Some(Self::reg_byte), + _ => None, + } + } + + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::reg => match ty.size().bits() { + 16 => Some(('x', "ax")), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + _ => None, + }, + Self::reg_abcd => match ty.size().bits() { + 16 => Some(('x', "ax")), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + _ => None, + }, + Self::reg_byte => None, + Self::xmm_reg => None, + Self::ymm_reg => match ty.size().bits() { + 256 => None, + _ => Some(('x', "xmm0")), + }, + Self::zmm_reg => match ty.size().bits() { + 512 => None, + 256 => Some(('y', "ymm0")), + _ => Some(('x', "xmm0")), + }, + Self::kreg => None, + } + } + + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::reg | Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + Some(('r', "rax")) + } else { + Some(('e', "eax")) + } + } + Self::reg_byte => None, + Self::xmm_reg => Some(('x', "xmm0")), + Self::ymm_reg => Some(('y', "ymm0")), + Self::zmm_reg => Some(('z', "zmm0")), + Self::kreg => None, + } + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg | Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + types! { _: I16, I32, I64, F32, F64; } + } else { + types! { _: I16, I32, F32; } + } + } + Self::reg_byte => types! { _: I8; }, + Self::xmm_reg => types! { + "sse": I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + }, + Self::ymm_reg => types! { + "avx": I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); + }, + Self::zmm_reg => types! { + "avx512f": I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4), + VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF32(16), VecF64(8); + }, + Self::kreg => types! { + "avx512f": I8, I16; + "avx512bw": I32, I64; + }, + } + } +} + +fn x86_64_only( + arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + _allocating: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86 => Err("register is only available on x86_64"), + InlineAsmArch::X86_64 => Ok(()), + _ => unreachable!(), + } +} + +fn high_byte( + arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + allocating: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86_64 if allocating => { + // The error message isn't actually used... + Err("high byte registers are not allocated by reg_byte") + } + _ => Ok(()), + } +} + +def_regs! { + X86 X86InlineAsmReg X86InlineAsmRegClass { + ax: reg, reg_abcd = ["ax", "eax", "rax"], + bx: reg, reg_abcd = ["bx", "ebx", "rbx"], + cx: reg, reg_abcd = ["cx", "ecx", "rcx"], + dx: reg, reg_abcd = ["dx", "edx", "rdx"], + si: reg = ["si", "esi", "rsi"], + di: reg = ["di", "edi", "rdi"], + r8: reg = ["r8", "r8w", "r8d"] % x86_64_only, + r9: reg = ["r9", "r9w", "r9d"] % x86_64_only, + r10: reg = ["r10", "r10w", "r10d"] % x86_64_only, + r11: reg = ["r11", "r11w", "r11d"] % x86_64_only, + r12: reg = ["r12", "r12w", "r12d"] % x86_64_only, + r13: reg = ["r13", "r13w", "r13d"] % x86_64_only, + r14: reg = ["r14", "r14w", "r14d"] % x86_64_only, + r15: reg = ["r15", "r15w", "r15d"] % x86_64_only, + al: reg_byte = ["al"], + ah: reg_byte = ["ah"] % high_byte, + bl: reg_byte = ["bl"], + bh: reg_byte = ["bh"] % high_byte, + cl: reg_byte = ["cl"], + ch: reg_byte = ["ch"] % high_byte, + dl: reg_byte = ["dl"], + dh: reg_byte = ["dh"] % high_byte, + sil: reg_byte = ["sil"] % x86_64_only, + dil: reg_byte = ["dil"] % x86_64_only, + r8b: reg_byte = ["r8b"] % x86_64_only, + r9b: reg_byte = ["r9b"] % x86_64_only, + r10b: reg_byte = ["r10b"] % x86_64_only, + r11b: reg_byte = ["r11b"] % x86_64_only, + r12b: reg_byte = ["r12b"] % x86_64_only, + r13b: reg_byte = ["r13b"] % x86_64_only, + r14b: reg_byte = ["r14b"] % x86_64_only, + r15b: reg_byte = ["r15b"] % x86_64_only, + xmm0: xmm_reg = ["xmm0"], + xmm1: xmm_reg = ["xmm1"], + xmm2: xmm_reg = ["xmm2"], + xmm3: xmm_reg = ["xmm3"], + xmm4: xmm_reg = ["xmm4"], + xmm5: xmm_reg = ["xmm5"], + xmm6: xmm_reg = ["xmm6"], + xmm7: xmm_reg = ["xmm7"], + xmm8: xmm_reg = ["xmm8"] % x86_64_only, + xmm9: xmm_reg = ["xmm9"] % x86_64_only, + xmm10: xmm_reg = ["xmm10"] % x86_64_only, + xmm11: xmm_reg = ["xmm11"] % x86_64_only, + xmm12: xmm_reg = ["xmm12"] % x86_64_only, + xmm13: xmm_reg = ["xmm13"] % x86_64_only, + xmm14: xmm_reg = ["xmm14"] % x86_64_only, + xmm15: xmm_reg = ["xmm15"] % x86_64_only, + ymm0: ymm_reg = ["ymm0"], + ymm1: ymm_reg = ["ymm1"], + ymm2: ymm_reg = ["ymm2"], + ymm3: ymm_reg = ["ymm3"], + ymm4: ymm_reg = ["ymm4"], + ymm5: ymm_reg = ["ymm5"], + ymm6: ymm_reg = ["ymm6"], + ymm7: ymm_reg = ["ymm7"], + ymm8: ymm_reg = ["ymm8"] % x86_64_only, + ymm9: ymm_reg = ["ymm9"] % x86_64_only, + ymm10: ymm_reg = ["ymm10"] % x86_64_only, + ymm11: ymm_reg = ["ymm11"] % x86_64_only, + ymm12: ymm_reg = ["ymm12"] % x86_64_only, + ymm13: ymm_reg = ["ymm13"] % x86_64_only, + ymm14: ymm_reg = ["ymm14"] % x86_64_only, + ymm15: ymm_reg = ["ymm15"] % x86_64_only, + zmm0: zmm_reg = ["zmm0"], + zmm1: zmm_reg = ["zmm1"], + zmm2: zmm_reg = ["zmm2"], + zmm3: zmm_reg = ["zmm3"], + zmm4: zmm_reg = ["zmm4"], + zmm5: zmm_reg = ["zmm5"], + zmm6: zmm_reg = ["zmm6"], + zmm7: zmm_reg = ["zmm7"], + zmm8: zmm_reg = ["zmm8"] % x86_64_only, + zmm9: zmm_reg = ["zmm9"] % x86_64_only, + zmm10: zmm_reg = ["zmm10"] % x86_64_only, + zmm11: zmm_reg = ["zmm11"] % x86_64_only, + zmm12: zmm_reg = ["zmm12"] % x86_64_only, + zmm13: zmm_reg = ["zmm13"] % x86_64_only, + zmm14: zmm_reg = ["zmm14"] % x86_64_only, + zmm15: zmm_reg = ["zmm15"] % x86_64_only, + zmm16: zmm_reg = ["zmm16", "xmm16", "ymm16"] % x86_64_only, + zmm17: zmm_reg = ["zmm17", "xmm17", "ymm17"] % x86_64_only, + zmm18: zmm_reg = ["zmm18", "xmm18", "ymm18"] % x86_64_only, + zmm19: zmm_reg = ["zmm19", "xmm19", "ymm19"] % x86_64_only, + zmm20: zmm_reg = ["zmm20", "xmm20", "ymm20"] % x86_64_only, + zmm21: zmm_reg = ["zmm21", "xmm21", "ymm21"] % x86_64_only, + zmm22: zmm_reg = ["zmm22", "xmm22", "ymm22"] % x86_64_only, + zmm23: zmm_reg = ["zmm23", "xmm23", "ymm23"] % x86_64_only, + zmm24: zmm_reg = ["zmm24", "xmm24", "ymm24"] % x86_64_only, + zmm25: zmm_reg = ["zmm25", "xmm25", "ymm25"] % x86_64_only, + zmm26: zmm_reg = ["zmm26", "xmm26", "ymm26"] % x86_64_only, + zmm27: zmm_reg = ["zmm27", "xmm27", "ymm27"] % x86_64_only, + zmm28: zmm_reg = ["zmm28", "xmm28", "ymm28"] % x86_64_only, + zmm29: zmm_reg = ["zmm29", "xmm29", "ymm29"] % x86_64_only, + zmm30: zmm_reg = ["zmm30", "xmm30", "ymm30"] % x86_64_only, + zmm31: zmm_reg = ["zmm31", "xmm31", "ymm31"] % x86_64_only, + k1: kreg = ["k1"], + k2: kreg = ["k2"], + k3: kreg = ["k3"], + k4: kreg = ["k4"], + k5: kreg = ["k5"], + k6: kreg = ["k6"], + k7: kreg = ["k7"], + #error = ["bp", "bpl", "ebp", "rbp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["sp", "spl", "esp", "rsp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["ip", "eip", "rip"] => + "the instruction pointer cannot be used as an operand for inline asm", + #error = ["st", "st(0)", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"] => + "x87 registers are not currently supported as operands for inline asm", + #error = ["mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"] => + "MMX registers are not currently supported as operands for inline asm", + #error = ["k0"] => + "the k0 AVX mask register cannot be used as an operand for inline asm", + } +} + +impl X86InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + arch: InlineAsmArch, + modifier: Option, + ) -> fmt::Result { + let reg_default_modifier = match arch { + InlineAsmArch::X86 => 'e', + InlineAsmArch::X86_64 => 'r', + _ => unreachable!(), + }; + if self as u32 <= Self::dx as u32 { + let root = ['a', 'b', 'c', 'd'][self as usize - Self::ax as usize]; + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}l", root), + 'h' => write!(out, "{}h", root), + 'x' => write!(out, "{}x", root), + 'e' => write!(out, "e{}x", root), + 'r' => write!(out, "r{}x", root), + _ => unreachable!(), + } + } else if self as u32 <= Self::di as u32 { + let root = self.name(); + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}l", root), + 'x' => write!(out, "{}", root), + 'e' => write!(out, "e{}", root), + 'r' => write!(out, "r{}", root), + _ => unreachable!(), + } + } else if self as u32 <= Self::r15 as u32 { + let root = self.name(); + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}b", root), + 'x' => write!(out, "{}w", root), + 'e' => write!(out, "{}d", root), + 'r' => out.write_str(root), + _ => unreachable!(), + } + } else if self as u32 <= Self::r15b as u32 { + out.write_str(self.name()) + } else if self as u32 <= Self::xmm15 as u32 { + let prefix = modifier.unwrap_or('x'); + let index = self as u32 - Self::xmm0 as u32; + write!(out, "{}{}", prefix, index) + } else if self as u32 <= Self::ymm15 as u32 { + let prefix = modifier.unwrap_or('y'); + let index = self as u32 - Self::ymm0 as u32; + write!(out, "{}{}", prefix, index) + } else if self as u32 <= Self::zmm31 as u32 { + let prefix = modifier.unwrap_or('z'); + let index = self as u32 - Self::zmm0 as u32; + write!(out, "{}{}", prefix, index) + } else { + out.write_str(self.name()) + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(X86InlineAsmReg)) { + macro_rules! reg_conflicts { + ( + $( + $w:ident : $l:ident $h:ident + ),*; + $( + $w2:ident : $l2:ident + ),*; + $( + $x:ident : $y:ident : $z:ident + ),*; + ) => { + match self { + $( + Self::$w => { + cb(Self::$w); + cb(Self::$l); + cb(Self::$h); + } + Self::$l => { + cb(Self::$w); + cb(Self::$l); + } + Self::$h => { + cb(Self::$w); + cb(Self::$h); + } + )* + $( + Self::$w2 | Self::$l2 => { + cb(Self::$w2); + cb(Self::$l2); + } + )* + $( + Self::$x | Self::$y | Self::$z => { + cb(Self::$x); + cb(Self::$y); + cb(Self::$z); + } + )* + r => cb(r), + } + }; + } + + // XMM*, YMM* and ZMM* are all different views of the same register. + // + // See section 15.5 of the combined Intel® 64 and IA-32 Architectures + // Software Developer’s Manual for more details. + // + // We don't need to specify conflicts for [x,y,z]mm[16-31] since these + // registers are only available with AVX-512, so we just specify them + // as aliases directly. + reg_conflicts! { + ax : al ah, + bx : bl bh, + cx : cl ch, + dx : dl dh; + si : sil, + di : dil, + r8 : r8b, + r9 : r9b, + r10 : r10b, + r11 : r11b, + r12 : r12b, + r13 : r13b, + r14 : r14b, + r15 : r15b; + xmm0 : ymm0 : zmm0, + xmm1 : ymm1 : zmm1, + xmm2 : ymm2 : zmm2, + xmm3 : ymm3 : zmm3, + xmm4 : ymm4 : zmm4, + xmm5 : ymm5 : zmm5, + xmm6 : ymm6 : zmm6, + xmm7 : ymm7 : zmm7, + xmm8 : ymm8 : zmm8, + xmm9 : ymm9 : zmm9, + xmm10 : ymm10 : zmm10, + xmm11 : ymm11 : zmm11, + xmm12 : ymm12 : zmm12, + xmm13 : ymm13 : zmm13, + xmm14 : ymm14 : zmm14, + xmm15 : ymm15 : zmm15; + } + } +} diff --git a/src/librustc_target/lib.rs b/src/librustc_target/lib.rs index 98190867d493f..c2cdd2fd3ecf6 100644 --- a/src/librustc_target/lib.rs +++ b/src/librustc_target/lib.rs @@ -17,13 +17,18 @@ #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] +// FIXME(#56935): Work around ICEs during cross-compilation. +#[allow(unused)] +extern crate rustc_macros; + #[macro_use] extern crate log; pub mod abi; +pub mod asm; pub mod spec; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc. +/// instead of implementing everything in librustc_middle. pub trait HashStableContext {} diff --git a/src/librustc_target/spec/aarch64_apple_ios.rs b/src/librustc_target/spec/aarch64_apple_ios.rs index e896b46da9a62..eac2c3e6aa40c 100644 --- a/src/librustc_target/spec/aarch64_apple_ios.rs +++ b/src/librustc_target/spec/aarch64_apple_ios.rs @@ -19,6 +19,18 @@ pub fn target() -> TargetResult { eliminate_frame_pointer: false, max_atomic_width: Some(128), abi_blacklist: super::arm_base::abi_blacklist(), + forces_embed_bitcode: true, + // Taken from a clang build on Xcode 11.4.1. + // These arguments are not actually invoked - they just have + // to look right to pass App Store validation. + bitcode_llvm_cmdline: "-triple\0\ + arm64-apple-ios11.0.0\0\ + -emit-obj\0\ + -disable-llvm-passes\0\ + -target-abi\0\ + darwinpcs\0\ + -Os\0" + .to_string(), ..base }, }) diff --git a/src/librustc_target/spec/aarch64_apple_tvos.rs b/src/librustc_target/spec/aarch64_apple_tvos.rs index 794bc7900e747..f1cd14ffd11a6 100644 --- a/src/librustc_target/spec/aarch64_apple_tvos.rs +++ b/src/librustc_target/spec/aarch64_apple_tvos.rs @@ -19,6 +19,7 @@ pub fn target() -> TargetResult { eliminate_frame_pointer: false, max_atomic_width: Some(128), abi_blacklist: super::arm_base::abi_blacklist(), + forces_embed_bitcode: true, ..base }, }) diff --git a/src/librustc_target/spec/aarch64_pc_windows_msvc.rs b/src/librustc_target/spec/aarch64_pc_windows_msvc.rs index 7a46c7da7c333..8c03f1e8a7eab 100644 --- a/src/librustc_target/spec/aarch64_pc_windows_msvc.rs +++ b/src/librustc_target/spec/aarch64_pc_windows_msvc.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_msvc_base::opts(); @@ -6,9 +6,6 @@ pub fn target() -> TargetResult { base.has_elf_tls = true; base.features = "+neon,+fp-armv8".to_string(); - // FIXME: this shouldn't be panic=abort, it should be panic=unwind - base.panic_strategy = PanicStrategy::Abort; - Ok(Target { llvm_target: "aarch64-pc-windows-msvc".to_string(), target_endian: "little".to_string(), diff --git a/src/librustc_target/spec/aarch64_unknown_none.rs b/src/librustc_target/spec/aarch64_unknown_none.rs index 13e62b60ca8f5..7177c4e251e77 100644 --- a/src/librustc_target/spec/aarch64_unknown_none.rs +++ b/src/librustc_target/spec/aarch64_unknown_none.rs @@ -6,14 +6,14 @@ // // For example, `-C target-cpu=cortex-a53`. -use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; pub fn target() -> Result { let opts = TargetOptions { linker: Some("rust-lld".to_owned()), features: "+strict-align,+neon,+fp-armv8".to_string(), executables: true, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, disable_redzone: true, linker_is_gnu: true, max_atomic_width: Some(128), diff --git a/src/librustc_target/spec/aarch64_unknown_none_softfloat.rs b/src/librustc_target/spec/aarch64_unknown_none_softfloat.rs index cf46252555cd8..986300c677dfc 100644 --- a/src/librustc_target/spec/aarch64_unknown_none_softfloat.rs +++ b/src/librustc_target/spec/aarch64_unknown_none_softfloat.rs @@ -6,14 +6,14 @@ // // For example, `-C target-cpu=cortex-a53`. -use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; pub fn target() -> Result { let opts = TargetOptions { linker: Some("rust-lld".to_owned()), features: "+strict-align,-neon,-fp-armv8".to_string(), executables: true, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, disable_redzone: true, linker_is_gnu: true, max_atomic_width: Some(128), diff --git a/src/librustc_target/spec/aarch64_uwp_windows_msvc.rs b/src/librustc_target/spec/aarch64_uwp_windows_msvc.rs index a793860260c8d..6a8d148259ac3 100644 --- a/src/librustc_target/spec/aarch64_uwp_windows_msvc.rs +++ b/src/librustc_target/spec/aarch64_uwp_windows_msvc.rs @@ -1,13 +1,10 @@ -use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_uwp_msvc_base::opts(); base.max_atomic_width = Some(64); base.has_elf_tls = true; - // FIXME: this shouldn't be panic=abort, it should be panic=unwind - base.panic_strategy = PanicStrategy::Abort; - Ok(Target { llvm_target: "aarch64-pc-windows-msvc".to_string(), target_endian: "little".to_string(), diff --git a/src/librustc_target/spec/armebv7r_none_eabi.rs b/src/librustc_target/spec/armebv7r_none_eabi.rs index 3a5957892b59c..a1f68f6706a2a 100644 --- a/src/librustc_target/spec/armebv7r_none_eabi.rs +++ b/src/librustc_target/spec/armebv7r_none_eabi.rs @@ -1,7 +1,7 @@ // Targets the Big endian Cortex-R4/R5 processor (ARMv7-R) -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; -use std::default::Default; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -19,7 +19,7 @@ pub fn target() -> TargetResult { options: TargetOptions { executables: true, linker: Some("rust-lld".to_owned()), - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, max_atomic_width: Some(32), abi_blacklist: super::arm_base::abi_blacklist(), diff --git a/src/librustc_target/spec/armebv7r_none_eabihf.rs b/src/librustc_target/spec/armebv7r_none_eabihf.rs index 9f95a1a6f44fc..4d81c21f52a7b 100644 --- a/src/librustc_target/spec/armebv7r_none_eabihf.rs +++ b/src/librustc_target/spec/armebv7r_none_eabihf.rs @@ -1,7 +1,7 @@ // Targets the Cortex-R4F/R5F processor (ARMv7-R) -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; -use std::default::Default; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -19,7 +19,7 @@ pub fn target() -> TargetResult { options: TargetOptions { executables: true, linker: Some("rust-lld".to_owned()), - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, features: "+vfp3,-d32,-fp16".to_string(), max_atomic_width: Some(32), diff --git a/src/librustc_target/spec/armv7a_none_eabi.rs b/src/librustc_target/spec/armv7a_none_eabi.rs index 2fbef154f814c..09f1494e81cdb 100644 --- a/src/librustc_target/spec/armv7a_none_eabi.rs +++ b/src/librustc_target/spec/armv7a_none_eabi.rs @@ -17,14 +17,14 @@ // - `relocation-model` set to `static`; also no PIE, no relro and no dynamic // linking. rationale: matches `thumb` targets -use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; pub fn target() -> Result { let opts = TargetOptions { linker: Some("rust-lld".to_owned()), features: "+v7,+thumb2,+soft-float,-neon,+strict-align".to_string(), executables: true, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(64), panic_strategy: PanicStrategy::Abort, diff --git a/src/librustc_target/spec/armv7a_none_eabihf.rs b/src/librustc_target/spec/armv7a_none_eabihf.rs index f31e68c5bd12a..653ca76435bc5 100644 --- a/src/librustc_target/spec/armv7a_none_eabihf.rs +++ b/src/librustc_target/spec/armv7a_none_eabihf.rs @@ -5,14 +5,14 @@ // changes (list in `armv7a_none_eabi.rs`) to bring it closer to the bare-metal // `thumb` & `aarch64` targets. -use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; pub fn target() -> Result { let opts = TargetOptions { linker: Some("rust-lld".to_owned()), features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".to_string(), executables: true, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(64), panic_strategy: PanicStrategy::Abort, diff --git a/src/librustc_target/spec/armv7r_none_eabi.rs b/src/librustc_target/spec/armv7r_none_eabi.rs index 517368e6a23e9..29dfa17039736 100644 --- a/src/librustc_target/spec/armv7r_none_eabi.rs +++ b/src/librustc_target/spec/armv7r_none_eabi.rs @@ -1,7 +1,7 @@ // Targets the Little-endian Cortex-R4/R5 processor (ARMv7-R) -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; -use std::default::Default; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -19,7 +19,7 @@ pub fn target() -> TargetResult { options: TargetOptions { executables: true, linker: Some("rust-lld".to_owned()), - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, max_atomic_width: Some(32), abi_blacklist: super::arm_base::abi_blacklist(), diff --git a/src/librustc_target/spec/armv7r_none_eabihf.rs b/src/librustc_target/spec/armv7r_none_eabihf.rs index 6363469d92925..e6b0187c3313a 100644 --- a/src/librustc_target/spec/armv7r_none_eabihf.rs +++ b/src/librustc_target/spec/armv7r_none_eabihf.rs @@ -1,7 +1,7 @@ // Targets the Little-endian Cortex-R4F/R5F processor (ARMv7-R) -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; -use std::default::Default; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -19,7 +19,7 @@ pub fn target() -> TargetResult { options: TargetOptions { executables: true, linker: Some("rust-lld".to_owned()), - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, features: "+vfp3,-d32,-fp16".to_string(), max_atomic_width: Some(32), diff --git a/src/librustc_target/spec/cloudabi_base.rs b/src/librustc_target/spec/cloudabi_base.rs index 53af9dcc18610..3659c9ecdfca6 100644 --- a/src/librustc_target/spec/cloudabi_base.rs +++ b/src/librustc_target/spec/cloudabi_base.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { let mut args = LinkArgs::new(); @@ -29,7 +29,7 @@ pub fn opts() -> TargetOptions { // (Global Offset Table) to obtain the effective address of a // thread-local variable. Using a GOT is useful only when doing // dynamic linking. - tls_model: "local-exec".to_string(), + tls_model: TlsModel::LocalExec, relro_level: RelroLevel::Full, ..Default::default() } diff --git a/src/librustc_target/spec/crt_objects.rs b/src/librustc_target/spec/crt_objects.rs new file mode 100644 index 0000000000000..8991691a9a30c --- /dev/null +++ b/src/librustc_target/spec/crt_objects.rs @@ -0,0 +1,145 @@ +//! Object files providing support for basic runtime facilities and added to the produced binaries +//! at the start and at the end of linking. +//! +//! Table of CRT objects for popular toolchains. +//! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc. +//! See https://dev.gentoo.org/~vapier/crt.txt for some more details. +//! +//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |----------------------|------------------------|------------------------|------------------|-------------------|------| +//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 | +//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - | +//! +//! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |-----------------------|---------------|---------------|----------------|--------|------| +//! | dynamic-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - | +//! | dynamic-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - | +//! | static-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - | +//! | static-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - | +//! | dynamic-dylib | crtendS, crtn | crtendS, crtn | crtend_so | crtend | - | +//! | static-dylib (gcc) | crtend, crtn | crtendS, crtn | crtend_so | crtend | - | +//! | static-dylib (clang) | crtendS, crtn | N/A | crtend_so | crtend | - | +//! +//! Use cases for rustc linking the CRT objects explicitly: +//! - rustc needs to add its own Rust-specific objects (mingw is the example) +//! - gcc wrapper cannot be used for some reason and linker like ld or lld is used directly. +//! - gcc wrapper pulls wrong CRT objects (e.g. from glibc when we are targeting musl). +//! +//! In general it is preferable to rely on the target's native toolchain to pull the objects. +//! However, for some targets (musl, mingw) rustc historically provides a more self-contained +//! installation not requiring users to install the native target's toolchain. +//! In that case rustc distributes the objects as a part of the target's Rust toolchain +//! and falls back to linking with them manually. +//! Unlike native toolchains, rustc only currently adds the libc's objects during linking, +//! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710) +//! when linking in self-contained mode. + +use crate::spec::LinkOutputKind; +use rustc_serialize::json::{Json, ToJson}; +use std::collections::BTreeMap; +use std::str::FromStr; + +pub type CrtObjects = BTreeMap>; + +pub(super) fn new(obj_table: &[(LinkOutputKind, &[&str])]) -> CrtObjects { + obj_table.iter().map(|(z, k)| (*z, k.iter().map(|b| b.to_string()).collect())).collect() +} + +pub(super) fn all(obj: &str) -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &[obj]), + (LinkOutputKind::DynamicPicExe, &[obj]), + (LinkOutputKind::StaticNoPicExe, &[obj]), + (LinkOutputKind::StaticPicExe, &[obj]), + (LinkOutputKind::DynamicDylib, &[obj]), + (LinkOutputKind::StaticDylib, &[obj]), + ]) +} + +pub(super) fn pre_musl_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o"]), + (LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt1.o", "crti.o"]), + (LinkOutputKind::StaticPicExe, &["rcrt1.o", "crti.o"]), + (LinkOutputKind::DynamicDylib, &["crti.o"]), + (LinkOutputKind::StaticDylib, &["crti.o"]), + ]) +} + +pub(super) fn post_musl_fallback() -> CrtObjects { + all("crtn.o") +} + +pub(super) fn pre_mingw_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::DynamicDylib, &["dllcrt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticDylib, &["dllcrt2.o", "rsbegin.o"]), + ]) +} + +pub(super) fn post_mingw_fallback() -> CrtObjects { + all("rsend.o") +} + +pub(super) fn pre_mingw() -> CrtObjects { + all("rsbegin.o") +} + +pub(super) fn post_mingw() -> CrtObjects { + all("rsend.o") +} + +pub(super) fn pre_wasi_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt1.o"]), + (LinkOutputKind::DynamicPicExe, &["crt1.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt1.o"]), + (LinkOutputKind::StaticPicExe, &["crt1.o"]), + ]) +} + +pub(super) fn post_wasi_fallback() -> CrtObjects { + new(&[]) +} + +/// Which logic to use to determine whether to fall back to the "self-contained" mode or not. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CrtObjectsFallback { + Musl, + Mingw, + Wasm, +} + +impl FromStr for CrtObjectsFallback { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "musl" => CrtObjectsFallback::Musl, + "mingw" => CrtObjectsFallback::Mingw, + "wasm" => CrtObjectsFallback::Wasm, + _ => return Err(()), + }) + } +} + +impl ToJson for CrtObjectsFallback { + fn to_json(&self) -> Json { + match *self { + CrtObjectsFallback::Musl => "musl", + CrtObjectsFallback::Mingw => "mingw", + CrtObjectsFallback::Wasm => "wasm", + } + .to_json() + } +} diff --git a/src/librustc_target/spec/dragonfly_base.rs b/src/librustc_target/spec/dragonfly_base.rs index e26d0ae50b26d..c7062e1ca5196 100644 --- a/src/librustc_target/spec/dragonfly_base.rs +++ b/src/librustc_target/spec/dragonfly_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { let mut args = LinkArgs::new(); diff --git a/src/librustc_target/spec/freebsd_base.rs b/src/librustc_target/spec/freebsd_base.rs index fc252b6d43d26..d2a087ab62f9f 100644 --- a/src/librustc_target/spec/freebsd_base.rs +++ b/src/librustc_target/spec/freebsd_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { let mut args = LinkArgs::new(); diff --git a/src/librustc_target/spec/fuchsia_base.rs b/src/librustc_target/spec/fuchsia_base.rs index 046388e9be8f4..96b5328e1ee46 100644 --- a/src/librustc_target/spec/fuchsia_base.rs +++ b/src/librustc_target/spec/fuchsia_base.rs @@ -1,5 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; -use std::default::Default; +use crate::spec::{crt_objects, LinkArgs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -24,7 +23,12 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, has_rpath: false, pre_link_args, - pre_link_objects_exe: vec!["Scrt1.o".to_string()], + pre_link_objects: crt_objects::new(&[ + (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]), + (LinkOutputKind::DynamicPicExe, &["Scrt1.o"]), + (LinkOutputKind::StaticNoPicExe, &["Scrt1.o"]), + (LinkOutputKind::StaticPicExe, &["Scrt1.o"]), + ]), position_independent_executables: true, has_elf_tls: true, ..Default::default() diff --git a/src/librustc_target/spec/haiku_base.rs b/src/librustc_target/spec/haiku_base.rs index 1ddab7be180e0..3d7ae6c302d9c 100644 --- a/src/librustc_target/spec/haiku_base.rs +++ b/src/librustc_target/spec/haiku_base.rs @@ -1,5 +1,4 @@ use crate::spec::{RelroLevel, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { TargetOptions { diff --git a/src/librustc_target/spec/hermit_base.rs b/src/librustc_target/spec/hermit_base.rs index ee29d672b4625..18fb2aa3d5693 100644 --- a/src/librustc_target/spec/hermit_base.rs +++ b/src/librustc_target/spec/hermit_base.rs @@ -1,5 +1,5 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; -use std::default::Default; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy}; +use crate::spec::{RelocModel, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -14,12 +14,11 @@ pub fn opts() -> TargetOptions { has_elf_tls: true, linker_is_gnu: true, pre_link_args, - no_default_libraries: true, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, target_family: None, - tls_model: "initial-exec".to_string(), + tls_model: TlsModel::InitialExec, ..Default::default() } } diff --git a/src/librustc_target/spec/hermit_kernel_base.rs b/src/librustc_target/spec/hermit_kernel_base.rs index 44d3ee671811e..7f2dada714d8f 100644 --- a/src/librustc_target/spec/hermit_kernel_base.rs +++ b/src/librustc_target/spec/hermit_kernel_base.rs @@ -1,5 +1,5 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; -use std::default::Default; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy}; +use crate::spec::{RelocModel, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -15,12 +15,11 @@ pub fn opts() -> TargetOptions { has_elf_tls: true, linker_is_gnu: true, pre_link_args, - no_default_libraries: true, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, target_family: None, - tls_model: "initial-exec".to_string(), + tls_model: TlsModel::InitialExec, ..Default::default() } } diff --git a/src/librustc_target/spec/i686_apple_darwin.rs b/src/librustc_target/spec/i686_apple_darwin.rs index 033b87b110e12..b7a34f9740ac4 100644 --- a/src/librustc_target/spec/i686_apple_darwin.rs +++ b/src/librustc_target/spec/i686_apple_darwin.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { let llvm_target = super::apple_base::macos_llvm_target(&arch); Ok(Target { - llvm_target: llvm_target, + llvm_target, target_endian: "little".to_string(), target_pointer_width: "32".to_string(), target_c_int_width: "32".to_string(), diff --git a/src/librustc_target/spec/i686_pc_windows_gnu.rs b/src/librustc_target/spec/i686_pc_windows_gnu.rs index 2091902d7ce21..d12afe5a40bcc 100644 --- a/src/librustc_target/spec/i686_pc_windows_gnu.rs +++ b/src/librustc_target/spec/i686_pc_windows_gnu.rs @@ -1,7 +1,7 @@ use crate::spec::{LinkerFlavor, Target, TargetResult}; pub fn target() -> TargetResult { - let mut base = super::windows_base::opts(); + let mut base = super::windows_gnu_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.eliminate_frame_pointer = false; // Required for backtraces diff --git a/src/librustc_target/spec/i686_pc_windows_msvc.rs b/src/librustc_target/spec/i686_pc_windows_msvc.rs index ffb66afc76182..9d0922b8ce5a9 100644 --- a/src/librustc_target/spec/i686_pc_windows_msvc.rs +++ b/src/librustc_target/spec/i686_pc_windows_msvc.rs @@ -1,18 +1,24 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_msvc_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - // Mark all dynamic libraries and executables as compatible with the larger 4GiB address - // space available to x86 Windows binaries on x86_64. - base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().push("/LARGEADDRESSAWARE".to_string()); - - // Ensure the linker will only produce an image if it can also produce a table of - // the image's safe exception handlers. - // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers - base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().push("/SAFESEH".to_string()); + let pre_link_args_msvc = vec![ + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + "/LARGEADDRESSAWARE".to_string(), + // Ensure the linker will only produce an image if it can also produce a table of + // the image's safe exception handlers. + // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers + "/SAFESEH".to_string(), + ]; + base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + base.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .extend(pre_link_args_msvc); Ok(Target { llvm_target: "i686-pc-windows-msvc".to_string(), diff --git a/src/librustc_target/spec/i686_unknown_uefi.rs b/src/librustc_target/spec/i686_unknown_uefi.rs index e299f92fdeb63..221d5f0785cd2 100644 --- a/src/librustc_target/spec/i686_unknown_uefi.rs +++ b/src/librustc_target/spec/i686_unknown_uefi.rs @@ -8,7 +8,7 @@ use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; pub fn target() -> TargetResult { - let mut base = super::uefi_base::opts(); + let mut base = super::uefi_msvc_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); @@ -23,11 +23,6 @@ pub fn target() -> TargetResult { // arguments, thus giving you access to full MMX/SSE acceleration. base.features = "-mmx,-sse,+soft-float".to_string(); - // UEFI mirrors the calling-conventions used on windows. In case of i686 this means small - // structs will be returned as int. This shouldn't matter much, since the restrictions placed - // by the UEFI specifications forbid any ABI to return structures. - base.abi_return_struct_as_int = true; - // Use -GNU here, because of the reason below: // Background and Problem: // If we use i686-unknown-windows, the LLVM IA32 MSVC generates compiler intrinsic diff --git a/src/librustc_target/spec/i686_uwp_windows_gnu.rs b/src/librustc_target/spec/i686_uwp_windows_gnu.rs index 93f396de0a051..4e582fb8c63ab 100644 --- a/src/librustc_target/spec/i686_uwp_windows_gnu.rs +++ b/src/librustc_target/spec/i686_uwp_windows_gnu.rs @@ -1,7 +1,7 @@ use crate::spec::{LinkerFlavor, Target, TargetResult}; pub fn target() -> TargetResult { - let mut base = super::windows_uwp_base::opts(); + let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.eliminate_frame_pointer = false; // Required for backtraces diff --git a/src/librustc_target/spec/illumos_base.rs b/src/librustc_target/spec/illumos_base.rs new file mode 100644 index 0000000000000..35ac346fb3f6f --- /dev/null +++ b/src/librustc_target/spec/illumos_base.rs @@ -0,0 +1,48 @@ +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; +use std::default::Default; + +pub fn opts() -> TargetOptions { + let mut late_link_args = LinkArgs::new(); + late_link_args.insert( + LinkerFlavor::Gcc, + vec![ + // LLVM will insert calls to the stack protector functions + // "__stack_chk_fail" and "__stack_chk_guard" into code in native + // object files. Some platforms include these symbols directly in + // libc, but at least historically these have been provided in + // libssp.so on illumos and Solaris systems. + "-lssp".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + has_rpath: true, + target_family: Some("unix".to_string()), + is_like_solaris: true, + limit_rdylib_exports: false, // Linker doesn't support this + eliminate_frame_pointer: false, + late_link_args, + + // While we support ELF TLS, rust requires a way to register + // cleanup handlers (in C, this would be something along the lines of: + // void register_callback(void (*fn)(void *), void *arg); + // (see src/libstd/sys/unix/fast_thread_local.rs) that is currently + // missing in illumos. For now at least, we must fallback to using + // pthread_{get,set}specific. + //has_elf_tls: true, + + // FIXME: Currently, rust is invoking cc to link, which ends up + // causing these to get included twice. We should eventually transition + // to having rustc invoke ld directly, in which case these will need to + // be uncommented. + // + // We want XPG6 behavior from libc and libm. See standards(5) + //pre_link_objects_exe: vec![ + // "/usr/lib/amd64/values-Xc.o".to_string(), + // "/usr/lib/amd64/values-xpg6.o".to_string(), + //], + ..Default::default() + } +} diff --git a/src/librustc_target/spec/l4re_base.rs b/src/librustc_target/spec/l4re_base.rs index b712dcae89970..5caad10161d8e 100644 --- a/src/librustc_target/spec/l4re_base.rs +++ b/src/librustc_target/spec/l4re_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, TargetOptions}; -use std::default::Default; //use std::process::Command; // Use GCC to locate code for crt* libraries from the host, not from L4Re. Note diff --git a/src/librustc_target/spec/linux_base.rs b/src/librustc_target/spec/linux_base.rs index a5d7f8e07c443..52892fc35924e 100644 --- a/src/librustc_target/spec/linux_base.rs +++ b/src/librustc_target/spec/linux_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { let mut args = LinkArgs::new(); diff --git a/src/librustc_target/spec/linux_kernel_base.rs b/src/librustc_target/spec/linux_kernel_base.rs index fae44836fa821..201d6a0fff93b 100644 --- a/src/librustc_target/spec/linux_kernel_base.rs +++ b/src/librustc_target/spec/linux_kernel_base.rs @@ -1,5 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelroLevel, TargetOptions}; -use std::default::Default; +use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -17,7 +16,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, needs_plt: true, relro_level: RelroLevel::Full, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, target_family: Some("unix".to_string()), pre_link_args, diff --git a/src/librustc_target/spec/linux_musl_base.rs b/src/librustc_target/spec/linux_musl_base.rs index e294e63982de4..0fdd876080677 100644 --- a/src/librustc_target/spec/linux_musl_base.rs +++ b/src/librustc_target/spec/linux_musl_base.rs @@ -1,29 +1,18 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; use crate::spec::{LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); - // Make sure that the linker/gcc really don't pull in anything, including - // default objects, libs, etc. - base.pre_link_args_crt.insert(LinkerFlavor::Gcc, Vec::new()); - base.pre_link_args_crt.get_mut(&LinkerFlavor::Gcc).unwrap().push("-nostdlib".to_string()); - // At least when this was tested, the linker would not add the // `GNU_EH_FRAME` program header to executables generated, which is required // when unwinding to locate the unwinding information. I'm not sure why this // argument is *not* necessary for normal builds, but it can't hurt! base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,--eh-frame-hdr".to_string()); - // When generating a statically linked executable there's generally some - // small setup needed which is listed in these files. These are provided by - // a musl toolchain and are linked by default by the `musl-gcc` script. Note - // that `gcc` also does this by default, it just uses some different files. - // - // Each target directory for musl has these object files included in it so - // they'll be included from there. - base.pre_link_objects_exe_crt.push("crt1.o".to_string()); - base.pre_link_objects_exe_crt.push("crti.o".to_string()); - base.post_link_objects_crt.push("crtn.o".to_string()); + base.pre_link_objects_fallback = crt_objects::pre_musl_fallback(); + base.post_link_objects_fallback = crt_objects::post_musl_fallback(); + base.crt_objects_fallback = Some(CrtObjectsFallback::Musl); // These targets statically link libc by default base.crt_static_default = true; diff --git a/src/librustc_target/spec/mipsel_sony_psp.rs b/src/librustc_target/spec/mipsel_sony_psp.rs new file mode 100644 index 0000000000000..0c74454d0c5fe --- /dev/null +++ b/src/librustc_target/spec/mipsel_sony_psp.rs @@ -0,0 +1,43 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +// The PSP has custom linker requirements. +const LINKER_SCRIPT: &str = include_str!("./mipsel_sony_psp_linker_script.ld"); + +pub fn target() -> TargetResult { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Lld(LldFlavor::Ld), + vec!["--eh-frame-hdr".to_string(), "--emit-relocs".to_string()], + ); + + Ok(Target { + llvm_target: "mipsel-sony-psp".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "psp".to_string(), + target_env: "".to_string(), + target_vendor: "sony".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + cpu: "mips2".to_string(), + executables: true, + linker: Some("rust-lld".to_owned()), + linker_is_gnu: true, + relocation_model: RelocModel::Static, + + // PSP FPU only supports single precision floats. + features: "+single-float".to_string(), + + // PSP does not support trap-on-condition instructions. + llvm_args: vec!["-mno-check-zero-division".to_string()], + pre_link_args, + link_script: Some(LINKER_SCRIPT.to_string()), + ..Default::default() + }, + }) +} diff --git a/src/librustc_target/spec/mipsel_sony_psp_linker_script.ld b/src/librustc_target/spec/mipsel_sony_psp_linker_script.ld new file mode 100644 index 0000000000000..1bd436d6f94cc --- /dev/null +++ b/src/librustc_target/spec/mipsel_sony_psp_linker_script.ld @@ -0,0 +1,34 @@ +ENTRY(module_start) +SECTIONS +{ + /* PRX format requires text to begin at 0 */ + .text 0 : { *(.text .text.*) } + + /* Sort stubs for convenient ordering */ + .sceStub.text : { *(.sceStub.text) *(SORT(.sceStub.text.*)) } + + /* Keep these sections around, even though they may appear unused to the linker */ + .lib.ent.top : { KEEP(*(.lib.ent.top)) } + .lib.ent : { KEEP(*(.lib.ent)) } + .lib.ent.btm : { KEEP(*(.lib.ent.btm)) } + .lib.stub.top : { KEEP(*(.lib.stub.top)) } + .lib.stub : { KEEP(*(.lib.stub)) } + .lib.stub.btm : { KEEP(*(.lib.stub.btm)) } + .eh_frame_hdr : { KEEP(*(.eh_frame_hdr)) } + + /* Add symbols for LLVM's libunwind */ + __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; + __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; + .eh_frame : + { + __eh_frame_start = .; + KEEP(*(.eh_frame)) + __eh_frame_end = .; + } + + /* These are explicitly listed to avoid being merged into .rodata */ + .rodata.sceResident : { *(.rodata.sceResident) } + .rodata.sceModuleInfo : { *(.rodata.sceModuleInfo) } + /* Sort NIDs for convenient ordering */ + .rodata.sceNid : { *(.rodata.sceNid) *(SORT(.rodata.sceNid.*)) } +} diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index 6e5111bd7018a..8770e033e0596 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -35,9 +35,9 @@ //! to the list specified by the target, rather than replace. use crate::spec::abi::{lookup as lookup_abi, Abi}; +use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_serialize::json::{Json, ToJson}; use std::collections::BTreeMap; -use std::default::Default; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; @@ -45,6 +45,8 @@ use std::{fmt, io}; use rustc_macros::HashStable_Generic; pub mod abi; +pub mod crt_objects; + mod android_base; mod apple_base; mod apple_sdk_base; @@ -56,22 +58,24 @@ mod fuchsia_base; mod haiku_base; mod hermit_base; mod hermit_kernel_base; +mod illumos_base; mod l4re_base; mod linux_base; mod linux_kernel_base; mod linux_musl_base; +mod msvc_base; mod netbsd_base; mod openbsd_base; mod redox_base; mod riscv_base; mod solaris_base; mod thumb_base; -mod uefi_base; +mod uefi_msvc_base; mod vxworks_base; mod wasm32_base; -mod windows_base; +mod windows_gnu_base; mod windows_msvc_base; -mod windows_uwp_base; +mod windows_uwp_gnu_base; mod windows_uwp_msvc_base; #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -264,6 +268,167 @@ impl ToJson for MergeFunctions { } } +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum RelocModel { + Static, + Pic, + DynamicNoPic, + Ropi, + Rwpi, + RopiRwpi, +} + +impl FromStr for RelocModel { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "static" => RelocModel::Static, + "pic" => RelocModel::Pic, + "dynamic-no-pic" => RelocModel::DynamicNoPic, + "ropi" => RelocModel::Ropi, + "rwpi" => RelocModel::Rwpi, + "ropi-rwpi" => RelocModel::RopiRwpi, + _ => return Err(()), + }) + } +} + +impl ToJson for RelocModel { + fn to_json(&self) -> Json { + match *self { + RelocModel::Static => "static", + RelocModel::Pic => "pic", + RelocModel::DynamicNoPic => "dynamic-no-pic", + RelocModel::Ropi => "ropi", + RelocModel::Rwpi => "rwpi", + RelocModel::RopiRwpi => "ropi-rwpi", + } + .to_json() + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CodeModel { + Tiny, + Small, + Kernel, + Medium, + Large, +} + +impl FromStr for CodeModel { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "tiny" => CodeModel::Tiny, + "small" => CodeModel::Small, + "kernel" => CodeModel::Kernel, + "medium" => CodeModel::Medium, + "large" => CodeModel::Large, + _ => return Err(()), + }) + } +} + +impl ToJson for CodeModel { + fn to_json(&self) -> Json { + match *self { + CodeModel::Tiny => "tiny", + CodeModel::Small => "small", + CodeModel::Kernel => "kernel", + CodeModel::Medium => "medium", + CodeModel::Large => "large", + } + .to_json() + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum TlsModel { + GeneralDynamic, + LocalDynamic, + InitialExec, + LocalExec, +} + +impl FromStr for TlsModel { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + // Note the difference "general" vs "global" difference. The model name is "general", + // but the user-facing option name is "global" for consistency with other compilers. + "global-dynamic" => TlsModel::GeneralDynamic, + "local-dynamic" => TlsModel::LocalDynamic, + "initial-exec" => TlsModel::InitialExec, + "local-exec" => TlsModel::LocalExec, + _ => return Err(()), + }) + } +} + +impl ToJson for TlsModel { + fn to_json(&self) -> Json { + match *self { + TlsModel::GeneralDynamic => "global-dynamic", + TlsModel::LocalDynamic => "local-dynamic", + TlsModel::InitialExec => "initial-exec", + TlsModel::LocalExec => "local-exec", + } + .to_json() + } +} + +/// Everything is flattened to a single enum to make the json encoding/decoding less annoying. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub enum LinkOutputKind { + /// Dynamically linked non position-independent executable. + DynamicNoPicExe, + /// Dynamically linked position-independent executable. + DynamicPicExe, + /// Statically linked non position-independent executable. + StaticNoPicExe, + /// Statically linked position-independent executable. + StaticPicExe, + /// Regular dynamic library ("dynamically linked"). + DynamicDylib, + /// Dynamic library with bundled libc ("statically linked"). + StaticDylib, +} + +impl LinkOutputKind { + fn as_str(&self) -> &'static str { + match self { + LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe", + LinkOutputKind::DynamicPicExe => "dynamic-pic-exe", + LinkOutputKind::StaticNoPicExe => "static-nopic-exe", + LinkOutputKind::StaticPicExe => "static-pic-exe", + LinkOutputKind::DynamicDylib => "dynamic-dylib", + LinkOutputKind::StaticDylib => "static-dylib", + } + } + + pub(super) fn from_str(s: &str) -> Option { + Some(match s { + "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, + "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, + "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, + "static-pic-exe" => LinkOutputKind::StaticPicExe, + "dynamic-dylib" => LinkOutputKind::DynamicDylib, + "static-dylib" => LinkOutputKind::StaticDylib, + _ => return None, + }) + } +} + +impl fmt::Display for LinkOutputKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + pub enum LoadTargetError { BuiltinTargetNotFound(String), Other(String), @@ -309,23 +474,14 @@ macro_rules! supported_targets { } #[cfg(test)] - mod test_json_encode_decode { - use rustc_serialize::json::ToJson; - use super::Target; - $(use super::$module;)+ + mod tests { + mod tests_impl; + // Cannot put this into a separate file without duplication, make an exception. $( - #[test] // `#[test]` - this is hard to put into a separate file, make an exception + #[test] // `#[test]` fn $module() { - // Grab the TargetResult struct. If we successfully retrieved - // a Target, then the test JSON encoding/decoding can run for this - // Target on this testing platform (i.e., checking the iOS targets - // only on a Mac test platform). - let _ = $module::target().map(|original| { - let as_json = original.to_json(); - let parsed = Target::from_json(as_json).unwrap(); - assert_eq!(original, parsed); - }); + tests_impl::test_target(super::$module::target()); } )+ } @@ -447,6 +603,8 @@ supported_targets! { ("x86_64-sun-solaris", "x86_64-pc-solaris", x86_64_sun_solaris), ("sparcv9-sun-solaris", sparcv9_sun_solaris), + ("x86_64-unknown-illumos", x86_64_unknown_illumos), + ("x86_64-pc-windows-gnu", x86_64_pc_windows_gnu), ("i686-pc-windows-gnu", i686_pc_windows_gnu), ("i686-uwp-windows-gnu", i686_uwp_windows_gnu), @@ -512,6 +670,8 @@ supported_targets! { ("powerpc-wrs-vxworks", powerpc_wrs_vxworks), ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe), ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks), + + ("mipsel-sony-psp", mipsel_sony_psp), } /// Everything `rustc` knows about how to compile for a specific target. @@ -538,7 +698,8 @@ pub struct Target { pub arch: String, /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. pub data_layout: String, - /// Linker flavor + /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed + /// on the command line. pub linker_flavor: LinkerFlavor, /// Optional settings with defaults. pub options: TargetOptions, @@ -566,19 +727,26 @@ pub struct TargetOptions { /// Linker to invoke pub linker: Option, - /// LLD flavor + /// LLD flavor used if `lld` (or `rust-lld`) is specified as a linker + /// without clarifying its flavor in any way. pub lld_flavor: LldFlavor, /// Linker arguments that are passed *before* any user-defined libraries. pub pre_link_args: LinkArgs, // ... unconditionally pub pre_link_args_crt: LinkArgs, // ... when linking with a bundled crt - /// Objects to link before all others, always found within the - /// sysroot folder. - pub pre_link_objects_exe: Vec, // ... when linking an executable, unconditionally - pub pre_link_objects_exe_crt: Vec, // ... when linking an executable with a bundled crt - pub pre_link_objects_dll: Vec, // ... when linking a dylib + /// Objects to link before and after all other object code. + pub pre_link_objects: CrtObjects, + pub post_link_objects: CrtObjects, + /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the + /// target's native gcc and fall back to the "self-contained" mode and pull them manually. + /// See `crt_objects.rs` for some more detailed documentation. + pub pre_link_objects_fallback: CrtObjects, + pub post_link_objects_fallback: CrtObjects, + /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. + pub crt_objects_fallback: Option, + /// Linker arguments that are unconditionally passed after any - /// user-defined but before post_link_objects. Standard platform + /// user-defined but before post-link objects. Standard platform /// libraries that should be always be linked to, usually go here. pub late_link_args: LinkArgs, /// Linker arguments used in addition to `late_link_args` if at least one @@ -587,13 +755,13 @@ pub struct TargetOptions { /// Linker arguments used in addition to `late_link_args` if aall Rust /// dependencies are statically linked. pub late_link_args_static: LinkArgs, - /// Objects to link after all others, always found within the - /// sysroot folder. - pub post_link_objects: Vec, // ... unconditionally - pub post_link_objects_crt: Vec, // ... when linking with a bundled crt /// Linker arguments that are unconditionally passed *after* any /// user-defined libraries. pub post_link_args: LinkArgs, + /// Optional link script applied to `dylib` and `executable` crate types. + /// This is a string containing the script, not a path. Can only be applied + /// to linkers where `linker_is_gnu` is true. + pub link_script: Option, /// Environment variables to be set for the linker invocation. pub link_env: Vec<(String, String)>, @@ -618,13 +786,14 @@ pub struct TargetOptions { /// libraries. Defaults to false. pub executables: bool, /// Relocation model to use in object file. Corresponds to `llc - /// -relocation-model=$relocation_model`. Defaults to "pic". - pub relocation_model: String, + /// -relocation-model=$relocation_model`. Defaults to `Pic`. + pub relocation_model: RelocModel, /// Code model to use. Corresponds to `llc -code-model=$code_model`. - pub code_model: Option, + /// Defaults to `None` which means "inherited from the base LLVM target". + pub code_model: Option, /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec" /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang. - pub tls_model: String, + pub tls_model: TlsModel, /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false. pub disable_redzone: bool, /// Eliminate frame pointers from stack frames if possible. Defaults to true. @@ -711,11 +880,10 @@ pub struct TargetOptions { // If we give emcc .o files that are actually .bc files it // will 'just work'. pub obj_is_bitcode: bool, - - // LLVM can't produce object files for this target. Instead, we'll make LLVM - // emit assembly and then use `gcc` to turn that assembly into an object - // file - pub no_integrated_as: bool, + /// Whether the target requires that emitted object code includes bitcode. + pub forces_embed_bitcode: bool, + /// Content of the LLVM cmdline section associated with embedded bitcode. + pub bitcode_llvm_cmdline: String, /// Don't use this field; instead use the `.min_atomic_width()` method. pub min_atomic_width: Option, @@ -771,9 +939,6 @@ pub struct TargetOptions { /// rather than "default" pub default_hidden_visibility: bool, - /// Whether or not bitcode is embedded in object files - pub embed_bitcode: bool, - /// Whether a .debug_gdb_scripts section will be added to the output object file pub emit_debug_gdb_scripts: bool, @@ -814,6 +979,10 @@ pub struct TargetOptions { /// Additional arguments to pass to LLVM, similar to the `-C llvm-args` codegen option. pub llvm_args: Vec, + + /// Whether to use legacy .ctors initialization hooks rather than .init_array. Defaults + /// to false (uses .init_array). + pub use_ctors_section: bool, } impl Default for TargetOptions { @@ -827,15 +996,16 @@ impl Default for TargetOptions { pre_link_args: LinkArgs::new(), pre_link_args_crt: LinkArgs::new(), post_link_args: LinkArgs::new(), + link_script: None, asm_args: Vec::new(), cpu: "generic".to_string(), features: String::new(), dynamic_linking: false, only_cdylib: false, executables: false, - relocation_model: "pic".to_string(), + relocation_model: RelocModel::Pic, code_model: None, - tls_model: "global-dynamic".to_string(), + tls_model: TlsModel::GeneralDynamic, disable_redzone: false, eliminate_frame_pointer: true, function_sections: true, @@ -860,11 +1030,11 @@ impl Default for TargetOptions { position_independent_executables: false, needs_plt: false, relro_level: RelroLevel::None, - pre_link_objects_exe: Vec::new(), - pre_link_objects_exe_crt: Vec::new(), - pre_link_objects_dll: Vec::new(), - post_link_objects: Vec::new(), - post_link_objects_crt: Vec::new(), + pre_link_objects: Default::default(), + post_link_objects: Default::default(), + pre_link_objects_fallback: Default::default(), + post_link_objects_fallback: Default::default(), + crt_objects_fallback: None, late_link_args: LinkArgs::new(), late_link_args_dynamic: LinkArgs::new(), late_link_args_static: LinkArgs::new(), @@ -875,7 +1045,8 @@ impl Default for TargetOptions { allow_asm: true, has_elf_tls: false, obj_is_bitcode: false, - no_integrated_as: false, + forces_embed_bitcode: false, + bitcode_llvm_cmdline: String::new(), min_atomic_width: None, max_atomic_width: None, atomic_cas: true, @@ -893,7 +1064,6 @@ impl Default for TargetOptions { no_builtins: false, codegen_backend: "llvm".to_string(), default_hidden_visibility: false, - embed_bitcode: false, emit_debug_gdb_scripts: true, requires_uwtable: false, simd_types_indirect: true, @@ -904,6 +1074,7 @@ impl Default for TargetOptions { llvm_abiname: "".to_string(), relax_elf_relocations: false, llvm_args: vec![], + use_ctors_section: false, } } } @@ -993,20 +1164,21 @@ impl Target { macro_rules! key { ($key_name:ident) => ( { let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).map(|o| o.as_string() - .map(|s| base.options.$key_name = s.to_string())); + if let Some(s) = obj.find(&name).and_then(Json::as_string) { + base.options.$key_name = s.to_string(); + } } ); ($key_name:ident, bool) => ( { let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]) - .map(|o| o.as_boolean() - .map(|s| base.options.$key_name = s)); + if let Some(s) = obj.find(&name).and_then(Json::as_boolean) { + base.options.$key_name = s; + } } ); ($key_name:ident, Option) => ( { let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]) - .map(|o| o.as_u64() - .map(|s| base.options.$key_name = Some(s))); + if let Some(s) = obj.find(&name).and_then(Json::as_u64) { + base.options.$key_name = Some(s); + } } ); ($key_name:ident, MergeFunctions) => ( { let name = (stringify!($key_name)).replace("_", "-"); @@ -1021,6 +1193,42 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, RelocModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(relocation_model) => base.options.$key_name = relocation_model, + _ => return Some(Err(format!("'{}' is not a valid relocation model. \ + Run `rustc --print relocation-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, CodeModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(code_model) => base.options.$key_name = Some(code_model), + _ => return Some(Err(format!("'{}' is not a valid code model. \ + Run `rustc --print code-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, TlsModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(tls_model) => base.options.$key_name = tls_model, + _ => return Some(Err(format!("'{}' is not a valid TLS model. \ + Run `rustc --print tls-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, PanicStrategy) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { @@ -1048,19 +1256,19 @@ impl Target { } ); ($key_name:ident, list) => ( { let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).map(|o| o.as_array() - .map(|v| base.options.$key_name = v.iter() - .map(|a| a.as_string().unwrap().to_string()).collect() - ) - ); + if let Some(v) = obj.find(&name).and_then(Json::as_array) { + base.options.$key_name = v.iter() + .map(|a| a.as_string().unwrap().to_string()) + .collect(); + } } ); ($key_name:ident, opt_list) => ( { let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).map(|o| o.as_array() - .map(|v| base.options.$key_name = Some(v.iter() - .map(|a| a.as_string().unwrap().to_string()).collect()) - ) - ); + if let Some(v) = obj.find(&name).and_then(Json::as_array) { + base.options.$key_name = Some(v.iter() + .map(|a| a.as_string().unwrap().to_string()) + .collect()); + } } ); ($key_name:ident, optional) => ( { let name = (stringify!($key_name)).replace("_", "-"); @@ -1093,6 +1301,45 @@ impl Target { }) })).unwrap_or(Ok(())) } ); + ($key_name:ident, crt_objects_fallback) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(fallback) => base.options.$key_name = Some(fallback), + _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ + Use 'musl', 'mingw' or 'wasm'", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, link_objects) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(val) = obj.find(&name[..]) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per CRT object kind.", name))?; + let mut args = CrtObjects::new(); + for (k, v) in obj { + let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for CRT object kind. \ + Use '(dynamic,static)-(nopic,pic)-exe' or \ + '(dynamic,static)-dylib'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_string().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_owned()) + }) + .collect::, String>>()?; + + args.insert(kind, v); + } + base.options.$key_name = args; + } + } ); ($key_name:ident, link_args) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(val) = obj.find(&name[..]) { @@ -1140,17 +1387,18 @@ impl Target { key!(is_builtin, bool); key!(linker, optional); key!(lld_flavor, LldFlavor)?; + key!(pre_link_objects, link_objects); + key!(post_link_objects, link_objects); + key!(pre_link_objects_fallback, link_objects); + key!(post_link_objects_fallback, link_objects); + key!(crt_objects_fallback, crt_objects_fallback)?; key!(pre_link_args, link_args); key!(pre_link_args_crt, link_args); - key!(pre_link_objects_exe, list); - key!(pre_link_objects_exe_crt, list); - key!(pre_link_objects_dll, list); key!(late_link_args, link_args); key!(late_link_args_dynamic, link_args); key!(late_link_args_static, link_args); - key!(post_link_objects, list); - key!(post_link_objects_crt, list); key!(post_link_args, link_args); + key!(link_script, optional); key!(link_env, env); key!(link_env_remove, list); key!(asm_args, list); @@ -1159,9 +1407,9 @@ impl Target { key!(dynamic_linking, bool); key!(only_cdylib, bool); key!(executables, bool); - key!(relocation_model); - key!(code_model, optional); - key!(tls_model); + key!(relocation_model, RelocModel)?; + key!(code_model, CodeModel)?; + key!(tls_model, TlsModel)?; key!(disable_redzone, bool); key!(eliminate_frame_pointer, bool); key!(function_sections, bool); @@ -1191,7 +1439,8 @@ impl Target { key!(main_needs_argc_argv, bool); key!(has_elf_tls, bool); key!(obj_is_bitcode, bool); - key!(no_integrated_as, bool); + key!(forces_embed_bitcode, bool); + key!(bitcode_llvm_cmdline); key!(max_atomic_width, Option); key!(min_atomic_width, Option); key!(atomic_cas, bool); @@ -1208,7 +1457,6 @@ impl Target { key!(no_builtins, bool); key!(codegen_backend); key!(default_hidden_visibility, bool); - key!(embed_bitcode, bool); key!(emit_debug_gdb_scripts, bool); key!(requires_uwtable, bool); key!(simd_types_indirect, bool); @@ -1219,6 +1467,7 @@ impl Target { key!(llvm_abiname); key!(relax_elf_relocations, bool); key!(llvm_args, list); + key!(use_ctors_section, bool); if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) { for name in array.iter().filter_map(|abi| abi.as_string()) { @@ -1369,17 +1618,18 @@ impl ToJson for Target { target_option_val!(is_builtin); target_option_val!(linker); target_option_val!(lld_flavor); + target_option_val!(pre_link_objects); + target_option_val!(post_link_objects); + target_option_val!(pre_link_objects_fallback); + target_option_val!(post_link_objects_fallback); + target_option_val!(crt_objects_fallback); target_option_val!(link_args - pre_link_args); target_option_val!(link_args - pre_link_args_crt); - target_option_val!(pre_link_objects_exe); - target_option_val!(pre_link_objects_exe_crt); - target_option_val!(pre_link_objects_dll); target_option_val!(link_args - late_link_args); target_option_val!(link_args - late_link_args_dynamic); target_option_val!(link_args - late_link_args_static); - target_option_val!(post_link_objects); - target_option_val!(post_link_objects_crt); target_option_val!(link_args - post_link_args); + target_option_val!(link_script); target_option_val!(env - link_env); target_option_val!(link_env_remove); target_option_val!(asm_args); @@ -1420,7 +1670,8 @@ impl ToJson for Target { target_option_val!(main_needs_argc_argv); target_option_val!(has_elf_tls); target_option_val!(obj_is_bitcode); - target_option_val!(no_integrated_as); + target_option_val!(forces_embed_bitcode); + target_option_val!(bitcode_llvm_cmdline); target_option_val!(min_atomic_width); target_option_val!(max_atomic_width); target_option_val!(atomic_cas); @@ -1437,7 +1688,6 @@ impl ToJson for Target { target_option_val!(no_builtins); target_option_val!(codegen_backend); target_option_val!(default_hidden_visibility); - target_option_val!(embed_bitcode); target_option_val!(emit_debug_gdb_scripts); target_option_val!(requires_uwtable); target_option_val!(simd_types_indirect); @@ -1448,6 +1698,7 @@ impl ToJson for Target { target_option_val!(llvm_abiname); target_option_val!(relax_elf_relocations); target_option_val!(llvm_args); + target_option_val!(use_ctors_section); if default.abi_blacklist != self.options.abi_blacklist { d.insert( diff --git a/src/librustc_target/spec/msp430_none_elf.rs b/src/librustc_target/spec/msp430_none_elf.rs index e05a18e76d21a..c6d0308f8f82f 100644 --- a/src/librustc_target/spec/msp430_none_elf.rs +++ b/src/librustc_target/spec/msp430_none_elf.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -22,7 +22,6 @@ pub fn target() -> TargetResult { // dependency on this specific gcc. asm_args: vec!["-mcpu=msp430".to_string()], linker: Some("msp430-elf-gcc".to_string()), - no_integrated_as: true, // There are no atomic CAS instructions available in the MSP430 // instruction set, and the LLVM backend doesn't currently support @@ -41,7 +40,7 @@ pub fn target() -> TargetResult { // Similarly, one almost always never wants to use relocatable // code because of the extra costs it involves. - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, // Right now we invoke an external assembler and this isn't // compatible with multiple codegen units, and plus we probably diff --git a/src/librustc_target/spec/msvc_base.rs b/src/librustc_target/spec/msvc_base.rs new file mode 100644 index 0000000000000..f57ef87cf1294 --- /dev/null +++ b/src/librustc_target/spec/msvc_base.rs @@ -0,0 +1,31 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let pre_link_args_msvc = vec![ + // Suppress the verbose logo and authorship debugging output, which would needlessly + // clog any log files. + "/NOLOGO".to_string(), + // Tell the compiler that non-code sections can be marked as non-executable, + // including stack pages. + // UEFI is fully compatible to non-executable data pages. + // In fact, firmware might enforce this, so we better let the linker know about this, + // so it will fail if the compiler ever tries placing code on the stack + // (e.g., trampoline constructs and alike). + "/NXCOMPAT".to_string(), + ]; + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert(LinkerFlavor::Msvc, pre_link_args_msvc.clone()); + pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), pre_link_args_msvc); + + TargetOptions { + executables: true, + is_like_windows: true, + is_like_msvc: true, + lld_flavor: LldFlavor::Link, + pre_link_args, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + + ..Default::default() + } +} diff --git a/src/librustc_target/spec/netbsd_base.rs b/src/librustc_target/spec/netbsd_base.rs index eb359b920463b..988346af2d72c 100644 --- a/src/librustc_target/spec/netbsd_base.rs +++ b/src/librustc_target/spec/netbsd_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { let mut args = LinkArgs::new(); @@ -24,6 +23,7 @@ pub fn opts() -> TargetOptions { pre_link_args: args, position_independent_executables: true, relro_level: RelroLevel::Full, + use_ctors_section: true, ..Default::default() } } diff --git a/src/librustc_target/spec/openbsd_base.rs b/src/librustc_target/spec/openbsd_base.rs index b66c56e1a7aea..cadd14df69352 100644 --- a/src/librustc_target/spec/openbsd_base.rs +++ b/src/librustc_target/spec/openbsd_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { let mut args = LinkArgs::new(); diff --git a/src/librustc_target/spec/redox_base.rs b/src/librustc_target/spec/redox_base.rs index 6398fa91f0253..18cafe654d17f 100644 --- a/src/librustc_target/spec/redox_base.rs +++ b/src/librustc_target/spec/redox_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { let mut args = LinkArgs::new(); diff --git a/src/librustc_target/spec/riscv32i_unknown_none_elf.rs b/src/librustc_target/spec/riscv32i_unknown_none_elf.rs index a7020bc8f919e..aade1e708232e 100644 --- a/src/librustc_target/spec/riscv32i_unknown_none_elf.rs +++ b/src/librustc_target/spec/riscv32i_unknown_none_elf.rs @@ -1,4 +1,5 @@ -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -21,7 +22,7 @@ pub fn target() -> TargetResult { features: String::new(), executables: true, panic_strategy: PanicStrategy::Abort, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, abi_blacklist: super::riscv_base::abi_blacklist(), eliminate_frame_pointer: false, diff --git a/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs b/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs index 6ec49410aebe2..e2990eeb826f7 100644 --- a/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs +++ b/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs @@ -1,4 +1,5 @@ -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -21,7 +22,7 @@ pub fn target() -> TargetResult { features: "+m,+a,+c".to_string(), executables: true, panic_strategy: PanicStrategy::Abort, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, abi_blacklist: super::riscv_base::abi_blacklist(), eliminate_frame_pointer: false, diff --git a/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs b/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs index 70346347fb38f..55a4d58dfccca 100644 --- a/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs +++ b/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs @@ -1,4 +1,5 @@ -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -21,7 +22,7 @@ pub fn target() -> TargetResult { features: "+m,+c".to_string(), executables: true, panic_strategy: PanicStrategy::Abort, - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, abi_blacklist: super::riscv_base::abi_blacklist(), eliminate_frame_pointer: false, diff --git a/src/librustc_target/spec/riscv64gc_unknown_linux_gnu.rs b/src/librustc_target/spec/riscv64gc_unknown_linux_gnu.rs index 638e6770ebf8c..715449d74ce22 100644 --- a/src/librustc_target/spec/riscv64gc_unknown_linux_gnu.rs +++ b/src/librustc_target/spec/riscv64gc_unknown_linux_gnu.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{CodeModel, LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -14,7 +14,7 @@ pub fn target() -> TargetResult { linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { abi_blacklist: super::riscv_base::abi_blacklist(), - code_model: Some("medium".to_string()), + code_model: Some(CodeModel::Medium), cpu: "generic-rv64".to_string(), features: "+m,+a,+f,+d,+c".to_string(), llvm_abiname: "lp64d".to_string(), diff --git a/src/librustc_target/spec/riscv64gc_unknown_none_elf.rs b/src/librustc_target/spec/riscv64gc_unknown_none_elf.rs index ca0f3d64d4a03..7376a14e951f5 100644 --- a/src/librustc_target/spec/riscv64gc_unknown_none_elf.rs +++ b/src/librustc_target/spec/riscv64gc_unknown_none_elf.rs @@ -1,4 +1,5 @@ -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { Ok(Target { @@ -21,8 +22,8 @@ pub fn target() -> TargetResult { features: "+m,+a,+f,+d,+c".to_string(), executables: true, panic_strategy: PanicStrategy::Abort, - relocation_model: "static".to_string(), - code_model: Some("medium".to_string()), + relocation_model: RelocModel::Static, + code_model: Some(CodeModel::Medium), emit_debug_gdb_scripts: false, abi_blacklist: super::riscv_base::abi_blacklist(), eliminate_frame_pointer: false, diff --git a/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs b/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs index 614403fe02af0..a3b0eb5334f40 100644 --- a/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs +++ b/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs @@ -1,4 +1,5 @@ -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{CodeModel, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; pub fn target() -> TargetResult { Ok(Target { @@ -21,8 +22,8 @@ pub fn target() -> TargetResult { features: "+m,+a,+c".to_string(), executables: true, panic_strategy: PanicStrategy::Abort, - relocation_model: "static".to_string(), - code_model: Some("medium".to_string()), + relocation_model: RelocModel::Static, + code_model: Some(CodeModel::Medium), emit_debug_gdb_scripts: false, abi_blacklist: super::riscv_base::abi_blacklist(), eliminate_frame_pointer: false, diff --git a/src/librustc_target/spec/solaris_base.rs b/src/librustc_target/spec/solaris_base.rs index 98a2a0fbc9cc1..8d3a3563f4164 100644 --- a/src/librustc_target/spec/solaris_base.rs +++ b/src/librustc_target/spec/solaris_base.rs @@ -1,5 +1,4 @@ use crate::spec::TargetOptions; -use std::default::Default; pub fn opts() -> TargetOptions { TargetOptions { diff --git a/src/librustc_target/spec/tests/tests_impl.rs b/src/librustc_target/spec/tests/tests_impl.rs new file mode 100644 index 0000000000000..4cf186bdd7c1a --- /dev/null +++ b/src/librustc_target/spec/tests/tests_impl.rs @@ -0,0 +1,43 @@ +use super::super::*; + +pub(super) fn test_target(target: TargetResult) { + // Grab the TargetResult struct. If we successfully retrieved + // a Target, then the test JSON encoding/decoding can run for this + // Target on this testing platform (i.e., checking the iOS targets + // only on a Mac test platform). + if let Ok(original) = target { + original.check_consistency(); + let as_json = original.to_json(); + let parsed = Target::from_json(as_json).unwrap(); + assert_eq!(original, parsed); + } +} + +impl Target { + fn check_consistency(&self) { + // Check that LLD with the given flavor is treated identically to the linker it emulates. + // If you target really needs to deviate from the rules below, whitelist it + // and document the reasons. + assert_eq!( + self.linker_flavor == LinkerFlavor::Msvc + || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link), + self.options.lld_flavor == LldFlavor::Link, + ); + for args in &[ + &self.options.pre_link_args, + &self.options.pre_link_args_crt, + &self.options.late_link_args, + &self.options.late_link_args_dynamic, + &self.options.late_link_args_static, + &self.options.post_link_args, + ] { + assert_eq!( + args.get(&LinkerFlavor::Msvc), + args.get(&LinkerFlavor::Lld(LldFlavor::Link)), + ); + if args.contains_key(&LinkerFlavor::Msvc) { + assert_eq!(self.options.lld_flavor, LldFlavor::Link); + } + } + } +} diff --git a/src/librustc_target/spec/thumb_base.rs b/src/librustc_target/spec/thumb_base.rs index 99ab996be959d..646a149a33621 100644 --- a/src/librustc_target/spec/thumb_base.rs +++ b/src/librustc_target/spec/thumb_base.rs @@ -27,8 +27,7 @@ // differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of // build scripts / gcc flags. -use crate::spec::{PanicStrategy, TargetOptions}; -use std::default::Default; +use crate::spec::{PanicStrategy, RelocModel, TargetOptions}; pub fn opts() -> TargetOptions { // See rust-lang/rfcs#1645 for a discussion about these defaults @@ -41,7 +40,7 @@ pub fn opts() -> TargetOptions { panic_strategy: PanicStrategy::Abort, // Similarly, one almost always never wants to use relocatable code because of the extra // costs it involves. - relocation_model: "static".to_string(), + relocation_model: RelocModel::Static, abi_blacklist: super::arm_base::abi_blacklist(), // When this section is added a volatile load to its start address is also generated. This // volatile load is a footgun as it can end up loading an invalid memory address, depending diff --git a/src/librustc_target/spec/thumbv7a_pc_windows_msvc.rs b/src/librustc_target/spec/thumbv7a_pc_windows_msvc.rs index ab0f7791e2cda..21d62d252e09a 100644 --- a/src/librustc_target/spec/thumbv7a_pc_windows_msvc.rs +++ b/src/librustc_target/spec/thumbv7a_pc_windows_msvc.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_msvc_base::opts(); @@ -10,7 +10,12 @@ pub fn target() -> TargetResult { // should be smart enough to insert branch islands only // where necessary, but this is not the observed behavior. // Disabling the LBR optimization works around the issue. - base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().push("/OPT:NOLBR".to_string()); + let pre_link_args_msvc = "/OPT:NOLBR".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().push(pre_link_args_msvc.clone()); + base.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .push(pre_link_args_msvc); // FIXME(jordanrh): use PanicStrategy::Unwind when SEH is // implemented for windows/arm in LLVM diff --git a/src/librustc_target/spec/uefi_base.rs b/src/librustc_target/spec/uefi_base.rs deleted file mode 100644 index d09da9478fb2b..0000000000000 --- a/src/librustc_target/spec/uefi_base.rs +++ /dev/null @@ -1,67 +0,0 @@ -// This defines a base target-configuration for native UEFI systems. The UEFI specification has -// quite detailed sections on the ABI of all the supported target architectures. In almost all -// cases it simply follows what Microsoft Windows does. Hence, whenever in doubt, see the MSDN -// documentation. -// UEFI uses COFF/PE32+ format for binaries. All binaries must be statically linked. No dynamic -// linker is supported. As native to COFF, binaries are position-dependent, but will be relocated -// by the loader if the pre-chosen memory location is already in use. -// UEFI forbids running code on anything but the boot-CPU. No interrupts are allowed other than -// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all -// code runs in the same environment, no process separation is supported. - -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; -use std::default::Default; - -pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - - pre_link_args.insert( - LinkerFlavor::Lld(LldFlavor::Link), - vec![ - // Suppress the verbose logo and authorship debugging output, which would needlessly - // clog any log files. - "/NOLOGO".to_string(), - // UEFI is fully compatible to non-executable data pages. Tell the compiler that - // non-code sections can be marked as non-executable, including stack pages. In fact, - // firmware might enforce this, so we better let the linker know about this, so it - // will fail if the compiler ever tries placing code on the stack (e.g., trampoline - // constructs and alike). - "/NXCOMPAT".to_string(), - // There is no runtime for UEFI targets, prevent them from being linked. UEFI targets - // must be freestanding. - "/nodefaultlib".to_string(), - // Non-standard subsystems have no default entry-point in PE+ files. We have to define - // one. "efi_main" seems to be a common choice amongst other implementations and the - // spec. - "/entry:efi_main".to_string(), - // COFF images have a "Subsystem" field in their header, which defines what kind of - // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION, - // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION, - // which is very likely the most common option. Individual projects can override this - // with custom linker flags. - // The subsystem-type only has minor effects on the application. It defines the memory - // regions the application is loaded into (runtime-drivers need to be put into - // reserved areas), as well as whether a return from the entry-point is treated as - // exit (default for applications). - "/subsystem:efi_application".to_string(), - ], - ); - - TargetOptions { - dynamic_linking: false, - executables: true, - disable_redzone: true, - exe_suffix: ".efi".to_string(), - allows_weak_linkage: false, - panic_strategy: PanicStrategy::Abort, - stack_probes: true, - singlethread: true, - emit_debug_gdb_scripts: false, - - linker: Some("rust-lld".to_string()), - lld_flavor: LldFlavor::Link, - pre_link_args, - - ..Default::default() - } -} diff --git a/src/librustc_target/spec/uefi_msvc_base.rs b/src/librustc_target/spec/uefi_msvc_base.rs new file mode 100644 index 0000000000000..3f7c78c8e7d47 --- /dev/null +++ b/src/librustc_target/spec/uefi_msvc_base.rs @@ -0,0 +1,58 @@ +// This defines a base target-configuration for native UEFI systems. The UEFI specification has +// quite detailed sections on the ABI of all the supported target architectures. In almost all +// cases it simply follows what Microsoft Windows does. Hence, whenever in doubt, see the MSDN +// documentation. +// UEFI uses COFF/PE32+ format for binaries. All binaries must be statically linked. No dynamic +// linker is supported. As native to COFF, binaries are position-dependent, but will be relocated +// by the loader if the pre-chosen memory location is already in use. +// UEFI forbids running code on anything but the boot-CPU. No interrupts are allowed other than +// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all +// code runs in the same environment, no process separation is supported. + +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut base = super::msvc_base::opts(); + + let pre_link_args_msvc = vec![ + // Non-standard subsystems have no default entry-point in PE+ files. We have to define + // one. "efi_main" seems to be a common choice amongst other implementations and the + // spec. + "/entry:efi_main".to_string(), + // COFF images have a "Subsystem" field in their header, which defines what kind of + // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION, + // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION, + // which is very likely the most common option. Individual projects can override this + // with custom linker flags. + // The subsystem-type only has minor effects on the application. It defines the memory + // regions the application is loaded into (runtime-drivers need to be put into + // reserved areas), as well as whether a return from the entry-point is treated as + // exit (default for applications). + "/subsystem:efi_application".to_string(), + ]; + base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + base.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .extend(pre_link_args_msvc); + + TargetOptions { + disable_redzone: true, + exe_suffix: ".efi".to_string(), + allows_weak_linkage: false, + panic_strategy: PanicStrategy::Abort, + stack_probes: true, + singlethread: true, + linker: Some("rust-lld".to_string()), + // FIXME: This should likely be `true` inherited from `msvc_base` + // because UEFI follows Windows ABI and uses PE/COFF. + // The `false` is probably causing ABI bugs right now. + is_like_windows: false, + // FIXME: This should likely be `true` inherited from `msvc_base` + // because UEFI follows Windows ABI and uses PE/COFF. + // The `false` is probably causing ABI bugs right now. + is_like_msvc: false, + + ..base + } +} diff --git a/src/librustc_target/spec/vxworks_base.rs b/src/librustc_target/spec/vxworks_base.rs index 1763c9139b1bf..1b25c51278d4a 100644 --- a/src/librustc_target/spec/vxworks_base.rs +++ b/src/librustc_target/spec/vxworks_base.rs @@ -1,5 +1,4 @@ use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { let mut args_crt = LinkArgs::new(); diff --git a/src/librustc_target/spec/wasm32_base.rs b/src/librustc_target/spec/wasm32_base.rs index 47e80e8db19f6..d4a65aa1a2574 100644 --- a/src/librustc_target/spec/wasm32_base.rs +++ b/src/librustc_target/spec/wasm32_base.rs @@ -1,4 +1,5 @@ -use super::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; +use super::crt_objects::CrtObjectsFallback; +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; use std::collections::BTreeMap; pub fn options() -> TargetOptions { @@ -123,6 +124,8 @@ pub fn options() -> TargetOptions { pre_link_args, + crt_objects_fallback: Some(CrtObjectsFallback::Wasm), + // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when // PIC code is implemented this has quite a drastric effect if it stays // at the default, `pic`. In an effort to keep wasm binaries as minimal @@ -130,7 +133,7 @@ pub fn options() -> TargetOptions { // 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: "static".to_string(), + 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 @@ -138,7 +141,7 @@ pub fn options() -> TargetOptions { // `has_elf_tls`) 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_elf_tls: true, - tls_model: "local-exec".to_string(), + tls_model: TlsModel::LocalExec, // gdb scripts don't work on wasm blobs emit_debug_gdb_scripts: false, diff --git a/src/librustc_target/spec/wasm32_unknown_unknown.rs b/src/librustc_target/spec/wasm32_unknown_unknown.rs index 22d3885e4afa7..ded95a34d55d4 100644 --- a/src/librustc_target/spec/wasm32_unknown_unknown.rs +++ b/src/librustc_target/spec/wasm32_unknown_unknown.rs @@ -21,10 +21,6 @@ pub fn target() -> Result { // otherwise clang_args.push("--target=wasm32-unknown-unknown".to_string()); - // Disable attempting to link crt1.o since it typically isn't present and - // isn't needed currently. - clang_args.push("-nostdlib".to_string()); - // For now this target just never has an entry symbol no matter the output // type, so unconditionally pass this. clang_args.push("-Wl,--no-entry".to_string()); diff --git a/src/librustc_target/spec/wasm32_wasi.rs b/src/librustc_target/spec/wasm32_wasi.rs index d5ef230dcf7d2..0bba7bdd4735c 100644 --- a/src/librustc_target/spec/wasm32_wasi.rs +++ b/src/librustc_target/spec/wasm32_wasi.rs @@ -73,7 +73,7 @@ //! you know what you're getting in to! use super::wasm32_base; -use super::{LinkerFlavor, LldFlavor, Target}; +use super::{crt_objects, LinkerFlavor, LldFlavor, Target}; pub fn target() -> Result { let mut options = wasm32_base::options(); @@ -84,9 +84,8 @@ pub fn target() -> Result { .or_insert(Vec::new()) .push("--target=wasm32-wasi".to_string()); - // When generating an executable be sure to put the startup object at the - // front so the main function is correctly hooked up. - options.pre_link_objects_exe_crt.push("crt1.o".to_string()); + options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback(); + options.post_link_objects_fallback = crt_objects::post_wasi_fallback(); // 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 diff --git a/src/librustc_target/spec/windows_base.rs b/src/librustc_target/spec/windows_base.rs deleted file mode 100644 index 188548b41fe75..0000000000000 --- a/src/librustc_target/spec/windows_base.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; -use std::default::Default; - -pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( - LinkerFlavor::Gcc, - vec![ - // Tell GCC to avoid linker plugins, because we are not bundling - // them with Windows installer, and Rust does its own LTO anyways. - "-fno-use-linker-plugin".to_string(), - // Always enable DEP (NX bit) when it is available - "-Wl,--nxcompat".to_string(), - // Do not use the standard system startup files or libraries when linking - "-nostdlib".to_string(), - ], - ); - - let mut late_link_args = LinkArgs::new(); - let mut late_link_args_dynamic = LinkArgs::new(); - let mut late_link_args_static = LinkArgs::new(); - late_link_args.insert( - LinkerFlavor::Gcc, - vec![ - "-lmingwex".to_string(), - "-lmingw32".to_string(), - "-lmsvcrt".to_string(), - // mingw's msvcrt is a weird hybrid import library and static library. - // And it seems that the linker fails to use import symbols from msvcrt - // that are required from functions in msvcrt in certain cases. For example - // `_fmode` that is used by an implementation of `__p__fmode` in x86_64. - // Listing the library twice seems to fix that, and seems to also be done - // by mingw's gcc (Though not sure if it's done on purpose, or by mistake). - // - // See https://github.com/rust-lang/rust/pull/47483 - "-lmsvcrt".to_string(), - "-luser32".to_string(), - "-lkernel32".to_string(), - ], - ); - late_link_args_dynamic.insert( - LinkerFlavor::Gcc, - vec![ - // If any of our crates are dynamically linked then we need to use - // the shared libgcc_s-dw2-1.dll. This is required to support - // unwinding across DLL boundaries. - "-lgcc_s".to_string(), - "-lgcc".to_string(), - "-lkernel32".to_string(), - ], - ); - late_link_args_static.insert( - LinkerFlavor::Gcc, - vec![ - // If all of our crates are statically linked then we can get away - // with statically linking the libgcc unwinding code. This allows - // binaries to be redistributed without the libgcc_s-dw2-1.dll - // dependency, but unfortunately break unwinding across DLL - // boundaries when unwinding across FFI boundaries. - "-lgcc".to_string(), - "-lgcc_eh".to_string(), - "-lpthread".to_string(), - "-lkernel32".to_string(), - ], - ); - - TargetOptions { - // FIXME(#13846) this should be enabled for windows - function_sections: false, - linker: Some("gcc".to_string()), - dynamic_linking: true, - executables: true, - dll_prefix: String::new(), - dll_suffix: ".dll".to_string(), - exe_suffix: ".exe".to_string(), - staticlib_prefix: String::new(), - staticlib_suffix: ".lib".to_string(), - no_default_libraries: true, - target_family: Some("windows".to_string()), - is_like_windows: true, - allows_weak_linkage: false, - pre_link_args, - pre_link_objects_exe: vec![ - "crt2.o".to_string(), // mingw C runtime initialization for executables - "rsbegin.o".to_string(), // Rust compiler runtime initialization, see rsbegin.rs - ], - pre_link_objects_dll: vec![ - "dllcrt2.o".to_string(), // mingw C runtime initialization for dlls - "rsbegin.o".to_string(), - ], - late_link_args, - late_link_args_dynamic, - late_link_args_static, - post_link_objects: vec!["rsend.o".to_string()], - abi_return_struct_as_int: true, - emit_debug_gdb_scripts: false, - requires_uwtable: true, - - ..Default::default() - } -} diff --git a/src/librustc_target/spec/windows_gnu_base.rs b/src/librustc_target/spec/windows_gnu_base.rs new file mode 100644 index 0000000000000..f556bf03f02a0 --- /dev/null +++ b/src/librustc_target/spec/windows_gnu_base.rs @@ -0,0 +1,96 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Gcc, + vec![ + // Tell GCC to avoid linker plugins, because we are not bundling + // them with Windows installer, and Rust does its own LTO anyways. + "-fno-use-linker-plugin".to_string(), + // Always enable DEP (NX bit) when it is available + "-Wl,--nxcompat".to_string(), + ], + ); + + let mut late_link_args = LinkArgs::new(); + let mut late_link_args_dynamic = LinkArgs::new(); + let mut late_link_args_static = LinkArgs::new(); + late_link_args.insert( + LinkerFlavor::Gcc, + vec![ + "-lmingwex".to_string(), + "-lmingw32".to_string(), + "-lmsvcrt".to_string(), + // mingw's msvcrt is a weird hybrid import library and static library. + // And it seems that the linker fails to use import symbols from msvcrt + // that are required from functions in msvcrt in certain cases. For example + // `_fmode` that is used by an implementation of `__p__fmode` in x86_64. + // Listing the library twice seems to fix that, and seems to also be done + // by mingw's gcc (Though not sure if it's done on purpose, or by mistake). + // + // See https://github.com/rust-lang/rust/pull/47483 + "-lmsvcrt".to_string(), + "-luser32".to_string(), + "-lkernel32".to_string(), + ], + ); + late_link_args_dynamic.insert( + LinkerFlavor::Gcc, + vec![ + // If any of our crates are dynamically linked then we need to use + // the shared libgcc_s-dw2-1.dll. This is required to support + // unwinding across DLL boundaries. + "-lgcc_s".to_string(), + "-lgcc".to_string(), + "-lkernel32".to_string(), + ], + ); + late_link_args_static.insert( + LinkerFlavor::Gcc, + vec![ + // If all of our crates are statically linked then we can get away + // with statically linking the libgcc unwinding code. This allows + // binaries to be redistributed without the libgcc_s-dw2-1.dll + // dependency, but unfortunately break unwinding across DLL + // boundaries when unwinding across FFI boundaries. + "-lgcc_eh".to_string(), + "-l:libpthread.a".to_string(), + "-lgcc".to_string(), + // libpthread depends on libmsvcrt, so we need to link it *again*. + "-lmsvcrt".to_string(), + "-lkernel32".to_string(), + ], + ); + + TargetOptions { + // FIXME(#13846) this should be enabled for windows + function_sections: false, + linker: Some("gcc".to_string()), + dynamic_linking: true, + executables: true, + dll_prefix: String::new(), + dll_suffix: ".dll".to_string(), + exe_suffix: ".exe".to_string(), + staticlib_prefix: "lib".to_string(), + staticlib_suffix: ".a".to_string(), + target_family: Some("windows".to_string()), + is_like_windows: true, + allows_weak_linkage: false, + pre_link_args, + pre_link_objects: crt_objects::pre_mingw(), + post_link_objects: crt_objects::post_mingw(), + pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), + post_link_objects_fallback: crt_objects::post_mingw_fallback(), + crt_objects_fallback: Some(CrtObjectsFallback::Mingw), + late_link_args, + late_link_args_dynamic, + late_link_args_static, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + requires_uwtable: true, + + ..Default::default() + } +} diff --git a/src/librustc_target/spec/windows_msvc_base.rs b/src/librustc_target/spec/windows_msvc_base.rs index 52b166df93996..77171f8672e8a 100644 --- a/src/librustc_target/spec/windows_msvc_base.rs +++ b/src/librustc_target/spec/windows_msvc_base.rs @@ -1,36 +1,30 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; -use std::default::Default; +use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { - let pre_args = vec!["/NOLOGO".to_string(), "/NXCOMPAT".to_string()]; - let mut args = LinkArgs::new(); - args.insert(LinkerFlavor::Msvc, pre_args.clone()); - args.insert(LinkerFlavor::Lld(LldFlavor::Link), pre_args); + let base = super::msvc_base::opts(); TargetOptions { - function_sections: true, dynamic_linking: true, - executables: true, dll_prefix: String::new(), dll_suffix: ".dll".to_string(), exe_suffix: ".exe".to_string(), staticlib_prefix: String::new(), staticlib_suffix: ".lib".to_string(), target_family: Some("windows".to_string()), - is_like_windows: true, - is_like_msvc: true, - // set VSLANG to 1033 can prevent link.exe from using - // language packs, and avoid generating Non-UTF-8 error - // messages if a link error occurred. - link_env: vec![("VSLANG".to_string(), "1033".to_string())], - lld_flavor: LldFlavor::Link, - pre_link_args: args, crt_static_allows_dylibs: true, crt_static_respected: true, - abi_return_struct_as_int: true, - emit_debug_gdb_scripts: false, requires_uwtable: true, + // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC + // as there's been trouble in the past of linking the C++ standard + // library required by LLVM. This likely needs to happen one day, but + // in general Windows is also a more controlled environment than + // Unix, so it's not necessarily as critical that this be implemented. + // + // Note that there are also some licensing worries about statically + // linking some libraries which require a specific agreement, so it may + // not ever be possible for us to pass this flag. + no_default_libraries: false, - ..Default::default() + ..base } } diff --git a/src/librustc_target/spec/windows_uwp_base.rs b/src/librustc_target/spec/windows_uwp_base.rs deleted file mode 100644 index 3f7eb442bbc73..0000000000000 --- a/src/librustc_target/spec/windows_uwp_base.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; -use std::default::Default; - -pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( - LinkerFlavor::Gcc, - vec![ - // Tell GCC to avoid linker plugins, because we are not bundling - // them with Windows installer, and Rust does its own LTO anyways. - "-fno-use-linker-plugin".to_string(), - // Always enable DEP (NX bit) when it is available - "-Wl,--nxcompat".to_string(), - ], - ); - - let mut late_link_args = LinkArgs::new(); - late_link_args.insert( - LinkerFlavor::Gcc, - vec![ - //"-lwinstorecompat".to_string(), - //"-lmingwex".to_string(), - //"-lwinstorecompat".to_string(), - "-lwinstorecompat".to_string(), - "-lruntimeobject".to_string(), - "-lsynchronization".to_string(), - "-lvcruntime140_app".to_string(), - "-lucrt".to_string(), - "-lwindowsapp".to_string(), - "-lmingwex".to_string(), - "-lmingw32".to_string(), - ], - ); - - TargetOptions { - // FIXME(#13846) this should be enabled for windows - function_sections: false, - linker: Some("gcc".to_string()), - dynamic_linking: true, - executables: false, - dll_prefix: String::new(), - dll_suffix: ".dll".to_string(), - exe_suffix: ".exe".to_string(), - staticlib_prefix: "lib".to_string(), - staticlib_suffix: ".a".to_string(), - no_default_libraries: true, - target_family: Some("windows".to_string()), - is_like_windows: true, - allows_weak_linkage: false, - pre_link_args, - pre_link_objects_exe: vec![ - "rsbegin.o".to_string(), // Rust compiler runtime initialization, see rsbegin.rs - ], - pre_link_objects_dll: vec!["rsbegin.o".to_string()], - late_link_args, - post_link_objects: vec!["rsend.o".to_string()], - abi_return_struct_as_int: true, - emit_debug_gdb_scripts: false, - requires_uwtable: true, - limit_rdylib_exports: false, - - ..Default::default() - } -} diff --git a/src/librustc_target/spec/windows_uwp_gnu_base.rs b/src/librustc_target/spec/windows_uwp_gnu_base.rs new file mode 100644 index 0000000000000..e12a37144da5e --- /dev/null +++ b/src/librustc_target/spec/windows_uwp_gnu_base.rs @@ -0,0 +1,37 @@ +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let base = super::windows_gnu_base::opts(); + + // FIXME: This should be updated for the exception machinery changes from #67502 + // and inherit from `windows_gnu_base`, at least partially. + let mut late_link_args = LinkArgs::new(); + let late_link_args_dynamic = LinkArgs::new(); + let late_link_args_static = LinkArgs::new(); + late_link_args.insert( + LinkerFlavor::Gcc, + vec![ + //"-lwinstorecompat".to_string(), + //"-lmingwex".to_string(), + //"-lwinstorecompat".to_string(), + "-lwinstorecompat".to_string(), + "-lruntimeobject".to_string(), + "-lsynchronization".to_string(), + "-lvcruntime140_app".to_string(), + "-lucrt".to_string(), + "-lwindowsapp".to_string(), + "-lmingwex".to_string(), + "-lmingw32".to_string(), + ], + ); + + TargetOptions { + executables: false, + limit_rdylib_exports: false, + late_link_args, + late_link_args_dynamic, + late_link_args_static, + + ..base + } +} diff --git a/src/librustc_target/spec/windows_uwp_msvc_base.rs b/src/librustc_target/spec/windows_uwp_msvc_base.rs index 3d639b6b628b3..04ffa1a0addbe 100644 --- a/src/librustc_target/spec/windows_uwp_msvc_base.rs +++ b/src/librustc_target/spec/windows_uwp_msvc_base.rs @@ -1,37 +1,14 @@ -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; -use std::default::Default; +use crate::spec::{LinkerFlavor, LldFlavor, TargetOptions}; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Msvc, - vec![ - "/NOLOGO".to_string(), - "/NXCOMPAT".to_string(), - "/APPCONTAINER".to_string(), - "mincore.lib".to_string(), - ], - ); + let mut opts = super::windows_msvc_base::opts(); - TargetOptions { - function_sections: true, - dynamic_linking: true, - executables: true, - dll_prefix: String::new(), - dll_suffix: ".dll".to_string(), - exe_suffix: ".exe".to_string(), - staticlib_prefix: String::new(), - staticlib_suffix: ".lib".to_string(), - target_family: Some("windows".to_string()), - is_like_windows: true, - is_like_msvc: true, - pre_link_args: args, - crt_static_allows_dylibs: true, - crt_static_respected: true, - abi_return_struct_as_int: true, - emit_debug_gdb_scripts: false, - requires_uwtable: true, + let pre_link_args_msvc = vec!["/APPCONTAINER".to_string(), "mincore.lib".to_string()]; + opts.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + opts.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .extend(pre_link_args_msvc); - ..Default::default() - } + opts } diff --git a/src/librustc_target/spec/x86_64_apple_darwin.rs b/src/librustc_target/spec/x86_64_apple_darwin.rs index e846f42f8f849..31011e8474958 100644 --- a/src/librustc_target/spec/x86_64_apple_darwin.rs +++ b/src/librustc_target/spec/x86_64_apple_darwin.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { let llvm_target = super::apple_base::macos_llvm_target(&arch); Ok(Target { - llvm_target: llvm_target, + llvm_target, target_endian: "little".to_string(), target_pointer_width: "64".to_string(), target_c_int_width: "32".to_string(), diff --git a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs b/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs index 3e9552ef0cf34..d26efc0985952 100644 --- a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs +++ b/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs @@ -1,6 +1,6 @@ use std::iter; -use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use super::{crt_objects, LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; pub fn target() -> Result { const PRE_LINK_ARGS: &[&str] = &[ @@ -68,7 +68,8 @@ pub fn target() -> Result { PRE_LINK_ARGS.iter().cloned().map(String::from).collect(), )) .collect(), - post_link_objects: vec!["libunwind.a".into()], + // FIXME: libunwind is certainly not a CRT object, use some other option instead. + post_link_objects: crt_objects::all("libunwind.a"), override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(String::from).collect()), relax_elf_relocations: true, ..Default::default() diff --git a/src/librustc_target/spec/x86_64_linux_kernel.rs b/src/librustc_target/spec/x86_64_linux_kernel.rs index 89070c99e3941..65bb97d84aae9 100644 --- a/src/librustc_target/spec/x86_64_linux_kernel.rs +++ b/src/librustc_target/spec/x86_64_linux_kernel.rs @@ -1,7 +1,7 @@ // This defines the amd64 target for the Linux Kernel. See the linux-kernel-base module for // generic Linux kernel options. -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{CodeModel, LinkerFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::linux_kernel_base::opts(); @@ -10,7 +10,7 @@ pub fn target() -> TargetResult { base.features = "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" .to_string(); - base.code_model = Some("kernel".to_string()); + base.code_model = Some(CodeModel::Kernel); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); Ok(Target { diff --git a/src/librustc_target/spec/x86_64_pc_windows_gnu.rs b/src/librustc_target/spec/x86_64_pc_windows_gnu.rs index 3d3acc682dea4..eb97fa56814d8 100644 --- a/src/librustc_target/spec/x86_64_pc_windows_gnu.rs +++ b/src/librustc_target/spec/x86_64_pc_windows_gnu.rs @@ -1,7 +1,7 @@ use crate::spec::{LinkerFlavor, Target, TargetResult}; pub fn target() -> TargetResult { - let mut base = super::windows_base::opts(); + let mut base = super::windows_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); diff --git a/src/librustc_target/spec/x86_64_unknown_illumos.rs b/src/librustc_target/spec/x86_64_unknown_illumos.rs new file mode 100644 index 0000000000000..8d461f67397f2 --- /dev/null +++ b/src/librustc_target/spec/x86_64_unknown_illumos.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::illumos_base::opts(); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string(), "-std=c99".to_string()]); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + + Ok(Target { + // LLVM does not currently have a separate illumos target, + // so we still pass Solaris to it + llvm_target: "x86_64-pc-solaris".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), + arch: "x86_64".to_string(), + target_os: "illumos".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/src/librustc_target/spec/x86_64_unknown_uefi.rs b/src/librustc_target/spec/x86_64_unknown_uefi.rs index 7660b68aae62e..849227a574aeb 100644 --- a/src/librustc_target/spec/x86_64_unknown_uefi.rs +++ b/src/librustc_target/spec/x86_64_unknown_uefi.rs @@ -5,10 +5,10 @@ // The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with // LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, Target, TargetResult}; pub fn target() -> TargetResult { - let mut base = super::uefi_base::opts(); + let mut base = super::uefi_msvc_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -26,12 +26,7 @@ pub fn target() -> TargetResult { // UEFI systems run without a host OS, hence we cannot assume any code locality. We must tell // LLVM to expect code to reference any address in the address-space. The "large" code-model // places no locality-restrictions, so it fits well here. - base.code_model = Some("large".to_string()); - - // UEFI mirrors the calling-conventions used on windows. In case of x86-64 this means small - // structs will be returned as int. This shouldn't matter much, since the restrictions placed - // by the UEFI specifications forbid any ABI to return structures. - base.abi_return_struct_as_int = true; + base.code_model = Some(CodeModel::Large); Ok(Target { llvm_target: "x86_64-unknown-windows".to_string(), diff --git a/src/librustc_target/spec/x86_64_uwp_windows_gnu.rs b/src/librustc_target/spec/x86_64_uwp_windows_gnu.rs index 48366e24a39e4..ad6002f6b89e4 100644 --- a/src/librustc_target/spec/x86_64_uwp_windows_gnu.rs +++ b/src/librustc_target/spec/x86_64_uwp_windows_gnu.rs @@ -1,7 +1,7 @@ use crate::spec::{LinkerFlavor, Target, TargetResult}; pub fn target() -> TargetResult { - let mut base = super::windows_uwp_base::opts(); + let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); diff --git a/src/librustc_trait_selection/Cargo.toml b/src/librustc_trait_selection/Cargo.toml index 5b2da41d06672..254695e53e04d 100644 --- a/src/librustc_trait_selection/Cargo.toml +++ b/src/librustc_trait_selection/Cargo.toml @@ -13,7 +13,7 @@ doctest = false fmt_macros = { path = "../libfmt_macros" } log = { version = "0.4", features = ["release_max_level_info", "std"] } rustc_attr = { path = "../librustc_attr" } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_ast = { path = "../librustc_ast" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_trait_selection/infer.rs b/src/librustc_trait_selection/infer.rs index 7abcbf45277fb..66df4fe951162 100644 --- a/src/librustc_trait_selection/infer.rs +++ b/src/librustc_trait_selection/infer.rs @@ -1,14 +1,14 @@ use crate::traits::query::outlives_bounds::InferCtxtExt as _; use crate::traits::{self, TraitEngine, TraitEngineExt}; -use rustc::arena::ArenaAllocatable; -use rustc::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse}; -use rustc::middle::lang_items; -use rustc::traits::query::Fallible; -use rustc::ty::{self, Ty, TypeFoldable}; use rustc_hir as hir; +use rustc_hir::lang_items; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::traits::ObligationCause; +use rustc_middle::arena::ArenaAllocatable; +use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse}; +use rustc_middle::traits::query::Fallible; +use rustc_middle::ty::{self, Ty, TypeFoldable}; use rustc_span::{Span, DUMMY_SP}; use std::fmt::Debug; @@ -43,7 +43,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { ) -> bool { let ty = self.resolve_vars_if_possible(&ty); - if !(param_env, ty).has_local_value() { + if !(param_env, ty).needs_infer() { return ty.is_copy_modulo_regions(self.tcx, param_env, span); } diff --git a/src/librustc_trait_selection/lib.rs b/src/librustc_trait_selection/lib.rs index 739aff4fb94c9..4796b431d8dca 100644 --- a/src/librustc_trait_selection/lib.rs +++ b/src/librustc_trait_selection/lib.rs @@ -2,9 +2,9 @@ //! //! - **Traits.** Trait resolution is implemented in the `traits` module. //! -//! For more information about how rustc works, see the [rustc guide]. +//! For more information about how rustc works, see the [rustc-dev-guide]. //! -//! [rustc guide]: https://rust-lang.github.io/rustc-guide/ +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ //! //! # Note //! @@ -15,6 +15,9 @@ #![feature(drain_filter)] #![feature(in_band_lifetimes)] #![feature(crate_visibility_modifier)] +#![feature(or_patterns)] +#![feature(str_strip)] +#![feature(option_zip)] #![recursion_limit = "512"] // For rustdoc #[macro_use] @@ -25,7 +28,7 @@ extern crate rustc_data_structures; #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; pub mod infer; pub mod opaque_types; diff --git a/src/librustc_trait_selection/opaque_types.rs b/src/librustc_trait_selection/opaque_types.rs index 30189d189f2ed..396965fcfb8b7 100644 --- a/src/librustc_trait_selection/opaque_types.rs +++ b/src/librustc_trait_selection/opaque_types.rs @@ -1,17 +1,17 @@ use crate::infer::InferCtxtExt as _; use crate::traits::{self, PredicateObligation}; -use rustc::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use rustc::ty::free_region_map::FreeRegionRelations; -use rustc::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; -use rustc::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::Node; use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; +use rustc_infer::infer::free_regions::FreeRegionRelations; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, InferCtxt, InferOk}; +use rustc_middle::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use rustc_session::config::nightly_options; use rustc_span::Span; @@ -418,7 +418,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let opaque_type = tcx.mk_opaque(def_id, opaque_defn.substs); let required_region_bounds = - required_region_bounds(tcx, opaque_type, bounds.predicates); + required_region_bounds(tcx, opaque_type, bounds.predicates.into_iter()); debug_assert!(!required_region_bounds.is_empty()); for required_region in required_region_bounds { @@ -972,7 +972,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { ) .emit(); - self.tcx().consts.err + self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty: ct.ty }) } } } @@ -1036,11 +1036,13 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // let x = || foo(); // returns the Opaque assoc with `foo` // } // ``` - if let Some(opaque_hir_id) = tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let opaque_hir_id = tcx.hir().as_local_hir_id(def_id); let parent_def_id = self.parent_def_id; let def_scope_default = || { let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); - parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id) + parent_def_id + == tcx.hir().local_def_id(opaque_parent_hir_id).to_def_id() }; let (in_definition_scope, origin) = match tcx.hir().find(opaque_hir_id) { Some(Node::Item(item)) => match item.kind { @@ -1056,14 +1058,22 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { origin, .. }) => ( - may_define_opaque_type(tcx, self.parent_def_id, opaque_hir_id), + may_define_opaque_type( + tcx, + self.parent_def_id.expect_local(), + opaque_hir_id, + ), origin, ), _ => (def_scope_default(), hir::OpaqueTyOrigin::TypeAlias), }, Some(Node::ImplItem(item)) => match item.kind { hir::ImplItemKind::OpaqueTy(_) => ( - may_define_opaque_type(tcx, self.parent_def_id, opaque_hir_id), + may_define_opaque_type( + tcx, + self.parent_def_id.expect_local(), + opaque_hir_id, + ), hir::OpaqueTyOrigin::TypeAlias, ), _ => (def_scope_default(), hir::OpaqueTyOrigin::TypeAlias), @@ -1074,7 +1084,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { ), }; if in_definition_scope { - return self.fold_opaque_ty(ty, def_id, substs, origin); + return self.fold_opaque_ty(ty, def_id.to_def_id(), substs, origin); } debug!( @@ -1127,7 +1137,8 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: bounds={:?}", bounds); - let required_region_bounds = required_region_bounds(tcx, ty, bounds.predicates.clone()); + let required_region_bounds = + required_region_bounds(tcx, ty, bounds.predicates.iter().cloned()); debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds); // Make sure that we are in fact defining the *entire* type @@ -1200,11 +1211,15 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { /// } /// ``` /// -/// Here, `def_id` is the `DefId` of the defining use of the opaque type (e.g., `f1` or `f2`), +/// Here, `def_id` is the `LocalDefId` of the defining use of the opaque type (e.g., `f1` or `f2`), /// and `opaque_hir_id` is the `HirId` of the definition of the opaque type `Baz`. /// For the above example, this function returns `true` for `f1` and `false` for `f2`. -pub fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: DefId, opaque_hir_id: hir::HirId) -> bool { - let mut hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); +pub fn may_define_opaque_type( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + opaque_hir_id: hir::HirId, +) -> bool { + let mut hir_id = tcx.hir().as_local_hir_id(def_id); // Named opaque types can be defined by any siblings or children of siblings. let scope = tcx.hir().get_defining_scope(opaque_hir_id); @@ -1245,18 +1260,16 @@ pub fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: DefId, opaque_hir_id: hir crate fn required_region_bounds( tcx: TyCtxt<'tcx>, erased_self_ty: Ty<'tcx>, - predicates: Vec>, + predicates: impl Iterator>, ) -> Vec> { - debug!( - "required_region_bounds(erased_self_ty={:?}, predicates={:?})", - erased_self_ty, predicates - ); + debug!("required_region_bounds(erased_self_ty={:?})", erased_self_ty); assert!(!erased_self_ty.has_escaping_bound_vars()); traits::elaborate_predicates(tcx, predicates) - .filter_map(|predicate| { - match predicate { + .filter_map(|obligation| { + debug!("required_region_bounds(obligation={:?})", obligation); + match obligation.predicate { ty::Predicate::Projection(..) | ty::Predicate::Trait(..) | ty::Predicate::Subtype(..) @@ -1264,7 +1277,8 @@ crate fn required_region_bounds( | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::RegionOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => None, ty::Predicate::TypeOutlives(predicate) => { // Search for a bound of the form `erased_self_ty // : 'a`, but be wary of something like `for<'a> diff --git a/src/librustc_trait_selection/traits/auto_trait.rs b/src/librustc_trait_selection/traits/auto_trait.rs index bd980e6eb8b8a..e19ddcd9e5e98 100644 --- a/src/librustc_trait_selection/traits/auto_trait.rs +++ b/src/librustc_trait_selection/traits/auto_trait.rs @@ -5,8 +5,8 @@ use super::*; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxt; -use rustc::ty::fold::TypeFolder; -use rustc::ty::{Region, RegionVid}; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::{Region, RegionVid}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -187,13 +187,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // to store all of the necessary region/lifetime bounds in the InferContext, as well as // an additional sanity check. let mut fulfill = FulfillmentContext::new(); - fulfill.register_bound( - &infcx, - full_env, - ty, - trait_did, - ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID), - ); + fulfill.register_bound(&infcx, full_env, ty, trait_did, ObligationCause::dummy()); fulfill.select_all_or_error(&infcx).unwrap_or_else(|e| { panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, e) }); @@ -201,7 +195,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let body_id_map: FxHashMap<_, _> = infcx .inner .borrow() - .region_obligations + .region_obligations() .iter() .map(|&(id, _)| (id, vec![])) .collect(); @@ -287,12 +281,12 @@ impl AutoTraitFinder<'tcx> { }, })); - let mut computed_preds: FxHashSet<_> = param_env.caller_bounds.iter().cloned().collect(); + let computed_preds = param_env.caller_bounds.iter().cloned(); let mut user_computed_preds: FxHashSet<_> = user_env.caller_bounds.iter().cloned().collect(); let mut new_env = param_env; - let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID); + let dummy_cause = ObligationCause::dummy(); while let Some(pred) = predicates.pop_front() { infcx.clear_caches(); @@ -314,19 +308,17 @@ impl AutoTraitFinder<'tcx> { &Ok(Some(ref vtable)) => { // If we see an explicit negative impl (e.g., `impl !Send for MyStruct`), // we immediately bail out, since it's impossible for us to continue. - match vtable { - Vtable::VtableImpl(VtableImplData { impl_def_id, .. }) => { - // Blame 'tidy' for the weird bracket placement. - if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { - debug!( - "evaluate_nested_obligations: found explicit negative impl\ + + if let Vtable::VtableImpl(VtableImplData { impl_def_id, .. }) = vtable { + // Blame 'tidy' for the weird bracket placement. + if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { + debug!( + "evaluate_nested_obligations: found explicit negative impl\ {:?}, bailing out", - impl_def_id - ); - return None; - } + impl_def_id + ); + return None; } - _ => {} } let obligations = vtable.clone().nested_obligations().into_iter(); @@ -366,9 +358,11 @@ impl AutoTraitFinder<'tcx> { _ => panic!("Unexpected error for '{:?}': {:?}", ty, result), }; - computed_preds.extend(user_computed_preds.iter().cloned()); - let normalized_preds = - elaborate_predicates(tcx, computed_preds.iter().cloned().collect()); + let normalized_preds = elaborate_predicates( + tcx, + computed_preds.clone().chain(user_computed_preds.iter().cloned()), + ) + .map(|o| o.predicate); new_env = ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal, None); } @@ -417,80 +411,77 @@ impl AutoTraitFinder<'tcx> { ) { let mut should_add_new = true; user_computed_preds.retain(|&old_pred| { - match (&new_pred, old_pred) { - (&ty::Predicate::Trait(new_trait, _), ty::Predicate::Trait(old_trait, _)) => { - if new_trait.def_id() == old_trait.def_id() { - let new_substs = new_trait.skip_binder().trait_ref.substs; - let old_substs = old_trait.skip_binder().trait_ref.substs; - - if !new_substs.types().eq(old_substs.types()) { - // We can't compare lifetimes if the types are different, - // so skip checking `old_pred`. - return true; - } + if let (&ty::Predicate::Trait(new_trait, _), ty::Predicate::Trait(old_trait, _)) = + (&new_pred, old_pred) + { + if new_trait.def_id() == old_trait.def_id() { + let new_substs = new_trait.skip_binder().trait_ref.substs; + let old_substs = old_trait.skip_binder().trait_ref.substs; + + if !new_substs.types().eq(old_substs.types()) { + // We can't compare lifetimes if the types are different, + // so skip checking `old_pred`. + return true; + } - for (new_region, old_region) in - new_substs.regions().zip(old_substs.regions()) - { - match (new_region, old_region) { - // If both predicates have an `ReLateBound` (a HRTB) in the - // same spot, we do nothing. - ( - ty::RegionKind::ReLateBound(_, _), - ty::RegionKind::ReLateBound(_, _), - ) => {} - - (ty::RegionKind::ReLateBound(_, _), _) - | (_, ty::RegionKind::ReVar(_)) => { - // One of these is true: - // The new predicate has a HRTB in a spot where the old - // predicate does not (if they both had a HRTB, the previous - // match arm would have executed). A HRBT is a 'stricter' - // bound than anything else, so we want to keep the newer - // predicate (with the HRBT) in place of the old predicate. - // - // OR - // - // The old predicate has a region variable where the new - // predicate has some other kind of region. An region - // variable isn't something we can actually display to a user, - // so we choose their new predicate (which doesn't have a region - // variable). - // - // In both cases, we want to remove the old predicate, - // from `user_computed_preds`, and replace it with the new - // one. Having both the old and the new - // predicate in a `ParamEnv` would confuse `SelectionContext`. - // - // We're currently in the predicate passed to 'retain', - // so we return `false` to remove the old predicate from - // `user_computed_preds`. - return false; - } - (_, ty::RegionKind::ReLateBound(_, _)) - | (ty::RegionKind::ReVar(_), _) => { - // This is the opposite situation as the previous arm. - // One of these is true: - // - // The old predicate has a HRTB lifetime in a place where the - // new predicate does not. - // - // OR - // - // The new predicate has a region variable where the old - // predicate has some other type of region. - // - // We want to leave the old - // predicate in `user_computed_preds`, and skip adding - // new_pred to `user_computed_params`. - should_add_new = false - } - _ => {} + for (new_region, old_region) in new_substs.regions().zip(old_substs.regions()) { + match (new_region, old_region) { + // If both predicates have an `ReLateBound` (a HRTB) in the + // same spot, we do nothing. + ( + ty::RegionKind::ReLateBound(_, _), + ty::RegionKind::ReLateBound(_, _), + ) => {} + + (ty::RegionKind::ReLateBound(_, _), _) + | (_, ty::RegionKind::ReVar(_)) => { + // One of these is true: + // The new predicate has a HRTB in a spot where the old + // predicate does not (if they both had a HRTB, the previous + // match arm would have executed). A HRBT is a 'stricter' + // bound than anything else, so we want to keep the newer + // predicate (with the HRBT) in place of the old predicate. + // + // OR + // + // The old predicate has a region variable where the new + // predicate has some other kind of region. An region + // variable isn't something we can actually display to a user, + // so we choose their new predicate (which doesn't have a region + // variable). + // + // In both cases, we want to remove the old predicate, + // from `user_computed_preds`, and replace it with the new + // one. Having both the old and the new + // predicate in a `ParamEnv` would confuse `SelectionContext`. + // + // We're currently in the predicate passed to 'retain', + // so we return `false` to remove the old predicate from + // `user_computed_preds`. + return false; + } + (_, ty::RegionKind::ReLateBound(_, _)) + | (ty::RegionKind::ReVar(_), _) => { + // This is the opposite situation as the previous arm. + // One of these is true: + // + // The old predicate has a HRTB lifetime in a place where the + // new predicate does not. + // + // OR + // + // The new predicate has a region variable where the old + // predicate has some other type of region. + // + // We want to leave the old + // predicate in `user_computed_preds`, and skip adding + // new_pred to `user_computed_params`. + should_add_new = false } + _ => {} } } } - _ => {} } true }); @@ -619,7 +610,7 @@ impl AutoTraitFinder<'tcx> { select: &mut SelectionContext<'_, 'tcx>, only_projections: bool, ) -> bool { - let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID); + let dummy_cause = ObligationCause::dummy(); for (obligation, mut predicate) in nested.map(|o| (o.clone(), o.predicate)) { let is_new_pred = fresh_preds.insert(self.clean_pred(select.infcx(), predicate)); @@ -749,7 +740,7 @@ impl AutoTraitFinder<'tcx> { if p.ty().skip_binder().has_infer_types() { if !self.evaluate_nested_obligations( ty, - v.clone().iter().cloned(), + v.into_iter(), computed_preds, fresh_preds, predicates, diff --git a/src/librustc_trait_selection/traits/chalk_fulfill.rs b/src/librustc_trait_selection/traits/chalk_fulfill.rs new file mode 100644 index 0000000000000..be0512dcac95b --- /dev/null +++ b/src/librustc_trait_selection/traits/chalk_fulfill.rs @@ -0,0 +1,262 @@ +//! Defines a Chalk-based `TraitEngine` + +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::InferCtxt; +use crate::traits::query::NoSolution; +use crate::traits::{ + ChalkEnvironmentAndGoal, ChalkEnvironmentClause, FulfillmentError, FulfillmentErrorCode, + ObligationCause, PredicateObligation, SelectionError, TraitEngine, +}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +pub struct FulfillmentContext<'tcx> { + obligations: FxHashSet>, +} + +impl FulfillmentContext<'tcx> { + crate fn new() -> Self { + FulfillmentContext { obligations: FxHashSet::default() } + } +} + +fn environment<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> &'tcx ty::List> { + use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind}; + use rustc_middle::ty::subst::GenericArgKind; + + debug!("environment(def_id = {:?})", def_id); + + // The environment of an impl Trait type is its defining function's environment. + if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { + return environment(tcx, parent); + } + + // Compute the bounds on `Self` and the type parameters. + let ty::InstantiatedPredicates { predicates, .. } = + tcx.predicates_of(def_id).instantiate_identity(tcx); + + let clauses = predicates.into_iter().map(ChalkEnvironmentClause::Predicate); + + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let node = tcx.hir().get(hir_id); + + enum NodeKind { + TraitImpl, + InherentImpl, + Fn, + Other, + }; + + let node_kind = match node { + Node::TraitItem(item) => match item.kind { + TraitItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + Node::ImplItem(item) => match item.kind { + ImplItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + Node::Item(item) => match item.kind { + ItemKind::Impl { of_trait: Some(_), .. } => NodeKind::TraitImpl, + ItemKind::Impl { of_trait: None, .. } => NodeKind::InherentImpl, + ItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + // FIXME: closures? + _ => NodeKind::Other, + }; + + // FIXME(eddyb) isn't the unordered nature of this a hazard? + let mut inputs = FxHashSet::default(); + + match node_kind { + // In a trait impl, we assume that the header trait ref and all its + // constituents are well-formed. + NodeKind::TraitImpl => { + let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl"); + + inputs.extend(trait_ref.substs.iter().flat_map(|&arg| arg.walk())); + } + + // In an inherent impl, we assume that the receiver type and all its + // constituents are well-formed. + NodeKind::InherentImpl => { + let self_ty = tcx.type_of(def_id); + inputs.extend(self_ty.walk()); + } + + // In an fn, we assume that the arguments and all their constituents are + // well-formed. + NodeKind::Fn => { + let fn_sig = tcx.fn_sig(def_id); + let fn_sig = tcx.liberate_late_bound_regions(def_id, &fn_sig); + + inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk())); + } + + NodeKind::Other => (), + } + let input_clauses = inputs.into_iter().filter_map(|arg| { + match arg.unpack() { + GenericArgKind::Type(ty) => Some(ChalkEnvironmentClause::TypeFromEnv(ty)), + + // FIXME(eddyb) no WF conditions from lifetimes? + GenericArgKind::Lifetime(_) => None, + + // FIXME(eddyb) support const generics in Chalk + GenericArgKind::Const(_) => None, + } + }); + + tcx.mk_chalk_environment_clause_list(clauses.chain(input_clauses)) +} + +/// We need to wrap a `ty::Predicate` in an elaborated environment *before* we +/// canonicalize. This is due to the fact that we insert extra clauses into the +/// environment for all input types (`FromEnv`). +fn in_environment( + infcx: &InferCtxt<'_, 'tcx>, + obligation: &PredicateObligation<'tcx>, +) -> ChalkEnvironmentAndGoal<'tcx> { + assert!(!infcx.is_in_snapshot()); + let obligation = infcx.resolve_vars_if_possible(obligation); + + let environment = match obligation.param_env.def_id { + Some(def_id) => environment(infcx.tcx, def_id), + None if obligation.param_env.caller_bounds.is_empty() => ty::List::empty(), + _ => bug!("non-empty `ParamEnv` with no def-id"), + }; + + ChalkEnvironmentAndGoal { environment, goal: obligation.predicate } +} + +impl TraitEngine<'tcx> for FulfillmentContext<'tcx> { + fn normalize_projection_type( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + _param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + _cause: ObligationCause<'tcx>, + ) -> Ty<'tcx> { + infcx.tcx.mk_ty(ty::Projection(projection_ty)) + } + + fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + obligation: PredicateObligation<'tcx>, + ) { + assert!(!infcx.is_in_snapshot()); + let obligation = infcx.resolve_vars_if_possible(&obligation); + + self.obligations.insert(obligation); + } + + fn select_all_or_error( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec>> { + self.select_where_possible(infcx)?; + + if self.obligations.is_empty() { + Ok(()) + } else { + let errors = self + .obligations + .iter() + .map(|obligation| FulfillmentError { + obligation: obligation.clone(), + code: FulfillmentErrorCode::CodeAmbiguity, + points_at_arg_span: false, + }) + .collect(); + Err(errors) + } + } + + fn select_where_possible( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec>> { + let mut errors = Vec::new(); + let mut next_round = FxHashSet::default(); + let mut making_progress; + + loop { + making_progress = false; + + // We iterate over all obligations, and record if we are able + // to unambiguously prove at least one obligation. + for obligation in self.obligations.drain() { + let goal_in_environment = in_environment(infcx, &obligation); + let mut orig_values = OriginalQueryValues::default(); + let canonical_goal = + infcx.canonicalize_query(&goal_in_environment, &mut orig_values); + + match infcx.tcx.evaluate_goal(canonical_goal) { + Ok(response) => { + if response.is_proven() { + making_progress = true; + + match infcx.instantiate_query_response_and_region_obligations( + &obligation.cause, + obligation.param_env, + &orig_values, + &response, + ) { + Ok(infer_ok) => next_round.extend( + infer_ok.obligations.into_iter().map(|obligation| { + assert!(!infcx.is_in_snapshot()); + infcx.resolve_vars_if_possible(&obligation) + }), + ), + + Err(_err) => errors.push(FulfillmentError { + obligation, + code: FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + ), + points_at_arg_span: false, + }), + } + } else { + // Ambiguous: retry at next round. + next_round.insert(obligation); + } + } + + Err(NoSolution) => errors.push(FulfillmentError { + obligation, + code: FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + ), + points_at_arg_span: false, + }), + } + } + next_round = std::mem::replace(&mut self.obligations, next_round); + + if !making_progress { + break; + } + } + + if errors.is_empty() { Ok(()) } else { Err(errors) } + } + + fn pending_obligations(&self) -> Vec> { + self.obligations.iter().cloned().collect() + } +} diff --git a/src/librustc_trait_selection/traits/codegen/mod.rs b/src/librustc_trait_selection/traits/codegen/mod.rs index 5c2fc3f305c1f..b2e46bc6da116 100644 --- a/src/librustc_trait_selection/traits/codegen/mod.rs +++ b/src/librustc_trait_selection/traits/codegen/mod.rs @@ -7,8 +7,9 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::{ FulfillmentContext, Obligation, ObligationCause, SelectionContext, TraitEngine, Vtable, }; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, TyCtxt}; +use rustc_errors::ErrorReported; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, TyCtxt}; /// Attempts to resolve an obligation to a vtable. The result is /// a shallow vtable resolution, meaning that we do not @@ -19,7 +20,7 @@ use rustc::ty::{self, TyCtxt}; pub fn codegen_fulfill_obligation<'tcx>( ty: TyCtxt<'tcx>, (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), -) -> Option> { +) -> Result, ErrorReported> { // Remove any references to regions; this helps improve caching. let trait_ref = ty.erase_regions(&trait_ref); @@ -55,7 +56,7 @@ pub fn codegen_fulfill_obligation<'tcx>( trait_ref ), ); - return None; + return Err(ErrorReported); } Err(e) => { bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) @@ -75,7 +76,7 @@ pub fn codegen_fulfill_obligation<'tcx>( let vtable = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &vtable); info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - Some(vtable) + Ok(vtable) }) } diff --git a/src/librustc_trait_selection/traits/coherence.rs b/src/librustc_trait_selection/traits/coherence.rs index dc13af99fec6c..85c2f9246afcc 100644 --- a/src/librustc_trait_selection/traits/coherence.rs +++ b/src/librustc_trait_selection/traits/coherence.rs @@ -8,12 +8,13 @@ use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::SkipLeakCheck; use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; +use std::iter; /// Whether we do the orphan check relative to this crate or /// to some remote crate. @@ -326,12 +327,12 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// try to implement this trait-ref. To check for this, we use InCrate::Remote /// mode. That is sound because we already know all the impls from known crates. /// -/// 3. For non-#[fundamental] traits, they guarantee that parent crates can +/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can /// add "non-blanket" impls without breaking negative reasoning in dependent /// crates. This is the "rebalancing coherence" (RFC 1023) restriction. /// /// For that, we only a allow crate to perform negative reasoning on -/// non-local-non-#[fundamental] only if there's a local key parameter as per (2). +/// non-local-non-`#[fundamental]` only if there's a local key parameter as per (2). /// /// Because we never perform negative reasoning generically (coherence does /// not involve type parameters), this can be interpreted as doing the full @@ -378,26 +379,36 @@ fn orphan_check_trait_ref<'tcx>( ty: Ty<'tcx>, in_crate: InCrate, ) -> Vec> { - if fundamental_ty(ty) && ty_is_non_local(ty, in_crate).is_some() { - ty.walk_shallow().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).collect() - } else { - vec![ty] + // FIXME(eddyb) figure out if this is redundant with `ty_is_non_local`, + // or maybe if this should be calling `ty_is_non_local_constructor`. + if ty_is_non_local(tcx, ty, in_crate).is_some() { + if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) { + return inner_tys + .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) + .collect(); + } } + + vec![ty] } let mut non_local_spans = vec![]; - for (i, input_ty) in - trait_ref.input_types().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).enumerate() + for (i, input_ty) in trait_ref + .substs + .types() + .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) + .enumerate() { debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty); - let non_local_tys = ty_is_non_local(input_ty, in_crate); + let non_local_tys = ty_is_non_local(tcx, input_ty, in_crate); if non_local_tys.is_none() { debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); return Ok(()); } else if let ty::Param(_) = input_ty.kind { debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); let local_type = trait_ref - .input_types() + .substs + .types() .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) .find(|ty| ty_is_non_local_constructor(ty, in_crate).is_none()); @@ -416,30 +427,53 @@ fn orphan_check_trait_ref<'tcx>( Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) } -fn ty_is_non_local<'t>(ty: Ty<'t>, in_crate: InCrate) -> Option>> { +fn ty_is_non_local(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, in_crate: InCrate) -> Option>> { match ty_is_non_local_constructor(ty, in_crate) { Some(ty) => { - if !fundamental_ty(ty) { - Some(vec![ty]) - } else { - let tys: Vec<_> = ty - .walk_shallow() - .filter_map(|t| ty_is_non_local(t, in_crate)) - .flat_map(|i| i) + if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) { + let tys: Vec<_> = inner_tys + .filter_map(|ty| ty_is_non_local(tcx, ty, in_crate)) + .flatten() .collect(); if tys.is_empty() { None } else { Some(tys) } + } else { + Some(vec![ty]) } } None => None, } } -fn fundamental_ty(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Ref(..) => true, - ty::Adt(def, _) => def.is_fundamental(), - _ => false, - } +/// For `#[fundamental]` ADTs and `&T` / `&mut T`, returns `Some` with the +/// type parameters of the ADT, or `T`, respectively. For non-fundamental +/// types, returns `None`. +fn fundamental_ty_inner_tys( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option>> { + let (first_ty, rest_tys) = match ty.kind { + ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()), + ty::Adt(def, substs) if def.is_fundamental() => { + let mut types = substs.types(); + + // FIXME(eddyb) actually validate `#[fundamental]` up-front. + match types.next() { + None => { + tcx.sess.span_err( + tcx.def_span(def.did), + "`#[fundamental]` requires at least one type parameter", + ); + + return None; + } + + Some(first_ty) => (first_ty, types), + } + } + _ => return None, + }; + + Some(iter::once(first_ty).chain(rest_tys)) } fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { @@ -451,6 +485,7 @@ fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { } } +// FIXME(eddyb) this can just return `bool` as it always returns `Some(ty)` or `None`. fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> { debug!("ty_is_non_local_constructor({:?})", ty); @@ -532,9 +567,8 @@ fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> ty::Error => None, - ty::UnnormalizedProjection(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) => bug!("ty_is_local invoked on unexpected type: {:?}", ty), + ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { + bug!("ty_is_local invoked on unexpected type: {:?}", ty) + } } } diff --git a/src/librustc_trait_selection/traits/engine.rs b/src/librustc_trait_selection/traits/engine.rs index ee4715e0c20f6..4d4778869794b 100644 --- a/src/librustc_trait_selection/traits/engine.rs +++ b/src/librustc_trait_selection/traits/engine.rs @@ -1,14 +1,18 @@ -use rustc::ty::TyCtxt; +use rustc_middle::ty::TyCtxt; -use super::FulfillmentContext; use super::TraitEngine; +use super::{ChalkFulfillmentContext, FulfillmentContext}; pub trait TraitEngineExt<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Box; } impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { - fn new(_tcx: TyCtxt<'tcx>) -> Box { - Box::new(FulfillmentContext::new()) + fn new(tcx: TyCtxt<'tcx>) -> Box { + if tcx.sess.opts.debugging_opts.chalk { + Box::new(ChalkFulfillmentContext::new()) + } else { + Box::new(FulfillmentContext::new()) + } } } diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index b9ee991aa0226..139b860072224 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -11,21 +11,19 @@ use super::{ use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc::mir::interpret::ErrorHandled; -use rustc::ty::error::ExpectedFound; -use rustc::ty::fast_reject; -use rustc::ty::fold::TypeFolder; -use rustc::ty::SubtypePredicate; -use rustc::ty::{ - self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::{Node, QPath, TyKind, WhereBoundPredicate, WherePredicate}; +use rustc_hir::Node; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::{ + self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, + TypeFoldable, WithConstness, +}; use rustc_session::DiagnosticMessageId; -use rustc_span::source_map::SourceMap; use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; @@ -66,7 +64,7 @@ pub trait InferCtxtExt<'tcx> { /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> (Span, Vec); + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)>; /// Reports an error when the number of arguments needed by a /// trait match doesn't match the number that the expression @@ -127,7 +125,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .borrow_mut() .entry(span) .or_default() - .push(error.obligation.predicate.clone()); + .push(error.obligation.predicate); } // We do this in 2 passes because we want to display errors in order, though @@ -285,6 +283,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .unwrap_or(false); let is_from = format!("{}", trait_ref.print_only_trait_path()) .starts_with("std::convert::From<"); + let is_unsize = + { Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() }; let (message, note) = if is_try && is_from { ( Some(format!( @@ -293,7 +293,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { )), Some( "the question mark operation (`?`) implicitly performs a \ - conversion on the error value using the `From` trait" + conversion on the error value using the `From` trait" .to_owned(), ), ) @@ -313,6 +313,37 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { )) ); + let should_convert_option_to_result = + format!("{}", trait_ref.print_only_trait_path()) + .starts_with("std::convert::From` into a `Result` \ + using `Option::ok_or` or `Option::ok_or_else`", + ".ok_or_else(|| /* error value */)".to_string(), + Applicability::HasPlaceholders, + ); + } else if should_convert_result_to_option { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider converting the `Result` into an `Option` \ + using `Result::ok`", + ".ok()".to_string(), + Applicability::MachineApplicable, + ); + } + if let Some(ret_span) = self.return_type_span(obligation) { + err.span_label( + ret_span, + &format!("expected `{}` because of this", trait_ref.self_ty()), + ); + } + } + let explanation = if obligation.cause.code == ObligationCauseCode::MainFunctionType { "consider using `()`, or a `Result`".to_owned() @@ -359,7 +390,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { tcx.hir().body_owner_def_id(hir::BodyId { hir_id: obligation.cause.body_id, }) - }), + }) + .to_def_id(), ); err.span_label(enclosing_scope_span, s.as_str()); @@ -370,11 +402,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.suggest_remove_reference(&obligation, &mut err, &trait_ref); self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); self.note_version_mismatch(&mut err, &trait_ref); + self.suggest_await_before_try(&mut err, &obligation, &trait_ref, span); if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { err.emit(); return; } + if is_unsize { + // If the obligation failed due to a missing implementation of the + // `Unsize` trait, give a pointer to why that might be the case + err.note( + "all implementations of `Unsize` are provided \ + automatically by the compiler, see \ + \ + for more information", + ); + } + // Try to report a help message if !trait_ref.has_infer_types_or_consts() && self.predicate_can_apply(obligation.param_env, trait_ref) @@ -388,7 +432,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // which is somewhat confusing. self.suggest_restricting_param_bound( &mut err, - &trait_ref, + trait_ref, obligation.cause.body_id, ); } else { @@ -397,12 +441,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let impl_candidates = self.find_similar_impl_candidates(trait_ref); self.report_similar_impl_candidates(impl_candidates, &mut err); } - self.suggest_change_mut( - &obligation, - &mut err, - &trait_ref, - points_at_arg, - ); + // Changing mutability doesn't make a difference to whether we have + // an `Unsize` impl (Fixes ICE in #71036) + if !is_unsize { + self.suggest_change_mut( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + ); + } } // If this error is due to `!: Trait` not implemented but `(): Trait` is @@ -482,12 +530,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { let found_kind = self.closure_kind(closure_substs).unwrap(); - let closure_span = self - .tcx - .sess - .source_map() - .def_span(self.tcx.hir().span_if_local(closure_def_id).unwrap()); - let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id).unwrap(); + let closure_span = + self.tcx.sess.source_map().guess_head_span( + self.tcx.hir().span_if_local(closure_def_id).unwrap(), + ); + let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id.expect_local()); let mut err = struct_span_err!( self.tcx.sess, closure_span, @@ -541,12 +588,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } ty::Predicate::WellFormed(ty) => { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); + if !self.tcx.sess.opts.debugging_opts.chalk { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); + } else { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.tcx.sess.struct_span_err( + span, + &format!("the type `{}` is not well-formed (chalk)", ty), + ) + } } ty::Predicate::ConstEvaluatable(..) => { @@ -559,6 +615,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation ) } + + ty::Predicate::ConstEquate(..) => { + // Errors for `ConstEquate` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-equate requirement gave wrong error: `{:?}`", + obligation + ) + } } } @@ -580,7 +647,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let found_span = found_did .and_then(|did| self.tcx.hir().span_if_local(did)) - .map(|sp| self.tcx.sess.source_map().def_span(sp)); // the sp could be an fn def + .map(|sp| self.tcx.sess.source_map().guess_head_span(sp)); // the sp could be an fn def if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { // We check closures twice, with obligations flowing in different directions, @@ -613,10 +680,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } else { let (closure_span, found) = found_did - .and_then(|did| self.tcx.hir().get_if_local(did)) - .map(|node| { - let (found_span, found) = self.get_fn_like_arguments(node); - (Some(found_span), found) + .and_then(|did| { + let node = self.tcx.hir().get_if_local(did)?; + let (found_span, found) = self.get_fn_like_arguments(node)?; + Some((Some(found_span), found)) }) .unwrap_or((found_span, found)); @@ -654,8 +721,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } // Already reported in the query. - ConstEvalFailure(ErrorHandled::Reported) => { - self.tcx.sess.delay_span_bug(span, "constant in type had an ignored error"); + ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => { + // FIXME(eddyb) remove this once `ErrorReported` becomes a proof token. + self.tcx.sess.delay_span_bug(span, "`ErrorReported` without an error"); + return; + } + + // Already reported in the query, but only as a lint. + // This shouldn't actually happen for constants used in types, modulo + // bugs. The `delay_span_bug` here ensures it won't be ignored. + ConstEvalFailure(ErrorHandled::Linted) => { + self.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); return; } @@ -674,43 +750,38 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> (Span, Vec) { - match node { + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(_, ref _decl, id, span, _), .. }) => ( - self.tcx.sess.source_map().def_span(span), - self.tcx - .hir() - .body(id) + sm.guess_head_span(span), + hir.body(id) .params .iter() .map(|arg| { if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = *arg.pat { - ArgKind::Tuple( + Some(ArgKind::Tuple( Some(span), args.iter() .map(|pat| { - let snippet = self - .tcx - .sess - .source_map() - .span_to_snippet(pat.span) - .unwrap(); - (snippet, "_".to_owned()) + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) }) - .collect::>(), - ) + .collect::>>()?, + )) } else { - let name = - self.tcx.sess.source_map().span_to_snippet(arg.pat.span).unwrap(); - ArgKind::Arg(name, "_".to_owned()) + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) } }) - .collect::>(), + .collect::>>()?, ), Node::Item(&hir::Item { span, kind: hir::ItemKind::Fn(ref sig, ..), .. }) | Node::ImplItem(&hir::ImplItem { @@ -723,7 +794,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { kind: hir::TraitItemKind::Fn(ref sig, _), .. }) => ( - self.tcx.sess.source_map().def_span(span), + sm.guess_head_span(span), sig.decl .inputs .iter() @@ -737,16 +808,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .collect::>(), ), Node::Ctor(ref variant_data) => { - let span = variant_data - .ctor_hir_id() - .map(|hir_id| self.tcx.hir().span(hir_id)) - .unwrap_or(DUMMY_SP); - let span = self.tcx.sess.source_map().def_span(span); - + let span = variant_data.ctor_hir_id().map(|id| hir.span(id)).unwrap_or(DUMMY_SP); + let span = sm.guess_head_span(span); (span, vec![ArgKind::empty(); variant_data.fields().len()]) } _ => panic!("non-FnLike node found: {:?}", node), - } + }) } /// Reports an error when the number of arguments needed by a @@ -987,8 +1054,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } }; - for implication in super::elaborate_predicates(self.tcx, vec![*cond]) { - if let ty::Predicate::Trait(implication, _) = implication { + for obligation in super::elaborate_predicates(self.tcx, std::iter::once(*cond)) { + if let ty::Predicate::Trait(implication, _) = obligation.predicate { let error = error.to_poly_trait_ref(); let implication = implication.to_poly_trait_ref(); // FIXME: I'm just not taking associated types at all here. @@ -1036,6 +1103,15 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ) .emit(); } + FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { + self.report_mismatched_consts( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + err.clone(), + ) + .emit(); + } } } @@ -1156,7 +1232,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Foreign(..) => Some(19), ty::GeneratorWitness(..) => Some(20), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => None, - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), } } @@ -1188,8 +1263,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { match simp { Some(simp) => all_impls - .iter() - .filter_map(|&def_id| { + .filter_map(|def_id| { let imp = self.tcx.impl_trait_ref(def_id).unwrap(); let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); if let Some(imp_simp) = imp_simp { @@ -1197,13 +1271,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { return None; } } - Some(imp) }) .collect(), - None => { - all_impls.iter().map(|&def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect() - } + None => all_impls.map(|def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect(), } } @@ -1388,7 +1459,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { return; } let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283); - err.note(&format!("cannot resolve `{}`", predicate)); + err.note(&format!("cannot satisfy `{}`", predicate)); if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); } else if let ( @@ -1398,7 +1469,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { (self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code) { let generics = self.tcx.generics_of(*def_id); - if !generics.params.is_empty() && !snippet.ends_with('>') { + if generics.params.iter().any(|p| p.name.as_str() != "Self") + && !snippet.ends_with('>') + { // FIXME: To avoid spurious suggestions in functions where type arguments // where already supplied, we check the snippet to make sure it doesn't // end with a turbofish. Ideally we would have access to a `PathSegment` @@ -1416,9 +1489,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // | `Tt::const_val::<[i8; 123]>::` // ... // LL | const fn const_val() -> usize { - // | --------- - required by this bound in `Tt::const_val` + // | - required by this bound in `Tt::const_val` // | - // = note: cannot resolve `_: Tt` + // = note: cannot satisfy `_: Tt` err.span_suggestion_verbose( span.shrink_to_hi(), @@ -1464,12 +1537,26 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Predicate::Projection(ref data) => { let trait_ref = data.to_poly_trait_ref(self.tcx); let self_ty = trait_ref.self_ty(); + let ty = data.skip_binder().ty; if predicate.references_error() { return; } - let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); - err.note(&format!("cannot resolve `{}`", predicate)); - err + if self_ty.needs_infer() && ty.needs_infer() { + // We do this for the `foo.collect()?` case to produce a suggestion. + let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); + err.note(&format!("cannot satisfy `{}`", predicate)); + err + } else { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, &format!("cannot satisfy `{}`", predicate)); + err + } } _ => { @@ -1480,10 +1567,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.sess, span, E0284, - "type annotations needed: cannot resolve `{}`", + "type annotations needed: cannot satisfy `{}`", predicate, ); - err.span_label(span, &format!("cannot resolve `{}`", predicate)); + err.span_label(span, &format!("cannot satisfy `{}`", predicate)); err } }; @@ -1582,12 +1669,13 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { for param in generics.params { if param.span == *span && !param.bounds.iter().any(|bound| { - bound.trait_def_id() == self.tcx.lang_items().sized_trait() + bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id()) + == self.tcx.lang_items().sized_trait() }) { let (span, separator) = match param.bounds { [] => (span.shrink_to_hi(), ":"), - [.., bound] => (bound.span().shrink_to_hi(), " + "), + [.., bound] => (bound.span().shrink_to_hi(), " +"), }; err.span_suggestion_verbose( span, @@ -1624,7 +1712,7 @@ pub fn recursive_type_with_infinite_size_error( ) -> DiagnosticBuilder<'tcx> { assert!(type_def_id.is_local()); let span = tcx.hir().span_if_local(type_def_id).unwrap(); - let span = tcx.sess.source_map().def_span(span); + let span = tcx.sess.source_map().guess_head_span(span); let mut err = struct_span_err!( tcx.sess, span, @@ -1671,229 +1759,3 @@ impl ArgKind { } } } - -/// Suggest restricting a type param with a new bound. -pub fn suggest_constraining_type_param( - tcx: TyCtxt<'_>, - generics: &hir::Generics<'_>, - err: &mut DiagnosticBuilder<'_>, - param_name: &str, - constraint: &str, - source_map: &SourceMap, - span: Span, - def_id: Option, -) -> bool { - const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound with"; - const MSG_RESTRICT_TYPE: &str = "consider restricting this type parameter with"; - const MSG_RESTRICT_TYPE_FURTHER: &str = "consider further restricting this type parameter with"; - - let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); - - let param = if let Some(param) = param { - param - } else { - return false; - }; - - if def_id == tcx.lang_items().sized_trait() { - // Type parameters are already `Sized` by default. - err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); - return true; - } - - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - err.span_help(param.span, &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint)); - - err.tool_only_span_suggestion( - param.span, - MSG_RESTRICT_BOUND_FURTHER, - format!("{} + {}", param_name, constraint), - Applicability::MachineApplicable, - ); - - return true; - } - - if generics.where_clause.predicates.is_empty() { - if let Some(bounds_span) = param.bounds_span() { - // If user has provided some bounds, suggest restricting them: - // - // fn foo(t: T) { ... } - // --- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - - err.span_help( - bounds_span, - &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint), - ); - - let span_hi = param.span.with_hi(span.hi()); - let span_with_colon = source_map.span_through_char(span_hi, ':'); - - if span_hi != param.span && span_with_colon != span_hi { - err.tool_only_span_suggestion( - span_with_colon, - MSG_RESTRICT_BOUND_FURTHER, - format!("{}: {} + ", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } else { - // If user hasn't provided any bounds, suggest adding a new one: - // - // fn foo(t: T) { ... } - // - help: consider restricting this type parameter with `T: Foo` - - err.span_help( - param.span, - &format!("{} `{}: {}`", MSG_RESTRICT_TYPE, param_name, constraint), - ); - - err.tool_only_span_suggestion( - param.span, - MSG_RESTRICT_TYPE, - format!("{}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - - true - } else { - // This part is a bit tricky, because using the `where` clause user can - // provide zero, one or many bounds for the same type parameter, so we - // have following cases to consider: - // - // 1) When the type parameter has been provided zero bounds - // - // Message: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - help: consider restricting this type parameter with `where X: Bar` - // - // Suggestion: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - insert: `, T: Zar` - - let mut param_spans = Vec::new(); - - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, bounded_ty, .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); - } - } - } - } - } - - let where_clause_span = - generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(); - - match ¶m_spans[..] { - &[] => { - err.span_help( - param.span, - &format!("{} `where {}: {}`", MSG_RESTRICT_TYPE, param_name, constraint), - ); - - err.tool_only_span_suggestion( - where_clause_span, - MSG_RESTRICT_TYPE, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - - &[¶m_span] => { - err.span_help( - param_span, - &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint), - ); - - let span_hi = param_span.with_hi(span.hi()); - let span_with_colon = source_map.span_through_char(span_hi, ':'); - - if span_hi != param_span && span_with_colon != span_hi { - err.tool_only_span_suggestion( - span_with_colon, - MSG_RESTRICT_BOUND_FURTHER, - format!("{}: {} +", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } - - _ => { - err.span_help( - param.span, - &format!( - "{} `where {}: {}`", - MSG_RESTRICT_TYPE_FURTHER, param_name, constraint, - ), - ); - - err.tool_only_span_suggestion( - where_clause_span, - MSG_RESTRICT_BOUND_FURTHER, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } - - true - } -} diff --git a/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs b/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs index 18b2ca8983720..fd87759a7621f 100644 --- a/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs +++ b/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs @@ -2,10 +2,10 @@ use super::{ ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, }; use crate::infer::InferCtxt; -use rustc::ty::subst::Subst; -use rustc::ty::{self, GenericParamDefKind}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, GenericParamDefKind}; use rustc_span::symbol::sym; use super::InferCtxtPrivExt; @@ -82,10 +82,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { match &node { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { self.describe_generator(*body_id).or_else(|| { - Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { - "an async function" - } else { - "a function" + Some(match sig.header { + hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function", + _ => "a function", }) }) } @@ -97,10 +96,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { kind: hir::ImplItemKind::Fn(sig, body_id), .. }) => self.describe_generator(*body_id).or_else(|| { - Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { - "an async method" - } else { - "a method" + Some(match sig.header { + hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method", + _ => "a method", }) }), hir::Node::Expr(hir::Expr { @@ -134,7 +132,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { match obligation.cause.code { ObligationCauseCode::BuiltinDerivedObligation(..) - | ObligationCauseCode::ImplDerivedObligation(..) => {} + | ObligationCauseCode::ImplDerivedObligation(..) + | ObligationCauseCode::DerivedObligation(..) => {} _ => { // this is a "direct", user-specified, rather than derived, // obligation. @@ -142,7 +141,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } - if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code { + if let ObligationCauseCode::ItemObligation(item) + | ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code + { // FIXME: maybe also have some way of handling methods // from other traits? That would require name resolution, // which we might want to be some sort of hygienic. @@ -219,11 +220,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } if let ty::Dynamic(traits, _) = self_ty.kind { for t in *traits.skip_binder() { - match t { - ty::ExistentialPredicate::Trait(trait_ref) => { - flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) - } - _ => {} + if let ty::ExistentialPredicate::Trait(trait_ref) = t { + flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) } } } diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 22b8d0583966b..b8771d59dd4bd 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -1,30 +1,44 @@ use super::{ EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + SelectionContext, }; use crate::infer::InferCtxt; -use crate::traits::error_reporting::suggest_constraining_type_param; +use crate::traits::normalize_projection_type; -use rustc::ty::TypeckTables; -use rustc::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; -use rustc_span::symbol::{kw, sym}; +use rustc_hir::lang_items; +use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; +use rustc_middle::ty::TypeckTables; +use rustc_middle::ty::{ + self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, + TyCtxt, TypeFoldable, WithConstness, +}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use std::fmt; use super::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -crate trait InferCtxtExt<'tcx> { +#[derive(Debug)] +pub enum GeneratorInteriorOrUpvar { + // span of interior type + Interior(Span), + // span of upvar + Upvar(Span), +} + +// This trait is public to expose the diagnostics methods to clippy. +pub trait InferCtxtExt<'tcx> { fn suggest_restricting_param_bound( &self, err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::PolyTraitRef<'_>, + trait_ref: ty::PolyTraitRef<'_>, body_id: hir::HirId, ); @@ -81,6 +95,8 @@ crate trait InferCtxtExt<'tcx> { trait_ref: &ty::Binder>, ); + fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option; + fn suggest_impl_trait( &self, err: &mut DiagnosticBuilder<'tcx>, @@ -120,12 +136,10 @@ crate trait InferCtxtExt<'tcx> { fn note_obligation_cause_for_async_await( &self, err: &mut DiagnosticBuilder<'_>, - target_span: Span, - scope_span: &Option, - expr: Option, - snippet: String, - first_generator: DefId, - last_generator: Option, + interior_or_upvar_span: GeneratorInteriorOrUpvar, + interior_extra_info: Option<(Option, Span, Option, Option)>, + inner_generator_body: Option<&hir::Body<'_>>, + outer_generator: Option, trait_ref: ty::TraitRef<'_>, target_ty: Ty<'tcx>, tables: &ty::TypeckTables<'_>, @@ -143,13 +157,163 @@ crate trait InferCtxtExt<'tcx> { T: fmt::Display; fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>); + + /// Suggest to await before try: future? => future.await? + fn suggest_await_before_try( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + trait_ref: &ty::Binder>, + span: Span, + ); +} + +fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { + ( + generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(), + format!( + "{} {}", + if !generics.where_clause.predicates.is_empty() { "," } else { " where" }, + pred, + ), + ) +} + +/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but +/// it can also be an `impl Trait` param that needs to be decomposed to a type +/// param for cleaner code. +fn suggest_restriction( + generics: &hir::Generics<'_>, + msg: &str, + err: &mut DiagnosticBuilder<'_>, + fn_sig: Option<&hir::FnSig<'_>>, + projection: Option<&ty::ProjectionTy<'_>>, + trait_ref: ty::PolyTraitRef<'_>, + super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>, +) { + // When we are dealing with a trait, `super_traits` will be `Some`: + // Given `trait T: A + B + C {}` + // - ^^^^^^^^^ GenericBounds + // | + // &Ident + let span = generics.where_clause.span_for_predicates_or_empty_place(); + if span.from_expansion() || span.desugaring_kind().is_some() { + return; + } + // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`... + if let Some((bound_str, fn_sig)) = + fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind { + // Shenanigans to get the `Trait` from the `impl Trait`. + ty::Param(param) => { + // `fn foo(t: impl Trait)` + // ^^^^^ get this string + param.name.as_str().strip_prefix("impl").map(|s| (s.trim_start().to_string(), sig)) + } + _ => None, + }) + { + // We know we have an `impl Trait` that doesn't satisfy a required projection. + + // Find all of the ocurrences of `impl Trait` for `Trait` in the function arguments' + // types. There should be at least one, but there might be *more* than one. In that + // case we could just ignore it and try to identify which one needs the restriction, + // but instead we choose to suggest replacing all instances of `impl Trait` with `T` + // where `T: Trait`. + let mut ty_spans = vec![]; + let impl_trait_str = format!("impl {}", bound_str); + for input in fn_sig.decl.inputs { + if let hir::TyKind::Path(hir::QPath::Resolved( + None, + hir::Path { segments: [segment], .. }, + )) = input.kind + { + if segment.ident.as_str() == impl_trait_str.as_str() { + // `fn foo(t: impl Trait)` + // ^^^^^^^^^^ get this to suggest `T` instead + + // There might be more than one `impl Trait`. + ty_spans.push(input.span); + } + } + } + + let type_param_name = generics.params.next_type_param_name(Some(&bound_str)); + // The type param `T: Trait` we will suggest to introduce. + let type_param = format!("{}: {}", type_param_name, bound_str); + + // FIXME: modify the `trait_ref` instead of string shenanigans. + // Turn `::Bar: Qux` into `::Bar: Qux`. + let pred = trait_ref.without_const().to_predicate().to_string(); + let pred = pred.replace(&impl_trait_str, &type_param_name); + let mut sugg = vec![ + match generics + .params + .iter() + .filter(|p| match p.kind { + hir::GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } => false, + _ => true, + }) + .last() + { + // `fn foo(t: impl Trait)` + // ^ suggest `` here + None => (generics.span, format!("<{}>", type_param)), + // `fn foo(t: impl Trait)` + // ^^^ suggest `` here + Some(param) => ( + param.bounds_span().unwrap_or(param.span).shrink_to_hi(), + format!(", {}", type_param), + ), + }, + // `fn foo(t: impl Trait)` + // ^ suggest `where ::A: Bound` + predicate_constraint(generics, pred), + ]; + sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string()))); + + // Suggest `fn foo(t: T) where ::A: Bound`. + // FIXME: once `#![feature(associated_type_bounds)]` is stabilized, we should suggest + // `fn foo(t: impl Trait)` instead. + err.multipart_suggestion( + "introduce a type parameter with a trait bound instead of using `impl Trait`", + sugg, + Applicability::MaybeIncorrect, + ); + } else { + // Trivial case: `T` needs an extra bound: `T: Bound`. + let (sp, suggestion) = match super_traits { + None => { + predicate_constraint(generics, trait_ref.without_const().to_predicate().to_string()) + } + Some((ident, bounds)) => match bounds { + [.., bound] => ( + bound.span().shrink_to_hi(), + format!(" + {}", trait_ref.print_only_trait_path().to_string()), + ), + [] => ( + ident.span.shrink_to_hi(), + format!(": {}", trait_ref.print_only_trait_path().to_string()), + ), + }, + }; + + err.span_suggestion_verbose( + sp, + &format!("consider further restricting {}", msg), + suggestion, + Applicability::MachineApplicable, + ); + } } impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn suggest_restricting_param_bound( &self, mut err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::PolyTraitRef<'_>, + trait_ref: ty::PolyTraitRef<'_>, body_id: hir::HirId, ) { let self_ty = trait_ref.self_ty(); @@ -159,110 +323,102 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => return, }; - let suggest_restriction = - |generics: &hir::Generics<'_>, msg, err: &mut DiagnosticBuilder<'_>| { - let span = generics.where_clause.span_for_predicates_or_empty_place(); - if !span.from_expansion() && span.desugaring_kind().is_none() { - err.span_suggestion( - generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(), - &format!("consider further restricting {}", msg), - format!( - "{} {} ", - if !generics.where_clause.predicates.is_empty() { - "," - } else { - " where" - }, - trait_ref.without_const().to_predicate(), - ), - Applicability::MachineApplicable, - ); - } - }; - // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we // don't suggest `T: Sized + ?Sized`. let mut hir_id = body_id; while let Some(node) = self.tcx.hir().find(hir_id) { match node { + hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Trait(_, _, generics, bounds, _), + .. + }) if self_ty == self.tcx.types.self_param => { + assert!(param_ty); + // Restricting `Self` for a single method. + suggest_restriction( + &generics, + "`Self`", + err, + None, + projection, + trait_ref, + Some((ident, bounds)), + ); + return; + } + hir::Node::TraitItem(hir::TraitItem { generics, kind: hir::TraitItemKind::Fn(..), .. - }) if param_ty && self_ty == self.tcx.types.self_param => { + }) if self_ty == self.tcx.types.self_param => { + assert!(param_ty); // Restricting `Self` for a single method. - suggest_restriction(&generics, "`Self`", err); + suggest_restriction( + &generics, "`Self`", err, None, projection, trait_ref, None, + ); return; } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) - | hir::Node::TraitItem(hir::TraitItem { + hir::Node::TraitItem(hir::TraitItem { generics, - kind: hir::TraitItemKind::Fn(..), + kind: hir::TraitItemKind::Fn(fn_sig, ..), .. }) | hir::Node::ImplItem(hir::ImplItem { generics, - kind: hir::ImplItemKind::Fn(..), + kind: hir::ImplItemKind::Fn(fn_sig, ..), .. }) | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, generics, _, _), + kind: hir::ItemKind::Fn(fn_sig, generics, _), .. + }) if projection.is_some() => { + // Missing restriction on associated type of type parameter (unmet projection). + suggest_restriction( + &generics, + "the associated type", + err, + Some(fn_sig), + projection, + trait_ref, + None, + ); + return; + } + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Trait(_, _, generics, _, _) + | hir::ItemKind::Impl { generics, .. }, .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { generics, .. }, .. }) if projection.is_some() => { - // Missing associated type bound. - suggest_restriction(&generics, "the associated type", err); + // Missing restriction on associated type of type parameter (unmet projection). + suggest_restriction( + &generics, + "the associated type", + err, + None, + projection, + trait_ref, + None, + ); return; } hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, generics), - span, + kind: + hir::ItemKind::Struct(_, generics) + | hir::ItemKind::Enum(_, generics) + | hir::ItemKind::Union(_, generics) + | hir::ItemKind::Trait(_, _, generics, ..) + | hir::ItemKind::Impl { generics, .. } + | hir::ItemKind::Fn(_, generics, _) + | hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::TraitAlias(generics, _) + | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), .. }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Enum(_, generics), span, .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Union(_, generics), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, generics, ..), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { generics, .. }, - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(_, generics, _), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::TyAlias(_, generics), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::TraitAlias(generics, _), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), - span, - .. - }) - | hir::Node::TraitItem(hir::TraitItem { generics, span, .. }) - | hir::Node::ImplItem(hir::ImplItem { generics, span, .. }) + | hir::Node::TraitItem(hir::TraitItem { generics, .. }) + | hir::Node::ImplItem(hir::ImplItem { generics, .. }) if param_ty => { // Missing generic type parameter bound. @@ -274,8 +430,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &mut err, ¶m_name, &constraint, - self.tcx.sess.source_map(), - *span, Some(trait_ref.def_id()), ) { return; @@ -342,7 +496,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; let hir = self.tcx.hir(); - let hir_id = hir.as_local_hir_id(def_id)?; + let hir_id = hir.as_local_hir_id(def_id.as_local()?); let parent_node = hir.get_parent_node(hir_id); match hir.find(parent_node) { Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { @@ -381,9 +535,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); match self.evaluate_obligation(&obligation) { - Ok(EvaluationResult::EvaluatedToOk) - | Ok(EvaluationResult::EvaluatedToOkModuloRegions) - | Ok(EvaluationResult::EvaluatedToAmbig) => {} + Ok( + EvaluationResult::EvaluatedToOk + | EvaluationResult::EvaluatedToOkModuloRegions + | EvaluationResult::EvaluatedToAmbig, + ) => {} _ => return, } let hir = self.tcx.hir(); @@ -599,6 +755,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { + if region.is_late_bound() || t_type.has_escaping_bound_vars() { + // Avoid debug assertion in `mk_obligation_for_def_id`. + // + // If the self type has escaping bound vars then it's not + // going to be the type of an expression, so the suggestion + // probably won't apply anyway. + return; + } + let trait_type = match mutability { hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type), hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type), @@ -671,6 +836,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } + fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option { + let hir = self.tcx.hir(); + let parent_node = hir.get_parent_node(obligation.cause.body_id); + let sig = match hir.find(parent_node) { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) => sig, + _ => return None, + }; + + if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { Some(ret_ty.span) } else { None } + } + /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if /// applicable and signal that the error has been expanded appropriately and needs to be /// emitted. @@ -737,12 +913,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .iter() .filter_map(|expr| tables.node_type_opt(expr.hir_id)) .map(|ty| self.resolve_vars_if_possible(&ty)); - let (last_ty, all_returns_have_same_type) = ret_types.clone().fold( - (None, true), - |(last_ty, mut same): (std::option::Option>, bool), ty| { + let (last_ty, all_returns_have_same_type, only_never_return) = ret_types.clone().fold( + (None, true, true), + |(last_ty, mut same, only_never_return): (std::option::Option>, bool, bool), + ty| { let ty = self.resolve_vars_if_possible(&ty); - same &= last_ty.map_or(true, |last_ty| last_ty == ty) && ty.kind != ty::Error; - (Some(ty), same) + same &= + ty.kind != ty::Error + && last_ty.map_or(true, |last_ty| { + // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes + // *after* in the dependency graph. + match (&ty.kind, &last_ty.kind) { + (Infer(InferTy::IntVar(_)), Infer(InferTy::IntVar(_))) + | (Infer(InferTy::FloatVar(_)), Infer(InferTy::FloatVar(_))) + | (Infer(InferTy::FreshIntTy(_)), Infer(InferTy::FreshIntTy(_))) + | ( + Infer(InferTy::FreshFloatTy(_)), + Infer(InferTy::FreshFloatTy(_)), + ) => true, + _ => ty == last_ty, + } + }); + (Some(ty), same, only_never_return && matches!(ty.kind, ty::Never)) }, ); let all_returns_conform_to_trait = @@ -751,13 +943,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Dynamic(predicates, _) => { let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); let param_env = ty::ParamEnv::empty(); - ret_types.all(|returned_ty| { - predicates.iter().all(|predicate| { - let pred = predicate.with_self_ty(self.tcx, returned_ty); - let obl = Obligation::new(cause.clone(), param_env, pred); - self.predicate_may_hold(&obl) + only_never_return + || ret_types.all(|returned_ty| { + predicates.iter().all(|predicate| { + let pred = predicate.with_self_ty(self.tcx, returned_ty); + let obl = Obligation::new(cause.clone(), param_env, pred); + self.predicate_may_hold(&obl) + }) }) - }) } _ => false, } @@ -765,21 +958,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { true }; - let (snippet, last_ty) = - if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( - // Verify that we're dealing with a return `dyn Trait` - ret_ty.span.overlaps(span), - &ret_ty.kind, - self.tcx.sess.source_map().span_to_snippet(ret_ty.span), - // If any of the return types does not conform to the trait, then we can't - // suggest `impl Trait` nor trait objects, it is a type mismatch error. - all_returns_conform_to_trait, - last_ty, - ) { - (snippet, last_ty) - } else { - return false; - }; + let sm = self.tcx.sess.source_map(); + let snippet = if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = ( + // Verify that we're dealing with a return `dyn Trait` + ret_ty.span.overlaps(span), + &ret_ty.kind, + sm.span_to_snippet(ret_ty.span), + // If any of the return types does not conform to the trait, then we can't + // suggest `impl Trait` nor trait objects: it is a type mismatch error. + all_returns_conform_to_trait, + ) { + snippet + } else { + return false; + }; err.code(error_code!(E0746)); err.set_primary_message("return type cannot have an unboxed trait object"); err.children.clear(); @@ -791,13 +983,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { #using-trait-objects-that-allow-for-values-of-different-types>"; let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; - if all_returns_have_same_type { + if only_never_return { + // No return paths, probably using `panic!()` or similar. + // Suggest `-> T`, `-> impl Trait`, and if `Trait` is object safe, `-> Box`. + suggest_trait_object_return_type_alternatives( + err, + ret_ty.span, + trait_obj, + is_object_safe, + ); + } else if let (Some(last_ty), true) = (last_ty, all_returns_have_same_type) { // Suggest `-> impl Trait`. err.span_suggestion( ret_ty.span, &format!( - "return `impl {1}` instead, as all return paths are of type `{}`, \ - which implements `{1}`", + "use `impl {1}` as the return type, as all return paths are of type `{}`, \ + which implements `{1}`", last_ty, trait_obj, ), format!("impl {}", trait_obj), @@ -808,26 +1009,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if is_object_safe { // Suggest `-> Box` and `Box::new(returned_value)`. // Get all the return values and collect their span and suggestion. - let mut suggestions = visitor + if let Some(mut suggestions) = visitor .returns .iter() .map(|expr| { - ( - expr.span, - format!( - "Box::new({})", - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap() - ), - ) + let snip = sm.span_to_snippet(expr.span).ok()?; + Some((expr.span, format!("Box::new({})", snip))) }) - .collect::>(); - // Add the suggestion for the return type. - suggestions.push((ret_ty.span, format!("Box", trait_obj))); - err.multipart_suggestion( - "return a boxed trait object instead", - suggestions, - Applicability::MaybeIncorrect, - ); + .collect::>>() + { + // Add the suggestion for the return type. + suggestions.push((ret_ty.span, format!("Box", trait_obj))); + err.multipart_suggestion( + "return a boxed trait object instead", + suggestions, + Applicability::MaybeIncorrect, + ); + } } else { // This is currently not possible to trigger because E0038 takes precedence, but // leave it in for completeness in case anything changes in an earlier stage. @@ -838,8 +1036,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } err.note(trait_obj_msg); err.note(&format!( - "if all the returned values were of the same type you could use \ - `impl {}` as the return type", + "if all the returned values were of the same type you could use `impl {}` as the \ + return type", trait_obj, )); err.note(impl_trait_msg); @@ -947,7 +1145,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.note(&format!( "{}s cannot be accessed directly on a `trait`, they can only be \ accessed through a specific `impl`", - assoc_item.kind.suggestion_descr(), + assoc_item.kind.as_def_kind().descr(def_id) )); err.span_suggestion( span, @@ -962,7 +1160,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// Adds an async-await specific note to the diagnostic when the future does not implement /// an auto trait because of a captured type. /// - /// ```ignore (diagnostic) + /// ```text /// note: future does not implement `Qux` as this value is used across an await /// --> $DIR/issue-64130-3-other.rs:17:5 /// | @@ -977,12 +1175,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// When the diagnostic does not implement `Send` or `Sync` specifically, then the diagnostic /// is "replaced" with a different message and a more specific error. /// - /// ```ignore (diagnostic) + /// ```text /// error: future cannot be sent between threads safely /// --> $DIR/issue-64130-2-send.rs:21:5 /// | /// LL | fn is_send(t: T) { } - /// | ------- ---- required by this bound in `is_send` + /// | ---- required by this bound in `is_send` /// ... /// LL | is_send(bar()); /// | ^^^^^^^ future returned by `bar` is not send @@ -1011,7 +1209,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation.cause.span={:?}", obligation.predicate, obligation.cause.span ); - let source_map = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); // Attempt to detect an async-await error by looking at the obligation causes, looking // for a generator to be present. @@ -1036,8 +1234,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // - `BindingObligation` with `impl_send (Send requirement) // // The first obligation in the chain is the most useful and has the generator that captured - // the type. The last generator has information about where the bound was introduced. At - // least one generator should be present for this diagnostic to be modified. + // the type. The last generator (`outer_generator` below) has information about where the + // bound was introduced. At least one generator should be present for this diagnostic to be + // modified. let (mut trait_ref, mut target_ty) = match obligation.predicate { ty::Predicate::Trait(p, _) => { (Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty())) @@ -1045,12 +1244,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => (None, None), }; let mut generator = None; - let mut last_generator = None; + let mut outer_generator = None; let mut next_code = Some(&obligation.cause.code); while let Some(code) = next_code { debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code); match code { - ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) + ObligationCauseCode::DerivedObligation(derived_obligation) + | ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) | ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { let ty = derived_obligation.parent_trait_ref.self_ty(); debug!( @@ -1062,7 +1262,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { match ty.kind { ty::Generator(did, ..) => { generator = generator.or(Some(did)); - last_generator = Some(did); + outer_generator = Some(did); } ty::GeneratorWitness(..) => {} _ if generator.is_none() => { @@ -1094,7 +1294,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let span = self.tcx.def_span(generator_did); // Do not ICE on closure typeck (#66868). - if self.tcx.hir().as_local_hir_id(generator_did).is_none() { + if !generator_did.is_local() { return false; } @@ -1115,58 +1315,102 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let tables: &TypeckTables<'tcx> = match &in_progress_tables { Some(t) if t.hir_owner.map(|owner| owner.to_def_id()) == Some(generator_did_root) => t, _ => { - query_tables = self.tcx.typeck_tables_of(generator_did); + query_tables = self.tcx.typeck_tables_of(generator_did.expect_local()); &query_tables } }; + let generator_body = generator_did + .as_local() + .map(|def_id| hir.as_local_hir_id(def_id)) + .and_then(|hir_id| hir.maybe_body_owned_by(hir_id)) + .map(|body_id| hir.body(body_id)); + let mut visitor = AwaitsVisitor::default(); + if let Some(body) = generator_body { + visitor.visit_body(body); + } + debug!("maybe_note_obligation_cause_for_async_await: awaits = {:?}", visitor.awaits); + // Look for a type inside the generator interior that matches the target type to get // a span. let target_ty_erased = self.tcx.erase_regions(&target_ty); - let target_span = tables + let ty_matches = |ty| -> bool { + // Careful: the regions for types that appear in the + // generator interior are not generally known, so we + // want to erase them when comparing (and anyway, + // `Send` and other bounds are generally unaffected by + // the choice of region). When erasing regions, we + // also have to erase late-bound regions. This is + // because the types that appear in the generator + // interior generally contain "bound regions" to + // represent regions that are part of the suspended + // generator frame. Bound regions are preserved by + // `erase_regions` and so we must also call + // `erase_late_bound_regions`. + let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(ty)); + let ty_erased = self.tcx.erase_regions(&ty_erased); + let eq = ty::TyS::same_type(ty_erased, target_ty_erased); + debug!( + "maybe_note_obligation_cause_for_async_await: ty_erased={:?} \ + target_ty_erased={:?} eq={:?}", + ty_erased, target_ty_erased, eq + ); + eq + }; + + let mut interior_or_upvar_span = None; + let mut interior_extra_info = None; + + if let Some(upvars) = self.tcx.upvars(generator_did) { + interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| { + let upvar_ty = tables.node_type(*upvar_id); + let upvar_ty = self.resolve_vars_if_possible(&upvar_ty); + if ty_matches(&upvar_ty) { + Some(GeneratorInteriorOrUpvar::Upvar(upvar.span)) + } else { + None + } + }); + }; + + tables .generator_interior_types .iter() - .find(|ty::GeneratorInteriorTypeCause { ty, .. }| { - // Careful: the regions for types that appear in the - // generator interior are not generally known, so we - // want to erase them when comparing (and anyway, - // `Send` and other bounds are generally unaffected by - // the choice of region). When erasing regions, we - // also have to erase late-bound regions. This is - // because the types that appear in the generator - // interior generally contain "bound regions" to - // represent regions that are part of the suspended - // generator frame. Bound regions are preserved by - // `erase_regions` and so we must also call - // `erase_late_bound_regions`. - let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(*ty)); - let ty_erased = self.tcx.erase_regions(&ty_erased); - let eq = ty::TyS::same_type(ty_erased, target_ty_erased); - debug!( - "maybe_note_obligation_cause_for_async_await: ty_erased={:?} \ - target_ty_erased={:?} eq={:?}", - ty_erased, target_ty_erased, eq - ); - eq - }) - .map(|ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. }| { - (span, source_map.span_to_snippet(*span), scope_span, expr) + .find(|ty::GeneratorInteriorTypeCause { ty, .. }| ty_matches(ty)) + .map(|cause| { + // Check to see if any awaited expressions have the target type. + let from_awaited_ty = visitor + .awaits + .into_iter() + .map(|id| hir.expect_expr(id)) + .find(|await_expr| { + let ty = tables.expr_ty_adjusted(&await_expr); + debug!( + "maybe_note_obligation_cause_for_async_await: await_expr={:?}", + await_expr + ); + ty_matches(ty) + }) + .map(|expr| expr.span); + let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = + cause; + + interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span)); + interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty)); }); debug!( - "maybe_note_obligation_cause_for_async_await: target_ty={:?} \ - generator_interior_types={:?} target_span={:?}", - target_ty, tables.generator_interior_types, target_span + "maybe_note_obligation_cause_for_async_await: interior_or_upvar={:?} \ + generator_interior_types={:?}", + interior_or_upvar_span, tables.generator_interior_types ); - if let Some((target_span, Ok(snippet), scope_span, expr)) = target_span { + if let Some(interior_or_upvar_span) = interior_or_upvar_span { self.note_obligation_cause_for_async_await( err, - *target_span, - scope_span, - *expr, - snippet, - generator_did, - last_generator, + interior_or_upvar_span, + interior_extra_info, + generator_body, + outer_generator, trait_ref, target_ty, tables, @@ -1184,12 +1428,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn note_obligation_cause_for_async_await( &self, err: &mut DiagnosticBuilder<'_>, - target_span: Span, - scope_span: &Option, - expr: Option, - snippet: String, - first_generator: DefId, - last_generator: Option, + interior_or_upvar_span: GeneratorInteriorOrUpvar, + interior_extra_info: Option<(Option, Span, Option, Option)>, + inner_generator_body: Option<&hir::Body<'_>>, + outer_generator: Option, trait_ref: ty::TraitRef<'_>, target_ty: Ty<'tcx>, tables: &ty::TypeckTables<'_>, @@ -1198,25 +1440,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) { let source_map = self.tcx.sess.source_map(); - let is_async_fn = self - .tcx - .parent(first_generator) - .map(|parent_did| self.tcx.asyncness(parent_did)) - .map(|parent_asyncness| parent_asyncness == hir::IsAsync::Async) - .unwrap_or(false); - let is_async_move = self - .tcx - .hir() - .as_local_hir_id(first_generator) - .and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id)) - .map(|body_id| self.tcx.hir().body(body_id)) + let is_async = inner_generator_body .and_then(|body| body.generator_kind()) - .map(|generator_kind| match generator_kind { - hir::GeneratorKind::Async(..) => true, - _ => false, - }) + .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..))) .unwrap_or(false); - let await_or_yield = if is_async_fn || is_async_move { "await" } else { "yield" }; + let (await_or_yield, an_await_or_yield) = + if is_async { ("await", "an await") } else { ("yield", "a yield") }; + let future_or_generator = if is_async { "future" } else { "generator" }; // Special case the primary error message when send or sync is the trait that was // not implemented. @@ -1229,22 +1459,35 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.clear_code(); err.set_primary_message(format!( - "future cannot be {} between threads safely", - trait_verb + "{} cannot be {} between threads safely", + future_or_generator, trait_verb )); let original_span = err.span.primary_span().unwrap(); let mut span = MultiSpan::from_span(original_span); - let message = if let Some(name) = last_generator - .and_then(|generator_did| self.tcx.parent(generator_did)) - .and_then(|parent_did| hir.as_local_hir_id(parent_did)) - .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) - { - format!("future returned by `{}` is not {}", name, trait_name) - } else { - format!("future is not {}", trait_name) - }; + let message = outer_generator + .and_then(|generator_did| { + Some(match self.tcx.generator_kind(generator_did).unwrap() { + GeneratorKind::Gen => format!("generator is not {}", trait_name), + GeneratorKind::Async(AsyncGeneratorKind::Fn) => self + .tcx + .parent(generator_did) + .and_then(|parent_did| parent_did.as_local()) + .map(|parent_did| hir.as_local_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("future returned by `{}` is not {}", name, trait_name) + })?, + GeneratorKind::Async(AsyncGeneratorKind::Block) => { + format!("future created by async block is not {}", trait_name) + } + GeneratorKind::Async(AsyncGeneratorKind::Closure) => { + format!("future created by async closure is not {}", trait_name) + } + }) + }) + .unwrap_or_else(|| format!("{} is not {}", future_or_generator, trait_name)); span.push_span_label(original_span, message); err.set_span(span); @@ -1254,72 +1497,119 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { format!("does not implement `{}`", trait_ref.print_only_trait_path()) }; - // Look at the last interior type to get a span for the `.await`. - let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap(); - let mut span = MultiSpan::from_span(await_span); - span.push_span_label( - await_span, - format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet), - ); - - span.push_span_label(target_span, format!("has type `{}`", target_ty)); - - // If available, use the scope span to annotate the drop location. - if let Some(scope_span) = scope_span { + let mut explain_yield = |interior_span: Span, + yield_span: Span, + scope_span: Option| { + let mut span = MultiSpan::from_span(yield_span); + if let Ok(snippet) = source_map.span_to_snippet(interior_span) { + span.push_span_label( + yield_span, + format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet), + ); + // If available, use the scope span to annotate the drop location. + if let Some(scope_span) = scope_span { + span.push_span_label( + source_map.end_point(scope_span), + format!("`{}` is later dropped here", snippet), + ); + } + } span.push_span_label( - source_map.end_point(*scope_span), - format!("`{}` is later dropped here", snippet), + interior_span, + format!("has type `{}` which {}", target_ty, trait_explanation), ); - } - err.span_note( - span, - &format!( - "future {} as this value is used across an {}", - trait_explanation, await_or_yield, - ), - ); - - if let Some(expr_id) = expr { - let expr = hir.expect_expr(expr_id); - debug!("target_ty evaluated from {:?}", expr); - - let parent = hir.get_parent_node(expr_id); - if let Some(hir::Node::Expr(e)) = hir.find(parent) { - let parent_span = hir.span(parent); - let parent_did = parent.owner.to_def_id(); - // ```rust - // impl T { - // fn foo(&self) -> i32 {} - // } - // T.foo(); - // ^^^^^^^ a temporary `&T` created inside this method call due to `&self` - // ``` - // - let is_region_borrow = - tables.expr_adjustments(expr).iter().any(|adj| adj.is_region_borrow()); - - // ```rust - // struct Foo(*const u8); - // bar(Foo(std::ptr::null())).await; - // ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor. - // ``` - debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did)); - let is_raw_borrow_inside_fn_like_call = match self.tcx.def_kind(parent_did) { - Some(DefKind::Fn) | Some(DefKind::Ctor(..)) => target_ty.is_unsafe_ptr(), - _ => false, - }; + err.span_note( + span, + &format!( + "{} {} as this value is used across {}", + future_or_generator, trait_explanation, an_await_or_yield + ), + ); + }; + match interior_or_upvar_span { + GeneratorInteriorOrUpvar::Interior(interior_span) => { + if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info { + if let Some(await_span) = from_awaited_ty { + // The type causing this obligation is one being awaited at await_span. + let mut span = MultiSpan::from_span(await_span); + span.push_span_label( + await_span, + format!( + "await occurs here on type `{}`, which {}", + target_ty, trait_explanation + ), + ); + err.span_note( + span, + &format!( + "future {not_trait} as it awaits another future which {not_trait}", + not_trait = trait_explanation + ), + ); + } else { + // Look at the last interior type to get a span for the `.await`. + debug!( + "note_obligation_cause_for_async_await generator_interior_types: {:#?}", + tables.generator_interior_types + ); + explain_yield(interior_span, yield_span, scope_span); + } - if (tables.is_method_call(e) && is_region_borrow) - || is_raw_borrow_inside_fn_like_call - { - err.span_help( - parent_span, - "consider moving this into a `let` \ + if let Some(expr_id) = expr { + let expr = hir.expect_expr(expr_id); + debug!("target_ty evaluated from {:?}", expr); + + let parent = hir.get_parent_node(expr_id); + if let Some(hir::Node::Expr(e)) = hir.find(parent) { + let parent_span = hir.span(parent); + let parent_did = parent.owner.to_def_id(); + // ```rust + // impl T { + // fn foo(&self) -> i32 {} + // } + // T.foo(); + // ^^^^^^^ a temporary `&T` created inside this method call due to `&self` + // ``` + // + let is_region_borrow = tables + .expr_adjustments(expr) + .iter() + .any(|adj| adj.is_region_borrow()); + + // ```rust + // struct Foo(*const u8); + // bar(Foo(std::ptr::null())).await; + // ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor. + // ``` + debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did)); + let is_raw_borrow_inside_fn_like_call = + match self.tcx.def_kind(parent_did) { + DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(), + _ => false, + }; + + if (tables.is_method_call(e) && is_region_borrow) + || is_raw_borrow_inside_fn_like_call + { + err.span_help( + parent_span, + "consider moving this into a `let` \ binding to create a shorter lived borrow", - ); + ); + } + } + } } } + GeneratorInteriorOrUpvar::Upvar(upvar_span) => { + let mut span = MultiSpan::from_span(upvar_span); + span.push_span_label( + upvar_span, + format!("has type `{}` which {}", target_ty, trait_explanation), + ); + err.span_note(span, &format!("captured value {}", trait_explanation)); + } } // Add a note for the item obligation that remains - normally a note pointing to the @@ -1379,9 +1669,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::ItemObligation(item_def_id) => { let item_name = tcx.def_path_str(item_def_id); let msg = format!("required by `{}`", item_name); - if let Some(sp) = tcx.hir().span_if_local(item_def_id) { - let sp = tcx.sess.source_map().def_span(sp); + let sp = tcx.sess.source_map().guess_head_span(sp); err.span_label(sp, &msg); } else { err.note(&msg); @@ -1391,7 +1680,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let item_name = tcx.def_path_str(item_def_id); let msg = format!("required by this bound in `{}`", item_name); if let Some(ident) = tcx.opt_item_name(item_def_id) { - err.span_label(ident.span, ""); + let sm = tcx.sess.source_map(); + let same_line = + match (sm.lookup_line(ident.span.hi()), sm.lookup_line(span.lo())) { + (Ok(l), Ok(r)) => l.line == r.line, + _ => true, + }; + if !ident.span.overlaps(span) && !same_line { + err.span_label(ident.span, "required by a bound in this"); + } } if span != DUMMY_SP { err.span_label(span, &msg); @@ -1415,7 +1712,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if suggest_const_in_array_repeat_expressions { err.note( "this array initializer can be evaluated at compile-time, see issue \ - #48147 \ + #49147 \ for more information", ); if tcx.sess.opts.unstable_features.is_nightly_build() { @@ -1476,6 +1773,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::ConstSized => { err.note("constant expressions must have a statically known size"); } + ObligationCauseCode::InlineAsmSized => { + err.note("all inline asm arguments must have a statically known size"); + } ObligationCauseCode::ConstPatternStructural => { err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`"); } @@ -1513,6 +1813,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligated_types, ); } + ObligationCauseCode::DerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + let parent_predicate = parent_trait_ref.without_const().to_predicate(); + self.note_obligation_cause_code( + err, + &parent_predicate, + &data.parent_code, + obligated_types, + ); + } ObligationCauseCode::CompareImplMethodObligation { .. } => { err.note(&format!( "the requirement `{}` appears on the impl method \ @@ -1527,6 +1837,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { predicate )); } + ObligationCauseCode::CompareImplConstObligation => { + err.note(&format!( + "the requirement `{}` appears on the associated impl constant \ + but not on the corresponding associated trait constant", + predicate + )); + } ObligationCauseCode::ReturnType | ObligationCauseCode::ReturnValue(_) | ObligationCauseCode::BlockTailExpression(_) => (), @@ -1536,15 +1853,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable"); } } - ObligationCauseCode::AssocTypeBound(ref data) => { - err.span_label(data.original, "associated type defined here"); - if let Some(sp) = data.impl_span { - err.span_label(sp, "in this `impl` item"); - } - for sp in &data.bounds { - err.span_label(*sp, "restricted in this bound"); - } - } } } @@ -1556,6 +1864,95 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { suggested_limit, self.tcx.crate_name, )); } + + fn suggest_await_before_try( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + trait_ref: &ty::Binder>, + span: Span, + ) { + debug!( + "suggest_await_befor_try: obligation={:?}, span={:?}, trait_ref={:?}, trait_ref_self_ty={:?}", + obligation, + span, + trait_ref, + trait_ref.self_ty() + ); + let body_hir_id = obligation.cause.body_id; + let item_id = self.tcx.hir().get_parent_node(body_hir_id); + + if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) { + let body = self.tcx.hir().body(body_id); + if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { + let future_trait = + self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None); + + let self_ty = self.resolve_vars_if_possible(&trait_ref.self_ty()); + + let impls_future = self.tcx.type_implements_trait(( + future_trait, + self_ty, + ty::List::empty(), + obligation.param_env, + )); + + let item_def_id = self + .tcx + .associated_items(future_trait) + .in_definition_order() + .next() + .unwrap() + .def_id; + // `::Output` + let projection_ty = ty::ProjectionTy { + // `T` + substs: self.tcx.mk_substs_trait( + trait_ref.self_ty(), + self.fresh_substs_for_item(span, item_def_id), + ), + // `Future::Output` + item_def_id, + }; + + let mut selcx = SelectionContext::new(self); + + let mut obligations = vec![]; + let normalized_ty = normalize_projection_type( + &mut selcx, + obligation.param_env, + projection_ty, + obligation.cause.clone(), + 0, + &mut obligations, + ); + + debug!( + "suggest_await_befor_try: normalized_projection_type {:?}", + self.resolve_vars_if_possible(&normalized_ty) + ); + let try_obligation = self.mk_obligation_for_def_id( + trait_ref.def_id(), + normalized_ty, + obligation.cause.clone(), + obligation.param_env, + ); + debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation); + if self.predicate_may_hold(&try_obligation) && impls_future { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + if snippet.ends_with('?') { + err.span_suggestion( + span, + "consider using `.await` here", + format!("{}.await?", snippet.trim_end_matches('?')), + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + } } /// Collect all the returned expressions within the input expression. @@ -1615,3 +2012,86 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> { hir::intravisit::walk_body(self, body); } } + +/// Collect all the awaited expressions within the input expression. +#[derive(Default)] +struct AwaitsVisitor { + awaits: Vec, +} + +impl<'v> Visitor<'v> for AwaitsVisitor { + type Map = hir::intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) = ex.kind { + self.awaits.push(id) + } + hir::intravisit::walk_expr(self, ex) + } +} + +pub trait NextTypeParamName { + fn next_type_param_name(&self, name: Option<&str>) -> String; +} + +impl NextTypeParamName for &[hir::GenericParam<'_>] { + fn next_type_param_name(&self, name: Option<&str>) -> String { + // This is the whitelist of possible parameter names that we might suggest. + let name = name.and_then(|n| n.chars().next()).map(|c| c.to_string().to_uppercase()); + let name = name.as_deref(); + let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"]; + let used_names = self + .iter() + .filter_map(|p| match p.name { + hir::ParamName::Plain(ident) => Some(ident.name), + _ => None, + }) + .collect::>(); + + possible_names + .iter() + .find(|n| !used_names.contains(&Symbol::intern(n))) + .unwrap_or(&"ParamName") + .to_string() + } +} + +fn suggest_trait_object_return_type_alternatives( + err: &mut DiagnosticBuilder<'tcx>, + ret_ty: Span, + trait_obj: &str, + is_object_safe: bool, +) { + err.span_suggestion( + ret_ty, + "use some type `T` that is `T: Sized` as the return type if all return paths have the \ + same type", + "T".to_string(), + Applicability::MaybeIncorrect, + ); + err.span_suggestion( + ret_ty, + &format!( + "use `impl {}` as the return type if all return paths have the same type but you \ + want to expose only the trait in the signature", + trait_obj, + ), + format!("impl {}", trait_obj), + Applicability::MaybeIncorrect, + ); + if is_object_safe { + err.span_suggestion( + ret_ty, + &format!( + "use a boxed trait object if all return paths implement trait `{}`", + trait_obj, + ), + format!("Box", trait_obj), + Applicability::MaybeIncorrect, + ); + } +} diff --git a/src/librustc_trait_selection/traits/fulfill.rs b/src/librustc_trait_selection/traits/fulfill.rs index 0578c00fefb17..98f6ac0e54728 100644 --- a/src/librustc_trait_selection/traits/fulfill.rs +++ b/src/librustc_trait_selection/traits/fulfill.rs @@ -1,10 +1,12 @@ use crate::infer::{InferCtxt, TyOrConstInferVar}; -use rustc::ty::error::ExpectedFound; -use rustc::ty::{self, ToPolyTraitRef, Ty, TypeFoldable}; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_errors::ErrorReported; use rustc_infer::traits::{TraitEngine, TraitEngineExt as _}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::{self, Const, ToPolyTraitRef, Ty, TypeFoldable}; use std::marker::PhantomData; use super::project; @@ -240,9 +242,15 @@ struct FulfillProcessor<'a, 'b, 'tcx> { register_region_obligations: bool, } -fn mk_pending(os: Vec>) -> Vec> { +fn mk_pending( + infcx: &InferCtxt<'_, 'tcx>, + os: Vec>, +) -> Vec> { os.into_iter() - .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) + .map(|mut o| { + o.predicate = infcx.resolve_vars_if_possible(&o.predicate); + PendingPredicateObligation { obligation: o, stalled_on: vec![] } + }) .collect() } @@ -312,14 +320,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); + let infcx = self.selcx.infcx(); + match obligation.predicate { ty::Predicate::Trait(ref data, _) => { - let trait_obligation = obligation.with(data.clone()); + let trait_obligation = obligation.with(*data); if data.is_global() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. - if self.selcx.infcx().predicate_must_hold_considering_regions(&obligation) { + if infcx.predicate_must_hold_considering_regions(&obligation) { debug!( "selecting trait `{:?}` at depth {} evaluated to holds", data, obligation.recursion_depth @@ -334,7 +344,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { "selecting trait `{:?}` at depth {} yielded Ok(Some)", data, obligation.recursion_depth ); - ProcessResult::Changed(mk_pending(vtable.nested_obligations())) + ProcessResult::Changed(mk_pending(infcx, vtable.nested_obligations())) } Ok(None) => { debug!( @@ -351,7 +361,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { debug!( "process_predicate: pending obligation {:?} now stalled on {:?}", - self.selcx.infcx().resolve_vars_if_possible(obligation), + infcx.resolve_vars_if_possible(obligation), pending_obligation.stalled_on ); @@ -369,7 +379,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::Predicate::RegionOutlives(ref binder) => { - match self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder) { + match infcx.region_outlives_predicate(&obligation.cause, binder) { Ok(()) => ProcessResult::Changed(vec![]), Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), } @@ -420,7 +430,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::Predicate::Projection(ref data) => { - let project_obligation = obligation.with(data.clone()); + let project_obligation = obligation.with(*data); match project::poly_project_and_unify_type(self.selcx, &project_obligation) { Ok(None) => { let tcx = self.selcx.tcx(); @@ -428,7 +438,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { trait_ref_type_vars(self.selcx, data.to_poly_trait_ref(tcx)); ProcessResult::Unchanged } - Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)), + Ok(Some(os)) => ProcessResult::Changed(mk_pending(infcx, os)), Err(e) => ProcessResult::Error(CodeProjectionError(e)), } } @@ -467,7 +477,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { vec![TyOrConstInferVar::maybe_from_ty(ty).unwrap()]; ProcessResult::Unchanged } - Some(os) => ProcessResult::Changed(mk_pending(os)), + Some(os) => ProcessResult::Changed(mk_pending(infcx, os)), } } @@ -485,7 +495,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { ]; ProcessResult::Unchanged } - Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Some(Ok(ok)) => ProcessResult::Changed(mk_pending(infcx, ok.obligations)), Some(Err(err)) => { let expected_found = ExpectedFound::new( subtype.skip_binder().a_is_expected, @@ -512,6 +522,68 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), } } + + ty::Predicate::ConstEquate(c1, c2) => { + debug!("equating consts: c1={:?} c2={:?}", c1, c2); + + let stalled_on = &mut pending_obligation.stalled_on; + + let mut evaluate = |c: &'tcx Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val { + match self.selcx.infcx().const_eval_resolve( + obligation.param_env, + def_id, + substs, + promoted, + Some(obligation.cause.span), + ) { + Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)), + Err(ErrorHandled::TooGeneric) => { + stalled_on.append( + &mut substs + .types() + .filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty)) + .collect(), + ); + Err(ErrorHandled::TooGeneric) + } + Err(err) => Err(err), + } + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match self + .selcx + .infcx() + .at(&obligation.cause, obligation.param_env) + .eq(c1, c2) + { + Ok(_) => ProcessResult::Changed(vec![]), + Err(err) => { + ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError( + ExpectedFound::new(true, c1, c2), + err, + )) + } + } + } + (Err(ErrorHandled::Reported(ErrorReported)), _) + | (_, Err(ErrorHandled::Reported(ErrorReported))) => ProcessResult::Error( + CodeSelectionError(ConstEvalFailure(ErrorHandled::Reported(ErrorReported))), + ), + (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!( + obligation.cause.span(self.selcx.tcx()), + "ConstEquate: const_eval_resolve returned an unexpected error" + ), + (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { + ProcessResult::Unchanged + } + } + } } } @@ -536,18 +608,17 @@ fn trait_ref_type_vars<'a, 'tcx>( selcx: &mut SelectionContext<'a, 'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, ) -> Vec> { - trait_ref + selcx + .infcx() + .resolve_vars_if_possible(&trait_ref) .skip_binder() // ok b/c this check doesn't care about regions - // FIXME(eddyb) walk over `GenericArg` to support const infer vars. - .input_types() - .map(|ty| selcx.infcx().resolve_vars_if_possible(&ty)) - // FIXME(eddyb) try using `maybe_walk` to skip *all* subtrees that - // don't contain inference variables, not just the outermost level. - // FIXME(eddyb) use `has_infer_types_or_const`. - .filter(|ty| ty.has_infer_types()) - .flat_map(|ty| ty.walk()) - // FIXME(eddyb) use `TyOrConstInferVar::maybe_from_generic_arg`. - .filter_map(TyOrConstInferVar::maybe_from_ty) + .substs + .iter() + // FIXME(eddyb) try using `skip_current_subtree` to skip everything that + // doesn't contain inference variables, not just the outermost level. + .filter(|arg| arg.has_infer_types_or_consts()) + .flat_map(|arg| arg.walk()) + .filter_map(TyOrConstInferVar::maybe_from_generic_arg) .collect() } diff --git a/src/librustc_trait_selection/traits/misc.rs b/src/librustc_trait_selection/traits/misc.rs index d500cff67c64b..7403b936391cc 100644 --- a/src/librustc_trait_selection/traits/misc.rs +++ b/src/librustc_trait_selection/traits/misc.rs @@ -3,9 +3,9 @@ use crate::infer::InferCtxtExt as _; use crate::traits::{self, ObligationCause}; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use crate::traits::error_reporting::InferCtxtExt; diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index 43a90c4a6c164..9592f93ce2e76 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -4,6 +4,7 @@ #[allow(dead_code)] pub mod auto_trait; +mod chalk_fulfill; pub mod codegen; mod coherence; mod engine; @@ -24,14 +25,16 @@ use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{InferCtxt, RegionckMode, TyCtxtInferExt}; use crate::traits::error_reporting::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc::middle::region; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::{InternalSubsts, SubstsRef}; -use rustc::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, WithConstness}; -use rustc::util::common::ErrorReported; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_span::{Span, DUMMY_SP}; +use rustc_middle::middle::region; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::{ + self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness, +}; +use rustc_span::Span; use std::fmt::Debug; @@ -54,7 +57,6 @@ pub use self::project::{ }; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; -pub use self::specialize::find_associated_item; pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; @@ -64,13 +66,14 @@ pub use self::structural_match::NonStructuralMatchTy; pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{ - get_vtable_index_of_object_method, impl_is_default, impl_item_is_final, - predicate_for_trait_def, upcast_choices, + get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, }; pub use self::util::{ supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, }; +pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext; + pub use rustc_infer::traits::*; /// Whether to skip the leak check, as part of a future compatibility warning step. @@ -112,8 +115,8 @@ pub enum TraitQueryMode { pub fn predicates_for_generics<'tcx>( cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - generic_bounds: &ty::InstantiatedPredicates<'tcx>, -) -> PredicateObligations<'tcx> { + generic_bounds: ty::InstantiatedPredicates<'tcx>, +) -> impl Iterator> { util::predicates_for_generics(cause, 0, param_env, generic_bounds) } @@ -138,7 +141,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) }; let obligation = Obligation { param_env, - cause: ObligationCause::misc(span, hir::DUMMY_HIR_ID), + cause: ObligationCause::misc(span, hir::CRATE_HIR_ID), recursion_depth: 0, predicate: trait_ref.without_const().to_predicate(), }; @@ -165,7 +168,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( // We can use a dummy node-id here because we won't pay any mind // to region obligations that arise (there shouldn't really be any // anyhow). - let cause = ObligationCause::misc(span, hir::DUMMY_HIR_ID); + let cause = ObligationCause::misc(span, hir::CRATE_HIR_ID); fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause); @@ -261,8 +264,8 @@ fn do_normalize_predicates<'tcx>( return Err(ErrorReported); } }; - if predicates.has_local_value() { - // FIXME: shouldn't we, you know, actually report an error here? or an ICE? + if predicates.needs_infer() { + tcx.sess.delay_span_bug(span, "encountered inference variables after `fully_resolve`"); Err(ErrorReported) } else { Ok(predicates) @@ -299,7 +302,9 @@ pub fn normalize_param_env_or_error<'tcx>( ); let mut predicates: Vec<_> = - util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec()).collect(); + util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.into_iter().cloned()) + .map(|obligation| obligation.predicate) + .collect(); debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); @@ -475,7 +480,7 @@ fn vtable_methods<'tcx>( let trait_methods = tcx .associated_items(trait_ref.def_id()) .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Method); + .filter(|item| item.kind == ty::AssocKind::Fn); // Now list each method's DefId and InternalSubsts (for within its trait). // If the method can never be called from this object, produce None. @@ -520,6 +525,43 @@ fn vtable_methods<'tcx>( })) } +/// Check whether a `ty` implements given trait(trait_def_id). +/// +/// NOTE: Always return `false` for a type which needs inference. +fn type_implements_trait<'tcx>( + tcx: TyCtxt<'tcx>, + key: ( + DefId, // trait_def_id, + Ty<'tcx>, // type + SubstsRef<'tcx>, + ParamEnv<'tcx>, + ), +) -> bool { + let (trait_def_id, ty, params, param_env) = key; + + debug!( + "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}", + trait_def_id, ty, params, param_env + ); + + // Do not check on infer_types to avoid panic in evaluate_obligation. + if ty.has_infer_types() { + return false; + } + + let ty = tcx.erase_regions(&ty); + + let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) }; + + let obligation = Obligation { + cause: ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.without_const().to_predicate(), + }; + tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) +} + pub fn provide(providers: &mut ty::query::Providers<'_>) { object_safety::provide(providers); *providers = ty::query::Providers { @@ -528,6 +570,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, vtable_methods, substitute_normalize_and_test_predicates, + type_implements_trait, ..*providers }; } diff --git a/src/librustc_trait_selection/traits/object_safety.rs b/src/librustc_trait_selection/traits/object_safety.rs index 5cc1da045fc37..1bfcacd6ccdc1 100644 --- a/src/librustc_trait_selection/traits/object_safety.rs +++ b/src/librustc_trait_selection/traits/object_safety.rs @@ -13,11 +13,12 @@ use super::elaborate_predicates; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, FatalError}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness}; +use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -47,13 +48,17 @@ pub fn astconv_object_safety_violations( violations } -fn object_safety_violations(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Vec { +fn object_safety_violations( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, +) -> &'tcx [ObjectSafetyViolation] { debug_assert!(tcx.generics_of(trait_def_id).has_self); debug!("object_safety_violations: {:?}", trait_def_id); - traits::supertrait_def_ids(tcx, trait_def_id) - .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)) - .collect() + tcx.arena.alloc_from_iter( + traits::supertrait_def_ids(tcx, trait_def_id) + .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)), + ) } /// We say a method is *vtable safe* if it can be invoked on a trait @@ -82,7 +87,7 @@ fn object_safety_violations_for_trait( let mut violations: Vec<_> = tcx .associated_items(trait_def_id) .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Method) + .filter(|item| item.kind == ty::AssocKind::Fn) .filter_map(|item| { object_safety_violation_for_method(tcx, trait_def_id, &item) .map(|(code, span)| ObjectSafetyViolation::Method(item.ident.name, code, span)) @@ -170,6 +175,24 @@ fn object_safety_violations_for_trait( violations } +fn sized_trait_bound_spans<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: hir::GenericBounds<'tcx>, +) -> impl 'tcx + Iterator { + bounds.iter().filter_map(move |b| match b { + hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None) + if trait_has_sized_self( + tcx, + trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), + ) => + { + // Fetch spans for supertraits that are `Sized`: `trait T: Super` + Some(trait_ref.span) + } + _ => None, + }) +} + fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { tcx.hir() .get_if_local(trait_def_id) @@ -189,33 +212,14 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { // Fetch spans for trait bounds that are Sized: // `trait T where Self: Pred` - Some(pred.bounds.iter().filter_map(|b| match b { - hir::GenericBound::Trait( - trait_ref, - hir::TraitBoundModifier::None, - ) if trait_has_sized_self( - tcx, - trait_ref.trait_ref.trait_def_id(), - ) => - { - Some(trait_ref.span) - } - _ => None, - })) + Some(sized_trait_bound_spans(tcx, pred.bounds)) } _ => None, } }) .flatten() - .chain(bounds.iter().filter_map(|b| match b { - hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None) - if trait_has_sized_self(tcx, trait_ref.trait_ref.trait_def_id()) => - { - // Fetch spans for supertraits that are `Sized`: `trait T: Super` - Some(trait_ref.span) - } - _ => None, - })) + // Fetch spans for supertraits that are `Sized`: `trait T: Super`. + .chain(sized_trait_bound_spans(tcx, bounds)) .collect::>(), ), _ => None, @@ -235,7 +239,7 @@ fn predicates_reference_self( tcx.predicates_of(trait_def_id) }; let self_ty = tcx.types.self_param; - let has_self_ty = |t: Ty<'_>| t.walk().any(|t| t == self_ty); + let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); predicates .predicates .iter() @@ -244,7 +248,7 @@ fn predicates_reference_self( match predicate { ty::Predicate::Trait(ref data, _) => { // In the case of a trait predicate, we can skip the "self" type. - if data.skip_binder().input_types().skip(1).any(has_self_ty) { + if data.skip_binder().trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None @@ -263,12 +267,8 @@ fn predicates_reference_self( // // This is ALT2 in issue #56288, see that for discussion of the // possible alternatives. - if data - .skip_binder() - .projection_ty - .trait_ref(tcx) - .input_types() - .skip(1) + if data.skip_binder().projection_ty.trait_ref(tcx).substs[1..] + .iter() .any(has_self_ty) { Some(sp) @@ -282,7 +282,8 @@ fn predicates_reference_self( | ty::Predicate::RegionOutlives(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::Subtype(..) - | ty::Predicate::ConstEvaluatable(..) => None, + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => None, } }) .collect() @@ -303,7 +304,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // Search for a predicate like `Self : Sized` amongst the trait bounds. let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; - elaborate_predicates(tcx, predicates).any(|predicate| match predicate { + elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| match obligation.predicate { ty::Predicate::Trait(ref trait_pred, _) => { trait_pred.def_id() == sized_def_id && trait_pred.skip_binder().self_ty().is_param(0) } @@ -314,7 +315,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => false, + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => false, }) } @@ -363,7 +365,7 @@ fn virtual_call_violation_for_method<'tcx>( method: &ty::AssocItem, ) -> Option { // The method's first parameter must be named `self` - if !method.method_has_self_argument { + if !method.fn_has_self_parameter { // We'll attempt to provide a structured suggestion for `Self: Sized`. let sugg = tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map( @@ -421,7 +423,7 @@ fn virtual_call_violation_for_method<'tcx>( } else { // Do sanity check to make sure the receiver actually has the layout of a pointer. - use rustc::ty::layout::Abi; + use rustc_target::abi::Abi; let param_env = tcx.param_env(method.def_id); @@ -617,7 +619,7 @@ fn receiver_is_dispatchable<'tcx>( // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can // replace this with `dyn Trait` let unsized_self_ty: Ty<'tcx> = - tcx.mk_ty_param(::std::u32::MAX, Symbol::intern("RustaceansAreAwesome")); + tcx.mk_ty_param(u32::MAX, Symbol::intern("RustaceansAreAwesome")); // `Receiver[Self => U]` let unsized_receiver_ty = @@ -725,52 +727,65 @@ fn contains_illegal_self_type_reference<'tcx>( // object type, and we cannot resolve `Self as SomeOtherTrait` // without knowing what `Self` is. - let mut supertraits: Option>> = None; - let mut error = false; - let self_ty = tcx.types.self_param; - ty.maybe_walk(|ty| { - match ty.kind { - ty::Param(_) => { - if ty == self_ty { - error = true; - } - - false // no contained types to walk - } - - ty::Projection(ref data) => { - // This is a projected type `::X`. + struct IllegalSelfTypeVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + self_ty: Ty<'tcx>, + trait_def_id: DefId, + supertraits: Option>>, + } - // Compute supertraits of current trait lazily. - if supertraits.is_none() { - let trait_ref = ty::Binder::bind(ty::TraitRef::identity(tcx, trait_def_id)); - supertraits = Some(traits::supertraits(tcx, trait_ref).collect()); - } + impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match t.kind { + ty::Param(_) => t == self.self_ty, + ty::Projection(ref data) => { + // This is a projected type `::X`. + + // Compute supertraits of current trait lazily. + if self.supertraits.is_none() { + let trait_ref = + ty::Binder::bind(ty::TraitRef::identity(self.tcx, self.trait_def_id)); + self.supertraits = Some(traits::supertraits(self.tcx, trait_ref).collect()); + } - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let projection_trait_ref = ty::Binder::bind(data.trait_ref(tcx)); - let is_supertrait_of_current_trait = - supertraits.as_ref().unwrap().contains(&projection_trait_ref); - - if is_supertrait_of_current_trait { - false // do not walk contained types, do not report error, do collect $200 - } else { - true // DO walk contained types, POSSIBLY reporting an error + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let projection_trait_ref = ty::Binder::bind(data.trait_ref(self.tcx)); + let is_supertrait_of_current_trait = + self.supertraits.as_ref().unwrap().contains(&projection_trait_ref); + + if is_supertrait_of_current_trait { + false // do not walk contained types, do not report error, do collect $200 + } else { + t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error + } } + _ => t.super_visit_with(self), // walk contained types, if any } + } - _ => true, // walk contained types, if any + fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool { + // FIXME(#72219) Look into the unevaluated constants for object safety violations. + // Do not walk substitutions of unevaluated consts, as they contain `Self`, even + // though the const expression doesn't necessary use it. Currently type variables + // inside array length expressions are forbidden, so they can't break the above + // rules. + false } - }); + } - error + ty.visit_with(&mut IllegalSelfTypeVisitor { + tcx, + self_ty: tcx.types.self_param, + trait_def_id, + supertraits: None, + }) } pub fn provide(providers: &mut ty::query::Providers<'_>) { diff --git a/src/librustc_trait_selection/traits/on_unimplemented.rs b/src/librustc_trait_selection/traits/on_unimplemented.rs index 19260293ee627..7e66e08f7e6a6 100644 --- a/src/librustc_trait_selection/traits/on_unimplemented.rs +++ b/src/librustc_trait_selection/traits/on_unimplemented.rs @@ -1,13 +1,11 @@ -use fmt_macros::{Parser, Piece, Position}; - -use rustc::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc::util::common::ErrorReported; +use fmt_macros::{ParseMode, Parser, Piece, Position}; use rustc_ast::ast::{MetaItem, NestedMetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, ErrorReported}; use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; @@ -83,7 +81,7 @@ impl<'tcx> OnUnimplementedDirective { None, ) })?; - attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true); + attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true); Some(cond.clone()) }; @@ -210,11 +208,16 @@ impl<'tcx> OnUnimplementedDirective { for command in self.subcommands.iter().chain(Some(self)).rev() { if let Some(ref condition) = command.condition { - if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| { - c.ident().map_or(false, |ident| { - options.contains(&(ident.name, c.value_str().map(|s| s.to_string()))) - }) - }) { + if !attr::eval_condition( + condition, + &tcx.sess.parse_sess, + Some(tcx.features()), + &mut |c| { + c.ident().map_or(false, |ident| { + options.contains(&(ident.name, c.value_str().map(|s| s.to_string()))) + }) + }, + ) { debug!("evaluate: skipping {:?} due to condition", command); continue; } @@ -269,7 +272,7 @@ impl<'tcx> OnUnimplementedFormatString { let name = tcx.item_name(trait_def_id); let generics = tcx.generics_of(trait_def_id); let s = self.0.as_str(); - let parser = Parser::new(&s, None, vec![], false); + let parser = Parser::new(&s, None, None, false, ParseMode::Format); let mut result = Ok(()); for token in parser { match token { @@ -347,7 +350,7 @@ impl<'tcx> OnUnimplementedFormatString { let empty_string = String::new(); let s = self.0.as_str(); - let parser = Parser::new(&s, None, vec![], false); + let parser = Parser::new(&s, None, None, false, ParseMode::Format); let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string); parser .map(|p| match p { diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs index 26aaf12d359d9..f102f34c744de 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -12,21 +12,26 @@ use super::Selection; use super::SelectionContext; use super::SelectionError; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; -use super::{VtableClosureData, VtableFnPointerData, VtableGeneratorData, VtableImplData}; +use super::{ + VtableClosureData, VtableDiscriminantKindData, VtableFnPointerData, VtableGeneratorData, + VtableImplData, +}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::InferCtxtExt; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; -use rustc_ast::ast::Ident; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; -use rustc_span::symbol::sym; +use rustc_hir::lang_items::{FnOnceTraitLangItem, GeneratorTraitLangItem}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::DUMMY_SP; -pub use rustc::traits::Reveal; +pub use rustc_middle::traits::Reveal; pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -261,7 +266,7 @@ where { debug!("normalize_with_depth(depth={}, value={:?})", depth, value); let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); - let result = normalizer.fold(value); + let result = ensure_sufficient_stack(|| normalizer.fold(value)); debug!( "normalize_with_depth: depth={} result={:?} with {} obligations", depth, @@ -387,7 +392,12 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - constant.eval(self.selcx.tcx(), self.param_env) + if self.selcx.tcx().lazy_normalization() { + constant + } else { + let constant = constant.super_fold_with(self); + constant.eval(self.selcx.tcx(), self.param_env) + } } } @@ -470,7 +480,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // bounds. It might be the case that we want two distinct caches, // or else another kind of cache entry. - let cache_result = infcx.inner.borrow_mut().projection_cache.try_start(cache_key); + let cache_result = infcx.inner.borrow_mut().projection_cache().try_start(cache_key); match cache_result { Ok(()) => {} Err(ProjectionCacheEntry::Ambiguous) => { @@ -536,7 +546,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // Once we have inferred everything we need to know, we // can ignore the `obligations` from that point on. if infcx.unresolved_type_vars(&ty.value).is_none() { - infcx.inner.borrow_mut().projection_cache.complete_normalized(cache_key, &ty); + infcx.inner.borrow_mut().projection_cache().complete_normalized(cache_key, &ty); // No need to extend `obligations`. } else { obligations.extend(ty.obligations); @@ -603,7 +613,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( }; let cache_value = prune_cache_value_obligations(infcx, &result); - infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, cache_value); + infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value); obligations.extend(result.obligations); Some(result.value) } @@ -614,7 +624,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( projected_ty ); let result = Normalized { value: projected_ty, obligations: vec![] }; - infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, result.clone()); + infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); // No need to extend `obligations`. Some(result.value) } @@ -623,7 +633,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( "opt_normalize_projection_type: \ too many candidates" ); - infcx.inner.borrow_mut().projection_cache.ambiguous(cache_key); + infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); None } Err(ProjectionTyError::TraitSelectionError(_)) => { @@ -633,7 +643,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // Trait`, which when processed will cause the error to be // reported later - infcx.inner.borrow_mut().projection_cache.error(cache_key); + infcx.inner.borrow_mut().projection_cache().error(cache_key); let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); Some(result.value) @@ -899,7 +909,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( // If so, extract what we know from the trait and try to come up with a good answer. let trait_predicates = tcx.predicates_of(def_id); let bounds = trait_predicates.instantiate(tcx, substs); - let bounds = elaborate_predicates(tcx, bounds.predicates); + let bounds = elaborate_predicates(tcx, bounds.predicates.into_iter()).map(|o| o.predicate); assemble_candidates_from_predicates( selcx, obligation, @@ -910,16 +920,14 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( ) } -fn assemble_candidates_from_predicates<'cx, 'tcx, I>( +fn assemble_candidates_from_predicates<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, obligation_trait_ref: &ty::TraitRef<'tcx>, candidate_set: &mut ProjectionTyCandidateSet<'tcx>, ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>, - env_predicates: I, -) where - I: IntoIterator>, -{ + env_predicates: impl Iterator>, +) { debug!("assemble_candidates_from_predicates(obligation={:?})", obligation); let infcx = selcx.infcx(); for predicate in env_predicates { @@ -1009,54 +1017,26 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // type. // // NOTE: This should be kept in sync with the similar code in - // `rustc::ty::instance::resolve_associated_item()`. + // `rustc_ty::instance::resolve_associated_item()`. let node_item = assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id) .map_err(|ErrorReported| ())?; - let is_default = if node_item.node.is_from_trait() { - // If true, the impl inherited a `type Foo = Bar` - // given in the trait, which is implicitly default. - // Otherwise, the impl did not specify `type` and - // neither did the trait: - // - // ```rust - // trait Foo { type T; } - // impl Foo for Bar { } - // ``` - // - // This is an error, but it will be - // reported in `check_impl_items_against_trait`. - // We accept it here but will flag it as - // an error when we confirm the candidate - // (which will ultimately lead to `normalize_to_error` - // being invoked). - false + if node_item.is_final() { + // Non-specializable items are always projectable. + true } else { - // If we're looking at a trait *impl*, the item is - // specializable if the impl or the item are marked - // `default`. - node_item.item.defaultness.is_default() - || super::util::impl_is_default(selcx.tcx(), node_item.node.def_id()) - }; - - match is_default { - // Non-specializable items are always projectable - false => true, - // Only reveal a specializable default if we're past type-checking // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - true if obligation.param_env.reveal == Reveal::All => { + if obligation.param_env.reveal == Reveal::All { // NOTE(eddyb) inference variables can resolve to parameters, so // assume `poly_trait_ref` isn't monomorphic, if it contains any. let poly_trait_ref = selcx.infcx().resolve_vars_if_possible(&poly_trait_ref); - !poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst() - } - - true => { + !poly_trait_ref.still_further_specializable() + } else { debug!( "assemble_candidates_from_impls: not eligible due to default: \ assoc_ty={} predicate={}", @@ -1067,6 +1047,46 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } + super::VtableDiscriminantKind(..) => { + // While `DiscriminantKind` is automatically implemented for every type, + // the concrete discriminant may not be known yet. + // + // Any type with multiple potential discriminant types is therefore not eligible. + let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); + + match self_ty.kind { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Tuple(..) + // Integers and floats always have `u8` as their discriminant. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + ty::Projection(..) + | ty::Opaque(..) + | ty::Param(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error => false, + } + } super::VtableParam(..) => { // This case tell us nothing about the value of an // associated type. Consider: @@ -1148,13 +1168,15 @@ fn confirm_select_candidate<'cx, 'tcx>( super::VtableGenerator(data) => confirm_generator_candidate(selcx, obligation, data), super::VtableClosure(data) => confirm_closure_candidate(selcx, obligation, data), super::VtableFnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data), + super::VtableDiscriminantKind(data) => { + confirm_discriminant_kind_candidate(selcx, obligation, data) + } super::VtableObject(_) => confirm_object_candidate(selcx, obligation, obligation_trait_ref), super::VtableAutoImpl(..) | super::VtableParam(..) | super::VtableBuiltin(..) - | super::VtableTraitAlias(..) => - // we don't create Select candidates with this kind of resolution - { + | super::VtableTraitAlias(..) => { + // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, "Cannot project an associated type from `{:?}`", @@ -1180,16 +1202,14 @@ fn confirm_object_candidate<'cx, 'tcx>( object_ty ), }; - let env_predicates = data - .projection_bounds() - .map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate()) - .collect(); + let env_predicates = + data.projection_bounds().map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate()); let env_predicate = { let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates); // select only those projections that are actually projecting an // item with the correct name - let env_predicates = env_predicates.filter_map(|p| match p { + let env_predicates = env_predicates.filter_map(|o| match o.predicate { ty::Predicate::Projection(data) => { if data.projection_def_id() == obligation.predicate.item_def_id { Some(data) @@ -1253,7 +1273,7 @@ fn confirm_generator_candidate<'cx, 'tcx>( let tcx = selcx.tcx(); - let gen_def_id = tcx.lang_items().gen_trait().unwrap(); + let gen_def_id = tcx.require_lang_item(GeneratorTraitLangItem, None); let predicate = super::util::generator_trait_ref_and_outputs( tcx, @@ -1285,6 +1305,37 @@ fn confirm_generator_candidate<'cx, 'tcx>( .with_addl_obligations(obligations) } +fn confirm_discriminant_kind_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + _: VtableDiscriminantKindData, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + + let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); + let substs = tcx.mk_substs([self_ty.into()].iter()); + + let assoc_items = tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); + // FIXME: emit an error if the trait definition is wrong + let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + + let discriminant_ty = match self_ty.kind { + // Use the discriminant type for enums. + ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), + // Default to `i32` for generators. + ty::Generator(..) => tcx.types.i32, + // Use `u8` for all other types. + _ => tcx.types.u8, + }; + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { substs, item_def_id: discriminant_def_id }, + ty: discriminant_ty, + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate)) +} + fn confirm_fn_pointer_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -1340,7 +1391,7 @@ fn confirm_callable_candidate<'cx, 'tcx>( debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig); // the `Output` associated type is declared on `FnOnce` - let fn_once_def_id = tcx.lang_items().fn_once_trait().unwrap(); + let fn_once_def_id = tcx.require_lang_item(FnOnceTraitLangItem, None); let predicate = super::util::closure_trait_ref_and_return_type( tcx, @@ -1421,7 +1472,8 @@ fn confirm_impl_candidate<'cx, 'tcx>( return Progress { ty: tcx.types.err, obligations: nested }; } let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs); - let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node); + let substs = + translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node); let ty = if let ty::AssocKind::OpaqueTy = assoc_ty.item.kind { let item_substs = InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id); tcx.mk_opaque(assoc_ty.item.def_id, item_substs) @@ -1446,7 +1498,7 @@ fn assoc_ty_def( selcx: &SelectionContext<'_, '_>, impl_def_id: DefId, assoc_ty_def_id: DefId, -) -> Result, ErrorReported> { +) -> Result { let tcx = selcx.tcx(); let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident; let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; @@ -1463,9 +1515,10 @@ fn assoc_ty_def( if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy) && tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id) { - return Ok(specialization_graph::NodeItem { - node: specialization_graph::Node::Impl(impl_def_id), + return Ok(specialization_graph::LeafDef { item: *item, + defining_node: impl_node, + finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) }, }); } } diff --git a/src/librustc_trait_selection/traits/query/dropck_outlives.rs b/src/librustc_trait_selection/traits/query/dropck_outlives.rs index 03c6cf35e8881..856a2111fc82c 100644 --- a/src/librustc_trait_selection/traits/query/dropck_outlives.rs +++ b/src/librustc_trait_selection/traits/query/dropck_outlives.rs @@ -2,10 +2,10 @@ use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferOk; -use rustc::ty::subst::GenericArg; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, Ty, TyCtxt}; -pub use rustc::traits::query::{DropckOutlivesResult, DtorckConstraint}; +pub use rustc_middle::traits::query::{DropckOutlivesResult, DtorckConstraint}; pub trait AtExt<'tcx> { fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>>; @@ -135,7 +135,5 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Infer(_) | ty::Bound(..) | ty::Generator(..) => false, - - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), } } diff --git a/src/librustc_trait_selection/traits/query/method_autoderef.rs b/src/librustc_trait_selection/traits/query/method_autoderef.rs index 80748c5ef388e..3c0ebec933539 100644 --- a/src/librustc_trait_selection/traits/query/method_autoderef.rs +++ b/src/librustc_trait_selection/traits/query/method_autoderef.rs @@ -1 +1,3 @@ -pub use rustc::traits::query::{CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult}; +pub use rustc_middle::traits::query::{ + CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, +}; diff --git a/src/librustc_trait_selection/traits/query/mod.rs b/src/librustc_trait_selection/traits/query/mod.rs index 77b5ec669a099..01f4f09e2381b 100644 --- a/src/librustc_trait_selection/traits/query/mod.rs +++ b/src/librustc_trait_selection/traits/query/mod.rs @@ -12,4 +12,4 @@ pub mod normalize; pub mod outlives_bounds; pub mod type_op; -pub use rustc::traits::query::*; +pub use rustc_middle::traits::query::*; diff --git a/src/librustc_trait_selection/traits/query/normalize.rs b/src/librustc_trait_selection/traits/query/normalize.rs index 99412fafcfa8d..3b985a4b150fa 100644 --- a/src/librustc_trait_selection/traits/query/normalize.rs +++ b/src/librustc_trait_selection/traits/query/normalize.rs @@ -7,14 +7,15 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::InferCtxtExt; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, Ty, TyCtxt}; use super::NoSolution; -pub use rustc::traits::query::NormalizationResult; +pub use rustc_middle::traits::query::NormalizationResult; pub trait AtExt<'tcx> { fn normalize(&self, value: &T) -> Result, NoSolution> @@ -59,11 +60,22 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { anon_depth: 0, }; - let value1 = value.fold_with(&mut normalizer); + let result = value.fold_with(&mut normalizer); + debug!( + "normalize::<{}>: result={:?} with {} obligations", + ::std::any::type_name::(), + result, + normalizer.obligations.len(), + ); + debug!( + "normalize::<{}>: obligations={:?}", + ::std::any::type_name::(), + normalizer.obligations, + ); if normalizer.error { Err(NoSolution) } else { - Ok(Normalized { value: value1, obligations: normalizer.obligations }) + Ok(Normalized { value: result, obligations: normalizer.obligations }) } } } @@ -120,7 +132,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { ty ); } - let folded_ty = self.fold_ty(concrete_ty); + let folded_ty = ensure_sufficient_stack(|| self.fold_ty(concrete_ty)); self.anon_depth -= 1; folded_ty } @@ -191,6 +203,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { } fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + let constant = constant.super_fold_with(self); constant.eval(self.infcx.tcx, self.param_env) } } diff --git a/src/librustc_trait_selection/traits/query/outlives_bounds.rs b/src/librustc_trait_selection/traits/query/outlives_bounds.rs index 05c96dd520ab7..a42409515db1e 100644 --- a/src/librustc_trait_selection/traits/query/outlives_bounds.rs +++ b/src/librustc_trait_selection/traits/query/outlives_bounds.rs @@ -2,12 +2,12 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; use crate::traits::query::NoSolution; use crate::traits::{FulfillmentContext, ObligationCause, TraitEngine}; -use rustc::ty::{self, Ty}; use rustc_hir as hir; use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; -pub use rustc::traits::query::OutlivesBound; +pub use rustc_middle::traits::query::OutlivesBound; pub trait InferCtxtExt<'tcx> { fn implied_outlives_bounds( diff --git a/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs b/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs index b14b79f090778..86b015767f0f8 100644 --- a/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs +++ b/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs @@ -1,8 +1,8 @@ use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; -pub use rustc::traits::query::type_op::AscribeUserType; +pub use rustc_middle::traits::query::type_op::AscribeUserType; impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { type QueryResponse = (); diff --git a/src/librustc_trait_selection/traits/query/type_op/eq.rs b/src/librustc_trait_selection/traits/query/type_op/eq.rs index 3b6fbc7d8dd72..490114aacd135 100644 --- a/src/librustc_trait_selection/traits/query/type_op/eq.rs +++ b/src/librustc_trait_selection/traits/query/type_op/eq.rs @@ -1,8 +1,8 @@ use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; -pub use rustc::traits::query::type_op::Eq; +pub use rustc_middle::traits::query::type_op::Eq; impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { type QueryResponse = (); diff --git a/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs index 3dad546872e5a..cf7f0a553c704 100644 --- a/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs +++ b/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs @@ -1,7 +1,7 @@ use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::outlives_bounds::OutlivesBound; use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; #[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] pub struct ImpliedOutlivesBounds<'tcx> { diff --git a/src/librustc_trait_selection/traits/query/type_op/mod.rs b/src/librustc_trait_selection/traits/query/type_op/mod.rs index 1644746c16eb1..ed6c6d0cc0a98 100644 --- a/src/librustc_trait_selection/traits/query/type_op/mod.rs +++ b/src/librustc_trait_selection/traits/query/type_op/mod.rs @@ -4,8 +4,8 @@ use crate::infer::canonical::{ use crate::infer::{InferCtxt, InferOk}; use crate::traits::query::Fallible; use crate::traits::ObligationCause; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{ParamEnvAnd, TyCtxt}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use std::fmt; use std::rc::Rc; @@ -19,7 +19,7 @@ pub mod prove_predicate; use self::prove_predicate::ProvePredicate; pub mod subtype; -pub use rustc::traits::query::type_op::*; +pub use rustc_middle::traits::query::type_op::*; /// "Type ops" are used in NLL to perform some particular action and /// extract out the resulting region constraints (or an error if it @@ -44,7 +44,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug { /// first canonicalize the key and then invoke the query on the tcx, /// which produces the resulting query region constraints. /// -/// [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html +/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx { type QueryResponse: TypeFoldable<'tcx>; diff --git a/src/librustc_trait_selection/traits/query/type_op/normalize.rs b/src/librustc_trait_selection/traits/query/type_op/normalize.rs index d2eec53bf80fe..729b66ac21c21 100644 --- a/src/librustc_trait_selection/traits/query/type_op/normalize.rs +++ b/src/librustc_trait_selection/traits/query/type_op/normalize.rs @@ -1,10 +1,10 @@ use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt}; use std::fmt; -pub use rustc::traits::query::type_op::Normalize; +pub use rustc_middle::traits::query::type_op::Normalize; impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize where diff --git a/src/librustc_trait_selection/traits/query/type_op/outlives.rs b/src/librustc_trait_selection/traits/query/type_op/outlives.rs index b94948cffd68f..5a27e57860ecd 100644 --- a/src/librustc_trait_selection/traits/query/type_op/outlives.rs +++ b/src/librustc_trait_selection/traits/query/type_op/outlives.rs @@ -1,7 +1,7 @@ use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutlivesResult}; use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, Lift)] pub struct DropckOutlives<'tcx> { diff --git a/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs b/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs index 8c68f7db9e5bc..981745af80544 100644 --- a/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs +++ b/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs @@ -1,8 +1,8 @@ use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, Predicate, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, Predicate, TyCtxt}; -pub use rustc::traits::query::type_op::ProvePredicate; +pub use rustc_middle::traits::query::type_op::ProvePredicate; impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { type QueryResponse = (); diff --git a/src/librustc_trait_selection/traits/query/type_op/subtype.rs b/src/librustc_trait_selection/traits/query/type_op/subtype.rs index 053411b0cac2e..57290b6691410 100644 --- a/src/librustc_trait_selection/traits/query/type_op/subtype.rs +++ b/src/librustc_trait_selection/traits/query/type_op/subtype.rs @@ -1,8 +1,8 @@ use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; -pub use rustc::traits::query::type_op::Subtype; +pub use rustc_middle::traits::query::type_op::Subtype; impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { type QueryResponse = (); diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index 2d6fabea6ec75..d903779e5075f 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -24,28 +24,34 @@ use super::{ObjectCastObligation, Obligation}; use super::{ObligationCause, PredicateObligation, TraitObligation}; use super::{OutputTypeParameterMismatch, Overflow, SelectionError, Unimplemented}; use super::{ - VtableAutoImpl, VtableBuiltin, VtableClosure, VtableFnPointer, VtableGenerator, VtableImpl, - VtableObject, VtableParam, VtableTraitAlias, + VtableAutoImpl, VtableBuiltin, VtableClosure, VtableDiscriminantKind, VtableFnPointer, + VtableGenerator, VtableImpl, VtableObject, VtableParam, VtableTraitAlias, }; use super::{ - VtableAutoImplData, VtableBuiltinData, VtableClosureData, VtableFnPointerData, - VtableGeneratorData, VtableImplData, VtableObjectData, VtableTraitAliasData, + VtableAutoImplData, VtableBuiltinData, VtableClosureData, VtableDiscriminantKindData, + VtableFnPointerData, VtableGeneratorData, VtableImplData, VtableObjectData, + VtableTraitAliasData, }; use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, PlaceholderMap, TypeFreshener}; use crate::traits::error_reporting::InferCtxtExt; use crate::traits::project::ProjectionCacheKeyExt; -use rustc::dep_graph::{DepKind, DepNodeIndex}; -use rustc::middle::lang_items; -use rustc::ty::fast_reject; -use rustc::ty::relate::TypeRelation; -use rustc::ty::subst::{Subst, SubstsRef}; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_ast::attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items; use rustc_index::bit_set::GrowableBitSet; +use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::fast_reject; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; +use rustc_middle::ty::{ + self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; @@ -55,7 +61,7 @@ use std::fmt::{self, Display}; use std::iter; use std::rc::Rc; -pub use rustc::traits::select::*; +pub use rustc_middle::traits::select::*; pub struct SelectionContext<'cx, 'tcx> { infcx: &'cx InferCtxt<'cx, 'tcx>, @@ -411,7 +417,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match obligation.predicate { ty::Predicate::Trait(ref t, _) => { debug_assert!(!t.has_escaping_bound_vars()); - let obligation = obligation.with(t.clone()); + let obligation = obligation.with(*t); self.evaluate_trait_predicate_recursively(previous_stack, obligation) } @@ -458,7 +464,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::Predicate::Projection(ref data) => { - let project_obligation = obligation.with(data.clone()); + let project_obligation = obligation.with(*data); match project::poly_project_and_unify_type(self, &project_obligation) { Ok(Some(mut subobligations)) => { self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); @@ -469,7 +475,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate(self, data) { - self.infcx.inner.borrow_mut().projection_cache.complete(key); + self.infcx.inner.borrow_mut().projection_cache().complete(key); } result } @@ -500,9 +506,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None, ) { Ok(_) => Ok(EvaluatedToOk), + Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), Err(_) => Ok(EvaluatedToErr), } } + + ty::Predicate::ConstEquate(c1, c2) => { + debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2); + + let evaluate = |c: &'tcx ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val { + self.infcx + .const_eval_resolve( + obligation.param_env, + def_id, + substs, + promoted, + Some(obligation.cause.span), + ) + .map(|val| ty::Const::from_value(self.tcx(), val, c.ty)) + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) { + Ok(_) => Ok(EvaluatedToOk), + Err(_) => Ok(EvaluatedToErr), + } + } + (Err(ErrorHandled::Reported(ErrorReported)), _) + | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr), + (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!( + obligation.cause.span(self.tcx()), + "ConstEquate: const_eval_resolve returned an unexpected error" + ), + (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { + Ok(EvaluatedToAmbig) + } + } + } } } @@ -650,7 +695,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, stack: &TraitObligationStack<'o, 'tcx>, ) -> Result { - // In intercrate mode, whenever any of the types are unbound, + // In intercrate mode, whenever any of the generics are unbound, // there can always be an impl. Even if there are no impls in // this crate, perhaps the type would be unified with // something from another crate that does provide an impl. @@ -675,7 +720,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // terms of `Fn` etc, but we could probably make this more // precise still. let unbound_input_types = - stack.fresh_trait_ref.skip_binder().input_types().any(|ty| ty.is_fresh()); + stack.fresh_trait_ref.skip_binder().substs.types().any(|ty| ty.is_fresh()); // This check was an imperfect workaround for a bug in the old // intercrate mode; it should be removed when that goes away. if unbound_input_types && self.intercrate { @@ -818,7 +863,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if self.can_use_global_caches(param_env) { - if !trait_ref.has_local_value() { + if !trait_ref.needs_infer() { debug!( "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", trait_ref, result, @@ -908,7 +953,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // separately rather than using `stack.fresh_trait_ref` -- // this is because we want the unbound variables to be // replaced with fresh types starting from index 0. - let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate.clone()); + let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate); debug!( "candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})", cache_fresh_trait_pred, stack @@ -1176,10 +1221,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Do note that if the type itself is not in the /// global tcx, the local caches will be used. fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { - // If there are any e.g. inference variables in the `ParamEnv`, then we + // If there are any inference variables in the `ParamEnv`, then we // always use a cache local to this particular scope. Otherwise, we // switch to a global cache. - if param_env.has_local_value() { + if param_env.needs_infer() { return false; } @@ -1240,9 +1285,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, ) -> bool { match result { - Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => { - !trait_ref.skip_binder().input_types().any(|t| t.walk().any(|t_| t_.is_ty_infer())) - } + Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(), _ => true, } } @@ -1269,8 +1312,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.can_use_global_caches(param_env) { if let Err(Overflow) = candidate { // Don't cache overflow globally; we only produce this in certain modes. - } else if !trait_ref.has_local_value() { - if !candidate.has_local_value() { + } else if !trait_ref.needs_infer() { + if !candidate.needs_infer() { debug!( "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", trait_ref, candidate, @@ -1340,6 +1383,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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 lang_items.discriminant_kind_trait() == Some(def_id) { + // `DiscriminantKind` is automatically implemented for every type. + candidates.vec.push(DiscriminantKindCandidate); } else if lang_items.sized_trait() == Some(def_id) { // Sized is never implementable by end-users, it is // always automatically computed. @@ -1443,13 +1489,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bounds ); - let elaborated_predicates = util::elaborate_predicates(self.tcx(), bounds.predicates); + let elaborated_predicates = + util::elaborate_predicates(self.tcx(), bounds.predicates.into_iter()); let matching_bound = elaborated_predicates.filter_to_traits().find(|bound| { self.infcx.probe(|_| { self.match_projection( obligation, - bound.clone(), - placeholder_trait_predicate.trait_ref.clone(), + *bound, + placeholder_trait_predicate.trait_ref, &placeholder_map, snapshot, ) @@ -1468,7 +1515,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let result = self.match_projection( obligation, bound, - placeholder_trait_predicate.trait_ref.clone(), + placeholder_trait_predicate.trait_ref, &placeholder_map, snapshot, ); @@ -1520,7 +1567,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Keep only those bounds which may apply, and propagate overflow if it occurs. let mut param_candidates = vec![]; for bound in matching_bounds { - let wc = self.evaluate_where_clause(stack, bound.clone())?; + let wc = self.evaluate_where_clause(stack, bound)?; if wc.may_apply() { param_candidates.push(ParamCandidate(bound)); } @@ -1952,11 +1999,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let is_global = |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); + // (*) Prefer `BuiltinCandidate { has_nested: false }` and `DiscriminantKindCandidate` + // to anything else. + // + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. match other.candidate { - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => true, + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => true, ParamCandidate(ref cand) => match victim.candidate { AutoImplCandidate(..) => { bug!( @@ -1964,10 +2014,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { when there are other valid candidates" ); } - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => false, + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -1995,10 +2043,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { when there are other valid candidates" ); } - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => false, + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -2139,8 +2185,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); match self_ty.kind { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) | ty::Bool @@ -2177,12 +2222,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, ty::Infer(ty::TyVar(_)) => Ambiguous, - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) + ty::Placeholder(..) | ty::Bound(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } } @@ -2251,12 +2293,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ambiguous } - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) + ty::Placeholder(..) | ty::Bound(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } } @@ -2283,22 +2322,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::FnPtr(_) | ty::Str | ty::Error - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never | ty::Char => Vec::new(), - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) + ty::Placeholder(..) | ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) | ty::Projection(..) | ty::Bound(..) - | ty::Infer(ty::TyVar(_)) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble constituent types of unexpected type: {:?}", t); } @@ -2373,13 +2407,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.commit_unconditionally(|_| { let (skol_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); let Normalized { value: normalized_ty, mut obligations } = - project::normalize_with_depth( - self, - param_env, - cause.clone(), - recursion_depth, - &skol_ty, - ); + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + param_env, + cause.clone(), + recursion_depth, + &skol_ty, + ) + }); let skol_obligation = predicate_for_trait_def( self.tcx(), param_env, @@ -2453,6 +2489,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(VtableFnPointer(data)) } + DiscriminantKindCandidate => Ok(VtableDiscriminantKind(VtableDiscriminantKindData)), + TraitAliasCandidate(alias_def_id) => { let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); Ok(VtableTraitAlias(data)) @@ -2496,7 +2534,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // where-clause trait-ref could be unified with the obligation // trait-ref. Repeat that unification now without any // transactional boundary; it should not fail. - match self.match_where_clause_trait_ref(obligation, param.clone()) { + match self.match_where_clause_trait_ref(obligation, param) { Ok(obligations) => obligations, Err(()) => { bug!( @@ -2533,13 +2571,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let cause = obligation.derived_cause(BuiltinDerivedObligation); - self.collect_predicates_for_types( - obligation.param_env, - cause, - obligation.recursion_depth + 1, - trait_def, - nested, - ) + ensure_sufficient_stack(|| { + self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def, + nested, + ) + }) } else { vec![] }; @@ -2576,38 +2616,39 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested: ty::Binder>>, ) -> VtableAutoImplData> { debug!("vtable_auto_impl: nested={:?}", nested); + ensure_sufficient_stack(|| { + let cause = obligation.derived_cause(BuiltinDerivedObligation); + let mut obligations = self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def_id, + nested, + ); - let cause = obligation.derived_cause(BuiltinDerivedObligation); - let mut obligations = self.collect_predicates_for_types( - obligation.param_env, - cause, - obligation.recursion_depth + 1, - trait_def_id, - nested, - ); - - let trait_obligations: Vec> = - self.infcx.commit_unconditionally(|_| { - let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); - let (trait_ref, _) = - self.infcx.replace_bound_vars_with_placeholders(&poly_trait_ref); - let cause = obligation.derived_cause(ImplDerivedObligation); - self.impl_or_trait_obligations( - cause, - obligation.recursion_depth + 1, - obligation.param_env, - trait_def_id, - &trait_ref.substs, - ) - }); + let trait_obligations: Vec> = + self.infcx.commit_unconditionally(|_| { + let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); + let (trait_ref, _) = + self.infcx.replace_bound_vars_with_placeholders(&poly_trait_ref); + let cause = obligation.derived_cause(ImplDerivedObligation); + self.impl_or_trait_obligations( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + trait_def_id, + &trait_ref.substs, + ) + }); - // 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); + // 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!("vtable_auto_impl: obligations={:?}", obligations); + debug!("vtable_auto_impl: obligations={:?}", obligations); - VtableAutoImplData { trait_def_id, nested: obligations } + VtableAutoImplData { trait_def_id, nested: obligations } + }) } fn confirm_impl_candidate( @@ -2623,13 +2664,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let substs = self.rematch_impl(impl_def_id, obligation, snapshot); debug!("confirm_impl_candidate: substs={:?}", substs); let cause = obligation.derived_cause(ImplDerivedObligation); - self.vtable_impl( - impl_def_id, - substs, - cause, - obligation.recursion_depth + 1, - obligation.param_env, - ) + ensure_sufficient_stack(|| { + self.vtable_impl( + impl_def_id, + substs, + cause, + obligation.recursion_depth + 1, + obligation.param_env, + ) + }) }) } @@ -2742,13 +2785,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let Normalized { value: trait_ref, obligations } = project::normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &trait_ref, - ); + let Normalized { value: trait_ref, obligations } = ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_ref, + ) + }); self.confirm_poly_trait_refs( obligation.cause.clone(), @@ -2806,13 +2851,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("confirm_generator_candidate({:?},{:?},{:?})", obligation, generator_def_id, substs); let trait_ref = self.generator_trait_ref_unnormalized(obligation, substs); - let Normalized { value: trait_ref, mut obligations } = normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &trait_ref, - ); + let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| { + normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_ref, + ) + }); debug!( "confirm_generator_candidate(generator_def_id={:?}, \ @@ -2851,13 +2898,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let trait_ref = self.closure_trait_ref_unnormalized(obligation, substs); - let Normalized { value: trait_ref, mut obligations } = normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &trait_ref, - ); + let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| { + normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_ref, + ) + }); debug!( "confirm_closure_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})", @@ -2871,11 +2920,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { trait_ref, )?); - obligations.push(Obligation::new( - obligation.cause.clone(), - obligation.param_env, - ty::Predicate::ClosureKind(closure_def_id, substs, kind), - )); + // FIXME: Chalk + + if !self.tcx().sess.opts.debugging_opts.chalk { + obligations.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env, + ty::Predicate::ClosureKind(closure_def_id, substs, kind), + )); + } Ok(VtableClosureData { closure_def_id, substs, nested: obligations }) } @@ -3046,20 +3099,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `Struct` -> `Struct` (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => { - let fields = - def.all_fields().map(|field| tcx.type_of(field.did)).collect::>(); - - // The last field of the structure has to exist and contain type parameters. - let field = if let Some(&field) = fields.last() { - field - } else { - return Err(Unimplemented); + let maybe_unsizing_param_idx = |arg: GenericArg<'tcx>| match arg.unpack() { + GenericArgKind::Type(ty) => match ty.kind { + ty::Param(p) => Some(p.index), + _ => None, + }, + + // Lifetimes aren't allowed to change during unsizing. + GenericArgKind::Lifetime(_) => None, + + GenericArgKind::Const(ct) => match ct.val { + ty::ConstKind::Param(p) => Some(p.index), + _ => None, + }, }; - let mut ty_params = GrowableBitSet::new_empty(); + + // The last field of the structure has to exist and contain type/const parameters. + let (tail_field, prefix_fields) = + def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?; + let tail_field_ty = tcx.type_of(tail_field.did); + + let mut unsizing_params = GrowableBitSet::new_empty(); let mut found = false; - for ty in field.walk() { - if let ty::Param(p) = ty.kind { - ty_params.insert(p.index as usize); + for arg in tail_field_ty.walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); found = true; } } @@ -3067,31 +3131,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(Unimplemented); } - // Replace type parameters used in unsizing with - // Error and ensure they do not affect any other fields. - // This could be checked after type collection for any struct - // with a potentially unsized trailing field. - let params = substs_a - .iter() - .enumerate() - .map(|(i, &k)| if ty_params.contains(i) { tcx.types.err.into() } else { k }); - let substs = tcx.mk_substs(params); - for &ty in fields.split_last().unwrap().1 { - if ty.subst(tcx, substs).references_error() { - return Err(Unimplemented); + // Ensure none of the other fields mention the parameters used + // in unsizing. + // FIXME(eddyb) cache this (including computing `unsizing_params`) + // by putting it in a query; it would only need the `DefId` as it + // looks at declared field types, not anything substituted. + for field in prefix_fields { + for arg in tcx.type_of(field.did).walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + if unsizing_params.contains(i) { + return Err(Unimplemented); + } + } } } - // Extract `Field` and `Field` from `Struct` and `Struct`. - let inner_source = field.subst(tcx, substs_a); - let inner_target = field.subst(tcx, substs_b); + // Extract `TailField` and `TailField` from `Struct` and `Struct`. + let source_tail = tail_field_ty.subst(tcx, substs_a); + let target_tail = tail_field_ty.subst(tcx, substs_b); // Check that the source struct with the target's - // unsized parameters is equal to the target. - let params = substs_a.iter().enumerate().map(|(i, &k)| { - if ty_params.contains(i) { substs_b.type_at(i).into() } else { k } - }); - let new_struct = tcx.mk_adt(def, tcx.mk_substs(params)); + // unsizing parameters is equal to the target. + let substs = tcx.mk_substs(substs_a.iter().enumerate().map(|(i, &k)| { + if unsizing_params.contains(i as u32) { substs_b[i] } else { k } + })); + let new_struct = tcx.mk_adt(def, substs); let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) @@ -3099,15 +3163,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|_| Unimplemented)?; nested.extend(obligations); - // Construct the nested `Field: Unsize>` predicate. + // Construct the nested `TailField: Unsize>` predicate. nested.push(predicate_for_trait_def( tcx, obligation.param_env, obligation.cause.clone(), obligation.predicate.def_id(), obligation.recursion_depth + 1, - inner_source, - &[inner_target.into()], + source_tail, + &[target_tail.into()], )); } @@ -3136,15 +3200,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested.extend(obligations); // Construct the nested `T: Unsize` predicate. - nested.push(predicate_for_trait_def( - tcx, - obligation.param_env, - obligation.cause.clone(), - obligation.predicate.def_id(), - obligation.recursion_depth + 1, - a_last.expect_ty(), - &[b_last], - )); + nested.push(ensure_sufficient_stack(|| { + predicate_for_trait_def( + tcx, + obligation.param_env, + obligation.cause.clone(), + obligation.predicate.def_id(), + obligation.recursion_depth + 1, + a_last.expect_ty(), + &[b_last], + ) + })); } _ => bug!(), @@ -3205,13 +3271,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = - project::normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &impl_trait_ref, - ); + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &impl_trait_ref, + ) + }); debug!( "match_impl(impl_def_id={:?}, obligation={:?}, \ @@ -3251,15 +3319,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // substitution if we find that any of the input types, when // simplified, do not match. - obligation.predicate.skip_binder().input_types().zip(impl_trait_ref.input_types()).any( - |(obligation_ty, impl_ty)| { - let simplified_obligation_ty = - fast_reject::simplify_type(self.tcx(), obligation_ty, true); - let simplified_impl_ty = fast_reject::simplify_type(self.tcx(), impl_ty, false); - - simplified_obligation_ty.is_some() - && simplified_impl_ty.is_some() - && simplified_obligation_ty != simplified_impl_ty + obligation.predicate.skip_binder().trait_ref.substs.iter().zip(impl_trait_ref.substs).any( + |(obligation_arg, impl_arg)| { + match (obligation_arg.unpack(), impl_arg.unpack()) { + (GenericArgKind::Type(obligation_ty), GenericArgKind::Type(impl_ty)) => { + let simplified_obligation_ty = + fast_reject::simplify_type(self.tcx(), obligation_ty, true); + let simplified_impl_ty = + fast_reject::simplify_type(self.tcx(), impl_ty, false); + + simplified_obligation_ty.is_some() + && simplified_impl_ty.is_some() + && simplified_obligation_ty != simplified_impl_ty + } + (GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_)) => { + // Lifetimes can never cause a rejection. + false + } + (GenericArgKind::Const(_), GenericArgKind::Const(_)) => { + // Conservatively ignore consts (i.e. assume they might + // unify later) until we have `fast_reject` support for + // them (if we'll ever need it, even). + false + } + _ => unreachable!(), + } }, ) } @@ -3623,11 +3707,7 @@ struct ProvisionalEvaluation { impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { fn default() -> Self { - Self { - dfn: Cell::new(0), - reached_depth: Cell::new(std::usize::MAX), - map: Default::default(), - } + Self { dfn: Cell::new(0), reached_depth: Cell::new(usize::MAX), map: Default::default() } } } @@ -3726,7 +3806,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { op(fresh_trait_ref, eval.result); } - self.reached_depth.set(std::usize::MAX); + self.reached_depth.set(usize::MAX); } } diff --git a/src/librustc_trait_selection/traits/specialize/mod.rs b/src/librustc_trait_selection/traits/specialize/mod.rs index b763851b86ef6..f2b43754acaea 100644 --- a/src/librustc_trait_selection/traits/specialize/mod.rs +++ b/src/librustc_trait_selection/traits/specialize/mod.rs @@ -15,12 +15,12 @@ use specialization_graph::GraphExt; use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; -use rustc::lint::LintDiagnosticBuilder; -use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc::ty::{self, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; use rustc_span::DUMMY_SP; @@ -112,48 +112,6 @@ pub fn translate_substs<'a, 'tcx>( source_substs.rebase_onto(infcx.tcx, source_impl, target_substs) } -/// Given a selected impl described by `impl_data`, returns the -/// definition and substitutions for the method with the name `name` -/// the kind `kind`, and trait method substitutions `substs`, in -/// that impl, a less specialized impl, or the trait default, -/// whichever applies. -pub fn find_associated_item<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - item: &ty::AssocItem, - substs: SubstsRef<'tcx>, - impl_data: &super::VtableImplData<'tcx, ()>, -) -> (DefId, SubstsRef<'tcx>) { - debug!("find_associated_item({:?}, {:?}, {:?}, {:?})", param_env, item, substs, impl_data); - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); - let trait_def = tcx.trait_def(trait_def_id); - - if let Ok(ancestors) = trait_def.ancestors(tcx, impl_data.impl_def_id) { - match ancestors.leaf_def(tcx, item.ident, item.kind) { - Some(node_item) => { - let substs = tcx.infer_ctxt().enter(|infcx| { - let param_env = param_env.with_reveal_all(); - let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs); - let substs = translate_substs( - &infcx, - param_env, - impl_data.impl_def_id, - substs, - node_item.node, - ); - infcx.tcx.erase_regions(&substs) - }); - (node_item.item.def_id, substs) - } - None => bug!("{:?} not found in {:?}", item, impl_data.impl_def_id), - } - } else { - (item.def_id, substs) - } -} - /// Is `impl1` a specialization of `impl2`? /// /// Specialization is determined by the sets of types to which the impls apply; @@ -231,26 +189,22 @@ fn fulfill_implication<'a, 'tcx>( let selcx = &mut SelectionContext::new(&infcx); let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); - let (target_trait_ref, mut obligations) = + let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs); - debug!( - "fulfill_implication: target_trait_ref={:?}, obligations={:?}", - target_trait_ref, obligations - ); // do the impls unify? If not, no specialization. - match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) { - Ok(InferOk { obligations: o, .. }) => { - obligations.extend(o); - } - Err(_) => { - debug!( - "fulfill_implication: {:?} does not unify with {:?}", - source_trait_ref, target_trait_ref - ); - return Err(()); - } - } + let more_obligations = + match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) + { + Ok(InferOk { obligations, .. }) => obligations, + Err(_) => { + debug!( + "fulfill_implication: {:?} does not unify with {:?}", + source_trait_ref, target_trait_ref + ); + return Err(()); + } + }; // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) @@ -268,7 +222,7 @@ fn fulfill_implication<'a, 'tcx>( // we already make a mockery out of the region system, so // why not ignore them a bit earlier? let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); - for oblig in obligations.into_iter() { + for oblig in obligations.chain(more_obligations) { fulfill_cx.register_predicate_obligation(&infcx, oblig); } match fulfill_cx.select_all_or_error(infcx) { @@ -300,10 +254,10 @@ fn fulfill_implication<'a, 'tcx>( pub(super) fn specialization_graph_provider( tcx: TyCtxt<'_>, trait_id: DefId, -) -> &specialization_graph::Graph { +) -> specialization_graph::Graph { let mut sg = specialization_graph::Graph::new(); - let mut trait_impls = tcx.all_impls(trait_id); + let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect(); // The coherence checking implementation seems to rely on impls being // iterated over (roughly) in definition order, so we are sorting by @@ -313,9 +267,9 @@ pub(super) fn specialization_graph_provider( .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); for impl_def_id in trait_impls { - if impl_def_id.is_local() { + if let Some(impl_def_id) = impl_def_id.as_local() { // This is where impl overlap checking happens: - let insert_result = sg.insert(tcx, impl_def_id); + let insert_result = sg.insert(tcx, impl_def_id.to_def_id()); // Report error if there was one. let (overlap, used_to_be_allowed) = match insert_result { Err(overlap) => (Some(overlap), None), @@ -324,86 +278,7 @@ pub(super) fn specialization_graph_provider( }; if let Some(overlap) = overlap { - let impl_span = - tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap()); - - // Work to be done after we've built the DiagnosticBuilder. We have to define it - // now because the struct_lint methods don't return back the DiagnosticBuilder - // that's passed in. - let decorate = |err: LintDiagnosticBuilder<'_>| { - let msg = format!( - "conflicting implementations of trait `{}`{}:{}", - overlap.trait_desc, - overlap - .self_desc - .clone() - .map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", - _ => "", - } - ); - let mut err = err.build(&msg); - match tcx.span_of_impl(overlap.with_impl) { - Ok(span) => { - err.span_label( - tcx.sess.source_map().def_span(span), - "first implementation here".to_string(), - ); - - err.span_label( - impl_span, - format!( - "conflicting implementation{}", - overlap - .self_desc - .map_or(String::new(), |ty| format!(" for `{}`", ty)) - ), - ); - } - Err(cname) => { - let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { - Some(s) => format!( - "conflicting implementation in crate `{}`:\n- {}", - cname, s - ), - None => format!("conflicting implementation in crate `{}`", cname), - }; - err.note(&msg); - } - } - - for cause in &overlap.intercrate_ambiguity_causes { - cause.add_intercrate_ambiguity_hint(&mut err); - } - - if overlap.involves_placeholder { - coherence::add_placeholder_note(&mut err); - } - err.emit() - }; - - match used_to_be_allowed { - None => { - sg.has_errored = true; - let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); - decorate(LintDiagnosticBuilder::new(err)); - } - Some(kind) => { - let lint = match kind { - FutureCompatOverlapErrorKind::Issue33140 => { - ORDER_DEPENDENT_TRAIT_OBJECTS - } - FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, - }; - tcx.struct_span_lint_hir( - lint, - tcx.hir().as_local_hir_id(impl_def_id).unwrap(), - impl_span, - decorate, - ) - } - }; + report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg); } } else { let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); @@ -411,7 +286,174 @@ pub(super) fn specialization_graph_provider( } } - tcx.arena.alloc(sg) + sg +} + +fn report_overlap_conflict( + tcx: TyCtxt<'_>, + overlap: OverlapError, + impl_def_id: LocalDefId, + used_to_be_allowed: Option, + sg: &mut specialization_graph::Graph, +) { + let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id()); + let other_polarity = tcx.impl_polarity(overlap.with_impl); + match (impl_polarity, other_polarity) { + (ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => { + report_negative_positive_conflict( + tcx, + &overlap, + impl_def_id, + impl_def_id.to_def_id(), + overlap.with_impl, + sg, + ); + } + + (ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => { + report_negative_positive_conflict( + tcx, + &overlap, + impl_def_id, + overlap.with_impl, + impl_def_id.to_def_id(), + sg, + ); + } + + _ => { + report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg); + } + } +} + +fn report_negative_positive_conflict( + tcx: TyCtxt<'_>, + overlap: &OverlapError, + local_impl_def_id: LocalDefId, + negative_impl_def_id: DefId, + positive_impl_def_id: DefId, + sg: &mut specialization_graph::Graph, +) { + let impl_span = tcx + .sess + .source_map() + .guess_head_span(tcx.span_of_impl(local_impl_def_id.to_def_id()).unwrap()); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0751, + "found both positive and negative implementation of trait `{}`{}:", + overlap.trait_desc, + overlap.self_desc.clone().map_or(String::new(), |ty| format!(" for type `{}`", ty)) + ); + + match tcx.span_of_impl(negative_impl_def_id) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().guess_head_span(span), + "negative implementation here".to_string(), + ); + } + Err(cname) => { + err.note(&format!("negative implementation in crate `{}`", cname)); + } + } + + match tcx.span_of_impl(positive_impl_def_id) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().guess_head_span(span), + "positive implementation here".to_string(), + ); + } + Err(cname) => { + err.note(&format!("positive implementation in crate `{}`", cname)); + } + } + + sg.has_errored = true; + err.emit(); +} + +fn report_conflicting_impls( + tcx: TyCtxt<'_>, + overlap: OverlapError, + impl_def_id: LocalDefId, + used_to_be_allowed: Option, + sg: &mut specialization_graph::Graph, +) { + let impl_span = + tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()); + + // Work to be done after we've built the DiagnosticBuilder. We have to define it + // now because the struct_lint methods don't return back the DiagnosticBuilder + // that's passed in. + let decorate = |err: LintDiagnosticBuilder<'_>| { + let msg = format!( + "conflicting implementations of trait `{}`{}:{}", + overlap.trait_desc, + overlap.self_desc.clone().map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", + _ => "", + } + ); + let mut err = err.build(&msg); + match tcx.span_of_impl(overlap.with_impl) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().guess_head_span(span), + "first implementation here".to_string(), + ); + + err.span_label( + impl_span, + format!( + "conflicting implementation{}", + overlap.self_desc.map_or(String::new(), |ty| format!(" for `{}`", ty)) + ), + ); + } + Err(cname) => { + let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { + Some(s) => format!("conflicting implementation in crate `{}`:\n- {}", cname, s), + None => format!("conflicting implementation in crate `{}`", cname), + }; + err.note(&msg); + } + } + + for cause in &overlap.intercrate_ambiguity_causes { + cause.add_intercrate_ambiguity_hint(&mut err); + } + + if overlap.involves_placeholder { + coherence::add_placeholder_note(&mut err); + } + err.emit() + }; + + match used_to_be_allowed { + None => { + sg.has_errored = true; + let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); + decorate(LintDiagnosticBuilder::new(err)); + } + Some(kind) => { + let lint = match kind { + FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS, + FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, + }; + tcx.struct_span_lint_hir( + lint, + tcx.hir().as_local_hir_id(impl_def_id), + impl_span, + decorate, + ) + } + }; } /// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a diff --git a/src/librustc_trait_selection/traits/specialize/specialization_graph.rs b/src/librustc_trait_selection/traits/specialize/specialization_graph.rs index 17d4a22b9dd55..56b8354d68c05 100644 --- a/src/librustc_trait_selection/traits/specialize/specialization_graph.rs +++ b/src/librustc_trait_selection/traits/specialize/specialization_graph.rs @@ -1,11 +1,11 @@ use super::OverlapError; use crate::traits; -use rustc::ty::fast_reject::{self, SimplifiedType}; -use rustc::ty::{self, TyCtxt, TypeFoldable}; use rustc_hir::def_id::DefId; +use rustc_middle::ty::fast_reject::{self, SimplifiedType}; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; -pub use rustc::traits::specialization_graph::*; +pub use rustc_middle::traits::specialization_graph::*; #[derive(Copy, Clone, Debug)] pub enum FutureCompatOverlapErrorKind { diff --git a/src/librustc_trait_selection/traits/structural_match.rs b/src/librustc_trait_selection/traits/structural_match.rs index 60682f5812917..eb63505b69b41 100644 --- a/src/librustc_trait_selection/traits/structural_match.rs +++ b/src/librustc_trait_selection/traits/structural_match.rs @@ -2,21 +2,23 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::ObligationCause; use crate::traits::{self, ConstPatternStructural, TraitEngine}; -use rustc::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; +use rustc_hir::lang_items::{StructuralPeqTraitLangItem, StructuralTeqTraitLangItem}; +use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::Span; #[derive(Debug)] pub enum NonStructuralMatchTy<'tcx> { Adt(&'tcx AdtDef), Param, + Dynamic, } /// This method traverses the structure of `ty`, trying to find an -/// instance of an ADT (i.e. struct or enum) that was declared without -/// the `#[structural_match]` attribute, or a generic type parameter -/// (which cannot be determined to be `structural_match`). +/// instance of an ADT (i.e. struct or enum) that doesn't implement +/// the structural-match traits, or a generic type parameter +/// (which cannot be determined to be structural-match). /// /// The "structure of a type" includes all components that would be /// considered when doing a pattern match on a constant of that @@ -30,8 +32,8 @@ pub enum NonStructuralMatchTy<'tcx> { /// instantiated generic like `PhantomData`. /// /// The reason we do this search is Rust currently require all ADTs -/// reachable from a constant's type to be annotated with -/// `#[structural_match]`, an attribute which essentially says that +/// reachable from a constant's type to implement the +/// structural-match traits, which essentially say that /// the implementation of `PartialEq::eq` behaves *equivalently* to a /// comparison against the unfolded structure. /// @@ -68,7 +70,7 @@ pub fn type_marked_structural( let mut fulfillment_cx = traits::FulfillmentContext::new(); let cause = ObligationCause::new(span, id, ConstPatternStructural); // require `#[derive(PartialEq)]` - let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap(); + let structural_peq_def_id = infcx.tcx.require_lang_item(StructuralPeqTraitLangItem, Some(span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), @@ -79,7 +81,7 @@ pub fn type_marked_structural( // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) let cause = ObligationCause::new(span, id, ConstPatternStructural); - let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap(); + let structural_teq_def_id = infcx.tcx.require_lang_item(StructuralTeqTraitLangItem, Some(span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), @@ -137,6 +139,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { self.found = Some(NonStructuralMatchTy::Param); return true; // Stop visiting. } + ty::Dynamic(..) => { + self.found = Some(NonStructuralMatchTy::Dynamic); + return true; // Stop visiting. + } ty::RawPtr(..) => { // structural-match ignores substructure of // `*const _`/`*mut _`, so skip `super_visit_with`. diff --git a/src/librustc_trait_selection/traits/util.rs b/src/librustc_trait_selection/traits/util.rs index cd4595e76ccec..eff73cf4a2d8a 100644 --- a/src/librustc_trait_selection/traits/util.rs +++ b/src/librustc_trait_selection/traits/util.rs @@ -3,273 +3,13 @@ use rustc_span::Span; use smallvec::smallvec; use smallvec::SmallVec; -use rustc::ty::outlives::Component; -use rustc::ty::subst::{GenericArg, Subst, SubstsRef}; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; - -fn anonymize_predicate<'tcx>(tcx: TyCtxt<'tcx>, pred: &ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - match *pred { - ty::Predicate::Trait(ref data, constness) => { - ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data), constness) - } - - ty::Predicate::RegionOutlives(ref data) => { - ty::Predicate::RegionOutlives(tcx.anonymize_late_bound_regions(data)) - } - - ty::Predicate::TypeOutlives(ref data) => { - ty::Predicate::TypeOutlives(tcx.anonymize_late_bound_regions(data)) - } - - ty::Predicate::Projection(ref data) => { - ty::Predicate::Projection(tcx.anonymize_late_bound_regions(data)) - } - - ty::Predicate::WellFormed(data) => ty::Predicate::WellFormed(data), - - ty::Predicate::ObjectSafe(data) => ty::Predicate::ObjectSafe(data), - - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) - } - - ty::Predicate::Subtype(ref data) => { - ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)) - } - - ty::Predicate::ConstEvaluatable(def_id, substs) => { - ty::Predicate::ConstEvaluatable(def_id, substs) - } - } -} - -struct PredicateSet<'tcx> { - tcx: TyCtxt<'tcx>, - set: FxHashSet>, -} - -impl PredicateSet<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Self { - Self { tcx, set: Default::default() } - } - - fn insert(&mut self, pred: &ty::Predicate<'tcx>) -> bool { - // We have to be careful here because we want - // - // for<'a> Foo<&'a int> - // - // and - // - // for<'b> Foo<&'b int> - // - // to be considered equivalent. So normalize all late-bound - // regions before we throw things into the underlying set. - self.set.insert(anonymize_predicate(self.tcx, pred)) - } -} - -impl>> Extend for PredicateSet<'tcx> { - fn extend>(&mut self, iter: I) { - for pred in iter { - self.insert(pred.as_ref()); - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// `Elaboration` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Elaboration" is the process of identifying all the predicates that -/// are implied by a source predicate. Currently, this basically means -/// walking the "supertraits" and other similar assumptions. For example, -/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` -/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that -/// `T: Foo`, then we know that `T: 'static`. -pub struct Elaborator<'tcx> { - stack: Vec>, - visited: PredicateSet<'tcx>, -} - -pub fn elaborate_trait_ref<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> Elaborator<'tcx> { - elaborate_predicates(tcx, vec![trait_ref.without_const().to_predicate()]) -} - -pub fn elaborate_trait_refs<'tcx>( - tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator>, -) -> Elaborator<'tcx> { - let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate()).collect(); - elaborate_predicates(tcx, predicates) -} - -pub fn elaborate_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - mut predicates: Vec>, -) -> Elaborator<'tcx> { - let mut visited = PredicateSet::new(tcx); - predicates.retain(|pred| visited.insert(pred)); - Elaborator { stack: predicates, visited } -} - -impl Elaborator<'tcx> { - pub fn filter_to_traits(self) -> FilterToTraits { - FilterToTraits::new(self) - } - - fn elaborate(&mut self, predicate: &ty::Predicate<'tcx>) { - let tcx = self.visited.tcx; - match *predicate { - ty::Predicate::Trait(ref data, _) => { - // Get predicates declared on the trait. - let predicates = tcx.super_predicates_of(data.def_id()); - - let predicates = predicates - .predicates - .iter() - .map(|(pred, _)| pred.subst_supertrait(tcx, &data.to_poly_trait_ref())); - debug!("super_predicates: data={:?} predicates={:?}", data, predicates.clone()); - - // Only keep those bounds that we haven't already seen. - // This is necessary to prevent infinite recursion in some - // cases. One common case is when people define - // `trait Sized: Sized { }` rather than `trait Sized { }`. - let visited = &mut self.visited; - let predicates = predicates.filter(|pred| visited.insert(pred)); - - self.stack.extend(predicates); - } - ty::Predicate::WellFormed(..) => { - // Currently, we do not elaborate WF predicates, - // although we easily could. - } - ty::Predicate::ObjectSafe(..) => { - // Currently, we do not elaborate object-safe - // predicates. - } - ty::Predicate::Subtype(..) => { - // Currently, we do not "elaborate" predicates like `X <: Y`, - // though conceivably we might. - } - ty::Predicate::Projection(..) => { - // Nothing to elaborate in a projection predicate. - } - ty::Predicate::ClosureKind(..) => { - // Nothing to elaborate when waiting for a closure's kind to be inferred. - } - ty::Predicate::ConstEvaluatable(..) => { - // Currently, we do not elaborate const-evaluatable - // predicates. - } - ty::Predicate::RegionOutlives(..) => { - // Nothing to elaborate from `'a: 'b`. - } - ty::Predicate::TypeOutlives(ref data) => { - // We know that `T: 'a` for some type `T`. We can - // often elaborate this. For example, if we know that - // `[U]: 'a`, that implies that `U: 'a`. Similarly, if - // we know `&'a U: 'b`, then we know that `'a: 'b` and - // `U: 'b`. - // - // We can basically ignore bound regions here. So for - // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to - // `'a: 'b`. - - // Ignore `for<'a> T: 'a` -- we might in the future - // consider this as evidence that `T: 'static`, but - // I'm a bit wary of such constructions and so for now - // I want to be conservative. --nmatsakis - let ty_max = data.skip_binder().0; - let r_min = data.skip_binder().1; - if r_min.is_late_bound() { - return; - } - - let visited = &mut self.visited; - let mut components = smallvec![]; - tcx.push_outlives_components(ty_max, &mut components); - self.stack.extend( - components - .into_iter() - .filter_map(|component| match component { - Component::Region(r) => { - if r.is_late_bound() { - None - } else { - Some(ty::Predicate::RegionOutlives(ty::Binder::dummy( - ty::OutlivesPredicate(r, r_min), - ))) - } - } - - Component::Param(p) => { - let ty = tcx.mk_ty_param(p.index, p.name); - Some(ty::Predicate::TypeOutlives(ty::Binder::dummy( - ty::OutlivesPredicate(ty, r_min), - ))) - } - - Component::UnresolvedInferenceVariable(_) => None, - - Component::Projection(_) | Component::EscapingProjection(_) => { - // We can probably do more here. This - // corresponds to a case like `>::U: 'b`. - None - } - }) - .filter(|p| visited.insert(p)), - ); - } - } - } -} - -impl Iterator for Elaborator<'tcx> { - type Item = ty::Predicate<'tcx>; - - fn size_hint(&self) -> (usize, Option) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option> { - // Extract next item from top-most stack frame, if any. - if let Some(pred) = self.stack.pop() { - self.elaborate(&pred); - Some(pred) - } else { - None - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Supertrait iterator -/////////////////////////////////////////////////////////////////////////// - -pub type Supertraits<'tcx> = FilterToTraits>; - -pub fn supertraits<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> Supertraits<'tcx> { - elaborate_trait_ref(tcx, trait_ref).filter_to_traits() -} - -pub fn transitive_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - bounds: impl Iterator>, -) -> Supertraits<'tcx> { - elaborate_trait_refs(tcx, bounds).filter_to_traits() -} +pub use rustc_infer::traits::util::*; /////////////////////////////////////////////////////////////////////////// // `TraitAliasExpander` iterator @@ -341,12 +81,10 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> { pub fn expand_trait_aliases<'tcx>( tcx: TyCtxt<'tcx>, - trait_refs: impl IntoIterator, Span)>, + trait_refs: impl Iterator, Span)>, ) -> TraitAliasExpander<'tcx> { - let items: Vec<_> = trait_refs - .into_iter() - .map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)) - .collect(); + let items: Vec<_> = + trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); TraitAliasExpander { tcx, stack: items } } @@ -451,40 +189,6 @@ impl Iterator for SupertraitDefIds<'tcx> { // Other /////////////////////////////////////////////////////////////////////////// -/// A filter around an iterator of predicates that makes it yield up -/// just trait references. -pub struct FilterToTraits { - base_iterator: I, -} - -impl FilterToTraits { - fn new(base: I) -> FilterToTraits { - FilterToTraits { base_iterator: base } - } -} - -impl<'tcx, I: Iterator>> Iterator for FilterToTraits { - type Item = ty::PolyTraitRef<'tcx>; - - fn next(&mut self) -> Option> { - while let Some(pred) = self.base_iterator.next() { - if let ty::Predicate::Trait(data, _) = pred { - return Some(data.to_poly_trait_ref()); - } - } - None - } - - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.base_iterator.size_hint(); - (0, upper) - } -} - -/////////////////////////////////////////////////////////////////////////// -// Other -/////////////////////////////////////////////////////////////////////////// - /// Instantiate all bound parameters of the impl with the given substs, /// returning the resulting trait ref and all obligations that arise. /// The obligations are closed under normalization. @@ -493,7 +197,7 @@ pub fn impl_trait_ref_and_oblig<'a, 'tcx>( param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, impl_substs: SubstsRef<'tcx>, -) -> (ty::TraitRef<'tcx>, Vec>) { +) -> (ty::TraitRef<'tcx>, impl Iterator>) { let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs); let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = @@ -504,36 +208,29 @@ pub fn impl_trait_ref_and_oblig<'a, 'tcx>( let Normalized { value: predicates, obligations: normalization_obligations2 } = super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates); let impl_obligations = - predicates_for_generics(ObligationCause::dummy(), 0, param_env, &predicates); + predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates); - let impl_obligations: Vec<_> = impl_obligations - .into_iter() - .chain(normalization_obligations1) - .chain(normalization_obligations2) - .collect(); + let impl_obligations = impl_obligations + .chain(normalization_obligations1.into_iter()) + .chain(normalization_obligations2.into_iter()); (impl_trait_ref, impl_obligations) } -/// See [`super::obligations_for_generics`]. pub fn predicates_for_generics<'tcx>( cause: ObligationCause<'tcx>, recursion_depth: usize, param_env: ty::ParamEnv<'tcx>, - generic_bounds: &ty::InstantiatedPredicates<'tcx>, -) -> Vec> { + generic_bounds: ty::InstantiatedPredicates<'tcx>, +) -> impl Iterator> { debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); - generic_bounds - .predicates - .iter() - .map(|&predicate| Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }) - .collect() + generic_bounds.predicates.into_iter().map(move |predicate| Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }) } pub fn predicate_for_trait_ref<'tcx>( @@ -587,7 +284,7 @@ pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<' // Count number of methods and add them to the total offset. // Skip over associated types and constants. for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() { - if trait_item.kind == ty::AssocKind::Method { + if trait_item.kind == ty::AssocKind::Fn { entries += 1; } } @@ -609,10 +306,10 @@ pub fn get_vtable_index_of_object_method( for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() { if trait_item.def_id == method_def_id { // The item with the ID we were given really ought to be a method. - assert_eq!(trait_item.kind, ty::AssocKind::Method); + assert_eq!(trait_item.kind, ty::AssocKind::Fn); return entries; } - if trait_item.kind == ty::AssocKind::Method { + if trait_item.kind == ty::AssocKind::Fn { entries += 1; } } @@ -651,22 +348,8 @@ pub fn generator_trait_ref_and_outputs( ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty)) } -pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool { - match tcx.hir().as_local_hir_id(node_item_def_id) { - Some(hir_id) => { - let item = tcx.hir().expect_item(hir_id); - if let hir::ItemKind::Impl { defaultness, .. } = item.kind { - defaultness.is_default() - } else { - false - } - } - None => tcx.impl_defaultness(node_item_def_id).is_default(), - } -} - pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { - assoc_item.defaultness.is_final() && !impl_is_default(tcx, assoc_item.container.id()) + assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final() } pub enum TupleArgumentsFlag { diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs index ac2da006df35d..4d3bbfa77c37d 100644 --- a/src/librustc_trait_selection/traits/wf.rs +++ b/src/librustc_trait_selection/traits/wf.rs @@ -1,13 +1,13 @@ use crate::infer::InferCtxt; use crate::opaque_types::required_region_bounds; -use crate::traits::{self, AssocTypeBoundData}; -use rustc::middle::lang_items; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; +use crate::traits; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_span::symbol::{kw, Ident}; +use rustc_hir::lang_items; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::Span; +use std::rc::Rc; /// Returns the set of obligations needed to make `ty` well-formed. /// If `ty` contains unresolved inference variables, this may include @@ -22,16 +22,27 @@ pub fn obligations<'a, 'tcx>( ty: Ty<'tcx>, span: Span, ) -> Option>> { + // Handle the "livelock" case (see comment above) by bailing out if necessary. + let 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". + return None; + } + + resolved_ty + } + _ => ty, + }; + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; - if wf.compute(ty) { - debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); - - let result = wf.normalize(); - debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result); - Some(result) - } else { - None // no progress made, return None - } + wf.compute(ty); + debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); + + let result = wf.normalize(); + debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result); + Some(result) } /// Returns the obligations that make this trait reference @@ -91,6 +102,10 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(ty); } } + ty::Predicate::ConstEquate(c1, c2) => { + wf.compute(c1.ty); + wf.compute(c2.ty); + } } wf.normalize() @@ -134,6 +149,72 @@ enum Elaborate { None, } +fn extend_cause_with_original_assoc_item_obligation<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: &ty::TraitRef<'tcx>, + item: Option<&hir::Item<'tcx>>, + cause: &mut traits::ObligationCause<'tcx>, + pred: &ty::Predicate<'_>, + mut trait_assoc_items: impl Iterator, +) { + debug!( + "extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}", + trait_ref, item, cause, pred + ); + let items = match item { + Some(hir::Item { kind: hir::ItemKind::Impl { items, .. }, .. }) => items, + _ => return, + }; + let fix_span = + |impl_item_ref: &hir::ImplItemRef<'_>| match tcx.hir().impl_item(impl_item_ref.id).kind { + hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::TyAlias(ty) => ty.span, + _ => impl_item_ref.span, + }; + match pred { + ty::Predicate::Projection(proj) => { + // The obligation comes not from the current `impl` nor the `trait` being + // implemented, but rather from a "second order" obligation, like in + // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`. + let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); + if let Some(impl_item_span) = + items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span) + { + cause.span = impl_item_span; + } else { + let kind = &proj.ty().skip_binder().kind; + if let ty::Projection(projection_ty) = kind { + // This happens when an associated type has a projection coming from another + // associated type. See `traits-assoc-type-in-supertrait-bad.rs`. + let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id); + if let Some(impl_item_span) = + items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span) + { + cause.span = impl_item_span; + } + } + } + } + ty::Predicate::Trait(pred, _) => { + // An associated item obligation born out of the `trait` failed to be met. An example + // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`. + debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred); + if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = + &pred.skip_binder().self_ty().kind + { + if let Some(impl_item_span) = trait_assoc_items + .find(|i| i.def_id == *item_def_id) + .and_then(|trait_assoc_item| { + items.iter().find(|i| i.ident == trait_assoc_item.ident).map(fix_span) + }) + { + cause.span = impl_item_span; + } + } + } + _ => {} + } +} + impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) @@ -160,181 +241,40 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let tcx = self.infcx.tcx; let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); + debug!("compute_trait_ref obligations {:?}", obligations); let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; - let item = &self.item; - let extend_cause_with_original_assoc_item_obligation = - |cause: &mut traits::ObligationCause<'_>, - pred: &ty::Predicate<'_>, - trait_assoc_items: &[ty::AssocItem]| { - let trait_item = tcx - .hir() - .as_local_hir_id(trait_ref.def_id) - .and_then(|trait_id| tcx.hir().find(trait_id)); - let (trait_name, trait_generics) = match trait_item { - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Trait(.., generics, _, _), - .. - })) - | Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::TraitAlias(generics, _), - .. - })) => (Some(ident), Some(generics)), - _ => (None, None), - }; + let item = self.item; - let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span)); - match pred { - ty::Predicate::Projection(proj) => { - // The obligation comes not from the current `impl` nor the `trait` being - // implemented, but rather from a "second order" obligation, like in - // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`: - // - // error[E0271]: type mismatch resolving `::Ok == ()` - // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 - // | - // LL | type Ok; - // | -- associated type defined here - // ... - // LL | impl Bar for Foo { - // | ---------------- in this `impl` item - // LL | type Ok = (); - // | ^^^^^^^^^^^^^ expected `u32`, found `()` - // | - // = note: expected type `u32` - // found type `()` - // - // FIXME: we would want to point a span to all places that contributed to this - // obligation. In the case above, it should be closer to: - // - // error[E0271]: type mismatch resolving `::Ok == ()` - // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 - // | - // LL | type Ok; - // | -- associated type defined here - // LL | type Sibling: Bar2; - // | -------------------------------- obligation set here - // ... - // LL | impl Bar for Foo { - // | ---------------- in this `impl` item - // LL | type Ok = (); - // | ^^^^^^^^^^^^^ expected `u32`, found `()` - // ... - // LL | impl Bar2 for Foo2 { - // | ---------------- in this `impl` item - // LL | type Ok = u32; - // | -------------- obligation set here - // | - // = note: expected type `u32` - // found type `()` - if let Some(hir::ItemKind::Impl { items, .. }) = item.map(|i| &i.kind) { - let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); - if let Some(impl_item) = - items.iter().find(|item| item.ident == trait_assoc_item.ident) - { - cause.span = impl_item.span; - cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData { - impl_span: item_span, - original: trait_assoc_item.ident.span, - bounds: vec![], - })); - } - } - } - ty::Predicate::Trait(proj, _) => { - // An associated item obligation born out of the `trait` failed to be met. - // Point at the `impl` that failed the obligation, the associated item that - // needed to meet the obligation, and the definition of that associated item, - // which should hold the obligation in most cases. An example can be seen in - // `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`: - // - // error[E0277]: the trait bound `bool: Bar` is not satisfied - // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 - // | - // LL | type Assoc: Bar; - // | ----- associated type defined here - // ... - // LL | impl Foo for () { - // | --------------- in this `impl` item - // LL | type Assoc = bool; - // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` - // - // If the obligation comes from the where clause in the `trait`, we point at it: - // - // error[E0277]: the trait bound `bool: Bar` is not satisfied - // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 - // | - // | trait Foo where >::Assoc: Bar { - // | -------------------------- restricted in this bound - // LL | type Assoc; - // | ----- associated type defined here - // ... - // LL | impl Foo for () { - // | --------------- in this `impl` item - // LL | type Assoc = bool; - // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` - if let ( - ty::Projection(ty::ProjectionTy { item_def_id, .. }), - Some(hir::ItemKind::Impl { items, .. }), - ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) - { - if let Some((impl_item, trait_assoc_item)) = trait_assoc_items - .iter() - .find(|i| i.def_id == *item_def_id) - .and_then(|trait_assoc_item| { - items - .iter() - .find(|i| i.ident == trait_assoc_item.ident) - .map(|impl_item| (impl_item, trait_assoc_item)) - }) - { - let bounds = trait_generics - .map(|generics| { - get_generic_bound_spans( - &generics, - trait_name, - trait_assoc_item.ident, - ) - }) - .unwrap_or_else(Vec::new); - cause.span = impl_item.span; - cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData { - impl_span: item_span, - original: trait_assoc_item.ident.span, - bounds, - })); - } - } - } - _ => {} - } - }; + let extend = |obligation: traits::PredicateObligation<'tcx>| { + let mut cause = cause.clone(); + if let Some(parent_trait_ref) = obligation.predicate.to_opt_poly_trait_ref() { + let derived_cause = traits::DerivedObligationCause { + parent_trait_ref, + parent_code: Rc::new(obligation.cause.code.clone()), + }; + cause.code = traits::ObligationCauseCode::DerivedObligation(derived_cause); + } + extend_cause_with_original_assoc_item_obligation( + tcx, + trait_ref, + item, + &mut cause, + &obligation.predicate, + tcx.associated_items(trait_ref.def_id).in_definition_order(), + ); + traits::Obligation::new(cause, param_env, obligation.predicate) + }; if let Elaborate::All = elaborate { - // FIXME: Make `extend_cause_with_original_assoc_item_obligation` take an iterator - // instead of a slice. - let trait_assoc_items: Vec<_> = - tcx.associated_items(trait_ref.def_id).in_definition_order().copied().collect(); - - let predicates = obligations.iter().map(|obligation| obligation.predicate).collect(); - let implied_obligations = traits::elaborate_predicates(tcx, predicates); - let implied_obligations = implied_obligations.map(|pred| { - let mut cause = cause.clone(); - extend_cause_with_original_assoc_item_obligation( - &mut cause, - &pred, - &*trait_assoc_items, - ); - traits::Obligation::new(cause, param_env, pred) - }); + let implied_obligations = traits::util::elaborate_obligations(tcx, obligations); + let implied_obligations = implied_obligations.map(extend); self.out.extend(implied_obligations); + } else { + self.out.extend(obligations); } - self.out.extend(obligations); - self.out.extend(trait_ref.substs.types().filter(|ty| !ty.has_escaping_bound_vars()).map( |ty| traits::Obligation::new(cause.clone(), param_env, ty::Predicate::WellFormed(ty)), )); @@ -386,14 +326,23 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } - /// Pushes new obligations into `out`. Returns `true` if it was able - /// to generate all the predicates needed to validate that `ty0` - /// is WF. Returns false if `ty0` is an unresolved type variable, - /// in which case we are not able to simplify at all. - fn compute(&mut self, ty0: Ty<'tcx>) -> bool { - let mut subtys = ty0.walk(); + /// Pushes all the predicates needed to validate that `ty` is WF into `out`. + fn compute(&mut self, ty: Ty<'tcx>) { + let mut walker = ty.walk(); let param_env = self.param_env; - while let Some(ty) = subtys.next() { + while let Some(arg) = walker.next() { + let ty = match arg.unpack() { + GenericArgKind::Type(ty) => ty, + + // No WF constraints for lifetimes being present, any outlives + // obligations are handled by the parent (e.g. `ty::Ref`). + GenericArgKind::Lifetime(_) => continue, + + // FIXME(eddyb) this is wrong and needs to be replaced + // (see https://github.com/rust-lang/rust/pull/70107). + GenericArgKind::Const(_) => continue, + }; + match ty.kind { ty::Bool | ty::Char @@ -411,12 +360,19 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // WfScalar, WfParameter, etc } + // Can only infer to `ty::Int(_) | ty::Uint(_)`. + ty::Infer(ty::IntVar(_)) => {} + + // Can only infer to `ty::Float(_)`. + ty::Infer(ty::FloatVar(_)) => {} + ty::Slice(subty) => { self.require_sized(subty, traits::SliceOrArrayElem); } ty::Array(subty, len) => { self.require_sized(subty, traits::SliceOrArrayElem); + // FIXME(eddyb) handle `GenericArgKind::Const` above instead. self.compute_array_len(*len); } @@ -433,12 +389,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } ty::Projection(data) => { - subtys.skip_current_subtree(); // subtree handled by compute_projection + walker.skip_current_subtree(); // subtree handled by compute_projection self.compute_projection(data); } - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - ty::Adt(def, substs) => { // WfNominalType let obligations = self.nominal_obligations(def.did, substs); @@ -504,8 +458,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // are not directly inspecting closure types // anyway, except via auto trait matching (which // only inspects the upvar types). - subtys.skip_current_subtree(); // subtree handled by compute_projection + walker.skip_current_subtree(); // subtree handled below for upvar_ty in substs.as_closure().upvar_tys() { + // FIXME(eddyb) add the type to `walker` instead of recursing. self.compute(upvar_ty); } } @@ -558,44 +513,31 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // // 1. Check if they have been resolved, and if so proceed with // THAT type. - // 2. If not, check whether this is the type that we - // started with (ty0). In that case, we've made no - // progress at all, so return false. Otherwise, - // we've at least simplified things (i.e., we went - // from `Vec<$0>: WF` to `$0: WF`, so we can + // 2. If not, we've at least simplified things (e.g., we went + // 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" + // prevention, which happens before this can be reached. ty::Infer(_) => { let ty = self.infcx.shallow_resolve(ty); - if let ty::Infer(_) = ty.kind { - // not yet resolved... - if ty == ty0 { - // ...this is the type we started from! no progress. - return false; - } - + if let ty::Infer(ty::TyVar(_)) = ty.kind { + // Not yet resolved, but we've made progress. let cause = self.cause(traits::MiscObligation); - self.out.push( - // ...not the type we started from, so we made progress. - traits::Obligation::new( - cause, - self.param_env, - ty::Predicate::WellFormed(ty), - ), - ); + self.out.push(traits::Obligation::new( + cause, + param_env, + ty::Predicate::WellFormed(ty), + )); } else { - // Yes, resolved, proceed with the - // result. Should never return false because - // `ty` is not a Infer. - assert!(self.compute(ty)); + // Yes, resolved, proceed with the result. + // FIXME(eddyb) add the type to `walker` instead of recursing. + self.compute(ty); } } } } - - // if we made it through that loop above, we made progress! - true } fn nominal_obligations( @@ -604,11 +546,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { substs: SubstsRef<'tcx>, ) -> Vec> { let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs); - let cause = self.cause(traits::ItemObligation(def_id)); predicates .predicates .into_iter() - .map(|pred| traits::Obligation::new(cause.clone(), self.param_env, pred)) + .zip(predicates.spans.into_iter()) + .map(|(pred, span)| { + let cause = self.cause(traits::BindingObligation(def_id, span)); + traits::Obligation::new(cause, self.param_env, pred) + }) .filter(|pred| !pred.has_escaping_bound_vars()) .collect() } @@ -685,69 +630,13 @@ pub fn object_region_bounds<'tcx>( // a placeholder type. let open_ty = tcx.mk_ty_infer(ty::FreshTy(0)); - let predicates = existential_predicates - .iter() - .filter_map(|predicate| { - if let ty::ExistentialPredicate::Projection(_) = *predicate.skip_binder() { - None - } else { - Some(predicate.with_self_ty(tcx, open_ty)) - } - }) - .collect(); - - required_region_bounds(tcx, open_ty, predicates) -} - -/// Find the span of a generic bound affecting an associated type. -fn get_generic_bound_spans( - generics: &hir::Generics<'_>, - trait_name: Option<&Ident>, - assoc_item_name: Ident, -) -> Vec { - let mut bounds = vec![]; - for clause in generics.where_clause.predicates.iter() { - if let hir::WherePredicate::BoundPredicate(pred) = clause { - match &pred.bounded_ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => { - let mut s = path.segments.iter(); - if let (a, Some(b), None) = (s.next(), s.next(), s.next()) { - if a.map(|s| &s.ident) == trait_name - && b.ident == assoc_item_name - && is_self_path(&ty.kind) - { - // `::Bar` - bounds.push(pred.span); - } - } - } - hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => { - if segment.ident == assoc_item_name { - if is_self_path(&ty.kind) { - // `Self::Bar` - bounds.push(pred.span); - } - } - } - _ => {} - } + let predicates = existential_predicates.iter().filter_map(|predicate| { + if let ty::ExistentialPredicate::Projection(_) = *predicate.skip_binder() { + None + } else { + Some(predicate.with_self_ty(tcx, open_ty)) } - } - bounds -} + }); -fn is_self_path(kind: &hir::TyKind<'_>) -> bool { - match kind { - hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { - let mut s = path.segments.iter(); - if let (Some(segment), None) = (s.next(), s.next()) { - if segment.ident.name == kw::SelfUpper { - // `type(Self)` - return true; - } - } - } - _ => {} - } - false + required_region_bounds(tcx, open_ty, predicates) } diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml index 5e33efb1cf9b5..e485bc2929bdb 100644 --- a/src/librustc_traits/Cargo.toml +++ b/src/librustc_traits/Cargo.toml @@ -10,13 +10,15 @@ path = "lib.rs" [dependencies] log = { version = "0.4" } -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_hir = { path = "../librustc_hir" } -rustc_macros = { path = "../librustc_macros" } -rustc_target = { path = "../librustc_target" } +rustc_index = { path = "../librustc_index" } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } +chalk-ir = "0.10.0" +chalk-rust-ir = "0.10.0" +chalk-solve = "0.10.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_infer = { path = "../librustc_infer" } rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_traits/chalk/db.rs b/src/librustc_traits/chalk/db.rs new file mode 100644 index 0000000000000..a2aee9b6ef74d --- /dev/null +++ b/src/librustc_traits/chalk/db.rs @@ -0,0 +1,517 @@ +//! Provides the `RustIrDatabase` implementation for `chalk-solve` +//! +//! The purpose of the `chalk_solve::RustIrDatabase` is to get data about +//! specific types, such as bounds, where clauses, or fields. This file contains +//! the minimal logic to assemble the types for `chalk-solve` by calling out to +//! either the `TyCtxt` (for information about types) or +//! `crate::chalk::lowering` (to lower rustc types into Chalk types). + +use rustc_middle::traits::{ChalkRustDefId as RustDefId, ChalkRustInterner as RustInterner}; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt}; + +use rustc_hir::def_id::DefId; + +use rustc_span::symbol::sym; + +use std::fmt; +use std::sync::Arc; + +use crate::chalk::lowering::LowerInto; + +pub struct RustIrDatabase<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub interner: RustInterner<'tcx>, +} + +impl fmt::Debug for RustIrDatabase<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RustIrDatabase") + } +} + +impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'tcx> { + fn interner(&self) -> &RustInterner<'tcx> { + &self.interner + } + + fn associated_ty_data( + &self, + assoc_type_id: chalk_ir::AssocTypeId>, + ) -> Arc>> { + let def_id = match assoc_type_id.0 { + RustDefId::AssocTy(def_id) => def_id, + _ => bug!("Did not use `AssocTy` variant when expecting associated type."), + }; + let assoc_item = self.tcx.associated_item(def_id); + let trait_def_id = match assoc_item.container { + AssocItemContainer::TraitContainer(def_id) => def_id, + _ => unimplemented!("Not possible??"), + }; + match assoc_item.kind { + AssocKind::Type => {} + _ => unimplemented!("Not possible??"), + } + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + // FIXME(chalk): this really isn't right I don't think. The functions + // for GATs are a bit hard to figure out. Are these supposed to be where + // clauses or bounds? + let predicates = self.tcx.predicates_defined_on(def_id).predicates; + let where_clauses: Vec<_> = predicates + .iter() + .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); + + Arc::new(chalk_rust_ir::AssociatedTyDatum { + trait_id: chalk_ir::TraitId(RustDefId::Trait(trait_def_id)), + id: assoc_type_id, + name: (), + binders: chalk_ir::Binders::new( + binders, + chalk_rust_ir::AssociatedTyDatumBound { bounds: vec![], where_clauses }, + ), + }) + } + + fn trait_datum( + &self, + trait_id: chalk_ir::TraitId>, + ) -> Arc>> { + let def_id = match trait_id.0 { + RustDefId::Trait(def_id) => def_id, + _ => bug!("Did not use `Trait` variant when expecting trait."), + }; + let trait_def = self.tcx.trait_def(def_id); + + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + let predicates = self.tcx.predicates_defined_on(def_id).predicates; + let where_clauses: Vec<_> = predicates + .iter() + .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); + + let well_known = + if self.tcx.lang_items().sized_trait().map(|t| def_id == t).unwrap_or(false) { + Some(chalk_rust_ir::WellKnownTrait::SizedTrait) + } else if self.tcx.lang_items().copy_trait().map(|t| def_id == t).unwrap_or(false) { + Some(chalk_rust_ir::WellKnownTrait::CopyTrait) + } else if self.tcx.lang_items().clone_trait().map(|t| def_id == t).unwrap_or(false) { + Some(chalk_rust_ir::WellKnownTrait::CloneTrait) + } else { + None + }; + Arc::new(chalk_rust_ir::TraitDatum { + id: trait_id, + binders: chalk_ir::Binders::new( + binders, + chalk_rust_ir::TraitDatumBound { where_clauses }, + ), + flags: chalk_rust_ir::TraitFlags { + auto: trait_def.has_auto_impl, + marker: trait_def.is_marker, + upstream: !def_id.is_local(), + fundamental: self.tcx.has_attr(def_id, sym::fundamental), + non_enumerable: true, + coinductive: false, + }, + associated_ty_ids: vec![], + well_known, + }) + } + + fn struct_datum( + &self, + struct_id: chalk_ir::StructId>, + ) -> Arc>> { + match struct_id.0 { + RustDefId::Adt(adt_def_id) => { + let adt_def = self.tcx.adt_def(adt_def_id); + + let bound_vars = bound_vars_for_item(self.tcx, adt_def_id); + let binders = binders_for(&self.interner, bound_vars); + + let predicates = self.tcx.predicates_of(adt_def_id).predicates; + let where_clauses: Vec<_> = predicates + .iter() + .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)) + .collect(); + let fields = match adt_def.adt_kind() { + ty::AdtKind::Struct | ty::AdtKind::Union => { + let variant = adt_def.non_enum_variant(); + variant + .fields + .iter() + .map(|field| { + self.tcx + .type_of(field.did) + .subst(self.tcx, bound_vars) + .lower_into(&self.interner) + }) + .collect() + } + // FIXME(chalk): handle enums; force_impl_for requires this + ty::AdtKind::Enum => vec![], + }; + let struct_datum = Arc::new(chalk_rust_ir::StructDatum { + id: struct_id, + binders: chalk_ir::Binders::new( + binders, + chalk_rust_ir::StructDatumBound { fields, where_clauses }, + ), + flags: chalk_rust_ir::StructFlags { + upstream: !adt_def_id.is_local(), + fundamental: adt_def.is_fundamental(), + }, + }); + struct_datum + } + RustDefId::Ref(_) => Arc::new(chalk_rust_ir::StructDatum { + id: struct_id, + binders: chalk_ir::Binders::new( + chalk_ir::ParameterKinds::from( + &self.interner, + vec![ + chalk_ir::ParameterKind::Lifetime(()), + chalk_ir::ParameterKind::Ty(()), + ], + ), + chalk_rust_ir::StructDatumBound { fields: vec![], where_clauses: vec![] }, + ), + flags: chalk_rust_ir::StructFlags { upstream: false, fundamental: false }, + }), + RustDefId::Array | RustDefId::Slice => Arc::new(chalk_rust_ir::StructDatum { + id: struct_id, + binders: chalk_ir::Binders::new( + chalk_ir::ParameterKinds::from( + &self.interner, + Some(chalk_ir::ParameterKind::Ty(())), + ), + chalk_rust_ir::StructDatumBound { fields: vec![], where_clauses: vec![] }, + ), + flags: chalk_rust_ir::StructFlags { upstream: false, fundamental: false }, + }), + RustDefId::Str | RustDefId::Never | RustDefId::FnDef(_) => { + Arc::new(chalk_rust_ir::StructDatum { + id: struct_id, + binders: chalk_ir::Binders::new( + chalk_ir::ParameterKinds::new(&self.interner), + chalk_rust_ir::StructDatumBound { fields: vec![], where_clauses: vec![] }, + ), + flags: chalk_rust_ir::StructFlags { upstream: false, fundamental: false }, + }) + } + + _ => bug!("Used not struct variant when expecting struct variant."), + } + } + + fn impl_datum( + &self, + impl_id: chalk_ir::ImplId>, + ) -> Arc>> { + let def_id = match impl_id.0 { + RustDefId::Impl(def_id) => def_id, + _ => bug!("Did not use `Impl` variant when expecting impl."), + }; + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + + let trait_ref = self.tcx.impl_trait_ref(def_id).expect("not an impl"); + let trait_ref = trait_ref.subst(self.tcx, bound_vars); + + let predicates = self.tcx.predicates_of(def_id).predicates; + let where_clauses: Vec<_> = predicates + .iter() + .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); + + let value = chalk_rust_ir::ImplDatumBound { + trait_ref: trait_ref.lower_into(&self.interner), + where_clauses, + }; + + Arc::new(chalk_rust_ir::ImplDatum { + polarity: chalk_rust_ir::Polarity::Positive, + binders: chalk_ir::Binders::new(binders, value), + impl_type: chalk_rust_ir::ImplType::Local, + associated_ty_value_ids: vec![], + }) + } + + fn impls_for_trait( + &self, + trait_id: chalk_ir::TraitId>, + parameters: &[chalk_ir::Parameter>], + ) -> Vec>> { + let def_id: DefId = match trait_id.0 { + RustDefId::Trait(def_id) => def_id, + _ => bug!("Did not use `Trait` variant when expecting trait."), + }; + + // FIXME(chalk): use TraitDef::for_each_relevant_impl, but that will + // require us to be able to interconvert `Ty<'tcx>`, and we're + // not there yet. + + let all_impls = self.tcx.all_impls(def_id); + let matched_impls = all_impls.filter(|impl_def_id| { + use chalk_ir::could_match::CouldMatch; + let trait_ref = self.tcx.impl_trait_ref(*impl_def_id).unwrap(); + let bound_vars = bound_vars_for_item(self.tcx, *impl_def_id); + + let self_ty = trait_ref.self_ty(); + let self_ty = self_ty.subst(self.tcx, bound_vars); + let lowered_ty = self_ty.lower_into(&self.interner); + + parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty) + }); + + let impls = matched_impls + .map(|matched_impl| chalk_ir::ImplId(RustDefId::Impl(matched_impl))) + .collect(); + impls + } + + fn impl_provided_for( + &self, + auto_trait_id: chalk_ir::TraitId>, + struct_id: chalk_ir::StructId>, + ) -> bool { + let trait_def_id: DefId = match auto_trait_id.0 { + RustDefId::Trait(def_id) => def_id, + _ => bug!("Did not use `Trait` variant when expecting trait."), + }; + let adt_def_id: DefId = match struct_id.0 { + RustDefId::Adt(def_id) => def_id, + _ => bug!("Did not use `Adt` variant when expecting adt."), + }; + let all_impls = self.tcx.all_impls(trait_def_id); + for impl_def_id in all_impls { + let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); + let self_ty = trait_ref.self_ty(); + match self_ty.kind { + ty::Adt(adt_def, _) => { + if adt_def.did == adt_def_id { + return true; + } + } + _ => {} + } + } + false + } + + fn associated_ty_value( + &self, + associated_ty_id: chalk_rust_ir::AssociatedTyValueId>, + ) -> Arc>> { + let def_id = match associated_ty_id.0 { + RustDefId::AssocTy(def_id) => def_id, + _ => bug!("Did not use `AssocTy` variant when expecting associated type."), + }; + let assoc_item = self.tcx.associated_item(def_id); + let impl_id = match assoc_item.container { + AssocItemContainer::TraitContainer(def_id) => def_id, + _ => unimplemented!("Not possible??"), + }; + match assoc_item.kind { + AssocKind::Type => {} + _ => unimplemented!("Not possible??"), + } + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + let ty = self.tcx.type_of(def_id); + + Arc::new(chalk_rust_ir::AssociatedTyValue { + impl_id: chalk_ir::ImplId(RustDefId::Impl(impl_id)), + associated_ty_id: chalk_ir::AssocTypeId(RustDefId::AssocTy(def_id)), + value: chalk_ir::Binders::new( + binders, + chalk_rust_ir::AssociatedTyValueBound { ty: ty.lower_into(&self.interner) }, + ), + }) + } + + fn custom_clauses(&self) -> Vec>> { + vec![] + } + + fn local_impls_to_coherence_check( + &self, + _trait_id: chalk_ir::TraitId>, + ) -> Vec>> { + unimplemented!() + } + + fn opaque_ty_data( + &self, + _id: chalk_ir::OpaqueTyId>, + ) -> Arc>> { + unimplemented!() + } + + /// Since Chalk can't handle all Rust types currently, we have to handle + /// some specially for now. Over time, these `Some` returns will change to + /// `None` and eventually this function will be removed. + fn force_impl_for( + &self, + well_known: chalk_rust_ir::WellKnownTrait, + ty: &chalk_ir::TyData>, + ) -> Option { + use chalk_ir::TyData::*; + match well_known { + chalk_rust_ir::WellKnownTrait::SizedTrait => match ty { + Apply(apply) => match apply.name { + chalk_ir::TypeName::Struct(chalk_ir::StructId(rust_def_id)) => { + use rustc_middle::traits::ChalkRustDefId::*; + match rust_def_id { + Never | Array | RawPtr | FnDef(_) | Ref(_) => Some(true), + + Adt(adt_def_id) => { + let adt_def = self.tcx.adt_def(adt_def_id); + match adt_def.adt_kind() { + ty::AdtKind::Struct | ty::AdtKind::Union => None, + ty::AdtKind::Enum => { + let constraint = self.tcx.adt_sized_constraint(adt_def_id); + if !constraint.0.is_empty() { + unimplemented!() + } else { + Some(true) + } + } + } + } + + Str | Slice => Some(false), + + Trait(_) | Impl(_) | AssocTy(_) => panic!(), + } + } + _ => None, + }, + Dyn(_) | Alias(_) | Placeholder(_) | Function(_) | InferenceVar(_) + | BoundVar(_) => None, + }, + chalk_rust_ir::WellKnownTrait::CopyTrait + | chalk_rust_ir::WellKnownTrait::CloneTrait => match ty { + Apply(apply) => match apply.name { + chalk_ir::TypeName::Struct(chalk_ir::StructId(rust_def_id)) => { + use rustc_middle::traits::ChalkRustDefId::*; + match rust_def_id { + Never | RawPtr | Ref(_) | Str | Slice => Some(false), + FnDef(_) | Array => Some(true), + Adt(adt_def_id) => { + let adt_def = self.tcx.adt_def(adt_def_id); + match adt_def.adt_kind() { + ty::AdtKind::Struct | ty::AdtKind::Union => None, + ty::AdtKind::Enum => { + let constraint = self.tcx.adt_sized_constraint(adt_def_id); + if !constraint.0.is_empty() { + unimplemented!() + } else { + Some(true) + } + } + } + } + Trait(_) | Impl(_) | AssocTy(_) => panic!(), + } + } + _ => None, + }, + Dyn(_) | Alias(_) | Placeholder(_) | Function(_) | InferenceVar(_) + | BoundVar(_) => None, + }, + chalk_rust_ir::WellKnownTrait::DropTrait => None, + } + } + + fn program_clauses_for_env( + &self, + environment: &chalk_ir::Environment>, + ) -> chalk_ir::ProgramClauses> { + chalk_solve::program_clauses_for_env(self, environment) + } + + fn well_known_trait_id( + &self, + well_known_trait: chalk_rust_ir::WellKnownTrait, + ) -> Option>> { + use chalk_rust_ir::WellKnownTrait::*; + let t = match well_known_trait { + SizedTrait => self + .tcx + .lang_items() + .sized_trait() + .map(|t| chalk_ir::TraitId(RustDefId::Trait(t))) + .unwrap(), + CopyTrait => self + .tcx + .lang_items() + .copy_trait() + .map(|t| chalk_ir::TraitId(RustDefId::Trait(t))) + .unwrap(), + CloneTrait => self + .tcx + .lang_items() + .clone_trait() + .map(|t| chalk_ir::TraitId(RustDefId::Trait(t))) + .unwrap(), + DropTrait => self + .tcx + .lang_items() + .drop_trait() + .map(|t| chalk_ir::TraitId(RustDefId::Trait(t))) + .unwrap(), + }; + Some(t) + } +} + +/// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked +/// var bound at index `0`. For types, we use a `BoundVar` index equal to +/// the type parameter index. For regions, we use the `BoundRegion::BrNamed` +/// variant (which has a `DefId`). +fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { + InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind { + ty::GenericParamDefKind::Type { .. } => tcx + .mk_ty(ty::Bound( + ty::INNERMOST, + ty::BoundTy { + var: ty::BoundVar::from(param.index), + kind: ty::BoundTyKind::Param(param.name), + }, + )) + .into(), + + ty::GenericParamDefKind::Lifetime => tcx + .mk_region(ty::RegionKind::ReLateBound( + ty::INNERMOST, + ty::BoundRegion::BrAnon(substs.len() as u32), + )) + .into(), + + ty::GenericParamDefKind::Const => tcx + .mk_const(ty::Const { + val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), + ty: tcx.type_of(param.def_id), + }) + .into(), + }) +} + +fn binders_for<'tcx>( + interner: &RustInterner<'tcx>, + bound_vars: SubstsRef<'tcx>, +) -> chalk_ir::ParameterKinds> { + chalk_ir::ParameterKinds::from( + interner, + bound_vars.iter().map(|arg| match arg.unpack() { + ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::ParameterKind::Lifetime(()), + ty::subst::GenericArgKind::Type(_ty) => chalk_ir::ParameterKind::Ty(()), + ty::subst::GenericArgKind::Const(_const) => chalk_ir::ParameterKind::Ty(()), + }), + ) +} diff --git a/src/librustc_traits/chalk/lowering.rs b/src/librustc_traits/chalk/lowering.rs new file mode 100644 index 0000000000000..184b9a9dc1040 --- /dev/null +++ b/src/librustc_traits/chalk/lowering.rs @@ -0,0 +1,720 @@ +//! Contains the logic to lower rustc types into Chalk types +//! +//! In many cases there is a 1:1 relationship between a rustc type and a Chalk type. +//! For example, a `SubstsRef` maps almost directly to a `Substitution`. In some +//! other cases, such as `Param`s, there is no Chalk type, so we have to handle +//! accordingly. +//! +//! ## `Ty` lowering +//! Much of the `Ty` lowering is 1:1 with Chalk. (Or will be eventually). A +//! helpful table for what types lower to what can be found in the +//! [Chalk book](http://rust-lang.github.io/chalk/book/types/rust_types.html). +//! The most notable difference lies with `Param`s. To convert from rustc to +//! Chalk, we eagerly and deeply convert `Param`s to placeholders (in goals) or +//! bound variables (for clause generation through functions in `db`). +//! +//! ## `Region` lowering +//! Regions are handled in rustc and Chalk is quite differently. In rustc, there +//! is a difference between "early bound" and "late bound" regions, where only +//! the late bound regions have a `DebruijnIndex`. Moreover, in Chalk all +//! regions (Lifetimes) have an associated index. In rustc, only `BrAnon`s have +//! an index, whereas `BrNamed` don't. In order to lower regions to Chalk, we +//! convert all regions into `BrAnon` late-bound regions. +//! +//! ## `Const` lowering +//! Chalk doesn't handle consts currently, so consts are currently lowered to +//! an empty tuple. +//! +//! ## Bound variable collection +//! Another difference between rustc and Chalk lies in the handling of binders. +//! Chalk requires that we store the bound parameter kinds, whereas rustc does +//! not. To lower anything wrapped in a `Binder`, we first deeply find any bound +//! variables from the current `Binder`. + +use rustc_middle::traits::{ + ChalkEnvironmentAndGoal, ChalkEnvironmentClause, ChalkRustDefId as RustDefId, + ChalkRustInterner as RustInterner, +}; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::subst::{GenericArg, SubstsRef}; +use rustc_middle::ty::{ + self, Binder, BoundRegion, Predicate, Region, RegionKind, Ty, TyCtxt, TyKind, TypeFoldable, + TypeVisitor, +}; +use rustc_span::def_id::DefId; + +use std::collections::btree_map::{BTreeMap, Entry}; + +/// Essentially an `Into` with a `&RustInterner` parameter +crate trait LowerInto<'tcx, T> { + /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`. + fn lower_into(self, interner: &RustInterner<'tcx>) -> T; +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution>> for SubstsRef<'tcx> { + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::Substitution> { + chalk_ir::Substitution::from(interner, self.iter().map(|s| s.lower_into(interner))) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::AliasTy>> for ty::ProjectionTy<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasTy> { + chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id: chalk_ir::AssocTypeId(RustDefId::AssocTy(self.item_def_id)), + substitution: self.substs.lower_into(interner), + }) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment>>> + for ChalkEnvironmentAndGoal<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::InEnvironment>> { + let clauses = self.environment.into_iter().filter_map(|clause| match clause { + ChalkEnvironmentClause::Predicate(predicate) => { + match predicate { + ty::Predicate::Trait(predicate, _) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + Some( + chalk_ir::ProgramClauseData::ForAll(chalk_ir::Binders::new( + binders, + chalk_ir::ProgramClauseImplication { + consequence: chalk_ir::DomainGoal::FromEnv( + chalk_ir::FromEnv::Trait( + predicate.trait_ref.lower_into(interner), + ), + ), + conditions: chalk_ir::Goals::new(interner), + priority: chalk_ir::ClausePriority::High, + }, + )) + .intern(interner), + ) + } + // FIXME(chalk): need to add RegionOutlives/TypeOutlives + ty::Predicate::RegionOutlives(_) => None, + ty::Predicate::TypeOutlives(_) => None, + ty::Predicate::Projection(predicate) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + Some( + chalk_ir::ProgramClauseData::ForAll(chalk_ir::Binders::new( + binders, + chalk_ir::ProgramClauseImplication { + consequence: chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq( + predicate.lower_into(interner), + ), + ), + conditions: chalk_ir::Goals::new(interner), + priority: chalk_ir::ClausePriority::High, + }, + )) + .intern(interner), + ) + } + ty::Predicate::WellFormed(..) + | ty::Predicate::ObjectSafe(..) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::Subtype(..) + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => bug!("unexpected predicate {}", predicate), + } + } + ChalkEnvironmentClause::TypeFromEnv(ty) => Some( + chalk_ir::ProgramClauseData::Implies(chalk_ir::ProgramClauseImplication { + consequence: chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty( + ty.lower_into(interner), + )), + conditions: chalk_ir::Goals::new(interner), + priority: chalk_ir::ClausePriority::High, + }) + .intern(interner), + ), + }); + + let goal: chalk_ir::GoalData> = self.goal.lower_into(&interner); + chalk_ir::InEnvironment { + environment: chalk_ir::Environment { + clauses: chalk_ir::ProgramClauses::from(&interner, clauses), + }, + goal: goal.intern(&interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predicate<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { + match self { + Predicate::Trait(predicate, _) => predicate.lower_into(interner), + // FIXME(chalk): we need to register constraints. + Predicate::RegionOutlives(_predicate) => { + chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + } + Predicate::TypeOutlives(_predicate) => { + chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + } + Predicate::Projection(predicate) => predicate.lower_into(interner), + Predicate::WellFormed(ty) => match ty.kind { + // These types are always WF. + ty::Str | ty::Placeholder(..) | ty::Error | ty::Never => { + chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + } + + // FIXME(chalk): Well-formed only if ref lifetime outlives type + ty::Ref(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)), + + ty::Param(..) => panic!("No Params expected."), + + // FIXME(chalk) -- ultimately I think this is what we + // want to do, and we just have rules for how to prove + // `WellFormed` for everything above, instead of + // inlining a bit the rules of the proof here. + _ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed( + chalk_ir::WellFormed::Ty(ty.lower_into(interner)), + )), + }, + + // FIXME(chalk): other predicates + // + // We can defer this, but ultimately we'll want to express + // some of these in terms of chalk operations. + Predicate::ObjectSafe(..) + | Predicate::ClosureKind(..) + | Predicate::Subtype(..) + | Predicate::ConstEvaluatable(..) + | Predicate::ConstEquate(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef>> + for rustc_middle::ty::TraitRef<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::TraitRef> { + chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(RustDefId::Trait(self.def_id)), + substitution: self.substs.lower_into(interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> + for ty::PolyTraitPredicate<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { + let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); + + chalk_ir::GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + chalk_ir::Binders::new( + binders, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::Implemented(ty.trait_ref.lower_into(interner)), + )) + .intern(interner), + ), + ) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq>> + for rustc_middle::ty::ProjectionPredicate<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasEq> { + chalk_ir::AliasEq { + ty: self.ty.lower_into(interner), + alias: self.projection_ty.lower_into(interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> + for ty::PolyProjectionPredicate<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { + let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); + + chalk_ir::GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + chalk_ir::Binders::new( + binders, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(ty.lower_into(interner)), + )) + .intern(interner), + ), + ) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty> { + use chalk_ir::TyData; + use rustc_ast::ast; + use TyKind::*; + + let empty = || chalk_ir::Substitution::empty(interner); + let struct_ty = |def_id| chalk_ir::TypeName::Struct(chalk_ir::StructId(def_id)); + let apply = |name, substitution| { + TyData::Apply(chalk_ir::ApplicationTy { name, substitution }).intern(interner) + }; + let int = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Int(i)), empty()); + let uint = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Uint(i)), empty()); + let float = |f| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Float(f)), empty()); + + match self.kind { + Bool => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Bool), empty()), + Char => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Char), empty()), + Int(ty) => match ty { + ast::IntTy::Isize => int(chalk_ir::IntTy::Isize), + ast::IntTy::I8 => int(chalk_ir::IntTy::I8), + ast::IntTy::I16 => int(chalk_ir::IntTy::I16), + ast::IntTy::I32 => int(chalk_ir::IntTy::I32), + ast::IntTy::I64 => int(chalk_ir::IntTy::I64), + ast::IntTy::I128 => int(chalk_ir::IntTy::I128), + }, + Uint(ty) => match ty { + ast::UintTy::Usize => uint(chalk_ir::UintTy::Usize), + ast::UintTy::U8 => uint(chalk_ir::UintTy::U8), + ast::UintTy::U16 => uint(chalk_ir::UintTy::U16), + ast::UintTy::U32 => uint(chalk_ir::UintTy::U32), + ast::UintTy::U64 => uint(chalk_ir::UintTy::U64), + ast::UintTy::U128 => uint(chalk_ir::UintTy::U128), + }, + Float(ty) => match ty { + ast::FloatTy::F32 => float(chalk_ir::FloatTy::F32), + ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64), + }, + Adt(def, substs) => { + apply(struct_ty(RustDefId::Adt(def.did)), substs.lower_into(interner)) + } + Foreign(_def_id) => unimplemented!(), + Str => apply(struct_ty(RustDefId::Str), empty()), + Array(ty, _) => apply( + struct_ty(RustDefId::Array), + chalk_ir::Substitution::from1( + interner, + chalk_ir::ParameterKind::Ty(ty.lower_into(interner)).intern(interner), + ), + ), + Slice(ty) => apply( + struct_ty(RustDefId::Slice), + chalk_ir::Substitution::from1( + interner, + chalk_ir::ParameterKind::Ty(ty.lower_into(interner)).intern(interner), + ), + ), + RawPtr(_) => apply(struct_ty(RustDefId::RawPtr), empty()), + Ref(region, ty, mutability) => apply( + struct_ty(RustDefId::Ref(mutability)), + chalk_ir::Substitution::from( + interner, + [ + chalk_ir::ParameterKind::Lifetime(region.lower_into(interner)) + .intern(interner), + chalk_ir::ParameterKind::Ty(ty.lower_into(interner)).intern(interner), + ] + .iter(), + ), + ), + FnDef(def_id, _) => apply(struct_ty(RustDefId::FnDef(def_id)), empty()), + FnPtr(sig) => { + let (inputs_and_outputs, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &sig.inputs_and_output()); + TyData::Function(chalk_ir::Fn { + num_binders: binders.len(interner), + substitution: chalk_ir::Substitution::from( + interner, + inputs_and_outputs.iter().map(|ty| { + chalk_ir::ParameterKind::Ty(ty.lower_into(interner)).intern(interner) + }), + ), + }) + .intern(interner) + } + Dynamic(_, _) => unimplemented!(), + Closure(_def_id, _) => unimplemented!(), + Generator(_def_id, _substs, _) => unimplemented!(), + GeneratorWitness(_) => unimplemented!(), + Never => apply(struct_ty(RustDefId::Never), empty()), + Tuple(substs) => { + apply(chalk_ir::TypeName::Tuple(substs.len()), substs.lower_into(interner)) + } + Projection(proj) => TyData::Alias(proj.lower_into(interner)).intern(interner), + Opaque(_def_id, _substs) => unimplemented!(), + // This should have been done eagerly prior to this, and all Params + // should have been substituted to placeholders + Param(_) => panic!("Lowering Param when not expected."), + Bound(db, bound) => TyData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.index(), + )) + .intern(interner), + Placeholder(_placeholder) => TyData::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() }, + idx: _placeholder.name.as_usize(), + }) + .intern(interner), + Infer(_infer) => unimplemented!(), + Error => unimplemented!(), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Lifetime> { + use rustc_middle::ty::RegionKind::*; + + match self { + ReEarlyBound(_) => { + panic!("Should have already been substituted."); + } + ReLateBound(db, br) => match br { + ty::BoundRegion::BrAnon(var) => { + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + *var as usize, + )) + .intern(interner) + } + ty::BoundRegion::BrNamed(_def_id, _name) => unimplemented!(), + ty::BrEnv => unimplemented!(), + }, + ReFree(_) => unimplemented!(), + ReScope(_) => unimplemented!(), + ReStatic => unimplemented!(), + ReVar(_) => unimplemented!(), + RePlaceholder(placeholder_region) => { + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() }, + idx: 0, + }) + .intern(interner) + } + ReEmpty(_) => unimplemented!(), + ReErased => unimplemented!(), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Parameter>> for GenericArg<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Parameter> { + match self.unpack() { + ty::subst::GenericArgKind::Type(ty) => { + chalk_ir::ParameterKind::Ty(ty.lower_into(interner)) + } + ty::subst::GenericArgKind::Lifetime(lifetime) => { + chalk_ir::ParameterKind::Lifetime(lifetime.lower_into(interner)) + } + ty::subst::GenericArgKind::Const(_) => chalk_ir::ParameterKind::Ty( + chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Tuple(0), + substitution: chalk_ir::Substitution::empty(interner), + }) + .intern(interner), + ), + } + .intern(interner) + } +} + +// We lower into an Option here since there are some predicates which Chalk +// doesn't have a representation for yet (as a `WhereClause`), but are so common +// that we just are accepting the unsoundness for now. The `Option` will +// eventually be removed. +impl<'tcx> LowerInto<'tcx, Option>>> + for ty::Predicate<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> Option>> { + match &self { + Predicate::Trait(predicate, _) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + Some(chalk_ir::Binders::new( + binders, + chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)), + )) + } + Predicate::RegionOutlives(_predicate) => None, + Predicate::TypeOutlives(_predicate) => None, + Predicate::Projection(_predicate) => None, + Predicate::WellFormed(_ty) => None, + + Predicate::ObjectSafe(..) + | Predicate::ClosureKind(..) + | Predicate::Subtype(..) + | Predicate::ConstEvaluatable(..) + | Predicate::ConstEquate(..) => bug!("unexpected predicate {}", &self), + } + } +} + +/// To collect bound vars, we have to do two passes. In the first pass, we +/// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then +/// replace `BrNamed` into `BrAnon`. The two separate passes are important, +/// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all +/// "real" `BrAnon`s. +/// +/// It's important to note that because of prior substitution, we may have +/// late-bound regions, even outside of fn contexts, since this is the best way +/// to prep types for chalk lowering. +crate fn collect_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>>( + interner: &RustInterner<'tcx>, + tcx: TyCtxt<'tcx>, + ty: &'a Binder, +) -> (T, chalk_ir::ParameterKinds>, BTreeMap) { + let mut bound_vars_collector = BoundVarsCollector::new(); + ty.skip_binder().visit_with(&mut bound_vars_collector); + let mut parameters = bound_vars_collector.parameters; + let named_parameters: BTreeMap = bound_vars_collector + .named_parameters + .into_iter() + .enumerate() + .map(|(i, def_id)| (def_id, (i + parameters.len()) as u32)) + .collect(); + + let mut bound_var_substitutor = NamedBoundVarSubstitutor::new(tcx, &named_parameters); + let new_ty = ty.skip_binder().fold_with(&mut bound_var_substitutor); + + for var in named_parameters.values() { + parameters.insert(*var, chalk_ir::ParameterKind::Lifetime(())); + } + + (0..parameters.len()).for_each(|i| { + parameters.get(&(i as u32)).expect("Skipped bound var index."); + }); + + let binders = chalk_ir::ParameterKinds::from(interner, parameters.into_iter().map(|(_, v)| v)); + + (new_ty, binders, named_parameters) +} + +crate struct BoundVarsCollector { + binder_index: ty::DebruijnIndex, + crate parameters: BTreeMap>, + crate named_parameters: Vec, +} + +impl BoundVarsCollector { + crate fn new() -> Self { + BoundVarsCollector { + binder_index: ty::INNERMOST, + parameters: BTreeMap::new(), + named_parameters: vec![], + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector { + fn visit_binder>(&mut self, t: &Binder) -> bool { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match t.kind { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + match self.parameters.entry(bound_ty.var.as_u32()) { + Entry::Vacant(entry) => { + entry.insert(chalk_ir::ParameterKind::Ty(())); + } + Entry::Occupied(entry) => { + entry.get().assert_ty_ref(); + } + } + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'tcx>) -> bool { + match r { + ty::ReLateBound(index, br) if *index == self.binder_index => match br { + ty::BoundRegion::BrNamed(def_id, _name) => { + if self.named_parameters.iter().find(|d| *d == def_id).is_none() { + self.named_parameters.push(*def_id); + } + } + + ty::BoundRegion::BrAnon(var) => match self.parameters.entry(*var) { + Entry::Vacant(entry) => { + entry.insert(chalk_ir::ParameterKind::Lifetime(())); + } + Entry::Occupied(entry) => { + entry.get().assert_lifetime_ref(); + } + }, + + ty::BrEnv => unimplemented!(), + }, + + ty::ReEarlyBound(_re) => { + // FIXME(chalk): jackh726 - I think we should always have already + // substituted away `ReEarlyBound`s for `ReLateBound`s, but need to confirm. + unimplemented!(); + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +/// This is used to replace `BoundRegion::BrNamed` with `BoundRegion::BrAnon`. +/// Note: we assume that we will always have room for more bound vars. (i.e. we +/// won't ever hit the `u32` limit in `BrAnon`s). +struct NamedBoundVarSubstitutor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + binder_index: ty::DebruijnIndex, + named_parameters: &'a BTreeMap, +} + +impl<'a, 'tcx> NamedBoundVarSubstitutor<'a, 'tcx> { + fn new(tcx: TyCtxt<'tcx>, named_parameters: &'a BTreeMap) -> Self { + NamedBoundVarSubstitutor { tcx, binder_index: ty::INNERMOST, named_parameters } + } +} + +impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder>(&mut self, t: &Binder) -> Binder { + self.binder_index.shift_in(1); + let result = t.super_fold_with(self); + self.binder_index.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + t.super_fold_with(self) + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + ty::ReLateBound(index, br) if *index == self.binder_index => match br { + ty::BoundRegion::BrNamed(def_id, _name) => { + match self.named_parameters.get(def_id) { + Some(idx) => { + return self.tcx.mk_region(RegionKind::ReLateBound( + *index, + BoundRegion::BrAnon(*idx), + )); + } + None => panic!("Missing `BrNamed`."), + } + } + ty::BrEnv => unimplemented!(), + ty::BoundRegion::BrAnon(_) => {} + }, + _ => (), + }; + + r.super_fold_with(self) + } +} + +/// Used to substitute `Param`s with placeholders. We do this since Chalk +/// have a notion of `Param`s. +crate struct ParamsSubstitutor<'tcx> { + tcx: TyCtxt<'tcx>, + binder_index: ty::DebruijnIndex, + list: Vec, + crate params: rustc_data_structures::fx::FxHashMap, + crate named_regions: BTreeMap, +} + +impl<'tcx> ParamsSubstitutor<'tcx> { + crate fn new(tcx: TyCtxt<'tcx>) -> Self { + ParamsSubstitutor { + tcx, + binder_index: ty::INNERMOST, + list: vec![], + params: rustc_data_structures::fx::FxHashMap::default(), + named_regions: BTreeMap::default(), + } + } +} + +impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder>(&mut self, t: &Binder) -> Binder { + self.binder_index.shift_in(1); + let result = t.super_fold_with(self); + self.binder_index.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind { + // FIXME(chalk): currently we convert params to placeholders starting at + // index `0`. To support placeholders, we'll actually need to do a + // first pass to collect placeholders. Then we can insert params after. + ty::Placeholder(_) => unimplemented!(), + ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { + Some(_idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::from_usize(0), + name: ty::BoundVar::from_usize(_idx), + })), + None => { + self.list.push(param); + let idx = self.list.len() - 1; + self.params.insert(idx, param); + self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::from_usize(0), + name: ty::BoundVar::from_usize(idx), + })) + } + }, + + _ => t.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + // FIXME(chalk) - jackh726 - this currently isn't hit in any tests. + // This covers any region variables in a goal, right? + ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { + Some(idx) => self.tcx.mk_region(RegionKind::ReLateBound( + self.binder_index, + BoundRegion::BrAnon(*idx), + )), + None => { + let idx = self.named_regions.len() as u32; + self.named_regions.insert(_re.def_id, idx); + self.tcx.mk_region(RegionKind::ReLateBound( + self.binder_index, + BoundRegion::BrAnon(idx), + )) + } + }, + + _ => r.super_fold_with(self), + } + } +} diff --git a/src/librustc_traits/chalk/mod.rs b/src/librustc_traits/chalk/mod.rs new file mode 100644 index 0000000000000..4e635b9db0901 --- /dev/null +++ b/src/librustc_traits/chalk/mod.rs @@ -0,0 +1,227 @@ +//! Calls `chalk-solve` to solve a `ty::Predicate` +//! +//! In order to call `chalk-solve`, this file must convert a +//! `ChalkCanonicalGoal` into a Chalk ucanonical goal. It then calls Chalk, and +//! converts the answer back into rustc solution. + +crate mod db; +crate mod lowering; + +use rustc_data_structures::fx::FxHashMap; + +use rustc_index::vec::IndexVec; + +use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind}; +use rustc_middle::traits::ChalkRustInterner; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{ + self, Bound, BoundVar, ParamTy, Region, RegionKind, Ty, TyCtxt, TypeFoldable, +}; + +use rustc_infer::infer::canonical::{ + Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse, +}; +use rustc_infer::traits::{self, ChalkCanonicalGoal, ChalkRustDefId as RustDefId}; + +use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase; +use crate::chalk::lowering::{LowerInto, ParamsSubstitutor}; + +use chalk_solve::Solution; + +crate fn provide(p: &mut Providers<'_>) { + *p = Providers { evaluate_goal, ..*p }; +} + +crate fn evaluate_goal<'tcx>( + tcx: TyCtxt<'tcx>, + obligation: ChalkCanonicalGoal<'tcx>, +) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> { + let interner = ChalkRustInterner { tcx }; + + // Chalk doesn't have a notion of `Params`, so instead we use placeholders. + let mut params_substitutor = ParamsSubstitutor::new(tcx); + let obligation = obligation.fold_with(&mut params_substitutor); + let _params: FxHashMap = params_substitutor.params; + let max_universe = obligation.max_universe.index(); + + let _lowered_goal: chalk_ir::UCanonical< + chalk_ir::InEnvironment>>, + > = chalk_ir::UCanonical { + canonical: chalk_ir::Canonical { + binders: chalk_ir::CanonicalVarKinds::from( + &interner, + obligation.variables.iter().map(|v| match v.kind { + CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(), + CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(), + CanonicalVarKind::Ty(ty) => match ty { + CanonicalTyVarKind::General(ui) => { + chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex { + counter: ui.index(), + }) + } + CanonicalTyVarKind::Int | CanonicalTyVarKind::Float => { + // FIXME(chalk) - this is actually really important + // These variable kinds put some limits on the + // types that can be substituted (floats or ints). + // While it's unclear exactly the design here, we + // probably want some way to "register" these. + chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::root()) + } + }, + CanonicalVarKind::Region(ui) => { + chalk_ir::ParameterKind::Lifetime(chalk_ir::UniverseIndex { + counter: ui.index(), + }) + } + CanonicalVarKind::Const(_ui) => unimplemented!(), + CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(), + }), + ), + value: obligation.value.lower_into(&interner), + }, + universes: max_universe + 1, + }; + + let solver_choice = chalk_solve::SolverChoice::SLG { max_size: 32, expected_answers: None }; + let mut solver = solver_choice.into_solver::>(); + + let db = ChalkRustIrDatabase { tcx, interner }; + let solution = solver.solve(&db, &_lowered_goal); + + // Ideally, the code to convert *back* to rustc types would live close to + // the code to convert *from* rustc types. Right now though, we don't + // really need this and so it's really minimal. + // Right now, we also treat a `Unique` solution the same as + // `Ambig(Definite)`. This really isn't right. + let make_solution = |_subst: chalk_ir::Substitution<_>| { + let mut var_values: IndexVec> = IndexVec::new(); + _subst.parameters(&interner).iter().for_each(|p| { + // FIXME(chalk): we should move this elsewhere, since this is + // essentially inverse of lowering a `GenericArg`. + let _data = p.data(&interner); + match _data { + chalk_ir::ParameterKind::Ty(_t) => { + use chalk_ir::TyData; + use rustc_ast::ast; + + let _data = _t.data(&interner); + let kind = match _data { + TyData::Apply(_application_ty) => match _application_ty.name { + chalk_ir::TypeName::Struct(_struct_id) => match _struct_id.0 { + RustDefId::Array => unimplemented!(), + RustDefId::Slice => unimplemented!(), + _ => unimplemented!(), + }, + chalk_ir::TypeName::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => ty::Bool, + chalk_ir::Scalar::Char => ty::Char, + chalk_ir::Scalar::Int(int_ty) => match int_ty { + chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), + chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), + chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), + chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), + chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), + chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), + }, + chalk_ir::Scalar::Uint(int_ty) => match int_ty { + chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), + chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), + chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), + chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), + chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), + chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), + }, + chalk_ir::Scalar::Float(float_ty) => match float_ty { + chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), + chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), + }, + }, + chalk_ir::TypeName::Tuple(_size) => unimplemented!(), + chalk_ir::TypeName::OpaqueType(_ty) => unimplemented!(), + chalk_ir::TypeName::AssociatedType(_assoc_ty) => unimplemented!(), + chalk_ir::TypeName::Error => unimplemented!(), + }, + TyData::Placeholder(_placeholder) => { + unimplemented!(); + } + TyData::Alias(_alias_ty) => unimplemented!(), + TyData::Function(_quantified_ty) => unimplemented!(), + TyData::BoundVar(_bound) => Bound( + ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), + ty::BoundTy { + var: ty::BoundVar::from_usize(_bound.index), + kind: ty::BoundTyKind::Anon, + }, + ), + TyData::InferenceVar(_) => unimplemented!(), + TyData::Dyn(_) => unimplemented!(), + }; + let _ty: Ty<'_> = tcx.mk_ty(kind); + let _arg: GenericArg<'_> = _ty.into(); + var_values.push(_arg); + } + chalk_ir::ParameterKind::Lifetime(_l) => { + let _data = _l.data(&interner); + let _lifetime: Region<'_> = match _data { + chalk_ir::LifetimeData::BoundVar(_var) => { + tcx.mk_region(RegionKind::ReLateBound( + rustc_middle::ty::DebruijnIndex::from_usize( + _var.debruijn.depth() as usize + ), + rustc_middle::ty::BoundRegion::BrAnon(_var.index as u32), + )) + } + chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), + chalk_ir::LifetimeData::Placeholder(_index) => unimplemented!(), + chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), + }; + let _arg: GenericArg<'_> = _lifetime.into(); + var_values.push(_arg); + } + } + }); + let sol = Canonical { + max_universe: ty::UniverseIndex::from_usize(0), + variables: obligation.variables.clone(), + value: QueryResponse { + var_values: CanonicalVarValues { var_values }, + region_constraints: QueryRegionConstraints::default(), + certainty: Certainty::Proven, + value: (), + }, + }; + &*tcx.arena.alloc(sol) + }; + solution + .map(|s| match s { + Solution::Unique(_subst) => { + // FIXME(chalk): handle constraints + assert!(_subst.value.constraints.is_empty()); + make_solution(_subst.value.subst) + } + Solution::Ambig(_guidance) => { + match _guidance { + chalk_solve::Guidance::Definite(_subst) => make_solution(_subst.value), + chalk_solve::Guidance::Suggested(_) => unimplemented!(), + chalk_solve::Guidance::Unknown => { + // chalk_fulfill doesn't use the var_values here, so + // let's just ignore that + let sol = Canonical { + max_universe: ty::UniverseIndex::from_usize(0), + variables: obligation.variables.clone(), + value: QueryResponse { + var_values: CanonicalVarValues { var_values: IndexVec::new() } + .make_identity(tcx), + region_constraints: QueryRegionConstraints::default(), + certainty: Certainty::Ambiguous, + value: (), + }, + }; + &*tcx.arena.alloc(sol) + } + } + } + }) + .ok_or(traits::query::NoSolution) +} diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index 7068723f534e7..08475d6a09db1 100644 --- a/src/librustc_traits/dropck_outlives.rs +++ b/src/librustc_traits/dropck_outlives.rs @@ -1,11 +1,11 @@ -use rustc::ty::query::Providers; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives; use rustc_trait_selection::traits::query::dropck_outlives::{ @@ -191,10 +191,12 @@ fn dtorck_constraint_for_ty<'tcx>( ty::Array(ety, _) | ty::Slice(ety) => { // single-element containers, behave like their element - dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints)?; + rustc_data_structures::stack::ensure_sufficient_stack(|| { + dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints) + })?; } - ty::Tuple(tys) => { + ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| { for ty in tys.iter() { dtorck_constraint_for_ty( tcx, @@ -205,13 +207,15 @@ fn dtorck_constraint_for_ty<'tcx>( constraints, )?; } - } + Ok::<_, NoSolution>(()) + })?, - ty::Closure(_, substs) => { + ty::Closure(_, substs) => rustc_data_structures::stack::ensure_sufficient_stack(|| { for ty in substs.as_closure().upvar_tys() { dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?; } - } + Ok::<_, NoSolution>(()) + })?, ty::Generator(_, substs, _movability) => { // rust-lang/rust#49918: types can be constructed, stored @@ -267,8 +271,6 @@ fn dtorck_constraint_for_ty<'tcx>( constraints.dtorck_types.push(ty); } - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => { // By the time this code runs, all type variables ought to // be fully resolved. diff --git a/src/librustc_traits/evaluate_obligation.rs b/src/librustc_traits/evaluate_obligation.rs index 87895d8e384da..e6afc15fa15fe 100644 --- a/src/librustc_traits/evaluate_obligation.rs +++ b/src/librustc_traits/evaluate_obligation.rs @@ -1,6 +1,6 @@ -use rustc::ty::query::Providers; -use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::source_map::DUMMY_SP; use rustc_trait_selection::traits::query::CanonicalPredicateGoal; use rustc_trait_selection::traits::{ diff --git a/src/librustc_traits/implied_outlives_bounds.rs b/src/librustc_traits/implied_outlives_bounds.rs index 4505a1e59d9ba..eaaab87ab7474 100644 --- a/src/librustc_traits/implied_outlives_bounds.rs +++ b/src/librustc_traits/implied_outlives_bounds.rs @@ -1,13 +1,14 @@ //! Provider for the `implied_outlives_bounds` query. -//! Do not call this query directory. See [`rustc::traits::query::implied_outlives_bounds`]. +//! Do not call this query directory. See +//! [`rustc_trait_selection::traits::query::type_op::implied_outlives_bounds`]. -use rustc::ty::outlives::Component; -use rustc::ty::query::Providers; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_hir as hir; use rustc_infer::infer::canonical::{self, Canonical}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::ty::outlives::Component; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_span::source_map::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::outlives_bounds::OutlivesBound; @@ -61,7 +62,7 @@ fn compute_implied_outlives_bounds<'tcx>( // unresolved inference variables here anyway, but there might be // during typeck under some circumstances.) let obligations = - wf::obligations(infcx, param_env, hir::DUMMY_HIR_ID, ty, DUMMY_SP).unwrap_or(vec![]); + wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, ty, DUMMY_SP).unwrap_or(vec![]); // N.B., all of these predicates *ought* to be easily proven // true. In fact, their correctness is (mostly) implied by @@ -99,7 +100,8 @@ fn compute_implied_outlives_bounds<'tcx>( | ty::Predicate::Projection(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ConstEvaluatable(..) => vec![], + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => vec![], ty::Predicate::WellFormed(subty) => { wf_types.push(subty); diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 894e3ef3a8f83..f3dfdffda4191 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -4,28 +4,29 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; +mod chalk; mod dropck_outlives; mod evaluate_obligation; mod implied_outlives_bounds; -pub mod lowering; mod normalize_erasing_regions; mod normalize_projection_ty; mod type_op; -use rustc::ty::query::Providers; +use rustc_middle::ty::query::Providers; pub fn provide(p: &mut Providers<'_>) { dropck_outlives::provide(p); evaluate_obligation::provide(p); implied_outlives_bounds::provide(p); - lowering::provide(p); + chalk::provide(p); normalize_projection_ty::provide(p); normalize_erasing_regions::provide(p); type_op::provide(p); diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs deleted file mode 100644 index ed6259d457361..0000000000000 --- a/src/librustc_traits/lowering/environment.rs +++ /dev/null @@ -1,254 +0,0 @@ -use rustc::traits::{ - Clause, Clauses, DomainGoal, Environment, FromEnv, ProgramClause, ProgramClauseCategory, -}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::DefId; - -struct ClauseVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - round: &'a mut FxHashSet>, -} - -impl ClauseVisitor<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, round: &'a mut FxHashSet>) -> Self { - ClauseVisitor { tcx, round } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) { - match ty.kind { - ty::Projection(data) => { - self.round.extend( - self.tcx - .program_clauses_for(data.item_def_id) - .iter() - .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound) - .cloned(), - ); - } - - ty::Dynamic(..) => { - // FIXME: trait object rules are not yet implemented - } - - ty::Adt(def, ..) => { - self.round.extend( - self.tcx - .program_clauses_for(def.did) - .iter() - .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound) - .cloned(), - ); - } - - ty::Foreign(def_id) - | ty::FnDef(def_id, ..) - | ty::Closure(def_id, ..) - | ty::Generator(def_id, ..) - | ty::Opaque(def_id, ..) => { - self.round.extend( - self.tcx - .program_clauses_for(def_id) - .iter() - .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound) - .cloned(), - ); - } - - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::FnPtr(..) - | ty::Tuple(..) - | ty::Ref(..) - | ty::Never - | ty::Infer(..) - | ty::Placeholder(..) - | ty::Param(..) - | ty::Bound(..) => (), - - ty::GeneratorWitness(..) | ty::UnnormalizedProjection(..) | ty::Error => { - bug!("unexpected type {:?}", ty); - } - } - } - - fn visit_from_env(&mut self, from_env: FromEnv<'tcx>) { - match from_env { - FromEnv::Trait(predicate) => { - self.round.extend( - self.tcx - .program_clauses_for(predicate.def_id()) - .iter() - .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound) - .cloned(), - ); - } - - FromEnv::Ty(ty) => self.visit_ty(ty), - } - } - - fn visit_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>) { - // The only domain goals we can find in an environment are: - // * `DomainGoal::Holds(..)` - // * `DomainGoal::FromEnv(..)` - // The former do not lead to any implied bounds. So we only need - // to visit the latter. - if let DomainGoal::FromEnv(from_env) = domain_goal { - self.visit_from_env(from_env); - } - } - - fn visit_program_clause(&mut self, clause: ProgramClause<'tcx>) { - self.visit_domain_goal(clause.goal); - // No need to visit `clause.hypotheses`: they are always of the form - // `FromEnv(...)` and were visited at a previous round. - } - - fn visit_clause(&mut self, clause: Clause<'tcx>) { - match clause { - Clause::Implies(clause) => self.visit_program_clause(clause), - Clause::ForAll(clause) => self.visit_program_clause(*clause.skip_binder()), - } - } -} - -crate fn program_clauses_for_env<'tcx>( - tcx: TyCtxt<'tcx>, - environment: Environment<'tcx>, -) -> Clauses<'tcx> { - debug!("program_clauses_for_env(environment={:?})", environment); - - let mut last_round = FxHashSet::default(); - { - let mut visitor = ClauseVisitor::new(tcx, &mut last_round); - for &clause in environment.clauses { - visitor.visit_clause(clause); - } - } - - let mut closure = last_round.clone(); - let mut next_round = FxHashSet::default(); - while !last_round.is_empty() { - let mut visitor = ClauseVisitor::new(tcx, &mut next_round); - for clause in last_round.drain() { - visitor.visit_clause(clause); - } - last_round.extend(next_round.drain().filter(|&clause| closure.insert(clause))); - } - - debug!("program_clauses_for_env: closure = {:#?}", closure); - - tcx.mk_clauses(closure.into_iter()) -} - -crate fn environment(tcx: TyCtxt<'_>, def_id: DefId) -> Environment<'_> { - use super::{IntoFromEnvGoal, Lower}; - use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind}; - - debug!("environment(def_id = {:?})", def_id); - - // The environment of an impl Trait type is its defining function's environment. - if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { - return environment(tcx, parent); - } - - // Compute the bounds on `Self` and the type parameters. - let ty::InstantiatedPredicates { predicates, .. } = - tcx.predicates_of(def_id).instantiate_identity(tcx); - - let clauses = predicates - .into_iter() - .map(|predicate| predicate.lower()) - .map(|domain_goal| domain_goal.map_bound(|bound| bound.into_from_env_goal())) - .map(|domain_goal| domain_goal.map_bound(|bound| bound.into_program_clause())) - // `ForAll` because each `domain_goal` is a `PolyDomainGoal` and - // could bound lifetimes. - .map(Clause::ForAll); - - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - let node = tcx.hir().get(hir_id); - - enum NodeKind { - TraitImpl, - InherentImpl, - Fn, - Other, - }; - - let node_kind = match node { - Node::TraitItem(item) => match item.kind { - TraitItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - Node::Item(item) => match item.kind { - ItemKind::Impl { of_trait: Some(_), .. } => NodeKind::TraitImpl, - ItemKind::Impl { of_trait: None, .. } => NodeKind::InherentImpl, - ItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - // FIXME: closures? - _ => NodeKind::Other, - }; - - let mut input_tys = FxHashSet::default(); - - match node_kind { - // In a trait impl, we assume that the header trait ref and all its - // constituents are well-formed. - NodeKind::TraitImpl => { - let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl"); - - input_tys.extend(trait_ref.input_types().flat_map(|ty| ty.walk())); - } - - // In an inherent impl, we assume that the receiver type and all its - // constituents are well-formed. - NodeKind::InherentImpl => { - let self_ty = tcx.type_of(def_id); - input_tys.extend(self_ty.walk()); - } - - // In an fn, we assume that the arguments and all their constituents are - // well-formed. - NodeKind::Fn => { - let fn_sig = tcx.fn_sig(def_id); - let fn_sig = tcx.liberate_late_bound_regions(def_id, &fn_sig); - - input_tys.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk())); - } - - NodeKind::Other => (), - } - - let clauses = clauses.chain( - input_tys - .into_iter() - .map(|ty| DomainGoal::FromEnv(FromEnv::Ty(ty))) - .map(|domain_goal| domain_goal.into_program_clause()) - .map(Clause::Implies), - ); - - debug!("environment: clauses = {:?}", clauses); - - Environment { clauses: tcx.mk_clauses(clauses) } -} diff --git a/src/librustc_traits/lowering/mod.rs b/src/librustc_traits/lowering/mod.rs deleted file mode 100644 index e3f9bd78073e9..0000000000000 --- a/src/librustc_traits/lowering/mod.rs +++ /dev/null @@ -1,629 +0,0 @@ -mod environment; - -use rustc::hir::map::Map; -use rustc::traits::{ - Clause, Clauses, DomainGoal, FromEnv, GoalKind, PolyDomainGoal, ProgramClause, - ProgramClauseCategory, WellFormed, WhereClause, -}; -use rustc::ty::query::Providers; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, List, TyCtxt}; -use rustc_ast::ast; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_hir::definitions::DefPathData; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_span::symbol::sym; - -use std::iter; - -crate fn provide(p: &mut Providers<'_>) { - *p = Providers { - program_clauses_for, - program_clauses_for_env: environment::program_clauses_for_env, - environment: environment::environment, - ..*p - }; -} - -crate trait Lower { - /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk-like type. - fn lower(&self) -> T; -} - -impl Lower> for Vec -where - T: Lower, -{ - fn lower(&self) -> Vec { - self.iter().map(|item| item.lower()).collect() - } -} - -impl<'tcx> Lower> for ty::TraitPredicate<'tcx> { - fn lower(&self) -> WhereClause<'tcx> { - WhereClause::Implemented(*self) - } -} - -impl<'tcx> Lower> for ty::ProjectionPredicate<'tcx> { - fn lower(&self) -> WhereClause<'tcx> { - WhereClause::ProjectionEq(*self) - } -} - -impl<'tcx> Lower> for ty::RegionOutlivesPredicate<'tcx> { - fn lower(&self) -> WhereClause<'tcx> { - WhereClause::RegionOutlives(*self) - } -} - -impl<'tcx> Lower> for ty::TypeOutlivesPredicate<'tcx> { - fn lower(&self) -> WhereClause<'tcx> { - WhereClause::TypeOutlives(*self) - } -} - -impl<'tcx, T> Lower> for T -where - T: Lower>, -{ - fn lower(&self) -> DomainGoal<'tcx> { - DomainGoal::Holds(self.lower()) - } -} - -/// `ty::Binder` is used for wrapping a rustc construction possibly containing generic -/// lifetimes, e.g., `for<'a> T: Fn(&'a i32)`. Instead of representing higher-ranked things -/// in that leaf-form (i.e., `Holds(Implemented(Binder))` in the previous -/// example), we model them with quantified domain goals, e.g., as for the previous example: -/// `forall<'a> { T: Fn(&'a i32) }` which corresponds to something like -/// `Binder`. -impl<'tcx, T> Lower> for ty::Binder -where - T: Lower> + ty::fold::TypeFoldable<'tcx>, -{ - fn lower(&self) -> PolyDomainGoal<'tcx> { - self.map_bound_ref(|p| p.lower()) - } -} - -impl<'tcx> Lower> for ty::Predicate<'tcx> { - fn lower(&self) -> PolyDomainGoal<'tcx> { - use rustc::ty::Predicate; - - match self { - Predicate::Trait(predicate, _) => predicate.lower(), - Predicate::RegionOutlives(predicate) => predicate.lower(), - Predicate::TypeOutlives(predicate) => predicate.lower(), - Predicate::Projection(predicate) => predicate.lower(), - - Predicate::WellFormed(..) - | Predicate::ObjectSafe(..) - | Predicate::ClosureKind(..) - | Predicate::Subtype(..) - | Predicate::ConstEvaluatable(..) => bug!("unexpected predicate {}", self), - } - } -} - -/// Used for implied bounds related rules (see rustc dev guide). -trait IntoFromEnvGoal { - /// Transforms an existing goal into a `FromEnv` goal. - fn into_from_env_goal(self) -> Self; -} - -/// Used for well-formedness related rules (see rustc dev guide). -trait IntoWellFormedGoal { - /// Transforms an existing goal into a `WellFormed` goal. - fn into_well_formed_goal(self) -> Self; -} - -impl<'tcx> IntoFromEnvGoal for DomainGoal<'tcx> { - fn into_from_env_goal(self) -> DomainGoal<'tcx> { - use self::WhereClause::*; - - match self { - DomainGoal::Holds(Implemented(trait_ref)) => { - DomainGoal::FromEnv(FromEnv::Trait(trait_ref)) - } - other => other, - } - } -} - -impl<'tcx> IntoWellFormedGoal for DomainGoal<'tcx> { - fn into_well_formed_goal(self) -> DomainGoal<'tcx> { - use self::WhereClause::*; - - match self { - DomainGoal::Holds(Implemented(trait_ref)) => { - DomainGoal::WellFormed(WellFormed::Trait(trait_ref)) - } - other => other, - } - } -} - -crate fn program_clauses_for(tcx: TyCtxt<'_>, def_id: DefId) -> Clauses<'_> { - // FIXME(eddyb) this should only be using `def_kind`. - match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::TypeNs(..) => match tcx.def_kind(def_id) { - Some(DefKind::Trait) | Some(DefKind::TraitAlias) => { - program_clauses_for_trait(tcx, def_id) - } - // FIXME(eddyb) deduplicate this `associated_item` call with - // `program_clauses_for_associated_type_{value,def}`. - Some(DefKind::AssocTy) => match tcx.associated_item(def_id).container { - ty::AssocItemContainer::ImplContainer(_) => { - program_clauses_for_associated_type_value(tcx, def_id) - } - ty::AssocItemContainer::TraitContainer(_) => { - program_clauses_for_associated_type_def(tcx, def_id) - } - }, - Some(DefKind::Struct) - | Some(DefKind::Enum) - | Some(DefKind::TyAlias) - | Some(DefKind::Union) - | Some(DefKind::OpaqueTy) => program_clauses_for_type_def(tcx, def_id), - _ => List::empty(), - }, - DefPathData::Impl => program_clauses_for_impl(tcx, def_id), - _ => List::empty(), - } -} - -fn program_clauses_for_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Clauses<'_> { - // `trait Trait where WC { .. } // P0 == Self` - - // Rule Implemented-From-Env (see rustc dev guide) - // - // ``` - // forall { - // Implemented(Self: Trait) :- FromEnv(Self: Trait) - // } - // ``` - - let bound_vars = InternalSubsts::bound_vars_for_item(tcx, def_id); - - // `Self: Trait` - let trait_pred = ty::TraitPredicate { trait_ref: ty::TraitRef { def_id, substs: bound_vars } }; - - // `Implemented(Self: Trait)` - let impl_trait: DomainGoal<'_> = trait_pred.lower(); - - // `FromEnv(Self: Trait)` - let from_env_goal = tcx.mk_goal(impl_trait.into_from_env_goal().into_goal()); - let hypotheses = tcx.intern_goals(&[from_env_goal]); - - // `Implemented(Self: Trait) :- FromEnv(Self: Trait)` - let implemented_from_env = ProgramClause { - goal: impl_trait, - hypotheses, - category: ProgramClauseCategory::ImpliedBound, - }; - - let implemented_from_env = Clause::ForAll(ty::Binder::bind(implemented_from_env)); - - let predicates = tcx.predicates_defined_on(def_id).predicates; - - // Warning: these where clauses are not substituted for bound vars yet, - // so that we don't need to adjust binders in the `FromEnv` rules below - // (see the FIXME). - let where_clauses = &predicates.iter().map(|(wc, _)| wc.lower()).collect::>(); - - // Rule Implied-Bound-From-Trait - // - // For each where clause WC: - // ``` - // forall { - // FromEnv(WC) :- FromEnv(Self: Trait)`, for each where clause WC - let implied_bound_clauses = where_clauses - .iter() - .cloned() - // `FromEnv(WC) :- FromEnv(Self: Trait)` - .map(|wc| { - // we move binders to the left - wc.map_bound(|goal| ProgramClause { - // FIXME: As where clauses can only bind lifetimes for now, and that named - // bound regions have a def-id, it is safe to just inject `bound_vars` and - // `hypotheses` (which contain named vars bound at index `0`) into this - // binding level. This may change if we ever allow where clauses to bind - // types (e.g. for GATs things), because bound types only use a `BoundVar` - // index (no def-id). - goal: goal.subst(tcx, bound_vars).into_from_env_goal(), - hypotheses, - - category: ProgramClauseCategory::ImpliedBound, - }) - }) - .map(Clause::ForAll); - - // Rule WellFormed-TraitRef - // - // Here `WC` denotes the set of all where clauses: - // ``` - // forall { - // WellFormed(Self: Trait) :- Implemented(Self: Trait) && WellFormed(WC) - // } - // ``` - - // `WellFormed(WC)` - let wf_conditions = where_clauses - .iter() - .map(|wc| wc.subst(tcx, bound_vars)) - .map(|wc| wc.map_bound(|goal| goal.into_well_formed_goal())); - - // `WellFormed(Self: Trait) :- Implemented(Self: Trait) && WellFormed(WC)` - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Trait(trait_pred)), - hypotheses: tcx.mk_goals( - iter::once(tcx.mk_goal(GoalKind::DomainGoal(impl_trait))).chain( - wf_conditions.map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))), - ), - ), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - tcx.mk_clauses( - iter::once(implemented_from_env).chain(implied_bound_clauses).chain(iter::once(wf_clause)), - ) -} - -fn program_clauses_for_impl(tcx: TyCtxt<'tcx>, def_id: DefId) -> Clauses<'tcx> { - if let ty::ImplPolarity::Negative = tcx.impl_polarity(def_id) { - return List::empty(); - } - - // Rule Implemented-From-Impl (see rustc dev guide) - // - // `impl Trait for A0 where WC { .. }` - // - // ``` - // forall { - // Implemented(A0: Trait) :- WC - // } - // ``` - - let bound_vars = InternalSubsts::bound_vars_for_item(tcx, def_id); - - let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl").subst(tcx, bound_vars); - - // `Implemented(A0: Trait)` - let trait_pred = ty::TraitPredicate { trait_ref }.lower(); - - // `WC` - let predicates = tcx.predicates_of(def_id).predicates; - let where_clauses = - predicates.iter().map(|(wc, _)| wc.lower()).map(|wc| wc.subst(tcx, bound_vars)); - - // `Implemented(A0: Trait) :- WC` - let clause = ProgramClause { - goal: trait_pred, - hypotheses: tcx.mk_goals( - where_clauses.map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))), - ), - category: ProgramClauseCategory::Other, - }; - tcx.mk_clauses(iter::once(Clause::ForAll(ty::Binder::bind(clause)))) -} - -pub fn program_clauses_for_type_def(tcx: TyCtxt<'_>, def_id: DefId) -> Clauses<'_> { - // Rule WellFormed-Type - // - // `struct Ty where WC1, ..., WCm` - // - // ``` - // forall { - // WellFormed(Ty<...>) :- WellFormed(WC1), ..., WellFormed(WCm)` - // } - // ``` - - let bound_vars = InternalSubsts::bound_vars_for_item(tcx, def_id); - - // `Ty<...>` - let ty = tcx.type_of(def_id).subst(tcx, bound_vars); - - // Warning: these where clauses are not substituted for bound vars yet, - // so that we don't need to adjust binders in the `FromEnv` rules below - // (see the FIXME). - let where_clauses = - tcx.predicates_of(def_id).predicates.iter().map(|(wc, _)| wc.lower()).collect::>(); - - // `WellFormed(Ty<...>) :- WellFormed(WC1), ..., WellFormed(WCm)` - let well_formed_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(ty)), - hypotheses: tcx.mk_goals( - where_clauses - .iter() - .map(|wc| wc.subst(tcx, bound_vars)) - .map(|wc| wc.map_bound(|bound| bound.into_well_formed_goal())) - .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))), - ), - category: ProgramClauseCategory::WellFormed, - }; - let well_formed_clause = Clause::ForAll(ty::Binder::bind(well_formed_clause)); - - // Rule Implied-Bound-From-Type - // - // For each where clause `WC`: - // ``` - // forall { - // FromEnv(WC) :- FromEnv(Ty<...>) - // } - // ``` - - // `FromEnv(Ty<...>)` - let from_env_goal = tcx.mk_goal(DomainGoal::FromEnv(FromEnv::Ty(ty)).into_goal()); - let hypotheses = tcx.intern_goals(&[from_env_goal]); - - // For each where clause `WC`: - let from_env_clauses = where_clauses - .into_iter() - // `FromEnv(WC) :- FromEnv(Ty<...>)` - .map(|wc| { - // move the binders to the left - wc.map_bound(|goal| ProgramClause { - // FIXME: we inject `bound_vars` and `hypotheses` into this binding - // level, which may be incorrect in the future: see the FIXME in - // `program_clauses_for_trait`. - goal: goal.subst(tcx, bound_vars).into_from_env_goal(), - hypotheses, - - category: ProgramClauseCategory::ImpliedBound, - }) - }) - .map(Clause::ForAll); - - tcx.mk_clauses(iter::once(well_formed_clause).chain(from_env_clauses)) -} - -pub fn program_clauses_for_associated_type_def(tcx: TyCtxt<'_>, item_id: DefId) -> Clauses<'_> { - // Rule ProjectionEq-Placeholder - // - // ``` - // trait Trait { - // type AssocType; - // } - // ``` - // - // `ProjectionEq` can succeed by skolemizing, see "associated type" - // chapter for more: - // ``` - // forall { - // ProjectionEq( - // >::AssocType = - // (Trait::AssocType) - // ) - // } - // ``` - - let item = tcx.associated_item(item_id); - debug_assert_eq!(item.kind, ty::AssocKind::Type); - let trait_id = match item.container { - ty::AssocItemContainer::TraitContainer(trait_id) => trait_id, - _ => bug!("not an trait container"), - }; - - let trait_bound_vars = InternalSubsts::bound_vars_for_item(tcx, trait_id); - let trait_ref = ty::TraitRef { def_id: trait_id, substs: trait_bound_vars }; - - let projection_ty = ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, item.ident); - let placeholder_ty = tcx.mk_ty(ty::UnnormalizedProjection(projection_ty)); - let projection_eq = - WhereClause::ProjectionEq(ty::ProjectionPredicate { projection_ty, ty: placeholder_ty }); - - let projection_eq_clause = ProgramClause { - goal: DomainGoal::Holds(projection_eq), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::Other, - }; - let projection_eq_clause = Clause::ForAll(ty::Binder::bind(projection_eq_clause)); - - // Rule WellFormed-AssocTy - // ``` - // forall { - // WellFormed((Trait::AssocType)) - // :- WellFormed(Self: Trait) - // } - // ``` - - let trait_predicate = ty::TraitPredicate { trait_ref }; - let hypothesis = - tcx.mk_goal(DomainGoal::WellFormed(WellFormed::Trait(trait_predicate)).into_goal()); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(placeholder_ty)), - hypotheses: tcx.mk_goals(iter::once(hypothesis)), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // Rule Implied-Trait-From-AssocTy - // ``` - // forall { - // FromEnv(Self: Trait) - // :- FromEnv((Trait::AssocType)) - // } - // ``` - - let hypothesis = tcx.mk_goal(DomainGoal::FromEnv(FromEnv::Ty(placeholder_ty)).into_goal()); - - let from_env_clause = ProgramClause { - goal: DomainGoal::FromEnv(FromEnv::Trait(trait_predicate)), - hypotheses: tcx.mk_goals(iter::once(hypothesis)), - category: ProgramClauseCategory::ImpliedBound, - }; - let from_env_clause = Clause::ForAll(ty::Binder::bind(from_env_clause)); - - // Rule ProjectionEq-Normalize - // - // ProjectionEq can succeed by normalizing: - // ``` - // forall { - // ProjectionEq(>::AssocType = U) :- - // Normalize(>::AssocType -> U) - // } - // ``` - - let offset = tcx.generics_of(trait_id).params.iter().map(|p| p.index).max().unwrap_or(0); - // Add a new type param after the existing ones (`U` in the comment above). - let ty_var = ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(offset + 1).into()); - - // `ProjectionEq(>::AssocType = U)` - let projection = ty::ProjectionPredicate { projection_ty, ty: tcx.mk_ty(ty_var) }; - - // `Normalize(>::AssocType -> U)` - let hypothesis = tcx.mk_goal(DomainGoal::Normalize(projection).into_goal()); - - // ProjectionEq(>::AssocType = U) :- - // Normalize(>::AssocType -> U) - let normalize_clause = ProgramClause { - goal: DomainGoal::Holds(WhereClause::ProjectionEq(projection)), - hypotheses: tcx.mk_goals(iter::once(hypothesis)), - category: ProgramClauseCategory::Other, - }; - let normalize_clause = Clause::ForAll(ty::Binder::bind(normalize_clause)); - - let clauses = iter::once(projection_eq_clause) - .chain(iter::once(wf_clause)) - .chain(iter::once(from_env_clause)) - .chain(iter::once(normalize_clause)); - - tcx.mk_clauses(clauses) -} - -pub fn program_clauses_for_associated_type_value(tcx: TyCtxt<'_>, item_id: DefId) -> Clauses<'_> { - // Rule Normalize-From-Impl (see rustc dev guide) - // - // ``` - // impl Trait for A0 { - // type AssocType = T; - // } - // ``` - // - // FIXME: For the moment, we don't account for where clauses written on the associated - // ty definition (i.e., in the trait def, as in `type AssocType where T: Sized`). - // ``` - // forall { - // forall { - // Normalize(>::AssocType -> T) :- - // Implemented(A0: Trait) - // } - // } - // ``` - - let item = tcx.associated_item(item_id); - debug_assert_eq!(item.kind, ty::AssocKind::Type); - let impl_id = match item.container { - ty::AssocItemContainer::ImplContainer(impl_id) => impl_id, - _ => bug!("not an impl container"), - }; - - let impl_bound_vars = InternalSubsts::bound_vars_for_item(tcx, impl_id); - - // `A0 as Trait` - let trait_ref = tcx.impl_trait_ref(impl_id).unwrap().subst(tcx, impl_bound_vars); - - // `T` - let ty = tcx.type_of(item_id); - - // `Implemented(A0: Trait)` - let trait_implemented: DomainGoal<'_> = ty::TraitPredicate { trait_ref }.lower(); - - // `>::AssocType` - let projection_ty = ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, item.ident); - - // `Normalize(>::AssocType -> T)` - let normalize_goal = DomainGoal::Normalize(ty::ProjectionPredicate { projection_ty, ty }); - - // `Normalize(... -> T) :- ...` - let normalize_clause = ProgramClause { - goal: normalize_goal, - hypotheses: tcx.mk_goals(iter::once(tcx.mk_goal(GoalKind::DomainGoal(trait_implemented)))), - category: ProgramClauseCategory::Other, - }; - let normalize_clause = Clause::ForAll(ty::Binder::bind(normalize_clause)); - - tcx.mk_clauses(iter::once(normalize_clause)) -} - -pub fn dump_program_clauses(tcx: TyCtxt<'_>) { - if !tcx.features().rustc_attrs { - return; - } - - let mut visitor = ClauseDumper { tcx }; - tcx.hir().krate().visit_all_item_likes(&mut visitor.as_deep_visitor()); -} - -struct ClauseDumper<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl ClauseDumper<'tcx> { - fn process_attrs(&mut self, hir_id: hir::HirId, attrs: &[ast::Attribute]) { - let def_id = self.tcx.hir().local_def_id(hir_id); - for attr in attrs { - let mut clauses = None; - - if attr.check_name(sym::rustc_dump_program_clauses) { - clauses = Some(self.tcx.program_clauses_for(def_id)); - } - - if attr.check_name(sym::rustc_dump_env_program_clauses) { - let environment = self.tcx.environment(def_id); - clauses = Some(self.tcx.program_clauses_for_env(environment)); - } - - if let Some(clauses) = clauses { - let mut err = self.tcx.sess.struct_span_err(attr.span, "program clause dump"); - - let mut strings: Vec<_> = clauses.iter().map(|clause| clause.to_string()).collect(); - - strings.sort(); - - for string in strings { - err.note(&string); - } - - err.emit(); - } - } - } -} - -impl Visitor<'tcx> for ClauseDumper<'tcx> { - type Map = Map<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) - } - - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - self.process_attrs(item.hir_id, &item.attrs); - intravisit::walk_item(self, item); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - self.process_attrs(trait_item.hir_id, &trait_item.attrs); - intravisit::walk_trait_item(self, trait_item); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - self.process_attrs(impl_item.hir_id, &impl_item.attrs); - intravisit::walk_impl_item(self, impl_item); - } - - fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) { - self.process_attrs(s.hir_id, &s.attrs); - intravisit::walk_struct_field(self, s); - } -} diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs index c2fb237a05b54..ed30ed5313e5c 100644 --- a/src/librustc_traits/normalize_erasing_regions.rs +++ b/src/librustc_traits/normalize_erasing_regions.rs @@ -1,23 +1,24 @@ -use rustc::traits::query::NoSolution; -use rustc::ty::query::Providers; -use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; use rustc_trait_selection::traits::query::normalize::AtExt; use rustc_trait_selection::traits::{Normalized, ObligationCause}; use std::sync::atomic::Ordering; crate fn provide(p: &mut Providers<'_>) { - *p = Providers { normalize_ty_after_erasing_regions, ..*p }; + *p = Providers { normalize_generic_arg_after_erasing_regions, ..*p }; } -fn normalize_ty_after_erasing_regions<'tcx>( +fn normalize_generic_arg_after_erasing_regions<'tcx>( tcx: TyCtxt<'tcx>, - goal: ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> Ty<'tcx> { - debug!("normalize_ty_after_erasing_regions(goal={:#?})", goal); + goal: ParamEnvAnd<'tcx, GenericArg<'tcx>>, +) -> GenericArg<'tcx> { + debug!("normalize_generic_arg_after_erasing_regions(goal={:#?})", goal); let ParamEnvAnd { param_env, value } = goal; - tcx.sess.perf_stats.normalize_ty_after_erasing_regions.fetch_add(1, Ordering::Relaxed); + tcx.sess.perf_stats.normalize_generic_arg_after_erasing_regions.fetch_add(1, Ordering::Relaxed); tcx.infer_ctxt().enter(|infcx| { let cause = ObligationCause::dummy(); match infcx.at(&cause, param_env).normalize(&value) { @@ -47,6 +48,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool { | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::Subtype(..) - | ty::Predicate::ConstEvaluatable(..) => true, + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => true, } } diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index 57abff769de9b..d6d3e86a2c8d3 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -1,10 +1,8 @@ -use rustc::ty::query::Providers; -use rustc::ty::{ParamEnvAnd, TyCtxt}; -use rustc_hir as hir; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::TraitEngineExt as _; -use rustc_span::DUMMY_SP; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::{ normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution, @@ -27,7 +25,7 @@ fn normalize_projection_ty<'tcx>( &goal, |infcx, fulfill_cx, ParamEnvAnd { param_env, value: goal }| { let selcx = &mut SelectionContext::new(infcx); - let cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID); + let cause = ObligationCause::dummy(); let mut obligations = vec![]; let answer = traits::normalize_projection_type( selcx, diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs index 7ed828c91679c..aeb31c2cb7367 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -1,14 +1,14 @@ -use rustc::ty::query::Providers; -use rustc::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts}; -use rustc::ty::{ - FnSig, Lift, ParamEnv, ParamEnvAnd, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable, Variance, -}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts}; +use rustc_middle::ty::{ + FnSig, Lift, ParamEnv, ParamEnvAnd, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable, Variance, +}; use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::infer::InferCtxtExt; diff --git a/src/librustc_ty/Cargo.toml b/src/librustc_ty/Cargo.toml index cf0b4b82eea72..b6db75e44f971 100644 --- a/src/librustc_ty/Cargo.toml +++ b/src/librustc_ty/Cargo.toml @@ -10,8 +10,9 @@ path = "lib.rs" [dependencies] log = "0.4" -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_data_structures = { path = "../librustc_data_structures" } +rustc_errors = { path = "../librustc_errors" } rustc_hir = { path = "../librustc_hir" } rustc_infer = { path = "../librustc_infer" } rustc_span = { path = "../librustc_span" } diff --git a/src/librustc_ty/common_traits.rs b/src/librustc_ty/common_traits.rs index 311ba383f3055..265b811571afe 100644 --- a/src/librustc_ty/common_traits.rs +++ b/src/librustc_ty/common_traits.rs @@ -1,8 +1,8 @@ //! Queries for checking whether a type implements one of a few common traits. -use rustc::middle::lang_items; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_hir::lang_items; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits; diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index a5abe7b6413cc..a793031d4025b 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -1,18 +1,21 @@ -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Instance, TyCtxt, TypeFoldable}; +use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Instance, TyCtxt, TypeFoldable}; use rustc_span::sym; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; +use traits::{translate_substs, Reveal}; use log::debug; -pub fn resolve_instance<'tcx>( +fn resolve_instance<'tcx>( tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, -) -> Option> { + key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)>, +) -> Result>, ErrorReported> { + let (param_env, (def_id, substs)) = key.into_parts(); + debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env); @@ -38,7 +41,7 @@ pub fn resolve_instance<'tcx>( if ty.needs_drop(tcx, param_env) { // `DropGlue` requires a monomorphic aka concrete type. if ty.needs_subst() { - return None; + return Ok(None); } debug!(" => nontrivial drop glue"); @@ -53,7 +56,7 @@ pub fn resolve_instance<'tcx>( ty::InstanceDef::Item(def_id) } }; - Some(Instance { def, substs }) + Ok(Some(Instance { def, substs })) }; debug!("resolve(def_id={:?}, substs={:?}) = {:?}", def_id, substs, result); result @@ -65,7 +68,7 @@ fn resolve_associated_item<'tcx>( param_env: ty::ParamEnv<'tcx>, trait_id: DefId, rcvr_substs: SubstsRef<'tcx>, -) -> Option> { +) -> Result>, ErrorReported> { let def_id = trait_item.def_id; debug!( "resolve_associated_item(trait_item={:?}, \ @@ -80,31 +83,103 @@ fn resolve_associated_item<'tcx>( // Now that we know which impl is being used, we can dispatch to // the actual function: - match vtbl { + Ok(match vtbl { traits::VtableImpl(impl_data) => { - let (def_id, substs) = - traits::find_associated_item(tcx, param_env, trait_item, rcvr_substs, &impl_data); - - let resolved_item = tcx.associated_item(def_id); + debug!( + "resolving VtableImpl: {:?}, {:?}, {:?}, {:?}", + param_env, trait_item, rcvr_substs, impl_data + ); + assert!(!rcvr_substs.needs_infer()); + assert!(!trait_ref.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); + let trait_def = tcx.trait_def(trait_def_id); + let leaf_def = trait_def + .ancestors(tcx, impl_data.impl_def_id)? + .leaf_def(tcx, trait_item.ident, trait_item.kind) + .unwrap_or_else(|| { + bug!("{:?} not found in {:?}", trait_item, impl_data.impl_def_id); + }); + + let substs = tcx.infer_ctxt().enter(|infcx| { + let param_env = param_env.with_reveal_all(); + let substs = rcvr_substs.rebase_onto(tcx, trait_def_id, impl_data.substs); + let substs = translate_substs( + &infcx, + param_env, + impl_data.impl_def_id, + substs, + leaf_def.defining_node, + ); + infcx.tcx.erase_regions(&substs) + }); // Since this is a trait item, we need to see if the item is either a trait default item // or a specialization because we can't resolve those unless we can `Reveal::All`. // NOTE: This should be kept in sync with the similar code in - // `rustc::traits::project::assemble_candidates_from_impls()`. - let eligible = if !resolved_item.defaultness.is_default() { + // `rustc_trait_selection::traits::project::assemble_candidates_from_impls()`. + let eligible = if leaf_def.is_final() { + // Non-specializable items are always projectable. true - } else if param_env.reveal == traits::Reveal::All { - !trait_ref.needs_subst() } else { - false + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal == Reveal::All { + !trait_ref.still_further_specializable() + } else { + false + } }; if !eligible { - return None; + return Ok(None); } let substs = tcx.erase_regions(&substs); - Some(ty::Instance::new(def_id, substs)) + + // Check if we just resolved an associated `const` declaration from + // a `trait` to an associated `const` definition in an `impl`, where + // the definition in the `impl` has the wrong type (for which an + // error has already been/will be emitted elsewhere). + // + // NB: this may be expensive, we try to skip it in all the cases where + // we know the error would've been caught (e.g. in an upstream crate). + // + // A better approach might be to just introduce a query (returning + // `Result<(), ErrorReported>`) for the check that `rustc_typeck` + // performs (i.e. that the definition's type in the `impl` matches + // the declaration in the `trait`), so that we can cheaply check + // here if it failed, instead of approximating it. + if trait_item.kind == ty::AssocKind::Const + && trait_item.def_id != leaf_def.item.def_id + && leaf_def.item.def_id.is_local() + { + let normalized_type_of = |def_id, substs| { + tcx.subst_and_normalize_erasing_regions(substs, param_env, &tcx.type_of(def_id)) + }; + + let original_ty = normalized_type_of(trait_item.def_id, rcvr_substs); + let resolved_ty = normalized_type_of(leaf_def.item.def_id, substs); + + if original_ty != resolved_ty { + let msg = format!( + "Instance::resolve: inconsistent associated `const` type: \ + was `{}: {}` but resolved to `{}: {}`", + tcx.def_path_str_with_substs(trait_item.def_id, rcvr_substs), + original_ty, + tcx.def_path_str_with_substs(leaf_def.item.def_id, substs), + resolved_ty, + ); + let span = tcx.def_span(leaf_def.item.def_id); + tcx.sess.delay_span_bug(span, &msg); + + return Err(ErrorReported); + } + } + + Some(ty::Instance::new(leaf_def.item.def_id, substs)) } traits::VtableGenerator(generator_data) => Some(Instance { def: ty::InstanceDef::Item(generator_data.generator_def_id), @@ -122,7 +197,7 @@ fn resolve_associated_item<'tcx>( traits::VtableFnPointer(ref data) => { // `FnPtrShim` requires a monomorphic aka concrete type. if data.fn_ty.needs_subst() { - return None; + return Ok(None); } Some(Instance { @@ -143,7 +218,7 @@ fn resolve_associated_item<'tcx>( // `CloneShim` requires a monomorphic aka concrete type. if self_ty.needs_subst() { - return None; + return Ok(None); } Some(Instance { @@ -161,6 +236,13 @@ fn resolve_associated_item<'tcx>( None } } - traits::VtableAutoImpl(..) | traits::VtableParam(..) | traits::VtableTraitAlias(..) => None, - } + traits::VtableAutoImpl(..) + | traits::VtableParam(..) + | traits::VtableTraitAlias(..) + | traits::VtableDiscriminantKind(..) => None, + }) +} + +pub fn provide(providers: &mut ty::query::Providers<'_>) { + *providers = ty::query::Providers { resolve_instance, ..*providers }; } diff --git a/src/librustc_ty/lib.rs b/src/librustc_ty/lib.rs index f9ee4e20d2721..75e62e796408a 100644 --- a/src/librustc_ty/lib.rs +++ b/src/librustc_ty/lib.rs @@ -10,11 +10,11 @@ #![recursion_limit = "256"] #[macro_use] -extern crate rustc; +extern crate rustc_middle; #[macro_use] extern crate log; -use rustc::ty::query::Providers; +use rustc_middle::ty::query::Providers; mod common_traits; pub mod instance; @@ -25,4 +25,5 @@ pub fn provide(providers: &mut Providers<'_>) { common_traits::provide(providers); needs_drop::provide(providers); ty::provide(providers); + instance::provide(providers); } diff --git a/src/librustc_ty/needs_drop.rs b/src/librustc_ty/needs_drop.rs index 37af8168f87d9..97994b465b54c 100644 --- a/src/librustc_ty/needs_drop.rs +++ b/src/librustc_ty/needs_drop.rs @@ -1,10 +1,10 @@ //! Check whether a type has (potentially) non-trivial drop glue. -use rustc::ty::subst::Subst; -use rustc::ty::util::{needs_drop_components, AlwaysRequiresDrop}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; type NeedsDropResult = Result; @@ -99,6 +99,23 @@ where } } + ty::Generator(_, substs, _) => { + let substs = substs.as_generator(); + for upvar_ty in substs.upvar_tys() { + queue_type(self, upvar_ty); + } + + let witness = substs.witness(); + let interior_tys = match &witness.kind { + ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys), + _ => bug!(), + }; + + for interior_ty in interior_tys { + queue_type(self, interior_ty); + } + } + // Check for a `Drop` impl and whether this is a union or // `ManuallyDrop`. If it's a struct or enum without a `Drop` // impl then check whether the field types need `Drop`. diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs index 4b522997537b7..1aa11a761c821 100644 --- a/src/librustc_ty/ty.rs +++ b/src/librustc_ty/ty.rs @@ -1,9 +1,9 @@ -use rustc::hir::map as hir_map; -use rustc::ty::subst::Subst; -use rustc::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_data_structures::svh::Svh; use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_session::CrateDisambiguator; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -47,8 +47,6 @@ fn sized_constraint_for_ty<'tcx>( vec![ty] } - UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - Param(..) => { // perf hack: if there is a `T: Sized` bound, then // we know that `T` is Sized and do not need to check @@ -78,14 +76,14 @@ fn sized_constraint_for_ty<'tcx>( fn associated_item_from_trait_item_ref( tcx: TyCtxt<'_>, - parent_def_id: DefId, + parent_def_id: LocalDefId, parent_vis: &hir::Visibility<'_>, trait_item_ref: &hir::TraitItemRef, ) -> ty::AssocItem { let def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); let (kind, has_self) = match trait_item_ref.kind { hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Method { has_self } => (ty::AssocKind::Method, has_self), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), hir::AssocItemKind::Type => (ty::AssocKind::Type, false), hir::AssocItemKind::OpaqueTy => bug!("only impls can have opaque types"), }; @@ -96,21 +94,21 @@ fn associated_item_from_trait_item_ref( // Visibility of trait items is inherited from their traits. vis: ty::Visibility::from_hir(parent_vis, trait_item_ref.id.hir_id, tcx), defaultness: trait_item_ref.defaultness, - def_id, - container: ty::TraitContainer(parent_def_id), - method_has_self_argument: has_self, + def_id: def_id.to_def_id(), + container: ty::TraitContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, } } fn associated_item_from_impl_item_ref( tcx: TyCtxt<'_>, - parent_def_id: DefId, + parent_def_id: LocalDefId, impl_item_ref: &hir::ImplItemRef<'_>, ) -> ty::AssocItem { let def_id = tcx.hir().local_def_id(impl_item_ref.id.hir_id); let (kind, has_self) = match impl_item_ref.kind { hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Method { has_self } => (ty::AssocKind::Method, has_self), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), hir::AssocItemKind::Type => (ty::AssocKind::Type, false), hir::AssocItemKind::OpaqueTy => (ty::AssocKind::OpaqueTy, false), }; @@ -121,14 +119,14 @@ fn associated_item_from_impl_item_ref( // Visibility of trait impl items doesn't matter. vis: ty::Visibility::from_hir(&impl_item_ref.vis, impl_item_ref.id.hir_id, tcx), defaultness: impl_item_ref.defaultness, - def_id, - container: ty::ImplContainer(parent_def_id), - method_has_self_argument: has_self, + def_id: def_id.to_def_id(), + container: ty::ImplContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, } } fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { - let id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let id = tcx.hir().as_local_hir_id(def_id.expect_local()); let parent_id = tcx.hir().get_parent_item(id); let parent_def_id = tcx.hir().local_def_id(parent_id); let parent_item = tcx.hir().expect_item(parent_id); @@ -165,6 +163,16 @@ fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { ) } +fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let item = tcx.hir().expect_item(hir_id); + if let hir::ItemKind::Impl { defaultness, .. } = item.kind { + defaultness + } else { + bug!("`impl_defaultness` called on {:?}", item); + } +} + /// Calculates the `Sized` constraint. /// /// In fact, there are only a few options for the types in the constraint: @@ -190,29 +198,29 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain } fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { - let id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let id = tcx.hir().as_local_hir_id(def_id.expect_local()); let item = tcx.hir().expect_item(id); match item.kind { hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( trait_item_refs .iter() .map(|trait_item_ref| trait_item_ref.id) - .map(|id| tcx.hir().local_def_id(id.hir_id)), + .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), ), hir::ItemKind::Impl { ref items, .. } => tcx.arena.alloc_from_iter( items .iter() .map(|impl_item_ref| impl_item_ref.id) - .map(|id| tcx.hir().local_def_id(id.hir_id)), + .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), ), hir::ItemKind::TraitAlias(..) => &[], _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), } } -fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AssociatedItems { +fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> { let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); - tcx.arena.alloc(ty::AssociatedItems::new(items)) + ty::AssociatedItems::new(items) } fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span { @@ -252,12 +260,18 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // are any errors at that point, so after type checking you can be // sure that this will succeed without errors anyway. - let unnormalized_env = - ty::ParamEnv::new(tcx.intern_predicates(&predicates), traits::Reveal::UserFacing, None); + let unnormalized_env = ty::ParamEnv::new( + tcx.intern_predicates(&predicates), + traits::Reveal::UserFacing, + tcx.sess.opts.debugging_opts.chalk.then_some(def_id), + ); - let body_id = tcx.hir().as_local_hir_id(def_id).map_or(hir::DUMMY_HIR_ID, |id| { - tcx.hir().maybe_body_owned_by(id).map_or(id, |body| body.hir_id) - }); + let body_id = def_id + .as_local() + .map(|def_id| tcx.hir().as_local_hir_id(def_id)) + .map_or(hir::CRATE_HIR_ID, |id| { + tcx.hir().maybe_body_owned_by(id).map_or(id, |body| body.hir_id) + }); let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id); traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause) } @@ -294,7 +308,7 @@ fn instance_def_size_estimate<'tcx>( /// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`. /// -/// See [`ImplOverlapKind::Issue33140`] for more details. +/// See [`ty::ImplOverlapKind::Issue33140`] for more details. fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { debug!("issue33140_self_ty({:?})", def_id); @@ -342,10 +356,7 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { /// Check if a function is async. fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { - let hir_id = tcx - .hir() - .as_local_hir_id(def_id) - .unwrap_or_else(|| bug!("asyncness: expected local `DefId`, got `{:?}`", def_id)); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); @@ -371,6 +382,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { crate_hash, instance_def_size_estimate, issue33140_self_ty, + impl_defaultness, ..*providers }; } diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index e61a36f844f87..8aaa29bc582fc 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] arena = { path = "../libarena" } log = "0.4" -rustc = { path = "../librustc" } +rustc_middle = { path = "../librustc_middle" } rustc_attr = { path = "../librustc_attr" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 6f558ec9b9508..6529d784ad452 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -6,27 +6,27 @@ // ignore-tidy-filelength use crate::collect::PlaceholderHirTyCollector; -use crate::middle::lang_items::SizedTraitLangItem; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; -use crate::util::common::ErrorReported; -use rustc::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; -use rustc::ty::{self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc::ty::{GenericParamDef, GenericParamDefKind}; -use rustc_ast::ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId}; +use rustc_errors::ErrorReported; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, FatalError}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_generics, Visitor}; -use rustc_hir::print; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::{walk_generics, Visitor as _}; +use rustc_hir::lang_items::SizedTraitLangItem; use rustc_hir::{Constness, GenericArg, GenericArgs}; +use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{ + self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; +use rustc_middle::ty::{GenericParamDef, GenericParamDefKind}; use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, LATE_BOUND_LIFETIME_ARGUMENTS}; use rustc_session::parse::feature_err; use rustc_session::Session; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits; @@ -113,7 +113,7 @@ pub enum SizedByDefault { } struct ConvertedBinding<'a, 'tcx> { - item_name: ast::Ident, + item_name: Ident, kind: ConvertedBindingKind<'a, 'tcx>, span: Span, } @@ -146,13 +146,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def: Option<&ty::GenericParamDef>, ) -> ty::Region<'tcx> { let tcx = self.tcx(); - let lifetime_name = |def_id| tcx.hir().name(tcx.hir().as_local_hir_id(def_id).unwrap()); + let lifetime_name = |def_id| tcx.hir().name(tcx.hir().as_local_hir_id(def_id)); let r = match tcx.named_region(lifetime.hir_id) { Some(rl::Region::Static) => tcx.lifetimes.re_static, Some(rl::Region::LateBound(debruijn, id, _)) => { - let name = lifetime_name(id); + let name = lifetime_name(id.expect_local()); tcx.mk_region(ty::ReLateBound(debruijn, ty::BrNamed(id, name))) } @@ -161,12 +161,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } Some(rl::Region::EarlyBound(index, id, _)) => { - let name = lifetime_name(id); + let name = lifetime_name(id.expect_local()); tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) } Some(rl::Region::Free(scope, id)) => { - let name = lifetime_name(id); + let name = lifetime_name(id.expect_local()); tcx.mk_region(ty::ReFree(ty::FreeRegion { scope, bound_region: ty::BrNamed(id, name), @@ -212,7 +212,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { None, ); - assoc_bindings.first().map(|b| Self::prohibit_assoc_ty_binding(self.tcx(), b.span)); + if let Some(b) = assoc_bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + } substs } @@ -591,8 +593,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { args.next(); params.next(); } - (GenericArg::Type(_), GenericParamDefKind::Lifetime) - | (GenericArg::Const(_), GenericParamDefKind::Lifetime) => { + ( + GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDefKind::Lifetime, + ) => { // We expected a lifetime argument, but got a type or const // argument. That means we're inferring the lifetimes. substs.push(inferred_kind(None, param, infer_args)); @@ -736,8 +740,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let default_needs_object_self = |param: &ty::GenericParamDef| { if let GenericParamDefKind::Type { has_default, .. } = param.kind { if is_object && has_default { + let default_ty = tcx.at(span).type_of(param.def_id); let self_param = tcx.types.self_param; - if tcx.at(span).type_of(param.def_id).walk().any(|ty| ty == self_param) { + if default_ty.walk().any(|arg| arg == self_param.into()) { // There is no suitable inference default for a type parameter // that references self, in an object type. return true; @@ -780,7 +785,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - let ct_def_id = tcx.hir().local_def_id(ct.value.hir_id).expect_local(); + let ct_def_id = tcx.hir().local_def_id(ct.value.hir_id); ty::Const::from_anon_const(tcx, ct_def_id).into() } _ => unreachable!(), @@ -824,14 +829,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } GenericParamDefKind::Const => { + let ty = tcx.at(span).type_of(param.def_id); // FIXME(const_generics:defaults) if infer_args { // No const parameters were provided, we can infer all. - let ty = tcx.at(span).type_of(param.def_id); self.ct_infer(ty, Some(param), span).into() } else { // We've already errored above about the mismatch. - tcx.consts.err.into() + tcx.mk_const(ty::Const { val: ty::ConstKind::Error, ty }).into() } } } @@ -991,7 +996,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ast_path_to_mono_trait_ref( trait_ref.path.span, - trait_ref.trait_def_id(), + trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), self_ty, trait_ref.path.segments.last().unwrap(), ) @@ -1007,24 +1012,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { bounds: &mut Bounds<'tcx>, speculative: bool, ) -> Result<(), GenericArgCountMismatch> { - let trait_def_id = trait_ref.trait_def_id(); + let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); - let path_span = if let [segment] = &trait_ref.path.segments[..] { - // FIXME: `trait_ref.path.span` can point to a full path with multiple - // segments, even though `trait_ref.path.segments` is of length `1`. Work - // around that bug here, even though it should be fixed elsewhere. - // This would otherwise cause an invalid suggestion. For an example, look at - // `src/test/ui/issues/issue-28344.rs`. - segment.ident.span - } else { - trait_ref.path.span - }; let (substs, assoc_bindings, arg_count_correct) = self.create_substs_for_ast_trait_ref( - path_span, + trait_ref.path.span, trait_def_id, self_ty, trait_ref.path.segments.last().unwrap(), @@ -1043,7 +1038,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { bounds, speculative, &mut dup_bindings, - span, + binding.span, ); // Okay to ignore `Err` because of `ErrorReported` (see above). } @@ -1101,7 +1096,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) -> ty::TraitRef<'tcx> { let (substs, assoc_bindings, _) = self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment); - assoc_bindings.first().map(|b| AstConv::prohibit_assoc_ty_binding(self.tcx(), b.span)); + if let Some(b) = assoc_bindings.first() { + AstConv::prohibit_assoc_ty_binding(self.tcx(), b.span); + } ty::TraitRef::new(trait_def_id, substs) } @@ -1118,6 +1115,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if !self.tcx().features().unboxed_closures && trait_segment.generic_args().parenthesized != trait_def.paren_sugar { + let sess = &self.tcx().sess.parse_sess; // For now, require that parenthetical notation be used only with `Fn()` etc. let (msg, sugg) = if trait_def.paren_sugar { ( @@ -1132,7 +1130,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .and_then(|args| args.args.get(0)) .and_then(|arg| match arg { hir::GenericArg::Type(ty) => { - Some(print::to_string(print::NO_ANN, |s| s.print_type(ty))) + sess.source_map().span_to_snippet(ty.span).ok() } _ => None, }) @@ -1141,20 +1139,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .generic_args() .bindings .iter() - .filter_map(|b| match (b.ident.as_str() == "Output", &b.kind) { + .find_map(|b| match (b.ident.as_str() == "Output", &b.kind) { (true, hir::TypeBindingKind::Equality { ty }) => { - Some(print::to_string(print::NO_ANN, |s| s.print_type(ty))) + sess.source_map().span_to_snippet(ty.span).ok() } _ => None, }) - .next() .unwrap_or_else(|| "()".to_string()), )), ) } else { ("parenthetical notation is only stable when used with `Fn`-family traits", None) }; - let sess = &self.tcx().sess.parse_sess; let mut err = feature_err(sess, sym::unboxed_closures, span, msg); if let Some(sugg) = sugg { let msg = "use parenthetical notation instead"; @@ -1186,11 +1182,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) } - fn trait_defines_associated_type_named( - &self, - trait_def_id: DefId, - assoc_name: ast::Ident, - ) -> bool { + fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { self.tcx() .associated_items(trait_def_id) .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id) @@ -1248,7 +1240,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// This helper takes a *converted* parameter type (`param_ty`) /// and an *unconverted* list of bounds: /// - /// ``` + /// ```text /// fn foo /// ^ ^^^^^ `ast_bounds` parameter, in HIR form /// | @@ -1581,7 +1573,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx, span, item.trait_ref().def_id(), - object_safety_violations, + &object_safety_violations[..], ) .emit(); return tcx.types.err; @@ -1599,12 +1591,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for (base_trait_ref, span, constness) in regular_traits_refs_spans { assert_eq!(constness, Constness::NotConst); - for trait_ref in traits::elaborate_trait_ref(tcx, base_trait_ref) { + for obligation in traits::elaborate_trait_ref(tcx, base_trait_ref) { debug!( "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", - trait_ref + obligation.predicate ); - match trait_ref { + match obligation.predicate { ty::Predicate::Trait(pred, _) => { associated_types.entry(span).or_default().extend( tcx.associated_items(pred.def_id()) @@ -1616,7 +1608,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::Predicate::Projection(pred) => { // A `Self` within the original bound will be substituted with a // `trait_object_dummy_self`, so check for that. - let references_self = pred.skip_binder().ty.walk().any(|t| t == dummy_self); + let references_self = + pred.skip_binder().ty.walk().any(|arg| arg == dummy_self.into()); // If the projection output contains `Self`, force the user to // elaborate it explicitly to avoid a lot of complexity. @@ -1724,6 +1717,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ast_region_to_region(lifetime, None) } else { self.re_infer(None, span).unwrap_or_else(|| { + // FIXME: these can be redundant with E0106, but not always. struct_span_err!( tcx.sess, span, @@ -1754,7 +1748,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { potential_assoc_types: Vec, trait_bounds: &[hir::PolyTraitRef<'_>], ) { - if !associated_types.values().any(|v| !v.is_empty()) { + if associated_types.values().all(|v| v.is_empty()) { return; } let tcx = self.tcx(); @@ -1784,9 +1778,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { )); } } - - match (&potential_assoc_types[..], &trait_bounds) { - ([], [bound]) => match &bound.trait_ref.path.segments[..] { + if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { + match &bound.trait_ref.path.segments[..] { // FIXME: `trait_ref.path.span` can point to a full path with multiple // segments, even though `trait_ref.path.segments` is of length `1`. Work // around that bug here, even though it should be fixed elsewhere. @@ -1818,8 +1811,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .collect(); } _ => {} - }, - _ => {} + } } names.sort(); trait_bound_spans.sort(); @@ -1941,7 +1933,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, type_str: &str, trait_str: &str, - name: ast::Name, + name: Symbol, ) { let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type"); if let (Some(_), Ok(snippet)) = ( @@ -1971,8 +1963,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // any ambiguity. fn find_bound_for_assoc_item( &self, - ty_param_def_id: DefId, - assoc_name: ast::Ident, + ty_param_def_id: LocalDefId, + assoc_name: Ident, span: Span, ) -> Result, ErrorReported> { let tcx = self.tcx(); @@ -1982,11 +1974,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty_param_def_id, assoc_name, span, ); - let predicates = &self.get_type_parameter_bounds(span, ty_param_def_id).predicates; + let predicates = + &self.get_type_parameter_bounds(span, ty_param_def_id.to_def_id()).predicates; debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); - let param_hir_id = tcx.hir().as_local_hir_id(ty_param_def_id).unwrap(); + let param_hir_id = tcx.hir().as_local_hir_id(ty_param_def_id); let param_name = tcx.hir().ty_param_name(param_hir_id); self.one_bound_for_assoc_type( || { @@ -2008,7 +2001,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, all_candidates: impl Fn() -> I, ty_param_name: impl Fn() -> String, - assoc_name: ast::Ident, + assoc_name: Ident, span: Span, is_equality: impl Fn() -> Option, ) -> Result, ErrorReported> @@ -2126,7 +2119,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, all_candidates: impl Fn() -> I, ty_param_name: &str, - assoc_name: ast::Ident, + assoc_name: Ident, span: Span, ) where I: Iterator>, @@ -2230,10 +2223,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { || None, )? } - (&ty::Param(_), Res::SelfTy(Some(param_did), None)) - | (&ty::Param(_), Res::Def(DefKind::TyParam, param_did)) => { - self.find_bound_for_assoc_item(param_did, assoc_ident, span)? - } + ( + &ty::Param(_), + Res::SelfTy(Some(param_did), None) | Res::Def(DefKind::TyParam, param_did), + ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, _ => { if variant_resolution.is_some() { // Variant in type position @@ -2269,7 +2262,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if let Some(sp) = tcx.hir().span_if_local(adt_def.did) { - let sp = tcx.sess.source_map().def_span(sp); + let sp = tcx.sess.source_map().guess_head_span(sp); err.span_label(sp, format!("variant `{}` not found here", assoc_ident)); } @@ -2369,8 +2362,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("qpath_to_ty: self.item_def_id()={:?}", def_id); let parent_def_id = def_id - .and_then(|def_id| tcx.hir().as_local_hir_id(def_id)) - .map(|hir_id| tcx.hir().get_parent_did(hir_id)); + .and_then(|def_id| { + def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + }) + .map(|hir_id| tcx.hir().get_parent_did(hir_id).to_def_id()); debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id); @@ -2441,6 +2436,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { continue; } err_for_ct = true; + has_err = true; (ct.span, "const") } }; @@ -2628,11 +2624,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let substs = self.ast_path_substs_for_ty(span, did, item_segment.0); self.normalize_ty(span, tcx.mk_opaque(did, substs)) } - Res::Def(DefKind::Enum, did) - | Res::Def(DefKind::TyAlias, did) - | Res::Def(DefKind::Struct, did) - | Res::Def(DefKind::Union, did) - | Res::Def(DefKind::ForeignTy, did) => { + Res::Def( + DefKind::Enum + | DefKind::TyAlias + | DefKind::Struct + | DefKind::Union + | DefKind::ForeignTy, + did, + ) => { assert_eq!(opt_self_ty, None); self.prohibit_generics(path.segments.split_last().unwrap().1); self.ast_path_to_ty(span, did, path.segments.last().unwrap()) @@ -2659,7 +2658,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert_eq!(opt_self_ty, None); self.prohibit_generics(path.segments); - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let item_id = tcx.hir().get_parent_node(hir_id); let item_def_id = tcx.hir().local_def_id(item_id); let generics = tcx.generics_of(item_def_id); @@ -2752,7 +2751,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::TyKind::Def(item_id, ref lifetimes) => { let did = tcx.hir().local_def_id(item_id.id); - self.impl_trait_ty_to_ty(did, lifetimes) + self.impl_trait_ty_to_ty(did.to_def_id(), lifetimes) } hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { debug!("ast_ty_to_ty: qself={:?} segment={:?}", qself, segment); @@ -2768,7 +2767,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .unwrap_or(tcx.types.err) } hir::TyKind::Array(ref ty, ref length) => { - let length_def_id = tcx.hir().local_def_id(length.hir_id).expect_local(); + let length_def_id = tcx.hir().local_def_id(length.hir_id); let length = ty::Const::from_anon_const(tcx, length_def_id); let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(&ty), length)); self.normalize_ty(ast_ty.span, array_ty) @@ -2883,16 +2882,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let bare_fn_ty = ty::Binder::bind(tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi)); - if !self.allow_ty_infer() { + if let (false, Some(ident_span)) = (self.allow_ty_infer(), ident_span) { // We always collect the spans for placeholder types when evaluating `fn`s, but we // only want to emit an error complaining about them if infer types (`_`) are not - // allowed. `allow_ty_infer` gates this behavior. + // allowed. `allow_ty_infer` gates this behavior. We check for the presence of + // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. crate::collect::placeholder_type_error( tcx, - ident_span.map(|sp| sp.shrink_to_hi()).unwrap_or(DUMMY_SP), + ident_span.shrink_to_hi(), &generics.params[..], visitor.0, - ident_span.is_some(), + true, ); } @@ -2989,7 +2989,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// representations). These lists of bounds occur in many places in /// Rust's syntax: /// -/// ``` +/// ```text /// trait Foo: Bar + Baz { } /// ^^^^^^^^^ supertrait list bounding the `Self` type parameter /// diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 20737b44e7c17..fb139b5033b3b 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -1,9 +1,9 @@ use crate::check::coercion::CoerceMany; use crate::check::{Diverges, Expectation, FnCtxt, Needs}; -use rustc::ty::Ty; use rustc_hir as hir; use rustc_hir::ExprKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty::Ty; use rustc_span::Span; use rustc_trait_selection::traits::ObligationCauseCode; use rustc_trait_selection::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause}; @@ -245,11 +245,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // check that the `if` expr without `else` is the fn body's expr if expr.span == span { - return self.get_fn_decl(hir_id).map(|(fn_decl, _)| { - ( - fn_decl.output.span(), - format!("expected `{}` because of this return type", fn_decl.output), - ) + return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| { + let span = fn_decl.output.span(); + let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?; + Some((span, format!("expected `{}` because of this return type", snippet))) }); } } @@ -332,7 +331,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // | |_____^ expected integer, found `()` // ``` if outer_sp.is_some() { - outer_sp = Some(self.tcx.sess.source_map().def_span(span)); + outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span)); } else_expr.span } @@ -437,6 +436,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(m) = contains_ref_bindings { self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m)) + } else if arms.is_empty() { + self.check_expr(scrut) } else { // ...but otherwise we want to use any supertype of the // scrutinee. This is sort of a workaround, see note (*) in diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 2315b42aec54c..ae6c1738da77d 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -1,14 +1,14 @@ use super::method::MethodCallee; use super::{FnCtxt, Needs, PlaceOp}; -use rustc::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; -use rustc::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; -use rustc::ty::{ToPredicate, TypeFoldable}; -use rustc_ast::ast::Ident; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; +use rustc_middle::ty::{ToPredicate, TypeFoldable}; use rustc_session::DiagnosticMessageId; +use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, TraitEngine}; @@ -114,10 +114,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { let tcx = self.infcx.tcx; - // + // let trait_ref = TraitRef { def_id: tcx.lang_items().deref_trait()?, - substs: tcx.mk_substs_trait(self.cur_ty, &[]), + substs: tcx.mk_substs_trait(ty, &[]), }; let cause = traits::ObligationCause::misc(self.span, self.body_id); diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index ec796043d3ac8..52ddacc1c4b1a 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -3,16 +3,18 @@ use super::method::MethodCallee; use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag}; use crate::type_error_struct; -use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_ast::ast::Ident; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::{infer, traits}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_target::spec::abi; @@ -265,7 +267,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let &ty::Adt(adt_def, ..) = t { if adt_def.is_enum() { if let hir::ExprKind::Call(ref expr, _) = call_expr.kind { - unit_variant = Some(self.tcx.hir().hir_to_pretty_string(expr.hir_id)) + unit_variant = + self.tcx.sess.source_map().span_to_snippet(expr.span).ok(); } } } @@ -335,16 +338,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(call_expr.span, "call expression requires function"); if let Some(span) = self.tcx.hir().res_span(def) { + let callee_ty = callee_ty.to_string(); let label = match (unit_variant, inner_callee_path) { - (Some(path), _) => format!("`{}` defined here", path), - (_, Some(hir::QPath::Resolved(_, path))) => format!( - "`{}` defined here returns `{}`", - path, - callee_ty.to_string() - ), - _ => format!("`{}` defined here", callee_ty.to_string()), + (Some(path), _) => Some(format!("`{}` defined here", path)), + (_, Some(hir::QPath::Resolved(_, path))) => { + self.tcx.sess.source_map().span_to_snippet(path.span).ok().map( + |p| format!("`{}` defined here returns `{}`", p, callee_ty), + ) + } + _ => Some(format!("`{}` defined here", callee_ty)), }; - err.span_label(span, label); + if let Some(label) = label { + err.span_label(span, label); + } } err.emit(); } else { diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 2875d38a996a0..46d6706cbf429 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -32,16 +32,15 @@ use super::FnCtxt; use crate::hir::def_id::DefId; use crate::type_error_struct; -use crate::util::common::ErrorReported; -use rustc::middle::lang_items; -use rustc::ty::adjustment::AllowTwoPhase; -use rustc::ty::cast::{CastKind, CastTy}; -use rustc::ty::error::TypeError; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Ty, TypeAndMut, TypeFoldable}; use rustc_ast::ast; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; +use rustc_hir::lang_items; +use rustc_middle::ty::adjustment::AllowTwoPhase; +use rustc_middle::ty::cast::{CastKind, CastTy}; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; use rustc_session::lint; use rustc_session::Session; use rustc_span::Span; @@ -116,7 +115,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Foreign(..) => Some(PointerKind::Thin), // We should really try to normalize here. ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), ty::Param(ref p) => Some(PointerKind::OfParam(p)), // Insufficient type information. @@ -526,8 +524,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// can return Ok and create type errors in the fcx rather than returning /// directly. coercion-cast is handled in check instead of here. fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { - use rustc::ty::cast::CastTy::*; - use rustc::ty::cast::IntTy::*; + use rustc_middle::ty::cast::CastTy::*; + use rustc_middle::ty::cast::IntTy::*; let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) { @@ -537,7 +535,10 @@ impl<'a, 'tcx> CastCheck<'tcx> { match self.expr_ty.kind { ty::FnDef(..) => { // Attempt a coercion to a fn pointer type. - let f = self.expr_ty.fn_sig(fcx.tcx); + let f = fcx.normalize_associated_types_in( + self.expr.span, + &self.expr_ty.fn_sig(fcx.tcx), + ); let res = fcx.try_coerce( self.expr, self.expr_ty, @@ -562,8 +563,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { ty::Int(_) | ty::Uint(_) | ty::Float(_) - | ty::Infer(ty::InferTy::IntVar(_)) - | ty::Infer(ty::InferTy::FloatVar(_)) => Err(CastError::NeedDeref), + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) => { + Err(CastError::NeedDeref) + } _ => Err(CastError::NeedViaPtr), }, // array-ptr-cast @@ -581,7 +583,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { match (t_from, t_cast) { // These types have invariants! can't cast into them. - (_, Int(CEnum)) | (_, FnPtr) => Err(CastError::NonScalar), + (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), // * -> Bool (_, Int(Bool)) => Err(CastError::CastToBool), @@ -591,16 +593,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { (_, Int(Char)) => Err(CastError::CastToChar), // prim -> float,ptr - (Int(Bool), Float) | (Int(CEnum), Float) | (Int(Char), Float) => { - Err(CastError::NeedViaInt) - } + (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), - (Int(Bool), Ptr(_)) - | (Int(CEnum), Ptr(_)) - | (Int(Char), Ptr(_)) - | (Ptr(_), Float) - | (FnPtr, Float) - | (Float, Ptr(_)) => Err(CastError::IllegalCast), + (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { + Err(CastError::IllegalCast) + } // ptr -> * (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast @@ -613,11 +610,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { // prim -> prim (Int(CEnum), Int(_)) => Ok(CastKind::EnumCast), - (Int(Char), Int(_)) | (Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), + (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), - (Int(_), Int(_)) | (Int(_), Float) | (Float, Int(_)) | (Float, Float) => { - Ok(CastKind::NumericCast) - } + (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), } } diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index a277220b1a200..87a6f119acb09 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -3,15 +3,16 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use crate::astconv::AstConv; -use crate::middle::{lang_items, region}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::InternalSubsts; -use rustc::ty::{self, GenericParamDefKind, Ty}; +use crate::middle::region; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::{FutureTraitLangItem, GeneratorTraitLangItem}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::ArgKind; @@ -70,49 +71,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id); let ClosureSignatures { bound_sig, liberated_sig } = - self.sig_of_closure(expr_def_id, decl, body, expected_sig); + self.sig_of_closure(expr_def_id.to_def_id(), decl, body, expected_sig); debug!("check_closure: ty_of_closure returns {:?}", liberated_sig); let generator_types = check_fn(self, self.param_env, liberated_sig, decl, expr.hir_id, body, gen).1; - let base_substs = - InternalSubsts::identity_for_item(self.tcx, self.tcx.closure_base_def_id(expr_def_id)); + let base_substs = InternalSubsts::identity_for_item( + self.tcx, + self.tcx.closure_base_def_id(expr_def_id.to_def_id()), + ); // HACK(eddyb) this hardcodes indices into substs but it should rely on // `ClosureSubsts` and `GeneratorSubsts` providing constructors, instead. // That would also remove the need for most of the inference variables, // as they immediately unified with the actual type below, including // the `InferCtxt::closure_sig` and `ClosureSubsts::sig_ty` methods. let tupled_upvars_idx = base_substs.len() + if generator_types.is_some() { 4 } else { 2 }; - let substs = base_substs.extend_to(self.tcx, expr_def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => span_bug!(expr.span, "closure has lifetime param"), - GenericParamDefKind::Type { .. } => if param.index as usize == tupled_upvars_idx { - self.tcx.mk_tup(self.tcx.upvars(expr_def_id).iter().flat_map(|upvars| { - upvars.iter().map(|(&var_hir_id, _)| { - // Create type variables (for now) to represent the transformed - // types of upvars. These will be unified during the upvar - // inference phase (`upvar.rs`). - self.infcx.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish upvar inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.hir().span(var_hir_id), + let substs = + base_substs.extend_to(self.tcx, expr_def_id.to_def_id(), |param, _| match param.kind { + GenericParamDefKind::Lifetime => span_bug!(expr.span, "closure has lifetime param"), + GenericParamDefKind::Type { .. } => if param.index as usize == tupled_upvars_idx { + self.tcx.mk_tup(self.tcx.upvars(expr_def_id).iter().flat_map(|upvars| { + upvars.iter().map(|(&var_hir_id, _)| { + // Create type variables (for now) to represent the transformed + // types of upvars. These will be unified during the upvar + // inference phase (`upvar.rs`). + self.infcx.next_ty_var(TypeVariableOrigin { + // FIXME(eddyb) distinguish upvar inference variables from the rest. + kind: TypeVariableOriginKind::ClosureSynthetic, + span: self.tcx.hir().span(var_hir_id), + }) }) + })) + } else { + // Create type variables (for now) to represent the various + // pieces of information kept in `{Closure,Generic}Substs`. + // They will either be unified below, or later during the upvar + // inference phase (`upvar.rs`) + self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr.span, }) - })) - } else { - // Create type variables (for now) to represent the various - // pieces of information kept in `{Closure,Generic}Substs`. - // They will either be unified below, or later during the upvar - // inference phase (`upvar.rs`) - self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr.span, - }) - } - .into(), - GenericParamDefKind::Const => span_bug!(expr.span, "closure has const param"), - }); + } + .into(), + GenericParamDefKind::Const => span_bug!(expr.span, "closure has const param"), + }); if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types { let generator_substs = substs.as_generator(); @@ -125,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // it should rely on `GeneratorSubsts` providing a constructor, instead. let substs = self.resolve_vars_if_possible(&substs); - return self.tcx.mk_generator(expr_def_id, substs, movability); + return self.tcx.mk_generator(expr_def_id.to_def_id(), substs, movability); } // Tuple up the arguments and insert the resulting function type into @@ -156,7 +160,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // it should rely on `ClosureSubsts` providing a constructor, instead. let substs = self.resolve_vars_if_possible(&substs); - let closure_type = self.tcx.mk_closure(expr_def_id, substs); + let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), substs); debug!("check_closure: expr.hir_id={:?} closure_type={:?}", expr.hir_id, closure_type); @@ -173,13 +177,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match expected_ty.kind { ty::Dynamic(ref object_type, ..) => { - let sig = object_type - .projection_bounds() - .filter_map(|pb| { - let pb = pb.with_self_ty(self.tcx, self.tcx.types.err); - self.deduce_sig_from_projection(None, &pb) - }) - .next(); + let sig = object_type.projection_bounds().find_map(|pb| { + let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); + self.deduce_sig_from_projection(None, &pb) + }); let kind = object_type .principal_def_id() .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did)); @@ -244,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_ref = projection.to_poly_trait_ref(tcx); let is_fn = tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some(); - let gen_trait = tcx.require_lang_item(lang_items::GeneratorTraitLangItem, cause_span); + let gen_trait = tcx.require_lang_item(GeneratorTraitLangItem, cause_span); let is_gen = gen_trait == trait_ref.def_id(); if !is_fn && !is_gen { debug!("deduce_sig_from_projection: not fn or generator"); @@ -431,18 +432,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { body: &hir::Body<'_>, expected_sig: ExpectedSig<'tcx>, ) -> ClosureSignatures<'tcx> { - let expr_map_node = self.tcx.hir().get_if_local(expr_def_id).unwrap(); + let hir = self.tcx.hir(); + let expr_map_node = hir.get_if_local(expr_def_id).unwrap(); let expected_args: Vec<_> = expected_sig .sig .inputs() .iter() .map(|ty| ArgKind::from_expected_ty(ty, None)) .collect(); - let (closure_span, found_args) = self.get_fn_like_arguments(expr_map_node); - let expected_span = expected_sig.cause_span.unwrap_or(closure_span); + let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) { + Some((sp, args)) => (Some(sp), args), + None => (None, Vec::new()), + }; + let expected_span = + expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap()); self.report_arg_count_mismatch( expected_span, - Some(closure_span), + closure_span, expected_args, found_args, true, @@ -672,7 +678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that this is a projection from the `Future` trait. let trait_ref = predicate.projection_ty.trait_ref(self.tcx); - let future_trait = self.tcx.lang_items().future_trait().unwrap(); + let future_trait = self.tcx.require_lang_item(FutureTraitLangItem, Some(cause_span)); if trait_ref.def_id != future_trait { debug!("deduce_future_output_from_projection: not a future"); return None; diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 33fc18b4b6e68..54562cf38addd 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -52,18 +52,18 @@ use crate::astconv::AstConv; use crate::check::{FnCtxt, Needs}; -use rustc::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, -}; -use rustc::ty::error::TypeError; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::relate::RelateResult; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Ty, TypeAndMut}; use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, +}; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::relate::RelateResult; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, Span}; @@ -101,13 +101,13 @@ fn coerce_mutbls<'tcx>( to_mutbl: hir::Mutability, ) -> RelateResult<'tcx, ()> { match (from_mutbl, to_mutbl) { - (hir::Mutability::Mut, hir::Mutability::Mut) - | (hir::Mutability::Not, hir::Mutability::Not) - | (hir::Mutability::Mut, hir::Mutability::Not) => Ok(()), + (hir::Mutability::Mut, hir::Mutability::Mut | hir::Mutability::Not) + | (hir::Mutability::Not, hir::Mutability::Not) => Ok(()), (hir::Mutability::Not, hir::Mutability::Mut) => Err(TypeError::Mutability), } } +/// Do not require any adjustments, i.e. coerce `x -> x`. fn identity(_: Ty<'_>) -> Vec> { vec![] } @@ -116,6 +116,7 @@ fn simple(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec> move |target| vec![Adjustment { kind, target }] } +/// This always returns `Ok(...)`. fn success<'tcx>( adj: Vec>, target: Ty<'tcx>, @@ -134,6 +135,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } fn unify(&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(|_| { if self.use_lub { self.at(&self.cause, self.fcx.param_env).lub(b, a) @@ -212,12 +214,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::RawPtr(mt_b) => { return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); } - - ty::Ref(r_b, ty, mutbl) => { - let mt_b = ty::TypeAndMut { ty, mutbl }; - return self.coerce_borrowed_pointer(a, b, r_b, mt_b); + ty::Ref(r_b, _, mutbl_b) => { + return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); } - _ => {} } @@ -256,7 +255,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { a: Ty<'tcx>, b: Ty<'tcx>, r_b: ty::Region<'tcx>, - mt_b: TypeAndMut<'tcx>, + mutbl_b: hir::Mutability, ) -> CoerceResult<'tcx> { debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); @@ -269,7 +268,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let (r_a, mt_a) = match a.kind { ty::Ref(r_a, ty, mutbl) => { let mt_a = ty::TypeAndMut { ty, mutbl }; - coerce_mutbls(mt_a.mutbl, mt_b.mutbl)?; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; (r_a, mt_a) } _ => return self.unify_and(a, b, identity), @@ -365,7 +364,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { r_a // [3] above } else { if r_borrow_var.is_none() { - // create var lazilly, at most once + // create var lazily, at most once let coercion = Coercion(span); let r = self.next_region_var(coercion); r_borrow_var = Some(r); // [4] above @@ -376,7 +375,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { r, TypeAndMut { ty: referent_ty, - mutbl: mt_b.mutbl, // [1] above + mutbl: mutbl_b, // [1] above }, ); match self.unify(derefd_ty_a, b) { @@ -418,11 +417,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // `self.x` both have `&mut `type would be a move of // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, // which is a borrow. - assert_eq!(mt_b.mutbl, hir::Mutability::Not); // can only coerce &T -> &U + assert_eq!(mutbl_b, hir::Mutability::Not); // can only coerce &T -> &U return success(vec![], ty, obligations); } - let needs = Needs::maybe_mut_place(mt_b.mutbl); + let needs = Needs::maybe_mut_place(mutbl_b); let InferOk { value: mut adjustments, obligations: o } = autoderef.adjust_steps_as_infer_ok(self, needs); obligations.extend(o); @@ -434,7 +433,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::Ref(r_borrow, _, _) => r_borrow, _ => span_bug!(span, "expected a ref type, got {:?}", ty), }; - let mutbl = match mt_b.mutbl { + let mutbl = match mutbl_b { hir::Mutability::Not => AutoBorrowMutability::Not, hir::Mutability::Mut => { AutoBorrowMutability::Mut { allow_two_phase_borrow: self.allow_two_phase } @@ -453,9 +452,43 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // &[T; n] or &mut [T; n] -> &[T] // or &mut [T; n] -> &mut [T] // or &Concrete -> &Trait, etc. - fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tcx> { + fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> { debug!("coerce_unsized(source={:?}, target={:?})", source, target); + source = self.shallow_resolve(source); + target = self.shallow_resolve(target); + debug!("coerce_unsized: resolved source={:?} target={:?}", source, target); + + // These 'if' statements require some explanation. + // The `CoerceUnsized` trait is special - it is only + // possible to write `impl CoerceUnsized for A` where + // A and B have 'matching' fields. This rules out the following + // two types of blanket impls: + // + // `impl CoerceUnsized for SomeType` + // `impl CoerceUnsized for T` + // + // Both of these trigger a special `CoerceUnsized`-related error (E0376) + // + // We can take advantage of this fact to avoid performing unecessary work. + // If either `source` or `target` is a type variable, then any applicable impl + // would need to be generic over the self-type (`impl CoerceUnsized for T`) + // or generic over the `CoerceUnsized` type parameter (`impl CoerceUnsized for + // SomeType`). + // + // However, these are exactly the kinds of impls which are forbidden by + // the compiler! Therefore, we can be sure that coercion will always fail + // when either the source or target type is a type variable. This allows us + // to skip performing any trait selection, and immediately bail out. + if source.is_ty_var() { + debug!("coerce_unsized: source is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + if target.is_ty_var() { + debug!("coerce_unsized: target is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + let traits = (self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait()); let (unsize_did, coerce_unsized_did) = if let (Some(u), Some(cu)) = traits { @@ -563,30 +596,30 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { while !queue.is_empty() { let obligation = queue.remove(0); debug!("coerce_unsized resolve step: {:?}", obligation); - let trait_ref = match obligation.predicate { - ty::Predicate::Trait(ref tr, _) if traits.contains(&tr.def_id()) => { - if unsize_did == tr.def_id() { - let sty = &tr.skip_binder().input_types().nth(1).unwrap().kind; - if let ty::Tuple(..) = sty { + let trait_pred = match obligation.predicate { + ty::Predicate::Trait(trait_pred, _) if traits.contains(&trait_pred.def_id()) => { + if unsize_did == trait_pred.def_id() { + let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty(); + if let ty::Tuple(..) = unsize_ty.kind { debug!("coerce_unsized: found unsized tuple coercion"); has_unsized_tuple_coercion = true; } } - *tr + trait_pred } _ => { coercion.obligations.push(obligation); continue; } }; - match selcx.select(&obligation.with(trait_ref)) { + match selcx.select(&obligation.with(trait_pred)) { // Uncertain or unimplemented. Ok(None) => { - if trait_ref.def_id() == unsize_did { - let trait_ref = self.resolve_vars_if_possible(&trait_ref); - let self_ty = trait_ref.skip_binder().self_ty(); - let unsize_ty = trait_ref.skip_binder().input_types().nth(1).unwrap(); - debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_ref); + if trait_pred.def_id() == unsize_did { + let trait_pred = self.resolve_vars_if_possible(&trait_pred); + let self_ty = trait_pred.skip_binder().self_ty(); + let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty(); + debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); match (&self_ty.kind, &unsize_ty.kind) { (ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) if self.type_var_is_sized(*v) => @@ -692,12 +725,22 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); match b.kind { - ty::FnPtr(_) => { + ty::FnPtr(b_sig) => { let a_sig = a.fn_sig(self.tcx); // Intrinsics are not coercible to function pointers if a_sig.abi() == Abi::RustIntrinsic || a_sig.abi() == Abi::PlatformIntrinsic { return Err(TypeError::IntrinsicCast); } + + // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). + if let ty::FnDef(def_id, _) = a.kind { + if b_sig.unsafety() == hir::Unsafety::Normal + && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + return Err(TypeError::TargetFeatureCast(def_id)); + } + } + let InferOk { value: a_sig, mut obligations } = self.normalize_associated_types_in_as_infer_ok(self.cause.span, &a_sig); @@ -750,7 +793,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // `unsafe fn(arg0,arg1,...) -> _` let closure_sig = substs_a.as_closure().sig(); let unsafety = fn_ty.unsafety(); - let pointer_ty = self.tcx.coerce_closure_fn_ty(closure_sig, unsafety); + let pointer_ty = + self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety)); debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); self.unify_and( pointer_ty, @@ -775,10 +819,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::RawPtr(mt) => (false, mt), _ => return self.unify_and(a, b, identity), }; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; // Check that the types which they point at are compatible. let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty }); - coerce_mutbls(mt_a.mutbl, mutbl_b)?; // Although references and unsafe ptrs have the same // representation, we still register an Adjust::DerefRef so that // regionck knows that the region for `a` must be valid here. @@ -832,6 +876,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.probe(|_| coerce.coerce(source, target)).is_ok() } + /// Given a type and a target type, this function will calculate and return + /// how many dereference steps needed to achieve `expr_ty <: target`. If + /// it's not possible, return `None`. + pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option { + let cause = self.cause(rustc_span::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); + coerce + .autoderef(rustc_span::DUMMY_SP, expr_ty) + .find_map(|(ty, steps)| coerce.unify(ty, target).ok().map(|_| steps)) + } + /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). @@ -854,23 +910,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty); // Special-case that coercion alone cannot handle: - // Two function item types of differing IDs or InternalSubsts. - if let (&ty::FnDef(..), &ty::FnDef(..)) = (&prev_ty.kind, &new_ty.kind) { - // Don't reify if the function types have a LUB, i.e., they - // are the same function and their parameters have a LUB. - let lub_ty = self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - .map(|ok| self.register_infer_ok_obligations(ok)); - - if lub_ty.is_ok() { - // We have a LUB of prev_ty and new_ty, just return it. - return lub_ty; + // Function items or non-capturing closures of differing IDs or InternalSubsts. + let (a_sig, b_sig) = { + let is_capturing_closure = |ty| { + if let &ty::Closure(_, substs) = ty { + substs.as_closure().upvar_tys().next().is_some() + } else { + false + } + }; + if is_capturing_closure(&prev_ty.kind) || is_capturing_closure(&new_ty.kind) { + (None, None) + } else { + match (&prev_ty.kind, &new_ty.kind) { + (&ty::FnDef(..), &ty::FnDef(..)) => { + // Don't reify if the function types have a LUB, i.e., they + // are the same function and their parameters have a LUB. + match self + .commit_if_ok(|_| 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(_) => { + (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx))) + } + } + } + (&ty::Closure(_, substs), &ty::FnDef(..)) => { + let b_sig = new_ty.fn_sig(self.tcx); + let a_sig = self + .tcx + .signature_unclosure(substs.as_closure().sig(), b_sig.unsafety()); + (Some(a_sig), Some(b_sig)) + } + (&ty::FnDef(..), &ty::Closure(_, substs)) => { + let a_sig = prev_ty.fn_sig(self.tcx); + let b_sig = self + .tcx + .signature_unclosure(substs.as_closure().sig(), a_sig.unsafety()); + (Some(a_sig), Some(b_sig)) + } + (&ty::Closure(_, substs_a), &ty::Closure(_, substs_b)) => ( + Some(self.tcx.signature_unclosure( + substs_a.as_closure().sig(), + hir::Unsafety::Normal, + )), + Some(self.tcx.signature_unclosure( + substs_b.as_closure().sig(), + hir::Unsafety::Normal, + )), + ), + _ => (None, None), + } } - + }; + if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { // The signature must match. - let a_sig = prev_ty.fn_sig(self.tcx); let a_sig = self.normalize_associated_types_in(new.span, &a_sig); - let b_sig = new_ty.fn_sig(self.tcx); let b_sig = self.normalize_associated_types_in(new.span, &b_sig); let sig = self .at(cause, self.param_env) @@ -880,17 +976,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Reify both sides and return the reified fn pointer type. let fn_ptr = self.tcx.mk_fn_ptr(sig); - for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) { - // The only adjustment that can produce an fn item is - // `NeverToAny`, so this should always be valid. + let prev_adjustment = match prev_ty.kind { + ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())), + ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), + _ => unreachable!(), + }; + let next_adjustment = match new_ty.kind { + ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())), + ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), + _ => unreachable!(), + }; + for expr in exprs.iter().map(|e| e.as_coercion_site()) { self.apply_adjustments( expr, - vec![Adjustment { - kind: Adjust::Pointer(PointerCast::ReifyFnPointer), - target: fn_ptr, - }], + vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }], ); } + self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]); return Ok(fn_ptr); } @@ -1394,7 +1496,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let ty = AstConv::ast_ty_to_ty(fcx, ty); // Get the `impl Trait`'s `DefId`. if let ty::Opaque(def_id, _) = ty.kind { - let hir_id = fcx.tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = fcx.tcx.hir().as_local_hir_id(def_id.expect_local()); // Get the `impl Trait`'s `Item` so that we can get its trait bounds and // get the `Trait`'s `DefId`. if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) = @@ -1402,9 +1504,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { { // Are of this `impl Trait`'s traits object safe? is_object_safe = bounds.iter().all(|bound| { - bound.trait_def_id().map_or(false, |def_id| { - fcx.tcx.object_safety_violations(def_id).is_empty() - }) + bound + .trait_ref() + .and_then(|t| t.trait_def_id()) + .map_or(false, |def_id| { + fcx.tcx.object_safety_violations(def_id).is_empty() + }) }) } } diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index f666ef72d5278..29cd9681295be 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -1,14 +1,13 @@ -use rustc::ty::error::{ExpectedFound, TypeError}; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::util::ExplicitSelf; -use rustc::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc::util::common::ErrorReported; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::util::ExplicitSelf; +use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; @@ -35,7 +34,7 @@ crate fn compare_impl_method<'tcx>( ) { debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); - let impl_m_span = tcx.sess.source_map().def_span(impl_m_span); + let impl_m_span = tcx.sess.source_map().guess_head_span(impl_m_span); if let Err(ErrorReported) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) { @@ -77,7 +76,7 @@ fn compare_predicate_entailment<'tcx>( // This node-id should be used for the `body_id` field on each // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. - let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id).unwrap(); + let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id.expect_local()); let cause = ObligationCause { span: impl_m_span, @@ -363,7 +362,7 @@ fn check_region_bounds_on_impl_item<'tcx>( // the moment, give a kind of vague error message. if trait_params != impl_params { let item_kind = assoc_item_kind_str(impl_m); - let def_span = tcx.sess.source_map().def_span(span); + let def_span = tcx.sess.source_map().guess_head_span(span); let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span); let mut err = struct_span_err!( tcx.sess, @@ -375,7 +374,7 @@ fn check_region_bounds_on_impl_item<'tcx>( ); err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind)); if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { - let def_sp = tcx.sess.source_map().def_span(sp); + let def_sp = tcx.sess.source_map().guess_head_span(sp); let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp); err.span_label( sp, @@ -400,7 +399,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( trait_sig: ty::FnSig<'tcx>, ) -> (Span, Option) { let tcx = infcx.tcx; - let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id).unwrap(); + let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id.expect_local()); let (impl_m_output, impl_m_iter) = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { ImplItemKind::Fn(ref impl_m_sig, _) => { (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) @@ -410,7 +409,8 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( match *terr { TypeError::Mutability => { - if let Some(trait_m_hir_id) = tcx.hir().as_local_hir_id(trait_m.def_id) { + if let Some(def_id) = trait_m.def_id.as_local() { + let trait_m_hir_id = tcx.hir().as_local_hir_id(def_id); let trait_m_iter = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { TraitItemKind::Fn(ref trait_m_sig, _) => trait_m_sig.decl.inputs.iter(), _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), @@ -437,7 +437,8 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( } } TypeError::Sorts(ExpectedFound { .. }) => { - if let Some(trait_m_hir_id) = tcx.hir().as_local_hir_id(trait_m.def_id) { + if let Some(def_id) = trait_m.def_id.as_local() { + let trait_m_hir_id = tcx.hir().as_local_hir_id(def_id); let (trait_m_output, trait_m_iter) = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { TraitItemKind::Fn(ref trait_m_sig, _) => { @@ -452,16 +453,13 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( .zip(trait_iter) .zip(impl_m_iter) .zip(trait_m_iter) - .filter_map( - |(((&impl_arg_ty, &trait_arg_ty), impl_arg), trait_arg)| match infcx - .at(&cause, param_env) - .sub(trait_arg_ty, impl_arg_ty) - { - Ok(_) => None, - Err(_) => Some((impl_arg.span, Some(trait_arg.span))), - }, - ) - .next() + .find_map(|(((&impl_arg_ty, &trait_arg_ty), impl_arg), trait_arg)| match infcx + .at(&cause, param_env) + .sub(trait_arg_ty, impl_arg_ty) + { + Ok(_) => None, + Err(_) => Some((impl_arg.span, Some(trait_arg.span))), + }) .unwrap_or_else(|| { if infcx .at(&cause, param_env) @@ -517,7 +515,7 @@ fn compare_self_type<'tcx>( }) }; - match (trait_m.method_has_self_argument, impl_m.method_has_self_argument) { + match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) { (false, false) | (true, true) => {} (false, true) => { @@ -588,33 +586,33 @@ fn compare_number_of_generics<'tcx>( if impl_count != trait_count { err_occurred = true; - let (trait_spans, impl_trait_spans) = - if let Some(trait_hir_id) = tcx.hir().as_local_hir_id(trait_.def_id) { - let trait_item = tcx.hir().expect_trait_item(trait_hir_id); - if trait_item.generics.params.is_empty() { - (Some(vec![trait_item.generics.span]), vec![]) - } else { - let arg_spans: Vec = - trait_item.generics.params.iter().map(|p| p.span).collect(); - let impl_trait_spans: Vec = trait_item - .generics - .params - .iter() - .filter_map(|p| match p.kind { - GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => Some(p.span), - _ => None, - }) - .collect(); - (Some(arg_spans), impl_trait_spans) - } + let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() { + let trait_hir_id = tcx.hir().as_local_hir_id(def_id); + let trait_item = tcx.hir().expect_trait_item(trait_hir_id); + if trait_item.generics.params.is_empty() { + (Some(vec![trait_item.generics.span]), vec![]) } else { - (trait_span.map(|s| vec![s]), vec![]) - }; + let arg_spans: Vec = + trait_item.generics.params.iter().map(|p| p.span).collect(); + let impl_trait_spans: Vec = trait_item + .generics + .params + .iter() + .filter_map(|p| match p.kind { + GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } => Some(p.span), + _ => None, + }) + .collect(); + (Some(arg_spans), impl_trait_spans) + } + } else { + (trait_span.map(|s| vec![s]), vec![]) + }; - let impl_hir_id = tcx.hir().as_local_hir_id(impl_.def_id).unwrap(); + let impl_hir_id = tcx.hir().as_local_hir_id(impl_.def_id.expect_local()); let impl_item = tcx.hir().expect_impl_item(impl_hir_id); let impl_item_impl_trait_spans: Vec = impl_item .generics @@ -705,8 +703,8 @@ fn compare_number_of_method_arguments<'tcx>( let trait_number_args = trait_m_fty.inputs().skip_binder().len(); let impl_number_args = impl_m_fty.inputs().skip_binder().len(); if trait_number_args != impl_number_args { - let trait_m_hir_id = tcx.hir().as_local_hir_id(trait_m.def_id); - let trait_span = if let Some(trait_id) = trait_m_hir_id { + let trait_span = if let Some(def_id) = trait_m.def_id.as_local() { + let trait_id = tcx.hir().as_local_hir_id(def_id); match tcx.hir().expect_trait_item(trait_id).kind { TraitItemKind::Fn(ref trait_m_sig, _) => { let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 }; @@ -729,7 +727,7 @@ fn compare_number_of_method_arguments<'tcx>( } else { trait_item_span }; - let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id).unwrap(); + let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id.expect_local()); let impl_span = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { ImplItemKind::Fn(ref impl_m_sig, _) => { let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 }; @@ -811,7 +809,7 @@ fn compare_synthetic_generics<'tcx>( impl_m_type_params.zip(trait_m_type_params) { if impl_synthetic != trait_synthetic { - let impl_hir_id = tcx.hir().as_local_hir_id(impl_def_id).unwrap(); + let impl_hir_id = tcx.hir().as_local_hir_id(impl_def_id.expect_local()); let impl_span = tcx.hir().span(impl_hir_id); let trait_span = tcx.def_span(trait_def_id); let mut err = struct_span_err!( @@ -832,10 +830,10 @@ fn compare_synthetic_generics<'tcx>( // FIXME: this is obviously suboptimal since the name can already be used // as another generic argument let new_name = tcx.sess.source_map().span_to_snippet(trait_span).ok()?; - let trait_m = tcx.hir().as_local_hir_id(trait_m.def_id)?; + let trait_m = tcx.hir().as_local_hir_id(trait_m.def_id.as_local()?); let trait_m = tcx.hir().trait_item(hir::TraitItemId { hir_id: trait_m }); - let impl_m = tcx.hir().as_local_hir_id(impl_m.def_id)?; + let impl_m = tcx.hir().as_local_hir_id(impl_m.def_id.as_local()?); let impl_m = tcx.hir().impl_item(hir::ImplItemId { hir_id: impl_m }); // in case there are no generics, take the spot between the function name @@ -869,7 +867,7 @@ fn compare_synthetic_generics<'tcx>( (None, Some(hir::SyntheticTyParamKind::ImplTrait)) => { err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); (|| { - let impl_m = tcx.hir().as_local_hir_id(impl_m.def_id)?; + let impl_m = tcx.hir().as_local_hir_id(impl_m.def_id.as_local()?); let impl_m = tcx.hir().impl_item(hir::ImplItemId { hir_id: impl_m }); let input_tys = match impl_m.kind { hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, @@ -962,12 +960,13 @@ crate fn compare_const_impl<'tcx>( // Create a parameter environment that represents the implementation's // method. - let impl_c_hir_id = tcx.hir().as_local_hir_id(impl_c.def_id).unwrap(); + let impl_c_hir_id = tcx.hir().as_local_hir_id(impl_c.def_id.expect_local()); // Compute placeholder form of impl and trait const tys. let impl_ty = tcx.type_of(impl_c.def_id); let trait_ty = tcx.type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs); let mut cause = ObligationCause::misc(impl_c_span, impl_c_hir_id); + cause.code = ObligationCauseCode::CompareImplConstObligation; // There is no "body" here, so just pass dummy id. let impl_ty = @@ -1006,7 +1005,8 @@ crate fn compare_const_impl<'tcx>( trait_c.ident ); - let trait_c_hir_id = tcx.hir().as_local_hir_id(trait_c.def_id); + let trait_c_hir_id = + trait_c.def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)); let trait_c_span = trait_c_hir_id.map(|trait_c_hir_id| { // Add a label to the Span containing just the type of the const match tcx.hir().expect_trait_item(trait_c_hir_id).kind { @@ -1094,7 +1094,7 @@ fn compare_type_predicate_entailment( // This `HirId` should be used for the `body_id` field on each // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. - let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id).unwrap(); + let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local()); let cause = ObligationCause { span: impl_ty_span, body_id: impl_ty_hir_id, @@ -1164,7 +1164,7 @@ fn compare_type_predicate_entailment( fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str { match impl_item.kind { ty::AssocKind::Const => "const", - ty::AssocKind::Method => "method", + ty::AssocKind::Fn => "method", ty::AssocKind::Type | ty::AssocKind::OpaqueTy => "type", } } diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 0556c80e4f707..9694ce9450c27 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -4,12 +4,13 @@ use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause}; -use rustc::ty::adjustment::AllowTwoPhase; -use rustc::ty::{self, AssocItem, Ty}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::{is_range_literal, print, Node}; +use rustc_hir::lang_items::{CloneTraitLangItem, DerefTraitLangItem}; +use rustc_hir::{is_range_literal, Node}; +use rustc_middle::ty::adjustment::AllowTwoPhase; +use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -25,7 +26,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { self.annotate_expected_due_to_let_ty(err, expr); self.suggest_compatible_variants(err, expr, expected, expr_ty); - self.suggest_ref_or_into(err, expr, expected, expr_ty); + self.suggest_deref_ref_or_into(err, expr, expected, expr_ty); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } @@ -36,7 +37,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Requires that the two types unify, and prints an error message if // they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - self.demand_suptype_diag(sp, expected, actual).map(|mut e| e.emit()); + if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) { + e.emit(); + } } pub fn demand_suptype_diag( @@ -198,13 +201,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .peekable(); if compatible_variants.peek().is_some() { - let expr_text = - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_else(|_| { - print::to_string(print::NO_ANN, |s| s.print_expr(expr)) - }); - let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text)); - let msg = "try using a variant of the expected enum"; - err.span_suggestions(expr.span, msg, suggestions, Applicability::MaybeIncorrect); + if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) { + let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text)); + let msg = "try using a variant of the expected enum"; + err.span_suggestions( + expr.span, + msg, + suggestions, + Applicability::MaybeIncorrect, + ); + } } } } @@ -214,16 +220,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, expected: Ty<'tcx>, checked_ty: Ty<'tcx>, + hir_id: hir::HirId, ) -> Vec { - let mut methods = self.probe_for_return_type( - span, - probe::Mode::MethodCall, - expected, - checked_ty, - hir::DUMMY_HIR_ID, - ); + let mut methods = + self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id); methods.retain(|m| { - self.has_no_input_arg(m) + self.has_only_self_parameter(m) && self .tcx .get_attrs(m.def_id) @@ -244,11 +246,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { methods } - // This function checks if the method isn't static and takes other arguments than `self`. - fn has_no_input_arg(&self, method: &AssocItem) -> bool { + /// 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::Method => { - self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1 + ty::AssocKind::Fn => { + method.fn_has_self_parameter + && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1 } _ => false, } @@ -351,6 +354,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + fn replace_prefix(&self, s: A, old: B, new: C) -> Option + where + A: AsRef, + B: AsRef, + C: AsRef, + { + let s = s.as_ref(); + let old = old.as_ref(); + if s.starts_with(old) { Some(new.as_ref().to_owned() + &s[old.len()..]) } else { None } + } + /// This function is used to determine potential "simple" improvements or users' errors and /// provide them useful help. For example: /// @@ -372,7 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - ) -> Option<(Span, &'static str, String)> { + ) -> Option<(Span, &'static str, String, Applicability)> { let sm = self.sess().source_map(); let sp = expr.span; if sm.is_imported(sp) { @@ -393,31 +407,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (&expr.kind, &expected.kind, &checked_ty.kind) { (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) { - (&ty::Str, &ty::Array(arr, _)) | (&ty::Str, &ty::Slice(arr)) - if arr == self.tcx.types.u8 => - { + (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if src.starts_with("b\"") { + if let Some(src) = self.replace_prefix(src, "b\"", "\"") { return Some(( sp, "consider removing the leading `b`", - src[1..].to_string(), + src, + Applicability::MachineApplicable, )); } } } } - (&ty::Array(arr, _), &ty::Str) | (&ty::Slice(arr), &ty::Str) - if arr == self.tcx.types.u8 => - { + (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if src.starts_with('"') { + if let Some(src) = self.replace_prefix(src, "\"", "b\"") { return Some(( sp, "consider adding a leading `b`", - format!("b{}", src), + src, + Applicability::MachineApplicable, )); } } @@ -444,8 +456,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self.can_coerce(ref_ty, expected) { let mut sugg_sp = sp; - if let hir::ExprKind::MethodCall(segment, _sp, args) = &expr.kind { - let clone_trait = self.tcx.lang_items().clone_trait().unwrap(); + if let hir::ExprKind::MethodCall(ref segment, sp, ref args) = expr.kind { + let clone_trait = self.tcx.require_lang_item(CloneTraitLangItem, Some(sp)); if let ([arg], Some(true), sym::clone) = ( &args[..], self.tables.borrow().type_dependent_def_id(expr.hir_id).map(|did| { @@ -470,7 +482,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sugg_expr = if needs_parens { format!("({})", src) } else { src }; if let Some(sugg) = self.can_use_as_ref(expr) { - return Some(sugg); + return Some(( + sugg.0, + sugg.1, + sugg.2, + Applicability::MachineApplicable, + )); } let field_name = if is_struct_pat_shorthand_field { format!("{}: ", sugg_expr) @@ -495,6 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "consider dereferencing here to assign to the mutable \ borrowed piece of memory", format!("*{}", src), + Applicability::MachineApplicable, )); } } @@ -505,11 +523,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp, "consider mutably borrowing here", format!("{}&mut {}", field_name, sugg_expr), + Applicability::MachineApplicable, ), hir::Mutability::Not => ( sp, "consider borrowing here", format!("{}&{}", field_name, sugg_expr), + Applicability::MachineApplicable, ), }); } @@ -526,25 +546,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We have `&T`, check if what was expected was `T`. If so, // we may want to suggest removing a `&`. if sm.is_imported(expr.span) { - if let Ok(code) = sm.span_to_snippet(sp) { - if code.starts_with('&') { + if let Ok(src) = sm.span_to_snippet(sp) { + if let Some(src) = self.replace_prefix(src, "&", "") { return Some(( sp, "consider removing the borrow", - code[1..].to_string(), + src, + Applicability::MachineApplicable, )); } } return None; } if let Ok(code) = sm.span_to_snippet(expr.span) { - return Some((sp, "consider removing the borrow", code)); + return Some(( + sp, + "consider removing the borrow", + code, + Applicability::MachineApplicable, + )); + } + } + ( + _, + &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }), + &ty::Ref(_, ty_a, mutbl_a), + ) => { + if let Some(steps) = self.deref_steps(ty_a, ty_b) { + // Only suggest valid if dereferencing needed. + if steps > 0 { + // The pointer type implements `Copy` trait so the suggestion is always valid. + if let Ok(src) = sm.span_to_snippet(sp) { + let derefs = &"*".repeat(steps); + if let Some((src, applicability)) = match mutbl_b { + hir::Mutability::Mut => { + let new_prefix = "&mut ".to_owned() + derefs; + match mutbl_a { + hir::Mutability::Mut => { + if let Some(s) = + self.replace_prefix(src, "&mut ", new_prefix) + { + Some((s, Applicability::MachineApplicable)) + } else { + None + } + } + hir::Mutability::Not => { + if let Some(s) = + self.replace_prefix(src, "&", new_prefix) + { + Some((s, Applicability::Unspecified)) + } else { + None + } + } + } + } + hir::Mutability::Not => { + let new_prefix = "&".to_owned() + derefs; + match mutbl_a { + hir::Mutability::Mut => { + if let Some(s) = + self.replace_prefix(src, "&mut ", new_prefix) + { + Some((s, Applicability::MachineApplicable)) + } else { + None + } + } + hir::Mutability::Not => { + if let Some(s) = + self.replace_prefix(src, "&", new_prefix) + { + Some((s, Applicability::MachineApplicable)) + } else { + None + } + } + } + } + } { + return Some((sp, "consider dereferencing", src, applicability)); + } + } + } } } _ if sp == expr.span && !is_macro => { // Check for `Deref` implementations by constructing a predicate to // prove: `::Output == U` - let deref_trait = self.tcx.lang_items().deref_trait().unwrap(); + let deref_trait = self.tcx.require_lang_item(DerefTraitLangItem, Some(sp)); let item_def_id = self .tcx .associated_items(deref_trait) @@ -582,7 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { format!("*{}", code) }; - return Some((sp, message, suggestion)); + return Some((sp, message, suggestion, Applicability::MachineApplicable)); } } } @@ -618,24 +709,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // For now, don't suggest casting with `as`. let can_cast = false; - let mut prefix = String::new(); - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Struct(_, fields, _), .. + let prefix = if let Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Struct(_, fields, _), + .. })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { // `expr` is a literal field for a struct, only suggest if appropriate - for field in *fields { - if field.expr.hir_id == expr.hir_id && field.is_shorthand { - // This is a field literal - prefix = format!("{}: ", field.ident); - break; - } - } - if &prefix == "" { + match (*fields) + .iter() + .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand) + { + // This is a field literal + Some(field) => format!("{}: ", field.ident), // Likely a field was meant, but this field wasn't found. Do not suggest anything. - return false; + None => return false, } - } + } else { + String::new() + }; if let hir::ExprKind::Call(path, args) = &expr.kind { if let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) = (&path.kind, args.len()) @@ -705,7 +796,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suffix_suggestion = format!( "{}{}{}{}", if needs_paren { "(" } else { "" }, - if let (ty::Int(_), ty::Float(_)) | (ty::Uint(_), ty::Float(_)) = + if let (ty::Int(_) | ty::Uint(_), ty::Float(_)) = (&expected_ty.kind, &checked_ty.kind,) { // Remove fractional part from literal, for example `42.0f32` into `42` @@ -727,7 +818,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suggest_to_change_suffix_or_into = |err: &mut DiagnosticBuilder<'_>, is_fallible: bool| { - let into_sugg = into_suggestion.clone(); err.span_suggestion( expr.span, if literal_is_ty_suffixed(expr) { @@ -742,7 +832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if is_fallible { try_into_suggestion } else { - into_sugg + into_suggestion.clone() }, Applicability::MachineApplicable, ); @@ -750,8 +840,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (&expected_ty.kind, &checked_ty.kind) { (&ty::Int(ref exp), &ty::Int(ref found)) => { - let is_fallible = match (found.bit_width(), exp.bit_width()) { - (Some(found), Some(exp)) if found > exp => true, + let is_fallible = match (exp.bit_width(), found.bit_width()) { + (Some(exp), Some(found)) if exp < found => true, + (None, Some(8 | 16)) => false, (None, _) | (_, None) => true, _ => false, }; @@ -759,15 +850,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } (&ty::Uint(ref exp), &ty::Uint(ref found)) => { - let is_fallible = match (found.bit_width(), exp.bit_width()) { - (Some(found), Some(exp)) if found > exp => true, + let is_fallible = match (exp.bit_width(), found.bit_width()) { + (Some(exp), Some(found)) if exp < found => true, + (None, Some(8 | 16)) => false, (None, _) | (_, None) => true, _ => false, }; suggest_to_change_suffix_or_into(err, is_fallible); true } - (&ty::Int(_), &ty::Uint(_)) | (&ty::Uint(_), &ty::Int(_)) => { + (&ty::Int(exp), &ty::Uint(found)) => { + let is_fallible = match (exp.bit_width(), found.bit_width()) { + (Some(exp), Some(found)) if found < exp => false, + (None, Some(8)) => false, + _ => true, + }; + suggest_to_change_suffix_or_into(err, is_fallible); + true + } + (&ty::Uint(_), &ty::Int(_)) => { suggest_to_change_suffix_or_into(err, true); true } @@ -792,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } true } - (&ty::Uint(_), &ty::Float(_)) | (&ty::Int(_), &ty::Float(_)) => { + (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { if literal_is_ty_suffixed(expr) { err.span_suggestion( expr.span, @@ -808,13 +909,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion ); - err.warn( - "if the rounded value cannot be represented by the target \ - integer type, including `Inf` and `NaN`, casting will cause \ - undefined behavior \ - (see issue #10184 \ - for more information)", - ); } true } diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index e48ebbbb23514..478a848cf09dd 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -1,16 +1,15 @@ use crate::check::regionck::RegionCtxt; use crate::hir; -use crate::hir::def_id::DefId; -use crate::util::common::ErrorReported; -use rustc::middle::region; -use rustc::ty::error::TypeError; -use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc::ty::subst::{Subst, SubstsRef}; -use rustc::ty::{self, Predicate, Ty, TyCtxt}; -use rustc_errors::struct_span_err; +use crate::hir::def_id::{DefId, LocalDefId}; +use rustc_errors::{struct_span_err, ErrorReported}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferOk, RegionckMode, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::middle::region; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, Predicate, Ty, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::query::dropck_outlives::AtExt; @@ -40,7 +39,7 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro ty::Adt(adt_def, self_to_impl_substs) => { ensure_drop_params_and_item_params_correspond( tcx, - drop_impl_did, + drop_impl_did.expect_local(), dtor_self_type, adt_def.did, )?; @@ -48,7 +47,7 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro ensure_drop_predicates_are_implied_by_item_defn( tcx, dtor_predicates, - adt_def.did, + adt_def.did.expect_local(), self_to_impl_substs, ) } @@ -68,11 +67,11 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro fn ensure_drop_params_and_item_params_correspond<'tcx>( tcx: TyCtxt<'tcx>, - drop_impl_did: DefId, + drop_impl_did: LocalDefId, drop_impl_ty: Ty<'tcx>, self_type_did: DefId, ) -> Result<(), ErrorReported> { - let drop_impl_hir_id = tcx.hir().as_local_hir_id(drop_impl_did).unwrap(); + let drop_impl_hir_id = tcx.hir().as_local_hir_id(drop_impl_did); // check that the impl type can be made to match the trait type. @@ -84,7 +83,8 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( let named_type = tcx.type_of(self_type_did); let drop_impl_span = tcx.def_span(drop_impl_did); - let fresh_impl_substs = infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did); + let fresh_impl_substs = + infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did.to_def_id()); let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs); let cause = &ObligationCause::misc(drop_impl_span, drop_impl_hir_id); @@ -94,10 +94,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( } Err(_) => { let item_span = tcx.def_span(self_type_did); - let self_descr = tcx - .def_kind(self_type_did) - .map(|kind| kind.descr(self_type_did)) - .unwrap_or("type"); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); struct_span_err!( tcx.sess, drop_impl_span, @@ -136,7 +133,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); infcx.resolve_regions_and_report_errors( - drop_impl_did, + drop_impl_did.to_def_id(), ®ion_scope_tree, &outlives_env, RegionckMode::default(), @@ -150,7 +147,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( tcx: TyCtxt<'tcx>, dtor_predicates: ty::GenericPredicates<'tcx>, - self_type_did: DefId, + self_type_did: LocalDefId, self_to_impl_substs: SubstsRef<'tcx>, ) -> Result<(), ErrorReported> { let mut result = Ok(()); @@ -190,7 +187,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // absent. So we report an error that the Drop impl injected a // predicate that is not present on the struct definition. - let self_type_hir_id = tcx.hir().as_local_hir_id(self_type_did).unwrap(); + let self_type_hir_id = tcx.hir().as_local_hir_id(self_type_did); // We can assume the predicates attached to struct/enum definition // hold. @@ -244,8 +241,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( if !assumptions_in_impl_context.iter().any(predicate_matches_closure) { let item_span = tcx.hir().span(self_type_hir_id); - let self_descr = - tcx.def_kind(self_type_did).map(|kind| kind.descr(self_type_did)).unwrap_or("type"); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did.to_def_id()); struct_span_err!( tcx.sess, *predicate_sp, diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index dffed9a836c21..266e9b21d69a9 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -15,27 +15,29 @@ use crate::check::FnCtxt; use crate::check::Needs; use crate::check::TupleArgumentsFlag::DontTupleArguments; use crate::type_error_struct; -use crate::util::common::ErrorReported; - -use rustc::middle::lang_items; -use rustc::ty; -use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; -use rustc::ty::Ty; -use rustc::ty::TypeFoldable; -use rustc::ty::{AdtKind, Visibility}; + use rustc_ast::ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; +use rustc_errors::ErrorReported; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items; use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::Ty; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{AdtKind, Visibility}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_trait_selection::traits::{self, ObligationCauseCode}; use std::fmt::Display; @@ -84,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { let expr = expr.peel_drop_temps(); - self.suggest_ref_or_into(&mut err, expr, expected_ty, ty); + self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty); extend_err(&mut err); // Error possibly reported in `check_assign` so avoid emitting error again. err.emit_unless(self.is_assign_to_bool(expr, expected_ty)); @@ -230,7 +232,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) } ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr), - ExprKind::InlineAsm(ref asm) => { + ExprKind::InlineAsm(asm) => self.check_expr_asm(asm), + ExprKind::LlvmInlineAsm(ref asm) => { for expr in asm.outputs_exprs.iter().chain(asm.inputs_exprs.iter()) { self.check_expr(expr); } @@ -475,7 +478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.types.err } Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, expr.span, qpath); + report_unexpected_variant_res(tcx, res, expr.span); tcx.types.err } _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, @@ -696,10 +699,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self, &cause, &mut |db| { - db.span_label( - fn_decl.output.span(), - format!("expected `{}` because of this return type", fn_decl.output,), - ); + let span = fn_decl.output.span(); + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + db.span_label( + span, + format!("expected `{}` because of this return type", snippet), + ); + } }, true, ); @@ -897,8 +903,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: MethodError<'tcx>, ) { let rcvr = &args[0]; - let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, rcvr_t, lang_item| { - if let Some(new_rcvr_t) = self.tcx.mk_lang_item(rcvr_t, lang_item) { + let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { + if let Some(new_rcvr_t) = new_rcvr_t { if let Ok(pick) = self.lookup_probe( span, segment.ident, @@ -926,10 +932,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Try alternative arbitrary self types that could fulfill this call. // FIXME: probe for all types that *could* be arbitrary self-types, not // just this whitelist. - try_alt_rcvr(&mut err, rcvr_t, lang_items::OwnedBoxLangItem); - try_alt_rcvr(&mut err, rcvr_t, lang_items::PinTypeLangItem); - try_alt_rcvr(&mut err, rcvr_t, lang_items::Arc); - try_alt_rcvr(&mut err, rcvr_t, lang_items::Rc); + try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, lang_items::OwnedBoxLangItem)); + try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, lang_items::PinTypeLangItem)); + try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc)); + try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc)); } err.emit(); } @@ -970,18 +976,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let uty = expected.to_option(self).and_then(|uty| match uty.kind { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }); - let element_ty = if !args.is_empty() { - let coerce_to = uty.unwrap_or_else(|| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, + let coerce_to = expected + .to_option(self) + .and_then(|uty| match uty.kind { + ty::Array(ty, _) | ty::Slice(ty) => Some(ty), + _ => None, }) - }); + .unwrap_or_else(|| { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: expr.span, + }) + }); let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); assert_eq!(self.diverges.get(), Diverges::Maybe); for e in args { @@ -1405,7 +1412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { find_best_match_for_name(names, field, None) } - fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { + fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { variant .fields .iter() @@ -1420,7 +1427,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - fn name_series_display(&self, names: Vec) -> String { + fn name_series_display(&self, names: Vec) -> String { // dynamic limit, to never omit just one field let limit = if names.len() == 6 { 6 } else { 5 }; let mut display = @@ -1437,7 +1444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, needs: Needs, base: &'tcx hir::Expr<'tcx>, - field: ast::Ident, + field: Ident, ) -> Ty<'tcx> { let expr_t = self.check_expr_with_needs(base, needs); let expr_t = self.structurally_resolved_type(base.span, expr_t); @@ -1516,7 +1523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn ban_nonexisting_field( &self, - field: ast::Ident, + field: Ident, base: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_t: Ty<'tcx>, @@ -1554,14 +1561,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, - field: ast::Ident, + field: Ident, base_did: DefId, ) { let struct_path = self.tcx().def_path_str(base_did); - let kind_name = match self.tcx().def_kind(base_did) { - Some(def_kind) => def_kind.descr(base_did), - _ => " ", - }; + let kind_name = self.tcx().def_kind(base_did).descr(base_did); let mut err = struct_span_err!( self.tcx().sess, field.span, @@ -1586,7 +1590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: ast::Ident) { + fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { let mut err = type_error_struct!( self.tcx().sess, field.span, @@ -1619,8 +1623,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } let param_def_id = generic_param.def_id; - let param_hir_id = match self.tcx.hir().as_local_hir_id(param_def_id) { - Some(x) => x, + let param_hir_id = match param_def_id.as_local() { + Some(x) => self.tcx.hir().as_local_hir_id(x), None => return, }; let param_span = self.tcx.hir().span(param_hir_id); @@ -1633,7 +1637,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut DiagnosticBuilder<'_>, def: &'tcx ty::AdtDef, - field: ast::Ident, + field: Ident, ) { if let Some(suggested_field_name) = Self::suggest_field_name(def.non_enum_variant(), &field.as_str(), vec![]) @@ -1662,26 +1666,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, - field: ast::Ident, + field: Ident, len: &ty::Const<'tcx>, ) { if let (Some(len), Ok(user_index)) = (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::()) { - let base = self - .tcx - .sess - .source_map() - .span_to_snippet(base.span) - .unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id)); - let help = "instead of using tuple indexing, use array indexing"; - let suggestion = format!("{}[{}]", base, field); - let applicability = if len < user_index { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - err.span_suggestion(expr.span, help, suggestion, applicability); + if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { + let help = "instead of using tuple indexing, use array indexing"; + let suggestion = format!("{}[{}]", base, field); + let applicability = if len < user_index { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + err.span_suggestion(expr.span, help, suggestion, applicability); + } } } @@ -1690,17 +1690,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, - field: ast::Ident, + field: Ident, ) { - let base = self - .tcx - .sess - .source_map() - .span_to_snippet(base.span) - .unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id)); - let msg = format!("`{}` is a raw pointer; try dereferencing it", base); - let suggestion = format!("(*{}).{}", base, field); - err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); + if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { + let msg = format!("`{}` is a raw pointer; try dereferencing it", base); + let suggestion = format!("(*{}).{}", base, field); + err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); + } } fn no_such_field_err( @@ -1800,7 +1796,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we know that the yield type must be `()`; however, the context won't contain this // information. Hence, we check the source of the yield expression here and check its // value's type against `()` (this check should always hold). - None if src == &hir::YieldSource::Await => { + None if src.is_await() => { self.check_expr_coercable_to_type(&value, self.tcx.mk_unit()); self.tcx.mk_unit() } @@ -1816,6 +1812,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + + fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) { + let needs = if is_input { Needs::None } else { Needs::MutPlace }; + let ty = self.check_expr_with_needs(expr, needs); + self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized); + + if !is_input && !expr.is_syntactic_place_expr() { + let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output"); + err.span_label(expr.span, "cannot assign to this expression"); + err.emit(); + } + + // If this is an input value, we require its type to be fully resolved + // at this point. This allows us to provide helpful coercions which help + // pass the type whitelist in a later pass. + // + // We don't require output types to be resolved at this point, which + // allows them to be inferred based on how they are used later in the + // function. + if is_input { + let ty = self.structurally_resolved_type(expr.span, &ty); + match ty.kind { + ty::FnDef(..) => { + let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); + self.demand_coerce(expr, ty, fnptr_ty, AllowTwoPhase::No); + } + ty::Ref(_, base_ty, mutbl) => { + let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl }); + self.demand_coerce(expr, ty, ptr_ty, AllowTwoPhase::No); + } + _ => {} + } + } + } + + fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { + for op in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr } => { + self.check_expr_asm_operand(expr, true); + } + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + self.check_expr_asm_operand(expr, false); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + self.check_expr_asm_operand(expr, false); + } + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.check_expr_asm_operand(in_expr, true); + if let Some(out_expr) = out_expr { + self.check_expr_asm_operand(out_expr, false); + } + } + hir::InlineAsmOperand::Sym { expr } => { + self.check_expr(expr); + } + } + } + if asm.options.contains(ast::InlineAsmOptions::NORETURN) { + self.tcx.types.never + } else { + self.tcx.mk_unit() + } + } } pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs index cdf68256a7a88..ce376a08ea604 100644 --- a/src/librustc_typeck/check/generator_interior.rs +++ b/src/librustc_typeck/check/generator_interior.rs @@ -4,14 +4,14 @@ //! types computed here. use super::FnCtxt; -use rustc::middle::region::{self, YieldData}; -use rustc::ty::{self, Ty}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; +use rustc_middle::middle::region::{self, YieldData}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; struct InteriorVisitor<'a, 'tcx> { @@ -96,6 +96,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { span: source_span, ty: &ty, scope_span, + yield_span: yield_data.span, expr: expr.map(|e| e.hir_id), }) .or_insert(entries); @@ -205,7 +206,7 @@ pub fn resolve_interior<'a, 'tcx>( } // This visitor has to have the same visit_expr calls as RegionResolutionVisitor in -// librustc/middle/region.rs since `expr_count` is compared against the results +// librustc_middle/middle/region.rs since `expr_count` is compared against the results // there. impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { type Map = intravisit::ErasedMap<'tcx>; @@ -235,9 +236,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // Direct calls never need to keep the callee `ty::FnDef` // ZST in a temporary, so skip its type, just in case it // can significantly complicate the generator type. - Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => { + Res::Def( + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn), + _, + ) => { // NOTE(eddyb) this assumes a path expression has // no nested expressions to keep track of. self.expr_count += 1; diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 6f9d8a8e58fa5..bded2c695c9db 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -3,11 +3,11 @@ use crate::require_same_types; -use rustc::traits::{ObligationCause, ObligationCauseCode}; -use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::Symbol; use rustc_target::spec::abi::Abi; @@ -69,7 +69,7 @@ fn equate_intrinsic_type<'tcx>( /// Returns `true` if the given intrinsic is unsafe to call or not. pub fn intrinsic_operation_unsafety(intrinsic: &str) -> hir::Unsafety { match intrinsic { - "size_of" | "min_align_of" | "needs_drop" | "caller_location" | "size_of_val" + "abort" | "size_of" | "min_align_of" | "needs_drop" | "caller_location" | "size_of_val" | "min_align_of_val" | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" | "wrapping_add" | "wrapping_sub" | "wrapping_mul" | "saturating_add" | "saturating_sub" | "rotate_left" | "rotate_right" | "ctpop" | "ctlz" | "cttz" @@ -130,7 +130,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { } }; (n_tps, inputs, output, hir::Unsafety::Unsafe) - } else if &name[..] == "abort" || &name[..] == "unreachable" { + } else if &name[..] == "abort" { + (0, Vec::new(), tcx.types.never, hir::Unsafety::Normal) + } else if &name[..] == "unreachable" { (0, Vec::new(), tcx.types.never, hir::Unsafety::Unsafe) } else { let unsafety = intrinsic_operation_unsafety(&name[..]); @@ -275,20 +277,26 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { (1, vec![param(0), param(0)], param(0)) } - "float_to_int_approx_unchecked" => (2, vec![param(0)], param(1)), + "float_to_int_unchecked" => (2, vec![param(0)], param(1)), "assume" => (0, vec![tcx.types.bool], tcx.mk_unit()), "likely" => (0, vec![tcx.types.bool], tcx.types.bool), "unlikely" => (0, vec![tcx.types.bool], tcx.types.bool), - "discriminant_value" => ( - 1, - vec![tcx.mk_imm_ref( - tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), - param(0), - )], - tcx.types.u64, - ), + "discriminant_value" => { + let assoc_items = + tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); + let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + + ( + 1, + vec![tcx.mk_imm_ref( + tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), + param(0), + )], + tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())), + ) + } "try" => { let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index d340d6ff5c271..c4805c54a7d43 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -4,13 +4,13 @@ use crate::astconv::AstConv; use crate::check::{callee, FnCtxt, Needs, PlaceOp}; use crate::hir::def_id::DefId; use crate::hir::GenericArg; -use rustc::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; -use rustc::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::{Subst, SubstsRef}; -use rustc::ty::{self, GenericParamDefKind, Ty}; use rustc_hir as hir; use rustc_infer::infer::{self, InferOk}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; +use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; use rustc_trait_selection::traits; @@ -114,7 +114,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // a custom error in that case. if illegal_sized_bound.is_none() { let method_ty = self.tcx.mk_fn_ptr(ty::Binder::bind(method_sig)); - self.add_obligations(method_ty, all_substs, &method_predicates); + self.add_obligations(method_ty, all_substs, method_predicates); } // Create the final `MethodCallee`. @@ -209,7 +209,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { "impl {:?} is not an inherent impl", impl_def_id ); - self.impl_self_ty(self.span, impl_def_id).substs + self.fresh_substs_for_item(self.span, impl_def_id) } probe::ObjectPick => { @@ -269,7 +269,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.fcx .autoderef(self.span, self_ty) .include_raw_pointers() - .filter_map(|(ty, _)| match ty.kind { + .find_map(|(ty, _)| match ty.kind { ty::Dynamic(ref data, ..) => Some(closure( self, ty, @@ -279,7 +279,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { )), _ => None, }) - .next() .unwrap_or_else(|| { span_bug!( self.span, @@ -395,7 +394,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { &mut self, fty: Ty<'tcx>, all_substs: SubstsRef<'tcx>, - method_predicates: &ty::InstantiatedPredicates<'tcx>, + method_predicates: ty::InstantiatedPredicates<'tcx>, ) { debug!( "add_obligations: fty={:?} all_substs={:?} method_predicates={:?}", @@ -469,7 +468,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { match expr.kind { hir::ExprKind::Index(ref base_expr, ref index_expr) => { - let index_expr_ty = self.node_ty(index_expr.hir_id); + // We need to get the final type in case dereferences were needed for the trait + // to apply (#72002). + let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr); self.convert_place_op_to_mutable( PlaceOp::Index, expr, @@ -551,12 +552,11 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } // If we have an autoref followed by unsizing at the end, fix the unsize target. - match adjustments[..] { - [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] => - { - *target = method.sig.inputs()[0]; - } - _ => {} + + if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = + adjustments[..] + { + *target = method.sig.inputs()[0]; } } } @@ -573,25 +573,25 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { None => return None, }; - traits::elaborate_predicates(self.tcx, predicates.predicates.clone()) - .filter_map(|predicate| match predicate { + traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) + .filter_map(|obligation| match obligation.predicate { ty::Predicate::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { let span = predicates .predicates .iter() .zip(predicates.spans.iter()) - .filter_map(|(p, span)| if *p == predicate { Some(*span) } else { None }) - .next() + .find_map( + |(p, span)| if *p == obligation.predicate { Some(*span) } else { None }, + ) .unwrap_or(rustc_span::DUMMY_SP); Some((trait_pred, span)) } _ => None, }) - .filter_map(|(trait_pred, span)| match trait_pred.skip_binder().self_ty().kind { + .find_map(|(trait_pred, span)| match trait_pred.skip_binder().self_ty().kind { ty::Dynamic(..) => Some(span), _ => None, }) - .next() } fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) { diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 962f9807546ac..a254aecf07bab 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -11,17 +11,17 @@ pub use self::CandidateSource::*; pub use self::MethodError::*; use crate::check::FnCtxt; -use rustc::ty::subst::Subst; -use rustc::ty::subst::{InternalSubsts, SubstsRef}; -use rustc::ty::GenericParamDefKind; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness}; -use rustc_ast::ast; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness}; +use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -104,7 +104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Determines whether the type `self_ty` supports a method name `method_name` or not. pub fn method_exists( &self, - method_name: ast::Ident, + method_name: Ident, self_ty: Ty<'tcx>, call_expr_id: hir::HirId, allow_private: bool, @@ -133,7 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut DiagnosticBuilder<'a>, msg: &str, - method_name: ast::Ident, + method_name: Ident, self_ty: Ty<'tcx>, call_expr: &hir::Expr<'_>, ) { @@ -171,11 +171,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Given a method call like `foo.bar::(...)`: /// - /// * `fcx`: the surrounding `FnCtxt` (!) - /// * `span`: the span for the method call - /// * `method_name`: the name of the method being called (`bar`) + /// * `self`: the surrounding `FnCtxt` (!) /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) - /// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`) + /// * `segment`: the name and generic arguments of the method (`bar::`) + /// * `span`: the span for the method call + /// * `call_expr`: the complete method call: (`foo.bar::(...)`) /// * `self_expr`: the self expression (`foo`) pub fn lookup_method( &self, @@ -198,7 +198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("used_trait_import: {:?}", import_def_id); Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) .unwrap() - .insert(import_def_id); + .insert(import_def_id.to_def_id()); } self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span); @@ -260,7 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn lookup_probe( &self, span: Span, - method_name: ast::Ident, + method_name: Ident, self_ty: Ty<'tcx>, call_expr: &'tcx hir::Expr<'tcx>, scope: ProbeScope, @@ -290,7 +290,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn lookup_method_in_trait( &self, span: Span, - m_name: ast::Ident, + m_name: Ident, trait_def_id: DefId, self_ty: Ty<'tcx>, opt_input_types: Option<&[Ty<'tcx>]>, @@ -390,7 +390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(!bounds.has_escaping_bound_vars()); let cause = traits::ObligationCause::misc(span, self.body_id); - obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, &bounds)); + obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); // Also add an obligation for the method type being well-formed. let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig)); @@ -414,7 +414,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn resolve_ufcs( &self, span: Span, - method_name: ast::Ident, + method_name: Ident, self_ty: Ty<'tcx>, expr_id: hir::HirId, ) -> Result<(DefKind, DefId), MethodError<'tcx>> { @@ -463,11 +463,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for import_id in pick.import_ids { let import_def_id = tcx.hir().local_def_id(import_id); debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id); - used_trait_imports.insert(import_def_id); + used_trait_imports.insert(import_def_id.to_def_id()); } } - let def_kind = pick.item.def_kind(); + let def_kind = pick.item.kind.as_def_kind(); debug!("resolve_ufcs: def_kind={:?}, def_id={:?}", def_kind, pick.item.def_id); tcx.check_stability(pick.item.def_id, Some(expr_id), span); Ok((def_kind, pick.item.def_id)) @@ -478,7 +478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn associated_item( &self, def_id: DefId, - item_name: ast::Ident, + item_name: Ident, ns: Namespace, ) -> Option { self.tcx diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 45b1c7d6ea740..e21db9035e25d 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -8,12 +8,6 @@ use crate::check::FnCtxt; use crate::hir::def::DefKind; use crate::hir::def_id::DefId; -use rustc::middle::stability; -use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc::ty::GenericParamDefKind; -use rustc::ty::{ - self, ParamEnvAnd, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; use rustc_ast::ast; use rustc_ast::util::lev_distance::{find_best_match_for_name, lev_distance}; use rustc_data_structures::fx::FxHashSet; @@ -26,9 +20,15 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; +use rustc_middle::middle::stability; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{ + self, ParamEnvAnd, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; use rustc_session::config::nightly_options; use rustc_session::lint; -use rustc_span::{symbol::Symbol, Span, DUMMY_SP}; +use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; use rustc_trait_selection::traits::query::method_autoderef::{ @@ -55,7 +55,7 @@ struct ProbeContext<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, mode: Mode, - method_name: Option, + method_name: Option, return_type: Option>, /// This is the OriginalQueryValues for the steps queries @@ -268,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, mode: Mode, - item_name: ast::Ident, + item_name: Ident, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, @@ -295,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &'a self, span: Span, mode: Mode, - method_name: Option, + method_name: Option, return_type: Option>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, @@ -452,7 +452,7 @@ fn method_autoderef_steps<'tcx>( tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = Autoderef::new(infcx, param_env, hir::DUMMY_HIR_ID, DUMMY_SP, self_ty) + let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty) .include_raw_pointers() .silence_errors(); let mut reached_raw_pointer = false; @@ -518,7 +518,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, mode: Mode, - method_name: Option, + method_name: Option, return_type: Option>, orig_steps_var_values: OriginalQueryValues<'tcx>, steps: Lrc>>, @@ -570,7 +570,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.extension_candidates.push(candidate); } } else if self.private_candidate.is_none() { - self.private_candidate = Some((candidate.item.def_kind(), candidate.item.def_id)); + self.private_candidate = + Some((candidate.item.kind.as_def_kind(), candidate.item.def_id)); } } @@ -648,11 +649,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } ty::RawPtr(ty::TypeAndMut { ty: _, mutbl }) => { - let lang_def_id = match mutbl { - hir::Mutability::Not => lang_items.const_ptr_impl(), - hir::Mutability::Mut => lang_items.mut_ptr_impl(), + let (lang_def_id1, lang_def_id2) = match mutbl { + hir::Mutability::Not => { + (lang_items.const_ptr_impl(), lang_items.const_slice_ptr_impl()) + } + hir::Mutability::Mut => { + (lang_items.mut_ptr_impl(), lang_items.mut_slice_ptr_impl()) + } }; - self.assemble_inherent_impl_for_primitive(lang_def_id); + self.assemble_inherent_impl_for_primitive(lang_def_id1); + self.assemble_inherent_impl_for_primitive(lang_def_id2); } ty::Int(i) => { let lang_def_id = match i { @@ -804,7 +810,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => None, }); self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { @@ -859,9 +866,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, expr_hir_id: hir::HirId, ) -> Result<(), MethodError<'tcx>> { - if expr_hir_id == hir::DUMMY_HIR_ID { - return Ok(()); - } let mut duplicates = FxHashSet::default(); let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id); if let Some(applicable_traits) = opt_applicable_traits { @@ -896,7 +900,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { expected: Ty<'tcx>, ) -> bool { match method.kind { - ty::AssocKind::Method => { + ty::AssocKind::Fn => { let fty = self.tcx.fn_sig(method.def_id); self.probe(|_| { let substs = self.fresh_substs_for_item(self.span, method.def_id); @@ -975,7 +979,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Ok(()) } - fn candidate_method_names(&self) -> Vec { + fn candidate_method_names(&self) -> Vec { let mut set = FxHashSet::default(); let mut names: Vec<_> = self .inherent_candidates @@ -1128,8 +1132,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) -> Option> { let tcx = self.tcx; - // In general, during probing we erase regions. See - // `impl_self_ty()` for an explanation. + // In general, during probing we erase regions. let region = tcx.lifetimes.re_erased; let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl }); @@ -1340,7 +1343,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // clauses) that must be considered. Make sure that those // match as well (or at least may match, sometimes we // don't have enough information to fully evaluate). - let candidate_obligations: Vec<_> = match probe.kind { + match probe.kind { InherentImplCandidate(ref substs, ref ref_obligations) => { // Check whether the impl imposes obligations we have to worry about. let impl_def_id = probe.item.container.id(); @@ -1351,19 +1354,23 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Convert the bounds into obligations. let impl_obligations = - traits::predicates_for_generics(cause, self.param_env, &impl_bounds); + traits::predicates_for_generics(cause, self.param_env, impl_bounds); - debug!("impl_obligations={:?}", impl_obligations); - impl_obligations - .into_iter() + let candidate_obligations = impl_obligations .chain(norm_obligations.into_iter()) - .chain(ref_obligations.iter().cloned()) - .collect() + .chain(ref_obligations.iter().cloned()); + // Evaluate those obligations to see if they might possibly hold. + for o in candidate_obligations { + let o = self.resolve_vars_if_possible(&o); + if !self.predicate_may_hold(&o) { + result = ProbeResult::NoMatch; + possibly_unsatisfied_predicates.push((o.predicate, None)); + } + } } ObjectCandidate | WhereClauseCandidate(..) => { // These have no additional conditions to check. - vec![] } TraitCandidate(trait_ref) => { @@ -1410,17 +1417,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return ProbeResult::NoMatch; } } - vec![] } - }; - - debug!( - "consider_probe - candidate_obligations={:?} sub_obligations={:?}", - candidate_obligations, sub_obligations - ); + } // Evaluate those obligations to see if they might possibly hold. - for o in candidate_obligations.into_iter().chain(sub_obligations) { + for o in sub_obligations { let o = self.resolve_vars_if_possible(&o); if !self.predicate_may_hold(&o) { result = ProbeResult::NoMatch; @@ -1513,7 +1514,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ); pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); - pcx.assemble_extension_candidates_for_traits_in_scope(hir::DUMMY_HIR_ID)?; let method_names = pcx.candidate_method_names(); pcx.allow_similar_names = false; @@ -1523,10 +1523,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.reset(); pcx.method_name = Some(method_name); pcx.assemble_inherent_candidates(); - pcx.assemble_extension_candidates_for_traits_in_scope(hir::DUMMY_HIR_ID) - .map_or(None, |_| { - pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) - }) + pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) }) .collect(); @@ -1554,10 +1551,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.method_has_self_argument, + Mode::MethodCall => item.fn_has_self_parameter, Mode::Path => match item.kind { ty::AssocKind::OpaqueTy | ty::AssocKind::Type => false, - ty::AssocKind::Method | ty::AssocKind::Const => true, + ty::AssocKind::Fn | ty::AssocKind::Const => true, }, } // FIXME -- check for types that deref to `Self`, @@ -1578,7 +1575,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { impl_ty: Ty<'tcx>, substs: SubstsRef<'tcx>, ) -> (Ty<'tcx>, Option>) { - if item.kind == ty::AssocKind::Method && self.mode == Mode::MethodCall { + if item.kind == ty::AssocKind::Fn && self.mode == Mode::MethodCall { let sig = self.xform_method_sig(item.def_id, substs); (sig.inputs()[0], Some(sig.output())) } else { @@ -1614,8 +1611,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } else { match param.kind { GenericParamDefKind::Lifetime => { - // In general, during probe we erase regions. See - // `impl_self_ty()` for an explanation. + // In general, during probe we erase regions. self.tcx.lifetimes.re_erased.into() } GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 68996f5aaf973..cf26c94418e2d 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -2,11 +2,6 @@ //! found or is otherwise invalid. use crate::check::FnCtxt; -use crate::middle::lang_items::FnOnceTraitLangItem; -use rustc::hir::map as hir_map; -use rustc::ty::print::with_crate_prefix; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc_ast::ast; use rustc_ast::util::lev_distance; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; @@ -14,9 +9,15 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit; +use rustc_hir::lang_items::FnOnceTraitLangItem; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_span::symbol::kw; +use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::print::with_crate_prefix; +use rustc_middle::ty::{ + self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; +use rustc_span::symbol::{kw, Ident}; use rustc_span::{source_map, FileName, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::Obligation; @@ -70,7 +71,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, rcvr_ty: Ty<'tcx>, - item_name: ast::Ident, + item_name: Ident, source: SelfSource<'b>, error: MethodError<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, @@ -115,7 +116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .span_if_local(item.def_id) .or_else(|| self.tcx.hir().span_if_local(impl_did)); - let impl_ty = self.impl_self_ty(span, impl_did).ty; + let impl_ty = self.tcx.at(span).type_of(impl_did); let insertion = match self.tcx.impl_trait_ref(impl_did) { None => String::new(), @@ -147,7 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(note_span) = note_span { // We have a span pointing to the method. Show note with snippet. err.span_note( - self.tcx.sess.source_map().def_span(note_span), + self.tcx.sess.source_map().guess_head_span(note_span), ¬e_str, ); } else { @@ -160,7 +161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::AssocKind::Const | ty::AssocKind::Type | ty::AssocKind::OpaqueTy => rcvr_ty, - ty::AssocKind::Method => self + ty::AssocKind::Fn => self .tcx .fn_sig(item.def_id) .inputs() @@ -177,6 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path, ty, item.kind, + item.def_id, sugg_span, idx, self.tcx.sess.source_map(), @@ -189,8 +191,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(item) => item, None => continue, }; - let item_span = - self.tcx.sess.source_map().def_span(self.tcx.def_span(item.def_id)); + let item_span = self + .tcx + .sess + .source_map() + .guess_head_span(self.tcx.def_span(item.def_id)); let idx = if sources.len() > 1 { let msg = &format!( "candidate #{} is defined in the trait `{}`", @@ -215,6 +220,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path, rcvr_ty, item.kind, + item.def_id, sugg_span, idx, self.tcx.sess.source_map(), @@ -266,11 +272,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut candidates = all_traits(self.tcx).into_iter().filter_map(|info| { self.associated_item(info.def_id, item_name, Namespace::ValueNS) }); - if let (true, false, SelfSource::MethodCall(expr), Some(_)) = ( + // There are methods that are defined on the primitive types and won't be + // found when exploring `all_traits`, but we also need them to be acurate on + // our suggestions (#47759). + let fund_assoc = |opt_def_id: Option| { + opt_def_id + .and_then(|id| self.associated_item(id, item_name, Namespace::ValueNS)) + .is_some() + }; + let lang_items = tcx.lang_items(); + let found_candidate = candidates.next().is_some() + || fund_assoc(lang_items.i8_impl()) + || fund_assoc(lang_items.i16_impl()) + || fund_assoc(lang_items.i32_impl()) + || fund_assoc(lang_items.i64_impl()) + || fund_assoc(lang_items.i128_impl()) + || fund_assoc(lang_items.u8_impl()) + || fund_assoc(lang_items.u16_impl()) + || fund_assoc(lang_items.u32_impl()) + || fund_assoc(lang_items.u64_impl()) + || fund_assoc(lang_items.u128_impl()) + || fund_assoc(lang_items.f32_impl()) + || fund_assoc(lang_items.f32_runtime_impl()) + || fund_assoc(lang_items.f64_impl()) + || fund_assoc(lang_items.f64_runtime_impl()); + if let (true, false, SelfSource::MethodCall(expr), true) = ( actual.is_numeric(), actual.has_concrete_skeleton(), source, - candidates.next(), + found_candidate, ) { let mut err = struct_span_err!( tcx.sess, @@ -397,7 +427,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(def) = actual.ty_adt_def() { if let Some(full_sp) = tcx.hir().span_if_local(def.did) { - let def_sp = tcx.sess.source_map().def_span(full_sp); + let def_sp = tcx.sess.source_map().guess_head_span(full_sp); err.span_label( def_sp, format!( @@ -508,7 +538,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // When the "method" is resolved through dereferencing, we really want the // original type that has the associated function for accurate suggestions. // (#61411) - let ty = self.impl_self_ty(span, *impl_did).ty; + let ty = tcx.at(span).type_of(*impl_did); match (&ty.peel_refs().kind, &actual.peel_refs().kind) { (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { // Use `actual` as it will have more `substs` filled in. @@ -537,8 +567,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut restrict_type_params = false; if !unsatisfied_predicates.is_empty() { - let def_span = - |def_id| self.tcx.sess.source_map().def_span(self.tcx.def_span(def_id)); + let def_span = |def_id| { + self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)) + }; let mut type_params = FxHashMap::default(); let mut bound_spans = vec![]; let mut collect_type_param_suggestions = @@ -547,31 +578,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&self_ty.kind, parent_pred) { if let ty::Adt(def, _) = p.skip_binder().trait_ref.self_ty().kind { - let node = self - .tcx - .hir() - .as_local_hir_id(def.did) - .map(|id| self.tcx.hir().get(id)); - match node { - Some(hir::Node::Item(hir::Item { kind, .. })) => { - if let Some(g) = kind.generics() { - let key = match &g.where_clause.predicates[..] { - [.., pred] => { - (pred.span().shrink_to_hi(), false) - } - [] => ( - g.where_clause - .span_for_predicates_or_empty_place(), - true, - ), - }; - type_params - .entry(key) - .or_insert_with(FxHashSet::default) - .insert(obligation.to_owned()); - } + let node = def.did.as_local().map(|def_id| { + self.tcx.hir().get(self.tcx.hir().as_local_hir_id(def_id)) + }); + if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { + if let Some(g) = kind.generics() { + let key = match &g.where_clause.predicates[..] { + [.., pred] => (pred.span().shrink_to_hi(), false), + [] => ( + g.where_clause + .span_for_predicates_or_empty_place(), + true, + ), + }; + type_params + .entry(key) + .or_insert_with(FxHashSet::default) + .insert(obligation.to_owned()); } - _ => {} } } } @@ -739,7 +763,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, msg); } } else if let Some(lev_candidate) = lev_candidate { - let def_kind = lev_candidate.def_kind(); + let def_kind = lev_candidate.kind.as_def_kind(); err.span_suggestion( span, &format!( @@ -830,7 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidates: Vec, ) { let module_did = self.tcx.parent_module(self.body_id); - let module_id = self.tcx.hir().as_local_hir_id(module_did.to_def_id()).unwrap(); + let module_id = self.tcx.hir().as_local_hir_id(module_did); let krate = self.tcx.hir().krate(); let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id); if let Some(span) = span { @@ -898,7 +922,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, span: Span, rcvr_ty: Ty<'tcx>, - item_name: ast::Ident, + item_name: Ident, source: SelfSource<'b>, valid_out_of_scope_traits: Vec, unsatisfied_predicates: &[(ty::Predicate<'tcx>, Option>)], @@ -922,18 +946,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this isn't perfect (that is, there are cases when // implementing a trait would be legal but is rejected // here). - !unsatisfied_predicates.iter().any(|(p, _)| match p { + unsatisfied_predicates.iter().all(|(p, _)| match p { // Hide traits if they are present in predicates as they can be fixed without // having to implement them. - ty::Predicate::Trait(t, _) => t.def_id() != info.def_id, - ty::Predicate::Projection(p) => p.item_def_id() != info.def_id, - _ => true, + ty::Predicate::Trait(t, _) => t.def_id() == info.def_id, + ty::Predicate::Projection(p) => p.item_def_id() == info.def_id, + _ => false, }) && (type_is_local || info.def_id.is_local()) && self .associated_item(info.def_id, item_name, Namespace::ValueNS) .filter(|item| { - if let ty::AssocKind::Method = item.kind { - let id = self.tcx.hir().as_local_hir_id(item.def_id); + if let ty::AssocKind::Fn = item.kind { + let id = item + .def_id + .as_local() + .map(|def_id| self.tcx.hir().as_local_hir_id(def_id)); if let Some(hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(fn_sig, method), .. @@ -944,23 +971,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ident.name == kw::SelfLower } hir::TraitFn::Provided(body_id) => { - match &self.tcx.hir().body(*body_id).params[..] { - [hir::Param { - pat: - hir::Pat { - kind: - hir::PatKind::Binding( - _, - _, - ident, - .., - ), - .. - }, - .. - }, ..] => ident.name == kw::SelfLower, - _ => false, - } + self.tcx.hir().body(*body_id).params.first().map_or( + false, + |param| { + matches!( + param.pat.kind, + hir::PatKind::Binding(_, _, ident, _) + if ident.name == kw::SelfLower + ) + }, + ) } _ => false, }; @@ -1025,7 +1045,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let generics = self.tcx.generics_of(table_owner.to_def_id()); let type_param = generics.type_param(param, self.tcx); let hir = &self.tcx.hir(); - if let Some(id) = hir.as_local_hir_id(type_param.def_id) { + if let Some(def_id) = type_param.def_id.as_local() { + let id = hir.as_local_hir_id(def_id); // Get the `hir::Param` to verify whether it already has any bounds. // We do this to avoid suggesting code that ends up as `T: FooBar`, // instead we suggest `T: Foo + Bar` in that case. @@ -1057,7 +1078,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_def_ids: FxHashSet = param .bounds .iter() - .filter_map(|bound| bound.trait_def_id()) + .filter_map(|bound| Some(bound.trait_ref()?.trait_def_id()?)) .collect(); if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { err.span_suggestions( @@ -1117,7 +1138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let [trait_info] = &candidates[..] { if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) { err.span_note( - self.tcx.sess.source_map().def_span(span), + self.tcx.sess.source_map().guess_head_span(span), &format!( "`{}` defines an item `{}`, perhaps you need to {} it", self.tcx.def_path_str(trait_info.def_id), @@ -1231,7 +1252,7 @@ fn compute_all_traits(tcx: TyCtxt<'_>) -> Vec { match i.kind { hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) => { let def_id = self.map.local_def_id(i.hir_id); - self.traits.push(def_id); + self.traits.push(def_id.to_def_id()); } _ => (), } @@ -1254,7 +1275,7 @@ fn compute_all_traits(tcx: TyCtxt<'_>) -> Vec { res: Res, ) { match res { - Res::Def(DefKind::Trait, def_id) | Res::Def(DefKind::TraitAlias, def_id) => { + Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) => { traits.push(def_id); } Res::Def(DefKind::Mod, def_id) => { @@ -1356,18 +1377,19 @@ impl intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> { } fn print_disambiguation_help( - item_name: ast::Ident, + item_name: Ident, args: Option<&'tcx [hir::Expr<'tcx>]>, err: &mut DiagnosticBuilder<'_>, trait_name: String, rcvr_ty: Ty<'_>, kind: ty::AssocKind, + def_id: DefId, span: Span, candidate: Option, source_map: &source_map::SourceMap, ) { let mut applicability = Applicability::MachineApplicable; - let sugg_args = if let (ty::AssocKind::Method, Some(args)) = (kind, args) { + let sugg_args = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) { format!( "({}{})", if rcvr_ty.is_region_ptr() { @@ -1391,7 +1413,7 @@ fn print_disambiguation_help( span, &format!( "disambiguate the {} for {}", - kind.suggestion_descr(), + kind.as_def_kind().descr(def_id), if let Some(candidate) = candidate { format!("candidate #{}", candidate) } else { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 085510452c47e..d72c74e4188ee 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -88,40 +88,45 @@ mod wfcheck; pub mod writeback; use crate::astconv::{AstConv, GenericArgCountMismatch, PathSeg}; -use crate::middle::lang_items; -use rustc::hir::map::blocks::FnLikeNode; -use rustc::middle::region; -use rustc::mir::interpret::ConstValue; -use rustc::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, -}; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::layout::VariantIdx; -use rustc::ty::query::Providers; -use rustc::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts}; -use rustc::ty::util::{Discr, IntTypeExt, Representability}; -use rustc::ty::{ - self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef, - ToPredicate, Ty, TyCtxt, UserType, WithConstness, -}; use rustc_ast::ast; use rustc_ast::util::parser::ExprPrecedence; use rustc_attr as attr; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::ErrorReported; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::lang_items::{ + FutureTraitLangItem, PinTypeLangItem, SizedTraitLangItem, VaListTypeLangItem, +}; use rustc_hir::{ExprKind, GenericArg, HirIdMap, Item, ItemKind, Node, PatKind, QPath}; +use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_infer::infer::{self, InferCtxt, InferOk, InferResult, TyCtxtInferExt}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::middle::region; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, +}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{ + GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, +}; +use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; +use rustc_middle::ty::{ + self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef, + ToPredicate, Ty, TyCtxt, UserType, WithConstness, +}; use rustc_session::config::{self, EntryFnType}; use rustc_session::lint; use rustc_session::parse::feature_err; @@ -130,6 +135,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl}; @@ -149,8 +155,7 @@ use std::ops::{self, Deref}; use std::slice; use crate::require_c_abi_if_c_variadic; -use crate::util::common::{indenter, ErrorReported}; -use crate::TypeAndSubsts; +use crate::util::common::indenter; use self::autoderef::Autoderef; use self::callee::DeferredCallResolution; @@ -751,15 +756,15 @@ fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) { }); } -fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) { +fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { wfcheck::check_item_well_formed(tcx, def_id); } -fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) { +fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { wfcheck::check_trait_item(tcx, def_id); } -fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) { +fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { wfcheck::check_impl_item(tcx, def_id); } @@ -835,14 +840,15 @@ fn has_typeck_tables(tcx: TyCtxt<'_>, def_id: DefId) -> bool { return tcx.has_typeck_tables(outer_def_id); } - if let Some(id) = tcx.hir().as_local_hir_id(def_id) { + if let Some(def_id) = def_id.as_local() { + let id = tcx.hir().local_def_id_to_hir_id(def_id); primary_body_of(tcx, id).is_some() } else { false } } -fn used_trait_imports(tcx: TyCtxt<'_>, def_id: DefId) -> &DefIdSet { +fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &DefIdSet { &*tcx.typeck_tables_of(def_id).used_trait_imports } @@ -957,8 +963,8 @@ where val.fold_with(&mut FixupFolder { tcx }) } -fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &ty::TypeckTables<'tcx> { - let fallback = move || tcx.type_of(def_id); +fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckTables<'tcx> { + let fallback = move || tcx.type_of(def_id.to_def_id()); typeck_tables_of_with_fallback(tcx, def_id, fallback) } @@ -966,11 +972,10 @@ fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &ty::TypeckTables /// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. fn diagnostic_only_typeck_tables_of<'tcx>( tcx: TyCtxt<'tcx>, - def_id: DefId, + def_id: LocalDefId, ) -> &ty::TypeckTables<'tcx> { - assert!(def_id.is_local()); let fallback = move || { - let span = tcx.hir().span(tcx.hir().as_local_hir_id(def_id).unwrap()); + let span = tcx.hir().span(tcx.hir().as_local_hir_id(def_id)); tcx.sess.delay_span_bug(span, "diagnostic only typeck table used"); tcx.types.err }; @@ -979,17 +984,17 @@ fn diagnostic_only_typeck_tables_of<'tcx>( fn typeck_tables_of_with_fallback<'tcx>( tcx: TyCtxt<'tcx>, - def_id: DefId, + def_id: LocalDefId, fallback: impl Fn() -> Ty<'tcx> + 'tcx, ) -> &'tcx ty::TypeckTables<'tcx> { // Closures' tables come from their outermost function, // as they are part of the same "inference environment". - let outer_def_id = tcx.closure_base_def_id(def_id); + let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); if outer_def_id != def_id { return tcx.typeck_tables_of(outer_def_id); } - let id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let id = tcx.hir().as_local_hir_id(def_id); let span = tcx.hir().span(id); // Figure out what primary body this item has. @@ -998,7 +1003,7 @@ fn typeck_tables_of_with_fallback<'tcx>( }); let body = tcx.hir().body(body_id); - let tables = Inherited::build(tcx, def_id.expect_local()).enter(|inh| { + let tables = Inherited::build(tcx, def_id).enter(|inh| { let param_env = tcx.param_env(def_id); let fcx = if let (Some(header), Some(decl)) = (fn_header, fn_decl) { let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { @@ -1018,7 +1023,7 @@ fn typeck_tables_of_with_fallback<'tcx>( check_abi(tcx, span, fn_sig.abi()); // Compute the fty from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id, &fn_sig); + let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), &fn_sig); let fn_sig = inh.normalize_associated_types_in( body.value.span, body_id.hir_id, @@ -1110,7 +1115,7 @@ fn typeck_tables_of_with_fallback<'tcx>( // because they don't constrain other type variables. fcx.closure_analyze(body); assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - fcx.resolve_generator_interiors(def_id); + fcx.resolve_generator_interiors(def_id.to_def_id()); for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { let ty = fcx.normalize_ty(span, ty); @@ -1157,7 +1162,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option>) -> Ty<'tcx> { match ty_opt { None => { - // infer the variable's type + // Infer the variable's type. let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span, @@ -1169,7 +1174,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { var_ty } Some(typ) => { - // take type that the user specified + // Take type that the user specified. self.fcx.locals.borrow_mut().insert(nid, typ); typ.revealed_ty } @@ -1240,7 +1245,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { intravisit::walk_pat(self, p); } - // Don't descend into the bodies of nested closures + // Don't descend into the bodies of nested closures. fn visit_fn( &mut self, _: intravisit::FnKind<'tcx>, @@ -1288,7 +1293,7 @@ fn check_fn<'a, 'tcx>( debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env); - // Create the function context. This is either derived from scratch or, + // Create the function context. This is either derived from scratch or, // in the case of closures, based on the outer context. let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id); @@ -1298,7 +1303,6 @@ fn check_fn<'a, 'tcx>( let hir = tcx.hir(); let declared_ret_ty = fn_sig.output(); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); let revealed_ret_ty = fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span()); debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty); @@ -1326,17 +1330,15 @@ fn check_fn<'a, 'tcx>( fcx.resume_yield_tys = Some((resume_ty, yield_ty)); } - let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id)); - let outer_hir_id = hir.as_local_hir_id(outer_def_id).unwrap(); + let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id).to_def_id()); + let outer_hir_id = hir.as_local_hir_id(outer_def_id.expect_local()); GatherLocalsVisitor { fcx: &fcx, parent_id: outer_hir_id }.visit_body(body); // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` // (as it's created inside the body itself, not passed in from outside). let maybe_va_list = if fn_sig.c_variadic { - let va_list_did = tcx.require_lang_item( - lang_items::VaListTypeLangItem, - Some(body.params.last().unwrap().span), - ); + let va_list_did = + tcx.require_lang_item(VaListTypeLangItem, Some(body.params.last().unwrap().span)); let region = tcx.mk_region(ty::ReScope(region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite, @@ -1367,7 +1369,25 @@ fn check_fn<'a, 'tcx>( inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - fcx.check_return_expr(&body.value); + if let ty::Dynamic(..) = declared_ret_ty.kind { + // FIXME: We need to verify that the return type is `Sized` after the return expression has + // been evaluated so that we have types available for all the nodes being returned, but that + // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this + // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, + // while keeping the current ordering we will ignore the tail expression's type because we + // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` + // because we will trigger "unreachable expression" lints unconditionally. + // Because of all of this, we perform a crude check to know whether the simplest `!Sized` + // case that a newcomer might make, returning a bare trait, and in that case we populate + // the tail expression's type so that the suggestion will be correct, but ignore all other + // possible cases. + fcx.check_expr(&body.value); + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + tcx.sess.delay_span_bug(decl.output.span(), "`!Sized` return type"); + } else { + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + fcx.check_return_expr(&body.value); + } // We insert the deferred_generator_interiors entry after visiting the body. // This ensures that all nested generators appear before the entry of this generator. @@ -1424,7 +1444,7 @@ fn check_fn<'a, 'tcx>( // Check that the main return type implements the termination trait. if let Some(term_id) = tcx.lang_items().termination() { if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) { - let main_id = hir.as_local_hir_id(def_id).unwrap(); + let main_id = hir.as_local_hir_id(def_id); if main_id == fn_id { let substs = tcx.mk_substs_trait(declared_ret_ty, &[]); let trait_ref = ty::TraitRef::new(term_id, substs); @@ -1446,7 +1466,7 @@ fn check_fn<'a, 'tcx>( // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` if let Some(panic_impl_did) = tcx.lang_items().panic_impl() { - if panic_impl_did == hir.local_def_id(fn_id) { + if panic_impl_did == hir.local_def_id(fn_id).to_def_id() { if let Some(panic_info_did) = tcx.lang_items().panic_info() { if declared_ret_ty.kind != ty::Never { sess.span_err(decl.output.span(), "return type should be `!`"); @@ -1479,7 +1499,7 @@ fn check_fn<'a, 'tcx>( } } } else { - let span = sess.source_map().def_span(span); + let span = sess.source_map().guess_head_span(span); sess.span_err(span, "function should have one argument"); } } else { @@ -1490,7 +1510,7 @@ fn check_fn<'a, 'tcx>( // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` if let Some(alloc_error_handler_did) = tcx.lang_items().oom() { - if alloc_error_handler_did == hir.local_def_id(fn_id) { + if alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() { if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() { if declared_ret_ty.kind != ty::Never { sess.span_err(decl.output.span(), "return type should be `!`"); @@ -1520,7 +1540,7 @@ fn check_fn<'a, 'tcx>( } } } else { - let span = sess.source_map().def_span(span); + let span = sess.source_map().guess_head_span(span); sess.span_err(span, "function should have one argument"); } } else { @@ -1542,8 +1562,8 @@ fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { check_simd(tcx, span, def_id); } - check_transparent(tcx, span, def_id); - check_packed(tcx, span, def_id); + check_transparent(tcx, span, def); + check_packed(tcx, span, def); } fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { @@ -1551,14 +1571,14 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { let def = tcx.adt_def(def_id); def.destructor(tcx); // force the destructor to be evaluated check_representable(tcx, span, def_id); - check_transparent(tcx, span, def_id); + check_transparent(tcx, span, def); check_union_fields(tcx, span, def_id); - check_packed(tcx, span, def_id); + check_packed(tcx, span, def); } /// When the `#![feature(untagged_unions)]` gate is active, /// check that the fields of the `union` does not contain fields that need dropping. -fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool { +fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { let item_type = tcx.type_of(item_def_id); if let ty::Adt(def, substs) = item_type.kind { assert!(def.is_union()); @@ -1590,7 +1610,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool { /// projections that would result in "inheriting lifetimes". fn check_opaque<'tcx>( tcx: TyCtxt<'tcx>, - def_id: DefId, + def_id: LocalDefId, substs: SubstsRef<'tcx>, span: Span, origin: &hir::OpaqueTyOrigin, @@ -1601,9 +1621,8 @@ fn check_opaque<'tcx>( /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result /// in "inheriting lifetimes". -fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: DefId, span: Span) { - let item = - tcx.hir().expect_item(tcx.hir().as_local_hir_id(def_id).expect("opaque type is not local")); +fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { + let item = tcx.hir().expect_item(tcx.hir().as_local_hir_id(def_id)); debug!( "check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}", def_id, span, item @@ -1629,14 +1648,28 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: DefId, span: r.super_visit_with(self) } + + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + if let ty::ConstKind::Unevaluated(..) = c.val { + // FIXME(#72219) We currenctly don't detect lifetimes within substs + // which would violate this check. Even though the particular substitution is not used + // within the const, this should still be fixed. + return false; + } + c.super_visit_with(self) + } } let prohibit_opaque = match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::AsyncFn, .. }) - | ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn, .. }) => { + ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn, + .. + }) => { let mut visitor = ProhibitOpaqueVisitor { - opaque_identity_ty: tcx - .mk_opaque(def_id, InternalSubsts::identity_for_item(tcx, def_id)), + opaque_identity_ty: tcx.mk_opaque( + def_id.to_def_id(), + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), + ), generics: tcx.generics_of(def_id), }; debug!("check_opaque_for_inheriting_lifetimes: visitor={:?}", visitor); @@ -1659,23 +1692,27 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: DefId, span: _ => unreachable!(), }; - tcx.sess.span_err(span, &format!( + tcx.sess.span_err( + span, + &format!( "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ a parent scope", if is_async { "async fn" } else { "impl Trait" }, - )); + ), + ); } } /// Checks that an opaque type does not contain cycles. fn check_opaque_for_cycles<'tcx>( tcx: TyCtxt<'tcx>, - def_id: DefId, + def_id: LocalDefId, substs: SubstsRef<'tcx>, span: Span, origin: &hir::OpaqueTyOrigin, ) { - if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id, substs) { + if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) + { if let hir::OpaqueTyOrigin::AsyncFn = origin { struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing",) .span_label(span, "recursive `async fn`") @@ -1707,18 +1744,18 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { debug!( "check_item_type(it.hir_id={}, it.name={})", it.hir_id, - tcx.def_path_str(tcx.hir().local_def_id(it.hir_id)) + tcx.def_path_str(tcx.hir().local_def_id(it.hir_id).to_def_id()) ); let _indenter = indenter(); match it.kind { // Consts can play a role in type-checking, so they are included here. hir::ItemKind::Static(..) => { let def_id = tcx.hir().local_def_id(it.hir_id); - tcx.typeck_tables_of(def_id); + tcx.ensure().typeck_tables_of(def_id); maybe_check_static_with_link_section(tcx, def_id, it.span); } hir::ItemKind::Const(..) => { - tcx.typeck_tables_of(tcx.hir().local_def_id(it.hir_id)); + tcx.ensure().typeck_tables_of(tcx.hir().local_def_id(it.hir_id)); } hir::ItemKind::Enum(ref enum_definition, _) => { check_enum(tcx, it.span, &enum_definition.variants, it.hir_id); @@ -1735,7 +1772,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } hir::ItemKind::Trait(_, _, _, _, ref items) => { let def_id = tcx.hir().local_def_id(it.hir_id); - check_on_unimplemented(tcx, def_id, it); + check_on_unimplemented(tcx, def_id.to_def_id(), it); for item in items.iter() { let item = tcx.hir().trait_item(item.id); @@ -1754,14 +1791,14 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { let def_id = tcx.hir().local_def_id(it.hir_id); - let substs = InternalSubsts::identity_for_item(tcx, def_id); + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); check_opaque(tcx, def_id, substs, it.span, &origin); } hir::ItemKind::TyAlias(..) => { let def_id = tcx.hir().local_def_id(it.hir_id); let pty_ty = tcx.type_of(def_id); let generics = tcx.generics_of(def_id); - check_bounds_are_used(tcx, &generics, pty_ty); + check_type_params_are_used(tcx, &generics, pty_ty); } hir::ItemKind::ForeignMod(ref m) => { check_abi(tcx, it.span, m.abi); @@ -1817,7 +1854,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } } -fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: DefId, span: Span) { +fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: Span) { // Only restricted on wasm32 target for now if !tcx.sess.opts.target_triple.triple().starts_with("wasm32") { return; @@ -1837,12 +1874,12 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: DefId, span: Span) // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. - match tcx.const_eval_poly(id) { + match tcx.const_eval_poly(id.to_def_id()) { Ok(ConstValue::ByRef { alloc, .. }) => { if alloc.relocations().len() != 0 { let msg = "statics with a custom `#[link_section]` must be a \ - simple list of bytes on the wasm target with no \ - extra levels of indirection such as references"; + simple list of bytes on the wasm target with no \ + extra levels of indirection such as references"; tcx.sess.span_err(span, msg); } } @@ -1854,7 +1891,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: DefId, span: Span) fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) { let item_def_id = tcx.hir().local_def_id(item.hir_id); // an error would be reported if this fails. - let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id); + let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id.to_def_id()); } fn report_forbidden_specialization( @@ -1897,7 +1934,7 @@ fn check_specialization_validity<'tcx>( ) { let kind = match impl_item.kind { hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Method, + hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, hir::ImplItemKind::OpaqueTy(..) => ty::AssocKind::OpaqueTy, hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, }; @@ -1937,7 +1974,7 @@ fn check_specialization_validity<'tcx>( // grandparent. In that case, if parent is a `default impl`, inherited items use the // "defaultness" from the grandparent, else they are final. None => { - if traits::impl_is_default(tcx, parent_impl.def_id()) { + if tcx.impl_defaultness(parent_impl.def_id()).is_default() { None } else { Some(Err(parent_impl.def_id())) @@ -1958,11 +1995,11 @@ fn check_specialization_validity<'tcx>( fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, full_impl_span: Span, - impl_id: DefId, + impl_id: LocalDefId, impl_trait_ref: ty::TraitRef<'tcx>, impl_item_refs: &[hir::ImplItemRef<'_>], ) { - let impl_span = tcx.sess.source_map().def_span(full_impl_span); + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); // If the trait reference itself is erroneous (so the compilation is going // to fail), skip checking the items here -- the `impl_item` table in `tcx` @@ -1971,6 +2008,24 @@ fn check_impl_items_against_trait<'tcx>( return; } + // Negative impls are not expected to have any items + match tcx.impl_polarity(impl_id) { + ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} + ty::ImplPolarity::Negative => { + if let [first_item_ref, ..] = impl_item_refs { + let first_item_span = tcx.hir().impl_item(first_item_ref.id).span; + struct_span_err!( + tcx.sess, + first_item_span, + E0749, + "negative impls cannot have any items" + ) + .emit(); + } + return; + } + } + // Locate trait definition and items let trait_def = tcx.trait_def(impl_trait_ref.def_id); @@ -2010,7 +2065,7 @@ fn check_impl_items_against_trait<'tcx>( impl_item.span, E0323, "item `{}` is an associated const, \ - which doesn't match its trait `{}`", + which doesn't match its trait `{}`", ty_impl_item.ident, impl_trait_ref.print_only_trait_path() ); @@ -2025,7 +2080,7 @@ fn check_impl_items_against_trait<'tcx>( } hir::ImplItemKind::Fn(..) => { let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - if ty_trait_item.kind == ty::AssocKind::Method { + if ty_trait_item.kind == ty::AssocKind::Fn { compare_impl_method( tcx, &ty_impl_item, @@ -2081,20 +2136,26 @@ fn check_impl_items_against_trait<'tcx>( } } - check_specialization_validity(tcx, trait_def, &ty_trait_item, impl_id, impl_item); + check_specialization_validity( + tcx, + trait_def, + &ty_trait_item, + impl_id.to_def_id(), + impl_item, + ); } } // Check for missing items from trait let mut missing_items = Vec::new(); - if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id) { + if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { let is_implemented = ancestors .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.node.is_from_trait()) + .map(|node_item| !node_item.defining_node.is_from_trait()) .unwrap_or(false); - if !is_implemented && !traits::impl_is_default(tcx, impl_id) { + if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { if !trait_item.defaultness.has_value() { missing_items.push(*trait_item); } @@ -2223,26 +2284,39 @@ fn fn_sig_suggestion( sig: &ty::FnSig<'_>, ident: Ident, predicates: ty::GenericPredicates<'_>, + assoc: &ty::AssocItem, ) -> String { let args = sig .inputs() .iter() - .map(|ty| { + .enumerate() + .map(|(i, ty)| { Some(match ty.kind { - ty::Param(param) if param.name == kw::SelfUpper => "self".to_string(), - ty::Ref(reg, ref_ty, mutability) => { + ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), + ty::Ref(reg, ref_ty, mutability) if i == 0 => { let reg = match &format!("{}", reg)[..] { "'_" | "" => String::new(), reg => format!("{} ", reg), }; - match ref_ty.kind { - ty::Param(param) if param.name == kw::SelfUpper => { - format!("&{}{}self", reg, mutability.prefix_str()) + if assoc.fn_has_self_parameter { + match ref_ty.kind { + ty::Param(param) if param.name == kw::SelfUpper => { + format!("&{}{}self", reg, mutability.prefix_str()) + } + + _ => format!("self: {}", ty), } - _ => format!("_: {:?}", ty), + } else { + format!("_: {:?}", ty) + } + } + _ => { + if assoc.fn_has_self_parameter && i == 0 { + format!("self: {:?}", ty) + } else { + format!("_: {:?}", ty) } } - _ => format!("_: {:?}", ty), }) }) .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None })) @@ -2271,7 +2345,7 @@ fn fn_sig_suggestion( /// structured suggestion. fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { match assoc.kind { - ty::AssocKind::Method => { + 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 @@ -2281,6 +2355,7 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { tcx.fn_sig(assoc.def_id).skip_binder(), assoc.ident, tcx.predicates_of(assoc.def_id), + assoc, ) } ty::AssocKind::Type => format!("type {} = Type;", assoc.ident), @@ -2297,7 +2372,7 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { /// Checks whether a type can be represented in memory. In particular, it /// identifies types that contain themselves without indirection through a /// pointer, which would mean their size is unbounded. -fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: DefId) -> bool { +fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { let rty = tcx.type_of(item_def_id); // Check that it is possible to represent this type. This call identifies @@ -2307,7 +2382,7 @@ fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: DefId) -> bool { // caught by case 1. match rty.is_representable(tcx, sp) { Representability::SelfRecursive(spans) => { - let mut err = recursive_type_with_infinite_size_error(tcx, item_def_id); + let mut err = recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id()); for span in spans { err.span_label(span, "recursive without indirection"); } @@ -2319,7 +2394,7 @@ fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: DefId) -> bool { true } -pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { +pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { let t = tcx.type_of(def_id); if let ty::Adt(def, substs) = t.kind { if def.is_struct() { @@ -2353,10 +2428,10 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { } } -fn check_packed(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { - let repr = tcx.adt_def(def_id).repr; +fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: &ty::AdtDef) { + let repr = def.repr; if repr.packed() { - for attr in tcx.get_attrs(def_id).iter() { + for attr in tcx.get_attrs(def.did).iter() { for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) { if let attr::ReprPacked(pack) = r { if let Some(repr_pack) = repr.pack { @@ -2382,7 +2457,7 @@ fn check_packed(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { ) .emit(); } else { - if let Some(def_spans) = check_packed_inner(tcx, def_id, &mut vec![]) { + if let Some(def_spans) = check_packed_inner(tcx, def.did, &mut vec![]) { let mut err = struct_span_err!( tcx.sess, sp, @@ -2391,34 +2466,32 @@ fn check_packed(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { ); let hir = tcx.hir(); - if let Some(hir_id) = hir.as_local_hir_id(def_spans[0].0) { - if let Node::Item(Item { ident, .. }) = hir.get(hir_id) { - err.span_note( - tcx.def_span(def_spans[0].0), - &format!("`{}` has a `#[repr(align)]` attribute", ident), - ); - } + let hir_id = hir.as_local_hir_id(def_spans[0].0.expect_local()); + if let Node::Item(Item { ident, .. }) = hir.get(hir_id) { + err.span_note( + tcx.def_span(def_spans[0].0), + &format!("`{}` has a `#[repr(align)]` attribute", ident), + ); } if def_spans.len() > 2 { let mut first = true; for (adt_def, span) in def_spans.iter().skip(1).rev() { - if let Some(hir_id) = hir.as_local_hir_id(*adt_def) { - if let Node::Item(Item { ident, .. }) = hir.get(hir_id) { - err.span_note( - *span, - &if first { - format!( - "`{}` contains a field of type `{}`", - tcx.type_of(def_id), - ident - ) - } else { - format!("...which contains a field of type `{}`", ident) - }, - ); - first = false; - } + let hir_id = hir.as_local_hir_id(adt_def.expect_local()); + if let Node::Item(Item { ident, .. }) = hir.get(hir_id) { + err.span_note( + *span, + &if first { + format!( + "`{}` contains a field of type `{}`", + tcx.type_of(def.did), + ident + ) + } else { + format!("...which contains a field of type `{}`", ident) + }, + ); + first = false; } } } @@ -2503,12 +2576,11 @@ fn bad_non_zero_sized_fields<'tcx>( err.emit(); } -fn check_transparent(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { - let adt = tcx.adt_def(def_id); +fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty::AdtDef) { if !adt.repr.transparent() { return; } - let sp = tcx.sess.source_map().def_span(sp); + let sp = tcx.sess.source_map().guess_head_span(sp); if adt.is_union() && !tcx.features().transparent_unions { feature_err( @@ -2521,7 +2593,7 @@ fn check_transparent(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { } if adt.variants.len() != 1 { - bad_variant_count(tcx, adt, sp, def_id); + bad_variant_count(tcx, adt, sp, adt.did); if adt.variants.is_empty() { // Don't bother checking the fields. No variants (and thus no fields) exist. return; @@ -2573,7 +2645,7 @@ pub fn check_enum<'tcx>( def.destructor(tcx); // force the destructor to be evaluated if vs.is_empty() { - let attributes = tcx.get_attrs(def_id); + let attributes = tcx.get_attrs(def_id.to_def_id()); if let Some(attr) = attr::find_by_name(&attributes, sym::repr) { struct_span_err!( tcx.sess, @@ -2601,7 +2673,7 @@ pub fn check_enum<'tcx>( for v in vs { if let Some(ref e) = v.disr_expr { - tcx.typeck_tables_of(tcx.hir().local_def_id(e.hir_id)); + tcx.ensure().typeck_tables_of(tcx.hir().local_def_id(e.hir_id)); } } @@ -2628,7 +2700,7 @@ pub fn check_enum<'tcx>( // Check for duplicate discriminant values if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { let variant_did = def.variants[VariantIdx::new(i)].def_id; - let variant_i_hir_id = tcx.hir().as_local_hir_id(variant_did).unwrap(); + let variant_i_hir_id = tcx.hir().as_local_hir_id(variant_did.expect_local()); let variant_i = tcx.hir().expect_variant(variant_i_hir_id); let i_span = match variant_i.disr_expr { Some(ref expr) => tcx.hir().span(expr.hir_id), @@ -2653,17 +2725,17 @@ pub fn check_enum<'tcx>( } check_representable(tcx, sp, def_id); - check_transparent(tcx, sp, def_id); + check_transparent(tcx, sp, def); } -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span, qpath: &QPath<'_>) { +fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { struct_span_err!( tcx.sess, span, E0533, - "expected unit struct, unit variant or constant, found {} `{}`", + "expected unit struct, unit variant or constant, found {}{}", res.descr(), - hir::print::to_string(&tcx.hir(), |s| s.print_qpath(qpath, false)) + tcx.sess.source_map().span_to_snippet(span).map_or(String::new(), |s| format!(" `{}`", s)), ) .emit(); } @@ -2689,7 +2761,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { let tcx = self.tcx; - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let item_id = tcx.hir().ty_param_owner(hir_id); let item_def_id = tcx.hir().local_def_id(item_id); let generics = tcx.generics_of(item_def_id); @@ -3182,7 +3254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (value, opaque_type_map) = self.register_infer_ok_obligations(self.instantiate_opaque_types( - parent_def_id, + parent_def_id.to_def_id(), self.body_id, self.param_env, value, @@ -3234,7 +3306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { code: traits::ObligationCauseCode<'tcx>, ) { if !ty.references_error() { - let lang_item = self.tcx.require_lang_item(lang_items::SizedTraitLangItem, None); + let lang_item = self.tcx.require_lang_item(SizedTraitLangItem, None); self.require_type_meets(ty, span, code, lang_item); } } @@ -3287,8 +3359,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> { - let c = self.tcx.hir().local_def_id(ast_c.hir_id).expect_local(); - ty::Const::from_anon_const(self.tcx, c) + let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); + let c = ty::Const::from_anon_const(self.tcx, const_def_id); + + // HACK(eddyb) emulate what a `WellFormedConst` obligation would do. + // This code should be replaced with the proper WF handling ASAP. + if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val { + assert!(promoted.is_none()); + + // HACK(eddyb) let's hope these are always empty. + // let obligations = self.nominal_obligations(def_id, substs); + // self.out.extend(obligations); + + let cause = traits::ObligationCause::new( + self.tcx.def_span(const_def_id.to_def_id()), + self.body_id, + traits::MiscObligation, + ); + self.register_predicate(traits::Obligation::new( + cause, + self.param_env, + ty::Predicate::ConstEvaluatable(def_id, substs), + )); + } + + c } // If the type given by the user has free regions, save it for later, since @@ -3367,7 +3462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn add_obligations_for_parameters( &self, cause: traits::ObligationCause<'tcx>, - predicates: &ty::InstantiatedPredicates<'tcx>, + predicates: ty::InstantiatedPredicates<'tcx>, ) { assert!(!predicates.has_escaping_bound_vars()); @@ -3418,8 +3513,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // In that case, fallback to Error. // The return value indicates whether fallback has occurred. fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { - use rustc::ty::error::UnconstrainedNumeric::Neither; - use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; + use rustc_middle::ty::error::UnconstrainedNumeric::Neither; + use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; assert!(ty.is_ty_infer()); let fallback = match self.type_is_unconstrained_numeric(ty) { @@ -3554,7 +3649,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let adjusted_ty = autoderef.unambiguous_final_ty(self); debug!( "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ - index_ty={:?})", + index_ty={:?})", expr, base_expr, adjusted_ty, index_ty ); @@ -3626,14 +3721,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option, ast::Ident) { + fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option, Ident) { let (tr, name) = match (op, is_mut) { (PlaceOp::Deref, false) => (self.tcx.lang_items().deref_trait(), sym::deref), (PlaceOp::Deref, true) => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), (PlaceOp::Index, false) => (self.tcx.lang_items().index_trait(), sym::index), (PlaceOp::Index, true) => (self.tcx.lang_items().index_mut_trait(), sym::index_mut), }; - (tr, ast::Ident::with_dummy_span(name)) + (tr, Ident::with_dummy_span(name)) } fn try_overloaded_place_op( @@ -3773,6 +3868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Predicate::WellFormed(..) => None, ty::Predicate::ObjectSafe(..) => None, ty::Predicate::ConstEvaluatable(..) => None, + ty::Predicate::ConstEquate(..) => None, // N.B., this predicate is created by breaking down a // `ClosureType: FnFoo()` predicate, where // `ClosureType` represents some `Closure`. It can't @@ -3875,7 +3971,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().def_span(sp)) { + if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) { err.span_label(def_s, "defined here"); } if sugg_unit { @@ -4062,10 +4158,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Float(ast::FloatTy::F32) => { variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); } - ty::Int(ast::IntTy::I8) | ty::Int(ast::IntTy::I16) | ty::Bool => { + ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); } - ty::Uint(ast::UintTy::U8) | ty::Uint(ast::UintTy::U16) => { + ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); } ty::FnDef(..) => { @@ -4115,20 +4211,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `FulfillmentError`. let mut referenced_in = final_arg_types .iter() - .map(|(i, checked_ty, _)| (i, checked_ty)) - .chain(final_arg_types.iter().map(|(i, _, coerced_ty)| (i, coerced_ty))) + .map(|&(i, checked_ty, _)| (i, checked_ty)) + .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty))) .flat_map(|(i, ty)| { - let ty = self.resolve_vars_if_possible(ty); + let ty = self.resolve_vars_if_possible(&ty); // We walk the argument type because the argument's type could have // been `Option`, but the `FulfillmentError` references `T`. - ty.walk() - .filter(|&ty| ty == predicate.skip_binder().self_ty()) - .map(move |_| *i) + if ty.walk().any(|arg| arg == predicate.skip_binder().self_ty().into()) { + Some(i) + } else { + None + } }) .collect::>(); // Both checked and coerced types could have matched, thus we need to remove // duplicates. + referenced_in.sort(); referenced_in.dedup(); if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) { @@ -4223,24 +4322,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // Determine the `Self` type, using fresh variables for all variables - // declared on the impl declaration e.g., `impl for Vec<(A,B)>` - // would return `($0, $1)` where `$0` and `$1` are freshly instantiated type - // variables. - pub fn impl_self_ty( - &self, - span: Span, // (potential) receiver for this impl - did: DefId, - ) -> TypeAndSubsts<'tcx> { - let ity = self.tcx.type_of(did); - debug!("impl_self_ty: ity={:?}", ity); - - let substs = self.fresh_substs_for_item(span, did); - let substd_ty = self.instantiate_type_scheme(span, &substs, &ity); - - TypeAndSubsts { substs, ty: substd_ty } - } - /// Unifies the output type with the expected type early, for more coercions /// and forward type information on the input expressions. fn expected_inputs_for_expected_output( @@ -4315,10 +4396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)), _ => bug!("unexpected type: {:?}", ty), }, - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) + Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfTy(..) => match ty.kind { ty::Adt(adt, substs) if !adt.is_enum() => { Some((adt.non_enum_variant(), adt.did, substs)) @@ -4336,7 +4414,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (bounds, _) = self.instantiate_bounds(path_span, did, substs); let cause = traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did)); - self.add_obligations_for_parameters(cause, &bounds); + self.add_obligations_for_parameters(cause, bounds); Some((variant, ty)) } else { @@ -4421,15 +4499,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => Err(ErrorReported), }; if item_name.name != kw::Invalid { - self.report_method_error( + if let Some(mut e) = self.report_method_error( span, ty, item_name, SelfSource::QPath(qself), error, None, - ) - .map(|mut e| e.emit()); + ) { + e.emit(); + } } result }); @@ -4705,7 +4784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label( fn_span, "implicitly returns `()` as its body has no tail or `return` \ - expression", + expression", ); } }, @@ -4749,19 +4828,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - fn get_parent_fn_decl( - &self, - blk_id: hir::HirId, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, ast::Ident)> { + fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id)); self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) } /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. - fn get_node_fn_decl( - &self, - node: Node<'tcx>, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, ast::Ident, bool)> { + fn get_node_fn_decl(&self, node: Node<'tcx>) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { match node { Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { // This is less than ideal, it will not suggest a return type span on any @@ -4848,15 +4921,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut msg = "call this function"; match hir.get_if_local(def_id) { - Some(Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })) - | Some(Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body_id), - .. - })) - | Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), - .. - })) => { + Some( + Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body_id), .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), + .. + }), + ) => { let body = hir.body(*body_id); sugg_call = body .params @@ -4898,11 +4972,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { sugg_call = fields.iter().map(|_| "_").collect::>().join(", "); - match hir.as_local_hir_id(def_id).and_then(|hir_id| hir.def_kind(hir_id)) { - Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { + match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { + Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { msg = "instantiate this tuple variant"; } - Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, _)) => { + Some(DefKind::Ctor(CtorOf::Struct, _)) => { msg = "instantiate this tuple struct"; } _ => {} @@ -4953,26 +5027,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - pub fn suggest_ref_or_into( + pub fn suggest_deref_ref_or_into( &self, err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, ) { - if let Some((sp, msg, suggestion)) = self.check_ref(expr, found, expected) { - err.span_suggestion(sp, msg, suggestion, Applicability::MachineApplicable); + if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { + err.span_suggestion(sp, msg, suggestion, applicability); } else if let (ty::FnDef(def_id, ..), true) = (&found.kind, self.suggest_fn_call(err, expr, expected, found)) { if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - let sp = self.sess().source_map().def_span(sp); + let sp = self.sess().source_map().guess_head_span(sp); err.span_label(sp, &format!("{} defined here", found)); } } else if !self.check_for_cast(err, expr, found, expected) { let is_struct_pat_shorthand_field = self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); - let methods = self.get_conversion_methods(expr.span, expected, found); + let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { let mut suggestions = iter::repeat(&expr_text) .zip(methods.iter()) @@ -5072,7 +5146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } let boxed_found = self.tcx.mk_box(found); - let new_found = self.tcx.mk_lang_item(boxed_found, lang_items::PinTypeLangItem).unwrap(); + let new_found = self.tcx.mk_lang_item(boxed_found, PinTypeLangItem).unwrap(); if let (true, Ok(snippet)) = ( self.can_coerce(new_found, expected), self.sess().source_map().span_to_snippet(expr.span), @@ -5219,6 +5293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) { + debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found); // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the // body isn't `async`. let item_id = self.tcx().hir().get_parent_node(self.body_id); @@ -5228,7 +5303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sp = expr.span; // Check for `Future` implementations by constructing a predicate to // prove: `::Output == U` - let future_trait = self.tcx.lang_items().future_trait().unwrap(); + let future_trait = self.tcx.require_lang_item(FutureTraitLangItem, Some(sp)); let item_def_id = self .tcx .associated_items(future_trait) @@ -5236,22 +5311,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .next() .unwrap() .def_id; + // `::Output` + let projection_ty = ty::ProjectionTy { + // `T` + substs: self + .tcx + .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)), + // `Future::Output` + item_def_id, + }; + let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate { - // `::Output` - projection_ty: ty::ProjectionTy { - // `T` - substs: self.tcx.mk_substs_trait( - found, - self.fresh_substs_for_item(sp, item_def_id), - ), - // `Future::Output` - item_def_id, - }, + projection_ty, ty: expected, })); let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); + debug!("suggest_missing_await: trying obligation {:?}", obligation); + if self.infcx.predicate_may_hold(&obligation) { debug!("suggest_missing_await: obligation held: {:?}", obligation); if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { @@ -5311,7 +5389,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => return None, }; let last_expr_ty = self.node_ty(last_expr.hir_id); - if self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() { + if matches!(last_expr_ty.kind, ty::Error) + || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() + { return None; } let original_span = original_sp(last_stmt.span, blk.span); @@ -5353,7 +5433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_alias_variant_ctor = true; } } - Res::Def(DefKind::AssocFn, def_id) | Res::Def(DefKind::AssocConst, def_id) => { + Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { let container = tcx.associated_item(def_id).container; debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); match container { @@ -5577,11 +5657,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { Ok(ok) => self.register_infer_ok_obligations(ok), Err(_) => { - self.tcx.sess.delay_span_bug(span, &format!( + self.tcx.sess.delay_span_bug( + span, + &format!( "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", self_ty, impl_ty, - )); + ), + ); } } } @@ -5601,9 +5684,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (i, mut obligation) in traits::predicates_for_generics( traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)), self.param_env, - &bounds, + bounds, ) - .into_iter() .enumerate() { // This makes the error point at the bound, but we want to point at the argument @@ -5717,43 +5799,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } -pub fn check_bounds_are_used<'tcx>(tcx: TyCtxt<'tcx>, generics: &ty::Generics, ty: Ty<'tcx>) { - let own_counts = generics.own_counts(); - debug!( - "check_bounds_are_used(n_tys={}, n_cts={}, ty={:?})", - own_counts.types, own_counts.consts, ty - ); +fn check_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, generics: &ty::Generics, ty: Ty<'tcx>) { + debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty); + + assert_eq!(generics.parent, None); - if own_counts.types == 0 { + if generics.own_counts().types == 0 { return; } - // Make a vector of booleans initially `false`; set to `true` when used. - let mut types_used = vec![false; own_counts.types]; + let mut params_used = BitSet::new_empty(generics.params.len()); - for leaf_ty in ty.walk() { - if let ty::Param(ty::ParamTy { index, .. }) = leaf_ty.kind { - debug!("found use of ty param num {}", index); - types_used[index as usize - own_counts.lifetimes] = true; - } else if let ty::Error = leaf_ty.kind { - // If there is already another error, do not emit - // an error for not using a type parameter. - assert!(tcx.sess.has_errors()); - return; + if ty.references_error() { + // If there is already another error, do not emit + // an error for not using a type parameter. + assert!(tcx.sess.has_errors()); + return; + } + + for leaf in ty.walk() { + if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { + if let ty::Param(param) = leaf_ty.kind { + debug!("found use of ty param {:?}", param); + params_used.insert(param.index); + } } } - let types = generics.params.iter().filter(|param| match param.kind { - ty::GenericParamDefKind::Type { .. } => true, - _ => false, - }); - for (&used, param) in types_used.iter().zip(types) { - if !used { - let id = tcx.hir().as_local_hir_id(param.def_id).unwrap(); - let span = tcx.hir().span(id); - struct_span_err!(tcx.sess, span, E0091, "type parameter `{}` is unused", param.name) + for param in &generics.params { + if !params_used.contains(param.index) { + if let ty::GenericParamDefKind::Type { .. } = param.kind { + let span = tcx.def_span(param.def_id); + struct_span_err!( + tcx.sess, + span, + E0091, + "type parameter `{}` is unused", + param.name, + ) .span_label(span, "unused type parameter") .emit(); + } } } } @@ -5767,7 +5853,7 @@ fn fatally_break_rust(sess: &Session) { handler.note_without_error("the compiler expectedly panicked. this is a feature."); handler.note_without_error( "we would appreciate a joke overview: \ - https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", + https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", ); handler.note_without_error(&format!( "rustc {} running on {}", diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index f589805e1e261..00ff2af82e303 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -2,13 +2,15 @@ use super::method::MethodCallee; use super::{FnCtxt, Needs}; -use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; -use rustc::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint}; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc_ast::ast::Ident; use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable}; +use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; @@ -249,8 +251,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Err(()) => { // error types are considered "builtin" - if !lhs_ty.references_error() { + if !lhs_ty.references_error() && !rhs_ty.references_error() { let source_map = self.tcx.sess.source_map(); + match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( @@ -315,12 +318,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g., "Hello " += "World!"). This means // we don't want the note in the else clause to be emitted - } else if let ty::Param(_) = lhs_ty.kind { - // FIXME: point to span of param - err.note(&format!( - "`{}` might need a bound for `{}`", - lhs_ty, missing_trait - )); + } else if let ty::Param(p) = lhs_ty.kind { + suggest_constraining_param( + self.tcx, + self.body_id, + &mut err, + lhs_ty, + rhs_ty, + missing_trait, + p, + false, + ); } else if !suggested_deref { suggest_impl_missing(&mut err, lhs_ty, &missing_trait); } @@ -328,46 +336,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } IsAssign::No => { - let (message, missing_trait) = match op.node { + let (message, missing_trait, use_output) = match op.node { hir::BinOpKind::Add => ( format!("cannot add `{}` to `{}`", rhs_ty, lhs_ty), Some("std::ops::Add"), + true, ), hir::BinOpKind::Sub => ( format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty), Some("std::ops::Sub"), + true, ), hir::BinOpKind::Mul => ( format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty), Some("std::ops::Mul"), + true, ), hir::BinOpKind::Div => ( format!("cannot divide `{}` by `{}`", lhs_ty, rhs_ty), Some("std::ops::Div"), + true, ), hir::BinOpKind::Rem => ( format!("cannot mod `{}` by `{}`", lhs_ty, rhs_ty), Some("std::ops::Rem"), + true, ), hir::BinOpKind::BitAnd => ( format!("no implementation for `{} & {}`", lhs_ty, rhs_ty), Some("std::ops::BitAnd"), + true, ), hir::BinOpKind::BitXor => ( format!("no implementation for `{} ^ {}`", lhs_ty, rhs_ty), Some("std::ops::BitXor"), + true, ), hir::BinOpKind::BitOr => ( format!("no implementation for `{} | {}`", lhs_ty, rhs_ty), Some("std::ops::BitOr"), + true, ), hir::BinOpKind::Shl => ( format!("no implementation for `{} << {}`", lhs_ty, rhs_ty), Some("std::ops::Shl"), + true, ), hir::BinOpKind::Shr => ( format!("no implementation for `{} >> {}`", lhs_ty, rhs_ty), Some("std::ops::Shr"), + true, ), hir::BinOpKind::Eq | hir::BinOpKind::Ne => ( format!( @@ -376,6 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty ), Some("std::cmp::PartialEq"), + false, ), hir::BinOpKind::Lt | hir::BinOpKind::Le @@ -387,6 +406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty ), Some("std::cmp::PartialOrd"), + false, ), _ => ( format!( @@ -395,6 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty ), None, + false, ), }; let mut err = struct_span_err!( @@ -457,12 +478,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted - } else if let ty::Param(_) = lhs_ty.kind { - // FIXME: point to span of param - err.note(&format!( - "`{}` might need a bound for `{}`", - lhs_ty, missing_trait - )); + } else if let ty::Param(p) = lhs_ty.kind { + suggest_constraining_param( + self.tcx, + self.body_id, + &mut err, + lhs_ty, + rhs_ty, + missing_trait, + p, + use_output, + ); } else if !suggested_deref && !involves_fn { suggest_impl_missing(&mut err, lhs_ty, &missing_trait); } @@ -479,7 +505,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// If one of the types is an uncalled function and calling it would yield the other type, - /// suggest calling the function. Returns whether a suggestion was given. + /// suggest calling the function. Returns `true` if suggestion would apply (even if not given). fn add_type_neq_err_label( &self, err: &mut rustc_errors::DiagnosticBuilder<'_>, @@ -492,36 +518,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, ty.to_string()); if let FnDef(def_id, _) = ty.kind { let source_map = self.tcx.sess.source_map(); - let hir_id = match self.tcx.hir().as_local_hir_id(def_id) { - Some(hir_id) => hir_id, - None => return false, - }; if !self.tcx.has_typeck_tables(def_id) { return false; } - let fn_sig = { - match self.tcx.typeck_tables_of(def_id).liberated_fn_sigs().get(hir_id) { - Some(f) => *f, - None => { - bug!("No fn-sig entry for def_id={:?}", def_id); - } - } - }; + // We're emitting a suggestion, so we can just ignore regions + let fn_sig = *self.tcx.fn_sig(def_id).skip_binder(); let other_ty = if let FnDef(def_id, _) = other_ty.kind { - let hir_id = match self.tcx.hir().as_local_hir_id(def_id) { - Some(hir_id) => hir_id, - None => return false, - }; if !self.tcx.has_typeck_tables(def_id) { return false; } - match self.tcx.typeck_tables_of(def_id).liberated_fn_sigs().get(hir_id) { - Some(f) => f.clone().output(), - None => { - bug!("No fn-sig entry for def_id={:?}", def_id); - } - } + // We're emitting a suggestion, so we can just ignore regions + self.tcx.fn_sig(def_id).skip_binder().output() } else { other_ty }; @@ -530,24 +538,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign)) .is_ok() { - let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { - ( - format!("{}( /* arguments */ )", source_map.span_to_snippet(span).unwrap()), - Applicability::HasPlaceholders, - ) - } else { - ( - format!("{}()", source_map.span_to_snippet(span).unwrap()), - Applicability::MaybeIncorrect, - ) - }; + if let Ok(snippet) = source_map.span_to_snippet(span) { + let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { + (format!("{}( /* arguments */ )", snippet), Applicability::HasPlaceholders) + } else { + (format!("{}()", snippet), Applicability::MaybeIncorrect) + }; - err.span_suggestion( - span, - "you might have forgotten to call this function", - variable_snippet, - applicability, - ); + err.span_suggestion( + span, + "you might have forgotten to call this function", + variable_snippet, + applicability, + ); + } return true; } } @@ -931,3 +935,43 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra } } } + +fn suggest_constraining_param( + tcx: TyCtxt<'_>, + body_id: hir::HirId, + mut err: &mut DiagnosticBuilder<'_>, + lhs_ty: Ty<'_>, + rhs_ty: Ty<'_>, + missing_trait: &str, + p: ty::ParamTy, + set_output: bool, +) { + let hir = tcx.hir(); + let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait); + // Try to find the def-id and details for the parameter p. We have only the index, + // so we have to find the enclosing function's def-id, then look through its declared + // generic parameters to get the declaration. + let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id }); + let generics = tcx.generics_of(def_id); + let param_def_id = generics.type_param(&p, tcx).def_id; + if let Some(generics) = param_def_id + .as_local() + .map(|id| hir.as_local_hir_id(id)) + .and_then(|id| hir.find(hir.get_parent_item(id))) + .as_ref() + .and_then(|node| node.generics()) + { + let output = if set_output { format!("", rhs_ty) } else { String::new() }; + suggest_constraining_type_param( + tcx, + generics, + &mut err, + &format!("{}", lhs_ty), + &format!("{}{}", missing_trait, output), + None, + ); + } else { + let span = tcx.def_span(param_def_id); + err.span_label(span, msg); + } +} diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index 0d38fa98bd7f8..8a10427260eee 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -1,6 +1,4 @@ use crate::check::FnCtxt; -use rustc::ty::subst::GenericArg; -use rustc::ty::{self, BindingMode, Ty, TypeFoldable}; use rustc_ast::ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; @@ -11,8 +9,11 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BindingMode, Ty, TypeFoldable}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::Ident; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use std::cmp; @@ -104,7 +105,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> { actual: Ty<'tcx>, ti: TopInfo<'tcx>, ) { - self.demand_eqtype_pat_diag(cause_span, expected, actual, ti).map(|mut err| err.emit()); + if let Some(mut err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { + err.emit(); + } } } @@ -171,9 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti) } - PatKind::Path(ref qpath) => { - self.check_pat_path(pat, path_res.unwrap(), qpath, expected, ti) - } + PatKind::Path(_) => self.check_pat_path(pat, path_res.unwrap(), expected, ti), PatKind::Struct(ref qpath, fields, etc) => { self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti) } @@ -288,7 +289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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, _) | Res::Def(DefKind::AssocConst, _) => AdjustMode::Pass, + 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 @@ -451,12 +452,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Subtyping doesn't matter here, as the value is some kind of scalar. let demand_eqtype = |x, y| { if let Some((_, x_ty, x_span)) = x { - self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti).map(|mut err| { + if let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) { if let Some((_, y_ty, y_span)) = y { self.endpoint_has_type(&mut err, y_span, y_ty); } err.emit(); - }); + }; } }; demand_eqtype(lhs, rhs); @@ -694,7 +695,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &Pat<'_>, path_resolution: (Res, Option>, &'b [hir::PathSegment<'b>]), - qpath: &hir::QPath<'_>, expected: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { @@ -707,17 +707,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(); return tcx.types.err; } - Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => { - report_unexpected_variant_res(tcx, res, pat.span, qpath); + Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { + report_unexpected_variant_res(tcx, res, pat.span); return tcx.types.err; } - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) - | Res::SelfCtor(..) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::ConstParam, _) => {} // OK + Res::SelfCtor(..) + | Res::Def( + DefKind::Ctor(_, CtorKind::Const) + | DefKind::Const + | DefKind::AssocConst + | DefKind::ConstParam, + _, + ) => {} // OK _ => bug!("unexpected pattern resolution: {:?}", res), } @@ -791,14 +792,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; let report_unexpected_res = |res: Res| { + let sm = tcx.sess.source_map(); + let path_str = sm + .span_to_snippet(sm.span_until_char(pat.span, '(')) + .map_or(String::new(), |s| format!(" `{}`", s.trim_end())); let msg = format!( - "expected tuple struct or tuple variant, found {} `{}`", + "expected tuple struct or tuple variant, found {}{}", res.descr(), - hir::print::to_string(&tcx.hir(), |s| s.print_qpath(qpath, false)), + path_str ); + let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{}", msg); - match (res, &pat.kind) { - (Res::Def(DefKind::Fn, _), _) | (Res::Def(DefKind::AssocFn, _), _) => { + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, _) => { err.span_label(pat.span, "`fn` calls are not allowed in patterns"); err.help( "for more information, visit \ @@ -835,7 +841,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { on_error(); return tcx.types.err; } - Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::AssocFn, _) => { + Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { report_unexpected_res(res); return tcx.types.err; } @@ -849,8 +855,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the tuple struct pattern against the expected type. let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); - let had_err = diag.is_some(); - diag.map(|mut err| err.emit()); + let had_err = if let Some(mut err) = diag { + err.emit(); + true + } else { + false + }; // Type-check subpatterns. if subpats.len() == variant.fields.len() @@ -1020,7 +1030,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(adt, substs) => (substs, adt), _ => span_bug!(pat.span, "struct pattern is not an ADT"), }; - let kind_name = adt.variant_descr(); // Index the struct fields' types. let field_map = variant @@ -1074,7 +1083,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !inexistent_fields.is_empty() && !variant.recovered { self.error_inexistent_fields( - kind_name, + adt.variant_descr(), &inexistent_fields, &mut unmentioned_fields, variant, @@ -1083,18 +1092,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Require `..` if struct has non_exhaustive attribute. if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc { - struct_span_err!( - tcx.sess, - pat.span, - E0638, - "`..` required with {} marked as non-exhaustive", - kind_name - ) - .emit(); + self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty()); } // Report an error if incorrect number of the fields were specified. - if kind_name == "union" { + if adt.is_union() { if fields.len() != 1 { tcx.sess .struct_span_err(pat.span, "union patterns should have exactly one field") @@ -1109,7 +1111,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { no_field_errors } - fn error_field_already_bound(&self, span: Span, ident: ast::Ident, other_field: Span) { + fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) { + let sess = self.tcx.sess; + let sm = sess.source_map(); + let sp_brace = sm.end_point(pat.span); + let sp_comma = sm.end_point(pat.span.with_hi(sp_brace.hi())); + let sugg = if no_fields || sp_brace != sp_comma { ".. }" } else { ", .. }" }; + + let mut err = struct_span_err!( + sess, + pat.span, + E0638, + "`..` required with {} marked as non-exhaustive", + descr + ); + err.span_suggestion_verbose( + sp_comma, + "add `..` at the end of the field list to ignore all other fields", + sugg.to_string(), + Applicability::MachineApplicable, + ); + err.emit(); + } + + fn error_field_already_bound(&self, span: Span, ident: Ident, other_field: Span) { struct_span_err!( self.tcx.sess, span, @@ -1125,8 +1150,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn error_inexistent_fields( &self, kind_name: &str, - inexistent_fields: &[ast::Ident], - unmentioned_fields: &mut Vec, + inexistent_fields: &[Ident], + unmentioned_fields: &mut Vec, variant: &ty::VariantDef, ) { let tcx = self.tcx; @@ -1201,7 +1226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn error_unmentioned_fields( &self, span: Span, - unmentioned_fields: &[ast::Ident], + unmentioned_fields: &[Ident], variant: &ty::VariantDef, ) { let field_names = if unmentioned_fields.len() == 1 { @@ -1335,77 +1360,92 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_bm: BindingMode, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { - let err = self.tcx.types.err; let expected = self.structurally_resolved_type(span, expected); - let (inner_ty, slice_ty, expected) = match expected.kind { + let (element_ty, opt_slice_ty, inferred) = match expected.kind { // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. - ty::Array(inner_ty, len) => { + ty::Array(element_ty, len) => { let min = before.len() as u64 + after.len() as u64; - let slice_ty = self - .check_array_pat_len(span, slice, len, min) - .map_or(err, |len| self.tcx.mk_array(inner_ty, len)); - (inner_ty, slice_ty, expected) + let (opt_slice_ty, expected) = + self.check_array_pat_len(span, element_ty, expected, slice, len, min); + // `opt_slice_ty.is_none()` => `slice.is_none()`. + // Note, though, that opt_slice_ty could be `Some(error_ty)`. + assert!(opt_slice_ty.is_some() || slice.is_none()); + (element_ty, opt_slice_ty, expected) } - ty::Slice(inner_ty) => (inner_ty, expected, expected), + ty::Slice(element_ty) => (element_ty, Some(expected), expected), // The expected type must be an array or slice, but was neither, so error. _ => { if !expected.references_error() { self.error_expected_array_or_slice(span, expected); } - (err, err, err) + let err = self.tcx.types.err; + (err, Some(err), err) } }; // Type check all the patterns before `slice`. for elt in before { - self.check_pat(&elt, inner_ty, def_bm, ti); + self.check_pat(&elt, element_ty, def_bm, ti); } // Type check the `slice`, if present, against its expected type. if let Some(slice) = slice { - self.check_pat(&slice, slice_ty, def_bm, ti); + self.check_pat(&slice, opt_slice_ty.unwrap(), def_bm, ti); } // Type check the elements after `slice`, if present. for elt in after { - self.check_pat(&elt, inner_ty, def_bm, ti); + self.check_pat(&elt, element_ty, def_bm, ti); } - expected + inferred } /// Type check the length of an array pattern. /// - /// Return the length of the variable length pattern, - /// if it exists and there are no errors. + /// Returns both the type of the variable length pattern (or `None`), and the potentially + /// inferred array type. We only return `None` for the slice type if `slice.is_none()`. fn check_array_pat_len( &self, span: Span, + element_ty: Ty<'tcx>, + arr_ty: Ty<'tcx>, slice: Option<&'tcx Pat<'tcx>>, len: &ty::Const<'tcx>, min_len: u64, - ) -> Option { + ) -> (Option>, Ty<'tcx>) { if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) { // Now we know the length... if slice.is_none() { // ...and since there is no variable-length pattern, // we require an exact match between the number of elements // in the array pattern and as provided by the matched type. - if min_len != len { - self.error_scrutinee_inconsistent_length(span, min_len, len); + if min_len == len { + return (None, arr_ty); } - } else if let r @ Some(_) = len.checked_sub(min_len) { + + self.error_scrutinee_inconsistent_length(span, min_len, len); + } else if let Some(pat_len) = len.checked_sub(min_len) { // The variable-length pattern was there, // so it has an array type with the remaining elements left as its size... - return r; + return (Some(self.tcx.mk_array(element_ty, pat_len)), arr_ty); } else { // ...however, in this case, there were no remaining elements. // That is, the slice pattern requires more than the array type offers. self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len); } + } else if slice.is_none() { + // We have a pattern with a fixed length, + // which we can use to infer the length of the array. + let updated_arr_ty = self.tcx.mk_array(element_ty, min_len); + self.demand_eqtype(span, updated_arr_ty, arr_ty); + return (None, updated_arr_ty); } else { - // No idea what the length is, which happens if we have e.g., - // `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`. + // We have a variable-length pattern and don't know the array length. + // This happens if we have e.g., + // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. self.error_scrutinee_unfixed_length(span); } - None + + // If we get here, we must have emitted an error. + (Some(self.tcx.types.err), arr_ty) } fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index a1345895689e0..049f4767247c5 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -76,15 +76,15 @@ use crate::check::dropck; use crate::check::FnCtxt; use crate::mem_categorization as mc; use crate::middle::region; -use rustc::ty::adjustment; -use rustc::ty::subst::{GenericArgKind, SubstsRef}; -use rustc::ty::{self, Ty}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, RegionObligation, RegionckMode}; +use rustc_middle::ty::adjustment; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_trait_selection::infer::OutlivesEnvironmentExt; use rustc_trait_selection::opaque_types::InferCtxtExt; @@ -180,7 +180,7 @@ pub struct RegionCtxt<'a, 'tcx> { // id of innermost fn body id body_id: hir::HirId, - body_owner: DefId, + body_owner: LocalDefId, // call_site scope of innermost fn call_site_scope: Option, @@ -189,7 +189,7 @@ pub struct RegionCtxt<'a, 'tcx> { repeating_scope: hir::HirId, // id of AST node being analyzed (the subject of the analysis). - subject_def_id: DefId, + subject_def_id: LocalDefId, } impl<'a, 'tcx> Deref for RegionCtxt<'a, 'tcx> { @@ -200,7 +200,7 @@ impl<'a, 'tcx> Deref for RegionCtxt<'a, 'tcx> { } pub struct RepeatingScope(hir::HirId); -pub struct Subject(DefId); +pub struct Subject(LocalDefId); impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { pub fn new( @@ -353,7 +353,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { ); self.fcx.resolve_regions_and_report_errors( - self.subject_def_id, + self.subject_def_id.to_def_id(), &self.region_scope_tree, &self.outlives_environment, mode, @@ -1105,19 +1105,21 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { /// itself the referent of a borrowed pointer. Let me give an /// example fragment of code to make clear(er) the situation: /// - /// let r: &'a mut T = ...; // the original reference "r" has lifetime 'a - /// ... - /// &'z *r // the reborrow has lifetime 'z + /// ```ignore (incomplete Rust code) + /// let r: &'a mut T = ...; // the original reference "r" has lifetime 'a + /// ... + /// &'z *r // the reborrow has lifetime 'z + /// ``` /// /// Now, in this case, our primary job is to add the inference /// constraint that `'z <= 'a`. Given this setup, let's clarify the /// parameters in (roughly) terms of the example: /// /// ```plain,ignore (pseudo-Rust) - /// A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T` - /// borrow_region ^~ ref_region ^~ - /// borrow_kind ^~ ref_kind ^~ - /// ref_cmt ^ + /// A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T` + /// borrow_region ^~ ref_region ^~ + /// borrow_kind ^~ ref_kind ^~ + /// ref_cmt ^ /// ``` /// /// Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc). @@ -1193,7 +1195,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { /// a `FnMut` or `Fn` closure. /// /// This function links the lifetimes of those references to the lifetime - /// of the borrow that's provided. See [link_reborrowed_region] for some + /// of the borrow that's provided. See [RegionCtxt::link_reborrowed_region] for some /// more explanation of this in the general case. /// /// We also supply a *cause*, and in this case we set the cause to @@ -1230,7 +1232,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { // reference to the closure. if let ty::Closure(_, substs) = ty.kind { match self.infcx.closure_kind(substs) { - Some(ty::ClosureKind::Fn) | Some(ty::ClosureKind::FnMut) => { + Some(ty::ClosureKind::Fn | ty::ClosureKind::FnMut) => { // Region of environment pointer let env_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion { scope: upvar_id.closure_expr_id.to_def_id(), diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index ff4e2cdab78d7..6aa8242193d5f 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -35,15 +35,14 @@ use super::FnCtxt; use crate::expr_use_visitor as euv; use crate::mem_categorization as mc; use crate::mem_categorization::PlaceBase; -use rustc::ty::{self, Ty, TyCtxt, UpvarSubsts}; -use rustc_ast::ast; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; -use rustc_span::Span; +use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; +use rustc_span::{Span, Symbol}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { @@ -147,7 +146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id()); - assert_eq!(body_owner_def_id, closure_def_id); + assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); let mut delegate = InferBorrowKind { fcx: self, closure_def_id, @@ -226,7 +225,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let upvar_ty = self.node_ty(var_hir_id); let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: closure_def_id.expect_local(), + closure_expr_id: closure_def_id, }; let capture = self.tables.borrow().upvar_capture(upvar_id); @@ -261,7 +260,7 @@ struct InferBorrowKind<'a, 'tcx> { // If we modified `current_closure_kind`, this field contains a `Some()` with the // variable access that caused us to do so. - current_origin: Option<(Span, ast::Name)>, + current_origin: Option<(Span, Symbol)>, // For each upvar that we access, we track the minimal kind of // access we need (ref, ref mut, move, etc). @@ -395,8 +394,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { ty::UpvarCapture::ByRef(mut upvar_borrow) => { match (upvar_borrow.kind, kind) { // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow) - | (ty::ImmBorrow, ty::MutBorrow) + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) | (ty::UniqueImmBorrow, ty::MutBorrow) => { upvar_borrow.kind = kind; self.adjust_upvar_captures @@ -404,8 +402,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } // Take LHS: (ty::ImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) + | (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow) | (ty::MutBorrow, _) => {} } } @@ -417,7 +414,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { closure_id: LocalDefId, new_kind: ty::ClosureKind, upvar_span: Span, - var_name: ast::Name, + var_name: Symbol, ) { debug!( "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})", @@ -440,14 +437,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { match (existing_kind, new_kind) { (ty::ClosureKind::Fn, ty::ClosureKind::Fn) - | (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) - | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) + | (ty::ClosureKind::FnMut, ty::ClosureKind::Fn | ty::ClosureKind::FnMut) | (ty::ClosureKind::FnOnce, _) => { // no change needed } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) - | (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { // new kind is stronger than the old kind self.current_closure_kind = new_kind; @@ -484,6 +479,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { } } -fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> ast::Name { +fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 27be0faade82f..b79ac50da8fa3 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -1,21 +1,21 @@ use crate::check::{FnCtxt, Inherited}; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; -use rustc::middle::lang_items; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::trait_def::TraitSpecializationKind; -use rustc::ty::{ - self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::itemlikevisit::ParItemLikeVisitor; +use rustc_hir::lang_items; use rustc_hir::ItemKind; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; +use rustc_middle::ty::trait_def::TraitSpecializationKind; +use rustc_middle::ty::{ + self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; use rustc_session::parse::feature_err; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_trait_selection::opaque_types::may_define_opaque_type; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -70,14 +70,14 @@ impl<'tcx> CheckWfFcxBuilder<'tcx> { /// 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 /// the types first. -pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); +pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let hir_id = tcx.hir().as_local_hir_id(def_id); let item = tcx.hir().expect_item(hir_id); debug!( "check_item_well_formed(it.hir_id={:?}, it.name={})", item.hir_id, - tcx.def_path_str(def_id) + tcx.def_path_str(def_id.to_def_id()) ); match item.kind { @@ -124,18 +124,16 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) { } (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { // FIXME(#27579): what amount of WF checking do we need for neg impls? - if let (Some(of_trait), false) = (of_trait, is_auto) { + if let hir::Defaultness::Default { .. } = defaultness { + let mut spans = vec![span]; + spans.extend(defaultness_span); struct_span_err!( tcx.sess, - span.to(of_trait.path.span), - E0192, - "invalid negative impl" - ) - .note( - "negative impls are only allowed for auto traits, like `Send` and \ - `Sync`", + spans, + E0750, + "negative impls cannot be default impls" ) - .emit() + .emit(); } } (ty::ImplPolarity::Reservation, _) => { @@ -185,8 +183,8 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) { } } -pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); +pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let hir_id = tcx.hir().as_local_hir_id(def_id); let trait_item = tcx.hir().expect_trait_item(hir_id); let method_sig = match trait_item.kind { @@ -197,10 +195,10 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) { check_associated_item(tcx, trait_item.hir_id, trait_item.span, method_sig); } -fn could_be_self(trait_def_id: DefId, ty: &hir::Ty<'_>) -> bool { +fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { match ty.kind { hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments { - [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id), + [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()), _ => false, }, _ => false, @@ -259,8 +257,8 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem } } -pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); +pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let hir_id = tcx.hir().as_local_hir_id(def_id); let impl_item = tcx.hir().expect_impl_item(hir_id); let method_sig = match impl_item.kind { @@ -296,7 +294,7 @@ fn check_associated_item( let ty = fcx.normalize_associated_types_in(span, &ty); fcx.register_wf_obligation(ty, span, code.clone()); } - ty::AssocKind::Method => { + ty::AssocKind::Fn => { let sig = fcx.tcx.fn_sig(item.def_id); let sig = fcx.normalize_associated_types_in(span, &sig); let hir_sig = sig_if_method.expect("bad signature for method"); @@ -332,12 +330,12 @@ fn for_item<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'_>) -> CheckWfFcxBuilder< } fn for_id(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) -> CheckWfFcxBuilder<'_> { - let def_id = tcx.hir().local_def_id(id).expect_local(); + let def_id = tcx.hir().local_def_id(id); CheckWfFcxBuilder { inherited: Inherited::build(tcx, def_id), id, span, - param_env: tcx.param_env(def_id.to_def_id()), + param_env: tcx.param_env(def_id), } } @@ -371,7 +369,7 @@ fn check_type_defn<'tcx, F>( packed && { let ty = variant.fields.last().unwrap().ty; let ty = fcx.tcx.erase_regions(&ty); - if ty.has_local_value() { + if ty.needs_infer() { fcx_tcx .sess .delay_span_bug(item.span, &format!("inference variables in {:?}", ty)); @@ -413,9 +411,26 @@ fn check_type_defn<'tcx, F>( ObligationCauseCode::MiscObligation, ) } + + // Explicit `enum` discriminant values must const-evaluate successfully. + if let Some(discr_def_id) = variant.explicit_discr { + let discr_substs = + InternalSubsts::identity_for_item(fcx.tcx, discr_def_id.to_def_id()); + + let cause = traits::ObligationCause::new( + fcx.tcx.def_span(discr_def_id), + fcx.body_id, + traits::MiscObligation, + ); + fcx.register_predicate(traits::Obligation::new( + cause, + fcx.param_env, + ty::Predicate::ConstEvaluatable(discr_def_id.to_def_id(), discr_substs), + )); + } } - check_where_clauses(tcx, fcx, item.span, def_id, None); + check_where_clauses(tcx, fcx, item.span, def_id.to_def_id(), None); // No implied bounds in a struct definition. vec![] @@ -443,8 +458,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { } for_item(tcx, item).with_fcx(|fcx, _| { - check_where_clauses(tcx, fcx, item.span, trait_def_id, None); - check_associated_type_defaults(fcx, trait_def_id); + check_where_clauses(tcx, fcx, item.span, trait_def_id.to_def_id(), None); + check_associated_type_defaults(fcx, trait_def_id.to_def_id()); vec![] }); @@ -557,7 +572,15 @@ fn check_item_fn(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { ItemKind::Fn(sig, ..) => sig, _ => bug!("expected `ItemKind::Fn`, found `{:?}`", item.kind), }; - check_fn_or_method(tcx, fcx, item.ident.span, sig, hir_sig, def_id, &mut implied_bounds); + check_fn_or_method( + tcx, + fcx, + item.ident.span, + sig, + hir_sig, + def_id.to_def_id(), + &mut implied_bounds, + ); implied_bounds }) } @@ -633,9 +656,9 @@ fn check_impl<'tcx>( } } - check_where_clauses(tcx, fcx, item.span, item_def_id, None); + check_where_clauses(tcx, fcx, item.span, item_def_id.to_def_id(), None); - fcx.impl_implied_bounds(item_def_id, item.span) + fcx.impl_implied_bounds(item_def_id.to_def_id(), item.span) }); } @@ -708,13 +731,13 @@ fn check_where_clauses<'tcx, 'fcx>( return default_ty.into(); } } - // Mark unwanted params as error. - fcx.tcx.types.err.into() + + fcx.tcx.mk_param_from_def(param) } GenericParamDefKind::Const => { // FIXME(const_generics:defaults) - fcx.tcx.consts.err.into() + fcx.tcx.mk_param_from_def(param) } } }); @@ -752,7 +775,10 @@ fn check_where_clauses<'tcx, 'fcx>( let substituted_pred = pred.subst(fcx.tcx, substs); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. - if substituted_pred.references_error() || param_count.params.len() > 1 || has_region { + if substituted_pred.has_param_types_or_consts() + || param_count.params.len() > 1 + || has_region + { None } else if predicates.predicates.iter().any(|&(p, _)| p == substituted_pred) { // Avoid duplication of predicates that contain no parameters, for example. @@ -780,7 +806,7 @@ fn check_where_clauses<'tcx, 'fcx>( let mut predicates = predicates.instantiate_identity(fcx.tcx); if let Some((return_ty, span)) = return_ty { - let opaque_types = check_opaque_types(tcx, fcx, def_id, span, return_ty); + let opaque_types = check_opaque_types(tcx, fcx, def_id.expect_local(), span, return_ty); for _ in 0..opaque_types.len() { predicates.spans.push(span); } @@ -853,7 +879,7 @@ fn check_fn_or_method<'fcx, 'tcx>( fn check_opaque_types<'fcx, 'tcx>( tcx: TyCtxt<'tcx>, fcx: &FnCtxt<'fcx, 'tcx>, - fn_def_id: DefId, + fn_def_id: LocalDefId, span: Span, ty: Ty<'tcx>, ) -> Vec> { @@ -866,87 +892,77 @@ fn check_opaque_types<'fcx, 'tcx>( trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs); let generics = tcx.generics_of(def_id); // Only check named `impl Trait` types defined in this crate. + // FIXME(eddyb) is `generics.parent.is_none()` correct? It seems + // potentially risky wrt associated types in `impl`s. if generics.parent.is_none() && def_id.is_local() { - let opaque_hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let opaque_hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); if may_define_opaque_type(tcx, fn_def_id, opaque_hir_id) { trace!("check_opaque_types: may define, generics={:#?}", generics); - let mut seen: FxHashMap<_, Vec<_>> = FxHashMap::default(); - for (subst, param) in substs.iter().zip(&generics.params) { - match subst.unpack() { - ty::subst::GenericArgKind::Type(ty) => match ty.kind { - ty::Param(..) => {} - // Prevent `fn foo() -> Foo` from being defining. - _ => { - tcx.sess - .struct_span_err( - span, - "non-defining opaque type use \ - in defining scope", - ) - .span_note( - tcx.def_span(param.def_id), - &format!( - "used non-generic type {} for \ - generic parameter", - ty, - ), - ) - .emit(); - } - }, + let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); + for (i, &arg) in substs.iter().enumerate() { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), - ty::subst::GenericArgKind::Lifetime(region) => { - let param_span = tcx.def_span(param.def_id); + GenericArgKind::Lifetime(region) => { if let ty::ReStatic = region { tcx.sess .struct_span_err( span, - "non-defining opaque type use \ - in defining scope", + "non-defining opaque type use in defining scope", ) .span_label( - param_span, + tcx.def_span(generics.param_at(i, tcx).def_id), "cannot use static lifetime; use a bound lifetime \ - instead or remove the lifetime parameter from the \ - opaque type", + instead or remove the lifetime parameter from the \ + opaque type", ) .emit(); - } else { - seen.entry(region).or_default().push(param_span); + continue; } + + true } - ty::subst::GenericArgKind::Const(ct) => match ct.val { - ty::ConstKind::Param(_) => {} - _ => { - tcx.sess - .struct_span_err( - span, - "non-defining opaque type use \ - in defining scope", - ) - .span_note( - tcx.def_span(param.def_id), - &format!( - "used non-generic const {} for \ - generic parameter", - ty, - ), - ) - .emit(); - } - }, - } // match subst - } // for (subst, param) - for (_, spans) in seen { - if spans.len() > 1 { + GenericArgKind::Const(ct) => { + matches!(ct.val, ty::ConstKind::Param(_)) + } + }; + + if arg_is_param { + seen_params.entry(arg).or_default().push(i); + } else { + // Prevent `fn foo() -> Foo` from being defining. + let opaque_param = generics.param_at(i, tcx); tcx.sess .struct_span_err( span, - "non-defining opaque type use \ - in defining scope", + "non-defining opaque type use in defining scope", ) - .span_note(spans, "lifetime used multiple times") + .span_note( + tcx.def_span(opaque_param.def_id), + &format!( + "used non-generic {} `{}` for generic parameter", + opaque_param.kind.descr(), + arg, + ), + ) + .emit(); + } + } // for (arg, param) + + for (_, indices) in seen_params { + if indices.len() > 1 { + let descr = generics.param_at(indices[0], tcx).kind.descr(); + let spans: Vec<_> = indices + .into_iter() + .map(|i| tcx.def_span(generics.param_at(i, tcx).def_id)) + .collect(); + tcx.sess + .struct_span_err( + span, + "non-defining opaque type use in defining scope", + ) + .span_note(spans, &format!("{} used multiple times", descr)) .emit(); } } @@ -994,7 +1010,7 @@ fn check_method_receiver<'fcx, 'tcx>( // Check that the method has a valid receiver type, given the type `Self`. debug!("check_method_receiver({:?}, self_ty={:?})", method, self_ty); - if !method.method_has_self_argument { + if !method.fn_has_self_parameter { return; } @@ -1030,7 +1046,7 @@ fn check_method_receiver<'fcx, 'tcx>( span, &format!( "`{}` cannot be used as the type of `self` without \ - the `arbitrary_self_types` feature", + the `arbitrary_self_types` feature", receiver_ty, ), ) @@ -1210,7 +1226,7 @@ fn check_variances_for_type_defn<'tcx>( } } -fn report_bivariance(tcx: TyCtxt<'_>, span: Span, param_name: ast::Name) { +fn report_bivariance(tcx: TyCtxt<'_>, span: Span, param_name: Symbol) { let mut err = error_392(tcx, span, param_name); let suggested_marker_id = tcx.lang_items().phantom_data(); @@ -1234,11 +1250,12 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, span: Span, id: hir::HirId) { let empty_env = ty::ParamEnv::empty(); let def_id = fcx.tcx.hir().local_def_id(id); - let predicates = fcx.tcx.predicates_of(def_id).predicates.iter().map(|(p, _)| *p).collect(); + let predicates = fcx.tcx.predicates_of(def_id).predicates.iter().map(|(p, _)| *p); // Check elaborated bounds. let implied_obligations = traits::elaborate_predicates(fcx.tcx, predicates); - for pred in implied_obligations { + for obligation in implied_obligations { + let pred = obligation.predicate; // Match the existing behavior. if pred.is_global() && !pred.has_late_bound_regions() { let pred = fcx.normalize_associated_types_in(span, &pred); @@ -1287,8 +1304,14 @@ impl ParItemLikeVisitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> { /////////////////////////////////////////////////////////////////////////// // ADT +// FIXME(eddyb) replace this with getting fields/discriminants through `ty::AdtDef`. struct AdtVariant<'tcx> { + /// Types of fields in the variant, that must be well-formed. fields: Vec>, + + /// Explicit discriminant of this variant (e.g. `A = 123`), + /// that must evaluate to a constant value. + explicit_discr: Option, } struct AdtField<'tcx> { @@ -1297,6 +1320,7 @@ struct AdtField<'tcx> { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + // FIXME(eddyb) replace this with getting fields through `ty::AdtDef`. fn non_enum_variant(&self, struct_def: &hir::VariantData<'_>) -> AdtVariant<'tcx> { let fields = struct_def .fields() @@ -1309,11 +1333,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { AdtField { ty: field_ty, span: field.span } }) .collect(); - AdtVariant { fields } + AdtVariant { fields, explicit_discr: None } } fn enum_variants(&self, enum_def: &hir::EnumDef<'_>) -> Vec> { - enum_def.variants.iter().map(|variant| self.non_enum_variant(&variant.data)).collect() + enum_def + .variants + .iter() + .map(|variant| AdtVariant { + fields: self.non_enum_variant(&variant.data).fields, + explicit_discr: variant + .disr_expr + .map(|explicit_discr| self.tcx.hir().local_def_id(explicit_discr.hir_id)), + }) + .collect() } fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec> { @@ -1335,7 +1368,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } -fn error_392(tcx: TyCtxt<'_>, span: Span, param_name: ast::Name) -> DiagnosticBuilder<'_> { +fn error_392(tcx: TyCtxt<'_>, span: Span, param_name: Symbol) -> DiagnosticBuilder<'_> { let mut err = struct_span_err!(tcx.sess, span, E0392, "parameter `{}` is never used", param_name); err.span_label(span, "unused parameter"); diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 65f81ef033dd7..154ca391aa5fd 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -4,15 +4,16 @@ use crate::check::FnCtxt; -use rustc::ty::adjustment::{Adjust, Adjustment, PointerCast}; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::DefIdSet; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::opaque_types::InferCtxtExt; @@ -40,8 +41,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let item_def_id = self.tcx.hir().local_def_id(item_id); // This attribute causes us to dump some writeback information - // in the form of errors, which is uSymbolfor unit tests. - let rustc_dump_user_substs = self.tcx.has_attr(item_def_id, sym::rustc_dump_user_substs); + // in the form of errors, which is uSymbol for unit tests. + let rustc_dump_user_substs = + self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs); let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_substs); for param in body.params { @@ -75,7 +77,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.tables.upvar_list = mem::replace(&mut self.tables.borrow_mut().upvar_list, Default::default()); - wbcx.tables.tainted_by_errors = self.is_tainted_by_errors(); + if self.is_tainted_by_errors() { + // FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted. + wbcx.tables.tainted_by_errors = Some(ErrorReported); + } debug!("writeback: tables for {:?} are {:#?}", item_def_id, wbcx.tables); @@ -133,8 +138,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // operating on scalars, we clear the overload. fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr<'_>) { match e.kind { - hir::ExprKind::Unary(hir::UnOp::UnNeg, ref inner) - | hir::ExprKind::Unary(hir::UnOp::UnNot, ref inner) => { + hir::ExprKind::Unary(hir::UnOp::UnNeg | hir::UnOp::UnNot, ref inner) => { let inner_ty = self.fcx.node_ty(inner.hir_id); let inner_ty = self.fcx.resolve_vars_if_possible(&inner_ty); @@ -161,12 +165,18 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { hir::ExprKind::Binary(..) => { if !op.node.is_by_value() { let mut adjustments = tables.adjustments_mut(); - adjustments.get_mut(lhs.hir_id).map(|a| a.pop()); - adjustments.get_mut(rhs.hir_id).map(|a| a.pop()); + if let Some(a) = adjustments.get_mut(lhs.hir_id) { + a.pop(); + } + if let Some(a) = adjustments.get_mut(rhs.hir_id) { + a.pop(); + } } } hir::ExprKind::AssignOp(..) => { - tables.adjustments_mut().get_mut(lhs.hir_id).map(|a| a.pop()); + if let Some(a) = tables.adjustments_mut().get_mut(lhs.hir_id) { + a.pop(); + } } _ => {} } @@ -211,22 +221,21 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { tables.type_dependent_defs_mut().remove(e.hir_id); tables.node_substs_mut().remove(e.hir_id); - tables.adjustments_mut().get_mut(base.hir_id).map(|a| { + if let Some(a) = tables.adjustments_mut().get_mut(base.hir_id) { // Discard the need for a mutable borrow - match a.pop() { - // Extra adjustment made when indexing causes a drop - // of size information - we need to get rid of it - // Since this is "after" the other adjustment to be - // discarded, we do an extra `pop()` - Some(Adjustment { - kind: Adjust::Pointer(PointerCast::Unsize), .. - }) => { - // So the borrow discard actually happens here - a.pop(); - } - _ => {} + + // Extra adjustment made when indexing causes a drop + // of size information - we need to get rid of it + // Since this is "after" the other adjustment to be + // discarded, we do an extra `pop()` + if let Some(Adjustment { + kind: Adjust::Pointer(PointerCast::Unsize), .. + }) = a.pop() + { + // So the borrow discard actually happens here + a.pop(); } - }); + } } } } @@ -366,11 +375,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for (&local_id, c_ty) in fcx_tables.user_provided_types().iter() { let hir_id = hir::HirId { owner: common_hir_owner, local_id }; - if cfg!(debug_assertions) && c_ty.has_local_value() { - span_bug!(hir_id.to_span(self.fcx.tcx), "writeback: `{:?}` is a local value", c_ty); + if cfg!(debug_assertions) && c_ty.needs_infer() { + span_bug!( + hir_id.to_span(self.fcx.tcx), + "writeback: `{:?}` has inference variables", + c_ty + ); }; - self.tables.user_provided_types_mut().insert(hir_id, c_ty.clone()); + self.tables.user_provided_types_mut().insert(hir_id, *c_ty); if let ty::UserType::TypeOf(_, user_substs) = c_ty.value { if self.rustc_dump_user_substs { @@ -400,15 +413,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); for (&def_id, c_sig) in fcx_tables.user_provided_sigs.iter() { - if cfg!(debug_assertions) && c_sig.has_local_value() { + if cfg!(debug_assertions) && c_sig.needs_infer() { span_bug!( self.fcx.tcx.hir().span_if_local(def_id).unwrap(), - "writeback: `{:?}` is a local value", + "writeback: `{:?}` has inference variables", c_sig ); }; - self.tables.user_provided_sigs.insert(def_id, c_sig.clone()); + self.tables.user_provided_sigs.insert(def_id, *c_sig); } } @@ -420,7 +433,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_opaque_types(&mut self, span: Span) { for (&def_id, opaque_defn) in self.fcx.opaque_types.borrow().iter() { - let hir_id = self.tcx().hir().as_local_hir_id(def_id).unwrap(); + let hir_id = self.tcx().hir().as_local_hir_id(def_id.expect_local()); let instantiated_ty = self.resolve(&opaque_defn.concrete_ty, &hir_id); debug_assert!(!instantiated_ty.has_escaping_bound_vars()); @@ -458,7 +471,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } - if !opaque_defn.substs.has_local_value() { + if !opaque_defn.substs.needs_infer() { // We only want to add an entry into `concrete_opaque_types` // if we actually found a defining usage of this opaque type. // Otherwise, we do nothing - we'll either find a defining usage @@ -486,7 +499,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } } else { - self.tcx().sess.delay_span_bug(span, "`opaque_defn` is a local value"); + self.tcx().sess.delay_span_bug(span, "`opaque_defn` has inference variables"); } } } @@ -559,7 +572,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for (&local_id, fn_sig) in fcx_tables.liberated_fn_sigs().iter() { let hir_id = hir::HirId { owner: common_hir_owner, local_id }; let fn_sig = self.resolve(fn_sig, &hir_id); - self.tables.liberated_fn_sigs_mut().insert(hir_id, fn_sig.clone()); + self.tables.liberated_fn_sigs_mut().insert(hir_id, fn_sig); } } @@ -575,14 +588,24 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } - fn resolve(&self, x: &T, span: &dyn Locatable) -> T + fn resolve(&mut self, x: &T, span: &dyn Locatable) -> T where T: TypeFoldable<'tcx>, { - let x = x.fold_with(&mut Resolver::new(self.fcx, span, self.body)); - if cfg!(debug_assertions) && x.has_local_value() { - span_bug!(span.to_span(self.fcx.tcx), "writeback: `{:?}` is a local value", x); + let mut resolver = Resolver::new(self.fcx, span, self.body); + let x = x.fold_with(&mut resolver); + if cfg!(debug_assertions) && x.needs_infer() { + span_bug!(span.to_span(self.fcx.tcx), "writeback: `{:?}` has inference variables", x); } + + // We may have introduced e.g. `ty::Error`, if inference failed, make sure + // to mark the `TypeckTables` as tainted in that case, so that downstream + // users of the tables don't produce extra errors, or worse, ICEs. + if resolver.replaced_with_error { + // FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted. + self.tables.tainted_by_errors = Some(ErrorReported); + } + x } } @@ -610,6 +633,9 @@ struct Resolver<'cx, 'tcx> { infcx: &'cx InferCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, + + /// Set to `true` if any `Ty` or `ty::Const` had to be replaced with an `Error`. + replaced_with_error: bool, } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { @@ -618,16 +644,29 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, ) -> Resolver<'cx, 'tcx> { - Resolver { tcx: fcx.tcx, infcx: fcx, span, body } + Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false } } - fn report_error(&self, t: Ty<'tcx>) { + fn report_type_error(&self, t: Ty<'tcx>) { if !self.tcx.sess.has_errors() { self.infcx .need_type_info_err(Some(self.body.id()), self.span.to_span(self.tcx), t, E0282) .emit(); } } + + fn report_const_error(&self, c: &'tcx ty::Const<'tcx>) { + if !self.tcx.sess.has_errors() { + self.infcx + .need_type_info_err_const( + Some(self.body.id()), + self.span.to_span(self.tcx), + c, + E0282, + ) + .emit(); + } + } } impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { @@ -640,7 +679,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { Ok(t) => self.infcx.tcx.erase_regions(&t), Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_error(t); + self.report_type_error(t); + self.replaced_with_error = true; self.tcx().types.err } } @@ -656,9 +696,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { Ok(ct) => self.infcx.tcx.erase_regions(&ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); - // FIXME: we'd like to use `self.report_error`, but it doesn't yet - // accept a &'tcx ty::Const. - self.tcx().consts.err + self.report_const_error(ct); + self.replaced_with_error = true; + self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty: ct.ty }) } } } diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs index d0414af5b2138..dfe86aecbf727 100644 --- a/src/librustc_typeck/check_unused.rs +++ b/src/librustc_typeck/check_unused.rs @@ -1,13 +1,11 @@ -use rustc::ty::TyCtxt; -use rustc_ast::ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::print::visibility_qualified; +use rustc_middle::ty::TyCtxt; use rustc_session::lint; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; pub fn check_crate(tcx: TyCtxt<'_>) { let mut used_trait_imports = DefIdSet::default(); @@ -51,7 +49,7 @@ impl CheckVisitor<'tcx> { return; } - if self.used_trait_imports.contains(&def_id) { + if self.used_trait_imports.contains(&def_id.to_def_id()) { return; } @@ -90,10 +88,8 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { // Note that if we carry through to the `extern_mod_stmt_cnum` query // below it'll cause a panic because `def_id` is actually bogus at this // point in time otherwise. - if let Some(id) = tcx.hir().as_local_hir_id(def_id) { - if tcx.hir().find(id).is_none() { - return false; - } + if tcx.hir().find(tcx.hir().as_local_hir_id(def_id.expect_local())).is_none() { + return false; } true }) @@ -116,7 +112,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { }); for extern_crate in &crates_to_lint { - let id = tcx.hir().as_local_hir_id(extern_crate.def_id).unwrap(); + let id = tcx.hir().as_local_hir_id(extern_crate.def_id.expect_local()); let item = tcx.hir().expect_item(id); // If the crate is fully unused, we suggest removing it altogether. @@ -176,16 +172,13 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), None => format!("use {};", item.ident.name), }; - - let replacement = visibility_qualified(&item.vis, base_replacement); - let msg = "`extern crate` is not idiomatic in the new edition"; - let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use")); - - lint.build(msg) + let vis = tcx.sess.source_map().span_to_snippet(item.vis.span).unwrap_or_default(); + let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; + lint.build("`extern crate` is not idiomatic in the new edition") .span_suggestion_short( extern_crate.span, - &help, - replacement, + &format!("convert it to a `{}`", add_vis("use".to_string())), + add_vis(base_replacement), Applicability::MachineApplicable, ) .emit(); @@ -208,7 +201,7 @@ struct ExternCrateToLint { /// if `Some`, then this is renamed (`extern crate orig_name as /// crate_name`), and -- perhaps surprisingly -- this stores the /// *original* name (`item.name` will contain the new name) - orig_name: Option, + orig_name: Option, /// if `false`, the original name started with `_`, so we shouldn't lint /// about it going unused (but we should still emit idiom lints). @@ -220,7 +213,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CollectExternCrateVisitor<'a, 'tcx> { if let hir::ItemKind::ExternCrate(orig_name) = item.kind { let extern_crate_def_id = self.tcx.hir().local_def_id(item.hir_id); self.crates_to_lint.push(ExternCrateToLint { - def_id: extern_crate_def_id, + def_id: extern_crate_def_id.to_def_id(), span: item.span, orig_name, warn_if_unused: !item.ident.as_str().starts_with('_'), diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index d0a87e240da89..efa3cd9955b49 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -1,18 +1,20 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. -use rustc::middle::lang_items::UnsizeTraitLangItem; -use rustc::middle::region; -use rustc::ty::adjustment::CoerceUnsizedInfo; -use rustc::ty::TypeFoldable; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::{ + CoerceUnsizedTraitLangItem, DispatchFromDynTraitLangItem, UnsizeTraitLangItem, +}; use rustc_hir::ItemKind; use rustc_infer::infer; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionckMode, TyCtxtInferExt}; +use rustc_middle::middle::region; +use rustc_middle::ty::adjustment::CoerceUnsizedInfo; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; use rustc_trait_selection::traits::predicate_for_trait_def; @@ -35,7 +37,7 @@ struct Checker<'tcx> { impl<'tcx> Checker<'tcx> { fn check(&self, trait_def_id: Option, mut f: F) -> &Self where - F: FnMut(TyCtxt<'tcx>, DefId), + F: FnMut(TyCtxt<'tcx>, LocalDefId), { if Some(self.trait_def_id) == trait_def_id { for &impl_id in self.tcx.hir().trait_impls(self.trait_def_id) { @@ -47,13 +49,13 @@ impl<'tcx> Checker<'tcx> { } } -fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: DefId) { +fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { // Destructors only work on nominal types. if let ty::Adt(..) | ty::Error = tcx.type_of(impl_did).kind { return; } - let impl_hir_id = tcx.hir().as_local_hir_id(impl_did).expect("foreign Drop impl on non-ADT"); + let impl_hir_id = tcx.hir().as_local_hir_id(impl_did); let sp = match tcx.hir().expect_item(impl_hir_id).kind { ItemKind::Impl { self_ty, .. } => self_ty.span, _ => bug!("expected Drop impl item"), @@ -69,15 +71,10 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: DefId) { .emit(); } -fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: DefId) { +fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); - let impl_hir_id = if let Some(n) = tcx.hir().as_local_hir_id(impl_did) { - n - } else { - debug!("visit_implementation_of_copy(): impl not in this crate"); - return; - }; + let impl_hir_id = tcx.hir().as_local_hir_id(impl_did); let self_type = tcx.type_of(impl_did); debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); @@ -137,209 +134,205 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: DefId) { } } -fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'tcx>, impl_did: DefId) { +fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); // Just compute this for the side-effects, in particular reporting // errors; other parts of the code may demand it for the info of // course. - if impl_did.is_local() { - let span = tcx.def_span(impl_did); - tcx.at(span).coerce_unsized_info(impl_did); - } + let span = tcx.def_span(impl_did); + tcx.at(span).coerce_unsized_info(impl_did); } -fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: DefId) { +fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) { debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); - if impl_did.is_local() { - let dispatch_from_dyn_trait = tcx.lang_items().dispatch_from_dyn_trait().unwrap(); - let impl_hir_id = tcx.hir().as_local_hir_id(impl_did).unwrap(); - let span = tcx.hir().span(impl_hir_id); + let impl_hir_id = tcx.hir().as_local_hir_id(impl_did); + let span = tcx.hir().span(impl_hir_id); - let source = tcx.type_of(impl_did); - assert!(!source.has_escaping_bound_vars()); - let target = { - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); - assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); + let dispatch_from_dyn_trait = tcx.require_lang_item(DispatchFromDynTraitLangItem, Some(span)); - trait_ref.substs.type_at(1) - }; + let source = tcx.type_of(impl_did); + assert!(!source.has_escaping_bound_vars()); + let target = { + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); + assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); - debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); + trait_ref.substs.type_at(1) + }; - let param_env = tcx.param_env(impl_did); + debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); - let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg); + let param_env = tcx.param_env(impl_did); - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::misc(span, impl_hir_id); + let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg); - use ty::TyKind::*; - match (&source.kind, &target.kind) { - (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) - if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {} - (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), - (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) - if def_a.is_struct() && def_b.is_struct() => - { - if def_a != def_b { - let source_path = tcx.def_path_str(def_a.did); - let target_path = tcx.def_path_str(def_b.did); + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::misc(span, impl_hir_id); - create_err(&format!( - "the trait `DispatchFromDyn` may only be implemented \ + use ty::TyKind::*; + match (&source.kind, &target.kind) { + (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) + if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {} + (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), + (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did); + let target_path = tcx.def_path_str(def_b.did); + + create_err(&format!( + "the trait `DispatchFromDyn` may only be implemented \ for a coercion between structures with the same \ definition; expected `{}`, found `{}`", - source_path, target_path, - )) - .emit(); + source_path, target_path, + )) + .emit(); - return; - } + return; + } - if def_a.repr.c() || def_a.repr.packed() { - create_err( - "structs implementing `DispatchFromDyn` may not have \ + if def_a.repr.c() || def_a.repr.packed() { + create_err( + "structs implementing `DispatchFromDyn` may not have \ `#[repr(packed)]` or `#[repr(C)]`", - ) - .emit(); - } + ) + .emit(); + } - let fields = &def_a.non_enum_variant().fields; + let fields = &def_a.non_enum_variant().fields; - let coerced_fields = fields - .iter() - .filter_map(|field| { - let ty_a = field.ty(tcx, substs_a); - let ty_b = field.ty(tcx, substs_b); + let coerced_fields = fields + .iter() + .filter_map(|field| { + let ty_a = field.ty(tcx, substs_a); + let ty_b = field.ty(tcx, substs_b); - if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { - if layout.is_zst() && layout.details.align.abi.bytes() == 1 { - // ignore ZST fields with alignment of 1 byte - return None; - } + if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { + if layout.is_zst() && layout.align.abi.bytes() == 1 { + // ignore ZST fields with alignment of 1 byte + return None; } + } - if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { - if ok.obligations.is_empty() { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ + if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { + if ok.obligations.is_empty() { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ for structs containing the field being coerced, \ ZST fields with 1 byte alignment, and nothing else", - ) - .note(&format!( - "extra field `{}` of type `{}` is not allowed", - field.ident, ty_a, - )) - .emit(); - - return None; - } + ) + .note(&format!( + "extra field `{}` of type `{}` is not allowed", + field.ident, ty_a, + )) + .emit(); + + return None; } + } - Some(field) - }) - .collect::>(); + Some(field) + }) + .collect::>(); - if coerced_fields.is_empty() { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ + if coerced_fields.is_empty() { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ for a coercion between structures with a single field \ being coerced, none found", - ) - .emit(); - } else if coerced_fields.len() > 1 { - create_err( - "implementing the `DispatchFromDyn` trait requires multiple coercions", - ) - .note( - "the trait `DispatchFromDyn` may only be implemented \ + ) + .emit(); + } else if coerced_fields.len() > 1 { + create_err( + "implementing the `DispatchFromDyn` trait requires multiple coercions", + ) + .note( + "the trait `DispatchFromDyn` may only be implemented \ for a coercion between structures with a single field \ being coerced", - ) - .note(&format!( - "currently, {} fields need coercions: {}", - coerced_fields.len(), - coerced_fields - .iter() - .map(|field| { - format!( - "`{}` (`{}` to `{}`)", - field.ident, - field.ty(tcx, substs_a), - field.ty(tcx, substs_b), - ) - }) - .collect::>() - .join(", ") - )) - .emit(); - } else { - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - - for field in coerced_fields { - let predicate = predicate_for_trait_def( - tcx, - param_env, - cause.clone(), - dispatch_from_dyn_trait, - 0, - field.ty(tcx, substs_a), - &[field.ty(tcx, substs_b).into()], - ); - - fulfill_cx.register_predicate_obligation(&infcx, predicate); - } + ) + .note(&format!( + "currently, {} fields need coercions: {}", + coerced_fields.len(), + coerced_fields + .iter() + .map(|field| { + format!( + "`{}` (`{}` to `{}`)", + field.ident, + field.ty(tcx, substs_a), + field.ty(tcx, substs_b), + ) + }) + .collect::>() + .join(", ") + )) + .emit(); + } else { + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + + for field in coerced_fields { + let predicate = predicate_for_trait_def( + tcx, + param_env, + cause.clone(), + dispatch_from_dyn_trait, + 0, + field.ty(tcx, substs_a), + &[field.ty(tcx, substs_b).into()], + ); - // Check that all transitive obligations are satisfied. - if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(&errors, None, false); - } + fulfill_cx.register_predicate_obligation(&infcx, predicate); + } - // Finally, resolve all regions. - let region_scope_tree = region::ScopeTree::default(); - let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors( - impl_did, - ®ion_scope_tree, - &outlives_env, - RegionckMode::default(), - ); + // Check that all transitive obligations are satisfied. + if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { + infcx.report_fulfillment_errors(&errors, None, false); } + + // Finally, resolve all regions. + let region_scope_tree = region::ScopeTree::default(); + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors( + impl_did.to_def_id(), + ®ion_scope_tree, + &outlives_env, + RegionckMode::default(), + ); } - _ => { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ + } + _ => { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ for a coercion between structures", - ) - .emit(); - } + ) + .emit(); } - }) - } + } + }) } pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); - let coerce_unsized_trait = tcx.lang_items().coerce_unsized_trait().unwrap(); + + // this provider should only get invoked for local def-ids + let impl_hir_id = tcx.hir().as_local_hir_id(impl_did.expect_local()); + let span = tcx.hir().span(impl_hir_id); + + let coerce_unsized_trait = tcx.require_lang_item(CoerceUnsizedTraitLangItem, Some(span)); let unsize_trait = tcx.lang_items().require(UnsizeTraitLangItem).unwrap_or_else(|err| { tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err)); }); - // this provider should only get invoked for local def-ids - let impl_hir_id = tcx.hir().as_local_hir_id(impl_did).unwrap_or_else(|| { - bug!("coerce_unsized_info: invoked for non-local def-id {:?}", impl_did) - }); - let source = tcx.type_of(impl_did); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); assert_eq!(trait_ref.def_id, coerce_unsized_trait); let target = trait_ref.substs.type_at(1); debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); - let span = tcx.hir().span(impl_hir_id); let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index c6ee9ab60abf3..653b7b8f2a5ba 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -7,23 +7,23 @@ //! `tcx.inherent_impls(def_id)`). That value, however, //! is computed by selecting an idea from this table. -use rustc::ty::{self, CrateInherentImpls, TyCtxt}; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::{self, CrateInherentImpls, TyCtxt}; use rustc_ast::ast; use rustc_span::Span; /// On-demand query: yields a map containing all types mapped to their inherent impls. -pub fn crate_inherent_impls(tcx: TyCtxt<'_>, crate_num: CrateNum) -> &CrateInherentImpls { +pub fn crate_inherent_impls(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateInherentImpls { assert_eq!(crate_num, LOCAL_CRATE); let krate = tcx.hir().krate(); let mut collect = InherentCollect { tcx, impls_map: Default::default() }; krate.visit_all_item_likes(&mut collect); - tcx.arena.alloc(collect.impls_map) + collect.impls_map } /// On-demand query: yields a vector of the inherent impls for a specific type. @@ -112,6 +112,30 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { item.span, ); } + ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not }) + if matches!(inner.kind, ty::Slice(_)) => + { + self.check_primitive_impl( + def_id, + lang_items.const_slice_ptr_impl(), + None, + "const_slice_ptr", + "*const [T]", + item.span, + ); + } + ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Mut }) + if matches!(inner.kind, ty::Slice(_)) => + { + self.check_primitive_impl( + def_id, + lang_items.mut_slice_ptr_impl(), + None, + "mut_slice_ptr", + "*mut [T]", + item.span, + ); + } ty::RawPtr(ty::TypeAndMut { ty: _, mutbl: hir::Mutability::Not }) => { self.check_primitive_impl( def_id, @@ -303,7 +327,7 @@ impl InherentCollect<'tcx> { // the implementation does not have any associated traits. let impl_def_id = self.tcx.hir().local_def_id(item.hir_id); let vec = self.impls_map.inherent_impls.entry(def_id).or_default(); - vec.push(impl_def_id); + vec.push(impl_def_id.to_def_id()); } else { struct_span_err!( self.tcx.sess, @@ -320,7 +344,7 @@ impl InherentCollect<'tcx> { fn check_primitive_impl( &self, - impl_def_id: DefId, + impl_def_id: LocalDefId, lang_def_id: Option, lang_def_id2: Option, lang: &str, @@ -328,10 +352,10 @@ impl InherentCollect<'tcx> { span: Span, ) { match (lang_def_id, lang_def_id2) { - (Some(lang_def_id), _) if lang_def_id == impl_def_id => { + (Some(lang_def_id), _) if lang_def_id == impl_def_id.to_def_id() => { // OK } - (_, Some(lang_def_id)) if lang_def_id == impl_def_id => { + (_, Some(lang_def_id)) if lang_def_id == impl_def_id.to_def_id() => { // OK } _ => { diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs index 7513759c76b01..be77d049cae40 100644 --- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs +++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs @@ -1,8 +1,8 @@ -use rustc::ty::TyCtxt; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::TyCtxt; use rustc_trait_selection::traits::{self, SkipLeakCheck}; pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) { diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 27b2c19499ccd..a45a44a6801e8 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -5,10 +5,10 @@ // done by the orphan and overlap modules. Then we build up various // mappings. That mapping code resides here. -use rustc::ty::query::Providers; -use rustc::ty::{self, TyCtxt, TypeFoldable}; use rustc_errors::struct_span_err; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::Span; use rustc_trait_selection::traits; @@ -19,15 +19,15 @@ mod orphan; mod unsafety; /// Obtains the span of just the impl header of `impl_def_id`. -fn impl_header_span(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Span { - tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap()) +fn impl_header_span(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) -> Span { + tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()) } -fn check_impl(tcx: TyCtxt<'_>, impl_def_id: DefId, trait_ref: ty::TraitRef<'_>) { +fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) { debug!( "(checking implementation) adding impl for trait '{:?}', item '{}'", trait_ref, - tcx.def_path_str(impl_def_id) + tcx.def_path_str(impl_def_id.to_def_id()) ); // Skip impls where one of the self type is an error type. @@ -40,11 +40,28 @@ fn check_impl(tcx: TyCtxt<'_>, impl_def_id: DefId, trait_ref: ty::TraitRef<'_>) enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id); } -fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, trait_def_id: DefId) { +fn enforce_trait_manually_implementable( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, + trait_def_id: DefId, +) { let did = Some(trait_def_id); let li = tcx.lang_items(); - // Disallow *all* explicit impls of `Sized` and `Unsize` for now. + // Disallow *all* explicit impls of `DiscriminantKind`, `Sized` and `Unsize` for now. + if did == li.discriminant_kind_trait() { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0322, + "explicit impls for the `DiscriminantKind` trait are not permitted" + ) + .span_label(span, "impl of 'DiscriminantKind' not allowed") + .emit(); + return; + } + if did == li.sized_trait() { let span = impl_header_span(tcx, impl_def_id); struct_span_err!( @@ -117,7 +134,11 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, tra /// We allow impls of marker traits to overlap, so they can't override impls /// as that could make it ambiguous which associated item to use. -fn enforce_empty_impls_for_marker_traits(tcx: TyCtxt<'_>, impl_def_id: DefId, trait_def_id: DefId) { +fn enforce_empty_impls_for_marker_traits( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, + trait_def_id: DefId, +) { if !tcx.trait_def(trait_def_id).is_marker { return; } @@ -148,7 +169,7 @@ pub fn provide(providers: &mut Providers<'_>) { fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { // Trigger building the specialization graph for the trait. This will detect and report any // overlap errors. - tcx.specialization_graph_of(def_id); + tcx.ensure().specialization_graph_of(def_id); let impls = tcx.hir().trait_impls(def_id); for &hir_id in impls { @@ -177,7 +198,7 @@ pub fn check_coherence(tcx: TyCtxt<'_>) { /// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. fn check_object_overlap<'tcx>( tcx: TyCtxt<'tcx>, - impl_def_id: DefId, + impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'tcx>, ) { let trait_def_id = trait_ref.def_id; diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs index fc77aad8688c6..71469770f2a33 100644 --- a/src/librustc_typeck/coherence/orphan.rs +++ b/src/librustc_typeck/coherence/orphan.rs @@ -1,11 +1,11 @@ //! Orphan checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. -use rustc::ty::{self, TyCtxt}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_trait_selection::traits; pub fn check(tcx: TyCtxt<'_>) { @@ -34,8 +34,8 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { let trait_ref = self.tcx.impl_trait_ref(def_id).unwrap(); let trait_def_id = trait_ref.def_id; let sm = self.tcx.sess.source_map(); - let sp = sm.def_span(item.span); - match traits::orphan_check(self.tcx, def_id) { + let sp = sm.guess_head_span(item.span); + match traits::orphan_check(self.tcx, def_id.to_def_id()) { Ok(()) => {} Err(traits::OrphanCheckErr::NonLocalInputType(tys)) => { let mut err = struct_span_err!( diff --git a/src/librustc_typeck/coherence/unsafety.rs b/src/librustc_typeck/coherence/unsafety.rs index 3b25f67aacc63..b281092ea631d 100644 --- a/src/librustc_typeck/coherence/unsafety.rs +++ b/src/librustc_typeck/coherence/unsafety.rs @@ -1,11 +1,11 @@ //! Unsafety checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. -use rustc::ty::TyCtxt; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Unsafety; +use rustc_middle::ty::TyCtxt; pub fn check(tcx: TyCtxt<'_>) { let mut unsafety = UnsafetyChecker { tcx }; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 8136417de0387..66ef6a04be914 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -17,34 +17,35 @@ use crate::astconv::{AstConv, Bounds, SizedByDefault}; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::constrained_generic_params as cgp; -use crate::middle::lang_items; use crate::middle::resolve_lifetime as rl; -use rustc::hir::map::blocks::FnLikeNode; -use rustc::hir::map::Map; -use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc::mir::mono::Linkage; -use rustc::ty::query::Providers; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::util::Discr; -use rustc::ty::util::IntTypeExt; -use rustc::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; -use rustc::ty::{ReprOptions, ToPredicate, WithConstness}; use rustc_ast::ast; -use rustc_ast::ast::{Ident, MetaItemKind}; +use rustc_ast::ast::MetaItemKind; use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{GenericParamKind, Node, Unsafety}; +use rustc_hir::weak_lang_items; +use rustc_hir::{GenericParamKind, Node}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::mir::mono::Linkage; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::util::Discr; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; use rustc_session::lint; use rustc_session::parse::feature_err; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; +use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; mod type_of; @@ -135,20 +136,7 @@ crate fn placeholder_type_error( if placeholder_types.is_empty() { return; } - // This is the whitelist of possible parameter names that we might suggest. - let possible_names = ["T", "K", "L", "A", "B", "C"]; - let used_names = generics - .iter() - .filter_map(|p| match p.name { - hir::ParamName::Plain(ident) => Some(ident.name), - _ => None, - }) - .collect::>(); - - let type_name = possible_names - .iter() - .find(|n| !used_names.contains(&Symbol::intern(n))) - .unwrap_or(&"ParamName"); + let type_name = generics.next_type_param_name(None); let mut sugg: Vec<_> = placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); @@ -162,8 +150,10 @@ crate fn placeholder_type_error( // `struct S(T);` instead of `struct S<_, T>(T);`. sugg.push((arg.span, (*type_name).to_string())); } else { + let last = generics.iter().last().unwrap(); sugg.push(( - generics.iter().last().unwrap().span.shrink_to_hi(), + // Account for bounds, we want `fn foo(_: K)` not `fn foo(_: K)`. + last.bounds_span().unwrap_or(last.span).shrink_to_hi(), format!(", {}", type_name), )); } @@ -217,12 +207,12 @@ impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { hir::GenericParamKind::Lifetime { .. } => {} hir::GenericParamKind::Type { default: Some(_), .. } => { let def_id = self.tcx.hir().local_def_id(param.hir_id); - self.tcx.type_of(def_id); + self.tcx.ensure().type_of(def_id); } hir::GenericParamKind::Type { .. } => {} hir::GenericParamKind::Const { .. } => { let def_id = self.tcx.hir().local_def_id(param.hir_id); - self.tcx.type_of(def_id); + self.tcx.ensure().type_of(def_id); } } } @@ -232,8 +222,8 @@ impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Closure(..) = expr.kind { let def_id = self.tcx.hir().local_def_id(expr.hir_id); - self.tcx.generics_of(def_id); - self.tcx.type_of(def_id); + self.tcx.ensure().generics_of(def_id); + self.tcx.ensure().type_of(def_id); } intravisit::walk_expr(self, expr); } @@ -279,10 +269,7 @@ impl ItemCtxt<'tcx> { } pub fn hir_id(&self) -> hir::HirId { - self.tcx - .hir() - .as_local_hir_id(self.item_def_id) - .expect("Non-local call to local provider is_const_fn") + self.tcx.hir().as_local_hir_id(self.item_def_id.expect_local()) } pub fn node(&self) -> hir::Node<'tcx> { @@ -308,7 +295,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { } fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { - self.tcx.at(span).type_param_predicates((self.item_def_id, def_id)) + self.tcx.at(span).type_param_predicates((self.item_def_id, def_id.expect_local())) } fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option> { @@ -326,13 +313,13 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { fn ct_infer( &self, - _: Ty<'tcx>, + ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span, ) -> &'tcx Const<'tcx> { bad_placeholder_type(self.tcx(), vec![span]).emit(); - self.tcx().consts.err + self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty }) } fn projected_ty_from_poly_trait_ref( @@ -408,9 +395,11 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { _ => {} } } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(..), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(..), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Union(..), .. }) => {} + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..), + .. + }) => {} hir::Node::Item(_) | hir::Node::ForeignItem(_) | hir::Node::TraitItem(_) @@ -489,7 +478,7 @@ fn get_new_lifetime_name<'tcx>( /// `X: Foo` where `X` is the type parameter `def_id`. fn type_param_predicates( tcx: TyCtxt<'_>, - (item_def_id, def_id): (DefId, DefId), + (item_def_id, def_id): (DefId, LocalDefId), ) -> ty::GenericPredicates<'_> { use rustc_hir::*; @@ -497,26 +486,29 @@ fn type_param_predicates( // written inline like `` or in a where-clause like // `where T: Foo`. - let param_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let param_id = tcx.hir().as_local_hir_id(def_id); let param_owner = tcx.hir().ty_param_owner(param_id); let param_owner_def_id = tcx.hir().local_def_id(param_owner); let generics = tcx.generics_of(param_owner_def_id); - let index = generics.param_def_id_to_index[&def_id]; + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(param_id)); // Don't look for bounds where the type parameter isn't in scope. - let parent = - if item_def_id == param_owner_def_id { None } else { tcx.generics_of(item_def_id).parent }; + let parent = if item_def_id == param_owner_def_id.to_def_id() { + None + } else { + tcx.generics_of(item_def_id).parent + }; let mut result = parent .map(|parent| { let icx = ItemCtxt::new(tcx, parent); - icx.get_type_parameter_bounds(DUMMY_SP, def_id) + icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id()) }) .unwrap_or_default(); let mut extend = None; - let item_hir_id = tcx.hir().as_local_hir_id(item_def_id).unwrap(); + let item_hir_id = tcx.hir().as_local_hir_id(item_def_id.expect_local()); let ast_generics = match tcx.hir().get(item_hir_id) { Node::TraitItem(item) => &item.generics, @@ -621,7 +613,7 @@ fn is_param(tcx: TyCtxt<'_>, ast_ty: &hir::Ty<'_>, param_id: hir::HirId) -> bool if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = ast_ty.kind { match path.res { Res::SelfTy(Some(def_id), None) | Res::Def(DefKind::TyParam, def_id) => { - def_id == tcx.hir().local_def_id(param_id) + def_id == tcx.hir().local_def_id(param_id).to_def_id() } _ => false, } @@ -643,47 +635,47 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { hir::ItemKind::ForeignMod(ref foreign_mod) => { for item in foreign_mod.items { let def_id = tcx.hir().local_def_id(item.hir_id); - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); if let hir::ForeignItemKind::Fn(..) = item.kind { - tcx.fn_sig(def_id); + tcx.ensure().fn_sig(def_id); } } } hir::ItemKind::Enum(ref enum_definition, _) => { - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); - convert_enum_variant_types(tcx, def_id, &enum_definition.variants); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + convert_enum_variant_types(tcx, def_id.to_def_id(), &enum_definition.variants); } hir::ItemKind::Impl { .. } => { - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.impl_trait_ref(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().impl_trait_ref(def_id); + tcx.ensure().predicates_of(def_id); } hir::ItemKind::Trait(..) => { - tcx.generics_of(def_id); - tcx.trait_def(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().trait_def(def_id); tcx.at(it.span).super_predicates_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().predicates_of(def_id); } hir::ItemKind::TraitAlias(..) => { - tcx.generics_of(def_id); + tcx.ensure().generics_of(def_id); tcx.at(it.span).super_predicates_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().predicates_of(def_id); } hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); for f in struct_def.fields() { let def_id = tcx.hir().local_def_id(f.hir_id); - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); } if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { @@ -699,11 +691,11 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { | hir::ItemKind::Static(..) | hir::ItemKind::Const(..) | hir::ItemKind::Fn(..) => { - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); if let hir::ItemKind::Fn(..) = it.kind { - tcx.fn_sig(def_id); + tcx.ensure().fn_sig(def_id); } } } @@ -712,20 +704,20 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) { let trait_item = tcx.hir().expect_trait_item(trait_item_id); let def_id = tcx.hir().local_def_id(trait_item.hir_id); - tcx.generics_of(def_id); + tcx.ensure().generics_of(def_id); match trait_item.kind { hir::TraitItemKind::Fn(..) => { - tcx.type_of(def_id); - tcx.fn_sig(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().fn_sig(def_id); } hir::TraitItemKind::Const(.., Some(_)) => { - tcx.type_of(def_id); + tcx.ensure().type_of(def_id); } hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(_, Some(_)) => { - tcx.type_of(def_id); + tcx.ensure().type_of(def_id); // Account for `const C: _;` and `type T = _;`. let mut visitor = PlaceholderHirTyCollector::default(); visitor.visit_trait_item(trait_item); @@ -735,18 +727,18 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) { hir::TraitItemKind::Type(_, None) => {} }; - tcx.predicates_of(def_id); + tcx.ensure().predicates_of(def_id); } fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::HirId) { let def_id = tcx.hir().local_def_id(impl_item_id); - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); let impl_item = tcx.hir().expect_impl_item(impl_item_id); match impl_item.kind { hir::ImplItemKind::Fn(..) => { - tcx.fn_sig(def_id); + tcx.ensure().fn_sig(def_id); } hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => { // Account for `type T = _;` @@ -760,9 +752,9 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::HirId) { fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) { let def_id = tcx.hir().local_def_id(ctor_id); - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); } fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::Variant<'_>]) { @@ -777,7 +769,7 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::V prev_discr = Some( if let Some(ref e) = variant.disr_expr { let expr_did = tcx.hir().local_def_id(e.hir_id); - def.eval_explicit_discr(tcx, expr_did) + def.eval_explicit_discr(tcx, expr_did.to_def_id()) } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { Some(discr) } else { @@ -798,9 +790,9 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::V for f in variant.data.fields() { let def_id = tcx.hir().local_def_id(f.hir_id); - tcx.generics_of(def_id); - tcx.type_of(def_id); - tcx.predicates_of(def_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); } // Convert the ctor, if any. This also registers the variant as @@ -813,16 +805,16 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::V fn convert_variant( tcx: TyCtxt<'_>, - variant_did: Option, - ctor_did: Option, + variant_did: Option, + ctor_did: Option, ident: Ident, discr: ty::VariantDiscr, def: &hir::VariantData<'_>, adt_kind: ty::AdtKind, - parent_did: DefId, + parent_did: LocalDefId, ) -> ty::VariantDef { - let mut seen_fields: FxHashMap = Default::default(); - let hir_id = tcx.hir().as_local_hir_id(variant_did.unwrap_or(parent_did)).unwrap(); + let mut seen_fields: FxHashMap = Default::default(); + let hir_id = tcx.hir().as_local_hir_id(variant_did.unwrap_or(parent_did)); let fields = def .fields() .iter() @@ -845,7 +837,7 @@ fn convert_variant( } ty::FieldDef { - did: fid, + did: fid.to_def_id(), ident: f.ident, vis: ty::Visibility::from_hir(&f.vis, hir_id, tcx), } @@ -858,13 +850,13 @@ fn convert_variant( ty::VariantDef::new( tcx, ident, - variant_did, - ctor_did, + variant_did.map(LocalDefId::to_def_id), + ctor_did.map(LocalDefId::to_def_id), discr, fields, CtorKind::from_hir(def), adt_kind, - parent_did, + parent_did.to_def_id(), recovered, ) } @@ -872,13 +864,14 @@ fn convert_variant( fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { use rustc_hir::*; - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let def_id = def_id.expect_local(); + let hir_id = tcx.hir().as_local_hir_id(def_id); let item = match tcx.hir().get(hir_id) { Node::Item(item) => item, _ => bug!(), }; - let repr = ReprOptions::new(tcx, def_id); + let repr = ReprOptions::new(tcx, def_id.to_def_id()); let (kind, variants) = match item.kind { ItemKind::Enum(ref def, _) => { let mut distance_from_explicit = 0; @@ -892,7 +885,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { let discr = if let Some(ref e) = v.disr_expr { distance_from_explicit = 0; - ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id)) + ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id).to_def_id()) } else { ty::VariantDiscr::Relative(distance_from_explicit) }; @@ -914,7 +907,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { (AdtKind::Enum, variants) } ItemKind::Struct(ref def, _) => { - let variant_did = None; + let variant_did = None::; let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); let variants = std::iter::once(convert_variant( @@ -951,7 +944,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { } _ => bug!(), }; - tcx.alloc_adt_def(def_id, kind, variants, repr) + tcx.alloc_adt_def(def_id.to_def_id(), kind, variants, repr) } /// Ensures that the super-predicates of the trait with a `DefId` @@ -959,7 +952,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { /// the transitive super-predicates are converted. fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredicates<'_> { debug!("super_predicates(trait_def_id={:?})", trait_def_id); - let trait_hir_id = tcx.hir().as_local_hir_id(trait_def_id).unwrap(); + let trait_hir_id = tcx.hir().as_local_hir_id(trait_def_id.expect_local()); let item = match tcx.hir().get(trait_hir_id) { Node::Item(item) => item, @@ -1009,8 +1002,8 @@ fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredi ty::GenericPredicates { parent: None, predicates: superbounds } } -fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TraitDef { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); +fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let item = tcx.hir().expect_item(hir_id); let (is_auto, unsafety) = match item.kind { @@ -1040,16 +1033,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TraitDef { ty::trait_def::TraitSpecializationKind::None }; let def_path_hash = tcx.def_path_hash(def_id); - let def = ty::TraitDef::new( - def_id, - unsafety, - paren_sugar, - is_auto, - is_marker, - spec_kind, - def_path_hash, - ); - tcx.arena.alloc(def) + ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, spec_kind, def_path_hash) } fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option { @@ -1099,13 +1083,15 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option {} - Some(rl::Region::LateBound(debruijn, _, _)) - | Some(rl::Region::LateBoundAnon(debruijn, _)) - if debruijn < self.outer_index => {} - Some(rl::Region::LateBound(..)) - | Some(rl::Region::LateBoundAnon(..)) - | Some(rl::Region::Free(..)) + Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} + Some( + rl::Region::LateBound(debruijn, _, _) | rl::Region::LateBoundAnon(debruijn, _), + ) if debruijn < self.outer_index => {} + Some( + rl::Region::LateBound(..) + | rl::Region::LateBoundAnon(..) + | rl::Region::Free(..), + ) | None => { self.has_late_bound_regions = Some(lt.span); } @@ -1163,10 +1149,10 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option, def_id: DefId) -> &ty::Generics { +fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { use rustc_hir::*; - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); let parent_def_id = match node { @@ -1176,18 +1162,35 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { | Node::Ctor(..) | Node::Field(_) => { let parent_id = tcx.hir().get_parent_item(hir_id); - Some(tcx.hir().local_def_id(parent_id)) + Some(tcx.hir().local_def_id(parent_id).to_def_id()) } - // FIXME(#43408) enable this always when we get lazy normalization. + // FIXME(#43408) always enable this once `lazy_normalization` is + // stable enough and does not need a feature gate anymore. Node::AnonConst(_) => { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_def_id = tcx.hir().local_def_id(parent_id); + // HACK(eddyb) this provides the correct generics when // `feature(const_generics)` is enabled, so that const expressions // used with const generics, e.g. `Foo<{N+1}>`, can work at all. - if tcx.features().const_generics { - let parent_id = tcx.hir().get_parent_item(hir_id); - Some(tcx.hir().local_def_id(parent_id)) + if tcx.lazy_normalization() { + Some(parent_def_id.to_def_id()) } else { - None + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + match parent_node { + // HACK(eddyb) this provides the correct generics for repeat + // expressions' count (i.e. `N` in `[x; N]`), and explicit + // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), + // as they shouldn't be able to cause query cycle errors. + Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) + | Node::Variant(Variant { disr_expr: Some(ref constant), .. }) + if constant.hir_id == hir_id => + { + Some(parent_def_id.to_def_id()) + } + + _ => None, + } } } Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { @@ -1206,7 +1209,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { if let Node::Item(hir::Item { kind: ItemKind::OpaqueTy(..), .. }) = tcx.hir().get(parent_id) { - Some(tcx.hir().local_def_id(parent_id)) + Some(tcx.hir().local_def_id(parent_id).to_def_id()) } else { None } @@ -1253,7 +1256,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { opt_self = Some(ty::GenericParamDef { index: 0, name: kw::SelfUpper, - def_id: tcx.hir().local_def_id(param_id), + def_id: tcx.hir().local_def_id(param_id).to_def_id(), pure_wrt_drop: false, kind: ty::GenericParamDefKind::Type { has_default: false, @@ -1296,7 +1299,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { name: param.name.ident().name, index: own_start + i as u32, - def_id: tcx.hir().local_def_id(param.hir_id), + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind: ty::GenericParamDefKind::Lifetime, })); @@ -1342,7 +1345,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { let param_def = ty::GenericParamDef { index: type_start + i as u32, name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id), + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind, }; @@ -1358,7 +1361,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { let param_def = ty::GenericParamDef { index: type_start + i as u32, name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id), + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind: ty::GenericParamDefKind::Const, }; @@ -1394,14 +1397,14 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); - tcx.arena.alloc(ty::Generics { + ty::Generics { parent: parent_def_id, parent_count, params, param_def_id_to_index, has_self: has_self || parent_has_self, has_late_bound_regions: has_late_bound_regions(tcx, node), - }) + } } fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { @@ -1450,9 +1453,10 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { use rustc_hir::Node::*; use rustc_hir::*; - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let def_id = def_id.expect_local(); + let hir_id = tcx.hir().as_local_hir_id(def_id); - let icx = ItemCtxt::new(tcx, def_id); + let icx = ItemCtxt::new(tcx, def_id.to_def_id()); match tcx.hir().get(hir_id) { TraitItem(hir::TraitItem { @@ -1501,13 +1505,17 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl, &generics, Some(ident.span)) } - ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(ref fn_decl, _, _), .. }) => { + ForeignItem(&hir::ForeignItem { + kind: ForeignItemKind::Fn(ref fn_decl, _, _), + ident, + .. + }) => { let abi = tcx.hir().get_foreign_abi(hir_id); - compute_sig_of_foreign_fn_decl(tcx, def_id, fn_decl, abi) + compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi, ident) } Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => { - let ty = tcx.type_of(tcx.hir().get_parent_did(hir_id)); + let ty = tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()); let inputs = data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id))); ty::Binder::bind(tcx.mk_fn_sig( @@ -1544,7 +1552,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { let icx = ItemCtxt::new(tcx, def_id); - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); match tcx.hir().expect_item(hir_id).kind { hir::ItemKind::Impl { ref of_trait, .. } => of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id); @@ -1555,7 +1563,7 @@ fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { } fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); let item = tcx.hir().expect_item(hir_id); match &item.kind { @@ -1644,7 +1652,7 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { // prove that the trait applies to the types that were // used, and adding the predicate into this list ensures // that this is done. - let span = tcx.def_span(def_id); + let span = tcx.sess.source_map().guess_head_span(tcx.def_span(def_id)); result.predicates = tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(), @@ -1689,7 +1697,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat } } - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); let mut is_trait = None; @@ -1833,7 +1841,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let mut index = parent_count + has_own_self as u32; for param in early_bound_lifetimes_from_generics(tcx, ast_generics) { let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: tcx.hir().local_def_id(param.hir_id), + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), index, name: param.name.ident().name, })); @@ -2029,7 +2037,8 @@ fn associated_item_predicates( } ty::GenericParamDefKind::Const => { unimplemented_error("const"); - tcx.consts.err.into() + tcx.mk_const(ty::Const { val: ty::ConstKind::Error, ty: tcx.type_of(param.def_id) }) + .into() } } }; @@ -2051,12 +2060,13 @@ fn associated_item_predicates( // the `Binder` around the the predicate. // // FIXME(generic_associated_types): Currently only lifetimes are handled. - self_trait_ref.substs.extend_to(tcx, item_def_id, mk_bound_param) + self_trait_ref.substs.extend_to(tcx, item_def_id.to_def_id(), mk_bound_param) } else { self_trait_ref.substs }; - let assoc_ty = tcx.mk_projection(tcx.hir().local_def_id(trait_item.hir_id), bound_substs); + let assoc_ty = + tcx.mk_projection(tcx.hir().local_def_id(trait_item.hir_id).to_def_id(), bound_substs); let bounds = AstConv::compute_bounds( &ItemCtxt::new(tcx, def_id), @@ -2116,6 +2126,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( def_id: DefId, decl: &'tcx hir::FnDecl<'tcx>, abi: abi::Abi, + ident: Ident, ) -> ty::PolyFnSig<'tcx> { let unsafety = if abi == abi::Abi::RustIntrinsic { intrinsic_operation_unsafety(&tcx.item_name(def_id).as_str()) @@ -2128,7 +2139,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( abi, decl, &hir::Generics::empty(), - None, + Some(ident.span), ); // Feature gate SIMD types in FFI, since I am not sure that the @@ -2139,13 +2150,18 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( { let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| { if ty.is_simd() { + let snip = tcx + .sess + .source_map() + .span_to_snippet(ast_ty.span) + .map_or(String::new(), |s| format!(" `{}`", s)); tcx.sess .struct_span_err( ast_ty.span, &format!( - "use of SIMD type `{}` in FFI is highly experimental and \ + "use of SIMD type{} in FFI is highly experimental and \ may result in invalid code", - tcx.hir().hir_to_pretty_string(ast_ty.hir_id) + snip ), ) .help("add `#![feature(simd_ffi)]` to the crate attributes to enable") @@ -2173,11 +2189,13 @@ fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool { fn static_mutability(tcx: TyCtxt<'_>, def_id: DefId) -> Option { match tcx.hir().get_if_local(def_id) { - Some(Node::Item(&hir::Item { kind: hir::ItemKind::Static(_, mutbl, _), .. })) - | Some(Node::ForeignItem(&hir::ForeignItem { - kind: hir::ForeignItemKind::Static(_, mutbl), - .. - })) => Some(mutbl), + Some( + Node::Item(&hir::Item { kind: hir::ItemKind::Static(_, mutbl, _), .. }) + | Node::ForeignItem(&hir::ForeignItem { + kind: hir::ForeignItemKind::Static(_, mutbl), + .. + }), + ) => Some(mutbl), Some(_) => None, _ => bug!("static_mutability applied to non-local def-id {:?}", def_id), } @@ -2261,6 +2279,7 @@ fn from_target_feature( Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, Some(sym::mips_target_feature) => rust_features.mips_target_feature, + Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, Some(sym::mmx_target_feature) => rust_features.mmx_target_feature, Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, @@ -2289,7 +2308,7 @@ fn from_target_feature( } fn linkage_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Linkage { - use rustc::mir::mono::Linkage::*; + use rustc_middle::mir::mono::Linkage::*; // Use the names from src/llvm/docs/LangRef.rst here. Most types are only // applicable to variable declarations and may not really make sense for @@ -2355,6 +2374,43 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { ) .emit(); } + } else if attr.check_name(sym::ffi_pure) { + if tcx.is_foreign_item(id) { + if attrs.iter().any(|a| a.check_name(sym::ffi_const)) { + // `#[ffi_const]` functions cannot be `#[ffi_pure]` + struct_span_err!( + tcx.sess, + attr.span, + E0757, + "`#[ffi_const]` function cannot be `#[ffi_pure]`" + ) + .emit(); + } else { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE; + } + } else { + // `#[ffi_pure]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0755, + "`#[ffi_pure]` may only be used on foreign functions" + ) + .emit(); + } + } else if attr.check_name(sym::ffi_const) { + if tcx.is_foreign_item(id) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST; + } else { + // `#[ffi_const]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0756, + "`#[ffi_const]` may only be used on foreign functions" + ) + .emit(); + } } else if attr.check_name(sym::rustc_allocator_nounwind) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; } else if attr.check_name(sym::naked) { @@ -2389,13 +2445,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.export_name = Some(s); } } else if attr.check_name(sym::target_feature) { - if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == Unsafety::Normal { - let msg = "`#[target_feature(..)]` can only be applied to `unsafe` functions"; - tcx.sess - .struct_span_err(attr.span, msg) - .span_label(attr.span, "can only be applied to `unsafe` functions") - .span_label(tcx.def_span(id), "not an `unsafe` function") - .emit(); + if !tcx.features().target_feature_11 { + check_target_feature_safe_fn(tcx, id, attr.span); + } else if let Some(local_id) = id.as_local() { + if tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { + check_target_feature_trait_unsafe(tcx, local_id, attr.span); + } } from_target_feature(tcx, id, attr, &whitelist, &mut codegen_fn_attrs.target_features); } else if attr.check_name(sym::linkage) { @@ -2534,7 +2589,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { if codegen_fn_attrs.flags.intersects(CodegenFnAttrFlags::NO_SANITIZE_ANY) { if codegen_fn_attrs.inline == InlineAttr::Always { if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { - let hir_id = tcx.hir().as_local_hir_id(id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(id.expect_local()); tcx.struct_span_lint_hir( lint::builtin::INLINE_NO_SANITIZE, hir_id, @@ -2557,7 +2612,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { if tcx.is_weak_lang_item(id) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } - if let Some(name) = lang_items::link_name(&attrs) { + if let Some(name) = weak_lang_items::link_name(&attrs) { codegen_fn_attrs.export_name = Some(name); codegen_fn_attrs.link_name = Some(name); } @@ -2583,7 +2638,7 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { .associated_items(trait_def_id) .filter_by_name_unhygienic(impl_item.ident.name) .find(move |trait_item| { - trait_item.kind == ty::AssocKind::Method + trait_item.kind == ty::AssocKind::Fn && tcx.hygienic_eq(impl_item.ident, trait_item.ident, trait_def_id) }) { @@ -2608,13 +2663,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { _ => None, }; if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list { - if *ordinal <= std::usize::MAX as u128 { + if *ordinal <= usize::MAX as u128 { Some(*ordinal as usize) } else { let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); tcx.sess .struct_span_err(attr.span, &msg) - .note("the value may not exceed `std::usize::MAX`") + .note("the value may not exceed `usize::MAX`") .emit(); None } @@ -2642,3 +2697,39 @@ fn check_link_name_xor_ordinal( tcx.sess.err(msg); } } + +/// Checks the function annotated with `#[target_feature]` is unsafe, +/// reporting an error if it isn't. +fn check_target_feature_safe_fn(tcx: TyCtxt<'_>, id: DefId, attr_span: Span) { + if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { + let mut err = feature_err( + &tcx.sess.parse_sess, + sym::target_feature_11, + attr_span, + "`#[target_feature(..)]` can only be applied to `unsafe` functions", + ); + err.span_label(tcx.def_span(id), "not an `unsafe` function"); + err.emit(); + } +} + +/// Checks the function annotated with `#[target_feature]` is not a safe +/// trait method implementation, reporting an error if it is. +fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { + let hir_id = tcx.hir().as_local_hir_id(id); + let node = tcx.hir().get(hir_id); + if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_item = tcx.hir().expect_item(parent_id); + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = parent_item.kind { + tcx.sess + .struct_span_err( + attr_span, + "`#[target_feature(..)]` cannot be applied to safe trait method", + ) + .span_label(attr_span, "cannot be applied to safe trait method") + .span_label(tcx.def_span(id), "not an `unsafe` function") + .emit(); + } + } +} diff --git a/src/librustc_typeck/collect/type_of.rs b/src/librustc_typeck/collect/type_of.rs index 30741ed53b58b..cf0e3f9cdf592 100644 --- a/src/librustc_typeck/collect/type_of.rs +++ b/src/librustc_typeck/collect/type_of.rs @@ -1,15 +1,15 @@ -use rustc::hir::map::Map; -use rustc::ty::subst::{GenericArgKind, InternalSubsts, Subst}; -use rustc::ty::util::IntTypeExt; -use rustc::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, Applicability, StashKey}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{struct_span_err, Applicability, ErrorReported, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -21,7 +21,7 @@ use super::{bad_placeholder_type, is_suggestable_infer_ty}; pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { use rustc_hir::*; - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); let icx = ItemCtxt::new(tcx, def_id); @@ -34,7 +34,13 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { TraitItemKind::Const(ref ty, body_id) => body_id .and_then(|body_id| { if is_suggestable_infer_ty(ty) { - Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)) + Some(infer_placeholder_type( + tcx, + def_id.expect_local(), + body_id, + ty.span, + item.ident, + )) } else { None } @@ -53,20 +59,20 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } ImplItemKind::Const(ref ty, body_id) => { if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) + infer_placeholder_type(tcx, def_id.expect_local(), body_id, ty.span, item.ident) } else { icx.to_ty(ty) } } ImplItemKind::OpaqueTy(_) => { - if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id)).is_none() { + if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id).to_def_id()).is_none() { report_assoc_ty_on_inherent_impl(tcx, item.span); } - find_opaque_ty_constraints(tcx, def_id) + find_opaque_ty_constraints(tcx, def_id.expect_local()) } ImplItemKind::TyAlias(ref ty) => { - if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id)).is_none() { + if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id).to_def_id()).is_none() { report_assoc_ty_on_inherent_impl(tcx, item.span); } @@ -78,7 +84,13 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { match item.kind { ItemKind::Static(ref ty, .., body_id) | ItemKind::Const(ref ty, body_id) => { if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) + infer_placeholder_type( + tcx, + def_id.expect_local(), + body_id, + ty.span, + item.ident, + ) } else { icx.to_ty(ty) } @@ -96,19 +108,19 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.mk_adt(def, substs) } ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }) => { - find_opaque_ty_constraints(tcx, def_id) + find_opaque_ty_constraints(tcx, def_id.expect_local()) } // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: Some(owner), origin, .. }) => { let concrete_types = match origin { OpaqueTyOrigin::FnReturn | OpaqueTyOrigin::AsyncFn => { - &tcx.mir_borrowck(owner).concrete_opaque_types + &tcx.mir_borrowck(owner.expect_local()).concrete_opaque_types } OpaqueTyOrigin::Misc => { // We shouldn't leak borrowck results through impl trait in bindings. // For example, we shouldn't be able to tell if `x` in // `let x: impl Sized + 'a = &()` has type `&'static ()` or `&'a ()`. - &tcx.typeck_tables_of(owner).concrete_opaque_types + &tcx.typeck_tables_of(owner.expect_local()).concrete_opaque_types } OpaqueTyOrigin::TypeAlias => { span_bug!(item.span, "Type alias impl trait shouldn't have an owner") @@ -125,7 +137,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { owner, def_id, ), ); - if tcx.typeck_tables_of(owner).tainted_by_errors { + if let Some(ErrorReported) = + tcx.typeck_tables_of(owner.expect_local()).tainted_by_errors + { // Some error in the // owner fn prevented us from populating // the `concrete_opaque_types` table. @@ -177,7 +191,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::Ctor(&ref def) | Node::Variant(Variant { data: ref def, .. }) => match *def { VariantData::Unit(..) | VariantData::Struct(..) => { - tcx.type_of(tcx.hir().get_parent_did(hir_id)) + tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()) } VariantData::Tuple(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id); @@ -207,102 +221,97 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.types.usize } - Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { - tcx.adt_def(tcx.hir().get_parent_did(hir_id)).repr.discr_type().to_ty(tcx) - } + Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx + .adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()) + .repr + .discr_type() + .to_ty(tcx), Node::Ty(&Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Struct(..), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_), .. }) + | Node::Expr(&Expr { kind: ExprKind::Struct(..) | ExprKind::Path(_), .. }) | Node::TraitRef(..) => { let path = match parent_node { - Node::Ty(&Ty { - kind: TyKind::Path(QPath::Resolved(_, ref path)), .. - }) + Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) | Node::Expr(&Expr { - kind: ExprKind::Path(QPath::Resolved(_, ref path)), + kind: + ExprKind::Path(QPath::Resolved(_, path)) + | ExprKind::Struct(&QPath::Resolved(_, path), ..), .. - }) => Some(&**path), - Node::Expr(&Expr { kind: ExprKind::Struct(ref path, ..), .. }) => { - if let QPath::Resolved(_, ref path) = **path { - Some(&**path) - } else { - None - } + }) + | Node::TraitRef(&TraitRef { path, .. }) => &*path, + _ => { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("unexpected const parent path {:?}", parent_node), + ); + return tcx.types.err; } - Node::TraitRef(&TraitRef { ref path, .. }) => Some(&**path), - _ => None, }; - if let Some(path) = path { - // We've encountered an `AnonConst` in some path, so we need to - // figure out which generic parameter it corresponds to and return - // the relevant type. - - let (arg_index, segment) = path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref().map(|args| (args.args, seg))) - .find_map(|(args, seg)| { - args.iter() - .filter(|arg| arg.is_const()) - .enumerate() - .filter(|(_, arg)| arg.id() == hir_id) - .map(|(index, _)| (index, seg)) - .next() - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in path"); - }); - - // Try to use the segment resolution if it is valid, otherwise we - // default to the path resolution. - let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); - let generics = match res { - Res::Def(DefKind::Ctor(..), def_id) => { - tcx.generics_of(tcx.parent(def_id).unwrap()) - } - Res::Def(_, def_id) => tcx.generics_of(def_id), - res => { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!( - "unexpected anon const res {:?} in path: {:?}", - res, path, - ), - ); - return tcx.types.err; + // We've encountered an `AnonConst` in some path, so we need to + // figure out which generic parameter it corresponds to and return + // the relevant type. + + let (arg_index, segment) = path + .segments + .iter() + .filter_map(|seg| seg.args.as_ref().map(|args| (args.args, seg))) + .find_map(|(args, seg)| { + args.iter() + .filter(|arg| arg.is_const()) + .enumerate() + .filter(|(_, arg)| arg.id() == hir_id) + .map(|(index, _)| (index, seg)) + .next() + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in path"); + }); + + // Try to use the segment resolution if it is valid, otherwise we + // default to the path resolution. + let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); + let generics = match res { + Res::Def(DefKind::Ctor(..), def_id) => { + tcx.generics_of(tcx.parent(def_id).unwrap()) + } + Res::Def(_, def_id) => tcx.generics_of(def_id), + res => { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!( + "unexpected anon const res {:?} in path: {:?}", + res, path, + ), + ); + return tcx.types.err; + } + }; + + let ty = generics + .params + .iter() + .filter(|param| { + if let ty::GenericParamDefKind::Const = param.kind { + true + } else { + false } - }; - - generics - .params - .iter() - .filter(|param| { - if let ty::GenericParamDefKind::Const = param.kind { - true - } else { - false - } - }) - .nth(arg_index) - .map(|param| tcx.type_of(param.def_id)) - // This is no generic parameter associated with the arg. This is - // probably from an extra arg where one is not needed. - .unwrap_or_else(|| { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!( - "missing generic parameter for `AnonConst`, parent: {:?}, res: {:?}", - parent_node, res - ), - ); - tcx.types.err - }) + }) + .nth(arg_index) + .map(|param| tcx.type_of(param.def_id)); + + if let Some(ty) = ty { + ty } else { + // This is no generic parameter associated with the arg. This is + // probably from an extra arg where one is not needed. tcx.sess.delay_span_bug( DUMMY_SP, - &format!("unexpected const parent path {:?}", parent_node,), + &format!( + "missing generic parameter for `AnonConst`, parent: {:?}, res: {:?}", + parent_node, res + ), ); tcx.types.err } @@ -344,17 +353,45 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty) .is_some() { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "the types of const generic parameters must derive `PartialEq` and `Eq`", - ) - .span_label( - hir_ty.span, - format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), - ) - .emit(); + // We use the same error code in both branches, because this is really the same + // issue: we just special-case the message for type parameters to make it + // clearer. + if let ty::Param(_) = ty.peel_refs().kind { + // Const parameters may not have type parameters as their types, + // because we cannot be sure that the type parameter derives `PartialEq` + // and `Eq` (just implementing them is not enough for `structural_match`). + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ + used as the type of a const parameter", + ty, + ) + .span_label( + hir_ty.span, + format!("`{}` may not derive both `PartialEq` and `Eq`", ty), + ) + .note( + "it is not currently possible to use a type parameter as the type of a \ + const parameter", + ) + .emit(); + } else { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ + the type of a const parameter", + ty, + ) + .span_label( + hir_ty.span, + format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), + ) + .emit(); + } } ty } @@ -367,7 +404,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } } -fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { +fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { use rustc_hir::{Expr, ImplItem, Item, TraitItem}; debug!("find_opaque_ty_constraints({:?})", def_id); @@ -375,17 +412,12 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { struct ConstraintLocator<'tcx> { tcx: TyCtxt<'tcx>, def_id: DefId, - // (first found type span, actual type, mapping from the opaque type's generic - // parameters to the concrete type's generic parameters) - // - // The mapping is an index for each use site of a generic parameter in the concrete type - // - // The indices index into the generic parameters on the opaque type. - found: Option<(Span, Ty<'tcx>, Vec)>, + // (first found type span, actual type) + found: Option<(Span, Ty<'tcx>)>, } impl ConstraintLocator<'_> { - fn check(&mut self, def_id: DefId) { + fn check(&mut self, def_id: LocalDefId) { // Don't try to check items that cannot possibly constrain the type. if !self.tcx.has_typeck_tables(def_id) { debug!( @@ -413,83 +445,51 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { // FIXME(oli-obk): trace the actual span from inference to improve errors. let span = self.tcx.def_span(def_id); - // used to quickly look up the position of a generic parameter - let mut index_map: FxHashMap = FxHashMap::default(); - // Skipping binder is ok, since we only use this to find generic parameters and - // their positions. - for (idx, subst) in substs.iter().enumerate() { - if let GenericArgKind::Type(ty) = subst.unpack() { - if let ty::Param(p) = ty.kind { - if index_map.insert(p, idx).is_some() { - // There was already an entry for `p`, meaning a generic parameter - // was used twice. - self.tcx.sess.span_err( - span, - &format!( - "defining opaque type use restricts opaque \ - type by using the generic parameter `{}` twice", - p, - ), - ); - return; - } - } else { + + // HACK(eddyb) this check shouldn't be needed, as `wfcheck` + // performs the same checks, in theory, but I've kept it here + // using `delay_span_bug`, just in case `wfcheck` slips up. + let opaque_generics = self.tcx.generics_of(self.def_id); + let mut used_params: FxHashSet<_> = FxHashSet::default(); + for (i, arg) in substs.iter().enumerate() { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), + GenericArgKind::Lifetime(lt) => { + matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + } + GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)), + }; + + if arg_is_param { + if !used_params.insert(arg) { + // There was already an entry for `arg`, meaning a generic parameter + // was used twice. self.tcx.sess.delay_span_bug( span, &format!( - "non-defining opaque ty use in defining scope: {:?}, {:?}", - concrete_type, substs, + "defining opaque type use restricts opaque \ + type by using the generic parameter `{}` twice", + arg, ), ); } - } - } - // Compute the index within the opaque type for each generic parameter used in - // the concrete type. - let indices = concrete_type - .subst(self.tcx, substs) - .walk() - .filter_map(|t| match &t.kind { - ty::Param(p) => Some(*index_map.get(p).unwrap()), - _ => None, - }) - .collect(); - let is_param = |ty: Ty<'_>| match ty.kind { - ty::Param(_) => true, - _ => false, - }; - let bad_substs: Vec<_> = substs - .iter() - .enumerate() - .filter_map(|(i, k)| { - if let GenericArgKind::Type(ty) = k.unpack() { Some((i, ty)) } else { None } - }) - .filter(|(_, ty)| !is_param(ty)) - .collect(); - if !bad_substs.is_empty() { - let identity_substs = InternalSubsts::identity_for_item(self.tcx, self.def_id); - for (i, bad_subst) in bad_substs { - self.tcx.sess.span_err( + } else { + let param = opaque_generics.param_at(i, self.tcx); + self.tcx.sess.delay_span_bug( span, &format!( "defining opaque type use does not fully define opaque type: \ - generic parameter `{}` is specified as concrete type `{}`", - identity_substs.type_at(i), - bad_subst + generic parameter `{}` is specified as concrete {} `{}`", + param.name, + param.kind.descr(), + arg, ), ); } - } else if let Some((prev_span, prev_ty, ref prev_indices)) = self.found { - let mut ty = concrete_type.walk().fuse(); - let mut p_ty = prev_ty.walk().fuse(); - let iter_eq = (&mut ty).zip(&mut p_ty).all(|(t, p)| match (&t.kind, &p.kind) { - // Type parameters are equal to any other type parameter for the purpose of - // concrete type equality, as it is possible to obtain the same type just - // by passing matching parameters to a function. - (ty::Param(_), ty::Param(_)) => true, - _ => t == p, - }); - if !iter_eq || ty.next().is_some() || p_ty.next().is_some() { + } + + if let Some((prev_span, prev_ty)) = self.found { + if *concrete_type != prev_ty { debug!("find_opaque_ty_constraints: span={:?}", span); // Found different concrete types for the opaque type. let mut err = self.tcx.sess.struct_span_err( @@ -502,34 +502,9 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { ); err.span_note(prev_span, "previous use here"); err.emit(); - } else if indices != *prev_indices { - // Found "same" concrete types, but the generic parameter order differs. - let mut err = self.tcx.sess.struct_span_err( - span, - "concrete type's generic parameters differ from previous defining use", - ); - use std::fmt::Write; - let mut s = String::new(); - write!(s, "expected [").unwrap(); - let list = |s: &mut String, indices: &Vec| { - let mut indices = indices.iter().cloned(); - if let Some(first) = indices.next() { - write!(s, "`{}`", substs[first]).unwrap(); - for i in indices { - write!(s, ", `{}`", substs[i]).unwrap(); - } - } - }; - list(&mut s, prev_indices); - write!(s, "], got [").unwrap(); - list(&mut s, &indices); - write!(s, "]").unwrap(); - err.span_label(span, s); - err.span_note(prev_span, "previous use here"); - err.emit(); } } else { - self.found = Some((span, concrete_type, indices)); + self.found = Some((span, concrete_type)); } } else { debug!( @@ -557,7 +532,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { debug!("find_existential_constraints: visiting {:?}", it); let def_id = self.tcx.hir().local_def_id(it.hir_id); // The opaque type itself or its children are not within its reveal scope. - if def_id != self.def_id { + if def_id.to_def_id() != self.def_id { self.check(def_id); intravisit::walk_item(self, it); } @@ -566,7 +541,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { debug!("find_existential_constraints: visiting {:?}", it); let def_id = self.tcx.hir().local_def_id(it.hir_id); // The opaque type itself or its children are not within its reveal scope. - if def_id != self.def_id { + if def_id.to_def_id() != self.def_id { self.check(def_id); intravisit::walk_impl_item(self, it); } @@ -579,9 +554,9 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } } - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let hir_id = tcx.hir().as_local_hir_id(def_id); let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id, tcx, found: None }; + let mut locator = ConstraintLocator { def_id: def_id.to_def_id(), tcx, found: None }; debug!("find_opaque_ty_constraints: scope={:?}", scope); @@ -612,7 +587,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } match locator.found { - Some((_, ty, _)) => ty, + Some((_, ty)) => ty, None => { let span = tcx.def_span(def_id); tcx.sess.span_err(span, "could not find defining uses"); @@ -623,7 +598,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { fn infer_placeholder_type( tcx: TyCtxt<'_>, - def_id: DefId, + def_id: LocalDefId, body_id: hir::BodyId, span: Span, item_ident: Ident, @@ -661,7 +636,11 @@ fn infer_placeholder_type( } } - ty + // Typeck doesn't expect erased regions to be returned from `type_of`. + tcx.fold_regions(&ty, &mut false, |r, _| match r { + ty::ReErased => tcx.lifetimes.re_static, + _ => r, + }) } fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { diff --git a/src/librustc_typeck/constrained_generic_params.rs b/src/librustc_typeck/constrained_generic_params.rs index b16aa6ff3b2e4..07ff3bc85cc19 100644 --- a/src/librustc_typeck/constrained_generic_params.rs +++ b/src/librustc_typeck/constrained_generic_params.rs @@ -1,6 +1,6 @@ -use rustc::ty::fold::{TypeFoldable, TypeVisitor}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; +use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::source_map::Span; #[derive(Clone, PartialEq, Eq, Hash, Debug)] diff --git a/src/librustc_typeck/expr_use_visitor.rs b/src/librustc_typeck/expr_use_visitor.rs index a45d8ce6823a3..9ba00faec4978 100644 --- a/src/librustc_typeck/expr_use_visitor.rs +++ b/src/librustc_typeck/expr_use_visitor.rs @@ -7,12 +7,12 @@ pub use self::ConsumeMode::*; // Export these here so that Clippy can use them. pub use mc::{Place, PlaceBase, Projection}; -use rustc::ty::{self, adjustment, TyCtxt}; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::PatKind; use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::{self, adjustment, TyCtxt}; use crate::mem_categorization as mc; use rustc_span::Span; @@ -84,7 +84,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { pub fn new( delegate: &'a mut (dyn Delegate<'tcx> + 'a), infcx: &'a InferCtxt<'a, 'tcx>, - body_owner: DefId, + body_owner: LocalDefId, param_env: ty::ParamEnv<'tcx>, tables: &'a ty::TypeckTables<'tcx>, ) -> Self { @@ -220,7 +220,31 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.borrow_expr(&base, bk); } - hir::ExprKind::InlineAsm(ref ia) => { + hir::ExprKind::InlineAsm(ref asm) => { + for op in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } + | hir::InlineAsmOperand::Const { expr, .. } + | hir::InlineAsmOperand::Sym { expr, .. } => self.consume_expr(expr), + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + self.mutate_expr(expr); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + self.mutate_expr(expr); + } + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.consume_expr(in_expr); + if let Some(out_expr) = out_expr { + self.mutate_expr(out_expr); + } + } + } + } + } + + hir::ExprKind::LlvmInlineAsm(ref ia) => { for (o, output) in ia.inner.outputs.iter().zip(ia.outputs_exprs) { if o.is_indirect { self.consume_expr(output); @@ -519,7 +543,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { for &var_id in upvars.keys() { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_def_id.expect_local(), + closure_expr_id: closure_def_id, }; let upvar_capture = self.mc.tables.upvar_capture(upvar_id); let captured_place = return_if_err!(self.cat_captured_var( diff --git a/src/librustc_typeck/impl_wf_check.rs b/src/librustc_typeck/impl_wf_check.rs index 42cb4fcf85dcb..e13d9ea2b2626 100644 --- a/src/librustc_typeck/impl_wf_check.rs +++ b/src/librustc_typeck/impl_wf_check.rs @@ -11,13 +11,13 @@ use crate::constrained_generic_params as cgp; use min_specialization::check_min_specialization; -use rustc::ty::query::Providers; -use rustc::ty::{self, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::Span; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -59,7 +59,7 @@ pub fn impl_wf_check(tcx: TyCtxt<'_>) { // but it's one that we must perform earlier than the rest of // WfCheck. for &module in tcx.hir().krate().modules.keys() { - tcx.ensure().check_mod_impl_wf(tcx.hir().local_def_id(module)); + tcx.ensure().check_mod_impl_wf(tcx.hir().local_def_id(module).to_def_id()); } } @@ -85,7 +85,7 @@ impl ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { enforce_impl_params_are_constrained(self.tcx, impl_def_id, items); enforce_impl_items_are_distinct(self.tcx, items); if self.min_specialization { - check_min_specialization(self.tcx, impl_def_id, item.span); + check_min_specialization(self.tcx, impl_def_id.to_def_id(), item.span); } } } @@ -97,7 +97,7 @@ impl ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { fn enforce_impl_params_are_constrained( tcx: TyCtxt<'_>, - impl_def_id: DefId, + impl_def_id: LocalDefId, impl_item_refs: &[hir::ImplItemRef<'_>], ) { // Every lifetime used in an associated type must be constrained. @@ -147,7 +147,7 @@ fn enforce_impl_params_are_constrained( let predicates = tcx.predicates_of(def_id).instantiate_identity(tcx); cgp::parameters_for(&predicates, true) } - ty::AssocKind::Method | ty::AssocKind::Const => Vec::new(), + ty::AssocKind::Fn | ty::AssocKind::Const => Vec::new(), } }) .collect(); diff --git a/src/librustc_typeck/impl_wf_check/min_specialization.rs b/src/librustc_typeck/impl_wf_check/min_specialization.rs index ef94500f5c444..919bcc9943d48 100644 --- a/src/librustc_typeck/impl_wf_check/min_specialization.rs +++ b/src/librustc_typeck/impl_wf_check/min_specialization.rs @@ -67,16 +67,16 @@ use crate::constrained_generic_params as cgp; -use rustc::middle::region::ScopeTree; -use rustc::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; -use rustc::ty::trait_def::TraitSpecializationKind; -use rustc::ty::{self, InstantiatedPredicates, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, RegionckMode, TyCtxtInferExt}; use rustc_infer::traits::specialization_graph::Node; +use rustc_middle::middle::region::ScopeTree; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; +use rustc_middle::ty::trait_def::TraitSpecializationKind; +use rustc_middle::ty::{self, InstantiatedPredicates, TyCtxt, TypeFoldable}; use rustc_span::Span; use rustc_trait_selection::traits::{self, translate_substs, wf}; @@ -130,7 +130,14 @@ fn check_always_applicable( check_static_lifetimes(tcx, &parent_substs, span); check_duplicate_params(tcx, impl1_substs, &parent_substs, span); - check_predicates(infcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); + check_predicates( + infcx, + impl1_def_id.expect_local(), + impl1_substs, + impl2_node, + impl2_substs, + span, + ); } } @@ -287,7 +294,7 @@ fn check_static_lifetimes<'tcx>( /// including the `Self`-type. fn check_predicates<'tcx>( infcx: &InferCtxt<'_, 'tcx>, - impl1_def_id: DefId, + impl1_def_id: LocalDefId, impl1_substs: SubstsRef<'tcx>, impl2_node: Node, impl2_substs: SubstsRef<'tcx>, @@ -322,7 +329,7 @@ fn check_predicates<'tcx>( // which is sound because we forbid impls like the following // // impl AlwaysApplicable for D { } - let always_applicable_traits: Vec<_> = impl1_predicates + let always_applicable_traits = impl1_predicates .predicates .iter() .filter(|predicate| { @@ -331,15 +338,14 @@ fn check_predicates<'tcx>( Some(TraitSpecializationKind::AlwaysApplicable) ) }) - .copied() - .collect(); + .copied(); // Include the well-formed predicates of the type parameters of the impl. for ty in tcx.impl_trait_ref(impl1_def_id).unwrap().substs.types() { if let Some(obligations) = wf::obligations( infcx, tcx.param_env(impl1_def_id), - tcx.hir().as_local_hir_id(impl1_def_id).unwrap(), + tcx.hir().as_local_hir_id(impl1_def_id), ty, span, ) { @@ -348,7 +354,10 @@ fn check_predicates<'tcx>( .extend(obligations.into_iter().map(|obligation| obligation.predicate)) } } - impl2_predicates.predicates.extend(traits::elaborate_predicates(tcx, always_applicable_traits)); + impl2_predicates.predicates.extend( + traits::elaborate_predicates(tcx, always_applicable_traits) + .map(|obligation| obligation.predicate), + ); for predicate in impl1_predicates.predicates { if !impl2_predicates.predicates.contains(&predicate) { @@ -404,6 +413,7 @@ fn trait_predicate_kind<'tcx>( | ty::Predicate::Subtype(_) | ty::Predicate::ObjectSafe(_) | ty::Predicate::ClosureKind(..) - | ty::Predicate::ConstEvaluatable(..) => None, + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => None, } } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index fd854c750184e..8d8a1b4d96761 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -72,7 +72,7 @@ This API is completely unstable and subject to change. extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; // This is used by Clippy. pub mod expr_use_visitor; @@ -89,18 +89,16 @@ mod outlives; mod structured_errors; mod variance; -use rustc::middle; -use rustc::ty::query::Providers; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::util; -use rustc::util::common::ErrorReported; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, ErrorReported}; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_hir::Node; use rustc_infer::infer::{InferOk, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::middle; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::util; use rustc_session::config::EntryFnType; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; @@ -112,10 +110,6 @@ use rustc_trait_selection::traits::{ use std::iter; use astconv::{AstConv, Bounds}; -pub struct TypeAndSubsts<'tcx> { - substs: SubstsRef<'tcx>, - ty: Ty<'tcx>, -} fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) { @@ -158,14 +152,14 @@ fn require_same_types<'tcx>( }) } -fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { - let main_id = tcx.hir().as_local_hir_id(main_def_id).unwrap(); +fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) { + let main_id = tcx.hir().as_local_hir_id(main_def_id); let main_span = tcx.def_span(main_def_id); let main_t = tcx.type_of(main_def_id); match main_t.kind { ty::FnDef(..) => { if let Some(Node::Item(it)) = tcx.hir().find(main_id) { - if let hir::ItemKind::Fn(.., ref generics, _) = it.kind { + if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { let mut error = false; if !generics.params.is_empty() { let msg = "`main` function is not allowed to have generic \ @@ -188,6 +182,18 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { .emit(); error = true; } + if let hir::IsAsync::Async = sig.header.asyncness { + let span = tcx.sess.source_map().guess_head_span(it.span); + struct_span_err!( + tcx.sess, + span, + E0752, + "`main` function is not allowed to be `async`" + ) + .span_label(span, "`main` function is not allowed to be `async`") + .emit(); + error = true; + } if error { return; } @@ -225,14 +231,14 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { } } -fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { - let start_id = tcx.hir().as_local_hir_id(start_def_id).unwrap(); +fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) { + let start_id = tcx.hir().as_local_hir_id(start_def_id); let start_span = tcx.def_span(start_def_id); let start_t = tcx.type_of(start_def_id); match start_t.kind { ty::FnDef(..) => { if let Some(Node::Item(it)) = tcx.hir().find(start_id) { - if let hir::ItemKind::Fn(.., ref generics, _) = it.kind { + if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { let mut error = false; if !generics.params.is_empty() { struct_span_err!( @@ -256,6 +262,18 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { .emit(); error = true; } + if let hir::IsAsync::Async = sig.header.asyncness { + let span = tcx.sess.source_map().guess_head_span(it.span); + struct_span_err!( + tcx.sess, + span, + E0752, + "start is not allowed to be `async`" + ) + .span_label(span, "start is not allowed to be `async`") + .emit(); + error = true; + } if error { return; } @@ -360,7 +378,7 @@ pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { // scope. This is derived from the enclosing item-like thing. let env_node_id = tcx.hir().get_parent_item(hir_ty.hir_id); let env_def_id = tcx.hir().local_def_id(env_node_id); - let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id); + let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); astconv::AstConv::ast_ty_to_ty(&item_cx, hir_ty) } @@ -368,20 +386,21 @@ pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { pub fn hir_trait_to_predicates<'tcx>( tcx: TyCtxt<'tcx>, hir_trait: &hir::TraitRef<'_>, + self_ty: Ty<'tcx>, ) -> Bounds<'tcx> { // In case there are any projections, etc., find the "environment" // def-ID that will be used to determine the traits/predicates in // scope. This is derived from the enclosing item-like thing. let env_hir_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); let env_def_id = tcx.hir().local_def_id(env_hir_id); - let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id); + let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); let mut bounds = Bounds::default(); let _ = AstConv::instantiate_poly_trait_ref_inner( &item_cx, hir_trait, DUMMY_SP, hir::Constness::NotConst, - tcx.types.err, + self_ty, &mut bounds, true, ); diff --git a/src/librustc_typeck/mem_categorization.rs b/src/librustc_typeck/mem_categorization.rs index 7d8bf71cf97b7..71f3e2d03c9ff 100644 --- a/src/librustc_typeck/mem_categorization.rs +++ b/src/librustc_typeck/mem_categorization.rs @@ -30,7 +30,7 @@ //! - `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 +//! 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. //! @@ -48,14 +48,14 @@ //! result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference //! tied to `x`. The type of `x'` will be a borrowed pointer. -use rustc::ty::adjustment; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::adjustment; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::PatKind; use rustc_infer::infer::InferCtxt; use rustc_span::Span; @@ -140,7 +140,7 @@ crate struct MemCategorizationContext<'a, 'tcx> { crate tables: &'a ty::TypeckTables<'tcx>, infcx: &'a InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, - body_owner: DefId, + body_owner: LocalDefId, upvars: Option<&'tcx FxIndexMap>, } @@ -151,7 +151,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { crate fn new( infcx: &'a InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, - body_owner: DefId, + body_owner: LocalDefId, tables: &'a ty::TypeckTables<'tcx>, ) -> MemCategorizationContext<'a, 'tcx> { MemCategorizationContext { @@ -406,6 +406,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::LlvmInlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), } @@ -421,12 +422,15 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); match res { - Res::Def(DefKind::Ctor(..), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::ConstParam, _) - | Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) + Res::Def( + DefKind::Ctor(..) + | DefKind::Const + | DefKind::ConstParam + | DefKind::AssocConst + | DefKind::Fn + | DefKind::AssocFn, + _, + ) | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, span, expr_ty)), Res::Def(DefKind::Static, _) => Ok(Place { @@ -470,7 +474,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_expr_def_id.expect_local(), + closure_expr_id: closure_expr_def_id, }; let var_ty = self.node_ty(var_id)?; diff --git a/src/librustc_typeck/outlives/explicit.rs b/src/librustc_typeck/outlives/explicit.rs index 7500c39cf216b..66daf0e7f7d9d 100644 --- a/src/librustc_typeck/outlives/explicit.rs +++ b/src/librustc_typeck/outlives/explicit.rs @@ -1,6 +1,6 @@ -use rustc::ty::{self, OutlivesPredicate, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, OutlivesPredicate, TyCtxt}; use super::utils::*; @@ -58,7 +58,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::Subtype(..) - | ty::Predicate::ConstEvaluatable(..) => (), + | ty::Predicate::ConstEvaluatable(..) + | ty::Predicate::ConstEquate(..) => (), } } diff --git a/src/librustc_typeck/outlives/implicit_infer.rs b/src/librustc_typeck/outlives/implicit_infer.rs index 44473fee643c6..15c72f8704f65 100644 --- a/src/librustc_typeck/outlives/implicit_infer.rs +++ b/src/librustc_typeck/outlives/implicit_infer.rs @@ -1,10 +1,10 @@ -use rustc::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Node; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use super::explicit::ExplicitPredicatesMap; @@ -57,7 +57,7 @@ impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> { debug!("InferVisitor::visit_item(item={:?})", item_did); - let hir_id = self.tcx.hir().as_local_hir_id(item_did).expect("expected local def-id"); + let hir_id = self.tcx.hir().as_local_hir_id(item_did); let item = match self.tcx.hir().get(hir_id) { Node::Item(item) => item, _ => bug!(), @@ -66,7 +66,7 @@ impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> { let mut item_required_predicates = RequiredPredicates::default(); match item.kind { hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) => { - let adt_def = self.tcx.adt_def(item_did); + let adt_def = self.tcx.adt_def(item_did.to_def_id()); // Iterate over all fields in item_did for field_def in adt_def.all_fields() { @@ -99,10 +99,10 @@ impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> { // we walk the crates again and re-calculate predicates for all // items. let item_predicates_len: usize = - self.global_inferred_outlives.get(&item_did).map(|p| p.len()).unwrap_or(0); + self.global_inferred_outlives.get(&item_did.to_def_id()).map(|p| p.len()).unwrap_or(0); if item_required_predicates.len() > item_predicates_len { *self.predicates_added = true; - self.global_inferred_outlives.insert(item_did, item_required_predicates); + self.global_inferred_outlives.insert(item_did.to_def_id(), item_required_predicates); } } @@ -119,7 +119,15 @@ fn insert_required_predicates_to_be_wf<'tcx>( required_predicates: &mut RequiredPredicates<'tcx>, explicit_map: &mut ExplicitPredicatesMap<'tcx>, ) { - for ty in field_ty.walk() { + for arg in field_ty.walk() { + let ty = match arg.unpack() { + GenericArgKind::Type(ty) => ty, + + // No predicates from lifetimes or constants, except potentially + // constants' types, but `walk` will get to them as well. + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, + }; + match ty.kind { // The field is of type &'a T which means that we will have // a predicate requirement of T: 'a (T outlives 'a). @@ -303,7 +311,7 @@ pub fn check_explicit_predicates<'tcx>( // 'b`. if let Some(self_ty) = ignored_self_ty { if let GenericArgKind::Type(ty) = outlives_predicate.0.unpack() { - if ty.walk().any(|ty| ty == self_ty) { + if ty.walk().any(|arg| arg == self_ty.into()) { debug!("skipping self ty = {:?}", &ty); continue; } diff --git a/src/librustc_typeck/outlives/mod.rs b/src/librustc_typeck/outlives/mod.rs index f7b6e0fce5a21..a49d8e5ed0f0a 100644 --- a/src/librustc_typeck/outlives/mod.rs +++ b/src/librustc_typeck/outlives/mod.rs @@ -1,9 +1,9 @@ use hir::Node; -use rustc::ty::query::Providers; -use rustc::ty::subst::GenericArgKind; -use rustc::ty::{self, CratePredicatesMap, TyCtxt}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, CratePredicatesMap, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -18,7 +18,7 @@ pub fn provide(providers: &mut Providers<'_>) { } fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] { - let id = tcx.hir().as_local_hir_id(item_def_id).expect("expected local def-id"); + let id = tcx.hir().as_local_hir_id(item_def_id.expect_local()); match tcx.hir().get(id) { Node::Item(item) => match item.kind { @@ -58,7 +58,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate } } -fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> &CratePredicatesMap<'_> { +fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CratePredicatesMap<'_> { assert_eq!(crate_num, LOCAL_CRATE); // Compute a map from each struct/enum/union S to the **explicit** @@ -105,5 +105,5 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> &CratePredic }) .collect(); - tcx.arena.alloc(ty::CratePredicatesMap { predicates }) + ty::CratePredicatesMap { predicates } } diff --git a/src/librustc_typeck/outlives/test.rs b/src/librustc_typeck/outlives/test.rs index 980d58ad939d7..abe9319d71c59 100644 --- a/src/librustc_typeck/outlives/test.rs +++ b/src/librustc_typeck/outlives/test.rs @@ -1,7 +1,7 @@ -use rustc::ty::TyCtxt; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { @@ -18,7 +18,7 @@ impl ItemLikeVisitor<'tcx> for OutlivesTest<'tcx> { // For unit testing: check for a special "rustc_outlives" // attribute and report an error with various results if found. - if self.tcx.has_attr(item_def_id, sym::rustc_outlives) { + if self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_outlives) { let inferred_outlives_of = self.tcx.inferred_outlives_of(item_def_id); struct_span_err!(self.tcx.sess, item.span, E0640, "{:?}", inferred_outlives_of).emit(); } diff --git a/src/librustc_typeck/outlives/utils.rs b/src/librustc_typeck/outlives/utils.rs index e1bd78e5113d1..3bbe3e34a6a45 100644 --- a/src/librustc_typeck/outlives/utils.rs +++ b/src/librustc_typeck/outlives/utils.rs @@ -1,6 +1,6 @@ -use rustc::ty::outlives::Component; -use rustc::ty::subst::{GenericArg, GenericArgKind}; -use rustc::ty::{self, Region, RegionKind, Ty, TyCtxt}; +use rustc_middle::ty::outlives::Component; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, Region, RegionKind, Ty, TyCtxt}; use rustc_span::Span; use smallvec::smallvec; use std::collections::BTreeMap; diff --git a/src/librustc_typeck/structured_errors.rs b/src/librustc_typeck/structured_errors.rs index a4f8472ae2891..83125a3e2feb6 100644 --- a/src/librustc_typeck/structured_errors.rs +++ b/src/librustc_typeck/structured_errors.rs @@ -1,5 +1,5 @@ -use rustc::ty::{Ty, TypeFoldable}; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_middle::ty::{Ty, TypeFoldable}; use rustc_session::Session; use rustc_span::Span; diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 11612066d44b2..3a680f55c8c30 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -3,11 +3,11 @@ //! The second pass over the AST determines the set of constraints. //! We walk the set of items and, for each member, generate new constraints. -use hir::def_id::DefId; -use rustc::ty::subst::{GenericArgKind, SubstsRef}; -use rustc::ty::{self, Ty, TyCtxt}; +use hir::def_id::{DefId, LocalDefId}; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use super::terms::VarianceTerm::*; use super::terms::*; @@ -128,16 +128,16 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.terms_cx.tcx } - fn build_constraints_for_item(&mut self, def_id: DefId) { + fn build_constraints_for_item(&mut self, def_id: LocalDefId) { let tcx = self.tcx(); - debug!("build_constraints_for_item({})", tcx.def_path_str(def_id)); + debug!("build_constraints_for_item({})", tcx.def_path_str(def_id.to_def_id())); // Skip items with no generics - there's nothing to infer in them. if tcx.generics_of(def_id).count() == 0 { return; } - let id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let id = tcx.hir().as_local_hir_id(def_id); let inferred_start = self.terms_cx.inferred_starts[&id]; let current_item = &CurrentItem { inferred_start }; match tcx.type_of(def_id).kind { @@ -315,11 +315,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_region(current, r, contra); if let Some(poly_trait_ref) = data.principal() { - let poly_trait_ref = - poly_trait_ref.with_self_ty(self.tcx(), self.tcx().types.err); - self.add_constraints_from_trait_ref( + self.add_constraints_from_invariant_substs( current, - *poly_trait_ref.skip_binder(), + poly_trait_ref.skip_binder().substs, variance, ); } @@ -346,11 +344,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // types, where we use Error as the Self type } - ty::Placeholder(..) - | ty::UnnormalizedProjection(..) - | ty::GeneratorWitness(..) - | ty::Bound(..) - | ty::Infer(..) => { + ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Bound(..) | ty::Infer(..) => { bug!( "unexpected type encountered in \ variance inference: {}", @@ -379,7 +373,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { return; } - let (local, remote) = if let Some(id) = self.tcx().hir().as_local_hir_id(def_id) { + let (local, remote) = if let Some(def_id) = def_id.as_local() { + let id = self.tcx().hir().as_local_hir_id(def_id); (Some(self.terms_cx.inferred_starts[&id]), None) } else { (None, Some(self.tcx().variances_of(def_id))) diff --git a/src/librustc_typeck/variance/mod.rs b/src/librustc_typeck/variance/mod.rs index 3cbb42bb5f309..631f551ade4fe 100644 --- a/src/librustc_typeck/variance/mod.rs +++ b/src/librustc_typeck/variance/mod.rs @@ -4,10 +4,10 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html use hir::Node; -use rustc::ty::query::Providers; -use rustc::ty::{self, CrateVariancesMap, TyCtxt}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt}; /// Defines the `TermsContext` basically houses an arena where we can /// allocate terms. @@ -29,16 +29,16 @@ pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { variances_of, crate_variances, ..*providers }; } -fn crate_variances(tcx: TyCtxt<'_>, crate_num: CrateNum) -> &CrateVariancesMap<'_> { +fn crate_variances(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateVariancesMap<'_> { assert_eq!(crate_num, LOCAL_CRATE); let mut arena = arena::TypedArena::default(); let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena); let constraints_cx = constraints::add_constraints_from_crate(terms_cx); - tcx.arena.alloc(solve::solve_constraints(constraints_cx)) + solve::solve_constraints(constraints_cx) } fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { - let id = tcx.hir().as_local_hir_id(item_def_id).expect("expected local def-id"); + let id = tcx.hir().as_local_hir_id(item_def_id.expect_local()); let unsupported = || { // Variance not relevant. span_bug!(tcx.hir().span(id), "asked to compute variance for wrong kind of item") diff --git a/src/librustc_typeck/variance/solve.rs b/src/librustc_typeck/variance/solve.rs index e285f44123af9..7402117a7ebb1 100644 --- a/src/librustc_typeck/variance/solve.rs +++ b/src/librustc_typeck/variance/solve.rs @@ -5,9 +5,9 @@ //! optimal solution to the constraints. The final variance for each //! inferred is then written into the `variance_map` in the tcx. -use rustc::ty; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; +use rustc_middle::ty; use super::constraints::*; use super::terms::VarianceTerm::*; @@ -115,7 +115,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { } } - (def_id, &*variances) + (def_id.to_def_id(), &*variances) }) .collect() } diff --git a/src/librustc_typeck/variance/terms.rs b/src/librustc_typeck/variance/terms.rs index 7e6ec96b379d8..fe585826d2205 100644 --- a/src/librustc_typeck/variance/terms.rs +++ b/src/librustc_typeck/variance/terms.rs @@ -10,10 +10,10 @@ // a variable. use arena::TypedArena; -use rustc::ty::{self, TyCtxt}; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::HirIdMap; +use rustc_middle::ty::{self, TyCtxt}; use std::fmt; use self::VarianceTerm::*; @@ -94,7 +94,7 @@ fn lang_items(tcx: TyCtxt<'_>) -> Vec<(hir::HirId, Vec)> { all.into_iter() // iterating over (Option, Variance) .filter(|&(ref d, _)| d.is_some()) .map(|(d, v)| (d.unwrap(), v)) // (DefId, Variance) - .filter_map(|(d, v)| tcx.hir().as_local_hir_id(d).map(|n| (n, v))) // (HirId, Variance) + .filter_map(|(d, v)| d.as_local().map(|d| tcx.hir().as_local_hir_id(d)).map(|n| (n, v))) // (HirId, Variance) .collect() } diff --git a/src/librustc_typeck/variance/test.rs b/src/librustc_typeck/variance/test.rs index ee94b1015a1f4..1aab89310c6e8 100644 --- a/src/librustc_typeck/variance/test.rs +++ b/src/librustc_typeck/variance/test.rs @@ -1,7 +1,7 @@ -use rustc::ty::TyCtxt; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; pub fn test_variance(tcx: TyCtxt<'_>) { @@ -18,7 +18,7 @@ impl ItemLikeVisitor<'tcx> for VarianceTest<'tcx> { // For unit testing: check for a special "rustc_variance" // attribute and report an error with various results if found. - if self.tcx.has_attr(item_def_id, sym::rustc_variance) { + if self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_variance) { let variances_of = self.tcx.variances_of(item_def_id); struct_span_err!(self.tcx.sess, item.span, E0208, "{:?}", variances_of).emit(); } diff --git a/src/librustc_typeck/variance/xform.rs b/src/librustc_typeck/variance/xform.rs index aed0dfca91a45..027f0859fcd54 100644 --- a/src/librustc_typeck/variance/xform.rs +++ b/src/librustc_typeck/variance/xform.rs @@ -1,4 +1,4 @@ -use rustc::ty; +use rustc_middle::ty; pub fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance { // Greatest lower bound of the variance lattice as diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index c85b21a55007f..144c1699a3ca8 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -1,6 +1,7 @@ -use rustc::ty::{self, Region, RegionVid, TypeFoldable}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; +use rustc_hir::lang_items; +use rustc_middle::ty::{self, Region, RegionVid, TypeFoldable}; use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult}; use std::fmt::Debug; @@ -314,25 +315,28 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { tcx: TyCtxt<'tcx>, pred: ty::Predicate<'tcx>, ) -> FxHashSet { - pred.walk_tys() - .flat_map(|t| { - let mut regions = FxHashSet::default(); - tcx.collect_regions(&t, &mut regions); - - regions.into_iter().flat_map(|r| { - match r { - // We only care about late bound regions, as we need to add them - // to the 'for<>' section - &ty::ReLateBound(_, ty::BoundRegion::BrNamed(_, name)) => { - Some(GenericParamDef { - name: name.to_string(), - kind: GenericParamDefKind::Lifetime, - }) - } - &ty::ReVar(_) | &ty::ReEarlyBound(_) | &ty::ReStatic => None, - _ => panic!("Unexpected region type {:?}", r), - } - }) + let regions = match pred { + ty::Predicate::Trait(poly_trait_pred, _) => { + tcx.collect_referenced_late_bound_regions(&poly_trait_pred) + } + ty::Predicate::Projection(poly_proj_pred) => { + tcx.collect_referenced_late_bound_regions(&poly_proj_pred) + } + _ => return FxHashSet::default(), + }; + + regions + .into_iter() + .filter_map(|br| { + match br { + // We only care about named late bound regions, as we need to add them + // to the 'for<>' section + ty::BrNamed(_, name) => Some(GenericParamDef { + name: name.to_string(), + kind: GenericParamDefKind::Lifetime, + }), + _ => None, + } }) .collect() } @@ -497,18 +501,15 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { // of the type. // Therefore, we make sure that we never add a ?Sized // bound for projections - match &ty { - &Type::QPath { .. } => { - has_sized.insert(ty.clone()); - } - _ => {} + if let Type::QPath { .. } = ty { + has_sized.insert(ty.clone()); } if bounds.is_empty() { continue; } - let mut for_generics = self.extract_for_generics(tcx, orig_p.clone()); + let mut for_generics = self.extract_for_generics(tcx, orig_p); assert!(bounds.len() == 1); let mut b = bounds.pop().expect("bounds were empty"); diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index e66f869771794..d987505e42735 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -1,10 +1,10 @@ use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc::ty::subst::Subst; -use rustc::ty::{ToPredicate, WithConstness}; use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_infer::infer::{InferOk, TyCtxtInferExt}; use rustc_infer::traits; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{ToPredicate, WithConstness}; use rustc_span::DUMMY_SP; use super::*; diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 775d600fc3d4b..57d499e38a77b 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -360,6 +360,7 @@ impl<'a> fmt::Display for Html<'a> { "fuchsia" => "Fuchsia", "haiku" => "Haiku", "hermit" => "HermitCore", + "illumos" => "illumos", "ios" => "iOS", "l4re" => "L4Re", "linux" => "Linux", diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 3b26742e716f8..716263393ba8d 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -3,7 +3,7 @@ use super::*; use rustc_ast::ast::*; use rustc_ast::attr; use rustc_ast::with_default_globals; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; fn word_cfg(s: &str) -> Cfg { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 153f7af9f97ca..4bf3649dcc2e2 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -2,7 +2,6 @@ use std::iter::once; -use rustc::ty; use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -10,8 +9,10 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::Mutability; use rustc_metadata::creader::LoadedMacro; +use rustc_middle::ty; use rustc_mir::const_eval::is_min_const_fn; use rustc_span::hygiene::MacroKind; +use rustc_span::symbol::Symbol; use rustc_span::Span; use crate::clean::{self, GetDefId, ToSource, TypeKind}; @@ -20,7 +21,7 @@ use crate::doctree; use super::Clean; -type Attrs<'hir> = rustc::ty::Attributes<'hir>; +type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>; /// Attempt to inline a definition into this AST. /// @@ -37,7 +38,7 @@ type Attrs<'hir> = rustc::ty::Attributes<'hir>; pub fn try_inline( cx: &DocContext<'_>, res: Res, - name: ast::Name, + name: Symbol, attrs: Option>, visited: &mut FxHashSet, ) -> Option> { @@ -47,7 +48,7 @@ pub fn try_inline( } let mut ret = Vec::new(); - let attrs_clone = attrs.clone(); + let attrs_clone = attrs; let inner = match res { Res::Def(DefKind::Trait, did) => { @@ -278,7 +279,7 @@ fn build_type_alias_type(cx: &DocContext<'_>, did: DefId) -> Option } pub fn build_ty(cx: &DocContext, did: DefId) -> Option { - match cx.tcx.def_kind(did)? { + match cx.tcx.def_kind(did) { DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::Const | DefKind::Static => { Some(cx.tcx.type_of(did).clean(cx)) } @@ -292,7 +293,7 @@ pub fn build_impls(cx: &DocContext<'_>, did: DefId, attrs: Option>) -> let mut impls = Vec::new(); for &did in tcx.inherent_impls(did).iter() { - build_impl(cx, did, attrs.clone(), &mut impls); + build_impl(cx, did, attrs, &mut impls); } impls @@ -340,7 +341,8 @@ pub fn build_impl( } } - let for_ = if let Some(hir_id) = tcx.hir().as_local_hir_id(did) { + let for_ = if let Some(did) = did.as_local() { + let hir_id = tcx.hir().as_local_hir_id(did); match tcx.hir().expect_item(hir_id).kind { hir::ItemKind::Impl { self_ty, .. } => self_ty.clean(cx), _ => panic!("did given to build_impl was not an impl"), @@ -360,7 +362,8 @@ pub fn build_impl( } let predicates = tcx.explicit_predicates_of(did); - let (trait_items, generics) = if let Some(hir_id) = tcx.hir().as_local_hir_id(did) { + let (trait_items, generics) = if let Some(did) = did.as_local() { + let hir_id = tcx.hir().as_local_hir_id(did); match tcx.hir().expect_item(hir_id).kind { hir::ItemKind::Impl { ref generics, ref items, .. } => ( items.iter().map(|item| tcx.hir().impl_item(item.id).clean(cx)).collect::>(), @@ -451,7 +454,11 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) name: None, attrs: clean::Attributes::default(), source: clean::Span::empty(), - def_id: cx.tcx.hir().local_def_id_from_node_id(ast::CRATE_NODE_ID), + def_id: cx + .tcx + .hir() + .local_def_id_from_node_id(ast::CRATE_NODE_ID) + .to_def_id(), visibility: clean::Public, stability: None, deprecation: None, @@ -482,8 +489,9 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) } pub fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { - if let Some(node_id) = cx.tcx.hir().as_local_hir_id(did) { - cx.tcx.hir().hir_to_pretty_string(node_id) + if let Some(did) = did.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(did); + rustc_hir_pretty::id_to_string(&cx.tcx.hir(), hir_id) } else { cx.tcx.rendered_const(did) } @@ -494,11 +502,9 @@ fn build_const(cx: &DocContext<'_>, did: DefId) -> clean::Constant { type_: cx.tcx.type_of(did).clean(cx), expr: print_inlined_const(cx, did), value: clean::utils::print_evaluated_const(cx, did), - is_literal: cx - .tcx - .hir() - .as_local_hir_id(did) - .map_or(false, |hir_id| clean::utils::is_literal_expr(cx, hir_id)), + is_literal: did.as_local().map_or(false, |did| { + clean::utils::is_literal_expr(cx, cx.tcx.hir().as_local_hir_id(did)) + }), } } @@ -510,7 +516,7 @@ fn build_static(cx: &DocContext<'_>, did: DefId, mutable: bool) -> clean::Static } } -fn build_macro(cx: &DocContext<'_>, did: DefId, name: ast::Name) -> clean::ItemEnum { +fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemEnum { let imported_from = cx.tcx.original_crate_name(did.krate); match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) { LoadedMacro::MacroDef(def, _) => { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7dfcf0a637eb3..c130ed3f46dbb 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -9,13 +9,7 @@ mod simplify; pub mod types; pub mod utils; -use rustc::middle::lang_items; -use rustc::middle::resolve_lifetime as rl; -use rustc::middle::stability; -use rustc::ty::fold::TypeFolder; -use rustc::ty::subst::InternalSubsts; -use rustc::ty::{self, AdtKind, Lift, Ty, TyCtxt}; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -23,9 +17,14 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_middle::middle::resolve_lifetime as rl; +use rustc_middle::middle::stability; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt}; use rustc_mir::const_eval::is_min_const_fn; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, Pos}; use rustc_typeck::hir_ty_to_ty; @@ -33,7 +32,6 @@ use std::collections::hash_map::Entry; use std::default::Default; use std::hash::Hash; use std::rc::Rc; -use std::u32; use std::{mem, vec}; use crate::core::{self, DocContext, ImplTraitParam}; @@ -86,15 +84,6 @@ impl, U> Clean> for Option { } } -impl Clean for ty::Binder -where - T: Clean, -{ - fn clean(&self, cx: &DocContext<'_>) -> U { - self.skip_binder().clean(cx) - } -} - impl Clean for CrateNum { fn clean(&self, cx: &DocContext<'_>) -> ExternalCrate { let root = DefId { krate: *self, index: CRATE_DEF_INDEX }; @@ -148,15 +137,16 @@ impl Clean for CrateNum { .filter_map(|&id| { let item = cx.tcx.hir().expect_item(id.id); match item.kind { - hir::ItemKind::Mod(_) => { - as_primitive(Res::Def(DefKind::Mod, cx.tcx.hir().local_def_id(id.id))) - } + hir::ItemKind::Mod(_) => as_primitive(Res::Def( + DefKind::Mod, + cx.tcx.hir().local_def_id(id.id).to_def_id(), + )), hir::ItemKind::Use(ref path, hir::UseKind::Single) if item.vis.node.is_pub() => { as_primitive(path.res).map(|(_, prim, attrs)| { // Pretend the primitive is local. - (cx.tcx.hir().local_def_id(id.id), prim, attrs) + (cx.tcx.hir().local_def_id(id.id).to_def_id(), prim, attrs) }) } _ => None, @@ -202,14 +192,15 @@ impl Clean for CrateNum { .filter_map(|&id| { let item = cx.tcx.hir().expect_item(id.id); match item.kind { - hir::ItemKind::Mod(_) => { - as_keyword(Res::Def(DefKind::Mod, cx.tcx.hir().local_def_id(id.id))) - } + hir::ItemKind::Mod(_) => as_keyword(Res::Def( + DefKind::Mod, + cx.tcx.hir().local_def_id(id.id).to_def_id(), + )), hir::ItemKind::Use(ref path, hir::UseKind::Single) if item.vis.node.is_pub() => { as_keyword(path.res).map(|(_, prim, attrs)| { - (cx.tcx.hir().local_def_id(id.id), prim, attrs) + (cx.tcx.hir().local_def_id(id.id).to_def_id(), prim, attrs) }) } _ => None, @@ -284,7 +275,7 @@ impl Clean for doctree::Module<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), inner: ModuleItem(Module { is_crate: self.is_crate, items }), } } @@ -307,59 +298,66 @@ impl Clean for hir::GenericBound<'_> { } } -impl<'a, 'tcx> Clean for (&'a ty::TraitRef<'tcx>, Vec) { - fn clean(&self, cx: &DocContext<'_>) -> GenericBound { - let (trait_ref, ref bounds) = *self; +impl Clean for (ty::TraitRef<'_>, &[TypeBinding]) { + fn clean(&self, cx: &DocContext<'_>) -> Type { + let (trait_ref, bounds) = *self; inline::record_extern_fqn(cx, trait_ref.def_id, TypeKind::Trait); let path = external_path( cx, cx.tcx.item_name(trait_ref.def_id), Some(trait_ref.def_id), true, - bounds.clone(), + bounds.to_vec(), trait_ref.substs, ); debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs); + ResolvedPath { path, param_names: None, did: trait_ref.def_id, is_generic: false } + } +} + +impl<'tcx> Clean for ty::TraitRef<'tcx> { + fn clean(&self, cx: &DocContext<'_>) -> GenericBound { + GenericBound::TraitBound( + PolyTrait { trait_: (*self, &[][..]).clean(cx), generic_params: vec![] }, + hir::TraitBoundModifier::None, + ) + } +} + +impl Clean for (ty::PolyTraitRef<'_>, &[TypeBinding]) { + fn clean(&self, cx: &DocContext<'_>) -> GenericBound { + let (poly_trait_ref, bounds) = *self; + let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap(); + // collect any late bound regions - let mut late_bounds = vec![]; - for ty_s in trait_ref.input_types().skip(1) { - if let ty::Tuple(ts) = ty_s.kind { - for &ty_s in ts { - if let ty::Ref(ref reg, _, _) = ty_s.expect_ty().kind { - if let &ty::RegionKind::ReLateBound(..) = *reg { - debug!(" hit an ReLateBound {:?}", reg); - if let Some(Lifetime(name)) = reg.clean(cx) { - late_bounds.push(GenericParamDef { - name, - kind: GenericParamDefKind::Lifetime, - }); - } - } - } - } - } - } + let late_bound_regions: Vec<_> = cx + .tcx + .collect_referenced_late_bound_regions(&poly_trait_ref) + .into_iter() + .filter_map(|br| match br { + ty::BrNamed(_, name) => Some(GenericParamDef { + name: name.to_string(), + kind: GenericParamDefKind::Lifetime, + }), + _ => None, + }) + .collect(); GenericBound::TraitBound( PolyTrait { - trait_: ResolvedPath { - path, - param_names: None, - did: trait_ref.def_id, - is_generic: false, - }, - generic_params: late_bounds, + trait_: (*poly_trait_ref.skip_binder(), bounds).clean(cx), + generic_params: late_bound_regions, }, hir::TraitBoundModifier::None, ) } } -impl<'tcx> Clean for ty::TraitRef<'tcx> { +impl<'tcx> Clean for ty::PolyTraitRef<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> GenericBound { - (self, vec![]).clean(cx) + (*self, &[][..]).clean(cx) } } @@ -379,18 +377,18 @@ impl<'tcx> Clean>> for InternalSubsts<'tcx> { impl Clean for hir::Lifetime { fn clean(&self, cx: &DocContext<'_>) -> Lifetime { - if self.hir_id != hir::DUMMY_HIR_ID { - let def = cx.tcx.named_region(self.hir_id); - match def { - Some(rl::Region::EarlyBound(_, node_id, _)) - | Some(rl::Region::LateBound(_, node_id, _)) - | Some(rl::Region::Free(_, node_id)) => { - if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() { - return lt; - } + let def = cx.tcx.named_region(self.hir_id); + match def { + Some( + rl::Region::EarlyBound(_, node_id, _) + | rl::Region::LateBound(_, node_id, _) + | rl::Region::Free(_, node_id), + ) => { + if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() { + return lt; } - _ => {} } + _ => {} } Lifetime(self.name.ident().to_string()) } @@ -423,7 +421,10 @@ impl Clean for hir::GenericParam<'_> { impl Clean for hir::ConstArg { fn clean(&self, cx: &DocContext<'_>) -> Constant { Constant { - type_: cx.tcx.type_of(cx.tcx.hir().body_owner_def_id(self.value.body)).clean(cx), + type_: cx + .tcx + .type_of(cx.tcx.hir().body_owner_def_id(self.value.body).to_def_id()) + .clean(cx), expr: print_const_expr(cx, self.value.body), value: None, is_literal: is_literal_expr(cx, self.value.body.hir_id), @@ -480,7 +481,7 @@ impl Clean for hir::WherePredicate<'_> { impl<'a> Clean> for ty::Predicate<'a> { fn clean(&self, cx: &DocContext<'_>) -> Option { - use rustc::ty::Predicate; + use rustc_middle::ty::Predicate; match *self { Predicate::Trait(ref pred, _) => Some(pred.clean(cx)), @@ -492,21 +493,23 @@ impl<'a> Clean> for ty::Predicate<'a> { Predicate::WellFormed(..) | Predicate::ObjectSafe(..) | Predicate::ClosureKind(..) - | Predicate::ConstEvaluatable(..) => panic!("not user writable"), + | Predicate::ConstEvaluatable(..) + | Predicate::ConstEquate(..) => panic!("not user writable"), } } } -impl<'a> Clean for ty::TraitPredicate<'a> { +impl<'a> Clean for ty::PolyTraitPredicate<'a> { fn clean(&self, cx: &DocContext<'_>) -> WherePredicate { + let poly_trait_ref = self.map_bound(|pred| pred.trait_ref); WherePredicate::BoundPredicate { - ty: self.trait_ref.self_ty().clean(cx), - bounds: vec![self.trait_ref.clean(cx)], + ty: poly_trait_ref.self_ty().clean(cx), + bounds: vec![poly_trait_ref.clean(cx)], } } } -impl<'tcx> Clean for ty::SubtypePredicate<'tcx> { +impl<'tcx> Clean for ty::PolySubtypePredicate<'tcx> { fn clean(&self, _cx: &DocContext<'_>) -> WherePredicate { panic!( "subtype predicates are an internal rustc artifact \ @@ -516,16 +519,13 @@ impl<'tcx> Clean for ty::SubtypePredicate<'tcx> { } impl<'tcx> Clean> - for ty::OutlivesPredicate, ty::Region<'tcx>> + for ty::PolyOutlivesPredicate, ty::Region<'tcx>> { fn clean(&self, cx: &DocContext<'_>) -> Option { - let ty::OutlivesPredicate(ref a, ref b) = *self; + let ty::OutlivesPredicate(a, b) = self.skip_binder(); - match (a, b) { - (ty::ReEmpty(_), ty::ReEmpty(_)) => { - return None; - } - _ => {} + if let (ty::ReEmpty(_), ty::ReEmpty(_)) = (a, b) { + return None; } Some(WherePredicate::RegionPredicate { @@ -535,13 +535,12 @@ impl<'tcx> Clean> } } -impl<'tcx> Clean> for ty::OutlivesPredicate, ty::Region<'tcx>> { +impl<'tcx> Clean> for ty::PolyOutlivesPredicate, ty::Region<'tcx>> { fn clean(&self, cx: &DocContext<'_>) -> Option { - let ty::OutlivesPredicate(ref ty, ref lt) = *self; + let ty::OutlivesPredicate(ty, lt) = self.skip_binder(); - match lt { - ty::ReEmpty(_) => return None, - _ => {} + if let ty::ReEmpty(_) = lt { + return None; } Some(WherePredicate::BoundPredicate { @@ -551,9 +550,10 @@ impl<'tcx> Clean> for ty::OutlivesPredicate, ty: } } -impl<'tcx> Clean for ty::ProjectionPredicate<'tcx> { +impl<'tcx> Clean for ty::PolyProjectionPredicate<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> WherePredicate { - WherePredicate::EqPredicate { lhs: self.projection_ty.clean(cx), rhs: self.ty.clean(cx) } + let ty::ProjectionPredicate { projection_ty, ty } = *self.skip_binder(); + WherePredicate::EqPredicate { lhs: projection_ty.clean(cx), rhs: ty.clean(cx) } } } @@ -627,7 +627,7 @@ impl Clean for hir::GenericParam<'_> { hir::GenericParamKind::Type { ref default, synthetic } => ( self.name.ident().name.clean(cx), GenericParamDefKind::Type { - did: cx.tcx.hir().local_def_id(self.hir_id), + did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), bounds: self.bounds.clean(cx), default: default.clean(cx), synthetic, @@ -636,7 +636,7 @@ impl Clean for hir::GenericParam<'_> { hir::GenericParamKind::Const { ref ty } => ( self.name.ident().name.clean(cx), GenericParamDefKind::Const { - did: cx.tcx.hir().local_def_id(self.hir_id), + did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), ty: ty.clean(cx), }, ), @@ -897,7 +897,7 @@ impl Clean for doctree::Function<'_> { enter_impl_trait(cx, || (self.generics.clean(cx), (self.decl, self.body).clean(cx))); let did = cx.tcx.hir().local_def_id(self.id); - let constness = if is_min_const_fn(cx.tcx, did) { + let constness = if is_min_const_fn(cx.tcx, did.to_def_id()) { hir::Constness::Const } else { hir::Constness::NotConst @@ -910,7 +910,7 @@ impl Clean for doctree::Function<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), - def_id: did, + def_id: did.to_def_id(), inner: FunctionItem(Function { decl, generics, @@ -922,7 +922,7 @@ impl Clean for doctree::Function<'_> { } } -impl<'a> Clean for (&'a [hir::Ty<'a>], &'a [ast::Ident]) { +impl<'a> Clean for (&'a [hir::Ty<'a>], &'a [Ident]) { fn clean(&self, cx: &DocContext<'_>) -> Arguments { Arguments { values: self @@ -977,11 +977,7 @@ where impl<'tcx> Clean for (DefId, ty::PolyFnSig<'tcx>) { fn clean(&self, cx: &DocContext<'_>) -> FnDecl { let (did, sig) = *self; - let mut names = if cx.tcx.hir().as_local_hir_id(did).is_some() { - vec![].into_iter() - } else { - cx.tcx.fn_arg_names(did).into_iter() - }; + let mut names = if did.is_local() { &[] } else { cx.tcx.fn_arg_names(did) }.iter(); FnDecl { output: Return(sig.skip_binder().output().clean(cx)), @@ -1018,7 +1014,7 @@ impl Clean for doctree::Trait<'_> { name: Some(self.name.clean(cx)), attrs, source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -1041,7 +1037,7 @@ impl Clean for doctree::TraitAlias<'_> { name: Some(self.name.clean(cx)), attrs, source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -1122,10 +1118,10 @@ impl Clean for hir::TraitItem<'_> { name: Some(self.ident.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.span.clean(cx), - def_id: local_did, + def_id: local_did.to_def_id(), visibility: Visibility::Inherited, - stability: get_stability(cx, local_did), - deprecation: get_deprecation(cx, local_did), + stability: get_stability(cx, local_did.to_def_id()), + deprecation: get_deprecation(cx, local_did.to_def_id()), inner, } } @@ -1155,10 +1151,10 @@ impl Clean for hir::ImplItem<'_> { name: Some(self.ident.name.clean(cx)), source: self.span.clean(cx), attrs: self.attrs.clean(cx), - def_id: local_did, + def_id: local_did.to_def_id(), visibility: self.vis.clean(cx), - stability: get_stability(cx, local_did), - deprecation: get_deprecation(cx, local_did), + stability: get_stability(cx, local_did.to_def_id()), + deprecation: get_deprecation(cx, local_did.to_def_id()), inner, } } @@ -1176,14 +1172,14 @@ impl Clean for ty::AssocItem { }; AssocConstItem(ty.clean(cx), default) } - ty::AssocKind::Method => { + ty::AssocKind::Fn => { let generics = (cx.tcx.generics_of(self.def_id), cx.tcx.explicit_predicates_of(self.def_id)) .clean(cx); let sig = cx.tcx.fn_sig(self.def_id); let mut decl = (self.def_id, sig).clean(cx); - if self.method_has_self_argument { + if self.fn_has_self_parameter { let self_ty = match self.container { ty::ImplContainer(def_id) => cx.tcx.type_of(def_id), ty::TraitContainer(_) => cx.tcx.types.self_param, @@ -1350,7 +1346,7 @@ impl Clean for hir::Ty<'_> { TyKind::Slice(ref ty) => Slice(box ty.clean(cx)), TyKind::Array(ref ty, ref length) => { let def_id = cx.tcx.hir().local_def_id(length.hir_id); - let length = match cx.tcx.const_eval_poly(def_id) { + let length = match cx.tcx.const_eval_poly(def_id.to_def_id()) { Ok(length) => { print_const(cx, ty::Const::from_value(cx.tcx, length, cx.tcx.types.usize)) } @@ -1384,8 +1380,9 @@ impl Clean for hir::Ty<'_> { let mut alias = None; if let Res::Def(DefKind::TyAlias, def_id) = path.res { // Substitute private type aliases - if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(def_id) { - if !cx.renderinfo.borrow().access_levels.is_exported(def_id) { + if let Some(def_id) = def_id.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(def_id); + if !cx.renderinfo.borrow().access_levels.is_exported(def_id.to_def_id()) { alias = Some(&cx.tcx.hir().expect_item(hir_id).kind); } } @@ -1417,7 +1414,7 @@ impl Clean for hir::Ty<'_> { if let Some(lt) = lifetime.cloned() { if !lt.is_elided() { let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id); - lt_substs.insert(lt_def_id, lt.clean(cx)); + lt_substs.insert(lt_def_id.to_def_id(), lt.clean(cx)); } } indices.lifetimes += 1; @@ -1437,9 +1434,10 @@ impl Clean for hir::Ty<'_> { _ => None, }); if let Some(ty) = type_ { - ty_substs.insert(ty_param_def_id, ty.clean(cx)); + ty_substs.insert(ty_param_def_id.to_def_id(), ty.clean(cx)); } else if let Some(default) = *default { - ty_substs.insert(ty_param_def_id, default.clean(cx)); + ty_substs + .insert(ty_param_def_id.to_def_id(), default.clean(cx)); } indices.types += 1; } @@ -1459,7 +1457,8 @@ impl Clean for hir::Ty<'_> { _ => None, }); if let Some(ct) = const_ { - ct_substs.insert(const_param_def_id, ct.clean(cx)); + ct_substs + .insert(const_param_def_id.to_def_id(), ct.clean(cx)); } // FIXME(const_generics:defaults) indices.consts += 1; @@ -1557,7 +1556,7 @@ impl<'tcx> Clean for Ty<'tcx> { BareFunction(box BareFunctionDecl { unsafety: sig.unsafety(), generic_params: Vec::new(), - decl: (local_def_id, sig).clean(cx), + decl: (local_def_id.to_def_id(), sig).clean(cx), abi: sig.abi(), }) } @@ -1601,7 +1600,9 @@ impl<'tcx> Clean for Ty<'tcx> { inline::record_extern_fqn(cx, did, TypeKind::Trait); let mut param_names = vec![]; - reg.clean(cx).map(|b| param_names.push(GenericBound::Outlives(b))); + if let Some(b) = reg.clean(cx) { + param_names.push(GenericBound::Outlives(b)); + } for did in dids { let empty = cx.tcx.intern_substs(&[]); let path = @@ -1664,10 +1665,9 @@ impl<'tcx> Clean for Ty<'tcx> { tr } else if let ty::Predicate::TypeOutlives(pred) = *predicate { // these should turn up at the end - pred.skip_binder() - .1 - .clean(cx) - .map(|r| regions.push(GenericBound::Outlives(r))); + if let Some(r) = pred.skip_binder().1.clean(cx) { + regions.push(GenericBound::Outlives(r)); + } return None; } else { return None; @@ -1680,7 +1680,7 @@ impl<'tcx> Clean for Ty<'tcx> { } } - let bounds = bounds + let bounds: Vec<_> = bounds .predicates .iter() .filter_map(|pred| { @@ -1709,7 +1709,7 @@ impl<'tcx> Clean for Ty<'tcx> { }) .collect(); - Some((trait_ref.skip_binder(), bounds).clean(cx)) + Some((trait_ref, &bounds[..]).clean(cx)) }) .collect::>(); bounds.extend(regions); @@ -1723,7 +1723,6 @@ impl<'tcx> Clean for Ty<'tcx> { ty::Bound(..) => panic!("Bound"), ty::Placeholder(..) => panic!("Placeholder"), - ty::UnnormalizedProjection(..) => panic!("UnnormalizedProjection"), ty::GeneratorWitness(..) => panic!("GeneratorWitness"), ty::Infer(..) => panic!("Infer"), ty::Error => panic!("Error"), @@ -1751,9 +1750,9 @@ impl Clean for hir::StructField<'_> { attrs: self.attrs.clean(cx), source: self.span.clean(cx), visibility: self.vis.clean(cx), - stability: get_stability(cx, local_did), - deprecation: get_deprecation(cx, local_did), - def_id: local_did, + stability: get_stability(cx, local_did.to_def_id()), + deprecation: get_deprecation(cx, local_did.to_def_id()), + def_id: local_did.to_def_id(), inner: StructFieldItem(self.ty.clean(cx)), } } @@ -1801,7 +1800,7 @@ impl Clean for doctree::Struct<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -1821,7 +1820,7 @@ impl Clean for doctree::Union<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -1851,7 +1850,7 @@ impl Clean for doctree::Enum<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -1873,7 +1872,7 @@ impl Clean for doctree::Variant<'_> { visibility: Inherited, stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), inner: VariantItem(Variant { kind: self.def.clean(cx) }), } } @@ -1942,6 +1941,7 @@ impl Clean for rustc_span::Span { let hi = sm.lookup_char_pos(self.hi()); Span { filename, + cnum: lo.file.cnum, loline: lo.line, locol: lo.col.to_usize(), hiline: hi.line, @@ -2006,7 +2006,7 @@ impl Clean for Ident { } } -impl Clean for ast::Name { +impl Clean for Symbol { #[inline] fn clean(&self, _: &DocContext<'_>) -> String { self.to_string() @@ -2021,7 +2021,7 @@ impl Clean for doctree::Typedef<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -2036,7 +2036,7 @@ impl Clean for doctree::OpaqueTy<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -2067,7 +2067,7 @@ impl Clean for doctree::Static<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -2088,14 +2088,14 @@ impl Clean for doctree::Constant<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id, + def_id: def_id.to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), inner: ConstantItem(Constant { type_: self.type_.clean(cx), expr: print_const_expr(cx, self.expr), - value: print_evaluated_const(cx, def_id), + value: print_evaluated_const(cx, def_id.to_def_id()), is_literal: is_literal_expr(cx, self.expr.hir_id), }), } @@ -2135,14 +2135,14 @@ impl Clean> for doctree::Impl<'_> { let for_ = self.for_.clean(cx); let type_alias = for_.def_id().and_then(|did| match cx.tcx.def_kind(did) { - Some(DefKind::TyAlias) => Some(cx.tcx.type_of(did).clean(cx)), + DefKind::TyAlias => Some(cx.tcx.type_of(did).clean(cx)), _ => None, }); let make_item = |trait_: Option, for_: Type, items: Vec| Item { name: None, attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id, + def_id: def_id.to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -2182,13 +2182,9 @@ impl Clean> for doctree::ExternCrate<'_> { let res = Res::Def(DefKind::Mod, DefId { krate: self.cnum, index: CRATE_DEF_INDEX }); - if let Some(items) = inline::try_inline( - cx, - res, - self.name, - Some(rustc::ty::Attributes::Borrowed(self.attrs)), - &mut visited, - ) { + if let Some(items) = + inline::try_inline(cx, res, self.name, Some(self.attrs), &mut visited) + { return items; } } @@ -2239,26 +2235,19 @@ impl Clean> for doctree::Import<'_> { } else { let name = self.name; if !please_inline { - match path.res { - Res::Def(DefKind::Mod, did) => { - if !did.is_local() && did.index == CRATE_DEF_INDEX { - // if we're `pub use`ing an extern crate root, don't inline it unless we - // were specifically asked for it - denied = true; - } + if let Res::Def(DefKind::Mod, did) = path.res { + if !did.is_local() && did.index == CRATE_DEF_INDEX { + // if we're `pub use`ing an extern crate root, don't inline it unless we + // were specifically asked for it + denied = true; } - _ => {} } } if !denied { let mut visited = FxHashSet::default(); - if let Some(items) = inline::try_inline( - cx, - path.res, - name, - Some(rustc::ty::Attributes::Borrowed(self.attrs)), - &mut visited, - ) { + if let Some(items) = + inline::try_inline(cx, path.res, name, Some(self.attrs), &mut visited) + { return items; } } @@ -2269,7 +2258,7 @@ impl Clean> for doctree::Import<'_> { name: None, attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id_from_node_id(ast::CRATE_NODE_ID), + def_id: cx.tcx.hir().local_def_id_from_node_id(ast::CRATE_NODE_ID).to_def_id(), visibility: self.vis.clean(cx), stability: None, deprecation: None, @@ -2311,7 +2300,7 @@ impl Clean for doctree::ForeignItem<'_> { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), @@ -2355,7 +2344,7 @@ impl Clean for doctree::ProcMacro<'_> { visibility: Public, stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), inner: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }), } } @@ -2426,10 +2415,9 @@ impl From for SimpleBound { GenericBound::TraitBound(t, mod_) => match t.trait_ { Type::ResolvedPath { path, param_names, .. } => SimpleBound::TraitBound( path.segments, - param_names.map_or_else( - || Vec::new(), - |v| v.iter().map(|p| SimpleBound::from(p.clone())).collect(), - ), + param_names.map_or_else(Vec::new, |v| { + v.iter().map(|p| SimpleBound::from(p.clone())).collect() + }), t.generic_params, mod_, ), diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 2b59c60f0b77f..1404d45ea8938 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -1,7 +1,7 @@ //! Simplification of where-clauses and parameter bounds into a prettier and //! more canonical form. //! -//! Currently all cross-crate-inlined function use `rustc::ty` to reconstruct +//! Currently all cross-crate-inlined function use `rustc_middle::ty` to reconstruct //! the AST (e.g., see all of `clean::inline`), but this is not always a //! non-lossy transformation. The current format of storage for where-clauses //! for functions and such is simply a list of predicates. One example of this @@ -14,8 +14,8 @@ use std::collections::BTreeMap; use std::mem; -use rustc::ty; use rustc_hir::def_id::DefId; +use rustc_middle::ty; use crate::clean; use crate::clean::GenericArgs as PP; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 73f2c399e5698..381238165274d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -8,22 +8,22 @@ use std::rc::Rc; use std::sync::Arc; use std::{slice, vec}; -use rustc::middle::lang_items; -use rustc::middle::stability; -use rustc::ty::layout::VariantIdx; -use rustc_ast::ast::{self, AttrStyle, Ident}; +use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::attr; use rustc_ast::util::comments::strip_doc_comment_decoration; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::lang_items; use rustc_hir::Mutability; use rustc_index::vec::IndexVec; +use rustc_middle::middle::stability; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::DUMMY_SP; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{self, FileName}; +use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use crate::clean::cfg::Cfg; @@ -643,6 +643,15 @@ impl Attributes { }) .collect() } + + pub fn get_doc_aliases(&self) -> FxHashSet { + self.other_attrs + .lists(sym::doc) + .filter(|a| a.check_name(sym::alias)) + .filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", ""))) + .filter(|v| !v.is_empty()) + .collect::>() + } } impl PartialEq for Attributes { @@ -1357,6 +1366,7 @@ pub enum VariantKind { #[derive(Clone, Debug)] pub struct Span { pub filename: FileName, + pub cnum: CrateNum, pub loline: usize, pub locol: usize, pub hiline: usize, @@ -1368,6 +1378,7 @@ impl Span { pub fn empty() -> Span { Span { filename: FileName::Anon(0), + cnum: LOCAL_CRATE, loline: 0, locol: 0, hiline: 0, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 82e34710f0cbf..c4e4802db6c07 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -9,13 +9,13 @@ use crate::clean::{ use crate::core::DocContext; use itertools::Itertools; -use rustc::mir::interpret::{sign_extend, ConstValue, Scalar}; -use rustc::ty::subst::{GenericArgKind, SubstsRef}; -use rustc::ty::{self, DefIdTree, Ty}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_middle::mir::interpret::{sign_extend, ConstValue, Scalar}; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_span::symbol::{kw, sym, Symbol}; use std::mem; @@ -121,7 +121,7 @@ pub fn external_generic_args( let args: Vec<_> = substs .iter() .filter_map(|kind| match kind.unpack() { - GenericArgKind::Lifetime(lt) => lt.clean(cx).map(|lt| GenericArg::Lifetime(lt)), + GenericArgKind::Lifetime(lt) => lt.clean(cx).map(GenericArg::Lifetime), GenericArgKind::Type(_) if skip_self => { skip_self = false; None @@ -198,27 +198,24 @@ pub fn get_real_types( }) { let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); for bound in bounds.iter() { - match *bound { - GenericBound::TraitBound(ref poly_trait, _) => { - for x in poly_trait.generic_params.iter() { - if !x.is_type() { - continue; - } - if let Some(ty) = x.get_type() { - let adds = get_real_types(generics, &ty, cx, recurse + 1); - if !adds.is_empty() { - res.extend(adds); - } else if !ty.is_full_generic() { - if let Some(did) = ty.def_id() { - if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { - res.insert((ty, kind)); - } - } + if let GenericBound::TraitBound(ref poly_trait, _) = *bound { + for x in poly_trait.generic_params.iter() { + if !x.is_type() { + continue; + } + if let Some(ty) = x.get_type() { + let adds = get_real_types(generics, &ty, cx, recurse + 1); + if !adds.is_empty() { + res.extend(adds); + } else if !ty.is_full_generic() { + if let Some(kind) = + ty.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) + { + res.insert((ty, kind)); } } } } - _ => {} } } } @@ -229,20 +226,16 @@ pub fn get_real_types( if !adds.is_empty() { res.extend(adds); } else if !ty.is_full_generic() { - if let Some(did) = ty.def_id() { - if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { - res.insert((ty.clone(), kind)); - } + if let Some(kind) = ty.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + res.insert((ty.clone(), kind)); } } } } } } else { - if let Some(did) = arg.def_id() { - if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { - res.insert((arg.clone(), kind)); - } + if let Some(kind) = arg.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + res.insert((arg.clone(), kind)); } if let Some(gens) = arg.generics() { for gen in gens.iter() { @@ -251,10 +244,8 @@ pub fn get_real_types( if !adds.is_empty() { res.extend(adds); } - } else if let Some(did) = gen.def_id() { - if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { - res.insert((gen.clone(), kind)); - } + } else if let Some(kind) = gen.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + res.insert((gen.clone(), kind)); } } } @@ -280,10 +271,8 @@ pub fn get_all_types( if !args.is_empty() { all_types.extend(args); } else { - if let Some(did) = arg.type_.def_id() { - if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { - all_types.insert((arg.type_.clone(), kind)); - } + if let Some(kind) = arg.type_.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + all_types.insert((arg.type_.clone(), kind)); } } } @@ -292,10 +281,8 @@ pub fn get_all_types( FnRetTy::Return(ref return_type) => { let mut ret = get_real_types(generics, &return_type, cx, 0); if ret.is_empty() { - if let Some(did) = return_type.def_id() { - if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { - ret.insert((return_type.clone(), kind)); - } + if let Some(kind) = return_type.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + ret.insert((return_type.clone(), kind)); } } ret.into_iter().collect() @@ -480,7 +467,8 @@ pub fn name_from_pat(p: &hir::Pat) -> String { pub fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { match n.val { ty::ConstKind::Unevaluated(def_id, _, promoted) => { - let mut s = if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(def_id) { + let mut s = if let Some(def_id) = def_id.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(def_id); print_const_expr(cx, cx.tcx.hir().body_owned_by(hir_id)) } else { inline::print_inlined_const(cx, def_id) @@ -578,16 +566,12 @@ pub fn print_const_expr(cx: &DocContext<'_>, body: hir::BodyId) -> String { None }; - snippet.unwrap_or_else(|| cx.tcx.hir().hir_to_pretty_string(body.hir_id)) + snippet.unwrap_or_else(|| rustc_hir_pretty::id_to_string(&cx.tcx.hir(), body.hir_id)) } /// Given a type Path, resolve it to a Type using the TyCtxt pub fn resolve_type(cx: &DocContext<'_>, path: Path, id: hir::HirId) -> Type { - if id == hir::DUMMY_HIR_ID { - debug!("resolve_type({:?})", path); - } else { - debug!("resolve_type({:?},{:?})", path, id); - } + debug!("resolve_type({:?},{:?})", path, id); let is_generic = match path.res { Res::PrimTy(p) => return Primitive(PrimitiveType::from(p)), @@ -597,7 +581,7 @@ pub fn resolve_type(cx: &DocContext<'_>, path: Path, id: hir::HirId) -> Type { Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { return Generic(format!("{:#}", path.print())); } - Res::SelfTy(..) | Res::Def(DefKind::TyParam, _) | Res::Def(DefKind::AssocTy, _) => true, + Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true, _ => false, }; let did = register_res(&*cx, path.res); diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 179c5bfacf32e..5dbcc5c9ec8b9 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -10,6 +10,7 @@ use rustc_session::config::{ nightly_options, }; use rustc_session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs}; +use rustc_session::getopts; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_span::edition::{Edition, DEFAULT_EDITION}; @@ -446,7 +447,7 @@ impl Options { None => return Err(3), }; - match matches.opt_str("r").as_ref().map(|s| &**s) { + match matches.opt_str("r").as_deref() { Some("rust") | None => {} Some(s) => { diag.struct_err(&format!("unknown input format: {}", s)).emit(); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index f0b9ad2852f51..bf59b3f25734d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,9 +1,7 @@ -use rustc::middle::cstore::CrateStore; -use rustc::middle::privacy::AccessLevels; -use rustc::ty::{Ty, TyCtxt}; use rustc_ast::ast::CRATE_NODE_ID; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::{self, Lrc}; use rustc_driver::abort_on_err; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; @@ -12,16 +10,18 @@ use rustc_hir::def::Namespace::TypeNS; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; use rustc_hir::HirId; use rustc_interface::interface; +use rustc_middle::middle::cstore::CrateStore; +use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_resolve as resolve; -use rustc_session::config::ErrorOutputType; +use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; use rustc_session::DiagnosticOutput; -use rustc_session::{config, Session}; +use rustc_session::Session; use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; -use rustc_data_structures::sync::{self, Lrc}; use std::cell::RefCell; use std::mem; use std::rc::Rc; @@ -30,7 +30,6 @@ use crate::clean; use crate::clean::{AttributesExt, MAX_DEF_ID}; use crate::config::{Options as RustdocOptions, RenderOptions}; use crate::html::render::RenderInfo; - use crate::passes::{self, Condition::*, ConditionalPass}; pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options}; @@ -103,7 +102,7 @@ impl<'tcx> DocContext<'tcx> { } // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly - // refactoring either librustdoc or librustc. In particular, allowing new DefIds to be + // refactoring either librustdoc or librustc_middle. In particular, allowing new DefIds to be // registered after the AST is constructed would require storing the defid mapping in a // RefCell, decreasing the performance for normal compilation for very little gain. // @@ -130,7 +129,7 @@ impl<'tcx> DocContext<'tcx> { ); MAX_DEF_ID.with(|m| { - m.borrow_mut().entry(def_id.krate.clone()).or_insert(start_def_id); + m.borrow_mut().entry(def_id.krate).or_insert(start_def_id); }); self.all_fake_def_ids.borrow_mut().insert(def_id); @@ -144,7 +143,7 @@ impl<'tcx> DocContext<'tcx> { if self.all_fake_def_ids.borrow().contains(&def_id) { None } else { - self.tcx.hir().as_local_hir_id(def_id) + def_id.as_local().map(|def_id| self.tcx.hir().as_local_hir_id(def_id)) } } @@ -152,12 +151,15 @@ impl<'tcx> DocContext<'tcx> { self.tcx .hir() .opt_local_def_id(id) - .and_then(|def_id| self.tcx.lookup_stability(def_id)) + .and_then(|def_id| self.tcx.lookup_stability(def_id.to_def_id())) .cloned() } pub fn deprecation(&self, id: HirId) -> Option { - self.tcx.hir().opt_local_def_id(id).and_then(|def_id| self.tcx.lookup_deprecation(def_id)) + self.tcx + .hir() + .opt_local_def_id(id) + .and_then(|def_id| self.tcx.lookup_deprecation(def_id.to_def_id())) } } @@ -182,7 +184,7 @@ pub fn new_handler( debugging_opts.terminal_width, false, ) - .ui_testing(debugging_opts.ui_testing()), + .ui_testing(debugging_opts.ui_testing), ) } ErrorOutputType::Json { pretty, json_rendered } => { @@ -191,7 +193,7 @@ pub fn new_handler( }); Box::new( JsonEmitter::stderr(None, source_map, pretty, json_rendered, false) - .ui_testing(debugging_opts.ui_testing()), + .ui_testing(debugging_opts.ui_testing), ) } }; @@ -202,6 +204,56 @@ pub fn new_handler( ) } +/// This function is used to setup the lint initialization. By default, in rustdoc, everything +/// is "allowed". Depending if we run in test mode or not, we want some of them to be at their +/// default level. For example, the "INVALID_CODEBLOCK_ATTRIBUTE" lint is activated in both +/// modes. +/// +/// A little detail easy to forget is that there is a way to set the lint level for all lints +/// through the "WARNINGS" lint. To prevent this to happen, we set it back to its "normal" level +/// inside this function. +/// +/// It returns a tuple containing: +/// * Vector of tuples of lints' name and their associated "max" level +/// * HashMap of lint id with their associated "max" level +pub fn init_lints( + mut whitelisted_lints: Vec, + lint_opts: Vec<(String, lint::Level)>, + filter_call: F, +) -> (Vec<(String, lint::Level)>, FxHashMap) +where + F: Fn(&lint::Lint) -> Option<(String, lint::Level)>, +{ + let warnings_lint_name = lint::builtin::WARNINGS.name; + + whitelisted_lints.push(warnings_lint_name.to_owned()); + whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned()); + + let lints = || { + lint::builtin::HardwiredLints::get_lints() + .into_iter() + .chain(rustc_lint::SoftLints::get_lints().into_iter()) + }; + + let lint_opts = lints() + .filter_map(|lint| if lint.name == warnings_lint_name { None } else { filter_call(lint) }) + .chain(lint_opts.into_iter()) + .collect::>(); + + let lint_caps = lints() + .filter_map(|lint| { + // We don't want to whitelist *all* lints so let's + // ignore those ones. + if whitelisted_lints.iter().any(|l| lint.name == l) { + None + } else { + Some((lint::LintId::of(lint), lint::Allow)) + } + }) + .collect(); + (lint_opts, lint_caps) +} + pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions) { // Parse, resolve, and typecheck the given crate. @@ -245,57 +297,35 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let input = Input::File(input); let intra_link_resolution_failure_name = lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE.name; - let warnings_lint_name = lint::builtin::WARNINGS.name; let missing_docs = rustc_lint::builtin::MISSING_DOCS.name; let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name; let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name; + let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name; + let invalid_codeblock_attribute_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE.name; // In addition to those specific lints, we also need to whitelist those given through // command line, otherwise they'll get ignored and we don't want that. - let mut whitelisted_lints = vec![ - warnings_lint_name.to_owned(), + let whitelisted_lints = vec![ intra_link_resolution_failure_name.to_owned(), missing_docs.to_owned(), missing_doc_example.to_owned(), private_doc_tests.to_owned(), + no_crate_level_docs.to_owned(), + invalid_codeblock_attribute_name.to_owned(), ]; - whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned()); - - let lints = || { - lint::builtin::HardwiredLints::get_lints() - .into_iter() - .chain(rustc_lint::SoftLints::get_lints().into_iter()) - }; - - let lint_opts = lints() - .filter_map(|lint| { - if lint.name == warnings_lint_name || lint.name == intra_link_resolution_failure_name { - None - } else { - Some((lint.name_lower(), lint::Allow)) - } - }) - .chain(lint_opts.into_iter()) - .collect::>(); - - let lint_caps = lints() - .filter_map(|lint| { - // We don't want to whitelist *all* lints so let's - // ignore those ones. - if whitelisted_lints.iter().any(|l| lint.name == l) { - None - } else { - Some((lint::LintId::of(lint), lint::Allow)) - } - }) - .collect(); + let (lint_opts, lint_caps) = init_lints(whitelisted_lints, lint_opts, |lint| { + if lint.name == intra_link_resolution_failure_name + || lint.name == invalid_codeblock_attribute_name + { + None + } else { + Some((lint.name_lower(), lint::Allow)) + } + }); - let crate_types = if proc_macro_crate { - vec![config::CrateType::ProcMacro] - } else { - vec![config::CrateType::Rlib] - }; + let crate_types = + if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; // plays with error output here! let sessopts = config::Options { maybe_sysroot, @@ -379,7 +409,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt map: access_levels .map .iter() - .map(|(&k, &v)| (tcx.hir().local_def_id(k), v)) + .map(|(&k, &v)| (tcx.hir().local_def_id(k).to_def_id(), v)) .collect(), }; @@ -411,10 +441,28 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut krate = clean::krate(&mut ctxt); + if let Some(ref m) = krate.module { + if let None | Some("") = m.doc_value() { + let help = "The following guide may be of use:\n\ + https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation\ + .html"; + tcx.struct_lint_node( + rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS, + ctxt.as_local_hir_id(m.def_id).unwrap(), + |lint| { + let mut diag = lint.build( + "no documentation found for this crate's top-level module", + ); + diag.help(help); + diag.emit(); + }, + ); + } + } + fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) { let mut msg = diag.struct_warn(&format!( - "the `#![doc({})]` attribute is \ - considered deprecated", + "the `#![doc({})]` attribute is considered deprecated", name )); msg.warn( diff --git a/src/librustdoc/docfs.rs b/src/librustdoc/docfs.rs index 9c9a00295c3fa..7ebb200abfe81 100644 --- a/src/librustdoc/docfs.rs +++ b/src/librustdoc/docfs.rs @@ -16,12 +16,12 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; macro_rules! try_err { - ($e:expr, $file:expr) => {{ + ($e:expr, $file:expr) => { match $e { Ok(e) => e, Err(e) => return Err(E::new(e, $file)), } - }}; + }; } pub trait PathError { diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 41b8e66d26592..5b13832742770 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -3,15 +3,14 @@ pub use self::StructType::*; use rustc_ast::ast; -use rustc_ast::ast::Name; use rustc_span::hygiene::MacroKind; -use rustc_span::{self, Span}; +use rustc_span::{self, Span, Symbol}; use rustc_hir as hir; use rustc_hir::def_id::CrateNum; pub struct Module<'hir> { - pub name: Option, + pub name: Option, pub attrs: &'hir [ast::Attribute], pub where_outer: Span, pub where_inner: Span, @@ -39,7 +38,7 @@ pub struct Module<'hir> { impl Module<'hir> { pub fn new( - name: Option, + name: Option, attrs: &'hir [ast::Attribute], vis: &'hir hir::Visibility<'hir>, ) -> Module<'hir> { @@ -86,7 +85,7 @@ pub struct Struct<'hir> { pub vis: &'hir hir::Visibility<'hir>, pub id: hir::HirId, pub struct_type: StructType, - pub name: Name, + pub name: Symbol, pub generics: &'hir hir::Generics<'hir>, pub attrs: &'hir [ast::Attribute], pub fields: &'hir [hir::StructField<'hir>], @@ -97,7 +96,7 @@ pub struct Union<'hir> { pub vis: &'hir hir::Visibility<'hir>, pub id: hir::HirId, pub struct_type: StructType, - pub name: Name, + pub name: Symbol, pub generics: &'hir hir::Generics<'hir>, pub attrs: &'hir [ast::Attribute], pub fields: &'hir [hir::StructField<'hir>], @@ -111,11 +110,11 @@ pub struct Enum<'hir> { pub attrs: &'hir [ast::Attribute], pub id: hir::HirId, pub whence: Span, - pub name: Name, + pub name: Symbol, } pub struct Variant<'hir> { - pub name: Name, + pub name: Symbol, pub id: hir::HirId, pub attrs: &'hir [ast::Attribute], pub def: &'hir hir::VariantData<'hir>, @@ -126,7 +125,7 @@ pub struct Function<'hir> { pub decl: &'hir hir::FnDecl<'hir>, pub attrs: &'hir [ast::Attribute], pub id: hir::HirId, - pub name: Name, + pub name: Symbol, pub vis: &'hir hir::Visibility<'hir>, pub header: hir::FnHeader, pub whence: Span, @@ -137,7 +136,7 @@ pub struct Function<'hir> { pub struct Typedef<'hir> { pub ty: &'hir hir::Ty<'hir>, pub gen: &'hir hir::Generics<'hir>, - pub name: Name, + pub name: Symbol, pub id: hir::HirId, pub attrs: &'hir [ast::Attribute], pub whence: Span, @@ -146,7 +145,7 @@ pub struct Typedef<'hir> { pub struct OpaqueTy<'hir> { pub opaque_ty: &'hir hir::OpaqueTy<'hir>, - pub name: Name, + pub name: Symbol, pub id: hir::HirId, pub attrs: &'hir [ast::Attribute], pub whence: Span, @@ -158,7 +157,7 @@ pub struct Static<'hir> { pub type_: &'hir hir::Ty<'hir>, pub mutability: hir::Mutability, pub expr: hir::BodyId, - pub name: Name, + pub name: Symbol, pub attrs: &'hir [ast::Attribute], pub vis: &'hir hir::Visibility<'hir>, pub id: hir::HirId, @@ -168,7 +167,7 @@ pub struct Static<'hir> { pub struct Constant<'hir> { pub type_: &'hir hir::Ty<'hir>, pub expr: hir::BodyId, - pub name: Name, + pub name: Symbol, pub attrs: &'hir [ast::Attribute], pub vis: &'hir hir::Visibility<'hir>, pub id: hir::HirId, @@ -178,7 +177,7 @@ pub struct Constant<'hir> { pub struct Trait<'hir> { pub is_auto: hir::IsAuto, pub unsafety: hir::Unsafety, - pub name: Name, + pub name: Symbol, pub items: Vec<&'hir hir::TraitItem<'hir>>, pub generics: &'hir hir::Generics<'hir>, pub bounds: &'hir [hir::GenericBound<'hir>], @@ -189,7 +188,7 @@ pub struct Trait<'hir> { } pub struct TraitAlias<'hir> { - pub name: Name, + pub name: Symbol, pub generics: &'hir hir::Generics<'hir>, pub bounds: &'hir [hir::GenericBound<'hir>], pub attrs: &'hir [ast::Attribute], @@ -217,7 +216,7 @@ pub struct Impl<'hir> { pub struct ForeignItem<'hir> { pub vis: &'hir hir::Visibility<'hir>, pub id: hir::HirId, - pub name: Name, + pub name: Symbol, pub kind: &'hir hir::ForeignItemKind<'hir>, pub attrs: &'hir [ast::Attribute], pub whence: Span, @@ -226,17 +225,17 @@ pub struct ForeignItem<'hir> { // For Macro we store the DefId instead of the NodeId, since we also create // these imported macro_rules (which only have a DUMMY_NODE_ID). pub struct Macro<'hir> { - pub name: Name, + pub name: Symbol, pub hid: hir::HirId, pub def_id: hir::def_id::DefId, pub attrs: &'hir [ast::Attribute], pub whence: Span, pub matchers: Vec, - pub imported_from: Option, + pub imported_from: Option, } pub struct ExternCrate<'hir> { - pub name: Name, + pub name: Symbol, pub cnum: CrateNum, pub path: Option, pub vis: &'hir hir::Visibility<'hir>, @@ -245,7 +244,7 @@ pub struct ExternCrate<'hir> { } pub struct Import<'hir> { - pub name: Name, + pub name: Symbol, pub id: hir::HirId, pub vis: &'hir hir::Visibility<'hir>, pub attrs: &'hir [ast::Attribute], @@ -255,10 +254,10 @@ pub struct Import<'hir> { } pub struct ProcMacro<'hir> { - pub name: Name, + pub name: Symbol, pub id: hir::HirId, pub kind: MacroKind, - pub helpers: Vec, + pub helpers: Vec, pub attrs: &'hir [ast::Attribute], pub whence: Span, } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 02f1947c99e5f..c4bc73770a76b 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -235,9 +235,7 @@ impl<'a> Classifier<'a> { // If this '&' or '*' token is followed by a non-whitespace token, assume that it's the // reference or dereference operator or a reference or pointer type, instead of the // bit-and or multiplication operator. - token::BinOp(token::And) | token::BinOp(token::Star) - if self.peek()? != &token::Whitespace => - { + token::BinOp(token::And | token::Star) if self.peek()? != &token::Whitespace => { Class::RefKeyWord } @@ -275,9 +273,7 @@ impl<'a> Classifier<'a> { | token::ModSep | token::LArrow | token::OpenDelim(_) - | token::CloseDelim(token::Brace) - | token::CloseDelim(token::Paren) - | token::CloseDelim(token::NoDelim) => Class::None, + | token::CloseDelim(token::Brace | token::Paren | token::NoDelim) => Class::None, token::Question => Class::QuestionMark, diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 0922c8cdd1200..ea65b3905272e 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -114,7 +114,6 @@ pub fn render( window.rootPath = \"{root_path}\";\ window.currentCrate = \"{krate}\";\ \ - \ \ {static_extra_scripts}\ {extra_scripts}\ diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index e13bf270440e2..c129e54c0f28a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -20,7 +20,12 @@ #![allow(non_camel_case_types)] use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_hir::HirId; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint; use rustc_span::edition::Edition; +use rustc_span::Span; use std::borrow::Cow; use std::cell::RefCell; use std::collections::VecDeque; @@ -39,7 +44,7 @@ use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag}; mod tests; fn opts() -> Options { - Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES + Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES | Options::ENABLE_STRIKETHROUGH } /// When `to_string` is called, this struct will emit the HTML corresponding to @@ -192,7 +197,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { if let Some(Event::Start(Tag::CodeBlock(kind))) = event { let parse_result = match kind { CodeBlockKind::Fenced(ref lang) => { - LangString::parse(&lang, self.check_error_codes, false) + LangString::parse_without_check(&lang, self.check_error_codes, false) } CodeBlockKind::Indented => LangString::all_false(), }; @@ -448,7 +453,7 @@ impl<'a, I: Iterator>> Iterator for SummaryLine<'a, I> { if !self.started { self.started = true; } - while let Some(event) = self.inner.next() { + if let Some(event) = self.inner.next() { let mut is_start = true; let is_allowed_tag = match event { Event::Start(Tag::CodeBlock(_)) | Event::End(Tag::CodeBlock(_)) => { @@ -560,6 +565,7 @@ pub fn find_testable_code( tests: &mut T, error_codes: ErrorCodes, enable_per_target_ignores: bool, + extra_info: Option<&ExtraInfo<'_, '_>>, ) { let mut parser = Parser::new(doc).into_offset_iter(); let mut prev_offset = 0; @@ -573,7 +579,12 @@ pub fn find_testable_code( if lang.is_empty() { LangString::all_false() } else { - LangString::parse(lang, error_codes, enable_per_target_ignores) + LangString::parse( + lang, + error_codes, + enable_per_target_ignores, + extra_info, + ) } } CodeBlockKind::Indented => LangString::all_false(), @@ -615,6 +626,49 @@ pub fn find_testable_code( } } +pub struct ExtraInfo<'a, 'b> { + hir_id: Option, + item_did: Option, + sp: Span, + tcx: &'a TyCtxt<'b>, +} + +impl<'a, 'b> ExtraInfo<'a, 'b> { + pub fn new(tcx: &'a TyCtxt<'b>, hir_id: HirId, sp: Span) -> ExtraInfo<'a, 'b> { + ExtraInfo { hir_id: Some(hir_id), item_did: None, sp, tcx } + } + + pub fn new_did(tcx: &'a TyCtxt<'b>, did: DefId, sp: Span) -> ExtraInfo<'a, 'b> { + ExtraInfo { hir_id: None, item_did: Some(did), sp, tcx } + } + + fn error_invalid_codeblock_attr(&self, msg: &str, help: &str) { + let hir_id = match (self.hir_id, self.item_did) { + (Some(h), _) => h, + (None, Some(item_did)) => { + match item_did.as_local() { + Some(item_did) => self.tcx.hir().as_local_hir_id(item_did), + None => { + // If non-local, no need to check anything. + return; + } + } + } + (None, None) => return, + }; + self.tcx.struct_span_lint_hir( + lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE, + hir_id, + self.sp, + |lint| { + let mut diag = lint.build(msg); + diag.help(help); + diag.emit(); + }, + ); + } +} + #[derive(Eq, PartialEq, Clone, Debug)] pub struct LangString { original: String, @@ -652,10 +706,19 @@ impl LangString { } } + fn parse_without_check( + string: &str, + allow_error_code_check: ErrorCodes, + enable_per_target_ignores: bool, + ) -> LangString { + Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) + } + fn parse( string: &str, allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, + extra: Option<&ExtraInfo<'_, '_>>, ) -> LangString { let allow_error_code_check = allow_error_code_check.as_bool(); let mut seen_rust_tags = false; @@ -715,6 +778,53 @@ impl LangString { seen_other_tags = true; } } + x if extra.is_some() => { + let s = x.to_lowercase(); + match if s == "compile-fail" || s == "compile_fail" || s == "compilefail" { + Some(( + "compile_fail", + "the code block will either not be tested if not marked as a rust one \ + or won't fail if it compiles successfully", + )) + } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" { + Some(( + "should_panic", + "the code block will either not be tested if not marked as a rust one \ + or won't fail if it doesn't panic when running", + )) + } else if s == "no-run" || s == "no_run" || s == "norun" { + Some(( + "no_run", + "the code block will either not be tested if not marked as a rust one \ + or will be run (which you might not want)", + )) + } else if s == "allow-fail" || s == "allow_fail" || s == "allowfail" { + Some(( + "allow_fail", + "the code block will either not be tested if not marked as a rust one \ + or will be run (which you might not want)", + )) + } else if s == "test-harness" || s == "test_harness" || s == "testharness" { + Some(( + "test_harness", + "the code block will either not be tested if not marked as a rust one \ + or the code will be wrapped inside a main function", + )) + } else { + None + } { + Some((flag, help)) => { + if let Some(ref extra) = extra { + extra.error_invalid_codeblock_attr( + &format!("unknown attribute `{}`. Did you mean `{}`?", x, flag), + help, + ); + } + } + None => {} + } + seen_other_tags = true; + } _ => seen_other_tags = true, } } @@ -823,7 +933,11 @@ impl MarkdownSummaryLine<'_> { } }; - let p = Parser::new_with_broken_link_callback(md, Options::empty(), Some(&replacer)); + let p = Parser::new_with_broken_link_callback( + md, + Options::ENABLE_STRIKETHROUGH, + Some(&replacer), + ); let mut s = String::new(); @@ -850,7 +964,7 @@ pub fn plain_summary_line(md: &str) -> String { Event::Start(Tag::Heading(_)) => (None, 1), Event::Code(code) => (Some(format!("`{}`", code)), 0), Event::Text(ref s) if self.is_in > 0 => (Some(s.as_ref().to_owned()), 0), - Event::End(Tag::Paragraph) | Event::End(Tag::Heading(_)) => (None, -1), + Event::End(Tag::Paragraph | Tag::Heading(_)) => (None, -1), _ => (None, 0), }; if is_in > 0 || (is_in < 0 && self.is_in > 0) { @@ -865,7 +979,11 @@ pub fn plain_summary_line(md: &str) -> String { } } let mut s = String::with_capacity(md.len() * 3 / 2); - let p = ParserWrapper { inner: Parser::new(md), is_in: 0, is_first: true }; + let p = ParserWrapper { + inner: Parser::new_ext(md, Options::ENABLE_STRIKETHROUGH), + is_in: 0, + is_first: true, + }; p.filter(|t| !t.is_empty()).for_each(|i| s.push_str(&i)); s } @@ -909,7 +1027,7 @@ pub fn markdown_links(md: &str) -> Vec<(String, Option>)> { debug!("found link: {}", dest); links.push(match dest { CowStr::Borrowed(s) => (s.to_owned(), locate(s)), - s @ CowStr::Boxed(..) | s @ CowStr::Inlined(..) => (s.into_string(), None), + s @ (CowStr::Boxed(..) | CowStr::Inlined(..)) => (s.into_string(), None), }); } } @@ -934,7 +1052,7 @@ crate struct RustCodeBlock { /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or /// untagged (and assumed to be rust). -crate fn rust_code_blocks(md: &str) -> Vec { +crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec { let mut code_blocks = vec![]; if md.is_empty() { @@ -944,75 +1062,70 @@ crate fn rust_code_blocks(md: &str) -> Vec { let mut p = Parser::new_ext(md, opts()).into_offset_iter(); while let Some((event, offset)) = p.next() { - match event { - Event::Start(Tag::CodeBlock(syntax)) => { - let (syntax, code_start, code_end, range, is_fenced) = match syntax { - CodeBlockKind::Fenced(syntax) => { - let syntax = syntax.as_ref(); - let lang_string = if syntax.is_empty() { - LangString::all_false() - } else { - LangString::parse(&*syntax, ErrorCodes::Yes, false) - }; - if !lang_string.rust { + if let Event::Start(Tag::CodeBlock(syntax)) = event { + let (syntax, code_start, code_end, range, is_fenced) = match syntax { + CodeBlockKind::Fenced(syntax) => { + let syntax = syntax.as_ref(); + let lang_string = if syntax.is_empty() { + LangString::all_false() + } else { + LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info)) + }; + if !lang_string.rust { + continue; + } + let syntax = if syntax.is_empty() { None } else { Some(syntax.to_owned()) }; + let (code_start, mut code_end) = match p.next() { + Some((Event::Text(_), offset)) => (offset.start, offset.end), + Some((_, sub_offset)) => { + let code = Range { start: sub_offset.start, end: sub_offset.start }; + code_blocks.push(RustCodeBlock { + is_fenced: true, + range: offset, + code, + syntax, + }); continue; } - let syntax = if syntax.is_empty() { None } else { Some(syntax.to_owned()) }; - let (code_start, mut code_end) = match p.next() { - Some((Event::Text(_), offset)) => (offset.start, offset.end), - Some((_, sub_offset)) => { - let code = Range { start: sub_offset.start, end: sub_offset.start }; - code_blocks.push(RustCodeBlock { - is_fenced: true, - range: offset, - code, - syntax, - }); - continue; - } - None => { - let code = Range { start: offset.end, end: offset.end }; - code_blocks.push(RustCodeBlock { - is_fenced: true, - range: offset, - code, - syntax, - }); - continue; - } - }; - while let Some((Event::Text(_), offset)) = p.next() { - code_end = offset.end; + None => { + let code = Range { start: offset.end, end: offset.end }; + code_blocks.push(RustCodeBlock { + is_fenced: true, + range: offset, + code, + syntax, + }); + continue; } - (syntax, code_start, code_end, offset, true) + }; + while let Some((Event::Text(_), offset)) = p.next() { + code_end = offset.end; } - CodeBlockKind::Indented => { - // The ending of the offset goes too far sometime so we reduce it by one in - // these cases. - if offset.end > offset.start - && md.get(offset.end..=offset.end) == Some(&"\n") - { - ( - None, - offset.start, - offset.end, - Range { start: offset.start, end: offset.end - 1 }, - false, - ) - } else { - (None, offset.start, offset.end, offset, false) - } + (syntax, code_start, code_end, offset, true) + } + CodeBlockKind::Indented => { + // The ending of the offset goes too far sometime so we reduce it by one in + // these cases. + if offset.end > offset.start && md.get(offset.end..=offset.end) == Some(&"\n") { + ( + None, + offset.start, + offset.end, + Range { start: offset.start, end: offset.end - 1 }, + false, + ) + } else { + (None, offset.start, offset.end, offset, false) } - }; + } + }; - code_blocks.push(RustCodeBlock { - is_fenced, - range, - code: Range { start: code_start, end: code_end }, - syntax, - }); - } - _ => (), + code_blocks.push(RustCodeBlock { + is_fenced, + range, + code: Range { start: code_start, end: code_end }, + syntax, + }); } } @@ -1024,9 +1137,36 @@ pub struct IdMap { map: FxHashMap, } +fn init_id_map() -> FxHashMap { + let mut map = FxHashMap::default(); + // This is the list of IDs used by rustdoc templates. + map.insert("mainThemeStyle".to_owned(), 1); + map.insert("themeStyle".to_owned(), 1); + map.insert("theme-picker".to_owned(), 1); + map.insert("theme-choices".to_owned(), 1); + map.insert("settings-menu".to_owned(), 1); + map.insert("main".to_owned(), 1); + map.insert("search".to_owned(), 1); + map.insert("crate-search".to_owned(), 1); + map.insert("render-detail".to_owned(), 1); + map.insert("toggle-all-docs".to_owned(), 1); + map.insert("all-types".to_owned(), 1); + // This is the list of IDs used by rustdoc sections. + map.insert("fields".to_owned(), 1); + map.insert("variants".to_owned(), 1); + map.insert("implementors-list".to_owned(), 1); + map.insert("synthetic-implementors-list".to_owned(), 1); + map.insert("implementations".to_owned(), 1); + map.insert("trait-implementations".to_owned(), 1); + map.insert("synthetic-implementations".to_owned(), 1); + map.insert("blanket-implementations".to_owned(), 1); + map.insert("deref-methods".to_owned(), 1); + map +} + impl IdMap { pub fn new() -> Self { - IdMap::default() + IdMap { map: init_id_map() } } pub fn populate>(&mut self, ids: I) { @@ -1036,7 +1176,7 @@ impl IdMap { } pub fn reset(&mut self) { - self.map = FxHashMap::default(); + self.map = init_id_map(); } pub fn derive(&mut self, candidate: String) -> String { diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 48231ce7b73ef..bf0451a1d9d65 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -29,8 +29,8 @@ fn test_unique_id() { "examples-2", "method.into_iter-1", "foo-1", - "main", - "search", + "main-1", + "search-1", "methods", "examples-3", "method.into_iter-2", @@ -64,7 +64,7 @@ fn test_lang_string_parse() { edition: Option, ) { assert_eq!( - LangString::parse(s, ErrorCodes::Yes, true), + LangString::parse(s, ErrorCodes::Yes, true, None), LangString { should_panic, no_run, @@ -191,8 +191,8 @@ fn test_header_ids_multiple_blocks() { t( &mut map, "# Main", - "", + "

\ + Main

", ); t( &mut map, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index eb7a367acf439..646c663ad9ccd 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -42,15 +42,15 @@ use std::rc::Rc; use std::str; use std::sync::Arc; -use rustc::middle::privacy::AccessLevels; -use rustc::middle::stability; use rustc_ast_pretty::pprust; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_feature::UnstableFeatures; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::Mutability; +use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::stability; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::FileName; @@ -293,7 +293,12 @@ impl Serialize for IndexItem { where S: Serializer, { - assert_eq!(self.parent.is_some(), self.parent_idx.is_some()); + assert_eq!( + self.parent.is_some(), + self.parent_idx.is_some(), + "`{}` is missing idx", + self.name + ); (self.ty, &self.name, &self.path, &self.desc, self.parent_idx, &self.search_type) .serialize(serializer) @@ -782,47 +787,41 @@ themePicker.onblur = handleThemeButtonsBlur; .split('"') .next() .map(|s| s.to_owned()) - .unwrap_or_else(|| String::new()), + .unwrap_or_else(String::new), ); } } Ok((ret, krates)) } - fn show_item(item: &IndexItem, krate: &str) -> String { - format!( - "{{'crate':'{}','ty':{},'name':'{}','desc':'{}','p':'{}'{}}}", - krate, - item.ty as usize, - item.name, - item.desc.replace("'", "\\'"), - item.path, - if let Some(p) = item.parent_idx { format!(",'parent':{}", p) } else { String::new() } - ) - } + fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec, Vec)> { + let mut ret = Vec::new(); + let mut krates = Vec::new(); - let dst = cx.dst.join(&format!("aliases{}.js", cx.shared.resource_suffix)); - { - let (mut all_aliases, _) = try_err!(collect(&dst, &krate.name, "ALIASES"), &dst); - let mut output = String::with_capacity(100); - for (alias, items) in &cx.cache.aliases { - if items.is_empty() { - continue; + if path.exists() { + for line in BufReader::new(File::open(path)?).lines() { + let line = line?; + if !line.starts_with('"') { + continue; + } + if line.starts_with(&format!("\"{}\"", krate)) { + continue; + } + if line.ends_with(",\\") { + ret.push(line[..line.len() - 2].to_string()); + } else { + // Ends with "\\" (it's the case for the last added crate line) + ret.push(line[..line.len() - 1].to_string()); + } + krates.push( + line.split('"') + .find(|s| !s.is_empty()) + .map(|s| s.to_owned()) + .unwrap_or_else(String::new), + ); } - output.push_str(&format!( - "\"{}\":[{}],", - alias, - items.iter().map(|v| show_item(v, &krate.name)).collect::>().join(",") - )); } - all_aliases.push(format!("ALIASES[\"{}\"] = {{{}}};", krate.name, output)); - all_aliases.sort(); - let mut v = Buffer::html(); - writeln!(&mut v, "var ALIASES = {{}};"); - for aliases in &all_aliases { - writeln!(&mut v, "{}", aliases); - } - cx.shared.fs.write(&dst, v.into_inner().into_bytes())?; + Ok((ret, krates)) } use std::ffi::OsString; @@ -909,18 +908,18 @@ themePicker.onblur = handleThemeButtonsBlur; // Update the search index let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); - let (mut all_indexes, mut krates) = try_err!(collect(&dst, &krate.name, "searchIndex"), &dst); + let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name), &dst); all_indexes.push(search_index); // Sort the indexes by crate so the file will be generated identically even // with rustdoc running in parallel. all_indexes.sort(); { - let mut v = String::from("var searchIndex={};\n"); - v.push_str(&all_indexes.join("\n")); + let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); + v.push_str(&all_indexes.join(",\\\n")); // "addSearchOptions" has to be called first so the crate filtering can be set before the // search might start (if it's set into the URL for example). - v.push_str("\naddSearchOptions(searchIndex);initSearch(searchIndex);"); + v.push_str("\\\n}');\naddSearchOptions(searchIndex);initSearch(searchIndex);"); cx.shared.fs.write(&dst, &v)?; } if options.enable_index_page { @@ -1623,14 +1622,14 @@ impl Context { _ => return None, }; - let (krate, path) = if item.def_id.is_local() { + let (krate, path) = if item.source.cnum == LOCAL_CRATE { if let Some(path) = self.shared.local_sources.get(file) { (&self.shared.layout.krate, path) } else { return None; } } else { - let (krate, src_root) = match *self.cache.extern_locations.get(&item.def_id.krate)? { + let (krate, src_root) = match *self.cache.extern_locations.get(&item.source.cnum)? { (ref name, ref src, Local) => (name, src), (ref name, ref src, Remote(ref s)) => { root = s.to_string(); @@ -2158,7 +2157,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: docs = MarkdownSummaryLine(doc_value, &myitem.links()).to_string(), class = myitem.type_(), add = add, - stab = stab.unwrap_or_else(|| String::new()), + stab = stab.unwrap_or_else(String::new), unsafety_flag = unsafety_flag, href = item_path(myitem.type_(), myitem.name.as_ref().unwrap()), title = [full_path(cx, myitem), myitem.type_().to_string()] @@ -2250,7 +2249,10 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { ); message.push_str(&format!(": {}", html.to_string())); } - stability.push(format!("
{}
", message)); + stability.push(format!( + "
👎 {}
", + message, + )); } if let Some(stab) = item.stability.as_ref().filter(|stab| stab.level == stability::Unstable) { @@ -3382,8 +3384,8 @@ fn render_assoc_items( write!( w, "\ -

\ - Methods\ +

\ + Implementations\

\ " ); @@ -3444,10 +3446,10 @@ fn render_assoc_items( write!( w, "\ -

\ - Trait Implementations\ +

\ + Trait Implementations\

\ -
{}
", +
{}
", impls ); } @@ -3496,14 +3498,13 @@ fn render_deref_methods( .inner_impl() .items .iter() - .filter_map(|item| match item.inner { + .find_map(|item| match item.inner { clean::TypedefItem(ref t, true) => Some(match *t { clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), _ => (&t.type_, &t.type_), }), _ => None, }) - .next() .expect("Expected associated type binding"); let what = AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; @@ -4067,8 +4068,8 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { ret.sort(); if !ret.is_empty() { out.push_str(&format!( - "Methods\ -
{}
", + "Methods\ +
{}
", ret.join("") )); } @@ -4080,18 +4081,14 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { .filter(|i| i.inner_impl().trait_.is_some()) .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) { - if let Some((target, real_target)) = impl_ - .inner_impl() - .items - .iter() - .filter_map(|item| match item.inner { + if let Some((target, real_target)) = + impl_.inner_impl().items.iter().find_map(|item| match item.inner { clean::TypedefItem(ref t, true) => Some(match *t { clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), _ => (&t.type_, &t.type_), }), _ => None, }) - .next() { let inner_impl = target .def_id() @@ -4165,8 +4162,8 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { if !concrete_format.is_empty() { out.push_str( - "\ - Trait Implementations", + "\ + Trait Implementations", ); out.push_str(&format!("
{}
", concrete_format)); } @@ -4174,7 +4171,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { if !synthetic_format.is_empty() { out.push_str( "\ - Auto Trait Implementations", + Auto Trait Implementations", ); out.push_str(&format!("
{}
", synthetic_format)); } @@ -4182,7 +4179,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { if !blanket_format.is_empty() { out.push_str( "\ - Blanket Implementations", + Blanket Implementations", ); out.push_str(&format!("
{}
", blanket_format)); } @@ -4318,20 +4315,19 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { let mut res = implementors .iter() .filter(|i| i.inner_impl().for_.def_id().map_or(false, |d| !c.paths.contains_key(&d))) - .filter_map(|i| match extract_for_impl_name(&i.impl_item) { - Some((ref name, ref id)) => { - Some(format!("{}", id, Escape(name))) - } - _ => None, - }) - .collect::>(); + .filter_map(|i| extract_for_impl_name(&i.impl_item)) + .collect::>(); + if !res.is_empty() { res.sort(); sidebar.push_str(&format!( "\ Implementations on Foreign Types
{}
", - res.join("") + res.into_iter() + .map(|(name, id)| format!("{}", id, Escape(&name))) + .collect::>() + .join("") )); } } @@ -4593,12 +4589,9 @@ fn collect_paths_for_type(first_ty: clean::Type) -> Vec { let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); - match fqp { - Some(path) => { - out.push(path.join("::")); - } - _ => {} - }; + if let Some(path) = fqp { + out.push(path.join("::")); + } } clean::Type::Tuple(tys) => { work.extend(tys.into_iter()); diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index b93738319a634..57d385de32096 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -1,8 +1,8 @@ use crate::clean::{self, AttributesExt, GetDefId}; use crate::fold::DocFolder; -use rustc::middle::privacy::AccessLevels; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; +use rustc_middle::middle::privacy::AccessLevels; use rustc_span::source_map::FileName; use rustc_span::symbol::sym; use std::collections::BTreeMap; @@ -120,7 +120,7 @@ crate struct Cache { /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, /// we need the alias element to have an array of items. - pub(super) aliases: FxHashMap>, + pub(super) aliases: BTreeMap>, } impl Cache { @@ -294,10 +294,13 @@ impl DocFolder for Cache { // for where the type was defined. On the other // hand, `paths` always has the right // information if present. - Some(&(ref fqp, ItemType::Trait)) - | Some(&(ref fqp, ItemType::Struct)) - | Some(&(ref fqp, ItemType::Union)) - | Some(&(ref fqp, ItemType::Enum)) => Some(&fqp[..fqp.len() - 1]), + Some(&( + ref fqp, + ItemType::Trait + | ItemType::Struct + | ItemType::Union + | ItemType::Enum, + )) => Some(&fqp[..fqp.len() - 1]), Some(..) => Some(&*self.stack), None => None, }; @@ -308,7 +311,7 @@ impl DocFolder for Cache { }; match parent { - (parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => { + (parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => { debug_assert!(!item.is_stripped()); // A crate has a module at its root, containing all items, @@ -324,6 +327,13 @@ impl DocFolder for Cache { parent_idx: None, search_type: get_index_search_type(&item), }); + + for alias in item.attrs.get_doc_aliases() { + self.aliases + .entry(alias.to_lowercase()) + .or_insert(Vec::new()) + .push(self.search_index.len() - 1); + } } } (Some(parent), None) if is_inherent_impl_item => { @@ -373,11 +383,8 @@ impl DocFolder for Cache { { self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); } - self.add_aliases(&item); } - clean::PrimitiveItem(..) => { - self.add_aliases(&item); self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); } @@ -485,40 +492,6 @@ impl DocFolder for Cache { } } -impl Cache { - fn add_aliases(&mut self, item: &clean::Item) { - if item.def_id.index == CRATE_DEF_INDEX { - return; - } - if let Some(ref item_name) = item.name { - let path = self - .paths - .get(&item.def_id) - .map(|p| p.0[..p.0.len() - 1].join("::")) - .unwrap_or("std".to_owned()); - for alias in item - .attrs - .lists(sym::doc) - .filter(|a| a.check_name(sym::alias)) - .filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", ""))) - .filter(|v| !v.is_empty()) - .collect::>() - .into_iter() - { - self.aliases.entry(alias).or_insert(Vec::with_capacity(1)).push(IndexItem { - ty: item.type_(), - name: item_name.to_string(), - path: path.clone(), - desc: shorten(plain_summary_line(item.doc_value())), - parent: None, - parent_idx: None, - search_type: get_index_search_type(&item), - }); - } - } - } -} - /// Attempts to find where an external crate is located, given that we're /// rendering in to the specified source destination. fn extern_location( @@ -564,7 +537,8 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { let mut crate_items = Vec::with_capacity(cache.search_index.len()); let mut crate_paths = vec![]; - let Cache { ref mut search_index, ref orphan_impl_items, ref paths, .. } = *cache; + let Cache { ref mut search_index, ref orphan_impl_items, ref paths, ref mut aliases, .. } = + *cache; // Attach all orphan items to the type's definition if the type // has since been learned. @@ -579,6 +553,12 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { parent_idx: None, search_type: get_index_search_type(&item), }); + for alias in item.attrs.get_doc_aliases() { + aliases + .entry(alias.to_lowercase()) + .or_insert(Vec::new()) + .push(search_index.len() - 1); + } } } @@ -590,7 +570,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { for item in search_index { item.parent_idx = item.parent.and_then(|defid| { if defid_to_pathid.contains_key(&defid) { - defid_to_pathid.get(&defid).map(|x| *x) + defid_to_pathid.get(&defid).copied() } else { let pathid = lastpathid; defid_to_pathid.insert(defid, pathid); @@ -627,18 +607,30 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { items: Vec<&'a IndexItem>, #[serde(rename = "p")] paths: Vec<(ItemType, String)>, + // The String is alias name and the vec is the list of the elements with this alias. + // + // To be noted: the `usize` elements are indexes to `items`. + #[serde(rename = "a")] + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + aliases: &'a BTreeMap>, } // Collect the index into a string format!( - r#"searchIndex["{}"] = {};"#, + r#""{}":{}"#, krate.name, serde_json::to_string(&CrateData { doc: crate_doc, items: crate_items, paths: crate_paths, + aliases, }) .expect("failed serde conversion") + // All these `replace` calls are because we have to go through JS string for JSON content. + .replace(r"\", r"\\") + .replace("'", r"\'") + // We need to escape double quotes for the JSON. + .replace("\\\"", "\\\\\"") ) } @@ -697,11 +689,11 @@ fn get_generics(clean_type: &clean::Type) -> Option> { let r = types .iter() .filter_map(|t| { - if let Some(name) = get_index_type_name(t, false) { - Some(Generic { name: name.to_ascii_lowercase(), defid: t.def_id(), idx: None }) - } else { - None - } + get_index_type_name(t, false).map(|name| Generic { + name: name.to_ascii_lowercase(), + defid: t.def_id(), + idx: None, + }) }) .collect::>(); if r.is_empty() { None } else { Some(r) } diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 86f46b2d7e154..c5f44baced2e4 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -5,6 +5,7 @@ use crate::html::format::Buffer; use crate::html::highlight; use crate::html::layout; use crate::html::render::{Error, SharedContext, BASIC_KEYWORDS}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_span::source_map::FileName; use std::ffi::OsStr; use std::fs; @@ -37,8 +38,8 @@ impl<'a> DocFolder for SourceCollector<'a> { if self.scx.include_sources // skip all synthetic "files" && item.source.filename.is_real() - // skip non-local items - && item.def_id.is_local() + // skip non-local files + && item.source.cnum == LOCAL_CRATE { // If it turns out that we couldn't read this file, then we probably // can't read any of the files (generating html output from json or diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 3f12fb893a440..9869c50fbb0cf 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -531,6 +531,7 @@ function getSearchElement() { var OUTPUT_DATA = 1; var NO_TYPE_FILTER = -1; var currentResults, index, searchIndex; + var ALIASES = {}; var params = getQueryStringParams(); // Populate search bar with query string search term when provided, @@ -963,6 +964,72 @@ function getSearchElement() { return itemTypes[ty.ty] + ty.path + ty.name; } + function createAliasFromItem(item) { + return { + crate: item.crate, + name: item.name, + path: item.path, + desc: item.desc, + ty: item.ty, + parent: item.parent, + type: item.type, + is_alias: true, + }; + } + + function handleAliases(ret, query, filterCrates) { + // We separate aliases and crate aliases because we want to have current crate + // aliases to be before the others in the displayed results. + var aliases = []; + var crateAliases = []; + var i; + if (filterCrates !== undefined && + ALIASES[filterCrates] && + ALIASES[filterCrates][query.search]) { + for (i = 0; i < ALIASES[crate][query.search].length; ++i) { + aliases.push( + createAliasFromItem(searchIndex[ALIASES[filterCrates][query.search]])); + } + } else { + Object.keys(ALIASES).forEach(function(crate) { + if (ALIASES[crate][query.search]) { + var pushTo = crate === window.currentCrate ? crateAliases : aliases; + for (i = 0; i < ALIASES[crate][query.search].length; ++i) { + pushTo.push( + createAliasFromItem( + searchIndex[ALIASES[crate][query.search][i]])); + } + } + }); + } + + var sortFunc = function(aaa, bbb) { + if (aaa.path < bbb.path) { + return 1; + } else if (aaa.path === bbb.path) { + return 0; + } + return -1; + }; + crateAliases.sort(sortFunc); + aliases.sort(sortFunc); + + var pushFunc = function(alias) { + alias.alias = query.raw; + var res = buildHrefAndPath(alias); + alias.displayPath = pathSplitter(res[0]); + alias.fullPath = alias.displayPath + alias.name; + alias.href = res[1]; + + ret.others.unshift(alias); + if (ret.others.length > MAX_RESULTS) { + ret.others.pop(); + } + }; + onEach(aliases, pushFunc); + onEach(crateAliases, pushFunc); + } + // quoted values mean literal search var nSearchWords = searchWords.length; var i; @@ -1190,23 +1257,7 @@ function getSearchElement() { "returned": sortResults(results_returned, true), "others": sortResults(results), }; - if (ALIASES && ALIASES[window.currentCrate] && - ALIASES[window.currentCrate][query.raw]) { - var aliases = ALIASES[window.currentCrate][query.raw]; - for (i = 0; i < aliases.length; ++i) { - aliases[i].is_alias = true; - aliases[i].alias = query.raw; - aliases[i].path = aliases[i].p; - var res = buildHrefAndPath(aliases[i]); - aliases[i].displayPath = pathSplitter(res[0]); - aliases[i].fullPath = aliases[i].displayPath + aliases[i].name; - aliases[i].href = res[1]; - ret.others.unshift(aliases[i]); - if (ret.others.length > MAX_RESULTS) { - ret.others.pop(); - } - } - } + handleAliases(ret, query, filterCrates); return ret; } @@ -1599,13 +1650,12 @@ function getSearchElement() { "returned": mergeArrays(results.returned), "others": mergeArrays(results.others), }; - } else { - return { - "in_args": results.in_args[0], - "returned": results.returned[0], - "others": results.others[0], - }; } + return { + "in_args": results.in_args[0], + "returned": results.returned[0], + "others": results.others[0], + }; } function getFilterCrates() { @@ -1656,10 +1706,13 @@ function getSearchElement() { searchIndex = []; var searchWords = []; var i; + var currentIndex = 0; for (var crate in rawSearchIndex) { if (!rawSearchIndex.hasOwnProperty(crate)) { continue; } + var crateSize = 0; + searchWords.push(crate); searchIndex.push({ crate: crate, @@ -1669,6 +1722,7 @@ function getSearchElement() { desc: rawSearchIndex[crate].doc, type: null, }); + currentIndex += 1; // an array of [(Number) item type, // (String) name, @@ -1680,6 +1734,9 @@ function getSearchElement() { // an array of [(Number) item type, // (String) name] var paths = rawSearchIndex[crate].p; + // a array of [(String) alias name + // [Number] index to items] + var aliases = rawSearchIndex[crate].a; // convert `rawPaths` entries into object form var len = paths.length; @@ -1698,9 +1755,18 @@ function getSearchElement() { var lastPath = ""; for (i = 0; i < len; ++i) { var rawRow = items[i]; - var row = {crate: crate, ty: rawRow[0], name: rawRow[1], - path: rawRow[2] || lastPath, desc: rawRow[3], - parent: paths[rawRow[4]], type: rawRow[5]}; + if (!rawRow[2]) { + rawRow[2] = lastPath; + } + var row = { + crate: crate, + ty: rawRow[0], + name: rawRow[1], + path: rawRow[2], + desc: rawRow[3], + parent: paths[rawRow[4]], + type: rawRow[5], + }; searchIndex.push(row); if (typeof row.name === "string") { var word = row.name.toLowerCase(); @@ -1709,7 +1775,25 @@ function getSearchElement() { searchWords.push(""); } lastPath = row.path; + crateSize += 1; } + + if (aliases) { + ALIASES[crate] = {}; + var j, local_aliases; + for (var alias_name in aliases) { + if (!aliases.hasOwnProperty(alias_name)) { continue; } + + if (!ALIASES[crate].hasOwnProperty(alias_name)) { + ALIASES[crate][alias_name] = []; + } + local_aliases = aliases[alias_name]; + for (j = 0; j < local_aliases.length; ++j) { + ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex); + } + } + } + currentIndex += crateSize; } return searchWords; } @@ -2180,7 +2264,7 @@ function getSearchElement() { if (collapse) { toggleAllDocs(pageId, true); } else if (getCurrentValue("rustdoc-auto-hide-trait-implementations") !== "false") { - var impl_list = document.getElementById("implementations-list"); + var impl_list = document.getElementById("trait-implementations-list"); if (impl_list !== null) { onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) { @@ -2703,3 +2787,9 @@ function focusSearchBar() { function defocusSearchBar() { getSearchInput().blur(); } + +// This is required in firefox. Explanations: when going back in the history, firefox doesn't re-run +// the JS, therefore preventing rustdoc from setting a few things required to be able to reload the +// previous search results (if you navigated to a search result with the keyboard, pressed enter on +// it to navigate to that result, and then came back to this page). +window.onunload = function(){}; diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 8887bca3c5962..ab52475172333 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -184,6 +184,25 @@ nav.sub { overflow: auto; } +/* Improve the scrollbar display on firefox */ +* { + scrollbar-width: initial; +} +.sidebar { + scrollbar-width: thin; +} + +/* Improve the scrollbar display on webkit-based browsers */ +::-webkit-scrollbar { + width: 12px; +} +.sidebar::-webkit-scrollbar { + width: 8px; +} +::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0; +} + .sidebar .block > ul > li { margin-right: -10px; } @@ -1082,8 +1101,8 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { pre.rust { position: relative; - tab-width: 4; - -moz-tab-width: 4; + tab-size: 4; + -moz-tab-size: 4; } .search-failed { diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index ff32a0fa09e4d..a2986c7b927e2 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -32,6 +32,28 @@ pre { background-color: #505050; } +/* Improve the scrollbar display on firefox */ +* { + scrollbar-color: rgb(64, 65, 67) #717171; +} +.sidebar { + scrollbar-color: rgba(32,34,37,.6) transparent; +} + +/* Improve the scrollbar display on webkit-based browsers */ +::-webkit-scrollbar-track { + background-color: #717171; +} +::-webkit-scrollbar-thumb { + background-color: rgba(32, 34, 37, .6); +} +.sidebar::-webkit-scrollbar-track { + background-color: #717171; +} +.sidebar::-webkit-scrollbar-thumb { + background-color: rgba(32, 34, 37, .6); +} + .sidebar .current { background-color: #333; } diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index 2b2819f7126b4..be173d8eb46d3 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -34,6 +34,29 @@ pre { background-color: #F1F1F1; } +/* Improve the scrollbar display on firefox */ +* { + scrollbar-color: rgba(36, 37, 39, 0.6) #e6e6e6; +} + +.sidebar { + scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9; +} + +/* Improve the scrollbar display on webkit-based browsers */ +::-webkit-scrollbar-track { + background-color: #ecebeb; +} +::-webkit-scrollbar-thumb { + background-color: rgba(36, 37, 39, 0.6); +} +.sidebar::-webkit-scrollbar-track { + background-color: #dcdcdc; +} +.sidebar::-webkit-scrollbar-thumb { + background-color: rgba(36, 37, 39, 0.6); +} + .sidebar .current { background-color: #fff; } diff --git a/src/librustdoc/html/toc.rs b/src/librustdoc/html/toc.rs index 034fb27300027..721988e29a678 100644 --- a/src/librustdoc/html/toc.rs +++ b/src/librustdoc/html/toc.rs @@ -5,7 +5,7 @@ pub struct Toc { /// The levels are strictly decreasing, i.e. /// - /// entries[0].level >= entries[1].level >= ... + /// `entries[0].level >= entries[1].level >= ...` /// /// Normally they are equal, but can differ in cases like A and B, /// both of which end up in the same `Toc` as they have the same @@ -39,8 +39,8 @@ pub struct TocEntry { pub struct TocBuilder { top_level: Toc, /// The current hierarchy of parent headings, the levels are - /// strictly increasing (i.e., chain[0].level < chain[1].level < - /// ...) with each entry being the most recent occurrence of a + /// strictly increasing (i.e., `chain[0].level < chain[1].level < + /// ...`) with each entry being the most recent occurrence of a /// heading with that level (it doesn't include the most recent /// occurrences of every level, just, if it *is* in `chain` then /// it is the most recent one). @@ -94,7 +94,7 @@ impl TocBuilder { loop { match self.chain.pop() { Some(mut next) => { - this.map(|e| next.children.entries.push(e)); + next.children.entries.extend(this); if next.level < level { // this is the parent we want, so return it to // its rightful place. @@ -105,7 +105,7 @@ impl TocBuilder { } } None => { - this.map(|e| self.top_level.entries.push(e)); + self.top_level.entries.extend(this); return; } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 33cabad9193c4..5fb7b7bf959cf 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -7,6 +7,7 @@ #![feature(box_syntax)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![feature(test)] #![feature(vec_remove_item)] #![feature(ptr_offset_from)] @@ -15,8 +16,6 @@ #![recursion_limit = "256"] extern crate env_logger; -extern crate getopts; -extern crate rustc; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; @@ -26,12 +25,14 @@ extern crate rustc_errors; extern crate rustc_expand; extern crate rustc_feature; extern crate rustc_hir; +extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; extern crate rustc_interface; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_metadata; +extern crate rustc_middle; extern crate rustc_mir; extern crate rustc_parse; extern crate rustc_resolve; @@ -50,6 +51,7 @@ use std::panic; use std::process; use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; +use rustc_session::getopts; use rustc_session::{early_error, early_warn}; #[macro_use] diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index a41fdd2ff17af..b4c0f0ac4c5d9 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -1,4 +1,4 @@ -use std::fs::File; +use std::fs::{create_dir_all, File}; use std::io::prelude::*; use std::path::PathBuf; @@ -40,6 +40,11 @@ pub fn render( diag: &rustc_errors::Handler, edition: Edition, ) -> i32 { + if let Err(e) = create_dir_all(&options.output) { + diag.struct_err(&format!("{}: {}", options.output.display(), e)).emit(); + return 4; + } + let mut output = options.output; output.push(input.file_name().unwrap()); output.set_extension("html"); @@ -148,7 +153,7 @@ pub fn test(mut options: Options, diag: &rustc_errors::Handler) -> i32 { collector.set_position(DUMMY_SP); let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()); - find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores); + find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); options.test_args.insert(0, "rustdoctest".to_string()); testing::test_main( diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 3e0ff0b3d9a67..d1f2c12ccd630 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -10,7 +10,7 @@ use crate::clean; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::{self, RustCodeBlock}; -use crate::passes::Pass; +use crate::passes::{span_of_attrs, Pass}; pub const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass { name: "check-code-block-syntax", @@ -114,7 +114,9 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> { fn fold_item(&mut self, item: clean::Item) -> Option { if let Some(dox) = &item.attrs.collapsed_doc_value() { - for code_block in markdown::rust_code_blocks(&dox) { + let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); + let extra = crate::html::markdown::ExtraInfo::new_did(&self.cx.tcx, item.def_id, sp); + for code_block in markdown::rust_code_blocks(&dox, &extra) { self.check_rust_syntax(&item, &dox, code_block); } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 113c781e33205..a3ef350a0487e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1,5 +1,4 @@ -use rustc::ty; -use rustc_ast::ast::{self, Ident}; +use rustc_ast::ast; use rustc_errors::Applicability; use rustc_expand::base::SyntaxExtensionKind; use rustc_feature::UnstableFeatures; @@ -10,8 +9,10 @@ use rustc_hir::def::{ PerNS, Res, }; use rustc_hir::def_id::DefId; +use rustc_middle::ty; use rustc_resolve::ParentScope; use rustc_session::lint; +use rustc_span::symbol::Ident; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; @@ -130,12 +131,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { current_item: &Option, parent_id: Option, extra_fragment: &Option, + item_opt: Option<&Item>, ) -> Result<(Res, Option), ErrorKind> { let cx = self.cx; // In case we're in a module, try to resolve the relative path. if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) { - let module_id = cx.tcx.hir().hir_to_node_id(module_id); + let module_id = cx.tcx.hir().hir_id_to_node_id(module_id); let result = cx.enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) }); @@ -149,7 +151,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // In case this is a trait item, skip the // early return and try looking for the trait. let value = match res { - Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::AssocConst, _) => true, + Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true, Res::Def(DefKind::AssocTy, _) => false, Res::Def(DefKind::Variant, _) => { return handle_variant(cx, res, extra_fragment); @@ -209,7 +211,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .filter_by_name_unhygienic(item_name) .next() .and_then(|item| match item.kind { - ty::AssocKind::Method => Some("method"), + ty::AssocKind::Fn => Some("method"), _ => None, }) .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name)))) @@ -226,24 +228,52 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { - Res::Def(DefKind::Struct, did) - | Res::Def(DefKind::Union, did) - | Res::Def(DefKind::Enum, did) - | Res::Def(DefKind::TyAlias, did) => { - let item = cx - .tcx - .inherent_impls(did) - .iter() - .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order()) - .find(|item| item.ident.name == item_name); + Res::Def( + DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias, + did, + ) => { + // We need item's parent to know if it's + // trait impl or struct/enum/etc impl + let item_parent = item_opt + .and_then(|item| self.cx.as_local_hir_id(item.def_id)) + .and_then(|item_hir| { + let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); + self.cx.tcx.hir().find(parent_hir) + }); + let item = match item_parent { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { of_trait: Some(_), self_ty, .. }, + .. + })) => { + // trait impl + cx.tcx + .associated_item_def_ids(self_ty.hir_id.owner) + .iter() + .map(|child| { + let associated_item = cx.tcx.associated_item(*child); + associated_item + }) + .find(|child| child.ident.name == item_name) + } + _ => { + // struct/enum/etc. impl + cx.tcx + .inherent_impls(did) + .iter() + .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order()) + .find(|item| item.ident.name == item_name) + } + }; + if let Some(item) = item { let out = match item.kind { - ty::AssocKind::Method if ns == ValueNS => "method", + ty::AssocKind::Fn if ns == ValueNS => "method", ty::AssocKind::Const if ns == ValueNS => "associatedconstant", + ty::AssocKind::Type if ns == ValueNS => "associatedtype", _ => return self.variant_field(path_str, current_item, module_id), }; if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Method { + Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Fn { "methods cannot be followed by anchors" } else { "associated constants cannot be followed by anchors" @@ -298,14 +328,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .map(|item| cx.tcx.associated_item(*item)) .find(|item| item.ident.name == item_name); if let Some(item) = item { - let kind = match item.kind { - ty::AssocKind::Const if ns == ValueNS => "associatedconstant", - ty::AssocKind::Type if ns == TypeNS => "associatedtype", - ty::AssocKind::Method if ns == ValueNS => { - if item.defaultness.has_value() { "method" } else { "tymethod" } - } - _ => return self.variant_field(path_str, current_item, module_id), - }; + let kind = + match item.kind { + ty::AssocKind::Const if ns == ValueNS => "associatedconstant", + ty::AssocKind::Type if ns == TypeNS => "associatedtype", + ty::AssocKind::Fn if ns == ValueNS => { + if item.defaultness.has_value() { "method" } else { "tymethod" } + } + _ => return self.variant_field(path_str, current_item, module_id), + }; if extra_fragment.is_some() { Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Const { @@ -334,8 +365,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { fn fold_item(&mut self, mut item: Item) -> Option { let item_hir_id = if item.is_mod() { - if let Some(id) = self.cx.tcx.hir().as_local_hir_id(item.def_id) { - Some(id) + if let Some(def_id) = item.def_id.as_local() { + Some(self.cx.tcx.hir().as_local_hir_id(def_id)) } else { debug!("attempting to fold on a non-local item: {:?}", item); return self.fold_item_recur(item); @@ -483,8 +514,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { match kind { Some(ns @ ValueNS) => { - match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) - { + match self.resolve( + path_str, + ns, + ¤t_item, + base_node, + &extra_fragment, + None, + ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { resolution_failure(cx, &item, path_str, &dox, link_range); @@ -500,8 +537,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } Some(ns @ TypeNS) => { - match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) - { + match self.resolve( + path_str, + ns, + ¤t_item, + base_node, + &extra_fragment, + None, + ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { resolution_failure(cx, &item, path_str, &dox, link_range); @@ -525,6 +568,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, + None, ) { Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); @@ -538,6 +582,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, + Some(&item), ) { Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); @@ -813,7 +858,7 @@ fn ambiguity_error( for (res, ns) in candidates { let (action, mut suggestion) = match res { - Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::Fn, _) => { + Res::Def(DefKind::AssocFn | DefKind::Fn, _) => { ("add parentheses", format!("{}()", path_str)) } Res::Def(DefKind::Macro(..), _) => { @@ -880,7 +925,7 @@ fn handle_variant( res: Res, extra_fragment: &Option, ) -> Result<(Res, Option), ErrorKind> { - use rustc::ty::DefIdTree; + use rustc_middle::ty::DefIdTree; if extra_fragment.is_some() { return Err(ErrorKind::AnchorFailure("variants cannot be followed by anchors")); diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index da0e97f1075b0..0fdeefd79e9f2 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -62,6 +62,8 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { lang_items.slice_u8_alloc_impl(), lang_items.const_ptr_impl(), lang_items.mut_ptr_impl(), + lang_items.const_slice_ptr_impl(), + lang_items.mut_slice_ptr_impl(), ]; for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) { @@ -87,11 +89,10 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { if cleaner.keep_item(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() { let target = items .iter() - .filter_map(|item| match item.inner { + .find_map(|item| match item.inner { TypedefItem(ref t, true) => Some(&t.type_), _ => None, }) - .next() .expect("Deref impl without Target type"); if let Some(prim) = target.primitive_type() { @@ -118,7 +119,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() { for &impl_node in cx.tcx.hir().trait_impls(trait_did) { let impl_did = cx.tcx.hir().local_def_id(impl_node); - inline::build_impl(cx, impl_did, None, &mut new_items); + inline::build_impl(cx, impl_did.to_def_id(), None, &mut new_items); } } diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 38f371783e994..70366c90139c2 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -1,8 +1,8 @@ //! Contains information about "passes", used to modify crate information during the documentation //! process. -use rustc::middle::privacy::AccessLevels; use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_middle::middle::privacy::AccessLevels; use rustc_session::lint; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use std::mem; @@ -338,7 +338,7 @@ pub fn look_for_tests<'tcx>( let mut tests = Tests { found_tests: 0 }; - find_testable_code(&dox, &mut tests, ErrorCodes::No, false); + find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); if check_missing_code && tests.found_tests == 0 { let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs index d4e09ce47a3c1..5604a9c2dc163 100644 --- a/src/librustdoc/passes/unindent_comments.rs +++ b/src/librustdoc/passes/unindent_comments.rs @@ -1,6 +1,5 @@ use std::cmp; use std::string::String; -use std::usize; use crate::clean::{self, DocFragment, Item}; use crate::core::DocContext; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index c5aa4677d5659..a00c9a0bcea65 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -1,29 +1,36 @@ -use rustc::hir::map::Map; -use rustc::util::common::ErrorReported; use rustc_ast::ast; use rustc_ast::with_globals; use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorReported; use rustc_feature::UnstableFeatures; use rustc_hir as hir; use rustc_hir::intravisit; +use rustc_hir::{HirId, CRATE_HIR_ID}; use rustc_interface::interface; -use rustc_session::{self, config, DiagnosticOutput, Session}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{self, CrateType}; +use rustc_session::{lint, DiagnosticOutput, Session}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::TargetTriple; +use tempfile::Builder as TempFileBuilder; + +use std::collections::HashMap; use std::env; use std::io::{self, Write}; use std::panic; use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; -use tempfile::Builder as TempFileBuilder; use crate::clean::Attributes; use crate::config::Options; +use crate::core::init_lints; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; +use crate::passes::span_of_attrs; #[derive(Clone, Default)] pub struct TestOptions { @@ -39,20 +46,32 @@ pub struct TestOptions { pub fn run(options: Options) -> i32 { let input = config::Input::File(options.input.clone()); - let crate_types = if options.proc_macro_crate { - vec![config::CrateType::ProcMacro] - } else { - vec![config::CrateType::Rlib] - }; + let invalid_codeblock_attribute_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE.name; + + // In addition to those specific lints, we also need to whitelist those given through + // command line, otherwise they'll get ignored and we don't want that. + let whitelisted_lints = vec![invalid_codeblock_attribute_name.to_owned()]; + + let (lint_opts, lint_caps) = init_lints(whitelisted_lints, options.lint_opts.clone(), |lint| { + if lint.name == invalid_codeblock_attribute_name { + None + } else { + Some((lint.name_lower(), lint::Allow)) + } + }); + + let crate_types = + if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; let sessopts = config::Options { maybe_sysroot: options.maybe_sysroot.clone(), search_paths: options.libs.clone(), crate_types, + lint_opts: if !options.display_warnings { lint_opts } else { vec![] }, + lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)), cg: options.codegen_options.clone(), externs: options.externs.clone(), unstable_features: UnstableFeatures::from_environment(), - lint_cap: Some(rustc_session::lint::Level::Allow), actually_rustdoc: true, debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() }, edition: options.edition, @@ -74,7 +93,7 @@ pub fn run(options: Options) -> i32 { diagnostic_output: DiagnosticOutput::Default, stderr: None, crate_name: options.crate_name.clone(), - lint_caps: Default::default(), + lint_caps, register_lints: None, override_queries: None, registry: rustc_driver::diagnostics_registry(), @@ -104,6 +123,7 @@ pub fn run(options: Options) -> i32 { global_ctxt.enter(|tcx| { let krate = tcx.hir().krate(); + let mut hir_collector = HirCollector { sess: compiler.session(), collector: &mut collector, @@ -111,10 +131,17 @@ pub fn run(options: Options) -> i32 { codes: ErrorCodes::from( compiler.session().opts.unstable_features.is_nightly_build(), ), + tcx, }; - hir_collector.visit_testable("".to_string(), &krate.item.attrs, |this| { - intravisit::walk_crate(this, krate); - }); + hir_collector.visit_testable( + "".to_string(), + &krate.item.attrs, + CRATE_HIR_ID, + krate.item.span, + |this| { + intravisit::walk_crate(this, krate); + }, + ); }); compiler.session().abort_if_errors(); @@ -190,10 +217,23 @@ enum TestFailure { UnexpectedRunPass, } +enum DirState { + Temp(tempfile::TempDir), + Perm(PathBuf), +} + +impl DirState { + fn path(&self) -> &std::path::Path { + match self { + DirState::Temp(t) => t.path(), + DirState::Perm(p) => p.as_path(), + } + } +} + fn run_test( test: &str, cratename: &str, - filename: &FileName, line: usize, options: Options, should_panic: bool, @@ -206,53 +246,16 @@ fn run_test( mut error_codes: Vec, opts: &TestOptions, edition: Edition, + outdir: DirState, + path: PathBuf, ) -> Result<(), TestFailure> { let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts, edition); - // FIXME(#44940): if doctests ever support path remapping, then this filename - // needs to be the result of `SourceMap::span_to_unmapped_path`. - let path = match filename { - FileName::Real(path) => path.clone(), - _ => PathBuf::from(r"doctest.rs"), - }; - - enum DirState { - Temp(tempfile::TempDir), - Perm(PathBuf), - } - - impl DirState { - fn path(&self) -> &std::path::Path { - match self { - DirState::Temp(t) => t.path(), - DirState::Perm(p) => p.as_path(), - } - } - } - - let outdir = if let Some(mut path) = options.persist_doctests { - path.push(format!( - "{}_{}", - filename.to_string().rsplit('/').next().unwrap().replace(".", "_"), - line - )); - std::fs::create_dir_all(&path).expect("Couldn't create directory for doctest executables"); - - DirState::Perm(path) - } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) - }; let output_file = outdir.path().join("rust_out"); let rustc_binary = options .test_builder - .as_ref() - .map(|v| &**v) + .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = Command::new(&rustc_binary); compiler.arg("--crate-type").arg("bin"); @@ -285,7 +288,12 @@ fn run_test( if no_run && !compile_fail { compiler.arg("--emit=metadata"); } - compiler.arg("--target").arg(target.to_string()); + compiler.arg("--target").arg(match target { + TargetTriple::TargetTriple(s) => s, + TargetTriple::TargetPath(path) => { + path.to_str().expect("target path must be valid unicode").to_string() + } + }); compiler.arg("-"); compiler.stdin(Stdio::piped()); @@ -334,8 +342,8 @@ fn run_test( if let Some(tool) = runtool { cmd = Command::new(tool); - cmd.arg(output_file); cmd.args(runtool_args); + cmd.arg(output_file); } else { cmd = Command::new(output_file); } @@ -639,6 +647,7 @@ pub struct Collector { position: Span, source_map: Option>, filename: Option, + visited_tests: HashMap<(String, usize), usize>, } impl Collector { @@ -662,6 +671,7 @@ impl Collector { position: DUMMY_SP, source_map, filename, + visited_tests: HashMap::new(), } } @@ -698,13 +708,55 @@ impl Tester for Collector { let name = self.generate_name(line, &filename); let cratename = self.cratename.to_string(); let opts = self.opts.clone(); - let edition = config.edition.unwrap_or(self.options.edition.clone()); + let edition = config.edition.unwrap_or(self.options.edition); let options = self.options.clone(); let runtool = self.options.runtool.clone(); let runtool_args = self.options.runtool_args.clone(); let target = self.options.target.clone(); let target_str = target.to_string(); + // FIXME(#44940): if doctests ever support path remapping, then this filename + // needs to be the result of `SourceMap::span_to_unmapped_path`. + let path = match &filename { + FileName::Real(path) => path.clone(), + _ => PathBuf::from(r"doctest.rs"), + }; + + let outdir = if let Some(mut path) = options.persist_doctests.clone() { + // For example `module/file.rs` would become `module_file_rs` + let folder_name = filename + .to_string() + .chars() + .map(|c| if c == '/' || c == '.' { '_' } else { c }) + .collect::(); + + path.push(format!( + "{name}_{line}_{number}", + name = folder_name, + number = { + // Increases the current test number, if this file already + // exists or it creates a new entry with a test number of 0. + self.visited_tests + .entry((folder_name.clone(), line)) + .and_modify(|v| *v += 1) + .or_insert(0) + }, + line = line, + )); + + std::fs::create_dir_all(&path) + .expect("Couldn't create directory for doctest executables"); + + DirState::Perm(path) + } else { + DirState::Temp( + TempFileBuilder::new() + .prefix("rustdoctest") + .tempdir() + .expect("rustdoc needs a tempdir"), + ) + }; + debug!("creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { @@ -723,7 +775,6 @@ impl Tester for Collector { let res = run_test( &test, &cratename, - &filename, line, options, config.should_panic, @@ -736,6 +787,8 @@ impl Tester for Collector { config.error_codes, &opts, edition, + outdir, + path, ); if let Err(err) = res { @@ -853,18 +906,21 @@ impl Tester for Collector { } } -struct HirCollector<'a, 'hir> { +struct HirCollector<'a, 'hir, 'tcx> { sess: &'a Session, collector: &'a mut Collector, map: Map<'hir>, codes: ErrorCodes, + tcx: TyCtxt<'tcx>, } -impl<'a, 'hir> HirCollector<'a, 'hir> { +impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { fn visit_testable( &mut self, name: String, attrs: &[ast::Attribute], + hir_id: HirId, + sp: Span, nested: F, ) { let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs); @@ -890,6 +946,11 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { self.collector, self.codes, self.collector.enable_per_target_ignores, + Some(&crate::html::markdown::ExtraInfo::new( + &self.tcx, + hir_id, + span_of_attrs(&attrs).unwrap_or(sp), + )), ); } @@ -901,7 +962,7 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { } } -impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { +impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { @@ -910,30 +971,30 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { fn visit_item(&mut self, item: &'hir hir::Item) { let name = if let hir::ItemKind::Impl { ref self_ty, .. } = item.kind { - self.map.hir_to_pretty_string(self_ty.hir_id) + rustc_hir_pretty::id_to_string(&self.map, self_ty.hir_id) } else { item.ident.to_string() }; - self.visit_testable(name, &item.attrs, |this| { + self.visit_testable(name, &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_item(this, item); }); } fn visit_trait_item(&mut self, item: &'hir hir::TraitItem) { - self.visit_testable(item.ident.to_string(), &item.attrs, |this| { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_trait_item(this, item); }); } fn visit_impl_item(&mut self, item: &'hir hir::ImplItem) { - self.visit_testable(item.ident.to_string(), &item.attrs, |this| { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_impl_item(this, item); }); } fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem) { - self.visit_testable(item.ident.to_string(), &item.attrs, |this| { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_foreign_item(this, item); }); } @@ -944,19 +1005,25 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { g: &'hir hir::Generics, item_id: hir::HirId, ) { - self.visit_testable(v.ident.to_string(), &v.attrs, |this| { + self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| { intravisit::walk_variant(this, v, g, item_id); }); } fn visit_struct_field(&mut self, f: &'hir hir::StructField) { - self.visit_testable(f.ident.to_string(), &f.attrs, |this| { + self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| { intravisit::walk_struct_field(this, f); }); } fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef) { - self.visit_testable(macro_def.ident.to_string(), ¯o_def.attrs, |_| ()); + self.visit_testable( + macro_def.ident.to_string(), + ¯o_def.attrs, + macro_def.hir_id, + macro_def.span, + |_| (), + ); } } diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 9dd1d3706ffbd..c8eb271c807d6 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -8,18 +8,6 @@ use rustc_errors::Handler; #[cfg(test)] mod tests; -macro_rules! try_something { - ($e:expr, $diag:expr, $out:expr) => {{ - match $e { - Ok(c) => c, - Err(e) => { - $diag.struct_err(&e.to_string()).emit(); - return $out; - } - } - }}; -} - #[derive(Debug, Clone, Eq)] pub struct CssPath { pub name: String, @@ -265,7 +253,13 @@ pub fn test_theme_against>( against: &CssPath, diag: &Handler, ) -> (bool, Vec) { - let data = try_something!(fs::read(f), diag, (false, vec![])); + let data = match fs::read(f) { + Ok(c) => c, + Err(e) => { + diag.struct_err(&e.to_string()).emit(); + return (false, vec![]); + } + }; let paths = load_css_paths(&data); let mut ret = vec![]; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index e49395c6fd438..d2a950027cf87 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -1,17 +1,17 @@ //! The Rust AST Visitor. Extracts useful information and massages it into a form //! usable for `clean`. -use rustc::middle::privacy::AccessLevel; -use rustc::ty::TyCtxt; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::Node; +use rustc_middle::middle::privacy::AccessLevel; +use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, Span}; use std::mem; @@ -85,7 +85,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { fn visit_variant_data( &mut self, item: &'tcx hir::Item, - name: ast::Name, + name: Symbol, sd: &'tcx hir::VariantData, generics: &'tcx hir::Generics, ) -> Struct<'tcx> { @@ -106,7 +106,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { fn visit_union_data( &mut self, item: &'tcx hir::Item, - name: ast::Name, + name: Symbol, sd: &'tcx hir::VariantData, generics: &'tcx hir::Generics, ) -> Union<'tcx> { @@ -127,7 +127,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { fn visit_enum_def( &mut self, it: &'tcx hir::Item, - name: ast::Name, + name: Symbol, def: &'tcx hir::EnumDef, generics: &'tcx hir::Generics, ) -> Enum<'tcx> { @@ -157,35 +157,30 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { &mut self, om: &mut Module<'tcx>, item: &'tcx hir::Item, - name: ast::Name, + name: Symbol, decl: &'tcx hir::FnDecl, header: hir::FnHeader, generics: &'tcx hir::Generics, body: hir::BodyId, ) { debug!("visiting fn"); - let macro_kind = item - .attrs - .iter() - .filter_map(|a| { - if a.check_name(sym::proc_macro) { - Some(MacroKind::Bang) - } else if a.check_name(sym::proc_macro_derive) { - Some(MacroKind::Derive) - } else if a.check_name(sym::proc_macro_attribute) { - Some(MacroKind::Attr) - } else { - None - } - }) - .next(); + let macro_kind = item.attrs.iter().find_map(|a| { + if a.check_name(sym::proc_macro) { + Some(MacroKind::Bang) + } else if a.check_name(sym::proc_macro_derive) { + Some(MacroKind::Derive) + } else if a.check_name(sym::proc_macro_attribute) { + Some(MacroKind::Attr) + } else { + None + } + }); match macro_kind { Some(kind) => { let name = if kind == MacroKind::Derive { item.attrs .lists(sym::proc_macro_derive) - .filter_map(|mi| mi.ident()) - .next() + .find_map(|mi| mi.ident()) .expect("proc-macro derives require a name") .name } else { @@ -239,7 +234,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { vis: &'tcx hir::Visibility, id: hir::HirId, m: &'tcx hir::Mod<'tcx>, - name: Option, + name: Option, ) -> Module<'tcx> { let mut om = Module::new(name, attrs, vis); om.where_outer = span; @@ -269,7 +264,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { &mut self, id: hir::HirId, res: Res, - renamed: Option, + renamed: Option, glob: bool, om: &mut Module<'tcx>, please_inline: bool, @@ -309,14 +304,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let attrs = clean::inline::load_attrs(self.cx, res_did); let self_is_hidden = attrs.lists(sym::doc).has_word(sym::hidden); match res { - Res::Def(DefKind::Trait, did) - | Res::Def(DefKind::Struct, did) - | Res::Def(DefKind::Union, did) - | Res::Def(DefKind::Enum, did) - | Res::Def(DefKind::ForeignTy, did) - | Res::Def(DefKind::TyAlias, did) - if !self_is_hidden => - { + Res::Def( + DefKind::Trait + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::ForeignTy + | DefKind::TyAlias, + did, + ) if !self_is_hidden => { self.cx.renderinfo.get_mut().access_levels.map.insert(did, AccessLevel::Public); } Res::Def(DefKind::Mod, did) => { @@ -330,8 +326,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { return false; } - let res_hir_id = match tcx.hir().as_local_hir_id(res_did) { - Some(n) => n, + let res_hir_id = match res_did.as_local() { + Some(n) => tcx.hir().as_local_hir_id(n), None => return false, }; @@ -379,18 +375,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ret } - fn visit_item( - &mut self, - item: &'tcx hir::Item, - renamed: Option, - om: &mut Module<'tcx>, - ) { + fn visit_item(&mut self, item: &'tcx hir::Item, renamed: Option, om: &mut Module<'tcx>) { debug!("visiting item {:?}", item); let ident = renamed.unwrap_or(item.ident); if item.vis.node.is_pub() { let def_id = self.cx.tcx.hir().local_def_id(item.hir_id); - self.store_path(def_id); + self.store_path(def_id.to_def_id()); } match item.kind { @@ -597,7 +588,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { fn visit_foreign_item( &mut self, item: &'tcx hir::ForeignItem, - renamed: Option, + renamed: Option, om: &mut Module<'tcx>, ) { // If inlining we only want to include public functions. @@ -616,11 +607,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } // Convert each `exported_macro` into a doc item. - fn visit_local_macro( - &self, - def: &'tcx hir::MacroDef, - renamed: Option, - ) -> Macro<'tcx> { + fn visit_local_macro(&self, def: &'tcx hir::MacroDef, renamed: Option) -> Macro<'tcx> { debug!("visit_local_macro: {}", def.ident); let tts = def.ast.body.inner_tokens().trees().collect::>(); // Extract the spans of all matchers. They represent the "interface" of the macro. @@ -628,7 +615,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { Macro { hid: def.hir_id, - def_id: self.cx.tcx.hir().local_def_id(def.hir_id), + def_id: self.cx.tcx.hir().local_def_id(def.hir_id).to_def_id(), attrs: &def.attrs, name: renamed.unwrap_or(def.ident.name), whence: def.span, diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 12ffd6cac81dc..ea2f5f8abc701 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -1,8 +1,8 @@ -use rustc::middle::privacy::{AccessLevel, AccessLevels}; -use rustc::ty::{TyCtxt, Visibility}; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; +use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; +use rustc_middle::ty::{TyCtxt, Visibility}; use rustc_span::symbol::sym; use crate::clean::{AttributesExt, NestedAttributesExt}; diff --git a/src/libserialize/hex.rs b/src/libserialize/hex.rs deleted file mode 100644 index cfb165a3d4397..0000000000000 --- a/src/libserialize/hex.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! Hex binary-to-text encoding - -pub use self::FromHexError::*; - -use std::error; -use std::fmt; - -/// A trait for converting a value to hexadecimal encoding -pub trait ToHex { - /// Converts the value of `self` to a hex value, returning the owned - /// string. - fn to_hex(&self) -> String; -} - -const CHARS: &[u8] = b"0123456789abcdef"; - -impl ToHex for [u8] { - /// Turn a vector of `u8` bytes into a hexadecimal string. - /// - /// # Examples - /// - /// ``` - /// #![feature(rustc_private)] - /// - /// extern crate serialize; - /// use serialize::hex::ToHex; - /// - /// fn main () { - /// let str = [52,32].to_hex(); - /// println!("{}", str); - /// } - /// ``` - fn to_hex(&self) -> String { - let mut v = Vec::with_capacity(self.len() * 2); - for &byte in self { - v.push(CHARS[(byte >> 4) as usize]); - v.push(CHARS[(byte & 0xf) as usize]); - } - - unsafe { String::from_utf8_unchecked(v) } - } -} - -/// A trait for converting hexadecimal encoded values -pub trait FromHex { - /// Converts the value of `self`, interpreted as hexadecimal encoded data, - /// into an owned vector of bytes, returning the vector. - fn from_hex(&self) -> Result, FromHexError>; -} - -/// Errors that can occur when decoding a hex encoded string -#[derive(Copy, Clone, Debug)] -pub enum FromHexError { - /// The input contained a character not part of the hex format - InvalidHexCharacter(char, usize), - /// The input had an invalid length - InvalidHexLength, -} - -impl fmt::Display for FromHexError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - InvalidHexCharacter(ch, idx) => { - write!(f, "Invalid character '{}' at position {}", ch, idx) - } - InvalidHexLength => write!(f, "Invalid input length"), - } - } -} - -impl error::Error for FromHexError {} - -impl FromHex for str { - /// Converts any hexadecimal encoded string (literal, `@`, `&`, or `~`) - /// to the byte values it encodes. - /// - /// You can use the `String::from_utf8` function to turn a - /// `Vec` into a string with characters corresponding to those values. - /// - /// # Examples - /// - /// This converts a string literal to hexadecimal and back. - /// - /// ``` - /// #![feature(rustc_private)] - /// - /// extern crate serialize; - /// use serialize::hex::{FromHex, ToHex}; - /// - /// fn main () { - /// let hello_str = "Hello, World".as_bytes().to_hex(); - /// println!("{}", hello_str); - /// let bytes = hello_str.from_hex().unwrap(); - /// println!("{:?}", bytes); - /// let result_str = String::from_utf8(bytes).unwrap(); - /// println!("{}", result_str); - /// } - /// ``` - fn from_hex(&self) -> Result, FromHexError> { - // This may be an overestimate if there is any whitespace - let mut b = Vec::with_capacity(self.len() / 2); - let mut modulus = 0; - let mut buf = 0; - - for (idx, byte) in self.bytes().enumerate() { - buf <<= 4; - - match byte { - b'A'..=b'F' => buf |= byte - b'A' + 10, - b'a'..=b'f' => buf |= byte - b'a' + 10, - b'0'..=b'9' => buf |= byte - b'0', - b' ' | b'\r' | b'\n' | b'\t' => { - buf >>= 4; - continue; - } - _ => { - let ch = self[idx..].chars().next().unwrap(); - return Err(InvalidHexCharacter(ch, idx)); - } - } - - modulus += 1; - if modulus == 2 { - modulus = 0; - b.push(buf); - } - } - - match modulus { - 0 => Ok(b), - _ => Err(InvalidHexLength), - } - } -} - -#[cfg(test)] -mod tests; diff --git a/src/libserialize/hex/tests.rs b/src/libserialize/hex/tests.rs deleted file mode 100644 index ce62c0ff2329d..0000000000000 --- a/src/libserialize/hex/tests.rs +++ /dev/null @@ -1,67 +0,0 @@ -extern crate test; -use crate::hex::{FromHex, ToHex}; -use test::Bencher; - -#[test] -pub fn test_to_hex() { - assert_eq!("foobar".as_bytes().to_hex(), "666f6f626172"); -} - -#[test] -pub fn test_from_hex_okay() { - assert_eq!("666f6f626172".from_hex().unwrap(), b"foobar"); - assert_eq!("666F6F626172".from_hex().unwrap(), b"foobar"); -} - -#[test] -pub fn test_from_hex_odd_len() { - assert!("666".from_hex().is_err()); - assert!("66 6".from_hex().is_err()); -} - -#[test] -pub fn test_from_hex_invalid_char() { - assert!("66y6".from_hex().is_err()); -} - -#[test] -pub fn test_from_hex_ignores_whitespace() { - assert_eq!("666f 6f6\r\n26172 ".from_hex().unwrap(), b"foobar"); -} - -#[test] -pub fn test_to_hex_all_bytes() { - for i in 0..256 { - assert_eq!([i as u8].to_hex(), format!("{:02x}", i as usize)); - } -} - -#[test] -pub fn test_from_hex_all_bytes() { - for i in 0..256 { - let ii: &[u8] = &[i as u8]; - assert_eq!(format!("{:02x}", i as usize).from_hex().unwrap(), ii); - assert_eq!(format!("{:02X}", i as usize).from_hex().unwrap(), ii); - } -} - -#[bench] -pub fn bench_to_hex(b: &mut Bencher) { - let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \ - ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン"; - b.iter(|| { - s.as_bytes().to_hex(); - }); - b.bytes = s.len() as u64; -} - -#[bench] -pub fn bench_from_hex(b: &mut Bencher) { - let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \ - ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン"; - let sb = s.as_bytes().to_hex(); - b.iter(|| { - sb.from_hex().unwrap(); - }); - b.bytes = sb.len() as u64; -} diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 1f9d43cb93043..2d4e953ac5165 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -198,7 +198,7 @@ use std::num::FpCategory as Fp; use std::ops::Index; use std::str::FromStr; use std::string; -use std::{char, f64, fmt, str}; +use std::{char, fmt, str}; use crate::Encodable; @@ -1417,16 +1417,18 @@ enum ParserState { /// A Stack represents the current position of the parser in the logical /// structure of the JSON stream. -/// For example foo.bar[3].x +/// +/// An example is `foo.bar[3].x`. pub struct Stack { stack: Vec, str_buffer: Vec, } /// StackElements compose a Stack. -/// For example, StackElement::Key("foo"), StackElement::Key("bar"), -/// StackElement::Index(3) and StackElement::Key("x") are the -/// StackElements compositing the stack that represents foo.bar[3].x +/// +/// As an example, `StackElement::Key("foo")`, `StackElement::Key("bar")`, +/// `StackElement::Index(3)`, and `StackElement::Key("x")` are the +/// StackElements composing the stack that represents `foo.bar[3].x`. #[derive(PartialEq, Clone, Debug)] pub enum StackElement<'l> { Index(u32), @@ -2305,7 +2307,7 @@ macro_rules! read_primitive { value => Err(ExpectedError("Number".to_owned(), value.to_string())), } } - } + }; } impl crate::Decoder for Decoder { @@ -2682,11 +2684,11 @@ impl ToJson for Vec { } } -impl ToJson for BTreeMap { +impl ToJson for BTreeMap { fn to_json(&self) -> Json { let mut d = BTreeMap::new(); for (key, value) in self { - d.insert((*key).clone(), value.to_json()); + d.insert(key.to_string(), value.to_json()); } Json::Object(d) } diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs index b990e71bef0dd..7261d631a6f31 100644 --- a/src/libserialize/lib.rs +++ b/src/libserialize/lib.rs @@ -10,7 +10,7 @@ Core encoding and decoding interfaces. test(attr(allow(unused_variables), deny(warnings))) )] #![feature(box_syntax)] -#![feature(specialization)] +#![feature(specialization)] // FIXME: min_specialization does not work #![feature(never_type)] #![feature(nll)] #![feature(associated_type_bounds)] @@ -25,7 +25,6 @@ pub use self::serialize::{UseSpecializedDecodable, UseSpecializedEncodable}; mod collection_impls; mod serialize; -pub mod hex; pub mod json; pub mod leb128; diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index 8c6548cd3c5b2..e80d16bb0c7ab 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -706,6 +706,30 @@ impl Decodable for Vec { } } +impl Encodable for [u8; 20] { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))? + } + Ok(()) + }) + } +} + +impl Decodable for [u8; 20] { + fn decode(d: &mut D) -> Result<[u8; 20], D::Error> { + d.read_seq(|d, len| { + assert!(len == 20); + let mut v = [0u8; 20]; + for i in 0..len { + v[i] = d.read_seq_elt(i, |d| Decodable::decode(d))?; + } + Ok(v) + }) + } +} + impl<'a, T: Encodable> Encodable for Cow<'a, [T]> where [T]: ToOwned>, diff --git a/src/libserialize/tests/json.rs b/src/libserialize/tests/json.rs index c16426b5919c9..688bddc2baf46 100644 --- a/src/libserialize/tests/json.rs +++ b/src/libserialize/tests/json.rs @@ -17,7 +17,6 @@ use rustc_serialize::{Decodable, Encodable}; use std::collections::BTreeMap; use std::io::prelude::*; use std::string; -use std::{f32, f64, i64, u64}; use Animal::*; #[derive(RustcDecodable, Eq, PartialEq, Debug)] diff --git a/src/libserialize/tests/opaque.rs b/src/libserialize/tests/opaque.rs index aa099bb8a3689..a6ec1580aca36 100644 --- a/src/libserialize/tests/opaque.rs +++ b/src/libserialize/tests/opaque.rs @@ -53,7 +53,7 @@ fn test_unit() { #[test] fn test_u8() { let mut vec = vec![]; - for i in ::std::u8::MIN..::std::u8::MAX { + for i in u8::MIN..u8::MAX { vec.push(i); } check_round_trip(vec); @@ -61,30 +61,30 @@ fn test_u8() { #[test] fn test_u16() { - for i in ::std::u16::MIN..::std::u16::MAX { + for i in u16::MIN..u16::MAX { check_round_trip(vec![1, 2, 3, i, i, i]); } } #[test] fn test_u32() { - check_round_trip(vec![1, 2, 3, ::std::u32::MIN, 0, 1, ::std::u32::MAX, 2, 1]); + 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, ::std::u64::MIN, 0, 1, ::std::u64::MAX, 2, 1]); + 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, ::std::usize::MIN, 0, 1, ::std::usize::MAX, 2, 1]); + 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 ::std::i8::MIN..::std::i8::MAX { + for i in i8::MIN..i8::MAX { vec.push(i); } check_round_trip(vec); @@ -92,24 +92,24 @@ fn test_i8() { #[test] fn test_i16() { - for i in ::std::i16::MIN..::std::i16::MAX { + for i in i16::MIN..i16::MAX { check_round_trip(vec![-1, 2, -3, i, i, i, 2]); } } #[test] fn test_i32() { - check_round_trip(vec![-1, 2, -3, ::std::i32::MIN, 0, 1, ::std::i32::MAX, 2, 1]); + 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, ::std::i64::MIN, 0, 1, ::std::i64::MAX, 2, 1]); + 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, ::std::isize::MIN, 0, 1, ::std::isize::MAX, 2, 1]); + check_round_trip(vec![-1, 2, -3, isize::MIN, 0, 1, isize::MAX, 2, 1]); } #[test] diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index b147aa55b2a61..077ca2a2faa7e 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -27,7 +27,7 @@ hashbrown = { version = "0.6.2", default-features = false, features = ['rustc-de [dependencies.backtrace_rs] package = "backtrace" -version = "0.3.44" +version = "0.3.46" default-features = false # without the libstd `backtrace` feature, stub out everything features = [ "rustc-dep-of-std" ] # enable build support for integrating into libstd @@ -41,13 +41,13 @@ dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } [target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies] -hermit-abi = { version = "0.1", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.1.13", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } [features] -default = ["std_detect_file_io", "std_detect_dlsym_getauxval"] +default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] backtrace = [ "backtrace_rs/dbghelp", # backtrace/symbolize on MSVC diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 25f3ddcbebab6..38d223d84e90f 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -61,6 +61,7 @@ #![stable(feature = "alloc_module", since = "1.28.0")] +use core::intrinsics; use core::ptr::NonNull; use core::sync::atomic::{AtomicPtr, Ordering}; use core::{mem, ptr}; @@ -138,59 +139,99 @@ pub struct System; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for System { #[inline] - fn alloc(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { - if layout.size() == 0 { - Ok((layout.dangling(), 0)) - } else { - unsafe { - NonNull::new(GlobalAlloc::alloc(self, layout)) - .ok_or(AllocErr) - .map(|p| (p, layout.size())) + fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { + unsafe { + let size = layout.size(); + if size == 0 { + Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) + } else { + let raw_ptr = match init { + AllocInit::Uninitialized => GlobalAlloc::alloc(self, layout), + AllocInit::Zeroed => GlobalAlloc::alloc_zeroed(self, layout), + }; + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + Ok(MemoryBlock { ptr, size }) } } } #[inline] - fn alloc_zeroed(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { - if layout.size() == 0 { - Ok((layout.dangling(), 0)) - } else { - unsafe { - NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)) - .ok_or(AllocErr) - .map(|p| (p, layout.size())) - } + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - if layout.size() != 0 { - GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) + unsafe fn grow( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + init: AllocInit, + ) -> Result { + let size = layout.size(); + debug_assert!( + new_size >= size, + "`new_size` must be greater than or equal to `memory.size()`" + ); + + if size == new_size { + return Ok(MemoryBlock { ptr, size }); + } + + match placement { + ReallocPlacement::InPlace => Err(AllocErr), + ReallocPlacement::MayMove if layout.size() == 0 => { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + self.alloc(new_layout, init) + } + ReallocPlacement::MayMove => { + // `realloc` probably checks for `new_size > size` or something similar. + intrinsics::assume(new_size > size); + let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size); + let memory = + MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }; + init.init_offset(memory, size); + Ok(memory) + } } } #[inline] - unsafe fn realloc( + unsafe fn shrink( &mut self, ptr: NonNull, layout: Layout, new_size: usize, - ) -> Result<(NonNull, usize), AllocErr> { - match (layout.size(), new_size) { - (0, 0) => Ok((layout.dangling(), 0)), - (0, _) => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())), - (_, 0) => { + placement: ReallocPlacement, + ) -> Result { + let size = layout.size(); + debug_assert!( + new_size <= size, + "`new_size` must be smaller than or equal to `memory.size()`" + ); + + if size == new_size { + return Ok(MemoryBlock { ptr, size }); + } + + match placement { + ReallocPlacement::InPlace => Err(AllocErr), + ReallocPlacement::MayMove if new_size == 0 => { self.dealloc(ptr, layout); - Ok((layout.dangling(), 0)) + Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) + } + ReallocPlacement::MayMove => { + // `realloc` probably checks for `new_size < size` or something similar. + intrinsics::assume(new_size < size); + let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size); + Ok(MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }) } - (_, _) => NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)) - .ok_or(AllocErr) - .map(|p| (p, new_size)), } } } - static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. @@ -238,9 +279,7 @@ pub fn rust_oom(layout: Layout) -> ! { let hook: fn(Layout) = if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; hook(layout); - unsafe { - crate::sys::abort_internal(); - } + crate::process::abort() } #[cfg(not(test))] @@ -250,10 +289,10 @@ pub fn rust_oom(layout: Layout) -> ! { pub mod __default_lib_allocator { use super::{GlobalAlloc, Layout, System}; // These magic symbol names are used as a fallback for implementing the - // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs) when there is + // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is // no `#[global_allocator]` attribute. - // for symbol names src/librustc/middle/allocator.rs + // for symbol names src/librustc_ast/expand/allocator.rs // for signatures src/librustc_allocator/lib.rs // linkage directives are provided as part of the current compiler allocator diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 41bdfea53e559..5cd2a25b11768 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -149,23 +149,35 @@ pub trait AsciiExt { macro_rules! delegating_ascii_methods { () => { #[inline] - fn is_ascii(&self) -> bool { self.is_ascii() } + fn is_ascii(&self) -> bool { + self.is_ascii() + } #[inline] - fn to_ascii_uppercase(&self) -> Self::Owned { self.to_ascii_uppercase() } + fn to_ascii_uppercase(&self) -> Self::Owned { + self.to_ascii_uppercase() + } #[inline] - fn to_ascii_lowercase(&self) -> Self::Owned { self.to_ascii_lowercase() } + fn to_ascii_lowercase(&self) -> Self::Owned { + self.to_ascii_lowercase() + } #[inline] - fn eq_ignore_ascii_case(&self, o: &Self) -> bool { self.eq_ignore_ascii_case(o) } + fn eq_ignore_ascii_case(&self, o: &Self) -> bool { + self.eq_ignore_ascii_case(o) + } #[inline] - fn make_ascii_uppercase(&mut self) { self.make_ascii_uppercase(); } + fn make_ascii_uppercase(&mut self) { + self.make_ascii_uppercase(); + } #[inline] - fn make_ascii_lowercase(&mut self) { self.make_ascii_lowercase(); } - } + fn make_ascii_lowercase(&mut self) { + self.make_ascii_lowercase(); + } + }; } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 8db7bc12cd308..743a1778fbda3 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -25,6 +25,14 @@ fn main() { println!("cargo:rustc-link-lib=posix4"); println!("cargo:rustc-link-lib=pthread"); println!("cargo:rustc-link-lib=resolv"); + } else if target.contains("illumos") { + println!("cargo:rustc-link-lib=socket"); + println!("cargo:rustc-link-lib=posix4"); + println!("cargo:rustc-link-lib=pthread"); + println!("cargo:rustc-link-lib=resolv"); + println!("cargo:rustc-link-lib=nsl"); + // Use libumem for the (malloc-compatible) allocator + println!("cargo:rustc-link-lib=umem"); } else if target.contains("apple-darwin") { println!("cargo:rustc-link-lib=System"); diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 44f8e8bd1717a..56cf9be339194 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -198,6 +198,7 @@ use crate::sys; /// ``` #[derive(Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashMap { base: base::HashMap, @@ -250,6 +251,9 @@ impl HashMap { /// cause many collisions and very poor performance. Setting it /// manually using this function can expose a DoS attack vector. /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// /// # Examples /// /// ``` @@ -260,6 +264,8 @@ impl HashMap { /// let mut map = HashMap::with_hasher(s); /// map.insert(1, 2); /// ``` + /// + /// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_hasher(hash_builder: S) -> HashMap { @@ -277,6 +283,9 @@ impl HashMap { /// cause many collisions and very poor performance. Setting it /// manually using this function can expose a DoS attack vector. /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// /// # Examples /// /// ``` @@ -287,6 +296,8 @@ impl HashMap { /// let mut map = HashMap::with_capacity_and_hasher(10, s); /// map.insert(1, 2); /// ``` + /// + /// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap { @@ -1943,6 +1954,34 @@ impl<'a, K, V> Entry<'a, K, V> { } } + #[unstable(feature = "or_insert_with_key", issue = "71024")] + /// Ensures a value is in the entry by inserting, if empty, the result of the default function, + /// which takes the key as its argument, and returns a mutable reference to the value in the + /// entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(or_insert_with_key)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, usize> = HashMap::new(); + /// + /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); + /// + /// assert_eq!(map["poneyland"], 9); + /// ``` + #[inline] + pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let value = default(entry.key()); + entry.insert(value) + } + } + } + /// Returns a reference to this entry's key. /// /// # Examples @@ -2617,7 +2656,6 @@ mod test_map { use crate::cell::RefCell; use rand::{thread_rng, Rng}; use realstd::collections::TryReserveError::*; - use realstd::usize; // https://github.com/rust-lang/rust/issues/62301 fn _assert_hashmap_is_unwind_safe() { diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 1ad99f03703dd..ca06457291cae 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -105,6 +105,7 @@ use super::map::{self, HashMap, Keys, RandomState}; /// [`PartialEq`]: ../../std/cmp/trait.PartialEq.html /// [`RefCell`]: ../../std/cell/struct.RefCell.html #[derive(Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashSet { map: HashMap, @@ -272,6 +273,9 @@ impl HashSet { /// cause many collisions and very poor performance. Setting it /// manually using this function can expose a DoS attack vector. /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// /// # Examples /// /// ``` @@ -282,6 +286,8 @@ impl HashSet { /// let mut set = HashSet::with_hasher(s); /// set.insert(2); /// ``` + /// + /// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_hasher(hasher: S) -> HashSet { @@ -299,6 +305,9 @@ impl HashSet { /// cause many collisions and very poor performance. Setting it /// manually using this function can expose a DoS attack vector. /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// /// # Examples /// /// ``` @@ -309,6 +318,8 @@ impl HashSet { /// let mut set = HashSet::with_capacity_and_hasher(10, s); /// set.insert(1); /// ``` + /// + /// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index e8b9e9cb1f29c..cc6663bebd3d4 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -110,10 +110,10 @@ //! //! For Sets, all operations have the cost of the equivalent Map operation. //! -//! | | get | insert | remove | predecessor | append | -//! |--------------|-----------|----------|----------|-------------|--------| -//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | -//! | [`BTreeMap`] | O(log n) | O(log n) | O(log n) | O(log n) | O(n+m) | +//! | | get | insert | remove | predecessor | append | +//! |--------------|-----------|-----------|-----------|-------------|--------| +//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | +//! | [`BTreeMap`] | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | O(n+m) | //! //! # Correct and Efficient Usage of Collections //! diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 6aad082a97f9a..97c20ca9459ef 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -567,7 +567,7 @@ impl Error for JoinPathsError { #[rustc_deprecated( since = "1.29.0", reason = "This function's behavior is unexpected and probably not what you want. \ - Consider using the home_dir function from https://crates.io/crates/dirs instead." + Consider using a crate from crates.io instead." )] #[stable(feature = "env", since = "1.0.0")] pub fn home_dir() -> Option { diff --git a/src/libstd/error.rs b/src/libstd/error.rs index b394f2efc2e35..24b57f12e8df4 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -15,7 +15,7 @@ use core::array; -use crate::alloc::{AllocErr, CannotReallocInPlace, LayoutErr}; +use crate::alloc::{AllocErr, LayoutErr}; use crate::any::TypeId; use crate::backtrace::Backtrace; use crate::borrow::Cow; @@ -409,13 +409,6 @@ impl Error for AllocErr {} )] impl Error for LayoutErr {} -#[unstable( - feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked.", - issue = "32838" -)] -impl Error for CannotReallocInPlace {} - #[stable(feature = "rust1", since = "1.0.0")] impl Error for str::ParseBoolError { #[allow(deprecated)] diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 20425aea8d517..8e743ace99bfb 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -112,8 +112,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 3.6_f32; /// let y = -3.6_f32; /// let abs_difference_x = (x.fract() - 0.6).abs(); @@ -135,8 +133,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 3.5_f32; /// let y = -3.5_f32; /// @@ -164,8 +160,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let f = 3.5_f32; /// /// assert_eq!(f.signum(), 1.0); @@ -177,7 +171,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn signum(self) -> f32 { - if self.is_nan() { NAN } else { 1.0_f32.copysign(self) } + if self.is_nan() { Self::NAN } else { 1.0_f32.copysign(self) } } /// Returns a number composed of the magnitude of `self` and the sign of @@ -190,8 +184,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let f = 3.5_f32; /// /// assert_eq!(f.copysign(0.42), 3.5_f32); @@ -217,8 +209,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let m = 10.0_f32; /// let x = 4.0_f32; /// let b = 60.0_f32; @@ -284,7 +274,7 @@ impl f32 { /// assert_eq!(a.rem_euclid(-b), 3.0); /// assert_eq!((-a).rem_euclid(-b), 1.0); /// // limitation due to round-off error - /// assert!((-std::f32::EPSILON).rem_euclid(3.0) != 0.0); + /// assert!((-f32::EPSILON).rem_euclid(3.0) != 0.0); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[inline] @@ -301,8 +291,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); /// @@ -320,8 +308,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); /// @@ -341,8 +327,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let positive = 4.0_f32; /// let negative = -4.0_f32; /// @@ -363,8 +347,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let one = 1.0f32; /// // e^1 /// let e = one.exp(); @@ -386,8 +368,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let f = 2.0f32; /// /// // 2^2 - 4 == 0 @@ -407,8 +387,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let one = 1.0f32; /// // e^1 /// let e = one.exp(); @@ -434,8 +412,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let five = 5.0f32; /// /// // log5(5) - 1 == 0 @@ -455,8 +431,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let two = 2.0f32; /// /// // log2(2) - 1 == 0 @@ -479,8 +453,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let ten = 10.0f32; /// /// // log10(10) - 1 == 0 @@ -503,8 +475,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 3.0f32; /// let y = -3.0f32; /// @@ -536,8 +506,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 8.0f32; /// /// // x^(1/3) - 2 == 0 @@ -558,8 +526,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 2.0f32; /// let y = 3.0f32; /// @@ -580,9 +546,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let x = f32::consts::FRAC_PI_2; + /// let x = std::f32::consts::FRAC_PI_2; /// /// let abs_difference = (x.sin() - 1.0).abs(); /// @@ -600,9 +564,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let x = 2.0 * f32::consts::PI; + /// let x = 2.0 * std::f32::consts::PI; /// /// let abs_difference = (x.cos() - 1.0).abs(); /// @@ -620,9 +582,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let x = f32::consts::FRAC_PI_4; + /// let x = std::f32::consts::FRAC_PI_4; /// let abs_difference = (x.tan() - 1.0).abs(); /// /// assert!(abs_difference <= f32::EPSILON); @@ -641,12 +601,10 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let f = f32::consts::FRAC_PI_2; + /// let f = std::f32::consts::FRAC_PI_2; /// /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - f32::consts::FRAC_PI_2).abs(); + /// let abs_difference = (f.sin().asin() - std::f32::consts::FRAC_PI_2).abs(); /// /// assert!(abs_difference <= f32::EPSILON); /// ``` @@ -664,12 +622,10 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let f = f32::consts::FRAC_PI_4; + /// let f = std::f32::consts::FRAC_PI_4; /// /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - f32::consts::FRAC_PI_4).abs(); + /// let abs_difference = (f.cos().acos() - std::f32::consts::FRAC_PI_4).abs(); /// /// assert!(abs_difference <= f32::EPSILON); /// ``` @@ -686,8 +642,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let f = 1.0f32; /// /// // atan(tan(1)) @@ -712,8 +666,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// // Positive angles measured counter-clockwise /// // from positive x axis /// // -pi/4 radians (45 deg clockwise) @@ -724,8 +676,8 @@ impl f32 { /// let x2 = -3.0f32; /// let y2 = 3.0f32; /// - /// let abs_difference_1 = (y1.atan2(x1) - (-f32::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * f32::consts::FRAC_PI_4)).abs(); + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f32::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f32::consts::FRAC_PI_4)).abs(); /// /// assert!(abs_difference_1 <= f32::EPSILON); /// assert!(abs_difference_2 <= f32::EPSILON); @@ -743,9 +695,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let x = f32::consts::FRAC_PI_4; + /// let x = std::f32::consts::FRAC_PI_4; /// let f = x.sin_cos(); /// /// let abs_difference_0 = (f.0 - x.sin()).abs(); @@ -766,8 +716,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 6.0f32; /// /// // e^(ln(6)) - 1 @@ -788,9 +736,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let x = f32::consts::E - 1.0; + /// let x = std::f32::consts::E - 1.0; /// /// // ln(1 + (e - 1)) == ln(e) == 1 /// let abs_difference = (x.ln_1p() - 1.0).abs(); @@ -809,9 +755,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let e = f32::consts::E; + /// let e = std::f32::consts::E; /// let x = 1.0f32; /// /// let f = x.sinh(); @@ -833,9 +777,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let e = f32::consts::E; + /// let e = std::f32::consts::E; /// let x = 1.0f32; /// let f = x.cosh(); /// // Solving cosh() at 1 gives this result @@ -857,9 +799,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let e = f32::consts::E; + /// let e = std::f32::consts::E; /// let x = 1.0f32; /// /// let f = x.tanh(); @@ -881,8 +821,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 1.0f32; /// let f = x.sinh().asinh(); /// @@ -894,8 +832,8 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f32 { - if self == NEG_INFINITY { - NEG_INFINITY + if self == Self::NEG_INFINITY { + Self::NEG_INFINITY } else { (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) } @@ -906,8 +844,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 1.0f32; /// let f = x.cosh().acosh(); /// @@ -919,7 +855,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acosh(self) -> f32 { - if self < 1.0 { crate::f32::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } + if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } } /// Inverse hyperbolic tangent function. @@ -927,9 +863,7 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// - /// let e = f32::consts::E; + /// let e = std::f32::consts::E; /// let f = e.tanh().atanh(); /// /// let abs_difference = (f - e).abs(); @@ -962,7 +896,7 @@ impl f32 { /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0); /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); - /// assert!((std::f32::NAN).clamp(-2.0, 1.0).is_nan()); + /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan()); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "clamp", issue = "44095")] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index a1128a589a64a..fe64d27b1efc8 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -133,8 +133,6 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// /// let x = 3.5_f64; /// let y = -3.5_f64; /// @@ -162,8 +160,6 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// /// let f = 3.5_f64; /// /// assert_eq!(f.signum(), 1.0); @@ -175,7 +171,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn signum(self) -> f64 { - if self.is_nan() { NAN } else { 1.0_f64.copysign(self) } + if self.is_nan() { Self::NAN } else { 1.0_f64.copysign(self) } } /// Returns a number composed of the magnitude of `self` and the sign of @@ -188,8 +184,6 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// /// let f = 3.5_f64; /// /// assert_eq!(f.copysign(0.42), 3.5_f64); @@ -280,7 +274,7 @@ impl f64 { /// assert_eq!(a.rem_euclid(-b), 3.0); /// assert_eq!((-a).rem_euclid(-b), 1.0); /// // limitation due to round-off error - /// assert!((-std::f64::EPSILON).rem_euclid(3.0) != 0.0); + /// assert!((-f64::EPSILON).rem_euclid(3.0) != 0.0); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[inline] @@ -554,9 +548,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let x = f64::consts::FRAC_PI_2; + /// let x = std::f64::consts::FRAC_PI_2; /// /// let abs_difference = (x.sin() - 1.0).abs(); /// @@ -574,9 +566,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let x = 2.0 * f64::consts::PI; + /// let x = 2.0 * std::f64::consts::PI; /// /// let abs_difference = (x.cos() - 1.0).abs(); /// @@ -594,9 +584,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let x = f64::consts::FRAC_PI_4; + /// let x = std::f64::consts::FRAC_PI_4; /// let abs_difference = (x.tan() - 1.0).abs(); /// /// assert!(abs_difference < 1e-14); @@ -615,12 +603,10 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let f = f64::consts::FRAC_PI_2; + /// let f = std::f64::consts::FRAC_PI_2; /// /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - f64::consts::FRAC_PI_2).abs(); + /// let abs_difference = (f.sin().asin() - std::f64::consts::FRAC_PI_2).abs(); /// /// assert!(abs_difference < 1e-10); /// ``` @@ -638,12 +624,10 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let f = f64::consts::FRAC_PI_4; + /// let f = std::f64::consts::FRAC_PI_4; /// /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - f64::consts::FRAC_PI_4).abs(); + /// let abs_difference = (f.cos().acos() - std::f64::consts::FRAC_PI_4).abs(); /// /// assert!(abs_difference < 1e-10); /// ``` @@ -684,8 +668,6 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// /// // Positive angles measured counter-clockwise /// // from positive x axis /// // -pi/4 radians (45 deg clockwise) @@ -696,8 +678,8 @@ impl f64 { /// let x2 = -3.0_f64; /// let y2 = 3.0_f64; /// - /// let abs_difference_1 = (y1.atan2(x1) - (-f64::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * f64::consts::FRAC_PI_4)).abs(); + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f64::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f64::consts::FRAC_PI_4)).abs(); /// /// assert!(abs_difference_1 < 1e-10); /// assert!(abs_difference_2 < 1e-10); @@ -715,9 +697,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let x = f64::consts::FRAC_PI_4; + /// let x = std::f64::consts::FRAC_PI_4; /// let f = x.sin_cos(); /// /// let abs_difference_0 = (f.0 - x.sin()).abs(); @@ -758,9 +738,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let x = f64::consts::E - 1.0; + /// let x = std::f64::consts::E - 1.0; /// /// // ln(1 + (e - 1)) == ln(e) == 1 /// let abs_difference = (x.ln_1p() - 1.0).abs(); @@ -779,9 +757,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let e = f64::consts::E; + /// let e = std::f64::consts::E; /// let x = 1.0_f64; /// /// let f = x.sinh(); @@ -803,9 +779,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let e = f64::consts::E; + /// let e = std::f64::consts::E; /// let x = 1.0_f64; /// let f = x.cosh(); /// // Solving cosh() at 1 gives this result @@ -827,9 +801,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let e = f64::consts::E; + /// let e = std::f64::consts::E; /// let x = 1.0_f64; /// /// let f = x.tanh(); @@ -862,8 +834,8 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f64 { - if self == NEG_INFINITY { - NEG_INFINITY + if self == Self::NEG_INFINITY { + Self::NEG_INFINITY } else { (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) } @@ -885,7 +857,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acosh(self) -> f64 { - if self < 1.0 { NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } + if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } } /// Inverse hyperbolic tangent function. @@ -893,9 +865,7 @@ impl f64 { /// # Examples /// /// ``` - /// use std::f64; - /// - /// let e = f64::consts::E; + /// let e = std::f64::consts::E; /// let f = e.tanh().atanh(); /// /// let abs_difference = (f - e).abs(); @@ -928,7 +898,7 @@ impl f64 { /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0); /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); - /// assert!((std::f64::NAN).clamp(-2.0, 1.0).is_nan()); + /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan()); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "clamp", issue = "44095")] @@ -949,23 +919,23 @@ impl f64 { // because of their non-standard behavior (e.g., log(-n) returns -Inf instead // of expected NaN). fn log_wrapper f64>(self, log_fn: F) -> f64 { - if !cfg!(target_os = "solaris") { + if !cfg!(any(target_os = "solaris", target_os = "illumos")) { log_fn(self) } else { if self.is_finite() { if self > 0.0 { log_fn(self) } else if self == 0.0 { - NEG_INFINITY // log(0) = -Inf + Self::NEG_INFINITY // log(0) = -Inf } else { - NAN // log(-n) = NaN + Self::NAN // log(-n) = NaN } } else if self.is_nan() { self // log(NaN) = NaN } else if self > 0.0 { self // log(Inf) = Inf } else { - NAN // log(-Inf) = NaN + Self::NAN // log(-Inf) = NaN } } } diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 04eaba515ff22..4bac9a4917d8f 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -730,6 +730,17 @@ impl From<&CStr> for Box { } } +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + #[inline] + fn from(cow: Cow<'_, CStr>) -> Box { + match cow { + Cow::Borrowed(s) => Box::from(s), + Cow::Owned(s) => Box::from(s), + } + } +} + #[stable(feature = "c_string_from_box", since = "1.18.0")] impl From> for CString { /// Converts a [`Box`]`` into a [`CString`] without copying or allocating. @@ -1329,6 +1340,12 @@ impl ToOwned for CStr { fn to_owned(&self) -> CString { CString { inner: self.to_bytes_with_nul().into() } } + + fn clone_into(&self, target: &mut CString) { + let mut b = Vec::from(mem::take(&mut target.inner)); + self.to_bytes_with_nul().clone_into(&mut b); + target.inner = b.into_boxed_slice(); + } } #[stable(feature = "cstring_asref", since = "1.7.0")] @@ -1510,6 +1527,17 @@ mod tests { assert_eq!(boxed.to_bytes_with_nul(), &[0]); } + #[test] + fn test_c_str_clone_into() { + let mut c_string = CString::new("lorem").unwrap(); + let c_ptr = c_string.as_ptr(); + let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap(); + c_str.clone_into(&mut c_string); + assert_eq!(c_str, c_string.as_c_str()); + // The exact same size shouldn't have needed to move its allocation + assert_eq!(c_ptr, c_string.as_ptr()); + } + #[test] fn into_rc() { let orig: &[u8] = b"Hello, world!\0"; diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index 72f7367c9dcdb..5aca7b7476a52 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -43,8 +43,8 @@ //! terminator, so the buffer length is really `len+1` characters. //! Rust strings don't have a nul terminator; their length is always //! stored and does not need to be calculated. While in Rust -//! accessing a string's length is a O(1) operation (because the -//! length is stored); in C it is an O(length) operation because the +//! accessing a string's length is a `O(1)` operation (because the +//! length is stored); in C it is an `O(length)` operation because the //! length needs to be computed by scanning the string for the nul //! terminator. //! diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 77da97219b147..d1eaf3c583f2d 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -4,6 +4,7 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::ops; use crate::rc::Rc; +use crate::str::FromStr; use crate::sync::Arc; use crate::sys::os_str::{Buf, Slice}; @@ -379,6 +380,14 @@ impl ops::Index for OsString { } } +#[stable(feature = "mut_osstr", since = "1.44.0")] +impl ops::IndexMut for OsString { + #[inline] + fn index_mut(&mut self, _index: ops::RangeFull) -> &mut OsStr { + OsStr::from_inner_mut(self.inner.as_mut_slice()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl ops::Deref for OsString { type Target = OsStr; @@ -389,6 +398,14 @@ impl ops::Deref for OsString { } } +#[stable(feature = "mut_osstr", since = "1.44.0")] +impl ops::DerefMut for OsString { + #[inline] + fn deref_mut(&mut self) -> &mut OsStr { + &mut self[..] + } +} + #[stable(feature = "osstring_default", since = "1.9.0")] impl Default for OsString { /// Constructs an empty `OsString`. @@ -509,9 +526,20 @@ impl OsStr { #[inline] fn from_inner(inner: &Slice) -> &OsStr { + // Safety: OsStr is just a wrapper of Slice, + // therefore converting &Slice to &OsStr is safe. unsafe { &*(inner as *const Slice as *const OsStr) } } + #[inline] + fn from_inner_mut(inner: &mut Slice) -> &mut OsStr { + // Safety: OsStr is just a wrapper of Slice, + // therefore converting &mut Slice to &mut OsStr is safe. + // Any method that mutates OsStr must be careful not to + // break platform-specific encoding, in particular Wtf8 on Windows. + unsafe { &mut *(inner as *mut Slice as *mut OsStr) } + } + /// Yields a [`&str`] slice if the `OsStr` is valid Unicode. /// /// This conversion may entail doing a check for UTF-8 validity. @@ -671,6 +699,147 @@ impl OsStr { fn bytes(&self) -> &[u8] { unsafe { &*(&self.inner as *const _ as *const [u8]) } } + + /// Converts this string to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// let mut s = OsString::from("GRÜßE, JÜRGEN ❤"); + /// + /// s.make_ascii_lowercase(); + /// + /// assert_eq!("grÜße, jÜrgen ❤", s); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn make_ascii_lowercase(&mut self) { + self.inner.make_ascii_lowercase() + } + + /// Converts this string to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// let mut s = OsString::from("Grüße, Jürgen ❤"); + /// + /// s.make_ascii_uppercase(); + /// + /// assert_eq!("GRüßE, JüRGEN ❤", s); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn make_ascii_uppercase(&mut self) { + self.inner.make_ascii_uppercase() + } + + /// Returns a copy of this string where each character is mapped to its + /// ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// let s = OsString::from("Grüße, Jürgen ❤"); + /// + /// assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase()); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn to_ascii_lowercase(&self) -> OsString { + OsString::from_inner(self.inner.to_ascii_lowercase()) + } + + /// Returns a copy of this string where each character is mapped to its + /// ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// let s = OsString::from("Grüße, Jürgen ❤"); + /// + /// assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase()); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn to_ascii_uppercase(&self) -> OsString { + OsString::from_inner(self.inner.to_ascii_uppercase()) + } + + /// Checks if all characters in this string are within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// let ascii = OsString::from("hello!\n"); + /// let non_ascii = OsString::from("Grüße, Jürgen ❤"); + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn is_ascii(&self) -> bool { + self.inner.is_ascii() + } + + /// Checks that two strings are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// assert!(OsString::from("Ferris").eq_ignore_ascii_case("FERRIS")); + /// assert!(OsString::from("Ferrös").eq_ignore_ascii_case("FERRöS")); + /// assert!(!OsString::from("Ferrös").eq_ignore_ascii_case("FERRÖS")); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn eq_ignore_ascii_case>(&self, other: &S) -> bool { + self.inner.eq_ignore_ascii_case(&other.as_ref().inner) + } } #[stable(feature = "box_from_os_str", since = "1.17.0")] @@ -681,6 +850,17 @@ impl From<&OsStr> for Box { } } +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + #[inline] + fn from(cow: Cow<'_, OsStr>) -> Box { + match cow { + Cow::Borrowed(s) => Box::from(s), + Cow::Owned(s) => Box::from(s), + } + } +} + #[stable(feature = "os_string_from_box", since = "1.18.0")] impl From> for OsString { /// Converts a [`Box`]`<`[`OsStr`]`>` into a `OsString` without copying or @@ -952,8 +1132,7 @@ impl ToOwned for OsStr { self.to_os_string() } fn clone_into(&self, target: &mut OsString) { - target.clear(); - target.push(self); + self.inner.clone_into(&mut target.inner) } } @@ -1007,6 +1186,15 @@ impl AsInner for OsStr { } } +#[stable(feature = "osstring_from_str", since = "1.45.0")] +impl FromStr for OsString { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(OsString::from(s)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index e20fcfafa229b..f4c164a324e32 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -659,6 +659,11 @@ impl Read for File { self.inner.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -674,6 +679,11 @@ impl Write for File { self.inner.write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { self.inner.flush() } @@ -694,6 +704,11 @@ impl Read for &File { self.inner.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -709,6 +724,11 @@ impl Write for &File { self.inner.write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { self.inner.flush() } @@ -734,7 +754,7 @@ impl OpenOptions { /// let file = options.read(true).open("foo.txt"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> OpenOptions { + pub fn new() -> Self { OpenOptions(fs_imp::OpenOptions::new()) } @@ -751,7 +771,7 @@ impl OpenOptions { /// let file = OpenOptions::new().read(true).open("foo.txt"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn read(&mut self, read: bool) -> &mut OpenOptions { + pub fn read(&mut self, read: bool) -> &mut Self { self.0.read(read); self } @@ -772,7 +792,7 @@ impl OpenOptions { /// let file = OpenOptions::new().write(true).open("foo.txt"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn write(&mut self, write: bool) -> &mut OpenOptions { + pub fn write(&mut self, write: bool) -> &mut Self { self.0.write(write); self } @@ -819,7 +839,7 @@ impl OpenOptions { /// let file = OpenOptions::new().append(true).open("foo.txt"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn append(&mut self, append: bool) -> &mut OpenOptions { + pub fn append(&mut self, append: bool) -> &mut Self { self.0.append(append); self } @@ -839,7 +859,7 @@ impl OpenOptions { /// let file = OpenOptions::new().write(true).truncate(true).open("foo.txt"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { + pub fn truncate(&mut self, truncate: bool) -> &mut Self { self.0.truncate(truncate); self } @@ -860,7 +880,7 @@ impl OpenOptions { /// let file = OpenOptions::new().write(true).create(true).open("foo.txt"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn create(&mut self, create: bool) -> &mut OpenOptions { + pub fn create(&mut self, create: bool) -> &mut Self { self.0.create(create); self } @@ -893,7 +913,7 @@ impl OpenOptions { /// .open("foo.txt"); /// ``` #[stable(feature = "expand_open_options2", since = "1.9.0")] - pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { + pub fn create_new(&mut self, create_new: bool) -> &mut Self { self.0.create_new(create_new); self } diff --git a/src/libstd/future.rs b/src/libstd/future.rs index c0675eeba98da..e2092cfefa369 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -1,111 +1,5 @@ //! Asynchronous values. -#[cfg(bootstrap)] -use core::{ - cell::Cell, - marker::Unpin, - ops::{Drop, Generator, GeneratorState}, - pin::Pin, - ptr::NonNull, - task::{Context, Poll}, -}; - #[doc(inline)] #[stable(feature = "futures_api", since = "1.36.0")] pub use core::future::*; - -/// Wrap a generator in a future. -/// -/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give -/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). -// This is `const` to avoid extra errors after we recover from `const async fn` -#[cfg(bootstrap)] -#[doc(hidden)] -#[unstable(feature = "gen_future", issue = "50547")] -pub const fn from_generator>(x: T) -> impl Future { - GenFuture(x) -} - -/// A wrapper around generators used to implement `Future` for `async`/`await` code. -#[cfg(bootstrap)] -#[doc(hidden)] -#[unstable(feature = "gen_future", issue = "50547")] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -struct GenFuture>(T); - -// We rely on the fact that async/await futures are immovable in order to create -// self-referential borrows in the underlying generator. -#[cfg(bootstrap)] -impl> !Unpin for GenFuture {} - -#[cfg(bootstrap)] -#[doc(hidden)] -#[unstable(feature = "gen_future", issue = "50547")] -impl> Future for GenFuture { - type Output = T::Return; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Safe because we're !Unpin + !Drop mapping to a ?Unpin value - let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; - let _guard = unsafe { set_task_context(cx) }; - match gen.resume(()) { - GeneratorState::Yielded(()) => Poll::Pending, - GeneratorState::Complete(x) => Poll::Ready(x), - } - } -} - -#[cfg(bootstrap)] -thread_local! { - static TLS_CX: Cell>>> = Cell::new(None); -} - -#[cfg(bootstrap)] -struct SetOnDrop(Option>>); - -#[cfg(bootstrap)] -impl Drop for SetOnDrop { - fn drop(&mut self) { - TLS_CX.with(|tls_cx| { - tls_cx.set(self.0.take()); - }); - } -} - -// Safety: the returned guard must drop before `cx` is dropped and before -// any previous guard is dropped. -#[cfg(bootstrap)] -unsafe fn set_task_context(cx: &mut Context<'_>) -> SetOnDrop { - // transmute the context's lifetime to 'static so we can store it. - let cx = core::mem::transmute::<&mut Context<'_>, &mut Context<'static>>(cx); - let old_cx = TLS_CX.with(|tls_cx| tls_cx.replace(Some(NonNull::from(cx)))); - SetOnDrop(old_cx) -} - -#[cfg(bootstrap)] -#[doc(hidden)] -#[unstable(feature = "gen_future", issue = "50547")] -/// Polls a future in the current thread-local task waker. -pub fn poll_with_tls_context(f: Pin<&mut F>) -> Poll -where - F: Future, -{ - let cx_ptr = TLS_CX.with(|tls_cx| { - // Clear the entry so that nested `get_task_waker` calls - // will fail or set their own value. - tls_cx.replace(None) - }); - let _reset = SetOnDrop(cx_ptr); - - let mut cx_ptr = cx_ptr.expect( - "TLS Context not set. This is a rustc bug. \ - Please file an issue on https://github.com/rust-lang/rust.", - ); - - // Safety: we've ensured exclusive access to the context by - // removing the pointer from TLS, only to be replaced once - // we're done with it. - // - // The pointer that was inserted came from an `&mut Context<'_>`, - // so it is safe to treat as mutable. - unsafe { F::poll(f, cx_ptr.as_mut()) } -} diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 8862226adbbd3..046b1a6888024 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -292,6 +292,10 @@ impl Read for BufReader { Ok(nread) } + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + // we can't skip unconditionally because of the large buffer case in read. unsafe fn initializer(&self) -> Initializer { self.inner.initializer() @@ -680,6 +684,10 @@ impl Write for BufWriter { } } + fn is_write_vectored(&self) -> bool { + self.get_ref().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { self.flush_buf().and_then(|()| self.get_mut().flush()) } @@ -1043,15 +1051,10 @@ impl Write for LineWriter { } // Find the last newline, and failing that write the whole buffer - let last_newline = bufs - .iter() - .enumerate() - .rev() - .filter_map(|(i, buf)| { - let pos = memchr::memrchr(b'\n', buf)?; - Some((i, pos)) - }) - .next(); + let last_newline = bufs.iter().enumerate().rev().find_map(|(i, buf)| { + let pos = memchr::memrchr(b'\n', buf)?; + Some((i, pos)) + }); let (i, j) = match last_newline { Some(pair) => pair, None => return self.inner.write_vectored(bufs), diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index f36aa1846a16c..f3e3fc81a5d82 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -266,6 +266,10 @@ where Ok(nread) } + fn is_read_vectored(&self) -> bool { + true + } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { let n = buf.len(); Read::read_exact(&mut self.fill_buf()?, buf)?; @@ -372,6 +376,11 @@ impl Write for Cursor<&mut [u8]> { slice_write_vectored(&mut self.pos, self.inner, bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -388,6 +397,11 @@ impl Write for Cursor<&mut Vec> { vec_write_vectored(&mut self.pos, self.inner, bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -404,6 +418,11 @@ impl Write for Cursor> { vec_write_vectored(&mut self.pos, &mut self.inner, bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -422,6 +441,11 @@ impl Write for Cursor> { slice_write_vectored(&mut self.pos, &mut self.inner, bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 3b55d9b900235..d80a38819ead0 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -487,9 +487,9 @@ impl Error { /// } /// /// fn main() { - /// // Will print "No inner error". + /// // Will print "Other". /// print_error(Error::last_os_error()); - /// // Will print "Inner error: ...". + /// // Will print "AddrInUse". /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); /// } /// ``` diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index b7f82e652990d..01dff0b3eb390 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -20,6 +20,11 @@ impl Read for &mut R { (**self).read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { (**self).initializer() @@ -52,6 +57,11 @@ impl Write for &mut W { (**self).write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + #[inline] fn flush(&mut self) -> io::Result<()> { (**self).flush() @@ -109,6 +119,11 @@ impl Read for Box { (**self).read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { (**self).initializer() @@ -141,6 +156,11 @@ impl Write for Box { (**self).write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + #[inline] fn flush(&mut self) -> io::Result<()> { (**self).flush() @@ -240,6 +260,11 @@ impl Read for &[u8] { Ok(nread) } + #[inline] + fn is_read_vectored(&self) -> bool { + true + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -316,6 +341,11 @@ impl Write for &mut [u8] { Ok(nwritten) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + #[inline] fn write_all(&mut self, data: &[u8]) -> io::Result<()> { if self.write(data)? == data.len() { @@ -351,6 +381,11 @@ impl Write for Vec { Ok(len) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.extend_from_slice(buf); diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 83c492fecf974..ebe6d09023f42 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -256,11 +256,13 @@ //! [`Read::read`]: trait.Read.html#tymethod.read //! [`Result`]: ../result/enum.Result.html //! [`.unwrap()`]: ../result/enum.Result.html#method.unwrap +// ignore-tidy-filelength #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp; use crate::fmt; +use crate::mem; use crate::memchr; use crate::ops::{Deref, DerefMut}; use crate::ptr; @@ -515,6 +517,11 @@ pub trait Read { /// reader will *always* no longer be able to produce bytes. /// 2. The buffer specified was 0 bytes in length. /// + /// It is not an error if the returned value `n` is smaller than the buffer size, + /// even when the reader is not at the end of the stream yet. + /// This may happen for example because fewer bytes are actually available right now + /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. + /// /// No guarantees are provided about the contents of `buf` when this /// function is called, implementations cannot rely on any property of the /// contents of `buf` being true. It is recommended that *implementations* @@ -569,8 +576,9 @@ pub trait Read { /// Like `read`, except that it reads into a slice of buffers. /// /// Data is copied to fill each buffer in order, with the final buffer - /// written to possibly being only partially filled. This method must behave - /// as a single call to `read` with the buffers concatenated would. + /// written to possibly being only partially filled. This method must + /// behave equivalently to a single call to `read` with concatenated + /// buffers. /// /// The default implementation calls `read` with either the first nonempty /// buffer provided, or an empty one if none exists. @@ -579,6 +587,19 @@ pub trait Read { default_read_vectored(|b| self.read(b), bufs) } + /// Determines if this `Read`er has an efficient `read_vectored` + /// implementation. + /// + /// If a `Read`er does not override the default `read_vectored` + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + #[unstable(feature = "can_vector", issue = "69941")] + fn is_read_vectored(&self) -> bool { + false + } + /// Determines if this `Read`er can work with buffers of uninitialized /// memory. /// @@ -951,6 +972,12 @@ pub trait Read { #[repr(transparent)] pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSliceMut<'a> {} + +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSliceMut<'a> {} + #[stable(feature = "iovec", since = "1.36.0")] impl<'a> fmt::Debug for IoSliceMut<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1054,6 +1081,12 @@ impl<'a> DerefMut for IoSliceMut<'a> { #[repr(transparent)] pub struct IoSlice<'a>(sys::io::IoSlice<'a>); +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSlice<'a> {} + +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSlice<'a> {} + #[stable(feature = "iovec", since = "1.36.0")] impl<'a> fmt::Debug for IoSlice<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1291,6 +1324,19 @@ pub trait Write { default_write_vectored(|b| self.write(b), bufs) } + /// Determines if this `Write`er has an efficient `write_vectored` + /// implementation. + /// + /// If a `Write`er does not override the default `write_vectored` + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + #[unstable(feature = "can_vector", issue = "69941")] + fn is_write_vectored(&self) -> bool { + false + } + /// Flush this output stream, ensuring that all intermediately buffered /// contents reach their destination. /// @@ -1364,6 +1410,70 @@ pub trait Write { Ok(()) } + /// Attempts to write multiple buffers into this writer. + /// + /// This method will continuously call [`write_vectored`] until there is no + /// more data to be written or an error of non-[`ErrorKind::Interrupted`] + /// kind is returned. This method will not return until all buffers have + /// been successfully written or such an error occurs. The first error that + /// is not of [`ErrorKind::Interrupted`] kind generated from this method + /// will be returned. + /// + /// If the buffer contains no data, this will never call [`write_vectored`]. + /// + /// [`write_vectored`]: #method.write_vectored + /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted + /// + /// # Notes + /// + /// + /// Unlike `io::Write::write_vectored`, this takes a *mutable* reference to + /// a slice of `IoSlice`s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this function returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to `write_vectored` were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// `IoSlice`s point (but not the `IoSlice`s themselves), are unchanged and + /// can be reused. + /// + /// # Examples + /// + /// ``` + /// #![feature(write_all_vectored)] + /// # fn main() -> std::io::Result<()> { + /// + /// use std::io::{Write, IoSlice}; + /// + /// let mut writer = Vec::new(); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs)?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); + /// # Ok(()) } + /// ``` + #[unstable(feature = "write_all_vectored", issue = "70436")] + fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { + while !bufs.is_empty() { + match self.write_vectored(bufs) { + Ok(0) => { + return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); + } + Ok(n) => bufs = IoSlice::advance(mem::take(&mut bufs), n), + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + /// Writes a formatted string into this writer, returning any error /// encountered. /// @@ -2411,7 +2521,7 @@ impl Iterator for Lines { #[cfg(test)] mod tests { use super::{repeat, Cursor, SeekFrom}; - use crate::cmp; + use crate::cmp::{self, min}; use crate::io::prelude::*; use crate::io::{self, IoSlice, IoSliceMut}; use crate::ops::Deref; @@ -2800,4 +2910,107 @@ mod tests { bufs = IoSlice::advance(bufs, 9); assert!(bufs.is_empty()); } + + /// Create a new writer that reads from at most `n_bufs` and reads + /// `per_call` bytes (in total) per call to write. + fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { + TestWriter { n_bufs, per_call, written: Vec::new() } + } + + struct TestWriter { + n_bufs: usize, + per_call: usize, + written: Vec, + } + + impl Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut left = self.per_call; + let mut written = 0; + for buf in bufs.iter().take(self.n_bufs) { + let n = min(left, buf.len()); + self.written.extend_from_slice(&buf[0..n]); + left -= n; + written += n; + } + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + #[test] + fn test_writer_read_from_one_buf() { + let mut writer = test_writer(1, 2); + + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.write_vectored(&[]).unwrap(), 0); + + // Read at most 2 bytes. + assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2); + let bufs = &[IoSlice::new(&[2, 2, 2])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 2); + + // Only read from first buf. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 1); + + assert_eq!(writer.written, &[1, 1, 2, 2, 3]); + } + + #[test] + fn test_writer_read_from_multiple_bufs() { + let mut writer = test_writer(3, 3); + + // Read at most 3 bytes from two buffers. + let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 3); + + // Read at most 3 bytes from three buffers. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 3); + + assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]); + } + + #[test] + fn test_write_all_vectored() { + #[rustfmt::skip] // Becomes unreadable otherwise. + let tests: Vec<(_, &'static [u8])> = vec![ + (vec![], &[]), + (vec![IoSlice::new(&[1])], &[1]), + (vec![IoSlice::new(&[1, 2])], &[1, 2]), + (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), + (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), + ]; + + let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]; + + for (n_bufs, per_call) in writer_configs.iter().copied() { + for (mut input, wanted) in tests.clone().into_iter() { + let mut writer = test_writer(n_bufs, per_call); + assert!(writer.write_all_vectored(&mut *input).is_ok()); + assert_eq!(&*writer.written, &*wanted); + } + } + } } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 9a82ae7626d97..b65b150d2c3a1 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -87,6 +87,11 @@ impl Read for StdinRaw { self.0.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -101,6 +106,11 @@ impl Write for StdoutRaw { self.0.write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { self.0.flush() } @@ -114,6 +124,11 @@ impl Write for StderrRaw { self.0.write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { self.0.flush() } @@ -140,6 +155,14 @@ impl io::Write for Maybe { } } + #[inline] + fn is_write_vectored(&self) -> bool { + match self { + Maybe::Real(w) => w.is_write_vectored(), + Maybe::Fake => true, + } + } + fn flush(&mut self) -> io::Result<()> { match *self { Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()), @@ -162,6 +185,14 @@ impl io::Read for Maybe { Maybe::Fake => Ok(0), } } + + #[inline] + fn is_read_vectored(&self) -> bool { + match self { + Maybe::Real(w) => w.is_read_vectored(), + Maybe::Fake => true, + } + } } fn handle_ebadf(r: io::Result, default: T) -> io::Result { @@ -352,6 +383,10 @@ impl Read for Stdin { self.lock().read_vectored(bufs) } #[inline] + fn is_read_vectored(&self) -> bool { + self.lock().is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() } @@ -376,6 +411,11 @@ impl Read for StdinLock<'_> { self.inner.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -496,7 +536,7 @@ pub fn stdout() -> Stdout { unsafe { let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))); ret.init(); - return ret; + ret } } } @@ -543,6 +583,10 @@ impl Write for Stdout { fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.lock().write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.lock().is_write_vectored() + } fn flush(&mut self) -> io::Result<()> { self.lock().flush() } @@ -561,6 +605,10 @@ impl Write for StdoutLock<'_> { fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.borrow_mut().write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.borrow_mut().is_write_vectored() + } fn flush(&mut self) -> io::Result<()> { self.inner.borrow_mut().flush() } @@ -664,7 +712,7 @@ pub fn stderr() -> Stderr { *INSTANCE.lock().borrow_mut() = Maybe::Real(stderr); } }); - return Stderr { inner: &INSTANCE }; + Stderr { inner: &INSTANCE } } impl Stderr { @@ -709,6 +757,10 @@ impl Write for Stderr { fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.lock().write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.lock().is_write_vectored() + } fn flush(&mut self) -> io::Result<()> { self.lock().flush() } @@ -727,6 +779,10 @@ impl Write for StderrLock<'_> { fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.borrow_mut().write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.borrow_mut().is_write_vectored() + } fn flush(&mut self) -> io::Result<()> { self.inner.borrow_mut().flush() } diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index b09161b97aa5e..b9d5dc27db006 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -179,6 +179,11 @@ impl Read for Repeat { Ok(nwritten) } + #[inline] + fn is_read_vectored(&self) -> bool { + true + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -235,6 +240,11 @@ impl Write for Sink { Ok(total_len) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index 314424631fcbf..6fbb0139b0eca 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -685,12 +685,12 @@ mod impl_keyword {} /// ## Literal Examples: /// /// * `for _ **in** 1..3 {}` - Iterate over an exclusive range up to but excluding 3. -/// * `for _ **in** 1..=3 {}` - Iterate over an inclusive range up to and includeing 3. +/// * `for _ **in** 1..=3 {}` - Iterate over an inclusive range up to and including 3. /// /// (Read more about [range patterns]) /// /// [`Iterator`]: ../book/ch13-04-performance.html -/// [`range patterns`]: ../reference/patterns.html?highlight=range#range-patterns +/// [range patterns]: ../reference/patterns.html?highlight=range#range-patterns /// [`for`]: keyword.for.html mod in_keyword {} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e5dad307a209a..ac07af5e278fb 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -91,7 +91,8 @@ //! pull-requests for your suggested changes. //! //! Contributions are appreciated! If you see a part of the docs that can be -//! improved, submit a PR, or chat with us first on irc.mozilla.org #rust-docs. +//! improved, submit a PR, or chat with us first on [Discord][rust-discord] +//! #docs. //! //! # A Tour of The Rust Standard Library //! @@ -194,6 +195,7 @@ //! [multithreading]: thread/index.html //! [other]: #what-is-in-the-standard-library-documentation //! [primitive types]: ../book/ch03-02-data-types.html +//! [rust-discord]: https://discord.gg/rust-lang #![stable(feature = "rust1", since = "1.0.0")] #![doc( @@ -240,7 +242,8 @@ #![feature(atomic_mut_ptr)] #![feature(box_syntax)] #![feature(c_variadic)] -#![cfg_attr(not(bootstrap), feature(cfg_accessible))] +#![feature(cfg_accessible)] +#![feature(can_vector)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] @@ -274,13 +277,16 @@ #![feature(libc)] #![feature(link_args)] #![feature(linkage)] +#![feature(llvm_asm)] #![feature(log_syntax)] #![feature(maybe_uninit_ref)] #![feature(maybe_uninit_slice)] #![feature(needs_panic_runtime)] +#![feature(negative_impls)] #![feature(never_type)] #![feature(nll)] #![feature(optin_builtin_traits)] +#![feature(or_patterns)] #![feature(panic_info_message)] #![feature(panic_internals)] #![feature(panic_unwind)] @@ -293,8 +299,7 @@ #![feature(shrink_to)] #![feature(slice_concat_ext)] #![feature(slice_internals)] -#![cfg_attr(bootstrap, feature(specialization))] -#![cfg_attr(not(bootstrap), feature(min_specialization))] +#![feature(min_specialization)] #![feature(staged_api)] #![feature(std_internals)] #![feature(stdsimd)] @@ -513,48 +518,17 @@ pub use std_detect::detect; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::{ - // Stable - assert_eq, - assert_ne, - debug_assert, - debug_assert_eq, - debug_assert_ne, - // Unstable - matches, - r#try, - todo, - unimplemented, - unreachable, - write, - writeln, + assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo, + unimplemented, unreachable, write, writeln, }; // Re-export built-in macros defined through libcore. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] pub use core::{ - // Unstable - asm, - // Stable - assert, - cfg, - column, - compile_error, - concat, - concat_idents, - env, - file, - format_args, - format_args_nl, - global_asm, - include, - include_bytes, - include_str, - line, - log_syntax, - module_path, - option_env, - stringify, - trace_macros, + asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, + format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, + module_path, option_env, stringify, trace_macros, }; #[stable(feature = "core_primitive", since = "1.43.0")] diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 5023d69240893..9ac54dd5f7a65 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -576,6 +576,11 @@ impl Read for TcpStream { self.0.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -591,6 +596,11 @@ impl Write for TcpStream { self.0.write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -605,6 +615,11 @@ impl Read for &TcpStream { self.0.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -620,6 +635,11 @@ impl Write for &TcpStream { self.0.write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/src/libstd/os/illumos/fs.rs b/src/libstd/os/illumos/fs.rs new file mode 100644 index 0000000000000..2abbf1fa9fa16 --- /dev/null +++ b/src/libstd/os/illumos/fs.rs @@ -0,0 +1,118 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use libc; + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::illumos::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/src/libstd/os/illumos/mod.rs b/src/libstd/os/illumos/mod.rs new file mode 100644 index 0000000000000..e61926f89356a --- /dev/null +++ b/src/libstd/os/illumos/mod.rs @@ -0,0 +1,6 @@ +//! illumos-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub mod raw; diff --git a/src/libstd/os/illumos/raw.rs b/src/libstd/os/illumos/raw.rs new file mode 100644 index 0000000000000..88c832ae7c773 --- /dev/null +++ b/src/libstd/os/illumos/raw.rs @@ -0,0 +1,74 @@ +//! illumos-specific raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated( + since = "1.8.0", + reason = "these type aliases are no longer supported by the standard library, the `libc` \ + crate on crates.io should be used instead for the correct definitions" +)] +#![allow(deprecated)] + +use crate::os::raw::c_long; +use crate::os::unix::raw::{gid_t, uid_t}; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type fflags_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = i64; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = u32; + +#[repr(C)] +#[derive(Clone)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: ino_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: mode_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: nlink_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: uid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: gid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: off_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: blksize_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: blkcnt_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused: [u8; 16], +} diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index 91e37ed833a4f..fd6ee088e961c 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -24,7 +24,7 @@ cfg_if::cfg_if! { // If we're not documenting libstd then we just expose the main modules // as we otherwise would. - #[cfg(any(target_os = "redox", unix, target_os = "vxworks"))] + #[cfg(any(target_os = "redox", unix, target_os = "vxworks", target_os = "hermit"))] #[stable(feature = "rust1", since = "1.0.0")] pub use crate::sys::ext as unix; @@ -52,6 +52,8 @@ pub mod freebsd; pub mod fuchsia; #[cfg(target_os = "haiku")] pub mod haiku; +#[cfg(target_os = "illumos")] +pub mod illumos; #[cfg(target_os = "ios")] pub mod ios; #[cfg(target_os = "macos")] diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 10078bd4aee82..bf381896a22bb 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -271,44 +271,12 @@ pub unsafe fn r#try R>(f: F) -> Result> let mut data = Data { f: ManuallyDrop::new(f) }; let data_ptr = &mut data as *mut _ as *mut u8; - return if do_try(do_call::, data_ptr, do_catch::) == 0 { + return if intrinsics::r#try(do_call::, data_ptr, do_catch::) == 0 { Ok(ManuallyDrop::into_inner(data.r)) } else { Err(ManuallyDrop::into_inner(data.p)) }; - // Compatibility wrapper around the try intrinsic for bootstrap. - // - // We also need to mark it #[inline(never)] to work around a bug on MinGW - // targets: the unwinding implementation was relying on UB, but this only - // becomes a problem in practice if inlining is involved. - #[cfg(not(bootstrap))] - use intrinsics::r#try as do_try; - #[cfg(bootstrap)] - #[inline(never)] - unsafe fn do_try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32 { - use crate::mem::MaybeUninit; - #[cfg(target_env = "msvc")] - type TryPayload = [u64; 2]; - #[cfg(not(target_env = "msvc"))] - type TryPayload = *mut u8; - - let mut payload: MaybeUninit = MaybeUninit::uninit(); - let payload_ptr = payload.as_mut_ptr() as *mut u8; - let r = intrinsics::r#try(try_fn, data, payload_ptr); - if r != 0 { - #[cfg(target_env = "msvc")] - { - catch_fn(data, payload_ptr) - } - #[cfg(not(target_env = "msvc"))] - { - catch_fn(data, payload.assume_init()) - } - } - r - } - // We consider unwinding to be rare, so mark this function as cold. However, // do not mark it no-inline -- that decision is best to leave to the // optimizer (in most cases this function is not inlined even as a normal, @@ -320,9 +288,7 @@ pub unsafe fn r#try R>(f: F) -> Result> obj } - // See comment on do_try above for why #[inline(never)] is needed on bootstrap. - #[cfg_attr(bootstrap, inline(never))] - #[cfg_attr(not(bootstrap), inline)] + #[inline] fn do_call R, R>(data: *mut u8) { unsafe { let data = data as *mut Data; @@ -366,7 +332,10 @@ pub fn panicking() -> bool { #[cfg_attr(feature = "panic_immediate_abort", inline)] pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { - unsafe { intrinsics::abort() } + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + intrinsics::abort() + } } let info = PanicInfo::internal_constructor(Some(msg), Location::caller()); @@ -432,7 +401,10 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { #[track_caller] pub fn begin_panic(msg: M) -> ! { if cfg!(feature = "panic_immediate_abort") { - unsafe { intrinsics::abort() } + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + intrinsics::abort() + } } rust_panic_with_hook(&mut PanicPayload::new(msg), None, Location::caller()); @@ -492,7 +464,10 @@ fn rust_panic_with_hook( "thread panicked while processing \ panic. aborting.\n" )); - unsafe { intrinsics::abort() } + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + intrinsics::abort() + } } unsafe { @@ -527,7 +502,10 @@ fn rust_panic_with_hook( "thread panicked while panicking. \ aborting.\n" )); - unsafe { intrinsics::abort() } + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + intrinsics::abort() + } } rust_panic(payload) diff --git a/src/libstd/path.rs b/src/libstd/path.rs index b8361d3e82599..0fe5451bb9564 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -157,10 +157,10 @@ pub enum Prefix<'a> { #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, ), - /// Verbatim disk prefix, e.g., `\\?\C:\`. + /// Verbatim disk prefix, e.g., `\\?\C:`. /// /// Verbatim disk prefixes consist of `\\?\` immediately followed by the - /// drive letter and `:\`. + /// drive letter and `:`. #[stable(feature = "rust1", since = "1.0.0")] VerbatimDisk(#[stable(feature = "rust1", since = "1.0.0")] u8), @@ -1116,7 +1116,6 @@ impl PathBuf { /// # Examples /// /// ``` - /// #![feature(path_buf_capacity)] /// use std::path::PathBuf; /// /// let mut path = PathBuf::with_capacity(10); @@ -1130,7 +1129,7 @@ impl PathBuf { /// /// [`with_capacity`]: ../ffi/struct.OsString.html#method.with_capacity /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "path_buf_capacity", issue = "58234")] + #[stable(feature = "path_buf_capacity", since = "1.44.0")] pub fn with_capacity(capacity: usize) -> PathBuf { PathBuf { inner: OsString::with_capacity(capacity) } } @@ -1374,7 +1373,7 @@ impl PathBuf { /// /// [`capacity`]: ../ffi/struct.OsString.html#method.capacity /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "path_buf_capacity", issue = "58234")] + #[stable(feature = "path_buf_capacity", since = "1.44.0")] pub fn capacity(&self) -> usize { self.inner.capacity() } @@ -1383,7 +1382,7 @@ impl PathBuf { /// /// [`clear`]: ../ffi/struct.OsString.html#method.clear /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "path_buf_capacity", issue = "58234")] + #[stable(feature = "path_buf_capacity", since = "1.44.0")] pub fn clear(&mut self) { self.inner.clear() } @@ -1392,7 +1391,7 @@ impl PathBuf { /// /// [`reserve`]: ../ffi/struct.OsString.html#method.reserve /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "path_buf_capacity", issue = "58234")] + #[stable(feature = "path_buf_capacity", since = "1.44.0")] pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional) } @@ -1401,7 +1400,7 @@ impl PathBuf { /// /// [`reserve_exact`]: ../ffi/struct.OsString.html#method.reserve_exact /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "path_buf_capacity", issue = "58234")] + #[stable(feature = "path_buf_capacity", since = "1.44.0")] pub fn reserve_exact(&mut self, additional: usize) { self.inner.reserve_exact(additional) } @@ -1410,7 +1409,7 @@ impl PathBuf { /// /// [`shrink_to_fit`]: ../ffi/struct.OsString.html#method.shrink_to_fit /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "path_buf_capacity", issue = "58234")] + #[stable(feature = "path_buf_capacity", since = "1.44.0")] pub fn shrink_to_fit(&mut self) { self.inner.shrink_to_fit() } @@ -1419,7 +1418,7 @@ impl PathBuf { /// /// [`shrink_to`]: ../ffi/struct.OsString.html#method.shrink_to /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "path_buf_capacity", issue = "58234")] + #[unstable(feature = "shrink_to", issue = "56431")] pub fn shrink_to(&mut self, min_capacity: usize) { self.inner.shrink_to(min_capacity) } @@ -1434,6 +1433,17 @@ impl From<&Path> for Box { } } +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + #[inline] + fn from(cow: Cow<'_, Path>) -> Box { + match cow { + Cow::Borrowed(path) => Box::from(path), + Cow::Owned(path) => Box::from(path), + } + } +} + #[stable(feature = "path_buf_from_box", since = "1.18.0")] impl From> for PathBuf { /// Converts a `Box` into a `PathBuf` diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index 6712f5ba5808c..0fbd6b62f18ff 100644 --- a/src/libstd/prelude/v1.rs +++ b/src/libstd/prelude/v1.rs @@ -36,11 +36,12 @@ pub use crate::result::Result::{self, Err, Ok}; // Re-exported built-in macros #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] #[doc(no_inline)] pub use core::prelude::v1::{ asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, global_asm, include, include_bytes, include_str, line, log_syntax, module_path, - option_env, stringify, trace_macros, + format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, + module_path, option_env, stringify, trace_macros, }; // FIXME: Attribute and derive macros are not documented because for them rustdoc generates @@ -53,7 +54,6 @@ pub use core::prelude::v1::{ PartialEq, PartialOrd, RustcDecodable, RustcEncodable, }; -#[cfg(not(bootstrap))] #[unstable( feature = "cfg_accessible", issue = "64797", diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index adad90f56d1cf..e0ceb9f3f3810 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -320,7 +320,7 @@ mod prim_char {} #[doc(primitive = "unit")] // -/// The `()` type, sometimes called "unit" or "nil". +/// The `()` type, also called "unit". /// /// The `()` type has exactly one value `()`, and is used when there /// is no other meaningful value that could be returned. `()` is most diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 3eee45d000cd1..f7e7a5abf8ea6 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -245,6 +245,10 @@ impl Write for ChildStdin { self.inner.write_vectored(bufs) } + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -300,6 +304,11 @@ impl Read for ChildStdout { self.inner.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -356,6 +365,11 @@ impl Read for ChildStderr { self.inner.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -1606,7 +1620,7 @@ pub fn exit(code: i32) -> ! { /// [panic hook]: ../../std/panic/fn.set_hook.html #[stable(feature = "process_abort", since = "1.17.0")] pub fn abort() -> ! { - unsafe { crate::sys::abort_internal() }; + crate::sys::abort_internal(); } /// Returns the OS-assigned process identifier associated with this process. diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs index 5b41525e06aaa..75f5621fa127e 100644 --- a/src/libstd/sync/mpsc/oneshot.rs +++ b/src/libstd/sync/mpsc/oneshot.rs @@ -260,7 +260,7 @@ impl Packet { let state = match self.state.load(Ordering::SeqCst) { // Each of these states means that no further activity will happen // with regard to abortion selection - s @ EMPTY | s @ DATA | s @ DISCONNECTED => s, + s @ (EMPTY | DATA | DISCONNECTED) => s, // If we've got a blocked thread, then use an atomic to gain ownership // of it (may fail) diff --git a/src/libstd/sync/mpsc/shared.rs b/src/libstd/sync/mpsc/shared.rs index 2b0393573fdc4..d1a46c5175743 100644 --- a/src/libstd/sync/mpsc/shared.rs +++ b/src/libstd/sync/mpsc/shared.rs @@ -12,7 +12,6 @@ use self::StartResult::*; use core::cmp; use core::intrinsics::abort; -use core::isize; use crate::cell::UnsafeCell; use crate::ptr; @@ -92,7 +91,7 @@ impl Packet { // // This can only be called at channel-creation time pub fn inherit_blocker(&self, token: Option, guard: MutexGuard<'_, ()>) { - token.map(|token| { + if let Some(token) = token { assert_eq!(self.cnt.load(Ordering::SeqCst), 0); assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); self.to_wake.store(unsafe { token.cast_to_usize() }, Ordering::SeqCst); @@ -119,7 +118,7 @@ impl Packet { unsafe { *self.steals.get() = -1; } - }); + } // When the shared packet is constructed, we grabbed this lock. The // purpose of this lock is to ensure that abort_selection() doesn't @@ -355,6 +354,8 @@ impl Packet { // See comments on Arc::clone() on why we do this (for `mem::forget`). if old_count > MAX_REFCOUNT { + // remove `unsafe` on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] unsafe { abort(); } diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs index 2e3270e81fcd0..9f7c1af895199 100644 --- a/src/libstd/sync/mpsc/stream.rs +++ b/src/libstd/sync/mpsc/stream.rs @@ -11,7 +11,6 @@ use self::Message::*; pub use self::UpgradeResult::*; use core::cmp; -use core::isize; use crate::cell::UnsafeCell; use crate::ptr; @@ -206,7 +205,7 @@ impl Packet { // Messages which actually popped from the queue shouldn't count as // a steal, so offset the decrement here (we already have our // "steal" factored into the channel count above). - data @ Ok(..) | data @ Err(Upgraded(..)) => unsafe { + data @ (Ok(..) | Err(Upgraded(..))) => unsafe { *self.queue.consumer_addition().steals.get() -= 1; data }, @@ -330,7 +329,7 @@ impl Packet { ); cnt != DISCONNECTED && cnt != steals } { - while let Some(_) = self.queue.pop() { + while self.queue.pop().is_some() { steals += 1; } } diff --git a/src/libstd/sync/mpsc/sync.rs b/src/libstd/sync/mpsc/sync.rs index 79e868171546b..603764922c5aa 100644 --- a/src/libstd/sync/mpsc/sync.rs +++ b/src/libstd/sync/mpsc/sync.rs @@ -26,7 +26,6 @@ use self::Blocker::*; pub use self::Failure::*; use core::intrinsics::abort; -use core::isize; use core::mem; use core::ptr; @@ -344,8 +343,12 @@ impl Packet { mem::drop(guard); // only outside of the lock do we wake up the pending threads - pending_sender1.map(|t| t.signal()); - pending_sender2.map(|t| t.signal()); + if let Some(token) = pending_sender1 { + token.signal(); + } + if let Some(token) = pending_sender2 { + token.signal(); + } } // Prepares this shared packet for a channel clone, essentially just bumping @@ -355,6 +358,8 @@ impl Packet { // See comments on Arc::clone() on why we do this (for `mem::forget`). if old_count > MAX_REFCOUNT { + // remove `unsafe` on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] unsafe { abort(); } @@ -411,7 +416,9 @@ impl Packet { while let Some(token) = queue.dequeue() { token.signal(); } - waiter.map(|t| t.signal()); + if let Some(token) = waiter { + token.signal(); + } } } diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 0cb16b19d7326..797b22fdd1279 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -108,6 +108,7 @@ use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; /// *guard += 1; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")] pub struct Mutex { // Note that this mutex is in a *box*, not inlined into the struct itself. // Once a native mutex has been used once, its address can never change (it diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 1e6b6c430be90..a3ee14e85d222 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -497,7 +497,7 @@ impl Drop for WaiterQueue<'_> { let mut queue = (state_and_queue & !STATE_MASK) as *const Waiter; while !queue.is_null() { let next = (*queue).next; - let thread = (*queue).thread.replace(None).unwrap(); + let thread = (*queue).thread.take().unwrap(); (*queue).signaled.store(true, Ordering::Release); // ^- FIXME (maybe): This is another case of issue #55005 // `store()` has a potentially dangling ref to `signaled`. diff --git a/src/libstd/sys/cloudabi/mod.rs b/src/libstd/sys/cloudabi/mod.rs index e5f1dd9843587..dde2b21c9bcff 100644 --- a/src/libstd/sys/cloudabi/mod.rs +++ b/src/libstd/sys/cloudabi/mod.rs @@ -51,8 +51,11 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } -pub unsafe fn abort_internal() -> ! { - core::intrinsics::abort(); +pub fn abort_internal() -> ! { + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + core::intrinsics::abort(); + } } pub use libc::strlen; diff --git a/src/libstd/sys/cloudabi/shims/fs.rs b/src/libstd/sys/cloudabi/shims/fs.rs index e6160d1457d26..ecb5b51cccdcd 100644 --- a/src/libstd/sys/cloudabi/shims/fs.rs +++ b/src/libstd/sys/cloudabi/shims/fs.rs @@ -202,6 +202,10 @@ impl File { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -210,6 +214,10 @@ impl File { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn flush(&self) -> io::Result<()> { match self.0 {} } diff --git a/src/libstd/sys/cloudabi/shims/net.rs b/src/libstd/sys/cloudabi/shims/net.rs index 67c436fa7955d..375aaab405dff 100644 --- a/src/libstd/sys/cloudabi/shims/net.rs +++ b/src/libstd/sys/cloudabi/shims/net.rs @@ -47,6 +47,10 @@ impl TcpStream { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _: &[u8]) -> io::Result { match self.0 {} } @@ -55,6 +59,10 @@ impl TcpStream { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn peer_addr(&self) -> io::Result { match self.0 {} } diff --git a/src/libstd/sys/cloudabi/shims/pipe.rs b/src/libstd/sys/cloudabi/shims/pipe.rs index fb14dc5910181..10d0925823eb9 100644 --- a/src/libstd/sys/cloudabi/shims/pipe.rs +++ b/src/libstd/sys/cloudabi/shims/pipe.rs @@ -12,6 +12,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -20,6 +24,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } diff --git a/src/libstd/sys/cloudabi/stack_overflow.rs b/src/libstd/sys/cloudabi/stack_overflow.rs index e97831b2c2855..9339b14373105 100644 --- a/src/libstd/sys/cloudabi/stack_overflow.rs +++ b/src/libstd/sys/cloudabi/stack_overflow.rs @@ -1,13 +1,5 @@ #![cfg_attr(test, allow(dead_code))] -pub struct Handler; - -impl Handler { - pub unsafe fn new() -> Handler { - Handler - } -} - pub unsafe fn init() {} pub unsafe fn cleanup() {} diff --git a/src/libstd/sys/cloudabi/thread.rs b/src/libstd/sys/cloudabi/thread.rs index 3afcae7ae7516..a15dc8653e83a 100644 --- a/src/libstd/sys/cloudabi/thread.rs +++ b/src/libstd/sys/cloudabi/thread.rs @@ -5,7 +5,6 @@ use crate::mem; use crate::ptr; use crate::sys::cloudabi::abi; use crate::sys::time::checked_dur2intervals; -use crate::sys_common::thread::*; use crate::time::Duration; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -22,7 +21,7 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = box p; + let p = Box::into_raw(box p); let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(libc::pthread_attr_init(&mut attr), 0); @@ -30,19 +29,25 @@ impl Thread { let stack_size = cmp::max(stack, min_stack_size(&attr)); assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); - let ret = libc::pthread_create(&mut native, &attr, thread_start, &*p as *const _ as *mut _); + let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); Err(io::Error::from_raw_os_error(ret)) } else { - mem::forget(p); // ownership passed to pthread_create Ok(Thread { id: native }) }; extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { unsafe { - start_thread(main as *mut u8); + // Let's run some code. + Box::from_raw(main as *mut Box)(); } ptr::null_mut() } diff --git a/src/libstd/sys/hermit/condvar.rs b/src/libstd/sys/hermit/condvar.rs index 5b7f16ce562b9..94e3275448ae9 100644 --- a/src/libstd/sys/hermit/condvar.rs +++ b/src/libstd/sys/hermit/condvar.rs @@ -12,9 +12,8 @@ impl Condvar { Condvar { identifier: 0 } } - #[inline] pub unsafe fn init(&mut self) { - // nothing to do + let _ = abi::init_queue(self.id()); } pub unsafe fn notify_one(&self) { @@ -50,7 +49,6 @@ impl Condvar { ret } - #[inline] pub unsafe fn destroy(&self) { let _ = abi::destroy_queue(self.id()); } diff --git a/src/libstd/sys/hermit/ext/ffi.rs b/src/libstd/sys/hermit/ext/ffi.rs new file mode 100644 index 0000000000000..07b59a02556d9 --- /dev/null +++ b/src/libstd/sys/hermit/ext/ffi.rs @@ -0,0 +1,38 @@ +//! HermitCore-specific extension to the primitives in the `std::ffi` module +//! +//! # Examples +//! +//! ``` +//! use std::ffi::OsString; +//! use std::os::hermit::ffi::OsStringExt; +//! +//! let bytes = b"foo".to_vec(); +//! +//! // OsStringExt::from_vec +//! let os_string = OsString::from_vec(bytes); +//! assert_eq!(os_string.to_str(), Some("foo")); +//! +//! // OsStringExt::into_vec +//! let bytes = os_string.into_vec(); +//! assert_eq!(bytes, b"foo"); +//! ``` +//! +//! ``` +//! use std::ffi::OsStr; +//! use std::os::hermit::ffi::OsStrExt; +//! +//! let bytes = b"foo"; +//! +//! // OsStrExt::from_bytes +//! let os_str = OsStr::from_bytes(bytes); +//! assert_eq!(os_str.to_str(), Some("foo")); +//! +//! // OsStrExt::as_bytes +//! let bytes = os_str.as_bytes(); +//! assert_eq!(bytes, b"foo"); +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys_common::os_str_bytes::*; diff --git a/src/libstd/sys/hermit/ext/mod.rs b/src/libstd/sys/hermit/ext/mod.rs new file mode 100644 index 0000000000000..ea87d0ad2c94d --- /dev/null +++ b/src/libstd/sys/hermit/ext/mod.rs @@ -0,0 +1,14 @@ +#![stable(feature = "rust1", since = "1.0.0")] +#![allow(missing_docs)] + +pub mod ffi; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; +} diff --git a/src/libstd/sys/hermit/fast_thread_local.rs b/src/libstd/sys/hermit/fast_thread_local.rs index 1108e2545bdeb..9b683fce15748 100644 --- a/src/libstd/sys/hermit/fast_thread_local.rs +++ b/src/libstd/sys/hermit/fast_thread_local.rs @@ -1,4 +1,36 @@ #![cfg(target_thread_local)] #![unstable(feature = "thread_local_internals", issue = "none")] -pub use crate::sys_common::thread_local::register_dtor_fallback as register_dtor; +// Simplify dtor registration by using a list of destructors. +// The this solution works like the implementation of macOS and +// doesn't additional OS support + +use crate::cell::Cell; +use crate::ptr; + +#[thread_local] +static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); + +type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; + +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + if DTORS.get().is_null() { + let v: Box = box Vec::new(); + DTORS.set(Box::into_raw(v)); + } + + let list: &mut List = &mut *DTORS.get(); + list.push((t, dtor)); +} + +// every thread call this function to run through all possible destructors +pub unsafe fn run_dtors() { + let mut ptr = DTORS.replace(ptr::null_mut()); + while !ptr.is_null() { + let list = Box::from_raw(ptr); + for (ptr, dtor) in list.into_iter() { + dtor(ptr); + } + ptr = DTORS.replace(ptr::null_mut()); + } +} diff --git a/src/libstd/sys/hermit/fs.rs b/src/libstd/sys/hermit/fs.rs index 37ac5984eeeaf..82ccab1462ba8 100644 --- a/src/libstd/sys/hermit/fs.rs +++ b/src/libstd/sys/hermit/fs.rs @@ -6,6 +6,7 @@ use crate::io::{IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; use crate::sys::cvt; use crate::sys::hermit::abi; +use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; use crate::sys::hermit::fd::FileDesc; use crate::sys::time::SystemTime; use crate::sys::{unsupported, Void}; @@ -17,14 +18,6 @@ pub use crate::sys_common::fs::copy; fn cstr(path: &Path) -> io::Result { Ok(CString::new(path.as_os_str().as_bytes())?) } -//const O_ACCMODE: i32 = 00000003; -const O_RDONLY: i32 = 00000000; -const O_WRONLY: i32 = 00000001; -const O_RDWR: i32 = 00000002; -const O_CREAT: i32 = 00000100; -const O_EXCL: i32 = 00000200; -const O_TRUNC: i32 = 00001000; -const O_APPEND: i32 = 00002000; #[derive(Debug)] pub struct File(FileDesc); @@ -308,6 +301,11 @@ impl File { crate::io::default_read_vectored(|buf| self.read(buf), bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } @@ -316,6 +314,11 @@ impl File { crate::io::default_write_vectored(|buf| self.write(buf), bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/src/libstd/sys/hermit/mod.rs b/src/libstd/sys/hermit/mod.rs index 1e4a53abdc7bd..7bdc1be3b1702 100644 --- a/src/libstd/sys/hermit/mod.rs +++ b/src/libstd/sys/hermit/mod.rs @@ -21,6 +21,7 @@ pub mod args; pub mod cmath; pub mod condvar; pub mod env; +pub mod ext; pub mod fast_thread_local; pub mod fd; pub mod fs; @@ -73,8 +74,10 @@ pub extern "C" fn floor(x: f64) -> f64 { unsafe { intrinsics::floorf64(x) } } -pub unsafe fn abort_internal() -> ! { - abi::abort(); +pub fn abort_internal() -> ! { + unsafe { + abi::abort(); + } } // FIXME: just a workaround to test the system @@ -87,15 +90,13 @@ pub fn hashmap_random_keys() -> (u64, u64) { #[cfg(not(test))] #[no_mangle] // NB. used by both libunwind and libpanic_abort -pub unsafe extern "C" fn __rust_abort() { +pub extern "C" fn __rust_abort() { abort_internal(); } #[cfg(not(test))] pub fn init() { - unsafe { - let _ = net::init(); - } + let _ = net::init(); } #[cfg(not(test))] @@ -105,6 +106,7 @@ pub unsafe extern "C" fn runtime_entry( argv: *const *const c_char, env: *const *const c_char, ) -> ! { + use crate::sys::hermit::fast_thread_local::run_dtors; extern "C" { fn main(argc: isize, argv: *const *const c_char) -> i32; } @@ -114,6 +116,7 @@ pub unsafe extern "C" fn runtime_entry( let result = main(argc as isize, argv); + run_dtors(); abi::exit(result); } diff --git a/src/libstd/sys/hermit/net.rs b/src/libstd/sys/hermit/net.rs index 82917e71be1f8..5b5379c8b0581 100644 --- a/src/libstd/sys/hermit/net.rs +++ b/src/libstd/sys/hermit/net.rs @@ -1,291 +1,372 @@ use crate::convert::TryFrom; use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::str; +use crate::sys::hermit::abi; use crate::sys::{unsupported, Void}; use crate::time::Duration; -//// Iinitializes HermitCore's network stack -pub unsafe fn init() -> io::Result<()> { +/// Checks whether the HermitCore's socket interface has been started already, and +/// if not, starts it. +pub fn init() -> io::Result<()> { + if abi::network_init() < 0 { + return Err(io::Error::new(ErrorKind::Other, "Unable to initialize network interface")); + } + Ok(()) } -pub struct TcpStream(Void); +pub struct TcpStream(abi::Handle); impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) { + Ok(handle) => Ok(TcpStream(handle)), + _ => { + Err(io::Error::new(ErrorKind::Other, "Unable to initiate a connection on a socket")) + } + } } - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() + pub fn connect_timeout(saddr: &SocketAddr, duration: Duration) -> io::Result { + match abi::tcpstream::connect( + saddr.ip().to_string().as_bytes(), + saddr.port(), + Some(duration.as_millis() as u64), + ) { + Ok(handle) => Ok(TcpStream(handle)), + _ => { + Err(io::Error::new(ErrorKind::Other, "Unable to initiate a connection on a socket")) + } + } } - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + pub fn set_read_timeout(&self, duration: Option) -> io::Result<()> { + abi::tcpstream::set_read_timeout(self.0, duration.map(|d| d.as_millis() as u64)) + .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to set timeout value")) } - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + pub fn set_write_timeout(&self, duration: Option) -> io::Result<()> { + abi::tcpstream::set_write_timeout(self.0, duration.map(|d| d.as_millis() as u64)) + .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to set timeout value")) } pub fn read_timeout(&self) -> io::Result> { - match self.0 {} + let duration = abi::tcpstream::get_read_timeout(self.0) + .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to determine timeout value"))?; + + Ok(duration.map(|d| Duration::from_millis(d))) } pub fn write_timeout(&self) -> io::Result> { - match self.0 {} + let duration = abi::tcpstream::get_write_timeout(self.0) + .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to determine timeout value"))?; + + Ok(duration.map(|d| Duration::from_millis(d))) } - pub fn peek(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + abi::tcpstream::peek(self.0, buf) + .map_err(|_| io::Error::new(ErrorKind::Other, "set_nodelay failed")) } - pub fn read(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + pub fn read(&self, buffer: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(buffer)]) } - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - match self.0 {} + pub fn read_vectored(&self, ioslice: &mut [IoSliceMut<'_>]) -> io::Result { + let mut size: usize = 0; + + for i in ioslice.iter_mut() { + let mut pos: usize = 0; + + while pos < i.len() { + let ret = abi::tcpstream::read(self.0, &mut i[pos..]) + .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to read on socket"))?; + + if ret == 0 { + return Ok(size); + } else { + size += ret; + pos += ret; + } + } + } + + Ok(size) } - pub fn write(&self, _: &[u8]) -> io::Result { - match self.0 {} + #[inline] + pub fn is_read_vectored(&self) -> bool { + true } - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - match self.0 {} + pub fn write(&self, buffer: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buffer)]) + } + + pub fn write_vectored(&self, ioslice: &[IoSlice<'_>]) -> io::Result { + let mut size: usize = 0; + + for i in ioslice.iter() { + size += abi::tcpstream::write(self.0, i) + .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to write on socket"))?; + } + + Ok(size) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true } pub fn peer_addr(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "peer_addr isn't supported")) } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "socket_addr isn't supported")) } - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - match self.0 {} + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + abi::tcpstream::shutdown(self.0, how as i32) + .map_err(|_| io::Error::new(ErrorKind::Other, "unable to shutdown socket")) } pub fn duplicate(&self) -> io::Result { - match self.0 {} + let handle = abi::tcpstream::duplicate(self.0) + .map_err(|_| io::Error::new(ErrorKind::Other, "unable to duplicate stream"))?; + + Ok(TcpStream(handle)) } - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - match self.0 {} + pub fn set_nodelay(&self, mode: bool) -> io::Result<()> { + abi::tcpstream::set_nodelay(self.0, mode) + .map_err(|_| io::Error::new(ErrorKind::Other, "set_nodelay failed")) } pub fn nodelay(&self) -> io::Result { - match self.0 {} + abi::tcpstream::nodelay(self.0) + .map_err(|_| io::Error::new(ErrorKind::Other, "nodelay failed")) } - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + pub fn set_ttl(&self, tll: u32) -> io::Result<()> { + abi::tcpstream::set_tll(self.0, tll) + .map_err(|_| io::Error::new(ErrorKind::Other, "unable to set TTL")) } pub fn ttl(&self) -> io::Result { - match self.0 {} + abi::tcpstream::get_tll(self.0) + .map_err(|_| io::Error::new(ErrorKind::Other, "unable to get TTL")) } pub fn take_error(&self) -> io::Result> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "take_error isn't supported")) } - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> { + abi::tcpstream::set_nonblocking(self.0, mode) + .map_err(|_| io::Error::new(ErrorKind::Other, "unable to set blocking mode")) + } +} + +impl Drop for TcpStream { + fn drop(&mut self) { + let _ = abi::tcpstream::close(self.0); } } impl fmt::Debug for TcpStream { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + Ok(()) } } -pub struct TcpListener(Void); +pub struct TcpListener(abi::Handle); impl TcpListener { pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn duplicate(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn ttl(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn only_v6(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn take_error(&self) -> io::Result> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } } impl fmt::Debug for TcpListener { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + Ok(()) } } -pub struct UdpSocket(Void); +pub struct UdpSocket(abi::Handle); impl UdpSocket { pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn peer_addr(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn duplicate(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn read_timeout(&self) -> io::Result> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn write_timeout(&self) -> io::Result> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn broadcast(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn multicast_loop_v4(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn multicast_ttl_v4(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn multicast_loop_v6(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn ttl(&self) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn take_error(&self) -> io::Result> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn recv(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn peek(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn send(&self, _: &[u8]) -> io::Result { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - match self.0 {} + Err(io::Error::new(ErrorKind::Other, "not supported")) } } impl fmt::Debug for UdpSocket { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + Ok(()) } } diff --git a/src/libstd/sys/hermit/pipe.rs b/src/libstd/sys/hermit/pipe.rs index fb14dc5910181..10d0925823eb9 100644 --- a/src/libstd/sys/hermit/pipe.rs +++ b/src/libstd/sys/hermit/pipe.rs @@ -12,6 +12,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -20,6 +24,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } diff --git a/src/libstd/sys/hermit/stack_overflow.rs b/src/libstd/sys/hermit/stack_overflow.rs index 65a1b17acce9a..121fe42011da5 100644 --- a/src/libstd/sys/hermit/stack_overflow.rs +++ b/src/libstd/sys/hermit/stack_overflow.rs @@ -1,11 +1,3 @@ -pub struct Handler; - -impl Handler { - pub unsafe fn new() -> Handler { - Handler - } -} - #[inline] pub unsafe fn init() {} diff --git a/src/libstd/sys/hermit/stdio.rs b/src/libstd/sys/hermit/stdio.rs index 2eb011ccb3974..208265de465ad 100644 --- a/src/libstd/sys/hermit/stdio.rs +++ b/src/libstd/sys/hermit/stdio.rs @@ -20,6 +20,11 @@ impl Stdin { // .read(data) Ok(0) } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } } impl Stdout { @@ -51,6 +56,11 @@ impl Stdout { } } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } @@ -85,6 +95,11 @@ impl Stderr { } } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/src/libstd/sys/hermit/thread.rs b/src/libstd/sys/hermit/thread.rs index c3c29c93826de..e11afed668728 100644 --- a/src/libstd/sys/hermit/thread.rs +++ b/src/libstd/sys/hermit/thread.rs @@ -1,39 +1,14 @@ #![allow(dead_code)] use crate::ffi::CStr; -use crate::fmt; use crate::io; use crate::mem; use crate::sys::hermit::abi; +use crate::sys::hermit::fast_thread_local::run_dtors; use crate::time::Duration; -use core::u32; - -use crate::sys_common::thread::*; pub type Tid = abi::Tid; -/// Priority of a task -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] -pub struct Priority(u8); - -impl Priority { - pub const fn into(self) -> u8 { - self.0 - } - - pub const fn from(x: u8) -> Self { - Priority(x) - } -} - -impl fmt::Display for Priority { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -pub const NORMAL_PRIO: Priority = Priority::from(2); - pub struct Thread { tid: Tid, } @@ -41,34 +16,39 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -pub const DEFAULT_MIN_STACK_SIZE: usize = 262144; +pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20; impl Thread { pub unsafe fn new_with_coreid( - _stack: usize, + stack: usize, p: Box, core_id: isize, ) -> io::Result { - let p = box p; - let mut tid: Tid = u32::MAX; - let ret = abi::spawn( - &mut tid as *mut Tid, + let p = Box::into_raw(box p); + let tid = abi::spawn2( thread_start, - &*p as *const _ as *const u8 as usize, - Priority::into(NORMAL_PRIO), + p as usize, + abi::Priority::into(abi::NORMAL_PRIO), + stack, core_id, ); - return if ret == 0 { - mem::forget(p); // ownership passed to pthread_create - Ok(Thread { tid: tid }) - } else { + return if tid == 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); Err(io::Error::new(io::ErrorKind::Other, "Unable to create thread!")) + } else { + Ok(Thread { tid: tid }) }; extern "C" fn thread_start(main: usize) { unsafe { - start_thread(main as *mut u8); + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + + // run all destructors + run_dtors(); } } } diff --git a/src/libstd/sys/hermit/thread_local.rs b/src/libstd/sys/hermit/thread_local.rs index c6f8adb21623a..f8be9863ed56f 100644 --- a/src/libstd/sys/hermit/thread_local.rs +++ b/src/libstd/sys/hermit/thread_local.rs @@ -1,60 +1,26 @@ -#![allow(dead_code)] // not used on all platforms - -use crate::collections::BTreeMap; -use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys_common::mutex::Mutex; - pub type Key = usize; -type Dtor = unsafe extern "C" fn(*mut u8); - -static NEXT_KEY: AtomicUsize = AtomicUsize::new(0); - -static mut KEYS: *mut BTreeMap> = ptr::null_mut(); -static KEYS_LOCK: Mutex = Mutex::new(); - -#[thread_local] -static mut LOCALS: *mut BTreeMap = ptr::null_mut(); - -unsafe fn keys() -> &'static mut BTreeMap> { - if KEYS.is_null() { - KEYS = Box::into_raw(Box::new(BTreeMap::new())); - } - &mut *KEYS -} - -unsafe fn locals() -> &'static mut BTreeMap { - if LOCALS.is_null() { - LOCALS = Box::into_raw(Box::new(BTreeMap::new())); - } - &mut *LOCALS -} - #[inline] -pub unsafe fn create(dtor: Option) -> Key { - let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); - let _guard = KEYS_LOCK.lock(); - keys().insert(key, dtor); - key +pub unsafe fn create(_dtor: Option) -> Key { + panic!("should not be used on the wasm target"); } #[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - if let Some(&entry) = locals().get(&key) { entry } else { ptr::null_mut() } +pub unsafe fn set(_key: Key, _value: *mut u8) { + panic!("should not be used on the wasm target"); } #[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - locals().insert(key, value); +pub unsafe fn get(_key: Key) -> *mut u8 { + panic!("should not be used on the wasm target"); } #[inline] -pub unsafe fn destroy(key: Key) { - keys().remove(&key); +pub unsafe fn destroy(_key: Key) { + panic!("should not be used on the wasm target"); } #[inline] pub fn requires_synchronized_create() -> bool { - false + panic!("should not be used on the wasm target"); } diff --git a/src/libstd/sys/sgx/abi/mem.rs b/src/libstd/sys/sgx/abi/mem.rs index 500e62b1cb59d..57fd7efdd49e6 100644 --- a/src/libstd/sys/sgx/abi/mem.rs +++ b/src/libstd/sys/sgx/abi/mem.rs @@ -22,7 +22,7 @@ extern "C" { #[unstable(feature = "sgx_platform", issue = "56975")] pub fn image_base() -> u64 { let base; - unsafe { asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) }; + unsafe { llvm_asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) }; base } diff --git a/src/libstd/sys/sgx/ext/arch.rs b/src/libstd/sys/sgx/ext/arch.rs index 5056e388112ce..0c97a87e2e445 100644 --- a/src/libstd/sys/sgx/ext/arch.rs +++ b/src/libstd/sys/sgx/ext/arch.rs @@ -31,7 +31,7 @@ pub fn egetkey(request: &Align512<[u8; 512]>) -> Result, u32> let mut out = MaybeUninit::uninit(); let error; - asm!( + llvm_asm!( "enclu" : "={eax}"(error) : "{eax}"(ENCLU_EGETKEY), @@ -60,7 +60,7 @@ pub fn ereport( unsafe { let mut report = MaybeUninit::uninit(); - asm!( + llvm_asm!( "enclu" : /* no output registers */ : "{eax}"(ENCLU_EREPORT), diff --git a/src/libstd/sys/sgx/fd.rs b/src/libstd/sys/sgx/fd.rs index 7da2424a64261..90158030c7fbe 100644 --- a/src/libstd/sys/sgx/fd.rs +++ b/src/libstd/sys/sgx/fd.rs @@ -34,6 +34,11 @@ impl FileDesc { 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)]) } @@ -42,6 +47,11 @@ impl FileDesc { usercalls::write(self.fd, bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn flush(&self) -> io::Result<()> { usercalls::flush(self.fd) } diff --git a/src/libstd/sys/sgx/fs.rs b/src/libstd/sys/sgx/fs.rs index e6160d1457d26..ecb5b51cccdcd 100644 --- a/src/libstd/sys/sgx/fs.rs +++ b/src/libstd/sys/sgx/fs.rs @@ -202,6 +202,10 @@ impl File { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -210,6 +214,10 @@ impl File { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn flush(&self) -> io::Result<()> { match self.0 {} } diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index 83cee0cf35a70..397dd496ae8af 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -124,7 +124,7 @@ pub unsafe fn strlen(mut s: *const c_char) -> usize { return n; } -pub unsafe fn abort_internal() -> ! { +pub fn abort_internal() -> ! { abi::usercalls::exit(true) } @@ -133,7 +133,7 @@ pub unsafe fn abort_internal() -> ! { #[cfg(not(test))] #[no_mangle] // NB. used by both libunwind and libpanic_abort -pub unsafe extern "C" fn __rust_abort() { +pub extern "C" fn __rust_abort() { abort_internal(); } diff --git a/src/libstd/sys/sgx/net.rs b/src/libstd/sys/sgx/net.rs index bd0652ab4649a..666a157b09cd0 100644 --- a/src/libstd/sys/sgx/net.rs +++ b/src/libstd/sys/sgx/net.rs @@ -149,6 +149,11 @@ impl TcpStream { self.inner.inner.read_vectored(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.inner.is_read_vectored() + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.inner.inner.write(buf) } @@ -157,6 +162,11 @@ impl TcpStream { self.inner.inner.write_vectored(bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.inner.is_write_vectored() + } + pub fn peer_addr(&self) -> io::Result { addr_to_sockaddr(&self.peer_addr) } diff --git a/src/libstd/sys/sgx/pipe.rs b/src/libstd/sys/sgx/pipe.rs index fb14dc5910181..10d0925823eb9 100644 --- a/src/libstd/sys/sgx/pipe.rs +++ b/src/libstd/sys/sgx/pipe.rs @@ -12,6 +12,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -20,6 +24,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } diff --git a/src/libstd/sys/sgx/stack_overflow.rs b/src/libstd/sys/sgx/stack_overflow.rs index a2d13d11849e7..b96652a8330e9 100644 --- a/src/libstd/sys/sgx/stack_overflow.rs +++ b/src/libstd/sys/sgx/stack_overflow.rs @@ -1,11 +1,3 @@ -pub struct Handler; - -impl Handler { - pub unsafe fn new() -> Handler { - Handler - } -} - #[cfg_attr(test, allow(dead_code))] pub unsafe fn init() {} diff --git a/src/libstd/sys/unix/alloc.rs b/src/libstd/sys/unix/alloc.rs index 77417e4133127..8e193935460eb 100644 --- a/src/libstd/sys/unix/alloc.rs +++ b/src/libstd/sys/unix/alloc.rs @@ -52,7 +52,12 @@ unsafe impl GlobalAlloc for System { } } -#[cfg(any(target_os = "android", target_os = "redox", target_os = "solaris"))] +#[cfg(any( + target_os = "android", + target_os = "illumos", + target_os = "redox", + target_os = "solaris" +))] #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { // On android we currently target API level 9 which unfortunately @@ -75,7 +80,12 @@ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { libc::memalign(layout.align(), layout.size()) as *mut u8 } -#[cfg(not(any(target_os = "android", target_os = "redox", target_os = "solaris")))] +#[cfg(not(any( + target_os = "android", + target_os = "illumos", + target_os = "redox", + target_os = "solaris" +)))] #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); diff --git a/src/libstd/sys/unix/args.rs b/src/libstd/sys/unix/args.rs index 09acc3f6e3ea8..4c3e8542d576d 100644 --- a/src/libstd/sys/unix/args.rs +++ b/src/libstd/sys/unix/args.rs @@ -65,6 +65,7 @@ impl DoubleEndedIterator for Args { target_os = "netbsd", target_os = "openbsd", target_os = "solaris", + target_os = "illumos", target_os = "emscripten", target_os = "haiku", target_os = "l4re", diff --git a/src/libstd/sys/unix/env.rs b/src/libstd/sys/unix/env.rs index 984bcfa45099b..7f5e9b04dba4b 100644 --- a/src/libstd/sys/unix/env.rs +++ b/src/libstd/sys/unix/env.rs @@ -97,6 +97,17 @@ pub mod os { 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"; diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 4c3cb67c9ee0f..32c2ac43129bf 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -7,6 +7,7 @@ use libc; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? #[cfg(not(unix))] +#[allow(non_camel_case_types)] mod libc { pub use libc::c_int; pub type socklen_t = u32; @@ -613,6 +614,11 @@ impl io::Read for UnixStream { io::Read::read_vectored(&mut &*self, bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + io::Read::is_read_vectored(&&*self) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -629,6 +635,11 @@ impl<'a> io::Read for &'a UnixStream { self.0.read_vectored(bufs) } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -645,6 +656,11 @@ impl io::Write for UnixStream { io::Write::write_vectored(&mut &*self, bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + io::Write::is_write_vectored(&&*self) + } + fn flush(&mut self) -> io::Result<()> { io::Write::flush(&mut &*self) } @@ -660,6 +676,11 @@ impl<'a> io::Write for &'a UnixStream { self.0.write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/src/libstd/sys/unix/ext/raw.rs b/src/libstd/sys/unix/ext/raw.rs index d81368a18b452..40fa53d484f84 100644 --- a/src/libstd/sys/unix/ext/raw.rs +++ b/src/libstd/sys/unix/ext/raw.rs @@ -11,10 +11,15 @@ #![allow(deprecated)] #[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] pub type uid_t = u32; + #[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] pub type gid_t = u32; + #[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] pub type pid_t = i32; #[doc(inline)] diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 8a99836912a33..1ef7ffacfcf14 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -64,6 +64,11 @@ impl FileDesc { 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) @@ -116,6 +121,11 @@ impl FileDesc { Ok(ret as usize) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { #[cfg(target_os = "android")] use super::android::cvt_pwrite64; @@ -153,6 +163,7 @@ impl FileDesc { #[cfg(not(any( target_env = "newlib", target_os = "solaris", + target_os = "illumos", target_os = "emscripten", target_os = "fuchsia", target_os = "l4re", @@ -169,6 +180,7 @@ impl FileDesc { #[cfg(any( target_env = "newlib", target_os = "solaris", + target_os = "illumos", target_os = "emscripten", target_os = "fuchsia", target_os = "l4re", diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index ab2a871b92df4..80cf6a5dbc27b 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -22,6 +22,7 @@ use libc::fstatat64; target_os = "linux", target_os = "emscripten", target_os = "solaris", + target_os = "illumos", target_os = "l4re", target_os = "fuchsia", target_os = "redox" @@ -200,7 +201,12 @@ pub struct DirEntry { // on Solaris and Fuchsia because a) it uses a zero-length // array to store the name, b) its lifetime between readdir // calls is not guaranteed. - #[cfg(any(target_os = "solaris", target_os = "fuchsia", target_os = "redox"))] + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox" + ))] name: Box<[u8]>, } @@ -403,7 +409,12 @@ impl fmt::Debug for ReadDir { impl Iterator for ReadDir { type Item = io::Result; - #[cfg(any(target_os = "solaris", target_os = "fuchsia", target_os = "redox"))] + #[cfg(any( + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos" + ))] fn next(&mut self) -> Option> { use crate::slice; @@ -441,7 +452,12 @@ impl Iterator for ReadDir { } } - #[cfg(not(any(target_os = "solaris", target_os = "fuchsia", target_os = "redox")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos" + )))] fn next(&mut self) -> Option> { if self.end_of_stream { return None; @@ -514,12 +530,12 @@ impl DirEntry { lstat(&self.path()) } - #[cfg(any(target_os = "solaris", target_os = "haiku"))] + #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))] pub fn file_type(&self) -> io::Result { lstat(&self.path()).map(|m| m.file_type()) } - #[cfg(not(any(target_os = "solaris", target_os = "haiku")))] + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "haiku")))] pub fn file_type(&self) -> io::Result { match self.entry.d_type { libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), @@ -540,6 +556,7 @@ impl DirEntry { target_os = "emscripten", target_os = "android", target_os = "solaris", + target_os = "illumos", target_os = "haiku", target_os = "l4re", target_os = "fuchsia", @@ -586,7 +603,12 @@ impl DirEntry { fn name_bytes(&self) -> &[u8] { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() } } - #[cfg(any(target_os = "solaris", target_os = "fuchsia", target_os = "redox"))] + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox" + ))] fn name_bytes(&self) -> &[u8] { &*self.name } @@ -681,6 +703,10 @@ impl File { | opts.get_access_mode()? | opts.get_creation_mode()? | (opts.custom_flags as c_int & !libc::O_ACCMODE); + // The third argument of `open64` is documented to have type `mode_t`. On + // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`. + // However, since this is a variadic function, C integer promotion rules mean that on + // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; let fd = FileDesc::new(fd); @@ -806,6 +832,11 @@ impl File { 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) } @@ -818,6 +849,11 @@ impl File { 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) } diff --git a/src/libstd/sys/unix/l4re.rs b/src/libstd/sys/unix/l4re.rs index c6e4f5693ed5a..a2912387108e1 100644 --- a/src/libstd/sys/unix/l4re.rs +++ b/src/libstd/sys/unix/l4re.rs @@ -55,6 +55,10 @@ pub mod net { unimpl!(); } + pub fn is_read_vectored(&self) -> bool { + unimpl!(); + } + pub fn peek(&self, _: &mut [u8]) -> io::Result { unimpl!(); } @@ -75,6 +79,10 @@ pub mod net { unimpl!(); } + pub fn is_write_vectored(&self) -> bool { + unimpl!(); + } + pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { unimpl!(); } @@ -171,6 +179,10 @@ pub mod net { unimpl!(); } + pub fn is_read_vectored(&self) -> bool { + unimpl!(); + } + pub fn write(&self, _: &[u8]) -> io::Result { unimpl!(); } @@ -179,6 +191,10 @@ pub mod net { unimpl!(); } + pub fn is_write_vectored(&self) -> bool { + unimpl!(); + } + pub fn peer_addr(&self) -> io::Result { unimpl!(); } diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index fbcb006ecdf11..b1688e74173d7 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -17,6 +17,8 @@ pub use crate::os::freebsd as platform; pub use crate::os::fuchsia as platform; #[cfg(all(not(doc), target_os = "haiku"))] pub use crate::os::haiku as platform; +#[cfg(all(not(doc), target_os = "illumos"))] +pub use crate::os::illumos as platform; #[cfg(all(not(doc), target_os = "ios"))] pub use crate::os::ios as platform; #[cfg(all(not(doc), target_os = "l4re"))] @@ -161,6 +163,6 @@ where // understandable error message like "Abort trap" rather than "Illegal // instruction" that intrinsics::abort would cause, as intrinsics::abort is // implemented as an illegal instruction. -pub unsafe fn abort_internal() -> ! { - libc::abort() +pub fn abort_internal() -> ! { + unsafe { libc::abort() } } diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs index 103d87e3d2f91..45c600f75f5cf 100644 --- a/src/libstd/sys/unix/mutex.rs +++ b/src/libstd/sys/unix/mutex.rs @@ -28,14 +28,20 @@ impl Mutex { // // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you - // try to re-lock it from the same thread when you already hold a lock. + // try to re-lock it from the same thread when you already hold a lock + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL + // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that + // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same + // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in + // a Mutex where re-locking is UB. // // In practice, glibc takes advantage of this undefined behavior to // implement hardware lock elision, which uses hardware transactional // memory to avoid acquiring the lock. While a transaction is in // progress, the lock appears to be unlocked. This isn't a problem for // other threads since the transactional memory will abort if a conflict - // is detected, however no abort is generated if re-locking from the + // is detected, however no abort is generated when re-locking from the // same thread. // // Since locking the same mutex twice will result in two aliasing &mut diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index b37675e0a0a00..f062bc012f7ef 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -226,6 +226,11 @@ impl Socket { self.0.read_vectored(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn recv_from_with_flags( &self, buf: &mut [u8], @@ -263,6 +268,11 @@ impl Socket { self.0.write_vectored(bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { let timeout = match dur { Some(dur) => { @@ -322,11 +332,19 @@ impl Socket { Ok(raw != 0) } + #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) } + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + // FIONBIO is inadequate for sockets on illumos/Solaris, so use the + // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. + self.0.set_nonblocking(nonblocking) + } + pub fn take_error(&self) -> io::Result> { let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 91f7d1524ccef..a9cd5094997bd 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -54,7 +54,7 @@ extern "C" { ), link_name = "__errno" )] - #[cfg_attr(target_os = "solaris", link_name = "___errno")] + #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] #[cfg_attr( any(target_os = "macos", target_os = "ios", target_os = "freebsd"), link_name = "__error" @@ -357,7 +357,7 @@ pub fn current_exe() -> io::Result { } } -#[cfg(any(target_os = "solaris"))] +#[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn current_exe() -> io::Result { extern "C" { fn getexecname() -> *const c_char; diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 2a861c878015e..f2a2eabef9132 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -64,6 +64,11 @@ impl AnonPipe { self.0.read_vectored(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } @@ -72,6 +77,11 @@ impl AnonPipe { self.0.write_vectored(bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + pub fn fd(&self) -> &FileDesc { &self.0 } diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index 07d0fbf61fe22..f389c60615f24 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -72,7 +72,7 @@ impl Command { } }; - let mut p = Process { pid: pid, status: None }; + let mut p = Process { pid, status: None }; drop(output); let mut bytes = [0; 8]; diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs index 079dea671ef76..2b5067a34f648 100644 --- a/src/libstd/sys/unix/rwlock.rs +++ b/src/libstd/sys/unix/rwlock.rs @@ -22,32 +22,33 @@ impl RWLock { pub unsafe fn read(&self) { let r = libc::pthread_rwlock_rdlock(self.inner.get()); - // According to the pthread_rwlock_rdlock spec, this function **may** - // fail with EDEADLK if a deadlock is detected. On the other hand - // pthread mutexes will *never* return EDEADLK if they are initialized - // as the "fast" kind (which ours always are). As a result, a deadlock - // situation may actually return from the call to pthread_rwlock_rdlock - // instead of blocking forever (as mutexes and Windows rwlocks do). Note - // that not all unix implementations, however, will return EDEADLK for - // their rwlocks. + // According to POSIX, when a thread tries to acquire this read lock + // while it already holds the write lock + // (or vice versa, or tries to acquire the write lock twice), + // "the call shall either deadlock or return [EDEADLK]" + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html, + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html). + // So, in principle, all we have to do here is check `r == 0` to be sure we properly + // got the lock. // - // We roughly maintain the deadlocking behavior by panicking to ensure - // that this lock acquisition does not succeed. - // - // We also check whether this lock is already write locked. This - // is only possible if it was write locked by the current thread and - // the implementation allows recursive locking. The POSIX standard - // doesn't require recursively locking a rwlock to deadlock, but we can't - // allow that because it could lead to aliasing issues. + // However, (at least) glibc before version 2.25 does not conform to this spec, + // and can return `r == 0` even when this thread already holds the write lock. + // We thus check for this situation ourselves and panic when detecting that a thread + // got the write lock more than once, or got a read and a write lock. if r == libc::EAGAIN { panic!("rwlock maximum reader count exceeded"); } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. if r == 0 { + // `pthread_rwlock_rdlock` succeeded when it should not have. self.raw_unlock(); } panic!("rwlock read lock would result in deadlock"); } else { - assert_eq!(r, 0); + // According to POSIX, for a properly initialized rwlock this can only + // return EAGAIN or EDEADLK or 0. We rely on that. + debug_assert_eq!(r, 0); self.num_readers.fetch_add(1, Ordering::Relaxed); } } @@ -56,6 +57,7 @@ impl RWLock { let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); if r == 0 { if *self.write_locked.get() { + // `pthread_rwlock_tryrdlock` succeeded when it should not have. self.raw_unlock(); false } else { @@ -69,17 +71,22 @@ impl RWLock { #[inline] pub unsafe fn write(&self) { let r = libc::pthread_rwlock_wrlock(self.inner.get()); - // See comments above for why we check for EDEADLK and write_locked. We - // also need to check that num_readers is 0. + // See comments above for why we check for EDEADLK and write_locked. For the same reason, + // we also need to check that there are no readers (tracked in `num_readers`). if r == libc::EDEADLK - || *self.write_locked.get() + || (r == 0 && *self.write_locked.get()) || self.num_readers.load(Ordering::Relaxed) != 0 { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. if r == 0 { + // `pthread_rwlock_wrlock` succeeded when it should not have. self.raw_unlock(); } panic!("rwlock write lock would result in deadlock"); } else { + // According to POSIX, for a properly initialized rwlock this can only + // return EDEADLK or 0. We rely on that. debug_assert_eq!(r, 0); } *self.write_locked.get() = true; @@ -89,6 +96,7 @@ impl RWLock { let r = libc::pthread_rwlock_trywrlock(self.inner.get()); if r == 0 { if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { + // `pthread_rwlock_trywrlock` succeeded when it should not have. self.raw_unlock(); false } else { diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 2626ca37cf8b8..5e10357835056 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -33,6 +33,7 @@ impl Drop for Handler { target_os = "dragonfly", target_os = "freebsd", target_os = "solaris", + target_os = "illumos", all(target_os = "netbsd", not(target_vendor = "rumprun")), target_os = "openbsd" ))] @@ -162,7 +163,8 @@ mod imp { target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "solaris" + target_os = "solaris", + target_os = "illumos" ))] unsafe fn get_stack() -> libc::stack_t { libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ } @@ -214,6 +216,7 @@ mod imp { target_os = "dragonfly", target_os = "freebsd", target_os = "solaris", + target_os = "illumos", all(target_os = "netbsd", not(target_vendor = "rumprun")), target_os = "openbsd" )))] diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index b9c56963885c0..f8353214cbca0 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -20,6 +20,11 @@ impl io::Read for Stdin { fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs) } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } } impl Stdout { @@ -37,6 +42,11 @@ impl io::Write for Stdout { ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -57,6 +67,11 @@ impl io::Write for Stderr { ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs) } + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 674d4c7113801..895ea48e2b43e 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -3,11 +3,9 @@ use crate::ffi::CStr; use crate::io; use crate::mem; use crate::ptr; -use crate::sys::os; +use crate::sys::{os, stack_overflow}; use crate::time::Duration; -use crate::sys_common::thread::*; - #[cfg(not(target_os = "l4re"))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; #[cfg(target_os = "l4re")] @@ -43,7 +41,7 @@ unsafe fn pthread_attr_setstacksize( impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = box p; + let p = Box::into_raw(box p); let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(libc::pthread_attr_init(&mut attr), 0); @@ -65,19 +63,28 @@ impl Thread { } }; - let ret = libc::pthread_create(&mut native, &attr, thread_start, &*p as *const _ as *mut _); + let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); Err(io::Error::from_raw_os_error(ret)) } else { - mem::forget(p); // ownership passed to pthread_create Ok(Thread { id: native }) }; extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { unsafe { - start_thread(main as *mut u8); + // Next, set up our stack overflow handler which may get triggered if we run + // out of stack. + let _handler = stack_overflow::Handler::new(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); } ptr::null_mut() } @@ -125,7 +132,7 @@ impl Thread { } } - #[cfg(target_os = "solaris")] + #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn set_name(name: &CStr) { weak! { fn pthread_setname_np( @@ -148,7 +155,7 @@ impl Thread { target_os = "redox" ))] pub fn set_name(_name: &CStr) { - // Newlib, Illumos, Haiku, and Emscripten have no way to set a thread name. + // Newlib, Haiku, and Emscripten have no way to set a thread name. } #[cfg(target_os = "fuchsia")] pub fn set_name(_name: &CStr) { diff --git a/src/libstd/sys/vxworks/fd.rs b/src/libstd/sys/vxworks/fd.rs index 65c67dabc1ad0..23e9dc428ce23 100644 --- a/src/libstd/sys/vxworks/fd.rs +++ b/src/libstd/sys/vxworks/fd.rs @@ -54,6 +54,11 @@ impl FileDesc { Ok(ret as usize) } + #[inline] + fn is_read_vectored(&self) -> bool { + true + } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { let mut me = self; (&mut me).read_to_end(buf) @@ -99,6 +104,11 @@ impl FileDesc { Ok(ret as usize) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { unsafe fn cvt_pwrite( fd: c_int, diff --git a/src/libstd/sys/vxworks/fs.rs b/src/libstd/sys/vxworks/fs.rs index 68f2c13317024..557e65ca01b1c 100644 --- a/src/libstd/sys/vxworks/fs.rs +++ b/src/libstd/sys/vxworks/fs.rs @@ -351,6 +351,11 @@ impl File { 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) } @@ -363,6 +368,11 @@ impl File { 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) } diff --git a/src/libstd/sys/vxworks/mod.rs b/src/libstd/sys/vxworks/mod.rs index e23191c94311f..0787e7098988c 100644 --- a/src/libstd/sys/vxworks/mod.rs +++ b/src/libstd/sys/vxworks/mod.rs @@ -108,6 +108,6 @@ where // understandable error message like "Abort trap" rather than "Illegal // instruction" that intrinsics::abort would cause, as intrinsics::abort is // implemented as an illegal instruction. -pub unsafe fn abort_internal() -> ! { - libc::abort() +pub fn abort_internal() -> ! { + unsafe { libc::abort() } } diff --git a/src/libstd/sys/vxworks/net.rs b/src/libstd/sys/vxworks/net.rs index 7d4e5624f7e39..de0b15b43a2e2 100644 --- a/src/libstd/sys/vxworks/net.rs +++ b/src/libstd/sys/vxworks/net.rs @@ -163,6 +163,11 @@ impl Socket { self.0.read_vectored(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn recv_from_with_flags( &self, buf: &mut [u8], @@ -200,6 +205,11 @@ impl Socket { self.0.write_vectored(bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { let timeout = match dur { Some(dur) => { diff --git a/src/libstd/sys/vxworks/pipe.rs b/src/libstd/sys/vxworks/pipe.rs index 0990cb8e83cf8..a18376212af51 100644 --- a/src/libstd/sys/vxworks/pipe.rs +++ b/src/libstd/sys/vxworks/pipe.rs @@ -24,10 +24,16 @@ impl AnonPipe { pub fn read(&self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.0.read_vectored(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } @@ -36,6 +42,11 @@ impl AnonPipe { self.0.write_vectored(bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + pub fn fd(&self) -> &FileDesc { &self.0 } diff --git a/src/libstd/sys/vxworks/thread.rs b/src/libstd/sys/vxworks/thread.rs index e0d104b5f3ec9..4d0196e4b4de5 100644 --- a/src/libstd/sys/vxworks/thread.rs +++ b/src/libstd/sys/vxworks/thread.rs @@ -3,11 +3,9 @@ use crate::ffi::CStr; use crate::io; use crate::mem; use crate::ptr; -use crate::sys::os; +use crate::sys::{os, stack_overflow}; use crate::time::Duration; -use crate::sys_common::thread::*; - pub const DEFAULT_MIN_STACK_SIZE: usize = 0x40000; // 256K pub struct Thread { @@ -31,7 +29,7 @@ unsafe fn pthread_attr_setstacksize( impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = box p; + let p = Box::into_raw(box p); let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(libc::pthread_attr_init(&mut attr), 0); @@ -53,19 +51,28 @@ impl Thread { } }; - let ret = libc::pthread_create(&mut native, &attr, thread_start, &*p as *const _ as *mut _); + let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); Err(io::Error::from_raw_os_error(ret)) } else { - mem::forget(p); // ownership passed to pthread_create Ok(Thread { id: native }) }; extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { unsafe { - start_thread(main as *mut u8); + // Next, set up our stack overflow handler which may get triggered if we run + // out of stack. + let _handler = stack_overflow::Handler::new(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); } ptr::null_mut() } diff --git a/src/libstd/sys/wasi/alloc.rs b/src/libstd/sys/wasi/alloc.rs index e9760d050e105..bc61416278401 100644 --- a/src/libstd/sys/wasi/alloc.rs +++ b/src/libstd/sys/wasi/alloc.rs @@ -10,7 +10,7 @@ unsafe impl GlobalAlloc for System { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { libc::malloc(layout.size()) as *mut u8 } else { - libc::aligned_alloc(layout.size(), layout.align()) as *mut u8 + libc::aligned_alloc(layout.align(), layout.size()) as *mut u8 } } diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs index a11f61fdd69fd..793daea43c215 100644 --- a/src/libstd/sys/wasi/fs.rs +++ b/src/libstd/sys/wasi/fs.rs @@ -399,6 +399,11 @@ impl File { self.fd.read(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.write_vectored(&[IoSlice::new(buf)]) } @@ -407,6 +412,11 @@ impl File { self.fd.write(bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/src/libstd/sys/wasi/mod.rs b/src/libstd/sys/wasi/mod.rs index 241d499ca3b2d..29fafaaa0b94f 100644 --- a/src/libstd/sys/wasi/mod.rs +++ b/src/libstd/sys/wasi/mod.rs @@ -100,8 +100,8 @@ pub unsafe fn strlen(mut s: *const c_char) -> usize { return n; } -pub unsafe fn abort_internal() -> ! { - libc::abort() +pub fn abort_internal() -> ! { + unsafe { libc::abort() } } pub fn hashmap_random_keys() -> (u64, u64) { diff --git a/src/libstd/sys/wasi/net.rs b/src/libstd/sys/wasi/net.rs index 8a69028ff1dcf..e186453588de5 100644 --- a/src/libstd/sys/wasi/net.rs +++ b/src/libstd/sys/wasi/net.rs @@ -48,6 +48,10 @@ impl TcpStream { unsupported() } + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn write(&self, _: &[u8]) -> io::Result { unsupported() } @@ -56,6 +60,10 @@ impl TcpStream { unsupported() } + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn peer_addr(&self) -> io::Result { unsupported() } diff --git a/src/libstd/sys/wasi/pipe.rs b/src/libstd/sys/wasi/pipe.rs index fb14dc5910181..10d0925823eb9 100644 --- a/src/libstd/sys/wasi/pipe.rs +++ b/src/libstd/sys/wasi/pipe.rs @@ -12,6 +12,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -20,6 +24,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } diff --git a/src/libstd/sys/wasi/stdio.rs b/src/libstd/sys/wasi/stdio.rs index 1d53884f2d6b4..9f9e35566ecf5 100644 --- a/src/libstd/sys/wasi/stdio.rs +++ b/src/libstd/sys/wasi/stdio.rs @@ -19,6 +19,11 @@ impl Stdin { ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn as_raw_fd(&self) -> u32 { 0 } @@ -37,6 +42,11 @@ impl Stdout { ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } @@ -59,6 +69,11 @@ impl Stderr { ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/src/libstd/sys/wasm/fs.rs b/src/libstd/sys/wasm/fs.rs index e6160d1457d26..ecb5b51cccdcd 100644 --- a/src/libstd/sys/wasm/fs.rs +++ b/src/libstd/sys/wasm/fs.rs @@ -202,6 +202,10 @@ impl File { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -210,6 +214,10 @@ impl File { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn flush(&self) -> io::Result<()> { match self.0 {} } diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs index c115f75645074..050e8099af4ba 100644 --- a/src/libstd/sys/wasm/mod.rs +++ b/src/libstd/sys/wasm/mod.rs @@ -81,8 +81,8 @@ pub unsafe fn strlen(mut s: *const c_char) -> usize { return n; } -pub unsafe fn abort_internal() -> ! { - crate::arch::wasm32::unreachable() +pub fn abort_internal() -> ! { + unsafe { crate::arch::wasm32::unreachable() } } // We don't have randomness yet, but I totally used a random number generator to diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/wasm/net.rs index b7c3108f172f6..5c9f1098f9b7f 100644 --- a/src/libstd/sys/wasm/net.rs +++ b/src/libstd/sys/wasm/net.rs @@ -44,6 +44,10 @@ impl TcpStream { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _: &[u8]) -> io::Result { match self.0 {} } @@ -52,6 +56,10 @@ impl TcpStream { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn peer_addr(&self) -> io::Result { match self.0 {} } diff --git a/src/libstd/sys/wasm/pipe.rs b/src/libstd/sys/wasm/pipe.rs index fb14dc5910181..10d0925823eb9 100644 --- a/src/libstd/sys/wasm/pipe.rs +++ b/src/libstd/sys/wasm/pipe.rs @@ -12,6 +12,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result { match self.0 {} } @@ -20,6 +24,10 @@ impl AnonPipe { match self.0 {} } + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } diff --git a/src/libstd/sys/wasm/stack_overflow.rs b/src/libstd/sys/wasm/stack_overflow.rs index cbf62b6e5b7e3..32555394cd5a5 100644 --- a/src/libstd/sys/wasm/stack_overflow.rs +++ b/src/libstd/sys/wasm/stack_overflow.rs @@ -1,11 +1,3 @@ -pub struct Handler; - -impl Handler { - pub unsafe fn new() -> Handler { - Handler - } -} - pub unsafe fn init() {} pub unsafe fn cleanup() {} diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 7f93ef8795308..6115d652b0cea 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -216,7 +216,6 @@ pub const SOCK_STREAM: c_int = 1; pub const SOL_SOCKET: c_int = 0xffff; pub const SO_RCVTIMEO: c_int = 0x1006; pub const SO_SNDTIMEO: c_int = 0x1005; -pub const SO_REUSEADDR: c_int = 0x0004; pub const IPPROTO_IP: c_int = 0; pub const IPPROTO_TCP: c_int = 6; pub const IPPROTO_IPV6: c_int = 41; @@ -778,7 +777,7 @@ extern "system" { pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut c_ulong) -> c_int; pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN; + pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOL; pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION); diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index d508a333484ae..f85120d170f73 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -224,7 +224,7 @@ pub trait OpenOptionsExt { /// opening a named pipe, to control to which degree a server process can /// act on behalf of a client process (security impersonation level). /// - /// When `security_qos_flags` is not set a malicious program can gain the + /// When `security_qos_flags` is not set, a malicious program can gain the /// elevated privileges of a privileged Rust process when it allows opening /// user-specified paths, by tricking it into opening a named pipe. So /// arguably `security_qos_flags` should also be set when opening arbitrary diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 427f4b684e14a..cdbfac267b9a1 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -409,6 +409,11 @@ impl File { 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) } @@ -421,6 +426,11 @@ impl File { 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) } diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index f2ad057b6b624..2131cfc2c94bc 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -92,6 +92,11 @@ impl RawHandle { crate::io::default_read_vectored(|buf| self.read(buf), bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { let mut read = 0; let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; @@ -115,8 +120,7 @@ impl RawHandle { ) -> io::Result> { let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; let mut amt = 0; - let res = - cvt({ c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped) }); + let res = cvt(c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped)); match res { Ok(_) => Ok(Some(amt as usize)), Err(e) => { @@ -139,7 +143,7 @@ impl RawHandle { unsafe { let mut bytes = 0; let wait = if wait { c::TRUE } else { c::FALSE }; - let res = cvt({ c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait) }); + let res = cvt(c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait)); match res { Ok(_) => Ok(bytes as usize), Err(e) => { @@ -172,6 +176,11 @@ impl RawHandle { crate::io::default_write_vectored(|buf| self.write(buf), bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { let mut written = 0; let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index b004cd19020f8..4098c6b3ee937 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -81,10 +81,54 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } +pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option { + let ptr = haystack.as_ptr(); + let mut len = haystack.len(); + let mut start = &haystack[..]; + + // For performance reasons unfold the loop eight times. + while len >= 8 { + if start[0] == needle { + return Some((start.as_ptr() as usize - ptr as usize) / 2); + } + if start[1] == needle { + return Some((start[1..].as_ptr() as usize - ptr as usize) / 2); + } + if start[2] == needle { + return Some((start[2..].as_ptr() as usize - ptr as usize) / 2); + } + if start[3] == needle { + return Some((start[3..].as_ptr() as usize - ptr as usize) / 2); + } + if start[4] == needle { + return Some((start[4..].as_ptr() as usize - ptr as usize) / 2); + } + if start[5] == needle { + return Some((start[5..].as_ptr() as usize - ptr as usize) / 2); + } + if start[6] == needle { + return Some((start[6..].as_ptr() as usize - ptr as usize) / 2); + } + if start[7] == needle { + return Some((start[7..].as_ptr() as usize - ptr as usize) / 2); + } + + start = &start[8..]; + len -= 8; + } + + for (i, c) in start.iter().enumerate() { + if *c == needle { + return Some((start.as_ptr() as usize - ptr as usize) / 2 + i); + } + } + None +} + pub fn to_u16s>(s: S) -> crate::io::Result> { fn inner(s: &OsStr) -> crate::io::Result> { let mut maybe_result: Vec = s.encode_wide().collect(); - if maybe_result.iter().any(|&u| u == 0) { + if unrolled_find_u16s(0, &maybe_result).is_some() { return Err(crate::io::Error::new( ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", @@ -214,7 +258,7 @@ fn wide_char_to_multi_byte( } pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] { - match v.iter().position(|c| *c == 0) { + match unrolled_find_u16s(0, v) { // don't include the 0 Some(i) => &v[..i], None => v, @@ -264,11 +308,14 @@ pub fn dur2timeout(dur: Duration) -> c::DWORD { // // https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail #[allow(unreachable_code)] -pub unsafe fn abort_internal() -> ! { +pub fn abort_internal() -> ! { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT + unsafe { + llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT crate::intrinsics::unreachable(); } - crate::intrinsics::abort(); + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + crate::intrinsics::abort(); + } } diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index d8d4fdfce2fe4..a15ded92f08c4 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -266,6 +266,11 @@ impl Socket { } } + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn peek(&self, buf: &mut [u8]) -> io::Result { self.recv_with_flags(buf, c::MSG_PEEK) } @@ -324,6 +329,11 @@ impl Socket { Ok(nwritten as usize) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { let timeout = match dur { Some(dur) => { diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index cc4ae40590693..a0da2498bb7e0 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -94,7 +94,7 @@ impl Iterator for Env { if *self.cur == 0 { return None; } - let p = &*self.cur as *const u16; + let p = self.cur as *const u16; let mut len = 0; while *p.offset(len) != 0 { len += 1; diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs index ef260f9c5d21f..2f5fc72ab44c2 100644 --- a/src/libstd/sys/windows/os_str.rs +++ b/src/libstd/sys/windows/os_str.rs @@ -77,9 +77,21 @@ impl Buf { } pub fn as_slice(&self) -> &Slice { + // Safety: Slice is just a wrapper for Wtf8, + // and self.inner.as_slice() returns &Wtf8. + // Therefore, transmuting &Wtf8 to &Slice is safe. unsafe { mem::transmute(self.inner.as_slice()) } } + pub fn as_mut_slice(&mut self) -> &mut Slice { + // Safety: Slice is just a wrapper for Wtf8, + // and self.inner.as_mut_slice() returns &mut Wtf8. + // Therefore, transmuting &mut Wtf8 to &mut Slice is safe. + // Additionally, care should be taken to ensure the slice + // is always valid Wtf8. + unsafe { mem::transmute(self.inner.as_mut_slice()) } + } + pub fn into_string(self) -> Result { self.inner.into_string().map_err(|buf| Buf { inner: buf }) } @@ -147,6 +159,10 @@ impl Slice { Buf { inner: buf } } + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + #[inline] pub fn into_box(&self) -> Box { unsafe { mem::transmute(self.inner.into_box()) } @@ -167,4 +183,34 @@ impl Slice { let rc = self.inner.into_rc(); unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } } + + #[inline] + pub fn make_ascii_lowercase(&mut self) { + self.inner.make_ascii_lowercase() + } + + #[inline] + pub fn make_ascii_uppercase(&mut self) { + self.inner.make_ascii_uppercase() + } + + #[inline] + pub fn to_ascii_lowercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_lowercase() } + } + + #[inline] + pub fn to_ascii_uppercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_uppercase() } + } + + #[inline] + pub fn is_ascii(&self) -> bool { + self.inner.is_ascii() + } + + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { + self.inner.eq_ignore_ascii_case(&other.inner) + } } diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 992e634dea510..104a8db46596e 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -182,6 +182,11 @@ impl AnonPipe { self.inner.read_vectored(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.inner.write(buf) } @@ -189,6 +194,11 @@ impl AnonPipe { pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.write_vectored(bufs) } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } } pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index a62a637393ea3..77f9a5c9dc7b9 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -20,7 +20,7 @@ use crate::sys::mutex::Mutex; use crate::sys::pipe::{self, AnonPipe}; use crate::sys::stdio; use crate::sys_common::process::CommandEnv; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::AsInner; use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; @@ -33,10 +33,9 @@ use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; pub struct EnvKey(OsString); impl From for EnvKey { - fn from(k: OsString) -> Self { - let mut buf = k.into_inner().into_inner(); - buf.make_ascii_uppercase(); - EnvKey(FromInner::from_inner(FromInner::from_inner(buf))) + fn from(mut k: OsString) -> Self { + k.make_ascii_uppercase(); + EnvKey(k) } } diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index c828243a59b11..38839ea5e90ed 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -1,10 +1,9 @@ use crate::ffi::CStr; use crate::io; -use crate::mem; use crate::ptr; use crate::sys::c; use crate::sys::handle::Handle; -use crate::sys_common::thread::*; +use crate::sys::stack_overflow; use crate::time::Duration; use libc::c_void; @@ -20,7 +19,7 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = box p; + let p = Box::into_raw(box p); // FIXME On UNIX, we guard against stack sizes that are too small but // that's because pthreads enforces that stacks are at least @@ -34,21 +33,27 @@ impl Thread { ptr::null_mut(), stack_size, thread_start, - &*p as *const _ as *mut _, + p as *mut _, c::STACK_SIZE_PARAM_IS_A_RESERVATION, ptr::null_mut(), ); return if ret as usize == 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); Err(io::Error::last_os_error()) } else { - mem::forget(p); // ownership passed to CreateThread Ok(Thread { handle: Handle::new(ret) }) }; extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { unsafe { - start_thread(main as *mut u8); + // Next, set up our stack overflow handler which may get triggered if we run + // out of stack. + let _handler = stack_overflow::Handler::new(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); } 0 } diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs index 135e8308afaea..1c03bc9234448 100644 --- a/src/libstd/sys_common/net.rs +++ b/src/libstd/sys_common/net.rs @@ -17,7 +17,7 @@ cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd", target_os = "netbsd", + target_os = "openbsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "haiku", target_os = "l4re"))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; @@ -43,7 +43,7 @@ cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "solaris"))] { + target_os = "solaris", target_os = "illumos"))] { use libc::c_uchar; type IpV4MultiCastType = c_uchar; } else { @@ -265,6 +265,11 @@ impl TcpStream { self.inner.read_vectored(bufs) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + pub fn write(&self, buf: &[u8]) -> io::Result { let len = cmp::min(buf.len(), ::max_value() as usize) as wrlen_t; let ret = cvt(unsafe { @@ -277,6 +282,11 @@ impl TcpStream { self.inner.write_vectored(bufs) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + pub fn peer_addr(&self) -> io::Result { sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) } @@ -358,12 +368,15 @@ impl TcpListener { let sock = Socket::new(addr, c::SOCK_STREAM)?; - // On platforms with Berkeley-derived sockets, this allows - // to quickly rebind a socket, without needing to wait for - // the OS to clean up the previous one. - if !cfg!(windows) { - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; - } + // On platforms with Berkeley-derived sockets, this allows to quickly + // rebind a socket, without needing to wait for the OS to clean up the + // previous one. + // + // On Windows, this allows rebinding sockets which are actively in use, + // which allows “socket hijacking”, so we explicitly don't set it here. + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + #[cfg(not(windows))] + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; // Bind our new socket let (addrp, len) = addr.into_inner(); diff --git a/src/libstd/sys_common/os_str_bytes.rs b/src/libstd/sys_common/os_str_bytes.rs index e965ea79aa039..984c032e2a388 100644 --- a/src/libstd/sys_common/os_str_bytes.rs +++ b/src/libstd/sys_common/os_str_bytes.rs @@ -106,9 +106,20 @@ impl Buf { #[inline] pub fn as_slice(&self) -> &Slice { + // Safety: Slice just wraps [u8], + // and &*self.inner is &[u8], therefore + // transmuting &[u8] to &Slice is safe. unsafe { mem::transmute(&*self.inner) } } + #[inline] + pub fn as_mut_slice(&mut self) -> &mut Slice { + // Safety: Slice just wraps [u8], + // and &mut *self.inner is &mut [u8], therefore + // transmuting &mut [u8] to &mut Slice is safe. + unsafe { mem::transmute(&mut *self.inner) } + } + pub fn into_string(self) -> Result { String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() }) } @@ -162,6 +173,10 @@ impl Slice { Buf { inner: self.inner.to_vec() } } + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + #[inline] pub fn into_box(&self) -> Box { let boxed: Box<[u8]> = self.inner.into(); @@ -184,6 +199,36 @@ impl Slice { let rc: Rc<[u8]> = Rc::from(&self.inner); unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } } + + #[inline] + pub fn make_ascii_lowercase(&mut self) { + self.inner.make_ascii_lowercase() + } + + #[inline] + pub fn make_ascii_uppercase(&mut self) { + self.inner.make_ascii_uppercase() + } + + #[inline] + pub fn to_ascii_lowercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_lowercase() } + } + + #[inline] + pub fn to_ascii_uppercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_uppercase() } + } + + #[inline] + pub fn is_ascii(&self) -> bool { + self.inner.is_ascii() + } + + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { + self.inner.eq_ignore_ascii_case(&other.inner) + } } /// Platform-specific extensions to [`OsString`]. diff --git a/src/libstd/sys_common/thread.rs b/src/libstd/sys_common/thread.rs index 6ab0d5cbe9c96..f3a8bef8f718f 100644 --- a/src/libstd/sys_common/thread.rs +++ b/src/libstd/sys_common/thread.rs @@ -1,18 +1,7 @@ use crate::env; use crate::sync::atomic::{self, Ordering}; -use crate::sys::stack_overflow; use crate::sys::thread as imp; -#[allow(dead_code)] -pub unsafe fn start_thread(main: *mut u8) { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(); - - // Finally, let's run some code. - Box::from_raw(main as *mut Box)() -} - pub fn min_stack() -> usize { static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0); match MIN.load(Ordering::SeqCst) { diff --git a/src/libstd/sys_common/util.rs b/src/libstd/sys_common/util.rs index 00f7db4c03761..9f7c3bd87952f 100644 --- a/src/libstd/sys_common/util.rs +++ b/src/libstd/sys_common/util.rs @@ -16,9 +16,7 @@ pub fn dumb_print(args: fmt::Arguments<'_>) { pub fn abort(args: fmt::Arguments<'_>) -> ! { dumb_print(format_args!("fatal runtime error: {}\n", args)); - unsafe { - crate::sys::abort_internal(); - } + crate::sys::abort_internal(); } #[allow(dead_code)] // stack overflow detection not enabled on all platforms diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index 498950e682101..a98407da44850 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -613,6 +613,10 @@ impl Wtf8 { } } + pub fn clone_into(&self, buf: &mut Wtf8Buf) { + self.bytes.clone_into(&mut buf.bytes) + } + /// Boxes this `Wtf8`. #[inline] pub fn into_box(&self) -> Box { @@ -637,6 +641,36 @@ impl Wtf8 { let rc: Rc<[u8]> = Rc::from(&self.bytes); unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) } } + + #[inline] + pub fn make_ascii_lowercase(&mut self) { + self.bytes.make_ascii_lowercase() + } + + #[inline] + pub fn make_ascii_uppercase(&mut self) { + self.bytes.make_ascii_uppercase() + } + + #[inline] + pub fn to_ascii_lowercase(&self) -> Wtf8Buf { + Wtf8Buf { bytes: self.bytes.to_ascii_lowercase() } + } + + #[inline] + pub fn to_ascii_uppercase(&self) -> Wtf8Buf { + Wtf8Buf { bytes: self.bytes.to_ascii_uppercase() } + } + + #[inline] + pub fn is_ascii(&self) -> bool { + self.bytes.is_ascii() + } + + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { + self.bytes.eq_ignore_ascii_case(&other.bytes) + } } /// Returns a slice of the given string for the byte range [`begin`..`end`). @@ -837,12 +871,6 @@ impl Hash for Wtf8 { } } -impl Wtf8 { - pub fn make_ascii_uppercase(&mut self) { - self.bytes.make_ascii_uppercase() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 1dd942e252f6b..094c468a6770e 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -253,6 +253,7 @@ impl LocalKey { /// This function will still `panic!()` if the key is uninitialized and the /// key's initializer panics. #[stable(feature = "thread_local_try_with", since = "1.26.0")] + #[inline] pub fn try_with(&'static self, f: F) -> Result where F: FnOnce(&T) -> R, @@ -300,7 +301,7 @@ mod lazy { // value (an aliasing violation). To avoid setting the "I'm running a // destructor" flag we just use `mem::replace` which should sequence the // operations a little differently and make this safe to call. - mem::replace(&mut *ptr, Some(value)); + let _ = mem::replace(&mut *ptr, Some(value)); // After storing `Some` we want to get a reference to the contents of // what we just stored. While we could use `unwrap` here and it should diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 282e268efd206..3134a5967566b 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -737,6 +737,8 @@ pub fn panicking() -> bool { /// The thread may sleep longer than the duration specified due to scheduling /// specifics or platform-dependent functionality. It will never sleep less. /// +/// This function is blocking, and should not be used in `async` functions. +/// /// # Platform-specific behavior /// /// On Unix platforms, the underlying syscall may be interrupted by a @@ -763,6 +765,8 @@ pub fn sleep_ms(ms: u32) { /// The thread may sleep longer than the duration specified due to scheduling /// specifics or platform-dependent functionality. It will never sleep less. /// +/// This function is blocking, and should not be used in `async` functions. +/// /// # Platform-specific behavior /// /// On Unix platforms, the underlying syscall may be interrupted by a @@ -1062,7 +1066,7 @@ impl ThreadId { // If we somehow use up all our bits, panic so that we're not // covering up subtle bugs of IDs being reused. - if COUNTER == crate::u64::MAX { + if COUNTER == u64::MAX { panic!("failed to generate unique thread ID: bitspace exhausted"); } @@ -1272,7 +1276,7 @@ impl Thread { } fn cname(&self) -> Option<&CStr> { - self.inner.name.as_ref().map(|s| &**s) + self.inner.name.as_deref() } } diff --git a/src/libterm/terminfo/mod.rs b/src/libterm/terminfo/mod.rs index 918875e792a66..fec59aaa0c279 100644 --- a/src/libterm/terminfo/mod.rs +++ b/src/libterm/terminfo/mod.rs @@ -173,14 +173,13 @@ impl Terminal for TerminfoTerminal { fn reset(&mut self) -> io::Result { // are there any terminals that have color/attrs and not sgr0? // Try falling back to sgr, then op - let cmd = - match ["sgr0", "sgr", "op"].iter().filter_map(|cap| self.ti.strings.get(*cap)).next() { - Some(op) => match expand(&op, &[], &mut Variables::new()) { - Ok(cmd) => cmd, - Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)), - }, - None => return Ok(false), - }; + let cmd = match ["sgr0", "sgr", "op"].iter().find_map(|cap| self.ti.strings.get(*cap)) { + Some(op) => match expand(&op, &[], &mut Variables::new()) { + Ok(cmd) => cmd, + Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)), + }, + None => return Ok(false), + }; self.out.write_all(&cmd).and(Ok(true)) } diff --git a/src/libtest/cli.rs b/src/libtest/cli.rs index 5317063b80d11..0cec8050c279d 100644 --- a/src/libtest/cli.rs +++ b/src/libtest/cli.rs @@ -213,7 +213,7 @@ macro_rules! unstable_optflag { let opt = $matches.opt_present($option_name); if !$allow_unstable && opt { return Err(format!( - "The \"{}\" flag is only accepted on the nightly compiler", + "The \"{}\" flag is only accepted on the nightly compiler with -Z unstable-options", $option_name )); } @@ -331,7 +331,7 @@ fn get_format( quiet: bool, allow_unstable: bool, ) -> OptPartRes { - let format = match matches.opt_str("format").as_ref().map(|s| &**s) { + let format = match matches.opt_str("format").as_deref() { None if quiet => OutputFormat::Terse, Some("pretty") | None => OutputFormat::Pretty, Some("terse") => OutputFormat::Terse, @@ -355,7 +355,7 @@ fn get_format( } fn get_color_config(matches: &getopts::Matches) -> OptPartRes { - let color = match matches.opt_str("color").as_ref().map(|s| &**s) { + let color = match matches.opt_str("color").as_deref() { Some("auto") | None => ColorConfig::AutoColor, Some("always") => ColorConfig::AlwaysColor, Some("never") => ColorConfig::NeverColor, diff --git a/src/libtest/helpers/concurrency.rs b/src/libtest/helpers/concurrency.rs index 6b0c8a8af32b4..e8f3820558a6d 100644 --- a/src/libtest/helpers/concurrency.rs +++ b/src/libtest/helpers/concurrency.rs @@ -77,6 +77,7 @@ pub fn get_concurrency() -> usize { target_os = "linux", target_os = "macos", target_os = "solaris", + target_os = "illumos", ))] fn num_cpus() -> usize { unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 55f9df9caafb2..933b647071f79 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -20,7 +20,6 @@ #![crate_name = "test"] #![unstable(feature = "test", issue = "50297")] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![feature(asm)] #![cfg_attr(any(unix, target_os = "cloudabi"), feature(libc))] #![feature(rustc_private)] #![feature(nll)] diff --git a/src/libtest/stats/tests.rs b/src/libtest/stats/tests.rs index 5bfd1d3885f2a..3a6e8401bf1ab 100644 --- a/src/libtest/stats/tests.rs +++ b/src/libtest/stats/tests.rs @@ -2,7 +2,6 @@ use super::*; extern crate test; use self::test::test::Bencher; -use std::f64; use std::io; use std::io::prelude::*; diff --git a/src/libunwind/build.rs b/src/libunwind/build.rs index 0628e5d2fc03a..1462639259cb8 100644 --- a/src/libunwind/build.rs +++ b/src/libunwind/build.rs @@ -30,6 +30,8 @@ fn main() { } } else if target.contains("solaris") { println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("illumos") { + println!("cargo:rustc-link-lib=gcc_s"); } else if target.contains("dragonfly") { println!("cargo:rustc-link-lib=gcc_pic"); } else if target.contains("pc-windows-gnu") { @@ -87,6 +89,7 @@ mod llvm_libunwind { cfg.flag("-fno-rtti"); cfg.flag("-fstrict-aliasing"); cfg.flag("-funwind-tables"); + cfg.flag("-fvisibility=hidden"); } let mut unwind_sources = vec![ diff --git a/src/llvm-project b/src/llvm-project index 9f65ad057357b..943dbddc8e086 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 9f65ad057357b307180955831968f79e74090a90 +Subproject commit 943dbddc8e0869a382c4e1b2c135a40e28cf605c diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 9e8614e3b6d34..6c638c5453a18 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -13,6 +13,8 @@ #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Verifier.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Passes/PassBuilder.h" #if LLVM_VERSION_GE(9, 0) #include "llvm/Passes/StandardInstrumentations.h" @@ -33,10 +35,8 @@ #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Support/TimeProfiler.h" #endif -#if LLVM_VERSION_GE(8, 0) #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" -#endif #if LLVM_VERSION_GE(9, 0) #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #endif @@ -138,19 +138,13 @@ extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool return wrap(createMemorySanitizerLegacyPassPass( MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel})); -#elif LLVM_VERSION_GE(8, 0) - return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover)); #else - return wrap(createMemorySanitizerPass(TrackOrigins, Recover)); + return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover)); #endif } extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { -#if LLVM_VERSION_GE(8, 0) return wrap(createThreadSanitizerLegacyPassPass()); -#else - return wrap(createThreadSanitizerPass()); -#endif } extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { @@ -280,7 +274,7 @@ extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, } enum class LLVMRustCodeModel { - Other, + Tiny, Small, Kernel, Medium, @@ -288,8 +282,10 @@ enum class LLVMRustCodeModel { None, }; -static CodeModel::Model fromRust(LLVMRustCodeModel Model) { +static Optional fromRust(LLVMRustCodeModel Model) { switch (Model) { + case LLVMRustCodeModel::Tiny: + return CodeModel::Tiny; case LLVMRustCodeModel::Small: return CodeModel::Small; case LLVMRustCodeModel::Kernel: @@ -298,6 +294,8 @@ static CodeModel::Model fromRust(LLVMRustCodeModel Model) { return CodeModel::Medium; case LLVMRustCodeModel::Large: return CodeModel::Large; + case LLVMRustCodeModel::None: + return None; default: report_fatal_error("Bad CodeModel."); } @@ -354,8 +352,7 @@ static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level } } -enum class LLVMRustRelocMode { - Default, +enum class LLVMRustRelocModel { Static, PIC, DynamicNoPic, @@ -364,21 +361,19 @@ enum class LLVMRustRelocMode { ROPIRWPI, }; -static Optional fromRust(LLVMRustRelocMode RustReloc) { +static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) { switch (RustReloc) { - case LLVMRustRelocMode::Default: - return None; - case LLVMRustRelocMode::Static: + case LLVMRustRelocModel::Static: return Reloc::Static; - case LLVMRustRelocMode::PIC: + case LLVMRustRelocModel::PIC: return Reloc::PIC_; - case LLVMRustRelocMode::DynamicNoPic: + case LLVMRustRelocModel::DynamicNoPic: return Reloc::DynamicNoPIC; - case LLVMRustRelocMode::ROPI: + case LLVMRustRelocModel::ROPI: return Reloc::ROPI; - case LLVMRustRelocMode::RWPI: + case LLVMRustRelocModel::RWPI: return Reloc::RWPI; - case LLVMRustRelocMode::ROPIRWPI: + case LLVMRustRelocModel::ROPIRWPI: return Reloc::ROPI_RWPI; } report_fatal_error("Bad RelocModel."); @@ -448,7 +443,7 @@ extern "C" const char* LLVMRustGetHostCPUName(size_t *len) { extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( const char *TripleStr, const char *CPU, const char *Feature, - const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocMode RustReloc, + const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc, LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, bool PositionIndependentExecutable, bool FunctionSections, bool DataSections, @@ -456,10 +451,12 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool Singlethread, bool AsmComments, bool EmitStackSizeSection, - bool RelaxELFRelocations) { + bool RelaxELFRelocations, + bool UseInitArray) { auto OptLevel = fromRust(RustOptLevel); auto RM = fromRust(RustReloc); + auto CM = fromRust(RustCM); std::string Error; Triple Trip(Triple::normalize(TripleStr)); @@ -482,6 +479,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.MCOptions.PreserveAsmComments = AsmComments; Options.MCOptions.ABIName = ABIStr; Options.RelaxELFRelocations = RelaxELFRelocations; + Options.UseInitArray = UseInitArray; if (TrapUnreachable) { // Tell LLVM to codegen `unreachable` into an explicit trap instruction. @@ -497,9 +495,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.EmitStackSizeSection = EmitStackSizeSection; - Optional CM; - if (RustCM != LLVMRustCodeModel::None) - CM = fromRust(RustCM); TargetMachine *TM = TheTarget->createTargetMachine( Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel); return wrap(TM); @@ -724,7 +719,7 @@ LLVMRustOptimizeWithNewPassManager( LLVMRustOptStage OptStage, bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers, bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, - bool DisableSimplifyLibCalls, + bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, void* LlvmSelfProfiler, @@ -860,7 +855,7 @@ LLVMRustOptimizeWithNewPassManager( MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } - MPM.addPass(AlwaysInlinerPass(/*InsertLifetimeIntrinsics=*/false)); + MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers)); #if LLVM_VERSION_GE(10, 0) if (PGOOpt) { @@ -1236,15 +1231,11 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, auto deadIsPrevailing = [&](GlobalValue::GUID G) { return PrevailingType::Unknown; }; -#if LLVM_VERSION_GE(8, 0) // We don't have a complete picture in our use of ThinLTO, just our immediate // crate, so we need `ImportEnabled = false` to limit internalization. // Otherwise, we sometimes lose `static` values -- see #60184. computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols, deadIsPrevailing, /* ImportEnabled = */ false); -#else - computeDeadSymbols(Ret->Index, Ret->GUIDPreservedSymbols, deadIsPrevailing); -#endif ComputeCrossModuleImport( Ret->Index, Ret->ModuleToDefinedGVSummaries, @@ -1277,10 +1268,8 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, #if LLVM_VERSION_GE(9, 0) thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, Ret->GUIDPreservedSymbols); -#elif LLVM_VERSION_GE(8, 0) - thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage); #else - thinLTOResolveWeakForLinkerInIndex(Ret->Index, isPrevailing, recordNewLinkage); + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage); #endif // Here we calculate an `ExportedGUIDs` set for use in the `isExported` @@ -1346,11 +1335,7 @@ extern "C" bool LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { Module &Mod = *unwrap(M); const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); -#if LLVM_VERSION_GE(8, 0) thinLTOResolvePrevailingInModule(Mod, DefinedGlobals); -#else - thinLTOResolveWeakForLinkerModule(Mod, DefinedGlobals); -#endif return true; } @@ -1496,6 +1481,32 @@ LLVMRustParseBitcodeForLTO(LLVMContextRef Context, return wrap(std::move(*SrcOrError).release()); } +// Find the bitcode section in the object file data and return it as a slice. +// Fail if the bitcode section is present but empty. +// +// On success, the return value is the pointer to the start of the slice and +// `out_len` is filled with the (non-zero) length. On failure, the return value +// is `nullptr` and `out_len` is set to zero. +extern "C" const char* +LLVMRustGetBitcodeSliceFromObjectData(const char *data, + size_t len, + size_t *out_len) { + *out_len = 0; + + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, ""); // The id is unused. + + Expected BitcodeOrError = + object::IRObjectFile::findBitcodeInMemBuffer(Buffer); + if (!BitcodeOrError) { + LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str()); + return nullptr; + } + + *out_len = BitcodeOrError->getBufferSize(); + return BitcodeOrError->getBufferStart(); +} + // Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See // the comment in `back/lto.rs` for why this exists. extern "C" void diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 799adb418822d..b988d06871bf6 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -203,6 +203,10 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::OptimizeNone; case ReturnsTwice: return Attribute::ReturnsTwice; + case ReadNone: + return Attribute::ReadNone; + case InaccessibleMemOnly: + return Attribute::InaccessibleMemOnly; } report_fatal_error("bad AttributeKind"); } @@ -586,7 +590,6 @@ inline LLVMRustDISPFlags virtuality(LLVMRustDISPFlags F) { return static_cast(static_cast(F) & 0x3); } -#if LLVM_VERSION_GE(8, 0) static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { DISubprogram::DISPFlags Result = DISubprogram::DISPFlags::SPFlagZero; @@ -619,7 +622,6 @@ static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { return Result; } -#endif enum class LLVMRustDebugEmissionKind { NoDebug, @@ -640,6 +642,25 @@ static DICompileUnit::DebugEmissionKind fromRust(LLVMRustDebugEmissionKind Kind) } } +enum class LLVMRustChecksumKind { + None, + MD5, + SHA1, +}; + +static Optional fromRust(LLVMRustChecksumKind Kind) { + switch (Kind) { + case LLVMRustChecksumKind::None: + return None; + case LLVMRustChecksumKind::MD5: + return DIFile::ChecksumKind::CSK_MD5; + case LLVMRustChecksumKind::SHA1: + return DIFile::ChecksumKind::CSK_SHA1; + default: + report_fatal_error("bad ChecksumKind."); + } +} + extern "C" uint32_t LLVMRustDebugMetadataVersion() { return DEBUG_METADATA_VERSION; } @@ -686,9 +707,15 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( LLVMRustDIBuilderRef Builder, const char *Filename, size_t FilenameLen, - const char *Directory, size_t DirectoryLen) { + const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind, + const char *Checksum, size_t ChecksumLen) { + Optional llvmCSKind = fromRust(CSKind); + Optional> CSInfo{}; + if (llvmCSKind) + CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen}); return wrap(Builder->createFile(StringRef(Filename, FilenameLen), - StringRef(Directory, DirectoryLen))); + StringRef(Directory, DirectoryLen), + CSInfo)); } extern "C" LLVMMetadataRef @@ -709,7 +736,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( LLVMMetadataRef Decl) { DITemplateParameterArray TParams = DITemplateParameterArray(unwrap(TParam)); -#if LLVM_VERSION_GE(8, 0) DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); DINode::DIFlags llvmFlags = fromRust(Flags); #if LLVM_VERSION_LT(9, 0) @@ -723,22 +749,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( unwrapDI(File), LineNo, unwrapDI(Ty), ScopeLine, llvmFlags, llvmSPFlags, TParams, unwrapDIPtr(Decl)); -#else - bool IsLocalToUnit = isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit); - bool IsDefinition = isSet(SPFlags & LLVMRustDISPFlags::SPFlagDefinition); - bool IsOptimized = isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized); - DINode::DIFlags llvmFlags = fromRust(Flags); - if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) - llvmFlags |= DINode::DIFlags::FlagMainSubprogram; - DISubprogram *Sub = Builder->createFunction( - unwrapDI(Scope), - StringRef(Name, NameLen), - StringRef(LinkageName, LinkageNameLen), - unwrapDI(File), LineNo, - unwrapDI(Ty), IsLocalToUnit, IsDefinition, - ScopeLine, llvmFlags, IsOptimized, TParams, - unwrapDIPtr(Decl)); -#endif unwrap(Fn)->setSubprogram(Sub); return wrap(Sub); } @@ -859,9 +869,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( /* isDefined */ true, #endif InitExpr, unwrapDIPtr(Decl), -#if LLVM_VERSION_GE(8, 0) /* templateParams */ nullptr, -#endif AlignInBits); InitVal->setMetadata("dbg", VarExpr); @@ -1082,11 +1090,7 @@ extern "C" void LLVMRustUnpackOptimizationDiagnostic( if (loc.isValid()) { *Line = loc.getLine(); *Column = loc.getColumn(); -#if LLVM_VERSION_GE(8, 0) FilenameOS << loc.getAbsolutePath(); -#else - FilenameOS << loc.getFilename(); -#endif } RawRustStringOstream MessageOS(MessageOut); diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index c3f0d174d4b41..da48048113bc2 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -82,6 +82,8 @@ enum LLVMRustAttribute { NonLazyBind = 23, OptimizeNone = 24, ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/src/stage0.txt b/src/stage0.txt index 4d9a91e38b33c..150d7a0943db0 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -10,9 +10,9 @@ # If you're looking at this file on the master branch, you'll likely see that # rustc and cargo are configured to `beta`, whereas if you're looking at a # source tarball for a stable release you'll likely see `1.x.0` for rustc and -# `0.x.0` for Cargo where they were released on `date`. +# `0.(x+1).0` for Cargo where they were released on `date`. -date: 2020-03-12 +date: 2020-04-22 rustc: beta cargo: beta @@ -20,7 +20,7 @@ cargo: beta # bootstrapping issues with use of new syntax in this repo. If you're looking at # the beta/stable branch, this key should be omitted, as we don't want to depend # on rustfmt from nightly there. -rustfmt: nightly-2020-01-31 +rustfmt: nightly-2020-04-22 # When making a stable release the process currently looks like: # diff --git a/src/stdarch b/src/stdarch index abe96ca3b87fc..ec6fccd34c300 160000 --- a/src/stdarch +++ b/src/stdarch @@ -1 +1 @@ -Subproject commit abe96ca3b87fcca6aa1dfcefd40d8c8d92d2e673 +Subproject commit ec6fccd34c30003a7ebf4e7a9dfe4e31f5b76e1b diff --git a/src/test/assembly/asm/aarch64-modifiers.rs b/src/test/assembly/asm/aarch64-modifiers.rs new file mode 100644 index 0000000000000..c2484e9b6d0a6 --- /dev/null +++ b/src/test/assembly/asm/aarch64-modifiers.rs @@ -0,0 +1,145 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: -O +// compile-flags: --target aarch64-unknown-linux-gnu + +#![feature(no_core, lang_items, rustc_attrs)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +impl Copy for i32 {} + +macro_rules! check { + ($func:ident $reg:ident $code:literal) => { + // -O and extern "C" guarantee that the selected register is always r0/s0/d0/q0 + #[no_mangle] + pub unsafe extern "C" fn $func() -> i32 { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!($code, out($reg) y); + y + } + }; +} + +// CHECK-LABEL: reg: +// CHECK: //APP +// CHECK: mov x0, x0 +// CHECK: //NO_APP +check!(reg reg "mov {0}, {0}"); + +// CHECK-LABEL: reg_w: +// CHECK: //APP +// CHECK: mov w0, w0 +// CHECK: //NO_APP +check!(reg_w reg "mov {0:w}, {0:w}"); + +// CHECK-LABEL: reg_x: +// CHECK: //APP +// CHECK: mov x0, x0 +// CHECK: //NO_APP +check!(reg_x reg "mov {0:x}, {0:x}"); + +// CHECK-LABEL: vreg: +// CHECK: //APP +// CHECK: add v0.4s, v0.4s, v0.4s +// CHECK: //NO_APP +check!(vreg vreg "add {0}.4s, {0}.4s, {0}.4s"); + +// CHECK-LABEL: vreg_b: +// CHECK: //APP +// CHECK: ldr b0, [x0] +// CHECK: //NO_APP +check!(vreg_b vreg "ldr {:b}, [x0]"); + +// CHECK-LABEL: vreg_h: +// CHECK: //APP +// CHECK: ldr h0, [x0] +// CHECK: //NO_APP +check!(vreg_h vreg "ldr {:h}, [x0]"); + +// CHECK-LABEL: vreg_s: +// CHECK: //APP +// CHECK: ldr s0, [x0] +// CHECK: //NO_APP +check!(vreg_s vreg "ldr {:s}, [x0]"); + +// CHECK-LABEL: vreg_d: +// CHECK: //APP +// CHECK: ldr d0, [x0] +// CHECK: //NO_APP +check!(vreg_d vreg "ldr {:d}, [x0]"); + +// CHECK-LABEL: vreg_q: +// CHECK: //APP +// CHECK: ldr q0, [x0] +// CHECK: //NO_APP +check!(vreg_q vreg "ldr {:q}, [x0]"); + +// CHECK-LABEL: vreg_v: +// CHECK: //APP +// CHECK: add v0.4s, v0.4s, v0.4s +// CHECK: //NO_APP +check!(vreg_v vreg "add {0:v}.4s, {0:v}.4s, {0:v}.4s"); + +// CHECK-LABEL: vreg_low16: +// CHECK: //APP +// CHECK: add v0.4s, v0.4s, v0.4s +// CHECK: //NO_APP +check!(vreg_low16 vreg_low16 "add {0}.4s, {0}.4s, {0}.4s"); + +// CHECK-LABEL: vreg_low16_b: +// CHECK: //APP +// CHECK: ldr b0, [x0] +// CHECK: //NO_APP +check!(vreg_low16_b vreg_low16 "ldr {:b}, [x0]"); + +// CHECK-LABEL: vreg_low16_h: +// CHECK: //APP +// CHECK: ldr h0, [x0] +// CHECK: //NO_APP +check!(vreg_low16_h vreg_low16 "ldr {:h}, [x0]"); + +// CHECK-LABEL: vreg_low16_s: +// CHECK: //APP +// CHECK: ldr s0, [x0] +// CHECK: //NO_APP +check!(vreg_low16_s vreg_low16 "ldr {:s}, [x0]"); + +// CHECK-LABEL: vreg_low16_d: +// CHECK: //APP +// CHECK: ldr d0, [x0] +// CHECK: //NO_APP +check!(vreg_low16_d vreg_low16 "ldr {:d}, [x0]"); + +// CHECK-LABEL: vreg_low16_q: +// CHECK: //APP +// CHECK: ldr q0, [x0] +// CHECK: //NO_APP +check!(vreg_low16_q vreg_low16 "ldr {:q}, [x0]"); + +// CHECK-LABEL: vreg_low16_v: +// CHECK: //APP +// CHECK: add v0.4s, v0.4s, v0.4s +// CHECK: //NO_APP +check!(vreg_low16_v vreg_low16 "add {0:v}.4s, {0:v}.4s, {0:v}.4s"); diff --git a/src/test/assembly/asm/aarch64-types.rs b/src/test/assembly/asm/aarch64-types.rs new file mode 100644 index 0000000000000..ce2f0082a06b1 --- /dev/null +++ b/src/test/assembly/asm/aarch64-types.rs @@ -0,0 +1,381 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: --target aarch64-unknown-linux-gnu + +#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register, non_camel_case_types)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *mut u8; + +#[repr(simd)] +pub struct i8x8(i8, i8, i8, i8, i8, i8, i8, i8); +#[repr(simd)] +pub struct i16x4(i16, i16, i16, i16); +#[repr(simd)] +pub struct i32x2(i32, i32); +#[repr(simd)] +pub struct i64x1(i64); +#[repr(simd)] +pub struct f32x2(f32, f32); +#[repr(simd)] +pub struct f64x1(f64); +#[repr(simd)] +pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); +#[repr(simd)] +pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); +#[repr(simd)] +pub struct i32x4(i32, i32, i32, i32); +#[repr(simd)] +pub struct i64x2(i64, i64); +#[repr(simd)] +pub struct f32x4(f32, f32, f32, f32); +#[repr(simd)] +pub struct f64x2(f64, f64); + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for i64 {} +impl Copy for f64 {} +impl Copy for ptr {} +impl Copy for i8x8 {} +impl Copy for i16x4 {} +impl Copy for i32x2 {} +impl Copy for i64x1 {} +impl Copy for f32x2 {} +impl Copy for f64x1 {} +impl Copy for i8x16 {} +impl Copy for i16x8 {} +impl Copy for i32x4 {} +impl Copy for i64x2 {} +impl Copy for f32x4 {} +impl Copy for f64x2 {} + +extern "C" { + fn extern_func(); + static extern_static: u8; +} + +// CHECK-LABEL: sym_fn: +// CHECK: //APP +// CHECK: bl extern_func +// CHECK: //NO_APP +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("bl {}", sym extern_func); +} + +// CHECK-LABEL: sym_static: +// CHECK: //APP +// CHECK: adr x0, extern_static +// CHECK: //NO_APP +#[no_mangle] +pub unsafe fn sym_static() { + asm!("adr x0, {}", sym extern_static); +} + +macro_rules! check { + ($func:ident $ty:ident $class:ident $mov:literal $modifier:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!( + concat!($mov, " {:", $modifier, "}, {:", $modifier, "}"), + out($class) y, + in($class) x + ); + y + } + }; +} + +// CHECK-LABEL: reg_i8: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check!(reg_i8 i8 reg "mov" ""); + +// CHECK-LABEL: reg_i16: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check!(reg_i16 i16 reg "mov" ""); + +// CHECK-LABEL: reg_i32: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check!(reg_i32 i32 reg "mov" ""); + +// CHECK-LABEL: reg_f32: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check!(reg_f32 f32 reg "mov" ""); + +// CHECK-LABEL: reg_i64: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check!(reg_i64 i64 reg "mov" ""); + +// CHECK-LABEL: reg_f64: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check!(reg_f64 f64 reg "mov" ""); + +// CHECK-LABEL: reg_ptr: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check!(reg_ptr ptr reg "mov" ""); + +// CHECK-LABEL: vreg_i8: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i8 i8 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i16: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i16 i16 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i32: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i32 i32 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_f32: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_f32 f32 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i64: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i64 i64 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_f64: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_f64 f64 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_ptr: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_ptr ptr vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i8x8: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i8x8 i8x8 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i16x4: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i16x4 i16x4 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i32x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i32x2 i32x2 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i64x1: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i64x1 i64x1 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_f32x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_f32x2 f32x2 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_f64x1: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_f64x1 f64x1 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i8x16: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i8x16 i8x16 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i16x8: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i16x8 i16x8 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i32x4: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i32x4 i32x4 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_i64x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_i64x2 i64x2 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_f32x4: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_f32x4 f32x4 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_f64x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_f64x2 f64x2 vreg "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i8: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i8 i8 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i16: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i16 i16 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_f32: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_f32 f32 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i64: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i64 i64 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_f64: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_f64 f64 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_ptr: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_ptr ptr vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i8x8: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i8x8 i8x8 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i16x4: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i16x4 i16x4 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i32x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i32x2 i32x2 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i64x1: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i64x1 i64x1 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_f32x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_f32x2 f32x2 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_f64x1: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_f64x1 f64x1 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i8x16: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i8x16 i8x16 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i16x8: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i16x8 i16x8 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i32x4: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i32x4 i32x4 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_i64x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_i64x2 i64x2 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_f32x4: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_f32x4 f32x4 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: vreg_low16_f64x2: +// CHECK: //APP +// CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: //NO_APP +check!(vreg_low16_f64x2 f64x2 vreg_low16 "fmov" "s"); diff --git a/src/test/assembly/asm/arm-modifiers.rs b/src/test/assembly/asm/arm-modifiers.rs new file mode 100644 index 0000000000000..b71503d0a535e --- /dev/null +++ b/src/test/assembly/asm/arm-modifiers.rs @@ -0,0 +1,150 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: -O +// compile-flags: --target armv7-unknown-linux-gnueabihf +// compile-flags: -C target-feature=+neon + +#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register, non_camel_case_types)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +#[repr(simd)] +pub struct f32x4(f32, f32, f32, f32); + +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for f64 {} +impl Copy for f32x4 {} + +macro_rules! check { + ($func:ident $modifier:literal $reg:ident $ty:ident $mov:literal) => { + // -O and extern "C" guarantee that the selected register is always r0/s0/d0/q0 + #[no_mangle] + pub unsafe extern "C" fn $func() -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {0:", $modifier, "}, {0:", $modifier, "}"), out($reg) y); + y + } + }; +} + +// CHECK-LABEL: reg: +// CHECK: @APP +// CHECK: mov r0, r0 +// CHECK: @NO_APP +check!(reg "" reg i32 "mov"); + +// CHECK-LABEL: reg_thumb: +// CHECK: @APP +// CHECK: mov r0, r0 +// CHECK: @NO_APP +check!(reg_thumb "" reg_thumb i32 "mov"); + +// CHECK-LABEL: sreg: +// CHECK: @APP +// CHECK: vmov.f32 s0, s0 +// CHECK: @NO_APP +check!(sreg "" sreg f32 "vmov.f32"); + +// CHECK-LABEL: sreg_low16: +// CHECK: @APP +// CHECK: vmov.f32 s0, s0 +// CHECK: @NO_APP +check!(sreg_low16 "" sreg_low16 f32 "vmov.f32"); + +// CHECK-LABEL: dreg: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check!(dreg "" dreg f64 "vmov.f64"); + +// CHECK-LABEL: dreg_low16: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check!(dreg_low16 "" dreg_low16 f64 "vmov.f64"); + +// CHECK-LABEL: dreg_low8: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check!(dreg_low8 "" dreg_low8 f64 "vmov.f64"); + +// CHECK-LABEL: qreg: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check!(qreg "" qreg f32x4 "vmov"); + +// CHECK-LABEL: qreg_e: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check!(qreg_e "e" qreg f32x4 "vmov.f64"); + +// CHECK-LABEL: qreg_f: +// CHECK: @APP +// CHECK: vmov.f64 d1, d1 +// CHECK: @NO_APP +check!(qreg_f "f" qreg f32x4 "vmov.f64"); + +// CHECK-LABEL: qreg_low8: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check!(qreg_low8 "" qreg_low8 f32x4 "vmov"); + +// CHECK-LABEL: qreg_low8_e: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check!(qreg_low8_e "e" qreg_low8 f32x4 "vmov.f64"); + +// CHECK-LABEL: qreg_low8_f: +// CHECK: @APP +// CHECK: vmov.f64 d1, d1 +// CHECK: @NO_APP +check!(qreg_low8_f "f" qreg_low8 f32x4 "vmov.f64"); + +// CHECK-LABEL: qreg_low4: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check!(qreg_low4 "" qreg_low4 f32x4 "vmov"); + +// CHECK-LABEL: qreg_low4_e: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check!(qreg_low4_e "e" qreg_low4 f32x4 "vmov.f64"); + +// CHECK-LABEL: qreg_low4_f: +// CHECK: @APP +// CHECK: vmov.f64 d1, d1 +// CHECK: @NO_APP +check!(qreg_low4_f "f" qreg_low4 f32x4 "vmov.f64"); diff --git a/src/test/assembly/asm/arm-types.rs b/src/test/assembly/asm/arm-types.rs new file mode 100644 index 0000000000000..1e338f56c4dd7 --- /dev/null +++ b/src/test/assembly/asm/arm-types.rs @@ -0,0 +1,414 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: --target armv7-unknown-linux-gnueabihf +// compile-flags: -C target-feature=+neon + +#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register, non_camel_case_types)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *mut u8; + +#[repr(simd)] +pub struct i8x8(i8, i8, i8, i8, i8, i8, i8, i8); +#[repr(simd)] +pub struct i16x4(i16, i16, i16, i16); +#[repr(simd)] +pub struct i32x2(i32, i32); +#[repr(simd)] +pub struct i64x1(i64); +#[repr(simd)] +pub struct f32x2(f32, f32); +#[repr(simd)] +pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); +#[repr(simd)] +pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); +#[repr(simd)] +pub struct i32x4(i32, i32, i32, i32); +#[repr(simd)] +pub struct i64x2(i64, i64); +#[repr(simd)] +pub struct f32x4(f32, f32, f32, f32); + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for i64 {} +impl Copy for f64 {} +impl Copy for ptr {} +impl Copy for i8x8 {} +impl Copy for i16x4 {} +impl Copy for i32x2 {} +impl Copy for i64x1 {} +impl Copy for f32x2 {} +impl Copy for i8x16 {} +impl Copy for i16x8 {} +impl Copy for i32x4 {} +impl Copy for i64x2 {} +impl Copy for f32x4 {} + +extern "C" { + fn extern_func(); + static extern_static: u8; +} + +// CHECK-LABEL: sym_fn: +// CHECK: @APP +// CHECK: bl extern_func +// CHECK: @NO_APP +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("bl {}", sym extern_func); +} + +// CHECK-LABEL: sym_static: +// CHECK: @APP +// CHECK: adr r0, extern_static +// CHECK: @NO_APP +#[no_mangle] +pub unsafe fn sym_static() { + asm!("adr r0, {}", sym extern_static); +} + +macro_rules! check { + ($func:ident $ty:ident $class:ident $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {}, {}"), out($class) y, in($class) x); + y + } + }; +} + +// CHECK-LABEL: reg_i8: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_i8 i8 reg "mov"); + +// CHECK-LABEL: reg_i16: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_i16 i16 reg "mov"); + +// CHECK-LABEL: reg_i32: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_i32 i32 reg "mov"); + +// CHECK-LABEL: reg_f32: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_f32 f32 reg "mov"); + +// CHECK-LABEL: reg_ptr: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_ptr ptr reg "mov"); + +// CHECK-LABEL: reg_thumb_i8: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_thumb_i8 i8 reg_thumb "mov"); + +// CHECK-LABEL: reg_thumb_i16: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_thumb_i16 i16 reg_thumb "mov"); + +// CHECK-LABEL: reg_thumb_i32: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_thumb_i32 i32 reg_thumb "mov"); + +// CHECK-LABEL: reg_thumb_f32: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_thumb_f32 f32 reg_thumb "mov"); + +// CHECK-LABEL: reg_thumb_ptr: +// CHECK: @APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: @NO_APP +check!(reg_thumb_ptr ptr reg_thumb "mov"); + +// CHECK-LABEL: sreg_i32: +// CHECK: @APP +// CHECK: vmov.f32 s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: @NO_APP +check!(sreg_i32 i32 sreg "vmov.f32"); + +// CHECK-LABEL: sreg_f32: +// CHECK: @APP +// CHECK: vmov.f32 s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: @NO_APP +check!(sreg_f32 f32 sreg "vmov.f32"); + +// CHECK-LABEL: sreg_ptr: +// CHECK: @APP +// CHECK: vmov.f32 s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: @NO_APP +check!(sreg_ptr ptr sreg "vmov.f32"); + +// CHECK-LABEL: sreg_low16_i32: +// CHECK: @APP +// CHECK: vmov.f32 s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: @NO_APP +check!(sreg_low16_i32 i32 sreg_low16 "vmov.f32"); + +// CHECK-LABEL: sreg_low16_f32: +// CHECK: @APP +// CHECK: vmov.f32 s{{[0-9]+}}, s{{[0-9]+}} +// CHECK: @NO_APP +check!(sreg_low16_f32 f32 sreg_low16 "vmov.f32"); + +// CHECK-LABEL: dreg_i64: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_i64 i64 dreg "vmov.f64"); + +// CHECK-LABEL: dreg_f64: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_f64 f64 dreg "vmov.f64"); + +// CHECK-LABEL: dreg_i8x8: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_i8x8 i8x8 dreg "vmov.f64"); + +// CHECK-LABEL: dreg_i16x4: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_i16x4 i16x4 dreg "vmov.f64"); + +// CHECK-LABEL: dreg_i32x2: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_i32x2 i32x2 dreg "vmov.f64"); + +// CHECK-LABEL: dreg_i64x1: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_i64x1 i64x1 dreg "vmov.f64"); + +// CHECK-LABEL: dreg_f32x2: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_f32x2 f32x2 dreg "vmov.f64"); + +// CHECK-LABEL: dreg_low16_i64: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low16_i64 i64 dreg_low16 "vmov.f64"); + +// CHECK-LABEL: dreg_low16_f64: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low16_f64 f64 dreg_low16 "vmov.f64"); + +// CHECK-LABEL: dreg_low16_i8x8: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low16_i8x8 i8x8 dreg_low16 "vmov.f64"); + +// CHECK-LABEL: dreg_low16_i16x4: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low16_i16x4 i16x4 dreg_low16 "vmov.f64"); + +// CHECK-LABEL: dreg_low16_i32x2: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low16_i32x2 i32x2 dreg_low16 "vmov.f64"); + +// CHECK-LABEL: dreg_low16_i64x1: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low16_i64x1 i64x1 dreg_low16 "vmov.f64"); + +// CHECK-LABEL: dreg_low16_f32x2: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low16_f32x2 f32x2 dreg_low16 "vmov.f64"); + +// CHECK-LABEL: dreg_low8_i64: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low8_i64 i64 dreg_low8 "vmov.f64"); + +// CHECK-LABEL: dreg_low8_f64: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low8_f64 f64 dreg_low8 "vmov.f64"); + +// CHECK-LABEL: dreg_low8_i8x8: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low8_i8x8 i8x8 dreg_low8 "vmov.f64"); + +// CHECK-LABEL: dreg_low8_i16x4: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low8_i16x4 i16x4 dreg_low8 "vmov.f64"); + +// CHECK-LABEL: dreg_low8_i32x2: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low8_i32x2 i32x2 dreg_low8 "vmov.f64"); + +// CHECK-LABEL: dreg_low8_i64x1: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low8_i64x1 i64x1 dreg_low8 "vmov.f64"); + +// CHECK-LABEL: dreg_low8_f32x2: +// CHECK: @APP +// CHECK: vmov.f64 d{{[0-9]+}}, d{{[0-9]+}} +// CHECK: @NO_APP +check!(dreg_low8_f32x2 f32x2 dreg_low8 "vmov.f64"); + +// CHECK-LABEL: qreg_i8x16: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_i8x16 i8x16 qreg "vmov"); + +// CHECK-LABEL: qreg_i16x8: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_i16x8 i16x8 qreg "vmov"); + +// CHECK-LABEL: qreg_i32x4: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_i32x4 i32x4 qreg "vmov"); + +// CHECK-LABEL: qreg_i64x2: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_i64x2 i64x2 qreg "vmov"); + +// CHECK-LABEL: qreg_f32x4: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_f32x4 f32x4 qreg "vmov"); + +// CHECK-LABEL: qreg_low8_i8x16: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low8_i8x16 i8x16 qreg_low8 "vmov"); + +// CHECK-LABEL: qreg_low8_i16x8: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low8_i16x8 i16x8 qreg_low8 "vmov"); + +// CHECK-LABEL: qreg_low8_i32x4: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low8_i32x4 i32x4 qreg_low8 "vmov"); + +// CHECK-LABEL: qreg_low8_i64x2: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low8_i64x2 i64x2 qreg_low8 "vmov"); + +// CHECK-LABEL: qreg_low8_f32x4: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low8_f32x4 f32x4 qreg_low8 "vmov"); + +// CHECK-LABEL: qreg_low4_i8x16: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low4_i8x16 i8x16 qreg_low4 "vmov"); + +// CHECK-LABEL: qreg_low4_i16x8: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low4_i16x8 i16x8 qreg_low4 "vmov"); + +// CHECK-LABEL: qreg_low4_i32x4: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low4_i32x4 i32x4 qreg_low4 "vmov"); + +// CHECK-LABEL: qreg_low4_i64x2: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low4_i64x2 i64x2 qreg_low4 "vmov"); + +// CHECK-LABEL: qreg_low4_f32x4: +// CHECK: @APP +// CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} +// CHECK: @NO_APP +check!(qreg_low4_f32x4 f32x4 qreg_low4 "vmov"); diff --git a/src/test/assembly/asm/riscv-modifiers.rs b/src/test/assembly/asm/riscv-modifiers.rs new file mode 100644 index 0000000000000..8c816e3220b74 --- /dev/null +++ b/src/test/assembly/asm/riscv-modifiers.rs @@ -0,0 +1,59 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: -O +// compile-flags: --target riscv64gc-unknown-linux-gnu +// compile-flags: -C target-feature=+f + +#![feature(no_core, lang_items, rustc_attrs)] +#![crate_type = "rlib"] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +impl Copy for f32 {} + +macro_rules! check { + ($func:ident $modifier:literal $reg:ident $mov:literal) => { + // -O and extern "C" guarantee that the selected register is always r0/s0/d0/q0 + #[no_mangle] + pub unsafe extern "C" fn $func() -> f32 { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {0:", $modifier, "}, {0:", $modifier, "}"), out($reg) y); + y + } + }; +} + +// CHECK-LABEL: reg: +// CHECK: #APP +// CHECK: mv a0, a0 +// CHECK: #NO_APP +check!(reg "" reg "mv"); + +// CHECK-LABEL: freg: +// CHECK: #APP +// CHECK: fmv.s fa0, fa0 +// CHECK: #NO_APP +check!(freg "" freg "fmv.s"); diff --git a/src/test/assembly/asm/riscv-types.rs b/src/test/assembly/asm/riscv-types.rs new file mode 100644 index 0000000000000..449213471cc6f --- /dev/null +++ b/src/test/assembly/asm/riscv-types.rs @@ -0,0 +1,135 @@ +// no-system-llvm +// revisions: riscv64 riscv32 +// assembly-output: emit-asm +//[riscv64] compile-flags: --target riscv64imac-unknown-none-elf +//[riscv32] compile-flags: --target riscv32imac-unknown-none-elf +// compile-flags: -C target-feature=+d + +#![feature(no_core, lang_items, rustc_attrs)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *mut u8; + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for i64 {} +impl Copy for f64 {} +impl Copy for ptr {} + +extern "C" { + fn extern_func(); + static extern_static: u8; +} + +// CHECK-LABEL: sym_fn: +// CHECK: #APP +// CHECK: call extern_func +// CHECK: #NO_APP +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("call {}", sym extern_func); +} + +// CHECK-LABEL: sym_static: +// CHECK: #APP +// CHECK: lb t0, extern_static +// CHECK: #NO_APP +#[no_mangle] +pub unsafe fn sym_static() { + asm!("lb t0, {}", sym extern_static); +} + +macro_rules! check { + ($func:ident $ty:ident $class:ident $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {}, {}"), out($class) y, in($class) x); + y + } + }; +} + +// CHECK-LABEL: reg_i8: +// CHECK: #APP +// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_i8 i8 reg "mv"); + +// CHECK-LABEL: reg_i16: +// CHECK: #APP +// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_i16 i16 reg "mv"); + +// CHECK-LABEL: reg_i32: +// CHECK: #APP +// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_i32 i32 reg "mv"); + +// CHECK-LABEL: reg_f32: +// CHECK: #APP +// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_f32 f32 reg "mv"); + +// riscv64-LABEL: reg_i64: +// riscv64: #APP +// riscv64: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// riscv64: #NO_APP +#[cfg(riscv64)] +check!(reg_i64 i64 reg "mv"); + +// riscv64-LABEL: reg_f64: +// riscv64: #APP +// riscv64: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// riscv64: #NO_APP +#[cfg(riscv64)] +check!(reg_f64 f64 reg "mv"); + +// CHECK-LABEL: reg_ptr: +// CHECK: #APP +// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_ptr ptr reg "mv"); + +// CHECK-LABEL: freg_f32: +// CHECK: #APP +// CHECK: fmv.s f{{[a-z0-9]+}}, f{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(freg_f32 f32 freg "fmv.s"); + +// CHECK-LABEL: freg_f64: +// CHECK: #APP +// CHECK: fmv.d f{{[a-z0-9]+}}, f{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(freg_f64 f64 freg "fmv.d"); diff --git a/src/test/assembly/asm/x86-modifiers.rs b/src/test/assembly/asm/x86-modifiers.rs new file mode 100644 index 0000000000000..e538167cd462a --- /dev/null +++ b/src/test/assembly/asm/x86-modifiers.rs @@ -0,0 +1,204 @@ +// no-system-llvm +// revisions: x86_64 i686 +// assembly-output: emit-asm +// compile-flags: -O +//[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//[i686] compile-flags: --target i686-unknown-linux-gnu +// compile-flags: -C llvm-args=--x86-asm-syntax=intel +// compile-flags: -C target-feature=+avx512bw + +#![feature(no_core, lang_items, rustc_attrs)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +impl Copy for i32 {} + +macro_rules! check { + ($func:ident $modifier:literal $reg:ident $mov:literal) => { + // -O and extern "C" guarantee that the selected register is always ax/xmm0 + #[no_mangle] + pub unsafe extern "C" fn $func() -> i32 { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {0:", $modifier, "}, {0:", $modifier, "}"), out($reg) y); + y + } + }; +} + +// CHECK-LABEL: reg: +// CHECK: #APP +// x86_64: mov rax, rax +// i686: mov eax, eax +// CHECK: #NO_APP +check!(reg "" reg "mov"); + +// x86_64-LABEL: reg_l: +// x86_64: #APP +// x86_64: mov al, al +// x86_64: #NO_APP +#[cfg(x86_64)] +check!(reg_l "l" reg "mov"); + +// CHECK-LABEL: reg_x: +// CHECK: #APP +// CHECK: mov ax, ax +// CHECK: #NO_APP +check!(reg_x "x" reg "mov"); + +// CHECK-LABEL: reg_e: +// CHECK: #APP +// CHECK: mov eax, eax +// CHECK: #NO_APP +check!(reg_e "e" reg "mov"); + +// x86_64-LABEL: reg_r: +// x86_64: #APP +// x86_64: mov rax, rax +// x86_64: #NO_APP +#[cfg(x86_64)] +check!(reg_r "r" reg "mov"); + +// CHECK-LABEL: reg_abcd: +// CHECK: #APP +// x86_64: mov rax, rax +// i686: mov eax, eax +// CHECK: #NO_APP +check!(reg_abcd "" reg_abcd "mov"); + +// CHECK-LABEL: reg_abcd_l: +// CHECK: #APP +// CHECK: mov al, al +// CHECK: #NO_APP +check!(reg_abcd_l "l" reg_abcd "mov"); + +// CHECK-LABEL: reg_abcd_h: +// CHECK: #APP +// CHECK: mov ah, ah +// CHECK: #NO_APP +check!(reg_abcd_h "h" reg_abcd "mov"); + +// CHECK-LABEL: reg_abcd_x: +// CHECK: #APP +// CHECK: mov ax, ax +// CHECK: #NO_APP +check!(reg_abcd_x "x" reg_abcd "mov"); + +// CHECK-LABEL: reg_abcd_e: +// CHECK: #APP +// CHECK: mov eax, eax +// CHECK: #NO_APP +check!(reg_abcd_e "e" reg_abcd "mov"); + +// x86_64-LABEL: reg_abcd_r: +// x86_64: #APP +// x86_64: mov rax, rax +// x86_64: #NO_APP +#[cfg(x86_64)] +check!(reg_abcd_r "r" reg_abcd "mov"); + +// CHECK-LABEL: xmm_reg +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check!(xmm_reg "" xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_x +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check!(xmm_reg_x "x" xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_y +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check!(xmm_reg_y "y" xmm_reg "vmovaps"); + +// CHECK-LABEL: xmm_reg_z +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check!(xmm_reg_z "z" xmm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg +// CHECK: #APP +// CHECK: movaps ymm0, ymm0 +// CHECK: #NO_APP +check!(ymm_reg "" ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_x +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check!(ymm_reg_x "x" ymm_reg "movaps"); + +// CHECK-LABEL: ymm_reg_y +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check!(ymm_reg_y "y" ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_z +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check!(ymm_reg_z "z" ymm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg +// CHECK: #APP +// CHECK: movaps zmm0, zmm0 +// CHECK: #NO_APP +check!(zmm_reg "" zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_x +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check!(zmm_reg_x "x" zmm_reg "movaps"); + +// CHECK-LABEL: zmm_reg_y +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check!(zmm_reg_y "y" zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_z +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check!(zmm_reg_z "z" zmm_reg "vmovaps"); + +// Note: we don't have any way of ensuring that k1 is actually the register +// chosen by the register allocator, so this check may fail if a different +// register is chosen. + +// CHECK-LABEL: kreg: +// CHECK: #APP +// CHECK: kmovb k1, k1 +// CHECK: #NO_APP +check!(kreg "" kreg "kmovb"); diff --git a/src/test/assembly/asm/x86-types.rs b/src/test/assembly/asm/x86-types.rs new file mode 100644 index 0000000000000..de2e67c421f2e --- /dev/null +++ b/src/test/assembly/asm/x86-types.rs @@ -0,0 +1,694 @@ +// no-system-llvm +// revisions: x86_64 i686 +// assembly-output: emit-asm +//[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//[i686] compile-flags: --target i686-unknown-linux-gnu +// compile-flags: -C llvm-args=--x86-asm-syntax=intel +// compile-flags: -C target-feature=+avx512bw + +#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register, non_camel_case_types)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *mut u8; + +#[repr(simd)] +pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); +#[repr(simd)] +pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); +#[repr(simd)] +pub struct i32x4(i32, i32, i32, i32); +#[repr(simd)] +pub struct i64x2(i64, i64); +#[repr(simd)] +pub struct f32x4(f32, f32, f32, f32); +#[repr(simd)] +pub struct f64x2(f64, f64); + +#[repr(simd)] +pub struct i8x32( + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, +); +#[repr(simd)] +pub struct i16x16(i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16); +#[repr(simd)] +pub struct i32x8(i32, i32, i32, i32, i32, i32, i32, i32); +#[repr(simd)] +pub struct i64x4(i64, i64, i64, i64); +#[repr(simd)] +pub struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32); +#[repr(simd)] +pub struct f64x4(f64, f64, f64, f64); + +#[repr(simd)] +pub struct i8x64( + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, + i8, +); +#[repr(simd)] +pub struct i16x32( + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, + i16, +); +#[repr(simd)] +pub struct i32x16(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32); +#[repr(simd)] +pub struct i64x8(i64, i64, i64, i64, i64, i64, i64, i64); +#[repr(simd)] +pub struct f32x16(f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32); +#[repr(simd)] +pub struct f64x8(f64, f64, f64, f64, f64, f64, f64, f64); + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for i64 {} +impl Copy for f64 {} +impl Copy for ptr {} +impl Copy for i8x16 {} +impl Copy for i16x8 {} +impl Copy for i32x4 {} +impl Copy for i64x2 {} +impl Copy for f32x4 {} +impl Copy for f64x2 {} +impl Copy for i8x32 {} +impl Copy for i16x16 {} +impl Copy for i32x8 {} +impl Copy for i64x4 {} +impl Copy for f32x8 {} +impl Copy for f64x4 {} +impl Copy for i8x64 {} +impl Copy for i16x32 {} +impl Copy for i32x16 {} +impl Copy for i64x8 {} +impl Copy for f32x16 {} +impl Copy for f64x8 {} + +extern "C" { + fn extern_func(); + static extern_static: u8; +} + +// CHECK-LABEL: sym_fn: +// CHECK: #APP +// CHECK: call extern_func +// CHECK: #NO_APP +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("call {}", sym extern_func); +} + +// CHECK-LABEL: sym_static: +// CHECK: #APP +// CHECK: mov al, byte ptr [extern_static] +// CHECK: #NO_APP +#[no_mangle] +pub unsafe fn sym_static() { + asm!("mov al, byte ptr [{}]", sym extern_static); +} + +macro_rules! check { + ($func:ident $ty:ident $class:ident $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {}, {}"), out($class) y, in($class) x); + y + } + }; +} + +// CHECK-LABEL: reg_i16: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_i16 i16 reg "mov"); + +// CHECK-LABEL: reg_i32: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_i32 i32 reg "mov"); + +// CHECK-LABEL: reg_f32: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_f32 f32 reg "mov"); + +// x86_64-LABEL: reg_i64: +// x86_64: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// x86_64: #NO_APP +#[cfg(x86_64)] +check!(reg_i64 i64 reg "mov"); + +// x86_64-LABEL: reg_f64: +// x86_64: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// x86_64: #NO_APP +#[cfg(x86_64)] +check!(reg_f64 f64 reg "mov"); + +// CHECK-LABEL: reg_ptr: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_ptr ptr reg "mov"); + +// CHECK-LABEL: reg_abcd_i16: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_abcd_i16 i16 reg_abcd "mov"); + +// CHECK-LABEL: reg_abcd_i32: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_abcd_i32 i32 reg_abcd "mov"); + +// CHECK-LABEL: reg_abcd_f32: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_abcd_f32 f32 reg_abcd "mov"); + +// x86_64-LABEL: reg_abcd_i64: +// x86_64: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// x86_64: #NO_APP +#[cfg(x86_64)] +check!(reg_abcd_i64 i64 reg_abcd "mov"); + +// x86_64-LABEL: reg_abcd_f64: +// x86_64: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// x86_64: #NO_APP +#[cfg(x86_64)] +check!(reg_abcd_f64 f64 reg_abcd "mov"); + +// CHECK-LABEL: reg_abcd_ptr: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_abcd_ptr ptr reg_abcd "mov"); + +// CHECK-LABEL: reg_byte: +// CHECK: #APP +// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_byte i8 reg_byte "mov"); + +// CHECK-LABEL: xmm_reg_i32: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_i32 i32 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_f32: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_f32 f32 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_i64: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_i64 i64 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_f64: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_f64 f64 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_ptr: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_ptr ptr xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_i8x16: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_i8x16 i8x16 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_i16x8: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_i16x8 i16x8 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_i32x4: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_i32x4 i32x4 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_i64x2: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_i64x2 i64x2 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_f32x4: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_f32x4 f32x4 xmm_reg "movaps"); + +// CHECK-LABEL: xmm_reg_f64x2: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_f64x2 f64x2 xmm_reg "movaps"); + +// CHECK-LABEL: ymm_reg_i32: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i32 i32 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_f32: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f32 f32 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i64: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i64 i64 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_f64: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f64 f64 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_ptr: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_ptr ptr ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i8x16: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i8x16 i8x16 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i16x8: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i16x8 i16x8 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i32x4: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i32x4 i32x4 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i64x2: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i64x2 i64x2 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_f32x4: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f32x4 f32x4 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_f64x2: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f64x2 f64x2 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i8x32: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i8x32 i8x32 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i16x16: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i16x16 i16x16 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i32x8: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i32x8 i32x8 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_i64x4: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_i64x4 i64x4 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_f32x8: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f32x8 f32x8 ymm_reg "vmovaps"); + +// CHECK-LABEL: ymm_reg_f64x4: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f64x4 f64x4 ymm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i32: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i32 i32 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f32: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f32 f32 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i64: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i64 i64 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f64: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f64 f64 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_ptr: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_ptr ptr zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i8x16: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i8x16 i8x16 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i16x8: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i16x8 i16x8 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i32x4: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i32x4 i32x4 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i64x2: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i64x2 i64x2 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f32x4: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f32x4 f32x4 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f64x2: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f64x2 f64x2 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i8x32: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i8x32 i8x32 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i16x16: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i16x16 i16x16 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i32x8: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i32x8 i32x8 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i64x4: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i64x4 i64x4 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f32x8: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f32x8 f32x8 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f64x4: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f64x4 f64x4 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i8x64: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i8x64 i8x64 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i16x32: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i16x32 i16x32 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i32x16: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i32x16 i32x16 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_i64x8: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_i64x8 i64x8 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f32x16: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f32x16 f32x16 zmm_reg "vmovaps"); + +// CHECK-LABEL: zmm_reg_f64x8: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f64x8 f64x8 zmm_reg "vmovaps"); + +// CHECK-LABEL: kreg_i8: +// CHECK: #APP +// CHECK: kmovb k{{[0-9]+}}, k{{[0-9]+}} +// CHECK: #NO_APP +check!(kreg_i8 i8 kreg "kmovb"); + +// CHECK-LABEL: kreg_i16: +// CHECK: #APP +// CHECK: kmovw k{{[0-9]+}}, k{{[0-9]+}} +// CHECK: #NO_APP +check!(kreg_i16 i16 kreg "kmovw"); + +// CHECK-LABEL: kreg_i32: +// CHECK: #APP +// CHECK: kmovd k{{[0-9]+}}, k{{[0-9]+}} +// CHECK: #NO_APP +check!(kreg_i32 i32 kreg "kmovd"); + +// CHECK-LABEL: kreg_i64: +// CHECK: #APP +// CHECK: kmovq k{{[0-9]+}}, k{{[0-9]+}} +// CHECK: #NO_APP +check!(kreg_i64 i64 kreg "kmovq"); + +// CHECK-LABEL: kreg_ptr: +// CHECK: #APP +// CHECK: kmovq k{{[0-9]+}}, k{{[0-9]+}} +// CHECK: #NO_APP +check!(kreg_ptr ptr kreg "kmovq"); diff --git a/src/test/auxiliary/rust_test_helpers.c b/src/test/auxiliary/rust_test_helpers.c index 897c940149b0a..92b7dd4b7c516 100644 --- a/src/test/auxiliary/rust_test_helpers.c +++ b/src/test/auxiliary/rust_test_helpers.c @@ -368,6 +368,7 @@ rust_dbg_unpack_option_u64(struct U8TaggedEnumOptionU64 o, uint64_t *into) { return 0; default: assert(0 && "unexpected tag"); + return 0; } } @@ -411,5 +412,6 @@ rust_dbg_unpack_option_u64u64(struct U8TaggedEnumOptionU64U64 o, uint64_t *a, ui return 0; default: assert(0 && "unexpected tag"); + return 0; } } diff --git a/src/test/codegen-units/partitioning/compiler-builtins.rs b/src/test/codegen-units/partitioning/compiler-builtins.rs new file mode 100644 index 0000000000000..25195743b0400 --- /dev/null +++ b/src/test/codegen-units/partitioning/compiler-builtins.rs @@ -0,0 +1,40 @@ +// Verifies that during compiler_builtins compilation the codegen units are kept +// unmerged. Even when only a single codegen unit is requested with -Ccodegen-units=1. +// +// compile-flags: -Zprint-mono-items=eager -Ccodegen-units=1 + +#![compiler_builtins] +#![crate_type="lib"] +#![feature(compiler_builtins)] + +mod atomics { + //~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_1[0] @@ compiler_builtins-cgu.0[External] + #[no_mangle] + pub extern "C" fn sync_1() {} + + //~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_2[0] @@ compiler_builtins-cgu.0[External] + #[no_mangle] + pub extern "C" fn sync_2() {} + + //~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_3[0] @@ compiler_builtins-cgu.0[External] + #[no_mangle] + pub extern "C" fn sync_3() {} +} + +mod x { + //~ MONO_ITEM fn compiler_builtins::x[0]::x[0] @@ compiler_builtins-cgu.1[External] + #[no_mangle] + pub extern "C" fn x() {} +} + +mod y { + //~ MONO_ITEM fn compiler_builtins::y[0]::y[0] @@ compiler_builtins-cgu.2[External] + #[no_mangle] + pub extern "C" fn y() {} +} + +mod z { + //~ MONO_ITEM fn compiler_builtins::z[0]::z[0] @@ compiler_builtins-cgu.3[External] + #[no_mangle] + pub extern "C" fn z() {} +} diff --git a/src/test/codegen-units/partitioning/extern-drop-glue.rs b/src/test/codegen-units/partitioning/extern-drop-glue.rs index 662519067d78e..1cb85382239cd 100644 --- a/src/test/codegen-units/partitioning/extern-drop-glue.rs +++ b/src/test/codegen-units/partitioning/extern-drop-glue.rs @@ -1,9 +1,9 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation // We specify opt-level=0 because `drop_in_place` is `Internal` when optimizing -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/extern-drop-glue +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/extern-drop-glue // compile-flags:-Zinline-in-all-cgus -Copt-level=0 #![allow(dead_code)] diff --git a/src/test/codegen-units/partitioning/extern-generic.rs b/src/test/codegen-units/partitioning/extern-generic.rs index c96c54312fbaa..88d6116a987fe 100644 --- a/src/test/codegen-units/partitioning/extern-generic.rs +++ b/src/test/codegen-units/partitioning/extern-generic.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/extern-generic -Zshare-generics=y +// compile-flags:-Zprint-mono-items=eager -Cincremental=tmp/partitioning-tests/extern-generic -Zshare-generics=y #![allow(dead_code)] #![crate_type="lib"] diff --git a/src/test/codegen-units/partitioning/incremental-merging.rs b/src/test/codegen-units/partitioning/incremental-merging.rs new file mode 100644 index 0000000000000..ca2df19096e4d --- /dev/null +++ b/src/test/codegen-units/partitioning/incremental-merging.rs @@ -0,0 +1,42 @@ +// ignore-tidy-linelength +// We specify -C incremental here because we want to test the partitioning for +// incremental compilation +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/incremental-merging +// compile-flags:-Ccodegen-units=3 + +#![crate_type = "rlib"] + +// This test makes sure that merging of CGUs works together with incremental +// compilation but at the same time does not modify names of CGUs that were not +// affected by merging. +// +// We expect CGUs `aaa` and `bbb` to be merged (because they are the smallest), +// while `ccc` and `ddd` are supposed to stay untouched. + +pub mod aaa { + //~ MONO_ITEM fn incremental_merging::aaa[0]::foo[0] @@ incremental_merging-aaa--incremental_merging-bbb[External] + pub fn foo(a: u64) -> u64 { + a + 1 + } +} + +pub mod bbb { + //~ MONO_ITEM fn incremental_merging::bbb[0]::foo[0] @@ incremental_merging-aaa--incremental_merging-bbb[External] + pub fn foo(a: u64, b: u64) -> u64 { + a + b + 1 + } +} + +pub mod ccc { + //~ MONO_ITEM fn incremental_merging::ccc[0]::foo[0] @@ incremental_merging-ccc[External] + pub fn foo(a: u64, b: u64, c: u64) -> u64 { + a + b + c + 1 + } +} + +pub mod ddd { + //~ MONO_ITEM fn incremental_merging::ddd[0]::foo[0] @@ incremental_merging-ddd[External] + pub fn foo(a: u64, b: u64, c: u64, d: u64) -> u64 { + a + b + c + d + 1 + } +} diff --git a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs index e943f54a40631..7afeb0a0f367b 100644 --- a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs +++ b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/inlining-from-extern-crate +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/inlining-from-extern-crate // compile-flags:-Zinline-in-all-cgus #![crate_type="lib"] diff --git a/src/test/codegen-units/partitioning/local-drop-glue.rs b/src/test/codegen-units/partitioning/local-drop-glue.rs index 14a50bf579806..c082b40827878 100644 --- a/src/test/codegen-units/partitioning/local-drop-glue.rs +++ b/src/test/codegen-units/partitioning/local-drop-glue.rs @@ -1,8 +1,8 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation // We specify opt-level=0 because `drop_in_place` is `Internal` when optimizing -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-drop-glue +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/local-drop-glue // compile-flags:-Zinline-in-all-cgus -Copt-level=0 #![allow(dead_code)] diff --git a/src/test/codegen-units/partitioning/local-generic.rs b/src/test/codegen-units/partitioning/local-generic.rs index dcff638b0b1ce..4518166a1c9ba 100644 --- a/src/test/codegen-units/partitioning/local-generic.rs +++ b/src/test/codegen-units/partitioning/local-generic.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/local-generic +// compile-flags:-Zprint-mono-items=eager -Cincremental=tmp/partitioning-tests/local-generic #![allow(dead_code)] #![crate_type="lib"] diff --git a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs index 18211fad31e58..6322f55d2b740 100644 --- a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs +++ b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining-but-not-all +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/local-inlining-but-not-all // compile-flags:-Zinline-in-all-cgus=no #![allow(dead_code)] diff --git a/src/test/codegen-units/partitioning/local-inlining.rs b/src/test/codegen-units/partitioning/local-inlining.rs index 7aa83e4bf4163..d75dfc91262e2 100644 --- a/src/test/codegen-units/partitioning/local-inlining.rs +++ b/src/test/codegen-units/partitioning/local-inlining.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/local-inlining // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] diff --git a/src/test/codegen-units/partitioning/local-transitive-inlining.rs b/src/test/codegen-units/partitioning/local-transitive-inlining.rs index 5bc56146794bf..3cf03966865e1 100644 --- a/src/test/codegen-units/partitioning/local-transitive-inlining.rs +++ b/src/test/codegen-units/partitioning/local-transitive-inlining.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-transitive-inlining +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/local-transitive-inlining // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] diff --git a/src/test/codegen-units/partitioning/methods-are-with-self-type.rs b/src/test/codegen-units/partitioning/methods-are-with-self-type.rs index c2961ed9322ad..6c55904c1bf10 100644 --- a/src/test/codegen-units/partitioning/methods-are-with-self-type.rs +++ b/src/test/codegen-units/partitioning/methods-are-with-self-type.rs @@ -4,9 +4,9 @@ // ignore-test // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/methods-are-with-self-type +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/methods-are-with-self-type #![allow(dead_code)] #![feature(start)] diff --git a/src/test/codegen-units/partitioning/regular-modules.rs b/src/test/codegen-units/partitioning/regular-modules.rs index f42dc3dfc17a9..c8ceeafd0bfe9 100644 --- a/src/test/codegen-units/partitioning/regular-modules.rs +++ b/src/test/codegen-units/partitioning/regular-modules.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/regular-modules +// compile-flags:-Zprint-mono-items=eager -Cincremental=tmp/partitioning-tests/regular-modules #![allow(dead_code)] #![crate_type="lib"] diff --git a/src/test/codegen-units/partitioning/shared-generics.rs b/src/test/codegen-units/partitioning/shared-generics.rs index 47ff94437ff37..99142dd6b7e25 100644 --- a/src/test/codegen-units/partitioning/shared-generics.rs +++ b/src/test/codegen-units/partitioning/shared-generics.rs @@ -2,7 +2,7 @@ // no-prefer-dynamic // NOTE: We always compile this test with -Copt-level=0 because higher opt-levels // prevent drop-glue from participating in share-generics. -// compile-flags:-Zprint-mono-items=eager -Zshare-generics=yes -Zincremental=tmp/partitioning-tests/shared-generics-exe -Copt-level=0 +// compile-flags:-Zprint-mono-items=eager -Zshare-generics=yes -Cincremental=tmp/partitioning-tests/shared-generics-exe -Copt-level=0 #![crate_type="rlib"] diff --git a/src/test/codegen-units/partitioning/statics.rs b/src/test/codegen-units/partitioning/statics.rs index bbded480b0c15..5eac046b810d7 100644 --- a/src/test/codegen-units/partitioning/statics.rs +++ b/src/test/codegen-units/partitioning/statics.rs @@ -1,6 +1,6 @@ -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/statics +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/statics #![crate_type="rlib"] diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs index 06e2ef6bb2257..5a1d95d266987 100644 --- a/src/test/codegen-units/partitioning/vtable-through-const.rs +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -1,8 +1,8 @@ // ignore-tidy-linelength -// We specify -Z incremental here because we want to test the partitioning for +// We specify -C incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/vtable-through-const +// compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/vtable-through-const // compile-flags:-Zinline-in-all-cgus // This test case makes sure, that references made through constants are diff --git a/src/test/codegen/align-enum.rs b/src/test/codegen/align-enum.rs index 72447fbc079dd..95ca7cfe75080 100644 --- a/src/test/codegen/align-enum.rs +++ b/src/test/codegen/align-enum.rs @@ -1,4 +1,4 @@ -// compile-flags: -C no-prepopulate-passes +// compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 // ignore-tidy-linelength #![crate_type = "lib"] diff --git a/src/test/codegen/align-struct.rs b/src/test/codegen/align-struct.rs index 5e290323907d0..cda7235a3d81d 100644 --- a/src/test/codegen/align-struct.rs +++ b/src/test/codegen/align-struct.rs @@ -1,4 +1,4 @@ -// compile-flags: -C no-prepopulate-passes +// compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 // ignore-tidy-linelength #![crate_type = "lib"] diff --git a/src/test/codegen/asm-options.rs b/src/test/codegen/asm-options.rs new file mode 100644 index 0000000000000..21e7eb4379634 --- /dev/null +++ b/src/test/codegen/asm-options.rs @@ -0,0 +1,96 @@ +// compile-flags: -O +// only-x86_64 + +#![crate_type = "rlib"] +#![feature(asm)] + +// CHECK-LABEL: @pure +// CHECK-NOT: asm +// CHECK: ret void +#[no_mangle] +pub unsafe fn pure(x: i32) { + let y: i32; + asm!("", out("ax") y, in("bx") x, options(pure, nomem)); +} + +// CHECK-LABEL: @noreturn +// CHECK: call void asm +// CHECK-NEXT: unreachable +#[no_mangle] +pub unsafe fn noreturn() { + asm!("", options(noreturn)); +} + +pub static mut VAR: i32 = 0; +pub static mut DUMMY_OUTPUT: i32 = 0; + +// CHECK-LABEL: @readonly +// CHECK: call i32 asm +// CHECK: ret i32 1 +#[no_mangle] +pub unsafe fn readonly() -> i32 { + VAR = 1; + asm!("", out("ax") DUMMY_OUTPUT, options(pure, readonly)); + VAR +} + +// CHECK-LABEL: @not_readonly +// CHECK: call i32 asm +// CHECK: ret i32 % +#[no_mangle] +pub unsafe fn not_readonly() -> i32 { + VAR = 1; + asm!("", out("ax") DUMMY_OUTPUT, options()); + VAR +} + +// CHECK-LABEL: @nomem +// CHECK-NOT: store +// CHECK: call i32 asm +// CHECK: store +// CHECK: ret i32 2 +#[no_mangle] +pub unsafe fn nomem() -> i32 { + VAR = 1; + asm!("", out("ax") DUMMY_OUTPUT, options(pure, nomem)); + VAR = 2; + VAR +} + +// CHECK-LABEL: @nomem_nopure +// CHECK-NOT: store +// CHECK: call i32 asm +// CHECK: store +// CHECK: ret i32 2 +#[no_mangle] +pub unsafe fn nomem_nopure() -> i32 { + VAR = 1; + asm!("", out("ax") DUMMY_OUTPUT, options(nomem)); + VAR = 2; + VAR +} + +// CHECK-LABEL: @not_nomem +// CHECK: store +// CHECK: call i32 asm +// CHECK: store +// CHECK: ret i32 2 +#[no_mangle] +pub unsafe fn not_nomem() -> i32 { + VAR = 1; + asm!("", out("ax") DUMMY_OUTPUT, options(pure, readonly)); + VAR = 2; + VAR +} + +// CHECK-LABEL: @dont_remove_nonpure +// CHECK: call void asm +// CHECK: call void asm +// CHECK: call void asm +// CHECK: ret void +#[no_mangle] +pub unsafe fn dont_remove_nonpure() { + asm!("", options()); + asm!("", options(nomem)); + asm!("", options(readonly)); +} diff --git a/src/test/codegen/c-variadic.rs b/src/test/codegen/c-variadic.rs index 971f4e3e12ea8..29c82686731ca 100644 --- a/src/test/codegen/c-variadic.rs +++ b/src/test/codegen/c-variadic.rs @@ -16,13 +16,13 @@ extern "C" { #[unwind(aborts)] // FIXME(#58794) pub unsafe extern "C" fn use_foreign_c_variadic_0() { // Ensure that we correctly call foreign C-variadic functions. - // CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM:i32( signext)?]] 0) + // CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM:i32( signext)?]] 0) foreign_c_variadic_0(0); - // CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42) + // CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42) foreign_c_variadic_0(0, 42i32); - // CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024) + // CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024) foreign_c_variadic_0(0, 42i32, 1024i32); - // CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024, [[PARAM]] 0) + // CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024, [[PARAM]] 0) foreign_c_variadic_0(0, 42i32, 1024i32, 0i32); } @@ -30,24 +30,24 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() { // removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics. #[unwind(aborts)] // FIXME(#58794) pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) { - // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap) + // CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap) foreign_c_variadic_1(ap); } #[unwind(aborts)] // FIXME(#58794) pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) { - // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42) + // CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42) foreign_c_variadic_1(ap, 42i32); } #[unwind(aborts)] // FIXME(#58794) pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) { - // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42) + // CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42) foreign_c_variadic_1(ap, 2i32, 42i32); } #[unwind(aborts)] // FIXME(#58794) pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) { - // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0) + // CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0) foreign_c_variadic_1(ap, 2i32, 42i32, 0i32); } diff --git a/src/test/codegen/call-llvm-intrinsics.rs b/src/test/codegen/call-llvm-intrinsics.rs new file mode 100644 index 0000000000000..c7a464a9b0ef2 --- /dev/null +++ b/src/test/codegen/call-llvm-intrinsics.rs @@ -0,0 +1,27 @@ +// compile-flags: -C no-prepopulate-passes + +#![feature(link_llvm_intrinsics)] +#![crate_type = "lib"] + +struct A; + +impl Drop for A { + fn drop(&mut self) { + println!("A"); + } +} + +extern { + #[link_name = "llvm.sqrt.f32"] + fn sqrt(x: f32) -> f32; +} + +pub fn do_call() { + let _a = A; + + unsafe { + // Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them + // CHECK: call float @llvm.sqrt.f32(float 4.000000e+00 + sqrt(4.0); + } +} diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index e53e75b339bef..ed93af2f993d3 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -10,11 +10,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @alloc5 = {{.*}}, align 2 +// CHECK: @alloc7 = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @alloc15, i32 0, i32 0, i32 0), {{.*}} +// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @alloc19, i32 0, i32 0, i32 0), {{.*}} #[derive(Copy, Clone)] // repr(i16) is required for the {low,high}_align_const test diff --git a/src/test/codegen/drop.rs b/src/test/codegen/drop.rs index 0c7f3bb2020a9..99a791464ab89 100644 --- a/src/test/codegen/drop.rs +++ b/src/test/codegen/drop.rs @@ -23,13 +23,13 @@ pub fn droppy() { // FIXME(eddyb) the `void @` forces a match on the instruction, instead of the // comment, that's `; call core::intrinsics::drop_in_place::` // for the `v0` mangling, should switch to matching on that once `legacy` is gone. +// CHECK-NOT: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName // CHECK-NOT: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName // CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName // CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName -// CHECK-NOT: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName -// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName // CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName -// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName // CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName // CHECK-NOT: {{(call|invoke) void @.*}}drop_in_place{{.*}}SomeUniqueName // The next line checks for the } that ends the function definition diff --git a/src/test/codegen/enum-debug-clike.rs b/src/test/codegen/enum-debug-clike.rs index f268c8bcbccdb..134443931e986 100644 --- a/src/test/codegen/enum-debug-clike.rs +++ b/src/test/codegen/enum-debug-clike.rs @@ -1,18 +1,13 @@ -// This test depends on a patch that was committed to upstream LLVM -// before 7.0, then backported to the Rust LLVM fork. It tests that -// debug info for "c-like" enums is properly emitted. +// This tests that debug info for "c-like" enums is properly emitted. +// This is ignored for the fallback mode on MSVC due to problems with PDB. // ignore-tidy-linelength -// ignore-windows -// min-system-llvm-version 8.0 +// ignore-msvc // compile-flags: -g -C no-prepopulate-passes -// DIFlagFixedEnum was deprecated in 8.0, renamed to DIFlagEnumClass. -// We match either for compatibility. - // CHECK-LABEL: @main -// CHECK: {{.*}}DICompositeType{{.*}}tag: DW_TAG_enumeration_type,{{.*}}name: "E",{{.*}}flags: {{(DIFlagEnumClass|DIFlagFixedEnum)}},{{.*}} +// CHECK: {{.*}}DICompositeType{{.*}}tag: DW_TAG_enumeration_type,{{.*}}name: "E",{{.*}}flags: DIFlagEnumClass,{{.*}} // CHECK: {{.*}}DIEnumerator{{.*}}name: "A",{{.*}}value: {{[0-9].*}} // CHECK: {{.*}}DIEnumerator{{.*}}name: "B",{{.*}}value: {{[0-9].*}} // CHECK: {{.*}}DIEnumerator{{.*}}name: "C",{{.*}}value: {{[0-9].*}} diff --git a/src/test/codegen/enum-debug-niche-2.rs b/src/test/codegen/enum-debug-niche-2.rs index 0f17976ef4965..0f78234d9774d 100644 --- a/src/test/codegen/enum-debug-niche-2.rs +++ b/src/test/codegen/enum-debug-niche-2.rs @@ -1,10 +1,8 @@ -// This test depends on a patch that was committed to upstream LLVM -// before 7.0, then backported to the Rust LLVM fork. It tests that -// optimized enum debug info accurately reflects the enum layout. +// This tests that optimized enum debug info accurately reflects the enum layout. +// This is ignored for the fallback mode on MSVC due to problems with PDB. // ignore-tidy-linelength -// ignore-windows -// min-system-llvm-version 8.0 +// ignore-msvc // compile-flags: -g -C no-prepopulate-passes diff --git a/src/test/codegen/enum-debug-niche.rs b/src/test/codegen/enum-debug-niche.rs index 2272488375fdd..b718a6854dd7e 100644 --- a/src/test/codegen/enum-debug-niche.rs +++ b/src/test/codegen/enum-debug-niche.rs @@ -1,9 +1,7 @@ -// This test depends on a patch that was committed to upstream LLVM -// before 7.0, then backported to the Rust LLVM fork. It tests that -// optimized enum debug info accurately reflects the enum layout. +// This tests that optimized enum debug info accurately reflects the enum layout. +// This is ignored for the fallback mode on MSVC due to problems with PDB. -// ignore-windows -// min-system-llvm-version 8.0 +// ignore-msvc // compile-flags: -g -C no-prepopulate-passes diff --git a/src/test/codegen/enum-debug-tagged.rs b/src/test/codegen/enum-debug-tagged.rs index 3539aae42eae7..095c49ac3acb9 100644 --- a/src/test/codegen/enum-debug-tagged.rs +++ b/src/test/codegen/enum-debug-tagged.rs @@ -1,9 +1,7 @@ -// This test depends on a patch that was committed to upstream LLVM -// before 7.0, then backported to the Rust LLVM fork. It tests that -// debug info for tagged (ordinary) enums is properly emitted. +// This tests that debug info for tagged (ordinary) enums is properly emitted. +// This is ignored for the fallback mode on MSVC due to problems with PDB. -// ignore-windows -// min-system-llvm-version 8.0 +// ignore-msvc // compile-flags: -g -C no-prepopulate-passes diff --git a/src/test/codegen/ffi-const.rs b/src/test/codegen/ffi-const.rs new file mode 100644 index 0000000000000..440d022a12cba --- /dev/null +++ b/src/test/codegen/ffi-const.rs @@ -0,0 +1,12 @@ +// compile-flags: -C no-prepopulate-passes +#![crate_type = "lib"] +#![feature(ffi_const)] + +pub fn bar() { unsafe { foo() } } + +extern { + // CHECK-LABEL: declare void @foo() + // CHECK-SAME: [[ATTRS:#[0-9]+]] + // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}readnone{{.*}} } + #[ffi_const] pub fn foo(); +} diff --git a/src/test/codegen/ffi-out-of-bounds-loads.rs b/src/test/codegen/ffi-out-of-bounds-loads.rs new file mode 100644 index 0000000000000..139a06ab53d05 --- /dev/null +++ b/src/test/codegen/ffi-out-of-bounds-loads.rs @@ -0,0 +1,25 @@ +// Regression test for #29988 + +// compile-flags: -C no-prepopulate-passes +// only-x86_64 +// ignore-windows + +#[repr(C)] +struct S { + f1: i32, + f2: i32, + f3: i32, +} + +extern { + fn foo(s: S); +} + +fn main() { + let s = S { f1: 1, f2: 2, f3: 3 }; + unsafe { + // CHECK: load { i64, i32 }, { i64, i32 }* {{.*}}, align 4 + // CHECK: call void @foo({ i64, i32 } {{.*}}) + foo(s); + } +} diff --git a/src/test/codegen/ffi-pure.rs b/src/test/codegen/ffi-pure.rs new file mode 100644 index 0000000000000..f0ebc1caa09bd --- /dev/null +++ b/src/test/codegen/ffi-pure.rs @@ -0,0 +1,12 @@ +// compile-flags: -C no-prepopulate-passes +#![crate_type = "lib"] +#![feature(ffi_pure)] + +pub fn bar() { unsafe { foo() } } + +extern { + // CHECK-LABEL: declare void @foo() + // CHECK-SAME: [[ATTRS:#[0-9]+]] + // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}readonly{{.*}} } + #[ffi_pure] pub fn foo(); +} diff --git a/src/test/codegen/force-frame-pointers.rs b/src/test/codegen/force-frame-pointers.rs index 4c94a601f33fd..637c4234654b4 100644 --- a/src/test/codegen/force-frame-pointers.rs +++ b/src/test/codegen/force-frame-pointers.rs @@ -1,4 +1,3 @@ -// min-llvm-version 8.0 // compile-flags: -C no-prepopulate-passes -C force-frame-pointers=y #![crate_type="lib"] diff --git a/src/test/codegen/force-unwind-tables.rs b/src/test/codegen/force-unwind-tables.rs new file mode 100644 index 0000000000000..fbaf38d69df7f --- /dev/null +++ b/src/test/codegen/force-unwind-tables.rs @@ -0,0 +1,7 @@ +// min-llvm-version 8.0 +// compile-flags: -C no-prepopulate-passes -C force-unwind-tables=y + +#![crate_type="lib"] + +// CHECK: attributes #{{.*}} uwtable +pub fn foo() {} diff --git a/src/test/codegen/instrument-mcount.rs b/src/test/codegen/instrument-mcount.rs index e4e6d5ca2b850..518a2a0da2a84 100644 --- a/src/test/codegen/instrument-mcount.rs +++ b/src/test/codegen/instrument-mcount.rs @@ -1,4 +1,3 @@ -// min-llvm-version 8.0 // ignore-tidy-linelength // compile-flags: -Z instrument-mcount diff --git a/src/test/codegen/iter-fold-closure-no-dupes.rs b/src/test/codegen/iter-fold-closure-no-dupes.rs deleted file mode 100644 index ec58f7068abac..0000000000000 --- a/src/test/codegen/iter-fold-closure-no-dupes.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Check that fold closures aren't duplicated for each iterator type. -// compile-flags: -C opt-level=0 - -fn main() { - (0i32..10).by_ref().count(); - (0i32..=10).by_ref().count(); -} - -// `count` calls `fold`, which calls `try_fold` -- find the `fold` closure: -// CHECK: {{^define.*Iterator::fold::.*closure}} -// -// Only one closure is needed for both `count` calls, even from different -// monomorphized iterator types, as it's only generic over the item type. -// CHECK-NOT: {{^define.*Iterator::fold::.*closure}} diff --git a/src/test/codegen/iter-fold-closure-no-iterator.rs b/src/test/codegen/iter-fold-closure-no-iterator.rs deleted file mode 100644 index fbeafd5f39582..0000000000000 --- a/src/test/codegen/iter-fold-closure-no-iterator.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Check that fold closures aren't generic in the iterator type. -// compile-flags: -C opt-level=0 - -fn main() { - (0i32..10).by_ref().count(); -} - -// `count` calls `fold`, which calls `try_fold` -- that `fold` closure should -// not be generic in the iterator type, only in the item type. -// CHECK-NOT: {{^define.*Iterator::fold::.*closure.*Range}} diff --git a/src/test/codegen/no-output-asm-is-volatile.rs b/src/test/codegen/no-output-asm-is-volatile.rs index 47b38d2941742..40376218908d1 100644 --- a/src/test/codegen/no-output-asm-is-volatile.rs +++ b/src/test/codegen/no-output-asm-is-volatile.rs @@ -1,6 +1,6 @@ // compile-flags: -O -#![feature(asm)] +#![feature(llvm_asm)] #![crate_type = "lib"] // Check that inline assembly expressions without any outputs @@ -9,6 +9,6 @@ // CHECK-LABEL: @assembly #[no_mangle] pub fn assembly() { - unsafe { asm!("") } + unsafe { llvm_asm!("") } // CHECK: tail call void asm sideeffect "", {{.*}} } diff --git a/src/test/codegen/nrvo.rs b/src/test/codegen/nrvo.rs new file mode 100644 index 0000000000000..fddb0d1fb3c8c --- /dev/null +++ b/src/test/codegen/nrvo.rs @@ -0,0 +1,17 @@ +// compile-flags: -O + +#![crate_type = "lib"] + +// Ensure that we do not call `memcpy` for the following function. +// `memset` and `init` should be called directly on the return pointer. +#[no_mangle] +pub fn nrvo(init: fn(&mut [u8; 4096])) -> [u8; 4096] { + // CHECK-LABEL: nrvo + // CHECK: @llvm.memset + // CHECK-NOT: @llvm.memcpy + // CHECK: ret + // CHECK-EMPTY + let mut buf = [0; 4096]; + init(&mut buf); + buf +} diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index 4724dc3c3e593..20475bab0fc92 100644 --- a/src/test/codegen/remap_path_prefix/main.rs +++ b/src/test/codegen/remap_path_prefix/main.rs @@ -22,7 +22,7 @@ fn main() { } // Here we check that local debuginfo is mapped correctly. -// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd/") +// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd/" // And here that debuginfo from other crates are expanded to absolute paths. -// CHECK: !DIFile(filename: "/the/aux-src/remap_path_prefix_aux.rs", directory: "") +// CHECK: !DIFile(filename: "/the/aux-src/remap_path_prefix_aux.rs", directory: "" diff --git a/src/test/codegen/remap_path_prefix/xcrate-generic.rs b/src/test/codegen/remap_path_prefix/xcrate-generic.rs index 30d6112fd02f6..7a9d2ca9b6bbd 100644 --- a/src/test/codegen/remap_path_prefix/xcrate-generic.rs +++ b/src/test/codegen/remap_path_prefix/xcrate-generic.rs @@ -11,4 +11,4 @@ pub fn foo() { } // Here we check that local debuginfo is mapped correctly. -// CHECK: !DIFile(filename: "/the/aux-src/xcrate-generic.rs", directory: "") +// CHECK: !DIFile(filename: "/the/aux-src/xcrate-generic.rs", directory: "" diff --git a/src/test/codegen/src-hash-algorithm/src-hash-algorithm-md5.rs b/src/test/codegen/src-hash-algorithm/src-hash-algorithm-md5.rs new file mode 100644 index 0000000000000..64be1127786f1 --- /dev/null +++ b/src/test/codegen/src-hash-algorithm/src-hash-algorithm-md5.rs @@ -0,0 +1,6 @@ +// compile-flags: -g -Z src-hash-algorithm=md5 + +#![crate_type = "lib"] + +pub fn test() {} +// CHECK: checksumkind: CSK_MD5 diff --git a/src/test/codegen/src-hash-algorithm/src-hash-algorithm-sha1.rs b/src/test/codegen/src-hash-algorithm/src-hash-algorithm-sha1.rs new file mode 100644 index 0000000000000..54e07152142ec --- /dev/null +++ b/src/test/codegen/src-hash-algorithm/src-hash-algorithm-sha1.rs @@ -0,0 +1,6 @@ +// compile-flags: -g -Z src-hash-algorithm=sha1 + +#![crate_type = "lib"] + +pub fn test() {} +// CHECK: checksumkind: CSK_SHA1 diff --git a/src/test/codegen/target-feature-multiple.rs b/src/test/codegen/target-feature-multiple.rs new file mode 100644 index 0000000000000..f71a9c3c58216 --- /dev/null +++ b/src/test/codegen/target-feature-multiple.rs @@ -0,0 +1,9 @@ +// only-x86_64 +// compile-flags: -C target-feature=+sse2,-avx,+avx2 -C target-feature=+avx,-avx2 + +#![crate_type = "lib"] + +#[no_mangle] +pub fn foo() { + // CHECK: attributes #0 = { {{.*}}"target-features"="+sse2,-avx,+avx2,+avx,-avx2"{{.*}} } +} diff --git a/src/test/codegen/unchecked-float-casts.rs b/src/test/codegen/unchecked-float-casts.rs index 34e9612222309..789feea12d6d7 100644 --- a/src/test/codegen/unchecked-float-casts.rs +++ b/src/test/codegen/unchecked-float-casts.rs @@ -1,7 +1,7 @@ -// compile-flags: -C no-prepopulate-passes +// This file tests that we don't generate any code for saturation when using the +// unchecked intrinsics. -// This file tests that we don't generate any code for saturation if -// -Z saturating-float-casts is not enabled. +// compile-flags: -C opt-level=3 #![crate_type = "lib"] @@ -12,7 +12,7 @@ pub fn f32_to_u32(x: f32) -> u32 { // CHECK-NOT: fcmp // CHECK-NOT: icmp // CHECK-NOT: select - x as u32 + unsafe { x.to_int_unchecked() } } // CHECK-LABEL: @f32_to_i32 @@ -22,7 +22,7 @@ pub fn f32_to_i32(x: f32) -> i32 { // CHECK-NOT: fcmp // CHECK-NOT: icmp // CHECK-NOT: select - x as i32 + unsafe { x.to_int_unchecked() } } #[no_mangle] @@ -31,5 +31,5 @@ pub fn f64_to_u16(x: f64) -> u16 { // CHECK-NOT: fcmp // CHECK-NOT: icmp // CHECK-NOT: select - x as u16 + unsafe { x.to_int_unchecked() } } diff --git a/src/test/compile-fail/asm-src-loc-codegen-units.rs b/src/test/compile-fail/asm-src-loc-codegen-units.rs index 798eb32181ce7..c9415aed930d5 100644 --- a/src/test/compile-fail/asm-src-loc-codegen-units.rs +++ b/src/test/compile-fail/asm-src-loc-codegen-units.rs @@ -3,10 +3,10 @@ // compile-flags: -C codegen-units=2 // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] fn main() { unsafe { - asm!("nowayisthisavalidinstruction"); //~ ERROR instruction + llvm_asm!("nowayisthisavalidinstruction"); //~ ERROR instruction } } diff --git a/src/test/compile-fail/asm-src-loc.rs b/src/test/compile-fail/asm-src-loc.rs index 0b60256e7fd09..7c87f370d4f68 100644 --- a/src/test/compile-fail/asm-src-loc.rs +++ b/src/test/compile-fail/asm-src-loc.rs @@ -1,9 +1,9 @@ // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] fn main() { unsafe { - asm!("nowayisthisavalidinstruction"); //~ ERROR instruction + llvm_asm!("nowayisthisavalidinstruction"); //~ ERROR instruction } } diff --git a/src/test/compile-fail/consts/const-fn-error.rs b/src/test/compile-fail/consts/const-fn-error.rs index 9db595af63efb..7dbf7d1a38691 100644 --- a/src/test/compile-fail/consts/const-fn-error.rs +++ b/src/test/compile-fail/consts/const-fn-error.rs @@ -10,7 +10,6 @@ const fn f(x: usize) -> usize { //~| ERROR E0658 //~| ERROR E0080 //~| ERROR E0744 - //~| ERROR E0019 sum += i; } sum diff --git a/src/test/compile-fail/issue-52443.rs b/src/test/compile-fail/issue-52443.rs index 3a022230b39a7..9761959335273 100644 --- a/src/test/compile-fail/issue-52443.rs +++ b/src/test/compile-fail/issue-52443.rs @@ -8,4 +8,8 @@ fn main() { //~| WARN denote infinite loops with [(); { for _ in 0usize.. {}; 0}]; //~^ ERROR `for` is not allowed in a `const` + //~| ERROR calls in constants are limited to constant functions + //~| ERROR references in constants may only refer to immutable values + //~| ERROR calls in constants are limited to constant functions + //~| ERROR evaluation of constant value failed } diff --git a/src/test/compile-fail/unwind-tables-panic-required.rs b/src/test/compile-fail/unwind-tables-panic-required.rs new file mode 100644 index 0000000000000..314d9e778d5ae --- /dev/null +++ b/src/test/compile-fail/unwind-tables-panic-required.rs @@ -0,0 +1,10 @@ +// Tests that the compiler errors if the user tries to turn off unwind tables +// when they are required. +// +// compile-flags: -C panic=unwind -C force-unwind-tables=no +// ignore-tidy-linelength +// +// error-pattern: panic=unwind requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`. + +pub fn main() { +} diff --git a/src/test/compile-fail/unwind-tables-target-required.rs b/src/test/compile-fail/unwind-tables-target-required.rs new file mode 100644 index 0000000000000..14c1789376414 --- /dev/null +++ b/src/test/compile-fail/unwind-tables-target-required.rs @@ -0,0 +1,11 @@ +// Tests that the compiler errors if the user tries to turn off unwind tables +// when they are required. +// +// only-x86_64-windows-msvc +// compile-flags: -C force-unwind-tables=no +// ignore-tidy-linelength +// +// error-pattern: target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`. + +pub fn main() { +} diff --git a/src/test/debuginfo/borrowed-enum.rs b/src/test/debuginfo/borrowed-enum.rs index 63c11f59c157d..85e11c10c688f 100644 --- a/src/test/debuginfo/borrowed-enum.rs +++ b/src/test/debuginfo/borrowed-enum.rs @@ -1,7 +1,6 @@ // ignore-tidy-linelength -// Require LLVM with DW_TAG_variant_part and a gdb or lldb that can read it. -// min-system-llvm-version: 8.0 +// Require a gdb or lldb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // rust-lldb diff --git a/src/test/debuginfo/enum-thinlto.rs b/src/test/debuginfo/enum-thinlto.rs index 13577b0587ff0..9359e55dceed3 100644 --- a/src/test/debuginfo/enum-thinlto.rs +++ b/src/test/debuginfo/enum-thinlto.rs @@ -1,5 +1,4 @@ -// Require LLVM with DW_TAG_variant_part and a gdb that can read it. -// min-system-llvm-version: 8.0 +// Require a gdb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // compile-flags:-g -Z thinlto diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index f19a3c71dd8d2..382b0231d3bd2 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -1,7 +1,6 @@ // ignore-tidy-linelength -// Require LLVM with DW_TAG_variant_part and a gdb that can read it. -// min-system-llvm-version: 8.0 +// Require a gdb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // compile-flags:-g diff --git a/src/test/debuginfo/generic-enum-with-different-disr-sizes.rs b/src/test/debuginfo/generic-enum-with-different-disr-sizes.rs index 72d38a6f04544..adcb04da30d06 100644 --- a/src/test/debuginfo/generic-enum-with-different-disr-sizes.rs +++ b/src/test/debuginfo/generic-enum-with-different-disr-sizes.rs @@ -1,8 +1,7 @@ // ignore-lldb: FIXME(#27089) // min-lldb-version: 310 -// Require LLVM with DW_TAG_variant_part and a gdb that can read it. -// min-system-llvm-version: 8.0 +// Require a gdb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // compile-flags:-g diff --git a/src/test/debuginfo/generic-function.rs b/src/test/debuginfo/generic-function.rs index 96f2aa3acf29b..f5e34c3911904 100644 --- a/src/test/debuginfo/generic-function.rs +++ b/src/test/debuginfo/generic-function.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // min-lldb-version: 310 // compile-flags:-g @@ -12,31 +10,21 @@ // gdb-check:$1 = 1 // gdb-command:print *t1 // gdb-check:$2 = 2.5 -// gdb-command:print ret -// gdbg-check:$3 = {__0 = {__0 = 1, __1 = 2.5}, __1 = {__0 = 2.5, __1 = 1}} -// gdbr-check:$3 = ((1, 2.5), (2.5, 1)) // gdb-command:continue // gdb-command:print *t0 -// gdb-check:$4 = 3.5 +// gdb-check:$3 = 3.5 // gdb-command:print *t1 -// gdb-check:$5 = 4 -// gdb-command:print ret -// gdbg-check:$6 = {__0 = {__0 = 3.5, __1 = 4}, __1 = {__0 = 4, __1 = 3.5}} -// gdbr-check:$6 = ((3.5, 4), (4, 3.5)) +// gdb-check:$4 = 4 // gdb-command:continue // gdb-command:print *t0 -// gdb-check:$7 = 5 +// gdb-check:$5 = 5 // gdb-command:print *t1 -// gdbg-check:$8 = {a = 6, b = 7.5} -// gdbr-check:$8 = generic_function::Struct {a: 6, b: 7.5} -// gdb-command:print ret -// gdbg-check:$9 = {__0 = {__0 = 5, __1 = {a = 6, b = 7.5}}, __1 = {__0 = {a = 6, b = 7.5}, __1 = 5}} -// gdbr-check:$9 = ((5, generic_function::Struct {a: 6, b: 7.5}), (generic_function::Struct {a: 6, b: 7.5}, 5)) +// gdbg-check:$6 = {a = 6, b = 7.5} +// gdbr-check:$6 = generic_function::Struct {a: 6, b: 7.5} // gdb-command:continue - // === LLDB TESTS ================================================================================== // lldb-command:run @@ -47,31 +35,22 @@ // lldb-command:print *t1 // lldbg-check:[...]$1 = 2.5 // lldbr-check:(f64) *t1 = 2.5 -// lldb-command:print ret -// lldbg-check:[...]$2 = ((1, 2.5), (2.5, 1)) -// lldbr-check:(((i32, f64), (f64, i32))) ret = { = { = 1 = 2.5 } = { = 2.5 = 1 } } // lldb-command:continue // lldb-command:print *t0 -// lldbg-check:[...]$3 = 3.5 +// lldbg-check:[...]$2 = 3.5 // lldbr-check:(f64) *t0 = 3.5 // lldb-command:print *t1 -// lldbg-check:[...]$4 = 4 +// lldbg-check:[...]$3 = 4 // lldbr-check:(u16) *t1 = 4 -// lldb-command:print ret -// lldbg-check:[...]$5 = ((3.5, 4), (4, 3.5)) -// lldbr-check:(((f64, u16), (u16, f64))) ret = { = { = 3.5 = 4 } = { = 4 = 3.5 } } // lldb-command:continue // lldb-command:print *t0 -// lldbg-check:[...]$6 = 5 +// lldbg-check:[...]$4 = 5 // lldbr-check:(i32) *t0 = 5 // lldb-command:print *t1 -// lldbg-check:[...]$7 = Struct { a: 6, b: 7.5 } +// lldbg-check:[...]$5 = Struct { a: 6, b: 7.5 } // lldbr-check:(generic_function::Struct) *t1 = Struct { a: 6, b: 7.5 } -// lldb-command:print ret -// lldbg-check:[...]$8 = ((5, Struct { a: 6, b: 7.5 }), (Struct { a: 6, b: 7.5 }, 5)) -// lldbr-check:(((i32, generic_function::Struct), (generic_function::Struct, i32))) ret = { = { = 5 = Struct { a: 6, b: 7.5 } } = { = Struct { a: 6, b: 7.5 } = 5 } } // lldb-command:continue #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/generic-struct-style-enum.rs b/src/test/debuginfo/generic-struct-style-enum.rs index 3dc5cb807b452..678ca8df04068 100644 --- a/src/test/debuginfo/generic-struct-style-enum.rs +++ b/src/test/debuginfo/generic-struct-style-enum.rs @@ -1,8 +1,7 @@ // ignore-tidy-linelength // min-lldb-version: 310 -// Require LLVM with DW_TAG_variant_part and a gdb that can read it. -// min-system-llvm-version: 8.0 +// Require a gdb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // compile-flags:-g diff --git a/src/test/debuginfo/generic-tuple-style-enum.rs b/src/test/debuginfo/generic-tuple-style-enum.rs index b16634ee6d7f9..89aa78a6e1049 100644 --- a/src/test/debuginfo/generic-tuple-style-enum.rs +++ b/src/test/debuginfo/generic-tuple-style-enum.rs @@ -1,8 +1,6 @@ // ignore-tidy-linelength -// Require LLVM with DW_TAG_variant_part and a gdb and lldb that can -// read it. -// min-system-llvm-version: 8.0 +// Require a gdb or lldb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // rust-lldb diff --git a/src/test/debuginfo/issue-57822.rs b/src/test/debuginfo/issue-57822.rs index 4de88e9dae62b..e9cfa3f7d69f7 100644 --- a/src/test/debuginfo/issue-57822.rs +++ b/src/test/debuginfo/issue-57822.rs @@ -1,8 +1,7 @@ // This test makes sure that the LLDB pretty printer does not throw an exception // for nested closures and generators. -// Require LLVM with DW_TAG_variant_part and a gdb that can read it. -// min-system-llvm-version: 8.0 +// Require a gdb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // ignore-tidy-linelength diff --git a/src/test/debuginfo/pretty-std-collections.rs b/src/test/debuginfo/pretty-std-collections.rs index 1da2042b2224a..1f4ff00100a23 100644 --- a/src/test/debuginfo/pretty-std-collections.rs +++ b/src/test/debuginfo/pretty-std-collections.rs @@ -26,14 +26,19 @@ // gdb-command: print empty_btree_map // gdb-check:$4 = BTreeMap(len: 0) +// gdb-command: print option_btree_map +// gdb-check:$5 = BTreeMap>(len: 2) = {[false] = [...], [true] = [...]} +// (abbreviated because both values vary wildly over gdb versions and/or linux distributions) + // gdb-command: print nasty_btree_map -// gdb-check:$5 = BTreeMap(len: 1) = {[1] = pretty_std_collections::MyLeafNode (11)} +// gdb-check:$6 = BTreeMap(len: 15) = {[0] = pretty_std_collections::MyLeafNode (0), [...]} +// (abbreviated because it's boring but we need enough elements to include internal nodes) // gdb-command: print vec_deque -// gdb-check:$6 = VecDeque(len: 3, cap: 8) = {5, 3, 7} +// gdb-check:$7 = VecDeque(len: 3, cap: 8) = {5, 3, 7} // gdb-command: print vec_deque2 -// gdb-check:$7 = VecDeque(len: 7, cap: 8) = {2, 3, 4, 5, 6, 7, 8} +// gdb-check:$8 = VecDeque(len: 7, cap: 8) = {2, 3, 4, 5, 6, 7, 8} #![allow(unused_variables)] use std::collections::BTreeMap; @@ -59,8 +64,14 @@ fn main() { let mut empty_btree_map: BTreeMap = BTreeMap::new(); + let mut option_btree_map: BTreeMap> = BTreeMap::new(); + option_btree_map.insert(false, None); + option_btree_map.insert(true, Some(true)); + let mut nasty_btree_map: BTreeMap = BTreeMap::new(); - nasty_btree_map.insert(1, MyLeafNode(11)); + for i in 0..15 { + nasty_btree_map.insert(i, MyLeafNode(i)); + } // VecDeque let mut vec_deque = VecDeque::new(); diff --git a/src/test/debuginfo/recursive-struct.rs b/src/test/debuginfo/recursive-struct.rs index 4f75ef4fa9b9a..c0bd67367012f 100644 --- a/src/test/debuginfo/recursive-struct.rs +++ b/src/test/debuginfo/recursive-struct.rs @@ -1,7 +1,6 @@ // ignore-lldb -// Require LLVM with DW_TAG_variant_part and a gdb that can read it. -// min-system-llvm-version: 8.0 +// Require a gdb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // compile-flags:-g diff --git a/src/test/debuginfo/struct-style-enum.rs b/src/test/debuginfo/struct-style-enum.rs index 5843b076b1f46..34f75a4e3045b 100644 --- a/src/test/debuginfo/struct-style-enum.rs +++ b/src/test/debuginfo/struct-style-enum.rs @@ -1,8 +1,6 @@ // ignore-tidy-linelength -// Require LLVM with DW_TAG_variant_part and a gdb and lldb that can -// read it. -// min-system-llvm-version: 8.0 +// Require a gdb or lldb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // rust-lldb diff --git a/src/test/debuginfo/tuple-style-enum.rs b/src/test/debuginfo/tuple-style-enum.rs index 4d9727a388b8e..87b0bc6294dd9 100644 --- a/src/test/debuginfo/tuple-style-enum.rs +++ b/src/test/debuginfo/tuple-style-enum.rs @@ -1,8 +1,6 @@ // ignore-tidy-linelength -// Require LLVM with DW_TAG_variant_part and a gdb and lldb that can -// read it. -// min-system-llvm-version: 8.0 +// Require a gdb or lldb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // rust-lldb diff --git a/src/test/debuginfo/unique-enum.rs b/src/test/debuginfo/unique-enum.rs index c440ce059f721..9d938b6e36919 100644 --- a/src/test/debuginfo/unique-enum.rs +++ b/src/test/debuginfo/unique-enum.rs @@ -1,6 +1,4 @@ -// Require LLVM with DW_TAG_variant_part and a gdb and lldb that can -// read it. -// min-system-llvm-version: 8.0 +// Require a gdb or lldb that can read DW_TAG_variant_part. // min-gdb-version: 8.2 // rust-lldb diff --git a/src/test/incremental/const-generics/issue-61516.rs b/src/test/incremental/const-generics/issue-61516.rs index a7465b77267a5..a193bf998dc73 100644 --- a/src/test/incremental/const-generics/issue-61516.rs +++ b/src/test/incremental/const-generics/issue-61516.rs @@ -4,7 +4,7 @@ struct FakeArray(T); -impl FakeArray { +impl FakeArray { fn len(&self) -> usize { N } diff --git a/src/test/incremental/const-generics/issue-62536.rs b/src/test/incremental/const-generics/issue-62536.rs index 90e279bfc7433..0eaeb910be64a 100644 --- a/src/test/incremental/const-generics/issue-62536.rs +++ b/src/test/incremental/const-generics/issue-62536.rs @@ -1,6 +1,6 @@ // revisions:cfail1 #![feature(const_generics)] -//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//[cfail1]~^ WARN the feature `const_generics` is incomplete struct S([T; N]); diff --git a/src/test/incremental/const-generics/issue-64087.rs b/src/test/incremental/const-generics/issue-64087.rs index b3c12fbb6e813..6b10c5404944d 100644 --- a/src/test/incremental/const-generics/issue-64087.rs +++ b/src/test/incremental/const-generics/issue-64087.rs @@ -1,6 +1,6 @@ // revisions:cfail1 #![feature(const_generics)] -//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//[cfail1]~^ WARN the feature `const_generics` is incomplete fn combinator() -> [T; S] {} //[cfail1]~^ ERROR mismatched types diff --git a/src/test/incremental/hashes/enum_constructors.rs b/src/test/incremental/hashes/enum_constructors.rs index 99c50f7e17356..2c07cbcb2054b 100644 --- a/src/test/incremental/hashes/enum_constructors.rs +++ b/src/test/incremental/hashes/enum_constructors.rs @@ -274,14 +274,14 @@ pub enum Clike2 { // Change constructor path (C-like) -------------------------------------- #[cfg(cfail1)] pub fn change_constructor_path_c_like() { - let _ = Clike::B; + let _x = Clike::B; } #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_c_like() { - let _ = Clike2::B; + let _x = Clike2::B; } @@ -289,14 +289,14 @@ pub fn change_constructor_path_c_like() { // Change constructor variant (C-like) -------------------------------------- #[cfg(cfail1)] pub fn change_constructor_variant_c_like() { - let _ = Clike::A; + let _x = Clike::A; } #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_c_like() { - let _ = Clike::C; + let _x = Clike::C; } diff --git a/src/test/incremental/hashes/inline_asm.rs b/src/test/incremental/hashes/inline_asm.rs index fb3c6378f74ab..3eaffc440615f 100644 --- a/src/test/incremental/hashes/inline_asm.rs +++ b/src/test/incremental/hashes/inline_asm.rs @@ -11,7 +11,7 @@ #![allow(warnings)] #![feature(rustc_attrs)] -#![feature(asm)] +#![feature(llvm_asm)] #![crate_type="rlib"] @@ -22,12 +22,12 @@ pub fn change_template(a: i32) -> i32 { let c: i32; unsafe { - asm!("add 1, $0" - : "=r"(c) - : "0"(a) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(c) + : "0"(a) + : + : + ); } c } @@ -39,12 +39,12 @@ pub fn change_template(a: i32) -> i32 { pub fn change_template(a: i32) -> i32 { let c: i32; unsafe { - asm!("add 2, $0" - : "=r"(c) - : "0"(a) - : - : - ); + llvm_asm!("add 2, $0" + : "=r"(c) + : "0"(a) + : + : + ); } c } @@ -58,12 +58,12 @@ pub fn change_output(a: i32) -> i32 { let mut _out1: i32 = 0; let mut _out2: i32 = 0; unsafe { - asm!("add 1, $0" - : "=r"(_out1) - : "0"(a) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out1) + : "0"(a) + : + : + ); } _out1 } @@ -76,12 +76,12 @@ pub fn change_output(a: i32) -> i32 { let mut _out1: i32 = 0; let mut _out2: i32 = 0; unsafe { - asm!("add 1, $0" - : "=r"(_out2) - : "0"(a) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out2) + : "0"(a) + : + : + ); } _out1 } @@ -94,12 +94,12 @@ pub fn change_output(a: i32) -> i32 { pub fn change_input(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "0"(_a) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : + ); } _out } @@ -111,12 +111,12 @@ pub fn change_input(_a: i32, _b: i32) -> i32 { pub fn change_input(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "0"(_b) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_b) + : + : + ); } _out } @@ -129,12 +129,12 @@ pub fn change_input(_a: i32, _b: i32) -> i32 { pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "0"(_a), "r"(_b) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a), "r"(_b) + : + : + ); } _out } @@ -146,12 +146,12 @@ pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "r"(_a), "0"(_b) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "r"(_a), "0"(_b) + : + : + ); } _out } @@ -164,12 +164,12 @@ pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { pub fn change_clobber(_a: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "0"(_a) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : + ); } _out } @@ -181,12 +181,12 @@ pub fn change_clobber(_a: i32) -> i32 { pub fn change_clobber(_a: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "0"(_a) - : "eax" - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : "eax" + : + ); } _out } @@ -199,12 +199,12 @@ pub fn change_clobber(_a: i32) -> i32 { pub fn change_options(_a: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "0"(_a) - : - : - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : + ); } _out } @@ -216,12 +216,12 @@ pub fn change_options(_a: i32) -> i32 { pub fn change_options(_a: i32) -> i32 { let _out; unsafe { - asm!("add 1, $0" - : "=r"(_out) - : "0"(_a) - : - : "volatile" - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : "volatile" + ); } _out } diff --git a/src/test/incremental/lto-in-linker.rs b/src/test/incremental/lto-in-linker.rs new file mode 100644 index 0000000000000..0e8c1ebb9198a --- /dev/null +++ b/src/test/incremental/lto-in-linker.rs @@ -0,0 +1,9 @@ +// revisions:cfail1 cfail2 +// compile-flags: -Z query-dep-graph --crate-type rlib -C linker-plugin-lto -O +// no-prefer-dynamic +// build-pass + +#![feature(rustc_attrs)] +#![rustc_partition_reused(module = "lto_in_linker", cfg = "cfail2")] + +pub fn foo() {} diff --git a/src/test/incremental/rlib-lto.rs b/src/test/incremental/rlib-lto.rs new file mode 100644 index 0000000000000..752fee5a0d5b6 --- /dev/null +++ b/src/test/incremental/rlib-lto.rs @@ -0,0 +1,8 @@ +// revisions:cfail1 cfail2 +// compile-flags: -Z query-dep-graph --crate-type rlib -C lto +// build-pass + +#![feature(rustc_attrs)] +#![rustc_partition_reused(module = "rlib_lto", cfg = "cfail2")] + +pub fn foo() {} diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs b/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs new file mode 100644 index 0000000000000..4d48a5f0ac528 --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs @@ -0,0 +1,26 @@ +// revisions: cfail1 cfail2 +// build-pass + +// rust-lang/rust#69798: +// +// This is analgous to cgu_invalidated_when_import_added, but it covers a +// problem uncovered where a change to the *export* set caused a link failure +// when reusing post-LTO optimized object code. + +pub struct Foo {} +impl Drop for Foo { + fn drop(&mut self) { + println!("Dropping Foo"); + } +} +#[no_mangle] +pub extern "C" fn run() { + thread_local! { pub static FOO : Foo = Foo { } ; } + + #[cfg(cfail2)] + { + FOO.with(|_f| ()) + } +} + +pub fn main() { run() } diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs b/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs new file mode 100644 index 0000000000000..e85b4856f3a96 --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs @@ -0,0 +1,26 @@ +// revisions: cfail1 cfail2 +// build-pass + +// rust-lang/rust#69798: +// +// This is analgous to cgu_invalidated_when_export_added, but it covers the +// other direction. This is analogous to cgu_invalidated_when_import_added: we +// include it, because it may uncover bugs in variant implementation strategies. + +pub struct Foo {} +impl Drop for Foo { + fn drop(&mut self) { + println!("Dropping Foo"); + } +} +#[no_mangle] +pub extern "C" fn run() { + thread_local! { pub static FOO : Foo = Foo { } ; } + + #[cfg(cfail1)] + { + FOO.with(|_f| ()) + } +} + +pub fn main() { run() } diff --git a/src/test/incremental/warnings-reemitted.rs b/src/test/incremental/warnings-reemitted.rs index 0eac2a1d57f8f..0e6b8823241c2 100644 --- a/src/test/incremental/warnings-reemitted.rs +++ b/src/test/incremental/warnings-reemitted.rs @@ -1,6 +1,6 @@ // revisions: cfail1 cfail2 cfail3 // compile-flags: -Coverflow-checks=on -// build-pass (FIXME(62277): could be check-pass?) +// build-pass #![warn(arithmetic_overflow)] diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md index ad4932b9fb945..a0550466cf07b 100644 --- a/src/test/mir-opt/README.md +++ b/src/test/mir-opt/README.md @@ -1,77 +1,39 @@ This folder contains tests for MIR optimizations. -The test format is: +The `mir-opt` test format emits MIR to extra files that you can automatically update by specifying +`--bless` on the command line (just like `ui` tests updating `.stderr` files). + +# `--bless`able test format + +By default 32 bit and 64 bit targets use the same dump files, which can be problematic in the +presence of pointers in constants or other bit width dependent things. In that case you can add ``` -(arbitrary rust code) -// END RUST SOURCE -// START $file_name_of_some_mir_dump_0 -// $expected_line_0 -// (lines or elision) -// $expected_line_N -// END $file_name_of_some_mir_dump_0 -// (lines or elision) -// START $file_name_of_some_mir_dump_N -// $expected_line_0 -// (lines or elision) -// $expected_line_N -// END $file_name_of_some_mir_dump_N +// EMIT_MIR_FOR_EACH_BIT_WIDTH ``` -All the test information is in comments so the test is runnable. +to your test, causing separate files to be generated for 32bit and 64bit systems. -For each $file_name, compiletest expects [$expected_line_0, ..., -$expected_line_N] to appear in the dumped MIR in order. Currently it allows -other non-matched lines before and after, but not between $expected_lines, -should you want to skip lines, you must include an elision comment, of the form -(as a regex) `//\s*...\s*`. The lines will be skipped lazily, that is, if there -are two identical lines in the output that match the line after the elision -comment, the first one will be matched. +## Emit a diff of the mir for a specific optimization -Examples: - -The following blocks will not match the one after it. +This is what you want most often when you want to see how an optimization changes the MIR. ``` -bb0: { - StorageLive(_1); - _1 = const true; - StorageDead(_1); -} +// EMIT_MIR $file_name_of_some_mir_dump.diff ``` -``` -bb0: { - StorageLive(_1); - _1 = const true; - goto -> bb1 -} -bb1: { - StorageDead(_1); - return; -} -``` +## Emit mir after a specific optimization -But this will match the one above, +Use this if you are just interested in the final state after an optimization. ``` -bb0: { - StorageLive(_1); - _1 = const true; - ... - StorageDead(_1); - ... -} +// EMIT_MIR $file_name_of_some_mir_dump.after.mir ``` -Lines match ignoring whitespace, and the prefix "//" is removed. +## Emit mir before a specific optimization -It also currently strips trailing comments -- partly because the full file path -in "scope comments" is unpredictable and partly because tidy complains about -the lines being too long. +This exists mainly for completeness and is rarely useful. -compiletest handles dumping the MIR before and after every pass for you. The -test writer only has to specify the file names of the dumped files (not the -full path to the file) and what lines to expect. There is an option to rustc -that tells it to dump the mir into some directly (rather then always dumping to -the current directory). +``` +// EMIT_MIR $file_name_of_some_mir_dump.before.mir +``` diff --git a/src/test/mir-opt/address-of.rs b/src/test/mir-opt/address-of.rs index bbd1ca68a8672..6cd14ccf434f2 100644 --- a/src/test/mir-opt/address-of.rs +++ b/src/test/mir-opt/address-of.rs @@ -1,3 +1,5 @@ +// EMIT_MIR rustc.address_of_reborrow.SimplifyCfg-initial.after.mir + fn address_of_reborrow() { let y = &[0; 10]; let mut z = &mut [0; 10]; @@ -35,6 +37,7 @@ fn address_of_reborrow() { } // The normal borrows here should be preserved +// EMIT_MIR rustc.borrow_and_cast.SimplifyCfg-initial.after.mir fn borrow_and_cast(mut x: i32) { let p = &x as *const i32; let q = &mut x as *const i32; @@ -42,71 +45,3 @@ fn borrow_and_cast(mut x: i32) { } fn main() {} - -// START rustc.address_of_reborrow.SimplifyCfg-initial.after.mir -// bb0: { -// ... -// _5 = &raw const (*_1); // & to *const casts -// ... -// _7 = &raw const (*_1); -// ... -// _11 = &raw const (*_1); -// ... -// _14 = &raw const (*_1); -// ... -// _16 = &raw const (*_1); -// ... -// _17 = &raw const (*_1); // & to *const coercions -// ... -// _18 = &raw const (*_1); -// ... -// _20 = &raw const (*_1); -// ... -// _22 = &raw const (*_1); -// ... -// _24 = &raw const (*_2); // &mut to *const casts -// ... -// _26 = &raw const (*_2); -// ... -// _30 = &raw const (*_2); -// ... -// _33 = &raw const (*_2); -// ... -// _34 = &raw const (*_2); // &mut to *const coercions -// ... -// _35 = &raw const (*_2); -// ... -// _37 = &raw const (*_2); -// ... -// _39 = &raw const (*_2); -// ... -// _41 = &raw mut (*_2); // &mut to *mut casts -// ... -// _43 = &raw mut (*_2); -// ... -// _47 = &raw mut (*_2); -// ... -// _50 = &raw mut (*_2); -// ... -// _51 = &raw mut (*_2); // &mut to *mut coercions -// ... -// _52 = &raw mut (*_2); -// ... -// _54 = &raw mut (*_2); -// ... -// _56 = &raw mut (*_2); -// ... -// } -// END rustc.address_of_reborrow.SimplifyCfg-initial.after.mir - -// START rustc.borrow_and_cast.EraseRegions.after.mir -// bb0: { -// ... -// _4 = &_1; -// ... -// _7 = &mut _1; -// ... -// _10 = &mut _1; -// ... -// } -// END rustc.borrow_and_cast.EraseRegions.after.mir diff --git a/src/test/mir-opt/address-of/rustc.address_of_reborrow.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address-of/rustc.address_of_reborrow.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..9742530bd812f --- /dev/null +++ b/src/test/mir-opt/address-of/rustc.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -0,0 +1,326 @@ +// MIR for `address_of_reborrow` after SimplifyCfg-initial + +| User Type Annotations +| 0: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:7:5: 7:18 +| 1: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:9:5: 9:25 +| 2: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:13:12: 13:20 +| 3: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:13:12: 13:20 +| 4: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:14:12: 14:28 +| 5: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:14:12: 14:28 +| 6: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:15:12: 15:27 +| 7: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:15:12: 15:27 +| 8: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:16:12: 16:24 +| 9: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:16:12: 16:24 +| 10: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:18:5: 18:18 +| 11: Canonical { max_universe: U3, variables: [CanonicalVarInfo { kind: Region(U3) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:20:5: 20:25 +| 12: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 +| 13: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 +| 14: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 +| 15: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 +| 16: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 +| 17: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 +| 18: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 +| 19: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 +| 20: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:28:5: 28:16 +| 21: Canonical { max_universe: U6, variables: [CanonicalVarInfo { kind: Region(U6) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:30:5: 30:23 +| 22: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 +| 23: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 +| 24: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 +| 25: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 +| 26: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 +| 27: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 +| 28: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 +| 29: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 +| +fn address_of_reborrow() -> () { + let mut _0: (); // return place in scope 0 at $DIR/address-of.rs:3:26: 3:26 + let _1: &[i32; 10]; // in scope 0 at $DIR/address-of.rs:4:9: 4:10 + let _2: [i32; 10]; // in scope 0 at $DIR/address-of.rs:4:14: 4:21 + let mut _4: [i32; 10]; // in scope 0 at $DIR/address-of.rs:5:22: 5:29 + let _5: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 + let mut _6: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 + let _7: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:8:5: 8:26 + let _8: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 + let mut _9: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 + let mut _10: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:9:5: 9:6 + let _11: *const [i32]; // in scope 0 at $DIR/address-of.rs:10:5: 10:22 + let mut _12: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:10:5: 10:6 + let _13: *const i32; // in scope 0 at $DIR/address-of.rs:11:5: 11:20 + let mut _14: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:11:5: 11:6 + let mut _18: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:15:30: 15:31 + let mut _20: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:16:27: 16:28 + let _21: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 + let mut _22: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 + let _23: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:19:5: 19:26 + let _24: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 + let mut _25: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 + let mut _26: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:20:5: 20:6 + let _27: *const [i32]; // in scope 0 at $DIR/address-of.rs:21:5: 21:22 + let mut _28: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:21:5: 21:6 + let mut _32: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:25:30: 25:31 + let mut _34: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:26:27: 26:28 + let _35: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 + let mut _36: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 + let _37: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:29:5: 29:24 + let _38: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 + let mut _39: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 + let mut _40: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:30:5: 30:6 + let _41: *mut [i32]; // in scope 0 at $DIR/address-of.rs:31:5: 31:20 + let mut _42: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:31:5: 31:6 + let mut _46: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:35:28: 35:29 + let mut _48: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:36:25: 36:26 + scope 1 { + debug y => _1; // in scope 1 at $DIR/address-of.rs:4:9: 4:10 + let mut _3: &mut [i32; 10]; // in scope 1 at $DIR/address-of.rs:5:9: 5:14 + scope 2 { + debug z => _3; // in scope 2 at $DIR/address-of.rs:5:9: 5:14 + let _15: *const [i32; 10] as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 2 at $DIR/address-of.rs:13:9: 13:10 + scope 3 { + debug p => _15; // in scope 3 at $DIR/address-of.rs:13:9: 13:10 + let _16: *const [i32; 10] as UserTypeProjection { base: UserType(4), projs: [] }; // in scope 3 at $DIR/address-of.rs:14:9: 14:10 + scope 4 { + debug p => _16; // in scope 4 at $DIR/address-of.rs:14:9: 14:10 + let _17: *const dyn std::marker::Send as UserTypeProjection { base: UserType(6), projs: [] }; // in scope 4 at $DIR/address-of.rs:15:9: 15:10 + scope 5 { + debug p => _17; // in scope 5 at $DIR/address-of.rs:15:9: 15:10 + let _19: *const [i32] as UserTypeProjection { base: UserType(8), projs: [] }; // in scope 5 at $DIR/address-of.rs:16:9: 16:10 + scope 6 { + debug p => _19; // in scope 6 at $DIR/address-of.rs:16:9: 16:10 + let _29: *const [i32; 10] as UserTypeProjection { base: UserType(12), projs: [] }; // in scope 6 at $DIR/address-of.rs:23:9: 23:10 + scope 7 { + debug p => _29; // in scope 7 at $DIR/address-of.rs:23:9: 23:10 + let _30: *const [i32; 10] as UserTypeProjection { base: UserType(14), projs: [] }; // in scope 7 at $DIR/address-of.rs:24:9: 24:10 + scope 8 { + debug p => _30; // in scope 8 at $DIR/address-of.rs:24:9: 24:10 + let _31: *const dyn std::marker::Send as UserTypeProjection { base: UserType(16), projs: [] }; // in scope 8 at $DIR/address-of.rs:25:9: 25:10 + scope 9 { + debug p => _31; // in scope 9 at $DIR/address-of.rs:25:9: 25:10 + let _33: *const [i32] as UserTypeProjection { base: UserType(18), projs: [] }; // in scope 9 at $DIR/address-of.rs:26:9: 26:10 + scope 10 { + debug p => _33; // in scope 10 at $DIR/address-of.rs:26:9: 26:10 + let _43: *mut [i32; 10] as UserTypeProjection { base: UserType(22), projs: [] }; // in scope 10 at $DIR/address-of.rs:33:9: 33:10 + scope 11 { + debug p => _43; // in scope 11 at $DIR/address-of.rs:33:9: 33:10 + let _44: *mut [i32; 10] as UserTypeProjection { base: UserType(24), projs: [] }; // in scope 11 at $DIR/address-of.rs:34:9: 34:10 + scope 12 { + debug p => _44; // in scope 12 at $DIR/address-of.rs:34:9: 34:10 + let _45: *mut dyn std::marker::Send as UserTypeProjection { base: UserType(26), projs: [] }; // in scope 12 at $DIR/address-of.rs:35:9: 35:10 + scope 13 { + debug p => _45; // in scope 13 at $DIR/address-of.rs:35:9: 35:10 + let _47: *mut [i32] as UserTypeProjection { base: UserType(28), projs: [] }; // in scope 13 at $DIR/address-of.rs:36:9: 36:10 + scope 14 { + debug p => _47; // in scope 14 at $DIR/address-of.rs:36:9: 36:10 + } + } + } + } + } + } + } + } + } + } + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/address-of.rs:4:9: 4:10 + StorageLive(_2); // scope 0 at $DIR/address-of.rs:4:14: 4:21 + _2 = [const 0i32; 10]; // scope 0 at $DIR/address-of.rs:4:14: 4:21 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/address-of.rs:4:15: 4:16 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + _1 = &_2; // scope 0 at $DIR/address-of.rs:4:13: 4:21 + FakeRead(ForLet, _1); // scope 0 at $DIR/address-of.rs:4:9: 4:10 + StorageLive(_3); // scope 1 at $DIR/address-of.rs:5:9: 5:14 + StorageLive(_4); // scope 1 at $DIR/address-of.rs:5:22: 5:29 + _4 = [const 0i32; 10]; // scope 1 at $DIR/address-of.rs:5:22: 5:29 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/address-of.rs:5:23: 5:24 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + _3 = &mut _4; // scope 1 at $DIR/address-of.rs:5:17: 5:29 + FakeRead(ForLet, _3); // scope 1 at $DIR/address-of.rs:5:9: 5:14 + StorageLive(_5); // scope 2 at $DIR/address-of.rs:7:5: 7:18 + StorageLive(_6); // scope 2 at $DIR/address-of.rs:7:5: 7:18 + _6 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:7:5: 7:6 + AscribeUserType(_6, o, UserTypeProjection { base: UserType(0), projs: [] }); // scope 2 at $DIR/address-of.rs:7:5: 7:18 + _5 = _6; // scope 2 at $DIR/address-of.rs:7:5: 7:18 + StorageDead(_6); // scope 2 at $DIR/address-of.rs:7:18: 7:19 + StorageDead(_5); // scope 2 at $DIR/address-of.rs:7:18: 7:19 + StorageLive(_7); // scope 2 at $DIR/address-of.rs:8:5: 8:26 + _7 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:8:5: 8:6 + StorageDead(_7); // scope 2 at $DIR/address-of.rs:8:26: 8:27 + StorageLive(_8); // scope 2 at $DIR/address-of.rs:9:5: 9:25 + StorageLive(_9); // scope 2 at $DIR/address-of.rs:9:5: 9:25 + StorageLive(_10); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + _10 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + _9 = move _10 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + StorageDead(_10); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + AscribeUserType(_9, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/address-of.rs:9:5: 9:25 + _8 = _9; // scope 2 at $DIR/address-of.rs:9:5: 9:25 + StorageDead(_9); // scope 2 at $DIR/address-of.rs:9:25: 9:26 + StorageDead(_8); // scope 2 at $DIR/address-of.rs:9:25: 9:26 + StorageLive(_11); // scope 2 at $DIR/address-of.rs:10:5: 10:22 + StorageLive(_12); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + _12 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + _11 = move _12 as *const [i32] (Pointer(Unsize)); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + StorageDead(_12); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + StorageDead(_11); // scope 2 at $DIR/address-of.rs:10:22: 10:23 + StorageLive(_13); // scope 2 at $DIR/address-of.rs:11:5: 11:20 + StorageLive(_14); // scope 2 at $DIR/address-of.rs:11:5: 11:6 + _14 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:11:5: 11:6 + _13 = move _14 as *const i32 (Pointer(ArrayToPointer)); // scope 2 at $DIR/address-of.rs:11:5: 11:20 + StorageDead(_14); // scope 2 at $DIR/address-of.rs:11:19: 11:20 + StorageDead(_13); // scope 2 at $DIR/address-of.rs:11:20: 11:21 + StorageLive(_15); // scope 2 at $DIR/address-of.rs:13:9: 13:10 + _15 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:13:23: 13:24 + FakeRead(ForLet, _15); // scope 2 at $DIR/address-of.rs:13:9: 13:10 + AscribeUserType(_15, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 2 at $DIR/address-of.rs:13:12: 13:20 + StorageLive(_16); // scope 3 at $DIR/address-of.rs:14:9: 14:10 + _16 = &raw const (*_1); // scope 3 at $DIR/address-of.rs:14:31: 14:32 + FakeRead(ForLet, _16); // scope 3 at $DIR/address-of.rs:14:9: 14:10 + AscribeUserType(_16, o, UserTypeProjection { base: UserType(5), projs: [] }); // scope 3 at $DIR/address-of.rs:14:12: 14:28 + StorageLive(_17); // scope 4 at $DIR/address-of.rs:15:9: 15:10 + StorageLive(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + _18 = &raw const (*_1); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + _17 = move _18 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + StorageDead(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + FakeRead(ForLet, _17); // scope 4 at $DIR/address-of.rs:15:9: 15:10 + AscribeUserType(_17, o, UserTypeProjection { base: UserType(7), projs: [] }); // scope 4 at $DIR/address-of.rs:15:12: 15:27 + StorageLive(_19); // scope 5 at $DIR/address-of.rs:16:9: 16:10 + StorageLive(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + _20 = &raw const (*_1); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + _19 = move _20 as *const [i32] (Pointer(Unsize)); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + StorageDead(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + FakeRead(ForLet, _19); // scope 5 at $DIR/address-of.rs:16:9: 16:10 + AscribeUserType(_19, o, UserTypeProjection { base: UserType(9), projs: [] }); // scope 5 at $DIR/address-of.rs:16:12: 16:24 + StorageLive(_21); // scope 6 at $DIR/address-of.rs:18:5: 18:18 + StorageLive(_22); // scope 6 at $DIR/address-of.rs:18:5: 18:18 + _22 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:18:5: 18:6 + AscribeUserType(_22, o, UserTypeProjection { base: UserType(10), projs: [] }); // scope 6 at $DIR/address-of.rs:18:5: 18:18 + _21 = _22; // scope 6 at $DIR/address-of.rs:18:5: 18:18 + StorageDead(_22); // scope 6 at $DIR/address-of.rs:18:18: 18:19 + StorageDead(_21); // scope 6 at $DIR/address-of.rs:18:18: 18:19 + StorageLive(_23); // scope 6 at $DIR/address-of.rs:19:5: 19:26 + _23 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:19:5: 19:6 + StorageDead(_23); // scope 6 at $DIR/address-of.rs:19:26: 19:27 + StorageLive(_24); // scope 6 at $DIR/address-of.rs:20:5: 20:25 + StorageLive(_25); // scope 6 at $DIR/address-of.rs:20:5: 20:25 + StorageLive(_26); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + _26 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + _25 = move _26 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + StorageDead(_26); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + AscribeUserType(_25, o, UserTypeProjection { base: UserType(11), projs: [] }); // scope 6 at $DIR/address-of.rs:20:5: 20:25 + _24 = _25; // scope 6 at $DIR/address-of.rs:20:5: 20:25 + StorageDead(_25); // scope 6 at $DIR/address-of.rs:20:25: 20:26 + StorageDead(_24); // scope 6 at $DIR/address-of.rs:20:25: 20:26 + StorageLive(_27); // scope 6 at $DIR/address-of.rs:21:5: 21:22 + StorageLive(_28); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + _28 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + _27 = move _28 as *const [i32] (Pointer(Unsize)); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + StorageDead(_28); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + StorageDead(_27); // scope 6 at $DIR/address-of.rs:21:22: 21:23 + StorageLive(_29); // scope 6 at $DIR/address-of.rs:23:9: 23:10 + _29 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:23:23: 23:24 + FakeRead(ForLet, _29); // scope 6 at $DIR/address-of.rs:23:9: 23:10 + AscribeUserType(_29, o, UserTypeProjection { base: UserType(13), projs: [] }); // scope 6 at $DIR/address-of.rs:23:12: 23:20 + StorageLive(_30); // scope 7 at $DIR/address-of.rs:24:9: 24:10 + _30 = &raw const (*_3); // scope 7 at $DIR/address-of.rs:24:31: 24:32 + FakeRead(ForLet, _30); // scope 7 at $DIR/address-of.rs:24:9: 24:10 + AscribeUserType(_30, o, UserTypeProjection { base: UserType(15), projs: [] }); // scope 7 at $DIR/address-of.rs:24:12: 24:28 + StorageLive(_31); // scope 8 at $DIR/address-of.rs:25:9: 25:10 + StorageLive(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + _32 = &raw const (*_3); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + _31 = move _32 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + StorageDead(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + FakeRead(ForLet, _31); // scope 8 at $DIR/address-of.rs:25:9: 25:10 + AscribeUserType(_31, o, UserTypeProjection { base: UserType(17), projs: [] }); // scope 8 at $DIR/address-of.rs:25:12: 25:27 + StorageLive(_33); // scope 9 at $DIR/address-of.rs:26:9: 26:10 + StorageLive(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + _34 = &raw const (*_3); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + _33 = move _34 as *const [i32] (Pointer(Unsize)); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + StorageDead(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + FakeRead(ForLet, _33); // scope 9 at $DIR/address-of.rs:26:9: 26:10 + AscribeUserType(_33, o, UserTypeProjection { base: UserType(19), projs: [] }); // scope 9 at $DIR/address-of.rs:26:12: 26:24 + StorageLive(_35); // scope 10 at $DIR/address-of.rs:28:5: 28:16 + StorageLive(_36); // scope 10 at $DIR/address-of.rs:28:5: 28:16 + _36 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:28:5: 28:6 + AscribeUserType(_36, o, UserTypeProjection { base: UserType(20), projs: [] }); // scope 10 at $DIR/address-of.rs:28:5: 28:16 + _35 = _36; // scope 10 at $DIR/address-of.rs:28:5: 28:16 + StorageDead(_36); // scope 10 at $DIR/address-of.rs:28:16: 28:17 + StorageDead(_35); // scope 10 at $DIR/address-of.rs:28:16: 28:17 + StorageLive(_37); // scope 10 at $DIR/address-of.rs:29:5: 29:24 + _37 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:29:5: 29:6 + StorageDead(_37); // scope 10 at $DIR/address-of.rs:29:24: 29:25 + StorageLive(_38); // scope 10 at $DIR/address-of.rs:30:5: 30:23 + StorageLive(_39); // scope 10 at $DIR/address-of.rs:30:5: 30:23 + StorageLive(_40); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + _40 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + _39 = move _40 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + StorageDead(_40); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + AscribeUserType(_39, o, UserTypeProjection { base: UserType(21), projs: [] }); // scope 10 at $DIR/address-of.rs:30:5: 30:23 + _38 = _39; // scope 10 at $DIR/address-of.rs:30:5: 30:23 + StorageDead(_39); // scope 10 at $DIR/address-of.rs:30:23: 30:24 + StorageDead(_38); // scope 10 at $DIR/address-of.rs:30:23: 30:24 + StorageLive(_41); // scope 10 at $DIR/address-of.rs:31:5: 31:20 + StorageLive(_42); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + _42 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + _41 = move _42 as *mut [i32] (Pointer(Unsize)); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + StorageDead(_42); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + StorageDead(_41); // scope 10 at $DIR/address-of.rs:31:20: 31:21 + StorageLive(_43); // scope 10 at $DIR/address-of.rs:33:9: 33:10 + _43 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:33:21: 33:22 + FakeRead(ForLet, _43); // scope 10 at $DIR/address-of.rs:33:9: 33:10 + AscribeUserType(_43, o, UserTypeProjection { base: UserType(23), projs: [] }); // scope 10 at $DIR/address-of.rs:33:12: 33:18 + StorageLive(_44); // scope 11 at $DIR/address-of.rs:34:9: 34:10 + _44 = &raw mut (*_3); // scope 11 at $DIR/address-of.rs:34:29: 34:30 + FakeRead(ForLet, _44); // scope 11 at $DIR/address-of.rs:34:9: 34:10 + AscribeUserType(_44, o, UserTypeProjection { base: UserType(25), projs: [] }); // scope 11 at $DIR/address-of.rs:34:12: 34:26 + StorageLive(_45); // scope 12 at $DIR/address-of.rs:35:9: 35:10 + StorageLive(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + _46 = &raw mut (*_3); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + _45 = move _46 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + StorageDead(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + FakeRead(ForLet, _45); // scope 12 at $DIR/address-of.rs:35:9: 35:10 + AscribeUserType(_45, o, UserTypeProjection { base: UserType(27), projs: [] }); // scope 12 at $DIR/address-of.rs:35:12: 35:25 + StorageLive(_47); // scope 13 at $DIR/address-of.rs:36:9: 36:10 + StorageLive(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + _48 = &raw mut (*_3); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + _47 = move _48 as *mut [i32] (Pointer(Unsize)); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + StorageDead(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + FakeRead(ForLet, _47); // scope 13 at $DIR/address-of.rs:36:9: 36:10 + AscribeUserType(_47, o, UserTypeProjection { base: UserType(29), projs: [] }); // scope 13 at $DIR/address-of.rs:36:12: 36:22 + _0 = const (); // scope 0 at $DIR/address-of.rs:3:26: 37:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/address-of.rs:3:26: 37:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_47); // scope 13 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_45); // scope 12 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_44); // scope 11 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_43); // scope 10 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_33); // scope 9 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_31); // scope 8 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_30); // scope 7 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_29); // scope 6 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_19); // scope 5 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_17); // scope 4 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_16); // scope 3 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_15); // scope 2 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_4); // scope 1 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_3); // scope 1 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_2); // scope 0 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_1); // scope 0 at $DIR/address-of.rs:37:1: 37:2 + return; // scope 0 at $DIR/address-of.rs:37:2: 37:2 + } +} diff --git a/src/test/mir-opt/address-of/rustc.borrow_and_cast.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address-of/rustc.borrow_and_cast.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..4a7e8de29ec22 --- /dev/null +++ b/src/test/mir-opt/address-of/rustc.borrow_and_cast.SimplifyCfg-initial.after.mir @@ -0,0 +1,53 @@ +// MIR for `borrow_and_cast` after SimplifyCfg-initial + +fn borrow_and_cast(_1: i32) -> () { + debug x => _1; // in scope 0 at $DIR/address-of.rs:41:20: 41:25 + let mut _0: (); // return place in scope 0 at $DIR/address-of.rs:41:32: 41:32 + let _2: *const i32; // in scope 0 at $DIR/address-of.rs:42:9: 42:10 + let _3: &i32; // in scope 0 at $DIR/address-of.rs:42:13: 42:15 + let _5: &mut i32; // in scope 0 at $DIR/address-of.rs:43:13: 43:19 + let mut _7: &mut i32; // in scope 0 at $DIR/address-of.rs:44:13: 44:19 + scope 1 { + debug p => _2; // in scope 1 at $DIR/address-of.rs:42:9: 42:10 + let _4: *const i32; // in scope 1 at $DIR/address-of.rs:43:9: 43:10 + scope 2 { + debug q => _4; // in scope 2 at $DIR/address-of.rs:43:9: 43:10 + let _6: *mut i32; // in scope 2 at $DIR/address-of.rs:44:9: 44:10 + scope 3 { + debug r => _6; // in scope 3 at $DIR/address-of.rs:44:9: 44:10 + } + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/address-of.rs:42:9: 42:10 + StorageLive(_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15 + _3 = &_1; // scope 0 at $DIR/address-of.rs:42:13: 42:15 + _2 = &raw const (*_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15 + FakeRead(ForLet, _2); // scope 0 at $DIR/address-of.rs:42:9: 42:10 + StorageDead(_3); // scope 0 at $DIR/address-of.rs:42:29: 42:30 + StorageLive(_4); // scope 1 at $DIR/address-of.rs:43:9: 43:10 + StorageLive(_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19 + _5 = &mut _1; // scope 1 at $DIR/address-of.rs:43:13: 43:19 + _4 = &raw const (*_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19 + FakeRead(ForLet, _4); // scope 1 at $DIR/address-of.rs:43:9: 43:10 + StorageDead(_5); // scope 1 at $DIR/address-of.rs:43:33: 43:34 + StorageLive(_6); // scope 2 at $DIR/address-of.rs:44:9: 44:10 + StorageLive(_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19 + _7 = &mut _1; // scope 2 at $DIR/address-of.rs:44:13: 44:19 + _6 = &raw mut (*_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19 + FakeRead(ForLet, _6); // scope 2 at $DIR/address-of.rs:44:9: 44:10 + StorageDead(_7); // scope 2 at $DIR/address-of.rs:44:31: 44:32 + _0 = const (); // scope 0 at $DIR/address-of.rs:41:32: 45:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/address-of.rs:41:32: 45:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_6); // scope 2 at $DIR/address-of.rs:45:1: 45:2 + StorageDead(_4); // scope 1 at $DIR/address-of.rs:45:1: 45:2 + StorageDead(_2); // scope 0 at $DIR/address-of.rs:45:1: 45:2 + return; // scope 0 at $DIR/address-of.rs:45:2: 45:2 + } +} diff --git a/src/test/mir-opt/array-index-is-temporary.rs b/src/test/mir-opt/array-index-is-temporary.rs index 57f30acb3a643..4667c4f66b296 100644 --- a/src/test/mir-opt/array-index-is-temporary.rs +++ b/src/test/mir-opt/array-index-is-temporary.rs @@ -7,35 +7,11 @@ unsafe fn foo(z: *mut usize) -> u32 { 99 } +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir fn main() { let mut x = [42, 43, 44]; let mut y = 1; let z: *mut usize = &mut y; x[y] = unsafe { foo(z) }; } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-elaborate-drops.after.mir -// bb0: { -// ... -// _4 = &mut _2; -// _3 = &raw mut (*_4); -// ... -// _6 = _3; -// _5 = const foo(move _6) -> bb1; -// } -// -// bb1: { -// ... -// _7 = _2; -// _8 = Len(_1); -// _9 = Lt(_7, _8); -// assert(move _9, "index out of bounds: the len is move _8 but the index is _7") -> bb2; -// } -// -// bb2: { -// _1[_7] = move _5; -// ... -// return; -// } -// END rustc.main.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/array-index-is-temporary/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/array-index-is-temporary/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..c42d5adce4f2b --- /dev/null +++ b/src/test/mir-opt/array-index-is-temporary/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,97 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array-index-is-temporary.rs:12:11: 12:11 + let mut _1: [u32; 3]; // in scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _4: &mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + let mut _5: u32; // in scope 0 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + let mut _6: *mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + let _7: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + let mut _8: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + let mut _9: bool; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _2: usize; // in scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + scope 2 { + debug y => _2; // in scope 2 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + let _3: *mut usize as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 4 { + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + _1 = [const 42u32, const 43u32, const 44u32]; // scope 0 at $DIR/array-index-is-temporary.rs:13:17: 13:29 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:13:18: 13:20 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002b)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:13:22: 13:24 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002b)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002c)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:13:26: 13:28 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002c)) } + StorageLive(_2); // scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + _2 = const 1usize; // scope 1 at $DIR/array-index-is-temporary.rs:14:17: 14:18 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:14:17: 14:18 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + StorageLive(_3); // scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + StorageLive(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _4 = &mut _2; // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _3 = &raw mut (*_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + StorageDead(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:31: 15:32 + StorageLive(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + StorageLive(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _6 = _3; // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _5 = const foo(move _6) -> bb1; // scope 4 at $DIR/array-index-is-temporary.rs:16:21: 16:27 + // ty::Const + // + ty: unsafe fn(*mut usize) -> u32 {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:16:21: 16:24 + // + literal: Const { ty: unsafe fn(*mut usize) -> u32 {foo}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:26: 16:27 + StorageLive(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _7 = _2; // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _8 = Len(_1); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + _9 = Lt(_7, _8); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + } + + bb2: { + _1[_7] = move _5; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:29 + StorageDead(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:28: 16:29 + StorageDead(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:29: 16:30 + _0 = const (); // scope 0 at $DIR/array-index-is-temporary.rs:12:11: 17:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:12:11: 17:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_3); // scope 2 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_2); // scope 1 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_1); // scope 0 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + return; // scope 0 at $DIR/array-index-is-temporary.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/array-index-is-temporary/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/array-index-is-temporary/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..05d9b3b9b6f77 --- /dev/null +++ b/src/test/mir-opt/array-index-is-temporary/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,97 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array-index-is-temporary.rs:12:11: 12:11 + let mut _1: [u32; 3]; // in scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _4: &mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + let mut _5: u32; // in scope 0 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + let mut _6: *mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + let _7: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + let mut _8: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + let mut _9: bool; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _2: usize; // in scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + scope 2 { + debug y => _2; // in scope 2 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + let _3: *mut usize as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 4 { + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + _1 = [const 42u32, const 43u32, const 44u32]; // scope 0 at $DIR/array-index-is-temporary.rs:13:17: 13:29 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:13:18: 13:20 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002b)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:13:22: 13:24 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002b)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002c)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:13:26: 13:28 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002c)) } + StorageLive(_2); // scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + _2 = const 1usize; // scope 1 at $DIR/array-index-is-temporary.rs:14:17: 14:18 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:14:17: 14:18 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + StorageLive(_3); // scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + StorageLive(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _4 = &mut _2; // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _3 = &raw mut (*_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + StorageDead(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:31: 15:32 + StorageLive(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + StorageLive(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _6 = _3; // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _5 = const foo(move _6) -> bb1; // scope 4 at $DIR/array-index-is-temporary.rs:16:21: 16:27 + // ty::Const + // + ty: unsafe fn(*mut usize) -> u32 {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:16:21: 16:24 + // + literal: Const { ty: unsafe fn(*mut usize) -> u32 {foo}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:26: 16:27 + StorageLive(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _7 = _2; // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _8 = Len(_1); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + _9 = Lt(_7, _8); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + } + + bb2: { + _1[_7] = move _5; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:29 + StorageDead(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:28: 16:29 + StorageDead(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:29: 16:30 + _0 = const (); // scope 0 at $DIR/array-index-is-temporary.rs:12:11: 17:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:12:11: 17:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_3); // scope 2 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_2); // scope 1 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_1); // scope 0 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + return; // scope 0 at $DIR/array-index-is-temporary.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs index ca0e9fa811a26..17141b6334c82 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -1,5 +1,7 @@ // this tests move up progration, which is not yet implemented +// EMIT_MIR rustc.main.SimplifyCfg-initial.after.mir + // Check codegen for assignments (`a = b`) where the left-hand-side is // not yet initialized. Assignments tend to be absent in simple code, // so subtle breakage in them can leave a quite hard-to-find trail of @@ -13,40 +15,10 @@ fn main() { // assignment: nodrop_y = nodrop_x; - let drop_x : Option> = None; + let drop_x: Option> = None; let drop_y; // Since the type of `drop_y` has drop, we generate a `replace` // terminator: drop_y = drop_x; } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-initial.after.mir -// bb0: { -// StorageLive(_1); -// _1 = const false; -// FakeRead(ForLet, _1); -// StorageLive(_2); -// StorageLive(_3); -// _3 = _1; -// _2 = move _3; -// StorageDead(_3); -// StorageLive(_4); -// _4 = std::option::Option::>::None; -// FakeRead(ForLet, _4); -// AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] }); -// StorageLive(_5); -// StorageLive(_6); -// _6 = move _4; -// replace(_5 <- move _6) -> [return: bb2, unwind: bb5]; -// } -// ... -// bb2: { -// drop(_6) -> [return: bb6, unwind: bb4]; -// } -// ... -// bb5 (cleanup): { -// drop(_6) -> bb4; -// } -// END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..c0a292332711b --- /dev/null +++ b/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir @@ -0,0 +1,96 @@ +// MIR for `main` after SimplifyCfg-initial + +| User Type Annotations +| 0: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option>) } at $DIR/basic_assignment.rs:18:17: 18:33 +| 1: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option>) } at $DIR/basic_assignment.rs:18:17: 18:33 +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/basic_assignment.rs:10:11: 10:11 + let _1: bool; // in scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 + let mut _3: bool; // in scope 0 at $DIR/basic_assignment.rs:16:16: 16:24 + let mut _6: std::option::Option>; // in scope 0 at $DIR/basic_assignment.rs:23:14: 23:20 + scope 1 { + debug nodrop_x => _1; // in scope 1 at $DIR/basic_assignment.rs:11:9: 11:17 + let _2: bool; // in scope 1 at $DIR/basic_assignment.rs:12:9: 12:17 + scope 2 { + debug nodrop_y => _2; // in scope 2 at $DIR/basic_assignment.rs:12:9: 12:17 + let _4: std::option::Option> as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 + scope 3 { + debug drop_x => _4; // in scope 3 at $DIR/basic_assignment.rs:18:9: 18:15 + let _5: std::option::Option>; // in scope 3 at $DIR/basic_assignment.rs:19:9: 19:15 + scope 4 { + debug drop_y => _5; // in scope 4 at $DIR/basic_assignment.rs:19:9: 19:15 + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 + _1 = const false; // scope 0 at $DIR/basic_assignment.rs:11:20: 11:25 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/basic_assignment.rs:11:20: 11:25 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + FakeRead(ForLet, _1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 + StorageLive(_2); // scope 1 at $DIR/basic_assignment.rs:12:9: 12:17 + StorageLive(_3); // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24 + _3 = _1; // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24 + _2 = move _3; // scope 2 at $DIR/basic_assignment.rs:16:5: 16:24 + StorageDead(_3); // scope 2 at $DIR/basic_assignment.rs:16:23: 16:24 + StorageLive(_4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 + _4 = std::option::Option::>::None; // scope 2 at $DIR/basic_assignment.rs:18:36: 18:40 + FakeRead(ForLet, _4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 + AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/basic_assignment.rs:18:17: 18:33 + StorageLive(_5); // scope 3 at $DIR/basic_assignment.rs:19:9: 19:15 + StorageLive(_6); // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20 + _6 = move _4; // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20 + replace(_5 <- move _6) -> [return: bb1, unwind: bb5]; // scope 4 at $DIR/basic_assignment.rs:23:5: 23:11 + } + + bb1: { + drop(_6) -> [return: bb2, unwind: bb6]; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + } + + bb2: { + StorageDead(_6); // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + _0 = const (); // scope 0 at $DIR/basic_assignment.rs:10:11: 24:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/basic_assignment.rs:10:11: 24:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_5) -> [return: bb3, unwind: bb7]; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb3: { + StorageDead(_5); // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + drop(_4) -> [return: bb4, unwind: bb8]; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb4: { + StorageDead(_4); // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + StorageDead(_2); // scope 1 at $DIR/basic_assignment.rs:24:1: 24:2 + StorageDead(_1); // scope 0 at $DIR/basic_assignment.rs:24:1: 24:2 + return; // scope 0 at $DIR/basic_assignment.rs:24:2: 24:2 + } + + bb5 (cleanup): { + drop(_6) -> bb6; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + } + + bb6 (cleanup): { + drop(_5) -> bb7; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb7 (cleanup): { + drop(_4) -> bb8; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb8 (cleanup): { + resume; // scope 0 at $DIR/basic_assignment.rs:10:1: 24:2 + } +} diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs index fa1a291858bec..beaf0baf12c0b 100644 --- a/src/test/mir-opt/box_expr.rs +++ b/src/test/mir-opt/box_expr.rs @@ -2,6 +2,7 @@ #![feature(box_syntax)] +// EMIT_MIR rustc.main.ElaborateDrops.before.mir fn main() { let x = box S::new(); drop(x); @@ -18,60 +19,3 @@ impl Drop for S { println!("splat!"); } } - -// END RUST SOURCE -// START rustc.main.ElaborateDrops.before.mir -// let mut _0: (); -// let _1: std::boxed::Box; -// let mut _2: std::boxed::Box; -// let _3: (); -// let mut _4: std::boxed::Box; -// scope 1 { -// debug x => _1; -// } -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = Box(S); -// (*_2) = const S::new() -> [return: bb2, unwind: bb3]; -// } -// -// bb1 (cleanup): { -// resume; -// } -// -// bb2: { -// _1 = move _2; -// drop(_2) -> bb4; -// } -// -// bb3 (cleanup): { -// drop(_2) -> bb1; -// } -// -// bb4: { -// StorageDead(_2); -// StorageLive(_3); -// StorageLive(_4); -// _4 = move _1; -// _3 = const std::mem::drop::>(move _4) -> [return: bb5, unwind: bb7]; -// } -// -// bb5: { -// StorageDead(_4); -// StorageDead(_3); -// _0 = (); -// drop(_1) -> bb8; -// } -// bb6 (cleanup): { -// drop(_1) -> bb1; -// } -// bb7 (cleanup): { -// drop(_4) -> bb6; -// } -// bb8: { -// StorageDead(_1); -// return; -// } -// } -// END rustc.main.ElaborateDrops.before.mir diff --git a/src/test/mir-opt/box_expr/rustc.main.ElaborateDrops.before.mir b/src/test/mir-opt/box_expr/rustc.main.ElaborateDrops.before.mir new file mode 100644 index 0000000000000..9d5b9be363d3f --- /dev/null +++ b/src/test/mir-opt/box_expr/rustc.main.ElaborateDrops.before.mir @@ -0,0 +1,78 @@ +// MIR for `main` before ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/box_expr.rs:6:11: 6:11 + let _1: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:7:9: 7:10 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:7:13: 7:25 + let _3: (); // in scope 0 at $DIR/box_expr.rs:8:5: 8:12 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:8:10: 8:11 + scope 1 { + debug x => _1; // in scope 1 at $DIR/box_expr.rs:7:9: 7:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/box_expr.rs:7:9: 7:10 + StorageLive(_2); // scope 0 at $DIR/box_expr.rs:7:13: 7:25 + _2 = Box(S); // scope 0 at $DIR/box_expr.rs:7:13: 7:25 + (*_2) = const S::new() -> [return: bb1, unwind: bb7]; // scope 0 at $DIR/box_expr.rs:7:17: 7:25 + // ty::Const + // + ty: fn() -> S {S::new} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/box_expr.rs:7:17: 7:23 + // + literal: Const { ty: fn() -> S {S::new}, val: Value(Scalar()) } + } + + bb1: { + _1 = move _2; // scope 0 at $DIR/box_expr.rs:7:13: 7:25 + drop(_2) -> bb2; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + } + + bb2: { + StorageDead(_2); // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + StorageLive(_3); // scope 1 at $DIR/box_expr.rs:8:5: 8:12 + StorageLive(_4); // scope 1 at $DIR/box_expr.rs:8:10: 8:11 + _4 = move _1; // scope 1 at $DIR/box_expr.rs:8:10: 8:11 + _3 = const std::mem::drop::>(move _4) -> [return: bb3, unwind: bb5]; // scope 1 at $DIR/box_expr.rs:8:5: 8:12 + // ty::Const + // + ty: fn(std::boxed::Box) {std::mem::drop::>} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/box_expr.rs:8:5: 8:9 + // + literal: Const { ty: fn(std::boxed::Box) {std::mem::drop::>}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_4); // scope 1 at $DIR/box_expr.rs:8:11: 8:12 + StorageDead(_3); // scope 1 at $DIR/box_expr.rs:8:12: 8:13 + _0 = const (); // scope 0 at $DIR/box_expr.rs:6:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/box_expr.rs:6:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_1) -> bb4; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + } + + bb4: { + StorageDead(_1); // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + return; // scope 0 at $DIR/box_expr.rs:9:2: 9:2 + } + + bb5 (cleanup): { + drop(_4) -> bb6; // scope 1 at $DIR/box_expr.rs:8:11: 8:12 + } + + bb6 (cleanup): { + drop(_1) -> bb8; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + } + + bb7 (cleanup): { + drop(_2) -> bb8; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + } + + bb8 (cleanup): { + resume; // scope 0 at $DIR/box_expr.rs:6:1: 9:2 + } +} diff --git a/src/test/mir-opt/byte_slice.rs b/src/test/mir-opt/byte_slice.rs index 0fb685c3c4e6d..317e96d6f52d6 100644 --- a/src/test/mir-opt/byte_slice.rs +++ b/src/test/mir-opt/byte_slice.rs @@ -1,15 +1,7 @@ // compile-flags: -Z mir-opt-level=0 +// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir fn main() { let x = b"foo"; let y = [5u8, b'x']; } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-elaborate-drops.after.mir -// ... -// _1 = const b"foo"; -// ... -// _2 = [const 5u8, const 120u8]; -// ... -// END rustc.main.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/byte_slice/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/byte_slice/rustc.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..88cb09ac15a9c --- /dev/null +++ b/src/test/mir-opt/byte_slice/rustc.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,52 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/byte_slice.rs:4:11: 4:11 + let _1: &[u8; 3]; // in scope 0 at $DIR/byte_slice.rs:5:9: 5:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/byte_slice.rs:5:9: 5:10 + let _2: [u8; 2]; // in scope 1 at $DIR/byte_slice.rs:6:9: 6:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/byte_slice.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/byte_slice.rs:5:9: 5:10 + _1 = const b"foo"; // scope 0 at $DIR/byte_slice.rs:5:13: 5:19 + // ty::Const + // + ty: &[u8; 3] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/byte_slice.rs:5:13: 5:19 + // + literal: Const { ty: &[u8; 3], val: Value(Scalar(alloc0)) } + StorageLive(_2); // scope 1 at $DIR/byte_slice.rs:6:9: 6:10 + _2 = [const 5u8, const 120u8]; // scope 1 at $DIR/byte_slice.rs:6:13: 6:24 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x05)) + // mir::Constant + // + span: $DIR/byte_slice.rs:6:14: 6:17 + // + literal: Const { ty: u8, val: Value(Scalar(0x05)) } + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x78)) + // mir::Constant + // + span: $DIR/byte_slice.rs:6:19: 6:23 + // + literal: Const { ty: u8, val: Value(Scalar(0x78)) } + _0 = const (); // scope 0 at $DIR/byte_slice.rs:4:11: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/byte_slice.rs:4:11: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/byte_slice.rs:7:1: 7:2 + StorageDead(_1); // scope 0 at $DIR/byte_slice.rs:7:1: 7:2 + return; // scope 0 at $DIR/byte_slice.rs:7:2: 7:2 + } +} + +alloc0 (size: 3, align: 1) { + 66 6f 6f │ foo +} diff --git a/src/test/mir-opt/combine_array_len.rs b/src/test/mir-opt/combine_array_len.rs index 9a046202cd004..aa1c7459ea155 100644 --- a/src/test/mir-opt/combine_array_len.rs +++ b/src/test/mir-opt/combine_array_len.rs @@ -1,3 +1,6 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.norm2.InstCombine.diff + fn norm2(x: [f32; 2]) -> f32 { let a = x[0]; let b = x[1]; @@ -7,17 +10,3 @@ fn norm2(x: [f32; 2]) -> f32 { fn main() { assert_eq!(norm2([3.0, 4.0]), 5.0*5.0); } - -// END RUST SOURCE - -// START rustc.norm2.InstCombine.before.mir -// _4 = Len(_1); -// ... -// _8 = Len(_1); -// END rustc.norm2.InstCombine.before.mir - -// START rustc.norm2.InstCombine.after.mir -// _4 = const 2usize; -// ... -// _8 = const 2usize; -// END rustc.norm2.InstCombine.after.mir diff --git a/src/test/mir-opt/combine_array_len/32bit/rustc.norm2.InstCombine.diff b/src/test/mir-opt/combine_array_len/32bit/rustc.norm2.InstCombine.diff new file mode 100644 index 0000000000000..e11619cf0cd0e --- /dev/null +++ b/src/test/mir-opt/combine_array_len/32bit/rustc.norm2.InstCombine.diff @@ -0,0 +1,101 @@ +- // MIR for `norm2` before InstCombine ++ // MIR for `norm2` after InstCombine + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; // in scope 0 at $DIR/combine_array_len.rs:4:10: 4:11 + let mut _0: f32; // return place in scope 0 at $DIR/combine_array_len.rs:4:26: 4:29 + let _2: f32; // in scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + let _3: usize; // in scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + let mut _4: usize; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let mut _5: bool; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let _7: usize; // in scope 0 at $DIR/combine_array_len.rs:6:15: 6:16 + let mut _8: usize; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _9: bool; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _10: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:8 + let mut _11: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:6 + let mut _12: f32; // in scope 0 at $DIR/combine_array_len.rs:7:7: 7:8 + let mut _13: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:14 + let mut _14: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:12 + let mut _15: f32; // in scope 0 at $DIR/combine_array_len.rs:7:13: 7:14 + scope 1 { + debug a => _2; // in scope 1 at $DIR/combine_array_len.rs:5:9: 5:10 + let _6: f32; // in scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + scope 2 { + debug b => _6; // in scope 2 at $DIR/combine_array_len.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + StorageLive(_3); // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + _3 = const 0usize; // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/combine_array_len.rs:5:15: 5:16 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } +- _4 = Len(_1); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 ++ _4 = const 2usize; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/combine_array_len.rs:5:13: 5:17 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } + _5 = Lt(_3, _4); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + } + + bb1: { + _2 = _1[_3]; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + StorageDead(_3); // scope 0 at $DIR/combine_array_len.rs:5:17: 5:18 + StorageLive(_6); // scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + StorageLive(_7); // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 + _7 = const 1usize; // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/combine_array_len.rs:6:15: 6:16 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } +- _8 = Len(_1); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 ++ _8 = const 2usize; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/combine_array_len.rs:6:13: 6:17 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } + _9 = Lt(_7, _8); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + } + + bb2: { + _6 = _1[_7]; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + StorageDead(_7); // scope 1 at $DIR/combine_array_len.rs:6:17: 6:18 + StorageLive(_10); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageLive(_11); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + _11 = _2; // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + StorageLive(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _12 = _2; // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _10 = Mul(move _11, move _12); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageDead(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageDead(_11); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageLive(_13); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageLive(_14); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + _14 = _6; // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + StorageLive(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _15 = _6; // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _13 = Mul(move _14, move _15); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageDead(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_14); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _0 = Add(move _10, move _13); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:14 + StorageDead(_13); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_10); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_6); // scope 1 at $DIR/combine_array_len.rs:8:1: 8:2 + StorageDead(_2); // scope 0 at $DIR/combine_array_len.rs:8:1: 8:2 + return; // scope 0 at $DIR/combine_array_len.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/combine_array_len/64bit/rustc.norm2.InstCombine.diff b/src/test/mir-opt/combine_array_len/64bit/rustc.norm2.InstCombine.diff new file mode 100644 index 0000000000000..050cfe359a175 --- /dev/null +++ b/src/test/mir-opt/combine_array_len/64bit/rustc.norm2.InstCombine.diff @@ -0,0 +1,101 @@ +- // MIR for `norm2` before InstCombine ++ // MIR for `norm2` after InstCombine + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; // in scope 0 at $DIR/combine_array_len.rs:4:10: 4:11 + let mut _0: f32; // return place in scope 0 at $DIR/combine_array_len.rs:4:26: 4:29 + let _2: f32; // in scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + let _3: usize; // in scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + let mut _4: usize; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let mut _5: bool; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let _7: usize; // in scope 0 at $DIR/combine_array_len.rs:6:15: 6:16 + let mut _8: usize; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _9: bool; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _10: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:8 + let mut _11: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:6 + let mut _12: f32; // in scope 0 at $DIR/combine_array_len.rs:7:7: 7:8 + let mut _13: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:14 + let mut _14: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:12 + let mut _15: f32; // in scope 0 at $DIR/combine_array_len.rs:7:13: 7:14 + scope 1 { + debug a => _2; // in scope 1 at $DIR/combine_array_len.rs:5:9: 5:10 + let _6: f32; // in scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + scope 2 { + debug b => _6; // in scope 2 at $DIR/combine_array_len.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + StorageLive(_3); // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + _3 = const 0usize; // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/combine_array_len.rs:5:15: 5:16 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } +- _4 = Len(_1); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 ++ _4 = const 2usize; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x0000000000000002)) ++ // mir::Constant ++ // + span: $DIR/combine_array_len.rs:5:13: 5:17 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } + _5 = Lt(_3, _4); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + } + + bb1: { + _2 = _1[_3]; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + StorageDead(_3); // scope 0 at $DIR/combine_array_len.rs:5:17: 5:18 + StorageLive(_6); // scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + StorageLive(_7); // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 + _7 = const 1usize; // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $DIR/combine_array_len.rs:6:15: 6:16 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } +- _8 = Len(_1); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 ++ _8 = const 2usize; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x0000000000000002)) ++ // mir::Constant ++ // + span: $DIR/combine_array_len.rs:6:13: 6:17 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } + _9 = Lt(_7, _8); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + } + + bb2: { + _6 = _1[_7]; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + StorageDead(_7); // scope 1 at $DIR/combine_array_len.rs:6:17: 6:18 + StorageLive(_10); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageLive(_11); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + _11 = _2; // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + StorageLive(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _12 = _2; // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _10 = Mul(move _11, move _12); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageDead(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageDead(_11); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageLive(_13); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageLive(_14); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + _14 = _6; // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + StorageLive(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _15 = _6; // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _13 = Mul(move _14, move _15); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageDead(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_14); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _0 = Add(move _10, move _13); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:14 + StorageDead(_13); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_10); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_6); // scope 1 at $DIR/combine_array_len.rs:8:1: 8:2 + StorageDead(_2); // scope 0 at $DIR/combine_array_len.rs:8:1: 8:2 + return; // scope 0 at $DIR/combine_array_len.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/const-promotion-extern-static.rs b/src/test/mir-opt/const-promotion-extern-static.rs index c858a4c5ee7c6..c9d350a98fd9c 100644 --- a/src/test/mir-opt/const-promotion-extern-static.rs +++ b/src/test/mir-opt/const-promotion-extern-static.rs @@ -4,70 +4,12 @@ extern "C" { static Y: i32 = 42; +// EMIT_MIR rustc.BAR.PromoteTemps.diff +// EMIT_MIR rustc.BAR-promoted[0].ConstProp.after.mir static mut BAR: *const &i32 = [&Y].as_ptr(); +// EMIT_MIR rustc.FOO.PromoteTemps.diff +// EMIT_MIR rustc.FOO-promoted[0].ConstProp.after.mir static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); fn main() {} - -// END RUST SOURCE -// START rustc.FOO.PromoteTemps.before.mir -// bb0: { -// ... -// _5 = const {alloc1+0: &i32}; -// _4 = &(*_5); -// _3 = [move _4]; -// _2 = &_3; -// _1 = move _2 as &[&i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; -// } -// ... -// bb2: { -// StorageDead(_5); -// StorageDead(_3); -// return; -// } -// END rustc.FOO.PromoteTemps.before.mir -// START rustc.BAR.PromoteTemps.before.mir -// bb0: { -// ... -// _5 = const {alloc0+0: &i32}; -// _4 = &(*_5); -// _3 = [move _4]; -// _2 = &_3; -// _1 = move _2 as &[&i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; -// } -// ... -// bb2: { -// StorageDead(_5); -// StorageDead(_3); -// return; -// } -// END rustc.BAR.PromoteTemps.before.mir -// START rustc.BAR.PromoteTemps.after.mir -// bb0: { -// ... -// _6 = const BAR::promoted[0]; -// _2 = &(*_6); -// _1 = move _2 as &[&i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; -// } -// ... -// bb2: { -// return; -// } -// END rustc.BAR.PromoteTemps.after.mir -// START rustc.FOO.PromoteTemps.after.mir -// bb0: { -// ... -// _6 = const FOO::promoted[0]; -// _2 = &(*_6); -// _1 = move _2 as &[&i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; -// } -// ... -// bb2: { -// return; -// } -// END rustc.FOO.PromoteTemps.after.mir diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir new file mode 100644 index 0000000000000..509947071b0c1 --- /dev/null +++ b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir @@ -0,0 +1,26 @@ +// MIR for `BAR::promoted[0]` after ConstProp + +promoted[0] in BAR: &[&i32; 1] = { + let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 + let mut _3: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 + + bb0: { + _3 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 + // ty::Const + // + ty: &i32 + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34 + // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) } + _2 = _3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 + _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + } +} + +alloc0 (static: Y, size: 4, align: 4) { + 2a 00 00 00 │ *... +} diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff new file mode 100644 index 0000000000000..8eb8d4c667b1c --- /dev/null +++ b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff @@ -0,0 +1,59 @@ +- // MIR for `BAR` before PromoteTemps ++ // MIR for `BAR` after PromoteTemps + + static mut BAR: *const &i32 = { + let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:17: 9:28 + let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 + let _5: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 ++ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 +- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 +- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 +- StorageLive(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 +- _5 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 ++ _6 = const BAR::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + // ty::Const +- // + ty: &i32 +- // + val: Value(Scalar(alloc0)) ++ // + ty: &[&i32; 1] ++ // + val: Unevaluated(DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), [], Some(promoted[0])) + // mir::Constant +- // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34 +- // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) } +- _4 = &(*_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 +- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 +- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 ++ // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:35 ++ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), [], Some(promoted[0])) } ++ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + _0 = const core::slice::::as_ptr(move _1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44 + // ty::Const + // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:9:36: 9:42 + // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } + } + + bb1: { +- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 +- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 + } +- } +- +- alloc0 (static: Y, size: 4, align: 4) { +- 2a 00 00 00 │ *... + } + diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir new file mode 100644 index 0000000000000..d9c6b4f0029a3 --- /dev/null +++ b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir @@ -0,0 +1,26 @@ +// MIR for `FOO::promoted[0]` after ConstProp + +promoted[0] in FOO: &[&i32; 1] = { + let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 + let mut _3: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 + scope 1 { + } + + bb0: { + _3 = const {alloc2: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 + // ty::Const + // + ty: &i32 + // + val: Value(Scalar(alloc2)) + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43 + // + literal: Const { ty: &i32, val: Value(Scalar(alloc2)) } + _2 = _3; // scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43 + _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + } +} + +alloc2 (extern static: X) diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff new file mode 100644 index 0000000000000..781aa3c5500c0 --- /dev/null +++ b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff @@ -0,0 +1,59 @@ +- // MIR for `FOO` before PromoteTemps ++ // MIR for `FOO` after PromoteTemps + + static mut FOO: *const &i32 = { + let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:17: 13:28 + let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 + let _5: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 ++ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + scope 1 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 +- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 +- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 +- StorageLive(_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 +- _5 = const {alloc2: &i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 ++ _6 = const FOO::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + // ty::Const +- // + ty: &i32 +- // + val: Value(Scalar(alloc2)) ++ // + ty: &[&i32; 1] ++ // + val: Unevaluated(DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), [], Some(promoted[0])) + // mir::Constant +- // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43 +- // + literal: Const { ty: &i32, val: Value(Scalar(alloc2)) } +- _4 = &(*_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43 +- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 +- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 ++ // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:46 ++ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), [], Some(promoted[0])) } ++ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + _0 = const core::slice::::as_ptr(move _1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55 + // ty::Const + // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:13:47: 13:53 + // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } + } + + bb1: { +- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 +- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 + } + } +- +- alloc2 (extern static: X) + diff --git a/src/test/mir-opt/const_allocation.rs b/src/test/mir-opt/const_allocation.rs new file mode 100644 index 0000000000000..aaf996ee8e1a7 --- /dev/null +++ b/src/test/mir-opt/const_allocation.rs @@ -0,0 +1,9 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +static FOO: &[(Option, &[&str])] = + &[(None, &[]), (None, &["foo", "bar"]), (Some(42), &["meh", "mop", "möp"])]; + +// EMIT_MIR rustc.main.ConstProp.after.mir +fn main() { + FOO; +} diff --git a/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir new file mode 100644 index 0000000000000..30a383fd162b2 --- /dev/null +++ b/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir @@ -0,0 +1,71 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11 + let _1: &[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + let mut _2: &&[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + _2 = const {alloc0: &&[(std::option::Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&str])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation.rs:8:5: 8:8 + // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + _0 = const (); // scope 0 at $DIR/const_allocation.rs:7:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_allocation.rs:7:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/const_allocation.rs:9:2: 9:2 + } +} + +alloc0 (static: FOO, size: 8, align: 4) { + ╾─alloc17─╼ 03 00 00 00 │ ╾──╼.... +} + +alloc17 (size: 48, align: 4) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼.... + 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 02 00 00 00 │ ....░░░░╾──╼.... + 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc13─╼ 03 00 00 00 │ ....*...╾──╼.... +} + +alloc4 (size: 0, align: 4) {} + +alloc8 (size: 16, align: 4) { + ╾─alloc7──╼ 03 00 00 00 ╾─alloc9──╼ 03 00 00 00 │ ╾──╼....╾──╼.... +} + +alloc7 (size: 3, align: 1) { + 66 6f 6f │ foo +} + +alloc9 (size: 3, align: 1) { + 62 61 72 │ bar +} + +alloc13 (size: 24, align: 4) { + 0x00 │ ╾─alloc12─╼ 03 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ╾──╼....╾──╼.... + 0x10 │ ╾─alloc15─╼ 04 00 00 00 │ ╾──╼.... +} + +alloc12 (size: 3, align: 1) { + 6d 65 68 │ meh +} + +alloc14 (size: 3, align: 1) { + 6d 6f 70 │ mop +} + +alloc15 (size: 4, align: 1) { + 6d c3 b6 70 │ m..p +} diff --git a/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir new file mode 100644 index 0000000000000..5fa54ae5a58ec --- /dev/null +++ b/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir @@ -0,0 +1,75 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11 + let _1: &[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + let mut _2: &&[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + _2 = const {alloc0: &&[(std::option::Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&str])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation.rs:8:5: 8:8 + // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + _0 = const (); // scope 0 at $DIR/const_allocation.rs:7:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_allocation.rs:7:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/const_allocation.rs:9:2: 9:2 + } +} + +alloc0 (static: FOO, size: 16, align: 8) { + ╾───────alloc17───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc17 (size: 72, align: 8) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ + 0x20 │ ╾───────alloc8────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc13───────╼ │ ....*...╾──────╼ + 0x40 │ 03 00 00 00 00 00 00 00 │ ........ +} + +alloc4 (size: 0, align: 8) {} + +alloc8 (size: 32, align: 8) { + 0x00 │ ╾───────alloc7────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ ╾───────alloc9────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc7 (size: 3, align: 1) { + 66 6f 6f │ foo +} + +alloc9 (size: 3, align: 1) { + 62 61 72 │ bar +} + +alloc13 (size: 48, align: 8) { + 0x00 │ ╾───────alloc12───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x20 │ ╾───────alloc15───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc12 (size: 3, align: 1) { + 6d 65 68 │ meh +} + +alloc14 (size: 3, align: 1) { + 6d 6f 70 │ mop +} + +alloc15 (size: 4, align: 1) { + 6d c3 b6 70 │ m..p +} diff --git a/src/test/mir-opt/const_allocation2.rs b/src/test/mir-opt/const_allocation2.rs new file mode 100644 index 0000000000000..ca61b84c0bcad --- /dev/null +++ b/src/test/mir-opt/const_allocation2.rs @@ -0,0 +1,11 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +// EMIT_MIR rustc.main.ConstProp.after.mir +fn main() { + FOO; +} + +const BAR: [u8; 4] = [42, 69, 21, 111]; + +static FOO: &[(Option, &[&u8])] = + &[(None, &[]), (None, &[&5, &6]), (Some(42), &[&BAR[3], &42, &BAR[2]])]; diff --git a/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir new file mode 100644 index 0000000000000..d386d24782926 --- /dev/null +++ b/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir @@ -0,0 +1,70 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11 + let _1: &[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + let mut _2: &&[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + _2 = const {alloc0: &&[(std::option::Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&u8])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation2.rs:5:5: 5:8 + // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation2.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_allocation2.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/const_allocation2.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 8, align: 4) { + ╾─alloc21─╼ 03 00 00 00 │ ╾──╼.... +} + +alloc21 (size: 48, align: 4) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼.... + 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc9──╼ 02 00 00 00 │ ....░░░░╾──╼.... + 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc19─╼ 03 00 00 00 │ ....*...╾──╼.... +} + +alloc4 (size: 0, align: 4) {} + +alloc9 (size: 8, align: 4) { + ╾─alloc7──╼ ╾─alloc8──╼ │ ╾──╼╾──╼ +} + +alloc7 (size: 1, align: 1) { + 05 │ . +} + +alloc8 (size: 1, align: 1) { + 06 │ . +} + +alloc19 (size: 12, align: 4) { + ╾─a15+0x3─╼ ╾─alloc16─╼ ╾─a18+0x2─╼ │ ╾──╼╾──╼╾──╼ +} + +alloc15 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} + +alloc16 (size: 1, align: 1) { + 2a │ * +} + +alloc18 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} diff --git a/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir new file mode 100644 index 0000000000000..d7acd0f0f4335 --- /dev/null +++ b/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir @@ -0,0 +1,73 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11 + let _1: &[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + let mut _2: &&[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + _2 = const {alloc0: &&[(std::option::Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&u8])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation2.rs:5:5: 5:8 + // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation2.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_allocation2.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/const_allocation2.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 16, align: 8) { + ╾───────alloc21───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc21 (size: 72, align: 8) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ + 0x20 │ ╾───────alloc9────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc19───────╼ │ ....*...╾──────╼ + 0x40 │ 03 00 00 00 00 00 00 00 │ ........ +} + +alloc4 (size: 0, align: 8) {} + +alloc9 (size: 16, align: 8) { + ╾───────alloc7────────╼ ╾───────alloc8────────╼ │ ╾──────╼╾──────╼ +} + +alloc7 (size: 1, align: 1) { + 05 │ . +} + +alloc8 (size: 1, align: 1) { + 06 │ . +} + +alloc19 (size: 24, align: 8) { + 0x00 │ ╾─────alloc15+0x3─────╼ ╾───────alloc16───────╼ │ ╾──────╼╾──────╼ + 0x10 │ ╾─────alloc18+0x2─────╼ │ ╾──────╼ +} + +alloc15 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} + +alloc16 (size: 1, align: 1) { + 2a │ * +} + +alloc18 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} diff --git a/src/test/mir-opt/const_allocation3.rs b/src/test/mir-opt/const_allocation3.rs new file mode 100644 index 0000000000000..73bb58e1a9892 --- /dev/null +++ b/src/test/mir-opt/const_allocation3.rs @@ -0,0 +1,29 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +// EMIT_MIR rustc.main.ConstProp.after.mir +fn main() { + FOO; +} + +#[repr(packed)] +struct Packed { + a: [u8; 28], + b: &'static i32, + c: u32, + d: [u8; 102], + e: fn(), + f: u16, + g: &'static u8, + h: [u8; 20], +} + +static FOO: &Packed = &Packed { + a: [0xAB; 28], + b: &42, + c: 0xABCD_EF01, + d: [0; 102], + e: main, + f: 0, + g: &[0; 100][99], + h: [0; 20], +}; diff --git a/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir new file mode 100644 index 0000000000000..39c60ad987a6f --- /dev/null +++ b/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir @@ -0,0 +1,64 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11 + let _1: &Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + let mut _2: &&Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + // ty::Const + // + ty: &&Packed + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation3.rs:5:5: 5:8 + // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation3.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_allocation3.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/const_allocation3.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 4, align: 4) { + ╾─alloc9──╼ │ ╾──╼ +} + +alloc9 (size: 168, align: 1) { + 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ + 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc4──╼ │ ............╾──╼ + 0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾─alloc6──╼ 00 00 │ ..........╾──╼.. + 0x90 │ ╾─a7+0x63─╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............ + 0xa0 │ 00 00 00 00 00 00 00 00 │ ........ +} + +alloc4 (size: 4, align: 4) { + 2a 00 00 00 │ *... +} + +alloc6 (fn: main) + +alloc7 (size: 100, align: 1) { + 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 │ .... +} diff --git a/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir new file mode 100644 index 0000000000000..96024f1c82caa --- /dev/null +++ b/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir @@ -0,0 +1,65 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11 + let _1: &Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + let mut _2: &&Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + // ty::Const + // + ty: &&Packed + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation3.rs:5:5: 5:8 + // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation3.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_allocation3.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/const_allocation3.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 8, align: 8) { + ╾───────alloc9────────╼ │ ╾──────╼ +} + +alloc9 (size: 180, align: 1) { + 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ + 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾──alloc4── │ ............╾─── + 0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─ + 0x90 │ ─────alloc6─────╼ 00 00 ╾─────alloc7+0x63─────╼ │ ─────╼..╾──────╼ + 0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0xb0 │ 00 00 00 00 │ .... +} + +alloc4 (size: 4, align: 4) { + 2a 00 00 00 │ *... +} + +alloc6 (fn: main) + +alloc7 (size: 100, align: 1) { + 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 │ .... +} diff --git a/src/test/mir-opt/const_prop/aggregate.rs b/src/test/mir-opt/const_prop/aggregate.rs index d04dcc6a05ce1..928ed8265d3fb 100644 --- a/src/test/mir-opt/const_prop/aggregate.rs +++ b/src/test/mir-opt/const_prop/aggregate.rs @@ -1,25 +1,6 @@ // compile-flags: -O +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x = (0, 1, 2).1 + 0; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _3 = (const 0i32, const 1i32, const 2i32); -// _2 = (_3.1: i32); -// _1 = Add(move _2, const 0i32); -// ... -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _3 = (const 0i32, const 1i32, const 2i32); -// _2 = const 1i32; -// _1 = const 1i32; -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/aggregate/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/aggregate/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..f4b6c7db444e5 --- /dev/null +++ b/src/test/mir-opt/const_prop/aggregate/rustc.main.ConstProp.diff @@ -0,0 +1,68 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/aggregate.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/aggregate.rs:5:9: 5:10 + let mut _2: i32; // in scope 0 at $DIR/aggregate.rs:5:13: 5:24 + let mut _3: (i32, i32, i32); // in scope 0 at $DIR/aggregate.rs:5:13: 5:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/aggregate.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/aggregate.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/aggregate.rs:5:13: 5:24 + StorageLive(_3); // scope 0 at $DIR/aggregate.rs:5:13: 5:22 + _3 = (const 0i32, const 1i32, const 2i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:22 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/aggregate.rs:5:14: 5:15 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/aggregate.rs:5:17: 5:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/aggregate.rs:5:20: 5:21 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } +- _2 = (_3.1: i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:24 +- _1 = Add(move _2, const 0i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:28 ++ _2 = const 1i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:24 + // ty::Const + // + ty: i32 +- // + val: Value(Scalar(0x00000000)) ++ // + val: Value(Scalar(0x00000001)) + // mir::Constant +- // + span: $DIR/aggregate.rs:5:27: 5:28 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } ++ // + span: $DIR/aggregate.rs:5:13: 5:24 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } ++ _1 = const 1i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:28 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/aggregate.rs:5:13: 5:28 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + StorageDead(_2); // scope 0 at $DIR/aggregate.rs:5:27: 5:28 + StorageDead(_3); // scope 0 at $DIR/aggregate.rs:5:28: 5:29 + _0 = const (); // scope 0 at $DIR/aggregate.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/aggregate.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/aggregate.rs:6:1: 6:2 + return; // scope 0 at $DIR/aggregate.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/array_index.rs b/src/test/mir-opt/const_prop/array_index.rs index 406585b5cab8e..9301e6f5d0393 100644 --- a/src/test/mir-opt/const_prop/array_index.rs +++ b/src/test/mir-opt/const_prop/array_index.rs @@ -1,33 +1,6 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x: u32 = [0, 1, 2, 3][2]; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _2 = [const 0u32, const 1u32, const 2u32, const 3u32]; -// ... -// _3 = const 2usize; -// _4 = const 4usize; -// _5 = Lt(_3, _4); -// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb1; -// } -// bb1: { -// _1 = _2[_3]; -// ... -// return; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _5 = const true; -// assert(const true, "index out of bounds: the len is move _4 but the index is _3") -> bb1; -// } -// bb1: { -// _1 = const 2u32; -// ... -// return; -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/array_index/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/array_index/32bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..e24751d39a7df --- /dev/null +++ b/src/test/mir-opt/const_prop/array_index/32bit/rustc.main.ConstProp.diff @@ -0,0 +1,98 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array_index.rs:4:11: 4:11 + let _1: u32 as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/array_index.rs:5:9: 5:10 + let mut _2: [u32; 4]; // in scope 0 at $DIR/array_index.rs:5:18: 5:30 + let _3: usize; // in scope 0 at $DIR/array_index.rs:5:31: 5:32 + let mut _4: usize; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + let mut _5: bool; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array_index.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array_index.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/array_index.rs:5:18: 5:30 + _2 = [const 0u32, const 1u32, const 2u32, const 3u32]; // scope 0 at $DIR/array_index.rs:5:18: 5:30 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/array_index.rs:5:19: 5:20 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/array_index.rs:5:22: 5:23 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/array_index.rs:5:25: 5:26 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/array_index.rs:5:28: 5:29 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_3); // scope 0 at $DIR/array_index.rs:5:31: 5:32 + _3 = const 2usize; // scope 0 at $DIR/array_index.rs:5:31: 5:32 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/array_index.rs:5:31: 5:32 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } + _4 = const 4usize; // scope 0 at $DIR/array_index.rs:5:18: 5:33 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/array_index.rs:5:18: 5:33 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000004)) } +- _5 = Lt(_3, _4); // scope 0 at $DIR/array_index.rs:5:18: 5:33 +- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _5 = const true; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/array_index.rs:5:18: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/array_index.rs:5:18: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb1: { +- _1 = _2[_3]; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _1 = const 2u32; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/array_index.rs:5:18: 5:33 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageDead(_3); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/array_index.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/array_index.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/array_index.rs:6:1: 6:2 + return; // scope 0 at $DIR/array_index.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/array_index/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/array_index/64bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..ad9992bb94905 --- /dev/null +++ b/src/test/mir-opt/const_prop/array_index/64bit/rustc.main.ConstProp.diff @@ -0,0 +1,98 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array_index.rs:4:11: 4:11 + let _1: u32 as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/array_index.rs:5:9: 5:10 + let mut _2: [u32; 4]; // in scope 0 at $DIR/array_index.rs:5:18: 5:30 + let _3: usize; // in scope 0 at $DIR/array_index.rs:5:31: 5:32 + let mut _4: usize; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + let mut _5: bool; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array_index.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array_index.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/array_index.rs:5:18: 5:30 + _2 = [const 0u32, const 1u32, const 2u32, const 3u32]; // scope 0 at $DIR/array_index.rs:5:18: 5:30 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/array_index.rs:5:19: 5:20 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/array_index.rs:5:22: 5:23 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/array_index.rs:5:25: 5:26 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/array_index.rs:5:28: 5:29 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_3); // scope 0 at $DIR/array_index.rs:5:31: 5:32 + _3 = const 2usize; // scope 0 at $DIR/array_index.rs:5:31: 5:32 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000002)) + // mir::Constant + // + span: $DIR/array_index.rs:5:31: 5:32 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } + _4 = const 4usize; // scope 0 at $DIR/array_index.rs:5:18: 5:33 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000004)) + // mir::Constant + // + span: $DIR/array_index.rs:5:18: 5:33 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000004)) } +- _5 = Lt(_3, _4); // scope 0 at $DIR/array_index.rs:5:18: 5:33 +- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _5 = const true; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/array_index.rs:5:18: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/array_index.rs:5:18: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb1: { +- _1 = _2[_3]; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _1 = const 2u32; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/array_index.rs:5:18: 5:33 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageDead(_3); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/array_index.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/array_index.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/array_index.rs:6:1: 6:2 + return; // scope 0 at $DIR/array_index.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_div_by_zero.rs b/src/test/mir-opt/const_prop/bad_op_div_by_zero.rs new file mode 100644 index 0000000000000..0cd1f37c9a787 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_div_by_zero.rs @@ -0,0 +1,6 @@ +// EMIT_MIR rustc.main.ConstProp.diff +#[allow(unconditional_panic)] +fn main() { + let y = 0; + let _z = 1 / y; +} diff --git a/src/test/mir-opt/const_prop/bad_op_div_by_zero/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_div_by_zero/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..333bf0e320b6e --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_div_by_zero/rustc.main.ConstProp.diff @@ -0,0 +1,117 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_div_by_zero.rs:3:11: 3:11 + let _1: i32; // in scope 0 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 + let mut _3: i32; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 + let mut _4: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + let mut _5: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + let mut _6: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + let mut _7: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + scope 1 { + debug y => _1; // in scope 1 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 + let _2: i32; // in scope 1 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 + scope 2 { + debug _z => _2; // in scope 2 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 + _1 = const 0i32; // scope 0 at $DIR/bad_op_div_by_zero.rs:4:13: 4:14 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/bad_op_div_by_zero.rs:4:13: 4:14 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageLive(_2); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 + StorageLive(_3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 +- _3 = _1; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 +- _4 = Eq(_3, const 0i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ _3 = const 0i32; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant +- // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // + span: $DIR/bad_op_div_by_zero.rs:5:18: 5:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } ++ _4 = const true; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + assert(!move _4, "attempt to divide by zero") -> bb1; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + } + + bb1: { +- _5 = Eq(_3, const -1i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ _5 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0xffffffff)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 +- // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } +- _6 = Eq(const 1i32, const std::i32::MIN); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ _6 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000001)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant +- // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:15 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } ++ // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ _7 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x80000000)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 +- // + literal: Const { ty: i32, val: Value(Scalar(0x80000000)) } +- _7 = BitAnd(move _5, move _6); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 +- assert(!move _7, "attempt to divide with overflow") -> bb2; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ assert(!const false, "attempt to divide with overflow") -> bb2; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb2: { + _2 = Div(const 1i32, move _3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:15 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + StorageDead(_3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 + _0 = const (); // scope 0 at $DIR/bad_op_div_by_zero.rs:3:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/bad_op_div_by_zero.rs:3:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/bad_op_div_by_zero.rs:6:1: 6:2 + StorageDead(_1); // scope 0 at $DIR/bad_op_div_by_zero.rs:6:1: 6:2 + return; // scope 0 at $DIR/bad_op_div_by_zero.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_mod_by_zero.rs b/src/test/mir-opt/const_prop/bad_op_mod_by_zero.rs new file mode 100644 index 0000000000000..26bccbb90ec82 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_mod_by_zero.rs @@ -0,0 +1,6 @@ +// EMIT_MIR rustc.main.ConstProp.diff +#[allow(unconditional_panic)] +fn main() { + let y = 0; + let _z = 1 % y; +} diff --git a/src/test/mir-opt/const_prop/bad_op_mod_by_zero/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_mod_by_zero/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..c081b46366e9b --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_mod_by_zero/rustc.main.ConstProp.diff @@ -0,0 +1,117 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_mod_by_zero.rs:3:11: 3:11 + let _1: i32; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 + let mut _3: i32; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 + let mut _4: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + let mut _5: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + let mut _6: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + let mut _7: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + scope 1 { + debug y => _1; // in scope 1 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 + let _2: i32; // in scope 1 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 + scope 2 { + debug _z => _2; // in scope 2 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 + _1 = const 0i32; // scope 0 at $DIR/bad_op_mod_by_zero.rs:4:13: 4:14 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/bad_op_mod_by_zero.rs:4:13: 4:14 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageLive(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 + StorageLive(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 +- _3 = _1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 +- _4 = Eq(_3, const 0i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ _3 = const 0i32; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant +- // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // + span: $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } ++ _4 = const true; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + assert(!move _4, "attempt to calculate the remainder with a divisor of zero") -> bb1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + } + + bb1: { +- _5 = Eq(_3, const -1i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ _5 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0xffffffff)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 +- // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } +- _6 = Eq(const 1i32, const std::i32::MIN); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ _6 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000001)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant +- // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:15 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } ++ // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ _7 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x80000000)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 +- // + literal: Const { ty: i32, val: Value(Scalar(0x80000000)) } +- _7 = BitAnd(move _5, move _6); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 +- assert(!move _7, "attempt to calculate the remainder with overflow") -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ assert(!const false, "attempt to calculate the remainder with overflow") -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb2: { + _2 = Rem(const 1i32, move _3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:15 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + StorageDead(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 + _0 = const (); // scope 0 at $DIR/bad_op_mod_by_zero.rs:3:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/bad_op_mod_by_zero.rs:3:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:6:1: 6:2 + StorageDead(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:6:1: 6:2 + return; // scope 0 at $DIR/bad_op_mod_by_zero.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs new file mode 100644 index 0000000000000..e517e467c372c --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs @@ -0,0 +1,9 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.ConstProp.diff +#[allow(unconditional_panic)] +fn main() { + let a: *const [_] = &[1, 2, 3]; + unsafe { + let _b = (*a)[3]; + } +} diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/32bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..8ecb77752bb39 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/32bit/rustc.main.ConstProp.diff @@ -0,0 +1,83 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 + let _1: *const [i32] as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 + let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + scope 2 { + let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + scope 3 { + debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // ty::Const + // + ty: &[i32; 3] + // + val: Unevaluated(DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // + literal: Const { ty: &[i32; 3], val: Unevaluated(DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), [], Some(promoted[0])) } + _3 = _9; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:34: 5:35 + StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:35: 5:36 + StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + StorageLive(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + _6 = const 3usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } +- _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 +- _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ _7 = const 3usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x00000003)) ++ // mir::Constant ++ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } ++ _8 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + } + + bb1: { + _5 = (*_1)[_6]; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + StorageDead(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:25: 7:26 + _0 = const (); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:8:5: 8:6 + StorageDead(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:1: 9:2 + return; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/64bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..2778ec02724df --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/64bit/rustc.main.ConstProp.diff @@ -0,0 +1,83 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 + let _1: *const [i32] as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 + let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + scope 2 { + let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + scope 3 { + debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // ty::Const + // + ty: &[i32; 3] + // + val: Unevaluated(DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // + literal: Const { ty: &[i32; 3], val: Unevaluated(DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), [], Some(promoted[0])) } + _3 = _9; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:34: 5:35 + StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:35: 5:36 + StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + StorageLive(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + _6 = const 3usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000003)) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } +- _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 +- _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ _7 = const 3usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x0000000000000003)) ++ // mir::Constant ++ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } ++ _8 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + } + + bb1: { + _5 = (*_1)[_6]; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + StorageDead(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:25: 7:26 + _0 = const (); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:8:5: 8:6 + StorageDead(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:1: 9:2 + return; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/boxes.rs b/src/test/mir-opt/const_prop/boxes.rs index cf134dadf2789..d45804ebb6cf2 100644 --- a/src/test/mir-opt/const_prop/boxes.rs +++ b/src/test/mir-opt/const_prop/boxes.rs @@ -7,50 +7,7 @@ // Note: this test verifies that we, in fact, do not const prop `box` +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x = *(box 42) + 0; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _4 = Box(i32); -// (*_4) = const 42i32; -// _3 = move _4; -// ... -// _2 = (*_3); -// _1 = Add(move _2, const 0i32); -// ... -// drop(_3) -> [return: bb2, unwind: bb1]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// ... -// _0 = (); -// ... -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _4 = Box(i32); -// (*_4) = const 42i32; -// _3 = move _4; -// ... -// _2 = (*_3); -// _1 = Add(move _2, const 0i32); -// ... -// drop(_3) -> [return: bb2, unwind: bb1]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// ... -// _0 = (); -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/boxes/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/boxes/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..c9b082ea6e30e --- /dev/null +++ b/src/test/mir-opt/const_prop/boxes/rustc.main.ConstProp.diff @@ -0,0 +1,58 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/boxes.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/boxes.rs:12:9: 12:10 + let mut _2: i32; // in scope 0 at $DIR/boxes.rs:12:13: 12:22 + let mut _3: std::boxed::Box; // in scope 0 at $DIR/boxes.rs:12:14: 12:22 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/boxes.rs:12:14: 12:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/boxes.rs:12:9: 12:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/boxes.rs:12:9: 12:10 + StorageLive(_2); // scope 0 at $DIR/boxes.rs:12:13: 12:22 + StorageLive(_3); // scope 0 at $DIR/boxes.rs:12:14: 12:22 + StorageLive(_4); // scope 0 at $DIR/boxes.rs:12:14: 12:22 + _4 = Box(i32); // scope 0 at $DIR/boxes.rs:12:14: 12:22 + (*_4) = const 42i32; // scope 0 at $DIR/boxes.rs:12:19: 12:21 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/boxes.rs:12:19: 12:21 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + _3 = move _4; // scope 0 at $DIR/boxes.rs:12:14: 12:22 + StorageDead(_4); // scope 0 at $DIR/boxes.rs:12:21: 12:22 + _2 = (*_3); // scope 0 at $DIR/boxes.rs:12:13: 12:22 + _1 = Add(move _2, const 0i32); // scope 0 at $DIR/boxes.rs:12:13: 12:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/boxes.rs:12:25: 12:26 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageDead(_2); // scope 0 at $DIR/boxes.rs:12:25: 12:26 + drop(_3) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/boxes.rs:12:26: 12:27 + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/boxes.rs:12:26: 12:27 + _0 = const (); // scope 0 at $DIR/boxes.rs:11:11: 13:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/boxes.rs:11:11: 13:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/boxes.rs:13:1: 13:2 + return; // scope 0 at $DIR/boxes.rs:13:2: 13:2 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/boxes.rs:11:1: 13:2 + } + } + diff --git a/src/test/mir-opt/const_prop/cast.rs b/src/test/mir-opt/const_prop/cast.rs index 9cfbfebdcc3df..2af5f32a66832 100644 --- a/src/test/mir-opt/const_prop/cast.rs +++ b/src/test/mir-opt/const_prop/cast.rs @@ -1,49 +1,7 @@ +// EMIT_MIR rustc.main.ConstProp.diff + fn main() { let x = 42u8 as u32; let y = 42u32 as u8; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// let mut _0: (); -// let _1: u32; -// scope 1 { -// debug x => _1; -// let _2: u8; -// scope 2 { -// debug y => _2; -// } -// } -// bb0: { -// StorageLive(_1); -// _1 = const 42u8 as u32 (Misc); -// StorageLive(_2); -// _2 = const 42u32 as u8 (Misc); -// _0 = (); -// StorageDead(_2); -// StorageDead(_1); -// return; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// let mut _0: (); -// let _1: u32; -// scope 1 { -// debug x => _1; -// let _2: u8; -// scope 2 { -// debug y => _2; -// } -// } -// bb0: { -// StorageLive(_1); -// _1 = const 42u32; -// StorageLive(_2); -// _2 = const 42u8; -// _0 = (); -// StorageDead(_2); -// StorageDead(_1); -// return; -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..58c27c5a20f8c --- /dev/null +++ b/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff @@ -0,0 +1,54 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/cast.rs:3:11: 3:11 + let _1: u32; // in scope 0 at $DIR/cast.rs:4:9: 4:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/cast.rs:4:9: 4:10 + let _2: u8; // in scope 1 at $DIR/cast.rs:6:9: 6:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/cast.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/cast.rs:4:9: 4:10 +- _1 = const 42u8 as u32 (Misc); // scope 0 at $DIR/cast.rs:4:13: 4:24 ++ _1 = const 42u32; // scope 0 at $DIR/cast.rs:4:13: 4:24 + // ty::Const +- // + ty: u8 +- // + val: Value(Scalar(0x2a)) +- // mir::Constant +- // + span: $DIR/cast.rs:4:13: 4:17 +- // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } +- StorageLive(_2); // scope 1 at $DIR/cast.rs:6:9: 6:10 +- _2 = const 42u32 as u8 (Misc); // scope 1 at $DIR/cast.rs:6:13: 6:24 +- // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant +- // + span: $DIR/cast.rs:6:13: 6:18 ++ // + span: $DIR/cast.rs:4:13: 4:24 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } ++ StorageLive(_2); // scope 1 at $DIR/cast.rs:6:9: 6:10 ++ _2 = const 42u8; // scope 1 at $DIR/cast.rs:6:13: 6:24 ++ // ty::Const ++ // + ty: u8 ++ // + val: Value(Scalar(0x2a)) ++ // mir::Constant ++ // + span: $DIR/cast.rs:6:13: 6:24 ++ // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } + _0 = const (); // scope 0 at $DIR/cast.rs:3:11: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/cast.rs:3:11: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/cast.rs:7:1: 7:2 + StorageDead(_1); // scope 0 at $DIR/cast.rs:7:1: 7:2 + return; // scope 0 at $DIR/cast.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/checked_add.rs b/src/test/mir-opt/const_prop/checked_add.rs index fe98cf24eec00..439bd2df91f57 100644 --- a/src/test/mir-opt/const_prop/checked_add.rs +++ b/src/test/mir-opt/const_prop/checked_add.rs @@ -1,21 +1,6 @@ // compile-flags: -C overflow-checks=on +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x: u32 = 1 + 1; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _2 = CheckedAdd(const 1u32, const 1u32); -// assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _2 = (const 2u32, const false); -// assert(!const false, "attempt to add with overflow") -> bb1; -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/checked_add/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/checked_add/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..92add8bafdc03 --- /dev/null +++ b/src/test/mir-opt/const_prop/checked_add/rustc.main.ConstProp.diff @@ -0,0 +1,65 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/checked_add.rs:4:11: 4:11 + let _1: u32 as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/checked_add.rs:5:9: 5:10 + let mut _2: (u32, bool); // in scope 0 at $DIR/checked_add.rs:5:18: 5:23 + scope 1 { + debug x => _1; // in scope 1 at $DIR/checked_add.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/checked_add.rs:5:9: 5:10 +- _2 = CheckedAdd(const 1u32, const 1u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ _2 = (const 2u32, const false); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 + // ty::Const + // + ty: u32 +- // + val: Value(Scalar(0x00000001)) ++ // + val: Value(Scalar(0x00000002)) + // mir::Constant +- // + span: $DIR/checked_add.rs:5:18: 5:19 +- // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } ++ // + span: $DIR/checked_add.rs:5:18: 5:23 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + // ty::Const +- // + ty: u32 +- // + val: Value(Scalar(0x00000001)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant +- // + span: $DIR/checked_add.rs:5:22: 5:23 +- // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } +- assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ // + span: $DIR/checked_add.rs:5:18: 5:23 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ assert(!const false, "attempt to add with overflow") -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/checked_add.rs:5:18: 5:23 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb1: { +- _1 = move (_2.0: u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ _1 = const 2u32; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/checked_add.rs:5:18: 5:23 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + _0 = const (); // scope 0 at $DIR/checked_add.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/checked_add.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/checked_add.rs:6:1: 6:2 + return; // scope 0 at $DIR/checked_add.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs index 3c8c0ff449345..c6c006c080912 100644 --- a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs +++ b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs @@ -1,31 +1,9 @@ #[inline(never)] fn read(_: usize) { } +// EMIT_MIR rustc.main.ConstProp.diff fn main() { const FOO: &i32 = &1; let x = FOO as *const i32 as usize; read(x); } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _2 = &raw const (*_3); -// _1 = move _2 as usize (Misc); -// ... -// _5 = _1; -// _4 = const read(move _5) -> bb1; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _3 = const main::FOO; -// _2 = &raw const (*_3); -// _1 = move _2 as usize (Misc); -// ... -// _5 = _1; -// _4 = const read(move _5) -> bb1; -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/const_prop_fails_gracefully/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..6c5fe7454b4ed --- /dev/null +++ b/src/test/mir-opt/const_prop/const_prop_fails_gracefully/rustc.main.ConstProp.diff @@ -0,0 +1,56 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_prop_fails_gracefully.rs:5:11: 5:11 + let _1: usize; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 + let mut _2: *const i32; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:30 + let _3: &i32; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + let _4: (); // in scope 0 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 + let mut _5: usize; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 + scope 1 { + debug x => _1; // in scope 1 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 + StorageLive(_2); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:30 + StorageLive(_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + _3 = const main::FOO; // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(DefId(0:5 ~ const_prop_fails_gracefully[317d]::main[0]::FOO[0]), [], None) + // mir::Constant + // + span: $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + // + literal: Const { ty: &i32, val: Unevaluated(DefId(0:5 ~ const_prop_fails_gracefully[317d]::main[0]::FOO[0]), [], None) } + _2 = &raw const (*_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + _1 = move _2 as usize (Misc); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:39 + StorageDead(_2); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:38: 7:39 + StorageDead(_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:39: 7:40 + StorageLive(_4); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 + StorageLive(_5); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 + _5 = _1; // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 + _4 = const read(move _5) -> bb1; // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 + // ty::Const + // + ty: fn(usize) {read} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_prop_fails_gracefully.rs:8:5: 8:9 + // + literal: Const { ty: fn(usize) {read}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_5); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:11: 8:12 + StorageDead(_4); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:12: 8:13 + _0 = const (); // scope 0 at $DIR/const_prop_fails_gracefully.rs:5:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_prop_fails_gracefully.rs:5:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/const_prop_fails_gracefully.rs:9:1: 9:2 + return; // scope 0 at $DIR/const_prop_fails_gracefully.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/control-flow-simplification.rs b/src/test/mir-opt/const_prop/control-flow-simplification.rs index 0e9f33b15e2cc..1071590dd9e51 100644 --- a/src/test/mir-opt/const_prop/control-flow-simplification.rs +++ b/src/test/mir-opt/const_prop/control-flow-simplification.rs @@ -6,6 +6,8 @@ trait NeedsDrop:Sized{ impl NeedsDrop for This{} +// EMIT_MIR rustc.hello.ConstProp.diff +// EMIT_MIR rustc.hello.PreCodegen.before.mir fn hello(){ if ::NEEDS { panic!() @@ -16,49 +18,3 @@ pub fn main() { hello::<()>(); hello::>(); } - -// END RUST SOURCE -// START rustc.hello.ConstProp.before.mir -// let mut _0: (); -// let mut _1: bool; -// let mut _2: !; -// bb0: { -// StorageLive(_1); -// _1 = const ::NEEDS; -// switchInt(_1) -> [false: bb1, otherwise: bb2]; -// } -// bb1: { -// _0 = (); -// StorageDead(_1); -// return; -// } -// bb2: { -// StorageLive(_2); -// const std::rt::begin_panic::<&str>(const "explicit panic"); -// } -// END rustc.hello.ConstProp.before.mir -// START rustc.hello.ConstProp.after.mir -// let mut _0: (); -// let mut _1: bool; -// let mut _2: !; -// bb0: { -// StorageLive(_1); -// _1 = const false; -// switchInt(const false) -> [false: bb1, otherwise: bb2]; -// } -// bb1: { -// _0 = (); -// StorageDead(_1); -// return; -// } -// bb2: { -// StorageLive(_2); -// const std::rt::begin_panic::<&str>(const "explicit panic"); -// } -// END rustc.hello.ConstProp.after.mir -// START rustc.hello.PreCodegen.before.mir -// let mut _0: (); -// bb0: { -// return; -// } -// END rustc.hello.PreCodegen.before.mir diff --git a/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.ConstProp.diff b/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.ConstProp.diff new file mode 100644 index 0000000000000..b4d1f087391f3 --- /dev/null +++ b/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.ConstProp.diff @@ -0,0 +1,60 @@ +- // MIR for `hello` before ConstProp ++ // MIR for `hello` after ConstProp + + fn hello() -> () { + let mut _0: (); // return place in scope 0 at $DIR/control-flow-simplification.rs:11:14: 11:14 + let mut _1: bool; // in scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 + let mut _2: !; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL + + bb0: { + StorageLive(_1); // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 +- _1 = const ::NEEDS; // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 ++ _1 = const false; // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 + // ty::Const + // + ty: bool +- // + val: Unevaluated(DefId(0:4 ~ control_flow_simplification[317d]::NeedsDrop[0]::NEEDS[0]), [bool], None) ++ // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/control-flow-simplification.rs:12:8: 12:21 +- // + literal: Const { ty: bool, val: Unevaluated(DefId(0:4 ~ control_flow_simplification[317d]::NeedsDrop[0]::NEEDS[0]), [bool], None) } +- switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/control-flow-simplification.rs:12:5: 14:6 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb1: { + _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/control-flow-simplification.rs:12:5: 14:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/control-flow-simplification.rs:15:1: 15:2 + return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 + } + + bb2: { + StorageLive(_2); // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL + const std::rt::begin_panic::<&str>(const "explicit panic"); // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL + // ty::Const + // + ty: fn(&str) -> ! {std::rt::begin_panic::<&str>} + // + val: Value(Scalar()) + // mir::Constant + // + span: $SRC_DIR/libstd/macros.rs:LL:COL + // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar()) } + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) + // mir::Constant + // + span: $SRC_DIR/libstd/macros.rs:LL:COL + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } + } + } + diff --git a/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.PreCodegen.before.mir b/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.PreCodegen.before.mir new file mode 100644 index 0000000000000..3569b9897f96c --- /dev/null +++ b/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.PreCodegen.before.mir @@ -0,0 +1,16 @@ +// MIR for `hello` before PreCodegen + +fn hello() -> () { + let mut _0: (); // return place in scope 0 at $DIR/control-flow-simplification.rs:11:14: 11:14 + + bb0: { + _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/control-flow-simplification.rs:12:5: 14:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/const_prop/discriminant.rs b/src/test/mir-opt/const_prop/discriminant.rs index 636aa1af65333..04541b94ad780 100644 --- a/src/test/mir-opt/const_prop/discriminant.rs +++ b/src/test/mir-opt/const_prop/discriminant.rs @@ -1,53 +1,7 @@ // compile-flags: -O +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x = (if let Some(true) = Some(true) { 42 } else { 10 }) + 0; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _3 = std::option::Option::::Some(const true,); -// _4 = discriminant(_3); -// switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; -// } -// bb1: { -// _2 = const 10i32; -// goto -> bb4; -// } -// bb2: { -// switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; -// } -// bb3: { -// _2 = const 42i32; -// goto -> bb4; -// } -// bb4: { -// _1 = Add(move _2, const 0i32); -// ... -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _3 = const {transmute(0x01): std::option::Option}; -// _4 = const 1isize; -// switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; -// } -// bb1: { -// _2 = const 10i32; -// goto -> bb4; -// } -// bb2: { -// switchInt(const true) -> [false: bb1, otherwise: bb3]; -// } -// bb3: { -// _2 = const 42i32; -// goto -> bb4; -// } -// bb4: { -// _1 = Add(move _2, const 0i32); -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..9979ea9549891 --- /dev/null +++ b/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff @@ -0,0 +1,101 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:5:11: 5:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:6:9: 6:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:6:13: 6:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:6:34: 6:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:6:21: 6:31 + scope 1 { + debug x => _1; // in scope 1 at $DIR/discriminant.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 +- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 ++ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 + // ty::Const +- // + ty: bool ++ // + ty: std::option::Option + // + val: Value(Scalar(0x01)) + // mir::Constant +- // + span: $DIR/discriminant.rs:6:39: 6:43 +- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:6:21: 6:31 +- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ // + span: $DIR/discriminant.rs:6:34: 6:44 ++ // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } ++ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } ++ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } + } + + bb1: { + _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:6:59: 6:61 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000000a)) + // mir::Constant + // + span: $DIR/discriminant.rs:6:59: 6:61 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + } + + bb2: { +- switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 ++ switchInt(const true) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:6:26: 6:30 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb3: { + _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:6:47: 6:49 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/discriminant.rs:6:47: 6:49 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + } + + bb4: { + _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:6:13: 6:68 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/discriminant.rs:6:67: 6:68 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:6:67: 6:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:6:68: 6:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:5:11: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/discriminant.rs:5:11: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:7:1: 7:2 + return; // scope 0 at $DIR/discriminant.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..ec0341e3de251 --- /dev/null +++ b/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff @@ -0,0 +1,101 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:5:11: 5:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:6:9: 6:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:6:13: 6:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:6:34: 6:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:6:21: 6:31 + scope 1 { + debug x => _1; // in scope 1 at $DIR/discriminant.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 +- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 ++ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 + // ty::Const +- // + ty: bool ++ // + ty: std::option::Option + // + val: Value(Scalar(0x01)) + // mir::Constant +- // + span: $DIR/discriminant.rs:6:39: 6:43 +- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:6:21: 6:31 +- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ // + span: $DIR/discriminant.rs:6:34: 6:44 ++ // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } ++ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x0000000000000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } ++ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x0000000000000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } + } + + bb1: { + _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:6:59: 6:61 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000000a)) + // mir::Constant + // + span: $DIR/discriminant.rs:6:59: 6:61 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + } + + bb2: { +- switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 ++ switchInt(const true) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:6:26: 6:30 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb3: { + _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:6:47: 6:49 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/discriminant.rs:6:47: 6:49 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + } + + bb4: { + _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:6:13: 6:68 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/discriminant.rs:6:67: 6:68 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:6:67: 6:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:6:68: 6:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:5:11: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/discriminant.rs:5:11: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:7:1: 7:2 + return; // scope 0 at $DIR/discriminant.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/indirect.rs b/src/test/mir-opt/const_prop/indirect.rs index b4ee18ed1b53b..961e4447d8b6b 100644 --- a/src/test/mir-opt/const_prop/indirect.rs +++ b/src/test/mir-opt/const_prop/indirect.rs @@ -1,23 +1,6 @@ // compile-flags: -C overflow-checks=on +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x = (2u32 as u8) + 1; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _2 = const 2u32 as u8 (Misc); -// _3 = CheckedAdd(move _2, const 1u8); -// assert(!move (_3.1: bool), "attempt to add with overflow") -> bb1; -//} -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _2 = const 2u8; -// _3 = (const 3u8, const false); -// assert(!const false, "attempt to add with overflow") -> bb1; -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/indirect/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/indirect/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..941cde9172a6f --- /dev/null +++ b/src/test/mir-opt/const_prop/indirect/rustc.main.ConstProp.diff @@ -0,0 +1,76 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/indirect.rs:4:11: 4:11 + let _1: u8; // in scope 0 at $DIR/indirect.rs:5:9: 5:10 + let mut _2: u8; // in scope 0 at $DIR/indirect.rs:5:13: 5:25 + let mut _3: (u8, bool); // in scope 0 at $DIR/indirect.rs:5:13: 5:29 + scope 1 { + debug x => _1; // in scope 1 at $DIR/indirect.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/indirect.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/indirect.rs:5:13: 5:25 +- _2 = const 2u32 as u8 (Misc); // scope 0 at $DIR/indirect.rs:5:13: 5:25 ++ _2 = const 2u8; // scope 0 at $DIR/indirect.rs:5:13: 5:25 + // ty::Const +- // + ty: u32 +- // + val: Value(Scalar(0x00000002)) ++ // + ty: u8 ++ // + val: Value(Scalar(0x02)) + // mir::Constant +- // + span: $DIR/indirect.rs:5:14: 5:18 +- // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } +- _3 = CheckedAdd(move _2, const 1u8); // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ // + span: $DIR/indirect.rs:5:13: 5:25 ++ // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } ++ _3 = (const 3u8, const false); // scope 0 at $DIR/indirect.rs:5:13: 5:29 + // ty::Const + // + ty: u8 +- // + val: Value(Scalar(0x01)) ++ // + val: Value(Scalar(0x03)) + // mir::Constant +- // + span: $DIR/indirect.rs:5:28: 5:29 +- // + literal: Const { ty: u8, val: Value(Scalar(0x01)) } +- assert(!move (_3.1: bool), "attempt to add with overflow") -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ // + span: $DIR/indirect.rs:5:13: 5:29 ++ // + literal: Const { ty: u8, val: Value(Scalar(0x03)) } ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/indirect.rs:5:13: 5:29 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ assert(!const false, "attempt to add with overflow") -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/indirect.rs:5:13: 5:29 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb1: { +- _1 = move (_3.0: u8); // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ _1 = const 3u8; // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ // ty::Const ++ // + ty: u8 ++ // + val: Value(Scalar(0x03)) ++ // mir::Constant ++ // + span: $DIR/indirect.rs:5:13: 5:29 ++ // + literal: Const { ty: u8, val: Value(Scalar(0x03)) } + StorageDead(_2); // scope 0 at $DIR/indirect.rs:5:28: 5:29 + _0 = const (); // scope 0 at $DIR/indirect.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/indirect.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/indirect.rs:6:1: 6:2 + return; // scope 0 at $DIR/indirect.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/issue-66971.rs b/src/test/mir-opt/const_prop/issue-66971.rs index f332bb895095b..50a1405b77b4c 100644 --- a/src/test/mir-opt/const_prop/issue-66971.rs +++ b/src/test/mir-opt/const_prop/issue-66971.rs @@ -11,28 +11,7 @@ fn encode(this: ((), u8, u8)) { assert!(this.2 == 0); } +// EMIT_MIR rustc.main.ConstProp.diff fn main() { encode(((), 0, 0)); } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _3 = (); -// _2 = (move _3, const 0u8, const 0u8); -// ... -// _1 = const encode(move _2) -> bb1; -// ... -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _3 = const (); -// _2 = (move _3, const 0u8, const 0u8); -// ... -// _1 = const encode(move _2) -> bb1; -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/issue-66971/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue-66971/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..c26494bdc1042 --- /dev/null +++ b/src/test/mir-opt/const_prop/issue-66971/rustc.main.ConstProp.diff @@ -0,0 +1,58 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-66971.rs:15:11: 15:11 + let _1: (); // in scope 0 at $DIR/issue-66971.rs:16:5: 16:23 + let mut _2: ((), u8, u8); // in scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + let mut _3: (); // in scope 0 at $DIR/issue-66971.rs:16:13: 16:15 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-66971.rs:16:5: 16:23 + StorageLive(_2); // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + StorageLive(_3); // scope 0 at $DIR/issue-66971.rs:16:13: 16:15 +- _3 = (); // scope 0 at $DIR/issue-66971.rs:16:13: 16:15 ++ _3 = const (); // scope 0 at $DIR/issue-66971.rs:16:13: 16:15 ++ // ty::Const ++ // + ty: () ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/issue-66971.rs:16:13: 16:15 ++ // + literal: Const { ty: (), val: Value(Scalar()) } + _2 = (move _3, const 0u8, const 0u8); // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-66971.rs:16:17: 16:18 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-66971.rs:16:20: 16:21 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + StorageDead(_3); // scope 0 at $DIR/issue-66971.rs:16:21: 16:22 + _1 = const encode(move _2) -> bb1; // scope 0 at $DIR/issue-66971.rs:16:5: 16:23 + // ty::Const + // + ty: fn(((), u8, u8)) {encode} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-66971.rs:16:5: 16:11 + // + literal: Const { ty: fn(((), u8, u8)) {encode}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/issue-66971.rs:16:22: 16:23 + StorageDead(_1); // scope 0 at $DIR/issue-66971.rs:16:23: 16:24 + _0 = const (); // scope 0 at $DIR/issue-66971.rs:15:11: 17:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-66971.rs:15:11: 17:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/issue-66971.rs:17:2: 17:2 + } + } + diff --git a/src/test/mir-opt/const_prop/issue-67019.rs b/src/test/mir-opt/const_prop/issue-67019.rs index c6d753a1209cd..3c832eb134402 100644 --- a/src/test/mir-opt/const_prop/issue-67019.rs +++ b/src/test/mir-opt/const_prop/issue-67019.rs @@ -6,29 +6,7 @@ fn test(this: ((u8, u8),)) { assert!((this.0).0 == 1); } +// EMIT_MIR rustc.main.ConstProp.diff fn main() { test(((1, 2),)); } - -// Important bit is parameter passing so we only check that below -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _3 = (const 1u8, const 2u8); -// _2 = (move _3,); -// ... -// _1 = const test(move _2) -> bb1; -// ... -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _3 = (const 1u8, const 2u8); -// _2 = (move _3,); -// ... -// _1 = const test(move _2) -> bb1; -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/issue-67019/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue-67019/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..e328febb407ae --- /dev/null +++ b/src/test/mir-opt/const_prop/issue-67019/rustc.main.ConstProp.diff @@ -0,0 +1,53 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-67019.rs:10:11: 10:11 + let _1: (); // in scope 0 at $DIR/issue-67019.rs:11:5: 11:20 + let mut _2: ((u8, u8),); // in scope 0 at $DIR/issue-67019.rs:11:10: 11:19 + let mut _3: (u8, u8); // in scope 0 at $DIR/issue-67019.rs:11:11: 11:17 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-67019.rs:11:5: 11:20 + StorageLive(_2); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 + StorageLive(_3); // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 + _3 = (const 1u8, const 2u8); // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x01)) + // mir::Constant +- // + span: $DIR/issue-67019.rs:11:12: 11:13 ++ // + span: $DIR/issue-67019.rs:11:11: 11:17 + // + literal: Const { ty: u8, val: Value(Scalar(0x01)) } + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x02)) + // mir::Constant +- // + span: $DIR/issue-67019.rs:11:15: 11:16 ++ // + span: $DIR/issue-67019.rs:11:11: 11:17 + // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } + _2 = (move _3,); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 + StorageDead(_3); // scope 0 at $DIR/issue-67019.rs:11:18: 11:19 + _1 = const test(move _2) -> bb1; // scope 0 at $DIR/issue-67019.rs:11:5: 11:20 + // ty::Const + // + ty: fn(((u8, u8),)) {test} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-67019.rs:11:5: 11:9 + // + literal: Const { ty: fn(((u8, u8),)) {test}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/issue-67019.rs:11:19: 11:20 + StorageDead(_1); // scope 0 at $DIR/issue-67019.rs:11:20: 11:21 + _0 = const (); // scope 0 at $DIR/issue-67019.rs:10:11: 12:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-67019.rs:10:11: 12:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/issue-67019.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable.rs b/src/test/mir-opt/const_prop/mutable_variable.rs new file mode 100644 index 0000000000000..b3a2d80fa950a --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable.rs @@ -0,0 +1,8 @@ +// compile-flags: -O + +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let mut x = 42; + x = 99; + let y = x; +} diff --git a/src/test/mir-opt/const_prop/mutable_variable/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..187c17454350a --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable/rustc.main.ConstProp.diff @@ -0,0 +1,52 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable.rs:4:11: 4:11 + let mut _1: i32; // in scope 0 at $DIR/mutable_variable.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable.rs:5:9: 5:14 + let _2: i32; // in scope 1 at $DIR/mutable_variable.rs:7:9: 7:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/mutable_variable.rs:7:9: 7:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable.rs:5:9: 5:14 + _1 = const 42i32; // scope 0 at $DIR/mutable_variable.rs:5:17: 5:19 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/mutable_variable.rs:5:17: 5:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + _1 = const 99i32; // scope 1 at $DIR/mutable_variable.rs:6:5: 6:11 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000063)) + // mir::Constant + // + span: $DIR/mutable_variable.rs:6:9: 6:11 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } + StorageLive(_2); // scope 1 at $DIR/mutable_variable.rs:7:9: 7:10 +- _2 = _1; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14 ++ _2 = const 99i32; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000063)) ++ // mir::Constant ++ // + span: $DIR/mutable_variable.rs:7:13: 7:14 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } + _0 = const (); // scope 0 at $DIR/mutable_variable.rs:4:11: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable.rs:4:11: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/mutable_variable.rs:8:1: 8:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable.rs:8:1: 8:2 + return; // scope 0 at $DIR/mutable_variable.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate.rs b/src/test/mir-opt/const_prop/mutable_variable_aggregate.rs new file mode 100644 index 0000000000000..3c5fb4574b61f --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate.rs @@ -0,0 +1,8 @@ +// compile-flags: -O + +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let mut x = (42, 43); + x.1 = 99; + let y = x; +} diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..cf432b2acc1c5 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate/rustc.main.ConstProp.diff @@ -0,0 +1,66 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 4:11 + let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 + let _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 + _1 = (const 42i32, const 43i32); // scope 0 at $DIR/mutable_variable_aggregate.rs:5:17: 5:25 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant +- // + span: $DIR/mutable_variable_aggregate.rs:5:18: 5:20 ++ // + span: $DIR/mutable_variable_aggregate.rs:5:17: 5:25 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002b)) + // mir::Constant +- // + span: $DIR/mutable_variable_aggregate.rs:5:22: 5:24 ++ // + span: $DIR/mutable_variable_aggregate.rs:5:17: 5:25 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) } + (_1.1: i32) = const 99i32; // scope 1 at $DIR/mutable_variable_aggregate.rs:6:5: 6:13 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000063)) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate.rs:6:11: 6:13 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } + StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 +- _2 = _1; // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 ++ _2 = (const 42i32, const 99i32); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x0000002a)) ++ // mir::Constant ++ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000063)) ++ // mir::Constant ++ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } + _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate.rs:4:11: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 + return; // scope 0 at $DIR/mutable_variable_aggregate.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs new file mode 100644 index 0000000000000..fc13cbf2abd56 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs @@ -0,0 +1,9 @@ +// compile-flags: -O + +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let mut x = (42, 43); + let z = &mut x; + z.1 = 99; + let y = x; +} diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..44203ac327ab1 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref/rustc.main.ConstProp.diff @@ -0,0 +1,58 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 4:11 + let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 + let _2: &mut (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 + scope 2 { + debug z => _2; // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 + let _3: (i32, i32); // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 + scope 3 { + debug y => _3; // in scope 3 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 + _1 = (const 42i32, const 43i32); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002b)) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) } + StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 + _2 = &mut _1; // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:13: 6:19 + ((*_2).1: i32) = const 99i32; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:7:5: 7:13 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000063)) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:7:11: 7:13 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } + StorageLive(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 + _3 = _1; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:13: 8:14 + _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 + StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 + return; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs new file mode 100644 index 0000000000000..4f43ec8c9470a --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs @@ -0,0 +1,14 @@ +// compile-flags: -O + +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let mut x: (i32, i32) = foo(); + x.1 = 99; + x.0 = 42; + let y = x.1; +} + +#[inline(never)] +fn foo() -> (i32, i32) { + unimplemented!() +} diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..6834bb6bdd4f7 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read/rustc.main.ConstProp.diff @@ -0,0 +1,62 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 4:11 + let mut _1: (i32, i32) as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 + let _2: i32; // in scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 + _1 = const foo() -> bb1; // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:29: 5:34 + // ty::Const + // + ty: fn() -> (i32, i32) {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_partial_read.rs:5:29: 5:32 + // + literal: Const { ty: fn() -> (i32, i32) {foo}, val: Value(Scalar()) } + } + + bb1: { + (_1.1: i32) = const 99i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:6:5: 6:13 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000063)) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_partial_read.rs:6:11: 6:13 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } + (_1.0: i32) = const 42i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:7:5: 7:13 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_partial_read.rs:7:11: 7:13 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 +- _2 = (_1.1: i32); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 ++ _2 = const 99i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000063)) ++ // mir::Constant ++ // + span: $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } + _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:9:1: 9:2 + return; // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_no_prop.rs b/src/test/mir-opt/const_prop/mutable_variable_no_prop.rs new file mode 100644 index 0000000000000..8c9cd00509622 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_no_prop.rs @@ -0,0 +1,12 @@ +// compile-flags: -O + +static mut STATIC: u32 = 42; + +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let mut x = 42; + unsafe { + x = STATIC; + } + let y = x; +} diff --git a/src/test/mir-opt/const_prop/mutable_variable_no_prop/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_no_prop/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..b7f1242d8d125 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_no_prop/rustc.main.ConstProp.diff @@ -0,0 +1,69 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_no_prop.rs:6:11: 6:11 + let mut _1: u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 + let _2: (); // in scope 0 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 + let mut _3: u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + let mut _4: *mut u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 + let _5: u32; // in scope 1 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 + scope 2 { + } + scope 3 { + debug y => _5; // in scope 3 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 + _1 = const 42u32; // scope 0 at $DIR/mutable_variable_no_prop.rs:7:17: 7:19 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/mutable_variable_no_prop.rs:7:17: 7:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + StorageLive(_2); // scope 1 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 + StorageLive(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + StorageLive(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + _4 = const {alloc0: *mut u32}; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + // ty::Const + // + ty: *mut u32 + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + // + literal: Const { ty: *mut u32, val: Value(Scalar(alloc0)) } + _3 = (*_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + _1 = move _3; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:9: 9:19 + StorageDead(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:18: 9:19 + StorageDead(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:19: 9:20 + _2 = const (); // scope 2 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_no_prop.rs:8:5: 10:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/mutable_variable_no_prop.rs:10:5: 10:6 + StorageLive(_5); // scope 1 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 + _5 = _1; // scope 1 at $DIR/mutable_variable_no_prop.rs:11:13: 11:14 + _0 = const (); // scope 0 at $DIR/mutable_variable_no_prop.rs:6:11: 12:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_no_prop.rs:6:11: 12:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_5); // scope 1 at $DIR/mutable_variable_no_prop.rs:12:1: 12:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_no_prop.rs:12:1: 12:2 + return; // scope 0 at $DIR/mutable_variable_no_prop.rs:12:2: 12:2 + } + } + + alloc0 (static: STATIC, size: 4, align: 4) { + 2a 00 00 00 │ *... + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.rs b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.rs new file mode 100644 index 0000000000000..40f801b1b5e58 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.rs @@ -0,0 +1,15 @@ +// compile-flags: -O + +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let a = foo(); + let mut x: (i32, i32) = (1, 2); + x.1 = a; + let y = x.1; + let z = x.0; // this could theoretically be allowed, but we can't handle it right now +} + +#[inline(never)] +fn foo() -> i32 { + unimplemented!() +} diff --git a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..738343c655e36 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign/rustc.main.ConstProp.diff @@ -0,0 +1,74 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_unprop_assign.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 + let mut _3: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + scope 1 { + debug a => _1; // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 + let mut _2: (i32, i32) as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 + scope 2 { + debug x => _2; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 + let _4: i32; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 + scope 3 { + debug y => _4; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 + let _5: i32; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 + scope 4 { + debug z => _5; // in scope 4 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 + _1 = const foo() -> bb1; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:13: 5:18 + // ty::Const + // + ty: fn() -> i32 {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_unprop_assign.rs:5:13: 5:16 + // + literal: Const { ty: fn() -> i32 {foo}, val: Value(Scalar()) } + } + + bb1: { + StorageLive(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 + _2 = (const 1i32, const 2i32); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant +- // + span: $DIR/mutable_variable_unprop_assign.rs:6:30: 6:31 ++ // + span: $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant +- // + span: $DIR/mutable_variable_unprop_assign.rs:6:33: 6:34 ++ // + span: $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + StorageLive(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + _3 = _1; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + (_2.1: i32) = move _3; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:5: 7:12 + StorageDead(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + StorageLive(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 + _4 = (_2.1: i32); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:13: 8:16 + StorageLive(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 + _5 = (_2.0: i32); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:13: 9:16 + _0 = const (); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:4:11: 10:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/mutable_variable_unprop_assign.rs:4:11: 10:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + StorageDead(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + StorageDead(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + return; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:10:2: 10:2 + } + } + diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.rs b/src/test/mir-opt/const_prop/optimizes_into_variable.rs index 93a53db909360..0ae172e777b9b 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.rs +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.rs @@ -5,145 +5,11 @@ struct Point { y: u32, } +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR rustc.main.SimplifyLocals.after.mir fn main() { let x = 2 + 2; let y = [0, 1, 2, 3, 4, 5][3]; let z = (Point { x: 12, y: 42}).y; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// let mut _0: (); -// let _1: i32; -// let mut _2: (i32, bool); -// let mut _4: [i32; 6]; -// let _5: usize; -// let mut _6: usize; -// let mut _7: bool; -// let mut _9: Point; -// scope 1 { -// debug x => _1; -// let _3: i32; -// scope 2 { -// debug y => _3; -// let _8: u32; -// scope 3 { -// debug z => _8; -// } -// } -// } -// bb0: { -// StorageLive(_1); -// _2 = CheckedAdd(const 2i32, const 2i32); -// assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; -// } -// bb1: { -// _1 = move (_2.0: i32); -// StorageLive(_3); -// StorageLive(_4); -// _4 = [const 0i32, const 1i32, const 2i32, const 3i32, const 4i32, const 5i32]; -// StorageLive(_5); -// _5 = const 3usize; -// _6 = const 6usize; -// _7 = Lt(_5, _6); -// assert(move _7, "index out of bounds: the len is move _6 but the index is _5") -> bb2; -// } -// bb2: { -// _3 = _4[_5]; -// StorageDead(_5); -// StorageDead(_4); -// StorageLive(_8); -// StorageLive(_9); -// _9 = Point { x: const 12u32, y: const 42u32 }; -// _8 = (_9.1: u32); -// StorageDead(_9); -// _0 = (); -// StorageDead(_8); -// StorageDead(_3); -// StorageDead(_1); -// return; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// let mut _0: (); -// let _1: i32; -// let mut _2: (i32, bool); -// let mut _4: [i32; 6]; -// let _5: usize; -// let mut _6: usize; -// let mut _7: bool; -// let mut _9: Point; -// scope 1 { -// debug x => _1; -// let _3: i32; -// scope 2 { -// debug y => _3; -// let _8: u32; -// scope 3 { -// debug z => _8; -// } -// } -// } -// bb0: { -// StorageLive(_1); -// _2 = (const 4i32, const false); -// assert(!const false, "attempt to add with overflow") -> bb1; -// } -// bb1: { -// _1 = const 4i32; -// StorageLive(_3); -// StorageLive(_4); -// _4 = [const 0i32, const 1i32, const 2i32, const 3i32, const 4i32, const 5i32]; -// StorageLive(_5); -// _5 = const 3usize; -// _6 = const 6usize; -// _7 = const true; -// assert(const true, "index out of bounds: the len is move _6 but the index is _5") -> bb2; -// } -// bb2: { -// _3 = const 3i32; -// StorageDead(_5); -// StorageDead(_4); -// StorageLive(_8); -// StorageLive(_9); -// _9 = Point { x: const 12u32, y: const 42u32 }; -// _8 = const 42u32; -// StorageDead(_9); -// _0 = (); -// StorageDead(_8); -// StorageDead(_3); -// StorageDead(_1); -// return; -// } -// END rustc.main.ConstProp.after.mir -// START rustc.main.SimplifyLocals.after.mir -// let mut _0: (); -// let _1: i32; -// let mut _3: [i32; 6]; -// scope 1 { -// debug x => _1; -// let _2: i32; -// scope 2 { -// debug y => _2; -// let _4: u32; -// scope 3 { -// debug z => _4; -// } -// } -// } -// bb0: { -// StorageLive(_1); -// _1 = const 4i32; -// StorageLive(_2); -// StorageLive(_3); -// _3 = [const 0i32, const 1i32, const 2i32, const 3i32, const 4i32, const 5i32]; -// _2 = const 3i32; -// StorageDead(_3); -// StorageLive(_4); -// _4 = const 42u32; -// StorageDead(_4); -// StorageDead(_2); -// StorageDead(_1); -// return; -// } -// END rustc.main.SimplifyLocals.after.mir diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..9bd9bfa9f2796 --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.ConstProp.diff @@ -0,0 +1,187 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let mut _2: (i32, bool); // in scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + let mut _4: [i32; 6]; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 +- _2 = CheckedAdd(const 2i32, const 2i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _2 = (const 4i32, const false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + // ty::Const + // + ty: i32 +- // + val: Value(Scalar(0x00000002)) ++ // + val: Value(Scalar(0x00000004)) + // mir::Constant +- // + span: $DIR/optimizes_into_variable.rs:12:13: 12:14 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000002)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant +- // + span: $DIR/optimizes_into_variable.rs:12:17: 12:18 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } +- assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ assert(!const false, "attempt to add with overflow") -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb1: { +- _1 = move (_2.0: i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _1 = const 4i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000004)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + StorageLive(_3); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + StorageLive(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + _4 = [const 0i32, const 1i32, const 2i32, const 3i32, const 4i32, const 5i32]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:14: 13:15 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:17: 13:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:20: 13:21 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:23: 13:24 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:26: 13:27 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:29: 13:30 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } + StorageLive(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + _5 = const 3usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:32: 13:33 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } + _6 = const 6usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000006)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000006)) } +- _7 = Lt(_5, _6); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 +- assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _7 = const true; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb2: { +- _3 = _4[_5]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _3 = const 3i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000003)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + _9 = Point { x: const 12u32, y: const 42u32 }; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000000c)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:14:25: 14:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000000c)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:14:32: 14:34 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } +- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ _8 = const 42u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x0000002a)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39 + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } + } + diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir new file mode 100644 index 0000000000000..8ea9316c7d49b --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir @@ -0,0 +1,55 @@ +// MIR for `main` after SimplifyLocals + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + _1 = const 4i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + _2 = const 3i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + _3 = const 42u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..1da763ec9558a --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.ConstProp.diff @@ -0,0 +1,187 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let mut _2: (i32, bool); // in scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + let mut _4: [i32; 6]; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 +- _2 = CheckedAdd(const 2i32, const 2i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _2 = (const 4i32, const false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + // ty::Const + // + ty: i32 +- // + val: Value(Scalar(0x00000002)) ++ // + val: Value(Scalar(0x00000004)) + // mir::Constant +- // + span: $DIR/optimizes_into_variable.rs:12:13: 12:14 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000002)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant +- // + span: $DIR/optimizes_into_variable.rs:12:17: 12:18 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } +- assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ assert(!const false, "attempt to add with overflow") -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb1: { +- _1 = move (_2.0: i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _1 = const 4i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000004)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + StorageLive(_3); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + StorageLive(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + _4 = [const 0i32, const 1i32, const 2i32, const 3i32, const 4i32, const 5i32]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:14: 13:15 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:17: 13:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:20: 13:21 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:23: 13:24 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:26: 13:27 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:29: 13:30 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } + StorageLive(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + _5 = const 3usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000003)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:32: 13:33 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } + _6 = const 6usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000006)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000006)) } +- _7 = Lt(_5, _6); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 +- assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _7 = const true; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb2: { +- _3 = _4[_5]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _3 = const 3i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000003)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + _9 = Point { x: const 12u32, y: const 42u32 }; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000000c)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:14:25: 14:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000000c)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:14:32: 14:34 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } +- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ _8 = const 42u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x0000002a)) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39 + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } + } + diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir new file mode 100644 index 0000000000000..8ea9316c7d49b --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir @@ -0,0 +1,55 @@ +// MIR for `main` after SimplifyLocals + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + _1 = const 4i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + _2 = const 3i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + _3 = const 42u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/const_prop/read_immutable_static.rs b/src/test/mir-opt/const_prop/read_immutable_static.rs index d307cebd71539..9635f7050a687 100644 --- a/src/test/mir-opt/const_prop/read_immutable_static.rs +++ b/src/test/mir-opt/const_prop/read_immutable_static.rs @@ -2,30 +2,7 @@ static FOO: u8 = 2; +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x = FOO + FOO; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _3 = const {alloc0+0: &u8}; -// _2 = (*_3); -// ... -// _5 = const {alloc0+0: &u8}; -// _4 = (*_5); -// _1 = Add(move _2, move _4); -// ... -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _2 = const 2u8; -// ... -// _4 = const 2u8; -// _1 = const 4u8; -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/read_immutable_static/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/read_immutable_static/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..103444f796ec6 --- /dev/null +++ b/src/test/mir-opt/const_prop/read_immutable_static/rustc.main.ConstProp.diff @@ -0,0 +1,78 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/read_immutable_static.rs:6:11: 6:11 + let _1: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:9: 7:10 + let mut _2: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + let mut _3: &u8; // in scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + let mut _4: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + let mut _5: &u8; // in scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/read_immutable_static.rs:7:9: 7:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/read_immutable_static.rs:7:9: 7:10 + StorageLive(_2); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + StorageLive(_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + _3 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + // ty::Const + // + ty: &u8 + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/read_immutable_static.rs:7:13: 7:16 + // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } +- _2 = (*_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 ++ _2 = const 2u8; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 ++ // ty::Const ++ // + ty: u8 ++ // + val: Value(Scalar(0x02)) ++ // mir::Constant ++ // + span: $DIR/read_immutable_static.rs:7:13: 7:16 ++ // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } + StorageLive(_4); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + StorageLive(_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + _5 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + // ty::Const + // + ty: &u8 + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/read_immutable_static.rs:7:19: 7:22 + // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } +- _4 = (*_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 +- _1 = Add(move _2, move _4); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:22 ++ _4 = const 2u8; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 ++ // ty::Const ++ // + ty: u8 ++ // + val: Value(Scalar(0x02)) ++ // mir::Constant ++ // + span: $DIR/read_immutable_static.rs:7:19: 7:22 ++ // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } ++ _1 = const 4u8; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:22 ++ // ty::Const ++ // + ty: u8 ++ // + val: Value(Scalar(0x04)) ++ // mir::Constant ++ // + span: $DIR/read_immutable_static.rs:7:13: 7:22 ++ // + literal: Const { ty: u8, val: Value(Scalar(0x04)) } + StorageDead(_4); // scope 0 at $DIR/read_immutable_static.rs:7:21: 7:22 + StorageDead(_2); // scope 0 at $DIR/read_immutable_static.rs:7:21: 7:22 + StorageDead(_5); // scope 0 at $DIR/read_immutable_static.rs:7:22: 7:23 + StorageDead(_3); // scope 0 at $DIR/read_immutable_static.rs:7:22: 7:23 + _0 = const (); // scope 0 at $DIR/read_immutable_static.rs:6:11: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/read_immutable_static.rs:6:11: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/read_immutable_static.rs:8:1: 8:2 + return; // scope 0 at $DIR/read_immutable_static.rs:8:2: 8:2 + } + } + + alloc0 (static: FOO, size: 1, align: 1) { + 02 │ . + } + diff --git a/src/test/mir-opt/const_prop/ref_deref.rs b/src/test/mir-opt/const_prop/ref_deref.rs index 8b48296a5d911..fc33e0e1f3b18 100644 --- a/src/test/mir-opt/const_prop/ref_deref.rs +++ b/src/test/mir-opt/const_prop/ref_deref.rs @@ -1,41 +1,6 @@ +// EMIT_MIR rustc.main.PromoteTemps.diff +// EMIT_MIR rustc.main.ConstProp.diff + fn main() { *(&4); } - -// END RUST SOURCE -// START rustc.main.PromoteTemps.before.mir -// bb0: { -// ... -// _3 = const 4i32; -// _2 = &_3; -// _1 = (*_2); -// ... -//} -// END rustc.main.PromoteTemps.before.mir -// START rustc.main.PromoteTemps.after.mir -// bb0: { -// ... -// _4 = const main::promoted[0]; -// _2 = &(*_4); -// _1 = (*_2); -// ... -//} -// END rustc.main.PromoteTemps.after.mir -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _4 = const main::promoted[0]; -// _2 = _4; -// _1 = (*_2); -// ... -//} -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _4 = const main::promoted[0]; -// _2 = _4; -// _1 = const 4i32; -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/ref_deref/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..dcd3d4019811e --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref/rustc.main.ConstProp.diff @@ -0,0 +1,42 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + let _3: i32; // in scope 0 at $DIR/ref_deref.rs:5:8: 5:9 + let mut _4: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(DefId(0:3 ~ ref_deref[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/ref_deref.rs:5:6: 5:10 + // + literal: Const { ty: &i32, val: Unevaluated(DefId(0:3 ~ ref_deref[317d]::main[0]), [], Some(promoted[0])) } + _2 = _4; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 +- _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 ++ _1 = const 4i32; // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000004)) ++ // mir::Constant ++ // + span: $DIR/ref_deref.rs:5:5: 5:10 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + _0 = const (); // scope 0 at $DIR/ref_deref.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/ref_deref.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/ref_deref.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/ref_deref/rustc.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref/rustc.main.PromoteTemps.diff new file mode 100644 index 0000000000000..ef696f1ab8052 --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref/rustc.main.PromoteTemps.diff @@ -0,0 +1,43 @@ +- // MIR for `main` before PromoteTemps ++ // MIR for `main` after PromoteTemps + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + let _3: i32; // in scope 0 at $DIR/ref_deref.rs:5:8: 5:9 ++ let mut _4: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 +- StorageLive(_3); // scope 0 at $DIR/ref_deref.rs:5:8: 5:9 +- _3 = const 4i32; // scope 0 at $DIR/ref_deref.rs:5:8: 5:9 ++ _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000004)) ++ // + ty: &i32 ++ // + val: Unevaluated(DefId(0:3 ~ ref_deref[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant +- // + span: $DIR/ref_deref.rs:5:8: 5:9 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } +- _2 = &_3; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 ++ // + span: $DIR/ref_deref.rs:5:6: 5:10 ++ // + literal: Const { ty: &i32, val: Unevaluated(DefId(0:3 ~ ref_deref[317d]::main[0]), [], Some(promoted[0])) } ++ _2 = &(*_4); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 +- StorageDead(_3); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + _0 = const (); // scope 0 at $DIR/ref_deref.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/ref_deref.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/ref_deref.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/ref_deref_project.rs b/src/test/mir-opt/const_prop/ref_deref_project.rs index ca539fb7462a5..0322e30064317 100644 --- a/src/test/mir-opt/const_prop/ref_deref_project.rs +++ b/src/test/mir-opt/const_prop/ref_deref_project.rs @@ -1,41 +1,6 @@ +// EMIT_MIR rustc.main.PromoteTemps.diff +// EMIT_MIR rustc.main.ConstProp.diff + fn main() { *(&(4, 5).1); // This does not currently propagate (#67862) } - -// END RUST SOURCE -// START rustc.main.PromoteTemps.before.mir -// bb0: { -// ... -// _3 = (const 4i32, const 5i32); -// _2 = &(_3.1: i32); -// _1 = (*_2); -// ... -//} -// END rustc.main.PromoteTemps.before.mir -// START rustc.main.PromoteTemps.after.mir -// bb0: { -// ... -// _4 = const main::promoted[0]; -// _2 = &((*_4).1: i32); -// _1 = (*_2); -// ... -//} -// END rustc.main.PromoteTemps.after.mir -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _4 = const main::promoted[0]; -// _2 = &((*_4).1: i32); -// _1 = (*_2); -// ... -//} -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _4 = const main::promoted[0]; -// _2 = &((*_4).1: i32); -// _1 = (*_2); -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..dd2f5bd906428 --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.ConstProp.diff @@ -0,0 +1,35 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref_project.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + let _3: (i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 + let mut _4: &(i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + // ty::Const + // + ty: &(i32, i32) + // + val: Unevaluated(DefId(0:3 ~ ref_deref_project[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/ref_deref_project.rs:5:6: 5:17 + // + literal: Const { ty: &(i32, i32), val: Unevaluated(DefId(0:3 ~ ref_deref_project[317d]::main[0]), [], Some(promoted[0])) } + _2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + _1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/ref_deref_project.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/ref_deref_project.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.PromoteTemps.diff new file mode 100644 index 0000000000000..7f23f5ea7a69a --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.PromoteTemps.diff @@ -0,0 +1,49 @@ +- // MIR for `main` before PromoteTemps ++ // MIR for `main` after PromoteTemps + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref_project.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + let _3: (i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 ++ let mut _4: &(i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 +- StorageLive(_3); // scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 +- _3 = (const 4i32, const 5i32); // scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 ++ _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000004)) ++ // + ty: &(i32, i32) ++ // + val: Unevaluated(DefId(0:3 ~ ref_deref_project[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant +- // + span: $DIR/ref_deref_project.rs:5:9: 5:10 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } +- // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000005)) +- // mir::Constant +- // + span: $DIR/ref_deref_project.rs:5:12: 5:13 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } +- _2 = &(_3.1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 ++ // + span: $DIR/ref_deref_project.rs:5:6: 5:17 ++ // + literal: Const { ty: &(i32, i32), val: Unevaluated(DefId(0:3 ~ ref_deref_project[317d]::main[0]), [], Some(promoted[0])) } ++ _2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + _1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 +- StorageDead(_3); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/ref_deref_project.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/ref_deref_project.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr.rs b/src/test/mir-opt/const_prop/reify_fn_ptr.rs index 4d6fe905b0c35..834eb0cb1e943 100644 --- a/src/test/mir-opt/const_prop/reify_fn_ptr.rs +++ b/src/test/mir-opt/const_prop/reify_fn_ptr.rs @@ -1,25 +1,5 @@ +// EMIT_MIR rustc.main.ConstProp.diff + fn main() { let _ = main as usize as *const fn(); } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _3 = const main as fn() (Pointer(ReifyFnPointer)); -// _2 = move _3 as usize (Misc); -// ... -// _1 = move _2 as *const fn() (Misc); -// ... -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _3 = const main as fn() (Pointer(ReifyFnPointer)); -// _2 = move _3 as usize (Misc); -// ... -// _1 = move _2 as *const fn() (Misc); -// ... -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/reify_fn_ptr/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..93fe856c8e81d --- /dev/null +++ b/src/test/mir-opt/const_prop/reify_fn_ptr/rustc.main.ConstProp.diff @@ -0,0 +1,38 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/reify_fn_ptr.rs:3:11: 3:11 + let mut _1: *const fn(); // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 + let mut _2: usize; // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 + let mut _3: fn(); // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 + scope 1 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 + StorageLive(_2); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 + StorageLive(_3); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 + _3 = const main as fn() (Pointer(ReifyFnPointer)); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 + // ty::Const + // + ty: fn() {main} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/reify_fn_ptr.rs:4:13: 4:17 + // + literal: Const { ty: fn() {main}, val: Value(Scalar()) } + _2 = move _3 as usize (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 + StorageDead(_3); // scope 0 at $DIR/reify_fn_ptr.rs:4:25: 4:26 + _1 = move _2 as *const fn() (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 + StorageDead(_2); // scope 0 at $DIR/reify_fn_ptr.rs:4:40: 4:41 + StorageDead(_1); // scope 0 at $DIR/reify_fn_ptr.rs:4:41: 4:42 + _0 = const (); // scope 0 at $DIR/reify_fn_ptr.rs:3:11: 5:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/reify_fn_ptr.rs:3:11: 5:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/reify_fn_ptr.rs:5:2: 5:2 + } + } + diff --git a/src/test/mir-opt/const_prop/repeat.rs b/src/test/mir-opt/const_prop/repeat.rs index 48c06290cec00..cdbfc46d6ca0e 100644 --- a/src/test/mir-opt/const_prop/repeat.rs +++ b/src/test/mir-opt/const_prop/repeat.rs @@ -1,37 +1,7 @@ // compile-flags: -O +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.ConstProp.diff fn main() { let x: u32 = [42; 8][2] + 0; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _3 = [const 42u32; 8]; -// ... -// _4 = const 2usize; -// _5 = const 8usize; -// _6 = Lt(_4, _5); -// assert(move _6, "index out of bounds: the len is move _5 but the index is _4") -> bb1; -// } -// bb1: { -// _2 = _3[_4]; -// _1 = Add(move _2, const 0u32); -// ... -// return; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _6 = const true; -// assert(const true, "index out of bounds: the len is move _5 but the index is _4") -> bb1; -// } -// bb1: { -// _2 = const 42u32; -// _1 = const 42u32; -// ... -// return; -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/repeat/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/repeat/32bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..4ccfe1838d9cc --- /dev/null +++ b/src/test/mir-opt/const_prop/repeat/32bit/rustc.main.ConstProp.diff @@ -0,0 +1,94 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/repeat.rs:5:11: 5:11 + let _1: u32 as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/repeat.rs:6:9: 6:10 + let mut _2: u32; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _3: [u32; 8]; // in scope 0 at $DIR/repeat.rs:6:18: 6:25 + let _4: usize; // in scope 0 at $DIR/repeat.rs:6:26: 6:27 + let mut _5: usize; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _6: bool; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/repeat.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/repeat.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/repeat.rs:6:18: 6:28 + StorageLive(_3); // scope 0 at $DIR/repeat.rs:6:18: 6:25 + _3 = [const 42u32; 8]; // scope 0 at $DIR/repeat.rs:6:18: 6:25 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/repeat.rs:6:19: 6:21 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + StorageLive(_4); // scope 0 at $DIR/repeat.rs:6:26: 6:27 + _4 = const 2usize; // scope 0 at $DIR/repeat.rs:6:26: 6:27 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/repeat.rs:6:26: 6:27 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } + _5 = const 8usize; // scope 0 at $DIR/repeat.rs:6:18: 6:28 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000008)) + // mir::Constant + // + span: $DIR/repeat.rs:6:18: 6:28 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000008)) } +- _6 = Lt(_4, _5); // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- assert(move _6, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ _6 = const true; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/repeat.rs:6:18: 6:28 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/repeat.rs:6:18: 6:28 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb1: { +- _2 = _3[_4]; // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- _1 = Add(move _2, const 0u32); // scope 0 at $DIR/repeat.rs:6:18: 6:32 ++ _2 = const 42u32; // scope 0 at $DIR/repeat.rs:6:18: 6:28 + // ty::Const + // + ty: u32 +- // + val: Value(Scalar(0x00000000)) ++ // + val: Value(Scalar(0x0000002a)) + // mir::Constant +- // + span: $DIR/repeat.rs:6:31: 6:32 +- // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } ++ // + span: $DIR/repeat.rs:6:18: 6:28 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } ++ _1 = const 42u32; // scope 0 at $DIR/repeat.rs:6:18: 6:32 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x0000002a)) ++ // mir::Constant ++ // + span: $DIR/repeat.rs:6:18: 6:32 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + StorageDead(_2); // scope 0 at $DIR/repeat.rs:6:31: 6:32 + StorageDead(_4); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + StorageDead(_3); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + _0 = const (); // scope 0 at $DIR/repeat.rs:5:11: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/repeat.rs:5:11: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/repeat.rs:7:1: 7:2 + return; // scope 0 at $DIR/repeat.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/repeat/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/repeat/64bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..fe9d16c4ffabb --- /dev/null +++ b/src/test/mir-opt/const_prop/repeat/64bit/rustc.main.ConstProp.diff @@ -0,0 +1,94 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/repeat.rs:5:11: 5:11 + let _1: u32 as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/repeat.rs:6:9: 6:10 + let mut _2: u32; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _3: [u32; 8]; // in scope 0 at $DIR/repeat.rs:6:18: 6:25 + let _4: usize; // in scope 0 at $DIR/repeat.rs:6:26: 6:27 + let mut _5: usize; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _6: bool; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/repeat.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/repeat.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/repeat.rs:6:18: 6:28 + StorageLive(_3); // scope 0 at $DIR/repeat.rs:6:18: 6:25 + _3 = [const 42u32; 8]; // scope 0 at $DIR/repeat.rs:6:18: 6:25 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/repeat.rs:6:19: 6:21 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + StorageLive(_4); // scope 0 at $DIR/repeat.rs:6:26: 6:27 + _4 = const 2usize; // scope 0 at $DIR/repeat.rs:6:26: 6:27 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000002)) + // mir::Constant + // + span: $DIR/repeat.rs:6:26: 6:27 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } + _5 = const 8usize; // scope 0 at $DIR/repeat.rs:6:18: 6:28 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000008)) + // mir::Constant + // + span: $DIR/repeat.rs:6:18: 6:28 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000008)) } +- _6 = Lt(_4, _5); // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- assert(move _6, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ _6 = const true; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/repeat.rs:6:18: 6:28 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/repeat.rs:6:18: 6:28 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb1: { +- _2 = _3[_4]; // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- _1 = Add(move _2, const 0u32); // scope 0 at $DIR/repeat.rs:6:18: 6:32 ++ _2 = const 42u32; // scope 0 at $DIR/repeat.rs:6:18: 6:28 + // ty::Const + // + ty: u32 +- // + val: Value(Scalar(0x00000000)) ++ // + val: Value(Scalar(0x0000002a)) + // mir::Constant +- // + span: $DIR/repeat.rs:6:31: 6:32 +- // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } ++ // + span: $DIR/repeat.rs:6:18: 6:28 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } ++ _1 = const 42u32; // scope 0 at $DIR/repeat.rs:6:18: 6:32 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x0000002a)) ++ // mir::Constant ++ // + span: $DIR/repeat.rs:6:18: 6:32 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } + StorageDead(_2); // scope 0 at $DIR/repeat.rs:6:31: 6:32 + StorageDead(_4); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + StorageDead(_3); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + _0 = const (); // scope 0 at $DIR/repeat.rs:5:11: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/repeat.rs:5:11: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/repeat.rs:7:1: 7:2 + return; // scope 0 at $DIR/repeat.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/return_place.rs b/src/test/mir-opt/const_prop/return_place.rs index ea7c1e7ccd0cb..8d5b63b9afd88 100644 --- a/src/test/mir-opt/const_prop/return_place.rs +++ b/src/test/mir-opt/const_prop/return_place.rs @@ -1,5 +1,7 @@ // compile-flags: -C overflow-checks=on +// EMIT_MIR rustc.add.ConstProp.diff +// EMIT_MIR rustc.add.PreCodegen.before.mir fn add() -> u32 { 2 + 2 } @@ -7,42 +9,3 @@ fn add() -> u32 { fn main() { add(); } - -// END RUST SOURCE -// START rustc.add.ConstProp.before.mir -// fn add() -> u32 { -// let mut _0: u32; -// let mut _1: (u32, bool); -// bb0: { -// _1 = CheckedAdd(const 2u32, const 2u32); -// assert(!move (_1.1: bool), "attempt to add with overflow") -> bb1; -// } -// bb1: { -// _0 = move (_1.0: u32); -// return; -// } -// } -// END rustc.add.ConstProp.before.mir -// START rustc.add.ConstProp.after.mir -// fn add() -> u32 { -// let mut _0: u32; -// let mut _1: (u32, bool); -// bb0: { -// _1 = (const 4u32, const false); -// assert(!const false, "attempt to add with overflow") -> bb1; -// } -// bb1: { -// _0 = const 4u32; -// return; -// } -// } -// END rustc.add.ConstProp.after.mir -// START rustc.add.PreCodegen.before.mir -// fn add() -> u32 { -// let mut _0: u32; -// bb0: { -// _0 = const 4u32; -// return; -// } -// } -// END rustc.add.PreCodegen.before.mir diff --git a/src/test/mir-opt/const_prop/return_place/rustc.add.ConstProp.diff b/src/test/mir-opt/const_prop/return_place/rustc.add.ConstProp.diff new file mode 100644 index 0000000000000..fc7d028277bd9 --- /dev/null +++ b/src/test/mir-opt/const_prop/return_place/rustc.add.ConstProp.diff @@ -0,0 +1,52 @@ +- // MIR for `add` before ConstProp ++ // MIR for `add` after ConstProp + + fn add() -> u32 { + let mut _0: u32; // return place in scope 0 at $DIR/return_place.rs:5:13: 5:16 + let mut _1: (u32, bool); // in scope 0 at $DIR/return_place.rs:6:5: 6:10 + + bb0: { +- _1 = CheckedAdd(const 2u32, const 2u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ _1 = (const 4u32, const false); // scope 0 at $DIR/return_place.rs:6:5: 6:10 + // ty::Const + // + ty: u32 +- // + val: Value(Scalar(0x00000002)) ++ // + val: Value(Scalar(0x00000004)) + // mir::Constant +- // + span: $DIR/return_place.rs:6:5: 6:6 +- // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } ++ // + span: $DIR/return_place.rs:6:5: 6:10 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000004)) } + // ty::Const +- // + ty: u32 +- // + val: Value(Scalar(0x00000002)) ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) + // mir::Constant +- // + span: $DIR/return_place.rs:6:9: 6:10 +- // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } +- assert(!move (_1.1: bool), "attempt to add with overflow") -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ // + span: $DIR/return_place.rs:6:5: 6:10 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ assert(!const false, "attempt to add with overflow") -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/return_place.rs:6:5: 6:10 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb1: { +- _0 = move (_1.0: u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ _0 = const 4u32; // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000004)) ++ // mir::Constant ++ // + span: $DIR/return_place.rs:6:5: 6:10 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000004)) } + return; // scope 0 at $DIR/return_place.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/return_place/rustc.add.PreCodegen.before.mir b/src/test/mir-opt/const_prop/return_place/rustc.add.PreCodegen.before.mir new file mode 100644 index 0000000000000..b741c786fb394 --- /dev/null +++ b/src/test/mir-opt/const_prop/return_place/rustc.add.PreCodegen.before.mir @@ -0,0 +1,16 @@ +// MIR for `add` before PreCodegen + +fn add() -> u32 { + let mut _0: u32; // return place in scope 0 at $DIR/return_place.rs:5:13: 5:16 + + bb0: { + _0 = const 4u32; // scope 0 at $DIR/return_place.rs:6:5: 6:10 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/return_place.rs:6:5: 6:10 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000004)) } + return; // scope 0 at $DIR/return_place.rs:7:2: 7:2 + } +} diff --git a/src/test/mir-opt/const_prop/scalar_literal_propagation.rs b/src/test/mir-opt/const_prop/scalar_literal_propagation.rs new file mode 100644 index 0000000000000..a740e69dca263 --- /dev/null +++ b/src/test/mir-opt/const_prop/scalar_literal_propagation.rs @@ -0,0 +1,8 @@ +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let x = 1; + consume(x); +} + +#[inline(never)] +fn consume(_: u32) { } diff --git a/src/test/mir-opt/const_prop/scalar_literal_propagation/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/scalar_literal_propagation/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..596ddcb43533b --- /dev/null +++ b/src/test/mir-opt/const_prop/scalar_literal_propagation/rustc.main.ConstProp.diff @@ -0,0 +1,62 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/scalar_literal_propagation.rs:2:11: 2:11 + let _1: u32; // in scope 0 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 + let _2: (); // in scope 0 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 + let mut _3: u32; // in scope 0 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 + _1 = const 1u32; // scope 0 at $DIR/scalar_literal_propagation.rs:3:13: 3:14 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/scalar_literal_propagation.rs:3:13: 3:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 + StorageLive(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 +- _3 = _1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 +- _2 = const consume(move _3) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 ++ _3 = const 1u32; // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 + // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/scalar_literal_propagation.rs:4:13: 4:14 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } ++ _2 = const consume(const 1u32) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 ++ // ty::Const + // + ty: fn(u32) {consume} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/scalar_literal_propagation.rs:4:5: 4:12 + // + literal: Const { ty: fn(u32) {consume}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/scalar_literal_propagation.rs:4:5: 4:15 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + } + + bb1: { + StorageDead(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:4:14: 4:15 + StorageDead(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:4:15: 4:16 + _0 = const (); // scope 0 at $DIR/scalar_literal_propagation.rs:2:11: 5:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/scalar_literal_propagation.rs:2:11: 5:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:5:1: 5:2 + return; // scope 0 at $DIR/scalar_literal_propagation.rs:5:2: 5:2 + } + } + diff --git a/src/test/mir-opt/const_prop/slice_len.rs b/src/test/mir-opt/const_prop/slice_len.rs index 43813e43d3681..0312f5e8e3a3a 100644 --- a/src/test/mir-opt/const_prop/slice_len.rs +++ b/src/test/mir-opt/const_prop/slice_len.rs @@ -1,43 +1,6 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +// EMIT_MIR rustc.main.ConstProp.diff fn main() { (&[1u32, 2, 3] as &[u32])[1]; } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _9 = const main::promoted[0]; -// _4 = _9; -// _3 = _4; -// _2 = move _3 as &[u32] (Pointer(Unsize)); -// ... -// _6 = const 1usize; -// _7 = Len((*_2)); -// _8 = Lt(_6, _7); -// assert(move _8, "index out of bounds: the len is move _7 but the index is _6") -> bb1; -// } -// bb1: { -// _1 = (*_2)[_6]; -// ... -// return; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// _9 = const main::promoted[0]; -// _4 = _9; -// _3 = _4; -// _2 = move _3 as &[u32] (Pointer(Unsize)); -// ... -// _6 = const 1usize; -// _7 = const 3usize; -// _8 = const true; -// assert(const true, "index out of bounds: the len is move _7 but the index is _6") -> bb1; -// } -// bb1: { -// _1 = const 2u32; -// ... -// return; -// } -// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/slice_len/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/slice_len/32bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..9471dbef410d1 --- /dev/null +++ b/src/test/mir-opt/const_prop/slice_len/32bit/rustc.main.ConstProp.diff @@ -0,0 +1,89 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/slice_len.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _2: &[u32]; // in scope 0 at $DIR/slice_len.rs:5:5: 5:30 + let mut _3: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _4: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _5: [u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:7: 5:19 + let _6: usize; // in scope 0 at $DIR/slice_len.rs:5:31: 5:32 + let mut _7: usize; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _8: bool; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _9: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + StorageLive(_2); // scope 0 at $DIR/slice_len.rs:5:5: 5:30 + StorageLive(_3); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageLive(_4); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _9 = const main::promoted[0]; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + // ty::Const + // + ty: &[u32; 3] + // + val: Unevaluated(DefId(0:3 ~ slice_len[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/slice_len.rs:5:6: 5:19 + // + literal: Const { ty: &[u32; 3], val: Unevaluated(DefId(0:3 ~ slice_len[317d]::main[0]), [], Some(promoted[0])) } + _4 = _9; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _3 = _4; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageDead(_3); // scope 0 at $DIR/slice_len.rs:5:18: 5:19 + StorageLive(_6); // scope 0 at $DIR/slice_len.rs:5:31: 5:32 + _6 = const 1usize; // scope 0 at $DIR/slice_len.rs:5:31: 5:32 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/slice_len.rs:5:31: 5:32 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } +- _7 = Len((*_2)); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- _8 = Lt(_6, _7); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _7 = const 3usize; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x00000003)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } ++ _8 = const true; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb1: { +- _1 = (*_2)[_6]; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _1 = const 2u32; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageDead(_6); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_4); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_1); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/slice_len.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/slice_len.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/slice_len.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/slice_len/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/slice_len/64bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..3ae88e0d79863 --- /dev/null +++ b/src/test/mir-opt/const_prop/slice_len/64bit/rustc.main.ConstProp.diff @@ -0,0 +1,89 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/slice_len.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _2: &[u32]; // in scope 0 at $DIR/slice_len.rs:5:5: 5:30 + let mut _3: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _4: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _5: [u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:7: 5:19 + let _6: usize; // in scope 0 at $DIR/slice_len.rs:5:31: 5:32 + let mut _7: usize; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _8: bool; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _9: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + StorageLive(_2); // scope 0 at $DIR/slice_len.rs:5:5: 5:30 + StorageLive(_3); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageLive(_4); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _9 = const main::promoted[0]; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + // ty::Const + // + ty: &[u32; 3] + // + val: Unevaluated(DefId(0:3 ~ slice_len[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/slice_len.rs:5:6: 5:19 + // + literal: Const { ty: &[u32; 3], val: Unevaluated(DefId(0:3 ~ slice_len[317d]::main[0]), [], Some(promoted[0])) } + _4 = _9; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _3 = _4; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageDead(_3); // scope 0 at $DIR/slice_len.rs:5:18: 5:19 + StorageLive(_6); // scope 0 at $DIR/slice_len.rs:5:31: 5:32 + _6 = const 1usize; // scope 0 at $DIR/slice_len.rs:5:31: 5:32 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $DIR/slice_len.rs:5:31: 5:32 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } +- _7 = Len((*_2)); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- _8 = Lt(_6, _7); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _7 = const 3usize; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x0000000000000003)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } ++ _8 = const true; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ assert(const true, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + } + + bb1: { +- _1 = (*_2)[_6]; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _1 = const 2u32; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/slice_len.rs:5:5: 5:33 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageDead(_6); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_4); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_1); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/slice_len.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/slice_len.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/slice_len.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/switch_int.rs b/src/test/mir-opt/const_prop/switch_int.rs index 904d303d87e31..46e6efb8180eb 100644 --- a/src/test/mir-opt/const_prop/switch_int.rs +++ b/src/test/mir-opt/const_prop/switch_int.rs @@ -1,38 +1,11 @@ #[inline(never)] fn foo(_: i32) { } +// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR rustc.main.SimplifyBranches-after-const-prop.diff fn main() { match 1 { 1 => foo(0), _ => foo(-1), } } - -// END RUST SOURCE -// START rustc.main.ConstProp.before.mir -// bb0: { -// ... -// _1 = const 1i32; -// switchInt(_1) -> [1i32: bb2, otherwise: bb1]; -// } -// END rustc.main.ConstProp.before.mir -// START rustc.main.ConstProp.after.mir -// bb0: { -// ... -// switchInt(const 1i32) -> [1i32: bb2, otherwise: bb1]; -// } -// END rustc.main.ConstProp.after.mir -// START rustc.main.SimplifyBranches-after-const-prop.before.mir -// bb0: { -// ... -// _1 = const 1i32; -// switchInt(const 1i32) -> [1i32: bb2, otherwise: bb1]; -// } -// END rustc.main.SimplifyBranches-after-const-prop.before.mir -// START rustc.main.SimplifyBranches-after-const-prop.after.mir -// bb0: { -// ... -// _1 = const 1i32; -// goto -> bb2; -// } -// END rustc.main.SimplifyBranches-after-const-prop.after.mir diff --git a/src/test/mir-opt/const_prop/switch_int/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/switch_int/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..8072424729ab0 --- /dev/null +++ b/src/test/mir-opt/const_prop/switch_int/rustc.main.ConstProp.diff @@ -0,0 +1,64 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/switch_int.rs:6:11: 6:11 + let mut _1: i32; // in scope 0 at $DIR/switch_int.rs:7:11: 7:12 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/switch_int.rs:7:11: 7:12 + _1 = const 1i32; // scope 0 at $DIR/switch_int.rs:7:11: 7:12 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/switch_int.rs:7:11: 7:12 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } +- switchInt(_1) -> [1i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 ++ switchInt(const 1i32) -> [1i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/switch_int.rs:8:9: 8:10 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + } + + bb1: { + _0 = const foo(const -1i32) -> bb3; // scope 0 at $DIR/switch_int.rs:9:14: 9:21 + // ty::Const + // + ty: fn(i32) {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/switch_int.rs:9:14: 9:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0xffffffff)) + // mir::Constant + // + span: $DIR/switch_int.rs:9:18: 9:20 + // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } + } + + bb2: { + _0 = const foo(const 0i32) -> bb3; // scope 0 at $DIR/switch_int.rs:8:14: 8:20 + // ty::Const + // + ty: fn(i32) {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/switch_int.rs:8:14: 8:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/switch_int.rs:8:18: 8:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + } + + bb3: { + StorageDead(_1); // scope 0 at $DIR/switch_int.rs:11:1: 11:2 + return; // scope 0 at $DIR/switch_int.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/const_prop/switch_int/rustc.main.SimplifyBranches-after-const-prop.diff b/src/test/mir-opt/const_prop/switch_int/rustc.main.SimplifyBranches-after-const-prop.diff new file mode 100644 index 0000000000000..51f3bf20c1aea --- /dev/null +++ b/src/test/mir-opt/const_prop/switch_int/rustc.main.SimplifyBranches-after-const-prop.diff @@ -0,0 +1,64 @@ +- // MIR for `main` before SimplifyBranches-after-const-prop ++ // MIR for `main` after SimplifyBranches-after-const-prop + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/switch_int.rs:6:11: 6:11 + let mut _1: i32; // in scope 0 at $DIR/switch_int.rs:7:11: 7:12 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/switch_int.rs:7:11: 7:12 + _1 = const 1i32; // scope 0 at $DIR/switch_int.rs:7:11: 7:12 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/switch_int.rs:7:11: 7:12 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } +- switchInt(const 1i32) -> [1i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 +- // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000001)) +- // mir::Constant +- // + span: $DIR/switch_int.rs:8:9: 8:10 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } ++ goto -> bb2; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 + } + + bb1: { + _0 = const foo(const -1i32) -> bb3; // scope 0 at $DIR/switch_int.rs:9:14: 9:21 + // ty::Const + // + ty: fn(i32) {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/switch_int.rs:9:14: 9:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0xffffffff)) + // mir::Constant + // + span: $DIR/switch_int.rs:9:18: 9:20 + // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } + } + + bb2: { + _0 = const foo(const 0i32) -> bb3; // scope 0 at $DIR/switch_int.rs:8:14: 8:20 + // ty::Const + // + ty: fn(i32) {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/switch_int.rs:8:14: 8:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/switch_int.rs:8:18: 8:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + } + + bb3: { + StorageDead(_1); // scope 0 at $DIR/switch_int.rs:11:1: 11:2 + return; // scope 0 at $DIR/switch_int.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/const_prop/tuple_literal_propagation.rs b/src/test/mir-opt/const_prop/tuple_literal_propagation.rs new file mode 100644 index 0000000000000..015607cbab1a0 --- /dev/null +++ b/src/test/mir-opt/const_prop/tuple_literal_propagation.rs @@ -0,0 +1,9 @@ +// EMIT_MIR rustc.main.ConstProp.diff +fn main() { + let x = (1, 2); + + consume(x); +} + +#[inline(never)] +fn consume(_: (u32, u32)) { } diff --git a/src/test/mir-opt/const_prop/tuple_literal_propagation/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/tuple_literal_propagation/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..1511b361f587f --- /dev/null +++ b/src/test/mir-opt/const_prop/tuple_literal_propagation/rustc.main.ConstProp.diff @@ -0,0 +1,69 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/tuple_literal_propagation.rs:2:11: 2:11 + let _1: (u32, u32); // in scope 0 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 + let _2: (); // in scope 0 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 + let mut _3: (u32, u32); // in scope 0 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 + _1 = (const 1u32, const 2u32); // scope 0 at $DIR/tuple_literal_propagation.rs:3:13: 3:19 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant +- // + span: $DIR/tuple_literal_propagation.rs:3:14: 3:15 ++ // + span: $DIR/tuple_literal_propagation.rs:3:13: 3:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant +- // + span: $DIR/tuple_literal_propagation.rs:3:17: 3:18 ++ // + span: $DIR/tuple_literal_propagation.rs:3:13: 3:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 + StorageLive(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 +- _3 = _1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 ++ _3 = (const 1u32, const 2u32); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000002)) ++ // mir::Constant ++ // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + _2 = const consume(move _3) -> bb1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 + // ty::Const + // + ty: fn((u32, u32)) {consume} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/tuple_literal_propagation.rs:5:5: 5:12 + // + literal: Const { ty: fn((u32, u32)) {consume}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:14: 5:15 + StorageDead(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:5:15: 5:16 + _0 = const (); // scope 0 at $DIR/tuple_literal_propagation.rs:2:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/tuple_literal_propagation.rs:2:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:6:1: 6:2 + return; // scope 0 at $DIR/tuple_literal_propagation.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index a86caf04b043b..b5db5497d4823 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -1,3 +1,5 @@ +// EMIT_MIR rustc.test.CopyPropagation.diff + fn test(x: u32) -> u32 { let y = x; y @@ -7,23 +9,3 @@ fn main() { // Make sure the function actually gets instantiated. test(0); } - -// END RUST SOURCE -// START rustc.test.CopyPropagation.before.mir -// bb0: { -// ... -// _2 = _1; -// ... -// _0 = _2; -// ... -// return; -// } -// END rustc.test.CopyPropagation.before.mir -// START rustc.test.CopyPropagation.after.mir -// bb0: { -// ... -// _0 = _1; -// ... -// return; -// } -// END rustc.test.CopyPropagation.after.mir diff --git a/src/test/mir-opt/copy_propagation/rustc.test.CopyPropagation.diff b/src/test/mir-opt/copy_propagation/rustc.test.CopyPropagation.diff new file mode 100644 index 0000000000000..f2838638aca0e --- /dev/null +++ b/src/test/mir-opt/copy_propagation/rustc.test.CopyPropagation.diff @@ -0,0 +1,25 @@ +- // MIR for `test` before CopyPropagation ++ // MIR for `test` after CopyPropagation + + fn test(_1: u32) -> u32 { + debug x => _1; // in scope 0 at $DIR/copy_propagation.rs:3:9: 3:10 + let mut _0: u32; // return place in scope 0 at $DIR/copy_propagation.rs:3:20: 3:23 + let _2: u32; // in scope 0 at $DIR/copy_propagation.rs:4:9: 4:10 + scope 1 { +- debug y => _2; // in scope 1 at $DIR/copy_propagation.rs:4:9: 4:10 ++ debug y => _1; // in scope 1 at $DIR/copy_propagation.rs:4:9: 4:10 + } + + bb0: { +- StorageLive(_2); // scope 0 at $DIR/copy_propagation.rs:4:9: 4:10 +- _2 = _1; // scope 0 at $DIR/copy_propagation.rs:4:13: 4:14 +- _0 = _2; // scope 1 at $DIR/copy_propagation.rs:5:5: 5:6 +- StorageDead(_2); // scope 0 at $DIR/copy_propagation.rs:6:1: 6:2 ++ nop; // scope 0 at $DIR/copy_propagation.rs:4:9: 4:10 ++ nop; // scope 0 at $DIR/copy_propagation.rs:4:13: 4:14 ++ _0 = _1; // scope 1 at $DIR/copy_propagation.rs:5:5: 5:6 ++ nop; // scope 0 at $DIR/copy_propagation.rs:6:1: 6:2 + return; // scope 0 at $DIR/copy_propagation.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs index 5e5fed12fb5f3..c4858be7f2b8c 100644 --- a/src/test/mir-opt/copy_propagation_arg.rs +++ b/src/test/mir-opt/copy_propagation_arg.rs @@ -5,21 +5,25 @@ fn dummy(x: u8) -> u8 { x } +// EMIT_MIR rustc.foo.CopyPropagation.diff fn foo(mut x: u8) { // calling `dummy` to make an use of `x` that copyprop cannot eliminate x = dummy(x); // this will assign a local to `x` } +// EMIT_MIR rustc.bar.CopyPropagation.diff fn bar(mut x: u8) { dummy(x); x = 5; } +// EMIT_MIR rustc.baz.CopyPropagation.diff fn baz(mut x: i32) { // self-assignment to a function argument should be eliminated x = x; } +// EMIT_MIR rustc.arg_src.CopyPropagation.diff fn arg_src(mut x: i32) -> i32 { let y = x; x = 123; // Don't propagate this assignment to `y` @@ -33,100 +37,3 @@ fn main() { baz(0); arg_src(0); } - -// END RUST SOURCE -// START rustc.foo.CopyPropagation.before.mir -// bb0: { -// ... -// _3 = _1; -// _2 = const dummy(move _3) -> bb1; -// } -// bb1: { -// ... -// _1 = move _2; -// ... -// } -// END rustc.foo.CopyPropagation.before.mir -// START rustc.foo.CopyPropagation.after.mir -// bb0: { -// ... -// _3 = _1; -// _2 = const dummy(move _3) -> bb1; -// } -// bb1: { -// ... -// _1 = move _2; -// ... -// } -// END rustc.foo.CopyPropagation.after.mir -// START rustc.bar.CopyPropagation.before.mir -// bb0: { -// StorageLive(_2); -// StorageLive(_3); -// _3 = _1; -// _2 = const dummy(move _3) -> bb1; -// } -// bb1: { -// StorageDead(_3); -// StorageDead(_2); -// _1 = const 5u8; -// ... -// return; -// } -// END rustc.bar.CopyPropagation.before.mir -// START rustc.bar.CopyPropagation.after.mir -// bb0: { -// ... -// _3 = _1; -// _2 = const dummy(move _3) -> bb1; -// } -// bb1: { -// ... -// _1 = const 5u8; -// ... -// return; -// } -// END rustc.bar.CopyPropagation.after.mir -// START rustc.baz.CopyPropagation.before.mir -// bb0: { -// StorageLive(_2); -// _2 = _1; -// _1 = move _2; -// StorageDead(_2); -// ... -// return; -// } -// END rustc.baz.CopyPropagation.before.mir -// START rustc.baz.CopyPropagation.after.mir -// bb0: { -// ... -// _2 = _1; -// _1 = move _2; -// ... -// return; -// } -// END rustc.baz.CopyPropagation.after.mir -// START rustc.arg_src.CopyPropagation.before.mir -// bb0: { -// ... -// _2 = _1; -// ... -// _1 = const 123i32; -// ... -// _0 = _2; -// ... -// return; -// } -// END rustc.arg_src.CopyPropagation.before.mir -// START rustc.arg_src.CopyPropagation.after.mir -// bb0: { -// ... -// _2 = _1; -// ... -// _1 = const 123i32; -// ... -// _0 = _2; -// ... -// return; -// } -// END rustc.arg_src.CopyPropagation.after.mir diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.arg_src.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.arg_src.CopyPropagation.diff new file mode 100644 index 0000000000000..1e0271a560f65 --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg/rustc.arg_src.CopyPropagation.diff @@ -0,0 +1,27 @@ +- // MIR for `arg_src` before CopyPropagation ++ // MIR for `arg_src` after CopyPropagation + + fn arg_src(_1: i32) -> i32 { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:27:12: 27:17 + let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:27:27: 27:30 + let _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:28:9: 28:10 + scope 1 { + debug y => _2; // in scope 1 at $DIR/copy_propagation_arg.rs:28:9: 28:10 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:28:9: 28:10 + _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:28:13: 28:14 + _1 = const 123i32; // scope 1 at $DIR/copy_propagation_arg.rs:29:5: 29:12 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000007b)) + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:29:9: 29:12 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000007b)) } + _0 = _2; // scope 1 at $DIR/copy_propagation_arg.rs:30:5: 30:6 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:31:1: 31:2 + return; // scope 0 at $DIR/copy_propagation_arg.rs:31:2: 31:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.bar.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.bar.CopyPropagation.diff new file mode 100644 index 0000000000000..b875bbea67bdf --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg/rustc.bar.CopyPropagation.diff @@ -0,0 +1,43 @@ +- // MIR for `bar` before CopyPropagation ++ // MIR for `bar` after CopyPropagation + + fn bar(_1: u8) -> () { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:15:8: 15:13 + let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:15:19: 15:19 + let _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 + let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 + StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 + _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 + _2 = const dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 + // ty::Const + // + ty: fn(u8) -> u8 {dummy} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:16:5: 16:10 + // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:16:12: 16:13 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:16:13: 16:14 + _1 = const 5u8; // scope 0 at $DIR/copy_propagation_arg.rs:17:5: 17:10 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x05)) + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:17:9: 17:10 + // + literal: Const { ty: u8, val: Value(Scalar(0x05)) } + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:15:19: 18:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:15:19: 18:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/copy_propagation_arg.rs:18:2: 18:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.baz.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.baz.CopyPropagation.diff new file mode 100644 index 0000000000000..ee20553f7cc3f --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg/rustc.baz.CopyPropagation.diff @@ -0,0 +1,24 @@ +- // MIR for `baz` before CopyPropagation ++ // MIR for `baz` after CopyPropagation + + fn baz(_1: i32) -> () { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:21:8: 21:13 + let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:21:20: 21:20 + let mut _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:23:5: 23:10 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:21:20: 24:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:21:20: 24:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/copy_propagation_arg.rs:24:2: 24:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.foo.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.foo.CopyPropagation.diff new file mode 100644 index 0000000000000..33aaa7486787d --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg/rustc.foo.CopyPropagation.diff @@ -0,0 +1,37 @@ +- // MIR for `foo` before CopyPropagation ++ // MIR for `foo` after CopyPropagation + + fn foo(_1: u8) -> () { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:9:8: 9:13 + let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:9:19: 9:19 + let mut _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 + let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 + StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 + _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 + _2 = const dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 + // ty::Const + // + ty: fn(u8) -> u8 {dummy} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:11:9: 11:14 + // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:11:16: 11:17 + _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:11:5: 11:17 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:11:16: 11:17 + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:9:19: 12:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:9:19: 12:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/copy_propagation_arg.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/deaggregator_test.rs b/src/test/mir-opt/deaggregator_test.rs index 44c2319bc7eb1..9004a63129100 100644 --- a/src/test/mir-opt/deaggregator_test.rs +++ b/src/test/mir-opt/deaggregator_test.rs @@ -4,6 +4,7 @@ struct Baz { z: bool, } +// EMIT_MIR rustc.bar.Deaggregator.diff fn bar(a: usize) -> Baz { Baz { x: a, y: 0.0, z: false } } @@ -12,27 +13,3 @@ fn main() { // Make sure the function actually gets instantiated. bar(0); } - -// END RUST SOURCE -// START rustc.bar.Deaggregator.before.mir -// bb0: { -// ... -// _2 = _1; -// ... -// _0 = Baz { x: move _2, y: const 0f32, z: const false }; -// ... -// return; -// } -// END rustc.bar.Deaggregator.before.mir -// START rustc.bar.Deaggregator.after.mir -// bb0: { -// ... -// _2 = _1; -// ... -// (_0.0: usize) = move _2; -// (_0.1: f32) = const 0f32; -// (_0.2: bool) = const false; -// ... -// return; -// } -// END rustc.bar.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test/rustc.bar.Deaggregator.diff b/src/test/mir-opt/deaggregator_test/rustc.bar.Deaggregator.diff new file mode 100644 index 0000000000000..524156e0b929d --- /dev/null +++ b/src/test/mir-opt/deaggregator_test/rustc.bar.Deaggregator.diff @@ -0,0 +1,32 @@ +- // MIR for `bar` before Deaggregator ++ // MIR for `bar` after Deaggregator + + fn bar(_1: usize) -> Baz { + debug a => _1; // in scope 0 at $DIR/deaggregator_test.rs:8:8: 8:9 + let mut _0: Baz; // return place in scope 0 at $DIR/deaggregator_test.rs:8:21: 8:24 + let mut _2: usize; // in scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 + _2 = _1; // scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 +- _0 = Baz { x: move _2, y: const 0f32, z: const false }; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 ++ (_0.0: usize) = move _2; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 ++ (_0.1: f32) = const 0f32; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 + // ty::Const + // + ty: f32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/deaggregator_test.rs:9:20: 9:23 + // + literal: Const { ty: f32, val: Value(Scalar(0x00000000)) } ++ (_0.2: bool) = const false; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/deaggregator_test.rs:9:28: 9:33 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_2); // scope 0 at $DIR/deaggregator_test.rs:9:34: 9:35 + return; // scope 0 at $DIR/deaggregator_test.rs:10:2: 10:2 + } + } + diff --git a/src/test/mir-opt/deaggregator_test_enum.rs b/src/test/mir-opt/deaggregator_test_enum.rs index fed1627159067..e74eafd011fe3 100644 --- a/src/test/mir-opt/deaggregator_test_enum.rs +++ b/src/test/mir-opt/deaggregator_test_enum.rs @@ -3,6 +3,7 @@ enum Baz { Foo { x: usize }, } +// EMIT_MIR rustc.bar.Deaggregator.diff fn bar(a: usize) -> Baz { Baz::Foo { x: a } } @@ -14,24 +15,3 @@ fn main() { Baz::Foo { x } => println!("{}", x), }; } - -// END RUST SOURCE -// START rustc.bar.Deaggregator.before.mir -// bb0: { -// StorageLive(_2); -// _2 = _1; -// _0 = Baz::Foo { x: move _2 }; -// StorageDead(_2); -// return; -// } -// END rustc.bar.Deaggregator.before.mir -// START rustc.bar.Deaggregator.after.mir -// bb0: { -// StorageLive(_2); -// _2 = _1; -// ((_0 as Foo).0: usize) = move _2; -// discriminant(_0) = 1; -// StorageDead(_2); -// return; -// } -// END rustc.bar.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test_enum/rustc.bar.Deaggregator.diff b/src/test/mir-opt/deaggregator_test_enum/rustc.bar.Deaggregator.diff new file mode 100644 index 0000000000000..5af9a53669379 --- /dev/null +++ b/src/test/mir-opt/deaggregator_test_enum/rustc.bar.Deaggregator.diff @@ -0,0 +1,19 @@ +- // MIR for `bar` before Deaggregator ++ // MIR for `bar` after Deaggregator + + fn bar(_1: usize) -> Baz { + debug a => _1; // in scope 0 at $DIR/deaggregator_test_enum.rs:7:8: 7:9 + let mut _0: Baz; // return place in scope 0 at $DIR/deaggregator_test_enum.rs:7:21: 7:24 + let mut _2: usize; // in scope 0 at $DIR/deaggregator_test_enum.rs:8:19: 8:20 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/deaggregator_test_enum.rs:8:19: 8:20 + _2 = _1; // scope 0 at $DIR/deaggregator_test_enum.rs:8:19: 8:20 +- _0 = Baz::Foo { x: move _2 }; // scope 0 at $DIR/deaggregator_test_enum.rs:8:5: 8:22 ++ ((_0 as Foo).0: usize) = move _2; // scope 0 at $DIR/deaggregator_test_enum.rs:8:5: 8:22 ++ discriminant(_0) = 1; // scope 0 at $DIR/deaggregator_test_enum.rs:8:5: 8:22 + StorageDead(_2); // scope 0 at $DIR/deaggregator_test_enum.rs:8:21: 8:22 + return; // scope 0 at $DIR/deaggregator_test_enum.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/deaggregator_test_enum_2.rs b/src/test/mir-opt/deaggregator_test_enum_2.rs index b39ad1bef8e34..d5201ed72a8d9 100644 --- a/src/test/mir-opt/deaggregator_test_enum_2.rs +++ b/src/test/mir-opt/deaggregator_test_enum_2.rs @@ -5,6 +5,7 @@ enum Foo { B(i32), } +// EMIT_MIR rustc.test1.Deaggregator.diff fn test1(x: bool, y: i32) -> Foo { if x { Foo::A(y) @@ -17,40 +18,3 @@ fn main() { // Make sure the function actually gets instantiated. test1(false, 0); } - -// END RUST SOURCE -// START rustc.test1.Deaggregator.before.mir -// bb1: { -// StorageLive(_5); -// _5 = _2; -// _0 = Foo::B(move _5,); -// StorageDead(_5); -// goto -> bb3; -// } -// bb2: { -// StorageLive(_4); -// _4 = _2; -// _0 = Foo::A(move _4,); -// StorageDead(_4); -// goto -> bb3; -// } -// END rustc.test1.Deaggregator.before.mir -// START rustc.test1.Deaggregator.after.mir -// bb1: { -// StorageLive(_5); -// _5 = _2; -// ((_0 as B).0: i32) = move _5; -// discriminant(_0) = 1; -// StorageDead(_5); -// goto -> bb3; -// } -// bb2: { -// StorageLive(_4); -// _4 = _2; -// ((_0 as A).0: i32) = move _4; -// discriminant(_0) = 0; -// StorageDead(_4); -// goto -> bb3; -// } -// END rustc.test1.Deaggregator.after.mir -// diff --git a/src/test/mir-opt/deaggregator_test_enum_2/rustc.test1.Deaggregator.diff b/src/test/mir-opt/deaggregator_test_enum_2/rustc.test1.Deaggregator.diff new file mode 100644 index 0000000000000..bf99f7efb4dd6 --- /dev/null +++ b/src/test/mir-opt/deaggregator_test_enum_2/rustc.test1.Deaggregator.diff @@ -0,0 +1,43 @@ +- // MIR for `test1` before Deaggregator ++ // MIR for `test1` after Deaggregator + + fn test1(_1: bool, _2: i32) -> Foo { + debug x => _1; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:9:10: 9:11 + debug y => _2; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:9:19: 9:20 + let mut _0: Foo; // return place in scope 0 at $DIR/deaggregator_test_enum_2.rs:9:30: 9:33 + let mut _3: bool; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:10:8: 10:9 + let mut _4: i32; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:11:16: 11:17 + let mut _5: i32; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:13:16: 13:17 + + bb0: { + StorageLive(_3); // scope 0 at $DIR/deaggregator_test_enum_2.rs:10:8: 10:9 + _3 = _1; // scope 0 at $DIR/deaggregator_test_enum_2.rs:10:8: 10:9 + switchInt(_3) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/deaggregator_test_enum_2.rs:10:5: 14:6 + } + + bb1: { + StorageLive(_5); // scope 0 at $DIR/deaggregator_test_enum_2.rs:13:16: 13:17 + _5 = _2; // scope 0 at $DIR/deaggregator_test_enum_2.rs:13:16: 13:17 +- _0 = Foo::B(move _5); // scope 0 at $DIR/deaggregator_test_enum_2.rs:13:9: 13:18 ++ ((_0 as B).0: i32) = move _5; // scope 0 at $DIR/deaggregator_test_enum_2.rs:13:9: 13:18 ++ discriminant(_0) = 1; // scope 0 at $DIR/deaggregator_test_enum_2.rs:13:9: 13:18 + StorageDead(_5); // scope 0 at $DIR/deaggregator_test_enum_2.rs:13:17: 13:18 + goto -> bb3; // scope 0 at $DIR/deaggregator_test_enum_2.rs:10:5: 14:6 + } + + bb2: { + StorageLive(_4); // scope 0 at $DIR/deaggregator_test_enum_2.rs:11:16: 11:17 + _4 = _2; // scope 0 at $DIR/deaggregator_test_enum_2.rs:11:16: 11:17 +- _0 = Foo::A(move _4); // scope 0 at $DIR/deaggregator_test_enum_2.rs:11:9: 11:18 ++ ((_0 as A).0: i32) = move _4; // scope 0 at $DIR/deaggregator_test_enum_2.rs:11:9: 11:18 ++ discriminant(_0) = 0; // scope 0 at $DIR/deaggregator_test_enum_2.rs:11:9: 11:18 + StorageDead(_4); // scope 0 at $DIR/deaggregator_test_enum_2.rs:11:17: 11:18 + goto -> bb3; // scope 0 at $DIR/deaggregator_test_enum_2.rs:10:5: 14:6 + } + + bb3: { + StorageDead(_3); // scope 0 at $DIR/deaggregator_test_enum_2.rs:15:1: 15:2 + return; // scope 0 at $DIR/deaggregator_test_enum_2.rs:15:2: 15:2 + } + } + diff --git a/src/test/mir-opt/deaggregator_test_multiple.rs b/src/test/mir-opt/deaggregator_test_multiple.rs index 34c41af273fea..824a970ce2fd3 100644 --- a/src/test/mir-opt/deaggregator_test_multiple.rs +++ b/src/test/mir-opt/deaggregator_test_multiple.rs @@ -5,6 +5,7 @@ enum Foo { B, } +// EMIT_MIR rustc.test.Deaggregator.diff fn test(x: i32) -> [Foo; 2] { [Foo::A(x), Foo::A(x)] } @@ -13,37 +14,3 @@ fn main() { // Make sure the function actually gets instantiated. test(0); } - -// END RUST SOURCE -// START rustc.test.Deaggregator.before.mir -// bb0: { -// ... -// _3 = _1; -// ... -// _2 = Foo::A(move _3,); -// ... -// _5 = _1; -// _4 = Foo::A(move _5,); -// ... -// _0 = [move _2, move _4]; -// ... -// return; -// } -// END rustc.test.Deaggregator.before.mir -// START rustc.test.Deaggregator.after.mir -// bb0: { -// ... -// _3 = _1; -// ... -// ((_2 as A).0: i32) = move _3; -// discriminant(_2) = 0; -// ... -// _5 = _1; -// ((_4 as A).0: i32) = move _5; -// discriminant(_4) = 0; -// ... -// _0 = [move _2, move _4]; -// ... -// return; -// } -// END rustc.test.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test_multiple/rustc.test.Deaggregator.diff b/src/test/mir-opt/deaggregator_test_multiple/rustc.test.Deaggregator.diff new file mode 100644 index 0000000000000..f5d8d0607c60b --- /dev/null +++ b/src/test/mir-opt/deaggregator_test_multiple/rustc.test.Deaggregator.diff @@ -0,0 +1,33 @@ +- // MIR for `test` before Deaggregator ++ // MIR for `test` after Deaggregator + + fn test(_1: i32) -> [Foo; 2] { + debug x => _1; // in scope 0 at $DIR/deaggregator_test_multiple.rs:9:9: 9:10 + let mut _0: [Foo; 2]; // return place in scope 0 at $DIR/deaggregator_test_multiple.rs:9:20: 9:28 + let mut _2: Foo; // in scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15 + let mut _3: i32; // in scope 0 at $DIR/deaggregator_test_multiple.rs:10:13: 10:14 + let mut _4: Foo; // in scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26 + let mut _5: i32; // in scope 0 at $DIR/deaggregator_test_multiple.rs:10:24: 10:25 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15 + StorageLive(_3); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:13: 10:14 + _3 = _1; // scope 0 at $DIR/deaggregator_test_multiple.rs:10:13: 10:14 +- _2 = Foo::A(move _3); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15 ++ ((_2 as A).0: i32) = move _3; // scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15 ++ discriminant(_2) = 0; // scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15 + StorageDead(_3); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:14: 10:15 + StorageLive(_4); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26 + StorageLive(_5); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:24: 10:25 + _5 = _1; // scope 0 at $DIR/deaggregator_test_multiple.rs:10:24: 10:25 +- _4 = Foo::A(move _5); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26 ++ ((_4 as A).0: i32) = move _5; // scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26 ++ discriminant(_4) = 0; // scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26 + StorageDead(_5); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:25: 10:26 + _0 = [move _2, move _4]; // scope 0 at $DIR/deaggregator_test_multiple.rs:10:5: 10:27 + StorageDead(_4); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:26: 10:27 + StorageDead(_2); // scope 0 at $DIR/deaggregator_test_multiple.rs:10:26: 10:27 + return; // scope 0 at $DIR/deaggregator_test_multiple.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/exponential-or.rs b/src/test/mir-opt/exponential-or.rs index 4c23582e1f894..9fce7928f6a97 100644 --- a/src/test/mir-opt/exponential-or.rs +++ b/src/test/mir-opt/exponential-or.rs @@ -1,9 +1,8 @@ // Test that simple or-patterns don't get expanded to exponentially large CFGs -// ignore-tidy-linelength - #![feature(or_patterns)] +// EMIT_MIR rustc.match_tuple.SimplifyCfg-initial.after.mir fn match_tuple(x: (u32, bool, Option, u32)) -> u32 { match x { (y @ (1 | 4), true | false, Some(1 | 8) | None, z @ (6..=9 | 13..=16)) => y ^ z, @@ -12,65 +11,3 @@ fn match_tuple(x: (u32, bool, Option, u32)) -> u32 { } fn main() {} - -// END RUST SOURCE - -// START rustc.match_tuple.SimplifyCfg-initial.after.mir -// scope 1 { -// debug y => _7; -// debug z => _8; -// } -// bb0: { -// FakeRead(ForMatchedPlace, _1); -// switchInt((_1.0: u32)) -> [1u32: bb2, 4u32: bb2, otherwise: bb1]; -// } -// bb1: { -// _0 = const 0u32; -// goto -> bb10; -// } -// bb2: { -// _2 = discriminant((_1.2: std::option::Option)); -// switchInt(move _2) -> [0isize: bb4, 1isize: bb3, otherwise: bb1]; -// } -// bb3: { -// switchInt((((_1.2: std::option::Option) as Some).0: i32)) -> [1i32: bb4, 8i32: bb4, otherwise: bb1]; -// } -// bb4: { -// _5 = Le(const 6u32, (_1.3: u32)); -// switchInt(move _5) -> [false: bb6, otherwise: bb5]; -// } -// bb5: { -// _6 = Le((_1.3: u32), const 9u32); -// switchInt(move _6) -> [false: bb6, otherwise: bb8]; -// } -// bb6: { -// _3 = Le(const 13u32, (_1.3: u32)); -// switchInt(move _3) -> [false: bb1, otherwise: bb7]; -// } -// bb7: { -// _4 = Le((_1.3: u32), const 16u32); -// switchInt(move _4) -> [false: bb1, otherwise: bb8]; -// } -// bb8: { -// falseEdges -> [real: bb9, imaginary: bb1]; -// } -// bb9: { -// StorageLive(_7); -// _7 = (_1.0: u32); -// StorageLive(_8); -// _8 = (_1.3: u32); -// StorageLive(_9); -// _9 = _7; -// StorageLive(_10); -// _10 = _8; -// _0 = BitXor(move _9, move _10); -// StorageDead(_10); -// StorageDead(_9); -// StorageDead(_8); -// StorageDead(_7); -// goto -> bb10; -// } -// bb10: { -// return; -// } -// END rustc.match_tuple.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/exponential-or/rustc.match_tuple.SimplifyCfg-initial.after.mir b/src/test/mir-opt/exponential-or/rustc.match_tuple.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..03d258d8f7289 --- /dev/null +++ b/src/test/mir-opt/exponential-or/rustc.match_tuple.SimplifyCfg-initial.after.mir @@ -0,0 +1,113 @@ +// MIR for `match_tuple` after SimplifyCfg-initial + +fn match_tuple(_1: (u32, bool, std::option::Option, u32)) -> u32 { + debug x => _1; // in scope 0 at $DIR/exponential-or.rs:6:16: 6:17 + let mut _0: u32; // return place in scope 0 at $DIR/exponential-or.rs:6:53: 6:56 + let mut _2: isize; // in scope 0 at $DIR/exponential-or.rs:8:37: 8:48 + let mut _3: bool; // in scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + let mut _4: bool; // in scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + let mut _5: bool; // in scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + let mut _6: bool; // in scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + let _7: u32; // in scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + let _8: u32; // in scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + let mut _9: u32; // in scope 0 at $DIR/exponential-or.rs:8:83: 8:84 + let mut _10: u32; // in scope 0 at $DIR/exponential-or.rs:8:87: 8:88 + scope 1 { + debug y => _7; // in scope 1 at $DIR/exponential-or.rs:8:10: 8:21 + debug z => _8; // in scope 1 at $DIR/exponential-or.rs:8:57: 8:78 + } + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/exponential-or.rs:7:11: 7:12 + switchInt((_1.0: u32)) -> [1u32: bb2, 4u32: bb2, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:15: 8:16 + } + + bb1: { + _0 = const 0u32; // scope 0 at $DIR/exponential-or.rs:9:14: 9:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/exponential-or.rs:9:14: 9:15 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + goto -> bb10; // scope 0 at $DIR/exponential-or.rs:7:5: 10:6 + } + + bb2: { + _2 = discriminant((_1.2: std::option::Option)); // scope 0 at $DIR/exponential-or.rs:8:37: 8:48 + switchInt(move _2) -> [0isize: bb4, 1isize: bb3, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:37: 8:48 + } + + bb3: { + switchInt((((_1.2: std::option::Option) as Some).0: i32)) -> [1i32: bb4, 8i32: bb4, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:42: 8:43 + } + + bb4: { + _5 = Le(const 6u32, (_1.3: u32)); // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000006)) + // mir::Constant + // + span: $DIR/exponential-or.rs:8:62: 8:67 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000006)) } + switchInt(move _5) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + } + + bb5: { + _6 = Le((_1.3: u32), const 9u32); // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000009)) + // mir::Constant + // + span: $DIR/exponential-or.rs:8:62: 8:67 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000009)) } + switchInt(move _6) -> [false: bb6, otherwise: bb8]; // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + } + + bb6: { + _3 = Le(const 13u32, (_1.3: u32)); // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x0000000d)) + // mir::Constant + // + span: $DIR/exponential-or.rs:8:70: 8:77 + // + literal: Const { ty: u32, val: Value(Scalar(0x0000000d)) } + switchInt(move _3) -> [false: bb1, otherwise: bb7]; // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + } + + bb7: { + _4 = Le((_1.3: u32), const 16u32); // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000010)) + // mir::Constant + // + span: $DIR/exponential-or.rs:8:70: 8:77 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000010)) } + switchInt(move _4) -> [false: bb1, otherwise: bb8]; // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + } + + bb8: { + falseEdges -> [real: bb9, imaginary: bb1]; // scope 0 at $DIR/exponential-or.rs:8:9: 8:79 + } + + bb9: { + StorageLive(_7); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + _7 = (_1.0: u32); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + StorageLive(_8); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + _8 = (_1.3: u32); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + StorageLive(_9); // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 + _9 = _7; // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 + StorageLive(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + _10 = _8; // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + _0 = BitXor(move _9, move _10); // scope 1 at $DIR/exponential-or.rs:8:83: 8:88 + StorageDead(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + StorageDead(_9); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + StorageDead(_8); // scope 0 at $DIR/exponential-or.rs:8:88: 8:89 + StorageDead(_7); // scope 0 at $DIR/exponential-or.rs:8:88: 8:89 + goto -> bb10; // scope 0 at $DIR/exponential-or.rs:7:5: 10:6 + } + + bb10: { + return; // scope 0 at $DIR/exponential-or.rs:11:2: 11:2 + } +} diff --git a/src/test/mir-opt/generator-drop-cleanup.rs b/src/test/mir-opt/generator-drop-cleanup.rs index 278dc49c92605..3e9707c6491f6 100644 --- a/src/test/mir-opt/generator-drop-cleanup.rs +++ b/src/test/mir-opt/generator-drop-cleanup.rs @@ -1,47 +1,14 @@ #![feature(generators, generator_trait)] +// ignore-wasm32-bare compiled with panic=abort by default + // Regression test for #58892, generator drop shims should not have blocks // spuriously marked as cleanup +// EMIT_MIR rustc.main-{{closure}}.generator_drop.0.mir fn main() { let gen = || { + let _s = String::new(); yield; }; } - -// END RUST SOURCE - -// START rustc.main-{{closure}}.generator_drop.0.mir -// bb0: { -// _7 = discriminant((*_1)); -// switchInt(move _7) -> [0u32: bb4, 3u32: bb7, otherwise: bb8]; -// } -// bb1: { -// StorageDead(_4); -// StorageDead(_3); -// goto -> bb5; -// } -// bb2: { -// return; -// } -// bb3: { -// return; -// } -// bb4: { -// goto -> bb6; -// } -// bb5: { -// goto -> bb2; -// } -// bb6: { -// goto -> bb3; -// } -// bb7: { -// StorageLive(_3); -// StorageLive(_4); -// goto -> bb1; -// } -// bb8: { -// return; -// } -// END rustc.main-{{closure}}.generator_drop.0.mir diff --git a/src/test/mir-opt/generator-drop-cleanup/rustc.main-{{closure}}.generator_drop.0.mir b/src/test/mir-opt/generator-drop-cleanup/rustc.main-{{closure}}.generator_drop.0.mir new file mode 100644 index 0000000000000..382273a1e73e3 --- /dev/null +++ b/src/test/mir-opt/generator-drop-cleanup/rustc.main-{{closure}}.generator_drop.0.mir @@ -0,0 +1,76 @@ +// MIR for `main::{{closure}}#0` 0 generator_drop +// generator_layout = GeneratorLayout { field_tys: [std::string::String], variant_fields: [[], [], [], [_0]], storage_conflicts: BitMatrix { num_rows: 1, num_columns: 1, words: [1], marker: PhantomData } } + +fn main::{{closure}}#0(_1: *mut [generator@$DIR/generator-drop-cleanup.rs:10:15: 13:6 {std::string::String, ()}]) -> () { + let mut _0: (); // return place in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + let mut _2: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + let _3: std::string::String; // in scope 0 at $DIR/generator-drop-cleanup.rs:11:13: 11:15 + let _4: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:12:9: 12:14 + let mut _5: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:12:9: 12:14 + let mut _7: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:18: 10:18 + let mut _8: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + let mut _9: isize; // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + scope 1 { + debug _s => (((*_1) as variant#3).0: std::string::String); // in scope 1 at $DIR/generator-drop-cleanup.rs:11:13: 11:15 + } + scope 2 { + let mut _6: std::vec::Vec; // in scope 2 at $DIR/generator-drop-cleanup.rs:11:18: 11:31 + scope 3 { + } + } + + bb0: { + _9 = discriminant((*_1)); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + switchInt(move _9) -> [0u32: bb7, 3u32: bb10, otherwise: bb11]; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb1: { + StorageDead(_5); // scope 1 at $DIR/generator-drop-cleanup.rs:12:13: 12:14 + StorageDead(_4); // scope 1 at $DIR/generator-drop-cleanup.rs:12:14: 12:15 + drop((((*_1) as variant#3).0: std::string::String)) -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb2: { + nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + goto -> bb8; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb3: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb4 (cleanup): { + resume; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb5 (cleanup): { + nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + goto -> bb4; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb6: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb7: { + goto -> bb9; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb8: { + goto -> bb3; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb9: { + goto -> bb6; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb10: { + StorageLive(_4); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + StorageLive(_5); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + goto -> bb1; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb11: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } +} diff --git a/src/test/mir-opt/generator-storage-dead-unwind.rs b/src/test/mir-opt/generator-storage-dead-unwind.rs index 82b216a99cf55..abfb39c77d6e9 100644 --- a/src/test/mir-opt/generator-storage-dead-unwind.rs +++ b/src/test/mir-opt/generator-storage-dead-unwind.rs @@ -17,6 +17,7 @@ struct Bar(i32); fn take(_x: T) {} +// EMIT_MIR rustc.main-{{closure}}.StateTransform.before.mir fn main() { let _gen = || { let a = Foo(5); @@ -26,89 +27,3 @@ fn main() { take(b); }; } - -// END RUST SOURCE - -// START rustc.main-{{closure}}.StateTransform.before.mir -// ... -// let _3: Foo; -// ... -// let mut _8: Foo; -// ... -// let mut _10: Bar; -// scope 1 { -// debug a => _3; -// let _4: Bar; -// scope 2 { -// debug b => _4; -// } -// } -// bb0: { -// StorageLive(_3); -// _3 = Foo(const 5i32,); -// StorageLive(_4); -// _4 = Bar(const 6i32,); -// ... -// _5 = yield(move _6) -> [resume: bb2, drop: bb4]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// ... -// StorageLive(_7); -// StorageLive(_8); -// _8 = move _3; -// _7 = const take::(move _8) -> [return: bb7, unwind: bb9]; -// } -// bb3 (cleanup): { -// StorageDead(_3); -// drop(_1) -> bb1; -// } -// bb4: { -// ... -// StorageDead(_4); -// drop(_3) -> [return: bb5, unwind: bb3]; -// } -// bb5: { -// StorageDead(_3); -// drop(_1) -> [return: bb6, unwind: bb1]; -// } -// bb6: { -// generator_drop; -// } -// bb7: { -// StorageDead(_8); -// StorageDead(_7); -// StorageLive(_9); -// StorageLive(_10); -// _10 = move _4; -// _9 = const take::(move _10) -> [return: bb10, unwind: bb11]; -// } -// bb8 (cleanup): { -// StorageDead(_4); -// StorageDead(_3); -// drop(_1) -> bb1; -// } -// bb9 (cleanup): { -// StorageDead(_8); -// StorageDead(_7); -// goto -> bb8; -// } -// bb10: { -// StorageDead(_10); -// StorageDead(_9); -// ... -// StorageDead(_4); -// StorageDead(_3); -// drop(_1) -> [return: bb12, unwind: bb1]; -// } -// bb11 (cleanup): { -// StorageDead(_10); -// StorageDead(_9); -// goto -> bb8; -// } -// bb12: { -// return; -// } -// END rustc.main-{{closure}}.StateTransform.before.mir diff --git a/src/test/mir-opt/generator-storage-dead-unwind/rustc.main-{{closure}}.StateTransform.before.mir b/src/test/mir-opt/generator-storage-dead-unwind/rustc.main-{{closure}}.StateTransform.before.mir new file mode 100644 index 0000000000000..e9e977a611b19 --- /dev/null +++ b/src/test/mir-opt/generator-storage-dead-unwind/rustc.main-{{closure}}.StateTransform.before.mir @@ -0,0 +1,136 @@ +// MIR for `main::{{closure}}#0` before StateTransform + +fn main::{{closure}}#0(_1: [generator@$DIR/generator-storage-dead-unwind.rs:22:16: 28:6 {Foo, Bar, ()}], _2: ()) -> () +yields () + { + let mut _0: (); // return place in scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 22:19 + let _3: Foo; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 + let _5: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + let mut _6: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + let _7: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + let mut _8: Foo; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + let _9: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + let mut _10: Bar; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + scope 1 { + debug a => _3; // in scope 1 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 + let _4: Bar; // in scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 + scope 2 { + debug b => _4; // in scope 2 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 + _3 = Foo(const 5i32); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:23:21: 23:22 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } + StorageLive(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 + _4 = Bar(const 6i32); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000006)) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:24:21: 24:22 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000006)) } + StorageLive(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + StorageLive(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + _6 = (); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + _5 = yield(move _6) -> [resume: bb1, drop: bb5]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + } + + bb1: { + StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 + StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 + StorageLive(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + StorageLive(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + _8 = move _3; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + _7 = const take::(move _8) -> [return: bb2, unwind: bb9]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + // ty::Const + // + ty: fn(Foo) {take::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:26:9: 26:13 + // + literal: Const { ty: fn(Foo) {take::}, val: Value(Scalar()) } + } + + bb2: { + StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 + StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 + StorageLive(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + StorageLive(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + _10 = move _4; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + _9 = const take::(move _10) -> [return: bb3, unwind: bb8]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + // ty::Const + // + ty: fn(Bar) {take::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:27:9: 27:13 + // + literal: Const { ty: fn(Bar) {take::}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 + StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 + _0 = const (); // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 28:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:22:19: 28:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> [return: bb4, unwind: bb11]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb4: { + return; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:6: 28:6 + } + + bb5: { + StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 + StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_3) -> [return: bb6, unwind: bb12]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb6: { + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> [return: bb7, unwind: bb11]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb7: { + generator_drop; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 + } + + bb8 (cleanup): { + StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 + StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 + goto -> bb10; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 + } + + bb9 (cleanup): { + StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 + StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 + goto -> bb10; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 + } + + bb10 (cleanup): { + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> bb11; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb11 (cleanup): { + resume; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 + } + + bb12 (cleanup): { + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> bb11; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } +} diff --git a/src/test/mir-opt/generator-tiny.rs b/src/test/mir-opt/generator-tiny.rs index 09e943bd962e6..c86e2865ca8a4 100644 --- a/src/test/mir-opt/generator-tiny.rs +++ b/src/test/mir-opt/generator-tiny.rs @@ -1,7 +1,8 @@ //! Tests that generators that cannot return or unwind don't have unnecessary //! panic branches. -// compile-flags: -Zno-landing-pads +// compile-flags: -C panic=abort +// no-prefer-dynamic #![feature(generators, generator_trait)] @@ -13,6 +14,7 @@ impl Drop for HasDrop { fn callee() {} +// EMIT_MIR rustc.main-{{closure}}.generator_resume.0.mir fn main() { let _gen = |_x: u8| { let _d = HasDrop; @@ -22,13 +24,3 @@ fn main() { } }; } - -// END RUST SOURCE - -// START rustc.main-{{closure}}.generator_resume.0.mir -// bb0: { -// ... -// switchInt(move _11) -> [0u32: bb1, 3u32: bb5, otherwise: bb6]; -// } -// ... -// END rustc.main-{{closure}}.generator_resume.0.mir diff --git a/src/test/mir-opt/generator-tiny/rustc.main-{{closure}}.generator_resume.0.mir b/src/test/mir-opt/generator-tiny/rustc.main-{{closure}}.generator_resume.0.mir new file mode 100644 index 0000000000000..75c2fb3d1307d --- /dev/null +++ b/src/test/mir-opt/generator-tiny/rustc.main-{{closure}}.generator_resume.0.mir @@ -0,0 +1,78 @@ +// MIR for `main::{{closure}}#0` 0 generator_resume +// generator_layout = GeneratorLayout { field_tys: [HasDrop], variant_fields: [[], [], [], [_0]], storage_conflicts: BitMatrix { num_rows: 1, num_columns: 1, words: [1], marker: PhantomData } } + +fn main::{{closure}}#0(_1: std::pin::Pin<&mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]>, _2: u8) -> std::ops::GeneratorState<(), ()> { + debug _x => _10; // in scope 0 at $DIR/generator-tiny.rs:19:17: 19:19 + let mut _0: std::ops::GeneratorState<(), ()>; // return place in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + let _3: HasDrop; // in scope 0 at $DIR/generator-tiny.rs:20:13: 20:15 + let mut _4: !; // in scope 0 at $DIR/generator-tiny.rs:21:9: 24:10 + let mut _5: (); // in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + let _6: u8; // in scope 0 at $DIR/generator-tiny.rs:22:13: 22:18 + let mut _7: (); // in scope 0 at $DIR/generator-tiny.rs:22:13: 22:18 + let _8: (); // in scope 0 at $DIR/generator-tiny.rs:23:13: 23:21 + let mut _9: (); // in scope 0 at $DIR/generator-tiny.rs:19:25: 19:25 + let _10: u8; // in scope 0 at $DIR/generator-tiny.rs:19:17: 19:19 + let mut _11: isize; // in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + scope 1 { + debug _d => (((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}])) as variant#3).0: HasDrop); // in scope 1 at $DIR/generator-tiny.rs:20:13: 20:15 + } + + bb0: { + _11 = discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]))); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + switchInt(move _11) -> [0u32: bb1, 3u32: bb5, otherwise: bb6]; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + } + + bb1: { + _10 = move _2; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + nop; // scope 0 at $DIR/generator-tiny.rs:20:13: 20:15 + (((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}])) as variant#3).0: HasDrop) = HasDrop; // scope 0 at $DIR/generator-tiny.rs:20:18: 20:25 + StorageLive(_4); // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 + goto -> bb2; // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 + } + + bb2: { + StorageLive(_6); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + StorageLive(_7); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + _7 = (); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + _0 = std::ops::GeneratorState::<(), ()>::Yielded(move _7); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]))) = 3; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + return; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + } + + bb3: { + StorageDead(_7); // scope 1 at $DIR/generator-tiny.rs:22:17: 22:18 + StorageDead(_6); // scope 1 at $DIR/generator-tiny.rs:22:18: 22:19 + StorageLive(_8); // scope 1 at $DIR/generator-tiny.rs:23:13: 23:21 + _8 = const callee() -> bb4; // scope 1 at $DIR/generator-tiny.rs:23:13: 23:21 + // ty::Const + // + ty: fn() {callee} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-tiny.rs:23:13: 23:19 + // + literal: Const { ty: fn() {callee}, val: Value(Scalar()) } + } + + bb4: { + StorageDead(_8); // scope 1 at $DIR/generator-tiny.rs:23:21: 23:22 + _5 = const (); // scope 1 at $DIR/generator-tiny.rs:21:14: 24:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-tiny.rs:21:14: 24:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb2; // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 + } + + bb5: { + StorageLive(_4); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + StorageLive(_6); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + StorageLive(_7); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + _6 = move _2; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + goto -> bb3; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + } + + bb6: { + unreachable; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + } +} diff --git a/src/test/mir-opt/graphviz.rs b/src/test/mir-opt/graphviz.rs index fcbb189c1117b..b1c0f0dd3c830 100644 --- a/src/test/mir-opt/graphviz.rs +++ b/src/test/mir-opt/graphviz.rs @@ -1,20 +1,5 @@ // Test graphviz output // compile-flags: -Z dump-mir-graphviz -// ignore-tidy-linelength - +// EMIT_MIR rustc.main.mir_map.0.dot fn main() {} - -// END RUST SOURCE -// START rustc.main.mir_map.0.dot -// digraph Mir_0_3 { // The name here MUST be an ASCII identifier. -// graph [fontname="monospace"]; -// node [fontname="monospace"]; -// edge [fontname="monospace"]; -// label=>; -// bb0__0_3 [shape="none", label=<

\ - Main

{blk}
{blk}
0
_0 = ()
goto
>]; -// bb1__0_3 [shape="none", label=<
1
resume
>]; -// bb2__0_3 [shape="none", label=<
2
return
>]; -// bb0__0_3 -> bb2__0_3 [label=""]; -// } -// END rustc.main.mir_map.0.dot diff --git a/src/test/mir-opt/graphviz/rustc.main.mir_map.0.dot b/src/test/mir-opt/graphviz/rustc.main.mir_map.0.dot new file mode 100644 index 0000000000000..1a66b53c69bc0 --- /dev/null +++ b/src/test/mir-opt/graphviz/rustc.main.mir_map.0.dot @@ -0,0 +1,7 @@ +digraph Mir_0_3 { + graph [fontname="monospace"]; + node [fontname="monospace"]; + edge [fontname="monospace"]; + label=>; + bb0__0_3 [shape="none", label=<
0
_0 = const ()
return
>]; +} diff --git a/src/test/mir-opt/inline/inline-any-operand.rs b/src/test/mir-opt/inline/inline-any-operand.rs index b545500371936..2edde12d72e0b 100644 --- a/src/test/mir-opt/inline/inline-any-operand.rs +++ b/src/test/mir-opt/inline/inline-any-operand.rs @@ -6,6 +6,7 @@ fn main() { println!("{}", bar()); } +// EMIT_MIR rustc.bar.Inline.after.mir fn bar() -> bool { let f = foo; f(1, -1) @@ -15,15 +16,3 @@ fn bar() -> bool { fn foo(x: i32, y: i32) -> bool { x == y } - -// END RUST SOURCE -// START rustc.bar.Inline.after.mir -// ... -// bb0: { -// ... -// _0 = Eq(move _3, move _4); -// ... -// return; -// } -// ... -// END rustc.bar.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-any-operand/rustc.bar.Inline.after.mir b/src/test/mir-opt/inline/inline-any-operand/rustc.bar.Inline.after.mir new file mode 100644 index 0000000000000..19748d52ec7e2 --- /dev/null +++ b/src/test/mir-opt/inline/inline-any-operand/rustc.bar.Inline.after.mir @@ -0,0 +1,47 @@ +// MIR for `bar` after Inline + +fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/inline-any-operand.rs:10:13: 10:17 + let _1: fn(i32, i32) -> bool {foo}; // in scope 0 at $DIR/inline-any-operand.rs:11:9: 11:10 + let mut _2: fn(i32, i32) -> bool {foo}; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:6 + let mut _3: i32; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:13 + let mut _4: i32; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:13 + scope 1 { + debug f => _1; // in scope 1 at $DIR/inline-any-operand.rs:11:9: 11:10 + scope 2 { + debug x => _3; // in scope 2 at $DIR/inline-any-operand.rs:16:8: 16:9 + debug y => _4; // in scope 2 at $DIR/inline-any-operand.rs:16:16: 16:17 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-any-operand.rs:11:9: 11:10 + _1 = const foo; // scope 0 at $DIR/inline-any-operand.rs:11:13: 11:16 + // ty::Const + // + ty: fn(i32, i32) -> bool {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/inline-any-operand.rs:11:13: 11:16 + // + literal: Const { ty: fn(i32, i32) -> bool {foo}, val: Value(Scalar()) } + StorageLive(_2); // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:6 + _2 = _1; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:6 + _3 = const 1i32; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:13 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/inline-any-operand.rs:12:7: 12:8 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + _4 = const -1i32; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:13 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0xffffffff)) + // mir::Constant + // + span: $DIR/inline-any-operand.rs:12:10: 12:12 + // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } + _0 = Eq(move _3, move _4); // scope 2 at $DIR/inline-any-operand.rs:17:5: 17:11 + StorageDead(_2); // scope 1 at $DIR/inline-any-operand.rs:12:12: 12:13 + StorageDead(_1); // scope 0 at $DIR/inline-any-operand.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-any-operand.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline/inline-closure-borrows-arg.rs index 768f495322809..a82a91945d862 100644 --- a/src/test/mir-opt/inline/inline-closure-borrows-arg.rs +++ b/src/test/mir-opt/inline/inline-closure-borrows-arg.rs @@ -7,6 +7,7 @@ fn main() { println!("{}", foo(0, &14)); } +// EMIT_MIR rustc.foo.Inline.after.mir fn foo(_t: T, q: &i32) -> i32 { let x = |r: &i32, _s: &i32| { let variable = &*r; @@ -14,46 +15,3 @@ fn foo(_t: T, q: &i32) -> i32 { }; x(q, q) } - -// END RUST SOURCE -// START rustc.foo.Inline.after.mir -// fn foo(_1: T, _2: &i32) -> i32{ -// debug _t => _1; -// debug q => _2; -// let mut _0: i32; -// let _3: [closure@foo::{{closure}}#0]; -// let mut _4: &[closure@foo::{{closure}}#0]; -// let mut _5: (&i32, &i32); -// let mut _6: &i32; -// let mut _7: &i32; -// let mut _8: &i32; -// let mut _9: &i32; -// scope 1 { -// debug x => _3; -// scope 2 { -// debug r => _8; -// debug _s => _9; -// } -// } -// scope 3 { -// debug variable => _8; -// } -// bb0: { -// ... -// _3 = [closure@foo::::{{closure}}#0]; -// ... -// _4 = &_3; -// ... -// _6 = &(*_2); -// ... -// _7 = &(*_2); -// _5 = (move _6, move _7); -// _8 = move (_5.0: &i32); -// _9 = move (_5.1: &i32); -// ... -// _0 = (*_8); -// ... -// return; -// } -// } -// END rustc.foo.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-closure-borrows-arg/rustc.foo.Inline.after.mir b/src/test/mir-opt/inline/inline-closure-borrows-arg/rustc.foo.Inline.after.mir new file mode 100644 index 0000000000000..cea3c59a3e479 --- /dev/null +++ b/src/test/mir-opt/inline/inline-closure-borrows-arg/rustc.foo.Inline.after.mir @@ -0,0 +1,54 @@ +// MIR for `foo` after Inline + +fn foo(_1: T, _2: &i32) -> i32 { + debug _t => _1; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:17: 11:19 + debug q => _2; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:24: 11:25 + let mut _0: i32; // return place in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:36: 11:39 + let _3: [closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 + let mut _4: &[closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 + let mut _5: (&i32, &i32); // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + let mut _6: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 + let mut _7: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 + let mut _8: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + let mut _9: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + scope 1 { + debug x => _3; // in scope 1 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 + scope 2 { + debug r => _8; // in scope 2 at $DIR/inline-closure-borrows-arg.rs:12:14: 12:15 + debug _s => _9; // in scope 2 at $DIR/inline-closure-borrows-arg.rs:12:23: 12:25 + } + } + scope 3 { + debug variable => _8; // in scope 3 at $DIR/inline-closure-borrows-arg.rs:13:13: 13:21 + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 + _3 = [closure@foo::::{{closure}}#0]; // scope 0 at $DIR/inline-closure-borrows-arg.rs:12:13: 15:6 + // closure + // + def_id: DefId(0:6 ~ inline_closure_borrows_arg[317d]::foo[0]::{{closure}}[0]) + // + substs: [ + // T, + // i8, + // for<'r, 's> extern "rust-call" fn((&'r i32, &'s i32)) -> i32, + // (), + // ] + StorageLive(_4); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 + _4 = &_3; // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 + StorageLive(_5); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + StorageLive(_6); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 + _6 = &(*_2); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 + StorageLive(_7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 + _7 = &(*_2); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 + _5 = (move _6, move _7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + _8 = move (_5.0: &i32); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + _9 = move (_5.1: &i32); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + _0 = (*_8); // scope 3 at $DIR/inline-closure-borrows-arg.rs:14:9: 14:18 + StorageDead(_7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_6); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_5); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_4); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_3); // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:1: 17:2 + return; // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/inline/inline-closure-captures.rs b/src/test/mir-opt/inline/inline-closure-captures.rs index e000a418d90c7..4a0aad9b0e699 100644 --- a/src/test/mir-opt/inline/inline-closure-captures.rs +++ b/src/test/mir-opt/inline/inline-closure-captures.rs @@ -6,55 +6,8 @@ fn main() { println!("{:?}", foo(0, 14)); } +// EMIT_MIR rustc.foo.Inline.after.mir fn foo(t: T, q: i32) -> (i32, T) { let x = |_q| (q, t); x(q) } - -// END RUST SOURCE -// START rustc.foo.Inline.after.mir -// fn foo(_1: T, _2: i32) -> (i32, T){ -// debug t => _1; -// debug q => _2; -// let mut _0: (i32, T); -// let _3: [closure@foo::{{closure}}#0 q:&i32, t:&T]; -// let mut _4: &i32; -// let mut _5: &T; -// let mut _6: &[closure@foo::{{closure}}#0 q:&i32, t:&T]; -// let mut _7: (i32,); -// let mut _8: i32; -// let mut _11: i32; -// scope 1 { -// debug x => _3; -// scope 2 { -// debug _q => _11; -// debug q => (*((*_6).0: &i32)); -// debug t => (*((*_6).1: &T)); -// let mut _9: i32; -// let mut _10: T; -// } -// } -// bb0: { -// ... -// _4 = &_2; -// ... -// _5 = &_1; -// _3 = [closure@foo::::{{closure}}#0] { q: move _4, t: move _5 }; -// ... -// _6 = &_3; -// ... -// ... -// _8 = _2; -// _7 = (move _8,); -// _11 = move (_7.0: i32); -// ... -// _9 = (*((*_6).0: &i32)); -// ... -// _10 = (*((*_6).1: &T)); -// (_0.0: i32) = move _9; -// (_0.1: T) = move _10; -// ... -// return; -// } -// } -// END rustc.foo.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-closure-captures/rustc.foo.Inline.after.mir b/src/test/mir-opt/inline/inline-closure-captures/rustc.foo.Inline.after.mir new file mode 100644 index 0000000000000..eeff914ccffb9 --- /dev/null +++ b/src/test/mir-opt/inline/inline-closure-captures/rustc.foo.Inline.after.mir @@ -0,0 +1,63 @@ +// MIR for `foo` after Inline + +fn foo(_1: T, _2: i32) -> (i32, T) { + debug t => _1; // in scope 0 at $DIR/inline-closure-captures.rs:10:17: 10:18 + debug q => _2; // in scope 0 at $DIR/inline-closure-captures.rs:10:23: 10:24 + let mut _0: (i32, T); // return place in scope 0 at $DIR/inline-closure-captures.rs:10:34: 10:42 + let _3: [closure@foo::{{closure}}#0 q:&i32, t:&T]; // in scope 0 at $DIR/inline-closure-captures.rs:11:9: 11:10 + let mut _4: &i32; // in scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + let mut _5: &T; // in scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + let mut _6: &[closure@foo::{{closure}}#0 q:&i32, t:&T]; // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:6 + let mut _7: (i32,); // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:9 + let mut _8: i32; // in scope 0 at $DIR/inline-closure-captures.rs:12:7: 12:8 + let mut _11: i32; // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:9 + scope 1 { + debug x => _3; // in scope 1 at $DIR/inline-closure-captures.rs:11:9: 11:10 + scope 2 { + debug _q => _11; // in scope 2 at $DIR/inline-closure-captures.rs:11:14: 11:16 + debug q => (*((*_6).0: &i32)); // in scope 2 at $DIR/inline-closure-captures.rs:10:23: 10:24 + debug t => (*((*_6).1: &T)); // in scope 2 at $DIR/inline-closure-captures.rs:10:17: 10:18 + let mut _9: i32; // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9 + let mut _10: T; // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/inline-closure-captures.rs:11:9: 11:10 + StorageLive(_4); // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + _4 = &_2; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + StorageLive(_5); // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + _5 = &_1; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + _3 = [closure@foo::::{{closure}}#0] { q: move _4, t: move _5 }; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + // closure + // + def_id: DefId(0:6 ~ inline_closure_captures[317d]::foo[0]::{{closure}}[0]) + // + substs: [ + // T, + // i8, + // extern "rust-call" fn((i32,)) -> (i32, T), + // (&i32, &T), + // ] + StorageDead(_5); // scope 0 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageDead(_4); // scope 0 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageLive(_6); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:6 + _6 = &_3; // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:6 + StorageLive(_7); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 + StorageLive(_8); // scope 1 at $DIR/inline-closure-captures.rs:12:7: 12:8 + _8 = _2; // scope 1 at $DIR/inline-closure-captures.rs:12:7: 12:8 + _7 = (move _8,); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 + _11 = move (_7.0: i32); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 + StorageLive(_9); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20 + _9 = (*((*_6).0: &i32)); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20 + StorageLive(_10); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23 + _10 = (*((*_6).1: &T)); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23 + (_0.0: i32) = move _9; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24 + (_0.1: T) = move _10; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24 + StorageDead(_10); // scope 2 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageDead(_9); // scope 2 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageDead(_8); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 + StorageDead(_7); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 + StorageDead(_6); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 + StorageDead(_3); // scope 0 at $DIR/inline-closure-captures.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-closure-captures.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline-closure.rs b/src/test/mir-opt/inline/inline-closure.rs index bd36e77818edc..77e424a2bb3f7 100644 --- a/src/test/mir-opt/inline/inline-closure.rs +++ b/src/test/mir-opt/inline/inline-closure.rs @@ -6,45 +6,8 @@ fn main() { println!("{}", foo(0, 14)); } +// EMIT_MIR rustc.foo.Inline.after.mir fn foo(_t: T, q: i32) -> i32 { let x = |_t, _q| _t; x(q, q) } - -// END RUST SOURCE -// START rustc.foo.Inline.after.mir -// fn foo(_1: T, _2: i32) -> i32{ -// debug _t => _1; -// debug q => _2; -// let mut _0: i32; -// let _3: [closure@foo::{{closure}}#0]; -// let mut _4: &[closure@foo::{{closure}}#0]; -// let mut _5: (i32, i32); -// let mut _6: i32; -// let mut _7: i32; -// let mut _8: i32; -// let mut _9: i32; -// scope 1 { -// debug x => _3; -// scope 2 { -// debug _t => _8; -// debug _q => _9; -// } -// } -// bb0: { -// ... -// _3 = [closure@foo::::{{closure}}#0]; -// ... -// _4 = &_3; -// ... -// _6 = _2; -// ... -// _7 = _2; -// _5 = (move _6, move _7); -// _8 = move (_5.0: i32); -// _9 = move (_5.1: i32); -// _0 = _8; -// ... -// return; -// } -// END rustc.foo.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-closure/rustc.foo.Inline.after.mir b/src/test/mir-opt/inline/inline-closure/rustc.foo.Inline.after.mir new file mode 100644 index 0000000000000..bd0ec8c7ddbd5 --- /dev/null +++ b/src/test/mir-opt/inline/inline-closure/rustc.foo.Inline.after.mir @@ -0,0 +1,51 @@ +// MIR for `foo` after Inline + +fn foo(_1: T, _2: i32) -> i32 { + debug _t => _1; // in scope 0 at $DIR/inline-closure.rs:10:17: 10:19 + debug q => _2; // in scope 0 at $DIR/inline-closure.rs:10:24: 10:25 + let mut _0: i32; // return place in scope 0 at $DIR/inline-closure.rs:10:35: 10:38 + let _3: [closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure.rs:11:9: 11:10 + let mut _4: &[closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:6 + let mut _5: (i32, i32); // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 + let mut _6: i32; // in scope 0 at $DIR/inline-closure.rs:12:7: 12:8 + let mut _7: i32; // in scope 0 at $DIR/inline-closure.rs:12:10: 12:11 + let mut _8: i32; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 + let mut _9: i32; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 + scope 1 { + debug x => _3; // in scope 1 at $DIR/inline-closure.rs:11:9: 11:10 + scope 2 { + debug _t => _8; // in scope 2 at $DIR/inline-closure.rs:11:14: 11:16 + debug _q => _9; // in scope 2 at $DIR/inline-closure.rs:11:18: 11:20 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/inline-closure.rs:11:9: 11:10 + _3 = [closure@foo::::{{closure}}#0]; // scope 0 at $DIR/inline-closure.rs:11:13: 11:24 + // closure + // + def_id: DefId(0:6 ~ inline_closure[317d]::foo[0]::{{closure}}[0]) + // + substs: [ + // T, + // i8, + // extern "rust-call" fn((i32, i32)) -> i32, + // (), + // ] + StorageLive(_4); // scope 1 at $DIR/inline-closure.rs:12:5: 12:6 + _4 = &_3; // scope 1 at $DIR/inline-closure.rs:12:5: 12:6 + StorageLive(_5); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + StorageLive(_6); // scope 1 at $DIR/inline-closure.rs:12:7: 12:8 + _6 = _2; // scope 1 at $DIR/inline-closure.rs:12:7: 12:8 + StorageLive(_7); // scope 1 at $DIR/inline-closure.rs:12:10: 12:11 + _7 = _2; // scope 1 at $DIR/inline-closure.rs:12:10: 12:11 + _5 = (move _6, move _7); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + _8 = move (_5.0: i32); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + _9 = move (_5.1: i32); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + _0 = _8; // scope 2 at $DIR/inline-closure.rs:11:22: 11:24 + StorageDead(_7); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_6); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_5); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_4); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_3); // scope 0 at $DIR/inline-closure.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-closure.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline-into-box-place.rs b/src/test/mir-opt/inline/inline-into-box-place.rs index f368bdef6f8e2..77834e9661cec 100644 --- a/src/test/mir-opt/inline/inline-into-box-place.rs +++ b/src/test/mir-opt/inline/inline-into-box-place.rs @@ -1,72 +1,9 @@ -// ignore-tidy-linelength // ignore-wasm32-bare compiled with panic=abort by default // compile-flags: -Z mir-opt-level=3 +// EMIT_MIR_FOR_EACH_BIT_WIDTH #![feature(box_syntax)] +// EMIT_MIR rustc.main.Inline.diff fn main() { let _x: Box> = box Vec::new(); } - -// END RUST SOURCE -// START rustc.main.Inline.before.mir -// let mut _0: (); -// let _1: std::boxed::Box> as UserTypeProjection { base: UserType(0), projs: [] }; -// let mut _2: std::boxed::Box>; -// let mut _3: (); -// scope 1 { -// debug _x => _1; -// } -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = Box(std::vec::Vec); -// (*_2) = const std::vec::Vec::::new() -> [return: bb2, unwind: bb4]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// _1 = move _2; -// StorageDead(_2); -// _0 = (); -// drop(_1) -> [return: bb3, unwind: bb1]; -// } -// bb3: { -// StorageDead(_1); -// return; -// } -// bb4 (cleanup): { -// _3 = const alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb1; -// } -// END rustc.main.Inline.before.mir -// START rustc.main.Inline.after.mir -// let mut _0: (); -// let _1: std::boxed::Box> as UserTypeProjection { base: UserType(0), projs: [] }; -// let mut _2: std::boxed::Box>; -// let mut _3: (); -// let mut _4: &mut std::vec::Vec; -// scope 1 { -// debug _x => _1; -// } -// scope 2 { -// } -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = Box(std::vec::Vec); -// _4 = &mut (*_2); -// ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec::::NEW; -// ((*_4).1: usize) = const 0usize; -// _1 = move _2; -// StorageDead(_2); -// _0 = (); -// drop(_1) -> [return: bb2, unwind: bb1]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// StorageDead(_1); -// return; -// } -// END rustc.main.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-into-box-place/32bit/rustc.main.Inline.diff b/src/test/mir-opt/inline/inline-into-box-place/32bit/rustc.main.Inline.diff new file mode 100644 index 0000000000000..607dd468e598b --- /dev/null +++ b/src/test/mir-opt/inline/inline-into-box-place/32bit/rustc.main.Inline.diff @@ -0,0 +1,79 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 + let _1: std::boxed::Box> as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- (*_2) = const std::vec::Vec::::new() -> [return: bb1, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: std::ptr::Unique:: { pointer: {0x4 as *const u32}, _marker: std::marker::PhantomData:: }, cap: 0usize, alloc: std::alloc::Global }; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL + // ty::Const +- // + ty: fn() -> std::vec::Vec {std::vec::Vec::::new} +- // + val: Value(Scalar()) ++ // + ty: alloc::raw_vec::RawVec ++ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) + // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 +- // + user_ty: UserType(1) +- // + literal: Const { ty: fn() -> std::vec::Vec {std::vec::Vec::::new}, val: Value(Scalar()) } +- } +- +- bb1: { ++ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL ++ // + user_ty: UserType(0) ++ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ ((*_4).1: usize) = const 0usize; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL ++ // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 + _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/inline-into-box-place.rs:7:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } +- drop(_1) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 ++ drop(_1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + } + +- bb2: { ++ bb1: { + StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 + } + +- bb3 (cleanup): { ++ bb2 (cleanup): { + resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 +- } +- +- bb4 (cleanup): { +- _3 = const alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb3; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- // ty::Const +- // + ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>} +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 +- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff b/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff new file mode 100644 index 0000000000000..e83ca36706af7 --- /dev/null +++ b/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff @@ -0,0 +1,79 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 + let _1: std::boxed::Box> as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- (*_2) = const std::vec::Vec::::new() -> [return: bb1, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: std::ptr::Unique:: { pointer: {0x4 as *const u32}, _marker: std::marker::PhantomData:: }, cap: 0usize, alloc: std::alloc::Global }; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL + // ty::Const +- // + ty: fn() -> std::vec::Vec {std::vec::Vec::::new} +- // + val: Value(Scalar()) ++ // + ty: alloc::raw_vec::RawVec ++ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) + // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 +- // + user_ty: UserType(1) +- // + literal: Const { ty: fn() -> std::vec::Vec {std::vec::Vec::::new}, val: Value(Scalar()) } +- } +- +- bb1: { ++ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL ++ // + user_ty: UserType(0) ++ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ ((*_4).1: usize) = const 0usize; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL ++ // ty::Const ++ // + ty: usize ++ // + val: Value(Scalar(0x0000000000000000)) ++ // mir::Constant ++ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL ++ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 + _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/inline-into-box-place.rs:7:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } +- drop(_1) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 ++ drop(_1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + } + +- bb2: { ++ bb1: { + StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 + } + +- bb3 (cleanup): { ++ bb2 (cleanup): { + resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 +- } +- +- bb4 (cleanup): { +- _3 = const alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb3; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- // ty::Const +- // + ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>} +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 +- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/inline/inline-retag.rs b/src/test/mir-opt/inline/inline-retag.rs index 7b78fc339f2c1..d7e425ec6586f 100644 --- a/src/test/mir-opt/inline/inline-retag.rs +++ b/src/test/mir-opt/inline/inline-retag.rs @@ -6,6 +6,7 @@ fn main() { println!("{}", bar()); } +// EMIT_MIR rustc.bar.Inline.after.mir fn bar() -> bool { let f = foo; f(&1, &-1) @@ -15,23 +16,3 @@ fn bar() -> bool { fn foo(x: &i32, y: &i32) -> bool { *x == *y } - -// END RUST SOURCE -// START rustc.bar.Inline.after.mir -// ... -// bb0: { -// ... -// Retag(_3); -// ... -// Retag(_3); -// Retag(_6); -// StorageLive(_11); -// _11 = (*_3); -// StorageLive(_12); -// _12 = (*_6); -// _0 = Eq(move _11, move _12); -// ... -// return; -// } -// ... -// END rustc.bar.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-retag/rustc.bar.Inline.after.mir b/src/test/mir-opt/inline/inline-retag/rustc.bar.Inline.after.mir new file mode 100644 index 0000000000000..e83cc92eb43ef --- /dev/null +++ b/src/test/mir-opt/inline/inline-retag/rustc.bar.Inline.after.mir @@ -0,0 +1,81 @@ +// MIR for `bar` after Inline + +fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/inline-retag.rs:10:13: 10:17 + let _1: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}; // in scope 0 at $DIR/inline-retag.rs:11:9: 11:10 + let mut _2: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}; // in scope 0 at $DIR/inline-retag.rs:12:5: 12:6 + let mut _3: &i32; // in scope 0 at $DIR/inline-retag.rs:12:7: 12:9 + let _4: &i32; // in scope 0 at $DIR/inline-retag.rs:12:7: 12:9 + let _5: i32; // in scope 0 at $DIR/inline-retag.rs:12:8: 12:9 + let mut _6: &i32; // in scope 0 at $DIR/inline-retag.rs:12:11: 12:14 + let _7: &i32; // in scope 0 at $DIR/inline-retag.rs:12:11: 12:14 + let _8: i32; // in scope 0 at $DIR/inline-retag.rs:12:12: 12:14 + scope 1 { + debug f => _1; // in scope 1 at $DIR/inline-retag.rs:11:9: 11:10 + let mut _9: &i32; // in scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + let mut _10: &i32; // in scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + scope 2 { + debug x => _3; // in scope 2 at $DIR/inline-retag.rs:16:8: 16:9 + debug y => _6; // in scope 2 at $DIR/inline-retag.rs:16:17: 16:18 + let mut _11: i32; // in scope 2 at $DIR/inline-retag.rs:12:5: 12:15 + let mut _12: i32; // in scope 2 at $DIR/inline-retag.rs:12:5: 12:15 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-retag.rs:11:9: 11:10 + _1 = const foo; // scope 0 at $DIR/inline-retag.rs:11:13: 11:16 + // ty::Const + // + ty: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/inline-retag.rs:11:13: 11:16 + // + literal: Const { ty: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}, val: Value(Scalar()) } + StorageLive(_2); // scope 1 at $DIR/inline-retag.rs:12:5: 12:6 + _2 = _1; // scope 1 at $DIR/inline-retag.rs:12:5: 12:6 + StorageLive(_3); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + StorageLive(_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + _10 = const bar::promoted[1]; // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(DefId(0:4 ~ inline_retag[317d]::bar[0]), [], Some(promoted[1])) + // mir::Constant + // + span: $DIR/inline-retag.rs:12:7: 12:9 + // + literal: Const { ty: &i32, val: Unevaluated(DefId(0:4 ~ inline_retag[317d]::bar[0]), [], Some(promoted[1])) } + Retag(_10); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + _4 = &(*_10); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + Retag(_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + _3 = &(*_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + Retag(_3); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + StorageLive(_6); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + StorageLive(_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + _9 = const bar::promoted[0]; // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(DefId(0:4 ~ inline_retag[317d]::bar[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/inline-retag.rs:12:11: 12:14 + // + literal: Const { ty: &i32, val: Unevaluated(DefId(0:4 ~ inline_retag[317d]::bar[0]), [], Some(promoted[0])) } + Retag(_9); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + _7 = &(*_9); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + Retag(_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + _6 = &(*_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + Retag(_6); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + Retag(_3); // scope 2 at $DIR/inline-retag.rs:16:1: 18:2 + Retag(_6); // scope 2 at $DIR/inline-retag.rs:16:1: 18:2 + StorageLive(_11); // scope 2 at $DIR/inline-retag.rs:17:5: 17:7 + _11 = (*_3); // scope 2 at $DIR/inline-retag.rs:17:5: 17:7 + StorageLive(_12); // scope 2 at $DIR/inline-retag.rs:17:11: 17:13 + _12 = (*_6); // scope 2 at $DIR/inline-retag.rs:17:11: 17:13 + _0 = Eq(move _11, move _12); // scope 2 at $DIR/inline-retag.rs:17:5: 17:13 + StorageDead(_12); // scope 2 at $DIR/inline-retag.rs:17:12: 17:13 + StorageDead(_11); // scope 2 at $DIR/inline-retag.rs:17:12: 17:13 + StorageDead(_6); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 + StorageDead(_3); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 + StorageDead(_2); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 + StorageDead(_1); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 + StorageDead(_7); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 + StorageDead(_4); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-retag.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline-specialization.rs b/src/test/mir-opt/inline/inline-specialization.rs index 9591019bb4f70..fcdaca460a9de 100644 --- a/src/test/mir-opt/inline/inline-specialization.rs +++ b/src/test/mir-opt/inline/inline-specialization.rs @@ -1,5 +1,6 @@ #![feature(specialization)] +// EMIT_MIR rustc.main.Inline.diff fn main() { let x = as Foo>::bar(); } @@ -12,37 +13,3 @@ impl Foo for Vec { #[inline(always)] default fn bar() -> u32 { 123 } } - -// END RUST SOURCE -// START rustc.main.Inline.before.mir -// let mut _0: (); -// let _1: u32; -// scope 1 { -// debug x => _1; -// } -// bb0: { -// StorageLive(_1); -// _1 = const as Foo>::bar() -> bb1; -// } -// bb1: { -// _0 = (); -// StorageDead(_1); -// return; -// } -// END rustc.main.Inline.before.mir -// START rustc.main.Inline.after.mir -// let mut _0: (); -// let _1: u32; -// scope 1 { -// debug x => _1; -// } -// scope 2 { -// } -// bb0: { -// StorageLive(_1); -// _1 = const 123u32; -// _0 = (); -// StorageDead(_1); -// return; -// } -// END rustc.main.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-specialization/rustc.main.Inline.diff b/src/test/mir-opt/inline/inline-specialization/rustc.main.Inline.diff new file mode 100644 index 0000000000000..a3e0d0a57a7c3 --- /dev/null +++ b/src/test/mir-opt/inline/inline-specialization/rustc.main.Inline.diff @@ -0,0 +1,41 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-specialization.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/inline-specialization.rs:5:9: 5:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/inline-specialization.rs:5:9: 5:10 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-specialization.rs:5:9: 5:10 +- _1 = const as Foo>::bar() -> bb1; // scope 0 at $DIR/inline-specialization.rs:5:13: 5:38 ++ _1 = const 123u32; // scope 2 at $DIR/inline-specialization.rs:14:31: 14:34 + // ty::Const +- // + ty: fn() -> u32 { as Foo>::bar} +- // + val: Value(Scalar()) ++ // + ty: u32 ++ // + val: Value(Scalar(0x0000007b)) + // mir::Constant +- // + span: $DIR/inline-specialization.rs:5:13: 5:36 +- // + literal: Const { ty: fn() -> u32 { as Foo>::bar}, val: Value(Scalar()) } +- } +- +- bb1: { ++ // + span: $DIR/inline-specialization.rs:14:31: 14:34 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000007b)) } + _0 = const (); // scope 0 at $DIR/inline-specialization.rs:4:11: 6:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/inline-specialization.rs:4:11: 6:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/inline-specialization.rs:6:1: 6:2 + return; // scope 0 at $DIR/inline-specialization.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/inline/inline-trait-method.rs b/src/test/mir-opt/inline/inline-trait-method.rs index a2c5fb920cd30..cb3db9b559246 100644 --- a/src/test/mir-opt/inline/inline-trait-method.rs +++ b/src/test/mir-opt/inline/inline-trait-method.rs @@ -4,6 +4,7 @@ fn main() { println!("{}", test(&())); } +// EMIT_MIR rustc.test.Inline.after.mir fn test(x: &dyn X) -> u32 { x.y() } @@ -19,13 +20,3 @@ impl X for () { 2 } } - -// END RUST SOURCE -// START rustc.test.Inline.after.mir -// ... -// bb0: { -// ... -// _0 = const ::y(move _2) -> bb1; -// } -// ... -// END rustc.test.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-trait-method/rustc.test.Inline.after.mir b/src/test/mir-opt/inline/inline-trait-method/rustc.test.Inline.after.mir new file mode 100644 index 0000000000000..8acc5ad5c0935 --- /dev/null +++ b/src/test/mir-opt/inline/inline-trait-method/rustc.test.Inline.after.mir @@ -0,0 +1,24 @@ +// MIR for `test` after Inline + +fn test(_1: &dyn X) -> u32 { + debug x => _1; // in scope 0 at $DIR/inline-trait-method.rs:8:9: 8:10 + let mut _0: u32; // return place in scope 0 at $DIR/inline-trait-method.rs:8:23: 8:26 + let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 + _2 = &(*_1); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 + _0 = const ::y(move _2) -> bb1; // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10 + // ty::Const + // + ty: for<'r> fn(&'r dyn X) -> u32 {::y} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/inline-trait-method.rs:9:7: 9:8 + // + literal: Const { ty: for<'r> fn(&'r dyn X) -> u32 {::y}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/inline-trait-method.rs:9:9: 9:10 + return; // scope 0 at $DIR/inline-trait-method.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/inline/inline-trait-method_2.rs b/src/test/mir-opt/inline/inline-trait-method_2.rs index 4ad4311113a3a..e37f091c5cd2b 100644 --- a/src/test/mir-opt/inline/inline-trait-method_2.rs +++ b/src/test/mir-opt/inline/inline-trait-method_2.rs @@ -1,5 +1,6 @@ // compile-flags: -Z span_free_formats -Z mir-opt-level=3 +// EMIT_MIR rustc.test2.Inline.after.mir fn test2(x: &dyn X) -> bool { test(x) } @@ -24,13 +25,3 @@ impl X for () { fn main() { println!("Should be true: {}", test2(&())); } - -// END RUST SOURCE -// START rustc.test2.Inline.after.mir -// ... -// bb0: { -// ... -// _0 = const ::y(move _2) -> bb1; -// } -// ... -// END rustc.test2.Inline.after.mir diff --git a/src/test/mir-opt/inline/inline-trait-method_2/rustc.test2.Inline.after.mir b/src/test/mir-opt/inline/inline-trait-method_2/rustc.test2.Inline.after.mir new file mode 100644 index 0000000000000..afea1d5ebffa2 --- /dev/null +++ b/src/test/mir-opt/inline/inline-trait-method_2/rustc.test2.Inline.after.mir @@ -0,0 +1,31 @@ +// MIR for `test2` after Inline + +fn test2(_1: &dyn X) -> bool { + debug x => _1; // in scope 0 at $DIR/inline-trait-method_2.rs:4:10: 4:11 + let mut _0: bool; // return place in scope 0 at $DIR/inline-trait-method_2.rs:4:24: 4:28 + let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + let mut _3: &dyn X; // in scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + scope 1 { + debug x => _2; // in scope 1 at $DIR/inline-trait-method_2.rs:9:9: 9:10 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + StorageLive(_3); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + _3 = &(*_1); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + _2 = move _3 as &dyn X (Pointer(Unsize)); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + StorageDead(_3); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + _0 = const ::y(move _2) -> bb1; // scope 1 at $DIR/inline-trait-method_2.rs:10:5: 10:10 + // ty::Const + // + ty: for<'r> fn(&'r dyn X) -> bool {::y} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/inline-trait-method_2.rs:10:7: 10:8 + // + literal: Const { ty: for<'r> fn(&'r dyn X) -> bool {::y}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/inline-trait-method_2.rs:5:11: 5:12 + return; // scope 0 at $DIR/inline-trait-method_2.rs:6:2: 6:2 + } +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut.rs b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut.rs new file mode 100644 index 0000000000000..317705f761212 --- /dev/null +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut.rs @@ -0,0 +1,27 @@ +// EMIT_MIR rustc.a.Inline.after.mir +pub fn a(x: &mut [T]) -> &mut [T] { + x.as_mut() +} + +// EMIT_MIR rustc.b.Inline.after.mir +pub fn b(x: &mut Box) -> &mut T { + x.as_mut() +} + +// EMIT_MIR rustc.c.Inline.after.mir +pub fn c(x: &[T]) -> &[T] { + x.as_ref() +} + +// EMIT_MIR rustc.d.Inline.after.mir +pub fn d(x: &Box) -> &T { + x.as_ref() +} + +fn main() { + let mut boxed = Box::new(1); + println!("{:?}", a(&mut [1])); + println!("{:?}", b(&mut boxed)); + println!("{:?}", c(&[1])); + println!("{:?}", d(&boxed)); +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir new file mode 100644 index 0000000000000..2eebf3f0eceb9 --- /dev/null +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir @@ -0,0 +1,26 @@ +// MIR for `a` after Inline + +fn a(_1: &mut [T]) -> &mut [T] { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:13: 2:14 + let mut _0: &mut [T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:29: 2:37 + let mut _2: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + let mut _3: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + let mut _4: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + scope 1 { + debug self => _4; // in scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + _3 = _4; // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:14: 3:15 + _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:2: 4:2 + } +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir new file mode 100644 index 0000000000000..f9e1699c55dfa --- /dev/null +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir @@ -0,0 +1,30 @@ +// MIR for `b` after Inline + +fn b(_1: &mut std::boxed::Box) -> &mut T { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:13: 7:14 + let mut _0: &mut T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:32: 7:38 + let mut _2: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _3: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _4: &mut std::boxed::Box; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + scope 1 { + debug self => _4; // in scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + let mut _5: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + StorageLive(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _5 = &mut (*(*_4)); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _3 = _5; // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + StorageDead(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:14: 8:15 + _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:2: 9:2 + } +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir new file mode 100644 index 0000000000000..67aea63bd9558 --- /dev/null +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir @@ -0,0 +1,22 @@ +// MIR for `c` after Inline + +fn c(_1: &[T]) -> &[T] { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:13: 12:14 + let mut _0: &[T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:25: 12:29 + let _2: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 + let mut _3: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 + scope 1 { + debug self => _3; // in scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 + _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 + _2 = _3; // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + _0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:14: 13:15 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:14:1: 14:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:14:2: 14:2 + } +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.d.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.d.Inline.after.mir new file mode 100644 index 0000000000000..08bd4784bde18 --- /dev/null +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.d.Inline.after.mir @@ -0,0 +1,22 @@ +// MIR for `d` after Inline + +fn d(_1: &std::boxed::Box) -> &T { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:13: 17:14 + let mut _0: &T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:28: 17:30 + let _2: &T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 + let mut _3: &std::boxed::Box; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 + scope 1 { + debug self => _3; // in scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 + _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 + _2 = &(*(*_3)); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:14: 18:15 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:19:1: 19:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:19:2: 19:2 + } +} diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index d980cc891dc40..f6883ac80861e 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -1,5 +1,6 @@ // check that we don't StorageDead booleans before they are used +// EMIT_MIR rustc.main.SimplifyCfg-initial.after.mir fn main() { let mut should_break = false; loop { @@ -9,42 +10,3 @@ fn main() { should_break = true; } } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-initial.after.mir -// bb0: { -// StorageLive(_1); -// _1 = const false; -// FakeRead(ForLet, _1); -// goto -> bb2; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// falseUnwind -> [real: bb3, cleanup: bb1]; -// } -// bb3: { -// StorageLive(_3); -// StorageLive(_4); -// _4 = _1; -// FakeRead(ForMatchedPlace, _4); -// switchInt(_4) -> [false: bb5, otherwise: bb4]; -// } -// ... -// bb5: { -// _3 = (); -// StorageDead(_4); -// StorageDead(_3); -// _1 = const true; -// _2 = (); -// goto -> bb2; -// } -// bb6: { -// _0 = (); -// StorageDead(_4); -// StorageDead(_3); -// StorageDead(_1); -// return; -// } -// END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/issue-38669/rustc.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/issue-38669/rustc.main.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..525254b15d496 --- /dev/null +++ b/src/test/mir-opt/issue-38669/rustc.main.SimplifyCfg-initial.after.mir @@ -0,0 +1,87 @@ +// MIR for `main` after SimplifyCfg-initial + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-38669.rs:4:11: 4:11 + let mut _1: bool; // in scope 0 at $DIR/issue-38669.rs:5:9: 5:25 + let mut _2: (); // in scope 0 at $DIR/issue-38669.rs:4:1: 12:2 + let _3: (); // in scope 0 at $DIR/issue-38669.rs:7:9: 9:10 + let mut _4: bool; // in scope 0 at $DIR/issue-38669.rs:7:12: 7:24 + let mut _5: !; // in scope 0 at $DIR/issue-38669.rs:7:25: 9:10 + scope 1 { + debug should_break => _1; // in scope 1 at $DIR/issue-38669.rs:5:9: 5:25 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25 + _1 = const false; // scope 0 at $DIR/issue-38669.rs:5:28: 5:33 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-38669.rs:5:28: 5:33 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + FakeRead(ForLet, _1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25 + goto -> bb1; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb1: { + falseUnwind -> [real: bb2, cleanup: bb6]; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb2: { + StorageLive(_3); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + StorageLive(_4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + _4 = _1; // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + FakeRead(ForMatchedPlace, _4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + switchInt(_4) -> [false: bb4, otherwise: bb3]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + } + + bb3: { + falseEdges -> [real: bb5, imaginary: bb4]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + } + + bb4: { + _3 = const (); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-38669.rs:7:9: 9:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + _1 = const true; // scope 1 at $DIR/issue-38669.rs:10:9: 10:28 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-38669.rs:10:24: 10:28 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _2 = const (); // scope 1 at $DIR/issue-38669.rs:6:10: 11:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-38669.rs:6:10: 11:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb1; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb5: { + _0 = const (); // scope 1 at $DIR/issue-38669.rs:8:13: 8:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-38669.rs:8:13: 8:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_1); // scope 0 at $DIR/issue-38669.rs:12:1: 12:2 + return; // scope 0 at $DIR/issue-38669.rs:12:2: 12:2 + } + + bb6 (cleanup): { + resume; // scope 0 at $DIR/issue-38669.rs:4:1: 12:2 + } +} diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs index 5ba54f98d00da..cc35b8785a733 100644 --- a/src/test/mir-opt/issue-41110.rs +++ b/src/test/mir-opt/issue-41110.rs @@ -2,12 +2,15 @@ // check that we don't emit multiple drop flags when they are not needed. + +// EMIT_MIR rustc.main.ElaborateDrops.after.mir fn main() { let x = S.other(S.id()); } // no_mangle to make sure this gets instantiated even in an executable. #[no_mangle] +// EMIT_MIR rustc.test.ElaborateDrops.after.mir pub fn test() { let u = S; let mut v = S; @@ -25,34 +28,3 @@ impl S { fn id(self) -> Self { self } fn other(self, s: Self) {} } - -// END RUST SOURCE -// START rustc.main.ElaborateDrops.after.mir -// let mut _0: (); -// let _1: (); -// let mut _2: S; -// let mut _3: S; -// let mut _4: S; -// let mut _5: bool; -// scope 1 { -// debug x => _1; -// } -// ... -// bb0: { -// END rustc.main.ElaborateDrops.after.mir -// START rustc.test.ElaborateDrops.after.mir -// let mut _0: (); -// let _1: S; -// let _3: (); -// let mut _4: S; -// let mut _5: S; -// let mut _6: bool; -// ... -// debug u => _1; -// ... -// let mut _2: S; -// ... -// debug v => _2; -// ... -// bb0: { -// END rustc.test.ElaborateDrops.after.mir diff --git a/src/test/mir-opt/issue-41110/rustc.main.ElaborateDrops.after.mir b/src/test/mir-opt/issue-41110/rustc.main.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..3272ca8454e9c --- /dev/null +++ b/src/test/mir-opt/issue-41110/rustc.main.ElaborateDrops.after.mir @@ -0,0 +1,106 @@ +// MIR for `main` after ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:7:11: 7:11 + let _1: (); // in scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + let mut _2: S; // in scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + let mut _3: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + let mut _5: bool; // in scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/issue-41110.rs:8:9: 8:10 + } + + bb0: { + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:9: 8:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + StorageLive(_2); // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + _5 = const true; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:13: 8:14 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _2 = S; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + StorageLive(_3); // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + StorageLive(_4); // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + _4 = S; // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + _3 = const S::id(move _4) -> [return: bb1, unwind: bb4]; // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + // ty::Const + // + ty: fn(S) -> S {S::id} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:23: 8:25 + // + literal: Const { ty: fn(S) -> S {S::id}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_4); // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:13: 8:28 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _1 = const S::other(move _2, move _3) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 + // ty::Const + // + ty: fn(S, S) {S::other} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:15: 8:20 + // + literal: Const { ty: fn(S, S) {S::other}, val: Value(Scalar()) } + } + + bb2: { + StorageDead(_3); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:27: 8:28 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_2); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + _0 = const (); // scope 0 at $DIR/issue-41110.rs:7:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:7:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-41110.rs:9:2: 9:2 + } + + bb3 (cleanup): { + goto -> bb5; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb4 (cleanup): { + goto -> bb5; // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 + } + + bb5 (cleanup): { + goto -> bb8; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb6 (cleanup): { + resume; // scope 0 at $DIR/issue-41110.rs:7:1: 9:2 + } + + bb7 (cleanup): { + drop(_2) -> bb6; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb8 (cleanup): { + switchInt(_5) -> [false: bb6, otherwise: bb7]; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } +} diff --git a/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir b/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..99da0398d9639 --- /dev/null +++ b/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir @@ -0,0 +1,134 @@ +// MIR for `test` after ElaborateDrops + +fn test() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:14:15: 14:15 + let _1: S; // in scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + let _3: (); // in scope 0 at $DIR/issue-41110.rs:17:5: 17:12 + let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:17:10: 17:11 + let mut _5: S; // in scope 0 at $DIR/issue-41110.rs:18:9: 18:10 + let mut _6: bool; // in scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + scope 1 { + debug u => _1; // in scope 1 at $DIR/issue-41110.rs:15:9: 15:10 + let mut _2: S; // in scope 1 at $DIR/issue-41110.rs:16:9: 16:14 + scope 2 { + debug v => _2; // in scope 2 at $DIR/issue-41110.rs:16:9: 16:14 + } + } + + bb0: { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:15:9: 15:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + _6 = const true; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41110.rs:15:13: 15:14 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _1 = S; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 + StorageLive(_2); // scope 1 at $DIR/issue-41110.rs:16:9: 16:14 + _2 = S; // scope 1 at $DIR/issue-41110.rs:16:17: 16:18 + StorageLive(_3); // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 + StorageLive(_4); // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 + _4 = move _2; // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 + _3 = const std::mem::drop::(move _4) -> [return: bb1, unwind: bb7]; // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 + // ty::Const + // + ty: fn(S) {std::mem::drop::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:17:5: 17:9 + // + literal: Const { ty: fn(S) {std::mem::drop::}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_4); // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 + StorageDead(_3); // scope 2 at $DIR/issue-41110.rs:17:12: 17:13 + StorageLive(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + _6 = const false; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:18:9: 18:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _5 = move _1; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + goto -> bb12; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb2: { + goto -> bb3; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + } + + bb3: { + StorageDead(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + _0 = const (); // scope 0 at $DIR/issue-41110.rs:14:15: 19:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:14:15: 19:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_2) -> [return: bb4, unwind: bb9]; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb4: { + StorageDead(_2); // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + goto -> bb5; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb5: { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:19:1: 19:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + return; // scope 0 at $DIR/issue-41110.rs:19:2: 19:2 + } + + bb6 (cleanup): { + goto -> bb8; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + } + + bb7 (cleanup): { + goto -> bb8; // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 + } + + bb8 (cleanup): { + goto -> bb9; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb9 (cleanup): { + goto -> bb14; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb10 (cleanup): { + resume; // scope 0 at $DIR/issue-41110.rs:14:1: 19:2 + } + + bb11 (cleanup): { + _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + goto -> bb6; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb12: { + _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + goto -> bb2; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb13 (cleanup): { + drop(_1) -> bb10; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb14 (cleanup): { + switchInt(_6) -> [false: bb10, otherwise: bb13]; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } +} diff --git a/src/test/mir-opt/issue-41697.rs b/src/test/mir-opt/issue-41697.rs index 5a461d6148254..07b9d175677ca 100644 --- a/src/test/mir-opt/issue-41697.rs +++ b/src/test/mir-opt/issue-41697.rs @@ -13,7 +13,9 @@ trait Foo { fn get(&self) -> [u8; 2]; } -impl Foo for [u8; 2] { +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir +impl Foo for [u8; 1+1] { fn get(&self) -> [u8; 2] { *self } diff --git a/src/test/mir-opt/issue-41697/32bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir b/src/test/mir-opt/issue-41697/32bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir new file mode 100644 index 0000000000000..0588ec9b4ceef --- /dev/null +++ b/src/test/mir-opt/issue-41697/32bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir @@ -0,0 +1,32 @@ +// MIR for `::{{constant}}#0` after SimplifyCfg-qualify-consts + +::{{constant}}#0: usize = { + let mut _0: usize; // return place in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + let mut _1: (usize, bool); // in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + + bb0: { + _1 = CheckedAdd(const 1usize, const 1usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/issue-41697.rs:18:19: 18:20 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/issue-41697.rs:18:21: 18:22 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + assert(!move (_1.1: bool), "attempt to add with overflow") -> [success: bb1, unwind: bb2]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb1: { + _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } +} diff --git a/src/test/mir-opt/issue-41697/64bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir b/src/test/mir-opt/issue-41697/64bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir new file mode 100644 index 0000000000000..4040403ffa059 --- /dev/null +++ b/src/test/mir-opt/issue-41697/64bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir @@ -0,0 +1,32 @@ +// MIR for `::{{constant}}#0` after SimplifyCfg-qualify-consts + +::{{constant}}#0: usize = { + let mut _0: usize; // return place in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + let mut _1: (usize, bool); // in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + + bb0: { + _1 = CheckedAdd(const 1usize, const 1usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $DIR/issue-41697.rs:18:19: 18:20 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $DIR/issue-41697.rs:18:21: 18:22 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + assert(!move (_1.1: bool), "attempt to add with overflow") -> [success: bb1, unwind: bb2]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb1: { + _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } +} diff --git a/src/test/mir-opt/issue-41888.rs b/src/test/mir-opt/issue-41888.rs index efe2b249d4a86..6caaa59d0af34 100644 --- a/src/test/mir-opt/issue-41888.rs +++ b/src/test/mir-opt/issue-41888.rs @@ -1,6 +1,8 @@ +// ignore-wasm32-bare compiled with panic=abort by default // check that we clear the "ADT master drop flag" even when there are // no fields to be dropped. +// EMIT_MIR rustc.main.ElaborateDrops.after.mir fn main() { let e; if cond() { @@ -20,159 +22,3 @@ enum E { F(K), G(Box) } - -// END RUST SOURCE -// fn main() -> () { -// let mut _0: (); -// scope 1 { -// let _1: E; -// debug e => _1; -// scope 2 { -// let _6: K; -// debug _k => _6; -// } -// } -// let mut _2: bool; -// let mut _3: (); -// let mut _4: E; -// let mut _5: K; -// let mut _7: isize; -// let mut _8: bool; // drop flag for `e` -// let mut _9: bool; -// let mut _10: bool; -// let mut _11: isize; -// let mut _12: isize; -// -// bb0: { -// _8 = const false; -// _10 = const false; -// _9 = const false; -// StorageLive(_1); -// StorageLive(_2); -// _2 = const cond() -> [return: bb3, unwind: bb2]; -// } -// -// bb1: { -// resume; -// } -// -// bb2: { -// goto -> bb1; -// } -// -// bb3: { -// switchInt(_2) -> [0u8: bb5, otherwise: bb4]; -// } -// -// bb4: { -// StorageLive(_4); -// StorageLive(_5); -// _5 = K::{{constructor}}; -// _4 = E::F(_5,); -// StorageDead(_5); -// goto -> bb15; -// } -// -// bb5: { -// _0 = (); -// goto -> bb12; -// } -// -// bb6: { -// goto -> bb2; -// } -// -// bb7: { -// goto -> bb8; -// } -// -// bb8: { -// StorageDead(_4); -// _7 = discriminant(_1); -// switchInt(_7) -> [0isize: bb10, otherwise: bb9]; -// } -// -// bb9: { -// _0 = (); -// goto -> bb11; -// } -// -// bb10: { -// StorageLive(_6); -// _10 = const false; -// _6 = ((_1 as F).0: K); -// _0 = (); -// goto -> bb11; -// } -// -// bb11: { -// StorageDead(_6); -// goto -> bb12; -// } -// -// bb12: { -// StorageDead(_2); -// goto -> bb22; -// } -// -// bb13: { -// StorageDead(_1); -// return; -// } -// -// bb14: { -// _8 = const true; -// _9 = const true; -// _10 = const true; -// _1 = _4; -// goto -> bb6; -// } -// -// bb15: { -// _8 = const true; -// _9 = const true; -// _10 = const true; -// _1 = _4; -// goto -> bb7; -// } -// -// bb16: { -// _8 = const false; // clear the drop flag - must always be reached -// goto -> bb13; -// } -// -// bb17: { -// _8 = const false; -// goto -> bb1; -// } -// -// bb18: { -// goto -> bb17; -// } -// -// bb19: { -// drop(_1) -> [return: bb16, unwind: bb17]; -// } -// -// bb20: { -// drop(_1) -> bb17; -// } -// -// bb21: { -// _11 = discriminant(_1); -// switchInt(_11) -> [0isize: bb16, otherwise: bb19]; -// } -// -// bb22: { -// switchInt(_8) -> [0u8: bb16, otherwise: bb21]; -// } -// -// bb23: { -// _12 = discriminant(_1); -// switchInt(_12) -> [0isize: bb18, otherwise: bb20]; -// } -// -// bb24: { -// switchInt(_8) -> [0u8: bb17, otherwise: bb23]; -// } -// } diff --git a/src/test/mir-opt/issue-41888/rustc.main.ElaborateDrops.after.mir b/src/test/mir-opt/issue-41888/rustc.main.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..b64dd5c3776cf --- /dev/null +++ b/src/test/mir-opt/issue-41888/rustc.main.ElaborateDrops.after.mir @@ -0,0 +1,257 @@ +// MIR for `main` after ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41888.rs:6:11: 6:11 + let _1: E; // in scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + let mut _2: bool; // in scope 0 at $DIR/issue-41888.rs:8:8: 8:14 + let mut _3: E; // in scope 0 at $DIR/issue-41888.rs:9:13: 9:20 + let mut _4: K; // in scope 0 at $DIR/issue-41888.rs:9:18: 9:19 + let mut _5: isize; // in scope 0 at $DIR/issue-41888.rs:10:16: 10:24 + let mut _7: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _8: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _9: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _10: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _11: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + scope 1 { + debug e => _1; // in scope 1 at $DIR/issue-41888.rs:7:9: 7:10 + let _6: K; // in scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + scope 2 { + debug _k => _6; // in scope 2 at $DIR/issue-41888.rs:10:21: 10:23 + } + } + + bb0: { + _9 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:7:9: 7:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _7 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:7:9: 7:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _8 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:7:9: 7:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageLive(_1); // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + StorageLive(_2); // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 + _2 = const cond() -> [return: bb1, unwind: bb11]; // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 + // ty::Const + // + ty: fn() -> bool {cond} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:8:8: 8:12 + // + literal: Const { ty: fn() -> bool {cond}, val: Value(Scalar()) } + } + + bb1: { + switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + } + + bb2: { + _0 = const (); // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:8:5: 14:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + } + + bb3: { + StorageLive(_3); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 + StorageLive(_4); // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 + _4 = K; // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 + _3 = E::F(move _4); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 + StorageDead(_4); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + goto -> bb14; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb4: { + goto -> bb5; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + } + + bb5: { + StorageDead(_3); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + _5 = discriminant(_1); // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 + switchInt(move _5) -> [0isize: bb7, otherwise: bb6]; // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 + } + + bb6: { + _0 = const (); // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:10:9: 13:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + } + + bb7: { + StorageLive(_6); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + _9 = const false; // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:10:21: 10:23 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _6 = move ((_1 as F).0: K); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + _0 = const (); // scope 2 at $DIR/issue-41888.rs:10:29: 13:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:10:29: 13:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_6); // scope 1 at $DIR/issue-41888.rs:13:9: 13:10 + goto -> bb8; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + } + + bb8: { + goto -> bb20; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb9: { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _8 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _9 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + StorageDead(_2); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + return; // scope 0 at $DIR/issue-41888.rs:15:2: 15:2 + } + + bb10 (cleanup): { + goto -> bb11; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + } + + bb11 (cleanup): { + goto -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb12 (cleanup): { + resume; // scope 0 at $DIR/issue-41888.rs:6:1: 15:2 + } + + bb13 (cleanup): { + _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + goto -> bb10; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb14: { + _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + goto -> bb4; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb15: { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + goto -> bb9; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb16 (cleanup): { + goto -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb17: { + drop(_1) -> [return: bb15, unwind: bb12]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb18 (cleanup): { + drop(_1) -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb19: { + _10 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + switchInt(move _10) -> [0isize: bb15, otherwise: bb17]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb20: { + switchInt(_7) -> [false: bb15, otherwise: bb19]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb21 (cleanup): { + _11 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + switchInt(move _11) -> [0isize: bb16, otherwise: bb18]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb22 (cleanup): { + switchInt(_7) -> [false: bb12, otherwise: bb21]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } +} diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs index 54c89b85f42d7..7d308980b9071 100644 --- a/src/test/mir-opt/issue-49232.rs +++ b/src/test/mir-opt/issue-49232.rs @@ -1,6 +1,7 @@ // We must mark a variable whose initialization fails due to an // abort statement as StorageDead. +// EMIT_MIR rustc.main.mir_map.0.mir fn main() { loop { let beacon = { @@ -12,82 +13,3 @@ fn main() { drop(&beacon); } } - -// END RUST SOURCE -// START rustc.main.mir_map.0.mir -// fn main() -> (){ -// let mut _0: (); -// let mut _1: (); -// let _2: i32; -// let mut _3: bool; -// let mut _4: !; -// let _5: (); -// let mut _6: &i32; -// scope 1 { -// debug beacon => _2; -// } -// bb0: { -// goto -> bb1; -// } -// bb1: { -// falseUnwind -> [real: bb3, cleanup: bb4]; -// } -// bb2: { -// goto -> bb14; -// } -// bb3: { -// StorageLive(_2); -// StorageLive(_3); -// _3 = const true; -// FakeRead(ForMatchedPlace, _3); -// switchInt(_3) -> [false: bb5, otherwise: bb6]; -// } -// bb4 (cleanup): { -// resume; -// } -// bb5: { -// falseEdges -> [real: bb7, imaginary: bb6]; -// } -// bb6: { -// _0 = (); -// goto -> bb8; -// } -// bb7: { -// _2 = const 4i32; -// goto -> bb12; -// } -// bb8: { -// StorageDead(_3); -// goto -> bb9; -// } -// bb9: { -// StorageDead(_2); -// goto -> bb2; -// } -// bb10: { -// _4 = (); -// unreachable; -// } -// bb11: { -// goto -> bb12; -// } -// bb12: { -// FakeRead(ForLet, _2); -// StorageDead(_3); -// StorageLive(_5); -// StorageLive(_6); -// _6 = &_2; -// _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4]; -// } -// bb13: { -// StorageDead(_6); -// StorageDead(_5); -// _1 = (); -// StorageDead(_2); -// goto -> bb1; -// } -// bb14: { -// return; -// } -// } -// END rustc.main.mir_map.0.mir diff --git a/src/test/mir-opt/issue-49232/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-49232/rustc.main.mir_map.0.mir new file mode 100644 index 0000000000000..75c1ca5af92b0 --- /dev/null +++ b/src/test/mir-opt/issue-49232/rustc.main.mir_map.0.mir @@ -0,0 +1,116 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-49232.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/issue-49232.rs:5:1: 15:2 + let _2: i32; // in scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + let mut _3: bool; // in scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + let mut _4: !; // in scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + let _5: (); // in scope 0 at $DIR/issue-49232.rs:13:9: 13:22 + let mut _6: &i32; // in scope 0 at $DIR/issue-49232.rs:13:14: 13:21 + scope 1 { + debug beacon => _2; // in scope 1 at $DIR/issue-49232.rs:7:13: 7:19 + } + + bb0: { + goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb1: { + falseUnwind -> [real: bb2, cleanup: bb11]; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb2: { + StorageLive(_2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + StorageLive(_3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + _3 = const true; // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-49232.rs:8:19: 8:23 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForMatchedPlace, _3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + switchInt(_3) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 + } + + bb3: { + falseEdges -> [real: bb5, imaginary: bb4]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 + } + + bb4: { + _0 = const (); // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-49232.rs:10:25: 10:30 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb10; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb5: { + _2 = const 4i32; // scope 0 at $DIR/issue-49232.rs:9:26: 9:27 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/issue-49232.rs:9:26: 9:27 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + goto -> bb8; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 + } + + bb6: { + _4 = const (); // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-49232.rs:10:25: 10:30 + // + literal: Const { ty: (), val: Value(Scalar()) } + unreachable; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb7: { + goto -> bb8; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 + } + + bb8: { + FakeRead(ForLet, _2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 + StorageLive(_5); // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 + StorageLive(_6); // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 + _6 = &_2; // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 + _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb9, unwind: bb11]; // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 + // ty::Const + // + ty: fn(&i32) {std::mem::drop::<&i32>} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-49232.rs:13:9: 13:13 + // + literal: Const { ty: fn(&i32) {std::mem::drop::<&i32>}, val: Value(Scalar()) } + } + + bb9: { + StorageDead(_6); // scope 1 at $DIR/issue-49232.rs:13:21: 13:22 + StorageDead(_5); // scope 1 at $DIR/issue-49232.rs:13:22: 13:23 + _1 = const (); // scope 0 at $DIR/issue-49232.rs:6:10: 14:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-49232.rs:6:10: 14:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 + goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb10: { + StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 + StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 + return; // scope 0 at $DIR/issue-49232.rs:15:2: 15:2 + } + + bb11 (cleanup): { + resume; // scope 0 at $DIR/issue-49232.rs:5:1: 15:2 + } +} diff --git a/src/test/mir-opt/issue-62289.rs b/src/test/mir-opt/issue-62289.rs index 8e619ffdf8b96..f0d57c572b343 100644 --- a/src/test/mir-opt/issue-62289.rs +++ b/src/test/mir-opt/issue-62289.rs @@ -1,10 +1,10 @@ // check that we don't forget to drop the Box if we early return before // initializing it -// ignore-tidy-linelength // ignore-wasm32-bare compiled with panic=abort by default #![feature(box_syntax)] +// EMIT_MIR rustc.test.ElaborateDrops.before.mir fn test() -> Option> { Some(box (None?)) } @@ -12,80 +12,3 @@ fn test() -> Option> { fn main() { test(); } - -// END RUST SOURCE -// START rustc.test.ElaborateDrops.before.mir -// fn test() -> std::option::Option> { -// ... -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = Box(u32); -// StorageLive(_3); -// StorageLive(_4); -// _4 = std::option::Option::::None; -// _3 = const as std::ops::Try>::into_result(move _4) -> [return: bb2, unwind: bb3]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// StorageDead(_4); -// _5 = discriminant(_3); -// switchInt(move _5) -> [0isize: bb4, 1isize: bb6, otherwise: bb5]; -// } -// bb3 (cleanup): { -// drop(_2) -> bb1; -// } -// bb4: { -// StorageLive(_10); -// _10 = ((_3 as Ok).0: u32); -// (*_2) = _10; -// StorageDead(_10); -// _1 = move _2; -// drop(_2) -> [return: bb12, unwind: bb11]; -// } -// bb5: { -// unreachable; -// } -// bb6: { -// StorageLive(_6); -// _6 = ((_3 as Err).0: std::option::NoneError); -// StorageLive(_8); -// StorageLive(_9); -// _9 = _6; -// _8 = const >::from(move _9) -> [return: bb8, unwind: bb3]; -// } -// bb7: { -// return; -// } -// bb8: { -// StorageDead(_9); -// _0 = const > as std::ops::Try>::from_error(move _8) -> [return: bb9, unwind: bb3]; -// } -// bb9: { -// StorageDead(_8); -// StorageDead(_6); -// drop(_2) -> bb10; -// } -// bb10: { -// StorageDead(_2); -// StorageDead(_1); -// StorageDead(_3); -// goto -> bb7; -// } -// bb11 (cleanup): { -// drop(_1) -> bb1; -// } -// bb12: { -// StorageDead(_2); -// _0 = std::option::Option::>::Some(move _1,); -// drop(_1) -> bb13; -// } -// bb13: { -// StorageDead(_1); -// StorageDead(_3); -// goto -> bb7; -// } -// } -// END rustc.test.ElaborateDrops.before.mir diff --git a/src/test/mir-opt/issue-62289/rustc.test.ElaborateDrops.before.mir b/src/test/mir-opt/issue-62289/rustc.test.ElaborateDrops.before.mir new file mode 100644 index 0000000000000..f59d157d2971a --- /dev/null +++ b/src/test/mir-opt/issue-62289/rustc.test.ElaborateDrops.before.mir @@ -0,0 +1,127 @@ +// MIR for `test` before ElaborateDrops + +fn test() -> std::option::Option> { + let mut _0: std::option::Option>; // return place in scope 0 at $DIR/issue-62289.rs:8:14: 8:30 + let mut _1: std::boxed::Box; // in scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + let mut _3: std::result::Result; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + let mut _4: std::option::Option; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:19 + let mut _5: isize; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let _6: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let mut _7: !; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let mut _8: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let mut _9: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let _10: u32; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + scope 1 { + debug err => _6; // in scope 1 at $DIR/issue-62289.rs:9:19: 9:20 + scope 2 { + } + } + scope 3 { + debug val => _10; // in scope 3 at $DIR/issue-62289.rs:9:15: 9:20 + scope 4 { + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + StorageLive(_2); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + _2 = Box(u32); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + StorageLive(_3); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + StorageLive(_4); // scope 0 at $DIR/issue-62289.rs:9:15: 9:19 + _4 = std::option::Option::::None; // scope 0 at $DIR/issue-62289.rs:9:15: 9:19 + _3 = const as std::ops::Try>::into_result(move _4) -> [return: bb1, unwind: bb12]; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + // ty::Const + // + ty: fn(std::option::Option) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-62289.rs:9:15: 9:20 + // + literal: Const { ty: fn(std::option::Option) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_4); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _5 = discriminant(_3); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + switchInt(move _5) -> [0isize: bb2, 1isize: bb4, otherwise: bb3]; // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + } + + bb2: { + StorageLive(_10); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + _10 = ((_3 as Ok).0: u32); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + (*_2) = _10; // scope 4 at $DIR/issue-62289.rs:9:15: 9:20 + StorageDead(_10); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _1 = move _2; // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + drop(_2) -> [return: bb7, unwind: bb11]; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb3: { + unreachable; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + } + + bb4: { + StorageLive(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _6 = ((_3 as Err).0: std::option::NoneError); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + StorageLive(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + StorageLive(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _9 = _6; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _8 = const >::from(move _9) -> [return: bb5, unwind: bb12]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + // ty::Const + // + ty: fn(std::option::NoneError) -> std::option::NoneError {>::from} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-62289.rs:9:19: 9:20 + // + literal: Const { ty: fn(std::option::NoneError) -> std::option::NoneError {>::from}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _0 = const > as std::ops::Try>::from_error(move _8) -> [return: bb6, unwind: bb12]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + // ty::Const + // + ty: fn(> as std::ops::Try>::Error) -> std::option::Option> {> as std::ops::Try>::from_error} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-62289.rs:9:15: 9:20 + // + literal: Const { ty: fn(> as std::ops::Try>::Error) -> std::option::Option> {> as std::ops::Try>::from_error}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + StorageDead(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + drop(_2) -> bb9; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb7: { + StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + _0 = std::option::Option::>::Some(move _1); // scope 0 at $DIR/issue-62289.rs:9:5: 9:22 + drop(_1) -> bb8; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + } + + bb8: { + StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 + goto -> bb10; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 + goto -> bb10; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 + } + + bb10: { + return; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 + } + + bb11 (cleanup): { + drop(_1) -> bb13; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + } + + bb12 (cleanup): { + drop(_2) -> bb13; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb13 (cleanup): { + resume; // scope 0 at $DIR/issue-62289.rs:8:1: 10:2 + } +} diff --git a/src/test/mir-opt/loop_test.rs b/src/test/mir-opt/loop_test.rs index 418febbdc01eb..cb23a4c671b22 100644 --- a/src/test/mir-opt/loop_test.rs +++ b/src/test/mir-opt/loop_test.rs @@ -2,6 +2,7 @@ // Tests to make sure we correctly generate falseUnwind edges in loops +// EMIT_MIR rustc.main.SimplifyCfg-qualify-consts.after.mir fn main() { // Exit early at runtime. Since only care about the generated MIR // and not the runtime behavior (which is exercised by other tests) @@ -14,31 +15,3 @@ fn main() { continue; } } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// ... -// bb1 (cleanup): { -// resume; -// } -// ... -// bb3: { // Entry into the loop -// _1 = (); -// StorageDead(_2); -// StorageDead(_1); -// StorageLive(_4); -// goto -> bb5; -// } -// ... -// bb5: { // The loop_block -// falseUnwind -> [real: bb6, cleanup: bb1]; -// } -// bb6: { // The loop body (body_block) -// StorageLive(_6); -// _6 = const 1i32; -// FakeRead(ForLet, _6); -// StorageDead(_6); -// goto -> bb5; -// } -// ... -// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/loop_test/rustc.main.SimplifyCfg-qualify-consts.after.mir b/src/test/mir-opt/loop_test/rustc.main.SimplifyCfg-qualify-consts.after.mir new file mode 100644 index 0000000000000..33c59bd3f1478 --- /dev/null +++ b/src/test/mir-opt/loop_test/rustc.main.SimplifyCfg-qualify-consts.after.mir @@ -0,0 +1,81 @@ +// MIR for `main` after SimplifyCfg-qualify-consts + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/loop_test.rs:6:11: 6:11 + let _1: (); // in scope 0 at $DIR/loop_test.rs:10:5: 12:6 + let mut _2: bool; // in scope 0 at $DIR/loop_test.rs:10:8: 10:12 + let mut _3: !; // in scope 0 at $DIR/loop_test.rs:10:13: 12:6 + let mut _4: !; // in scope 0 at $DIR/loop_test.rs:13:5: 16:6 + let mut _5: (); // in scope 0 at $DIR/loop_test.rs:6:1: 17:2 + let _6: i32; // in scope 0 at $DIR/loop_test.rs:14:13: 14:14 + scope 1 { + debug x => _6; // in scope 1 at $DIR/loop_test.rs:14:13: 14:14 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + StorageLive(_2); // scope 0 at $DIR/loop_test.rs:10:8: 10:12 + _2 = const true; // scope 0 at $DIR/loop_test.rs:10:8: 10:12 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/loop_test.rs:10:8: 10:12 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/loop_test.rs:10:8: 10:12 + switchInt(_2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + } + + bb1: { + falseEdges -> [real: bb3, imaginary: bb2]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + } + + bb2: { + _1 = const (); // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/loop_test.rs:10:5: 12:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageLive(_4); // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + goto -> bb4; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + } + + bb3: { + _0 = const (); // scope 0 at $DIR/loop_test.rs:11:9: 11:15 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/loop_test.rs:11:9: 11:15 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + return; // scope 0 at $DIR/loop_test.rs:17:2: 17:2 + } + + bb4: { + falseUnwind -> [real: bb5, cleanup: bb6]; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + } + + bb5: { + StorageLive(_6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14 + _6 = const 1i32; // scope 0 at $DIR/loop_test.rs:14:17: 14:18 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/loop_test.rs:14:17: 14:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + FakeRead(ForLet, _6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14 + StorageDead(_6); // scope 0 at $DIR/loop_test.rs:16:5: 16:6 + goto -> bb4; // scope 0 at $DIR/loop_test.rs:1:1: 1:1 + } + + bb6 (cleanup): { + resume; // scope 0 at $DIR/loop_test.rs:6:1: 17:2 + } +} diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs index 7afc3bbd6fae8..0e30a15671528 100644 --- a/src/test/mir-opt/match-arm-scopes.rs +++ b/src/test/mir-opt/match-arm-scopes.rs @@ -1,3 +1,4 @@ +// ignore-wasm32-bare compiled with panic=abort by default // Test that StorageDead and Drops are generated properly for bindings in // matches: // * The MIR should only contain a single drop of `s` and `t`: at the end @@ -8,6 +9,8 @@ // all of the bindings for that scope. // * No drop flags are used. +// EMIT_MIR rustc.complicated_match.SimplifyCfg-initial.after.mir +// EMIT_MIR rustc.complicated_match.ElaborateDrops.after.mir fn complicated_match(cond: bool, items: (bool, bool, String)) -> i32 { match items { (false, a, s) | (a, false, s) if if cond { return 3 } else { a } => 1, @@ -31,199 +34,3 @@ fn main() { assert_eq!(complicated_match(cond, (items_1, items_2, String::new())), result,); } } - -// END RUST SOURCE -// START rustc.complicated_match.SimplifyCfg-initial.after.mir -// let mut _0: i32; -// let mut _3: &bool; // Temp for fake borrow of `items.0` -// let mut _4: &bool; // Temp for fake borrow of `items.1` -// let _5: bool; // `a` in arm -// let _6: &bool; // `a` in guard -// let _7: std::string::String; // `s` in arm -// let _8: &std::string::String; // `s` in guard -// let mut _9: bool; // `if cond { return 3 } else { a }` -// let mut _10: bool; // `cond` -// let mut _11: !; // `return 3` -// let mut _12: bool; // `if cond { return 3 } else { a }` -// let mut _13: bool; // `cond` -// let mut _14: !; // `return 3` -// let _15: bool; // `b` -// let _16: std::string::String; // `t` -// scope 1 { -// debug a => _5; -// debug a => _6; -// debug s => _7; -// debug s => _8; -// } -// scope 2 { -// debug b => _15; -// debug t => _16; -// } -// bb0: { -// FakeRead(ForMatchedPlace, _2); -// switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb3]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { // pre-binding for arm 1 first pattern -// falseEdges -> [real: bb9, imaginary: bb4]; -// } -// bb3: { -// switchInt((_2.1: bool)) -> [false: bb4, otherwise: bb5]; -// } -// bb4: { // pre-binding for arm 1 second pattern -// falseEdges -> [real: bb18, imaginary: bb6]; -// } -// bb5: { -// switchInt((_2.0: bool)) -> [false: bb7, otherwise: bb6]; -// } -// bb6: { // pre-binding for arm 2 first pattern -// falseEdges -> [real: bb26, imaginary: bb7]; -// } -// bb7: { // bindings for arm 2 - second pattern -// StorageLive(_15); -// _15 = (_2.1: bool); -// StorageLive(_16); -// _16 = move (_2.2: std::string::String); -// goto -> bb25; -// } -// bb8: { // arm 1 -// _0 = const 1i32; -// drop(_7) -> [return: bb24, unwind: bb14]; -// } -// bb9: { // guard - first time -// StorageLive(_6); -// _6 = &(_2.1: bool); -// StorageLive(_8); -// _8 = &(_2.2: std::string::String); -// _3 = &shallow (_2.0: bool); -// _4 = &shallow (_2.1: bool); -// StorageLive(_9); -// StorageLive(_10); -// _10 = _1; -// FakeRead(ForMatchedPlace, _10); -// switchInt(_10) -> [false: bb11, otherwise: bb10]; -// } -// bb10: { -// falseEdges -> [real: bb12, imaginary: bb11]; -// } -// bb11: { // `else` block - first time -// _9 = (*_6); -// StorageDead(_10); -// switchInt(move _9) -> [false: bb17, otherwise: bb16]; -// } -// bb12: { // `return 3` - first time -// _0 = const 3i32; -// StorageDead(_10); -// StorageDead(_9); -// StorageDead(_8); -// StorageDead(_6); -// goto -> bb15; -// } -// bb13: { -// return; -// } -// bb14 (cleanup): { -// drop(_2) -> bb1; -// } -// bb15: { -// drop(_2) -> [return: bb13, unwind: bb1]; -// } -// bb16: { -// StorageDead(_9); -// FakeRead(ForMatchGuard, _3); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForGuardBinding, _6); -// FakeRead(ForGuardBinding, _8); -// StorageLive(_5); -// _5 = (_2.1: bool); -// StorageLive(_7); -// _7 = move (_2.2: std::string::String); -// goto -> bb8; -// } -// bb17: { // guard otherwise case - first time -// StorageDead(_9); -// StorageDead(_8); -// StorageDead(_6); -// falseEdges -> [real: bb3, imaginary: bb4]; -// } -// bb18: { // guard - second time -// StorageLive(_6); -// _6 = &(_2.0: bool); -// StorageLive(_8); -// _8 = &(_2.2: std::string::String); -// _3 = &shallow (_2.0: bool); -// _4 = &shallow (_2.1: bool); -// StorageLive(_12); -// StorageLive(_13); -// _13 = _1; -// FakeRead(ForMatchedPlace, _13); -// switchInt(_13) -> [false: bb20, otherwise: bb19]; -// } -// bb19: { -// falseEdges -> [real: bb21, imaginary: bb20]; -// } -// bb20: { // `else` block - second time -// _12 = (*_6); -// StorageDead(_13); -// switchInt(move _12) -> [false: bb23, otherwise: bb22]; -// } -// bb21: { -// _0 = const 3i32; -// StorageDead(_13); -// StorageDead(_12); -// StorageDead(_8); -// StorageDead(_6); -// goto -> bb15; -// } -// bb22: { // bindings for arm 1 -// StorageDead(_12); -// FakeRead(ForMatchGuard, _3); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForGuardBinding, _6); -// FakeRead(ForGuardBinding, _8); -// StorageLive(_5); -// _5 = (_2.0: bool); -// StorageLive(_7); -// _7 = move (_2.2: std::string::String); -// goto -> bb8; -// } -// bb23: { // Guard otherwise case - second time -// StorageDead(_12); -// StorageDead(_8); -// StorageDead(_6); -// falseEdges -> [real: bb5, imaginary: bb6]; -// } -// bb24: { // rest of arm 1 -// StorageDead(_7); -// StorageDead(_5); -// StorageDead(_8); -// StorageDead(_6); -// goto -> bb28; -// } -// bb25: { // arm 2 -// _0 = const 2i32; -// drop(_16) -> [return: bb27, unwind: bb14]; -// } -// bb26: { // bindings for arm 2 - first pattern -// StorageLive(_15); -// _15 = (_2.1: bool); -// StorageLive(_16); -// _16 = move (_2.2: std::string::String); -// goto -> bb25; -// } - -// bb27: { // rest of arm 2 -// StorageDead(_16); -// StorageDead(_15); -// goto -> bb28; -// } -// bb28: { -// drop(_2) -> [return: bb13, unwind: bb1]; -// } -// END rustc.complicated_match.SimplifyCfg-initial.after.mir -// START rustc.complicated_match.ElaborateDrops.after.mir -// let _16: std::string::String; // No drop flags, which would come after this. -// scope 1 { -// END rustc.complicated_match.ElaborateDrops.after.mir diff --git a/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.ElaborateDrops.after.mir b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..bca19398ce7af --- /dev/null +++ b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.ElaborateDrops.after.mir @@ -0,0 +1,225 @@ +// MIR for `complicated_match` after ElaborateDrops + +fn complicated_match(_1: bool, _2: (bool, bool, std::string::String)) -> i32 { + debug cond => _1; // in scope 0 at $DIR/match-arm-scopes.rs:14:22: 14:26 + debug items => _2; // in scope 0 at $DIR/match-arm-scopes.rs:14:34: 14:39 + let mut _0: i32; // return place in scope 0 at $DIR/match-arm-scopes.rs:14:66: 14:69 + let mut _3: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let mut _4: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let _5: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _6: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _7: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let _8: &std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let mut _9: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _10: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _11: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let mut _12: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _13: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _14: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let _15: bool; // in scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + let _16: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + scope 1 { + debug a => _5; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug a => _6; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug s => _7; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + debug s => _8; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + } + scope 2 { + debug b => _15; // in scope 2 at $DIR/match-arm-scopes.rs:17:16: 17:17 + debug t => _16; // in scope 2 at $DIR/match-arm-scopes.rs:17:19: 17:20 + } + + bb0: { + switchInt((_2.0: bool)) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:15 + } + + bb1: { + switchInt((_2.1: bool)) -> [false: bb10, otherwise: bb2]; // scope 0 at $DIR/match-arm-scopes.rs:16:29: 16:34 + } + + bb2: { + switchInt((_2.0: bool)) -> [false: bb3, otherwise: bb17]; // scope 0 at $DIR/match-arm-scopes.rs:17:10: 17:14 + } + + bb3: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + goto -> bb16; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb4: { + _0 = const 1i32; // scope 1 at $DIR/match-arm-scopes.rs:16:77: 16:78 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:16:77: 16:78 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + drop(_7) -> [return: bb15, unwind: bb22]; // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + } + + bb5: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + _6 = &(_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + StorageLive(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _10 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_10) -> [false: bb6, otherwise: bb7]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb6: { + _9 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _9) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb7: { + _0 = const 3i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:1:1: 1:1 + } + + bb8: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + _5 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + goto -> bb4; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb9: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb10: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + _6 = &(_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + StorageLive(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _13 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_13) -> [false: bb11, otherwise: bb12]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb11: { + _12 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _12) -> [false: bb14, otherwise: bb13]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb12: { + _0 = const 3i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:1:1: 1:1 + } + + bb13: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + _5 = (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + goto -> bb4; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb14: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb2; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb15: { + StorageDead(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb19; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb16: { + _0 = const 2i32; // scope 2 at $DIR/match-arm-scopes.rs:17:41: 17:42 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:17:41: 17:42 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + drop(_16) -> [return: bb18, unwind: bb22]; // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + } + + bb17: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + goto -> bb16; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb18: { + StorageDead(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + StorageDead(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + goto -> bb19; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb19: { + goto -> bb26; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb20: { + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + drop(_2) -> [return: bb21, unwind: bb23]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb21: { + return; // scope 0 at $DIR/match-arm-scopes.rs:19:2: 19:2 + } + + bb22 (cleanup): { + goto -> bb27; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb23 (cleanup): { + resume; // scope 0 at $DIR/match-arm-scopes.rs:14:1: 19:2 + } + + bb24: { + goto -> bb21; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb25 (cleanup): { + goto -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb26: { + goto -> bb24; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb27 (cleanup): { + goto -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } +} diff --git a/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..952aa0e804af9 --- /dev/null +++ b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir @@ -0,0 +1,244 @@ +// MIR for `complicated_match` after SimplifyCfg-initial + +fn complicated_match(_1: bool, _2: (bool, bool, std::string::String)) -> i32 { + debug cond => _1; // in scope 0 at $DIR/match-arm-scopes.rs:14:22: 14:26 + debug items => _2; // in scope 0 at $DIR/match-arm-scopes.rs:14:34: 14:39 + let mut _0: i32; // return place in scope 0 at $DIR/match-arm-scopes.rs:14:66: 14:69 + let mut _3: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let mut _4: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let _5: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _6: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _7: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let _8: &std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let mut _9: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _10: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _11: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let mut _12: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _13: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _14: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let _15: bool; // in scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + let _16: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + scope 1 { + debug a => _5; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug a => _6; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug s => _7; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + debug s => _8; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + } + scope 2 { + debug b => _15; // in scope 2 at $DIR/match-arm-scopes.rs:17:16: 17:17 + debug t => _16; // in scope 2 at $DIR/match-arm-scopes.rs:17:19: 17:20 + } + + bb0: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + switchInt((_2.0: bool)) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:15 + } + + bb1: { + falseEdges -> [real: bb8, imaginary: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:16:9: 16:22 + } + + bb2: { + switchInt((_2.1: bool)) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/match-arm-scopes.rs:16:29: 16:34 + } + + bb3: { + falseEdges -> [real: bb14, imaginary: bb5]; // scope 0 at $DIR/match-arm-scopes.rs:16:25: 16:38 + } + + bb4: { + switchInt((_2.0: bool)) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/match-arm-scopes.rs:17:10: 17:14 + } + + bb5: { + falseEdges -> [real: bb22, imaginary: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:17:9: 17:21 + } + + bb6: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + goto -> bb21; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb7: { + _0 = const 1i32; // scope 1 at $DIR/match-arm-scopes.rs:16:77: 16:78 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:16:77: 16:78 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + drop(_7) -> [return: bb20, unwind: bb27]; // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + } + + bb8: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + _6 = &(_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + StorageLive(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _10 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + FakeRead(ForMatchedPlace, _10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_10) -> [false: bb10, otherwise: bb9]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb9: { + falseEdges -> [real: bb11, imaginary: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb10: { + _9 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _9) -> [false: bb13, otherwise: bb12]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb11: { + _0 = const 3i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:1:1: 1:1 + } + + bb12: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + FakeRead(ForMatchGuard, _3); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + FakeRead(ForGuardBinding, _8); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + _5 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + goto -> bb7; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb13: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + falseEdges -> [real: bb2, imaginary: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb14: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + _6 = &(_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + StorageLive(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _13 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + FakeRead(ForMatchedPlace, _13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_13) -> [false: bb16, otherwise: bb15]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb15: { + falseEdges -> [real: bb17, imaginary: bb16]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb16: { + _12 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _12) -> [false: bb19, otherwise: bb18]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb17: { + _0 = const 3i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:1:1: 1:1 + } + + bb18: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + FakeRead(ForMatchGuard, _3); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + FakeRead(ForGuardBinding, _8); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + _5 = (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + goto -> bb7; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb19: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + falseEdges -> [real: bb4, imaginary: bb5]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb20: { + StorageDead(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + goto -> bb24; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb21: { + _0 = const 2i32; // scope 2 at $DIR/match-arm-scopes.rs:17:41: 17:42 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/match-arm-scopes.rs:17:41: 17:42 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + drop(_16) -> [return: bb23, unwind: bb27]; // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + } + + bb22: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + goto -> bb21; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb23: { + StorageDead(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + StorageDead(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + goto -> bb24; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb24: { + drop(_2) -> [return: bb26, unwind: bb28]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb25: { + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + drop(_2) -> [return: bb26, unwind: bb28]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb26: { + return; // scope 0 at $DIR/match-arm-scopes.rs:19:2: 19:2 + } + + bb27 (cleanup): { + drop(_2) -> bb28; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb28 (cleanup): { + resume; // scope 0 at $DIR/match-arm-scopes.rs:14:1: 19:2 + } +} diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 237828d9020db..91f4aad165edd 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -10,6 +10,7 @@ fn guard2(_: i32) -> bool { // no_mangle to make sure this gets instantiated even in an executable. #[no_mangle] +// EMIT_MIR rustc.full_tested_match.PromoteTemps.after.mir pub fn full_tested_match() { let _ = match Some(42) { Some(x) if guard() => (1, x), @@ -20,6 +21,7 @@ pub fn full_tested_match() { // no_mangle to make sure this gets instantiated even in an executable. #[no_mangle] +// EMIT_MIR rustc.full_tested_match2.PromoteTemps.before.mir pub fn full_tested_match2() { let _ = match Some(42) { Some(x) if guard() => (1, x), @@ -28,6 +30,7 @@ pub fn full_tested_match2() { }; } +// EMIT_MIR rustc.main.PromoteTemps.before.mir fn main() { let _ = match Some(1) { Some(_w) if guard() => 1, @@ -36,245 +39,3 @@ fn main() { _z => 4, }; } - -// END RUST SOURCE -// -// START rustc.full_tested_match.PromoteTemps.after.mir -// bb0: { -// ... -// _2 = std::option::Option::::Some(const 42i32,); -// FakeRead(ForMatchedPlace, _2); -// _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb2, 1isize: bb3, otherwise: bb5]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { // pre_binding3 and arm3 -// _1 = (const 3i32, const 3i32); -// goto -> bb11; -// } -// bb3: { -// falseEdges -> [real: bb6, imaginary: bb4]; //pre_binding1 -// } -// bb4: { -// falseEdges -> [real: bb10, imaginary: bb2]; //pre_binding2 -// } -// bb5: { -// unreachable; -// } -// bb6: { // binding1 and guard -// StorageLive(_6); -// _11 = const full_tested_match::promoted[0]; -// _6 = &(((*_11) as Some).0: i32); -// _4 = &shallow _2; -// StorageLive(_7); -// _7 = const guard() -> [return: bb7, unwind: bb1]; -// } -// bb7: { // end of guard -// switchInt(move _7) -> [false: bb9, otherwise: bb8]; -// } -// bb8: { // arm1 -// StorageDead(_7); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForGuardBinding, _6); -// StorageLive(_5); -// _5 = ((_2 as Some).0: i32); -// StorageLive(_8); -// _8 = _5; -// _1 = (const 1i32, move _8); -// StorageDead(_8); -// StorageDead(_5); -// StorageDead(_6); -// goto -> bb11; -// } -// bb9: { // to pre_binding2 -// StorageDead(_7); -// StorageDead(_6); -// goto -> bb4; -// } -// bb10: { // arm2 -// StorageLive(_9); -// _9 = ((_2 as Some).0: i32); -// StorageLive(_10); -// _10 = _9; -// _1 = (const 2i32, move _10); -// StorageDead(_10); -// StorageDead(_9); -// goto -> bb11; -// } -// bb11: { -// StorageDead(_2); -// StorageDead(_1); -// _0 = (); -// return; -// } -// END rustc.full_tested_match.PromoteTemps.after.mir -// -// START rustc.full_tested_match2.PromoteTemps.before.mir -// bb0: { -// ... -// _2 = std::option::Option::::Some(const 42i32,); -// FakeRead(ForMatchedPlace, _2); -// _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb2, 1isize: bb3, otherwise: bb5]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { // pre_binding2 -// falseEdges -> [real: bb10, imaginary: bb4]; -// } -// bb3: { // pre_binding1 -// falseEdges -> [real: bb6, imaginary: bb2]; -// } -// bb4: { // binding3 and arm3 -// StorageLive(_9); -// _9 = ((_2 as Some).0: i32); -// StorageLive(_10); -// _10 = _9; -// _1 = (const 2i32, move _10); -// StorageDead(_10); -// StorageDead(_9); -// goto -> bb11; -// } -// bb5: { -// unreachable; -// } -// bb6: { -// StorageLive(_6); -// _6 = &((_2 as Some).0: i32); -// _4 = &shallow _2; -// StorageLive(_7); -// _7 = const guard() -> [return: bb7, unwind: bb1]; -// } -// bb7: { // end of guard -// switchInt(move _7) -> [false: bb9, otherwise: bb8]; -// } -// bb8: { -// StorageDead(_7); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForGuardBinding, _6); -// StorageLive(_5); -// _5 = ((_2 as Some).0: i32); -// StorageLive(_8); -// _8 = _5; -// _1 = (const 1i32, move _8); -// StorageDead(_8); -// StorageDead(_5); -// StorageDead(_6); -// goto -> bb11; -// } -// bb9: { // to pre_binding3 (can skip 2 since this is `Some`) -// StorageDead(_7); -// StorageDead(_6); -// falseEdges -> [real: bb4, imaginary: bb2]; -// } -// bb10: { // arm2 -// _1 = (const 3i32, const 3i32); -// goto -> bb11; -// } -// bb11: { -// StorageDead(_2); -// StorageDead(_1); -// _0 = (); -// return; -// } -// END rustc.full_tested_match2.PromoteTemps.before.mir -// -// START rustc.main.PromoteTemps.before.mir -// bb0: { -// ... -// _2 = std::option::Option::::Some(const 1i32,); -// FakeRead(ForMatchedPlace, _2); -// _4 = discriminant(_2); -// switchInt(move _4) -> [1isize: bb3, otherwise: bb2]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// falseEdges -> [real: bb10, imaginary: bb5]; -// } -// bb3: { -// falseEdges -> [real: bb6, imaginary: bb2]; -// } -// bb4: { -// StorageLive(_14); -// _14 = _2; -// _1 = const 4i32; -// StorageDead(_14); -// goto -> bb15; -// } -// bb5: { -// falseEdges -> [real: bb11, imaginary: bb4]; -// } -// bb6: { //end of guard1 -// StorageLive(_7); -// _7 = &((_2 as Some).0: i32); -// _5 = &shallow _2; -// StorageLive(_8); -// _8 = const guard() -> [return: bb7, unwind: bb1]; -// } -// bb7: { -// switchInt(move _8) -> [false: bb9, otherwise: bb8]; -// } -// bb8: { -// StorageDead(_8); -// FakeRead(ForMatchGuard, _5); -// FakeRead(ForGuardBinding, _7); -// StorageLive(_6); -// _6 = ((_2 as Some).0: i32); -// _1 = const 1i32; -// StorageDead(_6); -// StorageDead(_7); -// goto -> bb15; -// } -// bb9: { -// StorageDead(_8); -// StorageDead(_7); -// falseEdges -> [real: bb2, imaginary: bb2]; -// } -// bb10: { // binding2 & arm2 -// StorageLive(_9); -// _9 = _2; -// _1 = const 2i32; -// StorageDead(_9); -// goto -> bb15; -// } -// bb11: { // binding3: Some(y) if guard2(y) -// StorageLive(_11); -// _11 = &((_2 as Some).0: i32); -// _5 = &shallow _2; -// StorageLive(_12); -// StorageLive(_13); -// _13 = (*_11); -// _12 = const guard2(move _13) -> [return: bb12, unwind: bb1]; -// } -// bb12: { // end of guard2 -// StorageDead(_13); -// switchInt(move _12) -> [false: bb14, otherwise: bb13]; -// } -// bb13: { // binding4 & arm4 -// StorageDead(_12); -// FakeRead(ForMatchGuard, _5); -// FakeRead(ForGuardBinding, _11); -// StorageLive(_10); -// _10 = ((_2 as Some).0: i32); -// _1 = const 3i32; -// StorageDead(_10); -// StorageDead(_11); -// goto -> bb15; -// } -// bb14: { -// StorageDead(_12); -// StorageDead(_11); -// falseEdges -> [real: bb4, imaginary: bb4]; -// } -// bb15: { -// StorageDead(_2); -// StorageDead(_1); -// _0 = (); -// return; -// } -// END rustc.main.PromoteTemps.before.mir diff --git a/src/test/mir-opt/match_false_edges/rustc.full_tested_match.PromoteTemps.after.mir b/src/test/mir-opt/match_false_edges/rustc.full_tested_match.PromoteTemps.after.mir new file mode 100644 index 0000000000000..ddb0e1b75face --- /dev/null +++ b/src/test/mir-opt/match_false_edges/rustc.full_tested_match.PromoteTemps.after.mir @@ -0,0 +1,155 @@ +// MIR for `full_tested_match` after PromoteTemps + +fn full_tested_match() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:14:28: 14:28 + let mut _1: (i32, i32); // in scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + let mut _4: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + let _5: i32; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + let _6: &i32; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + let mut _7: bool; // in scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + let mut _8: i32; // in scope 0 at $DIR/match_false_edges.rs:16:35: 16:36 + let _9: i32; // in scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + let mut _10: i32; // in scope 0 at $DIR/match_false_edges.rs:17:24: 17:25 + let mut _11: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + scope 1 { + } + scope 2 { + debug x => _5; // in scope 2 at $DIR/match_false_edges.rs:16:14: 16:15 + debug x => _6; // in scope 2 at $DIR/match_false_edges.rs:16:14: 16:15 + } + scope 3 { + debug y => _9; // in scope 3 at $DIR/match_false_edges.rs:17:14: 17:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + _2 = std::option::Option::::Some(const 42i32); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:15:24: 15:26 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + _3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + switchInt(move _3) -> [0isize: bb1, 1isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + } + + bb1: { + _1 = (const 3i32, const 3i32); // scope 0 at $DIR/match_false_edges.rs:18:17: 18:23 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:18:18: 18:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:18:21: 18:22 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + goto -> bb10; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb2: { + falseEdges -> [real: bb5, imaginary: bb3]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + } + + bb3: { + falseEdges -> [real: bb9, imaginary: bb1]; // scope 0 at $DIR/match_false_edges.rs:17:9: 17:16 + } + + bb4: { + unreachable; // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + } + + bb5: { + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + _11 = const full_tested_match::promoted[0]; // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + // ty::Const + // + ty: &std::option::Option + // + val: Unevaluated(DefId(0:5 ~ match_false_edges[317d]::full_tested_match[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/match_false_edges.rs:16:14: 16:15 + // + literal: Const { ty: &std::option::Option, val: Unevaluated(DefId(0:5 ~ match_false_edges[317d]::full_tested_match[0]), [], Some(promoted[0])) } + _6 = &(((*_11) as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + _4 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + _7 = const guard() -> [return: bb6, unwind: bb11]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + // ty::Const + // + ty: fn() -> bool {guard} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:16:20: 16:25 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb6: { + switchInt(move _7) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + } + + bb7: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16:37: 16:38 + FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match_false_edges.rs:16:26: 16:27 + FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match_false_edges.rs:16:26: 16:27 + StorageLive(_5); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + _5 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + StorageLive(_8); // scope 2 at $DIR/match_false_edges.rs:16:35: 16:36 + _8 = _5; // scope 2 at $DIR/match_false_edges.rs:16:35: 16:36 + _1 = (const 1i32, move _8); // scope 2 at $DIR/match_false_edges.rs:16:31: 16:37 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:16:32: 16:33 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + StorageDead(_8); // scope 2 at $DIR/match_false_edges.rs:16:36: 16:37 + StorageDead(_5); // scope 0 at $DIR/match_false_edges.rs:16:37: 16:38 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:16:37: 16:38 + goto -> bb10; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb8: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16:37: 16:38 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:16:37: 16:38 + goto -> bb3; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + } + + bb9: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 + _10 = _9; // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 + _1 = (const 2i32, move _10); // scope 3 at $DIR/match_false_edges.rs:17:20: 17:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:17:21: 17:22 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + StorageDead(_10); // scope 3 at $DIR/match_false_edges.rs:17:25: 17:26 + StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:17:26: 17:27 + goto -> bb10; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb10: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:14:28: 20:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:14:28: 20:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/match_false_edges.rs:20:2: 20:2 + } + + bb11 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:14:1: 20:2 + } +} diff --git a/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir new file mode 100644 index 0000000000000..41687006ca3b7 --- /dev/null +++ b/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir @@ -0,0 +1,147 @@ +// MIR for `full_tested_match2` before PromoteTemps + +fn full_tested_match2() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:25:29: 25:29 + let mut _1: (i32, i32); // in scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + let mut _4: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + let _5: i32; // in scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + let _6: &i32; // in scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + let mut _7: bool; // in scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + let mut _8: i32; // in scope 0 at $DIR/match_false_edges.rs:27:35: 27:36 + let _9: i32; // in scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + let mut _10: i32; // in scope 0 at $DIR/match_false_edges.rs:29:24: 29:25 + scope 1 { + } + scope 2 { + debug x => _5; // in scope 2 at $DIR/match_false_edges.rs:27:14: 27:15 + debug x => _6; // in scope 2 at $DIR/match_false_edges.rs:27:14: 27:15 + } + scope 3 { + debug y => _9; // in scope 3 at $DIR/match_false_edges.rs:29:14: 29:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + _2 = std::option::Option::::Some(const 42i32); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:26:24: 26:26 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + _3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + switchInt(move _3) -> [0isize: bb1, 1isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + } + + bb1: { + falseEdges -> [real: bb9, imaginary: bb3]; // scope 0 at $DIR/match_false_edges.rs:28:9: 28:13 + } + + bb2: { + falseEdges -> [real: bb5, imaginary: bb1]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + } + + bb3: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 + _10 = _9; // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 + _1 = (const 2i32, move _10); // scope 3 at $DIR/match_false_edges.rs:29:20: 29:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:29:21: 29:22 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + StorageDead(_10); // scope 3 at $DIR/match_false_edges.rs:29:25: 29:26 + StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:29:26: 29:27 + goto -> bb10; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb4: { + unreachable; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + } + + bb5: { + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _6 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _4 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + _7 = const guard() -> [return: bb6, unwind: bb11]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + // ty::Const + // + ty: fn() -> bool {guard} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:27:20: 27:25 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb6: { + switchInt(move _7) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + } + + bb7: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27:37: 27:38 + FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match_false_edges.rs:27:26: 27:27 + FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match_false_edges.rs:27:26: 27:27 + StorageLive(_5); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _5 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + StorageLive(_8); // scope 2 at $DIR/match_false_edges.rs:27:35: 27:36 + _8 = _5; // scope 2 at $DIR/match_false_edges.rs:27:35: 27:36 + _1 = (const 1i32, move _8); // scope 2 at $DIR/match_false_edges.rs:27:31: 27:37 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:27:32: 27:33 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + StorageDead(_8); // scope 2 at $DIR/match_false_edges.rs:27:36: 27:37 + StorageDead(_5); // scope 0 at $DIR/match_false_edges.rs:27:37: 27:38 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:27:37: 27:38 + goto -> bb10; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb8: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27:37: 27:38 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:27:37: 27:38 + falseEdges -> [real: bb3, imaginary: bb1]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + } + + bb9: { + _1 = (const 3i32, const 3i32); // scope 0 at $DIR/match_false_edges.rs:28:17: 28:23 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:28:18: 28:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:28:21: 28:22 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + goto -> bb10; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb10: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:25:29: 31:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:25:29: 31:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/match_false_edges.rs:31:2: 31:2 + } + + bb11 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:25:1: 31:2 + } +} diff --git a/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir new file mode 100644 index 0000000000000..208e8e698ab74 --- /dev/null +++ b/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir @@ -0,0 +1,194 @@ +// MIR for `main` before PromoteTemps + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:34:11: 34:11 + let mut _1: i32; // in scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:38:9: 38:16 + let mut _4: isize; // in scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + let mut _5: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + let _6: i32; // in scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + let _7: &i32; // in scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + let mut _8: bool; // in scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + let _9: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + let _10: i32; // in scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + let _11: &i32; // in scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + let mut _12: bool; // in scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + let mut _13: i32; // in scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + let _14: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + scope 1 { + } + scope 2 { + debug _w => _6; // in scope 2 at $DIR/match_false_edges.rs:36:14: 36:16 + debug _w => _7; // in scope 2 at $DIR/match_false_edges.rs:36:14: 36:16 + } + scope 3 { + debug _x => _9; // in scope 3 at $DIR/match_false_edges.rs:37:9: 37:11 + } + scope 4 { + debug y => _10; // in scope 4 at $DIR/match_false_edges.rs:38:14: 38:15 + debug y => _11; // in scope 4 at $DIR/match_false_edges.rs:38:14: 38:15 + } + scope 5 { + debug _z => _14; // in scope 5 at $DIR/match_false_edges.rs:39:9: 39:11 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + _2 = std::option::Option::::Some(const 1i32); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:35:24: 35:25 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + _4 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + } + + bb1: { + falseEdges -> [real: bb9, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + } + + bb2: { + falseEdges -> [real: bb5, imaginary: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + } + + bb3: { + StorageLive(_14); // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + _14 = _2; // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + _1 = const 4i32; // scope 5 at $DIR/match_false_edges.rs:39:15: 39:16 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000004)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:39:15: 39:16 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } + StorageDead(_14); // scope 0 at $DIR/match_false_edges.rs:39:16: 39:17 + goto -> bb14; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb4: { + falseEdges -> [real: bb10, imaginary: bb3]; // scope 0 at $DIR/match_false_edges.rs:38:9: 38:16 + } + + bb5: { + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _7 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + StorageLive(_8); // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + _8 = const guard() -> [return: bb6, unwind: bb15]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + // ty::Const + // + ty: fn() -> bool {guard} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:36:21: 36:26 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb6: { + switchInt(move _8) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + } + + bb7: { + StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36:33: 36:34 + FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/match_false_edges.rs:36:27: 36:28 + FakeRead(ForGuardBinding, _7); // scope 0 at $DIR/match_false_edges.rs:36:27: 36:28 + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _6 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _1 = const 1i32; // scope 2 at $DIR/match_false_edges.rs:36:32: 36:33 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:36:32: 36:33 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:36:33: 36:34 + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:36:33: 36:34 + goto -> bb14; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb8: { + StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36:33: 36:34 + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:36:33: 36:34 + falseEdges -> [real: bb1, imaginary: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + } + + bb9: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + _9 = _2; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + _1 = const 2i32; // scope 3 at $DIR/match_false_edges.rs:37:15: 37:16 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:37:15: 37:16 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:37:16: 37:17 + goto -> bb14; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb10: { + StorageLive(_11); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _11 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + StorageLive(_12); // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + StorageLive(_13); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + _13 = (*_11); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + _12 = const guard2(move _13) -> [return: bb11, unwind: bb15]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + // ty::Const + // + ty: fn(i32) -> bool {guard2} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:38:20: 38:26 + // + literal: Const { ty: fn(i32) -> bool {guard2}, val: Value(Scalar()) } + } + + bb11: { + StorageDead(_13); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 + switchInt(move _12) -> [false: bb13, otherwise: bb12]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + } + + bb12: { + StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38:34: 38:35 + FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 + FakeRead(ForGuardBinding, _11); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 + StorageLive(_10); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _10 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _1 = const 3i32; // scope 4 at $DIR/match_false_edges.rs:38:33: 38:34 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match_false_edges.rs:38:33: 38:34 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + StorageDead(_10); // scope 0 at $DIR/match_false_edges.rs:38:34: 38:35 + StorageDead(_11); // scope 0 at $DIR/match_false_edges.rs:38:34: 38:35 + goto -> bb14; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb13: { + StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38:34: 38:35 + StorageDead(_11); // scope 0 at $DIR/match_false_edges.rs:38:34: 38:35 + falseEdges -> [real: bb3, imaginary: bb3]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + } + + bb14: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:34:11: 41:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:34:11: 41:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/match_false_edges.rs:41:2: 41:2 + } + + bb15 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:34:1: 41:2 + } +} diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index 5ee3e1447d832..c3b07d42f5e62 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -2,6 +2,7 @@ #![feature(exclusive_range_pattern)] +// EMIT_MIR rustc.main.SimplifyCfg-initial.after.mir fn main() { let x = 3; let b = true; @@ -15,70 +16,3 @@ fn main() { _ => 3, }; } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-initial.after.mir -// bb0: { -// ... -// switchInt(move _6) -> [false: bb4, otherwise: bb1]; -// } -// bb1: { -// _7 = Lt(_1, const 10i32); -// switchInt(move _7) -> [false: bb4, otherwise: bb2]; -// } -// bb2: { -// falseEdges -> [real: bb9, imaginary: bb6]; -// } -// bb3: { -// _3 = const 3i32; -// goto -> bb14; -// } -// bb4: { -// _4 = Le(const 10i32, _1); -// switchInt(move _4) -> [false: bb7, otherwise: bb5]; -// } -// bb5: { -// _5 = Le(_1, const 20i32); -// switchInt(move _5) -> [false: bb7, otherwise: bb6]; -// } -// bb6: { -// falseEdges -> [real: bb12, imaginary: bb8]; -// } -// bb7: { -// switchInt(_1) -> [-1i32: bb8, otherwise: bb3]; -// } -// bb8: { -// falseEdges -> [real: bb13, imaginary: bb3]; -// } -// bb9: { -// _8 = &shallow _1; -// StorageLive(_9); -// _9 = _2; -// switchInt(move _9) -> [false: bb11, otherwise: bb10]; -// } -// bb10: { -// StorageDead(_9); -// FakeRead(ForMatchGuard, _8); -// _3 = const 0i32; -// goto -> bb14; -// } -// bb11: { -// StorageDead(_9); -// falseEdges -> [real: bb3, imaginary: bb6]; -// } -// bb12: { -// _3 = const 1i32; -// goto -> bb14; -// } -// bb13: { -// _3 = const 2i32; -// goto -> bb14; -// } -// bb14: { -// StorageDead(_3); -// _0 = (); -// StorageDead(_2); -// StorageDead(_1); -// return; -// } -// END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/match_test/rustc.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/match_test/rustc.main.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..9408248b25dd3 --- /dev/null +++ b/src/test/mir-opt/match_test/rustc.main.SimplifyCfg-initial.after.mir @@ -0,0 +1,172 @@ +// MIR for `main` after SimplifyCfg-initial + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_test.rs:6:11: 6:11 + let _1: i32; // in scope 0 at $DIR/match_test.rs:7:9: 7:10 + let _3: i32; // in scope 0 at $DIR/match_test.rs:12:5: 17:6 + let mut _4: bool; // in scope 0 at $DIR/match_test.rs:14:9: 14:16 + let mut _5: bool; // in scope 0 at $DIR/match_test.rs:14:9: 14:16 + let mut _6: bool; // in scope 0 at $DIR/match_test.rs:13:9: 13:14 + let mut _7: bool; // in scope 0 at $DIR/match_test.rs:13:9: 13:14 + let mut _8: &i32; // in scope 0 at $DIR/match_test.rs:12:11: 12:12 + let mut _9: bool; // in scope 0 at $DIR/match_test.rs:13:18: 13:19 + scope 1 { + debug x => _1; // in scope 1 at $DIR/match_test.rs:7:9: 7:10 + let _2: bool; // in scope 1 at $DIR/match_test.rs:8:9: 8:10 + scope 2 { + debug b => _2; // in scope 2 at $DIR/match_test.rs:8:9: 8:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_test.rs:7:9: 7:10 + _1 = const 3i32; // scope 0 at $DIR/match_test.rs:7:13: 7:14 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match_test.rs:7:13: 7:14 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + FakeRead(ForLet, _1); // scope 0 at $DIR/match_test.rs:7:9: 7:10 + StorageLive(_2); // scope 1 at $DIR/match_test.rs:8:9: 8:10 + _2 = const true; // scope 1 at $DIR/match_test.rs:8:13: 8:17 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/match_test.rs:8:13: 8:17 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForLet, _2); // scope 1 at $DIR/match_test.rs:8:9: 8:10 + StorageLive(_3); // scope 2 at $DIR/match_test.rs:12:5: 17:6 + FakeRead(ForMatchedPlace, _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12 + _6 = Le(const 0i32, _1); // scope 2 at $DIR/match_test.rs:13:9: 13:14 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/match_test.rs:13:9: 13:14 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 + } + + bb1: { + _7 = Lt(_1, const 10i32); // scope 2 at $DIR/match_test.rs:13:9: 13:14 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000000a)) + // mir::Constant + // + span: $DIR/match_test.rs:13:9: 13:14 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } + switchInt(move _7) -> [false: bb4, otherwise: bb2]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 + } + + bb2: { + falseEdges -> [real: bb9, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 + } + + bb3: { + _3 = const 3i32; // scope 2 at $DIR/match_test.rs:16:14: 16:15 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/match_test.rs:16:14: 16:15 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb4: { + _4 = Le(const 10i32, _1); // scope 2 at $DIR/match_test.rs:14:9: 14:16 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000000a)) + // mir::Constant + // + span: $DIR/match_test.rs:14:9: 14:16 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } + switchInt(move _4) -> [false: bb7, otherwise: bb5]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 + } + + bb5: { + _5 = Le(_1, const 20i32); // scope 2 at $DIR/match_test.rs:14:9: 14:16 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000014)) + // mir::Constant + // + span: $DIR/match_test.rs:14:9: 14:16 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000014)) } + switchInt(move _5) -> [false: bb7, otherwise: bb6]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 + } + + bb6: { + falseEdges -> [real: bb12, imaginary: bb8]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 + } + + bb7: { + switchInt(_1) -> [-1i32: bb8, otherwise: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11 + } + + bb8: { + falseEdges -> [real: bb13, imaginary: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11 + } + + bb9: { + _8 = &shallow _1; // scope 2 at $DIR/match_test.rs:12:11: 12:12 + StorageLive(_9); // scope 2 at $DIR/match_test.rs:13:18: 13:19 + _9 = _2; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + switchInt(move _9) -> [false: bb11, otherwise: bb10]; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + } + + bb10: { + StorageDead(_9); // scope 2 at $DIR/match_test.rs:13:24: 13:25 + FakeRead(ForMatchGuard, _8); // scope 2 at $DIR/match_test.rs:13:18: 13:19 + _3 = const 0i32; // scope 2 at $DIR/match_test.rs:13:23: 13:24 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/match_test.rs:13:23: 13:24 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb11: { + StorageDead(_9); // scope 2 at $DIR/match_test.rs:13:24: 13:25 + falseEdges -> [real: bb3, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + } + + bb12: { + _3 = const 1i32; // scope 2 at $DIR/match_test.rs:14:20: 14:21 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/match_test.rs:14:20: 14:21 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb13: { + _3 = const 2i32; // scope 2 at $DIR/match_test.rs:15:15: 15:16 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/match_test.rs:15:15: 15:16 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb14: { + StorageDead(_3); // scope 2 at $DIR/match_test.rs:17:6: 17:7 + _0 = const (); // scope 0 at $DIR/match_test.rs:6:11: 18:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_test.rs:6:11: 18:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/match_test.rs:18:1: 18:2 + StorageDead(_1); // scope 0 at $DIR/match_test.rs:18:1: 18:2 + return; // scope 0 at $DIR/match_test.rs:18:2: 18:2 + } +} diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 2a6c2db03bec1..073ccf7e6c643 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -8,31 +8,8 @@ #![allow(warnings)] +// EMIT_MIR rustc.use_x.nll.0.mir fn use_x<'a, 'b: 'a, 'c>(w: &'a mut i32, x: &'b u32, y: &'a u32, z: &'c u32) -> bool { true } fn main() { } - -// END RUST SOURCE -// START rustc.use_x.nll.0.mir -// | Free Region Mapping -// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r] -// | '_#1r | External | ['_#1r, '_#4r] -// | '_#2r | External | ['_#2r, '_#1r, '_#4r] -// | '_#3r | Local | ['_#4r, '_#3r] -// | '_#4r | Local | ['_#4r] -// | -// | Inferred Region Values -// | '_#0r | U0 | {bb0[0..=1], '_#0r, '_#1r, '_#2r, '_#3r, '_#4r} -// | '_#1r | U0 | {bb0[0..=1], '_#1r} -// | '_#2r | U0 | {bb0[0..=1], '_#2r} -// | '_#3r | U0 | {bb0[0..=1], '_#3r} -// | '_#4r | U0 | {bb0[0..=1], '_#4r} -// | '_#5r | U0 | {bb0[0..=1], '_#1r} -// | '_#6r | U0 | {bb0[0..=1], '_#2r} -// | '_#7r | U0 | {bb0[0..=1], '_#1r} -// | '_#8r | U0 | {bb0[0..=1], '_#3r} -// | -// ... -// fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool { -// END rustc.use_x.nll.0.mir diff --git a/src/test/mir-opt/nll/named-lifetimes-basic/rustc.use_x.nll.0.mir b/src/test/mir-opt/nll/named-lifetimes-basic/rustc.use_x.nll.0.mir new file mode 100644 index 0000000000000..dcfb069b84aad --- /dev/null +++ b/src/test/mir-opt/nll/named-lifetimes-basic/rustc.use_x.nll.0.mir @@ -0,0 +1,54 @@ +// MIR for `use_x` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r] +| '_#1r | External | ['_#1r, '_#4r] +| '_#2r | External | ['_#2r, '_#1r, '_#4r] +| '_#3r | Local | ['_#4r, '_#3r] +| '_#4r | Local | ['_#4r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=1], '_#0r, '_#1r, '_#2r, '_#3r, '_#4r} +| '_#1r | U0 | {bb0[0..=1], '_#1r} +| '_#2r | U0 | {bb0[0..=1], '_#2r} +| '_#3r | U0 | {bb0[0..=1], '_#3r} +| '_#4r | U0 | {bb0[0..=1], '_#4r} +| '_#5r | U0 | {} +| '_#6r | U0 | {bb0[0..=1], '_#1r} +| '_#7r | U0 | {bb0[0..=1], '_#2r} +| '_#8r | U0 | {bb0[0..=1], '_#1r} +| '_#9r | U0 | {bb0[0..=1], '_#3r} +| +| Inference Constraints +| '_#0r live at {bb0[0..=1]} +| '_#1r live at {bb0[0..=1]} +| '_#2r live at {bb0[0..=1]} +| '_#3r live at {bb0[0..=1]} +| '_#4r live at {bb0[0..=1]} +| '_#1r: '_#6r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) +| '_#1r: '_#8r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) +| '_#2r: '_#7r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) +| '_#3r: '_#9r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) +| '_#6r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) +| '_#7r: '_#2r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) +| '_#8r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) +| '_#9r: '_#3r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) +| +fn use_x(_1: &'_#6r mut i32, _2: &'_#7r u32, _3: &'_#8r u32, _4: &'_#9r u32) -> bool { + debug w => _1; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:26: 12:27 + debug x => _2; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:42: 12:43 + debug y => _3; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:54: 12:55 + debug z => _4; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:66: 12:67 + let mut _0: bool; // return place in scope 0 at $DIR/named-lifetimes-basic.rs:12:81: 12:85 + + bb0: { + _0 = const Const(Value(Scalar(0x01)): bool); // bb0[0]: scope 0 at $DIR/named-lifetimes-basic.rs:12:88: 12:92 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/named-lifetimes-basic.rs:12:88: 12:92 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + return; // bb0[1]: scope 0 at $DIR/named-lifetimes-basic.rs:12:94: 12:94 + } +} diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 16e357fc16255..66d7cda2b85a0 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -7,8 +7,12 @@ #![allow(warnings)] -fn use_x(_: usize) -> bool { true } +fn use_x(_: usize) -> bool { + true +} +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.nll.0.mir fn main() { let mut v = [1, 2, 3]; let p = &v[0]; @@ -19,23 +23,3 @@ fn main() { use_x(22); } } - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// | '_#2r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]} -// | '_#3r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]} -// | '_#4r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]} -// END rustc.main.nll.0.mir -// START rustc.main.nll.0.mir -// let _2: &'_#3r usize; -// ... -// debug p => _2; -// ... -// let _6: &'_#4r usize; -// ... -// debug q => _6; -// ... -// _2 = &'_#2r _1[_3]; -// ... -// _6 = _2; -// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-subtyping-basic/32bit/rustc.main.nll.0.mir b/src/test/mir-opt/nll/region-subtyping-basic/32bit/rustc.main.nll.0.mir new file mode 100644 index 0000000000000..d6a3017d86537 --- /dev/null +++ b/src/test/mir-opt/nll/region-subtyping-basic/32bit/rustc.main.nll.0.mir @@ -0,0 +1,171 @@ +// MIR for `main` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#0r, '_#1r] +| '_#1r | Local | ['_#1r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb1[0..=8], bb2[0], bb4[0..=2]} +| '_#4r | U0 | {bb1[1..=8], bb2[0], bb4[0..=2]} +| '_#5r | U0 | {bb1[4..=8], bb2[0], bb4[0..=2]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0]} +| '_#1r live at {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0]} +| '_#3r live at {bb1[0]} +| '_#4r live at {bb1[1..=3]} +| '_#5r live at {bb1[4..=8], bb2[0], bb4[0..=2]} +| '_#3r: '_#4r due to Assignment at Single(bb1[0]) +| '_#4r: '_#5r due to Assignment at Single(bb1[3]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 + let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x00000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + scope 1 { + debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + scope 2 { + debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + scope 3 { + debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + } + } + } + + bb0: { + StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + _1 = [const Const(Value(Scalar(0x00000001)): usize), const Const(Value(Scalar(0x00000002)): usize), const Const(Value(Scalar(0x00000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:18: 17:19 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:21: 17:22 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:24: 17:25 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } + FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const Const(Value(Scalar(0x00000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:18:16: 18:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb1, unwind: bb8]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + } + + bb1: { + _2 = &'_#3r _1[_3]; // bb1[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 + FakeRead(ForLet, _2); // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_6); // bb1[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + _6 = _2; // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 + FakeRead(ForLet, _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + StorageLive(_7); // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:8: 20:12 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForMatchedPlace, _7); // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb3, otherwise: bb2]; // bb1[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb2: { + falseEdges -> [real: bb4, imaginary: bb3]; // bb2[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb3: { + StorageLive(_10); // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x00000016)): usize)) -> [return: bb6, unwind: bb8]; // bb3[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000016)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:15: 23:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000016)) } + } + + bb4: { + StorageLive(_8); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + StorageLive(_9); // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _9 = (*_6); // bb4[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _8 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb5, unwind: bb8]; // bb4[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_9); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 + StorageDead(_8); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 + _0 = const Const(Value(Scalar()): ()); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:13: 22:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb7; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb6: { + StorageDead(_10); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 + _0 = const Const(Value(Scalar()): ()); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:22:12: 24:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb7; // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb7: { + StorageDead(_6); // bb7[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_3); // bb7[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_2); // bb7[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_1); // bb7[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_7); // bb7[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + return; // bb7[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 + } + + bb8 (cleanup): { + resume; // bb8[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 + } +} diff --git a/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir b/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir new file mode 100644 index 0000000000000..66f2850566fff --- /dev/null +++ b/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir @@ -0,0 +1,171 @@ +// MIR for `main` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#0r, '_#1r] +| '_#1r | Local | ['_#1r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb1[0..=8], bb2[0], bb4[0..=2]} +| '_#4r | U0 | {bb1[1..=8], bb2[0], bb4[0..=2]} +| '_#5r | U0 | {bb1[4..=8], bb2[0], bb4[0..=2]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0]} +| '_#1r live at {bb0[0..=8], bb1[0..=8], bb2[0], bb3[0..=1], bb4[0..=3], bb5[0..=3], bb6[0..=2], bb7[0..=5], bb8[0]} +| '_#3r live at {bb1[0]} +| '_#4r live at {bb1[1..=3]} +| '_#5r live at {bb1[4..=8], bb2[0], bb4[0..=2]} +| '_#3r: '_#4r due to Assignment at Single(bb1[0]) +| '_#4r: '_#5r due to Assignment at Single(bb1[3]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 + let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x0000000000000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + scope 1 { + debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + scope 2 { + debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + scope 3 { + debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + } + } + } + + bb0: { + StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + _1 = [const Const(Value(Scalar(0x0000000000000001)): usize), const Const(Value(Scalar(0x0000000000000002)): usize), const Const(Value(Scalar(0x0000000000000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:18: 17:19 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000002)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:21: 17:22 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000003)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:24: 17:25 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } + FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const Const(Value(Scalar(0x0000000000000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:18:16: 18:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb1, unwind: bb8]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + } + + bb1: { + _2 = &'_#3r _1[_3]; // bb1[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 + FakeRead(ForLet, _2); // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_6); // bb1[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + _6 = _2; // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 + FakeRead(ForLet, _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + StorageLive(_7); // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:8: 20:12 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForMatchedPlace, _7); // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb3, otherwise: bb2]; // bb1[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb2: { + falseEdges -> [real: bb4, imaginary: bb3]; // bb2[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb3: { + StorageLive(_10); // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x0000000000000016)): usize)) -> [return: bb6, unwind: bb8]; // bb3[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000016)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:15: 23:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000016)) } + } + + bb4: { + StorageLive(_8); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + StorageLive(_9); // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _9 = (*_6); // bb4[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _8 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb5, unwind: bb8]; // bb4[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_9); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 + StorageDead(_8); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 + _0 = const Const(Value(Scalar()): ()); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:13: 22:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb7; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb6: { + StorageDead(_10); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 + _0 = const Const(Value(Scalar()): ()); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:22:12: 24:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb7; // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb7: { + StorageDead(_6); // bb7[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_3); // bb7[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_2); // bb7[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_1); // bb7[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_7); // bb7[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + return; // bb7[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 + } + + bb8 (cleanup): { + resume; // bb8[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 + } +} diff --git a/src/test/mir-opt/no-drop-for-inactive-variant.rs b/src/test/mir-opt/no-drop-for-inactive-variant.rs index 74a606af28fc1..cf6426b878a3c 100644 --- a/src/test/mir-opt/no-drop-for-inactive-variant.rs +++ b/src/test/mir-opt/no-drop-for-inactive-variant.rs @@ -3,6 +3,7 @@ // Ensure that there are no drop terminators in `unwrap` (except the one along the cleanup // path). +// EMIT_MIR rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir fn unwrap(opt: Option) -> T { match opt { Some(x) => x, @@ -13,31 +14,3 @@ fn unwrap(opt: Option) -> T { fn main() { let _ = unwrap(Some(1i32)); } - -// END RUST SOURCE -// START rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir -// fn unwrap(_1: std::option::Option) -> T { -// ... -// bb0: { -// ... -// switchInt(move _2) -> [0isize: bb2, 1isize: bb4, otherwise: bb3]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// ... -// const std::rt::begin_panic::<&str>(const "explicit panic") -> bb5; -// } -// bb3: { -// unreachable; -// } -// bb4: { -// ... -// return; -// } -// bb5 (cleanup): { -// drop(_1) -> bb1; -// } -// } -// END rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/no-drop-for-inactive-variant/rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/no-drop-for-inactive-variant/rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..cc8e01d298507 --- /dev/null +++ b/src/test/mir-opt/no-drop-for-inactive-variant/rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,57 @@ +// MIR for `unwrap` after SimplifyCfg-elaborate-drops + +fn unwrap(_1: std::option::Option) -> T { + debug opt => _1; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:14: 7:17 + let mut _0: T; // return place in scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:33: 7:34 + let mut _2: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 + let _3: T; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + let mut _4: !; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL + let mut _5: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + let mut _6: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + scope 1 { + debug x => _3; // in scope 1 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 + switchInt(move _2) -> [0isize: bb1, 1isize: bb3, otherwise: bb2]; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 + } + + bb1: { + StorageLive(_4); // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL + const std::rt::begin_panic::<&str>(const "explicit panic") -> bb4; // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL + // ty::Const + // + ty: fn(&str) -> ! {std::rt::begin_panic::<&str>} + // + val: Value(Scalar()) + // mir::Constant + // + span: $SRC_DIR/libstd/macros.rs:LL:COL + // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar()) } + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) + // mir::Constant + // + span: $SRC_DIR/libstd/macros.rs:LL:COL + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } + } + + bb2: { + unreachable; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:8:11: 8:14 + } + + bb3: { + StorageLive(_3); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + _3 = move ((_1 as Some).0: T); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + _0 = move _3; // scope 1 at $DIR/no-drop-for-inactive-variant.rs:9:20: 9:21 + StorageDead(_3); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:21: 9:22 + _5 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + return; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:2: 12:2 + } + + bb4 (cleanup): { + drop(_1) -> bb5; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + } + + bb5 (cleanup): { + resume; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:1: 12:2 + } +} diff --git a/src/test/mir-opt/no-spurious-drop-after-call.rs b/src/test/mir-opt/no-spurious-drop-after-call.rs index 782bc31186ca5..ab58654e07c05 100644 --- a/src/test/mir-opt/no-spurious-drop-after-call.rs +++ b/src/test/mir-opt/no-spurious-drop-after-call.rs @@ -4,21 +4,7 @@ // MIR drop of the argument. (We used to have a `DROP(_2)` in the code // below, as part of bb3.) +// EMIT_MIR rustc.main.ElaborateDrops.before.mir fn main() { std::mem::drop("".to_string()); } - -// END RUST SOURCE -// START rustc.main.ElaborateDrops.before.mir -// bb2: { -// StorageDead(_3); -// _1 = const std::mem::drop::(move _2) -> [return: bb3, unwind: bb4]; -// } -// bb3: { -// StorageDead(_2); -// StorageDead(_4); -// StorageDead(_1); -// _0 = (); -// return; -// } -// END rustc.main.ElaborateDrops.before.mir diff --git a/src/test/mir-opt/no-spurious-drop-after-call/rustc.main.ElaborateDrops.before.mir b/src/test/mir-opt/no-spurious-drop-after-call/rustc.main.ElaborateDrops.before.mir new file mode 100644 index 0000000000000..0d619af101a18 --- /dev/null +++ b/src/test/mir-opt/no-spurious-drop-after-call/rustc.main.ElaborateDrops.before.mir @@ -0,0 +1,64 @@ +// MIR for `main` before ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 8:11 + let _1: (); // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 + let mut _2: std::string::String; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 + let mut _3: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + let _4: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 + StorageLive(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 + StorageLive(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + StorageLive(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + _4 = const ""; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, size: Size { raw: 0 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, size: Size { raw: 0 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) } + _3 = &(*_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + _2 = const ::to_string(move _3) -> bb1; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 + // ty::Const + // + ty: for<'r> fn(&'r str) -> std::string::String {::to_string} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:23: 9:32 + // + literal: Const { ty: for<'r> fn(&'r str) -> std::string::String {::to_string}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:33: 9:34 + _1 = const std::mem::drop::(move _2) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 + // ty::Const + // + ty: fn(std::string::String) {std::mem::drop::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:5: 9:19 + // + literal: Const { ty: fn(std::string::String) {std::mem::drop::}, val: Value(Scalar()) } + } + + bb2: { + StorageDead(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 + StorageDead(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 + StorageDead(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 + _0 = const (); // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 10:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:8:11: 10:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/no-spurious-drop-after-call.rs:10:2: 10:2 + } + + bb3 (cleanup): { + drop(_2) -> bb4; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 + } + + bb4 (cleanup): { + resume; // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:1: 10:2 + } +} diff --git a/src/test/mir-opt/nrvo-simple.rs b/src/test/mir-opt/nrvo-simple.rs new file mode 100644 index 0000000000000..bf3a0efeada0b --- /dev/null +++ b/src/test/mir-opt/nrvo-simple.rs @@ -0,0 +1,10 @@ +// EMIT_MIR rustc.nrvo.RenameReturnPlace.diff +fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { + let mut buf = [0; 1024]; + init(&mut buf); + buf +} + +fn main() { + let _ = nrvo(|buf| { buf[4] = 4; }); +} diff --git a/src/test/mir-opt/nrvo-simple/rustc.nrvo.RenameReturnPlace.diff b/src/test/mir-opt/nrvo-simple/rustc.nrvo.RenameReturnPlace.diff new file mode 100644 index 0000000000000..79d92897cb572 --- /dev/null +++ b/src/test/mir-opt/nrvo-simple/rustc.nrvo.RenameReturnPlace.diff @@ -0,0 +1,41 @@ +- // MIR for `nrvo` before RenameReturnPlace ++ // MIR for `nrvo` after RenameReturnPlace + + fn nrvo(_1: for<'r> fn(&'r mut [u8; 1024])) -> [u8; 1024] { + debug init => _1; // in scope 0 at $DIR/nrvo-simple.rs:2:9: 2:13 +- let mut _0: [u8; 1024]; // return place in scope 0 at $DIR/nrvo-simple.rs:2:39: 2:49 ++ let mut _0: [u8; 1024]; // return place in scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 + let mut _2: [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 + let _3: (); // in scope 0 at $DIR/nrvo-simple.rs:4:5: 4:19 + let mut _4: for<'r> fn(&'r mut [u8; 1024]); // in scope 0 at $DIR/nrvo-simple.rs:4:5: 4:9 + let mut _5: &mut [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:4:10: 4:18 + let mut _6: &mut [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:4:10: 4:18 + scope 1 { +- debug buf => _2; // in scope 1 at $DIR/nrvo-simple.rs:3:9: 3:16 ++ debug buf => _0; // in scope 1 at $DIR/nrvo-simple.rs:3:9: 3:16 + } + + bb0: { +- StorageLive(_2); // scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 +- _2 = [const 0u8; 1024]; // scope 0 at $DIR/nrvo-simple.rs:3:19: 3:28 ++ _0 = [const 0u8; 1024]; // scope 0 at $DIR/nrvo-simple.rs:3:19: 3:28 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/nrvo-simple.rs:3:20: 3:21 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + StorageLive(_3); // scope 1 at $DIR/nrvo-simple.rs:4:5: 4:19 +- _6 = &mut _2; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 ++ _6 = &mut _0; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 + _3 = move _1(move _6) -> bb1; // scope 1 at $DIR/nrvo-simple.rs:4:5: 4:19 + } + + bb1: { + StorageDead(_3); // scope 1 at $DIR/nrvo-simple.rs:4:19: 4:20 +- _0 = _2; // scope 1 at $DIR/nrvo-simple.rs:5:5: 5:8 +- StorageDead(_2); // scope 0 at $DIR/nrvo-simple.rs:6:1: 6:2 + return; // scope 0 at $DIR/nrvo-simple.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/packed-struct-drop-aligned.rs b/src/test/mir-opt/packed-struct-drop-aligned.rs index 39b9006017958..daf397c3d9c14 100644 --- a/src/test/mir-opt/packed-struct-drop-aligned.rs +++ b/src/test/mir-opt/packed-struct-drop-aligned.rs @@ -1,5 +1,7 @@ // ignore-wasm32-bare compiled with panic=abort by default +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir fn main() { let mut x = Packed(Aligned(Droppy(0))); x.0 = Aligned(Droppy(0)); @@ -13,47 +15,3 @@ struct Droppy(usize); impl Drop for Droppy { fn drop(&mut self) {} } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-elaborate-drops.after.mir -// fn main() -> () { -// let mut _0: (); -// let mut _1: Packed; -// let mut _2: Aligned; -// let mut _3: Droppy; -// let mut _4: Aligned; -// let mut _5: Droppy; -// let mut _6: Aligned; -// scope 1 { -// debug x => _1; -// } -// -// bb0: { -// StorageLive(_1); -// ... -// _1 = Packed(move _2,); -// ... -// StorageLive(_6); -// _6 = move (_1.0: Aligned); -// drop(_6) -> [return: bb4, unwind: bb3]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// StorageDead(_1); -// return; -// } -// bb3 (cleanup): { -// (_1.0: Aligned) = move _4; -// drop(_1) -> bb1; -// } -// bb4: { -// StorageDead(_6); -// (_1.0: Aligned) = move _4; -// StorageDead(_4); -// _0 = (); -// drop(_1) -> [return: bb2, unwind: bb1]; -// } -// } -// END rustc.main.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/packed-struct-drop-aligned/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/packed-struct-drop-aligned/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..b2da77320b8e1 --- /dev/null +++ b/src/test/mir-opt/packed-struct-drop-aligned/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,73 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 5:11 + let mut _1: Packed; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + let mut _2: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + let mut _3: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + let mut _4: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + let mut _5: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + let mut _6: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + scope 1 { + debug x => _1; // in scope 1 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + _3 = Droppy(const 0usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:6:39: 6:40 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:41: 6:42 + _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:17: 6:43 + StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:42: 6:43 + StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + _5 = Droppy(const 0usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:7:26: 7:27 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + _6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_6) -> [return: bb4, unwind: bb3]; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + } + + bb1: { + StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 + } + + bb3 (cleanup): { + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_1) -> bb2; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } +} diff --git a/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..b9466d88299d5 --- /dev/null +++ b/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,73 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 5:11 + let mut _1: Packed; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + let mut _2: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + let mut _3: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + let mut _4: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + let mut _5: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + let mut _6: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + scope 1 { + debug x => _1; // in scope 1 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + _3 = Droppy(const 0usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:6:39: 6:40 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:41: 6:42 + _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:17: 6:43 + StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:42: 6:43 + StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + _5 = Droppy(const 0usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:7:26: 7:27 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + _6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_6) -> [return: bb4, unwind: bb3]; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + } + + bb1: { + StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 + } + + bb3 (cleanup): { + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_1) -> bb2; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } +} diff --git a/src/test/mir-opt/remove-never-const.rs b/src/test/mir-opt/remove-never-const.rs new file mode 100644 index 0000000000000..b2d4f14aa4cd8 --- /dev/null +++ b/src/test/mir-opt/remove-never-const.rs @@ -0,0 +1,23 @@ +// This was originally a regression test for #66975 - ensure that we do not generate never typed +// consts in codegen. We also have tests for this that catches the error, see +// src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs. + +// Force generation of optimized mir for functions that do not reach codegen. +// compile-flags: --emit mir,link + +#![feature(const_panic)] +#![feature(never_type)] +#![warn(const_err)] + +struct PrintName(T); + +impl PrintName { + const VOID: ! = panic!(); +} + +// EMIT_MIR rustc.no_codegen.PreCodegen.after.mir +fn no_codegen() { + let _ = PrintName::::VOID; +} + +fn main() {} diff --git a/src/test/mir-opt/remove-never-const/rustc.no_codegen.PreCodegen.after.mir b/src/test/mir-opt/remove-never-const/rustc.no_codegen.PreCodegen.after.mir new file mode 100644 index 0000000000000..6f4a024d20f93 --- /dev/null +++ b/src/test/mir-opt/remove-never-const/rustc.no_codegen.PreCodegen.after.mir @@ -0,0 +1,11 @@ +// MIR for `no_codegen` after PreCodegen + +fn no_codegen() -> () { + let mut _0: (); // return place in scope 0 at $DIR/remove-never-const.rs:19:20: 19:20 + scope 1 { + } + + bb0: { + unreachable; // scope 0 at $DIR/remove-never-const.rs:20:13: 20:33 + } +} diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index ea106eaf59529..fd2f1d0dbffc6 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -2,6 +2,7 @@ // ignore-wasm32-bare compiled with panic=abort by default +// EMIT_MIR rustc.match_guard.CleanupNonCodegenStatements.diff fn match_guard(x: Option<&&i32>, c: bool) -> i32 { match x { Some(0) if c => 0, @@ -12,97 +13,3 @@ fn match_guard(x: Option<&&i32>, c: bool) -> i32 { fn main() { match_guard(None, true); } - -// END RUST SOURCE - -// START rustc.match_guard.CleanupNonCodegenStatements.before.mir -// bb0: { -// FakeRead(ForMatchedPlace, _1); -// _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb2, otherwise: bb1]; -// } -// bb1: { -// _0 = const 1i32; -// goto -> bb7; -// } -// bb2: { -// switchInt((*(*((_1 as Some).0: &&i32)))) -> [0i32: bb3, otherwise: bb1]; -// } -// bb3: { -// goto -> bb4; -// } -// bb4: { -// _4 = &shallow _1; -// _5 = &shallow ((_1 as Some).0: &&i32); -// _6 = &shallow (*((_1 as Some).0: &&i32)); -// _7 = &shallow (*(*((_1 as Some).0: &&i32))); -// StorageLive(_8); -// _8 = _2; -// switchInt(move _8) -> [false: bb6, otherwise: bb5]; -// } -// bb5: { -// StorageDead(_8); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForMatchGuard, _5); -// FakeRead(ForMatchGuard, _6); -// FakeRead(ForMatchGuard, _7); -// _0 = const 0i32; -// goto -> bb7; -// } -// bb6: { -// StorageDead(_8); -// goto -> bb1; -// } -// bb7: { -// return; -// } -// bb8 (cleanup): { -// resume; -// } -// END rustc.match_guard.CleanupNonCodegenStatements.before.mir - -// START rustc.match_guard.CleanupNonCodegenStatements.after.mir -// bb0: { -// nop; -// _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb2, otherwise: bb1]; -// } -// bb1: { -// _0 = const 1i32; -// goto -> bb7; -// } -// bb2: { -// switchInt((*(*((_1 as Some).0: &&i32)))) -> [0i32: bb3, otherwise: bb1]; -// } -// bb3: { -// goto -> bb4; -// } -// bb4: { -// nop; -// nop; -// nop; -// nop; -// StorageLive(_8); -// _8 = _2; -// switchInt(move _8) -> [false: bb6, otherwise: bb5]; -// } -// bb5: { -// StorageDead(_8); -// nop; -// nop; -// nop; -// nop; -// _0 = const 0i32; -// goto -> bb7; -// } -// bb6: { -// StorageDead(_8); -// goto -> bb1; -// } -// bb7: { -// return; -// } -// bb8 (cleanup): { -// resume; -// } -// END rustc.match_guard.CleanupNonCodegenStatements.after.mir diff --git a/src/test/mir-opt/remove_fake_borrows/rustc.match_guard.CleanupNonCodegenStatements.diff b/src/test/mir-opt/remove_fake_borrows/rustc.match_guard.CleanupNonCodegenStatements.diff new file mode 100644 index 0000000000000..4e626b1384afc --- /dev/null +++ b/src/test/mir-opt/remove_fake_borrows/rustc.match_guard.CleanupNonCodegenStatements.diff @@ -0,0 +1,88 @@ +- // MIR for `match_guard` before CleanupNonCodegenStatements ++ // MIR for `match_guard` after CleanupNonCodegenStatements + + fn match_guard(_1: std::option::Option<&&i32>, _2: bool) -> i32 { + debug x => _1; // in scope 0 at $DIR/remove_fake_borrows.rs:6:16: 6:17 + debug c => _2; // in scope 0 at $DIR/remove_fake_borrows.rs:6:34: 6:35 + let mut _0: i32; // return place in scope 0 at $DIR/remove_fake_borrows.rs:6:46: 6:49 + let mut _3: isize; // in scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + let mut _4: &std::option::Option<&&i32>; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _5: &&&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _6: &&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _7: &i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _8: bool; // in scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + + bb0: { +- FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + _3 = discriminant(_1); // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + switchInt(move _3) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + } + + bb1: { + _0 = const 1i32; // scope 0 at $DIR/remove_fake_borrows.rs:9:14: 9:15 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/remove_fake_borrows.rs:9:14: 9:15 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:7:5: 10:6 + } + + bb2: { + switchInt((*(*((_1 as Some).0: &&i32)))) -> [0i32: bb3, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:14: 8:15 + } + + bb3: { + goto -> bb4; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + } + + bb4: { +- _4 = &shallow _1; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 +- _5 = &shallow ((_1 as Some).0: &&i32); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 +- _6 = &shallow (*((_1 as Some).0: &&i32)); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 +- _7 = &shallow (*(*((_1 as Some).0: &&i32))); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + StorageLive(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + _8 = _2; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + switchInt(move _8) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + } + + bb5: { + StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:26: 8:27 +- FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 +- FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 +- FakeRead(ForMatchGuard, _6); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 +- FakeRead(ForMatchGuard, _7); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + _0 = const 0i32; // scope 0 at $DIR/remove_fake_borrows.rs:8:25: 8:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/remove_fake_borrows.rs:8:25: 8:26 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:7:5: 10:6 + } + + bb6: { + StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:26: 8:27 + goto -> bb1; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + } + + bb7: { + return; // scope 0 at $DIR/remove_fake_borrows.rs:11:2: 11:2 + } + + bb8 (cleanup): { + resume; // scope 0 at $DIR/remove_fake_borrows.rs:6:1: 11:2 + } + } + diff --git a/src/test/mir-opt/retag.rs b/src/test/mir-opt/retag.rs index a0a7a760c2da5..eba0f567c4a08 100644 --- a/src/test/mir-opt/retag.rs +++ b/src/test/mir-opt/retag.rs @@ -6,6 +6,8 @@ struct Test(i32); +// EMIT_MIR rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir impl Test { // Make sure we run the pass on a method, not just on bare functions. fn foo<'x>(&self, x: &'x mut i32) -> &'x mut i32 { @@ -16,10 +18,14 @@ impl Test { } } +// EMIT_MIR rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir + impl Drop for Test { fn drop(&mut self) {} } +// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir fn main() { let mut x = 0; { @@ -43,94 +49,3 @@ fn main() { // escape-to-raw (shr) let _w = _w as *const _; } - -// END RUST SOURCE -// START rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir -// bb0: { -// Retag([fn entry] _1); -// Retag([fn entry] _2); -// ... -// _0 = &mut (*_3); -// Retag(_0); -// ... -// return; -// } -// END rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir -// START rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir -// bb0: { -// Retag([fn entry] _1); -// Retag([fn entry] _2); -// ... -// _0 = _2; -// Retag(_0); -// ... -// return; -// } -// END rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir -// START rustc.main.SimplifyCfg-elaborate-drops.after.mir -// fn main() -> () { -// ... -// bb0: { -// ... -// _3 = const Test::foo(move _4, move _6) -> [return: bb2, unwind: bb3]; -// } -// -// ... -// -// bb2: { -// Retag(_3); -// ... -// _9 = move _3; -// Retag(_9); -// _8 = &mut (*_9); -// Retag(_8); -// StorageDead(_9); -// StorageLive(_10); -// _10 = move _8; -// Retag(_10); -// ... -// _12 = &raw mut (*_10); -// Retag([raw] _12); -// ... -// _15 = move _16(move _17) -> bb5; -// } -// -// bb5: { -// Retag(_15); -// ... -// _19 = const Test::foo_shr(move _20, move _22) -> [return: bb6, unwind: bb7]; -// } -// -// ... -// } -// END rustc.main.SimplifyCfg-elaborate-drops.after.mir -// START rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir -// fn main::{{closure}}#0(_1: &[closure@main::{{closure}}#0], _2: &i32) -> &i32 { -// ... -// bb0: { -// Retag([fn entry] _1); -// Retag([fn entry] _2); -// StorageLive(_3); -// _3 = _2; -// Retag(_3); -// _0 = _2; -// Retag(_0); -// StorageDead(_3); -// return; -// } -// } -// END rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir -// START rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir -// fn std::intrinsics::drop_in_place(_1: *mut Test) -> () { -// ... -// bb0: { -// Retag([raw] _1); -// _2 = &mut (*_1); -// _3 = const ::drop(move _2) -> bb1; -// } -// -// bb1: { -// return; -// } -// } -// END rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir diff --git a/src/test/mir-opt/retag/rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag/rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..01f5fbb7d236c --- /dev/null +++ b/src/test/mir-opt/retag/rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,22 @@ +// MIR for `main::{{closure}}#0` after SimplifyCfg-elaborate-drops + +fn main::{{closure}}#0(_1: &[closure@main::{{closure}}#0], _2: &i32) -> &i32 { + debug x => _2; // in scope 0 at $DIR/retag.rs:40:32: 40:33 + let mut _0: &i32; // return place in scope 0 at $DIR/retag.rs:40:44: 40:48 + let _3: &i32; // in scope 0 at $DIR/retag.rs:41:13: 41:15 + scope 1 { + debug _y => _3; // in scope 1 at $DIR/retag.rs:41:13: 41:15 + } + + bb0: { + Retag([fn entry] _1); // scope 0 at $DIR/retag.rs:40:31: 43:6 + Retag([fn entry] _2); // scope 0 at $DIR/retag.rs:40:31: 43:6 + StorageLive(_3); // scope 0 at $DIR/retag.rs:41:13: 41:15 + _3 = _2; // scope 0 at $DIR/retag.rs:41:18: 41:19 + Retag(_3); // scope 0 at $DIR/retag.rs:41:18: 41:19 + _0 = _2; // scope 1 at $DIR/retag.rs:42:9: 42:10 + Retag(_0); // scope 1 at $DIR/retag.rs:42:9: 42:10 + StorageDead(_3); // scope 0 at $DIR/retag.rs:43:5: 43:6 + return; // scope 0 at $DIR/retag.rs:43:6: 43:6 + } +} diff --git a/src/test/mir-opt/retag/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag/rustc.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..f2154ef6b1e91 --- /dev/null +++ b/src/test/mir-opt/retag/rustc.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,239 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/retag.rs:29:11: 29:11 + let mut _1: i32; // in scope 0 at $DIR/retag.rs:30:9: 30:14 + let _2: (); // in scope 0 at $DIR/retag.rs:31:5: 37:6 + let mut _4: &Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24 + let _5: Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24 + let mut _6: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35 + let mut _7: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35 + let mut _9: &mut i32; // in scope 0 at $DIR/retag.rs:33:19: 33:20 + let mut _12: *mut i32; // in scope 0 at $DIR/retag.rs:36:18: 36:29 + let mut _14: [closure@main::{{closure}}#0]; // in scope 0 at $DIR/retag.rs:40:31: 43:6 + let mut _16: for<'r> fn(&'r i32) -> &'r i32; // in scope 0 at $DIR/retag.rs:44:14: 44:15 + let mut _17: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18 + let _18: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18 + let _19: &i32; // in scope 0 at $DIR/retag.rs:47:5: 47:24 + let mut _20: &Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12 + let _21: Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12 + let mut _22: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23 + let _23: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23 + let _24: i32; // in scope 0 at $DIR/retag.rs:47:22: 47:23 + let mut _26: *const i32; // in scope 0 at $DIR/retag.rs:50:14: 50:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/retag.rs:30:9: 30:14 + let _3: &mut i32; // in scope 1 at $DIR/retag.rs:32:13: 32:14 + let _13: for<'r> fn(&'r i32) -> &'r i32 as UserTypeProjection { base: UserType(1), projs: [] }; // in scope 1 at $DIR/retag.rs:40:9: 40:10 + scope 2 { + debug v => _3; // in scope 2 at $DIR/retag.rs:32:13: 32:14 + let _8: &mut i32; // in scope 2 at $DIR/retag.rs:33:13: 33:14 + scope 3 { + debug w => _8; // in scope 3 at $DIR/retag.rs:33:13: 33:14 + let _10: &mut i32; // in scope 3 at $DIR/retag.rs:34:13: 34:14 + scope 4 { + debug w => _10; // in scope 4 at $DIR/retag.rs:34:13: 34:14 + let _11: *mut i32; // in scope 4 at $DIR/retag.rs:36:13: 36:15 + scope 5 { + debug _w => _11; // in scope 5 at $DIR/retag.rs:36:13: 36:15 + } + } + } + } + scope 6 { + debug c => _13; // in scope 6 at $DIR/retag.rs:40:9: 40:10 + let _15: &i32; // in scope 6 at $DIR/retag.rs:44:9: 44:11 + scope 7 { + debug _w => _15; // in scope 7 at $DIR/retag.rs:44:9: 44:11 + let _25: *const i32; // in scope 7 at $DIR/retag.rs:50:9: 50:11 + let mut _27: &i32; // in scope 7 at $DIR/retag.rs:47:21: 47:23 + scope 8 { + debug _w => _25; // in scope 8 at $DIR/retag.rs:50:9: 50:11 + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/retag.rs:30:9: 30:14 + _1 = const 0i32; // scope 0 at $DIR/retag.rs:30:17: 30:18 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/retag.rs:30:17: 30:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageLive(_2); // scope 1 at $DIR/retag.rs:31:5: 37:6 + StorageLive(_3); // scope 1 at $DIR/retag.rs:32:13: 32:14 + StorageLive(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24 + StorageLive(_5); // scope 1 at $DIR/retag.rs:32:17: 32:24 + _5 = Test(const 0i32); // scope 1 at $DIR/retag.rs:32:17: 32:24 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/retag.rs:32:22: 32:23 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + _4 = &_5; // scope 1 at $DIR/retag.rs:32:17: 32:24 + Retag(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24 + StorageLive(_6); // scope 1 at $DIR/retag.rs:32:29: 32:35 + StorageLive(_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 + _7 = &mut _1; // scope 1 at $DIR/retag.rs:32:29: 32:35 + Retag(_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 + _6 = &mut (*_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 + Retag([2phase] _6); // scope 1 at $DIR/retag.rs:32:29: 32:35 + _3 = const Test::foo(move _4, move _6) -> [return: bb1, unwind: bb7]; // scope 1 at $DIR/retag.rs:32:17: 32:36 + // ty::Const + // + ty: for<'r, 'x> fn(&'r Test, &'x mut i32) -> &'x mut i32 {Test::foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:32:25: 32:28 + // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x mut i32) -> &'x mut i32 {Test::foo}, val: Value(Scalar()) } + } + + bb1: { + Retag(_3); // scope 1 at $DIR/retag.rs:32:17: 32:36 + StorageDead(_6); // scope 1 at $DIR/retag.rs:32:35: 32:36 + StorageDead(_4); // scope 1 at $DIR/retag.rs:32:35: 32:36 + StorageDead(_7); // scope 1 at $DIR/retag.rs:32:36: 32:37 + drop(_5) -> [return: bb2, unwind: bb8]; // scope 1 at $DIR/retag.rs:32:36: 32:37 + } + + bb2: { + StorageDead(_5); // scope 1 at $DIR/retag.rs:32:36: 32:37 + StorageLive(_8); // scope 2 at $DIR/retag.rs:33:13: 33:14 + StorageLive(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + _9 = move _3; // scope 2 at $DIR/retag.rs:33:19: 33:20 + Retag(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + _8 = &mut (*_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + Retag(_8); // scope 2 at $DIR/retag.rs:33:19: 33:20 + StorageDead(_9); // scope 2 at $DIR/retag.rs:33:22: 33:23 + StorageLive(_10); // scope 3 at $DIR/retag.rs:34:13: 34:14 + _10 = move _8; // scope 3 at $DIR/retag.rs:34:17: 34:18 + Retag(_10); // scope 3 at $DIR/retag.rs:34:17: 34:18 + StorageLive(_11); // scope 4 at $DIR/retag.rs:36:13: 36:15 + StorageLive(_12); // scope 4 at $DIR/retag.rs:36:18: 36:29 + _12 = &raw mut (*_10); // scope 4 at $DIR/retag.rs:36:18: 36:19 + Retag([raw] _12); // scope 4 at $DIR/retag.rs:36:18: 36:19 + _11 = _12; // scope 4 at $DIR/retag.rs:36:18: 36:29 + StorageDead(_12); // scope 4 at $DIR/retag.rs:36:29: 36:30 + _2 = const (); // scope 1 at $DIR/retag.rs:31:5: 37:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:31:5: 37:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_11); // scope 4 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_10); // scope 3 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_8); // scope 2 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_3); // scope 1 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_2); // scope 1 at $DIR/retag.rs:37:5: 37:6 + StorageLive(_13); // scope 1 at $DIR/retag.rs:40:9: 40:10 + StorageLive(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 + _14 = [closure@main::{{closure}}#0]; // scope 1 at $DIR/retag.rs:40:31: 43:6 + // closure + // + def_id: DefId(0:14 ~ retag[317d]::main[0]::{{closure}}[0]) + // + substs: [ + // i8, + // for<'r> extern "rust-call" fn((&'r i32,)) -> &'r i32, + // (), + // ] + Retag(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 + _13 = move _14 as for<'r> fn(&'r i32) -> &'r i32 (Pointer(ClosureFnPointer(Normal))); // scope 1 at $DIR/retag.rs:40:31: 43:6 + StorageDead(_14); // scope 1 at $DIR/retag.rs:43:5: 43:6 + StorageLive(_15); // scope 6 at $DIR/retag.rs:44:9: 44:11 + StorageLive(_16); // scope 6 at $DIR/retag.rs:44:14: 44:15 + _16 = _13; // scope 6 at $DIR/retag.rs:44:14: 44:15 + StorageLive(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 + StorageLive(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _18 = &_1; // scope 6 at $DIR/retag.rs:44:16: 44:18 + Retag(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _17 = &(*_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + Retag(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _15 = move _16(move _17) -> bb3; // scope 6 at $DIR/retag.rs:44:14: 44:19 + } + + bb3: { + Retag(_15); // scope 6 at $DIR/retag.rs:44:14: 44:19 + StorageDead(_17); // scope 6 at $DIR/retag.rs:44:18: 44:19 + StorageDead(_16); // scope 6 at $DIR/retag.rs:44:18: 44:19 + StorageDead(_18); // scope 6 at $DIR/retag.rs:44:19: 44:20 + StorageLive(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 + StorageLive(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12 + StorageLive(_21); // scope 7 at $DIR/retag.rs:47:5: 47:12 + _21 = Test(const 0i32); // scope 7 at $DIR/retag.rs:47:5: 47:12 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/retag.rs:47:10: 47:11 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + _20 = &_21; // scope 7 at $DIR/retag.rs:47:5: 47:12 + Retag(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12 + StorageLive(_22); // scope 7 at $DIR/retag.rs:47:21: 47:23 + StorageLive(_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _27 = const main::promoted[0]; // scope 7 at $DIR/retag.rs:47:21: 47:23 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(DefId(0:13 ~ retag[317d]::main[0]), [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/retag.rs:47:21: 47:23 + // + literal: Const { ty: &i32, val: Unevaluated(DefId(0:13 ~ retag[317d]::main[0]), [], Some(promoted[0])) } + Retag(_27); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _23 = &(*_27); // scope 7 at $DIR/retag.rs:47:21: 47:23 + Retag(_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _22 = &(*_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 + Retag(_22); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _19 = const Test::foo_shr(move _20, move _22) -> [return: bb4, unwind: bb6]; // scope 7 at $DIR/retag.rs:47:5: 47:24 + // ty::Const + // + ty: for<'r, 'x> fn(&'r Test, &'x i32) -> &'x i32 {Test::foo_shr} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:47:13: 47:20 + // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x i32) -> &'x i32 {Test::foo_shr}, val: Value(Scalar()) } + } + + bb4: { + Retag(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 + StorageDead(_22); // scope 7 at $DIR/retag.rs:47:23: 47:24 + StorageDead(_20); // scope 7 at $DIR/retag.rs:47:23: 47:24 + StorageDead(_23); // scope 7 at $DIR/retag.rs:47:24: 47:25 + drop(_21) -> [return: bb5, unwind: bb8]; // scope 7 at $DIR/retag.rs:47:24: 47:25 + } + + bb5: { + StorageDead(_21); // scope 7 at $DIR/retag.rs:47:24: 47:25 + StorageDead(_19); // scope 7 at $DIR/retag.rs:47:24: 47:25 + StorageLive(_25); // scope 7 at $DIR/retag.rs:50:9: 50:11 + StorageLive(_26); // scope 7 at $DIR/retag.rs:50:14: 50:28 + _26 = &raw const (*_15); // scope 7 at $DIR/retag.rs:50:14: 50:16 + Retag([raw] _26); // scope 7 at $DIR/retag.rs:50:14: 50:16 + _25 = _26; // scope 7 at $DIR/retag.rs:50:14: 50:28 + StorageDead(_26); // scope 7 at $DIR/retag.rs:50:28: 50:29 + _0 = const (); // scope 0 at $DIR/retag.rs:29:11: 51:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:29:11: 51:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_25); // scope 7 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_15); // scope 6 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_13); // scope 1 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_1); // scope 0 at $DIR/retag.rs:51:1: 51:2 + return; // scope 0 at $DIR/retag.rs:51:2: 51:2 + } + + bb6 (cleanup): { + drop(_21) -> bb8; // scope 7 at $DIR/retag.rs:47:24: 47:25 + } + + bb7 (cleanup): { + drop(_5) -> bb8; // scope 1 at $DIR/retag.rs:32:36: 32:37 + } + + bb8 (cleanup): { + resume; // scope 0 at $DIR/retag.rs:29:1: 51:2 + } +} diff --git a/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir b/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir new file mode 100644 index 0000000000000..995c8c141c66f --- /dev/null +++ b/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir @@ -0,0 +1,23 @@ +// MIR for `std::intrinsics::drop_in_place` after SimplifyCfg-make_shim + +fn std::intrinsics::drop_in_place(_1: *mut Test) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _2: &mut Test; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + + bb0: { + Retag([raw] _1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _3 = const ::drop(move _2) -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: for<'r> fn(&'r mut Test) {::drop} + // + val: Value(Scalar()) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut Test) {::drop}, val: Value(Scalar()) } + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } +} diff --git a/src/test/mir-opt/retag/rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag/rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..f9ed3932d3335 --- /dev/null +++ b/src/test/mir-opt/retag/rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,20 @@ +// MIR for `::foo` after SimplifyCfg-elaborate-drops + +fn ::foo(_1: &Test, _2: &mut i32) -> &mut i32 { + debug self => _1; // in scope 0 at $DIR/retag.rs:13:16: 13:21 + debug x => _2; // in scope 0 at $DIR/retag.rs:13:23: 13:24 + let mut _0: &mut i32; // return place in scope 0 at $DIR/retag.rs:13:42: 13:53 + let mut _3: &mut i32; // in scope 0 at $DIR/retag.rs:14:9: 14:10 + + bb0: { + Retag([fn entry] _1); // scope 0 at $DIR/retag.rs:13:5: 15:6 + Retag([fn entry] _2); // scope 0 at $DIR/retag.rs:13:5: 15:6 + StorageLive(_3); // scope 0 at $DIR/retag.rs:14:9: 14:10 + _3 = &mut (*_2); // scope 0 at $DIR/retag.rs:14:9: 14:10 + Retag(_3); // scope 0 at $DIR/retag.rs:14:9: 14:10 + _0 = &mut (*_3); // scope 0 at $DIR/retag.rs:14:9: 14:10 + Retag(_0); // scope 0 at $DIR/retag.rs:14:9: 14:10 + StorageDead(_3); // scope 0 at $DIR/retag.rs:15:5: 15:6 + return; // scope 0 at $DIR/retag.rs:15:6: 15:6 + } +} diff --git a/src/test/mir-opt/retag/rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag/rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..87a8603a931da --- /dev/null +++ b/src/test/mir-opt/retag/rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,15 @@ +// MIR for `::foo_shr` after SimplifyCfg-elaborate-drops + +fn ::foo_shr(_1: &Test, _2: &i32) -> &i32 { + debug self => _1; // in scope 0 at $DIR/retag.rs:16:20: 16:25 + debug x => _2; // in scope 0 at $DIR/retag.rs:16:27: 16:28 + let mut _0: &i32; // return place in scope 0 at $DIR/retag.rs:16:42: 16:49 + + bb0: { + Retag([fn entry] _1); // scope 0 at $DIR/retag.rs:16:5: 18:6 + Retag([fn entry] _2); // scope 0 at $DIR/retag.rs:16:5: 18:6 + _0 = _2; // scope 0 at $DIR/retag.rs:17:9: 17:10 + Retag(_0); // scope 0 at $DIR/retag.rs:17:9: 17:10 + return; // scope 0 at $DIR/retag.rs:18:6: 18:6 + } +} diff --git a/src/test/mir-opt/retain-never-const.rs b/src/test/mir-opt/retain-never-const.rs deleted file mode 100644 index 8e9bae8569f19..0000000000000 --- a/src/test/mir-opt/retain-never-const.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Regression test for #66975 - ensure that we don't keep unevaluated -// `!`-typed constants until codegen. - -// Force generation of optimized mir for functions that do not reach codegen. -// compile-flags: --emit mir,link - -#![feature(const_panic)] -#![feature(never_type)] -#![warn(const_err)] - -struct PrintName(T); - -impl PrintName { - const VOID: ! = panic!(); -} - -fn no_codegen() { - let _ = PrintName::::VOID; -} - -fn main() {} - -// END RUST SOURCE -// START rustc.no_codegen.PreCodegen.after.mir -// bb0: { -// StorageLive(_1); -// _1 = const PrintName::::VOID; -// unreachable; -// } -// END rustc.no_codegen.PreCodegen.after.mir diff --git a/src/test/mir-opt/simple-match.rs b/src/test/mir-opt/simple-match.rs index fc1a3bb1bf453..c8c7e9188c2ba 100644 --- a/src/test/mir-opt/simple-match.rs +++ b/src/test/mir-opt/simple-match.rs @@ -1,5 +1,7 @@ // Test that we don't generate unnecessarily large MIR for very simple matches +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.match_bool.mir_map.0.mir fn match_bool(x: bool) -> usize { match x { true => 10, @@ -8,32 +10,3 @@ fn match_bool(x: bool) -> usize { } fn main() {} - - -// END RUST SOURCE -// START rustc.match_bool.mir_map.0.mir -// bb0: { -// FakeRead(ForMatchedPlace, _1); -// switchInt(_1) -> [false: bb3, otherwise: bb2]; -// } -// bb1 (cleanup): { -// resume; -// } -// bb2: { -// falseEdges -> [real: bb4, imaginary: bb3]; -// } -// bb3: { -// _0 = const 20usize; -// goto -> bb5; -// } -// bb4: { -// _0 = const 10usize; -// goto -> bb5; -// } -// bb5: { -// goto -> bb6; -// } -// bb6: { -// return; -// } -// END rustc.match_bool.mir_map.0.mir diff --git a/src/test/mir-opt/simple-match/32bit/rustc.match_bool.mir_map.0.mir b/src/test/mir-opt/simple-match/32bit/rustc.match_bool.mir_map.0.mir new file mode 100644 index 0000000000000..be4a472e9af50 --- /dev/null +++ b/src/test/mir-opt/simple-match/32bit/rustc.match_bool.mir_map.0.mir @@ -0,0 +1,41 @@ +// MIR for `match_bool` 0 mir_map + +fn match_bool(_1: bool) -> usize { + debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 + let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 + switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb1: { + falseEdges -> [real: bb3, imaginary: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb2: { + _0 = const 20usize; // scope 0 at $DIR/simple-match.rs:8:14: 8:16 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000014)) + // mir::Constant + // + span: $DIR/simple-match.rs:8:14: 8:16 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000014)) } + goto -> bb4; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb3: { + _0 = const 10usize; // scope 0 at $DIR/simple-match.rs:7:17: 7:19 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000a)) + // mir::Constant + // + span: $DIR/simple-match.rs:7:17: 7:19 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000a)) } + goto -> bb4; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb4: { + return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir b/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir new file mode 100644 index 0000000000000..1dde4386ab848 --- /dev/null +++ b/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir @@ -0,0 +1,41 @@ +// MIR for `match_bool` 0 mir_map + +fn match_bool(_1: bool) -> usize { + debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 + let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 + switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb1: { + falseEdges -> [real: bb3, imaginary: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb2: { + _0 = const 20usize; // scope 0 at $DIR/simple-match.rs:8:14: 8:16 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000014)) + // mir::Constant + // + span: $DIR/simple-match.rs:8:14: 8:16 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000014)) } + goto -> bb4; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb3: { + _0 = const 10usize; // scope 0 at $DIR/simple-match.rs:7:17: 7:19 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x000000000000000a)) + // mir::Constant + // + span: $DIR/simple-match.rs:7:17: 7:19 + // + literal: Const { ty: usize, val: Value(Scalar(0x000000000000000a)) } + goto -> bb4; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb4: { + return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/simplify-arm-identity.rs b/src/test/mir-opt/simplify-arm-identity.rs index a8fa64255fb9a..24e91b3ff611c 100644 --- a/src/test/mir-opt/simplify-arm-identity.rs +++ b/src/test/mir-opt/simplify-arm-identity.rs @@ -2,6 +2,7 @@ // Regression test for issue #66856. // // compile-flags: -Zmir-opt-level=2 +// EMIT_MIR_FOR_EACH_BIT_WIDTH enum Src { Foo(u8), @@ -12,6 +13,7 @@ enum Dst { Foo(u8), } +// EMIT_MIR rustc.main.SimplifyArmIdentity.diff fn main() { let e: Src = Src::Foo(0); let _: Dst = match e { @@ -19,57 +21,3 @@ fn main() { Src::Bar => Dst::Foo(0), }; } - -// END RUST SOURCE -// START rustc.main.SimplifyArmIdentity.before.mir -// fn main() -> () { -// ... -// bb0: { -// StorageLive(_1); -// ((_1 as Foo).0: u8) = const 0u8; -// discriminant(_1) = 0; -// StorageLive(_2); -// _3 = discriminant(_1); -// switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; -// } -// bb1: { -// ((_2 as Foo).0: u8) = const 0u8; -// discriminant(_2) = 0; -// goto -> bb4; -// } -// ... -// bb3: { -// _4 = ((_1 as Foo).0: u8); -// ((_2 as Foo).0: u8) = move _4; -// discriminant(_2) = 0; -// goto -> bb4; -// } -// ... -// } -// END rustc.main.SimplifyArmIdentity.before.mir -// START rustc.main.SimplifyArmIdentity.after.mir -// fn main() -> () { -// ... -// bb0: { -// StorageLive(_1); -// ((_1 as Foo).0: u8) = const 0u8; -// discriminant(_1) = 0; -// StorageLive(_2); -// _3 = discriminant(_1); -// switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; -// } -// bb1: { -// ((_2 as Foo).0: u8) = const 0u8; -// discriminant(_2) = 0; -// goto -> bb4; -// } -// ... -// bb3: { -// _4 = ((_1 as Foo).0: u8); -// ((_2 as Foo).0: u8) = move _4; -// discriminant(_2) = 0; -// goto -> bb4; -// } -// ... -// } -// END rustc.main.SimplifyArmIdentity.after.mir diff --git a/src/test/mir-opt/simplify-arm-identity/32bit/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm-identity/32bit/rustc.main.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..6199e2c56625d --- /dev/null +++ b/src/test/mir-opt/simplify-arm-identity/32bit/rustc.main.SimplifyArmIdentity.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 + let _1: Src as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + ((_1 as Foo).0: u8) = const 0u8; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:18:27: 18:28 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + _3 = const 0isize; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:20:9: 20:20 + // + literal: Const { ty: isize, val: Value(Scalar(0x00000000)) } + goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + } + + bb1: { + ((_2 as Foo).0: u8) = const 0u8; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:21:30: 21:31 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + discriminant(_2) = 0; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:35: 20:36 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb4: { + StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:22:6: 22:7 + _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:17:11: 23:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:17:11: 23:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:23:1: 23:2 + return; // scope 0 at $DIR/simplify-arm-identity.rs:23:2: 23:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm-identity/64bit/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm-identity/64bit/rustc.main.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..bf875c6a555fe --- /dev/null +++ b/src/test/mir-opt/simplify-arm-identity/64bit/rustc.main.SimplifyArmIdentity.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 + let _1: Src as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + ((_1 as Foo).0: u8) = const 0u8; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:18:27: 18:28 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + _3 = const 0isize; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:20:9: 20:20 + // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000000)) } + goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + } + + bb1: { + ((_2 as Foo).0: u8) = const 0u8; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:21:30: 21:31 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + discriminant(_2) = 0; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:35: 20:36 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb4: { + StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:22:6: 22:7 + _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:17:11: 23:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:17:11: 23:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:23:1: 23:2 + return; // scope 0 at $DIR/simplify-arm-identity.rs:23:2: 23:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm-identity/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm-identity/rustc.main.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..b2517cb7012b4 --- /dev/null +++ b/src/test/mir-opt/simplify-arm-identity/rustc.main.SimplifyArmIdentity.diff @@ -0,0 +1,71 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:16:11: 16:11 + let _1: Src as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/simplify-arm-identity.rs:17:9: 17:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:19:9: 19:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:19:33: 19:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:17:9: 17:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:19:18: 19:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:19:18: 19:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:17:9: 17:10 + ((_1 as Foo).0: u8) = const 0u8; // scope 0 at $DIR/simplify-arm-identity.rs:17:18: 17:29 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:17:27: 17:28 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:17:18: 17:29 + StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + _3 = discriminant(_1); // scope 1 at $DIR/simplify-arm-identity.rs:19:9: 19:20 + switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // scope 1 at $DIR/simplify-arm-identity.rs:19:9: 19:20 + } + + bb1: { + ((_2 as Foo).0: u8) = const 0u8; // scope 1 at $DIR/simplify-arm-identity.rs:20:21: 20:32 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:20:30: 20:31 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + discriminant(_2) = 0; // scope 1 at $DIR/simplify-arm-identity.rs:20:21: 20:32 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:18:24: 18:25 + } + + bb3: { + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 19:19 + ((_2 as Foo).0: u8) = move _4; // scope 3 at $DIR/simplify-arm-identity.rs:19:24: 19:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:19:24: 19:35 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + } + + bb4: { + StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:21:6: 21:7 + _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:16:11: 22:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:16:11: 22:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:22:1: 22:2 + return; // scope 0 at $DIR/simplify-arm-identity.rs:22:2: 22:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm.rs b/src/test/mir-opt/simplify-arm.rs new file mode 100644 index 0000000000000..0e3f86501bb44 --- /dev/null +++ b/src/test/mir-opt/simplify-arm.rs @@ -0,0 +1,32 @@ +// compile-flags: -Z mir-opt-level=1 +// EMIT_MIR rustc.id.SimplifyArmIdentity.diff +// EMIT_MIR rustc.id.SimplifyBranchSame.diff +// EMIT_MIR rustc.id_result.SimplifyArmIdentity.diff +// EMIT_MIR rustc.id_result.SimplifyBranchSame.diff +// EMIT_MIR rustc.id_try.SimplifyArmIdentity.diff +// EMIT_MIR rustc.id_try.SimplifyBranchSame.diff + +fn id(o: Option) -> Option { + match o { + Some(v) => Some(v), + None => None, + } +} + +fn id_result(r: Result) -> Result { + match r { + Ok(x) => Ok(x), + Err(y) => Err(y), + } +} + +fn id_try(r: Result) -> Result { + let x = r?; + Ok(x) +} + +fn main() { + id(None); + id_result(Ok(4)); + id_try(Ok(4)); +} diff --git a/src/test/mir-opt/simplify-arm/rustc.id.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..8d08267d75bfd --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyArmIdentity.diff @@ -0,0 +1,45 @@ +- // MIR for `id` before SimplifyArmIdentity ++ // MIR for `id` after SimplifyArmIdentity + + fn id(_1: std::option::Option) -> std::option::Option { + debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:9:7: 9:8 + let mut _0: std::option::Option; // return place in scope 0 at $DIR/simplify-arm.rs:9:25: 9:35 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:11:25: 11:26 + scope 1 { + debug v => _3; // in scope 1 at $DIR/simplify-arm.rs:11:14: 11:15 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + switchInt(move _2) -> [0isize: bb1, 1isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + } + + bb1: { + discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12 + } + + bb3: { +- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 +- _3 = ((_1 as Some).0: u8); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 +- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 +- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 +- ((_0 as Some).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 +- discriminant(_0) = 1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 +- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:11:26: 11:27 +- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:11:27: 11:28 ++ _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 + } + + bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:14:2: 14:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm/rustc.id.SimplifyBranchSame.diff b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyBranchSame.diff new file mode 100644 index 0000000000000..23fa9817f80f6 --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyBranchSame.diff @@ -0,0 +1,37 @@ +- // MIR for `id` before SimplifyBranchSame ++ // MIR for `id` after SimplifyBranchSame + + fn id(_1: std::option::Option) -> std::option::Option { + debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:9:7: 9:8 + let mut _0: std::option::Option; // return place in scope 0 at $DIR/simplify-arm.rs:9:25: 9:35 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:11:25: 11:26 + scope 1 { + debug v => _3; // in scope 1 at $DIR/simplify-arm.rs:11:14: 11:15 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + switchInt(move _2) -> [0isize: bb1, 1isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + } + + bb1: { + discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12 + } + + bb3: { + _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 + } + + bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:14:2: 14:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..e2a12ca5be26c --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyArmIdentity.diff @@ -0,0 +1,58 @@ +- // MIR for `id_result` before SimplifyArmIdentity ++ // MIR for `id_result` after SimplifyArmIdentity + + fn id_result(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:16:14: 16:15 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:16:37: 16:52 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:18:21: 18:22 + let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 + let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:19:23: 19:24 + scope 1 { + debug x => _3; // in scope 1 at $DIR/simplify-arm.rs:18:12: 18:13 + } + scope 2 { + debug y => _5; // in scope 2 at $DIR/simplify-arm.rs:19:13: 19:14 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + switchInt(move _2) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + } + + bb1: { +- StorageLive(_5); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 +- _5 = ((_1 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 +- StorageLive(_6); // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 +- _6 = _5; // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 +- ((_0 as Err).0: i32) = move _6; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 +- discriminant(_0) = 1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 +- StorageDead(_6); // scope 2 at $DIR/simplify-arm.rs:19:24: 19:25 +- StorageDead(_5); // scope 0 at $DIR/simplify-arm.rs:19:25: 19:26 ++ _0 = move _1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:17:11: 17:12 + } + + bb3: { +- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 +- _3 = ((_1 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 +- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 +- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 +- ((_0 as Ok).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 +- discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 +- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:18:22: 18:23 +- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:18:23: 18:24 ++ _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 + } + + bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:21:2: 21:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyBranchSame.diff b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyBranchSame.diff new file mode 100644 index 0000000000000..9d1ff22dc5109 --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyBranchSame.diff @@ -0,0 +1,45 @@ +- // MIR for `id_result` before SimplifyBranchSame ++ // MIR for `id_result` after SimplifyBranchSame + + fn id_result(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:16:14: 16:15 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:16:37: 16:52 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:18:21: 18:22 + let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 + let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:19:23: 19:24 + scope 1 { + debug x => _3; // in scope 1 at $DIR/simplify-arm.rs:18:12: 18:13 + } + scope 2 { + debug y => _5; // in scope 2 at $DIR/simplify-arm.rs:19:13: 19:14 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 +- switchInt(move _2) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 ++ goto -> bb1; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + } + + bb1: { +- _0 = move _1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 +- } +- +- bb2: { +- unreachable; // scope 0 at $DIR/simplify-arm.rs:17:11: 17:12 +- } +- +- bb3: { + _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 ++ goto -> bb2; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 + } + +- bb4: { ++ bb2: { + return; // scope 0 at $DIR/simplify-arm.rs:21:2: 21:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..ba6e1ac24cb4e --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff @@ -0,0 +1,109 @@ +- // MIR for `id_try` before SimplifyArmIdentity ++ // MIR for `id_try` after SimplifyArmIdentity + + fn id_try(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:23:11: 23:12 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:23:34: 23:49 + let _2: u8; // in scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + let mut _5: isize; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _6: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _7: !; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _8: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _9: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _10: u8; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _11: u8; // in scope 0 at $DIR/simplify-arm.rs:25:8: 25:9 + scope 1 { + debug x => _2; // in scope 1 at $DIR/simplify-arm.rs:24:9: 24:10 + } + scope 2 { + debug err => _6; // in scope 2 at $DIR/simplify-arm.rs:24:14: 24:15 + scope 3 { + } + } + scope 4 { + debug val => _10; // in scope 4 at $DIR/simplify-arm.rs:24:13: 24:15 + scope 5 { + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + StorageLive(_4); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _4 = _1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _3 = const as std::ops::Try>::into_result(move _4) -> bb1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + // ty::Const + // + ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:13: 24:15 + // + literal: Const { ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_4); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + switchInt(move _5) -> [0isize: bb2, 1isize: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + } + + bb2: { +- StorageLive(_10); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 +- _10 = ((_3 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 +- _2 = _10; // scope 5 at $DIR/simplify-arm.rs:24:13: 24:15 +- StorageDead(_10); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 ++ _0 = move _3; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 +- StorageLive(_11); // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 +- _11 = _2; // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 +- ((_0 as Ok).0: u8) = move _11; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 +- discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 +- StorageDead(_11); // scope 1 at $DIR/simplify-arm.rs:25:9: 25:10 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb7; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb3: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + } + + bb4: { + StorageLive(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _9 = _6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _8 = const >::from(move _9) -> bb5; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn(i32) -> i32 {>::from} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:14: 24:15 + // + literal: Const { ty: fn(i32) -> i32 {>::from}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _0 = const as std::ops::Try>::from_error(move _8) -> bb6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:13: 24:15 + // + literal: Const { ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb7; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb7: { + return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff new file mode 100644 index 0000000000000..4061c5e74ac61 --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff @@ -0,0 +1,100 @@ +- // MIR for `id_try` before SimplifyBranchSame ++ // MIR for `id_try` after SimplifyBranchSame + + fn id_try(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:23:11: 23:12 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:23:34: 23:49 + let _2: u8; // in scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + let mut _5: isize; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _6: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _7: !; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _8: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _9: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _10: u8; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _11: u8; // in scope 0 at $DIR/simplify-arm.rs:25:8: 25:9 + scope 1 { + debug x => _2; // in scope 1 at $DIR/simplify-arm.rs:24:9: 24:10 + } + scope 2 { + debug err => _6; // in scope 2 at $DIR/simplify-arm.rs:24:14: 24:15 + scope 3 { + } + } + scope 4 { + debug val => _10; // in scope 4 at $DIR/simplify-arm.rs:24:13: 24:15 + scope 5 { + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + StorageLive(_4); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _4 = _1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _3 = const as std::ops::Try>::into_result(move _4) -> bb1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + // ty::Const + // + ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:13: 24:15 + // + literal: Const { ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_4); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + switchInt(move _5) -> [0isize: bb2, 1isize: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + } + + bb2: { + _0 = move _3; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb7; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb3: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + } + + bb4: { + StorageLive(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _9 = _6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _8 = const >::from(move _9) -> bb5; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn(i32) -> i32 {>::from} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:14: 24:15 + // + literal: Const { ty: fn(i32) -> i32 {>::from}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _0 = const as std::ops::Try>::from_error(move _8) -> bb6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:13: 24:15 + // + literal: Const { ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb7; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb7: { + return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + } + diff --git a/src/test/mir-opt/simplify-locals-fixedpoint.rs b/src/test/mir-opt/simplify-locals-fixedpoint.rs new file mode 100644 index 0000000000000..aa5bc345359eb --- /dev/null +++ b/src/test/mir-opt/simplify-locals-fixedpoint.rs @@ -0,0 +1,15 @@ +// compile-flags: -Zmir-opt-level=1 + +fn foo() { + if let (Some(a), None) = (Option::::None, Option::::None) { + if a > 42u8 { + + } + } +} + +fn main() { + foo::<()>(); +} + +// EMIT_MIR rustc.foo.SimplifyLocals.diff diff --git a/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff new file mode 100644 index 0000000000000..f7db14e716526 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff @@ -0,0 +1,102 @@ +- // MIR for `foo` before SimplifyLocals ++ // MIR for `foo` after SimplifyLocals + + fn foo() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-locals-fixedpoint.rs:3:13: 3:13 + let mut _1: (std::option::Option, std::option::Option); // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + let mut _2: std::option::Option; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 + let mut _3: std::option::Option; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 + let mut _4: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 + let mut _5: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 + let _6: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + let mut _7: bool; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 + let mut _8: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 + scope 1 { + debug a => _6; // in scope 1 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + StorageLive(_2); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 + discriminant(_2) = 0; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 + StorageLive(_3); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 + discriminant(_3) = 0; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 + (_1.0: std::option::Option) = move _2; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + (_1.1: std::option::Option) = move _3; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + StorageDead(_3); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 + StorageDead(_2); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 + _5 = discriminant((_1.0: std::option::Option)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 + switchInt(move _5) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 + } + + bb2: { + _4 = discriminant((_1.1: std::option::Option)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 + switchInt(move _4) -> [0isize: bb3, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 + } + + bb3: { + StorageLive(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + _6 = (((_1.0: std::option::Option) as Some).0: u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + StorageLive(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 + StorageLive(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 + _8 = _6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 + _7 = Gt(move _8, const 42u8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x2a)) + // mir::Constant + // + span: $DIR/simplify-locals-fixedpoint.rs:5:16: 5:20 + // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } + StorageDead(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20 + switchInt(_7) -> [false: bb4, otherwise: bb5]; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + } + + bb4: { + _0 = const (); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + } + + bb5: { + _0 = const (); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + } + + bb6: { + StorageDead(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6 + StorageDead(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6 + goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 + } + + bb7: { + drop(_1) -> bb8; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 + } + + bb8: { + StorageDead(_1); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 + return; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs index e427fd55ad680..48cee3c30d2da 100644 --- a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs +++ b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs @@ -8,82 +8,10 @@ struct Temp { fn use_u8(_: u8) {} +// EMIT_MIR rustc.main.SimplifyLocals.diff fn main() { let ((), ()) = ((), ()); use_zst(((), ())); use_u8((Temp { x: 40 }).x + 2); } - -// END RUST SOURCE - -// START rustc.main.SimplifyLocals.before.mir -// let mut _0: (); -// let mut _1: ((), ()); -// let mut _2: (); -// let mut _3: (); -// let _4: (); -// let mut _5: ((), ()); -// let mut _6: (); -// let mut _7: (); -// let _8: (); -// let mut _9: u8; -// let mut _10: u8; -// let mut _11: Temp; -// scope 1 { -// } -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = const (); -// StorageLive(_3); -// _3 = const (); -// _1 = const {transmute(()): ((), ())}; -// StorageDead(_3); -// StorageDead(_2); -// StorageDead(_1); -// StorageLive(_4); -// StorageLive(_6); -// _6 = const (); -// StorageLive(_7); -// _7 = const (); -// StorageDead(_7); -// StorageDead(_6); -// _4 = const use_zst(const {transmute(()): ((), ())}) -> bb1; -// } -// bb1: { -// StorageDead(_4); -// StorageLive(_8); -// StorageLive(_10); -// StorageLive(_11); -// _11 = const {transmute(0x28) : Temp}; -// _10 = const 40u8; -// StorageDead(_10); -// _8 = const use_u8(const 42u8) -> bb2; -// } -// bb2: { -// StorageDead(_11); -// StorageDead(_8); -// return; -// } -// END rustc.main.SimplifyLocals.before.mir -// START rustc.main.SimplifyLocals.after.mir -// let mut _0: (); -// let _1: (); -// let _2: (); -// scope 1 { -// } -// bb0: { -// StorageLive(_1); -// _1 = const use_zst(const {transmute(()): ((), ())}) -> bb1; -// } -// bb1: { -// StorageDead(_1); -// StorageLive(_2); -// _2 = const use_u8(const 42u8) -> bb2; -// } -// bb2: { -// StorageDead(_2); -// return; -// } -// END rustc.main.SimplifyLocals.after.mir diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts/rustc.main.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-removes-unused-consts/rustc.main.SimplifyLocals.diff new file mode 100644 index 0000000000000..0bd4ba97b3ca0 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-consts/rustc.main.SimplifyLocals.diff @@ -0,0 +1,156 @@ +- // MIR for `main` before SimplifyLocals ++ // MIR for `main` after SimplifyLocals + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:12:11: 12:11 +- let mut _1: ((), ()); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- let mut _2: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 +- let mut _3: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 +- let _4: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 +- let mut _5: ((), ()); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- let mut _6: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 +- let mut _7: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 +- let _8: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 +- let mut _9: u8; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 +- let mut _10: u8; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 +- let mut _11: Temp; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 ++ let _1: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 ++ let _2: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 + scope 1 { + } + + bb0: { +- StorageLive(_1); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- StorageLive(_2); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 +- _2 = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 ++ StorageLive(_1); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 ++ _1 = const use_zst(const ((), ())) -> bb1; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 + // ty::Const +- // + ty: () +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 +- // + literal: Const { ty: (), val: Value(Scalar()) } +- StorageLive(_3); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 +- _3 = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 +- // ty::Const +- // + ty: () +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 +- // + literal: Const { ty: (), val: Value(Scalar()) } +- _1 = const ((), ()); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- // ty::Const +- // + ty: ((), ()) +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- // + literal: Const { ty: ((), ()), val: Value(Scalar()) } +- StorageDead(_3); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:27: 13:28 +- StorageDead(_2); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:27: 13:28 +- StorageDead(_1); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:28: 13:29 +- StorageLive(_4); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 +- StorageLive(_5); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- StorageLive(_6); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 +- _6 = const (); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 +- // ty::Const +- // + ty: () +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 +- // + literal: Const { ty: (), val: Value(Scalar()) } +- StorageLive(_7); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 +- _7 = const (); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 +- // ty::Const +- // + ty: () +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 +- // + literal: Const { ty: (), val: Value(Scalar()) } +- _5 = const ((), ()); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- // ty::Const +- // + ty: ((), ()) +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- // + literal: Const { ty: ((), ()), val: Value(Scalar()) } +- StorageDead(_7); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:20: 14:21 +- StorageDead(_6); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:20: 14:21 +- _4 = const use_zst(const ((), ())) -> bb1; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 +- // ty::Const + // + ty: fn(((), ())) {use_zst} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:12 + // + literal: Const { ty: fn(((), ())) {use_zst}, val: Value(Scalar()) } + // ty::Const + // + ty: ((), ()) + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 + // + literal: Const { ty: ((), ()), val: Value(Scalar()) } + } + + bb1: { +- StorageDead(_5); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:21: 14:22 +- StorageDead(_4); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:22: 14:23 +- StorageLive(_8); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 +- StorageLive(_9); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 +- StorageLive(_10); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 +- StorageLive(_11); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 +- _11 = const Temp { x: 40u8 }; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 ++ StorageDead(_1); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:22: 14:23 ++ StorageLive(_2); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 ++ _2 = const use_u8(const 42u8) -> bb2; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 + // ty::Const +- // + ty: Temp +- // + val: Value(Scalar(0x28)) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 +- // + literal: Const { ty: Temp, val: Value(Scalar(0x28)) } +- _10 = const 40u8; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 +- // ty::Const +- // + ty: u8 +- // + val: Value(Scalar(0x28)) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 +- // + literal: Const { ty: u8, val: Value(Scalar(0x28)) } +- _9 = const 42u8; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 +- // ty::Const +- // + ty: u8 +- // + val: Value(Scalar(0x2a)) +- // mir::Constant +- // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 +- // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } +- StorageDead(_10); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:33: 16:34 +- _8 = const use_u8(const 42u8) -> bb2; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 +- // ty::Const + // + ty: fn(u8) {use_u8} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:11 + // + literal: Const { ty: fn(u8) {use_u8}, val: Value(Scalar()) } + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x2a)) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 + // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } + } + + bb2: { +- StorageDead(_9); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:34: 16:35 +- StorageDead(_11); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 +- StorageDead(_8); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 ++ StorageDead(_2); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 + _0 = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:12:11: 17:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:12:11: 17:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:17:2: 17:2 + } + } + diff --git a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs new file mode 100644 index 0000000000000..067fa879b4038 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs @@ -0,0 +1,12 @@ +fn map(x: Option>) -> Option> { + match x { + None => None, + Some(x) => Some(x), + } +} + +fn main() { + map(None); +} + +// EMIT_MIR rustc.map.SimplifyLocals.diff diff --git a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/rustc.map.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/rustc.map.SimplifyLocals.diff new file mode 100644 index 0000000000000..0ca54af85e3b6 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/rustc.map.SimplifyLocals.diff @@ -0,0 +1,36 @@ +- // MIR for `map` before SimplifyLocals ++ // MIR for `map` after SimplifyLocals + + fn map(_1: std::option::Option>) -> std::option::Option> { + debug x => _1; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:8: 1:9 + let mut _0: std::option::Option>; // return place in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:31: 1:46 + let mut _2: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 + let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 +- let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:25: 4:26 +- let mut _5: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 +- let mut _6: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 + scope 1 { + debug x => _3; // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 + switchInt(move _2) -> [0isize: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 + } + + bb1: { + _0 = move _1; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:20: 4:27 + goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 + } + + bb2: { + discriminant(_0) = 0; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 + goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 + } + + bb3: { +- _5 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 + return; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/simplify_cfg.rs b/src/test/mir-opt/simplify_cfg.rs index ef843f7158130..8d588a39d651b 100644 --- a/src/test/mir-opt/simplify_cfg.rs +++ b/src/test/mir-opt/simplify_cfg.rs @@ -1,5 +1,7 @@ // Test that the goto chain starting from bb0 is collapsed. +// EMIT_MIR rustc.main.SimplifyCfg-initial.diff +// EMIT_MIR rustc.main.SimplifyCfg-early-opt.diff fn main() { loop { if bar() { @@ -12,43 +14,3 @@ fn main() { fn bar() -> bool { true } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-initial.before.mir -// bb0: { -// goto -> bb1; -// } -// bb1: { -// falseUnwind -> [real: bb3, cleanup: bb4]; -// } -// ... -// bb11: { -// ... -// goto -> bb1; -// } -// END rustc.main.SimplifyCfg-initial.before.mir -// START rustc.main.SimplifyCfg-initial.after.mir -// bb0: { -// falseUnwind -> [real: bb1, cleanup: bb2]; -// } -// ... -// bb5: { -// ... -// goto -> bb0; -// } -// END rustc.main.SimplifyCfg-initial.after.mir -// START rustc.main.SimplifyCfg-early-opt.before.mir -// bb0: { -// goto -> bb1; -// } -// bb1: { -// StorageLive(_2); -// _2 = const bar() -> bb3; -// } -// END rustc.main.SimplifyCfg-early-opt.before.mir -// START rustc.main.SimplifyCfg-early-opt.after.mir -// bb0: { -// StorageLive(_2); -// _2 = const bar() -> bb1; -// } -// END rustc.main.SimplifyCfg-early-opt.after.mir diff --git a/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-early-opt.diff b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-early-opt.diff new file mode 100644 index 0000000000000..803635bd344fe --- /dev/null +++ b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-early-opt.diff @@ -0,0 +1,67 @@ +- // MIR for `main` before SimplifyCfg-early-opt ++ // MIR for `main` after SimplifyCfg-early-opt + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 + + bb0: { +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb1: { + StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- _2 = const bar() -> bb2; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 ++ _2 = const bar() -> bb1; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:12: 7:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + +- bb2: { +- nop; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ bb1: { ++ switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb3: { +- goto -> bb5; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb4: { ++ bb2: { + _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:9: 9:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 + goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + +- bb5: { ++ bb3: { + _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:8:13: 8:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 + return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 +- } +- +- bb6 (cleanup): { +- resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + } + } + diff --git a/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff new file mode 100644 index 0000000000000..b19b91653db5f --- /dev/null +++ b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff @@ -0,0 +1,93 @@ +- // MIR for `main` before SimplifyCfg-initial ++ // MIR for `main` after SimplifyCfg-initial + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 + + bb0: { +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 ++ falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + + bb1: { +- falseUnwind -> [real: bb2, cleanup: bb11]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb2: { + StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- _2 = const bar() -> [return: bb3, unwind: bb11]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 ++ _2 = const bar() -> [return: bb2, unwind: bb6]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:12: 7:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + +- bb3: { ++ bb2: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb4: { +- falseEdges -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ bb3: { ++ falseEdges -> [real: bb5, imaginary: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb5: { ++ bb4: { + _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:9: 9:10 + // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 ++ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + +- bb6: { ++ bb5: { + _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:8:13: 8:18 + // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb10; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 +- } +- +- bb7: { +- unreachable; // scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 +- } +- +- bb8: { +- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb9: { + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb10: { +- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 + return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 + } + +- bb11 (cleanup): { ++ bb6 (cleanup): { + resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + } + } + diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index 471c1df3300f0..e2d3ebe69c4a4 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -1,19 +1,9 @@ +#[inline(never)] +fn noop() {} + +// EMIT_MIR rustc.main.SimplifyBranches-after-const-prop.diff fn main() { if false { - println!("hello world!"); + noop(); } } - -// END RUST SOURCE -// START rustc.main.SimplifyBranches-after-const-prop.before.mir -// bb0: { -// ... -// switchInt(const false) -> [false: bb1, otherwise: bb2]; -// } -// END rustc.main.SimplifyBranches-after-const-prop.before.mir -// START rustc.main.SimplifyBranches-after-const-prop.after.mir -// bb0: { -// ... -// goto -> bb1; -// } -// END rustc.main.SimplifyBranches-after-const-prop.after.mir diff --git a/src/test/mir-opt/simplify_if/rustc.main.SimplifyBranches-after-const-prop.diff b/src/test/mir-opt/simplify_if/rustc.main.SimplifyBranches-after-const-prop.diff new file mode 100644 index 0000000000000..e94e49bf0cb4f --- /dev/null +++ b/src/test/mir-opt/simplify_if/rustc.main.SimplifyBranches-after-const-prop.diff @@ -0,0 +1,67 @@ +- // MIR for `main` before SimplifyBranches-after-const-prop ++ // MIR for `main` after SimplifyBranches-after-const-prop + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_if.rs:5:11: 5:11 + let mut _1: bool; // in scope 0 at $DIR/simplify_if.rs:6:8: 6:13 + let _2: (); // in scope 0 at $DIR/simplify_if.rs:7:9: 7:15 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify_if.rs:6:8: 6:13 + _1 = const false; // scope 0 at $DIR/simplify_if.rs:6:8: 6:13 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify_if.rs:6:8: 6:13 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } +- switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 +- // ty::Const +- // + ty: bool +- // + val: Value(Scalar(0x00)) +- // mir::Constant +- // + span: $DIR/simplify_if.rs:6:5: 8:6 +- // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ goto -> bb1; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_if.rs:6:5: 8:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb4; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + } + + bb2: { + StorageLive(_2); // scope 0 at $DIR/simplify_if.rs:7:9: 7:15 + _2 = const noop() -> bb3; // scope 0 at $DIR/simplify_if.rs:7:9: 7:15 + // ty::Const + // + ty: fn() {noop} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_if.rs:7:9: 7:13 + // + literal: Const { ty: fn() {noop}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/simplify_if.rs:7:15: 7:16 + _0 = const (); // scope 0 at $DIR/simplify_if.rs:6:14: 8:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_if.rs:6:14: 8:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb4; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + } + + bb4: { + StorageDead(_1); // scope 0 at $DIR/simplify_if.rs:9:1: 9:2 + return; // scope 0 at $DIR/simplify_if.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/simplify_match.rs b/src/test/mir-opt/simplify_match.rs index 8624899a0abf2..b8e1ea6f981fa 100644 --- a/src/test/mir-opt/simplify_match.rs +++ b/src/test/mir-opt/simplify_match.rs @@ -1,22 +1,10 @@ +#[inline(never)] +fn noop() {} + +// EMIT_MIR rustc.main.ConstProp.diff fn main() { match { let x = false; x } { - true => println!("hello world!"), + true => noop(), false => {}, } } - -// END RUST SOURCE -// START rustc.main.SimplifyBranches-after-copy-prop.before.mir -// bb0: { -// ... -// switchInt(const false) -> [false: bb1, otherwise: bb2]; -// } -// bb1: { -// END rustc.main.SimplifyBranches-after-copy-prop.before.mir -// START rustc.main.SimplifyBranches-after-copy-prop.after.mir -// bb0: { -// ... -// goto -> bb1; -// } -// bb1: { -// END rustc.main.SimplifyBranches-after-copy-prop.after.mir diff --git a/src/test/mir-opt/simplify_match/rustc.main.ConstProp.diff b/src/test/mir-opt/simplify_match/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..8003112c46c4b --- /dev/null +++ b/src/test/mir-opt/simplify_match/rustc.main.ConstProp.diff @@ -0,0 +1,67 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_match.rs:5:11: 5:11 + let mut _1: bool; // in scope 0 at $DIR/simplify_match.rs:6:11: 6:31 + let _2: bool; // in scope 0 at $DIR/simplify_match.rs:6:17: 6:18 + scope 1 { + debug x => _2; // in scope 1 at $DIR/simplify_match.rs:6:17: 6:18 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify_match.rs:6:11: 6:31 + StorageLive(_2); // scope 0 at $DIR/simplify_match.rs:6:17: 6:18 + _2 = const false; // scope 0 at $DIR/simplify_match.rs:6:21: 6:26 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify_match.rs:6:21: 6:26 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } +- _1 = _2; // scope 1 at $DIR/simplify_match.rs:6:28: 6:29 ++ _1 = const false; // scope 1 at $DIR/simplify_match.rs:6:28: 6:29 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/simplify_match.rs:6:28: 6:29 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_2); // scope 0 at $DIR/simplify_match.rs:6:30: 6:31 +- switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:7:9: 7:13 ++ switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:7:9: 7:13 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/simplify_match.rs:7:9: 7:13 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + } + + bb1: { + _0 = const (); // scope 0 at $DIR/simplify_match.rs:8:18: 8:20 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_match.rs:8:18: 8:20 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb3; // scope 0 at $DIR/simplify_match.rs:6:5: 9:6 + } + + bb2: { + _0 = const noop() -> bb3; // scope 0 at $DIR/simplify_match.rs:7:17: 7:23 + // ty::Const + // + ty: fn() {noop} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_match.rs:7:17: 7:21 + // + literal: Const { ty: fn() {noop}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_1); // scope 0 at $DIR/simplify_match.rs:10:1: 10:2 + return; // scope 0 at $DIR/simplify_match.rs:10:2: 10:2 + } + } + diff --git a/src/test/mir-opt/simplify_try.rs b/src/test/mir-opt/simplify_try.rs index abac66d95c548..88a0451a76f67 100644 --- a/src/test/mir-opt/simplify_try.rs +++ b/src/test/mir-opt/simplify_try.rs @@ -1,3 +1,7 @@ +// EMIT_MIR rustc.try_identity.SimplifyArmIdentity.diff +// EMIT_MIR rustc.try_identity.SimplifyBranchSame.after.mir +// EMIT_MIR rustc.try_identity.SimplifyLocals.after.mir + fn try_identity(x: Result) -> Result { let y = x?; Ok(y) @@ -6,212 +10,3 @@ fn try_identity(x: Result) -> Result { fn main() { let _ = try_identity(Ok(0)); } - -// END RUST SOURCE -// START rustc.try_identity.SimplifyArmIdentity.before.mir -// fn try_identity(_1: std::result::Result) -> std::result::Result { -// debug x => _1; -// let mut _0: std::result::Result; -// let _2: u32; -// let mut _3: std::result::Result; -// let mut _4: std::result::Result; -// let mut _5: isize; -// let _6: i32; -// let mut _7: !; -// let mut _8: i32; -// let mut _9: i32; -// let _10: u32; -// let mut _11: u32; -// scope 1 { -// debug y => _10; -// } -// scope 2 { -// debug err => _6; -// scope 3 { -// scope 7 { -// debug t => _6; -// } -// scope 8 { -// debug v => _6; -// let mut _12: i32; -// } -// } -// } -// scope 4 { -// debug val => _10; -// scope 5 { -// } -// } -// scope 6 { -// debug self => _1; -// } -// bb0: { -// _5 = discriminant(_1); -// switchInt(move _5) -> [0isize: bb1, otherwise: bb2]; -// } -// bb1: { -// _10 = ((_1 as Ok).0: u32); -// ((_0 as Ok).0: u32) = move _10; -// discriminant(_0) = 0; -// goto -> bb3; -// } -// bb2: { -// _6 = ((_1 as Err).0: i32); -// ((_0 as Err).0: i32) = move _6; -// discriminant(_0) = 1; -// goto -> bb3; -// } -// bb3: { -// return; -// } -// } -// END rustc.try_identity.SimplifyArmIdentity.before.mir - -// START rustc.try_identity.SimplifyArmIdentity.after.mir -// fn try_identity(_1: std::result::Result) -> std::result::Result { -// debug x => _1; -// let mut _0: std::result::Result; -// let _2: u32; -// let mut _3: std::result::Result; -// let mut _4: std::result::Result; -// let mut _5: isize; -// let _6: i32; -// let mut _7: !; -// let mut _8: i32; -// let mut _9: i32; -// let _10: u32; -// let mut _11: u32; -// scope 1 { -// debug y => _10; -// } -// scope 2 { -// debug err => _6; -// scope 3 { -// scope 7 { -// debug t => _6; -// } -// scope 8 { -// debug v => _6; -// let mut _12: i32; -// } -// } -// } -// scope 4 { -// debug val => _10; -// scope 5 { -// } -// } -// scope 6 { -// debug self => _1; -// } -// bb0: { -// _5 = discriminant(_1); -// switchInt(move _5) -> [0isize: bb1, otherwise: bb2]; -// } -// bb1: { -// _0 = move _1; -// nop; -// nop; -// goto -> bb3; -// } -// bb2: { -// _0 = move _1; -// nop; -// nop; -// goto -> bb3; -// } -// bb3: { -// return; -// } -// } -// END rustc.try_identity.SimplifyArmIdentity.after.mir - -// START rustc.try_identity.SimplifyBranchSame.after.mir -// fn try_identity(_1: std::result::Result) -> std::result::Result { -// debug x => _1; -// let mut _0: std::result::Result; -// let _2: u32; -// let mut _3: std::result::Result; -// let mut _4: std::result::Result; -// let mut _5: isize; -// let _6: i32; -// let mut _7: !; -// let mut _8: i32; -// let mut _9: i32; -// let _10: u32; -// let mut _11: u32; -// scope 1 { -// debug y => _10; -// } -// scope 2 { -// debug err => _6; -// scope 3 { -// scope 7 { -// debug t => _6; -// } -// scope 8 { -// debug v => _6; -// let mut _12: i32; -// } -// } -// } -// scope 4 { -// debug val => _10; -// scope 5 { -// } -// } -// scope 6 { -// debug self => _1; -// } -// bb0: { -// _5 = discriminant(_1); -// goto -> bb1; -// } -// bb1: { -// _0 = move _1; -// nop; -// nop; -// goto -> bb2; -// } -// bb2: { -// return; -// } -// } -// END rustc.try_identity.SimplifyBranchSame.after.mir - -// START rustc.try_identity.SimplifyLocals.after.mir -// fn try_identity(_1: std::result::Result) -> std::result::Result { -// debug x => _1; -// let mut _0: std::result::Result; -// let mut _2: isize; -// let _3: i32; -// let _4: u32; -// scope 1 { -// debug y => _4; -// } -// scope 2 { -// debug err => _3; -// scope 3 { -// scope 7 { -// debug t => _3; -// } -// scope 8 { -// debug v => _3; -// } -// } -// } -// scope 4 { -// debug val => _4; -// scope 5 { -// } -// } -// scope 6 { -// debug self => _1; -// } -// bb0: { -// _2 = discriminant(_1); -// _0 = move _1; -// return; -// } -// } -// END rustc.try_identity.SimplifyLocals.after.mir diff --git a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..58c5313909f6b --- /dev/null +++ b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyArmIdentity.diff @@ -0,0 +1,93 @@ +- // MIR for `try_identity` before SimplifyArmIdentity ++ // MIR for `try_identity` after SimplifyArmIdentity + + fn try_identity(_1: std::result::Result) -> std::result::Result { + debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 + let _2: u32; // in scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + let mut _5: isize; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _6: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _7: !; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _8: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _9: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _10: u32; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _11: u32; // in scope 0 at $DIR/simplify_try.rs:7:8: 7:9 + scope 1 { + debug y => _2; // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 + } + scope 2 { + debug err => _6; // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 + scope 3 { + scope 7 { + debug t => _9; // in scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + } + scope 8 { + debug v => _8; // in scope 8 at $SRC_DIR/libcore/result.rs:LL:COL + let mut _12: i32; // in scope 8 at $DIR/simplify_try.rs:6:14: 6:15 + } + } + } + scope 4 { + debug val => _10; // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 + scope 5 { + } + } + scope 6 { + debug self => _4; // in scope 6 at $SRC_DIR/libcore/result.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + StorageLive(_3); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + StorageLive(_4); // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _4 = _1; // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _3 = move _4; // scope 6 at $SRC_DIR/libcore/result.rs:LL:COL + StorageDead(_4); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + switchInt(move _5) -> [0isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + } + + bb1: { +- StorageLive(_10); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 +- _10 = ((_3 as Ok).0: u32); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 +- _2 = _10; // scope 5 at $DIR/simplify_try.rs:6:13: 6:15 +- StorageDead(_10); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 ++ _0 = move _3; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 + StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 +- StorageLive(_11); // scope 1 at $DIR/simplify_try.rs:7:8: 7:9 +- _11 = _2; // scope 1 at $DIR/simplify_try.rs:7:8: 7:9 +- ((_0 as Ok).0: u32) = move _11; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 +- discriminant(_0) = 0; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 +- StorageDead(_11); // scope 1 at $DIR/simplify_try.rs:7:9: 7:10 + StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 + goto -> bb3; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + + bb2: { +- StorageLive(_6); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 +- _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageLive(_8); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageLive(_9); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- _9 = _6; // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- _8 = move _9; // scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL +- StorageDead(_9); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageLive(_12); // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL +- _12 = move _8; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL +- ((_0 as Err).0: i32) = move _12; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL +- discriminant(_0) = 1; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL +- StorageDead(_12); // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL +- StorageDead(_8); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageDead(_6); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 ++ _0 = move _3; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL + StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 + StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 + goto -> bb3; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + + bb3: { + return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir new file mode 100644 index 0000000000000..be61e5e2a9fff --- /dev/null +++ b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir @@ -0,0 +1,61 @@ +// MIR for `try_identity` after SimplifyBranchSame + +fn try_identity(_1: std::result::Result) -> std::result::Result { + debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 + let _2: u32; // in scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + let mut _5: isize; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _6: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _7: !; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _8: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _9: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _10: u32; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _11: u32; // in scope 0 at $DIR/simplify_try.rs:7:8: 7:9 + scope 1 { + debug y => _2; // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 + } + scope 2 { + debug err => _6; // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 + scope 3 { + scope 7 { + debug t => _9; // in scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + } + scope 8 { + debug v => _8; // in scope 8 at $SRC_DIR/libcore/result.rs:LL:COL + let mut _12: i32; // in scope 8 at $DIR/simplify_try.rs:6:14: 6:15 + } + } + } + scope 4 { + debug val => _10; // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 + scope 5 { + } + } + scope 6 { + debug self => _4; // in scope 6 at $SRC_DIR/libcore/result.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + StorageLive(_3); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + StorageLive(_4); // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _4 = _1; // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _3 = move _4; // scope 6 at $SRC_DIR/libcore/result.rs:LL:COL + StorageDead(_4); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + goto -> bb1; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + } + + bb1: { + _0 = move _3; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 + StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 + StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 + goto -> bb2; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + + bb2: { + return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } +} diff --git a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyLocals.after.mir b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyLocals.after.mir new file mode 100644 index 0000000000000..b12036f6a03e4 --- /dev/null +++ b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyLocals.after.mir @@ -0,0 +1,40 @@ +// MIR for `try_identity` after SimplifyLocals + +fn try_identity(_1: std::result::Result) -> std::result::Result { + debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 + let _2: u32; // in scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + let _3: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _4: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _5: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _6: u32; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + scope 1 { + debug y => _2; // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 + } + scope 2 { + debug err => _3; // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 + scope 3 { + scope 7 { + debug t => _5; // in scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + } + scope 8 { + debug v => _4; // in scope 8 at $SRC_DIR/libcore/result.rs:LL:COL + } + } + } + scope 4 { + debug val => _6; // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 + scope 5 { + } + } + scope 6 { + debug self => _1; // in scope 6 at $SRC_DIR/libcore/result.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + _0 = move _1; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 + StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 + return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } +} diff --git a/src/test/mir-opt/simplify_try_if_let.rs b/src/test/mir-opt/simplify_try_if_let.rs new file mode 100644 index 0000000000000..daa961c3c8c6c --- /dev/null +++ b/src/test/mir-opt/simplify_try_if_let.rs @@ -0,0 +1,40 @@ +// compile-flags: -Zmir-opt-level=1 +// EMIT_MIR rustc.{{impl}}-append.SimplifyArmIdentity.diff + +use std::ptr::NonNull; + +pub struct LinkedList { + head: Option>, + tail: Option>, +} + +pub struct Node { + next: Option>, +} + +impl LinkedList { + pub fn new() -> Self { + Self { head: None, tail: None } + } + + pub fn append(&mut self, other: &mut Self) { + match self.tail { + None => { }, + Some(mut tail) => { + // `as_mut` is okay here because we have exclusive access to the entirety + // of both lists. + if let Some(other_head) = other.head.take() { + unsafe { + tail.as_mut().next = Some(other_head); + } + } + } + } + } +} + +fn main() { + let mut one = LinkedList::new(); + let mut two = LinkedList::new(); + one.append(&mut two); +} diff --git a/src/test/mir-opt/simplify_try_if_let/rustc.{{impl}}-append.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_try_if_let/rustc.{{impl}}-append.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..6ccec937b9b90 --- /dev/null +++ b/src/test/mir-opt/simplify_try_if_let/rustc.{{impl}}-append.SimplifyArmIdentity.diff @@ -0,0 +1,127 @@ +- // MIR for `::append` before SimplifyArmIdentity ++ // MIR for `::append` after SimplifyArmIdentity + + fn ::append(_1: &mut LinkedList, _2: &mut LinkedList) -> () { + debug self => _1; // in scope 0 at $DIR/simplify_try_if_let.rs:20:19: 20:28 + debug other => _2; // in scope 0 at $DIR/simplify_try_if_let.rs:20:30: 20:35 + let mut _0: (); // return place in scope 0 at $DIR/simplify_try_if_let.rs:20:48: 20:48 + let mut _3: isize; // in scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 + let mut _4: std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + let mut _5: std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:26:43: 26:60 + let mut _6: &mut std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:26:43: 26:53 + let mut _7: isize; // in scope 0 at $DIR/simplify_try_if_let.rs:26:24: 26:40 + let mut _9: std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:28:46: 28:62 + let mut _10: std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:28:51: 28:61 + let mut _11: &mut Node; // in scope 0 at $DIR/simplify_try_if_let.rs:28:25: 28:38 + let mut _12: &mut std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:28:25: 28:29 + scope 1 { + debug tail => _4; // in scope 1 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + let _8: std::ptr::NonNull; // in scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + scope 2 { + debug other_head => _8; // in scope 2 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + scope 3 { + } + } + } + + bb0: { + _3 = discriminant(((*_1).1: std::option::Option>)); // scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 + switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 + } + + bb1: { + StorageLive(_4); // scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + _4 = ((((*_1).1: std::option::Option>) as Some).0: std::ptr::NonNull); // scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + StorageLive(_5); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:60 + StorageLive(_6); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:53 + _6 = &mut ((*_2).0: std::option::Option>); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:53 + _5 = const std::option::Option::>::take(move _6) -> bb4; // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:60 + // ty::Const + // + ty: for<'r> fn(&'r mut std::option::Option>) -> std::option::Option> {std::option::Option::>::take} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_try_if_let.rs:26:54: 26:58 + // + literal: Const { ty: for<'r> fn(&'r mut std::option::Option>) -> std::option::Option> {std::option::Option::>::take}, val: Value(Scalar()) } + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify_try_if_let.rs:21:15: 21:24 + } + + bb3: { + _0 = const (); // scope 0 at $DIR/simplify_try_if_let.rs:22:21: 22:24 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_try_if_let.rs:22:21: 22:24 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb9; // scope 0 at $DIR/simplify_try_if_let.rs:21:9: 32:10 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/simplify_try_if_let.rs:26:59: 26:60 + _7 = discriminant(_5); // scope 1 at $DIR/simplify_try_if_let.rs:26:24: 26:40 + switchInt(move _7) -> [1isize: bb6, otherwise: bb5]; // scope 1 at $DIR/simplify_try_if_let.rs:26:24: 26:40 + } + + bb5: { + _0 = const (); // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_try_if_let.rs:26:17: 30:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 + } + + bb6: { + StorageLive(_8); // scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + _8 = ((_5 as Some).0: std::ptr::NonNull); // scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + StorageLive(_9); // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 +- StorageLive(_10); // scope 3 at $DIR/simplify_try_if_let.rs:28:51: 28:61 +- _10 = _8; // scope 3 at $DIR/simplify_try_if_let.rs:28:51: 28:61 +- ((_9 as Some).0: std::ptr::NonNull) = move _10; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 +- discriminant(_9) = 1; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 +- StorageDead(_10); // scope 3 at $DIR/simplify_try_if_let.rs:28:61: 28:62 ++ _9 = move _5; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 + StorageLive(_11); // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:38 + StorageLive(_12); // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:29 + _12 = &mut _4; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:29 + _11 = const std::ptr::NonNull::::as_mut(move _12) -> bb7; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:38 + // ty::Const + // + ty: for<'r> unsafe fn(&'r mut std::ptr::NonNull) -> &'r mut Node {std::ptr::NonNull::::as_mut} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_try_if_let.rs:28:30: 28:36 + // + literal: Const { ty: for<'r> unsafe fn(&'r mut std::ptr::NonNull) -> &'r mut Node {std::ptr::NonNull::::as_mut}, val: Value(Scalar()) } + } + + bb7: { + StorageDead(_12); // scope 3 at $DIR/simplify_try_if_let.rs:28:37: 28:38 + ((*_11).0: std::option::Option>) = move _9; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:62 + StorageDead(_9); // scope 3 at $DIR/simplify_try_if_let.rs:28:61: 28:62 + StorageDead(_11); // scope 3 at $DIR/simplify_try_if_let.rs:28:62: 28:63 + _0 = const (); // scope 3 at $DIR/simplify_try_if_let.rs:27:21: 29:22 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_try_if_let.rs:27:21: 29:22 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_8); // scope 1 at $DIR/simplify_try_if_let.rs:30:17: 30:18 + goto -> bb8; // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 + } + + bb8: { + StorageDead(_5); // scope 1 at $DIR/simplify_try_if_let.rs:31:13: 31:14 + StorageDead(_4); // scope 0 at $DIR/simplify_try_if_let.rs:32:9: 32:10 + goto -> bb9; // scope 0 at $DIR/simplify_try_if_let.rs:21:9: 32:10 + } + + bb9: { + return; // scope 0 at $DIR/simplify_try_if_let.rs:33:6: 33:6 + } + } + diff --git a/src/test/mir-opt/slice-drop-shim.rs b/src/test/mir-opt/slice-drop-shim.rs index a25375594d3e1..5d8d37e0bc50f 100644 --- a/src/test/mir-opt/slice-drop-shim.rs +++ b/src/test/mir-opt/slice-drop-shim.rs @@ -1,90 +1,7 @@ // compile-flags: -Zmir-opt-level=0 +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir fn main() { let _fn = std::ptr::drop_in_place::<[String]> as unsafe fn(_); } - -// END RUST SOURCE - -// START rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir -// let mut _2: usize; -// let mut _3: usize; -// let mut _4: usize; -// let mut _5: *mut std::string::String; -// let mut _6: bool; -// let mut _7: *mut std::string::String; -// let mut _8: bool; -// let mut _9: *mut std::string::String; -// let mut _10: *mut std::string::String; -// let mut _11: *mut std::string::String; -// let mut _12: bool; -// let mut _13: *mut std::string::String; -// let mut _14: bool; -// let mut _15: *mut [std::string::String]; -// bb0: { -// goto -> bb15; -// } -// bb1: { -// return; -// } -// bb2 (cleanup): { -// resume; -// } -// bb3 (cleanup): { -// _5 = &raw mut (*_1)[_4]; -// _4 = Add(move _4, const 1usize); -// drop((*_5)) -> bb4; -// } -// bb4 (cleanup): { -// _6 = Eq(_4, _3); -// switchInt(move _6) -> [false: bb3, otherwise: bb2]; -// } -// bb5: { -// _7 = &raw mut (*_1)[_4]; -// _4 = Add(move _4, const 1usize); -// drop((*_7)) -> [return: bb6, unwind: bb4]; -// } -// bb6: { -// _8 = Eq(_4, _3); -// switchInt(move _8) -> [false: bb5, otherwise: bb1]; -// } -// bb7: { -// _4 = const 0usize; -// goto -> bb6; -// } -// bb8: { -// goto -> bb7; -// } -// bb9 (cleanup): { -// _11 = _9; -// _9 = Offset(move _9, const 1usize); -// drop((*_11)) -> bb10; -// } -// bb10 (cleanup): { -// _12 = Eq(_9, _10); -// switchInt(move _12) -> [false: bb9, otherwise: bb2]; -// } -// bb11: { -// _13 = _9; -// _9 = Offset(move _9, const 1usize); -// drop((*_13)) -> [return: bb12, unwind: bb10]; -// } -// bb12: { -// _14 = Eq(_9, _10); -// switchInt(move _14) -> [false: bb11, otherwise: bb1]; -// } -// bb13: { -// _15 = &raw mut (*_1); -// _9 = move _15 as *mut std::string::String (Misc); -// _10 = Offset(_9, move _3); -// goto -> bb12; -// } -// bb14: { -// goto -> bb13; -// } -// bb15: { -// _2 = SizeOf(std::string::String); -// _3 = Len((*_1)); -// switchInt(move _2) -> [0usize: bb8, otherwise: bb14]; -// } -// END rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir diff --git a/src/test/mir-opt/slice-drop-shim/32bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir b/src/test/mir-opt/slice-drop-shim/32bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir new file mode 100644 index 0000000000000..e79dcba13b00b --- /dev/null +++ b/src/test/mir-opt/slice-drop-shim/32bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir @@ -0,0 +1,131 @@ +// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops + +fn std::intrinsics::drop_in_place(_1: *mut [std::string::String]) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _2: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _3: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _4: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _5: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _6: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _7: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _8: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _9: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _10: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _11: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _12: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _13: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _14: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _15: *mut [std::string::String]; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb15; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb3 (cleanup): { + _5 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + drop((*_5)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + _6 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _6) -> [false: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb5: { + _7 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + drop((*_7)) -> [return: bb6, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb6: { + _8 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _8) -> [false: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb7: { + _4 = const 0usize; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + goto -> bb6; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb8: { + goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb9 (cleanup): { + _11 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + drop((*_11)) -> bb10; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb10 (cleanup): { + _12 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _12) -> [false: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb11: { + _13 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + drop((*_13)) -> [return: bb12, unwind: bb10]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb12: { + _14 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _14) -> [false: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb13: { + _15 = &raw mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _9 = move _15 as *mut std::string::String (Misc); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _10 = Offset(_9, move _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + goto -> bb12; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb14: { + goto -> bb13; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb15: { + _2 = SizeOf(std::string::String); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _3 = Len((*_1)); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _2) -> [0usize: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } +} diff --git a/src/test/mir-opt/slice-drop-shim/64bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir b/src/test/mir-opt/slice-drop-shim/64bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir new file mode 100644 index 0000000000000..604a0228f63cf --- /dev/null +++ b/src/test/mir-opt/slice-drop-shim/64bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir @@ -0,0 +1,131 @@ +// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops + +fn std::intrinsics::drop_in_place(_1: *mut [std::string::String]) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _2: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _3: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _4: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _5: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _6: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _7: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _8: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _9: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _10: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _11: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _12: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _13: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _14: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _15: *mut [std::string::String]; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb15; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb3 (cleanup): { + _5 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + drop((*_5)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + _6 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _6) -> [false: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb5: { + _7 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + drop((*_7)) -> [return: bb6, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb6: { + _8 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _8) -> [false: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb7: { + _4 = const 0usize; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + goto -> bb6; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb8: { + goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb9 (cleanup): { + _11 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + drop((*_11)) -> bb10; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb10 (cleanup): { + _12 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _12) -> [false: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb11: { + _13 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + drop((*_13)) -> [return: bb12, unwind: bb10]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb12: { + _14 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _14) -> [false: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb13: { + _15 = &raw mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _9 = move _15 as *mut std::string::String (Misc); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _10 = Offset(_9, move _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + goto -> bb12; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb14: { + goto -> bb13; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb15: { + _2 = SizeOf(std::string::String); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _3 = Len((*_1)); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + switchInt(move _2) -> [0usize: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } +} diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs index 1c98766b9685c..a269914f2620d 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.rs +++ b/src/test/mir-opt/storage_live_dead_in_statics.rs @@ -1,8 +1,7 @@ // Check that when we compile the static `XXX` into MIR, we do not // generate `StorageStart` or `StorageEnd` statements. -// ignore-tidy-linelength - +// EMIT_MIR rustc.XXX.mir_map.0.mir static XXX: &'static Foo = &Foo { tup: "hi", data: &[ @@ -32,159 +31,3 @@ struct Foo { fn main() { println!("{:?}", XXX); } - -// END RUST SOURCE -// START rustc.XXX.mir_map.0.mir -// let mut _0: &Foo; -// let _1: &Foo; -// let _2: Foo; -// let mut _3: &[(u32, u32)]; -// let mut _4: &[(u32, u32); 42]; -// let _5: &[(u32, u32); 42]; -// let _6: [(u32, u32); 42]; -// let mut _7: (u32, u32); -// let mut _8: (u32, u32); -// let mut _9: (u32, u32); -// let mut _10: (u32, u32); -// let mut _11: (u32, u32); -// let mut _12: (u32, u32); -// let mut _13: (u32, u32); -// let mut _14: (u32, u32); -// let mut _15: (u32, u32); -// let mut _16: (u32, u32); -// let mut _17: (u32, u32); -// let mut _18: (u32, u32); -// let mut _19: (u32, u32); -// let mut _20: (u32, u32); -// let mut _21: (u32, u32); -// let mut _22: (u32, u32); -// let mut _23: (u32, u32); -// let mut _24: (u32, u32); -// let mut _25: (u32, u32); -// let mut _26: (u32, u32); -// let mut _27: (u32, u32); -// let mut _28: (u32, u32); -// let mut _29: (u32, u32); -// let mut _30: (u32, u32); -// let mut _31: (u32, u32); -// let mut _32: (u32, u32); -// let mut _33: (u32, u32); -// let mut _34: (u32, u32); -// let mut _35: (u32, u32); -// let mut _36: (u32, u32); -// let mut _37: (u32, u32); -// let mut _38: (u32, u32); -// let mut _39: (u32, u32); -// let mut _40: (u32, u32); -// let mut _41: (u32, u32); -// let mut _42: (u32, u32); -// let mut _43: (u32, u32); -// let mut _44: (u32, u32); -// let mut _45: (u32, u32); -// let mut _46: (u32, u32); -// let mut _47: (u32, u32); -// let mut _48: (u32, u32); -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// StorageLive(_3); -// StorageLive(_4); -// StorageLive(_5); -// StorageLive(_6); -// StorageLive(_7); -// _7 = (const 0u32, const 1u32); -// StorageLive(_8); -// _8 = (const 0u32, const 2u32); -// StorageLive(_9); -// _9 = (const 0u32, const 3u32); -// StorageLive(_10); -// _10 = (const 0u32, const 1u32); -// StorageLive(_11); -// _11 = (const 0u32, const 2u32); -// StorageLive(_12); -// _12 = (const 0u32, const 3u32); -// StorageLive(_13); -// _13 = (const 0u32, const 1u32); -// StorageLive(_14); -// _14 = (const 0u32, const 2u32); -// StorageLive(_15); -// _15 = (const 0u32, const 3u32); -// StorageLive(_16); -// _16 = (const 0u32, const 1u32); -// StorageLive(_17); -// _17 = (const 0u32, const 2u32); -// StorageLive(_18); -// _18 = (const 0u32, const 3u32); -// StorageLive(_19); -// _19 = (const 0u32, const 1u32); -// StorageLive(_20); -// _20 = (const 0u32, const 2u32); -// StorageLive(_21); -// _21 = (const 0u32, const 3u32); -// StorageLive(_22); -// _22 = (const 0u32, const 1u32); -// StorageLive(_23); -// _23 = (const 0u32, const 2u32); -// StorageLive(_24); -// _24 = (const 0u32, const 3u32); -// StorageLive(_25); -// _25 = (const 0u32, const 1u32); -// StorageLive(_26); -// _26 = (const 0u32, const 2u32); -// StorageLive(_27); -// _27 = (const 0u32, const 3u32); -// StorageLive(_28); -// _28 = (const 0u32, const 1u32); -// StorageLive(_29); -// _29 = (const 0u32, const 2u32); -// StorageLive(_30); -// _30 = (const 0u32, const 3u32); -// StorageLive(_31); -// _31 = (const 0u32, const 1u32); -// StorageLive(_32); -// _32 = (const 0u32, const 2u32); -// StorageLive(_33); -// _33 = (const 0u32, const 3u32); -// StorageLive(_34); -// _34 = (const 0u32, const 1u32); -// StorageLive(_35); -// _35 = (const 0u32, const 2u32); -// StorageLive(_36); -// _36 = (const 0u32, const 3u32); -// StorageLive(_37); -// _37 = (const 0u32, const 1u32); -// StorageLive(_38); -// _38 = (const 0u32, const 2u32); -// StorageLive(_39); -// _39 = (const 0u32, const 3u32); -// StorageLive(_40); -// _40 = (const 0u32, const 1u32); -// StorageLive(_41); -// _41 = (const 0u32, const 2u32); -// StorageLive(_42); -// _42 = (const 0u32, const 3u32); -// StorageLive(_43); -// _43 = (const 0u32, const 1u32); -// StorageLive(_44); -// _44 = (const 0u32, const 2u32); -// StorageLive(_45); -// _45 = (const 0u32, const 3u32); -// StorageLive(_46); -// _46 = (const 0u32, const 1u32); -// StorageLive(_47); -// _47 = (const 0u32, const 2u32); -// StorageLive(_48); -// _48 = (const 0u32, const 3u32); -// _6 = [move _7, move _8, move _9, move _10, move _11, move _12, move _13, move _14, move _15, move _16, move _17, move _18, move _19, move _20, move _21, move _22, move _23, move _24, move _25, move _26, move _27, move _28, move _29, move _30, move _31, move _32, move _33, move _34, move _35, move _36, move _37, move _38, move _39, move _40, move _41, move _42, move _43, move _44, move _45, move _46, move _47, move _48]; -// _5 = &_6; -// _4 = &(*_5); -// _3 = move _4 as &[(u32, u32)] (Pointer(Unsize)); -// _2 = Foo { tup: const "hi", data: move _3 }; -// _1 = &_2; -// _0 = &(*_1); -// StorageDead(_5); -// StorageDead(_1); -// return; -// } -//} -// END rustc.XXX.mir_map.0.mir diff --git a/src/test/mir-opt/storage_live_dead_in_statics/rustc.XXX.mir_map.0.mir b/src/test/mir-opt/storage_live_dead_in_statics/rustc.XXX.mir_map.0.mir new file mode 100644 index 0000000000000..8891f19e45904 --- /dev/null +++ b/src/test/mir-opt/storage_live_dead_in_statics/rustc.XXX.mir_map.0.mir @@ -0,0 +1,666 @@ +// MIR for `XXX` 0 mir_map + +static XXX: &Foo = { + let mut _0: &Foo; // return place in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:13: 5:25 + let _1: &Foo; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + let _2: Foo; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 + let mut _3: &[(u32, u32)]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + let mut _4: &[(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + let _5: &[(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + let _6: [(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 + let mut _7: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 + let mut _8: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 + let mut _9: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 + let mut _10: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 + let mut _11: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 + let mut _12: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 + let mut _13: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 + let mut _14: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 + let mut _15: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 + let mut _16: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 + let mut _17: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 + let mut _18: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 + let mut _19: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 + let mut _20: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 + let mut _21: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 + let mut _22: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 + let mut _23: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 + let mut _24: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 + let mut _25: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 + let mut _26: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 + let mut _27: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 + let mut _28: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 + let mut _29: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 + let mut _30: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 + let mut _31: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 + let mut _32: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 + let mut _33: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 + let mut _34: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 + let mut _35: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 + let mut _36: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 + let mut _37: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 + let mut _38: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 + let mut _39: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 + let mut _40: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 + let mut _41: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 + let mut _42: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 + let mut _43: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 + let mut _44: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 + let mut _45: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 + let mut _46: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 + let mut _47: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 + let mut _48: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + StorageLive(_2); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 + StorageLive(_3); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + StorageLive(_4); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + StorageLive(_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + StorageLive(_6); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 + StorageLive(_7); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 + _7 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:8:10: 8:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:8:13: 8:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_8); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 + _8 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:8:18: 8:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:8:21: 8:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_9); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 + _9 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:8:26: 8:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:8:29: 8:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_10); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 + _10 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:9:10: 9:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:9:13: 9:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_11); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 + _11 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:9:18: 9:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:9:21: 9:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_12); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 + _12 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:9:26: 9:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:9:29: 9:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_13); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 + _13 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:10:10: 10:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:10:13: 10:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_14); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 + _14 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:10:18: 10:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:10:21: 10:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_15); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 + _15 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:10:26: 10:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:10:29: 10:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_16); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 + _16 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:11:10: 11:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:11:13: 11:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_17); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 + _17 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:11:18: 11:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:11:21: 11:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_18); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 + _18 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:11:26: 11:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:11:29: 11:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_19); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 + _19 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:12:10: 12:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:12:13: 12:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_20); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 + _20 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:12:18: 12:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:12:21: 12:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_21); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 + _21 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:12:26: 12:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:12:29: 12:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_22); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 + _22 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:13:10: 13:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:13:13: 13:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_23); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 + _23 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:13:18: 13:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:13:21: 13:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_24); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 + _24 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:13:26: 13:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:13:29: 13:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_25); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 + _25 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:14:10: 14:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:14:13: 14:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_26); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 + _26 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:14:18: 14:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:14:21: 14:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_27); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 + _27 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:14:26: 14:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:14:29: 14:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_28); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 + _28 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:15:10: 15:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:15:13: 15:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_29); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 + _29 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:15:18: 15:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:15:21: 15:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_30); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 + _30 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:15:26: 15:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:15:29: 15:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_31); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 + _31 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:16:10: 16:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:16:13: 16:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 + _32 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:16:18: 16:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:16:21: 16:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_33); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 + _33 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:16:26: 16:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:16:29: 16:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_34); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 + _34 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:17:10: 17:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:17:13: 17:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_35); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 + _35 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:17:18: 17:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:17:21: 17:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_36); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 + _36 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:17:26: 17:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:17:29: 17:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_37); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 + _37 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:18:10: 18:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:18:13: 18:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_38); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 + _38 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:18:18: 18:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:18:21: 18:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_39); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 + _39 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:18:26: 18:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:18:29: 18:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_40); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 + _40 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:19:10: 19:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:19:13: 19:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_41); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 + _41 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:19:18: 19:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:19:21: 19:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_42); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 + _42 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:19:26: 19:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:19:29: 19:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_43); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 + _43 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:20:10: 20:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:20:13: 20:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_44); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 + _44 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:20:18: 20:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:20:21: 20:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_45); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 + _45 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:20:26: 20:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:20:29: 20:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + StorageLive(_46); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 + _46 = (const 0u32, const 1u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:21:10: 21:11 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:21:13: 21:14 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } + StorageLive(_47); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 + _47 = (const 0u32, const 2u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:21:18: 21:19 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:21:21: 21:22 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } + StorageLive(_48); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 + _48 = (const 0u32, const 3u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:21:26: 21:27 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:21:29: 21:30 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } + _6 = [move _7, move _8, move _9, move _10, move _11, move _12, move _13, move _14, move _15, move _16, move _17, move _18, move _19, move _20, move _21, move _22, move _23, move _24, move _25, move _26, move _27, move _28, move _29, move _30, move _31, move _32, move _33, move _34, move _35, move _36, move _37, move _38, move _39, move _40, move _41, move _42, move _43, move _44, move _45, move _46, move _47, move _48]; // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 + _5 = &_6; // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + _4 = &(*_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + _3 = move _4 as &[(u32, u32)] (Pointer(Unsize)); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + _2 = Foo { tup: const "hi", data: move _3 }; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [104, 105], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 2 }) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:6:10: 6:14 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [104, 105], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 2 }) } + _1 = &_2; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + _0 = &(*_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + StorageDead(_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:23:1: 23:2 + StorageDead(_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:23:1: 23:2 + return; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:1: 23:3 + } +} diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs index 95570ff76a6d0..7b3c77aca27eb 100644 --- a/src/test/mir-opt/storage_ranges.rs +++ b/src/test/mir-opt/storage_ranges.rs @@ -1,3 +1,5 @@ +// EMIT_MIR rustc.main.nll.0.mir + fn main() { let a = 0; { @@ -5,32 +7,3 @@ fn main() { } let c = 1; } - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// bb0: { -// StorageLive(_1); -// _1 = const 0i32; -// FakeRead(ForLet, _1); -// StorageLive(_2); -// StorageLive(_3); -// StorageLive(_4); -// StorageLive(_5); -// _5 = _1; -// _4 = std::option::Option::::Some(move _5,); -// StorageDead(_5); -// _3 = &_4; -// FakeRead(ForLet, _3); -// _2 = (); -// StorageDead(_4); -// StorageDead(_3); -// StorageDead(_2); -// StorageLive(_6); -// _6 = const 1i32; -// FakeRead(ForLet, _6); -// _0 = (); -// StorageDead(_6); -// StorageDead(_1); -// return; -// } -// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/storage_ranges/rustc.main.nll.0.mir b/src/test/mir-opt/storage_ranges/rustc.main.nll.0.mir new file mode 100644 index 0000000000000..7799f20d974bc --- /dev/null +++ b/src/test/mir-opt/storage_ranges/rustc.main.nll.0.mir @@ -0,0 +1,88 @@ +// MIR for `main` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#0r, '_#1r] +| '_#1r | Local | ['_#1r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=22], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=22], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb0[10..=11]} +| '_#4r | U0 | {bb0[11]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=22]} +| '_#1r live at {bb0[0..=22]} +| '_#3r live at {bb0[10]} +| '_#4r live at {bb0[11]} +| '_#3r: '_#4r due to Assignment at Single(bb0[10]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/storage_ranges.rs:3:11: 3:11 + let _1: i32; // in scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 + let _2: (); // in scope 0 at $DIR/storage_ranges.rs:5:5: 7:6 + let _4: std::option::Option; // in scope 0 at $DIR/storage_ranges.rs:6:18: 6:25 + let mut _5: i32; // in scope 0 at $DIR/storage_ranges.rs:6:23: 6:24 + scope 1 { + debug a => _1; // in scope 1 at $DIR/storage_ranges.rs:4:9: 4:10 + let _3: &std::option::Option; // in scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 + let _6: i32; // in scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 + scope 2 { + debug b => _3; // in scope 2 at $DIR/storage_ranges.rs:6:13: 6:14 + } + scope 3 { + debug c => _6; // in scope 3 at $DIR/storage_ranges.rs:8:9: 8:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 + _1 = const 0i32; // scope 0 at $DIR/storage_ranges.rs:4:13: 4:14 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/storage_ranges.rs:4:13: 4:14 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + FakeRead(ForLet, _1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 + StorageLive(_2); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6 + StorageLive(_3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 + StorageLive(_4); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25 + StorageLive(_5); // scope 1 at $DIR/storage_ranges.rs:6:23: 6:24 + _5 = _1; // scope 1 at $DIR/storage_ranges.rs:6:23: 6:24 + _4 = std::option::Option::::Some(move _5); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25 + StorageDead(_5); // scope 1 at $DIR/storage_ranges.rs:6:24: 6:25 + _3 = &_4; // scope 1 at $DIR/storage_ranges.rs:6:17: 6:25 + FakeRead(ForLet, _3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 + _2 = const (); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/storage_ranges.rs:5:5: 7:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 + StorageDead(_3); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 + StorageDead(_2); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 + StorageLive(_6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 + _6 = const 1i32; // scope 1 at $DIR/storage_ranges.rs:8:13: 8:14 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/storage_ranges.rs:8:13: 8:14 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + FakeRead(ForLet, _6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 + _0 = const (); // scope 0 at $DIR/storage_ranges.rs:3:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/storage_ranges.rs:3:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_6); // scope 1 at $DIR/storage_ranges.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/storage_ranges.rs:9:1: 9:2 + return; // scope 0 at $DIR/storage_ranges.rs:9:2: 9:2 + } +} diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs index d587d237227ad..c1b7ebdbc3ab9 100644 --- a/src/test/mir-opt/uniform_array_move_out.rs +++ b/src/test/mir-opt/uniform_array_move_out.rs @@ -1,10 +1,12 @@ #![feature(box_syntax)] +// EMIT_MIR rustc.move_out_from_end.mir_map.0.mir fn move_out_from_end() { let a = [box 1, box 2]; let [.., _y] = a; } +// EMIT_MIR rustc.move_out_by_subslice.mir_map.0.mir fn move_out_by_subslice() { let a = [box 1, box 2]; let [_y @ ..] = a; @@ -14,15 +16,3 @@ fn main() { move_out_by_subslice(); move_out_from_end(); } - -// END RUST SOURCE - -// START rustc.move_out_from_end.mir_map.0.mir -// _6 = move _1[1 of 2]; -// _0 = (); -// END rustc.move_out_from_end.mir_map.0.mir - -// START rustc.move_out_by_subslice.mir_map.0.mir -// _6 = move _1[0..2]; -// _0 = (); -// END rustc.move_out_by_subslice.mir_map.0.mir diff --git a/src/test/mir-opt/uniform_array_move_out/rustc.move_out_by_subslice.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_by_subslice.mir_map.0.mir new file mode 100644 index 0000000000000..d6478c628dee1 --- /dev/null +++ b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_by_subslice.mir_map.0.mir @@ -0,0 +1,101 @@ +// MIR for `move_out_by_subslice` 0 mir_map + +fn move_out_by_subslice() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uniform_array_move_out.rs:10:27: 10:27 + let _1: [std::boxed::Box; 2]; // in scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + let mut _3: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + let mut _5: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + scope 1 { + debug a => _1; // in scope 1 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + let _6: [std::boxed::Box; 2]; // in scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + scope 2 { + debug _y => _6; // in scope 2 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + StorageLive(_3); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + _3 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + (*_3) = const 1i32; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:11:18: 11:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + _2 = move _3; // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + drop(_3) -> [return: bb1, unwind: bb9]; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + (*_5) = const 2i32; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:11:25: 11:26 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + _4 = move _5; // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + drop(_5) -> [return: bb2, unwind: bb8]; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + } + + bb2: { + StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:11:13: 11:27 + drop(_4) -> [return: bb3, unwind: bb9]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb3: { + StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + drop(_2) -> [return: bb4, unwind: bb10]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb4: { + StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + _6 = move _1[0..2]; // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:10:27: 13:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:10:27: 13:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_6) -> [return: bb5, unwind: bb7]; // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb5: { + StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + drop(_1) -> [return: bb6, unwind: bb10]; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb6: { + StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + return; // scope 0 at $DIR/uniform_array_move_out.rs:13:2: 13:2 + } + + bb7 (cleanup): { + drop(_1) -> bb10; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb8 (cleanup): { + drop(_4) -> bb9; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb9 (cleanup): { + drop(_2) -> bb10; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb10 (cleanup): { + resume; // scope 0 at $DIR/uniform_array_move_out.rs:10:1: 13:2 + } +} diff --git a/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir new file mode 100644 index 0000000000000..bba616de9a68e --- /dev/null +++ b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir @@ -0,0 +1,101 @@ +// MIR for `move_out_from_end` 0 mir_map + +fn move_out_from_end() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uniform_array_move_out.rs:4:24: 4:24 + let _1: [std::boxed::Box; 2]; // in scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + let mut _3: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + let mut _5: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + scope 1 { + debug a => _1; // in scope 1 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + let _6: std::boxed::Box; // in scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + scope 2 { + debug _y => _6; // in scope 2 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + StorageLive(_3); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + _3 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + (*_3) = const 1i32; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:5:18: 5:19 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } + _2 = move _3; // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + drop(_3) -> [return: bb1, unwind: bb9]; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + (*_5) = const 2i32; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:5:25: 5:26 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + _4 = move _5; // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + drop(_5) -> [return: bb2, unwind: bb8]; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + } + + bb2: { + StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:5:13: 5:27 + drop(_4) -> [return: bb3, unwind: bb9]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb3: { + StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + drop(_2) -> [return: bb4, unwind: bb10]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb4: { + StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + _6 = move _1[1 of 2]; // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:4:24: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:4:24: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_6) -> [return: bb5, unwind: bb7]; // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb5: { + StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + drop(_1) -> [return: bb6, unwind: bb10]; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb6: { + StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + return; // scope 0 at $DIR/uniform_array_move_out.rs:7:2: 7:2 + } + + bb7 (cleanup): { + drop(_1) -> bb10; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb8 (cleanup): { + drop(_4) -> bb9; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb9 (cleanup): { + drop(_2) -> bb10; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb10 (cleanup): { + resume; // scope 0 at $DIR/uniform_array_move_out.rs:4:1: 7:2 + } +} diff --git a/src/test/mir-opt/uninhabited-enum.rs b/src/test/mir-opt/uninhabited-enum.rs index 904a9c43c1bcc..6503e19360836 100644 --- a/src/test/mir-opt/uninhabited-enum.rs +++ b/src/test/mir-opt/uninhabited-enum.rs @@ -2,11 +2,13 @@ pub enum Void {} +// EMIT_MIR rustc.process_never.SimplifyLocals.after.mir #[no_mangle] pub fn process_never(input: *const !) { let _input = unsafe { &*input }; } +// EMIT_MIR rustc.process_void.SimplifyLocals.after.mir #[no_mangle] pub fn process_void(input: *const Void) { let _input = unsafe { &*input }; @@ -15,23 +17,3 @@ pub fn process_void(input: *const Void) { } fn main() {} - -// END RUST SOURCE -// -// START rustc.process_never.SimplifyLocals.after.mir -// bb0: { -// StorageLive(_2); -// _2 = &(*_1); -// StorageDead(_2); -// unreachable; -// } -// END rustc.process_never.SimplifyLocals.after.mir -// -// START rustc.process_void.SimplifyLocals.after.mir -// bb0: { -// StorageLive(_2); -// _2 = &(*_1); -// StorageDead(_2); -// return; -// } -// END rustc.process_void.SimplifyLocals.after.mir diff --git a/src/test/mir-opt/uninhabited-enum/rustc.process_never.SimplifyLocals.after.mir b/src/test/mir-opt/uninhabited-enum/rustc.process_never.SimplifyLocals.after.mir new file mode 100644 index 0000000000000..c17fe3bb75757 --- /dev/null +++ b/src/test/mir-opt/uninhabited-enum/rustc.process_never.SimplifyLocals.after.mir @@ -0,0 +1,19 @@ +// MIR for `process_never` after SimplifyLocals + +fn process_never(_1: *const !) -> () { + debug input => _1; // in scope 0 at $DIR/uninhabited-enum.rs:7:22: 7:27 + let mut _0: (); // return place in scope 0 at $DIR/uninhabited-enum.rs:7:39: 7:39 + let _2: &!; // in scope 0 at $DIR/uninhabited-enum.rs:8:8: 8:14 + scope 1 { + debug _input => _2; // in scope 1 at $DIR/uninhabited-enum.rs:8:8: 8:14 + } + scope 2 { + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/uninhabited-enum.rs:8:8: 8:14 + _2 = &(*_1); // scope 2 at $DIR/uninhabited-enum.rs:8:26: 8:33 + StorageDead(_2); // scope 0 at $DIR/uninhabited-enum.rs:9:1: 9:2 + unreachable; // scope 0 at $DIR/uninhabited-enum.rs:7:39: 9:2 + } +} diff --git a/src/test/mir-opt/uninhabited-enum/rustc.process_void.SimplifyLocals.after.mir b/src/test/mir-opt/uninhabited-enum/rustc.process_void.SimplifyLocals.after.mir new file mode 100644 index 0000000000000..8cfcd64a70f7d --- /dev/null +++ b/src/test/mir-opt/uninhabited-enum/rustc.process_void.SimplifyLocals.after.mir @@ -0,0 +1,26 @@ +// MIR for `process_void` after SimplifyLocals + +fn process_void(_1: *const Void) -> () { + debug input => _1; // in scope 0 at $DIR/uninhabited-enum.rs:13:21: 13:26 + let mut _0: (); // return place in scope 0 at $DIR/uninhabited-enum.rs:13:41: 13:41 + let _2: &Void; // in scope 0 at $DIR/uninhabited-enum.rs:14:8: 14:14 + scope 1 { + debug _input => _2; // in scope 1 at $DIR/uninhabited-enum.rs:14:8: 14:14 + } + scope 2 { + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/uninhabited-enum.rs:14:8: 14:14 + _2 = &(*_1); // scope 2 at $DIR/uninhabited-enum.rs:14:26: 14:33 + _0 = const (); // scope 0 at $DIR/uninhabited-enum.rs:13:41: 17:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/uninhabited-enum.rs:13:41: 17:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/uninhabited-enum.rs:17:1: 17:2 + return; // scope 0 at $DIR/uninhabited-enum.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/uninhabited_enum_branching.rs b/src/test/mir-opt/uninhabited_enum_branching.rs index dda5fd4fb7577..daf1156d20ebf 100644 --- a/src/test/mir-opt/uninhabited_enum_branching.rs +++ b/src/test/mir-opt/uninhabited_enum_branching.rs @@ -14,6 +14,8 @@ enum Test2 { E = 5, } +// EMIT_MIR rustc.main.UninhabitedEnumBranching.diff +// EMIT_MIR rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir fn main() { match Test1::C { Test1::A(_) => "A(Empty)", @@ -26,178 +28,3 @@ fn main() { Test2::E => "E", }; } - -// END RUST SOURCE -// -// START rustc.main.UninhabitedEnumBranching.before.mir -// let mut _0: (); -// let _1: &str; -// let mut _2: Test1; -// let mut _3: isize; -// let _4: &str; -// let _5: &str; -// let _6: &str; -// let mut _7: Test2; -// let mut _8: isize; -// let _9: &str; -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = Test1::C; -// _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb2, 1isize: bb3, otherwise: bb1]; -// } -// bb1: { -// StorageLive(_5); -// _5 = const "C"; -// _1 = &(*_5); -// StorageDead(_5); -// goto -> bb4; -// } -// bb2: { -// _1 = const "A(Empty)"; -// goto -> bb4; -// } -// bb3: { -// StorageLive(_4); -// _4 = const "B(Empty)"; -// _1 = &(*_4); -// StorageDead(_4); -// goto -> bb4; -// } -// bb4: { -// StorageDead(_2); -// StorageDead(_1); -// StorageLive(_6); -// StorageLive(_7); -// _7 = Test2::D; -// _8 = discriminant(_7); -// switchInt(move _8) -> [4isize: bb6, otherwise: bb5]; -// } -// bb5: { -// StorageLive(_9); -// _9 = const "E"; -// _6 = &(*_9); -// StorageDead(_9); -// goto -> bb7; -// } -// bb6: { -// _6 = const "D"; -// goto -> bb7; -// } -// bb7: { -// StorageDead(_7); -// StorageDead(_6); -// _0 = (); -// return; -// } -// END rustc.main.UninhabitedEnumBranching.before.mir -// START rustc.main.UninhabitedEnumBranching.after.mir -// let mut _0: (); -// let _1: &str; -// let mut _2: Test1; -// let mut _3: isize; -// let _4: &str; -// let _5: &str; -// let _6: &str; -// let mut _7: Test2; -// let mut _8: isize; -// let _9: &str; -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = Test1::C; -// _3 = discriminant(_2); -// switchInt(move _3) -> bb1; -// } -// bb1: { -// StorageLive(_5); -// _5 = const "C"; -// _1 = &(*_5); -// StorageDead(_5); -// goto -> bb4; -// } -// bb2: { -// _1 = const "A(Empty)"; -// goto -> bb4; -// } -// bb3: { -// StorageLive(_4); -// _4 = const "B(Empty)"; -// _1 = &(*_4); -// StorageDead(_4); -// goto -> bb4; -// } -// bb4: { -// StorageDead(_2); -// StorageDead(_1); -// StorageLive(_6); -// StorageLive(_7); -// _7 = Test2::D; -// _8 = discriminant(_7); -// switchInt(move _8) -> [4isize: bb6, otherwise: bb5]; -// } -// bb5: { -// StorageLive(_9); -// _9 = const "E"; -// _6 = &(*_9); -// StorageDead(_9); -// goto -> bb7; -// } -// bb6: { -// _6 = const "D"; -// goto -> bb7; -// } -// bb7: { -// StorageDead(_7); -// StorageDead(_6); -// _0 = (); -// return; -// } -// END rustc.main.UninhabitedEnumBranching.after.mir -// START rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir -// let mut _0: (); -// let _1: &str; -// let mut _2: Test1; -// let mut _3: isize; -// let _4: &str; -// let _5: &str; -// let _6: &str; -// let mut _7: Test2; -// let mut _8: isize; -// let _9: &str; -// bb0: { -// StorageLive(_1); -// StorageLive(_2); -// _2 = Test1::C; -// _3 = discriminant(_2); -// StorageLive(_5); -// _5 = const "C"; -// _1 = &(*_5); -// StorageDead(_5); -// StorageDead(_2); -// StorageDead(_1); -// StorageLive(_6); -// StorageLive(_7); -// _7 = Test2::D; -// _8 = discriminant(_7); -// switchInt(move _8) -> [4isize: bb2, otherwise: bb1]; -// } -// bb1: { -// StorageLive(_9); -// _9 = const "E"; -// _6 = &(*_9); -// StorageDead(_9); -// goto -> bb3; -// } -// bb2: { -// _6 = const "D"; -// goto -> bb3; -// } -// bb3: { -// StorageDead(_7); -// StorageDead(_6); -// _0 = (); -// return; -// } -// END rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir diff --git a/src/test/mir-opt/uninhabited_enum_branching/rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/src/test/mir-opt/uninhabited_enum_branching/rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir new file mode 100644 index 0000000000000..df6c90fc7fb37 --- /dev/null +++ b/src/test/mir-opt/uninhabited_enum_branching/rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir @@ -0,0 +1,76 @@ +// MIR for `main` after SimplifyCfg-after-uninhabited-enum-branching + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 19:11 + let _1: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + let mut _2: Test1; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + let mut _3: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + let _4: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + let _5: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + let _6: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + let mut _7: Test2; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + let mut _8: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + let _9: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + _3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + StorageLive(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 + StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + _7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + _8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + switchInt(move _8) -> [4isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + } + + bb1: { + StorageLive(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 + goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb2: { + _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb3: { + StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + _0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 30:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:19:11: 30:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/uninhabited_enum_branching.rs:30:2: 30:2 + } +} diff --git a/src/test/mir-opt/uninhabited_enum_branching/rustc.main.UninhabitedEnumBranching.diff b/src/test/mir-opt/uninhabited_enum_branching/rustc.main.UninhabitedEnumBranching.diff new file mode 100644 index 0000000000000..fa1474aa049de --- /dev/null +++ b/src/test/mir-opt/uninhabited_enum_branching/rustc.main.UninhabitedEnumBranching.diff @@ -0,0 +1,112 @@ +- // MIR for `main` before UninhabitedEnumBranching ++ // MIR for `main` after UninhabitedEnumBranching + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 19:11 + let _1: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + let mut _2: Test1; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + let mut _3: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + let _4: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + let _5: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + let _6: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + let mut _7: Test2; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + let mut _8: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + let _9: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + _3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 +- switchInt(move _3) -> [0isize: bb2, 1isize: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 ++ switchInt(move _3) -> bb1; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + } + + bb1: { + StorageLive(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 + goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + } + + bb2: { + _1 = const "A(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:24: 21:34 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:21:24: 21:34 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + } + + bb3: { + StorageLive(_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + _4 = const "B(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + _1 = &(*_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + StorageDead(_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:33: 22:34 + goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + } + + bb4: { + StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + _7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + _8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + switchInt(move _8) -> [4isize: bb6, otherwise: bb5]; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + } + + bb5: { + StorageLive(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 + goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb6: { + _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb7: { + StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + _0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 30:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:19:11: 30:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/uninhabited_enum_branching.rs:30:2: 30:2 + } + } + diff --git a/src/test/mir-opt/unreachable.rs b/src/test/mir-opt/unreachable.rs index fa5c1a074ee15..6f0c4ca3cf5ae 100644 --- a/src/test/mir-opt/unreachable.rs +++ b/src/test/mir-opt/unreachable.rs @@ -4,6 +4,7 @@ fn empty() -> Option { None } +// EMIT_MIR rustc.main.UnreachablePropagation.diff fn main() { if let Some(_x) = empty() { let mut _y; @@ -17,62 +18,3 @@ fn main() { match _x { } } } - -// END RUST SOURCE -// START rustc.main.UnreachablePropagation.before.mir -// bb0: { -// StorageLive(_1); -// _1 = const empty() -> bb1; -// } -// bb1: { -// _2 = discriminant(_1); -// switchInt(move _2) -> [1isize: bb3, otherwise: bb2]; -// } -// bb2: { -// _0 = (); -// StorageDead(_1); -// return; -// } -// bb3: { -// StorageLive(_3); -// _3 = move ((_1 as Some).0: Empty); -// StorageLive(_4); -// StorageLive(_5); -// StorageLive(_6); -// _6 = const true; -// switchInt(_6) -> [false: bb4, otherwise: bb5]; -// } -// bb4: { -// _4 = const 42i32; -// _5 = (); -// goto -> bb6; -// } -// bb5: { -// _4 = const 21i32; -// _5 = (); -// goto -> bb6; -// } -// bb6: { -// StorageDead(_6); -// StorageDead(_5); -// StorageLive(_7); -// unreachable; -// } -// } -// END rustc.main.UnreachablePropagation.before.mir -// START rustc.main.UnreachablePropagation.after.mir -// bb0: { -// StorageLive(_1); -// _1 = const empty() -> bb1; -// } -// bb1: { -// _2 = discriminant(_1); -// goto -> bb2; -// } -// bb2: { -// _0 = (); -// StorageDead(_1); -// return; -// } -// } -// END rustc.main.UnreachablePropagation.after.mir diff --git a/src/test/mir-opt/unreachable/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable/rustc.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..ccd9612caddd4 --- /dev/null +++ b/src/test/mir-opt/unreachable/rustc.main.UnreachablePropagation.diff @@ -0,0 +1,108 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable.rs:8:11: 8:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable.rs:9:23: 9:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable.rs:9:12: 9:20 + let _3: Empty; // in scope 0 at $DIR/unreachable.rs:9:17: 9:19 + let _5: (); // in scope 0 at $DIR/unreachable.rs:12:9: 16:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable.rs:12:12: 12:16 + let mut _7: !; // in scope 0 at $DIR/unreachable.rs:18:9: 18:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable.rs:9:17: 9:19 + let mut _4: i32; // in scope 1 at $DIR/unreachable.rs:10:13: 10:19 + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable.rs:10:13: 10:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable.rs:9:23: 9:30 + _1 = const empty() -> bb1; // scope 0 at $DIR/unreachable.rs:9:23: 9:30 + // ty::Const + // + ty: fn() -> std::option::Option {empty} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable.rs:9:23: 9:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable.rs:9:12: 9:20 +- switchInt(move _2) -> [1isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable.rs:9:12: 9:20 ++ goto -> bb2; // scope 0 at $DIR/unreachable.rs:9:12: 9:20 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/unreachable.rs:9:5: 19:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable.rs:9:5: 19:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/unreachable.rs:20:1: 20:2 + return; // scope 0 at $DIR/unreachable.rs:20:2: 20:2 +- } +- +- bb3: { +- StorageLive(_3); // scope 0 at $DIR/unreachable.rs:9:17: 9:19 +- _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable.rs:9:17: 9:19 +- StorageLive(_4); // scope 1 at $DIR/unreachable.rs:10:13: 10:19 +- StorageLive(_5); // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- StorageLive(_6); // scope 2 at $DIR/unreachable.rs:12:12: 12:16 +- _6 = const true; // scope 2 at $DIR/unreachable.rs:12:12: 12:16 +- // ty::Const +- // + ty: bool +- // + val: Value(Scalar(0x01)) +- // mir::Constant +- // + span: $DIR/unreachable.rs:12:12: 12:16 +- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } +- switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- } +- +- bb4: { +- _4 = const 42i32; // scope 2 at $DIR/unreachable.rs:15:13: 15:20 +- // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x0000002a)) +- // mir::Constant +- // + span: $DIR/unreachable.rs:15:18: 15:20 +- // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } +- _5 = const (); // scope 2 at $DIR/unreachable.rs:14:16: 16:10 +- // ty::Const +- // + ty: () +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/unreachable.rs:14:16: 16:10 +- // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb6; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- } +- +- bb5: { +- _4 = const 21i32; // scope 2 at $DIR/unreachable.rs:13:13: 13:20 +- // ty::Const +- // + ty: i32 +- // + val: Value(Scalar(0x00000015)) +- // mir::Constant +- // + span: $DIR/unreachable.rs:13:18: 13:20 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000015)) } +- _5 = const (); // scope 2 at $DIR/unreachable.rs:12:17: 14:10 +- // ty::Const +- // + ty: () +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/unreachable.rs:12:17: 14:10 +- // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb6; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- } +- +- bb6: { +- StorageDead(_6); // scope 2 at $DIR/unreachable.rs:16:9: 16:10 +- StorageDead(_5); // scope 2 at $DIR/unreachable.rs:16:9: 16:10 +- StorageLive(_7); // scope 2 at $DIR/unreachable.rs:18:9: 18:21 +- unreachable; // scope 2 at $DIR/unreachable.rs:18:15: 18:17 + } + } + diff --git a/src/test/mir-opt/unreachable_asm.rs b/src/test/mir-opt/unreachable_asm.rs index ca614ac32b764..4bbf22b822756 100644 --- a/src/test/mir-opt/unreachable_asm.rs +++ b/src/test/mir-opt/unreachable_asm.rs @@ -1,5 +1,4 @@ -// ignore-tidy-linelength -#![feature(asm)] +#![feature(llvm_asm)] enum Empty {} @@ -7,6 +6,7 @@ fn empty() -> Option { None } +// EMIT_MIR rustc.main.UnreachablePropagation.diff fn main() { if let Some(_x) = empty() { let mut _y; @@ -18,55 +18,7 @@ fn main() { } // asm instruction stops unreachable propagation to if else blocks bb4 and bb5. - unsafe { asm!("NOP"); } + unsafe { llvm_asm!("NOP"); } match _x { } } } - -// END RUST SOURCE -// START rustc.main.UnreachablePropagation.before.mir -// bb4: { -// _4 = const 42i32; -// _5 = (); -// goto -> bb6; -// } -// bb5: { -// _4 = const 21i32; -// _5 = (); -// goto -> bb6; -// } -// bb6: { -// StorageDead(_6); -// StorageDead(_5); -// StorageLive(_7); -// asm!(InlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); -// _7 = (); -// StorageDead(_7); -// StorageLive(_8); -// unreachable; -// } -// } -// END rustc.main.UnreachablePropagation.before.mir -// START rustc.main.UnreachablePropagation.after.mir -// bb4: { -// _4 = const 42i32; -// _5 = (); -// goto -> bb6; -// } -// bb5: { -// _4 = const 21i32; -// _5 = (); -// goto -> bb6; -// } -// bb6: { -// StorageDead(_6); -// StorageDead(_5); -// StorageLive(_7); -// asm!(InlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); -// _7 = (); -// StorageDead(_7); -// StorageLive(_8); -// unreachable; -// } -// } -// END rustc.main.UnreachablePropagation.after.mir diff --git a/src/test/mir-opt/unreachable_asm/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm/rustc.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..449bea06207d9 --- /dev/null +++ b/src/test/mir-opt/unreachable_asm/rustc.main.UnreachablePropagation.diff @@ -0,0 +1,120 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm.rs:10:11: 10:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + let _3: Empty; // in scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + let _5: (); // in scope 0 at $DIR/unreachable_asm.rs:14:9: 18:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_asm.rs:14:12: 14:16 + let _7: (); // in scope 0 at $DIR/unreachable_asm.rs:21:9: 21:37 + let mut _8: !; // in scope 0 at $DIR/unreachable_asm.rs:22:9: 22:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable_asm.rs:11:17: 11:19 + let mut _4: i32; // in scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19 + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable_asm.rs:12:13: 12:19 + scope 3 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + _1 = const empty() -> bb1; // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + // ty::Const + // + ty: fn() -> std::option::Option {empty} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:11:23: 11:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + switchInt(move _2) -> [1isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/unreachable_asm.rs:11:5: 23:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:11:5: 23:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/unreachable_asm.rs:24:1: 24:2 + return; // scope 0 at $DIR/unreachable_asm.rs:24:2: 24:2 + } + + bb3: { + StorageLive(_3); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + StorageLive(_4); // scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19 + StorageLive(_5); // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 + _6 = const true; // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:14:12: 14:16 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb4: { + _4 = const 42i32; // scope 2 at $DIR/unreachable_asm.rs:17:13: 17:20 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:17:18: 17:20 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:16:16: 18:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:16:16: 18:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb6; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb5: { + _4 = const 21i32; // scope 2 at $DIR/unreachable_asm.rs:15:13: 15:20 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000015)) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:15:18: 15:20 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000015)) } + _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:14:17: 16:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:14:17: 16:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb6; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb6: { + StorageDead(_6); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 + StorageDead(_5); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 + StorageLive(_7); // scope 2 at $DIR/unreachable_asm.rs:21:9: 21:37 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm.rs:21:18: 21:35 + _7 = const (); // scope 3 at $DIR/unreachable_asm.rs:21:9: 21:37 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm.rs:21:9: 21:37 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_7); // scope 2 at $DIR/unreachable_asm.rs:21:36: 21:37 + StorageLive(_8); // scope 2 at $DIR/unreachable_asm.rs:22:9: 22:21 + unreachable; // scope 2 at $DIR/unreachable_asm.rs:22:15: 22:17 + } + } + diff --git a/src/test/mir-opt/unreachable_asm_2.rs b/src/test/mir-opt/unreachable_asm_2.rs index 8fdbcfb5cab7f..f1610db999ecb 100644 --- a/src/test/mir-opt/unreachable_asm_2.rs +++ b/src/test/mir-opt/unreachable_asm_2.rs @@ -1,5 +1,4 @@ -// ignore-tidy-linelength -#![feature(asm)] +#![feature(llvm_asm)] enum Empty {} @@ -7,78 +6,21 @@ fn empty() -> Option { None } +// EMIT_MIR rustc.main.UnreachablePropagation.diff fn main() { if let Some(_x) = empty() { let mut _y; if true { // asm instruction stops unreachable propagation to block bb3. - unsafe { asm!("NOP"); } + unsafe { llvm_asm!("NOP"); } _y = 21; } else { // asm instruction stops unreachable propagation to block bb3. - unsafe { asm!("NOP"); } + unsafe { llvm_asm!("NOP"); } _y = 42; } match _x { } } } - -// END RUST SOURCE -// START rustc.main.UnreachablePropagation.before.mir -// bb3: { -// ... -// switchInt(_6) -> [false: bb4, otherwise: bb5]; -// } -// bb4: { -// StorageLive(_8); -// asm!(InlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); -// _8 = (); -// StorageDead(_8); -// _4 = const 42i32; -// _5 = (); -// goto -> bb6; -// } -// bb5: { -// StorageLive(_7); -// asm!(InlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); -// _7 = (); -// StorageDead(_7); -// _4 = const 21i32; -// _5 = (); -// goto -> bb6; -// } -// bb6: { -// StorageDead(_6); -// StorageDead(_5); -// StorageLive(_9); -// unreachable; -// } -// } -// END rustc.main.UnreachablePropagation.before.mir -// START rustc.main.UnreachablePropagation.after.mir -// bb3: { -// ... -// switchInt(_6) -> [false: bb4, otherwise: bb5]; -// } -// bb4: { -// StorageLive(_8); -// asm!(InlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); -// _8 = (); -// StorageDead(_8); -// _4 = const 42i32; -// _5 = (); -// unreachable; -// } -// bb5: { -// StorageLive(_7); -// asm!(InlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); -// _7 = (); -// StorageDead(_7); -// _4 = const 21i32; -// _5 = (); -// unreachable; -// } -// } -// END rustc.main.UnreachablePropagation.after.mir diff --git a/src/test/mir-opt/unreachable_asm_2/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm_2/rustc.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..a152e1dbe892f --- /dev/null +++ b/src/test/mir-opt/unreachable_asm_2/rustc.main.UnreachablePropagation.diff @@ -0,0 +1,135 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm_2.rs:10:11: 10:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + let _3: Empty; // in scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + let _5: (); // in scope 0 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + let _7: (); // in scope 0 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + let _8: (); // in scope 0 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + let mut _9: !; // in scope 0 at $DIR/unreachable_asm_2.rs:24:9: 24:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + let mut _4: i32; // in scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + scope 3 { + } + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + _1 = const empty() -> bb1; // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + // ty::Const + // + ty: fn() -> std::option::Option {empty} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:11:23: 11:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + switchInt(move _2) -> [1isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/unreachable_asm_2.rs:11:5: 25:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:11:5: 25:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/unreachable_asm_2.rs:26:1: 26:2 + return; // scope 0 at $DIR/unreachable_asm_2.rs:26:2: 26:2 + } + + bb3: { + StorageLive(_3); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + StorageLive(_4); // scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + StorageLive(_5); // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + _6 = const true; // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:14:12: 14:16 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + + bb4: { + StorageLive(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 4 at $DIR/unreachable_asm_2.rs:20:22: 20:39 + _8 = const (); // scope 4 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:20:13: 20:41 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:40: 20:41 + _4 = const 42i32; // scope 2 at $DIR/unreachable_asm_2.rs:21:13: 21:20 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:21:18: 21:20 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:18:16: 22:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:18:16: 22:10 + // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb6; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 ++ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + + bb5: { + StorageLive(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm_2.rs:16:22: 16:39 + _7 = const (); // scope 3 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:16:13: 16:41 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:40: 16:41 + _4 = const 21i32; // scope 2 at $DIR/unreachable_asm_2.rs:17:13: 17:20 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000015)) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:17:18: 17:20 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000015)) } + _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:14:17: 18:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:14:17: 18:10 + // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb6; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 +- } +- +- bb6: { +- StorageDead(_6); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 +- StorageDead(_5); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 +- StorageLive(_9); // scope 2 at $DIR/unreachable_asm_2.rs:24:9: 24:21 +- unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:24:15: 24:17 ++ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + } + diff --git a/src/test/mir-opt/unreachable_diverging.rs b/src/test/mir-opt/unreachable_diverging.rs index bf05019d5ced1..53c753f717bd0 100644 --- a/src/test/mir-opt/unreachable_diverging.rs +++ b/src/test/mir-opt/unreachable_diverging.rs @@ -8,6 +8,7 @@ fn loop_forever() { loop {} } +// EMIT_MIR rustc.main.UnreachablePropagation.diff fn main() { let x = true; if let Some(bomb) = empty() { @@ -17,49 +18,3 @@ fn main() { match bomb {} } } - -// END RUST SOURCE -// START rustc.main.UnreachablePropagation.before.mir -// bb3: { -// StorageLive(_4); -// _4 = move ((_2 as Some).0: Empty); -// StorageLive(_5); -// StorageLive(_6); -// _6 = _1; -// switchInt(_6) -> [false: bb4, otherwise: bb5]; -// } -// bb4: { -// _5 = (); -// goto -> bb6; -// } -// bb5: { -// _5 = const loop_forever() -> bb6; -// } -// bb6: { -// StorageDead(_6); -// StorageDead(_5); -// StorageLive(_7); -// unreachable; -// } -// } -// END rustc.main.UnreachablePropagation.before.mir -// START rustc.main.UnreachablePropagation.after.mir -// bb3: { -// StorageLive(_4); -// _4 = move ((_2 as Some).0: Empty); -// StorageLive(_5); -// StorageLive(_6); -// _6 = _1; -// goto -> bb4; -// } -// bb4: { -// _5 = const loop_forever() -> bb5; -// } -// bb5: { -// StorageDead(_6); -// StorageDead(_5); -// StorageLive(_7); -// unreachable; -// } -// } -// END rustc.main.UnreachablePropagation.after.mir diff --git a/src/test/mir-opt/unreachable_diverging/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_diverging/rustc.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..ff23baf0b4e9f --- /dev/null +++ b/src/test/mir-opt/unreachable_diverging/rustc.main.UnreachablePropagation.diff @@ -0,0 +1,97 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_diverging.rs:12:11: 12:11 + let _1: bool; // in scope 0 at $DIR/unreachable_diverging.rs:13:9: 13:10 + let mut _2: std::option::Option; // in scope 0 at $DIR/unreachable_diverging.rs:14:25: 14:32 + let mut _3: isize; // in scope 0 at $DIR/unreachable_diverging.rs:14:12: 14:22 + let _5: (); // in scope 0 at $DIR/unreachable_diverging.rs:15:9: 17:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_diverging.rs:15:12: 15:13 + let mut _7: !; // in scope 0 at $DIR/unreachable_diverging.rs:18:9: 18:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/unreachable_diverging.rs:13:9: 13:10 + let _4: Empty; // in scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 + scope 2 { + debug bomb => _4; // in scope 2 at $DIR/unreachable_diverging.rs:14:17: 14:21 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_diverging.rs:13:9: 13:10 + _1 = const true; // scope 0 at $DIR/unreachable_diverging.rs:13:13: 13:17 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/unreachable_diverging.rs:13:13: 13:17 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + StorageLive(_2); // scope 1 at $DIR/unreachable_diverging.rs:14:25: 14:32 + _2 = const empty() -> bb1; // scope 1 at $DIR/unreachable_diverging.rs:14:25: 14:32 + // ty::Const + // + ty: fn() -> std::option::Option {empty} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_diverging.rs:14:25: 14:30 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _3 = discriminant(_2); // scope 1 at $DIR/unreachable_diverging.rs:14:12: 14:22 + switchInt(move _3) -> [1isize: bb3, otherwise: bb2]; // scope 1 at $DIR/unreachable_diverging.rs:14:12: 14:22 + } + + bb2: { + _0 = const (); // scope 1 at $DIR/unreachable_diverging.rs:14:5: 19:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_diverging.rs:14:5: 19:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2 + StorageDead(_2); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2 + return; // scope 0 at $DIR/unreachable_diverging.rs:20:2: 20:2 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 + _4 = move ((_2 as Some).0: Empty); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 + StorageLive(_5); // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13 + _6 = _1; // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13 +- switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 ++ goto -> bb4; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 + } + + bb4: { +- _5 = const (); // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 ++ _5 = const loop_forever() -> bb5; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27 + // ty::Const +- // + ty: () +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/unreachable_diverging.rs:15:9: 17:10 +- // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 +- } +- +- bb5: { +- _5 = const loop_forever() -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27 +- // ty::Const + // + ty: fn() {loop_forever} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/unreachable_diverging.rs:16:13: 16:25 + // + literal: Const { ty: fn() {loop_forever}, val: Value(Scalar()) } + } + +- bb6: { ++ bb5: { + StorageDead(_6); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10 + StorageDead(_5); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10 + StorageLive(_7); // scope 2 at $DIR/unreachable_diverging.rs:18:9: 18:22 + unreachable; // scope 2 at $DIR/unreachable_diverging.rs:18:15: 18:19 + } + } + diff --git a/src/test/mir-opt/unusual-item-types.rs b/src/test/mir-opt/unusual-item-types.rs index 88cfb62a0d094..ffe8ca01dfb4a 100644 --- a/src/test/mir-opt/unusual-item-types.rs +++ b/src/test/mir-opt/unusual-item-types.rs @@ -1,86 +1,29 @@ // Test that we don't ICE when trying to dump MIR for unusual item types and // that we don't create filenames containing `<` and `>` -// ignore-tidy-linelength + +// EMIT_MIR_FOR_EACH_BIT_WIDTH struct A; +// EMIT_MIR rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir impl A { const ASSOCIATED_CONSTANT: i32 = 2; } // See #59021 +// EMIT_MIR rustc.Test-X-{{constructor}}.mir_map.0.mir enum Test { X(usize), Y { a: usize }, } +// EMIT_MIR rustc.E-V-{{constant}}.mir_map.0.mir enum E { V = 5, } fn main() { let f = Test::X as fn(usize) -> Test; +// EMIT_MIR rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir let v = Vec::::new(); } - -// END RUST SOURCE - -// START rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir -// bb0: { -// _0 = const 2i32; -// return; -// } -// bb1 (cleanup): { -// resume; -// } -// END rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir - -// START rustc.E-V-{{constant}}.mir_map.0.mir -// bb0: { -// _0 = const 5isize; -// return; -// } -// bb1 (cleanup): { -// resume; -// } -// END rustc.E-V-{{constant}}.mir_map.0.mir - -// START rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir -// bb0: { -// goto -> bb7; -// } -// bb1: { -// return; -// } -// bb2 (cleanup): { -// resume; -// } -// bb3: { -// goto -> bb1; -// } -// bb4 (cleanup): { -// goto -> bb2; -// } -// bb5 (cleanup): { -// drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb4; -// } -// bb6: { -// drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb4]; -// } -// bb7: { -// _2 = &mut (*_1); -// _3 = const as std::ops::Drop>::drop(move _2) -> [return: bb6, unwind: bb5]; -// } -// END rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir - -// START rustc.Test-X-{{constructor}}.mir_map.0.mir -// fn Test::X(_1: usize) -> Test { -// let mut _0: Test; -// -// bb0: { -// ((_0 as X).0: usize) = move _1; -// discriminant(_0) = 0; -// return; -// } -// } -// END rustc.Test-X-{{constructor}}.mir_map.0.mir diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.E-V-{{constant}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.E-V-{{constant}}.mir_map.0.mir new file mode 100644 index 0000000000000..b17f379f4b6a9 --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/32bit/rustc.E-V-{{constant}}.mir_map.0.mir @@ -0,0 +1,16 @@ +// MIR for `E::V::{{constant}}#0` 0 mir_map + +E::V::{{constant}}#0: isize = { + let mut _0: isize; // return place in scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + + bb0: { + _0 = const 5isize; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/unusual-item-types.rs:22:9: 22:10 + // + literal: Const { ty: isize, val: Value(Scalar(0x00000005)) } + return; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } +} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.Test-X-{{constructor}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.Test-X-{{constructor}}.mir_map.0.mir new file mode 100644 index 0000000000000..832f18e14c25d --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/32bit/rustc.Test-X-{{constructor}}.mir_map.0.mir @@ -0,0 +1,11 @@ +// MIR for `Test::X` 0 mir_map + +fn Test::X(_1: usize) -> Test { + let mut _0: Test; // return place in scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + + bb0: { + ((_0 as X).0: usize) = move _1; // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + discriminant(_0) = 0; // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + return; // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + } +} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir new file mode 100644 index 0000000000000..d56d15062d208 --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/32bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir @@ -0,0 +1,42 @@ +// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops + +fn std::intrinsics::drop_in_place(_1: *mut std::vec::Vec) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb6; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb3: { + goto -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb2; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb5: { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb6: { + _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _3 = const as std::ops::Drop>::drop(move _2) -> [return: bb5, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop} + // + val: Value(Scalar()) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } + } +} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir new file mode 100644 index 0000000000000..7349307f94c71 --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir @@ -0,0 +1,16 @@ +// MIR for `::ASSOCIATED_CONSTANT` 0 mir_map + +const ::ASSOCIATED_CONSTANT: i32 = { + let mut _0: i32; // return place in scope 0 at $DIR/unusual-item-types.rs:10:32: 10:35 + + bb0: { + _0 = const 2i32; // scope 0 at $DIR/unusual-item-types.rs:10:38: 10:39 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/unusual-item-types.rs:10:38: 10:39 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + return; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } +} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir new file mode 100644 index 0000000000000..12073d612a195 --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir @@ -0,0 +1,16 @@ +// MIR for `E::V::{{constant}}#0` 0 mir_map + +E::V::{{constant}}#0: isize = { + let mut _0: isize; // return place in scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + + bb0: { + _0 = const 5isize; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x0000000000000005)) + // mir::Constant + // + span: $DIR/unusual-item-types.rs:22:9: 22:10 + // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000005)) } + return; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } +} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.Test-X-{{constructor}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.Test-X-{{constructor}}.mir_map.0.mir new file mode 100644 index 0000000000000..832f18e14c25d --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/64bit/rustc.Test-X-{{constructor}}.mir_map.0.mir @@ -0,0 +1,11 @@ +// MIR for `Test::X` 0 mir_map + +fn Test::X(_1: usize) -> Test { + let mut _0: Test; // return place in scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + + bb0: { + ((_0 as X).0: usize) = move _1; // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + discriminant(_0) = 0; // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + return; // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13 + } +} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir new file mode 100644 index 0000000000000..d56d15062d208 --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/64bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir @@ -0,0 +1,42 @@ +// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops + +fn std::intrinsics::drop_in_place(_1: *mut std::vec::Vec) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb6; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb3: { + goto -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb2; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb5: { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb6: { + _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _3 = const as std::ops::Drop>::drop(move _2) -> [return: bb5, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop} + // + val: Value(Scalar()) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } + } +} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir new file mode 100644 index 0000000000000..7349307f94c71 --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir @@ -0,0 +1,16 @@ +// MIR for `::ASSOCIATED_CONSTANT` 0 mir_map + +const ::ASSOCIATED_CONSTANT: i32 = { + let mut _0: i32; // return place in scope 0 at $DIR/unusual-item-types.rs:10:32: 10:35 + + bb0: { + _0 = const 2i32; // scope 0 at $DIR/unusual-item-types.rs:10:38: 10:39 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/unusual-item-types.rs:10:38: 10:39 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } + return; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } +} diff --git a/src/test/mir-opt/while-storage.rs b/src/test/mir-opt/while-storage.rs index 86c3f79d3a706..56f6c3380a719 100644 --- a/src/test/mir-opt/while-storage.rs +++ b/src/test/mir-opt/while-storage.rs @@ -5,6 +5,7 @@ fn get_bool(c: bool) -> bool { c } +// EMIT_MIR rustc.while_loop.PreCodegen.after.mir fn while_loop(c: bool) { while get_bool(c) { if get_bool(c) { @@ -16,41 +17,3 @@ fn while_loop(c: bool) { fn main() { while_loop(false); } - -// END RUST SOURCE - -// START rustc.while_loop.PreCodegen.after.mir -// bb0: { -// StorageLive(_2); -// StorageLive(_3); -// _3 = _1; -// _2 = const get_bool(move _3) -> bb1; -// } -// bb1: { -// StorageDead(_3); -// switchInt(_2) -> [false: bb6, otherwise: bb2]; -// } -// bb2: { -// StorageLive(_4); -// StorageLive(_5); -// _5 = _1; -// _4 = const get_bool(move _5) -> bb3; -// } -// bb3: { -// StorageDead(_5); -// switchInt(_4) -> [false: bb4, otherwise: bb5]; -// } -// bb4: { -// StorageDead(_4); -// StorageDead(_2); -// goto -> bb0; -// } -// bb5: { -// StorageDead(_4); -// goto -> bb6; -// } -// bb6: { -// StorageDead(_2); -// return; -// } -// END rustc.while_loop.PreCodegen.after.mir diff --git a/src/test/mir-opt/while-storage/rustc.while_loop.PreCodegen.after.mir b/src/test/mir-opt/while-storage/rustc.while_loop.PreCodegen.after.mir new file mode 100644 index 0000000000000..4742a0fb63169 --- /dev/null +++ b/src/test/mir-opt/while-storage/rustc.while_loop.PreCodegen.after.mir @@ -0,0 +1,80 @@ +// MIR for `while_loop` after PreCodegen + +fn while_loop(_1: bool) -> () { + debug c => _1; // in scope 0 at $DIR/while-storage.rs:9:15: 9:16 + let mut _0: (); // return place in scope 0 at $DIR/while-storage.rs:9:24: 9:24 + let mut _2: bool; // in scope 0 at $DIR/while-storage.rs:10:11: 10:22 + let mut _3: bool; // in scope 0 at $DIR/while-storage.rs:10:20: 10:21 + let mut _4: bool; // in scope 0 at $DIR/while-storage.rs:11:12: 11:23 + let mut _5: bool; // in scope 0 at $DIR/while-storage.rs:11:21: 11:22 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/while-storage.rs:10:11: 10:22 + StorageLive(_3); // scope 0 at $DIR/while-storage.rs:10:20: 10:21 + _3 = _1; // scope 0 at $DIR/while-storage.rs:10:20: 10:21 + _2 = const get_bool(move _3) -> bb1; // scope 0 at $DIR/while-storage.rs:10:11: 10:22 + // ty::Const + // + ty: fn(bool) -> bool {get_bool} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/while-storage.rs:10:11: 10:19 + // + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/while-storage.rs:10:21: 10:22 + switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/while-storage.rs:10:5: 14:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb7; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + } + + bb3: { + StorageLive(_4); // scope 0 at $DIR/while-storage.rs:11:12: 11:23 + StorageLive(_5); // scope 0 at $DIR/while-storage.rs:11:21: 11:22 + _5 = _1; // scope 0 at $DIR/while-storage.rs:11:21: 11:22 + _4 = const get_bool(move _5) -> bb4; // scope 0 at $DIR/while-storage.rs:11:12: 11:23 + // ty::Const + // + ty: fn(bool) -> bool {get_bool} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/while-storage.rs:11:12: 11:20 + // + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(Scalar()) } + } + + bb4: { + StorageDead(_5); // scope 0 at $DIR/while-storage.rs:11:22: 11:23 + switchInt(_4) -> [false: bb5, otherwise: bb6]; // scope 0 at $DIR/while-storage.rs:11:9: 13:10 + } + + bb5: { + StorageDead(_4); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + goto -> bb0; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + } + + bb6: { + _0 = const (); // scope 0 at $DIR/while-storage.rs:12:13: 12:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/while-storage.rs:12:13: 12:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + goto -> bb7; // scope 0 at $DIR/while-storage.rs:1:1: 1:1 + } + + bb7: { + StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + return; // scope 0 at $DIR/while-storage.rs:15:2: 15:2 + } +} diff --git a/src/test/pretty/asm-clobbers.rs b/src/test/pretty/asm-clobbers.rs deleted file mode 100644 index 1bc9f008bbbf9..0000000000000 --- a/src/test/pretty/asm-clobbers.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![feature(asm)] - -pub fn main() { unsafe { asm!("" : : : "hello", "world") }; } diff --git a/src/test/pretty/asm-options.rs b/src/test/pretty/asm-options.rs deleted file mode 100644 index 5c2bbd9edd931..0000000000000 --- a/src/test/pretty/asm-options.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(asm)] - -// pp-exact - -pub fn main() { - unsafe { - asm!("" : : : : "volatile"); - asm!("" : : : : "alignstack"); - asm!("" : : : : "intel"); - } -} diff --git a/src/test/pretty/asm.pp b/src/test/pretty/asm.pp new file mode 100644 index 0000000000000..1723e1cc1cb09 --- /dev/null +++ b/src/test/pretty/asm.pp @@ -0,0 +1,25 @@ +#![feature(prelude_import)] +#![no_std] +#![feature(asm)] +#[prelude_import] +use ::std::prelude::v1::*; +#[macro_use] +extern crate std; + +// pretty-mode:expanded +// pp-exact:asm.pp + +pub fn main() { + let a: i32; + let mut b = 4i32; + unsafe { + asm!(""); + asm!(""); + asm!("", options(nomem, nostack)); + asm!("{0}", in(reg) 4); + asm!("{0}", out(reg) a); + asm!("{0}", inout(reg) b); + asm!("{0} {1}", out(reg) _, inlateout(reg) b => _); + asm!("", out("al") _, lateout("rbx") _); + } +} diff --git a/src/test/pretty/asm.rs b/src/test/pretty/asm.rs new file mode 100644 index 0000000000000..9812f1d97e5ff --- /dev/null +++ b/src/test/pretty/asm.rs @@ -0,0 +1,19 @@ +#![feature(asm)] + +// pretty-mode:expanded +// pp-exact:asm.pp + +pub fn main() { + let a: i32; + let mut b = 4i32; + unsafe { + asm!(""); + asm!("", options()); + asm!("", options(nostack, nomem)); + asm!("{}", in(reg) 4); + asm!("{0}", out(reg) a); + asm!("{name}", name = inout(reg) b); + asm!("{} {}", out(reg) _, inlateout(reg) b => _); + asm!("", out("al") _, lateout("rbx") _); + } +} diff --git a/src/test/pretty/llvm-asm-clobbers.rs b/src/test/pretty/llvm-asm-clobbers.rs new file mode 100644 index 0000000000000..2c09646e47e4a --- /dev/null +++ b/src/test/pretty/llvm-asm-clobbers.rs @@ -0,0 +1,3 @@ +#![feature(llvm_asm)] + +pub fn main() { unsafe { llvm_asm!("" : : : "hello", "world") }; } diff --git a/src/test/pretty/llvm-asm-options.rs b/src/test/pretty/llvm-asm-options.rs new file mode 100644 index 0000000000000..86a881bfbd18a --- /dev/null +++ b/src/test/pretty/llvm-asm-options.rs @@ -0,0 +1,11 @@ +#![feature(llvm_asm)] + +// pp-exact + +pub fn main() { + unsafe { + llvm_asm!("" : : : : "volatile"); + llvm_asm!("" : : : : "alignstack"); + llvm_asm!("" : : : : "intel"); + } +} diff --git a/src/test/pretty/raw-str-nonexpr.rs b/src/test/pretty/raw-str-nonexpr.rs index cb23124f2103f..41227898f24a8 100644 --- a/src/test/pretty/raw-str-nonexpr.rs +++ b/src/test/pretty/raw-str-nonexpr.rs @@ -1,8 +1,8 @@ // pp-exact -#![feature(asm)] +#![feature(llvm_asm)] #[cfg(foo = r#"just parse this"#)] extern crate blah as blah; -fn main() { unsafe { asm!(r###"blah"###); } } +fn main() { unsafe { llvm_asm!(r###"blah"###); } } diff --git a/src/test/pretty/trait-polarity.rs b/src/test/pretty/trait-polarity.rs index 3aab99bf6a0ae..df1a7946afb11 100644 --- a/src/test/pretty/trait-polarity.rs +++ b/src/test/pretty/trait-polarity.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] // pp-exact diff --git a/src/test/run-fail/assert-as-macro.rs b/src/test/run-fail/assert-as-macro.rs deleted file mode 100644 index f715e21f781bd..0000000000000 --- a/src/test/run-fail/assert-as-macro.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:assertion failed: 1 == 2 - -fn main() { - assert!(1 == 2); -} diff --git a/src/test/run-fail/assert-macro-explicit.rs b/src/test/run-fail/assert-macro-explicit.rs deleted file mode 100644 index 3689323c999d6..0000000000000 --- a/src/test/run-fail/assert-macro-explicit.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'assertion failed: false' - -fn main() { - assert!(false); -} diff --git a/src/test/run-fail/assert-macro-fmt.rs b/src/test/run-fail/assert-macro-fmt.rs deleted file mode 100644 index 9fbfb085c2fa5..0000000000000 --- a/src/test/run-fail/assert-macro-fmt.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'test-assert-fmt 42 rust' - -fn main() { - assert!(false, "test-assert-fmt {} {}", 42, "rust"); -} diff --git a/src/test/run-fail/assert-macro-owned.rs b/src/test/run-fail/assert-macro-owned.rs deleted file mode 100644 index bd58d35eb719f..0000000000000 --- a/src/test/run-fail/assert-macro-owned.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'test-assert-owned' - -fn main() { - assert!(false, "test-assert-owned".to_string()); -} diff --git a/src/test/run-fail/assert-macro-static.rs b/src/test/run-fail/assert-macro-static.rs deleted file mode 100644 index 650aaeab4f66f..0000000000000 --- a/src/test/run-fail/assert-macro-static.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'test-assert-static' - -fn main() { - assert!(false, "test-assert-static"); -} diff --git a/src/test/run-fail/binop-fail-3.rs b/src/test/run-fail/binop-fail-3.rs deleted file mode 100644 index a7696fffda0c2..0000000000000 --- a/src/test/run-fail/binop-fail-3.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:quux -fn foo() -> ! { - panic!("quux"); -} - -#[allow(resolve_trait_on_defaulted_unit)] -fn main() { - foo() == foo(); // these types wind up being defaulted to () -} diff --git a/src/test/run-fail/bug-2470-bounds-check-overflow.rs b/src/test/run-fail/bug-2470-bounds-check-overflow.rs deleted file mode 100644 index b4e3f24699118..0000000000000 --- a/src/test/run-fail/bug-2470-bounds-check-overflow.rs +++ /dev/null @@ -1,25 +0,0 @@ -// error-pattern:index out of bounds - -use std::mem; - -fn main() { - - // This should cause a bounds-check panic, but may not if we do our - // bounds checking by comparing the scaled index to the vector's - // address-bounds, since we've scaled the index to wrap around to the - // address of the 0th cell in the array (even though the index is - // huge). - - let x = vec![1_usize, 2_usize, 3_usize]; - - let base = x.as_ptr() as usize; - let idx = base / mem::size_of::(); - println!("ov1 base = 0x{:x}", base); - println!("ov1 idx = 0x{:x}", idx); - println!("ov1 sizeof::() = 0x{:x}", mem::size_of::()); - println!("ov1 idx * sizeof::() = 0x{:x}", - idx * mem::size_of::()); - - // This should panic. - println!("ov1 0x{:x}", x[idx]); -} diff --git a/src/test/run-fail/bug-811.rs b/src/test/run-fail/bug-811.rs deleted file mode 100644 index e36ec0f59010b..0000000000000 --- a/src/test/run-fail/bug-811.rs +++ /dev/null @@ -1,24 +0,0 @@ -// error-pattern:quux - -use std::marker::PhantomData; - -fn test00_start(ch: chan_t, message: isize) { - send(ch, message); -} - -type task_id = isize; -type port_id = isize; - -struct chan_t { - task: task_id, - port: port_id, - marker: PhantomData<*mut T>, -} - -fn send(_ch: chan_t, _data: T) { - panic!(); -} - -fn main() { - panic!("quux"); -} diff --git a/src/test/run-fail/die-macro-expr.rs b/src/test/run-fail/die-macro-expr.rs deleted file mode 100644 index 70413f978969c..0000000000000 --- a/src/test/run-fail/die-macro-expr.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:test - -fn main() { - let __isize: isize = panic!("test"); -} diff --git a/src/test/run-fail/die-macro-pure.rs b/src/test/run-fail/die-macro-pure.rs deleted file mode 100644 index cec0742d5035b..0000000000000 --- a/src/test/run-fail/die-macro-pure.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:test - -fn f() { - panic!("test"); -} - -fn main() { - f(); -} diff --git a/src/test/run-fail/die-macro.rs b/src/test/run-fail/die-macro.rs deleted file mode 100644 index 846b9ea24d3b7..0000000000000 --- a/src/test/run-fail/die-macro.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:test - -fn main() { - panic!("test"); -} diff --git a/src/test/run-fail/diverging-closure.rs b/src/test/run-fail/diverging-closure.rs deleted file mode 100644 index a92e07a21fe4f..0000000000000 --- a/src/test/run-fail/diverging-closure.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:oops - -fn main() { - let func = || -> ! { - panic!("oops"); - }; - func(); -} diff --git a/src/test/run-fail/divide-by-zero.rs b/src/test/run-fail/divide-by-zero.rs deleted file mode 100644 index fdb3f4842e50a..0000000000000 --- a/src/test/run-fail/divide-by-zero.rs +++ /dev/null @@ -1,6 +0,0 @@ -// error-pattern:attempt to divide by zero - -fn main() { - let y = 0; - let _z = 1 / y; -} diff --git a/src/test/run-fail/doublepanic.rs b/src/test/run-fail/doublepanic.rs deleted file mode 100644 index 10d303e491e44..0000000000000 --- a/src/test/run-fail/doublepanic.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![allow(unreachable_code)] - -// error-pattern:One -fn main() { - panic!("One"); - panic!("Two"); -} diff --git a/src/test/run-fail/dst-raw-slice.rs b/src/test/run-fail/dst-raw-slice.rs deleted file mode 100644 index 561b1fb42ed06..0000000000000 --- a/src/test/run-fail/dst-raw-slice.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Test bounds checking for DST raw slices -// error-pattern:index out of bounds - -fn main() { - let a: *const [_] = &[1, 2, 3]; - unsafe { - let _b = (*a)[3]; - } -} diff --git a/src/test/run-fail/explicit-panic.rs b/src/test/run-fail/explicit-panic.rs deleted file mode 100644 index 11ea6b4122128..0000000000000 --- a/src/test/run-fail/explicit-panic.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:explicit -fn main() { - panic!(); -} diff --git a/src/test/run-fail/expr-fn-panic.rs b/src/test/run-fail/expr-fn-panic.rs deleted file mode 100644 index 0532c32ec7028..0000000000000 --- a/src/test/run-fail/expr-fn-panic.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:explicit panic - -fn f() -> ! { - panic!() -} - -fn main() { - f(); -} diff --git a/src/test/run-fail/expr-match-panic.rs b/src/test/run-fail/expr-match-panic.rs deleted file mode 100644 index b2f0179a08323..0000000000000 --- a/src/test/run-fail/expr-match-panic.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:explicit panic - -fn main() { - let _x = match true { - false => 0, - true => panic!(), - }; -} diff --git a/src/test/run-fail/fmt-panic.rs b/src/test/run-fail/fmt-panic.rs deleted file mode 100644 index 5749991914c02..0000000000000 --- a/src/test/run-fail/fmt-panic.rs +++ /dev/null @@ -1,6 +0,0 @@ -// error-pattern:meh - -fn main() { - let str_var: String = "meh".to_string(); - panic!("{}", str_var); -} diff --git a/src/test/run-fail/for-each-loop-panic.rs b/src/test/run-fail/for-each-loop-panic.rs deleted file mode 100644 index d24e81e152c3c..0000000000000 --- a/src/test/run-fail/for-each-loop-panic.rs +++ /dev/null @@ -1,7 +0,0 @@ -// error-pattern:moop - -fn main() { - for _ in 0_usize..10_usize { - panic!("moop"); - } -} diff --git a/src/test/run-fail/glob-use-std.rs b/src/test/run-fail/glob-use-std.rs deleted file mode 100644 index d19a782986b16..0000000000000 --- a/src/test/run-fail/glob-use-std.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Issue #7580 - -// error-pattern:panic works - -use std::*; - -fn main() { - panic!("panic works") -} diff --git a/src/test/run-fail/issue-12920.rs b/src/test/run-fail/issue-12920.rs deleted file mode 100644 index 0819e992d1378..0000000000000 --- a/src/test/run-fail/issue-12920.rs +++ /dev/null @@ -1,6 +0,0 @@ -// error-pattern:explicit panic - -pub fn main() { - panic!(); - println!("{}", 1); -} diff --git a/src/test/run-fail/issue-13202.rs b/src/test/run-fail/issue-13202.rs deleted file mode 100644 index cf3a6b3d986f5..0000000000000 --- a/src/test/run-fail/issue-13202.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:bad input - -fn main() { - Some("foo").unwrap_or(panic!("bad input")).to_string(); -} diff --git a/src/test/run-fail/issue-20971.rs b/src/test/run-fail/issue-20971.rs deleted file mode 100644 index 6c2de783c0166..0000000000000 --- a/src/test/run-fail/issue-20971.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Regression test for Issue #20971. - -// error-pattern:Hello, world! - -pub trait Parser { - type Input; - fn parse(&mut self, input: ::Input); -} - -impl Parser for () { - type Input = (); - fn parse(&mut self, input: ()) {} -} - -pub fn many() -> Box::Input> + 'static> { - panic!("Hello, world!") -} - -fn main() { - many().parse(()); -} diff --git a/src/test/run-fail/issue-23354.rs b/src/test/run-fail/issue-23354.rs deleted file mode 100644 index 4c2fb022a6bf6..0000000000000 --- a/src/test/run-fail/issue-23354.rs +++ /dev/null @@ -1,6 +0,0 @@ -// error-pattern:panic evaluated - -#[allow(unused_variables)] -fn main() { - let x = [panic!("panic evaluated"); 0]; -} diff --git a/src/test/run-fail/issue-2444.rs b/src/test/run-fail/issue-2444.rs deleted file mode 100644 index 17f89e4d20d3e..0000000000000 --- a/src/test/run-fail/issue-2444.rs +++ /dev/null @@ -1,15 +0,0 @@ -// error-pattern:explicit panic - -use std::sync::Arc; - -enum e { - ee(Arc), -} - -fn foo() -> e { - panic!(); -} - -fn main() { - let _f = foo(); -} diff --git a/src/test/run-fail/issue-2761.rs b/src/test/run-fail/issue-2761.rs deleted file mode 100644 index 84d90930d2d23..0000000000000 --- a/src/test/run-fail/issue-2761.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:custom message - -fn main() { - assert!(false, "custom message"); -} diff --git a/src/test/run-fail/issue-51345.rs b/src/test/run-fail/issue-51345.rs deleted file mode 100644 index c62f98ea78d1e..0000000000000 --- a/src/test/run-fail/issue-51345.rs +++ /dev/null @@ -1,6 +0,0 @@ -// error-pattern: thread 'main' panicked at 'explicit panic' - -fn main() { - let mut vec = vec![]; - vec.push((vec.len(), panic!())); -} diff --git a/src/test/run-fail/issue-6458-1.rs b/src/test/run-fail/issue-6458-1.rs deleted file mode 100644 index 550bb2b832f44..0000000000000 --- a/src/test/run-fail/issue-6458-1.rs +++ /dev/null @@ -1,6 +0,0 @@ -// error-pattern:explicit panic - -fn foo(t: T) {} -fn main() { - foo(panic!()) -} diff --git a/src/test/run-fail/main-panic.rs b/src/test/run-fail/main-panic.rs deleted file mode 100644 index 3a9409562db6a..0000000000000 --- a/src/test/run-fail/main-panic.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:thread 'main' panicked at - -fn main() { - panic!() -} diff --git a/src/test/run-fail/mir_codegen_no_landing_pads.rs b/src/test/run-fail/mir_codegen_no_landing_pads.rs deleted file mode 100644 index f3384dc45f3d2..0000000000000 --- a/src/test/run-fail/mir_codegen_no_landing_pads.rs +++ /dev/null @@ -1,27 +0,0 @@ -// compile-flags: -Z no-landing-pads -C codegen-units=1 -// error-pattern:converging_fn called -// ignore-cloudabi no std::process - -use std::io::{self, Write}; - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - ::std::process::exit(1) - } -} - -fn converging_fn() { - panic!("converging_fn called") -} - -fn mir(d: Droppable) { - let x = Droppable; - converging_fn(); - drop(x); - drop(d); -} - -fn main() { - mir(Droppable); -} diff --git a/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs b/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs deleted file mode 100644 index 08f6d578bb2e0..0000000000000 --- a/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs +++ /dev/null @@ -1,27 +0,0 @@ -// compile-flags: -Z no-landing-pads -C codegen-units=1 -// error-pattern:diverging_fn called -// ignore-cloudabi no std::process - -use std::io::{self, Write}; - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - ::std::process::exit(1) - } -} - -fn diverging_fn() -> ! { - panic!("diverging_fn called") -} - -fn mir(d: Droppable) { - let x = Droppable; - diverging_fn(); - drop(x); - drop(d); -} - -fn main() { - mir(Droppable); -} diff --git a/src/test/run-fail/mod-zero.rs b/src/test/run-fail/mod-zero.rs deleted file mode 100644 index ac2959fcd3853..0000000000000 --- a/src/test/run-fail/mod-zero.rs +++ /dev/null @@ -1,6 +0,0 @@ -// error-pattern:attempt to calculate the remainder with a divisor of zero - -fn main() { - let y = 0; - let _z = 1 % y; -} diff --git a/src/test/run-fail/overflowing-lsh-1.rs b/src/test/run-fail/overflowing-lsh-1.rs deleted file mode 100644 index 977cfea0fe05d..0000000000000 --- a/src/test/run-fail/overflowing-lsh-1.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift left with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] - -fn main() { - let _x = 1_i32 << 32; -} diff --git a/src/test/run-fail/overflowing-lsh-2.rs b/src/test/run-fail/overflowing-lsh-2.rs deleted file mode 100644 index 3517dacde3aa3..0000000000000 --- a/src/test/run-fail/overflowing-lsh-2.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift left with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] - -fn main() { - let _x = 1 << -1; -} diff --git a/src/test/run-fail/overflowing-lsh-3.rs b/src/test/run-fail/overflowing-lsh-3.rs deleted file mode 100644 index 4a575c3fa7f6a..0000000000000 --- a/src/test/run-fail/overflowing-lsh-3.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift left with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] - -fn main() { - let _x = 1_u64 << 64; -} diff --git a/src/test/run-fail/overflowing-rsh-1.rs b/src/test/run-fail/overflowing-rsh-1.rs deleted file mode 100644 index 4592b2b6260bd..0000000000000 --- a/src/test/run-fail/overflowing-rsh-1.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift right with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] - -fn main() { - let _x = -1_i32 >> 32; -} diff --git a/src/test/run-fail/overflowing-rsh-2.rs b/src/test/run-fail/overflowing-rsh-2.rs deleted file mode 100644 index 066267b770db2..0000000000000 --- a/src/test/run-fail/overflowing-rsh-2.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift right with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] - -fn main() { - let _x = -1_i32 >> -1; -} diff --git a/src/test/run-fail/overflowing-rsh-3.rs b/src/test/run-fail/overflowing-rsh-3.rs deleted file mode 100644 index 67e78482866cb..0000000000000 --- a/src/test/run-fail/overflowing-rsh-3.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift right with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] - -fn main() { - let _x = -1_i64 >> 64; -} diff --git a/src/test/run-fail/overflowing-rsh-5.rs b/src/test/run-fail/overflowing-rsh-5.rs deleted file mode 100644 index 20ef324a82aeb..0000000000000 --- a/src/test/run-fail/overflowing-rsh-5.rs +++ /dev/null @@ -1,9 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift right with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] - -fn main() { - let _n = 1i64 >> [64][0]; -} diff --git a/src/test/run-fail/overflowing-rsh-6.rs b/src/test/run-fail/overflowing-rsh-6.rs deleted file mode 100644 index 589a98bab0401..0000000000000 --- a/src/test/run-fail/overflowing-rsh-6.rs +++ /dev/null @@ -1,10 +0,0 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift right with overflow' -// compile-flags: -C debug-assertions - -#![warn(arithmetic_overflow)] -#![warn(const_err)] -#![feature(const_indexing)] - -fn main() { - let _n = 1i64 >> [64][0]; -} diff --git a/src/test/run-fail/panic-arg.rs b/src/test/run-fail/panic-arg.rs deleted file mode 100644 index c164ff94630b1..0000000000000 --- a/src/test/run-fail/panic-arg.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:woe -fn f(a: isize) { - println!("{}", a); -} - -fn main() { - f(panic!("woe")); -} diff --git a/src/test/run-fail/panic-macro-any-wrapped.rs b/src/test/run-fail/panic-macro-any-wrapped.rs deleted file mode 100644 index 83eb39a538f16..0000000000000 --- a/src/test/run-fail/panic-macro-any-wrapped.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'Box' - -fn main() { - panic!(Box::new(612_i64)); -} diff --git a/src/test/run-fail/panic-macro-any.rs b/src/test/run-fail/panic-macro-any.rs deleted file mode 100644 index 72d42e5b799cd..0000000000000 --- a/src/test/run-fail/panic-macro-any.rs +++ /dev/null @@ -1,7 +0,0 @@ -// error-pattern:panicked at 'Box' - -#![feature(box_syntax)] - -fn main() { - panic!(box 413 as Box<::std::any::Any + Send>); -} diff --git a/src/test/run-fail/panic-macro-explicit.rs b/src/test/run-fail/panic-macro-explicit.rs deleted file mode 100644 index f632034807cd4..0000000000000 --- a/src/test/run-fail/panic-macro-explicit.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'explicit panic' - -fn main() { - panic!(); -} diff --git a/src/test/run-fail/panic-macro-fmt.rs b/src/test/run-fail/panic-macro-fmt.rs deleted file mode 100644 index 658ae56e7e49f..0000000000000 --- a/src/test/run-fail/panic-macro-fmt.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'test-fail-fmt 42 rust' - -fn main() { - panic!("test-fail-fmt {} {}", 42, "rust"); -} diff --git a/src/test/run-fail/panic-macro-owned.rs b/src/test/run-fail/panic-macro-owned.rs deleted file mode 100644 index 9b935717638bc..0000000000000 --- a/src/test/run-fail/panic-macro-owned.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'test-fail-owned' - -fn main() { - panic!("test-fail-owned"); -} diff --git a/src/test/run-fail/panic-macro-static.rs b/src/test/run-fail/panic-macro-static.rs deleted file mode 100644 index 31ac488beb234..0000000000000 --- a/src/test/run-fail/panic-macro-static.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:panicked at 'test-fail-static' - -fn main() { - panic!("test-fail-static"); -} diff --git a/src/test/run-fail/panic-main.rs b/src/test/run-fail/panic-main.rs deleted file mode 100644 index 881eb7b5823b7..0000000000000 --- a/src/test/run-fail/panic-main.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:moop -fn main() { - panic!("moop"); -} diff --git a/src/test/run-fail/panic-take-handler-nop.rs b/src/test/run-fail/panic-take-handler-nop.rs deleted file mode 100644 index bb191a38f8473..0000000000000 --- a/src/test/run-fail/panic-take-handler-nop.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:thread 'main' panicked at 'foobar' - -use std::panic; - -fn main() { - panic::take_hook(); - panic!("foobar"); -} diff --git a/src/test/run-fail/panic.rs b/src/test/run-fail/panic.rs deleted file mode 100644 index 95f20dedad26c..0000000000000 --- a/src/test/run-fail/panic.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:1 == 2 -fn main() { - assert!(1 == 2); -} diff --git a/src/test/run-fail/promoted_div_by_zero.rs b/src/test/run-fail/promoted_div_by_zero.rs deleted file mode 100644 index dc6719ce025f2..0000000000000 --- a/src/test/run-fail/promoted_div_by_zero.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![allow(unconditional_panic, const_err)] - -// error-pattern: attempt to divide by zero - -fn main() { - let x = &(1 / (1 - 1)); -} diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-never.rs b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-never.rs deleted file mode 100644 index cb37b8e067090..0000000000000 --- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-never.rs +++ /dev/null @@ -1,5 +0,0 @@ -// error-pattern:oh, dear - -fn main() -> ! { - panic!("oh, dear"); -} diff --git a/src/test/run-fail/test-panic.rs b/src/test/run-fail/test-panic.rs deleted file mode 100644 index 92f5146b710f2..0000000000000 --- a/src/test/run-fail/test-panic.rs +++ /dev/null @@ -1,9 +0,0 @@ -// check-stdout -// error-pattern:thread 'test_foo' panicked at -// compile-flags: --test -// ignore-emscripten - -#[test] -fn test_foo() { - panic!() -} diff --git a/src/test/run-fail/test-should-panic-bad-message.rs b/src/test/run-fail/test-should-panic-bad-message.rs deleted file mode 100644 index b73d4d7377a34..0000000000000 --- a/src/test/run-fail/test-should-panic-bad-message.rs +++ /dev/null @@ -1,9 +0,0 @@ -// compile-flags: --test - -// error-pattern:panicked at 'bar' -// check-stdout -#[test] -#[should_panic(expected = "foo")] -pub fn test_bar() { - panic!("bar") -} diff --git a/src/test/run-fail/test-should-panic-no-message.rs b/src/test/run-fail/test-should-panic-no-message.rs deleted file mode 100644 index b18389ec7440e..0000000000000 --- a/src/test/run-fail/test-should-panic-no-message.rs +++ /dev/null @@ -1,9 +0,0 @@ -// compile-flags: --test - -// error-pattern:panicked at 'explicit panic' -// check-stdout -#[test] -#[should_panic(expected = "foo")] -pub fn test_explicit() { - panic!() -} diff --git a/src/test/run-fail/unimplemented-macro-panic.rs b/src/test/run-fail/unimplemented-macro-panic.rs deleted file mode 100644 index 4d9cb740fc609..0000000000000 --- a/src/test/run-fail/unimplemented-macro-panic.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:not implemented -fn main() { - unimplemented!() -} diff --git a/src/test/run-fail/unreachable-fmt-msg.rs b/src/test/run-fail/unreachable-fmt-msg.rs deleted file mode 100644 index ac2a52163b4de..0000000000000 --- a/src/test/run-fail/unreachable-fmt-msg.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:internal error: entered unreachable code: 6 is not prime -fn main() { - unreachable!("{} is not {}", 6u32, "prime"); -} diff --git a/src/test/run-fail/unreachable-macro-panic.rs b/src/test/run-fail/unreachable-macro-panic.rs deleted file mode 100644 index 597a01447228d..0000000000000 --- a/src/test/run-fail/unreachable-macro-panic.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:internal error: entered unreachable code -fn main() { - unreachable!() -} diff --git a/src/test/run-fail/unreachable-static-msg.rs b/src/test/run-fail/unreachable-static-msg.rs deleted file mode 100644 index 40a2881cc5714..0000000000000 --- a/src/test/run-fail/unreachable-static-msg.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:internal error: entered unreachable code: uhoh -fn main() { - unreachable!("uhoh") -} diff --git a/src/test/run-fail/unreachable.rs b/src/test/run-fail/unreachable.rs deleted file mode 100644 index 597a01447228d..0000000000000 --- a/src/test/run-fail/unreachable.rs +++ /dev/null @@ -1,4 +0,0 @@ -// error-pattern:internal error: entered unreachable code -fn main() { - unreachable!() -} diff --git a/src/test/run-fail/unwind-interleaved.rs b/src/test/run-fail/unwind-interleaved.rs deleted file mode 100644 index c163678ae9873..0000000000000 --- a/src/test/run-fail/unwind-interleaved.rs +++ /dev/null @@ -1,14 +0,0 @@ -// error-pattern:fail - -fn a() {} - -fn b() { - panic!(); -} - -fn main() { - let _x = vec![0]; - a(); - let _y = vec![0]; - b(); -} diff --git a/src/test/run-fail/unwind-rec.rs b/src/test/run-fail/unwind-rec.rs deleted file mode 100644 index 83ac19ff4a533..0000000000000 --- a/src/test/run-fail/unwind-rec.rs +++ /dev/null @@ -1,14 +0,0 @@ -// error-pattern:fail - - -fn build() -> Vec { - panic!(); -} - -struct Blk { - node: Vec, -} - -fn main() { - let _blk = Blk { node: build() }; -} diff --git a/src/test/run-fail/unwind-unique.rs b/src/test/run-fail/unwind-unique.rs deleted file mode 100644 index 7b761faad9534..0000000000000 --- a/src/test/run-fail/unwind-unique.rs +++ /dev/null @@ -1,10 +0,0 @@ -// error-pattern:fail - -fn failfn() { - panic!(); -} - -fn main() { - Box::new(0); - failfn(); -} diff --git a/src/test/run-make-fulldeps/extern-fn-reachable/main.rs b/src/test/run-make-fulldeps/extern-fn-reachable/main.rs index a9d28d1bebeb7..c1de647758585 100644 --- a/src/test/run-make-fulldeps/extern-fn-reachable/main.rs +++ b/src/test/run-make-fulldeps/extern-fn-reachable/main.rs @@ -8,7 +8,7 @@ use std::path::Path; pub fn main() { unsafe { let path = Path::new("libdylib.so"); - let a = DynamicLibrary::open(Some(&path)).unwrap(); + let a = DynamicLibrary::open(&path).unwrap(); assert!(a.symbol::("fun1").is_ok()); assert!(a.symbol::("fun2").is_ok()); assert!(a.symbol::("fun3").is_ok()); diff --git a/src/test/run-make-fulldeps/hotplug_codegen_backend/Makefile b/src/test/run-make-fulldeps/hotplug_codegen_backend/Makefile index e203ec2737fc7..d8ceace7fff25 100644 --- a/src/test/run-make-fulldeps/hotplug_codegen_backend/Makefile +++ b/src/test/run-make-fulldeps/hotplug_codegen_backend/Makefile @@ -1,5 +1,7 @@ include ../tools.mk +# ignore-stage1 + all: /bin/echo || exit 0 # This test requires /bin/echo to exist $(RUSTC) the_backend.rs --crate-name the_backend --crate-type dylib \ diff --git a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index 0e6c39e0affca..efc62361694a5 100644 --- a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -1,7 +1,8 @@ #![feature(rustc_private)] -extern crate rustc; extern crate rustc_codegen_ssa; +extern crate rustc_errors; +extern crate rustc_middle; #[macro_use] extern crate rustc_data_structures; extern crate rustc_driver; @@ -11,21 +12,20 @@ extern crate rustc_span; extern crate rustc_symbol_mangling; extern crate rustc_target; -use rustc::dep_graph::DepGraph; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn}; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; -use rustc::util::common::ErrorReported; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::owning_ref::OwningRef; use rustc_data_structures::sync::MetadataRef; +use rustc_errors::ErrorReported; +use rustc_middle::dep_graph::DepGraph; +use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_session::config::OutputFilenames; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::Target; use std::any::Any; use std::path::Path; -use std::sync::Arc; pub struct NoLlvmMetadataLoader; @@ -53,10 +53,10 @@ impl CodegenBackend for TheBackend { rustc_symbol_mangling::provide(providers); providers.target_features_whitelist = |tcx, _cnum| { - tcx.arena.alloc(Default::default()) // Just a dummy + Default::default() // Just a dummy }; providers.is_reachable_non_generic = |_tcx, _defid| true; - providers.exported_symbols = |_tcx, _crate| Arc::new(Vec::new()); + providers.exported_symbols = |_tcx, _crate| &[]; } fn provide_extern(&self, providers: &mut Providers) { diff --git a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs index 936001c43a48f..2e81667cf39c6 100644 --- a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs +++ b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs @@ -1,11 +1,11 @@ -#![feature(asm)] +#![feature(llvm_asm)] #![crate_type="lib"] #[deny(unreachable_code)] pub fn exit(n: usize) -> i32 { unsafe { // Pretend this asm is an exit() syscall. - asm!("" :: "r"(n) :: "volatile"); + llvm_asm!("" :: "r"(n) :: "volatile"); // Can't actually reach this point, but rustc doesn't know that. } // This return value is just here to generate some extra code for a return diff --git a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs index 00b09cb9460b1..fb3848b0db617 100644 --- a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs +++ b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs @@ -1,4 +1,4 @@ -#![feature(asm, core_intrinsics)] +#![feature(llvm_asm, core_intrinsics)] #![crate_type="lib"] use std::intrinsics; @@ -7,7 +7,7 @@ use std::intrinsics; pub fn exit(n: usize) -> i32 { unsafe { // Pretend this asm is an exit() syscall. - asm!("" :: "r"(n) :: "volatile"); + llvm_asm!("" :: "r"(n) :: "volatile"); intrinsics::unreachable() } // This return value is just here to generate some extra code for a return diff --git a/src/test/run-make-fulldeps/link-args-order/Makefile b/src/test/run-make-fulldeps/link-args-order/Makefile new file mode 100644 index 0000000000000..98c1e0eac3b0e --- /dev/null +++ b/src/test/run-make-fulldeps/link-args-order/Makefile @@ -0,0 +1,10 @@ +# ignore-msvc + +-include ../tools.mk + +RUSTC_FLAGS = -C linker-flavor=ld -C link-arg=a -C link-args="b c" -C link-args="d e" -C link-arg=f +RUSTC_FLAGS_PRE = -C linker-flavor=ld -Z pre-link-arg=a -Z pre-link-args="b c" -Z pre-link-args="d e" -Z pre-link-arg=f + +all: + $(RUSTC) $(RUSTC_FLAGS) empty.rs 2>&1 | $(CGREP) '"a" "b" "c" "d" "e" "f" "g"' + $(RUSTC) $(RUSTC_FLAGS_PRE) empty.rs 2>&1 | $(CGREP) '"a" "b" "c" "d" "e" "f"' diff --git a/src/test/run-make-fulldeps/link-args-order/empty.rs b/src/test/run-make-fulldeps/link-args-order/empty.rs new file mode 100644 index 0000000000000..2439171004b5f --- /dev/null +++ b/src/test/run-make-fulldeps/link-args-order/empty.rs @@ -0,0 +1,6 @@ +#![feature(link_args)] + +#[link_args = "g"] +extern "C" {} + +fn main() {} diff --git a/src/test/run-make-fulldeps/no-integrated-as/Makefile b/src/test/run-make-fulldeps/no-integrated-as/Makefile deleted file mode 100644 index 1567b325d4fe1..0000000000000 --- a/src/test/run-make-fulldeps/no-integrated-as/Makefile +++ /dev/null @@ -1,8 +0,0 @@ --include ../tools.mk - -# only-linux -# only-x86_64 - -all: - $(RUSTC) hello.rs -C no_integrated_as - $(call RUN,hello) diff --git a/src/test/run-make-fulldeps/no-integrated-as/hello.rs b/src/test/run-make-fulldeps/no-integrated-as/hello.rs deleted file mode 100644 index e7a11a969c037..0000000000000 --- a/src/test/run-make-fulldeps/no-integrated-as/hello.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/src/test/run-make-fulldeps/output-type-permutations/Makefile b/src/test/run-make-fulldeps/output-type-permutations/Makefile index ffd3e6da25633..b6e0cbaf5ddc7 100644 --- a/src/test/run-make-fulldeps/output-type-permutations/Makefile +++ b/src/test/run-make-fulldeps/output-type-permutations/Makefile @@ -78,7 +78,7 @@ all: rm $(TMPDIR)/$(call BIN,foo) $(RUSTC) foo.rs --crate-type=dylib --emit=link=$(TMPDIR)/$(call BIN,foo) rm $(TMPDIR)/$(call BIN,foo) - rm -f $(TMPDIR)/{lib,}foo.{dll.exp,dll.lib,pdb,dll.a,exe.lib} + rm -f $(TMPDIR)/{lib,}foo.{dll.exp,dll.lib,pdb,dll.a,exe.a} [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] || (ls -1 $(TMPDIR) && exit 1) $(RUSTC) foo.rs --crate-type=staticlib -o $(TMPDIR)/foo diff --git a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile index 35317dca1e824..5d46be87eac6b 100644 --- a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile @@ -11,10 +11,7 @@ LOG := $(TMPDIR)/log.txt # are compiled with address sanitizer, and we assert that a fault in the cdylib # is correctly detected. -# See comment in sanitizer-address/Makefile for why this is here -EXTRA_RUSTFLAG=-C relocation-model=dynamic-no-pic - all: - $(RUSTC) -g -Z sanitizer=address --crate-type cdylib --target $(TARGET) $(EXTRA_RUSTFLAG) library.rs - $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) $(EXTRA_RUSTFLAG) -llibrary program.rs + $(RUSTC) -g -Z sanitizer=address --crate-type cdylib --target $(TARGET) library.rs + $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) -llibrary program.rs LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow diff --git a/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile index 24d2ebd8f48aa..f62c3a6654ed4 100644 --- a/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile @@ -11,10 +11,7 @@ LOG := $(TMPDIR)/log.txt # are compiled with address sanitizer, and we assert that a fault in the dylib # is correctly detected. -# See comment in sanitizer-address/Makefile for why this is here -EXTRA_RUSTFLAG=-C relocation-model=dynamic-no-pic - all: - $(RUSTC) -g -Z sanitizer=address --crate-type dylib --target $(TARGET) $(EXTRA_RUSTFLAG) library.rs - $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) $(EXTRA_RUSTFLAG) -llibrary program.rs + $(RUSTC) -g -Z sanitizer=address --crate-type dylib --target $(TARGET) library.rs + $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) -llibrary program.rs LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow diff --git a/src/test/run-make-fulldeps/sysroot-crates-are-unstable/Makefile b/src/test/run-make-fulldeps/sysroot-crates-are-unstable/Makefile index a35174b3c2ac4..1e267fb9576ba 100644 --- a/src/test/run-make-fulldeps/sysroot-crates-are-unstable/Makefile +++ b/src/test/run-make-fulldeps/sysroot-crates-are-unstable/Makefile @@ -1,2 +1,2 @@ all: - python2.7 test.py + '$(PYTHON)' test.py diff --git a/src/test/run-make-fulldeps/tools.mk b/src/test/run-make-fulldeps/tools.mk index 48fd3ff724634..04bf78ed2105b 100644 --- a/src/test/run-make-fulldeps/tools.mk +++ b/src/test/run-make-fulldeps/tools.mk @@ -44,8 +44,13 @@ RUN = PATH="$(PATH):$(TARGET_RPATH_DIR)" $(RUN_BINFILE) FAIL = PATH="$(PATH):$(TARGET_RPATH_DIR)" $(RUN_BINFILE) && exit 1 || exit 0 DYLIB_GLOB = $(1)*.dll DYLIB = $(TMPDIR)/$(1).dll +ifdef IS_MSVC STATICLIB = $(TMPDIR)/$(1).lib STATICLIB_GLOB = $(1)*.lib +else +STATICLIB = $(TMPDIR)/lib$(1).a +STATICLIB_GLOB = lib$(1)*.a +endif BIN = $(1).exe LLVM_FILECHECK := $(shell cygpath -u "$(LLVM_FILECHECK)") else diff --git a/src/test/run-make-fulldeps/treat-err-as-bug/Makefile b/src/test/run-make-fulldeps/treat-err-as-bug/Makefile index 9b3bcef2faf32..57cac76aec2a5 100644 --- a/src/test/run-make-fulldeps/treat-err-as-bug/Makefile +++ b/src/test/run-make-fulldeps/treat-err-as-bug/Makefile @@ -3,3 +3,5 @@ all: $(RUSTC) err.rs -Z treat-err-as-bug 2>&1 \ | $(CGREP) "panicked at 'aborting due to \`-Z treat-err-as-bug=1\`'" + $(RUSTC) delay_span_bug.rs -Z treat-err-as-bug 2>&1 \ + | $(CGREP) "panicked at 'aborting due to \`-Z treat-err-as-bug=1\`'" diff --git a/src/test/run-make-fulldeps/treat-err-as-bug/delay_span_bug.rs b/src/test/run-make-fulldeps/treat-err-as-bug/delay_span_bug.rs new file mode 100644 index 0000000000000..dad33e498b52f --- /dev/null +++ b/src/test/run-make-fulldeps/treat-err-as-bug/delay_span_bug.rs @@ -0,0 +1,4 @@ +#![feature(rustc_attrs)] + +#[rustc_error(delay_span_bug_from_inside_query)] +fn main() {} diff --git a/src/test/run-make-fulldeps/windows-binary-no-external-deps/Makefile b/src/test/run-make-fulldeps/windows-binary-no-external-deps/Makefile new file mode 100644 index 0000000000000..f6adb6d76279d --- /dev/null +++ b/src/test/run-make-fulldeps/windows-binary-no-external-deps/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +# only-windows + +PATH=$(SYSTEMROOT)/system32 + +all: + $(RUSTC) hello.rs + $(TMPDIR)/hello.exe diff --git a/src/test/run-make-fulldeps/windows-binary-no-external-deps/hello.rs b/src/test/run-make-fulldeps/windows-binary-no-external-deps/hello.rs new file mode 100644 index 0000000000000..47ad8c634112b --- /dev/null +++ b/src/test/run-make-fulldeps/windows-binary-no-external-deps/hello.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello World!"); +} diff --git a/src/test/run-make/wasm-panic-small/foo.rs b/src/test/run-make/wasm-panic-small/foo.rs index fd3dddb18eb20..6df52affe3993 100644 --- a/src/test/run-make/wasm-panic-small/foo.rs +++ b/src/test/run-make/wasm-panic-small/foo.rs @@ -23,5 +23,5 @@ pub fn foo() { pub fn foo() -> usize { use std::cell::Cell; thread_local!(static A: Cell> = Cell::new(Vec::new())); - A.try_with(|x| x.replace(Vec::new()).len()).unwrap_or(0) + A.try_with(|x| x.take().len()).unwrap_or(0) } diff --git a/src/test/rustdoc-js-std/alias-2.js b/src/test/rustdoc-js-std/alias-2.js index f3c6713692b59..798fa29efbd2d 100644 --- a/src/test/rustdoc-js-std/alias-2.js +++ b/src/test/rustdoc-js-std/alias-2.js @@ -1,10 +1,10 @@ -// ignore-order - const QUERY = '+'; const EXPECTED = { 'others': [ { 'path': 'std::ops', 'name': 'AddAssign' }, { 'path': 'std::ops', 'name': 'Add' }, + { 'path': 'core::ops', 'name': 'AddAssign' }, + { 'path': 'core::ops', 'name': 'Add' }, ], }; diff --git a/src/test/rustdoc-js/doc-alias.js b/src/test/rustdoc-js/doc-alias.js new file mode 100644 index 0000000000000..896808d415780 --- /dev/null +++ b/src/test/rustdoc-js/doc-alias.js @@ -0,0 +1,263 @@ +// exact-check + +const QUERY = [ + 'StructItem', + 'StructFieldItem', + 'StructMethodItem', + 'ImplTraitItem', + 'ImplAssociatedConstItem', + 'ImplTraitFunction', + 'EnumItem', + 'VariantItem', + 'EnumMethodItem', + 'TypedefItem', + 'TraitItem', + 'TraitTypeItem', + 'AssociatedConstItem', + 'TraitFunctionItem', + 'FunctionItem', + 'ModuleItem', + 'ConstItem', + 'StaticItem', + 'UnionItem', + 'UnionFieldItem', + 'UnionMethodItem', + 'MacroItem', +]; + +const EXPECTED = [ + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Struct', + 'alias': 'StructItem', + 'href': '../doc_alias/struct.Struct.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Struct', + 'name': 'field', + 'alias': 'StructFieldItem', + 'href': '../doc_alias/struct.Struct.html#structfield.field', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Struct', + 'name': 'method', + 'alias': 'StructMethodItem', + 'href': '../doc_alias/struct.Struct.html#method.method', + 'is_alias': true + }, + ], + }, + { + // ImplTraitItem + 'others': [], + }, + { + // ImplAssociatedConstItem + 'others': [], + }, + { + 'others': [ + { + 'path': 'doc_alias::Struct', + 'name': 'function', + 'alias': 'ImplTraitFunction', + 'href': '../doc_alias/struct.Struct.html#method.function', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Enum', + 'alias': 'EnumItem', + 'href': '../doc_alias/enum.Enum.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Enum', + 'name': 'Variant', + 'alias': 'VariantItem', + 'href': '../doc_alias/enum.Enum.html#variant.Variant', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Enum', + 'name': 'method', + 'alias': 'EnumMethodItem', + 'href': '../doc_alias/enum.Enum.html#method.method', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Typedef', + 'alias': 'TypedefItem', + 'href': '../doc_alias/type.Typedef.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Trait', + 'alias': 'TraitItem', + 'href': '../doc_alias/trait.Trait.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Trait', + 'name': 'Target', + 'alias': 'TraitTypeItem', + 'href': '../doc_alias/trait.Trait.html#associatedtype.Target', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Trait', + 'name': 'AssociatedConst', + 'alias': 'AssociatedConstItem', + 'href': '../doc_alias/trait.Trait.html#associatedconstant.AssociatedConst', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Trait', + 'name': 'function', + 'alias': 'TraitFunctionItem', + 'href': '../doc_alias/trait.Trait.html#tymethod.function', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'function', + 'alias': 'FunctionItem', + 'href': '../doc_alias/fn.function.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Module', + 'alias': 'ModuleItem', + 'href': '../doc_alias/Module/index.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Const', + 'alias': 'ConstItem', + 'href': '../doc_alias/constant.Const.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Static', + 'alias': 'StaticItem', + 'href': '../doc_alias/static.Static.html', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Union', + 'alias': 'UnionItem', + 'href': '../doc_alias/union.Union.html', + 'is_alias': true + }, + // Not an alias! + { + 'path': 'doc_alias::Union', + 'name': 'union_item', + 'href': '../doc_alias/union.Union.html#structfield.union_item' + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Union', + 'name': 'union_item', + 'alias': 'UnionFieldItem', + 'href': '../doc_alias/union.Union.html#structfield.union_item', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias::Union', + 'name': 'method', + 'alias': 'UnionMethodItem', + 'href': '../doc_alias/union.Union.html#method.method', + 'is_alias': true + }, + ], + }, + { + 'others': [ + { + 'path': 'doc_alias', + 'name': 'Macro', + 'alias': 'MacroItem', + 'href': '../doc_alias/macro.Macro.html', + 'is_alias': true + }, + ], + }, +]; diff --git a/src/test/rustdoc-js/doc-alias.rs b/src/test/rustdoc-js/doc-alias.rs new file mode 100644 index 0000000000000..84c638a199507 --- /dev/null +++ b/src/test/rustdoc-js/doc-alias.rs @@ -0,0 +1,79 @@ +#![feature(doc_alias)] + +#[doc(alias = "StructItem")] +pub struct Struct { + #[doc(alias = "StructFieldItem")] + pub field: u32, +} + +impl Struct { + #[doc(alias = "StructMethodItem")] + pub fn method(&self) {} +} + +impl Trait for Struct { + // Shouldn't be listed in aliases! + #[doc(alias = "ImplTraitItem")] + type Target = u32; + // Shouldn't be listed in aliases! + #[doc(alias = "ImplAssociatedConstItem")] + const AssociatedConst: i32 = 12; + + #[doc(alias = "ImplTraitFunction")] + fn function() -> Self::Target { 0 } +} + +#[doc(alias = "EnumItem")] +pub enum Enum { + #[doc(alias = "VariantItem")] + Variant, +} + +impl Enum { + #[doc(alias = "EnumMethodItem")] + pub fn method(&self) {} +} + +#[doc(alias = "TypedefItem")] +pub type Typedef = i32; + +#[doc(alias = "TraitItem")] +pub trait Trait { + #[doc(alias = "TraitTypeItem")] + type Target; + #[doc(alias = "AssociatedConstItem")] + const AssociatedConst: i32; + + #[doc(alias = "TraitFunctionItem")] + fn function() -> Self::Target; +} + +#[doc(alias = "FunctionItem")] +pub fn function() {} + +#[doc(alias = "ModuleItem")] +pub mod Module {} + +#[doc(alias = "ConstItem")] +pub const Const: u32 = 0; + +#[doc(alias = "StaticItem")] +pub static Static: u32 = 0; + +#[doc(alias = "UnionItem")] +pub union Union { + #[doc(alias = "UnionFieldItem")] + pub union_item: u32, + pub y: f32, +} + +impl Union { + #[doc(alias = "UnionMethodItem")] + pub fn method(&self) {} +} + +#[doc(alias = "MacroItem")] +#[macro_export] +macro_rules! Macro { + () => {} +} diff --git a/src/test/rustdoc-ui/cfg-test.rs b/src/test/rustdoc-ui/cfg-test.rs index 587fe21f8fa73..597c86a1f19ca 100644 --- a/src/test/rustdoc-ui/cfg-test.rs +++ b/src/test/rustdoc-ui/cfg-test.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // compile-flags:--test --test-args --test-threads=1 // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" diff --git a/src/test/rustdoc-ui/check-attr-test.rs b/src/test/rustdoc-ui/check-attr-test.rs new file mode 100644 index 0000000000000..c4140bbb70a78 --- /dev/null +++ b/src/test/rustdoc-ui/check-attr-test.rs @@ -0,0 +1,38 @@ +// compile-flags:--test + +#![deny(invalid_codeblock_attribute)] + +/// foo +/// +/// ```compile-fail,compilefail,comPile_fail +/// boo +/// ``` +pub fn foo() {} + +/// bar +/// +/// ```should-panic,shouldpanic,shOuld_panic +/// boo +/// ``` +pub fn bar() {} + +/// foobar +/// +/// ```no-run,norun,nO_run +/// boo +/// ``` +pub fn foobar() {} + +/// barfoo +/// +/// ```allow-fail,allowfail,allOw_fail +/// boo +/// ``` +pub fn barfoo() {} + +/// b +/// +/// ```test-harness,testharness,tesT_harness +/// boo +/// ``` +pub fn b() {} diff --git a/src/test/rustdoc-ui/check-attr-test.stderr b/src/test/rustdoc-ui/check-attr-test.stderr new file mode 100644 index 0000000000000..45a2d6ec15e7d --- /dev/null +++ b/src/test/rustdoc-ui/check-attr-test.stderr @@ -0,0 +1,187 @@ +error: unknown attribute `compile-fail`. Did you mean `compile_fail`? + --> $DIR/check-attr-test.rs:5:1 + | +5 | / /// foo +6 | | /// +7 | | /// ```compile-fail,compilefail,comPile_fail +8 | | /// boo +9 | | /// ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/check-attr-test.rs:3:9 + | +3 | #![deny(invalid_codeblock_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `compilefail`. Did you mean `compile_fail`? + --> $DIR/check-attr-test.rs:5:1 + | +5 | / /// foo +6 | | /// +7 | | /// ```compile-fail,compilefail,comPile_fail +8 | | /// boo +9 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `comPile_fail`. Did you mean `compile_fail`? + --> $DIR/check-attr-test.rs:5:1 + | +5 | / /// foo +6 | | /// +7 | | /// ```compile-fail,compilefail,comPile_fail +8 | | /// boo +9 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `should-panic`. Did you mean `should_panic`? + --> $DIR/check-attr-test.rs:12:1 + | +12 | / /// bar +13 | | /// +14 | | /// ```should-panic,shouldpanic,shOuld_panic +15 | | /// boo +16 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `shouldpanic`. Did you mean `should_panic`? + --> $DIR/check-attr-test.rs:12:1 + | +12 | / /// bar +13 | | /// +14 | | /// ```should-panic,shouldpanic,shOuld_panic +15 | | /// boo +16 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `shOuld_panic`. Did you mean `should_panic`? + --> $DIR/check-attr-test.rs:12:1 + | +12 | / /// bar +13 | | /// +14 | | /// ```should-panic,shouldpanic,shOuld_panic +15 | | /// boo +16 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `no-run`. Did you mean `no_run`? + --> $DIR/check-attr-test.rs:19:1 + | +19 | / /// foobar +20 | | /// +21 | | /// ```no-run,norun,nO_run +22 | | /// boo +23 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `norun`. Did you mean `no_run`? + --> $DIR/check-attr-test.rs:19:1 + | +19 | / /// foobar +20 | | /// +21 | | /// ```no-run,norun,nO_run +22 | | /// boo +23 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `nO_run`. Did you mean `no_run`? + --> $DIR/check-attr-test.rs:19:1 + | +19 | / /// foobar +20 | | /// +21 | | /// ```no-run,norun,nO_run +22 | | /// boo +23 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allow-fail`. Did you mean `allow_fail`? + --> $DIR/check-attr-test.rs:26:1 + | +26 | / /// barfoo +27 | | /// +28 | | /// ```allow-fail,allowfail,allOw_fail +29 | | /// boo +30 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allowfail`. Did you mean `allow_fail`? + --> $DIR/check-attr-test.rs:26:1 + | +26 | / /// barfoo +27 | | /// +28 | | /// ```allow-fail,allowfail,allOw_fail +29 | | /// boo +30 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allOw_fail`. Did you mean `allow_fail`? + --> $DIR/check-attr-test.rs:26:1 + | +26 | / /// barfoo +27 | | /// +28 | | /// ```allow-fail,allowfail,allOw_fail +29 | | /// boo +30 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `test-harness`. Did you mean `test_harness`? + --> $DIR/check-attr-test.rs:33:1 + | +33 | / /// b +34 | | /// +35 | | /// ```test-harness,testharness,tesT_harness +36 | | /// boo +37 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `testharness`. Did you mean `test_harness`? + --> $DIR/check-attr-test.rs:33:1 + | +33 | / /// b +34 | | /// +35 | | /// ```test-harness,testharness,tesT_harness +36 | | /// boo +37 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `tesT_harness`. Did you mean `test_harness`? + --> $DIR/check-attr-test.rs:33:1 + | +33 | / /// b +34 | | /// +35 | | /// ```test-harness,testharness,tesT_harness +36 | | /// boo +37 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: aborting due to 15 previous errors + diff --git a/src/test/rustdoc-ui/check-attr.rs b/src/test/rustdoc-ui/check-attr.rs new file mode 100644 index 0000000000000..a93ec29131907 --- /dev/null +++ b/src/test/rustdoc-ui/check-attr.rs @@ -0,0 +1,51 @@ +#![deny(invalid_codeblock_attribute)] + +/// foo +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```compile-fail,compilefail,comPile_fail +/// boo +/// ``` +pub fn foo() {} + +/// bar +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```should-panic,shouldpanic,sHould_panic +/// boo +/// ``` +pub fn bar() {} + +/// foobar +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```no-run,norun,no_Run +/// boo +/// ``` +pub fn foobar() {} + +/// barfoo +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```allow-fail,allowfail,alLow_fail +/// boo +/// ``` +pub fn barfoo() {} + +/// b +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```test-harness,testharness,teSt_harness +/// boo +/// ``` +pub fn b() {} diff --git a/src/test/rustdoc-ui/check-attr.stderr b/src/test/rustdoc-ui/check-attr.stderr new file mode 100644 index 0000000000000..5d6939bd09205 --- /dev/null +++ b/src/test/rustdoc-ui/check-attr.stderr @@ -0,0 +1,217 @@ +error: unknown attribute `compile-fail`. Did you mean `compile_fail`? + --> $DIR/check-attr.rs:3:1 + | +LL | / /// foo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/check-attr.rs:1:9 + | +LL | #![deny(invalid_codeblock_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `compilefail`. Did you mean `compile_fail`? + --> $DIR/check-attr.rs:3:1 + | +LL | / /// foo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `comPile_fail`. Did you mean `compile_fail`? + --> $DIR/check-attr.rs:3:1 + | +LL | / /// foo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `should-panic`. Did you mean `should_panic`? + --> $DIR/check-attr.rs:13:1 + | +LL | / /// bar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `shouldpanic`. Did you mean `should_panic`? + --> $DIR/check-attr.rs:13:1 + | +LL | / /// bar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `sHould_panic`. Did you mean `should_panic`? + --> $DIR/check-attr.rs:13:1 + | +LL | / /// bar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `no-run`. Did you mean `no_run`? + --> $DIR/check-attr.rs:23:1 + | +LL | / /// foobar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `norun`. Did you mean `no_run`? + --> $DIR/check-attr.rs:23:1 + | +LL | / /// foobar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `no_Run`. Did you mean `no_run`? + --> $DIR/check-attr.rs:23:1 + | +LL | / /// foobar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allow-fail`. Did you mean `allow_fail`? + --> $DIR/check-attr.rs:33:1 + | +LL | / /// barfoo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allowfail`. Did you mean `allow_fail`? + --> $DIR/check-attr.rs:33:1 + | +LL | / /// barfoo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `alLow_fail`. Did you mean `allow_fail`? + --> $DIR/check-attr.rs:33:1 + | +LL | / /// barfoo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `test-harness`. Did you mean `test_harness`? + --> $DIR/check-attr.rs:43:1 + | +LL | / /// b +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `testharness`. Did you mean `test_harness`? + --> $DIR/check-attr.rs:43:1 + | +LL | / /// b +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `teSt_harness`. Did you mean `test_harness`? + --> $DIR/check-attr.rs:43:1 + | +LL | / /// b +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: aborting due to 15 previous errors + diff --git a/src/test/rustdoc-ui/coverage/basic.rs b/src/test/rustdoc-ui/coverage/basic.rs index d25ac633d7bf2..98507f99e8d4b 100644 --- a/src/test/rustdoc-ui/coverage/basic.rs +++ b/src/test/rustdoc-ui/coverage/basic.rs @@ -1,5 +1,5 @@ // compile-flags:-Z unstable-options --show-coverage -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(extern_types)] diff --git a/src/test/rustdoc-ui/coverage/empty.rs b/src/test/rustdoc-ui/coverage/empty.rs index 27bcf6f39383a..55a87e9d97b31 100644 --- a/src/test/rustdoc-ui/coverage/empty.rs +++ b/src/test/rustdoc-ui/coverage/empty.rs @@ -1,4 +1,4 @@ // compile-flags:-Z unstable-options --show-coverage -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // an empty crate still has one item to document: the crate root diff --git a/src/test/rustdoc-ui/coverage/enums.rs b/src/test/rustdoc-ui/coverage/enums.rs index e4171d7cfb250..a4ae36d6837af 100644 --- a/src/test/rustdoc-ui/coverage/enums.rs +++ b/src/test/rustdoc-ui/coverage/enums.rs @@ -1,5 +1,5 @@ // compile-flags:-Z unstable-options --show-coverage -// build-pass (FIXME(62277): could be check-pass?) +// check-pass //! (remember the crate root is still a module) diff --git a/src/test/rustdoc-ui/coverage/exotic.rs b/src/test/rustdoc-ui/coverage/exotic.rs index 414d6f8405816..281ce571aa03f 100644 --- a/src/test/rustdoc-ui/coverage/exotic.rs +++ b/src/test/rustdoc-ui/coverage/exotic.rs @@ -1,5 +1,5 @@ // compile-flags:-Z unstable-options --show-coverage -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(doc_keyword)] diff --git a/src/test/rustdoc-ui/coverage/json.rs b/src/test/rustdoc-ui/coverage/json.rs index b1220b32e9194..2bd6a312ab583 100644 --- a/src/test/rustdoc-ui/coverage/json.rs +++ b/src/test/rustdoc-ui/coverage/json.rs @@ -1,4 +1,4 @@ -// build-pass +// check-pass // compile-flags:-Z unstable-options --output-format json --show-coverage pub mod foo { diff --git a/src/test/rustdoc-ui/coverage/private.rs b/src/test/rustdoc-ui/coverage/private.rs index 6ff1bfa7275de..2a0271727f26e 100644 --- a/src/test/rustdoc-ui/coverage/private.rs +++ b/src/test/rustdoc-ui/coverage/private.rs @@ -1,5 +1,5 @@ // compile-flags:-Z unstable-options --show-coverage --document-private-items -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![allow(unused)] diff --git a/src/test/rustdoc-ui/coverage/statics-consts.rs b/src/test/rustdoc-ui/coverage/statics-consts.rs index b7d2b1dc10c62..5a35260fa3580 100644 --- a/src/test/rustdoc-ui/coverage/statics-consts.rs +++ b/src/test/rustdoc-ui/coverage/statics-consts.rs @@ -1,5 +1,5 @@ // compile-flags:-Z unstable-options --show-coverage -// build-pass (FIXME(62277): could be check-pass?) +// check-pass //! gotta make sure we can count statics and consts correctly, too diff --git a/src/test/rustdoc-ui/coverage/traits.rs b/src/test/rustdoc-ui/coverage/traits.rs index 97f73b4e1f224..7d5cf049e7fdd 100644 --- a/src/test/rustdoc-ui/coverage/traits.rs +++ b/src/test/rustdoc-ui/coverage/traits.rs @@ -1,5 +1,5 @@ // compile-flags:-Z unstable-options --show-coverage -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(trait_alias)] diff --git a/src/test/rustdoc-ui/deprecated-attrs.rs b/src/test/rustdoc-ui/deprecated-attrs.rs index 21169eeb8c83e..0d5dfa733fab6 100644 --- a/src/test/rustdoc-ui/deprecated-attrs.rs +++ b/src/test/rustdoc-ui/deprecated-attrs.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![doc(no_default_passes, passes = "collapse-docs unindent-comments")] diff --git a/src/test/rustdoc-ui/deprecated-attrs.stderr b/src/test/rustdoc-ui/deprecated-attrs.stderr index 61228034a689d..f68fb46744805 100644 --- a/src/test/rustdoc-ui/deprecated-attrs.stderr +++ b/src/test/rustdoc-ui/deprecated-attrs.stderr @@ -7,3 +7,5 @@ warning: the `#![doc(passes = "...")]` attribute is considered deprecated | = warning: see issue #44136 for more information +warning: 2 warnings emitted + diff --git a/src/test/rustdoc-ui/doc-test-doctest-feature.rs b/src/test/rustdoc-ui/doc-test-doctest-feature.rs index 984d49b43efd0..9a79fb8838351 100644 --- a/src/test/rustdoc-ui/doc-test-doctest-feature.rs +++ b/src/test/rustdoc-ui/doc-test-doctest-feature.rs @@ -1,4 +1,4 @@ -// build-pass +// check-pass // compile-flags:--test // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" diff --git a/src/test/rustdoc-ui/doc-test-rustdoc-feature.rs b/src/test/rustdoc-ui/doc-test-rustdoc-feature.rs index 62fd3da9233fa..2af5782453e6d 100644 --- a/src/test/rustdoc-ui/doc-test-rustdoc-feature.rs +++ b/src/test/rustdoc-ui/doc-test-rustdoc-feature.rs @@ -1,4 +1,4 @@ -// build-pass +// check-pass // compile-flags:--test // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.rs b/src/test/rustdoc-ui/intra-links-warning-crlf.rs index ccd2841ff0be3..18c9837b0bb45 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.rs +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.rs @@ -1,5 +1,5 @@ // ignore-tidy-cr -// build-pass +// check-pass // This file checks the spans of intra-link warnings in a file with CRLF line endings. The // .gitattributes file in this directory should enforce it. diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index e4dd13cfa0195..ac8691a8743ba 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr @@ -31,3 +31,5 @@ LL | * It also has an [error]. | = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` +warning: 4 warnings emitted + diff --git a/src/test/rustdoc-ui/intra-links-warning.rs b/src/test/rustdoc-ui/intra-links-warning.rs index b0c637521f9ae..623dcc320bb8d 100644 --- a/src/test/rustdoc-ui/intra-links-warning.rs +++ b/src/test/rustdoc-ui/intra-links-warning.rs @@ -1,4 +1,4 @@ -// build-pass +// check-pass //! Test with [Foo::baz], [Bar::foo], ... //~^ WARNING `[Foo::baz]` cannot be resolved diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 91b1fff5a3a07..914a19fc536c7 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -177,3 +177,5 @@ LL | f!("Foo\nbar [BarF] bar\nbaz"); = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +warning: 19 warnings emitted + diff --git a/src/test/rustdoc-ui/invalid-syntax.rs b/src/test/rustdoc-ui/invalid-syntax.rs index 72037dd74be35..c395a8ef3d41a 100644 --- a/src/test/rustdoc-ui/invalid-syntax.rs +++ b/src/test/rustdoc-ui/invalid-syntax.rs @@ -1,4 +1,4 @@ -// build-pass +// check-pass /// ``` /// \__________pkt->size___________/ \_result->size_/ \__pkt->size__/ diff --git a/src/test/rustdoc-ui/invalid-syntax.stderr b/src/test/rustdoc-ui/invalid-syntax.stderr index a90d3bbb979f6..9a7a4d2101362 100644 --- a/src/test/rustdoc-ui/invalid-syntax.stderr +++ b/src/test/rustdoc-ui/invalid-syntax.stderr @@ -147,3 +147,5 @@ help: mark blocks that do not contain Rust code as text LL | /// ```text | ^^^^^^^ +warning: 12 warnings emitted + diff --git a/src/test/rustdoc-ui/issue-58473-2.rs b/src/test/rustdoc-ui/issue-58473-2.rs index 1bb19353ba2f7..e5f3b4daf5729 100644 --- a/src/test/rustdoc-ui/issue-58473-2.rs +++ b/src/test/rustdoc-ui/issue-58473-2.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![deny(private_doc_tests)] diff --git a/src/test/rustdoc-ui/issue-58473.rs b/src/test/rustdoc-ui/issue-58473.rs index 6756d3b5a6051..44e1f58d0a0fb 100644 --- a/src/test/rustdoc-ui/issue-58473.rs +++ b/src/test/rustdoc-ui/issue-58473.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass pub trait Foo { /** diff --git a/src/test/rustdoc-ui/no-crate-level-doc-lint.rs b/src/test/rustdoc-ui/no-crate-level-doc-lint.rs new file mode 100644 index 0000000000000..152a7cd88bcb1 --- /dev/null +++ b/src/test/rustdoc-ui/no-crate-level-doc-lint.rs @@ -0,0 +1,3 @@ +#![deny(missing_crate_level_docs)] + +pub fn foo() {} diff --git a/src/test/rustdoc-ui/no-crate-level-doc-lint.stderr b/src/test/rustdoc-ui/no-crate-level-doc-lint.stderr new file mode 100644 index 0000000000000..6e7e2fb3eb73f --- /dev/null +++ b/src/test/rustdoc-ui/no-crate-level-doc-lint.stderr @@ -0,0 +1,12 @@ +error: no documentation found for this crate's top-level module + | +note: the lint level is defined here + --> $DIR/no-crate-level-doc-lint.rs:1:9 + | +LL | #![deny(missing_crate_level_docs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = help: The following guide may be of use: + https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html + +error: aborting due to previous error + diff --git a/src/test/rustdoc-ui/test-no_std.rs b/src/test/rustdoc-ui/test-no_std.rs index 166a87382cb65..af4843ad32405 100644 --- a/src/test/rustdoc-ui/test-no_std.rs +++ b/src/test/rustdoc-ui/test-no_std.rs @@ -1,6 +1,6 @@ // compile-flags:--test // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" -// build-pass +// check-pass #![no_std] diff --git a/src/test/rustdoc-ui/unused-braces-lint.rs b/src/test/rustdoc-ui/unused-braces-lint.rs new file mode 100644 index 0000000000000..be0e31e4be2ff --- /dev/null +++ b/src/test/rustdoc-ui/unused-braces-lint.rs @@ -0,0 +1,14 @@ +// check-pass + +// This tests the bug in #70814, where the unused_braces lint triggered on the following code +// without providing a span. + +#![deny(unused_braces)] + +fn main() { + { + { + use std; + } + } +} diff --git a/src/test/rustdoc-ui/unused.rs b/src/test/rustdoc-ui/unused.rs index ffa421d4f7f2d..702b24c36c56c 100644 --- a/src/test/rustdoc-ui/unused.rs +++ b/src/test/rustdoc-ui/unused.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // This test purpose is to check that unused_imports lint isn't fired // by rustdoc. Why would it? Because when rustdoc is running, it uses diff --git a/src/test/rustdoc/auxiliary/external-macro-src.rs b/src/test/rustdoc/auxiliary/external-macro-src.rs new file mode 100644 index 0000000000000..ce20ca5c91e6b --- /dev/null +++ b/src/test/rustdoc/auxiliary/external-macro-src.rs @@ -0,0 +1,15 @@ +// compile-flags:--remap-path-prefix={{src-base}}=/does-not-exist + +#![doc(html_root_url = "https://example.com/")] + +#[macro_export] +macro_rules! make_foo { + () => { + pub struct Foo; + impl Foo { + pub fn new() -> Foo { + Foo + } + } + } +} diff --git a/src/test/rustdoc/auxiliary/issue-27362.rs b/src/test/rustdoc/auxiliary/issue-27362-aux.rs similarity index 100% rename from src/test/rustdoc/auxiliary/issue-27362.rs rename to src/test/rustdoc/auxiliary/issue-27362-aux.rs diff --git a/src/test/rustdoc/const-generics/add-impl.rs b/src/test/rustdoc/const-generics/add-impl.rs index 54bdd768f8a73..905f958826897 100644 --- a/src/test/rustdoc/const-generics/add-impl.rs +++ b/src/test/rustdoc/const-generics/add-impl.rs @@ -11,7 +11,7 @@ pub struct Simd { inner: T, } -// @has foo/struct.Simd.html '//div[@id="implementations-list"]/h3/code' 'impl Add> for Simd' +// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]/h3/code' 'impl Add> for Simd' impl Add for Simd { type Output = Self; diff --git a/src/test/rustdoc/duplicate_impls/issue-33054.rs b/src/test/rustdoc/duplicate_impls/issue-33054.rs index 3f7cec1856331..112d632971a5f 100644 --- a/src/test/rustdoc/duplicate_impls/issue-33054.rs +++ b/src/test/rustdoc/duplicate_impls/issue-33054.rs @@ -1,7 +1,7 @@ // @has issue_33054/impls/struct.Foo.html // @has - '//code' 'impl Foo' // @has - '//code' 'impl Bar for Foo' -// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1 +// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 // @count - '//*[@id="main"]/*[@class="impl"]' 1 // @has issue_33054/impls/bar/trait.Bar.html // @has - '//code' 'impl Bar for Foo' diff --git a/src/test/rustdoc/empty-section.rs b/src/test/rustdoc/empty-section.rs index d95f3a80365cd..665aa38b11eba 100644 --- a/src/test/rustdoc/empty-section.rs +++ b/src/test/rustdoc/empty-section.rs @@ -1,6 +1,6 @@ #![crate_name = "foo"] -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] pub struct Foo; diff --git a/src/test/rustdoc/external-macro-src.rs b/src/test/rustdoc/external-macro-src.rs new file mode 100644 index 0000000000000..4394415e5c768 --- /dev/null +++ b/src/test/rustdoc/external-macro-src.rs @@ -0,0 +1,15 @@ +// aux-build:external-macro-src.rs +// ignore-tidy-linelength + +#![crate_name = "foo"] + +#[macro_use] +extern crate external_macro_src; + +// @has foo/index.html '//a[@href="../src/foo/external-macro-src.rs.html#4-15"]' '[src]' + +// @has foo/struct.Foo.html +// @has - '//a[@href="https://example.com/src/external_macro_src/external-macro-src.rs.html#8"]' '[src]' +// @has - '//a[@href="https://example.com/src/external_macro_src/external-macro-src.rs.html#9-13"]' '[src]' +// @has - '//a[@href="https://example.com/src/external_macro_src/external-macro-src.rs.html#10-12"]' '[src]' +make_foo!(); diff --git a/src/test/rustdoc/impl-parts-crosscrate.rs b/src/test/rustdoc/impl-parts-crosscrate.rs index f9583d1a72296..a68db9c70ad2d 100644 --- a/src/test/rustdoc/impl-parts-crosscrate.rs +++ b/src/test/rustdoc/impl-parts-crosscrate.rs @@ -1,7 +1,7 @@ // aux-build:rustdoc-impl-parts-crosscrate.rs // ignore-cross-compile -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] extern crate rustdoc_impl_parts_crosscrate; diff --git a/src/test/rustdoc/impl-parts.rs b/src/test/rustdoc/impl-parts.rs index fbb4e725481c0..68baca9a04e9a 100644 --- a/src/test/rustdoc/impl-parts.rs +++ b/src/test/rustdoc/impl-parts.rs @@ -1,3 +1,4 @@ +#![feature(negative_impls)] #![feature(optin_builtin_traits)] pub auto trait AnOibit {} diff --git a/src/test/rustdoc/intra-link-trait-impl.rs b/src/test/rustdoc/intra-link-trait-impl.rs new file mode 100644 index 0000000000000..fab8406d525e5 --- /dev/null +++ b/src/test/rustdoc/intra-link-trait-impl.rs @@ -0,0 +1,35 @@ +#![crate_name = "foo"] + +// ignore-tidy-linelength + +pub struct MyStruct; + +impl MyTrait for MyStruct { + +// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' + + /// [`AssoType`] + /// + /// [`AssoType`]: MyStruct::AssoType + type AssoType = u32; + +// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' + + /// [`ASSO_CONST`] + /// + /// [`ASSO_CONST`]: MyStruct::ASSO_CONST + const ASSO_CONST: i32 = 10; + +// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.trait_fn' + + /// [`trait_fn`] + /// + /// [`trait_fn`]: MyStruct::trait_fn + fn trait_fn() { } +} + +pub trait MyTrait { + type AssoType; + const ASSO_CONST: i32 = 1; + fn trait_fn(); +} diff --git a/src/test/rustdoc/issue-21474.rs b/src/test/rustdoc/issue-21474.rs index 4c530f72b8ab6..896fc1a78f13f 100644 --- a/src/test/rustdoc/issue-21474.rs +++ b/src/test/rustdoc/issue-21474.rs @@ -7,5 +7,5 @@ mod inner { pub trait Blah { } // @count issue_21474/struct.What.html \ -// '//*[@id="implementations-list"]/*[@class="impl"]' 1 +// '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 pub struct What; diff --git a/src/test/rustdoc/issue-26606.rs b/src/test/rustdoc/issue-26606.rs index d2aa4bbbd1251..c8e9a63ea9f77 100644 --- a/src/test/rustdoc/issue-26606.rs +++ b/src/test/rustdoc/issue-26606.rs @@ -7,5 +7,5 @@ extern crate issue_26606_macro; // @has issue_26606/constant.FOO.html -// @has - '//a/@href' '../src/issue_26606/auxiliary/issue-26606-macro.rs.html#3' +// @has - '//a/@href' '../src/issue_26606_macro/issue-26606-macro.rs.html#3' make_item!(FOO); diff --git a/src/test/rustdoc/issue-27362.rs b/src/test/rustdoc/issue-27362.rs index 3f3878350d515..1cbba4b663df8 100644 --- a/src/test/rustdoc/issue-27362.rs +++ b/src/test/rustdoc/issue-27362.rs @@ -1,10 +1,10 @@ -// aux-build:issue-27362.rs +// aux-build:issue-27362-aux.rs // ignore-cross-compile -// ignore-test This test fails on beta/stable #32019 -extern crate issue_27362; -pub use issue_27362 as quux; +extern crate issue_27362_aux; -// @matches issue_27362/quux/fn.foo.html '//pre' "pub const fn foo()" -// @matches issue_27362/quux/fn.bar.html '//pre' "pub const unsafe fn bar()" -// @matches issue_27362/quux/struct.Foo.html '//code' "const unsafe fn baz()" +pub use issue_27362_aux::*; + +// @matches issue_27362/fn.foo.html '//pre' "pub const fn foo()" +// @matches issue_27362/fn.bar.html '//pre' "pub const unsafe fn bar()" +// @matches issue_27362/struct.Foo.html '//code' "const unsafe fn baz()" diff --git a/src/test/rustdoc/issue-32374.rs b/src/test/rustdoc/issue-32374.rs index 7babfaf6060f4..11caa34d4b114 100644 --- a/src/test/rustdoc/issue-32374.rs +++ b/src/test/rustdoc/issue-32374.rs @@ -10,7 +10,7 @@ // @matches issue_32374/index.html '//*[@class="docblock-short"]/text()' 'Docs' // @has issue_32374/struct.T.html '//*[@class="stab deprecated"]' \ -// 'Deprecated since 1.0.0: text' +// '👎 Deprecated since 1.0.0: text' // @has - 'test 
#32374' // @matches issue_32374/struct.T.html '//*[@class="stab unstable"]' \ // '🔬 This is a nightly-only experimental API. \(test\s#32374\)$' @@ -20,7 +20,7 @@ pub struct T; // @has issue_32374/struct.U.html '//*[@class="stab deprecated"]' \ -// 'Deprecated since 1.0.0: deprecated' +// '👎 Deprecated since 1.0.0: deprecated' // @has issue_32374/struct.U.html '//*[@class="stab unstable"]' \ // '🔬 This is a nightly-only experimental API. (test #32374)' // @has issue_32374/struct.U.html '//details' \ diff --git a/src/test/rustdoc/issue-45584.rs b/src/test/rustdoc/issue-45584.rs index cd8c275d8527b..0225c0c5c2fa7 100644 --- a/src/test/rustdoc/issue-45584.rs +++ b/src/test/rustdoc/issue-45584.rs @@ -4,12 +4,12 @@ pub trait Bar {} // @has 'foo/struct.Foo1.html' pub struct Foo1; -// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1 +// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 // @has - '//*[@class="impl"]' "impl Bar for Foo1" impl Bar for Foo1 {} // @has 'foo/struct.Foo2.html' pub struct Foo2; -// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1 +// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 // @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8" impl Bar<&'static Foo2, Foo2> for u8 {} diff --git a/src/test/rustdoc/issue-55321.rs b/src/test/rustdoc/issue-55321.rs index 257cb32c65c25..d312a5114595a 100644 --- a/src/test/rustdoc/issue-55321.rs +++ b/src/test/rustdoc/issue-55321.rs @@ -1,8 +1,8 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] // @has issue_55321/struct.A.html -// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' "impl !Send for A" -// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' "impl !Sync for A" +// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Send for A" +// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Sync for A" pub struct A(); impl !Send for A {} diff --git a/src/test/rustdoc/negative-impl-sidebar.rs b/src/test/rustdoc/negative-impl-sidebar.rs index 838ca0402e48a..3414d9540776a 100644 --- a/src/test/rustdoc/negative-impl-sidebar.rs +++ b/src/test/rustdoc/negative-impl-sidebar.rs @@ -1,9 +1,9 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![crate_name = "foo"] pub struct Foo; // @has foo/struct.Foo.html -// @has - '//*[@class="sidebar-title"][@href="#implementations"]' 'Trait Implementations' +// @has - '//*[@class="sidebar-title"][@href="#trait-implementations"]' 'Trait Implementations' // @has - '//*[@class="sidebar-links"]/a' '!Sync' impl !Sync for Foo {} diff --git a/src/test/rustdoc/negative-impl.rs b/src/test/rustdoc/negative-impl.rs index 8ac87f4f0cb97..d76aac6906c46 100644 --- a/src/test/rustdoc/negative-impl.rs +++ b/src/test/rustdoc/negative-impl.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] // @matches negative_impl/struct.Alpha.html '//pre' "pub struct Alpha" pub struct Alpha; diff --git a/src/test/rustdoc/show-const-contents.rs b/src/test/rustdoc/show-const-contents.rs index 064c026e6a078..b35f67ef91243 100644 --- a/src/test/rustdoc/show-const-contents.rs +++ b/src/test/rustdoc/show-const-contents.rs @@ -51,7 +51,7 @@ pub const MY_TYPE_WITH_STR: MyTypeWithStr = MyTypeWithStr("show this"); // @has show_const_contents/constant.PI.html '; // 3.14159274f32' pub use std::f32::consts::PI; -// @has show_const_contents/constant.MAX.html '= i32::max_value(); // 2_147_483_647i32' +// @has show_const_contents/constant.MAX.html '= i32::MAX; // 2_147_483_647i32' pub use std::i32::MAX; macro_rules! int_module { diff --git a/src/test/rustdoc/struct-implementations-title.rs b/src/test/rustdoc/struct-implementations-title.rs new file mode 100644 index 0000000000000..96eb11311d6b4 --- /dev/null +++ b/src/test/rustdoc/struct-implementations-title.rs @@ -0,0 +1,9 @@ +#![crate_name = "foo"] + +pub struct Struc; + +// @has foo/struct.Struc.html +// @has - '//*[@id="main"]/h2[@id="implementations"]' "Implementations" +impl Struc { + pub const S: u64 = 0; +} diff --git a/src/test/rustdoc/synthetic_auto/manual.rs b/src/test/rustdoc/synthetic_auto/manual.rs index 458403462d64a..d20b4744af15b 100644 --- a/src/test/rustdoc/synthetic_auto/manual.rs +++ b/src/test/rustdoc/synthetic_auto/manual.rs @@ -2,10 +2,10 @@ // @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl Sync for \ // Foo where T: Sync' // -// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' \ +// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' \ // 'impl Send for Foo' // -// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1 +// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 // @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 4 pub struct Foo { field: T, diff --git a/src/test/rustdoc/test-strikethrough.rs b/src/test/rustdoc/test-strikethrough.rs new file mode 100644 index 0000000000000..c7855729a98ee --- /dev/null +++ b/src/test/rustdoc/test-strikethrough.rs @@ -0,0 +1,6 @@ +#![crate_name = "foo"] + +// @has foo/fn.f.html +// @has - //del "Y" +/// ~~Y~~ +pub fn f() {} diff --git a/src/test/rustdoc/thread-local-src.rs b/src/test/rustdoc/thread-local-src.rs new file mode 100644 index 0000000000000..022d81a4dbfca --- /dev/null +++ b/src/test/rustdoc/thread-local-src.rs @@ -0,0 +1,6 @@ +#![crate_name = "foo"] + +// @has foo/index.html '//a[@href="../src/foo/thread-local-src.rs.html#1-6"]' '[src]' + +// @has foo/constant.FOO.html '//a/@href' 'https://doc.rust-lang.org/nightly/src/std/' +thread_local!(pub static FOO: bool = false); diff --git a/src/test/rustdoc/typedef.rs b/src/test/rustdoc/typedef.rs index 80351ff52f5cc..7f834d3d5a512 100644 --- a/src/test/rustdoc/typedef.rs +++ b/src/test/rustdoc/typedef.rs @@ -13,8 +13,8 @@ impl MyStruct { // @has - '//*[@class="impl"]//code' 'impl MyTrait for MyAlias' // @has - 'Alias docstring' // @has - '//*[@class="sidebar"]//p[@class="location"]' 'Type Definition MyAlias' -// @has - '//*[@class="sidebar"]//a[@href="#methods"]' 'Methods' -// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Trait Implementations' +// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Methods' +// @has - '//*[@class="sidebar"]//a[@href="#trait-implementations"]' 'Trait Implementations' /// Alias docstring pub type MyAlias = MyStruct; diff --git a/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs b/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs deleted file mode 100644 index 837ed1f002fc9..0000000000000 --- a/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs +++ /dev/null @@ -1,35 +0,0 @@ -// ignore-musl - dlsym doesn't see symbols without "-C link-arg=-Wl,--export-dynamic" - -#![feature(rustc_private)] - -extern crate rustc_metadata; - -use rustc_metadata::dynamic_lib::DynamicLibrary; - -#[no_mangle] -pub fn foo() { - bar(); -} - -pub fn foo2() { - fn bar2() { - bar(); - } - bar2(); -} - -#[no_mangle] -fn bar() {} - -#[allow(dead_code)] -#[no_mangle] -fn baz() {} - -pub fn test() { - let lib = DynamicLibrary::open(None).unwrap(); - unsafe { - assert!(lib.symbol::("foo").is_ok()); - assert!(lib.symbol::("baz").is_ok()); - assert!(lib.symbol::("bar").is_ok()); - } -} diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs index 52620b2464bd6..f8cb1640cb4c1 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs @@ -2,19 +2,19 @@ #![feature(plugin_registrar, rustc_private)] #![feature(box_syntax)] + extern crate rustc_driver; extern crate rustc_hir; -extern crate rustc_span; -#[macro_use] extern crate rustc_lint; +extern crate rustc_span; #[macro_use] extern crate rustc_session; extern crate rustc_ast; +use rustc_ast::attr; use rustc_driver::plugin::Registry; use rustc_lint::{LateContext, LateLintPass, LintContext, LintPass}; use rustc_span::symbol::Symbol; -use rustc_ast::attr; macro_rules! fake_lint_pass { ($struct:ident, $($attr:expr),*) => { @@ -50,17 +50,17 @@ declare_lint!(CRATE_NOT_GREEN, Warn, "crate not marked with #![crate_green]"); fake_lint_pass! { PassOkay, - Symbol::intern("rustc_crate_okay") + Symbol::intern("crate_okay") } fake_lint_pass! { PassRedBlue, - Symbol::intern("rustc_crate_red"), Symbol::intern("rustc_crate_blue") + Symbol::intern("crate_red"), Symbol::intern("crate_blue") } fake_lint_pass! { PassGreyGreen, - Symbol::intern("rustc_crate_grey"), Symbol::intern("rustc_crate_green") + Symbol::intern("crate_grey"), Symbol::intern("crate_green") } #[plugin_registrar] diff --git a/src/test/ui-fulldeps/auxiliary/lto-syntax-extension-plugin.rs b/src/test/ui-fulldeps/auxiliary/lto-syntax-extension-plugin.rs index 89bc9a2b9dbf4..5fbb3efabb37b 100644 --- a/src/test/ui-fulldeps/auxiliary/lto-syntax-extension-plugin.rs +++ b/src/test/ui-fulldeps/auxiliary/lto-syntax-extension-plugin.rs @@ -3,7 +3,7 @@ #![feature(plugin_registrar)] #![feature(rustc_private)] -extern crate rustc; +extern crate rustc_middle; extern crate rustc_driver; use rustc_driver::plugin::Registry; diff --git a/src/test/ui-fulldeps/auxiliary/macro-crate-test.rs b/src/test/ui-fulldeps/auxiliary/macro-crate-test.rs deleted file mode 100644 index 56a560acbb44a..0000000000000 --- a/src/test/ui-fulldeps/auxiliary/macro-crate-test.rs +++ /dev/null @@ -1,33 +0,0 @@ -// force-host -// no-prefer-dynamic - -#![crate_type = "proc-macro"] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc; -extern crate rustc_driver; -extern crate proc_macro; - -use proc_macro::{TokenTree, TokenStream}; - -#[proc_macro_attribute] -pub fn rustc_duplicate(attr: TokenStream, item: TokenStream) -> TokenStream { - let mut new_name = Some(attr.into_iter().nth(0).unwrap()); - let mut encountered_idents = 0; - let input = item.to_string(); - let ret = item.into_iter().map(move |token| match token { - TokenTree::Ident(_) if encountered_idents == 1 => { - encountered_idents += 1; - new_name.take().unwrap() - } - TokenTree::Ident(_) => { - encountered_idents += 1; - token - } - _ => token - }).collect::(); - let mut input_again = input.parse::().unwrap(); - input_again.extend(ret); - input_again -} diff --git a/src/test/ui-fulldeps/auxiliary/outlive-expansion-phase.rs b/src/test/ui-fulldeps/auxiliary/outlive-expansion-phase.rs index e5c4f5b8f7a66..6584b905f5ed3 100644 --- a/src/test/ui-fulldeps/auxiliary/outlive-expansion-phase.rs +++ b/src/test/ui-fulldeps/auxiliary/outlive-expansion-phase.rs @@ -3,7 +3,7 @@ #![feature(plugin_registrar)] #![feature(box_syntax, rustc_private)] -extern crate rustc; +extern crate rustc_middle; extern crate rustc_driver; use std::any::Any; diff --git a/src/test/ui-fulldeps/auxiliary/rlib-crate-test.rs b/src/test/ui-fulldeps/auxiliary/rlib-crate-test.rs index 1c0de98da56f2..e8e8ae2985a68 100644 --- a/src/test/ui-fulldeps/auxiliary/rlib-crate-test.rs +++ b/src/test/ui-fulldeps/auxiliary/rlib-crate-test.rs @@ -3,7 +3,7 @@ #![crate_type = "rlib"] #![feature(plugin_registrar, rustc_private)] -extern crate rustc; +extern crate rustc_middle; extern crate rustc_driver; use rustc_driver::plugin::Registry; diff --git a/src/test/ui-fulldeps/feature-gate-plugin.stderr b/src/test/ui-fulldeps/feature-gate-plugin.stderr index 02c569073e9e7..5e40561c7f55c 100644 --- a/src/test/ui-fulldeps/feature-gate-plugin.stderr +++ b/src/test/ui-fulldeps/feature-gate-plugin.stderr @@ -15,6 +15,6 @@ LL | #![plugin(empty_plugin)] | = note: `#[warn(deprecated)]` on by default -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui-fulldeps/gated-plugin.stderr b/src/test/ui-fulldeps/gated-plugin.stderr index df2de40a8c1b1..b8b7dd23c1cd3 100644 --- a/src/test/ui-fulldeps/gated-plugin.stderr +++ b/src/test/ui-fulldeps/gated-plugin.stderr @@ -15,6 +15,6 @@ LL | #![plugin(empty_plugin)] | = note: `#[warn(deprecated)]` on by default -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui-fulldeps/hash-stable-is-unstable.rs b/src/test/ui-fulldeps/hash-stable-is-unstable.rs index d79ef62c31207..d93e21cf7916c 100644 --- a/src/test/ui-fulldeps/hash-stable-is-unstable.rs +++ b/src/test/ui-fulldeps/hash-stable-is-unstable.rs @@ -2,7 +2,7 @@ extern crate rustc_data_structures; //~^ use of unstable library feature 'rustc_private' -extern crate rustc; +extern crate rustc_middle; //~^ use of unstable library feature 'rustc_private' extern crate rustc_macros; //~^ use of unstable library feature 'rustc_private' diff --git a/src/test/ui-fulldeps/hash-stable-is-unstable.stderr b/src/test/ui-fulldeps/hash-stable-is-unstable.stderr index bc5e2d8937858..3c30e0402d96d 100644 --- a/src/test/ui-fulldeps/hash-stable-is-unstable.stderr +++ b/src/test/ui-fulldeps/hash-stable-is-unstable.stderr @@ -10,8 +10,8 @@ LL | extern crate rustc_data_structures; 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:5:1 | -LL | extern crate rustc; - | ^^^^^^^^^^^^^^^^^^^ +LL | extern crate rustc_middle; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #27812 for more information = help: add `#![feature(rustc_private)]` to the crate attributes to enable diff --git a/src/test/ui-fulldeps/internal-lints/lint_pass_impl_without_macro.rs b/src/test/ui-fulldeps/internal-lints/lint_pass_impl_without_macro.rs index 8d9cbe45fc696..f6f0c038536c8 100644 --- a/src/test/ui-fulldeps/internal-lints/lint_pass_impl_without_macro.rs +++ b/src/test/ui-fulldeps/internal-lints/lint_pass_impl_without_macro.rs @@ -3,7 +3,7 @@ #![feature(rustc_private)] #![deny(rustc::lint_pass_impl_without_macro)] -extern crate rustc; +extern crate rustc_middle; extern crate rustc_session; use rustc_session::lint::{LintArray, LintPass}; diff --git a/src/test/ui-fulldeps/internal-lints/pass_ty_by_ref.rs b/src/test/ui-fulldeps/internal-lints/pass_ty_by_ref.rs index 7564c0245802d..e0fdbaeac3069 100644 --- a/src/test/ui-fulldeps/internal-lints/pass_ty_by_ref.rs +++ b/src/test/ui-fulldeps/internal-lints/pass_ty_by_ref.rs @@ -4,9 +4,9 @@ #![deny(rustc::ty_pass_by_reference)] #![allow(unused)] -extern crate rustc; +extern crate rustc_middle; -use rustc::ty::{Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; fn ty_by_ref( ty_val: Ty<'_>, diff --git a/src/test/ui-fulldeps/internal-lints/qualified_ty_ty_ctxt.rs b/src/test/ui-fulldeps/internal-lints/qualified_ty_ty_ctxt.rs index 0040230ec7d7c..32b987338c057 100644 --- a/src/test/ui-fulldeps/internal-lints/qualified_ty_ty_ctxt.rs +++ b/src/test/ui-fulldeps/internal-lints/qualified_ty_ty_ctxt.rs @@ -4,9 +4,9 @@ #![deny(rustc::usage_of_qualified_ty)] #![allow(unused)] -extern crate rustc; +extern crate rustc_middle; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; macro_rules! qualified_macro { ($a:ident) => { diff --git a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs index f716a78a031f2..27fe432e96ded 100644 --- a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs +++ b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs @@ -2,9 +2,9 @@ #![feature(rustc_private)] -extern crate rustc; +extern crate rustc_middle; -use rustc::ty::{self, Ty, TyKind}; +use rustc_middle::ty::{self, Ty, TyKind}; #[deny(rustc::usage_of_ty_tykind)] fn main() { @@ -32,7 +32,6 @@ fn main() { TyKind::Never => (), //~ ERROR usage of `ty::TyKind::` TyKind::Tuple(..) => (), //~ ERROR usage of `ty::TyKind::` TyKind::Projection(..) => (), //~ ERROR usage of `ty::TyKind::` - TyKind::UnnormalizedProjection(..) => (), //~ ERROR usage of `ty::TyKind::` TyKind::Opaque(..) => (), //~ ERROR usage of `ty::TyKind::` TyKind::Param(..) => (), //~ ERROR usage of `ty::TyKind::` TyKind::Bound(..) => (), //~ ERROR usage of `ty::TyKind::` diff --git a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr index ee9f1ff10f88e..0486c90a5a07a 100644 --- a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr +++ b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr @@ -139,58 +139,52 @@ LL | TyKind::Projection(..) => (), error: usage of `ty::TyKind::` --> $DIR/ty_tykind_usage.rs:35:9 | -LL | TyKind::UnnormalizedProjection(..) => (), - | ^^^^^^ help: try using ty:: directly: `ty` - -error: usage of `ty::TyKind::` - --> $DIR/ty_tykind_usage.rs:36:9 - | LL | TyKind::Opaque(..) => (), | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind::` - --> $DIR/ty_tykind_usage.rs:37:9 + --> $DIR/ty_tykind_usage.rs:36:9 | LL | TyKind::Param(..) => (), | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind::` - --> $DIR/ty_tykind_usage.rs:38:9 + --> $DIR/ty_tykind_usage.rs:37:9 | LL | TyKind::Bound(..) => (), | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind::` - --> $DIR/ty_tykind_usage.rs:39:9 + --> $DIR/ty_tykind_usage.rs:38:9 | LL | TyKind::Placeholder(..) => (), | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind::` - --> $DIR/ty_tykind_usage.rs:40:9 + --> $DIR/ty_tykind_usage.rs:39:9 | LL | TyKind::Infer(..) => (), | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind::` - --> $DIR/ty_tykind_usage.rs:41:9 + --> $DIR/ty_tykind_usage.rs:40:9 | LL | TyKind::Error => (), | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind::` - --> $DIR/ty_tykind_usage.rs:46:12 + --> $DIR/ty_tykind_usage.rs:45:12 | LL | if let TyKind::Int(int_ty) = kind {} | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind` - --> $DIR/ty_tykind_usage.rs:48:24 + --> $DIR/ty_tykind_usage.rs:47:24 | LL | fn ty_kind(ty_bad: TyKind<'_>, ty_good: Ty<'_>) {} | ^^^^^^^^^^ | = help: try using `Ty` instead -error: aborting due to 31 previous errors +error: aborting due to 30 previous errors diff --git a/src/test/ui-fulldeps/issue-15778-fail.stderr b/src/test/ui-fulldeps/issue-15778-fail.stderr index e76044c56ef94..a37893e120351 100644 --- a/src/test/ui-fulldeps/issue-15778-fail.stderr +++ b/src/test/ui-fulldeps/issue-15778-fail.stderr @@ -18,5 +18,5 @@ LL | | pub fn main() { } | = note: requested on the command line with `-D crate-not-okay` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui-fulldeps/issue-15778-pass.rs b/src/test/ui-fulldeps/issue-15778-pass.rs index 4b3cf07e2830d..c031dbc7155db 100644 --- a/src/test/ui-fulldeps/issue-15778-pass.rs +++ b/src/test/ui-fulldeps/issue-15778-pass.rs @@ -1,23 +1,23 @@ -// run-pass +// check-pass // aux-build:lint-for-crate-rpass.rs // ignore-stage1 // compile-flags: -D crate-not-okay -#![feature(plugin, register_attr, custom_inner_attributes, rustc_attrs)] +#![feature(plugin, register_attr, custom_inner_attributes)] #![register_attr( - rustc_crate_okay, - rustc_crate_blue, - rustc_crate_red, - rustc_crate_grey, - rustc_crate_green, + crate_okay, + crate_blue, + crate_red, + crate_grey, + crate_green, )] #![plugin(lint_for_crate_rpass)] //~ WARNING compiler plugins are deprecated -#![rustc_crate_okay] -#![rustc_crate_blue] -#![rustc_crate_red] -#![rustc_crate_grey] -#![rustc_crate_green] +#![crate_okay] +#![crate_blue] +#![crate_red] +#![crate_grey] +#![crate_green] fn main() {} diff --git a/src/test/ui-fulldeps/issue-15778-pass.stderr b/src/test/ui-fulldeps/issue-15778-pass.stderr index 48b42958489e7..a9d9721ac7b89 100644 --- a/src/test/ui-fulldeps/issue-15778-pass.stderr +++ b/src/test/ui-fulldeps/issue-15778-pass.stderr @@ -6,3 +6,5 @@ LL | #![plugin(lint_for_crate_rpass)] | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui-fulldeps/issue-40001.stderr b/src/test/ui-fulldeps/issue-40001.stderr index d0ad0275ed158..73ec0692464ad 100644 --- a/src/test/ui-fulldeps/issue-40001.stderr +++ b/src/test/ui-fulldeps/issue-40001.stderr @@ -6,3 +6,5 @@ LL | #![plugin(issue_40001_plugin)] | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui-fulldeps/linkage-visibility.rs b/src/test/ui-fulldeps/linkage-visibility.rs deleted file mode 100644 index ae46fbc4e8a03..0000000000000 --- a/src/test/ui-fulldeps/linkage-visibility.rs +++ /dev/null @@ -1,13 +0,0 @@ -// run-pass -// aux-build:linkage-visibility.rs -// ignore-android: FIXME(#10356) -// ignore-windows: std::dynamic_lib does not work on Windows well -// ignore-emscripten no dynamic linking - -extern crate linkage_visibility as foo; - -pub fn main() { - foo::test(); - foo::foo2::(); - foo::foo(); -} diff --git a/src/test/ui-fulldeps/lint-group-forbid-always-trumps-cli.rs b/src/test/ui-fulldeps/lint-group-forbid-always-trumps-cli.rs new file mode 100644 index 0000000000000..fc19bc039063a --- /dev/null +++ b/src/test/ui-fulldeps/lint-group-forbid-always-trumps-cli.rs @@ -0,0 +1,7 @@ +// aux-build:lint-group-plugin-test.rs +// compile-flags: -F unused -A unused + +fn main() { + let x = 1; + //~^ ERROR unused variable: `x` +} diff --git a/src/test/ui-fulldeps/lint-group-forbid-always-trumps-cli.stderr b/src/test/ui-fulldeps/lint-group-forbid-always-trumps-cli.stderr new file mode 100644 index 0000000000000..6bab367b0d175 --- /dev/null +++ b/src/test/ui-fulldeps/lint-group-forbid-always-trumps-cli.stderr @@ -0,0 +1,10 @@ +error: unused variable: `x` + --> $DIR/lint-group-forbid-always-trumps-cli.rs:5:9 + | +LL | let x = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | + = note: `-F unused-variables` implied by `-F unused` + +error: aborting due to previous error + diff --git a/src/test/ui-fulldeps/lint-group-plugin-deny-cmdline.stderr b/src/test/ui-fulldeps/lint-group-plugin-deny-cmdline.stderr index f8a4f271da5aa..20486d596d9a3 100644 --- a/src/test/ui-fulldeps/lint-group-plugin-deny-cmdline.stderr +++ b/src/test/ui-fulldeps/lint-group-plugin-deny-cmdline.stderr @@ -22,5 +22,5 @@ LL | fn pleaselintme() { } | = note: `-D please-lint` implied by `-D lint-me` -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui-fulldeps/lint-group-plugin.stderr b/src/test/ui-fulldeps/lint-group-plugin.stderr index 58dc78b06d3f3..6f429dad01757 100644 --- a/src/test/ui-fulldeps/lint-group-plugin.stderr +++ b/src/test/ui-fulldeps/lint-group-plugin.stderr @@ -22,3 +22,5 @@ LL | fn pleaselintme() { } | = note: `#[warn(please_lint)]` on by default +warning: 3 warnings emitted + diff --git a/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr b/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr index 77265782fa366..f06703a27848a 100644 --- a/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr +++ b/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr @@ -6,3 +6,5 @@ LL | #![plugin(lint_plugin_test)] | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui-fulldeps/lint-plugin-cmdline-load.stderr b/src/test/ui-fulldeps/lint-plugin-cmdline-load.stderr index 1263a0efe624f..981631494fafd 100644 --- a/src/test/ui-fulldeps/lint-plugin-cmdline-load.stderr +++ b/src/test/ui-fulldeps/lint-plugin-cmdline-load.stderr @@ -14,3 +14,5 @@ LL | fn lintme() { } | = note: `#[warn(test_lint)]` on by default +warning: 2 warnings emitted + diff --git a/src/test/ui-fulldeps/lint-plugin-deny-attr.stderr b/src/test/ui-fulldeps/lint-plugin-deny-attr.stderr index a0081b15f53c5..b9774c044623c 100644 --- a/src/test/ui-fulldeps/lint-plugin-deny-attr.stderr +++ b/src/test/ui-fulldeps/lint-plugin-deny-attr.stderr @@ -18,5 +18,5 @@ note: the lint level is defined here LL | #![deny(test_lint)] | ^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui-fulldeps/lint-plugin-deny-cmdline.stderr b/src/test/ui-fulldeps/lint-plugin-deny-cmdline.stderr index 03668fbfe664f..cbabb09f6a596 100644 --- a/src/test/ui-fulldeps/lint-plugin-deny-cmdline.stderr +++ b/src/test/ui-fulldeps/lint-plugin-deny-cmdline.stderr @@ -14,5 +14,5 @@ LL | fn lintme() { } | = note: requested on the command line with `-D test-lint` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr b/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr index df92bc70a7971..7b868c3393f50 100644 --- a/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr +++ b/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr @@ -45,6 +45,6 @@ LL | #![forbid(test_lint)] LL | #[allow(test_lint)] | ^^^^^^^^^ overruled by previous forbid -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0453`. diff --git a/src/test/ui-fulldeps/lint-plugin-forbid-cmdline.stderr b/src/test/ui-fulldeps/lint-plugin-forbid-cmdline.stderr index 0302ec84d5620..e9ec63ef9831a 100644 --- a/src/test/ui-fulldeps/lint-plugin-forbid-cmdline.stderr +++ b/src/test/ui-fulldeps/lint-plugin-forbid-cmdline.stderr @@ -38,6 +38,6 @@ LL | #[allow(test_lint)] | = note: `forbid` lint level was set on command line -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0453`. diff --git a/src/test/ui-fulldeps/lint-plugin.stderr b/src/test/ui-fulldeps/lint-plugin.stderr index e95650090dde3..765832071cbea 100644 --- a/src/test/ui-fulldeps/lint-plugin.stderr +++ b/src/test/ui-fulldeps/lint-plugin.stderr @@ -14,3 +14,5 @@ LL | fn lintme() { } | = note: `#[warn(test_lint)]` on by default +warning: 2 warnings emitted + diff --git a/src/test/ui-fulldeps/lint-tool-cmdline-allow.stderr b/src/test/ui-fulldeps/lint-tool-cmdline-allow.stderr index 2f1c29ea7b832..b4fb9e22da417 100644 --- a/src/test/ui-fulldeps/lint-tool-cmdline-allow.stderr +++ b/src/test/ui-fulldeps/lint-tool-cmdline-allow.stderr @@ -30,3 +30,5 @@ warning: lint name `test_lint` is deprecated and does not have an effect anymore | = note: requested on the command line with `-A test_lint` +warning: 6 warnings emitted + diff --git a/src/test/ui-fulldeps/lint-tool-test.stderr b/src/test/ui-fulldeps/lint-tool-test.stderr index 31d25652d5d39..5e1cb4fb843fd 100644 --- a/src/test/ui-fulldeps/lint-tool-test.stderr +++ b/src/test/ui-fulldeps/lint-tool-test.stderr @@ -96,5 +96,5 @@ warning: lint name `test_group` is deprecated and may not have an effect in the LL | #[allow(test_group)] | ^^^^^^^^^^ help: change it to: `clippy::test_group` -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 11 warnings emitted diff --git a/src/test/ui-fulldeps/lto-syntax-extension.stderr b/src/test/ui-fulldeps/lto-syntax-extension.stderr index 529da32e10eeb..555493f32305a 100644 --- a/src/test/ui-fulldeps/lto-syntax-extension.stderr +++ b/src/test/ui-fulldeps/lto-syntax-extension.stderr @@ -6,3 +6,5 @@ LL | #![plugin(lto_syntax_extension_plugin)] | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui-fulldeps/macro-crate-multi-decorator.rs b/src/test/ui-fulldeps/macro-crate-multi-decorator.rs deleted file mode 100644 index f21617be5d26f..0000000000000 --- a/src/test/ui-fulldeps/macro-crate-multi-decorator.rs +++ /dev/null @@ -1,44 +0,0 @@ -// check-pass -// aux-build:macro-crate-test.rs -// ignore-stage1 - -#![feature(rustc_attrs)] - -#[macro_use] -extern crate macro_crate_test; - -// The duplicate macro will create a copy of the item with the given identifier. - -#[rustc_duplicate(MyCopy)] -struct MyStruct { - number: i32 -} - -trait TestTrait { - #[rustc_duplicate(TestType2)] - type TestType; - - #[rustc_duplicate(required_fn2)] - fn required_fn(&self); - - #[rustc_duplicate(provided_fn2)] - fn provided_fn(&self) { } -} - -impl TestTrait for MyStruct { - #[rustc_duplicate(TestType2)] - type TestType = f64; - - #[rustc_duplicate(required_fn2)] - fn required_fn(&self) { } -} - -fn main() { - let s = MyStruct { number: 42 }; - s.required_fn(); - s.required_fn2(); - s.provided_fn(); - s.provided_fn2(); - - let s = MyCopy { number: 42 }; -} diff --git a/src/test/ui-fulldeps/macro-crate-rlib.stderr b/src/test/ui-fulldeps/macro-crate-rlib.stderr index b5bd761f1b580..342663312a853 100644 --- a/src/test/ui-fulldeps/macro-crate-rlib.stderr +++ b/src/test/ui-fulldeps/macro-crate-rlib.stderr @@ -12,5 +12,5 @@ LL | #![plugin(rlib_crate_test)] | = note: `#[warn(deprecated)]` on by default -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui-fulldeps/outlive-expansion-phase.stderr b/src/test/ui-fulldeps/outlive-expansion-phase.stderr index d06fc480fb522..e40a08ae73b60 100644 --- a/src/test/ui-fulldeps/outlive-expansion-phase.stderr +++ b/src/test/ui-fulldeps/outlive-expansion-phase.stderr @@ -6,3 +6,5 @@ LL | #![plugin(outlive_expansion_phase)] | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui-fulldeps/pathless-extern-unstable.rs b/src/test/ui-fulldeps/pathless-extern-unstable.rs index 00b3ec5409ff0..524b0c2f73a5d 100644 --- a/src/test/ui-fulldeps/pathless-extern-unstable.rs +++ b/src/test/ui-fulldeps/pathless-extern-unstable.rs @@ -1,10 +1,10 @@ // ignore-stage1 // edition:2018 -// compile-flags:--extern rustc +// compile-flags:--extern rustc_middle -// Test that `--extern rustc` fails with `rustc_private`. +// Test that `--extern rustc_middle` fails with `rustc_private`. -pub use rustc; +pub use rustc_middle; //~^ ERROR use of unstable library feature 'rustc_private' fn main() {} diff --git a/src/test/ui-fulldeps/pathless-extern-unstable.stderr b/src/test/ui-fulldeps/pathless-extern-unstable.stderr index 09f1e600b25d5..dcc3cddd32cfd 100644 --- a/src/test/ui-fulldeps/pathless-extern-unstable.stderr +++ b/src/test/ui-fulldeps/pathless-extern-unstable.stderr @@ -1,8 +1,8 @@ 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 | -LL | pub use rustc; - | ^^^^^ +LL | pub use rustc_middle; + | ^^^^^^^^^^^^ | = note: see issue #27812 for more information = help: add `#![feature(rustc_private)]` to the crate attributes to enable diff --git a/src/test/ui-fulldeps/plugin-args.stderr b/src/test/ui-fulldeps/plugin-args.stderr index 2b9094c4c44b3..2e255f185e291 100644 --- a/src/test/ui-fulldeps/plugin-args.stderr +++ b/src/test/ui-fulldeps/plugin-args.stderr @@ -12,5 +12,5 @@ LL | #![plugin(empty_plugin(args))] | = note: `#[warn(deprecated)]` on by default -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index 365ae301c0fb5..a3d31d257748d 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -32,6 +32,7 @@ use rustc_parse::new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::source_map::{Spanned, DUMMY_SP, FileName}; use rustc_span::source_map::FilePathMapping; +use rustc_span::symbol::Ident; use rustc_ast::ast::*; use rustc_ast::mut_visit::{self, MutVisitor, visit_clobber}; use rustc_ast::ptr::P; diff --git a/src/test/ui-fulldeps/undef_mask.rs b/src/test/ui-fulldeps/undef_mask.rs deleted file mode 100644 index 0caccad622957..0000000000000 --- a/src/test/ui-fulldeps/undef_mask.rs +++ /dev/null @@ -1,27 +0,0 @@ -// run-pass -// ignore-cross-compile -// ignore-stage1 - -#![feature(rustc_private)] - -extern crate rustc; - -use rustc::mir::interpret::UndefMask; -use rustc::ty::layout::Size; - -fn main() { - let mut mask = UndefMask::new(Size::from_bytes(500), false); - assert!(!mask.get(Size::from_bytes(499))); - mask.set(Size::from_bytes(499), true); - assert!(mask.get(Size::from_bytes(499))); - mask.set_range_inbounds(Size::from_bytes(100), Size::from_bytes(256), true); - for i in 0..100 { - assert!(!mask.get(Size::from_bytes(i))); - } - for i in 100..256 { - assert!(mask.get(Size::from_bytes(i))); - } - for i in 256..499 { - assert!(!mask.get(Size::from_bytes(i))); - } -} diff --git a/src/test/ui-fulldeps/uninit_mask.rs b/src/test/ui-fulldeps/uninit_mask.rs new file mode 100644 index 0000000000000..84ce291016aaf --- /dev/null +++ b/src/test/ui-fulldeps/uninit_mask.rs @@ -0,0 +1,28 @@ +// run-pass +// ignore-cross-compile +// ignore-stage1 + +#![feature(rustc_private)] + +extern crate rustc_middle; +extern crate rustc_target; + +use rustc_middle::mir::interpret::InitMask; +use rustc_target::abi::Size; + +fn main() { + let mut mask = InitMask::new(Size::from_bytes(500), false); + assert!(!mask.get(Size::from_bytes(499))); + mask.set(Size::from_bytes(499), true); + assert!(mask.get(Size::from_bytes(499))); + mask.set_range_inbounds(Size::from_bytes(100), Size::from_bytes(256), true); + for i in 0..100 { + assert!(!mask.get(Size::from_bytes(i))); + } + for i in 100..256 { + assert!(mask.get(Size::from_bytes(i))); + } + for i in 256..499 { + assert!(!mask.get(Size::from_bytes(i))); + } +} diff --git a/src/test/ui/abi/abi-sysv64-register-usage.rs b/src/test/ui/abi/abi-sysv64-register-usage.rs index 0c7e2d906b7c1..fcdff59ffa984 100644 --- a/src/test/ui/abi/abi-sysv64-register-usage.rs +++ b/src/test/ui/abi/abi-sysv64-register-usage.rs @@ -6,7 +6,7 @@ // ignore-arm // ignore-aarch64 -#![feature(asm)] +#![feature(llvm_asm)] #[cfg(target_arch = "x86_64")] pub extern "sysv64" fn all_the_registers(rdi: i64, rsi: i64, rdx: i64, @@ -54,34 +54,34 @@ pub extern "sysv64" fn large_struct_by_val(mut foo: LargeStruct) -> LargeStruct pub fn main() { let result: i64; unsafe { - asm!("mov rdi, 1; - mov rsi, 2; - mov rdx, 3; - mov rcx, 4; - mov r8, 5; - mov r9, 6; - mov eax, 0x3F800000; - movd xmm0, eax; - mov eax, 0x40000000; - movd xmm1, eax; - mov eax, 0x40800000; - movd xmm2, eax; - mov eax, 0x41000000; - movd xmm3, eax; - mov eax, 0x41800000; - movd xmm4, eax; - mov eax, 0x42000000; - movd xmm5, eax; - mov eax, 0x42800000; - movd xmm6, eax; - mov eax, 0x43000000; - movd xmm7, eax; - call r10 - " - : "={rax}"(result) - : "{r10}"(all_the_registers as usize) - : "rdi", "rsi", "rdx", "rcx", "r8", "r9", "r11", "cc", "memory" - : "intel", "alignstack" + llvm_asm!("mov rdi, 1; + mov rsi, 2; + mov rdx, 3; + mov rcx, 4; + mov r8, 5; + mov r9, 6; + mov eax, 0x3F800000; + movd xmm0, eax; + mov eax, 0x40000000; + movd xmm1, eax; + mov eax, 0x40800000; + movd xmm2, eax; + mov eax, 0x41000000; + movd xmm3, eax; + mov eax, 0x41800000; + movd xmm4, eax; + mov eax, 0x42000000; + movd xmm5, eax; + mov eax, 0x42800000; + movd xmm6, eax; + mov eax, 0x43000000; + movd xmm7, eax; + call r10 + " + : "={rax}"(result) + : "{r10}"(all_the_registers as usize) + : "rdi", "rsi", "rdx", "rcx", "r8", "r9", "r11", "cc", "memory" + : "intel", "alignstack" ) } assert_eq!(result, 42); diff --git a/src/test/ui/allocator/custom.rs b/src/test/ui/allocator/custom.rs index c275db14b427c..184e4706a4c86 100644 --- a/src/test/ui/allocator/custom.rs +++ b/src/test/ui/allocator/custom.rs @@ -7,7 +7,7 @@ extern crate helper; -use std::alloc::{self, Global, AllocRef, System, Layout}; +use std::alloc::{self, AllocInit, AllocRef, Global, Layout, System}; use std::sync::atomic::{AtomicUsize, Ordering}; static HITS: AtomicUsize = AtomicUsize::new(0); @@ -37,10 +37,10 @@ fn main() { unsafe { let layout = Layout::from_size_align(4, 2).unwrap(); - let (ptr, _) = Global.alloc(layout.clone()).unwrap(); - helper::work_with(&ptr); + let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); + helper::work_with(&memory.ptr); assert_eq!(HITS.load(Ordering::SeqCst), n + 1); - Global.dealloc(ptr, layout.clone()); + Global.dealloc(memory.ptr, layout); assert_eq!(HITS.load(Ordering::SeqCst), n + 2); let s = String::with_capacity(10); @@ -49,10 +49,10 @@ fn main() { drop(s); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); - let (ptr, _) = System.alloc(layout.clone()).unwrap(); + let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); - helper::work_with(&ptr); - System.dealloc(ptr, layout); + helper::work_with(&memory.ptr); + System.dealloc(memory.ptr, layout); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); } } diff --git a/src/test/ui/allocator/xcrate-use.rs b/src/test/ui/allocator/xcrate-use.rs index e4746d1a7ec09..7de1ab7a55315 100644 --- a/src/test/ui/allocator/xcrate-use.rs +++ b/src/test/ui/allocator/xcrate-use.rs @@ -9,8 +9,8 @@ extern crate custom; extern crate helper; -use std::alloc::{Global, AllocRef, System, Layout}; -use std::sync::atomic::{Ordering, AtomicUsize}; +use std::alloc::{AllocInit, AllocRef, Global, Layout, System}; +use std::sync::atomic::{AtomicUsize, Ordering}; #[global_allocator] static GLOBAL: custom::A = custom::A(AtomicUsize::new(0)); @@ -20,16 +20,16 @@ fn main() { let n = GLOBAL.0.load(Ordering::SeqCst); let layout = Layout::from_size_align(4, 2).unwrap(); - let (ptr, _) = Global.alloc(layout.clone()).unwrap(); - helper::work_with(&ptr); + let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); + helper::work_with(&memory.ptr); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1); - Global.dealloc(ptr, layout.clone()); + Global.dealloc(memory.ptr, layout); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); - let (ptr, _) = System.alloc(layout.clone()).unwrap(); + let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); - helper::work_with(&ptr); - System.dealloc(ptr, layout); + helper::work_with(&memory.ptr); + System.dealloc(memory.ptr, layout); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); } } diff --git a/src/test/ui/anon-params/anon-params-deprecated.fixed b/src/test/ui/anon-params/anon-params-deprecated.fixed index fe42113eb2ee9..d288bba5957a2 100644 --- a/src/test/ui/anon-params/anon-params-deprecated.fixed +++ b/src/test/ui/anon-params/anon-params-deprecated.fixed @@ -1,7 +1,7 @@ #![warn(anonymous_parameters)] // Test for the anonymous_parameters deprecation lint (RFC 1685) -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // edition:2015 // run-rustfix diff --git a/src/test/ui/anon-params/anon-params-deprecated.rs b/src/test/ui/anon-params/anon-params-deprecated.rs index dc0357721ec73..d677e0c32b04a 100644 --- a/src/test/ui/anon-params/anon-params-deprecated.rs +++ b/src/test/ui/anon-params/anon-params-deprecated.rs @@ -1,7 +1,7 @@ #![warn(anonymous_parameters)] // Test for the anonymous_parameters deprecation lint (RFC 1685) -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // edition:2015 // run-rustfix diff --git a/src/test/ui/anon-params/anon-params-deprecated.stderr b/src/test/ui/anon-params/anon-params-deprecated.stderr index 4520559845f47..c1bf5f690ecba 100644 --- a/src/test/ui/anon-params/anon-params-deprecated.stderr +++ b/src/test/ui/anon-params/anon-params-deprecated.stderr @@ -30,3 +30,5 @@ LL | fn bar_with_default_impl(String, String) {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! = note: for more information, see issue #41686 +warning: 3 warnings emitted + diff --git a/src/test/ui/anonymous-higher-ranked-lifetime.stderr b/src/test/ui/anonymous-higher-ranked-lifetime.stderr index c6d9a61bdd95a..de478417d0678 100644 --- a/src/test/ui/anonymous-higher-ranked-lifetime.stderr +++ b/src/test/ui/anonymous-higher-ranked-lifetime.stderr @@ -7,7 +7,7 @@ LL | f1(|_: (), _: ()| {}); | expected signature of `for<'r, 's> fn(&'r (), &'s ()) -> _` ... LL | fn f1(_: F) where F: Fn(&(), &()) {} - | -- ------------ required by this bound in `f1` + | ------------ required by this bound in `f1` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:3:5 @@ -18,7 +18,7 @@ LL | f2(|_: (), _: ()| {}); | expected signature of `for<'a, 'r> fn(&'a (), &'r ()) -> _` ... LL | fn f2(_: F) where F: for<'a> Fn(&'a (), &()) {} - | -- ----------------------- required by this bound in `f2` + | ----------------------- required by this bound in `f2` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:4:5 @@ -29,7 +29,7 @@ LL | f3(|_: (), _: ()| {}); | expected signature of `for<'r> fn(&(), &'r ()) -> _` ... LL | fn f3<'a, F>(_: F) where F: Fn(&'a (), &()) {} - | -- --------------- required by this bound in `f3` + | --------------- required by this bound in `f3` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:5:5 @@ -40,7 +40,7 @@ LL | f4(|_: (), _: ()| {}); | expected signature of `for<'s, 'r> fn(&'s (), &'r ()) -> _` ... LL | fn f4(_: F) where F: for<'r> Fn(&(), &'r ()) {} - | -- ----------------------- required by this bound in `f4` + | ----------------------- required by this bound in `f4` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:6:5 @@ -51,7 +51,7 @@ LL | f5(|_: (), _: ()| {}); | expected signature of `for<'r> fn(&'r (), &'r ()) -> _` ... LL | fn f5(_: F) where F: for<'r> Fn(&'r (), &'r ()) {} - | -- -------------------------- required by this bound in `f5` + | -------------------------- required by this bound in `f5` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:7:5 @@ -62,7 +62,7 @@ LL | g1(|_: (), _: ()| {}); | expected signature of `for<'r> fn(&'r (), std::boxed::Box<(dyn for<'s> std::ops::Fn(&'s ()) + 'static)>) -> _` ... LL | fn g1(_: F) where F: Fn(&(), Box) {} - | -- ------------------------- required by this bound in `g1` + | ------------------------- required by this bound in `g1` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:8:5 @@ -73,7 +73,7 @@ LL | g2(|_: (), _: ()| {}); | expected signature of `for<'r> fn(&'r (), for<'s> fn(&'s ())) -> _` ... LL | fn g2(_: F) where F: Fn(&(), fn(&())) {} - | -- ---------------- required by this bound in `g2` + | ---------------- required by this bound in `g2` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:9:5 @@ -84,7 +84,7 @@ LL | g3(|_: (), _: ()| {}); | expected signature of `for<'s> fn(&'s (), std::boxed::Box<(dyn for<'r> std::ops::Fn(&'r ()) + 'static)>) -> _` ... LL | fn g3(_: F) where F: for<'s> Fn(&'s (), Box) {} - | -- ------------------------------------ required by this bound in `g3` + | ------------------------------------ required by this bound in `g3` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:10:5 @@ -95,7 +95,7 @@ LL | g4(|_: (), _: ()| {}); | expected signature of `for<'s> fn(&'s (), for<'r> fn(&'r ())) -> _` ... LL | fn g4(_: F) where F: Fn(&(), for<'r> fn(&'r ())) {} - | -- --------------------------- required by this bound in `g4` + | --------------------------- required by this bound in `g4` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:11:5 @@ -106,7 +106,7 @@ LL | h1(|_: (), _: (), _: (), _: ()| {}); | expected signature of `for<'r, 's> fn(&'r (), std::boxed::Box<(dyn for<'t0> std::ops::Fn(&'t0 ()) + 'static)>, &'s (), for<'t0, 't1> fn(&'t0 (), &'t1 ())) -> _` ... LL | fn h1(_: F) where F: Fn(&(), Box, &(), fn(&(), &())) {} - | -- -------------------------------------------- required by this bound in `h1` + | -------------------------------------------- required by this bound in `h1` error[E0631]: type mismatch in closure arguments --> $DIR/anonymous-higher-ranked-lifetime.rs:12:5 @@ -117,7 +117,7 @@ LL | h2(|_: (), _: (), _: (), _: ()| {}); | expected signature of `for<'r, 't0> fn(&'r (), std::boxed::Box<(dyn for<'s> std::ops::Fn(&'s ()) + 'static)>, &'t0 (), for<'s, 't1> fn(&'s (), &'t1 ())) -> _` ... LL | fn h2(_: F) where F: for<'t0> Fn(&(), Box, &'t0 (), fn(&(), &())) {} - | -- --------------------------------------------------------- required by this bound in `h2` + | --------------------------------------------------------- required by this bound in `h2` error: aborting due to 11 previous errors diff --git a/src/test/ui/array-break-length.rs b/src/test/ui/array-break-length.rs index 959f4a2babbf8..60589f7c264a6 100644 --- a/src/test/ui/array-break-length.rs +++ b/src/test/ui/array-break-length.rs @@ -1,11 +1,9 @@ fn main() { loop { |_: [_; break]| {} //~ ERROR: `break` outside of a loop - //~^ ERROR mismatched types } loop { |_: [_; continue]| {} //~ ERROR: `continue` outside of a loop - //~^ ERROR mismatched types } } diff --git a/src/test/ui/array-break-length.stderr b/src/test/ui/array-break-length.stderr index 69c7599cce199..93f1c238bcc47 100644 --- a/src/test/ui/array-break-length.stderr +++ b/src/test/ui/array-break-length.stderr @@ -5,30 +5,11 @@ LL | |_: [_; break]| {} | ^^^^^ cannot `break` outside of a loop error[E0268]: `continue` outside of a loop - --> $DIR/array-break-length.rs:8:17 + --> $DIR/array-break-length.rs:7:17 | LL | |_: [_; continue]| {} | ^^^^^^^^ cannot `continue` outside of a loop -error[E0308]: mismatched types - --> $DIR/array-break-length.rs:3:9 - | -LL | |_: [_; break]| {} - | ^^^^^^^^^^^^^^^^^^ expected `()`, found closure - | - = note: expected unit type `()` - found closure `[closure@$DIR/array-break-length.rs:3:9: 3:27]` - -error[E0308]: mismatched types - --> $DIR/array-break-length.rs:8:9 - | -LL | |_: [_; continue]| {} - | ^^^^^^^^^^^^^^^^^^^^^ expected `()`, found closure - | - = note: expected unit type `()` - found closure `[closure@$DIR/array-break-length.rs:8:9: 8:30]` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0268, E0308. -For more information about an error, try `rustc --explain E0268`. +For more information about this error, try `rustc --explain E0268`. diff --git a/src/test/run-fail/bounds-check-no-overflow.rs b/src/test/ui/array-slice-vec/bounds-check-no-overflow.rs similarity index 77% rename from src/test/run-fail/bounds-check-no-overflow.rs rename to src/test/ui/array-slice-vec/bounds-check-no-overflow.rs index 3943f87f7fe0c..3caf5f4499522 100644 --- a/src/test/run-fail/bounds-check-no-overflow.rs +++ b/src/test/ui/array-slice-vec/bounds-check-no-overflow.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:index out of bounds +// ignore-emscripten no processes use std::usize; use std::mem::size_of; diff --git a/src/test/ui/array-slice-vec/dst-raw-slice.rs b/src/test/ui/array-slice-vec/dst-raw-slice.rs new file mode 100644 index 0000000000000..371d16f093a74 --- /dev/null +++ b/src/test/ui/array-slice-vec/dst-raw-slice.rs @@ -0,0 +1,13 @@ +// Test bounds checking for DST raw slices + +// run-fail +// error-pattern:index out of bounds +// ignore-emscripten no processes + +#[allow(unconditional_panic)] +fn main() { + let a: *const [_] = &[1, 2, 3]; + unsafe { + let _b = (*a)[3]; + } +} diff --git a/src/test/ui/array-slice-vec/infer_array_len.rs b/src/test/ui/array-slice-vec/infer_array_len.rs new file mode 100644 index 0000000000000..22fe7cb883888 --- /dev/null +++ b/src/test/ui/array-slice-vec/infer_array_len.rs @@ -0,0 +1,21 @@ +// see issue #70529 +struct A; + +impl From for [u8; 2] { + fn from(a: A) -> Self { + [0; 2] + } +} + +impl From for [u8; 3] { + fn from(a: A) -> Self { + [0; 3] + } +} + + +fn main() { + let a = A; + let [_, _] = a.into(); + //~^ ERROR type annotations needed +} diff --git a/src/test/ui/array-slice-vec/infer_array_len.stderr b/src/test/ui/array-slice-vec/infer_array_len.stderr new file mode 100644 index 0000000000000..6eed4ce4f0c01 --- /dev/null +++ b/src/test/ui/array-slice-vec/infer_array_len.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/infer_array_len.rs:19:9 + | +LL | let [_, _] = a.into(); + | ^^^^^^ consider giving this pattern a type + | + = note: type must be known at this point + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/array-slice-vec/match_arr_unknown_len.rs b/src/test/ui/array-slice-vec/match_arr_unknown_len.rs new file mode 100644 index 0000000000000..45b2889f1ca4c --- /dev/null +++ b/src/test/ui/array-slice-vec/match_arr_unknown_len.rs @@ -0,0 +1,11 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +fn is_123(x: [u32; N]) -> bool { + match x { + [1, 2] => true, //~ ERROR mismatched types + _ => false + } +} + +fn main() {} diff --git a/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr new file mode 100644 index 0000000000000..4fe8572c2d531 --- /dev/null +++ b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr @@ -0,0 +1,21 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/match_arr_unknown_len.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0308]: mismatched types + --> $DIR/match_arr_unknown_len.rs:6:9 + | +LL | [1, 2] => true, + | ^^^^^^ expected `2usize`, found `N` + | + = note: expected array `[u32; 2]` + found array `[u32; N]` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/array-slice-vec/vec-fixed-length.rs b/src/test/ui/array-slice-vec/vec-fixed-length.rs index 5db02ee066bbe..908c39c7951c0 100644 --- a/src/test/ui/array-slice-vec/vec-fixed-length.rs +++ b/src/test/ui/array-slice-vec/vec-fixed-length.rs @@ -9,7 +9,7 @@ fn test_big_vec() {} #[cfg(target_pointer_width = "64")] fn test_big_vec() { - assert_eq!(size_of::<[u8; (1 << 32)]>(), (1 << 32)); + assert_eq!(size_of::<[u8; 1 << 32]>(), (1 << 32)); } fn main() { diff --git a/src/test/ui/asm-concat-src.rs b/src/test/ui/asm-concat-src.rs deleted file mode 100644 index c4160bfeca105..0000000000000 --- a/src/test/ui/asm-concat-src.rs +++ /dev/null @@ -1,9 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 -// ignore-emscripten no asm - -#![feature(asm)] - -pub fn main() { - unsafe { asm!(concat!("", "")) }; -} diff --git a/src/test/ui/asm-in-moved.rs b/src/test/ui/asm-in-moved.rs deleted file mode 100644 index 6525d2f53b099..0000000000000 --- a/src/test/ui/asm-in-moved.rs +++ /dev/null @@ -1,31 +0,0 @@ -// run-pass - -#![feature(asm)] -#![allow(dead_code)] - -use std::cell::Cell; - -#[repr(C)] -struct NoisyDrop<'a>(&'a Cell<&'static str>); -impl<'a> Drop for NoisyDrop<'a> { - fn drop(&mut self) { - self.0.set("destroyed"); - } -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn main() { - let status = Cell::new("alive"); - { - let _y: Box; - let x = Box::new(NoisyDrop(&status)); - unsafe { - asm!("mov $1, $0" : "=r"(_y) : "r"(x)); - } - assert_eq!(status.get(), "alive"); - } - assert_eq!(status.get(), "destroyed"); -} - -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -fn main() {} diff --git a/src/test/ui/asm-in-out-operand.rs b/src/test/ui/asm-in-out-operand.rs deleted file mode 100644 index 13d0363a6a070..0000000000000 --- a/src/test/ui/asm-in-out-operand.rs +++ /dev/null @@ -1,56 +0,0 @@ -// run-pass - -#![feature(asm)] - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -unsafe fn next_power_of_2(n: u32) -> u32 { - let mut tmp = n; - asm!("dec $0" : "+rm"(tmp) :: "cc"); - let mut shift = 1_u32; - while shift <= 16 { - asm!( - "shr %cl, $2 - or $2, $0 - shl $$1, $1" - : "+&rm"(tmp), "+{ecx}"(shift) : "r"(tmp) : "cc" - ); - } - asm!("inc $0" : "+rm"(tmp) :: "cc"); - return tmp; -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn main() { - unsafe { - assert_eq!(64, next_power_of_2(37)); - assert_eq!(2147483648, next_power_of_2(2147483647)); - } - - let mut y: isize = 5; - let x: isize; - unsafe { - // Treat the output as initialization. - asm!( - "shl $2, $1 - add $3, $1 - mov $1, $0" - : "=r"(x), "+r"(y) : "i"(3_usize), "ir"(7_usize) : "cc" - ); - } - assert_eq!(x, 47); - assert_eq!(y, 47); - - let mut x = x + 1; - assert_eq!(x, 48); - - unsafe { - // Assignment to mutable. - // Early clobber "&": - // Forbids the use of a single register by both operands. - asm!("shr $$2, $1; add $1, $0" : "+&r"(x) : "r"(x) : "cc"); - } - assert_eq!(x, 60); -} - -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -pub fn main() {} diff --git a/src/test/ui/asm-indirect-memory.rs b/src/test/ui/asm-indirect-memory.rs deleted file mode 100644 index 2e8011af50295..0000000000000 --- a/src/test/ui/asm-indirect-memory.rs +++ /dev/null @@ -1,43 +0,0 @@ -// run-pass - -#![feature(asm)] - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn read(ptr: &u32) -> u32 { - let out: u32; - unsafe { - asm!("mov $1, $0" : "=r" (out) : "*m" (ptr)); - } - out -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn write(ptr: &mut u32, val: u32) { - unsafe { - asm!("mov $1, $0" : "=*m" (ptr) : "r" (val)); - } -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn replace(ptr: &mut u32, val: u32) -> u32 { - let out: u32; - unsafe { - asm!("mov $0, $1; mov $2, $0" : "+*m" (ptr), "=&r" (out) : "r" (val)); - } - out -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn main() { - let a = 1; - assert_eq!(read(&a), 1); - let mut b = 2; - write(&mut b, 3); - assert_eq!(b, 3); - let mut c = 4; - assert_eq!(replace(&mut c, 5), 4); - assert_eq!(c, 5); -} - -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -pub fn main() {} diff --git a/src/test/ui/asm-out-assign.rs b/src/test/ui/asm-out-assign.rs deleted file mode 100644 index ed63d1b4d492a..0000000000000 --- a/src/test/ui/asm-out-assign.rs +++ /dev/null @@ -1,25 +0,0 @@ -// run-pass - -#![feature(asm)] - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn main() { - let x: isize; - unsafe { - // Treat the output as initialization. - asm!("mov $1, $0" : "=r"(x) : "r"(5_usize)); - } - assert_eq!(x, 5); - - let mut x = x + 1; - assert_eq!(x, 6); - - unsafe { - // Assignment to mutable. - asm!("mov $1, $0" : "=r"(x) : "r"(x + 7)); - } - assert_eq!(x, 13); -} - -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -pub fn main() {} diff --git a/src/test/ui/asm/asm-bad-clobber.rs b/src/test/ui/asm/asm-bad-clobber.rs deleted file mode 100644 index 8406a1cc7a824..0000000000000 --- a/src/test/ui/asm/asm-bad-clobber.rs +++ /dev/null @@ -1,25 +0,0 @@ -// ignore-android -// ignore-arm -// ignore-aarch64 -// ignore-s390x -// ignore-emscripten -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-sparc -// ignore-sparc64 -// ignore-mips -// ignore-mips64 - -#![feature(asm)] - -#[cfg(any(target_arch = "x86", - target_arch = "x86_64"))] - -pub fn main() { - unsafe { - // clobber formatted as register input/output - asm!("xor %eax, %eax" : : : "{eax}"); - //~^ ERROR clobber should not be surrounded by braces - } -} diff --git a/src/test/ui/asm/asm-bad-clobber.stderr b/src/test/ui/asm/asm-bad-clobber.stderr deleted file mode 100644 index a279421241fac..0000000000000 --- a/src/test/ui/asm/asm-bad-clobber.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0664]: clobber should not be surrounded by braces - --> $DIR/asm-bad-clobber.rs:22:37 - | -LL | asm!("xor %eax, %eax" : : : "{eax}"); - | ^^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0664`. diff --git a/src/test/ui/asm/asm-in-bad-modifier.rs b/src/test/ui/asm/asm-in-bad-modifier.rs deleted file mode 100644 index 38cd11e251586..0000000000000 --- a/src/test/ui/asm/asm-in-bad-modifier.rs +++ /dev/null @@ -1,34 +0,0 @@ -// ignore-s390x -// ignore-emscripten -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-sparc -// ignore-sparc64 -// ignore-mips -// ignore-mips64 - -#![feature(asm)] - -fn foo(x: isize) { println!("{}", x); } - -#[cfg(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64"))] -pub fn main() { - let x: isize; - let y: isize; - unsafe { - asm!("mov $1, $0" : "=r"(x) : "=r"(5)); //~ ERROR operand constraint contains '=' - asm!("mov $1, $0" : "=r"(y) : "+r"(5)); //~ ERROR operand constraint contains '+' - } - foo(x); - foo(y); -} - -#[cfg(not(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64")))] -pub fn main() {} diff --git a/src/test/ui/asm/asm-in-bad-modifier.stderr b/src/test/ui/asm/asm-in-bad-modifier.stderr deleted file mode 100644 index d45b3e57038cd..0000000000000 --- a/src/test/ui/asm/asm-in-bad-modifier.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0662]: input operand constraint contains '=' - --> $DIR/asm-in-bad-modifier.rs:23:39 - | -LL | asm!("mov $1, $0" : "=r"(x) : "=r"(5)); - | ^^^^ - -error[E0663]: input operand constraint contains '+' - --> $DIR/asm-in-bad-modifier.rs:24:39 - | -LL | asm!("mov $1, $0" : "=r"(y) : "+r"(5)); - | ^^^^ - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0662, E0663. -For more information about an error, try `rustc --explain E0662`. diff --git a/src/test/ui/asm/asm-literal-escaping.rs b/src/test/ui/asm/asm-literal-escaping.rs deleted file mode 100644 index 8d464e752e637..0000000000000 --- a/src/test/ui/asm/asm-literal-escaping.rs +++ /dev/null @@ -1,12 +0,0 @@ -// build-pass -// only-x86_64 - -#![feature(asm)] - -fn main() { - unsafe { - // "nop" :: "r"(x) : "eax" : "volatile" - let x = 10; - asm!("\x6Eop" :: "\x72"(x) : "\x65ax" : "\x76olatile"); - } -} diff --git a/src/test/ui/asm/asm-misplaced-option.rs b/src/test/ui/asm/asm-misplaced-option.rs deleted file mode 100644 index 14ff4c2e981b3..0000000000000 --- a/src/test/ui/asm/asm-misplaced-option.rs +++ /dev/null @@ -1,35 +0,0 @@ -// check-pass -// ignore-android -// ignore-arm -// ignore-aarch64 -// ignore-s390x -// ignore-emscripten -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-sparc -// ignore-sparc64 -// ignore-mips -// ignore-mips64 - -#![feature(asm)] - -#[cfg(any(target_arch = "x86", - target_arch = "x86_64"))] -fn main() { - // assignment not dead - let mut x: isize = 0; - unsafe { - // extra colon - asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc"); - //~^ WARNING unrecognized option - } - assert_eq!(x, 5); - - unsafe { - // comma in place of a colon - asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile"); - //~^ WARNING expected a clobber, found an option - } - assert_eq!(x, 13); -} diff --git a/src/test/ui/asm/asm-misplaced-option.stderr b/src/test/ui/asm/asm-misplaced-option.stderr deleted file mode 100644 index 3d4b28c3dc444..0000000000000 --- a/src/test/ui/asm/asm-misplaced-option.stderr +++ /dev/null @@ -1,12 +0,0 @@ -warning: unrecognized option - --> $DIR/asm-misplaced-option.rs:24:64 - | -LL | asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc"); - | ^^^^ - -warning: expected a clobber, found an option - --> $DIR/asm-misplaced-option.rs:31:80 - | -LL | asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile"); - | ^^^^^^^^^^ - diff --git a/src/test/ui/asm/asm-out-assign-imm.rs b/src/test/ui/asm/asm-out-assign-imm.rs deleted file mode 100644 index 7a8be2a133ec7..0000000000000 --- a/src/test/ui/asm/asm-out-assign-imm.rs +++ /dev/null @@ -1,34 +0,0 @@ -// ignore-s390x -// ignore-emscripten -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-sparc -// ignore-sparc64 -// ignore-mips -// ignore-mips64 - -#![feature(asm)] - -fn foo(x: isize) { println!("{}", x); } - -#[cfg(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64"))] -pub fn main() { - let x: isize; - x = 1; - foo(x); - unsafe { - asm!("mov $1, $0" : "=r"(x) : "r"(5)); - //~^ ERROR cannot assign twice to immutable variable `x` - } - foo(x); -} - -#[cfg(not(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64")))] -pub fn main() {} diff --git a/src/test/ui/asm/asm-out-assign-imm.stderr b/src/test/ui/asm/asm-out-assign-imm.stderr deleted file mode 100644 index ac38218b8492f..0000000000000 --- a/src/test/ui/asm/asm-out-assign-imm.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0384]: cannot assign twice to immutable variable `x` - --> $DIR/asm-out-assign-imm.rs:24:34 - | -LL | let x: isize; - | - help: make this binding mutable: `mut x` -LL | x = 1; - | ----- first assignment to `x` -... -LL | asm!("mov $1, $0" : "=r"(x) : "r"(5)); - | ^ cannot assign twice to immutable variable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0384`. diff --git a/src/test/ui/asm/asm-out-no-modifier.rs b/src/test/ui/asm/asm-out-no-modifier.rs deleted file mode 100644 index d9142b9f2e0c8..0000000000000 --- a/src/test/ui/asm/asm-out-no-modifier.rs +++ /dev/null @@ -1,31 +0,0 @@ -// ignore-s390x -// ignore-emscripten -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-sparc -// ignore-sparc64 -// ignore-mips -// ignore-mips64 - -#![feature(asm)] - -fn foo(x: isize) { println!("{}", x); } - -#[cfg(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64"))] -pub fn main() { - let x: isize; - unsafe { - asm!("mov $1, $0" : "r"(x) : "r"(5)); //~ ERROR output operand constraint lacks '=' - } - foo(x); -} - -#[cfg(not(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64")))] -pub fn main() {} diff --git a/src/test/ui/asm/asm-out-no-modifier.stderr b/src/test/ui/asm/asm-out-no-modifier.stderr deleted file mode 100644 index 99134ceba3327..0000000000000 --- a/src/test/ui/asm/asm-out-no-modifier.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0661]: output operand constraint lacks '=' or '+' - --> $DIR/asm-out-no-modifier.rs:22:29 - | -LL | asm!("mov $1, $0" : "r"(x) : "r"(5)); - | ^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0661`. diff --git a/src/test/ui/asm/asm-out-read-uninit.rs b/src/test/ui/asm/asm-out-read-uninit.rs deleted file mode 100644 index 78458ff60d4aa..0000000000000 --- a/src/test/ui/asm/asm-out-read-uninit.rs +++ /dev/null @@ -1,32 +0,0 @@ -// ignore-s390x -// ignore-emscripten -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-sparc -// ignore-sparc64 -// ignore-mips -// ignore-mips64 - -#![feature(asm)] - -fn foo(x: isize) { println!("{}", x); } - -#[cfg(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64"))] -pub fn main() { - let x: isize; - unsafe { - asm!("mov $1, $0" : "=r"(x) : "r"(x)); - //~^ ERROR use of possibly-uninitialized variable: `x` - } - foo(x); -} - -#[cfg(not(any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64")))] -pub fn main() {} diff --git a/src/test/ui/asm/asm-out-read-uninit.stderr b/src/test/ui/asm/asm-out-read-uninit.stderr deleted file mode 100644 index 71aeda2ad4d2e..0000000000000 --- a/src/test/ui/asm/asm-out-read-uninit.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0381]: use of possibly-uninitialized variable: `x` - --> $DIR/asm-out-read-uninit.rs:22:43 - | -LL | asm!("mov $1, $0" : "=r"(x) : "r"(x)); - | ^ use of possibly-uninitialized `x` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0381`. diff --git a/src/test/ui/asm/asm-parse-errors.rs b/src/test/ui/asm/asm-parse-errors.rs deleted file mode 100644 index e712ac5826e2c..0000000000000 --- a/src/test/ui/asm/asm-parse-errors.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(asm)] - -fn main() { - asm!(); //~ ERROR requires a string literal as an argument - asm!("nop" : struct); //~ ERROR expected string literal - asm!("mov %eax, $$0x2" : struct); //~ ERROR expected string literal - asm!("mov %eax, $$0x2" : "={eax}" struct); //~ ERROR expected `(` - asm!("mov %eax, $$0x2" : "={eax}"(struct)); //~ ERROR expected expression - asm!("in %dx, %al" : "={al}"(result) : struct); //~ ERROR expected string literal - asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); //~ ERROR expected `(` - asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); //~ ERROR expected expression - asm!("mov $$0x200, %eax" : : : struct); //~ ERROR expected string literal - asm!("mov eax, 2" : "={eax}"(foo) : : : struct); //~ ERROR expected string literal - asm!(123); //~ ERROR inline assembly must be a string literal -} diff --git a/src/test/ui/asm/asm-parse-errors.stderr b/src/test/ui/asm/asm-parse-errors.stderr deleted file mode 100644 index 2b29332fef5e5..0000000000000 --- a/src/test/ui/asm/asm-parse-errors.stderr +++ /dev/null @@ -1,68 +0,0 @@ -error: macro requires a string literal as an argument - --> $DIR/asm-parse-errors.rs:4:5 - | -LL | asm!(); - | ^^^^^^^ string literal required - -error: expected string literal - --> $DIR/asm-parse-errors.rs:5:18 - | -LL | asm!("nop" : struct); - | ^^^^^^ not a string literal - -error: expected string literal - --> $DIR/asm-parse-errors.rs:6:30 - | -LL | asm!("mov %eax, $$0x2" : struct); - | ^^^^^^ not a string literal - -error: expected `(`, found keyword `struct` - --> $DIR/asm-parse-errors.rs:7:39 - | -LL | asm!("mov %eax, $$0x2" : "={eax}" struct); - | ^^^^^^ expected `(` - -error: expected expression, found keyword `struct` - --> $DIR/asm-parse-errors.rs:8:39 - | -LL | asm!("mov %eax, $$0x2" : "={eax}"(struct)); - | ^^^^^^ expected expression - -error: expected string literal - --> $DIR/asm-parse-errors.rs:9:44 - | -LL | asm!("in %dx, %al" : "={al}"(result) : struct); - | ^^^^^^ not a string literal - -error: expected `(`, found keyword `struct` - --> $DIR/asm-parse-errors.rs:10:51 - | -LL | asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); - | ^^^^^^ expected `(` - -error: expected expression, found keyword `struct` - --> $DIR/asm-parse-errors.rs:11:51 - | -LL | asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); - | ^^^^^^ expected expression - -error: expected string literal - --> $DIR/asm-parse-errors.rs:12:36 - | -LL | asm!("mov $$0x200, %eax" : : : struct); - | ^^^^^^ not a string literal - -error: expected string literal - --> $DIR/asm-parse-errors.rs:13:45 - | -LL | asm!("mov eax, 2" : "={eax}"(foo) : : : struct); - | ^^^^^^ not a string literal - -error: inline assembly must be a string literal - --> $DIR/asm-parse-errors.rs:14:10 - | -LL | asm!(123); - | ^^^ - -error: aborting due to 11 previous errors - diff --git a/src/test/ui/asm/bad-options.rs b/src/test/ui/asm/bad-options.rs new file mode 100644 index 0000000000000..755fc2ca238aa --- /dev/null +++ b/src/test/ui/asm/bad-options.rs @@ -0,0 +1,18 @@ +// only-x86_64 + +#![feature(asm)] + +fn main() { + let mut foo = 0; + unsafe { + asm!("", options(nomem, readonly)); + //~^ ERROR the `nomem` and `readonly` options are mutually exclusive + asm!("", options(pure, nomem, noreturn)); + //~^ ERROR the `pure` and `noreturn` options are mutually exclusive + //~^^ ERROR asm with `pure` option must have at least one output + asm!("{}", in(reg) foo, options(pure, nomem)); + //~^ ERROR asm with `pure` option must have at least one output + asm!("{}", out(reg) foo, options(noreturn)); + //~^ ERROR asm outputs are not allowed with the `noreturn` option + } +} diff --git a/src/test/ui/asm/bad-options.stderr b/src/test/ui/asm/bad-options.stderr new file mode 100644 index 0000000000000..c5e8e2ccf44cc --- /dev/null +++ b/src/test/ui/asm/bad-options.stderr @@ -0,0 +1,32 @@ +error: the `nomem` and `readonly` options are mutually exclusive + --> $DIR/bad-options.rs:8:18 + | +LL | asm!("", options(nomem, readonly)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the `pure` and `noreturn` options are mutually exclusive + --> $DIR/bad-options.rs:10:18 + | +LL | asm!("", options(pure, nomem, noreturn)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: asm with `pure` option must have at least one output + --> $DIR/bad-options.rs:10:18 + | +LL | asm!("", options(pure, nomem, noreturn)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: asm with `pure` option must have at least one output + --> $DIR/bad-options.rs:13:33 + | +LL | asm!("{}", in(reg) foo, options(pure, nomem)); + | ^^^^^^^^^^^^^^^^^^^^ + +error: asm outputs are not allowed with the `noreturn` option + --> $DIR/bad-options.rs:15:20 + | +LL | asm!("{}", out(reg) foo, options(noreturn)); + | ^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/asm/bad-reg.rs b/src/test/ui/asm/bad-reg.rs new file mode 100644 index 0000000000000..016ea9329c4d0 --- /dev/null +++ b/src/test/ui/asm/bad-reg.rs @@ -0,0 +1,55 @@ +// only-x86_64 +// compile-flags: -C target-feature=+avx2 + +#![feature(asm)] + +fn main() { + let mut foo = 0; + let mut bar = 0; + unsafe { + // Bad register/register class + + asm!("{}", in(foo) foo); + //~^ ERROR invalid register class `foo`: unknown register class + asm!("", in("foo") foo); + //~^ ERROR invalid register `foo`: unknown register + asm!("{:z}", in(reg) foo); + //~^ ERROR invalid asm template modifier for this register class + asm!("{:r}", in(xmm_reg) foo); + //~^ ERROR invalid asm template modifier for this register class + asm!("{:a}", const 0); + //~^ ERROR asm template modifiers are not allowed for `const` arguments + asm!("{:a}", sym main); + //~^ ERROR asm template modifiers are not allowed for `sym` arguments + asm!("{}", in(zmm_reg) foo); + //~^ ERROR register class `zmm_reg` requires the `avx512f` target feature + asm!("", in("zmm0") foo); + //~^ ERROR register class `zmm_reg` requires the `avx512f` target feature + asm!("", in("ebp") foo); + //~^ ERROR invalid register `ebp`: the frame pointer cannot be used as an operand + asm!("", in("rsp") foo); + //~^ ERROR invalid register `rsp`: the stack pointer cannot be used as an operand + asm!("", in("ip") foo); + //~^ ERROR invalid register `ip`: the instruction pointer cannot be used as an operand + asm!("", in("st(2)") foo); + //~^ ERROR invalid register `st(2)`: x87 registers are not currently supported as operands + asm!("", in("mm0") foo); + //~^ ERROR invalid register `mm0`: MMX registers are not currently supported as operands + asm!("", in("k0") foo); + //~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand + + // Explicit register conflicts + // (except in/lateout which don't conflict) + + asm!("", in("eax") foo, in("al") bar); + //~^ ERROR register `al` conflicts with register `ax` + asm!("", in("rax") foo, out("rax") bar); + //~^ ERROR register `ax` conflicts with register `ax` + asm!("", in("al") foo, lateout("al") bar); + asm!("", in("xmm0") foo, in("ymm0") bar); + //~^ ERROR register `ymm0` conflicts with register `xmm0` + asm!("", in("xmm0") foo, out("ymm0") bar); + //~^ ERROR register `ymm0` conflicts with register `xmm0` + asm!("", in("xmm0") foo, lateout("ymm0") bar); + } +} diff --git a/src/test/ui/asm/bad-reg.stderr b/src/test/ui/asm/bad-reg.stderr new file mode 100644 index 0000000000000..c6b7d310dfa6c --- /dev/null +++ b/src/test/ui/asm/bad-reg.stderr @@ -0,0 +1,142 @@ +error: invalid register class `foo`: unknown register class + --> $DIR/bad-reg.rs:12:20 + | +LL | asm!("{}", in(foo) foo); + | ^^^^^^^^^^^ + +error: invalid register `foo`: unknown register + --> $DIR/bad-reg.rs:14:18 + | +LL | asm!("", in("foo") foo); + | ^^^^^^^^^^^^^ + +error: invalid asm template modifier for this register class + --> $DIR/bad-reg.rs:16:15 + | +LL | asm!("{:z}", in(reg) foo); + | ^^^^ ----------- argument + | | + | template modifier + | + = note: the `reg` register class supports the following template modifiers: `l`, `x`, `e`, `r` + +error: invalid asm template modifier for this register class + --> $DIR/bad-reg.rs:18:15 + | +LL | asm!("{:r}", in(xmm_reg) foo); + | ^^^^ --------------- argument + | | + | template modifier + | + = note: the `xmm_reg` register class supports the following template modifiers: `x`, `y`, `z` + +error: asm template modifiers are not allowed for `const` arguments + --> $DIR/bad-reg.rs:20:15 + | +LL | asm!("{:a}", const 0); + | ^^^^ ------- argument + | | + | template modifier + +error: asm template modifiers are not allowed for `sym` arguments + --> $DIR/bad-reg.rs:22:15 + | +LL | asm!("{:a}", sym main); + | ^^^^ -------- argument + | | + | template modifier + +error: register class `zmm_reg` requires the `avx512f` target feature + --> $DIR/bad-reg.rs:24:20 + | +LL | asm!("{}", in(zmm_reg) foo); + | ^^^^^^^^^^^^^^^ + +error: register class `zmm_reg` requires the `avx512f` target feature + --> $DIR/bad-reg.rs:26:18 + | +LL | asm!("", in("zmm0") foo); + | ^^^^^^^^^^^^^^ + +error: invalid register `ebp`: the frame pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:28:18 + | +LL | asm!("", in("ebp") foo); + | ^^^^^^^^^^^^^ + +error: invalid register `rsp`: the stack pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:30:18 + | +LL | asm!("", in("rsp") foo); + | ^^^^^^^^^^^^^ + +error: invalid register `ip`: the instruction pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:32:18 + | +LL | asm!("", in("ip") foo); + | ^^^^^^^^^^^^ + +error: invalid register `st(2)`: x87 registers are not currently supported as operands for inline asm + --> $DIR/bad-reg.rs:34:18 + | +LL | asm!("", in("st(2)") foo); + | ^^^^^^^^^^^^^^^ + +error: invalid register `mm0`: MMX registers are not currently supported as operands for inline asm + --> $DIR/bad-reg.rs:36:18 + | +LL | asm!("", in("mm0") foo); + | ^^^^^^^^^^^^^ + +error: invalid register `k0`: the k0 AVX mask register cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:38:18 + | +LL | asm!("", in("k0") foo); + | ^^^^^^^^^^^^ + +error: register `al` conflicts with register `ax` + --> $DIR/bad-reg.rs:44:33 + | +LL | asm!("", in("eax") foo, in("al") bar); + | ------------- ^^^^^^^^^^^^ register `al` + | | + | register `ax` + +error: register `ax` conflicts with register `ax` + --> $DIR/bad-reg.rs:46:33 + | +LL | asm!("", in("rax") foo, out("rax") bar); + | ------------- ^^^^^^^^^^^^^^ register `ax` + | | + | register `ax` + | +help: use `lateout` instead of `out` to avoid conflict + --> $DIR/bad-reg.rs:46:18 + | +LL | asm!("", in("rax") foo, out("rax") bar); + | ^^^^^^^^^^^^^ + +error: register `ymm0` conflicts with register `xmm0` + --> $DIR/bad-reg.rs:49:34 + | +LL | asm!("", in("xmm0") foo, in("ymm0") bar); + | -------------- ^^^^^^^^^^^^^^ register `ymm0` + | | + | register `xmm0` + +error: register `ymm0` conflicts with register `xmm0` + --> $DIR/bad-reg.rs:51:34 + | +LL | asm!("", in("xmm0") foo, out("ymm0") bar); + | -------------- ^^^^^^^^^^^^^^^ register `ymm0` + | | + | register `xmm0` + | +help: use `lateout` instead of `out` to avoid conflict + --> $DIR/bad-reg.rs:51:18 + | +LL | asm!("", in("xmm0") foo, out("ymm0") bar); + | ^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors + diff --git a/src/test/ui/asm/bad-template.rs b/src/test/ui/asm/bad-template.rs new file mode 100644 index 0000000000000..0b333eca1ab91 --- /dev/null +++ b/src/test/ui/asm/bad-template.rs @@ -0,0 +1,26 @@ +// only-x86_64 + +#![feature(asm)] + +fn main() { + let mut foo = 0; + unsafe { + asm!("{}"); + //~^ ERROR invalid reference to argument at index 0 + asm!("{1}", in(reg) foo); + //~^ ERROR invalid reference to argument at index 1 + //~^^ ERROR argument never used + asm!("{a}"); + //~^ ERROR there is no argument named `a` + asm!("{}", a = in(reg) foo); + //~^ ERROR invalid reference to argument at index 0 + //~^^ ERROR argument never used + asm!("{1}", a = in(reg) foo); + //~^ ERROR invalid reference to argument at index 1 + //~^^ ERROR named argument never used + asm!("{}", in("eax") foo); + //~^ ERROR invalid reference to argument at index 0 + asm!("{:foo}", in(reg) foo); + //~^ ERROR asm template modifier must be a single character + } +} diff --git a/src/test/ui/asm/bad-template.stderr b/src/test/ui/asm/bad-template.stderr new file mode 100644 index 0000000000000..2de76ef824192 --- /dev/null +++ b/src/test/ui/asm/bad-template.stderr @@ -0,0 +1,86 @@ +error: invalid reference to argument at index 0 + --> $DIR/bad-template.rs:8:15 + | +LL | asm!("{}"); + | ^^ from here + | + = note: no arguments were given + +error: invalid reference to argument at index 1 + --> $DIR/bad-template.rs:10:15 + | +LL | asm!("{1}", in(reg) foo); + | ^^^ from here + | + = note: there is 1 argument + +error: argument never used + --> $DIR/bad-template.rs:10:21 + | +LL | asm!("{1}", in(reg) foo); + | ^^^^^^^^^^^ argument never used + +error: there is no argument named `a` + --> $DIR/bad-template.rs:13:15 + | +LL | asm!("{a}"); + | ^^^ + +error: invalid reference to argument at index 0 + --> $DIR/bad-template.rs:15:15 + | +LL | asm!("{}", a = in(reg) foo); + | ^^ --------------- named argument + | | + | from here + | + = note: no positional arguments were given +note: named arguments cannot be referenced by position + --> $DIR/bad-template.rs:15:20 + | +LL | asm!("{}", a = in(reg) foo); + | ^^^^^^^^^^^^^^^ + +error: named argument never used + --> $DIR/bad-template.rs:15:20 + | +LL | asm!("{}", a = in(reg) foo); + | ^^^^^^^^^^^^^^^ named argument never used + +error: invalid reference to argument at index 1 + --> $DIR/bad-template.rs:18:15 + | +LL | asm!("{1}", a = in(reg) foo); + | ^^^ from here + | + = note: no positional arguments were given + +error: named argument never used + --> $DIR/bad-template.rs:18:21 + | +LL | asm!("{1}", a = in(reg) foo); + | ^^^^^^^^^^^^^^^ named argument never used + +error: invalid reference to argument at index 0 + --> $DIR/bad-template.rs:21:15 + | +LL | asm!("{}", in("eax") foo); + | ^^ ------------- explicit register argument + | | + | from here + | + = note: no positional arguments were given +note: explicit register arguments cannot be used in the asm template + --> $DIR/bad-template.rs:21:20 + | +LL | asm!("{}", in("eax") foo); + | ^^^^^^^^^^^^^ + +error: asm template modifier must be a single character + --> $DIR/bad-template.rs:23:17 + | +LL | asm!("{:foo}", in(reg) foo); + | ^^^ + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/asm/const.rs b/src/test/ui/asm/const.rs new file mode 100644 index 0000000000000..e08da24f44a22 --- /dev/null +++ b/src/test/ui/asm/const.rs @@ -0,0 +1,56 @@ +// no-system-llvm +// only-x86_64 +// run-pass + +#![feature(asm)] + +use std::mem::size_of; + +trait Proj { + const C: usize; +} +impl Proj for i8 { + const C: usize = 8; +} +impl Proj for i16 { + const C: usize = 16; +} + +const fn constfn(x: usize) -> usize { + x +} + +fn generic() { + unsafe { + let a: usize; + asm!("mov {}, {}", out(reg) a, const size_of::()); + assert_eq!(a, size_of::()); + + let b: usize; + asm!("mov {}, {}", out(reg) b, const size_of::() + constfn(5)); + assert_eq!(b, size_of::() + 5); + + let c: usize; + asm!("mov {}, {}", out(reg) c, const T::C); + assert_eq!(c, T::C); + } +} + +fn main() { + unsafe { + let a: usize; + asm!("mov {}, {}", out(reg) a, const 5); + assert_eq!(a, 5); + + let b: usize; + asm!("mov {}, {}", out(reg) b, const constfn(5)); + assert_eq!(b, 5); + + let c: usize; + asm!("mov {}, {}", out(reg) c, const constfn(5) + constfn(5)); + assert_eq!(c, 10); + } + + generic::(); + generic::(); +} diff --git a/src/test/ui/asm/issue-51431.rs b/src/test/ui/asm/issue-51431.rs deleted file mode 100644 index 4cef42d17d600..0000000000000 --- a/src/test/ui/asm/issue-51431.rs +++ /dev/null @@ -1,11 +0,0 @@ -// build-fail -// ignore-emscripten no asm! support - -#![feature(asm)] - -fn main() { - unsafe { - asm! {"mov $0,$1"::"0"("bx"),"1"(0x00)} - //~^ ERROR: invalid value for constraint in inline assembly - } -} diff --git a/src/test/ui/asm/issue-51431.stderr b/src/test/ui/asm/issue-51431.stderr deleted file mode 100644 index a024f3311f186..0000000000000 --- a/src/test/ui/asm/issue-51431.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0669]: invalid value for constraint in inline assembly - --> $DIR/issue-51431.rs:8:32 - | -LL | asm! {"mov $0,$1"::"0"("bx"),"1"(0x00)} - | ^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0669`. diff --git a/src/test/ui/asm/issue-62046.rs b/src/test/ui/asm/issue-62046.rs deleted file mode 100644 index 105dadd5fd373..0000000000000 --- a/src/test/ui/asm/issue-62046.rs +++ /dev/null @@ -1,11 +0,0 @@ -// build-fail -// ignore-emscripten no asm! support - -#![feature(asm)] - -fn main() { - unsafe { - asm!("nop" : "+r"("r15")); - //~^ malformed inline assembly - } -} diff --git a/src/test/ui/asm/issue-69092.rs b/src/test/ui/asm/issue-69092.rs deleted file mode 100644 index caa5c2e0b9f96..0000000000000 --- a/src/test/ui/asm/issue-69092.rs +++ /dev/null @@ -1,10 +0,0 @@ -// build-fail -// ignore-emscripten no asm! support -// Regression test for #69092 - -#![feature(asm)] - -fn main() { - unsafe { asm!(".ascii \"Xen\0\""); } - //~^ ERROR: :1:9: error: expected string in '.ascii' directive -} diff --git a/src/test/ui/asm/issue-69092.stderr b/src/test/ui/asm/issue-69092.stderr deleted file mode 100644 index 5661097cb8b80..0000000000000 --- a/src/test/ui/asm/issue-69092.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: :1:9: error: expected string in '.ascii' directive - .ascii "Xen - ^ - - --> $DIR/issue-69092.rs:8:14 - | -LL | unsafe { asm!(".ascii \"Xen\0\""); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/asm/noreturn.rs b/src/test/ui/asm/noreturn.rs new file mode 100644 index 0000000000000..5e1ee93bfb073 --- /dev/null +++ b/src/test/ui/asm/noreturn.rs @@ -0,0 +1,17 @@ +// only-x86_64 +// check-pass + +#![feature(asm, never_type)] +#![crate_type = "rlib"] + +pub unsafe fn asm1() { + let _: () = asm!(""); +} + +pub unsafe fn asm2() { + let _: ! = asm!("", options(noreturn)); +} + +pub unsafe fn asm3() -> ! { + asm!("", options(noreturn)); +} diff --git a/src/test/ui/asm/parse-error.rs b/src/test/ui/asm/parse-error.rs new file mode 100644 index 0000000000000..2b1f018f3642e --- /dev/null +++ b/src/test/ui/asm/parse-error.rs @@ -0,0 +1,56 @@ +// only-x86_64 + +#![feature(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 one of + 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 argument to `sym` must be a path expression + 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(), options()); + //~^ ERROR asm options cannot be specified multiple times + asm!("", options(), options(), options()); + //~^ ERROR asm options cannot be specified multiple times + //~^^ ERROR asm options cannot be specified multiple times + asm!("{}", options(), const foo); + //~^ ERROR arguments are not allowed after options + asm!("{a}", a = const foo, a = const bar); + //~^ ERROR duplicate argument named `a` + //~^^ ERROR argument never used + asm!("", a = in("eax") foo); + //~^ ERROR explicit register arguments cannot have names + asm!("{a}", in("eax") foo, a = const bar); + //~^ ERROR named arguments cannot follow explicit register arguments + asm!("{a}", in("eax") foo, a = const bar); + //~^ ERROR named arguments cannot follow explicit register arguments + asm!("{1}", in("eax") foo, const bar); + //~^ ERROR positional arguments cannot follow named arguments or explicit register arguments + } +} diff --git a/src/test/ui/asm/parse-error.stderr b/src/test/ui/asm/parse-error.stderr new file mode 100644 index 0000000000000..fa422f56bece5 --- /dev/null +++ b/src/test/ui/asm/parse-error.stderr @@ -0,0 +1,162 @@ +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 one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `foo` + --> $DIR/parse-error.rs:15:20 + | +LL | asm!("{}", foo); + | ^^^ expected one of 8 possible tokens + +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: argument to `sym` must be a path expression + --> $DIR/parse-error.rs:29:24 + | +LL | asm!("{}", sym foo + bar); + | ^^^^^^^^^ + +error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, or `readonly`, found `foo` + --> $DIR/parse-error.rs:31:26 + | +LL | asm!("", options(foo)); + | ^^^ expected one of 8 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`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, or `readonly`, found `foo` + --> $DIR/parse-error.rs:35:33 + | +LL | asm!("", options(nomem, foo)); + | ^^^ expected one of 8 possible tokens + +error: asm options cannot be specified multiple times + --> $DIR/parse-error.rs:37:29 + | +LL | asm!("", options(), options()); + | --------- ^^^^^^^^^ duplicate options + | | + | previously here + +error: asm options cannot be specified multiple times + --> $DIR/parse-error.rs:39:29 + | +LL | asm!("", options(), options(), options()); + | --------- ^^^^^^^^^ duplicate options + | | + | previously here + +error: asm options cannot be specified multiple times + --> $DIR/parse-error.rs:39:40 + | +LL | asm!("", options(), options(), options()); + | --------- ^^^^^^^^^ duplicate options + | | + | previously here + +error: arguments are not allowed after options + --> $DIR/parse-error.rs:42:31 + | +LL | asm!("{}", options(), const foo); + | --------- ^^^^^^^^^ argument + | | + | previous options + +error: duplicate argument named `a` + --> $DIR/parse-error.rs:44:36 + | +LL | asm!("{a}", a = const foo, a = const bar); + | ------------- ^^^^^^^^^^^^^ duplicate argument + | | + | previously here + +error: argument never used + --> $DIR/parse-error.rs:44:36 + | +LL | asm!("{a}", a = const foo, a = const bar); + | ^^^^^^^^^^^^^ argument never used + +error: explicit register arguments cannot have names + --> $DIR/parse-error.rs:47:18 + | +LL | asm!("", a = in("eax") foo); + | ^^^^^^^^^^^^^^^^^ + +error: named arguments cannot follow explicit register arguments + --> $DIR/parse-error.rs:49:36 + | +LL | asm!("{a}", in("eax") foo, a = const bar); + | ------------- ^^^^^^^^^^^^^ named argument + | | + | explicit register argument + +error: named arguments cannot follow explicit register arguments + --> $DIR/parse-error.rs:51:36 + | +LL | asm!("{a}", in("eax") foo, a = const bar); + | ------------- ^^^^^^^^^^^^^ named argument + | | + | explicit register argument + +error: positional arguments cannot follow named arguments or explicit register arguments + --> $DIR/parse-error.rs:53:36 + | +LL | asm!("{1}", in("eax") foo, const bar); + | ------------- ^^^^^^^^^ positional argument + | | + | explicit register argument + +error: aborting due to 24 previous errors + diff --git a/src/test/ui/asm/rustfix-asm.fixed b/src/test/ui/asm/rustfix-asm.fixed new file mode 100644 index 0000000000000..c9271059810c7 --- /dev/null +++ b/src/test/ui/asm/rustfix-asm.fixed @@ -0,0 +1,16 @@ +// run-rustfix +// only-x86_64 + +#![feature(asm, llvm_asm)] + +fn main() { + unsafe { + let x = 1; + let y: i32; + llvm_asm!("" :: "r" (x)); + //~^ ERROR legacy asm! syntax is no longer supported + llvm_asm!("" : "=r" (y)); + //~^ ERROR legacy asm! syntax is no longer supported + let _ = y; + } +} diff --git a/src/test/ui/asm/rustfix-asm.rs b/src/test/ui/asm/rustfix-asm.rs new file mode 100644 index 0000000000000..a108595ca1b66 --- /dev/null +++ b/src/test/ui/asm/rustfix-asm.rs @@ -0,0 +1,16 @@ +// run-rustfix +// only-x86_64 + +#![feature(asm, llvm_asm)] + +fn main() { + unsafe { + let x = 1; + let y: i32; + asm!("" :: "r" (x)); + //~^ ERROR legacy asm! syntax is no longer supported + asm!("" : "=r" (y)); + //~^ ERROR legacy asm! syntax is no longer supported + let _ = y; + } +} diff --git a/src/test/ui/asm/rustfix-asm.stderr b/src/test/ui/asm/rustfix-asm.stderr new file mode 100644 index 0000000000000..28675b51d15fb --- /dev/null +++ b/src/test/ui/asm/rustfix-asm.stderr @@ -0,0 +1,18 @@ +error: legacy asm! syntax is no longer supported + --> $DIR/rustfix-asm.rs:10:9 + | +LL | asm!("" :: "r" (x)); + | ----^^^^^^^^^^^^^^^^ + | | + | help: replace with: `llvm_asm!` + +error: legacy asm! syntax is no longer supported + --> $DIR/rustfix-asm.rs:12:9 + | +LL | asm!("" : "=r" (y)); + | ----^^^^^^^^^^^^^^^^ + | | + | help: replace with: `llvm_asm!` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/asm/type-check-1.rs b/src/test/ui/asm/type-check-1.rs new file mode 100644 index 0000000000000..7880382c3b74f --- /dev/null +++ b/src/test/ui/asm/type-check-1.rs @@ -0,0 +1,25 @@ +// only-x86_64 + +#![feature(asm)] + +fn main() { + unsafe { + // Outputs must be place expressions + + asm!("{}", in(reg) 1 + 2); + asm!("{}", out(reg) 1 + 2); + //~^ ERROR invalid asm output + asm!("{}", inout(reg) 1 + 2); + //~^ ERROR invalid asm output + + // Operands must be sized + + let v: [u64; 3] = [0, 1, 2]; + asm!("{}", in(reg) v[..]); + //~^ ERROR the size for values of type `[u64]` cannot be known at compilation time + asm!("{}", out(reg) v[..]); + //~^ ERROR the size for values of type `[u64]` cannot be known at compilation time + asm!("{}", inout(reg) v[..]); + //~^ ERROR the size for values of type `[u64]` cannot be known at compilation time + } +} diff --git a/src/test/ui/asm/type-check-1.stderr b/src/test/ui/asm/type-check-1.stderr new file mode 100644 index 0000000000000..7c9c041f45784 --- /dev/null +++ b/src/test/ui/asm/type-check-1.stderr @@ -0,0 +1,45 @@ +error: invalid asm output + --> $DIR/type-check-1.rs:10:29 + | +LL | asm!("{}", out(reg) 1 + 2); + | ^^^^^ cannot assign to this expression + +error: invalid asm output + --> $DIR/type-check-1.rs:12:31 + | +LL | asm!("{}", inout(reg) 1 + 2); + | ^^^^^ cannot assign to this expression + +error[E0277]: the size for values of type `[u64]` cannot be known at compilation time + --> $DIR/type-check-1.rs:18:28 + | +LL | asm!("{}", in(reg) v[..]); + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u64]` + = note: to learn more, visit + = note: all inline asm arguments must have a statically known size + +error[E0277]: the size for values of type `[u64]` cannot be known at compilation time + --> $DIR/type-check-1.rs:20:29 + | +LL | asm!("{}", out(reg) v[..]); + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u64]` + = note: to learn more, visit + = note: all inline asm arguments must have a statically known size + +error[E0277]: the size for values of type `[u64]` cannot be known at compilation time + --> $DIR/type-check-1.rs:22:31 + | +LL | asm!("{}", inout(reg) v[..]); + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u64]` + = note: to learn more, visit + = note: all inline asm arguments must have a statically known size + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/asm/type-check-2.rs b/src/test/ui/asm/type-check-2.rs new file mode 100644 index 0000000000000..1652e9e4c9f66 --- /dev/null +++ b/src/test/ui/asm/type-check-2.rs @@ -0,0 +1,104 @@ +// only-x86_64 + +#![feature(asm, repr_simd, never_type)] + +#[repr(simd)] +struct SimdNonCopy(f32, f32, f32, f32); + +fn main() { + unsafe { + // Inputs must be initialized + + let x: u64; + asm!("{}", in(reg) x); + //~^ ERROR use of possibly-uninitialized variable: `x` + let mut y: u64; + asm!("{}", inout(reg) y); + //~^ ERROR use of possibly-uninitialized variable: `y` + let _ = y; + + // Outputs require mutable places + + let v: Vec = vec![0, 1, 2]; + asm!("{}", in(reg) v[0]); + asm!("{}", out(reg) v[0]); + //~^ ERROR cannot borrow `v` as mutable, as it is not declared as mutable + asm!("{}", inout(reg) v[0]); + //~^ ERROR cannot borrow `v` as mutable, as it is not declared as mutable + + // Const operands must be integer or floats, and must be constants. + + let x = 0; + const C: i32 = 0; + const fn const_foo(x: i32) -> i32 { + x + } + const fn const_bar(x: T) -> T { + x + } + asm!("{}", const 0i32); + asm!("{}", const 0f32); + asm!("{}", const 0 as *mut u8); + //~^ ERROR asm `const` arguments must be integer or floating-point values + asm!("{}", const &0); + //~^ ERROR asm `const` arguments must be integer or floating-point values + asm!("{}", const x); + //~^ ERROR argument 1 is required to be a constant + asm!("{}", const const_foo(0)); + asm!("{}", const const_foo(x)); + //~^ ERROR argument 1 is required to be a constant + asm!("{}", const const_bar(0)); + asm!("{}", const const_bar(x)); + //~^ ERROR argument 1 is required to be a constant + + // Sym operands must point to a function or static + + static S: i32 = 0; + asm!("{}", sym S); + asm!("{}", sym main); + asm!("{}", sym C); + //~^ ERROR asm `sym` operand must point to a fn or static + asm!("{}", sym x); + //~^ ERROR asm `sym` operand must point to a fn or static + + // Register operands must be Copy + + asm!("{}", in(xmm_reg) SimdNonCopy(0.0, 0.0, 0.0, 0.0)); + //~^ ERROR arguments for inline assembly must be copyable + + // Register operands must be integers, floats, SIMD vectors, pointers or + // function pointers. + + asm!("{}", in(reg) 0i64); + asm!("{}", in(reg) 0f64); + asm!("{}", in(xmm_reg) std::arch::x86_64::_mm_setzero_ps()); + asm!("{}", in(reg) 0 as *const u8); + asm!("{}", in(reg) 0 as *mut u8); + asm!("{}", in(reg) main as fn()); + asm!("{}", in(reg) |x: i32| x); + //~^ ERROR cannot use value of type + asm!("{}", in(reg) vec![0]); + //~^ ERROR cannot use value of type `std::vec::Vec` for inline assembly + asm!("{}", in(reg) (1, 2, 3)); + //~^ ERROR cannot use value of type `(i32, i32, i32)` for inline assembly + asm!("{}", in(reg) [1, 2, 3]); + //~^ ERROR cannot use value of type `[i32; 3]` for inline assembly + + // Register inputs (but not outputs) allow references and function types + + let mut f = main; + let mut r = &mut 0; + asm!("{}", in(reg) f); + asm!("{}", inout(reg) f); + //~^ ERROR cannot use value of type `fn() {main}` for inline assembly + asm!("{}", in(reg) r); + asm!("{}", inout(reg) r); + //~^ ERROR cannot use value of type `&mut i32` for inline assembly + let _ = (f, r); + + // Type checks ignore never type + + let u: ! = unreachable!(); + asm!("{}", in(reg) u); + } +} diff --git a/src/test/ui/asm/type-check-2.stderr b/src/test/ui/asm/type-check-2.stderr new file mode 100644 index 0000000000000..dc7949534f1a9 --- /dev/null +++ b/src/test/ui/asm/type-check-2.stderr @@ -0,0 +1,133 @@ +error: asm `const` arguments must be integer or floating-point values + --> $DIR/type-check-2.rs:41:26 + | +LL | asm!("{}", const 0 as *mut u8); + | ^^^^^^^^^^^^ + +error: asm `const` arguments must be integer or floating-point values + --> $DIR/type-check-2.rs:43:26 + | +LL | asm!("{}", const &0); + | ^^ + +error: arguments for inline assembly must be copyable + --> $DIR/type-check-2.rs:66: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:78:28: 78:38]` for inline assembly + --> $DIR/type-check-2.rs:78:28 + | +LL | asm!("{}", in(reg) |x: i32| x); + | ^^^^^^^^^^ + | + = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly + +error: cannot use value of type `std::vec::Vec` for inline assembly + --> $DIR/type-check-2.rs:80:28 + | +LL | asm!("{}", in(reg) vec![0]); + | ^^^^^^^ + | + = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly + = note: this error originates in a macro (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:82:28 + | +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:84:28 + | +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:92:31 + | +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:95:31 + | +LL | asm!("{}", inout(reg) r); + | ^ + | + = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly + +error: asm `sym` operand must point to a fn or static + --> $DIR/type-check-2.rs:59:24 + | +LL | asm!("{}", sym C); + | ^ + +error: asm `sym` operand must point to a fn or static + --> $DIR/type-check-2.rs:61:24 + | +LL | asm!("{}", sym x); + | ^ + +error: argument 1 is required to be a constant + --> $DIR/type-check-2.rs:45:9 + | +LL | asm!("{}", const x); + | ^^^^^^^^^^^^^^^^^^^^ + +error: argument 1 is required to be a constant + --> $DIR/type-check-2.rs:48:9 + | +LL | asm!("{}", const const_foo(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: argument 1 is required to be a constant + --> $DIR/type-check-2.rs:51:9 + | +LL | asm!("{}", const const_bar(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/type-check-2.rs:13:28 + | +LL | asm!("{}", in(reg) x); + | ^ use of possibly-uninitialized `x` + +error[E0381]: use of possibly-uninitialized variable: `y` + --> $DIR/type-check-2.rs:16:9 + | +LL | asm!("{}", inout(reg) y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `y` + +error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable + --> $DIR/type-check-2.rs:24:29 + | +LL | let v: Vec = vec![0, 1, 2]; + | - help: consider changing this to be mutable: `mut v` +LL | asm!("{}", in(reg) v[0]); +LL | asm!("{}", out(reg) v[0]); + | ^ cannot borrow as mutable + +error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable + --> $DIR/type-check-2.rs:26:31 + | +LL | let v: Vec = vec![0, 1, 2]; + | - help: consider changing this to be mutable: `mut v` +... +LL | asm!("{}", inout(reg) v[0]); + | ^ cannot borrow as mutable + +error: aborting due to 18 previous errors + +Some errors have detailed explanations: E0381, E0596. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/asm/type-check-3.rs b/src/test/ui/asm/type-check-3.rs new file mode 100644 index 0000000000000..5de15fe49067a --- /dev/null +++ b/src/test/ui/asm/type-check-3.rs @@ -0,0 +1,71 @@ +// only-x86_64 +// compile-flags: -C target-feature=+avx512f + +#![feature(asm)] + +use std::arch::x86_64::{_mm256_setzero_ps, _mm_setzero_ps}; + +fn main() { + unsafe { + // Types must be in the whitelist for the register class + + asm!("{}", in(reg) 0i128); + //~^ ERROR type `i128` cannot be used with this register class + asm!("{}", in(reg) _mm_setzero_ps()); + //~^ ERROR type `std::arch::x86_64::__m128` cannot be used with this register class + asm!("{}", in(reg) _mm256_setzero_ps()); + //~^ ERROR type `std::arch::x86_64::__m256` cannot be used with this register class + asm!("{}", in(xmm_reg) 0u8); + //~^ ERROR type `u8` cannot be used with this register class + asm!("{:e}", in(reg) 0i32); + asm!("{}", in(xmm_reg) 0i32); + asm!("{:e}", in(reg) 0f32); + asm!("{}", in(xmm_reg) 0f32); + asm!("{}", in(xmm_reg) _mm_setzero_ps()); + asm!("{:x}", in(ymm_reg) _mm_setzero_ps()); + asm!("{}", in(kreg) 0u16); + asm!("{}", in(kreg) 0u64); + //~^ ERROR `avx512bw` target feature is not enabled + + // Template modifier suggestions for sub-registers + + asm!("{0} {0}", in(reg) 0i16); + //~^ WARN formatting may not be suitable for sub-register argument + asm!("{0} {0:x}", in(reg) 0i16); + //~^ WARN formatting may not be suitable for sub-register argument + asm!("{}", in(reg) 0i32); + //~^ WARN formatting may not be suitable for sub-register argument + asm!("{}", in(reg) 0i64); + asm!("{}", in(ymm_reg) 0i64); + //~^ WARN formatting may not be suitable for sub-register argument + asm!("{}", in(ymm_reg) _mm256_setzero_ps()); + asm!("{:l}", in(reg) 0i16); + asm!("{:l}", in(reg) 0i32); + asm!("{:l}", in(reg) 0i64); + asm!("{:x}", in(ymm_reg) 0i64); + asm!("{:x}", in(ymm_reg) _mm256_setzero_ps()); + + // Suggest different register class for type + + asm!("{}", in(reg) 0i8); + //~^ ERROR type `i8` cannot be used with this register class + asm!("{}", in(reg_byte) 0i8); + + // Split inout operands must have compatible types + + let mut val_i16: i16; + let mut val_f32: f32; + let mut val_u32: u32; + let mut val_u64: u64; + let mut val_ptr: *mut u8; + asm!("{:r}", inout(reg) 0u16 => val_i16); + asm!("{:r}", inout(reg) 0u32 => val_f32); + //~^ ERROR incompatible types for asm inout argument + asm!("{:r}", inout(reg) 0u32 => val_ptr); + //~^ ERROR incompatible types for asm inout argument + asm!("{:r}", inout(reg) main => val_u32); + //~^ ERROR incompatible types for asm inout argument + asm!("{:r}", inout(reg) 0u64 => val_ptr); + asm!("{:r}", inout(reg) main => val_u64); + } +} diff --git a/src/test/ui/asm/type-check-3.stderr b/src/test/ui/asm/type-check-3.stderr new file mode 100644 index 0000000000000..01dbe78db887a --- /dev/null +++ b/src/test/ui/asm/type-check-3.stderr @@ -0,0 +1,118 @@ +error: type `i128` cannot be used with this register class + --> $DIR/type-check-3.rs:12:28 + | +LL | asm!("{}", in(reg) 0i128); + | ^^^^^ + | + = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + +error: type `std::arch::x86_64::__m128` cannot be used with this register class + --> $DIR/type-check-3.rs:14:28 + | +LL | asm!("{}", in(reg) _mm_setzero_ps()); + | ^^^^^^^^^^^^^^^^ + | + = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + +error: type `std::arch::x86_64::__m256` cannot be used with this register class + --> $DIR/type-check-3.rs:16:28 + | +LL | asm!("{}", in(reg) _mm256_setzero_ps()); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + +error: type `u8` cannot be used with this register class + --> $DIR/type-check-3.rs:18:32 + | +LL | asm!("{}", in(xmm_reg) 0u8); + | ^^^ + | + = note: register class `xmm_reg` supports these types: i32, i64, f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 + +error: `avx512bw` target feature is not enabled + --> $DIR/type-check-3.rs:27:29 + | +LL | asm!("{}", in(kreg) 0u64); + | ^^^^ + | + = note: this is required to use type `u64` with register class `kreg` + +warning: formatting may not be suitable for sub-register argument + --> $DIR/type-check-3.rs:32:15 + | +LL | asm!("{0} {0}", in(reg) 0i16); + | ^^^ ^^^ ---- for this argument + | + = note: `#[warn(asm_sub_register)]` on by default + = help: use the `x` modifier to have the register formatted as `ax` + = help: or use the `r` modifier to keep the default formatting of `rax` + +warning: formatting may not be suitable for sub-register argument + --> $DIR/type-check-3.rs:34:15 + | +LL | asm!("{0} {0:x}", in(reg) 0i16); + | ^^^ ---- for this argument + | + = help: use the `x` modifier to have the register formatted as `ax` + = help: or use the `r` modifier to keep the default formatting of `rax` + +warning: formatting may not be suitable for sub-register argument + --> $DIR/type-check-3.rs:36:15 + | +LL | asm!("{}", in(reg) 0i32); + | ^^ ---- for this argument + | + = help: use the `e` modifier to have the register formatted as `eax` + = help: or use the `r` modifier to keep the default formatting of `rax` + +warning: formatting may not be suitable for sub-register argument + --> $DIR/type-check-3.rs:39:15 + | +LL | asm!("{}", in(ymm_reg) 0i64); + | ^^ ---- for this argument + | + = help: use the `x` modifier to have the register formatted as `xmm0` + = help: or use the `y` modifier to keep the default formatting of `ymm0` + +error: type `i8` cannot be used with this register class + --> $DIR/type-check-3.rs:50:28 + | +LL | asm!("{}", in(reg) 0i8); + | ^^^ + | + = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + = help: consider using the `reg_byte` register class instead + +error: incompatible types for asm inout argument + --> $DIR/type-check-3.rs:62:33 + | +LL | asm!("{:r}", inout(reg) 0u32 => val_f32); + | ^^^^ ^^^^^^^ type `f32` + | | + | type `u32` + | + = note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size + +error: incompatible types for asm inout argument + --> $DIR/type-check-3.rs:64:33 + | +LL | asm!("{:r}", inout(reg) 0u32 => val_ptr); + | ^^^^ ^^^^^^^ type `*mut u8` + | | + | type `u32` + | + = note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size + +error: incompatible types for asm inout argument + --> $DIR/type-check-3.rs:66:33 + | +LL | asm!("{:r}", inout(reg) main => val_u32); + | ^^^^ ^^^^^^^ type `u32` + | | + | type `fn()` + | + = note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size + +error: aborting due to 9 previous errors; 4 warnings emitted + diff --git a/src/test/ui/asm/type-check-4.rs b/src/test/ui/asm/type-check-4.rs new file mode 100644 index 0000000000000..2be627c11657b --- /dev/null +++ b/src/test/ui/asm/type-check-4.rs @@ -0,0 +1,23 @@ +// only-x86_64 + +#![feature(asm)] + +fn main() { + unsafe { + // Can't output to borrowed values. + + let mut a = 0isize; + let p = &a; + asm!("{}", out(reg) a); + //~^ cannot assign to `a` because it is borrowed + println!("{}", p); + + // Can't read from mutable borrowed values. + + let mut a = 0isize; + let p = &mut a; + asm!("{}", in(reg) a); + //~^ cannot use `a` because it was mutably borrowed + println!("{}", p); + } +} diff --git a/src/test/ui/asm/type-check-4.stderr b/src/test/ui/asm/type-check-4.stderr new file mode 100644 index 0000000000000..8035bbefc1aa1 --- /dev/null +++ b/src/test/ui/asm/type-check-4.stderr @@ -0,0 +1,26 @@ +error[E0506]: cannot assign to `a` because it is borrowed + --> $DIR/type-check-4.rs:11:9 + | +LL | let p = &a; + | -- borrow of `a` occurs here +LL | asm!("{}", out(reg) a); + | ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `a` occurs here +LL | +LL | println!("{}", p); + | - borrow later used here + +error[E0503]: cannot use `a` because it was mutably borrowed + --> $DIR/type-check-4.rs:19:28 + | +LL | let p = &mut a; + | ------ borrow of `a` occurs here +LL | asm!("{}", in(reg) a); + | ^ use of borrowed `a` +LL | +LL | println!("{}", p); + | - borrow later used here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0503, E0506. +For more information about an error, try `rustc --explain E0503`. diff --git a/src/test/ui/associated-const/associated-const-dead-code.rs b/src/test/ui/associated-const/associated-const-dead-code.rs index c47e474d2dd74..e659bdb83f9c1 100644 --- a/src/test/ui/associated-const/associated-const-dead-code.rs +++ b/src/test/ui/associated-const/associated-const-dead-code.rs @@ -4,7 +4,7 @@ struct MyFoo; impl MyFoo { const BAR: u32 = 1; - //~^ ERROR associated const is never used: `BAR` + //~^ ERROR associated constant is never used: `BAR` } fn main() { diff --git a/src/test/ui/associated-const/associated-const-dead-code.stderr b/src/test/ui/associated-const/associated-const-dead-code.stderr index 172aed733fca9..9b6bbb68a71f7 100644 --- a/src/test/ui/associated-const/associated-const-dead-code.stderr +++ b/src/test/ui/associated-const/associated-const-dead-code.stderr @@ -1,4 +1,4 @@ -error: associated const is never used: `BAR` +error: associated constant is never used: `BAR` --> $DIR/associated-const-dead-code.rs:6:5 | LL | const BAR: u32 = 1; diff --git a/src/test/ui/associated-const/associated-const-generic-obligations.stderr b/src/test/ui/associated-const/associated-const-generic-obligations.stderr index d6cdcd4747ff2..d8bac07e058da 100644 --- a/src/test/ui/associated-const/associated-const-generic-obligations.stderr +++ b/src/test/ui/associated-const/associated-const-generic-obligations.stderr @@ -9,8 +9,6 @@ LL | const FROM: &'static str = "foo"; | = note: expected associated type `::Out` found reference `&'static str` - = note: consider constraining the associated type `::Out` to `&'static str` or calling a method that returns `::Out` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to previous error diff --git a/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.rs b/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.rs index f1f82caf7d40b..8fe79b97d9ba2 100644 --- a/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.rs +++ b/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.rs @@ -14,7 +14,7 @@ impl Foo for Def { pub fn test() { let _array = [4; ::Y]; - //~^ ERROR the trait bound `A: Foo` is not satisfied [E0277] + //~^ ERROR constant expression depends on a generic parameter } fn main() { diff --git a/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.stderr b/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.stderr index ec60db47f4429..0bc019b2dc875 100644 --- a/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.stderr +++ b/src/test/ui/associated-const/associated-const-type-parameter-arrays-2.stderr @@ -1,18 +1,10 @@ -error[E0277]: the trait bound `A: Foo` is not satisfied +error: constant expression depends on a generic parameter --> $DIR/associated-const-type-parameter-arrays-2.rs:16:22 | -LL | const Y: usize; - | --------------- required by `Foo::Y` -... LL | let _array = [4; ::Y]; - | ^^^^^^^^^^^^^ the trait `Foo` is not implemented for `A` + | ^^^^^^^^^^^^^ | -help: consider further restricting this bound with `+ Foo` - --> $DIR/associated-const-type-parameter-arrays-2.rs:15:16 - | -LL | pub fn test() { - | ^^^ + = note: this may fail depending on what value the parameter takes error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-const/associated-const-type-parameter-arrays.stderr b/src/test/ui/associated-const/associated-const-type-parameter-arrays.stderr index 3d38deb5a8763..ac40e390cfbbd 100644 --- a/src/test/ui/associated-const/associated-const-type-parameter-arrays.stderr +++ b/src/test/ui/associated-const/associated-const-type-parameter-arrays.stderr @@ -7,11 +7,10 @@ LL | const Y: usize; LL | let _array: [u32; ::Y]; | ^^^^^^^^^^^^^ the trait `Foo` is not implemented for `A` | -help: consider further restricting this bound with `+ Foo` - --> $DIR/associated-const-type-parameter-arrays.rs:15:16 +help: consider further restricting this bound | -LL | pub fn test() { - | ^^^ +LL | pub fn test() { + | ^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-const/defaults-cyclic-fail.rs b/src/test/ui/associated-const/defaults-cyclic-fail.rs index 9b899ee316a0e..9fb1bbebc9610 100644 --- a/src/test/ui/associated-const/defaults-cyclic-fail.rs +++ b/src/test/ui/associated-const/defaults-cyclic-fail.rs @@ -1,9 +1,9 @@ // build-fail +//~^ ERROR cycle detected when normalizing `<() as Tr>::A` // Cyclic assoc. const defaults don't error unless *used* trait Tr { const A: u8 = Self::B; - //~^ ERROR cycle detected when const-evaluating + checking `Tr::A` const B: u8 = Self::A; } diff --git a/src/test/ui/associated-const/defaults-cyclic-fail.stderr b/src/test/ui/associated-const/defaults-cyclic-fail.stderr index 940182d4aa676..6b2fbe5be4e30 100644 --- a/src/test/ui/associated-const/defaults-cyclic-fail.stderr +++ b/src/test/ui/associated-const/defaults-cyclic-fail.stderr @@ -1,30 +1,42 @@ -error[E0391]: cycle detected when const-evaluating + checking `Tr::A` - --> $DIR/defaults-cyclic-fail.rs:5:5 +error[E0391]: cycle detected when normalizing `<() as Tr>::A` + | +note: ...which requires const-evaluating + checking `Tr::A`... + --> $DIR/defaults-cyclic-fail.rs:6:5 | LL | const A: u8 = Self::B; | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `Tr::A`... + --> $DIR/defaults-cyclic-fail.rs:6:5 | +LL | const A: u8 = Self::B; + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating `Tr::A`... - --> $DIR/defaults-cyclic-fail.rs:5:19 + --> $DIR/defaults-cyclic-fail.rs:6:5 | LL | const A: u8 = Self::B; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `<() as Tr>::B`... +note: ...which requires const-evaluating + checking `Tr::B`... + --> $DIR/defaults-cyclic-fail.rs:8:5 + | +LL | const B: u8 = Self::A; + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Tr::B`... --> $DIR/defaults-cyclic-fail.rs:8:5 | LL | const B: u8 = Self::A; | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating `Tr::B`... - --> $DIR/defaults-cyclic-fail.rs:8:19 + --> $DIR/defaults-cyclic-fail.rs:8:5 | LL | const B: u8 = Self::A; - | ^^^^^^^ - = note: ...which again requires const-evaluating + checking `Tr::A`, completing the cycle + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires normalizing `<() as Tr>::A`, completing the cycle note: cycle used when const-evaluating `main` - --> $DIR/defaults-cyclic-fail.rs:16:16 + --> $DIR/defaults-cyclic-fail.rs:14:1 | -LL | assert_eq!(<() as Tr>::A, 0); - | ^^^^^^^^^^^^^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-const/issue-63496.stderr b/src/test/ui/associated-const/issue-63496.stderr index 3a70e7d43c25e..34e947030a072 100644 --- a/src/test/ui/associated-const/issue-63496.stderr +++ b/src/test/ui/associated-const/issue-63496.stderr @@ -10,7 +10,7 @@ LL | fn f() -> ([u8; A::C], [u8; A::C]); | cannot infer type | help: use the fully qualified path to an implementation: `::C` | - = note: cannot resolve `_: A` + = note: cannot satisfy `_: A` = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl` error[E0283]: type annotations needed @@ -25,7 +25,7 @@ LL | fn f() -> ([u8; A::C], [u8; A::C]); | cannot infer type | help: use the fully qualified path to an implementation: `::C` | - = note: cannot resolve `_: A` + = note: cannot satisfy `_: A` = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl` error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.noopt.stderr b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.noopt.stderr new file mode 100644 index 0000000000000..510a13ea5b1be --- /dev/null +++ b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.noopt.stderr @@ -0,0 +1,54 @@ +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:29:22 + | +LL | const NEG: i32 = -i32::MIN + T::NEG; + | ^^^^^^^^^ attempt to negate with overflow + | + = note: `#[deny(arithmetic_overflow)]` on by default + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:31:35 + | +LL | const NEG_REV: i32 = T::NEG + (-i32::MIN); + | ^^^^^^^^^^^ attempt to negate with overflow + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:34:22 + | +LL | const ADD: i32 = (i32::MAX+1) + T::ADD; + | ^^^^^^^^^^^^ attempt to add with overflow + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:36:36 + | +LL | const ADD_REV: i32 = T::ADD + (i32::MAX+1); + | ^^^^^^^^^^^^ attempt to add with overflow + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:39:22 + | +LL | const DIV: i32 = (1/0) + T::DIV; + | ^^^^^ attempt to divide by zero + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:41:35 + | +LL | const DIV_REV: i32 = T::DIV + (1/0); + | ^^^^^ attempt to divide by zero + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:44:22 + | +LL | const OOB: i32 = [1][1] + T::OOB; + | ^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:46:35 + | +LL | const OOB_REV: i32 = T::OOB + [1][1]; + | ^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.opt.stderr b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.opt.stderr new file mode 100644 index 0000000000000..510a13ea5b1be --- /dev/null +++ b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.opt.stderr @@ -0,0 +1,54 @@ +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:29:22 + | +LL | const NEG: i32 = -i32::MIN + T::NEG; + | ^^^^^^^^^ attempt to negate with overflow + | + = note: `#[deny(arithmetic_overflow)]` on by default + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:31:35 + | +LL | const NEG_REV: i32 = T::NEG + (-i32::MIN); + | ^^^^^^^^^^^ attempt to negate with overflow + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:34:22 + | +LL | const ADD: i32 = (i32::MAX+1) + T::ADD; + | ^^^^^^^^^^^^ attempt to add with overflow + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:36:36 + | +LL | const ADD_REV: i32 = T::ADD + (i32::MAX+1); + | ^^^^^^^^^^^^ attempt to add with overflow + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:39:22 + | +LL | const DIV: i32 = (1/0) + T::DIV; + | ^^^^^ attempt to divide by zero + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:41:35 + | +LL | const DIV_REV: i32 = T::DIV + (1/0); + | ^^^^^ attempt to divide by zero + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:44:22 + | +LL | const OOB: i32 = [1][1] + T::OOB; + | ^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:46:35 + | +LL | const OOB_REV: i32 = T::OOB + [1][1]; + | ^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.opt_with_overflow_checks.stderr b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.opt_with_overflow_checks.stderr new file mode 100644 index 0000000000000..510a13ea5b1be --- /dev/null +++ b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.opt_with_overflow_checks.stderr @@ -0,0 +1,54 @@ +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:29:22 + | +LL | const NEG: i32 = -i32::MIN + T::NEG; + | ^^^^^^^^^ attempt to negate with overflow + | + = note: `#[deny(arithmetic_overflow)]` on by default + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:31:35 + | +LL | const NEG_REV: i32 = T::NEG + (-i32::MIN); + | ^^^^^^^^^^^ attempt to negate with overflow + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:34:22 + | +LL | const ADD: i32 = (i32::MAX+1) + T::ADD; + | ^^^^^^^^^^^^ attempt to add with overflow + +error: this arithmetic operation will overflow + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:36:36 + | +LL | const ADD_REV: i32 = T::ADD + (i32::MAX+1); + | ^^^^^^^^^^^^ attempt to add with overflow + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:39:22 + | +LL | const DIV: i32 = (1/0) + T::DIV; + | ^^^^^ attempt to divide by zero + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:41:35 + | +LL | const DIV_REV: i32 = T::DIV + (1/0); + | ^^^^^ attempt to divide by zero + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:44:22 + | +LL | const OOB: i32 = [1][1] + T::OOB; + | ^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: this operation will panic at runtime + --> $DIR/issue-69020-assoc-const-arith-overflow.rs:46:35 + | +LL | const OOB_REV: i32 = T::OOB + [1][1]; + | ^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.rs b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.rs new file mode 100644 index 0000000000000..850f65ae9d183 --- /dev/null +++ b/src/test/ui/associated-const/issue-69020-assoc-const-arith-overflow.rs @@ -0,0 +1,48 @@ +// revisions: noopt opt opt_with_overflow_checks +//[noopt]compile-flags: -C opt-level=0 +//[opt]compile-flags: -O +//[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O + +#![crate_type="lib"] + +use std::i32; + +pub trait Foo { + const NEG: i32; + const NEG_REV: i32; + + const ADD: i32; + const ADD_REV: i32; + + const DIV: i32; + const DIV_REV: i32; + + const OOB: i32; + const OOB_REV: i32; +} + +// These constants cannot be evaluated already (they depend on `T::N`), so they can just be linted +// like normal run-time code. But codegen works a bit different in const context, so this test +// makes sure that we still catch overflow. Also make sure we emit the same lints if we reverse the +// operands (so that the generic operand comes first). +impl Foo for Vec { + const NEG: i32 = -i32::MIN + T::NEG; + //~^ ERROR arithmetic operation will overflow + const NEG_REV: i32 = T::NEG + (-i32::MIN); + //~^ ERROR arithmetic operation will overflow + + const ADD: i32 = (i32::MAX+1) + T::ADD; + //~^ ERROR arithmetic operation will overflow + const ADD_REV: i32 = T::ADD + (i32::MAX+1); + //~^ ERROR arithmetic operation will overflow + + const DIV: i32 = (1/0) + T::DIV; + //~^ ERROR operation will panic + const DIV_REV: i32 = T::DIV + (1/0); + //~^ ERROR operation will panic + + const OOB: i32 = [1][1] + T::OOB; + //~^ ERROR operation will panic + const OOB_REV: i32 = T::OOB + [1][1]; + //~^ ERROR operation will panic +} diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs new file mode 100644 index 0000000000000..be8162c86b947 --- /dev/null +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs @@ -0,0 +1,15 @@ +// Check for recursion involving references to impl-associated const. + +trait Foo { + const BAR: u32; +} + +const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; //~ ERROR E0391 + +struct GlobalImplRef; + +impl GlobalImplRef { + const BAR: u32 = IMPL_REF_BAR; +} + +fn main() {} diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr new file mode 100644 index 0000000000000..01a34d310064c --- /dev/null +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr @@ -0,0 +1,44 @@ +error[E0391]: cycle detected when const-evaluating + checking `IMPL_REF_BAR` + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 + | +LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `IMPL_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 + | +LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `IMPL_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 + | +LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `::BAR`... +note: ...which requires const-evaluating + checking `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 + | +LL | const BAR: u32 = IMPL_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 + | +LL | const BAR: u32 = IMPL_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 + | +LL | const BAR: u32 = IMPL_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires processing `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 + | +LL | const BAR: u32 = IMPL_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `IMPL_REF_BAR`... + = note: ...which again requires const-evaluating + checking `IMPL_REF_BAR`, completing the cycle + = note: cycle used when running analysis passes on this crate + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs new file mode 100644 index 0000000000000..cec75ae19f457 --- /dev/null +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs @@ -0,0 +1,17 @@ +// Check for recursion involving references to trait-associated const default. + +trait Foo { + const BAR: u32; +} + +trait FooDefault { + const BAR: u32 = DEFAULT_REF_BAR; +} + +const DEFAULT_REF_BAR: u32 = ::BAR; //~ ERROR E0391 + +struct GlobalDefaultRef; + +impl FooDefault for GlobalDefaultRef {} + +fn main() {} diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr new file mode 100644 index 0000000000000..cad60cba1df62 --- /dev/null +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr @@ -0,0 +1,44 @@ +error[E0391]: cycle detected when const-evaluating + checking `DEFAULT_REF_BAR` + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 + | +LL | const DEFAULT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `DEFAULT_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 + | +LL | const DEFAULT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `DEFAULT_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 + | +LL | const DEFAULT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `::BAR`... +note: ...which requires const-evaluating + checking `FooDefault::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 + | +LL | const BAR: u32 = DEFAULT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `FooDefault::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 + | +LL | const BAR: u32 = DEFAULT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `FooDefault::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 + | +LL | const BAR: u32 = DEFAULT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires processing `FooDefault::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 + | +LL | const BAR: u32 = DEFAULT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `DEFAULT_REF_BAR`... + = note: ...which again requires const-evaluating + checking `DEFAULT_REF_BAR`, completing the cycle + = note: cycle used when running analysis passes on this crate + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs new file mode 100644 index 0000000000000..62af85343406f --- /dev/null +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs @@ -0,0 +1,15 @@ +// Check for recursion involving references to trait-associated const. + +trait Foo { + const BAR: u32; +} + +const TRAIT_REF_BAR: u32 = ::BAR; //~ ERROR E0391 + +struct GlobalTraitRef; + +impl Foo for GlobalTraitRef { + const BAR: u32 = TRAIT_REF_BAR; +} + +fn main() {} diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr new file mode 100644 index 0000000000000..f415980dc1ea8 --- /dev/null +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr @@ -0,0 +1,44 @@ +error[E0391]: cycle detected when const-evaluating + checking `TRAIT_REF_BAR` + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 + | +LL | const TRAIT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `TRAIT_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 + | +LL | const TRAIT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `TRAIT_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 + | +LL | const TRAIT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `::BAR`... +note: ...which requires const-evaluating + checking `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 + | +LL | const BAR: u32 = TRAIT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 + | +LL | const BAR: u32 = TRAIT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 + | +LL | const BAR: u32 = TRAIT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires processing `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 + | +LL | const BAR: u32 = TRAIT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `TRAIT_REF_BAR`... + = note: ...which again requires const-evaluating + checking `TRAIT_REF_BAR`, completing the cycle + = note: cycle used when running analysis passes on this crate + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/associated-item/issue-48027.stderr b/src/test/ui/associated-item/issue-48027.stderr index 62a380732a8bb..98b545c6e0e78 100644 --- a/src/test/ui/associated-item/issue-48027.stderr +++ b/src/test/ui/associated-item/issue-48027.stderr @@ -22,7 +22,7 @@ LL | fn return_n(&self) -> [u8; Bar::X]; | cannot infer type | help: use the fully qualified path to an implementation: `::X` | - = note: cannot resolve `_: Bar` + = note: cannot satisfy `_: Bar` = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl` error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr index efd5a92a4fced..cbacc3610dcf7 100644 --- a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr +++ b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr @@ -1,6 +1,12 @@ error[E0277]: `>::App` doesn't implement `std::fmt::Debug` --> $DIR/bad-bounds-on-assoc-in-trait.rs:31:6 | +LL | trait Case1 { + | ----- required by a bound in this +... +LL | Debug + | ----- required by this bound in `Case1` +... LL | impl Case1 for S1 { | ^^^^^ `>::App` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | @@ -10,43 +16,58 @@ error[E0277]: `<::C as std::iter::Iterator>::Item` is not an iterato --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 | LL | fn assume_case1() { - | ^^^^^ - help: consider further restricting the associated type: `where <::C as std::iter::Iterator>::Item: std::iter::Iterator` - | | - | `<::C as std::iter::Iterator>::Item` is not an iterator + | ^^^^^ `<::C as std::iter::Iterator>::Item` is not an iterator | = help: the trait `std::iter::Iterator` is not implemented for `<::C as std::iter::Iterator>::Item` +help: consider further restricting the associated type + | +LL | fn assume_case1() where <::C as std::iter::Iterator>::Item: std::iter::Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: `<::C as std::iter::Iterator>::Item` cannot be sent between threads safely --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 | LL | trait Case1 { - | ----------- required by `Case1` + | ----- required by a bound in this +LL | type C: Clone + Iterator() { - | ^^^^^ - help: consider further restricting the associated type: `where <::C as std::iter::Iterator>::Item: std::marker::Send` - | | - | `<::C as std::iter::Iterator>::Item` cannot be sent between threads safely + | ^^^^^ `<::C as std::iter::Iterator>::Item` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `<::C as std::iter::Iterator>::Item` +help: consider further restricting the associated type + | +LL | fn assume_case1() where <::C as std::iter::Iterator>::Item: std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: `<::C as std::iter::Iterator>::Item` cannot be shared between threads safely --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 | LL | trait Case1 { - | ----------- required by `Case1` + | ----- required by a bound in this +... +LL | > + Sync>; + | ---- required by this bound in `Case1` ... LL | fn assume_case1() { - | ^^^^^ - help: consider further restricting the associated type: `where <::C as std::iter::Iterator>::Item: std::marker::Sync` - | | - | `<::C as std::iter::Iterator>::Item` cannot be shared between threads safely + | ^^^^^ `<::C as std::iter::Iterator>::Item` cannot be shared between threads safely | = help: the trait `std::marker::Sync` is not implemented for `<::C as std::iter::Iterator>::Item` +help: consider further restricting the associated type + | +LL | fn assume_case1() where <::C as std::iter::Iterator>::Item: std::marker::Sync { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: `<_ as Lam<&'a u8>>::App` doesn't implement `std::fmt::Debug` --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 | LL | trait Case1 { - | ----------- required by `Case1` + | ----- required by a bound in this +... +LL | Debug + | ----- required by this bound in `Case1` ... LL | fn assume_case1() { | ^^^^^ `<_ as Lam<&'a u8>>::App` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` diff --git a/src/test/ui/associated-type-bounds/duplicate.rs b/src/test/ui/associated-type-bounds/duplicate.rs index f8d230da36523..8b396f23efd54 100644 --- a/src/test/ui/associated-type-bounds/duplicate.rs +++ b/src/test/ui/associated-type-bounds/duplicate.rs @@ -2,7 +2,7 @@ #![feature(associated_type_bounds)] #![feature(type_alias_impl_trait)] -#![feature(impl_trait_in_bindings)] //~ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash [incomplete_features] +#![feature(impl_trait_in_bindings)] //~ WARN the feature `impl_trait_in_bindings` is incomplete #![feature(untagged_unions)] use std::iter; diff --git a/src/test/ui/associated-type-bounds/duplicate.stderr b/src/test/ui/associated-type-bounds/duplicate.stderr index 82b2d32d09d57..71f6e4ff8b62d 100644 --- a/src/test/ui/associated-type-bounds/duplicate.stderr +++ b/src/test/ui/associated-type-bounds/duplicate.stderr @@ -1,10 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/duplicate.rs:5:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified --> $DIR/duplicate.rs:10:36 @@ -726,6 +727,6 @@ error: could not find defining uses LL | type TADyn3 = dyn Iterator; | ^^^^^^^^^^^^^ -error: aborting due to 96 previous errors +error: aborting due to 96 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0719`. diff --git a/src/test/ui/associated-type-bounds/dyn-lcsit.stderr b/src/test/ui/associated-type-bounds/dyn-lcsit.stderr index 1b3975f0999b6..3637f9558be7b 100644 --- a/src/test/ui/associated-type-bounds/dyn-lcsit.stderr +++ b/src/test/ui/associated-type-bounds/dyn-lcsit.stderr @@ -1,8 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/dyn-lcsit.rs:4:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/associated-type-bounds/lcsit.stderr b/src/test/ui/associated-type-bounds/lcsit.stderr index 7c4349541e000..11ff03db36147 100644 --- a/src/test/ui/associated-type-bounds/lcsit.stderr +++ b/src/test/ui/associated-type-bounds/lcsit.stderr @@ -1,8 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/lcsit.rs:4:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/associated-type-bounds/type-alias.stderr b/src/test/ui/associated-type-bounds/type-alias.stderr index 7f58f7f73e38c..42be09de03f22 100644 --- a/src/test/ui/associated-type-bounds/type-alias.stderr +++ b/src/test/ui/associated-type-bounds/type-alias.stderr @@ -131,3 +131,5 @@ help: the bound will not be checked when the type alias is used, and should be r LL | type _TaInline6 = T; | -- +warning: 12 warnings emitted + diff --git a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr index 8d0cd57fad442..b6a88179c1f63 100644 --- a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr +++ b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr @@ -28,7 +28,7 @@ LL | fn dent(c: C, color: ::Color) { | ^^^^^^^^^^^^^^^^^^^^^ error[E0222]: ambiguous associated type `Color` in bounds of `BoxCar` - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30 + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:37 | LL | type Color; | ----------- ambiguous `Color` from `Vehicle` @@ -37,7 +37,7 @@ LL | type Color; | ----------- ambiguous `Color` from `Box` ... LL | fn dent_object(c: dyn BoxCar) { - | ^^^^^^^^^^^^^^^^^^^ ambiguous associated type `Color` + | ^^^^^^^^^^^ ambiguous associated type `Color` | = help: consider introducing a new type parameter `T` and adding `where` constraints: where diff --git a/src/test/ui/associated-types/associated-types-binding-to-type-defined-in-supertrait.stderr b/src/test/ui/associated-types/associated-types-binding-to-type-defined-in-supertrait.stderr index 86e651b53f0b9..029c923aa7d4a 100644 --- a/src/test/ui/associated-types/associated-types-binding-to-type-defined-in-supertrait.stderr +++ b/src/test/ui/associated-types/associated-types-binding-to-type-defined-in-supertrait.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `::Color == Blue` --> $DIR/associated-types-binding-to-type-defined-in-supertrait.rs:31:10 | LL | fn blue_car>(c: C) { - | -------- ---------- required by this bound in `blue_car` + | ---------- required by this bound in `blue_car` ... LL | fn b() { blue_car(ModelT); } | ^^^^^^^^ expected struct `Blue`, found struct `Black` @@ -11,7 +11,7 @@ error[E0271]: type mismatch resolving `::Color == Black` --> $DIR/associated-types-binding-to-type-defined-in-supertrait.rs:32:10 | LL | fn black_car>(c: C) { - | --------- ----------- required by this bound in `black_car` + | ----------- required by this bound in `black_car` ... LL | fn c() { black_car(ModelU); } | ^^^^^^^^^ expected struct `Black`, found struct `Blue` diff --git a/src/test/ui/associated-types/associated-types-bound-failure.fixed b/src/test/ui/associated-types/associated-types-bound-failure.fixed index cc47f31d00456..68ee38d16b3f3 100644 --- a/src/test/ui/associated-types/associated-types-bound-failure.fixed +++ b/src/test/ui/associated-types/associated-types-bound-failure.fixed @@ -14,7 +14,7 @@ pub trait GetToInt } fn foo(g: G) -> isize - where G : GetToInt, ::R: ToInt + where G : GetToInt, ::R: ToInt { ToInt::to_int(&g.get()) //~ ERROR E0277 } diff --git a/src/test/ui/associated-types/associated-types-bound-failure.stderr b/src/test/ui/associated-types/associated-types-bound-failure.stderr index c420c86a2758f..ab8909d1092ba 100644 --- a/src/test/ui/associated-types/associated-types-bound-failure.stderr +++ b/src/test/ui/associated-types/associated-types-bound-failure.stderr @@ -4,11 +4,13 @@ error[E0277]: the trait bound `::R: ToInt` is not satisfied LL | fn to_int(&self) -> isize; | -------------------------- required by `ToInt::to_int` ... -LL | where G : GetToInt - | - help: consider further restricting the associated type: `, ::R: ToInt` -LL | { LL | ToInt::to_int(&g.get()) | ^^^^^^^^ the trait `ToInt` is not implemented for `::R` + | +help: consider further restricting the associated type + | +LL | where G : GetToInt, ::R: ToInt + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-eq-3.stderr b/src/test/ui/associated-types/associated-types-eq-3.stderr index d4e6bed82321b..dffa4780a09ff 100644 --- a/src/test/ui/associated-types/associated-types-eq-3.stderr +++ b/src/test/ui/associated-types/associated-types-eq-3.stderr @@ -8,14 +8,16 @@ LL | let _: Bar = x.boo(); | = note: expected struct `Bar` found associated type `::A` - = note: consider constraining the associated type `::A` to `Bar` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `::A` to `Bar` + | +LL | fn foo2>(x: I) { + | ^^^^^^^^^ error[E0271]: type mismatch resolving `::A == Bar` --> $DIR/associated-types-eq-3.rs:38:5 | LL | fn foo1>(x: I) { - | ---- ----- required by this bound in `foo1` + | ----- required by this bound in `foo1` ... LL | foo1(a); | ^^^^ expected struct `Bar`, found `usize` diff --git a/src/test/ui/associated-types/associated-types-eq-hr.stderr b/src/test/ui/associated-types/associated-types-eq-hr.stderr index fd7d89d193381..58d72746e76aa 100644 --- a/src/test/ui/associated-types/associated-types-eq-hr.stderr +++ b/src/test/ui/associated-types/associated-types-eq-hr.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `for<'x> $DIR/associated-types-eq-hr.rs:82:5 | LL | fn foo() - | --- + | --- required by a bound in this LL | where T : for<'x> TheTrait<&'x isize, A = &'x isize> | ------------- required by this bound in `foo` ... @@ -16,7 +16,7 @@ error[E0271]: type mismatch resolving `for<'x> --> $DIR/associated-types-eq-hr.rs:86:5 | LL | fn bar() - | --- + | --- required by a bound in this LL | where T : for<'x> TheTrait<&'x isize, A = &'x usize> | ------------- required by this bound in `bar` ... @@ -30,7 +30,7 @@ error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize --> $DIR/associated-types-eq-hr.rs:91:17 | LL | fn tuple_one() - | --------- + | --------- required by a bound in this LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize> | ---------------------------------------------------------- required by this bound in `tuple_one` ... @@ -44,7 +44,7 @@ error[E0271]: type mismatch resolving `for<'x, 'y> $DIR/associated-types-eq-hr.rs:91:5 | LL | fn tuple_one() - | --------- + | --------- required by a bound in this LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize> | ------------- required by this bound in `tuple_one` ... @@ -55,7 +55,7 @@ error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize --> $DIR/associated-types-eq-hr.rs:97:17 | LL | fn tuple_two() - | --------- + | --------- required by a bound in this LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize> | ---------------------------------------------------------- required by this bound in `tuple_two` ... @@ -69,7 +69,7 @@ error[E0271]: type mismatch resolving `for<'x, 'y> $DIR/associated-types-eq-hr.rs:97:5 | LL | fn tuple_two() - | --------- + | --------- required by a bound in this LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize> | ------------- required by this bound in `tuple_two` ... @@ -80,7 +80,7 @@ error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize --> $DIR/associated-types-eq-hr.rs:107:18 | LL | fn tuple_four() - | ---------- + | ---------- required by a bound in this LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize)> | ------------------------------------------- required by this bound in `tuple_four` ... diff --git a/src/test/ui/associated-types/associated-types-for-unimpl-trait.fixed b/src/test/ui/associated-types/associated-types-for-unimpl-trait.fixed index aa23326506f63..80bbef17469db 100644 --- a/src/test/ui/associated-types/associated-types-for-unimpl-trait.fixed +++ b/src/test/ui/associated-types/associated-types-for-unimpl-trait.fixed @@ -7,7 +7,7 @@ trait Get { } trait Other { - fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} + fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} //~^ ERROR the trait bound `Self: Get` is not satisfied } diff --git a/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr b/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr index 83d5390417e77..6d7289bd0712b 100644 --- a/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr +++ b/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr @@ -2,10 +2,12 @@ error[E0277]: the trait bound `Self: Get` is not satisfied --> $DIR/associated-types-for-unimpl-trait.rs:10:5 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | | - | | help: consider further restricting `Self`: `where Self: Get` - | the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-invalid-trait-ref-issue-18865.stderr b/src/test/ui/associated-types/associated-types-invalid-trait-ref-issue-18865.stderr index bac663dfea2b3..1df127873538d 100644 --- a/src/test/ui/associated-types/associated-types-invalid-trait-ref-issue-18865.stderr +++ b/src/test/ui/associated-types/associated-types-invalid-trait-ref-issue-18865.stderr @@ -4,11 +4,10 @@ error[E0277]: the trait bound `T: Foo` is not satisfied LL | let u: >::Bar = t.get_bar(); | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `T` | -help: consider further restricting this bound with `+ Foo` - --> $DIR/associated-types-invalid-trait-ref-issue-18865.rs:9:8 +help: consider further restricting this bound | -LL | fn f>(t: &T) { - | ^^^^^^^^^^ +LL | fn f + Foo>(t: &T) { + | ^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-issue-20346.stderr b/src/test/ui/associated-types/associated-types-issue-20346.stderr index cebcae44fd00c..db35c1af17147 100644 --- a/src/test/ui/associated-types/associated-types-issue-20346.stderr +++ b/src/test/ui/associated-types/associated-types-issue-20346.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving ` as Iterator>::Item == std::op --> $DIR/associated-types-issue-20346.rs:34:5 | LL | fn is_iterator_of>(_: &I) {} - | -------------- ------ required by this bound in `is_iterator_of` + | ------ required by this bound in `is_iterator_of` ... LL | fn test_adapter>>(it: I) { | - this type parameter @@ -12,8 +12,6 @@ LL | is_iterator_of::, _>(&adapter); | = note: expected enum `std::option::Option` found type `T` - = 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 previous error diff --git a/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr b/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr index d56b45dc2512e..b8f20d00ff8e4 100644 --- a/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr +++ b/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr @@ -5,12 +5,14 @@ LL | want_y(t); | ^^^^^^ expected `i32`, found associated type ... LL | fn want_y>(t: &T) { } - | ------ ----- required by this bound in `want_y` + | ----- required by this bound in `want_y` | = note: expected type `i32` found associated type `::Y` - = note: consider constraining the associated type `::Y` to `i32` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `::Y` to `i32` + | +LL | fn have_x_want_y>(t: &T) + | ^^^^^^^^^ error[E0271]: type mismatch resolving `::X == u32` --> $DIR/associated-types-multiple-types-one-trait.rs:18:5 @@ -19,12 +21,14 @@ LL | want_x(t); | ^^^^^^ expected `u32`, found associated type ... LL | fn want_x>(t: &T) { } - | ------ ----- required by this bound in `want_x` + | ----- required by this bound in `want_x` | = note: expected type `u32` found associated type `::X` - = note: consider constraining the associated type `::X` to `u32` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `::X` to `u32` + | +LL | fn have_y_want_x>(t: &T) + | ^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr b/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr index 770845167cf9c..0b5dee611e489 100644 --- a/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr +++ b/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr @@ -4,11 +4,10 @@ error[E0277]: the trait bound `T: Get` is not satisfied LL | fn uhoh(foo: ::Value) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `T` | -help: consider restricting this type parameter with `T: Get` - --> $DIR/associated-types-no-suitable-bound.rs:11:13 +help: consider restricting type parameter `T` | -LL | fn uhoh(foo: ::Value) {} - | ^ +LL | fn uhoh(foo: ::Value) {} + | ^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr b/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr index 6aa0403088d3c..dfe62aa5d6b00 100644 --- a/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr +++ b/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr @@ -2,10 +2,12 @@ error[E0277]: the trait bound `Self: Get` is not satisfied --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:5 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | | - | | help: consider further restricting `Self`: `where Self: Get` - | the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr b/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr index 8c242be979611..f0f2451a1ecea 100644 --- a/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr +++ b/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr @@ -2,10 +2,12 @@ error[E0277]: the trait bound `Self: Get` is not satisfied --> $DIR/associated-types-no-suitable-supertrait.rs:17:5 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | | - | | help: consider further restricting `Self`: `where Self: Get` - | the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} + | ^^^^^^^^^^^^^^^ error[E0277]: the trait bound `(T, U): Get` is not satisfied --> $DIR/associated-types-no-suitable-supertrait.rs:22:5 diff --git a/src/test/ui/associated-types/associated-types-overridden-binding.stderr b/src/test/ui/associated-types/associated-types-overridden-binding.stderr index 9e10ed7b72952..b8321ce5b2537 100644 --- a/src/test/ui/associated-types/associated-types-overridden-binding.stderr +++ b/src/test/ui/associated-types/associated-types-overridden-binding.stderr @@ -1,22 +1,18 @@ -error[E0284]: type annotations needed +error[E0284]: type annotations needed: cannot satisfy `::Item == i32` --> $DIR/associated-types-overridden-binding.rs:4:12 | LL | trait Foo: Iterator {} - | ------------------------------- required by `Foo` + | ---------- required by this bound in `Foo` LL | trait Bar: Foo {} - | ^^^^^^^^^^^^^^^ cannot infer type for type parameter `Self` - | - = note: cannot resolve `::Item == i32` + | ^^^^^^^^^^^^^^^ cannot satisfy `::Item == i32` -error[E0284]: type annotations needed +error[E0284]: type annotations needed: cannot satisfy `::Item == i32` --> $DIR/associated-types-overridden-binding.rs:7:21 | LL | trait I32Iterator = Iterator; - | ----------------------------------------- required by `I32Iterator` + | ---------- required by this bound in `I32Iterator` LL | trait U32Iterator = I32Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `Self` - | - = note: cannot resolve `::Item == i32` + | ^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Item == i32` error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/associated-types-path-2.stderr b/src/test/ui/associated-types/associated-types-path-2.stderr index ec24260ec7548..b2599410f05e4 100644 --- a/src/test/ui/associated-types/associated-types-path-2.stderr +++ b/src/test/ui/associated-types/associated-types-path-2.stderr @@ -13,7 +13,7 @@ error[E0277]: the trait bound `u32: Foo` is not satisfied --> $DIR/associated-types-path-2.rs:29:5 | LL | pub fn f1(a: T, x: T::A) {} - | -- --- required by this bound in `f1` + | --- required by this bound in `f1` ... LL | f1(2u32, 4u32); | ^^ the trait `Foo` is not implemented for `u32` @@ -28,7 +28,7 @@ error[E0277]: the trait bound `u32: Foo` is not satisfied --> $DIR/associated-types-path-2.rs:35:5 | LL | pub fn f1(a: T, x: T::A) {} - | -- --- required by this bound in `f1` + | --- required by this bound in `f1` ... LL | f1(2u32, 4i32); | ^^ the trait `Foo` is not implemented for `u32` diff --git a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.fixed b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.fixed index f357045a456e6..9bc308465ebdd 100644 --- a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.fixed +++ b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.fixed @@ -7,7 +7,7 @@ trait Get { } trait Other { - fn okay(&self, foo: U, bar: ::Value) where Self: Get ; + fn okay(&self, foo: U, bar: ::Value) where Self: Get; //~^ ERROR E0277 } diff --git a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr index cb01488fa34d4..4528f03c54a63 100644 --- a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr +++ b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr @@ -2,10 +2,12 @@ error[E0277]: the trait bound `Self: Get` is not satisfied --> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:5 | LL | fn okay(&self, foo: U, bar: ::Value); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | | - | | help: consider further restricting `Self`: `where Self: Get` - | the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn okay(&self, foo: U, bar: ::Value) where Self: Get; + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-unconstrained.stderr b/src/test/ui/associated-types/associated-types-unconstrained.stderr index 14ce4836f97f8..2914a7f868b2b 100644 --- a/src/test/ui/associated-types/associated-types-unconstrained.stderr +++ b/src/test/ui/associated-types/associated-types-unconstrained.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed LL | let x: isize = Foo::bar(); | ^^^^^^^^ cannot infer type | - = note: cannot resolve `<_ as Foo>::A == _` + = note: cannot satisfy `<_ as Foo>::A == _` error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-unsized.fixed b/src/test/ui/associated-types/associated-types-unsized.fixed index f780d171fee8e..9837796e308de 100644 --- a/src/test/ui/associated-types/associated-types-unsized.fixed +++ b/src/test/ui/associated-types/associated-types-unsized.fixed @@ -6,7 +6,7 @@ trait Get { fn get(&self) -> ::Value; } -fn foo(t: T) where ::Value: std::marker::Sized { +fn foo(t: T) where ::Value: std::marker::Sized { let x = t.get(); //~ ERROR the size for values of type } diff --git a/src/test/ui/associated-types/associated-types-unsized.stderr b/src/test/ui/associated-types/associated-types-unsized.stderr index 2352ac4ad3822..6daba54ac6969 100644 --- a/src/test/ui/associated-types/associated-types-unsized.stderr +++ b/src/test/ui/associated-types/associated-types-unsized.stderr @@ -1,8 +1,6 @@ error[E0277]: the size for values of type `::Value` cannot be known at compilation time --> $DIR/associated-types-unsized.rs:10:9 | -LL | fn foo(t: T) { - | - help: consider further restricting the associated type: `where ::Value: std::marker::Sized` LL | let x = t.get(); | ^ doesn't have a size known at compile-time | @@ -10,6 +8,10 @@ LL | let x = t.get(); = note: to learn more, visit = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature +help: consider further restricting the associated type + | +LL | fn foo(t: T) where ::Value: std::marker::Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr b/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr index 2745e44ac0cfc..00f44129cc8b7 100644 --- a/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr +++ b/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr @@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier --> $DIR/bound-lifetime-in-binding-only.rs:52:23 | LL | fn elision &i32>() { - | ^ help: consider giving it a 'static lifetime: `&'static` + | ^ 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 + | +LL | fn elision &'static i32>() { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr b/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr index 96f0cb85c8c4c..a5242707c7105 100644 --- a/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr +++ b/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr @@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier --> $DIR/bound-lifetime-in-return-only.rs:34:23 | LL | fn elision(_: fn() -> &i32) { - | ^ help: consider giving it a 'static lifetime: `&'static` + | ^ 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 + | +LL | fn elision(_: fn() -> &'static i32) { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/defaults-in-other-trait-items.rs b/src/test/ui/associated-types/defaults-in-other-trait-items.rs index 9f2e8aca47712..4014f46285d70 100644 --- a/src/test/ui/associated-types/defaults-in-other-trait-items.rs +++ b/src/test/ui/associated-types/defaults-in-other-trait-items.rs @@ -3,15 +3,13 @@ // Associated type defaults may not be assumed inside the trait defining them. // ie. they only resolve to `::A`, not the actual type `()` trait Tr { - type A = (); + type A = (); //~ NOTE associated type defaults can't be assumed inside the trait defining them fn f(p: Self::A) { let () = p; //~^ ERROR mismatched types //~| NOTE expected associated type, found `()` //~| NOTE expected associated type `::A` - //~| NOTE consider constraining the associated type - //~| NOTE for more information, visit } } @@ -31,15 +29,13 @@ impl Tr for u8 { } trait AssocConst { - type Ty = u8; + type Ty = u8; //~ NOTE associated type defaults can't be assumed inside the trait defining them // Assoc. consts also cannot assume that default types hold const C: Self::Ty = 0u8; //~^ ERROR mismatched types //~| NOTE expected associated type, found `u8` //~| NOTE expected associated type `::Ty` - //~| NOTE consider constraining the associated type - //~| NOTE for more information, visit } // An impl can, however diff --git a/src/test/ui/associated-types/defaults-in-other-trait-items.stderr b/src/test/ui/associated-types/defaults-in-other-trait-items.stderr index 9ecfe49c2b571..493df30a64daf 100644 --- a/src/test/ui/associated-types/defaults-in-other-trait-items.stderr +++ b/src/test/ui/associated-types/defaults-in-other-trait-items.stderr @@ -1,24 +1,26 @@ error[E0308]: mismatched types --> $DIR/defaults-in-other-trait-items.rs:9:13 | +LL | type A = (); + | ------------ associated type defaults can't be assumed inside the trait defining them +... LL | let () = p; | ^^ expected associated type, found `()` | = note: expected associated type `::A` found unit type `()` - = note: consider constraining the associated type `::A` to `()` or calling a method that returns `::A` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types - --> $DIR/defaults-in-other-trait-items.rs:37:25 + --> $DIR/defaults-in-other-trait-items.rs:35:25 | +LL | type Ty = u8; + | ------------- associated type defaults can't be assumed inside the trait defining them +... LL | const C: Self::Ty = 0u8; | ^^^ expected associated type, found `u8` | = note: expected associated type `::Ty` found type `u8` - = note: consider constraining the associated type `::Ty` to `u8` or calling a method that returns `::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/defaults-specialization.stderr b/src/test/ui/associated-types/defaults-specialization.stderr index 1dd536ec6360f..37a4d9b60fdfd 100644 --- a/src/test/ui/associated-types/defaults-specialization.stderr +++ b/src/test/ui/associated-types/defaults-specialization.stderr @@ -9,8 +9,6 @@ LL | fn make() -> u8 { 0 } | = note: expected fn pointer `fn() -> as Tr>::Ty` found fn pointer `fn() -> u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0053]: method `make` has an incompatible type for trait --> $DIR/defaults-specialization.rs:34:18 @@ -18,17 +16,21 @@ error[E0053]: method `make` has an incompatible type for trait LL | fn make() -> Self::Ty { | -------- type in trait ... +LL | default type Ty = bool; + | ----------------------- expected this associated type +LL | LL | fn make() -> bool { true } | ^^^^ expected associated type, found `bool` | = note: expected fn pointer `fn() -> as Tr>::Ty` found fn pointer `fn() -> bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:9:9 | +LL | type Ty = u8; + | ------------- associated type defaults can't be assumed inside the trait defining them +LL | LL | fn make() -> Self::Ty { | -------- expected `::Ty` because of return type LL | 0u8 @@ -36,8 +38,6 @@ LL | 0u8 | = note: expected associated type `::Ty` found type `u8` - = note: consider constraining the associated type `::Ty` to `u8` or calling a method that returns `::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:25:29 @@ -49,12 +49,15 @@ LL | fn make() -> Self::Ty { 0u8 } | = note: expected associated type ` as Tr>::Ty` found type `u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` + = help: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:43:29 | +LL | default type Ty = bool; + | ----------------------- expected this associated type +LL | LL | fn make() -> Self::Ty { true } | -------- ^^^^ expected associated type, found `bool` | | @@ -62,8 +65,6 @@ LL | fn make() -> Self::Ty { true } | = note: expected associated type ` as Tr>::Ty` found type `bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:86:32 @@ -75,8 +76,11 @@ LL | let _: as Tr>::Ty = 0u8; | = note: expected associated type ` as Tr>::Ty` found type `u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:87:32 @@ -88,8 +92,11 @@ LL | let _: as Tr>::Ty = true; | = note: expected associated type ` as Tr>::Ty` found type `bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:88:33 @@ -101,8 +108,11 @@ LL | let _: as Tr>::Ty = 0u8; | = note: expected associated type ` as Tr>::Ty` found type `u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:89:33 @@ -114,8 +124,11 @@ LL | let _: as Tr>::Ty = true; | = note: expected associated type ` as Tr>::Ty` found type `bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error: aborting due to 9 previous errors diff --git a/src/test/ui/associated-types/defaults-suitability.stderr b/src/test/ui/associated-types/defaults-suitability.stderr index 60e1821b300d2..8676c1fa22319 100644 --- a/src/test/ui/associated-types/defaults-suitability.stderr +++ b/src/test/ui/associated-types/defaults-suitability.stderr @@ -23,12 +23,11 @@ LL | trait Foo { LL | type Bar: Clone = Vec; | ^^^^^ the trait `std::clone::Clone` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::clone::Clone` - --> $DIR/defaults-suitability.rs:32:11 - | -LL | trait Foo { - | ^ = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec` +help: consider restricting type parameter `T` + | +LL | trait Foo { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `(): Foo` is not satisfied --> $DIR/defaults-suitability.rs:39:17 @@ -86,25 +85,29 @@ error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not --> $DIR/defaults-suitability.rs:72:15 | LL | trait Foo2 { - | -------------- help: consider further restricting the associated type: `where >::Baz: std::clone::Clone` - | | - | required by `Foo2` + | ------------- required by `Foo2` LL | type Bar: Clone = Vec; | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` | = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` +help: consider further restricting the associated type + | +LL | trait Foo2 where >::Baz: std::clone::Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied --> $DIR/defaults-suitability.rs:81:15 | LL | trait Foo25 { - | ---------------------- help: consider further restricting the associated type: `where >::Baz: std::clone::Clone` - | | - | required by `Foo25` + | --------------------- required by `Foo25` LL | type Bar: Clone = Vec; | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` | = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` +help: consider further restricting the associated type + | +LL | trait Foo25 where >::Baz: std::clone::Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied --> $DIR/defaults-suitability.rs:90:16 @@ -119,21 +122,24 @@ LL | | type Baz = T; LL | | } | |_- required by `Foo3` | -help: consider restricting this type parameter with `where T: std::clone::Clone` - --> $DIR/defaults-suitability.rs:88:12 +help: consider further restricting type parameter `T` | -LL | trait Foo3 where - | ^ +LL | Self::Baz: Clone, T: std::clone::Clone + | ^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/defaults-suitability.rs:27:5 | LL | type Ty = Vec<[u8]>; | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/liballoc/vec.rs:LL:COL + | +LL | pub struct Vec { + | - required by this bound in `std::vec::Vec` | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = note: to learn more, visit - = note: required by `std::vec::Vec` error: aborting due to 11 previous errors diff --git a/src/test/ui/associated-types/defaults-unsound-62211-1.stderr b/src/test/ui/associated-types/defaults-unsound-62211-1.stderr index 9c4a126013942..69c310766c1cc 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-1.stderr +++ b/src/test/ui/associated-types/defaults-unsound-62211-1.stderr @@ -6,6 +6,11 @@ LL | trait UncheckedCopy: Sized { ... LL | type Output: Copy | ^^^^ the trait `std::marker::Copy` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::marker::Copy { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: cannot add-assign `&'static str` to `Self` --> $DIR/defaults-unsound-62211-1.rs:25:7 @@ -17,6 +22,10 @@ LL | + AddAssign<&'static str> | ^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `Self += &'static str` | = help: the trait `std::ops::AddAssign<&'static str>` is not implemented for `Self` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::ops::AddAssign<&'static str> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Self: std::ops::Deref` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:23:7 @@ -26,6 +35,11 @@ LL | trait UncheckedCopy: Sized { ... LL | + Deref | ^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::ops::Deref { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `Self` doesn't implement `std::fmt::Display` --> $DIR/defaults-unsound-62211-1.rs:28:7 @@ -38,57 +52,81 @@ LL | + Display = Self; | = help: the trait `std::fmt::Display` is not implemented for `Self` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::fmt::Display { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` doesn't implement `std::fmt::Display` --> $DIR/defaults-unsound-62211-1.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | + Display = Self; + | ------- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ `T` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `T` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead -help: consider restricting this type parameter with `T: std::fmt::Display` - --> $DIR/defaults-unsound-62211-1.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::ops::Deref` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | + Deref + | ------------------- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::ops::Deref` - --> $DIR/defaults-unsound-62211-1.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^ error[E0277]: cannot add-assign `&'static str` to `T` --> $DIR/defaults-unsound-62211-1.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | + AddAssign<&'static str> + | ----------------------- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ no implementation for `T += &'static str` | = help: the trait `std::ops::AddAssign<&'static str>` is not implemented for `T` -help: consider restricting this type parameter with `T: std::ops::AddAssign<&'static str>` - --> $DIR/defaults-unsound-62211-1.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl> UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | type Output: Copy + | ---- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/defaults-unsound-62211-1.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/associated-types/defaults-unsound-62211-2.stderr b/src/test/ui/associated-types/defaults-unsound-62211-2.stderr index 4602fbc99fa62..84f0ba7529ea2 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-2.stderr +++ b/src/test/ui/associated-types/defaults-unsound-62211-2.stderr @@ -6,6 +6,11 @@ LL | trait UncheckedCopy: Sized { ... LL | type Output: Copy | ^^^^ the trait `std::marker::Copy` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::marker::Copy { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: cannot add-assign `&'static str` to `Self` --> $DIR/defaults-unsound-62211-2.rs:25:7 @@ -17,6 +22,10 @@ LL | + AddAssign<&'static str> | ^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `Self += &'static str` | = help: the trait `std::ops::AddAssign<&'static str>` is not implemented for `Self` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::ops::AddAssign<&'static str> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Self: std::ops::Deref` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:23:7 @@ -26,6 +35,11 @@ LL | trait UncheckedCopy: Sized { ... LL | + Deref | ^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::ops::Deref { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `Self` doesn't implement `std::fmt::Display` --> $DIR/defaults-unsound-62211-2.rs:28:7 @@ -38,57 +52,81 @@ LL | + Display = Self; | = help: the trait `std::fmt::Display` is not implemented for `Self` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::fmt::Display { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` doesn't implement `std::fmt::Display` --> $DIR/defaults-unsound-62211-2.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | + Display = Self; + | ------- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ `T` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `T` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead -help: consider restricting this type parameter with `T: std::fmt::Display` - --> $DIR/defaults-unsound-62211-2.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::ops::Deref` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | + Deref + | ------------------- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::ops::Deref` - --> $DIR/defaults-unsound-62211-2.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^ error[E0277]: cannot add-assign `&'static str` to `T` --> $DIR/defaults-unsound-62211-2.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | + AddAssign<&'static str> + | ----------------------- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ no implementation for `T += &'static str` | = help: the trait `std::ops::AddAssign<&'static str>` is not implemented for `T` -help: consider restricting this type parameter with `T: std::ops::AddAssign<&'static str>` - --> $DIR/defaults-unsound-62211-2.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl> UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:41:9 | +LL | trait UncheckedCopy: Sized { + | ------------- required by a bound in this +... +LL | type Output: Copy + | ---- required by this bound in `UncheckedCopy` +... LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/defaults-unsound-62211-2.rs:41:6 +help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/associated-types/higher-ranked-projection.bad.stderr b/src/test/ui/associated-types/higher-ranked-projection.bad.stderr index 74c9ad2c39e67..3b3e4c3ea117a 100644 --- a/src/test/ui/associated-types/higher-ranked-projection.bad.stderr +++ b/src/test/ui/associated-types/higher-ranked-projection.bad.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `for<'a> <&'a _ as Mirror>::Image == _` --> $DIR/higher-ranked-projection.rs:25:5 | LL | fn foo(_t: T) - | --- + | --- required by a bound in this LL | where for<'a> &'a T: Mirror | ------- required by this bound in `foo` ... diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs new file mode 100644 index 0000000000000..5f994f26534bd --- /dev/null +++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs @@ -0,0 +1,32 @@ +trait Foo { + type Item; +} + +trait Bar: Foo {} + +struct S; + +impl Foo for S { + type Item = i32; +} +impl Bar for S {} + +struct T; + +impl Foo for T { + type Item = u32; +} +impl Bar for T {} + +fn bar() -> impl Bar { + T +} + +fn baz() -> impl Bar { +//~^ ERROR type mismatch resolving `::Item == i32` + bar() +} + +fn main() { + let _ = baz(); +} diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr new file mode 100644 index 0000000000000..566e390a31e48 --- /dev/null +++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr @@ -0,0 +1,20 @@ +error[E0271]: type mismatch resolving `::Item == i32` + --> $DIR/impl-trait-return-missing-constraint.rs:25:13 + | +LL | fn bar() -> impl Bar { + | -------- the expected opaque type +... +LL | fn baz() -> impl Bar { + | ^^^^^^^^^^^^^^^^^^^^ expected associated type, found `i32` + | + = note: expected associated type `::Item` + found type `i32` + = note: the return type of a function must have a statically known size +help: consider constraining the associated type `::Item` to `i32` + | +LL | fn bar() -> impl Bar { + | ^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/associated-types/issue-26681.stderr b/src/test/ui/associated-types/issue-26681.stderr index da10933df92b0..74411008c9dda 100644 --- a/src/test/ui/associated-types/issue-26681.stderr +++ b/src/test/ui/associated-types/issue-26681.stderr @@ -6,7 +6,7 @@ LL | const C: ::Bar = 6665; | = note: expected associated type `<::Fv as Foo>::Bar` found type `{integer}` - = note: consider constraining the associated type `<::Fv as Foo>::Bar` to `{integer}` or calling a method that returns `<::Fv as Foo>::Bar` + = help: consider constraining the associated type `<::Fv as Foo>::Bar` to `{integer}` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to previous error diff --git a/src/test/ui/associated-types/issue-43924.stderr b/src/test/ui/associated-types/issue-43924.stderr index 75a5b3f3551cb..f21846fd82c43 100644 --- a/src/test/ui/associated-types/issue-43924.stderr +++ b/src/test/ui/associated-types/issue-43924.stderr @@ -9,12 +9,22 @@ LL | type Out: Default + ToString + ?Sized = dyn ToString; error[E0277]: the trait bound `(dyn std::string::ToString + 'static): std::default::Default` is not satisfied --> $DIR/issue-43924.rs:10:6 | +LL | trait Foo { + | --- required by a bound in this +LL | type Out: Default + ToString + ?Sized = dyn ToString; + | ------- required by this bound in `Foo` +... LL | impl Foo for () {} | ^^^^^^^^ the trait `std::default::Default` is not implemented for `(dyn std::string::ToString + 'static)` error[E0277]: the trait bound `(dyn std::string::ToString + 'static): std::default::Default` is not satisfied --> $DIR/issue-43924.rs:11:6 | +LL | trait Foo { + | --- required by a bound in this +LL | type Out: Default + ToString + ?Sized = dyn ToString; + | ------- required by this bound in `Foo` +... LL | impl Foo for () {} | ^^^^^^^^ the trait `std::default::Default` is not implemented for `(dyn std::string::ToString + 'static)` diff --git a/src/test/ui/associated-types/issue-63593.stderr b/src/test/ui/associated-types/issue-63593.stderr index c27800f5a3fb1..82e76ff0b7cb5 100644 --- a/src/test/ui/associated-types/issue-63593.stderr +++ b/src/test/ui/associated-types/issue-63593.stderr @@ -8,6 +8,10 @@ LL | type This = Self; | = help: the trait `std::marker::Sized` is not implemented for `Self` = note: to learn more, visit +help: consider further restricting `Self` + | +LL | trait MyTrait: std::marker::Sized { + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/issue-65774-1.stderr b/src/test/ui/associated-types/issue-65774-1.stderr index 559136be705e2..72f47df5d80e7 100644 --- a/src/test/ui/associated-types/issue-65774-1.stderr +++ b/src/test/ui/associated-types/issue-65774-1.stderr @@ -9,6 +9,11 @@ LL | type MpuConfig: MyDisplay = T; error[E0277]: the trait bound `T: MyDisplay` is not satisfied --> $DIR/issue-65774-1.rs:16:6 | +LL | trait MPU { + | --- required by a bound in this +LL | type MpuConfig: MyDisplay = T; + | --------- required by this bound in `MPU` +... LL | impl MPU for S { } | ^^^ the trait `MyDisplay` is not implemented for `T` diff --git a/src/test/ui/associated-types/issue-65774-2.stderr b/src/test/ui/associated-types/issue-65774-2.stderr index cb515964226a5..aef70885af369 100644 --- a/src/test/ui/associated-types/issue-65774-2.stderr +++ b/src/test/ui/associated-types/issue-65774-2.stderr @@ -9,6 +9,11 @@ LL | type MpuConfig: MyDisplay = T; error[E0277]: the trait bound `T: MyDisplay` is not satisfied --> $DIR/issue-65774-2.rs:16:6 | +LL | trait MPU { + | --- required by a bound in this +LL | type MpuConfig: MyDisplay = T; + | --------- required by this bound in `MPU` +... LL | impl MPU for S { } | ^^^ the trait `MyDisplay` is not implemented for `T` diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr index 072e9dad062e0..3118a9c5352c3 100644 --- a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr @@ -1,39 +1,31 @@ error[E0277]: the trait bound `bool: Bar` is not satisfied - --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + --> $DIR/point-at-type-on-obligation-failure-2.rs:8:18 | +LL | trait Foo { + | --- required by a bound in this LL | type Assoc: Bar; - | ----- associated type defined here + | --- required by this bound in `Foo` ... -LL | impl Foo for () { - | --------------- in this `impl` item LL | type Assoc = bool; - | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + | ^^^^ the trait `Bar` is not implemented for `bool` error[E0277]: the trait bound `bool: Bar` is not satisfied - --> $DIR/point-at-type-on-obligation-failure-2.rs:16:5 + --> $DIR/point-at-type-on-obligation-failure-2.rs:16:18 | LL | trait Baz where Self::Assoc: Bar { - | ---------------- restricted in this bound -LL | type Assoc; - | ----- associated type defined here + | --- required by this bound in `Baz` ... -LL | impl Baz for () { - | --------------- in this `impl` item LL | type Assoc = bool; - | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + | ^^^^ the trait `Bar` is not implemented for `bool` error[E0277]: the trait bound `bool: Bar` is not satisfied - --> $DIR/point-at-type-on-obligation-failure-2.rs:24:5 + --> $DIR/point-at-type-on-obligation-failure-2.rs:24:18 | LL | trait Bat where ::Assoc: Bar { - | ------------------------- restricted in this bound -LL | type Assoc; - | ----- associated type defined here + | --- required by this bound in `Bat` ... -LL | impl Bat for () { - | --------------- in this `impl` item LL | type Assoc = bool; - | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + | ^^^^ the trait `Bar` is not implemented for `bool` error: aborting due to 3 previous errors diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr b/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr index e86b460f818b8..818702b7afe2a 100644 --- a/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr @@ -1,13 +1,8 @@ error[E0271]: type mismatch resolving `::Ok == ()` - --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + --> $DIR/point-at-type-on-obligation-failure.rs:13:15 | -LL | type Ok; - | -- associated type defined here -... -LL | impl Bar for Foo { - | ---------------- in this `impl` item LL | type Ok = (); - | ^^^^^^^^^^^^^ expected `u32`, found `()` + | ^^ expected `u32`, found `()` error: aborting due to previous error diff --git a/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.rs b/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.rs new file mode 100644 index 0000000000000..0474bf0a33944 --- /dev/null +++ b/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.rs @@ -0,0 +1,11 @@ +use std::ops::{Add, Sub, Mul, Div}; + +trait ArithmeticOps: Add + Sub + Mul + Div {} +//~^ ERROR the size for values of type `Self` cannot be known at compilation time + +impl ArithmeticOps for T where T: Add + Sub + Mul + Div { + // Nothing to implement, since T already supports the other traits. + // It has the functions it needs already +} + +fn main() {} diff --git a/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.stderr b/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.stderr new file mode 100644 index 0000000000000..a37573dffff44 --- /dev/null +++ b/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.stderr @@ -0,0 +1,21 @@ +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/trait-with-supertraits-needing-sized-self.rs:3:22 + | +LL | trait ArithmeticOps: Add + Sub + Mul + Div {} + | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/libcore/ops/arith.rs:LL:COL + | +LL | pub trait Add { + | --- required by this bound in `std::ops::Add` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: consider further restricting `Self` + | +LL | trait ArithmeticOps: Add + Sub + Mul + Div + std::marker::Sized {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/ast-json/ast-json-ice.rs b/src/test/ui/ast-json/ast-json-ice.rs index e8a622e1b8772..60e6c88fc7924 100644 --- a/src/test/ui/ast-json/ast-json-ice.rs +++ b/src/test/ui/ast-json/ast-json-ice.rs @@ -8,7 +8,7 @@ // check-pass // dont-check-compiler-stdout - don't check for any AST change. -#![feature(asm)] +#![feature(llvm_asm)] enum V { A(i32), @@ -30,7 +30,7 @@ fn main() { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] - unsafe { asm!(""::::); } + unsafe { llvm_asm!(""::::); } let x: (i32) = 35; let y = x as i64<> + 5; diff --git a/src/test/ui/ast-json/ast-json-noexpand-output.rs b/src/test/ui/ast-json/ast-json-noexpand-output.rs new file mode 100644 index 0000000000000..cba539f006539 --- /dev/null +++ b/src/test/ui/ast-json/ast-json-noexpand-output.rs @@ -0,0 +1,10 @@ +// Check that AST json printing works. +#![crate_type = "lib"] + +// check-pass +// compile-flags: -Zast-json-noexpand +// normalize-stdout-test ":\d+" -> ":0" + +// Only include a single item to reduce how often the test output needs +// updating. +extern crate core; diff --git a/src/test/ui/ast-json/ast-json-noexpand-output.stdout b/src/test/ui/ast-json/ast-json-noexpand-output.stdout new file mode 100644 index 0000000000000..1a07968bdf162 --- /dev/null +++ b/src/test/ui/ast-json/ast-json-noexpand-output.stdout @@ -0,0 +1 @@ +{"module":{"inner":{"lo":0,"hi":0},"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["extern",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["core",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":"Semi","span":{"lo":0,"hi":0}}]},"NonJoint"]]}}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"NonJoint"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} diff --git a/src/test/ui/ast-json/ast-json-output.rs b/src/test/ui/ast-json/ast-json-output.rs index e444a07460248..2e009149ed68d 100644 --- a/src/test/ui/ast-json/ast-json-output.rs +++ b/src/test/ui/ast-json/ast-json-output.rs @@ -1,7 +1,8 @@ // Check that AST json printing works. +#![crate_type = "lib"] // check-pass -// compile-flags: -Zast-json-noexpand +// compile-flags: -Zast-json // normalize-stdout-test ":\d+" -> ":0" // Only include a single item to reduce how often the test output needs diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout index 35e418696f17c..0b3704e8e0045 100644 --- a/src/test/ui/ast-json/ast-json-output.stdout +++ b/src/test/ui/ast-json/ast-json-output.stdout @@ -1 +1 @@ -{"module":{"inner":{"lo":0,"hi":0},"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["extern",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["core",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":"Semi","span":{"lo":0,"hi":0}}]},"NonJoint"]]}}],"inline":true},"attrs":[],"span":{"lo":0,"hi":0},"proc_macros":[]} +{"module":{"inner":{"lo":0,"hi":0},"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":"Empty"}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":"Empty"}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["extern",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["core",false]},"span":{"lo":0,"hi":0}}]},"NonJoint"],[{"variant":"Token","fields":[{"kind":"Semi","span":{"lo":0,"hi":0}}]},"NonJoint"]]}}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"NonJoint"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} diff --git a/src/test/ui/async-await/async-await.rs b/src/test/ui/async-await/async-await.rs index 1dc7315e88c11..0207752afe098 100644 --- a/src/test/ui/async-await/async-await.rs +++ b/src/test/ui/async-await/async-await.rs @@ -1,5 +1,8 @@ // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + #![allow(unused)] // edition:2018 diff --git a/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed b/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed index f004b4180ddc9..605cfdfe747a3 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed +++ b/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed @@ -1,12 +1,18 @@ // edition:2018 // run-rustfix -fn foo() -> Box> { +fn test_boxed() -> Box> { let x = 0u32; Box::new(async move { x } ) //~^ ERROR E0373 } +fn test_ref(x: &u32) -> impl std::future::Future + '_ { + async move { *x } + //~^ ERROR E0373 +} + fn main() { - let _foo = foo(); + let _ = test_boxed(); + let _ = test_ref(&0u32); } diff --git a/src/test/ui/async-await/async-borrowck-escaping-block-error.rs b/src/test/ui/async-await/async-borrowck-escaping-block-error.rs index 4f35fd52ca39b..ec752c15fa284 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-block-error.rs +++ b/src/test/ui/async-await/async-borrowck-escaping-block-error.rs @@ -1,12 +1,18 @@ // edition:2018 // run-rustfix -fn foo() -> Box> { +fn test_boxed() -> Box> { let x = 0u32; Box::new(async { x } ) //~^ ERROR E0373 } +fn test_ref(x: &u32) -> impl std::future::Future + '_ { + async { *x } + //~^ ERROR E0373 +} + fn main() { - let _foo = foo(); + let _ = test_boxed(); + let _ = test_ref(&0u32); } diff --git a/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr b/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr index 0eb3971d14a38..193026541d073 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr +++ b/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr @@ -1,4 +1,4 @@ -error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function +error[E0373]: async block may outlive the current function, but it borrows `x`, which is owned by the current function --> $DIR/async-borrowck-escaping-block-error.rs:6:20 | LL | Box::new(async { x } ) @@ -7,16 +7,35 @@ LL | Box::new(async { x } ) | | `x` is borrowed here | may outlive borrowed value `x` | -note: generator is returned here - --> $DIR/async-borrowck-escaping-block-error.rs:4:13 +note: async block is returned here + --> $DIR/async-borrowck-escaping-block-error.rs:4:20 | -LL | fn foo() -> Box> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn test_boxed() -> Box> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: to force the async block to take ownership of `x` (and any other referenced variables), use the `move` keyword | LL | Box::new(async move { x } ) | ^^^^^^^^^^ -error: aborting due to previous error +error[E0373]: async block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/async-borrowck-escaping-block-error.rs:11:11 + | +LL | async { *x } + | ^^^-^^ + | | | + | | `x` is borrowed here + | may outlive borrowed value `x` + | +note: async block is returned here + --> $DIR/async-borrowck-escaping-block-error.rs:11:5 + | +LL | async { *x } + | ^^^^^^^^^^^^ +help: to force the async block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | async move { *x } + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0373`. diff --git a/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs b/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs index d2fa5d0a3d0f1..e667b72aee530 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs +++ b/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs @@ -1,5 +1,5 @@ // edition:2018 -#![feature(async_closure,async_await)] +#![feature(async_closure)] fn foo() -> Box> { let x = 0u32; Box::new((async || x)()) diff --git a/src/test/ui/async-await/async-closure.rs b/src/test/ui/async-await/async-closure.rs index 9a24bd8c95439..12d66b19e07d4 100644 --- a/src/test/ui/async-await/async-closure.rs +++ b/src/test/ui/async-await/async-closure.rs @@ -1,5 +1,8 @@ // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + // edition:2018 // aux-build:arc_wake.rs diff --git a/src/test/ui/async-await/async-fn-nonsend.stderr b/src/test/ui/async-await/async-fn-nonsend.stderr index 3a2c42b383700..d36d59f1f68f6 100644 --- a/src/test/ui/async-await/async-fn-nonsend.stderr +++ b/src/test/ui/async-await/async-fn-nonsend.stderr @@ -2,7 +2,7 @@ error: future cannot be sent between threads safely --> $DIR/async-fn-nonsend.rs:49:5 | LL | fn assert_send(_: impl Send) {} - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send(local_dropped_before_await()); | ^^^^^^^^^^^ future returned by `local_dropped_before_await` is not `Send` @@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:24:5 | LL | let x = non_send(); - | - has type `impl std::fmt::Debug` + | - has type `impl std::fmt::Debug` which is not `Send` LL | drop(x); LL | fut().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later @@ -23,7 +23,7 @@ error: future cannot be sent between threads safely --> $DIR/async-fn-nonsend.rs:51:5 | LL | fn assert_send(_: impl Send) {} - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send(non_send_temporary_in_match()); | ^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send` @@ -33,7 +33,7 @@ note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:33:20 | LL | match Some(non_send()) { - | ---------- has type `impl std::fmt::Debug` + | ---------- has type `impl std::fmt::Debug` which is not `Send` LL | Some(_) => fut().await, | ^^^^^^^^^^^ await occurs here, with `non_send()` maybe used later ... @@ -44,7 +44,7 @@ error: future cannot be sent between threads safely --> $DIR/async-fn-nonsend.rs:53:5 | LL | fn assert_send(_: impl Send) {} - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send(non_sync_with_method_call()); | ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send` @@ -54,7 +54,7 @@ note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:42:9 | LL | let f: &mut std::fmt::Formatter = panic!(); - | - has type `&mut std::fmt::Formatter<'_>` + | - has type `&mut std::fmt::Formatter<'_>` which is not `Send` LL | if non_sync().fmt(f).unwrap() == () { LL | fut().await; | ^^^^^^^^^^^ await occurs here, with `f` maybe used later diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr index 61f2570b2ff93..96158fc0e0496 100644 --- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr +++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr @@ -237,7 +237,10 @@ error[E0277]: the `?` operator can only be applied to values that implement `std --> $DIR/incorrect-syntax-suggestions.rs:16:19 | LL | let _ = await bar()?; - | ^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future` + | ^^^^^^ + | | + | the `?` operator cannot be applied to type `impl std::future::Future` + | help: consider using `.await` here: `bar().await?` | = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` = note: required by `std::ops::Try::into_result` diff --git a/src/test/ui/async-await/drop-order/drop-order-for-async-fn-parameters.rs b/src/test/ui/async-await/drop-order/drop-order-for-async-fn-parameters.rs index 00072786a50a7..6c10ead3690b2 100644 --- a/src/test/ui/async-await/drop-order/drop-order-for-async-fn-parameters.rs +++ b/src/test/ui/async-await/drop-order/drop-order-for-async-fn-parameters.rs @@ -2,6 +2,9 @@ // edition:2018 // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + #![allow(unused_variables)] // Test that the drop order for parameters in a fn and async fn matches up. Also test that diff --git a/src/test/ui/async-await/drop-order/drop-order-for-temporary-in-tail-return-expr.rs b/src/test/ui/async-await/drop-order/drop-order-for-temporary-in-tail-return-expr.rs index e40acff6dc117..4ec43708584ac 100644 --- a/src/test/ui/async-await/drop-order/drop-order-for-temporary-in-tail-return-expr.rs +++ b/src/test/ui/async-await/drop-order/drop-order-for-temporary-in-tail-return-expr.rs @@ -2,6 +2,9 @@ // edition:2018 // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + #![allow(unused_variables)] // Test the drop order for parameters relative to local variables and diff --git a/src/test/ui/async-await/drop-order/drop-order-when-cancelled.rs b/src/test/ui/async-await/drop-order/drop-order-when-cancelled.rs index 9e8304935bffc..cfd68bc0d2345 100644 --- a/src/test/ui/async-await/drop-order/drop-order-when-cancelled.rs +++ b/src/test/ui/async-await/drop-order/drop-order-when-cancelled.rs @@ -2,6 +2,9 @@ // edition:2018 // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + // Test that the drop order for parameters in a fn and async fn matches up. Also test that // parameters (used or unused) are not dropped until the async fn is cancelled. // This file is mostly copy-pasted from drop-order-for-async-fn-parameters.rs diff --git a/src/test/ui/async-await/edition-deny-async-fns-2015.stderr b/src/test/ui/async-await/edition-deny-async-fns-2015.stderr index f3d982801bb99..8bffeb2131dec 100644 --- a/src/test/ui/async-await/edition-deny-async-fns-2015.stderr +++ b/src/test/ui/async-await/edition-deny-async-fns-2015.stderr @@ -2,9 +2,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:3:1 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -12,9 +11,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:5:12 | LL | fn baz() { async fn foo() {} } - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -22,9 +20,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:7:1 | LL | async fn async_baz() { - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -32,9 +29,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:8:5 | LL | async fn bar() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -42,9 +38,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:14:5 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -52,9 +47,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:18:5 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -62,9 +56,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:36:9 | LL | async fn bar() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -72,9 +65,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:26:9 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -82,9 +74,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:31:13 | LL | async fn bar() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs new file mode 100644 index 0000000000000..13b45df64eabe --- /dev/null +++ b/src/test/ui/async-await/issue-61076.rs @@ -0,0 +1,32 @@ +// edition:2018 + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct T; + +impl Future for T { + type Output = Result<(), ()>; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +async fn foo() -> Result<(), ()> { + Ok(()) +} + +async fn bar() -> Result<(), ()> { + foo()?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + Ok(()) +} + +async fn baz() -> Result<(), ()> { + let t = T; + t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + Ok(()) +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr new file mode 100644 index 0000000000000..e71f4e7136dad --- /dev/null +++ b/src/test/ui/async-await/issue-61076.stderr @@ -0,0 +1,27 @@ +error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` + --> $DIR/issue-61076.rs:22:5 + | +LL | foo()?; + | ^^^^^^ + | | + | the `?` operator cannot be applied to type `impl std::future::Future` + | help: consider using `.await` here: `foo().await?` + | + = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` + = note: required by `std::ops::Try::into_result` + +error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` + --> $DIR/issue-61076.rs:28:5 + | +LL | t?; + | ^^ + | | + | the `?` operator cannot be applied to type `T` + | help: consider using `.await` here: `t.await?` + | + = help: the trait `std::ops::Try` is not implemented for `T` + = note: required by `std::ops::Try::into_result` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-64130-1-sync.rs b/src/test/ui/async-await/issue-64130-1-sync.rs index cc5ca89f03af0..af83f14bbda5d 100644 --- a/src/test/ui/async-await/issue-64130-1-sync.rs +++ b/src/test/ui/async-await/issue-64130-1-sync.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] // edition:2018 // This tests the the specialized async-await-specific error when futures don't implement an diff --git a/src/test/ui/async-await/issue-64130-1-sync.stderr b/src/test/ui/async-await/issue-64130-1-sync.stderr index 8beb31f152a9d..42e9e4642cea5 100644 --- a/src/test/ui/async-await/issue-64130-1-sync.stderr +++ b/src/test/ui/async-await/issue-64130-1-sync.stderr @@ -2,7 +2,7 @@ error: future cannot be shared between threads safely --> $DIR/issue-64130-1-sync.rs:21:5 | LL | fn is_sync(t: T) { } - | ------- ---- required by this bound in `is_sync` + | ---- required by this bound in `is_sync` ... LL | is_sync(bar()); | ^^^^^^^ future returned by `bar` is not `Sync` @@ -12,7 +12,7 @@ note: future is not `Sync` as this value is used across an await --> $DIR/issue-64130-1-sync.rs:15:5 | LL | let x = Foo; - | - has type `Foo` + | - has type `Foo` which is not `Sync` LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later LL | } diff --git a/src/test/ui/async-await/issue-64130-2-send.rs b/src/test/ui/async-await/issue-64130-2-send.rs index 1efe2ab3f85e2..2362831d8b8f6 100644 --- a/src/test/ui/async-await/issue-64130-2-send.rs +++ b/src/test/ui/async-await/issue-64130-2-send.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] // edition:2018 // This tests the the specialized async-await-specific error when futures don't implement an diff --git a/src/test/ui/async-await/issue-64130-2-send.stderr b/src/test/ui/async-await/issue-64130-2-send.stderr index 823b88e18c5b6..f6f834618d36f 100644 --- a/src/test/ui/async-await/issue-64130-2-send.stderr +++ b/src/test/ui/async-await/issue-64130-2-send.stderr @@ -2,7 +2,7 @@ error: future cannot be sent between threads safely --> $DIR/issue-64130-2-send.rs:21:5 | LL | fn is_send(t: T) { } - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(bar()); | ^^^^^^^ future returned by `bar` is not `Send` @@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-2-send.rs:15:5 | LL | let x = Foo; - | - has type `Foo` + | - has type `Foo` which is not `Send` LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later LL | } diff --git a/src/test/ui/async-await/issue-64130-3-other.rs b/src/test/ui/async-await/issue-64130-3-other.rs index 901544edba18a..b819970d59d50 100644 --- a/src/test/ui/async-await/issue-64130-3-other.rs +++ b/src/test/ui/async-await/issue-64130-3-other.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] // edition:2018 // This tests the the unspecialized async-await-specific error when futures don't implement an diff --git a/src/test/ui/async-await/issue-64130-3-other.stderr b/src/test/ui/async-await/issue-64130-3-other.stderr index d6828172928dd..3475b66b375dd 100644 --- a/src/test/ui/async-await/issue-64130-3-other.stderr +++ b/src/test/ui/async-await/issue-64130-3-other.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl std::future::Future` - --> $DIR/issue-64130-3-other.rs:23:5 + --> $DIR/issue-64130-3-other.rs:24:5 | LL | fn is_qux(t: T) { } - | ------ --- required by this bound in `is_qux` + | --- required by this bound in `is_qux` LL | LL | async fn bar() { | - within this `impl std::future::Future` @@ -13,10 +13,10 @@ LL | is_qux(bar()); = help: the following implementations were found: note: future does not implement `Qux` as this value is used across an await - --> $DIR/issue-64130-3-other.rs:17:5 + --> $DIR/issue-64130-3-other.rs:18:5 | LL | let x = Foo; - | - has type `Foo` + | - has type `Foo` which does not implement `Qux` LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later LL | } diff --git a/src/test/ui/async-await/issue-64130-4-async-move.stderr b/src/test/ui/async-await/issue-64130-4-async-move.stderr index 1e52d74f1559c..fc231d394c11f 100644 --- a/src/test/ui/async-await/issue-64130-4-async-move.stderr +++ b/src/test/ui/async-await/issue-64130-4-async-move.stderr @@ -2,7 +2,7 @@ error: future cannot be sent between threads safely --> $DIR/issue-64130-4-async-move.rs:15:17 | LL | pub fn foo() -> impl Future + Send { - | ^^^^^^^^^^^^^^^^^^ future returned by `foo` is not `Send` + | ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` ... LL | / async move { LL | | match client.status() { @@ -18,7 +18,7 @@ note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-4-async-move.rs:21:26 | LL | match client.status() { - | ------ has type `&Client` + | ------ has type `&Client` which is not `Send` LL | 200 => { LL | let _x = get().await; | ^^^^^^^^^^^ await occurs here, with `client` maybe used later diff --git a/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr b/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr index 662407f7017f5..f72757339cc5e 100644 --- a/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr +++ b/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr @@ -2,7 +2,7 @@ error: future cannot be sent between threads safely --> $DIR/issue-64130-non-send-future-diags.rs:21:5 | LL | fn is_send(t: T) { } - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(foo()); | ^^^^^^^ future returned by `foo` is not `Send` @@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-non-send-future-diags.rs:15:5 | LL | let g = x.lock().unwrap(); - | - has type `std::sync::MutexGuard<'_, u32>` + | - has type `std::sync::MutexGuard<'_, u32>` which is not `Send` LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `g` maybe used later LL | } diff --git a/src/test/ui/async-await/issue-66312.rs b/src/test/ui/async-await/issue-66312.rs new file mode 100644 index 0000000000000..9224971ecb123 --- /dev/null +++ b/src/test/ui/async-await/issue-66312.rs @@ -0,0 +1,14 @@ +// edition:2018 + +trait Test { + fn is_some(self: T); //~ ERROR invalid `self` parameter type +} + +async fn f() { + let x = Some(2); + if x.is_some() { + println!("Some"); + } +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-66312.stderr b/src/test/ui/async-await/issue-66312.stderr new file mode 100644 index 0000000000000..80d294a10a018 --- /dev/null +++ b/src/test/ui/async-await/issue-66312.stderr @@ -0,0 +1,12 @@ +error[E0307]: invalid `self` parameter type: T + --> $DIR/issue-66312.rs:4:22 + | +LL | fn is_some(self: T); + | ^ + | + = 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: aborting due to previous error + +For more information about this error, try `rustc --explain E0307`. diff --git a/src/test/ui/async-await/issue-67252-unnamed-future.stderr b/src/test/ui/async-await/issue-67252-unnamed-future.stderr index 24aedeb96597a..b43478ee2070b 100644 --- a/src/test/ui/async-await/issue-67252-unnamed-future.stderr +++ b/src/test/ui/async-await/issue-67252-unnamed-future.stderr @@ -2,17 +2,17 @@ error: future cannot be sent between threads safely --> $DIR/issue-67252-unnamed-future.rs:18:5 | LL | fn spawn(_: T) {} - | ----- ---- required by this bound in `spawn` + | ---- required by this bound in `spawn` ... LL | spawn(async { - | ^^^^^ future is not `Send` + | ^^^^^ future created by async block is not `Send` | = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*mut ()` note: future is not `Send` as this value is used across an await --> $DIR/issue-67252-unnamed-future.rs:20:9 | LL | let _a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send` - | -- has type `*mut ()` + | -- has type `*mut ()` which is not `Send` LL | AFuture.await; | ^^^^^^^^^^^^^ await occurs here, with `_a` maybe used later LL | }); diff --git a/src/test/ui/async-await/issue-68112.rs b/src/test/ui/async-await/issue-68112.rs new file mode 100644 index 0000000000000..11b1783680807 --- /dev/null +++ b/src/test/ui/async-await/issue-68112.rs @@ -0,0 +1,64 @@ +// edition:2018 + +use std::{ + future::Future, + cell::RefCell, + sync::Arc, + pin::Pin, + task::{Context, Poll}, +}; + +fn require_send(_: impl Send) {} + +struct Ready(Option); +impl Future for Ready { + type Output = T; + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(self.0.take().unwrap()) + } +} +fn ready(t: T) -> Ready { + Ready(Some(t)) +} + +fn make_non_send_future1() -> impl Future>> { + ready(Arc::new(RefCell::new(0))) +} + +fn test1() { + let send_fut = async { + let non_send_fut = make_non_send_future1(); + let _ = non_send_fut.await; + ready(0).await; + }; + require_send(send_fut); + //~^ ERROR future cannot be sent between threads +} + +fn test1_no_let() { + let send_fut = async { + let _ = make_non_send_future1().await; + ready(0).await; + }; + require_send(send_fut); + //~^ ERROR future cannot be sent between threads +} + +async fn ready2(t: T) -> T { t } +fn make_non_send_future2() -> impl Future>> { + ready2(Arc::new(RefCell::new(0))) +} + +// Ideally this test would have diagnostics similar to the test above, but right +// now it doesn't. +fn test2() { + let send_fut = async { + let non_send_fut = make_non_send_future2(); + let _ = non_send_fut.await; + ready(0).await; + }; + require_send(send_fut); + //~^ ERROR `std::cell::RefCell` cannot be shared between threads safely +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-68112.stderr b/src/test/ui/async-await/issue-68112.stderr new file mode 100644 index 0000000000000..6ded3e475bc37 --- /dev/null +++ b/src/test/ui/async-await/issue-68112.stderr @@ -0,0 +1,56 @@ +error: future cannot be sent between threads safely + --> $DIR/issue-68112.rs:34:5 + | +LL | fn require_send(_: impl Send) {} + | ---- required by this bound in `require_send` +... +LL | require_send(send_fut); + | ^^^^^^^^^^^^ future created by async block is not `Send` + | + = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` +note: future is not `Send` as it awaits another future which is not `Send` + --> $DIR/issue-68112.rs:31:17 + | +LL | let _ = non_send_fut.await; + | ^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send` + +error: future cannot be sent between threads safely + --> $DIR/issue-68112.rs:43:5 + | +LL | fn require_send(_: impl Send) {} + | ---- required by this bound in `require_send` +... +LL | require_send(send_fut); + | ^^^^^^^^^^^^ future created by async block is not `Send` + | + = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` +note: future is not `Send` as it awaits another future which is not `Send` + --> $DIR/issue-68112.rs:40:17 + | +LL | let _ = make_non_send_future1().await; + | ^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send` + +error[E0277]: `std::cell::RefCell` cannot be shared between threads safely + --> $DIR/issue-68112.rs:60:5 + | +LL | fn require_send(_: impl Send) {} + | ---- required by this bound in `require_send` +... +LL | require_send(send_fut); + | ^^^^^^^^^^^^ `std::cell::RefCell` cannot be shared between threads safely + | + = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` + = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc>` + = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc> {}]` + = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc> {}]>` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `{std::future::ResumeTy, impl std::future::Future, (), i32, Ready}` + = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready}]` + = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready}]>` + = note: required because it appears within the type `impl std::future::Future` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-68523-start.rs b/src/test/ui/async-await/issue-68523-start.rs new file mode 100644 index 0000000000000..5988dffd68fa7 --- /dev/null +++ b/src/test/ui/async-await/issue-68523-start.rs @@ -0,0 +1,9 @@ +// edition:2018 + +#![feature(start)] + +#[start] +pub async fn start(_: isize, _: *const *const u8) -> isize { +//~^ ERROR start is not allowed to be `async` + 0 +} diff --git a/src/test/ui/async-await/issue-68523-start.stderr b/src/test/ui/async-await/issue-68523-start.stderr new file mode 100644 index 0000000000000..e471945900e7d --- /dev/null +++ b/src/test/ui/async-await/issue-68523-start.stderr @@ -0,0 +1,9 @@ +error[E0752]: start is not allowed to be `async` + --> $DIR/issue-68523-start.rs:6:1 + | +LL | pub async fn start(_: isize, _: *const *const u8) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ start is not allowed to be `async` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0752`. diff --git a/src/test/ui/async-await/issue-68523.rs b/src/test/ui/async-await/issue-68523.rs new file mode 100644 index 0000000000000..e6250c40c714c --- /dev/null +++ b/src/test/ui/async-await/issue-68523.rs @@ -0,0 +1,7 @@ +// edition:2018 + +async fn main() -> Result { +//~^ ERROR `main` function is not allowed to be `async` +//~^^ ERROR `main` has invalid return type `impl std::future::Future` + Ok(1) +} diff --git a/src/test/ui/async-await/issue-68523.stderr b/src/test/ui/async-await/issue-68523.stderr new file mode 100644 index 0000000000000..62e37cf2629d7 --- /dev/null +++ b/src/test/ui/async-await/issue-68523.stderr @@ -0,0 +1,18 @@ +error[E0277]: `main` has invalid return type `impl std::future::Future` + --> $DIR/issue-68523.rs:3:20 + | +LL | async fn main() -> Result { + | ^^^^^^^^^^^^^^^ `main` can only return types that implement `std::process::Termination` + | + = help: consider using `()`, or a `Result` + +error[E0752]: `main` function is not allowed to be `async` + --> $DIR/issue-68523.rs:3:1 + | +LL | async fn main() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` function is not allowed to be `async` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0752. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-70594.rs b/src/test/ui/async-await/issue-70594.rs new file mode 100644 index 0000000000000..e78231a68512d --- /dev/null +++ b/src/test/ui/async-await/issue-70594.rs @@ -0,0 +1,12 @@ +// edition:2018 + +async fn fun() { + [1; ().await]; + //~^ error: `await` is only allowed inside `async` functions and blocks + //~| error: `.await` is not allowed in a `const` + //~| error: `loop` is not allowed in a `const` + //~| error: `.await` is not allowed in a `const` + //~| error: the trait bound `(): std::future::Future` is not satisfied +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-70594.stderr b/src/test/ui/async-await/issue-70594.stderr new file mode 100644 index 0000000000000..496ca506c60f2 --- /dev/null +++ b/src/test/ui/async-await/issue-70594.stderr @@ -0,0 +1,41 @@ +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/issue-70594.rs:4:9 + | +LL | async fn fun() { + | --- this is not `async` +LL | [1; ().await]; + | ^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0744]: `.await` is not allowed in a `const` + --> $DIR/issue-70594.rs:4:9 + | +LL | [1; ().await]; + | ^^^^^^^^ + +error[E0658]: `loop` is not allowed in a `const` + --> $DIR/issue-70594.rs:4:9 + | +LL | [1; ().await]; + | ^^^^^^^^ + | + = note: see issue #52000 for more information + = help: add `#![feature(const_loop)]` to the crate attributes to enable + +error[E0744]: `.await` is not allowed in a `const` + --> $DIR/issue-70594.rs:4:9 + | +LL | [1; ().await]; + | ^^^^^^^^ + +error[E0277]: the trait bound `(): std::future::Future` is not satisfied + --> $DIR/issue-70594.rs:4:9 + | +LL | [1; ().await]; + | ^^^^^^^^ the trait `std::future::Future` is not implemented for `()` + | + = note: required by `std::future::Future::poll` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0658, E0728, E0744. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-70818.rs b/src/test/ui/async-await/issue-70818.rs new file mode 100644 index 0000000000000..0609e4fc08170 --- /dev/null +++ b/src/test/ui/async-await/issue-70818.rs @@ -0,0 +1,9 @@ +// edition:2018 + +use std::future::Future; +fn foo(ty: T, ty1: U) -> impl Future + Send { +//~^ Error future cannot be sent between threads safely + async { (ty, ty1) } +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-70818.stderr b/src/test/ui/async-await/issue-70818.stderr new file mode 100644 index 0000000000000..5fb772fa10acb --- /dev/null +++ b/src/test/ui/async-await/issue-70818.stderr @@ -0,0 +1,23 @@ +error: future cannot be sent between threads safely + --> $DIR/issue-70818.rs:4:38 + | +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` +LL | +LL | async { (ty, ty1) } + | ------------------- this returned value is of type `impl std::future::Future` + | + = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `U` +note: captured value is not `Send` + --> $DIR/issue-70818.rs:6:18 + | +LL | async { (ty, ty1) } + | ^^^ has type `U` which is not `Send` + = note: the return type of a function must have a statically known size +help: consider restricting type parameter `U` + | +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/issue-71137.rs b/src/test/ui/async-await/issue-71137.rs new file mode 100644 index 0000000000000..ebb392a45308e --- /dev/null +++ b/src/test/ui/async-await/issue-71137.rs @@ -0,0 +1,21 @@ +// edition:2018 + +use std::future::Future; +use std::sync::Mutex; + +fn fake_spawn(f: F) { } + +async fn wrong_mutex() { + let m = Mutex::new(1); + { + let mut guard = m.lock().unwrap(); + (async { "right"; }).await; + *guard += 1; + } + + (async { "wrong"; }).await; +} + +fn main() { + fake_spawn(wrong_mutex()); //~ Error future cannot be sent between threads safely +} diff --git a/src/test/ui/async-await/issue-71137.stderr b/src/test/ui/async-await/issue-71137.stderr new file mode 100644 index 0000000000000..788a9bc2c7e47 --- /dev/null +++ b/src/test/ui/async-await/issue-71137.stderr @@ -0,0 +1,23 @@ +error: future cannot be sent between threads safely + --> $DIR/issue-71137.rs:20:3 + | +LL | fn fake_spawn(f: F) { } + | ---- required by this bound in `fake_spawn` +... +LL | fake_spawn(wrong_mutex()); + | ^^^^^^^^^^ future returned by `wrong_mutex` is not `Send` + | + = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, i32>` +note: future is not `Send` as this value is used across an await + --> $DIR/issue-71137.rs:12:5 + | +LL | let mut guard = m.lock().unwrap(); + | --------- has type `std::sync::MutexGuard<'_, i32>` which is not `Send` +LL | (async { "right"; }).await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `mut guard` maybe used later +LL | *guard += 1; +LL | } + | - `mut guard` is later dropped here + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/issues/auxiliary/issue_67893.rs b/src/test/ui/async-await/issues/auxiliary/issue_67893.rs new file mode 100644 index 0000000000000..387966a5064fa --- /dev/null +++ b/src/test/ui/async-await/issues/auxiliary/issue_67893.rs @@ -0,0 +1,10 @@ +// edition:2018 + +use std::sync::{Arc, Mutex}; + +pub async fn f(_: ()) {} + +pub async fn run() { + let x: Arc> = unimplemented!(); + f(*x.lock().unwrap()).await; +} diff --git a/src/test/ui/async-await/issues/issue-54752-async-block.stderr b/src/test/ui/async-await/issues/issue-54752-async-block.stderr index c3b3392cfc495..e39a049e2d304 100644 --- a/src/test/ui/async-await/issues/issue-54752-async-block.stderr +++ b/src/test/ui/async-await/issues/issue-54752-async-block.stderr @@ -6,3 +6,5 @@ LL | fn main() { let _a = (async { }); } | = note: `#[warn(unused_parens)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/async-await/issues/issue-55324.rs b/src/test/ui/async-await/issues/issue-55324.rs index 1d77d420127a8..9ecb3b1295ee4 100644 --- a/src/test/ui/async-await/issues/issue-55324.rs +++ b/src/test/ui/async-await/issues/issue-55324.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // edition:2018 use std::future::Future; diff --git a/src/test/ui/async-await/issues/issue-58885.rs b/src/test/ui/async-await/issues/issue-58885.rs index 72a45b5007d7a..11920b07243e6 100644 --- a/src/test/ui/async-await/issues/issue-58885.rs +++ b/src/test/ui/async-await/issues/issue-58885.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // edition:2018 struct Xyz { diff --git a/src/test/ui/async-await/issues/issue-59001.rs b/src/test/ui/async-await/issues/issue-59001.rs index ea780d9f62214..4ddebcf20a368 100644 --- a/src/test/ui/async-await/issues/issue-59001.rs +++ b/src/test/ui/async-await/issues/issue-59001.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // edition:2018 use std::future::Future; diff --git a/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs b/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs index 0d015e54f8b1c..66a3b07c3bd96 100644 --- a/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs +++ b/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs @@ -1,6 +1,6 @@ // Test that opaque `impl Trait` types are allowed to contain late-bound regions. -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // edition:2018 #![feature(type_alias_impl_trait)] diff --git a/src/test/ui/async-await/issues/issue-62009-1.rs b/src/test/ui/async-await/issues/issue-62009-1.rs index aa142ebd78cb1..3ee7ab2e9d12f 100644 --- a/src/test/ui/async-await/issues/issue-62009-1.rs +++ b/src/test/ui/async-await/issues/issue-62009-1.rs @@ -1,8 +1,4 @@ // edition:2018 -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl async fn print_dur() {} diff --git a/src/test/ui/async-await/issues/issue-62009-1.stderr b/src/test/ui/async-await/issues/issue-62009-1.stderr index 0624c049048c7..ec4e9e397a81e 100644 --- a/src/test/ui/async-await/issues/issue-62009-1.stderr +++ b/src/test/ui/async-await/issues/issue-62009-1.stderr @@ -1,5 +1,5 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/issue-62009-1.rs:10:5 + --> $DIR/issue-62009-1.rs:6:5 | LL | fn main() { | ---- this is not `async` @@ -7,7 +7,7 @@ LL | async { let (); }.await; | ^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/issue-62009-1.rs:12:5 + --> $DIR/issue-62009-1.rs:8:5 | LL | fn main() { | ---- this is not `async` @@ -19,7 +19,7 @@ LL | | }.await; | |___________^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/issue-62009-1.rs:16:5 + --> $DIR/issue-62009-1.rs:12:5 | LL | fn main() { | ---- this is not `async` @@ -27,16 +27,13 @@ LL | fn main() { LL | (|_| 2333).await; | ^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks -error[E0277]: the trait bound `[closure@$DIR/issue-62009-1.rs:16:5: 16:15]: std::future::Future` is not satisfied - --> $DIR/issue-62009-1.rs:16:5 +error[E0277]: the trait bound `[closure@$DIR/issue-62009-1.rs:12:5: 12:15]: std::future::Future` is not satisfied + --> $DIR/issue-62009-1.rs:12:5 | LL | (|_| 2333).await; - | ^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `[closure@$DIR/issue-62009-1.rs:16:5: 16:15]` - | - ::: $SRC_DIR/libcore/future/mod.rs:LL:COL + | ^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `[closure@$DIR/issue-62009-1.rs:12:5: 12:15]` | -LL | F: Future, - | ------ required by this bound in `std::future::poll_with_context` + = note: required by `std::future::Future::poll` error: aborting due to 4 previous errors diff --git a/src/test/ui/async-await/issues/issue-63388-2.stderr b/src/test/ui/async-await/issues/issue-63388-2.stderr index 6edb9e63d480a..ca42263dfed7b 100644 --- a/src/test/ui/async-await/issues/issue-63388-2.stderr +++ b/src/test/ui/async-await/issues/issue-63388-2.stderr @@ -4,9 +4,13 @@ error[E0106]: missing lifetime specifier LL | foo: &dyn Foo, bar: &'a dyn Foo | -------- ----------- LL | ) -> &dyn Foo - | ^ help: consider using the named lifetime: `&'a` + | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `foo` or `bar` +help: consider using the `'a` lifetime + | +LL | ) -> &'a dyn Foo + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr index 7638ba1fe7de8..49cd30e11a0c1 100644 --- a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr +++ b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr @@ -2,10 +2,10 @@ error: future cannot be sent between threads safely --> $DIR/issue-65436-raw-ptr-not-send.rs:12:5 | LL | fn assert_send(_: T) {} - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send(async { - | ^^^^^^^^^^^ future returned by `main` is not `Send` + | ^^^^^^^^^^^ future created by async block is not `Send` | = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*const u8` note: future is not `Send` as this value is used across an await @@ -14,7 +14,7 @@ note: future is not `Send` as this value is used across an await LL | bar(Foo(std::ptr::null())).await; | ^^^^^^^^----------------^^^^^^^^- `std::ptr::null()` is later dropped here | | | - | | has type `*const u8` + | | has type `*const u8` which is not `Send` | await occurs here, with `std::ptr::null()` maybe used later help: consider moving this into a `let` binding to create a shorter lived borrow --> $DIR/issue-65436-raw-ptr-not-send.rs:14:13 diff --git a/src/test/ui/async-await/issues/issue-67893.rs b/src/test/ui/async-await/issues/issue-67893.rs new file mode 100644 index 0000000000000..9679e3807b629 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-67893.rs @@ -0,0 +1,11 @@ +// aux-build: issue_67893.rs +// edition:2018 + +extern crate issue_67893; + +fn g(_: impl Send) {} + +fn main() { + g(issue_67893::run()) + //~^ ERROR: `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely +} diff --git a/src/test/ui/async-await/issues/issue-67893.stderr b/src/test/ui/async-await/issues/issue-67893.stderr new file mode 100644 index 0000000000000..343a35a1663ac --- /dev/null +++ b/src/test/ui/async-await/issues/issue-67893.stderr @@ -0,0 +1,24 @@ +error[E0277]: `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely + --> $DIR/issue-67893.rs:9:5 + | +LL | fn g(_: impl Send) {} + | ---- required by this bound in `g` +... +LL | g(issue_67893::run()) + | ^ `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely + | + ::: $DIR/auxiliary/issue_67893.rs:7:20 + | +LL | pub async fn run() { + | - within this `impl std::future::Future` + | + = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, ()>` + = note: required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3> {std::future::ResumeTy, std::sync::Arc>, &'r std::sync::Mutex<()>, std::result::Result, std::sync::PoisonError>>, &'t1 std::sync::MutexGuard<'t2, ()>, std::sync::MutexGuard<'t3, ()>, (), impl std::future::Future}` + = note: required because it appears within the type `[static generator@issue_67893::run::{{closure}}#0 for<'r, 's, 't0, 't1, 't2, 't3> {std::future::ResumeTy, std::sync::Arc>, &'r std::sync::Mutex<()>, std::result::Result, std::sync::PoisonError>>, &'t1 std::sync::MutexGuard<'t2, ()>, std::sync::MutexGuard<'t3, ()>, (), impl std::future::Future}]` + = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@issue_67893::run::{{closure}}#0 for<'r, 's, 't0, 't1, 't2, 't3> {std::future::ResumeTy, std::sync::Arc>, &'r std::sync::Mutex<()>, std::result::Result, std::sync::PoisonError>>, &'t1 std::sync::MutexGuard<'t2, ()>, std::sync::MutexGuard<'t3, ()>, (), impl std::future::Future}]>` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issues/issue-69307-nested.rs b/src/test/ui/async-await/issues/issue-69307-nested.rs new file mode 100644 index 0000000000000..b7cdf3987f1cb --- /dev/null +++ b/src/test/ui/async-await/issues/issue-69307-nested.rs @@ -0,0 +1,30 @@ +// Regression test for #69307 +// +// Having a `async { .. foo.await .. }` block appear inside of a `+=` +// expression was causing an ICE due to a failure to save/restore +// state in the AST numbering pass when entering a nested body. +// +// check-pass +// edition:2018 + +fn block_on(_: F) -> usize { + 0 +} + +fn main() {} + +async fn bar() { + let mut sum = 0; + sum += { + block_on(async { + baz().await; + let mut inner = 1; + inner += block_on(async { + baz().await; + 0 + }) + }) + }; +} + +async fn baz() {} diff --git a/src/test/ui/async-await/issues/issue-69307.rs b/src/test/ui/async-await/issues/issue-69307.rs new file mode 100644 index 0000000000000..4dae96ec8a6a7 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-69307.rs @@ -0,0 +1,23 @@ +// Regression test for #69307 +// +// Having a `async { .. foo.await .. }` block appear inside of a `+=` +// expression was causing an ICE due to a failure to save/restore +// state in the AST numbering pass when entering a nested body. +// +// check-pass +// edition:2018 + +fn block_on(_: F) -> usize { + 0 +} + +fn main() {} + +async fn bar() { + let mut sum = 0; + sum += block_on(async { + baz().await; + }); +} + +async fn baz() {} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr index c69595a3f4d01..da584e8ad4e0d 100644 --- a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr @@ -34,7 +34,7 @@ LL | | (a, b) LL | | } | |_^ | - = note: hidden type `(&u8, &u8)` captures lifetime '_#4r + = note: hidden type `(&u8, &u8)` captures lifetime '_#5r error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> $DIR/ret-impl-trait-no-fg.rs:9:1 @@ -48,7 +48,7 @@ LL | | (a, b) LL | | } | |_^ | - = note: hidden type `(&u8, &u8)` captures lifetime '_#5r + = note: hidden type `(&u8, &u8)` captures lifetime '_#6r error: aborting due to 5 previous errors diff --git a/src/test/ui/async-await/no-params-non-move-async-closure.stderr b/src/test/ui/async-await/no-params-non-move-async-closure.stderr index 04c8c325fe7da..1f589c516a90b 100644 --- a/src/test/ui/async-await/no-params-non-move-async-closure.stderr +++ b/src/test/ui/async-await/no-params-non-move-async-closure.stderr @@ -8,3 +8,4 @@ LL | let _ = async |x: u8| {}; error: aborting due to previous error +For more information about this error, try `rustc --explain E0708`. diff --git a/src/test/ui/async-await/try-on-option-in-async.rs b/src/test/ui/async-await/try-on-option-in-async.rs index 51ac522017cb3..c520a07abc172 100644 --- a/src/test/ui/async-await/try-on-option-in-async.rs +++ b/src/test/ui/async-await/try-on-option-in-async.rs @@ -7,7 +7,8 @@ async fn an_async_block() -> u32 { let x: Option = None; x?; //~ ERROR the `?` operator 22 - }.await + } + .await } async fn async_closure_containing_fn() -> u32 { diff --git a/src/test/ui/async-await/try-on-option-in-async.stderr b/src/test/ui/async-await/try-on-option-in-async.stderr index 46f8f41076bf5..700296d674784 100644 --- a/src/test/ui/async-await/try-on-option-in-async.stderr +++ b/src/test/ui/async-await/try-on-option-in-async.stderr @@ -7,14 +7,14 @@ LL | | let x: Option = None; LL | | x?; | | ^^ cannot use the `?` operator in an async block that returns `{integer}` LL | | 22 -LL | | }.await +LL | | } | |_____- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `{integer}` = note: required by `std::ops::Try::from_error` error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`) - --> $DIR/try-on-option-in-async.rs:16:9 + --> $DIR/try-on-option-in-async.rs:17:9 | LL | let async_closure = async || { | __________________________________- @@ -29,7 +29,7 @@ LL | | }; = note: required by `std::ops::Try::from_error` error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) - --> $DIR/try-on-option-in-async.rs:25:5 + --> $DIR/try-on-option-in-async.rs:26:5 | LL | async fn an_async_function() -> u32 { | _____________________________________- diff --git a/src/test/ui/attributes/item-attributes.rs b/src/test/ui/attributes/item-attributes.rs index 79cd0f5fbc358..a3d5933965a41 100644 --- a/src/test/ui/attributes/item-attributes.rs +++ b/src/test/ui/attributes/item-attributes.rs @@ -2,7 +2,7 @@ // for completeness since .rs files linked from .rc files support this // notation to specify their module's attributes -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(rustc_attrs)] diff --git a/src/test/ui/auxiliary/cond_plugin.rs b/src/test/ui/auxiliary/cond_plugin.rs index 2819541bf6966..8d3c4ec239a1a 100644 --- a/src/test/ui/auxiliary/cond_plugin.rs +++ b/src/test/ui/auxiliary/cond_plugin.rs @@ -2,7 +2,6 @@ // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(proc_macro_hygiene)] #![feature(proc_macro_quote)] extern crate proc_macro; diff --git a/src/test/ui/auxiliary/hello_macro.rs b/src/test/ui/auxiliary/hello_macro.rs index f2e9e0eaa8c0a..a05b8d54dc10e 100644 --- a/src/test/ui/auxiliary/hello_macro.rs +++ b/src/test/ui/auxiliary/hello_macro.rs @@ -2,7 +2,7 @@ // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(proc_macro_hygiene, proc_macro_quote)] +#![feature(proc_macro_quote)] extern crate proc_macro; diff --git a/src/test/ui/auxiliary/lto-rustc-loads-linker-plugin.rs b/src/test/ui/auxiliary/lto-rustc-loads-linker-plugin.rs new file mode 100644 index 0000000000000..d24375b2d0a63 --- /dev/null +++ b/src/test/ui/auxiliary/lto-rustc-loads-linker-plugin.rs @@ -0,0 +1,6 @@ +// compile-flags: -Clinker-plugin-lto +// no-prefer-dynamic + +#![crate_type = "rlib"] + +pub fn foo() {} diff --git a/src/test/ui/auxiliary/proc_macro_def.rs b/src/test/ui/auxiliary/proc_macro_def.rs index 49cfb5518ba9c..0497e4ae07d9a 100644 --- a/src/test/ui/auxiliary/proc_macro_def.rs +++ b/src/test/ui/auxiliary/proc_macro_def.rs @@ -2,7 +2,6 @@ // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(proc_macro_hygiene)] #![feature(proc_macro_quote)] extern crate proc_macro; diff --git a/src/test/ui/bad/bad-lint-cap3.stderr b/src/test/ui/bad/bad-lint-cap3.stderr index a4e399b1fac39..0fb65322f39e6 100644 --- a/src/test/ui/bad/bad-lint-cap3.stderr +++ b/src/test/ui/bad/bad-lint-cap3.stderr @@ -11,3 +11,5 @@ LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[warn(unused_imports)]` implied by `#[warn(warnings)]` +warning: 1 warning emitted + diff --git a/src/test/ui/bad/bad-method-typaram-kind.stderr b/src/test/ui/bad/bad-method-typaram-kind.stderr index 9732363221286..81fc961e3dea0 100644 --- a/src/test/ui/bad/bad-method-typaram-kind.stderr +++ b/src/test/ui/bad/bad-method-typaram-kind.stderr @@ -5,11 +5,10 @@ LL | 1.bar::(); | ^^^ `T` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `T` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/bad-method-typaram-kind.rs:1:10 +help: consider further restricting this bound | -LL | fn foo() { - | ^^^^^^^ +LL | fn foo() { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/bad/bad-sized.stderr b/src/test/ui/bad/bad-sized.stderr index e9ded557281a4..5c169af4eb8ae 100644 --- a/src/test/ui/bad/bad-sized.stderr +++ b/src/test/ui/bad/bad-sized.stderr @@ -14,10 +14,14 @@ error[E0277]: the size for values of type `dyn Trait` cannot be known at compila | LL | let x: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/liballoc/vec.rs:LL:COL + | +LL | pub struct Vec { + | - required by this bound in `std::vec::Vec` | = help: the trait `std::marker::Sized` is not implemented for `dyn Trait` = note: to learn more, visit - = note: required by `std::vec::Vec` error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time --> $DIR/bad-sized.rs:4:37 diff --git a/src/test/ui/bastion-of-the-turbofish.rs b/src/test/ui/bastion-of-the-turbofish.rs index cc43210d8e3b9..d9ca7ddc7cab7 100644 --- a/src/test/ui/bastion-of-the-turbofish.rs +++ b/src/test/ui/bastion-of-the-turbofish.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // Bastion of the Turbofish // ------------------------ diff --git a/src/test/ui/binding/ambiguity-item.rs b/src/test/ui/binding/ambiguity-item.rs index 10613cc616413..0f48340c2cd33 100644 --- a/src/test/ui/binding/ambiguity-item.rs +++ b/src/test/ui/binding/ambiguity-item.rs @@ -14,5 +14,6 @@ fn main() { let v = f; //~ ERROR `f` is ambiguous match v { f => {} //~ ERROR `f` is ambiguous + mut f => {} // OK, unambiguously a fresh binding due to `mut` } } diff --git a/src/test/ui/binding/const-param.stderr b/src/test/ui/binding/const-param.stderr index 25b1c75c9a004..316fac6232548 100644 --- a/src/test/ui/binding/const-param.stderr +++ b/src/test/ui/binding/const-param.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0158]: const parameters cannot be referenced in patterns --> $DIR/const-param.rs:7:9 @@ -12,6 +13,6 @@ error[E0158]: const parameters cannot be referenced in patterns LL | N => {} | ^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0158`. diff --git a/src/test/ui/binding/issue-53114-borrow-checks.rs b/src/test/ui/binding/issue-53114-borrow-checks.rs new file mode 100644 index 0000000000000..7646472f45fac --- /dev/null +++ b/src/test/ui/binding/issue-53114-borrow-checks.rs @@ -0,0 +1,84 @@ +// Issue #53114: NLL's borrow check had some deviations from the old borrow +// checker, and both had some deviations from our ideal state. This test +// captures the behavior of how `_` bindings are handled with respect to how we +// flag expressions that are meant to request unsafe blocks. +#![allow(irrefutable_let_patterns)] +struct M; + +fn let_wild_gets_moved_expr() { + let m = M; + drop(m); + let _ = m; // accepted, and want it to continue to be + + let mm = (M, M); // variation on above with `_` in substructure + let (_x, _) = mm; + let (_, _y) = mm; + let (_, _) = mm; +} + +fn match_moved_expr_to_wild() { + let m = M; + drop(m); + match m { _ => { } } // #53114: should eventually be accepted too + //~^ ERROR [E0382] + + let mm = (M, M); // variation on above with `_` in substructure + match mm { (_x, _) => { } } + match mm { (_, _y) => { } } + //~^ ERROR [E0382] + match mm { (_, _) => { } } + //~^ ERROR [E0382] +} + +fn if_let_moved_expr_to_wild() { + let m = M; + drop(m); + if let _ = m { } // #53114: should eventually be accepted too + //~^ ERROR [E0382] + + let mm = (M, M); // variation on above with `_` in substructure + if let (_x, _) = mm { } + if let (_, _y) = mm { } + //~^ ERROR [E0382] + if let (_, _) = mm { } + //~^ ERROR [E0382] +} + +fn let_wild_gets_borrowed_expr() { + let mut m = M; + let r = &mut m; + let _ = m; // accepted, and want it to continue to be + // let _x = m; // (compare with this error.) + drop(r); + + let mut mm = (M, M); // variation on above with `_` in substructure + let (r1, r2) = (&mut mm.0, &mut mm.1); + let (_, _) = mm; + drop((r1, r2)); +} + +fn match_borrowed_expr_to_wild() { + let mut m = M; + let r = &mut m; + match m { _ => {} } ; // accepted, and want it to continue to be + drop(r); + + let mut mm = (M, M); // variation on above with `_` in substructure + let (r1, r2) = (&mut mm.0, &mut mm.1); + match mm { (_, _) => { } } + drop((r1, r2)); +} + +fn if_let_borrowed_expr_to_wild() { + let mut m = M; + let r = &mut m; + if let _ = m { } // accepted, and want it to continue to be + drop(r); + + let mut mm = (M, M); // variation on above with `_` in substructure + let (r1, r2) = (&mut mm.0, &mut mm.1); + if let (_, _) = mm { } + drop((r1, r2)); +} + +fn main() { } diff --git a/src/test/ui/binding/issue-53114-borrow-checks.stderr b/src/test/ui/binding/issue-53114-borrow-checks.stderr new file mode 100644 index 0000000000000..2a7a721324d69 --- /dev/null +++ b/src/test/ui/binding/issue-53114-borrow-checks.stderr @@ -0,0 +1,65 @@ +error[E0382]: use of moved value: `m` + --> $DIR/issue-53114-borrow-checks.rs:22:11 + | +LL | let m = M; + | - move occurs because `m` has type `M`, which does not implement the `Copy` trait +LL | drop(m); + | - value moved here +LL | match m { _ => { } } // #53114: should eventually be accepted too + | ^ value used here after move + +error[E0382]: use of moved value: `mm` + --> $DIR/issue-53114-borrow-checks.rs:27:11 + | +LL | match mm { (_x, _) => { } } + | -- value moved here +LL | match mm { (_, _y) => { } } + | ^^ value used here after partial move + | + = note: move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `mm` + --> $DIR/issue-53114-borrow-checks.rs:29:11 + | +LL | match mm { (_, _y) => { } } + | -- value moved here +LL | +LL | match mm { (_, _) => { } } + | ^^ value used here after partial move + | + = note: move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `m` + --> $DIR/issue-53114-borrow-checks.rs:36:16 + | +LL | let m = M; + | - move occurs because `m` has type `M`, which does not implement the `Copy` trait +LL | drop(m); + | - value moved here +LL | if let _ = m { } // #53114: should eventually be accepted too + | ^ value used here after move + +error[E0382]: use of moved value: `mm` + --> $DIR/issue-53114-borrow-checks.rs:41:22 + | +LL | if let (_x, _) = mm { } + | -- value moved here +LL | if let (_, _y) = mm { } + | ^^ value used here after partial move + | + = note: move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `mm` + --> $DIR/issue-53114-borrow-checks.rs:43:21 + | +LL | if let (_, _y) = mm { } + | -- value moved here +LL | +LL | if let (_, _) = mm { } + | ^^ value used here after partial move + | + = note: move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/binding/issue-53114-safety-checks.rs b/src/test/ui/binding/issue-53114-safety-checks.rs new file mode 100644 index 0000000000000..28adb7571a98b --- /dev/null +++ b/src/test/ui/binding/issue-53114-safety-checks.rs @@ -0,0 +1,51 @@ +// Issue #53114: NLL's borrow check had some deviations from the old borrow +// checker, and both had some deviations from our ideal state. This test +// captures the behavior of how `_` bindings are handled with respect to how we +// flag expressions that are meant to request unsafe blocks. + +#![feature(untagged_unions)] + +struct I(i64); +struct F(f64); + +union U { a: I, b: F } + +#[repr(packed)] +struct P { + a: &'static i8, + b: &'static u32, +} + +fn let_wild_gets_unsafe_field() { + let u1 = U { a: I(0) }; + let u2 = U { a: I(1) }; + let p = P { a: &2, b: &3 }; + let _ = &p.b; //~ WARN E0133 + //~^ WARN will become a hard error + let _ = u1.a; // #53114: should eventually signal error as well + let _ = &u2.a; //~ ERROR [E0133] + + // variation on above with `_` in substructure + let (_,) = (&p.b,); //~ WARN E0133 + //~^ WARN will become a hard error + let (_,) = (u1.a,); //~ ERROR [E0133] + let (_,) = (&u2.a,); //~ ERROR [E0133] +} + +fn match_unsafe_field_to_wild() { + let u1 = U { a: I(0) }; + let u2 = U { a: I(1) }; + let p = P { a: &2, b: &3 }; + match &p.b { _ => { } } //~ WARN E0133 + //~^ WARN will become a hard error + match u1.a { _ => { } } //~ ERROR [E0133] + match &u2.a { _ => { } } //~ ERROR [E0133] + + // variation on above with `_` in substructure + match (&p.b,) { (_,) => { } } //~ WARN E0133 + //~^ WARN will become a hard error + match (u1.a,) { (_,) => { } } //~ ERROR [E0133] + match (&u2.a,) { (_,) => { } } //~ ERROR [E0133] +} + +fn main() { } diff --git a/src/test/ui/binding/issue-53114-safety-checks.stderr b/src/test/ui/binding/issue-53114-safety-checks.stderr new file mode 100644 index 0000000000000..d4b8dfbade5d6 --- /dev/null +++ b/src/test/ui/binding/issue-53114-safety-checks.stderr @@ -0,0 +1,100 @@ +warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) + --> $DIR/issue-53114-safety-checks.rs:23:13 + | +LL | let _ = &p.b; + | ^^^^ + | + = note: `#[warn(safe_packed_borrows)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/issue-53114-safety-checks.rs:26:13 + | +LL | let _ = &u2.a; + | ^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) + --> $DIR/issue-53114-safety-checks.rs:29:17 + | +LL | let (_,) = (&p.b,); + | ^^^^ + | + = 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 #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/issue-53114-safety-checks.rs:31:17 + | +LL | let (_,) = (u1.a,); + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/issue-53114-safety-checks.rs:32:17 + | +LL | let (_,) = (&u2.a,); + | ^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) + --> $DIR/issue-53114-safety-checks.rs:39:11 + | +LL | match &p.b { _ => { } } + | ^^^^ + | + = 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 #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/issue-53114-safety-checks.rs:41:11 + | +LL | match u1.a { _ => { } } + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/issue-53114-safety-checks.rs:42:11 + | +LL | match &u2.a { _ => { } } + | ^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) + --> $DIR/issue-53114-safety-checks.rs:45:12 + | +LL | match (&p.b,) { (_,) => { } } + | ^^^^ + | + = 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 #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/issue-53114-safety-checks.rs:47:12 + | +LL | match (u1.a,) { (_,) => { } } + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/issue-53114-safety-checks.rs:48:12 + | +LL | match (&u2.a,) { (_,) => { } } + | ^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error: aborting due to 7 previous errors; 4 warnings emitted + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/binop/binop-consume-args.stderr b/src/test/ui/binop/binop-consume-args.stderr index 3fe7c9cbff420..acdc03e372638 100644 --- a/src/test/ui/binop/binop-consume-args.stderr +++ b/src/test/ui/binop/binop-consume-args.stderr @@ -8,11 +8,10 @@ LL | lhs + rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:5:11 +help: consider further restricting this bound | -LL | fn add, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^ +LL | fn add + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:8:10 @@ -25,11 +24,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:5:30 +help: consider restricting type parameter `B` | -LL | fn add, B>(lhs: A, rhs: B) { - | ^ +LL | fn add, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:13:10 @@ -41,11 +39,10 @@ LL | lhs - rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:11:11 +help: consider further restricting this bound | -LL | fn sub, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^ +LL | fn sub + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:14:10 @@ -58,11 +55,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:11:30 +help: consider restricting type parameter `B` | -LL | fn sub, B>(lhs: A, rhs: B) { - | ^ +LL | fn sub, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:19:10 @@ -74,11 +70,10 @@ LL | lhs * rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:17:11 +help: consider further restricting this bound | -LL | fn mul, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^ +LL | fn mul + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:20:10 @@ -91,11 +86,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:17:30 +help: consider restricting type parameter `B` | -LL | fn mul, B>(lhs: A, rhs: B) { - | ^ +LL | fn mul, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:25:10 @@ -107,11 +101,10 @@ LL | lhs / rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:23:11 +help: consider further restricting this bound | -LL | fn div, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^ +LL | fn div + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:26:10 @@ -124,11 +117,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:23:30 +help: consider restricting type parameter `B` | -LL | fn div, B>(lhs: A, rhs: B) { - | ^ +LL | fn div, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:31:10 @@ -140,11 +132,10 @@ LL | lhs % rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:29:11 +help: consider further restricting this bound | -LL | fn rem, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^ +LL | fn rem + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:32:10 @@ -157,11 +148,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:29:30 +help: consider restricting type parameter `B` | -LL | fn rem, B>(lhs: A, rhs: B) { - | ^ +LL | fn rem, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:37:10 @@ -173,11 +163,10 @@ LL | lhs & rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:35:14 +help: consider further restricting this bound | -LL | fn bitand, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^^^^ +LL | fn bitand + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:38:10 @@ -190,11 +179,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:35:36 +help: consider restricting type parameter `B` | -LL | fn bitand, B>(lhs: A, rhs: B) { - | ^ +LL | fn bitand, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:43:10 @@ -206,11 +194,10 @@ LL | lhs | rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:41:13 +help: consider further restricting this bound | -LL | fn bitor, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn bitor + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:44:10 @@ -223,11 +210,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:41:34 +help: consider restricting type parameter `B` | -LL | fn bitor, B>(lhs: A, rhs: B) { - | ^ +LL | fn bitor, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:49:10 @@ -239,11 +225,10 @@ LL | lhs ^ rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:47:14 +help: consider further restricting this bound | -LL | fn bitxor, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^^^^ +LL | fn bitxor + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:50:10 @@ -256,11 +241,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:47:36 +help: consider restricting type parameter `B` | -LL | fn bitxor, B>(lhs: A, rhs: B) { - | ^ +LL | fn bitxor, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:55:10 @@ -272,11 +256,10 @@ LL | lhs << rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:53:11 +help: consider further restricting this bound | -LL | fn shl, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^ +LL | fn shl + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:56:10 @@ -289,11 +272,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:53:30 +help: consider restricting type parameter `B` | -LL | fn shl, B>(lhs: A, rhs: B) { - | ^ +LL | fn shl, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `lhs` --> $DIR/binop-consume-args.rs:61:10 @@ -305,11 +287,10 @@ LL | lhs >> rhs; LL | drop(lhs); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-consume-args.rs:59:11 +help: consider further restricting this bound | -LL | fn shr, B>(lhs: A, rhs: B) { - | ^^^^^^^^^^^^^^^^^ +LL | fn shr + Copy, B>(lhs: A, rhs: B) { + | ^^^^^^ error[E0382]: use of moved value: `rhs` --> $DIR/binop-consume-args.rs:62:10 @@ -322,11 +303,10 @@ LL | drop(lhs); LL | drop(rhs); | ^^^ value used here after move | -help: consider restricting this type parameter with `B: Copy` - --> $DIR/binop-consume-args.rs:59:30 +help: consider restricting type parameter `B` | -LL | fn shr, B>(lhs: A, rhs: B) { - | ^ +LL | fn shr, B: Copy>(lhs: A, rhs: B) { + | ^^^^^^ error: aborting due to 20 previous errors diff --git a/src/test/ui/binop/binop-fail-3.rs b/src/test/ui/binop/binop-fail-3.rs new file mode 100644 index 0000000000000..49f635e0c11d6 --- /dev/null +++ b/src/test/ui/binop/binop-fail-3.rs @@ -0,0 +1,11 @@ +// run-fail +// error-pattern:quux +// ignore-emscripten no processes + +fn foo() -> ! { + panic!("quux"); +} + +fn main() { + foo() == foo(); // these types wind up being defaulted to () +} diff --git a/src/test/ui/binop/binop-move-semantics.stderr b/src/test/ui/binop/binop-move-semantics.stderr index 31b594eeab4bf..6d5ac9cab30c0 100644 --- a/src/test/ui/binop/binop-move-semantics.stderr +++ b/src/test/ui/binop/binop-move-semantics.stderr @@ -9,11 +9,10 @@ LL | + LL | x; | ^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-move-semantics.rs:5:19 +help: consider further restricting this bound | -LL | fn double_move>(x: T) { - | ^^^^^^^^^^^^^^ +LL | fn double_move + Copy>(x: T) { + | ^^^^^^ error[E0382]: borrow of moved value: `x` --> $DIR/binop-move-semantics.rs:14:5 @@ -26,11 +25,10 @@ LL | + LL | x.clone(); | ^ value borrowed here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/binop-move-semantics.rs:11:24 +help: consider further restricting this bound | -LL | fn move_then_borrow + Clone>(x: T) { - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | fn move_then_borrow + Clone + Copy>(x: T) { + | ^^^^^^ error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/binop-move-semantics.rs:21:5 diff --git a/src/test/run-fail/binop-panic.rs b/src/test/ui/binop/binop-panic.rs similarity index 75% rename from src/test/run-fail/binop-panic.rs rename to src/test/ui/binop/binop-panic.rs index dba5cecc67e11..44cdfffeeb7a7 100644 --- a/src/test/run-fail/binop-panic.rs +++ b/src/test/ui/binop/binop-panic.rs @@ -1,8 +1,12 @@ +// run-fail // error-pattern:quux +// ignore-emscripten no processes + fn my_err(s: String) -> ! { println!("{}", s); panic!("quux"); } + fn main() { 3_usize == my_err("bye".to_string()); } diff --git a/src/test/ui/block-expr-precedence.stderr b/src/test/ui/block-expr-precedence.stderr index decee1f2f1629..c28980bf14786 100644 --- a/src/test/ui/block-expr-precedence.stderr +++ b/src/test/ui/block-expr-precedence.stderr @@ -6,3 +6,5 @@ LL | if (true) { 12; };;; -num; | = note: `#[warn(redundant_semicolons)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/block-fn-coerce.rs b/src/test/ui/block-fn-coerce.rs index fc5f51d46b255..d993ad9945974 100644 --- a/src/test/ui/block-fn-coerce.rs +++ b/src/test/ui/block-fn-coerce.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn force(f: F) -> isize where F: FnOnce() -> isize { return f(); } diff --git a/src/test/ui/block-result/block-must-not-have-result-while.stderr b/src/test/ui/block-result/block-must-not-have-result-while.stderr index 638ce03cb3663..d4845290d8a90 100644 --- a/src/test/ui/block-result/block-must-not-have-result-while.stderr +++ b/src/test/ui/block-result/block-must-not-have-result-while.stderr @@ -9,9 +9,15 @@ LL | while true { error[E0308]: mismatched types --> $DIR/block-must-not-have-result-while.rs:3:9 | -LL | true - | ^^^^ expected `()`, found `bool` +LL | / while true { +LL | | true + | | ^^^^ expected `()`, found `bool` +LL | | +LL | | } + | | -- help: consider using a semicolon here + | |_____| + | expected this to be `()` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/borrowck/borrowck-asm.rs b/src/test/ui/borrowck/borrowck-asm.rs index c1b0f39f9366c..d16b424536aac 100644 --- a/src/test/ui/borrowck/borrowck-asm.rs +++ b/src/test/ui/borrowck/borrowck-asm.rs @@ -6,7 +6,7 @@ // ignore-sparc // ignore-sparc64 -#![feature(asm)] +#![feature(llvm_asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64", @@ -19,7 +19,7 @@ mod test_cases { let y: &mut isize; let x = &mut 0isize; unsafe { - asm!("nop" : : "r"(x)); + llvm_asm!("nop" : : "r"(x)); } let z = x; //~ ERROR use of moved value: `x` } @@ -28,7 +28,7 @@ mod test_cases { let mut x = 3; let y = &mut x; unsafe { - asm!("nop" : : "r"(x)); //~ ERROR cannot use + llvm_asm!("nop" : : "r"(x)); //~ ERROR cannot use } let z = y; } @@ -36,12 +36,12 @@ mod test_cases { fn out_is_assign() { let x = 3; unsafe { - asm!("nop" : "=r"(x)); //~ ERROR cannot assign twice + llvm_asm!("nop" : "=r"(x)); //~ ERROR cannot assign twice } let mut a = &mut 3; let b = &*a; unsafe { - asm!("nop" : "=r"(a)); // OK, Shallow write to `a` + llvm_asm!("nop" : "=r"(a)); // OK, Shallow write to `a` } let c = b; let d = *a; @@ -50,14 +50,14 @@ mod test_cases { fn rw_is_assign() { let x = 3; unsafe { - asm!("nop" : "+r"(x)); //~ ERROR cannot assign twice + llvm_asm!("nop" : "+r"(x)); //~ ERROR cannot assign twice } } fn indirect_is_not_init() { let x: i32; unsafe { - asm!("nop" : "=*r"(x)); //~ ERROR use of possibly-uninitialized variable + llvm_asm!("nop" : "=*r"(x)); //~ ERROR use of possibly-uninitialized variable } } @@ -65,7 +65,7 @@ mod test_cases { let mut x = &mut 3; let y = &*x; unsafe { - asm!("nop" : "+r"(x)); //~ ERROR cannot assign to `x` because it is borrowed + llvm_asm!("nop" : "+r"(x)); //~ ERROR cannot assign to `x` because it is borrowed } let z = y; } @@ -73,7 +73,7 @@ mod test_cases { fn two_moves() { let x = &mut 2; unsafe { - asm!("nop" : : "r"(x), "r"(x) ); //~ ERROR use of moved value + llvm_asm!("nop" : : "r"(x), "r"(x) ); //~ ERROR use of moved value } } } diff --git a/src/test/ui/borrowck/borrowck-asm.stderr b/src/test/ui/borrowck/borrowck-asm.stderr index f85b5983acced..d7e94bd34d35b 100644 --- a/src/test/ui/borrowck/borrowck-asm.stderr +++ b/src/test/ui/borrowck/borrowck-asm.stderr @@ -4,26 +4,26 @@ error[E0382]: use of moved value: `x` LL | let x = &mut 0isize; | - move occurs because `x` has type `&mut isize`, which does not implement the `Copy` trait LL | unsafe { -LL | asm!("nop" : : "r"(x)); - | - value moved here +LL | llvm_asm!("nop" : : "r"(x)); + | - value moved here LL | } LL | let z = x; | ^ value used here after move error[E0503]: cannot use `x` because it was mutably borrowed - --> $DIR/borrowck-asm.rs:31:32 + --> $DIR/borrowck-asm.rs:31:37 | LL | let y = &mut x; | ------ borrow of `x` occurs here LL | unsafe { -LL | asm!("nop" : : "r"(x)); - | ^ use of borrowed `x` +LL | llvm_asm!("nop" : : "r"(x)); + | ^ use of borrowed `x` LL | } LL | let z = y; | - borrow later used here error[E0384]: cannot assign twice to immutable variable `x` - --> $DIR/borrowck-asm.rs:39:31 + --> $DIR/borrowck-asm.rs:39:36 | LL | let x = 3; | - @@ -31,11 +31,11 @@ LL | let x = 3; | first assignment to `x` | help: make this binding mutable: `mut x` LL | unsafe { -LL | asm!("nop" : "=r"(x)); - | ^ cannot assign twice to immutable variable +LL | llvm_asm!("nop" : "=r"(x)); + | ^ cannot assign twice to immutable variable error[E0384]: cannot assign twice to immutable variable `x` - --> $DIR/borrowck-asm.rs:53:31 + --> $DIR/borrowck-asm.rs:53:36 | LL | let x = 3; | - @@ -43,37 +43,37 @@ LL | let x = 3; | first assignment to `x` | help: make this binding mutable: `mut x` LL | unsafe { -LL | asm!("nop" : "+r"(x)); - | ^ cannot assign twice to immutable variable +LL | llvm_asm!("nop" : "+r"(x)); + | ^ cannot assign twice to immutable variable error[E0381]: use of possibly-uninitialized variable: `x` - --> $DIR/borrowck-asm.rs:60:32 + --> $DIR/borrowck-asm.rs:60:37 | -LL | asm!("nop" : "=*r"(x)); - | ^ use of possibly-uninitialized `x` +LL | llvm_asm!("nop" : "=*r"(x)); + | ^ use of possibly-uninitialized `x` error[E0506]: cannot assign to `x` because it is borrowed - --> $DIR/borrowck-asm.rs:68:31 + --> $DIR/borrowck-asm.rs:68:36 | LL | let y = &*x; | --- borrow of `x` occurs here LL | unsafe { -LL | asm!("nop" : "+r"(x)); - | ^ assignment to borrowed `x` occurs here +LL | llvm_asm!("nop" : "+r"(x)); + | ^ assignment to borrowed `x` occurs here LL | } LL | let z = y; | - borrow later used here error[E0382]: use of moved value: `x` - --> $DIR/borrowck-asm.rs:76:40 + --> $DIR/borrowck-asm.rs:76:45 | LL | let x = &mut 2; | - move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait LL | unsafe { -LL | asm!("nop" : : "r"(x), "r"(x) ); - | - ^ value used here after move - | | - | value moved here +LL | llvm_asm!("nop" : : "r"(x), "r"(x) ); + | - ^ value used here after move + | | + | value moved here error: aborting due to 7 previous errors diff --git a/src/test/run-fail/borrowck-local-borrow.rs b/src/test/ui/borrowck/borrowck-local-borrow.rs similarity index 77% rename from src/test/run-fail/borrowck-local-borrow.rs rename to src/test/ui/borrowck/borrowck-local-borrow.rs index d07f76b6252dc..ea4589338c46f 100644 --- a/src/test/run-fail/borrowck-local-borrow.rs +++ b/src/test/ui/borrowck/borrowck-local-borrow.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:panic 1 +// ignore-emscripten no processes // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir diff --git a/src/test/ui/borrowck/borrowck-unboxed-closures.stderr b/src/test/ui/borrowck/borrowck-unboxed-closures.stderr index 33a0b0286dfe9..a51cda548efd7 100644 --- a/src/test/ui/borrowck/borrowck-unboxed-closures.stderr +++ b/src/test/ui/borrowck/borrowck-unboxed-closures.stderr @@ -26,11 +26,10 @@ LL | f(1, 2); LL | f(1, 2); | ^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/borrowck-unboxed-closures.rs:10:8 +help: consider further restricting this bound | -LL | fn c isize>(f: F) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn c isize + Copy>(f: F) { + | ^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/move-error-in-promoted-2.rs b/src/test/ui/borrowck/move-error-in-promoted-2.rs new file mode 100644 index 0000000000000..13da34f3922c2 --- /dev/null +++ b/src/test/ui/borrowck/move-error-in-promoted-2.rs @@ -0,0 +1,10 @@ +// Regression test for #70934 + +struct S; + +fn foo() { + &([S][0],); + //~^ ERROR cannot move out of type `[S; 1]` +} + +fn main() {} diff --git a/src/test/ui/borrowck/move-error-in-promoted-2.stderr b/src/test/ui/borrowck/move-error-in-promoted-2.stderr new file mode 100644 index 0000000000000..38dba94bdd41b --- /dev/null +++ b/src/test/ui/borrowck/move-error-in-promoted-2.stderr @@ -0,0 +1,12 @@ +error[E0508]: cannot move out of type `[S; 1]`, a non-copy array + --> $DIR/move-error-in-promoted-2.rs:6:7 + | +LL | &([S][0],); + | ^^^^^^ + | | + | cannot move out of here + | move occurs because value has type `S`, which does not implement the `Copy` trait + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/borrowck/move-error-in-promoted.rs b/src/test/ui/borrowck/move-error-in-promoted.rs new file mode 100644 index 0000000000000..b94db64513123 --- /dev/null +++ b/src/test/ui/borrowck/move-error-in-promoted.rs @@ -0,0 +1,17 @@ +// Regression test for #70934 + +fn f() { + const C: [S2; 1] = [S2]; + let _ = S1(C[0]).clone(); + //~^ ERROR cannot move out of type `[S2; 1]` +} + +#[derive(Clone)] +struct S1(S2); + +#[derive(Clone)] +struct S2; + +fn main() { + f(); +} diff --git a/src/test/ui/borrowck/move-error-in-promoted.stderr b/src/test/ui/borrowck/move-error-in-promoted.stderr new file mode 100644 index 0000000000000..a4432e38da0e4 --- /dev/null +++ b/src/test/ui/borrowck/move-error-in-promoted.stderr @@ -0,0 +1,12 @@ +error[E0508]: cannot move out of type `[S2; 1]`, a non-copy array + --> $DIR/move-error-in-promoted.rs:5:16 + | +LL | let _ = S1(C[0]).clone(); + | ^^^^ + | | + | cannot move out of here + | move occurs because value has type `S2`, which does not implement the `Copy` trait + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/borrowck/mut-borrow-in-loop.stderr b/src/test/ui/borrowck/mut-borrow-in-loop.stderr index daee1a0d5cff9..260b9673d74ba 100644 --- a/src/test/ui/borrowck/mut-borrow-in-loop.stderr +++ b/src/test/ui/borrowck/mut-borrow-in-loop.stderr @@ -42,6 +42,6 @@ LL | (self.func)(arg) | | mutable borrow starts here in previous iteration of loop | argument requires that `*arg` is borrowed for `'a` -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr index 88e9ced03ddde..e4fceb197be59 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr @@ -35,6 +35,6 @@ LL | v.push(shared.len()); = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future = note: for more information, see issue #59159 -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr index 88e9ced03ddde..e4fceb197be59 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr @@ -35,6 +35,6 @@ LL | v.push(shared.len()); = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future = note: for more information, see issue #59159 -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr index 85779e53437c7..03f49d2d92fa1 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr @@ -36,5 +36,5 @@ LL | #![deny(mutable_borrow_reservation_conflict)] = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future = note: for more information, see issue #59159 -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/bound-suggestions.fixed b/src/test/ui/bound-suggestions.fixed index c77421c97e7c0..9c98200db5134 100644 --- a/src/test/ui/bound-suggestions.fixed +++ b/src/test/ui/bound-suggestions.fixed @@ -13,7 +13,7 @@ fn test_no_bounds(t: T) { } #[allow(dead_code)] -fn test_one_bound(t: T) { +fn test_one_bound(t: T) { println!("{:?}", t); //~^ ERROR doesn't implement } @@ -25,7 +25,7 @@ fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt: } #[allow(dead_code)] -fn test_one_bound_where(x: X) where X: std::fmt::Debug + Sized { +fn test_one_bound_where(x: X) where X: Sized + std::fmt::Debug { println!("{:?}", x); //~^ ERROR doesn't implement } diff --git a/src/test/ui/bound-suggestions.stderr b/src/test/ui/bound-suggestions.stderr index 1e85c2bf36e46..b9bc503f5301a 100644 --- a/src/test/ui/bound-suggestions.stderr +++ b/src/test/ui/bound-suggestions.stderr @@ -5,13 +5,12 @@ LL | println!("{:?}", t); | ^ `impl Sized` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | = help: the trait `std::fmt::Debug` is not implemented for `impl Sized` -help: consider further restricting this bound with `+ std::fmt::Debug` - --> $DIR/bound-suggestions.rs:4:17 - | -LL | fn test_impl(t: impl Sized) { - | ^^^^^^^^^^ = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting this bound + | +LL | fn test_impl(t: impl Sized + std::fmt::Debug) { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `T` doesn't implement `std::fmt::Debug` --> $DIR/bound-suggestions.rs:11:22 @@ -20,13 +19,12 @@ LL | println!("{:?}", t); | ^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | = help: the trait `std::fmt::Debug` is not implemented for `T` -help: consider restricting this type parameter with `T: std::fmt::Debug` - --> $DIR/bound-suggestions.rs:10:19 - | -LL | fn test_no_bounds(t: T) { - | ^ = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider restricting type parameter `T` + | +LL | fn test_no_bounds(t: T) { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `T` doesn't implement `std::fmt::Debug` --> $DIR/bound-suggestions.rs:17:22 @@ -35,13 +33,12 @@ LL | println!("{:?}", t); | ^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | = help: the trait `std::fmt::Debug` is not implemented for `T` -help: consider further restricting this bound with `+ std::fmt::Debug` - --> $DIR/bound-suggestions.rs:16:22 - | -LL | fn test_one_bound(t: T) { - | ^^^^^ = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting this bound + | +LL | fn test_one_bound(t: T) { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `Y` doesn't implement `std::fmt::Debug` --> $DIR/bound-suggestions.rs:23:30 @@ -50,13 +47,12 @@ LL | println!("{:?} {:?}", x, y); | ^ `Y` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | = help: the trait `std::fmt::Debug` is not implemented for `Y` -help: consider restricting this type parameter with `where Y: std::fmt::Debug` - --> $DIR/bound-suggestions.rs:22:28 - | -LL | fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug { - | ^ = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting type parameter `Y` + | +LL | fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ error[E0277]: `X` doesn't implement `std::fmt::Debug` --> $DIR/bound-suggestions.rs:29:22 @@ -65,13 +61,12 @@ LL | println!("{:?}", x); | ^ `X` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | = help: the trait `std::fmt::Debug` is not implemented for `X` -help: consider further restricting this bound with `+ std::fmt::Debug` - --> $DIR/bound-suggestions.rs:28:40 - | -LL | fn test_one_bound_where(x: X) where X: Sized { - | ^^^^^^^^ = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting this bound + | +LL | fn test_one_bound_where(x: X) where X: Sized + std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `X` doesn't implement `std::fmt::Debug` --> $DIR/bound-suggestions.rs:35:22 @@ -80,13 +75,12 @@ LL | println!("{:?}", x); | ^ `X` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | = help: the trait `std::fmt::Debug` is not implemented for `X` -help: consider further restricting this type parameter with `where X: std::fmt::Debug` - --> $DIR/bound-suggestions.rs:34:27 - | -LL | fn test_many_bounds_where(x: X) where X: Sized, X: Sized { - | ^ = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting type parameter `X` + | +LL | fn test_many_bounds_where(x: X) where X: Sized, X: Sized, X: std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/box-into-boxed-slice-fail.rs b/src/test/ui/box-into-boxed-slice-fail.rs new file mode 100644 index 0000000000000..5f8a3fd9d6a54 --- /dev/null +++ b/src/test/ui/box-into-boxed-slice-fail.rs @@ -0,0 +1,15 @@ +// ignore-tidy-linelength +#![feature(box_into_boxed_slice)] + +use std::boxed::Box; +use std::fmt::Debug; +fn main() { + let boxed_slice = Box::new([1,2,3]) as Box<[u8]>; + let _ = Box::into_boxed_slice(boxed_slice); + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time + //~^^ ERROR the size for values of type `[u8]` cannot be known at compilation time + let boxed_trait: Box = Box::new(5u8); + let _ = Box::into_boxed_slice(boxed_trait); + //~^ ERROR the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time + //~^^ ERROR the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time +} diff --git a/src/test/ui/box-into-boxed-slice-fail.stderr b/src/test/ui/box-into-boxed-slice-fail.stderr new file mode 100644 index 0000000000000..dfc4999958a57 --- /dev/null +++ b/src/test/ui/box-into-boxed-slice-fail.stderr @@ -0,0 +1,43 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/box-into-boxed-slice-fail.rs:8:35 + | +LL | let _ = Box::into_boxed_slice(boxed_slice); + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = note: to learn more, visit + = note: required by `std::boxed::Box::::into_boxed_slice` + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/box-into-boxed-slice-fail.rs:8:13 + | +LL | let _ = Box::into_boxed_slice(boxed_slice); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = note: to learn more, visit + = note: slice and array elements must have `Sized` type + +error[E0277]: the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time + --> $DIR/box-into-boxed-slice-fail.rs:12:35 + | +LL | let _ = Box::into_boxed_slice(boxed_trait); + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `dyn std::fmt::Debug` + = note: to learn more, visit + = note: required by `std::boxed::Box::::into_boxed_slice` + +error[E0277]: the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time + --> $DIR/box-into-boxed-slice-fail.rs:12:13 + | +LL | let _ = Box::into_boxed_slice(boxed_trait); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `dyn std::fmt::Debug` + = note: to learn more, visit + = note: slice and array elements must have `Sized` type + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/box-into-boxed-slice.rs b/src/test/ui/box-into-boxed-slice.rs new file mode 100644 index 0000000000000..61b3d91525347 --- /dev/null +++ b/src/test/ui/box-into-boxed-slice.rs @@ -0,0 +1,11 @@ +// run-pass +#![feature(box_into_boxed_slice)] + +use std::boxed::Box; +fn main() { + assert_eq!(Box::into_boxed_slice(Box::new(5u8)), Box::new([5u8]) as Box<[u8]>); + assert_eq!(Box::into_boxed_slice(Box::new([25u8])), Box::new([[25u8]]) as Box<[[u8; 1]]>); + let a: Box<[Box<[u8; 1]>]> = Box::into_boxed_slice(Box::new(Box::new([5u8]))); + let b: Box<[Box<[u8; 1]>]> = Box::new([Box::new([5u8])]); + assert_eq!(a, b); +} diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr index a38705c834a37..4e7b513629d05 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr @@ -1,30 +1,34 @@ error[E0277]: `T` cannot be sent between threads safely --> $DIR/builtin-superkinds-double-superkind.rs:6:24 | +LL | trait Foo : Send+Sync { } + | ---- required by this bound in `Foo` +LL | LL | impl Foo for (T,) { } | ^^^ `T` cannot be sent between threads safely | = help: within `(T,)`, the trait `std::marker::Send` is not implemented for `T` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/builtin-superkinds-double-superkind.rs:6:10 - | -LL | impl Foo for (T,) { } - | ^^^^^^^^^^^^ = note: required because it appears within the type `(T,)` +help: consider further restricting this bound + | +LL | impl Foo for (T,) { } + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be shared between threads safely --> $DIR/builtin-superkinds-double-superkind.rs:9:16 | +LL | trait Foo : Send+Sync { } + | ---- required by this bound in `Foo` +... LL | impl Foo for (T,T) { } | ^^^ `T` cannot be shared between threads safely | = help: within `(T, T)`, the trait `std::marker::Sync` is not implemented for `T` -help: consider further restricting this bound with `+ std::marker::Sync` - --> $DIR/builtin-superkinds-double-superkind.rs:9:10 - | -LL | impl Foo for (T,T) { } - | ^^^^ = note: required because it appears within the type `(T, T)` +help: consider further restricting this bound + | +LL | impl Foo for (T,T) { } + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr index f379d97bd76c8..3fb1af3a67cc2 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr @@ -3,14 +3,18 @@ error[E0277]: `T` cannot be sent between threads safely | LL | impl RequiresRequiresShareAndSend for X { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `T` cannot be sent between threads safely + | + ::: $DIR/auxiliary/trait_superkinds_in_metadata.rs:7:58 | - = help: within `X`, the trait `std::marker::Send` is not implemented for `T` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/builtin-superkinds-in-metadata.rs:13:9 +LL | pub trait RequiresRequiresShareAndSend : RequiresShare + Send { } + | ---- required by this bound in `trait_superkinds_in_metadata::RequiresRequiresShareAndSend` | -LL | impl RequiresRequiresShareAndSend for X { } - | ^^^^^^^^^^^^ + = help: within `X`, the trait `std::marker::Send` is not implemented for `T` = note: required because it appears within the type `X` +help: consider further restricting this bound + | +LL | impl RequiresRequiresShareAndSend for X { } + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr index a0ff64077c4b3..592cc3b1c4ec1 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr @@ -1,6 +1,9 @@ error[E0277]: `std::rc::Rc` cannot be sent between threads safely --> $DIR/builtin-superkinds-simple.rs:6:6 | +LL | trait Foo : Send { } + | ---- required by this bound in `Foo` +LL | LL | impl Foo for std::rc::Rc { } | ^^^ `std::rc::Rc` cannot be sent between threads safely | diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr index 996f39bfb665c..9c5073a1e49d7 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr @@ -1,15 +1,17 @@ error[E0277]: `T` cannot be sent between threads safely --> $DIR/builtin-superkinds-typaram-not-send.rs:5:24 | +LL | trait Foo : Send { } + | ---- required by this bound in `Foo` +LL | LL | impl Foo for T { } | ^^^ `T` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `T` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/builtin-superkinds-typaram-not-send.rs:5:10 +help: consider further restricting this bound | -LL | impl Foo for T { } - | ^^^^^^^^^^^^ +LL | impl Foo for T { } + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/c-variadic/variadic-ffi-6.stderr b/src/test/ui/c-variadic/variadic-ffi-6.stderr index 882e7f89f2a0d..4626a4bc2dcff 100644 --- a/src/test/ui/c-variadic/variadic-ffi-6.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-6.stderr @@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier --> $DIR/variadic-ffi-6.rs:7:6 | LL | ) -> &usize { - | ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static` + | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | ) -> &'static usize { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/chalkify/basic.rs b/src/test/ui/chalkify/basic.rs new file mode 100644 index 0000000000000..dbd60fc8bb1ff --- /dev/null +++ b/src/test/ui/chalkify/basic.rs @@ -0,0 +1,12 @@ +// check-pass +// compile-flags: -Z chalk + +trait Foo {} + +struct Bar {} + +impl Foo for Bar {} + +fn main() -> () { + let _ = Bar {}; +} diff --git a/src/test/ui/chalkify/builtin-copy-clone.rs b/src/test/ui/chalkify/builtin-copy-clone.rs new file mode 100644 index 0000000000000..d403514b553b0 --- /dev/null +++ b/src/test/ui/chalkify/builtin-copy-clone.rs @@ -0,0 +1,44 @@ +// run-pass +// compile-flags: -Z chalk + +// Test that `Clone` is correctly implemented for builtin types. + +#[derive(Copy, Clone)] +struct S(i32); + +fn test_clone(arg: T) { + let _ = arg.clone(); +} + +fn test_copy(arg: T) { + let _ = arg; + let _ = arg; +} + +fn test_copy_clone(arg: T) { + test_copy(arg); + test_clone(arg); +} + +fn foo() { } + +fn main() { + test_copy_clone(foo); + let f: fn() = foo; + test_copy_clone(f); + // FIXME: add closures when they're considered WF + test_copy_clone([1; 56]); + test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)); + test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, true, 'a', 1.1)); + test_copy_clone(()); + test_copy_clone(((1, 1), (1, 1, 1), (1.1, 1, 1, 'a'), ())); + + let a = ( + (S(1), S(0)), + ( + (S(0), S(0), S(1)), + S(0) + ) + ); + test_copy_clone(a); +} diff --git a/src/test/ui/chalkify/chalk_initial_program.rs b/src/test/ui/chalkify/chalk_initial_program.rs new file mode 100644 index 0000000000000..df25bad622b3d --- /dev/null +++ b/src/test/ui/chalkify/chalk_initial_program.rs @@ -0,0 +1,16 @@ +// compile-flags: -Z chalk + +trait Foo { } + +impl Foo for i32 { } + +impl Foo for u32 { } + +fn gimme() { } + +// Note: this also tests that `std::process::Termination` is implemented for `()`. +fn main() { + gimme::(); + gimme::(); + gimme::(); //~ERROR the trait bound `f32: Foo` is not satisfied +} diff --git a/src/test/ui/chalkify/chalk_initial_program.stderr b/src/test/ui/chalkify/chalk_initial_program.stderr new file mode 100644 index 0000000000000..f2e13a6a46904 --- /dev/null +++ b/src/test/ui/chalkify/chalk_initial_program.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `f32: Foo` is not satisfied + --> $DIR/chalk_initial_program.rs:15:13 + | +LL | fn gimme() { } + | --- required by this bound in `gimme` +... +LL | gimme::(); + | ^^^ the trait `Foo` is not implemented for `f32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/chalkify/generic_impls.rs b/src/test/ui/chalkify/generic_impls.rs new file mode 100644 index 0000000000000..d70c6f8055ddf --- /dev/null +++ b/src/test/ui/chalkify/generic_impls.rs @@ -0,0 +1,18 @@ +// compile-flags: -Z chalk + +trait Foo { } + +impl Foo for (T, u32) { } + +fn gimme() { } + +fn foo() { + gimme::<(T, u32)>(); + gimme::<(Option, u32)>(); + gimme::<(Option, f32)>(); //~ ERROR +} + +fn main() { + gimme::<(i32, u32)>(); + gimme::<(i32, f32)>(); //~ ERROR +} diff --git a/src/test/ui/chalkify/generic_impls.stderr b/src/test/ui/chalkify/generic_impls.stderr new file mode 100644 index 0000000000000..4ac57a2f13fd1 --- /dev/null +++ b/src/test/ui/chalkify/generic_impls.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `(std::option::Option, f32): Foo` is not satisfied + --> $DIR/generic_impls.rs:12:13 + | +LL | fn gimme() { } + | --- required by this bound in `gimme` +... +LL | gimme::<(Option, f32)>(); + | ^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(std::option::Option, f32)` + | + = help: the following implementations were found: + <(T, u32) as Foo> + +error[E0277]: the trait bound `(i32, f32): Foo` is not satisfied + --> $DIR/generic_impls.rs:17:13 + | +LL | fn gimme() { } + | --- required by this bound in `gimme` +... +LL | gimme::<(i32, f32)>(); + | ^^^^^^^^^^ the trait `Foo` is not implemented for `(i32, f32)` + | + = help: the following implementations were found: + <(T, u32) as Foo> + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/chalkify/impl_wf.rs b/src/test/ui/chalkify/impl_wf.rs new file mode 100644 index 0000000000000..8aa876422924d --- /dev/null +++ b/src/test/ui/chalkify/impl_wf.rs @@ -0,0 +1,47 @@ +// compile-flags: -Z chalk + +trait Foo: Sized { } + +trait Bar { + type Item: Foo; +} + +impl Foo for i32 { } + +// FIXME(chalk): blocked on better handling of builtin traits for non-struct +// application types (or a workaround) +/* +impl Foo for str { } +//^ ERROR the size for values of type `str` cannot be known at compilation time +*/ + +// Implicit `T: Sized` bound. +impl Foo for Option { } + +impl Bar for () { + type Item = i32; +} + +impl Bar for Option { + type Item = Option; +} + +// FIXME(chalk): the ordering of these two errors differs between CI and local +// We need to figure out why its non-deterministic +/* +impl Bar for f32 { +//^ ERROR the trait bound `f32: Foo` is not satisfied + type Item = f32; + //^ ERROR the trait bound `f32: Foo` is not satisfied +} +*/ + +trait Baz where U: Foo { } + +impl Baz for i32 { } + +impl Baz for f32 { } +//~^ ERROR the trait bound `f32: Foo` is not satisfied + +fn main() { +} diff --git a/src/test/ui/chalkify/impl_wf.stderr b/src/test/ui/chalkify/impl_wf.stderr new file mode 100644 index 0000000000000..befd688741c80 --- /dev/null +++ b/src/test/ui/chalkify/impl_wf.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `f32: Foo` is not satisfied + --> $DIR/impl_wf.rs:43:6 + | +LL | trait Baz where U: Foo { } + | --- required by this bound in `Baz` +... +LL | impl Baz for f32 { } + | ^^^^^^^^ the trait `Foo` is not implemented for `f32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/chalkify/inherent_impl.rs b/src/test/ui/chalkify/inherent_impl.rs new file mode 100644 index 0000000000000..44e120c1eebba --- /dev/null +++ b/src/test/ui/chalkify/inherent_impl.rs @@ -0,0 +1,42 @@ +// run-pass +// compile-flags: -Z chalk + +trait Foo { } + +impl Foo for i32 { } + +struct S { + x: T, +} + +fn only_foo(_x: &T) { } + +impl S { + // Test that we have the correct environment inside an inherent method. + fn dummy_foo(&self) { + only_foo(&self.x) + } +} + +trait Bar { } +impl Bar for u32 { } + +fn only_bar() { } + +impl S { + // Test that the environment of `dummy_bar` adds up with the environment + // of the inherent impl. + fn dummy_bar(&self) { + only_foo(&self.x); + only_bar::(); + } +} + +fn main() { + let s = S { + x: 5, + }; + + s.dummy_foo(); + s.dummy_bar::(); +} diff --git a/src/test/ui/chalkify/inherent_impl_min.rs b/src/test/ui/chalkify/inherent_impl_min.rs new file mode 100644 index 0000000000000..774c46e401ca3 --- /dev/null +++ b/src/test/ui/chalkify/inherent_impl_min.rs @@ -0,0 +1,27 @@ +// run-pass +// compile-flags: -Z chalk + +trait Foo { } + +impl Foo for i32 { } + +struct S { + x: T, +} + +fn only_foo(_x: &T) { } + +impl S { + // Test that we have the correct environment inside an inherent method. + fn dummy_foo(&self) { + only_foo(&self.x) + } +} + +fn main() { + let s = S { + x: 5, + }; + + s.dummy_foo(); +} diff --git a/src/test/ui/chalkify/lower_env1.rs b/src/test/ui/chalkify/lower_env1.rs new file mode 100644 index 0000000000000..e3c7569592149 --- /dev/null +++ b/src/test/ui/chalkify/lower_env1.rs @@ -0,0 +1,14 @@ +// check-pass +// compile-flags: -Z chalk + +#![allow(dead_code)] + +trait Foo { } + +trait Bar where Self: Foo { } + +fn bar() { +} + +fn main() { +} diff --git a/src/test/ui/chalkify/lower_env2.rs b/src/test/ui/chalkify/lower_env2.rs new file mode 100644 index 0000000000000..b5432ce0e307b --- /dev/null +++ b/src/test/ui/chalkify/lower_env2.rs @@ -0,0 +1,16 @@ +// check-pass +// compile-flags: -Z chalk + +#![allow(dead_code)] + +trait Foo { } + +struct S<'a, T: ?Sized> where T: Foo { + data: &'a T, +} + +fn bar(_x: S<'_, T>) { // note that we have an implicit `T: Sized` bound +} + +fn main() { +} diff --git a/src/test/ui/chalkify/lower_env3.rs b/src/test/ui/chalkify/lower_env3.rs new file mode 100644 index 0000000000000..673f08d78abd0 --- /dev/null +++ b/src/test/ui/chalkify/lower_env3.rs @@ -0,0 +1,16 @@ +// check-pass +// compile-flags: -Z chalk + +#![allow(dead_code)] + +trait Foo { + fn foo(&self); +} + +impl Foo for T where T: Clone { + fn foo(&self) { + } +} + +fn main() { +} diff --git a/src/test/ui/chalkify/lower_impl.rs b/src/test/ui/chalkify/lower_impl.rs new file mode 100644 index 0000000000000..f586cf083915d --- /dev/null +++ b/src/test/ui/chalkify/lower_impl.rs @@ -0,0 +1,17 @@ +// check-pass +// compile-flags: -Z chalk + +trait Foo { } + +impl Foo for T where T: Iterator { } + +trait Bar { + type Assoc; +} + +impl Bar for T where T: Iterator { + type Assoc = Vec; +} + +fn main() { +} diff --git a/src/test/ui/chalkify/lower_struct.rs b/src/test/ui/chalkify/lower_struct.rs new file mode 100644 index 0000000000000..94a0716d38354 --- /dev/null +++ b/src/test/ui/chalkify/lower_struct.rs @@ -0,0 +1,8 @@ +// check-pass +// compile-flags: -Z chalk + +struct Foo<'a, T> where Box: Clone { + _x: std::marker::PhantomData<&'a T>, +} + +fn main() { } diff --git a/src/test/ui/chalkify/lower_trait.rs b/src/test/ui/chalkify/lower_trait.rs new file mode 100644 index 0000000000000..d8f6180ceb315 --- /dev/null +++ b/src/test/ui/chalkify/lower_trait.rs @@ -0,0 +1,11 @@ +// check-pass +// compile-flags: -Z chalk + +trait Bar { } + +trait Foo { + type Assoc: Bar + ?Sized; +} + +fn main() { +} diff --git a/src/test/ui/chalkify/lower_trait_higher_rank.rs b/src/test/ui/chalkify/lower_trait_higher_rank.rs new file mode 100644 index 0000000000000..a48979491a10d --- /dev/null +++ b/src/test/ui/chalkify/lower_trait_higher_rank.rs @@ -0,0 +1,9 @@ +// check-pass +// compile-flags: -Z chalk + +trait Foo where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8 +{ +} + +fn main() { +} diff --git a/src/test/ui/chalkify/lower_trait_where_clause.rs b/src/test/ui/chalkify/lower_trait_where_clause.rs new file mode 100644 index 0000000000000..19cff8db7cb4a --- /dev/null +++ b/src/test/ui/chalkify/lower_trait_where_clause.rs @@ -0,0 +1,16 @@ +// check-pass +// compile-flags: -Z chalk + +use std::borrow::Borrow; + +trait Foo<'a, 'b, T, U> +where + T: Borrow + ?Sized, + U: ?Sized + 'b, + 'a: 'b, + Box:, // NOTE(#53696) this checks an empty list of bounds. +{ +} + +fn main() { +} diff --git a/src/test/ui/chalkify/println.rs b/src/test/ui/chalkify/println.rs new file mode 100644 index 0000000000000..cf36aef8afaf3 --- /dev/null +++ b/src/test/ui/chalkify/println.rs @@ -0,0 +1,7 @@ +// check-pass +// compile-flags: -Z chalk + +fn main() { + // FIXME(chalk): Require `RegionOutlives`/`TypeOutlives`/`Subtype` support + //println!("hello"); +} diff --git a/src/test/ui/chalkify/projection.rs b/src/test/ui/chalkify/projection.rs new file mode 100644 index 0000000000000..d6a8dd7a4a203 --- /dev/null +++ b/src/test/ui/chalkify/projection.rs @@ -0,0 +1,25 @@ +// run-pass +// compile-flags: -Z chalk + +trait Foo { } + +trait Bar { + type Item: Foo; +} + +impl Foo for i32 { } +impl Bar for i32 { + type Item = i32; +} + +fn only_foo() { } + +fn only_bar() { + // `T` implements `Bar` hence `::Item` must also implement `Bar` + only_foo::() +} + +fn main() { + only_bar::(); + only_foo::<::Item>(); +} diff --git a/src/test/ui/chalkify/recursive_where_clause_on_type.rs b/src/test/ui/chalkify/recursive_where_clause_on_type.rs new file mode 100644 index 0000000000000..6ee13f5e7a104 --- /dev/null +++ b/src/test/ui/chalkify/recursive_where_clause_on_type.rs @@ -0,0 +1,35 @@ +// FIXME(chalk): should fail, see comments +// check-pass +// compile-flags: -Z chalk + +#![feature(trivial_bounds)] + +trait Bar { + fn foo(); +} +trait Foo: Bar { } + +struct S where S: Foo; +//~^ WARN Trait bound S: Foo does not depend on any type or lifetime parameters + +impl Foo for S { +} + +fn bar() { + T::foo(); +} + +fn foo() { + bar::() +} + +fn main() { + // For some reason, the error is duplicated... + + // FIXME(chalk): this order of this duplicate error seems non-determistic + // and causes test to fail + /* + foo::() // ERROR the type `S` is not well-formed (chalk) + //^ ERROR the type `S` is not well-formed (chalk) + */ +} diff --git a/src/test/ui/chalkify/recursive_where_clause_on_type.stderr b/src/test/ui/chalkify/recursive_where_clause_on_type.stderr new file mode 100644 index 0000000000000..a5b7ef7fdb2e3 --- /dev/null +++ b/src/test/ui/chalkify/recursive_where_clause_on_type.stderr @@ -0,0 +1,10 @@ +warning: Trait bound S: Foo does not depend on any type or lifetime parameters + --> $DIR/recursive_where_clause_on_type.rs:12:19 + | +LL | struct S where S: Foo; + | ^^^ + | + = note: `#[warn(trivial_bounds)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/chalkify/super_trait.rs b/src/test/ui/chalkify/super_trait.rs new file mode 100644 index 0000000000000..eeff9fd9b80a3 --- /dev/null +++ b/src/test/ui/chalkify/super_trait.rs @@ -0,0 +1,19 @@ +// run-pass +// compile-flags: -Z chalk + +trait Foo { } +trait Bar: Foo { } + +impl Foo for i32 { } +impl Bar for i32 { } + +fn only_foo() { } + +fn only_bar() { + // `T` implements `Bar` hence `T` must also implement `Foo` + only_foo::() +} + +fn main() { + only_bar::() +} diff --git a/src/test/ui/chalkify/trait_implied_bound.rs b/src/test/ui/chalkify/trait_implied_bound.rs new file mode 100644 index 0000000000000..8a2e1cf599008 --- /dev/null +++ b/src/test/ui/chalkify/trait_implied_bound.rs @@ -0,0 +1,18 @@ +// run-pass +// compile-flags: -Z chalk + +trait Foo { } +trait Bar where U: Foo { } + +impl Foo for i32 { } +impl Bar for i32 { } + +fn only_foo() { } + +fn only_bar>() { + only_foo::() +} + +fn main() { + only_bar::() +} diff --git a/src/test/ui/chalkify/type_implied_bound.rs b/src/test/ui/chalkify/type_implied_bound.rs new file mode 100644 index 0000000000000..8673f5319bdf0 --- /dev/null +++ b/src/test/ui/chalkify/type_implied_bound.rs @@ -0,0 +1,29 @@ +// run-pass +// compile-flags: -Z chalk + +trait Eq { } +trait Hash: Eq { } + +impl Eq for i32 { } +impl Hash for i32 { } + +struct Set { + _x: T, +} + +fn only_eq() { } + +fn take_a_set(_: &Set) { + // `Set` is an input type of `take_a_set`, hence we know that + // `T` must implement `Hash`, and we know in turn that `T` must + // implement `Eq`. + only_eq::() +} + +fn main() { + let set = Set { + _x: 5, + }; + + take_a_set(&set); +} diff --git a/src/test/ui/chalkify/type_inference.rs b/src/test/ui/chalkify/type_inference.rs new file mode 100644 index 0000000000000..5175c5d062a6e --- /dev/null +++ b/src/test/ui/chalkify/type_inference.rs @@ -0,0 +1,28 @@ +// compile-flags: -Z chalk + +trait Foo { } +impl Foo for i32 { } + +trait Bar { } +impl Bar for i32 { } +impl Bar for u32 { } + +fn only_foo(_x: T) { } + +fn only_bar(_x: T) { } + +fn main() { + let x = 5.0; + + // The only type which implements `Foo` is `i32`, so the chalk trait solver + // is expecting a variable of type `i32`. This behavior differs from the + // old-style trait solver. I guess this will change, that's why I'm + // adding that test. + // FIXME(chalk): partially blocked on float/int special casing + only_foo(x); //~ ERROR the trait bound `f64: Foo` is not satisfied + + // Here we have two solutions so we get back the behavior of the old-style + // trait solver. + // FIXME(chalk): blocked on float/int special casing + //only_bar(x); // ERROR the trait bound `{float}: Bar` is not satisfied +} diff --git a/src/test/ui/chalkify/type_inference.stderr b/src/test/ui/chalkify/type_inference.stderr new file mode 100644 index 0000000000000..ee9e67c6c7884 --- /dev/null +++ b/src/test/ui/chalkify/type_inference.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `f64: Foo` is not satisfied + --> $DIR/type_inference.rs:22:5 + | +LL | fn only_foo(_x: T) { } + | --- required by this bound in `only_foo` +... +LL | only_foo(x); + | ^^^^^^^^ the trait `Foo` is not implemented for `f64` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/chalkify/type_wf.rs b/src/test/ui/chalkify/type_wf.rs new file mode 100644 index 0000000000000..396baf814a0b0 --- /dev/null +++ b/src/test/ui/chalkify/type_wf.rs @@ -0,0 +1,32 @@ +// FIXME(chalk): should have an error, see below +// check-pass +// compile-flags: -Z chalk + +trait Foo { } + +struct S { + x: T, +} + +impl Foo for i32 { } +impl Foo for Option { } + +fn main() { + let s = S { + x: 5, + }; + + // FIXME(chalk): blocked on float/int special handling. Needs to know that {float}: !i32 + /* + let s = S { // ERROR the trait bound `{float}: Foo` is not satisfied + x: 5.0, + }; + */ + + // FIXME(chalk): blocked on float/int special handling. Needs to know that {float}: Sized + /* + let s = S { + x: Some(5.0), + }; + */ +} diff --git a/src/test/ui/char_unicode.rs b/src/test/ui/char_unicode.rs index 93e5300e36fe9..65dda47066f4e 100644 --- a/src/test/ui/char_unicode.rs +++ b/src/test/ui/char_unicode.rs @@ -1,12 +1,10 @@ // run-pass -#![feature(unicode_version)] - -/// Tests access to the internal Unicode Version type and value. +/// Tests access to the Unicode version constant. pub fn main() { check(std::char::UNICODE_VERSION); } -pub fn check(unicode_version: std::char::UnicodeVersion) { - assert!(unicode_version.major >= 10); +pub fn check(unicode_version: (u8, u8, u8)) { + assert!(unicode_version.0 >= 10); } diff --git a/src/test/ui/check-static-values-constraints.stderr b/src/test/ui/check-static-values-constraints.stderr index 7d7ecbd1a26a5..6b5a739899cac 100644 --- a/src/test/ui/check-static-values-constraints.stderr +++ b/src/test/ui/check-static-values-constraints.stderr @@ -18,6 +18,8 @@ error[E0019]: static contains unimplemented expression type | LL | static STATIC11: Box = box MyOwned; | ^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants --> $DIR/check-static-values-constraints.rs:90:32 @@ -36,6 +38,8 @@ error[E0019]: static contains unimplemented expression type | LL | box MyOwned, | ^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0010]: allocations are not allowed in statics --> $DIR/check-static-values-constraints.rs:97:5 @@ -48,6 +52,8 @@ error[E0019]: static contains unimplemented expression type | LL | box MyOwned, | ^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0010]: allocations are not allowed in statics --> $DIR/check-static-values-constraints.rs:102:6 @@ -60,6 +66,8 @@ error[E0019]: static contains unimplemented expression type | LL | &box MyOwned, | ^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0010]: allocations are not allowed in statics --> $DIR/check-static-values-constraints.rs:104:6 @@ -72,6 +80,8 @@ error[E0019]: static contains unimplemented expression type | LL | &box MyOwned, | ^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0010]: allocations are not allowed in statics --> $DIR/check-static-values-constraints.rs:111:5 @@ -84,6 +94,8 @@ error[E0019]: static contains unimplemented expression type | LL | box 3; | ^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0507]: cannot move out of static item `x` --> $DIR/check-static-values-constraints.rs:116:45 @@ -105,6 +117,8 @@ error[E0019]: static contains unimplemented expression type | LL | let y = { static x: Box = box 3; x }; | ^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to 17 previous errors diff --git a/src/test/ui/class-missing-self.stderr b/src/test/ui/class-missing-self.stderr index 681d0ffea8be8..d501200d73cec 100644 --- a/src/test/ui/class-missing-self.stderr +++ b/src/test/ui/class-missing-self.stderr @@ -10,7 +10,7 @@ error[E0425]: cannot find function `sleep` in this scope LL | sleep(); | ^^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this function | LL | use std::thread::sleep; | diff --git a/src/test/ui/cleanup-rvalue-scopes.rs b/src/test/ui/cleanup-rvalue-scopes.rs index f51f13abf792f..c5dd87c0f5a1f 100644 --- a/src/test/ui/cleanup-rvalue-scopes.rs +++ b/src/test/ui/cleanup-rvalue-scopes.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![allow(non_snake_case)] #![allow(unused_variables)] // Test that destructors for rvalue temporaries run either at end of diff --git a/src/test/ui/closure-expected-type/expect-fn-supply-fn.nll.stderr b/src/test/ui/closure-expected-type/expect-fn-supply-fn.nll.stderr index 7141c047d7f53..97fdb76dd11c7 100644 --- a/src/test/ui/closure-expected-type/expect-fn-supply-fn.nll.stderr +++ b/src/test/ui/closure-expected-type/expect-fn-supply-fn.nll.stderr @@ -2,7 +2,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/expect-fn-supply-fn.rs:30:5 | LL | fn with_closure_expecting_fn_with_free_region(_: F) - | ------------------------------------------ + | ------------------------------------------ required by a bound in this LL | where F: for<'a> FnOnce(fn(&'a u32), &i32) | ------------------------- required by this bound in `with_closure_expecting_fn_with_free_region` ... @@ -15,7 +15,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/expect-fn-supply-fn.rs:37:5 | LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- + | ------------------------------------------- required by a bound in this LL | where F: FnOnce(fn(&u32), &i32) | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` ... @@ -28,7 +28,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/expect-fn-supply-fn.rs:46:5 | LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- + | ------------------------------------------- required by a bound in this LL | where F: FnOnce(fn(&u32), &i32) | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` ... diff --git a/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr b/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr index 0033395846815..fae41c4114abc 100644 --- a/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr +++ b/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr @@ -40,7 +40,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/expect-fn-supply-fn.rs:30:5 | LL | fn with_closure_expecting_fn_with_free_region(_: F) - | ------------------------------------------ + | ------------------------------------------ required by a bound in this LL | where F: for<'a> FnOnce(fn(&'a u32), &i32) | ------------------------- required by this bound in `with_closure_expecting_fn_with_free_region` ... @@ -53,7 +53,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/expect-fn-supply-fn.rs:37:5 | LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- + | ------------------------------------------- required by a bound in this LL | where F: FnOnce(fn(&u32), &i32) | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` ... @@ -66,7 +66,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/expect-fn-supply-fn.rs:46:5 | LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- + | ------------------------------------------- required by a bound in this LL | where F: FnOnce(fn(&u32), &i32) | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` ... diff --git a/src/test/ui/closure-expected-type/expect-infer-var-appearing-twice.stderr b/src/test/ui/closure-expected-type/expect-infer-var-appearing-twice.stderr index 1c6564ee426e5..93b42a5a305f2 100644 --- a/src/test/ui/closure-expected-type/expect-infer-var-appearing-twice.stderr +++ b/src/test/ui/closure-expected-type/expect-infer-var-appearing-twice.stderr @@ -2,7 +2,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/expect-infer-var-appearing-twice.rs:14:5 | LL | fn with_closure(_: F) - | ------------ + | ------------ required by a bound in this LL | where F: FnOnce(A, A) | ------------ required by this bound in `with_closure` ... diff --git a/src/test/ui/closures/closure-array-break-length.rs b/src/test/ui/closures/closure-array-break-length.rs index f3567db1fac9c..fda590fda022c 100644 --- a/src/test/ui/closures/closure-array-break-length.rs +++ b/src/test/ui/closures/closure-array-break-length.rs @@ -2,8 +2,6 @@ fn main() { |_: [_; continue]| {}; //~ ERROR: `continue` outside of a loop while |_: [_; continue]| {} {} //~ ERROR: `continue` outside of a loop - //~^ ERROR mismatched types while |_: [_; break]| {} {} //~ ERROR: `break` outside of a loop - //~^ ERROR mismatched types } diff --git a/src/test/ui/closures/closure-array-break-length.stderr b/src/test/ui/closures/closure-array-break-length.stderr index f6991a23f4d4d..2b8ab9bfc4414 100644 --- a/src/test/ui/closures/closure-array-break-length.stderr +++ b/src/test/ui/closures/closure-array-break-length.stderr @@ -11,30 +11,11 @@ LL | while |_: [_; continue]| {} {} | ^^^^^^^^ cannot `continue` outside of a loop error[E0268]: `break` outside of a loop - --> $DIR/closure-array-break-length.rs:7:19 + --> $DIR/closure-array-break-length.rs:6:19 | LL | while |_: [_; break]| {} {} | ^^^^^ cannot `break` outside of a loop -error[E0308]: mismatched types - --> $DIR/closure-array-break-length.rs:4:11 - | -LL | while |_: [_; continue]| {} {} - | ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found closure - | - = note: expected type `bool` - found closure `[closure@$DIR/closure-array-break-length.rs:4:11: 4:32]` - -error[E0308]: mismatched types - --> $DIR/closure-array-break-length.rs:7:11 - | -LL | while |_: [_; break]| {} {} - | ^^^^^^^^^^^^^^^^^^ expected `bool`, found closure - | - = note: expected type `bool` - found closure `[closure@$DIR/closure-array-break-length.rs:7:11: 7:29]` - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0268, E0308. -For more information about an error, try `rustc --explain E0268`. +For more information about this error, try `rustc --explain E0268`. diff --git a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr index b4135af7d7755..ffd70fac6b19b 100644 --- a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr +++ b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr @@ -2,17 +2,16 @@ error[E0277]: `F` cannot be sent between threads safely --> $DIR/closure-bounds-cant-promote-superkind-in-struct.rs:5:22 | LL | struct X where F: FnOnce() + 'static + Send { - | ---------------------------------------------- required by `X` + | ---- required by this bound in `X` ... LL | fn foo(blk: F) -> X where F: FnOnce() + 'static { | ^^^^ `F` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `F` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/closure-bounds-cant-promote-superkind-in-struct.rs:5:33 +help: consider further restricting this bound | -LL | fn foo(blk: F) -> X where F: FnOnce() + 'static { - | ^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo(blk: F) -> X where F: FnOnce() + 'static + std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/closures/closure-bounds-subtype.stderr b/src/test/ui/closures/closure-bounds-subtype.stderr index 47504de814dbe..691864c9e1d45 100644 --- a/src/test/ui/closures/closure-bounds-subtype.stderr +++ b/src/test/ui/closures/closure-bounds-subtype.stderr @@ -2,17 +2,16 @@ error[E0277]: `F` cannot be shared between threads safely --> $DIR/closure-bounds-subtype.rs:13:22 | LL | fn take_const_owned(_: F) where F: FnOnce() + Sync + Send { - | ---------------- ---- required by this bound in `take_const_owned` + | ---- required by this bound in `take_const_owned` ... LL | take_const_owned(f); | ^ `F` cannot be shared between threads safely | = help: the trait `std::marker::Sync` is not implemented for `F` -help: consider further restricting this bound with `+ std::marker::Sync` - --> $DIR/closure-bounds-subtype.rs:11:30 +help: consider further restricting this bound | -LL | fn give_owned(f: F) where F: FnOnce() + Send { - | ^^^^^^^^^^^^^^^^^^ +LL | fn give_owned(f: F) where F: FnOnce() + Send + std::marker::Sync { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.polonius.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.polonius.stderr index 2a7461fb469b2..df60416709f13 100644 --- a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.polonius.stderr +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.polonius.stderr @@ -2,7 +2,7 @@ error[E0521]: borrowed data escapes outside of closure --> $DIR/expect-region-supply-region.rs:18:9 | LL | let mut f: Option<&u32> = None; - | ----- `f` is declared here, outside of the closure body + | ----- `f` declared here, outside of the closure body LL | closure_expecting_bound(|x| { | - `x` is a reference that is only valid in the closure body LL | f = Some(x); @@ -12,7 +12,7 @@ error[E0521]: borrowed data escapes outside of closure --> $DIR/expect-region-supply-region.rs:28:9 | LL | let mut f: Option<&u32> = None; - | ----- `f` is declared here, outside of the closure body + | ----- `f` declared here, outside of the closure body LL | closure_expecting_bound(|x: &u32| { | - `x` is a reference that is only valid in the closure body LL | f = Some(x); @@ -33,7 +33,7 @@ error[E0521]: borrowed data escapes outside of closure --> $DIR/expect-region-supply-region.rs:42:9 | LL | let mut f: Option<&u32> = None; - | ----- `f` is declared here, outside of the closure body + | ----- `f` declared here, outside of the closure body ... LL | closure_expecting_bound(|x: &'x u32| { | - `x` is a reference that is only valid in the closure body diff --git a/src/test/ui/closures/closure-move-sync.rs b/src/test/ui/closures/closure-move-sync.rs index 87388247fbfda..580cd1af4f303 100644 --- a/src/test/ui/closures/closure-move-sync.rs +++ b/src/test/ui/closures/closure-move-sync.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::thread; use std::sync::mpsc::channel; diff --git a/src/test/ui/closures/closure-move-sync.stderr b/src/test/ui/closures/closure-move-sync.stderr index 2187823d7582a..a1fc427bc1723 100644 --- a/src/test/ui/closures/closure-move-sync.stderr +++ b/src/test/ui/closures/closure-move-sync.stderr @@ -1,5 +1,5 @@ error[E0277]: `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely - --> $DIR/closure-move-sync.rs:10:13 + --> $DIR/closure-move-sync.rs:6:13 | LL | let t = thread::spawn(|| { | ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely @@ -11,10 +11,10 @@ LL | F: Send + 'static, | = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Receiver<()>` = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Receiver<()>` - = note: required because it appears within the type `[closure@$DIR/closure-move-sync.rs:10:27: 13:6 recv:&std::sync::mpsc::Receiver<()>]` + = note: required because it appears within the type `[closure@$DIR/closure-move-sync.rs:6:27: 9:6 recv:&std::sync::mpsc::Receiver<()>]` error[E0277]: `std::sync::mpsc::Sender<()>` cannot be shared between threads safely - --> $DIR/closure-move-sync.rs:22:5 + --> $DIR/closure-move-sync.rs:18:5 | LL | thread::spawn(|| tx.send(()).unwrap()); | ^^^^^^^^^^^^^ `std::sync::mpsc::Sender<()>` cannot be shared between threads safely @@ -26,7 +26,7 @@ LL | F: Send + 'static, | = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Sender<()>` = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Sender<()>` - = note: required because it appears within the type `[closure@$DIR/closure-move-sync.rs:22:19: 22:42 tx:&std::sync::mpsc::Sender<()>]` + = note: required because it appears within the type `[closure@$DIR/closure-move-sync.rs:18:19: 18:42 tx:&std::sync::mpsc::Sender<()>]` error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/closure_cap_coerce_many_fail.rs b/src/test/ui/closures/closure_cap_coerce_many_fail.rs new file mode 100644 index 0000000000000..9133a29210308 --- /dev/null +++ b/src/test/ui/closures/closure_cap_coerce_many_fail.rs @@ -0,0 +1,39 @@ +fn add(a: i32, b: i32) -> i32 { + a + b +} +fn main() { + // We shouldn't coerce capturing closure to a function + let cap = 0; + let _ = match "+" { + "+" => add, + "-" => |a, b| (a - b + cap) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types + + + // We shouldn't coerce capturing closure to a non-capturing closure + let _ = match "+" { + "+" => |a, b| (a + b) as i32, + "-" => |a, b| (a - b + cap) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types + + + // We shouldn't coerce non-capturing closure to a capturing closure + let _ = match "+" { + "+" => |a, b| (a + b + cap) as i32, + "-" => |a, b| (a - b) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types + + // We shouldn't coerce capturing closure to a capturing closure + let _ = match "+" { + "+" => |a, b| (a + b + cap) as i32, + "-" => |a, b| (a - b + cap) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types +} diff --git a/src/test/ui/closures/closure_cap_coerce_many_fail.stderr b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr new file mode 100644 index 0000000000000..63eb0bd8fabad --- /dev/null +++ b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr @@ -0,0 +1,73 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:9:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => add, + | | --- this is found to be of type `fn(i32, i32) -> i32 {add}` +LL | | "-" => |a, b| (a - b + cap) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `fn(i32, i32) -> i32 {add}` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:43 cap:_]` + +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:18:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => |a, b| (a + b) as i32, + | | --------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]` +LL | | "-" => |a, b| (a - b + cap) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:43 cap:_]` + = note: no two closures, even if identical, have the same type + = help: consider boxing your closure and/or using it as a trait object + +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:27:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => |a, b| (a + b + cap) as i32, + | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]` +LL | | "-" => |a, b| (a - b) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:37]` + = note: no two closures, even if identical, have the same type + = help: consider boxing your closure and/or using it as a trait object + +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:35:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => |a, b| (a + b + cap) as i32, + | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]` +LL | | "-" => |a, b| (a - b + cap) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:43 cap:_]` + = note: no two closures, even if identical, have the same type + = help: consider boxing your closure and/or using it as a trait object + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs b/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs new file mode 100644 index 0000000000000..ce461810ec990 --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs @@ -0,0 +1,166 @@ +// check-pass +// Ensure non-capturing Closure passes CoerceMany. +fn foo(x: usize) -> usize { + 0 +} + +fn bar(x: usize) -> usize { + 1 +} + +fn main() { + // One FnDef and one non-capturing Closure + let _ = match 0 { + 0 => foo, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 2 => |a| 2, + 0 => foo, + _ => unimplemented!(), + }; + + let _ = [foo, |a| 2]; + let _ = [|a| 2, foo]; + + + + // Two FnDefs and one non-capturing Closure + let _ = match 0 { + 0 => foo, + 1 => bar, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 0 => foo, + 2 => |a| 2, + 1 => bar, + _ => unimplemented!(), + }; + + let _ = match 0 { + 2 => |a| 2, + 0 => foo, + 1 => bar, + _ => unimplemented!(), + }; + + let _ = [foo, bar, |a| 2]; + let _ = [foo, |a| 2, bar]; + let _ = [|a| 2, foo, bar]; + + + + // One FnDef and two non-capturing Closures + let _ = match 0 { + 0 => foo, + 1 => |a| 1, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 1 => |a| 1, + 0 => foo, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 1 => |a| 1, + 2 => |a| 2, + 0 => foo, + _ => unimplemented!(), + }; + + let _ = [foo, |a| 1, |a| 2]; + let _ = [|a| 1, foo, |a| 2]; + let _ = [|a| 1, |a| 2, foo]; + + + + // Three non-capturing Closures + let _ = match 0 { + 0 => |a: usize| 0, + 1 => |a| 1, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = [|a: usize| 0, |a| 1, |a| 2]; + + + + // Three non-capturing Closures variable + let clo0 = |a: usize| 0; + let clo1 = |a| 1; + let clo2 = |a| 2; + let _ = match 0 { + 0 => clo0, + 1 => clo1, + 2 => clo2, + _ => unimplemented!(), + }; + + let clo0 = |a: usize| 0; + let clo1 = |a| 1; + let clo2 = |a| 2; + let _ = [clo0, clo1, clo2]; + + + + // --- Function pointer related part + + // Closure is not in a variable + type FnPointer = fn(usize) -> usize; + + let _ = match 0 { + 0 => foo as FnPointer, + 2 => |a| 2, + _ => unimplemented!(), + }; + let _ = match 0 { + 2 => |a| 2, + 0 => foo as FnPointer, + _ => unimplemented!(), + }; + let _ = [foo as FnPointer, |a| 2]; + let _ = [|a| 2, foo as FnPointer]; + let _ = [foo, bar, |x| x]; + let _ = [foo as FnPointer, bar, |x| x]; + let _ = [foo, bar as FnPointer, |x| x]; + let _ = [foo, bar, (|x| x) as FnPointer]; + let _ = [foo as FnPointer, bar as FnPointer, |x| x]; + + // Closure is in a variable + let x = |a| 2; + let _ = match 0 { + 0 => foo as FnPointer, + 2 => x, + _ => unimplemented!(), + }; + let x = |a| 2; + let _ = match 0 { + 2 => x, + 0 => foo as FnPointer, + _ => unimplemented!(), + }; + let x = |a| 2; + let _ = [foo as FnPointer, x]; + let _ = [x, foo as FnPointer]; + + let x = |a| 2; + let _ = [foo, bar, x]; + let x: FnPointer = |a| 2; + let _ = [foo, bar, x]; + let x = |a| 2; + let _ = [foo, bar as FnPointer, x]; + let x = |a| 2; + let _ = [foo as FnPointer, bar, x]; + let x = |a| 2; + let _ = [foo as FnPointer, bar as FnPointer, x]; +} diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs b/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs new file mode 100644 index 0000000000000..3c5fe8a550276 --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs @@ -0,0 +1,59 @@ +// run-pass +// Ensure non-capturing Closure passing CoerceMany work correctly. +fn foo(_: usize) -> usize { + 0 +} + +fn bar(_: usize) -> usize { + 1 +} + +fn add(a: i32, b: i32) -> i32 { + a + b +} + +fn main() { + // Coerce result check + + type FnPointer = fn(usize) -> usize; + + let c = |x| x; + let c_pointer: FnPointer = c; + assert_eq!(c_pointer(42), 42); + + let f = match 0 { + 0 => foo, + 1 => |_| 1, + _ => unimplemented!(), + }; + assert_eq!(f(42), 0); + + let f = match 2 { + 2 => |_| 2, + 0 => foo, + _ => unimplemented!(), + }; + assert_eq!(f(42), 2); + + let f = match 1 { + 0 => foo, + 1 => bar, + 2 => |_| 2, + _ => unimplemented!(), + }; + assert_eq!(f(42), 1); + + let clo0 = |_: usize| 0; + let clo1 = |_| 1; + let clo2 = |_| 2; + let f = match 0 { + 0 => clo0, + 1 => clo1, + 2 => clo2, + _ => unimplemented!(), + }; + assert_eq!(f(42), 0); + + let funcs = [add, |a, b| (a - b) as i32]; + assert_eq!([funcs[0](5, 5), funcs[1](5, 5)], [10, 0]); +} diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs new file mode 100644 index 0000000000000..76a0f2914103d --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs @@ -0,0 +1,22 @@ +// Ensure we get unsafe function after coercion +unsafe fn add(a: i32, b: i32) -> i32 { + a + b +} +fn main() { + // We can coerce non-capturing closure to unsafe function + let foo = match "+" { + "+" => add, + "-" => |a, b| (a - b) as i32, + _ => unimplemented!(), + }; + let result: i32 = foo(5, 5); //~ ERROR call to unsafe function + + + // We can coerce unsafe function to non-capturing closure + let foo = match "+" { + "-" => |a, b| (a - b) as i32, + "+" => add, + _ => unimplemented!(), + }; + let result: i32 = foo(5, 5); //~ ERROR call to unsafe function +} diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr new file mode 100644 index 0000000000000..190b4792ebcbc --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:12:23 + | +LL | let result: i32 = foo(5, 5); + | ^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:21:23 + | +LL | let result: i32 = foo(5, 5); + | ^^^^^^^^^ 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/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs new file mode 100644 index 0000000000000..a6d6125a1b9f9 --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs @@ -0,0 +1,23 @@ +// run-pass +// Ensure we get correct unsafe function after coercion +unsafe fn add(a: i32, b: i32) -> i32 { + a + b +} +fn main() { + // We can coerce non-capturing closure to unsafe function + let foo = match "+" { + "+" => add, + "-" => |a, b| (a - b) as i32, + _ => unimplemented!(), + }; + assert_eq!(unsafe { foo(5, 5) }, 10); + + + // We can coerce unsafe function to non-capturing closure + let foo = match "-" { + "-" => |a, b| (a - b) as i32, + "+" => add, + _ => unimplemented!(), + }; + assert_eq!(unsafe { foo(5, 5) }, 0); +} diff --git a/src/test/ui/closures/diverging-closure.rs b/src/test/ui/closures/diverging-closure.rs new file mode 100644 index 0000000000000..1213a883ef0a3 --- /dev/null +++ b/src/test/ui/closures/diverging-closure.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:oops +// ignore-emscripten no processes + +fn main() { + let func = || -> ! { + panic!("oops"); + }; + func(); +} diff --git a/src/test/ui/closures/issue-46742.rs b/src/test/ui/closures/issue-46742.rs new file mode 100644 index 0000000000000..cd8dc486906bb --- /dev/null +++ b/src/test/ui/closures/issue-46742.rs @@ -0,0 +1,9 @@ +// check-pass +fn main() { + let _: i32 = (match "" { + "+" => ::std::ops::Add::add, + "-" => ::std::ops::Sub::sub, + "<" => |a,b| (a < b) as i32, + _ => unimplemented!(), + })(5, 5); +} diff --git a/src/test/ui/closures/issue-48109.rs b/src/test/ui/closures/issue-48109.rs new file mode 100644 index 0000000000000..ce1f2a0364764 --- /dev/null +++ b/src/test/ui/closures/issue-48109.rs @@ -0,0 +1,14 @@ +// check-pass +fn useful(i: usize) -> usize { + i +} + +fn useful2(i: usize) -> usize { + i +} + +fn main() { + for f in &[useful, useful2, |x| x] { + println!("{}", f(6)); + } +} diff --git a/src/test/ui/closures/issue-67123.stderr b/src/test/ui/closures/issue-67123.stderr index f14478d7278cd..5a6dfb2fdf946 100644 --- a/src/test/ui/closures/issue-67123.stderr +++ b/src/test/ui/closures/issue-67123.stderr @@ -6,12 +6,11 @@ LL | || { t; t; }; | | | value moved here | -help: consider restricting this type parameter with `T: Copy` - --> $DIR/issue-67123.rs:1:8 - | -LL | fn foo(t: T) { - | ^ = note: move occurs because `t` has type `T`, which does not implement the `Copy` trait +help: consider restricting type parameter `T` + | +LL | fn foo(t: T) { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/empty_span.rs b/src/test/ui/codemap_tests/empty_span.rs index 4d52b391280a7..7753e2eceb543 100644 --- a/src/test/ui/codemap_tests/empty_span.rs +++ b/src/test/ui/codemap_tests/empty_span.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] fn main() { struct Foo; diff --git a/src/test/ui/codemap_tests/two_files.stderr b/src/test/ui/codemap_tests/two_files.stderr index 5027b78b38e34..de2ffc2e5dc1d 100644 --- a/src/test/ui/codemap_tests/two_files.stderr +++ b/src/test/ui/codemap_tests/two_files.stderr @@ -4,7 +4,11 @@ error[E0404]: expected trait, found type alias `Bar` LL | impl Bar for Baz { } | ^^^ type aliases cannot be used as traits | - = note: did you mean to use a trait alias? +help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias + --> $DIR/two_files_data.rs:5:1 + | +LL | type Bar = dyn Foo; + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/unicode.stderr b/src/test/ui/codemap_tests/unicode.stderr index fc2a0b5950e7d..01d54ac8cc8a2 100644 --- a/src/test/ui/codemap_tests/unicode.stderr +++ b/src/test/ui/codemap_tests/unicode.stderr @@ -8,3 +8,4 @@ LL | extern "路濫狼á́́" fn foo() {} error: aborting due to previous error +For more information about this error, try `rustc --explain E0703`. diff --git a/src/test/ui/codemap_tests/unicode_3.rs b/src/test/ui/codemap_tests/unicode_3.rs index 3994d3186817d..34582de45cb9c 100644 --- a/src/test/ui/codemap_tests/unicode_3.rs +++ b/src/test/ui/codemap_tests/unicode_3.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass fn main() { let s = "ZͨA͑ͦ͒͋ͤ͑̚L̄͑͋Ĝͨͥ̿͒̽̈́Oͥ͛ͭ!̏"; while true { break; } //~ WARNING while_true diff --git a/src/test/ui/codemap_tests/unicode_3.stderr b/src/test/ui/codemap_tests/unicode_3.stderr index cc1a80bc07456..a35ed99d8a3dd 100644 --- a/src/test/ui/codemap_tests/unicode_3.stderr +++ b/src/test/ui/codemap_tests/unicode_3.stderr @@ -6,3 +6,5 @@ LL | let s = "ZͨA͑ͦ͒͋ͤ͑̚L̄͑͋Ĝͨͥ̿͒̽̈́Oͥ͛ͭ!̏"; while tru | = note: `#[warn(while_true)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/coerce/coerce-expect-unsized.rs b/src/test/ui/coerce/coerce-expect-unsized.rs index b44aa6ab37760..d486fdf73aba8 100644 --- a/src/test/ui/coerce/coerce-expect-unsized.rs +++ b/src/test/ui/coerce/coerce-expect-unsized.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![feature(box_syntax)] use std::cell::RefCell; diff --git a/src/test/ui/coerce/coerce-overloaded-autoderef.rs b/src/test/ui/coerce/coerce-overloaded-autoderef.rs index 3fe18103ef8c3..d5484607c8b52 100644 --- a/src/test/ui/coerce/coerce-overloaded-autoderef.rs +++ b/src/test/ui/coerce/coerce-overloaded-autoderef.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(dead_code)] // pretty-expanded FIXME #23616 diff --git a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.rs b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.rs index b4f5f9ef56bb1..24b878927530c 100644 --- a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.rs +++ b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![feature(marker_trait_attr)] #[marker] @@ -6,13 +6,11 @@ trait MyTrait {} struct TestType(::std::marker::PhantomData); -unsafe impl Send for TestType {} +unsafe impl Send for TestType {} -impl !Send for TestType {} -//~^ ERROR conflicting implementations +impl !Send for TestType {} //~ ERROR found both positive and negative implementation -unsafe impl Send for TestType {} -//~^ ERROR conflicting implementations +unsafe impl Send for TestType {} //~ ERROR conflicting implementations impl !Send for TestType {} diff --git a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr index 25d3d3ee997a5..4d9f815c79581 100644 --- a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr +++ b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr @@ -1,21 +1,22 @@ -error[E0119]: conflicting implementations of trait `std::marker::Send` for type `TestType<_>`: +error[E0751]: found both positive and negative implementation of trait `std::marker::Send` for type `TestType<_>`: --> $DIR/coherence-conflicting-negative-trait-impl.rs:11:1 | -LL | unsafe impl Send for TestType {} - | ---------------------------------------------------- first implementation here +LL | unsafe impl Send for TestType {} + | ------------------------------------------------------ positive implementation here LL | LL | impl !Send for TestType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here error[E0119]: conflicting implementations of trait `std::marker::Send` for type `TestType<_>`: - --> $DIR/coherence-conflicting-negative-trait-impl.rs:14:1 + --> $DIR/coherence-conflicting-negative-trait-impl.rs:13:1 | -LL | unsafe impl Send for TestType {} - | ---------------------------------------------------- first implementation here +LL | unsafe impl Send for TestType {} + | ------------------------------------------------------ first implementation here ... -LL | unsafe impl Send for TestType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>` +LL | unsafe impl Send for TestType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>` error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0119`. +Some errors have detailed explanations: E0119, E0751. +For more information about an error, try `rustc --explain E0119`. diff --git a/src/test/ui/coherence/coherence-default-trait-impl.rs b/src/test/ui/coherence/coherence-default-trait-impl.rs index db24662e2d5ad..4115ba34e17b2 100644 --- a/src/test/ui/coherence/coherence-default-trait-impl.rs +++ b/src/test/ui/coherence/coherence-default-trait-impl.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait MySafeTrait {} diff --git a/src/test/ui/coherence/coherence-default-trait-impl.stderr b/src/test/ui/coherence/coherence-default-trait-impl.stderr index f6a163268a14a..b08ccb087d91c 100644 --- a/src/test/ui/coherence/coherence-default-trait-impl.stderr +++ b/src/test/ui/coherence/coherence-default-trait-impl.stderr @@ -1,11 +1,11 @@ error[E0199]: implementing the trait `MySafeTrait` is not unsafe - --> $DIR/coherence-default-trait-impl.rs:7:1 + --> $DIR/coherence-default-trait-impl.rs:8:1 | LL | unsafe impl MySafeTrait for Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0200]: the trait `MyUnsafeTrait` requires an `unsafe impl` declaration - --> $DIR/coherence-default-trait-impl.rs:12:1 + --> $DIR/coherence-default-trait-impl.rs:13:1 | LL | impl MyUnsafeTrait for Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs index 5ea69190951e1..a9c8d20a79d75 100644 --- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs +++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] // Test for issue #56934 - that it is impossible to redundantly // implement an auto-trait for a trait object type that contains it. diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr index b8137b36948cd..23db5328a728b 100644 --- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr +++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr @@ -1,17 +1,17 @@ error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1` - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:14:1 + --> $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[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:16:1 + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1 | LL | impl !Marker2 for dyn Object + Marker2 { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2` error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:22:1 + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:23:1 | LL | impl !Send for dyn Marker2 {} | ^^^^^^^^^^^^^^^----------- @@ -22,13 +22,13 @@ LL | impl !Send for dyn Marker2 {} = note: define and implement a trait or new type instead error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:26:1 + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:27:1 | LL | impl !Send for dyn Object {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + Marker2 + 'static)` - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:27:1 + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:28:1 | LL | impl !Send for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs index 6b5689e8260f0..c565f9c83e89e 100644 --- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs +++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] // Test for issue #56934 - that it is impossible to redundantly // implement an auto-trait for a trait object type that contains it. diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr index d68337bed0066..141ab7771f325 100644 --- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr +++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr @@ -1,17 +1,17 @@ error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1` - --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:14:1 + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1 | LL | impl Marker1 for dyn Object + Marker2 { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1` error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` - --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:16:1 + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:17:1 | LL | impl Marker2 for dyn Object + Marker2 { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2` error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:22:1 + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:23:1 | LL | unsafe impl Send for dyn Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^----------- @@ -22,13 +22,13 @@ LL | unsafe impl Send for dyn Marker2 {} = note: define and implement a trait or new type instead error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` - --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:26:1 + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:27:1 | LL | unsafe impl Send for dyn Object {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + Marker2 + 'static)` - --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:27:1 + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:28:1 | LL | unsafe impl Send for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type diff --git a/src/test/ui/coherence/coherence-impls-copy.rs b/src/test/ui/coherence/coherence-impls-copy.rs index dec40f9dd40aa..a86ca0e5eacdc 100644 --- a/src/test/ui/coherence/coherence-impls-copy.rs +++ b/src/test/ui/coherence/coherence-impls-copy.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Copy; diff --git a/src/test/ui/coherence/coherence-impls-send.rs b/src/test/ui/coherence/coherence-impls-send.rs index 7898dc9831da2..e00cb9a7c5b51 100644 --- a/src/test/ui/coherence/coherence-impls-send.rs +++ b/src/test/ui/coherence/coherence-impls-send.rs @@ -1,9 +1,9 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Copy; enum TestE { - A + A, } struct MyType; @@ -26,5 +26,4 @@ unsafe impl Send for &'static [NotSync] {} //~^ ERROR conflicting implementations of trait //~| ERROR only traits defined in the current crate -fn main() { -} +fn main() {} diff --git a/src/test/ui/coherence/coherence-impls-sized.rs b/src/test/ui/coherence/coherence-impls-sized.rs index 19e7349c507ef..231b96ad42efb 100644 --- a/src/test/ui/coherence/coherence-impls-sized.rs +++ b/src/test/ui/coherence/coherence-impls-sized.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Copy; diff --git a/src/test/ui/coherence/coherence-negative-impls-safe-rpass.rs b/src/test/ui/coherence/coherence-negative-impls-safe-rpass.rs index 695a71cbd2d7c..b87e162aca096 100644 --- a/src/test/ui/coherence/coherence-negative-impls-safe-rpass.rs +++ b/src/test/ui/coherence/coherence-negative-impls-safe-rpass.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] // pretty-expanded FIXME #23616 -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Send; diff --git a/src/test/ui/coherence/coherence-negative-impls-safe.rs b/src/test/ui/coherence/coherence-negative-impls-safe.rs index 45c478ecc0362..4821aa6b5ad3d 100644 --- a/src/test/ui/coherence/coherence-negative-impls-safe.rs +++ b/src/test/ui/coherence/coherence-negative-impls-safe.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Send; diff --git a/src/test/ui/coherence/coherence-orphan.rs b/src/test/ui/coherence/coherence-orphan.rs index a7b48825d7c02..3beac04c7e829 100644 --- a/src/test/ui/coherence/coherence-orphan.rs +++ b/src/test/ui/coherence/coherence-orphan.rs @@ -1,5 +1,5 @@ // aux-build:coherence_orphan_lib.rs -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] extern crate coherence_orphan_lib as lib; diff --git a/src/test/ui/coherence/coherence-subtyping.old.stderr b/src/test/ui/coherence/coherence-subtyping.old.stderr index 76f5cc1b78232..b3c2f4516349e 100644 --- a/src/test/ui/coherence/coherence-subtyping.old.stderr +++ b/src/test/ui/coherence/coherence-subtyping.old.stderr @@ -12,3 +12,5 @@ LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { = note: for more information, see issue #56105 = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details +warning: 1 warning emitted + diff --git a/src/test/ui/coherence/coherence-subtyping.re.stderr b/src/test/ui/coherence/coherence-subtyping.re.stderr index 76f5cc1b78232..b3c2f4516349e 100644 --- a/src/test/ui/coherence/coherence-subtyping.re.stderr +++ b/src/test/ui/coherence/coherence-subtyping.re.stderr @@ -12,3 +12,5 @@ LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { = note: for more information, see issue #56105 = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details +warning: 1 warning emitted + diff --git a/src/test/ui/coherence/coherence-unsafe-trait-object-impl.stderr b/src/test/ui/coherence/coherence-unsafe-trait-object-impl.stderr index b5a86acfb978f..93a06fccbf53c 100644 --- a/src/test/ui/coherence/coherence-unsafe-trait-object-impl.stderr +++ b/src/test/ui/coherence/coherence-unsafe-trait-object-impl.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `&dyn Trait: Trait` is not satisfied --> $DIR/coherence-unsafe-trait-object-impl.rs:15:13 | LL | fn takes_t(s: S) { - | ------- ----- required by this bound in `takes_t` + | ----- required by this bound in `takes_t` ... LL | takes_t(t); | ^ the trait `Trait` is not implemented for `&dyn Trait` diff --git a/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs b/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs index 49b3abc99b731..bc1e18b657f31 100644 --- a/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs +++ b/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs @@ -8,8 +8,8 @@ extern crate coherence_lib as lib; use lib::*; #[fundamental] -struct Local; +struct Local(T); -impl Remote for Local {} +impl Remote for Local<()> {} fn main() {} diff --git a/src/test/ui/collections-const-new.rs b/src/test/ui/collections-const-new.rs index a93f9a136db23..978f25f9a9344 100644 --- a/src/test/ui/collections-const-new.rs +++ b/src/test/ui/collections-const-new.rs @@ -3,9 +3,18 @@ // Test several functions can be used for constants // 1. Vec::new() // 2. String::new() +// 3. BTreeMap::new() +// 4. BTreeSet::new() + +#![feature(const_btree_new)] const MY_VEC: Vec = Vec::new(); const MY_STRING: String = String::new(); +use std::collections::{BTreeMap, BTreeSet}; +const MY_BTREEMAP: BTreeMap = BTreeMap::new(); + +const MY_BTREESET: BTreeSet = BTreeSet::new(); + fn main() {} diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr index d2c613845a0ff..d7b5d2d263a10 100644 --- a/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr +++ b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr @@ -36,3 +36,5 @@ note: the lint level is defined here LL | #![warn(unused_must_use)] | ^^^^^^^^^^^^^^^ +warning: 5 warnings emitted + diff --git a/src/test/ui/const-generics/apit-with-const-param.rs b/src/test/ui/const-generics/apit-with-const-param.rs index 7acc50819a6ad..f9c6e201b1762 100644 --- a/src/test/ui/const-generics/apit-with-const-param.rs +++ b/src/test/ui/const-generics/apit-with-const-param.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete trait Trait {} diff --git a/src/test/ui/const-generics/apit-with-const-param.stderr b/src/test/ui/const-generics/apit-with-const-param.stderr index c4ad38a571170..4389e4738eadc 100644 --- a/src/test/ui/const-generics/apit-with-const-param.stderr +++ b/src/test/ui/const-generics/apit-with-const-param.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/apit-with-const-param.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/argument_order.rs b/src/test/ui/const-generics/argument_order.rs index 3446600d0495f..6110d16c070d9 100644 --- a/src/test/ui/const-generics/argument_order.rs +++ b/src/test/ui/const-generics/argument_order.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct Bad { //~ ERROR type parameters must be declared prior arr: [u8; { N }], diff --git a/src/test/ui/const-generics/argument_order.stderr b/src/test/ui/const-generics/argument_order.stderr index 1e3b364eb6089..f77ae49cf10b1 100644 --- a/src/test/ui/const-generics/argument_order.stderr +++ b/src/test/ui/const-generics/argument_order.stderr @@ -4,13 +4,14 @@ error: type parameters must be declared prior to const parameters LL | struct Bad { | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/argument_order.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs index db941a440e104..0d0765e971d50 100644 --- a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs +++ b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs @@ -14,6 +14,10 @@ where Vec::::new() } +pub fn yes_array_into_vec() -> Vec { + [].into() +} + use std::collections::VecDeque; pub fn yes_vecdeque_partial_eq_array() -> impl PartialEq<[B; 32]> diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs index 3a23b9b5832c0..4b195f3a06edc 100644 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs +++ b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs @@ -2,6 +2,11 @@ use std::{convert::TryFrom, rc::Rc, sync::Arc}; +pub fn no_vec() { + let v: Vec<_> = [0; 33].into(); + //~^ ERROR arrays only have std trait implementations for lengths 0..=32 +} + pub fn no_box() { let boxed_slice = Box::new([0; 33]) as Box<[i32]>; let boxed_array = >::try_from(boxed_slice); diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr index 193fb4c4374a4..ce1c9ae551ea5 100644 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr @@ -1,5 +1,14 @@ +error[E0277]: arrays only have std trait implementations for lengths 0..=32 + --> $DIR/alloc-types-no-impls-length-33.rs:6:29 + | +LL | let v: Vec<_> = [0; 33].into(); + | ^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[{integer}; 33]` + | + = note: required because of the requirements on the impl of `std::convert::From<[{integer}; 33]>` for `std::vec::Vec<{integer}>` + = note: required because of the requirements on the impl of `std::convert::Into>` for `[{integer}; 33]` + error[E0277]: the trait bound `std::boxed::Box<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:7:23 + --> $DIR/alloc-types-no-impls-length-33.rs:12:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::boxed::Box<[i32; 33]>` @@ -9,67 +18,67 @@ LL | let boxed_array = >::try_from(boxed_slice); as std::convert::From<&str>> as std::convert::From>> as std::convert::From> - and 16 others + and 21 others = note: required because of the requirements on the impl of `std::convert::Into>` for `std::boxed::Box<[i32]>` = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::boxed::Box<[i32; 33]>` error[E0277]: the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:7:23 + --> $DIR/alloc-types-no-impls-length-33.rs:12:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::boxed::Box<[i32; 33]>` | = help: the following implementations were found: - as std::convert::TryFrom>> + as std::convert::TryFrom>> error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:14:23 + --> $DIR/alloc-types-no-impls-length-33.rs:19:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::rc::Rc<[i32; 33]>` | = help: the following implementations were found: + as std::convert::From>> as std::convert::From> as std::convert::From>> as std::convert::From<&[T]>> - as std::convert::From>> - and 8 others + and 9 others = note: required because of the requirements on the impl of `std::convert::Into>` for `std::rc::Rc<[i32]>` = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::rc::Rc<[i32; 33]>` error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:14:23 + --> $DIR/alloc-types-no-impls-length-33.rs:19:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::rc::Rc<[i32; 33]>` | = help: the following implementations were found: - as std::convert::TryFrom>> + as std::convert::TryFrom>> error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:21:23 + --> $DIR/alloc-types-no-impls-length-33.rs:26:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::sync::Arc<[i32; 33]>` | = help: the following implementations were found: + as std::convert::From>> as std::convert::From> as std::convert::From>> as std::convert::From<&[T]>> - as std::convert::From>> - and 8 others + and 9 others = note: required because of the requirements on the impl of `std::convert::Into>` for `std::sync::Arc<[i32]>` = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::sync::Arc<[i32; 33]>` error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:21:23 + --> $DIR/alloc-types-no-impls-length-33.rs:26:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::sync::Arc<[i32; 33]>` | = help: the following implementations were found: - as std::convert::TryFrom>> + as std::convert::TryFrom>> -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.stderr index c03377d74e9b7..76ccc48c32ac1 100644 --- a/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.stderr @@ -39,9 +39,9 @@ LL | for _ in &[0_usize; 33] { | ^^^^^^^^^^^^^^ the trait `std::iter::IntoIterator` is not implemented for `&[usize; 33]` | = help: the following implementations were found: - <&'a [T; _] as std::iter::IntoIterator> + <&'a [T; N] as std::iter::IntoIterator> <&'a [T] as std::iter::IntoIterator> - <&'a mut [T; _] as std::iter::IntoIterator> + <&'a mut [T; N] as std::iter::IntoIterator> <&'a mut [T] as std::iter::IntoIterator> = note: required by `std::iter::IntoIterator::into_iter` diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.rs b/src/test/ui/const-generics/array-size-in-generic-struct-param.rs index d996bf56fcc10..5c02e585dc8ba 100644 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.rs +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #[allow(dead_code)] struct ArithArrayLen([u32; 0 + N]); diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.stderr b/src/test/ui/const-generics/array-size-in-generic-struct-param.stderr index 6ae70c493b1dd..14cf64eeb7ac6 100644 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.stderr +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/array-size-in-generic-struct-param.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error: constant expression depends on a generic parameter --> $DIR/array-size-in-generic-struct-param.rs:5:38 @@ -22,5 +23,5 @@ LL | arr: [u8; CFG.arr_size], | = note: this may fail depending on what value the parameter takes -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/const-generics/array-wrapper-struct-ctor.rs b/src/test/ui/const-generics/array-wrapper-struct-ctor.rs index 2d1a405ebdd80..49fc53b32bd92 100644 --- a/src/test/ui/const-generics/array-wrapper-struct-ctor.rs +++ b/src/test/ui/const-generics/array-wrapper-struct-ctor.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #![allow(dead_code)] diff --git a/src/test/ui/const-generics/array-wrapper-struct-ctor.stderr b/src/test/ui/const-generics/array-wrapper-struct-ctor.stderr index 5a5eaba0b197e..e6eb2a0a78303 100644 --- a/src/test/ui/const-generics/array-wrapper-struct-ctor.stderr +++ b/src/test/ui/const-generics/array-wrapper-struct-ctor.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/array-wrapper-struct-ctor.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/broken-mir-1.rs b/src/test/ui/const-generics/broken-mir-1.rs index 9a11bd3d0313a..f137be2d6a6fa 100644 --- a/src/test/ui/const-generics/broken-mir-1.rs +++ b/src/test/ui/const-generics/broken-mir-1.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete pub trait Foo { fn foo(&self); diff --git a/src/test/ui/const-generics/broken-mir-1.stderr b/src/test/ui/const-generics/broken-mir-1.stderr index 51de98ad5237a..a5532bde1f5e9 100644 --- a/src/test/ui/const-generics/broken-mir-1.stderr +++ b/src/test/ui/const-generics/broken-mir-1.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/broken-mir-1.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/broken-mir-2.rs b/src/test/ui/const-generics/broken-mir-2.rs index d9a4411b4f981..c2f9b786f8f89 100644 --- a/src/test/ui/const-generics/broken-mir-2.rs +++ b/src/test/ui/const-generics/broken-mir-2.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete use std::fmt::Debug; diff --git a/src/test/ui/const-generics/broken-mir-2.stderr b/src/test/ui/const-generics/broken-mir-2.stderr index 7d95b46790d65..05552027f13d0 100644 --- a/src/test/ui/const-generics/broken-mir-2.stderr +++ b/src/test/ui/const-generics/broken-mir-2.stderr @@ -1,22 +1,23 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/broken-mir-2.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0277]: arrays only have std trait implementations for lengths 0..=32 --> $DIR/broken-mir-2.rs:7:36 | LL | struct S([T; N]); - | ^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[T; _]` + | ^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[T; N]` | - = note: required because of the requirements on the impl of `std::fmt::Debug` for `[T; _]` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[T; _]` + = note: required because of the requirements on the impl of `std::fmt::Debug` for `[T; N]` + = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[T; N]` = note: required for the cast to the object type `dyn std::fmt::Debug` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/cannot-infer-const-args.rs b/src/test/ui/const-generics/cannot-infer-const-args.rs index e1061c6d1a33d..2f6ad2654c12c 100644 --- a/src/test/ui/const-generics/cannot-infer-const-args.rs +++ b/src/test/ui/const-generics/cannot-infer-const-args.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn foo() -> usize { 0 diff --git a/src/test/ui/const-generics/cannot-infer-const-args.stderr b/src/test/ui/const-generics/cannot-infer-const-args.stderr index c1d7022d56b5f..6696b025855a8 100644 --- a/src/test/ui/const-generics/cannot-infer-const-args.stderr +++ b/src/test/ui/const-generics/cannot-infer-const-args.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/cannot-infer-const-args.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0282]: type annotations needed --> $DIR/cannot-infer-const-args.rs:9:5 @@ -12,6 +13,6 @@ error[E0282]: type annotations needed LL | foo(); | ^^^ cannot infer type for fn item `fn() -> usize {foo::<{_: usize}>}` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs b/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs index 0fbd0bbcbaebd..aac5d195f76af 100644 --- a/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs +++ b/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs @@ -1,6 +1,6 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete // This test confirms that the types can be inferred correctly for this example with const // generics. Previously this would ICE, and more recently error. diff --git a/src/test/ui/const-generics/cannot-infer-type-for-const-param.stderr b/src/test/ui/const-generics/cannot-infer-type-for-const-param.stderr index 00a98e3ba9b3e..c5c48d7be4689 100644 --- a/src/test/ui/const-generics/cannot-infer-type-for-const-param.stderr +++ b/src/test/ui/const-generics/cannot-infer-type-for-const-param.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/cannot-infer-type-for-const-param.rs:2:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/concrete-const-as-fn-arg.rs b/src/test/ui/const-generics/concrete-const-as-fn-arg.rs index 54981b77a2b84..18ebba49f6f91 100644 --- a/src/test/ui/const-generics/concrete-const-as-fn-arg.rs +++ b/src/test/ui/const-generics/concrete-const-as-fn-arg.rs @@ -2,7 +2,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct A; // ok diff --git a/src/test/ui/const-generics/concrete-const-as-fn-arg.stderr b/src/test/ui/const-generics/concrete-const-as-fn-arg.stderr index 0392488fce1c7..c8f3a8beaf83f 100644 --- a/src/test/ui/const-generics/concrete-const-as-fn-arg.stderr +++ b/src/test/ui/const-generics/concrete-const-as-fn-arg.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/concrete-const-as-fn-arg.rs:4:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/concrete-const-impl-method.rs b/src/test/ui/const-generics/concrete-const-impl-method.rs index 226ea4151806e..c1ddf9a33140d 100644 --- a/src/test/ui/const-generics/concrete-const-impl-method.rs +++ b/src/test/ui/const-generics/concrete-const-impl-method.rs @@ -3,7 +3,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete pub struct A; diff --git a/src/test/ui/const-generics/concrete-const-impl-method.stderr b/src/test/ui/const-generics/concrete-const-impl-method.stderr index 5e730b5643a7d..5edb4f4f6cdad 100644 --- a/src/test/ui/const-generics/concrete-const-impl-method.stderr +++ b/src/test/ui/const-generics/concrete-const-impl-method.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/concrete-const-impl-method.rs:5:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/condition-in-trait-const-arg.rs b/src/test/ui/const-generics/condition-in-trait-const-arg.rs index 091fe904826d4..9d8aaed54bd75 100644 --- a/src/test/ui/const-generics/condition-in-trait-const-arg.rs +++ b/src/test/ui/const-generics/condition-in-trait-const-arg.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete trait IsZeroTrait{} diff --git a/src/test/ui/const-generics/condition-in-trait-const-arg.stderr b/src/test/ui/const-generics/condition-in-trait-const-arg.stderr index c9e22ab39018b..9ac33454128b5 100644 --- a/src/test/ui/const-generics/condition-in-trait-const-arg.stderr +++ b/src/test/ui/const-generics/condition-in-trait-const-arg.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/condition-in-trait-const-arg.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/const-arg-in-fn.rs b/src/test/ui/const-generics/const-arg-in-fn.rs index 3f86782838ca1..5ea2cf92fdc60 100644 --- a/src/test/ui/const-generics/const-arg-in-fn.rs +++ b/src/test/ui/const-generics/const-arg-in-fn.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn const_u32_identity() -> u32 { X diff --git a/src/test/ui/const-generics/const-arg-in-fn.stderr b/src/test/ui/const-generics/const-arg-in-fn.stderr index 61ba9cdaf55b4..bb66849c7fe6c 100644 --- a/src/test/ui/const-generics/const-arg-in-fn.stderr +++ b/src/test/ui/const-generics/const-arg-in-fn.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-arg-in-fn.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs index f024eb6a957e3..9f989ee20a569 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete type Array = [T; N]; diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr index 150a6011c2c13..ad38b632b75f0 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-arg-type-arg-misordered.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0747]: constant provided when a type was expected --> $DIR/const-arg-type-arg-misordered.rs:6:35 @@ -14,6 +15,6 @@ LL | fn foo() -> Array { | = note: type arguments must be provided before constant arguments -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 22c6c35162281..e0b66a7c14c3a 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn i32_identity() -> i32 { 5 diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr index 28bea4ec94f13..e421c22be01a8 100644 --- a/src/test/ui/const-generics/const-expression-parameter.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.stderr @@ -4,13 +4,14 @@ error: expected one of `,` or `>`, found `+` LL | i32_identity::<1 + 2>(); | ^ expected one of `,` or `>` -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-expression-parameter.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/const-fn-with-const-param.rs b/src/test/ui/const-generics/const-fn-with-const-param.rs index e9e236be55630..bbc55815e9a20 100644 --- a/src/test/ui/const-generics/const-fn-with-const-param.rs +++ b/src/test/ui/const-generics/const-fn-with-const-param.rs @@ -1,11 +1,11 @@ +// run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete const fn const_u32_identity() -> u32 { - //~^ ERROR const parameters are not permitted in const functions X } fn main() { - println!("{:?}", const_u32_identity::<18>()); + assert_eq!(const_u32_identity::<18>(), 18); } diff --git a/src/test/ui/const-generics/const-fn-with-const-param.stderr b/src/test/ui/const-generics/const-fn-with-const-param.stderr index ca31d695361f4..109b50028480b 100644 --- a/src/test/ui/const-generics/const-fn-with-const-param.stderr +++ b/src/test/ui/const-generics/const-fn-with-const-param.stderr @@ -1,23 +1,11 @@ -error: const parameters are not permitted in const functions - --> $DIR/const-fn-with-const-param.rs:4:1 - | -LL | const fn const_u32_identity() -> u32 { - | ^---- - | | - | _`const` because of this - | | -LL | | -LL | | X -LL | | } - | |_^ - -warning: the feature `const_generics` is incomplete and may cause the compiler to crash - --> $DIR/const-fn-with-const-param.rs:1:12 +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/const-fn-with-const-param.rs:2:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error: aborting due to previous error +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/const-generic-array-wrapper.rs b/src/test/ui/const-generics/const-generic-array-wrapper.rs index 56a58c582f645..3e43387163b62 100644 --- a/src/test/ui/const-generics/const-generic-array-wrapper.rs +++ b/src/test/ui/const-generics/const-generic-array-wrapper.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct Foo([T; N]); diff --git a/src/test/ui/const-generics/const-generic-array-wrapper.stderr b/src/test/ui/const-generics/const-generic-array-wrapper.stderr index 5c7b6a70d3b3f..47448bbd19d6d 100644 --- a/src/test/ui/const-generics/const-generic-array-wrapper.stderr +++ b/src/test/ui/const-generics/const-generic-array-wrapper.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-generic-array-wrapper.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/const-generic-type_name.rs b/src/test/ui/const-generics/const-generic-type_name.rs index 469843d6aae1e..22f9bd2a0f0b2 100644 --- a/src/test/ui/const-generics/const-generic-type_name.rs +++ b/src/test/ui/const-generics/const-generic-type_name.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #[derive(Debug)] struct S; diff --git a/src/test/ui/const-generics/const-generic-type_name.stderr b/src/test/ui/const-generics/const-generic-type_name.stderr index 6b60a77effea5..f161739c9c8a6 100644 --- a/src/test/ui/const-generics/const-generic-type_name.stderr +++ b/src/test/ui/const-generics/const-generic-type_name.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-generic-type_name.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.rs b/src/test/ui/const-generics/const-param-elided-lifetime.rs index 5679dd35c307a..5e6b6c4dabe02 100644 --- a/src/test/ui/const-generics/const-param-elided-lifetime.rs +++ b/src/test/ui/const-generics/const-param-elided-lifetime.rs @@ -4,7 +4,7 @@ // lifetimes within const/static items. #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct A; //~^ ERROR `&` without an explicit lifetime name cannot be used here diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.stderr index 6841d1fdf360b..8c50fb73679a9 100644 --- a/src/test/ui/const-generics/const-param-elided-lifetime.stderr +++ b/src/test/ui/const-generics/const-param-elided-lifetime.stderr @@ -28,14 +28,15 @@ error[E0637]: `&` without an explicit lifetime name cannot be used here LL | fn bar() {} | ^ explicit lifetime name needed here -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-elided-lifetime.rs:6:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error: aborting due to 5 previous errors +error: aborting due to 5 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0637`. diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.rs b/src/test/ui/const-generics/const-param-from-outer-fn.rs index 6534bcf5ce64c..4b8e2db7233e4 100644 --- a/src/test/ui/const-generics/const-param-from-outer-fn.rs +++ b/src/test/ui/const-generics/const-param-from-outer-fn.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn foo() { fn bar() -> u32 { diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.stderr b/src/test/ui/const-generics/const-param-from-outer-fn.stderr index 90ca85cd62f92..30bd1d7291456 100644 --- a/src/test/ui/const-generics/const-param-from-outer-fn.stderr +++ b/src/test/ui/const-generics/const-param-from-outer-fn.stderr @@ -8,14 +8,15 @@ LL | fn bar() -> u32 { LL | X | ^ use of generic parameter from outer function -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-from-outer-fn.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0401`. diff --git a/src/test/ui/const-generics/const-param-in-trait.rs b/src/test/ui/const-generics/const-param-in-trait.rs index 6e4f65fe6cac0..6874072571108 100644 --- a/src/test/ui/const-generics/const-param-in-trait.rs +++ b/src/test/ui/const-generics/const-param-in-trait.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete trait Trait {} diff --git a/src/test/ui/const-generics/const-param-in-trait.stderr b/src/test/ui/const-generics/const-param-in-trait.stderr index c45523d2fa6f6..a2e367b25ade0 100644 --- a/src/test/ui/const-generics/const-param-in-trait.stderr +++ b/src/test/ui/const-generics/const-param-in-trait.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-in-trait.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs index 78bd549ba791a..86ab8075896aa 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; struct B(PhantomData<[T; N]>); //~ ERROR const generics are unstable -//~^ ERROR the types of const generic parameters must derive `PartialEq` and `Eq` +//~^ ERROR `T` is not guaranteed to `#[derive(PartialEq, Eq)]` fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr index 14ade3f33fd9b..92a7edf96bccb 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr @@ -7,11 +7,13 @@ LL | struct B(PhantomData<[T; N]>); = note: see issue #44580 for more information = help: add `#![feature(const_generics)]` to the crate attributes to enable -error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq` +error[E0741]: `T` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be used as the type of a const parameter --> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:22 | LL | struct B(PhantomData<[T; N]>); - | ^ `T` doesn't derive both `PartialEq` and `Eq` + | ^ `T` may not derive both `PartialEq` and `Eq` + | + = note: it is not currently possible to use a type parameter as the type of a const parameter error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs index b76209571b05c..654e36df37e98 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs @@ -1,12 +1,12 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete // Currently, const parameters cannot depend on type parameters, because there is no way to -// enforce the `structural_match` property on an arbitrary type parameter. This restriction +// enforce the structural-match property on an arbitrary type parameter. This restriction // may be relaxed in the future. See https://github.com/rust-lang/rfcs/pull/2000 for more // details. pub struct Dependent([(); X]); -//~^ ERROR the types of const generic parameters must derive `PartialEq` and `Eq` +//~^ ERROR `T` is not guaranteed to `#[derive(PartialEq, Eq)]` fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr index c9d6db7e2c220..ed05264161e53 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr @@ -1,17 +1,20 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-param-type-depends-on-type-param.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq` +error[E0741]: `T` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be used as the type of a const parameter --> $DIR/const-param-type-depends-on-type-param.rs:9:34 | LL | pub struct Dependent([(); X]); - | ^ `T` doesn't derive both `PartialEq` and `Eq` + | ^ `T` may not derive both `PartialEq` and `Eq` + | + = note: it is not currently possible to use a type parameter as the type of a const parameter -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.rs b/src/test/ui/const-generics/const-parameter-uppercase-lint.rs index 164205dd75cbc..54a33e2181284 100644 --- a/src/test/ui/const-generics/const-parameter-uppercase-lint.rs +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #![deny(non_upper_case_globals)] diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.stderr b/src/test/ui/const-generics/const-parameter-uppercase-lint.stderr index 3fb7c8c48b90d..b7febed7bdd22 100644 --- a/src/test/ui/const-generics/const-parameter-uppercase-lint.stderr +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-parameter-uppercase-lint.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error: const parameter `x` should have an upper case name --> $DIR/const-parameter-uppercase-lint.rs:6:15 @@ -18,5 +19,5 @@ note: the lint level is defined here LL | #![deny(non_upper_case_globals)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/const-types.rs b/src/test/ui/const-generics/const-types.rs index bc5188133d7f1..bde80f4a1ed09 100644 --- a/src/test/ui/const-generics/const-types.rs +++ b/src/test/ui/const-generics/const-types.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #![allow(dead_code, unused_variables)] diff --git a/src/test/ui/const-generics/const-types.stderr b/src/test/ui/const-generics/const-types.stderr index ca3d7810ca530..4628c90031884 100644 --- a/src/test/ui/const-generics/const-types.stderr +++ b/src/test/ui/const-generics/const-types.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/const-types.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/derive-debug-array-wrapper.rs b/src/test/ui/const-generics/derive-debug-array-wrapper.rs index eee634c15644b..c6d8b32f276f3 100644 --- a/src/test/ui/const-generics/derive-debug-array-wrapper.rs +++ b/src/test/ui/const-generics/derive-debug-array-wrapper.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #[derive(Debug)] struct X { diff --git a/src/test/ui/const-generics/derive-debug-array-wrapper.stderr b/src/test/ui/const-generics/derive-debug-array-wrapper.stderr index c4aef4c9d4785..a0abbd168946a 100644 --- a/src/test/ui/const-generics/derive-debug-array-wrapper.stderr +++ b/src/test/ui/const-generics/derive-debug-array-wrapper.stderr @@ -1,22 +1,23 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/derive-debug-array-wrapper.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0277]: arrays only have std trait implementations for lengths 0..=32 --> $DIR/derive-debug-array-wrapper.rs:6:5 | LL | a: [u32; N], - | ^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[u32; _]` + | ^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[u32; N]` | - = note: required because of the requirements on the impl of `std::fmt::Debug` for `[u32; _]` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[u32; _]` + = note: required because of the requirements on the impl of `std::fmt::Debug` for `[u32; N]` + = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[u32; N]` = note: required for the cast to the object type `dyn std::fmt::Debug` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/different_byref.rs b/src/test/ui/const-generics/different_byref.rs new file mode 100644 index 0000000000000..78964eb3dee6e --- /dev/null +++ b/src/test/ui/const-generics/different_byref.rs @@ -0,0 +1,11 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +struct Const {} + +fn main() { + let mut x = Const::<{ [3] }> {}; + x = Const::<{ [4] }> {}; + //~^ ERROR mismatched types + +} diff --git a/src/test/ui/const-generics/different_byref.stderr b/src/test/ui/const-generics/different_byref.stderr new file mode 100644 index 0000000000000..7eb826b8a36b1 --- /dev/null +++ b/src/test/ui/const-generics/different_byref.stderr @@ -0,0 +1,21 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/different_byref.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0308]: mismatched types + --> $DIR/different_byref.rs:8:9 + | +LL | x = Const::<{ [4] }> {}; + | ^^^^^^^^^^^^^^^^^^^ expected `3usize`, found `4usize` + | + = note: expected type `[3usize]` + found type `[4usize]` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/fn-const-param-call.rs b/src/test/ui/const-generics/fn-const-param-call.rs index cd4b19db35331..afa577fa67ff2 100644 --- a/src/test/ui/const-generics/fn-const-param-call.rs +++ b/src/test/ui/const-generics/fn-const-param-call.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn function() -> u32 { 17 diff --git a/src/test/ui/const-generics/fn-const-param-call.stderr b/src/test/ui/const-generics/fn-const-param-call.stderr index c677d70374931..9c0f7e3ab9b87 100644 --- a/src/test/ui/const-generics/fn-const-param-call.stderr +++ b/src/test/ui/const-generics/fn-const-param-call.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/fn-const-param-call.rs:3:12 | LL | #![feature(const_generics, const_compare_raw_pointers)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/fn-const-param-infer.rs b/src/test/ui/const-generics/fn-const-param-infer.rs index dc69fa9eea585..08f6e0db31cae 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.rs +++ b/src/test/ui/const-generics/fn-const-param-infer.rs @@ -1,5 +1,5 @@ #![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct Checked bool>; diff --git a/src/test/ui/const-generics/fn-const-param-infer.stderr b/src/test/ui/const-generics/fn-const-param-infer.stderr index 05d2dff8e9868..de41d2984a655 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.stderr +++ b/src/test/ui/const-generics/fn-const-param-infer.stderr @@ -1,21 +1,20 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/fn-const-param-infer.rs:1:12 | LL | #![feature(const_generics, const_compare_raw_pointers)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0308]: mismatched types --> $DIR/fn-const-param-infer.rs:16:31 | LL | let _: Checked = Checked::; - | ---------------- ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}` - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}` | - = note: expected struct `Checked<{not_one as fn(usize) -> bool}>` - found struct `Checked<{not_two as fn(usize) -> bool}>` + = note: expected type `{not_one as fn(usize) -> bool}` + found type `{not_two as fn(usize) -> bool}` error[E0308]: mismatched types --> $DIR/fn-const-param-infer.rs:20:24 @@ -36,14 +35,12 @@ error[E0308]: mismatched types --> $DIR/fn-const-param-infer.rs:25:40 | LL | let _: Checked<{generic::}> = Checked::<{generic::}>; - | ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic:: as fn(usize) -> bool}`, found `{generic:: as fn(usize) -> bool}` - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic:: as fn(usize) -> bool}`, found `{generic:: as fn(usize) -> bool}` | - = note: expected struct `Checked<{generic:: as fn(usize) -> bool}>` - found struct `Checked<{generic:: as fn(usize) -> bool}>` + = note: expected type `{generic:: as fn(usize) -> bool}` + found type `{generic:: as fn(usize) -> bool}` -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted Some errors have detailed explanations: E0282, E0308. For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/fn-taking-const-generic-array.rs b/src/test/ui/const-generics/fn-taking-const-generic-array.rs index d3d17cca4da27..8e16221ed4bd2 100644 --- a/src/test/ui/const-generics/fn-taking-const-generic-array.rs +++ b/src/test/ui/const-generics/fn-taking-const-generic-array.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete use std::fmt::Display; diff --git a/src/test/ui/const-generics/fn-taking-const-generic-array.stderr b/src/test/ui/const-generics/fn-taking-const-generic-array.stderr index d7f8f1364eef8..52fd0a8fec03b 100644 --- a/src/test/ui/const-generics/fn-taking-const-generic-array.stderr +++ b/src/test/ui/const-generics/fn-taking-const-generic-array.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/fn-taking-const-generic-array.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.rs b/src/test/ui/const-generics/forbid-non-structural_match-types.rs index 7bc4f3986eb75..514e215ba1aa2 100644 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.rs +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #[derive(PartialEq, Eq)] struct A; @@ -8,6 +8,6 @@ struct B; // ok struct C; -struct D; //~ ERROR the types of const generic parameters must derive +struct D; //~ ERROR `C` must be annotated with `#[derive(PartialEq, Eq)]` fn main() {} diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.stderr b/src/test/ui/const-generics/forbid-non-structural_match-types.stderr index 0fd9e0599e80e..600be64b1e1b8 100644 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.stderr +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.stderr @@ -1,17 +1,18 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/forbid-non-structural_match-types.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq` +error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter --> $DIR/forbid-non-structural_match-types.rs:11:19 | LL | struct D; | ^ `C` doesn't derive both `PartialEq` and `Eq` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.rs b/src/test/ui/const-generics/foreign-item-const-parameter.rs index 4673c8606c393..41113780de32e 100644 --- a/src/test/ui/const-generics/foreign-item-const-parameter.rs +++ b/src/test/ui/const-generics/foreign-item-const-parameter.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete extern "C" { fn foo(); //~ ERROR foreign items may not have const parameters diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.stderr b/src/test/ui/const-generics/foreign-item-const-parameter.stderr index 999feed2d3b20..ee947943af134 100644 --- a/src/test/ui/const-generics/foreign-item-const-parameter.stderr +++ b/src/test/ui/const-generics/foreign-item-const-parameter.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/foreign-item-const-parameter.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0044]: foreign items may not have const parameters --> $DIR/foreign-item-const-parameter.rs:5:5 @@ -22,6 +23,6 @@ LL | fn bar(_: T); | = help: replace the type or const parameters with concrete types or consts -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0044`. diff --git a/src/test/ui/const-generics/impl-const-generic-struct.rs b/src/test/ui/const-generics/impl-const-generic-struct.rs index 87572e51e8142..4c2aee59ffebe 100644 --- a/src/test/ui/const-generics/impl-const-generic-struct.rs +++ b/src/test/ui/const-generics/impl-const-generic-struct.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct S; diff --git a/src/test/ui/const-generics/impl-const-generic-struct.stderr b/src/test/ui/const-generics/impl-const-generic-struct.stderr index 1eae9c4038959..9d68df07ce677 100644 --- a/src/test/ui/const-generics/impl-const-generic-struct.stderr +++ b/src/test/ui/const-generics/impl-const-generic-struct.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/impl-const-generic-struct.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.rs b/src/test/ui/const-generics/incorrect-number-of-const-args.rs index 7059e9d8348e3..cea64654e11a0 100644 --- a/src/test/ui/const-generics/incorrect-number-of-const-args.rs +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn foo() -> usize { 0 diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.stderr b/src/test/ui/const-generics/incorrect-number-of-const-args.stderr index 6aa1c23176bfa..51064d7f90fb2 100644 --- a/src/test/ui/const-generics/incorrect-number-of-const-args.stderr +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/incorrect-number-of-const-args.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0107]: wrong number of const arguments: expected 2, found 1 --> $DIR/incorrect-number-of-const-args.rs:9:5 @@ -18,6 +19,6 @@ error[E0107]: wrong number of const arguments: expected 2, found 3 LL | foo::<0, 0, 0>(); | ^ unexpected const argument -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/infer_arg_from_pat.rs b/src/test/ui/const-generics/infer_arg_from_pat.rs new file mode 100644 index 0000000000000..7e8152dacc46c --- /dev/null +++ b/src/test/ui/const-generics/infer_arg_from_pat.rs @@ -0,0 +1,27 @@ +// run-pass +// +// see issue #70529 +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +struct A { + arr: [u8; N], +} + +impl A { + fn new() -> Self { + A { + arr: [0; N], + } + } + + fn value(&self) -> usize { + N + } +} + +fn main() { + let a = A::new(); + let [_, _] = a.arr; + assert_eq!(a.value(), 2); +} diff --git a/src/test/ui/const-generics/infer_arg_from_pat.stderr b/src/test/ui/const-generics/infer_arg_from_pat.stderr new file mode 100644 index 0000000000000..f52e5e49a3bde --- /dev/null +++ b/src/test/ui/const-generics/infer_arg_from_pat.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/infer_arg_from_pat.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.rs b/src/test/ui/const-generics/infer_arr_len_from_pat.rs new file mode 100644 index 0000000000000..cede9ea045d44 --- /dev/null +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.rs @@ -0,0 +1,13 @@ +// check-pass +// +// see issue #70529 +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +fn as_chunks() -> [u8; N] { + loop {} +} + +fn main() { + let [_, _] = as_chunks(); +} diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.stderr b/src/test/ui/const-generics/infer_arr_len_from_pat.stderr new file mode 100644 index 0000000000000..dfadfbb16637a --- /dev/null +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/infer_arr_len_from_pat.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs index 30fbfda112c55..952e05bac30f4 100644 --- a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs +++ b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn takes_closure_of_array_3(f: F) where F: Fn([i32; 3]) { f([1, 2, 3]); diff --git a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.stderr b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.stderr index 7f37f3e2791ec..aadd10e5ccab3 100644 --- a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.stderr +++ b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/integer-literal-generic-arg-in-where-clause.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.rs b/src/test/ui/const-generics/issue-61522-array-len-succ.rs new file mode 100644 index 0000000000000..7c8cdeece8718 --- /dev/null +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.rs @@ -0,0 +1,14 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +pub struct MyArray([u8; COUNT + 1]); +//~^ ERROR constant expression depends on a generic parameter + +impl MyArray { + fn inner(&self) -> &[u8; COUNT + 1] { + //~^ ERROR constant expression depends on a generic parameter + &self.0 + } +} + +fn main() {} diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.stderr b/src/test/ui/const-generics/issue-61522-array-len-succ.stderr new file mode 100644 index 0000000000000..a1fbd5f2025bf --- /dev/null +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.stderr @@ -0,0 +1,27 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61522-array-len-succ.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/issue-61522-array-len-succ.rs:4:40 + | +LL | pub struct MyArray([u8; COUNT + 1]); + | ^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/issue-61522-array-len-succ.rs:8:24 + | +LL | fn inner(&self) -> &[u8; COUNT + 1] { + | ^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs new file mode 100644 index 0000000000000..74f036e6d89b5 --- /dev/null +++ b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs @@ -0,0 +1,16 @@ +// check-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait Trait { + type Assoc; +} + +impl Trait<"0"> for () { + type Assoc = (); +} + +fn main() { + let _: <() as Trait<"0">>::Assoc = (); +} diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.stderr b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.stderr new file mode 100644 index 0000000000000..720420d9cd684 --- /dev/null +++ b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-66596-impl-trait-for-str-const-arg.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs index b810efe73847e..26d74ffb254ce 100644 --- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs +++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs @@ -1,7 +1,7 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct Generic; diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr index 3e0cd8168818f..94a2b673a51ec 100644 --- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr +++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-60818-struct-constructors.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61336-1.rs b/src/test/ui/const-generics/issues/issue-61336-1.rs index 165d3e1c2e601..2135c868bbc70 100644 --- a/src/test/ui/const-generics/issues/issue-61336-1.rs +++ b/src/test/ui/const-generics/issues/issue-61336-1.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete // build-pass diff --git a/src/test/ui/const-generics/issues/issue-61336-1.stderr b/src/test/ui/const-generics/issues/issue-61336-1.stderr index d48d8ff689462..b2c69d57c40b7 100644 --- a/src/test/ui/const-generics/issues/issue-61336-1.stderr +++ b/src/test/ui/const-generics/issues/issue-61336-1.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-61336-1.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61336-2.rs b/src/test/ui/const-generics/issues/issue-61336-2.rs index c5bf6b6ce94a8..52969056f00a5 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.rs +++ b/src/test/ui/const-generics/issues/issue-61336-2.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn f(x: T) -> [T; N] { [x; { N }] diff --git a/src/test/ui/const-generics/issues/issue-61336-2.stderr b/src/test/ui/const-generics/issues/issue-61336-2.stderr index 9ced427b93c65..5f3395223f95d 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.stderr +++ b/src/test/ui/const-generics/issues/issue-61336-2.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-61336-2.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/issue-61336-2.rs:9:5 @@ -12,13 +13,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | [x; { N }] | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/issue-61336-2.rs:8:6 - | -LL | fn g(x: T) -> [T; N] { - | ^ = note: the `Copy` trait is required because the repeated element will be copied +help: consider restricting type parameter `T` + | +LL | fn g(x: T) -> [T; N] { + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-61336.rs b/src/test/ui/const-generics/issues/issue-61336.rs index 7e84e62d8be42..eb0f309762764 100644 --- a/src/test/ui/const-generics/issues/issue-61336.rs +++ b/src/test/ui/const-generics/issues/issue-61336.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn f(x: T) -> [T; N] { [x; N] diff --git a/src/test/ui/const-generics/issues/issue-61336.stderr b/src/test/ui/const-generics/issues/issue-61336.stderr index ace7955fbdd77..0eee37df3dd52 100644 --- a/src/test/ui/const-generics/issues/issue-61336.stderr +++ b/src/test/ui/const-generics/issues/issue-61336.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-61336.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/issue-61336.rs:9:5 @@ -12,13 +13,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | [x; N] | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/issue-61336.rs:8:6 - | -LL | fn g(x: T) -> [T; N] { - | ^ = note: the `Copy` trait is required because the repeated element will be copied +help: consider restricting type parameter `T` + | +LL | fn g(x: T) -> [T; N] { + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-61422.rs b/src/test/ui/const-generics/issues/issue-61422.rs index 4fa150ffef09e..7e7ef6867ed07 100644 --- a/src/test/ui/const-generics/issues/issue-61422.rs +++ b/src/test/ui/const-generics/issues/issue-61422.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete use std::mem; diff --git a/src/test/ui/const-generics/issues/issue-61422.stderr b/src/test/ui/const-generics/issues/issue-61422.stderr index 166bd3c2d3b67..69bbaada69187 100644 --- a/src/test/ui/const-generics/issues/issue-61422.stderr +++ b/src/test/ui/const-generics/issues/issue-61422.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-61422.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61432.rs b/src/test/ui/const-generics/issues/issue-61432.rs index 832095ce54206..0440468e9e622 100644 --- a/src/test/ui/const-generics/issues/issue-61432.rs +++ b/src/test/ui/const-generics/issues/issue-61432.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn promote() { // works: diff --git a/src/test/ui/const-generics/issues/issue-61432.stderr b/src/test/ui/const-generics/issues/issue-61432.stderr index 33f77b028104e..1d547b1b6c98e 100644 --- a/src/test/ui/const-generics/issues/issue-61432.stderr +++ b/src/test/ui/const-generics/issues/issue-61432.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-61432.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61747.rs b/src/test/ui/const-generics/issues/issue-61747.rs index 64674bb894e1f..9e0572d3568cb 100644 --- a/src/test/ui/const-generics/issues/issue-61747.rs +++ b/src/test/ui/const-generics/issues/issue-61747.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct Const; diff --git a/src/test/ui/const-generics/issues/issue-61747.stderr b/src/test/ui/const-generics/issues/issue-61747.stderr index ccf36a7f805ec..2e405370dc0df 100644 --- a/src/test/ui/const-generics/issues/issue-61747.stderr +++ b/src/test/ui/const-generics/issues/issue-61747.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-61747.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61935.rs b/src/test/ui/const-generics/issues/issue-61935.rs new file mode 100644 index 0000000000000..5c987e63a9e07 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61935.rs @@ -0,0 +1,24 @@ +// check-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait Foo {} + +impl Foo for [(); N] + where + Self:FooImpl<{N==0}> +{} + +trait FooImpl{} + +impl FooImpl for [(); 0] {} + +impl FooImpl for [();N] {} + +fn foo(_: impl Foo) {} + +fn main() { + foo([]); + foo([()]); +} diff --git a/src/test/ui/const-generics/issues/issue-61935.stderr b/src/test/ui/const-generics/issues/issue-61935.stderr new file mode 100644 index 0000000000000..cf0c0e24a7604 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61935.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61935.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs index 4e5e4d045c8f2..2f3b5c5dc5b89 100644 --- a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs +++ b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete pub trait BitLen: Sized { const BIT_LEN: usize; diff --git a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.stderr b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.stderr index 72f0b333c5a35..a9abb877c094c 100644 --- a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.stderr +++ b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-62187-encountered-polymorphic-const.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information warning: unused variable: `foo` --> $DIR/issue-62187-encountered-polymorphic-const.rs:15:9 @@ -14,3 +15,5 @@ LL | let foo = <[u8; 2]>::BIT_LEN; | = note: `#[warn(unused_variables)]` on by default +warning: 2 warnings emitted + diff --git a/src/test/ui/const-generics/issues/issue-62220.rs b/src/test/ui/const-generics/issues/issue-62220.rs new file mode 100644 index 0000000000000..c95b306320175 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62220.rs @@ -0,0 +1,22 @@ +// build-pass +#![allow(incomplete_features)] + +#![feature(const_generics)] +pub struct Vector([T; N]); + +pub type TruncatedVector = Vector; + +impl Vector { + /// Drop the last component and return the vector with one fewer dimension. + pub fn trunc(self) -> (TruncatedVector, T) { + unimplemented!() + } +} + +fn vec4(a: T, b: T, c: T, d: T) -> Vector { + Vector([a, b, c, d]) +} + +fn main() { + let (_xyz, _w): (TruncatedVector, u32) = vec4(0u32, 1, 2, 3).trunc(); +} diff --git a/src/test/ui/const-generics/issues/issue-62456.rs b/src/test/ui/const-generics/issues/issue-62456.rs index 14b1190df0f99..37947ad1b331c 100644 --- a/src/test/ui/const-generics/issues/issue-62456.rs +++ b/src/test/ui/const-generics/issues/issue-62456.rs @@ -1,10 +1,9 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash - -// build-pass +//~^ WARN the feature `const_generics` is incomplete fn foo() { let _ = [0u64; N + 1]; + //~^ ERROR constant expression depends on a generic parameter } fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-62456.stderr b/src/test/ui/const-generics/issues/issue-62456.stderr index 47dd3c01fa9e0..0454fed670598 100644 --- a/src/test/ui/const-generics/issues/issue-62456.stderr +++ b/src/test/ui/const-generics/issues/issue-62456.stderr @@ -1,8 +1,19 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-62456.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/issue-62456.rs:5:20 + | +LL | let _ = [0u64; N + 1]; + | ^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-62504.rs b/src/test/ui/const-generics/issues/issue-62504.rs index cd3cfaac3b95b..4e05aadd3930f 100644 --- a/src/test/ui/const-generics/issues/issue-62504.rs +++ b/src/test/ui/const-generics/issues/issue-62504.rs @@ -7,16 +7,16 @@ trait HasSize { const SIZE: usize; } -impl HasSize for ArrayHolder<{ X }> { +impl HasSize for ArrayHolder { const SIZE: usize = X; } struct ArrayHolder([u32; X]); -impl ArrayHolder<{ X }> { +impl ArrayHolder { pub const fn new() -> Self { ArrayHolder([0; Self::SIZE]) - //~^ ERROR: mismatched types + //~^ ERROR constant expression depends on a generic parameter } } diff --git a/src/test/ui/const-generics/issues/issue-62504.stderr b/src/test/ui/const-generics/issues/issue-62504.stderr index 4482389bbdd49..f09af76325e96 100644 --- a/src/test/ui/const-generics/issues/issue-62504.stderr +++ b/src/test/ui/const-generics/issues/issue-62504.stderr @@ -1,12 +1,10 @@ -error[E0308]: mismatched types - --> $DIR/issue-62504.rs:18:21 +error: constant expression depends on a generic parameter + --> $DIR/issue-62504.rs:18:25 | LL | ArrayHolder([0; Self::SIZE]) - | ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE` + | ^^^^^^^^^^ | - = note: expected array `[u32; _]` - found array `[u32; _]` + = note: this may fail depending on what value the parameter takes error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.rs b/src/test/ui/const-generics/issues/issue-62579-no-match.rs index 0ff7ddc41fe4c..7eaf5eea0787b 100644 --- a/src/test/ui/const-generics/issues/issue-62579-no-match.rs +++ b/src/test/ui/const-generics/issues/issue-62579-no-match.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete #[derive(PartialEq, Eq)] struct NoMatch; diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.stderr b/src/test/ui/const-generics/issues/issue-62579-no-match.stderr index 759d5fdeb4c16..9fb9b5b13d8d5 100644 --- a/src/test/ui/const-generics/issues/issue-62579-no-match.stderr +++ b/src/test/ui/const-generics/issues/issue-62579-no-match.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-62579-no-match.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs new file mode 100644 index 0000000000000..2bcaa27b4d271 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs @@ -0,0 +1,15 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait A {} +struct B; +impl A for B {} + +fn test() { + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` to be used + unimplemented!() +} + +fn main() { + test::<{ &B }>(); +} diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.stderr new file mode 100644 index 0000000000000..32054e43716cb --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.stderr @@ -0,0 +1,18 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-63322-forbid-dyn.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter + --> $DIR/issue-63322-forbid-dyn.rs:8:18 + | +LL | fn test() { + | ^^^^^^^^^^^^^^ `&'static (dyn A + 'static)` doesn't derive both `PartialEq` and `Eq` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-64519.rs b/src/test/ui/const-generics/issues/issue-64519.rs index 72cce9b4843d7..e9391096b04d4 100644 --- a/src/test/ui/const-generics/issues/issue-64519.rs +++ b/src/test/ui/const-generics/issues/issue-64519.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct Foo { state: Option<[u8; D]>, diff --git a/src/test/ui/const-generics/issues/issue-64519.stderr b/src/test/ui/const-generics/issues/issue-64519.stderr index d368f39d903a0..6552aea4ad1f1 100644 --- a/src/test/ui/const-generics/issues/issue-64519.stderr +++ b/src/test/ui/const-generics/issues/issue-64519.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-64519.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-66205.rs b/src/test/ui/const-generics/issues/issue-66205.rs index 2e47b4d1882f2..76bde1815be18 100644 --- a/src/test/ui/const-generics/issues/issue-66205.rs +++ b/src/test/ui/const-generics/issues/issue-66205.rs @@ -1,10 +1,10 @@ -// check-pass - #![allow(incomplete_features, dead_code, unconditional_recursion)] #![feature(const_generics)] +#![feature(lazy_normalization_consts)] fn fact() { fact::<{ N - 1 }>(); + //~^ ERROR constant expression depends on a generic parameter } fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-66205.stderr b/src/test/ui/const-generics/issues/issue-66205.stderr new file mode 100644 index 0000000000000..416b675b56d28 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-66205.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-66205.rs:6:12 + | +LL | fact::<{ N - 1 }>(); + | ^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-66906.rs b/src/test/ui/const-generics/issues/issue-66906.rs index 461fe837dac44..486c72d8a349f 100644 --- a/src/test/ui/const-generics/issues/issue-66906.rs +++ b/src/test/ui/const-generics/issues/issue-66906.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete pub struct Tuple; diff --git a/src/test/ui/const-generics/issues/issue-66906.stderr b/src/test/ui/const-generics/issues/issue-66906.stderr index f8710b67b687e..8e8b552f90eb5 100644 --- a/src/test/ui/const-generics/issues/issue-66906.stderr +++ b/src/test/ui/const-generics/issues/issue-66906.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-66906.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-67185-1.rs b/src/test/ui/const-generics/issues/issue-67185-1.rs new file mode 100644 index 0000000000000..b08057851a1ba --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67185-1.rs @@ -0,0 +1,32 @@ +// check-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait Baz { + type Quaks; +} +impl Baz for u8 { + type Quaks = [u16; 3]; +} + +trait Bar {} +impl Bar for [u16; 3] {} +impl Bar for [[u16; 3]; 2] {} + +trait Foo + where + [::Quaks; 2]: Bar, + ::Quaks: Bar, +{ +} + +struct FooImpl; + +impl Foo for FooImpl {} + +fn f(_: impl Foo) {} + +fn main() { + f(FooImpl) +} diff --git a/src/test/ui/const-generics/issues/issue-67185-1.stderr b/src/test/ui/const-generics/issues/issue-67185-1.stderr new file mode 100644 index 0000000000000..9cc797d6d8a01 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67185-1.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-67185-1.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-67185-2.rs b/src/test/ui/const-generics/issues/issue-67185-2.rs new file mode 100644 index 0000000000000..111b718dd5efd --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67185-2.rs @@ -0,0 +1,35 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait Baz { + type Quaks; +} +impl Baz for u8 { + type Quaks = [u16; 3]; +} + +trait Bar {} +impl Bar for [u16; 4] {} +impl Bar for [[u16; 3]; 3] {} + +trait Foo //~ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277] + //~^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277] + where + [::Quaks; 2]: Bar, + ::Quaks: Bar, +{ +} + +struct FooImpl; + +impl Foo for FooImpl {} +//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277] +//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277] + +fn f(_: impl Foo) {} +//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277] +//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277] + +fn main() { + f(FooImpl) +} diff --git a/src/test/ui/const-generics/issues/issue-67185-2.stderr b/src/test/ui/const-generics/issues/issue-67185-2.stderr new file mode 100644 index 0000000000000..7d947a907a0ee --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67185-2.stderr @@ -0,0 +1,112 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-67185-2.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:15:1 + | +LL | / trait Foo +LL | | +LL | | where +LL | | [::Quaks; 2]: Bar, +LL | | ::Quaks: Bar, +LL | | { +LL | | } + | |_^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:15:1 + | +LL | / trait Foo +LL | | +LL | | where +LL | | [::Quaks; 2]: Bar, +LL | | ::Quaks: Bar, +LL | | { +LL | | } + | |_^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:25:6 + | +LL | trait Foo + | --- required by a bound in this +... +LL | ::Quaks: Bar, + | --- required by this bound in `Foo` +... +LL | impl Foo for FooImpl {} + | ^^^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:25:6 + | +LL | trait Foo + | --- required by a bound in this +... +LL | [::Quaks; 2]: Bar, + | --- required by this bound in `Foo` +... +LL | impl Foo for FooImpl {} + | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:29:14 + | +LL | trait Foo + | --- required by a bound in this +... +LL | [::Quaks; 2]: Bar, + | --- required by this bound in `Foo` +... +LL | fn f(_: impl Foo) {} + | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:29:14 + | +LL | trait Foo + | --- required by a bound in this +... +LL | ::Quaks: Bar, + | --- required by this bound in `Foo` +... +LL | fn f(_: impl Foo) {} + | ^^^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error: aborting due to 6 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-67739.rs b/src/test/ui/const-generics/issues/issue-67739.rs index 3d657b0947b18..c8ee182123985 100644 --- a/src/test/ui/const-generics/issues/issue-67739.rs +++ b/src/test/ui/const-generics/issues/issue-67739.rs @@ -1,7 +1,5 @@ // Regression test for #67739 -// check-pass - #![allow(incomplete_features)] #![feature(const_generics)] @@ -12,6 +10,7 @@ pub trait Trait { fn associated_size(&self) -> usize { [0u8; mem::size_of::()]; + //~^ ERROR constant expression depends on a generic parameter 0 } } diff --git a/src/test/ui/const-generics/issues/issue-67739.stderr b/src/test/ui/const-generics/issues/issue-67739.stderr new file mode 100644 index 0000000000000..27a56b8eb02b2 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67739.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-67739.rs:12:15 + | +LL | [0u8; mem::size_of::()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-68615-adt.rs b/src/test/ui/const-generics/issues/issue-68615-adt.rs new file mode 100644 index 0000000000000..140bb28ec5a4f --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68615-adt.rs @@ -0,0 +1,11 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +struct Const {} +type MyConst = Const<{ [] }>; + +fn main() { + let _x = Const::<{ [] }> {}; + let _y = MyConst {}; +} diff --git a/src/test/ui/const-generics/issues/issue-68615-array.rs b/src/test/ui/const-generics/issues/issue-68615-array.rs new file mode 100644 index 0000000000000..c384bc1e36d02 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68615-array.rs @@ -0,0 +1,11 @@ +// check-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +struct Foo {} + +type MyFoo = Foo<{ [] }>; + +fn main() { + let _ = Foo::<{ [] }> {}; +} diff --git a/src/test/ui/const-generics/issues/issue-70125-1.rs b/src/test/ui/const-generics/issues/issue-70125-1.rs index 8b933c078ff26..08a8309d4319f 100644 --- a/src/test/ui/const-generics/issues/issue-70125-1.rs +++ b/src/test/ui/const-generics/issues/issue-70125-1.rs @@ -1,6 +1,6 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete const L: usize = 4; diff --git a/src/test/ui/const-generics/issues/issue-70125-1.stderr b/src/test/ui/const-generics/issues/issue-70125-1.stderr index f28f58cf5ce4d..8ad4b25ae5bc0 100644 --- a/src/test/ui/const-generics/issues/issue-70125-1.stderr +++ b/src/test/ui/const-generics/issues/issue-70125-1.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-70125-1.rs:2:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-70125-2.rs b/src/test/ui/const-generics/issues/issue-70125-2.rs index ea7a68c2f93d9..fb7d4886a7c17 100644 --- a/src/test/ui/const-generics/issues/issue-70125-2.rs +++ b/src/test/ui/const-generics/issues/issue-70125-2.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn main() { <()>::foo(); @@ -13,4 +13,4 @@ trait Foo { } } -impl Foo<{3}> for () {} +impl Foo<3> for () {} diff --git a/src/test/ui/const-generics/issues/issue-70125-2.stderr b/src/test/ui/const-generics/issues/issue-70125-2.stderr index 664aa7ba45161..c1f9634810e48 100644 --- a/src/test/ui/const-generics/issues/issue-70125-2.stderr +++ b/src/test/ui/const-generics/issues/issue-70125-2.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-70125-2.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-70167.rs b/src/test/ui/const-generics/issues/issue-70167.rs index 58fac8e05114a..b53cec80071fd 100644 --- a/src/test/ui/const-generics/issues/issue-70167.rs +++ b/src/test/ui/const-generics/issues/issue-70167.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete pub trait Trait: From<>::Item> { type Item; diff --git a/src/test/ui/const-generics/issues/issue-70167.stderr b/src/test/ui/const-generics/issues/issue-70167.stderr index 4ba3c204097dc..5d647e933c4c5 100644 --- a/src/test/ui/const-generics/issues/issue-70167.stderr +++ b/src/test/ui/const-generics/issues/issue-70167.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-70167.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs index a192ddea9c6ab..c22e61d0ce337 100644 --- a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs +++ b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete trait T { fn f(); diff --git a/src/test/ui/const-generics/issues/issue70273-assoc-fn.stderr b/src/test/ui/const-generics/issues/issue70273-assoc-fn.stderr index 64007ade0f2ed..931701b64b481 100644 --- a/src/test/ui/const-generics/issues/issue70273-assoc-fn.stderr +++ b/src/test/ui/const-generics/issues/issue70273-assoc-fn.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue70273-assoc-fn.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/lazy-normalization/issue-71922.rs b/src/test/ui/const-generics/lazy-normalization/issue-71922.rs new file mode 100644 index 0000000000000..36513f94a9e97 --- /dev/null +++ b/src/test/ui/const-generics/lazy-normalization/issue-71922.rs @@ -0,0 +1,19 @@ +// run-pass +#![feature(const_generics)] +#![allow(incomplete_features)] +trait Foo {} + +impl Foo for [(); N] where Self: FooImpl<{ N == 0 }> {} + +trait FooImpl {} + +impl FooImpl<{ 0u8 == 0u8 }> for [(); 0] {} + +impl FooImpl<{ 0u8 != 0u8 }> for [(); N] {} + +fn foo(_: T) {} + +fn main() { + foo([]); + foo([()]); +} diff --git a/src/test/ui/const-generics/lazy-normalization/issue-71986.rs b/src/test/ui/const-generics/lazy-normalization/issue-71986.rs new file mode 100644 index 0000000000000..048ed18c927bf --- /dev/null +++ b/src/test/ui/const-generics/lazy-normalization/issue-71986.rs @@ -0,0 +1,8 @@ +// check-pass +#![allow(incomplete_features)] +#![feature(const_generics)] + +pub trait Foo {} +pub fn bar>() {} + +fn main() {} diff --git a/src/test/ui/const-generics/mut-ref-const-param-array.rs b/src/test/ui/const-generics/mut-ref-const-param-array.rs index f930fb8796325..9ca1f4552f596 100644 --- a/src/test/ui/const-generics/mut-ref-const-param-array.rs +++ b/src/test/ui/const-generics/mut-ref-const-param-array.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete use std::ops::AddAssign; diff --git a/src/test/ui/const-generics/mut-ref-const-param-array.stderr b/src/test/ui/const-generics/mut-ref-const-param-array.stderr index bd7ae49193eed..acbc2df1d740f 100644 --- a/src/test/ui/const-generics/mut-ref-const-param-array.stderr +++ b/src/test/ui/const-generics/mut-ref-const-param-array.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/mut-ref-const-param-array.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.rs b/src/test/ui/const-generics/raw-ptr-const-param-deref.rs index 745dde3c28766..c498bfe2e9781 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.rs @@ -1,6 +1,6 @@ // run-pass #![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete const A: u32 = 3; diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.stderr index 73221596c8e87..1ffc63ffdac03 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/raw-ptr-const-param-deref.rs:2:12 | LL | #![feature(const_generics, const_compare_raw_pointers)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/raw-ptr-const-param.rs b/src/test/ui/const-generics/raw-ptr-const-param.rs index f0349f469626f..d7d970e952b65 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param.rs @@ -1,5 +1,5 @@ #![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct Const; diff --git a/src/test/ui/const-generics/raw-ptr-const-param.stderr b/src/test/ui/const-generics/raw-ptr-const-param.stderr index d9794f60a19ce..7a665397c1207 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param.stderr @@ -1,22 +1,21 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/raw-ptr-const-param.rs:1:12 | LL | #![feature(const_generics, const_compare_raw_pointers)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0308]: mismatched types --> $DIR/raw-ptr-const-param.rs:7:40 | LL | let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>; - | ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}` - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}` | - = note: expected struct `Const<{0xf as *const u32}>` - found struct `Const<{0xa as *const u32}>` + = note: expected type `{0xf as *const u32}` + found type `{0xa as *const u32}` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.rs b/src/test/ui/const-generics/slice-const-param-mismatch.rs index 73c75ae666805..4f321b02b8277 100644 --- a/src/test/ui/const-generics/slice-const-param-mismatch.rs +++ b/src/test/ui/const-generics/slice-const-param-mismatch.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct ConstString; struct ConstBytes; diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.stderr b/src/test/ui/const-generics/slice-const-param-mismatch.stderr index a588d82318b94..cc21f197e08b1 100644 --- a/src/test/ui/const-generics/slice-const-param-mismatch.stderr +++ b/src/test/ui/const-generics/slice-const-param-mismatch.stderr @@ -1,10 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/slice-const-param-mismatch.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0308]: mismatched types --> $DIR/slice-const-param-mismatch.rs:9:35 @@ -39,6 +40,6 @@ LL | let _: ConstBytes = ConstBytes::; = note: expected struct `ConstBytes` found struct `ConstBytes` -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/slice-const-param.rs b/src/test/ui/const-generics/slice-const-param.rs index 2629caa392106..9668f7ddabb38 100644 --- a/src/test/ui/const-generics/slice-const-param.rs +++ b/src/test/ui/const-generics/slice-const-param.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete pub fn function_with_str() -> &'static str { STRING diff --git a/src/test/ui/const-generics/slice-const-param.stderr b/src/test/ui/const-generics/slice-const-param.stderr index 79214a34fdba0..524bd41a669b4 100644 --- a/src/test/ui/const-generics/slice-const-param.stderr +++ b/src/test/ui/const-generics/slice-const-param.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/slice-const-param.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.rs b/src/test/ui/const-generics/struct-with-invalid-const-param.rs index 207b07bf69514..0b00481d903e0 100644 --- a/src/test/ui/const-generics/struct-with-invalid-const-param.rs +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct S(C); //~ ERROR expected type, found const parameter diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.stderr b/src/test/ui/const-generics/struct-with-invalid-const-param.stderr index a96b071c05f42..a968b26bc2611 100644 --- a/src/test/ui/const-generics/struct-with-invalid-const-param.stderr +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.stderr @@ -7,14 +7,15 @@ LL | struct S(C); | | help: a struct with a similar name exists: `S` | similarly named struct `S` defined here -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/struct-with-invalid-const-param.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0573`. diff --git a/src/test/ui/const-generics/trait-const-args.rs b/src/test/ui/const-generics/trait-const-args.rs new file mode 100644 index 0000000000000..b60d7e8965142 --- /dev/null +++ b/src/test/ui/const-generics/trait-const-args.rs @@ -0,0 +1,29 @@ +// check-pass +#![allow(incomplete_features)] +#![feature(const_generics)] + +struct Const; +trait Foo {} + +impl Foo for Const {} + +fn foo_impl(_: impl Foo<3>) {} + +fn foo_explicit>(_: T) {} + +fn foo_where(_: T) +where + T: Foo<3>, +{ +} + +fn main() { + foo_impl(Const); + foo_impl(Const::<3>); + + foo_explicit(Const); + foo_explicit(Const::<3>); + + foo_where(Const); + foo_where(Const::<3>); +} diff --git a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs index 794048174f903..1aed9cfe92730 100644 --- a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs +++ b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete use std::mem::MaybeUninit; diff --git a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.stderr b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.stderr index 156eddafff010..6077fe5b1ed39 100644 --- a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.stderr +++ b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/transparent-maybeunit-array-wrapper.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/type_of_anon_const.rs b/src/test/ui/const-generics/type_of_anon_const.rs index 776084b77a577..588c7b9523aad 100644 --- a/src/test/ui/const-generics/type_of_anon_const.rs +++ b/src/test/ui/const-generics/type_of_anon_const.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete trait T { fn l() -> usize; diff --git a/src/test/ui/const-generics/type_of_anon_const.stderr b/src/test/ui/const-generics/type_of_anon_const.stderr index 495d34ce09b81..8afed0d39866a 100644 --- a/src/test/ui/const-generics/type_of_anon_const.stderr +++ b/src/test/ui/const-generics/type_of_anon_const.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/type_of_anon_const.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/types-mismatch-const-args.rs b/src/test/ui/const-generics/types-mismatch-const-args.rs index b25b7331017e7..bf517c11262f0 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.rs +++ b/src/test/ui/const-generics/types-mismatch-const-args.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete // tests the diagnostic output of type mismatches for types that have const generics arguments. diff --git a/src/test/ui/const-generics/types-mismatch-const-args.stderr b/src/test/ui/const-generics/types-mismatch-const-args.stderr index 4266fd250b057..53328c2e89bf4 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.stderr +++ b/src/test/ui/const-generics/types-mismatch-const-args.stderr @@ -1,21 +1,20 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/types-mismatch-const-args.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0308]: mismatched types --> $DIR/types-mismatch-const-args.rs:13:41 | LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData }; - | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32` - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32` | - = note: expected struct `A<'_, _, 2u32, _>` - found struct `A<'_, _, 4u32, _>` + = note: expected type `2u32` + found type `4u32` error[E0308]: mismatched types --> $DIR/types-mismatch-const-args.rs:15:41 @@ -25,9 +24,9 @@ LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data | | | expected due to this | - = note: expected struct `A<'a, u16, _, _>` - found struct `A<'b, u32, _, _>` + = note: expected struct `A<'a, u16, {2u32}, {3u32}>` + found struct `A<'b, u32, {2u32}, {3u32}>` -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs index 7942631bb70b9..7473718351e91 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete use std::fmt; diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.stderr b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.stderr index 3c05a354440f0..f41628d5d8ee9 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.stderr +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/uninferred-consts-during-codegen-1.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs index 0cf505906f626..8b95a010473e2 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs @@ -1,7 +1,7 @@ // run-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete use std::fmt; diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.stderr b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.stderr index f27fc531031f9..f1703bc3a2f8d 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.stderr +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/uninferred-consts-during-codegen-2.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/uninferred-consts.rs b/src/test/ui/const-generics/uninferred-consts.rs new file mode 100644 index 0000000000000..3b2bb49197d01 --- /dev/null +++ b/src/test/ui/const-generics/uninferred-consts.rs @@ -0,0 +1,12 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +// taken from https://github.com/rust-lang/rust/issues/70507#issuecomment-615268893 +struct Foo; +impl Foo { + fn foo(self) {} +} +fn main() { + Foo.foo(); + //~^ ERROR type annotations needed +} diff --git a/src/test/ui/const-generics/uninferred-consts.stderr b/src/test/ui/const-generics/uninferred-consts.stderr new file mode 100644 index 0000000000000..a3620084a4289 --- /dev/null +++ b/src/test/ui/const-generics/uninferred-consts.stderr @@ -0,0 +1,20 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/uninferred-consts.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0282]: type annotations needed + --> $DIR/uninferred-consts.rs:10:5 + | +LL | Foo.foo(); + | ^^^^^^^^^ + | + = note: unable to infer the value of a const parameter + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/unused-const-param.rs b/src/test/ui/const-generics/unused-const-param.rs index 8025b3af8f1bf..d9292efc21b74 100644 --- a/src/test/ui/const-generics/unused-const-param.rs +++ b/src/test/ui/const-generics/unused-const-param.rs @@ -1,7 +1,7 @@ // check-pass #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete struct A; // ok diff --git a/src/test/ui/const-generics/unused-const-param.stderr b/src/test/ui/const-generics/unused-const-param.stderr index 27f023eeeff4c..be015a689ae14 100644 --- a/src/test/ui/const-generics/unused-const-param.stderr +++ b/src/test/ui/const-generics/unused-const-param.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/unused-const-param.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/const-generics/unused_braces.rs b/src/test/ui/const-generics/unused_braces.rs new file mode 100644 index 0000000000000..2c3ce7c9eab4d --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.rs @@ -0,0 +1,13 @@ +// check-pass +#![warn(unused_braces)] + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +struct A; + +fn main() { + let _: A<7>; // ok + let _: A<{ 7 }>; //~ WARN unnecessary braces + let _: A<{ 3 + 5 }>; // ok +} diff --git a/src/test/ui/const-generics/unused_braces.stderr b/src/test/ui/const-generics/unused_braces.stderr new file mode 100644 index 0000000000000..e14958ee566ee --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.stderr @@ -0,0 +1,23 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unused_braces.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: unnecessary braces around const expression + --> $DIR/unused_braces.rs:11:14 + | +LL | let _: A<{ 7 }>; + | ^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:2:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/src/test/ui/const-suggest-feature.rs b/src/test/ui/const-suggest-feature.rs new file mode 100644 index 0000000000000..89fafbbe6f044 --- /dev/null +++ b/src/test/ui/const-suggest-feature.rs @@ -0,0 +1,9 @@ +const WRITE: () = unsafe { + *std::ptr::null_mut() = 0; + //~^ ERROR dereferencing raw pointers in constants is unstable + //~| HELP add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable + //~| ERROR constant contains unimplemented expression type + //~| HELP add `#![feature(const_mut_refs)]` to the crate attributes to enable +}; + +fn main() {} diff --git a/src/test/ui/const-suggest-feature.stderr b/src/test/ui/const-suggest-feature.stderr new file mode 100644 index 0000000000000..6b91df6b42d91 --- /dev/null +++ b/src/test/ui/const-suggest-feature.stderr @@ -0,0 +1,21 @@ +error[E0658]: dereferencing raw pointers in constants is unstable + --> $DIR/const-suggest-feature.rs:2:5 + | +LL | *std::ptr::null_mut() = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51911 for more information + = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable + +error[E0019]: constant contains unimplemented expression type + --> $DIR/const-suggest-feature.rs:2:5 + | +LL | *std::ptr::null_mut() = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0019, E0658. +For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/consts/array-literal-index-oob.rs b/src/test/ui/consts/array-literal-index-oob.rs index 492afa9372c62..f36ebf38db4fe 100644 --- a/src/test/ui/consts/array-literal-index-oob.rs +++ b/src/test/ui/consts/array-literal-index-oob.rs @@ -1,5 +1,5 @@ // build-pass -// ignore-pass (emit codegen-time warnings and verify that they are indeed warnings and not errors) +// ignore-pass (test emits codegen-time warnings and verifies that they are not errors) #![warn(const_err, unconditional_panic)] diff --git a/src/test/ui/consts/array-literal-index-oob.stderr b/src/test/ui/consts/array-literal-index-oob.stderr index 6e0e7fedb7b97..08c0231536a7f 100644 --- a/src/test/ui/consts/array-literal-index-oob.stderr +++ b/src/test/ui/consts/array-literal-index-oob.stderr @@ -30,3 +30,5 @@ warning: erroneous constant used LL | &{ [1, 2, 3][4] }; | ^^^^^^^^^^^^^^^^^ referenced constant has errors +warning: 3 warnings emitted + diff --git a/src/test/ui/consts/assoc_const_generic_impl.stderr b/src/test/ui/consts/assoc_const_generic_impl.stderr index 104197fa17fc6..cd27331ad512d 100644 --- a/src/test/ui/consts/assoc_const_generic_impl.stderr +++ b/src/test/ui/consts/assoc_const_generic_impl.stderr @@ -18,5 +18,5 @@ error: erroneous constant encountered LL | let () = Self::I_AM_ZERO_SIZED; | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/consts/const-block.rs b/src/test/ui/consts/const-block.rs index 7172a34c8cfeb..ec99c70f6e0b9 100644 --- a/src/test/ui/consts/const-block.rs +++ b/src/test/ui/consts/const-block.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![allow(dead_code)] #![allow(unused_unsafe)] diff --git a/src/test/ui/consts/const-err.stderr b/src/test/ui/consts/const-err.stderr index 069521e6e4541..ea27aa8fc8da0 100644 --- a/src/test/ui/consts/const-err.stderr +++ b/src/test/ui/consts/const-err.stderr @@ -24,6 +24,6 @@ error[E0080]: erroneous constant used LL | black_box((FOO, FOO)); | ^^^ referenced constant has errors -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/assign-to-static-within-other-static-2.stderr b/src/test/ui/consts/const-eval/assign-to-static-within-other-static-2.stderr index 148b1210d39e1..14dcc074639e5 100644 --- a/src/test/ui/consts/const-eval/assign-to-static-within-other-static-2.stderr +++ b/src/test/ui/consts/const-eval/assign-to-static-within-other-static-2.stderr @@ -3,6 +3,8 @@ error[E0019]: static contains unimplemented expression type | LL | *FOO.0.get() = 5; | ^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr b/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr index cb4d35b9a1809..bf5e476d80045 100644 --- a/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr +++ b/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/assign-to-static-within-other-static.rs:10:5 | LL | FOO = 5; - | ^^^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^^^ modifying a static's initial value from another static's initializer error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/conditional_array_execution.stderr b/src/test/ui/consts/const-eval/conditional_array_execution.stderr index c72a1ed40c5d4..df8efc44c4966 100644 --- a/src/test/ui/consts/const-eval/conditional_array_execution.stderr +++ b/src/test/ui/consts/const-eval/conditional_array_execution.stderr @@ -24,6 +24,6 @@ warning: erroneous constant used LL | println!("{}", FOO); | ^^^ referenced constant has errors -error: aborting due to previous error +error: aborting due to previous error; 2 warnings emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs index 3ae55ebdbaf35..6fd8e9cbc806b 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs @@ -19,7 +19,6 @@ const A_I8_I : [u32; (i8::MAX as usize) + 1] = [0; (i8::MAX + 1) as usize]; //~^ ERROR evaluation of constant value failed -//~| ERROR mismatched types fn main() { foo(&A_I8_I[..]); diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr index 94b7c12fc1a8b..2c5b4607aa4d3 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr @@ -4,16 +4,6 @@ error[E0080]: evaluation of constant value failed LL | = [0; (i8::MAX + 1) as usize]; | ^^^^^^^^^^^^^ attempt to add with overflow -error[E0308]: mismatched types - --> $DIR/const-eval-overflow-3.rs:20:7 - | -LL | = [0; (i8::MAX + 1) as usize]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `128usize`, found `(i8::MAX + 1) as usize` - | - = note: expected array `[u32; 128]` - found array `[u32; _]` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0080, E0308. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs b/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs index e7b88e00febaa..db6f17a671aea 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs @@ -18,7 +18,6 @@ const A_I8_I = [0; (i8::MAX + 1u8) as usize]; //~^ ERROR mismatched types //~| ERROR cannot add `u8` to `i8` -//~| ERROR mismatched types fn main() { foo(&A_I8_I[..]); diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr index aebe4feef8d5f..3da34fe9af7ec 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr @@ -12,16 +12,7 @@ LL | = [0; (i8::MAX + 1u8) as usize]; | = help: the trait `std::ops::Add` is not implemented for `i8` -error[E0308]: mismatched types - --> $DIR/const-eval-overflow-3b.rs:18:7 - | -LL | = [0; (i8::MAX + 1u8) as usize]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `128usize`, found `(i8::MAX + 1u8) as usize` - | - = note: expected array `[u32; 128]` - found array `[u32; _]` - -error: aborting due to 3 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/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr index 305f259eac22d..d24491e1bc5cb 100644 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:25:5 | LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes | = 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. @@ -36,7 +36,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:37:5 | LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc22, but expected initialized plain (non-pointer) bytes | = 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. @@ -76,7 +76,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:52:5 | LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc47, but expected initialized plain (non-pointer) bytes | = 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. @@ -100,7 +100,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:61:5 | LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc62, but expected initialized plain (non-pointer) bytes | = 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. @@ -148,7 +148,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:79:5 | LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc86, but expected initialized plain (non-pointer) bytes | = 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. @@ -188,7 +188,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:94:5 | LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc101, but expected initialized plain (non-pointer) bytes | = 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. @@ -212,7 +212,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:103:5 | LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc110, but expected initialized plain (non-pointer) bytes | = 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. diff --git a/src/test/ui/consts/const-eval/const_fn_ptr.rs b/src/test/ui/consts/const-eval/const_fn_ptr.rs index 9b94b45f52264..045fe9ad11a91 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr.rs +++ b/src/test/ui/consts/const-eval/const_fn_ptr.rs @@ -9,15 +9,15 @@ const X: fn(usize) -> usize = double; const X_CONST: fn(usize) -> usize = double_const; const fn bar(x: usize) -> usize { - X(x) //~ WARNING skipping const checks + X(x) } const fn bar_const(x: usize) -> usize { - X_CONST(x) //~ WARNING skipping const checks + X_CONST(x) } const fn foo(x: fn(usize) -> usize, y: usize) -> usize { - x(y) //~ WARNING skipping const checks + x(y) } fn main() { diff --git a/src/test/ui/consts/const-eval/const_fn_ptr.stderr b/src/test/ui/consts/const-eval/const_fn_ptr.stderr index 19fa39603460b..d0ae94079da2e 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr.stderr +++ b/src/test/ui/consts/const-eval/const_fn_ptr.stderr @@ -1,18 +1,20 @@ warning: skipping const checks + | +help: skipping check that does not even have a feature gate --> $DIR/const_fn_ptr.rs:12:5 | LL | X(x) | ^^^^ - -warning: skipping const checks +help: skipping check that does not even have a feature gate --> $DIR/const_fn_ptr.rs:16:5 | LL | X_CONST(x) | ^^^^^^^^^^ - -warning: skipping const checks +help: skipping check that does not even have a feature gate --> $DIR/const_fn_ptr.rs:20:5 | LL | x(y) | ^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const-eval/const_fn_ptr_fail.rs b/src/test/ui/consts/const-eval/const_fn_ptr_fail.rs index 90d3cba07a598..14bd6558e7f89 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr_fail.rs +++ b/src/test/ui/consts/const-eval/const_fn_ptr_fail.rs @@ -8,7 +8,6 @@ const X: fn(usize) -> usize = double; const fn bar(x: usize) -> usize { X(x) // FIXME: this should error someday - //~^ WARN: skipping const checks } fn main() {} diff --git a/src/test/ui/consts/const-eval/const_fn_ptr_fail.stderr b/src/test/ui/consts/const-eval/const_fn_ptr_fail.stderr index e80f363ff8be4..0a7182fd39c68 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr_fail.stderr +++ b/src/test/ui/consts/const-eval/const_fn_ptr_fail.stderr @@ -1,6 +1,10 @@ warning: skipping const checks + | +help: skipping check that does not even have a feature gate --> $DIR/const_fn_ptr_fail.rs:10:5 | LL | X(x) // FIXME: this should error someday | ^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs index 81f53826d8103..f67871e6142ef 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs +++ b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs @@ -10,7 +10,7 @@ fn double(x: usize) -> usize { const X: fn(usize) -> usize = double; const fn bar(x: fn(usize) -> usize, y: usize) -> usize { - x(y) //~ WARN skipping const checks + x(y) } const Y: usize = bar(X, 2); // FIXME: should fail to typeck someday diff --git a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr index 4c3ebece0a839..90ee2afa315d8 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr +++ b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr @@ -1,9 +1,3 @@ -warning: skipping const checks - --> $DIR/const_fn_ptr_fail2.rs:13:5 - | -LL | x(y) - | ^^^^ - error[E0080]: evaluation of constant expression failed --> $DIR/const_fn_ptr_fail2.rs:20:5 | @@ -24,6 +18,14 @@ LL | assert_eq!(Z, 4); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const_fn_ptr_fail2.rs:13:5 + | +LL | x(y) + | ^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/double_check2.stderr b/src/test/ui/consts/const-eval/double_check2.stderr index 28e0922ecafa6..81fa5c0df13f6 100644 --- a/src/test/ui/consts/const-eval/double_check2.stderr +++ b/src/test/ui/consts/const-eval/double_check2.stderr @@ -5,7 +5,7 @@ LL | / static FOO: (&Foo, &Bar) = unsafe {( LL | | Union { u8: &BAR }.foo, LL | | Union { u8: &BAR }.bar, LL | | )}; - | |___^ type validation failed: encountered 5 at .1., but expected a valid enum discriminant + | |___^ type validation failed: encountered 0x05 at .1., but expected a valid enum discriminant | = 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. diff --git a/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs b/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs index 4444cdfcda9c7..e514682af9c20 100644 --- a/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs +++ b/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs @@ -1,4 +1,5 @@ -// check-pass +// build-pass (tests post-monomorphisation failure) +#![crate_type = "lib"] pub trait Nullable { const NULL: Self; @@ -13,6 +14,3 @@ impl Nullable for *const T { *self == Self::NULL } } - -fn main() { -} diff --git a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr index 8fadfabe41c93..d78e0da00f5e1 100644 --- a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr +++ b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr @@ -18,5 +18,5 @@ error: erroneous constant encountered LL | let _ = PrintName::::VOID; | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/consts/const-eval/issue-43197.stderr b/src/test/ui/consts/const-eval/issue-43197.stderr index 668d061b799d0..8aaae9fe6a7d1 100644 --- a/src/test/ui/consts/const-eval/issue-43197.stderr +++ b/src/test/ui/consts/const-eval/issue-43197.stderr @@ -44,6 +44,6 @@ warning: erroneous constant used LL | println!("{} {}", X, Y); | ^ referenced constant has errors -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 4 warnings emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/issue-52442.rs b/src/test/ui/consts/const-eval/issue-52442.rs index df4cc8e302677..07fb491015a85 100644 --- a/src/test/ui/consts/const-eval/issue-52442.rs +++ b/src/test/ui/consts/const-eval/issue-52442.rs @@ -1,4 +1,6 @@ fn main() { [(); { &loop { break } as *const _ as usize } ]; //~^ ERROR `loop` is not allowed in a `const` + //~| ERROR casting pointers to integers in constants is unstable + //~| ERROR evaluation of constant value failed } diff --git a/src/test/ui/consts/const-eval/issue-52442.stderr b/src/test/ui/consts/const-eval/issue-52442.stderr index 0ea974f1f6666..eda2dbf0b6b15 100644 --- a/src/test/ui/consts/const-eval/issue-52442.stderr +++ b/src/test/ui/consts/const-eval/issue-52442.stderr @@ -7,6 +7,22 @@ LL | [(); { &loop { break } as *const _ as usize } ]; = note: see issue #52000 for more information = help: add `#![feature(const_loop)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: casting pointers to integers in constants is unstable + --> $DIR/issue-52442.rs:2:13 + | +LL | [(); { &loop { break } as *const _ as usize } ]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51910 for more information + = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable + +error[E0080]: evaluation of constant value failed + --> $DIR/issue-52442.rs:2:13 + | +LL | [(); { &loop { break } as *const _ as usize } ]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0080, E0658. +For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/issue-70723.rs b/src/test/ui/consts/const-eval/issue-70723.rs new file mode 100644 index 0000000000000..8b79d5d53c5c2 --- /dev/null +++ b/src/test/ui/consts/const-eval/issue-70723.rs @@ -0,0 +1,5 @@ +#![feature(const_loop)] + +static _X: () = loop {}; //~ ERROR could not evaluate static initializer + +fn main() {} diff --git a/src/test/ui/consts/const-eval/issue-70723.stderr b/src/test/ui/consts/const-eval/issue-70723.stderr new file mode 100644 index 0000000000000..687d6565a7163 --- /dev/null +++ b/src/test/ui/consts/const-eval/issue-70723.stderr @@ -0,0 +1,9 @@ +error[E0080]: could not evaluate static initializer + --> $DIR/issue-70723.rs:3:17 + | +LL | static _X: () = loop {}; + | ^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/issue-70804-fn-subtyping.rs b/src/test/ui/consts/const-eval/issue-70804-fn-subtyping.rs new file mode 100644 index 0000000000000..59d46ea66c9ca --- /dev/null +++ b/src/test/ui/consts/const-eval/issue-70804-fn-subtyping.rs @@ -0,0 +1,10 @@ +// check-pass +#![feature(const_fn)] + +const fn nested(x: (for<'a> fn(&'a ()), String)) -> (fn(&'static ()), String) { + x +} + +pub const TEST: (fn(&'static ()), String) = nested((|_x| (), String::new())); + +fn main() {} diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.rs b/src/test/ui/consts/const-eval/match-test-ptr-null.rs index 5cfe36f57e647..80494d1662987 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.rs +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.rs @@ -2,7 +2,7 @@ fn main() { // Make sure match uses the usual pointer comparison code path -- i.e., it should complain // that pointer comparison is disallowed, not that parts of a pointer are accessed as raw // bytes. - let _: [u8; 0] = [4; { //~ ERROR mismatched types + let _: [u8; 0] = [4; { match &1 as *const i32 as usize { //~^ ERROR casting pointers to integers in constants //~| ERROR `match` is not allowed in a `const` diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr index 7c4da5e7d86ca..b47f6d5f845fe 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr @@ -28,30 +28,7 @@ error[E0080]: evaluation of constant value failed LL | match &1 as *const i32 as usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants -error[E0308]: mismatched types - --> $DIR/match-test-ptr-null.rs:5:22 - | -LL | let _: [u8; 0] = [4; { - | ____________-------___^ - | | | - | | expected due to this -LL | | match &1 as *const i32 as usize { -LL | | -LL | | -... | -LL | | } -LL | | }]; - | |______^ expected `0usize`, found `{ - match &1 as *const i32 as usize { - 0 => 42, - n => n, - } - }` - | - = note: expected array `[u8; 0]` - found array `[u8; _]` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0080, E0308, E0658. +Some errors have detailed explanations: E0080, E0658. For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/mod-static-with-const-fn.stderr b/src/test/ui/consts/const-eval/mod-static-with-const-fn.stderr index 50cd3214507a3..44ae1ecf04718 100644 --- a/src/test/ui/consts/const-eval/mod-static-with-const-fn.stderr +++ b/src/test/ui/consts/const-eval/mod-static-with-const-fn.stderr @@ -3,6 +3,8 @@ error[E0019]: static contains unimplemented expression type | LL | *FOO.0.get() = 5; | ^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants --> $DIR/mod-static-with-const-fn.rs:21:5 diff --git a/src/test/ui/consts/const-eval/nrvo.rs b/src/test/ui/consts/const-eval/nrvo.rs new file mode 100644 index 0000000000000..1d2c6acc06cd5 --- /dev/null +++ b/src/test/ui/consts/const-eval/nrvo.rs @@ -0,0 +1,26 @@ +// run-pass + +// When the NRVO is applied, the return place (`_0`) gets treated like a normal local. For example, +// its address may be taken and it may be written to indirectly. Ensure that MIRI can handle this. + +#![feature(const_mut_refs)] + +#[inline(never)] // Try to ensure that MIR optimizations don't optimize this away. +const fn init(buf: &mut [u8; 1024]) { + buf[33] = 3; + buf[444] = 4; +} + +const fn nrvo() -> [u8; 1024] { + let mut buf = [0; 1024]; + init(&mut buf); + buf +} + +const BUF: [u8; 1024] = nrvo(); + +fn main() { + assert_eq!(BUF[33], 3); + assert_eq!(BUF[19], 0); + assert_eq!(BUF[444], 4); +} diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr index ea4eba89eb7fc..979f4a5904d1c 100644 --- a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr +++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr @@ -19,6 +19,6 @@ error[E0080]: erroneous constant used LL | let _ = PrintName::VOID; | ^^^^^^^^^^^^^^^ referenced constant has errors -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/panic-never-type.stderr b/src/test/ui/consts/const-eval/panic-never-type.stderr index 28333c511dca4..af68a2ff44211 100644 --- a/src/test/ui/consts/const-eval/panic-never-type.stderr +++ b/src/test/ui/consts/const-eval/panic-never-type.stderr @@ -19,6 +19,6 @@ error[E0080]: erroneous constant used LL | let _ = VOID; | ^^^^ referenced constant has errors -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr index 94c1593240bc6..a545503ce8955 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr @@ -76,3 +76,5 @@ warning: this operation will panic at runtime LL | let _x = 1 / (false as u32); | ^^^^^^^^^^^^^^^^^^ attempt to divide by zero +warning: 10 warnings emitted + diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr index 034dea06568e0..4887826178244 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr @@ -70,3 +70,5 @@ warning: this operation will panic at runtime LL | let _x = 1 / (false as u32); | ^^^^^^^^^^^^^^^^^^ attempt to divide by zero +warning: 9 warnings emitted + diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr index 94c1593240bc6..a545503ce8955 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr @@ -76,3 +76,5 @@ warning: this operation will panic at runtime LL | let _x = 1 / (false as u32); | ^^^^^^^^^^^^^^^^^^ attempt to divide by zero +warning: 10 warnings emitted + diff --git a/src/test/ui/consts/const-eval/promoted_errors.rs b/src/test/ui/consts/const-eval/promoted_errors.rs index 3ab6ce28478c3..142ce75eebc80 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.rs +++ b/src/test/ui/consts/const-eval/promoted_errors.rs @@ -4,7 +4,7 @@ //[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O // build-pass -// ignore-pass (emit codegen-time warnings and verify that they are indeed warnings and not errors) +// ignore-pass (test emits codegen-time warnings and verifies that they are not errors) #![warn(const_err, arithmetic_overflow, unconditional_panic)] diff --git a/src/test/ui/consts/const-eval/pub_const_err.stderr b/src/test/ui/consts/const-eval/pub_const_err.stderr index ded2df4e013a3..1f1dd203a6401 100644 --- a/src/test/ui/consts/const-eval/pub_const_err.stderr +++ b/src/test/ui/consts/const-eval/pub_const_err.stderr @@ -12,3 +12,5 @@ note: the lint level is defined here LL | #![warn(const_err)] | ^^^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const-eval/pub_const_err_bin.stderr b/src/test/ui/consts/const-eval/pub_const_err_bin.stderr index 570a8e49319a2..3ae0a11026f38 100644 --- a/src/test/ui/consts/const-eval/pub_const_err_bin.stderr +++ b/src/test/ui/consts/const-eval/pub_const_err_bin.stderr @@ -12,3 +12,5 @@ note: the lint level is defined here LL | #![warn(const_err)] | ^^^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const-eval/ref_to_int_match.stderr b/src/test/ui/consts/const-eval/ref_to_int_match.stderr index 17f8744ed9fd7..cb0ba5d9929b9 100644 --- a/src/test/ui/consts/const-eval/ref_to_int_match.stderr +++ b/src/test/ui/consts/const-eval/ref_to_int_match.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ref_to_int_match.rs:25:1 | LL | const BAR: Int = unsafe { Foo { r: &42 }.f }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes | = 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. diff --git a/src/test/ui/consts/const-eval/transmute-const.stderr b/src/test/ui/consts/const-eval/transmute-const.stderr index e93a6887ba8f7..0de6ead4f525f 100644 --- a/src/test/ui/consts/const-eval/transmute-const.stderr +++ b/src/test/ui/consts/const-eval/transmute-const.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/transmute-const.rs:5:1 | LL | static FOO: bool = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3, but expected a boolean + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03, but expected a boolean | = 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. diff --git a/src/test/ui/consts/const-eval/ub-enum.rs b/src/test/ui/consts/const-eval/ub-enum.rs index 1922d59891f1e..c49997c6c33f6 100644 --- a/src/test/ui/consts/const-eval/ub-enum.rs +++ b/src/test/ui/consts/const-eval/ub-enum.rs @@ -1,3 +1,4 @@ +// normalize-stderr-64bit "0x0000000000" -> "0x00" #![feature(const_transmute, never_type)] #![allow(const_err)] // make sure we cannot allow away the errors tested here diff --git a/src/test/ui/consts/const-eval/ub-enum.stderr b/src/test/ui/consts/const-eval/ub-enum.stderr index 10a3d2fa1ab99..d8dafac3e70a1 100644 --- a/src/test/ui/consts/const-eval/ub-enum.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.stderr @@ -1,53 +1,53 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:23:1 + --> $DIR/ub-enum.rs:24:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 1, but expected a valid enum discriminant + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000001, but expected a valid enum discriminant | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:26:1 + --> $DIR/ub-enum.rs:27:1 | LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc8 at ., but expected initialized plain (non-pointer) bytes | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:29:1 + --> $DIR/ub-enum.rs:30:1 | LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .0., but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc13 at .0., but expected initialized plain (non-pointer) bytes | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:41:1 + --> $DIR/ub-enum.rs:42:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected a valid enum discriminant + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000000, but expected a valid enum discriminant | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:43:1 + --> $DIR/ub-enum.rs:44:1 | LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc20 at ., but expected initialized plain (non-pointer) bytes | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:46:1 + --> $DIR/ub-enum.rs:47:1 | LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .0., but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc25 at .0., but expected initialized plain (non-pointer) bytes | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:55:1 + --> $DIR/ub-enum.rs:56:1 | LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at ., but expected initialized plain (non-pointer) bytes @@ -55,15 +55,15 @@ LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:59:1 + --> $DIR/ub-enum.rs:60:1 | LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc32 at ., but expected initialized plain (non-pointer) bytes | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:76:1 + --> $DIR/ub-enum.rs:77:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at ..0 @@ -71,7 +71,7 @@ LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:78:1 + --> $DIR/ub-enum.rs:79:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at ..0 @@ -79,15 +79,15 @@ LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:86:1 + --> $DIR/ub-enum.rs:87:1 | LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 4294967295 at ..0.1, but expected a valid unicode codepoint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0xffffffff at ..0.1, but expected a valid unicode codepoint | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:90:1 + --> $DIR/ub-enum.rs:91:1 | LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(1u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at ..0.1 @@ -95,7 +95,7 @@ LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:92:1 + --> $DIR/ub-enum.rs:93:1 | LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(1u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at ..0.1 diff --git a/src/test/ui/consts/const-eval/ub-int-array.rs b/src/test/ui/consts/const-eval/ub-int-array.rs new file mode 100644 index 0000000000000..8907b0c160f88 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-int-array.rs @@ -0,0 +1,65 @@ +#![feature(const_transmute)] +#![allow(const_err)] // make sure we cannot allow away the errors tested here + +//! Test the "array of int" fast path in validity checking, and in particular whether it +//! points at the right array element. + +use std::mem; + +#[repr(C)] +union MaybeUninit { + uninit: (), + init: T, +} + +const UNINIT_INT_0: [u32; 3] = unsafe { +//~^ ERROR it is undefined behavior to use this value +//~| type validation failed: encountered uninitialized bytes at [0] + [ + MaybeUninit { uninit: () }.init, + 1, + 2, + ] +}; +const UNINIT_INT_1: [u32; 3] = unsafe { +//~^ ERROR it is undefined behavior to use this value +//~| type validation failed: encountered uninitialized bytes at [1] + mem::transmute( + [ + 0u8, + 0u8, + 0u8, + 0u8, + 1u8, + MaybeUninit { uninit: () }.init, + 1u8, + 1u8, + 2u8, + 2u8, + MaybeUninit { uninit: () }.init, + 2u8, + ] + ) +}; +const UNINIT_INT_2: [u32; 3] = unsafe { +//~^ ERROR it is undefined behavior to use this value +//~| type validation failed: encountered uninitialized bytes at [2] + mem::transmute( + [ + 0u8, + 0u8, + 0u8, + 0u8, + 1u8, + 1u8, + 1u8, + 1u8, + 2u8, + 2u8, + 2u8, + MaybeUninit { uninit: () }.init, + ] + ) +}; + +fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-int-array.stderr b/src/test/ui/consts/const-eval/ub-int-array.stderr new file mode 100644 index 0000000000000..b4a3c63b5a103 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-int-array.stderr @@ -0,0 +1,45 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:15:1 + | +LL | / const UNINIT_INT_0: [u32; 3] = unsafe { +LL | | +LL | | +LL | | [ +... | +LL | | ] +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [0] + | + = 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. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:24:1 + | +LL | / const UNINIT_INT_1: [u32; 3] = unsafe { +LL | | +LL | | +LL | | mem::transmute( +... | +LL | | ) +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [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. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:44:1 + | +LL | / const UNINIT_INT_2: [u32; 3] = unsafe { +LL | | +LL | | +LL | | mem::transmute( +... | +LL | | ) +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [2] + | + = 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. + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-nonnull.stderr b/src/test/ui/consts/const-eval/ub-nonnull.stderr index adad1b4f7fafe..38e9bdecdb9d2 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.stderr @@ -13,7 +13,7 @@ LL | / const OUT_OF_BOUNDS_PTR: NonNull = { unsafe { LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL! LL | | let out_of_bounds_ptr = &ptr[255]; - | | ^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc8 which has size 1 + | | ^^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc11 which has size 1 LL | | mem::transmute(out_of_bounds_ptr) LL | | } }; | |____- diff --git a/src/test/ui/consts/const-eval/ub-ref.rs b/src/test/ui/consts/const-eval/ub-ref.rs index 562ec99111b69..10f4c8c03330e 100644 --- a/src/test/ui/consts/const-eval/ub-ref.rs +++ b/src/test/ui/consts/const-eval/ub-ref.rs @@ -6,11 +6,11 @@ use std::mem; const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; //~^ ERROR it is undefined behavior to use this value -//~^^ type validation failed: encountered an unaligned reference (required 2 byte alignment but found 1) +//~| type validation failed: 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 -//~^^ type validation failed: encountered an unaligned box (required 2 byte alignment but found 1) +//~| type validation failed: 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 diff --git a/src/test/ui/consts/const-eval/ub-ref.stderr b/src/test/ui/consts/const-eval/ub-ref.stderr index fb3df8ace4e15..a219679f18264 100644 --- a/src/test/ui/consts/const-eval/ub-ref.stderr +++ b/src/test/ui/consts/const-eval/ub-ref.stderr @@ -34,7 +34,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref.rs:24:1 | LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc16, but expected initialized plain (non-pointer) bytes | = 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. diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.rs b/src/test/ui/consts/const-eval/ub-wide-ptr.rs index 0200bfe9f08f8..f69f6a1109f76 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs @@ -42,11 +42,11 @@ const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; //~^ ERROR it is undefined behavior to use this value -// invalid UTF-8 -const STR_NO_UTF8: &str = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; +// uninitialized byte +const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; //~^ ERROR it is undefined behavior to use this value -// invalid UTF-8 in user-defined str-like -const MYSTR_NO_UTF8: &MyStr = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; +// uninitialized byte in user-defined str-like +const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; //~^ ERROR it is undefined behavior to use this value // # slice @@ -104,6 +104,14 @@ const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u // bad trait object const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) }; //~^ ERROR it is undefined behavior to use this value +const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; +//~^ ERROR it is undefined behavior to use this value +const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; +//~^ ERROR it is undefined behavior to use this value +const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; +//~^ ERROR it is undefined behavior to use this value +const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) }; +//~^ ERROR it is undefined behavior to use this value // bad data *inside* the trait object const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr index 80e60dbb58a5d..47d29ffc9b3c1 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr @@ -41,16 +41,16 @@ LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize: error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:46:1 | -LL | const STR_NO_UTF8: &str = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at . +LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at . | = 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. error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:49:1 | -LL | const MYSTR_NO_UTF8: &MyStr = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at ..0 +LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at ..0 | = 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. @@ -62,7 +62,7 @@ LL | | LL | | let uninit_len = MaybeUninit:: { uninit: () }; LL | | mem::transmute((42, uninit_len)) LL | | }; - | |__^ type validation failed: encountered undefined pointer + | |__^ type validation failed: encountered uninitialized reference | = 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. @@ -102,7 +102,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:75:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .[0], but expected a boolean + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .[0], but expected a boolean | = 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. @@ -110,7 +110,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:81:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at ..0, but expected a boolean + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at ..0, but expected a boolean | = 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. @@ -118,7 +118,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:84:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at ..1[0], but expected a boolean + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at ..1[0], but expected a boolean | = 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. @@ -130,7 +130,7 @@ LL | | LL | | let uninit_len = MaybeUninit:: { uninit: () }; LL | | mem::transmute((42, uninit_len)) LL | | }; - | |__^ type validation failed: encountered undefined pointer + | |__^ type validation failed: encountered uninitialized raw pointer | = 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. @@ -138,7 +138,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:99:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable | = 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. @@ -146,7 +146,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:102:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable | = 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. @@ -154,46 +154,78 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:105:1 | LL | const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer + | + = 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. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:107:1 + | +LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer | = 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. error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:109:1 | -LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .., but expected a boolean +LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) + | + = 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. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:111:1 + | +LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) | = 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. error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:113:1 | +LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) + | + = 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. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:117:1 + | +LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .., but expected a boolean + | + = 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. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:121:1 + | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer | = 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. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:115:1 + --> $DIR/ub-wide-ptr.rs:123:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable | = 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. error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:121:5 + --> $DIR/ub-wide-ptr.rs:129:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid use of NULL pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:125:5 + --> $DIR/ub-wide-ptr.rs:133:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N -error: aborting due to 24 previous errors +error: aborting due to 28 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/union-ice.stderr b/src/test/ui/consts/const-eval/union-ice.stderr index 476f3651740ab..2545167aa02f7 100644 --- a/src/test/ui/consts/const-eval/union-ice.stderr +++ b/src/test/ui/consts/const-eval/union-ice.stderr @@ -27,7 +27,7 @@ LL | | unsafe { UNION.field3 }, ... | LL | | a: 42, LL | | }; - | |__^ type validation failed: encountered undefined bytes at .b[1] + | |__^ type validation failed: encountered uninitialized bytes at .b[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. diff --git a/src/test/ui/consts/const-eval/union-ub.stderr b/src/test/ui/consts/const-eval/union-ub.stderr index 9d90d6e8548d8..fd3e66765c61b 100644 --- a/src/test/ui/consts/const-eval/union-ub.stderr +++ b/src/test/ui/consts/const-eval/union-ub.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/union-ub.rs:31:1 | LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected a boolean + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x2a, but expected a boolean | = 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. diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr index cede356a6b8a1..2adff5fc7d408 100644 --- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr +++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr @@ -5,7 +5,8 @@ LL | unsafe { std::mem::transmute(()) } | ^^^^^^^^^^^^^^^^^^^^^^^ | | | transmuting to uninhabited type - | inside call to `foo` at $DIR/validate_uninhabited_zsts.rs:14:26 + | inside `foo` at $DIR/validate_uninhabited_zsts.rs:5:14 + | inside `FOO` at $DIR/validate_uninhabited_zsts.rs:14:26 ... LL | const FOO: [Empty; 3] = [foo(); 3]; | ----------------------------------- @@ -47,6 +48,6 @@ LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; | = note: enums with no variants have no valid value -error: aborting due to previous error +error: aborting due to previous error; 3 warnings emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-int-arithmetic.rs b/src/test/ui/consts/const-int-arithmetic.rs index 2c3421b7a8d0b..ab24abeba32fe 100644 --- a/src/test/ui/consts/const-int-arithmetic.rs +++ b/src/test/ui/consts/const-int-arithmetic.rs @@ -1,6 +1,5 @@ // run-pass -#![feature(saturating_neg)] #![feature(const_checked_int_methods)] #![feature(const_euclidean_int_methods)] #![feature(const_overflowing_int_methods)] diff --git a/src/test/ui/consts/const-integer-bool-ops.rs b/src/test/ui/consts/const-integer-bool-ops.rs index 6924956bdf706..35915a7a606a4 100644 --- a/src/test/ui/consts/const-integer-bool-ops.rs +++ b/src/test/ui/consts/const-integer-bool-ops.rs @@ -6,7 +6,6 @@ const X: usize = 42 && 39; //~| ERROR mismatched types //~| expected `usize`, found `bool` const ARR: [i32; X] = [99; 34]; -//~^ ERROR evaluation of constant value failed const X1: usize = 42 || 39; //~^ ERROR mismatched types @@ -16,7 +15,6 @@ const X1: usize = 42 || 39; //~| ERROR mismatched types //~| expected `usize`, found `bool` const ARR1: [i32; X1] = [99; 47]; -//~^ ERROR evaluation of constant value failed const X2: usize = -42 || -39; //~^ ERROR mismatched types @@ -26,7 +24,6 @@ const X2: usize = -42 || -39; //~| ERROR mismatched types //~| expected `usize`, found `bool` const ARR2: [i32; X2] = [99; 18446744073709551607]; -//~^ ERROR evaluation of constant value failed const X3: usize = -42 && -39; //~^ ERROR mismatched types @@ -36,43 +33,36 @@ const X3: usize = -42 && -39; //~| ERROR mismatched types //~| expected `usize`, found `bool` const ARR3: [i32; X3] = [99; 6]; -//~^ ERROR evaluation of constant value failed const Y: usize = 42.0 == 42.0; //~^ ERROR mismatched types //~| expected `usize`, found `bool` const ARRR: [i32; Y] = [99; 1]; -//~^ ERROR evaluation of constant value failed const Y1: usize = 42.0 >= 42.0; //~^ ERROR mismatched types //~| expected `usize`, found `bool` const ARRR1: [i32; Y1] = [99; 1]; -//~^ ERROR evaluation of constant value failed const Y2: usize = 42.0 <= 42.0; //~^ ERROR mismatched types //~| expected `usize`, found `bool` const ARRR2: [i32; Y2] = [99; 1]; -//~^ ERROR evaluation of constant value failed const Y3: usize = 42.0 > 42.0; //~^ ERROR mismatched types //~| expected `usize`, found `bool` const ARRR3: [i32; Y3] = [99; 0]; -//~^ ERROR evaluation of constant value failed const Y4: usize = 42.0 < 42.0; //~^ ERROR mismatched types //~| expected `usize`, found `bool` const ARRR4: [i32; Y4] = [99; 0]; -//~^ ERROR evaluation of constant value failed const Y5: usize = 42.0 != 42.0; //~^ ERROR mismatched types //~| expected `usize`, found `bool` const ARRR5: [i32; Y5] = [99; 0]; -//~^ ERROR evaluation of constant value failed fn main() { let _ = ARR; diff --git a/src/test/ui/consts/const-integer-bool-ops.stderr b/src/test/ui/consts/const-integer-bool-ops.stderr index 9001fefd1029f..4e503e5a5c0a4 100644 --- a/src/test/ui/consts/const-integer-bool-ops.stderr +++ b/src/test/ui/consts/const-integer-bool-ops.stderr @@ -16,157 +16,96 @@ error[E0308]: mismatched types LL | const X: usize = 42 && 39; | ^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:8:18 - | -LL | const ARR: [i32; X] = [99; 34]; - | ^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:11:19 + --> $DIR/const-integer-bool-ops.rs:10:19 | LL | const X1: usize = 42 || 39; | ^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:11:25 + --> $DIR/const-integer-bool-ops.rs:10:25 | LL | const X1: usize = 42 || 39; | ^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:11:19 + --> $DIR/const-integer-bool-ops.rs:10:19 | LL | const X1: usize = 42 || 39; | ^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:18:19 - | -LL | const ARR1: [i32; X1] = [99; 47]; - | ^^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:21:19 + --> $DIR/const-integer-bool-ops.rs:19:19 | LL | const X2: usize = -42 || -39; | ^^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:21:26 + --> $DIR/const-integer-bool-ops.rs:19:26 | LL | const X2: usize = -42 || -39; | ^^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:21:19 + --> $DIR/const-integer-bool-ops.rs:19:19 | LL | const X2: usize = -42 || -39; | ^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:28:19 - | -LL | const ARR2: [i32; X2] = [99; 18446744073709551607]; - | ^^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:31:19 + --> $DIR/const-integer-bool-ops.rs:28:19 | LL | const X3: usize = -42 && -39; | ^^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:31:26 + --> $DIR/const-integer-bool-ops.rs:28:26 | LL | const X3: usize = -42 && -39; | ^^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:31:19 + --> $DIR/const-integer-bool-ops.rs:28:19 | LL | const X3: usize = -42 && -39; | ^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:38:19 - | -LL | const ARR3: [i32; X3] = [99; 6]; - | ^^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:41:18 + --> $DIR/const-integer-bool-ops.rs:37:18 | LL | const Y: usize = 42.0 == 42.0; | ^^^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:44:19 - | -LL | const ARRR: [i32; Y] = [99; 1]; - | ^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:47:19 + --> $DIR/const-integer-bool-ops.rs:42:19 | LL | const Y1: usize = 42.0 >= 42.0; | ^^^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:50:20 - | -LL | const ARRR1: [i32; Y1] = [99; 1]; - | ^^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:53:19 + --> $DIR/const-integer-bool-ops.rs:47:19 | LL | const Y2: usize = 42.0 <= 42.0; | ^^^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:56:20 - | -LL | const ARRR2: [i32; Y2] = [99; 1]; - | ^^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:59:19 + --> $DIR/const-integer-bool-ops.rs:52:19 | LL | const Y3: usize = 42.0 > 42.0; | ^^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:62:20 - | -LL | const ARRR3: [i32; Y3] = [99; 0]; - | ^^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:65:19 + --> $DIR/const-integer-bool-ops.rs:57:19 | LL | const Y4: usize = 42.0 < 42.0; | ^^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:68:20 - | -LL | const ARRR4: [i32; Y4] = [99; 0]; - | ^^ referenced constant has errors - error[E0308]: mismatched types - --> $DIR/const-integer-bool-ops.rs:71:19 + --> $DIR/const-integer-bool-ops.rs:62:19 | LL | const Y5: usize = 42.0 != 42.0; | ^^^^^^^^^^^^ expected `usize`, found `bool` -error[E0080]: evaluation of constant value failed - --> $DIR/const-integer-bool-ops.rs:74:20 - | -LL | const ARRR5: [i32; Y5] = [99; 0]; - | ^^ referenced constant has errors - -error: aborting due to 28 previous errors +error: aborting due to 18 previous errors -Some errors have detailed explanations: E0080, E0308. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/consts/const-match-check.eval1.stderr b/src/test/ui/consts/const-match-check.eval1.stderr index 087cc3c86a68d..3850b1d82bf5b 100644 --- a/src/test/ui/consts/const-match-check.eval1.stderr +++ b/src/test/ui/consts/const-match-check.eval1.stderr @@ -6,6 +6,7 @@ LL | A = { let 0 = 0; 0 }, | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | A = { if let 0 = 0 { /* */ } 0 }, diff --git a/src/test/ui/consts/const-match-check.eval2.stderr b/src/test/ui/consts/const-match-check.eval2.stderr index 80d9f794bc1d5..4e1d50f42d461 100644 --- a/src/test/ui/consts/const-match-check.eval2.stderr +++ b/src/test/ui/consts/const-match-check.eval2.stderr @@ -6,6 +6,7 @@ LL | let x: [i32; { let 0 = 0; 0 }] = []; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let x: [i32; { if let 0 = 0 { /* */ } 0 }] = []; diff --git a/src/test/ui/consts/const-match-check.matchck.stderr b/src/test/ui/consts/const-match-check.matchck.stderr index e6b2f212bb430..2aabc0ca494cb 100644 --- a/src/test/ui/consts/const-match-check.matchck.stderr +++ b/src/test/ui/consts/const-match-check.matchck.stderr @@ -6,6 +6,7 @@ LL | const X: i32 = { let 0 = 0; 0 }; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; @@ -19,6 +20,7 @@ LL | static Y: i32 = { let 0 = 0; 0 }; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | static Y: i32 = { if let 0 = 0 { /* */ } 0 }; @@ -32,6 +34,7 @@ LL | const X: i32 = { let 0 = 0; 0 }; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; @@ -45,6 +48,7 @@ LL | const X: i32 = { let 0 = 0; 0 }; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; diff --git a/src/test/ui/consts/const-pattern-irrefutable.stderr b/src/test/ui/consts/const-pattern-irrefutable.stderr index 4814aa9a5b2ca..863e1372a9b8a 100644 --- a/src/test/ui/consts/const-pattern-irrefutable.stderr +++ b/src/test/ui/consts/const-pattern-irrefutable.stderr @@ -9,6 +9,8 @@ LL | let a = 4; | | | interpreted as a constant pattern, not a new variable | help: introduce a variable instead: `a_var` + | + = note: the matched value is of type `u8` error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:13:9 @@ -21,6 +23,8 @@ LL | let c = 4; | | | interpreted as a constant pattern, not a new variable | help: introduce a variable instead: `c_var` + | + = note: the matched value is of type `u8` error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:14:9 @@ -33,6 +37,8 @@ LL | let d = 4; | | | interpreted as a constant pattern, not a new variable | help: introduce a variable instead: `d_var` + | + = note: the matched value is of type `u8` error: aborting due to 3 previous errors diff --git a/src/test/ui/consts/const-points-to-static.rs b/src/test/ui/consts/const-points-to-static.rs index b998b7a97be4e..7087b6e6a6764 100644 --- a/src/test/ui/consts/const-points-to-static.rs +++ b/src/test/ui/consts/const-points-to-static.rs @@ -3,8 +3,9 @@ #![allow(dead_code)] const TEST: &u8 = &MY_STATIC; -//~^ skipping const checks -//~| it is undefined behavior to use this value +//~^ ERROR it is undefined behavior to use this value +//~| NOTE encountered a reference pointing to a static variable +//~| NOTE static MY_STATIC: u8 = 4; diff --git a/src/test/ui/consts/const-points-to-static.stderr b/src/test/ui/consts/const-points-to-static.stderr index f2ca7ff782591..465537fb3d5ea 100644 --- a/src/test/ui/consts/const-points-to-static.stderr +++ b/src/test/ui/consts/const-points-to-static.stderr @@ -1,9 +1,3 @@ -warning: skipping const checks - --> $DIR/const-points-to-static.rs:5:20 - | -LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^ - error[E0080]: it is undefined behavior to use this value --> $DIR/const-points-to-static.rs:5:1 | @@ -12,6 +6,14 @@ LL | const TEST: &u8 = &MY_STATIC; | = 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. -error: aborting due to previous error +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const-points-to-static.rs:5:20 + | +LL | const TEST: &u8 = &MY_STATIC; + | ^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-prop-read-static-in-const.rs b/src/test/ui/consts/const-prop-read-static-in-const.rs index 14ec064e4ceef..13b1b2d14125b 100644 --- a/src/test/ui/consts/const-prop-read-static-in-const.rs +++ b/src/test/ui/consts/const-prop-read-static-in-const.rs @@ -3,7 +3,6 @@ #![allow(dead_code)] const TEST: u8 = MY_STATIC; //~ ERROR any use of this value will cause an error -//~^ skipping const checks static MY_STATIC: u8 = 4; diff --git a/src/test/ui/consts/const-prop-read-static-in-const.stderr b/src/test/ui/consts/const-prop-read-static-in-const.stderr index bfaa0f934ade2..7a517d1d7b363 100644 --- a/src/test/ui/consts/const-prop-read-static-in-const.stderr +++ b/src/test/ui/consts/const-prop-read-static-in-const.stderr @@ -1,9 +1,3 @@ -warning: skipping const checks - --> $DIR/const-prop-read-static-in-const.rs:5:18 - | -LL | const TEST: u8 = MY_STATIC; - | ^^^^^^^^^ - error: any use of this value will cause an error --> $DIR/const-prop-read-static-in-const.rs:5:18 | @@ -14,5 +8,13 @@ LL | const TEST: u8 = MY_STATIC; | = note: `#[deny(const_err)]` on by default -error: aborting due to previous error +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const-prop-read-static-in-const.rs:5:18 + | +LL | const TEST: u8 = MY_STATIC; + | ^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/consts/const-ptr-unique-rpass.rs b/src/test/ui/consts/const-ptr-unique-rpass.rs index e8735e1a32c2c..fc13bb98bd2c9 100644 --- a/src/test/ui/consts/const-ptr-unique-rpass.rs +++ b/src/test/ui/consts/const-ptr-unique-rpass.rs @@ -8,9 +8,9 @@ use test::black_box as b; // prevent promotion of the argument and const-propaga use std::ptr::Unique; -const PTR: *mut u32 = Unique::empty().as_ptr(); +const PTR: *mut u32 = Unique::dangling().as_ptr(); pub fn main() { // Be super-extra paranoid and cast the fn items to fn pointers before blackboxing them. - assert_eq!(PTR, b:: _>(Unique::::empty)().as_ptr()); + assert_eq!(PTR, b:: _>(Unique::::dangling)().as_ptr()); } diff --git a/src/test/ui/consts/const-size_of-cycle.rs b/src/test/ui/consts/const-size_of-cycle.rs index c94bb4fbb2796..1f56c8bd8e658 100644 --- a/src/test/ui/consts/const-size_of-cycle.rs +++ b/src/test/ui/consts/const-size_of-cycle.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // error-pattern: cycle detected struct Foo { diff --git a/src/test/ui/consts/const-size_of-cycle.stderr b/src/test/ui/consts/const-size_of-cycle.stderr index c03b7a19ffc61..730ad57de8169 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -1,16 +1,16 @@ error[E0391]: cycle detected when const-evaluating + checking `Foo::bytes::{{constant}}#0` - --> $DIR/const-size_of-cycle.rs:8:17 + --> $DIR/const-size_of-cycle.rs:4:17 | LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: ...which requires const-evaluating + checking `Foo::bytes::{{constant}}#0`... - --> $DIR/const-size_of-cycle.rs:8:17 + --> $DIR/const-size_of-cycle.rs:4:17 | LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating `Foo::bytes::{{constant}}#0`... - --> $DIR/const-size_of-cycle.rs:8:17 + --> $DIR/const-size_of-cycle.rs:4:17 | LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,10 +25,10 @@ note: ...which requires const-evaluating + checking `std::intrinsics::size_of`.. LL | pub fn size_of() -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Foo`... - = note: ...which requires normalizing `ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: All, def_id: None }, value: [u8; _] }`... + = note: ...which requires normalizing `[u8; _]`... = note: ...which again requires const-evaluating + checking `Foo::bytes::{{constant}}#0`, completing the cycle note: cycle used when processing `Foo` - --> $DIR/const-size_of-cycle.rs:7:1 + --> $DIR/const-size_of-cycle.rs:3:1 | LL | struct Foo { | ^^^^^^^^^^ diff --git a/src/test/ui/consts/const-tup-index-span.rs b/src/test/ui/consts/const-tup-index-span.rs index 763263c6aeb4f..8057d64369a23 100644 --- a/src/test/ui/consts/const-tup-index-span.rs +++ b/src/test/ui/consts/const-tup-index-span.rs @@ -4,7 +4,6 @@ const TUP: (usize,) = 5usize << 64; //~^ ERROR mismatched types //~| expected tuple, found `usize` const ARR: [i32; TUP.0] = []; -//~^ ERROR evaluation of constant value failed fn main() { } diff --git a/src/test/ui/consts/const-tup-index-span.stderr b/src/test/ui/consts/const-tup-index-span.stderr index 8e4a092e40f5e..6724984d8d7ac 100644 --- a/src/test/ui/consts/const-tup-index-span.stderr +++ b/src/test/ui/consts/const-tup-index-span.stderr @@ -7,13 +7,6 @@ LL | const TUP: (usize,) = 5usize << 64; = note: expected tuple `(usize,)` found type `usize` -error[E0080]: evaluation of constant value failed - --> $DIR/const-tup-index-span.rs:6:18 - | -LL | const ARR: [i32; TUP.0] = []; - | ^^^ referenced constant has errors - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0080, E0308. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/consts/const_in_pattern/accept_structural.rs b/src/test/ui/consts/const_in_pattern/accept_structural.rs new file mode 100644 index 0000000000000..5093fe5391547 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/accept_structural.rs @@ -0,0 +1,66 @@ +// run-pass + +#![warn(indirect_structural_match)] + +// This test is checking our logic for structural match checking by enumerating +// the different kinds of const expressions. This test is collecting cases where +// we have accepted the const expression as a pattern in the past and wish to +// continue doing so. +// +// Even if a non-structural-match type is part of an expression in a const's +// definition, that does not necessarily disqualify the const from being a match +// pattern: in principle, we just need the types involved in the final value to +// be structurally matchable. + +// See also RFC 1445 + +#![feature(type_ascription)] + +#[derive(Copy, Clone, Debug)] +struct NoPartialEq(u32); + +#[derive(Copy, Clone, Debug)] +struct NoDerive(u32); + +// This impl makes `NoDerive` irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } +impl Eq for NoDerive { } + +type OND = Option; + +fn main() { + const FIELD1: u32 = NoPartialEq(1).0; + match 1 { FIELD1 => dbg!(FIELD1), _ => panic!("whoops"), }; + const FIELD2: u32 = NoDerive(1).0; + match 1 { FIELD2 => dbg!(FIELD2), _ => panic!("whoops"), }; + + enum CLike { One = 1, #[allow(dead_code)] Two = 2, } + const ONE_CAST: u32 = CLike::One as u32; + match 1 { ONE_CAST => dbg!(ONE_CAST), _ => panic!("whoops"), }; + + const NO_DERIVE_NONE: OND = None; + const INDIRECT: OND = NO_DERIVE_NONE; + match None { INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + + const TUPLE: (OND, OND) = (None, None); + match (None, None) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + + const TYPE_ASCRIPTION: OND = None: OND; + match None { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; + + const ARRAY: [OND; 2] = [None, None]; + match [None; 2] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + + const REPEAT: [OND; 2] = [None; 2]; + match [None, None] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + + trait Trait: Sized { const ASSOC: Option; } + impl Trait for NoDerive { const ASSOC: Option = None; } + match None { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + + const BLOCK: OND = { NoDerive(10); None }; + match None { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + + const ADDR_OF: &OND = &None; + match &None { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; +} diff --git a/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs b/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs new file mode 100644 index 0000000000000..b438bcd9fb5eb --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs @@ -0,0 +1,16 @@ +pub struct CustomEq; + +impl Eq for CustomEq {} +impl PartialEq for CustomEq { + fn eq(&self, _: &Self) -> bool { + false + } +} + +pub const NONE: Option = None; +pub const SOME: Option = Some(CustomEq); + +pub trait AssocConst { + const NONE: Option = None; + const SOME: Option = Some(CustomEq); +} diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs new file mode 100644 index 0000000000000..05c53e5edccc5 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs @@ -0,0 +1,27 @@ +// aux-build:consts.rs + +#![warn(indirect_structural_match)] + +extern crate consts; + +struct Defaulted; +impl consts::AssocConst for Defaulted {} + +fn main() { + let _ = Defaulted; + match None { + consts::SOME => panic!(), + //~^ must be annotated with `#[derive(PartialEq, Eq)]` + //~| must be annotated with `#[derive(PartialEq, Eq)]` + + _ => {} + } + + match None { + ::SOME => panic!(), + //~^ must be annotated with `#[derive(PartialEq, Eq)]` + //~| must be annotated with `#[derive(PartialEq, Eq)]` + + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr new file mode 100644 index 0000000000000..5d147e32f5a86 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr @@ -0,0 +1,26 @@ +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:13:9 + | +LL | consts::SOME => panic!(), + | ^^^^^^^^^^^^ + +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:21:9 + | +LL | ::SOME => panic!(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:13:9 + | +LL | consts::SOME => panic!(), + | ^^^^^^^^^^^^ + +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:21:9 + | +LL | ::SOME => panic!(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs new file mode 100644 index 0000000000000..1d8ecf8ae6640 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs @@ -0,0 +1,23 @@ +// run-pass +// aux-build:consts.rs + +#![warn(indirect_structural_match)] + +extern crate consts; +use consts::CustomEq; + +struct Defaulted; +impl consts::AssocConst for Defaulted {} + +fn main() { + let _ = Defaulted; + match Some(CustomEq) { + consts::NONE => panic!(), + _ => {} + } + + match Some(CustomEq) { + ::NONE => panic!(), + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/custom-eq-branch-pass.rs b/src/test/ui/consts/const_in_pattern/custom-eq-branch-pass.rs new file mode 100644 index 0000000000000..81a2024a81b7b --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/custom-eq-branch-pass.rs @@ -0,0 +1,33 @@ +// run-pass + +#![feature(const_if_match)] +#![warn(indirect_structural_match)] + +struct CustomEq; + +impl Eq for CustomEq {} +impl PartialEq for CustomEq { + fn eq(&self, _: &Self) -> bool { + false + } +} + +#[derive(PartialEq, Eq)] +enum Foo { + Bar, + Baz, + Qux(CustomEq), +} + +const BAR_BAZ: Foo = if 42 == 42 { + Foo::Bar +} else { + Foo::Baz +}; + +fn main() { + match Foo::Qux(CustomEq) { + BAR_BAZ => panic!(), + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.rs b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.rs new file mode 100644 index 0000000000000..21c4de6fbb1f1 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.rs @@ -0,0 +1,39 @@ +// check-pass + +#![feature(const_if_match)] +#![warn(indirect_structural_match)] +//~^ NOTE lint level is defined here + +struct CustomEq; + +impl Eq for CustomEq {} +impl PartialEq for CustomEq { + fn eq(&self, _: &Self) -> bool { + false + } +} + +#[derive(PartialEq, Eq)] +enum Foo { + Bar, + Baz, + Qux(CustomEq), +} + +// We know that `BAR_BAZ` will always be `Foo::Bar` and thus eligible for structural matching, but +// dataflow will be more conservative. +const BAR_BAZ: Foo = if 42 == 42 { + Foo::Bar +} else { + Foo::Qux(CustomEq) +}; + +fn main() { + match Foo::Qux(CustomEq) { + BAR_BAZ => panic!(), + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN this was previously accepted + //~| NOTE see issue #62411 + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.stderr b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.stderr new file mode 100644 index 0000000000000..06ec2a7fdd35c --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.stderr @@ -0,0 +1,16 @@ +warning: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/custom-eq-branch-warn.rs:33:9 + | +LL | BAR_BAZ => panic!(), + | ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/custom-eq-branch-warn.rs:4:9 + | +LL | #![warn(indirect_structural_match)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = 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 #62411 + +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const_in_pattern/issue-62614.rs b/src/test/ui/consts/const_in_pattern/issue-62614.rs new file mode 100644 index 0000000000000..4ea9a283618ea --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-62614.rs @@ -0,0 +1,24 @@ +// run-pass + +struct Sum(u32, u32); + +impl PartialEq for Sum { + fn eq(&self, other: &Self) -> bool { self.0 + self.1 == other.0 + other.1 } +} + +impl Eq for Sum { } + +#[derive(PartialEq, Eq)] +enum Eek { + TheConst, + UnusedByTheConst(Sum) +} + +const THE_CONST: Eek = Eek::TheConst; + +pub fn main() { + match Eek::UnusedByTheConst(Sum(1,2)) { + THE_CONST => { panic!(); } + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-65466.rs b/src/test/ui/consts/const_in_pattern/issue-65466.rs new file mode 100644 index 0000000000000..0e3e0f6dd8834 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-65466.rs @@ -0,0 +1,23 @@ +// FIXME: This still ICEs. +// +// ignore-test + +#![deny(indirect_structural_match)] + +#[derive(PartialEq, Eq)] +enum O { + Some(*const T), // Can also use PhantomData + None, +} + +struct B; + +const C: &[O] = &[O::None]; + +fn main() { + let x = O::None; + match &[x][..] { + C => (), + _ => (), + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-65466.stderr b/src/test/ui/consts/const_in_pattern/issue-65466.stderr new file mode 100644 index 0000000000000..9fe3049d1d85f --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-65466.stderr @@ -0,0 +1,15 @@ +error[E0601]: `main` function not found in crate `issue_65466` + --> $DIR/issue-65466.rs:1:1 + | +LL | / #![deny(indirect_structural_match)] +LL | | +LL | | #[derive(PartialEq, Eq)] +LL | | enum O { +... | +LL | | } +LL | | } + | |_^ consider adding a `main` function to `$DIR/issue-65466.rs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.rs b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.rs new file mode 100644 index 0000000000000..28b3fbb952563 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.rs @@ -0,0 +1,27 @@ +#![feature(const_if_match)] +#![warn(indirect_structural_match)] + +struct NoEq; + +enum Foo { + Bar, + Baz, + Qux(NoEq), +} + +// Even though any of these values can be compared structurally, we still disallow it in a pattern +// because `Foo` does not impl `PartialEq`. +const BAR_BAZ: Foo = if 42 == 42 { + Foo::Baz +} else { + Foo::Bar +}; + +fn main() { + match Foo::Qux(NoEq) { + BAR_BAZ => panic!(), + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.stderr b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.stderr new file mode 100644 index 0000000000000..cb870ec7dbcf2 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.stderr @@ -0,0 +1,14 @@ +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/no-eq-branch-fail.rs:22:9 + | +LL | BAR_BAZ => panic!(), + | ^^^^^^^ + +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/no-eq-branch-fail.rs:22:9 + | +LL | BAR_BAZ => panic!(), + | ^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.rs b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.rs new file mode 100644 index 0000000000000..a8216901c027f --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.rs @@ -0,0 +1,32 @@ +// This test is illustrating the difference between how failing to derive +// `PartialEq` is handled compared to failing to implement it at all. + +// See also RFC 1445 + +#[derive(PartialEq, Eq)] +struct Structural(u32); + +struct NoPartialEq(u32); + +struct NoDerive(u32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +const NO_DERIVE_NONE: Option = None; +const NO_PARTIAL_EQ_NONE: Option = None; + +fn main() { + match None { + NO_DERIVE_NONE => println!("NO_DERIVE_NONE"), + _ => panic!("whoops"), + } + + match None { + NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"), + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => panic!("whoops"), + } +} diff --git a/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.stderr b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.stderr new file mode 100644 index 0000000000000..95cfa4a9ebe95 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.stderr @@ -0,0 +1,8 @@ +error: to use a constant of type `NoPartialEq` in a pattern, `NoPartialEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_partial_eq.rs:28:9 + | +LL | NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"), + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/consts/const_in_pattern/reject_non_structural.rs b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs new file mode 100644 index 0000000000000..bbeaeea1f87d8 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs @@ -0,0 +1,93 @@ +// This test of structural match checking enumerates the different kinds of +// const definitions, collecting cases where the const pattern is rejected. +// +// Note: Even if a non-structural-match type is part of an expression in a +// const's definition, that does not necessarily disqualify the const from being +// a match pattern: in principle, we just need the types involved in the final +// value to be structurally matchable. + +// See also RFC 1445 + +#![feature(type_ascription)] +#![warn(indirect_structural_match)] +//~^ NOTE lint level is defined here + +#[derive(Copy, Clone, Debug)] +struct NoPartialEq; + +#[derive(Copy, Clone, Debug)] +struct NoDerive; + +// This impl makes `NoDerive` irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +type OND = Option; + +struct TrivialEq(OND); + +// This impl makes `TrivialEq` trivial. +impl PartialEq for TrivialEq { fn eq(&self, _: &Self) -> bool { true } } + +impl Eq for TrivialEq { } + +fn main() { + #[derive(PartialEq, Eq, Debug)] + enum Derive { Some(X), None, } + + const ENUM: Derive = Derive::Some(NoDerive); + match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const FIELD: OND = TrivialEq(Some(NoDerive)).0; + match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const NO_DERIVE_SOME: OND = Some(NoDerive); + const INDIRECT: OND = NO_DERIVE_SOME; + match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const TUPLE: (OND, OND) = (None, Some(NoDerive)); + match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const TYPE_ASCRIPTION: OND = Some(NoDerive): OND; + match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const ARRAY: [OND; 2] = [None, Some(NoDerive)]; + match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const REPEAT: [OND; 2] = [Some(NoDerive); 2]; + match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + trait Trait: Sized { const ASSOC: Option; } + impl Trait for NoDerive { const ASSOC: Option = Some(NoDerive); } + match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const BLOCK: OND = { NoDerive; Some(NoDerive) }; + match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const ADDR_OF: &OND = &Some(NoDerive); + match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN previously accepted by the compiler but is being phased out + //~| NOTE for more information, see issue #62411 +} diff --git a/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr b/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr new file mode 100644 index 0000000000000..b1310cf101eaa --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr @@ -0,0 +1,136 @@ +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:40:36 + | +LL | match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; + | ^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:45:28 + | +LL | match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:51:27 + | +LL | match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + | ^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:56:36 + | +LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:61:28 + | +LL | match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:66:36 + | +LL | match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:79:28 + | +LL | match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:84:28 + | +LL | match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + | ^^^^^ + +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:89:29 + | +LL | match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; + | ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/reject_non_structural.rs:12:9 + | +LL | #![warn(indirect_structural_match)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = 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 #62411 + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:40:36 + | +LL | match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; + | ^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:45:28 + | +LL | match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:51:27 + | +LL | match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + | ^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:56:36 + | +LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:61:28 + | +LL | match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:66:36 + | +LL | match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:79:28 + | +LL | match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:84:28 + | +LL | match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + | ^^^^^ + +error: aborting due to 20 previous errors; 1 warning emitted + diff --git a/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs b/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs new file mode 100644 index 0000000000000..c6b794de19526 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs @@ -0,0 +1,41 @@ +// run-pass + +// This test is checking our logic for structural match checking by enumerating +// the different kinds of const expressions. This test is collecting cases where +// we have accepted the const expression as a pattern in the past but we want +// to begin warning the user that a future version of Rust may start rejecting +// such const expressions. + +// The specific corner cases we are exploring here are instances where the +// const-evaluator computes a value that *does* meet the conditions for +// structural-match, but the const expression itself has abstractions (like +// calls to const functions) that may fit better with a type-based analysis +// rather than a committment to a specific value. + +#![warn(indirect_structural_match)] + +#[derive(Copy, Clone, Debug)] +struct NoDerive(u32); + +// This impl makes `NoDerive` irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } +impl Eq for NoDerive { } + +fn main() { + const INDEX: Option = [None, Some(NoDerive(10))][0]; + match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN this was previously accepted + + const fn build() -> Option { None } + const CALL: Option = build(); + match None { Some(_) => panic!("whoops"), CALL => dbg!(CALL), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN this was previously accepted + + impl NoDerive { const fn none() -> Option { None } } + const METHOD_CALL: Option = NoDerive::none(); + match None { Some(_) => panic!("whoops"), METHOD_CALL => dbg!(METHOD_CALL), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN this was previously accepted +} diff --git a/src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr b/src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr new file mode 100644 index 0000000000000..3e7ed573c74d7 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr @@ -0,0 +1,34 @@ +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/warn_corner_cases.rs:26:47 + | +LL | match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), }; + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/warn_corner_cases.rs:15:9 + | +LL | #![warn(indirect_structural_match)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = 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 #62411 + +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/warn_corner_cases.rs:32:47 + | +LL | match None { Some(_) => panic!("whoops"), CALL => dbg!(CALL), }; + | ^^^^ + | + = 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 #62411 + +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/warn_corner_cases.rs:38:47 + | +LL | match None { Some(_) => panic!("whoops"), METHOD_CALL => dbg!(METHOD_CALL), }; + | ^^^^^^^^^^^ + | + = 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 #62411 + +warning: 3 warnings emitted + diff --git a/src/test/ui/consts/const_let_assign3.stderr b/src/test/ui/consts/const_let_assign3.stderr index 5e2a85cc03d9f..62fd04ea522c3 100644 --- a/src/test/ui/consts/const_let_assign3.stderr +++ b/src/test/ui/consts/const_let_assign3.stderr @@ -3,6 +3,8 @@ error[E0019]: constant function contains unimplemented expression type | LL | self.state = x; | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0658]: references in constants may only refer to immutable values --> $DIR/const_let_assign3.rs:16:5 @@ -27,6 +29,8 @@ error[E0019]: constant contains unimplemented expression type | LL | *y = 42; | ^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const_let_refutable.stderr b/src/test/ui/consts/const_let_refutable.stderr index 0ba79e5f71a2e..02296e6de75fc 100644 --- a/src/test/ui/consts/const_let_refutable.stderr +++ b/src/test/ui/consts/const_let_refutable.stderr @@ -3,6 +3,8 @@ error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, | LL | const fn slice(&[a, b]: &[i32]) -> i32 { | ^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _, ..]` not covered + | + = note: the matched value is of type `&[i32]` error[E0723]: loops and conditional expressions are not stable in const fn --> $DIR/const_let_refutable.rs:3:17 diff --git a/src/test/ui/consts/control-flow/feature-gate-const-if-match.rs b/src/test/ui/consts/control-flow/feature-gate-const-if-match.rs index 00576d50ac66b..c49dd830a12e8 100644 --- a/src/test/ui/consts/control-flow/feature-gate-const-if-match.rs +++ b/src/test/ui/consts/control-flow/feature-gate-const-if-match.rs @@ -113,6 +113,5 @@ fn main() { //[if_match]~ ERROR fatal error triggered by #[rustc_error] //[stock]~^ ERROR `match` is not allowed in a `const` if let Some(x) = Some(x) { x } else { 1 } //[stock]~^ ERROR `if` is not allowed in a `const` - //[stock]~| ERROR constant contains unimplemented expression type }]; } diff --git a/src/test/ui/consts/control-flow/feature-gate-const-if-match.stock.stderr b/src/test/ui/consts/control-flow/feature-gate-const-if-match.stock.stderr index ca087b8588689..b27971dccac6c 100644 --- a/src/test/ui/consts/control-flow/feature-gate-const-if-match.stock.stderr +++ b/src/test/ui/consts/control-flow/feature-gate-const-if-match.stock.stderr @@ -237,13 +237,6 @@ LL | const MATCH: i32 = match 0 { 1 => 2, _ => 0 }; = note: see issue #49146 for more information = help: add `#![feature(const_if_match)]` to the crate attributes to enable -error[E0019]: constant contains unimplemented expression type - --> $DIR/feature-gate-const-if-match.rs:114:21 - | -LL | if let Some(x) = Some(x) { x } else { 1 } - | ^ - -error: aborting due to 25 previous errors +error: aborting due to 24 previous errors -Some errors have detailed explanations: E0019, E0658. -For more information about an error, try `rustc --explain E0019`. +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/dangling-alloc-id-ice.rs b/src/test/ui/consts/dangling-alloc-id-ice.rs index dbc50f1fbd4b4..3b7f1de5b9bea 100644 --- a/src/test/ui/consts/dangling-alloc-id-ice.rs +++ b/src/test/ui/consts/dangling-alloc-id-ice.rs @@ -1,11 +1,13 @@ // https://github.com/rust-lang/rust/issues/55223 +#![allow(const_err)] union Foo<'a> { y: &'a (), long_live_the_unit: &'static (), } -const FOO: &() = { //~ ERROR any use of this value will cause an error +const FOO: &() = { //~ ERROR it is undefined behavior to use this value +//~^ ERROR encountered dangling pointer in final constant let y = (); unsafe { Foo { y: &y }.long_live_the_unit } }; diff --git a/src/test/ui/consts/dangling-alloc-id-ice.stderr b/src/test/ui/consts/dangling-alloc-id-ice.stderr index 0e213555052c8..14a49810b9de5 100644 --- a/src/test/ui/consts/dangling-alloc-id-ice.stderr +++ b/src/test/ui/consts/dangling-alloc-id-ice.stderr @@ -1,13 +1,25 @@ -error: any use of this value will cause an error - --> $DIR/dangling-alloc-id-ice.rs:8:1 +error: encountered dangling pointer in final constant + --> $DIR/dangling-alloc-id-ice.rs:9:1 | LL | / const FOO: &() = { +LL | | LL | | let y = (); LL | | unsafe { Foo { y: &y }.long_live_the_unit } LL | | }; - | |__^ encountered dangling pointer in final constant + | |__^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/dangling-alloc-id-ice.rs:9:1 + | +LL | / const FOO: &() = { +LL | | +LL | | let y = (); +LL | | unsafe { Foo { y: &y }.long_live_the_unit } +LL | | }; + | |__^ type validation failed: encountered a dangling reference (use-after-free) | - = note: `#[deny(const_err)]` on by default + = 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. -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/dangling_raw_ptr.rs b/src/test/ui/consts/dangling_raw_ptr.rs index c2d8e6d421a28..ddd1fb1ba76e1 100644 --- a/src/test/ui/consts/dangling_raw_ptr.rs +++ b/src/test/ui/consts/dangling_raw_ptr.rs @@ -1,4 +1,4 @@ -const FOO: *const u32 = { //~ ERROR any use of this value will cause an error +const FOO: *const u32 = { //~ ERROR encountered dangling pointer in final constant let x = 42; &x }; diff --git a/src/test/ui/consts/dangling_raw_ptr.stderr b/src/test/ui/consts/dangling_raw_ptr.stderr index 4d4c2876c4598..a79ac62d5cdbd 100644 --- a/src/test/ui/consts/dangling_raw_ptr.stderr +++ b/src/test/ui/consts/dangling_raw_ptr.stderr @@ -1,13 +1,11 @@ -error: any use of this value will cause an error +error: encountered dangling pointer in final constant --> $DIR/dangling_raw_ptr.rs:1:1 | LL | / const FOO: *const u32 = { LL | | let x = 42; LL | | &x LL | | }; - | |__^ encountered dangling pointer in final constant - | - = note: `#[deny(const_err)]` on by default + | |__^ error: aborting due to previous error diff --git a/src/test/ui/consts/ice-zst-static-access.rs b/src/test/ui/consts/ice-zst-static-access.rs new file mode 100644 index 0000000000000..b68e442a57c71 --- /dev/null +++ b/src/test/ui/consts/ice-zst-static-access.rs @@ -0,0 +1,32 @@ +// check-pass + +// This is a regression test for ICEs from +// https://github.com/rust-lang/rust/issues/71612 +// and +// https://github.com/rust-lang/rust/issues/71709 + +#[derive(Copy, Clone)] +pub struct Glfw; + +static mut GLFW: Option = None; +pub fn new() -> Glfw { + unsafe { + if let Some(glfw) = GLFW { + return glfw; + } else { + todo!() + } + }; +} + +extern "C" { + static _dispatch_queue_attr_concurrent: [u8; 0]; +} + +static DISPATCH_QUEUE_CONCURRENT: &'static [u8; 0] = + unsafe { &_dispatch_queue_attr_concurrent }; + +fn main() { + *DISPATCH_QUEUE_CONCURRENT; + new(); +} diff --git a/src/test/ui/consts/inline_asm.rs b/src/test/ui/consts/inline_asm.rs new file mode 100644 index 0000000000000..c2ab97e54f0c8 --- /dev/null +++ b/src/test/ui/consts/inline_asm.rs @@ -0,0 +1,6 @@ +#![feature(llvm_asm)] + +const _: () = unsafe { llvm_asm!("nop") }; +//~^ ERROR contains unimplemented expression type + +fn main() {} diff --git a/src/test/ui/consts/inline_asm.stderr b/src/test/ui/consts/inline_asm.stderr new file mode 100644 index 0000000000000..0a064c8136651 --- /dev/null +++ b/src/test/ui/consts/inline_asm.stderr @@ -0,0 +1,11 @@ +error[E0019]: constant contains unimplemented expression type + --> $DIR/inline_asm.rs:3:24 + | +LL | const _: () = unsafe { llvm_asm!("nop") }; + | ^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0019`. diff --git a/src/test/ui/consts/issue-52432.rs b/src/test/ui/consts/issue-52432.rs index ded79458e637f..d719bf1b97161 100644 --- a/src/test/ui/consts/issue-52432.rs +++ b/src/test/ui/consts/issue-52432.rs @@ -6,4 +6,5 @@ fn main() { //~| ERROR: type annotations needed [(); &(static || {}) as *const _ as usize]; //~^ ERROR: closures cannot be static + //~| ERROR evaluation of constant value failed } diff --git a/src/test/ui/consts/issue-52432.stderr b/src/test/ui/consts/issue-52432.stderr index d25c11138f400..e9539d24118a0 100644 --- a/src/test/ui/consts/issue-52432.stderr +++ b/src/test/ui/consts/issue-52432.stderr @@ -16,7 +16,13 @@ error[E0282]: type annotations needed LL | [(); &(static |x| {}) as *const _ as usize]; | ^ consider giving this closure parameter a type -error: aborting due to 3 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/issue-52432.rs:7:10 + | +LL | [(); &(static || {}) as *const _ as usize]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0282, E0697. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0080, E0282, E0697. +For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/issue-69020.noopt.stderr b/src/test/ui/consts/issue-69020.noopt.stderr deleted file mode 100644 index c48a106ef4656..0000000000000 --- a/src/test/ui/consts/issue-69020.noopt.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error: this arithmetic operation will overflow - --> $DIR/issue-69020.rs:21:22 - | -LL | const NEG: i32 = -i32::MIN + T::NEG; - | ^^^^^^^^^ attempt to negate with overflow - | - = note: `#[deny(arithmetic_overflow)]` on by default - -error: this arithmetic operation will overflow - --> $DIR/issue-69020.rs:23:22 - | -LL | const ADD: i32 = (i32::MAX+1) + T::ADD; - | ^^^^^^^^^^^^ attempt to add with overflow - -error: this operation will panic at runtime - --> $DIR/issue-69020.rs:25:22 - | -LL | const DIV: i32 = (1/0) + T::DIV; - | ^^^^^ attempt to divide by zero - | - = note: `#[deny(unconditional_panic)]` on by default - -error: this operation will panic at runtime - --> $DIR/issue-69020.rs:27:22 - | -LL | const OOB: i32 = [1][1] + T::OOB; - | ^^^^^^ index out of bounds: the len is 1 but the index is 1 - -error: aborting due to 4 previous errors - diff --git a/src/test/ui/consts/issue-69020.opt.stderr b/src/test/ui/consts/issue-69020.opt.stderr deleted file mode 100644 index c48a106ef4656..0000000000000 --- a/src/test/ui/consts/issue-69020.opt.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error: this arithmetic operation will overflow - --> $DIR/issue-69020.rs:21:22 - | -LL | const NEG: i32 = -i32::MIN + T::NEG; - | ^^^^^^^^^ attempt to negate with overflow - | - = note: `#[deny(arithmetic_overflow)]` on by default - -error: this arithmetic operation will overflow - --> $DIR/issue-69020.rs:23:22 - | -LL | const ADD: i32 = (i32::MAX+1) + T::ADD; - | ^^^^^^^^^^^^ attempt to add with overflow - -error: this operation will panic at runtime - --> $DIR/issue-69020.rs:25:22 - | -LL | const DIV: i32 = (1/0) + T::DIV; - | ^^^^^ attempt to divide by zero - | - = note: `#[deny(unconditional_panic)]` on by default - -error: this operation will panic at runtime - --> $DIR/issue-69020.rs:27:22 - | -LL | const OOB: i32 = [1][1] + T::OOB; - | ^^^^^^ index out of bounds: the len is 1 but the index is 1 - -error: aborting due to 4 previous errors - diff --git a/src/test/ui/consts/issue-69020.opt_with_overflow_checks.stderr b/src/test/ui/consts/issue-69020.opt_with_overflow_checks.stderr deleted file mode 100644 index c48a106ef4656..0000000000000 --- a/src/test/ui/consts/issue-69020.opt_with_overflow_checks.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error: this arithmetic operation will overflow - --> $DIR/issue-69020.rs:21:22 - | -LL | const NEG: i32 = -i32::MIN + T::NEG; - | ^^^^^^^^^ attempt to negate with overflow - | - = note: `#[deny(arithmetic_overflow)]` on by default - -error: this arithmetic operation will overflow - --> $DIR/issue-69020.rs:23:22 - | -LL | const ADD: i32 = (i32::MAX+1) + T::ADD; - | ^^^^^^^^^^^^ attempt to add with overflow - -error: this operation will panic at runtime - --> $DIR/issue-69020.rs:25:22 - | -LL | const DIV: i32 = (1/0) + T::DIV; - | ^^^^^ attempt to divide by zero - | - = note: `#[deny(unconditional_panic)]` on by default - -error: this operation will panic at runtime - --> $DIR/issue-69020.rs:27:22 - | -LL | const OOB: i32 = [1][1] + T::OOB; - | ^^^^^^ index out of bounds: the len is 1 but the index is 1 - -error: aborting due to 4 previous errors - diff --git a/src/test/ui/consts/issue-69020.rs b/src/test/ui/consts/issue-69020.rs deleted file mode 100644 index e079feb04d447..0000000000000 --- a/src/test/ui/consts/issue-69020.rs +++ /dev/null @@ -1,29 +0,0 @@ -// revisions: noopt opt opt_with_overflow_checks -//[noopt]compile-flags: -C opt-level=0 -//[opt]compile-flags: -O -//[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O - -#![crate_type="lib"] - -use std::i32; - -pub trait Foo { - const NEG: i32; - const ADD: i32; - const DIV: i32; - const OOB: i32; -} - -// These constants cannot be evaluated already (they depend on `T::N`), so -// they can just be linted like normal run-time code. But codegen works -// a bit different in const context, so this test makes sure that we still catch overflow. -impl Foo for Vec { - const NEG: i32 = -i32::MIN + T::NEG; - //~^ ERROR arithmetic operation will overflow - const ADD: i32 = (i32::MAX+1) + T::ADD; - //~^ ERROR arithmetic operation will overflow - const DIV: i32 = (1/0) + T::DIV; - //~^ ERROR operation will panic - const OOB: i32 = [1][1] + T::OOB; - //~^ ERROR operation will panic -} diff --git a/src/test/ui/consts/issue-70773-mir-typeck-lt-norm.rs b/src/test/ui/consts/issue-70773-mir-typeck-lt-norm.rs new file mode 100644 index 0000000000000..9d44aa1361cfc --- /dev/null +++ b/src/test/ui/consts/issue-70773-mir-typeck-lt-norm.rs @@ -0,0 +1,16 @@ +// run-pass + +const HASH_LEN: usize = 20; +struct Hash([u8; HASH_LEN]); +fn init_hash(_: &mut [u8; HASH_LEN]) {} + +fn foo<'a>() -> &'a () { + Hash([0; HASH_LEN]); + init_hash(&mut [0; HASH_LEN]); + let (_array,) = ([0; HASH_LEN],); + &() +} + +fn main() { + foo(); +} diff --git a/src/test/ui/consts/issue-70942-trait-vs-impl-mismatch.rs b/src/test/ui/consts/issue-70942-trait-vs-impl-mismatch.rs new file mode 100644 index 0000000000000..b65f5345034e5 --- /dev/null +++ b/src/test/ui/consts/issue-70942-trait-vs-impl-mismatch.rs @@ -0,0 +1,14 @@ +trait Nat { + const VALUE: usize; +} + +struct Zero; + +impl Nat for Zero { + const VALUE: i32 = 0; + //~^ ERROR implemented const `VALUE` has an incompatible type for trait +} + +fn main() { + let _: [i32; Zero::VALUE] = []; +} diff --git a/src/test/ui/consts/issue-70942-trait-vs-impl-mismatch.stderr b/src/test/ui/consts/issue-70942-trait-vs-impl-mismatch.stderr new file mode 100644 index 0000000000000..19d9ff7166784 --- /dev/null +++ b/src/test/ui/consts/issue-70942-trait-vs-impl-mismatch.stderr @@ -0,0 +1,12 @@ +error[E0326]: implemented const `VALUE` has an incompatible type for trait + --> $DIR/issue-70942-trait-vs-impl-mismatch.rs:8:18 + | +LL | const VALUE: usize; + | ----- type in trait +... +LL | const VALUE: i32 = 0; + | ^^^ expected `usize`, found `i32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0326`. diff --git a/src/test/ui/consts/match_ice.stderr b/src/test/ui/consts/match_ice.stderr index b25ac09ab1211..5477170fb1e41 100644 --- a/src/test/ui/consts/match_ice.stderr +++ b/src/test/ui/consts/match_ice.stderr @@ -14,6 +14,7 @@ LL | match K { | ^ pattern `&T` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&T` error: to use a constant of type `S` in a pattern, `S` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/match_ice.rs:11:9 diff --git a/src/test/ui/consts/miri_unleashed/abi-mismatch.rs b/src/test/ui/consts/miri_unleashed/abi-mismatch.rs index d8e63b0bfb24e..ae440d4f8f7b5 100644 --- a/src/test/ui/consts/miri_unleashed/abi-mismatch.rs +++ b/src/test/ui/consts/miri_unleashed/abi-mismatch.rs @@ -2,15 +2,18 @@ // compile-flags: -Z unleash-the-miri-inside-of-you #![feature(const_extern_fn)] +#![allow(const_err)] const extern "C" fn c_fn() {} const fn call_rust_fn(my_fn: extern "Rust" fn()) { - my_fn(); //~ ERROR any use of this value will cause an error - //~^ WARN skipping const checks + my_fn(); + //~^ ERROR could not evaluate static initializer + //~| NOTE calling a function with ABI C using caller ABI Rust + //~| NOTE inside `call_rust_fn` } -const VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); -//~^ WARN skipping const checks +static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); +//~^ NOTE inside `VAL` fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr b/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr index c7e902132e91a..d55090c75e614 100644 --- a/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr +++ b/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr @@ -1,28 +1,28 @@ -warning: skipping const checks - --> $DIR/abi-mismatch.rs:9:5 +error[E0080]: could not evaluate static initializer + --> $DIR/abi-mismatch.rs:10:5 | LL | my_fn(); | ^^^^^^^ + | | + | calling a function with ABI C using caller ABI Rust + | inside `call_rust_fn` at $DIR/abi-mismatch.rs:10:5 +... +LL | static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); + | --------------------------------------------------------------------- inside `VAL` at $DIR/abi-mismatch.rs:16:18 warning: skipping const checks - --> $DIR/abi-mismatch.rs:13:39 | -LL | const VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: any use of this value will cause an error - --> $DIR/abi-mismatch.rs:9:5 +help: skipping check that does not even have a feature gate + --> $DIR/abi-mismatch.rs:10:5 | LL | my_fn(); | ^^^^^^^ - | | - | calling a function with ABI C using caller ABI Rust - | inside call to `call_rust_fn` at $DIR/abi-mismatch.rs:13:17 -... -LL | const VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); - | -------------------------------------------------------------------------------------- +help: skipping check that does not even have a feature gate + --> $DIR/abi-mismatch.rs:16:40 | - = note: `#[deny(const_err)]` on by default +LL | static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/assoc_const.rs b/src/test/ui/consts/miri_unleashed/assoc_const.rs index cfbcc959d3b04..5f520c2cfdbce 100644 --- a/src/test/ui/consts/miri_unleashed/assoc_const.rs +++ b/src/test/ui/consts/miri_unleashed/assoc_const.rs @@ -11,7 +11,7 @@ trait Foo { } trait Bar> { - const F: u32 = (U::X, 42).1; //~ WARN skipping const checks + const F: u32 = (U::X, 42).1; } impl Foo for () { diff --git a/src/test/ui/consts/miri_unleashed/assoc_const.stderr b/src/test/ui/consts/miri_unleashed/assoc_const.stderr index 1ccf2b196fd4d..193a49bb2666f 100644 --- a/src/test/ui/consts/miri_unleashed/assoc_const.stderr +++ b/src/test/ui/consts/miri_unleashed/assoc_const.stderr @@ -1,15 +1,17 @@ -warning: skipping const checks - --> $DIR/assoc_const.rs:14:20 - | -LL | const F: u32 = (U::X, 42).1; - | ^^^^^^^^^^ - error[E0080]: erroneous constant used --> $DIR/assoc_const.rs:31:13 | LL | let y = , String>>::F; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors -error: aborting due to previous error +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/assoc_const.rs:14:20 + | +LL | const F: u32 = (U::X, 42).1; + | ^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/auxiliary/static_cross_crate.rs b/src/test/ui/consts/miri_unleashed/auxiliary/static_cross_crate.rs new file mode 100644 index 0000000000000..4fc6ae66a1242 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/auxiliary/static_cross_crate.rs @@ -0,0 +1,3 @@ +pub static mut ZERO: [u8; 1] = [0]; +pub static ZERO_REF: &[u8; 1] = unsafe { &ZERO }; +pub static mut OPT_ZERO: Option = Some(0); diff --git a/src/test/ui/consts/miri_unleashed/box.rs b/src/test/ui/consts/miri_unleashed/box.rs new file mode 100644 index 0000000000000..1f0b7f7e78a69 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/box.rs @@ -0,0 +1,13 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![feature(box_syntax)] +#![allow(const_err)] + +use std::mem::ManuallyDrop; + +fn main() {} + +static TEST_BAD: &mut i32 = { + &mut *(box 0) + //~^ ERROR could not evaluate static initializer + //~| NOTE heap allocations +}; diff --git a/src/test/ui/consts/miri_unleashed/box.stderr b/src/test/ui/consts/miri_unleashed/box.stderr new file mode 100644 index 0000000000000..768b795ca5b39 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/box.stderr @@ -0,0 +1,32 @@ +error[E0080]: could not evaluate static initializer + --> $DIR/box.rs:10:11 + | +LL | &mut *(box 0) + | ^^^^^^^ "heap allocations via `box` keyword" needs an rfc before being allowed inside constants + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/box.rs:10:11 + | +LL | &mut *(box 0) + | ^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/box.rs:10:16 + | +LL | &mut *(box 0) + | ^ +help: skipping check for `const_mut_refs` feature + --> $DIR/box.rs:10:5 + | +LL | &mut *(box 0) + | ^^^^^^^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/box.rs:10:5 + | +LL | &mut *(box 0) + | ^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static.rs b/src/test/ui/consts/miri_unleashed/const_refers_to_static.rs index edbf0e02d8de2..c9dc1de515b90 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static.rs +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static.rs @@ -1,39 +1,31 @@ +// build-fail // compile-flags: -Zunleash-the-miri-inside-of-you -#![warn(const_err)] - -#![feature(const_raw_ptr_deref)] +#![allow(const_err)] use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -const REF_INTERIOR_MUT: &usize = { //~ ERROR undefined behavior to use this value - static FOO: AtomicUsize = AtomicUsize::new(0); - unsafe { &*(&FOO as *const _ as *const usize) } - //~^ WARN skipping const checks -}; +// These fail during CTFE (as they read a static), so they only cause an error +// when *using* the const. const MUTATE_INTERIOR_MUT: usize = { static FOO: AtomicUsize = AtomicUsize::new(0); - FOO.fetch_add(1, Ordering::Relaxed) //~ WARN any use of this value will cause an error - //~^ WARN skipping const checks - //~| WARN skipping const checks + FOO.fetch_add(1, Ordering::Relaxed) }; const READ_INTERIOR_MUT: usize = { static FOO: AtomicUsize = AtomicUsize::new(0); - unsafe { *(&FOO as *const _ as *const usize) } //~ WARN any use of this value will cause an err - //~^ WARN skipping const checks + unsafe { *(&FOO as *const _ as *const usize) } }; static mut MUTABLE: u32 = 0; -const READ_MUT: u32 = unsafe { MUTABLE }; //~ WARN any use of this value will cause an error -//~^ WARN skipping const checks -//~| WARN skipping const checks +const READ_MUT: u32 = unsafe { MUTABLE }; -// ok some day perhaps -const READ_IMMUT: &usize = { //~ ERROR it is undefined behavior to use this value - static FOO: usize = 0; - &FOO - //~^ WARN skipping const checks -}; -fn main() {} +fn main() { + MUTATE_INTERIOR_MUT; + //~^ ERROR: erroneous constant used + READ_INTERIOR_MUT; + //~^ ERROR: erroneous constant used + READ_MUT; + //~^ ERROR: erroneous constant used +} diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr index ad777cfe8ea4b..e5cd86b3d6c2f 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr @@ -1,106 +1,54 @@ -warning: skipping const checks - --> $DIR/const_refers_to_static.rs:11:18 +error[E0080]: erroneous constant used + --> $DIR/const_refers_to_static.rs:25:5 + | +LL | MUTATE_INTERIOR_MUT; + | ^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error[E0080]: erroneous constant used + --> $DIR/const_refers_to_static.rs:27:5 + | +LL | READ_INTERIOR_MUT; + | ^^^^^^^^^^^^^^^^^ referenced constant has errors + +error[E0080]: erroneous constant used + --> $DIR/const_refers_to_static.rs:29:5 | -LL | unsafe { &*(&FOO as *const _ as *const usize) } - | ^^^ +LL | READ_MUT; + | ^^^^^^^^ referenced constant has errors warning: skipping const checks - --> $DIR/const_refers_to_static.rs:17:5 + | +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static.rs:13:5 | LL | FOO.fetch_add(1, Ordering::Relaxed) | ^^^ - -warning: skipping const checks - --> $DIR/const_refers_to_static.rs:17:5 +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static.rs:13:5 | LL | FOO.fetch_add(1, Ordering::Relaxed) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: skipping const checks - --> $DIR/const_refers_to_static.rs:24:17 +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static.rs:18:17 | LL | unsafe { *(&FOO as *const _ as *const usize) } | ^^^ - -warning: skipping const checks - --> $DIR/const_refers_to_static.rs:29:32 +help: skipping check for `const_raw_ptr_deref` feature + --> $DIR/const_refers_to_static.rs:18:14 | -LL | const READ_MUT: u32 = unsafe { MUTABLE }; - | ^^^^^^^ - -warning: skipping const checks - --> $DIR/const_refers_to_static.rs:29:32 +LL | unsafe { *(&FOO as *const _ as *const usize) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static.rs:22:32 | LL | const READ_MUT: u32 = unsafe { MUTABLE }; | ^^^^^^^ - -warning: skipping const checks - --> $DIR/const_refers_to_static.rs:36:6 - | -LL | &FOO - | ^^^ - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static.rs:9:1 - | -LL | / const REF_INTERIOR_MUT: &usize = { -LL | | static FOO: AtomicUsize = AtomicUsize::new(0); -LL | | unsafe { &*(&FOO as *const _ as *const usize) } -LL | | -LL | | }; - | |__^ type validation failed: encountered a reference pointing to a static variable - | - = 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. - -warning: any use of this value will cause an error - --> $DIR/const_refers_to_static.rs:17:5 - | -LL | / const MUTATE_INTERIOR_MUT: usize = { -LL | | static FOO: AtomicUsize = AtomicUsize::new(0); -LL | | FOO.fetch_add(1, Ordering::Relaxed) - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling non-const function `std::sync::atomic::AtomicUsize::fetch_add` -LL | | -LL | | -LL | | }; - | |__- - | -note: the lint level is defined here - --> $DIR/const_refers_to_static.rs:2:9 - | -LL | #![warn(const_err)] - | ^^^^^^^^^ - -warning: any use of this value will cause an error - --> $DIR/const_refers_to_static.rs:24:14 - | -LL | / const READ_INTERIOR_MUT: usize = { -LL | | static FOO: AtomicUsize = AtomicUsize::new(0); -LL | | unsafe { *(&FOO as *const _ as *const usize) } - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static -LL | | -LL | | }; - | |__- - -warning: any use of this value will cause an error - --> $DIR/const_refers_to_static.rs:29:32 +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static.rs:22:32 | LL | const READ_MUT: u32 = unsafe { MUTABLE }; - | -------------------------------^^^^^^^--- - | | - | constant accesses static - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static.rs:34:1 - | -LL | / const READ_IMMUT: &usize = { -LL | | static FOO: usize = 0; -LL | | &FOO -LL | | -LL | | }; - | |__^ type validation failed: encountered a reference pointing to a static variable - | - = 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. + | ^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.rs b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.rs new file mode 100644 index 0000000000000..b5db685ef2c06 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.rs @@ -0,0 +1,25 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![allow(const_err)] + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +// These only fail during validation (they do not use but just create a reference to a static), +// so they cause an immediate error when *defining* the const. + +const REF_INTERIOR_MUT: &usize = { //~ ERROR undefined behavior to use this value +//~| NOTE encountered a reference pointing to a static variable +//~| NOTE + static FOO: AtomicUsize = AtomicUsize::new(0); + unsafe { &*(&FOO as *const _ as *const usize) } +}; + +// ok some day perhaps +const READ_IMMUT: &usize = { //~ ERROR it is undefined behavior to use this value +//~| NOTE encountered a reference pointing to a static variable +//~| NOTE + static FOO: usize = 0; + &FOO +}; + +fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.stderr new file mode 100644 index 0000000000000..2e40b38dac768 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.stderr @@ -0,0 +1,47 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static2.rs:10:1 + | +LL | / const REF_INTERIOR_MUT: &usize = { +LL | | +LL | | +LL | | static FOO: AtomicUsize = AtomicUsize::new(0); +LL | | unsafe { &*(&FOO as *const _ as *const usize) } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = 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. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static2.rs:18:1 + | +LL | / const READ_IMMUT: &usize = { +LL | | +LL | | +LL | | static FOO: usize = 0; +LL | | &FOO +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = 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. + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static2.rs:14:18 + | +LL | unsafe { &*(&FOO as *const _ as *const usize) } + | ^^^ +help: skipping check for `const_raw_ptr_deref` feature + --> $DIR/const_refers_to_static2.rs:14:14 + | +LL | unsafe { &*(&FOO as *const _ as *const usize) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static2.rs:22:6 + | +LL | &FOO + | ^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs new file mode 100644 index 0000000000000..52f6536b5ea67 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs @@ -0,0 +1,82 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +// aux-build:static_cross_crate.rs +#![allow(const_err)] + +// `const_if_match` is a HIR check and thus needed even when unleashed. +#![feature(exclusive_range_pattern, half_open_range_patterns, const_if_match)] + +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 to use this value +//~| NOTE encountered a reference pointing to a static variable +//~| NOTE + unsafe { &static_cross_crate::ZERO } +}; + +const U8_MUT: &u8 = { //~ ERROR undefined behavior to use this value +//~| NOTE encountered a reference pointing to a static variable +//~| NOTE + unsafe { &static_cross_crate::ZERO[0] } +}; + +// Also test indirection that reads from other static. This causes a const_err. +#[warn(const_err)] //~ NOTE +const U8_MUT2: &u8 = { //~ NOTE + unsafe { &(*static_cross_crate::ZERO_REF)[0] } + //~^ WARN [const_err] + //~| NOTE constant accesses static +}; +#[warn(const_err)] //~ NOTE +const U8_MUT3: &u8 = { //~ NOTE + unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + //~^ WARN [const_err] + //~| NOTE constant accesses static +}; + +pub fn test(x: &[u8; 1]) -> bool { + match x { + SLICE_MUT => true, + //~^ ERROR could not evaluate constant pattern + //~| ERROR could not evaluate constant pattern + &[1..] => false, + } +} + +pub fn test2(x: &u8) -> bool { + match x { + U8_MUT => true, + //~^ ERROR could not evaluate constant pattern + //~| ERROR could not evaluate constant pattern + &(1..) => false, + } +} + +// We need to use these *in a pattern* to trigger the failure... likely because +// the errors above otherwise stop compilation too early? +pub fn test3(x: &u8) -> bool { + match x { + U8_MUT2 => true, + //~^ ERROR could not evaluate constant pattern + //~| ERROR could not evaluate constant pattern + &(1..) => false, + } +} +pub fn test4(x: &u8) -> bool { + match x { + U8_MUT3 => true, + //~^ ERROR could not evaluate constant pattern + //~| ERROR could not evaluate constant pattern + &(1..) => false, + } +} + +fn main() { + unsafe { + static_cross_crate::ZERO[0] = 1; + } + // Now the pattern is not exhaustive any more! + test(&[0]); + test2(&0); +} diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr new file mode 100644 index 0000000000000..9d1e88a811f2f --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr @@ -0,0 +1,168 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static_cross_crate.rs:12:1 + | +LL | / const SLICE_MUT: &[u8; 1] = { +LL | | +LL | | +LL | | unsafe { &static_cross_crate::ZERO } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = 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. + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:40:9 + | +LL | SLICE_MUT => true, + | ^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static_cross_crate.rs:18:1 + | +LL | / const U8_MUT: &u8 = { +LL | | +LL | | +LL | | unsafe { &static_cross_crate::ZERO[0] } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = 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. + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:49:9 + | +LL | U8_MUT => true, + | ^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/const_refers_to_static_cross_crate.rs:27:14 + | +LL | / const U8_MUT2: &u8 = { +LL | | unsafe { &(*static_cross_crate::ZERO_REF)[0] } + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static +LL | | +LL | | +LL | | }; + | |__- + | +note: the lint level is defined here + --> $DIR/const_refers_to_static_cross_crate.rs:25:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:60:9 + | +LL | U8_MUT2 => true, + | ^^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/const_refers_to_static_cross_crate.rs:33:51 + | +LL | / const U8_MUT3: &u8 = { +LL | | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | | ^^^^^^^^^^^ constant accesses static +LL | | +LL | | +LL | | }; + | |__- + | +note: the lint level is defined here + --> $DIR/const_refers_to_static_cross_crate.rs:31:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:68:9 + | +LL | U8_MUT3 => true, + | ^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:40:9 + | +LL | SLICE_MUT => true, + | ^^^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:49:9 + | +LL | U8_MUT => true, + | ^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:60:9 + | +LL | U8_MUT2 => true, + | ^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:68:9 + | +LL | U8_MUT3 => true, + | ^^^^^^^ + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:15:15 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:15:15 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:21:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:21:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:21:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:27:17 + | +LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:33:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:33:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:33:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check for `const_panic` feature + --> $DIR/const_refers_to_static_cross_crate.rs:33:77 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:33:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors; 3 warnings emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/drop.rs b/src/test/ui/consts/miri_unleashed/drop.rs index d2c34bcd4ae2b..9bd56e81cbf8e 100644 --- a/src/test/ui/consts/miri_unleashed/drop.rs +++ b/src/test/ui/consts/miri_unleashed/drop.rs @@ -1,10 +1,6 @@ // compile-flags: -Zunleash-the-miri-inside-of-you -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // error-pattern: calling non-const function ` as std::ops::Drop>::drop` -#![deny(const_err)] +#![allow(const_err)] use std::mem::ManuallyDrop; @@ -19,5 +15,4 @@ static TEST_OK: () = { // The actual error is tested by the error-pattern above. static TEST_BAD: () = { let _v: Vec = Vec::new(); - //~^ WARN skipping const check }; diff --git a/src/test/ui/consts/miri_unleashed/drop.stderr b/src/test/ui/consts/miri_unleashed/drop.stderr index 2439d527bd197..34ab5155e22d0 100644 --- a/src/test/ui/consts/miri_unleashed/drop.stderr +++ b/src/test/ui/consts/miri_unleashed/drop.stderr @@ -1,9 +1,3 @@ -warning: skipping const checks - --> $DIR/drop.rs:21:9 - | -LL | let _v: Vec = Vec::new(); - | ^^ - error[E0080]: could not evaluate static initializer --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL | @@ -12,13 +6,24 @@ LL | | // Code here does not matter - this is replaced by the LL | | // real drop glue by the compiler. LL | | drop_in_place(to_drop) LL | | } - | |_^ calling non-const function ` as std::ops::Drop>::drop` + | | ^ + | | | + | |_calling non-const function ` as std::ops::Drop>::drop` + | inside `std::intrinsics::drop_in_place::> - shim(Some(std::vec::Vec))` at $SRC_DIR/libcore/ptr/mod.rs:LL:COL | - ::: $DIR/drop.rs:23:1 + ::: $DIR/drop.rs:18:1 | LL | }; - | - inside call to `std::intrinsics::drop_in_place::> - shim(Some(std::vec::Vec))` at $DIR/drop.rs:23:1 + | - inside `TEST_BAD` at $DIR/drop.rs:18:1 + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/drop.rs:17:9 + | +LL | let _v: Vec = Vec::new(); + | ^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/inline_asm.rs b/src/test/ui/consts/miri_unleashed/inline_asm.rs new file mode 100644 index 0000000000000..7b2b1ed4965f2 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/inline_asm.rs @@ -0,0 +1,15 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +// only-x86_64 +#![feature(llvm_asm)] +#![allow(const_err)] + +fn main() {} + +// Make sure we catch executing inline assembly. +static TEST_BAD: () = { + unsafe { llvm_asm!("xor %eax, %eax" ::: "eax"); } + //~^ ERROR could not evaluate static initializer + //~| NOTE inline assembly is not supported + //~| NOTE in this expansion of llvm_asm! + //~| NOTE in this expansion of llvm_asm! +}; diff --git a/src/test/ui/consts/miri_unleashed/inline_asm.stderr b/src/test/ui/consts/miri_unleashed/inline_asm.stderr new file mode 100644 index 0000000000000..0f5ee5de39634 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/inline_asm.stderr @@ -0,0 +1,20 @@ +error[E0080]: could not evaluate static initializer + --> $DIR/inline_asm.rs:10:14 + | +LL | unsafe { llvm_asm!("xor %eax, %eax" ::: "eax"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inline assembly is not supported + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/inline_asm.rs:10:14 + | +LL | unsafe { llvm_asm!("xor %eax, %eax" ::: "eax"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/mutable_const.rs b/src/test/ui/consts/miri_unleashed/mutable_const.rs deleted file mode 100644 index 972f59549ea74..0000000000000 --- a/src/test/ui/consts/miri_unleashed/mutable_const.rs +++ /dev/null @@ -1,20 +0,0 @@ -// compile-flags: -Zunleash-the-miri-inside-of-you - -#![feature(const_raw_ptr_deref)] -#![feature(const_mut_refs)] -#![deny(const_err)] - -use std::cell::UnsafeCell; - -// make sure we do not just intern this as mutable -const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; -//~^ WARN: skipping const checks - -const MUTATING_BEHIND_RAW: () = { - // Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time. - unsafe { - *MUTABLE_BEHIND_RAW = 99 //~ ERROR any use of this value will cause an error - } -}; - -fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/mutable_const.stderr b/src/test/ui/consts/miri_unleashed/mutable_const.stderr deleted file mode 100644 index 8456e8ec6870d..0000000000000 --- a/src/test/ui/consts/miri_unleashed/mutable_const.stderr +++ /dev/null @@ -1,26 +0,0 @@ -warning: skipping const checks - --> $DIR/mutable_const.rs:10:38 - | -LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^ - -error: any use of this value will cause an error - --> $DIR/mutable_const.rs:16:9 - | -LL | / const MUTATING_BEHIND_RAW: () = { -LL | | // Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time. -LL | | unsafe { -LL | | *MUTABLE_BEHIND_RAW = 99 - | | ^^^^^^^^^^^^^^^^^^^^^^^^ writing to alloc1 which is read-only -LL | | } -LL | | }; - | |__- - | -note: the lint level is defined here - --> $DIR/mutable_const.rs:5:9 - | -LL | #![deny(const_err)] - | ^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/consts/miri_unleashed/mutable_const2.rs b/src/test/ui/consts/miri_unleashed/mutable_const2.rs deleted file mode 100644 index 97af1f2f993c3..0000000000000 --- a/src/test/ui/consts/miri_unleashed/mutable_const2.rs +++ /dev/null @@ -1,19 +0,0 @@ -// compile-flags: -Zunleash-the-miri-inside-of-you -// failure-status: 101 -// rustc-env:RUST_BACKTRACE=0 -// normalize-stderr-test "note: rustc 1.* running on .*" -> "note: rustc VERSION running on TARGET" -// normalize-stderr-test "note: compiler flags: .*" -> "note: compiler flags: FLAGS" -// normalize-stderr-test "interpret/intern.rs:[0-9]*:[0-9]*" -> "interpret/intern.rs:LL:CC" - -#![feature(const_raw_ptr_deref)] -#![feature(const_mut_refs)] -#![deny(const_err)] - -use std::cell::UnsafeCell; - -// make sure we do not just intern this as mutable -const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; -//~^ WARN: skipping const checks -//~| ERROR: mutable allocation in constant - -fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/mutable_const2.stderr b/src/test/ui/consts/miri_unleashed/mutable_const2.stderr deleted file mode 100644 index dda9ddf1f487f..0000000000000 --- a/src/test/ui/consts/miri_unleashed/mutable_const2.stderr +++ /dev/null @@ -1,25 +0,0 @@ -warning: skipping const checks - --> $DIR/mutable_const2.rs:15:38 - | -LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^ - -error: internal compiler error: mutable allocation in constant - --> $DIR/mutable_const2.rs:15:1 - | -LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:360:17 -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace - -error: internal compiler error: unexpected panic - -note: the compiler unexpectedly panicked. this is a bug. - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports - -note: rustc VERSION running on TARGET - -note: compiler flags: FLAGS - diff --git a/src/test/ui/consts/miri_unleashed/mutable_references.rs b/src/test/ui/consts/miri_unleashed/mutable_references.rs index fe3c4ee70f2a2..ca927ef4a518b 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_references.rs +++ b/src/test/ui/consts/miri_unleashed/mutable_references.rs @@ -1,5 +1,4 @@ // compile-flags: -Zunleash-the-miri-inside-of-you -#![feature(const_mut_refs)] #![allow(const_err)] use std::cell::UnsafeCell; @@ -18,15 +17,13 @@ struct Foo(T); // this is fine for the same reason as `BAR`. static BOO: &mut Foo<()> = &mut Foo(()); +// interior mutability is fine struct Meh { x: &'static UnsafeCell, } - unsafe impl Sync for Meh {} - static MEH: Meh = Meh { x: &UnsafeCell::new(42), - //~^ WARN: skipping const checks }; // this is fine for the same reason as `BAR`. diff --git a/src/test/ui/consts/miri_unleashed/mutable_references.stderr b/src/test/ui/consts/miri_unleashed/mutable_references.stderr index 3e1300c63c17d..7109ffd8b61d7 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_references.stderr +++ b/src/test/ui/consts/miri_unleashed/mutable_references.stderr @@ -1,15 +1,37 @@ +error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item + --> $DIR/mutable_references.rs:36:5 + | +LL | *OH_YES = 99; + | ^^^^^^^^^^^^ cannot assign + warning: skipping const checks - --> $DIR/mutable_references.rs:28:8 + | +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references.rs:9:26 + | +LL | static FOO: &&mut u32 = &&mut 42; + | ^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references.rs:13:23 + | +LL | static BAR: &mut () = &mut (); + | ^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references.rs:18:28 + | +LL | static BOO: &mut Foo<()> = &mut Foo(()); + | ^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references.rs:26:8 | LL | x: &UnsafeCell::new(42), | ^^^^^^^^^^^^^^^^^^^^ - -error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item - --> $DIR/mutable_references.rs:39:5 +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references.rs:30:27 | -LL | *OH_YES = 99; - | ^^^^^^^^^^^^ cannot assign +LL | static OH_YES: &mut i32 = &mut 42; + | ^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.rs b/src/test/ui/consts/miri_unleashed/mutable_references_err.rs new file mode 100644 index 0000000000000..06fb27bcff866 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutable_references_err.rs @@ -0,0 +1,37 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you + +#![allow(const_err)] + +use std::cell::UnsafeCell; + +// this test ensures that our mutability story is sound + +struct Meh { + x: &'static UnsafeCell, +} +unsafe impl Sync for Meh {} + +// 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: mutable memory (`UnsafeCell`) is not allowed in constant + x: &UnsafeCell::new(42), +}; + +struct Synced { + x: UnsafeCell, +} +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: mutable memory (`UnsafeCell`) is not allowed in constant + +// Make sure we also catch mutable references. +const BLUNT: &mut i32 = &mut 42; +//~^ ERROR: mutable memory (`&mut`) is not allowed in constant + +fn main() { + unsafe { + *MUH.x.get() = 99; + } +} diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_err.stderr new file mode 100644 index 0000000000000..45e7d5a2cc3b3 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutable_references_err.stderr @@ -0,0 +1,40 @@ +error: mutable memory (`UnsafeCell`) is not allowed in constant + --> $DIR/mutable_references_err.rs:16:1 + | +LL | / const MUH: Meh = Meh { +LL | | x: &UnsafeCell::new(42), +LL | | }; + | |__^ + +error: mutable memory (`UnsafeCell`) is not allowed in constant + --> $DIR/mutable_references_err.rs:26:1 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: mutable memory (`&mut`) is not allowed in constant + --> $DIR/mutable_references_err.rs:30:1 + | +LL | const BLUNT: &mut i32 = &mut 42; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:17:8 + | +LL | x: &UnsafeCell::new(42), + | ^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:26:27 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references_err.rs:30:25 + | +LL | const BLUNT: &mut i32 = &mut 42; + | ^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs b/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs deleted file mode 100644 index 635cad81c9798..0000000000000 --- a/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs +++ /dev/null @@ -1,29 +0,0 @@ -// compile-flags: -Zunleash-the-miri-inside-of-you -// failure-status: 101 -// rustc-env:RUST_BACKTRACE=0 -// normalize-stderr-test "note: rustc 1.* running on .*" -> "note: rustc VERSION running on TARGET" -// normalize-stderr-test "note: compiler flags: .*" -> "note: compiler flags: FLAGS" -// normalize-stderr-test "interpret/intern.rs:[0-9]*:[0-9]*" -> "interpret/intern.rs:LL:CC" - -#![allow(const_err)] - -use std::cell::UnsafeCell; - -// this test ICEs to ensure that our mutability story is sound - -struct Meh { - x: &'static UnsafeCell, -} - -unsafe impl Sync for Meh {} - -// the following will never be ok! -const MUH: Meh = Meh { - x: &UnsafeCell::new(42), //~ WARN: skipping const checks -}; - -fn main() { - unsafe { - *MUH.x.get() = 99; - } -} diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr deleted file mode 100644 index c292fcef7f660..0000000000000 --- a/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr +++ /dev/null @@ -1,21 +0,0 @@ -warning: skipping const checks - --> $DIR/mutable_references_ice.rs:22:8 - | -LL | x: &UnsafeCell::new(42), - | ^^^^^^^^^^^^^^^^^^^^ - -thread 'rustc' panicked at 'assertion failed: `(left != right)` - left: `Const`, - right: `Const`: UnsafeCells are not allowed behind references in constants. This should have been prevented statically by const qualification. If this were allowed one would be able to change a constant at one use site and other use sites could observe that mutation.', src/librustc_mir/interpret/intern.rs:LL:CC -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace - -error: internal compiler error: unexpected panic - -note: the compiler unexpectedly panicked. this is a bug. - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports - -note: rustc VERSION running on TARGET - -note: compiler flags: FLAGS - diff --git a/src/test/ui/consts/miri_unleashed/mutating_global.rs b/src/test/ui/consts/miri_unleashed/mutating_global.rs new file mode 100644 index 0000000000000..902fe0aa1e7e4 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutating_global.rs @@ -0,0 +1,16 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![allow(const_err)] + +// Make sure we cannot mutate globals. + +static mut GLOBAL: i32 = 0; + +static MUTATING_GLOBAL: () = { + unsafe { + GLOBAL = 99 + //~^ ERROR could not evaluate static initializer + //~| NOTE modifying a static's initial value + } +}; + +fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/mutating_global.stderr b/src/test/ui/consts/miri_unleashed/mutating_global.stderr new file mode 100644 index 0000000000000..ba9dd56190ac1 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutating_global.stderr @@ -0,0 +1,9 @@ +error[E0080]: could not evaluate static initializer + --> $DIR/mutating_global.rs:10:9 + | +LL | GLOBAL = 99 + | ^^^^^^^^^^^ modifying a static's initial value from another static's initializer + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/non_const_fn.rs b/src/test/ui/consts/miri_unleashed/non_const_fn.rs index cfb57d21ceec5..70da94df7a265 100644 --- a/src/test/ui/consts/miri_unleashed/non_const_fn.rs +++ b/src/test/ui/consts/miri_unleashed/non_const_fn.rs @@ -1,17 +1,13 @@ -// build-fail // compile-flags: -Zunleash-the-miri-inside-of-you -#![warn(const_err)] +#![allow(const_err)] // A test demonstrating that we prevent calling non-const fn during CTFE. fn foo() {} -const C: () = foo(); //~ WARN: skipping const checks -//~^ WARN any use of this value will cause an error +static C: () = foo(); +//~^ ERROR could not evaluate static initializer +//~| NOTE calling non-const function `foo` -fn main() { - println!("{:?}", C); - //~^ ERROR: evaluation of constant expression failed - //~| WARN: erroneous constant used [const_err] -} +fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/non_const_fn.stderr b/src/test/ui/consts/miri_unleashed/non_const_fn.stderr index dcd37345fdd58..3e9658ad88ec0 100644 --- a/src/test/ui/consts/miri_unleashed/non_const_fn.stderr +++ b/src/test/ui/consts/miri_unleashed/non_const_fn.stderr @@ -1,35 +1,17 @@ -warning: skipping const checks - --> $DIR/non_const_fn.rs:10:15 +error[E0080]: could not evaluate static initializer + --> $DIR/non_const_fn.rs:9:16 | -LL | const C: () = foo(); - | ^^^^^ +LL | static C: () = foo(); + | ^^^^^ calling non-const function `foo` -warning: any use of this value will cause an error - --> $DIR/non_const_fn.rs:10:15 - | -LL | const C: () = foo(); - | --------------^^^^^- - | | - | calling non-const function `foo` - | -note: the lint level is defined here - --> $DIR/non_const_fn.rs:4:9 - | -LL | #![warn(const_err)] - | ^^^^^^^^^ - -error[E0080]: evaluation of constant expression failed - --> $DIR/non_const_fn.rs:14:22 +warning: skipping const checks | -LL | println!("{:?}", C); - | ^ referenced constant has errors - -warning: erroneous constant used - --> $DIR/non_const_fn.rs:14:22 +help: skipping check that does not even have a feature gate + --> $DIR/non_const_fn.rs:9:16 | -LL | println!("{:?}", C); - | ^ referenced constant has errors +LL | static C: () = foo(); + | ^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.rs b/src/test/ui/consts/miri_unleashed/ptr_arith.rs new file mode 100644 index 0000000000000..81985f9f625a5 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -0,0 +1,29 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![feature(core_intrinsics)] +#![allow(const_err)] + +// A test demonstrating that we prevent doing even trivial +// pointer arithmetic or comparison during CTFE. + +static CMP: () = { + let x = &0 as *const _; + let _v = x == x; + //~^ ERROR could not evaluate static initializer + //~| NOTE pointer arithmetic or comparison +}; + +static INT_PTR_ARITH: () = unsafe { + let x: usize = std::mem::transmute(&0); + let _v = x + 0; + //~^ ERROR could not evaluate static initializer + //~| NOTE pointer-to-integer cast +}; + +static PTR_ARITH: () = unsafe { + let x = &0 as *const _; + let _v = core::intrinsics::offset(x, 0); + //~^ ERROR could not evaluate static initializer + //~| NOTE calling intrinsic `offset` +}; + +fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr new file mode 100644 index 0000000000000..5bd534a16b863 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr @@ -0,0 +1,39 @@ +error[E0080]: could not evaluate static initializer + --> $DIR/ptr_arith.rs:10:14 + | +LL | let _v = x == x; + | ^^^^^^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants + +error[E0080]: could not evaluate static initializer + --> $DIR/ptr_arith.rs:17:14 + | +LL | let _v = x + 0; + | ^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants + +error[E0080]: could not evaluate static initializer + --> $DIR/ptr_arith.rs:24:14 + | +LL | let _v = core::intrinsics::offset(x, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "calling intrinsic `offset`" needs an rfc before being allowed inside constants + +warning: skipping const checks + | +help: skipping check for `const_compare_raw_pointers` feature + --> $DIR/ptr_arith.rs:10:14 + | +LL | let _v = x == x; + | ^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/ptr_arith.rs:16:20 + | +LL | let x: usize = std::mem::transmute(&0); + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/ptr_arith.rs:24:14 + | +LL | let _v = core::intrinsics::offset(x, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/raw_mutable_const.rs b/src/test/ui/consts/miri_unleashed/raw_mutable_const.rs new file mode 100644 index 0000000000000..cabd754e01ac3 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/raw_mutable_const.rs @@ -0,0 +1,10 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you + +#![allow(const_err)] + +use std::cell::UnsafeCell; + +const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; +//~^ ERROR: untyped pointers are not allowed in constant + +fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/raw_mutable_const.stderr b/src/test/ui/consts/miri_unleashed/raw_mutable_const.stderr new file mode 100644 index 0000000000000..b5b5a965295a7 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/raw_mutable_const.stderr @@ -0,0 +1,16 @@ +error: untyped pointers are not allowed in constant + --> $DIR/raw_mutable_const.rs:7:1 + | +LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/raw_mutable_const.rs:7:38 + | +LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/consts/miri_unleashed/read_from_static.rs b/src/test/ui/consts/miri_unleashed/read_from_static.rs deleted file mode 100644 index 821c501c9fcc5..0000000000000 --- a/src/test/ui/consts/miri_unleashed/read_from_static.rs +++ /dev/null @@ -1,11 +0,0 @@ -// run-pass -// compile-flags: -Zunleash-the-miri-inside-of-you -#![feature(const_mut_refs)] -#![allow(const_err)] - -static OH_YES: &mut i32 = &mut 42; - -fn main() { - // Make sure `OH_YES` can be read. - assert_eq!(*OH_YES, 42); -} diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs index 6c4beaf2ea5ac..a7902f20467a1 100644 --- a/src/test/ui/consts/offset_from_ub.rs +++ b/src/test/ui/consts/offset_from_ub.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #![feature(const_raw_ptr_deref)] #![feature(const_ptr_offset_from)] #![feature(ptr_offset_from)] diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr index 217530740079f..92ecea5fdacdd 100644 --- a/src/test/ui/consts/offset_from_ub.stderr +++ b/src/test/ui/consts/offset_from_ub.stderr @@ -5,9 +5,10 @@ LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | ptr_offset_from cannot compute offset of pointers into different allocations. - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:22:27 + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `DIFFERENT_ALLOC` at $DIR/offset_from_ub.rs:17:27 | - ::: $DIR/offset_from_ub.rs:16:1 + ::: $DIR/offset_from_ub.rs:11:1 | LL | / pub const DIFFERENT_ALLOC: usize = { LL | | @@ -27,9 +28,10 @@ LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unable to turn bytes into a pointer - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:28:14 + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `NOT_PTR` at $DIR/offset_from_ub.rs:23:14 | - ::: $DIR/offset_from_ub.rs:26:1 + ::: $DIR/offset_from_ub.rs:21:1 | LL | / pub const NOT_PTR: usize = { LL | | @@ -44,9 +46,10 @@ LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | exact_div: 1isize cannot be divided by 2isize without remainder - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:36:14 + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `NOT_MULTIPLE_OF_SIZE` at $DIR/offset_from_ub.rs:31:14 | - ::: $DIR/offset_from_ub.rs:31:1 + ::: $DIR/offset_from_ub.rs:26:1 | LL | / pub const NOT_MULTIPLE_OF_SIZE: isize = { LL | | @@ -63,10 +66,11 @@ error: any use of this value will cause an error LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | invalid use of NULL pointer - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:42:14 + | inbounds test failed: 0x0 is not a valid pointer + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `OFFSET_FROM_NULL` at $DIR/offset_from_ub.rs:37:14 | - ::: $DIR/offset_from_ub.rs:39:1 + ::: $DIR/offset_from_ub.rs:34:1 | LL | / pub const OFFSET_FROM_NULL: isize = { LL | | @@ -82,9 +86,10 @@ LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unable to turn bytes into a pointer - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:49:14 + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `DIFFERENT_INT` at $DIR/offset_from_ub.rs:44:14 | - ::: $DIR/offset_from_ub.rs:45:1 + ::: $DIR/offset_from_ub.rs:40:1 | LL | / pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC LL | | diff --git a/src/test/ui/consts/packed_pattern.stderr b/src/test/ui/consts/packed_pattern.stderr index 9b7daf2e674fb..9ca50a95e4e41 100644 --- a/src/test/ui/consts/packed_pattern.stderr +++ b/src/test/ui/consts/packed_pattern.stderr @@ -6,3 +6,5 @@ LL | FOO => unreachable!(), | = note: `#[warn(unreachable_patterns)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/consts/packed_pattern2.stderr b/src/test/ui/consts/packed_pattern2.stderr index 6cc0225d3043d..4dc54461eeb24 100644 --- a/src/test/ui/consts/packed_pattern2.stderr +++ b/src/test/ui/consts/packed_pattern2.stderr @@ -6,3 +6,5 @@ LL | FOO => unreachable!(), | = note: `#[warn(unreachable_patterns)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/consts/projection_qualif.stock.stderr b/src/test/ui/consts/projection_qualif.stock.stderr index 75625a4bd1bc3..cfa48d947c992 100644 --- a/src/test/ui/consts/projection_qualif.stock.stderr +++ b/src/test/ui/consts/projection_qualif.stock.stderr @@ -21,6 +21,8 @@ error[E0019]: constant contains unimplemented expression type | LL | unsafe { *b = 5; } | ^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to 3 previous errors diff --git a/src/test/ui/consts/promoted_div_by_zero.rs b/src/test/ui/consts/promoted_div_by_zero.rs new file mode 100644 index 0000000000000..b4503f691ffd9 --- /dev/null +++ b/src/test/ui/consts/promoted_div_by_zero.rs @@ -0,0 +1,9 @@ +#![allow(unconditional_panic, const_err)] + +// run-fail +// error-pattern: attempt to divide by zero +// ignore-emscripten no processes + +fn main() { + let x = &(1 / (1 - 1)); +} diff --git a/src/test/ui/consts/raw-ptr-const.rs b/src/test/ui/consts/raw-ptr-const.rs new file mode 100644 index 0000000000000..00fad046b557d --- /dev/null +++ b/src/test/ui/consts/raw-ptr-const.rs @@ -0,0 +1,10 @@ +#![allow(const_err)] // make sure we hit the `delay_span_bug` + +// This is a regression test for a `delay_span_bug` during interning when a constant +// evaluates to a (non-dangling) raw pointer. For now this errors; potentially it +// could also be allowed. + +const CONST_RAW: *const Vec = &Vec::new() as *const _; +//~^ ERROR untyped pointers are not allowed in constant + +fn main() {} diff --git a/src/test/ui/consts/raw-ptr-const.stderr b/src/test/ui/consts/raw-ptr-const.stderr new file mode 100644 index 0000000000000..974b1c3ff45b5 --- /dev/null +++ b/src/test/ui/consts/raw-ptr-const.stderr @@ -0,0 +1,8 @@ +error: untyped pointers are not allowed in constant + --> $DIR/raw-ptr-const.rs:7:1 + | +LL | const CONST_RAW: *const Vec = &Vec::new() as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/consts/read_from_static_mut_ref.rs b/src/test/ui/consts/read_from_static_mut_ref.rs new file mode 100644 index 0000000000000..c18227e0f5515 --- /dev/null +++ b/src/test/ui/consts/read_from_static_mut_ref.rs @@ -0,0 +1,10 @@ +// run-pass +#![feature(const_mut_refs)] +#![allow(const_err)] + +static OH_YES: &mut i32 = &mut 42; + +fn main() { + // Make sure `OH_YES` can be read. + assert_eq!(*OH_YES, 42); +} diff --git a/src/test/ui/consts/recursive-zst-static.default.stderr b/src/test/ui/consts/recursive-zst-static.default.stderr new file mode 100644 index 0000000000000..d424b22f000bf --- /dev/null +++ b/src/test/ui/consts/recursive-zst-static.default.stderr @@ -0,0 +1,21 @@ +error[E0391]: cycle detected when const-evaluating `FOO` + --> $DIR/recursive-zst-static.rs:10:18 + | +LL | static FOO: () = FOO; + | ^^^ + | +note: ...which requires const-evaluating `FOO`... + --> $DIR/recursive-zst-static.rs:10:1 + | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires const-evaluating `FOO`, completing the cycle +note: cycle used when const-evaluating + checking `FOO` + --> $DIR/recursive-zst-static.rs:10:1 + | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/consts/recursive-zst-static.rs b/src/test/ui/consts/recursive-zst-static.rs index df7562bd9f5d2..29a467c006a49 100644 --- a/src/test/ui/consts/recursive-zst-static.rs +++ b/src/test/ui/consts/recursive-zst-static.rs @@ -1,6 +1,13 @@ -// build-pass +// revisions: default unleash +//[unleash]compile-flags: -Zunleash-the-miri-inside-of-you -static FOO: () = FOO; +// This test ensures that we do not allow ZST statics to initialize themselves without ever +// actually creating a value of that type. This is important, as the ZST may have private fields +// that users can reasonably expect to only get initialized by their own code. Thus unsafe code +// can depend on this fact and will thus do unsound things when it is violated. +// See https://github.com/rust-lang/rust/issues/71078 for more details. + +static FOO: () = FOO; //~ cycle detected when const-evaluating `FOO` fn main() { FOO diff --git a/src/test/ui/consts/recursive-zst-static.unleash.stderr b/src/test/ui/consts/recursive-zst-static.unleash.stderr new file mode 100644 index 0000000000000..d424b22f000bf --- /dev/null +++ b/src/test/ui/consts/recursive-zst-static.unleash.stderr @@ -0,0 +1,21 @@ +error[E0391]: cycle detected when const-evaluating `FOO` + --> $DIR/recursive-zst-static.rs:10:18 + | +LL | static FOO: () = FOO; + | ^^^ + | +note: ...which requires const-evaluating `FOO`... + --> $DIR/recursive-zst-static.rs:10:1 + | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires const-evaluating `FOO`, completing the cycle +note: cycle used when const-evaluating + checking `FOO` + --> $DIR/recursive-zst-static.rs:10:1 + | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr b/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr index b43fbc86f99f2..8db75dd63cf2a 100644 --- a/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr +++ b/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/static_mut_containing_mut_ref2.rs:7:45 | LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer error: aborting due to previous error diff --git a/src/test/ui/consts/static_mut_containing_mut_ref2.stock.stderr b/src/test/ui/consts/static_mut_containing_mut_ref2.stock.stderr index c70431886e868..cc169351bf268 100644 --- a/src/test/ui/consts/static_mut_containing_mut_ref2.stock.stderr +++ b/src/test/ui/consts/static_mut_containing_mut_ref2.stock.stderr @@ -12,6 +12,8 @@ error[E0019]: static contains unimplemented expression type | LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/static_mut_containing_mut_ref3.stderr b/src/test/ui/consts/static_mut_containing_mut_ref3.stderr index e88e49b097af2..91f9dbd8d0b9e 100644 --- a/src/test/ui/consts/static_mut_containing_mut_ref3.stderr +++ b/src/test/ui/consts/static_mut_containing_mut_ref3.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/static_mut_containing_mut_ref3.rs:3:31 | LL | static mut BAR: () = unsafe { FOO.0 = 99; }; - | ^^^^^^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^^^^^^ modifying a static's initial value from another static's initializer error: aborting due to previous error diff --git a/src/test/ui/consts/too_generic_eval_ice.rs b/src/test/ui/consts/too_generic_eval_ice.rs index 7e4d4dbe44610..3ea5f88f07d1e 100644 --- a/src/test/ui/consts/too_generic_eval_ice.rs +++ b/src/test/ui/consts/too_generic_eval_ice.rs @@ -4,10 +4,9 @@ impl Foo { const HOST_SIZE: usize = std::mem::size_of::(); pub fn crash() -> bool { - [5; Self::HOST_SIZE] == [6; 0] //~ ERROR no associated item named `HOST_SIZE` - //~^ the size for values of type `A` cannot be known - //~| the size for values of type `B` cannot be known - //~| binary operation `==` cannot be applied to type `[{integer}; _]` + [5; Self::HOST_SIZE] == [6; 0] + //~^ ERROR constant expression depends on a generic parameter + //~| ERROR binary operation `==` cannot be applied to type `[{integer}; _]` } } diff --git a/src/test/ui/consts/too_generic_eval_ice.stderr b/src/test/ui/consts/too_generic_eval_ice.stderr index ffa28225b79c6..8b29c533bcc93 100644 --- a/src/test/ui/consts/too_generic_eval_ice.stderr +++ b/src/test/ui/consts/too_generic_eval_ice.stderr @@ -1,45 +1,10 @@ -error[E0599]: no associated item named `HOST_SIZE` found for struct `Foo` in the current scope - --> $DIR/too_generic_eval_ice.rs:7:19 - | -LL | pub struct Foo(A, B); - | --------------------------- associated item `HOST_SIZE` not found for this -... -LL | [5; Self::HOST_SIZE] == [6; 0] - | ^^^^^^^^^ associated item not found in `Foo` - | - = note: the method `HOST_SIZE` exists but the following trait bounds were not satisfied: - `A: std::marker::Sized` - `B: std::marker::Sized` - -error[E0277]: the size for values of type `A` cannot be known at compilation time - --> $DIR/too_generic_eval_ice.rs:7:13 - | -LL | pub struct Foo(A, B); - | --------------------------- required by `Foo` -LL | -LL | impl Foo { - | - this type parameter needs to be `std::marker::Sized` -... -LL | [5; Self::HOST_SIZE] == [6; 0] - | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `std::marker::Sized` is not implemented for `A` - = note: to learn more, visit - -error[E0277]: the size for values of type `B` cannot be known at compilation time +error: constant expression depends on a generic parameter --> $DIR/too_generic_eval_ice.rs:7:13 | -LL | pub struct Foo(A, B); - | --------------------------- required by `Foo` -LL | -LL | impl Foo { - | - this type parameter needs to be `std::marker::Sized` -... LL | [5; Self::HOST_SIZE] == [6; 0] - | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^^ | - = help: the trait `std::marker::Sized` is not implemented for `B` - = note: to learn more, visit + = note: this may fail depending on what value the parameter takes error[E0369]: binary operation `==` cannot be applied to type `[{integer}; _]` --> $DIR/too_generic_eval_ice.rs:7:30 @@ -49,7 +14,6 @@ LL | [5; Self::HOST_SIZE] == [6; 0] | | | [{integer}; _] -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0277, E0369, E0599. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0369`. diff --git a/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr b/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr index 5a47771459625..b4970c82adb3e 100644 --- a/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr +++ b/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | tried to transmute from usize to &[u8], but their sizes differed + | transmuting `usize` to `&[u8]` is not possible, because these types do not have the same size | = note: `#[deny(const_err)]` on by default diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.stderr b/src/test/ui/consts/uninhabited-const-issue-61744.stderr index 1f3e2cf5b2f58..ca232380897e3 100644 --- a/src/test/ui/consts/uninhabited-const-issue-61744.stderr +++ b/src/test/ui/consts/uninhabited-const-issue-61744.stderr @@ -4,76 +4,140 @@ error[E0080]: evaluation of constant value failed LL | hint_unreachable() | ------------------ | | - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 - | inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 ... LL | fake_type() | ^^^^^^^^^^^ | | | reached the configured maximum number of stack frames - | inside call to `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 + | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 error: any use of this value will cause an error --> $DIR/uninhabited-const-issue-61744.rs:12:36 diff --git a/src/test/ui/copy-a-resource.rs b/src/test/ui/copy-a-resource.rs index 1a647692018e5..55f2dd4ee6dde 100644 --- a/src/test/ui/copy-a-resource.rs +++ b/src/test/ui/copy-a-resource.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #[derive(Debug)] struct Foo { i: isize, diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr index 71d2eead3554c..a5c961a061acb 100644 --- a/src/test/ui/copy-a-resource.stderr +++ b/src/test/ui/copy-a-resource.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `Foo` in the current scope - --> $DIR/copy-a-resource.rs:23:16 + --> $DIR/copy-a-resource.rs:18:16 | LL | struct Foo { | ---------- method `clone` not found for this diff --git a/src/test/ui/crate-in-paths.stderr b/src/test/ui/crate-in-paths.stderr index 38d222f980d2d..c3aa135f77d5b 100644 --- a/src/test/ui/crate-in-paths.stderr +++ b/src/test/ui/crate-in-paths.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find value `Foo` in this scope LL | Foo; | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this unit struct | LL | use crate::bar::Foo; | diff --git a/src/test/ui/crt-static-on-works.rs b/src/test/ui/crt-static-on-works.rs index 21407b1b9118c..f89d1edd6586a 100644 --- a/src/test/ui/crt-static-on-works.rs +++ b/src/test/ui/crt-static-on-works.rs @@ -1,9 +1,6 @@ // run-pass - -#![allow(stable_features)] -// compile-flags:-C target-feature=+crt-static -Z unstable-options - -#![feature(cfg_target_feature)] +// compile-flags:-C target-feature=+crt-static +// only-msvc #[cfg(target_feature = "crt-static")] fn main() {} diff --git a/src/test/ui/deduplicate-diagnostics-2.deduplicate.stderr b/src/test/ui/deduplicate-diagnostics-2.deduplicate.stderr index 7a28c6428a355..7f6a432d5ab1b 100644 --- a/src/test/ui/deduplicate-diagnostics-2.deduplicate.stderr +++ b/src/test/ui/deduplicate-diagnostics-2.deduplicate.stderr @@ -26,3 +26,5 @@ LL | 1.0 => {} = 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 #41620 +warning: 3 warnings emitted + diff --git a/src/test/ui/deduplicate-diagnostics-2.duplicate.stderr b/src/test/ui/deduplicate-diagnostics-2.duplicate.stderr index 4fff3a8c0f374..f2315bc91ec64 100644 --- a/src/test/ui/deduplicate-diagnostics-2.duplicate.stderr +++ b/src/test/ui/deduplicate-diagnostics-2.duplicate.stderr @@ -35,3 +35,5 @@ LL | 2.0 => {} = 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 #41620 +warning: 4 warnings emitted + diff --git a/src/test/ui/deprecation/atomic_initializers.fixed b/src/test/ui/deprecation/atomic_initializers.fixed index 363bda75f1695..d8485ed7da169 100644 --- a/src/test/ui/deprecation/atomic_initializers.fixed +++ b/src/test/ui/deprecation/atomic_initializers.fixed @@ -1,5 +1,5 @@ // run-rustfix -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #[allow(deprecated, unused_imports)] use std::sync::atomic::{AtomicIsize, ATOMIC_ISIZE_INIT}; diff --git a/src/test/ui/deprecation/atomic_initializers.rs b/src/test/ui/deprecation/atomic_initializers.rs index 00c5f7b0b1257..b15a1bbfd92d6 100644 --- a/src/test/ui/deprecation/atomic_initializers.rs +++ b/src/test/ui/deprecation/atomic_initializers.rs @@ -1,5 +1,5 @@ // run-rustfix -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #[allow(deprecated, unused_imports)] use std::sync::atomic::{AtomicIsize, ATOMIC_ISIZE_INIT}; diff --git a/src/test/ui/deprecation/atomic_initializers.stderr b/src/test/ui/deprecation/atomic_initializers.stderr index 16db00b6e853a..75baf4a1bf90d 100644 --- a/src/test/ui/deprecation/atomic_initializers.stderr +++ b/src/test/ui/deprecation/atomic_initializers.stderr @@ -6,3 +6,5 @@ LL | static FOO: AtomicIsize = ATOMIC_ISIZE_INIT; | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/deprecation/deprecated-macro_escape-inner.stderr b/src/test/ui/deprecation/deprecated-macro_escape-inner.stderr index 4b0fc07463a99..5b866bbbe8367 100644 --- a/src/test/ui/deprecation/deprecated-macro_escape-inner.stderr +++ b/src/test/ui/deprecation/deprecated-macro_escape-inner.stderr @@ -6,3 +6,5 @@ LL | #![macro_escape] | = help: try an outer attribute: `#[macro_use]` +warning: 1 warning emitted + diff --git a/src/test/ui/deprecation/deprecated-macro_escape.stderr b/src/test/ui/deprecation/deprecated-macro_escape.stderr index 70094083d4b34..0bb8dc17e4293 100644 --- a/src/test/ui/deprecation/deprecated-macro_escape.stderr +++ b/src/test/ui/deprecation/deprecated-macro_escape.stderr @@ -4,3 +4,5 @@ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` LL | #[macro_escape] | ^^^^^^^^^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/deprecation/deprecation-in-future.stderr b/src/test/ui/deprecation/deprecation-in-future.stderr index 4268680e9d9e0..3040dcd9939fe 100644 --- a/src/test/ui/deprecation/deprecation-in-future.stderr +++ b/src/test/ui/deprecation/deprecation-in-future.stderr @@ -6,3 +6,5 @@ LL | deprecated_future(); // ok; deprecated_in_future only applies to rustc_ | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/derive-uninhabited-enum-38885.rs b/src/test/ui/derive-uninhabited-enum-38885.rs index 0b4c8f3952fcd..35c3065ea8150 100644 --- a/src/test/ui/derive-uninhabited-enum-38885.rs +++ b/src/test/ui/derive-uninhabited-enum-38885.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // compile-flags: -Wunused // ensure there are no special warnings about uninhabited types diff --git a/src/test/ui/derive-uninhabited-enum-38885.stderr b/src/test/ui/derive-uninhabited-enum-38885.stderr index a3ed6798a7039..72607629d3c10 100644 --- a/src/test/ui/derive-uninhabited-enum-38885.stderr +++ b/src/test/ui/derive-uninhabited-enum-38885.stderr @@ -6,3 +6,5 @@ LL | Void(Void), | = note: `-W dead-code` implied by `-W unused` +warning: 1 warning emitted + diff --git a/src/test/ui/derives/derive-assoc-type-not-impl.rs b/src/test/ui/derives/derive-assoc-type-not-impl.rs index fa5afd2419261..0f642d63a1dcb 100644 --- a/src/test/ui/derives/derive-assoc-type-not-impl.rs +++ b/src/test/ui/derives/derive-assoc-type-not-impl.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - trait Foo { type X; fn method(&self) {} diff --git a/src/test/ui/derives/derive-assoc-type-not-impl.stderr b/src/test/ui/derives/derive-assoc-type-not-impl.stderr index f15aba97ded81..be446feb847eb 100644 --- a/src/test/ui/derives/derive-assoc-type-not-impl.stderr +++ b/src/test/ui/derives/derive-assoc-type-not-impl.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `Bar` in the current scope - --> $DIR/derive-assoc-type-not-impl.rs:23:30 + --> $DIR/derive-assoc-type-not-impl.rs:18:30 | LL | struct Bar { | ------------------ diff --git a/src/test/ui/derives/derives-span-Clone-enum-struct-variant.rs b/src/test/ui/derives/derives-span-Clone-enum-struct-variant.rs index 7c416fdcfb182..b556d442420e0 100644 --- a/src/test/ui/derives/derives-span-Clone-enum-struct-variant.rs +++ b/src/test/ui/derives/derives-span-Clone-enum-struct-variant.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr index 8ef2d3d30238f..bbb8776f4fdef 100644 --- a/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied - --> $DIR/derives-span-Clone-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-Clone-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ the trait `std::clone::Clone` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Clone-enum.rs b/src/test/ui/derives/derives-span-Clone-enum.rs index c013ccd934ed1..9bb4f486c3ef0 100644 --- a/src/test/ui/derives/derives-span-Clone-enum.rs +++ b/src/test/ui/derives/derives-span-Clone-enum.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Clone-enum.stderr b/src/test/ui/derives/derives-span-Clone-enum.stderr index 8c740733e2fa4..0e410e795e0e5 100644 --- a/src/test/ui/derives/derives-span-Clone-enum.stderr +++ b/src/test/ui/derives/derives-span-Clone-enum.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied - --> $DIR/derives-span-Clone-enum.rs:13:6 + --> $DIR/derives-span-Clone-enum.rs:9:6 | LL | Error | ^^^^^ the trait `std::clone::Clone` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Clone-struct.rs b/src/test/ui/derives/derives-span-Clone-struct.rs index 5a78a92b823d7..f151636f848a0 100644 --- a/src/test/ui/derives/derives-span-Clone-struct.rs +++ b/src/test/ui/derives/derives-span-Clone-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Clone-struct.stderr b/src/test/ui/derives/derives-span-Clone-struct.stderr index 75a59fbf035d4..889128a662349 100644 --- a/src/test/ui/derives/derives-span-Clone-struct.stderr +++ b/src/test/ui/derives/derives-span-Clone-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied - --> $DIR/derives-span-Clone-struct.rs:12:5 + --> $DIR/derives-span-Clone-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ the trait `std::clone::Clone` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Clone-tuple-struct.rs b/src/test/ui/derives/derives-span-Clone-tuple-struct.rs index 39461d67d16ed..7a62885324ebd 100644 --- a/src/test/ui/derives/derives-span-Clone-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-Clone-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr b/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr index 1860c5f2ff6ac..0024199ca59cd 100644 --- a/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied - --> $DIR/derives-span-Clone-tuple-struct.rs:12:5 + --> $DIR/derives-span-Clone-tuple-struct.rs:8:5 | LL | Error | ^^^^^ the trait `std::clone::Clone` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Debug-enum-struct-variant.rs b/src/test/ui/derives/derives-span-Debug-enum-struct-variant.rs index 060983d36737f..949597bc8f6ee 100644 --- a/src/test/ui/derives/derives-span-Debug-enum-struct-variant.rs +++ b/src/test/ui/derives/derives-span-Debug-enum-struct-variant.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr index ab3c5ef3c1d81..77779a55b68ce 100644 --- a/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0277]: `Error` doesn't implement `std::fmt::Debug` - --> $DIR/derives-span-Debug-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-Debug-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ `Error` cannot be formatted using `{:?}` diff --git a/src/test/ui/derives/derives-span-Debug-enum.rs b/src/test/ui/derives/derives-span-Debug-enum.rs index 109c8f23cdf7c..b2a39708ceb9a 100644 --- a/src/test/ui/derives/derives-span-Debug-enum.rs +++ b/src/test/ui/derives/derives-span-Debug-enum.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Debug-enum.stderr b/src/test/ui/derives/derives-span-Debug-enum.stderr index e0a76d5251594..f64c33c2bcc36 100644 --- a/src/test/ui/derives/derives-span-Debug-enum.stderr +++ b/src/test/ui/derives/derives-span-Debug-enum.stderr @@ -1,5 +1,5 @@ error[E0277]: `Error` doesn't implement `std::fmt::Debug` - --> $DIR/derives-span-Debug-enum.rs:13:6 + --> $DIR/derives-span-Debug-enum.rs:9:6 | LL | Error | ^^^^^ `Error` cannot be formatted using `{:?}` diff --git a/src/test/ui/derives/derives-span-Debug-struct.rs b/src/test/ui/derives/derives-span-Debug-struct.rs index b52e2879a4c5a..cf91c9436a623 100644 --- a/src/test/ui/derives/derives-span-Debug-struct.rs +++ b/src/test/ui/derives/derives-span-Debug-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Debug-struct.stderr b/src/test/ui/derives/derives-span-Debug-struct.stderr index 2f5cba09e4c2a..0013bcf8325e6 100644 --- a/src/test/ui/derives/derives-span-Debug-struct.stderr +++ b/src/test/ui/derives/derives-span-Debug-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: `Error` doesn't implement `std::fmt::Debug` - --> $DIR/derives-span-Debug-struct.rs:12:5 + --> $DIR/derives-span-Debug-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ `Error` cannot be formatted using `{:?}` diff --git a/src/test/ui/derives/derives-span-Debug-tuple-struct.rs b/src/test/ui/derives/derives-span-Debug-tuple-struct.rs index 1855c7fba8812..cea973c91a783 100644 --- a/src/test/ui/derives/derives-span-Debug-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-Debug-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr b/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr index 58ec131d54155..7e0039e8a795d 100644 --- a/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: `Error` doesn't implement `std::fmt::Debug` - --> $DIR/derives-span-Debug-tuple-struct.rs:12:5 + --> $DIR/derives-span-Debug-tuple-struct.rs:8:5 | LL | Error | ^^^^^ `Error` cannot be formatted using `{:?}` diff --git a/src/test/ui/derives/derives-span-Default-struct.rs b/src/test/ui/derives/derives-span-Default-struct.rs index bf60b12ac4806..71fd5829e7585 100644 --- a/src/test/ui/derives/derives-span-Default-struct.rs +++ b/src/test/ui/derives/derives-span-Default-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Default-struct.stderr b/src/test/ui/derives/derives-span-Default-struct.stderr index b97dda719ab78..492847fc022f3 100644 --- a/src/test/ui/derives/derives-span-Default-struct.stderr +++ b/src/test/ui/derives/derives-span-Default-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::default::Default` is not satisfied - --> $DIR/derives-span-Default-struct.rs:12:5 + --> $DIR/derives-span-Default-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ the trait `std::default::Default` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Default-tuple-struct.rs b/src/test/ui/derives/derives-span-Default-tuple-struct.rs index 8b89b75672071..463f7d230ca41 100644 --- a/src/test/ui/derives/derives-span-Default-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-Default-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Default-tuple-struct.stderr b/src/test/ui/derives/derives-span-Default-tuple-struct.stderr index d976891f41fea..fa7b27e770f0a 100644 --- a/src/test/ui/derives/derives-span-Default-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Default-tuple-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::default::Default` is not satisfied - --> $DIR/derives-span-Default-tuple-struct.rs:12:5 + --> $DIR/derives-span-Default-tuple-struct.rs:8:5 | LL | Error | ^^^^^ the trait `std::default::Default` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Eq-enum-struct-variant.rs b/src/test/ui/derives/derives-span-Eq-enum-struct-variant.rs index 36e6ae81e275c..d2dab8687f774 100644 --- a/src/test/ui/derives/derives-span-Eq-enum-struct-variant.rs +++ b/src/test/ui/derives/derives-span-Eq-enum-struct-variant.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr index f886c29c4db9a..3d7487a4d92a3 100644 --- a/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr @@ -1,10 +1,14 @@ error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied - --> $DIR/derives-span-Eq-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-Eq-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | + ::: $SRC_DIR/libcore/cmp.rs:LL:COL + | +LL | pub struct AssertParamIsEq { + | -- required by this bound in `std::cmp::AssertParamIsEq` | - = note: required by `std::cmp::AssertParamIsEq` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Eq-enum.rs b/src/test/ui/derives/derives-span-Eq-enum.rs index 4282515862cb2..c6c0d4321083b 100644 --- a/src/test/ui/derives/derives-span-Eq-enum.rs +++ b/src/test/ui/derives/derives-span-Eq-enum.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-Eq-enum.stderr b/src/test/ui/derives/derives-span-Eq-enum.stderr index 0b5470138a5b3..00345243cac5d 100644 --- a/src/test/ui/derives/derives-span-Eq-enum.stderr +++ b/src/test/ui/derives/derives-span-Eq-enum.stderr @@ -1,10 +1,14 @@ error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied - --> $DIR/derives-span-Eq-enum.rs:13:6 + --> $DIR/derives-span-Eq-enum.rs:9:6 | LL | Error | ^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | + ::: $SRC_DIR/libcore/cmp.rs:LL:COL + | +LL | pub struct AssertParamIsEq { + | -- required by this bound in `std::cmp::AssertParamIsEq` | - = note: required by `std::cmp::AssertParamIsEq` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Eq-struct.rs b/src/test/ui/derives/derives-span-Eq-struct.rs index d290ee5ae0fe8..df310039847d4 100644 --- a/src/test/ui/derives/derives-span-Eq-struct.rs +++ b/src/test/ui/derives/derives-span-Eq-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-Eq-struct.stderr b/src/test/ui/derives/derives-span-Eq-struct.stderr index 76904d6723587..3d0efa1d147ee 100644 --- a/src/test/ui/derives/derives-span-Eq-struct.stderr +++ b/src/test/ui/derives/derives-span-Eq-struct.stderr @@ -1,10 +1,14 @@ error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied - --> $DIR/derives-span-Eq-struct.rs:12:5 + --> $DIR/derives-span-Eq-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | + ::: $SRC_DIR/libcore/cmp.rs:LL:COL + | +LL | pub struct AssertParamIsEq { + | -- required by this bound in `std::cmp::AssertParamIsEq` | - = note: required by `std::cmp::AssertParamIsEq` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Eq-tuple-struct.rs b/src/test/ui/derives/derives-span-Eq-tuple-struct.rs index 6458b63139d2f..abf6526b90078 100644 --- a/src/test/ui/derives/derives-span-Eq-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-Eq-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr b/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr index ff94b989d26d5..2aec8ffdbe7ec 100644 --- a/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr @@ -1,10 +1,14 @@ error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied - --> $DIR/derives-span-Eq-tuple-struct.rs:12:5 + --> $DIR/derives-span-Eq-tuple-struct.rs:8:5 | LL | Error | ^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | + ::: $SRC_DIR/libcore/cmp.rs:LL:COL + | +LL | pub struct AssertParamIsEq { + | -- required by this bound in `std::cmp::AssertParamIsEq` | - = note: required by `std::cmp::AssertParamIsEq` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.rs b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.rs index fc04b1a2c0658..3018a7b6d03ee 100644 --- a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.rs +++ b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr index 889c725c843e7..32f4265a4d297 100644 --- a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied - --> $DIR/derives-span-Hash-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-Hash-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ the trait `std::hash::Hash` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Hash-enum.rs b/src/test/ui/derives/derives-span-Hash-enum.rs index daff0b27553e6..8ce7df18f06fd 100644 --- a/src/test/ui/derives/derives-span-Hash-enum.rs +++ b/src/test/ui/derives/derives-span-Hash-enum.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' struct Error; diff --git a/src/test/ui/derives/derives-span-Hash-enum.stderr b/src/test/ui/derives/derives-span-Hash-enum.stderr index 70b8a85d107f1..b8d6277b9bed7 100644 --- a/src/test/ui/derives/derives-span-Hash-enum.stderr +++ b/src/test/ui/derives/derives-span-Hash-enum.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied - --> $DIR/derives-span-Hash-enum.rs:12:6 + --> $DIR/derives-span-Hash-enum.rs:8:6 | LL | Error | ^^^^^ the trait `std::hash::Hash` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Hash-struct.rs b/src/test/ui/derives/derives-span-Hash-struct.rs index 12a9edae6301f..fa5e2af6be870 100644 --- a/src/test/ui/derives/derives-span-Hash-struct.rs +++ b/src/test/ui/derives/derives-span-Hash-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Hash-struct.stderr b/src/test/ui/derives/derives-span-Hash-struct.stderr index 61897392a726a..ae431d221ca40 100644 --- a/src/test/ui/derives/derives-span-Hash-struct.stderr +++ b/src/test/ui/derives/derives-span-Hash-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied - --> $DIR/derives-span-Hash-struct.rs:12:5 + --> $DIR/derives-span-Hash-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ the trait `std::hash::Hash` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Hash-tuple-struct.rs b/src/test/ui/derives/derives-span-Hash-tuple-struct.rs index 344b85d7b9116..3822bce1466ea 100644 --- a/src/test/ui/derives/derives-span-Hash-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-Hash-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr b/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr index fb929ad985b5f..db32193cee05b 100644 --- a/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied - --> $DIR/derives-span-Hash-tuple-struct.rs:12:5 + --> $DIR/derives-span-Hash-tuple-struct.rs:8:5 | LL | Error | ^^^^^ the trait `std::hash::Hash` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Ord-enum-struct-variant.rs b/src/test/ui/derives/derives-span-Ord-enum-struct-variant.rs index 196996c64c357..62355cc2d9619 100644 --- a/src/test/ui/derives/derives-span-Ord-enum-struct-variant.rs +++ b/src/test/ui/derives/derives-span-Ord-enum-struct-variant.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(Eq,PartialOrd,PartialEq)] diff --git a/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr index 7e73392fd51d5..d0286ad17e405 100644 --- a/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied - --> $DIR/derives-span-Ord-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-Ord-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Ord-enum.rs b/src/test/ui/derives/derives-span-Ord-enum.rs index 6282a69076b05..72738931d10f2 100644 --- a/src/test/ui/derives/derives-span-Ord-enum.rs +++ b/src/test/ui/derives/derives-span-Ord-enum.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(Eq,PartialOrd,PartialEq)] diff --git a/src/test/ui/derives/derives-span-Ord-enum.stderr b/src/test/ui/derives/derives-span-Ord-enum.stderr index 68df309e0462f..aabbd0a1d1b51 100644 --- a/src/test/ui/derives/derives-span-Ord-enum.stderr +++ b/src/test/ui/derives/derives-span-Ord-enum.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied - --> $DIR/derives-span-Ord-enum.rs:13:6 + --> $DIR/derives-span-Ord-enum.rs:9:6 | LL | Error | ^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Ord-struct.rs b/src/test/ui/derives/derives-span-Ord-struct.rs index e7bc7cf1c435e..53d4c2c22b55f 100644 --- a/src/test/ui/derives/derives-span-Ord-struct.rs +++ b/src/test/ui/derives/derives-span-Ord-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(Eq,PartialOrd,PartialEq)] diff --git a/src/test/ui/derives/derives-span-Ord-struct.stderr b/src/test/ui/derives/derives-span-Ord-struct.stderr index 5e1ed33509406..eaac3dafd080d 100644 --- a/src/test/ui/derives/derives-span-Ord-struct.stderr +++ b/src/test/ui/derives/derives-span-Ord-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied - --> $DIR/derives-span-Ord-struct.rs:12:5 + --> $DIR/derives-span-Ord-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-Ord-tuple-struct.rs b/src/test/ui/derives/derives-span-Ord-tuple-struct.rs index 3b623558d2af5..4e09c27098641 100644 --- a/src/test/ui/derives/derives-span-Ord-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-Ord-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(Eq,PartialOrd,PartialEq)] diff --git a/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr b/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr index d9692e56431ef..0ae36bcb8bfce 100644 --- a/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied - --> $DIR/derives-span-Ord-tuple-struct.rs:12:5 + --> $DIR/derives-span-Ord-tuple-struct.rs:8:5 | LL | Error | ^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` diff --git a/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.rs b/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.rs index f935d58025966..d66faa086dec7 100644 --- a/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.rs +++ b/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr index c669636c85043..8ff4b4687be3a 100644 --- a/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0369]: binary operation `==` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-PartialEq-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0369]: binary operation `!=` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-PartialEq-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ diff --git a/src/test/ui/derives/derives-span-PartialEq-enum.rs b/src/test/ui/derives/derives-span-PartialEq-enum.rs index a0c56818358ff..66edf460b312a 100644 --- a/src/test/ui/derives/derives-span-PartialEq-enum.rs +++ b/src/test/ui/derives/derives-span-PartialEq-enum.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-PartialEq-enum.stderr b/src/test/ui/derives/derives-span-PartialEq-enum.stderr index ff98edea4dcff..b4a12b1c4107d 100644 --- a/src/test/ui/derives/derives-span-PartialEq-enum.stderr +++ b/src/test/ui/derives/derives-span-PartialEq-enum.stderr @@ -1,5 +1,5 @@ error[E0369]: binary operation `==` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-enum.rs:13:6 + --> $DIR/derives-span-PartialEq-enum.rs:9:6 | LL | Error | ^^^^^ @@ -8,7 +8,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0369]: binary operation `!=` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-enum.rs:13:6 + --> $DIR/derives-span-PartialEq-enum.rs:9:6 | LL | Error | ^^^^^ diff --git a/src/test/ui/derives/derives-span-PartialEq-struct.rs b/src/test/ui/derives/derives-span-PartialEq-struct.rs index c190dd00396c2..ce5c67af77f05 100644 --- a/src/test/ui/derives/derives-span-PartialEq-struct.rs +++ b/src/test/ui/derives/derives-span-PartialEq-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-PartialEq-struct.stderr b/src/test/ui/derives/derives-span-PartialEq-struct.stderr index 200b8e2d503c8..b4f6c51ac3c62 100644 --- a/src/test/ui/derives/derives-span-PartialEq-struct.stderr +++ b/src/test/ui/derives/derives-span-PartialEq-struct.stderr @@ -1,5 +1,5 @@ error[E0369]: binary operation `==` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-struct.rs:12:5 + --> $DIR/derives-span-PartialEq-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0369]: binary operation `!=` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-struct.rs:12:5 + --> $DIR/derives-span-PartialEq-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ diff --git a/src/test/ui/derives/derives-span-PartialEq-tuple-struct.rs b/src/test/ui/derives/derives-span-PartialEq-tuple-struct.rs index dfc9c03ddc7a5..eaa628311361c 100644 --- a/src/test/ui/derives/derives-span-PartialEq-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-PartialEq-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' diff --git a/src/test/ui/derives/derives-span-PartialEq-tuple-struct.stderr b/src/test/ui/derives/derives-span-PartialEq-tuple-struct.stderr index 9e3d1309c2258..2e6b1d71199f4 100644 --- a/src/test/ui/derives/derives-span-PartialEq-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-PartialEq-tuple-struct.stderr @@ -1,5 +1,5 @@ error[E0369]: binary operation `==` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-tuple-struct.rs:12:5 + --> $DIR/derives-span-PartialEq-tuple-struct.rs:8:5 | LL | Error | ^^^^^ @@ -8,7 +8,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0369]: binary operation `!=` cannot be applied to type `Error` - --> $DIR/derives-span-PartialEq-tuple-struct.rs:12:5 + --> $DIR/derives-span-PartialEq-tuple-struct.rs:8:5 | LL | Error | ^^^^^ diff --git a/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.rs b/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.rs index beef639462ed3..4e7a8d71a18f4 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.rs +++ b/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr index 6433d1f5e27a5..0be75972e8ce4 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -9,7 +9,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -19,7 +19,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -29,7 +29,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -39,7 +39,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` diff --git a/src/test/ui/derives/derives-span-PartialOrd-enum.rs b/src/test/ui/derives/derives-span-PartialOrd-enum.rs index b02828da0d276..d0a6c5ab52ad7 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-enum.rs +++ b/src/test/ui/derives/derives-span-PartialOrd-enum.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-PartialOrd-enum.stderr b/src/test/ui/derives/derives-span-PartialOrd-enum.stderr index b1be7dd05f984..64290023c6dd3 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-enum.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-enum.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum.rs:9:6 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -9,7 +9,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum.rs:9:6 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -19,7 +19,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum.rs:9:6 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -29,7 +29,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum.rs:9:6 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -39,7 +39,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-enum.rs:13:6 + --> $DIR/derives-span-PartialOrd-enum.rs:9:6 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` diff --git a/src/test/ui/derives/derives-span-PartialOrd-struct.rs b/src/test/ui/derives/derives-span-PartialOrd-struct.rs index bfcfc3d5dfdd7..a596a2e32959d 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-struct.rs +++ b/src/test/ui/derives/derives-span-PartialOrd-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-PartialOrd-struct.stderr b/src/test/ui/derives/derives-span-PartialOrd-struct.stderr index 064c91fd7ddc3..dcd81589e923c 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-struct.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -9,7 +9,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -19,7 +19,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -29,7 +29,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -39,7 +39,7 @@ LL | x: Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` diff --git a/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.rs b/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.rs index c8bdd6423a023..6dd1623471045 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.rs +++ b/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // This file was auto-generated using 'src/etc/generate-deriving-span-tests.py' #[derive(PartialEq)] diff --git a/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr b/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr index 5b627022cca46..8dbf103d2dac1 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-tuple-struct.rs:8:5 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -9,7 +9,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-tuple-struct.rs:8:5 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -19,7 +19,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-tuple-struct.rs:8:5 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -29,7 +29,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-tuple-struct.rs:8:5 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` @@ -39,7 +39,7 @@ LL | Error = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `Error` with `Error` - --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5 + --> $DIR/derives-span-PartialOrd-tuple-struct.rs:8:5 | LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` diff --git a/src/test/ui/derives/deriving-copyclone.stderr b/src/test/ui/derives/deriving-copyclone.stderr index e23d48ca6304b..5fa2710cbad4e 100644 --- a/src/test/ui/derives/deriving-copyclone.stderr +++ b/src/test/ui/derives/deriving-copyclone.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `C: std::marker::Copy` is not satisfied --> $DIR/deriving-copyclone.rs:31:13 | LL | fn is_copy(_: T) {} - | ------- ---- required by this bound in `is_copy` + | ---- required by this bound in `is_copy` ... LL | is_copy(B { a: 1, b: C }); | ^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ error[E0277]: the trait bound `C: std::clone::Clone` is not satisfied --> $DIR/deriving-copyclone.rs:32:14 | LL | fn is_clone(_: T) {} - | -------- ----- required by this bound in `is_clone` + | ----- required by this bound in `is_clone` ... LL | is_clone(B { a: 1, b: C }); | ^^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ error[E0277]: the trait bound `D: std::marker::Copy` is not satisfied --> $DIR/deriving-copyclone.rs:35:13 | LL | fn is_copy(_: T) {} - | ------- ---- required by this bound in `is_copy` + | ---- required by this bound in `is_copy` ... LL | is_copy(B { a: 1, b: D }); | ^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/derives/deriving-meta-unknown-trait.rs b/src/test/ui/derives/deriving-meta-unknown-trait.rs index d1af5b458cc0a..6463a7664de93 100644 --- a/src/test/ui/derives/deriving-meta-unknown-trait.rs +++ b/src/test/ui/derives/deriving-meta-unknown-trait.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl #[derive(Eqr)] //~^ ERROR cannot find derive macro `Eqr` in this scope //~| ERROR cannot find derive macro `Eqr` in this scope diff --git a/src/test/ui/derives/deriving-meta-unknown-trait.stderr b/src/test/ui/derives/deriving-meta-unknown-trait.stderr index ead131323246a..a587c342384f1 100644 --- a/src/test/ui/derives/deriving-meta-unknown-trait.stderr +++ b/src/test/ui/derives/deriving-meta-unknown-trait.stderr @@ -1,5 +1,5 @@ error: cannot find derive macro `Eqr` in this scope - --> $DIR/deriving-meta-unknown-trait.rs:5:10 + --> $DIR/deriving-meta-unknown-trait.rs:1:10 | LL | #[derive(Eqr)] | ^^^ help: a derive macro with a similar name exists: `Eq` @@ -10,7 +10,7 @@ LL | pub macro Eq($item:item) { | ------------------------ similarly named derive macro `Eq` defined here error: cannot find derive macro `Eqr` in this scope - --> $DIR/deriving-meta-unknown-trait.rs:5:10 + --> $DIR/deriving-meta-unknown-trait.rs:1:10 | LL | #[derive(Eqr)] | ^^^ help: a derive macro with a similar name exists: `Eq` diff --git a/src/test/ui/deriving/deriving-hash.rs b/src/test/ui/deriving/deriving-hash.rs index 68c68c235ef4a..8b51370bca502 100644 --- a/src/test/ui/deriving/deriving-hash.rs +++ b/src/test/ui/deriving/deriving-hash.rs @@ -24,7 +24,7 @@ struct Person { enum E { A=1, B } fn hash(t: &T) -> u64 { - let mut s = SipHasher::new_with_keys(0, 0); + let mut s = SipHasher::new(); t.hash(&mut s); s.finish() } diff --git a/src/test/ui/did_you_mean/bad-assoc-ty.stderr b/src/test/ui/did_you_mean/bad-assoc-ty.stderr index 875c02bae4ae0..c409ea9c6576d 100644 --- a/src/test/ui/did_you_mean/bad-assoc-ty.stderr +++ b/src/test/ui/did_you_mean/bad-assoc-ty.stderr @@ -145,8 +145,8 @@ LL | fn foo>(x: X) {} | help: use type parameters instead | -LL | fn foo>(x: X) {} - | ^^^ ^ ^ +LL | fn foo, T>(x: X) {} + | ^ ^ ^^^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/bad-assoc-ty.rs:52:34 @@ -167,8 +167,8 @@ LL | fn baz _>(_: F) {} | help: use type parameters instead | -LL | fn baz T>(_: F) {} - | ^^^ ^ +LL | fn baz T, T>(_: F) {} + | ^^^^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/bad-assoc-ty.rs:58:33 diff --git a/src/test/ui/did_you_mean/issue-31424.stderr b/src/test/ui/did_you_mean/issue-31424.stderr index 947ea6c24a345..b9eb8dd236d2d 100644 --- a/src/test/ui/did_you_mean/issue-31424.stderr +++ b/src/test/ui/did_you_mean/issue-31424.stderr @@ -28,6 +28,6 @@ LL | (&mut self).bar(); | cannot borrow as mutable | try removing `&mut` here -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr index f952136a7bfe3..10972697f9fcd 100644 --- a/src/test/ui/did_you_mean/issue-40396.stderr +++ b/src/test/ui/did_you_mean/issue-40396.stderr @@ -2,16 +2,8 @@ error: comparison operators cannot be chained --> $DIR/issue-40396.rs:2:20 | LL | (0..13).collect>(); - | ^^^^^ + | ^ ^ | -help: split the comparison into two... - | -LL | (0..13).collect < Vec && Vec >(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | ((0..13).collect < Vec) >(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `::<...>` instead of `<...>` to specify type arguments | LL | (0..13).collect::>(); @@ -21,7 +13,7 @@ error: comparison operators cannot be chained --> $DIR/issue-40396.rs:4:8 | LL | Vec::new(); - | ^^^^^ + | ^ ^ | help: use `::<...>` instead of `<...>` to specify type arguments | @@ -32,16 +24,8 @@ error: comparison operators cannot be chained --> $DIR/issue-40396.rs:6:20 | LL | (0..13).collect(); - | ^^^^^ - | -help: split the comparison into two... - | -LL | (0..13).collect < Vec && Vec (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons + | ^ ^ | -LL | ((0..13).collect < Vec) (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `::<...>` instead of `<...>` to specify type arguments | LL | (0..13).collect::(); diff --git a/src/test/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr b/src/test/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr index 792b36e00bbfe..9429a0b576532 100644 --- a/src/test/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr +++ b/src/test/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr @@ -22,7 +22,7 @@ error[E0425]: cannot find value `Set` in this scope LL | fn setup() -> Set { Set } | ^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use AffixHeart::Set; | diff --git a/src/test/ui/did_you_mean/recursion_limit.stderr b/src/test/ui/did_you_mean/recursion_limit.stderr index fc14b7fa5b75c..45cb054b80c91 100644 --- a/src/test/ui/did_you_mean/recursion_limit.stderr +++ b/src/test/ui/did_you_mean/recursion_limit.stderr @@ -2,7 +2,7 @@ error[E0275]: overflow evaluating the requirement `J: std::marker::Send` --> $DIR/recursion_limit.rs:34:5 | LL | fn is_send() { } - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send::(); | ^^^^^^^^^^^^ diff --git a/src/test/ui/dst/dst-sized-trait-param.stderr b/src/test/ui/dst/dst-sized-trait-param.stderr index 40dc9978f367b..749d569b9aedc 100644 --- a/src/test/ui/dst/dst-sized-trait-param.stderr +++ b/src/test/ui/dst/dst-sized-trait-param.stderr @@ -1,6 +1,9 @@ error[E0277]: the size for values of type `[isize]` cannot be known at compilation time --> $DIR/dst-sized-trait-param.rs:7:6 | +LL | trait Foo : Sized { fn take(self, x: &T) { } } // Note: T is sized + | - required by this bound in `Foo` +LL | LL | impl Foo<[isize]> for usize { } | ^^^^^^^^^^^^ doesn't have a size known at compile-time | @@ -10,6 +13,9 @@ LL | impl Foo<[isize]> for usize { } error[E0277]: the size for values of type `[usize]` cannot be known at compilation time --> $DIR/dst-sized-trait-param.rs:10:6 | +LL | trait Foo : Sized { fn take(self, x: &T) { } } // Note: T is sized + | ----- required by this bound in `Foo` +... LL | impl Foo for [usize] { } | ^^^^^^^^^^ doesn't have a size known at compile-time | diff --git a/src/test/ui/editions/async-block-2015.rs b/src/test/ui/editions/async-block-2015.rs new file mode 100644 index 0000000000000..985606a6f2545 --- /dev/null +++ b/src/test/ui/editions/async-block-2015.rs @@ -0,0 +1,30 @@ +async fn foo() { +//~^ ERROR `async fn` is not permitted in the 2015 edition +//~| NOTE to use `async fn`, switch to Rust 2018 +//~| HELP set `edition = "2018"` in `Cargo.toml` +//~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide + + let x = async {}; + //~^ ERROR cannot find struct, variant or union type `async` in this scope + //~| NOTE `async` blocks are only allowed in the 2018 edition + let y = async { //~ NOTE `async` blocks are only allowed in the 2018 edition + let x = 42; + //~^ ERROR expected identifier, found keyword `let` + //~| NOTE expected identifier, found keyword + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide + 42 + }; + let z = async { //~ NOTE `async` blocks are only allowed in the 2018 edition + 42 + //~^ ERROR expected identifier, found `42` + //~| NOTE expected identifier + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide + }; + y.await; + z.await; + x +} + +fn main() {} diff --git a/src/test/ui/editions/async-block-2015.stderr b/src/test/ui/editions/async-block-2015.stderr new file mode 100644 index 0000000000000..8e5e5d8bfab9a --- /dev/null +++ b/src/test/ui/editions/async-block-2015.stderr @@ -0,0 +1,41 @@ +error[E0670]: `async fn` is not permitted in the 2015 edition + --> $DIR/async-block-2015.rs:1:1 + | +LL | async fn foo() { + | ^^^^^ to use `async fn`, switch to Rust 2018 + | + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error: expected identifier, found keyword `let` + --> $DIR/async-block-2015.rs:11:9 + | +LL | let y = async { + | ----- `async` blocks are only allowed in the 2018 edition +LL | let x = 42; + | ^^^ expected identifier, found keyword + | + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error: expected identifier, found `42` + --> $DIR/async-block-2015.rs:19:9 + | +LL | let z = async { + | ----- `async` blocks are only allowed in the 2018 edition +LL | 42 + | ^^ expected identifier + | + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0422]: cannot find struct, variant or union type `async` in this scope + --> $DIR/async-block-2015.rs:7:13 + | +LL | let x = async {}; + | ^^^^^ `async` blocks are only allowed in the 2018 edition + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0422, E0670. +For more information about an error, try `rustc --explain E0422`. diff --git a/src/test/ui/editions/edition-extern-crate-allowed.rs b/src/test/ui/editions/edition-extern-crate-allowed.rs index 93fe69e0af217..8d142cea5de00 100644 --- a/src/test/ui/editions/edition-extern-crate-allowed.rs +++ b/src/test/ui/editions/edition-extern-crate-allowed.rs @@ -1,6 +1,6 @@ // aux-build:edition-extern-crate-allowed.rs // edition:2015 -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(rust_2018_idioms)] diff --git a/src/test/ui/editions/edition-extern-crate-allowed.stderr b/src/test/ui/editions/edition-extern-crate-allowed.stderr index dd39847d49afa..dde774c520d71 100644 --- a/src/test/ui/editions/edition-extern-crate-allowed.stderr +++ b/src/test/ui/editions/edition-extern-crate-allowed.stderr @@ -11,3 +11,5 @@ LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +warning: 1 warning emitted + diff --git a/src/test/ui/editions/edition-feature-redundant.rs b/src/test/ui/editions/edition-feature-redundant.rs index 4309a777d33f7..1049a2da8fd1c 100644 --- a/src/test/ui/editions/edition-feature-redundant.rs +++ b/src/test/ui/editions/edition-feature-redundant.rs @@ -1,5 +1,5 @@ // edition:2018 -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(rust_2018_preview)] //~^ WARN the feature `rust_2018_preview` is included in the Rust 2018 edition diff --git a/src/test/ui/editions/edition-feature-redundant.stderr b/src/test/ui/editions/edition-feature-redundant.stderr index 36e90f3a246e6..b11e616d7f2e9 100644 --- a/src/test/ui/editions/edition-feature-redundant.stderr +++ b/src/test/ui/editions/edition-feature-redundant.stderr @@ -4,3 +4,6 @@ warning[E0705]: the feature `rust_2018_preview` is included in the Rust 2018 edi LL | #![feature(rust_2018_preview)] | ^^^^^^^^^^^^^^^^^ +warning: 1 warning emitted + +For more information about this error, try `rustc --explain E0705`. diff --git a/src/test/ui/emit-artifact-notifications.rs b/src/test/ui/emit-artifact-notifications.rs index 6aab237b94d62..984a7fabb6633 100644 --- a/src/test/ui/emit-artifact-notifications.rs +++ b/src/test/ui/emit-artifact-notifications.rs @@ -1,5 +1,5 @@ // compile-flags:--emit=metadata --error-format=json --json artifacts -// build-pass (FIXME(62277): could be check-pass?) +// build-pass // ignore-pass // ^-- needed because `--pass check` does not emit the output needed. diff --git a/src/test/ui/empty/empty-never-array.stderr b/src/test/ui/empty/empty-never-array.stderr index a4ffceea4c97f..64d640c0e9dbc 100644 --- a/src/test/ui/empty/empty-never-array.stderr +++ b/src/test/ui/empty/empty-never-array.stderr @@ -14,6 +14,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/ch18-02-refutability.html + = note: the matched value is of type `Helper` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Helper::U(u) = Helper::T(t, []) { /* */ } diff --git a/src/test/ui/enum-discriminant/actually_not_an_enum-discriminant.rs b/src/test/ui/enum-discriminant/actually_not_an_enum-discriminant.rs new file mode 100644 index 0000000000000..6a566ab3a3d88 --- /dev/null +++ b/src/test/ui/enum-discriminant/actually_not_an_enum-discriminant.rs @@ -0,0 +1,49 @@ +// run-pass +#![feature(core_intrinsics)] + +use std::intrinsics::discriminant_value; + +struct Zst; + +struct Struct { + _a: u32, +} + +union Union { + _a: u32, +} + +fn check(v: u8) { + assert_eq!(v, 0); +} + +pub fn generic() +where + for<'a> T: Fn(&'a isize), +{ + let v: Vec = Vec::new(); + let _: u8 = discriminant_value(&v); +} + +fn main() { + // check that we use `u8` as the discriminant value + // for everything that is not an enum. + check(discriminant_value(&true)); + check(discriminant_value(&'a')); + check(discriminant_value(&7)); + check(discriminant_value(&7.0)); + check(discriminant_value(&Zst)); + check(discriminant_value(&Struct { _a: 7 })); + check(discriminant_value(&Union { _a: 7 })); + check(discriminant_value(&[7, 77])); + check(discriminant_value(&(7 as *const ()))); + check(discriminant_value(&(7 as *mut ()))); + check(discriminant_value(&&7)); + check(discriminant_value(&&mut 7)); + check(discriminant_value(&check)); + let fn_ptr: fn(u8) = check; + check(discriminant_value(&fn_ptr)); + let hrtb: for<'a> fn(&'a str) -> &'a str = |x| x; + check(discriminant_value(&hrtb)); + check(discriminant_value(&(7, 77, 777))); +} diff --git a/src/test/ui/enum-discriminant/discriminant_size.rs b/src/test/ui/enum-discriminant/discriminant_size.rs new file mode 100644 index 0000000000000..4cede8c2a2ded --- /dev/null +++ b/src/test/ui/enum-discriminant/discriminant_size.rs @@ -0,0 +1,53 @@ +// run-pass +#![feature(core_intrinsics, repr128)] + +use std::intrinsics::discriminant_value; + +enum E1 { + A, + B, +} + +#[repr(i8)] +enum E2 { + A = 7, + B = -2, +} + +#[repr(C)] +enum E3 { + A = 42, + B = 100, +} + +#[repr(i128)] +enum E4 { + A = 0x1223_3445_5667_7889, + B = -0x1223_3445_5667_7889, +} + +fn main() { + let mut target: [isize; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E1::A); + assert_eq!(target, [0, 0, 0]); + target[1] = discriminant_value(&E1::B); + assert_eq!(target, [0, 1, 0]); + + let mut target: [i8; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E2::A); + assert_eq!(target, [0, 7, 0]); + target[1] = discriminant_value(&E2::B); + assert_eq!(target, [0, -2, 0]); + + let mut target: [isize; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E3::A); + assert_eq!(target, [0, 42, 0]); + target[1] = discriminant_value(&E3::B); + assert_eq!(target, [0, 100, 0]); + + let mut target: [i128; 3] = [0, 0, 0]; + target[1] = discriminant_value(&E4::A); + assert_eq!(target, [0, 0x1223_3445_5667_7889, 0]); + target[1] = discriminant_value(&E4::B); + assert_eq!(target, [0, -0x1223_3445_5667_7889, 0]); +} diff --git a/src/test/ui/enum-discriminant/discriminant_value.rs b/src/test/ui/enum-discriminant/discriminant_value.rs index 32d2d40241c82..eb60aaf4b2d04 100644 --- a/src/test/ui/enum-discriminant/discriminant_value.rs +++ b/src/test/ui/enum-discriminant/discriminant_value.rs @@ -51,31 +51,31 @@ enum Mixed { } pub fn main() { - assert_eq!(discriminant_value(&CLike1::A), 0); + assert_eq!(discriminant_value(&CLike1::A), 0isize); assert_eq!(discriminant_value(&CLike1::B), 1); assert_eq!(discriminant_value(&CLike1::C), 2); assert_eq!(discriminant_value(&CLike1::D), 3); - assert_eq!(discriminant_value(&CLike2::A), 5); + assert_eq!(discriminant_value(&CLike2::A), 5isize); assert_eq!(discriminant_value(&CLike2::B), 2); assert_eq!(discriminant_value(&CLike2::C), 19); assert_eq!(discriminant_value(&CLike2::D), 20); - assert_eq!(discriminant_value(&CLike3::A), 5); + assert_eq!(discriminant_value(&CLike3::A), 5i8); assert_eq!(discriminant_value(&CLike3::B), 6); - assert_eq!(discriminant_value(&CLike3::C), -1_i8 as u64); + assert_eq!(discriminant_value(&CLike3::C), -1); assert_eq!(discriminant_value(&CLike3::D), 0); - assert_eq!(discriminant_value(&ADT::First(0,0)), 0); + assert_eq!(discriminant_value(&ADT::First(0,0)), 0isize); assert_eq!(discriminant_value(&ADT::Second(5)), 1); - assert_eq!(discriminant_value(&NullablePointer::Nothing), 1); + assert_eq!(discriminant_value(&NullablePointer::Nothing), 1isize); assert_eq!(discriminant_value(&NullablePointer::Something(&CONST)), 0); - assert_eq!(discriminant_value(&10), 0); - assert_eq!(discriminant_value(&"test"), 0); + assert_eq!(discriminant_value(&10), 0u8); + assert_eq!(discriminant_value(&"test"), 0u8); - assert_eq!(3, discriminant_value(&Mixed::Unit)); - assert_eq!(2, discriminant_value(&Mixed::Tuple(5))); - assert_eq!(1, discriminant_value(&Mixed::Struct{a: 7, b: 11})); + assert_eq!(discriminant_value(&Mixed::Unit), 3isize); + assert_eq!(discriminant_value(&Mixed::Tuple(5)), 2); + assert_eq!(discriminant_value(&Mixed::Struct{a: 7, b: 11}), 1); } diff --git a/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.rs b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.rs new file mode 100644 index 0000000000000..4760ca5482a07 --- /dev/null +++ b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.rs @@ -0,0 +1,14 @@ +#![feature(discriminant_kind)] + +use std::marker::DiscriminantKind; + +enum Uninhabited {} + +struct NewType; + +impl DiscriminantKind for NewType { + //~^ ERROR explicit impls for the `DiscriminantKind` trait are not permitted + type Discriminant = Uninhabited; +} + +fn main() {} diff --git a/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.stderr b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.stderr new file mode 100644 index 0000000000000..54360c4f47b3e --- /dev/null +++ b/src/test/ui/enum-discriminant/forbidden-discriminant-kind-impl.stderr @@ -0,0 +1,9 @@ +error[E0322]: explicit impls for the `DiscriminantKind` trait are not permitted + --> $DIR/forbidden-discriminant-kind-impl.rs:9:1 + | +LL | impl DiscriminantKind for NewType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of 'DiscriminantKind' not allowed + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0322`. diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs new file mode 100644 index 0000000000000..0cfb93d466835 --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs @@ -0,0 +1,16 @@ +#![feature(arbitrary_enum_discriminant, core_intrinsics)] + +extern crate core; +use core::intrinsics::discriminant_value; + +#[repr(usize)] +enum MyWeirdOption { + None = 0, + Some(T) = std::mem::size_of::(), + //~^ ERROR constant expression depends on a generic parameter +} + +fn main() { + assert_eq!(discriminant_value(&MyWeirdOption::::None), 0); + assert_eq!(discriminant_value(&MyWeirdOption::Some(0u8)), 1); +} diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.stderr b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.stderr new file mode 100644 index 0000000000000..91d488a07cc6d --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-70453-generics-in-discr-ice-2.rs:9:15 + | +LL | Some(T) = std::mem::size_of::(), + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs new file mode 100644 index 0000000000000..676f1115dde01 --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs @@ -0,0 +1,17 @@ +#![feature(core_intrinsics)] + +extern crate core; +use core::intrinsics::discriminant_value; + +#[repr(usize)] +enum MyWeirdOption { +//~^ ERROR parameter `T` is never used + None = 0, + Some = std::mem::size_of::(), + //~^ ERROR constant expression depends on a generic parameter +} + +fn main() { + assert_eq!(discriminant_value(&MyWeirdOption::::None), 0); + assert_eq!(discriminant_value(&MyWeirdOption::::Some), 1); +} diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr new file mode 100644 index 0000000000000..52e58aa4c6d70 --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr @@ -0,0 +1,19 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-70453-generics-in-discr-ice.rs:10:12 + | +LL | Some = std::mem::size_of::(), + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error[E0392]: parameter `T` is never used + --> $DIR/issue-70453-generics-in-discr-ice.rs:7:20 + | +LL | enum MyWeirdOption { + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0392`. diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs new file mode 100644 index 0000000000000..5a528379b0414 --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(arbitrary_enum_discriminant, core_intrinsics)] + +extern crate core; +use core::intrinsics::discriminant_value; + +#[repr(usize)] +enum MyWeirdOption { + None = 0, + Some(T) = core::mem::size_of::<*mut T>(), +} + +fn main() { + assert_eq!(discriminant_value(&MyWeirdOption::<()>::None), 0); + assert_eq!(discriminant_value(&MyWeirdOption::Some(())), core::mem::size_of::()); +} diff --git a/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs b/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs new file mode 100644 index 0000000000000..e9c6104e3875a --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs @@ -0,0 +1,17 @@ +// run-pass +#![feature(repr128, arbitrary_enum_discriminant)] + +#[derive(PartialEq, Debug)] +#[repr(i128)] +enum Test { + A(Box) = 0, + B(usize) = u64::max_value() as i128 + 1, +} + +fn main() { + assert_ne!(Test::A(Box::new(2)), Test::B(0)); + // This previously caused a segfault. + // + // See https://github.com/rust-lang/rust/issues/70509#issuecomment-620654186 + // for a detailed explanation. +} diff --git a/src/test/ui/enum-discriminant/repr128.rs b/src/test/ui/enum-discriminant/repr128.rs new file mode 100644 index 0000000000000..420b6007c6d85 --- /dev/null +++ b/src/test/ui/enum-discriminant/repr128.rs @@ -0,0 +1,44 @@ +// run-pass +#![feature(repr128, core_intrinsics, discriminant_kind)] + +use std::intrinsics::discriminant_value; +use std::marker::DiscriminantKind; + +#[repr(i128)] +enum Signed { + Zero = 0, + Staircase = 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, + U64Limit = u64::max_value() as i128 + 1, + SmallNegative = -1, + BigNegative = i128::min_value(), + Next, +} + +#[repr(u128)] +enum Unsigned { + Zero = 0, + Staircase = 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f, + U64Limit = u64::max_value() as u128 + 1, + Next, +} + +fn discr(v: T, value: U) +where + ::Discriminant: PartialEq, +{ + assert!(discriminant_value(&v) == value); +} + +fn main() { + discr(Signed::Zero, 0); + discr(Signed::Staircase, 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f); + discr(Signed::U64Limit, u64::max_value() as i128 + 1); + discr(Signed::SmallNegative, -1); + discr(Signed::BigNegative, i128::min_value()); + discr(Signed::Next, i128::min_value() + 1); + + discr(Unsigned::Zero, 0); + discr(Unsigned::Staircase, 0x01_02_03_04_05_06_07_08_09_0a_0b_0c_0d_0e_0f); + discr(Unsigned::U64Limit, u64::max_value() as u128 + 1); + discr(Unsigned::Next, u64::max_value() as u128 + 2); +} diff --git a/src/test/ui/enum/enum-size-variance.stderr b/src/test/ui/enum/enum-size-variance.stderr index cf8321d5f3a44..6012033dc62d4 100644 --- a/src/test/ui/enum/enum-size-variance.stderr +++ b/src/test/ui/enum/enum-size-variance.stderr @@ -10,3 +10,5 @@ note: the lint level is defined here LL | #![warn(variant_size_differences)] | ^^^^^^^^^^^^^^^^^^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/enum/issue-67945-1.rs b/src/test/ui/enum/issue-67945-1.rs new file mode 100644 index 0000000000000..7977bddae7bcb --- /dev/null +++ b/src/test/ui/enum/issue-67945-1.rs @@ -0,0 +1,8 @@ +enum Bug { + Var = { + let x: S = 0; //~ ERROR: mismatched types + 0 + }, +} + +fn main() {} diff --git a/src/test/ui/enum/issue-67945-1.stderr b/src/test/ui/enum/issue-67945-1.stderr new file mode 100644 index 0000000000000..6583fe13d0c63 --- /dev/null +++ b/src/test/ui/enum/issue-67945-1.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/issue-67945-1.rs:3:20 + | +LL | enum Bug { + | - this type parameter +LL | Var = { +LL | let x: S = 0; + | - ^ expected type parameter `S`, found integer + | | + | expected due to this + | + = note: expected type parameter `S` + found type `{integer}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/enum/issue-67945-2.rs b/src/test/ui/enum/issue-67945-2.rs new file mode 100644 index 0000000000000..16bd8530ab38c --- /dev/null +++ b/src/test/ui/enum/issue-67945-2.rs @@ -0,0 +1,9 @@ +#![feature(type_ascription)] + +enum Bug { + Var = 0: S, + //~^ ERROR: mismatched types + //~| ERROR: mismatched types +} + +fn main() {} diff --git a/src/test/ui/enum/issue-67945-2.stderr b/src/test/ui/enum/issue-67945-2.stderr new file mode 100644 index 0000000000000..c40506d59edd9 --- /dev/null +++ b/src/test/ui/enum/issue-67945-2.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/issue-67945-2.rs:4:11 + | +LL | enum Bug { + | - this type parameter +LL | Var = 0: S, + | ^ expected type parameter `S`, found integer + | + = note: expected type parameter `S` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/issue-67945-2.rs:4:11 + | +LL | enum Bug { + | - this type parameter +LL | Var = 0: S, + | ^^^^ expected `isize`, found type parameter `S` + | + = note: expected type `isize` + found type parameter `S` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/eprint-on-tls-drop.rs b/src/test/ui/eprint-on-tls-drop.rs index 9c4800c1a3fa1..f5243077384ac 100644 --- a/src/test/ui/eprint-on-tls-drop.rs +++ b/src/test/ui/eprint-on-tls-drop.rs @@ -1,5 +1,6 @@ // run-pass // ignore-emscripten no processes +// ignore-sgx no processes use std::cell::RefCell; use std::env; diff --git a/src/test/ui/error-codes/E0004-2.rs b/src/test/ui/error-codes/E0004-2.rs index 7f1d064cf3f50..c7612fd50a74e 100644 --- a/src/test/ui/error-codes/E0004-2.rs +++ b/src/test/ui/error-codes/E0004-2.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - fn main() { let x = Some(1); diff --git a/src/test/ui/error-codes/E0004-2.stderr b/src/test/ui/error-codes/E0004-2.stderr index e47a4fa755cac..e48bc74d3579c 100644 --- a/src/test/ui/error-codes/E0004-2.stderr +++ b/src/test/ui/error-codes/E0004-2.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `None` and `Some(_)` not covered - --> $DIR/E0004-2.rs:9:11 + --> $DIR/E0004-2.rs:4:11 | LL | match x { } | ^ patterns `None` and `Some(_)` not covered @@ -13,6 +13,7 @@ LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | ---- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::option::Option` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0004.stderr b/src/test/ui/error-codes/E0004.stderr index 2940ad4bb1e2d..5bf375a64843a 100644 --- a/src/test/ui/error-codes/E0004.stderr +++ b/src/test/ui/error-codes/E0004.stderr @@ -12,6 +12,7 @@ LL | match x { | ^ pattern `HastaLaVistaBaby` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Terminator` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0005.rs b/src/test/ui/error-codes/E0005.rs index 75faad80579c6..f4730697e1843 100644 --- a/src/test/ui/error-codes/E0005.rs +++ b/src/test/ui/error-codes/E0005.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - fn main() { let x = Some(1); let Some(y) = x; //~ ERROR E0005 diff --git a/src/test/ui/error-codes/E0005.stderr b/src/test/ui/error-codes/E0005.stderr index 192b994403191..68aff4638c889 100644 --- a/src/test/ui/error-codes/E0005.stderr +++ b/src/test/ui/error-codes/E0005.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding: `None` not covered - --> $DIR/E0005.rs:8:9 + --> $DIR/E0005.rs:3:9 | LL | let Some(y) = x; | ^^^^^^^ pattern `None` not covered @@ -11,6 +11,7 @@ LL | None, | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::option::Option` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Some(y) = x { /* */ } diff --git a/src/test/ui/error-codes/E0010-teach.stderr b/src/test/ui/error-codes/E0010-teach.stderr index 4c9d140692ad0..c15ab5c655a46 100644 --- a/src/test/ui/error-codes/E0010-teach.stderr +++ b/src/test/ui/error-codes/E0010-teach.stderr @@ -12,6 +12,7 @@ error[E0019]: constant contains unimplemented expression type LL | const CON : Box = box 0; | ^ | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable = note: A function call isn't allowed in the const's initialization expression because the expression's value must be known at compile-time. = note: Remember: you can't use a function call inside a const's initialization expression! However, you can use it anywhere else. diff --git a/src/test/ui/error-codes/E0010.stderr b/src/test/ui/error-codes/E0010.stderr index 48472d8acda38..f49fb9c46326b 100644 --- a/src/test/ui/error-codes/E0010.stderr +++ b/src/test/ui/error-codes/E0010.stderr @@ -9,6 +9,8 @@ error[E0019]: constant contains unimplemented expression type | LL | const CON : Box = box 0; | ^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0017.stderr b/src/test/ui/error-codes/E0017.stderr index 2e687c18ed3ff..f959ad0d00887 100644 --- a/src/test/ui/error-codes/E0017.stderr +++ b/src/test/ui/error-codes/E0017.stderr @@ -12,6 +12,8 @@ error[E0019]: static contains unimplemented expression type | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0658]: references in statics may only refer to immutable values --> $DIR/E0017.rs:6:39 diff --git a/src/test/ui/error-codes/E0034.stderr b/src/test/ui/error-codes/E0034.stderr index 7977e529a11a6..471512ca8f72c 100644 --- a/src/test/ui/error-codes/E0034.stderr +++ b/src/test/ui/error-codes/E0034.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in an impl of the trait `Trait2` for the type `Tes | LL | fn foo() {} | ^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | Trait1::foo() | ^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | Trait2::foo() | ^^^^^^^^^^^ diff --git a/src/test/ui/error-codes/E0152.rs b/src/test/ui/error-codes/E0152.rs index dcaf920883543..94467b9bddeb0 100644 --- a/src/test/ui/error-codes/E0152.rs +++ b/src/test/ui/error-codes/E0152.rs @@ -1,6 +1,6 @@ #![feature(lang_items)] -#[lang = "arc"] +#[lang = "owned_box"] struct Foo; //~ ERROR E0152 fn main() { diff --git a/src/test/ui/error-codes/E0152.stderr b/src/test/ui/error-codes/E0152.stderr index 29981991ee02b..fbaa276ce1093 100644 --- a/src/test/ui/error-codes/E0152.stderr +++ b/src/test/ui/error-codes/E0152.stderr @@ -1,4 +1,4 @@ -error[E0152]: found duplicate lang item `arc` +error[E0152]: found duplicate lang item `owned_box` --> $DIR/E0152.rs:4:1 | LL | struct Foo; diff --git a/src/test/ui/error-codes/E0192.rs b/src/test/ui/error-codes/E0192.rs deleted file mode 100644 index c52977e49b457..0000000000000 --- a/src/test/ui/error-codes/E0192.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(optin_builtin_traits)] - -trait Trait { - type Bar; -} - -struct Foo; - -impl !Trait for Foo { } //~ ERROR E0192 - -fn main() { -} diff --git a/src/test/ui/error-codes/E0192.stderr b/src/test/ui/error-codes/E0192.stderr deleted file mode 100644 index da706dea167f6..0000000000000 --- a/src/test/ui/error-codes/E0192.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0192]: invalid negative impl - --> $DIR/E0192.rs:9:6 - | -LL | impl !Trait for Foo { } - | ^^^^^^ - | - = note: negative impls are only allowed for auto traits, like `Send` and `Sync` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0192`. diff --git a/src/test/ui/error-codes/E0198.rs b/src/test/ui/error-codes/E0198.rs index 00ab0c3562378..041bbe8fdcf69 100644 --- a/src/test/ui/error-codes/E0198.rs +++ b/src/test/ui/error-codes/E0198.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] struct Foo; diff --git a/src/test/ui/error-codes/E0199.rs b/src/test/ui/error-codes/E0199.rs index c95afa3f97d4a..2421bf0a55f65 100644 --- a/src/test/ui/error-codes/E0199.rs +++ b/src/test/ui/error-codes/E0199.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] struct Foo; diff --git a/src/test/ui/error-codes/E0271.stderr b/src/test/ui/error-codes/E0271.stderr index b2dcdf8ee2ea2..580b5aef07e6d 100644 --- a/src/test/ui/error-codes/E0271.stderr +++ b/src/test/ui/error-codes/E0271.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `::AssociatedType == u32` --> $DIR/E0271.rs:10:5 | LL | fn foo(t: T) where T: Trait { - | --- ------------------ required by this bound in `foo` + | ------------------ required by this bound in `foo` ... LL | foo(3_i8); | ^^^ expected `u32`, found `&str` diff --git a/src/test/ui/error-codes/E0275.stderr b/src/test/ui/error-codes/E0275.stderr index c551a00096e23..a9fd0564ff548 100644 --- a/src/test/ui/error-codes/E0275.stderr +++ b/src/test/ui/error-codes/E0275.stderr @@ -2,7 +2,7 @@ error[E0275]: overflow evaluating the requirement `Bar $DIR/E0275.rs:5:33 | LL | trait Foo {} - | --------- required by `Foo` + | --------- required by this bound in `Foo` ... LL | impl Foo for T where Bar: Foo {} | ^^^ diff --git a/src/test/ui/error-codes/E0277-2.stderr b/src/test/ui/error-codes/E0277-2.stderr index 407e51e4f5f9c..f5ba46ca01e1f 100644 --- a/src/test/ui/error-codes/E0277-2.stderr +++ b/src/test/ui/error-codes/E0277-2.stderr @@ -2,7 +2,7 @@ error[E0277]: `*const u8` cannot be sent between threads safely --> $DIR/E0277-2.rs:16:5 | LL | fn is_send() { } - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send::(); | ^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely diff --git a/src/test/ui/error-codes/E0277.stderr b/src/test/ui/error-codes/E0277.stderr index a069d048c8862..a9ea85d14cff5 100644 --- a/src/test/ui/error-codes/E0277.stderr +++ b/src/test/ui/error-codes/E0277.stderr @@ -14,7 +14,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied --> $DIR/E0277.rs:17:15 | LL | fn some_func(foo: T) { - | --------- --- required by this bound in `some_func` + | --- required by this bound in `some_func` ... LL | some_func(5i32); | ^^^^ the trait `Foo` is not implemented for `i32` diff --git a/src/test/ui/error-codes/E0283.stderr b/src/test/ui/error-codes/E0283.stderr index ae5b7c3ae8f67..e95583c91a72f 100644 --- a/src/test/ui/error-codes/E0283.stderr +++ b/src/test/ui/error-codes/E0283.stderr @@ -7,7 +7,7 @@ LL | fn create() -> u32; LL | let cont: u32 = Generator::create(); | ^^^^^^^^^^^^^^^^^ cannot infer type | - = note: cannot resolve `_: Generator` + = note: cannot satisfy `_: Generator` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0297.rs b/src/test/ui/error-codes/E0297.rs index b26ede9c8e219..27c7960d97731 100644 --- a/src/test/ui/error-codes/E0297.rs +++ b/src/test/ui/error-codes/E0297.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - fn main() { let xs : Vec> = vec![Some(1), None]; diff --git a/src/test/ui/error-codes/E0297.stderr b/src/test/ui/error-codes/E0297.stderr index 4a75e9d1771f3..b2d181b838fda 100644 --- a/src/test/ui/error-codes/E0297.stderr +++ b/src/test/ui/error-codes/E0297.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in `for` loop binding: `None` not covered - --> $DIR/E0297.rs:9:9 + --> $DIR/E0297.rs:4:9 | LL | for Some(x) in xs {} | ^^^^^^^ pattern `None` not covered @@ -8,6 +8,8 @@ LL | for Some(x) in xs {} | LL | None, | ---- not covered + | + = note: the matched value is of type `std::option::Option` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0388.stderr b/src/test/ui/error-codes/E0388.stderr index 52822ebdd9e67..8bdfbac36816b 100644 --- a/src/test/ui/error-codes/E0388.stderr +++ b/src/test/ui/error-codes/E0388.stderr @@ -12,6 +12,8 @@ error[E0019]: static contains unimplemented expression type | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0658]: references in statics may only refer to immutable values --> $DIR/E0388.rs:5:39 diff --git a/src/test/ui/error-codes/E0423.stderr b/src/test/ui/error-codes/E0423.stderr index a985e963e5726..d4860394259b7 100644 --- a/src/test/ui/error-codes/E0423.stderr +++ b/src/test/ui/error-codes/E0423.stderr @@ -45,9 +45,12 @@ error[E0423]: expected value, found struct `T` --> $DIR/E0423.rs:14:8 | LL | if T {} == T {} { println!("Ok"); } - | ^--- - | | - | help: surround the struct literal with parenthesis: `(T {})` + | ^ + | +help: surround the struct literal with parentheses + | +LL | if (T {}) == T {} { println!("Ok"); } + | ^ ^ error: aborting due to 5 previous errors diff --git a/src/test/ui/error-codes/E0429.stderr b/src/test/ui/error-codes/E0429.stderr index b5f76a1fcd848..c598803fa6cb8 100644 --- a/src/test/ui/error-codes/E0429.stderr +++ b/src/test/ui/error-codes/E0429.stderr @@ -1,8 +1,17 @@ error[E0429]: `self` imports are only allowed within a { } list - --> $DIR/E0429.rs:1:5 + --> $DIR/E0429.rs:1:13 | LL | use std::fmt::self; - | ^^^^^^^^^^^^^^ + | ^^^^^^ + | +help: consider importing the module directly + | +LL | use std::fmt; + | -- +help: alternatively, use the multi-path `use` syntax to import `self` + | +LL | use std::fmt::{self}; + | ^ ^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0657.stderr b/src/test/ui/error-codes/E0657.stderr index b24b413600c6c..df76b45a5891f 100644 --- a/src/test/ui/error-codes/E0657.stderr +++ b/src/test/ui/error-codes/E0657.stderr @@ -12,3 +12,4 @@ LL | -> Box Id>> error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0657`. diff --git a/src/test/ui/error-codes/E0660.rs b/src/test/ui/error-codes/E0660.rs index 6280d39061035..842ae59ee8deb 100644 --- a/src/test/ui/error-codes/E0660.rs +++ b/src/test/ui/error-codes/E0660.rs @@ -1,9 +1,9 @@ -#![feature(asm)] +#![feature(llvm_asm)] fn main() { let a; - asm!("nop" "nop"); + llvm_asm!("nop" "nop"); //~^ ERROR E0660 - asm!("nop" "nop" : "=r"(a)); + llvm_asm!("nop" "nop" : "=r"(a)); //~^ ERROR E0660 } diff --git a/src/test/ui/error-codes/E0660.stderr b/src/test/ui/error-codes/E0660.stderr index d355531ef5d91..69288ea6fcdb3 100644 --- a/src/test/ui/error-codes/E0660.stderr +++ b/src/test/ui/error-codes/E0660.stderr @@ -1,14 +1,14 @@ error[E0660]: malformed inline assembly --> $DIR/E0660.rs:5:5 | -LL | asm!("nop" "nop"); - | ^^^^^^^^^^^^^^^^^^ +LL | llvm_asm!("nop" "nop"); + | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0660]: malformed inline assembly --> $DIR/E0660.rs:7:5 | -LL | asm!("nop" "nop" : "=r"(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | llvm_asm!("nop" "nop" : "=r"(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0661.rs b/src/test/ui/error-codes/E0661.rs index 5ac0c415ae1b2..1099edd848b28 100644 --- a/src/test/ui/error-codes/E0661.rs +++ b/src/test/ui/error-codes/E0661.rs @@ -1,9 +1,9 @@ // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] fn main() { let a; //~ ERROR type annotations needed - asm!("nop" : "r"(a)); + llvm_asm!("nop" : "r"(a)); //~^ ERROR E0661 } diff --git a/src/test/ui/error-codes/E0661.stderr b/src/test/ui/error-codes/E0661.stderr index 6e849649aca2a..fe3887e72604d 100644 --- a/src/test/ui/error-codes/E0661.stderr +++ b/src/test/ui/error-codes/E0661.stderr @@ -1,8 +1,8 @@ error[E0661]: output operand constraint lacks '=' or '+' - --> $DIR/E0661.rs:7:18 + --> $DIR/E0661.rs:7:23 | -LL | asm!("nop" : "r"(a)); - | ^^^ +LL | llvm_asm!("nop" : "r"(a)); + | ^^^ error[E0282]: type annotations needed --> $DIR/E0661.rs:6:9 diff --git a/src/test/ui/error-codes/E0662.rs b/src/test/ui/error-codes/E0662.rs index 343ed27f83f7e..095005999039f 100644 --- a/src/test/ui/error-codes/E0662.rs +++ b/src/test/ui/error-codes/E0662.rs @@ -1,10 +1,10 @@ // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] fn main() { - asm!("xor %eax, %eax" - : - : "=test"("a") //~ ERROR E0662 - ); + llvm_asm!("xor %eax, %eax" + : + : "=test"("a") //~ ERROR E0662 + ); } diff --git a/src/test/ui/error-codes/E0662.stderr b/src/test/ui/error-codes/E0662.stderr index 7480f03c3d4c3..ebc5f628f2699 100644 --- a/src/test/ui/error-codes/E0662.stderr +++ b/src/test/ui/error-codes/E0662.stderr @@ -1,8 +1,8 @@ error[E0662]: input operand constraint contains '=' - --> $DIR/E0662.rs:8:12 + --> $DIR/E0662.rs:8:17 | -LL | : "=test"("a") - | ^^^^^^^ +LL | : "=test"("a") + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0663.rs b/src/test/ui/error-codes/E0663.rs index cfbb4b37758ce..0783d705a5bfe 100644 --- a/src/test/ui/error-codes/E0663.rs +++ b/src/test/ui/error-codes/E0663.rs @@ -1,10 +1,10 @@ // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] fn main() { - asm!("xor %eax, %eax" - : - : "+test"("a") //~ ERROR E0663 - ); + llvm_asm!("xor %eax, %eax" + : + : "+test"("a") //~ ERROR E0663 + ); } diff --git a/src/test/ui/error-codes/E0663.stderr b/src/test/ui/error-codes/E0663.stderr index 2b7598d1577a6..4e421aa007352 100644 --- a/src/test/ui/error-codes/E0663.stderr +++ b/src/test/ui/error-codes/E0663.stderr @@ -1,8 +1,8 @@ error[E0663]: input operand constraint contains '+' - --> $DIR/E0663.rs:8:12 + --> $DIR/E0663.rs:8:17 | -LL | : "+test"("a") - | ^^^^^^^ +LL | : "+test"("a") + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0664.rs b/src/test/ui/error-codes/E0664.rs index fe70c9f96e06d..f8ca5c9c8c6de 100644 --- a/src/test/ui/error-codes/E0664.rs +++ b/src/test/ui/error-codes/E0664.rs @@ -1,11 +1,11 @@ // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] fn main() { - asm!("mov $$0x200, %eax" - : - : - : "{eax}" //~ ERROR E0664 - ); + llvm_asm!("mov $$0x200, %eax" + : + : + : "{eax}" //~ ERROR E0664 + ); } diff --git a/src/test/ui/error-codes/E0664.stderr b/src/test/ui/error-codes/E0664.stderr index 224fc63696a99..d0ed0f01ce79f 100644 --- a/src/test/ui/error-codes/E0664.stderr +++ b/src/test/ui/error-codes/E0664.stderr @@ -1,8 +1,8 @@ error[E0664]: clobber should not be surrounded by braces - --> $DIR/E0664.rs:9:12 + --> $DIR/E0664.rs:9:17 | -LL | : "{eax}" - | ^^^^^^^ +LL | : "{eax}" + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0705.rs b/src/test/ui/error-codes/E0705.rs index 4e32ef355606c..05abcb629b1c8 100644 --- a/src/test/ui/error-codes/E0705.rs +++ b/src/test/ui/error-codes/E0705.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // This is a stub feature that doesn't control anything, so to make tidy happy, // gate-test-test_2018_feature diff --git a/src/test/ui/error-codes/E0705.stderr b/src/test/ui/error-codes/E0705.stderr index 1cb83f2e381dc..6fa843158bbf1 100644 --- a/src/test/ui/error-codes/E0705.stderr +++ b/src/test/ui/error-codes/E0705.stderr @@ -4,3 +4,6 @@ warning[E0705]: the feature `test_2018_feature` is included in the Rust 2018 edi LL | #![feature(test_2018_feature)] | ^^^^^^^^^^^^^^^^^ +warning: 1 warning emitted + +For more information about this error, try `rustc --explain E0705`. diff --git a/src/test/ui/error-codes/E0718.rs b/src/test/ui/error-codes/E0718.rs index 82ab2d4af1ba8..909cae0ba25a2 100644 --- a/src/test/ui/error-codes/E0718.rs +++ b/src/test/ui/error-codes/E0718.rs @@ -1,7 +1,7 @@ #![feature(lang_items)] -// Arc is expected to be a struct, so this will error. -#[lang = "arc"] //~ ERROR language item must be applied to a struct +// Box is expected to be a struct, so this will error. +#[lang = "owned_box"] //~ ERROR language item must be applied to a struct static X: u32 = 42; fn main() {} diff --git a/src/test/ui/error-codes/E0718.stderr b/src/test/ui/error-codes/E0718.stderr index 412c856ce065a..30378dd167457 100644 --- a/src/test/ui/error-codes/E0718.stderr +++ b/src/test/ui/error-codes/E0718.stderr @@ -1,8 +1,8 @@ -error[E0718]: `arc` language item must be applied to a struct +error[E0718]: `owned_box` language item must be applied to a struct --> $DIR/E0718.rs:4:1 | -LL | #[lang = "arc"] - | ^^^^^^^^^^^^^^^ attribute should be applied to a struct, not a static item +LL | #[lang = "owned_box"] + | ^^^^^^^^^^^^^^^^^^^^^ attribute should be applied to a struct, not a static item error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0730.rs b/src/test/ui/error-codes/E0730.rs index e5048d6e6e320..30745814b4a74 100644 --- a/src/test/ui/error-codes/E0730.rs +++ b/src/test/ui/error-codes/E0730.rs @@ -1,9 +1,9 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn is_123(x: [u32; N]) -> bool { match x { - [1, 2, 3] => true, //~ ERROR cannot pattern-match on an array without a fixed length + [1, 2, ..] => true, //~ ERROR cannot pattern-match on an array without a fixed length _ => false } } diff --git a/src/test/ui/error-codes/E0730.stderr b/src/test/ui/error-codes/E0730.stderr index 9309ee99064c9..f915f6edef52b 100644 --- a/src/test/ui/error-codes/E0730.stderr +++ b/src/test/ui/error-codes/E0730.stderr @@ -1,17 +1,18 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/E0730.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0730]: cannot pattern-match on an array without a fixed length --> $DIR/E0730.rs:6:9 | -LL | [1, 2, 3] => true, - | ^^^^^^^^^ +LL | [1, 2, ..] => true, + | ^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0730`. diff --git a/src/test/ui/error-codes/E0746.stderr b/src/test/ui/error-codes/E0746.stderr index e7a8fd304cabe..3757ed6d0926e 100644 --- a/src/test/ui/error-codes/E0746.stderr +++ b/src/test/ui/error-codes/E0746.stderr @@ -5,7 +5,7 @@ LL | fn foo() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: return `impl Trait` instead, as all return paths are of type `Struct`, which implements `Trait` +help: use `impl Trait` as the return type, as all return paths are of type `Struct`, which implements `Trait` | LL | fn foo() -> impl Trait { Struct } | ^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | fn bar() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: return `impl Trait` instead, as all return paths are of type `{integer}`, which implements `Trait` +help: use `impl Trait` as the return type, as all return paths are of type `{integer}`, which implements `Trait` | LL | fn bar() -> impl Trait { | ^^^^^^^^^^ diff --git a/src/test/ui/error-should-say-copy-not-pod.stderr b/src/test/ui/error-should-say-copy-not-pod.stderr index d0148f418e33b..96ffa6f3e06ba 100644 --- a/src/test/ui/error-should-say-copy-not-pod.stderr +++ b/src/test/ui/error-should-say-copy-not-pod.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not sa --> $DIR/error-should-say-copy-not-pod.rs:6:17 | LL | fn check_bound(_: T) {} - | ----------- ---- required by this bound in `check_bound` + | ---- required by this bound in `check_bound` ... LL | check_bound("nocopy".to_string()); | ^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::string::String` diff --git a/src/test/ui/explain.rs b/src/test/ui/explain.rs index 28973d6756482..5364d92e0c46b 100644 --- a/src/test/ui/explain.rs +++ b/src/test/ui/explain.rs @@ -1,2 +1,2 @@ // compile-flags: --explain E0591 -// build-pass (FIXME(62277): could be check-pass?) +// check-pass diff --git a/src/test/ui/expr-block-generic-unique1.rs b/src/test/ui/expr-block-generic-unique1.rs index c14191f2ffcb5..d081cb2be7ee3 100644 --- a/src/test/ui/expr-block-generic-unique1.rs +++ b/src/test/ui/expr-block-generic-unique1.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![feature(box_syntax)] fn test_generic(expected: Box, eq: F) where T: Clone, F: FnOnce(Box, Box) -> bool { diff --git a/src/test/ui/expr-block-generic-unique2.rs b/src/test/ui/expr-block-generic-unique2.rs index 90ebc02931ad1..9362eb86fc309 100644 --- a/src/test/ui/expr-block-generic-unique2.rs +++ b/src/test/ui/expr-block-generic-unique2.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![feature(box_syntax)] fn test_generic(expected: T, eq: F) where T: Clone, F: FnOnce(T, T) -> bool { diff --git a/src/test/ui/expr-block-generic.rs b/src/test/ui/expr-block-generic.rs index ec93f59722d0c..29c7c42219c73 100644 --- a/src/test/ui/expr-block-generic.rs +++ b/src/test/ui/expr-block-generic.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn test_generic(expected: T, eq: F) where F: FnOnce(T, T) -> bool { let actual: T = { expected.clone() }; diff --git a/src/test/ui/expr-block-unique.rs b/src/test/ui/expr-block-unique.rs index fe1a7d9f1fb31..eff3fd3a15152 100644 --- a/src/test/ui/expr-block-unique.rs +++ b/src/test/ui/expr-block-unique.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![feature(box_syntax)] pub fn main() { let x: Box<_> = { box 100 }; assert_eq!(*x, 100); } diff --git a/src/test/ui/expr-block.rs b/src/test/ui/expr-block.rs index 549ccf9774f43..ff87595c934e9 100644 --- a/src/test/ui/expr-block.rs +++ b/src/test/ui/expr-block.rs @@ -1,10 +1,7 @@ // run-pass - +#![allow(unused_braces)] #![allow(dead_code)] - - - // Tests for standalone blocks as expressions fn test_basic() { let rs: bool = { true }; assert!((rs)); } diff --git a/src/test/ui/expr-fn.rs b/src/test/ui/expr-fn.rs index af809f563fc04..253cbfd5d38fa 100644 --- a/src/test/ui/expr-fn.rs +++ b/src/test/ui/expr-fn.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn test_int() { fn f() -> isize { 10 } diff --git a/src/test/ui/extern/extern-types-not-sync-send.stderr b/src/test/ui/extern/extern-types-not-sync-send.stderr index c395f3875ea0e..a1138c3234451 100644 --- a/src/test/ui/extern/extern-types-not-sync-send.stderr +++ b/src/test/ui/extern/extern-types-not-sync-send.stderr @@ -2,7 +2,7 @@ error[E0277]: `A` cannot be shared between threads safely --> $DIR/extern-types-not-sync-send.rs:13:19 | LL | fn assert_sync() { } - | ----------- ---- required by this bound in `assert_sync` + | ---- required by this bound in `assert_sync` ... LL | assert_sync::(); | ^ `A` cannot be shared between threads safely @@ -13,7 +13,7 @@ error[E0277]: `A` cannot be sent between threads safely --> $DIR/extern-types-not-sync-send.rs:16:19 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::(); | ^ `A` cannot be sent between threads safely diff --git a/src/test/ui/extern/extern-types-unsized.stderr b/src/test/ui/extern/extern-types-unsized.stderr index 871757ec7b0c6..9ed52511fa399 100644 --- a/src/test/ui/extern/extern-types-unsized.stderr +++ b/src/test/ui/extern/extern-types-unsized.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `A` cannot be known at compilation tim --> $DIR/extern-types-unsized.rs:22:20 | LL | fn assert_sized() { } - | ------------ - required by this bound in `assert_sized` + | - required by this bound in `assert_sized` ... LL | assert_sized::(); | ^ doesn't have a size known at compile-time @@ -18,7 +18,7 @@ error[E0277]: the size for values of type `A` cannot be known at compilation tim --> $DIR/extern-types-unsized.rs:25:5 | LL | fn assert_sized() { } - | ------------ - required by this bound in `assert_sized` + | - required by this bound in `assert_sized` ... LL | assert_sized::(); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -31,7 +31,7 @@ error[E0277]: the size for values of type `A` cannot be known at compilation tim --> $DIR/extern-types-unsized.rs:28:5 | LL | fn assert_sized() { } - | ------------ - required by this bound in `assert_sized` + | - required by this bound in `assert_sized` ... LL | assert_sized::>(); | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -44,7 +44,7 @@ error[E0277]: the size for values of type `A` cannot be known at compilation tim --> $DIR/extern-types-unsized.rs:31:5 | LL | fn assert_sized() { } - | ------------ - required by this bound in `assert_sized` + | - required by this bound in `assert_sized` ... LL | assert_sized::>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/extern/extern-wrong-value-type.stderr b/src/test/ui/extern/extern-wrong-value-type.stderr index 9a6af8119a8c5..64f01b47792c7 100644 --- a/src/test/ui/extern/extern-wrong-value-type.stderr +++ b/src/test/ui/extern/extern-wrong-value-type.stderr @@ -2,7 +2,7 @@ error[E0277]: expected a `std::ops::Fn<()>` closure, found `extern "C" fn() {f}` --> $DIR/extern-wrong-value-type.rs:9:11 | LL | fn is_fn(_: F) where F: Fn() {} - | ----- ---- required by this bound in `is_fn` + | ---- required by this bound in `is_fn` ... LL | is_fn(f); | ^ expected an `Fn<()>` closure, found `extern "C" fn() {f}` diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr index 96c87a675a9fa..02bed6723bf72 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr @@ -1234,3 +1234,5 @@ warning: unused attribute LL | #![proc_macro_derive()] | ^^^^^^^^^^^^^^^^^^^^^^^ +warning: 203 warnings emitted + diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-macro_escape.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-macro_escape.stderr index 402dc4e540925..0eaec5202c418 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-macro_escape.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-macro_escape.stderr @@ -6,3 +6,5 @@ LL | #![macro_escape] | = help: try an outer attribute: `#[macro_use]` +warning: 1 warning emitted + diff --git a/src/test/ui/feature-gates/feature-gate-asm.rs b/src/test/ui/feature-gates/feature-gate-asm.rs index 1fce279c9ef3d..4eb72031d51b1 100644 --- a/src/test/ui/feature-gates/feature-gate-asm.rs +++ b/src/test/ui/feature-gates/feature-gate-asm.rs @@ -2,6 +2,9 @@ fn main() { unsafe { - asm!(""); //~ ERROR inline assembly is not stable enough + asm!(""); + //~^ ERROR inline assembly is not stable enough + llvm_asm!(""); + //~^ ERROR LLVM-style inline assembly will never be stabilized } } diff --git a/src/test/ui/feature-gates/feature-gate-asm.stderr b/src/test/ui/feature-gates/feature-gate-asm.stderr index 265d38f83f540..a71643e0d33a3 100644 --- a/src/test/ui/feature-gates/feature-gate-asm.stderr +++ b/src/test/ui/feature-gates/feature-gate-asm.stderr @@ -4,9 +4,18 @@ error[E0658]: use of unstable library feature 'asm': inline assembly is not stab LL | asm!(""); | ^^^ | - = note: see issue #29722 for more information + = note: see issue #72016 for more information = help: add `#![feature(asm)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: use of unstable library feature 'llvm_asm': LLVM-style inline assembly will never be stabilized, prefer using asm! instead + --> $DIR/feature-gate-asm.rs:7:9 + | +LL | llvm_asm!(""); + | ^^^^^^^^ + | + = note: see issue #70173 for more information + = help: add `#![feature(llvm_asm)]` to the crate attributes to enable + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-asm2.rs b/src/test/ui/feature-gates/feature-gate-asm2.rs index 4f56aa7234464..8bd7226aca730 100644 --- a/src/test/ui/feature-gates/feature-gate-asm2.rs +++ b/src/test/ui/feature-gates/feature-gate-asm2.rs @@ -2,6 +2,9 @@ fn main() { unsafe { - println!("{:?}", asm!("")); //~ ERROR inline assembly is not stable + println!("{:?}", asm!("")); + //~^ ERROR inline assembly is not stable enough + println!("{:?}", llvm_asm!("")); + //~^ ERROR LLVM-style inline assembly will never be stabilized } } diff --git a/src/test/ui/feature-gates/feature-gate-asm2.stderr b/src/test/ui/feature-gates/feature-gate-asm2.stderr index 7ea7bdac44183..a8022cb72e0e3 100644 --- a/src/test/ui/feature-gates/feature-gate-asm2.stderr +++ b/src/test/ui/feature-gates/feature-gate-asm2.stderr @@ -4,9 +4,18 @@ error[E0658]: use of unstable library feature 'asm': inline assembly is not stab LL | println!("{:?}", asm!("")); | ^^^ | - = note: see issue #29722 for more information + = note: see issue #72016 for more information = help: add `#![feature(asm)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: use of unstable library feature 'llvm_asm': LLVM-style inline assembly will never be stabilized, prefer using asm! instead + --> $DIR/feature-gate-asm2.rs:7:26 + | +LL | println!("{:?}", llvm_asm!("")); + | ^^^^^^^^ + | + = note: see issue #70173 for more information + = help: add `#![feature(llvm_asm)]` to the crate attributes to enable + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-cfg-version.rs b/src/test/ui/feature-gates/feature-gate-cfg-version.rs new file mode 100644 index 0000000000000..c29ef99945e71 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg-version.rs @@ -0,0 +1,41 @@ +#[cfg(version("1.44"))] +//~^ ERROR `cfg(version)` is experimental and subject to change +fn foo() -> bool { true } +#[cfg(not(version("1.44")))] +//~^ ERROR `cfg(version)` is experimental and subject to change +fn foo() -> bool { false } + +#[cfg(version("1.43", "1.44", "1.45"))] //~ ERROR: expected single version literal +//~^ ERROR `cfg(version)` is experimental and subject to change +fn bar() -> bool { false } +#[cfg(version(false))] //~ ERROR: expected a version literal +//~^ ERROR `cfg(version)` is experimental and subject to change +fn bar() -> bool { false } +#[cfg(version("foo"))] //~ ERROR: invalid version literal +//~^ ERROR `cfg(version)` is experimental and subject to change +fn bar() -> bool { false } +#[cfg(version("999"))] +//~^ ERROR `cfg(version)` is experimental and subject to change +fn bar() -> bool { false } +#[cfg(version("-1"))] //~ ERROR: invalid version literal +//~^ ERROR `cfg(version)` is experimental and subject to change +fn bar() -> bool { false } +#[cfg(version("65536"))] //~ ERROR: invalid version literal +//~^ ERROR `cfg(version)` is experimental and subject to change +fn bar() -> bool { false } +#[cfg(version("0"))] +//~^ ERROR `cfg(version)` is experimental and subject to change +fn bar() -> bool { true } + +#[cfg(version("1.65536.2"))] +//~^ ERROR `cfg(version)` is experimental and subject to change +fn version_check_bug() {} + +fn main() { + // This should fail but due to a bug in version_check `1.65536.2` is interpreted as `1.2`. + // See https://github.com/SergioBenitez/version_check/issues/11 + version_check_bug(); + assert!(foo()); + assert!(bar()); + assert!(cfg!(version("1.42"))); //~ ERROR `cfg(version)` is experimental and subject to change +} diff --git a/src/test/ui/feature-gates/feature-gate-cfg-version.stderr b/src/test/ui/feature-gates/feature-gate-cfg-version.stderr new file mode 100644 index 0000000000000..bdf160b5a0270 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg-version.stderr @@ -0,0 +1,132 @@ +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:1:7 + | +LL | #[cfg(version("1.44"))] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:4:11 + | +LL | #[cfg(not(version("1.44")))] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:8:7 + | +LL | #[cfg(version("1.43", "1.44", "1.45"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error: expected single version literal + --> $DIR/feature-gate-cfg-version.rs:8:7 + | +LL | #[cfg(version("1.43", "1.44", "1.45"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:11:7 + | +LL | #[cfg(version(false))] + | ^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error: expected a version literal + --> $DIR/feature-gate-cfg-version.rs:11:15 + | +LL | #[cfg(version(false))] + | ^^^^^ + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:14:7 + | +LL | #[cfg(version("foo"))] + | ^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error: invalid version literal + --> $DIR/feature-gate-cfg-version.rs:14:15 + | +LL | #[cfg(version("foo"))] + | ^^^^^ + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:17:7 + | +LL | #[cfg(version("999"))] + | ^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:20:7 + | +LL | #[cfg(version("-1"))] + | ^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error: invalid version literal + --> $DIR/feature-gate-cfg-version.rs:20:15 + | +LL | #[cfg(version("-1"))] + | ^^^^ + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:23:7 + | +LL | #[cfg(version("65536"))] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error: invalid version literal + --> $DIR/feature-gate-cfg-version.rs:23:15 + | +LL | #[cfg(version("65536"))] + | ^^^^^^^ + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:26:7 + | +LL | #[cfg(version("0"))] + | ^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:30:7 + | +LL | #[cfg(version("1.65536.2"))] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error[E0658]: `cfg(version)` is experimental and subject to change + --> $DIR/feature-gate-cfg-version.rs:40:18 + | +LL | assert!(cfg!(version("1.42"))); + | ^^^^^^^^^^^^^^^ + | + = note: see issue #64796 for more information + = help: add `#![feature(cfg_version)]` to the crate attributes to enable + +error: aborting due to 16 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr index 632afb24aa55d..6772178068289 100644 --- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr @@ -7,7 +7,7 @@ LL | let arr: [Option; 2] = [None::; 2]; = help: the following implementations were found: as std::marker::Copy> = note: the `Copy` trait is required because the repeated element will be copied - = note: this array initializer can be evaluated at compile-time, see issue #48147 for more information + = note: this array initializer can be evaluated at compile-time, see issue #49147 for more information = help: add `#![feature(const_in_array_repeat_expressions)]` to the crate attributes to enable error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied diff --git a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.rs b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.rs index d2e52299c0d86..f0cc9ea70550e 100644 --- a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.rs +++ b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #![feature(never_type)] fn foo() -> Result { diff --git a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr index c2dd90b91e700..823dad2c95e0e 100644 --- a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr +++ b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered - --> $DIR/feature-gate-exhaustive-patterns.rs:13:9 + --> $DIR/feature-gate-exhaustive-patterns.rs:8:9 | LL | let Ok(_x) = foo(); | ^^^^^^ pattern `Err(_)` not covered @@ -11,6 +11,7 @@ LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::result::Result` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Ok(_x) = foo() { /* */ } diff --git a/src/test/ui/feature-gates/feature-gate-ffi_const.rs b/src/test/ui/feature-gates/feature-gate-ffi_const.rs new file mode 100644 index 0000000000000..27323b1b60280 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-ffi_const.rs @@ -0,0 +1,6 @@ +#![crate_type = "lib"] + +extern { + #[ffi_const] //~ ERROR the `#[ffi_const]` attribute is an experimental feature + pub fn foo(); +} diff --git a/src/test/ui/feature-gates/feature-gate-ffi_const.stderr b/src/test/ui/feature-gates/feature-gate-ffi_const.stderr new file mode 100644 index 0000000000000..bed6a2ce48825 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-ffi_const.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[ffi_const]` attribute is an experimental feature + --> $DIR/feature-gate-ffi_const.rs:4:5 + | +LL | #[ffi_const] + | ^^^^^^^^^^^^ + | + = note: see issue #58328 for more information + = help: add `#![feature(ffi_const)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-ffi_pure.rs b/src/test/ui/feature-gates/feature-gate-ffi_pure.rs new file mode 100644 index 0000000000000..e24a686853c88 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-ffi_pure.rs @@ -0,0 +1,6 @@ +#![crate_type = "lib"] + +extern { + #[ffi_pure] //~ ERROR the `#[ffi_pure]` attribute is an experimental feature + pub fn foo(); +} diff --git a/src/test/ui/feature-gates/feature-gate-ffi_pure.stderr b/src/test/ui/feature-gates/feature-gate-ffi_pure.stderr new file mode 100644 index 0000000000000..2b0308fd661c2 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-ffi_pure.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[ffi_pure]` attribute is an experimental feature + --> $DIR/feature-gate-ffi_pure.rs:4:5 + | +LL | #[ffi_pure] + | ^^^^^^^^^^^ + | + = note: see issue #58329 for more information + = help: add `#![feature(ffi_pure)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-link_cfg.stderr b/src/test/ui/feature-gates/feature-gate-link_cfg.stderr index 10b151ffa7565..41a7dfc3f379f 100644 --- a/src/test/ui/feature-gates/feature-gate-link_cfg.stderr +++ b/src/test/ui/feature-gates/feature-gate-link_cfg.stderr @@ -1,4 +1,4 @@ -error[E0658]: is unstable +error[E0658]: kind="link_cfg" is unstable --> $DIR/feature-gate-link_cfg.rs:1:1 | LL | #[link(name = "foo", cfg(foo))] diff --git a/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr b/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr index 490d29ad8a35f..1553d0531dc09 100644 --- a/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr +++ b/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr @@ -13,8 +13,8 @@ error[E0658]: negative trait bounds are not yet fully implemented; use marker ty LL | impl !AutoDummyTrait for DummyStruct {} | ^^^^^^^^^^^^^^^ | - = note: see issue #13231 for more information - = help: add `#![feature(optin_builtin_traits)]` to the crate attributes to enable + = note: see issue #68318 for more information + = help: add `#![feature(negative_impls)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-plugin_registrar.stderr b/src/test/ui/feature-gates/feature-gate-plugin_registrar.stderr index 0847d77a85608..b3a43f4eef2a9 100644 --- a/src/test/ui/feature-gates/feature-gate-plugin_registrar.stderr +++ b/src/test/ui/feature-gates/feature-gate-plugin_registrar.stderr @@ -24,6 +24,6 @@ LL | #[plugin_registrar] | = note: `#[warn(deprecated)]` on by default -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr b/src/test/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr index 12b796d2e60a1..6c5d0091c5add 100644 --- a/src/test/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr +++ b/src/test/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr @@ -5,6 +5,7 @@ LL | match 0usize { | ^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `usize` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/feature-gate-precise_pointer_size_matching.rs:10:11 @@ -13,6 +14,7 @@ LL | match 0isize { | ^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `isize` error: aborting due to 2 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr b/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr index 1e039f17a0d11..1517a7a5c731a 100644 --- a/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr +++ b/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr @@ -1,10 +1,8 @@ -error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler +error: attributes starting with `rustc` are reserved for use by the `rustc` compiler --> $DIR/feature-gate-rustc-attrs.rs:8:3 | LL | #[rustc::unknown] | ^^^^^ - | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: expected attribute, found macro `rustc::unknown` --> $DIR/feature-gate-rustc-attrs.rs:8:3 @@ -12,13 +10,11 @@ error: expected attribute, found macro `rustc::unknown` LL | #[rustc::unknown] | ^^^^^^^^^^^^^^ not an attribute -error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler +error: attributes starting with `rustc` are reserved for use by the `rustc` compiler --> $DIR/feature-gate-rustc-attrs.rs:13:12 | LL | #[unknown::rustc] | ^^^^^ - | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: expected attribute, found macro `unknown::rustc` --> $DIR/feature-gate-rustc-attrs.rs:13:3 @@ -26,13 +22,11 @@ error: expected attribute, found macro `unknown::rustc` LL | #[unknown::rustc] | ^^^^^^^^^^^^^^ not an attribute -error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler +error: attributes starting with `rustc` are reserved for use by the `rustc` compiler --> $DIR/feature-gate-rustc-attrs.rs:20:3 | LL | #[rustc_unknown] | ^^^^^^^^^^^^^ - | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: cannot find attribute `rustc_unknown` in this scope --> $DIR/feature-gate-rustc-attrs.rs:20:3 diff --git a/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs b/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs index ff6e2b8290389..eecf2046ccbea 100644 --- a/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs +++ b/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs @@ -8,24 +8,28 @@ struct Foo; impl Fn<()> for Foo { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change +//~| ERROR manual implementations of `Fn` are experimental extern "rust-call" fn call(self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change } struct Foo1; impl FnOnce() for Foo1 { //~^ ERROR associated type bindings are not allowed here +//~| ERROR manual implementations of `FnOnce` are experimental extern "rust-call" fn call_once(self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change } struct Bar; impl FnMut<()> for Bar { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change +//~| ERROR manual implementations of `FnMut` are experimental extern "rust-call" fn call_mut(&self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change } struct Baz; impl FnOnce<()> for Baz { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change +//~| ERROR manual implementations of `FnOnce` are experimental extern "rust-call" fn call_once(&self, args: ()) -> () {} //~^ ERROR rust-call ABI is subject to change } diff --git a/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index 892020332d702..22a1ce3061889 100644 --- a/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/src/test/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -1,5 +1,5 @@ error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:11:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:12:12 | LL | extern "rust-call" fn call(self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | extern "rust-call" fn call(self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:17:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:19:12 | LL | extern "rust-call" fn call_once(self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | extern "rust-call" fn call_once(self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:12 | LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:29:12 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:33:12 | LL | extern "rust-call" fn call_once(&self, args: ()) -> () {} | ^^^^^^^^^^^ @@ -44,13 +44,13 @@ LL | impl Fn<()> for Foo { = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0229]: associated type bindings are not allowed here - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:15:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6 | LL | impl FnOnce() for Foo1 { | ^^^^^^^^ associated type not allowed here error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:21:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:6 | LL | impl FnMut<()> for Bar { | ^^^^^^^^^ help: use parenthetical notation instead: `FnMut() -> ()` @@ -59,7 +59,7 @@ LL | impl FnMut<()> for Bar { = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change - --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:27:6 + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:30:6 | LL | impl FnOnce<()> for Baz { | ^^^^^^^^^^ help: use parenthetical notation instead: `FnOnce() -> ()` @@ -67,7 +67,39 @@ LL | impl FnOnce<()> for Baz { = note: see issue #29625 for more information = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error: aborting due to 8 previous errors +error[E0183]: manual implementations of `Fn` are experimental + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:9:1 + | +LL | impl Fn<()> for Foo { + | ^^^^^^^^^^^^^^^^^^^ manual implementations of `Fn` are experimental + | + = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable + +error[E0183]: manual implementations of `FnMut` are experimental + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:1 + | +LL | impl FnMut<()> for Bar { + | ^^^^^^^^^^^^^^^^^^^^^^ manual implementations of `FnMut` are experimental + | + = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable + +error[E0183]: manual implementations of `FnOnce` are experimental + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:1 + | +LL | impl FnOnce() for Foo1 { + | ^^^^^^^^^^^^^^^^^^^^^^ manual implementations of `FnOnce` are experimental + | + = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable + +error[E0183]: manual implementations of `FnOnce` are experimental + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:30:1 + | +LL | impl FnOnce<()> for Baz { + | ^^^^^^^^^^^^^^^^^^^^^^^ manual implementations of `FnOnce` are experimental + | + = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable + +error: aborting due to 12 previous errors Some errors have detailed explanations: E0229, E0658. For more information about an error, try `rustc --explain E0229`. diff --git a/src/test/ui/feature-gates/feature-gate-unboxed-closures.rs b/src/test/ui/feature-gates/feature-gate-unboxed-closures.rs index b8d3aa4a141df..ebc5a2536f672 100644 --- a/src/test/ui/feature-gates/feature-gate-unboxed-closures.rs +++ b/src/test/ui/feature-gates/feature-gate-unboxed-closures.rs @@ -4,6 +4,7 @@ struct Test; impl FnOnce<(u32, u32)> for Test { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change +//~| ERROR manual implementations of `FnOnce` are experimental type Output = u32; extern "rust-call" fn call_once(self, (a, b): (u32, u32)) -> u32 { diff --git a/src/test/ui/feature-gates/feature-gate-unboxed-closures.stderr b/src/test/ui/feature-gates/feature-gate-unboxed-closures.stderr index feda2fe49f95c..2c8915d0ac334 100644 --- a/src/test/ui/feature-gates/feature-gate-unboxed-closures.stderr +++ b/src/test/ui/feature-gates/feature-gate-unboxed-closures.stderr @@ -1,5 +1,5 @@ error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-unboxed-closures.rs:9:12 + --> $DIR/feature-gate-unboxed-closures.rs:10:12 | LL | extern "rust-call" fn call_once(self, (a, b): (u32, u32)) -> u32 { | ^^^^^^^^^^^ @@ -16,6 +16,14 @@ LL | impl FnOnce<(u32, u32)> for Test { = note: see issue #29625 for more information = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error: aborting due to 2 previous errors +error[E0183]: manual implementations of `FnOnce` are experimental + --> $DIR/feature-gate-unboxed-closures.rs:5:1 + | +LL | impl FnOnce<(u32, u32)> for Test { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ manual implementations of `FnOnce` are experimental + | + = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/ffi_const.rs b/src/test/ui/ffi_const.rs new file mode 100644 index 0000000000000..7aeb5a49a1b58 --- /dev/null +++ b/src/test/ui/ffi_const.rs @@ -0,0 +1,5 @@ +#![feature(ffi_const)] +#![crate_type = "lib"] + +#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions +pub fn foo() {} diff --git a/src/test/ui/ffi_const.stderr b/src/test/ui/ffi_const.stderr new file mode 100644 index 0000000000000..623551cc07bbb --- /dev/null +++ b/src/test/ui/ffi_const.stderr @@ -0,0 +1,8 @@ +error[E0756]: `#[ffi_const]` may only be used on foreign functions + --> $DIR/ffi_const.rs:4:1 + | +LL | #[ffi_const] + | ^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/ffi_const2.rs b/src/test/ui/ffi_const2.rs new file mode 100644 index 0000000000000..4bd9637f0832c --- /dev/null +++ b/src/test/ui/ffi_const2.rs @@ -0,0 +1,11 @@ +#![feature(ffi_const, ffi_pure)] + +extern { + #[ffi_pure] //~ ERROR `#[ffi_const]` function cannot be `#[ffi_pure]` + #[ffi_const] + pub fn baz(); +} + +fn main() { + unsafe { baz() }; +} diff --git a/src/test/ui/ffi_const2.stderr b/src/test/ui/ffi_const2.stderr new file mode 100644 index 0000000000000..0b401942c4792 --- /dev/null +++ b/src/test/ui/ffi_const2.stderr @@ -0,0 +1,8 @@ +error[E0757]: `#[ffi_const]` function cannot be `#[ffi_pure]` + --> $DIR/ffi_const2.rs:4:5 + | +LL | #[ffi_pure] + | ^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/ffi_pure.rs b/src/test/ui/ffi_pure.rs new file mode 100644 index 0000000000000..c37d34c8784bb --- /dev/null +++ b/src/test/ui/ffi_pure.rs @@ -0,0 +1,5 @@ +#![feature(ffi_pure)] +#![crate_type = "lib"] + +#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions +pub fn foo() {} diff --git a/src/test/ui/ffi_pure.stderr b/src/test/ui/ffi_pure.stderr new file mode 100644 index 0000000000000..3a849c0bca79c --- /dev/null +++ b/src/test/ui/ffi_pure.stderr @@ -0,0 +1,8 @@ +error[E0755]: `#[ffi_pure]` may only be used on foreign functions + --> $DIR/ffi_pure.rs:4:1 + | +LL | #[ffi_pure] + | ^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/fmt/send-sync.stderr b/src/test/ui/fmt/send-sync.stderr index c8439764effc3..b3b53971a3712 100644 --- a/src/test/ui/fmt/send-sync.stderr +++ b/src/test/ui/fmt/send-sync.stderr @@ -2,7 +2,7 @@ error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely --> $DIR/send-sync.rs:8:5 | LL | fn send(_: T) {} - | ---- ---- required by this bound in `send` + | ---- required by this bound in `send` ... LL | send(format_args!("{:?}", c)); | ^^^^ `core::fmt::Opaque` cannot be shared between threads safely @@ -18,7 +18,7 @@ error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely --> $DIR/send-sync.rs:9:5 | LL | fn sync(_: T) {} - | ---- ---- required by this bound in `sync` + | ---- required by this bound in `sync` ... LL | sync(format_args!("{:?}", c)); | ^^^^ `core::fmt::Opaque` cannot be shared between threads safely diff --git a/src/test/ui/fn/dyn-fn-alignment.rs b/src/test/ui/fn/dyn-fn-alignment.rs new file mode 100644 index 0000000000000..125f44bbf0093 --- /dev/null +++ b/src/test/ui/fn/dyn-fn-alignment.rs @@ -0,0 +1,24 @@ +// run-pass + +#![feature(unsized_locals)] +#![allow(dead_code)] +#[repr(align(256))] +struct A { + v: u8, +} + +impl A { + fn f(&self) -> *const A { + self + } +} + +fn f2(v: u8) -> Box *const A> { + let a = A { v }; + Box::new(move || a.f()) +} + +fn main() { + let addr = f2(0)(); + assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); +} diff --git a/src/test/ui/fn/expr-fn-panic.rs b/src/test/ui/fn/expr-fn-panic.rs new file mode 100644 index 0000000000000..123b57f97a4ec --- /dev/null +++ b/src/test/ui/fn/expr-fn-panic.rs @@ -0,0 +1,11 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +fn f() -> ! { + panic!() +} + +fn main() { + f(); +} diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr index 7d4de63759b88..e3ada4f6bae06 100644 --- a/src/test/ui/fn/fn-trait-formatting.stderr +++ b/src/test/ui/fn/fn-trait-formatting.stderr @@ -35,7 +35,7 @@ error[E0277]: expected a `std::ops::Fn<(isize,)>` closure, found `{integer}` --> $DIR/fn-trait-formatting.rs:19:14 | LL | fn needs_fn(x: F) where F: Fn(isize) -> isize {} - | -------- ------------------ required by this bound in `needs_fn` + | ------------------ required by this bound in `needs_fn` ... LL | needs_fn(1); | ^ expected an `Fn<(isize,)>` closure, found `{integer}` diff --git a/src/test/ui/fn_must_use.rs b/src/test/ui/fn_must_use.rs index e411854661603..b4e9da0fc8405 100644 --- a/src/test/ui/fn_must_use.rs +++ b/src/test/ui/fn_must_use.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(unused_must_use)] diff --git a/src/test/ui/fn_must_use.stderr b/src/test/ui/fn_must_use.stderr index 64f865e5b70ae..d6b1cf3ae1f83 100644 --- a/src/test/ui/fn_must_use.stderr +++ b/src/test/ui/fn_must_use.stderr @@ -55,3 +55,5 @@ warning: unused comparison that must be used LL | m == n; | ^^^^^^ +warning: 8 warnings emitted + diff --git a/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr b/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr index 14aea2dc27eea..ce12b7853b6c1 100644 --- a/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr +++ b/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr @@ -3,6 +3,8 @@ error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` a | LL | for &1 in [1].iter() {} | ^^ patterns `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered + | + = note: the matched value is of type `&i32` error: aborting due to previous error diff --git a/src/test/ui/foreign-fn-return-lifetime.stderr b/src/test/ui/foreign-fn-return-lifetime.stderr index 575da18f24043..feecb6d80e771 100644 --- a/src/test/ui/foreign-fn-return-lifetime.stderr +++ b/src/test/ui/foreign-fn-return-lifetime.stderr @@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier --> $DIR/foreign-fn-return-lifetime.rs:5:19 | LL | pub fn f() -> &u8; - | ^ help: consider giving it a 'static lifetime: `&'static` + | ^ 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 + | +LL | pub fn f() -> &'static u8; + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/fully-qualified-type/fully-qualified-type-name3.rs b/src/test/ui/fully-qualified-type/fully-qualified-type-name3.rs deleted file mode 100644 index 22faa66d9fb03..0000000000000 --- a/src/test/ui/fully-qualified-type/fully-qualified-type-name3.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Test that we use fully-qualified type names in error messages. - -// ignore-test - -type T1 = usize; -type T2 = isize; - -fn bar(x: T1) -> T2 { - return x; - //~^ ERROR mismatched types: expected `T2`, found `T1` -} - -fn main() { -} diff --git a/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr b/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr index 19703fc99fd27..2aeffc3e5e457 100644 --- a/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr +++ b/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr @@ -2,7 +2,7 @@ error[E0451]: field `secret_uid` of struct `foo::S` is private --> $DIR/functional-struct-update-respects-privacy.rs:28:49 | LL | let s_2 = foo::S { b: format!("ess two"), ..s_1 }; // FRU ... - | ^^^ private field + | ^^^ field `secret_uid` is private error: aborting due to previous error diff --git a/src/test/ui/functions-closures/closure-inference.rs b/src/test/ui/functions-closures/closure-inference.rs index 96878445245bc..1877414f09942 100644 --- a/src/test/ui/functions-closures/closure-inference.rs +++ b/src/test/ui/functions-closures/closure-inference.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] fn foo(i: isize) -> isize { i + 1 } diff --git a/src/test/ui/functions-closures/closure-inference2.rs b/src/test/ui/functions-closures/closure-inference2.rs index f2dfa5888aac5..4ce132e86caa4 100644 --- a/src/test/ui/functions-closures/closure-inference2.rs +++ b/src/test/ui/functions-closures/closure-inference2.rs @@ -1,6 +1,6 @@ // run-pass // Test a rather underspecified example: - +#![allow(unused_braces)] pub fn main() { let f = {|i| i}; diff --git a/src/test/ui/generator/auto-trait-regions.nll.stderr b/src/test/ui/generator/auto-trait-regions.nll.stderr index bf87aea0d4c1a..794369a8dc02b 100644 --- a/src/test/ui/generator/auto-trait-regions.nll.stderr +++ b/src/test/ui/generator/auto-trait-regions.nll.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/auto-trait-regions.rs:45:24 + --> $DIR/auto-trait-regions.rs:46:24 | LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement @@ -12,7 +12,7 @@ LL | assert_foo(a); = note: consider using a `let` binding to create a longer lived value error[E0716]: temporary value dropped while borrowed - --> $DIR/auto-trait-regions.rs:45:35 + --> $DIR/auto-trait-regions.rs:46:35 | LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement @@ -25,13 +25,13 @@ LL | assert_foo(a); = note: consider using a `let` binding to create a longer lived value error: higher-ranked subtype error - --> $DIR/auto-trait-regions.rs:30:5 + --> $DIR/auto-trait-regions.rs:31:5 | LL | assert_foo(gen); | ^^^^^^^^^^^^^^^ error: higher-ranked subtype error - --> $DIR/auto-trait-regions.rs:49:5 + --> $DIR/auto-trait-regions.rs:50:5 | LL | assert_foo(gen); | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/generator/auto-trait-regions.rs b/src/test/ui/generator/auto-trait-regions.rs index dbd8965dcf0d0..1e77d8058a72b 100644 --- a/src/test/ui/generator/auto-trait-regions.rs +++ b/src/test/ui/generator/auto-trait-regions.rs @@ -1,5 +1,6 @@ #![feature(generators)] #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait Foo {} diff --git a/src/test/ui/generator/auto-trait-regions.stderr b/src/test/ui/generator/auto-trait-regions.stderr index 29a3907d93c84..5ec462e10465f 100644 --- a/src/test/ui/generator/auto-trait-regions.stderr +++ b/src/test/ui/generator/auto-trait-regions.stderr @@ -1,5 +1,5 @@ error: implementation of `Foo` is not general enough - --> $DIR/auto-trait-regions.rs:30:5 + --> $DIR/auto-trait-regions.rs:31:5 | LL | auto trait Foo {} | ----------------- trait `Foo` defined here @@ -11,7 +11,7 @@ LL | assert_foo(gen); = note: ...but `Foo` is actually implemented for the type `&'1 OnlyFooIfStaticRef`, for some specific lifetime `'1` error: implementation of `Foo` is not general enough - --> $DIR/auto-trait-regions.rs:30:5 + --> $DIR/auto-trait-regions.rs:31:5 | LL | auto trait Foo {} | ----------------- trait `Foo` defined here @@ -23,7 +23,7 @@ LL | assert_foo(gen); = note: ...but `Foo` is actually implemented for the type `&'1 OnlyFooIfStaticRef`, for some specific lifetime `'1` error: implementation of `Foo` is not general enough - --> $DIR/auto-trait-regions.rs:49:5 + --> $DIR/auto-trait-regions.rs:50:5 | LL | auto trait Foo {} | ----------------- trait `Foo` defined here @@ -35,7 +35,7 @@ LL | assert_foo(gen); = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2` error: implementation of `Foo` is not general enough - --> $DIR/auto-trait-regions.rs:49:5 + --> $DIR/auto-trait-regions.rs:50:5 | LL | auto trait Foo {} | ----------------- trait `Foo` defined here diff --git a/src/test/ui/generator/borrowing.stderr b/src/test/ui/generator/borrowing.stderr index 83987e19839ce..38e1ace8c4efb 100644 --- a/src/test/ui/generator/borrowing.stderr +++ b/src/test/ui/generator/borrowing.stderr @@ -1,19 +1,16 @@ error[E0597]: `a` does not live long enough --> $DIR/borrowing.rs:9:33 | +LL | let _b = { + | -- borrow later stored here +LL | let a = 3; LL | Pin::new(&mut || yield &a).resume(()) - | ----------^ - | | | - | | borrowed value does not live long enough + | -- ^ borrowed value does not live long enough + | | | value captured here by generator - | a temporary with access to the borrow is created here ... LL | LL | }; - | -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for generator - | | - | `a` dropped here while still borrowed - | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + | - `a` dropped here while still borrowed error[E0597]: `a` does not live long enough --> $DIR/borrowing.rs:16:20 diff --git a/src/test/ui/generator/conditional-drop.rs b/src/test/ui/generator/conditional-drop.rs index 990d94e6efc1b..0927df86927e0 100644 --- a/src/test/ui/generator/conditional-drop.rs +++ b/src/test/ui/generator/conditional-drop.rs @@ -1,5 +1,8 @@ // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + #![feature(generators, generator_trait)] use std::ops::Generator; diff --git a/src/test/ui/generator/control-flow.rs b/src/test/ui/generator/control-flow.rs index 9d4c217b76ed7..4f69c7855605a 100644 --- a/src/test/ui/generator/control-flow.rs +++ b/src/test/ui/generator/control-flow.rs @@ -1,5 +1,8 @@ // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + #![feature(generators, generator_trait)] use std::marker::Unpin; diff --git a/src/test/ui/generator/discriminant.rs b/src/test/ui/generator/discriminant.rs index 8a0f8a380ab59..3d0930da42243 100644 --- a/src/test/ui/generator/discriminant.rs +++ b/src/test/ui/generator/discriminant.rs @@ -3,10 +3,10 @@ // run-pass -#![feature(generators, generator_trait, core_intrinsics)] +#![feature(generators, generator_trait, core_intrinsics, discriminant_kind)] use std::intrinsics::discriminant_value; -use std::marker::Unpin; +use std::marker::{Unpin, DiscriminantKind}; use std::mem::size_of_val; use std::{cmp, ops::*}; @@ -65,7 +65,10 @@ macro_rules! yield250 { }; } -fn cycle(gen: impl Generator<()> + Unpin, expected_max_discr: u64) { +fn cycle( + gen: impl Generator<()> + Unpin + DiscriminantKind, + expected_max_discr: i32 +) { let mut gen = Box::pin(gen); let mut max_discr = 0; loop { diff --git a/src/test/ui/generator/drop-env.rs b/src/test/ui/generator/drop-env.rs index 7ba711881045d..66dfb8c2c0984 100644 --- a/src/test/ui/generator/drop-env.rs +++ b/src/test/ui/generator/drop-env.rs @@ -1,5 +1,8 @@ // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + #![feature(generators, generator_trait)] use std::ops::Generator; diff --git a/src/test/run-fail/generator-resume-after-panic.rs b/src/test/ui/generator/generator-resume-after-panic.rs similarity index 90% rename from src/test/run-fail/generator-resume-after-panic.rs rename to src/test/ui/generator/generator-resume-after-panic.rs index 1a7c2e8062901..55704f40e9f2b 100644 --- a/src/test/run-fail/generator-resume-after-panic.rs +++ b/src/test/ui/generator/generator-resume-after-panic.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:generator resumed after panicking +// ignore-emscripten no processes // Test that we get the correct message for resuming a panicked generator. diff --git a/src/test/ui/generator/generator-yielding-or-returning-itself.stderr b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr index fc8064d8225bf..9699abd5661a2 100644 --- a/src/test/ui/generator/generator-yielding-or-returning-itself.stderr +++ b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-re --> $DIR/generator-yielding-or-returning-itself.rs:15:5 | LL | pub fn want_cyclic_generator_return(_: T) - | ---------------------------- + | ---------------------------- required by a bound in this LL | where T: Generator | ---------- required by this bound in `want_cyclic_generator_return` ... @@ -18,7 +18,7 @@ error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-re --> $DIR/generator-yielding-or-returning-itself.rs:28:5 | LL | pub fn want_cyclic_generator_yield(_: T) - | --------------------------- + | --------------------------- required by a bound in this LL | where T: Generator | --------- required by this bound in `want_cyclic_generator_yield` ... diff --git a/src/test/ui/generator/issue-68112.rs b/src/test/ui/generator/issue-68112.rs new file mode 100644 index 0000000000000..9ab2abf740572 --- /dev/null +++ b/src/test/ui/generator/issue-68112.rs @@ -0,0 +1,56 @@ +#![feature(generators, generator_trait)] + +use std::{ + cell::RefCell, + sync::Arc, + pin::Pin, + ops::{Generator, GeneratorState}, +}; + +pub struct Ready(Option); +impl Generator<()> for Ready { + type Return = T; + type Yield = (); + fn resume(mut self: Pin<&mut Self>, _args: ()) -> GeneratorState<(), T> { + GeneratorState::Complete(self.0.take().unwrap()) + } +} +pub fn make_gen1(t: T) -> Ready { + Ready(Some(t)) +} + +fn require_send(_: impl Send) {} + +fn make_non_send_generator() -> impl Generator>> { + make_gen1(Arc::new(RefCell::new(0))) +} + +fn test1() { + let send_gen = || { + let _non_send_gen = make_non_send_generator(); + yield; + }; + require_send(send_gen); + //~^ ERROR generator cannot be sent between threads +} + +pub fn make_gen2(t: T) -> impl Generator { + || { + yield; + t + } +} +fn make_non_send_generator2() -> impl Generator>> { + make_gen2(Arc::new(RefCell::new(0))) +} + +fn test2() { + let send_gen = || { + let _non_send_gen = make_non_send_generator2(); + yield; + }; + require_send(send_gen); + //~^ ERROR `std::cell::RefCell` cannot be shared between threads safely +} + +fn main() {} diff --git a/src/test/ui/generator/issue-68112.stderr b/src/test/ui/generator/issue-68112.stderr new file mode 100644 index 0000000000000..83536f2af1406 --- /dev/null +++ b/src/test/ui/generator/issue-68112.stderr @@ -0,0 +1,40 @@ +error: generator cannot be sent between threads safely + --> $DIR/issue-68112.rs:33:5 + | +LL | fn require_send(_: impl Send) {} + | ---- required by this bound in `require_send` +... +LL | require_send(send_gen); + | ^^^^^^^^^^^^ generator is not `Send` + | + = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` +note: generator is not `Send` as this value is used across a yield + --> $DIR/issue-68112.rs:31:9 + | +LL | let _non_send_gen = make_non_send_generator(); + | ------------- has type `impl std::ops::Generator` which is not `Send` +LL | yield; + | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later +LL | }; + | - `_non_send_gen` is later dropped here + +error[E0277]: `std::cell::RefCell` cannot be shared between threads safely + --> $DIR/issue-68112.rs:52:5 + | +LL | fn require_send(_: impl Send) {} + | ---- required by this bound in `require_send` +... +LL | require_send(send_gen); + | ^^^^^^^^^^^^ `std::cell::RefCell` cannot be shared between threads safely + | + = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` + = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc>` + = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:38:5: 41:6 t:std::sync::Arc> {()}]` + = note: required because it appears within the type `impl std::ops::Generator` + = note: required because it appears within the type `impl std::ops::Generator` + = note: required because it appears within the type `{impl std::ops::Generator, ()}` + = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:48:20: 51:6 {impl std::ops::Generator, ()}]` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/generator/not-send-sync.rs b/src/test/ui/generator/not-send-sync.rs index 0db01c6f756ac..8ca5565fb2ab5 100644 --- a/src/test/ui/generator/not-send-sync.rs +++ b/src/test/ui/generator/not-send-sync.rs @@ -7,7 +7,7 @@ fn main() { fn assert_send(_: T) {} assert_sync(|| { - //~^ ERROR: future cannot be shared between threads safely + //~^ ERROR: generator cannot be shared between threads safely let a = Cell::new(2); yield; }); diff --git a/src/test/ui/generator/not-send-sync.stderr b/src/test/ui/generator/not-send-sync.stderr index 0ac1d189b79b0..5df2c1b52fb8a 100644 --- a/src/test/ui/generator/not-send-sync.stderr +++ b/src/test/ui/generator/not-send-sync.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::cell::Cell` cannot be shared between threads safely --> $DIR/not-send-sync.rs:16:5 | LL | fn assert_send(_: T) {} - | ----------- ---- required by this bound in `main::assert_send` + | ---- required by this bound in `main::assert_send` ... LL | assert_send(|| { | ^^^^^^^^^^^ `std::cell::Cell` cannot be shared between threads safely @@ -11,21 +11,21 @@ LL | assert_send(|| { = note: required because of the requirements on the impl of `std::marker::Send` for `&std::cell::Cell` = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:16:17: 20:6 a:&std::cell::Cell _]` -error: future cannot be shared between threads safely +error: generator cannot be shared between threads safely --> $DIR/not-send-sync.rs:9:5 | LL | fn assert_sync(_: T) {} - | ----------- ---- required by this bound in `main::assert_sync` + | ---- required by this bound in `main::assert_sync` ... LL | assert_sync(|| { - | ^^^^^^^^^^^ future returned by `main` is not `Sync` + | ^^^^^^^^^^^ generator is not `Sync` | = help: within `[generator@$DIR/not-send-sync.rs:9:17: 13:6 {std::cell::Cell, ()}]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell` -note: future is not `Sync` as this value is used across an yield +note: generator is not `Sync` as this value is used across a yield --> $DIR/not-send-sync.rs:12:9 | LL | let a = Cell::new(2); - | - has type `std::cell::Cell` + | - has type `std::cell::Cell` which is not `Sync` LL | yield; | ^^^^^ yield occurs here, with `a` maybe used later LL | }); diff --git a/src/test/ui/generator/resume-arg-late-bound.stderr b/src/test/ui/generator/resume-arg-late-bound.stderr index 7719d5123f466..ffa440daed8c4 100644 --- a/src/test/ui/generator/resume-arg-late-bound.stderr +++ b/src/test/ui/generator/resume-arg-late-bound.stderr @@ -2,7 +2,7 @@ error[E0631]: type mismatch in function arguments --> $DIR/resume-arg-late-bound.rs:15:10 | LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {} - | ---- ------------------------------- required by this bound in `test` + | ------------------------------- required by this bound in `test` ... LL | test(gen); | ^^^ diff --git a/src/test/ui/generator/retain-resume-ref.stderr b/src/test/ui/generator/retain-resume-ref.stderr index bc715c7030eb3..e33310d12d9ef 100644 --- a/src/test/ui/generator/retain-resume-ref.stderr +++ b/src/test/ui/generator/retain-resume-ref.stderr @@ -4,10 +4,9 @@ error[E0499]: cannot borrow `thing` as mutable more than once at a time LL | gen.as_mut().resume(&mut thing); | ---------- first mutable borrow occurs here LL | gen.as_mut().resume(&mut thing); - | ^^^^^^^^^^ second mutable borrow occurs here -LL | -LL | } - | - first borrow might be used here, when `gen` is dropped and runs the destructor for generator + | ------ ^^^^^^^^^^ second mutable borrow occurs here + | | + | first borrow later used by call error: aborting due to previous error diff --git a/src/test/ui/generator/smoke-resume-args.rs b/src/test/ui/generator/smoke-resume-args.rs index 32f3ee32d77b9..fa9271c538f53 100644 --- a/src/test/ui/generator/smoke-resume-args.rs +++ b/src/test/ui/generator/smoke-resume-args.rs @@ -1,5 +1,8 @@ // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + #![feature(generators, generator_trait)] use std::fmt::Debug; diff --git a/src/test/ui/generator/smoke.rs b/src/test/ui/generator/smoke.rs index 9289710b34bf9..7a917a05dd9a6 100644 --- a/src/test/ui/generator/smoke.rs +++ b/src/test/ui/generator/smoke.rs @@ -1,5 +1,8 @@ // run-pass +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + // ignore-emscripten no threads support // compile-flags: --test diff --git a/src/test/ui/generator/static-not-unpin.stderr b/src/test/ui/generator/static-not-unpin.stderr index 6512d67319b0b..3bb899cd89024 100644 --- a/src/test/ui/generator/static-not-unpin.stderr +++ b/src/test/ui/generator/static-not-unpin.stderr @@ -2,7 +2,7 @@ error[E0277]: `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]` cannot --> $DIR/static-not-unpin.rs:14:18 | LL | fn assert_unpin(_: T) { - | ------------ ----- required by this bound in `assert_unpin` + | ----- required by this bound in `assert_unpin` ... LL | assert_unpin(generator); | ^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]` diff --git a/src/test/ui/generic-associated-types/construct_with_other_type.stderr b/src/test/ui/generic-associated-types/construct_with_other_type.stderr index bad746f7ef121..b9468b3330b44 100644 --- a/src/test/ui/generic-associated-types/construct_with_other_type.stderr +++ b/src/test/ui/generic-associated-types/construct_with_other_type.stderr @@ -2,11 +2,16 @@ error[E0271]: type mismatch resolving `for<'a> <::Baa<'a> as std::ops: --> $DIR/construct_with_other_type.rs:19:9 | LL | impl Baz for T where T: Foo { - | ^^^ expected type parameter `T`, found associated type + | - ^^^ expected type parameter `T`, found associated type + | | + | this type parameter | = note: expected associated type `::Bar<'_, 'static>` found associated type `<::Quux<'_> as Foo>::Bar<'_, 'static>` - = note: you might be missing a type parameter or trait bound +help: consider further restricting this bound + | +LL | impl Baz for T where T: Foo + Baz { + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/gat-incomplete-warning.stderr b/src/test/ui/generic-associated-types/gat-incomplete-warning.stderr index d75f9fb8451b9..0215ff395df7d 100644 --- a/src/test/ui/generic-associated-types/gat-incomplete-warning.stderr +++ b/src/test/ui/generic-associated-types/gat-incomplete-warning.stderr @@ -1,8 +1,11 @@ -warning: the feature `generic_associated_types` is incomplete and may cause the compiler to crash +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/gat-incomplete-warning.rs:3:12 | LL | #![feature(generic_associated_types)] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/generic-associated-types/impl_bounds.stderr b/src/test/ui/generic-associated-types/impl_bounds.stderr index 486b538045e49..d5560c8133773 100644 --- a/src/test/ui/generic-associated-types/impl_bounds.stderr +++ b/src/test/ui/generic-associated-types/impl_bounds.stderr @@ -34,13 +34,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | type C where Self: Copy = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/impl_bounds.rs:14:6 - | -LL | impl Foo for Fooy { - | ^ = note: required because of the requirements on the impl of `std::marker::Copy` for `Fooy` = note: the requirement `Fooy: std::marker::Copy` appears on the associated impl type but not on the corresponding associated trait type +help: consider restricting type parameter `T` + | +LL | impl Foo for Fooy { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.stderr b/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.stderr index 687423962361b..4b06baa09ffbf 100644 --- a/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.stderr +++ b/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.stderr @@ -2,9 +2,9 @@ error[E0280]: the requirement `for<'a> ::Item<'a>: 'a` is not --> $DIR/issue-62326-parameter-out-of-range.rs:7:20 | LL | trait Iterator { - | -------------- required by `Iterator` + | -------- required by a bound in this LL | type Item<'a>: 'a; - | ^^ + | ^^ required by this bound in `Iterator` error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/iterable.rs b/src/test/ui/generic-associated-types/iterable.rs index 616421112db67..105ab4a8adc38 100644 --- a/src/test/ui/generic-associated-types/iterable.rs +++ b/src/test/ui/generic-associated-types/iterable.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #![allow(incomplete_features)] #![feature(generic_associated_types)] diff --git a/src/test/ui/generic-associated-types/iterable.stderr b/src/test/ui/generic-associated-types/iterable.stderr index e18c6cec64e71..6e75462122513 100644 --- a/src/test/ui/generic-associated-types/iterable.stderr +++ b/src/test/ui/generic-associated-types/iterable.stderr @@ -1,65 +1,57 @@ error[E0271]: type mismatch resolving `for<'a> < as Iterable>::Iter<'a> as std::iter::Iterator>::Item == as Iterable>::Item<'a>` - --> $DIR/iterable.rs:20:5 + --> $DIR/iterable.rs:15:33 | -LL | impl Iterable for Vec { - | --------------------------- in this `impl` item LL | type Item<'a> where T: 'a = as Iterator>::Item; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found associated type - | - ::: $SRC_DIR/libcore/iter/traits/iterator.rs:LL:COL - | -LL | type Item; - | ---- associated type defined here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found associated type | = note: expected reference `&T` found associated type ` as Iterable>::Item<'_>` - = note: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` + = help: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0271]: type mismatch resolving `for<'a> <<[T] as Iterable>::Iter<'a> as std::iter::Iterator>::Item == <[T] as Iterable>::Item<'a>` - --> $DIR/iterable.rs:32:5 + --> $DIR/iterable.rs:27:33 | -LL | impl Iterable for [T] { - | ------------------------ in this `impl` item LL | type Item<'a> where T: 'a = as Iterator>::Item; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found associated type - | - ::: $SRC_DIR/libcore/iter/traits/iterator.rs:LL:COL - | -LL | type Item; - | ---- associated type defined here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found associated type | = note: expected reference `&T` found associated type `<[T] as Iterable>::Item<'_>` - = note: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` + = help: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0271]: type mismatch resolving `for<'a> < as Iterable>::Iter<'a> as std::iter::Iterator>::Item == as Iterable>::Item<'a>` - --> $DIR/iterable.rs:24:30 + --> $DIR/iterable.rs:19:30 | LL | trait Iterable { - | -------------- required by `Iterable` + | -------- required by a bound in this +LL | type Item<'a> where Self: 'a; +LL | type Iter<'a>: Iterator> where Self: 'a; + | --------------------- required by this bound in `Iterable` ... LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { | ^^^^^^^^^^^^^^ expected associated type, found reference | = note: expected associated type ` as Iterable>::Item<'_>` found reference `&T` - = note: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` or calling a method that returns ` as Iterable>::Item<'_>` + = help: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` or calling a method that returns ` as Iterable>::Item<'_>` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0271]: type mismatch resolving `for<'a> <<[T] as Iterable>::Iter<'a> as std::iter::Iterator>::Item == <[T] as Iterable>::Item<'a>` - --> $DIR/iterable.rs:36:30 + --> $DIR/iterable.rs:31:30 | LL | trait Iterable { - | -------------- required by `Iterable` + | -------- required by a bound in this +LL | type Item<'a> where Self: 'a; +LL | type Iter<'a>: Iterator> where Self: 'a; + | --------------------- required by this bound in `Iterable` ... LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { | ^^^^^^^^^^^^^^ expected associated type, found reference | = note: expected associated type `<[T] as Iterable>::Item<'_>` found reference `&T` - = note: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` or calling a method that returns `<[T] as Iterable>::Item<'_>` + = help: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` or calling a method that returns `<[T] as Iterable>::Item<'_>` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 4 previous errors diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed new file mode 100644 index 0000000000000..364d2388741b0 --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.fixed @@ -0,0 +1,46 @@ +// run-rustfix + +use std::ops::Add; + +struct A(B); + +impl Add for A where B: Add + std::ops::Add { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + A(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct C(B); + +impl> Add for C { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct D(B); + +impl> Add for D { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B` + } +} + +struct E(B); + +impl Add for E where B: Add, B: std::ops::Add { + //~^ ERROR equality constraints are not yet supported in `where` clauses + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs new file mode 100644 index 0000000000000..ffafff5e9f586 --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.rs @@ -0,0 +1,46 @@ +// run-rustfix + +use std::ops::Add; + +struct A(B); + +impl Add for A where B: Add { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + A(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct C(B); + +impl Add for C { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +struct D(B); + +impl Add for D { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B` + } +} + +struct E(B); + +impl Add for E where ::Output = B { + //~^ ERROR equality constraints are not yet supported in `where` clauses + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr new file mode 100644 index 0000000000000..50536fdaca96e --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.stderr @@ -0,0 +1,77 @@ +error: equality constraints are not yet supported in `where` clauses + --> $DIR/missing-bounds.rs:37:33 + | +LL | impl Add for E where ::Output = B { + | ^^^^^^^^^^^^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `Output` is an associated type you're trying to set, use the associated type binding syntax + | +LL | impl Add for E where B: Add { + | ^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/missing-bounds.rs:11:11 + | +LL | impl Add for A where B: Add { + | - this type parameter +... +LL | A(self.0 + rhs.0) + | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type + | + = note: expected type parameter `B` + found associated type `::Output` +help: consider further restricting this bound + | +LL | impl Add for A where B: Add + std::ops::Add { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/missing-bounds.rs:21:14 + | +LL | impl Add for C { + | - this type parameter +... +LL | Self(self.0 + rhs.0) + | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type + | + = note: expected type parameter `B` + found associated type `::Output` +help: consider further restricting this bound + | +LL | impl> Add for C { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0369]: cannot add `B` to `B` + --> $DIR/missing-bounds.rs:31:21 + | +LL | Self(self.0 + rhs.0) + | ------ ^ ----- B + | | + | B + | +help: consider restricting type parameter `B` + | +LL | impl> Add for D { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/missing-bounds.rs:42:14 + | +LL | impl Add for E where ::Output = B { + | - this type parameter +... +LL | Self(self.0 + rhs.0) + | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type + | + = note: expected type parameter `B` + found associated type `::Output` +help: consider further restricting type parameter `B` + | +LL | impl Add for E where ::Output = B, B: std::ops::Add { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr b/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr index ea0664c48d4d8..95f4aa9e6dbaa 100644 --- a/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr +++ b/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr @@ -1,6 +1,9 @@ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/issue-61631-default-type-param-can-reference-self-in-trait.rs:19:6 | +LL | trait Tsized {} + | - required by this bound in `Tsized` +LL | LL | impl Tsized for () {} | ^^^^^^ doesn't have a size known at compile-time | diff --git a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr index 9f410c0dbbbd2..1106a06782280 100644 --- a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr +++ b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr @@ -5,16 +5,33 @@ LL | fn should_error() where T : Into<&u32> {} | ^ explicit lifetime name needed here error[E0106]: missing lifetime specifier - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:9:19 + --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:9:21 | LL | fn foo<'b, L: X<&'b Nested>>(); - | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | ^ expected named lifetime parameter + | +note: these named lifetimes are available to use + --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:8:9 + | +LL | trait X<'a, K: 'a> { + | ^^ +LL | fn foo<'b, L: X<&'b Nested>>(); + | ^^ +help: consider using one of the available lifetimes here + | +LL | fn foo<'b, L: X<'lifetime, &'b Nested>>(); + | ^^^^^^^^^^ error[E0106]: missing lifetime specifier - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:15 + --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:17 | LL | fn bar<'b, L: X<&'b Nested>>(){} - | ^^^^^^^^^^^^^^^^^^ expected lifetime parameter + | ^ expected named lifetime parameter + | +help: consider using the `'b` lifetime + | +LL | fn bar<'b, L: X<'b, &'b Nested>>(){} + | ^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/glob-resolve1.stderr b/src/test/ui/glob-resolve1.stderr index efbd53fd223a4..995da6cc1f975 100644 --- a/src/test/ui/glob-resolve1.stderr +++ b/src/test/ui/glob-resolve1.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find function `fpriv` in this scope LL | fpriv(); | ^^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this function | LL | use bar::fpriv; | @@ -15,7 +15,7 @@ error[E0425]: cannot find function `epriv` in this scope LL | epriv(); | ^^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this function | LL | use bar::epriv; | @@ -32,7 +32,7 @@ error[E0425]: cannot find value `C` in this scope LL | C; | ^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this unit struct | LL | use bar::C; | @@ -56,7 +56,7 @@ help: an enum with a similar name exists | LL | foo::(); | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this enum | LL | use bar::A; | @@ -74,7 +74,7 @@ help: an enum with a similar name exists | LL | foo::(); | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this struct | LL | use bar::C; | @@ -92,7 +92,7 @@ help: an enum with a similar name exists | LL | foo::(); | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this type alias | LL | use bar::D; | diff --git a/src/test/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr b/src/test/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr index 26d0cf9e9ecba..6c7a0cdb77adf 100644 --- a/src/test/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr +++ b/src/test/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr @@ -5,6 +5,7 @@ LL | m!(0f32, core::f32::NEG_INFINITY..); | ^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `f32` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:17:8 @@ -13,6 +14,7 @@ LL | m!(0f32, ..core::f32::INFINITY); | ^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `f32` error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:26:8 @@ -21,6 +23,7 @@ LL | m!('a', ..core::char::MAX); | ^^^ pattern `'\u{10ffff}'` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `char` error[E0004]: non-exhaustive patterns: `'\u{10fffe}'..='\u{10ffff}'` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:27:8 @@ -29,6 +32,7 @@ LL | m!('a', ..ALMOST_MAX); | ^^^ pattern `'\u{10fffe}'..='\u{10ffff}'` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `char` error[E0004]: non-exhaustive patterns: `'\u{0}'` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:28:8 @@ -37,6 +41,7 @@ LL | m!('a', ALMOST_MIN..); | ^^^ pattern `'\u{0}'` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `char` error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:29:8 @@ -45,6 +50,7 @@ LL | m!('a', ..=ALMOST_MAX); | ^^^ pattern `'\u{10ffff}'` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `char` error[E0004]: non-exhaustive patterns: `'b'` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:30:8 @@ -53,6 +59,7 @@ LL | m!('a', ..=VAL | VAL_2..); | ^^^ pattern `'b'` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `char` error[E0004]: non-exhaustive patterns: `'b'` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:31:8 @@ -61,6 +68,7 @@ LL | m!('a', ..VAL_1 | VAL_2..); | ^^^ pattern `'b'` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `char` error[E0004]: non-exhaustive patterns: `std::u8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:41:12 @@ -69,6 +77,7 @@ LL | m!(0, ..core::u8::MAX); | ^ pattern `std::u8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `254u8..=std::u8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:42:12 @@ -77,6 +86,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `254u8..=std::u8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `0u8` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:43:12 @@ -85,6 +95,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `0u8` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `std::u8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:44:12 @@ -93,6 +104,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::u8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `43u8` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:45:12 @@ -101,6 +113,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43u8` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `43u8` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:46:12 @@ -109,6 +122,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43u8` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `std::u16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:54:12 @@ -117,6 +131,7 @@ LL | m!(0, ..core::u16::MAX); | ^ pattern `std::u16::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u16` error[E0004]: non-exhaustive patterns: `65534u16..=std::u16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:55:12 @@ -125,6 +140,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `65534u16..=std::u16::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u16` error[E0004]: non-exhaustive patterns: `0u16` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:56:12 @@ -133,6 +149,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `0u16` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u16` error[E0004]: non-exhaustive patterns: `std::u16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:57:12 @@ -141,6 +158,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::u16::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u16` error[E0004]: non-exhaustive patterns: `43u16` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:58:12 @@ -149,6 +167,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43u16` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u16` error[E0004]: non-exhaustive patterns: `43u16` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:59:12 @@ -157,6 +176,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43u16` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u16` error[E0004]: non-exhaustive patterns: `std::u32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:67:12 @@ -165,6 +185,7 @@ LL | m!(0, ..core::u32::MAX); | ^ pattern `std::u32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u32` error[E0004]: non-exhaustive patterns: `4294967294u32..=std::u32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:68:12 @@ -173,6 +194,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `4294967294u32..=std::u32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u32` error[E0004]: non-exhaustive patterns: `0u32` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:69:12 @@ -181,6 +203,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `0u32` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u32` error[E0004]: non-exhaustive patterns: `std::u32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:70:12 @@ -189,6 +212,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::u32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u32` error[E0004]: non-exhaustive patterns: `43u32` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:71:12 @@ -197,6 +221,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43u32` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u32` error[E0004]: non-exhaustive patterns: `43u32` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:72:12 @@ -205,6 +230,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43u32` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u32` error[E0004]: non-exhaustive patterns: `std::u64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:80:12 @@ -213,6 +239,7 @@ LL | m!(0, ..core::u64::MAX); | ^ pattern `std::u64::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u64` error[E0004]: non-exhaustive patterns: `18446744073709551614u64..=std::u64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:81:12 @@ -221,6 +248,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `18446744073709551614u64..=std::u64::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u64` error[E0004]: non-exhaustive patterns: `0u64` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:82:12 @@ -229,6 +257,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `0u64` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u64` error[E0004]: non-exhaustive patterns: `std::u64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:83:12 @@ -237,6 +266,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::u64::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u64` error[E0004]: non-exhaustive patterns: `43u64` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:84:12 @@ -245,6 +275,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43u64` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u64` error[E0004]: non-exhaustive patterns: `43u64` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:85:12 @@ -253,6 +284,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43u64` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u64` error[E0004]: non-exhaustive patterns: `std::u128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:93:12 @@ -261,6 +293,7 @@ LL | m!(0, ..core::u128::MAX); | ^ pattern `std::u128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211454u128..=std::u128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:94:12 @@ -269,6 +302,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `340282366920938463463374607431768211454u128..=std::u128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `0u128` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:95:12 @@ -277,6 +311,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `0u128` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `std::u128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:96:12 @@ -285,6 +320,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::u128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `43u128` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:97:12 @@ -293,6 +329,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43u128` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `43u128` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:98:12 @@ -301,6 +338,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43u128` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `std::i8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:109:12 @@ -309,6 +347,7 @@ LL | m!(0, ..core::i8::MAX); | ^ pattern `std::i8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `126i8..=std::i8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:110:12 @@ -317,6 +356,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `126i8..=std::i8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `std::i8::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:111:12 @@ -325,6 +365,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `std::i8::MIN` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `std::i8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:112:12 @@ -333,6 +374,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::i8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `43i8` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:113:12 @@ -341,6 +383,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43i8` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `43i8` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:114:12 @@ -349,6 +392,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43i8` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `std::i16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:122:12 @@ -357,6 +401,7 @@ LL | m!(0, ..core::i16::MAX); | ^ pattern `std::i16::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i16` error[E0004]: non-exhaustive patterns: `32766i16..=std::i16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:123:12 @@ -365,6 +410,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `32766i16..=std::i16::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i16` error[E0004]: non-exhaustive patterns: `std::i16::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:124:12 @@ -373,6 +419,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `std::i16::MIN` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i16` error[E0004]: non-exhaustive patterns: `std::i16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:125:12 @@ -381,6 +428,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::i16::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i16` error[E0004]: non-exhaustive patterns: `43i16` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:126:12 @@ -389,6 +437,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43i16` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i16` error[E0004]: non-exhaustive patterns: `43i16` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:127:12 @@ -397,6 +446,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43i16` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i16` error[E0004]: non-exhaustive patterns: `std::i32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:135:12 @@ -405,6 +455,7 @@ LL | m!(0, ..core::i32::MAX); | ^ pattern `std::i32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error[E0004]: non-exhaustive patterns: `2147483646i32..=std::i32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:136:12 @@ -413,6 +464,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `2147483646i32..=std::i32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error[E0004]: non-exhaustive patterns: `std::i32::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:137:12 @@ -421,6 +473,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `std::i32::MIN` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error[E0004]: non-exhaustive patterns: `std::i32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:138:12 @@ -429,6 +482,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::i32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error[E0004]: non-exhaustive patterns: `43i32` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:139:12 @@ -437,6 +491,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43i32` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error[E0004]: non-exhaustive patterns: `43i32` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:140:12 @@ -445,6 +500,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43i32` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error[E0004]: non-exhaustive patterns: `std::i64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:148:12 @@ -453,6 +509,7 @@ LL | m!(0, ..core::i64::MAX); | ^ pattern `std::i64::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i64` error[E0004]: non-exhaustive patterns: `9223372036854775806i64..=std::i64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:149:12 @@ -461,6 +518,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `9223372036854775806i64..=std::i64::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i64` error[E0004]: non-exhaustive patterns: `std::i64::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:150:12 @@ -469,6 +527,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `std::i64::MIN` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i64` error[E0004]: non-exhaustive patterns: `std::i64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:151:12 @@ -477,6 +536,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::i64::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i64` error[E0004]: non-exhaustive patterns: `43i64` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:152:12 @@ -485,6 +545,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43i64` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i64` error[E0004]: non-exhaustive patterns: `43i64` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:153:12 @@ -493,6 +554,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43i64` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i64` error[E0004]: non-exhaustive patterns: `std::i128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:161:12 @@ -501,6 +563,7 @@ LL | m!(0, ..core::i128::MAX); | ^ pattern `std::i128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i128` error[E0004]: non-exhaustive patterns: `170141183460469231731687303715884105726i128..=std::i128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:162:12 @@ -509,6 +572,7 @@ LL | m!(0, ..ALMOST_MAX); | ^ pattern `170141183460469231731687303715884105726i128..=std::i128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i128` error[E0004]: non-exhaustive patterns: `std::i128::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:163:12 @@ -517,6 +581,7 @@ LL | m!(0, ALMOST_MIN..); | ^ pattern `std::i128::MIN` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i128` error[E0004]: non-exhaustive patterns: `std::i128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:164:12 @@ -525,6 +590,7 @@ LL | m!(0, ..=ALMOST_MAX); | ^ pattern `std::i128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i128` error[E0004]: non-exhaustive patterns: `43i128` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:165:12 @@ -533,6 +599,7 @@ LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43i128` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i128` error[E0004]: non-exhaustive patterns: `43i128` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:166:12 @@ -541,6 +608,7 @@ LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43i128` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i128` error: aborting due to 68 previous errors diff --git a/src/test/run-fail/hashmap-capacity-overflow.rs b/src/test/ui/hashmap/hashmap-capacity-overflow.rs similarity index 86% rename from src/test/run-fail/hashmap-capacity-overflow.rs rename to src/test/ui/hashmap/hashmap-capacity-overflow.rs index 038f2756ff322..5f88683f4adfc 100644 --- a/src/test/run-fail/hashmap-capacity-overflow.rs +++ b/src/test/ui/hashmap/hashmap-capacity-overflow.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:capacity overflow +// ignore-emscripten no processes use std::collections::hash_map::HashMap; use std::usize; diff --git a/src/test/ui/hashmap-iter-value-lifetime.nll.stderr b/src/test/ui/hashmap/hashmap-iter-value-lifetime.nll.stderr similarity index 100% rename from src/test/ui/hashmap-iter-value-lifetime.nll.stderr rename to src/test/ui/hashmap/hashmap-iter-value-lifetime.nll.stderr diff --git a/src/test/ui/hashmap-iter-value-lifetime.rs b/src/test/ui/hashmap/hashmap-iter-value-lifetime.rs similarity index 100% rename from src/test/ui/hashmap-iter-value-lifetime.rs rename to src/test/ui/hashmap/hashmap-iter-value-lifetime.rs diff --git a/src/test/ui/hashmap-iter-value-lifetime.stderr b/src/test/ui/hashmap/hashmap-iter-value-lifetime.stderr similarity index 100% rename from src/test/ui/hashmap-iter-value-lifetime.stderr rename to src/test/ui/hashmap/hashmap-iter-value-lifetime.stderr diff --git a/src/test/ui/hashmap-lifetimes.nll.stderr b/src/test/ui/hashmap/hashmap-lifetimes.nll.stderr similarity index 100% rename from src/test/ui/hashmap-lifetimes.nll.stderr rename to src/test/ui/hashmap/hashmap-lifetimes.nll.stderr diff --git a/src/test/ui/hashmap-lifetimes.rs b/src/test/ui/hashmap/hashmap-lifetimes.rs similarity index 100% rename from src/test/ui/hashmap-lifetimes.rs rename to src/test/ui/hashmap/hashmap-lifetimes.rs diff --git a/src/test/ui/hashmap-lifetimes.stderr b/src/test/ui/hashmap/hashmap-lifetimes.stderr similarity index 100% rename from src/test/ui/hashmap-lifetimes.stderr rename to src/test/ui/hashmap/hashmap-lifetimes.stderr diff --git a/src/test/ui/hashmap-memory.rs b/src/test/ui/hashmap/hashmap-memory.rs similarity index 100% rename from src/test/ui/hashmap-memory.rs rename to src/test/ui/hashmap/hashmap-memory.rs diff --git a/src/test/ui/hrtb/due-to-where-clause.nll.stderr b/src/test/ui/hrtb/due-to-where-clause.nll.stderr index e476047a7a644..90803a0adb01b 100644 --- a/src/test/ui/hrtb/due-to-where-clause.nll.stderr +++ b/src/test/ui/hrtb/due-to-where-clause.nll.stderr @@ -2,7 +2,7 @@ error: higher-ranked subtype error --> $DIR/due-to-where-clause.rs:2:5 | LL | test::(&mut 42); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hrtb/hrtb-conflate-regions.stderr b/src/test/ui/hrtb/hrtb-conflate-regions.stderr index 9822b48f4f48f..7250935ea296b 100644 --- a/src/test/ui/hrtb/hrtb-conflate-regions.stderr +++ b/src/test/ui/hrtb/hrtb-conflate-regions.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `for<'a, 'b> SomeStruct: Foo<(&'a isize, &'b isize --> $DIR/hrtb-conflate-regions.rs:27:22 | LL | fn want_foo2() - | --------- + | --------- required by a bound in this LL | where T : for<'a,'b> Foo<(&'a isize, &'b isize)> | -------------------------------------- required by this bound in `want_foo2` ... diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.stderr index 969d9eda73519..7a7285d3d76e0 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `(): Trait fn(&'b u32)>` is not satisfied --> $DIR/hrtb-exists-forall-trait-contravariant.rs:34:11 | LL | fn foo() - | --- + | --- required by a bound in this LL | where LL | T: Trait fn(&'b u32)>, | -------------------------- required by this bound in `foo` diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.stderr index dddc2bcce49e5..1e335f9ee9610 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `(): Trait fn(fn(&'b u32))>` is not satisf --> $DIR/hrtb-exists-forall-trait-covariant.rs:36:11 | LL | fn foo() - | --- + | --- required by a bound in this LL | where LL | T: Trait fn(fn(&'b u32))>, | ------------------------------ required by this bound in `foo` diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr index 23ef75944d317..9174ea4d8419d 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `(): Trait fn(std::cell::Cell<&'b u32>)>` --> $DIR/hrtb-exists-forall-trait-invariant.rs:28:11 | LL | fn foo() - | --- + | --- required by a bound in this LL | where LL | T: Trait fn(Cell<&'b u32>)>, | -------------------------------- required by this bound in `foo` diff --git a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr index 6307a9b380ebf..87a13889298df 100644 --- a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr +++ b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr @@ -2,18 +2,17 @@ error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied --> $DIR/hrtb-higher-ranker-supertraits-transitive.rs:47:26 | LL | fn want_bar_for_any_ccx(b: &B) - | -------------------- + | -------------------- required by a bound in this LL | where B : for<'ccx> Bar<'ccx> | ------------------- required by this bound in `want_bar_for_any_ccx` ... LL | want_bar_for_any_ccx(b); | ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B` | -help: consider further restricting this bound with `+ for<'ccx> Bar<'ccx>` - --> $DIR/hrtb-higher-ranker-supertraits-transitive.rs:44:11 +help: consider further restricting this bound | -LL | where B : Qux - | ^^^^^^^ +LL | where B : Qux + for<'ccx> Bar<'ccx> + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr index 762c7c05f7ae8..0123faa36dbcd 100644 --- a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr +++ b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr @@ -5,15 +5,14 @@ LL | want_foo_for_any_tcx(f); | ^ the trait `for<'tcx> Foo<'tcx>` is not implemented for `F` ... LL | fn want_foo_for_any_tcx(f: &F) - | -------------------- + | -------------------- required by a bound in this LL | where F : for<'tcx> Foo<'tcx> | ------------------- required by this bound in `want_foo_for_any_tcx` | -help: consider further restricting this bound with `+ for<'tcx> Foo<'tcx>` - --> $DIR/hrtb-higher-ranker-supertraits.rs:15:11 +help: consider further restricting this bound | -LL | where F : Foo<'x> - | ^^^^^^^^^^^ +LL | where F : Foo<'x> + for<'tcx> Foo<'tcx> + | ^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied --> $DIR/hrtb-higher-ranker-supertraits.rs:35:26 @@ -22,15 +21,14 @@ LL | want_bar_for_any_ccx(b); | ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B` ... LL | fn want_bar_for_any_ccx(b: &B) - | -------------------- + | -------------------- required by a bound in this LL | where B : for<'ccx> Bar<'ccx> | ------------------- required by this bound in `want_bar_for_any_ccx` | -help: consider further restricting this bound with `+ for<'ccx> Bar<'ccx>` - --> $DIR/hrtb-higher-ranker-supertraits.rs:29:11 +help: consider further restricting this bound | -LL | where B : Bar<'x> - | ^^^^^^^^^^^ +LL | where B : Bar<'x> + for<'ccx> Bar<'ccx> + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/hrtb/hrtb-just-for-static.stderr b/src/test/ui/hrtb/hrtb-just-for-static.stderr index 6ec0beefd60e3..4fa404624775b 100644 --- a/src/test/ui/hrtb/hrtb-just-for-static.stderr +++ b/src/test/ui/hrtb/hrtb-just-for-static.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `for<'a> StaticInt: Foo<&'a isize>` is not satisfi --> $DIR/hrtb-just-for-static.rs:24:17 | LL | fn want_hrtb() - | --------- + | --------- required by a bound in this LL | where T : for<'a> Foo<&'a isize> | ---------------------- required by this bound in `want_hrtb` ... @@ -16,7 +16,7 @@ error[E0277]: the trait bound `for<'a> &'a u32: Foo<&'a isize>` is not satisfied --> $DIR/hrtb-just-for-static.rs:30:17 | LL | fn want_hrtb() - | --------- + | --------- required by a bound in this LL | where T : for<'a> Foo<&'a isize> | ---------------------- required by this bound in `want_hrtb` ... diff --git a/src/test/ui/hrtb/hrtb-perfect-forwarding.nll.stderr b/src/test/ui/hrtb/hrtb-perfect-forwarding.nll.stderr index 303c0cc645e38..edce0e6702ee1 100644 --- a/src/test/ui/hrtb/hrtb-perfect-forwarding.nll.stderr +++ b/src/test/ui/hrtb/hrtb-perfect-forwarding.nll.stderr @@ -76,5 +76,5 @@ LL | | } | = help: a `loop` may express intention better if this is on purpose -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 4 warnings emitted diff --git a/src/test/ui/hrtb/hrtb-perfect-forwarding.polonius.stderr b/src/test/ui/hrtb/hrtb-perfect-forwarding.polonius.stderr index 558d643cde895..676a934569c47 100644 --- a/src/test/ui/hrtb/hrtb-perfect-forwarding.polonius.stderr +++ b/src/test/ui/hrtb/hrtb-perfect-forwarding.polonius.stderr @@ -39,6 +39,7 @@ LL | | // Not OK -- The forwarding impl for `Foo` requires that `Bar` also ... | LL | | foo_hrtb_bar_not(&mut t); | | ------------------------ recursive call site +LL | | LL | | } | |_^ cannot return without recursing | @@ -51,7 +52,7 @@ LL | foo_hrtb_bar_not(&mut t); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:49:1 + --> $DIR/hrtb-perfect-forwarding.rs:50:1 | LL | / fn foo_hrtb_bar_hrtb(mut t: T) LL | | where T : for<'a> Foo<&'a isize> + for<'b> Bar<&'b isize> diff --git a/src/test/ui/hrtb/issue-46989.stderr b/src/test/ui/hrtb/issue-46989.stderr index c818041e59632..0a7382c4dd818 100644 --- a/src/test/ui/hrtb/issue-46989.stderr +++ b/src/test/ui/hrtb/issue-46989.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `for<'r> fn(&'r i32): Foo` is not satisfied --> $DIR/issue-46989.rs:40:18 | LL | fn assert_foo() {} - | ---------- --- required by this bound in `assert_foo` + | --- required by this bound in `assert_foo` ... LL | assert_foo::(); | ^^^^^^^^ the trait `Foo` is not implemented for `for<'r> fn(&'r i32)` diff --git a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr index 759c7302d13c6..1c7bfa65d7cfe 100644 --- a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr +++ b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr @@ -6,7 +6,7 @@ LL | let v = Unit2.m( | = note: expected struct `Unit4` found associated type `<_ as Ty<'_>>::V` - = note: consider constraining the associated type `<_ as Ty<'_>>::V` to `Unit4` + = help: consider constraining the associated type `<_ as Ty<'_>>::V` to `Unit4` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0271]: type mismatch resolving `<[closure@$DIR/issue-62203-hrtb-ice.rs:42:17: 42:39] as std::ops::FnOnce<((&u8,),)>>::Output == Unit3` diff --git a/src/test/ui/hygiene/generic_params.stderr b/src/test/ui/hygiene/generic_params.stderr index b3e4fef08c20e..4ca6d1998353f 100644 --- a/src/test/ui/hygiene/generic_params.stderr +++ b/src/test/ui/hygiene/generic_params.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/generic_params.rs:6:37 | LL | #![feature(decl_macro, rustc_attrs, const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/hygiene/globs.stderr b/src/test/ui/hygiene/globs.stderr index 153ad8cbecfcd..8f6b7aca8fda8 100644 --- a/src/test/ui/hygiene/globs.stderr +++ b/src/test/ui/hygiene/globs.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find function `f` in this scope LL | f(); | ^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing one of these items | LL | use foo::f; | @@ -23,7 +23,7 @@ LL | | } | |_____- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use bar::g; | @@ -41,7 +41,7 @@ LL | n!(f); LL | n!(f); | ^ not found in this scope | - = note: possible candidate is found in another module, you can import it into scope: + = note: consider importing one of these items: foo::f = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -54,7 +54,7 @@ LL | n!(f); LL | f | ^ not found in this scope | - = note: possible candidate is found in another module, you can import it into scope: + = note: consider importing one of these items: foo::f = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/hygiene/hygienic-labels-in-let.stderr b/src/test/ui/hygiene/hygienic-labels-in-let.stderr index 7c82a08753aa2..3ff45a8a566bb 100644 --- a/src/test/ui/hygiene/hygienic-labels-in-let.stderr +++ b/src/test/ui/hygiene/hygienic-labels-in-let.stderr @@ -330,3 +330,5 @@ LL | run_once!(continue 'x); | = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +warning: 28 warnings emitted + diff --git a/src/test/ui/hygiene/hygienic-labels.stderr b/src/test/ui/hygiene/hygienic-labels.stderr index 960da15ef3c97..25098c25c82e3 100644 --- a/src/test/ui/hygiene/hygienic-labels.stderr +++ b/src/test/ui/hygiene/hygienic-labels.stderr @@ -330,3 +330,5 @@ LL | run_once!(continue 'x); | = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +warning: 28 warnings emitted + diff --git a/src/test/ui/hygiene/issue-61574-const-parameters.stderr b/src/test/ui/hygiene/issue-61574-const-parameters.stderr index c9aac6609a184..b351b8b73a0e5 100644 --- a/src/test/ui/hygiene/issue-61574-const-parameters.stderr +++ b/src/test/ui/hygiene/issue-61574-const-parameters.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-61574-const-parameters.rs:6:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/if-attrs/let-chains-attr.stderr b/src/test/ui/if-attrs/let-chains-attr.stderr index a6c91bb9203b3..8b9874715342c 100644 --- a/src/test/ui/if-attrs/let-chains-attr.stderr +++ b/src/test/ui/if-attrs/let-chains-attr.stderr @@ -1,8 +1,11 @@ -warning: the feature `let_chains` is incomplete and may cause the compiler to crash +warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/let-chains-attr.rs:3:12 | LL | #![feature(let_chains)] | ^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53667 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/if-ret.stderr b/src/test/ui/if-ret.stderr index 58cc707605423..41bbd791862be 100644 --- a/src/test/ui/if-ret.stderr +++ b/src/test/ui/if-ret.stderr @@ -8,3 +8,5 @@ LL | fn foo() { if (return) { } } | = note: `#[warn(unreachable_code)]` on by default +warning: 1 warning emitted + diff --git a/src/test/run-fail/expr-if-panic-fn.rs b/src/test/ui/if/expr-if-panic-fn.rs similarity index 80% rename from src/test/run-fail/expr-if-panic-fn.rs rename to src/test/ui/if/expr-if-panic-fn.rs index 660b1396e38a9..36e49785a49d0 100644 --- a/src/test/run-fail/expr-if-panic-fn.rs +++ b/src/test/ui/if/expr-if-panic-fn.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:explicit panic +// ignore-emscripten no processes fn f() -> ! { panic!() diff --git a/src/test/run-fail/expr-if-panic.rs b/src/test/ui/if/expr-if-panic.rs similarity index 76% rename from src/test/run-fail/expr-if-panic.rs rename to src/test/ui/if/expr-if-panic.rs index 36aaf459a56ee..520ee0870ee15 100644 --- a/src/test/run-fail/expr-if-panic.rs +++ b/src/test/ui/if/expr-if-panic.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:explicit panic +// ignore-emscripten no processes fn main() { let _x = if false { diff --git a/src/test/run-fail/if-check-panic.rs b/src/test/ui/if/if-check-panic.rs similarity index 87% rename from src/test/run-fail/if-check-panic.rs rename to src/test/ui/if/if-check-panic.rs index f9a4b8fcb38a5..037cd427ccf36 100644 --- a/src/test/run-fail/if-check-panic.rs +++ b/src/test/ui/if/if-check-panic.rs @@ -1,4 +1,7 @@ +// run-fail // error-pattern:Number is odd +// ignore-emscripten no processes + fn even(x: usize) -> bool { if x < 2 { return false; diff --git a/src/test/run-fail/if-cond-bot.rs b/src/test/ui/if/if-cond-bot.rs similarity index 75% rename from src/test/run-fail/if-cond-bot.rs rename to src/test/ui/if/if-cond-bot.rs index c680cad258f24..bcd114678528c 100644 --- a/src/test/run-fail/if-cond-bot.rs +++ b/src/test/ui/if/if-cond-bot.rs @@ -1,8 +1,12 @@ +// run-fail // error-pattern:quux +// ignore-emscripten no processes + fn my_err(s: String) -> ! { println!("{}", s); panic!("quux"); } + fn main() { if my_err("bye".to_string()) { } diff --git a/src/test/ui/if/if-let.rs b/src/test/ui/if/if-let.rs index 157eb3863203a..2ab0f9fed3fc6 100644 --- a/src/test/ui/if/if-let.rs +++ b/src/test/ui/if/if-let.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass fn macros() { macro_rules! foo{ diff --git a/src/test/ui/if/if-let.stderr b/src/test/ui/if/if-let.stderr index ad4aefb6e58a7..ee2b78af3b84d 100644 --- a/src/test/ui/if/if-let.stderr +++ b/src/test/ui/if/if-let.stderr @@ -63,3 +63,5 @@ LL | | println!("irrefutable pattern"); LL | | } | |_____^ +warning: 6 warnings emitted + diff --git a/src/test/ui/if/if-without-block.stderr b/src/test/ui/if/if-without-block.stderr index 34df8e3d77946..ee2bb62e2bb57 100644 --- a/src/test/ui/if/if-without-block.stderr +++ b/src/test/ui/if/if-without-block.stderr @@ -6,6 +6,8 @@ LL | if 5 == { ... LL | } | ^ expected `{` + | + = help: maybe you forgot the right operand of the condition? error: aborting due to previous error diff --git a/src/test/ui/impl-bounds-checking.stderr b/src/test/ui/impl-bounds-checking.stderr index b52f3d6b839d4..8698ed6e875f3 100644 --- a/src/test/ui/impl-bounds-checking.stderr +++ b/src/test/ui/impl-bounds-checking.stderr @@ -1,6 +1,9 @@ error[E0277]: the trait bound `isize: Clone2` is not satisfied --> $DIR/impl-bounds-checking.rs:10:6 | +LL | trait Getter { + | ------ required by this bound in `Getter` +... LL | impl Getter for isize { | ^^^^^^^^^^^^^ the trait `Clone2` is not implemented for `isize` diff --git a/src/test/ui/impl-trait-in-bindings.rs b/src/test/ui/impl-trait-in-bindings.rs index 2e9b6cd5c78d4..c7fae45d5ca2c 100644 --- a/src/test/ui/impl-trait-in-bindings.rs +++ b/src/test/ui/impl-trait-in-bindings.rs @@ -1,7 +1,7 @@ // run-pass #![feature(impl_trait_in_bindings)] -//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +//~^ WARN the feature `impl_trait_in_bindings` is incomplete use std::fmt::Debug; diff --git a/src/test/ui/impl-trait-in-bindings.stderr b/src/test/ui/impl-trait-in-bindings.stderr index 629089d8c5c5d..bf739d4722f68 100644 --- a/src/test/ui/impl-trait-in-bindings.stderr +++ b/src/test/ui/impl-trait-in-bindings.stderr @@ -1,8 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/impl-trait-in-bindings.rs:3:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/impl-trait/auto-trait-leak.stderr b/src/test/ui/impl-trait/auto-trait-leak.stderr index 3d60cbff3203f..164524be1bc41 100644 --- a/src/test/ui/impl-trait/auto-trait-leak.stderr +++ b/src/test/ui/impl-trait/auto-trait-leak.stderr @@ -258,7 +258,7 @@ error[E0277]: `std::rc::Rc` cannot be sent between threads --> $DIR/auto-trait-leak.rs:16:5 | LL | fn send(_: T) {} - | ---- ---- required by this bound in `send` + | ---- required by this bound in `send` ... LL | send(cycle2().clone()); | ^^^^ `std::rc::Rc` cannot be sent between threads safely diff --git a/src/test/ui/impl-trait/auto-trait-leak2.stderr b/src/test/ui/impl-trait/auto-trait-leak2.stderr index a93b3dbc71b60..b02ef7d4a5b13 100644 --- a/src/test/ui/impl-trait/auto-trait-leak2.stderr +++ b/src/test/ui/impl-trait/auto-trait-leak2.stderr @@ -5,7 +5,7 @@ LL | fn before() -> impl Fn(i32) { | ------------ within this `impl std::ops::Fn<(i32,)>` ... LL | fn send(_: T) {} - | ---- ---- required by this bound in `send` + | ---- required by this bound in `send` ... LL | send(before()); | ^^^^ `std::rc::Rc>` cannot be sent between threads safely @@ -18,7 +18,7 @@ error[E0277]: `std::rc::Rc>` cannot be sent between threads --> $DIR/auto-trait-leak2.rs:16:5 | LL | fn send(_: T) {} - | ---- ---- required by this bound in `send` + | ---- required by this bound in `send` ... LL | send(after()); | ^^^^ `std::rc::Rc>` cannot be sent between threads safely diff --git a/src/test/ui/impl-trait/bindings-opaque.rs b/src/test/ui/impl-trait/bindings-opaque.rs index d4eef29ed3207..d1f42be077dc8 100644 --- a/src/test/ui/impl-trait/bindings-opaque.rs +++ b/src/test/ui/impl-trait/bindings-opaque.rs @@ -1,5 +1,5 @@ #![feature(impl_trait_in_bindings)] -//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +//~^ WARN the feature `impl_trait_in_bindings` is incomplete const FOO: impl Copy = 42; diff --git a/src/test/ui/impl-trait/bindings-opaque.stderr b/src/test/ui/impl-trait/bindings-opaque.stderr index 1605f3434cf5a..6656968d79ae0 100644 --- a/src/test/ui/impl-trait/bindings-opaque.stderr +++ b/src/test/ui/impl-trait/bindings-opaque.stderr @@ -1,10 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/bindings-opaque.rs:1:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information error[E0599]: no method named `count_ones` found for opaque type `impl std::marker::Copy` in the current scope --> $DIR/bindings-opaque.rs:11:17 @@ -24,6 +25,6 @@ error[E0599]: no method named `count_ones` found for opaque type `impl std::mark LL | let _ = foo.count_ones(); | ^^^^^^^^^^ method not found in `impl std::marker::Copy` -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/impl-trait/bindings.rs b/src/test/ui/impl-trait/bindings.rs index 104a44d65662e..fd79ba68fbddb 100644 --- a/src/test/ui/impl-trait/bindings.rs +++ b/src/test/ui/impl-trait/bindings.rs @@ -1,5 +1,5 @@ #![feature(impl_trait_in_bindings)] -//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +//~^ WARN the feature `impl_trait_in_bindings` is incomplete fn a(x: T) { const foo: impl Clone = x; diff --git a/src/test/ui/impl-trait/bindings.stderr b/src/test/ui/impl-trait/bindings.stderr index e938595519784..e983fdecdba79 100644 --- a/src/test/ui/impl-trait/bindings.stderr +++ b/src/test/ui/impl-trait/bindings.stderr @@ -22,14 +22,15 @@ error[E0435]: attempt to use a non-constant value in a constant LL | const foo: impl Clone = x; | ^ non-constant value -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/bindings.rs:1:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0435`. diff --git a/src/test/ui/impl-trait/bound-normalization-fail.stderr b/src/test/ui/impl-trait/bound-normalization-fail.stderr index 22ba8342ff41f..36b4ebca4dfc5 100644 --- a/src/test/ui/impl-trait/bound-normalization-fail.stderr +++ b/src/test/ui/impl-trait/bound-normalization-fail.stderr @@ -1,10 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/bound-normalization-fail.rs:4:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information error[E0271]: type mismatch resolving ` as FooLike>::Output == ::Assoc` --> $DIR/bound-normalization-fail.rs:27:32 @@ -14,9 +15,11 @@ LL | fn foo_fail() -> impl FooLike { | = note: expected type `()` found associated type `::Assoc` - = note: consider constraining the associated type `::Assoc` to `()` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html = note: the return type of a function must have a statically known size +help: consider constraining the associated type `::Assoc` to `()` + | +LL | fn foo_fail>() -> impl FooLike { + | ^^^^^^^^^^^^ error: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope --> $DIR/bound-normalization-fail.rs:43:41 @@ -32,10 +35,12 @@ LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike { | = note: expected type `()` found associated type `>::Assoc` - = note: consider constraining the associated type `>::Assoc` to `()` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html = note: the return type of a function must have a statically known size +help: consider constraining the associated type `>::Assoc` to `()` + | +LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike { + | ^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/impl-trait/bound-normalization-pass.stderr b/src/test/ui/impl-trait/bound-normalization-pass.stderr index d048da7f60beb..afc181a906ac7 100644 --- a/src/test/ui/impl-trait/bound-normalization-pass.stderr +++ b/src/test/ui/impl-trait/bound-normalization-pass.stderr @@ -1,8 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/bound-normalization-pass.rs:5:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/impl-trait/does-not-live-long-enough.rs b/src/test/ui/impl-trait/does-not-live-long-enough.rs index 6179132b3f608..d2a345231eb43 100644 --- a/src/test/ui/impl-trait/does-not-live-long-enough.rs +++ b/src/test/ui/impl-trait/does-not-live-long-enough.rs @@ -4,7 +4,7 @@ struct List { impl List { fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator { self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref()) - //~^ ERROR does not live long enough + //~^ ERROR E0373 } } diff --git a/src/test/ui/impl-trait/does-not-live-long-enough.stderr b/src/test/ui/impl-trait/does-not-live-long-enough.stderr index 83d0f87015bf9..468c2f366299c 100644 --- a/src/test/ui/impl-trait/does-not-live-long-enough.stderr +++ b/src/test/ui/impl-trait/does-not-live-long-enough.stderr @@ -1,21 +1,21 @@ -error[E0597]: `prefix` does not live long enough - --> $DIR/does-not-live-long-enough.rs:6:51 +error[E0373]: closure may outlive the current function, but it borrows `prefix`, which is owned by the current function + --> $DIR/does-not-live-long-enough.rs:6:33 | -LL | fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator { - | -- lifetime `'a` defined here --------------------------- opaque type requires that `prefix` is borrowed for `'a` LL | self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref()) - | --- ^^^^^^ borrowed value does not live long enough + | ^^^ ------ `prefix` is borrowed here | | - | value captured here -LL | -LL | } - | - `prefix` dropped here while still borrowed + | may outlive borrowed value `prefix` + | +note: closure is returned here + --> $DIR/does-not-live-long-enough.rs:5:55 | -help: you can add a bound to the opaque type to make it last less than `'static` and match `'a` +LL | fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to force the closure to take ownership of `prefix` (and any other referenced variables), use the `move` keyword | -LL | fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | self.data.iter().filter(move |s| s.starts_with(prefix)).map(|s| s.as_ref()) + | ^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0597`. +For more information about this error, try `rustc --explain E0373`. diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs index 08bab5734fd7a..cbf1daabe2b4e 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs @@ -14,7 +14,7 @@ fn bap() -> Trait { Struct } //~^ ERROR E0746 fn ban() -> dyn Trait { Struct } //~^ ERROR E0746 -fn bak() -> dyn Trait { unimplemented!() } //~ ERROR E0277 +fn bak() -> dyn Trait { unimplemented!() } //~ ERROR E0746 // Suggest using `Box` fn bal() -> dyn Trait { //~ ERROR E0746 if true { @@ -26,7 +26,7 @@ fn bax() -> dyn Trait { //~ ERROR E0746 if true { Struct } else { - 42 + 42 //~ ERROR `if` and `else` have incompatible types } } fn bam() -> Box { diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index 664a897c593fc..c55dbd7d2fafe 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -49,7 +49,7 @@ LL | fn bap() -> Trait { Struct } | ^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: return `impl Trait` instead, as all return paths are of type `Struct`, which implements `Trait` +help: use `impl Trait` as the return type, as all return paths are of type `Struct`, which implements `Trait` | LL | fn bap() -> impl Trait { Struct } | ^^^^^^^^^^ @@ -61,20 +61,29 @@ LL | fn ban() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: return `impl Trait` instead, as all return paths are of type `Struct`, which implements `Trait` +help: use `impl Trait` as the return type, as all return paths are of type `Struct`, which implements `Trait` | LL | fn ban() -> impl Trait { Struct } | ^^^^^^^^^^ -error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time +error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:17:13 | LL | fn bak() -> dyn Trait { unimplemented!() } | ^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` - = note: to learn more, visit - = note: the return type of a function must have a statically known size +help: use some type `T` that is `T: Sized` as the return type if all return paths have the same type + | +LL | fn bak() -> T { unimplemented!() } + | ^ +help: use `impl Trait` as the return type if all return paths have the same type but you want to expose only the trait in the signature + | +LL | fn bak() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^ +help: use a boxed trait object if all return paths implement trait `Trait` + | +LL | fn bak() -> Box { unimplemented!() } + | ^^^^^^^^^^^^^^ error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13 @@ -95,6 +104,18 @@ LL | } LL | Box::new(42) | +error[E0308]: `if` and `else` have incompatible types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:29:9 + | +LL | / if true { +LL | | Struct + | | ------ expected because of this +LL | | } else { +LL | | 42 + | | ^^ expected struct `Struct`, found integer +LL | | } + | |_____- `if` and `else` have incompatible types + error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:25:13 | @@ -249,7 +270,7 @@ LL | fn bat() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: return `impl Trait` instead, as all return paths are of type `{integer}`, which implements `Trait` +help: use `impl Trait` as the return type, as all return paths are of type `{integer}`, which implements `Trait` | LL | fn bat() -> impl Trait { | ^^^^^^^^^^ @@ -261,12 +282,12 @@ LL | fn bay() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: return `impl Trait` instead, as all return paths are of type `{integer}`, which implements `Trait` +help: use `impl Trait` as the return type, as all return paths are of type `{integer}`, which implements `Trait` | LL | fn bay() -> impl Trait { | ^^^^^^^^^^ -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors Some errors have detailed explanations: E0277, E0308, E0746. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr b/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr index eb064b4e14a5e..51247f1d7b07a 100644 --- a/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr +++ b/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr @@ -6,3 +6,5 @@ LL | fn equal_regions_static<'a: 'static>(x: &'a i32) -> impl Sized { | = help: you can use the `'static` lifetime directly, in place of `'a` +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/equality2.stderr b/src/test/ui/impl-trait/equality2.stderr index b882514f61609..2454c218ffc8b 100644 --- a/src/test/ui/impl-trait/equality2.stderr +++ b/src/test/ui/impl-trait/equality2.stderr @@ -25,7 +25,7 @@ LL | let _: i32 = Leak::leak(hide(0_i32)); | = note: expected type `i32` found associated type `::T` - = note: consider constraining the associated type `::T` to `i32` + = help: consider constraining the associated type `::T` to `i32` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types diff --git a/src/test/ui/impl-trait/example-calendar.rs b/src/test/ui/impl-trait/example-calendar.rs index f1b1656745e7c..fafab8a102a90 100644 --- a/src/test/ui/impl-trait/example-calendar.rs +++ b/src/test/ui/impl-trait/example-calendar.rs @@ -2,6 +2,7 @@ #![feature(fn_traits, step_trait, + step_trait_ext, unboxed_closures, )] @@ -10,7 +11,6 @@ //! Originally converted to Rust by [Daniel Keep](https://github.com/DanielKeep). use std::fmt::Write; -use std::mem; /// Date representation. #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -156,32 +156,16 @@ impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate { } } -impl std::iter::Step for NaiveDate { +unsafe impl std::iter::Step for NaiveDate { fn steps_between(_: &Self, _: &Self) -> Option { unimplemented!() } - fn replace_one(&mut self) -> Self { - mem::replace(self, NaiveDate(0, 0, 1)) + fn forward_checked(start: Self, n: usize) -> Option { + Some((0..n).fold(start, |x, _| x.succ())) } - fn replace_zero(&mut self) -> Self { - mem::replace(self, NaiveDate(0, 0, 0)) - } - - fn add_one(&self) -> Self { - self.succ() - } - - fn sub_one(&self) -> Self { - unimplemented!() - } - - fn add_usize(&self, _: usize) -> Option { - unimplemented!() - } - - fn sub_usize(&self, _: usize) -> Option { + fn backward_checked(_: Self, _: usize) -> Option { unimplemented!() } } diff --git a/src/test/ui/impl-trait/impl-generic-mismatch.rs b/src/test/ui/impl-trait/impl-generic-mismatch.rs index 615dd6d2f6f13..ba678bb032dcb 100644 --- a/src/test/ui/impl-trait/impl-generic-mismatch.rs +++ b/src/test/ui/impl-trait/impl-generic-mismatch.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - use std::fmt::Debug; trait Foo { diff --git a/src/test/ui/impl-trait/impl-generic-mismatch.stderr b/src/test/ui/impl-trait/impl-generic-mismatch.stderr index 0c294d1e485a8..8d8daa063e094 100644 --- a/src/test/ui/impl-trait/impl-generic-mismatch.stderr +++ b/src/test/ui/impl-trait/impl-generic-mismatch.stderr @@ -1,5 +1,5 @@ error[E0643]: method `foo` has incompatible signature for trait - --> $DIR/impl-generic-mismatch.rs:13:12 + --> $DIR/impl-generic-mismatch.rs:8:12 | LL | fn foo(&self, _: &impl Debug); | ---------- declaration in trait here @@ -13,7 +13,7 @@ LL | fn foo(&self, _: &impl Debug) { } | -- ^^^^^^^^^^ error[E0643]: method `bar` has incompatible signature for trait - --> $DIR/impl-generic-mismatch.rs:22:23 + --> $DIR/impl-generic-mismatch.rs:17:23 | LL | fn bar(&self, _: &U); | - declaration in trait here @@ -27,7 +27,7 @@ LL | fn bar(&self, _: &U) { } | ^^^^^^^^^^ ^ error[E0643]: method `hash` has incompatible signature for trait - --> $DIR/impl-generic-mismatch.rs:33:33 + --> $DIR/impl-generic-mismatch.rs:28:33 | LL | fn hash(&self, hasher: &mut impl Hasher) {} | ^^^^^^^^^^^ expected generic parameter, found `impl Trait` diff --git a/src/test/ui/impl-trait/issue-55872-1.stderr b/src/test/ui/impl-trait/issue-55872-1.stderr index d62b8b1c253ea..6cb2c9fb892f3 100644 --- a/src/test/ui/impl-trait/issue-55872-1.stderr +++ b/src/test/ui/impl-trait/issue-55872-1.stderr @@ -4,13 +4,12 @@ error[E0277]: the trait bound `S: std::marker::Copy` is not satisfied in `(S, T) LL | type E = impl Copy; | ^^^^^^^^^^^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `S` | -help: consider further restricting this bound with `+ std::marker::Copy` - --> $DIR/issue-55872-1.rs:11:9 - | -LL | impl Bar for S { - | ^^^^^^^ = note: required because it appears within the type `(S, T)` = note: the return type of a function must have a statically known size +help: consider further restricting this bound + | +LL | impl Bar for S { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied in `(S, T)` --> $DIR/issue-55872-1.rs:12:5 @@ -18,13 +17,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied in `(S, T) LL | type E = impl Copy; | ^^^^^^^^^^^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `T` | -help: consider further restricting this bound with `+ std::marker::Copy` - --> $DIR/issue-55872-1.rs:16:15 - | -LL | fn foo() -> Self::E { - | ^^^^^^^ = note: required because it appears within the type `(S, T)` = note: the return type of a function must have a statically known size +help: consider further restricting this bound + | +LL | fn foo() -> Self::E { + | ^^^^^^^^^^^^^^^^^^^ error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias --> $DIR/issue-55872-1.rs:16:37 diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr index 72e8fa33d7b4d..6ce3aaf49eb33 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr @@ -1,10 +1,13 @@ error: lifetime may not live long enough - --> $DIR/error-handling.rs:13:56 + --> $DIR/error-handling.rs:23:16 | LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { - | -- -- lifetime `'b` defined here ^^^^^^^^^ opaque type requires that `'a` must outlive `'b` + | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here +... +LL | let _: &'b i32 = *u.0; + | ^^^^^^^ type annotation requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr index 5bfc446f6a573..129af80ce4a62 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr @@ -4,7 +4,7 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> | ^^^^^^^^^^^^^^^^^^ | - = note: hidden type `Ordinary<'_>` captures lifetime '_#8r + = note: hidden type `Ordinary<'_>` captures lifetime '_#9r error: aborting due to previous error diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr index 7291eee7b9e88..de6d5edcae511 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr @@ -4,7 +4,7 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> | ^^^^^^^^^^^^^^^^^^ | - = note: hidden type `Ordinary<'_>` captures lifetime '_#5r + = note: hidden type `Ordinary<'_>` captures lifetime '_#6r error: aborting due to previous error diff --git a/src/test/ui/impl-trait/universal-mismatched-type.stderr b/src/test/ui/impl-trait/universal-mismatched-type.stderr index 3ffa2b55712eb..a12b01b4d2b0d 100644 --- a/src/test/ui/impl-trait/universal-mismatched-type.stderr +++ b/src/test/ui/impl-trait/universal-mismatched-type.stderr @@ -10,8 +10,6 @@ LL | x | = note: expected struct `std::string::String` found type parameter `impl Debug` - = 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 previous error diff --git a/src/test/ui/impl-trait/universal_wrong_bounds.stderr b/src/test/ui/impl-trait/universal_wrong_bounds.stderr index 32b638dc465c5..3b1a5e5f4ad00 100644 --- a/src/test/ui/impl-trait/universal_wrong_bounds.stderr +++ b/src/test/ui/impl-trait/universal_wrong_bounds.stderr @@ -4,7 +4,7 @@ error[E0404]: expected trait, found derive macro `Debug` LL | fn wants_debug(g: impl Debug) { } | ^^^^^ not a trait | -help: possible better candidate is found in another module, you can import it into scope +help: consider importing this trait instead | LL | use std::fmt::Debug; | @@ -15,7 +15,7 @@ error[E0404]: expected trait, found derive macro `Debug` LL | fn wants_display(g: impl Debug) { } | ^^^^^ not a trait | -help: possible better candidate is found in another module, you can import it into scope +help: consider importing this trait instead | LL | use std::fmt::Debug; | diff --git a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.rs b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.rs index 8c23def95b895..6ff3ab73639c0 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.rs +++ b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // aux-build:two_macros.rs macro_rules! define_vec { diff --git a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr index e344d0591473d..3269945a252eb 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr +++ b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr @@ -1,5 +1,5 @@ error: macro-expanded `extern crate` items cannot shadow names passed with `--extern` - --> $DIR/extern-prelude-extern-crate-restricted-shadowing.rs:23:9 + --> $DIR/extern-prelude-extern-crate-restricted-shadowing.rs:19:9 | LL | extern crate std as core; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -10,13 +10,13 @@ LL | define_other_core!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0659]: `Vec` is ambiguous (macro-expanded name vs less macro-expanded name from outer scope during import/macro resolution) - --> $DIR/extern-prelude-extern-crate-restricted-shadowing.rs:17:9 + --> $DIR/extern-prelude-extern-crate-restricted-shadowing.rs:13:9 | LL | Vec::panic!(); | ^^^ ambiguous name | note: `Vec` could refer to the crate imported here - --> $DIR/extern-prelude-extern-crate-restricted-shadowing.rs:9:9 + --> $DIR/extern-prelude-extern-crate-restricted-shadowing.rs:5:9 | LL | extern crate std as Vec; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/imports/glob-use-std.rs b/src/test/ui/imports/glob-use-std.rs new file mode 100644 index 0000000000000..ef06cc570d557 --- /dev/null +++ b/src/test/ui/imports/glob-use-std.rs @@ -0,0 +1,11 @@ +// Issue #7580 + +// run-fail +// error-pattern:panic works +// ignore-emscripten no processes + +use std::*; + +fn main() { + panic!("panic works") +} diff --git a/src/test/ui/imports/import-in-block.rs b/src/test/ui/imports/import-in-block.rs index c0ba6220b5443..19703904ece91 100644 --- a/src/test/ui/imports/import-in-block.rs +++ b/src/test/ui/imports/import-in-block.rs @@ -4,7 +4,7 @@ pub fn main() { use std::mem::replace; let mut x = 5; - replace(&mut x, 6); + let _ = replace(&mut x, 6); { use std::mem::*; let mut y = 6; diff --git a/src/test/ui/imports/reexports.stderr b/src/test/ui/imports/reexports.stderr index 79c8e1130c87a..8cbff0ac73dee 100644 --- a/src/test/ui/imports/reexports.stderr +++ b/src/test/ui/imports/reexports.stderr @@ -56,7 +56,7 @@ note: the lint level is defined here LL | #![warn(unused_imports)] | ^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 1 warning emitted Some errors have detailed explanations: E0364, E0603. For more information about an error, try `rustc --explain E0364`. diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.rs b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.rs index b07a7f107da23..1b524ec3833e8 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.rs +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::ops::Deref; trait Trait {} diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index 374e308a639ce..c1c4ec9ed7b92 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -1,5 +1,5 @@ error: `impl` item signature doesn't match `trait` item signature - --> $DIR/mismatched_trait_impl-2.rs:12:5 + --> $DIR/mismatched_trait_impl-2.rs:8:5 | LL | fn deref(&self) -> &dyn Trait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&Struct) -> &dyn Trait diff --git a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs index 7d75f254bfe75..2e96022318b47 100644 --- a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs +++ b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs @@ -1,6 +1,6 @@ // edition:2018 #![feature(impl_trait_in_bindings)] -//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +//~^ WARN the feature `impl_trait_in_bindings` is incomplete use std::io::Error; diff --git a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr index f67e45b01d27e..89a22f5e5d635 100644 --- a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr +++ b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr @@ -1,10 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/cannot-infer-async-enabled-impl-trait-bindings.rs:2:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information error[E0282]: type annotations needed for `impl std::future::Future` --> $DIR/cannot-infer-async-enabled-impl-trait-bindings.rs:13:9 @@ -14,6 +15,6 @@ LL | let fut = async { LL | make_unit()?; | ^^^^^^^^^^^^ cannot infer type -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/inference/inference-variable-behind-raw-pointer.stderr b/src/test/ui/inference/inference-variable-behind-raw-pointer.stderr index b5b885e233fe0..12848982b8d28 100644 --- a/src/test/ui/inference/inference-variable-behind-raw-pointer.stderr +++ b/src/test/ui/inference/inference-variable-behind-raw-pointer.stderr @@ -8,3 +8,5 @@ LL | if data.is_null() {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! = note: for more information, see issue #46906 +warning: 1 warning emitted + diff --git a/src/test/ui/inference/inference_unstable.stderr b/src/test/ui/inference/inference_unstable.stderr index 1f5cc8b13fb46..df52012463255 100644 --- a/src/test/ui/inference/inference_unstable.stderr +++ b/src/test/ui/inference/inference_unstable.stderr @@ -10,3 +10,5 @@ LL | assert_eq!('x'.ipu_flatten(), 1); = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method = help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten` +warning: 1 warning emitted + diff --git a/src/test/ui/inference/inference_unstable_featured.stderr b/src/test/ui/inference/inference_unstable_featured.stderr index fa908440e41ea..e23b934ac187f 100644 --- a/src/test/ui/inference/inference_unstable_featured.stderr +++ b/src/test/ui/inference/inference_unstable_featured.stderr @@ -6,11 +6,11 @@ LL | assert_eq!('x'.ipu_flatten(), 0); | = note: candidate #1 is defined in an impl of the trait `inference_unstable_iterator::IpuIterator` for the type `char` = note: candidate #2 is defined in an impl of the trait `inference_unstable_itertools::IpuItertools` for the type `char` -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | assert_eq!(inference_unstable_iterator::IpuIterator::ipu_flatten(&'x'), 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | assert_eq!(inference_unstable_itertools::IpuItertools::ipu_flatten(&'x'), 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/inline-asm-bad-constraint.rs b/src/test/ui/inline-asm-bad-constraint.rs index 04fd5760cf8ce..edf2c2e3180e8 100644 --- a/src/test/ui/inline-asm-bad-constraint.rs +++ b/src/test/ui/inline-asm-bad-constraint.rs @@ -3,7 +3,7 @@ // build-fail // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] extern "C" { fn foo(a: usize); @@ -19,7 +19,7 @@ fn main() { fn bad_register_constraint() { let rax: u64; unsafe { - asm!("" :"={rax"(rax)) //~ ERROR E0668 + llvm_asm!("" :"={rax"(rax)) //~ ERROR E0668 }; println!("Accumulator is: {}", rax); } @@ -27,14 +27,14 @@ fn bad_register_constraint() { // Issue #54376 fn bad_input() { unsafe { - asm!("callq $0" : : "0"(foo)) //~ ERROR E0668 + llvm_asm!("callq $0" : : "0"(foo)) //~ ERROR E0668 }; } fn wrong_size_output() { let rax: u64 = 0; unsafe { - asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668 + llvm_asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668 } println!("rax: {}", rax); } diff --git a/src/test/ui/inline-asm-bad-constraint.stderr b/src/test/ui/inline-asm-bad-constraint.stderr index 2647e337b9d53..d6a3b6e8382f4 100644 --- a/src/test/ui/inline-asm-bad-constraint.stderr +++ b/src/test/ui/inline-asm-bad-constraint.stderr @@ -1,24 +1,24 @@ error[E0668]: malformed inline assembly --> $DIR/inline-asm-bad-constraint.rs:22:9 | -LL | asm!("" :"={rax"(rax)) - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | llvm_asm!("" :"={rax"(rax)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0668]: malformed inline assembly --> $DIR/inline-asm-bad-constraint.rs:30:9 | -LL | asm!("callq $0" : : "0"(foo)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | llvm_asm!("callq $0" : : "0"(foo)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0668]: malformed inline assembly --> $DIR/inline-asm-bad-constraint.rs:37:9 | -LL | asm!("addb $1, $0" : "={rax}"((0i32, rax))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | llvm_asm!("addb $1, $0" : "={rax}"((0i32, rax))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/inline-asm-bad-operand.rs b/src/test/ui/inline-asm-bad-operand.rs index f4e9922164f03..e5fc4ee010678 100644 --- a/src/test/ui/inline-asm-bad-operand.rs +++ b/src/test/ui/inline-asm-bad-operand.rs @@ -4,7 +4,7 @@ // build-fail // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] #[repr(C)] struct MyPtr(usize); @@ -19,41 +19,41 @@ fn main() { fn issue_37433() { unsafe { - asm!("" :: "r"("")); //~ ERROR E0669 + llvm_asm!("" :: "r"("")); //~ ERROR E0669 } unsafe { let target = MyPtr(0); - asm!("ret" : : "{rdi}"(target)); //~ ERROR E0669 + llvm_asm!("ret" : : "{rdi}"(target)); //~ ERROR E0669 } } fn issue_37437() { let hello: &str = "hello"; // this should fail... - unsafe { asm!("" :: "i"(hello)) }; //~ ERROR E0669 + unsafe { llvm_asm!("" :: "i"(hello)) }; //~ ERROR E0669 // but this should succeed. - unsafe { asm!("" :: "r"(hello.as_ptr())) }; + unsafe { llvm_asm!("" :: "r"(hello.as_ptr())) }; } fn issue_40187() { let arr: [u8; 1] = [0; 1]; unsafe { - asm!("movups $1, %xmm0"::"m"(arr)); //~ ERROR E0669 + llvm_asm!("movups $1, %xmm0"::"m"(arr)); //~ ERROR E0669 } } fn issue_54067() { let addr: Option = Some(123); unsafe { - asm!("mov sp, $0"::"r"(addr)); //~ ERROR E0669 + llvm_asm!("mov sp, $0"::"r"(addr)); //~ ERROR E0669 } } fn multiple_errors() { let addr: (u32, u32) = (1, 2); unsafe { - asm!("mov sp, $0"::"r"(addr), //~ ERROR E0669 - "r"("hello e0669")); //~ ERROR E0669 + llvm_asm!("mov sp, $0"::"r"(addr), //~ ERROR E0669 + "r"("hello e0669")); //~ ERROR E0669 } } diff --git a/src/test/ui/inline-asm-bad-operand.stderr b/src/test/ui/inline-asm-bad-operand.stderr index fe6c6c9914199..1ac7024ec8bf0 100644 --- a/src/test/ui/inline-asm-bad-operand.stderr +++ b/src/test/ui/inline-asm-bad-operand.stderr @@ -1,41 +1,41 @@ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/inline-asm-bad-operand.rs:22:24 + --> $DIR/inline-asm-bad-operand.rs:22:29 | -LL | asm!("" :: "r"("")); - | ^^ +LL | llvm_asm!("" :: "r"("")); + | ^^ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/inline-asm-bad-operand.rs:27:32 + --> $DIR/inline-asm-bad-operand.rs:27:37 | -LL | asm!("ret" : : "{rdi}"(target)); - | ^^^^^^ +LL | llvm_asm!("ret" : : "{rdi}"(target)); + | ^^^^^^ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/inline-asm-bad-operand.rs:34:29 + --> $DIR/inline-asm-bad-operand.rs:34:34 | -LL | unsafe { asm!("" :: "i"(hello)) }; - | ^^^^^ +LL | unsafe { llvm_asm!("" :: "i"(hello)) }; + | ^^^^^ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/inline-asm-bad-operand.rs:42:38 + --> $DIR/inline-asm-bad-operand.rs:42:43 | -LL | asm!("movups $1, %xmm0"::"m"(arr)); - | ^^^ +LL | llvm_asm!("movups $1, %xmm0"::"m"(arr)); + | ^^^ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/inline-asm-bad-operand.rs:49:32 + --> $DIR/inline-asm-bad-operand.rs:49:37 | -LL | asm!("mov sp, $0"::"r"(addr)); - | ^^^^ +LL | llvm_asm!("mov sp, $0"::"r"(addr)); + | ^^^^ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/inline-asm-bad-operand.rs:56:32 + --> $DIR/inline-asm-bad-operand.rs:56:37 | -LL | asm!("mov sp, $0"::"r"(addr), - | ^^^^ +LL | llvm_asm!("mov sp, $0"::"r"(addr), + | ^^^^ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/inline-asm-bad-operand.rs:57:32 + --> $DIR/inline-asm-bad-operand.rs:57:37 | LL | ... "r"("hello e0669")); | ^^^^^^^^^^^^^ diff --git a/src/test/ui/integer-literal-suffix-inference.rs b/src/test/ui/integer-literal-suffix-inference.rs index 3f4bedc4c2224..c320f2bb7b446 100644 --- a/src/test/ui/integer-literal-suffix-inference.rs +++ b/src/test/ui/integer-literal-suffix-inference.rs @@ -16,6 +16,7 @@ fn main() { fn id_i16(n: i16) -> i16 { n } fn id_i32(n: i32) -> i32 { n } fn id_i64(n: i64) -> i64 { n } + fn id_isize(n: isize) -> isize { n } // the smallest values that need these types let b8: u8 = 16; @@ -27,6 +28,11 @@ fn main() { fn id_u16(n: u16) -> u16 { n } fn id_u32(n: u32) -> u32 { n } fn id_u64(n: u64) -> u64 { n } + fn id_usize(n: usize) -> usize { n } + + // Values for testing *size + let asize: isize = 1; + let bsize: usize = 3; id_i8(a8); // ok id_i8(a16); @@ -38,6 +44,9 @@ fn main() { id_i8(a64); //~^ ERROR mismatched types //~| expected `i8`, found `i64` + id_i8(asize); + //~^ ERROR mismatched types + //~| expected `i8`, found `isize` id_i16(a8); //~^ ERROR mismatched types @@ -49,6 +58,9 @@ fn main() { id_i16(a64); //~^ ERROR mismatched types //~| expected `i16`, found `i64` + id_i16(asize); + //~^ ERROR mismatched types + //~| expected `i16`, found `isize` id_i32(a8); //~^ ERROR mismatched types @@ -60,6 +72,9 @@ fn main() { id_i32(a64); //~^ ERROR mismatched types //~| expected `i32`, found `i64` + id_i32(asize); + //~^ ERROR mismatched types + //~| expected `i32`, found `isize` id_i64(a8); //~^ ERROR mismatched types @@ -71,6 +86,23 @@ fn main() { //~^ ERROR mismatched types //~| expected `i64`, found `i32` id_i64(a64); // ok + id_i64(asize); + //~^ ERROR mismatched types + //~| expected `i64`, found `isize` + + id_isize(a8); + //~^ ERROR mismatched types + //~| expected `isize`, found `i8` + id_isize(a16); + //~^ ERROR mismatched types + //~| expected `isize`, found `i16` + id_isize(a32); + //~^ ERROR mismatched types + //~| expected `isize`, found `i32` + id_isize(a64); + //~^ ERROR mismatched types + //~| expected `isize`, found `i64` + id_isize(asize); //ok id_i8(c8); // ok id_i8(c16); @@ -126,6 +158,9 @@ fn main() { id_u8(b64); //~^ ERROR mismatched types //~| expected `u8`, found `u64` + id_u8(bsize); + //~^ ERROR mismatched types + //~| expected `u8`, found `usize` id_u16(b8); //~^ ERROR mismatched types @@ -137,6 +172,9 @@ fn main() { id_u16(b64); //~^ ERROR mismatched types //~| expected `u16`, found `u64` + id_u16(bsize); + //~^ ERROR mismatched types + //~| expected `u16`, found `usize` id_u32(b8); //~^ ERROR mismatched types @@ -148,6 +186,9 @@ fn main() { id_u32(b64); //~^ ERROR mismatched types //~| expected `u32`, found `u64` + id_u32(bsize); + //~^ ERROR mismatched types + //~| expected `u32`, found `usize` id_u64(b8); //~^ ERROR mismatched types @@ -159,4 +200,21 @@ fn main() { //~^ ERROR mismatched types //~| expected `u64`, found `u32` id_u64(b64); // ok + id_u64(bsize); + //~^ ERROR mismatched types + //~| expected `u64`, found `usize` + + id_usize(b8); + //~^ ERROR mismatched types + //~| expected `usize`, found `u8` + id_usize(b16); + //~^ ERROR mismatched types + //~| expected `usize`, found `u16` + id_usize(b32); + //~^ ERROR mismatched types + //~| expected `usize`, found `u32` + id_usize(b64); + //~^ ERROR mismatched types + //~| expected `usize`, found `u64` + id_usize(bsize); //ok } diff --git a/src/test/ui/integer-literal-suffix-inference.stderr b/src/test/ui/integer-literal-suffix-inference.stderr index a34f0645c6b97..b8502768e1d42 100644 --- a/src/test/ui/integer-literal-suffix-inference.stderr +++ b/src/test/ui/integer-literal-suffix-inference.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:32:11 + --> $DIR/integer-literal-suffix-inference.rs:38:11 | LL | id_i8(a16); | ^^^ expected `i8`, found `i16` @@ -10,7 +10,7 @@ LL | id_i8(a16.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:35:11 + --> $DIR/integer-literal-suffix-inference.rs:41:11 | LL | id_i8(a32); | ^^^ expected `i8`, found `i32` @@ -21,7 +21,7 @@ LL | id_i8(a32.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:38:11 + --> $DIR/integer-literal-suffix-inference.rs:44:11 | LL | id_i8(a64); | ^^^ expected `i8`, found `i64` @@ -32,7 +32,18 @@ LL | id_i8(a64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:42:12 + --> $DIR/integer-literal-suffix-inference.rs:47:11 + | +LL | id_i8(asize); + | ^^^^^ expected `i8`, found `isize` + | +help: you can convert an `isize` to `i8` and panic if the converted value wouldn't fit + | +LL | id_i8(asize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:51:12 | LL | id_i16(a8); | ^^ @@ -41,7 +52,7 @@ LL | id_i16(a8); | help: you can convert an `i8` to `i16`: `a8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:46:12 + --> $DIR/integer-literal-suffix-inference.rs:55:12 | LL | id_i16(a32); | ^^^ expected `i16`, found `i32` @@ -52,7 +63,7 @@ LL | id_i16(a32.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:49:12 + --> $DIR/integer-literal-suffix-inference.rs:58:12 | LL | id_i16(a64); | ^^^ expected `i16`, found `i64` @@ -63,7 +74,18 @@ LL | id_i16(a64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:53:12 + --> $DIR/integer-literal-suffix-inference.rs:61:12 + | +LL | id_i16(asize); + | ^^^^^ expected `i16`, found `isize` + | +help: you can convert an `isize` to `i16` and panic if the converted value wouldn't fit + | +LL | id_i16(asize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:65:12 | LL | id_i32(a8); | ^^ @@ -72,7 +94,7 @@ LL | id_i32(a8); | help: you can convert an `i8` to `i32`: `a8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:56:12 + --> $DIR/integer-literal-suffix-inference.rs:68:12 | LL | id_i32(a16); | ^^^ @@ -81,7 +103,7 @@ LL | id_i32(a16); | help: you can convert an `i16` to `i32`: `a16.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:60:12 + --> $DIR/integer-literal-suffix-inference.rs:72:12 | LL | id_i32(a64); | ^^^ expected `i32`, found `i64` @@ -92,7 +114,18 @@ LL | id_i32(a64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:64:12 + --> $DIR/integer-literal-suffix-inference.rs:75:12 + | +LL | id_i32(asize); + | ^^^^^ expected `i32`, found `isize` + | +help: you can convert an `isize` to `i32` and panic if the converted value wouldn't fit + | +LL | id_i32(asize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:79:12 | LL | id_i64(a8); | ^^ @@ -101,7 +134,7 @@ LL | id_i64(a8); | help: you can convert an `i8` to `i64`: `a8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:67:12 + --> $DIR/integer-literal-suffix-inference.rs:82:12 | LL | id_i64(a16); | ^^^ @@ -110,7 +143,7 @@ LL | id_i64(a16); | help: you can convert an `i16` to `i64`: `a16.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:70:12 + --> $DIR/integer-literal-suffix-inference.rs:85:12 | LL | id_i64(a32); | ^^^ @@ -119,7 +152,58 @@ LL | id_i64(a32); | help: you can convert an `i32` to `i64`: `a32.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:76:11 + --> $DIR/integer-literal-suffix-inference.rs:89:12 + | +LL | id_i64(asize); + | ^^^^^ expected `i64`, found `isize` + | +help: you can convert an `isize` to `i64` and panic if the converted value wouldn't fit + | +LL | id_i64(asize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:93:14 + | +LL | id_isize(a8); + | ^^ + | | + | expected `isize`, found `i8` + | help: you can convert an `i8` to `isize`: `a8.into()` + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:96:14 + | +LL | id_isize(a16); + | ^^^ + | | + | expected `isize`, found `i16` + | help: you can convert an `i16` to `isize`: `a16.into()` + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:99:14 + | +LL | id_isize(a32); + | ^^^ expected `isize`, found `i32` + | +help: you can convert an `i32` to `isize` and panic if the converted value wouldn't fit + | +LL | id_isize(a32.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:102:14 + | +LL | id_isize(a64); + | ^^^ expected `isize`, found `i64` + | +help: you can convert an `i64` to `isize` and panic if the converted value wouldn't fit + | +LL | id_isize(a64.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:108:11 | LL | id_i8(c16); | ^^^ expected `i8`, found `i16` @@ -130,7 +214,7 @@ LL | id_i8(c16.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:79:11 + --> $DIR/integer-literal-suffix-inference.rs:111:11 | LL | id_i8(c32); | ^^^ expected `i8`, found `i32` @@ -141,7 +225,7 @@ LL | id_i8(c32.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:82:11 + --> $DIR/integer-literal-suffix-inference.rs:114:11 | LL | id_i8(c64); | ^^^ expected `i8`, found `i64` @@ -152,7 +236,7 @@ LL | id_i8(c64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:86:12 + --> $DIR/integer-literal-suffix-inference.rs:118:12 | LL | id_i16(c8); | ^^ @@ -161,7 +245,7 @@ LL | id_i16(c8); | help: you can convert an `i8` to `i16`: `c8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:90:12 + --> $DIR/integer-literal-suffix-inference.rs:122:12 | LL | id_i16(c32); | ^^^ expected `i16`, found `i32` @@ -172,7 +256,7 @@ LL | id_i16(c32.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:93:12 + --> $DIR/integer-literal-suffix-inference.rs:125:12 | LL | id_i16(c64); | ^^^ expected `i16`, found `i64` @@ -183,7 +267,7 @@ LL | id_i16(c64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:97:12 + --> $DIR/integer-literal-suffix-inference.rs:129:12 | LL | id_i32(c8); | ^^ @@ -192,7 +276,7 @@ LL | id_i32(c8); | help: you can convert an `i8` to `i32`: `c8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:100:12 + --> $DIR/integer-literal-suffix-inference.rs:132:12 | LL | id_i32(c16); | ^^^ @@ -201,7 +285,7 @@ LL | id_i32(c16); | help: you can convert an `i16` to `i32`: `c16.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:104:12 + --> $DIR/integer-literal-suffix-inference.rs:136:12 | LL | id_i32(c64); | ^^^ expected `i32`, found `i64` @@ -212,7 +296,7 @@ LL | id_i32(c64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:108:12 + --> $DIR/integer-literal-suffix-inference.rs:140:12 | LL | id_i64(a8); | ^^ @@ -221,7 +305,7 @@ LL | id_i64(a8); | help: you can convert an `i8` to `i64`: `a8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:111:12 + --> $DIR/integer-literal-suffix-inference.rs:143:12 | LL | id_i64(a16); | ^^^ @@ -230,7 +314,7 @@ LL | id_i64(a16); | help: you can convert an `i16` to `i64`: `a16.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:114:12 + --> $DIR/integer-literal-suffix-inference.rs:146:12 | LL | id_i64(a32); | ^^^ @@ -239,7 +323,7 @@ LL | id_i64(a32); | help: you can convert an `i32` to `i64`: `a32.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:120:11 + --> $DIR/integer-literal-suffix-inference.rs:152:11 | LL | id_u8(b16); | ^^^ expected `u8`, found `u16` @@ -250,7 +334,7 @@ LL | id_u8(b16.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:123:11 + --> $DIR/integer-literal-suffix-inference.rs:155:11 | LL | id_u8(b32); | ^^^ expected `u8`, found `u32` @@ -261,7 +345,7 @@ LL | id_u8(b32.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:126:11 + --> $DIR/integer-literal-suffix-inference.rs:158:11 | LL | id_u8(b64); | ^^^ expected `u8`, found `u64` @@ -272,7 +356,18 @@ LL | id_u8(b64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:130:12 + --> $DIR/integer-literal-suffix-inference.rs:161:11 + | +LL | id_u8(bsize); + | ^^^^^ expected `u8`, found `usize` + | +help: you can convert an `usize` to `u8` and panic if the converted value wouldn't fit + | +LL | id_u8(bsize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:165:12 | LL | id_u16(b8); | ^^ @@ -281,7 +376,7 @@ LL | id_u16(b8); | help: you can convert an `u8` to `u16`: `b8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:134:12 + --> $DIR/integer-literal-suffix-inference.rs:169:12 | LL | id_u16(b32); | ^^^ expected `u16`, found `u32` @@ -292,7 +387,7 @@ LL | id_u16(b32.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:137:12 + --> $DIR/integer-literal-suffix-inference.rs:172:12 | LL | id_u16(b64); | ^^^ expected `u16`, found `u64` @@ -303,7 +398,18 @@ LL | id_u16(b64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:141:12 + --> $DIR/integer-literal-suffix-inference.rs:175:12 + | +LL | id_u16(bsize); + | ^^^^^ expected `u16`, found `usize` + | +help: you can convert an `usize` to `u16` and panic if the converted value wouldn't fit + | +LL | id_u16(bsize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:179:12 | LL | id_u32(b8); | ^^ @@ -312,7 +418,7 @@ LL | id_u32(b8); | help: you can convert an `u8` to `u32`: `b8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:144:12 + --> $DIR/integer-literal-suffix-inference.rs:182:12 | LL | id_u32(b16); | ^^^ @@ -321,7 +427,7 @@ LL | id_u32(b16); | help: you can convert an `u16` to `u32`: `b16.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:148:12 + --> $DIR/integer-literal-suffix-inference.rs:186:12 | LL | id_u32(b64); | ^^^ expected `u32`, found `u64` @@ -332,7 +438,18 @@ LL | id_u32(b64.try_into().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:152:12 + --> $DIR/integer-literal-suffix-inference.rs:189:12 + | +LL | id_u32(bsize); + | ^^^^^ expected `u32`, found `usize` + | +help: you can convert an `usize` to `u32` and panic if the converted value wouldn't fit + | +LL | id_u32(bsize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:193:12 | LL | id_u64(b8); | ^^ @@ -341,7 +458,7 @@ LL | id_u64(b8); | help: you can convert an `u8` to `u64`: `b8.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:155:12 + --> $DIR/integer-literal-suffix-inference.rs:196:12 | LL | id_u64(b16); | ^^^ @@ -350,7 +467,7 @@ LL | id_u64(b16); | help: you can convert an `u16` to `u64`: `b16.into()` error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:158:12 + --> $DIR/integer-literal-suffix-inference.rs:199:12 | LL | id_u64(b32); | ^^^ @@ -358,6 +475,57 @@ LL | id_u64(b32); | expected `u64`, found `u32` | help: you can convert an `u32` to `u64`: `b32.into()` -error: aborting due to 36 previous errors +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:203:12 + | +LL | id_u64(bsize); + | ^^^^^ expected `u64`, found `usize` + | +help: you can convert an `usize` to `u64` and panic if the converted value wouldn't fit + | +LL | id_u64(bsize.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:207:14 + | +LL | id_usize(b8); + | ^^ + | | + | expected `usize`, found `u8` + | help: you can convert an `u8` to `usize`: `b8.into()` + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:210:14 + | +LL | id_usize(b16); + | ^^^ + | | + | expected `usize`, found `u16` + | help: you can convert an `u16` to `usize`: `b16.into()` + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:213:14 + | +LL | id_usize(b32); + | ^^^ expected `usize`, found `u32` + | +help: you can convert an `u32` to `usize` and panic if the converted value wouldn't fit + | +LL | id_usize(b32.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/integer-literal-suffix-inference.rs:216:14 + | +LL | id_usize(b64); + | ^^^ expected `usize`, found `u64` + | +help: you can convert an `u64` to `usize` and panic if the converted value wouldn't fit + | +LL | id_usize(b64.try_into().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 52 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/interior-mutability/interior-mutability.rs b/src/test/ui/interior-mutability/interior-mutability.rs index e6586de4ab611..ddc882cccf390 100644 --- a/src/test/ui/interior-mutability/interior-mutability.rs +++ b/src/test/ui/interior-mutability/interior-mutability.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::cell::Cell; use std::panic::catch_unwind; fn main() { diff --git a/src/test/ui/interior-mutability/interior-mutability.stderr b/src/test/ui/interior-mutability/interior-mutability.stderr index 2378e412172a5..1a726be4aa6f4 100644 --- a/src/test/ui/interior-mutability/interior-mutability.stderr +++ b/src/test/ui/interior-mutability/interior-mutability.stderr @@ -1,5 +1,5 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary - --> $DIR/interior-mutability.rs:9:5 + --> $DIR/interior-mutability.rs:5:5 | LL | catch_unwind(|| { x.set(23); }); | ^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary @@ -12,7 +12,7 @@ LL | pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { = help: within `std::cell::Cell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` = note: required because it appears within the type `std::cell::Cell` = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::Cell` - = note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:9:18: 9:35 x:&std::cell::Cell]` + = note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:5:18: 5:35 x:&std::cell::Cell]` error: aborting due to previous error diff --git a/src/test/ui/intrinsics/intrinsic-alignment.rs b/src/test/ui/intrinsics/intrinsic-alignment.rs index 02e3139d29444..896651361be2a 100644 --- a/src/test/ui/intrinsics/intrinsic-alignment.rs +++ b/src/test/ui/intrinsics/intrinsic-alignment.rs @@ -56,16 +56,6 @@ mod m { #[cfg(target_os = "windows")] mod m { #[main] - #[cfg(target_arch = "x86")] - pub fn main() { - unsafe { - assert_eq!(::rusti::pref_align_of::(), 8); - assert_eq!(::rusti::min_align_of::(), 8); - } - } - - #[main] - #[cfg(target_arch = "x86_64")] pub fn main() { unsafe { assert_eq!(::rusti::pref_align_of::(), 8); diff --git a/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs b/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs index a2068429af5ea..9804c421db081 100644 --- a/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs +++ b/src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(unused_unsafe)] #![allow(unreachable_code)] // ignore-emscripten no threads support diff --git a/src/test/ui/intrinsics/issue-28575.rs b/src/test/ui/intrinsics/issue-28575.rs new file mode 100644 index 0000000000000..141136d25b215 --- /dev/null +++ b/src/test/ui/intrinsics/issue-28575.rs @@ -0,0 +1,9 @@ +#![feature(intrinsics)] + +extern "C" { + pub static FOO: extern "rust-intrinsic" fn(); +} + +fn main() { + FOO() //~ ERROR: use of extern static is unsafe +} diff --git a/src/test/ui/intrinsics/issue-28575.stderr b/src/test/ui/intrinsics/issue-28575.stderr new file mode 100644 index 0000000000000..66369decf4224 --- /dev/null +++ b/src/test/ui/intrinsics/issue-28575.stderr @@ -0,0 +1,11 @@ +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/issue-28575.rs:8: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 previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/invalid/invalid-plugin-attr.stderr b/src/test/ui/invalid/invalid-plugin-attr.stderr index 9d07eafcc8f20..c822d908dddf9 100644 --- a/src/test/ui/invalid/invalid-plugin-attr.stderr +++ b/src/test/ui/invalid/invalid-plugin-attr.stderr @@ -24,5 +24,5 @@ error: crate-level attribute should be an inner attribute: add an exclamation ma LL | #[plugin(bla)] | ^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/issues-71798.rs b/src/test/ui/issues-71798.rs new file mode 100644 index 0000000000000..08b10463d3927 --- /dev/null +++ b/src/test/ui/issues-71798.rs @@ -0,0 +1,7 @@ +fn test_ref(x: &u32) -> impl std::future::Future + '_ { + *x //~^ ERROR the trait bound `u32: std::future::Future` is not satisfied +} + +fn main() { + let _ = test_ref & u; //~ ERROR cannot find value `u` in this scope +} diff --git a/src/test/ui/issues-71798.stderr b/src/test/ui/issues-71798.stderr new file mode 100644 index 0000000000000..85da87914e768 --- /dev/null +++ b/src/test/ui/issues-71798.stderr @@ -0,0 +1,20 @@ +error[E0425]: cannot find value `u` in this scope + --> $DIR/issues-71798.rs:6:24 + | +LL | let _ = test_ref & u; + | ^ not found in this scope + +error[E0277]: the trait bound `u32: std::future::Future` is not satisfied + --> $DIR/issues-71798.rs:1:25 + | +LL | fn test_ref(x: &u32) -> impl std::future::Future + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `u32` +LL | *x + | -- this returned value is of type `u32` + | + = note: the return type of a function must have a statically known size + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0425. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr index 0793dd99b4d12..888576c43365f 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -49,6 +49,9 @@ LL | impl<'self> Serializable for &'self str { error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/issue-10412.rs:6:13 | +LL | trait Serializable<'self, T> { + | - required by this bound in `Serializable` +... LL | impl<'self> Serializable for &'self str { | ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | diff --git a/src/test/ui/issues/issue-12028.stderr b/src/test/ui/issues/issue-12028.stderr index fe7e8f89f7f1a..30cb7a1df8071 100644 --- a/src/test/ui/issues/issue-12028.stderr +++ b/src/test/ui/issues/issue-12028.stderr @@ -1,10 +1,8 @@ -error[E0284]: type annotations needed +error[E0284]: type annotations needed: cannot satisfy `<_ as StreamHasher>::S == ::S` --> $DIR/issue-12028.rs:27:14 | LL | self.input_stream(&mut stream); - | ^^^^^^^^^^^^ cannot infer type for type parameter `H` declared on the trait `StreamHash` - | - = note: cannot resolve `<_ as StreamHasher>::S == ::S` + | ^^^^^^^^^^^^ cannot satisfy `<_ as StreamHasher>::S == ::S` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-12920.rs b/src/test/ui/issues/issue-12920.rs new file mode 100644 index 0000000000000..a0cfea055be2b --- /dev/null +++ b/src/test/ui/issues/issue-12920.rs @@ -0,0 +1,8 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +pub fn main() { + panic!(); + println!("{}", 1); +} diff --git a/src/test/ui/issues/issue-13202.rs b/src/test/ui/issues/issue-13202.rs new file mode 100644 index 0000000000000..16debb5b6c4a6 --- /dev/null +++ b/src/test/ui/issues/issue-13202.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:bad input +// ignore-emscripten no processes + +fn main() { + Some("foo").unwrap_or(panic!("bad input")).to_string(); +} diff --git a/src/test/ui/issues/issue-13497.stderr b/src/test/ui/issues/issue-13497.stderr index b72f0277052b9..a231f73d06729 100644 --- a/src/test/ui/issues/issue-13497.stderr +++ b/src/test/ui/issues/issue-13497.stderr @@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier --> $DIR/issue-13497.rs:2:5 | LL | &str - | ^ help: consider giving it a 'static lifetime: `&'static` + | ^ 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 + | +LL | &'static str + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-13853.stderr b/src/test/ui/issues/issue-13853.stderr index 2f31636f8adf9..3f1b955dddb2b 100644 --- a/src/test/ui/issues/issue-13853.stderr +++ b/src/test/ui/issues/issue-13853.stderr @@ -9,8 +9,6 @@ LL | self.iter() | = note: expected type parameter `I` found struct `std::slice::Iter<'_, N>` - = 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[E0599]: no method named `iter` found for reference `&G` in the current scope --> $DIR/issue-13853.rs:27:23 diff --git a/src/test/ui/issues/issue-14221.stderr b/src/test/ui/issues/issue-14221.stderr index 63680f6ca56a7..fc8ae1ed7b5b0 100644 --- a/src/test/ui/issues/issue-14221.stderr +++ b/src/test/ui/issues/issue-14221.stderr @@ -27,6 +27,6 @@ note: the lint level is defined here LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 2 warnings emitted For more information about this error, try `rustc --explain E0170`. diff --git a/src/test/ui/issues/issue-14936.rs b/src/test/ui/issues/issue-14936.rs index 33532855fcd7e..02095a2f7e4d2 100644 --- a/src/test/ui/issues/issue-14936.rs +++ b/src/test/ui/issues/issue-14936.rs @@ -1,7 +1,7 @@ // build-pass #![allow(unused_macros)] #![allow(dead_code)] -#![feature(asm)] +#![feature(llvm_asm)] type History = Vec<&'static str>; @@ -18,10 +18,10 @@ macro_rules! demo { let mut history: History = vec![]; unsafe { - asm!("mov ($1), $0" - : $output_constraint (*wrap(&mut x, "out", &mut history)) - : "r"(&wrap(y, "in", &mut history)) - :: "volatile"); + llvm_asm!("mov ($1), $0" + : $output_constraint (*wrap(&mut x, "out", &mut history)) + : "r"(&wrap(y, "in", &mut history)) + :: "volatile"); } assert_eq!((x,y), (1,1)); let b: &[_] = &["out", "in"]; diff --git a/src/test/ui/issues/issue-15129.stderr b/src/test/ui/issues/issue-15129.stderr index b93fa14db0387..aa4434e72b5c7 100644 --- a/src/test/ui/issues/issue-15129.stderr +++ b/src/test/ui/issues/issue-15129.stderr @@ -5,6 +5,7 @@ LL | match (T::T1(()), V::V2(true)) { | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `(T1(()), V2(_))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(T, V)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-15381.stderr b/src/test/ui/issues/issue-15381.stderr index 35f46ab57279c..c4667ce1c8ba1 100644 --- a/src/test/ui/issues/issue-15381.stderr +++ b/src/test/ui/issues/issue-15381.stderr @@ -3,6 +3,8 @@ error[E0005]: refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` | LL | for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { | ^^^^^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered + | + = note: the matched value is of type `&[u8]` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-15487.rs b/src/test/ui/issues/issue-15487.rs index 98714cba0e42d..17b16a62a74e7 100644 --- a/src/test/ui/issues/issue-15487.rs +++ b/src/test/ui/issues/issue-15487.rs @@ -2,6 +2,7 @@ #![allow(unused_attributes)] // ignore-windows // ignore-wasm32-bare no libs to link +// ignore-sgx no libs to link #![feature(link_args)] diff --git a/src/test/ui/issues/issue-16530.rs b/src/test/ui/issues/issue-16530.rs index 22a6ef7fa091f..25817a2a63d60 100644 --- a/src/test/ui/issues/issue-16530.rs +++ b/src/test/ui/issues/issue-16530.rs @@ -7,9 +7,9 @@ use std::hash::{SipHasher, Hasher, Hash}; struct Empty; pub fn main() { - let mut s1 = SipHasher::new_with_keys(0, 0); + let mut s1 = SipHasher::new(); Empty.hash(&mut s1); - let mut s2 = SipHasher::new_with_keys(0, 0); + let mut s2 = SipHasher::new(); Empty.hash(&mut s2); assert_eq!(s1.finish(), s2.finish()); } diff --git a/src/test/ui/issues/issue-17025.rs b/src/test/ui/issues/issue-17025.rs deleted file mode 100644 index 6b7b6d010aa22..0000000000000 --- a/src/test/ui/issues/issue-17025.rs +++ /dev/null @@ -1,13 +0,0 @@ -// ignore-test the unsized enum no longer compiles - -enum A { - B(char), - C([Box]), -} - -fn c(c:char) { - A::B(c); - //~^ ERROR cannot move a value of type A: the size of A cannot be statically determined -} - -pub fn main() {} diff --git a/src/test/ui/issues/issue-17252.stderr b/src/test/ui/issues/issue-17252.stderr index 8fd67b19d6a5a..ee621a8cb1473 100644 --- a/src/test/ui/issues/issue-17252.stderr +++ b/src/test/ui/issues/issue-17252.stderr @@ -1,11 +1,22 @@ -error[E0391]: cycle detected when const checking `FOO` - --> $DIR/issue-17252.rs:1:20 +error[E0391]: cycle detected when normalizing `FOO` + | +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/issue-17252.rs:1:1 + | +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/issue-17252.rs:1:1 | LL | const FOO: usize = FOO; - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `FOO`... + --> $DIR/issue-17252.rs:1:1 | - = note: ...which again requires const checking `FOO`, completing the cycle -note: cycle used when const checking `main::{{constant}}#0` +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires normalizing `FOO`, completing the cycle +note: cycle used when const-evaluating `main::{{constant}}#0` --> $DIR/issue-17252.rs:4:18 | LL | let _x: [u8; FOO]; // caused stack overflow prior to fix diff --git a/src/test/ui/issues/issue-17546.rs b/src/test/ui/issues/issue-17546.rs index c93a03cdec66a..6c62010f1762b 100644 --- a/src/test/ui/issues/issue-17546.rs +++ b/src/test/ui/issues/issue-17546.rs @@ -1,7 +1,5 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl +// ignore-sgx std::os::fortanix_sgx::usercalls::raw::Result changes compiler suggestions + use foo::MyEnum::Result; use foo::NoResult; // Through a re-export diff --git a/src/test/ui/issues/issue-17546.stderr b/src/test/ui/issues/issue-17546.stderr index 2d532cdb9d8a9..8bf40790f0b24 100644 --- a/src/test/ui/issues/issue-17546.stderr +++ b/src/test/ui/issues/issue-17546.stderr @@ -1,5 +1,5 @@ error[E0573]: expected type, found variant `NoResult` - --> $DIR/issue-17546.rs:16:17 + --> $DIR/issue-17546.rs:14:17 | LL | fn new() -> NoResult { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,12 +19,12 @@ LL | fn new() -> Result { | ^^^^^^ error[E0573]: expected type, found variant `Result` - --> $DIR/issue-17546.rs:26:17 + --> $DIR/issue-17546.rs:24:17 | LL | fn new() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a type | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::fmt::Result; | @@ -37,12 +37,12 @@ LL | use std::result::Result; and 1 other candidate error[E0573]: expected type, found variant `Result` - --> $DIR/issue-17546.rs:32:13 + --> $DIR/issue-17546.rs:30:13 | LL | fn new() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a type | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::fmt::Result; | @@ -55,7 +55,7 @@ LL | use std::result::Result; and 1 other candidate error[E0573]: expected type, found variant `NoResult` - --> $DIR/issue-17546.rs:37:15 + --> $DIR/issue-17546.rs:35:15 | LL | fn newer() -> NoResult { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/issues/issue-17718-const-naming.rs b/src/test/ui/issues/issue-17718-const-naming.rs index d30b95843f300..7386478f9f08c 100644 --- a/src/test/ui/issues/issue-17718-const-naming.rs +++ b/src/test/ui/issues/issue-17718-const-naming.rs @@ -3,6 +3,6 @@ const foo: isize = 3; //~^ ERROR: should have an upper case name -//~^^ ERROR: constant item is never used +//~^^ ERROR: constant is never used fn main() {} diff --git a/src/test/ui/issues/issue-17718-const-naming.stderr b/src/test/ui/issues/issue-17718-const-naming.stderr index 4c0aa0553ebd2..ce4ebcb5e3ef6 100644 --- a/src/test/ui/issues/issue-17718-const-naming.stderr +++ b/src/test/ui/issues/issue-17718-const-naming.stderr @@ -1,4 +1,4 @@ -error: constant item is never used: `foo` +error: constant is never used: `foo` --> $DIR/issue-17718-const-naming.rs:4:1 | LL | const foo: isize = 3; diff --git a/src/test/ui/issues/issue-17718-static-sync.rs b/src/test/ui/issues/issue-17718-static-sync.rs index dccbde6a3c532..6f278d76bb18b 100644 --- a/src/test/ui/issues/issue-17718-static-sync.rs +++ b/src/test/ui/issues/issue-17718-static-sync.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Sync; diff --git a/src/test/ui/issues/issue-18107.rs b/src/test/ui/issues/issue-18107.rs index 122940f1c1726..4bf5b6c0f3032 100644 --- a/src/test/ui/issues/issue-18107.rs +++ b/src/test/ui/issues/issue-18107.rs @@ -2,7 +2,7 @@ pub trait AbstractRenderer {} fn _create_render(_: &()) -> dyn AbstractRenderer -//~^ ERROR the size for values of type +//~^ ERROR return type cannot have an unboxed trait object { match 0 { _ => unimplemented!() diff --git a/src/test/ui/issues/issue-18107.stderr b/src/test/ui/issues/issue-18107.stderr index 9bdf470413b1c..1eb6822b8a11a 100644 --- a/src/test/ui/issues/issue-18107.stderr +++ b/src/test/ui/issues/issue-18107.stderr @@ -1,13 +1,22 @@ -error[E0277]: the size for values of type `(dyn AbstractRenderer + 'static)` cannot be known at compilation time +error[E0746]: return type cannot have an unboxed trait object --> $DIR/issue-18107.rs:4:5 | LL | dyn AbstractRenderer | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn AbstractRenderer + 'static)` - = note: to learn more, visit - = note: the return type of a function must have a statically known size +help: use some type `T` that is `T: Sized` as the return type if all return paths have the same type + | +LL | T + | +help: use `impl AbstractRenderer` as the return type if all return paths have the same type but you want to expose only the trait in the signature + | +LL | impl AbstractRenderer + | +help: use a boxed trait object if all return paths implement trait `AbstractRenderer` + | +LL | Box + | error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0746`. diff --git a/src/test/ui/issues/issue-18446.stderr b/src/test/ui/issues/issue-18446.stderr index 3422add9dd96b..11c8cfdcf66a5 100644 --- a/src/test/ui/issues/issue-18446.stderr +++ b/src/test/ui/issues/issue-18446.stderr @@ -5,9 +5,9 @@ LL | x.foo(); | --^^^-- | | | | | multiple `foo` found - | help: disambiguate the method call for candidate #2: `T::foo(&x)` + | help: disambiguate the associated function for candidate #2: `T::foo(&x)` | -note: candidate #1 is defined in an impl for the type `dyn T` +note: candidate #1 is defined in an impl for the type `(dyn T + 'a)` --> $DIR/issue-18446.rs:9:5 | LL | fn foo(&self) {} diff --git a/src/test/run-fail/issue-18576.rs b/src/test/ui/issues/issue-18576.rs similarity index 85% rename from src/test/run-fail/issue-18576.rs rename to src/test/ui/issues/issue-18576.rs index ca9d1e5f5e7de..389cf108b05ee 100644 --- a/src/test/run-fail/issue-18576.rs +++ b/src/test/ui/issues/issue-18576.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:stop +// ignore-emscripten no processes // #18576 // Make sure that calling an extern function pointer in an unreachable @@ -10,4 +12,5 @@ fn main() { let pointer = other; pointer(); } + extern "C" fn other() {} diff --git a/src/test/ui/issues/issue-18919.rs b/src/test/ui/issues/issue-18919.rs index 91fbb13cd6988..f06771e9ea59d 100644 --- a/src/test/ui/issues/issue-18919.rs +++ b/src/test/ui/issues/issue-18919.rs @@ -4,4 +4,9 @@ fn ho_func(f: Option) { //~^ ERROR the size for values of type } +enum Option { + Some(T), + None, +} + fn main() {} diff --git a/src/test/ui/issues/issue-18919.stderr b/src/test/ui/issues/issue-18919.stderr index c8b9045efe6a0..1ea40d0728ed1 100644 --- a/src/test/ui/issues/issue-18919.stderr +++ b/src/test/ui/issues/issue-18919.stderr @@ -3,10 +3,12 @@ error[E0277]: the size for values of type `dyn for<'r> std::ops::Fn(&'r isize) - | LL | fn ho_func(f: Option) { | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | enum Option { + | - required by this bound in `Option` | = help: the trait `std::marker::Sized` is not implemented for `dyn for<'r> std::ops::Fn(&'r isize) -> isize` = note: to learn more, visit - = note: required by `std::option::Option` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-19100.stderr b/src/test/ui/issues/issue-19100.stderr index 01e5313fcc1d9..293430691ddcf 100644 --- a/src/test/ui/issues/issue-19100.stderr +++ b/src/test/ui/issues/issue-19100.stderr @@ -12,3 +12,6 @@ warning[E0170]: pattern binding `Baz` is named the same as one of the variants o LL | Baz if false | ^^^ help: to match on the variant, qualify the path: `Foo::Baz` +warning: 2 warnings emitted + +For more information about this error, try `rustc --explain E0170`. diff --git a/src/test/ui/issues/issue-1920-1.stderr b/src/test/ui/issues/issue-1920-1.stderr index 089968ede7d0f..3130434f6f6d5 100644 --- a/src/test/ui/issues/issue-1920-1.stderr +++ b/src/test/ui/issues/issue-1920-1.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `foo::issue_1920::S: std::clone::Clone` is not sat --> $DIR/issue-1920-1.rs:12:20 | LL | fn assert_clone() where T : Clone { } - | ------------ ----- required by this bound in `assert_clone` + | ----- required by this bound in `assert_clone` ... LL | assert_clone::(); | ^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `foo::issue_1920::S` diff --git a/src/test/ui/issues/issue-1920-2.stderr b/src/test/ui/issues/issue-1920-2.stderr index eaf34e076c088..1084c47f001b8 100644 --- a/src/test/ui/issues/issue-1920-2.stderr +++ b/src/test/ui/issues/issue-1920-2.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `bar::S: std::clone::Clone` is not satisfied --> $DIR/issue-1920-2.rs:10:20 | LL | fn assert_clone() where T : Clone { } - | ------------ ----- required by this bound in `assert_clone` + | ----- required by this bound in `assert_clone` ... LL | assert_clone::(); | ^^^^^^ the trait `std::clone::Clone` is not implemented for `bar::S` diff --git a/src/test/ui/issues/issue-1920-3.stderr b/src/test/ui/issues/issue-1920-3.stderr index 0550f5feba5be..11740317e546e 100644 --- a/src/test/ui/issues/issue-1920-3.stderr +++ b/src/test/ui/issues/issue-1920-3.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `issue_1920::S: std::clone::Clone` is not satisfie --> $DIR/issue-1920-3.rs:14:20 | LL | fn assert_clone() where T : Clone { } - | ------------ ----- required by this bound in `assert_clone` + | ----- required by this bound in `assert_clone` ... LL | assert_clone::(); | ^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `issue_1920::S` diff --git a/src/test/ui/issues/issue-20005.stderr b/src/test/ui/issues/issue-20005.stderr index 529571a6b74dd..775f9702401a6 100644 --- a/src/test/ui/issues/issue-20005.stderr +++ b/src/test/ui/issues/issue-20005.stderr @@ -2,15 +2,21 @@ error[E0277]: the size for values of type `Self` cannot be known at compilation --> $DIR/issue-20005.rs:10:49 | LL | trait From { - | --------------- required by `From` + | --- required by this bound in `From` ... LL | ) -> >::Result where Dst: From { - | ^^^^^^^^^^- help: consider further restricting `Self`: `, Self: std::marker::Sized` - | | - | doesn't have a size known at compile-time + | ^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Self` = note: to learn more, visit +help: consider further restricting `Self` + | +LL | ) -> >::Result where Dst: From, Self: std::marker::Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | trait From { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr index 1c5911e05f767..3bcc50ded8425 100644 --- a/src/test/ui/issues/issue-20225.stderr +++ b/src/test/ui/issues/issue-20225.stderr @@ -8,8 +8,6 @@ LL | extern "rust-call" fn call(&self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&Foo, (T,))` - = 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[E0053]: method `call_mut` has an incompatible type for trait --> $DIR/issue-20225.rs:11:3 @@ -21,8 +19,6 @@ LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&mut Foo, (T,))` - = 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[E0053]: method `call_once` has an incompatible type for trait --> $DIR/issue-20225.rs:18:3 @@ -35,8 +31,6 @@ LL | extern "rust-call" fn call_once(self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(Foo, (T,))` - = 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 3 previous errors diff --git a/src/test/ui/issues/issue-20413.stderr b/src/test/ui/issues/issue-20413.stderr index 84e64ff74ae96..ad33eef07cba5 100644 --- a/src/test/ui/issues/issue-20413.stderr +++ b/src/test/ui/issues/issue-20413.stderr @@ -10,7 +10,7 @@ error[E0275]: overflow evaluating the requirement `NoData $DIR/issue-20413.rs:8:36 | LL | trait Foo { - | --------- required by `Foo` + | --------- required by this bound in `Foo` ... LL | impl Foo for T where NoData: Foo { | ^^^ @@ -148,7 +148,7 @@ error[E0275]: overflow evaluating the requirement `NoData $DIR/issue-20413.rs:8:36 | LL | trait Foo { - | --------- required by `Foo` + | --------- required by this bound in `Foo` ... LL | impl Foo for T where NoData: Foo { | ^^^ diff --git a/src/test/ui/issues/issue-20433.stderr b/src/test/ui/issues/issue-20433.stderr index abd2290952baf..1dab637e489db 100644 --- a/src/test/ui/issues/issue-20433.stderr +++ b/src/test/ui/issues/issue-20433.stderr @@ -3,10 +3,14 @@ error[E0277]: the size for values of type `[i32]` cannot be known at compilation | LL | fn iceman(c: Vec<[i32]>) {} | ^^^^^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/liballoc/vec.rs:LL:COL + | +LL | pub struct Vec { + | - required by this bound in `std::vec::Vec` | = help: the trait `std::marker::Sized` is not implemented for `[i32]` = note: to learn more, visit - = note: required by `std::vec::Vec` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20971.rs b/src/test/ui/issues/issue-20971.rs new file mode 100644 index 0000000000000..2e10418178c42 --- /dev/null +++ b/src/test/ui/issues/issue-20971.rs @@ -0,0 +1,23 @@ +// Regression test for Issue #20971. + +// run-fail +// error-pattern:Hello, world! +// ignore-emscripten no processes + +pub trait Parser { + type Input; + fn parse(&mut self, input: ::Input); +} + +impl Parser for () { + type Input = (); + fn parse(&mut self, input: ()) {} +} + +pub fn many() -> Box::Input> + 'static> { + panic!("Hello, world!") +} + +fn main() { + many().parse(()); +} diff --git a/src/test/ui/issues/issue-2111.stderr b/src/test/ui/issues/issue-2111.stderr index 90fdb48ea625d..aab2559a155ae 100644 --- a/src/test/ui/issues/issue-2111.stderr +++ b/src/test/ui/issues/issue-2111.stderr @@ -5,6 +5,7 @@ LL | match (a,b) { | ^^^^^ pattern `(None, None)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(std::option::Option, std::option::Option)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21160.rs b/src/test/ui/issues/issue-21160.rs index a13b7783370a8..46733566cf383 100644 --- a/src/test/ui/issues/issue-21160.rs +++ b/src/test/ui/issues/issue-21160.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl struct Bar; impl Bar { diff --git a/src/test/ui/issues/issue-21160.stderr b/src/test/ui/issues/issue-21160.stderr index a24dc8a259dfb..0c3d75c08ffee 100644 --- a/src/test/ui/issues/issue-21160.stderr +++ b/src/test/ui/issues/issue-21160.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Bar: std::hash::Hash` is not satisfied - --> $DIR/issue-21160.rs:12:12 + --> $DIR/issue-21160.rs:8:12 | LL | struct Foo(Bar); | ^^^ the trait `std::hash::Hash` is not implemented for `Bar` diff --git a/src/test/ui/issues/issue-21763.stderr b/src/test/ui/issues/issue-21763.stderr index 2bede9120cf1d..3ec876f37d460 100644 --- a/src/test/ui/issues/issue-21763.stderr +++ b/src/test/ui/issues/issue-21763.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely --> $DIR/issue-21763.rs:9:5 | LL | fn foo() {} - | --- ---- required by this bound in `foo` + | ---- required by this bound in `foo` ... LL | foo::, Rc<()>>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely diff --git a/src/test/ui/issues/issue-21837.stderr b/src/test/ui/issues/issue-21837.stderr index cfc294b5fa2d7..f7e46b25cf82b 100644 --- a/src/test/ui/issues/issue-21837.stderr +++ b/src/test/ui/issues/issue-21837.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: Bound` is not satisfied --> $DIR/issue-21837.rs:8:9 | LL | pub struct Foo(T); - | ---------------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | impl Trait2 for Foo {} | ^^^^^^ the trait `Bound` is not implemented for `T` | -help: consider restricting this type parameter with `T: Bound` - --> $DIR/issue-21837.rs:8:6 +help: consider restricting type parameter `T` | -LL | impl Trait2 for Foo {} - | ^ +LL | impl Trait2 for Foo {} + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21974.stderr b/src/test/ui/issues/issue-21974.stderr index 19823499066eb..fea2c7d5d26d7 100644 --- a/src/test/ui/issues/issue-21974.stderr +++ b/src/test/ui/issues/issue-21974.stderr @@ -2,12 +2,12 @@ error[E0283]: type annotations needed --> $DIR/issue-21974.rs:11:19 | LL | trait Foo { - | --------- required by `Foo` + | --------- required by this bound in `Foo` ... LL | where &'a T : Foo, | ^^^ cannot infer type for reference `&'a T` | - = note: cannot resolve `&'a T: Foo` + = note: cannot satisfy `&'a T: Foo` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22872.stderr b/src/test/ui/issues/issue-22872.stderr index 283a5e04a8b6f..038490bbd7c7b 100644 --- a/src/test/ui/issues/issue-22872.stderr +++ b/src/test/ui/issues/issue-22872.stderr @@ -1,14 +1,16 @@ error[E0277]: `

>::Item` is not an iterator --> $DIR/issue-22872.rs:20:40 | -LL | fn push_process

(process: P) where P: Process<'static> { - | - help: consider further restricting the associated type: `,

>::Item: std::iter::Iterator` LL | let _: Box Wrap<'b>> = Box::new(Wrapper(process)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `

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

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

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

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

>::Item: std::iter::Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23080-2.rs b/src/test/ui/issues/issue-23080-2.rs index d20bb4bd90726..7f6b9e3fba79f 100644 --- a/src/test/ui/issues/issue-23080-2.rs +++ b/src/test/ui/issues/issue-23080-2.rs @@ -1,6 +1,7 @@ //~ ERROR #![feature(optin_builtin_traits)] +#![feature(negative_impls)] unsafe auto trait Trait { type Output; //~ ERROR E0380 diff --git a/src/test/ui/issues/issue-23080-2.stderr b/src/test/ui/issues/issue-23080-2.stderr index fcd1ecfa98288..48ce09aaa34da 100644 --- a/src/test/ui/issues/issue-23080-2.stderr +++ b/src/test/ui/issues/issue-23080-2.stderr @@ -1,5 +1,5 @@ error[E0380]: auto traits cannot have methods or associated items - --> $DIR/issue-23080-2.rs:6:10 + --> $DIR/issue-23080-2.rs:7:10 | LL | unsafe auto trait Trait { | ----- auto trait cannot have items diff --git a/src/test/ui/issues/issue-23080.rs b/src/test/ui/issues/issue-23080.rs index fa5c35316bc28..035db82ba5de0 100644 --- a/src/test/ui/issues/issue-23080.rs +++ b/src/test/ui/issues/issue-23080.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] unsafe auto trait Trait { fn method(&self) { //~ ERROR E0380 diff --git a/src/test/ui/issues/issue-23080.stderr b/src/test/ui/issues/issue-23080.stderr index dbb9861b5784a..73ecb1c362e17 100644 --- a/src/test/ui/issues/issue-23080.stderr +++ b/src/test/ui/issues/issue-23080.stderr @@ -1,5 +1,5 @@ error[E0380]: auto traits cannot have methods or associated items - --> $DIR/issue-23080.rs:4:8 + --> $DIR/issue-23080.rs:5:8 | LL | unsafe auto trait Trait { | ----- auto trait cannot have items diff --git a/src/test/ui/issues/issue-23281.rs b/src/test/ui/issues/issue-23281.rs index d5f7472886273..72716896426e4 100644 --- a/src/test/ui/issues/issue-23281.rs +++ b/src/test/ui/issues/issue-23281.rs @@ -5,4 +5,8 @@ impl Struct { //~^ ERROR the size for values of type } +struct Vec { + t: T, +} + fn main() {} diff --git a/src/test/ui/issues/issue-23281.stderr b/src/test/ui/issues/issue-23281.stderr index 68a90c6d80f3b..3b4b8997a7009 100644 --- a/src/test/ui/issues/issue-23281.stderr +++ b/src/test/ui/issues/issue-23281.stderr @@ -3,10 +3,12 @@ error[E0277]: the size for values of type `(dyn std::ops::Fn() + 'static)` canno | LL | pub fn function(funs: Vec ()>) {} | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Vec { + | - required by this bound in `Vec` | = help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::Fn() + 'static)` = note: to learn more, visit - = note: required by `std::vec::Vec` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23302-1.stderr b/src/test/ui/issues/issue-23302-1.stderr index f2457774326dd..b6c85b9e22749 100644 --- a/src/test/ui/issues/issue-23302-1.stderr +++ b/src/test/ui/issues/issue-23302-1.stderr @@ -1,15 +1,26 @@ -error[E0391]: cycle detected when const checking `X::A::{{constant}}#0` +error[E0391]: cycle detected when const-evaluating + checking `X::A::{{constant}}#0` --> $DIR/issue-23302-1.rs:4:9 | LL | A = X::A as isize, | ^^^^^^^^^^^^^ | - = note: ...which again requires const checking `X::A::{{constant}}#0`, completing the cycle -note: cycle used when processing `X::A::{{constant}}#0` +note: ...which requires const-evaluating + checking `X::A::{{constant}}#0`... --> $DIR/issue-23302-1.rs:4:9 | LL | A = X::A as isize, | ^^^^^^^^^^^^^ +note: ...which requires const-evaluating `X::A::{{constant}}#0`... + --> $DIR/issue-23302-1.rs:4:9 + | +LL | A = X::A as isize, + | ^^^^^^^^^^^^^ + = note: ...which requires normalizing `X::A as isize`... + = note: ...which again requires const-evaluating + checking `X::A::{{constant}}#0`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/issue-23302-1.rs:3:1 + | +LL | enum X { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23302-2.stderr b/src/test/ui/issues/issue-23302-2.stderr index c121c17b904ea..d014922fe2069 100644 --- a/src/test/ui/issues/issue-23302-2.stderr +++ b/src/test/ui/issues/issue-23302-2.stderr @@ -1,15 +1,26 @@ -error[E0391]: cycle detected when const checking `Y::A::{{constant}}#0` +error[E0391]: cycle detected when const-evaluating + checking `Y::A::{{constant}}#0` --> $DIR/issue-23302-2.rs:4:9 | LL | A = Y::B as isize, | ^^^^^^^^^^^^^ | - = note: ...which again requires const checking `Y::A::{{constant}}#0`, completing the cycle -note: cycle used when processing `Y::A::{{constant}}#0` +note: ...which requires const-evaluating + checking `Y::A::{{constant}}#0`... --> $DIR/issue-23302-2.rs:4:9 | LL | A = Y::B as isize, | ^^^^^^^^^^^^^ +note: ...which requires const-evaluating `Y::A::{{constant}}#0`... + --> $DIR/issue-23302-2.rs:4:9 + | +LL | A = Y::B as isize, + | ^^^^^^^^^^^^^ + = note: ...which requires normalizing `Y::B as isize`... + = note: ...which again requires const-evaluating + checking `Y::A::{{constant}}#0`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/issue-23302-2.rs:3:1 + | +LL | enum Y { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23302-3.stderr b/src/test/ui/issues/issue-23302-3.stderr index 0229469f04140..b30b1214271a0 100644 --- a/src/test/ui/issues/issue-23302-3.stderr +++ b/src/test/ui/issues/issue-23302-3.stderr @@ -1,20 +1,38 @@ -error[E0391]: cycle detected when const checking `A` - --> $DIR/issue-23302-3.rs:1:16 +error[E0391]: cycle detected when const-evaluating + checking `A` + --> $DIR/issue-23302-3.rs:1:1 | LL | const A: i32 = B; - | ^ + | ^^^^^^^^^^^^^^^^^ | -note: ...which requires const checking `B`... - --> $DIR/issue-23302-3.rs:3:16 +note: ...which requires const-evaluating + checking `A`... + --> $DIR/issue-23302-3.rs:1:1 | -LL | const B: i32 = A; - | ^ - = note: ...which again requires const checking `A`, completing the cycle -note: cycle used when processing `A` +LL | const A: i32 = B; + | ^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `A`... --> $DIR/issue-23302-3.rs:1:1 | LL | const A: i32 = B; | ^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `B`... +note: ...which requires const-evaluating + checking `B`... + --> $DIR/issue-23302-3.rs:3:1 + | +LL | const B: i32 = A; + | ^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `B`... + --> $DIR/issue-23302-3.rs:3:1 + | +LL | const B: i32 = A; + | ^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `B`... + --> $DIR/issue-23302-3.rs:3:1 + | +LL | const B: i32 = A; + | ^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `A`... + = note: ...which again requires const-evaluating + checking `A`, completing the cycle + = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/run-fail/issue-23354-2.rs b/src/test/ui/issues/issue-23354-2.rs similarity index 80% rename from src/test/run-fail/issue-23354-2.rs rename to src/test/ui/issues/issue-23354-2.rs index 8f7baff56dc55..c291d8a5eaf3d 100644 --- a/src/test/run-fail/issue-23354-2.rs +++ b/src/test/ui/issues/issue-23354-2.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:panic evaluated +// ignore-emscripten no processes #[allow(unused_variables)] fn main() { diff --git a/src/test/ui/issues/issue-23354.rs b/src/test/ui/issues/issue-23354.rs new file mode 100644 index 0000000000000..8b7c2eef2fc1b --- /dev/null +++ b/src/test/ui/issues/issue-23354.rs @@ -0,0 +1,8 @@ +// run-fail +// error-pattern:panic evaluated +// ignore-emscripten no processes + +#[allow(unused_variables)] +fn main() { + let x = [panic!("panic evaluated"); 0]; +} diff --git a/src/test/ui/issues/issue-23458.rs b/src/test/ui/issues/issue-23458.rs index 521db37170ab5..423b19c3ebd09 100644 --- a/src/test/ui/issues/issue-23458.rs +++ b/src/test/ui/issues/issue-23458.rs @@ -1,11 +1,11 @@ -#![feature(asm)] +#![feature(llvm_asm)] // build-fail // only-x86_64 fn main() { unsafe { - asm!("int $3"); //~ ERROR too few operands for instruction - //~| ERROR invalid operand in inline asm + llvm_asm!("int $3"); //~ ERROR too few operands for instruction + //~| ERROR invalid operand in inline asm } } diff --git a/src/test/ui/issues/issue-23458.stderr b/src/test/ui/issues/issue-23458.stderr index 76c3e6da82ea2..81f06e6397542 100644 --- a/src/test/ui/issues/issue-23458.stderr +++ b/src/test/ui/issues/issue-23458.stderr @@ -1,8 +1,8 @@ error: invalid operand in inline asm: 'int $3' --> $DIR/issue-23458.rs:8:9 | -LL | asm!("int $3"); - | ^^^^^^^^^^^^^^^ +LL | llvm_asm!("int $3"); + | ^^^^^^^^^^^^^^^^^^^^ error: :1:2: error: too few operands for instruction int @@ -10,8 +10,8 @@ error: :1:2: error: too few operands for instruction --> $DIR/issue-23458.rs:8:9 | -LL | asm!("int $3"); - | ^^^^^^^^^^^^^^^ +LL | llvm_asm!("int $3"); + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs b/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs index 6ef7fd42ec6d0..403cf970bcb0a 100644 --- a/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs +++ b/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs @@ -153,6 +153,7 @@ impl<'a> Drop for E<'a> { } }; + #[allow(unused_must_use)] if do_drop { mem::replace(self, E::A(GaspA(f_a, 0xA3A0, log, D::new("drop", 6, log)), true)); } diff --git a/src/test/ui/issues/issue-23898.rs b/src/test/ui/issues/issue-23898.rs index a8787f279b71b..3de365675ad22 100644 --- a/src/test/ui/issues/issue-23898.rs +++ b/src/test/ui/issues/issue-23898.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_parens)] #![allow(non_camel_case_types)] // Note: This test was used to demonstrate #5873 (now #23898). diff --git a/src/test/ui/issues/issue-24036.rs b/src/test/ui/issues/issue-24036.rs index bd82f95c9ef66..7df036c8e3a45 100644 --- a/src/test/ui/issues/issue-24036.rs +++ b/src/test/ui/issues/issue-24036.rs @@ -10,7 +10,7 @@ fn closure_from_match() { 2 => |c| c - 1, _ => |c| c - 1 }; - //~^^^ ERROR `match` arms have incompatible types + //~^^^^ ERROR type annotations needed } fn main() { } diff --git a/src/test/ui/issues/issue-24036.stderr b/src/test/ui/issues/issue-24036.stderr index 036c05fc848cf..e6b8367f74fb5 100644 --- a/src/test/ui/issues/issue-24036.stderr +++ b/src/test/ui/issues/issue-24036.stderr @@ -11,24 +11,13 @@ LL | x = |c| c + 1; = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object -error[E0308]: `match` arms have incompatible types - --> $DIR/issue-24036.rs:10:14 +error[E0282]: type annotations needed + --> $DIR/issue-24036.rs:9:15 | -LL | let x = match 1usize { - | _____________- -LL | | 1 => |c| c + 1, - | | --------- this is found to be of type `[closure@$DIR/issue-24036.rs:9:14: 9:23]` -LL | | 2 => |c| c - 1, - | | ^^^^^^^^^ expected closure, found a different closure -LL | | _ => |c| c - 1 -LL | | }; - | |_____- `match` arms have incompatible types - | - = note: expected type `[closure@$DIR/issue-24036.rs:9:14: 9:23]` - found closure `[closure@$DIR/issue-24036.rs:10:14: 10:23]` - = note: no two closures, even if identical, have the same type - = help: consider boxing your closure and/or using it as a trait object +LL | 1 => |c| c + 1, + | ^ consider giving this closure parameter a type error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0282, E0308. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/issues/issue-24204.stderr b/src/test/ui/issues/issue-24204.stderr index 2a714861da1fd..d5cbcf786bf1a 100644 --- a/src/test/ui/issues/issue-24204.stderr +++ b/src/test/ui/issues/issue-24204.stderr @@ -2,10 +2,14 @@ error[E0271]: type mismatch resolving `<::A as MultiDispatch>:: --> $DIR/issue-24204.rs:14:12 | LL | trait Trait: Sized { - | ------------------ required by `Trait` + | ----- required by a bound in this +LL | type A: MultiDispatch; + | -------- required by this bound in `Trait` ... LL | fn test>(b: i32) -> T where T::A: MultiDispatch { T::new(b) } - | ^^^^^^^^^^^^ expected type parameter `T`, found associated type + | - ^^^^^^^^^^^^ expected type parameter `T`, found associated type + | | + | this type parameter | = note: expected type parameter `T` found associated type `<::A as MultiDispatch>::O` diff --git a/src/test/ui/issues/issue-24424.stderr b/src/test/ui/issues/issue-24424.stderr index 538d44c3b2ef3..9f5e934295b87 100644 --- a/src/test/ui/issues/issue-24424.stderr +++ b/src/test/ui/issues/issue-24424.stderr @@ -2,12 +2,12 @@ error[E0283]: type annotations needed --> $DIR/issue-24424.rs:4:57 | LL | trait Trait0<'l0> {} - | ----------------- required by `Trait0` + | ----------------- required by this bound in `Trait0` LL | LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {} | ^^^^^^^^^^^ cannot infer type for type parameter `T0` | - = note: cannot resolve `T0: Trait0<'l0>` + = note: cannot satisfy `T0: Trait0<'l0>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-2444.rs b/src/test/ui/issues/issue-2444.rs new file mode 100644 index 0000000000000..ac0d0506a342d --- /dev/null +++ b/src/test/ui/issues/issue-2444.rs @@ -0,0 +1,17 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +use std::sync::Arc; + +enum Err { + Errr(Arc), +} + +fn foo() -> Err { + panic!(); +} + +fn main() { + let _f = foo(); +} diff --git a/src/test/ui/issues/issue-2470-bounds-check-overflow.rs b/src/test/ui/issues/issue-2470-bounds-check-overflow.rs new file mode 100644 index 0000000000000..f0e8e185e5633 --- /dev/null +++ b/src/test/ui/issues/issue-2470-bounds-check-overflow.rs @@ -0,0 +1,27 @@ +// run-fail +// error-pattern:index out of bounds +// ignore-emscripten no processes + +use std::mem; + +fn main() { + + // This should cause a bounds-check panic, but may not if we do our + // bounds checking by comparing the scaled index to the vector's + // address-bounds, since we've scaled the index to wrap around to the + // address of the 0th cell in the array (even though the index is + // huge). + + let x = vec![1_usize, 2_usize, 3_usize]; + + let base = x.as_ptr() as usize; + let idx = base / mem::size_of::(); + println!("ov1 base = 0x{:x}", base); + println!("ov1 idx = 0x{:x}", idx); + println!("ov1 sizeof::() = 0x{:x}", mem::size_of::()); + println!("ov1 idx * sizeof::() = 0x{:x}", + idx * mem::size_of::()); + + // This should panic. + println!("ov1 0x{:x}", x[idx]); +} diff --git a/src/test/ui/issues/issue-25076.stderr b/src/test/ui/issues/issue-25076.stderr index 0a13a2bc33023..27c577a0d5f6d 100644 --- a/src/test/ui/issues/issue-25076.stderr +++ b/src/test/ui/issues/issue-25076.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `(): InOut<_>` is not satisfied --> $DIR/issue-25076.rs:10:20 | LL | fn do_fold>(init: B, f: F) {} - | ------- --------------- required by this bound in `do_fold` + | --------------- required by this bound in `do_fold` ... LL | do_fold(bot(), ()); | ^^ the trait `InOut<_>` is not implemented for `()` diff --git a/src/test/ui/issues/issue-26638.stderr b/src/test/ui/issues/issue-26638.stderr index 1d8fbdc63c5e0..3df58d66d1f8e 100644 --- a/src/test/ui/issues/issue-26638.stderr +++ b/src/test/ui/issues/issue-26638.stderr @@ -14,17 +14,25 @@ error[E0106]: missing lifetime specifier --> $DIR/issue-26638.rs:4:40 | LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } - | ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static` + | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &'static str { iter() } + | ^^^^^^^^ error[E0106]: missing lifetime specifier --> $DIR/issue-26638.rs:7:22 | LL | fn parse_type_3() -> &str { unimplemented!() } - | ^ help: consider giving it a 'static lifetime: `&'static` + | ^ 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 + | +LL | fn parse_type_3() -> &'static str { unimplemented!() } + | ^^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-27033.rs b/src/test/ui/issues/issue-27033.rs index 2798e51090407..a23819a20f9aa 100644 --- a/src/test/ui/issues/issue-27033.rs +++ b/src/test/ui/issues/issue-27033.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl fn main() { match Some(1) { None @ _ => {} //~ ERROR match bindings cannot shadow unit variants diff --git a/src/test/ui/issues/issue-27033.stderr b/src/test/ui/issues/issue-27033.stderr index c0de0f14268a7..3bd7469afff07 100644 --- a/src/test/ui/issues/issue-27033.stderr +++ b/src/test/ui/issues/issue-27033.stderr @@ -1,5 +1,5 @@ error[E0530]: match bindings cannot shadow unit variants - --> $DIR/issue-27033.rs:7:9 + --> $DIR/issue-27033.rs:3:9 | LL | None @ _ => {} | ^^^^ cannot be named the same as a unit variant @@ -10,7 +10,7 @@ LL | pub use crate::option::Option::{self, None, Some}; | ---- the unit variant `None` is defined here error[E0530]: match bindings cannot shadow constants - --> $DIR/issue-27033.rs:11:9 + --> $DIR/issue-27033.rs:7:9 | LL | const C: u8 = 1; | ---------------- the constant `C` is defined here diff --git a/src/test/ui/issues/issue-27042.stderr b/src/test/ui/issues/issue-27042.stderr index 69c452b88f315..7dee1a6a5f044 100644 --- a/src/test/ui/issues/issue-27042.stderr +++ b/src/test/ui/issues/issue-27042.stderr @@ -43,6 +43,6 @@ LL | / 'd: LL | | while let Some(_) = None { break }; | |__________________________________________^ expected `i32`, found `()` -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-27078.stderr b/src/test/ui/issues/issue-27078.stderr index fbc72d063f37c..3eb9d3c62039f 100644 --- a/src/test/ui/issues/issue-27078.stderr +++ b/src/test/ui/issues/issue-27078.stderr @@ -2,14 +2,16 @@ error[E0277]: the size for values of type `Self` cannot be known at compilation --> $DIR/issue-27078.rs:5:12 | LL | fn foo(self) -> &'static i32 { - | ^^^^ - help: consider further restricting `Self`: `where Self: std::marker::Sized` - | | - | doesn't have a size known at compile-time + | ^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Self` = note: to learn more, visit = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature +help: consider further restricting `Self` + | +LL | fn foo(self) -> &'static i32 where Self: std::marker::Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-2761.rs b/src/test/ui/issues/issue-2761.rs new file mode 100644 index 0000000000000..3ba098abbe65a --- /dev/null +++ b/src/test/ui/issues/issue-2761.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:custom message +// ignore-emscripten no processes + +fn main() { + assert!(false, "custom message"); +} diff --git a/src/test/ui/issues/issue-2823.rs b/src/test/ui/issues/issue-2823.rs index f00c2304733ca..7b443b4152613 100644 --- a/src/test/ui/issues/issue-2823.rs +++ b/src/test/ui/issues/issue-2823.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - struct C { x: isize, } diff --git a/src/test/ui/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr index 6e11dd4028836..0cdc501d56811 100644 --- a/src/test/ui/issues/issue-2823.stderr +++ b/src/test/ui/issues/issue-2823.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `C` in the current scope - --> $DIR/issue-2823.rs:18:16 + --> $DIR/issue-2823.rs:13:16 | LL | struct C { | -------- method `clone` not found for this diff --git a/src/test/ui/issues/issue-28777.rs b/src/test/ui/issues/issue-28777.rs index 74de00adadbda..1f426b7185e8f 100644 --- a/src/test/ui/issues/issue-28777.rs +++ b/src/test/ui/issues/issue-28777.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] fn main() { let v1 = { 1 + {2} * {3} }; let v2 = 1 + {2} * {3} ; diff --git a/src/test/run-fail/issue-28934.rs b/src/test/ui/issues/issue-28934.rs similarity index 92% rename from src/test/run-fail/issue-28934.rs rename to src/test/ui/issues/issue-28934.rs index 5915372b6920f..1e48878f632e5 100644 --- a/src/test/run-fail/issue-28934.rs +++ b/src/test/ui/issues/issue-28934.rs @@ -1,7 +1,9 @@ // Regression test: issue had to do with "givens" in region inference, // which were not being considered during the contraction phase. +// run-fail // error-pattern:explicit panic +// ignore-emscripten no processes struct Parser<'i: 't, 't>(&'i u8, &'t u8); diff --git a/src/test/ui/issues/issue-29147.stderr b/src/test/ui/issues/issue-29147.stderr index 1efedb45cace7..94aff5963544c 100644 --- a/src/test/ui/issues/issue-29147.stderr +++ b/src/test/ui/issues/issue-29147.stderr @@ -7,7 +7,7 @@ LL | trait Foo { fn xxx(&self); } LL | let _ = >::xxx; | ^^^^^^^^^^^^ cannot infer type for struct `S5<_>` | - = note: cannot resolve `S5<_>: Foo` + = note: cannot satisfy `S5<_>: Foo` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-29181.rs b/src/test/ui/issues/issue-29181.rs index 45752ad4f62b6..70e5bc0192086 100644 --- a/src/test/ui/issues/issue-29181.rs +++ b/src/test/ui/issues/issue-29181.rs @@ -4,4 +4,6 @@ extern crate issue_29181 as foo; fn main() { 0.homura(); //~ ERROR no method named `homura` found + // Issue #47759, detect existing method on the fundamental impl: + let _ = |x: f64| x * 2.0.exp(); //~ ERROR can't call method `exp` on ambiguous numeric type } diff --git a/src/test/ui/issues/issue-29181.stderr b/src/test/ui/issues/issue-29181.stderr index 250b158ab8e33..b66dcb88d0062 100644 --- a/src/test/ui/issues/issue-29181.stderr +++ b/src/test/ui/issues/issue-29181.stderr @@ -4,6 +4,18 @@ error[E0599]: no method named `homura` found for type `{integer}` in the current LL | 0.homura(); | ^^^^^^ method not found in `{integer}` -error: aborting due to previous error +error[E0689]: can't call method `exp` on ambiguous numeric type `{float}` + --> $DIR/issue-29181.rs:8:30 + | +LL | let _ = |x: f64| x * 2.0.exp(); + | ^^^ + | +help: you must specify a concrete type for this numeric value, like `f32` + | +LL | let _ = |x: f64| x * 2.0_f32.exp(); + | ^^^^^^^ + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0599, E0689. +For more information about an error, try `rustc --explain E0599`. diff --git a/src/test/ui/issues/issue-29516.rs b/src/test/ui/issues/issue-29516.rs index d43367e34529b..035f904b15bb3 100644 --- a/src/test/ui/issues/issue-29516.rs +++ b/src/test/ui/issues/issue-29516.rs @@ -1,5 +1,6 @@ // check-pass #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait NotSame {} diff --git a/src/test/run-fail/issue-29798.rs b/src/test/ui/issues/issue-29798.rs similarity index 77% rename from src/test/run-fail/issue-29798.rs rename to src/test/ui/issues/issue-29798.rs index b06aa5fc728d9..5eff5d1915bad 100644 --- a/src/test/run-fail/issue-29798.rs +++ b/src/test/ui/issues/issue-29798.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:index out of bounds: the len is 5 but the index is 5 +// ignore-emscripten no processes const fn test(x: usize) -> i32 { [42;5][x] diff --git a/src/test/ui/issues/issue-30079.stderr b/src/test/ui/issues/issue-30079.stderr index 6fc8b810745aa..f4a530124ff34 100644 --- a/src/test/ui/issues/issue-30079.stderr +++ b/src/test/ui/issues/issue-30079.stderr @@ -26,6 +26,6 @@ LL | impl ::SemiPrivTrait for () { LL | type Assoc = Priv; | ^^^^^^^^^^^^^^^^^^ can't leak private type -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0446`. diff --git a/src/test/ui/issues/issue-30240.stderr b/src/test/ui/issues/issue-30240.stderr index 8b683b4af65cd..a2c58d6e051b5 100644 --- a/src/test/ui/issues/issue-30240.stderr +++ b/src/test/ui/issues/issue-30240.stderr @@ -5,6 +5,7 @@ LL | match "world" { | ^^^^^^^ pattern `&_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&str` error[E0004]: non-exhaustive patterns: `&_` not covered --> $DIR/issue-30240.rs:6:11 @@ -13,6 +14,7 @@ LL | match "world" { | ^^^^^^^ pattern `&_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&str` error: aborting due to 2 previous errors diff --git a/src/test/run-fail/issue-3029.rs b/src/test/ui/issues/issue-3029.rs similarity index 83% rename from src/test/run-fail/issue-3029.rs rename to src/test/ui/issues/issue-3029.rs index face808b68ff3..a5d30960a4cdb 100644 --- a/src/test/run-fail/issue-3029.rs +++ b/src/test/ui/issues/issue-3029.rs @@ -1,9 +1,11 @@ +// run-fail +// error-pattern:so long +// ignore-emscripten no processes + #![allow(unused_allocation)] #![allow(unreachable_code)] #![allow(unused_variables)] - -// error-pattern:so long fn main() { let mut x = Vec::new(); let y = vec![3]; diff --git a/src/test/ui/issues/issue-30302.stderr b/src/test/ui/issues/issue-30302.stderr index 770ed3d4f0152..849ff1ebd9236 100644 --- a/src/test/ui/issues/issue-30302.stderr +++ b/src/test/ui/issues/issue-30302.stderr @@ -21,6 +21,6 @@ note: the lint level is defined here LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0170`. diff --git a/src/test/run-fail/issue-30380.rs b/src/test/ui/issues/issue-30380.rs similarity index 94% rename from src/test/run-fail/issue-30380.rs rename to src/test/ui/issues/issue-30380.rs index 036071a89c7a4..48b329c5de148 100644 --- a/src/test/run-fail/issue-30380.rs +++ b/src/test/ui/issues/issue-30380.rs @@ -1,7 +1,9 @@ // check that panics in destructors during assignment do not leave // destroyed values lying around for other destructors to observe. +// run-fail // error-pattern:panicking destructors ftw! +// ignore-emscripten no processes struct Observer<'a>(&'a mut FilledOnDrop); diff --git a/src/test/ui/issues/issue-3096-1.stderr b/src/test/ui/issues/issue-3096-1.stderr index c5a7fa7e0eb83..97c34755189de 100644 --- a/src/test/ui/issues/issue-3096-1.stderr +++ b/src/test/ui/issues/issue-3096-1.stderr @@ -5,6 +5,7 @@ LL | match () { } | ^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3096-2.stderr b/src/test/ui/issues/issue-3096-2.stderr index 6f2e0e760d7f6..472d1a91e6a15 100644 --- a/src/test/ui/issues/issue-3096-2.stderr +++ b/src/test/ui/issues/issue-3096-2.stderr @@ -5,6 +5,7 @@ LL | match x { } | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `*const Bottom` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-31173.rs b/src/test/ui/issues/issue-31173.rs index 25be266c52893..26195318380d2 100644 --- a/src/test/ui/issues/issue-31173.rs +++ b/src/test/ui/issues/issue-31173.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::vec::IntoIter; pub fn get_tok(it: &mut IntoIter) { diff --git a/src/test/ui/issues/issue-31173.stderr b/src/test/ui/issues/issue-31173.stderr index 20bfdeea4b1fa..62c9e566d8689 100644 --- a/src/test/ui/issues/issue-31173.stderr +++ b/src/test/ui/issues/issue-31173.stderr @@ -1,5 +1,5 @@ -error[E0271]: type mismatch resolving `, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]> as std::iter::Iterator>::Item == &_` - --> $DIR/issue-31173.rs:14:10 +error[E0271]: type mismatch resolving `, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]> as std::iter::Iterator>::Item == &_` + --> $DIR/issue-31173.rs:10:10 | LL | .cloned() | ^^^^^^ expected `u8`, found reference @@ -7,11 +7,11 @@ LL | .cloned() = note: expected type `u8` found reference `&_` -error[E0599]: no method named `collect` found for struct `std::iter::Cloned, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>` in the current scope - --> $DIR/issue-31173.rs:18:10 +error[E0599]: no method named `collect` found for struct `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>` in the current scope + --> $DIR/issue-31173.rs:14:10 | LL | .collect(); - | ^^^^^^^ method not found in `std::iter::Cloned, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>` + | ^^^^^^^ method not found in `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>` | ::: $SRC_DIR/libcore/iter/adapters/mod.rs:LL:COL | @@ -22,10 +22,10 @@ LL | pub struct TakeWhile { | -------------------------- doesn't satisfy `<_ as std::iter::Iterator>::Item = &_` | = note: the method `collect` exists but the following trait bounds were not satisfied: - `, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]> as std::iter::Iterator>::Item = &_` - which is required by `std::iter::Cloned, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>: std::iter::Iterator` - `std::iter::Cloned, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>: std::iter::Iterator` - which is required by `&mut std::iter::Cloned, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>: std::iter::Iterator` + `, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]> as std::iter::Iterator>::Item = &_` + which is required by `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>: std::iter::Iterator` + `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>: std::iter::Iterator` + which is required by `&mut std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>: std::iter::Iterator` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-31561.stderr b/src/test/ui/issues/issue-31561.stderr index d3c8e876b8a88..2f562b23692de 100644 --- a/src/test/ui/issues/issue-31561.stderr +++ b/src/test/ui/issues/issue-31561.stderr @@ -15,6 +15,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/ch18-02-refutability.html + = note: the matched value is of type `Thing` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Thing::Foo(y) = Thing::Foo(1) { /* */ } diff --git a/src/test/ui/issues/issue-32122-1.fixed b/src/test/ui/issues/issue-32122-1.fixed new file mode 100644 index 0000000000000..4fc5f64ff9a45 --- /dev/null +++ b/src/test/ui/issues/issue-32122-1.fixed @@ -0,0 +1,17 @@ +// run-rustfix +use std::ops::Deref; + +struct Foo(u8); + +impl Deref for Foo { + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let a = Foo(0); + // Should suggest `&*` when coercing &ty to *const ty + let _: *const u8 = &*a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-1.rs b/src/test/ui/issues/issue-32122-1.rs new file mode 100644 index 0000000000000..3c4859f07a2e7 --- /dev/null +++ b/src/test/ui/issues/issue-32122-1.rs @@ -0,0 +1,17 @@ +// run-rustfix +use std::ops::Deref; + +struct Foo(u8); + +impl Deref for Foo { + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let a = Foo(0); + // Should suggest `&*` when coercing &ty to *const ty + let _: *const u8 = &a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-1.stderr b/src/test/ui/issues/issue-32122-1.stderr new file mode 100644 index 0000000000000..dfbd3223efc86 --- /dev/null +++ b/src/test/ui/issues/issue-32122-1.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/issue-32122-1.rs:16:24 + | +LL | let _: *const u8 = &a; + | --------- ^^ + | | | + | | expected `u8`, found struct `Foo` + | | help: consider dereferencing: `&*a` + | expected due to this + | + = note: expected raw pointer `*const u8` + found reference `&Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-32122-2.fixed b/src/test/ui/issues/issue-32122-2.fixed new file mode 100644 index 0000000000000..cee0e59297657 --- /dev/null +++ b/src/test/ui/issues/issue-32122-2.fixed @@ -0,0 +1,28 @@ +// run-rustfix +use std::ops::Deref; +struct Bar(u8); +struct Foo(Bar); +struct Emm(Foo); +impl Deref for Bar{ + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Emm { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +fn main() { + let a = Emm(Foo(Bar(0))); + // Should suggest `&***` even when deref is pretty deep + let _: *const u8 = &***a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-2.rs b/src/test/ui/issues/issue-32122-2.rs new file mode 100644 index 0000000000000..39e9df4224e74 --- /dev/null +++ b/src/test/ui/issues/issue-32122-2.rs @@ -0,0 +1,28 @@ +// run-rustfix +use std::ops::Deref; +struct Bar(u8); +struct Foo(Bar); +struct Emm(Foo); +impl Deref for Bar{ + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Emm { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +fn main() { + let a = Emm(Foo(Bar(0))); + // Should suggest `&***` even when deref is pretty deep + let _: *const u8 = &a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-2.stderr b/src/test/ui/issues/issue-32122-2.stderr new file mode 100644 index 0000000000000..2e199e2a19f73 --- /dev/null +++ b/src/test/ui/issues/issue-32122-2.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/issue-32122-2.rs:27:24 + | +LL | let _: *const u8 = &a; + | --------- ^^ + | | | + | | expected `u8`, found struct `Emm` + | | help: consider dereferencing: `&***a` + | expected due to this + | + = note: expected raw pointer `*const u8` + found reference `&Emm` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-3214.rs b/src/test/ui/issues/issue-3214.rs index 9a727aa305797..030677c879fb5 100644 --- a/src/test/ui/issues/issue-3214.rs +++ b/src/test/ui/issues/issue-3214.rs @@ -1,3 +1,5 @@ +// ignore-tidy-linelength + fn foo() { struct Foo { x: T, //~ ERROR can't use generic parameters from outer function @@ -5,6 +7,7 @@ fn foo() { impl Drop for Foo { //~^ ERROR wrong number of type arguments + //~| ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates fn drop(&mut self) {} } } diff --git a/src/test/ui/issues/issue-3214.stderr b/src/test/ui/issues/issue-3214.stderr index 02c8da10bb4a3..30bc6cb115ffc 100644 --- a/src/test/ui/issues/issue-3214.stderr +++ b/src/test/ui/issues/issue-3214.stderr @@ -1,5 +1,5 @@ error[E0401]: can't use generic parameters from outer function - --> $DIR/issue-3214.rs:3:12 + --> $DIR/issue-3214.rs:5:12 | LL | fn foo() { | --- - type parameter from outer function @@ -10,12 +10,18 @@ LL | x: T, | ^ use of generic parameter from outer function error[E0107]: wrong number of type arguments: expected 0, found 1 - --> $DIR/issue-3214.rs:6:26 + --> $DIR/issue-3214.rs:8:26 | LL | impl Drop for Foo { | ^ unexpected type argument -error: aborting due to 2 previous errors +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-3214.rs:8:10 + | +LL | impl Drop for Foo { + | ^ unconstrained type parameter + +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0107, E0401. +Some errors have detailed explanations: E0107, E0207, E0401. For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/issues/issue-32323.stderr b/src/test/ui/issues/issue-32323.stderr index 7c0928b192499..369f56b9869a3 100644 --- a/src/test/ui/issues/issue-32323.stderr +++ b/src/test/ui/issues/issue-32323.stderr @@ -8,8 +8,10 @@ LL | pub fn f<'a, T: Tr<'a>>() -> >::Out {} | = note: expected associated type `>::Out` found unit type `()` - = note: consider constraining the associated type `>::Out` to `()` or calling a method that returns `>::Out` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `>::Out` to `()` + | +LL | pub fn f<'a, T: Tr<'a, Out = ()>>() -> >::Out {} + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-32709.stderr b/src/test/ui/issues/issue-32709.stderr index 04b8c3aa35396..af272633f210d 100644 --- a/src/test/ui/issues/issue-32709.stderr +++ b/src/test/ui/issues/issue-32709.stderr @@ -1,6 +1,8 @@ error[E0277]: `?` couldn't convert the error to `()` --> $DIR/issue-32709.rs:4:11 | +LL | fn a() -> Result { + | --------------- expected `()` because of this LL | Err(5)?; | ^ the trait `std::convert::From<{integer}>` is not implemented for `()` | diff --git a/src/test/ui/issues/issue-32963.stderr b/src/test/ui/issues/issue-32963.stderr index 450c37f456a80..34d5c894e36f2 100644 --- a/src/test/ui/issues/issue-32963.stderr +++ b/src/test/ui/issues/issue-32963.stderr @@ -24,7 +24,7 @@ error[E0277]: the trait bound `dyn Misc: std::marker::Copy` is not satisfied --> $DIR/issue-32963.rs:8:5 | LL | fn size_of_copy() -> usize { mem::size_of::() } - | ------------ ---- required by this bound in `size_of_copy` + | ---- required by this bound in `size_of_copy` ... LL | size_of_copy::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `dyn Misc` diff --git a/src/test/ui/issues/issue-33140-hack-boundaries.rs b/src/test/ui/issues/issue-33140-hack-boundaries.rs index fbdef51c13255..d091162fced6b 100644 --- a/src/test/ui/issues/issue-33140-hack-boundaries.rs +++ b/src/test/ui/issues/issue-33140-hack-boundaries.rs @@ -1,11 +1,10 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![allow(order_dependent_trait_objects)] // Check that the issue #33140 hack does not allow unintended things. // OK -trait Trait0 { -} +trait Trait0 {} impl Trait0 for dyn Send {} impl Trait0 for dyn Send {} @@ -20,58 +19,49 @@ impl Trait1 for dyn Send {} //~^ ERROR E0119 // Problem 2: negative impl -trait Trait2 { -} +trait Trait2 {} impl Trait2 for dyn Send {} impl !Trait2 for dyn Send {} -//~^ ERROR E0119 - +//~^ ERROR E0751 // Problem 3: type parameter -trait Trait3 { -} +trait Trait3 {} impl Trait3 for dyn Send {} impl Trait3 for dyn Send {} //~^ ERROR E0119 // Problem 4a: not a trait object - generic -trait Trait4a { -} +trait Trait4a {} impl Trait4a for T {} impl Trait4a for dyn Send {} //~^ ERROR E0119 // Problem 4b: not a trait object - misc -trait Trait4b { -} +trait Trait4b {} impl Trait4b for () {} impl Trait4b for () {} //~^ ERROR E0119 // Problem 4c: not a principal-less trait object -trait Trait4c { -} +trait Trait4c {} impl Trait4c for dyn Trait1 + Send {} impl Trait4c for dyn Trait1 + Send {} //~^ ERROR E0119 // Problem 4d: lifetimes -trait Trait4d { -} +trait Trait4d {} impl<'a> Trait4d for dyn Send + 'a {} impl<'a> Trait4d for dyn Send + 'a {} //~^ ERROR E0119 - // Problem 5: where-clauses -trait Trait5 { -} +trait Trait5 {} impl Trait5 for dyn Send {} impl Trait5 for dyn Send where u32: Copy {} diff --git a/src/test/ui/issues/issue-33140-hack-boundaries.stderr b/src/test/ui/issues/issue-33140-hack-boundaries.stderr index 95aaa55ba7c67..ae65701ecb52a 100644 --- a/src/test/ui/issues/issue-33140-hack-boundaries.stderr +++ b/src/test/ui/issues/issue-33140-hack-boundaries.stderr @@ -1,21 +1,21 @@ error[E0119]: conflicting implementations of trait `Trait1` for type `(dyn std::marker::Send + 'static)`: - --> $DIR/issue-33140-hack-boundaries.rs:19:1 + --> $DIR/issue-33140-hack-boundaries.rs:18:1 | LL | impl Trait1 for dyn Send {} | ------------------------ first implementation here LL | impl Trait1 for dyn Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)` -error[E0119]: conflicting implementations of trait `Trait2` for type `(dyn std::marker::Send + 'static)`: - --> $DIR/issue-33140-hack-boundaries.rs:27:1 +error[E0751]: found both positive and negative implementation of trait `Trait2` for type `(dyn std::marker::Send + 'static)`: + --> $DIR/issue-33140-hack-boundaries.rs:25:1 | LL | impl Trait2 for dyn Send {} - | ------------------------ first implementation here + | ------------------------ positive implementation here LL | impl !Trait2 for dyn Send {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here error[E0119]: conflicting implementations of trait `Trait3<(dyn std::marker::Sync + 'static)>` for type `(dyn std::marker::Send + 'static)`: - --> $DIR/issue-33140-hack-boundaries.rs:36:1 + --> $DIR/issue-33140-hack-boundaries.rs:32:1 | LL | impl Trait3 for dyn Send {} | ---------------------------------- first implementation here @@ -23,7 +23,7 @@ LL | impl Trait3 for dyn Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)` error[E0119]: conflicting implementations of trait `Trait4a` for type `(dyn std::marker::Send + 'static)`: - --> $DIR/issue-33140-hack-boundaries.rs:44:1 + --> $DIR/issue-33140-hack-boundaries.rs:39:1 | LL | impl Trait4a for T {} | ----------------------------- first implementation here @@ -31,7 +31,7 @@ LL | impl Trait4a for dyn Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)` error[E0119]: conflicting implementations of trait `Trait4b` for type `()`: - --> $DIR/issue-33140-hack-boundaries.rs:52:1 + --> $DIR/issue-33140-hack-boundaries.rs:46:1 | LL | impl Trait4b for () {} | ------------------- first implementation here @@ -39,7 +39,7 @@ LL | impl Trait4b for () {} | ^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` error[E0119]: conflicting implementations of trait `Trait4c` for type `(dyn Trait1 + std::marker::Send + 'static)`: - --> $DIR/issue-33140-hack-boundaries.rs:60:1 + --> $DIR/issue-33140-hack-boundaries.rs:53:1 | LL | impl Trait4c for dyn Trait1 + Send {} | ---------------------------------- first implementation here @@ -47,7 +47,7 @@ LL | impl Trait4c for dyn Trait1 + Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Trait1 + std::marker::Send + 'static)` error[E0119]: conflicting implementations of trait `Trait4d` for type `dyn std::marker::Send`: - --> $DIR/issue-33140-hack-boundaries.rs:68:1 + --> $DIR/issue-33140-hack-boundaries.rs:60:1 | LL | impl<'a> Trait4d for dyn Send + 'a {} | ---------------------------------- first implementation here @@ -55,7 +55,7 @@ LL | impl<'a> Trait4d for dyn Send + 'a {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `dyn std::marker::Send` error[E0119]: conflicting implementations of trait `Trait5` for type `(dyn std::marker::Send + 'static)`: - --> $DIR/issue-33140-hack-boundaries.rs:77:1 + --> $DIR/issue-33140-hack-boundaries.rs:67:1 | LL | impl Trait5 for dyn Send {} | ------------------------ first implementation here @@ -64,4 +64,5 @@ LL | impl Trait5 for dyn Send where u32: Copy {} error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0119`. +Some errors have detailed explanations: E0119, E0751. +For more information about an error, try `rustc --explain E0119`. diff --git a/src/test/ui/issues/issue-33140-traitobject-crate.stderr b/src/test/ui/issues/issue-33140-traitobject-crate.stderr index efa77f5ceb8ca..781decb5ae281 100644 --- a/src/test/ui/issues/issue-33140-traitobject-crate.stderr +++ b/src/test/ui/issues/issue-33140-traitobject-crate.stderr @@ -38,3 +38,5 @@ LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send + Sync { } = 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 + diff --git a/src/test/ui/issues/issue-33264.rs b/src/test/ui/issues/issue-33264.rs index 31638b004391f..32a36e44aa1ae 100644 --- a/src/test/ui/issues/issue-33264.rs +++ b/src/test/ui/issues/issue-33264.rs @@ -2,7 +2,7 @@ // only-x86_64 #![allow(dead_code, non_upper_case_globals)] -#![feature(asm)] +#![feature(llvm_asm)] #[repr(C)] pub struct D32x4(f32,f32,f32,f32); @@ -11,16 +11,16 @@ impl D32x4 { fn add(&self, vec: Self) -> Self { unsafe { let ret: Self; - asm!(" - movaps $1, %xmm1 - movaps $2, %xmm2 - addps %xmm1, %xmm2 - movaps $xmm1, $0 - " - : "=r"(ret) - : "1"(self), "2"(vec) - : "xmm1", "xmm2" - ); + llvm_asm!(" + movaps $1, %xmm1 + movaps $2, %xmm2 + addps %xmm1, %xmm2 + movaps $xmm1, $0 + " + : "=r"(ret) + : "1"(self), "2"(vec) + : "xmm1", "xmm2" + ); ret } } diff --git a/src/test/ui/issues/issue-34721.fixed b/src/test/ui/issues/issue-34721.fixed index ba2810ee3d725..f135ad3836ea1 100644 --- a/src/test/ui/issues/issue-34721.fixed +++ b/src/test/ui/issues/issue-34721.fixed @@ -18,7 +18,7 @@ pub mod bar { mod baz { use bar; use Foo; - pub fn baz(x: T) -> T { + pub fn baz(x: T) -> T { if 0 == 1 { bar::bar(x.zero()) } else { diff --git a/src/test/ui/issues/issue-34721.stderr b/src/test/ui/issues/issue-34721.stderr index 5c51d0444461a..6cfed20f43a04 100644 --- a/src/test/ui/issues/issue-34721.stderr +++ b/src/test/ui/issues/issue-34721.stderr @@ -13,11 +13,10 @@ LL | }; LL | x.zero() | ^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/issue-34721.rs:21:19 +help: consider further restricting this bound | -LL | pub fn baz(x: T) -> T { - | ^^^ +LL | pub fn baz(x: T) -> T { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-35675.stderr b/src/test/ui/issues/issue-35675.stderr index a9a27da55b1a1..8637e574c5ecb 100644 --- a/src/test/ui/issues/issue-35675.stderr +++ b/src/test/ui/issues/issue-35675.stderr @@ -15,7 +15,7 @@ error[E0425]: cannot find function, tuple struct or tuple variant `Apple` in thi LL | Apple(5) | ^^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this tuple variant | LL | use Fruit::Apple; | @@ -35,7 +35,7 @@ error[E0425]: cannot find function, tuple struct or tuple variant `Apple` in thi LL | Apple(5) | ^^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this tuple variant | LL | use Fruit::Apple; | diff --git a/src/test/ui/issues/issue-3601.stderr b/src/test/ui/issues/issue-3601.stderr index 445eb4107d1df..6b2a5d76243d8 100644 --- a/src/test/ui/issues/issue-3601.stderr +++ b/src/test/ui/issues/issue-3601.stderr @@ -5,6 +5,7 @@ LL | box NodeKind::Element(ed) => match ed.kind { | ^^^^^^^ pattern `Box(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::boxed::Box` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-36163.stderr b/src/test/ui/issues/issue-36163.stderr index 3866243914b89..7c2da9dce6e9d 100644 --- a/src/test/ui/issues/issue-36163.stderr +++ b/src/test/ui/issues/issue-36163.stderr @@ -1,20 +1,48 @@ -error[E0391]: cycle detected when const checking `Foo::B::{{constant}}#0` +error[E0391]: cycle detected when const-evaluating + checking `Foo::B::{{constant}}#0` --> $DIR/issue-36163.rs:4:9 | LL | B = A, | ^ | -note: ...which requires const checking `A`... - --> $DIR/issue-36163.rs:1:18 +note: ...which requires const-evaluating + checking `Foo::B::{{constant}}#0`... + --> $DIR/issue-36163.rs:4:9 | -LL | const A: isize = Foo::B as isize; - | ^^^^^^^^^^^^^^^ - = note: ...which again requires const checking `Foo::B::{{constant}}#0`, completing the cycle -note: cycle used when processing `Foo::B::{{constant}}#0` +LL | B = A, + | ^ +note: ...which requires const-evaluating `Foo::B::{{constant}}#0`... --> $DIR/issue-36163.rs:4:9 | LL | B = A, | ^ + = note: ...which requires normalizing `A`... +note: ...which requires const-evaluating + checking `A`... + --> $DIR/issue-36163.rs:1:1 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `A`... + --> $DIR/issue-36163.rs:1:1 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating `A`... + --> $DIR/issue-36163.rs:1:1 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `A`... + = note: ...which again requires const-evaluating + checking `Foo::B::{{constant}}#0`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/issue-36163.rs:1:1 + | +LL | / const A: isize = Foo::B as isize; +LL | | +LL | | enum Foo { +LL | | B = A, +LL | | } +LL | | +LL | | fn main() {} + | |____________^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3702-2.stderr b/src/test/ui/issues/issue-3702-2.stderr index b18e407c3d464..6d8d17292f2d1 100644 --- a/src/test/ui/issues/issue-3702-2.stderr +++ b/src/test/ui/issues/issue-3702-2.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in an impl of the trait `Add` for the type `isize` | LL | fn to_int(&self) -> isize { *self } | ^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | ToPrimitive::to_int(&self) + other.to_int() | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | Add::to_int(&self) + other.to_int() | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/issues/issue-37366.rs b/src/test/ui/issues/issue-37366.rs index 6bf3a276ce138..be9b4af8fbc41 100644 --- a/src/test/ui/issues/issue-37366.rs +++ b/src/test/ui/issues/issue-37366.rs @@ -1,12 +1,12 @@ // check-pass // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] macro_rules! interrupt_handler { () => { unsafe fn _interrupt_handler() { - asm!("pop eax" :::: "intel"); + llvm_asm!("pop eax" :::: "intel"); } } } diff --git a/src/test/ui/issues/issue-37433.rs b/src/test/ui/issues/issue-37433.rs index c4d427f3ad3af..2ea970327f06b 100644 --- a/src/test/ui/issues/issue-37433.rs +++ b/src/test/ui/issues/issue-37433.rs @@ -1,11 +1,11 @@ // build-fail -// ignore-emscripten no asm! support +// ignore-emscripten no llvm_asm! support -#![feature(asm)] +#![feature(llvm_asm)] fn main() { unsafe { - asm!("" :: "r"("")); + llvm_asm!("" :: "r"("")); //~^ ERROR: invalid value for constraint in inline assembly } } diff --git a/src/test/ui/issues/issue-37433.stderr b/src/test/ui/issues/issue-37433.stderr index d9e1c98e9ee41..ff6965ad353b2 100644 --- a/src/test/ui/issues/issue-37433.stderr +++ b/src/test/ui/issues/issue-37433.stderr @@ -1,8 +1,8 @@ error[E0669]: invalid value for constraint in inline assembly - --> $DIR/issue-37433.rs:8:24 + --> $DIR/issue-37433.rs:8:29 | -LL | asm!("" :: "r"("")); - | ^^ +LL | llvm_asm!("" :: "r"("")); + | ^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-37515.stderr b/src/test/ui/issues/issue-37515.stderr index 1aafa512d835c..204a39bc8e8e9 100644 --- a/src/test/ui/issues/issue-37515.stderr +++ b/src/test/ui/issues/issue-37515.stderr @@ -11,3 +11,5 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(dead_code)]` implied by `#[warn(unused)]` +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-37534.stderr b/src/test/ui/issues/issue-37534.stderr index 1a05c7ab42028..5d008cf24dc90 100644 --- a/src/test/ui/issues/issue-37534.stderr +++ b/src/test/ui/issues/issue-37534.stderr @@ -4,7 +4,7 @@ error[E0404]: expected trait, found derive macro `Hash` LL | struct Foo { } | ^^^^ not a trait | -help: possible better candidate is found in another module, you can import it into scope +help: consider importing this trait instead | LL | use std::hash::Hash; | @@ -23,7 +23,7 @@ LL | struct Foo { } | = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted Some errors have detailed explanations: E0392, E0404. For more information about an error, try `rustc --explain E0392`. diff --git a/src/test/ui/issues/issue-38293.stderr b/src/test/ui/issues/issue-38293.stderr index cc3c72b496f5f..d2450ab125062 100644 --- a/src/test/ui/issues/issue-38293.stderr +++ b/src/test/ui/issues/issue-38293.stderr @@ -10,7 +10,7 @@ error[E0423]: expected function, found module `baz` LL | baz(); | ^^^ not a function | -help: possible better candidate is found in another module, you can import it into scope +help: consider importing this function instead | LL | use bar::baz; | diff --git a/src/test/ui/issues/issue-38857.rs b/src/test/ui/issues/issue-38857.rs index c0695f8216512..81d881c100bb6 100644 --- a/src/test/ui/issues/issue-38857.rs +++ b/src/test/ui/issues/issue-38857.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - fn main() { let a = std::sys::imp::process::process_common::StdioPipes { ..panic!() }; //~^ ERROR failed to resolve: could not find `imp` in `sys` [E0433] diff --git a/src/test/ui/issues/issue-38857.stderr b/src/test/ui/issues/issue-38857.stderr index 1a287e94bafd3..ed700ff95e5b5 100644 --- a/src/test/ui/issues/issue-38857.stderr +++ b/src/test/ui/issues/issue-38857.stderr @@ -1,11 +1,11 @@ error[E0433]: failed to resolve: could not find `imp` in `sys` - --> $DIR/issue-38857.rs:7:23 + --> $DIR/issue-38857.rs:2:23 | LL | let a = std::sys::imp::process::process_common::StdioPipes { ..panic!() }; | ^^^ could not find `imp` in `sys` error[E0603]: module `sys` is private - --> $DIR/issue-38857.rs:7:18 + --> $DIR/issue-38857.rs:2:18 | LL | let a = std::sys::imp::process::process_common::StdioPipes { ..panic!() }; | ^^^ private module diff --git a/src/test/ui/issues/issue-39211.rs b/src/test/ui/issues/issue-39211.rs index db101ae248cb6..c7b6f1d58f33d 100644 --- a/src/test/ui/issues/issue-39211.rs +++ b/src/test/ui/issues/issue-39211.rs @@ -8,7 +8,8 @@ trait Mat { } fn m() { - let a = [3; M::Row::DIM]; //~ ERROR associated type `Row` not found for `M` + let a = [3; M::Row::DIM]; + //~^ ERROR constant expression depends on a generic parameter } fn main() { } diff --git a/src/test/ui/issues/issue-39211.stderr b/src/test/ui/issues/issue-39211.stderr index c14c663e5a1a9..c555983ea68e0 100644 --- a/src/test/ui/issues/issue-39211.stderr +++ b/src/test/ui/issues/issue-39211.stderr @@ -1,9 +1,10 @@ -error[E0220]: associated type `Row` not found for `M` - --> $DIR/issue-39211.rs:11:20 +error: constant expression depends on a generic parameter + --> $DIR/issue-39211.rs:11:17 | LL | let a = [3; M::Row::DIM]; - | ^^^ associated type `Row` not found + | ^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes error: aborting due to previous error -For more information about this error, try `rustc --explain E0220`. diff --git a/src/test/ui/issues/issue-39362.stderr b/src/test/ui/issues/issue-39362.stderr index 55cd14a5c1e08..8c162e55619e0 100644 --- a/src/test/ui/issues/issue-39362.stderr +++ b/src/test/ui/issues/issue-39362.stderr @@ -10,6 +10,7 @@ LL | match f { | ^ patterns `Bar { bar: C, .. }`, `Bar { bar: D, .. }`, `Bar { bar: E, .. }` and 1 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-39559-2.rs b/src/test/ui/issues/issue-39559-2.rs index ec0275b2d6c12..3a52e4d6216a5 100644 --- a/src/test/ui/issues/issue-39559-2.rs +++ b/src/test/ui/issues/issue-39559-2.rs @@ -17,5 +17,4 @@ fn main() { = [0; Dim3::dim()]; //~^ ERROR E0015 //~| ERROR E0080 - //~| ERROR mismatched types } diff --git a/src/test/ui/issues/issue-39559-2.stderr b/src/test/ui/issues/issue-39559-2.stderr index 7cbf63c2da0a9..586debbbe5353 100644 --- a/src/test/ui/issues/issue-39559-2.stderr +++ b/src/test/ui/issues/issue-39559-2.stderr @@ -22,19 +22,7 @@ error[E0080]: evaluation of constant value failed LL | = [0; Dim3::dim()]; | ^^^^^^^^^^^ calling non-const function `::dim` -error[E0308]: mismatched types - --> $DIR/issue-39559-2.rs:17:11 - | -LL | let array: [usize; Dim3::dim()] - | -------------------- expected due to this -... -LL | = [0; Dim3::dim()]; - | ^^^^^^^^^^^^^^^^ expected `Dim3::dim()`, found `Dim3::dim()` - | - = note: expected array `[usize; _]` - found array `[usize; _]` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0015, E0080, E0308. +Some errors have detailed explanations: E0015, E0080. For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/issues/issue-40827.stderr b/src/test/ui/issues/issue-40827.stderr index 3fe47e249f10c..a10abb89021a3 100644 --- a/src/test/ui/issues/issue-40827.stderr +++ b/src/test/ui/issues/issue-40827.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::rc::Rc` cannot be sent between threads safely --> $DIR/issue-40827.rs:14:5 | LL | fn f(_: T) {} - | - ---- required by this bound in `f` + | ---- required by this bound in `f` ... LL | f(Foo(Arc::new(Bar::B(None)))); | ^ `std::rc::Rc` cannot be sent between threads safely @@ -16,7 +16,7 @@ error[E0277]: `std::rc::Rc` cannot be shared between threads safely --> $DIR/issue-40827.rs:14:5 | LL | fn f(_: T) {} - | - ---- required by this bound in `f` + | ---- required by this bound in `f` ... LL | f(Foo(Arc::new(Bar::B(None)))); | ^ `std::rc::Rc` cannot be shared between threads safely diff --git a/src/test/ui/issues/issue-41394.rs b/src/test/ui/issues/issue-41394.rs index 64873ac35a002..06a330813406a 100644 --- a/src/test/ui/issues/issue-41394.rs +++ b/src/test/ui/issues/issue-41394.rs @@ -5,7 +5,6 @@ enum Foo { enum Bar { A = Foo::A as isize - //~^ ERROR evaluation of constant value failed } fn main() {} diff --git a/src/test/ui/issues/issue-41394.stderr b/src/test/ui/issues/issue-41394.stderr index 47a24547d4533..fa95ca9c18a10 100644 --- a/src/test/ui/issues/issue-41394.stderr +++ b/src/test/ui/issues/issue-41394.stderr @@ -6,13 +6,6 @@ LL | A = "" + 1 | | | &str -error[E0080]: evaluation of constant value failed - --> $DIR/issue-41394.rs:7:9 - | -LL | A = Foo::A as isize - | ^^^^^^^^^^^^^^^ referenced constant has errors - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0080, E0369. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0369`. diff --git a/src/test/ui/issues/issue-42312.stderr b/src/test/ui/issues/issue-42312.stderr index 915bfffd6d592..0d4797a7a0673 100644 --- a/src/test/ui/issues/issue-42312.stderr +++ b/src/test/ui/issues/issue-42312.stderr @@ -2,14 +2,16 @@ error[E0277]: the size for values of type `::Target` ca --> $DIR/issue-42312.rs:4:12 | LL | fn baz(_: Self::Target) where Self: Deref {} - | ^ - help: consider further restricting the associated type: `, ::Target: std::marker::Sized` - | | - | doesn't have a size known at compile-time + | ^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `::Target` = note: to learn more, visit = note: all function arguments must have a statically known size = help: unsized locals are gated as an unstable feature +help: consider further restricting the associated type + | +LL | fn baz(_: Self::Target) where Self: Deref, ::Target: std::marker::Sized {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `(dyn std::string::ToString + 'static)` cannot be known at compilation time --> $DIR/issue-42312.rs:8:10 diff --git a/src/test/ui/issues/issue-42944.stderr b/src/test/ui/issues/issue-42944.stderr index c71194f41c114..e7e251e39c04f 100644 --- a/src/test/ui/issues/issue-42944.stderr +++ b/src/test/ui/issues/issue-42944.stderr @@ -10,7 +10,7 @@ error[E0425]: cannot find function, tuple struct or tuple variant `B` in this sc LL | B(()); | ^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this tuple struct | LL | use foo::B; | diff --git a/src/test/ui/issues/issue-43162.stderr b/src/test/ui/issues/issue-43162.stderr index 0ed3d27c65b6e..a443db40732ac 100644 --- a/src/test/ui/issues/issue-43162.stderr +++ b/src/test/ui/issues/issue-43162.stderr @@ -17,9 +17,6 @@ LL | fn foo() -> bool { | --- ^^^^ expected `bool`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression -LL | -LL | break true; - | - help: consider removing this semicolon error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-4321.stderr b/src/test/ui/issues/issue-4321.stderr index afb4fe775d58c..1e8852556b161 100644 --- a/src/test/ui/issues/issue-4321.stderr +++ b/src/test/ui/issues/issue-4321.stderr @@ -5,6 +5,7 @@ LL | println!("foo {:}", match tup { | ^^^ pattern `(true, false)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(bool, bool)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-43623.stderr b/src/test/ui/issues/issue-43623.stderr index d90eb53f9006f..99fb2a1f5d030 100644 --- a/src/test/ui/issues/issue-43623.stderr +++ b/src/test/ui/issues/issue-43623.stderr @@ -2,7 +2,7 @@ error[E0631]: type mismatch in function arguments --> $DIR/issue-43623.rs:14:5 | LL | pub fn break_me(f: F) - | -------- + | -------- required by a bound in this LL | where T: for<'b> Trait<'b>, LL | F: for<'b> FnMut(>::Assoc) { | -------------------------------------- required by this bound in `break_me` @@ -16,7 +16,7 @@ error[E0271]: type mismatch resolving `for<'b> $DIR/issue-43623.rs:14:5 | LL | pub fn break_me(f: F) - | -------- + | -------- required by a bound in this LL | where T: for<'b> Trait<'b>, LL | F: for<'b> FnMut(>::Assoc) { | ------------------------------ required by this bound in `break_me` diff --git a/src/test/ui/issues/issue-4366-2.stderr b/src/test/ui/issues/issue-4366-2.stderr index 60a1155c614f6..ecee595d4ab6a 100644 --- a/src/test/ui/issues/issue-4366-2.stderr +++ b/src/test/ui/issues/issue-4366-2.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `Bar` in this scope LL | fn sub() -> Bar { 1 } | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this type alias | LL | use a::b::Bar; | @@ -15,7 +15,7 @@ error[E0423]: expected function, found module `foo` LL | foo(); | ^^^ not a function | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use foo::foo; | diff --git a/src/test/ui/issues/issue-4366.stderr b/src/test/ui/issues/issue-4366.stderr index d931d51911756..a094180572daa 100644 --- a/src/test/ui/issues/issue-4366.stderr +++ b/src/test/ui/issues/issue-4366.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find function `foo` in this scope LL | fn sub() -> isize { foo(); 1 } | ^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use foo::foo; | diff --git a/src/test/ui/issues/issue-43784-associated-type.stderr b/src/test/ui/issues/issue-43784-associated-type.stderr index 2f50a53f26c77..d8e9110fbbd1d 100644 --- a/src/test/ui/issues/issue-43784-associated-type.stderr +++ b/src/test/ui/issues/issue-43784-associated-type.stderr @@ -1,19 +1,13 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied - --> $DIR/issue-43784-associated-type.rs:14:5 + --> $DIR/issue-43784-associated-type.rs:14:18 | -LL | type Assoc: Partial; - | ----- associated type defined here -... -LL | impl Complete for T { - | ---------------------- in this `impl` item LL | type Assoc = T; - | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/issue-43784-associated-type.rs:13:6 +help: consider restricting type parameter `T` | -LL | impl Complete for T { - | ^ +LL | impl Complete for T { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-43784-supertrait.stderr b/src/test/ui/issues/issue-43784-supertrait.stderr index 1795db32a57bd..2fb0583ee7d59 100644 --- a/src/test/ui/issues/issue-43784-supertrait.stderr +++ b/src/test/ui/issues/issue-43784-supertrait.stderr @@ -4,11 +4,10 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | impl Complete for T {} | ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/issue-43784-supertrait.rs:8:6 +help: consider restricting type parameter `T` | -LL | impl Complete for T {} - | ^ +LL | impl Complete for T {} + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/run-fail/issue-44216-add-instant.rs b/src/test/ui/issues/issue-44216-add-instant.rs similarity index 78% rename from src/test/run-fail/issue-44216-add-instant.rs rename to src/test/ui/issues/issue-44216-add-instant.rs index 76ad0a3d41bf8..c2f3598f645bb 100644 --- a/src/test/run-fail/issue-44216-add-instant.rs +++ b/src/test/ui/issues/issue-44216-add-instant.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:overflow +// ignore-emscripten no processes use std::time::{Instant, Duration}; diff --git a/src/test/run-fail/issue-44216-add-system-time.rs b/src/test/ui/issues/issue-44216-add-system-time.rs similarity index 78% rename from src/test/run-fail/issue-44216-add-system-time.rs rename to src/test/ui/issues/issue-44216-add-system-time.rs index aa861f7d5993e..9a88cb7c18916 100644 --- a/src/test/run-fail/issue-44216-add-system-time.rs +++ b/src/test/ui/issues/issue-44216-add-system-time.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:overflow +// ignore-emscripten no processes use std::time::{Duration, SystemTime}; diff --git a/src/test/run-fail/issue-44216-sub-instant.rs b/src/test/ui/issues/issue-44216-sub-instant.rs similarity index 78% rename from src/test/run-fail/issue-44216-sub-instant.rs rename to src/test/ui/issues/issue-44216-sub-instant.rs index 8bc1f47ae2d96..2decd88bbc06b 100644 --- a/src/test/run-fail/issue-44216-sub-instant.rs +++ b/src/test/ui/issues/issue-44216-sub-instant.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:overflow +// ignore-emscripten no processes use std::time::{Instant, Duration}; diff --git a/src/test/run-fail/issue-44216-sub-system-time.rs b/src/test/ui/issues/issue-44216-sub-system-time.rs similarity index 78% rename from src/test/run-fail/issue-44216-sub-system-time.rs rename to src/test/ui/issues/issue-44216-sub-system-time.rs index 37ab0e7c3f99f..e58a31a41a5e9 100644 --- a/src/test/run-fail/issue-44216-sub-system-time.rs +++ b/src/test/ui/issues/issue-44216-sub-system-time.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:overflow +// ignore-emscripten no processes use std::time::{Duration, SystemTime}; diff --git a/src/test/ui/issues/issue-45829/import-self.rs b/src/test/ui/issues/issue-45829/import-self.rs index 6cb18e1cdb7d9..2dc4331ced775 100644 --- a/src/test/ui/issues/issue-45829/import-self.rs +++ b/src/test/ui/issues/issue-45829/import-self.rs @@ -9,7 +9,7 @@ use foo::{self}; use foo as self; //~^ ERROR expected identifier -use foo::self; +use foo::self; //~ ERROR is defined multiple times //~^ ERROR `self` imports are only allowed within a { } list use foo::A; diff --git a/src/test/ui/issues/issue-45829/import-self.stderr b/src/test/ui/issues/issue-45829/import-self.stderr index 39522cd818392..158e81cdd9643 100644 --- a/src/test/ui/issues/issue-45829/import-self.stderr +++ b/src/test/ui/issues/issue-45829/import-self.stderr @@ -5,10 +5,19 @@ LL | use foo as self; | ^^^^ expected identifier, found keyword error[E0429]: `self` imports are only allowed within a { } list - --> $DIR/import-self.rs:12:5 + --> $DIR/import-self.rs:12:8 | LL | use foo::self; - | ^^^^^^^^^ + | ^^^^^^ + | +help: consider importing the module directly + | +LL | use foo; + | -- +help: alternatively, use the multi-path `use` syntax to import `self` + | +LL | use foo::{self}; + | ^ ^ error[E0255]: the name `foo` is defined multiple times --> $DIR/import-self.rs:6:11 @@ -25,6 +34,21 @@ help: you can use `as` to change the binding name of the import LL | use foo::{self as other_foo}; | ^^^^^^^^^^^^^^^^^ +error[E0255]: the name `foo` is defined multiple times + --> $DIR/import-self.rs:12:5 + | +LL | mod foo { + | ------- previous definition of the module `foo` here +... +LL | use foo::self; + | ^^^^^^^^^ `foo` reimported here + | + = note: `foo` must be defined only once in the type namespace of this module +help: you can use `as` to change the binding name of the import + | +LL | use foo as other_foo; + | ^^^^^^^^^^^^^^^^ + error[E0252]: the name `A` is defined multiple times --> $DIR/import-self.rs:16:11 | @@ -39,7 +63,7 @@ help: you can use `as` to change the binding name of the import LL | use foo::{self as OtherA}; | ^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0252, E0255, E0429. For more information about an error, try `rustc --explain E0252`. diff --git a/src/test/ui/issues/issue-47706.stderr b/src/test/ui/issues/issue-47706.stderr index 6cde93734667f..c84d8ecb4c9e6 100644 --- a/src/test/ui/issues/issue-47706.stderr +++ b/src/test/ui/issues/issue-47706.stderr @@ -14,7 +14,7 @@ LL | Bar(i32), | -------- takes 1 argument ... LL | fn foo(f: F) - | --- + | --- required by a bound in this LL | where LL | F: Fn(), | ---- required by this bound in `foo` diff --git a/src/test/ui/issues/issue-49934.stderr b/src/test/ui/issues/issue-49934.stderr index 64bf5214e6dd4..8a5596521ec55 100644 --- a/src/test/ui/issues/issue-49934.stderr +++ b/src/test/ui/issues/issue-49934.stderr @@ -36,3 +36,5 @@ warning: unused attribute LL | #[derive(Debug)] | ^^^^^^^^^^^^^^^^ +warning: 5 warnings emitted + diff --git a/src/test/ui/issues/issue-50599.rs b/src/test/ui/issues/issue-50599.rs index 78a20cf8ebb05..00588735b9a59 100644 --- a/src/test/ui/issues/issue-50599.rs +++ b/src/test/ui/issues/issue-50599.rs @@ -2,5 +2,4 @@ fn main() { const N: u32 = 1_000; const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize; //~ ERROR cannot find value let mut digits = [0u32; M]; - //~^ ERROR evaluation of constant value failed } diff --git a/src/test/ui/issues/issue-50599.stderr b/src/test/ui/issues/issue-50599.stderr index 5c8cac444387d..7ec567a06f09d 100644 --- a/src/test/ui/issues/issue-50599.stderr +++ b/src/test/ui/issues/issue-50599.stderr @@ -4,20 +4,13 @@ error[E0425]: cannot find value `LOG10_2` in module `std::f64` LL | const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize; | ^^^^^^^ not found in `std::f64` | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::f32::consts::LOG10_2; | LL | use std::f64::consts::LOG10_2; | -error[E0080]: evaluation of constant value failed - --> $DIR/issue-50599.rs:4:29 - | -LL | let mut digits = [0u32; M]; - | ^ referenced constant has errors - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0080, E0425. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/issues/issue-50993.stderr b/src/test/ui/issues/issue-50993.stderr index d7b33a22e9c31..45cbfef07097d 100644 --- a/src/test/ui/issues/issue-50993.stderr +++ b/src/test/ui/issues/issue-50993.stderr @@ -1,2 +1,4 @@ warning: dropping unsupported crate type `dylib` for target `thumbv7em-none-eabihf` +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-51345-2.rs b/src/test/ui/issues/issue-51345-2.rs new file mode 100644 index 0000000000000..52f342a85005e --- /dev/null +++ b/src/test/ui/issues/issue-51345-2.rs @@ -0,0 +1,8 @@ +// run-fail +// error-pattern: thread 'main' panicked at 'explicit panic' +// ignore-emscripten no processes + +fn main() { + let mut vec = vec![]; + vec.push((vec.len(), panic!())); +} diff --git a/src/test/ui/issues/issue-52060.rs b/src/test/ui/issues/issue-52060.rs index 2688049fcc9ff..fed08902c8b9d 100644 --- a/src/test/ui/issues/issue-52060.rs +++ b/src/test/ui/issues/issue-52060.rs @@ -4,6 +4,5 @@ static A: &'static [u32] = &[1]; static B: [u32; 1] = [0; A.len()]; //~^ ERROR [E0013] //~| ERROR evaluation of constant value failed -//~| ERROR mismatched types fn main() {} diff --git a/src/test/ui/issues/issue-52060.stderr b/src/test/ui/issues/issue-52060.stderr index e076e183937f2..502825e9766e3 100644 --- a/src/test/ui/issues/issue-52060.stderr +++ b/src/test/ui/issues/issue-52060.stderr @@ -12,16 +12,7 @@ error[E0080]: evaluation of constant value failed LL | static B: [u32; 1] = [0; A.len()]; | ^ constant accesses static -error[E0308]: mismatched types - --> $DIR/issue-52060.rs:4:22 - | -LL | static B: [u32; 1] = [0; A.len()]; - | ^^^^^^^^^^^^ expected `1usize`, found `A.len()` - | - = note: expected array `[u32; 1]` - found array `[u32; _]` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0013, E0080, E0308. +Some errors have detailed explanations: E0013, E0080. For more information about an error, try `rustc --explain E0013`. diff --git a/src/test/ui/issues/issue-53787-inline-assembler-macro.rs b/src/test/ui/issues/issue-53787-inline-assembler-macro.rs index d911ac5efbe03..38591b0a9f84e 100644 --- a/src/test/ui/issues/issue-53787-inline-assembler-macro.rs +++ b/src/test/ui/issues/issue-53787-inline-assembler-macro.rs @@ -3,12 +3,12 @@ // build-fail // ignore-emscripten -#![feature(asm)] +#![feature(llvm_asm)] macro_rules! fake_jump { ($id:expr) => { unsafe { - asm!( + llvm_asm!( " jmp $0 lea eax, [ebx] diff --git a/src/test/ui/issues/issue-54094.rs b/src/test/ui/issues/issue-54094.rs new file mode 100644 index 0000000000000..ec38dc40e610a --- /dev/null +++ b/src/test/ui/issues/issue-54094.rs @@ -0,0 +1,14 @@ +// check-pass +trait Zoo { + type X; +} + +impl Zoo for u16 { + type X = usize; +} + +fn foo(abc: ::X) {} + +fn main() { + let x: *const u8 = foo as _; +} diff --git a/src/test/ui/issues/issue-54954.rs b/src/test/ui/issues/issue-54954.rs index 3d6355f5c5978..00805eb5dc90d 100644 --- a/src/test/ui/issues/issue-54954.rs +++ b/src/test/ui/issues/issue-54954.rs @@ -11,8 +11,6 @@ trait Tt { } fn f(z: [f32; ARR_LEN]) -> [f32; ARR_LEN] { - //~^ ERROR evaluation of constant value failed - //~| ERROR evaluation of constant value failed z } diff --git a/src/test/ui/issues/issue-54954.stderr b/src/test/ui/issues/issue-54954.stderr index 4967b82216e46..29d439b457ff6 100644 --- a/src/test/ui/issues/issue-54954.stderr +++ b/src/test/ui/issues/issue-54954.stderr @@ -11,23 +11,11 @@ LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type ... LL | const fn const_val() -> usize { - | --------- - required by this bound in `Tt::const_val` + | - required by this bound in `Tt::const_val` | - = note: cannot resolve `_: Tt` + = note: cannot satisfy `_: Tt` -error[E0080]: evaluation of constant value failed - --> $DIR/issue-54954.rs:13:15 - | -LL | fn f(z: [f32; ARR_LEN]) -> [f32; ARR_LEN] { - | ^^^^^^^ referenced constant has errors - -error[E0080]: evaluation of constant value failed - --> $DIR/issue-54954.rs:13:34 - | -LL | fn f(z: [f32; ARR_LEN]) -> [f32; ARR_LEN] { - | ^^^^^^^ referenced constant has errors - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0080, E0283, E0379. -For more information about an error, try `rustc --explain E0080`. +Some errors have detailed explanations: E0283, E0379. +For more information about an error, try `rustc --explain E0283`. diff --git a/src/test/ui/issues/issue-55511.rs b/src/test/ui/issues/issue-55511.rs index 055886bf3676c..7dfa9c7bcdffe 100644 --- a/src/test/ui/issues/issue-55511.rs +++ b/src/test/ui/issues/issue-55511.rs @@ -14,8 +14,6 @@ fn main() { //~^ ERROR `a` does not live long enough [E0597] match b { <() as Foo<'static>>::C => { } - //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` - //~| WARN will become a hard error in a future release _ => { } } } diff --git a/src/test/ui/issues/issue-55511.stderr b/src/test/ui/issues/issue-55511.stderr index 91b81ba6943ad..bf3e58e8cdb19 100644 --- a/src/test/ui/issues/issue-55511.stderr +++ b/src/test/ui/issues/issue-55511.stderr @@ -1,17 +1,3 @@ -warning: to use a constant of type `std::cell::Cell` in a pattern, `std::cell::Cell` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/issue-55511.rs:16:9 - | -LL | <() as Foo<'static>>::C => { } - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/issue-55511.rs:1:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = 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 #62411 - error[E0597]: `a` does not live long enough --> $DIR/issue-55511.rs:13:28 | diff --git a/src/test/ui/issues/issue-58022.stderr b/src/test/ui/issues/issue-58022.stderr index 70a7c38b83425..fb31467ec47fa 100644 --- a/src/test/ui/issues/issue-58022.stderr +++ b/src/test/ui/issues/issue-58022.stderr @@ -16,7 +16,7 @@ LL | fn new(slice: &[u8; Foo::SIZE]) -> Self; | cannot infer type | help: use the fully qualified path to an implementation: `::SIZE` | - = note: cannot resolve `_: Foo` + = note: cannot satisfy `_: Foo` = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-59508-1.rs b/src/test/ui/issues/issue-59508-1.rs index 4fbed9b08f215..a687a9e3be12c 100644 --- a/src/test/ui/issues/issue-59508-1.rs +++ b/src/test/ui/issues/issue-59508-1.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete // This test checks that generic parameter re-ordering diagnostic suggestions mention that // consts come after types and lifetimes when the `const_generics` feature is enabled. diff --git a/src/test/ui/issues/issue-59508-1.stderr b/src/test/ui/issues/issue-59508-1.stderr index dd78c7c83134c..85db20b13fb4c 100644 --- a/src/test/ui/issues/issue-59508-1.stderr +++ b/src/test/ui/issues/issue-59508-1.stderr @@ -4,13 +4,14 @@ error: lifetime parameters must be declared prior to type parameters LL | pub fn do_things() { | ----^^--^^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b: 'a, T>` -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-59508-1.rs:2:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/issues/issue-60218.stderr b/src/test/ui/issues/issue-60218.stderr index a9970cc109699..77b9d9c4aaa3a 100644 --- a/src/test/ui/issues/issue-60218.stderr +++ b/src/test/ui/issues/issue-60218.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `for<'t> $DIR/issue-60218.rs:18:5 | LL | pub fn trigger_error(iterable: I, functor: F) - | ------------- + | ------------- required by a bound in this ... LL | for<'t> ::IntoIter, F> as Iterator>::Item: Foo, | --- required by this bound in `trigger_error` diff --git a/src/test/ui/issues/issue-60283.stderr b/src/test/ui/issues/issue-60283.stderr index d13dcd54a479a..e74a34e247a67 100644 --- a/src/test/ui/issues/issue-60283.stderr +++ b/src/test/ui/issues/issue-60283.stderr @@ -2,7 +2,7 @@ error[E0631]: type mismatch in function arguments --> $DIR/issue-60283.rs:14:13 | LL | pub fn foo(_: T, _: F) - | --- + | --- required by a bound in this LL | where T: for<'a> Trait<'a>, LL | F: for<'a> FnMut(>::Item) {} | ------------------------------------- required by this bound in `foo` @@ -17,7 +17,7 @@ error[E0271]: type mismatch resolving `for<'a> } as s --> $DIR/issue-60283.rs:14:5 | LL | pub fn foo(_: T, _: F) - | --- + | --- required by a bound in this LL | where T: for<'a> Trait<'a>, LL | F: for<'a> FnMut(>::Item) {} | ----------------------------- required by this bound in `foo` diff --git a/src/test/ui/issues/issue-6458-1.rs b/src/test/ui/issues/issue-6458-1.rs new file mode 100644 index 0000000000000..184e4832b90b0 --- /dev/null +++ b/src/test/ui/issues/issue-6458-1.rs @@ -0,0 +1,8 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +fn foo(t: T) {} +fn main() { + foo(panic!()) +} diff --git a/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr b/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr index feaf3dc753ffb..83d8770b2e03b 100644 --- a/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr +++ b/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | async::r#struct(&r#fn {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | await::r#struct(&r#fn {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/issues/issue-65673.stderr b/src/test/ui/issues/issue-65673.stderr index a556e35b6a944..114f2d62e561a 100644 --- a/src/test/ui/issues/issue-65673.stderr +++ b/src/test/ui/issues/issue-65673.stderr @@ -1,13 +1,13 @@ error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time - --> $DIR/issue-65673.rs:9:5 + --> $DIR/issue-65673.rs:9:16 | +LL | trait WithType { + | -------- required by a bound in this LL | type Ctx; - | --- associated type defined here + | --------- required by this bound in `WithType` ... -LL | impl WithType for T { - | ---------------------- in this `impl` item LL | type Ctx = dyn Alias; - | ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` = note: to learn more, visit diff --git a/src/test/ui/issues/issue-66667-function-cmp-cycle.rs b/src/test/ui/issues/issue-66667-function-cmp-cycle.rs new file mode 100644 index 0000000000000..7b025be11a09e --- /dev/null +++ b/src/test/ui/issues/issue-66667-function-cmp-cycle.rs @@ -0,0 +1,16 @@ +fn first() { + second == 1 //~ ERROR binary operation + //~^ ERROR mismatched types +} + +fn second() { + first == 1 //~ ERROR binary operation + //~^ ERROR mismatched types +} + +fn bar() { + bar == 1 //~ ERROR binary operation + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/issues/issue-66667-function-cmp-cycle.stderr b/src/test/ui/issues/issue-66667-function-cmp-cycle.stderr new file mode 100644 index 0000000000000..887699ef5ce85 --- /dev/null +++ b/src/test/ui/issues/issue-66667-function-cmp-cycle.stderr @@ -0,0 +1,55 @@ +error[E0369]: binary operation `==` cannot be applied to type `fn() {second}` + --> $DIR/issue-66667-function-cmp-cycle.rs:2:12 + | +LL | second == 1 + | ------ ^^ - {integer} + | | + | fn() {second} + +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:2:15 + | +LL | second == 1 + | ^ expected fn item, found integer + | + = note: expected fn item `fn() {second}` + found type `{integer}` + +error[E0369]: binary operation `==` cannot be applied to type `fn() {first}` + --> $DIR/issue-66667-function-cmp-cycle.rs:7:11 + | +LL | first == 1 + | ----- ^^ - {integer} + | | + | fn() {first} + +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:7:14 + | +LL | first == 1 + | ^ expected fn item, found integer + | + = note: expected fn item `fn() {first}` + found type `{integer}` + +error[E0369]: binary operation `==` cannot be applied to type `fn() {bar}` + --> $DIR/issue-66667-function-cmp-cycle.rs:12:9 + | +LL | bar == 1 + | --- ^^ - {integer} + | | + | fn() {bar} + +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:12:12 + | +LL | bar == 1 + | ^ expected fn item, found integer + | + = note: expected fn item `fn() {bar}` + found type `{integer}` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-66706.rs b/src/test/ui/issues/issue-66706.rs new file mode 100644 index 0000000000000..02305191f6ebc --- /dev/null +++ b/src/test/ui/issues/issue-66706.rs @@ -0,0 +1,26 @@ +fn a() { + [0; [|_: _ &_| ()].len()] + //~^ ERROR expected `,`, found `&` + //~| ERROR type annotations needed + //~| ERROR mismatched types +} + +fn b() { + [0; [|f @ &ref _| {} ; 0 ].len() ]; + //~^ ERROR expected identifier, found reserved identifier `_` +} + +fn c() { + [0; [|&_: _ &_| {}; 0 ].len()] + //~^ ERROR expected `,`, found `&` + //~| ERROR mismatched types +} + +fn d() { + [0; match [|f @ &ref _| () ] {} ] + //~^ ERROR expected identifier, found reserved identifier `_` + //~| ERROR `match` is not allowed in a `const` + //~| ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/issues/issue-66706.stderr b/src/test/ui/issues/issue-66706.stderr new file mode 100644 index 0000000000000..ea461cc5d03fa --- /dev/null +++ b/src/test/ui/issues/issue-66706.stderr @@ -0,0 +1,71 @@ +error: expected `,`, found `&` + --> $DIR/issue-66706.rs:2:16 + | +LL | [0; [|_: _ &_| ()].len()] + | -^ expected `,` + | | + | help: missing `,` + +error: expected identifier, found reserved identifier `_` + --> $DIR/issue-66706.rs:9:20 + | +LL | [0; [|f @ &ref _| {} ; 0 ].len() ]; + | ^ expected identifier, found reserved identifier + +error: expected `,`, found `&` + --> $DIR/issue-66706.rs:14:17 + | +LL | [0; [|&_: _ &_| {}; 0 ].len()] + | -^ expected `,` + | | + | help: missing `,` + +error: expected identifier, found reserved identifier `_` + --> $DIR/issue-66706.rs:20:26 + | +LL | [0; match [|f @ &ref _| () ] {} ] + | ^ expected identifier, found reserved identifier + +error[E0658]: `match` is not allowed in a `const` + --> $DIR/issue-66706.rs:20:9 + | +LL | [0; match [|f @ &ref _| () ] {} ] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #49146 for more information + = help: add `#![feature(const_if_match)]` to the crate attributes to enable + +error[E0282]: type annotations needed + --> $DIR/issue-66706.rs:2:11 + | +LL | [0; [|_: _ &_| ()].len()] + | ^ consider giving this closure parameter a type + +error[E0308]: mismatched types + --> $DIR/issue-66706.rs:2:5 + | +LL | fn a() { + | - help: try adding a return type: `-> [{integer}; _]` +LL | [0; [|_: _ &_| ()].len()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found array `[{integer}; _]` + +error[E0308]: mismatched types + --> $DIR/issue-66706.rs:14:5 + | +LL | fn c() { + | - help: try adding a return type: `-> [{integer}; _]` +LL | [0; [|&_: _ &_| {}; 0 ].len()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found array `[{integer}; _]` + +error[E0308]: mismatched types + --> $DIR/issue-66706.rs:20:5 + | +LL | fn d() { + | - help: try adding a return type: `-> [{integer}; _]` +LL | [0; match [|f @ &ref _| () ] {} ] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found array `[{integer}; _]` + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0282, E0308, E0658. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/issues/issue-6738.stderr b/src/test/ui/issues/issue-6738.stderr index 82b670bd03bc5..a428ff7e91fad 100644 --- a/src/test/ui/issues/issue-6738.stderr +++ b/src/test/ui/issues/issue-6738.stderr @@ -6,7 +6,10 @@ LL | self.x += v.x; | | | cannot use `+=` on type `T` | - = note: `T` might need a bound for `std::ops::AddAssign` +help: consider restricting type parameter `T` + | +LL | impl Foo { + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-69130.rs b/src/test/ui/issues/issue-69130.rs new file mode 100644 index 0000000000000..9552e8ec2a876 --- /dev/null +++ b/src/test/ui/issues/issue-69130.rs @@ -0,0 +1,7 @@ +// Issue 69130: character indexing bug in rustc_errors::CodeSuggestion::splice_lines(). + +enum F { +M (§& u8)} +//~^ ERROR unknown start of token +//~| missing lifetime specifier +fn main() {} diff --git a/src/test/ui/issues/issue-69130.stderr b/src/test/ui/issues/issue-69130.stderr new file mode 100644 index 0000000000000..a4700a5ed1da0 --- /dev/null +++ b/src/test/ui/issues/issue-69130.stderr @@ -0,0 +1,21 @@ +error: unknown start of token: \u{a7} + --> $DIR/issue-69130.rs:4:4 + | +LL | M (§& u8)} + | ^ + +error[E0106]: missing lifetime specifier + --> $DIR/issue-69130.rs:4:5 + | +LL | M (§& u8)} + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | enum F<'a> { +LL | M (§&'a u8)} + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/issues/issue-69306.stderr b/src/test/ui/issues/issue-69306.stderr index a2a42739ca8be..58e85ec700d2d 100644 --- a/src/test/ui/issues/issue-69306.stderr +++ b/src/test/ui/issues/issue-69306.stderr @@ -8,8 +8,6 @@ LL | const C: S0 = Self(0); | = note: expected type parameter `T` found type `{integer}` - = 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[E0308]: mismatched types --> $DIR/issue-69306.rs:5:23 @@ -21,8 +19,6 @@ LL | const C: S0 = Self(0); | = note: expected struct `S0` found struct `S0` - = 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[E0308]: mismatched types --> $DIR/issue-69306.rs:10:14 @@ -35,8 +31,6 @@ LL | Self(0); | = note: expected type parameter `T` found type `{integer}` - = 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[E0308]: mismatched types --> $DIR/issue-69306.rs:27:14 @@ -49,8 +43,6 @@ LL | Self(0); | = note: expected type parameter `T` found type `{integer}` - = 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[E0308]: mismatched types --> $DIR/issue-69306.rs:33:32 @@ -62,8 +54,6 @@ LL | const C: S1 = Self(0, 1); | = note: expected type parameter `T` found type `{integer}` - = 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[E0308]: mismatched types --> $DIR/issue-69306.rs:33:27 @@ -75,8 +65,6 @@ LL | const C: S1 = Self(0, 1); | = note: expected struct `S1` found struct `S1` - = 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[E0308]: mismatched types --> $DIR/issue-69306.rs:41:14 diff --git a/src/test/ui/issues/issue-69455.rs b/src/test/ui/issues/issue-69455.rs new file mode 100644 index 0000000000000..f1935ae253463 --- /dev/null +++ b/src/test/ui/issues/issue-69455.rs @@ -0,0 +1,30 @@ +// Regression test for #69455: projection predicate was not satisfied. +// Compiler should indicate the correct location of the +// unsatisfied projection predicate + +pub trait Test { + type Output; + + fn test(self, rhs: Rhs) -> Self::Output; +} + +impl Test for u64 { + type Output = u64; + + fn test(self, other: u32) -> u64 { + self + (other as u64) + } +} + +impl Test for u64 { + type Output = u64; + + fn test(self, other: u64) -> u64 { + (self + other) as u64 + } +} + +fn main() { + let xs: Vec = vec![1, 2, 3]; + println!("{}", 23u64.test(xs.iter().sum())); //~ ERROR: type annotations needed +} diff --git a/src/test/ui/issues/issue-69455.stderr b/src/test/ui/issues/issue-69455.stderr new file mode 100644 index 0000000000000..430bbcabf83e8 --- /dev/null +++ b/src/test/ui/issues/issue-69455.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `>::Output == _` + --> $DIR/issue-69455.rs:29:26 + | +LL | println!("{}", 23u64.test(xs.iter().sum())); + | ^^^^ cannot satisfy `>::Output == _` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/src/test/ui/issues/issue-69683.rs b/src/test/ui/issues/issue-69683.rs new file mode 100644 index 0000000000000..cc7f1fa0f5580 --- /dev/null +++ b/src/test/ui/issues/issue-69683.rs @@ -0,0 +1,32 @@ +pub trait Element { + type Array; +} + +impl Element<()> for T { + type Array = T; +} + +impl, S> Element<[S; 3]> for T { + type Array = [T::Array; 3]; +} + +trait Foo +where + u8: Element, +{ + fn foo(self, x: >::Array); +} + +impl Foo for u16 +where + u8: Element, +{ + fn foo(self, _: >::Array) {} +} + +fn main() { + let b: [u8; 3] = [0u8; 3]; + + 0u16.foo(b); //~ ERROR type annotations needed + //>::foo(0u16, b); +} diff --git a/src/test/ui/issues/issue-69683.stderr b/src/test/ui/issues/issue-69683.stderr new file mode 100644 index 0000000000000..776370331a4c9 --- /dev/null +++ b/src/test/ui/issues/issue-69683.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `>::Array == [u8; 3]` + --> $DIR/issue-69683.rs:30:10 + | +LL | 0u16.foo(b); + | ^^^ cannot satisfy `>::Array == [u8; 3]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/src/test/ui/issues/issue-69725.rs b/src/test/ui/issues/issue-69725.rs index a8e72e9459e33..b8130b41f2167 100644 --- a/src/test/ui/issues/issue-69725.rs +++ b/src/test/ui/issues/issue-69725.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - // aux-build:issue-69725.rs extern crate issue_69725; diff --git a/src/test/ui/issues/issue-69725.stderr b/src/test/ui/issues/issue-69725.stderr index 439fae9511167..d9d61fe66f78e 100644 --- a/src/test/ui/issues/issue-69725.stderr +++ b/src/test/ui/issues/issue-69725.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `issue_69725::Struct` in the current scope - --> $DIR/issue-69725.rs:12:32 + --> $DIR/issue-69725.rs:7:32 | LL | let _ = Struct::::new().clone(); | ^^^^^ method not found in `issue_69725::Struct` diff --git a/src/test/ui/issues/issue-69841.rs b/src/test/ui/issues/issue-69841.rs new file mode 100644 index 0000000000000..1aca16ca80451 --- /dev/null +++ b/src/test/ui/issues/issue-69841.rs @@ -0,0 +1,31 @@ +// This is a regression test for issue rust-lang/rust#69841, which exposed an +// LLVM bug which needed a fix to be backported. + +// run-pass +// no-system-llvm + +fn main() { + let buffer = [49u8, 10]; + let mut a : u64 = 0; + 'read: loop { + for c in &buffer { + match c { + 48..=57 => { + a*= 10; + a+= *c as u64 - 48; + } + 10 => { + break 'read; + } + _ => { + unsafe { std::hint::unreachable_unchecked() }; + } + } + } + } + if a == 1 { + println!("What did you expect?"); + } else { + panic!("this should be unreachable."); + } +} diff --git a/src/test/ui/issues/issue-70041.stderr b/src/test/ui/issues/issue-70041.stderr index b180175c5ab76..ecd618eae8b07 100644 --- a/src/test/ui/issues/issue-70041.stderr +++ b/src/test/ui/issues/issue-70041.stderr @@ -17,3 +17,5 @@ LL | use regex; | = note: `#[warn(unused_imports)]` on by default +warning: 2 warnings emitted + diff --git a/src/test/ui/issues/issue-70093.rs b/src/test/ui/issues/issue-70093.rs new file mode 100644 index 0000000000000..95ab86ebcb1f4 --- /dev/null +++ b/src/test/ui/issues/issue-70093.rs @@ -0,0 +1,8 @@ +// run-pass +// compile-flags: -Zlink-native-libraries=no -Cdefault-linker-libraries=yes +// ignore-windows - this will probably only work on unixish systems + +#[link(name = "some-random-non-existent-library", kind = "static")] +extern "C" {} + +fn main() {} diff --git a/src/test/ui/issues/issue-70673.rs b/src/test/ui/issues/issue-70673.rs new file mode 100644 index 0000000000000..3561f40127737 --- /dev/null +++ b/src/test/ui/issues/issue-70673.rs @@ -0,0 +1,12 @@ +// Regression test for https://github.com/rust-lang/rust/issues/70673. + +// run-pass + +#![feature(thread_local)] + +#[thread_local] +static A: &u8 = &42; + +fn main() { + dbg!(*A); +} diff --git a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.rs b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.rs new file mode 100644 index 0000000000000..c2683157f797f --- /dev/null +++ b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.rs @@ -0,0 +1,10 @@ +fn a() -> i32 { + 3 +} + +pub fn main() { + assert_eq!(a, 0); + //~^ ERROR binary operation `==` cannot + //~| ERROR mismatched types + //~| ERROR doesn't implement +} diff --git a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr new file mode 100644 index 0000000000000..467c15cc52d45 --- /dev/null +++ b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr @@ -0,0 +1,41 @@ +error[E0369]: binary operation `==` cannot be applied to type `fn() -> i32 {a}` + --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5 + | +LL | assert_eq!(a, 0); + | ^^^^^^^^^^^^^^^^^ + | | + | fn() -> i32 {a} + | {integer} + | help: you might have forgotten to call this function: `*left_val()` + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5 + | +LL | assert_eq!(a, 0); + | ^^^^^^^^^^^^^^^^^ expected fn item, found integer + | + = note: expected fn item `fn() -> i32 {a}` + found type `i32` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `fn() -> i32 {a}` doesn't implement `std::fmt::Debug` + --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5 + | +LL | fn a() -> i32 { + | - consider calling this function +... +LL | assert_eq!(a, 0); + | ^^^^^^^^^^^^^^^^^ `fn() -> i32 {a}` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | + = help: the trait `std::fmt::Debug` is not implemented for `fn() -> i32 {a}` + = help: use parentheses to call the function: `a()` + = note: required because of the requirements on the impl of `std::fmt::Debug` for `&fn() -> i32 {a}` + = note: required by `std::fmt::Debug::fmt` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308, E0369. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/issues/issue-71036.rs b/src/test/ui/issues/issue-71036.rs new file mode 100644 index 0000000000000..01d1cff42e4ba --- /dev/null +++ b/src/test/ui/issues/issue-71036.rs @@ -0,0 +1,17 @@ +#![feature(unsize, dispatch_from_dyn)] + +use std::marker::Unsize; +use std::ops::DispatchFromDyn; + +#[allow(unused)] +struct Foo<'a, T: ?Sized> { + _inner: &'a &'a T, +} + +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn> for Foo<'a, T> {} +//~^ ERROR the trait bound `&'a T: std::marker::Unsize<&'a U>` is not satisfied +//~| NOTE the trait `std::marker::Unsize<&'a U>` is not implemented for `&'a T` +//~| NOTE all implementations of `Unsize` are provided automatically by the compiler +//~| NOTE required because of the requirements on the impl + +fn main() {} diff --git a/src/test/ui/issues/issue-71036.stderr b/src/test/ui/issues/issue-71036.stderr new file mode 100644 index 0000000000000..57cf24689454e --- /dev/null +++ b/src/test/ui/issues/issue-71036.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `&'a T: std::marker::Unsize<&'a U>` is not satisfied + --> $DIR/issue-71036.rs:11:1 + | +LL | impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn> for Foo<'a, T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unsize<&'a U>` is not implemented for `&'a T` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: required because of the requirements on the impl of `std::ops::DispatchFromDyn<&'a &'a U>` for `&'a &'a T` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/issues/issue-71406.rs b/src/test/ui/issues/issue-71406.rs new file mode 100644 index 0000000000000..6266112c3a86c --- /dev/null +++ b/src/test/ui/issues/issue-71406.rs @@ -0,0 +1,6 @@ +use std::sync::mpsc; + +fn main() { + let (tx, rx) = mpsc::channel::new(1); + //~^ ERROR expected type, found function `channel` in `mpsc` +} diff --git a/src/test/ui/issues/issue-71406.stderr b/src/test/ui/issues/issue-71406.stderr new file mode 100644 index 0000000000000..918163b609473 --- /dev/null +++ b/src/test/ui/issues/issue-71406.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: expected type, found function `channel` in `mpsc` + --> $DIR/issue-71406.rs:4:26 + | +LL | let (tx, rx) = mpsc::channel::new(1); + | ^^^^^^^ expected type, found function `channel` in `mpsc` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/issues/issue-71584.rs b/src/test/ui/issues/issue-71584.rs new file mode 100644 index 0000000000000..c96cd598f0ce0 --- /dev/null +++ b/src/test/ui/issues/issue-71584.rs @@ -0,0 +1,5 @@ +fn main() { + let n: u32 = 1; + let mut d: u64 = 2; + d = d % n.into(); //~ ERROR type annotations needed +} diff --git a/src/test/ui/issues/issue-71584.stderr b/src/test/ui/issues/issue-71584.stderr new file mode 100644 index 0000000000000..c162d338a93be --- /dev/null +++ b/src/test/ui/issues/issue-71584.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `>::Output == u64` + --> $DIR/issue-71584.rs:4:11 + | +LL | d = d % n.into(); + | ^ cannot satisfy `>::Output == u64` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/src/test/ui/issues/issue-71676-1.fixed b/src/test/ui/issues/issue-71676-1.fixed new file mode 100644 index 0000000000000..cbc0e8c061b82 --- /dev/null +++ b/src/test/ui/issues/issue-71676-1.fixed @@ -0,0 +1,53 @@ +// run-rustfix +use std::ops::Deref; +use std::ops::DerefMut; +struct Bar(u8); +struct Foo(Bar); +struct Emm(Foo); +impl Deref for Bar{ + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Emm { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for Bar{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Emm { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +fn main() { + // Suggest dereference with arbitrary mutability + let a = Emm(Foo(Bar(0))); + let _: *const u8 = &***a; //~ ERROR mismatched types + + let mut a = Emm(Foo(Bar(0))); + let _: *mut u8 = &mut ***a; //~ ERROR mismatched types + + let a = Emm(Foo(Bar(0))); + let _: *const u8 = &***a; //~ ERROR mismatched types + + let mut a = Emm(Foo(Bar(0))); + let _: *mut u8 = &mut ***a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-71676-1.rs b/src/test/ui/issues/issue-71676-1.rs new file mode 100644 index 0000000000000..6e87c7174c633 --- /dev/null +++ b/src/test/ui/issues/issue-71676-1.rs @@ -0,0 +1,53 @@ +// run-rustfix +use std::ops::Deref; +use std::ops::DerefMut; +struct Bar(u8); +struct Foo(Bar); +struct Emm(Foo); +impl Deref for Bar{ + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Emm { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for Bar{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Emm { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +fn main() { + // Suggest dereference with arbitrary mutability + let a = Emm(Foo(Bar(0))); + let _: *const u8 = &a; //~ ERROR mismatched types + + let mut a = Emm(Foo(Bar(0))); + let _: *mut u8 = &a; //~ ERROR mismatched types + + let a = Emm(Foo(Bar(0))); + let _: *const u8 = &mut a; //~ ERROR mismatched types + + let mut a = Emm(Foo(Bar(0))); + let _: *mut u8 = &mut a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-71676-1.stderr b/src/test/ui/issues/issue-71676-1.stderr new file mode 100644 index 0000000000000..bbabc2202dc84 --- /dev/null +++ b/src/test/ui/issues/issue-71676-1.stderr @@ -0,0 +1,55 @@ +error[E0308]: mismatched types + --> $DIR/issue-71676-1.rs:43:24 + | +LL | let _: *const u8 = &a; + | --------- ^^ + | | | + | | expected `u8`, found struct `Emm` + | | help: consider dereferencing: `&***a` + | expected due to this + | + = note: expected raw pointer `*const u8` + found reference `&Emm` + +error[E0308]: mismatched types + --> $DIR/issue-71676-1.rs:46:22 + | +LL | let _: *mut u8 = &a; + | ------- ^^ + | | | + | | types differ in mutability + | | help: consider dereferencing: `&mut ***a` + | expected due to this + | + = note: expected raw pointer `*mut u8` + found reference `&Emm` + +error[E0308]: mismatched types + --> $DIR/issue-71676-1.rs:49:24 + | +LL | let _: *const u8 = &mut a; + | --------- ^^^^^^ + | | | + | | expected `u8`, found struct `Emm` + | | help: consider dereferencing: `&***a` + | expected due to this + | + = note: expected raw pointer `*const u8` + found mutable reference `&mut Emm` + +error[E0308]: mismatched types + --> $DIR/issue-71676-1.rs:52:22 + | +LL | let _: *mut u8 = &mut a; + | ------- ^^^^^^ + | | | + | | expected `u8`, found struct `Emm` + | | help: consider dereferencing: `&mut ***a` + | expected due to this + | + = note: expected raw pointer `*mut u8` + found mutable reference `&mut Emm` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-71676-2.rs b/src/test/ui/issues/issue-71676-2.rs new file mode 100644 index 0000000000000..f3183899dc523 --- /dev/null +++ b/src/test/ui/issues/issue-71676-2.rs @@ -0,0 +1,42 @@ +use std::ops::Deref; +use std::ops::DerefMut; +struct Bar(u8); +struct Foo(Bar); +struct Emm(Foo); +impl Deref for Bar{ + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Emm { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for Bar{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Emm { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +fn main() { + let a = Emm(Foo(Bar(0))); + let _: *mut u8 = &a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-71676-2.stderr b/src/test/ui/issues/issue-71676-2.stderr new file mode 100644 index 0000000000000..ebdd345809af5 --- /dev/null +++ b/src/test/ui/issues/issue-71676-2.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/issue-71676-2.rs:41:22 + | +LL | let _: *mut u8 = &a; + | ------- ^^ + | | | + | | types differ in mutability + | | help: consider dereferencing: `&mut ***a` + | expected due to this + | + = note: expected raw pointer `*mut u8` + found reference `&Emm` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-72002.rs b/src/test/ui/issues/issue-72002.rs new file mode 100644 index 0000000000000..54ff89355ff3a --- /dev/null +++ b/src/test/ui/issues/issue-72002.rs @@ -0,0 +1,29 @@ +// check-pass +struct Indexable; + +impl Indexable { + fn boo(&mut self) {} +} + +impl std::ops::Index<&str> for Indexable { + type Output = Indexable; + + fn index(&self, field: &str) -> &Indexable { + self + } +} + +impl std::ops::IndexMut<&str> for Indexable { + fn index_mut(&mut self, field: &str) -> &mut Indexable { + self + } +} + +fn main() { + let mut v = Indexable; + let field = "hello".to_string(); + + v[field.as_str()].boo(); + + v[&field].boo(); // < This should work +} diff --git a/src/test/ui/issues/issue-7364.stderr b/src/test/ui/issues/issue-7364.stderr index 1f1079555a91f..efff2c24525e8 100644 --- a/src/test/ui/issues/issue-7364.stderr +++ b/src/test/ui/issues/issue-7364.stderr @@ -9,6 +9,8 @@ error[E0019]: static contains unimplemented expression type | LL | static boxed: Box> = box RefCell::new(0); | ^^^^^^^^^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0277]: `std::cell::RefCell` cannot be shared between threads safely --> $DIR/issue-7364.rs:6:1 diff --git a/src/test/ui/issues/issue-7607-1.rs b/src/test/ui/issues/issue-7607-1.rs index 1571cd2bbf683..5221f2c529bc6 100644 --- a/src/test/ui/issues/issue-7607-1.rs +++ b/src/test/ui/issues/issue-7607-1.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl struct Foo { x: isize } diff --git a/src/test/ui/issues/issue-7607-1.stderr b/src/test/ui/issues/issue-7607-1.stderr index 94f489e209e32..e86896a5681d1 100644 --- a/src/test/ui/issues/issue-7607-1.stderr +++ b/src/test/ui/issues/issue-7607-1.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Fo` in this scope - --> $DIR/issue-7607-1.rs:9:6 + --> $DIR/issue-7607-1.rs:5:6 | LL | impl Fo { | ^^ help: a trait with a similar name exists: `Fn` diff --git a/src/test/ui/issues/issue-811.rs b/src/test/ui/issues/issue-811.rs new file mode 100644 index 0000000000000..f929d388819a9 --- /dev/null +++ b/src/test/ui/issues/issue-811.rs @@ -0,0 +1,26 @@ +// run-fail +// error-pattern:quux +// ignore-emscripten no processes + +use std::marker::PhantomData; + +fn test00_start(ch: Chan, message: isize) { + send(ch, message); +} + +type TaskId = isize; +type PortId = isize; + +struct Chan { + task: TaskId, + port: PortId, + marker: PhantomData<*mut T>, +} + +fn send(_ch: Chan, _data: T) { + panic!(); +} + +fn main() { + panic!("quux"); +} diff --git a/src/test/ui/issues/issue-8727.stderr b/src/test/ui/issues/issue-8727.stderr index 2fd6ea5dc82b8..ee0672c598d5c 100644 --- a/src/test/ui/issues/issue-8727.stderr +++ b/src/test/ui/issues/issue-8727.stderr @@ -17,5 +17,5 @@ LL | | generic::>(); LL | | } | |_^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/issues/issue-9129.rs b/src/test/ui/issues/issue-9129.rs index 3d87e1c203783..9a0376ad51f75 100644 --- a/src/test/ui/issues/issue-9129.rs +++ b/src/test/ui/issues/issue-9129.rs @@ -12,7 +12,7 @@ impl bomb for S { fn boom(&self, _: Ident) { } } pub struct Ident { name: usize } -// macro_rules! int3 { () => ( unsafe { asm!( "int3" ); } ) } +// macro_rules! int3 { () => ( unsafe { llvm_asm!( "int3" ); } ) } macro_rules! int3 { () => ( { } ) } fn Ident_new() -> Ident { diff --git a/src/test/run-fail/issue-948.rs b/src/test/ui/issues/issue-948.rs similarity index 82% rename from src/test/run-fail/issue-948.rs rename to src/test/ui/issues/issue-948.rs index 8f1c6587f030a..b9bbeb3951e47 100644 --- a/src/test/run-fail/issue-948.rs +++ b/src/test/ui/issues/issue-948.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:beep boop +// ignore-emscripten no processes #![allow(unused_variables)] diff --git a/src/test/ui/iterators/bound.stderr b/src/test/ui/iterators/bound.stderr index 92a91ff4cb1ba..1a5aad6c36d42 100644 --- a/src/test/ui/iterators/bound.stderr +++ b/src/test/ui/iterators/bound.stderr @@ -2,7 +2,7 @@ error[E0277]: `u8` is not an iterator --> $DIR/bound.rs:2:10 | LL | struct S(I); - | ------------------------- required by `S` + | -------- required by this bound in `S` LL | struct T(S); | ^^^^^ `u8` is not an iterator | diff --git a/src/test/ui/iterators/into-iter-on-arrays-lint.stderr b/src/test/ui/iterators/into-iter-on-arrays-lint.stderr index e9cc427f6d8b7..bbec9147f574b 100644 --- a/src/test/ui/iterators/into-iter-on-arrays-lint.stderr +++ b/src/test/ui/iterators/into-iter-on-arrays-lint.stderr @@ -107,3 +107,5 @@ LL | Box::new(Box::new([0u8; 33])).into_iter(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #66145 +warning: 12 warnings emitted + diff --git a/src/test/ui/iterators/issue-58952-filter-type-length.rs b/src/test/ui/iterators/issue-58952-filter-type-length.rs new file mode 100644 index 0000000000000..046e37840849e --- /dev/null +++ b/src/test/ui/iterators/issue-58952-filter-type-length.rs @@ -0,0 +1,31 @@ +// run-pass +//! This snippet causes the type length to blowup exponentially, +//! so check that we don't accidentially exceed the type length limit. +// FIXME: Once the size of iterator adaptors is further reduced, +// increase the complexity of this test. + +fn main() { + let c = 2; + let bv = vec![2]; + let b = bv + .iter() + .filter(|a| **a == c); + + let _a = vec![1, 2, 3] + .into_iter() + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .filter(|a| b.clone().any(|b| *b == *a)) + .collect::>(); +} diff --git a/src/test/ui/json-bom-plus-crlf-multifile.stderr b/src/test/ui/json-bom-plus-crlf-multifile.stderr index 99f91cc881617..8d3c316e467bd 100644 --- a/src/test/ui/json-bom-plus-crlf-multifile.stderr +++ b/src/test/ui/json-bom-plus-crlf-multifile.stderr @@ -12,11 +12,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":612,"byte_end":618,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -33,11 +32,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":672,"byte_end":678,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -54,11 +52,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":735,"byte_end":741,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -75,11 +72,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":801,"byte_end":809,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `std::string::String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":792,"byte_end":798,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types "} {"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors diff --git a/src/test/ui/json-bom-plus-crlf.stderr b/src/test/ui/json-bom-plus-crlf.stderr index 3e84f5ef54d2c..ed6b583f329d6 100644 --- a/src/test/ui/json-bom-plus-crlf.stderr +++ b/src/test/ui/json-bom-plus-crlf.stderr @@ -12,11 +12,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":597,"byte_end":603,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -33,11 +32,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":657,"byte_end":663,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -54,11 +52,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":720,"byte_end":726,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -75,11 +72,10 @@ let x: i32 = \"I am not a number!\"; // type `i32` assigned to variable `x` ``` -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can happen in several cases, the most common being a mismatch -between the type that the compiler inferred for a variable based on its -initializing expression, on the one hand, and the type the author explicitly -assigned to the variable, on the other hand. +This error occurs when the compiler is unable to infer the concrete type of a +variable. It can occur in several cases, the most common being a mismatch +between two types: the type the author explicitly assigned, and the type the +compiler inferred. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":786,"byte_end":794,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `std::string::String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":777,"byte_end":783,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types "} {"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs index 3845a9aa017ce..12aa059766b06 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.rs @@ -1,3 +1,3 @@ -type A = extern::foo::bar; //~ ERROR expected `fn`, found `::` +type A = extern::foo::bar; //~ ERROR expected type, found keyword `extern` fn main() {} diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr index 48c2f556f1dd9..20ecf6bac764c 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr @@ -1,8 +1,8 @@ -error: expected `fn`, found `::` - --> $DIR/keyword-extern-as-identifier-type.rs:1:16 +error: expected type, found keyword `extern` + --> $DIR/keyword-extern-as-identifier-type.rs:1:10 | LL | type A = extern::foo::bar; - | ^^ expected `fn` + | ^^^^^^ expected type error: aborting due to previous error diff --git a/src/test/ui/kindck/kindck-copy.stderr b/src/test/ui/kindck/kindck-copy.stderr index 3ca9cf7e973db..5a7cd458e52d3 100644 --- a/src/test/ui/kindck/kindck-copy.stderr +++ b/src/test/ui/kindck/kindck-copy.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `&'static mut isize: std::marker::Copy` is not sat --> $DIR/kindck-copy.rs:27:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::<&'static mut isize>(); | ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&'static mut isize` @@ -14,7 +14,7 @@ error[E0277]: the trait bound `&'a mut isize: std::marker::Copy` is not satisfie --> $DIR/kindck-copy.rs:28:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::<&'a mut isize>(); | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&'a mut isize` @@ -26,7 +26,7 @@ error[E0277]: the trait bound `std::boxed::Box: std::marker::Copy` is not --> $DIR/kindck-copy.rs:31:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box` @@ -35,7 +35,7 @@ error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not sa --> $DIR/kindck-copy.rs:32:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::(); | ^^^^^^ the trait `std::marker::Copy` is not implemented for `std::string::String` @@ -44,7 +44,7 @@ error[E0277]: the trait bound `std::vec::Vec: std::marker::Copy` is not s --> $DIR/kindck-copy.rs:33:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy:: >(); | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::vec::Vec` @@ -53,7 +53,7 @@ error[E0277]: the trait bound `std::boxed::Box<&'a mut isize>: std::marker::Copy --> $DIR/kindck-copy.rs:34:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); | ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<&'a mut isize>` @@ -62,7 +62,7 @@ error[E0277]: the trait bound `std::boxed::Box: std::marker::Copy` is --> $DIR/kindck-copy.rs:42:5 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box` @@ -71,7 +71,7 @@ error[E0277]: the trait bound `std::boxed::Box: s --> $DIR/kindck-copy.rs:43:5 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box` @@ -80,7 +80,7 @@ error[E0277]: the trait bound `&'a mut (dyn Dummy + std::marker::Send + 'a): std --> $DIR/kindck-copy.rs:46:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::<&'a mut (dyn Dummy + Send)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&'a mut (dyn Dummy + std::marker::Send + 'a)` @@ -89,7 +89,7 @@ error[E0277]: the trait bound `MyNoncopyStruct: std::marker::Copy` is not satisf --> $DIR/kindck-copy.rs:64:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::(); | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `MyNoncopyStruct` @@ -98,7 +98,7 @@ error[E0277]: the trait bound `std::rc::Rc: std::marker::Copy` is not sat --> $DIR/kindck-copy.rs:67:19 | LL | fn assert_copy() { } - | ----------- ---- required by this bound in `assert_copy` + | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::rc::Rc` diff --git a/src/test/ui/kindck/kindck-impl-type-params-2.stderr b/src/test/ui/kindck/kindck-impl-type-params-2.stderr index 318b7b0f10a0a..984960efaeef6 100644 --- a/src/test/ui/kindck/kindck-impl-type-params-2.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params-2.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied --> $DIR/kindck-impl-type-params-2.rs:13:16 | LL | fn take_param(foo: &T) { } - | ---------- --- required by this bound in `take_param` + | --- required by this bound in `take_param` ... LL | take_param(&x); | ^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<{integer}>` diff --git a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr index 593f55a5172d8..a2f70a8c24082 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr @@ -5,13 +5,12 @@ LL | let a = &t as &dyn Gettable; | ^^ `T` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `T` -help: consider restricting this type parameter with `T: std::marker::Send` - --> $DIR/kindck-impl-type-params.rs:16:6 - | -LL | fn f(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn f(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:18:13 @@ -19,13 +18,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | let a = &t as &dyn Gettable; | ^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/kindck-impl-type-params.rs:16:6 - | -LL | fn f(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn f(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be sent between threads safely --> $DIR/kindck-impl-type-params.rs:25:31 @@ -34,13 +32,12 @@ LL | let a: &dyn Gettable = &t; | ^^ `T` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `T` -help: consider restricting this type parameter with `T: std::marker::Send` - --> $DIR/kindck-impl-type-params.rs:23:6 - | -LL | fn g(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn g(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:25:31 @@ -48,13 +45,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | let a: &dyn Gettable = &t; | ^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/kindck-impl-type-params.rs:23:6 - | -LL | fn g(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn g(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:38:13 diff --git a/src/test/ui/kindck/kindck-impl-type-params.stderr b/src/test/ui/kindck/kindck-impl-type-params.stderr index 42318623b4d24..cc98f1d9f34b8 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params.stderr @@ -5,13 +5,12 @@ LL | let a = &t as &dyn Gettable; | ^^ `T` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `T` -help: consider restricting this type parameter with `T: std::marker::Send` - --> $DIR/kindck-impl-type-params.rs:16:6 - | -LL | fn f(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn f(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:18:13 @@ -19,13 +18,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | let a = &t as &dyn Gettable; | ^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/kindck-impl-type-params.rs:16:6 - | -LL | fn f(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn f(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be sent between threads safely --> $DIR/kindck-impl-type-params.rs:25:31 @@ -34,13 +32,12 @@ LL | let a: &dyn Gettable = &t; | ^^ `T` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `T` -help: consider restricting this type parameter with `T: std::marker::Send` - --> $DIR/kindck-impl-type-params.rs:23:6 - | -LL | fn g(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn g(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:25:31 @@ -48,13 +45,12 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied LL | let a: &dyn Gettable = &t; | ^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/kindck-impl-type-params.rs:23:6 - | -LL | fn g(val: T) { - | ^ = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` +help: consider restricting type parameter `T` + | +LL | fn g(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0477]: the type `&'a isize` does not fulfill the required lifetime --> $DIR/kindck-impl-type-params.rs:32:13 diff --git a/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr b/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr index 2a9fd13be5f01..7df98366edb67 100644 --- a/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr +++ b/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied --> $DIR/kindck-inherited-copy-bound.rs:21:16 | LL | fn take_param(foo: &T) { } - | ---------- --- required by this bound in `take_param` + | --- required by this bound in `take_param` ... LL | take_param(&x); | ^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<{integer}>` diff --git a/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr b/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr index 6227ada4dc938..6b511e0a6e6f5 100644 --- a/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr +++ b/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied --> $DIR/kindck-inherited-copy-bound.rs:21:16 | LL | fn take_param(foo: &T) { } - | ---------- --- required by this bound in `take_param` + | --- required by this bound in `take_param` ... LL | take_param(&x); | ^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<{integer}>` diff --git a/src/test/ui/kindck/kindck-nonsendable-1.stderr b/src/test/ui/kindck/kindck-nonsendable-1.stderr index 39640e373991f..c7f9058dd7e80 100644 --- a/src/test/ui/kindck/kindck-nonsendable-1.stderr +++ b/src/test/ui/kindck/kindck-nonsendable-1.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::rc::Rc` cannot be sent between threads safely --> $DIR/kindck-nonsendable-1.rs:9:5 | LL | fn bar(_: F) { } - | --- ---- required by this bound in `bar` + | ---- required by this bound in `bar` ... LL | bar(move|| foo(x)); | ^^^ ------------- within this `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:std::rc::Rc]` diff --git a/src/test/ui/kindck/kindck-send-object.stderr b/src/test/ui/kindck/kindck-send-object.stderr index 8708537f8630f..a59a375c6c837 100644 --- a/src/test/ui/kindck/kindck-send-object.stderr +++ b/src/test/ui/kindck/kindck-send-object.stderr @@ -2,7 +2,7 @@ error[E0277]: `(dyn Dummy + 'static)` cannot be shared between threads safely --> $DIR/kindck-send-object.rs:12:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::<&'static (dyn Dummy + 'static)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'static)` cannot be shared between threads safely @@ -14,7 +14,7 @@ error[E0277]: `dyn Dummy` cannot be sent between threads safely --> $DIR/kindck-send-object.rs:17:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely diff --git a/src/test/ui/kindck/kindck-send-object1.nll.stderr b/src/test/ui/kindck/kindck-send-object1.nll.stderr index f882e06ed222a..14a6f554f6de5 100644 --- a/src/test/ui/kindck/kindck-send-object1.nll.stderr +++ b/src/test/ui/kindck/kindck-send-object1.nll.stderr @@ -2,7 +2,7 @@ error[E0277]: `(dyn Dummy + 'a)` cannot be shared between threads safely --> $DIR/kindck-send-object1.rs:10:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::<&'a dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be shared between threads safely @@ -14,7 +14,7 @@ error[E0277]: `(dyn Dummy + 'a)` cannot be sent between threads safely --> $DIR/kindck-send-object1.rs:29:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be sent between threads safely diff --git a/src/test/ui/kindck/kindck-send-object1.stderr b/src/test/ui/kindck/kindck-send-object1.stderr index b2e89087e387f..b6d82e3195e04 100644 --- a/src/test/ui/kindck/kindck-send-object1.stderr +++ b/src/test/ui/kindck/kindck-send-object1.stderr @@ -2,7 +2,7 @@ error[E0277]: `(dyn Dummy + 'a)` cannot be shared between threads safely --> $DIR/kindck-send-object1.rs:10:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::<&'a dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be shared between threads safely @@ -22,7 +22,7 @@ error[E0277]: `(dyn Dummy + 'a)` cannot be sent between threads safely --> $DIR/kindck-send-object1.rs:29:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be sent between threads safely diff --git a/src/test/ui/kindck/kindck-send-object2.stderr b/src/test/ui/kindck/kindck-send-object2.stderr index 6cb82edf263b1..e6daf987c8c48 100644 --- a/src/test/ui/kindck/kindck-send-object2.stderr +++ b/src/test/ui/kindck/kindck-send-object2.stderr @@ -2,7 +2,7 @@ error[E0277]: `(dyn Dummy + 'static)` cannot be shared between threads safely --> $DIR/kindck-send-object2.rs:7:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::<&'static dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'static)` cannot be shared between threads safely @@ -14,7 +14,7 @@ error[E0277]: `dyn Dummy` cannot be sent between threads safely --> $DIR/kindck-send-object2.rs:12:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely diff --git a/src/test/ui/kindck/kindck-send-owned.stderr b/src/test/ui/kindck/kindck-send-owned.stderr index c740349542458..2c6c2c6267dc0 100644 --- a/src/test/ui/kindck/kindck-send-owned.stderr +++ b/src/test/ui/kindck/kindck-send-owned.stderr @@ -2,7 +2,7 @@ error[E0277]: `*mut u8` cannot be sent between threads safely --> $DIR/kindck-send-owned.rs:12:5 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut u8` cannot be sent between threads safely diff --git a/src/test/ui/kindck/kindck-send-unsafe.stderr b/src/test/ui/kindck/kindck-send-unsafe.stderr index 05ed51d0f1175..34f98218193a1 100644 --- a/src/test/ui/kindck/kindck-send-unsafe.stderr +++ b/src/test/ui/kindck/kindck-send-unsafe.stderr @@ -2,7 +2,7 @@ error[E0277]: `*mut &'a isize` cannot be sent between threads safely --> $DIR/kindck-send-unsafe.rs:6:19 | LL | fn assert_send() { } - | ----------- ---- required by this bound in `assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send::<*mut &'a isize>(); | ^^^^^^^^^^^^^^ `*mut &'a isize` cannot be sent between threads safely diff --git a/src/test/ui/label/label_break_value_continue.stderr b/src/test/ui/label/label_break_value_continue.stderr index c5f79ed6333ee..9b8693dc584c4 100644 --- a/src/test/ui/label/label_break_value_continue.stderr +++ b/src/test/ui/label/label_break_value_continue.stderr @@ -21,4 +21,5 @@ LL | continue; error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0695`. +Some errors have detailed explanations: E0695, E0696. +For more information about an error, try `rustc --explain E0695`. diff --git a/src/test/ui/layout/debug.rs b/src/test/ui/layout/debug.rs index 70ae200e3e5aa..299151df66493 100644 --- a/src/test/ui/layout/debug.rs +++ b/src/test/ui/layout/debug.rs @@ -1,15 +1,22 @@ // normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN" -#![feature(never_type, rustc_attrs)] +#![feature(never_type, rustc_attrs, type_alias_impl_trait)] #![crate_type = "lib"] #[rustc_layout(debug)] -enum E { Foo, Bar(!, i32, i32) } //~ ERROR: layout debugging +enum E { Foo, Bar(!, i32, i32) } //~ ERROR: layout_of #[rustc_layout(debug)] -struct S { f1: i32, f2: (), f3: i32 } //~ ERROR: layout debugging +struct S { f1: i32, f2: (), f3: i32 } //~ ERROR: layout_of #[rustc_layout(debug)] -union U { f1: (i32, i32), f3: i32 } //~ ERROR: layout debugging +union U { f1: (i32, i32), f3: i32 } //~ ERROR: layout_of #[rustc_layout(debug)] -type Test = Result; //~ ERROR: layout debugging +type Test = Result; //~ ERROR: layout_of + +#[rustc_layout(debug)] +type T = impl std::fmt::Debug; //~ ERROR: layout_of + +fn f() -> T { + 0i32 +} diff --git a/src/test/ui/layout/debug.stderr b/src/test/ui/layout/debug.stderr index 0ce538285f8c4..cd8ebdffb730b 100644 --- a/src/test/ui/layout/debug.stderr +++ b/src/test/ui/layout/debug.stderr @@ -1,4 +1,4 @@ -error: layout debugging: LayoutDetails { +error: layout_of(E) = Layout { fields: Arbitrary { offsets: [ Size { @@ -20,7 +20,7 @@ error: layout debugging: LayoutDetails { discr_kind: Tag, discr_index: 0, variants: [ - LayoutDetails { + Layout { fields: Arbitrary { offsets: [], memory_index: [], @@ -42,7 +42,7 @@ error: layout debugging: LayoutDetails { raw: 4, }, }, - LayoutDetails { + Layout { fields: Arbitrary { offsets: [ Size { @@ -110,7 +110,7 @@ error: layout debugging: LayoutDetails { LL | enum E { Foo, Bar(!, i32, i32) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: layout debugging: LayoutDetails { +error: layout_of(S) = Layout { fields: Arbitrary { offsets: [ Size { @@ -164,7 +164,7 @@ error: layout debugging: LayoutDetails { LL | struct S { f1: i32, f2: (), f3: i32 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: layout debugging: LayoutDetails { +error: layout_of(U) = Layout { fields: Union( 2, ), @@ -190,7 +190,7 @@ error: layout debugging: LayoutDetails { LL | union U { f1: (i32, i32), f3: i32 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: layout debugging: LayoutDetails { +error: layout_of(std::result::Result) = Layout { fields: Arbitrary { offsets: [ Size { @@ -212,7 +212,7 @@ error: layout debugging: LayoutDetails { discr_kind: Tag, discr_index: 0, variants: [ - LayoutDetails { + Layout { fields: Arbitrary { offsets: [ Size { @@ -240,7 +240,7 @@ error: layout debugging: LayoutDetails { raw: 8, }, }, - LayoutDetails { + Layout { fields: Arbitrary { offsets: [ Size { @@ -315,5 +315,35 @@ error: layout debugging: LayoutDetails { LL | type Test = Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: layout_of(i32) = Layout { + fields: Primitive, + variants: Single { + index: 0, + }, + abi: Scalar( + Scalar { + value: Int( + I32, + true, + ), + valid_range: 0..=4294967295, + }, + ), + largest_niche: None, + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 4, + }, +} + --> $DIR/debug.rs:18:1 + | +LL | type T = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/test/ui/lexical-scopes.stderr b/src/test/ui/lexical-scopes.stderr index 6bb0877e9b9ba..dce7054517030 100644 --- a/src/test/ui/lexical-scopes.stderr +++ b/src/test/ui/lexical-scopes.stderr @@ -4,7 +4,7 @@ error[E0574]: expected struct, variant or union type, found type parameter `T` LL | let t = T { i: 0 }; | ^ not a struct, variant or union type | -help: possible better candidate is found in another module, you can import it into scope +help: consider importing this struct instead | LL | use T; | diff --git a/src/test/ui/lifetimes/issue-34979.rs b/src/test/ui/lifetimes/issue-34979.rs new file mode 100644 index 0000000000000..252486dd92192 --- /dev/null +++ b/src/test/ui/lifetimes/issue-34979.rs @@ -0,0 +1,9 @@ +trait Foo {} +impl<'a, T> Foo for &'a T {} + +struct Ctx<'a>(&'a ()) +where + &'a (): Foo, //~ ERROR: type annotations needed + &'static (): Foo; + +fn main() {} diff --git a/src/test/ui/lifetimes/issue-34979.stderr b/src/test/ui/lifetimes/issue-34979.stderr new file mode 100644 index 0000000000000..04ad0d1276647 --- /dev/null +++ b/src/test/ui/lifetimes/issue-34979.stderr @@ -0,0 +1,14 @@ +error[E0283]: type annotations needed + --> $DIR/issue-34979.rs:6:13 + | +LL | trait Foo {} + | --------- required by this bound in `Foo` +... +LL | &'a (): Foo, + | ^^^ cannot infer type for reference `&'a ()` + | + = note: cannot satisfy `&'a (): Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/src/test/ui/lifetimes/issue-70917-lifetimes-in-fn-def.rs b/src/test/ui/lifetimes/issue-70917-lifetimes-in-fn-def.rs new file mode 100644 index 0000000000000..b9aab27142e31 --- /dev/null +++ b/src/test/ui/lifetimes/issue-70917-lifetimes-in-fn-def.rs @@ -0,0 +1,13 @@ +// check-pass + +fn assert_static(_: T) {} + +// NOTE(eddyb) the `'a: 'a` may look a bit strange, but we *really* want +// `'a` to be an *early-bound* parameter, otherwise it doesn't matter anyway. +fn capture_lifetime<'a: 'a>() {} + +fn test_lifetime<'a>() { + assert_static(capture_lifetime::<'a>); +} + +fn main() {} diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr index 461c1832e9af9..5809b5bd661e0 100644 --- a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr +++ b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr @@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:2:11 | LL | fn f() -> &isize { - | ^ help: consider giving it a 'static lifetime: `&'static` + | ^ 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 + | +LL | fn f() -> &'static isize { + | ^^^^^^^^ error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:7:33 @@ -34,25 +38,37 @@ error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:21:20 | LL | fn i(_x: isize) -> &isize { - | ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static` + | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | fn i(_x: isize) -> &'static isize { + | ^^^^^^^^ error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:34:24 | LL | fn j(_x: StaticStr) -> &isize { - | ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static` + | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | fn j(_x: StaticStr) -> &'static isize { + | ^^^^^^^^ error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:40:49 | LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { - | ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static` + | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'a` lifetime + | +LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &'a isize { + | ^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/linkage-attr/linkage3.rs b/src/test/ui/linkage-attr/linkage3.rs index bd4e5ba2d4a06..e91dbf675d35f 100644 --- a/src/test/ui/linkage-attr/linkage3.rs +++ b/src/test/ui/linkage-attr/linkage3.rs @@ -1,6 +1,6 @@ // FIXME https://github.com/rust-lang/rust/issues/59774 -// build-fail +// check-fail // normalize-stderr-test "thread.*panicked.*Metadata module not compiled.*\n" -> "" // normalize-stderr-test "note:.*RUST_BACKTRACE=1.*\n" -> "" diff --git a/src/test/ui/lint/command-line-lint-group-warn.stderr b/src/test/ui/lint/command-line-lint-group-warn.stderr index 42a198fe7e3e2..e9c80b4ef21af 100644 --- a/src/test/ui/lint/command-line-lint-group-warn.stderr +++ b/src/test/ui/lint/command-line-lint-group-warn.stderr @@ -6,3 +6,5 @@ LL | let _InappropriateCamelCasing = true; | = note: `-W non-snake-case` implied by `-W bad-style` +warning: 1 warning emitted + diff --git a/src/test/ui/lint/dead-code/lint-dead-code-1.rs b/src/test/ui/lint/dead-code/lint-dead-code-1.rs index 09977f8df51cf..896147fcc7738 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-1.rs +++ b/src/test/ui/lint/dead-code/lint-dead-code-1.rs @@ -17,14 +17,14 @@ mod foo2 { } pub static pub_static: isize = 0; -static priv_static: isize = 0; //~ ERROR: static item is never used +static priv_static: isize = 0; //~ ERROR: static is never used const used_static: isize = 0; pub static used_static2: isize = used_static; const USED_STATIC: isize = 0; const STATIC_USED_IN_ENUM_DISCRIMINANT: isize = 10; pub const pub_const: isize = 0; -const priv_const: isize = 0; //~ ERROR: constant item is never used +const priv_const: isize = 0; //~ ERROR: constant is never used const used_const: isize = 0; pub const used_const2: isize = used_const; const USED_CONST: isize = 1; diff --git a/src/test/ui/lint/dead-code/lint-dead-code-1.stderr b/src/test/ui/lint/dead-code/lint-dead-code-1.stderr index 0a08aa6da9ac0..af97ea98b2b6d 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-1.stderr @@ -10,13 +10,13 @@ note: the lint level is defined here LL | #![deny(dead_code)] | ^^^^^^^^^ -error: static item is never used: `priv_static` +error: static is never used: `priv_static` --> $DIR/lint-dead-code-1.rs:20:1 | LL | static priv_static: isize = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: constant item is never used: `priv_const` +error: constant is never used: `priv_const` --> $DIR/lint-dead-code-1.rs:27:1 | LL | const priv_const: isize = 0; diff --git a/src/test/ui/lint/dead-code/lint-dead-code-3.rs b/src/test/ui/lint/dead-code/lint-dead-code-3.rs index 4397522f3f32f..6826d2cd67eb9 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-3.rs +++ b/src/test/ui/lint/dead-code/lint-dead-code-3.rs @@ -12,7 +12,7 @@ extern { struct Foo; //~ ERROR: struct is never constructed impl Foo { - fn foo(&self) { //~ ERROR: method is never used + fn foo(&self) { //~ ERROR: associated function is never used bar() } } @@ -58,7 +58,7 @@ mod blah { enum c_void {} //~ ERROR: enum is never used extern { - fn free(p: *const c_void); //~ ERROR: foreign function is never used + fn free(p: *const c_void); //~ ERROR: function is never used } // Check provided method diff --git a/src/test/ui/lint/dead-code/lint-dead-code-3.stderr b/src/test/ui/lint/dead-code/lint-dead-code-3.stderr index aab25c481e6c7..6d174e8d9bc38 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-3.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-3.stderr @@ -10,11 +10,11 @@ note: the lint level is defined here LL | #![deny(dead_code)] | ^^^^^^^^^ -error: method is never used: `foo` - --> $DIR/lint-dead-code-3.rs:15:5 +error: associated function is never used: `foo` + --> $DIR/lint-dead-code-3.rs:15:8 | LL | fn foo(&self) { - | ^^^^^^^^^^^^^ + | ^^^ error: function is never used: `bar` --> $DIR/lint-dead-code-3.rs:20:4 @@ -28,7 +28,7 @@ error: enum is never used: `c_void` LL | enum c_void {} | ^^^^^^ -error: foreign function is never used: `free` +error: function is never used: `free` --> $DIR/lint-dead-code-3.rs:61:5 | LL | fn free(p: *const c_void); diff --git a/src/test/ui/lint/dead-code/lint-dead-code-5.rs b/src/test/ui/lint/dead-code/lint-dead-code-5.rs index 764a23e4e49e9..b477c97c5457b 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-5.rs +++ b/src/test/ui/lint/dead-code/lint-dead-code-5.rs @@ -13,6 +13,23 @@ enum Enum2 { Variant5 { _x: isize }, //~ ERROR: variant is never constructed: `Variant5` Variant6(isize), //~ ERROR: variant is never constructed: `Variant6` _Variant7, + Variant8 { _field: bool }, + Variant9, + Variant10(usize) +} + +impl Enum2 { + fn new_variant8() -> Enum2 { + Self::Variant8 { _field: true } + } + + fn new_variant9() -> Enum2 { + Self::Variant9 + } + + fn new_variant10() -> Enum2 { + Self::Variant10(10) + } } enum Enum3 { //~ ERROR: enum is never used @@ -27,4 +44,7 @@ fn main() { Enum1::Variant2 => () } let x = Enum2::Variant3(true); + let _ = Enum2::new_variant8(); + let _ = Enum2::new_variant9(); + let _ = Enum2::new_variant10(); } diff --git a/src/test/ui/lint/dead-code/lint-dead-code-5.stderr b/src/test/ui/lint/dead-code/lint-dead-code-5.stderr index c0de469102077..519add826273f 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-5.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-5.stderr @@ -23,7 +23,7 @@ LL | Variant6(isize), | ^^^^^^^^^^^^^^^ error: enum is never used: `Enum3` - --> $DIR/lint-dead-code-5.rs:18:6 + --> $DIR/lint-dead-code-5.rs:35:6 | LL | enum Enum3 { | ^^^^^ diff --git a/src/test/ui/lint/dead-code/lint-dead-code-6.rs b/src/test/ui/lint/dead-code/lint-dead-code-6.rs new file mode 100644 index 0000000000000..0a543d5c6228d --- /dev/null +++ b/src/test/ui/lint/dead-code/lint-dead-code-6.rs @@ -0,0 +1,20 @@ +#![deny(dead_code)] + +struct UnusedStruct; //~ ERROR struct is never constructed: `UnusedStruct` +impl UnusedStruct { + fn unused_impl_fn_1() { //~ ERROR associated function is never used: `unused_impl_fn_1` + println!("blah"); + } + + fn unused_impl_fn_2(var: i32) { //~ ERROR associated function is never used: `unused_impl_fn_2` + println!("foo {}", var); + } + + fn unused_impl_fn_3( //~ ERROR associated function is never used: `unused_impl_fn_3` + var: i32, + ) { + println!("bar {}", var); + } +} + +fn main() {} diff --git a/src/test/ui/lint/dead-code/lint-dead-code-6.stderr b/src/test/ui/lint/dead-code/lint-dead-code-6.stderr new file mode 100644 index 0000000000000..7dc60730d6aad --- /dev/null +++ b/src/test/ui/lint/dead-code/lint-dead-code-6.stderr @@ -0,0 +1,32 @@ +error: struct is never constructed: `UnusedStruct` + --> $DIR/lint-dead-code-6.rs:3:8 + | +LL | struct UnusedStruct; + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-dead-code-6.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: associated function is never used: `unused_impl_fn_1` + --> $DIR/lint-dead-code-6.rs:5:8 + | +LL | fn unused_impl_fn_1() { + | ^^^^^^^^^^^^^^^^ + +error: associated function is never used: `unused_impl_fn_2` + --> $DIR/lint-dead-code-6.rs:9:8 + | +LL | fn unused_impl_fn_2(var: i32) { + | ^^^^^^^^^^^^^^^^ + +error: associated function is never used: `unused_impl_fn_3` + --> $DIR/lint-dead-code-6.rs:13:8 + | +LL | fn unused_impl_fn_3( + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/lint/inclusive-range-pattern-syntax.stderr b/src/test/ui/lint/inclusive-range-pattern-syntax.stderr index f5768626136e0..19fe9ed892acb 100644 --- a/src/test/ui/lint/inclusive-range-pattern-syntax.stderr +++ b/src/test/ui/lint/inclusive-range-pattern-syntax.stderr @@ -16,3 +16,5 @@ warning: `...` range patterns are deprecated LL | &1...2 => {} | ^^^^^^ help: use `..=` for an inclusive range: `&(1..=2)` +warning: 2 warnings emitted + diff --git a/src/test/ui/lint/inline-trait-and-foreign-items.stderr b/src/test/ui/lint/inline-trait-and-foreign-items.stderr index 5f386ea045bf0..15aaf8961b7b1 100644 --- a/src/test/ui/lint/inline-trait-and-foreign-items.stderr +++ b/src/test/ui/lint/inline-trait-and-foreign-items.stderr @@ -67,6 +67,6 @@ error: could not find defining uses LL | type U = impl Trait; | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 6 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0518`. diff --git a/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr b/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr index cc675a709a2c6..3efd87f6a5f11 100644 --- a/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr +++ b/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr @@ -12,16 +12,16 @@ LL | #![warn(unused)] // UI tests pass `-A unused` (#43896) = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` warning: unused variable: `mut_unused_var` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:33:13 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:33:9 | LL | let mut mut_unused_var = 1; - | ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_mut_unused_var` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_mut_unused_var` warning: unused variable: `var` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:14 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:10 | LL | let (mut var, unused_var) = (1, 2); - | ^^^ help: if this is intentional, prefix it with an underscore: `_var` + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_var` warning: unused variable: `unused_var` --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:19 @@ -36,10 +36,10 @@ LL | if let SoulHistory { corridors_of_light, | ^^^^^^^^^^^^^^^^^^ help: try ignoring the field: `corridors_of_light: _` warning: variable `hours_are_suns` is assigned to, but never used - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:46:30 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:46:26 | LL | mut hours_are_suns, - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ | = note: consider using `_hours_are_suns` instead @@ -122,3 +122,5 @@ LL | let (mut var, unused_var) = (1, 2); | | | help: remove this `mut` +warning: 16 warnings emitted + diff --git a/src/test/ui/lint/issue-54180-unused-ref-field.stderr b/src/test/ui/lint/issue-54180-unused-ref-field.stderr index 840ecc0ce09ee..c501aa25f1352 100644 --- a/src/test/ui/lint/issue-54180-unused-ref-field.stderr +++ b/src/test/ui/lint/issue-54180-unused-ref-field.stderr @@ -1,10 +1,8 @@ error: unused variable: `field` - --> $DIR/issue-54180-unused-ref-field.rs:20:26 + --> $DIR/issue-54180-unused-ref-field.rs:20:22 | LL | E::Variant { ref field } => (), - | ----^^^^^ - | | - | help: try ignoring the field: `field: _` + | ^^^^^^^^^ help: try ignoring the field: `field: _` | note: the lint level is defined here --> $DIR/issue-54180-unused-ref-field.rs:3:9 @@ -20,20 +18,16 @@ LL | let _: i32 = points.iter().map(|Point { x, y }| y).sum(); | ^ help: try ignoring the field: `x: _` error: unused variable: `f1` - --> $DIR/issue-54180-unused-ref-field.rs:26:17 + --> $DIR/issue-54180-unused-ref-field.rs:26:13 | LL | let S { ref f1 } = s; - | ----^^ - | | - | help: try ignoring the field: `f1: _` + | ^^^^^^ help: try ignoring the field: `f1: _` error: unused variable: `x` - --> $DIR/issue-54180-unused-ref-field.rs:32:28 + --> $DIR/issue-54180-unused-ref-field.rs:32:20 | LL | Point { y, ref mut x } => y, - | --------^ - | | - | help: try ignoring the field: `x: _` + | ^^^^^^^^^ help: try ignoring the field: `x: _` error: aborting due to 4 previous errors diff --git a/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.rs b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.rs new file mode 100644 index 0000000000000..b21f1ef6b2603 --- /dev/null +++ b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.rs @@ -0,0 +1,86 @@ +// FIXME: should be run-rustfix, but rustfix doesn't currently support multipart suggestions, see +// #53934 + +#![feature(or_patterns)] +#![deny(unused)] + +pub enum MyEnum { + A { i: i32, j: i32 }, + B { i: i32, j: i32 }, +} + +pub enum MixedEnum { + A { i: i32 }, + B(i32), +} + +pub fn no_ref(x: MyEnum) { + use MyEnum::*; + + match x { + A { i, j } | B { i, j } => { //~ ERROR unused variable + println!("{}", i); + } + } +} + +pub fn with_ref(x: MyEnum) { + use MyEnum::*; + + match x { + A { i, ref j } | B { i, ref j } => { //~ ERROR unused variable + println!("{}", i); + } + } +} + +pub fn inner_no_ref(x: Option) { + use MyEnum::*; + + match x { + Some(A { i, j } | B { i, j }) => { //~ ERROR unused variable + println!("{}", i); + } + + _ => {} + } +} + +pub fn inner_with_ref(x: Option) { + use MyEnum::*; + + match x { + Some(A { i, ref j } | B { i, ref j }) => { //~ ERROR unused variable + println!("{}", i); + } + + _ => {} + } +} + +pub fn mixed_no_ref(x: MixedEnum) { + match x { + MixedEnum::A { i } | MixedEnum::B(i) => { //~ ERROR unused variable + println!("match"); + } + } +} + +pub fn mixed_with_ref(x: MixedEnum) { + match x { + MixedEnum::A { ref i } | MixedEnum::B(ref i) => { //~ ERROR unused variable + println!("match"); + } + } +} + +pub fn main() { + no_ref(MyEnum::A { i: 1, j: 2 }); + with_ref(MyEnum::A { i: 1, j: 2 }); + + inner_no_ref(Some(MyEnum::A { i: 1, j: 2 })); + inner_with_ref(Some(MyEnum::A { i: 1, j: 2 })); + + mixed_no_ref(MixedEnum::B(5)); + mixed_with_ref(MixedEnum::B(5)); +} diff --git a/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.stderr b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.stderr new file mode 100644 index 0000000000000..9cff2900908e6 --- /dev/null +++ b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.stderr @@ -0,0 +1,74 @@ +error: unused variable: `j` + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:21:16 + | +LL | A { i, j } | B { i, j } => { + | ^ ^ + | +note: the lint level is defined here + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:5:9 + | +LL | #![deny(unused)] + | ^^^^^^ + = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` +help: try ignoring the field + | +LL | A { i, j: _ } | B { i, j: _ } => { + | ^^^^ ^^^^ + +error: unused variable: `j` + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:31:16 + | +LL | A { i, ref j } | B { i, ref j } => { + | ^^^^^ ^^^^^ + | +help: try ignoring the field + | +LL | A { i, j: _ } | B { i, j: _ } => { + | ^^^^ ^^^^ + +error: unused variable: `j` + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:41:21 + | +LL | Some(A { i, j } | B { i, j }) => { + | ^ ^ + | +help: try ignoring the field + | +LL | Some(A { i, j: _ } | B { i, j: _ }) => { + | ^^^^ ^^^^ + +error: unused variable: `j` + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:53:21 + | +LL | Some(A { i, ref j } | B { i, ref j }) => { + | ^^^^^ ^^^^^ + | +help: try ignoring the field + | +LL | Some(A { i, j: _ } | B { i, j: _ }) => { + | ^^^^ ^^^^ + +error: unused variable: `i` + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:63:24 + | +LL | MixedEnum::A { i } | MixedEnum::B(i) => { + | ^ ^ + | +help: try ignoring the field + | +LL | MixedEnum::A { i: _ } | MixedEnum::B(_) => { + | ^^^^ ^ + +error: unused variable: `i` + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:71:24 + | +LL | MixedEnum::A { ref i } | MixedEnum::B(ref i) => { + | ^^^^^ ^^^^^ + | +help: try ignoring the field + | +LL | MixedEnum::A { i: _ } | MixedEnum::B(_) => { + | ^^^^ ^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/lint/issue-71290-unused-paren-binop.rs b/src/test/ui/lint/issue-71290-unused-paren-binop.rs new file mode 100644 index 0000000000000..24d77e36d94f5 --- /dev/null +++ b/src/test/ui/lint/issue-71290-unused-paren-binop.rs @@ -0,0 +1,23 @@ +// check-pass +// Make sure unused parens lint doesn't emit a false positive. +// See https://github.com/rust-lang/rust/issues/71290 for details. +#![deny(unused_parens)] + +fn x() -> u8 { + ({ 0 }) + 1 +} + +fn y() -> u8 { + ({ 0 } + 1) +} + +pub fn foo(a: bool, b: bool) -> u8 { + (if a { 1 } else { 0 } + if b { 1 } else { 0 }) +} + +pub fn bar() -> u8 { + // Make sure nested expressions are handled correctly as well + ({ 0 } + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9) +} + +fn main() {} diff --git a/src/test/ui/lint/lint-change-warnings.stderr b/src/test/ui/lint/lint-change-warnings.stderr index 0926dada05d5a..3fd5283aa6a4a 100644 --- a/src/test/ui/lint/lint-change-warnings.stderr +++ b/src/test/ui/lint/lint-change-warnings.stderr @@ -32,5 +32,5 @@ LL | #[forbid(warnings)] | ^^^^^^^^ = note: `#[forbid(while_true)]` implied by `#[forbid(warnings)]` -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr b/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr index ce9b02b6d82a7..8dbfeff7972fb 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr +++ b/src/test/ui/lint/lint-exceeding-bitshifts.noopt.stderr @@ -1,146 +1,152 @@ -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:22:13 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:17:20 | -LL | let _ = x << 42; - | ^^^^^^^ attempt to shift left with overflow +LL | const N: i32 = T::N << 42; + | ^^^^^^^^^^ attempt to shift left with overflow | note: the lint level is defined here --> $DIR/lint-exceeding-bitshifts.rs:9:9 | -LL | #![deny(arithmetic_overflow, const_err)] +LL | #![warn(arithmetic_overflow, const_err)] | ^^^^^^^^^^^^^^^^^^^ -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:27:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:21:13 + | +LL | let _ = x << 42; + | ^^^^^^^ attempt to shift left with overflow + +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:26:15 | LL | let n = 1u8 << 8; | ^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:29:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:28:15 | LL | let n = 1u16 << 16; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:31:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:30:15 | LL | let n = 1u32 << 32; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:33:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:32:15 | LL | let n = 1u64 << 64; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:35:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:34:15 | LL | let n = 1i8 << 8; | ^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:37:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:36:15 | LL | let n = 1i16 << 16; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:39:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:38:15 | LL | let n = 1i32 << 32; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:41:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:40:15 | LL | let n = 1i64 << 64; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:44:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:43:15 | LL | let n = 1u8 >> 8; | ^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:46:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:45:15 | LL | let n = 1u16 >> 16; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:48:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:47:15 | LL | let n = 1u32 >> 32; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:50:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:49:15 | LL | let n = 1u64 >> 64; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:52:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:51:15 | LL | let n = 1i8 >> 8; | ^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:54:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:53:15 | LL | let n = 1i16 >> 16; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:56:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:55:15 | LL | let n = 1i32 >> 32; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:58:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:57:15 | LL | let n = 1i64 >> 64; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:62:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:61:15 | LL | let n = n << 8; | ^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:64:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:63:15 | LL | let n = 1u8 << -8; | ^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:69:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:68:15 | LL | let n = 1u8 << (4+4); | ^^^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:71:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:70:15 | LL | let n = 1i64 >> [64][0]; | ^^^^^^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:77:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:76:15 | LL | let n = 1_isize << BITS; | ^^^^^^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:78:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:77:15 | LL | let n = 1_usize << BITS; | ^^^^^^^^^^^^^^^ attempt to shift left with overflow -error: aborting due to 23 previous errors +warning: 24 warnings emitted diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr b/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr index ce9b02b6d82a7..8dbfeff7972fb 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr +++ b/src/test/ui/lint/lint-exceeding-bitshifts.opt.stderr @@ -1,146 +1,152 @@ -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:22:13 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:17:20 | -LL | let _ = x << 42; - | ^^^^^^^ attempt to shift left with overflow +LL | const N: i32 = T::N << 42; + | ^^^^^^^^^^ attempt to shift left with overflow | note: the lint level is defined here --> $DIR/lint-exceeding-bitshifts.rs:9:9 | -LL | #![deny(arithmetic_overflow, const_err)] +LL | #![warn(arithmetic_overflow, const_err)] | ^^^^^^^^^^^^^^^^^^^ -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:27:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:21:13 + | +LL | let _ = x << 42; + | ^^^^^^^ attempt to shift left with overflow + +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:26:15 | LL | let n = 1u8 << 8; | ^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:29:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:28:15 | LL | let n = 1u16 << 16; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:31:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:30:15 | LL | let n = 1u32 << 32; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:33:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:32:15 | LL | let n = 1u64 << 64; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:35:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:34:15 | LL | let n = 1i8 << 8; | ^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:37:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:36:15 | LL | let n = 1i16 << 16; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:39:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:38:15 | LL | let n = 1i32 << 32; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:41:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:40:15 | LL | let n = 1i64 << 64; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:44:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:43:15 | LL | let n = 1u8 >> 8; | ^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:46:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:45:15 | LL | let n = 1u16 >> 16; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:48:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:47:15 | LL | let n = 1u32 >> 32; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:50:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:49:15 | LL | let n = 1u64 >> 64; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:52:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:51:15 | LL | let n = 1i8 >> 8; | ^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:54:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:53:15 | LL | let n = 1i16 >> 16; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:56:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:55:15 | LL | let n = 1i32 >> 32; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:58:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:57:15 | LL | let n = 1i64 >> 64; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:62:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:61:15 | LL | let n = n << 8; | ^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:64:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:63:15 | LL | let n = 1u8 << -8; | ^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:69:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:68:15 | LL | let n = 1u8 << (4+4); | ^^^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:71:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:70:15 | LL | let n = 1i64 >> [64][0]; | ^^^^^^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:77:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:76:15 | LL | let n = 1_isize << BITS; | ^^^^^^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:78:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:77:15 | LL | let n = 1_usize << BITS; | ^^^^^^^^^^^^^^^ attempt to shift left with overflow -error: aborting due to 23 previous errors +warning: 24 warnings emitted diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr b/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr index ce9b02b6d82a7..8dbfeff7972fb 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr +++ b/src/test/ui/lint/lint-exceeding-bitshifts.opt_with_overflow_checks.stderr @@ -1,146 +1,152 @@ -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:22:13 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:17:20 | -LL | let _ = x << 42; - | ^^^^^^^ attempt to shift left with overflow +LL | const N: i32 = T::N << 42; + | ^^^^^^^^^^ attempt to shift left with overflow | note: the lint level is defined here --> $DIR/lint-exceeding-bitshifts.rs:9:9 | -LL | #![deny(arithmetic_overflow, const_err)] +LL | #![warn(arithmetic_overflow, const_err)] | ^^^^^^^^^^^^^^^^^^^ -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:27:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:21:13 + | +LL | let _ = x << 42; + | ^^^^^^^ attempt to shift left with overflow + +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:26:15 | LL | let n = 1u8 << 8; | ^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:29:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:28:15 | LL | let n = 1u16 << 16; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:31:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:30:15 | LL | let n = 1u32 << 32; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:33:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:32:15 | LL | let n = 1u64 << 64; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:35:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:34:15 | LL | let n = 1i8 << 8; | ^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:37:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:36:15 | LL | let n = 1i16 << 16; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:39:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:38:15 | LL | let n = 1i32 << 32; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:41:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:40:15 | LL | let n = 1i64 << 64; | ^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:44:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:43:15 | LL | let n = 1u8 >> 8; | ^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:46:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:45:15 | LL | let n = 1u16 >> 16; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:48:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:47:15 | LL | let n = 1u32 >> 32; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:50:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:49:15 | LL | let n = 1u64 >> 64; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:52:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:51:15 | LL | let n = 1i8 >> 8; | ^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:54:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:53:15 | LL | let n = 1i16 >> 16; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:56:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:55:15 | LL | let n = 1i32 >> 32; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:58:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:57:15 | LL | let n = 1i64 >> 64; | ^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:62:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:61:15 | LL | let n = n << 8; | ^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:64:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:63:15 | LL | let n = 1u8 << -8; | ^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:69:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:68:15 | LL | let n = 1u8 << (4+4); | ^^^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:71:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:70:15 | LL | let n = 1i64 >> [64][0]; | ^^^^^^^^^^^^^^^ attempt to shift right with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:77:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:76:15 | LL | let n = 1_isize << BITS; | ^^^^^^^^^^^^^^^ attempt to shift left with overflow -error: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:78:15 +warning: this arithmetic operation will overflow + --> $DIR/lint-exceeding-bitshifts.rs:77:15 | LL | let n = 1_usize << BITS; | ^^^^^^^^^^^^^^^ attempt to shift left with overflow -error: aborting due to 23 previous errors +warning: 24 warnings emitted diff --git a/src/test/ui/lint/lint-exceeding-bitshifts.rs b/src/test/ui/lint/lint-exceeding-bitshifts.rs index 7deee5320a878..4d56d103a8343 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts.rs +++ b/src/test/ui/lint/lint-exceeding-bitshifts.rs @@ -2,78 +2,77 @@ //[noopt]compile-flags: -C opt-level=0 //[opt]compile-flags: -O //[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O - -// build-fail +// build-pass +// ignore-pass (test emits codegen-time warnings and verifies that they are not errors) #![crate_type="lib"] -#![deny(arithmetic_overflow, const_err)] -#![allow(unused_variables)] -#![allow(dead_code)] +#![warn(arithmetic_overflow, const_err)] + pub trait Foo { const N: i32; } impl Foo for Vec { - const N: i32 = T::N << 42; // FIXME this should warn + const N: i32 = T::N << 42; //~ WARN: arithmetic operation will overflow } pub fn foo(x: i32) { - let _ = x << 42; //~ ERROR: arithmetic operation will overflow + let _ = x << 42; //~ WARN: arithmetic operation will overflow } pub fn main() { let n = 1u8 << 7; - let n = 1u8 << 8; //~ ERROR: arithmetic operation will overflow + let n = 1u8 << 8; //~ WARN: arithmetic operation will overflow let n = 1u16 << 15; - let n = 1u16 << 16; //~ ERROR: arithmetic operation will overflow + let n = 1u16 << 16; //~ WARN: arithmetic operation will overflow let n = 1u32 << 31; - let n = 1u32 << 32; //~ ERROR: arithmetic operation will overflow + let n = 1u32 << 32; //~ WARN: arithmetic operation will overflow let n = 1u64 << 63; - let n = 1u64 << 64; //~ ERROR: arithmetic operation will overflow + let n = 1u64 << 64; //~ WARN: arithmetic operation will overflow let n = 1i8 << 7; - let n = 1i8 << 8; //~ ERROR: arithmetic operation will overflow + let n = 1i8 << 8; //~ WARN: arithmetic operation will overflow let n = 1i16 << 15; - let n = 1i16 << 16; //~ ERROR: arithmetic operation will overflow + let n = 1i16 << 16; //~ WARN: arithmetic operation will overflow let n = 1i32 << 31; - let n = 1i32 << 32; //~ ERROR: arithmetic operation will overflow + let n = 1i32 << 32; //~ WARN: arithmetic operation will overflow let n = 1i64 << 63; - let n = 1i64 << 64; //~ ERROR: arithmetic operation will overflow + let n = 1i64 << 64; //~ WARN: arithmetic operation will overflow let n = 1u8 >> 7; - let n = 1u8 >> 8; //~ ERROR: arithmetic operation will overflow + let n = 1u8 >> 8; //~ WARN: arithmetic operation will overflow let n = 1u16 >> 15; - let n = 1u16 >> 16; //~ ERROR: arithmetic operation will overflow + let n = 1u16 >> 16; //~ WARN: arithmetic operation will overflow let n = 1u32 >> 31; - let n = 1u32 >> 32; //~ ERROR: arithmetic operation will overflow + let n = 1u32 >> 32; //~ WARN: arithmetic operation will overflow let n = 1u64 >> 63; - let n = 1u64 >> 64; //~ ERROR: arithmetic operation will overflow + let n = 1u64 >> 64; //~ WARN: arithmetic operation will overflow let n = 1i8 >> 7; - let n = 1i8 >> 8; //~ ERROR: arithmetic operation will overflow + let n = 1i8 >> 8; //~ WARN: arithmetic operation will overflow let n = 1i16 >> 15; - let n = 1i16 >> 16; //~ ERROR: arithmetic operation will overflow + let n = 1i16 >> 16; //~ WARN: arithmetic operation will overflow let n = 1i32 >> 31; - let n = 1i32 >> 32; //~ ERROR: arithmetic operation will overflow + let n = 1i32 >> 32; //~ WARN: arithmetic operation will overflow let n = 1i64 >> 63; - let n = 1i64 >> 64; //~ ERROR: arithmetic operation will overflow + let n = 1i64 >> 64; //~ WARN: arithmetic operation will overflow let n = 1u8; let n = n << 7; - let n = n << 8; //~ ERROR: arithmetic operation will overflow + let n = n << 8; //~ WARN: arithmetic operation will overflow - let n = 1u8 << -8; //~ ERROR: arithmetic operation will overflow + let n = 1u8 << -8; //~ WARN: arithmetic operation will overflow let n = 1i8<<(1isize+-1); let n = 1u8 << (4+3); - let n = 1u8 << (4+4); //~ ERROR: arithmetic operation will overflow + let n = 1u8 << (4+4); //~ WARN: arithmetic operation will overflow let n = 1i64 >> [63][0]; - let n = 1i64 >> [64][0]; //~ ERROR: arithmetic operation will overflow + let n = 1i64 >> [64][0]; //~ WARN: arithmetic operation will overflow #[cfg(target_pointer_width = "32")] const BITS: usize = 32; #[cfg(target_pointer_width = "64")] const BITS: usize = 64; - let n = 1_isize << BITS; //~ ERROR: arithmetic operation will overflow - let n = 1_usize << BITS; //~ ERROR: arithmetic operation will overflow + let n = 1_isize << BITS; //~ WARN: arithmetic operation will overflow + let n = 1_usize << BITS; //~ WARN: arithmetic operation will overflow } diff --git a/src/test/ui/lint/lint-group-nonstandard-style.stderr b/src/test/ui/lint/lint-group-nonstandard-style.stderr index 4ba49bf1ba7f5..0ce33090f663a 100644 --- a/src/test/ui/lint/lint-group-nonstandard-style.stderr +++ b/src/test/ui/lint/lint-group-nonstandard-style.stderr @@ -63,5 +63,5 @@ LL | #![warn(nonstandard_style)] | ^^^^^^^^^^^^^^^^^ = note: `#[warn(non_snake_case)]` implied by `#[warn(nonstandard_style)]` -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 2 warnings emitted diff --git a/src/test/ui/lint/lint-output-format-2.stderr b/src/test/ui/lint/lint-output-format-2.stderr index fcaf01488ab24..a95fd69fb01c7 100644 --- a/src/test/ui/lint/lint-output-format-2.stderr +++ b/src/test/ui/lint/lint-output-format-2.stderr @@ -12,3 +12,5 @@ warning: use of deprecated item 'lint_output_format::foo': text LL | let _x = foo(); | ^^^ +warning: 2 warnings emitted + diff --git a/src/test/ui/lint/lint-pre-expansion-extern-module.stderr b/src/test/ui/lint/lint-pre-expansion-extern-module.stderr index c683a3fa670ae..6efd03f14a1dc 100644 --- a/src/test/ui/lint/lint-pre-expansion-extern-module.stderr +++ b/src/test/ui/lint/lint-pre-expansion-extern-module.stderr @@ -8,3 +8,5 @@ LL | pub fn try() {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! = note: for more information, see issue #49716 +warning: 1 warning emitted + diff --git a/src/test/ui/lint/lint-removed-cmdline.stderr b/src/test/ui/lint/lint-removed-cmdline.stderr index 4adc18cc2ce5d..1c45c38774e5a 100644 --- a/src/test/ui/lint/lint-removed-cmdline.stderr +++ b/src/test/ui/lint/lint-removed-cmdline.stderr @@ -27,5 +27,5 @@ LL | #[deny(warnings)] | ^^^^^^^^ = note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]` -error: aborting due to previous error +error: aborting due to previous error; 4 warnings emitted diff --git a/src/test/ui/lint/lint-removed.stderr b/src/test/ui/lint/lint-removed.stderr index 190e10c8a9944..44480d84203bd 100644 --- a/src/test/ui/lint/lint-removed.stderr +++ b/src/test/ui/lint/lint-removed.stderr @@ -18,5 +18,5 @@ note: the lint level is defined here LL | #[deny(unused_variables)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/lint/lint-renamed-cmdline.stderr b/src/test/ui/lint/lint-renamed-cmdline.stderr index ef617dfe31f02..1c37a5baa6db0 100644 --- a/src/test/ui/lint/lint-renamed-cmdline.stderr +++ b/src/test/ui/lint/lint-renamed-cmdline.stderr @@ -27,5 +27,5 @@ LL | #[deny(unused)] | ^^^^^^ = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` -error: aborting due to previous error +error: aborting due to previous error; 4 warnings emitted diff --git a/src/test/ui/lint/lint-renamed.stderr b/src/test/ui/lint/lint-renamed.stderr index 0225e22623424..9842545714529 100644 --- a/src/test/ui/lint/lint-renamed.stderr +++ b/src/test/ui/lint/lint-renamed.stderr @@ -19,5 +19,5 @@ LL | #[deny(unused)] | ^^^^^^ = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/lint/lint-stability-deprecated.stderr b/src/test/ui/lint/lint-stability-deprecated.stderr index 734c6093e2b62..801e04a7f4f83 100644 --- a/src/test/ui/lint/lint-stability-deprecated.stderr +++ b/src/test/ui/lint/lint-stability-deprecated.stderr @@ -652,3 +652,5 @@ warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeD LL | TypeDeprecated = u16, | ^^^^^^^^^^^^^^^^^^^^ +warning: 108 warnings emitted + diff --git a/src/test/ui/lint/lint-type-limits2.stderr b/src/test/ui/lint/lint-type-limits2.stderr index 1e3c88dfc469c..e8746ce980a96 100644 --- a/src/test/ui/lint/lint-type-limits2.stderr +++ b/src/test/ui/lint/lint-type-limits2.stderr @@ -19,5 +19,5 @@ LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/lint/lint-type-limits3.stderr b/src/test/ui/lint/lint-type-limits3.stderr index 150e9a2aa4792..0e8a64510695a 100644 --- a/src/test/ui/lint/lint-type-limits3.stderr +++ b/src/test/ui/lint/lint-type-limits3.stderr @@ -19,5 +19,5 @@ LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ = note: the literal `200` does not fit into the type `i8` whose range is `-128..=127` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/lint/lint-unconditional-recursion.rs b/src/test/ui/lint/lint-unconditional-recursion.rs index ab60a326cd220..d2a0329585b71 100644 --- a/src/test/ui/lint/lint-unconditional-recursion.rs +++ b/src/test/ui/lint/lint-unconditional-recursion.rs @@ -131,4 +131,22 @@ trait Bar { } } +// Do not trigger on functions that may diverge instead of self-recursing (#54444) + +pub fn loops(x: bool) { + if x { + loops(x); + } else { + loop {} + } +} + +pub fn panics(x: bool) { + if x { + panics(!x); + } else { + panic!("panics"); + } +} + fn main() {} diff --git a/src/test/ui/lint/lint-unexported-no-mangle.stderr b/src/test/ui/lint/lint-unexported-no-mangle.stderr index 3a78ed2ceea15..48d9b38a99b65 100644 --- a/src/test/ui/lint/lint-unexported-no-mangle.stderr +++ b/src/test/ui/lint/lint-unexported-no-mangle.stderr @@ -48,5 +48,5 @@ LL | pub const PUB_FOO: u64 = 1; | | | help: try a static value: `pub static` -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 8 warnings emitted diff --git a/src/test/ui/lint/lint-unnecessary-parens.rs b/src/test/ui/lint/lint-unnecessary-parens.rs index 5ce1f57608132..623cd04d9bce3 100644 --- a/src/test/ui/lint/lint-unnecessary-parens.rs +++ b/src/test/ui/lint/lint-unnecessary-parens.rs @@ -48,11 +48,11 @@ fn main() { if (true) {} //~ ERROR unnecessary parentheses around `if` condition while (true) {} //~ ERROR unnecessary parentheses around `while` condition //~^ WARN denote infinite loops with - match (true) { //~ ERROR unnecessary parentheses around `match` head expression + match (true) { //~ ERROR unnecessary parentheses around `match` scrutinee expression _ => {} } - if let 1 = (1) {} //~ ERROR unnecessary parentheses around `let` head expression - while let 1 = (2) {} //~ ERROR unnecessary parentheses around `let` head expression + if let 1 = (1) {} //~ ERROR unnecessary parentheses around `let` scrutinee expression + while let 1 = (2) {} //~ ERROR unnecessary parentheses around `let` scrutinee expression let v = X { y: false }; // struct lits needs parens, so these shouldn't warn. if (v == X { y: true }) {} diff --git a/src/test/ui/lint/lint-unnecessary-parens.stderr b/src/test/ui/lint/lint-unnecessary-parens.stderr index 8858c95327322..f5a2564a5ff65 100644 --- a/src/test/ui/lint/lint-unnecessary-parens.stderr +++ b/src/test/ui/lint/lint-unnecessary-parens.stderr @@ -72,19 +72,19 @@ LL | while (true) {} | = note: `#[warn(while_true)]` on by default -error: unnecessary parentheses around `match` head expression +error: unnecessary parentheses around `match` scrutinee expression --> $DIR/lint-unnecessary-parens.rs:51:11 | LL | match (true) { | ^^^^^^ help: remove these parentheses -error: unnecessary parentheses around `let` head expression +error: unnecessary parentheses around `let` scrutinee expression --> $DIR/lint-unnecessary-parens.rs:54:16 | LL | if let 1 = (1) {} | ^^^ help: remove these parentheses -error: unnecessary parentheses around `let` head expression +error: unnecessary parentheses around `let` scrutinee expression --> $DIR/lint-unnecessary-parens.rs:55:19 | LL | while let 1 = (2) {} @@ -114,5 +114,5 @@ error: unnecessary parentheses around assigned value LL | _a += (1); | ^^^ help: remove these parentheses -error: aborting due to 17 previous errors +error: aborting due to 17 previous errors; 1 warning emitted diff --git a/src/test/ui/lint/lint-unused-mut-variables.stderr b/src/test/ui/lint/lint-unused-mut-variables.stderr index b56b3c7569f71..42365f24274b9 100644 --- a/src/test/ui/lint/lint-unused-mut-variables.stderr +++ b/src/test/ui/lint/lint-unused-mut-variables.stderr @@ -218,5 +218,5 @@ note: the lint level is defined here LL | #[deny(unused_mut)] | ^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 25 warnings emitted diff --git a/src/test/ui/lint/lint-uppercase-variables.stderr b/src/test/ui/lint/lint-uppercase-variables.stderr index 1d7e0909e40ab..d476d856e24c5 100644 --- a/src/test/ui/lint/lint-uppercase-variables.stderr +++ b/src/test/ui/lint/lint-uppercase-variables.stderr @@ -85,6 +85,6 @@ 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 6 previous errors +error: aborting due to 6 previous errors; 6 warnings emitted For more information about this error, try `rustc --explain E0170`. diff --git a/src/test/ui/lint/lints-in-foreign-macros.stderr b/src/test/ui/lint/lints-in-foreign-macros.stderr index dcea5adb863f6..ea8d4bf964115 100644 --- a/src/test/ui/lint/lints-in-foreign-macros.stderr +++ b/src/test/ui/lint/lints-in-foreign-macros.stderr @@ -56,3 +56,5 @@ warning: missing documentation for a function LL | baz2!(pub fn undocumented2() {}); | ^^^^^^^^^^^^^^^^^^^^^^ +warning: 6 warnings emitted + diff --git a/src/test/ui/lint/must-use-ops.stderr b/src/test/ui/lint/must-use-ops.stderr index 4490d4afbd605..3fb80f7e79881 100644 --- a/src/test/ui/lint/must-use-ops.stderr +++ b/src/test/ui/lint/must-use-ops.stderr @@ -130,3 +130,5 @@ warning: unused unary operation that must be used LL | *val_pointer; | ^^^^^^^^^^^^ +warning: 21 warnings emitted + diff --git a/src/test/ui/lint/not_found.stderr b/src/test/ui/lint/not_found.stderr index 5a651e9ce0f3a..ea118c73ce784 100644 --- a/src/test/ui/lint/not_found.stderr +++ b/src/test/ui/lint/not_found.stderr @@ -18,3 +18,5 @@ warning: unknown lint: `Warnings` LL | #[deny(Warnings)] | ^^^^^^^^ help: did you mean (notice the capitalization): `warnings` +warning: 3 warnings emitted + diff --git a/src/test/ui/lint/reasons-erroneous.stderr b/src/test/ui/lint/reasons-erroneous.stderr index a84167fed12d0..d7926b73cee57 100644 --- a/src/test/ui/lint/reasons-erroneous.stderr +++ b/src/test/ui/lint/reasons-erroneous.stderr @@ -186,6 +186,6 @@ error[E0452]: malformed lint attribute input LL | #![warn(keyword_idents, reason = "root in rubble", macro_use_extern_crate)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last -error: aborting due to 30 previous errors +error: aborting due to 30 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0452`. diff --git a/src/test/ui/lint/reasons.stderr b/src/test/ui/lint/reasons.stderr index 30bb8daf48a22..150237c6be293 100644 --- a/src/test/ui/lint/reasons.stderr +++ b/src/test/ui/lint/reasons.stderr @@ -26,3 +26,5 @@ LL | nonstandard_style, | ^^^^^^^^^^^^^^^^^ = note: `#[warn(non_snake_case)]` implied by `#[warn(nonstandard_style)]` +warning: 2 warnings emitted + diff --git a/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr b/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr index a79fba9bf3f00..bc0c533032413 100644 --- a/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr +++ b/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr @@ -1,4 +1,4 @@ -TokenStream [Ident { ident: "fn", span: #0 bytes(198..200) }, Ident { ident: "span_preservation", span: #0 bytes(201..218) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(218..220) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "let", span: #0 bytes(228..231) }, Ident { ident: "tst", span: #0 bytes(232..235) }, Punct { ch: '=', spacing: Alone, span: #0 bytes(236..237) }, Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(238), hi: BytePos(241), ctxt: #0 } }, Punct { ch: ';', spacing: Joint, span: #0 bytes(241..242) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(242..243) }, Ident { ident: "match", span: #0 bytes(289..294) }, Ident { ident: "tst", span: #0 bytes(295..298) }, Group { delimiter: Brace, stream: TokenStream [Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(483), hi: BytePos(486), ctxt: #0 } }, Punct { ch: '=', spacing: Joint, span: #0 bytes(487..489) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(487..489) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(490..492) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(492..493) }, Ident { ident: "_", span: #0 bytes(502..503) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(504..506) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(504..506) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(507..509) }], span: #0 bytes(299..515) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(515..516) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(516..517) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(517..518) }], span: #0 bytes(222..562) }] +TokenStream [Ident { ident: "fn", span: #0 bytes(198..200) }, Ident { ident: "span_preservation", span: #0 bytes(201..218) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(218..220) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "let", span: #0 bytes(228..231) }, Ident { ident: "tst", span: #0 bytes(232..235) }, Punct { ch: '=', spacing: Alone, span: #0 bytes(236..237) }, Literal { kind: Integer, symbol: "123", suffix: None, span: #0 bytes(238..241) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(241..242) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(242..243) }, Ident { ident: "match", span: #0 bytes(289..294) }, Ident { ident: "tst", span: #0 bytes(295..298) }, Group { delimiter: Brace, stream: TokenStream [Literal { kind: Integer, symbol: "123", suffix: None, span: #0 bytes(483..486) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(487..489) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(487..489) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(490..492) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(492..493) }, Ident { ident: "_", span: #0 bytes(502..503) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(504..506) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(504..506) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(507..509) }], span: #0 bytes(299..515) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(515..516) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(516..517) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(517..518) }], span: #0 bytes(222..562) }] error: unnecessary trailing semicolon --> $DIR/redundant-semi-proc-macro.rs:9:19 | diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs new file mode 100644 index 0000000000000..12093837d2630 --- /dev/null +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs @@ -0,0 +1,9 @@ +#![feature(non_ascii_idents)] +#![deny(confusable_idents)] +#![allow(uncommon_codepoints, non_upper_case_globals)] + +const s: usize = 42; //~ ERROR identifier pair considered confusable + +fn main() { + let s = "rust"; +} diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr new file mode 100644 index 0000000000000..40ee18acb3cd4 --- /dev/null +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr @@ -0,0 +1,17 @@ +error: identifier pair considered confusable between `s` and `s` + --> $DIR/lint-confusable-idents.rs:5:7 + | +LL | const s: usize = 42; + | ^^ +... +LL | let s = "rust"; + | - this is where the previous identifier occurred + | +note: the lint level is defined here + --> $DIR/lint-confusable-idents.rs:2:9 + | +LL | #![deny(confusable_idents)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/lint/suggestions.stderr b/src/test/ui/lint/suggestions.stderr index 0ef5d72609ae2..0730c22417c05 100644 --- a/src/test/ui/lint/suggestions.stderr +++ b/src/test/ui/lint/suggestions.stderr @@ -105,5 +105,5 @@ LL | #[no_mangle] pub(crate) fn crossfield() {} | | | help: remove this attribute -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 8 warnings emitted diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index a7a788b877a6d..6ba8b43954d3e 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -61,3 +61,5 @@ LL | let fail = -0b1111_1111i8; | = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into the type `i8` and will become `-1i8` +warning: 7 warnings emitted + diff --git a/src/test/ui/lint/unreachable_pub-pub_crate.stderr b/src/test/ui/lint/unreachable_pub-pub_crate.stderr index fd3f2dbc07670..ef38a516e149a 100644 --- a/src/test/ui/lint/unreachable_pub-pub_crate.stderr +++ b/src/test/ui/lint/unreachable_pub-pub_crate.stderr @@ -144,3 +144,5 @@ LL | pub fn catalyze() -> bool; | = help: or consider exporting it for use by other crates +warning: 14 warnings emitted + diff --git a/src/test/ui/lint/unreachable_pub.stderr b/src/test/ui/lint/unreachable_pub.stderr index ad687a4b54e6c..1e554612fa865 100644 --- a/src/test/ui/lint/unreachable_pub.stderr +++ b/src/test/ui/lint/unreachable_pub.stderr @@ -144,3 +144,5 @@ LL | pub fn catalyze() -> bool; | = help: or consider exporting it for use by other crates +warning: 14 warnings emitted + diff --git a/src/test/ui/lint/unused_braces.rs b/src/test/ui/lint/unused_braces.rs new file mode 100644 index 0000000000000..952398ef0685b --- /dev/null +++ b/src/test/ui/lint/unused_braces.rs @@ -0,0 +1,50 @@ +// check-pass +#![warn(unused_braces, unused_parens)] + +fn consume(_: T) {} + +fn main() { + let _ = (7); + //~^WARN unnecessary parentheses + + // Do not emit a lint in these cases, + // as we have to be careful with + // `ref` patterns. + { + let _ = { 7 }; + + if let 7 = { 7 } { } + + match { 7 } { + _ => (), + } + } + + if { true } { + //~^ WARN unnecessary braces + } + + while { false } { + //~^ WARN unnecessary braces + } + + let _: [u8; { 3 }]; + //~^ WARN unnecessary braces + + consume({ 7 }); + //~^ WARN unnecessary braces + + // Do not emit lint for multiline blocks. + let _ = { + 7 + }; + + // Do not emit lint for unsafe blocks. + let _ = unsafe { 7 }; + + // Do not emit lint, as the `{` would then + // be parsed as part of the `return`. + if { return } { + + } +} diff --git a/src/test/ui/lint/unused_braces.stderr b/src/test/ui/lint/unused_braces.stderr new file mode 100644 index 0000000000000..541d64b3e2a5d --- /dev/null +++ b/src/test/ui/lint/unused_braces.stderr @@ -0,0 +1,44 @@ +warning: unnecessary parentheses around assigned value + --> $DIR/unused_braces.rs:7:13 + | +LL | let _ = (7); + | ^^^ help: remove these parentheses + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:2:24 + | +LL | #![warn(unused_braces, unused_parens)] + | ^^^^^^^^^^^^^ + +warning: unnecessary braces around `if` condition + --> $DIR/unused_braces.rs:23:8 + | +LL | if { true } { + | ^^^^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:2:9 + | +LL | #![warn(unused_braces, unused_parens)] + | ^^^^^^^^^^^^^ + +warning: unnecessary braces around `while` condition + --> $DIR/unused_braces.rs:27:11 + | +LL | while { false } { + | ^^^^^^^^^ help: remove these braces + +warning: unnecessary braces around const expression + --> $DIR/unused_braces.rs:31:17 + | +LL | let _: [u8; { 3 }]; + | ^^^^^ help: remove these braces + +warning: unnecessary braces around function argument + --> $DIR/unused_braces.rs:34:13 + | +LL | consume({ 7 }); + | ^^^^^ help: remove these braces + +warning: 5 warnings emitted + diff --git a/src/test/ui/lint/unused_braces_borrow.rs b/src/test/ui/lint/unused_braces_borrow.rs new file mode 100644 index 0000000000000..d0b059744e1fd --- /dev/null +++ b/src/test/ui/lint/unused_braces_borrow.rs @@ -0,0 +1,24 @@ +// check-pass +#![warn(unused_braces)] + +// changing `&{ expr }` to `&expr` changes the semantic of the program +// so we should not warn this case + +#[repr(packed)] +struct A { + a: u8, + b: u32, +} + +fn consume(_: T) {} + +fn main() { + let a = A { + a: 42, + b: 1729, + }; + + consume(&{ a.b }); + consume({ a.b }); + //~^ WARN unnecessary braces +} diff --git a/src/test/ui/lint/unused_braces_borrow.stderr b/src/test/ui/lint/unused_braces_borrow.stderr new file mode 100644 index 0000000000000..187fb9a212e0b --- /dev/null +++ b/src/test/ui/lint/unused_braces_borrow.stderr @@ -0,0 +1,14 @@ +warning: unnecessary braces around function argument + --> $DIR/unused_braces_borrow.rs:22:13 + | +LL | consume({ a.b }); + | ^^^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_braces_borrow.rs:2:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/src/test/ui/lint/unused_import_warning_issue_45268.stderr b/src/test/ui/lint/unused_import_warning_issue_45268.stderr index 1d6338572f31a..fa8699abcbde6 100644 --- a/src/test/ui/lint/unused_import_warning_issue_45268.stderr +++ b/src/test/ui/lint/unused_import_warning_issue_45268.stderr @@ -10,3 +10,5 @@ note: the lint level is defined here LL | #![warn(unused_imports)] // Warning explanation here, it's OK | ^^^^^^^^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/lint/unused_labels.stderr b/src/test/ui/lint/unused_labels.stderr index 809aad2468873..443faebd0f802 100644 --- a/src/test/ui/lint/unused_labels.stderr +++ b/src/test/ui/lint/unused_labels.stderr @@ -61,3 +61,5 @@ LL | LL | 'many_used_shadowed: for _ in 0..10 { | ^^^^^^^^^^^^^^^^^^^ lifetime 'many_used_shadowed already in scope +warning: 9 warnings emitted + diff --git a/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr b/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr index c3bf77a3a6f2d..5fb67fd7c95a3 100644 --- a/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr +++ b/src/test/ui/lint/unused_parens_remove_json_suggestion.stderr @@ -46,14 +46,14 @@ LL | while(true && false) { | ^^^^^^^^^^^^^^^ help: remove these parentheses "} -{"message":"unnecessary parentheses around `for` head expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":987,"byte_end":995,"line_start":44,"line_end":44,"column_start":18,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){ +{"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":987,"byte_end":995,"line_start":44,"line_end":44,"column_start":18,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){ --> $DIR/unused_parens_remove_json_suggestion.rs:44:18 | LL | for _ in (0 .. 3){ | ^^^^^^^^ help: remove these parentheses "} -{"message":"unnecessary parentheses around `for` head expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1088,"byte_end":1096,"line_start":49,"line_end":49,"column_start":14,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) { +{"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":1088,"byte_end":1096,"line_start":49,"line_end":49,"column_start":14,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) { --> $DIR/unused_parens_remove_json_suggestion.rs:49:14 | LL | for _ in (0 .. 3) { diff --git a/src/test/ui/lint/use-redundant.stderr b/src/test/ui/lint/use-redundant.stderr index 85a5cce4dd071..c861a1956e1d1 100644 --- a/src/test/ui/lint/use-redundant.stderr +++ b/src/test/ui/lint/use-redundant.stderr @@ -25,3 +25,5 @@ LL | use crate::foo::Bar; LL | use crate::foo::Bar; | ^^^^^^^^^^^^^^^ +warning: 3 warnings emitted + diff --git a/src/test/ui/lint/use_suggestion_json.rs b/src/test/ui/lint/use_suggestion_json.rs index 1828b8c2dc735..d7efa4aac6516 100644 --- a/src/test/ui/lint/use_suggestion_json.rs +++ b/src/test/ui/lint/use_suggestion_json.rs @@ -1,5 +1,6 @@ // ignore-cloudabi // ignore-windows +// ignore-sgx std::os::fortanix_sgx::usercalls::alloc::Iter changes compiler suggestions // compile-flags: --error-format pretty-json --json=diagnostic-rendered-ansi // The output for humans should just highlight the whole span without showing diff --git a/src/test/ui/lint/use_suggestion_json.stderr b/src/test/ui/lint/use_suggestion_json.stderr index 7176f17bc3fab..d0d91bb61f457 100644 --- a/src/test/ui/lint/use_suggestion_json.stderr +++ b/src/test/ui/lint/use_suggestion_json.stderr @@ -72,10 +72,10 @@ mod foo { "spans": [ { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 471, - "byte_end": 475, - "line_start": 12, - "line_end": 12, + "byte_start": 560, + "byte_end": 564, + "line_start": 13, + "line_end": 13, "column_start": 12, "column_end": 16, "is_primary": true, @@ -94,16 +94,16 @@ mod foo { ], "children": [ { - "message": "possible candidates are found in other modules, you can import them into scope", + "message": "consider importing one of these items", "code": null, "level": "help", "spans": [ { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -123,10 +123,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -146,10 +146,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -169,10 +169,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -192,10 +192,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -215,10 +215,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -238,10 +238,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -261,10 +261,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -284,10 +284,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -307,10 +307,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -330,10 +330,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -353,10 +353,10 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 448, - "byte_end": 448, - "line_start": 11, - "line_end": 11, + "byte_start": 537, + "byte_end": 537, + "line_start": 12, + "line_end": 12, "column_start": 1, "column_end": 1, "is_primary": true, @@ -380,12 +380,12 @@ mod foo { } ], "rendered": "\u001b[0m\u001b[1m\u001b[38;5;9merror[E0412]\u001b[0m\u001b[0m\u001b[1m: cannot find type `Iter` in this scope\u001b[0m -\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0m$DIR/use_suggestion_json.rs:12:12\u001b[0m +\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0m$DIR/use_suggestion_json.rs:13:12\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m let x: Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m -\u001b[0m\u001b[1m\u001b[38;5;14mhelp\u001b[0m\u001b[0m: possible candidates are found in other modules, you can import them into scope\u001b[0m +\u001b[0m\u001b[1m\u001b[38;5;14mhelp\u001b[0m\u001b[0m: consider importing one of these items\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0muse std::collections::binary_heap::Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m diff --git a/src/test/ui/liveness/liveness-dead.stderr b/src/test/ui/liveness/liveness-dead.stderr index 12680ab11568f..e9d20cf981fbd 100644 --- a/src/test/ui/liveness/liveness-dead.stderr +++ b/src/test/ui/liveness/liveness-dead.stderr @@ -1,8 +1,8 @@ error: value assigned to `x` is never read - --> $DIR/liveness-dead.rs:9:13 + --> $DIR/liveness-dead.rs:9:9 | LL | let mut x: isize = 3; - | ^ + | ^^^^^ | note: the lint level is defined here --> $DIR/liveness-dead.rs:2:9 @@ -20,10 +20,10 @@ LL | x = 4; = help: maybe it is overwritten before being read? error: value passed to `x` is never read - --> $DIR/liveness-dead.rs:20:11 + --> $DIR/liveness-dead.rs:20:7 | LL | fn f4(mut x: i32) { - | ^ + | ^^^^^ | = help: maybe it is overwritten before being read? diff --git a/src/test/ui/liveness/liveness-move-in-while.stderr b/src/test/ui/liveness/liveness-move-in-while.stderr index 8350f2708eae0..45c00e8d6d331 100644 --- a/src/test/ui/liveness/liveness-move-in-while.stderr +++ b/src/test/ui/liveness/liveness-move-in-while.stderr @@ -29,6 +29,6 @@ LL | println!("{}", y); LL | while true { while true { while true { x = y; x.clone(); } } } | - value moved here, in previous iteration of loop -error: aborting due to previous error +error: aborting due to previous error; 3 warnings emitted For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/liveness/liveness-unused.stderr b/src/test/ui/liveness/liveness-unused.stderr index 42187330a3eb1..2c5550ac47f22 100644 --- a/src/test/ui/liveness/liveness-unused.stderr +++ b/src/test/ui/liveness/liveness-unused.stderr @@ -44,10 +44,10 @@ LL | let x = 3; | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used - --> $DIR/liveness-unused.rs:30:13 + --> $DIR/liveness-unused.rs:30:9 | LL | let mut x = 3; - | ^ + | ^^^^^ | = note: consider using `_x` instead @@ -65,10 +65,10 @@ LL | #![deny(unused_assignments)] = help: maybe it is overwritten before being read? error: variable `z` is assigned to, but never used - --> $DIR/liveness-unused.rs:37:13 + --> $DIR/liveness-unused.rs:37:9 | LL | let mut z = 3; - | ^ + | ^^^^^ | = note: consider using `_z` instead @@ -112,5 +112,5 @@ LL | x = 0; | = help: maybe it is overwritten before being read? -error: aborting due to 13 previous errors +error: aborting due to 13 previous errors; 1 warning emitted diff --git a/src/test/ui/llvm-asm/issue-51431.rs b/src/test/ui/llvm-asm/issue-51431.rs new file mode 100644 index 0000000000000..ca06bdab27b96 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-51431.rs @@ -0,0 +1,11 @@ +// build-fail +// ignore-emscripten no llvm_asm! support + +#![feature(llvm_asm)] + +fn main() { + unsafe { + llvm_asm! {"mov $0,$1"::"0"("bx"),"1"(0x00)} + //~^ ERROR: invalid value for constraint in inline assembly + } +} diff --git a/src/test/ui/llvm-asm/issue-51431.stderr b/src/test/ui/llvm-asm/issue-51431.stderr new file mode 100644 index 0000000000000..b4b39a2a44ec3 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-51431.stderr @@ -0,0 +1,9 @@ +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/issue-51431.rs:8:37 + | +LL | llvm_asm! {"mov $0,$1"::"0"("bx"),"1"(0x00)} + | ^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0669`. diff --git a/src/test/ui/llvm-asm/issue-54067.rs b/src/test/ui/llvm-asm/issue-54067.rs new file mode 100644 index 0000000000000..f2e097222bd7d --- /dev/null +++ b/src/test/ui/llvm-asm/issue-54067.rs @@ -0,0 +1,12 @@ +// check-pass +// ignore-emscripten no llvm_asm! support + +#![feature(llvm_asm)] + +pub fn boot(addr: Option) { + unsafe { + llvm_asm!("mov sp, $0"::"r" (addr)); + } +} + +fn main() {} diff --git a/src/test/ui/llvm-asm/issue-62046.rs b/src/test/ui/llvm-asm/issue-62046.rs new file mode 100644 index 0000000000000..fd4d9bdd23dbf --- /dev/null +++ b/src/test/ui/llvm-asm/issue-62046.rs @@ -0,0 +1,11 @@ +// build-fail +// ignore-emscripten no asm! support + +#![feature(llvm_asm)] + +fn main() { + unsafe { + llvm_asm!("nop" : "+r"("r15")); + //~^ malformed inline assembly + } +} diff --git a/src/test/ui/asm/issue-62046.stderr b/src/test/ui/llvm-asm/issue-62046.stderr similarity index 76% rename from src/test/ui/asm/issue-62046.stderr rename to src/test/ui/llvm-asm/issue-62046.stderr index a38a300548d48..cf27052df05aa 100644 --- a/src/test/ui/asm/issue-62046.stderr +++ b/src/test/ui/llvm-asm/issue-62046.stderr @@ -1,8 +1,8 @@ error[E0668]: malformed inline assembly --> $DIR/issue-62046.rs:8:9 | -LL | asm!("nop" : "+r"("r15")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | llvm_asm!("nop" : "+r"("r15")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/llvm-asm/issue-69092.rs b/src/test/ui/llvm-asm/issue-69092.rs new file mode 100644 index 0000000000000..ecce7bfdf5bba --- /dev/null +++ b/src/test/ui/llvm-asm/issue-69092.rs @@ -0,0 +1,10 @@ +// build-fail +// ignore-emscripten no asm! support +// Regression test for #69092 + +#![feature(llvm_asm)] + +fn main() { + unsafe { llvm_asm!(".ascii \"Xen\0\""); } + //~^ ERROR: :1:9: error: expected string in '.ascii' directive +} diff --git a/src/test/ui/llvm-asm/issue-69092.stderr b/src/test/ui/llvm-asm/issue-69092.stderr new file mode 100644 index 0000000000000..35f77edc3c402 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-69092.stderr @@ -0,0 +1,11 @@ +error: :1:9: error: expected string in '.ascii' directive + .ascii "Xen + ^ + + --> $DIR/issue-69092.rs:8:14 + | +LL | unsafe { llvm_asm!(".ascii \"Xen\0\""); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs new file mode 100644 index 0000000000000..9f5662cbd1e93 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs @@ -0,0 +1,25 @@ +// ignore-android +// ignore-arm +// ignore-aarch64 +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64"))] + +pub fn main() { + unsafe { + // clobber formatted as register input/output + llvm_asm!("xor %eax, %eax" : : : "{eax}"); + //~^ ERROR clobber should not be surrounded by braces + } +} diff --git a/src/test/ui/llvm-asm/llvm-asm-bad-clobber.stderr b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.stderr new file mode 100644 index 0000000000000..9ecd12caa0e2a --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.stderr @@ -0,0 +1,9 @@ +error[E0664]: clobber should not be surrounded by braces + --> $DIR/llvm-asm-bad-clobber.rs:22:42 + | +LL | llvm_asm!("xor %eax, %eax" : : : "{eax}"); + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0664`. diff --git a/src/test/ui/llvm-asm/llvm-asm-concat-src.rs b/src/test/ui/llvm-asm/llvm-asm-concat-src.rs new file mode 100644 index 0000000000000..1dc1c859c6b0e --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-concat-src.rs @@ -0,0 +1,9 @@ +// run-pass +// pretty-expanded FIXME #23616 +// ignore-emscripten no asm + +#![feature(llvm_asm)] + +pub fn main() { + unsafe { llvm_asm!(concat!("", "")) }; +} diff --git a/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs new file mode 100644 index 0000000000000..b791ec3e8c8b1 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs @@ -0,0 +1,34 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + let y: isize; + unsafe { + llvm_asm!("mov $1, $0" : "=r"(x) : "=r"(5)); //~ ERROR operand constraint contains '=' + llvm_asm!("mov $1, $0" : "=r"(y) : "+r"(5)); //~ ERROR operand constraint contains '+' + } + foo(x); + foo(y); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.stderr b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.stderr new file mode 100644 index 0000000000000..e94ac94f59f9a --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.stderr @@ -0,0 +1,16 @@ +error[E0662]: input operand constraint contains '=' + --> $DIR/llvm-asm-in-bad-modifier.rs:23:44 + | +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "=r"(5)); + | ^^^^ + +error[E0663]: input operand constraint contains '+' + --> $DIR/llvm-asm-in-bad-modifier.rs:24:44 + | +LL | llvm_asm!("mov $1, $0" : "=r"(y) : "+r"(5)); + | ^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0662, E0663. +For more information about an error, try `rustc --explain E0662`. diff --git a/src/test/ui/llvm-asm/llvm-asm-in-moved.rs b/src/test/ui/llvm-asm/llvm-asm-in-moved.rs new file mode 100644 index 0000000000000..35f4d92c8ffbc --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-moved.rs @@ -0,0 +1,31 @@ +// run-pass + +#![feature(llvm_asm)] +#![allow(dead_code)] + +use std::cell::Cell; + +#[repr(C)] +struct NoisyDrop<'a>(&'a Cell<&'static str>); +impl<'a> Drop for NoisyDrop<'a> { + fn drop(&mut self) { + self.0.set("destroyed"); + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn main() { + let status = Cell::new("alive"); + { + let _y: Box; + let x = Box::new(NoisyDrop(&status)); + unsafe { + llvm_asm!("mov $1, $0" : "=r"(_y) : "r"(x)); + } + assert_eq!(status.get(), "alive"); + } + assert_eq!(status.get(), "destroyed"); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs b/src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs new file mode 100644 index 0000000000000..acefabd8a666e --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs @@ -0,0 +1,56 @@ +// run-pass + +#![feature(llvm_asm)] + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe fn next_power_of_2(n: u32) -> u32 { + let mut tmp = n; + llvm_asm!("dec $0" : "+rm"(tmp) :: "cc"); + let mut shift = 1_u32; + while shift <= 16 { + llvm_asm!( + "shr %cl, $2 + or $2, $0 + shl $$1, $1" + : "+&rm"(tmp), "+{ecx}"(shift) : "r"(tmp) : "cc" + ); + } + llvm_asm!("inc $0" : "+rm"(tmp) :: "cc"); + return tmp; +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn main() { + unsafe { + assert_eq!(64, next_power_of_2(37)); + assert_eq!(2147483648, next_power_of_2(2147483647)); + } + + let mut y: isize = 5; + let x: isize; + unsafe { + // Treat the output as initialization. + llvm_asm!( + "shl $2, $1 + add $3, $1 + mov $1, $0" + : "=r"(x), "+r"(y) : "i"(3_usize), "ir"(7_usize) : "cc" + ); + } + assert_eq!(x, 47); + assert_eq!(y, 47); + + let mut x = x + 1; + assert_eq!(x, 48); + + unsafe { + // Assignment to mutable. + // Early clobber "&": + // Forbids the use of a single register by both operands. + llvm_asm!("shr $$2, $1; add $1, $0" : "+&r"(x) : "r"(x) : "cc"); + } + assert_eq!(x, 60); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-indirect-memory.rs b/src/test/ui/llvm-asm/llvm-asm-indirect-memory.rs new file mode 100644 index 0000000000000..556ad83a4ead8 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-indirect-memory.rs @@ -0,0 +1,43 @@ +// run-pass + +#![feature(llvm_asm)] + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn read(ptr: &u32) -> u32 { + let out: u32; + unsafe { + llvm_asm!("mov $1, $0" : "=r" (out) : "*m" (ptr)); + } + out +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn write(ptr: &mut u32, val: u32) { + unsafe { + llvm_asm!("mov $1, $0" : "=*m" (ptr) : "r" (val)); + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn replace(ptr: &mut u32, val: u32) -> u32 { + let out: u32; + unsafe { + llvm_asm!("mov $0, $1; mov $2, $0" : "+*m" (ptr), "=&r" (out) : "r" (val)); + } + out +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn main() { + let a = 1; + assert_eq!(read(&a), 1); + let mut b = 2; + write(&mut b, 3); + assert_eq!(b, 3); + let mut c = 4; + assert_eq!(replace(&mut c, 5), 4); + assert_eq!(c, 5); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-literal-escaping.rs b/src/test/ui/llvm-asm/llvm-asm-literal-escaping.rs new file mode 100644 index 0000000000000..5d45f5084c5ee --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-literal-escaping.rs @@ -0,0 +1,12 @@ +// build-pass +// only-x86_64 + +#![feature(llvm_asm)] + +fn main() { + unsafe { + // "nop" :: "r"(x) : "eax" : "volatile" + let x = 10; + llvm_asm!("\x6Eop" :: "\x72"(x) : "\x65ax" : "\x76olatile"); + } +} diff --git a/src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs new file mode 100644 index 0000000000000..3c44fc90ef3f2 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs @@ -0,0 +1,35 @@ +// check-pass +// ignore-android +// ignore-arm +// ignore-aarch64 +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64"))] +fn main() { + // assignment not dead + let mut x: isize = 0; + unsafe { + // extra colon + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc"); + //~^ WARNING unrecognized option + } + assert_eq!(x, 5); + + unsafe { + // comma in place of a colon + llvm_asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile"); + //~^ WARNING expected a clobber, found an option + } + assert_eq!(x, 13); +} diff --git a/src/test/ui/llvm-asm/llvm-asm-misplaced-option.stderr b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.stderr new file mode 100644 index 0000000000000..21fd27825a185 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.stderr @@ -0,0 +1,14 @@ +warning: unrecognized option + --> $DIR/llvm-asm-misplaced-option.rs:24:69 + | +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc"); + | ^^^^ + +warning: expected a clobber, found an option + --> $DIR/llvm-asm-misplaced-option.rs:31:85 + | +LL | llvm_asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile"); + | ^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs new file mode 100644 index 0000000000000..1a46879f9f291 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs @@ -0,0 +1,34 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + x = 1; + foo(x); + unsafe { + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5)); + //~^ ERROR cannot assign twice to immutable variable `x` + } + foo(x); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr new file mode 100644 index 0000000000000..e110aec220936 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr @@ -0,0 +1,14 @@ +error[E0384]: cannot assign twice to immutable variable `x` + --> $DIR/llvm-asm-out-assign-imm.rs:24:39 + | +LL | let x: isize; + | - help: make this binding mutable: `mut x` +LL | x = 1; + | ----- first assignment to `x` +... +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5)); + | ^ cannot assign twice to immutable variable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0384`. diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign.rs b/src/test/ui/llvm-asm/llvm-asm-out-assign.rs new file mode 100644 index 0000000000000..321f28565ff18 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign.rs @@ -0,0 +1,25 @@ +// run-pass + +#![feature(llvm_asm)] + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn main() { + let x: isize; + unsafe { + // Treat the output as initialization. + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5_usize)); + } + assert_eq!(x, 5); + + let mut x = x + 1; + assert_eq!(x, 6); + + unsafe { + // Assignment to mutable. + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(x + 7)); + } + assert_eq!(x, 13); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs new file mode 100644 index 0000000000000..d198437c50894 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs @@ -0,0 +1,31 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + unsafe { + llvm_asm!("mov $1, $0" : "r"(x) : "r"(5)); //~ ERROR output operand constraint lacks '=' + } + foo(x); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.stderr b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.stderr new file mode 100644 index 0000000000000..1f2b272792435 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.stderr @@ -0,0 +1,9 @@ +error[E0661]: output operand constraint lacks '=' or '+' + --> $DIR/llvm-asm-out-no-modifier.rs:22:34 + | +LL | llvm_asm!("mov $1, $0" : "r"(x) : "r"(5)); + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0661`. diff --git a/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs new file mode 100644 index 0000000000000..d45498d4bb4a1 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs @@ -0,0 +1,32 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + unsafe { + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(x)); + //~^ ERROR use of possibly-uninitialized variable: `x` + } + foo(x); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.stderr b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.stderr new file mode 100644 index 0000000000000..a22ebe4e4d9db --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.stderr @@ -0,0 +1,9 @@ +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/llvm-asm-out-read-uninit.rs:22:48 + | +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "r"(x)); + | ^ use of possibly-uninitialized `x` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0381`. diff --git a/src/test/ui/llvm-asm/llvm-asm-parse-errors.rs b/src/test/ui/llvm-asm/llvm-asm-parse-errors.rs new file mode 100644 index 0000000000000..d458be815296b --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-parse-errors.rs @@ -0,0 +1,15 @@ +#![feature(llvm_asm)] + +fn main() { + llvm_asm!(); //~ ERROR requires a string literal as an argument + llvm_asm!("nop" : struct); //~ ERROR expected string literal + llvm_asm!("mov %eax, $$0x2" : struct); //~ ERROR expected string literal + llvm_asm!("mov %eax, $$0x2" : "={eax}" struct); //~ ERROR expected `(` + llvm_asm!("mov %eax, $$0x2" : "={eax}"(struct)); //~ ERROR expected expression + llvm_asm!("in %dx, %al" : "={al}"(result) : struct); //~ ERROR expected string literal + llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); //~ ERROR expected `(` + llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); //~ ERROR expected expression + llvm_asm!("mov $$0x200, %eax" : : : struct); //~ ERROR expected string literal + llvm_asm!("mov eax, 2" : "={eax}"(foo) : : : struct); //~ ERROR expected string literal + llvm_asm!(123); //~ ERROR inline assembly must be a string literal +} diff --git a/src/test/ui/llvm-asm/llvm-asm-parse-errors.stderr b/src/test/ui/llvm-asm/llvm-asm-parse-errors.stderr new file mode 100644 index 0000000000000..1fd46809f3eed --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-parse-errors.stderr @@ -0,0 +1,68 @@ +error: macro requires a string literal as an argument + --> $DIR/llvm-asm-parse-errors.rs:4:5 + | +LL | llvm_asm!(); + | ^^^^^^^^^^^^ string literal required + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:5:23 + | +LL | llvm_asm!("nop" : struct); + | ^^^^^^ not a string literal + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:6:35 + | +LL | llvm_asm!("mov %eax, $$0x2" : struct); + | ^^^^^^ not a string literal + +error: expected `(`, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:7:44 + | +LL | llvm_asm!("mov %eax, $$0x2" : "={eax}" struct); + | ^^^^^^ expected `(` + +error: expected expression, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:8:44 + | +LL | llvm_asm!("mov %eax, $$0x2" : "={eax}"(struct)); + | ^^^^^^ expected expression + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:9:49 + | +LL | llvm_asm!("in %dx, %al" : "={al}"(result) : struct); + | ^^^^^^ not a string literal + +error: expected `(`, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:10:56 + | +LL | llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); + | ^^^^^^ expected `(` + +error: expected expression, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:11:56 + | +LL | llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); + | ^^^^^^ expected expression + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:12:41 + | +LL | llvm_asm!("mov $$0x200, %eax" : : : struct); + | ^^^^^^ not a string literal + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:13:50 + | +LL | llvm_asm!("mov eax, 2" : "={eax}"(foo) : : : struct); + | ^^^^^^ not a string literal + +error: inline assembly must be a string literal + --> $DIR/llvm-asm-parse-errors.rs:14:15 + | +LL | llvm_asm!(123); + | ^^^ + +error: aborting due to 11 previous errors + diff --git a/src/test/ui/loops/for-each-loop-panic.rs b/src/test/ui/loops/for-each-loop-panic.rs new file mode 100644 index 0000000000000..5156999f4db9c --- /dev/null +++ b/src/test/ui/loops/for-each-loop-panic.rs @@ -0,0 +1,9 @@ +// run-fail +// error-pattern:moop +// ignore-emscripten no processes + +fn main() { + for _ in 0_usize..10_usize { + panic!("moop"); + } +} diff --git a/src/test/ui/loops/loop-break-value.stderr b/src/test/ui/loops/loop-break-value.stderr index 3bb0cd50d63fb..0503d3d4c7817 100644 --- a/src/test/ui/loops/loop-break-value.stderr +++ b/src/test/ui/loops/loop-break-value.stderr @@ -151,7 +151,7 @@ LL | break; | expected integer, found `()` | help: give it a value of the expected type: `break value` -error: aborting due to 16 previous errors +error: aborting due to 16 previous errors; 1 warning emitted Some errors have detailed explanations: E0308, E0571. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/loops/loops-reject-duplicate-labels-2.rs b/src/test/ui/loops/loops-reject-duplicate-labels-2.rs index 316ee64072de0..a0f3aeffe9f7c 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels-2.rs +++ b/src/test/ui/loops/loops-reject-duplicate-labels-2.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // ignore-tidy-linelength diff --git a/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr b/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr index fc42449aa58bc..3c8e2938d4188 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr +++ b/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr @@ -62,3 +62,5 @@ LL | { 'lt: loop { break; } } LL | { 'lt: while let Some(_) = None:: { break; } } | ^^^ lifetime 'lt already in scope +warning: 8 warnings emitted + diff --git a/src/test/ui/loops/loops-reject-duplicate-labels.rs b/src/test/ui/loops/loops-reject-duplicate-labels.rs index 5ed8b2f416ecd..a501ac18588fb 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels.rs +++ b/src/test/ui/loops/loops-reject-duplicate-labels.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // ignore-tidy-linelength diff --git a/src/test/ui/loops/loops-reject-duplicate-labels.stderr b/src/test/ui/loops/loops-reject-duplicate-labels.stderr index 4574c5ca0bd51..5a3e5158fed8c 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels.stderr +++ b/src/test/ui/loops/loops-reject-duplicate-labels.stderr @@ -62,3 +62,5 @@ LL | 'lt: loop { break; } LL | 'lt: while let Some(_) = None:: { break; } | ^^^ lifetime 'lt already in scope +warning: 8 warnings emitted + diff --git a/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.rs b/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.rs index 9047fbb95a2ea..741ea0c1ca8bc 100644 --- a/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.rs +++ b/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.rs @@ -1,7 +1,7 @@ // Issue #21633: reject duplicate loop labels in function bodies. // This is testing interaction between lifetime-params and labels. -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![allow(dead_code, unused_variables)] diff --git a/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.stderr b/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.stderr index e9f93abb62796..c27e61190bbec 100644 --- a/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.stderr +++ b/src/test/ui/loops/loops-reject-labels-shadowing-lifetimes.stderr @@ -100,3 +100,5 @@ LL | fn meth_bad<'bad>(&self) { LL | 'bad: loop { break 'bad; } | ^^^^ lifetime 'bad already in scope +warning: 12 warnings emitted + diff --git a/src/test/ui/loops/loops-reject-lifetime-shadowing-label.rs b/src/test/ui/loops/loops-reject-lifetime-shadowing-label.rs index 9bb6a253b7f4a..3212b78b08cd8 100644 --- a/src/test/ui/loops/loops-reject-lifetime-shadowing-label.rs +++ b/src/test/ui/loops/loops-reject-lifetime-shadowing-label.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![allow(dead_code, unused_variables)] diff --git a/src/test/ui/loops/loops-reject-lifetime-shadowing-label.stderr b/src/test/ui/loops/loops-reject-lifetime-shadowing-label.stderr index e5d376675c62a..b31ef273fc625 100644 --- a/src/test/ui/loops/loops-reject-lifetime-shadowing-label.stderr +++ b/src/test/ui/loops/loops-reject-lifetime-shadowing-label.stderr @@ -6,3 +6,5 @@ LL | 'a: loop { LL | let b = Box::new(|x: &i8| *x) as Box Fn(&'a i8) -> i8>; | ^^ lifetime 'a already in scope +warning: 1 warning emitted + diff --git a/src/test/ui/lto-and-no-bitcode-in-rlib.rs b/src/test/ui/lto-and-no-bitcode-in-rlib.rs new file mode 100644 index 0000000000000..f381240e70a44 --- /dev/null +++ b/src/test/ui/lto-and-no-bitcode-in-rlib.rs @@ -0,0 +1,3 @@ +// compile-flags: -C lto -C embed-bitcode=no + +fn main() {} diff --git a/src/test/ui/lto-and-no-bitcode-in-rlib.stderr b/src/test/ui/lto-and-no-bitcode-in-rlib.stderr new file mode 100644 index 0000000000000..11e370e914c8e --- /dev/null +++ b/src/test/ui/lto-and-no-bitcode-in-rlib.stderr @@ -0,0 +1,2 @@ +error: options `-C embed-bitcode=no` and `-C lto` are incompatible + diff --git a/src/test/ui/lto-duplicate-symbols.stderr b/src/test/ui/lto-duplicate-symbols.stderr index b7a930b61cc96..713b79bae32e6 100644 --- a/src/test/ui/lto-duplicate-symbols.stderr +++ b/src/test/ui/lto-duplicate-symbols.stderr @@ -1,6 +1,6 @@ warning: Linking globals named 'foo': symbol multiply defined! -error: failed to load bc of "lto_duplicate_symbols2.3a1fbbbh-cgu.0": +error: failed to load bc of "lto-duplicate-symbols2.lto_duplicate_symbols2.3a1fbbbh-cgu.0.rcgu.o": -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/lto-rustc-loads-linker-plugin.rs b/src/test/ui/lto-rustc-loads-linker-plugin.rs new file mode 100644 index 0000000000000..6ef1d4540b8d7 --- /dev/null +++ b/src/test/ui/lto-rustc-loads-linker-plugin.rs @@ -0,0 +1,17 @@ +// compile-flags: -C lto +// aux-build:lto-rustc-loads-linker-plugin.rs +// run-pass +// no-prefer-dynamic + +// This test ensures that if a dependency was compiled with +// `-Clinker-plugin-lto` then we can compile with `-Clto` and still link against +// that upstream rlib. This should work because LTO implies we're not actually +// linking against upstream rlibs since we're generating the object code +// locally. This test will fail if rustc can't find bytecode in rlibs compiled +// with `-Clinker-plugin-lto`. + +extern crate lto_rustc_loads_linker_plugin; + +fn main() { + lto_rustc_loads_linker_plugin::foo(); +} diff --git a/src/test/ui/lto-thin-rustc-loads-linker-plugin.rs b/src/test/ui/lto-thin-rustc-loads-linker-plugin.rs new file mode 100644 index 0000000000000..4d54ce32fb563 --- /dev/null +++ b/src/test/ui/lto-thin-rustc-loads-linker-plugin.rs @@ -0,0 +1,13 @@ +// compile-flags: -C lto=thin +// aux-build:lto-rustc-loads-linker-plugin.rs +// run-pass +// no-prefer-dynamic + +// Same as the adjacent `lto-thin-rustc-loads-linker-plugin.rs` test, only with +// ThinLTO. + +extern crate lto_rustc_loads_linker_plugin; + +fn main() { + lto_rustc_loads_linker_plugin::foo(); +} diff --git a/src/test/ui/macro-quote-cond.rs b/src/test/ui/macro-quote-cond.rs index 569451e42593c..48307f4d9ae64 100644 --- a/src/test/ui/macro-quote-cond.rs +++ b/src/test/ui/macro-quote-cond.rs @@ -1,9 +1,7 @@ // run-pass - -#![allow(unused_parens)] // aux-build:cond_plugin.rs -#![feature(proc_macro_hygiene)] +#![allow(unused_parens)] extern crate cond_plugin; diff --git a/src/test/ui/macro-quote-test.rs b/src/test/ui/macro-quote-test.rs index 7815b8e6df1ee..2ba61acadcb85 100644 --- a/src/test/ui/macro-quote-test.rs +++ b/src/test/ui/macro-quote-test.rs @@ -1,10 +1,8 @@ -// run-pass // Test that a macro can emit delimiters with nothing inside - `()`, `{}` +// run-pass // aux-build:hello_macro.rs -#![feature(proc_macro_hygiene)] - extern crate hello_macro; fn main() { diff --git a/src/test/ui/macros/assert-as-macro.rs b/src/test/ui/macros/assert-as-macro.rs new file mode 100644 index 0000000000000..23c0548081333 --- /dev/null +++ b/src/test/ui/macros/assert-as-macro.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:assertion failed: 1 == 2 +// ignore-emscripten no processes + +fn main() { + assert!(1 == 2); +} diff --git a/src/test/run-fail/assert-eq-macro-panic.rs b/src/test/ui/macros/assert-eq-macro-panic.rs similarity index 76% rename from src/test/run-fail/assert-eq-macro-panic.rs rename to src/test/ui/macros/assert-eq-macro-panic.rs index 863fec12d7475..5e505c30b3503 100644 --- a/src/test/run-fail/assert-eq-macro-panic.rs +++ b/src/test/ui/macros/assert-eq-macro-panic.rs @@ -1,6 +1,8 @@ +// run-fail // error-pattern:assertion failed: `(left == right)` // error-pattern: left: `14` // error-pattern:right: `15` +// ignore-emscripten no processes fn main() { assert_eq!(14, 15); diff --git a/src/test/ui/macros/assert-macro-explicit.rs b/src/test/ui/macros/assert-macro-explicit.rs new file mode 100644 index 0000000000000..578ef5632780f --- /dev/null +++ b/src/test/ui/macros/assert-macro-explicit.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'assertion failed: false' +// ignore-emscripten no processes + +fn main() { + assert!(false); +} diff --git a/src/test/ui/macros/assert-macro-fmt.rs b/src/test/ui/macros/assert-macro-fmt.rs new file mode 100644 index 0000000000000..b8d319d85f404 --- /dev/null +++ b/src/test/ui/macros/assert-macro-fmt.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'test-assert-fmt 42 rust' +// ignore-emscripten no processes + +fn main() { + assert!(false, "test-assert-fmt {} {}", 42, "rust"); +} diff --git a/src/test/ui/macros/assert-macro-owned.rs b/src/test/ui/macros/assert-macro-owned.rs new file mode 100644 index 0000000000000..b50fe65c0150f --- /dev/null +++ b/src/test/ui/macros/assert-macro-owned.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'test-assert-owned' +// ignore-emscripten no processes + +fn main() { + assert!(false, "test-assert-owned".to_string()); +} diff --git a/src/test/ui/macros/assert-macro-static.rs b/src/test/ui/macros/assert-macro-static.rs new file mode 100644 index 0000000000000..dc5274a7e8880 --- /dev/null +++ b/src/test/ui/macros/assert-macro-static.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'test-assert-static' +// ignore-emscripten no processes + +fn main() { + assert!(false, "test-assert-static"); +} diff --git a/src/test/run-fail/assert-ne-macro-panic.rs b/src/test/ui/macros/assert-ne-macro-panic.rs similarity index 76% rename from src/test/run-fail/assert-ne-macro-panic.rs rename to src/test/ui/macros/assert-ne-macro-panic.rs index f55ef2b3ff5ca..4f507d7b54d99 100644 --- a/src/test/run-fail/assert-ne-macro-panic.rs +++ b/src/test/ui/macros/assert-ne-macro-panic.rs @@ -1,6 +1,8 @@ +// run-fail // error-pattern:assertion failed: `(left != right)` // error-pattern: left: `14` // error-pattern:right: `14` +// ignore-emscripten no processes fn main() { assert_ne!(14, 14); diff --git a/src/test/ui/macros/auxiliary/proc_macro_sequence.rs b/src/test/ui/macros/auxiliary/proc_macro_sequence.rs index c460db36f1aa3..cb8055de6e3f6 100644 --- a/src/test/ui/macros/auxiliary/proc_macro_sequence.rs +++ b/src/test/ui/macros/auxiliary/proc_macro_sequence.rs @@ -2,7 +2,7 @@ // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(proc_macro_span, proc_macro_hygiene, proc_macro_quote)] +#![feature(proc_macro_span, proc_macro_quote)] extern crate proc_macro; diff --git a/src/test/ui/macros/die-macro-2.rs b/src/test/ui/macros/die-macro-2.rs new file mode 100644 index 0000000000000..ebbce528a18fa --- /dev/null +++ b/src/test/ui/macros/die-macro-2.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:test +// ignore-emscripten no processes + +fn main() { + panic!("test"); +} diff --git a/src/test/ui/macros/die-macro-expr.rs b/src/test/ui/macros/die-macro-expr.rs new file mode 100644 index 0000000000000..c4b5f68ddf9ff --- /dev/null +++ b/src/test/ui/macros/die-macro-expr.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:test +// ignore-emscripten no processes + +fn main() { + let __isize: isize = panic!("test"); +} diff --git a/src/test/ui/macros/die-macro-pure.rs b/src/test/ui/macros/die-macro-pure.rs new file mode 100644 index 0000000000000..588fbe61b0e76 --- /dev/null +++ b/src/test/ui/macros/die-macro-pure.rs @@ -0,0 +1,11 @@ +// run-fail +// error-pattern:test +// ignore-emscripten no processes + +fn f() { + panic!("test"); +} + +fn main() { + f(); +} diff --git a/src/test/ui/macros/issue-34421-mac-expr-bad-stmt-good-add-semi.rs b/src/test/ui/macros/issue-34421-mac-expr-bad-stmt-good-add-semi.rs new file mode 100644 index 0000000000000..d78139365549a --- /dev/null +++ b/src/test/ui/macros/issue-34421-mac-expr-bad-stmt-good-add-semi.rs @@ -0,0 +1,15 @@ +macro_rules! make_item { + ($a:ident) => { + struct $a; + }; //~^ ERROR expected expression + //~| ERROR expected expression +} + +fn a() { + make_item!(A) +} +fn b() { + make_item!(B) +} + +fn main() {} diff --git a/src/test/ui/macros/issue-34421-mac-expr-bad-stmt-good-add-semi.stderr b/src/test/ui/macros/issue-34421-mac-expr-bad-stmt-good-add-semi.stderr new file mode 100644 index 0000000000000..c8d69640071c6 --- /dev/null +++ b/src/test/ui/macros/issue-34421-mac-expr-bad-stmt-good-add-semi.stderr @@ -0,0 +1,34 @@ +error: expected expression, found keyword `struct` + --> $DIR/issue-34421-mac-expr-bad-stmt-good-add-semi.rs:3:9 + | +LL | struct $a; + | ^^^^^^ expected expression +... +LL | make_item!(A) + | ------------- in this macro invocation + | + = note: the macro call doesn't expand to an expression, but it can expand to a statement + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `;` to interpret the expansion as a statement + | +LL | make_item!(A); + | ^ + +error: expected expression, found keyword `struct` + --> $DIR/issue-34421-mac-expr-bad-stmt-good-add-semi.rs:3:9 + | +LL | struct $a; + | ^^^^^^ expected expression +... +LL | make_item!(B) + | ------------- in this macro invocation + | + = note: the macro call doesn't expand to an expression, but it can expand to a statement + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `;` to interpret the expansion as a statement + | +LL | make_item!(B); + | ^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs index 85ebd66b66cb6..bc70f8ffec2b2 100644 --- a/src/test/ui/macros/issue-68060.rs +++ b/src/test/ui/macros/issue-68060.rs @@ -1,5 +1,3 @@ -// build-fail - #![feature(track_caller)] fn main() { diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr index 230867410d966..22187c4a4098a 100644 --- a/src/test/ui/macros/issue-68060.stderr +++ b/src/test/ui/macros/issue-68060.stderr @@ -1,24 +1,28 @@ -error: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/issue-68060.rs:8:13 +error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions + --> $DIR/issue-68060.rs:6:13 | LL | #[target_feature(enable = "")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | |_| (), | ------ not an `unsafe` function + | + = note: see issue #69098 for more information + = help: add `#![feature(target_feature_11)]` to the crate attributes to enable error: the feature named `` is not valid for this target - --> $DIR/issue-68060.rs:8:30 + --> $DIR/issue-68060.rs:6:30 | LL | #[target_feature(enable = "")] | ^^^^^^^^^^^ `` is not valid for this target error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/issue-68060.rs:11:13 + --> $DIR/issue-68060.rs:9:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0737`. +Some errors have detailed explanations: E0658, E0737. +For more information about an error, try `rustc --explain E0658`. diff --git a/src/test/ui/macros/issue-70446.rs b/src/test/ui/macros/issue-70446.rs new file mode 100644 index 0000000000000..407094d55ffef --- /dev/null +++ b/src/test/ui/macros/issue-70446.rs @@ -0,0 +1,13 @@ +// check-pass + +macro_rules! foo { + ($(: $p:path)? $(: $l:lifetime)? ) => { bar! {$(: $p)? $(: $l)? } }; +} + +macro_rules! bar { + ($(: $p:path)? $(: $l:lifetime)? ) => {}; +} + +foo! {: 'a } + +fn main() {} diff --git a/src/test/ui/macros/macro-deprecation.stderr b/src/test/ui/macros/macro-deprecation.stderr index 75915b9091006..0e8ecb58fe588 100644 --- a/src/test/ui/macros/macro-deprecation.stderr +++ b/src/test/ui/macros/macro-deprecation.stderr @@ -12,3 +12,5 @@ warning: use of deprecated item 'deprecated_macro': deprecation note LL | deprecated_macro!(); | ^^^^^^^^^^^^^^^^ +warning: 2 warnings emitted + diff --git a/src/test/ui/macros/macro-expanded-include/test.rs b/src/test/ui/macros/macro-expanded-include/test.rs index b8eb854b0b0f1..f1a71059a8901 100644 --- a/src/test/ui/macros/macro-expanded-include/test.rs +++ b/src/test/ui/macros/macro-expanded-include/test.rs @@ -1,4 +1,4 @@ -// ignore-emscripten no asm! support +// ignore-emscripten no llvm_asm! support // build-pass (FIXME(62277): could be check-pass?) #![feature(asm)] #![allow(unused)] diff --git a/src/test/ui/macros/macro-in-expression-context-2.stderr b/src/test/ui/macros/macro-in-expression-context-2.stderr index 672871c49ca5a..8f9660963937f 100644 --- a/src/test/ui/macros/macro-in-expression-context-2.stderr +++ b/src/test/ui/macros/macro-in-expression-context-2.stderr @@ -6,6 +6,12 @@ LL | macro_rules! empty { () => () } ... LL | _ => { empty!() } | ^^^^^^^^ expected expression + | + = note: the macro call doesn't expand to an expression, but it can expand to a statement +help: add `;` to interpret the expansion as a statement + | +LL | _ => { empty!(); } + | ^ error: aborting due to previous error diff --git a/src/test/ui/macros/macro-lifetime-used-with-labels.stderr b/src/test/ui/macros/macro-lifetime-used-with-labels.stderr index 162b337bbef13..98ee85d908d4e 100644 --- a/src/test/ui/macros/macro-lifetime-used-with-labels.stderr +++ b/src/test/ui/macros/macro-lifetime-used-with-labels.stderr @@ -11,3 +11,5 @@ LL | br2!('b); | = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +warning: 1 warning emitted + diff --git a/src/test/ui/macros/macro-name-typo.rs b/src/test/ui/macros/macro-name-typo.rs index b2892f3b6c239..1ddc419d302ac 100644 --- a/src/test/ui/macros/macro-name-typo.rs +++ b/src/test/ui/macros/macro-name-typo.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl fn main() { printlx!("oh noes!"); //~ ERROR cannot find } diff --git a/src/test/ui/macros/macro-name-typo.stderr b/src/test/ui/macros/macro-name-typo.stderr index 00afbde8932fc..5604341fa34dc 100644 --- a/src/test/ui/macros/macro-name-typo.stderr +++ b/src/test/ui/macros/macro-name-typo.stderr @@ -1,5 +1,5 @@ error: cannot find macro `printlx` in this scope - --> $DIR/macro-name-typo.rs:6:5 + --> $DIR/macro-name-typo.rs:2:5 | LL | printlx!("oh noes!"); | ^^^^^^^ help: a macro with a similar name exists: `println` diff --git a/src/test/ui/macros/macro-outer-attributes.stderr b/src/test/ui/macros/macro-outer-attributes.stderr index 86a6baca05324..8e064d980afab 100644 --- a/src/test/ui/macros/macro-outer-attributes.stderr +++ b/src/test/ui/macros/macro-outer-attributes.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find function `bar` in module `a` LL | a::bar(); | ^^^ not found in `a` | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this function | LL | use b::bar; | diff --git a/src/test/ui/macros/macro-path-prelude-fail-3.rs b/src/test/ui/macros/macro-path-prelude-fail-3.rs index 3c3948ca3c361..68eb350a95614 100644 --- a/src/test/ui/macros/macro-path-prelude-fail-3.rs +++ b/src/test/ui/macros/macro-path-prelude-fail-3.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl fn main() { inline!(); //~ ERROR cannot find macro `inline` in this scope } diff --git a/src/test/ui/macros/macro-path-prelude-fail-3.stderr b/src/test/ui/macros/macro-path-prelude-fail-3.stderr index 536459067437d..3e3a0b3879be9 100644 --- a/src/test/ui/macros/macro-path-prelude-fail-3.stderr +++ b/src/test/ui/macros/macro-path-prelude-fail-3.stderr @@ -1,5 +1,5 @@ error: cannot find macro `inline` in this scope - --> $DIR/macro-path-prelude-fail-3.rs:6:5 + --> $DIR/macro-path-prelude-fail-3.rs:2:5 | LL | inline!(); | ^^^^^^ help: a macro with a similar name exists: `line` diff --git a/src/test/ui/macros/macro-stability.stderr b/src/test/ui/macros/macro-stability.stderr index d357314d84c3e..9e127a3b8559b 100644 --- a/src/test/ui/macros/macro-stability.stderr +++ b/src/test/ui/macros/macro-stability.stderr @@ -36,6 +36,6 @@ warning: use of deprecated item 'local_deprecated': local deprecation reason LL | local_deprecated!(); | ^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/macros/macro-use-all-and-none.stderr b/src/test/ui/macros/macro-use-all-and-none.stderr index cbabf0672fa8d..bdee5f4d872a8 100644 --- a/src/test/ui/macros/macro-use-all-and-none.stderr +++ b/src/test/ui/macros/macro-use-all-and-none.stderr @@ -10,3 +10,5 @@ note: the lint level is defined here LL | #![warn(unused_attributes)] | ^^^^^^^^^^^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/macros/macros-nonfatal-errors.rs b/src/test/ui/macros/macros-nonfatal-errors.rs index 1eb82a20729ca..0a496c9dc3d33 100644 --- a/src/test/ui/macros/macros-nonfatal-errors.rs +++ b/src/test/ui/macros/macros-nonfatal-errors.rs @@ -3,7 +3,7 @@ // test that errors in a (selection) of macros don't kill compilation // immediately, so that we get more errors listed at a time. -#![feature(asm)] +#![feature(asm, llvm_asm)] #![feature(trace_macros, concat_idents)] #[derive(Default)] //~ ERROR @@ -11,6 +11,7 @@ enum OrDeriveThis {} fn main() { asm!(invalid); //~ ERROR + llvm_asm!(invalid); //~ ERROR concat_idents!("not", "idents"); //~ ERROR diff --git a/src/test/ui/macros/macros-nonfatal-errors.stderr b/src/test/ui/macros/macros-nonfatal-errors.stderr index 1ab6b79a61ecb..6ef757a55b8fb 100644 --- a/src/test/ui/macros/macros-nonfatal-errors.stderr +++ b/src/test/ui/macros/macros-nonfatal-errors.stderr @@ -6,44 +6,50 @@ LL | #[derive(Default)] | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: inline assembly must be a string literal +error: asm template must be a string literal --> $DIR/macros-nonfatal-errors.rs:13:10 | LL | asm!(invalid); | ^^^^^^^ +error: inline assembly must be a string literal + --> $DIR/macros-nonfatal-errors.rs:14:15 + | +LL | llvm_asm!(invalid); + | ^^^^^^^ + error: concat_idents! requires ident args. - --> $DIR/macros-nonfatal-errors.rs:15:5 + --> $DIR/macros-nonfatal-errors.rs:16:5 | LL | concat_idents!("not", "idents"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:17:17 + --> $DIR/macros-nonfatal-errors.rs:18:17 | LL | option_env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:18:10 + --> $DIR/macros-nonfatal-errors.rs:19:10 | LL | env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:19:10 + --> $DIR/macros-nonfatal-errors.rs:20:10 | LL | env!(foo, abr, baz); | ^^^ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined - --> $DIR/macros-nonfatal-errors.rs:20:5 + --> $DIR/macros-nonfatal-errors.rs:21:5 | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: format argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:22:13 + --> $DIR/macros-nonfatal-errors.rs:23:13 | LL | format!(invalid); | ^^^^^^^ @@ -54,19 +60,19 @@ LL | format!("{}", invalid); | ^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:24:14 + --> $DIR/macros-nonfatal-errors.rs:25:14 | LL | include!(invalid); | ^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:26:18 + --> $DIR/macros-nonfatal-errors.rs:27: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 (os error 2) - --> $DIR/macros-nonfatal-errors.rs:27:5 + --> $DIR/macros-nonfatal-errors.rs:28:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,13 +80,13 @@ LL | include_str!("i'd be quite surprised if a file with this name existed") = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:28:20 + --> $DIR/macros-nonfatal-errors.rs:29: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 (os error 2) - --> $DIR/macros-nonfatal-errors.rs:29:5 + --> $DIR/macros-nonfatal-errors.rs:30:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,11 +94,11 @@ LL | include_bytes!("i'd be quite surprised if a file with this name existed = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: trace_macros! accepts only `true` or `false` - --> $DIR/macros-nonfatal-errors.rs:31:5 + --> $DIR/macros-nonfatal-errors.rs:32:5 | LL | trace_macros!(invalid); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0665`. diff --git a/src/test/ui/macros/must-use-in-macro-55516.rs b/src/test/ui/macros/must-use-in-macro-55516.rs index 4b6b65ec48b26..e7c3462867b59 100644 --- a/src/test/ui/macros/must-use-in-macro-55516.rs +++ b/src/test/ui/macros/must-use-in-macro-55516.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // compile-flags: -Wunused // make sure write!() can't hide its unused Result diff --git a/src/test/ui/macros/must-use-in-macro-55516.stderr b/src/test/ui/macros/must-use-in-macro-55516.stderr index e3649e32d768d..a694c887085f0 100644 --- a/src/test/ui/macros/must-use-in-macro-55516.stderr +++ b/src/test/ui/macros/must-use-in-macro-55516.stderr @@ -8,3 +8,5 @@ LL | write!(&mut example, "{}", 42); = note: this `Result` may be an `Err` variant, which should be handled = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +warning: 1 warning emitted + diff --git a/src/test/ui/macros/unimplemented-macro-panic.rs b/src/test/ui/macros/unimplemented-macro-panic.rs new file mode 100644 index 0000000000000..e7169903f8ea5 --- /dev/null +++ b/src/test/ui/macros/unimplemented-macro-panic.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:not implemented +// ignore-emscripten no processes + +fn main() { + unimplemented!() +} diff --git a/src/test/ui/macros/unknown-builtin.rs b/src/test/ui/macros/unknown-builtin.rs index 716a0005ba3e4..a96b99ae4ff78 100644 --- a/src/test/ui/macros/unknown-builtin.rs +++ b/src/test/ui/macros/unknown-builtin.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - // error-pattern: cannot find a built-in macro with name `line` #![feature(rustc_attrs)] diff --git a/src/test/ui/macros/unknown-builtin.stderr b/src/test/ui/macros/unknown-builtin.stderr index ed163750a6ea4..665e92f242418 100644 --- a/src/test/ui/macros/unknown-builtin.stderr +++ b/src/test/ui/macros/unknown-builtin.stderr @@ -1,5 +1,5 @@ error: cannot find a built-in macro with name `unknown` - --> $DIR/unknown-builtin.rs:11:1 + --> $DIR/unknown-builtin.rs:6:1 | LL | macro_rules! unknown { () => () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/macros/unreachable-fmt-msg.rs b/src/test/ui/macros/unreachable-fmt-msg.rs new file mode 100644 index 0000000000000..eb17ed92711c9 --- /dev/null +++ b/src/test/ui/macros/unreachable-fmt-msg.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:internal error: entered unreachable code: 6 is not prime +// ignore-emscripten no processes + +fn main() { + unreachable!("{} is not {}", 6u32, "prime"); +} diff --git a/src/test/ui/macros/unreachable-macro-panic.rs b/src/test/ui/macros/unreachable-macro-panic.rs new file mode 100644 index 0000000000000..55e2102e2cc6f --- /dev/null +++ b/src/test/ui/macros/unreachable-macro-panic.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:internal error: entered unreachable code +// ignore-emscripten no processes + +fn main() { + unreachable!() +} diff --git a/src/test/ui/macros/unreachable-static-msg.rs b/src/test/ui/macros/unreachable-static-msg.rs new file mode 100644 index 0000000000000..55edf3af7d9e5 --- /dev/null +++ b/src/test/ui/macros/unreachable-static-msg.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:internal error: entered unreachable code: uhoh +// ignore-emscripten no processes + +fn main() { + unreachable!("uhoh") +} diff --git a/src/test/ui/macros/unreachable.rs b/src/test/ui/macros/unreachable.rs new file mode 100644 index 0000000000000..55e2102e2cc6f --- /dev/null +++ b/src/test/ui/macros/unreachable.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:internal error: entered unreachable code +// ignore-emscripten no processes + +fn main() { + unreachable!() +} diff --git a/src/test/ui/malformed/malformed-derive-entry.stderr b/src/test/ui/malformed/malformed-derive-entry.stderr index ddc75c905ac20..2c45a498240ef 100644 --- a/src/test/ui/malformed/malformed-derive-entry.stderr +++ b/src/test/ui/malformed/malformed-derive-entry.stderr @@ -21,6 +21,11 @@ error[E0277]: the trait bound `Test1: std::clone::Clone` is not satisfied | LL | #[derive(Copy(Bad))] | ^^^^ the trait `std::clone::Clone` is not implemented for `Test1` + | + ::: $SRC_DIR/libcore/marker.rs:LL:COL + | +LL | pub trait Copy: Clone { + | ----- required by this bound in `std::marker::Copy` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -29,6 +34,11 @@ error[E0277]: the trait bound `Test2: std::clone::Clone` is not satisfied | LL | #[derive(Copy="bad")] | ^^^^ the trait `std::clone::Clone` is not implemented for `Test2` + | + ::: $SRC_DIR/libcore/marker.rs:LL:COL + | +LL | pub trait Copy: Clone { + | ----- required by this bound in `std::marker::Copy` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/malformed/malformed-plugin-1.stderr b/src/test/ui/malformed/malformed-plugin-1.stderr index 2a4f772850e92..98744434d4f8c 100644 --- a/src/test/ui/malformed/malformed-plugin-1.stderr +++ b/src/test/ui/malformed/malformed-plugin-1.stderr @@ -12,5 +12,5 @@ LL | #![plugin] | = note: `#[warn(deprecated)]` on by default -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/malformed/malformed-plugin-2.stderr b/src/test/ui/malformed/malformed-plugin-2.stderr index fe116a4061025..9bf0bf9345c6c 100644 --- a/src/test/ui/malformed/malformed-plugin-2.stderr +++ b/src/test/ui/malformed/malformed-plugin-2.stderr @@ -12,5 +12,5 @@ LL | #![plugin="bleh"] | = note: `#[warn(deprecated)]` on by default -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/malformed/malformed-plugin-3.stderr b/src/test/ui/malformed/malformed-plugin-3.stderr index 4af933c15f61e..11abdb16e0b4e 100644 --- a/src/test/ui/malformed/malformed-plugin-3.stderr +++ b/src/test/ui/malformed/malformed-plugin-3.stderr @@ -12,5 +12,5 @@ LL | #![plugin(foo="bleh")] | = note: `#[warn(deprecated)]` on by default -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/malformed/malformed-regressions.stderr b/src/test/ui/malformed/malformed-regressions.stderr index b14f99be50c2d..13c12ff721360 100644 --- a/src/test/ui/malformed/malformed-regressions.stderr +++ b/src/test/ui/malformed/malformed-regressions.stderr @@ -26,7 +26,7 @@ LL | #[inline = ""] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 -error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ cfg = "...")]` +error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]` --> $DIR/malformed-regressions.rs:7:1 | LL | #[link] @@ -35,7 +35,7 @@ LL | #[link] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 -error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ cfg = "...")]` +error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...")]` --> $DIR/malformed-regressions.rs:9:1 | LL | #[link = ""] diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr b/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr index 4508870746bcb..0fc266454ee80 100644 --- a/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr +++ b/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `NotDebugOrDisplay: Marker` is not satisfied --> $DIR/overlap-marker-trait.rs:27:17 | LL | fn is_marker() { } - | --------- ------ required by this bound in `is_marker` + | ------ required by this bound in `is_marker` ... LL | is_marker::(); | ^^^^^^^^^^^^^^^^^ the trait `Marker` is not implemented for `NotDebugOrDisplay` diff --git a/src/test/run-fail/expr-match-panic-fn.rs b/src/test/ui/match/expr-match-panic-fn.rs similarity index 80% rename from src/test/run-fail/expr-match-panic-fn.rs rename to src/test/ui/match/expr-match-panic-fn.rs index 120df61b4bfe6..ea471717e883a 100644 --- a/src/test/run-fail/expr-match-panic-fn.rs +++ b/src/test/ui/match/expr-match-panic-fn.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:explicit panic +// ignore-emscripten no processes fn f() -> ! { panic!() diff --git a/src/test/ui/match/expr-match-panic.rs b/src/test/ui/match/expr-match-panic.rs new file mode 100644 index 0000000000000..53f8a8bd30ddb --- /dev/null +++ b/src/test/ui/match/expr-match-panic.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +fn main() { + let _x = match true { + false => 0, + true => panic!(), + }; +} diff --git a/src/test/ui/match/issue-50900.stderr b/src/test/ui/match/issue-50900.stderr index 7192f11a5e8f0..d378b6e8efe37 100644 --- a/src/test/ui/match/issue-50900.stderr +++ b/src/test/ui/match/issue-50900.stderr @@ -8,6 +8,7 @@ LL | match Tag::ExifIFDPointer { | ^^^^^^^^^^^^^^^^^^^ pattern `Tag(Exif, _)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Tag` error: aborting due to previous error diff --git a/src/test/ui/match/issue-70972-dyn-trait.rs b/src/test/ui/match/issue-70972-dyn-trait.rs new file mode 100644 index 0000000000000..a9b2699cafdc4 --- /dev/null +++ b/src/test/ui/match/issue-70972-dyn-trait.rs @@ -0,0 +1,10 @@ +const F: &'static dyn Send = &7u32; + +fn main() { + let a: &dyn Send = &7u32; + match a { + F => panic!(), + //~^ ERROR trait objects cannot be used in patterns + _ => {} + } +} diff --git a/src/test/ui/match/issue-70972-dyn-trait.stderr b/src/test/ui/match/issue-70972-dyn-trait.stderr new file mode 100644 index 0000000000000..a4e827357de6b --- /dev/null +++ b/src/test/ui/match/issue-70972-dyn-trait.stderr @@ -0,0 +1,8 @@ +error: trait objects cannot be used in patterns + --> $DIR/issue-70972-dyn-trait.rs:6:9 + | +LL | F => panic!(), + | ^ + +error: aborting due to previous error + diff --git a/src/test/run-fail/match-bot-panic.rs b/src/test/ui/match/match-bot-panic.rs similarity index 84% rename from src/test/run-fail/match-bot-panic.rs rename to src/test/ui/match/match-bot-panic.rs index f4da8c4e43fb6..e4a6f6d6fe44a 100644 --- a/src/test/run-fail/match-bot-panic.rs +++ b/src/test/ui/match/match-bot-panic.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:explicit panic +// ignore-emscripten no processes #![allow(unreachable_code)] #![allow(unused_variables)] diff --git a/src/test/run-fail/match-disc-bot.rs b/src/test/ui/match/match-disc-bot.rs similarity index 77% rename from src/test/run-fail/match-disc-bot.rs rename to src/test/ui/match/match-disc-bot.rs index a9312fbb1fb35..18cfd5e23950b 100644 --- a/src/test/run-fail/match-disc-bot.rs +++ b/src/test/ui/match/match-disc-bot.rs @@ -1,4 +1,7 @@ +// run-fail // error-pattern:quux +// ignore-emscripten no processes + fn f() -> ! { panic!("quux") } diff --git a/src/test/run-fail/match-wildcards.rs b/src/test/ui/match/match-wildcards.rs similarity index 87% rename from src/test/run-fail/match-wildcards.rs rename to src/test/ui/match/match-wildcards.rs index 7a65ad5255896..43f6e4913ac76 100644 --- a/src/test/run-fail/match-wildcards.rs +++ b/src/test/ui/match/match-wildcards.rs @@ -1,4 +1,7 @@ +// run-fail // error-pattern:squirrelcupcake +// ignore-emscripten no processes + fn cmp() -> isize { match (Some('a'), None::) { (Some(_), _) => { diff --git a/src/test/ui/maybe-bounds-where.stderr b/src/test/ui/maybe-bounds-where.stderr index 19f9cd28a9a01..0ef8e9e9c795a 100644 --- a/src/test/ui/maybe-bounds-where.stderr +++ b/src/test/ui/maybe-bounds-where.stderr @@ -40,6 +40,6 @@ warning: default bound relaxed for a type parameter, but this does nothing becau LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^ -error: aborting due to 6 previous errors +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0203`. diff --git a/src/test/run-fail/meta-revision-bad.rs b/src/test/ui/meta-revision-bad.rs similarity index 96% rename from src/test/run-fail/meta-revision-bad.rs rename to src/test/ui/meta-revision-bad.rs index 17f6398b73554..01f1518c1c6ed 100644 --- a/src/test/run-fail/meta-revision-bad.rs +++ b/src/test/ui/meta-revision-bad.rs @@ -1,6 +1,7 @@ // Meta test for compiletest: check that when we give the wrong error // patterns, the test fails. +// run-fail // revisions: foo bar // should-fail //[foo] error-pattern:bar diff --git a/src/test/run-fail/meta-revision-ok.rs b/src/test/ui/meta-revision-ok.rs similarity index 87% rename from src/test/run-fail/meta-revision-ok.rs rename to src/test/ui/meta-revision-ok.rs index 8693ee5f4ef96..7df9a6ea48fae 100644 --- a/src/test/run-fail/meta-revision-ok.rs +++ b/src/test/ui/meta-revision-ok.rs @@ -1,9 +1,11 @@ // Meta test for compiletest: check that when we give the right error // patterns, the test passes. See all `meta-revision-bad.rs`. +// run-fail // revisions: foo bar //[foo] error-pattern:foo //[bar] error-pattern:bar +// ignore-emscripten no processes #[cfg(foo)] fn die() { diff --git a/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr b/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr index fa3add81a28f5..1b354fc697adc 100644 --- a/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr @@ -10,11 +10,11 @@ note: candidate #1 is defined in an impl of the trait `Me2` for the type `usize` LL | impl Me2 for usize { fn me(&self) -> usize { *self } } | ^^^^^^^^^^^^^^^^^^^^^ = note: candidate #2 is defined in an impl of the trait `ambig_impl_2_lib::Me` for the type `usize` -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | fn main() { Me2::me(&1_usize); } | ^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | fn main() { ambig_impl_2_lib::Me::me(&1_usize); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr b/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr index b6c81c2377ee4..5cbed652b0a40 100644 --- a/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-from-bounds.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in the trait `B` | LL | trait B { fn foo(&self); } | ^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | A::foo(t); | ^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | B::foo(t); | ^^^^^^^^^ diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr b/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr index 71c65f7ccc68d..8585929934e31 100644 --- a/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in an impl of the trait `B` for the type `AB` | LL | fn foo(self) {} | ^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | A::foo(AB {}); | ^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | B::foo(AB {}); | ^^^^^^^^^^^^^ diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr index 249464108875c..85b3964788590 100644 --- a/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in an impl of the trait `B` for the type `AB` | LL | fn foo() {} | ^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | A::foo(); | ^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | B::foo(); | ^^^^^^ diff --git a/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr b/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr index 3dbb17371004a..4ce7236ed96c5 100644 --- a/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-with-default-method.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in an impl of the trait `Bar` for the type `usize` | LL | trait Bar { fn method(&self) {} } | ^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | Foo::method(&1_usize); | ^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | Bar::method(&1_usize); | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index e7f295df8c482..1bc7f30d04d0d 100644 --- a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -20,12 +20,12 @@ error[E0034]: multiple applicable items in scope LL | let z = x.foo(); | ^^^ multiple `foo` found | -note: candidate #1 is defined in an impl of the trait `internal::X` for the type `_` +note: candidate #1 is defined in an impl of the trait `internal::X` for the type `T` --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:43:9 | LL | fn foo(self: Smaht) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `nuisance_foo::NuisanceFoo` for the type `_` +note: candidate #2 is defined in an impl of the trait `nuisance_foo::NuisanceFoo` for the type `T` --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:70:9 | LL | fn foo(self) {} @@ -35,15 +35,15 @@ note: candidate #3 is defined in the trait `FinalFoo` | LL | fn foo(&self) -> u8; | ^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | let z = internal::X::foo(x); | ^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | let z = nuisance_foo::NuisanceFoo::foo(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #3 +help: disambiguate the associated function for candidate #3 | LL | let z = FinalFoo::foo(x); | ^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/methods/method-path-in-pattern.stderr b/src/test/ui/methods/method-path-in-pattern.stderr index 1d1bdb6b052a8..ed3c0222c7542 100644 --- a/src/test/ui/methods/method-path-in-pattern.stderr +++ b/src/test/ui/methods/method-path-in-pattern.stderr @@ -4,13 +4,13 @@ error[E0533]: expected unit struct, unit variant or constant, found associated f LL | Foo::bar => {} | ^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `::bar` --> $DIR/method-path-in-pattern.rs:19:9 | LL | ::bar => {} | ^^^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::trait_bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `::trait_bar` --> $DIR/method-path-in-pattern.rs:23:9 | LL | ::trait_bar => {} @@ -22,7 +22,7 @@ error[E0533]: expected unit struct, unit variant or constant, found associated f LL | if let Foo::bar = 0u32 {} | ^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `::bar` --> $DIR/method-path-in-pattern.rs:28:12 | LL | if let ::bar = 0u32 {} diff --git a/src/test/ui/mir-dataflow/liveness-ptr.rs b/src/test/ui/mir-dataflow/liveness-ptr.rs new file mode 100644 index 0000000000000..34097d7526a6e --- /dev/null +++ b/src/test/ui/mir-dataflow/liveness-ptr.rs @@ -0,0 +1,28 @@ +#![feature(core_intrinsics, rustc_attrs)] + +use std::intrinsics::rustc_peek; + +#[rustc_mir(rustc_peek_liveness, stop_after_dataflow)] +fn foo() -> i32 { + let mut x: i32; + let mut p: *const i32; + + x = 0; + + // `x` is live here since it is used in the next statement... + unsafe { rustc_peek(x); } + + p = &x; + + // ... but not here, even while it can be accessed through `p`. + unsafe { rustc_peek(x); } //~ ERROR rustc_peek: bit not set + let tmp = unsafe { *p }; + + x = tmp + 1; + + unsafe { rustc_peek(x); } + + x +} + +fn main() {} diff --git a/src/test/ui/mir-dataflow/liveness-ptr.stderr b/src/test/ui/mir-dataflow/liveness-ptr.stderr new file mode 100644 index 0000000000000..3397d0c5a121d --- /dev/null +++ b/src/test/ui/mir-dataflow/liveness-ptr.stderr @@ -0,0 +1,10 @@ +error: rustc_peek: bit not set + --> $DIR/liveness-ptr.rs:18:14 + | +LL | unsafe { rustc_peek(x); } + | ^^^^^^^^^^^^^ + +error: stop_after_dataflow ended compilation + +error: aborting due to 2 previous errors + diff --git a/src/test/run-fail/mir_codegen_calls_converging_drops.rs b/src/test/ui/mir/mir_codegen_calls_converging_drops.rs similarity index 89% rename from src/test/run-fail/mir_codegen_calls_converging_drops.rs rename to src/test/ui/mir/mir_codegen_calls_converging_drops.rs index ee0dc98ce6819..b562f93081419 100644 --- a/src/test/run-fail/mir_codegen_calls_converging_drops.rs +++ b/src/test/ui/mir/mir_codegen_calls_converging_drops.rs @@ -1,6 +1,8 @@ +// run-fail // error-pattern:converging_fn called // error-pattern:0 dropped // error-pattern:exit +// ignore-emscripten no processes struct Droppable(u8); impl Drop for Droppable { diff --git a/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs b/src/test/ui/mir/mir_codegen_calls_converging_drops_2.rs similarity index 90% rename from src/test/run-fail/mir_codegen_calls_converging_drops_2.rs rename to src/test/ui/mir/mir_codegen_calls_converging_drops_2.rs index ee2c25ce8565e..e9446da9e3911 100644 --- a/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs +++ b/src/test/ui/mir/mir_codegen_calls_converging_drops_2.rs @@ -1,6 +1,8 @@ +// run-fail // error-pattern:complex called // error-pattern:dropped // error-pattern:exit +// ignore-emscripten no processes struct Droppable; impl Drop for Droppable { diff --git a/src/test/run-fail/mir_codegen_calls_diverging.rs b/src/test/ui/mir/mir_codegen_calls_diverging.rs similarity index 77% rename from src/test/run-fail/mir_codegen_calls_diverging.rs rename to src/test/ui/mir/mir_codegen_calls_diverging.rs index dceae0a4e4a03..736d580e2da18 100644 --- a/src/test/run-fail/mir_codegen_calls_diverging.rs +++ b/src/test/ui/mir/mir_codegen_calls_diverging.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:diverging_fn called +// ignore-emscripten no processes fn diverging_fn() -> ! { panic!("diverging_fn called") diff --git a/src/test/run-fail/mir_codegen_calls_diverging_drops.rs b/src/test/ui/mir/mir_codegen_calls_diverging_drops.rs similarity index 88% rename from src/test/run-fail/mir_codegen_calls_diverging_drops.rs rename to src/test/ui/mir/mir_codegen_calls_diverging_drops.rs index 187e526f7c092..796d744779391 100644 --- a/src/test/run-fail/mir_codegen_calls_diverging_drops.rs +++ b/src/test/ui/mir/mir_codegen_calls_diverging_drops.rs @@ -1,5 +1,7 @@ +// run-fail // error-pattern:diverging_fn called // error-pattern:0 dropped +// ignore-emscripten no processes struct Droppable(u8); impl Drop for Droppable { diff --git a/src/test/ui/mir/mir_detects_invalid_ops.rs b/src/test/ui/mir/mir_detects_invalid_ops.rs new file mode 100644 index 0000000000000..0940dbe6a5e87 --- /dev/null +++ b/src/test/ui/mir/mir_detects_invalid_ops.rs @@ -0,0 +1,24 @@ +// build-fail + +fn main() { + divide_by_zero(); + mod_by_zero(); + oob_error_for_slices(); +} + +fn divide_by_zero() { + let y = 0; + let _z = 1 / y; //~ ERROR this operation will panic at runtime [unconditional_panic] +} + +fn mod_by_zero() { + let y = 0; + let _z = 1 % y; //~ ERROR this operation will panic at runtime [unconditional_panic] +} + +fn oob_error_for_slices() { + let a: *const [_] = &[1, 2, 3]; + unsafe { + let _b = (*a)[3]; //~ ERROR this operation will panic at runtime [unconditional_panic] + } +} diff --git a/src/test/ui/mir/mir_detects_invalid_ops.stderr b/src/test/ui/mir/mir_detects_invalid_ops.stderr new file mode 100644 index 0000000000000..41f03789f237f --- /dev/null +++ b/src/test/ui/mir/mir_detects_invalid_ops.stderr @@ -0,0 +1,22 @@ +error: this operation will panic at runtime + --> $DIR/mir_detects_invalid_ops.rs:11:14 + | +LL | let _z = 1 / y; + | ^^^^^ attempt to divide by zero + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/mir_detects_invalid_ops.rs:16:14 + | +LL | let _z = 1 % y; + | ^^^^^ attempt to calculate the remainder with a divisor of zero + +error: this operation will panic at runtime + --> $DIR/mir_detects_invalid_ops.rs:22:18 + | +LL | let _b = (*a)[3]; + | ^^^^^^^ index out of bounds: the len is 3 but the index is 3 + +error: aborting due to 3 previous errors + diff --git a/src/test/run-fail/mir_drop_panics.rs b/src/test/ui/mir/mir_drop_panics.rs similarity index 88% rename from src/test/run-fail/mir_drop_panics.rs rename to src/test/ui/mir/mir_drop_panics.rs index bda555b926288..bf269ee901b93 100644 --- a/src/test/run-fail/mir_drop_panics.rs +++ b/src/test/ui/mir/mir_drop_panics.rs @@ -1,5 +1,7 @@ +// run-fail // error-pattern:panic 1 // error-pattern:drop 2 +// ignore-emscripten no processes struct Droppable(u32); impl Drop for Droppable { diff --git a/src/test/run-fail/mir_dynamic_drops_1.rs b/src/test/ui/mir/mir_dynamic_drops_1.rs similarity index 93% rename from src/test/run-fail/mir_dynamic_drops_1.rs rename to src/test/ui/mir/mir_dynamic_drops_1.rs index db8d0af29db2c..a77b2368d3baa 100644 --- a/src/test/run-fail/mir_dynamic_drops_1.rs +++ b/src/test/ui/mir/mir_dynamic_drops_1.rs @@ -1,6 +1,8 @@ +// run-fail // error-pattern:drop 1 // error-pattern:drop 2 // ignore-cloudabi no std::process +// ignore-emscripten no processes /// Structure which will not allow to be dropped twice. struct Droppable<'a>(&'a mut bool, u32); diff --git a/src/test/run-fail/mir_dynamic_drops_2.rs b/src/test/ui/mir/mir_dynamic_drops_2.rs similarity index 92% rename from src/test/run-fail/mir_dynamic_drops_2.rs rename to src/test/ui/mir/mir_dynamic_drops_2.rs index 21d3a042b1e31..088a16d338787 100644 --- a/src/test/run-fail/mir_dynamic_drops_2.rs +++ b/src/test/ui/mir/mir_dynamic_drops_2.rs @@ -1,5 +1,7 @@ +// run-fail // error-pattern:drop 1 // ignore-cloudabi no std::process +// ignore-emscripten no processes /// Structure which will not allow to be dropped twice. struct Droppable<'a>(&'a mut bool, u32); diff --git a/src/test/run-fail/mir_dynamic_drops_3.rs b/src/test/ui/mir/mir_dynamic_drops_3.rs similarity index 94% rename from src/test/run-fail/mir_dynamic_drops_3.rs rename to src/test/ui/mir/mir_dynamic_drops_3.rs index b90499686823a..029bdcd9a1590 100644 --- a/src/test/run-fail/mir_dynamic_drops_3.rs +++ b/src/test/ui/mir/mir_dynamic_drops_3.rs @@ -1,8 +1,10 @@ +// run-fail // error-pattern:unwind happens // error-pattern:drop 3 // error-pattern:drop 2 // error-pattern:drop 1 // ignore-cloudabi no std::process +// ignore-emscripten no processes /// Structure which will not allow to be dropped twice. struct Droppable<'a>(&'a mut bool, u32); diff --git a/src/test/run-fail/mir_indexing_oob_1.rs b/src/test/ui/mir/mir_indexing_oob_1.rs similarity index 80% rename from src/test/run-fail/mir_indexing_oob_1.rs rename to src/test/ui/mir/mir_indexing_oob_1.rs index 1cd53e309ebf9..6d769b6b23a84 100644 --- a/src/test/run-fail/mir_indexing_oob_1.rs +++ b/src/test/ui/mir/mir_indexing_oob_1.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:index out of bounds: the len is 5 but the index is 10 +// ignore-emscripten no processes const C: [u32; 5] = [0; 5]; diff --git a/src/test/run-fail/mir_indexing_oob_2.rs b/src/test/ui/mir/mir_indexing_oob_2.rs similarity index 81% rename from src/test/run-fail/mir_indexing_oob_2.rs rename to src/test/ui/mir/mir_indexing_oob_2.rs index 64b260993c994..a9e8505701536 100644 --- a/src/test/run-fail/mir_indexing_oob_2.rs +++ b/src/test/ui/mir/mir_indexing_oob_2.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:index out of bounds: the len is 5 but the index is 10 +// ignore-emscripten no processes const C: &'static [u8; 5] = b"hello"; diff --git a/src/test/run-fail/mir_indexing_oob_3.rs b/src/test/ui/mir/mir_indexing_oob_3.rs similarity index 80% rename from src/test/run-fail/mir_indexing_oob_3.rs rename to src/test/ui/mir/mir_indexing_oob_3.rs index 3688088439bbf..4f5cab59bfc67 100644 --- a/src/test/run-fail/mir_indexing_oob_3.rs +++ b/src/test/ui/mir/mir_indexing_oob_3.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:index out of bounds: the len is 5 but the index is 10 +// ignore-emscripten no processes const C: &'static [u8; 5] = b"hello"; diff --git a/src/test/ui/mismatched_types/E0631.stderr b/src/test/ui/mismatched_types/E0631.stderr index 06f5c058f81f5..1b10325564a64 100644 --- a/src/test/ui/mismatched_types/E0631.stderr +++ b/src/test/ui/mismatched_types/E0631.stderr @@ -2,7 +2,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/E0631.rs:7:5 | LL | fn foo(_: F) {} - | --- --------- required by this bound in `foo` + | --------- required by this bound in `foo` ... LL | foo(|_: isize| {}); | ^^^ ---------- found signature of `fn(isize) -> _` @@ -13,7 +13,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/E0631.rs:8:5 | LL | fn bar>(_: F) {} - | --- --------- required by this bound in `bar` + | --------- required by this bound in `bar` ... LL | bar(|_: isize| {}); | ^^^ ---------- found signature of `fn(isize) -> _` @@ -24,7 +24,7 @@ error[E0631]: type mismatch in function arguments --> $DIR/E0631.rs:9:9 | LL | fn foo(_: F) {} - | --- --------- required by this bound in `foo` + | --------- required by this bound in `foo` ... LL | fn f(_: u64) {} | ------------ found signature of `fn(u64) -> _` @@ -36,7 +36,7 @@ error[E0631]: type mismatch in function arguments --> $DIR/E0631.rs:10:9 | LL | fn bar>(_: F) {} - | --- --------- required by this bound in `bar` + | --------- required by this bound in `bar` LL | fn main() { LL | fn f(_: u64) {} | ------------ found signature of `fn(u64) -> _` diff --git a/src/test/ui/mismatched_types/closure-arg-count.stderr b/src/test/ui/mismatched_types/closure-arg-count.stderr index 13954343246fa..405343783de05 100644 --- a/src/test/ui/mismatched_types/closure-arg-count.stderr +++ b/src/test/ui/mismatched_types/closure-arg-count.stderr @@ -49,7 +49,7 @@ error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments --> $DIR/closure-arg-count.rs:13:5 | LL | fn f>(_: F) {} - | - --------- required by this bound in `f` + | --------- required by this bound in `f` ... LL | f(|| panic!()); | ^ -- takes 0 arguments @@ -65,7 +65,7 @@ error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments --> $DIR/closure-arg-count.rs:15:5 | LL | fn f>(_: F) {} - | - --------- required by this bound in `f` + | --------- required by this bound in `f` ... LL | f( move || panic!()); | ^ ---------- takes 0 arguments @@ -150,7 +150,7 @@ LL | call(Foo); | ^^^ expected function that takes 0 arguments ... LL | fn call(_: F) where F: FnOnce() -> R {} - | ---- ------------- required by this bound in `call` + | ------------- required by this bound in `call` LL | struct Foo(u8); | --------------- takes 1 argument diff --git a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr index ed5028247124f..69a4b458ebf50 100644 --- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr @@ -26,7 +26,7 @@ error[E0631]: type mismatch in function arguments --> $DIR/closure-arg-type-mismatch.rs:10:9 | LL | fn baz(_: F) {} - | --- ------------- required by this bound in `baz` + | ------------- required by this bound in `baz` LL | fn _test<'a>(f: fn(*mut &'a u32)) { LL | baz(f); | ^ @@ -38,7 +38,7 @@ error[E0271]: type mismatch resolving `for<'r> $DIR/closure-arg-type-mismatch.rs:10:5 | LL | fn baz(_: F) {} - | --- ------------- required by this bound in `baz` + | ------------- required by this bound in `baz` LL | fn _test<'a>(f: fn(*mut &'a u32)) { LL | baz(f); | ^^^ expected bound lifetime parameter, found concrete lifetime diff --git a/src/test/ui/mismatched_types/closure-mismatch.stderr b/src/test/ui/mismatched_types/closure-mismatch.stderr index f3874c0907be0..389b21574465a 100644 --- a/src/test/ui/mismatched_types/closure-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-mismatch.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/closure-mismatch.r --> $DIR/closure-mismatch.rs:8:5 | LL | fn baz(_: T) {} - | --- --- required by this bound in `baz` + | --- required by this bound in `baz` ... LL | baz(|_| ()); | ^^^ expected bound lifetime parameter, found concrete lifetime @@ -13,7 +13,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/closure-mismatch.rs:8:5 | LL | fn baz(_: T) {} - | --- --- required by this bound in `baz` + | --- required by this bound in `baz` ... LL | baz(|_| ()); | ^^^ ------ found signature of `fn(_) -> _` diff --git a/src/test/ui/mismatched_types/fn-variance-1.stderr b/src/test/ui/mismatched_types/fn-variance-1.stderr index 88c92661994cb..dbb281bbf415e 100644 --- a/src/test/ui/mismatched_types/fn-variance-1.stderr +++ b/src/test/ui/mismatched_types/fn-variance-1.stderr @@ -5,7 +5,7 @@ LL | fn takes_mut(x: &mut isize) { } | --------------------------- found signature of `for<'r> fn(&'r mut isize) -> _` LL | LL | fn apply(t: T, f: F) where F: FnOnce(T) { - | ----- --------- required by this bound in `apply` + | --------- required by this bound in `apply` ... LL | apply(&3, takes_mut); | ^^^^^^^^^ expected signature of `fn(&{integer}) -> _` @@ -17,7 +17,7 @@ LL | fn takes_imm(x: &isize) { } | ----------------------- found signature of `for<'r> fn(&'r isize) -> _` ... LL | fn apply(t: T, f: F) where F: FnOnce(T) { - | ----- --------- required by this bound in `apply` + | --------- required by this bound in `apply` ... LL | apply(&mut 3, takes_imm); | ^^^^^^^^^ expected signature of `fn(&mut {integer}) -> _` diff --git a/src/test/ui/mismatched_types/issue-35030.stderr b/src/test/ui/mismatched_types/issue-35030.stderr index 6fb04ef5c998f..9f4e4398984ae 100644 --- a/src/test/ui/mismatched_types/issue-35030.stderr +++ b/src/test/ui/mismatched_types/issue-35030.stderr @@ -9,8 +9,6 @@ LL | Some(true) | = note: expected type parameter `bool` (type parameter `bool`) found type `bool` (`bool`) - = 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 previous error diff --git a/src/test/ui/mismatched_types/issue-36053-2.rs b/src/test/ui/mismatched_types/issue-36053-2.rs index 36211b4ce701d..9035e3380b0c5 100644 --- a/src/test/ui/mismatched_types/issue-36053-2.rs +++ b/src/test/ui/mismatched_types/issue-36053-2.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // Regression test for #36053. ICE was caused due to obligations // being added to a special, dedicated fulfillment cx during // a probe. diff --git a/src/test/ui/mismatched_types/issue-36053-2.stderr b/src/test/ui/mismatched_types/issue-36053-2.stderr index f8c0470172d19..2793acf885757 100644 --- a/src/test/ui/mismatched_types/issue-36053-2.stderr +++ b/src/test/ui/mismatched_types/issue-36053-2.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `count` found for struct `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>` in the current scope - --> $DIR/issue-36053-2.rs:11:55 +error[E0599]: no method named `count` found for struct `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` in the current scope + --> $DIR/issue-36053-2.rs:7:55 | LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); - | -------------- ^^^^^ method not found in `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>` + | -------------- ^^^^^ method not found in `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` | | | doesn't satisfy `<_ as std::ops::FnOnce<(&&str,)>>::Output = bool` | doesn't satisfy `_: std::ops::FnMut<(&&str,)>` @@ -13,15 +13,15 @@ LL | pub struct Filter { | ----------------------- doesn't satisfy `_: std::iter::Iterator` | = note: the method `count` exists but the following trait bounds were not satisfied: - `<[closure@$DIR/issue-36053-2.rs:11:39: 11:53] as std::ops::FnOnce<(&&str,)>>::Output = bool` - which is required by `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>: std::iter::Iterator` - `[closure@$DIR/issue-36053-2.rs:11:39: 11:53]: std::ops::FnMut<(&&str,)>` - which is required by `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>: std::iter::Iterator` - `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>: std::iter::Iterator` - which is required by `&mut std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>: std::iter::Iterator` + `<[closure@$DIR/issue-36053-2.rs:7:39: 7:53] as std::ops::FnOnce<(&&str,)>>::Output = bool` + which is required by `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` + `[closure@$DIR/issue-36053-2.rs:7:39: 7:53]: std::ops::FnMut<(&&str,)>` + which is required by `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` + `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` + which is required by `&mut std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` error[E0631]: type mismatch in closure arguments - --> $DIR/issue-36053-2.rs:11:32 + --> $DIR/issue-36053-2.rs:7:32 | LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); | ^^^^^^ -------------- found signature of `for<'r> fn(&'r str) -> _` diff --git a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs index 2bd4d3384469f..ab36b8536bf60 100644 --- a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs +++ b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs @@ -6,7 +6,6 @@ fn to_fn_mut>(f: F) -> F { f } fn call_itisize>(y: isize, mut f: F) -> isize { //~^ NOTE required by this bound in `call_it` -//~| NOTE f(2, y) } diff --git a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr index 3c999f200d9c7..111ff4a0c3251 100644 --- a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr +++ b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr @@ -1,8 +1,8 @@ error[E0631]: type mismatch in closure arguments - --> $DIR/unboxed-closures-vtable-mismatch.rs:16:24 + --> $DIR/unboxed-closures-vtable-mismatch.rs:15:24 | LL | fn call_itisize>(y: isize, mut f: F) -> isize { - | ------- ------------------------- required by this bound in `call_it` + | ------------------------- required by this bound in `call_it` ... LL | let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y }); | ----------------------------- found signature of `fn(usize, isize) -> _` diff --git a/src/test/ui/missing/missing-items/auxiliary/m1.rs b/src/test/ui/missing/missing-items/auxiliary/m1.rs index 7705066760c50..fcf52c9e88743 100644 --- a/src/test/ui/missing/missing-items/auxiliary/m1.rs +++ b/src/test/ui/missing/missing-items/auxiliary/m1.rs @@ -2,4 +2,8 @@ pub trait X { const CONSTANT: u32; type Type; fn method(&self, s: String) -> Self::Type; + fn method2(self: Box, s: String) -> Self::Type; + fn method3(other: &Self, s: String) -> Self::Type; + fn method4(&self, other: &Self) -> Self::Type; + fn method5(self: &Box) -> Self::Type; } diff --git a/src/test/ui/missing/missing-items/issue-40221.stderr b/src/test/ui/missing/missing-items/issue-40221.stderr index 8e5286f210013..98efe805a0b34 100644 --- a/src/test/ui/missing/missing-items/issue-40221.stderr +++ b/src/test/ui/missing/missing-items/issue-40221.stderr @@ -11,6 +11,7 @@ LL | match proto { | ^^^^^ pattern `C(QA)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `P` error: aborting due to previous error diff --git a/src/test/ui/missing/missing-items/m2.stderr b/src/test/ui/missing/missing-items/m2.stderr index 094782099f6ed..64e9530e61348 100644 --- a/src/test/ui/missing/missing-items/m2.stderr +++ b/src/test/ui/missing/missing-items/m2.stderr @@ -1,12 +1,16 @@ -error[E0046]: not all trait items implemented, missing: `CONSTANT`, `Type`, `method` +error[E0046]: not all trait items implemented, missing: `CONSTANT`, `Type`, `method`, `method2`, `method3`, `method4`, `method5` --> $DIR/m2.rs:9:1 | LL | impl m1::X for X { - | ^^^^^^^^^^^^^^^^ missing `CONSTANT`, `Type`, `method` in implementation + | ^^^^^^^^^^^^^^^^ missing `CONSTANT`, `Type`, `method`, `method2`, `method3`, `method4`, `method5` in implementation | = help: implement the missing item: `const CONSTANT: u32 = 42;` = help: implement the missing item: `type Type = Type;` = help: implement the missing item: `fn method(&self, _: std::string::String) -> ::Type { todo!() }` + = help: implement the missing item: `fn method2(self: std::boxed::Box, _: std::string::String) -> ::Type { todo!() }` + = help: implement the missing item: `fn method3(_: &Self, _: std::string::String) -> ::Type { todo!() }` + = help: implement the missing item: `fn method4(&self, _: &Self) -> ::Type { todo!() }` + = help: implement the missing item: `fn method5(self: &std::boxed::Box) -> ::Type { todo!() }` error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr b/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr index 552273b8ba927..a30bfa66c5a9c 100644 --- a/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr +++ b/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr @@ -17,11 +17,10 @@ LL | let mut r = R {c: Box::new(f)}; LL | f(&mut r, false) | ^ value borrowed here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/moves-based-on-type-no-recursive-stack-closure.rs:30:35 +help: consider further restricting this bound | -LL | fn conspirator(mut f: F) where F: FnMut(&mut R, bool) { - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | fn conspirator(mut f: F) where F: FnMut(&mut R, bool) + Copy { + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/multiple-plugin-registrars.stderr b/src/test/ui/multiple-plugin-registrars.stderr index dad8172e0c59e..dffc73a21e418 100644 --- a/src/test/ui/multiple-plugin-registrars.stderr +++ b/src/test/ui/multiple-plugin-registrars.stderr @@ -25,5 +25,5 @@ note: one is here LL | pub fn two() {} | ^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 2 warnings emitted diff --git a/src/test/ui/mut/mutable-enum-indirect.rs b/src/test/ui/mut/mutable-enum-indirect.rs index 611ff0d66a24e..502859c041353 100644 --- a/src/test/ui/mut/mutable-enum-indirect.rs +++ b/src/test/ui/mut/mutable-enum-indirect.rs @@ -1,7 +1,7 @@ // Tests that an `&` pointer to something inherently mutable is itself // to be considered mutable. -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Sync; diff --git a/src/test/ui/mut/mutable-enum-indirect.stderr b/src/test/ui/mut/mutable-enum-indirect.stderr index 0290efc3d9679..9decba790d2ad 100644 --- a/src/test/ui/mut/mutable-enum-indirect.stderr +++ b/src/test/ui/mut/mutable-enum-indirect.stderr @@ -2,7 +2,7 @@ error[E0277]: `NoSync` cannot be shared between threads safely --> $DIR/mutable-enum-indirect.rs:17:5 | LL | fn bar(_: T) {} - | --- ---- required by this bound in `bar` + | ---- required by this bound in `bar` ... LL | bar(&x); | ^^^ `NoSync` cannot be shared between threads safely diff --git a/src/test/ui/mutexguard-sync.stderr b/src/test/ui/mutexguard-sync.stderr index 71a06fce4b9e1..8b5362490bf94 100644 --- a/src/test/ui/mutexguard-sync.stderr +++ b/src/test/ui/mutexguard-sync.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::cell::Cell` cannot be shared between threads safely --> $DIR/mutexguard-sync.rs:11:15 | LL | fn test_sync(_t: T) {} - | --------- ---- required by this bound in `test_sync` + | ---- required by this bound in `test_sync` ... LL | test_sync(guard); | ^^^^^ `std::cell::Cell` cannot be shared between threads safely diff --git a/src/test/ui/namespace/namespace-mix.stderr b/src/test/ui/namespace/namespace-mix.stderr index 13d727de441cb..c80055f00d7d9 100644 --- a/src/test/ui/namespace/namespace-mix.stderr +++ b/src/test/ui/namespace/namespace-mix.stderr @@ -12,7 +12,7 @@ help: a tuple struct with a similar name exists | LL | check(m1::TS); | ^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use m2::S; | @@ -35,7 +35,7 @@ help: a tuple struct with a similar name exists | LL | check(xm1::TS); | ^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use m2::S; | @@ -57,7 +57,7 @@ help: a tuple variant with a similar name exists | LL | check(m7::TV); | ^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use m8::V; | @@ -79,7 +79,7 @@ help: a tuple variant with a similar name exists | LL | check(xm7::TV); | ^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use m8::V; | @@ -90,7 +90,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:33:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m1::S{}); | ^^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -99,7 +99,7 @@ error[E0277]: the trait bound `c::S: Impossible` is not satisfied --> $DIR/namespace-mix.rs:35:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m2::S{}); | ^^^^^^^ the trait `Impossible` is not implemented for `c::S` @@ -108,7 +108,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:36:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m2::S); | ^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -117,7 +117,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:39:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm1::S{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -126,7 +126,7 @@ error[E0277]: the trait bound `namespace_mix::c::S: Impossible` is not satisfied --> $DIR/namespace-mix.rs:41:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm2::S{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::S` @@ -135,7 +135,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:42:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm2::S); | ^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -144,7 +144,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:55:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m3::TS{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -153,7 +153,7 @@ error[E0277]: the trait bound `fn() -> c::TS {c::TS}: Impossible` is not satisfi --> $DIR/namespace-mix.rs:56:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m3::TS); | ^^^^^^ the trait `Impossible` is not implemented for `fn() -> c::TS {c::TS}` @@ -162,7 +162,7 @@ error[E0277]: the trait bound `c::TS: Impossible` is not satisfied --> $DIR/namespace-mix.rs:57:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m4::TS{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::TS` @@ -171,7 +171,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:58:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m4::TS); | ^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -180,7 +180,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:61:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm3::TS{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -189,7 +189,7 @@ error[E0277]: the trait bound `fn() -> namespace_mix::c::TS {namespace_mix::c::T --> $DIR/namespace-mix.rs:62:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm3::TS); | ^^^^^^^ the trait `Impossible` is not implemented for `fn() -> namespace_mix::c::TS {namespace_mix::c::TS}` @@ -198,7 +198,7 @@ error[E0277]: the trait bound `namespace_mix::c::TS: Impossible` is not satisfie --> $DIR/namespace-mix.rs:63:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm4::TS{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::TS` @@ -207,7 +207,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:64:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm4::TS); | ^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -216,7 +216,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:77:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m5::US{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -225,7 +225,7 @@ error[E0277]: the trait bound `c::US: Impossible` is not satisfied --> $DIR/namespace-mix.rs:78:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m5::US); | ^^^^^^ the trait `Impossible` is not implemented for `c::US` @@ -234,7 +234,7 @@ error[E0277]: the trait bound `c::US: Impossible` is not satisfied --> $DIR/namespace-mix.rs:79:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m6::US{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::US` @@ -243,7 +243,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:80:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m6::US); | ^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -252,7 +252,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:83:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm5::US{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -261,7 +261,7 @@ error[E0277]: the trait bound `namespace_mix::c::US: Impossible` is not satisfie --> $DIR/namespace-mix.rs:84:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm5::US); | ^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::US` @@ -270,7 +270,7 @@ error[E0277]: the trait bound `namespace_mix::c::US: Impossible` is not satisfie --> $DIR/namespace-mix.rs:85:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm6::US{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::US` @@ -279,7 +279,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:86:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm6::US); | ^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -288,7 +288,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:99:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m7::V{}); | ^^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -297,7 +297,7 @@ error[E0277]: the trait bound `c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:101:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m8::V{}); | ^^^^^^^ the trait `Impossible` is not implemented for `c::E` @@ -306,7 +306,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:102:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m8::V); | ^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -315,7 +315,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:105:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm7::V{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -324,7 +324,7 @@ error[E0277]: the trait bound `namespace_mix::c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:107:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm8::V{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::E` @@ -333,7 +333,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:108:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm8::V); | ^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -342,7 +342,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:121:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m9::TV{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -351,7 +351,7 @@ error[E0277]: the trait bound `fn() -> c::E {c::E::TV}: Impossible` is not satis --> $DIR/namespace-mix.rs:122:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(m9::TV); | ^^^^^^ the trait `Impossible` is not implemented for `fn() -> c::E {c::E::TV}` @@ -360,7 +360,7 @@ error[E0277]: the trait bound `c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:123:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(mA::TV{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::E` @@ -369,7 +369,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:124:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(mA::TV); | ^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -378,7 +378,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:127:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm9::TV{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -387,7 +387,7 @@ error[E0277]: the trait bound `fn() -> namespace_mix::c::E {namespace_mix::xm7:: --> $DIR/namespace-mix.rs:128:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xm9::TV); | ^^^^^^^ the trait `Impossible` is not implemented for `fn() -> namespace_mix::c::E {namespace_mix::xm7::TV}` @@ -396,7 +396,7 @@ error[E0277]: the trait bound `namespace_mix::c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:129:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xmA::TV{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::E` @@ -405,7 +405,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:130:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xmA::TV); | ^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -414,7 +414,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:143:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(mB::UV{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -423,7 +423,7 @@ error[E0277]: the trait bound `c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:144:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(mB::UV); | ^^^^^^ the trait `Impossible` is not implemented for `c::E` @@ -432,7 +432,7 @@ error[E0277]: the trait bound `c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:145:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(mC::UV{}); | ^^^^^^^^ the trait `Impossible` is not implemented for `c::E` @@ -441,7 +441,7 @@ error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:146:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(mC::UV); | ^^^^^^ the trait `Impossible` is not implemented for `c::Item` @@ -450,7 +450,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:149:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xmB::UV{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` @@ -459,7 +459,7 @@ error[E0277]: the trait bound `namespace_mix::c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:150:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xmB::UV); | ^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::E` @@ -468,7 +468,7 @@ error[E0277]: the trait bound `namespace_mix::c::E: Impossible` is not satisfied --> $DIR/namespace-mix.rs:151:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xmC::UV{}); | ^^^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::E` @@ -477,7 +477,7 @@ error[E0277]: the trait bound `namespace_mix::c::Item: Impossible` is not satisf --> $DIR/namespace-mix.rs:152:11 | LL | fn check(_: T) {} - | ----- ---------- required by this bound in `check` + | ---------- required by this bound in `check` ... LL | check(xmC::UV); | ^^^^^^^ the trait `Impossible` is not implemented for `namespace_mix::c::Item` diff --git a/src/test/ui/never_type/auto-traits.rs b/src/test/ui/never_type/auto-traits.rs index 2d9689888cb30..84c8db4053e4f 100644 --- a/src/test/ui/never_type/auto-traits.rs +++ b/src/test/ui/never_type/auto-traits.rs @@ -1,6 +1,7 @@ // check-pass #![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![feature(never_type)] fn main() { diff --git a/src/test/ui/never_type/defaulted-never-note.rs b/src/test/ui/never_type/defaulted-never-note.rs index 1780cb6535d05..c96c4784dcf32 100644 --- a/src/test/ui/never_type/defaulted-never-note.rs +++ b/src/test/ui/never_type/defaulted-never-note.rs @@ -20,7 +20,6 @@ impl ImplementedForUnitButNotNever for () {} fn foo(_t: T) {} //~^ NOTE required by this bound in `foo` -//~| NOTE fn smeg() { let _x = return; diff --git a/src/test/ui/never_type/defaulted-never-note.stderr b/src/test/ui/never_type/defaulted-never-note.stderr index 046d0c5fa19ff..69691883de1e3 100644 --- a/src/test/ui/never_type/defaulted-never-note.stderr +++ b/src/test/ui/never_type/defaulted-never-note.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `!: ImplementedForUnitButNotNever` is not satisfied - --> $DIR/defaulted-never-note.rs:27:5 + --> $DIR/defaulted-never-note.rs:26:5 | LL | fn foo(_t: T) {} - | --- ----------------------------- required by this bound in `foo` + | ----------------------------- required by this bound in `foo` ... LL | foo(_x); | ^^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!` diff --git a/src/test/ui/never_type/never-assign-dead-code.stderr b/src/test/ui/never_type/never-assign-dead-code.stderr index 5525802c2511a..5c5cafadc85fb 100644 --- a/src/test/ui/never_type/never-assign-dead-code.stderr +++ b/src/test/ui/never_type/never-assign-dead-code.stderr @@ -34,3 +34,5 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` +warning: 3 warnings emitted + diff --git a/src/test/run-fail/return-never-coerce.rs b/src/test/ui/never_type/return-never-coerce.rs similarity index 84% rename from src/test/run-fail/return-never-coerce.rs rename to src/test/ui/never_type/return-never-coerce.rs index 18182ff0f9d47..d615940eff1f0 100644 --- a/src/test/run-fail/return-never-coerce.rs +++ b/src/test/ui/never_type/return-never-coerce.rs @@ -1,6 +1,8 @@ // Test that ! coerces to other types. +// run-fail // error-pattern:aah! +// ignore-emscripten no processes fn call_another_fn T>(f: F) -> T { f() diff --git a/src/test/ui/nll/issue-51191.stderr b/src/test/ui/nll/issue-51191.stderr index 7fa355eabb230..4e2e4c20a02be 100644 --- a/src/test/ui/nll/issue-51191.stderr +++ b/src/test/ui/nll/issue-51191.stderr @@ -48,6 +48,6 @@ LL | (&mut self).bar(); | cannot borrow as mutable | try removing `&mut` here -error: aborting due to 5 previous errors +error: aborting due to 5 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr b/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr index 8412cbdc54b3b..2dca92e2be1f3 100644 --- a/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr +++ b/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr @@ -13,7 +13,10 @@ LL | LL | ; | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | D("other").next(&_thing1); + | ^ error: aborting due to previous error diff --git a/src/test/ui/nll/issue-54556-niconii.stderr b/src/test/ui/nll/issue-54556-niconii.stderr index 40cd04de5ecc1..b4791fd22b4ad 100644 --- a/src/test/ui/nll/issue-54556-niconii.stderr +++ b/src/test/ui/nll/issue-54556-niconii.stderr @@ -13,7 +13,10 @@ LL | } | `counter` dropped here while still borrowed | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::result::Result, ()>` | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | if let Ok(_) = counter.lock() { }; + | ^ error: aborting due to previous error diff --git a/src/test/ui/nll/issue-54556-stephaneyfx.stderr b/src/test/ui/nll/issue-54556-stephaneyfx.stderr index 0bf76485eef2a..77065f0b8d213 100644 --- a/src/test/ui/nll/issue-54556-stephaneyfx.stderr +++ b/src/test/ui/nll/issue-54556-stephaneyfx.stderr @@ -12,7 +12,12 @@ LL | } | `stmt` dropped here while still borrowed | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::iter::Map, [closure@$DIR/issue-54556-stephaneyfx.rs:28:14: 28:23]>` | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = rows.map(|row| row).next(); x + | ^^^^^^^ ^^^ error: aborting due to previous error diff --git a/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr index 513dca7950af9..047fdbc9148ac 100644 --- a/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr +++ b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr @@ -12,7 +12,10 @@ LL | LL | ; | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | D(&_thing1).end(); + | ^ error: aborting due to previous error diff --git a/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr b/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr index 52d0870b78f95..85920a8e7394c 100644 --- a/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr +++ b/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr @@ -8,7 +8,10 @@ LL | { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); } ; // suggest `;` + | ^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:13:55 @@ -20,7 +23,10 @@ LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } } ; // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); } } ; // suggest `;` + | ^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:16:55 @@ -32,7 +38,10 @@ LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }; } // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); }; } // suggest `;` + | ^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:19:55 @@ -44,7 +53,10 @@ LL | let _ = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | let _ = { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); } ; // suggest `;` + | ^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:22:55 @@ -56,7 +68,10 @@ LL | let _u = { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } ; // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | let _u = { let mut _t1 = D(Box::new("t1")); D(&_t1).unit(); } ; // suggest `;` + | ^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:25:55 @@ -68,7 +83,12 @@ LL | let _x = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let _x = { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // `let x = ...; x` + | ^^^^^^^ ^^^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:30:55 @@ -80,7 +100,12 @@ LL | _y = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `l | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | _y = { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // `let x = ...; x` + | ^^^^^^^ ^^^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:37:55 @@ -93,7 +118,10 @@ LL | fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped. +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit(); } // suggest `;` + | ^ error[E0597]: `_t1` does not live long enough --> $DIR/issue-54556-used-vs-unused-tails.rs:40:55 @@ -106,7 +134,12 @@ LL | fn f() -> String { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } // | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | fn f() -> String { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } // `let x = ...; x` + | ^^^^^^^ ^^^ error: aborting due to 9 previous errors diff --git a/src/test/ui/nll/issue-68550.rs b/src/test/ui/nll/issue-68550.rs new file mode 100644 index 0000000000000..6bfd18de18c6a --- /dev/null +++ b/src/test/ui/nll/issue-68550.rs @@ -0,0 +1,15 @@ +// Regression test for issue #68550. +// +// The `&'static A:` where clause was triggering +// ICEs because it wound up being compiled to reference +// the `'empty(U0)` region. + +fn run<'a, A>(x: A) +where + A: 'static, + &'static A: , +{ + let _: &'a A = &x; //~ ERROR `x` does not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/issue-68550.stderr b/src/test/ui/nll/issue-68550.stderr new file mode 100644 index 0000000000000..e234ebb04e16a --- /dev/null +++ b/src/test/ui/nll/issue-68550.stderr @@ -0,0 +1,16 @@ +error[E0597]: `x` does not live long enough + --> $DIR/issue-68550.rs:12:20 + | +LL | fn run<'a, A>(x: A) + | -- lifetime `'a` defined here +... +LL | let _: &'a A = &x; + | ----- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'a` +LL | } + | - `x` dropped here while still borrowed + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/outlives-suggestion-simple.polonius.stderr b/src/test/ui/nll/outlives-suggestion-simple.polonius.stderr index 815744618f62c..dbbda62d2086c 100644 --- a/src/test/ui/nll/outlives-suggestion-simple.polonius.stderr +++ b/src/test/ui/nll/outlives-suggestion-simple.polonius.stderr @@ -72,6 +72,8 @@ LL | (x, x) | = help: consider adding the following bound: `'a: 'c` +help: add bound `'a: 'b + 'c` + error: lifetime may not live long enough --> $DIR/outlives-suggestion-simple.rs:31:9 | @@ -106,16 +108,16 @@ LL | self.x | = help: consider adding the following bound: `'b: 'a` -error[E0521]: borrowed data escapes outside of function +error[E0521]: borrowed data escapes outside of associated function --> $DIR/outlives-suggestion-simple.rs:73:9 | LL | fn get_bar(&self) -> Bar2 { | ----- | | - | `self` is declared here, outside of the function body - | `self` is a reference that is only valid in the function body + | `self` declared here, outside of the associated function body + | `self` is a reference that is only valid in the associated function body LL | Bar2::new(&self) - | ^^^^^^^^^^^^^^^^ `self` escapes the function body here + | ^^^^^^^^^^^^^^^^ `self` escapes the associated function body here error: aborting due to 10 previous errors diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs new file mode 100644 index 0000000000000..d3964a7f515de --- /dev/null +++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs @@ -0,0 +1,36 @@ +// Test that the NLL solver cannot find a solution +// for `exists { forall { R2: R1 } }`. +// +// In this test, the impl should match `fn(T)` for some `T`, +// but we ask it to match `for<'a> fn(&'a ())`. Due to argument +// contravariance, this effectively requires a `T = &'b ()` where +// `forall<'a> { 'a: 'b }`. Therefore, we get an error. +// +// Note the use of `-Zno-leak-check` and `feature(nll)` here. These +// are presently required in order to skip the leak-check errors. +// +// c.f. Issue #57642. +// +// compile-flags:-Zno-leak-check + +#![feature(nll)] + +trait Y { + type F; + fn make_f() -> Self::F; +} + +impl Y for fn(T) { + type F = fn(T); + + fn make_f() -> Self::F { + |_| {} + } +} + +fn main() { + let _x = ::make_f(); + //~^ higher-ranked subtype error + //~| higher-ranked subtype error + //~| higher-ranked subtype error +} diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr new file mode 100644 index 0000000000000..70fb877d71689 --- /dev/null +++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr @@ -0,0 +1,20 @@ +error: higher-ranked subtype error + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14 + | +LL | let _x = ::make_f(); + | ^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14 + | +LL | let _x = ::make_f(); + | ^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14 + | +LL | let _x = ::make_f(); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/nll/user-annotations/closure-substs.polonius.stderr b/src/test/ui/nll/user-annotations/closure-substs.polonius.stderr index d5bcdf6444171..46b6c04dcbc34 100644 --- a/src/test/ui/nll/user-annotations/closure-substs.polonius.stderr +++ b/src/test/ui/nll/user-annotations/closure-substs.polonius.stderr @@ -50,7 +50,7 @@ error[E0521]: borrowed data escapes outside of closure --> $DIR/closure-substs.rs:29:9 | LL | |x: &i32, b: fn(&'static i32)| { - | - - `b` is declared here, outside of the closure body + | - - `b` declared here, outside of the closure body | | | `x` is a reference that is only valid in the closure body LL | b(x); diff --git a/src/test/ui/no-implicit-prelude-nested.stderr b/src/test/ui/no-implicit-prelude-nested.stderr index e57d8af5f99b9..8a26366d751d3 100644 --- a/src/test/ui/no-implicit-prelude-nested.stderr +++ b/src/test/ui/no-implicit-prelude-nested.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `Add` in this scope LL | impl Add for Test {} | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use std::ops::Add; | @@ -15,7 +15,7 @@ error[E0404]: expected trait, found derive macro `Clone` LL | impl Clone for Test {} | ^^^^^ not a trait | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::clone::Clone; | @@ -28,7 +28,7 @@ error[E0405]: cannot find trait `Iterator` in this scope LL | impl Iterator for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::iter::Iterator; | @@ -41,7 +41,7 @@ error[E0405]: cannot find trait `ToString` in this scope LL | impl ToString for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::prelude::v1::ToString; | @@ -60,7 +60,7 @@ error[E0425]: cannot find function `drop` in this scope LL | drop(2) | ^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::mem::drop; | @@ -73,7 +73,7 @@ error[E0405]: cannot find trait `Add` in this scope LL | impl Add for Test {} | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use std::ops::Add; | @@ -84,7 +84,7 @@ error[E0404]: expected trait, found derive macro `Clone` LL | impl Clone for Test {} | ^^^^^ not a trait | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::clone::Clone; | @@ -97,7 +97,7 @@ error[E0405]: cannot find trait `Iterator` in this scope LL | impl Iterator for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::iter::Iterator; | @@ -110,7 +110,7 @@ error[E0405]: cannot find trait `ToString` in this scope LL | impl ToString for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::prelude::v1::ToString; | @@ -129,7 +129,7 @@ error[E0425]: cannot find function `drop` in this scope LL | drop(2) | ^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::mem::drop; | @@ -142,7 +142,7 @@ error[E0405]: cannot find trait `Add` in this scope LL | impl Add for Test {} | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use std::ops::Add; | @@ -153,7 +153,7 @@ error[E0404]: expected trait, found derive macro `Clone` LL | impl Clone for Test {} | ^^^^^ not a trait | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::clone::Clone; | @@ -166,7 +166,7 @@ error[E0405]: cannot find trait `Iterator` in this scope LL | impl Iterator for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::iter::Iterator; | @@ -179,7 +179,7 @@ error[E0405]: cannot find trait `ToString` in this scope LL | impl ToString for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::prelude::v1::ToString; | @@ -198,7 +198,7 @@ error[E0425]: cannot find function `drop` in this scope LL | drop(2) | ^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::mem::drop; | diff --git a/src/test/ui/no-implicit-prelude.stderr b/src/test/ui/no-implicit-prelude.stderr index 8b99529f4dd7c..9cda4f64c79d0 100644 --- a/src/test/ui/no-implicit-prelude.stderr +++ b/src/test/ui/no-implicit-prelude.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `Add` in this scope LL | impl Add for Test {} | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use std::ops::Add; | @@ -15,7 +15,7 @@ error[E0404]: expected trait, found derive macro `Clone` LL | impl Clone for Test {} | ^^^^^ not a trait | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::clone::Clone; | @@ -28,7 +28,7 @@ error[E0405]: cannot find trait `Iterator` in this scope LL | impl Iterator for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::iter::Iterator; | @@ -41,7 +41,7 @@ error[E0405]: cannot find trait `ToString` in this scope LL | impl ToString for Test {} | ^^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::prelude::v1::ToString; | @@ -60,7 +60,7 @@ error[E0425]: cannot find function `drop` in this scope LL | drop(2) | ^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::mem::drop; | diff --git a/src/test/ui/no-send-res-ports.rs b/src/test/ui/no-send-res-ports.rs index 6a1965f7cd358..e10f447365ea6 100644 --- a/src/test/ui/no-send-res-ports.rs +++ b/src/test/ui/no-send-res-ports.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::thread; use std::rc::Rc; diff --git a/src/test/ui/no-send-res-ports.stderr b/src/test/ui/no-send-res-ports.stderr index 65946ee8a20cf..13683cf86dba8 100644 --- a/src/test/ui/no-send-res-ports.stderr +++ b/src/test/ui/no-send-res-ports.stderr @@ -1,5 +1,5 @@ error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely - --> $DIR/no-send-res-ports.rs:29:5 + --> $DIR/no-send-res-ports.rs:25:5 | LL | thread::spawn(move|| { | _____^^^^^^^^^^^^^_- @@ -9,17 +9,17 @@ LL | | LL | | let y = x; LL | | println!("{:?}", y); LL | | }); - | |_____- within this `[closure@$DIR/no-send-res-ports.rs:29:19: 33:6 x:main::Foo]` + | |_____- within this `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:main::Foo]` | ::: $SRC_DIR/libstd/thread/mod.rs:LL:COL | LL | F: Send + 'static, | ---- required by this bound in `std::thread::spawn` | - = help: within `[closure@$DIR/no-send-res-ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` + = help: within `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` = note: required because it appears within the type `Port<()>` = note: required because it appears within the type `main::Foo` - = note: required because it appears within the type `[closure@$DIR/no-send-res-ports.rs:29:19: 33:6 x:main::Foo]` + = note: required because it appears within the type `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:main::Foo]` error: aborting due to previous error diff --git a/src/test/ui/no_send-enum.rs b/src/test/ui/no_send-enum.rs index 4b4d06f1e32cd..bd560649b990e 100644 --- a/src/test/ui/no_send-enum.rs +++ b/src/test/ui/no_send-enum.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Send; diff --git a/src/test/ui/no_send-enum.stderr b/src/test/ui/no_send-enum.stderr index 8a4b2e9c7a7c1..95a0d77676de8 100644 --- a/src/test/ui/no_send-enum.stderr +++ b/src/test/ui/no_send-enum.stderr @@ -2,7 +2,7 @@ error[E0277]: `NoSend` cannot be sent between threads safely --> $DIR/no_send-enum.rs:16:5 | LL | fn bar(_: T) {} - | --- ---- required by this bound in `bar` + | ---- required by this bound in `bar` ... LL | bar(x); | ^^^ `NoSend` cannot be sent between threads safely diff --git a/src/test/ui/no_send-rc.stderr b/src/test/ui/no_send-rc.stderr index bd646d0509daf..1eb2edb14b80a 100644 --- a/src/test/ui/no_send-rc.stderr +++ b/src/test/ui/no_send-rc.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::rc::Rc<{integer}>` cannot be sent between threads safely --> $DIR/no_send-rc.rs:7:9 | LL | fn bar(_: T) {} - | --- ---- required by this bound in `bar` + | ---- required by this bound in `bar` ... LL | bar(x); | ^ `std::rc::Rc<{integer}>` cannot be sent between threads safely diff --git a/src/test/ui/no_send-struct.rs b/src/test/ui/no_send-struct.rs index 67816bfee5d02..75a363f9f7639 100644 --- a/src/test/ui/no_send-struct.rs +++ b/src/test/ui/no_send-struct.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Send; diff --git a/src/test/ui/no_send-struct.stderr b/src/test/ui/no_send-struct.stderr index 4823852c2ff80..4e8801a58bfae 100644 --- a/src/test/ui/no_send-struct.stderr +++ b/src/test/ui/no_send-struct.stderr @@ -2,7 +2,7 @@ error[E0277]: `Foo` cannot be sent between threads safely --> $DIR/no_send-struct.rs:15:9 | LL | fn bar(_: T) {} - | --- ---- required by this bound in `bar` + | ---- required by this bound in `bar` ... LL | bar(x); | ^ `Foo` cannot be sent between threads safely diff --git a/src/test/ui/no_share-enum.rs b/src/test/ui/no_share-enum.rs index f5edb63cf86e9..44bf1913e7aac 100644 --- a/src/test/ui/no_share-enum.rs +++ b/src/test/ui/no_share-enum.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Sync; diff --git a/src/test/ui/no_share-enum.stderr b/src/test/ui/no_share-enum.stderr index f42228ef6ab42..40996aef702a0 100644 --- a/src/test/ui/no_share-enum.stderr +++ b/src/test/ui/no_share-enum.stderr @@ -2,7 +2,7 @@ error[E0277]: `NoSync` cannot be shared between threads safely --> $DIR/no_share-enum.rs:14:5 | LL | fn bar(_: T) {} - | --- ---- required by this bound in `bar` + | ---- required by this bound in `bar` ... LL | bar(x); | ^^^ `NoSync` cannot be shared between threads safely diff --git a/src/test/ui/no_share-struct.rs b/src/test/ui/no_share-struct.rs index 35867d0f2166d..7d8a36a76f274 100644 --- a/src/test/ui/no_share-struct.rs +++ b/src/test/ui/no_share-struct.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Sync; diff --git a/src/test/ui/no_share-struct.stderr b/src/test/ui/no_share-struct.stderr index 620b5427b9aec..f14b06835f9da 100644 --- a/src/test/ui/no_share-struct.stderr +++ b/src/test/ui/no_share-struct.stderr @@ -2,7 +2,7 @@ error[E0277]: `Foo` cannot be shared between threads safely --> $DIR/no_share-struct.rs:12:9 | LL | fn bar(_: T) {} - | --- ---- required by this bound in `bar` + | ---- required by this bound in `bar` ... LL | bar(x); | ^ `Foo` cannot be shared between threads safely diff --git a/src/test/ui/non-copyable-void.rs b/src/test/ui/non-copyable-void.rs index 186731f2e7233..ddaaee436ae23 100644 --- a/src/test/ui/non-copyable-void.rs +++ b/src/test/ui/non-copyable-void.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - // ignore-wasm32-bare no libc to test ffi with #![feature(rustc_private)] diff --git a/src/test/ui/non-copyable-void.stderr b/src/test/ui/non-copyable-void.stderr index dd67a110d2218..78d212f7a7ba7 100644 --- a/src/test/ui/non-copyable-void.stderr +++ b/src/test/ui/non-copyable-void.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for enum `libc::c_void` in the current scope - --> $DIR/non-copyable-void.rs:16:23 + --> $DIR/non-copyable-void.rs:11:23 | LL | let _z = (*y).clone(); | ^^^^^ method not found in `libc::c_void` diff --git a/src/test/ui/non-ice-error-on-worker-io-fail.rs b/src/test/ui/non-ice-error-on-worker-io-fail.rs index 8af17742850da..30779fc65c0fd 100644 --- a/src/test/ui/non-ice-error-on-worker-io-fail.rs +++ b/src/test/ui/non-ice-error-on-worker-io-fail.rs @@ -4,8 +4,12 @@ // // An attempt to `-o` into a directory we cannot write into should indeed // be an error; but not an ICE. +// +// However, some folks run tests as root, which can write `/dev/` and end +// up clobbering `/dev/null`. Instead we'll use a non-existent path, which +// also used to ICE, but even root can't magically write there. -// compile-flags: -o /dev/null +// compile-flags: -o /does-not-exist/output // The error-pattern check occurs *before* normalization, and the error patterns // are wildly different between build environments. So this is a cop-out (and we @@ -15,10 +19,10 @@ // error-pattern: error // On Mac OS X, we get an error like the below -// normalize-stderr-test "failed to write bytecode to /dev/null.non_ice_error_on_worker_io_fail.*" -> "io error modifying /dev/" +// normalize-stderr-test "failed to write bytecode to /does-not-exist/output.non_ice_error_on_worker_io_fail.*" -> "io error modifying /does-not-exist/" // On Linux, we get an error like the below -// normalize-stderr-test "couldn't create a temp dir.*" -> "io error modifying /dev/" +// normalize-stderr-test "couldn't create a temp dir.*" -> "io error modifying /does-not-exist/" // ignore-tidy-linelength // ignore-windows - this is a unix-specific test diff --git a/src/test/ui/non-ice-error-on-worker-io-fail.stderr b/src/test/ui/non-ice-error-on-worker-io-fail.stderr index f5601ad03d5d8..edadecf273a7b 100644 --- a/src/test/ui/non-ice-error-on-worker-io-fail.stderr +++ b/src/test/ui/non-ice-error-on-worker-io-fail.stderr @@ -1,6 +1,6 @@ warning: ignoring --out-dir flag due to -o flag -error: io error modifying /dev/ +error: io error modifying /does-not-exist/ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/noncopyable-class.rs b/src/test/ui/noncopyable-class.rs index 731f4ab9c784a..11b6eb736e9db 100644 --- a/src/test/ui/noncopyable-class.rs +++ b/src/test/ui/noncopyable-class.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - // Test that a class with a non-copyable field can't be // copied diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr index 472ce34870a0a..994eb65ae15bf 100644 --- a/src/test/ui/noncopyable-class.stderr +++ b/src/test/ui/noncopyable-class.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `Foo` in the current scope - --> $DIR/noncopyable-class.rs:39:16 + --> $DIR/noncopyable-class.rs:34:16 | LL | struct Foo { | ---------- method `clone` not found for this diff --git a/src/test/ui/not-panic/not-panic-safe-2.stderr b/src/test/ui/not-panic/not-panic-safe-2.stderr index 6668d2d0db191..c52d5b9adee06 100644 --- a/src/test/ui/not-panic/not-panic-safe-2.stderr +++ b/src/test/ui/not-panic/not-panic-safe-2.stderr @@ -2,7 +2,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutabil --> $DIR/not-panic-safe-2.rs:10:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary @@ -15,7 +15,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutab --> $DIR/not-panic-safe-2.rs:10:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary diff --git a/src/test/ui/not-panic/not-panic-safe-3.stderr b/src/test/ui/not-panic/not-panic-safe-3.stderr index c23b08fc9eda9..711346b7b1c2b 100644 --- a/src/test/ui/not-panic/not-panic-safe-3.stderr +++ b/src/test/ui/not-panic/not-panic-safe-3.stderr @@ -2,7 +2,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutabil --> $DIR/not-panic-safe-3.rs:10:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary @@ -15,7 +15,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutab --> $DIR/not-panic-safe-3.rs:10:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary diff --git a/src/test/ui/not-panic/not-panic-safe-4.stderr b/src/test/ui/not-panic/not-panic-safe-4.stderr index 916804a834f58..ada22fe9a7785 100644 --- a/src/test/ui/not-panic/not-panic-safe-4.stderr +++ b/src/test/ui/not-panic/not-panic-safe-4.stderr @@ -2,7 +2,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutabil --> $DIR/not-panic-safe-4.rs:9:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::<&RefCell>(); | ^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary @@ -15,7 +15,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutab --> $DIR/not-panic-safe-4.rs:9:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::<&RefCell>(); | ^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary diff --git a/src/test/ui/not-panic/not-panic-safe-5.stderr b/src/test/ui/not-panic/not-panic-safe-5.stderr index d5c189723f402..c987ca7c088af 100644 --- a/src/test/ui/not-panic/not-panic-safe-5.stderr +++ b/src/test/ui/not-panic/not-panic-safe-5.stderr @@ -2,7 +2,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutabil --> $DIR/not-panic-safe-5.rs:9:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::<*const UnsafeCell>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary diff --git a/src/test/ui/not-panic/not-panic-safe-6.stderr b/src/test/ui/not-panic/not-panic-safe-6.stderr index c8013a836a177..f184a459b829f 100644 --- a/src/test/ui/not-panic/not-panic-safe-6.stderr +++ b/src/test/ui/not-panic/not-panic-safe-6.stderr @@ -2,7 +2,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutabil --> $DIR/not-panic-safe-6.rs:9:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::<*mut RefCell>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary @@ -15,7 +15,7 @@ error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutab --> $DIR/not-panic-safe-6.rs:9:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::<*mut RefCell>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary diff --git a/src/test/ui/not-panic/not-panic-safe.stderr b/src/test/ui/not-panic/not-panic-safe.stderr index 2362ccd32de99..b254a0416667a 100644 --- a/src/test/ui/not-panic/not-panic-safe.stderr +++ b/src/test/ui/not-panic/not-panic-safe.stderr @@ -2,7 +2,7 @@ error[E0277]: the type `&mut i32` may not be safely transferred across an unwind --> $DIR/not-panic-safe.rs:9:5 | LL | fn assert() {} - | ------ ---------- required by this bound in `assert` + | ---------- required by this bound in `assert` ... LL | assert::<&mut i32>(); | ^^^^^^^^^^^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary diff --git a/src/test/ui/not-sync.stderr b/src/test/ui/not-sync.stderr index 8bb4ce2e2c773..25f1a66062bea 100644 --- a/src/test/ui/not-sync.stderr +++ b/src/test/ui/not-sync.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::cell::Cell` cannot be shared between threads safely --> $DIR/not-sync.rs:8:12 | LL | fn test() {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test::>(); | ^^^^^^^^^ `std::cell::Cell` cannot be shared between threads safely @@ -13,7 +13,7 @@ error[E0277]: `std::cell::RefCell` cannot be shared between threads safely --> $DIR/not-sync.rs:10:12 | LL | fn test() {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test::>(); | ^^^^^^^^^^^^ `std::cell::RefCell` cannot be shared between threads safely @@ -24,7 +24,7 @@ error[E0277]: `std::rc::Rc` cannot be shared between threads safely --> $DIR/not-sync.rs:13:12 | LL | fn test() {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test::>(); | ^^^^^^^ `std::rc::Rc` cannot be shared between threads safely @@ -35,7 +35,7 @@ error[E0277]: `std::rc::Weak` cannot be shared between threads safely --> $DIR/not-sync.rs:15:12 | LL | fn test() {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test::>(); | ^^^^^^^^^ `std::rc::Weak` cannot be shared between threads safely @@ -46,7 +46,7 @@ error[E0277]: `std::sync::mpsc::Receiver` cannot be shared between threads --> $DIR/not-sync.rs:18:12 | LL | fn test() {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test::>(); | ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver` cannot be shared between threads safely @@ -57,7 +57,7 @@ error[E0277]: `std::sync::mpsc::Sender` cannot be shared between threads sa --> $DIR/not-sync.rs:20:12 | LL | fn test() {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test::>(); | ^^^^^^^^^^^ `std::sync::mpsc::Sender` cannot be shared between threads safely diff --git a/src/test/ui/numbers-arithmetic/divide-by-zero.rs b/src/test/ui/numbers-arithmetic/divide-by-zero.rs new file mode 100644 index 0000000000000..30e0e6c1bdd4c --- /dev/null +++ b/src/test/ui/numbers-arithmetic/divide-by-zero.rs @@ -0,0 +1,9 @@ +// run-fail +// error-pattern:attempt to divide by zero +// ignore-emscripten no processes + +#[allow(unconditional_panic)] +fn main() { + let y = 0; + let _z = 1 / y; +} diff --git a/src/test/ui/numbers-arithmetic/mod-zero.rs b/src/test/ui/numbers-arithmetic/mod-zero.rs new file mode 100644 index 0000000000000..083716394124a --- /dev/null +++ b/src/test/ui/numbers-arithmetic/mod-zero.rs @@ -0,0 +1,9 @@ +// run-fail +// error-pattern:attempt to calculate the remainder with a divisor of zero +// ignore-emscripten no processes + +#[allow(unconditional_panic)] +fn main() { + let y = 0; + let _z = 1 % y; +} diff --git a/src/test/run-fail/overflowing-add.rs b/src/test/ui/numbers-arithmetic/overflowing-add.rs similarity index 80% rename from src/test/run-fail/overflowing-add.rs rename to src/test/ui/numbers-arithmetic/overflowing-add.rs index 5ca91314d95a2..b0f22a74b4a8f 100644 --- a/src/test/run-fail/overflowing-add.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-add.rs @@ -1,5 +1,7 @@ +// run-fail // error-pattern:thread 'main' panicked at 'attempt to add with overflow' // compile-flags: -C debug-assertions +// ignore-emscripten no processes #![allow(arithmetic_overflow)] diff --git a/src/test/ui/numbers-arithmetic/overflowing-lsh-1.rs b/src/test/ui/numbers-arithmetic/overflowing-lsh-1.rs new file mode 100644 index 0000000000000..e5ce80336397d --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-1.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _x = 1_i32 << 32; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-lsh-1.stderr b/src/test/ui/numbers-arithmetic/overflowing-lsh-1.stderr new file mode 100644 index 0000000000000..54008d33968bc --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-1.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-lsh-1.rs:7:14 + | +LL | let _x = 1_i32 << 32; + | ^^^^^^^^^^^ attempt to shift left with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-lsh-1.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/numbers-arithmetic/overflowing-lsh-2.rs b/src/test/ui/numbers-arithmetic/overflowing-lsh-2.rs new file mode 100644 index 0000000000000..7fd3407a056ef --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-2.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _x = 1 << -1; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-lsh-2.stderr b/src/test/ui/numbers-arithmetic/overflowing-lsh-2.stderr new file mode 100644 index 0000000000000..872e71bb73796 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-2.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-lsh-2.rs:7:14 + | +LL | let _x = 1 << -1; + | ^^^^^^^ attempt to shift left with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-lsh-2.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/numbers-arithmetic/overflowing-lsh-3.rs b/src/test/ui/numbers-arithmetic/overflowing-lsh-3.rs new file mode 100644 index 0000000000000..e007eb4a2e2f7 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-3.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _x = 1_u64 << 64; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-lsh-3.stderr b/src/test/ui/numbers-arithmetic/overflowing-lsh-3.stderr new file mode 100644 index 0000000000000..d55ed4a046c9d --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-3.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-lsh-3.rs:7:14 + | +LL | let _x = 1_u64 << 64; + | ^^^^^^^^^^^ attempt to shift left with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-lsh-3.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/run-fail/overflowing-lsh-4.rs b/src/test/ui/numbers-arithmetic/overflowing-lsh-4.rs similarity index 85% rename from src/test/run-fail/overflowing-lsh-4.rs rename to src/test/ui/numbers-arithmetic/overflowing-lsh-4.rs index 0d3912ce13f81..738d013391579 100644 --- a/src/test/run-fail/overflowing-lsh-4.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-4.rs @@ -1,15 +1,15 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift left with overflow' +// build-fail // compile-flags: -C debug-assertions // This function is checking that our automatic truncation does not // sidestep the overflow checking. -#![warn(arithmetic_overflow)] -#![warn(const_err)] +#![deny(arithmetic_overflow, const_err)] fn main() { // this signals overflow when checking is on let x = 1_i8 << 17; + //~^ ERROR: this arithmetic operation will overflow // ... but when checking is off, the fallback will truncate the // input to its lower three bits (= 1). Note that this is *not* diff --git a/src/test/ui/numbers-arithmetic/overflowing-lsh-4.stderr b/src/test/ui/numbers-arithmetic/overflowing-lsh-4.stderr new file mode 100644 index 0000000000000..1ef8dd3466c0a --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-lsh-4.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-lsh-4.rs:11:13 + | +LL | let x = 1_i8 << 17; + | ^^^^^^^^^^ attempt to shift left with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-lsh-4.rs:7:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/run-fail/overflowing-mul.rs b/src/test/ui/numbers-arithmetic/overflowing-mul.rs similarity index 80% rename from src/test/run-fail/overflowing-mul.rs rename to src/test/ui/numbers-arithmetic/overflowing-mul.rs index 2dfc9bb5ae472..34ab5d8fad5e6 100644 --- a/src/test/run-fail/overflowing-mul.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-mul.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:thread 'main' panicked at 'attempt to multiply with overflow' +// ignore-emscripten no processes // compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] diff --git a/src/test/run-fail/overflowing-neg.rs b/src/test/ui/numbers-arithmetic/overflowing-neg.rs similarity index 80% rename from src/test/run-fail/overflowing-neg.rs rename to src/test/ui/numbers-arithmetic/overflowing-neg.rs index f512aa35beda6..fe77544641cc1 100644 --- a/src/test/run-fail/overflowing-neg.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-neg.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:thread 'main' panicked at 'attempt to negate with overflow' +// ignore-emscripten no processes // compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] diff --git a/src/test/run-fail/overflowing-pow-signed.rs b/src/test/ui/numbers-arithmetic/overflowing-pow-signed.rs similarity index 77% rename from src/test/run-fail/overflowing-pow-signed.rs rename to src/test/ui/numbers-arithmetic/overflowing-pow-signed.rs index c539c685faf9e..b59efe6f21278 100644 --- a/src/test/run-fail/overflowing-pow-signed.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-pow-signed.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:thread 'main' panicked at 'attempt to multiply with overflow' +// ignore-emscripten no processes // compile-flags: -C debug-assertions fn main() { diff --git a/src/test/run-fail/overflowing-pow-unsigned.rs b/src/test/ui/numbers-arithmetic/overflowing-pow-unsigned.rs similarity index 77% rename from src/test/run-fail/overflowing-pow-unsigned.rs rename to src/test/ui/numbers-arithmetic/overflowing-pow-unsigned.rs index 1d4fa3b5c7eb8..f2643c1646316 100644 --- a/src/test/run-fail/overflowing-pow-unsigned.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-pow-unsigned.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:thread 'main' panicked at 'attempt to multiply with overflow' +// ignore-emscripten no processes // compile-flags: -C debug-assertions fn main() { diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-1.rs b/src/test/ui/numbers-arithmetic/overflowing-rsh-1.rs new file mode 100644 index 0000000000000..f1488cf8559a2 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-1.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _x = -1_i32 >> 32; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-1.stderr b/src/test/ui/numbers-arithmetic/overflowing-rsh-1.stderr new file mode 100644 index 0000000000000..236303e2e9aa3 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-1.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-rsh-1.rs:7:14 + | +LL | let _x = -1_i32 >> 32; + | ^^^^^^^^^^^^ attempt to shift right with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-rsh-1.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-2.rs b/src/test/ui/numbers-arithmetic/overflowing-rsh-2.rs new file mode 100644 index 0000000000000..39127b9703b6b --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-2.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _x = -1_i32 >> -1; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-2.stderr b/src/test/ui/numbers-arithmetic/overflowing-rsh-2.stderr new file mode 100644 index 0000000000000..981c8986f76b9 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-2.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-rsh-2.rs:7:14 + | +LL | let _x = -1_i32 >> -1; + | ^^^^^^^^^^^^ attempt to shift right with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-rsh-2.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-3.rs b/src/test/ui/numbers-arithmetic/overflowing-rsh-3.rs new file mode 100644 index 0000000000000..8ee6dde93eafe --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-3.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _x = -1_i64 >> 64; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-3.stderr b/src/test/ui/numbers-arithmetic/overflowing-rsh-3.stderr new file mode 100644 index 0000000000000..c2994503f0efc --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-3.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-rsh-3.rs:7:14 + | +LL | let _x = -1_i64 >> 64; + | ^^^^^^^^^^^^ attempt to shift right with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-rsh-3.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/run-fail/overflowing-rsh-4.rs b/src/test/ui/numbers-arithmetic/overflowing-rsh-4.rs similarity index 85% rename from src/test/run-fail/overflowing-rsh-4.rs rename to src/test/ui/numbers-arithmetic/overflowing-rsh-4.rs index 1877d5c968526..ce7f818e330b9 100644 --- a/src/test/run-fail/overflowing-rsh-4.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-4.rs @@ -1,15 +1,15 @@ -// error-pattern:thread 'main' panicked at 'attempt to shift right with overflow' +// build-fail // compile-flags: -C debug-assertions // This function is checking that our (type-based) automatic // truncation does not sidestep the overflow checking. -#![warn(arithmetic_overflow)] -#![warn(const_err)] +#![deny(arithmetic_overflow, const_err)] fn main() { // this signals overflow when checking is on let x = 2_i8 >> 17; + //~^ ERROR: this arithmetic operation will overflow // ... but when checking is off, the fallback will truncate the // input to its lower three bits (= 1). Note that this is *not* diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-4.stderr b/src/test/ui/numbers-arithmetic/overflowing-rsh-4.stderr new file mode 100644 index 0000000000000..3db1da06dbed8 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-4.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-rsh-4.rs:11:13 + | +LL | let x = 2_i8 >> 17; + | ^^^^^^^^^^ attempt to shift right with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-rsh-4.rs:7:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-5.rs b/src/test/ui/numbers-arithmetic/overflowing-rsh-5.rs new file mode 100644 index 0000000000000..88928c9959666 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-5.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _n = 1i64 >> [64][0]; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-5.stderr b/src/test/ui/numbers-arithmetic/overflowing-rsh-5.stderr new file mode 100644 index 0000000000000..bd3eae82977e3 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-5.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-rsh-5.rs:7:14 + | +LL | let _n = 1i64 >> [64][0]; + | ^^^^^^^^^^^^^^^ attempt to shift right with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-rsh-5.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-6.rs b/src/test/ui/numbers-arithmetic/overflowing-rsh-6.rs new file mode 100644 index 0000000000000..88928c9959666 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-6.rs @@ -0,0 +1,9 @@ +// build-fail +// compile-flags: -C debug-assertions + +#![deny(arithmetic_overflow, const_err)] + +fn main() { + let _n = 1i64 >> [64][0]; + //~^ ERROR: this arithmetic operation will overflow +} diff --git a/src/test/ui/numbers-arithmetic/overflowing-rsh-6.stderr b/src/test/ui/numbers-arithmetic/overflowing-rsh-6.stderr new file mode 100644 index 0000000000000..5d76639fb50f3 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/overflowing-rsh-6.stderr @@ -0,0 +1,14 @@ +error: this arithmetic operation will overflow + --> $DIR/overflowing-rsh-6.rs:7:14 + | +LL | let _n = 1i64 >> [64][0]; + | ^^^^^^^^^^^^^^^ attempt to shift right with overflow + | +note: the lint level is defined here + --> $DIR/overflowing-rsh-6.rs:4:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/run-fail/overflowing-sub.rs b/src/test/ui/numbers-arithmetic/overflowing-sub.rs similarity index 80% rename from src/test/run-fail/overflowing-sub.rs rename to src/test/ui/numbers-arithmetic/overflowing-sub.rs index fb096c31957ee..66685ac961a17 100644 --- a/src/test/run-fail/overflowing-sub.rs +++ b/src/test/ui/numbers-arithmetic/overflowing-sub.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:thread 'main' panicked at 'attempt to subtract with overflow' +// ignore-emscripten no processes // compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] diff --git a/src/test/run-fail/promoted_overflow.rs b/src/test/ui/numbers-arithmetic/promoted_overflow.rs similarity index 92% rename from src/test/run-fail/promoted_overflow.rs rename to src/test/ui/numbers-arithmetic/promoted_overflow.rs index 3c42da4b1d8ed..da59e81ed6bf7 100644 --- a/src/test/run-fail/promoted_overflow.rs +++ b/src/test/ui/numbers-arithmetic/promoted_overflow.rs @@ -1,5 +1,6 @@ #![allow(arithmetic_overflow)] +// run-fail // error-pattern: overflow // compile-flags: -C overflow-checks=yes diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs index f13964fb38665..e6d0c94a02fac 100644 --- a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs @@ -1,15 +1,22 @@ // run-pass +// compile-flags:-Zmir-opt-level=0 // Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. -// compile-flags: -Z saturating-float-casts +// +// Some of these tests come from a similar file in miri, +// tests/run-pass/float.rs. Individual test cases are potentially duplicated +// with the previously existing tests, but since this runs so quickly anyway, +// we're not spending the time to figure out exactly which ones should be +// merged. #![feature(test, stmt_expr_attributes)] +#![feature(track_caller)] #![deny(overflowing_literals)] extern crate test; use std::{f32, f64}; -use std::{u8, i8, u16, i16, u32, i32, u64, i64}; -#[cfg(not(target_os="emscripten"))] -use std::{u128, i128}; +#[cfg(not(target_os = "emscripten"))] +use std::{i128, u128}; +use std::{i16, i32, i64, i8, u16, u32, u64, u8}; use test::black_box; macro_rules! test { @@ -17,31 +24,18 @@ macro_rules! test { // black_box disables constant evaluation to test run-time conversions: assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, "run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); - ); - - ($fval:expr, f* -> $ity:ident, $ival:expr) => ( - test!($fval, f32 -> $ity, $ival); - test!($fval, f64 -> $ity, $ival); - ) -} -// This macro tests const eval in addition to run-time evaluation. -// If and when saturating casts are adopted, this macro should be merged with test!() to ensure -// that run-time and const eval agree on inputs that currently trigger a const eval error. -macro_rules! test_c { - ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ({ - test!($val, $src_ty -> $dest_ty, $expected); { const X: $src_ty = $val; const Y: $dest_ty = X as $dest_ty; assert_eq!(Y, $expected, "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); } - }); + ); ($fval:expr, f* -> $ity:ident, $ival:expr) => ( - test_c!($fval, f32 -> $ity, $ival); - test_c!($fval, f64 -> $ity, $ival); + test!($fval, f32 -> $ity, $ival); + test!($fval, f64 -> $ity, $ival); ) } @@ -55,11 +49,11 @@ macro_rules! common_fptoi_tests { // as well, the test is just slightly misplaced. test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); - test_c!(0., $fty -> $ity, 0); - test_c!($fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(0., $fty -> $ity, 0); + test!($fty::MIN_POSITIVE, $fty -> $ity, 0); test!(-0.9, $fty -> $ity, 0); - test_c!(1., $fty -> $ity, 1); - test_c!(42., $fty -> $ity, 42); + test!(1., $fty -> $ity, 1); + test!(42., $fty -> $ity, 42); )+ }); (f* -> $($ity:ident)+) => ({ @@ -85,11 +79,392 @@ macro_rules! fptoui_tests { }) } +use std::fmt::Debug; + +// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE. +#[track_caller] +#[inline(never)] +fn assert_eq(x: T, y: T) { + assert_eq!(x, y); +} + +trait FloatToInt: Copy { + fn cast(self) -> Int; + unsafe fn cast_unchecked(self) -> Int; +} + +impl FloatToInt for f32 { + fn cast(self) -> i8 { + self as _ + } + unsafe fn cast_unchecked(self) -> i8 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> i32 { + self as _ + } + unsafe fn cast_unchecked(self) -> i32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> u32 { + self as _ + } + unsafe fn cast_unchecked(self) -> u32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> i64 { + self as _ + } + unsafe fn cast_unchecked(self) -> i64 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> u64 { + self as _ + } + unsafe fn cast_unchecked(self) -> u64 { + self.to_int_unchecked() + } +} + +impl FloatToInt for f64 { + fn cast(self) -> i8 { + self as _ + } + unsafe fn cast_unchecked(self) -> i8 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> i32 { + self as _ + } + unsafe fn cast_unchecked(self) -> i32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> u32 { + self as _ + } + unsafe fn cast_unchecked(self) -> u32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> i64 { + self as _ + } + unsafe fn cast_unchecked(self) -> i64 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> u64 { + self as _ + } + unsafe fn cast_unchecked(self) -> u64 { + self.to_int_unchecked() + } +} +// FIXME emscripten does not support i128 +#[cfg(not(target_os = "emscripten"))] +impl FloatToInt for f64 { + fn cast(self) -> i128 { + self as _ + } + unsafe fn cast_unchecked(self) -> i128 { + self.to_int_unchecked() + } +} +// FIXME emscripten does not support i128 +#[cfg(not(target_os = "emscripten"))] +impl FloatToInt for f64 { + fn cast(self) -> u128 { + self as _ + } + unsafe fn cast_unchecked(self) -> u128 { + self.to_int_unchecked() + } +} + +/// Test this cast both via `as` and via `to_int_unchecked` (i.e., it must not saturate). +#[track_caller] +#[inline(never)] +fn test_both_cast(x: F, y: I) +where + F: FloatToInt, + I: PartialEq + Debug, +{ + assert_eq!(x.cast(), y); + assert_eq!(unsafe { x.cast_unchecked() }, y); +} + +fn casts() { + // f32 -> i8 + test_both_cast::(127.99, 127); + test_both_cast::(-128.99, -128); + + // f32 -> i32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x1p-149*/ f32::from_bits(0x00000001), 0); + test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_both_cast::(/*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), -1); + test_both_cast::(1.9, 1); + test_both_cast::(-1.9, -1); + test_both_cast::(5.0, 5); + test_both_cast::(-5.0, -5); + test_both_cast::(2147483520.0, 2147483520); + test_both_cast::(-2147483648.0, -2147483648); + // unrepresentable casts + assert_eq::(2147483648.0f32 as i32, i32::MAX); + assert_eq::(-2147483904.0f32 as i32, i32::MIN); + assert_eq::(f32::MAX as i32, i32::MAX); + assert_eq::(f32::MIN as i32, i32::MIN); + assert_eq::(f32::INFINITY as i32, i32::MAX); + assert_eq::(f32::NEG_INFINITY as i32, i32::MIN); + assert_eq::(f32::NAN as i32, 0); + assert_eq::((-f32::NAN) as i32, 0); + + // f32 -> u32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(-0.9999999, 0); + test_both_cast::(/*0x1p-149*/ f32::from_bits(0x1), 0); + test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_both_cast::(1.9, 1); + test_both_cast::(5.0, 5); + test_both_cast::(2147483648.0, 0x8000_0000); + test_both_cast::(4294967040.0, 0u32.wrapping_sub(256)); + test_both_cast::(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666), 0); + test_both_cast::(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff), 0); + test_both_cast::((u32::MAX - 128) as f32, u32::MAX - 255); // rounding loss + + // unrepresentable casts: + + // rounds up and then becomes unrepresentable + assert_eq::((u32::MAX - 127) as f32 as u32, u32::MAX); + + assert_eq::(4294967296.0f32 as u32, u32::MAX); + assert_eq::(-5.0f32 as u32, 0); + assert_eq::(f32::MAX as u32, u32::MAX); + assert_eq::(f32::MIN as u32, 0); + assert_eq::(f32::INFINITY as u32, u32::MAX); + assert_eq::(f32::NEG_INFINITY as u32, 0); + assert_eq::(f32::NAN as u32, 0); + assert_eq::((-f32::NAN) as u32, 0); + + // f32 -> i64 + test_both_cast::(4294967296.0, 4294967296); + test_both_cast::(-4294967296.0, -4294967296); + test_both_cast::(9223371487098961920.0, 9223371487098961920); + test_both_cast::(-9223372036854775808.0, -9223372036854775808); + + // f64 -> i8 + test_both_cast::(127.99, 127); + test_both_cast::(-128.99, -128); + + // f64 -> i32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_both_cast::( + /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), + -1, + ); + test_both_cast::(1.9, 1); + test_both_cast::(-1.9, -1); + test_both_cast::(1e8, 100_000_000); + test_both_cast::(2147483647.0, 2147483647); + test_both_cast::(-2147483648.0, -2147483648); + // unrepresentable casts + assert_eq::(2147483648.0f64 as i32, i32::MAX); + assert_eq::(-2147483649.0f64 as i32, i32::MIN); + + // f64 -> i64 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1), 0); + test_both_cast::( + /*-0x0.0000000000001p-1022*/ f64::from_bits(0x8000000000000001), + 0, + ); + test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_both_cast::( + /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), + -1, + ); + test_both_cast::(5.0, 5); + test_both_cast::(5.9, 5); + test_both_cast::(-5.0, -5); + test_both_cast::(-5.9, -5); + test_both_cast::(4294967296.0, 4294967296); + test_both_cast::(-4294967296.0, -4294967296); + test_both_cast::(9223372036854774784.0, 9223372036854774784); + test_both_cast::(-9223372036854775808.0, -9223372036854775808); + // unrepresentable casts + assert_eq::(9223372036854775808.0f64 as i64, i64::MAX); + assert_eq::(-9223372036854777856.0f64 as i64, i64::MIN); + assert_eq::(f64::MAX as i64, i64::MAX); + assert_eq::(f64::MIN as i64, i64::MIN); + assert_eq::(f64::INFINITY as i64, i64::MAX); + assert_eq::(f64::NEG_INFINITY as i64, i64::MIN); + assert_eq::(f64::NAN as i64, 0); + assert_eq::((-f64::NAN) as i64, 0); + + // f64 -> u64 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(-0.99999999999, 0); + test_both_cast::(5.0, 5); + test_both_cast::(1e16, 10000000000000000); + test_both_cast::((u64::MAX - 1024) as f64, u64::MAX - 2047); // rounding loss + test_both_cast::(9223372036854775808.0, 9223372036854775808); + // unrepresentable casts + assert_eq::(-5.0f64 as u64, 0); + // rounds up and then becomes unrepresentable + assert_eq::((u64::MAX - 1023) as f64 as u64, u64::MAX); + assert_eq::(18446744073709551616.0f64 as u64, u64::MAX); + assert_eq::(f64::MAX as u64, u64::MAX); + assert_eq::(f64::MIN as u64, 0); + assert_eq::(f64::INFINITY as u64, u64::MAX); + assert_eq::(f64::NEG_INFINITY as u64, 0); + assert_eq::(f64::NAN as u64, 0); + assert_eq::((-f64::NAN) as u64, 0); + + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + // f64 -> i128 + assert_eq::(f64::MAX as i128, i128::MAX); + assert_eq::(f64::MIN as i128, i128::MIN); + + // f64 -> u128 + assert_eq::(f64::MAX as u128, u128::MAX); + assert_eq::(f64::MIN as u128, 0); + } + + // int -> f32 + assert_eq::(127i8 as f32, 127.0); + assert_eq::(2147483647i32 as f32, 2147483648.0); + assert_eq::((-2147483648i32) as f32, -2147483648.0); + assert_eq::(1234567890i32 as f32, /*0x1.26580cp+30*/ f32::from_bits(0x4e932c06)); + assert_eq::(16777217i32 as f32, 16777216.0); + assert_eq::((-16777217i32) as f32, -16777216.0); + assert_eq::(16777219i32 as f32, 16777220.0); + assert_eq::((-16777219i32) as f32, -16777220.0); + assert_eq::( + 0x7fffff4000000001i64 as f32, + /*0x1.fffffep+62*/ f32::from_bits(0x5effffff), + ); + assert_eq::( + 0x8000004000000001u64 as i64 as f32, + /*-0x1.fffffep+62*/ f32::from_bits(0xdeffffff), + ); + assert_eq::( + 0x0020000020000001i64 as f32, + /*0x1.000002p+53*/ f32::from_bits(0x5a000001), + ); + assert_eq::( + 0xffdfffffdfffffffu64 as i64 as f32, + /*-0x1.000002p+53*/ f32::from_bits(0xda000001), + ); + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + assert_eq::(i128::MIN as f32, -170141183460469231731687303715884105728.0f32); + assert_eq::(u128::MAX as f32, f32::INFINITY); // saturation + } + + // int -> f64 + assert_eq::(127i8 as f64, 127.0); + assert_eq::(i16::MIN as f64, -32768.0f64); + assert_eq::(2147483647i32 as f64, 2147483647.0); + assert_eq::(-2147483648i32 as f64, -2147483648.0); + assert_eq::(987654321i32 as f64, 987654321.0); + assert_eq::(9223372036854775807i64 as f64, 9223372036854775807.0); + assert_eq::(-9223372036854775808i64 as f64, -9223372036854775808.0); + assert_eq::(4669201609102990i64 as f64, 4669201609102990.0); // Feigenbaum (?) + assert_eq::(9007199254740993i64 as f64, 9007199254740992.0); + assert_eq::(-9007199254740993i64 as f64, -9007199254740992.0); + assert_eq::(9007199254740995i64 as f64, 9007199254740996.0); + assert_eq::(-9007199254740995i64 as f64, -9007199254740996.0); + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + // even that fits... + assert_eq::(u128::MAX as f64, 340282366920938463463374607431768211455.0f64); + } + + // f32 -> f64 + assert_eq::((0.0f32 as f64).to_bits(), 0.0f64.to_bits()); + assert_eq::(((-0.0f32) as f64).to_bits(), (-0.0f64).to_bits()); + assert_eq::(5.0f32 as f64, 5.0f64); + assert_eq::( + /*0x1p-149*/ f32::from_bits(0x1) as f64, + /*0x1p-149*/ f64::from_bits(0x36a0000000000000), + ); + assert_eq::( + /*-0x1p-149*/ f32::from_bits(0x80000001) as f64, + /*-0x1p-149*/ f64::from_bits(0xb6a0000000000000), + ); + assert_eq::( + /*0x1.fffffep+127*/ f32::from_bits(0x7f7fffff) as f64, + /*0x1.fffffep+127*/ f64::from_bits(0x47efffffe0000000), + ); + assert_eq::( + /*-0x1.fffffep+127*/ (-f32::from_bits(0x7f7fffff)) as f64, + /*-0x1.fffffep+127*/ -f64::from_bits(0x47efffffe0000000), + ); + assert_eq::( + /*0x1p-119*/ f32::from_bits(0x4000000) as f64, + /*0x1p-119*/ f64::from_bits(0x3880000000000000), + ); + assert_eq::( + /*0x1.8f867ep+125*/ f32::from_bits(0x7e47c33f) as f64, + 6.6382536710104395e+37, + ); + assert_eq::(f32::INFINITY as f64, f64::INFINITY); + assert_eq::(f32::NEG_INFINITY as f64, f64::NEG_INFINITY); + + // f64 -> f32 + assert_eq::((0.0f64 as f32).to_bits(), 0.0f32.to_bits()); + assert_eq::(((-0.0f64) as f32).to_bits(), (-0.0f32).to_bits()); + assert_eq::(5.0f64 as f32, 5.0f32); + assert_eq::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1) as f32, 0.0); + assert_eq::(/*-0x0.0000000000001p-1022*/ (-f64::from_bits(0x1)) as f32, -0.0); + assert_eq::( + /*0x1.fffffe0000000p-127*/ f64::from_bits(0x380fffffe0000000) as f32, + /*0x1p-149*/ f32::from_bits(0x800000), + ); + assert_eq::( + /*0x1.4eae4f7024c7p+108*/ f64::from_bits(0x46b4eae4f7024c70) as f32, + /*0x1.4eae5p+108*/ f32::from_bits(0x75a75728), + ); + assert_eq::(f64::MAX as f32, f32::INFINITY); + assert_eq::(f64::MIN as f32, f32::NEG_INFINITY); + assert_eq::(f64::INFINITY as f32, f32::INFINITY); + assert_eq::(f64::NEG_INFINITY as f32, f32::NEG_INFINITY); +} + pub fn main() { + casts(); // from miri's tests + common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); fptoui_tests!(f* -> u8 u16 u32 u64); // FIXME emscripten does not support i128 - #[cfg(not(target_os="emscripten"))] { + #[cfg(not(target_os = "emscripten"))] + { common_fptoi_tests!(f* -> i128 u128); fptoui_tests!(f* -> u128); } @@ -97,39 +472,39 @@ pub fn main() { // The following tests cover edge cases for some integer types. // # u8 - test_c!(254., f* -> u8, 254); + test!(254., f* -> u8, 254); test!(256., f* -> u8, 255); // # i8 - test_c!(-127., f* -> i8, -127); + test!(-127., f* -> i8, -127); test!(-129., f* -> i8, -128); - test_c!(126., f* -> i8, 126); + test!(126., f* -> i8, 126); test!(128., f* -> i8, 127); // # i32 // -2147483648. is i32::MIN (exactly) - test_c!(-2147483648., f* -> i32, i32::MIN); + test!(-2147483648., f* -> i32, i32::MIN); // 2147483648. is i32::MAX rounded up test!(2147483648., f32 -> i32, 2147483647); // With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to // multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: - test_c!(2147483520., f32 -> i32, 2147483520); + test!(2147483520., f32 -> i32, 2147483520); // Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 test!(-2147483904., f* -> i32, i32::MIN); - test_c!(-2147483520., f* -> i32, -2147483520); + test!(-2147483520., f* -> i32, -2147483520); // # u32 // round(MAX) and nextUp(round(MAX)) - test_c!(4294967040., f* -> u32, 4294967040); + test!(4294967040., f* -> u32, 4294967040); test!(4294967296., f* -> u32, 4294967295); // # u128 - #[cfg(not(target_os="emscripten"))] + #[cfg(not(target_os = "emscripten"))] { // float->int: - test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); + test!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); // nextDown(f32::MAX) = 2^128 - 2 * 2^104 const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; - test_c!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); + test!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); } } diff --git a/src/test/ui/numeric/numeric-cast-2.stderr b/src/test/ui/numeric/numeric-cast-2.stderr index 465b507b788fd..3f900062cbb6e 100644 --- a/src/test/ui/numeric/numeric-cast-2.stderr +++ b/src/test/ui/numeric/numeric-cast-2.stderr @@ -15,27 +15,21 @@ error[E0308]: mismatched types --> $DIR/numeric-cast-2.rs:7:18 | LL | let y: i64 = x + x; - | --- ^^^^^ expected `i64`, found `u16` - | | + | --- ^^^^^ + | | | + | | expected `i64`, found `u16` + | | help: you can convert an `u16` to `i64`: `(x + x).into()` | expected due to this - | -help: you can convert an `u16` to `i64` and panic if the converted value wouldn't fit - | -LL | let y: i64 = (x + x).try_into().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types --> $DIR/numeric-cast-2.rs:9:18 | LL | let z: i32 = x + x; - | --- ^^^^^ expected `i32`, found `u16` - | | + | --- ^^^^^ + | | | + | | expected `i32`, found `u16` + | | help: you can convert an `u16` to `i32`: `(x + x).into()` | expected due to this - | -help: you can convert an `u16` to `i32` and panic if the converted value wouldn't fit - | -LL | let z: i32 = (x + x).try_into().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/numeric/numeric-cast.fixed b/src/test/ui/numeric/numeric-cast.fixed index 6f78228a85d44..cf0560a107772 100644 --- a/src/test/ui/numeric/numeric-cast.fixed +++ b/src/test/ui/numeric/numeric-cast.fixed @@ -24,9 +24,9 @@ fn main() { //~^ ERROR mismatched types foo::(x_u32.try_into().unwrap()); //~^ ERROR mismatched types - foo::(x_u16.try_into().unwrap()); + foo::(x_u16.into()); //~^ ERROR mismatched types - foo::(x_u8.try_into().unwrap()); + foo::(x_u8.into()); //~^ ERROR mismatched types foo::(x_isize.try_into().unwrap()); //~^ ERROR mismatched types @@ -49,16 +49,16 @@ fn main() { //~^ ERROR mismatched types foo::(x_u16.try_into().unwrap()); //~^ ERROR mismatched types - foo::(x_u8.try_into().unwrap()); + foo::(x_u8.into()); //~^ ERROR mismatched types foo::(x_isize); foo::(x_i64.try_into().unwrap()); //~^ ERROR mismatched types foo::(x_i32.try_into().unwrap()); //~^ ERROR mismatched types - foo::(x_i16.try_into().unwrap()); + foo::(x_i16.into()); //~^ ERROR mismatched types - foo::(x_i8.try_into().unwrap()); + foo::(x_i8.into()); //~^ ERROR mismatched types // foo::(x_f64); // foo::(x_f32); @@ -89,11 +89,11 @@ fn main() { //~^ ERROR mismatched types foo::(x_u64.try_into().unwrap()); //~^ ERROR mismatched types - foo::(x_u32.try_into().unwrap()); + foo::(x_u32.into()); //~^ ERROR mismatched types - foo::(x_u16.try_into().unwrap()); + foo::(x_u16.into()); //~^ ERROR mismatched types - foo::(x_u8.try_into().unwrap()); + foo::(x_u8.into()); //~^ ERROR mismatched types foo::(x_isize.try_into().unwrap()); //~^ ERROR mismatched types @@ -135,9 +135,9 @@ fn main() { //~^ ERROR mismatched types foo::(x_u32.try_into().unwrap()); //~^ ERROR mismatched types - foo::(x_u16.try_into().unwrap()); + foo::(x_u16.into()); //~^ ERROR mismatched types - foo::(x_u8.try_into().unwrap()); + foo::(x_u8.into()); //~^ ERROR mismatched types foo::(x_isize.try_into().unwrap()); //~^ ERROR mismatched types @@ -181,7 +181,7 @@ fn main() { //~^ ERROR mismatched types foo::(x_u16.try_into().unwrap()); //~^ ERROR mismatched types - foo::(x_u8.try_into().unwrap()); + foo::(x_u8.into()); //~^ ERROR mismatched types foo::(x_isize.try_into().unwrap()); //~^ ERROR mismatched types diff --git a/src/test/ui/numeric/numeric-cast.stderr b/src/test/ui/numeric/numeric-cast.stderr index eef40cbdbe4e8..cc1aa72d21451 100644 --- a/src/test/ui/numeric/numeric-cast.stderr +++ b/src/test/ui/numeric/numeric-cast.stderr @@ -24,23 +24,19 @@ error[E0308]: mismatched types --> $DIR/numeric-cast.rs:27:18 | LL | foo::(x_u16); - | ^^^^^ expected `usize`, found `u16` - | -help: you can convert an `u16` to `usize` and panic if the converted value wouldn't fit - | -LL | foo::(x_u16.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ + | | + | expected `usize`, found `u16` + | help: you can convert an `u16` to `usize`: `x_u16.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:29:18 | LL | foo::(x_u8); - | ^^^^ expected `usize`, found `u8` - | -help: you can convert an `u8` to `usize` and panic if the converted value wouldn't fit - | -LL | foo::(x_u8.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ + | | + | expected `usize`, found `u8` + | help: you can convert an `u8` to `usize`: `x_u8.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:31:18 @@ -145,12 +141,10 @@ error[E0308]: mismatched types --> $DIR/numeric-cast.rs:52:18 | LL | foo::(x_u8); - | ^^^^ expected `isize`, found `u8` - | -help: you can convert an `u8` to `isize` and panic if the converted value wouldn't fit - | -LL | foo::(x_u8.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ + | | + | expected `isize`, found `u8` + | help: you can convert an `u8` to `isize`: `x_u8.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:55:18 @@ -178,23 +172,19 @@ error[E0308]: mismatched types --> $DIR/numeric-cast.rs:59:18 | LL | foo::(x_i16); - | ^^^^^ expected `isize`, found `i16` - | -help: you can convert an `i16` to `isize` and panic if the converted value wouldn't fit - | -LL | foo::(x_i16.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ + | | + | expected `isize`, found `i16` + | help: you can convert an `i16` to `isize`: `x_i16.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:61:18 | LL | foo::(x_i8); - | ^^^^ expected `isize`, found `i8` - | -help: you can convert an `i8` to `isize` and panic if the converted value wouldn't fit - | -LL | foo::(x_i8.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ + | | + | expected `isize`, found `i8` + | help: you can convert an `i8` to `isize`: `x_i8.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:66:16 @@ -315,34 +305,28 @@ error[E0308]: mismatched types --> $DIR/numeric-cast.rs:92:16 | LL | foo::(x_u32); - | ^^^^^ expected `i64`, found `u32` - | -help: you can convert an `u32` to `i64` and panic if the converted value wouldn't fit - | -LL | foo::(x_u32.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ + | | + | expected `i64`, found `u32` + | help: you can convert an `u32` to `i64`: `x_u32.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:94:16 | LL | foo::(x_u16); - | ^^^^^ expected `i64`, found `u16` - | -help: you can convert an `u16` to `i64` and panic if the converted value wouldn't fit - | -LL | foo::(x_u16.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ + | | + | expected `i64`, found `u16` + | help: you can convert an `u16` to `i64`: `x_u16.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:96:16 | LL | foo::(x_u8); - | ^^^^ expected `i64`, found `u8` - | -help: you can convert an `u8` to `i64` and panic if the converted value wouldn't fit - | -LL | foo::(x_u8.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ + | | + | expected `i64`, found `u8` + | help: you can convert an `u8` to `i64`: `x_u8.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:98:16 @@ -514,23 +498,19 @@ error[E0308]: mismatched types --> $DIR/numeric-cast.rs:138:16 | LL | foo::(x_u16); - | ^^^^^ expected `i32`, found `u16` - | -help: you can convert an `u16` to `i32` and panic if the converted value wouldn't fit - | -LL | foo::(x_u16.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ + | | + | expected `i32`, found `u16` + | help: you can convert an `u16` to `i32`: `x_u16.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:140:16 | LL | foo::(x_u8); - | ^^^^ expected `i32`, found `u8` - | -help: you can convert an `u8` to `i32` and panic if the converted value wouldn't fit - | -LL | foo::(x_u8.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ + | | + | expected `i32`, found `u8` + | help: you can convert an `u8` to `i32`: `x_u8.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:142:16 @@ -717,12 +697,10 @@ error[E0308]: mismatched types --> $DIR/numeric-cast.rs:184:16 | LL | foo::(x_u8); - | ^^^^ expected `i16`, found `u8` - | -help: you can convert an `u8` to `i16` and panic if the converted value wouldn't fit - | -LL | foo::(x_u8.try_into().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ + | | + | expected `i16`, found `u8` + | help: you can convert an `u8` to `i16`: `x_u8.into()` error[E0308]: mismatched types --> $DIR/numeric-cast.rs:186:16 diff --git a/src/test/ui/object-does-not-impl-trait.stderr b/src/test/ui/object-does-not-impl-trait.stderr index 7ac199d094383..1d3675bf1c1f6 100644 --- a/src/test/ui/object-does-not-impl-trait.stderr +++ b/src/test/ui/object-does-not-impl-trait.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `std::boxed::Box: Foo` is not satisfied --> $DIR/object-does-not-impl-trait.rs:6:44 | LL | fn take_foo(f: F) {} - | -------- --- required by this bound in `take_foo` + | --- required by this bound in `take_foo` LL | fn take_object(f: Box) { take_foo(f); } | ^ the trait `Foo` is not implemented for `std::boxed::Box` diff --git a/src/test/ui/object-lifetime/object-lifetime-default-ambiguous.stderr b/src/test/ui/object-lifetime/object-lifetime-default-ambiguous.stderr index 0c3dbffeea638..bd50a27fd5e58 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-ambiguous.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-ambiguous.stderr @@ -18,3 +18,4 @@ LL | fn f(t: &Ref2) { error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0228`. diff --git a/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr b/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr index 9dbf7a78ed7a7..f06a9da1deea5 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr @@ -6,3 +6,4 @@ LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } error: aborting due to previous error +For more information about this error, try `rustc --explain E0228`. diff --git a/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr b/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr index d069f52ce47db..51d8450af768b 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr @@ -6,3 +6,4 @@ LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } error: aborting due to previous error +For more information about this error, try `rustc --explain E0228`. diff --git a/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr b/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr index 9c7b6b98f2e36..f721bf39419b5 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr @@ -6,3 +6,4 @@ LL | fn bar(x: &str) -> &dyn Foo { &() } error: aborting due to previous error +For more information about this error, try `rustc --explain E0228`. diff --git a/src/test/ui/on-unimplemented/enclosing-scope.stderr b/src/test/ui/on-unimplemented/enclosing-scope.stderr index 092e560330b4c..4c1aaf39c7b34 100644 --- a/src/test/ui/on-unimplemented/enclosing-scope.stderr +++ b/src/test/ui/on-unimplemented/enclosing-scope.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `Foo: Trait` is not satisfied --> $DIR/enclosing-scope.rs:14:11 | LL | fn f(x: T) {} - | - ----- required by this bound in `f` + | ----- required by this bound in `f` ... LL | let x = || { | _____________- @@ -18,7 +18,7 @@ error[E0277]: the trait bound `Foo: Trait` is not satisfied --> $DIR/enclosing-scope.rs:16:15 | LL | fn f(x: T) {} - | - ----- required by this bound in `f` + | ----- required by this bound in `f` ... LL | let y = || { | _________________- @@ -31,7 +31,7 @@ error[E0277]: the trait bound `Foo: Trait` is not satisfied --> $DIR/enclosing-scope.rs:22:15 | LL | fn f(x: T) {} - | - ----- required by this bound in `f` + | ----- required by this bound in `f` LL | LL | / fn main() { LL | | let x = || { @@ -49,7 +49,7 @@ error[E0277]: the trait bound `Foo: Trait` is not satisfied --> $DIR/enclosing-scope.rs:26:7 | LL | fn f(x: T) {} - | - ----- required by this bound in `f` + | ----- required by this bound in `f` LL | LL | / fn main() { LL | | let x = || { diff --git a/src/test/ui/on-unimplemented/on-trait.stderr b/src/test/ui/on-unimplemented/on-trait.stderr index 8fe7ed4a20443..be8efbf2ce4f6 100644 --- a/src/test/ui/on-unimplemented/on-trait.stderr +++ b/src/test/ui/on-unimplemented/on-trait.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `std::option::Option>: MyFromIte --> $DIR/on-trait.rs:28:30 | LL | fn collect, B: MyFromIterator>(it: I) -> B { - | ------- ----------------- required by this bound in `collect` + | ----------------- required by this bound in `collect` ... LL | let y: Option> = collect(x.iter()); // this should give approximately the same error for x.iter().collect() | ^^^^^^^ a collection of type `std::option::Option>` cannot be built from an iterator over elements of type `&u8` @@ -13,7 +13,7 @@ error[E0277]: the trait bound `std::string::String: Bar::Foo` is not --> $DIR/on-trait.rs:31:21 | LL | fn foobar>() -> T { - | ------ --------------- required by this bound in `foobar` + | --------------- required by this bound in `foobar` ... LL | let x: String = foobar(); | ^^^^^^ test error `std::string::String` with `u8` `_` `u32` in `Bar::Foo` diff --git a/src/test/ui/once-cant-call-twice-on-heap.stderr b/src/test/ui/once-cant-call-twice-on-heap.stderr index d4884469ce4d2..7133a32431a67 100644 --- a/src/test/ui/once-cant-call-twice-on-heap.stderr +++ b/src/test/ui/once-cant-call-twice-on-heap.stderr @@ -8,11 +8,10 @@ LL | blk(); LL | blk(); | ^^^ value used here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/once-cant-call-twice-on-heap.rs:7:10 +help: consider further restricting this bound | -LL | fn foo(blk: F) { - | ^^^^^^^^ +LL | fn foo(blk: F) { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/optimization-fuel-0.rs b/src/test/ui/optimization-fuel-0.rs index f86972b734826..a97c5750f94c3 100644 --- a/src/test/ui/optimization-fuel-0.rs +++ b/src/test/ui/optimization-fuel-0.rs @@ -4,8 +4,7 @@ use std::mem::size_of; -// (#55495: The --error-format is to sidestep an issue in our test harness) -// compile-flags: --error-format human -Z fuel=foo=0 +// compile-flags: -Z fuel=foo=0 struct S1(u8, u16, u8); struct S2(u8, u16, u8); diff --git a/src/test/ui/optimization-fuel-0.stderr b/src/test/ui/optimization-fuel-0.stderr index 3ad405b2b50ff..f0e2ebfc37a37 100644 --- a/src/test/ui/optimization-fuel-0.stderr +++ b/src/test/ui/optimization-fuel-0.stderr @@ -1 +1,4 @@ -optimization-fuel-exhausted: Reorder fields of "S1" +warning: optimization-fuel-exhausted: Reorder fields of "S1" + +warning: 1 warning emitted + diff --git a/src/test/ui/optimization-fuel-1.rs b/src/test/ui/optimization-fuel-1.rs index 98283066361c2..a09f91c68abe7 100644 --- a/src/test/ui/optimization-fuel-1.rs +++ b/src/test/ui/optimization-fuel-1.rs @@ -4,8 +4,7 @@ use std::mem::size_of; -// (#55495: The --error-format is to sidestep an issue in our test harness) -// compile-flags: --error-format human -Z fuel=foo=1 +// compile-flags: -Z fuel=foo=1 struct S1(u8, u16, u8); struct S2(u8, u16, u8); diff --git a/src/test/ui/optimization-fuel-1.stderr b/src/test/ui/optimization-fuel-1.stderr index 197e45219c3f8..53eafb05830cb 100644 --- a/src/test/ui/optimization-fuel-1.stderr +++ b/src/test/ui/optimization-fuel-1.stderr @@ -1 +1,4 @@ -optimization-fuel-exhausted: Reorder fields of "S2" +warning: optimization-fuel-exhausted: Reorder fields of "S2" + +warning: 1 warning emitted + diff --git a/src/test/ui/option-to-result.rs b/src/test/ui/option-to-result.rs new file mode 100644 index 0000000000000..00e8b5244c54a --- /dev/null +++ b/src/test/ui/option-to-result.rs @@ -0,0 +1,13 @@ +fn main(){ } + +fn test_result() -> Result<(),()> { + let a:Option<()> = Some(()); + a?;//~ ERROR `?` couldn't convert the error + Ok(()) +} + +fn test_option() -> Option{ + let a:Result = Ok(5); + a?;//~ ERROR `?` couldn't convert the error + Some(5) +} diff --git a/src/test/ui/option-to-result.stderr b/src/test/ui/option-to-result.stderr new file mode 100644 index 0000000000000..5fa06778389d9 --- /dev/null +++ b/src/test/ui/option-to-result.stderr @@ -0,0 +1,35 @@ +error[E0277]: `?` couldn't convert the error to `()` + --> $DIR/option-to-result.rs:5:6 + | +LL | fn test_result() -> Result<(),()> { + | ------------- expected `()` because of this +LL | let a:Option<()> = Some(()); +LL | a?; + | ^ the trait `std::convert::From` is not implemented for `()` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = note: required by `std::convert::From::from` +help: consider converting the `Option` into a `Result` using `Option::ok_or` or `Option::ok_or_else` + | +LL | a.ok_or_else(|| /* error value */)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `?` couldn't convert the error to `std::option::NoneError` + --> $DIR/option-to-result.rs:11:6 + | +LL | fn test_option() -> Option{ + | ----------- expected `std::option::NoneError` because of this +LL | let a:Result = Ok(5); +LL | a?; + | ^ the trait `std::convert::From` is not implemented for `std::option::NoneError` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = note: required by `std::convert::From::from` +help: consider converting the `Result` into an `Option` using `Result::ok` + | +LL | a.ok()?; + | ^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr index 3ba26de10d3d5..b45e947f3ea37 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr @@ -5,6 +5,7 @@ LL | match (0u8, 0u8) { | ^^^^^^^^^^ pattern `(2u8..=std::u8::MAX, _)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(u8, u8)` error[E0004]: non-exhaustive patterns: `((4u8..=std::u8::MAX))` not covered --> $DIR/exhaustiveness-non-exhaustive.rs:10:11 @@ -13,6 +14,7 @@ LL | match ((0u8,),) { | ^^^^^^^^^ pattern `((4u8..=std::u8::MAX))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `((u8,),)` error[E0004]: non-exhaustive patterns: `(Some(2u8..=std::u8::MAX))` not covered --> $DIR/exhaustiveness-non-exhaustive.rs:14:11 @@ -21,6 +23,7 @@ LL | match (Some(0u8),) { | ^^^^^^^^^^^^ pattern `(Some(2u8..=std::u8::MAX))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(std::option::Option,)` error: aborting due to 3 previous errors diff --git a/src/test/ui/or-patterns/feature-gate-const-fn.rs b/src/test/ui/or-patterns/feature-gate-const-fn.rs index 2ef5537db60ad..d21cf3dc72c99 100644 --- a/src/test/ui/or-patterns/feature-gate-const-fn.rs +++ b/src/test/ui/or-patterns/feature-gate-const-fn.rs @@ -30,8 +30,6 @@ fn main() { let x = Ok(3); let Ok(y) | Err(y) = x; //~^ ERROR or-pattern is not allowed in a `const` - //~| ERROR constant contains unimplemented expression type - //~| ERROR constant contains unimplemented expression type 2 }]; } diff --git a/src/test/ui/or-patterns/feature-gate-const-fn.stderr b/src/test/ui/or-patterns/feature-gate-const-fn.stderr index 38233a944cfac..345d6c7098112 100644 --- a/src/test/ui/or-patterns/feature-gate-const-fn.stderr +++ b/src/test/ui/or-patterns/feature-gate-const-fn.stderr @@ -52,19 +52,6 @@ LL | let Ok(y) | Err(y) = x; = note: see issue #49146 for more information = help: add `#![feature(const_if_match)]` to the crate attributes to enable -error[E0019]: constant contains unimplemented expression type - --> $DIR/feature-gate-const-fn.rs:31:25 - | -LL | let Ok(y) | Err(y) = x; - | ^ - -error[E0019]: constant contains unimplemented expression type - --> $DIR/feature-gate-const-fn.rs:31:16 - | -LL | let Ok(y) | Err(y) = x; - | ^ - -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0019, E0658. -For more information about an error, try `rustc --explain E0019`. +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr index 58286e87869a4..351700a6aa529 100644 --- a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -6,6 +6,7 @@ LL | let 0 | (1 | 2) = 0; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let 0 | (1 | 2) = 0 { /* */ } @@ -18,6 +19,7 @@ LL | match 0 { | ^ patterns `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error: aborting due to 2 previous errors diff --git a/src/test/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs b/src/test/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs new file mode 100644 index 0000000000000..eb6706e50006f --- /dev/null +++ b/src/test/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs @@ -0,0 +1,22 @@ +// check-pass + +#![deny(unreachable_patterns)] + +#![feature(or_patterns)] +fn main() { + match (3,42) { + (a,_) | (_,a) if a > 10 => {println!("{}", a)} + _ => () + } + + match Some((3,42)) { + Some((a, _)) | Some((_, a)) if a > 10 => {println!("{}", a)} + _ => () + + } + + match Some((3,42)) { + Some((a, _) | (_, a)) if a > 10 => {println!("{}", a)} + _ => () + } +} diff --git a/src/test/ui/or-patterns/mismatched-bindings-async-fn.rs b/src/test/ui/or-patterns/mismatched-bindings-async-fn.rs new file mode 100644 index 0000000000000..5c5c68f81d1f2 --- /dev/null +++ b/src/test/ui/or-patterns/mismatched-bindings-async-fn.rs @@ -0,0 +1,16 @@ +// Regression test for #71297 +// edition:2018 + +#![feature(or_patterns)] + +async fn a((x | s): String) {} +//~^ ERROR variable `x` is not bound in all patterns +//~| ERROR variable `s` is not bound in all patterns + +async fn b() { + let x | s = String::new(); + //~^ ERROR variable `x` is not bound in all patterns + //~| ERROR variable `s` is not bound in all patterns +} + +fn main() {} diff --git a/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr b/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr new file mode 100644 index 0000000000000..b9c742664110e --- /dev/null +++ b/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr @@ -0,0 +1,35 @@ +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/mismatched-bindings-async-fn.rs:6:17 + | +LL | async fn a((x | s): String) {} + | - ^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0408]: variable `s` is not bound in all patterns + --> $DIR/mismatched-bindings-async-fn.rs:6:13 + | +LL | async fn a((x | s): String) {} + | ^ - variable not in all patterns + | | + | pattern doesn't bind `s` + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/mismatched-bindings-async-fn.rs:11:13 + | +LL | let x | s = String::new(); + | - ^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0408]: variable `s` is not bound in all patterns + --> $DIR/mismatched-bindings-async-fn.rs:11:9 + | +LL | let x | s = String::new(); + | ^ - variable not in all patterns + | | + | pattern doesn't bind `s` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0408`. diff --git a/src/test/ui/out-of-stack.rs b/src/test/ui/out-of-stack.rs index 5e9265be4b982..d04b0c1a6303e 100644 --- a/src/test/ui/out-of-stack.rs +++ b/src/test/ui/out-of-stack.rs @@ -8,7 +8,7 @@ // ignore-emscripten no processes // ignore-sgx no processes -#![feature(asm)] +#![feature(llvm_asm)] #![feature(rustc_private)] #[cfg(unix)] @@ -22,7 +22,7 @@ use std::thread; // Inlining to avoid llvm turning the recursive functions into tail calls, // which doesn't consume stack. #[inline(always)] -pub fn black_box(dummy: T) { unsafe { asm!("" : : "r"(&dummy)) } } +pub fn black_box(dummy: T) { unsafe { llvm_asm!("" : : "r"(&dummy)) } } fn silent_recurse() { let buf = [0u8; 1000]; diff --git a/src/test/ui/panic-runtime/unwind-interleaved.rs b/src/test/ui/panic-runtime/unwind-interleaved.rs new file mode 100644 index 0000000000000..a8b3f3493097c --- /dev/null +++ b/src/test/ui/panic-runtime/unwind-interleaved.rs @@ -0,0 +1,16 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +fn a() {} + +fn b() { + panic!(); +} + +fn main() { + let _x = vec![0]; + a(); + let _y = vec![0]; + b(); +} diff --git a/src/test/ui/panic-runtime/unwind-rec.rs b/src/test/ui/panic-runtime/unwind-rec.rs new file mode 100644 index 0000000000000..a9b7ee8ec7d4a --- /dev/null +++ b/src/test/ui/panic-runtime/unwind-rec.rs @@ -0,0 +1,15 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +fn build() -> Vec { + panic!(); +} + +struct Blk { + node: Vec, +} + +fn main() { + let _blk = Blk { node: build() }; +} diff --git a/src/test/run-fail/unwind-rec2.rs b/src/test/ui/panic-runtime/unwind-rec2.rs similarity index 76% rename from src/test/run-fail/unwind-rec2.rs rename to src/test/ui/panic-runtime/unwind-rec2.rs index 4dfc282b6c0f5..a130f9e879f2d 100644 --- a/src/test/run-fail/unwind-rec2.rs +++ b/src/test/ui/panic-runtime/unwind-rec2.rs @@ -1,5 +1,6 @@ -// error-pattern:fail - +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes fn build1() -> Vec { vec![0, 0, 0, 0, 0, 0, 0] diff --git a/src/test/ui/panic-runtime/unwind-unique.rs b/src/test/ui/panic-runtime/unwind-unique.rs new file mode 100644 index 0000000000000..d66e39110eaca --- /dev/null +++ b/src/test/ui/panic-runtime/unwind-unique.rs @@ -0,0 +1,12 @@ +// run-fail +// error-pattern:explicit panic +// ignore-emscripten no processes + +fn failfn() { + panic!(); +} + +fn main() { + Box::new(0); + failfn(); +} diff --git a/src/test/run-fail/args-panic.rs b/src/test/ui/panics/args-panic.rs similarity index 78% rename from src/test/run-fail/args-panic.rs rename to src/test/ui/panics/args-panic.rs index 9a62652dde1e1..322054caf1161 100644 --- a/src/test/run-fail/args-panic.rs +++ b/src/test/ui/panics/args-panic.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:meep +// ignore-emscripten no processes #![feature(box_syntax)] diff --git a/src/test/ui/panics/doublepanic.rs b/src/test/ui/panics/doublepanic.rs new file mode 100644 index 0000000000000..c1fcc875c3613 --- /dev/null +++ b/src/test/ui/panics/doublepanic.rs @@ -0,0 +1,10 @@ +#![allow(unreachable_code)] + +// run-fail +// error-pattern:One +// ignore-emscripten no processes + +fn main() { + panic!("One"); + panic!("Two"); +} diff --git a/src/test/run-fail/explicit-panic-msg.rs b/src/test/ui/panics/explicit-panic-msg.rs similarity index 80% rename from src/test/run-fail/explicit-panic-msg.rs rename to src/test/ui/panics/explicit-panic-msg.rs index 35e0518b9be57..1789e2e62c8b2 100644 --- a/src/test/run-fail/explicit-panic-msg.rs +++ b/src/test/ui/panics/explicit-panic-msg.rs @@ -1,7 +1,10 @@ #![allow(unused_assignments)] #![allow(unused_variables)] +// run-fail // error-pattern:wooooo +// ignore-emscripten no processes + fn main() { let mut a = 1; if 1 == 1 { diff --git a/src/test/ui/panics/explicit-panic.rs b/src/test/ui/panics/explicit-panic.rs new file mode 100644 index 0000000000000..27c73d3493cb8 --- /dev/null +++ b/src/test/ui/panics/explicit-panic.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:explicit +// ignore-emscripten no processes + +fn main() { + panic!(); +} diff --git a/src/test/ui/panics/fmt-panic.rs b/src/test/ui/panics/fmt-panic.rs new file mode 100644 index 0000000000000..87fb2e6dd54b9 --- /dev/null +++ b/src/test/ui/panics/fmt-panic.rs @@ -0,0 +1,8 @@ +// run-fail +// error-pattern:meh +// ignore-emscripten no processes + +fn main() { + let str_var: String = "meh".to_string(); + panic!("{}", str_var); +} diff --git a/src/test/ui/panics/main-panic.rs b/src/test/ui/panics/main-panic.rs new file mode 100644 index 0000000000000..023ab47012596 --- /dev/null +++ b/src/test/ui/panics/main-panic.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:thread 'main' panicked at +// ignore-emscripten no processes + +fn main() { + panic!() +} diff --git a/src/test/ui/panics/panic-arg.rs b/src/test/ui/panics/panic-arg.rs new file mode 100644 index 0000000000000..f7c2dbb096f29 --- /dev/null +++ b/src/test/ui/panics/panic-arg.rs @@ -0,0 +1,11 @@ +// run-fail +// error-pattern:woe +// ignore-emscripten no processes + +fn f(a: isize) { + println!("{}", a); +} + +fn main() { + f(panic!("woe")); +} diff --git a/src/test/ui/panics/panic-macro-any-wrapped.rs b/src/test/ui/panics/panic-macro-any-wrapped.rs new file mode 100644 index 0000000000000..80c87c6f32c4b --- /dev/null +++ b/src/test/ui/panics/panic-macro-any-wrapped.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'Box' +// ignore-emscripten no processes + +fn main() { + panic!(Box::new(612_i64)); +} diff --git a/src/test/ui/panics/panic-macro-any.rs b/src/test/ui/panics/panic-macro-any.rs new file mode 100644 index 0000000000000..ffc7114c1f5f2 --- /dev/null +++ b/src/test/ui/panics/panic-macro-any.rs @@ -0,0 +1,9 @@ +// run-fail +// error-pattern:panicked at 'Box' +// ignore-emscripten no processes + +#![feature(box_syntax)] + +fn main() { + panic!(box 413 as Box); +} diff --git a/src/test/ui/panics/panic-macro-explicit.rs b/src/test/ui/panics/panic-macro-explicit.rs new file mode 100644 index 0000000000000..ac4d6f8128b87 --- /dev/null +++ b/src/test/ui/panics/panic-macro-explicit.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'explicit panic' +// ignore-emscripten no processes + +fn main() { + panic!(); +} diff --git a/src/test/ui/panics/panic-macro-fmt.rs b/src/test/ui/panics/panic-macro-fmt.rs new file mode 100644 index 0000000000000..a755ebc0f4e3a --- /dev/null +++ b/src/test/ui/panics/panic-macro-fmt.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'test-fail-fmt 42 rust' +// ignore-emscripten no processes + +fn main() { + panic!("test-fail-fmt {} {}", 42, "rust"); +} diff --git a/src/test/ui/panics/panic-macro-owned.rs b/src/test/ui/panics/panic-macro-owned.rs new file mode 100644 index 0000000000000..b898fde77a3b6 --- /dev/null +++ b/src/test/ui/panics/panic-macro-owned.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'test-fail-owned' +// ignore-emscripten no processes + +fn main() { + panic!("test-fail-owned"); +} diff --git a/src/test/ui/panics/panic-macro-static.rs b/src/test/ui/panics/panic-macro-static.rs new file mode 100644 index 0000000000000..a1d467cbfb524 --- /dev/null +++ b/src/test/ui/panics/panic-macro-static.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:panicked at 'test-fail-static' +// ignore-emscripten no processes + +fn main() { + panic!("test-fail-static"); +} diff --git a/src/test/ui/panics/panic-main.rs b/src/test/ui/panics/panic-main.rs new file mode 100644 index 0000000000000..87df7688f0b40 --- /dev/null +++ b/src/test/ui/panics/panic-main.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:moop +// ignore-emscripten no processes + +fn main() { + panic!("moop"); +} diff --git a/src/test/run-fail/panic-parens.rs b/src/test/ui/panics/panic-parens.rs similarity index 86% rename from src/test/run-fail/panic-parens.rs rename to src/test/ui/panics/panic-parens.rs index e7f98e58c4f39..59ab544464967 100644 --- a/src/test/run-fail/panic-parens.rs +++ b/src/test/ui/panics/panic-parens.rs @@ -1,6 +1,9 @@ // Fail macros without arguments need to be disambiguated in // certain positions + +// run-fail // error-pattern:oops +// ignore-emscripten no processes fn bigpanic() { while (panic!("oops")) { diff --git a/src/test/run-fail/panic-set-handler.rs b/src/test/ui/panics/panic-set-handler.rs similarity index 81% rename from src/test/run-fail/panic-set-handler.rs rename to src/test/ui/panics/panic-set-handler.rs index ea2b152c6c45a..3c00183e253d7 100644 --- a/src/test/run-fail/panic-set-handler.rs +++ b/src/test/ui/panics/panic-set-handler.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:greetings from the panic handler +// ignore-emscripten no processes use std::panic; diff --git a/src/test/run-fail/panic-set-unset-handler.rs b/src/test/ui/panics/panic-set-unset-handler.rs similarity index 83% rename from src/test/run-fail/panic-set-unset-handler.rs rename to src/test/ui/panics/panic-set-unset-handler.rs index f8809c2f3889e..dde0c72f76550 100644 --- a/src/test/run-fail/panic-set-unset-handler.rs +++ b/src/test/ui/panics/panic-set-unset-handler.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:thread 'main' panicked at 'foobar' +// ignore-emscripten no processes use std::panic; diff --git a/src/test/ui/panics/panic-take-handler-nop.rs b/src/test/ui/panics/panic-take-handler-nop.rs new file mode 100644 index 0000000000000..41cbac97c443f --- /dev/null +++ b/src/test/ui/panics/panic-take-handler-nop.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:thread 'main' panicked at 'foobar' +// ignore-emscripten no processes + +use std::panic; + +fn main() { + panic::take_hook(); + panic!("foobar"); +} diff --git a/src/test/run-fail/panic-task-name-none.rs b/src/test/ui/panics/panic-task-name-none.rs similarity index 96% rename from src/test/run-fail/panic-task-name-none.rs rename to src/test/ui/panics/panic-task-name-none.rs index c7f504046baed..4e95fb5bdb803 100644 --- a/src/test/run-fail/panic-task-name-none.rs +++ b/src/test/ui/panics/panic-task-name-none.rs @@ -1,3 +1,4 @@ +// run-fail // error-pattern:thread '' panicked at 'test' // ignore-emscripten Needs threads diff --git a/src/test/run-fail/panic-task-name-owned.rs b/src/test/ui/panics/panic-task-name-owned.rs similarity index 97% rename from src/test/run-fail/panic-task-name-owned.rs rename to src/test/ui/panics/panic-task-name-owned.rs index 58f76ff787f9f..f85be7bb8e299 100644 --- a/src/test/run-fail/panic-task-name-owned.rs +++ b/src/test/ui/panics/panic-task-name-owned.rs @@ -1,3 +1,4 @@ +// run-fail // error-pattern:thread 'owned name' panicked at 'test' // ignore-emscripten Needs threads. diff --git a/src/test/ui/panics/panic.rs b/src/test/ui/panics/panic.rs new file mode 100644 index 0000000000000..b6227a582ce0b --- /dev/null +++ b/src/test/ui/panics/panic.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:1 == 2 +// ignore-emscripten no processes + +fn main() { + assert!(1 == 2); +} diff --git a/src/test/run-fail/result-get-panic.rs b/src/test/ui/panics/result-get-panic.rs similarity index 79% rename from src/test/run-fail/result-get-panic.rs rename to src/test/ui/panics/result-get-panic.rs index cddf20ee49df3..461f30b9134b0 100644 --- a/src/test/run-fail/result-get-panic.rs +++ b/src/test/ui/panics/result-get-panic.rs @@ -1,4 +1,6 @@ +// run-fail // error-pattern:called `Result::unwrap()` on an `Err` value +// ignore-emscripten no processes use std::result::Result::Err; diff --git a/src/test/ui/panics/test-panic.rs b/src/test/ui/panics/test-panic.rs new file mode 100644 index 0000000000000..85c9279cdf27b --- /dev/null +++ b/src/test/ui/panics/test-panic.rs @@ -0,0 +1,9 @@ +// run-fail +// check-stdout +// compile-flags: --test +// ignore-emscripten + +#[test] +fn test_foo() { + panic!() +} diff --git a/src/test/run-fail/test-should-fail-bad-message.rs b/src/test/ui/panics/test-should-fail-bad-message.rs similarity index 75% rename from src/test/run-fail/test-should-fail-bad-message.rs rename to src/test/ui/panics/test-should-fail-bad-message.rs index 3c69bb07d3b18..701f267764858 100644 --- a/src/test/run-fail/test-should-fail-bad-message.rs +++ b/src/test/ui/panics/test-should-fail-bad-message.rs @@ -1,5 +1,5 @@ +// run-fail // check-stdout -// error-pattern:thread 'test_foo' panicked at // compile-flags: --test // ignore-emscripten diff --git a/src/test/ui/panics/test-should-panic-bad-message.rs b/src/test/ui/panics/test-should-panic-bad-message.rs new file mode 100644 index 0000000000000..a82c4e1440aae --- /dev/null +++ b/src/test/ui/panics/test-should-panic-bad-message.rs @@ -0,0 +1,10 @@ +// run-fail +// compile-flags: --test +// check-stdout +// ignore-emscripten no processes + +#[test] +#[should_panic(expected = "foo")] +pub fn test_bar() { + panic!("bar") +} diff --git a/src/test/ui/panics/test-should-panic-no-message.rs b/src/test/ui/panics/test-should-panic-no-message.rs new file mode 100644 index 0000000000000..13f67a41cdd53 --- /dev/null +++ b/src/test/ui/panics/test-should-panic-no-message.rs @@ -0,0 +1,10 @@ +// run-fail +// compile-flags: --test +// check-stdout +// ignore-emscripten no processes + +#[test] +#[should_panic(expected = "foo")] +pub fn test_explicit() { + panic!() +} diff --git a/src/test/run-fail/unique-panic.rs b/src/test/ui/panics/unique-panic.rs similarity index 84% rename from src/test/run-fail/unique-panic.rs rename to src/test/ui/panics/unique-panic.rs index adefd796af492..22e0d63d5946f 100644 --- a/src/test/run-fail/unique-panic.rs +++ b/src/test/ui/panics/unique-panic.rs @@ -1,3 +1,4 @@ +// run-fail // error-pattern: panic fn main() { diff --git a/src/test/run-fail/while-body-panics.rs b/src/test/ui/panics/while-body-panics.rs similarity index 76% rename from src/test/run-fail/while-body-panics.rs rename to src/test/ui/panics/while-body-panics.rs index 76acb4ba42de4..2c05eb389ccfe 100644 --- a/src/test/run-fail/while-body-panics.rs +++ b/src/test/ui/panics/while-body-panics.rs @@ -1,6 +1,9 @@ #![allow(while_true)] +// run-fail // error-pattern:quux +// ignore-emscripten no processes + fn main() { let _x: isize = { while true { diff --git a/src/test/run-fail/while-panic.rs b/src/test/ui/panics/while-panic.rs similarity index 77% rename from src/test/run-fail/while-panic.rs rename to src/test/ui/panics/while-panic.rs index a0827591729f7..857f65a225228 100644 --- a/src/test/run-fail/while-panic.rs +++ b/src/test/ui/panics/while-panic.rs @@ -1,6 +1,9 @@ #![allow(while_true)] +// run-fail // error-pattern:giraffe +// ignore-emscripten no processes + fn main() { panic!({ while true { diff --git a/src/test/ui/parser/chained-comparison-suggestion.rs b/src/test/ui/parser/chained-comparison-suggestion.rs index 0431196f1744e..bbd46082c9f90 100644 --- a/src/test/ui/parser/chained-comparison-suggestion.rs +++ b/src/test/ui/parser/chained-comparison-suggestion.rs @@ -37,4 +37,17 @@ fn comp8() { //~^ ERROR mismatched types } +fn comp9() { + 1 == 2 < 3; //~ ERROR comparison operators cannot be chained +} + +fn comp10() { + 1 > 2 == false; //~ ERROR comparison operators cannot be chained +} + +fn comp11() { + 1 == 2 == 3; //~ ERROR comparison operators cannot be chained + //~^ ERROR mismatched types +} + fn main() {} diff --git a/src/test/ui/parser/chained-comparison-suggestion.stderr b/src/test/ui/parser/chained-comparison-suggestion.stderr index 5c10a4599dd03..067920d12f486 100644 --- a/src/test/ui/parser/chained-comparison-suggestion.stderr +++ b/src/test/ui/parser/chained-comparison-suggestion.stderr @@ -2,127 +2,122 @@ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:4:7 | LL | 1 < 2 <= 3; - | ^^^^^^ + | ^ ^^ | -help: split the comparison into two... +help: split the comparison into two | LL | 1 < 2 && 2 <= 3; - | ^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | (1 < 2) <= 3; - | ^^^^^^^^^^ + | ^^^^ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:9:7 | LL | 1 < 2 < 3; - | ^^^^^ + | ^ ^ | - = help: use `::<...>` instead of `<...>` to specify type arguments - = help: or use `(...)` if you meant to specify fn arguments -help: split the comparison into two... +help: split the comparison into two | LL | 1 < 2 && 2 < 3; - | ^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | (1 < 2) < 3; - | ^^^^^^^^^ + | ^^^^ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:13:7 | LL | 1 <= 2 < 3; - | ^^^^^^ + | ^^ ^ | -help: split the comparison into two... +help: split the comparison into two | LL | 1 <= 2 && 2 < 3; - | ^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | (1 <= 2) < 3; - | ^^^^^^^^^^ + | ^^^^ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:18:7 | LL | 1 <= 2 <= 3; - | ^^^^^^^ + | ^^ ^^ | -help: split the comparison into two... +help: split the comparison into two | LL | 1 <= 2 && 2 <= 3; - | ^^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | (1 <= 2) <= 3; - | ^^^^^^^^^^^ + | ^^^^ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:23:7 | LL | 1 > 2 >= 3; - | ^^^^^^ + | ^ ^^ | -help: split the comparison into two... +help: split the comparison into two | LL | 1 > 2 && 2 >= 3; - | ^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | (1 > 2) >= 3; - | ^^^^^^^^^^ + | ^^^^ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:28:7 | LL | 1 > 2 > 3; - | ^^^^^ + | ^ ^ | - = help: use `::<...>` instead of `<...>` to specify type arguments - = help: or use `(...)` if you meant to specify fn arguments -help: split the comparison into two... +help: split the comparison into two | LL | 1 > 2 && 2 > 3; - | ^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | (1 > 2) > 3; - | ^^^^^^^^^ + | ^^^^ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:32:7 | LL | 1 >= 2 > 3; - | ^^^^^^ + | ^^ ^ | - = help: use `::<...>` instead of `<...>` to specify type arguments - = help: or use `(...)` if you meant to specify fn arguments -help: split the comparison into two... +help: split the comparison into two | LL | 1 >= 2 && 2 > 3; - | ^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons - | -LL | (1 >= 2) > 3; - | ^^^^^^^^^^ + | ^^^^ error: comparison operators cannot be chained --> $DIR/chained-comparison-suggestion.rs:36:7 | LL | 1 >= 2 >= 3; - | ^^^^^^^ + | ^^ ^^ | -help: split the comparison into two... +help: split the comparison into two | LL | 1 >= 2 && 2 >= 3; - | ^^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons + | ^^^^ + +error: comparison operators cannot be chained + --> $DIR/chained-comparison-suggestion.rs:41:7 + | +LL | 1 == 2 < 3; + | ^^ ^ | -LL | (1 >= 2) >= 3; - | ^^^^^^^^^^^ +help: parenthesize the comparison + | +LL | 1 == (2 < 3); + | ^ ^ + +error: comparison operators cannot be chained + --> $DIR/chained-comparison-suggestion.rs:45:7 + | +LL | 1 > 2 == false; + | ^ ^^ + | +help: parenthesize the comparison + | +LL | (1 > 2) == false; + | ^ ^ + +error: comparison operators cannot be chained + --> $DIR/chained-comparison-suggestion.rs:49:7 + | +LL | 1 == 2 == 3; + | ^^ ^^ + | +help: split the comparison into two + | +LL | 1 == 2 && 2 == 3; + | ^^^^ error[E0308]: mismatched types --> $DIR/chained-comparison-suggestion.rs:4:14 @@ -154,6 +149,12 @@ error[E0308]: mismatched types LL | 1 >= 2 >= 3; | ^ expected `bool`, found integer -error: aborting due to 13 previous errors +error[E0308]: mismatched types + --> $DIR/chained-comparison-suggestion.rs:49:15 + | +LL | 1 == 2 == 3; + | ^ expected `bool`, found integer + +error: aborting due to 17 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/circular_modules_main.stderr b/src/test/ui/parser/circular_modules_main.stderr index 90f81c64835b7..5d4db8c31a2c3 100644 --- a/src/test/ui/parser/circular_modules_main.stderr +++ b/src/test/ui/parser/circular_modules_main.stderr @@ -10,7 +10,7 @@ error[E0425]: cannot find function `say_hello` in module `circular_modules_hello LL | circular_modules_hello::say_hello(); | ^^^^^^^^^ not found in `circular_modules_hello` | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this function | LL | use circular_modules_hello::say_hello; | diff --git a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs new file mode 100644 index 0000000000000..afbd13e6fd9a8 --- /dev/null +++ b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs @@ -0,0 +1,9 @@ +// check-pass + +#[cfg(FALSE)] +fn syntax() { + foo::(); + foo::(); +} + +fn main() {} diff --git a/src/test/ui/parser/default-on-wrong-item-kind.stderr b/src/test/ui/parser/default-on-wrong-item-kind.stderr index 9788bd64725b8..af513f7617b09 100644 --- a/src/test/ui/parser/default-on-wrong-item-kind.stderr +++ b/src/test/ui/parser/default-on-wrong-item-kind.stderr @@ -123,6 +123,8 @@ error: extern crate is not supported in `extern` blocks | LL | default extern crate foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the extern crate out to a nearby module scope error: a `use` import cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:35:5 @@ -137,6 +139,8 @@ error: `use` import is not supported in `extern` blocks | LL | default use foo; | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the `use` import out to a nearby module scope error: a static item cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:37:5 @@ -169,6 +173,8 @@ error: module is not supported in `extern` blocks | LL | default mod foo {} | ^^^^^^^^^^^^^^^ + | + = help: consider moving the module out to a nearby module scope error: an extern block cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:43:5 @@ -183,6 +189,8 @@ error: extern block is not supported in `extern` blocks | LL | default extern "C" {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the extern block out to a nearby module scope error: an enum cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:46:5 @@ -197,6 +205,8 @@ error: enum is not supported in `extern` blocks | LL | default enum foo {} | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the enum out to a nearby module scope error: a struct cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:48:5 @@ -211,6 +221,8 @@ error: struct is not supported in `extern` blocks | LL | default struct foo {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error: a union cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:50:5 @@ -225,6 +237,8 @@ error: union is not supported in `extern` blocks | LL | default union foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the union out to a nearby module scope error: a trait cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:52:5 @@ -239,6 +253,8 @@ error: trait is not supported in `extern` blocks | LL | default trait foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the trait out to a nearby module scope error: a trait alias cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:54:5 @@ -253,12 +269,16 @@ error: trait alias is not supported in `extern` blocks | LL | default trait foo = Ord; | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the trait alias out to a nearby module scope error: implementation is not supported in `extern` blocks --> $DIR/default-on-wrong-item-kind.rs:56:5 | LL | default impl foo {} | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope error: an item macro invocation cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:60:5 @@ -289,6 +309,8 @@ error: macro definition is not supported in `extern` blocks | LL | default macro foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the macro definition out to a nearby module scope error: a macro definition cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:64:5 @@ -303,6 +325,8 @@ error: macro definition is not supported in `extern` blocks | LL | default macro_rules! foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the macro definition out to a nearby module scope error: an extern crate cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:70:5 @@ -317,6 +341,8 @@ error: extern crate is not supported in `trait`s or `impl`s | LL | default extern crate foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the extern crate out to a nearby module scope error: a `use` import cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:72:5 @@ -331,6 +357,8 @@ error: `use` import is not supported in `trait`s or `impl`s | LL | default use foo; | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the `use` import out to a nearby module scope error: a static item cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:74:5 @@ -359,6 +387,8 @@ error: module is not supported in `trait`s or `impl`s | LL | default mod foo {} | ^^^^^^^^^^^^^^^ + | + = help: consider moving the module out to a nearby module scope error: an extern block cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:80:5 @@ -373,6 +403,8 @@ error: extern block is not supported in `trait`s or `impl`s | LL | default extern "C" {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the extern block out to a nearby module scope error: an enum cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:83:5 @@ -387,6 +419,8 @@ error: enum is not supported in `trait`s or `impl`s | LL | default enum foo {} | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the enum out to a nearby module scope error: a struct cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:85:5 @@ -401,6 +435,8 @@ error: struct is not supported in `trait`s or `impl`s | LL | default struct foo {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error: a union cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:87:5 @@ -415,6 +451,8 @@ error: union is not supported in `trait`s or `impl`s | LL | default union foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the union out to a nearby module scope error: a trait cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:89:5 @@ -429,6 +467,8 @@ error: trait is not supported in `trait`s or `impl`s | LL | default trait foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the trait out to a nearby module scope error: a trait alias cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:91:5 @@ -443,12 +483,16 @@ error: trait alias is not supported in `trait`s or `impl`s | LL | default trait foo = Ord; | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the trait alias out to a nearby module scope error: implementation is not supported in `trait`s or `impl`s --> $DIR/default-on-wrong-item-kind.rs:93:5 | LL | default impl foo {} | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope error: an item macro invocation cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:97:5 @@ -479,6 +523,8 @@ error: macro definition is not supported in `trait`s or `impl`s | LL | default macro foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the macro definition out to a nearby module scope error: a macro definition cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:101:5 @@ -493,6 +539,8 @@ error: macro definition is not supported in `trait`s or `impl`s | LL | default macro_rules! foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the macro definition out to a nearby module scope error: an extern crate cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:107:5 @@ -507,6 +555,8 @@ error: extern crate is not supported in `trait`s or `impl`s | LL | default extern crate foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the extern crate out to a nearby module scope error: a `use` import cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:109:5 @@ -521,6 +571,8 @@ error: `use` import is not supported in `trait`s or `impl`s | LL | default use foo; | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the `use` import out to a nearby module scope error: a static item cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:111:5 @@ -549,6 +601,8 @@ error: module is not supported in `trait`s or `impl`s | LL | default mod foo {} | ^^^^^^^^^^^^^^^ + | + = help: consider moving the module out to a nearby module scope error: an extern block cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:117:5 @@ -563,6 +617,8 @@ error: extern block is not supported in `trait`s or `impl`s | LL | default extern "C" {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the extern block out to a nearby module scope error: an enum cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:120:5 @@ -577,6 +633,8 @@ error: enum is not supported in `trait`s or `impl`s | LL | default enum foo {} | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the enum out to a nearby module scope error: a struct cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:122:5 @@ -591,6 +649,8 @@ error: struct is not supported in `trait`s or `impl`s | LL | default struct foo {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error: a union cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:124:5 @@ -605,6 +665,8 @@ error: union is not supported in `trait`s or `impl`s | LL | default union foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the union out to a nearby module scope error: a trait cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:126:5 @@ -619,6 +681,8 @@ error: trait is not supported in `trait`s or `impl`s | LL | default trait foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the trait out to a nearby module scope error: a trait alias cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:128:5 @@ -633,12 +697,16 @@ error: trait alias is not supported in `trait`s or `impl`s | LL | default trait foo = Ord; | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the trait alias out to a nearby module scope error: implementation is not supported in `trait`s or `impl`s --> $DIR/default-on-wrong-item-kind.rs:130:5 | LL | default impl foo {} | ^^^^^^^^^^^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope error: an item macro invocation cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:134:5 @@ -669,6 +737,8 @@ error: macro definition is not supported in `trait`s or `impl`s | LL | default macro foo {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider moving the macro definition out to a nearby module scope error: a macro definition cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:138:5 @@ -683,6 +753,8 @@ error: macro definition is not supported in `trait`s or `impl`s | LL | default macro_rules! foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the macro definition out to a nearby module scope error: aborting due to 95 previous errors diff --git a/src/test/ui/parser/doc-comment-in-if-statement.stderr b/src/test/ui/parser/doc-comment-in-if-statement.stderr index af21b78733f90..be52a0afd46b7 100644 --- a/src/test/ui/parser/doc-comment-in-if-statement.stderr +++ b/src/test/ui/parser/doc-comment-in-if-statement.stderr @@ -1,4 +1,4 @@ -error: expected outer doc comment +error[E0753]: expected outer doc comment --> $DIR/doc-comment-in-if-statement.rs:2:13 | LL | if true /*!*/ {} @@ -17,3 +17,4 @@ LL | if true /*!*/ {} error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0753`. diff --git a/src/test/ui/parser/impl-item-type-no-body-semantic-fail.stderr b/src/test/ui/parser/impl-item-type-no-body-semantic-fail.stderr index 541d9317c79d0..214467793bcea 100644 --- a/src/test/ui/parser/impl-item-type-no-body-semantic-fail.stderr +++ b/src/test/ui/parser/impl-item-type-no-body-semantic-fail.stderr @@ -42,13 +42,14 @@ LL | type W where Self: Eq; | | | help: provide a definition for the type: `= ;` -warning: the feature `generic_associated_types` is incomplete and may cause the compiler to crash +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/impl-item-type-no-body-semantic-fail.rs:1:12 | LL | #![feature(generic_associated_types)] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information error[E0202]: associated types are not yet supported in inherent impls (see #8995) --> $DIR/impl-item-type-no-body-semantic-fail.rs:9:5 @@ -74,6 +75,6 @@ error[E0202]: associated types are not yet supported in inherent impls (see #899 LL | type W where Self: Eq; | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 10 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0202`. diff --git a/src/test/ui/parser/issue-30318.stderr b/src/test/ui/parser/issue-30318.stderr index 489451bb5dc8d..b3a27f1985171 100644 --- a/src/test/ui/parser/issue-30318.stderr +++ b/src/test/ui/parser/issue-30318.stderr @@ -1,4 +1,4 @@ -error: expected outer doc comment +error[E0753]: expected outer doc comment --> $DIR/issue-30318.rs:3:1 | LL | //! Misplaced comment... @@ -8,3 +8,4 @@ LL | //! Misplaced comment... error: aborting due to previous error +For more information about this error, try `rustc --explain E0753`. diff --git a/src/test/ui/parser/issue-3036.stderr b/src/test/ui/parser/issue-3036.stderr index b6557163d4520..e5f5a7d8968dc 100644 --- a/src/test/ui/parser/issue-3036.stderr +++ b/src/test/ui/parser/issue-3036.stderr @@ -1,4 +1,4 @@ -error: expected `;`, found ``}`` +error: expected `;`, found `}` --> $DIR/issue-3036.rs:5:14 | LL | let x = 3 diff --git a/src/test/ui/parser/issue-32214.rs b/src/test/ui/parser/issue-32214.rs index 82f7ce62b9457..1379eeb58e6e8 100644 --- a/src/test/ui/parser/issue-32214.rs +++ b/src/test/ui/parser/issue-32214.rs @@ -1,6 +1,6 @@ trait Trait { type Item; } pub fn test >() {} -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR generic arguments must come before the first constraint fn main() { } diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr index 08b230a14f50e..bc61b3b74e217 100644 --- a/src/test/ui/parser/issue-32214.stderr +++ b/src/test/ui/parser/issue-32214.stderr @@ -1,10 +1,15 @@ -error: associated type bindings must be declared after generic parameters - --> $DIR/issue-32214.rs:3:25 +error: generic arguments must come before the first constraint + --> $DIR/issue-32214.rs:3:34 | LL | pub fn test >() {} - | -------^^^ + | ------- ^ generic argument | | - | this associated type binding should be moved after the generic parameters + | constraint + | +help: move the constraint after the generic argument + | +LL | pub fn test >() {} + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/parser/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.stderr b/src/test/ui/parser/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.stderr index bfd27a1a41e13..c83205aadd672 100644 --- a/src/test/ui/parser/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.stderr +++ b/src/test/ui/parser/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.stderr @@ -3,6 +3,8 @@ error: struct is not supported in `trait`s or `impl`s | LL | struct BadS; | ^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error: enum is not supported in `trait`s or `impl`s --> $DIR/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.rs:5:9 @@ -13,6 +15,7 @@ LL | enum BadE {} LL | expand_to_enum!(); | ------------------ in this macro invocation | + = help: consider moving the enum out to a nearby module scope = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: struct is not supported in `trait`s or `impl`s @@ -20,6 +23,8 @@ error: struct is not supported in `trait`s or `impl`s | LL | struct BadS; | ^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error: enum is not supported in `trait`s or `impl`s --> $DIR/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.rs:5:9 @@ -30,6 +35,7 @@ LL | enum BadE {} LL | expand_to_enum!(); | ------------------ in this macro invocation | + = help: consider moving the enum out to a nearby module scope = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: struct is not supported in `extern` blocks @@ -37,6 +43,8 @@ error: struct is not supported in `extern` blocks | LL | struct BadS; | ^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error: enum is not supported in `extern` blocks --> $DIR/issue-48137-macros-cannot-interpolate-impl-items-bad-variants.rs:5:9 @@ -47,6 +55,7 @@ LL | enum BadE {} LL | expand_to_enum!(); | ------------------ in this macro invocation | + = help: consider moving the enum out to a nearby module scope = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/src/test/ui/parser/issue-62894.rs b/src/test/ui/parser/issue-62894.rs index e38b7b6508937..b9c0bf834ddb2 100644 --- a/src/test/ui/parser/issue-62894.rs +++ b/src/test/ui/parser/issue-62894.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - // Regression test for #62894, shouldn't crash. // error-pattern: this file contains an unclosed delimiter // error-pattern: expected one of `(`, `[`, or `{`, found keyword `fn` diff --git a/src/test/ui/parser/issue-62894.stderr b/src/test/ui/parser/issue-62894.stderr index 4a1d7e275bed8..73e3552e3ec7a 100644 --- a/src/test/ui/parser/issue-62894.stderr +++ b/src/test/ui/parser/issue-62894.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-62894.rs:12:14 + --> $DIR/issue-62894.rs:7:14 | LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! | - - - unclosed delimiter @@ -11,7 +11,7 @@ LL | fn main() {} | ^ error: this file contains an unclosed delimiter - --> $DIR/issue-62894.rs:12:14 + --> $DIR/issue-62894.rs:7:14 | LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! | - - - unclosed delimiter @@ -23,7 +23,7 @@ LL | fn main() {} | ^ error: this file contains an unclosed delimiter - --> $DIR/issue-62894.rs:12:14 + --> $DIR/issue-62894.rs:7:14 | LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! | - - - unclosed delimiter @@ -35,7 +35,7 @@ LL | fn main() {} | ^ error: expected one of `(`, `[`, or `{`, found keyword `fn` - --> $DIR/issue-62894.rs:12:1 + --> $DIR/issue-62894.rs:7:1 | LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! | - expected one of `(`, `[`, or `{` diff --git a/src/test/ui/parser/issue-63116.stderr b/src/test/ui/parser/issue-63116.stderr index 15cd3df860b0d..80a450dbd3691 100644 --- a/src/test/ui/parser/issue-63116.stderr +++ b/src/test/ui/parser/issue-63116.stderr @@ -12,7 +12,7 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;` LL | impl W `, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;` +error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `async`, `const`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;` --> $DIR/issue-63116.rs:3:15 | LL | impl W $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:42 + | +LL | V = [PhantomData; { [ () ].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:36 + | +LL | V = [Vec::new; { [].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:36 + | +LL | V = [Vec::new; { [0].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:42 + | +LL | V = [PhantomData; { [ () ].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:36 + | +LL | V = [Vec::new; { [].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:36 + | +LL | V = [Vec::new; { [0].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:42 + | +LL | V = [PhantomData; { [ () ].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:36 + | +LL | V = [Vec::new; { [].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:36 + | +LL | V = [Vec::new; { [0].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:42 + | +LL | V = [PhantomData; { [ () ].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:36 + | +LL | V = [Vec::new; { [].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `]` + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:36 + | +LL | V = [Vec::new; { [0].len() ].len() as isize, + | - - ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error[E0282]: type annotations needed + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:29 + | +LL | V = [Vec::new; { [].len() ].len() as isize, + | ^^^ cannot infer type for type parameter `T` + +error[E0282]: type annotations needed + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:14 + | +LL | V = [Vec::new; { [0].len() ].len() as isize, + | ^^^^^^^^ cannot infer type for type parameter `T` + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/parser/issue-68890-2.stderr b/src/test/ui/parser/issue-68890-2.stderr index 967eee06e282f..e51c2c0e8428b 100644 --- a/src/test/ui/parser/issue-68890-2.stderr +++ b/src/test/ui/parser/issue-68890-2.stderr @@ -18,6 +18,6 @@ error[E0224]: at least one trait is required for an object type LL | type X<'a> = (?'a) +; | ^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0224`. diff --git a/src/test/ui/parser/issue-70388-recover-dotdotdot-rest-pat.rs b/src/test/ui/parser/issue-70388-recover-dotdotdot-rest-pat.rs new file mode 100644 index 0000000000000..ca8abd78c47ac --- /dev/null +++ b/src/test/ui/parser/issue-70388-recover-dotdotdot-rest-pat.rs @@ -0,0 +1,7 @@ +struct Foo(i32); + +fn main() { + let Foo(...) = Foo(0); //~ ERROR unexpected `...` + let [_, ..., _] = [0, 1]; //~ ERROR unexpected `...` + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/issue-70388-recover-dotdotdot-rest-pat.stderr b/src/test/ui/parser/issue-70388-recover-dotdotdot-rest-pat.stderr new file mode 100644 index 0000000000000..4961e8fc0492d --- /dev/null +++ b/src/test/ui/parser/issue-70388-recover-dotdotdot-rest-pat.stderr @@ -0,0 +1,29 @@ +error: unexpected `...` + --> $DIR/issue-70388-recover-dotdotdot-rest-pat.rs:4:13 + | +LL | let Foo(...) = Foo(0); + | ^^^ + | | + | not a valid pattern + | help: for a rest pattern, use `..` instead of `...` + +error: unexpected `...` + --> $DIR/issue-70388-recover-dotdotdot-rest-pat.rs:5:13 + | +LL | let [_, ..., _] = [0, 1]; + | ^^^ + | | + | not a valid pattern + | help: for a rest pattern, use `..` instead of `...` + +error[E0308]: mismatched types + --> $DIR/issue-70388-recover-dotdotdot-rest-pat.rs:6:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.rs b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.rs new file mode 100644 index 0000000000000..aeccd0d9f76ca --- /dev/null +++ b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.rs @@ -0,0 +1,18 @@ +struct S {} + +impl S { + fn foo(&mur Self) {} + //~^ ERROR expected identifier, found keyword `Self` + //~| ERROR expected one of `:`, `@` + //~| ERROR the `Self` constructor can only be used with + fn bar(&'static mur Self) {} + //~^ ERROR unexpected lifetime + //~| ERROR expected identifier, found keyword `Self` + //~| ERROR expected one of `:`, `@` + //~| ERROR the `Self` constructor can only be used with + + fn baz(&mur Self @ _) {} + //~^ ERROR expected one of `:`, `@` +} + +fn main() {} diff --git a/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.stderr b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.stderr new file mode 100644 index 0000000000000..421f145403623 --- /dev/null +++ b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.stderr @@ -0,0 +1,56 @@ +error: expected identifier, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:4:17 + | +LL | fn foo(&mur Self) {} + | ^^^^ expected identifier, found keyword + +error: expected one of `:`, `@`, or `|`, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:4:17 + | +LL | fn foo(&mur Self) {} + | -----^^^^ + | | | + | | expected one of `:`, `@`, or `|` + | help: declare the type after the parameter binding: `: ` + +error: unexpected lifetime `'static` in pattern + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:13 + | +LL | fn bar(&'static mur Self) {} + | ^^^^^^^ help: remove the lifetime + +error: expected identifier, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:25 + | +LL | fn bar(&'static mur Self) {} + | ^^^^ expected identifier, found keyword + +error: expected one of `:`, `@`, or `|`, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:25 + | +LL | fn bar(&'static mur Self) {} + | -------------^^^^ + | | | + | | expected one of `:`, `@`, or `|` + | help: declare the type after the parameter binding: `: ` + +error: expected one of `:`, `@`, or `|`, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:14:17 + | +LL | fn baz(&mur Self @ _) {} + | ^^^^ expected one of `:`, `@`, or `|` + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:4:17 + | +LL | fn foo(&mur Self) {} + | ^^^^ help: use curly brackets: `Self { /* fields */ }` + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:25 + | +LL | fn bar(&'static mur Self) {} + | ^^^^ help: use curly brackets: `Self { /* fields */ }` + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.rs b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.rs new file mode 100644 index 0000000000000..9b6dd7db4beb3 --- /dev/null +++ b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.rs @@ -0,0 +1,3 @@ +fn main() { + expr as fun()(:); //~ ERROR expected expression +} diff --git a/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.stderr b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.stderr new file mode 100644 index 0000000000000..f03c92e1b1f17 --- /dev/null +++ b/src/test/ui/parser/issue-70552-ascription-in-parens-after-call.stderr @@ -0,0 +1,8 @@ +error: expected expression, found `:` + --> $DIR/issue-70552-ascription-in-parens-after-call.rs:2:19 + | +LL | expr as fun()(:); + | ^ expected expression + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-70583-block-is-empty-1.rs b/src/test/ui/parser/issue-70583-block-is-empty-1.rs new file mode 100644 index 0000000000000..f560f68f61368 --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-1.rs @@ -0,0 +1,20 @@ +pub enum ErrorHandled { + Reported, + TooGeneric, +} + +impl ErrorHandled { + pub fn assert_reported(self) { + match self { + ErrorHandled::Reported => {} + ErrorHandled::TooGeneric => panic!(), + } + } +} + +fn struct_generic(x: Vec) { + for v in x { + println!("{}", v); + } + } +} //~ ERROR unexpected closing delimiter: `}` diff --git a/src/test/ui/parser/issue-70583-block-is-empty-1.stderr b/src/test/ui/parser/issue-70583-block-is-empty-1.stderr new file mode 100644 index 0000000000000..39bf113ef83de --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-1.stderr @@ -0,0 +1,13 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-70583-block-is-empty-1.rs:20:1 + | +LL | fn struct_generic(x: Vec) { + | - this opening brace... +... +LL | } + | - ...matches this closing brace +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-70583-block-is-empty-2.rs b/src/test/ui/parser/issue-70583-block-is-empty-2.rs new file mode 100644 index 0000000000000..80f53338a689e --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-2.rs @@ -0,0 +1,14 @@ +pub enum ErrorHandled { + Reported, + TooGeneric, +} + +impl ErrorHandled { + pub fn assert_reported(self) { + match self { + ErrorHandled::Reported => {}} + //^~ ERROR block is empty, you might have not meant to close it + ErrorHandled::TooGeneric => panic!(), + } + } +} //~ ERROR unexpected closing delimiter: `}` diff --git a/src/test/ui/parser/issue-70583-block-is-empty-2.stderr b/src/test/ui/parser/issue-70583-block-is-empty-2.stderr new file mode 100644 index 0000000000000..5d37b216427f6 --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-2.stderr @@ -0,0 +1,11 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-70583-block-is-empty-2.rs:14:1 + | +LL | ErrorHandled::Reported => {}} + | -- block is empty, you might have not meant to close it +... +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-8537.stderr b/src/test/ui/parser/issue-8537.stderr index b20226f87e8f8..a0793d94653da 100644 --- a/src/test/ui/parser/issue-8537.stderr +++ b/src/test/ui/parser/issue-8537.stderr @@ -8,3 +8,4 @@ LL | "invalid-ab_isize" error: aborting due to previous error +For more information about this error, try `rustc --explain E0703`. diff --git a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr index 042142ac35033..424c7a60c196f 100644 --- a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr @@ -1,6 +1,11 @@ error: unexpected closing delimiter: `}` --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 | +LL | fn main() { + | - this opening brace... +... +LL | } + | - ...matches this closing brace LL | } | ^ unexpected closing delimiter diff --git a/src/test/ui/parser/macro/trait-object-macro-matcher.stderr b/src/test/ui/parser/macro/trait-object-macro-matcher.stderr index a6fa9f8dddc27..b12eedf3581b5 100644 --- a/src/test/ui/parser/macro/trait-object-macro-matcher.stderr +++ b/src/test/ui/parser/macro/trait-object-macro-matcher.stderr @@ -18,6 +18,6 @@ error[E0224]: at least one trait is required for an object type LL | m!('static); | ^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0224`. diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr index 1655a96839569..cc7cc0c55d5ff 100644 --- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr +++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr @@ -12,12 +12,16 @@ error: trait is not supported in `trait`s or `impl`s | LL | trait T { | ^^^^^^^ + | + = help: consider moving the trait out to a nearby module scope error: struct is not supported in `trait`s or `impl`s --> $DIR/missing-close-brace-in-impl-trait.rs:11:1 | LL | pub(crate) struct Bar(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error[E0405]: cannot find trait `T` in this scope --> $DIR/missing-close-brace-in-impl-trait.rs:3:6 diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr index 43a3883357a75..7c6254356e077 100644 --- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr +++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr @@ -12,12 +12,16 @@ error: struct is not supported in `trait`s or `impl`s | LL | pub(crate) struct Bar(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the struct out to a nearby module scope error: implementation is not supported in `trait`s or `impl`s --> $DIR/missing-close-brace-in-trait.rs:7:1 | LL | impl T for Bar { | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope error: aborting due to 3 previous errors diff --git a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr index f1be5dc5ba7fa..c871e549c9ec3 100644 --- a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr +++ b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr @@ -1,6 +1,12 @@ error: unexpected closing delimiter: `}` --> $DIR/mismatched-delim-brace-empty-block.rs:5:1 | +LL | fn main() { + | - this opening brace... +LL | +LL | } + | - ...matches this closing brace +LL | let _ = (); LL | } | ^ unexpected closing delimiter diff --git a/src/test/ui/parser/raw-byte-string-eof.stderr b/src/test/ui/parser/raw-byte-string-eof.stderr deleted file mode 100644 index d5f22e2a1a814..0000000000000 --- a/src/test/ui/parser/raw-byte-string-eof.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0748]: unterminated raw string - --> $DIR/raw-byte-string-eof.rs:2:5 - | -LL | br##"a"#; - | ^ unterminated raw string - | - = note: this raw string should be terminated with `"##` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0748`. diff --git a/src/test/ui/parser/raw-str-unbalanced.rs b/src/test/ui/parser/raw-str-unbalanced.rs deleted file mode 100644 index 5a1d1be11b633..0000000000000 --- a/src/test/ui/parser/raw-str-unbalanced.rs +++ /dev/null @@ -1,4 +0,0 @@ -static s: &'static str = - r#" - "## //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `#` -; diff --git a/src/test/ui/parser/raw-str-unbalanced.stderr b/src/test/ui/parser/raw-str-unbalanced.stderr deleted file mode 100644 index ddb75722bef9f..0000000000000 --- a/src/test/ui/parser/raw-str-unbalanced.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `.`, `;`, `?`, or an operator, found `#` - --> $DIR/raw-str-unbalanced.rs:3:9 - | -LL | "## - | ^ expected one of `.`, `;`, `?`, or an operator - -error: aborting due to previous error - diff --git a/src/test/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs b/src/test/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs new file mode 100644 index 0000000000000..bdfc29a3d57ab --- /dev/null +++ b/src/test/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs @@ -0,0 +1,5 @@ +// This won't actually panic because of the error comment -- the `"` needs to be +// 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 diff --git a/src/test/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.stderr b/src/test/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.stderr new file mode 100644 index 0000000000000..3a7e2a4b14afb --- /dev/null +++ b/src/test/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.stderr @@ -0,0 +1,9 @@ +error[E0748]: unterminated raw string + --> $DIR/issue-70677-panic-on-unterminated-raw-str-at-eof.rs:5:1 + | +LL | r" + | ^ unterminated raw string + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0748`. diff --git a/src/test/ui/parser/raw-byte-string-eof.rs b/src/test/ui/parser/raw/raw-byte-string-eof.rs similarity index 100% rename from src/test/ui/parser/raw-byte-string-eof.rs rename to src/test/ui/parser/raw/raw-byte-string-eof.rs diff --git a/src/test/ui/parser/raw/raw-byte-string-eof.stderr b/src/test/ui/parser/raw/raw-byte-string-eof.stderr new file mode 100644 index 0000000000000..a76668e8051b5 --- /dev/null +++ b/src/test/ui/parser/raw/raw-byte-string-eof.stderr @@ -0,0 +1,13 @@ +error[E0748]: unterminated raw string + --> $DIR/raw-byte-string-eof.rs:2:5 + | +LL | br##"a"#; + | ^ - help: consider terminating the string here: `##` + | | + | unterminated raw string + | + = note: this raw string should be terminated with `"##` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0748`. diff --git a/src/test/ui/parser/raw-byte-string-literals.rs b/src/test/ui/parser/raw/raw-byte-string-literals.rs similarity index 100% rename from src/test/ui/parser/raw-byte-string-literals.rs rename to src/test/ui/parser/raw/raw-byte-string-literals.rs diff --git a/src/test/ui/parser/raw-byte-string-literals.stderr b/src/test/ui/parser/raw/raw-byte-string-literals.stderr similarity index 100% rename from src/test/ui/parser/raw-byte-string-literals.stderr rename to src/test/ui/parser/raw/raw-byte-string-literals.stderr diff --git a/src/test/ui/parser/raw-str-delim.rs b/src/test/ui/parser/raw/raw-str-delim.rs similarity index 100% rename from src/test/ui/parser/raw-str-delim.rs rename to src/test/ui/parser/raw/raw-str-delim.rs diff --git a/src/test/ui/parser/raw-str-delim.stderr b/src/test/ui/parser/raw/raw-str-delim.stderr similarity index 100% rename from src/test/ui/parser/raw-str-delim.stderr rename to src/test/ui/parser/raw/raw-str-delim.stderr diff --git a/src/test/ui/parser/raw/raw-str-in-macro-call.rs b/src/test/ui/parser/raw/raw-str-in-macro-call.rs new file mode 100644 index 0000000000000..462c2279f5c1c --- /dev/null +++ b/src/test/ui/parser/raw/raw-str-in-macro-call.rs @@ -0,0 +1,14 @@ +// check-pass + +macro_rules! m1 { + ($tt:tt #) => () +} + +macro_rules! m2 { + ($tt:tt) => () +} + +fn main() { + m1!(r#"abc"##); + m2!(r#"abc"#); +} diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.rs b/src/test/ui/parser/raw/raw-str-unbalanced.rs new file mode 100644 index 0000000000000..35f118f5ce6ee --- /dev/null +++ b/src/test/ui/parser/raw/raw-str-unbalanced.rs @@ -0,0 +1,4 @@ +static s: &'static str = + r#" + "## //~ too many `#` when terminating raw string +; diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.stderr b/src/test/ui/parser/raw/raw-str-unbalanced.stderr new file mode 100644 index 0000000000000..bf8f3a7a5a4bd --- /dev/null +++ b/src/test/ui/parser/raw/raw-str-unbalanced.stderr @@ -0,0 +1,10 @@ +error: too many `#` when terminating raw string + --> $DIR/raw-str-unbalanced.rs:3:9 + | +LL | "## + | ^ help: remove the extra `#` + | + = note: the raw string started with 1 `#`s + +error: aborting due to previous error + diff --git a/src/test/ui/parser/raw-str-unterminated.rs b/src/test/ui/parser/raw/raw-str-unterminated.rs similarity index 100% rename from src/test/ui/parser/raw-str-unterminated.rs rename to src/test/ui/parser/raw/raw-str-unterminated.rs diff --git a/src/test/ui/parser/raw-str-unterminated.stderr b/src/test/ui/parser/raw/raw-str-unterminated.stderr similarity index 100% rename from src/test/ui/parser/raw-str-unterminated.stderr rename to src/test/ui/parser/raw/raw-str-unterminated.stderr diff --git a/src/test/ui/parser/raw/raw-string-2.rs b/src/test/ui/parser/raw/raw-string-2.rs new file mode 100644 index 0000000000000..067332d2819bd --- /dev/null +++ b/src/test/ui/parser/raw/raw-string-2.rs @@ -0,0 +1,4 @@ +fn main() { + let x = r###"here's a long string"# "# "##; + //~^ ERROR unterminated raw string +} diff --git a/src/test/ui/parser/raw/raw-string-2.stderr b/src/test/ui/parser/raw/raw-string-2.stderr new file mode 100644 index 0000000000000..8bbac9d7bd0bd --- /dev/null +++ b/src/test/ui/parser/raw/raw-string-2.stderr @@ -0,0 +1,11 @@ +error[E0748]: unterminated raw string + --> $DIR/raw-string-2.rs:2:13 + | +LL | let x = r###"here's a long string"# "# "##; + | ^ unterminated raw string -- help: consider terminating the string here: `###` + | + = note: this raw string should be terminated with `"###` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0748`. diff --git a/src/test/ui/parser/raw/raw_string.rs b/src/test/ui/parser/raw/raw-string.rs similarity index 100% rename from src/test/ui/parser/raw/raw_string.rs rename to src/test/ui/parser/raw/raw-string.rs diff --git a/src/test/ui/parser/raw/raw-string.stderr b/src/test/ui/parser/raw/raw-string.stderr new file mode 100644 index 0000000000000..b2b853a89e751 --- /dev/null +++ b/src/test/ui/parser/raw/raw-string.stderr @@ -0,0 +1,13 @@ +error[E0748]: unterminated raw string + --> $DIR/raw-string.rs:2:13 + | +LL | let x = r##"lol"#; + | ^ - help: consider terminating the string here: `##` + | | + | unterminated raw string + | + = note: this raw string should be terminated with `"##` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0748`. diff --git a/src/test/ui/parser/raw/raw_string.stderr b/src/test/ui/parser/raw/raw_string.stderr deleted file mode 100644 index 0f1d7e4651deb..0000000000000 --- a/src/test/ui/parser/raw/raw_string.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0748]: unterminated raw string - --> $DIR/raw_string.rs:2:13 - | -LL | let x = r##"lol"#; - | ^ unterminated raw string - | - = note: this raw string should be terminated with `"##` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0748`. diff --git a/src/test/ui/parser/recover-assoc-const-constraint.rs b/src/test/ui/parser/recover-assoc-const-constraint.rs new file mode 100644 index 0000000000000..06be3cdcc1a95 --- /dev/null +++ b/src/test/ui/parser/recover-assoc-const-constraint.rs @@ -0,0 +1,7 @@ +#[cfg(FALSE)] +fn syntax() { + bar::(); //~ ERROR cannot constrain an associated constant to a value + bar::(); //~ ERROR cannot constrain an associated constant to a value +} + +fn main() {} diff --git a/src/test/ui/parser/recover-assoc-const-constraint.stderr b/src/test/ui/parser/recover-assoc-const-constraint.stderr new file mode 100644 index 0000000000000..c6733b33faa58 --- /dev/null +++ b/src/test/ui/parser/recover-assoc-const-constraint.stderr @@ -0,0 +1,20 @@ +error: cannot constrain an associated constant to a value + --> $DIR/recover-assoc-const-constraint.rs:3:11 + | +LL | bar::(); + | ----^^^-- + | | | + | | ...cannot be constrained to this value + | this associated constant... + +error: cannot constrain an associated constant to a value + --> $DIR/recover-assoc-const-constraint.rs:4:11 + | +LL | bar::(); + | ----^^^------ + | | | + | | ...cannot be constrained to this value + | this associated constant... + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/recover-assoc-eq-missing-term.rs b/src/test/ui/parser/recover-assoc-eq-missing-term.rs new file mode 100644 index 0000000000000..4b42c44dc64e5 --- /dev/null +++ b/src/test/ui/parser/recover-assoc-eq-missing-term.rs @@ -0,0 +1,6 @@ +#[cfg(FALSE)] +fn syntax() { + bar::(); //~ ERROR missing type to the right of `=` +} + +fn main() {} diff --git a/src/test/ui/parser/recover-assoc-eq-missing-term.stderr b/src/test/ui/parser/recover-assoc-eq-missing-term.stderr new file mode 100644 index 0000000000000..6e41e139220ae --- /dev/null +++ b/src/test/ui/parser/recover-assoc-eq-missing-term.stderr @@ -0,0 +1,17 @@ +error: missing type to the right of `=` + --> $DIR/recover-assoc-eq-missing-term.rs:3:17 + | +LL | bar::(); + | ^^^ + | +help: to constrain the associated type, add a type after `=` + | +LL | bar::(); + | ^^^^^^^ +help: remove the `=` if `Item` is a type + | +LL | bar::(); + | -- + +error: aborting due to previous error + diff --git a/src/test/ui/parser/recover-assoc-lifetime-constraint.rs b/src/test/ui/parser/recover-assoc-lifetime-constraint.rs new file mode 100644 index 0000000000000..558fcdfe1776f --- /dev/null +++ b/src/test/ui/parser/recover-assoc-lifetime-constraint.rs @@ -0,0 +1,6 @@ +#[cfg(FALSE)] +fn syntax() { + bar::(); //~ ERROR associated lifetimes are not supported +} + +fn main() {} diff --git a/src/test/ui/parser/recover-assoc-lifetime-constraint.stderr b/src/test/ui/parser/recover-assoc-lifetime-constraint.stderr new file mode 100644 index 0000000000000..79437533d7c0b --- /dev/null +++ b/src/test/ui/parser/recover-assoc-lifetime-constraint.stderr @@ -0,0 +1,12 @@ +error: associated lifetimes are not supported + --> $DIR/recover-assoc-lifetime-constraint.rs:3:11 + | +LL | bar::(); + | ^^^^^^^-- + | | + | the lifetime is given here + | + = help: if you meant to specify a trait object, write `dyn Trait + 'lifetime` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/recover-const-async-fn-ptr.rs b/src/test/ui/parser/recover-const-async-fn-ptr.rs new file mode 100644 index 0000000000000..25af8772cedbd --- /dev/null +++ b/src/test/ui/parser/recover-const-async-fn-ptr.rs @@ -0,0 +1,25 @@ +// edition:2018 + +type T0 = const fn(); //~ ERROR an `fn` pointer type cannot be `const` +type T1 = const extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +type T2 = const unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` +type T3 = async fn(); //~ ERROR an `fn` pointer type cannot be `async` +type T4 = async extern fn(); //~ ERROR an `fn` pointer type cannot be `async` +type T5 = async unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +type T6 = const async unsafe extern "C" fn(); +//~^ ERROR an `fn` pointer type cannot be `const` +//~| ERROR an `fn` pointer type cannot be `async` + +type FT0 = for<'a> const fn(); //~ ERROR an `fn` pointer type cannot be `const` +type FT1 = for<'a> const extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +type FT2 = for<'a> const unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` +type FT3 = for<'a> async fn(); //~ ERROR an `fn` pointer type cannot be `async` +type FT4 = for<'a> async extern fn(); //~ ERROR an `fn` pointer type cannot be `async` +type FT5 = for<'a> async unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +type FT6 = for<'a> const async unsafe extern "C" fn(); +//~^ ERROR an `fn` pointer type cannot be `const` +//~| ERROR an `fn` pointer type cannot be `async` + +fn main() { + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/recover-const-async-fn-ptr.stderr b/src/test/ui/parser/recover-const-async-fn-ptr.stderr new file mode 100644 index 0000000000000..7012096b64450 --- /dev/null +++ b/src/test/ui/parser/recover-const-async-fn-ptr.stderr @@ -0,0 +1,155 @@ +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:3:11 + | +LL | type T0 = const fn(); + | -----^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:4:11 + | +LL | type T1 = const extern "C" fn(); + | -----^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:5:11 + | +LL | type T2 = const unsafe extern fn(); + | -----^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:6:11 + | +LL | type T3 = async fn(); + | -----^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:7:11 + | +LL | type T4 = async extern fn(); + | -----^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:8:11 + | +LL | type T5 = async unsafe extern "C" fn(); + | -----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:9:11 + | +LL | type T6 = const async unsafe extern "C" fn(); + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:9:11 + | +LL | type T6 = const async unsafe extern "C" fn(); + | ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:13:12 + | +LL | type FT0 = for<'a> const fn(); + | ^^^^^^^^-----^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:14:12 + | +LL | type FT1 = for<'a> const extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:15:12 + | +LL | type FT2 = for<'a> const unsafe extern fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:16:12 + | +LL | type FT3 = for<'a> async fn(); + | ^^^^^^^^-----^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:17:12 + | +LL | type FT4 = for<'a> async extern fn(); + | ^^^^^^^^-----^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:18:12 + | +LL | type FT5 = for<'a> async unsafe extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error: an `fn` pointer type cannot be `const` + --> $DIR/recover-const-async-fn-ptr.rs:19:12 + | +LL | type FT6 = for<'a> const async unsafe extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `const` because of this + | help: remove the `const` qualifier + +error: an `fn` pointer type cannot be `async` + --> $DIR/recover-const-async-fn-ptr.rs:19:12 + | +LL | type FT6 = for<'a> const async unsafe extern "C" fn(); + | ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | help: remove the `async` qualifier + +error[E0308]: mismatched types + --> $DIR/recover-const-async-fn-ptr.rs:24:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 17 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/recover-missing-semi.stderr b/src/test/ui/parser/recover-missing-semi.stderr index 2f2464d3629c6..ba47982853871 100644 --- a/src/test/ui/parser/recover-missing-semi.stderr +++ b/src/test/ui/parser/recover-missing-semi.stderr @@ -1,4 +1,4 @@ -error: expected `;`, found `keyword `let`` +error: expected `;`, found keyword `let` --> $DIR/recover-missing-semi.rs:2:22 | LL | let _: usize = () @@ -7,7 +7,7 @@ LL | let _: usize = () LL | let _ = 3; | --- unexpected token -error: expected `;`, found `keyword `return`` +error: expected `;`, found keyword `return` --> $DIR/recover-missing-semi.rs:9:22 | LL | let _: usize = () diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.rs b/src/test/ui/parser/require-parens-for-chained-comparison.rs index e27b03dddc5be..4e97904ed6d5f 100644 --- a/src/test/ui/parser/require-parens-for-chained-comparison.rs +++ b/src/test/ui/parser/require-parens-for-chained-comparison.rs @@ -4,11 +4,11 @@ struct X; fn main() { false == false == false; //~^ ERROR comparison operators cannot be chained + //~| HELP split the comparison into two false == 0 < 2; //~^ ERROR comparison operators cannot be chained - //~| ERROR mismatched types - //~| ERROR mismatched types + //~| HELP parenthesize the comparison f(); //~^ ERROR comparison operators cannot be chained @@ -16,8 +16,6 @@ fn main() { f, Option>>(1, 2); //~^ ERROR comparison operators cannot be chained - //~| HELP split the comparison into two... - //~| ...or parenthesize one of the comparisons //~| HELP use `::<...>` instead of `<...>` to specify type arguments use std::convert::identity; diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.stderr b/src/test/ui/parser/require-parens-for-chained-comparison.stderr index 44edf2de7f8de..7001aa8e8a1d8 100644 --- a/src/test/ui/parser/require-parens-for-chained-comparison.stderr +++ b/src/test/ui/parser/require-parens-for-chained-comparison.stderr @@ -2,19 +2,29 @@ error: comparison operators cannot be chained --> $DIR/require-parens-for-chained-comparison.rs:5:11 | LL | false == false == false; - | ^^^^^^^^^^^ + | ^^ ^^ + | +help: split the comparison into two + | +LL | false == false && false == false; + | ^^^^^^^^ error: comparison operators cannot be chained - --> $DIR/require-parens-for-chained-comparison.rs:8:11 + --> $DIR/require-parens-for-chained-comparison.rs:9:11 | LL | false == 0 < 2; - | ^^^^^^ + | ^^ ^ + | +help: parenthesize the comparison + | +LL | false == (0 < 2); + | ^ ^ error: comparison operators cannot be chained --> $DIR/require-parens-for-chained-comparison.rs:13:6 | LL | f(); - | ^^^ + | ^ ^ | help: use `::<...>` instead of `<...>` to specify type arguments | @@ -25,42 +35,21 @@ error: comparison operators cannot be chained --> $DIR/require-parens-for-chained-comparison.rs:17:6 | LL | f, Option>>(1, 2); - | ^^^^^^^^ - | -help: split the comparison into two... - | -LL | f < Result && Result , Option>>(1, 2); - | ^^^^^^^^^^^^^^^^^^^^^^ -help: ...or parenthesize one of the comparisons + | ^ ^ | -LL | (f < Result) , Option>>(1, 2); - | ^^^^^^^^^^^^^^ help: use `::<...>` instead of `<...>` to specify type arguments | LL | f::, Option>>(1, 2); | ^^ error: comparison operators cannot be chained - --> $DIR/require-parens-for-chained-comparison.rs:24:21 + --> $DIR/require-parens-for-chained-comparison.rs:22:21 | LL | let _ = identity; - | ^^^^ + | ^ ^ | = help: use `::<...>` instead of `<...>` to specify type arguments = help: or use `(...)` if you meant to specify fn arguments -error[E0308]: mismatched types - --> $DIR/require-parens-for-chained-comparison.rs:8:14 - | -LL | false == 0 < 2; - | ^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/require-parens-for-chained-comparison.rs:8:18 - | -LL | false == 0 < 2; - | ^ expected `bool`, found integer - -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/trait-object-bad-parens.rs b/src/test/ui/parser/trait-object-bad-parens.rs index 048e028be1ca1..0a2836d691f5f 100644 --- a/src/test/ui/parser/trait-object-bad-parens.rs +++ b/src/test/ui/parser/trait-object-bad-parens.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![allow(bare_trait_objects)] auto trait Auto {} diff --git a/src/test/ui/parser/trait-object-bad-parens.stderr b/src/test/ui/parser/trait-object-bad-parens.stderr index f53afdff5e7c2..74e484eebee1f 100644 --- a/src/test/ui/parser/trait-object-bad-parens.stderr +++ b/src/test/ui/parser/trait-object-bad-parens.stderr @@ -1,23 +1,23 @@ error[E0178]: expected a path on the left-hand side of `+`, not `((Auto))` - --> $DIR/trait-object-bad-parens.rs:7:16 + --> $DIR/trait-object-bad-parens.rs:8:16 | LL | let _: Box<((Auto)) + Auto>; | ^^^^^^^^^^^^^^^ expected a path error[E0178]: expected a path on the left-hand side of `+`, not `(Auto + Auto)` - --> $DIR/trait-object-bad-parens.rs:9:16 + --> $DIR/trait-object-bad-parens.rs:10:16 | LL | let _: Box<(Auto + Auto) + Auto>; | ^^^^^^^^^^^^^^^^^^^^ expected a path error[E0178]: expected a path on the left-hand side of `+`, not `(Auto)` - --> $DIR/trait-object-bad-parens.rs:11:16 + --> $DIR/trait-object-bad-parens.rs:12:16 | LL | let _: Box<(Auto +) + Auto>; | ^^^^^^^^^^^^^^^ expected a path error[E0178]: expected a path on the left-hand side of `+`, not `(dyn Auto)` - --> $DIR/trait-object-bad-parens.rs:13:16 + --> $DIR/trait-object-bad-parens.rs:14:16 | LL | let _: Box<(dyn Auto) + Auto>; | ^^^^^^^^^^^^^^^^^ expected a path diff --git a/src/test/ui/parser/trait-object-trait-parens.stderr b/src/test/ui/parser/trait-object-trait-parens.stderr index 7022a66ca1a17..62da99bc47d26 100644 --- a/src/test/ui/parser/trait-object-trait-parens.stderr +++ b/src/test/ui/parser/trait-object-trait-parens.stderr @@ -69,6 +69,6 @@ LL | let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>; | first non-auto trait | trait alias used in trait object type (first use) -error: aborting due to 6 previous errors +error: aborting due to 6 previous errors; 3 warnings emitted For more information about this error, try `rustc --explain E0225`. diff --git a/src/test/ui/parser/underscore-suffix-for-string.rs b/src/test/ui/parser/underscore-suffix-for-string.rs index dd0599b4ab363..2e0ebe2cfa446 100644 --- a/src/test/ui/parser/underscore-suffix-for-string.rs +++ b/src/test/ui/parser/underscore-suffix-for-string.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass fn main() { let _ = "Foo"_; diff --git a/src/test/ui/parser/underscore-suffix-for-string.stderr b/src/test/ui/parser/underscore-suffix-for-string.stderr index 0a325ae9070e4..00c7657f17bd3 100644 --- a/src/test/ui/parser/underscore-suffix-for-string.stderr +++ b/src/test/ui/parser/underscore-suffix-for-string.stderr @@ -7,3 +7,5 @@ LL | let _ = "Foo"_; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: see issue #42326 for more information +warning: 1 warning emitted + diff --git a/src/test/ui/path-lookahead.stderr b/src/test/ui/path-lookahead.stderr index 62b3b507e1d26..7a57b6100f380 100644 --- a/src/test/ui/path-lookahead.stderr +++ b/src/test/ui/path-lookahead.stderr @@ -10,3 +10,5 @@ note: the lint level is defined here LL | #![warn(unused_parens)] | ^^^^^^^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs index 32c638bcbcca3..f1680e9e8884e 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs @@ -45,19 +45,19 @@ fn main() { *b = NC; let ref a @ box ref mut b = Box::new(NC); //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable - //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable + //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable *b = NC; drop(a); let ref mut a @ box ref b = Box::new(NC); //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable - //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable + //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable *a = Box::new(NC); drop(b); fn f5(ref mut a @ box ref b: Box) { //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable - //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable + //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable *a = Box::new(NC); drop(b); } @@ -65,7 +65,7 @@ fn main() { match Box::new(nc()) { ref mut a @ box ref b => { //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable - //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable + //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable *a = Box::new(NC); drop(b); } diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr index 5534d0a75e63d..5ce546f08bf6f 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr @@ -99,7 +99,7 @@ LL | a @ box b => {} | | value used here after move | value moved here -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-at-and-box.rs:46:21 | LL | let ref a @ box ref mut b = Box::new(NC); @@ -111,7 +111,7 @@ LL | let ref a @ box ref mut b = Box::new(NC); LL | drop(a); | - immutable borrow later used here -error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable --> $DIR/borrowck-pat-at-and-box.rs:52:25 | LL | let ref mut a @ box ref b = Box::new(NC); @@ -123,7 +123,7 @@ LL | let ref mut a @ box ref b = Box::new(NC); LL | *a = Box::new(NC); | -- mutable borrow later used here -error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable --> $DIR/borrowck-pat-at-and-box.rs:66:25 | LL | ref mut a @ box ref b => { @@ -155,7 +155,7 @@ LL | fn f2(a @ box b: Box) {} | value moved here | move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait -error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable --> $DIR/borrowck-pat-at-and-box.rs:58:27 | LL | fn f5(ref mut a @ box ref b: Box) { diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs index 58d4a9b018cee..2b5e339c6396e 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs @@ -10,7 +10,7 @@ fn main() { match &mut Some(1) { ref mut z @ &mut Some(ref a) => { //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable - //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable + //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable **z = None; println!("{}", *a); } @@ -47,12 +47,12 @@ fn main() { let ref mut a @ ref b = u(); //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable - //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable + //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable *a = u(); drop(b); let ref a @ ref mut b = u(); //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable - //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable + //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable *b = u(); drop(a); @@ -78,8 +78,8 @@ fn main() { ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => { //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable - //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable - //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable + //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable + //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable *b = U; drop(a); } @@ -123,15 +123,15 @@ fn main() { let ref a @ (ref mut b, ref mut c) = (U, U); //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable - //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable - //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable + //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable + //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable *b = U; drop(a); let ref a @ (ref mut b, ref mut c) = (U, U); //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable - *b = U; //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable - *c = U; //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable + *b = U; //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable + *c = U; //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable drop(a); let ref mut a @ (ref b, ref c) = (U, U); //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr index 8c6ca888e0762..b161054414a30 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr @@ -294,7 +294,7 @@ LL | fn f4_also_moved(ref a @ ref mut b @ c: U) {} | | value moved into `c` here | value borrowed, by `b`, here -error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:11:31 | LL | ref mut z @ &mut Some(ref a) => { @@ -306,7 +306,7 @@ LL | ref mut z @ &mut Some(ref a) => { LL | **z = None; | ---------- mutable borrow later used here -error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:48:21 | LL | let ref mut a @ ref b = u(); @@ -318,7 +318,7 @@ LL | let ref mut a @ ref b = u(); LL | *a = u(); | -------- mutable borrow later used here -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:53:17 | LL | let ref a @ ref mut b = u(); @@ -330,7 +330,7 @@ LL | let ref a @ ref mut b = u(); LL | drop(a); | - immutable borrow later used here -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:78:20 | LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => { @@ -342,7 +342,7 @@ LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => { LL | drop(a); | - immutable borrow later used here -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:78:45 | LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => { @@ -402,7 +402,7 @@ LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:124:18 | LL | let ref a @ (ref mut b, ref mut c) = (U, U); @@ -414,7 +414,7 @@ LL | let ref a @ (ref mut b, ref mut c) = (U, U); LL | drop(a); | - immutable borrow later used here -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:124:29 | LL | let ref a @ (ref mut b, ref mut c) = (U, U); @@ -426,7 +426,7 @@ LL | let ref a @ (ref mut b, ref mut c) = (U, U); LL | drop(a); | - immutable borrow later used here -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:131:18 | LL | let ref a @ (ref mut b, ref mut c) = (U, U); @@ -438,7 +438,7 @@ LL | let ref a @ (ref mut b, ref mut c) = (U, U); LL | drop(a); | - immutable borrow later used here -error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable +error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable --> $DIR/borrowck-pat-ref-mut-and-ref.rs:131:29 | LL | let ref a @ (ref mut b, ref mut c) = (U, U); diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs index f5c39a7ac5276..a208d0087ff53 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs @@ -27,7 +27,7 @@ fn main() { let ref mut a @ ref mut b = U; //~^ ERROR cannot borrow value as mutable more than once at a time - //~| ERROR cannot borrow `_` as mutable more than once at a time + //~| ERROR cannot borrow value as mutable more than once at a time drop(a); let ref mut a @ ref mut b = U; //~^ ERROR cannot borrow value as mutable more than once at a time @@ -37,7 +37,7 @@ fn main() { let ref mut a @ ref mut b = U; //~^ ERROR cannot borrow value as mutable more than once at a time - //~| ERROR cannot borrow `_` as mutable more than once at a time + //~| ERROR cannot borrow value as mutable more than once at a time *a = U; let ref mut a @ ref mut b = U; //~^ ERROR cannot borrow value as mutable more than once at a time @@ -95,11 +95,11 @@ fn main() { ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { //~^ ERROR cannot borrow value as mutable more than once at a time //~| ERROR cannot borrow value as mutable more than once at a time - //~| ERROR cannot borrow `_` as mutable more than once at a time - //~| ERROR cannot borrow `_` as mutable more than once at a time + //~| ERROR cannot borrow value as mutable more than once at a time + //~| ERROR cannot borrow value as mutable more than once at a time *a = Err(U); - // FIXME: The binding name `_` used above makes for problematic diagnostics. + // FIXME: The binding name value used above makes for problematic diagnostics. // Resolve that somehow... } } @@ -107,8 +107,8 @@ fn main() { ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { //~^ ERROR cannot borrow value as mutable more than once at a time //~| ERROR cannot borrow value as mutable more than once at a time - //~| ERROR cannot borrow `_` as mutable more than once at a time - //~| ERROR cannot borrow `_` as mutable more than once at a time + //~| ERROR cannot borrow value as mutable more than once at a time + //~| ERROR cannot borrow value as mutable more than once at a time drop(a); } } diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr index e74f227b5e48c..ae7c8f38e1eb4 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr @@ -258,7 +258,7 @@ LL | fn f4_also_moved(ref mut a @ ref mut b @ c: U) {} | | value moved into `c` here | value borrowed, by `b`, here -error[E0499]: cannot borrow `_` as mutable more than once at a time +error[E0499]: cannot borrow value as mutable more than once at a time --> $DIR/borrowck-pat-ref-mut-twice.rs:28:21 | LL | let ref mut a @ ref mut b = U; @@ -270,7 +270,7 @@ LL | let ref mut a @ ref mut b = U; LL | drop(a); | - first borrow later used here -error[E0499]: cannot borrow `_` as mutable more than once at a time +error[E0499]: cannot borrow value as mutable more than once at a time --> $DIR/borrowck-pat-ref-mut-twice.rs:38:21 | LL | let ref mut a @ ref mut b = U; @@ -318,7 +318,7 @@ LL | let a @ &mut (ref mut b, ref mut c) = &mut (U, U); | | value borrowed here after move | value moved here -error[E0499]: cannot borrow `_` as mutable more than once at a time +error[E0499]: cannot borrow value as mutable more than once at a time --> $DIR/borrowck-pat-ref-mut-twice.rs:95:24 | LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { @@ -330,7 +330,7 @@ LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { LL | *a = Err(U); | ----------- first borrow later used here -error[E0499]: cannot borrow `_` as mutable more than once at a time +error[E0499]: cannot borrow value as mutable more than once at a time --> $DIR/borrowck-pat-ref-mut-twice.rs:95:53 | LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { @@ -342,7 +342,7 @@ LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { LL | *a = Err(U); | ----------- first borrow later used here -error[E0499]: cannot borrow `_` as mutable more than once at a time +error[E0499]: cannot borrow value as mutable more than once at a time --> $DIR/borrowck-pat-ref-mut-twice.rs:107:24 | LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { @@ -354,7 +354,7 @@ LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { LL | drop(a); | - first borrow later used here -error[E0499]: cannot borrow `_` as mutable more than once at a time +error[E0499]: cannot borrow value as mutable more than once at a time --> $DIR/borrowck-pat-ref-mut-twice.rs:107:53 | LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => { diff --git a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs index b40c3e3358aa3..a45497229ac9e 100644 --- a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs +++ b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs @@ -29,7 +29,7 @@ fn main() { let _a: &NotCopy = a; let _b: NotCopy = b; let ref mut a @ b = NotCopy; //~ ERROR cannot move out of value because it is borrowed - //~^ ERROR cannot move out of `_` because it is borrowed + //~^ ERROR cannot move out of value because it is borrowed let _a: &NotCopy = a; let _b: NotCopy = b; match Ok(NotCopy) { diff --git a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr index 19e815a1ae8ad..141d667c7460c 100644 --- a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr +++ b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr @@ -44,7 +44,7 @@ LL | ref a @ b => { | | value moved into `b` here | value borrowed, by `a`, here -error[E0505]: cannot move out of `_` because it is borrowed +error[E0505]: cannot move out of value because it is borrowed --> $DIR/default-binding-modes-both-sides-independent.rs:31:21 | LL | let ref mut a @ b = NotCopy; diff --git a/src/test/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr b/src/test/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr index 21218d9a17368..6f3613b63c9aa 100644 --- a/src/test/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr +++ b/src/test/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr @@ -36,3 +36,6 @@ warning[E0170]: pattern binding `Baz` is named the same as one of the variants o LL | Baz => {}, | ^^^ help: to match on the variant, qualify the path: `Foo::Baz` +warning: 6 warnings emitted + +For more information about this error, try `rustc --explain E0170`. diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr index e1079f912d076..0fa77fb73da1f 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -5,6 +5,7 @@ LL | match uninhab_ref() { | ^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&!` error[E0004]: non-exhaustive patterns: type `Foo` is non-empty --> $DIR/always-inhabited-union-ref.rs:27:11 @@ -18,6 +19,7 @@ LL | match uninhab_union() { | ^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` error: aborting due to 2 previous errors diff --git a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr index 5866df5cb1db8..edc5ece558a70 100644 --- a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr +++ b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr @@ -17,6 +17,7 @@ LL | match x { | ^ pattern `128u8..=std::u8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered --> $DIR/exhaustive_integer_patterns.rs:33:11 @@ -25,6 +26,7 @@ LL | match x { | ^ patterns `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error: unreachable pattern --> $DIR/exhaustive_integer_patterns.rs:44:9 @@ -39,6 +41,7 @@ LL | match x { | ^ patterns `std::i8::MIN..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `std::i8::MIN` not covered --> $DIR/exhaustive_integer_patterns.rs:83:11 @@ -47,6 +50,7 @@ LL | match 0i8 { | ^^^ pattern `std::i8::MIN` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `0i16` not covered --> $DIR/exhaustive_integer_patterns.rs:91:11 @@ -55,6 +59,7 @@ LL | match 0i16 { | ^^^^ pattern `0i16` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i16` error[E0004]: non-exhaustive patterns: `128u8..=std::u8::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:109:11 @@ -63,6 +68,7 @@ LL | match 0u8 { | ^^^ pattern `128u8..=std::u8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `(0u8, Some(_))` and `(2u8..=std::u8::MAX, Some(_))` not covered --> $DIR/exhaustive_integer_patterns.rs:121:11 @@ -71,6 +77,7 @@ LL | match (0u8, Some(())) { | ^^^^^^^^^^^^^^^ patterns `(0u8, Some(_))` and `(2u8..=std::u8::MAX, Some(_))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(u8, std::option::Option<()>)` error[E0004]: non-exhaustive patterns: `(126u8..=127u8, false)` not covered --> $DIR/exhaustive_integer_patterns.rs:126:11 @@ -79,6 +86,7 @@ LL | match (0u8, true) { | ^^^^^^^^^^^ pattern `(126u8..=127u8, false)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(u8, bool)` error: multiple patterns covering the same range --> $DIR/exhaustive_integer_patterns.rs:141:9 @@ -101,6 +109,7 @@ LL | match 0u128 { | ^^^^^ pattern `std::u128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `5u128..=std::u128::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:150:11 @@ -109,6 +118,7 @@ LL | match 0u128 { | ^^^^^ pattern `5u128..=std::u128::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `0u128..=3u128` not covered --> $DIR/exhaustive_integer_patterns.rs:154:11 @@ -117,6 +127,7 @@ LL | match 0u128 { | ^^^^^ pattern `0u128..=3u128` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u128` error: unreachable pattern --> $DIR/exhaustive_integer_patterns.rs:162:9 diff --git a/src/test/ui/pattern/usefulness/issue-35609.stderr b/src/test/ui/pattern/usefulness/issue-35609.stderr index af22535c55e5a..66f904aced11b 100644 --- a/src/test/ui/pattern/usefulness/issue-35609.stderr +++ b/src/test/ui/pattern/usefulness/issue-35609.stderr @@ -5,6 +5,7 @@ LL | match (A, ()) { | ^^^^^^^ patterns `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(Enum, ())` error[E0004]: non-exhaustive patterns: `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered --> $DIR/issue-35609.rs:14:11 @@ -13,6 +14,7 @@ LL | match (A, A) { | ^^^^^^ patterns `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(Enum, Enum)` error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered --> $DIR/issue-35609.rs:18:11 @@ -21,6 +23,7 @@ LL | match ((A, ()), ()) { | ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `((Enum, ()), ())` error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered --> $DIR/issue-35609.rs:22:11 @@ -29,6 +32,7 @@ LL | match ((A, ()), A) { | ^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `((Enum, ()), Enum)` error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered --> $DIR/issue-35609.rs:26:11 @@ -37,6 +41,7 @@ LL | match ((A, ()), ()) { | ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `((Enum, ()), ())` error[E0004]: non-exhaustive patterns: `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered --> $DIR/issue-35609.rs:31:11 @@ -48,6 +53,7 @@ LL | match S(A, ()) { | ^^^^^^^^ patterns `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `S` error[E0004]: non-exhaustive patterns: `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered --> $DIR/issue-35609.rs:35:11 @@ -59,6 +65,7 @@ LL | match (Sd { x: A, y: () }) { | ^^^^^^^^^^^^^^^^^^^^ patterns `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Sd` error[E0004]: non-exhaustive patterns: `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered --> $DIR/issue-35609.rs:39:11 @@ -67,6 +74,7 @@ LL | match Some(A) { | ^^^^^^^ patterns `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::option::Option` error: aborting due to 8 previous errors diff --git a/src/test/ui/pattern/usefulness/issue-43253.stderr b/src/test/ui/pattern/usefulness/issue-43253.stderr index cdd3067a678a5..6e65c51dd3cf3 100644 --- a/src/test/ui/pattern/usefulness/issue-43253.stderr +++ b/src/test/ui/pattern/usefulness/issue-43253.stderr @@ -48,3 +48,5 @@ warning: unreachable pattern LL | 6 => {}, | ^ +warning: 6 warnings emitted + diff --git a/src/test/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs b/src/test/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs new file mode 100644 index 0000000000000..e2ff9ac87ef51 --- /dev/null +++ b/src/test/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs @@ -0,0 +1,22 @@ +// check-pass + +// In PR 71930, it was discovered that the code to retrieve the inferred type of a match scrutinee +// was incorrect. + +fn f() -> ! { + panic!() +} + +fn g() -> usize { + match f() { // Should infer type `bool` + false => 0, + true => 1, + } +} + +fn h() -> usize { + match f() { // Should infer type `!` + } +} + +fn main() {} diff --git a/src/test/ui/pattern/usefulness/match-arm-statics-2.rs b/src/test/ui/pattern/usefulness/match-arm-statics-2.rs index 728d4a64495b1..4c5f2d356491b 100644 --- a/src/test/ui/pattern/usefulness/match-arm-statics-2.rs +++ b/src/test/ui/pattern/usefulness/match-arm-statics-2.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - use self::Direction::{North, East, South, West}; #[derive(PartialEq, Eq)] diff --git a/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr b/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr index 7bb6a700a3717..3d329e2e6efb1 100644 --- a/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr +++ b/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr @@ -1,13 +1,14 @@ error[E0004]: non-exhaustive patterns: `(true, false)` not covered - --> $DIR/match-arm-statics-2.rs:22:11 + --> $DIR/match-arm-statics-2.rs:17:11 | LL | match (true, false) { | ^^^^^^^^^^^^^ pattern `(true, false)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(bool, bool)` error[E0004]: non-exhaustive patterns: `Some(Some(West))` not covered - --> $DIR/match-arm-statics-2.rs:34:11 + --> $DIR/match-arm-statics-2.rs:29:11 | LL | match Some(Some(North)) { | ^^^^^^^^^^^^^^^^^ pattern `Some(Some(West))` not covered @@ -21,9 +22,10 @@ LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::option::Option>` error[E0004]: non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }` not covered - --> $DIR/match-arm-statics-2.rs:53:11 + --> $DIR/match-arm-statics-2.rs:48:11 | LL | / struct Foo { LL | | bar: Option, @@ -35,6 +37,7 @@ LL | match (Foo { bar: Some(North), baz: NewBool(true) }) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { bar: Some(North), baz: NewBool(true) }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` error: aborting due to 3 previous errors diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index 539aa854f9e6b..323449eebc540 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -5,6 +5,7 @@ LL | match buf { | ^^^ patterns `&[0u8..=64u8, _, _, _]` and `&[66u8..=std::u8::MAX, _, _, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[u8; 4]` error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 @@ -13,6 +14,7 @@ LL | match buf { | ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[u8]` error: aborting due to 2 previous errors diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 49c38d2a9d3d7..1f6503e3e9c71 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -35,6 +35,7 @@ LL | match_empty!(0u8); | ^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:66:18 @@ -46,6 +47,7 @@ LL | match_empty!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:68:18 @@ -59,6 +61,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:70:18 @@ -73,6 +76,7 @@ LL | match_empty!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered --> $DIR/match-empty-exhaustive_patterns.rs:72:18 @@ -89,6 +93,7 @@ LL | match_empty!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered --> $DIR/match-empty-exhaustive_patterns.rs:74:18 @@ -109,6 +114,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered --> $DIR/match-empty-exhaustive_patterns.rs:76:18 @@ -122,6 +128,7 @@ LL | match_empty!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum5` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/match-empty-exhaustive_patterns.rs:79:18 @@ -130,6 +137,7 @@ LL | match_false!(0u8); | ^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered --> $DIR/match-empty-exhaustive_patterns.rs:81:18 @@ -141,6 +149,7 @@ LL | match_false!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered --> $DIR/match-empty-exhaustive_patterns.rs:83:18 @@ -154,6 +163,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered --> $DIR/match-empty-exhaustive_patterns.rs:85:18 @@ -168,6 +178,7 @@ LL | match_false!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered --> $DIR/match-empty-exhaustive_patterns.rs:87:18 @@ -184,6 +195,7 @@ LL | match_false!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered --> $DIR/match-empty-exhaustive_patterns.rs:89:18 @@ -204,6 +216,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered --> $DIR/match-empty-exhaustive_patterns.rs:91:18 @@ -217,6 +230,7 @@ LL | match_false!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum5` error: aborting due to 18 previous errors diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 72e3fc0a16744..08095f6e7fb12 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -8,6 +8,7 @@ LL | match_false!(x); // Not detected as unreachable nor exhaustive. | ^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` error[E0004]: non-exhaustive patterns: type `u8` is non-empty --> $DIR/match-empty.rs:63:18 @@ -16,6 +17,7 @@ LL | match_empty!(0u8); | ^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty --> $DIR/match-empty.rs:65:18 @@ -27,6 +29,7 @@ LL | match_empty!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty --> $DIR/match-empty.rs:67:18 @@ -40,6 +43,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty --> $DIR/match-empty.rs:69:18 @@ -54,6 +58,7 @@ LL | match_empty!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered --> $DIR/match-empty.rs:71:18 @@ -70,6 +75,7 @@ LL | match_empty!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered --> $DIR/match-empty.rs:73:18 @@ -90,6 +96,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered --> $DIR/match-empty.rs:75:18 @@ -103,6 +110,7 @@ LL | match_empty!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum5` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/match-empty.rs:78:18 @@ -111,6 +119,7 @@ LL | match_false!(0u8); | ^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered --> $DIR/match-empty.rs:80:18 @@ -122,6 +131,7 @@ LL | match_false!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered --> $DIR/match-empty.rs:82:18 @@ -135,6 +145,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered --> $DIR/match-empty.rs:84:18 @@ -149,6 +160,7 @@ LL | match_false!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered --> $DIR/match-empty.rs:86:18 @@ -165,6 +177,7 @@ LL | match_false!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered --> $DIR/match-empty.rs:88:18 @@ -185,6 +198,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered --> $DIR/match-empty.rs:90:18 @@ -198,6 +212,7 @@ LL | match_false!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonEmptyEnum5` error: aborting due to 15 previous errors diff --git a/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr index 211f333882b10..84cfe1da315da 100644 --- a/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr +++ b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr @@ -5,6 +5,7 @@ LL | match 0 { 1 => () } | ^ patterns `std::i32::MIN..=0i32` and `2i32..=std::i32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/match-non-exhaustive.rs:3:11 @@ -13,6 +14,7 @@ LL | match 0 { 0 if false => () } | ^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `i32` error: aborting due to 2 previous errors diff --git a/src/test/ui/pattern/usefulness/match-privately-empty.rs b/src/test/ui/pattern/usefulness/match-privately-empty.rs index c7cde468bb9bf..315eb03d16564 100644 --- a/src/test/ui/pattern/usefulness/match-privately-empty.rs +++ b/src/test/ui/pattern/usefulness/match-privately-empty.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #![feature(never_type)] #![feature(exhaustive_patterns)] diff --git a/src/test/ui/pattern/usefulness/match-privately-empty.stderr b/src/test/ui/pattern/usefulness/match-privately-empty.stderr index 4dcbf05ecce2b..50a4674def7e8 100644 --- a/src/test/ui/pattern/usefulness/match-privately-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-privately-empty.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered - --> $DIR/match-privately-empty.rs:18:11 + --> $DIR/match-privately-empty.rs:13:11 | LL | match private::DATA { | ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered @@ -10,6 +10,7 @@ LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | ---- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::option::Option` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/match-range-fail-dominate.stderr b/src/test/ui/pattern/usefulness/match-range-fail-dominate.stderr index 76a6d1d3eaa1b..6922170fccbc8 100644 --- a/src/test/ui/pattern/usefulness/match-range-fail-dominate.stderr +++ b/src/test/ui/pattern/usefulness/match-range-fail-dominate.stderr @@ -89,5 +89,5 @@ LL | 0.02f64 => {} = 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 #41620 -error: aborting due to 5 previous errors +error: aborting due to 5 previous errors; 6 warnings emitted diff --git a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr index 977a112808190..ba5312d213590 100644 --- a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr @@ -5,6 +5,7 @@ LL | match list { | ^^^^ pattern `&[_, Some(_), .., None, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[std::option::Option<()>]` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index e5f01174ac1bf..29aa0c1c92670 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -20,6 +20,7 @@ LL | match e1 { | ^^ patterns `B` and `C` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `E` error[E0005]: refutable pattern in local binding: `B` and `C` not covered --> $DIR/non-exhaustive-defined-here.rs:36:9 @@ -44,6 +45,7 @@ LL | let E::A = e; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `E` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let E::A = e { /* */ } @@ -71,6 +73,7 @@ LL | match e { | ^ patterns `&B` and `&C` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&E` error[E0005]: refutable pattern in local binding: `&B` and `&C` not covered --> $DIR/non-exhaustive-defined-here.rs:44:9 @@ -95,6 +98,7 @@ LL | let E::A = e; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `&E` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let E::A = e { /* */ } @@ -122,6 +126,7 @@ LL | match e { | ^ patterns `&&mut &B` and `&&mut &C` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&&mut &E` error[E0005]: refutable pattern in local binding: `&&mut &B` and `&&mut &C` not covered --> $DIR/non-exhaustive-defined-here.rs:52:9 @@ -146,6 +151,7 @@ LL | let E::A = e; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `&&mut &E` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let E::A = e { /* */ } @@ -168,6 +174,7 @@ LL | match e { | ^ pattern `None` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Opt` error[E0005]: refutable pattern in local binding: `None` not covered --> $DIR/non-exhaustive-defined-here.rs:69:9 @@ -187,6 +194,7 @@ LL | let Opt::Some(ref _x) = e; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `Opt` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Opt::Some(ref _x) = e { /* */ } diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.stderr index 6de615c3de4fd..4835fa86cc0ee 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.stderr @@ -5,6 +5,7 @@ LL | match 0.0 { | ^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `f64` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr index 72b4b522198e0..c9f26db6f1f8d 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr @@ -5,6 +5,7 @@ LL | match (l1, l2) { | ^^^^^^^^ pattern `(Some(&[]), Err(_))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(std::option::Option<&[T]>, std::result::Result<&[T], ()>)` error[E0004]: non-exhaustive patterns: `A(C)` not covered --> $DIR/non-exhaustive-match-nested.rs:15:11 @@ -19,6 +20,7 @@ LL | match x { | ^ pattern `A(C)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `T` error: aborting due to 2 previous errors diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs index 59f7bb892c68c..9947989dc1211 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #![allow(illegal_floating_point_literal_pattern)] enum T { A, B } diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr index dff2c8d9424c3..436a293b6ce75 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `A` not covered - --> $DIR/non-exhaustive-match.rs:12:11 + --> $DIR/non-exhaustive-match.rs:7:11 | LL | enum T { A, B } | --------------- @@ -11,17 +11,19 @@ LL | match x { T::B => { } } | ^ pattern `A` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `T` error[E0004]: non-exhaustive patterns: `false` not covered - --> $DIR/non-exhaustive-match.rs:13:11 + --> $DIR/non-exhaustive-match.rs:8:11 | LL | match true { | ^^^^ pattern `false` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `bool` error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/non-exhaustive-match.rs:16:11 + --> $DIR/non-exhaustive-match.rs:11:11 | LL | match Some(10) { | ^^^^^^^^ pattern `Some(_)` not covered @@ -32,25 +34,28 @@ LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | ---- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::option::Option` error[E0004]: non-exhaustive patterns: `(_, _, std::i32::MIN..=3i32)` and `(_, _, 5i32..=std::i32::MAX)` not covered - --> $DIR/non-exhaustive-match.rs:19:11 + --> $DIR/non-exhaustive-match.rs:14:11 | LL | match (2, 3, 4) { | ^^^^^^^^^ patterns `(_, _, std::i32::MIN..=3i32)` and `(_, _, 5i32..=std::i32::MAX)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(i32, i32, i32)` error[E0004]: non-exhaustive patterns: `(A, A)` not covered - --> $DIR/non-exhaustive-match.rs:23:11 + --> $DIR/non-exhaustive-match.rs:18:11 | LL | match (T::A, T::A) { | ^^^^^^^^^^^^ pattern `(A, A)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(T, T)` error[E0004]: non-exhaustive patterns: `B` not covered - --> $DIR/non-exhaustive-match.rs:27:11 + --> $DIR/non-exhaustive-match.rs:22:11 | LL | enum T { A, B } | --------------- @@ -62,22 +67,25 @@ LL | match T::A { | ^^^^ pattern `B` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `T` error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/non-exhaustive-match.rs:38:11 + --> $DIR/non-exhaustive-match.rs:33:11 | LL | match *vec { | ^^^^ pattern `[]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `[std::option::Option]` error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered - --> $DIR/non-exhaustive-match.rs:51:11 + --> $DIR/non-exhaustive-match.rs:46:11 | LL | match *vec { | ^^^^ pattern `[_, _, _, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `[f32]` error: aborting due to 8 previous errors diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr index 2a9fa07d22fe7..c9ed12aae5fbc 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr @@ -11,6 +11,7 @@ LL | match (Foo { first: true, second: None }) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { first: false, second: Some([_, _, _, _]) }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` error[E0004]: non-exhaustive patterns: `Red` not covered --> $DIR/non-exhaustive-pattern-witness.rs:23:11 @@ -27,6 +28,7 @@ LL | match Color::Red { | ^^^^^^^^^^ pattern `Red` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Color` error[E0004]: non-exhaustive patterns: `East`, `South` and `West` not covered --> $DIR/non-exhaustive-pattern-witness.rs:35:11 @@ -44,6 +46,7 @@ LL | match Direction::North { | ^^^^^^^^^^^^^^^^ patterns `East`, `South` and `West` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Direction` error[E0004]: non-exhaustive patterns: `Second`, `Third`, `Fourth` and 8 more not covered --> $DIR/non-exhaustive-pattern-witness.rs:46:11 @@ -57,6 +60,7 @@ LL | match ExcessiveEnum::First { | ^^^^^^^^^^^^^^^^^^^^ patterns `Second`, `Third`, `Fourth` and 8 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `ExcessiveEnum` error[E0004]: non-exhaustive patterns: `CustomRGBA { a: true, .. }` not covered --> $DIR/non-exhaustive-pattern-witness.rs:54:11 @@ -73,6 +77,7 @@ LL | match Color::Red { | ^^^^^^^^^^ pattern `CustomRGBA { a: true, .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Color` error[E0004]: non-exhaustive patterns: `[Second(true), Second(false)]` not covered --> $DIR/non-exhaustive-pattern-witness.rs:70:11 @@ -81,6 +86,7 @@ LL | match *x { | ^^ pattern `[Second(true), Second(false)]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `[Enum]` error[E0004]: non-exhaustive patterns: `((), false)` not covered --> $DIR/non-exhaustive-pattern-witness.rs:83:11 @@ -89,6 +95,7 @@ LL | match ((), false) { | ^^^^^^^^^^^ pattern `((), false)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `((), bool)` error: aborting due to 7 previous errors diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr index 0cf5d9cd5f12a..f5895c01599d7 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -3,6 +3,8 @@ error[E0005]: refutable pattern in function argument: `(_, _)` not covered | LL | fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } | ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered + | + = note: the matched value is of type `(isize, (std::option::Option, isize))` error[E0005]: refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered --> $DIR/refutable-pattern-errors.rs:7:9 @@ -12,6 +14,7 @@ LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `(i32, (std::option::Option, i32))` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { /* */ } diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr index 8666e6bb73ebf..c9d8cf43f95fd 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr @@ -3,6 +3,8 @@ error[E0005]: refutable pattern in function argument: `_` not covered | LL | let f = |3: isize| println!("hello"); | ^ pattern `_` not covered + | + = note: the matched value is of type `isize` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr index b3701efef3de2..8b85eaeda0acf 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr @@ -5,6 +5,7 @@ LL | match s2 { | ^^ pattern `&[false, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool; 2]` error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:12:11 @@ -13,6 +14,7 @@ LL | match s3 { | ^^ pattern `&[false, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool; 3]` error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:16:11 @@ -21,6 +23,7 @@ LL | match s10 { | ^^^ pattern `&[false, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool; 10]` error[E0004]: non-exhaustive patterns: `&[false, true]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:25:11 @@ -29,6 +32,7 @@ LL | match s2 { | ^^ pattern `&[false, true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool; 2]` error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:30:11 @@ -37,6 +41,7 @@ LL | match s3 { | ^^ pattern `&[false, .., true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool; 3]` error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:35:11 @@ -45,6 +50,7 @@ LL | match s { | ^ pattern `&[false, .., true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:42:11 @@ -53,6 +59,7 @@ LL | match s { | ^ pattern `&[_, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:46:11 @@ -61,6 +68,7 @@ LL | match s { | ^ pattern `&[_, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:51:11 @@ -69,6 +77,7 @@ LL | match s { | ^ pattern `&[false, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[false, _, ..]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:56:11 @@ -77,6 +86,7 @@ LL | match s { | ^ pattern `&[false, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[_, .., false]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:62:11 @@ -85,6 +95,7 @@ LL | match s { | ^ pattern `&[_, .., false]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[_, _, .., true]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:69:11 @@ -93,6 +104,7 @@ LL | match s { | ^ pattern `&[_, _, .., true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[true, _, .., _]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:76:11 @@ -101,6 +113,7 @@ LL | match s { | ^ pattern `&[true, _, .., _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[..]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:85:11 @@ -109,6 +122,7 @@ LL | match s { | ^ pattern `&[..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[true]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:89:11 @@ -117,6 +131,7 @@ LL | match s { | ^ pattern `&[true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool]` error[E0004]: non-exhaustive patterns: `&[false]` not covered --> $DIR/slice-patterns-exhaustiveness.rs:97:11 @@ -125,6 +140,7 @@ LL | match s1 { | ^^ pattern `&[false]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[bool; 1]` error: aborting due to 16 previous errors diff --git a/src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr b/src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr index d6b5af1796403..23ff6c626f759 100644 --- a/src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr +++ b/src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr @@ -12,6 +12,7 @@ LL | match x { | ^ pattern `B { x: Some(_) }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `A` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr b/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr index bbdf9ceed23a2..ca8f67f3c8df2 100644 --- a/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr +++ b/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr @@ -8,6 +8,7 @@ LL | match x { | ^ pattern `Foo(_, _)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` error: aborting due to previous error diff --git a/src/test/ui/phantom-oibit.stderr b/src/test/ui/phantom-oibit.stderr index 7b6b105eb032b..fd0307f15c79a 100644 --- a/src/test/ui/phantom-oibit.stderr +++ b/src/test/ui/phantom-oibit.stderr @@ -2,40 +2,38 @@ error[E0277]: `T` cannot be shared between threads safely --> $DIR/phantom-oibit.rs:21:12 | LL | fn is_zen(_: T) {} - | ------ --- required by this bound in `is_zen` + | --- required by this bound in `is_zen` ... LL | is_zen(x) | ^ `T` cannot be shared between threads safely | = help: the trait `std::marker::Sync` is not implemented for `T` -help: consider restricting this type parameter with `T: std::marker::Sync` - --> $DIR/phantom-oibit.rs:20:13 - | -LL | fn not_sync(x: Guard) { - | ^ = note: required because of the requirements on the impl of `Zen` for `&T` = note: required because it appears within the type `std::marker::PhantomData<&T>` = note: required because it appears within the type `Guard<'_, T>` +help: consider restricting type parameter `T` + | +LL | fn not_sync(x: Guard) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be shared between threads safely --> $DIR/phantom-oibit.rs:26:12 | LL | fn is_zen(_: T) {} - | ------ --- required by this bound in `is_zen` + | --- required by this bound in `is_zen` ... LL | is_zen(x) | ^ `T` cannot be shared between threads safely | = help: the trait `std::marker::Sync` is not implemented for `T` -help: consider restricting this type parameter with `T: std::marker::Sync` - --> $DIR/phantom-oibit.rs:25:20 - | -LL | fn nested_not_sync(x: Nested>) { - | ^ = note: required because of the requirements on the impl of `Zen` for `&T` = note: required because it appears within the type `std::marker::PhantomData<&T>` = note: required because it appears within the type `Guard<'_, T>` = note: required because it appears within the type `Nested>` +help: consider restricting type parameter `T` + | +LL | fn nested_not_sync(x: Nested>) { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/precise_pointer_size_matching.stderr b/src/test/ui/precise_pointer_size_matching.stderr index 2c2c2aa04c233..91ea323f07bb2 100644 --- a/src/test/ui/precise_pointer_size_matching.stderr +++ b/src/test/ui/precise_pointer_size_matching.stderr @@ -5,6 +5,7 @@ LL | match 0isize { | ^^^^^^ patterns `std::isize::MIN..=-6isize` and `21isize..=std::isize::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `isize` error[E0004]: non-exhaustive patterns: `0usize` and `21usize..=std::usize::MAX` not covered --> $DIR/precise_pointer_size_matching.rs:29:11 @@ -13,6 +14,7 @@ LL | match 0usize { | ^^^^^^ patterns `0usize` and `21usize..=std::usize::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `usize` error: aborting due to 2 previous errors diff --git a/src/test/ui/privacy/associated-item-privacy-trait.rs b/src/test/ui/privacy/associated-item-privacy-trait.rs index 03347d5b99a31..b1482bc040f53 100644 --- a/src/test/ui/privacy/associated-item-privacy-trait.rs +++ b/src/test/ui/privacy/associated-item-privacy-trait.rs @@ -21,9 +21,9 @@ mod priv_trait { Pub.method(); //~^ ERROR type `for<'r> fn(&'r Self) {::method}` is private ::CONST; - //~^ ERROR associated constant `PrivTr::CONST` is private + //~^ ERROR associated constant `::CONST` is private let _: ::AssocTy; - //~^ ERROR associated type `PrivTr::AssocTy` is private + //~^ ERROR associated type `::AssocTy` is private pub type InSignatureTy = ::AssocTy; //~^ ERROR trait `priv_trait::PrivTr` is private pub trait InSignatureTr: PrivTr {} @@ -115,7 +115,7 @@ mod priv_parent_substs { >::CONST; //~^ ERROR type `priv_parent_substs::Priv` is private - let _: ::AssocTy; // FIXME no longer an error?! + let _: ::AssocTy; // FIXME no longer an error?! let _: >::AssocTy; //~^ ERROR type `priv_parent_substs::Priv` is private let _: >::AssocTy; diff --git a/src/test/ui/privacy/associated-item-privacy-trait.stderr b/src/test/ui/privacy/associated-item-privacy-trait.stderr index c30cc947d4508..b9f3e35d72261 100644 --- a/src/test/ui/privacy/associated-item-privacy-trait.stderr +++ b/src/test/ui/privacy/associated-item-privacy-trait.stderr @@ -31,7 +31,7 @@ LL | priv_trait::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: associated constant `PrivTr::CONST` is private +error: associated constant `::CONST` is private --> $DIR/associated-item-privacy-trait.rs:23:9 | LL | ::CONST; @@ -42,7 +42,7 @@ LL | priv_trait::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: associated type `PrivTr::AssocTy` is private +error: associated type `::AssocTy` is private --> $DIR/associated-item-privacy-trait.rs:25:16 | LL | let _: ::AssocTy; diff --git a/src/test/ui/privacy/privacy-ns1.stderr b/src/test/ui/privacy/privacy-ns1.stderr index 66e9b78f6764a..4d2af735fa6b9 100644 --- a/src/test/ui/privacy/privacy-ns1.stderr +++ b/src/test/ui/privacy/privacy-ns1.stderr @@ -11,7 +11,7 @@ help: a unit struct with a similar name exists | LL | Baz(); | ^^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use foo1::Bar; | @@ -33,7 +33,7 @@ help: a unit struct with a similar name exists | LL | Baz(); | ^^^ -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use foo1::Bar; | @@ -55,7 +55,7 @@ help: a struct with a similar name exists | LL | let _x: Box; | ^^^ -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use foo1::Bar; | @@ -63,10 +63,6 @@ LL | use foo2::Bar; | LL | use foo3::Bar; | -help: you might be missing a type parameter - | -LL | fn test_glob3() { - | ^^^^^ error[E0107]: wrong number of const arguments: expected 0, found 1 --> $DIR/privacy-ns1.rs:35:17 diff --git a/src/test/ui/privacy/privacy-ns2.stderr b/src/test/ui/privacy/privacy-ns2.stderr index 0c826147a1ca7..f1aa523742ae4 100644 --- a/src/test/ui/privacy/privacy-ns2.stderr +++ b/src/test/ui/privacy/privacy-ns2.stderr @@ -4,7 +4,7 @@ error[E0423]: expected function, tuple struct or tuple variant, found trait `Bar LL | Bar(); | ^^^ not a function, tuple struct or tuple variant | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use foo1::Bar; | @@ -26,7 +26,7 @@ help: a unit struct with a similar name exists | LL | Baz(); | ^^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use foo1::Bar; | @@ -45,7 +45,7 @@ help: use `=` if you meant to assign | LL | let _x = Bar(); | ^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use foo1::Bar; | diff --git a/src/test/ui/privacy/privacy-sanity.rs b/src/test/ui/privacy/privacy-sanity.rs index f83b24c49a10a..8bbf1ab5d1f30 100644 --- a/src/test/ui/privacy/privacy-sanity.rs +++ b/src/test/ui/privacy/privacy-sanity.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] pub trait Tr { fn f(); diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.stderr b/src/test/ui/privacy/private-in-public-assoc-ty.stderr index c57073a004d8f..dd2ea7481f331 100644 --- a/src/test/ui/privacy/private-in-public-assoc-ty.stderr +++ b/src/test/ui/privacy/private-in-public-assoc-ty.stderr @@ -89,7 +89,7 @@ LL | trait PrivTr {} LL | type Exist = impl PrivTr; | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait -error: aborting due to 5 previous errors +error: aborting due to 5 previous errors; 3 warnings emitted Some errors have detailed explanations: E0445, E0446. For more information about an error, try `rustc --explain E0445`. diff --git a/src/test/ui/privacy/private-in-public-non-principal-2.rs b/src/test/ui/privacy/private-in-public-non-principal-2.rs index 8a59073fa6c7e..cd3d609ca3ea6 100644 --- a/src/test/ui/privacy/private-in-public-non-principal-2.rs +++ b/src/test/ui/privacy/private-in-public-non-principal-2.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] #[allow(private_in_public)] mod m { diff --git a/src/test/ui/privacy/private-in-public-non-principal-2.stderr b/src/test/ui/privacy/private-in-public-non-principal-2.stderr index 1b6d143b93217..7850694aab207 100644 --- a/src/test/ui/privacy/private-in-public-non-principal-2.stderr +++ b/src/test/ui/privacy/private-in-public-non-principal-2.stderr @@ -1,5 +1,5 @@ error: trait `m::PrivNonPrincipal` is private - --> $DIR/private-in-public-non-principal-2.rs:11:5 + --> $DIR/private-in-public-non-principal-2.rs:12:5 | LL | m::leak_dyn_nonprincipal(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ private trait diff --git a/src/test/ui/privacy/private-in-public-non-principal.rs b/src/test/ui/privacy/private-in-public-non-principal.rs index b306849ac8e5e..aa946f5c0ac05 100644 --- a/src/test/ui/privacy/private-in-public-non-principal.rs +++ b/src/test/ui/privacy/private-in-public-non-principal.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] pub trait PubPrincipal {} auto trait PrivNonPrincipal {} diff --git a/src/test/ui/privacy/private-in-public-non-principal.stderr b/src/test/ui/privacy/private-in-public-non-principal.stderr index 778c98671ad4d..43469f74538ea 100644 --- a/src/test/ui/privacy/private-in-public-non-principal.stderr +++ b/src/test/ui/privacy/private-in-public-non-principal.stderr @@ -1,5 +1,5 @@ warning: private trait `PrivNonPrincipal` in public interface (error E0445) - --> $DIR/private-in-public-non-principal.rs:6:1 + --> $DIR/private-in-public-non-principal.rs:7:1 | LL | pub fn leak_dyn_nonprincipal() -> Box { loop {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,16 +9,16 @@ LL | pub fn leak_dyn_nonprincipal() -> Box = note: for more information, see issue #34537 error: missing documentation for an associated function - --> $DIR/private-in-public-non-principal.rs:13:9 + --> $DIR/private-in-public-non-principal.rs:14:9 | LL | pub fn check_doc_lint() {} | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/private-in-public-non-principal.rs:10:8 + --> $DIR/private-in-public-non-principal.rs:11:8 | LL | #[deny(missing_docs)] | ^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/privacy/private-in-public-warn.stderr b/src/test/ui/privacy/private-in-public-warn.stderr index 079331bffd228..38081295e7eab 100644 --- a/src/test/ui/privacy/private-in-public-warn.stderr +++ b/src/test/ui/privacy/private-in-public-warn.stderr @@ -356,6 +356,6 @@ help: the clause will not be checked when the type alias is used, and should be LL | pub type Alias = T; | -- -error: aborting due to 36 previous errors +error: aborting due to 36 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0446`. diff --git a/src/test/ui/proc-macro/attr-invalid-exprs.rs b/src/test/ui/proc-macro/attr-invalid-exprs.rs index fab98f0ce5ebe..9dcffc3405ead 100644 --- a/src/test/ui/proc-macro/attr-invalid-exprs.rs +++ b/src/test/ui/proc-macro/attr-invalid-exprs.rs @@ -1,8 +1,9 @@ -// aux-build:attr-stmt-expr.rs - //! Attributes producing expressions in invalid locations -#![feature(stmt_expr_attributes, proc_macro_hygiene)] +// aux-build:attr-stmt-expr.rs + +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] extern crate attr_stmt_expr; use attr_stmt_expr::{duplicate, no_output}; diff --git a/src/test/ui/proc-macro/attr-invalid-exprs.stderr b/src/test/ui/proc-macro/attr-invalid-exprs.stderr index 49fe0bd0fcfe2..bcb54df0ecac7 100644 --- a/src/test/ui/proc-macro/attr-invalid-exprs.stderr +++ b/src/test/ui/proc-macro/attr-invalid-exprs.stderr @@ -1,11 +1,11 @@ error: expected expression, found end of macro arguments - --> $DIR/attr-invalid-exprs.rs:11:13 + --> $DIR/attr-invalid-exprs.rs:12:13 | LL | let _ = #[no_output] "Hello, world!"; | ^^^^^^^^^^^^ error: macro expansion ignores token `,` and any following - --> $DIR/attr-invalid-exprs.rs:14:13 + --> $DIR/attr-invalid-exprs.rs:15:13 | LL | let _ = #[duplicate] "Hello, world!"; | ^^^^^^^^^^^^- help: you might be missing a semicolon here: `;` @@ -15,7 +15,7 @@ LL | let _ = #[duplicate] "Hello, world!"; = note: the usage of `duplicate!` is likely invalid in expression context error: macro expansion ignores token `,` and any following - --> $DIR/attr-invalid-exprs.rs:23:9 + --> $DIR/attr-invalid-exprs.rs:24:9 | LL | #[duplicate] | ^^^^^^^^^^^^- help: you might be missing a semicolon here: `;` diff --git a/src/test/ui/proc-macro/attributes-included.rs b/src/test/ui/proc-macro/attributes-included.rs index 4769607ff395d..95e8e10a3ece9 100644 --- a/src/test/ui/proc-macro/attributes-included.rs +++ b/src/test/ui/proc-macro/attributes-included.rs @@ -1,5 +1,5 @@ // aux-build:attributes-included.rs -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(unused)] diff --git a/src/test/ui/proc-macro/attributes-included.stderr b/src/test/ui/proc-macro/attributes-included.stderr index bfbcf68b6c7e6..72c88d5d8b752 100644 --- a/src/test/ui/proc-macro/attributes-included.stderr +++ b/src/test/ui/proc-macro/attributes-included.stderr @@ -11,3 +11,5 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` +warning: 1 warning emitted + diff --git a/src/test/ui/proc-macro/attributes-on-definitions.stderr b/src/test/ui/proc-macro/attributes-on-definitions.stderr index c61e043b22971..3e6b8f6a435d8 100644 --- a/src/test/ui/proc-macro/attributes-on-definitions.stderr +++ b/src/test/ui/proc-macro/attributes-on-definitions.stderr @@ -6,3 +6,5 @@ LL | attributes_on_definitions::with_attrs!(); | = note: `#[warn(deprecated)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/proc-macro/attributes-on-modules-fail.stderr b/src/test/ui/proc-macro/attributes-on-modules-fail.stderr index f0ab107e15178..b37f1bd393c6f 100644 --- a/src/test/ui/proc-macro/attributes-on-modules-fail.stderr +++ b/src/test/ui/proc-macro/attributes-on-modules-fail.stderr @@ -50,7 +50,7 @@ help: a type alias with a similar name exists | LL | type A = A; | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this struct | LL | use Y; | @@ -65,7 +65,7 @@ help: a type alias with a similar name exists | LL | type A = A; | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this struct | LL | use m::X; | diff --git a/src/test/ui/proc-macro/auxiliary/count_compound_ops.rs b/src/test/ui/proc-macro/auxiliary/count_compound_ops.rs index e09622e48bf29..3a656d6485e1c 100644 --- a/src/test/ui/proc-macro/auxiliary/count_compound_ops.rs +++ b/src/test/ui/proc-macro/auxiliary/count_compound_ops.rs @@ -1,7 +1,7 @@ // force-host // no-prefer-dynamic -#![feature(proc_macro_hygiene, proc_macro_quote)] +#![feature(proc_macro_quote)] #![crate_type = "proc-macro"] extern crate proc_macro; diff --git a/src/test/ui/proc-macro/auxiliary/duplicate.rs b/src/test/ui/proc-macro/auxiliary/duplicate.rs new file mode 100644 index 0000000000000..b8f82b46f0945 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/duplicate.rs @@ -0,0 +1,32 @@ +// force-host +// no-prefer-dynamic + +#![deny(unused)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_attribute] +pub fn duplicate(attr: TokenStream, item: TokenStream) -> TokenStream { + let mut new_name = Some(attr.into_iter().nth(0).unwrap()); + let mut encountered_idents = 0; + let input = item.to_string(); + let ret = item + .into_iter() + .map(move |token| match token { + TokenTree::Ident(_) if encountered_idents == 1 => { + encountered_idents += 1; + new_name.take().unwrap() + } + TokenTree::Ident(_) => { + encountered_idents += 1; + token + } + _ => token, + }) + .collect::(); + let mut input_again = input.parse::().unwrap(); + input_again.extend(ret); + input_again +} diff --git a/src/test/ui/proc-macro/auxiliary/generate-dollar-ident.rs b/src/test/ui/proc-macro/auxiliary/generate-dollar-ident.rs index c9f0664c3a3ac..3f3e12eed6c6f 100644 --- a/src/test/ui/proc-macro/auxiliary/generate-dollar-ident.rs +++ b/src/test/ui/proc-macro/auxiliary/generate-dollar-ident.rs @@ -1,7 +1,6 @@ // force-host // no-prefer-dynamic -#![feature(proc_macro_hygiene)] #![feature(proc_macro_quote)] #![crate_type = "proc-macro"] diff --git a/src/test/ui/proc-macro/auxiliary/hygiene_example_codegen.rs b/src/test/ui/proc-macro/auxiliary/hygiene_example_codegen.rs index 5e50a6e916f41..2bd4d33360f8a 100644 --- a/src/test/ui/proc-macro/auxiliary/hygiene_example_codegen.rs +++ b/src/test/ui/proc-macro/auxiliary/hygiene_example_codegen.rs @@ -1,7 +1,7 @@ // force-host // no-prefer-dynamic -#![feature(proc_macro_quote, proc_macro_hygiene)] +#![feature(proc_macro_quote)] #![crate_type = "proc-macro"] extern crate proc_macro as proc_macro_renamed; // This does not break `quote!` diff --git a/src/test/ui/proc-macro/auxiliary/is-available.rs b/src/test/ui/proc-macro/auxiliary/is-available.rs new file mode 100644 index 0000000000000..0caf186db1d5f --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/is-available.rs @@ -0,0 +1,14 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_is_available)] + +extern crate proc_macro; + +use proc_macro::{Literal, TokenStream, TokenTree}; + +#[proc_macro] +pub fn from_inside_proc_macro(_input: TokenStream) -> TokenStream { + proc_macro::is_available().to_string().parse().unwrap() +} diff --git a/src/test/ui/proc-macro/auxiliary/mixed-site-span.rs b/src/test/ui/proc-macro/auxiliary/mixed-site-span.rs index dea5ea04aa850..c2a4987004860 100644 --- a/src/test/ui/proc-macro/auxiliary/mixed-site-span.rs +++ b/src/test/ui/proc-macro/auxiliary/mixed-site-span.rs @@ -1,8 +1,6 @@ // force-host // no-prefer-dynamic -#![feature(proc_macro_hygiene)] -#![feature(proc_macro_mixed_site)] #![feature(proc_macro_quote)] #![crate_type = "proc-macro"] diff --git a/src/test/ui/proc-macro/auxiliary/resolved-located-at.rs b/src/test/ui/proc-macro/auxiliary/resolved-located-at.rs new file mode 100644 index 0000000000000..db660824fbb0b --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/resolved-located-at.rs @@ -0,0 +1,31 @@ +// force-host +// no-prefer-dynamic + +#![feature(proc_macro_def_site)] +#![feature(proc_macro_diagnostic)] +#![feature(proc_macro_quote)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub fn resolve_located_at(input: TokenStream) -> TokenStream { + match &*input.into_iter().collect::>() { + [a, b, ..] => { + // The error is reported at input `a`. + let mut diag = Diagnostic::new(Level::Error, "expected error"); + diag.set_spans(Span::def_site().located_at(a.span())); + diag.emit(); + + // Resolves to `struct S;` at def site, but the error is reported at input `b`. + let s = TokenTree::Ident(Ident::new("S", b.span().resolved_at(Span::def_site()))); + quote!({ + struct S; + + $s + }) + } + _ => panic!("unexpected input"), + } +} diff --git a/src/test/ui/proc-macro/bang-macro.rs b/src/test/ui/proc-macro/bang-macro.rs index 7073c71538cf7..92810791314df 100644 --- a/src/test/ui/proc-macro/bang-macro.rs +++ b/src/test/ui/proc-macro/bang-macro.rs @@ -1,8 +1,6 @@ // run-pass // aux-build:bang-macro.rs -#![feature(proc_macro_hygiene)] - extern crate bang_macro; use bang_macro::rewrite; diff --git a/src/test/ui/proc-macro/call-site.rs b/src/test/ui/proc-macro/call-site.rs index 096d0ec533a76..12c77250c0e72 100644 --- a/src/test/ui/proc-macro/call-site.rs +++ b/src/test/ui/proc-macro/call-site.rs @@ -1,13 +1,7 @@ -// run-pass - -#![allow(unused_variables)] -#![allow(unused_imports)] +// check-pass // aux-build:call-site.rs -#![feature(proc_macro_hygiene)] - extern crate call_site; -use call_site::*; fn main() { let x1 = 10; diff --git a/src/test/ui/proc-macro/count_compound_ops.rs b/src/test/ui/proc-macro/count_compound_ops.rs index 966ab616cdf0c..2cb8718448898 100644 --- a/src/test/ui/proc-macro/count_compound_ops.rs +++ b/src/test/ui/proc-macro/count_compound_ops.rs @@ -1,8 +1,6 @@ // run-pass // aux-build:count_compound_ops.rs -#![feature(proc_macro_hygiene)] - extern crate count_compound_ops; use count_compound_ops::count_compound_ops; diff --git a/src/test/ui/proc-macro/crt-static.rs b/src/test/ui/proc-macro/crt-static.rs index 90e3d422b3cbc..97f6265e3089b 100644 --- a/src/test/ui/proc-macro/crt-static.rs +++ b/src/test/ui/proc-macro/crt-static.rs @@ -3,6 +3,7 @@ // override -Ctarget-feature=-crt-static from compiletest // compile-flags: -Ctarget-feature= // ignore-wasm32 +// ignore-sgx no support for proc-macro crate type // build-pass #![crate_type = "proc-macro"] diff --git a/src/test/ui/proc-macro/debug/auxiliary/macro-dump-debug.rs b/src/test/ui/proc-macro/debug/auxiliary/macro-dump-debug.rs new file mode 100644 index 0000000000000..56ad0612f74bd --- /dev/null +++ b/src/test/ui/proc-macro/debug/auxiliary/macro-dump-debug.rs @@ -0,0 +1,15 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![crate_name = "macro_dump_debug"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn dump_debug(tokens: TokenStream) -> TokenStream { + eprintln!("{:?}", tokens); + eprintln!("{:#?}", tokens); + TokenStream::new() +} diff --git a/src/test/ui/proc-macro/debug/dump-debug.rs b/src/test/ui/proc-macro/debug/dump-debug.rs new file mode 100644 index 0000000000000..0ed36b690f49b --- /dev/null +++ b/src/test/ui/proc-macro/debug/dump-debug.rs @@ -0,0 +1,40 @@ +// run-pass +// aux-build:macro-dump-debug.rs + +extern crate macro_dump_debug; +use macro_dump_debug::dump_debug; + +dump_debug! { + ident // ident + r#ident // raw ident + , // alone punct + ==> // joint punct + () // empty group + [_] // nonempty group + + // unsuffixed literals + 0 + 1.0 + "S" + b"B" + r"R" + r##"R"## + br"BR" + br##"BR"## + 'C' + b'B' + + // suffixed literals + 0q + 1.0q + "S"q + b"B"q + r"R"q + r##"R"##q + br"BR"q + br##"BR"##q + 'C'q + b'B'q +} + +fn main() {} diff --git a/src/test/ui/proc-macro/debug/dump-debug.stderr b/src/test/ui/proc-macro/debug/dump-debug.stderr new file mode 100644 index 0000000000000..0aedefd4e6091 --- /dev/null +++ b/src/test/ui/proc-macro/debug/dump-debug.stderr @@ -0,0 +1,166 @@ +TokenStream [Ident { ident: "ident", span: #0 bytes(130..135) }, Ident { ident: "r#ident", span: #0 bytes(151..158) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(176..177) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(203..205) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(203..205) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(205..206) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(230..232) }, Group { delimiter: Bracket, stream: TokenStream [Ident { ident: "_", span: #0 bytes(258..259) }], span: #0 bytes(257..260) }, Literal { kind: Integer, symbol: "0", suffix: None, span: #0 bytes(315..316) }, Literal { kind: Float, symbol: "1.0", suffix: None, span: #0 bytes(321..324) }, Literal { kind: Str, symbol: "S", suffix: None, span: #0 bytes(329..332) }, Literal { kind: ByteStr, symbol: "B", suffix: None, span: #0 bytes(337..341) }, Literal { kind: StrRaw(0), symbol: "R", suffix: None, span: #0 bytes(346..350) }, Literal { kind: StrRaw(2), symbol: "R", suffix: None, span: #0 bytes(355..363) }, Literal { kind: ByteStrRaw(0), symbol: "BR", suffix: None, span: #0 bytes(368..374) }, Literal { kind: ByteStrRaw(2), symbol: "BR", suffix: None, span: #0 bytes(379..389) }, Literal { kind: Char, symbol: "C", suffix: None, span: #0 bytes(394..397) }, Literal { kind: Byte, symbol: "B", suffix: None, span: #0 bytes(402..406) }, Literal { kind: Integer, symbol: "0", suffix: Some("q"), span: #0 bytes(437..439) }, Literal { kind: Float, symbol: "1.0", suffix: Some("q"), span: #0 bytes(444..448) }, Literal { kind: Str, symbol: "S", suffix: Some("q"), span: #0 bytes(453..457) }, Literal { kind: ByteStr, symbol: "B", suffix: Some("q"), span: #0 bytes(462..467) }, Literal { kind: StrRaw(0), symbol: "R", suffix: Some("q"), span: #0 bytes(472..477) }, Literal { kind: StrRaw(2), symbol: "R", suffix: Some("q"), span: #0 bytes(482..491) }, Literal { kind: ByteStrRaw(0), symbol: "BR", suffix: Some("q"), span: #0 bytes(496..503) }, Literal { kind: ByteStrRaw(2), symbol: "BR", suffix: Some("q"), span: #0 bytes(508..519) }, Literal { kind: Char, symbol: "C", suffix: Some("q"), span: #0 bytes(524..528) }, Literal { kind: Byte, symbol: "B", suffix: Some("q"), span: #0 bytes(533..538) }] +TokenStream [ + Ident { + ident: "ident", + span: #0 bytes(130..135), + }, + Ident { + ident: "r#ident", + span: #0 bytes(151..158), + }, + Punct { + ch: ',', + spacing: Alone, + span: #0 bytes(176..177), + }, + Punct { + ch: '=', + spacing: Joint, + span: #0 bytes(203..205), + }, + Punct { + ch: '=', + spacing: Joint, + span: #0 bytes(203..205), + }, + Punct { + ch: '>', + spacing: Alone, + span: #0 bytes(205..206), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: #0 bytes(230..232), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "_", + span: #0 bytes(258..259), + }, + ], + span: #0 bytes(257..260), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: #0 bytes(315..316), + }, + Literal { + kind: Float, + symbol: "1.0", + suffix: None, + span: #0 bytes(321..324), + }, + Literal { + kind: Str, + symbol: "S", + suffix: None, + span: #0 bytes(329..332), + }, + Literal { + kind: ByteStr, + symbol: "B", + suffix: None, + span: #0 bytes(337..341), + }, + Literal { + kind: StrRaw(0), + symbol: "R", + suffix: None, + span: #0 bytes(346..350), + }, + Literal { + kind: StrRaw(2), + symbol: "R", + suffix: None, + span: #0 bytes(355..363), + }, + Literal { + kind: ByteStrRaw(0), + symbol: "BR", + suffix: None, + span: #0 bytes(368..374), + }, + Literal { + kind: ByteStrRaw(2), + symbol: "BR", + suffix: None, + span: #0 bytes(379..389), + }, + Literal { + kind: Char, + symbol: "C", + suffix: None, + span: #0 bytes(394..397), + }, + Literal { + kind: Byte, + symbol: "B", + suffix: None, + span: #0 bytes(402..406), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: Some("q"), + span: #0 bytes(437..439), + }, + Literal { + kind: Float, + symbol: "1.0", + suffix: Some("q"), + span: #0 bytes(444..448), + }, + Literal { + kind: Str, + symbol: "S", + suffix: Some("q"), + span: #0 bytes(453..457), + }, + Literal { + kind: ByteStr, + symbol: "B", + suffix: Some("q"), + span: #0 bytes(462..467), + }, + Literal { + kind: StrRaw(0), + symbol: "R", + suffix: Some("q"), + span: #0 bytes(472..477), + }, + Literal { + kind: StrRaw(2), + symbol: "R", + suffix: Some("q"), + span: #0 bytes(482..491), + }, + Literal { + kind: ByteStrRaw(0), + symbol: "BR", + suffix: Some("q"), + span: #0 bytes(496..503), + }, + Literal { + kind: ByteStrRaw(2), + symbol: "BR", + suffix: Some("q"), + span: #0 bytes(508..519), + }, + Literal { + kind: Char, + symbol: "C", + suffix: Some("q"), + span: #0 bytes(524..528), + }, + Literal { + kind: Byte, + symbol: "B", + suffix: Some("q"), + span: #0 bytes(533..538), + }, +] diff --git a/src/test/ui/proc-macro/expand-to-unstable-2.stderr b/src/test/ui/proc-macro/expand-to-unstable-2.stderr index 19144b210a127..5974fa4c554ca 100644 --- a/src/test/ui/proc-macro/expand-to-unstable-2.stderr +++ b/src/test/ui/proc-macro/expand-to-unstable-2.stderr @@ -1,12 +1,10 @@ -error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler +error: attributes starting with `rustc` are reserved for use by the `rustc` compiler --> $DIR/expand-to-unstable-2.rs:10:10 | LL | #[derive(Unstable)] | ^^^^^^^^ | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/proc-macro/generate-mod.stderr b/src/test/ui/proc-macro/generate-mod.stderr index d239097263428..9b946b5e2449e 100644 --- a/src/test/ui/proc-macro/generate-mod.stderr +++ b/src/test/ui/proc-macro/generate-mod.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `FromOutside` in this scope LL | generate_mod::check!(); | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope | - = note: possible candidate is found in another module, you can import it into scope: + = note: consider importing this struct: FromOutside = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -14,7 +14,7 @@ error[E0412]: cannot find type `Outer` in this scope LL | generate_mod::check!(); | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope | - = note: possible candidate is found in another module, you can import it into scope: + = note: consider importing this struct: Outer = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -24,7 +24,7 @@ error[E0412]: cannot find type `FromOutside` in this scope LL | #[generate_mod::check_attr] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope | - = note: possible candidate is found in another module, you can import it into scope: + = note: consider importing this struct: FromOutside = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error[E0412]: cannot find type `OuterAttr` in this scope LL | #[generate_mod::check_attr] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope | - = note: possible candidate is found in another module, you can import it into scope: + = note: consider importing this struct: OuterAttr = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -75,6 +75,6 @@ LL | #[derive(generate_mod::CheckDerive)] = 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 #50504 -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 4 warnings emitted For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/proc-macro/hygiene_example.rs b/src/test/ui/proc-macro/hygiene_example.rs index 56ea9daacc3b9..346ed1207cde5 100644 --- a/src/test/ui/proc-macro/hygiene_example.rs +++ b/src/test/ui/proc-macro/hygiene_example.rs @@ -1,11 +1,7 @@ -// run-pass - -#![allow(unused_macros)] +// check-pass // aux-build:hygiene_example_codegen.rs // aux-build:hygiene_example.rs -#![feature(proc_macro_hygiene)] - extern crate hygiene_example; use hygiene_example::hello; diff --git a/src/test/ui/proc-macro/is-available.rs b/src/test/ui/proc-macro/is-available.rs new file mode 100644 index 0000000000000..52f7e00d57280 --- /dev/null +++ b/src/test/ui/proc-macro/is-available.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(proc_macro_is_available)] + +extern crate proc_macro; + +// aux-build:is-available.rs +extern crate is_available; + +fn main() { + let a = proc_macro::is_available(); + let b = is_available::from_inside_proc_macro!(); + let c = proc_macro::is_available(); + assert!(!a); + assert!(b); + assert!(!c); +} diff --git a/src/test/ui/proc-macro/lints_in_proc_macros.rs b/src/test/ui/proc-macro/lints_in_proc_macros.rs index 8d2957ef5da74..377a1f25b635c 100644 --- a/src/test/ui/proc-macro/lints_in_proc_macros.rs +++ b/src/test/ui/proc-macro/lints_in_proc_macros.rs @@ -1,8 +1,5 @@ // aux-build:bang_proc_macro2.rs -#![feature(proc_macro_hygiene)] -#![allow(unused_macros)] - extern crate bang_proc_macro2; use bang_proc_macro2::bang_proc_macro2; diff --git a/src/test/ui/proc-macro/lints_in_proc_macros.stderr b/src/test/ui/proc-macro/lints_in_proc_macros.stderr index df9d7e1efe3de..03c4d19268ceb 100644 --- a/src/test/ui/proc-macro/lints_in_proc_macros.stderr +++ b/src/test/ui/proc-macro/lints_in_proc_macros.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `foobar2` in this scope - --> $DIR/lints_in_proc_macros.rs:12:5 + --> $DIR/lints_in_proc_macros.rs:9:5 | LL | bang_proc_macro2!(); | ^^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `foobar` diff --git a/src/test/ui/proc-macro/macro-crate-multi-decorator.rs b/src/test/ui/proc-macro/macro-crate-multi-decorator.rs new file mode 100644 index 0000000000000..ec57dec14ed20 --- /dev/null +++ b/src/test/ui/proc-macro/macro-crate-multi-decorator.rs @@ -0,0 +1,41 @@ +// The duplicate macro will create a copy of the item with the given identifier. + +// check-pass +// aux-build:duplicate.rs + +#[macro_use] +extern crate duplicate; + +#[duplicate(MyCopy)] +struct MyStruct { + number: i32, +} + +trait TestTrait { + #[duplicate(TestType2)] + type TestType; + + #[duplicate(required_fn2)] + fn required_fn(&self); + + #[duplicate(provided_fn2)] + fn provided_fn(&self) {} +} + +impl TestTrait for MyStruct { + #[duplicate(TestType2)] + type TestType = f64; + + #[duplicate(required_fn2)] + fn required_fn(&self) {} +} + +fn main() { + let s = MyStruct { number: 42 }; + s.required_fn(); + s.required_fn2(); + s.provided_fn(); + s.provided_fn2(); + + let s = MyCopy { number: 42 }; +} diff --git a/src/test/ui/proc-macro/macro-use-bang.rs b/src/test/ui/proc-macro/macro-use-bang.rs index 9d30f48846dba..4a0bf0b2f63ba 100644 --- a/src/test/ui/proc-macro/macro-use-bang.rs +++ b/src/test/ui/proc-macro/macro-use-bang.rs @@ -1,8 +1,6 @@ // build-pass (FIXME(62277): could be check-pass?) // aux-build:test-macros.rs -#![feature(proc_macro_hygiene)] - #[macro_use] extern crate test_macros; diff --git a/src/test/ui/proc-macro/mixed-site-span.rs b/src/test/ui/proc-macro/mixed-site-span.rs index 69c32a96ca055..0083846568e29 100644 --- a/src/test/ui/proc-macro/mixed-site-span.rs +++ b/src/test/ui/proc-macro/mixed-site-span.rs @@ -2,8 +2,6 @@ // aux-build:mixed-site-span.rs -#![feature(proc_macro_hygiene)] - #[macro_use] extern crate mixed_site_span; diff --git a/src/test/ui/proc-macro/mixed-site-span.stderr b/src/test/ui/proc-macro/mixed-site-span.stderr index 30a4cd7c116a6..2b851a76f6a0f 100644 --- a/src/test/ui/proc-macro/mixed-site-span.stderr +++ b/src/test/ui/proc-macro/mixed-site-span.stderr @@ -1,5 +1,5 @@ error[E0426]: use of undeclared label `'label_use` - --> $DIR/mixed-site-span.rs:15:9 + --> $DIR/mixed-site-span.rs:13:9 | LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^^ undeclared label `'label_use` @@ -7,7 +7,7 @@ LL | proc_macro_rules!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_use` in this scope - --> $DIR/mixed-site-span.rs:15:9 + --> $DIR/mixed-site-span.rs:13:9 | LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^^ not found in this scope @@ -15,19 +15,19 @@ LL | proc_macro_rules!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_def` in this scope - --> $DIR/mixed-site-span.rs:19:9 + --> $DIR/mixed-site-span.rs:17:9 | LL | local_def; | ^^^^^^^^^ not found in this scope error[E0412]: cannot find type `ItemUse` in crate `$crate` - --> $DIR/mixed-site-span.rs:26:1 + --> $DIR/mixed-site-span.rs:24:1 | LL | pass_dollar_crate!(); | ^^^^^^^^^^^^^^^^^^^^^ not found in `$crate` | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -help: possible candidate is found in another module, you can import it into scope +help: consider importing this struct | LL | use ItemUse; | diff --git a/src/test/ui/proc-macro/multispan.rs b/src/test/ui/proc-macro/multispan.rs index d06947761e3ff..e9e0349f2c2d7 100644 --- a/src/test/ui/proc-macro/multispan.rs +++ b/src/test/ui/proc-macro/multispan.rs @@ -1,7 +1,5 @@ // aux-build:multispan.rs -#![feature(proc_macro_hygiene)] - extern crate multispan; use multispan::hello; diff --git a/src/test/ui/proc-macro/multispan.stderr b/src/test/ui/proc-macro/multispan.stderr index 4405278528eeb..8dc2f3d12afd6 100644 --- a/src/test/ui/proc-macro/multispan.stderr +++ b/src/test/ui/proc-macro/multispan.stderr @@ -1,89 +1,89 @@ error: hello to you, too! - --> $DIR/multispan.rs:14:5 + --> $DIR/multispan.rs:12:5 | LL | hello!(hi); | ^^^^^^^^^^^ | note: found these 'hi's - --> $DIR/multispan.rs:14:12 + --> $DIR/multispan.rs:12:12 | LL | hello!(hi); | ^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/multispan.rs:17:5 + --> $DIR/multispan.rs:15:5 | LL | hello!(hi hi); | ^^^^^^^^^^^^^^ | note: found these 'hi's - --> $DIR/multispan.rs:17:12 + --> $DIR/multispan.rs:15:12 | LL | hello!(hi hi); | ^^ ^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/multispan.rs:20:5 + --> $DIR/multispan.rs:18:5 | LL | hello!(hi hi hi); | ^^^^^^^^^^^^^^^^^ | note: found these 'hi's - --> $DIR/multispan.rs:20:12 + --> $DIR/multispan.rs:18:12 | LL | hello!(hi hi hi); | ^^ ^^ ^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/multispan.rs:23:5 + --> $DIR/multispan.rs:21:5 | LL | hello!(hi hey hi yo hi beep beep hi hi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's - --> $DIR/multispan.rs:23:12 + --> $DIR/multispan.rs:21:12 | LL | hello!(hi hey hi yo hi beep beep hi hi); | ^^ ^^ ^^ ^^ ^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/multispan.rs:24:5 + --> $DIR/multispan.rs:22:5 | LL | hello!(hi there, hi how are you? hi... hi.); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's - --> $DIR/multispan.rs:24:12 + --> $DIR/multispan.rs:22:12 | LL | hello!(hi there, hi how are you? hi... hi.); | ^^ ^^ ^^ ^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/multispan.rs:25:5 + --> $DIR/multispan.rs:23:5 | LL | hello!(whoah. hi di hi di ho); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's - --> $DIR/multispan.rs:25:19 + --> $DIR/multispan.rs:23:19 | LL | hello!(whoah. hi di hi di ho); | ^^ ^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/multispan.rs:26:5 + --> $DIR/multispan.rs:24:5 | LL | hello!(hi good hi and good bye); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's - --> $DIR/multispan.rs:26:12 + --> $DIR/multispan.rs:24:12 | LL | hello!(hi good hi and good bye); | ^^ ^^ diff --git a/src/test/ui/proc-macro/negative-token.rs b/src/test/ui/proc-macro/negative-token.rs index 3d018fe60a134..2ed3cbc08cd51 100644 --- a/src/test/ui/proc-macro/negative-token.rs +++ b/src/test/ui/proc-macro/negative-token.rs @@ -1,8 +1,6 @@ // run-pass // aux-build:negative-token.rs -#![feature(proc_macro_hygiene)] - extern crate negative_token; use negative_token::*; diff --git a/src/test/ui/proc-macro/no-macro-use-attr.stderr b/src/test/ui/proc-macro/no-macro-use-attr.stderr index 27943a3f7bf8c..1831300a0d97c 100644 --- a/src/test/ui/proc-macro/no-macro-use-attr.stderr +++ b/src/test/ui/proc-macro/no-macro-use-attr.stderr @@ -16,5 +16,5 @@ error: fatal error triggered by #[rustc_error] LL | fn main() {} | ^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/proc-macro/parent-source-spans.rs b/src/test/ui/proc-macro/parent-source-spans.rs index 95a3f96951270..354657db4db38 100644 --- a/src/test/ui/proc-macro/parent-source-spans.rs +++ b/src/test/ui/proc-macro/parent-source-spans.rs @@ -1,9 +1,6 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // aux-build:parent-source-spans.rs -#![feature(decl_macro, proc_macro_hygiene)] + +#![feature(decl_macro)] extern crate parent_source_spans; diff --git a/src/test/ui/proc-macro/parent-source-spans.stderr b/src/test/ui/proc-macro/parent-source-spans.stderr index 254f87751fd8e..45a3f31e3ddcf 100644 --- a/src/test/ui/proc-macro/parent-source-spans.stderr +++ b/src/test/ui/proc-macro/parent-source-spans.stderr @@ -1,5 +1,5 @@ error: first final: "hello" - --> $DIR/parent-source-spans.rs:19:12 + --> $DIR/parent-source-spans.rs:16:12 | LL | three!($a, $b); | ^^ @@ -10,7 +10,7 @@ LL | one!("hello", "world"); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: second final: "world" - --> $DIR/parent-source-spans.rs:19:16 + --> $DIR/parent-source-spans.rs:16:16 | LL | three!($a, $b); | ^^ @@ -21,7 +21,7 @@ LL | one!("hello", "world"); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: first parent: "hello" - --> $DIR/parent-source-spans.rs:13:5 + --> $DIR/parent-source-spans.rs:10:5 | LL | two!($a, $b); | ^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | one!("hello", "world"); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: second parent: "world" - --> $DIR/parent-source-spans.rs:13:5 + --> $DIR/parent-source-spans.rs:10:5 | LL | two!($a, $b); | ^^^^^^^^^^^^^ @@ -43,31 +43,31 @@ LL | one!("hello", "world"); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: first grandparent: "hello" - --> $DIR/parent-source-spans.rs:39:5 + --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: second grandparent: "world" - --> $DIR/parent-source-spans.rs:39:5 + --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: first source: "hello" - --> $DIR/parent-source-spans.rs:39:5 + --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: second source: "world" - --> $DIR/parent-source-spans.rs:39:5 + --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: first final: "yay" - --> $DIR/parent-source-spans.rs:19:12 + --> $DIR/parent-source-spans.rs:16:12 | LL | three!($a, $b); | ^^ @@ -78,7 +78,7 @@ LL | two!("yay", "rust"); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: second final: "rust" - --> $DIR/parent-source-spans.rs:19:16 + --> $DIR/parent-source-spans.rs:16:16 | LL | three!($a, $b); | ^^ @@ -89,55 +89,55 @@ LL | two!("yay", "rust"); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: first parent: "yay" - --> $DIR/parent-source-spans.rs:45:5 + --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^^ error: second parent: "rust" - --> $DIR/parent-source-spans.rs:45:5 + --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^^ error: first source: "yay" - --> $DIR/parent-source-spans.rs:45:5 + --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^^ error: second source: "rust" - --> $DIR/parent-source-spans.rs:45:5 + --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^^ error: first final: "hip" - --> $DIR/parent-source-spans.rs:51:12 + --> $DIR/parent-source-spans.rs:48:12 | LL | three!("hip", "hop"); | ^^^^^ error: second final: "hop" - --> $DIR/parent-source-spans.rs:51:19 + --> $DIR/parent-source-spans.rs:48:19 | LL | three!("hip", "hop"); | ^^^^^ error: first source: "hip" - --> $DIR/parent-source-spans.rs:51:12 + --> $DIR/parent-source-spans.rs:48:12 | LL | three!("hip", "hop"); | ^^^^^ error: second source: "hop" - --> $DIR/parent-source-spans.rs:51:19 + --> $DIR/parent-source-spans.rs:48:19 | LL | three!("hip", "hop"); | ^^^^^ error[E0425]: cannot find value `ok` in this scope - --> $DIR/parent-source-spans.rs:32:5 + --> $DIR/parent-source-spans.rs:29:5 | LL | parent_source_spans!($($tokens)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok` @@ -153,7 +153,7 @@ LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `ok` in this scope - --> $DIR/parent-source-spans.rs:32:5 + --> $DIR/parent-source-spans.rs:29:5 | LL | parent_source_spans!($($tokens)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok` @@ -169,7 +169,7 @@ LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `ok` in this scope - --> $DIR/parent-source-spans.rs:32:5 + --> $DIR/parent-source-spans.rs:29:5 | LL | parent_source_spans!($($tokens)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok` diff --git a/src/test/ui/proc-macro/proc-macro-gates.rs b/src/test/ui/proc-macro/proc-macro-gates.rs index 5df6ac422ac4b..b3b677fa7ffed 100644 --- a/src/test/ui/proc-macro/proc-macro-gates.rs +++ b/src/test/ui/proc-macro/proc-macro-gates.rs @@ -45,13 +45,4 @@ fn attrs() { //~^ ERROR: custom attributes cannot be applied to expressions } -fn main() { - if let identity!(Some(_x)) = Some(3) {} - //~^ ERROR: procedural macros cannot be expanded to patterns - - empty!(struct S;); //~ ERROR: procedural macros cannot be expanded to statements - empty!(let _x = 3;); //~ ERROR: procedural macros cannot be expanded to statements - - let _x = identity!(3); //~ ERROR: procedural macros cannot be expanded to expressions - let _x = [empty!(3)]; //~ ERROR: procedural macros cannot be expanded to expressions -} +fn main() {} diff --git a/src/test/ui/proc-macro/proc-macro-gates.stderr b/src/test/ui/proc-macro/proc-macro-gates.stderr index a94274de9c134..c034349553177 100644 --- a/src/test/ui/proc-macro/proc-macro-gates.stderr +++ b/src/test/ui/proc-macro/proc-macro-gates.stderr @@ -76,51 +76,6 @@ LL | let _x = #[identity_attr] println!(); = note: see issue #54727 for more information = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable -error[E0658]: procedural macros cannot be expanded to patterns - --> $DIR/proc-macro-gates.rs:49:12 - | -LL | if let identity!(Some(_x)) = Some(3) {} - | ^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #54727 for more information - = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable - -error[E0658]: procedural macros cannot be expanded to statements - --> $DIR/proc-macro-gates.rs:52:5 - | -LL | empty!(struct S;); - | ^^^^^^^^^^^^^^^^^^ - | - = note: see issue #54727 for more information - = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable - -error[E0658]: procedural macros cannot be expanded to statements - --> $DIR/proc-macro-gates.rs:53:5 - | -LL | empty!(let _x = 3;); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #54727 for more information - = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable - -error[E0658]: procedural macros cannot be expanded to expressions - --> $DIR/proc-macro-gates.rs:55:14 - | -LL | let _x = identity!(3); - | ^^^^^^^^^^^^ - | - = note: see issue #54727 for more information - = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable - -error[E0658]: procedural macros cannot be expanded to expressions - --> $DIR/proc-macro-gates.rs:56:15 - | -LL | let _x = [empty!(3)]; - | ^^^^^^^^^ - | - = note: see issue #54727 for more information - = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable - -error: aborting due to 14 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/proc-macro/resolve-error.rs b/src/test/ui/proc-macro/resolve-error.rs index 8ff36ff0a26e2..ad8a5bbb0f9ff 100644 --- a/src/test/ui/proc-macro/resolve-error.rs +++ b/src/test/ui/proc-macro/resolve-error.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // aux-build:derive-foo.rs // aux-build:derive-clona.rs // aux-build:test-macros.rs diff --git a/src/test/ui/proc-macro/resolve-error.stderr b/src/test/ui/proc-macro/resolve-error.stderr index 15a1d6d9479fe..fc189828ad15a 100644 --- a/src/test/ui/proc-macro/resolve-error.stderr +++ b/src/test/ui/proc-macro/resolve-error.stderr @@ -1,5 +1,5 @@ error: cannot find macro `bang_proc_macrp` in this scope - --> $DIR/resolve-error.rs:64:5 + --> $DIR/resolve-error.rs:60:5 | LL | bang_proc_macrp!(); | ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `bang_proc_macro` @@ -10,13 +10,13 @@ LL | pub fn empty(_: TokenStream) -> TokenStream { | ------------------------------------------- similarly named macro `bang_proc_macro` defined here error: cannot find macro `Dlona` in this scope - --> $DIR/resolve-error.rs:61:5 + --> $DIR/resolve-error.rs:57:5 | LL | Dlona!(); | ^^^^^ error: cannot find macro `attr_proc_macra` in this scope - --> $DIR/resolve-error.rs:58:5 + --> $DIR/resolve-error.rs:54:5 | LL | macro_rules! attr_proc_mac { | -------------------------- similarly named macro `attr_proc_mac` defined here @@ -25,7 +25,7 @@ LL | attr_proc_macra!(); | ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `attr_proc_mac` error: cannot find macro `FooWithLongNama` in this scope - --> $DIR/resolve-error.rs:55:5 + --> $DIR/resolve-error.rs:51:5 | LL | macro_rules! FooWithLongNam { | --------------------------- similarly named macro `FooWithLongNam` defined here @@ -34,19 +34,19 @@ LL | FooWithLongNama!(); | ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `FooWithLongNam` error: cannot find derive macro `attr_proc_macra` in this scope - --> $DIR/resolve-error.rs:49:10 + --> $DIR/resolve-error.rs:45:10 | LL | #[derive(attr_proc_macra)] | ^^^^^^^^^^^^^^^ error: cannot find derive macro `attr_proc_macra` in this scope - --> $DIR/resolve-error.rs:49:10 + --> $DIR/resolve-error.rs:45:10 | LL | #[derive(attr_proc_macra)] | ^^^^^^^^^^^^^^^ error: cannot find derive macro `Dlona` in this scope - --> $DIR/resolve-error.rs:44:10 + --> $DIR/resolve-error.rs:40:10 | LL | #[derive(Dlona)] | ^^^^^ help: a derive macro with a similar name exists: `Clona` @@ -57,7 +57,7 @@ LL | pub fn derive_clonea(input: TokenStream) -> TokenStream { | ------------------------------------------------------- similarly named derive macro `Clona` defined here error: cannot find derive macro `Dlona` in this scope - --> $DIR/resolve-error.rs:44:10 + --> $DIR/resolve-error.rs:40:10 | LL | #[derive(Dlona)] | ^^^^^ help: a derive macro with a similar name exists: `Clona` @@ -68,7 +68,7 @@ LL | pub fn derive_clonea(input: TokenStream) -> TokenStream { | ------------------------------------------------------- similarly named derive macro `Clona` defined here error: cannot find derive macro `Dlone` in this scope - --> $DIR/resolve-error.rs:39:10 + --> $DIR/resolve-error.rs:35:10 | LL | #[derive(Dlone)] | ^^^^^ help: a derive macro with a similar name exists: `Clone` @@ -79,7 +79,7 @@ LL | pub macro Clone($item:item) { | --------------------------- similarly named derive macro `Clone` defined here error: cannot find derive macro `Dlone` in this scope - --> $DIR/resolve-error.rs:39:10 + --> $DIR/resolve-error.rs:35:10 | LL | #[derive(Dlone)] | ^^^^^ help: a derive macro with a similar name exists: `Clone` @@ -90,13 +90,13 @@ LL | pub macro Clone($item:item) { | --------------------------- similarly named derive macro `Clone` defined here error: cannot find attribute `FooWithLongNan` in this scope - --> $DIR/resolve-error.rs:36:3 + --> $DIR/resolve-error.rs:32:3 | LL | #[FooWithLongNan] | ^^^^^^^^^^^^^^ error: cannot find attribute `attr_proc_macra` in this scope - --> $DIR/resolve-error.rs:32:3 + --> $DIR/resolve-error.rs:28:3 | LL | #[attr_proc_macra] | ^^^^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `attr_proc_macro` @@ -107,7 +107,7 @@ LL | pub fn empty_attr(_: TokenStream, _: TokenStream) -> TokenStream { | ---------------------------------------------------------------- similarly named attribute macro `attr_proc_macro` defined here error: cannot find derive macro `FooWithLongNan` in this scope - --> $DIR/resolve-error.rs:26:10 + --> $DIR/resolve-error.rs:22:10 | LL | #[derive(FooWithLongNan)] | ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName` @@ -118,7 +118,7 @@ LL | pub fn derive_foo(input: TokenStream) -> TokenStream { | ---------------------------------------------------- similarly named derive macro `FooWithLongName` defined here error: cannot find derive macro `FooWithLongNan` in this scope - --> $DIR/resolve-error.rs:26:10 + --> $DIR/resolve-error.rs:22:10 | LL | #[derive(FooWithLongNan)] | ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName` diff --git a/src/test/ui/proc-macro/resolved-located-at.rs b/src/test/ui/proc-macro/resolved-located-at.rs new file mode 100644 index 0000000000000..b785573f2031f --- /dev/null +++ b/src/test/ui/proc-macro/resolved-located-at.rs @@ -0,0 +1,10 @@ +// aux-build:resolved-located-at.rs + +#[macro_use] +extern crate resolved_located_at; + +fn main() { + resolve_located_at!(a b) + //~^ ERROR expected error + //~| ERROR mismatched types +} diff --git a/src/test/ui/proc-macro/resolved-located-at.stderr b/src/test/ui/proc-macro/resolved-located-at.stderr new file mode 100644 index 0000000000000..e71e79514f2fe --- /dev/null +++ b/src/test/ui/proc-macro/resolved-located-at.stderr @@ -0,0 +1,21 @@ +error: expected error + --> $DIR/resolved-located-at.rs:7:25 + | +LL | resolve_located_at!(a b) + | ^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/resolved-located-at.rs:7:27 + | +LL | fn main() { + | - expected `()` because of default return type +LL | resolve_located_at!(a b) + | ^ expected `()`, found struct `main::S` + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/proc-macro/span-api-tests.rs b/src/test/ui/proc-macro/span-api-tests.rs index 5c0cbd77a8da7..5c149e4a1e526 100644 --- a/src/test/ui/proc-macro/span-api-tests.rs +++ b/src/test/ui/proc-macro/span-api-tests.rs @@ -1,11 +1,8 @@ // run-pass +// ignore-pretty // aux-build:span-api-tests.rs // aux-build:span-test-macros.rs -// ignore-pretty - -#![feature(proc_macro_hygiene)] - #[macro_use] extern crate span_test_macros; diff --git a/src/test/ui/proc-macro/three-equals.rs b/src/test/ui/proc-macro/three-equals.rs index 50a144f7960b7..21b137c99a74c 100644 --- a/src/test/ui/proc-macro/three-equals.rs +++ b/src/test/ui/proc-macro/three-equals.rs @@ -1,8 +1,5 @@ // aux-build:three-equals.rs - -#![feature(proc_macro_hygiene)] - extern crate three_equals; use three_equals::three_equals; diff --git a/src/test/ui/proc-macro/three-equals.stderr b/src/test/ui/proc-macro/three-equals.stderr index ca82a34534525..33a8c762a945b 100644 --- a/src/test/ui/proc-macro/three-equals.stderr +++ b/src/test/ui/proc-macro/three-equals.stderr @@ -1,5 +1,5 @@ error: found 2 equal signs, need exactly 3 - --> $DIR/three-equals.rs:15:5 + --> $DIR/three-equals.rs:12:5 | LL | three_equals!(==); | ^^^^^^^^^^^^^^^^^^ @@ -8,38 +8,38 @@ LL | three_equals!(==); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: expected EOF, found `=`. - --> $DIR/three-equals.rs:18:21 + --> $DIR/three-equals.rs:15:21 | LL | three_equals!(=====); | ^^ | note: last good input was here - --> $DIR/three-equals.rs:18:21 + --> $DIR/three-equals.rs:15:21 | LL | three_equals!(=====); | ^^ = help: input must be: `===` error: expected `=`, found `abc`. - --> $DIR/three-equals.rs:21:19 + --> $DIR/three-equals.rs:18:19 | LL | three_equals!(abc); | ^^^ error: expected `=`, found `!`. - --> $DIR/three-equals.rs:24:19 + --> $DIR/three-equals.rs:21:19 | LL | three_equals!(!!); | ^ error: expected EOF, found `a`. - --> $DIR/three-equals.rs:27:22 + --> $DIR/three-equals.rs:24:22 | LL | three_equals!(===a); | ^ | note: last good input was here - --> $DIR/three-equals.rs:27:21 + --> $DIR/three-equals.rs:24:21 | LL | three_equals!(===a); | ^ diff --git a/src/test/ui/proc_macro.rs b/src/test/ui/proc_macro.rs index 7ff946490034e..66f9cdc5567ca 100644 --- a/src/test/ui/proc_macro.rs +++ b/src/test/ui/proc_macro.rs @@ -2,8 +2,6 @@ // aux-build:proc_macro_def.rs // ignore-cross-compile -#![feature(proc_macro_hygiene)] - extern crate proc_macro_def; use proc_macro_def::{attr_tru, attr_identity, identity, ret_tru, tru}; diff --git a/src/test/ui/process-termination/process-termination-blocking-io.rs b/src/test/ui/process-termination/process-termination-blocking-io.rs new file mode 100644 index 0000000000000..f306a61a53876 --- /dev/null +++ b/src/test/ui/process-termination/process-termination-blocking-io.rs @@ -0,0 +1,19 @@ +// program should terminate even if a thread is blocked on I/O. +// https://github.com/fortanix/rust-sgx/issues/109 + +// run-pass +// ignore-emscripten no threads support + +use std::{net::TcpListener, sync::mpsc, thread}; + +fn main() { + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + let listen = TcpListener::bind("0.0.0.0:0").unwrap(); + tx.send(()).unwrap(); + while let Ok(_) = listen.accept() {} + }); + rx.recv().unwrap(); + for _ in 0..3 { thread::yield_now(); } + println!("Exiting main thread"); +} diff --git a/src/test/ui/process-termination/process-termination-simple.rs b/src/test/ui/process-termination/process-termination-simple.rs new file mode 100644 index 0000000000000..8f2e5b94c3a6e --- /dev/null +++ b/src/test/ui/process-termination/process-termination-simple.rs @@ -0,0 +1,13 @@ +// program should terminate when std::process::exit is called from any thread + +// run-pass +// ignore-emscripten no threads support + +use std::{process, thread}; + +fn main() { + let h = thread::spawn(|| { + process::exit(0); + }); + let _ = h.join(); +} diff --git a/src/test/run-fail/tls-exit-status.rs b/src/test/ui/process/tls-exit-status.rs similarity index 79% rename from src/test/run-fail/tls-exit-status.rs rename to src/test/ui/process/tls-exit-status.rs index f15fd4f6894f7..36d6aff9e7730 100644 --- a/src/test/run-fail/tls-exit-status.rs +++ b/src/test/ui/process/tls-exit-status.rs @@ -1,6 +1,8 @@ +// run-fail // error-pattern:nonzero // exec-env:RUST_NEWRT=1 // ignore-cloudabi no std::env +// ignore-emscripten no processes use std::env; diff --git a/src/test/ui/qualified/qualified-path-params.stderr b/src/test/ui/qualified/qualified-path-params.stderr index 7ff43f4404c54..4214e2503c345 100644 --- a/src/test/ui/qualified/qualified-path-params.stderr +++ b/src/test/ui/qualified/qualified-path-params.stderr @@ -1,4 +1,4 @@ -error[E0533]: expected unit struct, unit variant or constant, found associated function `<::A>::f` +error[E0533]: expected unit struct, unit variant or constant, found associated function `::A::f::` --> $DIR/qualified-path-params.rs:20:9 | LL | ::A::f:: => {} diff --git a/src/test/ui/question-mark-type-infer.stderr b/src/test/ui/question-mark-type-infer.stderr index 262344fba5999..64d8f685637fb 100644 --- a/src/test/ui/question-mark-type-infer.stderr +++ b/src/test/ui/question-mark-type-infer.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed LL | l.iter().map(f).collect()? | ^^^^^^^ cannot infer type | - = note: cannot resolve `<_ as std::ops::Try>::Ok == _` + = note: cannot satisfy `<_ as std::ops::Try>::Ok == _` help: consider specifying the type argument in the method call | LL | l.iter().map(f).collect::()? diff --git a/src/test/ui/range.rs b/src/test/ui/range.rs index 82983e37ea18e..f3f7508d12434 100644 --- a/src/test/ui/range.rs +++ b/src/test/ui/range.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces)] #![allow(unused_comparisons)] #![allow(dead_code)] #![allow(unused_mut)] diff --git a/src/test/ui/range/range-inclusive-pattern-precedence.stderr b/src/test/ui/range/range-inclusive-pattern-precedence.stderr index 8c4ebd10fc909..3a4a514df7ade 100644 --- a/src/test/ui/range/range-inclusive-pattern-precedence.stderr +++ b/src/test/ui/range/range-inclusive-pattern-precedence.stderr @@ -28,5 +28,5 @@ warning: `...` range patterns are deprecated LL | box 0...9 => {} | ^^^ help: use `..=` for an inclusive range -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 2 warnings emitted diff --git a/src/test/ui/range_inclusive.rs b/src/test/ui/range_inclusive.rs index 68d9bf7d26b69..540b35e0392de 100644 --- a/src/test/ui/range_inclusive.rs +++ b/src/test/ui/range_inclusive.rs @@ -1,7 +1,7 @@ // run-pass // Test inclusive range syntax. - #![feature(range_is_empty)] +#![allow(unused_braces)] #![allow(unused_comparisons)] use std::ops::RangeToInclusive; diff --git a/src/test/ui/reachable/unreachable-try-pattern.stderr b/src/test/ui/reachable/unreachable-try-pattern.stderr index d141e382313bf..8f3e23119fb9e 100644 --- a/src/test/ui/reachable/unreachable-try-pattern.stderr +++ b/src/test/ui/reachable/unreachable-try-pattern.stderr @@ -31,3 +31,5 @@ warning: unreachable pattern LL | let y = (match x { Ok(n) => Ok(n), Err(e) => Err(e) })?; | ^^^^^^ +warning: 3 warnings emitted + diff --git a/src/test/ui/realloc-16687.rs b/src/test/ui/realloc-16687.rs index eb6224ad1bbb6..0687a9ce454cc 100644 --- a/src/test/ui/realloc-16687.rs +++ b/src/test/ui/realloc-16687.rs @@ -6,7 +6,7 @@ #![feature(allocator_api)] -use std::alloc::{Global, AllocRef, Layout, handle_alloc_error}; +use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout, ReallocPlacement}; use std::ptr::{self, NonNull}; fn main() { @@ -16,17 +16,17 @@ fn main() { } unsafe fn test_triangle() -> bool { - static COUNT : usize = 16; + static COUNT: usize = 16; let mut ascend = vec![ptr::null_mut(); COUNT]; let ascend = &mut *ascend; - static ALIGN : usize = 1; + static ALIGN: usize = 1; // Checks that `ascend` forms triangle of ascending size formed // from pairs of rows (where each pair of rows is equally sized), // and the elements of the triangle match their row-pair index. unsafe fn sanity_check(ascend: &[*mut u8]) { for i in 0..COUNT / 2 { - let (p0, p1, size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + let (p0, p1, size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i)); for j in 0..size { assert_eq!(*p0.add(j), i as u8); assert_eq!(*p1.add(j), i as u8); @@ -34,20 +34,22 @@ unsafe fn test_triangle() -> bool { } } - static PRINT : bool = false; + static PRINT: bool = false; unsafe fn allocate(layout: Layout) -> *mut u8 { if PRINT { println!("allocate({:?})", layout); } - let (ptr, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + let memory = Global + .alloc(layout, AllocInit::Uninitialized) + .unwrap_or_else(|_| handle_alloc_error(layout)); if PRINT { - println!("allocate({:?}) = {:?}", layout, ptr); + println!("allocate({:?}) = {:?}", layout, memory.ptr); } - ptr.cast().as_ptr() + memory.ptr.cast().as_ptr() } unsafe fn deallocate(ptr: *mut u8, layout: Layout) { @@ -63,19 +65,31 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let (ptr, _) = Global.realloc(NonNull::new_unchecked(ptr), old, new.size()) - .unwrap_or_else(|_| handle_alloc_error( - Layout::from_size_align_unchecked(new.size(), old.align()) - )); + let memory = if new.size() > old.size() { + Global.grow( + NonNull::new_unchecked(ptr), + old, + new.size(), + ReallocPlacement::MayMove, + AllocInit::Uninitialized, + ) + } else { + Global.shrink(NonNull::new_unchecked(ptr), old, new.size(), ReallocPlacement::MayMove) + }; + + let memory = memory.unwrap_or_else(|_| { + handle_alloc_error(Layout::from_size_align_unchecked(new.size(), old.align())) + }); if PRINT { - println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", - ptr, old, new, ptr); + println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, memory.ptr); } - ptr.cast().as_ptr() + memory.ptr.cast().as_ptr() } - fn idx_to_size(i: usize) -> usize { (i+1) * 10 } + fn idx_to_size(i: usize) -> usize { + (i + 1) * 10 + } // Allocate pairs of rows that form a triangle shape. (Hope is // that at least two rows will be allocated near each other, so @@ -83,13 +97,13 @@ unsafe fn test_triangle() -> bool { // way.) for i in 0..COUNT / 2 { let size = idx_to_size(i); - ascend[2*i] = allocate(Layout::from_size_align(size, ALIGN).unwrap()); - ascend[2*i+1] = allocate(Layout::from_size_align(size, ALIGN).unwrap()); + ascend[2 * i] = allocate(Layout::from_size_align(size, ALIGN).unwrap()); + ascend[2 * i + 1] = allocate(Layout::from_size_align(size, ALIGN).unwrap()); } // Initialize each pair of rows to distinct value. for i in 0..COUNT / 2 { - let (p0, p1, size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + let (p0, p1, size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i)); for j in 0..size { *p0.add(j) = i as u8; *p1.add(j) = i as u8; @@ -104,8 +118,8 @@ unsafe fn test_triangle() -> bool { for i in 0..COUNT / 2 { let size = idx_to_size(i); - deallocate(ascend[2*i], Layout::from_size_align(size, ALIGN).unwrap()); - deallocate(ascend[2*i+1], Layout::from_size_align(size, ALIGN).unwrap()); + deallocate(ascend[2 * i], Layout::from_size_align(size, ALIGN).unwrap()); + deallocate(ascend[2 * i + 1], Layout::from_size_align(size, ALIGN).unwrap()); } return true; @@ -115,68 +129,68 @@ unsafe fn test_triangle() -> bool { // realloc'ing each row from top to bottom, and checking all the // rows as we go. unsafe fn test_1(ascend: &mut [*mut u8]) { - let new_size = idx_to_size(COUNT-1); + let new_size = idx_to_size(COUNT - 1); let new = Layout::from_size_align(new_size, ALIGN).unwrap(); for i in 0..COUNT / 2 { - let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + let (p0, p1, old_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i)); assert!(old_size < new_size); let old = Layout::from_size_align(old_size, ALIGN).unwrap(); - ascend[2*i] = reallocate(p0, old.clone(), new.clone()); + ascend[2 * i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); + ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); } } // Test 2: turn the square back into a triangle, top to bottom. unsafe fn test_2(ascend: &mut [*mut u8]) { - let old_size = idx_to_size(COUNT-1); + let old_size = idx_to_size(COUNT - 1); let old = Layout::from_size_align(old_size, ALIGN).unwrap(); for i in 0..COUNT / 2 { - let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + let (p0, p1, new_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i)); assert!(new_size < old_size); let new = Layout::from_size_align(new_size, ALIGN).unwrap(); - ascend[2*i] = reallocate(p0, old.clone(), new.clone()); + ascend[2 * i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); + ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); } } // Test 3: turn triangle into a square, bottom to top. unsafe fn test_3(ascend: &mut [*mut u8]) { - let new_size = idx_to_size(COUNT-1); + let new_size = idx_to_size(COUNT - 1); let new = Layout::from_size_align(new_size, ALIGN).unwrap(); for i in (0..COUNT / 2).rev() { - let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + let (p0, p1, old_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i)); assert!(old_size < new_size); let old = Layout::from_size_align(old_size, ALIGN).unwrap(); - ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); + ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i] = reallocate(p0, old.clone(), new.clone()); + ascend[2 * i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); } } // Test 4: turn the square back into a triangle, bottom to top. unsafe fn test_4(ascend: &mut [*mut u8]) { - let old_size = idx_to_size(COUNT-1); + let old_size = idx_to_size(COUNT - 1); let old = Layout::from_size_align(old_size, ALIGN).unwrap(); for i in (0..COUNT / 2).rev() { - let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + let (p0, p1, new_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i)); assert!(new_size < old_size); let new = Layout::from_size_align(new_size, ALIGN).unwrap(); - ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); + ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i] = reallocate(p0, old.clone(), new.clone()); + ascend[2 * i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); } } diff --git a/src/test/ui/recursion/recursive-requirements.stderr b/src/test/ui/recursion/recursive-requirements.stderr index 9846c938ba90b..0237675aee4d5 100644 --- a/src/test/ui/recursion/recursive-requirements.stderr +++ b/src/test/ui/recursion/recursive-requirements.stderr @@ -2,7 +2,7 @@ error[E0277]: `*const Bar` cannot be shared between threads safely --> $DIR/recursive-requirements.rs:16:12 | LL | struct AssertSync(PhantomData); - | ------------------------------------------- required by `AssertSync` + | ---- required by this bound in `AssertSync` ... LL | let _: AssertSync = unimplemented!(); | ^^^^^^^^^^^^^^^ `*const Bar` cannot be shared between threads safely @@ -14,7 +14,7 @@ error[E0277]: `*const Foo` cannot be shared between threads safely --> $DIR/recursive-requirements.rs:16:12 | LL | struct AssertSync(PhantomData); - | ------------------------------------------- required by `AssertSync` + | ---- required by this bound in `AssertSync` ... LL | let _: AssertSync = unimplemented!(); | ^^^^^^^^^^^^^^^ `*const Foo` cannot be shared between threads safely diff --git a/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs b/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs index 5be426eb38278..4489303638358 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - struct R<'a> { r: &'a R<'a>, } diff --git a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr index f371d460cf733..75e8ae264e79d 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered - --> $DIR/recursive-types-are-not-uninhabited.rs:11:9 + --> $DIR/recursive-types-are-not-uninhabited.rs:6:9 | LL | let Ok(x) = res; | ^^^^^ pattern `Err(_)` not covered @@ -11,6 +11,7 @@ LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::result::Result` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Ok(x) = res { /* */ } diff --git a/src/test/ui/regions/issue-72051-member-region-hang.rs b/src/test/ui/regions/issue-72051-member-region-hang.rs new file mode 100644 index 0000000000000..b7340b79d682a --- /dev/null +++ b/src/test/ui/regions/issue-72051-member-region-hang.rs @@ -0,0 +1,7 @@ +// Regression test for #72051, hang when resolving regions. + +// check-pass +// edition:2018 + +pub async fn query<'a>(_: &(), _: &(), _: (&(dyn std::any::Any + 'a),) ) {} +fn main() {} diff --git a/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr b/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr index c720b26aa03db..a2396ad4286f7 100644 --- a/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr +++ b/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr @@ -19,6 +19,6 @@ LL | (|x| f(x))(call_rec(f)) | | borrow occurs due to use in closure | borrow of `f` occurs here -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0505`. diff --git a/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr b/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr index 184cead21231f..ea9be77a3e8b5 100644 --- a/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr +++ b/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr @@ -31,5 +31,5 @@ LL | struct Foo<'a,'b,'c> { error: aborting due to 3 previous errors -Some errors have detailed explanations: E0392, E0478. -For more information about an error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0226, E0392, E0478. +For more information about an error, try `rustc --explain E0226`. diff --git a/src/test/ui/regions/regions-mock-codegen.rs b/src/test/ui/regions/regions-mock-codegen.rs index fe3a864fe4ba5..380310190be01 100644 --- a/src/test/ui/regions/regions-mock-codegen.rs +++ b/src/test/ui/regions/regions-mock-codegen.rs @@ -1,34 +1,34 @@ // run-pass #![allow(dead_code)] #![allow(non_camel_case_types)] - // pretty-expanded FIXME #23616 - #![feature(allocator_api)] -use std::alloc::{AllocRef, Global, Layout, handle_alloc_error}; +use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout}; use std::ptr::NonNull; struct arena(()); struct Bcx<'a> { - fcx: &'a Fcx<'a> + fcx: &'a Fcx<'a>, } struct Fcx<'a> { arena: &'a arena, - ccx: &'a Ccx + ccx: &'a Ccx, } struct Ccx { - x: isize + x: isize, } fn alloc(_bcx: &arena) -> &Bcx<'_> { unsafe { let layout = Layout::new::(); - let (ptr, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); - &*(ptr.as_ptr() as *const _) + let memory = Global + .alloc(layout, AllocInit::Uninitialized) + .unwrap_or_else(|_| handle_alloc_error(layout)); + &*(memory.ptr.as_ptr() as *const _) } } diff --git a/src/test/ui/removing-extern-crate.stderr b/src/test/ui/removing-extern-crate.stderr index c86556c89f43b..4dddf160ce27b 100644 --- a/src/test/ui/removing-extern-crate.stderr +++ b/src/test/ui/removing-extern-crate.stderr @@ -29,3 +29,5 @@ warning: unused extern crate LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: remove it +warning: 4 warnings emitted + diff --git a/src/test/ui/repeat_count_const_in_async_fn.rs b/src/test/ui/repeat_count_const_in_async_fn.rs new file mode 100644 index 0000000000000..ebabc3fbf10f9 --- /dev/null +++ b/src/test/ui/repeat_count_const_in_async_fn.rs @@ -0,0 +1,10 @@ +// check-pass +// edition:2018 +// compile-flags: --crate-type=lib + +pub async fn test() { + const C: usize = 4; + foo(&mut [0u8; C]).await; +} + +async fn foo(_: &mut [u8]) {} diff --git a/src/test/ui/reserved/reserved-attr-on-macro.stderr b/src/test/ui/reserved/reserved-attr-on-macro.stderr index c387bba0a1310..e55b58bef2855 100644 --- a/src/test/ui/reserved/reserved-attr-on-macro.stderr +++ b/src/test/ui/reserved/reserved-attr-on-macro.stderr @@ -1,10 +1,8 @@ -error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler +error: attributes starting with `rustc` are reserved for use by the `rustc` compiler --> $DIR/reserved-attr-on-macro.rs:1:3 | LL | #[rustc_attribute_should_be_reserved] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: cannot determine resolution for the macro `foo` --> $DIR/reserved-attr-on-macro.rs:10:5 @@ -22,4 +20,3 @@ LL | #[rustc_attribute_should_be_reserved] error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/resolve/enums-are-namespaced-xc.stderr b/src/test/ui/resolve/enums-are-namespaced-xc.stderr index 61816709ecc5d..621686dd292d6 100644 --- a/src/test/ui/resolve/enums-are-namespaced-xc.stderr +++ b/src/test/ui/resolve/enums-are-namespaced-xc.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find value `A` in crate `namespaced_enums` LL | let _ = namespaced_enums::A; | ^ not found in `namespaced_enums` | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this unit variant | LL | use namespaced_enums::Foo::A; | @@ -15,7 +15,7 @@ error[E0425]: cannot find function, tuple struct or tuple variant `B` in crate ` LL | let _ = namespaced_enums::B(10); | ^ not found in `namespaced_enums` | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this tuple variant | LL | use namespaced_enums::Foo::B; | @@ -26,7 +26,7 @@ error[E0422]: cannot find struct, variant or union type `C` in crate `namespaced LL | let _ = namespaced_enums::C { a: 10 }; | ^ not found in `namespaced_enums` | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this variant | LL | use namespaced_enums::Foo::C; | diff --git a/src/test/ui/resolve/issue-16058.rs b/src/test/ui/resolve/issue-16058.rs index d41023e82c00c..048aaf65fbfa8 100644 --- a/src/test/ui/resolve/issue-16058.rs +++ b/src/test/ui/resolve/issue-16058.rs @@ -1,3 +1,5 @@ +// ignore-sgx std::os::fortanix_sgx::usercalls::raw::Result changes compiler suggestions + pub struct GslResult { pub val: f64, pub err: f64 diff --git a/src/test/ui/resolve/issue-16058.stderr b/src/test/ui/resolve/issue-16058.stderr index 31f4998bd83b6..c47d22cef5f6c 100644 --- a/src/test/ui/resolve/issue-16058.stderr +++ b/src/test/ui/resolve/issue-16058.stderr @@ -1,10 +1,10 @@ error[E0574]: expected struct, variant or union type, found enum `Result` - --> $DIR/issue-16058.rs:8:9 + --> $DIR/issue-16058.rs:10:9 | LL | Result { | ^^^^^^ not a struct, variant or union type | -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::fmt::Result; | diff --git a/src/test/ui/resolve/issue-17518.stderr b/src/test/ui/resolve/issue-17518.stderr index 6098d4f490155..034d0d01bfb80 100644 --- a/src/test/ui/resolve/issue-17518.stderr +++ b/src/test/ui/resolve/issue-17518.stderr @@ -4,7 +4,7 @@ error[E0422]: cannot find struct, variant or union type `E` in this scope LL | E { name: "foobar" }; | ^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this variant | LL | use SomeEnum::E; | diff --git a/src/test/ui/resolve/issue-21221-1.stderr b/src/test/ui/resolve/issue-21221-1.stderr index 27fd612faca9c..d3e1953435359 100644 --- a/src/test/ui/resolve/issue-21221-1.stderr +++ b/src/test/ui/resolve/issue-21221-1.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `Mul` in this scope LL | impl Mul for Foo { | ^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use mul1::Mul; | @@ -19,7 +19,7 @@ error[E0412]: cannot find type `Mul` in this scope LL | fn getMul() -> Mul { | ^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use mul1::Mul; | @@ -43,7 +43,7 @@ error[E0405]: cannot find trait `Div` in this scope LL | impl Div for Foo { | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use std::ops::Div; | diff --git a/src/test/ui/resolve/issue-21221-2.stderr b/src/test/ui/resolve/issue-21221-2.stderr index b360fda6f9d7c..f9263d2af5026 100644 --- a/src/test/ui/resolve/issue-21221-2.stderr +++ b/src/test/ui/resolve/issue-21221-2.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `T` in this scope LL | impl T for Foo { } | ^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use foo::bar::T; | diff --git a/src/test/ui/resolve/issue-21221-3.stderr b/src/test/ui/resolve/issue-21221-3.stderr index f2c94d467e25e..f12e5b09bacd0 100644 --- a/src/test/ui/resolve/issue-21221-3.stderr +++ b/src/test/ui/resolve/issue-21221-3.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `OuterTrait` in this scope LL | impl OuterTrait for Foo {} | ^^^^^^^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use issue_21221_3::outer::OuterTrait; | diff --git a/src/test/ui/resolve/issue-21221-4.stderr b/src/test/ui/resolve/issue-21221-4.stderr index 0b1527f91bd00..fc15444d0c0fb 100644 --- a/src/test/ui/resolve/issue-21221-4.stderr +++ b/src/test/ui/resolve/issue-21221-4.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `T` in this scope LL | impl T for Foo {} | ^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this trait | LL | use issue_21221_4::T; | diff --git a/src/test/ui/resolve/issue-3907.stderr b/src/test/ui/resolve/issue-3907.stderr index 384df571e2a80..4d0b0af58a320 100644 --- a/src/test/ui/resolve/issue-3907.stderr +++ b/src/test/ui/resolve/issue-3907.stderr @@ -4,8 +4,12 @@ error[E0404]: expected trait, found type alias `Foo` LL | impl Foo for S { | ^^^ type aliases cannot be used as traits | - = note: did you mean to use a trait alias? -help: possible better candidate is found in another module, you can import it into scope +help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias + --> $DIR/issue-3907.rs:5:1 + | +LL | type Foo = dyn issue_3907::Foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider importing this trait instead | LL | use issue_3907::Foo; | diff --git a/src/test/ui/resolve/issue-5035.stderr b/src/test/ui/resolve/issue-5035.stderr index 622f0dfcda4cb..41dff2fe54205 100644 --- a/src/test/ui/resolve/issue-5035.stderr +++ b/src/test/ui/resolve/issue-5035.stderr @@ -16,7 +16,11 @@ LL | impl K for isize {} | type aliases cannot be used as traits | help: a trait with a similar name exists: `I` | - = note: did you mean to use a trait alias? +help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias + --> $DIR/issue-5035.rs:2:1 + | +LL | type K = dyn I; + | ^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/issue-65035-static-with-parent-generics.rs b/src/test/ui/resolve/issue-65035-static-with-parent-generics.rs index 708d72a2df756..f09ab3bf91988 100644 --- a/src/test/ui/resolve/issue-65035-static-with-parent-generics.rs +++ b/src/test/ui/resolve/issue-65035-static-with-parent-generics.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete fn f() { extern "C" { @@ -23,8 +23,7 @@ fn h() { fn i() { static a: [u8; N] = [0; N]; //~^ ERROR can't use generic parameters from outer function - //~^^ ERROR can't use generic parameters from outer function - //~| ERROR mismatched types + //~| ERROR can't use generic parameters from outer function } fn main() {} diff --git a/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr b/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr index 97c60c7229837..7f8151db06f5b 100644 --- a/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr +++ b/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr @@ -40,24 +40,15 @@ LL | fn i() { LL | static a: [u8; N] = [0; N]; | ^ use of generic parameter from outer function -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-65035-static-with-parent-generics.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -error[E0308]: mismatched types - --> $DIR/issue-65035-static-with-parent-generics.rs:24:25 - | -LL | static a: [u8; N] = [0; N]; - | ^^^^^^ expected `N`, found `N` - | - = note: expected array `[u8; _]` - found array `[u8; _]` - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors; 1 warning emitted -Some errors have detailed explanations: E0308, E0401. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0401`. diff --git a/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.rs b/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.rs new file mode 100644 index 0000000000000..cc36f054bc3a0 --- /dev/null +++ b/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.rs @@ -0,0 +1,20 @@ +// edition:2018 + +async fn free(); //~ ERROR without a body + +struct A; +impl A { + async fn inherent(); //~ ERROR without body +} + +trait B { + async fn associated(); + //~^ ERROR cannot be declared `async` +} +impl B for A { + async fn associated(); //~ ERROR without body + //~^ ERROR cannot be declared `async` + //~| ERROR incompatible type for trait +} + +fn main() {} diff --git a/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.stderr b/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.stderr new file mode 100644 index 0000000000000..a324d04d394cf --- /dev/null +++ b/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.stderr @@ -0,0 +1,65 @@ +error: free function without a body + --> $DIR/issue-70736-async-fn-no-body-def-collector.rs:3:1 + | +LL | async fn free(); + | ^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the function: `{ }` + +error: associated function in `impl` without body + --> $DIR/issue-70736-async-fn-no-body-def-collector.rs:7:5 + | +LL | async fn inherent(); + | ^^^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the function: `{ }` + +error[E0706]: functions in traits cannot be declared `async` + --> $DIR/issue-70736-async-fn-no-body-def-collector.rs:11:5 + | +LL | async fn associated(); + | -----^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error: associated function in `impl` without body + --> $DIR/issue-70736-async-fn-no-body-def-collector.rs:15:5 + | +LL | async fn associated(); + | ^^^^^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the function: `{ }` + +error[E0706]: functions in traits cannot be declared `async` + --> $DIR/issue-70736-async-fn-no-body-def-collector.rs:15:5 + | +LL | async fn associated(); + | -----^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error[E0053]: method `associated` has an incompatible type for trait + --> $DIR/issue-70736-async-fn-no-body-def-collector.rs:15:26 + | +LL | async fn associated(); + | - type in trait +... +LL | async fn associated(); + | ^ + | | + | the `Output` of this `async fn`'s found opaque type + | expected `()`, found opaque type + | + = note: expected fn pointer `fn()` + found fn pointer `fn() -> impl std::future::Future` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0053, E0706. +For more information about an error, try `rustc --explain E0053`. diff --git a/src/test/ui/resolve/levenshtein.rs b/src/test/ui/resolve/levenshtein.rs index 6a98782a9badf..a6f4716256881 100644 --- a/src/test/ui/resolve/levenshtein.rs +++ b/src/test/ui/resolve/levenshtein.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl const MAX_ITEM: usize = 10; fn foo_bar() {} diff --git a/src/test/ui/resolve/levenshtein.stderr b/src/test/ui/resolve/levenshtein.stderr index ecdec3c24a1d8..68e0cf08ffacf 100644 --- a/src/test/ui/resolve/levenshtein.stderr +++ b/src/test/ui/resolve/levenshtein.stderr @@ -1,11 +1,11 @@ error[E0412]: cannot find type `esize` in this scope - --> $DIR/levenshtein.rs:9:11 + --> $DIR/levenshtein.rs:5:11 | LL | fn foo(c: esize) {} // Misspelled primitive type name. | ^^^^^ help: a builtin type with a similar name exists: `isize` error[E0412]: cannot find type `Baz` in this scope - --> $DIR/levenshtein.rs:14:10 + --> $DIR/levenshtein.rs:10:10 | LL | enum Bar { } | -------- similarly named enum `Bar` defined here @@ -14,7 +14,7 @@ LL | type A = Baz; // Misspelled type name. | ^^^ help: an enum with a similar name exists: `Bar` error[E0412]: cannot find type `Opiton` in this scope - --> $DIR/levenshtein.rs:16:10 + --> $DIR/levenshtein.rs:12:10 | LL | type B = Opiton; // Misspelled type name from the prelude. | ^^^^^^ help: an enum with a similar name exists: `Option` @@ -25,13 +25,13 @@ LL | pub enum Option { | ------------------ similarly named enum `Option` defined here error[E0412]: cannot find type `Baz` in this scope - --> $DIR/levenshtein.rs:20:14 + --> $DIR/levenshtein.rs:16:14 | LL | type A = Baz; // No suggestion here, Bar is not visible | ^^^ not found in this scope error[E0425]: cannot find value `MAXITEM` in this scope - --> $DIR/levenshtein.rs:28:20 + --> $DIR/levenshtein.rs:24:20 | LL | const MAX_ITEM: usize = 10; | --------------------------- similarly named constant `MAX_ITEM` defined here @@ -40,7 +40,7 @@ LL | let v = [0u32; MAXITEM]; // Misspelled constant name. | ^^^^^^^ help: a constant with a similar name exists: `MAX_ITEM` error[E0425]: cannot find function `foobar` in this scope - --> $DIR/levenshtein.rs:30:5 + --> $DIR/levenshtein.rs:26:5 | LL | fn foo_bar() {} | ------------ similarly named function `foo_bar` defined here @@ -49,7 +49,7 @@ LL | foobar(); // Misspelled function name. | ^^^^^^ help: a function with a similar name exists: `foo_bar` error[E0412]: cannot find type `first` in module `m` - --> $DIR/levenshtein.rs:32:15 + --> $DIR/levenshtein.rs:28:15 | LL | pub struct First; | ----------------- similarly named struct `First` defined here @@ -58,7 +58,7 @@ LL | let b: m::first = m::second; // Misspelled item in module. | ^^^^^ help: a struct with a similar name exists (notice the capitalization): `First` error[E0425]: cannot find value `second` in module `m` - --> $DIR/levenshtein.rs:32:26 + --> $DIR/levenshtein.rs:28:26 | LL | pub struct Second; | ------------------ similarly named unit struct `Second` defined here diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr index 30ac783ea2f7e..d9b1b9c59558a 100644 --- a/src/test/ui/resolve/privacy-enum-ctor.stderr +++ b/src/test/ui/resolve/privacy-enum-ctor.stderr @@ -67,7 +67,7 @@ LL | let _: E = E::Struct; | ^^^^^^^^^ LL | let _: E = E::Unit; | ^^^^^^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::f32::consts::E; | @@ -99,7 +99,7 @@ LL | let _: E = E::Struct; | ^^^^^^^^^ LL | let _: E = E::Unit; | ^^^^^^^ -help: possible better candidates are found in other modules, you can import them into scope +help: consider importing one of these items instead | LL | use std::f32::consts::E; | @@ -130,7 +130,7 @@ help: an enum with a similar name exists | LL | let _: E = m::n::Z; | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this enum | LL | use m::n::Z; | @@ -163,7 +163,7 @@ help: an enum with a similar name exists | LL | let _: E = m::n::Z::Fn; | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this enum | LL | use m::n::Z; | @@ -181,7 +181,7 @@ help: an enum with a similar name exists | LL | let _: E = m::n::Z::Struct; | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this enum | LL | use m::n::Z; | @@ -210,7 +210,7 @@ help: an enum with a similar name exists | LL | let _: E = m::n::Z::Unit {}; | ^ -help: possible candidate is found in another module, you can import it into scope +help: consider importing this enum | LL | use m::n::Z; | diff --git a/src/test/ui/resolve/privacy-struct-ctor.stderr b/src/test/ui/resolve/privacy-struct-ctor.stderr index baf7dd84eb09d..e0305b129a881 100644 --- a/src/test/ui/resolve/privacy-struct-ctor.stderr +++ b/src/test/ui/resolve/privacy-struct-ctor.stderr @@ -33,7 +33,7 @@ error[E0423]: expected value, found struct `xcrate::S` LL | xcrate::S; | ^^^^^^^^^ constructor is not visible here due to private fields | -help: possible better candidate is found in another module, you can import it into scope +help: consider importing this tuple struct instead | LL | use m::S; | diff --git a/src/test/ui/resolve/resolve-primitive-fallback.stderr b/src/test/ui/resolve/resolve-primitive-fallback.stderr index 9d381a8a94e3f..8611306e82d03 100644 --- a/src/test/ui/resolve/resolve-primitive-fallback.stderr +++ b/src/test/ui/resolve/resolve-primitive-fallback.stderr @@ -10,7 +10,7 @@ error[E0412]: cannot find type `u8` in the crate root LL | let _: ::u8; | ^^ not found in the crate root | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this builtin type | LL | use std::primitive::u8; | diff --git a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr index c86a6d70344cc..2974d08eb23b1 100644 --- a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr +++ b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr @@ -10,7 +10,11 @@ error[E0404]: expected trait, found type alias `Typedef` LL | fn g isize>(x: F) {} | ^^^^^^^^^^^^^^^^^^^^^^^ type aliases cannot be used as traits | - = note: did you mean to use a trait alias? +help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias + --> $DIR/unboxed-closure-sugar-nonexistent-trait.rs:4:1 + | +LL | type Typedef = isize; + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/use_suggestion_placement.stderr b/src/test/ui/resolve/use_suggestion_placement.stderr index ef451ea847a37..9c337f515adbc 100644 --- a/src/test/ui/resolve/use_suggestion_placement.stderr +++ b/src/test/ui/resolve/use_suggestion_placement.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `Path` in this scope LL | type Bar = Path; | ^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this struct | LL | use std::path::Path; | @@ -15,7 +15,7 @@ error[E0425]: cannot find value `A` in this scope LL | let _ = A; | ^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this constant | LL | use m::A; | @@ -26,7 +26,7 @@ error[E0412]: cannot find type `HashMap` in this scope LL | type Dict = HashMap; | ^^^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use std::collections::HashMap; | diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs similarity index 84% rename from src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs rename to src/test/ui/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs index 796729ac4cc28..10dc6115dcb20 100644 --- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs @@ -1,5 +1,7 @@ +// run-fail // error-pattern:returned Box from main() // failure-status: 1 +// ignore-emscripten no processes use std::error::Error; use std::io; diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-for-never.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-never.rs new file mode 100644 index 0000000000000..faf2526c8d8fd --- /dev/null +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-never.rs @@ -0,0 +1,7 @@ +// run-fail +// error-pattern:oh, dear +// ignore-emscripten no processes + +fn main() -> ! { + panic!("oh, dear"); +} diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs similarity index 83% rename from src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs rename to src/test/ui/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs index 2f3a73a30ad95..6a625fb05e8f8 100644 --- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs @@ -1,5 +1,7 @@ +// run-fail // error-pattern:returned Box from main() // failure-status: 1 +// ignore-emscripten no processes use std::io::{Error, ErrorKind}; diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-str.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-str.rs similarity index 75% rename from src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-str.rs rename to src/test/ui/rfc-1937-termination-trait/termination-trait-for-str.rs index bd6fa8af93513..94f16c6fd0212 100644 --- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-str.rs +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-for-str.rs @@ -1,5 +1,7 @@ +// run-fail // error-pattern: An error message for you // failure-status: 1 +// ignore-emscripten no processes fn main() -> Result<(), &'static str> { Err("An error message for you") diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs index 519fb75be55aa..193a523aed24b 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs @@ -1,8 +1,4 @@ // compile-flags: --test -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::num::ParseFloatError; diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr index d4bd760770d5f..1c47aafec6b97 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr @@ -1,5 +1,5 @@ error[E0277]: `main` has invalid return type `std::result::Result` - --> $DIR/termination-trait-test-wrong-type.rs:10:1 + --> $DIR/termination-trait-test-wrong-type.rs:6:1 | LL | / fn can_parse_zero_as_f32() -> Result { LL | | "0".parse() diff --git a/src/test/ui/rfc-2005-default-binding-mode/slice.stderr b/src/test/ui/rfc-2005-default-binding-mode/slice.stderr index c234fdf46ed2d..18d8f5481c9fb 100644 --- a/src/test/ui/rfc-2005-default-binding-mode/slice.stderr +++ b/src/test/ui/rfc-2005-default-binding-mode/slice.stderr @@ -5,6 +5,7 @@ LL | match sl { | ^^ pattern `&[]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[u8]` error: aborting due to previous error diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr index a2bdcbaa4478d..28e450336f58d 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr @@ -5,6 +5,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `enums::EmptyNonExhaustiveEnum` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/enum.rs:16:11 @@ -13,6 +14,7 @@ LL | match enum_unit { | ^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `enums::NonExhaustiveEnum` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/enum.rs:23:11 @@ -21,6 +23,7 @@ LL | match enum_unit {}; | ^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `enums::NonExhaustiveEnum` error: aborting due to 3 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr index a99a690bc9e5a..752b08b2b65f1 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr @@ -18,6 +18,7 @@ LL | match NonExhaustiveEnum::Unit {} | ^^^^^^^^^^^^^^^^^^^^^^^ patterns `Unit`, `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NonExhaustiveEnum` error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered --> $DIR/enum_same_crate_empty_match.rs:35:11 @@ -39,6 +40,7 @@ LL | match NormalEnum::Unit {} | ^^^^^^^^^^^^^^^^ patterns `Unit`, `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `NormalEnum` error: aborting due to 2 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/struct.stderr b/src/test/ui/rfc-2008-non-exhaustive/struct.stderr index 4e91e7bff34b2..3bc38830537cf 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/struct.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/struct.stderr @@ -62,18 +62,33 @@ error[E0638]: `..` required with struct marked as non-exhaustive | LL | let NormalStruct { first_field, second_field } = ns; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `..` at the end of the field list to ignore all other fields + | +LL | let NormalStruct { first_field, second_field , .. } = ns; + | ^^^^^^ error[E0638]: `..` required with struct marked as non-exhaustive --> $DIR/struct.rs:26:9 | LL | let TupleStruct { 0: first_field, 1: second_field } = ts; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `..` at the end of the field list to ignore all other fields + | +LL | let TupleStruct { 0: first_field, 1: second_field , .. } = ts; + | ^^^^^^ error[E0638]: `..` required with struct marked as non-exhaustive --> $DIR/struct.rs:35:9 | LL | let UnitStruct { } = us; | ^^^^^^^^^^^^^^ + | +help: add `..` at the end of the field list to ignore all other fields + | +LL | let UnitStruct { .. } = us; + | ^^^^ error: aborting due to 9 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr index abca542373f3a..bd136333b761d 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr @@ -5,6 +5,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedEnum` error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match.rs:23:11 @@ -13,6 +14,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedStruct` error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match.rs:27:11 @@ -21,6 +23,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match.rs:33:11 @@ -29,6 +32,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr index 989cb791a417c..42bf67c0a45df 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr @@ -8,6 +8,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `IndirectUninhabitedEnum` error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match_same_crate.rs:38:11 @@ -19,6 +20,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `IndirectUninhabitedStruct` error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match_same_crate.rs:42:11 @@ -30,6 +32,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `IndirectUninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match_same_crate.rs:48:11 @@ -41,6 +44,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `IndirectUninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr index 17a8d01007205..5211b57726428 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr @@ -5,6 +5,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedEnum` error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:27:11 @@ -13,6 +14,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedStruct` error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:31:11 @@ -21,6 +23,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:37:11 @@ -29,6 +32,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::IndirectUninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr index a214a652a387f..961b3e567325f 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr @@ -5,6 +5,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedEnum` error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty --> $DIR/match.rs:23:11 @@ -13,6 +14,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedStruct` error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty --> $DIR/match.rs:27:11 @@ -21,6 +23,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match.rs:31:11 @@ -36,6 +39,7 @@ LL | #[non_exhaustive] Struct { x: ! } | ------ not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr index 858aae585765f..e4d0c7022f3b4 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr @@ -10,6 +10,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `UninhabitedStruct` error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty --> $DIR/match_same_crate.rs:34:11 @@ -21,6 +22,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `UninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match_same_crate.rs:38:11 @@ -37,6 +39,7 @@ LL | match x {} | ^ patterns `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `UninhabitedVariants` error: aborting due to 3 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index 63564e9c3cc8c..c489edeb699d8 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -5,6 +5,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedEnum` error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty --> $DIR/match_with_exhaustive_patterns.rs:26:11 @@ -13,6 +14,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedStruct` error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty --> $DIR/match_with_exhaustive_patterns.rs:30:11 @@ -21,6 +23,7 @@ LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match_with_exhaustive_patterns.rs:34:11 @@ -36,6 +39,7 @@ LL | #[non_exhaustive] Struct { x: ! } | ------ not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `uninhabited::UninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/variant.stderr b/src/test/ui/rfc-2008-non-exhaustive/variant.stderr index ae4f6aff11a02..fbdbb0c9930a6 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/variant.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/variant.stderr @@ -69,12 +69,22 @@ error[E0638]: `..` required with variant marked as non-exhaustive | LL | NonExhaustiveVariants::Struct { field } => "" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `..` at the end of the field list to ignore all other fields + | +LL | NonExhaustiveVariants::Struct { field , .. } => "" + | ^^^^^^ error[E0638]: `..` required with variant marked as non-exhaustive --> $DIR/variant.rs:30:12 | LL | if let NonExhaustiveVariants::Struct { field } = variant_struct { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `..` at the end of the field list to ignore all other fields + | +LL | if let NonExhaustiveVariants::Struct { field , .. } = variant_struct { + | ^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs b/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs index fe2b92eafdb76..edf9ba2c41a15 100644 --- a/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs +++ b/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs @@ -18,7 +18,7 @@ const fn attributed() -> L { const fn calling_attributed() -> L { // We need `-Z unleash-the-miri-inside-of-you` for this as we don't have `const fn` pointers. let ptr: fn() -> L = attributed; - ptr() //~ WARN skipping const checks + ptr() } fn main() { diff --git a/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr b/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr index fcf0945ed4cc6..cf8ca57714c29 100644 --- a/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr +++ b/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr @@ -1,6 +1,10 @@ warning: skipping const checks + | +help: skipping check that does not even have a feature gate --> $DIR/caller-location-fnptr-rt-ctfe-equiv.rs:21:5 | LL | ptr() | ^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/rfc-2091-track-caller/error-extern-fn.rs b/src/test/ui/rfc-2091-track-caller/error-extern-fn.rs deleted file mode 100644 index 9f6a69a51c0ce..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-extern-fn.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(track_caller)] -#![allow(dead_code)] - -extern "Rust" { - #[track_caller] //~ ERROR: `#[track_caller]` is not supported on foreign functions - fn bar(); -} - -fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/error-extern-fn.stderr b/src/test/ui/rfc-2091-track-caller/error-extern-fn.stderr deleted file mode 100644 index b03f5fbbdb20e..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-extern-fn.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0738]: `#[track_caller]` is not supported on foreign functions - --> $DIR/error-extern-fn.rs:5:5 - | -LL | #[track_caller] - | ^^^^^^^^^^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0738`. diff --git a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs index 20d29619ba404..1145f7786c78b 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs +++ b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs @@ -4,4 +4,10 @@ extern "C" fn f() {} //~^^ ERROR `#[track_caller]` requires Rust ABI +extern "C" { + #[track_caller] + fn g(); + //~^^ ERROR `#[track_caller]` requires Rust ABI +} + fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr index 2a3a4385c8bf7..e08c52fabd6b7 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr +++ b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr @@ -4,6 +4,12 @@ error[E0737]: `#[track_caller]` requires Rust ABI LL | #[track_caller] | ^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0737]: `#[track_caller]` requires Rust ABI + --> $DIR/error-with-invalid-abi.rs:8:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0737`. diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs index dd9e5d0413585..f457384833335 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs @@ -1,8 +1,21 @@ #![feature(naked_functions, track_caller)] -#[track_caller] +#[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` #[naked] fn f() {} -//~^^^ ERROR cannot use `#[track_caller]` with `#[naked]` + +struct S; + +impl S { + #[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` + #[naked] + fn g() {} +} + +extern "Rust" { + #[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` + #[naked] + fn h(); +} fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr index 2f5003cfdb7a5..1249d1df07179 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr @@ -4,6 +4,18 @@ error[E0736]: cannot use `#[track_caller]` with `#[naked]` LL | #[track_caller] | ^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0736]: cannot use `#[track_caller]` with `#[naked]` + --> $DIR/error-with-naked.rs:16:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error[E0736]: cannot use `#[track_caller]` with `#[naked]` + --> $DIR/error-with-naked.rs:10:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0736`. diff --git a/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs b/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs index be13076b8af52..35a2956ee26b8 100644 --- a/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs +++ b/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs @@ -2,10 +2,14 @@ // ignore-wasm32-bare compiled with panic=abort by default #![feature(option_expect_none, option_unwrap_none)] +#![allow(unconditional_panic)] //! Test that panic locations for `#[track_caller]` functions in std have the correct //! location reported. +use std::collections::{BTreeMap, HashMap, VecDeque}; +use std::ops::{Index, IndexMut}; + fn main() { // inspect the `PanicInfo` we receive to ensure the right file is the source std::panic::set_hook(Box::new(|info| { @@ -35,4 +39,22 @@ fn main() { let fine: Result<(), ()> = Ok(()); assert_panicked(|| fine.unwrap_err()); assert_panicked(|| fine.expect_err("")); + + let mut small = [0]; // the implementation backing str, vec, etc + assert_panicked(move || { small.index(1); }); + assert_panicked(move || { small[1]; }); + assert_panicked(move || { small.index_mut(1); }); + assert_panicked(move || { small[1] += 1; }); + + let sorted: BTreeMap = Default::default(); + assert_panicked(|| { sorted.index(&false); }); + assert_panicked(|| { sorted[&false]; }); + + let unsorted: HashMap = Default::default(); + assert_panicked(|| { unsorted.index(&false); }); + assert_panicked(|| { unsorted[&false]; }); + + let weirdo: VecDeque<()> = Default::default(); + assert_panicked(|| { weirdo.index(1); }); + assert_panicked(|| { weirdo[1]; }); } diff --git a/src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs b/src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs new file mode 100644 index 0000000000000..23c17d743c43c --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs @@ -0,0 +1,50 @@ +// run-pass + +#![feature(track_caller)] + +use std::panic::Location; + +extern "Rust" { + #[track_caller] + fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static>; + fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static>; +} + +fn rust_track_caller_ffi_test_nested_tracked() -> &'static Location<'static> { + unsafe { rust_track_caller_ffi_test_tracked() } +} + +mod provides { + use std::panic::Location; + #[track_caller] // UB if we did not have this! + #[no_mangle] + fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static> { + Location::caller() + } + #[no_mangle] + fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static> { + Location::caller() + } +} + +fn main() { + let location = Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), 31); + assert_eq!(location.column(), 20); + + let tracked = unsafe { rust_track_caller_ffi_test_tracked() }; + assert_eq!(tracked.file(), file!()); + assert_eq!(tracked.line(), 36); + assert_eq!(tracked.column(), 28); + + let untracked = unsafe { rust_track_caller_ffi_test_untracked() }; + assert_eq!(untracked.file(), file!()); + assert_eq!(untracked.line(), 26); + assert_eq!(untracked.column(), 9); + + let contained = rust_track_caller_ffi_test_nested_tracked(); + assert_eq!(contained.file(), file!()); + assert_eq!(contained.line(), 14); + assert_eq!(contained.column(), 14); +} diff --git a/src/test/ui/rfc-2457/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs b/src/test/ui/rfc-2457/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs new file mode 100644 index 0000000000000..e373b64384f92 --- /dev/null +++ b/src/test/ui/rfc-2457/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs @@ -0,0 +1 @@ +pub trait Foo {} diff --git a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs new file mode 100644 index 0000000000000..efd2932f15294 --- /dev/null +++ b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs @@ -0,0 +1,6 @@ +#![feature(non_ascii_idents)] + +mod řųśť; //~ trying to load file for +//~^ file not found for + +fn main() {} diff --git a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr new file mode 100644 index 0000000000000..be729836f4f21 --- /dev/null +++ b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr @@ -0,0 +1,20 @@ +error[E0583]: file not found for module `řųśť` + --> $DIR/mod_file_nonascii_forbidden.rs:3:1 + | +LL | mod řųśť; + | ^^^^^^^^^ + | + = help: to create the module `řųśť`, create file "$DIR/řųśť.rs" + +error[E0754]: trying to load file for module `řųśť` with non ascii identifer name + --> $DIR/mod_file_nonascii_forbidden.rs:3:5 + | +LL | mod řųśť; + | ^^^^ + | + = help: consider using `#[path]` attribute to specify filesystem path + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0583, E0754. +For more information about an error, try `rustc --explain E0583`. diff --git a/src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs b/src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs new file mode 100644 index 0000000000000..e9f3fba2fb01e --- /dev/null +++ b/src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs @@ -0,0 +1,7 @@ +// check-pass +#![feature(non_ascii_idents)] + +#[path="auxiliary/mod_file_nonascii_with_path_allowed-aux.rs"] +mod řųśť; + +fn main() {} diff --git a/src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs b/src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs new file mode 100644 index 0000000000000..dd27da432ba65 --- /dev/null +++ b/src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs @@ -0,0 +1,8 @@ +// check-pass +#![feature(non_ascii_idents)] + +mod řųśť { + const IS_GREAT: bool = true; +} + +fn main() {} diff --git a/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs new file mode 100644 index 0000000000000..a408c9757165c --- /dev/null +++ b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs @@ -0,0 +1,6 @@ +#![feature(non_ascii_idents)] + +#[no_mangle] +pub fn řųśť() {} //~ `#[no_mangle]` requires ASCII identifier + +fn main() {} diff --git a/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr new file mode 100644 index 0000000000000..4ca83e4103208 --- /dev/null +++ b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr @@ -0,0 +1,9 @@ +error[E0754]: `#[no_mangle]` requires ASCII identifier + --> $DIR/no_mangle_nonascii_forbidden.rs:4:1 + | +LL | pub fn řųśť() {} + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0754`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 39874a6c680cd..4c3a00e5f3583 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -499,19 +499,22 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if`- and `while`-expressions = note: as well as when nested within `&&` and parenthesis in those conditions -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/disallowed-positions.rs:20:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information -warning: the feature `let_chains` is incomplete and may cause the compiler to crash +warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/disallowed-positions.rs:22:12 | LL | #![feature(let_chains)] // Avoid inflating `.stderr` with overzealous gates in this test. | ^^^^^^^^^^ + | + = note: see issue #53667 for more information error[E0658]: `match` is not allowed in a `const` --> $DIR/disallowed-positions.rs:218:17 @@ -980,7 +983,7 @@ LL | let 0 = 0?; = help: the trait `std::ops::Try` is not implemented for `{integer}` = note: required by `std::ops::Try::into_result` -error: aborting due to 106 previous errors +error: aborting due to 106 previous errors; 2 warnings emitted Some errors have detailed explanations: E0277, E0308, E0600, E0614, E0658. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr index cf8f0e98305ea..81cefdd29b3ea 100644 --- a/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr @@ -8,3 +8,5 @@ LL | if let _ = return true && false {}; | = note: `#[warn(unreachable_code)]` on by default +warning: 1 warning emitted + diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs index 5769366fb45a4..bf082932bd6ce 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs @@ -1,5 +1,5 @@ #![feature(raw_dylib)] -//~^ WARN the feature `raw_dylib` is incomplete and may cause the compiler to crash +//~^ WARN the feature `raw_dylib` is incomplete #[link(name="foo")] extern { diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr index 303a1c02eb85f..5d8545b506204 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr @@ -1,10 +1,11 @@ -warning: the feature `raw_dylib` is incomplete and may cause the compiler to crash +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/link-ordinal-and-name.rs:1:12 | LL | #![feature(raw_dylib)] | ^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information error: cannot use `#[link_name]` with `#[link_ordinal]` --> $DIR/link-ordinal-and-name.rs:7:5 @@ -12,5 +13,5 @@ error: cannot use `#[link_name]` with `#[link_ordinal]` LL | #[link_ordinal(42)] | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs index 82fb1151c23df..ea633c5bcce24 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs @@ -1,5 +1,5 @@ #![feature(raw_dylib)] -//~^ WARN the feature `raw_dylib` is incomplete and may cause the compiler to crash +//~^ WARN the feature `raw_dylib` is incomplete #[link(name="foo")] extern { diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr index 14556a7262b1c..8453a3966bee5 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr @@ -1,10 +1,11 @@ -warning: the feature `raw_dylib` is incomplete and may cause the compiler to crash +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/link-ordinal-invalid-format.rs:1:12 | LL | #![feature(raw_dylib)] | ^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information error: illegal ordinal format in `link_ordinal` --> $DIR/link-ordinal-invalid-format.rs:6:5 @@ -14,5 +15,5 @@ LL | #[link_ordinal("JustMonika")] | = note: an unsuffixed integer value, e.g., `1`, is expected -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs index 69596ad04fff3..55cc329dc594b 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs @@ -1,5 +1,5 @@ #![feature(raw_dylib)] -//~^ WARN the feature `raw_dylib` is incomplete and may cause the compiler to crash +//~^ WARN the feature `raw_dylib` is incomplete #[link(name="foo")] extern { diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr index b3b22f9776df7..35f9b53fdf720 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr @@ -1,10 +1,11 @@ -warning: the feature `raw_dylib` is incomplete and may cause the compiler to crash +warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/link-ordinal-too-large.rs:1:12 | LL | #![feature(raw_dylib)] | ^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #58713 for more information error: ordinal value in `link_ordinal` is too large: `18446744073709551616` --> $DIR/link-ordinal-too-large.rs:6:5 @@ -12,7 +13,7 @@ error: ordinal value in `link_ordinal` is too large: `18446744073709551616` LL | #[link_ordinal(18446744073709551616)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the value may not exceed `std::usize::MAX` + = note: the value may not exceed `usize::MAX` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/rfc-2632-const-trait-impl/hir-const-check.rs b/src/test/ui/rfc-2632-const-trait-impl/hir-const-check.rs new file mode 100644 index 0000000000000..f7af1b506f0db --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/hir-const-check.rs @@ -0,0 +1,16 @@ +// Regression test for #69615. + +#![feature(const_trait_impl, const_fn)] +#![allow(incomplete_features)] + +pub trait MyTrait { + fn method(&self); +} + +impl const MyTrait for () { + fn method(&self) { + match *self {} //~ ERROR `match` is not allowed in a `const fn` + } +} + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/hir-const-check.stderr b/src/test/ui/rfc-2632-const-trait-impl/hir-const-check.stderr new file mode 100644 index 0000000000000..563a9afe5bb84 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/hir-const-check.stderr @@ -0,0 +1,12 @@ +error[E0658]: `match` is not allowed in a `const fn` + --> $DIR/hir-const-check.rs:12:9 + | +LL | match *self {} + | ^^^^^^^^^^^^^^ + | + = note: see issue #49146 for more information + = help: add `#![feature(const_if_match)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs index e9dcb4f85f60b..c663535e533bd 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs +++ b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs @@ -1,6 +1,6 @@ // This is part of a set of tests exploring the different ways a -// `#[structural_match]` ADT might try to hold a -// non-`#[structural_match]` in hidden manner that lets matches +// structural-match ADT might try to hold a +// non-structural-match in hidden manner that lets matches // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs index ab1cb3babaa25..872bf5a63fffd 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs +++ b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs @@ -1,6 +1,6 @@ // This is part of a set of tests exploring the different ways a -// `#[structural_match]` ADT might try to hold a -// non-`#[structural_match]` in hidden manner that lets matches +// structural-match ADT might try to hold a +// non-structural-match in hidden manner that lets matches // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs index 0328db5a49cf3..f6947819695a6 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs @@ -1,6 +1,6 @@ // This is part of a set of tests exploring the different ways a -// `#[structural_match]` ADT might try to hold a -// non-`#[structural_match]` in hidden manner that lets matches +// structural-match ADT might try to hold a +// non-structural-match in hidden manner that lets matches // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr index 030deda9af62e..659a981267233 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr @@ -12,3 +12,5 @@ LL | #![warn(indirect_structural_match)] = 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 #62411 +warning: 1 warning emitted + diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs index 54579e487a6b9..1c29d67b65529 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs @@ -1,6 +1,6 @@ // This is part of a set of tests exploring the different ways a -// `#[structural_match]` ADT might try to hold a -// non-`#[structural_match]` in hidden manner that lets matches +// structural-match ADT might try to hold a +// non-structural-match in hidden manner that lets matches // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr index e274d8ed0848c..c8c36510542a2 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr @@ -12,3 +12,5 @@ LL | #![warn(indirect_structural_match)] = 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 #62411 +warning: 1 warning emitted + diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs index 2a24316898b5d..1a41dbb55c2e9 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs @@ -1,6 +1,6 @@ // This is part of a set of tests exploring the different ways a -// `#[structural_match]` ADT might try to hold a -// non-`#[structural_match]` in hidden manner that lets matches +// structural-match ADT might try to hold a +// non-structural-match in hidden manner that lets matches // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr index 067677fbfb6af..8abbd5d342be7 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr @@ -12,3 +12,5 @@ LL | #![warn(indirect_structural_match)] = 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 #62411 +warning: 1 warning emitted + diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs index 64e777f232234..46032c4b0ebd4 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs @@ -1,6 +1,6 @@ // This is part of a set of tests exploring the different ways a -// `#[structural_match]` ADT might try to hold a -// non-`#[structural_match]` in hidden manner that lets matches +// structural-match ADT might try to hold a +// non-structural-match in hidden manner that lets matches // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr index 31b294f379ce8..3a716d54fcc2c 100644 --- a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr @@ -12,3 +12,5 @@ LL | #![warn(indirect_structural_match)] = 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 #62411 +warning: 1 warning emitted + diff --git a/src/test/ui/rfc1445/feature-gate.rs b/src/test/ui/rfc1445/feature-gate.rs index 21addfab8f50d..ee6674097ce2d 100644 --- a/src/test/ui/rfc1445/feature-gate.rs +++ b/src/test/ui/rfc1445/feature-gate.rs @@ -1,4 +1,4 @@ -// Test that structural match is only permitted with a feature gate, +// Test that use of structural-match traits is only permitted with a feature gate, // and that if a feature gate is supplied, it permits the type to be // used in a match. diff --git a/src/test/ui/rfc1445/fn-ptr-is-structurally-matchable.rs b/src/test/ui/rfc1445/fn-ptr-is-structurally-matchable.rs index 5b378fb2a5928..2b3fbd2a4d28a 100644 --- a/src/test/ui/rfc1445/fn-ptr-is-structurally-matchable.rs +++ b/src/test/ui/rfc1445/fn-ptr-is-structurally-matchable.rs @@ -36,7 +36,7 @@ fn main() { // a singleton type of the fn itself that the type inference would // otherwise assign. - // Check that fn() is #[structural_match] + // Check that fn() is structural-match const CFN1: Wrap = Wrap(trivial); let input: Wrap = Wrap(trivial); match Wrap(input) { @@ -44,7 +44,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn(T) is #[structural_match] when T is too. + // Check that fn(T) is structural-match when T is too. const CFN2: Wrap = Wrap(sm_to); let input: Wrap = Wrap(sm_to); match Wrap(input) { @@ -52,7 +52,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn() -> T is #[structural_match] when T is too. + // Check that fn() -> T is structural-match when T is too. const CFN3: Wrap SM> = Wrap(to_sm); let input: Wrap SM> = Wrap(to_sm); match Wrap(input) { @@ -60,7 +60,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn(T) is #[structural_match] even if T is not. + // Check that fn(T) is structural-match even if T is not. const CFN4: Wrap = Wrap(not_sm_to); let input: Wrap = Wrap(not_sm_to); match Wrap(input) { @@ -68,7 +68,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn() -> T is #[structural_match] even if T is not. + // Check that fn() -> T is structural-match even if T is not. const CFN5: Wrap NotSM> = Wrap(to_not_sm); let input: Wrap NotSM> = Wrap(to_not_sm); match Wrap(input) { @@ -76,7 +76,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn(&T) is #[structural_match] when T is too. + // Check that fn(&T) is structural-match when T is too. const CFN6: Wrap = Wrap(r_sm_to); let input: Wrap = Wrap(r_sm_to); match Wrap(input) { @@ -84,7 +84,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn() -> &T is #[structural_match] when T is too. + // Check that fn() -> &T is structural-match when T is too. const CFN7: Wrap &SM> = Wrap(r_to_r_sm); let input: Wrap &SM> = Wrap(r_to_r_sm); match Wrap(input) { @@ -92,7 +92,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn(T) is #[structural_match] even if T is not. + // Check that fn(T) is structural-match even if T is not. const CFN8: Wrap = Wrap(r_not_sm_to); let input: Wrap = Wrap(r_not_sm_to); match Wrap(input) { @@ -100,7 +100,7 @@ fn main() { Wrap(_) => {} }; - // Check that fn() -> T is #[structural_match] even if T is not. + // Check that fn() -> T is structural-match even if T is not. const CFN9: Wrap &NotSM> = Wrap(r_to_r_not_sm); let input: Wrap &NotSM> = Wrap(r_to_r_not_sm); match Wrap(input) { @@ -108,7 +108,7 @@ fn main() { Wrap(_) => {} }; - // Check that a type which has fn ptrs is `#[structural_match]`. + // Check that a type which has fn ptrs is structural-match. #[derive(PartialEq, Eq)] struct Foo { alpha: fn(NotSM), diff --git a/src/test/ui/rfc1445/issue-61188-match-slice-forbidden-without-eq.rs b/src/test/ui/rfc1445/issue-61188-match-slice-forbidden-without-eq.rs index e288beca09081..2a915d61e3d90 100644 --- a/src/test/ui/rfc1445/issue-61188-match-slice-forbidden-without-eq.rs +++ b/src/test/ui/rfc1445/issue-61188-match-slice-forbidden-without-eq.rs @@ -1,7 +1,7 @@ // Issue 61188 pointed out a case where we hit an ICE during code gen: // the compiler assumed that `PartialEq` was always implemented on any // use of a `const` item in a pattern context, but the pre-existing -// checking for the presence of `#[structural_match]` was too shallow +// structural-match checking was too shallow // (see rust-lang/rust#62307), and so we hit cases where we were // trying to dispatch to `PartialEq` on types that did not implement // that trait. diff --git a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs index 98943a9666a9a..6ebb948d736ec 100644 --- a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs +++ b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs @@ -8,8 +8,8 @@ // resolve the question of what semantics is used for such matching. // (See RFC 1445 for more details and discussion.) -// Issue 62307 pointed out a case where the checking for -// `#[structural_match]` was too shallow. +// Issue 62307 pointed out a case where the structural-match checking +// was too shallow. #![warn(indirect_structural_match)] // run-pass diff --git a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr index c495c37f6a157..ae011dfcdba90 100644 --- a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr +++ b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr @@ -21,3 +21,5 @@ LL | RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); = 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 #62411 +warning: 2 warnings emitted + diff --git a/src/test/ui/rfc1445/match-forbidden-without-eq.stderr b/src/test/ui/rfc1445/match-forbidden-without-eq.stderr index b9476e399f3e7..1f26f0f11dc14 100644 --- a/src/test/ui/rfc1445/match-forbidden-without-eq.stderr +++ b/src/test/ui/rfc1445/match-forbidden-without-eq.stderr @@ -29,5 +29,5 @@ LL | f32::INFINITY => { } = 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 #41620 -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 2 warnings emitted diff --git a/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs index 9ef8a68da80b9..4112e8f45179c 100644 --- a/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs +++ b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs @@ -1,5 +1,5 @@ -// Issue 62307 pointed out a case where the checking for -// `#[structural_match]` was too shallow. +// Issue 62307 pointed out a case where the structural-match checking +// was too shallow. // // Here we check similar behavior for non-empty arrays of types that // do not derive `Eq`. diff --git a/src/test/ui/rfc1445/phantom-data-is-structurally-matchable.rs b/src/test/ui/rfc1445/phantom-data-is-structurally-matchable.rs index af025b9bbbf76..50f91420ce2f1 100644 --- a/src/test/ui/rfc1445/phantom-data-is-structurally-matchable.rs +++ b/src/test/ui/rfc1445/phantom-data-is-structurally-matchable.rs @@ -14,25 +14,25 @@ fn main() { #[derive(PartialEq, Eq)] struct SM; - // Check that SM is #[structural_match]: + // Check that SM is structural-match: const CSM: SM = SM; match SM { CSM => count += 1, }; - // Check that PhantomData is #[structural_match] even if T is not. + // Check that PhantomData is structural-match even if T is not. const CPD1: PhantomData = PhantomData; match PhantomData { CPD1 => count += 1, }; - // Check that PhantomData is #[structural_match] when T is. + // Check that PhantomData is structural-match when T is. const CPD2: PhantomData = PhantomData; match PhantomData { CPD2 => count += 1, }; - // Check that a type which has a PhantomData is `#[structural_match]`. + // Check that a type which has a PhantomData is structural-match. #[derive(PartialEq, Eq, Default)] struct Foo { alpha: PhantomData, diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs new file mode 100644 index 0000000000000..58a2c271ecfbc --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs @@ -0,0 +1,50 @@ +// Tests the new rules added by RFC 2396, including: +// - applying `#[target_feature]` to safe functions is allowed +// - calling functions with `#[target_feature]` is allowed in +// functions which have (at least) the same features +// - calling functions with `#[target_feature]` is allowed in +// unsafe contexts +// - functions with `#[target_feature]` can coerce to unsafe fn pointers + +// check-pass +// only-x86_64 + +#![feature(target_feature_11)] + +#[target_feature(enable = "sse2")] +const fn sse2() {} + +#[cfg(target_feature = "sse2")] +const SSE2_ONLY: () = unsafe { + sse2(); +}; + +#[target_feature(enable = "sse2")] +fn also_sse2() { + sse2(); +} + +#[target_feature(enable = "sse2")] +#[target_feature(enable = "avx")] +fn sse2_and_avx() { + sse2(); +} + +struct Foo; + +impl Foo { + #[target_feature(enable = "sse2")] + fn sse2(&self) { + sse2(); + } +} + +fn main() { + if cfg!(target_feature = "sse2") { + unsafe { + sse2(); + Foo.sse2(); + } + } + let sse2_ptr: unsafe fn() = sse2; +} diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs new file mode 100644 index 0000000000000..975d7a1f694c6 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs @@ -0,0 +1,6 @@ +// only-x86_64 + +#[target_feature(enable = "sse2")] //~ ERROR can only be applied to `unsafe` functions +fn foo() {} + +fn main() {} diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr new file mode 100644 index 0000000000000..413890f436d0f --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr @@ -0,0 +1,14 @@ +error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions + --> $DIR/feature-gate-target_feature_11.rs:3:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo() {} + | ----------- not an `unsafe` function + | + = note: see issue #69098 for more information + = help: add `#![feature(target_feature_11)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs new file mode 100644 index 0000000000000..3ecea5c531390 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs @@ -0,0 +1,10 @@ +// only-x86_64 + +#![feature(target_feature_11)] + +#[target_feature(enable = "sse2")] +fn foo() {} + +fn main() { + let foo: fn() = foo; //~ ERROR mismatched types +} diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr new file mode 100644 index 0000000000000..06cfdde3fb974 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/fn-ptr.rs:9:21 + | +LL | #[target_feature(enable = "sse2")] + | ---------------------------------- `#[target_feature]` added here +... +LL | let foo: fn() = foo; + | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers + | | + | expected due to this + | + = note: expected fn pointer `fn()` + found fn item `fn() {foo}` + = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs new file mode 100644 index 0000000000000..8da3affc4477b --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs @@ -0,0 +1,47 @@ +// only-x86_64 + +#![feature(target_feature_11)] + +#[target_feature(enable = "sse2")] +const fn sse2() {} + +#[target_feature(enable = "avx")] +#[target_feature(enable = "bmi2")] +fn avx_bmi2() {} + +struct Quux; + +impl Quux { + #[target_feature(enable = "avx")] + #[target_feature(enable = "bmi2")] + fn avx_bmi2(&self) {} +} + +fn foo() { + sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe + avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe + Quux.avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe +} + +#[target_feature(enable = "sse2")] +fn bar() { + avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe + Quux.avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe +} + +#[target_feature(enable = "avx")] +fn baz() { + sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe + avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe + Quux.avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe +} + +#[target_feature(enable = "avx")] +#[target_feature(enable = "bmi2")] +fn qux() { + sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe +} + +const name: () = sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe + +fn main() {} diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr new file mode 100644 index 0000000000000..b9f748640b558 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -0,0 +1,83 @@ +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:21:5 + | +LL | sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:22:5 + | +LL | avx_bmi2(); + | ^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:23:5 + | +LL | Quux.avx_bmi2(); + | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:28:5 + | +LL | avx_bmi2(); + | ^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:29:5 + | +LL | Quux.avx_bmi2(); + | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:34:5 + | +LL | sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:35:5 + | +LL | avx_bmi2(); + | ^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:36:5 + | +LL | Quux.avx_bmi2(); + | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:42:5 + | +LL | sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:45:18 + | +LL | const name: () = sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs new file mode 100644 index 0000000000000..7314fa8cced2a --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs @@ -0,0 +1,21 @@ +// only-x86_64 + +#![feature(target_feature_11)] + +trait Foo { + fn foo(&self); + unsafe fn unsf_foo(&self); +} + +struct Bar; + +impl Foo for Bar { + #[target_feature(enable = "sse2")] + //~^ ERROR cannot be applied to safe trait method + fn foo(&self) {} + + #[target_feature(enable = "sse2")] + unsafe fn unsf_foo(&self) {} +} + +fn main() {} diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr new file mode 100644 index 0000000000000..3c56e0fc5c6e3 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr @@ -0,0 +1,11 @@ +error: `#[target_feature(..)]` cannot be applied to safe trait method + --> $DIR/trait-impl.rs:13:5 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method +LL | +LL | fn foo(&self) {} + | ---------------- not an `unsafe` function + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/issue-52202-use-suggestions.stderr b/src/test/ui/rust-2018/issue-52202-use-suggestions.stderr index c712fd048f191..38cd9713d1a13 100644 --- a/src/test/ui/rust-2018/issue-52202-use-suggestions.stderr +++ b/src/test/ui/rust-2018/issue-52202-use-suggestions.stderr @@ -4,7 +4,7 @@ error[E0422]: cannot find struct, variant or union type `Drain` in this scope LL | let _d = Drain {}; | ^^^^^ not found in this scope | -help: possible candidates are found in other modules, you can import them into scope +help: consider importing one of these items | LL | use crate::plumbing::Drain; | diff --git a/src/test/ui/rust-2018/macro-use-warned-against.rs b/src/test/ui/rust-2018/macro-use-warned-against.rs index 65400163ddd86..72f2868e0bfd6 100644 --- a/src/test/ui/rust-2018/macro-use-warned-against.rs +++ b/src/test/ui/rust-2018/macro-use-warned-against.rs @@ -1,6 +1,6 @@ // aux-build:macro-use-warned-against.rs // aux-build:macro-use-warned-against2.rs -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(macro_use_extern_crate, unused)] diff --git a/src/test/ui/rust-2018/macro-use-warned-against.stderr b/src/test/ui/rust-2018/macro-use-warned-against.stderr index ef00b865815c0..6b46f002e32ee 100644 --- a/src/test/ui/rust-2018/macro-use-warned-against.stderr +++ b/src/test/ui/rust-2018/macro-use-warned-against.stderr @@ -23,3 +23,5 @@ LL | #![warn(macro_use_extern_crate, unused)] | ^^^^^^ = note: `#[warn(unused_imports)]` implied by `#[warn(unused)]` +warning: 2 warnings emitted + diff --git a/src/test/ui/rust-2018/remove-extern-crate.fixed b/src/test/ui/rust-2018/remove-extern-crate.fixed index 2e10d5555908b..832632268fb2e 100644 --- a/src/test/ui/rust-2018/remove-extern-crate.fixed +++ b/src/test/ui/rust-2018/remove-extern-crate.fixed @@ -1,6 +1,6 @@ // run-rustfix // edition:2018 -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // aux-build:remove-extern-crate.rs // compile-flags:--extern remove_extern_crate diff --git a/src/test/ui/rust-2018/remove-extern-crate.rs b/src/test/ui/rust-2018/remove-extern-crate.rs index 9b04f901310ea..bbb84cd462d7e 100644 --- a/src/test/ui/rust-2018/remove-extern-crate.rs +++ b/src/test/ui/rust-2018/remove-extern-crate.rs @@ -1,6 +1,6 @@ // run-rustfix // edition:2018 -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // aux-build:remove-extern-crate.rs // compile-flags:--extern remove_extern_crate diff --git a/src/test/ui/rust-2018/remove-extern-crate.stderr b/src/test/ui/rust-2018/remove-extern-crate.stderr index 8df93c56e93f0..bde4c180811fa 100644 --- a/src/test/ui/rust-2018/remove-extern-crate.stderr +++ b/src/test/ui/rust-2018/remove-extern-crate.stderr @@ -17,3 +17,5 @@ warning: `extern crate` is not idiomatic in the new edition LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: convert it to a `use` +warning: 2 warnings emitted + diff --git a/src/test/ui/rust-2018/suggestions-not-always-applicable.fixed b/src/test/ui/rust-2018/suggestions-not-always-applicable.fixed index 7d136667b6dfb..12348e6e07dbc 100644 --- a/src/test/ui/rust-2018/suggestions-not-always-applicable.fixed +++ b/src/test/ui/rust-2018/suggestions-not-always-applicable.fixed @@ -2,7 +2,7 @@ // edition:2015 // run-rustfix // rustfix-only-machine-applicable -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(rust_2018_preview)] #![warn(rust_2018_compatibility)] diff --git a/src/test/ui/rust-2018/suggestions-not-always-applicable.rs b/src/test/ui/rust-2018/suggestions-not-always-applicable.rs index 7d136667b6dfb..12348e6e07dbc 100644 --- a/src/test/ui/rust-2018/suggestions-not-always-applicable.rs +++ b/src/test/ui/rust-2018/suggestions-not-always-applicable.rs @@ -2,7 +2,7 @@ // edition:2015 // run-rustfix // rustfix-only-machine-applicable -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(rust_2018_preview)] #![warn(rust_2018_compatibility)] diff --git a/src/test/ui/rust-2018/suggestions-not-always-applicable.stderr b/src/test/ui/rust-2018/suggestions-not-always-applicable.stderr index 30f98d6df9e09..45502a5b88081 100644 --- a/src/test/ui/rust-2018/suggestions-not-always-applicable.stderr +++ b/src/test/ui/rust-2018/suggestions-not-always-applicable.stderr @@ -24,3 +24,5 @@ LL | #[foo] = note: for more information, see issue #53130 = note: this warning originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) +warning: 2 warnings emitted + diff --git a/src/test/ui/rust-2018/try-ident.fixed b/src/test/ui/rust-2018/try-ident.fixed index f285b2c0ee6fa..13f6f8e28291d 100644 --- a/src/test/ui/rust-2018/try-ident.fixed +++ b/src/test/ui/rust-2018/try-ident.fixed @@ -1,5 +1,5 @@ // run-rustfix -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(rust_2018_compatibility)] diff --git a/src/test/ui/rust-2018/try-ident.rs b/src/test/ui/rust-2018/try-ident.rs index d740801b562c8..bed7118011e9a 100644 --- a/src/test/ui/rust-2018/try-ident.rs +++ b/src/test/ui/rust-2018/try-ident.rs @@ -1,5 +1,5 @@ // run-rustfix -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(rust_2018_compatibility)] diff --git a/src/test/ui/rust-2018/try-ident.stderr b/src/test/ui/rust-2018/try-ident.stderr index a5cb839ec0ee4..2939dc1df705a 100644 --- a/src/test/ui/rust-2018/try-ident.stderr +++ b/src/test/ui/rust-2018/try-ident.stderr @@ -22,3 +22,5 @@ LL | fn try() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! = note: for more information, see issue #49716 +warning: 2 warnings emitted + diff --git a/src/test/ui/rust-2018/try-macro.stderr b/src/test/ui/rust-2018/try-macro.stderr index b92d5048b3899..cdbb215605e71 100644 --- a/src/test/ui/rust-2018/try-macro.stderr +++ b/src/test/ui/rust-2018/try-macro.stderr @@ -13,3 +13,5 @@ LL | #![warn(rust_2018_compatibility)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! = note: for more information, see issue #49716 +warning: 1 warning emitted + diff --git a/src/test/ui/sanitize-inline-always.stderr b/src/test/ui/sanitize-inline-always.stderr deleted file mode 100644 index 50b9474baa2d6..0000000000000 --- a/src/test/ui/sanitize-inline-always.stderr +++ /dev/null @@ -1,13 +0,0 @@ -warning: `no_sanitize` will have no effect after inlining - --> $DIR/sanitize-inline-always.rs:7:1 - | -LL | #[no_sanitize(address)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(inline_no_sanitize)]` on by default -note: inlining requested here - --> $DIR/sanitize-inline-always.rs:5:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - diff --git a/src/test/ui/sanitize-inline-always.rs b/src/test/ui/sanitize/inline-always.rs similarity index 100% rename from src/test/ui/sanitize-inline-always.rs rename to src/test/ui/sanitize/inline-always.rs diff --git a/src/test/ui/sanitize/inline-always.stderr b/src/test/ui/sanitize/inline-always.stderr new file mode 100644 index 0000000000000..918762d1f66df --- /dev/null +++ b/src/test/ui/sanitize/inline-always.stderr @@ -0,0 +1,15 @@ +warning: `no_sanitize` will have no effect after inlining + --> $DIR/inline-always.rs:7:1 + | +LL | #[no_sanitize(address)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(inline_no_sanitize)]` on by default +note: inlining requested here + --> $DIR/inline-always.rs:5:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/src/test/ui/sanitize/issue-72154-lifetime-markers.rs b/src/test/ui/sanitize/issue-72154-lifetime-markers.rs new file mode 100644 index 0000000000000..458f99143b648 --- /dev/null +++ b/src/test/ui/sanitize/issue-72154-lifetime-markers.rs @@ -0,0 +1,31 @@ +// Regression test for issue 72154, where the use of AddressSanitizer enabled +// emission of lifetime markers during codegen, while at the same time asking +// always inliner pass not to insert them. This eventually lead to a +// miscompilation which was subsequently detected by AddressSanitizer as UB. +// +// needs-sanitizer-support +// only-x86_64 +// +// compile-flags: -Copt-level=0 -Zsanitizer=address +// run-pass + +pub struct Wrap { + pub t: [usize; 1] +} + +impl Wrap { + #[inline(always)] + pub fn new(t: [usize; 1]) -> Self { + Wrap { t } + } +} + +#[inline(always)] +pub fn assume_init() -> [usize; 1] { + [1234] +} + +fn main() { + let x: [usize; 1] = assume_init(); + Wrap::new(x); +} diff --git a/src/test/ui/sanitize/thread.rs b/src/test/ui/sanitize/thread.rs new file mode 100644 index 0000000000000..26590be8b1870 --- /dev/null +++ b/src/test/ui/sanitize/thread.rs @@ -0,0 +1,57 @@ +// Verifies that ThreadSanitizer is able to detect a data race in heap allocated +// memory block. +// +// Test case minimizes the use of the standard library to avoid its ambiguous +// status with respect to instrumentation (it could vary depending on whatever +// a function call is inlined or not). +// +// The conflicting data access is de-facto synchronized with a special TSAN +// barrier, which does not introduce synchronization from TSAN perspective, but +// is necessary to make the test robust. Without the barrier data race detection +// would occasionally fail, making test flaky. +// +// needs-sanitizer-support +// only-x86_64 +// +// compile-flags: -Z sanitizer=thread -O +// +// run-fail +// error-pattern: WARNING: ThreadSanitizer: data race +// error-pattern: Location is heap block of size 4 +// error-pattern: allocated by main thread + +#![feature(raw_ref_op)] +#![feature(rustc_private)] +extern crate libc; + +use std::mem; +use std::ptr; + +static mut BARRIER: u64 = 0; + +extern "C" { + fn __tsan_testonly_barrier_init(barrier: *mut u64, count: u32); + fn __tsan_testonly_barrier_wait(barrier: *mut u64); +} + +extern "C" fn start(c: *mut libc::c_void) -> *mut libc::c_void { + unsafe { + let c: *mut u32 = c.cast(); + *c += 1; + __tsan_testonly_barrier_wait(&raw mut BARRIER); + ptr::null_mut() + } +} + +fn main() { + unsafe { + __tsan_testonly_barrier_init(&raw mut BARRIER, 2); + let c: *mut u32 = Box::into_raw(Box::new(1)); + let mut t: libc::pthread_t = mem::zeroed(); + libc::pthread_create(&mut t, ptr::null(), start, c.cast()); + __tsan_testonly_barrier_wait(&raw mut BARRIER); + *c += 1; + libc::pthread_join(t, ptr::null_mut()); + Box::from_raw(c); + } +} diff --git a/src/test/ui/save-analysis/issue-72267.rs b/src/test/ui/save-analysis/issue-72267.rs new file mode 100644 index 0000000000000..eea0a7fea0cef --- /dev/null +++ b/src/test/ui/save-analysis/issue-72267.rs @@ -0,0 +1,7 @@ +// compile-flags: -Z save-analysis + +fn main() { + let _: Box<(dyn ?Sized)>; + //~^ ERROR `?Trait` is not permitted in trait object types + //~| ERROR at least one trait is required for an object type +} diff --git a/src/test/ui/save-analysis/issue-72267.stderr b/src/test/ui/save-analysis/issue-72267.stderr new file mode 100644 index 0000000000000..76fc6c57cbc36 --- /dev/null +++ b/src/test/ui/save-analysis/issue-72267.stderr @@ -0,0 +1,15 @@ +error: `?Trait` is not permitted in trait object types + --> $DIR/issue-72267.rs:4:21 + | +LL | let _: Box<(dyn ?Sized)>; + | ^^^^^^ + +error[E0224]: at least one trait is required for an object type + --> $DIR/issue-72267.rs:4:17 + | +LL | let _: Box<(dyn ?Sized)>; + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0224`. diff --git a/src/test/ui/self/self_type_keyword.stderr b/src/test/ui/self/self_type_keyword.stderr index fa603276c8eb5..7997cdc2957b4 100644 --- a/src/test/ui/self/self_type_keyword.stderr +++ b/src/test/ui/self/self_type_keyword.stderr @@ -66,7 +66,7 @@ error[E0531]: cannot find unit struct, unit variant or constant `Self` in this s LL | mut Self => (), | ^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this unit struct | LL | use foo::Self; | diff --git a/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs b/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs index b2ddcf023ebcb..e664ecadda259 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs @@ -1,6 +1,5 @@ // run-pass // ignore-emscripten -// min-llvm-version 8.0 #![allow(non_camel_case_types)] #![feature(repr_simd, platform_intrinsics)] diff --git a/src/test/ui/span/destructor-restrictions.stderr b/src/test/ui/span/destructor-restrictions.stderr index a3c6cfb6ae447..b5cd29f99c6b4 100644 --- a/src/test/ui/span/destructor-restrictions.stderr +++ b/src/test/ui/span/destructor-restrictions.stderr @@ -11,7 +11,12 @@ LL | }; | | | `*a` dropped here while still borrowed | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = *a.borrow() + 1; x + | ^^^^^^^ ^^^ error: aborting due to previous error diff --git a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr index 4696945814586..46702d364a8f3 100644 --- a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr +++ b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr @@ -12,7 +12,12 @@ LL | } | `y` dropped here while still borrowed | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, std::string::String>` | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = y.borrow().clone(); x + | ^^^^^^^ ^^^ error[E0597]: `y` does not live long enough --> $DIR/issue-23338-locals-die-before-temps-of-body.rs:17:9 @@ -27,7 +32,12 @@ LL | }; | | | `y` dropped here while still borrowed | - = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block. + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = y.borrow().clone(); x + | ^^^^^^^ ^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/span/issue-24690.stderr b/src/test/ui/span/issue-24690.stderr index 2a090f442cc5f..73e166e6403e4 100644 --- a/src/test/ui/span/issue-24690.stderr +++ b/src/test/ui/span/issue-24690.stderr @@ -25,3 +25,5 @@ warning: variable `theOtherTwo` should have a snake case name LL | let theOtherTwo = 2; | ^^^^^^^^^^^ help: convert the identifier to snake case: `the_other_two` +warning: 3 warnings emitted + diff --git a/src/test/ui/span/issue-35987.stderr b/src/test/ui/span/issue-35987.stderr index 3245d8655e87d..2bc3ff4c3b619 100644 --- a/src/test/ui/span/issue-35987.stderr +++ b/src/test/ui/span/issue-35987.stderr @@ -4,7 +4,7 @@ error[E0404]: expected trait, found type parameter `Add` LL | impl Add for Foo { | ^^^ not a trait | -help: possible better candidate is found in another module, you can import it into scope +help: consider importing this trait instead | LL | use std::ops::Add; | diff --git a/src/test/ui/span/issue-37767.stderr b/src/test/ui/span/issue-37767.stderr index 9ed6c8b826f79..fc6c556c16d88 100644 --- a/src/test/ui/span/issue-37767.stderr +++ b/src/test/ui/span/issue-37767.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in the trait `B` | LL | fn foo(&mut self) {} | ^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | A::foo(&a) | ^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | B::foo(&a) | ^^^^^^^^^^ @@ -39,11 +39,11 @@ note: candidate #2 is defined in the trait `D` | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | C::foo(&a) | ^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | D::foo(&a) | ^^^^^^^^^^ @@ -64,11 +64,11 @@ note: candidate #2 is defined in the trait `F` | LL | fn foo(self) {} | ^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | E::foo(a) | ^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | F::foo(a) | ^^^^^^^^^ diff --git a/src/test/ui/span/issue-7575.stderr b/src/test/ui/span/issue-7575.stderr index 89b36848a2897..16a1ac6d71814 100644 --- a/src/test/ui/span/issue-7575.stderr +++ b/src/test/ui/span/issue-7575.stderr @@ -25,15 +25,15 @@ LL | fn f9(_: usize) -> usize; candidate #1: `CtxtFn` candidate #2: `OtherTrait` candidate #3: `UnusedTrait` -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | u.f8(42) + CtxtFn::f9(u, 342) + m.fff(42) | ^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | u.f8(42) + OtherTrait::f9(u, 342) + m.fff(42) | ^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #3 +help: disambiguate the associated function for candidate #3 | LL | u.f8(42) + UnusedTrait::f9(u, 342) + m.fff(42) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | t.is_str() | --^^^^^^-- | | | | | this is an associated function, not a method - | help: disambiguate the method call for the candidate: `ManyImplTrait::is_str(t)` + | help: disambiguate the associated function for the candidate: `ManyImplTrait::is_str(t)` | = note: found the following associated functions; to be used as methods, functions must have a `self` parameter note: the candidate is defined in the trait `ManyImplTrait` diff --git a/src/test/ui/span/macro-span-replacement.rs b/src/test/ui/span/macro-span-replacement.rs index 04c7ab0ea586f..25df4a2aa3860 100644 --- a/src/test/ui/span/macro-span-replacement.rs +++ b/src/test/ui/span/macro-span-replacement.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(unused)] diff --git a/src/test/ui/span/macro-span-replacement.stderr b/src/test/ui/span/macro-span-replacement.stderr index 721d3b121726b..45cf5f8688cd1 100644 --- a/src/test/ui/span/macro-span-replacement.stderr +++ b/src/test/ui/span/macro-span-replacement.stderr @@ -15,3 +15,5 @@ LL | #![warn(unused)] = note: `#[warn(dead_code)]` implied by `#[warn(unused)]` = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +warning: 1 warning emitted + diff --git a/src/test/ui/span/multispan-import-lint.rs b/src/test/ui/span/multispan-import-lint.rs index a49c60e1277f3..3ce7f2ce35da8 100644 --- a/src/test/ui/span/multispan-import-lint.rs +++ b/src/test/ui/span/multispan-import-lint.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(unused)] diff --git a/src/test/ui/span/multispan-import-lint.stderr b/src/test/ui/span/multispan-import-lint.stderr index 4faa9e128d19a..4a955d1b31f58 100644 --- a/src/test/ui/span/multispan-import-lint.stderr +++ b/src/test/ui/span/multispan-import-lint.stderr @@ -11,3 +11,5 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_imports)]` implied by `#[warn(unused)]` +warning: 1 warning emitted + diff --git a/src/test/ui/span/unused-warning-point-at-identifier.stderr b/src/test/ui/span/unused-warning-point-at-identifier.stderr index f8d38d7b4b8fb..6ef877da122f5 100644 --- a/src/test/ui/span/unused-warning-point-at-identifier.stderr +++ b/src/test/ui/span/unused-warning-point-at-identifier.stderr @@ -29,3 +29,5 @@ warning: function is never used: `func_complete_span` LL | func_complete_span() | ^^^^^^^^^^^^^^^^^^ +warning: 4 warnings emitted + diff --git a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr index ee7c002b16db1..f499c1f569859 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr +++ b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr @@ -1,14 +1,16 @@ error[E0277]: the trait bound `U: std::cmp::Eq` is not satisfied --> $DIR/specialization-wfcheck.rs:7:17 | +LL | trait Foo<'a, T: Eq + 'a> { } + | -- required by this bound in `Foo` +LL | LL | default impl Foo<'static, U> for () {} | ^^^^^^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `U` | -help: consider restricting this type parameter with `U: std::cmp::Eq` - --> $DIR/specialization-wfcheck.rs:7:14 +help: consider restricting type parameter `U` | -LL | default impl Foo<'static, U> for () {} - | ^ +LL | default impl Foo<'static, U> for () {} + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/specialization/defaultimpl/validation.rs b/src/test/ui/specialization/defaultimpl/validation.rs index 5b8a72104e31a..8134333c58f73 100644 --- a/src/test/ui/specialization/defaultimpl/validation.rs +++ b/src/test/ui/specialization/defaultimpl/validation.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![feature(specialization)] struct S; @@ -8,8 +8,9 @@ default impl S {} //~ ERROR inherent impls cannot be `default` 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 trait Tr {} -default impl !Tr for S {} //~ ERROR invalid negative impl +default impl !Tr for S {} //~ ERROR negative impls cannot be default impls fn main() {} diff --git a/src/test/ui/specialization/defaultimpl/validation.stderr b/src/test/ui/specialization/defaultimpl/validation.stderr index e03153f343b8e..254eaf51a646b 100644 --- a/src/test/ui/specialization/defaultimpl/validation.stderr +++ b/src/test/ui/specialization/defaultimpl/validation.stderr @@ -24,14 +24,18 @@ LL | default impl !Send for Z {} | | | default because of this -error[E0192]: invalid negative impl - --> $DIR/validation.rs:13:14 +error[E0750]: negative impls cannot be default impls + --> $DIR/validation.rs:10:1 | -LL | default impl !Tr for S {} - | ^^^ +LL | default impl !Send for Z {} + | ^^^^^^^ ^ + +error[E0750]: negative impls cannot be default impls + --> $DIR/validation.rs:14:1 | - = note: negative impls are only allowed for auto traits, like `Send` and `Sync` +LL | default impl !Tr for S {} + | ^^^^^^^ ^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0192`. +For more information about this error, try `rustc --explain E0750`. diff --git a/src/test/ui/specialization/issue-70442.rs b/src/test/ui/specialization/issue-70442.rs new file mode 100644 index 0000000000000..4371dd2e16747 --- /dev/null +++ b/src/test/ui/specialization/issue-70442.rs @@ -0,0 +1,23 @@ +#![feature(specialization)] + +// check-pass + +trait Trait { + type Assoc; +} + +impl Trait for T { + default type Assoc = bool; +} + +// This impl inherits the `Assoc` definition from above and "locks it in", or finalizes it, making +// child impls unable to further specialize it. However, since the specialization graph didn't +// correctly track this, we would refuse to project `Assoc` from this impl, even though that should +// happen for items that are final. +impl Trait for () {} + +fn foo>() {} + +fn main() { + foo::<()>(); // `<() as Trait>::Assoc` is normalized to `bool` correctly +} diff --git a/src/test/ui/specialization/specialization-default-projection.stderr b/src/test/ui/specialization/specialization-default-projection.stderr index d03aec7ab30d4..ac15ab0681a02 100644 --- a/src/test/ui/specialization/specialization-default-projection.stderr +++ b/src/test/ui/specialization/specialization-default-projection.stderr @@ -9,7 +9,7 @@ LL | () | = note: expected associated type `::Assoc` found unit type `()` - = note: consider constraining the associated type `::Assoc` to `()` or calling a method that returns `::Assoc` + = 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 @@ -25,7 +25,7 @@ LL | generic::<()>() | = note: expected unit type `()` found associated type `<() as Foo>::Assoc` - = note: consider constraining the associated type `<() as Foo>::Assoc` to `()` + = 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 diff --git a/src/test/ui/specialization/specialization-default-types.stderr b/src/test/ui/specialization/specialization-default-types.stderr index 257c114252cfc..7233387eba1fa 100644 --- a/src/test/ui/specialization/specialization-default-types.stderr +++ b/src/test/ui/specialization/specialization-default-types.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/specialization-default-types.rs:15:9 | +LL | default type Output = Box; + | ----------------------------- expected this associated type LL | default fn generate(self) -> Self::Output { | ------------ expected `::Output` because of return type LL | Box::new(self) @@ -8,8 +10,6 @@ LL | Box::new(self) | = note: expected associated type `::Output` found struct `std::boxed::Box` - = note: consider constraining the associated type `::Output` to `std::boxed::Box` or calling a method that returns `::Output` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/specialization-default-types.rs:25:5 @@ -21,7 +21,7 @@ LL | Example::generate(t) | = note: expected struct `std::boxed::Box` found associated type `::Output` - = note: consider constraining the associated type `::Output` to `std::boxed::Box` + = help: consider constraining the associated type `::Output` to `std::boxed::Box` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors diff --git a/src/test/ui/specialization/specialization-overlap-negative.rs b/src/test/ui/specialization/specialization-overlap-negative.rs index af80d6686e3a1..90dbef3075b7f 100644 --- a/src/test/ui/specialization/specialization-overlap-negative.rs +++ b/src/test/ui/specialization/specialization-overlap-negative.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![feature(specialization)] trait MyTrait {} @@ -6,6 +6,6 @@ trait MyTrait {} struct TestType(::std::marker::PhantomData); unsafe impl Send for TestType {} -impl !Send for TestType {} //~ ERROR E0119 +impl !Send for TestType {} //~ ERROR E0751 fn main() {} diff --git a/src/test/ui/specialization/specialization-overlap-negative.stderr b/src/test/ui/specialization/specialization-overlap-negative.stderr index 947aad824ea88..e2616534d2041 100644 --- a/src/test/ui/specialization/specialization-overlap-negative.stderr +++ b/src/test/ui/specialization/specialization-overlap-negative.stderr @@ -1,11 +1,11 @@ -error[E0119]: conflicting implementations of trait `std::marker::Send` for type `TestType<_>`: +error[E0751]: found both positive and negative implementation of trait `std::marker::Send` for type `TestType<_>`: --> $DIR/specialization-overlap-negative.rs:9:1 | LL | unsafe impl Send for TestType {} - | ------------------------------------------ first implementation here + | ------------------------------------------ positive implementation here LL | impl !Send for TestType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here error: aborting due to previous error -For more information about this error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0751`. diff --git a/src/test/ui/specialization/specialization-polarity.rs b/src/test/ui/specialization/specialization-polarity.rs index d574480674571..e78035f171075 100644 --- a/src/test/ui/specialization/specialization-polarity.rs +++ b/src/test/ui/specialization/specialization-polarity.rs @@ -1,16 +1,17 @@ // Make sure specialization cannot change impl polarity #![feature(optin_builtin_traits)] +#![feature(negative_impls)] #![feature(specialization)] auto trait Foo {} impl Foo for T {} -impl !Foo for u8 {} //~ ERROR E0119 +impl !Foo for u8 {} //~ ERROR E0751 auto trait Bar {} impl !Bar for T {} -impl Bar for u8 {} //~ ERROR E0119 +impl Bar for u8 {} //~ ERROR E0751 fn main() {} diff --git a/src/test/ui/specialization/specialization-polarity.stderr b/src/test/ui/specialization/specialization-polarity.stderr index bc1b2aeb70fc7..44e60cad67aa4 100644 --- a/src/test/ui/specialization/specialization-polarity.stderr +++ b/src/test/ui/specialization/specialization-polarity.stderr @@ -1,19 +1,19 @@ -error[E0119]: conflicting implementations of trait `Foo` for type `u8`: - --> $DIR/specialization-polarity.rs:9:1 +error[E0751]: found both positive and negative implementation of trait `Foo` for type `u8`: + --> $DIR/specialization-polarity.rs:10:1 | LL | impl Foo for T {} - | ----------------- first implementation here + | ----------------- positive implementation here LL | impl !Foo for u8 {} - | ^^^^^^^^^^^^^^^^ conflicting implementation for `u8` + | ^^^^^^^^^^^^^^^^ negative implementation here -error[E0119]: conflicting implementations of trait `Bar` for type `u8`: - --> $DIR/specialization-polarity.rs:14:1 +error[E0751]: found both positive and negative implementation of trait `Bar` for type `u8`: + --> $DIR/specialization-polarity.rs:15:1 | LL | impl !Bar for T {} - | ------------------ first implementation here + | ------------------ negative implementation here LL | impl Bar for u8 {} - | ^^^^^^^^^^^^^^^ conflicting implementation for `u8` + | ^^^^^^^^^^^^^^^ positive implementation here error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0751`. diff --git a/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.rs b/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.rs index 8f750ae62f5e4..38faa24691604 100644 --- a/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.rs +++ b/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.rs @@ -1,4 +1,4 @@ #![feature(staged_api)] -//~^ ERROR crate has missing stability attribute +//~^ ERROR module has missing stability attribute fn main() {} diff --git a/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.stderr b/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.stderr index b6c9564e904c4..c7ade234d3dcc 100644 --- a/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.stderr +++ b/src/test/ui/stability-attribute/missing-stability-attr-at-top-level.stderr @@ -1,4 +1,4 @@ -error: crate has missing stability attribute +error: module has missing stability attribute --> $DIR/missing-stability-attr-at-top-level.rs:1:1 | LL | / #![feature(staged_api)] diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index d0ca170503796..3c5da3f144035 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -108,4 +108,5 @@ LL | fn deprecated_without_unstable_or_stable() { } error: aborting due to 18 previous errors -For more information about this error, try `rustc --explain E0541`. +Some errors have detailed explanations: E0539, E0541. +For more information about an error, try `rustc --explain E0539`. diff --git a/src/test/ui/stability-in-private-module.rs b/src/test/ui/stability-in-private-module.rs index 1815897f17a60..f12e9198b0d7c 100644 --- a/src/test/ui/stability-in-private-module.rs +++ b/src/test/ui/stability-in-private-module.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - fn main() { let _ = std::thread::thread_info::current_thread(); //~^ERROR module `thread_info` is private diff --git a/src/test/ui/stability-in-private-module.stderr b/src/test/ui/stability-in-private-module.stderr index 35a09b2199591..8a7588c80d71e 100644 --- a/src/test/ui/stability-in-private-module.stderr +++ b/src/test/ui/stability-in-private-module.stderr @@ -1,5 +1,5 @@ error[E0603]: module `thread_info` is private - --> $DIR/stability-in-private-module.rs:7:26 + --> $DIR/stability-in-private-module.rs:2:26 | LL | let _ = std::thread::thread_info::current_thread(); | ^^^^^^^^^^^ private module diff --git a/src/test/ui/static/auxiliary/issue_24843.rs b/src/test/ui/static/auxiliary/issue_24843.rs new file mode 100644 index 0000000000000..6ca04f8606038 --- /dev/null +++ b/src/test/ui/static/auxiliary/issue_24843.rs @@ -0,0 +1 @@ +pub static TEST_STR: &'static str = "Hello world"; diff --git a/src/test/ui/static/issue-24843.rs b/src/test/ui/static/issue-24843.rs new file mode 100644 index 0000000000000..0b3397e210d70 --- /dev/null +++ b/src/test/ui/static/issue-24843.rs @@ -0,0 +1,8 @@ +// aux-build: issue_24843.rs +// check-pass + +extern crate issue_24843; + +static _TEST_STR_2: &'static str = &issue_24843::TEST_STR; + +fn main() {} diff --git a/src/test/ui/static/static-lifetime-bound.stderr b/src/test/ui/static/static-lifetime-bound.stderr index 90d728204e70b..79d9506619e17 100644 --- a/src/test/ui/static/static-lifetime-bound.stderr +++ b/src/test/ui/static/static-lifetime-bound.stderr @@ -17,6 +17,6 @@ LL | f(&x); LL | } | - `x` dropped here while still borrowed -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/static/static-mut-not-constant.stderr b/src/test/ui/static/static-mut-not-constant.stderr index 3560be0e29e43..a618b49d1089e 100644 --- a/src/test/ui/static/static-mut-not-constant.stderr +++ b/src/test/ui/static/static-mut-not-constant.stderr @@ -9,6 +9,8 @@ error[E0019]: static contains unimplemented expression type | LL | static mut a: Box = box 3; | ^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/src/test/ui/str/str-mut-idx.stderr b/src/test/ui/str/str-mut-idx.stderr index d0afb2ae7af74..2fd805e646991 100644 --- a/src/test/ui/str/str-mut-idx.stderr +++ b/src/test/ui/str/str-mut-idx.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t --> $DIR/str-mut-idx.rs:4:15 | LL | fn bot() -> T { loop {} } - | --- - required by this bound in `bot` + | - required by this bound in `bot` ... LL | s[1..2] = bot(); | ^^^ doesn't have a size known at compile-time diff --git a/src/test/run-fail/str-overrun.rs b/src/test/ui/str/str-overrun.rs similarity index 80% rename from src/test/run-fail/str-overrun.rs rename to src/test/ui/str/str-overrun.rs index e566308a08767..a3ec8941322d5 100644 --- a/src/test/run-fail/str-overrun.rs +++ b/src/test/ui/str/str-overrun.rs @@ -1,4 +1,7 @@ +// run-fail // error-pattern:index out of bounds: the len is 5 but the index is 5 +// ignore-emscripten no processes + fn main() { let s: String = "hello".to_string(); diff --git a/src/test/ui/struct-literal-variant-in-if.stderr b/src/test/ui/struct-literal-variant-in-if.stderr index d232a46f8ec29..4cd1169cc1bb8 100644 --- a/src/test/ui/struct-literal-variant-in-if.stderr +++ b/src/test/ui/struct-literal-variant-in-if.stderr @@ -46,9 +46,12 @@ error[E0423]: expected value, found struct variant `E::V` --> $DIR/struct-literal-variant-in-if.rs:10:13 | LL | if x == E::V { field } {} - | ^^^^---------- - | | - | help: surround the struct literal with parenthesis: `(E::V { field })` + | ^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::V { field }) {} + | ^ ^ error[E0308]: mismatched types --> $DIR/struct-literal-variant-in-if.rs:10:20 diff --git a/src/test/ui/structs-enums/empty-tag.rs b/src/test/ui/structs-enums/empty-tag.rs index 56a438200c00b..271ab72c74fc1 100644 --- a/src/test/ui/structs-enums/empty-tag.rs +++ b/src/test/ui/structs-enums/empty-tag.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(non_camel_case_types)] #[derive(Copy, Clone, Debug)] diff --git a/src/test/ui/structs-enums/rec-align-u64.rs b/src/test/ui/structs-enums/rec-align-u64.rs index 680a690ba34e3..b06e204d31bf0 100644 --- a/src/test/ui/structs-enums/rec-align-u64.rs +++ b/src/test/ui/structs-enums/rec-align-u64.rs @@ -67,13 +67,6 @@ mod m { #[cfg(target_os = "windows")] mod m { - #[cfg(target_arch = "x86")] - pub mod m { - pub fn align() -> usize { 8 } - pub fn size() -> usize { 16 } - } - - #[cfg(target_arch = "x86_64")] pub mod m { pub fn align() -> usize { 8 } pub fn size() -> usize { 16 } diff --git a/src/test/run-fail/rhs-type.rs b/src/test/ui/structs/rhs-type.rs similarity index 85% rename from src/test/run-fail/rhs-type.rs rename to src/test/ui/structs/rhs-type.rs index 6efbeadd70421..c48e7c08ed2da 100644 --- a/src/test/run-fail/rhs-type.rs +++ b/src/test/ui/structs/rhs-type.rs @@ -1,6 +1,9 @@ // Tests that codegen treats the rhs of pth's decl // as a _|_-typed thing, not a str-typed thing + +// run-fail // error-pattern:bye +// ignore-emscripten no processes #![allow(unreachable_code)] #![allow(unused_variables)] diff --git a/src/test/ui/substs-ppaux.normal.stderr b/src/test/ui/substs-ppaux.normal.stderr index 3ad2a1414f969..bcdeed262ecba 100644 --- a/src/test/ui/substs-ppaux.normal.stderr +++ b/src/test/ui/substs-ppaux.normal.stderr @@ -74,7 +74,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t --> $DIR/substs-ppaux.rs:49:5 | LL | fn bar<'a, T>() where T: 'a {} - | --- -- required by this bound in `Foo::bar` + | -- required by this bound in `Foo::bar` ... LL | >::bar; | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/substs-ppaux.verbose.stderr b/src/test/ui/substs-ppaux.verbose.stderr index e23f06a3ef590..fb5e6fbcfe712 100644 --- a/src/test/ui/substs-ppaux.verbose.stderr +++ b/src/test/ui/substs-ppaux.verbose.stderr @@ -74,7 +74,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t --> $DIR/substs-ppaux.rs:49:5 | LL | fn bar<'a, T>() where T: 'a {} - | --- -- required by this bound in `Foo::bar` + | -- required by this bound in `Foo::bar` ... LL | >::bar; | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index 638d504d7feab..99ba4e2a646e5 100644 --- a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -5,7 +5,7 @@ LL | async fn foo() {} | --- consider calling this function LL | LL | fn bar(f: impl Future) {} - | --- ----------------- required by this bound in `bar` + | ----------------- required by this bound in `bar` ... LL | bar(foo); | ^^^ the trait `std::future::Future` is not implemented for `fn() -> impl std::future::Future {foo}` @@ -19,7 +19,7 @@ error[E0277]: the trait bound `[closure@$DIR/async-fn-ctor-passed-as-arg-where-i --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:9 | LL | fn bar(f: impl Future) {} - | --- ----------------- required by this bound in `bar` + | ----------------- required by this bound in `bar` ... LL | let async_closure = async || (); | -------- consider calling this closure diff --git a/src/test/ui/suggestions/attribute-typos.rs b/src/test/ui/suggestions/attribute-typos.rs index e1e3317093a31..7c8231bbb24f8 100644 --- a/src/test/ui/suggestions/attribute-typos.rs +++ b/src/test/ui/suggestions/attribute-typos.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl #[deprcated] //~ ERROR cannot find attribute `deprcated` in this scope fn foo() {} diff --git a/src/test/ui/suggestions/attribute-typos.stderr b/src/test/ui/suggestions/attribute-typos.stderr index c7c257ba5fe53..152700a07980a 100644 --- a/src/test/ui/suggestions/attribute-typos.stderr +++ b/src/test/ui/suggestions/attribute-typos.stderr @@ -1,19 +1,17 @@ -error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler - --> $DIR/attribute-typos.rs:11:3 +error: attributes starting with `rustc` are reserved for use by the `rustc` compiler + --> $DIR/attribute-typos.rs:7:3 | LL | #[rustc_err] | ^^^^^^^^^ - | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: cannot find attribute `rustc_err` in this scope - --> $DIR/attribute-typos.rs:11:3 + --> $DIR/attribute-typos.rs:7:3 | LL | #[rustc_err] | ^^^^^^^^^ help: a built-in attribute with a similar name exists: `rustc_error` error: cannot find attribute `tests` in this scope - --> $DIR/attribute-typos.rs:8:3 + --> $DIR/attribute-typos.rs:4:3 | LL | #[tests] | ^^^^^ help: an attribute macro with a similar name exists: `test` @@ -24,11 +22,10 @@ LL | pub macro test($item:item) { | -------------------------- similarly named attribute macro `test` defined here error: cannot find attribute `deprcated` in this scope - --> $DIR/attribute-typos.rs:5:3 + --> $DIR/attribute-typos.rs:1:3 | LL | #[deprcated] | ^^^^^^^^^ help: a built-in attribute with a similar name exists: `deprecated` error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/suggestions/const-no-type.rs b/src/test/ui/suggestions/const-no-type.rs index 6b79697e9839e..b931a04c2860c 100644 --- a/src/test/ui/suggestions/const-no-type.rs +++ b/src/test/ui/suggestions/const-no-type.rs @@ -35,6 +35,11 @@ const C = 42; //~| HELP provide a type for the item //~| SUGGESTION C: i32 +const D = &&42; +//~^ ERROR missing type for `const` item +//~| HELP provide a type for the item +//~| SUGGESTION D: &&i32 + static S = Vec::::new(); //~^ ERROR missing type for `static` item //~| HELP provide a type for the item diff --git a/src/test/ui/suggestions/const-no-type.stderr b/src/test/ui/suggestions/const-no-type.stderr index a7b5aa5e5b124..874c1bac10bd5 100644 --- a/src/test/ui/suggestions/const-no-type.stderr +++ b/src/test/ui/suggestions/const-no-type.stderr @@ -4,14 +4,20 @@ error: missing type for `const` item LL | const C = 42; | ^ help: provide a type for the item: `C: i32` +error: missing type for `const` item + --> $DIR/const-no-type.rs:38:7 + | +LL | const D = &&42; + | ^ help: provide a type for the item: `D: &&i32` + error: missing type for `static` item - --> $DIR/const-no-type.rs:38:8 + --> $DIR/const-no-type.rs:43:8 | LL | static S = Vec::::new(); | ^ help: provide a type for the item: `S: std::vec::Vec` error: missing type for `static mut` item - --> $DIR/const-no-type.rs:43:12 + --> $DIR/const-no-type.rs:48:12 | LL | static mut SM = "abc"; | ^^ help: provide a type for the item: `SM: &str` @@ -34,5 +40,5 @@ error: missing type for `static mut` item LL | static mut SM2 = "abc"; | ^^^ help: provide a type for the item: `SM2: ` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr index fc17199bf91d4..1512eac76670d 100644 --- a/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr +++ b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr @@ -9,6 +9,8 @@ LL | let A = 3; ... LL | const A: i32 = 2; | ----------------- constant defined here + | + = note: the matched value is of type `i32` error: aborting due to previous error diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs index 0a1686eac9d34..5dee0f5dae0b0 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs @@ -11,19 +11,26 @@ fn foo + Send + 'static>(x: F) -> BoxFuture<'static, i32> x //~ ERROR mismatched types } -// FIXME: uncomment these once this commit is in Beta and we can rely on `rustc_on_unimplemented` -// having filtering for `Self` being a trait. -// -// fn bar + Send + 'static>(x: F) -> BoxFuture<'static, i32> { -// Box::new(x) -// } -// -// fn baz + Send + 'static>(x: F) -> BoxFuture<'static, i32> { -// Pin::new(x) -// } -// -// fn qux + Send + 'static>(x: F) -> BoxFuture<'static, i32> { -// Pin::new(Box::new(x)) -// } +// This case is still subpar: +// `Pin::new(x)`: store this in the heap by calling `Box::new`: `Box::new(x)` +// Should suggest changing the code from `Pin::new` to `Box::pin`. +fn bar + Send + 'static>(x: F) -> BoxFuture<'static, i32> { + Box::new(x) //~ ERROR mismatched types +} + +fn baz + Send + 'static>(x: F) -> BoxFuture<'static, i32> { + Pin::new(x) //~ ERROR mismatched types + //~^ ERROR E0277 +} + +fn qux + Send + 'static>(x: F) -> BoxFuture<'static, i32> { + Pin::new(Box::new(x)) //~ ERROR E0277 +} + +fn zap() -> BoxFuture<'static, i32> { + async { //~ ERROR mismatched types + 42 + } +} fn main() {} diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 48d941283b62d..52e13dbc2dd85 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -12,9 +12,77 @@ LL | x | = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` found type parameter `F` - = 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 previous error +error[E0308]: mismatched types + --> $DIR/expected-boxed-future-isnt-pinned.rs:18:5 + | +LL | fn bar + Send + 'static>(x: F) -> BoxFuture<'static, i32> { + | ----------------------- expected `std::pin::Pin + std::marker::Send + 'static)>>` because of return type +LL | Box::new(x) + | ^^^^^^^^^^^ expected struct `std::pin::Pin`, found struct `std::boxed::Box` + | + = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` + found struct `std::boxed::Box` + = help: use `Box::pin` + +error[E0308]: mismatched types + --> $DIR/expected-boxed-future-isnt-pinned.rs:22:14 + | +LL | fn baz + Send + 'static>(x: F) -> BoxFuture<'static, i32> { + | - this type parameter +LL | Pin::new(x) + | ^ + | | + | expected struct `std::boxed::Box`, found type parameter `F` + | help: store this in the heap by calling `Box::new`: `Box::new(x)` + | + = note: expected struct `std::boxed::Box + std::marker::Send>` + found type parameter `F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html + +error[E0277]: `dyn std::future::Future + std::marker::Send` cannot be unpinned + --> $DIR/expected-boxed-future-isnt-pinned.rs:22:5 + | +LL | Pin::new(x) + | ^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn std::future::Future + std::marker::Send` + | + = note: consider using `Box::pin` + = note: required by `std::pin::Pin::

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

::new` + +error[E0308]: mismatched types + --> $DIR/expected-boxed-future-isnt-pinned.rs:31:5 + | +LL | fn zap() -> BoxFuture<'static, i32> { + | ----------------------- expected `std::pin::Pin + std::marker::Send + 'static)>>` because of return type +LL | / async { +LL | | 42 +LL | | } + | |_____^ expected struct `std::pin::Pin`, found opaque type + | + ::: $SRC_DIR/libcore/future/mod.rs:LL:COL + | +LL | pub const fn from_generator(gen: T) -> impl Future + | ------------------------------- the found opaque type + | + = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` + found opaque type `impl std::future::Future` +help: you need to pin and box this expression + | +LL | Box::pin(async { +LL | 42 +LL | }) + | + +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index ed4a0b8487dff..8589a2757e91d 100644 --- a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -5,7 +5,7 @@ LL | fn foo() -> impl T { S } | --- consider calling this function LL | LL | fn bar(f: impl T) {} - | --- ------- required by this bound in `bar` + | ------- required by this bound in `bar` ... LL | bar(foo); | ^^^ the trait `T` is not implemented for `fn() -> impl T {foo}` @@ -19,7 +19,7 @@ error[E0277]: the trait bound `[closure@$DIR/fn-ctor-passed-as-arg-where-it-shou --> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:19:9 | LL | fn bar(f: impl T) {} - | --- ------- required by this bound in `bar` + | ------- required by this bound in `bar` ... LL | let closure = || S; | -- consider calling this closure diff --git a/src/test/ui/suggestions/fn-needing-specified-return-type-param.rs b/src/test/ui/suggestions/fn-needing-specified-return-type-param.rs new file mode 100644 index 0000000000000..2f140f823afb9 --- /dev/null +++ b/src/test/ui/suggestions/fn-needing-specified-return-type-param.rs @@ -0,0 +1,5 @@ +fn f() -> A { unimplemented!() } +fn foo() { + let _ = f; //~ ERROR type annotations needed for `fn() -> A` +} +fn main() {} diff --git a/src/test/ui/suggestions/fn-needing-specified-return-type-param.stderr b/src/test/ui/suggestions/fn-needing-specified-return-type-param.stderr new file mode 100644 index 0000000000000..b59a3818e7042 --- /dev/null +++ b/src/test/ui/suggestions/fn-needing-specified-return-type-param.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed for `fn() -> A` + --> $DIR/fn-needing-specified-return-type-param.rs:3:13 + | +LL | let _ = f; + | - ^ cannot infer type for type parameter `A` declared on the function `f` + | | + | consider giving this pattern the explicit type `fn() -> A`, where the type parameter `A` is specified + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.rs b/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.rs new file mode 100644 index 0000000000000..319789c4ec282 --- /dev/null +++ b/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.rs @@ -0,0 +1,18 @@ +// Regression test for #70813 (this used to trigger a debug assertion) + +trait Trait {} + +struct S; + +impl<'a> Trait for &'a mut S {} + +fn foo(_: X) +where + for<'b> &'b X: Trait, +{ +} + +fn main() { + let s = S; + foo::(s); //~ ERROR the trait bound `for<'b> &'b S: Trait` is not satisfied +} diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.stderr b/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.stderr new file mode 100644 index 0000000000000..83de3c4cfe0a5 --- /dev/null +++ b/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `for<'b> &'b S: Trait` is not satisfied + --> $DIR/imm-ref-trait-object-literal-bound-regions.rs:17:5 + | +LL | fn foo(_: X) + | --- required by a bound in this +LL | where +LL | for<'b> &'b X: Trait, + | ----- required by this bound in `foo` +... +LL | foo::(s); + | ^^^^^^^^ the trait `for<'b> Trait` is not implemented for `&'b S` + | + = help: the following implementations were found: + <&'a mut S as Trait> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr index 84ba935191b48..df483b3912d69 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `&S: Trait` is not satisfied --> $DIR/imm-ref-trait-object-literal.rs:12:7 | LL | fn foo(_: X) {} - | --- ----- required by this bound in `foo` + | ----- required by this bound in `foo` ... LL | foo(&s); | ^^ the trait `Trait` is not implemented for `&S` @@ -18,7 +18,7 @@ error[E0277]: the trait bound `S: Trait` is not satisfied --> $DIR/imm-ref-trait-object-literal.rs:13:7 | LL | fn foo(_: X) {} - | --- ----- required by this bound in `foo` + | ----- required by this bound in `foo` ... LL | foo(s); | ^ the trait `Trait` is not implemented for `S` diff --git a/src/test/ui/suggestions/imm-ref-trait-object.rs b/src/test/ui/suggestions/imm-ref-trait-object.rs index 241dde9fec1d6..288d6c699f59a 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object.rs +++ b/src/test/ui/suggestions/imm-ref-trait-object.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - fn test(t: &dyn Iterator) -> u64 { t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object } diff --git a/src/test/ui/suggestions/imm-ref-trait-object.stderr b/src/test/ui/suggestions/imm-ref-trait-object.stderr index c5fe6ddb8a9bf..37c2053522961 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object.stderr @@ -1,5 +1,5 @@ error: the `min` method cannot be invoked on a trait object - --> $DIR/imm-ref-trait-object.rs:7:8 + --> $DIR/imm-ref-trait-object.rs:2:8 | LL | t.min().unwrap() | ^^^ diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs new file mode 100644 index 0000000000000..6e9e8821cfea7 --- /dev/null +++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs @@ -0,0 +1,44 @@ +// The double space in `impl Iterator` is load bearing! We want to make sure we don't regress by +// accident if the internal string representation changes. +#[rustfmt::skip] +fn foo(constraints: impl Iterator) { + for constraint in constraints { + qux(constraint); +//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` + } +} + +fn bar(t: T, constraints: impl Iterator) where T: std::fmt::Debug { + for constraint in constraints { + qux(t); + qux(constraint); +//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` + } +} + +fn baz(t: impl std::fmt::Debug, constraints: impl Iterator) { + for constraint in constraints { + qux(t); + qux(constraint); +//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` + } +} + +fn bat(t: T, constraints: impl Iterator, _: I) { + for constraint in constraints { + qux(t); + qux(constraint); +//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` + } +} + +fn bak(constraints: impl Iterator + std::fmt::Debug) { + for constraint in constraints { + qux(constraint); +//~^ ERROR `::Item` doesn't implement + } +} + +fn qux(_: impl std::fmt::Debug) {} + +fn main() {} diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr new file mode 100644 index 0000000000000..fb0914a8743e8 --- /dev/null +++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr @@ -0,0 +1,78 @@ +error[E0277]: `::Item` doesn't implement `std::fmt::Debug` + --> $DIR/impl-trait-with-missing-bounds.rs:6:13 + | +LL | qux(constraint); + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` +... +LL | fn qux(_: impl std::fmt::Debug) {} + | --------------- required by this bound in `qux` + | + = help: the trait `std::fmt::Debug` is not implemented for `::Item` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | fn foo(constraints: I) where ::Item: std::fmt::Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `::Item` doesn't implement `std::fmt::Debug` + --> $DIR/impl-trait-with-missing-bounds.rs:14:13 + | +LL | qux(constraint); + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` +... +LL | fn qux(_: impl std::fmt::Debug) {} + | --------------- required by this bound in `qux` + | + = help: the trait `std::fmt::Debug` is not implemented for `::Item` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | fn bar(t: T, constraints: I) where T: std::fmt::Debug, ::Item: std::fmt::Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `::Item` doesn't implement `std::fmt::Debug` + --> $DIR/impl-trait-with-missing-bounds.rs:22:13 + | +LL | qux(constraint); + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` +... +LL | fn qux(_: impl std::fmt::Debug) {} + | --------------- required by this bound in `qux` + | + = help: the trait `std::fmt::Debug` is not implemented for `::Item` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | fn baz(t: impl std::fmt::Debug, constraints: I) where ::Item: std::fmt::Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `::Item` doesn't implement `std::fmt::Debug` + --> $DIR/impl-trait-with-missing-bounds.rs:30:13 + | +LL | qux(constraint); + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` +... +LL | fn qux(_: impl std::fmt::Debug) {} + | --------------- required by this bound in `qux` + | + = help: the trait `std::fmt::Debug` is not implemented for `::Item` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | fn bat(t: T, constraints: U, _: I) where ::Item: std::fmt::Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `::Item` doesn't implement `std::fmt::Debug` + --> $DIR/impl-trait-with-missing-bounds.rs:37:13 + | +LL | qux(constraint); + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` +... +LL | fn qux(_: impl std::fmt::Debug) {} + | --------------- required by this bound in `qux` + | + = help: the trait `std::fmt::Debug` is not implemented for `::Item` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | fn bak(constraints: I) where ::Item: std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/into-str.stderr b/src/test/ui/suggestions/into-str.stderr index a1e1f4d13572a..7414a7cc24c92 100644 --- a/src/test/ui/suggestions/into-str.stderr +++ b/src/test/ui/suggestions/into-str.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `&str: std::convert::From` is --> $DIR/into-str.rs:4:5 | LL | fn foo<'a, T>(_t: T) where T: Into<&'a str> {} - | --- ------------- required by this bound in `foo` + | ------------- required by this bound in `foo` ... LL | foo(String::new()); | ^^^ the trait `std::convert::From` is not implemented for `&str` diff --git a/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr b/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr index 31d974ed43d99..a8ea214796183 100644 --- a/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr +++ b/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr @@ -2,40 +2,53 @@ error[E0277]: the trait bound `::Assoc: Child` is not satisfied --> $DIR/missing-assoc-type-bound-restriction.rs:17:19 | LL | trait Parent { - | ------------ required by `Parent` + | ------ required by a bound in this +LL | type Ty; +LL | type Assoc: Child; + | --------------- required by this bound in `Parent` ... LL | impl> Parent for ParentWrapper { - | ^^^^^^ - help: consider further restricting the associated type: `where ::Assoc: Child` - | | - | the trait `Child` is not implemented for `::Assoc` + | ^^^^^^ the trait `Child` is not implemented for `::Assoc` + | +help: consider further restricting the associated type + | +LL | impl> Parent for ParentWrapper where ::Assoc: Child { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `::Assoc: Child` is not satisfied - --> $DIR/missing-assoc-type-bound-restriction.rs:20:5 + --> $DIR/missing-assoc-type-bound-restriction.rs:20:18 | +LL | trait Parent { + | ------ required by a bound in this +LL | type Ty; LL | type Assoc: Child; - | ----- associated type defined here -... -LL | impl> Parent for ParentWrapper { - | ------------------------------------------------------- help: consider further restricting the associated type: `where ::Assoc: Child` - | | - | in this `impl` item + | --------------- required by this bound in `Parent` ... LL | type Assoc = ChildWrapper; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Child` is not implemented for `::Assoc` + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Child` is not implemented for `::Assoc` | = note: required because of the requirements on the impl of `Child` for `ChildWrapper<::Assoc>` +help: consider further restricting the associated type + | +LL | impl> Parent for ParentWrapper where ::Assoc: Child { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `::Assoc: Child` is not satisfied --> $DIR/missing-assoc-type-bound-restriction.rs:20:5 | LL | trait Parent { - | ------------ required by `Parent` -... -LL | impl> Parent for ParentWrapper { - | - help: consider further restricting the associated type: `where ::Assoc: Child` + | ------ required by a bound in this +LL | type Ty; +LL | type Assoc: Child; + | --------------- required by this bound in `Parent` ... LL | type Assoc = ChildWrapper; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Child` is not implemented for `::Assoc` + | +help: consider further restricting the associated type + | +LL | impl> Parent for ParentWrapper where ::Assoc: Child { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/suggestions/missing-lifetime-specifier.rs b/src/test/ui/suggestions/missing-lifetime-specifier.rs new file mode 100644 index 0000000000000..b09c1879d7015 --- /dev/null +++ b/src/test/ui/suggestions/missing-lifetime-specifier.rs @@ -0,0 +1,65 @@ +#![allow(bare_trait_objects)] +use std::collections::HashMap; +use std::cell::RefCell; + +pub union Foo<'t, 'k> { + i: &'t i64, + f: &'k f64, +} +trait Bar<'t, 'k> {} + +pub union Qux<'t, 'k, I> { + i: &'t I, + f: &'k I, +} +trait Tar<'t, 'k, I> {} + +thread_local! { + static a: RefCell>>> = RefCell::new(HashMap::new()); + //~^ ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier +} +thread_local! { + static b: RefCell>>> = RefCell::new(HashMap::new()); + //~^ ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier + //~| ERROR the lifetime bound for this object type cannot be deduced from context + //~| ERROR the lifetime bound for this object type cannot be deduced from context +} +thread_local! { + static c: RefCell>>>> = RefCell::new(HashMap::new()); + //~^ ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier +} +thread_local! { + static d: RefCell>>>> = RefCell::new(HashMap::new()); + //~^ ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier + //~| ERROR the lifetime bound for this object type cannot be deduced from context + //~| ERROR the lifetime bound for this object type cannot be deduced from context +} + +thread_local! { + static e: RefCell>>>> = RefCell::new(HashMap::new()); + //~^ ERROR wrong number of lifetime arguments: expected 2, found 1 + //~| ERROR wrong number of lifetime arguments: expected 2, found 1 + //~| ERROR wrong number of lifetime arguments: expected 2, found 1 + //~| ERROR wrong number of lifetime arguments: expected 2, found 1 +} +thread_local! { + static f: RefCell>>>> = RefCell::new(HashMap::new()); + //~^ ERROR the lifetime bound for this object type cannot be deduced from context + //~| ERROR the lifetime bound for this object type cannot be deduced from context + //~| ERROR wrong number of lifetime arguments: expected 2, found 1 + //~| ERROR wrong number of lifetime arguments: expected 2, found 1 + //~| ERROR wrong number of lifetime arguments: expected 2, found 1 + //~| ERROR wrong number of lifetime arguments: expected 2, found 1 + //~| ERROR missing lifetime specifier + //~| ERROR missing lifetime specifier +} + +fn main() {} diff --git a/src/test/ui/suggestions/missing-lifetime-specifier.stderr b/src/test/ui/suggestions/missing-lifetime-specifier.stderr new file mode 100644 index 0000000000000..2630cf1affae6 --- /dev/null +++ b/src/test/ui/suggestions/missing-lifetime-specifier.stderr @@ -0,0 +1,256 @@ +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:18:44 + | +LL | static a: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^ expected 2 lifetime parameters + | + = 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 + | +LL | static a: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:18:44 + | +LL | static a: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^ expected 2 lifetime parameters + | + = 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 + | +LL | static a: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:23:44 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^ 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 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:23:45 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^ expected 2 lifetime parameters + | + = 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 + | +LL | static b: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:23:44 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^ 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 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:23:45 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^ expected 2 lifetime parameters + | + = 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 + | +LL | static b: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:32:48 + | +LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ expected 2 lifetime parameters + | + = 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 + | +LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:32:48 + | +LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ expected 2 lifetime parameters + | + = 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 + | +LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:37:44 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ 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 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:37:49 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ expected 2 lifetime parameters + | + = 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 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:37:44 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ 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 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-specifier.rs:37:49 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ expected 2 lifetime parameters + | + = 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 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:54:44 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ 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 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:54:44 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^ 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 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/missing-lifetime-specifier.rs:23:45 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^ + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/missing-lifetime-specifier.rs:23:45 + | +LL | static b: RefCell>>> = RefCell::new(HashMap::new()); + | ^^^ + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/missing-lifetime-specifier.rs:37:45 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/missing-lifetime-specifier.rs:37:45 + | +LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:47:44 + | +LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:47:44 + | +LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:47:44 + | +LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:47:44 + | +LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:54:45 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:54:45 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/missing-lifetime-specifier.rs:54:45 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:54:45 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/missing-lifetime-specifier.rs:54:45 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ + +error[E0107]: wrong number of lifetime arguments: expected 2, found 1 + --> $DIR/missing-lifetime-specifier.rs:54:45 + | +LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments + +error: aborting due to 28 previous errors + +Some errors have detailed explanations: E0106, E0107, E0228. +For more information about an error, try `rustc --explain E0106`. diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs b/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs index 49a37498fd955..f8b86377187c5 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::env::args; use std::fs::File; use std::io::{stdout, Write, BufWriter}; diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr index e4234cfcd5112..9ccddda45e2bb 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:21:29 + --> $DIR/mut-borrow-needed-by-trait.rs:17:29 | LL | let fp = BufWriter::new(fp); | ^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` @@ -8,25 +8,33 @@ LL | let fp = BufWriter::new(fp); = note: required by `std::io::BufWriter::::new` error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:21:14 + --> $DIR/mut-borrow-needed-by-trait.rs:17:14 | LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` + | + ::: $SRC_DIR/libstd/io/buffered.rs:LL:COL + | +LL | pub struct BufWriter { + | ----- required by this bound in `std::io::BufWriter` | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` - = note: required by `std::io::BufWriter` error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:21:14 + --> $DIR/mut-borrow-needed-by-trait.rs:17:14 | LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` + | + ::: $SRC_DIR/libstd/io/buffered.rs:LL:COL + | +LL | pub struct BufWriter { + | ----- required by this bound in `std::io::BufWriter` | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` - = note: required by `std::io::BufWriter` error[E0599]: no method named `write_fmt` found for struct `std::io::BufWriter<&dyn std::io::Write>` in the current scope - --> $DIR/mut-borrow-needed-by-trait.rs:26:5 + --> $DIR/mut-borrow-needed-by-trait.rs:22:5 | LL | writeln!(fp, "hello world").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `std::io::BufWriter<&dyn std::io::Write>` diff --git a/src/test/ui/suggestions/no-extern-crate-in-type.stderr b/src/test/ui/suggestions/no-extern-crate-in-type.stderr index 0a73a269134ef..876eef2b6249a 100644 --- a/src/test/ui/suggestions/no-extern-crate-in-type.stderr +++ b/src/test/ui/suggestions/no-extern-crate-in-type.stderr @@ -4,14 +4,10 @@ error[E0412]: cannot find type `Foo` in this scope LL | type Output = Option; | ^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this struct | LL | use foo::Foo; | -help: you might be missing a type parameter - | -LL | type Output = Option; - | ^^^^^ error: aborting due to previous error diff --git a/src/test/ui/suggestions/raw-name-use-suggestion.stderr b/src/test/ui/suggestions/raw-name-use-suggestion.stderr index 62b76318e09b5..7447cf87ce168 100644 --- a/src/test/ui/suggestions/raw-name-use-suggestion.stderr +++ b/src/test/ui/suggestions/raw-name-use-suggestion.stderr @@ -26,7 +26,7 @@ error[E0425]: cannot find function `r#break` in this scope LL | r#break(); | ^^^^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this function | LL | use foo::r#break; | diff --git a/src/test/ui/suggestions/restrict-type-argument.stderr b/src/test/ui/suggestions/restrict-type-argument.stderr index 4d5cb8907e887..0c52778b0d886 100644 --- a/src/test/ui/suggestions/restrict-type-argument.stderr +++ b/src/test/ui/suggestions/restrict-type-argument.stderr @@ -2,97 +2,91 @@ error[E0277]: `impl Sync` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:4:13 | LL | fn is_send(val: T) {} - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(val); | ^^^ `impl Sync` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `impl Sync` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/restrict-type-argument.rs:3:23 +help: consider further restricting this bound | -LL | fn use_impl_sync(val: impl Sync) { - | ^^^^^^^^^ +LL | fn use_impl_sync(val: impl Sync + std::marker::Send) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:8:13 | LL | fn is_send(val: T) {} - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(val); | ^^^ `S` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `S` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/restrict-type-argument.rs:7:31 +help: consider further restricting this bound | -LL | fn use_where(val: S) where S: Sync { - | ^^^^^^^ +LL | fn use_where(val: S) where S: Sync + std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:12:13 | LL | fn is_send(val: T) {} - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(val); | ^^^ `S` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `S` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/restrict-type-argument.rs:11:17 +help: consider further restricting this bound | -LL | fn use_bound(val: S) { - | ^^^^ +LL | fn use_bound(val: S) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:20:13 | LL | fn is_send(val: T) {} - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(val); | ^^^ `S` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `S` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/restrict-type-argument.rs:18:5 +help: consider further restricting this bound | -LL | Sync - | ^^^^ +LL | Sync + std::marker::Send + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:24:13 | LL | fn is_send(val: T) {} - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(val); | ^^^ `S` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `S` -help: consider further restricting this bound with `+ std::marker::Send` - --> $DIR/restrict-type-argument.rs:23:47 +help: consider further restricting this bound | -LL | fn use_bound_and_where(val: S) where S: std::fmt::Debug { - | ^^^^^^^^^^^^^^^^^^ +LL | fn use_bound_and_where(val: S) where S: std::fmt::Debug + std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:28:13 | LL | fn is_send(val: T) {} - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send(val); | ^^^ `S` cannot be sent between threads safely | = help: the trait `std::marker::Send` is not implemented for `S` -help: consider restricting this type parameter with `S: std::marker::Send` - --> $DIR/restrict-type-argument.rs:27:16 +help: consider restricting type parameter `S` | -LL | fn use_unbound(val: S) { - | ^ +LL | fn use_unbound(val: S) { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/suggestions/return-without-lifetime.stderr b/src/test/ui/suggestions/return-without-lifetime.stderr index ce3b1748da435..2a237d61f50fe 100644 --- a/src/test/ui/suggestions/return-without-lifetime.stderr +++ b/src/test/ui/suggestions/return-without-lifetime.stderr @@ -2,23 +2,36 @@ error[E0106]: missing lifetime specifier --> $DIR/return-without-lifetime.rs:2:16 | LL | struct Foo<'a>(&usize); - | ^ help: consider using the named lifetime: `&'a` + | ^ expected named lifetime parameter + | +help: consider using the `'a` lifetime + | +LL | struct Foo<'a>(&'a usize); + | ^^^ error[E0106]: missing lifetime specifier --> $DIR/return-without-lifetime.rs:5:34 | LL | fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() } - | --------- ^ help: consider using the named lifetime: `&'a` + | --------- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from +help: consider using the `'a` lifetime + | +LL | fn func1<'a>(_arg: &'a Thing) -> &'a () { unimplemented!() } + | ^^^ error[E0106]: missing lifetime specifier --> $DIR/return-without-lifetime.rs:7:35 | LL | fn func2<'a>(_arg: &Thing<'a>) -> &() { unimplemented!() } - | ---------- ^ help: consider using the named lifetime: `&'a` + | ---------- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from +help: consider using the `'a` lifetime + | +LL | fn func2<'a>(_arg: &Thing<'a>) -> &'a () { unimplemented!() } + | ^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish.stderr b/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish.stderr index dfc1887d3af90..f1c0cd6b543e4 100644 --- a/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish.stderr +++ b/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish.stderr @@ -11,7 +11,7 @@ LL | x.default_hello(); | help: use associated function syntax instead: `GenericAssocMethod::::default_hello` | = note: found the following associated functions; to be used as methods, functions must have a `self` parameter -note: the candidate is defined in an impl for the type `GenericAssocMethod<_>` +note: the candidate is defined in an impl for the type `GenericAssocMethod` --> $DIR/suggest-assoc-fn-call-with-turbofish.rs:4:5 | LL | fn default_hello() {} diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs index 6505a97de6e4b..27930626a6209 100644 --- a/src/test/ui/suggestions/suggest-move-types.rs +++ b/src/test/ui/suggestions/suggest-move-types.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![allow(warnings)] // This test verifies that the suggestion to move types before associated type bindings @@ -25,20 +23,22 @@ trait ThreeWithLifetime<'a, 'b, 'c, T, U, V> { type C; } -struct A> { //~ ERROR associated type bindings must be declared after generic parameters +struct A> { +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, } struct Al<'a, T, M: OneWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, } -struct B> { //~ ERROR associated type bindings must be declared after generic parameters +struct B> { +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, u: U, @@ -46,7 +46,7 @@ struct B> { //~ ERROR associated ty } struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, @@ -54,7 +54,8 @@ struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { //~ ERROR associated type bindings must be declared after generic parameters +struct C> { +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, u: U, @@ -62,7 +63,7 @@ struct C> { //~ ERROR associated ty } struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, @@ -70,7 +71,8 @@ struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { //~ ERROR associated type bindings must be declared after generic parameters +struct D> { +//~^ ERROR generic arguments must come before the first constraint m: M, t: T, u: U, @@ -78,7 +80,7 @@ struct D> { //~ ERROR associated ty } struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { -//~^ ERROR associated type bindings must be declared after generic parameters +//~^ ERROR generic arguments must come before the first constraint //~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr index ac91813f92839..3bb6fd6e4f423 100644 --- a/src/test/ui/suggestions/suggest-move-types.stderr +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -1,81 +1,109 @@ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:28:20 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:26:26 | LL | struct A> { - | ----^^^ + | ---- ^ generic argument | | - | this associated type binding should be moved after the generic parameters + | constraint + | +help: move the constraint after the generic argument + | +LL | struct A> { + | ^^^^^^^^^^^ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:34:37 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:33:43 | LL | struct Al<'a, T, M: OneWithLifetime> { - | ----^^^^^^^ + | ---- ^ ^^ generic arguments | | - | this associated type binding should be moved after the generic parameters + | constraint + | +help: move the constraint after the generic arguments + | +LL | struct Al<'a, T, M: OneWithLifetime<'a, T, A = ()>> { + | ^^^^^^^^^^^^^^^ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:41:28 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:40:46 | LL | struct B> { - | ----^^----^^----^^^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ---- ---- ---- ^ ^ ^ generic arguments + | | + | constraints + | +help: move the constraints after the generic arguments + | +LL | struct B> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:48:53 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:48:71 | LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ----^^----^^----^^^^^^^^^^^^^^^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ---- ---- ---- ^ ^ ^ ^^ ^^ ^^ generic arguments + | | + | constraints + | +help: move the constraints after the generic arguments + | +LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: associated type bindings must be declared after generic parameters +error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:57:28 | LL | struct C> { - | ^^^----^^----^^----^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^ ---- ---- ---- ^ ^ generic arguments + | | + | constraints + | +help: move the constraints after the generic arguments + | +LL | struct C> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:64:53 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:65:53 | LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^^^^^^----^^----^^----^^^^^^^^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^ ^^ ---- ---- ---- ^ ^^ ^ ^^ generic arguments + | | + | constraints + | +help: move the constraints after the generic arguments + | +LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:73:28 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:74:28 | LL | struct D> { - | ^^^----^^----^^^^^----^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^ ---- ---- ^ ---- ^ generic arguments + | | + | constraints + | +help: move the constraints after the generic arguments + | +LL | struct D> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: associated type bindings must be declared after generic parameters - --> $DIR/suggest-move-types.rs:80:53 +error: generic arguments must come before the first constraint + --> $DIR/suggest-move-types.rs:82:53 | LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^^^^^^----^^----^^^^^^^^^----^^^^^^^ - | | | | - | | | this associated type binding should be moved after the generic parameters - | | this associated type binding should be moved after the generic parameters - | this associated type binding should be moved after the generic parameters + | ^ ^^ ---- ---- ^ ^^ ---- ^ ^^ generic arguments + | | + | constraints + | +help: move the constraints after the generic arguments + | +LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a lifetime was expected - --> $DIR/suggest-move-types.rs:34:43 + --> $DIR/suggest-move-types.rs:33:43 | LL | struct Al<'a, T, M: OneWithLifetime> { | ^ @@ -91,7 +119,7 @@ LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime $DIR/suggest-move-types.rs:64:56 + --> $DIR/suggest-move-types.rs:65:56 | LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { | ^^ @@ -99,7 +127,7 @@ LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime $DIR/suggest-move-types.rs:80:56 + --> $DIR/suggest-move-types.rs:82:56 | LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { | ^^ diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.fixed b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.fixed new file mode 100644 index 0000000000000..8ef7e34ab3050 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(unused)] // for the fixed file + +trait Trait { + type A; + + fn func(&self) -> Self::A; +} + +struct S(T); +impl S { + fn foo<'a, T: Trait + 'a>(&self, _: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types + } + + fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types + } +} + +fn foo<'a, T: Trait + 'a>(_: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn foo2(x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar2>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types +} + +fn qux(_: usize) {} + +fn main() {} diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.rs b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.rs new file mode 100644 index 0000000000000..7bd38d0d45d90 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.rs @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(unused)] // for the fixed file + +trait Trait { + type A; + + fn func(&self) -> Self::A; +} + +struct S(T); +impl S { + fn foo<'a, T: Trait + 'a>(&self, _: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types + } + + fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types + } +} + +fn foo<'a, T: Trait + 'a>(_: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn foo2(x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar2>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types +} + +fn qux(_: usize) {} + +fn main() {} diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.stderr b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.stderr new file mode 100644 index 0000000000000..f785f7b84a76f --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.stderr @@ -0,0 +1,94 @@ +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:13:13 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn foo<'a, T: Trait + 'a>(&self, _: impl Trait, x: impl Trait, _: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:17:13 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn ban(x: T) where T: Trait { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:22:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn foo<'a, T: Trait + 'a>(_: impl Trait, x: impl Trait, _: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:26:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn bar>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:30:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type ` as Trait>::A` +help: consider constraining the associated type ` as Trait>::A` to `usize` + | +LL | fn foo2(x: impl Trait) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:34:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `>::A` +help: consider constraining the associated type `>::A` to `usize` + | +LL | fn bar2>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:38:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn ban(x: T) where T: Trait { + | ^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs new file mode 100644 index 0000000000000..7465049787f59 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs @@ -0,0 +1,43 @@ +// These are all the possible variations of this error I could think of for. +// `trait-with-missing-associated-type-restriction-fixable.rs` contains the subset of these that +// can be fixed with `rustfix`. + +trait Trait { + type A; + + fn func(&self) -> Self::A; + fn funk(&self, _: Self::A); +} + +fn foo(_: impl Trait, x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn foo2(x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar2>(x: T) { + x.funk(3); //~ ERROR mismatched types + qux(x.func()) //~ ERROR mismatched types +} + +fn baz>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bat(x: &mut dyn Trait<(), A = ()>) { + qux(x.func()) //~ ERROR mismatched types +} + +fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types +} + +fn qux(_: usize) {} + +fn main() {} diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr new file mode 100644 index 0000000000000..5ae1d45c6b703 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr @@ -0,0 +1,103 @@ +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:13:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn foo(_: impl Trait, x: impl Trait) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:17:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn bar>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:21:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type ` as Trait>::A` +help: consider constraining the associated type ` as Trait>::A` to `usize` + | +LL | fn foo2(x: impl Trait) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:25:12 + | +LL | x.funk(3); + | ^ expected associated type, found integer + | + = note: expected associated type `>::A` + found type `{integer}` +help: a method is available that returns `>::A` + --> $DIR/trait-with-missing-associated-type-restriction.rs:8:5 + | +LL | fn func(&self) -> Self::A; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ consider calling `Trait::func` +help: consider constraining the associated type `>::A` to `{integer}` + | +LL | fn bar2>(x: T) { + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:26:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `>::A` +help: consider constraining the associated type `>::A` to `usize` + | +LL | fn bar2>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:30:9 + | +LL | fn baz>(x: T) { + | - this type parameter +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found type parameter `D` + | + = note: expected type `usize` + found type parameter `D` + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:34:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found `()` + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:38:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn ban(x: T) where T: Trait { + | ^^^^^^^^^^^ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/syntax-trait-polarity-feature-gate.stderr b/src/test/ui/syntax-trait-polarity-feature-gate.stderr index 5d4c1b354f700..3562deecbd522 100644 --- a/src/test/ui/syntax-trait-polarity-feature-gate.stderr +++ b/src/test/ui/syntax-trait-polarity-feature-gate.stderr @@ -4,8 +4,8 @@ error[E0658]: negative trait bounds are not yet fully implemented; use marker ty LL | impl !Send for TestType {} | ^^^^^ | - = note: see issue #13231 for more information - = help: add `#![feature(optin_builtin_traits)]` to the crate attributes to enable + = note: see issue #68318 for more information + = help: add `#![feature(negative_impls)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/syntax-trait-polarity.rs b/src/test/ui/syntax-trait-polarity.rs index bfbe6394c1694..ed2947493b02c 100644 --- a/src/test/ui/syntax-trait-polarity.rs +++ b/src/test/ui/syntax-trait-polarity.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::marker::Send; @@ -12,7 +12,6 @@ trait TestTrait {} unsafe impl !Send for TestType {} //~^ ERROR negative impls cannot be unsafe impl !TestTrait for TestType {} -//~^ ERROR invalid negative impl struct TestType2(T); @@ -22,6 +21,5 @@ impl !TestType2 {} unsafe impl !Send for TestType2 {} //~^ ERROR negative impls cannot be unsafe impl !TestTrait for TestType2 {} -//~^ ERROR invalid negative impl fn main() {} diff --git a/src/test/ui/syntax-trait-polarity.stderr b/src/test/ui/syntax-trait-polarity.stderr index f70d67ec7dc98..1fd40fb66570d 100644 --- a/src/test/ui/syntax-trait-polarity.stderr +++ b/src/test/ui/syntax-trait-polarity.stderr @@ -16,7 +16,7 @@ LL | unsafe impl !Send for TestType {} | unsafe because of this error: inherent impls cannot be negative - --> $DIR/syntax-trait-polarity.rs:19:10 + --> $DIR/syntax-trait-polarity.rs:18:10 | LL | impl !TestType2 {} | -^^^^^^^^^^^^ inherent impl for this type @@ -24,7 +24,7 @@ LL | impl !TestType2 {} | negative because of this error[E0198]: negative impls cannot be unsafe - --> $DIR/syntax-trait-polarity.rs:22:16 + --> $DIR/syntax-trait-polarity.rs:21:16 | LL | unsafe impl !Send for TestType2 {} | ------ -^^^^ @@ -32,23 +32,6 @@ LL | unsafe impl !Send for TestType2 {} | | negative because of this | unsafe because of this -error[E0192]: invalid negative impl - --> $DIR/syntax-trait-polarity.rs:14:6 - | -LL | impl !TestTrait for TestType {} - | ^^^^^^^^^^ - | - = note: negative impls are only allowed for auto traits, like `Send` and `Sync` - -error[E0192]: invalid negative impl - --> $DIR/syntax-trait-polarity.rs:24:9 - | -LL | impl !TestTrait for TestType2 {} - | ^^^^^^^^^^ - | - = note: negative impls are only allowed for auto traits, like `Send` and `Sync` - -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0192, E0198. -For more information about an error, try `rustc --explain E0192`. +For more information about this error, try `rustc --explain E0198`. diff --git a/src/test/ui/target-feature/gate.rs b/src/test/ui/target-feature/gate.rs index 2d51cab675e00..f738c16673dca 100644 --- a/src/test/ui/target-feature/gate.rs +++ b/src/test/ui/target-feature/gate.rs @@ -25,6 +25,7 @@ // gate-test-movbe_target_feature // gate-test-rtm_target_feature // gate-test-f16c_target_feature +// gate-test-riscv_target_feature #[target_feature(enable = "avx512bw")] //~^ ERROR: currently unstable diff --git a/src/test/ui/target-feature/gate.stderr b/src/test/ui/target-feature/gate.stderr index 848538a4e92fd..2384a00aa47aa 100644 --- a/src/test/ui/target-feature/gate.stderr +++ b/src/test/ui/target-feature/gate.stderr @@ -1,5 +1,5 @@ error[E0658]: the target feature `avx512bw` is currently unstable - --> $DIR/gate.rs:29:18 + --> $DIR/gate.rs:30:18 | LL | #[target_feature(enable = "avx512bw")] | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/target-feature/invalid-attribute.rs b/src/test/ui/target-feature/invalid-attribute.rs index 46680336632f9..63b1951a71631 100644 --- a/src/test/ui/target-feature/invalid-attribute.rs +++ b/src/test/ui/target-feature/invalid-attribute.rs @@ -26,7 +26,7 @@ unsafe fn foo() {} #[target_feature(enable = "sse2")] //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions -//~| NOTE can only be applied to `unsafe` functions +//~| NOTE see issue #69098 fn bar() {} //~^ NOTE not an `unsafe` function @@ -65,9 +65,26 @@ trait Baz { } #[target_feature(enable = "sse2")] unsafe fn test() {} +trait Quux { + fn foo(); +} + +impl Quux for Foo { + #[target_feature(enable = "sse2")] + //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions + //~| NOTE see issue #69098 + fn foo() {} + //~^ NOTE not an `unsafe` function +} + fn main() { unsafe { foo(); bar(); } + #[target_feature(enable = "sse2")] + //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions + //~| NOTE see issue #69098 + || {}; + //~^ NOTE not an `unsafe` function } diff --git a/src/test/ui/target-feature/invalid-attribute.stderr b/src/test/ui/target-feature/invalid-attribute.stderr index abfe5dd219770..21d6aa218ec79 100644 --- a/src/test/ui/target-feature/invalid-attribute.stderr +++ b/src/test/ui/target-feature/invalid-attribute.stderr @@ -22,14 +22,17 @@ error: malformed `target_feature` attribute input LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` -error: `#[target_feature(..)]` can only be applied to `unsafe` functions +error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions --> $DIR/invalid-attribute.rs:27:1 | LL | #[target_feature(enable = "sse2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | fn bar() {} | ----------- not an `unsafe` function + | + = note: see issue #69098 for more information + = help: add `#![feature(target_feature_11)]` to the crate attributes to enable error: attribute should be applied to a function --> $DIR/invalid-attribute.rs:33:1 @@ -91,5 +94,30 @@ error: cannot use `#[inline(always)]` with `#[target_feature]` LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions + --> $DIR/invalid-attribute.rs:85:5 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | || {}; + | ----- not an `unsafe` function + | + = note: see issue #69098 for more information + = help: add `#![feature(target_feature_11)]` to the crate attributes to enable + +error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions + --> $DIR/invalid-attribute.rs:73:5 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn foo() {} + | ----------- not an `unsafe` function + | + = note: see issue #69098 for more information + = help: add `#![feature(target_feature_11)]` to the crate attributes to enable + +error: aborting due to 14 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/run-fail/run-unexported-tests.rs b/src/test/ui/test-attrs/run-unexported-tests.rs similarity index 79% rename from src/test/run-fail/run-unexported-tests.rs rename to src/test/ui/test-attrs/run-unexported-tests.rs index 11e100e716eb4..f533a3ef885d6 100644 --- a/src/test/run-fail/run-unexported-tests.rs +++ b/src/test/ui/test-attrs/run-unexported-tests.rs @@ -1,4 +1,4 @@ -// error-pattern:ran an unexported test +// run-fail // compile-flags:--test // check-stdout diff --git a/src/test/ui/test-attrs/test-on-macro.stderr b/src/test/ui/test-attrs/test-on-macro.stderr index 256a41722fa95..98190b060cec5 100644 --- a/src/test/ui/test-attrs/test-on-macro.stderr +++ b/src/test/ui/test-attrs/test-on-macro.stderr @@ -4,3 +4,5 @@ warning: `#[test]` attribute should not be used on macros. Use `#[cfg(test)]` in LL | foo!(); | ^^^^^^^ +warning: 1 warning emitted + diff --git a/src/test/ui/test-attrs/test-should-panic-attr.rs b/src/test/ui/test-attrs/test-should-panic-attr.rs index 9c38322fe96fd..b71878406d764 100644 --- a/src/test/ui/test-attrs/test-should-panic-attr.rs +++ b/src/test/ui/test-attrs/test-should-panic-attr.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // compile-flags: --test #[test] diff --git a/src/test/ui/test-attrs/test-should-panic-attr.stderr b/src/test/ui/test-attrs/test-should-panic-attr.stderr index d7d0d84432ce6..375ee79ca5ab7 100644 --- a/src/test/ui/test-attrs/test-should-panic-attr.stderr +++ b/src/test/ui/test-attrs/test-should-panic-attr.stderr @@ -30,3 +30,5 @@ LL | #[should_panic(expected = "foo", bar)] | = note: errors in this attribute were erroneously allowed and will become a hard error in a future release. +warning: 4 warnings emitted + diff --git a/src/test/ui/test-panic-abort-nocapture.rs b/src/test/ui/test-panic-abort-nocapture.rs index 75f7983865020..978732a9ec374 100644 --- a/src/test/ui/test-panic-abort-nocapture.rs +++ b/src/test/ui/test-panic-abort-nocapture.rs @@ -7,6 +7,7 @@ // ignore-wasm no panic or subprocess support // ignore-emscripten no panic or subprocess support +// ignore-sgx no subprocess support #![cfg(test)] diff --git a/src/test/ui/test-panic-abort-nocapture.run.stderr b/src/test/ui/test-panic-abort-nocapture.run.stderr index 37fbe3d3ff21f..3388813d5a0bd 100644 --- a/src/test/ui/test-panic-abort-nocapture.run.stderr +++ b/src/test/ui/test-panic-abort-nocapture.run.stderr @@ -1,9 +1,9 @@ thread 'main' panicked at 'assertion failed: `(left == right)` left: `2`, - right: `4`', $DIR/test-panic-abort-nocapture.rs:31:5 + right: `4`', $DIR/test-panic-abort-nocapture.rs:32:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' panicked at 'assertion failed: `(left == right)` left: `2`, - right: `4`', $DIR/test-panic-abort-nocapture.rs:25:5 + right: `4`', $DIR/test-panic-abort-nocapture.rs:26:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace testing321 diff --git a/src/test/ui/test-panic-abort.rs b/src/test/ui/test-panic-abort.rs index b7bf5a150ea8b..21e7dc393f512 100644 --- a/src/test/ui/test-panic-abort.rs +++ b/src/test/ui/test-panic-abort.rs @@ -7,6 +7,7 @@ // ignore-wasm no panic or subprocess support // ignore-emscripten no panic or subprocess support +// ignore-sgx no subprocess support #![cfg(test)] diff --git a/src/test/ui/test-panic-abort.run.stdout b/src/test/ui/test-panic-abort.run.stdout index 2f4bc32ed6a1f..33ddd519030ff 100644 --- a/src/test/ui/test-panic-abort.run.stdout +++ b/src/test/ui/test-panic-abort.run.stdout @@ -18,7 +18,7 @@ testing123 testing321 thread 'main' panicked at 'assertion failed: `(left == right)` left: `2`, - right: `5`', $DIR/test-panic-abort.rs:32:5 + right: `5`', $DIR/test-panic-abort.rs:33:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/test/run-fail/task-spawn-barefn.rs b/src/test/ui/threads-sendsync/task-spawn-barefn.rs similarity index 97% rename from src/test/run-fail/task-spawn-barefn.rs rename to src/test/ui/threads-sendsync/task-spawn-barefn.rs index 497c5ea71804f..e5b899e0af967 100644 --- a/src/test/run-fail/task-spawn-barefn.rs +++ b/src/test/ui/threads-sendsync/task-spawn-barefn.rs @@ -1,3 +1,4 @@ +// run-fail // error-pattern:Ensure that the child thread runs by panicking // ignore-emscripten Needs threads. diff --git a/src/test/run-fail/test-tasks-invalid-value.rs b/src/test/ui/threads-sendsync/test-tasks-invalid-value.rs similarity index 95% rename from src/test/run-fail/test-tasks-invalid-value.rs rename to src/test/ui/threads-sendsync/test-tasks-invalid-value.rs index 2dae1b4592c0f..6411421429c4c 100644 --- a/src/test/run-fail/test-tasks-invalid-value.rs +++ b/src/test/ui/threads-sendsync/test-tasks-invalid-value.rs @@ -1,6 +1,7 @@ // This checks that RUST_TEST_THREADS not being 1, 2, ... is detected // properly. +// run-fail // error-pattern:should be a positive integer // compile-flags: --test // exec-env:RUST_TEST_THREADS=foo diff --git a/src/test/ui/tool_lints.stderr b/src/test/ui/tool_lints.stderr index 86f87784eaf86..1bcd7fd735de8 100644 --- a/src/test/ui/tool_lints.stderr +++ b/src/test/ui/tool_lints.stderr @@ -18,3 +18,4 @@ LL | #[warn(foo::bar)] error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0710`. diff --git a/src/test/ui/traits/auto-traits.rs b/src/test/ui/traits/auto-traits.rs index c495b97b25bfe..15fdddc5f3f5c 100644 --- a/src/test/ui/traits/auto-traits.rs +++ b/src/test/ui/traits/auto-traits.rs @@ -1,6 +1,7 @@ // run-pass #![allow(unused_doc_comments)] #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait Auto {} unsafe auto trait AutoUnsafe {} diff --git a/src/test/ui/traits/cycle-cache-err-60010.stderr b/src/test/ui/traits/cycle-cache-err-60010.stderr index 295845b1146ef..3188ee83e7d39 100644 --- a/src/test/ui/traits/cycle-cache-err-60010.stderr +++ b/src/test/ui/traits/cycle-cache-err-60010.stderr @@ -7,15 +7,15 @@ LL | _parse: >::Data, = note: required because of the requirements on the impl of `Query` for `ParseQuery` error[E0275]: overflow evaluating the requirement `Runtime: std::panic::RefUnwindSafe` - --> $DIR/cycle-cache-err-60010.rs:31:5 + --> $DIR/cycle-cache-err-60010.rs:31:20 | +LL | trait Database { + | -------- required by a bound in this LL | type Storage; - | ------- associated type defined here + | ------------- required by this bound in `Database` ... -LL | impl Database for RootDatabase { - | ------------------------------ in this `impl` item LL | type Storage = SalsaStorage; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ | = note: required because it appears within the type `RootDatabase` = note: required because of the requirements on the impl of `SourceDatabase` for `RootDatabase` diff --git a/src/test/ui/traits/impl_trait_as_trait_return_position.rs b/src/test/ui/traits/impl_trait_as_trait_return_position.rs new file mode 100644 index 0000000000000..c3325fd80ca0c --- /dev/null +++ b/src/test/ui/traits/impl_trait_as_trait_return_position.rs @@ -0,0 +1,17 @@ +// check-pass + +trait A { + type Foo; +} + +impl A for T { + type Foo = (); +} + +fn foo() -> impl std::borrow::Borrow<::Foo> { + () +} + +fn main() { + foo(); +} diff --git a/src/test/ui/traits/negative-impls/auxiliary/foreign_trait.rs b/src/test/ui/traits/negative-impls/auxiliary/foreign_trait.rs new file mode 100644 index 0000000000000..681f26438e695 --- /dev/null +++ b/src/test/ui/traits/negative-impls/auxiliary/foreign_trait.rs @@ -0,0 +1,6 @@ +#![feature(negative_impls)] + +pub trait ForeignTrait {} + +impl ForeignTrait for u32 {} +impl !ForeignTrait for String {} diff --git a/src/test/ui/traits/negative-impls/feature-gate-negative_impls.rs b/src/test/ui/traits/negative-impls/feature-gate-negative_impls.rs new file mode 100644 index 0000000000000..683fd6db6f29d --- /dev/null +++ b/src/test/ui/traits/negative-impls/feature-gate-negative_impls.rs @@ -0,0 +1,3 @@ +trait MyTrait {} +impl !MyTrait for u32 {} //~ ERROR negative trait bounds are not yet fully implemented +fn main() {} diff --git a/src/test/ui/traits/negative-impls/feature-gate-negative_impls.stderr b/src/test/ui/traits/negative-impls/feature-gate-negative_impls.stderr new file mode 100644 index 0000000000000..b253fbd0da7f9 --- /dev/null +++ b/src/test/ui/traits/negative-impls/feature-gate-negative_impls.stderr @@ -0,0 +1,12 @@ +error[E0658]: negative trait bounds are not yet fully implemented; use marker types for now + --> $DIR/feature-gate-negative_impls.rs:2:6 + | +LL | impl !MyTrait for u32 {} + | ^^^^^^^^ + | + = note: see issue #68318 for more information + = help: add `#![feature(negative_impls)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/traits/negative-impls/negated-auto-traits-error.rs b/src/test/ui/traits/negative-impls/negated-auto-traits-error.rs new file mode 100644 index 0000000000000..4bdad5dc591c5 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negated-auto-traits-error.rs @@ -0,0 +1,68 @@ +// The dummy functions are used to avoid adding new cfail files. +// What happens is that the compiler attempts to squash duplicates and some +// errors are not reported. This way, we make sure that, for each function, different +// typeck phases are involved and all errors are reported. + +#![feature(negative_impls)] + +use std::marker::Send; + +struct Outer(T); + +struct Outer2(T); + +unsafe impl Sync for Outer2 {} + +fn is_send(_: T) {} +fn is_sync(_: T) {} + +fn dummy() { + struct TestType; + impl !Send for TestType {} + + Outer(TestType); + //~^ ERROR `dummy::TestType` cannot be sent between threads safely + //~| ERROR `dummy::TestType` cannot be sent between threads safely +} + +fn dummy1b() { + struct TestType; + impl !Send for TestType {} + + is_send(TestType); + //~^ ERROR `dummy1b::TestType` cannot be sent between threads safely +} + +fn dummy1c() { + struct TestType; + impl !Send for TestType {} + + is_send((8, TestType)); + //~^ ERROR `dummy1c::TestType` cannot be sent between threads safely +} + +fn dummy2() { + struct TestType; + impl !Send for TestType {} + + is_send(Box::new(TestType)); + //~^ ERROR `dummy2::TestType` cannot be sent between threads safely +} + +fn dummy3() { + struct TestType; + impl !Send for TestType {} + + is_send(Box::new(Outer2(TestType))); + //~^ ERROR `dummy3::TestType` cannot be sent between threads safely +} + +fn main() { + struct TestType; + impl !Send for TestType {} + + // This will complain about a missing Send impl because `Sync` is implement *just* + // for T that are `Send`. Look at #20366 and #19950 + is_sync(Outer2(TestType)); + //~^ ERROR `main::TestType` cannot be sent between threads safely +} diff --git a/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr b/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr new file mode 100644 index 0000000000000..446b8dbf1148f --- /dev/null +++ b/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr @@ -0,0 +1,93 @@ +error[E0277]: `dummy::TestType` cannot be sent between threads safely + --> $DIR/negated-auto-traits-error.rs:23:11 + | +LL | struct Outer(T); + | ------------------------- required by `Outer` +... +LL | Outer(TestType); + | ^^^^^^^^ `dummy::TestType` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dummy::TestType` + +error[E0277]: `dummy::TestType` cannot be sent between threads safely + --> $DIR/negated-auto-traits-error.rs:23:5 + | +LL | struct Outer(T); + | ---- required by this bound in `Outer` +... +LL | Outer(TestType); + | ^^^^^^^^^^^^^^^ `dummy::TestType` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dummy::TestType` + +error[E0277]: `dummy1b::TestType` cannot be sent between threads safely + --> $DIR/negated-auto-traits-error.rs:32:13 + | +LL | fn is_send(_: T) {} + | ---- required by this bound in `is_send` +... +LL | is_send(TestType); + | ^^^^^^^^ `dummy1b::TestType` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dummy1b::TestType` + +error[E0277]: `dummy1c::TestType` cannot be sent between threads safely + --> $DIR/negated-auto-traits-error.rs:40:13 + | +LL | fn is_send(_: T) {} + | ---- required by this bound in `is_send` +... +LL | is_send((8, TestType)); + | ^^^^^^^^^^^^^ `dummy1c::TestType` cannot be sent between threads safely + | + = help: within `({integer}, dummy1c::TestType)`, the trait `std::marker::Send` is not implemented for `dummy1c::TestType` + = note: required because it appears within the type `({integer}, dummy1c::TestType)` + +error[E0277]: `dummy2::TestType` cannot be sent between threads safely + --> $DIR/negated-auto-traits-error.rs:48:13 + | +LL | fn is_send(_: T) {} + | ---- required by this bound in `is_send` +... +LL | is_send(Box::new(TestType)); + | ^^^^^^^^^^^^^^^^^^ + | | + | expected an implementor of trait `std::marker::Send` + | help: consider borrowing here: `&Box::new(TestType)` + | + = note: the trait bound `dummy2::TestType: std::marker::Send` is not satisfied + = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique` + = note: required because it appears within the type `std::boxed::Box` + +error[E0277]: `dummy3::TestType` cannot be sent between threads safely + --> $DIR/negated-auto-traits-error.rs:56:13 + | +LL | fn is_send(_: T) {} + | ---- required by this bound in `is_send` +... +LL | is_send(Box::new(Outer2(TestType))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `dummy3::TestType` cannot be sent between threads safely + | + = help: within `Outer2`, the trait `std::marker::Send` is not implemented for `dummy3::TestType` + = note: required because it appears within the type `Outer2` + = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique>` + = note: required because it appears within the type `std::boxed::Box>` + +error[E0277]: `main::TestType` cannot be sent between threads safely + --> $DIR/negated-auto-traits-error.rs:66:13 + | +LL | fn is_sync(_: T) {} + | ---- required by this bound in `is_sync` +... +LL | is_sync(Outer2(TestType)); + | ^^^^^^^^^^^^^^^^ + | | + | expected an implementor of trait `std::marker::Sync` + | help: consider borrowing here: `&Outer2(TestType)` + | + = note: the trait bound `main::TestType: std::marker::Sync` is not satisfied + = note: required because of the requirements on the impl of `std::marker::Sync` for `Outer2` + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/negative-impls/negated-auto-traits-rpass.rs b/src/test/ui/traits/negative-impls/negated-auto-traits-rpass.rs new file mode 100644 index 0000000000000..010dbf2466403 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negated-auto-traits-rpass.rs @@ -0,0 +1,21 @@ +// run-pass +#![allow(unused_variables)] +#![feature(negative_impls)] + +use std::marker::Send; + +pub struct WaitToken; +impl !Send for WaitToken {} + +pub struct Test(T); +unsafe impl Send for Test {} + +pub fn spawn(_: F) -> () where F: FnOnce(), F: Send + 'static {} + +fn main() { + let wt = Test(WaitToken); + spawn(move || { + let x = wt; + println!("Hello, World!"); + }); +} diff --git a/src/test/ui/traits/negative-impls/negative-default-impls.rs b/src/test/ui/traits/negative-impls/negative-default-impls.rs new file mode 100644 index 0000000000000..2d50bc83ec304 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-default-impls.rs @@ -0,0 +1,10 @@ +#![feature(negative_impls)] +#![feature(specialization)] + +trait MyTrait { + type Foo; +} + +default impl !MyTrait for u32 {} //~ ERROR negative impls cannot be default impls + +fn main() {} diff --git a/src/test/ui/traits/negative-impls/negative-default-impls.stderr b/src/test/ui/traits/negative-impls/negative-default-impls.stderr new file mode 100644 index 0000000000000..a70bbe6b948d1 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-default-impls.stderr @@ -0,0 +1,9 @@ +error[E0750]: negative impls cannot be default impls + --> $DIR/negative-default-impls.rs:8:1 + | +LL | default impl !MyTrait for u32 {} + | ^^^^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0750`. diff --git a/src/test/ui/traits/negative-impls/negative-impls-basic.rs b/src/test/ui/traits/negative-impls/negative-impls-basic.rs new file mode 100644 index 0000000000000..474e0381799bd --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-impls-basic.rs @@ -0,0 +1,17 @@ +// A simple test that we are able to create negative impls, when the +// feature gate is given. +// +// run-pass + +#![feature(negative_impls)] +#![allow(dead_code)] + +struct TestType; + +trait TestTrait { + fn dummy(&self) {} +} + +impl !TestTrait for TestType {} + +fn main() {} diff --git a/src/test/ui/traits/negative-impls/negative-specializes-negative.rs b/src/test/ui/traits/negative-impls/negative-specializes-negative.rs new file mode 100644 index 0000000000000..877c3e8af4f1a --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-negative.rs @@ -0,0 +1,13 @@ +#![feature(specialization)] +#![feature(negative_impls)] + +// Test a negative impl that "specializes" another negative impl. +// +// run-pass + +trait MyTrait {} + +impl !MyTrait for T {} +impl !MyTrait for u32 {} + +fn main() {} diff --git a/src/test/ui/traits/negative-impls/negative-specializes-positive-item.rs b/src/test/ui/traits/negative-impls/negative-specializes-positive-item.rs new file mode 100644 index 0000000000000..da22e43377f52 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive-item.rs @@ -0,0 +1,13 @@ +#![feature(specialization)] +#![feature(negative_impls)] + +// Negative impl for u32 cannot "specialize" the base impl. +trait MyTrait { + fn foo(); +} +impl MyTrait for T { + default fn foo() {} +} +impl !MyTrait for u32 {} //~ ERROR E0751 + +fn main() {} diff --git a/src/test/ui/traits/negative-impls/negative-specializes-positive-item.stderr b/src/test/ui/traits/negative-impls/negative-specializes-positive-item.stderr new file mode 100644 index 0000000000000..079546a7df40b --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive-item.stderr @@ -0,0 +1,12 @@ +error[E0751]: found both positive and negative implementation of trait `MyTrait` for type `u32`: + --> $DIR/negative-specializes-positive-item.rs:11:1 + | +LL | impl MyTrait for T { + | --------------------- positive implementation here +... +LL | impl !MyTrait for u32 {} + | ^^^^^^^^^^^^^^^^^^^^^ negative implementation here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0751`. diff --git a/src/test/ui/traits/negative-impls/negative-specializes-positive.rs b/src/test/ui/traits/negative-impls/negative-specializes-positive.rs new file mode 100644 index 0000000000000..1939a098b50ee --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive.rs @@ -0,0 +1,14 @@ +#![feature(specialization)] +#![feature(negative_impls)] + +// Negative impl for u32 cannot "specialize" the base impl. +trait MyTrait {} +impl MyTrait for T {} +impl !MyTrait for u32 {} //~ ERROR E0751 + +// The second impl specializes the first, no error. +trait MyTrait2 {} +impl MyTrait2 for T {} +impl MyTrait2 for u32 {} + +fn main() {} diff --git a/src/test/ui/traits/negative-impls/negative-specializes-positive.stderr b/src/test/ui/traits/negative-impls/negative-specializes-positive.stderr new file mode 100644 index 0000000000000..ea005c1cbe0c6 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive.stderr @@ -0,0 +1,11 @@ +error[E0751]: found both positive and negative implementation of trait `MyTrait` for type `u32`: + --> $DIR/negative-specializes-positive.rs:7:1 + | +LL | impl MyTrait for T {} + | --------------------- positive implementation here +LL | impl !MyTrait for u32 {} + | ^^^^^^^^^^^^^^^^^^^^^ negative implementation here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0751`. diff --git a/src/test/ui/traits/negative-impls/no-items.rs b/src/test/ui/traits/negative-impls/no-items.rs new file mode 100644 index 0000000000000..5fc6be9b30007 --- /dev/null +++ b/src/test/ui/traits/negative-impls/no-items.rs @@ -0,0 +1,11 @@ +#![feature(negative_impls)] + +trait MyTrait { + type Foo; +} + +impl !MyTrait for u32 { + type Foo = i32; //~ ERROR negative impls cannot have any items +} + +fn main() {} diff --git a/src/test/ui/traits/negative-impls/no-items.stderr b/src/test/ui/traits/negative-impls/no-items.stderr new file mode 100644 index 0000000000000..67b94bba12143 --- /dev/null +++ b/src/test/ui/traits/negative-impls/no-items.stderr @@ -0,0 +1,9 @@ +error[E0749]: negative impls cannot have any items + --> $DIR/no-items.rs:8:5 + | +LL | type Foo = i32; + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0749`. diff --git a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.rs b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.rs new file mode 100644 index 0000000000000..a5b85646581bf --- /dev/null +++ b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.rs @@ -0,0 +1,26 @@ +use std::cell::Cell; +use std::marker::PhantomPinned; +use std::pin::Pin; + +struct MyType<'a>(Cell>>, PhantomPinned); + +impl<'a> Clone for &'a mut MyType<'a> { + //~^ ERROR E0751 + fn clone(&self) -> &'a mut MyType<'a> { + self.0.take().unwrap() + } +} + +fn main() { + let mut unpinned = MyType(Cell::new(None), PhantomPinned); + let bad_addr = &unpinned as *const MyType<'_> as usize; + let mut p = Box::pin(MyType(Cell::new(Some(&mut unpinned)), PhantomPinned)); + + // p_mut1 is okay: it does not point to the bad_addr + let p_mut1: Pin<&mut MyType<'_>> = p.as_mut(); + assert_ne!(bad_addr, &*p_mut1 as *const _ as usize); + + // but p_mut2 does point to bad_addr! this is unsound + let p_mut2: Pin<&mut MyType<'_>> = p_mut1.clone(); + assert_eq!(bad_addr, &*p_mut2 as *const _ as usize); +} diff --git a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr new file mode 100644 index 0000000000000..d7039e3db6bde --- /dev/null +++ b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr @@ -0,0 +1,11 @@ +error[E0751]: found both positive and negative implementation of trait `std::clone::Clone` for type `&mut MyType<'_>`: + --> $DIR/pin-unsound-issue-66544-clone.rs:7:1 + | +LL | impl<'a> Clone for &'a mut MyType<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ positive implementation here + | + = note: negative implementation in crate `core` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0751`. diff --git a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.rs b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.rs new file mode 100644 index 0000000000000..606cc65a84bc2 --- /dev/null +++ b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.rs @@ -0,0 +1,33 @@ +// Demonstrate that "rogue" `DerefMut` impls for `&T` are not allowed. +// +// https://github.com/rust-lang/rust/issues/66544 + +use std::cell::Cell; +use std::marker::PhantomPinned; +use std::ops::DerefMut; +use std::pin::Pin; + +struct MyType<'a>(Cell>>, PhantomPinned); + +impl<'a> DerefMut for &'a MyType<'a> { + //~^ ERROR E0751 + fn deref_mut(&mut self) -> &mut MyType<'a> { + self.0.take().unwrap() + } +} + +fn main() { + let mut unpinned = MyType(Cell::new(None), PhantomPinned); + let bad_addr = &unpinned as *const MyType<'_> as usize; + let p = Box::pin(MyType(Cell::new(Some(&mut unpinned)), PhantomPinned)); + + // p_ref is okay: it does not point to the bad_addr + let mut p_ref: Pin<&MyType<'_>> = p.as_ref(); + assert_ne!(bad_addr, &*p_ref as *const _ as usize); + + // but p_mut does point to bad_addr! this is unsound + let p_mut: Pin<&mut MyType<'_>> = p_ref.as_mut(); + assert_eq!(bad_addr, &*p_mut as *const _ as usize); + + println!("oh no!"); +} diff --git a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr new file mode 100644 index 0000000000000..a0b62a8bab68f --- /dev/null +++ b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr @@ -0,0 +1,11 @@ +error[E0751]: found both positive and negative implementation of trait `std::ops::DerefMut` for type `&MyType<'_>`: + --> $DIR/pin-unsound-issue-66544-derefmut.rs:12:1 + | +LL | impl<'a> DerefMut for &'a MyType<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ positive implementation here + | + = note: negative implementation in crate `core` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0751`. diff --git a/src/test/ui/traits/negative-impls/positive-specializes-negative.rs b/src/test/ui/traits/negative-impls/positive-specializes-negative.rs new file mode 100644 index 0000000000000..f2c5f507a4ebb --- /dev/null +++ b/src/test/ui/traits/negative-impls/positive-specializes-negative.rs @@ -0,0 +1,9 @@ +#![feature(specialization)] +#![feature(negative_impls)] + +trait MyTrait {} + +impl !MyTrait for T {} +impl MyTrait for u32 {} //~ ERROR E0751 + +fn main() {} diff --git a/src/test/ui/traits/negative-impls/positive-specializes-negative.stderr b/src/test/ui/traits/negative-impls/positive-specializes-negative.stderr new file mode 100644 index 0000000000000..a24d7aa442f4a --- /dev/null +++ b/src/test/ui/traits/negative-impls/positive-specializes-negative.stderr @@ -0,0 +1,11 @@ +error[E0751]: found both positive and negative implementation of trait `MyTrait` for type `u32`: + --> $DIR/positive-specializes-negative.rs:7:1 + | +LL | impl !MyTrait for T {} + | ---------------------- negative implementation here +LL | impl MyTrait for u32 {} + | ^^^^^^^^^^^^^^^^^^^^ positive implementation here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0751`. diff --git a/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.rs b/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.rs new file mode 100644 index 0000000000000..db72aaf18034f --- /dev/null +++ b/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.rs @@ -0,0 +1,21 @@ +#![feature(negative_impls)] + +// aux-build: foreign_trait.rs + +// Test that we cannot implement `LocalTrait` for `String`, +// even though there is a `String: !ForeignTrait` impl. +// +// This may not be the behavior we want long term, but it's the +// current semantics that we implemented so as to land `!Foo` impls +// quickly. See internals thread: +// +// https://internals.rust-lang.org/t/foo/11587/ + +extern crate foreign_trait; +use foreign_trait::ForeignTrait; + +trait LocalTrait { } +impl LocalTrait for T { } +impl LocalTrait for String { } //~ ERROR conflicting implementations + +fn main() { } diff --git a/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.stderr b/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.stderr new file mode 100644 index 0000000000000..7cce45d2c8f8f --- /dev/null +++ b/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.stderr @@ -0,0 +1,11 @@ +error[E0119]: conflicting implementations of trait `LocalTrait` for type `std::string::String`: + --> $DIR/rely-on-negative-impl-in-coherence.rs:19:1 + | +LL | impl LocalTrait for T { } + | -------------------------------------- first implementation here +LL | impl LocalTrait for String { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::string::String` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.rs b/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.rs index 86029473b513b..4106f56d64ac6 100644 --- a/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.rs +++ b/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] // Overlapping negative impls for `MyStruct` are not permitted: struct MyStruct; @@ -7,5 +7,4 @@ impl !Send for MyStruct {} impl !Send for MyStruct {} //~^ ERROR conflicting implementations of trait -fn main() { -} +fn main() {} diff --git a/src/test/ui/traits/overlap-permitted-for-marker-traits.rs b/src/test/ui/traits/overlap-permitted-for-marker-traits.rs new file mode 100644 index 0000000000000..00823d13b3696 --- /dev/null +++ b/src/test/ui/traits/overlap-permitted-for-marker-traits.rs @@ -0,0 +1,28 @@ +// run-pass +// Tests for RFC 1268: we allow overlapping impls of marker traits, +// that is, traits without items. In this case, a type `T` is +// `MyMarker` if it is either `Debug` or `Display`. + +#![feature(marker_trait_attr)] +#![feature(negative_impls)] + +use std::fmt::{Debug, Display}; + +#[marker] +trait MyMarker {} + +impl MyMarker for T {} +impl MyMarker for T {} + +fn foo(t: T) -> T { + t +} + +fn main() { + // Debug && Display: + assert_eq!(1, foo(1)); + assert_eq!(2.0, foo(2.0)); + + // Debug && !Display: + assert_eq!(vec![1], foo(vec![1])); +} diff --git a/src/test/ui/traits/syntax-trait-polarity.rs b/src/test/ui/traits/syntax-trait-polarity.rs index c6524f5c8e782..c809f9e89f934 100644 --- a/src/test/ui/traits/syntax-trait-polarity.rs +++ b/src/test/ui/traits/syntax-trait-polarity.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] // pretty-expanded FIXME #23616 -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] struct TestType; diff --git a/src/test/ui/traits/trait-alias-ambiguous.stderr b/src/test/ui/traits/trait-alias-ambiguous.stderr index 48a029104aeca..7c00bb5207bdf 100644 --- a/src/test/ui/traits/trait-alias-ambiguous.stderr +++ b/src/test/ui/traits/trait-alias-ambiguous.stderr @@ -14,11 +14,11 @@ note: candidate #2 is defined in an impl of the trait `inner::B` for the type `u | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #1 +help: disambiguate the associated function for candidate #1 | LL | inner::A::foo(&t); | ^^^^^^^^^^^^^^^^^ -help: disambiguate the method call for candidate #2 +help: disambiguate the associated function for candidate #2 | LL | inner::B::foo(&t); | ^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr b/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr index cad5c81f5a6ca..04c86cb240341 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::rc::Rc` cannot be sent between threads safely --> $DIR/trait-alias-cross-crate.rs:14:17 | LL | fn use_alias() {} - | --------- -------- required by this bound in `use_alias` + | -------- required by this bound in `use_alias` ... LL | use_alias::>(); | ^^^^^^^ `std::rc::Rc` cannot be sent between threads safely @@ -13,7 +13,7 @@ error[E0277]: `std::rc::Rc` cannot be shared between threads safely --> $DIR/trait-alias-cross-crate.rs:14:17 | LL | fn use_alias() {} - | --------- -------- required by this bound in `use_alias` + | -------- required by this bound in `use_alias` ... LL | use_alias::>(); | ^^^^^^^ `std::rc::Rc` cannot be shared between threads safely diff --git a/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs b/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs index 3be8db8663ab7..d62fd7e59c920 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs +++ b/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #![feature(trait_alias)] trait EqAlias = Eq; diff --git a/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr b/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr index 21818097bd6a0..56ecb7256f8cd 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr @@ -1,5 +1,5 @@ error[E0038]: the trait `std::cmp::Eq` cannot be made into an object - --> $DIR/trait-alias-object-fail.rs:12:13 + --> $DIR/trait-alias-object-fail.rs:7:13 | LL | let _: &dyn EqAlias = &123; | ^^^^^^^^^^^ the trait `std::cmp::Eq` cannot be made into an object @@ -10,7 +10,7 @@ LL | pub trait Eq: PartialEq { | --------------- the trait cannot be made into an object because it uses `Self` as a type parameter in this error[E0191]: the value of the associated type `Item` (from trait `std::iter::Iterator`) must be specified - --> $DIR/trait-alias-object-fail.rs:14:17 + --> $DIR/trait-alias-object-fail.rs:9:17 | LL | let _: &dyn IteratorAlias = &vec![123].into_iter(); | ^^^^^^^^^^^^^ help: specify the associated type: `IteratorAlias` diff --git a/src/test/ui/traits/trait-alias/trait-alias-wf.stderr b/src/test/ui/traits/trait-alias/trait-alias-wf.stderr index e7ed16a02a3f0..e0df76381e088 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-wf.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-wf.stderr @@ -2,15 +2,14 @@ error[E0277]: the trait bound `T: Foo` is not satisfied --> $DIR/trait-alias-wf.rs:5:14 | LL | trait A {} - | --------------- required by `A` + | --- required by this bound in `A` LL | trait B = A; | ^^^^ the trait `Foo` is not implemented for `T` | -help: consider restricting this type parameter with `T: Foo` - --> $DIR/trait-alias-wf.rs:5:9 +help: consider restricting type parameter `T` | -LL | trait B = A; - | ^ +LL | trait B = A; + | ^^^^^ error: aborting due to previous error diff --git a/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr b/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr index f64e637425d05..5e685105b45a3 100644 --- a/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr +++ b/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr @@ -17,6 +17,6 @@ LL | fn foo(_x: Foo + Send) { = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-fns.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-fns.stderr index a2253021a7f1f..6ca8ce0707f8c 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-fns.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-fns.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `u32: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-in-fns.rs:13:15 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | fn explode(x: Foo) {} | ^^^^^^^^ the trait `Trait` is not implemented for `u32` @@ -11,7 +11,7 @@ error[E0277]: the trait bound `f32: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-in-fns.rs:16:14 | LL | enum Bar { - | ----------------- required by `Bar` + | ----- required by this bound in `Bar` ... LL | fn kaboom(y: Bar) {} | ^^^^^^^^ the trait `Trait` is not implemented for `f32` diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-impls.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-impls.stderr index 7e8db610fe233..87271e7f1ee15 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-impls.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-in-impls.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `u16: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-in-impls.rs:20:6 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | impl PolyTrait> for Struct { | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `u16` diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-locals.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-locals.stderr index 070b7b013e5cc..df016a7727428 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-locals.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-locals.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `usize: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-locals.rs:15:14 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | let baz: Foo = loop { }; | ^^^^^^^^^^ the trait `Trait` is not implemented for `usize` diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-static.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-static.stderr index 722f01750cb66..4b650e78badf9 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-static.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-static.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `usize: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-static.rs:9:11 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | static X: Foo = Foo { | ^^^^^^^^^^ the trait `Trait` is not implemented for `usize` diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr index c5a7746afdfdb..d2fa211b487de 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr @@ -3,16 +3,22 @@ error[E0277]: the trait bound `usize: trait_bounds_on_structs_and_enums_xc::Trai | LL | fn explode(x: Foo) {} | ^^^^^^^^^^ the trait `trait_bounds_on_structs_and_enums_xc::Trait` is not implemented for `usize` + | + ::: $DIR/auxiliary/trait_bounds_on_structs_and_enums_xc.rs:5:18 | - = note: required by `trait_bounds_on_structs_and_enums_xc::Foo` +LL | pub struct Foo { + | ----- required by this bound in `trait_bounds_on_structs_and_enums_xc::Foo` error[E0277]: the trait bound `f32: trait_bounds_on_structs_and_enums_xc::Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-xc.rs:10:14 | LL | fn kaboom(y: Bar) {} | ^^^^^^^^ the trait `trait_bounds_on_structs_and_enums_xc::Trait` is not implemented for `f32` + | + ::: $DIR/auxiliary/trait_bounds_on_structs_and_enums_xc.rs:9:16 | - = note: required by `trait_bounds_on_structs_and_enums_xc::Bar` +LL | pub enum Bar { + | ----- required by this bound in `trait_bounds_on_structs_and_enums_xc::Bar` error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr index 57db65df5f331..ee3e755c95318 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr @@ -3,8 +3,11 @@ error[E0277]: the trait bound `f64: trait_bounds_on_structs_and_enums_xc::Trait` | LL | let bar: Bar = return; | ^^^^^^^^ the trait `trait_bounds_on_structs_and_enums_xc::Trait` is not implemented for `f64` + | + ::: $DIR/auxiliary/trait_bounds_on_structs_and_enums_xc.rs:9:16 | - = note: required by `trait_bounds_on_structs_and_enums_xc::Bar` +LL | pub enum Bar { + | ----- required by this bound in `trait_bounds_on_structs_and_enums_xc::Bar` error[E0277]: the trait bound `{integer}: trait_bounds_on_structs_and_enums_xc::Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-xc1.rs:8:15 diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums.stderr index 56a9e3ff54ec2..271ed07ce42ab 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums.stderr @@ -2,22 +2,21 @@ error[E0277]: the trait bound `T: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums.rs:13:9 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | impl Foo { | ^^^^^^ the trait `Trait` is not implemented for `T` | -help: consider restricting this type parameter with `T: Trait` - --> $DIR/trait-bounds-on-structs-and-enums.rs:13:6 +help: consider restricting type parameter `T` | -LL | impl Foo { - | ^ +LL | impl Foo { + | ^^^^^^^ error[E0277]: the trait bound `isize: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums.rs:19:5 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | a: Foo, | ^^^^^^^^^^^^^ the trait `Trait` is not implemented for `isize` @@ -26,7 +25,7 @@ error[E0277]: the trait bound `usize: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums.rs:23:10 | LL | enum Bar { - | ----------------- required by `Bar` + | ----- required by this bound in `Bar` ... LL | Quux(Bar), | ^^^^^^^^^^ the trait `Trait` is not implemented for `usize` @@ -35,37 +34,35 @@ error[E0277]: the trait bound `U: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums.rs:27:5 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | b: Foo, | ^^^^^^^^^ the trait `Trait` is not implemented for `U` | -help: consider restricting this type parameter with `U: Trait` - --> $DIR/trait-bounds-on-structs-and-enums.rs:26:16 +help: consider restricting type parameter `U` | -LL | struct Badness { - | ^ +LL | struct Badness { + | ^^^^^^^ error[E0277]: the trait bound `V: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums.rs:31:21 | LL | enum Bar { - | ----------------- required by `Bar` + | ----- required by this bound in `Bar` ... LL | EvenMoreBadness(Bar), | ^^^^^^ the trait `Trait` is not implemented for `V` | -help: consider restricting this type parameter with `V: Trait` - --> $DIR/trait-bounds-on-structs-and-enums.rs:30:18 +help: consider restricting type parameter `V` | -LL | enum MoreBadness { - | ^ +LL | enum MoreBadness { + | ^^^^^^^ error[E0277]: the trait bound `i32: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums.rs:35:5 | LL | struct Foo { - | ------------------- required by `Foo` + | ----- required by this bound in `Foo` ... LL | Foo, | ^^^^^^^^ the trait `Trait` is not implemented for `i32` @@ -74,7 +71,7 @@ error[E0277]: the trait bound `u8: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums.rs:39:22 | LL | enum Bar { - | ----------------- required by `Bar` + | ----- required by this bound in `Bar` ... LL | DictionaryLike { field: Bar }, | ^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `u8` diff --git a/src/test/ui/traits/trait-static-method-generic-inference.stderr b/src/test/ui/traits/trait-static-method-generic-inference.stderr index f9718dac3547d..8f20cc5093e11 100644 --- a/src/test/ui/traits/trait-static-method-generic-inference.stderr +++ b/src/test/ui/traits/trait-static-method-generic-inference.stderr @@ -7,7 +7,7 @@ LL | fn new() -> T; LL | let _f: base::Foo = base::HasNew::new(); | ^^^^^^^^^^^^^^^^^ cannot infer type | - = note: cannot resolve `_: base::HasNew` + = note: cannot satisfy `_: base::HasNew` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-suggest-where-clause.rs b/src/test/ui/traits/trait-suggest-where-clause.rs index 5b34ed0919001..8405e5ff62e8e 100644 --- a/src/test/ui/traits/trait-suggest-where-clause.rs +++ b/src/test/ui/traits/trait-suggest-where-clause.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl use std::mem; struct Misc(T); diff --git a/src/test/ui/traits/trait-suggest-where-clause.stderr b/src/test/ui/traits/trait-suggest-where-clause.stderr index 9680d58b8c0c7..4dddcd68f26c9 100644 --- a/src/test/ui/traits/trait-suggest-where-clause.stderr +++ b/src/test/ui/traits/trait-suggest-where-clause.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `U` cannot be known at compilation time - --> $DIR/trait-suggest-where-clause.rs:11:20 + --> $DIR/trait-suggest-where-clause.rs:7:20 | LL | fn check() { | - this type parameter needs to be `std::marker::Sized` @@ -16,7 +16,7 @@ LL | pub const fn size_of() -> usize { = note: to learn more, visit error[E0277]: the size for values of type `U` cannot be known at compilation time - --> $DIR/trait-suggest-where-clause.rs:14:5 + --> $DIR/trait-suggest-where-clause.rs:10:5 | LL | fn check() { | - this type parameter needs to be `std::marker::Sized` @@ -34,7 +34,7 @@ LL | pub const fn size_of() -> usize { = note: required because it appears within the type `Misc` error[E0277]: the trait bound `u64: std::convert::From` is not satisfied - --> $DIR/trait-suggest-where-clause.rs:19:5 + --> $DIR/trait-suggest-where-clause.rs:15:5 | LL | >::from; | ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `u64` @@ -42,7 +42,7 @@ LL | >::from; = note: required by `std::convert::From::from` error[E0277]: the trait bound `u64: std::convert::From<::Item>` is not satisfied - --> $DIR/trait-suggest-where-clause.rs:22:5 + --> $DIR/trait-suggest-where-clause.rs:18:5 | LL | ::Item>>::from; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<::Item>` is not implemented for `u64` @@ -50,7 +50,7 @@ LL | ::Item>>::from; = note: required by `std::convert::From::from` error[E0277]: the trait bound `Misc<_>: std::convert::From` is not satisfied - --> $DIR/trait-suggest-where-clause.rs:27:5 + --> $DIR/trait-suggest-where-clause.rs:23:5 | LL | as From>::from; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `Misc<_>` @@ -58,7 +58,7 @@ LL | as From>::from; = note: required by `std::convert::From::from` error[E0277]: the size for values of type `[T]` cannot be known at compilation time - --> $DIR/trait-suggest-where-clause.rs:32:20 + --> $DIR/trait-suggest-where-clause.rs:28:20 | LL | mem::size_of::<[T]>(); | ^^^ doesn't have a size known at compile-time @@ -72,7 +72,7 @@ LL | pub const fn size_of() -> usize { = note: to learn more, visit error[E0277]: the size for values of type `[&U]` cannot be known at compilation time - --> $DIR/trait-suggest-where-clause.rs:35:5 + --> $DIR/trait-suggest-where-clause.rs:31:5 | LL | mem::size_of::<[&U]>(); | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.rs b/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.rs index 47d7075ac3576..579ce7cf70669 100644 --- a/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.rs +++ b/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.rs @@ -8,8 +8,8 @@ pub trait Foo: Iterator::Key> { type Key; } -impl Foo for IntoIter { //~ ERROR type mismatch - type Key = u32; +impl Foo for IntoIter { + type Key = u32; //~ ERROR type mismatch } fn main() { diff --git a/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr b/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr index 5d0fb6b44cb29..604763f8e354e 100644 --- a/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr +++ b/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr @@ -1,8 +1,8 @@ error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == u32` - --> $DIR/traits-assoc-type-in-supertrait-bad.rs:11:6 + --> $DIR/traits-assoc-type-in-supertrait-bad.rs:12:16 | -LL | impl Foo for IntoIter { - | ^^^ expected `i32`, found `u32` +LL | type Key = u32; + | ^^^ expected `i32`, found `u32` error: aborting due to previous error diff --git a/src/test/ui/traits/traits-inductive-overflow-simultaneous.stderr b/src/test/ui/traits/traits-inductive-overflow-simultaneous.stderr index 6fd6a37b22dfe..a0d2d13fbf3d1 100644 --- a/src/test/ui/traits/traits-inductive-overflow-simultaneous.stderr +++ b/src/test/ui/traits/traits-inductive-overflow-simultaneous.stderr @@ -2,7 +2,7 @@ error[E0275]: overflow evaluating the requirement `{integer}: Tweedledum` --> $DIR/traits-inductive-overflow-simultaneous.rs:18:5 | LL | fn is_ee(t: T) { - | ----- ----- required by this bound in `is_ee` + | ----- required by this bound in `is_ee` ... LL | is_ee(4); | ^^^^^ diff --git a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.rs b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.rs index c65242b1bb1c3..571f934fc5bc7 100644 --- a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.rs +++ b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.rs @@ -3,6 +3,7 @@ // to be synthesized. #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait Magic: Copy {} //~ ERROR E0568 diff --git a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr index a83ff3701511d..b97197285ed0c 100644 --- a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr +++ b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr @@ -1,5 +1,5 @@ error[E0568]: auto traits cannot have super traits - --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:7:19 + --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:8:19 | LL | auto trait Magic: Copy {} | ----- ^^^^ help: remove the super traits @@ -7,10 +7,10 @@ LL | auto trait Magic: Copy {} | auto trait cannot have super traits error[E0277]: the trait bound `NoClone: std::marker::Copy` is not satisfied - --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:15:23 + --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:16:23 | LL | fn copy(x: T) -> (T, T) { (x, x) } - | ---- ----- required by this bound in `copy` + | ----- required by this bound in `copy` ... LL | let (a, b) = copy(NoClone); | ^^^^^^^ the trait `std::marker::Copy` is not implemented for `NoClone` diff --git a/src/test/ui/traits/traits-inductive-overflow-supertrait.stderr b/src/test/ui/traits/traits-inductive-overflow-supertrait.stderr index 96a9343d4ebfb..a86648d9a1749 100644 --- a/src/test/ui/traits/traits-inductive-overflow-supertrait.stderr +++ b/src/test/ui/traits/traits-inductive-overflow-supertrait.stderr @@ -2,7 +2,7 @@ error[E0275]: overflow evaluating the requirement `NoClone: Magic` --> $DIR/traits-inductive-overflow-supertrait.rs:13:18 | LL | fn copy(x: T) -> (T, T) { (x, x) } - | ---- ----- required by this bound in `copy` + | ----- required by this bound in `copy` ... LL | let (a, b) = copy(NoClone); | ^^^^ diff --git a/src/test/ui/traits/traits-inductive-overflow-two-traits.stderr b/src/test/ui/traits/traits-inductive-overflow-two-traits.stderr index 447fc56434831..f66cfce55c903 100644 --- a/src/test/ui/traits/traits-inductive-overflow-two-traits.stderr +++ b/src/test/ui/traits/traits-inductive-overflow-two-traits.stderr @@ -2,7 +2,7 @@ error[E0275]: overflow evaluating the requirement `*mut (): Magic` --> $DIR/traits-inductive-overflow-two-traits.rs:19:5 | LL | fn wizard() { check::<::X>(); } - | ------ ----- required by this bound in `wizard` + | ----- required by this bound in `wizard` ... LL | wizard::<*mut ()>(); | ^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/traits/traits-negative-impls-rpass.rs b/src/test/ui/traits/traits-negative-impls-rpass.rs deleted file mode 100644 index 8664b6a6a6e36..0000000000000 --- a/src/test/ui/traits/traits-negative-impls-rpass.rs +++ /dev/null @@ -1,21 +0,0 @@ -// run-pass -#![allow(unused_variables)] -#![feature(optin_builtin_traits)] - -use std::marker::Send; - -pub struct WaitToken; -impl !Send for WaitToken {} - -pub struct Test(T); -unsafe impl Send for Test {} - -pub fn spawn(_: F) -> () where F: FnOnce(), F: Send + 'static {} - -fn main() { - let wt = Test(WaitToken); - spawn(move || { - let x = wt; - println!("Hello, World!"); - }); -} diff --git a/src/test/ui/traits/traits-negative-impls.rs b/src/test/ui/traits/traits-negative-impls.rs deleted file mode 100644 index fb9a3a99748d0..0000000000000 --- a/src/test/ui/traits/traits-negative-impls.rs +++ /dev/null @@ -1,68 +0,0 @@ -// The dummy functions are used to avoid adding new cfail files. -// What happens is that the compiler attempts to squash duplicates and some -// errors are not reported. This way, we make sure that, for each function, different -// typeck phases are involved and all errors are reported. - -#![feature(optin_builtin_traits)] - -use std::marker::Send; - -struct Outer(T); - -struct Outer2(T); - -unsafe impl Sync for Outer2 {} - -fn is_send(_: T) {} -fn is_sync(_: T) {} - -fn dummy() { - struct TestType; - impl !Send for TestType {} - - Outer(TestType); - //~^ ERROR `dummy::TestType` cannot be sent between threads safely - //~| ERROR `dummy::TestType` cannot be sent between threads safely -} - -fn dummy1b() { - struct TestType; - impl !Send for TestType {} - - is_send(TestType); - //~^ ERROR `dummy1b::TestType` cannot be sent between threads safely -} - -fn dummy1c() { - struct TestType; - impl !Send for TestType {} - - is_send((8, TestType)); - //~^ ERROR `dummy1c::TestType` cannot be sent between threads safely -} - -fn dummy2() { - struct TestType; - impl !Send for TestType {} - - is_send(Box::new(TestType)); - //~^ ERROR `dummy2::TestType` cannot be sent between threads safely -} - -fn dummy3() { - struct TestType; - impl !Send for TestType {} - - is_send(Box::new(Outer2(TestType))); - //~^ ERROR `dummy3::TestType` cannot be sent between threads safely -} - -fn main() { - struct TestType; - impl !Send for TestType {} - - // This will complain about a missing Send impl because `Sync` is implement *just* - // for T that are `Send`. Look at #20366 and #19950 - is_sync(Outer2(TestType)); - //~^ ERROR `main::TestType` cannot be sent between threads safely -} diff --git a/src/test/ui/traits/traits-negative-impls.stderr b/src/test/ui/traits/traits-negative-impls.stderr deleted file mode 100644 index 599bbfe222546..0000000000000 --- a/src/test/ui/traits/traits-negative-impls.stderr +++ /dev/null @@ -1,93 +0,0 @@ -error[E0277]: `dummy::TestType` cannot be sent between threads safely - --> $DIR/traits-negative-impls.rs:23:11 - | -LL | struct Outer(T); - | ------------------------- required by `Outer` -... -LL | Outer(TestType); - | ^^^^^^^^ `dummy::TestType` cannot be sent between threads safely - | - = help: the trait `std::marker::Send` is not implemented for `dummy::TestType` - -error[E0277]: `dummy::TestType` cannot be sent between threads safely - --> $DIR/traits-negative-impls.rs:23:5 - | -LL | struct Outer(T); - | ------------------------- required by `Outer` -... -LL | Outer(TestType); - | ^^^^^^^^^^^^^^^ `dummy::TestType` cannot be sent between threads safely - | - = help: the trait `std::marker::Send` is not implemented for `dummy::TestType` - -error[E0277]: `dummy1b::TestType` cannot be sent between threads safely - --> $DIR/traits-negative-impls.rs:32:13 - | -LL | fn is_send(_: T) {} - | ------- ---- required by this bound in `is_send` -... -LL | is_send(TestType); - | ^^^^^^^^ `dummy1b::TestType` cannot be sent between threads safely - | - = help: the trait `std::marker::Send` is not implemented for `dummy1b::TestType` - -error[E0277]: `dummy1c::TestType` cannot be sent between threads safely - --> $DIR/traits-negative-impls.rs:40:13 - | -LL | fn is_send(_: T) {} - | ------- ---- required by this bound in `is_send` -... -LL | is_send((8, TestType)); - | ^^^^^^^^^^^^^ `dummy1c::TestType` cannot be sent between threads safely - | - = help: within `({integer}, dummy1c::TestType)`, the trait `std::marker::Send` is not implemented for `dummy1c::TestType` - = note: required because it appears within the type `({integer}, dummy1c::TestType)` - -error[E0277]: `dummy2::TestType` cannot be sent between threads safely - --> $DIR/traits-negative-impls.rs:48:13 - | -LL | fn is_send(_: T) {} - | ------- ---- required by this bound in `is_send` -... -LL | is_send(Box::new(TestType)); - | ^^^^^^^^^^^^^^^^^^ - | | - | expected an implementor of trait `std::marker::Send` - | help: consider borrowing here: `&Box::new(TestType)` - | - = note: the trait bound `dummy2::TestType: std::marker::Send` is not satisfied - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique` - = note: required because it appears within the type `std::boxed::Box` - -error[E0277]: `dummy3::TestType` cannot be sent between threads safely - --> $DIR/traits-negative-impls.rs:56:13 - | -LL | fn is_send(_: T) {} - | ------- ---- required by this bound in `is_send` -... -LL | is_send(Box::new(Outer2(TestType))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `dummy3::TestType` cannot be sent between threads safely - | - = help: within `Outer2`, the trait `std::marker::Send` is not implemented for `dummy3::TestType` - = note: required because it appears within the type `Outer2` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique>` - = note: required because it appears within the type `std::boxed::Box>` - -error[E0277]: `main::TestType` cannot be sent between threads safely - --> $DIR/traits-negative-impls.rs:66:13 - | -LL | fn is_sync(_: T) {} - | ------- ---- required by this bound in `is_sync` -... -LL | is_sync(Outer2(TestType)); - | ^^^^^^^^^^^^^^^^ - | | - | expected an implementor of trait `std::marker::Sync` - | help: consider borrowing here: `&Outer2(TestType)` - | - = note: the trait bound `main::TestType: std::marker::Sync` is not satisfied - = note: required because of the requirements on the impl of `std::marker::Sync` for `Outer2` - -error: aborting due to 7 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/traits-repeated-supertrait-ambig.stderr b/src/test/ui/traits/traits-repeated-supertrait-ambig.stderr index 5b7f32ba1e0f0..4107c49bd80ce 100644 --- a/src/test/ui/traits/traits-repeated-supertrait-ambig.stderr +++ b/src/test/ui/traits/traits-repeated-supertrait-ambig.stderr @@ -10,11 +10,10 @@ error[E0277]: the trait bound `C: CompareTo` is not satisfied LL | c.same_as(22) | ^^^^^^^ the trait `CompareTo` is not implemented for `C` | -help: consider further restricting this bound with `+ CompareTo` - --> $DIR/traits-repeated-supertrait-ambig.rs:29:17 +help: consider further restricting this bound | -LL | fn with_trait(c: &C) -> bool { - | ^^^^^^^^^^^^^ +LL | fn with_trait>(c: &C) -> bool { + | ^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `dyn CompareToInts: CompareTo` is not satisfied --> $DIR/traits-repeated-supertrait-ambig.rs:34:5 @@ -34,11 +33,10 @@ LL | fn same_as(&self, t: T) -> bool; LL | CompareTo::same_as(c, 22) | ^^^^^^^^^^^^^^^^^^ the trait `CompareTo` is not implemented for `C` | -help: consider further restricting this bound with `+ CompareTo` - --> $DIR/traits-repeated-supertrait-ambig.rs:37:17 +help: consider further restricting this bound | -LL | fn with_ufcs2(c: &C) -> bool { - | ^^^^^^^^^^^^^ +LL | fn with_ufcs2>(c: &C) -> bool { + | ^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `i64: CompareTo` is not satisfied --> $DIR/traits-repeated-supertrait-ambig.rs:42:23 diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr index 17389a8731332..39f7fb148f041 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr @@ -24,3 +24,5 @@ warning: Trait bound for<'b> &'b mut i32: std::marker::Copy does not depend on a LL | fn copy_mut<'a>(t: &&'a mut i32) -> &'a mut i32 where for<'b> &'b mut i32: Copy { | ^^^^ +warning: 4 warnings emitted + diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-projection.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-projection.stderr index dc685cbf6b37e..e7835814cb83e 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-projection.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-projection.stderr @@ -42,3 +42,5 @@ warning: Trait bound B: A does not depend on any type or lifetime parameters LL | B: A + A | ^ +warning: 7 warnings emitted + diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr index d4fa698c7375f..aa5d4fcc724c2 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr @@ -18,3 +18,5 @@ warning: Trait bound str: std::marker::Sized does not depend on any type or life LL | fn return_str() -> str where str: Sized { | ^^^^^ +warning: 3 warnings emitted + diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr index fdc3ff1d3b59b..ffcfbdf54a7ab 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr @@ -12,3 +12,5 @@ warning: Trait bound str: std::marker::Copy does not depend on any type or lifet LL | pub fn foo() where Vec: Debug, str: Copy { | ^^^^ +warning: 2 warnings emitted + diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr index 156d38e3df58d..d863cf6249142 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr @@ -90,3 +90,5 @@ warning: Trait bound i32: std::iter::Iterator does not depend on any type or lif LL | fn use_for() where i32: Iterator { | ^^^^^^^^ +warning: 14 warnings emitted + diff --git a/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr b/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr index 4e153081d9fe9..006fa933d34ca 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr @@ -37,7 +37,7 @@ LL | generic_function(5i32); | ^^^^ the trait `Foo` is not implemented for `i32` ... LL | fn generic_function(t: T) {} - | ---------------- --- required by this bound in `generic_function` + | --- required by this bound in `generic_function` error: aborting due to 4 previous errors diff --git a/src/test/ui/try-block/try-block-in-match.rs b/src/test/ui/try-block/try-block-in-match.rs index bce0d0340b658..cd0b967e79d07 100644 --- a/src/test/ui/try-block/try-block-in-match.rs +++ b/src/test/ui/try-block/try-block-in-match.rs @@ -1,7 +1,11 @@ +// run-pass // compile-flags: --edition 2018 #![feature(try_blocks)] fn main() { - match try { false } { _ => {} } //~ ERROR expected expression, found reserved keyword `try` + match try { } { + Err(()) => (), + Ok(()) => (), + } } diff --git a/src/test/ui/try-block/try-block-in-match.stderr b/src/test/ui/try-block/try-block-in-match.stderr deleted file mode 100644 index 936e0fe19bafe..0000000000000 --- a/src/test/ui/try-block/try-block-in-match.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: expected expression, found reserved keyword `try` - --> $DIR/try-block-in-match.rs:6:11 - | -LL | match try { false } { _ => {} } - | ----- ^^^ expected expression - | | - | while parsing this match expression - -error: aborting due to previous error - diff --git a/src/test/ui/try-block/try-block-in-while.rs b/src/test/ui/try-block/try-block-in-while.rs index 98af796dd3780..33d2723651929 100644 --- a/src/test/ui/try-block/try-block-in-while.rs +++ b/src/test/ui/try-block/try-block-in-while.rs @@ -3,5 +3,6 @@ #![feature(try_blocks)] fn main() { - while try { false } {} //~ ERROR expected expression, found reserved keyword `try` + while try { false } {} + //~^ ERROR the trait bound `bool: std::ops::Try` is not satisfied } diff --git a/src/test/ui/try-block/try-block-in-while.stderr b/src/test/ui/try-block/try-block-in-while.stderr index 026df15eb877a..ac41ddfd8c042 100644 --- a/src/test/ui/try-block/try-block-in-while.stderr +++ b/src/test/ui/try-block/try-block-in-while.stderr @@ -1,8 +1,11 @@ -error: expected expression, found reserved keyword `try` - --> $DIR/try-block-in-while.rs:6:11 +error[E0277]: the trait bound `bool: std::ops::Try` is not satisfied + --> $DIR/try-block-in-while.rs:6:15 | LL | while try { false } {} - | ^^^ expected expression + | ^^^^^^^^^ the trait `std::ops::Try` is not implemented for `bool` + | + = note: required by `std::ops::Try::from_ok` error: aborting due to previous error +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/try-block/try-block-unreachable-code-lint.stderr b/src/test/ui/try-block/try-block-unreachable-code-lint.stderr index c883994c876f8..61df702fb8706 100644 --- a/src/test/ui/try-block/try-block-unreachable-code-lint.stderr +++ b/src/test/ui/try-block/try-block-unreachable-code-lint.stderr @@ -36,3 +36,5 @@ LL | LL | 42 | ^^ unreachable expression +warning: 3 warnings emitted + diff --git a/src/test/ui/try-block/try-block-unused-delims.rs b/src/test/ui/try-block/try-block-unused-delims.rs new file mode 100644 index 0000000000000..0b767eb2dad77 --- /dev/null +++ b/src/test/ui/try-block/try-block-unused-delims.rs @@ -0,0 +1,28 @@ +// check-pass +// compile-flags: --edition 2018 + +#![feature(try_blocks)] +#![warn(unused_parens, unused_braces)] + +fn consume(_: Result) -> T { todo!() } + +fn main() { + consume((try {})); + //~^ WARN unnecessary parentheses + + consume({ try {} }); + //~^ WARN unnecessary braces + + match (try {}) { + //~^ WARN unnecessary parentheses + Ok(()) | Err(()) => (), + } + + if let Err(()) = (try {}) {} + //~^ WARN unnecessary parentheses + + match (try {}) { + //~^ WARN unnecessary parentheses + Ok(()) | Err(()) => (), + } +} diff --git a/src/test/ui/try-block/try-block-unused-delims.stderr b/src/test/ui/try-block/try-block-unused-delims.stderr new file mode 100644 index 0000000000000..5c7602ee0ab12 --- /dev/null +++ b/src/test/ui/try-block/try-block-unused-delims.stderr @@ -0,0 +1,44 @@ +warning: unnecessary parentheses around function argument + --> $DIR/try-block-unused-delims.rs:10:13 + | +LL | consume((try {})); + | ^^^^^^^^ help: remove these parentheses + | +note: the lint level is defined here + --> $DIR/try-block-unused-delims.rs:5:9 + | +LL | #![warn(unused_parens, unused_braces)] + | ^^^^^^^^^^^^^ + +warning: unnecessary braces around function argument + --> $DIR/try-block-unused-delims.rs:13:13 + | +LL | consume({ try {} }); + | ^^^^^^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/try-block-unused-delims.rs:5:24 + | +LL | #![warn(unused_parens, unused_braces)] + | ^^^^^^^^^^^^^ + +warning: unnecessary parentheses around `match` scrutinee expression + --> $DIR/try-block-unused-delims.rs:16:11 + | +LL | match (try {}) { + | ^^^^^^^^ help: remove these parentheses + +warning: unnecessary parentheses around `let` scrutinee expression + --> $DIR/try-block-unused-delims.rs:21:22 + | +LL | if let Err(()) = (try {}) {} + | ^^^^^^^^ help: remove these parentheses + +warning: unnecessary parentheses around `match` scrutinee expression + --> $DIR/try-block-unused-delims.rs:24:11 + | +LL | match (try {}) { + | ^^^^^^^^ help: remove these parentheses + +warning: 5 warnings emitted + diff --git a/src/test/ui/try-macro-suggestion.rs b/src/test/ui/try-macro-suggestion.rs new file mode 100644 index 0000000000000..635ceac0b199e --- /dev/null +++ b/src/test/ui/try-macro-suggestion.rs @@ -0,0 +1,9 @@ +// compile-flags: --edition 2018 +fn foo() -> Result<(), ()> { + Ok(try!()); //~ ERROR use of deprecated `try` macro + Ok(try!(Ok(()))) //~ ERROR use of deprecated `try` macro +} + +fn main() { + let _ = foo(); +} diff --git a/src/test/ui/try-macro-suggestion.stderr b/src/test/ui/try-macro-suggestion.stderr new file mode 100644 index 0000000000000..9d833ef5ed9fb --- /dev/null +++ b/src/test/ui/try-macro-suggestion.stderr @@ -0,0 +1,30 @@ +error: use of deprecated `try` macro + --> $DIR/try-macro-suggestion.rs:3:8 + | +LL | Ok(try!()); + | ^^^^^^ + | + = note: in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated +help: you can still access the deprecated `try!()` macro using the "raw identifier" syntax + | +LL | Ok(r#try!()); + | ^^ + +error: use of deprecated `try` macro + --> $DIR/try-macro-suggestion.rs:4:8 + | +LL | Ok(try!(Ok(()))) + | ^^^^^^^^^^^^ + | + = note: in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated +help: you can use the `?` operator instead + | +LL | Ok(Ok(())?) + | -- ^ +help: alternatively, you can still access the deprecated `try!()` macro using the "raw identifier" syntax + | +LL | Ok(r#try!(Ok(()))) + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/try-on-option.stderr b/src/test/ui/try-on-option.stderr index 07615b52a48a5..33ca58bf7feb1 100644 --- a/src/test/ui/try-on-option.stderr +++ b/src/test/ui/try-on-option.stderr @@ -1,11 +1,18 @@ error[E0277]: `?` couldn't convert the error to `()` --> $DIR/try-on-option.rs:7:6 | +LL | fn foo() -> Result { + | --------------- expected `()` because of this +LL | let x: Option = None; LL | x?; | ^ the trait `std::convert::From` is not implemented for `()` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = note: required by `std::convert::From::from` +help: consider converting the `Option` into a `Result` using `Option::ok_or` or `Option::ok_or_else` + | +LL | x.ok_or_else(|| /* error value */)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-on-option.rs:13:5 diff --git a/src/test/ui/try-operator-on-main.stderr b/src/test/ui/try-operator-on-main.stderr index d8ba264583e45..ecad5a7d11ab6 100644 --- a/src/test/ui/try-operator-on-main.stderr +++ b/src/test/ui/try-operator-on-main.stderr @@ -30,7 +30,7 @@ LL | try_trait_generic::<()>(); | ^^ the trait `std::ops::Try` is not implemented for `()` ... LL | fn try_trait_generic() -> T { - | ----------------- --- required by this bound in `try_trait_generic` + | --- required by this bound in `try_trait_generic` error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` --> $DIR/try-operator-on-main.rs:22:5 diff --git a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr index 412b4dbda4fde..caea791e6536b 100644 --- a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr +++ b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr @@ -9,8 +9,6 @@ LL | Self::TSVariant(()); | = note: expected type parameter `T` found unit type `()` - = 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[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:15:27 @@ -35,8 +33,6 @@ LL | Self::<()>::TSVariant(()); | = note: expected type parameter `T` found unit type `()` - = 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[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:20:16 @@ -61,8 +57,6 @@ LL | Self::SVariant { v: () }; | = note: expected type parameter `T` found unit type `()` - = 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[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:28:26 @@ -81,8 +75,6 @@ LL | Self::SVariant::<()> { v: () }; | = note: expected type parameter `T` found unit type `()` - = 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[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:31:16 @@ -101,8 +93,6 @@ LL | Self::<()>::SVariant { v: () }; | = note: expected type parameter `T` found unit type `()` - = 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[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:34:16 @@ -127,8 +117,6 @@ LL | Self::<()>::SVariant::<()> { v: () }; | = note: expected type parameter `T` found unit type `()` - = 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[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:41:26 diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.rs b/src/test/ui/type-alias-impl-trait/assoc-type-const.rs index 2907c21c6203c..d53f562e99f4b 100644 --- a/src/test/ui/type-alias-impl-trait/assoc-type-const.rs +++ b/src/test/ui/type-alias-impl-trait/assoc-type-const.rs @@ -4,7 +4,7 @@ #![feature(type_alias_impl_trait)] #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +//~^ WARN the feature `const_generics` is incomplete trait UnwrapItemsExt<'a, const C: usize> { type Iter; @@ -18,16 +18,16 @@ trait MyTrait<'a, const C: usize> { const MY_CONST: usize; } -impl<'a, const C: usize> MyTrait<'a, { C }> for MyStruct<{ C }> { +impl<'a, const C: usize> MyTrait<'a, C> for MyStruct { type MyItem = u8; const MY_CONST: usize = C; } -impl<'a, I, const C: usize> UnwrapItemsExt<'a, { C }> for I { - type Iter = impl MyTrait<'a, { C }>; +impl<'a, I, const C: usize> UnwrapItemsExt<'a, C> for I { + type Iter = impl MyTrait<'a, C>; fn unwrap_items(self) -> Self::Iter { - MyStruct::<{ C }> {} + MyStruct:: {} } } diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr index 0adbee2f24439..e0c1b02386127 100644 --- a/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr +++ b/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr @@ -1,8 +1,11 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/assoc-type-const.rs:6:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction2.rs b/src/test/ui/type-alias-impl-trait/bound_reduction2.rs index 1becb1e83a5ac..0a4cc9b7fe8be 100644 --- a/src/test/ui/type-alias-impl-trait/bound_reduction2.rs +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.rs @@ -8,13 +8,12 @@ trait TraitWithAssoc { } type Foo = impl Trait; -//~^ ERROR could not find defining uses -//~| ERROR the trait bound `T: TraitWithAssoc` is not satisfied +//~^ ERROR the trait bound `T: TraitWithAssoc` is not satisfied trait Trait {} impl Trait for () {} -fn foo_desugared(_: T) -> Foo { //~ ERROR does not fully define +fn foo_desugared(_: T) -> Foo { () } diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr b/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr index 74b858105b92f..b871f79aa1dc5 100644 --- a/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr @@ -4,26 +4,11 @@ error[E0277]: the trait bound `T: TraitWithAssoc` is not satisfied LL | type Foo = impl Trait; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TraitWithAssoc` is not implemented for `T` | -help: consider further restricting this bound with `+ TraitWithAssoc` - --> $DIR/bound_reduction2.rs:18:21 +help: consider further restricting this bound | -LL | fn foo_desugared(_: T) -> Foo { - | ^^^^^^^^^^^^^^ +LL | fn foo_desugared(_: T) -> Foo { + | ^^^^^^^^^^^^^^^^ -error: defining opaque type use does not fully define opaque type: generic parameter `V` is specified as concrete type `::Assoc` - --> $DIR/bound_reduction2.rs:18:1 - | -LL | / fn foo_desugared(_: T) -> Foo { -LL | | () -LL | | } - | |_^ - -error: could not find defining uses - --> $DIR/bound_reduction2.rs:10:1 - | -LL | type Foo = impl Trait; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs index 165e320be5e9a..4503607a83638 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs @@ -1,14 +1,26 @@ -#![feature(type_alias_impl_trait)] +#![feature(type_alias_impl_trait, const_generics)] +#![allow(incomplete_features)] use std::fmt::Debug; fn main() {} // test that unused generic parameters are ok -type Two = impl Debug; -//~^ could not find defining uses +type TwoTys = impl Debug; +type TwoLifetimes<'a, 'b> = impl Debug; +type TwoConsts = impl Debug; -fn one(t: T) -> Two { -//~^ ERROR defining opaque type use restricts opaque type +fn one_ty(t: T) -> TwoTys { +//~^ ERROR non-defining opaque type use in defining scope + t +} + +fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> { +//~^ ERROR non-defining opaque type use in defining scope + t +} + +fn one_const(t: *mut [u8; N]) -> TwoConsts { +//~^ ERROR non-defining opaque type use in defining scope t } diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr index e1794034e20dd..b4757e2763d06 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr @@ -1,17 +1,38 @@ -error: defining opaque type use restricts opaque type by using the generic parameter `T` twice - --> $DIR/generic_duplicate_param_use.rs:11:1 - | -LL | / fn one(t: T) -> Two { -LL | | -LL | | t -LL | | } - | |_^ +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:13:30 + | +LL | fn one_ty(t: T) -> TwoTys { + | ^^^^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use.rs:9:13 + | +LL | type TwoTys = impl Debug; + | ^ ^ -error: could not find defining uses - --> $DIR/generic_duplicate_param_use.rs:8:1 +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:18:36 + | +LL | fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: lifetime used multiple times + --> $DIR/generic_duplicate_param_use.rs:10:19 + | +LL | type TwoLifetimes<'a, 'b> = impl Debug; + | ^^ ^^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:23:50 + | +LL | fn one_const(t: *mut [u8; N]) -> TwoConsts { + | ^^^^^^^^^^^^^^^ + | +note: constant used multiple times + --> $DIR/generic_duplicate_param_use.rs:11:22 | -LL | type Two = impl Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type TwoConsts = impl Debug; + | ^ ^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs index 0adce817c5c37..2b98d8fc63a11 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs @@ -8,10 +8,10 @@ fn main() {} type Two = impl Debug; fn one(t: T) -> Two { -//~^ defining opaque type use restricts opaque type t } fn two(t: T, _: U) -> Two { +//~^ ERROR concrete type differs from previous defining opaque type use t } diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr index a9a51fa0b4bad..8170c671f68cd 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr @@ -1,8 +1,16 @@ -error: defining opaque type use restricts opaque type by using the generic parameter `T` twice +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use2.rs:14:1 + | +LL | / fn two(t: T, _: U) -> Two { +LL | | +LL | | t +LL | | } + | |_^ expected `U`, got `T` + | +note: previous use here --> $DIR/generic_duplicate_param_use2.rs:10:1 | LL | / fn one(t: T) -> Two { -LL | | LL | | t LL | | } | |_^ diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs index 8d3e7f9f42497..d9133fd11f7cd 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs @@ -8,15 +8,14 @@ fn main() {} type Two = impl Debug; fn one(t: T) -> Two { -//~^ defining opaque type use restricts opaque type t } fn two(t: T, _: U) -> Two { +//~^ ERROR concrete type differs from previous defining opaque type use t } fn three(_: T, u: U) -> Two { -//~^ concrete type's generic parameters differ from previous defining use u } diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr index 04dcdc295f9cb..86dd33684005b 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr @@ -1,28 +1,19 @@ -error: defining opaque type use restricts opaque type by using the generic parameter `T` twice - --> $DIR/generic_duplicate_param_use3.rs:10:1 +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use3.rs:14:1 | -LL | / fn one(t: T) -> Two { +LL | / fn two(t: T, _: U) -> Two { LL | | LL | | t LL | | } - | |_^ - -error: concrete type's generic parameters differ from previous defining use - --> $DIR/generic_duplicate_param_use3.rs:19:1 - | -LL | / fn three(_: T, u: U) -> Two { -LL | | -LL | | u -LL | | } - | |_^ expected [`T`], got [`U`] + | |_^ expected `U`, got `T` | note: previous use here - --> $DIR/generic_duplicate_param_use3.rs:15:1 + --> $DIR/generic_duplicate_param_use3.rs:10:1 | -LL | / fn two(t: T, _: U) -> Two { +LL | / fn one(t: T) -> Two { LL | | t LL | | } | |_^ -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs index 65f7d7f485d49..40388c3b6c88a 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs @@ -8,7 +8,7 @@ fn main() {} type Two = impl Debug; fn one(t: T) -> Two { -//~^ ERROR defining opaque type use restricts opaque type +//~^ ERROR non-defining opaque type use in defining scope t } diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr index 082177b82128d..fcf01f5164ae4 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr @@ -1,11 +1,14 @@ -error: defining opaque type use restricts opaque type by using the generic parameter `T` twice - --> $DIR/generic_duplicate_param_use4.rs:10:1 +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use4.rs:10:27 | -LL | / fn one(t: T) -> Two { -LL | | -LL | | t -LL | | } - | |_^ +LL | fn one(t: T) -> Two { + | ^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use4.rs:8:10 + | +LL | type Two = impl Debug; + | ^ ^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs index 60106eba1756e..b1782120f84cc 100644 --- a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs +++ b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs @@ -1,13 +1,27 @@ -#![feature(type_alias_impl_trait)] +#![feature(type_alias_impl_trait, const_generics)] +#![allow(incomplete_features)] + +use std::fmt::Debug; fn main() {} -type Cmp = impl 'static; -//~^ ERROR could not find defining uses -//~^^ ERROR: at least one trait must be specified +type OneTy = impl Debug; +type OneLifetime<'a> = impl Debug; +type OneConst = impl Debug; +// Not defining uses, because they doesn't define *all* possible generics. -// not a defining use, because it doesn't define *all* possible generics -fn cmp() -> Cmp { //~ ERROR defining opaque type use does not fully define +fn concrete_ty() -> OneTy { +//~^ ERROR non-defining opaque type use in defining scope 5u32 } + +fn concrete_lifetime() -> OneLifetime<'static> { +//~^ ERROR non-defining opaque type use in defining scope + 6u32 +} + +fn concrete_const() -> OneConst<{123}> { +//~^ ERROR non-defining opaque type use in defining scope + 7u32 +} diff --git a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr index b952aaa79ccee..88f8dbe1a7d72 100644 --- a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr @@ -1,22 +1,35 @@ -error: at least one trait must be specified - --> $DIR/generic_nondefining_use.rs:5:15 +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:14:21 | -LL | type Cmp = impl 'static; - | ^^^^^^^^^^^^ +LL | fn concrete_ty() -> OneTy { + | ^^^^^^^^^^ + | +note: used non-generic type `u32` for generic parameter + --> $DIR/generic_nondefining_use.rs:8:12 + | +LL | type OneTy = impl Debug; + | ^ -error: defining opaque type use does not fully define opaque type: generic parameter `T` is specified as concrete type `u32` - --> $DIR/generic_nondefining_use.rs:11:1 +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:19:27 | -LL | / fn cmp() -> Cmp { -LL | | 5u32 -LL | | } - | |_^ +LL | type OneLifetime<'a> = impl Debug; + | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type +... +LL | fn concrete_lifetime() -> OneLifetime<'static> { + | ^^^^^^^^^^^^^^^^^^^^ -error: could not find defining uses - --> $DIR/generic_nondefining_use.rs:5:1 +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:24:24 + | +LL | fn concrete_const() -> OneConst<{123}> { + | ^^^^^^^^^^^^^^^ + | +note: used non-generic constant `{123}` for generic parameter + --> $DIR/generic_nondefining_use.rs:10:21 | -LL | type Cmp = impl 'static; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type OneConst = impl Debug; + | ^ error: aborting due to 3 previous errors diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr index 299c7eae8d3a8..f7a04263259f6 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr @@ -10,12 +10,11 @@ error[E0277]: the trait bound `T: Trait` is not satisfied LL | type Underconstrained = impl 'static; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` | -help: consider restricting this type parameter with `T: Trait` - --> $DIR/generic_underconstrained.rs:10:19 - | -LL | fn underconstrain(_: T) -> Underconstrained { - | ^ = note: the return type of a function must have a statically known size +help: consider restricting type parameter `T` + | +LL | fn underconstrain(_: T) -> Underconstrained { + | ^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr index 56966a32b43b2..ad160abcbd573 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -20,12 +20,11 @@ LL | 5u32 | ---- this returned value is of type `u32` | = help: the trait `std::fmt::Debug` is not implemented for `U` -help: consider restricting this type parameter with `U: std::fmt::Debug` - --> $DIR/generic_underconstrained2.rs:10:21 - | -LL | fn underconstrained(_: U) -> Underconstrained { - | ^ = note: the return type of a function must have a statically known size +help: consider restricting type parameter `U` + | +LL | fn underconstrained(_: U) -> Underconstrained { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `V` doesn't implement `std::fmt::Debug` --> $DIR/generic_underconstrained2.rs:14:1 @@ -37,12 +36,11 @@ LL | 5u32 | ---- this returned value is of type `u32` | = help: the trait `std::fmt::Debug` is not implemented for `V` -help: consider restricting this type parameter with `V: std::fmt::Debug` - --> $DIR/generic_underconstrained2.rs:19:25 - | -LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { - | ^ = note: the return type of a function must have a statically known size +help: consider restricting type parameter `V` + | +LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { + | ^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.rs b/src/test/ui/type-alias-impl-trait/issue-60564.rs index 73acc92172bad..4eb7f7836d869 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60564.rs +++ b/src/test/ui/type-alias-impl-trait/issue-60564.rs @@ -6,7 +6,6 @@ trait IterBits { } type IterBitsIter = impl std::iter::Iterator; -//~^ ERROR could not find defining uses impl IterBits for T where @@ -18,7 +17,8 @@ where { type BitsIter = IterBitsIter; fn iter_bits(self, n: u8) -> Self::BitsIter { - //~^ ERROR defining opaque type use does not fully define opaque type + //~^ ERROR non-defining opaque type use in defining scope + //~| ERROR non-defining opaque type use in defining scope (0u8..n) .rev() .map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.stderr b/src/test/ui/type-alias-impl-trait/issue-60564.stderr index 9de3e759e1521..55984609437b0 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60564.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-60564.stderr @@ -1,19 +1,26 @@ -error: defining opaque type use does not fully define opaque type: generic parameter `I` is specified as concrete type `u8` - --> $DIR/issue-60564.rs:20:5 +error: non-defining opaque type use in defining scope + --> $DIR/issue-60564.rs:19:34 | -LL | / fn iter_bits(self, n: u8) -> Self::BitsIter { -LL | | -LL | | (0u8..n) -LL | | .rev() -LL | | .map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) -LL | | } - | |_____^ +LL | fn iter_bits(self, n: u8) -> Self::BitsIter { + | ^^^^^^^^^^^^^^ + | +note: used non-generic type `_` for generic parameter + --> $DIR/issue-60564.rs:8:22 + | +LL | type IterBitsIter = impl std::iter::Iterator; + | ^ -error: could not find defining uses - --> $DIR/issue-60564.rs:8:1 +error: non-defining opaque type use in defining scope + --> $DIR/issue-60564.rs:19:34 + | +LL | fn iter_bits(self, n: u8) -> Self::BitsIter { + | ^^^^^^^^^^^^^^ + | +note: used non-generic type `u8` for generic parameter + --> $DIR/issue-60564.rs:8:25 | LL | type IterBitsIter = impl std::iter::Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs index d00f8d7a90119..3b6decbe9c65e 100644 --- a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs +++ b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs @@ -4,9 +4,9 @@ #![feature(type_alias_impl_trait)] trait Trait {} -type Alias<'a, U> = impl Trait; //~ ERROR could not find defining uses +type Alias<'a, U> = impl Trait; fn f<'a>() -> Alias<'a, ()> {} -//~^ ERROR defining opaque type use does not fully define opaque type: generic parameter `U` +//~^ ERROR non-defining opaque type use in defining scope fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr index b585942406fd4..c2fa54f50f881 100644 --- a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr @@ -1,14 +1,14 @@ -error: defining opaque type use does not fully define opaque type: generic parameter `U` is specified as concrete type `()` - --> $DIR/issue-68368-non-defining-use.rs:8:1 +error: non-defining opaque type use in defining scope + --> $DIR/issue-68368-non-defining-use.rs:8:15 | LL | fn f<'a>() -> Alias<'a, ()> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/issue-68368-non-defining-use.rs:7:1 + | ^^^^^^^^^^^^^ + | +note: used non-generic type `()` for generic parameter + --> $DIR/issue-68368-non-defining-use.rs:7:16 | LL | type Alias<'a, U> = impl Trait; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs b/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs index ca00e582d3442..02485b24e7b8a 100644 --- a/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs +++ b/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs @@ -7,7 +7,6 @@ fn main() {} type Two = impl Debug; fn two(t: T) -> Two { - //~^ ERROR defining opaque type use does not fully define opaque type (t, 4i8) } diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr b/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr index d68f1bd30a0da..cce861b76c95e 100644 --- a/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr +++ b/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr @@ -1,14 +1,5 @@ -error: defining opaque type use does not fully define opaque type: generic parameter `U` is specified as concrete type `u32` - --> $DIR/not_a_defining_use.rs:9:1 - | -LL | / fn two(t: T) -> Two { -LL | | -LL | | (t, 4i8) -LL | | } - | |_^ - error: concrete type differs from previous defining opaque type use - --> $DIR/not_a_defining_use.rs:30:1 + --> $DIR/not_a_defining_use.rs:29:1 | LL | / fn four(t: T) -> Two { LL | | (t, ::FOO) @@ -16,12 +7,12 @@ LL | | } | |_^ expected `(T, i8)`, got `(T, ::Blub)` | note: previous use here - --> $DIR/not_a_defining_use.rs:14:1 + --> $DIR/not_a_defining_use.rs:9:1 | -LL | / fn three(t: T) -> Two { -LL | | (t, 5i8) +LL | / fn two(t: T) -> Two { +LL | | (t, 4i8) LL | | } | |_^ -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs index 0fd4d26ef60b7..bc2bf9eca93bd 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs @@ -6,7 +6,7 @@ // Specifically, this line requires `impl_trait_in_bindings` to be enabled: // https://github.com/rust-lang/rust/blob/481068a707679257e2a738b40987246e0420e787/src/librustc_typeck/check/mod.rs#L856 #![feature(impl_trait_in_bindings)] -//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +//~^ WARN the feature `impl_trait_in_bindings` is incomplete // Ensures that `const` items can constrain an opaque `impl Trait`. diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.stderr index 691de82c9d838..b0593d51a250c 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.stderr +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.stderr @@ -1,8 +1,11 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/type-alias-impl-trait-const.rs:8:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 for more information + +warning: 1 warning emitted diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs new file mode 100644 index 0000000000000..c54df664243e7 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs @@ -0,0 +1,17 @@ +// check-pass + +#![feature(type_alias_impl_trait)] + +type A = impl Sized; +fn f1() -> A { 0 } + +type B = impl ?Sized; +fn f2() -> &'static B { &[0] } + +type C = impl ?Sized + 'static; +fn f3() -> &'static C { &[0] } + +type D = impl ?Sized; +fn f4() -> &'static D { &1 } + +fn main() {} diff --git a/src/test/ui/type-sizes.rs b/src/test/ui/type-sizes.rs index 27433fd770b05..6a3f3c98f127a 100644 --- a/src/test/ui/type-sizes.rs +++ b/src/test/ui/type-sizes.rs @@ -37,6 +37,29 @@ enum ReorderedEnum { B(u8, u16, u8), } +enum ReorderedEnum2 { + A(u8, u32, u8), + B(u16, u8, u16, u8), + + // 0x100 niche variants. + _00, _01, _02, _03, _04, _05, _06, _07, _08, _09, _0A, _0B, _0C, _0D, _0E, _0F, + _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _1A, _1B, _1C, _1D, _1E, _1F, + _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _2A, _2B, _2C, _2D, _2E, _2F, + _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _3A, _3B, _3C, _3D, _3E, _3F, + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _4A, _4B, _4C, _4D, _4E, _4F, + _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _5A, _5B, _5C, _5D, _5E, _5F, + _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _6A, _6B, _6C, _6D, _6E, _6F, + _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _7A, _7B, _7C, _7D, _7E, _7F, + _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _8A, _8B, _8C, _8D, _8E, _8F, + _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _9A, _9B, _9C, _9D, _9E, _9F, + _A0, _A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8, _A9, _AA, _AB, _AC, _AD, _AE, _AF, + _B0, _B1, _B2, _B3, _B4, _B5, _B6, _B7, _B8, _B9, _BA, _BB, _BC, _BD, _BE, _BF, + _C0, _C1, _C2, _C3, _C4, _C5, _C6, _C7, _C8, _C9, _CA, _CB, _CC, _CD, _CE, _CF, + _D0, _D1, _D2, _D3, _D4, _D5, _D6, _D7, _D8, _D9, _DA, _DB, _DC, _DD, _DE, _DF, + _E0, _E1, _E2, _E3, _E4, _E5, _E6, _E7, _E8, _E9, _EA, _EB, _EC, _ED, _EE, _EF, + _F0, _F1, _F2, _F3, _F4, _F5, _F6, _F7, _F8, _F9, _FA, _FB, _FC, _FD, _FE, _FF, +} + enum EnumEmpty {} enum EnumSingle1 { @@ -74,6 +97,11 @@ enum NicheFilledEnumWithAbsentVariant { C, } +enum Option2 { + Some(A, B), + None +} + pub fn main() { assert_eq!(size_of::(), 1 as usize); assert_eq!(size_of::(), 4 as usize); @@ -99,6 +127,8 @@ pub fn main() { assert_eq!(size_of::(), 4 as usize); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 6); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::(), 0); assert_eq!(size_of::(), 0); @@ -113,4 +143,6 @@ pub fn main() { assert_eq!(size_of::>>(), size_of::<(bool, &())>()); assert_eq!(size_of::>>(), size_of::<(bool, &())>()); + assert_eq!(size_of::>>(), size_of::<(bool, &())>()); + assert_eq!(size_of::>>(), size_of::<(bool, &())>()); } diff --git a/src/test/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr b/src/test/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr index 37b51b50b964e..c1c76659c6745 100644 --- a/src/test/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr +++ b/src/test/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr @@ -10,3 +10,5 @@ help: the bound will not be checked when the type alias is used, and should be r LL | pub type T

= P; | -- +warning: 1 warning emitted + diff --git a/src/test/ui/type/type-alias-bounds.stderr b/src/test/ui/type/type-alias-bounds.stderr index e4d3753f8a59a..d4188b5f01a41 100644 --- a/src/test/ui/type/type-alias-bounds.stderr +++ b/src/test/ui/type/type-alias-bounds.stderr @@ -108,3 +108,5 @@ help: the bound will not be checked when the type alias is used, and should be r LL | type T6 = ::std::vec::Vec; | -- +warning: 9 warnings emitted + diff --git a/src/test/ui/type/type-annotation-needed.rs b/src/test/ui/type/type-annotation-needed.rs index a420515be496d..553318ecac6e2 100644 --- a/src/test/ui/type/type-annotation-needed.rs +++ b/src/test/ui/type/type-annotation-needed.rs @@ -1,10 +1,9 @@ fn foo>(x: i32) {} //~^ NOTE required by -//~| NOTE fn main() { foo(42); //~^ ERROR type annotations needed //~| NOTE cannot infer type - //~| NOTE cannot resolve + //~| NOTE cannot satisfy } diff --git a/src/test/ui/type/type-annotation-needed.stderr b/src/test/ui/type/type-annotation-needed.stderr index df7d73d7a7c1d..927cc507265fa 100644 --- a/src/test/ui/type/type-annotation-needed.stderr +++ b/src/test/ui/type/type-annotation-needed.stderr @@ -1,13 +1,13 @@ error[E0283]: type annotations needed - --> $DIR/type-annotation-needed.rs:6:5 + --> $DIR/type-annotation-needed.rs:5:5 | LL | fn foo>(x: i32) {} - | --- ------------ required by this bound in `foo` + | ------------ required by this bound in `foo` ... LL | foo(42); | ^^^ cannot infer type for type parameter `T` declared on the function `foo` | - = note: cannot resolve `_: std::convert::Into` + = note: cannot satisfy `_: std::convert::Into` help: consider specifying the type argument in the function call | LL | foo::(42); diff --git a/src/test/ui/type/type-check-defaults.stderr b/src/test/ui/type/type-check-defaults.stderr index 31ee15e0745db..e2729c65e02c4 100644 --- a/src/test/ui/type/type-check-defaults.stderr +++ b/src/test/ui/type/type-check-defaults.stderr @@ -2,7 +2,7 @@ error[E0277]: a value of type `i32` cannot be built from an iterator over elemen --> $DIR/type-check-defaults.rs:6:19 | LL | struct Foo>(T, U); - | ---------------------------------------- required by `Foo` + | --------------- required by this bound in `Foo` LL | struct WellFormed>(Z); | ^ value of type `i32` cannot be built from `std::iter::Iterator` | @@ -12,7 +12,7 @@ error[E0277]: a value of type `i32` cannot be built from an iterator over elemen --> $DIR/type-check-defaults.rs:8:27 | LL | struct Foo>(T, U); - | ---------------------------------------- required by `Foo` + | --------------- required by this bound in `Foo` ... LL | struct WellFormedNoBounds>(Z); | ^ value of type `i32` cannot be built from `std::iter::Iterator` @@ -50,15 +50,14 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/type-check-defaults.rs:21:25 | LL | trait Super { } - | -------------------- required by `Super` + | ---- required by this bound in `Super` LL | trait Base: Super { } | ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/type-check-defaults.rs:21:12 +help: consider further restricting type parameter `T` | -LL | trait Base: Super { } - | ^ +LL | trait Base: Super, T: std::marker::Copy { } + | ^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: cannot add `u8` to `i32` --> $DIR/type-check-defaults.rs:24:66 diff --git a/src/test/ui/type/type-check/issue-40294.stderr b/src/test/ui/type/type-check/issue-40294.stderr index 2c889b6c2ca0a..ea7771a9c2270 100644 --- a/src/test/ui/type/type-check/issue-40294.stderr +++ b/src/test/ui/type/type-check/issue-40294.stderr @@ -2,12 +2,12 @@ error[E0283]: type annotations needed --> $DIR/issue-40294.rs:6:19 | LL | trait Foo: Sized { - | ---------------- required by `Foo` + | ---------------- required by this bound in `Foo` ... LL | where &'a T : Foo, | ^^^ cannot infer type for reference `&'a T` | - = note: cannot resolve `&'a T: Foo` + = note: cannot satisfy `&'a T: Foo` error: aborting due to previous error diff --git a/src/test/ui/type/type-check/missing_trait_impl.stderr b/src/test/ui/type/type-check/missing_trait_impl.stderr index 7186d6a542dc9..30df1261cefa1 100644 --- a/src/test/ui/type/type-check/missing_trait_impl.stderr +++ b/src/test/ui/type/type-check/missing_trait_impl.stderr @@ -6,7 +6,10 @@ LL | let z = x + y; | | | T | - = note: `T` might need a bound for `std::ops::Add` +help: consider restricting type parameter `T` + | +LL | fn foo>(x: T, y: T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0368]: binary assignment operation `+=` cannot be applied to type `T` --> $DIR/missing_trait_impl.rs:9:5 @@ -16,7 +19,10 @@ LL | x += x; | | | cannot use `+=` on type `T` | - = note: `T` might need a bound for `std::ops::AddAssign` +help: consider restricting type parameter `T` + | +LL | fn bar(x: T) { + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/type/type-dependent-def-issue-49241.rs b/src/test/ui/type/type-dependent-def-issue-49241.rs index a25e3ba5fa89f..4b6bc6124dbf3 100644 --- a/src/test/ui/type/type-dependent-def-issue-49241.rs +++ b/src/test/ui/type/type-dependent-def-issue-49241.rs @@ -2,6 +2,4 @@ fn main() { let v = vec![0]; const l: usize = v.count(); //~ ERROR attempt to use a non-constant value in a constant let s: [u32; l] = v.into_iter().collect(); - //~^ ERROR evaluation of constant value failed - //~^^ ERROR a value of type } diff --git a/src/test/ui/type/type-dependent-def-issue-49241.stderr b/src/test/ui/type/type-dependent-def-issue-49241.stderr index 18a69c50ebd6d..c5dcfa7a43133 100644 --- a/src/test/ui/type/type-dependent-def-issue-49241.stderr +++ b/src/test/ui/type/type-dependent-def-issue-49241.stderr @@ -4,21 +4,6 @@ error[E0435]: attempt to use a non-constant value in a constant LL | const l: usize = v.count(); | ^ non-constant value -error[E0080]: evaluation of constant value failed - --> $DIR/type-dependent-def-issue-49241.rs:4:18 - | -LL | let s: [u32; l] = v.into_iter().collect(); - | ^ referenced constant has errors - -error[E0277]: a value of type `[u32; _]` cannot be built from an iterator over elements of type `{integer}` - --> $DIR/type-dependent-def-issue-49241.rs:4:37 - | -LL | let s: [u32; l] = v.into_iter().collect(); - | ^^^^^^^ value of type `[u32; _]` cannot be built from `std::iter::Iterator` - | - = help: the trait `std::iter::FromIterator<{integer}>` is not implemented for `[u32; _]` - -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0080, E0277, E0435. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0435`. diff --git a/src/test/ui/type/type-params-in-different-spaces-2.stderr b/src/test/ui/type/type-params-in-different-spaces-2.stderr index 7ce249a60b85e..e0039f2a31602 100644 --- a/src/test/ui/type/type-params-in-different-spaces-2.stderr +++ b/src/test/ui/type/type-params-in-different-spaces-2.stderr @@ -4,10 +4,13 @@ error[E0277]: the trait bound `Self: Tr` is not satisfied LL | fn op(_: T) -> Self; | -------------------- required by `Tr::op` ... -LL | fn test(u: U) -> Self { - | - help: consider further restricting `Self`: `where Self: Tr` LL | Tr::op(u) | ^^^^^^ the trait `Tr` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn test(u: U) -> Self where Self: Tr { + | ^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Self: Tr` is not satisfied --> $DIR/type-params-in-different-spaces-2.rs:16:9 @@ -15,10 +18,13 @@ error[E0277]: the trait bound `Self: Tr` is not satisfied LL | fn op(_: T) -> Self; | -------------------- required by `Tr::op` ... -LL | fn test(u: U) -> Self { - | - help: consider further restricting `Self`: `where Self: Tr` LL | Tr::op(u) | ^^^^^^ the trait `Tr` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn test(u: U) -> Self where Self: Tr { + | ^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/type_length_limit.rs b/src/test/ui/type_length_limit.rs index 0ecd5cf5fafaa..1f1c8ad962690 100644 --- a/src/test/ui/type_length_limit.rs +++ b/src/test/ui/type_length_limit.rs @@ -1,8 +1,4 @@ // build-fail -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl // error-pattern: reached the type-length limit while instantiating // Test that the type length limit can be changed. diff --git a/src/test/ui/typeck/issue-67971.rs b/src/test/ui/typeck/issue-67971.rs new file mode 100644 index 0000000000000..8bf725cb5ee38 --- /dev/null +++ b/src/test/ui/typeck/issue-67971.rs @@ -0,0 +1,9 @@ +struct S {} + +fn foo(ctx: &mut S) -> String { //~ ERROR mismatched types + // Don't suggest to remove semicolon as it won't fix anything + ctx.sleep = 0; + //~^ ERROR no field `sleep` on type `&mut S` +} + +fn main() {} diff --git a/src/test/ui/typeck/issue-67971.stderr b/src/test/ui/typeck/issue-67971.stderr new file mode 100644 index 0000000000000..36ad3fcb342a8 --- /dev/null +++ b/src/test/ui/typeck/issue-67971.stderr @@ -0,0 +1,18 @@ +error[E0609]: no field `sleep` on type `&mut S` + --> $DIR/issue-67971.rs:5:9 + | +LL | ctx.sleep = 0; + | ^^^^^ unknown field + +error[E0308]: mismatched types + --> $DIR/issue-67971.rs:3:24 + | +LL | fn foo(ctx: &mut S) -> String { + | --- ^^^^^^ expected struct `std::string::String`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0609. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.rs b/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.rs index 92d8ba887270e..8824a6d2767f7 100644 --- a/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.rs +++ b/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait Magic : Sized where Option : Magic {} //~ ERROR E0568 impl Magic for T {} diff --git a/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr b/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr index e397629327754..63b3300f6dbf4 100644 --- a/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr +++ b/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr @@ -1,5 +1,5 @@ error[E0568]: auto traits cannot have super traits - --> $DIR/typeck-auto-trait-no-supertraits-2.rs:3:20 + --> $DIR/typeck-auto-trait-no-supertraits-2.rs:4:20 | LL | auto trait Magic : Sized where Option : Magic {} | ----- ^^^^^ help: remove the super traits diff --git a/src/test/ui/typeck/typeck-auto-trait-no-supertraits.rs b/src/test/ui/typeck/typeck-auto-trait-no-supertraits.rs index e48017acfc718..edbca91512486 100644 --- a/src/test/ui/typeck/typeck-auto-trait-no-supertraits.rs +++ b/src/test/ui/typeck/typeck-auto-trait-no-supertraits.rs @@ -23,6 +23,7 @@ // } #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait Magic: Copy {} //~ ERROR E0568 impl Magic for T {} diff --git a/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr b/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr index b1602e3642ecb..796638fc54dce 100644 --- a/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr +++ b/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr @@ -1,5 +1,5 @@ error[E0568]: auto traits cannot have super traits - --> $DIR/typeck-auto-trait-no-supertraits.rs:27:19 + --> $DIR/typeck-auto-trait-no-supertraits.rs:28:19 | LL | auto trait Magic: Copy {} | ----- ^^^^ help: remove the super traits diff --git a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed index 7a108d880bed3..dd1195b99f694 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed +++ b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed @@ -7,7 +7,7 @@ trait Trait { type AssocType; fn dummy(&self) { } } -fn bar() where ::AssocType: std::marker::Send { +fn bar() where ::AssocType: std::marker::Send { is_send::(); //~ ERROR E0277 } diff --git a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr index 2e54cdf01320d..f97d41637ba04 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr @@ -1,15 +1,17 @@ error[E0277]: `::AssocType` cannot be sent between threads safely --> $DIR/typeck-default-trait-impl-assoc-type.rs:11:5 | -LL | fn bar() { - | - help: consider further restricting the associated type: `where ::AssocType: std::marker::Send` LL | is_send::(); | ^^^^^^^^^^^^^^^^^^^^^^^ `::AssocType` cannot be sent between threads safely ... LL | fn is_send() { - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` | = help: the trait `std::marker::Send` is not implemented for `::AssocType` +help: consider further restricting the associated type + | +LL | fn bar() where ::AssocType: std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.rs b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.rs index 6c170fb5bae7d..71ac2b466c10a 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait MyTrait {} diff --git a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.stderr b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.stderr index f060afea24e7f..53ba9b8a3f6b4 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `MyS2: MyTrait` is not satisfied in `(MyS2, MyS)` - --> $DIR/typeck-default-trait-impl-constituent-types-2.rs:16:5 + --> $DIR/typeck-default-trait-impl-constituent-types-2.rs:17:5 | LL | fn is_mytrait() {} - | ---------- ------- required by this bound in `is_mytrait` + | ------- required by this bound in `is_mytrait` ... LL | is_mytrait::<(MyS2, MyS)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `(MyS2, MyS)`, the trait `MyTrait` is not implemented for `MyS2` diff --git a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.rs b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.rs index d72f676121875..6483b9213dc53 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait MyTrait {} diff --git a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.stderr b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.stderr index 22a2cb3e0ecb8..bc50000498463 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-constituent-types.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `MyS2: MyTrait` is not satisfied - --> $DIR/typeck-default-trait-impl-constituent-types.rs:20:18 + --> $DIR/typeck-default-trait-impl-constituent-types.rs:21:18 | LL | fn is_mytrait() {} - | ---------- ------- required by this bound in `is_mytrait` + | ------- required by this bound in `is_mytrait` ... LL | is_mytrait::(); | ^^^^ the trait `MyTrait` is not implemented for `MyS2` diff --git a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs index 212e165151d81..772ac322032ec 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs @@ -3,7 +3,7 @@ // Test that we do not consider associated types to be sendable without // some applicable trait bound (and we don't ICE). -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] extern crate tdticc_coherence_lib as lib; diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-send.rs b/src/test/ui/typeck/typeck-default-trait-impl-negation-send.rs index 617d0f3b565a8..3a2fc39d409d9 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-send.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-send.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] struct MySendable { t: *mut u8 diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr b/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr index e30d4dfa1b3c4..b6ab36f5159d5 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr @@ -2,7 +2,7 @@ error[E0277]: `MyNotSendable` cannot be sent between threads safely --> $DIR/typeck-default-trait-impl-negation-send.rs:19:15 | LL | fn is_send() {} - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` ... LL | is_send::(); | ^^^^^^^^^^^^^ `MyNotSendable` cannot be sent between threads safely diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs index e4487fb110cf1..2734b761e61b7 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs @@ -1,4 +1,4 @@ -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] struct Managed; impl !Send for Managed {} diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr index 4dd8e01cf2d36..d671b8eb7549b 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr @@ -2,7 +2,7 @@ error[E0277]: `MyNotSync` cannot be shared between threads safely --> $DIR/typeck-default-trait-impl-negation-sync.rs:33:15 | LL | fn is_sync() {} - | ------- ---- required by this bound in `is_sync` + | ---- required by this bound in `is_sync` ... LL | is_sync::(); | ^^^^^^^^^ `MyNotSync` cannot be shared between threads safely @@ -13,7 +13,7 @@ error[E0277]: `std::cell::UnsafeCell` cannot be shared between threads safel --> $DIR/typeck-default-trait-impl-negation-sync.rs:36:5 | LL | fn is_sync() {} - | ------- ---- required by this bound in `is_sync` + | ---- required by this bound in `is_sync` ... LL | is_sync::(); | ^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` cannot be shared between threads safely @@ -25,7 +25,7 @@ error[E0277]: `Managed` cannot be shared between threads safely --> $DIR/typeck-default-trait-impl-negation-sync.rs:39:5 | LL | fn is_sync() {} - | ------- ---- required by this bound in `is_sync` + | ---- required by this bound in `is_sync` ... LL | is_sync::(); | ^^^^^^^^^^^^^^^^^^^^^^^^ `Managed` cannot be shared between threads safely diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation.rs b/src/test/ui/typeck/typeck-default-trait-impl-negation.rs index 0b6e13be6e108..47cab60625dce 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation.rs @@ -1,4 +1,5 @@ #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait MyTrait {} diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation.stderr b/src/test/ui/typeck/typeck-default-trait-impl-negation.stderr index 4b13fcc885a0d..76a6994cb009a 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `ThisImplsUnsafeTrait: MyTrait` is not satisfied - --> $DIR/typeck-default-trait-impl-negation.rs:21:19 + --> $DIR/typeck-default-trait-impl-negation.rs:22:19 | LL | fn is_my_trait() {} - | ----------- ------- required by this bound in `is_my_trait` + | ------- required by this bound in `is_my_trait` ... LL | is_my_trait::(); | ^^^^^^^^^^^^^^^^^^^^ the trait `MyTrait` is not implemented for `ThisImplsUnsafeTrait` @@ -11,10 +11,10 @@ LL | is_my_trait::(); error[E0277]: the trait bound `ThisImplsTrait: MyUnsafeTrait` is not satisfied - --> $DIR/typeck-default-trait-impl-negation.rs:24:26 + --> $DIR/typeck-default-trait-impl-negation.rs:25:26 | LL | fn is_my_unsafe_trait() {} - | ------------------ ------------- required by this bound in `is_my_unsafe_trait` + | ------------- required by this bound in `is_my_unsafe_trait` ... LL | is_my_unsafe_trait::(); | ^^^^^^^^^^^^^^ the trait `MyUnsafeTrait` is not implemented for `ThisImplsTrait` diff --git a/src/test/ui/typeck/typeck-default-trait-impl-precedence.rs b/src/test/ui/typeck/typeck-default-trait-impl-precedence.rs index 9b228f706465b..614a5ff55b1ed 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-precedence.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-precedence.rs @@ -4,6 +4,7 @@ // impls whose types unify. #![feature(optin_builtin_traits)] +#![feature(negative_impls)] auto trait Defaulted { } impl<'a,T:Signed> Defaulted for &'a T { } diff --git a/src/test/ui/typeck/typeck-default-trait-impl-precedence.stderr b/src/test/ui/typeck/typeck-default-trait-impl-precedence.stderr index 1587730441820..5962d19129288 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-precedence.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-precedence.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `u32: Signed` is not satisfied - --> $DIR/typeck-default-trait-impl-precedence.rs:18:5 + --> $DIR/typeck-default-trait-impl-precedence.rs:19:5 | LL | fn is_defaulted() { } - | ------------ --------- required by this bound in `is_defaulted` + | --------- required by this bound in `is_defaulted` ... LL | is_defaulted::<&'static u32>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32` diff --git a/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr b/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr index 45c9d8be85ee9..9cba3578449c3 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr @@ -5,14 +5,13 @@ LL | is_send::() | ^ `T` cannot be sent between threads safely ... LL | fn is_send() { - | ------- ---- required by this bound in `is_send` + | ---- required by this bound in `is_send` | = help: the trait `std::marker::Send` is not implemented for `T` -help: consider restricting this type parameter with `T: std::marker::Send` - --> $DIR/typeck-default-trait-impl-send-param.rs:4:8 +help: consider restricting type parameter `T` | -LL | fn foo() { - | ^ +LL | fn foo() { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck-negative-impls-builtin.rs b/src/test/ui/typeck/typeck-negative-impls-builtin.rs deleted file mode 100644 index fef98977cc435..0000000000000 --- a/src/test/ui/typeck/typeck-negative-impls-builtin.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(optin_builtin_traits)] - -struct TestType; - -trait TestTrait { - fn dummy(&self) { } -} - -impl !TestTrait for TestType {} -//~^ ERROR invalid negative impl - -fn main() {} diff --git a/src/test/ui/typeck/typeck-negative-impls-builtin.stderr b/src/test/ui/typeck/typeck-negative-impls-builtin.stderr deleted file mode 100644 index c90655086acda..0000000000000 --- a/src/test/ui/typeck/typeck-negative-impls-builtin.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0192]: invalid negative impl - --> $DIR/typeck-negative-impls-builtin.rs:9:6 - | -LL | impl !TestTrait for TestType {} - | ^^^^^^^^^^ - | - = note: negative impls are only allowed for auto traits, like `Send` and `Sync` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0192`. diff --git a/src/test/ui/typeck/typeck-unsafe-always-share.rs b/src/test/ui/typeck/typeck-unsafe-always-share.rs index 7d1ff732983fe..dc5ddf5156302 100644 --- a/src/test/ui/typeck/typeck-unsafe-always-share.rs +++ b/src/test/ui/typeck/typeck-unsafe-always-share.rs @@ -1,6 +1,6 @@ // Verify that UnsafeCell is *always* !Sync regardless if `T` is sync. -#![feature(optin_builtin_traits)] +#![feature(negative_impls)] use std::cell::UnsafeCell; use std::marker::Sync; diff --git a/src/test/ui/typeck/typeck-unsafe-always-share.stderr b/src/test/ui/typeck/typeck-unsafe-always-share.stderr index d08613238f866..61585fcc1c865 100644 --- a/src/test/ui/typeck/typeck-unsafe-always-share.stderr +++ b/src/test/ui/typeck/typeck-unsafe-always-share.stderr @@ -2,7 +2,7 @@ error[E0277]: `std::cell::UnsafeCell>` cannot be shared betwee --> $DIR/typeck-unsafe-always-share.rs:19:10 | LL | fn test(s: T) {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test(us); | ^^ `std::cell::UnsafeCell>` cannot be shared between threads safely @@ -13,7 +13,7 @@ error[E0277]: `std::cell::UnsafeCell` cannot be shared between threads s --> $DIR/typeck-unsafe-always-share.rs:23:10 | LL | fn test(s: T) {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test(uns); | ^^^ `std::cell::UnsafeCell` cannot be shared between threads safely @@ -24,7 +24,7 @@ error[E0277]: `std::cell::UnsafeCell` cannot be shared between threads s --> $DIR/typeck-unsafe-always-share.rs:27:5 | LL | fn test(s: T) {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test(ms); | ^^^^ `std::cell::UnsafeCell` cannot be shared between threads safely @@ -36,7 +36,7 @@ error[E0277]: `NoSync` cannot be shared between threads safely --> $DIR/typeck-unsafe-always-share.rs:30:10 | LL | fn test(s: T) {} - | ---- ---- required by this bound in `test` + | ---- required by this bound in `test` ... LL | test(NoSync); | ^^^^^^ `NoSync` cannot be shared between threads safely diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs index 5444fc62d8211..99a7023089283 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.rs +++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs @@ -32,7 +32,6 @@ fn test7(x: _) { let _x: usize = x; } fn test8(_f: fn() -> _) { } //~^ ERROR the type placeholder `_` is not allowed within types on item signatures -//~| ERROR the type placeholder `_` is not allowed within types on item signatures struct Test9; @@ -99,7 +98,6 @@ pub fn main() { fn fn_test8(_f: fn() -> _) { } //~^ ERROR the type placeholder `_` is not allowed within types on item signatures - //~| ERROR the type placeholder `_` is not allowed within types on item signatures struct FnTest9; diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr index 955765e1175ce..6c0653d5fcb7c 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr +++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr @@ -1,35 +1,35 @@ error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:154:18 + --> $DIR/typeck_type_placeholder_item.rs:152:18 | LL | struct BadStruct<_>(_); | ^ expected identifier, found reserved identifier error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:157:16 + --> $DIR/typeck_type_placeholder_item.rs:155:16 | LL | trait BadTrait<_> {} | ^ expected identifier, found reserved identifier error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:167:19 + --> $DIR/typeck_type_placeholder_item.rs:165:19 | LL | struct BadStruct1<_, _>(_); | ^ expected identifier, found reserved identifier error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:167:22 + --> $DIR/typeck_type_placeholder_item.rs:165:22 | LL | struct BadStruct1<_, _>(_); | ^ expected identifier, found reserved identifier error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:172:19 + --> $DIR/typeck_type_placeholder_item.rs:170:19 | LL | struct BadStruct2<_, T>(_, T); | ^ expected identifier, found reserved identifier error: associated constant in `impl` without body - --> $DIR/typeck_type_placeholder_item.rs:203:5 + --> $DIR/typeck_type_placeholder_item.rs:201:5 | LL | const C: _; | ^^^^^^^^^^- @@ -37,7 +37,7 @@ LL | const C: _; | help: provide a definition for the constant: `= ;` error[E0403]: the name `_` is already used for a generic parameter in this item's generic parameters - --> $DIR/typeck_type_placeholder_item.rs:167:22 + --> $DIR/typeck_type_placeholder_item.rs:165:22 | LL | struct BadStruct1<_, _>(_); | - ^ already used @@ -106,7 +106,7 @@ LL | fn test6_b(_: _, _: T) { } | help: use type parameters instead | -LL | fn test6_b(_: K, _: T) { } +LL | fn test6_b(_: U, _: T) { } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures @@ -117,7 +117,7 @@ LL | fn test6_c(_: _, _: (T, K, L, A, B)) { } | help: use type parameters instead | -LL | fn test6_c(_: C, _: (T, K, L, A, B)) { } +LL | fn test6_c(_: U, _: (T, K, L, A, B)) { } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures @@ -131,12 +131,6 @@ help: use type parameters instead LL | fn test7(x: T) { let _x: usize = x; } | ^^^ ^ -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:33:22 - | -LL | fn test8(_f: fn() -> _) { } - | ^ not allowed in type signatures - error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:33:22 | @@ -149,7 +143,7 @@ LL | fn test8(_f: fn() -> T) { } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:47:26 + --> $DIR/typeck_type_placeholder_item.rs:46:26 | LL | fn test11(x: &usize) -> &_ { | -^ @@ -158,7 +152,7 @@ LL | fn test11(x: &usize) -> &_ { | help: replace with the correct return type: `&&usize` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:52:52 + --> $DIR/typeck_type_placeholder_item.rs:51:52 | LL | unsafe fn test12(x: *const usize) -> *const *const _ { | --------------^ @@ -167,7 +161,7 @@ LL | unsafe fn test12(x: *const usize) -> *const *const _ { | help: replace with the correct return type: `*const *const usize` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:66:8 + --> $DIR/typeck_type_placeholder_item.rs:65:8 | LL | a: _, | ^ not allowed in type signatures @@ -186,13 +180,13 @@ LL | b: (T, T), | error: missing type for `static` item - --> $DIR/typeck_type_placeholder_item.rs:72:12 + --> $DIR/typeck_type_placeholder_item.rs:71:12 | LL | static A = 42; | ^ help: provide a type for the item: `A: i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:74:15 + --> $DIR/typeck_type_placeholder_item.rs:73:15 | LL | static B: _ = 42; | ^ @@ -201,13 +195,13 @@ LL | static B: _ = 42; | help: replace `_` with the correct type: `i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:76:15 + --> $DIR/typeck_type_placeholder_item.rs:75:15 | LL | static C: Option<_> = Some(42); | ^^^^^^^^^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:79:21 + --> $DIR/typeck_type_placeholder_item.rs:78:21 | LL | fn fn_test() -> _ { 5 } | ^ @@ -216,7 +210,7 @@ LL | fn fn_test() -> _ { 5 } | help: replace with the correct return type: `i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:82:23 + --> $DIR/typeck_type_placeholder_item.rs:81:23 | LL | fn fn_test2() -> (_, _) { (5, 5) } | -^--^- @@ -226,7 +220,7 @@ LL | fn fn_test2() -> (_, _) { (5, 5) } | help: replace with the correct return type: `(i32, i32)` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:85:22 + --> $DIR/typeck_type_placeholder_item.rs:84:22 | LL | static FN_TEST3: _ = "test"; | ^ @@ -235,7 +229,7 @@ LL | static FN_TEST3: _ = "test"; | help: replace `_` with the correct type: `&str` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:88:22 + --> $DIR/typeck_type_placeholder_item.rs:87:22 | LL | static FN_TEST4: _ = 145; | ^ @@ -244,13 +238,13 @@ LL | static FN_TEST4: _ = 145; | help: replace `_` with the correct type: `i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:91:22 + --> $DIR/typeck_type_placeholder_item.rs:90:22 | LL | static FN_TEST5: (_, _) = (1, 2); | ^^^^^^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:94:20 + --> $DIR/typeck_type_placeholder_item.rs:93:20 | LL | fn fn_test6(_: _) { } | ^ not allowed in type signatures @@ -261,7 +255,7 @@ LL | fn fn_test6(_: T) { } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:97:20 + --> $DIR/typeck_type_placeholder_item.rs:96:20 | LL | fn fn_test7(x: _) { let _x: usize = x; } | ^ not allowed in type signatures @@ -272,13 +266,7 @@ LL | fn fn_test7(x: T) { let _x: usize = x; } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:100:29 - | -LL | fn fn_test8(_f: fn() -> _) { } - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:100:29 + --> $DIR/typeck_type_placeholder_item.rs:99:29 | LL | fn fn_test8(_f: fn() -> _) { } | ^ not allowed in type signatures @@ -289,7 +277,7 @@ LL | fn fn_test8(_f: fn() -> T) { } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:123:12 + --> $DIR/typeck_type_placeholder_item.rs:121:12 | LL | a: _, | ^ not allowed in type signatures @@ -308,13 +296,13 @@ LL | b: (T, T), | error[E0282]: type annotations needed - --> $DIR/typeck_type_placeholder_item.rs:128:18 + --> $DIR/typeck_type_placeholder_item.rs:126:18 | LL | fn fn_test11(_: _) -> (_, _) { panic!() } | ^ cannot infer type error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:128:28 + --> $DIR/typeck_type_placeholder_item.rs:126:28 | LL | fn fn_test11(_: _) -> (_, _) { panic!() } | ^ ^ not allowed in type signatures @@ -322,7 +310,7 @@ LL | fn fn_test11(_: _) -> (_, _) { panic!() } | not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:132:30 + --> $DIR/typeck_type_placeholder_item.rs:130:30 | LL | fn fn_test12(x: i32) -> (_, _) { (x, x) } | -^--^- @@ -332,7 +320,7 @@ LL | fn fn_test12(x: i32) -> (_, _) { (x, x) } | help: replace with the correct return type: `(i32, i32)` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:135:33 + --> $DIR/typeck_type_placeholder_item.rs:133:33 | LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } | ------^- @@ -341,7 +329,7 @@ LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } | help: replace with the correct return type: `(i32, i32)` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:154:21 + --> $DIR/typeck_type_placeholder_item.rs:152:21 | LL | struct BadStruct<_>(_); | ^ not allowed in type signatures @@ -352,7 +340,7 @@ LL | struct BadStruct(T); | ^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:159:15 + --> $DIR/typeck_type_placeholder_item.rs:157:15 | LL | impl BadTrait<_> for BadStruct<_> {} | ^ ^ not allowed in type signatures @@ -365,13 +353,13 @@ LL | impl BadTrait for BadStruct {} | ^^^ ^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:162:34 + --> $DIR/typeck_type_placeholder_item.rs:160:34 | LL | fn impl_trait() -> impl BadTrait<_> { | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:167:25 + --> $DIR/typeck_type_placeholder_item.rs:165:25 | LL | struct BadStruct1<_, _>(_); | ^ not allowed in type signatures @@ -382,24 +370,24 @@ LL | struct BadStruct1(T); | ^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:172:25 + --> $DIR/typeck_type_placeholder_item.rs:170:25 | LL | struct BadStruct2<_, T>(_, T); | ^ not allowed in type signatures | help: use type parameters instead | -LL | struct BadStruct2(K, T); +LL | struct BadStruct2(U, T); | ^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:176:14 + --> $DIR/typeck_type_placeholder_item.rs:174:14 | LL | type X = Box<_>; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:43:27 + --> $DIR/typeck_type_placeholder_item.rs:42:27 | LL | fn test10(&self, _x : _) { } | ^ not allowed in type signatures @@ -410,7 +398,7 @@ LL | fn test10(&self, _x : T) { } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:140:31 + --> $DIR/typeck_type_placeholder_item.rs:138:31 | LL | fn method_test1(&self, x: _); | ^ not allowed in type signatures @@ -421,7 +409,7 @@ LL | fn method_test1(&self, x: T); | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:142:31 + --> $DIR/typeck_type_placeholder_item.rs:140:31 | LL | fn method_test2(&self, x: _) -> _; | ^ ^ not allowed in type signatures @@ -434,7 +422,7 @@ LL | fn method_test2(&self, x: T) -> T; | ^^^ ^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:144:31 + --> $DIR/typeck_type_placeholder_item.rs:142:31 | LL | fn method_test3(&self) -> _; | ^ not allowed in type signatures @@ -445,7 +433,7 @@ LL | fn method_test3(&self) -> T; | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:146:26 + --> $DIR/typeck_type_placeholder_item.rs:144:26 | LL | fn assoc_fn_test1(x: _); | ^ not allowed in type signatures @@ -456,7 +444,7 @@ LL | fn assoc_fn_test1(x: T); | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:148:26 + --> $DIR/typeck_type_placeholder_item.rs:146:26 | LL | fn assoc_fn_test2(x: _) -> _; | ^ ^ not allowed in type signatures @@ -469,7 +457,7 @@ LL | fn assoc_fn_test2(x: T) -> T; | ^^^ ^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:150:28 + --> $DIR/typeck_type_placeholder_item.rs:148:28 | LL | fn assoc_fn_test3() -> _; | ^ not allowed in type signatures @@ -480,7 +468,7 @@ LL | fn assoc_fn_test3() -> T; | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:61:37 + --> $DIR/typeck_type_placeholder_item.rs:60:37 | LL | fn clone_from(&mut self, other: _) { *self = Test9; } | ^ not allowed in type signatures @@ -491,7 +479,7 @@ LL | fn clone_from(&mut self, other: T) { *self = Test9; } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:110:34 + --> $DIR/typeck_type_placeholder_item.rs:108:34 | LL | fn fn_test10(&self, _x : _) { } | ^ not allowed in type signatures @@ -502,7 +490,7 @@ LL | fn fn_test10(&self, _x : T) { } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:118:41 + --> $DIR/typeck_type_placeholder_item.rs:116:41 | LL | fn clone_from(&mut self, other: _) { *self = FnTest9; } | ^ not allowed in type signatures @@ -513,25 +501,25 @@ LL | fn clone_from(&mut self, other: T) { *self = FnTest9; } | ^^^ ^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:182:21 + --> $DIR/typeck_type_placeholder_item.rs:180:21 | LL | type Y = impl Trait<_>; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:190:14 + --> $DIR/typeck_type_placeholder_item.rs:188:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:192:14 + --> $DIR/typeck_type_placeholder_item.rs:190:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:194:14 + --> $DIR/typeck_type_placeholder_item.rs:192:14 | LL | const D: _ = 42; | ^ @@ -540,7 +528,7 @@ LL | const D: _ = 42; | help: replace `_` with the correct type: `i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:40:24 + --> $DIR/typeck_type_placeholder_item.rs:39:24 | LL | fn test9(&self) -> _ { () } | ^ @@ -549,7 +537,7 @@ LL | fn test9(&self) -> _ { () } | help: replace with the correct return type: `()` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:58:24 + --> $DIR/typeck_type_placeholder_item.rs:57:24 | LL | fn clone(&self) -> _ { Test9 } | ^ @@ -558,7 +546,7 @@ LL | fn clone(&self) -> _ { Test9 } | help: replace with the correct return type: `Test9` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:107:31 + --> $DIR/typeck_type_placeholder_item.rs:105:31 | LL | fn fn_test9(&self) -> _ { () } | ^ @@ -567,7 +555,7 @@ LL | fn fn_test9(&self) -> _ { () } | help: replace with the correct return type: `()` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:115:28 + --> $DIR/typeck_type_placeholder_item.rs:113:28 | LL | fn clone(&self) -> _ { FnTest9 } | ^ @@ -576,25 +564,25 @@ LL | fn clone(&self) -> _ { FnTest9 } | help: replace with the correct return type: `main::FnTest9` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:199:14 + --> $DIR/typeck_type_placeholder_item.rs:197:14 | LL | type A = _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:201:14 + --> $DIR/typeck_type_placeholder_item.rs:199:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:203:14 + --> $DIR/typeck_type_placeholder_item.rs:201:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:206:14 + --> $DIR/typeck_type_placeholder_item.rs:204:14 | LL | const D: _ = 42; | ^ @@ -602,7 +590,7 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace `_` with the correct type: `i32` -error: aborting due to 66 previous errors +error: aborting due to 64 previous errors Some errors have detailed explanations: E0121, E0282, E0403. For more information about an error, try `rustc --explain E0121`. diff --git a/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr b/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr index 7af9c57a8300a..b824d160d7160 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr @@ -2,7 +2,7 @@ error[E0658]: parenthetical notation is only stable when used with `Fn`-family t --> $DIR/unboxed-closure-feature-gate.rs:13:20 | LL | let x: Box; - | ^^^ + | ^^^^^^^^^^ | = note: see issue #29625 for more information = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr index 6ec4063828917..908d854385157 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `dyn Foo<(isize,), isize, Output = ()>: Eq $DIR/unboxed-closure-sugar-default.rs:21:5 | LL | fn eq() where A : Eq { } - | -- ----- required by this bound in `eq` + | ----- required by this bound in `eq` ... LL | eq::, dyn Foo(isize)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq>` is not implemented for `dyn Foo<(isize,), isize, Output = ()>` diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr index 8dd32ee7f104a..8ce7e825a1ced 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `dyn Foo<(char,), Output = ()>: Eq $DIR/unboxed-closure-sugar-equiv.rs:43:5 | LL | fn eq>() { } - | -- ----- required by this bound in `eq` + | ----- required by this bound in `eq` ... LL | / eq::< dyn Foo<(),Output=()>, LL | | dyn Foo(char) >(); diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr index 9a3bdd2bd5ea4..9da36906d5542 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr @@ -2,7 +2,7 @@ error[E0658]: the precise format of `Fn`-family traits' type parameters is subje --> $DIR/unboxed-closure-sugar-not-used-on-fn.rs:3:17 | LL | fn bar1(x: &dyn Fn<(), Output=()>) { - | ^^ help: use parenthetical notation instead: `Fn() -> ()` + | ^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn() -> ()` | = note: see issue #29625 for more information = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable @@ -11,7 +11,7 @@ error[E0658]: the precise format of `Fn`-family traits' type parameters is subje --> $DIR/unboxed-closure-sugar-not-used-on-fn.rs:7:28 | LL | fn bar2(x: &T) where T: Fn<()> { - | ^^ help: use parenthetical notation instead: `Fn() -> ()` + | ^^^^^^ help: use parenthetical notation instead: `Fn() -> ()` | = note: see issue #29625 for more information = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr index b92f054498b68..e9d51983a7a48 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr @@ -2,7 +2,7 @@ error[E0107]: wrong number of lifetime arguments: expected 1, found 0 --> $DIR/unboxed-closure-sugar-region.rs:30:51 | LL | fn test2(x: &dyn Foo<(isize,),Output=()>, y: &dyn Foo(isize)) { - | ^^^ expected 1 lifetime argument + | ^^^^^^^^^^ expected 1 lifetime argument error: aborting due to previous error diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr index f482098cbffcb..f42ac38d370d5 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr @@ -2,7 +2,7 @@ error[E0107]: wrong number of type arguments: expected 3, found 1 --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs:5:16 | LL | fn foo(_: &dyn Three()) - | ^^^^^ expected 3 type arguments + | ^^^^^^^ expected 3 type arguments error[E0220]: associated type `Output` not found for `Three<(), [type error], [type error]>` --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs:5:16 diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr index ff65fd968c5e7..c81402a3dcc00 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr @@ -5,10 +5,10 @@ LL | fn f isize>(x: F) {} | ^^^^^^^^^^^^ unexpected type argument error[E0220]: associated type `Output` not found for `Trait` - --> $DIR/unboxed-closure-sugar-wrong-trait.rs:5:8 + --> $DIR/unboxed-closure-sugar-wrong-trait.rs:5:24 | LL | fn f isize>(x: F) {} - | ^^^^^^^^^^^^^^^^^^^^^ associated type `Output` not found + | ^^^^^ associated type `Output` not found error: aborting due to 2 previous errors diff --git a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr index dc7661815310d..d427873ebcb60 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr @@ -2,7 +2,7 @@ error[E0277]: expected a `std::ops::Fn<(isize,)>` closure, found `S` --> $DIR/unboxed-closures-fnmut-as-fn.rs:28:21 | LL | fn call_itisize>(f: &F, x: isize) -> isize { - | ------- ---------------- required by this bound in `call_it` + | ---------------- required by this bound in `call_it` ... LL | let x = call_it(&S, 22); | ^^ expected an `Fn<(isize,)>` closure, found `S` diff --git a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr index 0b86719df848a..b9ee9e460201a 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr @@ -2,7 +2,7 @@ error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `for<'r> unsaf --> $DIR/unboxed-closures-unsafe-extern-fn.rs:12:21 | LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----------------- required by this bound in `call_it` + | ----------------- required by this bound in `call_it` ... LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `Fn<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` @@ -13,7 +13,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> u --> $DIR/unboxed-closures-unsafe-extern-fn.rs:12:21 | LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----- required by this bound in `call_it` + | ----- required by this bound in `call_it` ... LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` @@ -24,7 +24,7 @@ error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `for<'r> un --> $DIR/unboxed-closures-unsafe-extern-fn.rs:18:25 | LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- -------------------- required by this bound in `call_it_mut` + | -------------------- required by this bound in `call_it_mut` ... LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnMut<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` @@ -35,7 +35,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> u --> $DIR/unboxed-closures-unsafe-extern-fn.rs:18:25 | LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- ----- required by this bound in `call_it_mut` + | ----- required by this bound in `call_it_mut` ... LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` @@ -46,7 +46,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> u --> $DIR/unboxed-closures-unsafe-extern-fn.rs:24:26 | LL | fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } - | ------------ ----- required by this bound in `call_it_once` + | ----- required by this bound in `call_it_once` ... LL | let z = call_it_once(square, 22); | ^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr index 17faf047c14e3..654b626cf65cc 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr @@ -2,7 +2,7 @@ error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `for<'r> exter --> $DIR/unboxed-closures-wrong-abi.rs:12:21 | LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----------------- required by this bound in `call_it` + | ----------------- required by this bound in `call_it` ... LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `Fn<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` @@ -13,7 +13,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> e --> $DIR/unboxed-closures-wrong-abi.rs:12:21 | LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----- required by this bound in `call_it` + | ----- required by this bound in `call_it` ... LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` @@ -24,7 +24,7 @@ error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `for<'r> ex --> $DIR/unboxed-closures-wrong-abi.rs:18:25 | LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- -------------------- required by this bound in `call_it_mut` + | -------------------- required by this bound in `call_it_mut` ... LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnMut<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` @@ -35,7 +35,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> e --> $DIR/unboxed-closures-wrong-abi.rs:18:25 | LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- ----- required by this bound in `call_it_mut` + | ----- required by this bound in `call_it_mut` ... LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` @@ -46,7 +46,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> e --> $DIR/unboxed-closures-wrong-abi.rs:24:26 | LL | fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } - | ------------ ----- required by this bound in `call_it_once` + | ----- required by this bound in `call_it_once` ... LL | let z = call_it_once(square, 22); | ^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr index 5b1d6eb5b681b..434c8a579f671 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr @@ -2,7 +2,7 @@ error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `unsafe fn(isi --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:13:21 | LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----------------- required by this bound in `call_it` + | ----------------- required by this bound in `call_it` ... LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `Fn<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` @@ -13,7 +13,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `unsafe fn --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:13:21 | LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----- required by this bound in `call_it` + | ----- required by this bound in `call_it` ... LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` @@ -24,7 +24,7 @@ error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `unsafe fn( --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:19:25 | LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- -------------------- required by this bound in `call_it_mut` + | -------------------- required by this bound in `call_it_mut` ... LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnMut<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` @@ -35,7 +35,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `unsafe fn --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:19:25 | LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- ----- required by this bound in `call_it_mut` + | ----- required by this bound in `call_it_mut` ... LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` @@ -46,7 +46,7 @@ error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `unsafe fn --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:25:26 | LL | fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } - | ------------ ----- required by this bound in `call_it_once` + | ----- required by this bound in `call_it_once` ... LL | let z = call_it_once(square, 22); | ^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` diff --git a/src/test/ui/underscore-imports/basic.rs b/src/test/ui/underscore-imports/basic.rs index 4766d75c8f412..c021ad5ee0d2b 100644 --- a/src/test/ui/underscore-imports/basic.rs +++ b/src/test/ui/underscore-imports/basic.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // aux-build:underscore-imports.rs #![warn(unused_imports, unused_extern_crates)] diff --git a/src/test/ui/underscore-imports/basic.stderr b/src/test/ui/underscore-imports/basic.stderr index 891730dc84387..c51493562ebd7 100644 --- a/src/test/ui/underscore-imports/basic.stderr +++ b/src/test/ui/underscore-imports/basic.stderr @@ -16,3 +16,5 @@ warning: unused import: `S as _` LL | use S as _; | ^^^^^^ +warning: 2 warnings emitted + diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr index fe242e6a909e3..2c595833cd1b0 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr @@ -18,4 +18,5 @@ LL | x: Box, error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0106`. +Some errors have detailed explanations: E0106, E0228. +For more information about an error, try `rustc --explain E0106`. diff --git a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr index ada4551baefff..594cdd245b3ec 100644 --- a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr +++ b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr @@ -14,15 +14,24 @@ error[E0106]: missing lifetime specifier --> $DIR/underscore-lifetime-binders.rs:2:17 | LL | struct Baz<'a>(&'_ &'a u8); - | ^^ help: consider using the named lifetime: `'a` + | ^^ expected named lifetime parameter + | +help: consider using the `'a` lifetime + | +LL | struct Baz<'a>(&'a &'a u8); + | ^^ error[E0106]: missing lifetime specifier --> $DIR/underscore-lifetime-binders.rs:10:33 | LL | fn meh() -> Box Meh<'_>> - | ^^ help: consider giving it a 'static lifetime: `'static` + | ^^ 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 + | +LL | fn meh() -> Box Meh<'static>> + | ^^^^^^^ error[E0106]: missing lifetime specifier --> $DIR/underscore-lifetime-binders.rs:16:35 diff --git a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr index 26e1be34ea75d..e1ff38f3057f1 100644 --- a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr @@ -15,6 +15,7 @@ LL | let Foo::D(_y) = x; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `Foo` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Foo::D(_y) = x { /* */ } diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs index a959aaae05568..e804afcf9ed99 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - use std::mem::zeroed; enum Void {} diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 9245e293caa85..c7bf6710d06bf 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/uninhabited-matches-feature-gated.rs:11:19 + --> $DIR/uninhabited-matches-feature-gated.rs:6:19 | LL | let _ = match x { | ^ pattern `Err(_)` not covered @@ -10,9 +10,10 @@ LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | --- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::result::Result` error[E0004]: non-exhaustive patterns: type `&Void` is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:20:19 + --> $DIR/uninhabited-matches-feature-gated.rs:15:19 | LL | enum Void {} | ------------ `Void` defined here @@ -21,33 +22,37 @@ LL | let _ = match x {}; | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&Void` error[E0004]: non-exhaustive patterns: type `(Void,)` is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:23:19 + --> $DIR/uninhabited-matches-feature-gated.rs:18:19 | LL | let _ = match x {}; | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `(Void,)` error[E0004]: non-exhaustive patterns: type `[Void; 1]` is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:26:19 + --> $DIR/uninhabited-matches-feature-gated.rs:21:19 | LL | let _ = match x {}; | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `[Void; 1]` error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/uninhabited-matches-feature-gated.rs:29:19 + --> $DIR/uninhabited-matches-feature-gated.rs:24:19 | LL | let _ = match x { | ^ pattern `&[_, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&[Void]` error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/uninhabited-matches-feature-gated.rs:37:19 + --> $DIR/uninhabited-matches-feature-gated.rs:32:19 | LL | let _ = match x { | ^ pattern `Err(_)` not covered @@ -58,9 +63,10 @@ LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | --- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::result::Result` error[E0005]: refutable pattern in local binding: `Err(_)` not covered - --> $DIR/uninhabited-matches-feature-gated.rs:42:9 + --> $DIR/uninhabited-matches-feature-gated.rs:37:9 | LL | let Ok(x) = x; | ^^^^^ pattern `Err(_)` not covered @@ -72,6 +78,7 @@ LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::result::Result` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Ok(x) = x { /* */ } diff --git a/src/test/ui/union/union-derive-clone.rs b/src/test/ui/union/union-derive-clone.rs index 4b92475f1e4cd..4a106cc940a18 100644 --- a/src/test/ui/union/union-derive-clone.rs +++ b/src/test/ui/union/union-derive-clone.rs @@ -1,8 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl - #![feature(untagged_unions)] use std::mem::ManuallyDrop; diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.stderr index d0a82a96c165c..b536325810a3b 100644 --- a/src/test/ui/union/union-derive-clone.stderr +++ b/src/test/ui/union/union-derive-clone.stderr @@ -1,14 +1,18 @@ error[E0277]: the trait bound `U1: std::marker::Copy` is not satisfied - --> $DIR/union-derive-clone.rs:10:10 + --> $DIR/union-derive-clone.rs:5:10 | LL | #[derive(Clone)] | ^^^^^ the trait `std::marker::Copy` is not implemented for `U1` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | pub struct AssertParamIsCopy { + | ---- required by this bound in `std::clone::AssertParamIsCopy` | - = note: required by `std::clone::AssertParamIsCopy` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: no method named `clone` found for union `U5` in the current scope - --> $DIR/union-derive-clone.rs:42:15 + --> $DIR/union-derive-clone.rs:37:15 | LL | union U5 { | ----------- diff --git a/src/test/ui/union/union-derive-eq.stderr b/src/test/ui/union/union-derive-eq.stderr index 0955c161871d2..ae0cd5af4b053 100644 --- a/src/test/ui/union/union-derive-eq.stderr +++ b/src/test/ui/union/union-derive-eq.stderr @@ -3,8 +3,12 @@ error[E0277]: the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied | LL | a: PartialEqNotEq, | ^^^^^^^^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `PartialEqNotEq` + | + ::: $SRC_DIR/libcore/cmp.rs:LL:COL + | +LL | pub struct AssertParamIsEq { + | -- required by this bound in `std::cmp::AssertParamIsEq` | - = note: required by `std::cmp::AssertParamIsEq` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/unique-object-noncopyable.rs b/src/test/ui/unique-object-noncopyable.rs index bedaf27c2dddf..dd38a7190aa0e 100644 --- a/src/test/ui/unique-object-noncopyable.rs +++ b/src/test/ui/unique-object-noncopyable.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl #![feature(box_syntax)] trait Foo { diff --git a/src/test/ui/unique-object-noncopyable.stderr b/src/test/ui/unique-object-noncopyable.stderr index 2e81e7cf83200..161e25bb8c5f1 100644 --- a/src/test/ui/unique-object-noncopyable.stderr +++ b/src/test/ui/unique-object-noncopyable.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `std::boxed::Box` in the current scope - --> $DIR/unique-object-noncopyable.rs:28:16 + --> $DIR/unique-object-noncopyable.rs:24:16 | LL | trait Foo { | --------- diff --git a/src/test/ui/unique-pinned-nocopy.rs b/src/test/ui/unique-pinned-nocopy.rs index 091b8a4386235..4c30450c70455 100644 --- a/src/test/ui/unique-pinned-nocopy.rs +++ b/src/test/ui/unique-pinned-nocopy.rs @@ -1,7 +1,3 @@ -// FIXME: missing sysroot spans (#53081) -// ignore-i586-unknown-linux-gnu -// ignore-i586-unknown-linux-musl -// ignore-i686-unknown-linux-musl #[derive(Debug)] struct R { b: bool, diff --git a/src/test/ui/unique-pinned-nocopy.stderr b/src/test/ui/unique-pinned-nocopy.stderr index 06c4b95baef56..38c110c04c479 100644 --- a/src/test/ui/unique-pinned-nocopy.stderr +++ b/src/test/ui/unique-pinned-nocopy.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `std::boxed::Box` in the current scope - --> $DIR/unique-pinned-nocopy.rs:16:16 + --> $DIR/unique-pinned-nocopy.rs:12:16 | LL | struct R { | -------- doesn't satisfy `R: std::clone::Clone` diff --git a/src/test/ui/unknown-lint-tool-name.stderr b/src/test/ui/unknown-lint-tool-name.stderr index 1940f61a47b68..414816d229cdb 100644 --- a/src/test/ui/unknown-lint-tool-name.stderr +++ b/src/test/ui/unknown-lint-tool-name.stderr @@ -36,3 +36,4 @@ LL | #[allow(foo::bar)] error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0710`. diff --git a/src/test/ui/unop-move-semantics.stderr b/src/test/ui/unop-move-semantics.stderr index ab641c40dfe44..e0499cfe95ce9 100644 --- a/src/test/ui/unop-move-semantics.stderr +++ b/src/test/ui/unop-move-semantics.stderr @@ -9,11 +9,10 @@ LL | LL | x.clone(); | ^ value borrowed here after move | -help: consider further restricting this bound with `+ Copy` - --> $DIR/unop-move-semantics.rs:5:24 +help: consider further restricting this bound | -LL | fn move_then_borrow + Clone>(x: T) { - | ^^^^^^^^^^^^^^^^^^^^^ +LL | fn move_then_borrow + Clone + Copy>(x: T) { + | ^^^^^^ error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/unop-move-semantics.rs:15:6 diff --git a/src/test/ui/unsized-locals/borrow-after-move.stderr b/src/test/ui/unsized-locals/borrow-after-move.stderr index 010e182674b75..110edab69be87 100644 --- a/src/test/ui/unsized-locals/borrow-after-move.stderr +++ b/src/test/ui/unsized-locals/borrow-after-move.stderr @@ -45,12 +45,12 @@ LL | println!("{}", &y); error[E0382]: borrow of moved value: `x` --> $DIR/borrow-after-move.rs:39:24 | +LL | let x = "hello".to_owned().into_boxed_str(); + | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait LL | x.foo(); | - value moved here LL | println!("{}", &x); - | ^^ value borrowed here after partial move - | - = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait + | ^^ value borrowed here after move error: aborting due to 5 previous errors diff --git a/src/test/ui/unsized-locals/double-move.stderr b/src/test/ui/unsized-locals/double-move.stderr index 47fa0d4a437ba..5b936fb64474f 100644 --- a/src/test/ui/unsized-locals/double-move.stderr +++ b/src/test/ui/unsized-locals/double-move.stderr @@ -38,25 +38,25 @@ LL | y.foo(); LL | y.foo(); | ^ value used here after move -error[E0382]: use of moved value: `*x` +error[E0382]: use of moved value: `x` --> $DIR/double-move.rs:45:9 | LL | let _y = *x; | -- value moved here LL | x.foo(); - | ^ value used here after move + | ^ value used here after partial move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait error[E0382]: use of moved value: `*x` --> $DIR/double-move.rs:51:18 | +LL | let x = "hello".to_owned().into_boxed_str(); + | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait LL | x.foo(); | - value moved here LL | let _y = *x; | ^^ value used here after move - | - = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait error: aborting due to 6 previous errors diff --git a/src/test/ui/unsized-locals/issue-30276-feature-flagged.rs b/src/test/ui/unsized-locals/issue-30276-feature-flagged.rs new file mode 100644 index 0000000000000..4193210b8bd3b --- /dev/null +++ b/src/test/ui/unsized-locals/issue-30276-feature-flagged.rs @@ -0,0 +1,7 @@ +#![feature(unsized_locals)] + +struct Test([i32]); + +fn main() { + let _x: fn(_) -> Test = Test; +} //~^the size for values of type `[i32]` cannot be known at compilation time diff --git a/src/test/ui/unsized-locals/issue-30276-feature-flagged.stderr b/src/test/ui/unsized-locals/issue-30276-feature-flagged.stderr new file mode 100644 index 0000000000000..35f63a91b2b53 --- /dev/null +++ b/src/test/ui/unsized-locals/issue-30276-feature-flagged.stderr @@ -0,0 +1,14 @@ +error[E0277]: the size for values of type `[i32]` cannot be known at compilation time + --> $DIR/issue-30276-feature-flagged.rs:6:29 + | +LL | let _x: fn(_) -> Test = Test; + | ^^^^ doesn't have a size known at compile-time + | + = help: within `Test`, the trait `std::marker::Sized` is not implemented for `[i32]` + = note: to learn more, visit + = note: required because it appears within the type `Test` + = note: the return type of a function must have a statically known size + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized-locals/issue-30276.rs b/src/test/ui/unsized-locals/issue-30276.rs new file mode 100644 index 0000000000000..9c4bf062a40e9 --- /dev/null +++ b/src/test/ui/unsized-locals/issue-30276.rs @@ -0,0 +1,5 @@ +struct Test([i32]); + +fn main() { + let _x: fn(_) -> Test = Test; +} //~^the size for values of type `[i32]` cannot be known at compilation time diff --git a/src/test/ui/unsized-locals/issue-30276.stderr b/src/test/ui/unsized-locals/issue-30276.stderr new file mode 100644 index 0000000000000..d42fddb3a4a26 --- /dev/null +++ b/src/test/ui/unsized-locals/issue-30276.stderr @@ -0,0 +1,14 @@ +error[E0277]: the size for values of type `[i32]` cannot be known at compilation time + --> $DIR/issue-30276.rs:4:29 + | +LL | let _x: fn(_) -> Test = Test; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[i32]` + = note: to learn more, visit + = note: all function arguments must have a statically known size + = help: unsized locals are gated as an unstable feature + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized-locals/unsized-exprs-rpass.rs b/src/test/ui/unsized-locals/unsized-exprs-rpass.rs index bc64fcdec2e39..24c2758a0a255 100644 --- a/src/test/ui/unsized-locals/unsized-exprs-rpass.rs +++ b/src/test/ui/unsized-locals/unsized-exprs-rpass.rs @@ -1,5 +1,5 @@ // run-pass - +#![allow(unused_braces, unused_parens)] #![feature(unsized_tuple_coercion, unsized_locals)] struct A(X); @@ -30,7 +30,6 @@ fn main() { *foo() }); udrop::<[u8]>({*foo()}); - #[allow(unused_parens)] udrop::<[u8]>((*foo())); udrop::<[u8]>((*tfoo()).1); *afoo() + 42; diff --git a/src/test/ui/unsized-locals/unsized-exprs2.stderr b/src/test/ui/unsized-locals/unsized-exprs2.stderr index 650285cb6740a..88269f237afb7 100644 --- a/src/test/ui/unsized-locals/unsized-exprs2.stderr +++ b/src/test/ui/unsized-locals/unsized-exprs2.stderr @@ -1,11 +1,11 @@ error[E0508]: cannot move out of type `[u8]`, a non-copy slice - --> $DIR/unsized-exprs2.rs:22:19 + --> $DIR/unsized-exprs2.rs:22:5 | LL | udrop::<[u8]>(foo()[..]); - | ^^^^^^^^^ - | | - | cannot move out of here - | move occurs because value has type `[u8]`, which does not implement the `Copy` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because value has type `[u8]`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/unsized/return-unsized-from-trait-method.rs b/src/test/ui/unsized/return-unsized-from-trait-method.rs new file mode 100644 index 0000000000000..2d265d5db5c14 --- /dev/null +++ b/src/test/ui/unsized/return-unsized-from-trait-method.rs @@ -0,0 +1,16 @@ +// ignore-tidy-linelength + +// regression test for #26376 + +trait Foo { + fn foo(&self) -> [u8]; +} + +fn foo(f: Option<&dyn Foo>) { + if let Some(f) = f { + let _ = f.foo(); + //~^ ERROR cannot move a value of type [u8]: the size of [u8] cannot be statically determined + } +} + +fn main() { foo(None) } diff --git a/src/test/ui/unsized/return-unsized-from-trait-method.stderr b/src/test/ui/unsized/return-unsized-from-trait-method.stderr new file mode 100644 index 0000000000000..7ecdd28616674 --- /dev/null +++ b/src/test/ui/unsized/return-unsized-from-trait-method.stderr @@ -0,0 +1,9 @@ +error[E0161]: cannot move a value of type [u8]: the size of [u8] cannot be statically determined + --> $DIR/return-unsized-from-trait-method.rs:11:17 + | +LL | let _ = f.foo(); + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0161`. diff --git a/src/test/ui/unsized/unsized-bare-typaram.stderr b/src/test/ui/unsized/unsized-bare-typaram.stderr index 772de23e64cf0..3ff6f30db2a84 100644 --- a/src/test/ui/unsized/unsized-bare-typaram.stderr +++ b/src/test/ui/unsized/unsized-bare-typaram.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/unsized-bare-typaram.rs:2:29 | LL | fn bar() { } - | --- - required by this bound in `bar` + | - required by this bound in `bar` LL | fn foo() { bar::() } | - ^ doesn't have a size known at compile-time | | diff --git a/src/test/ui/unsized/unsized-enum.stderr b/src/test/ui/unsized/unsized-enum.stderr index 88f7b1f77aee0..f43d00f97398d 100644 --- a/src/test/ui/unsized/unsized-enum.stderr +++ b/src/test/ui/unsized/unsized-enum.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/unsized-enum.rs:6:36 | LL | enum Foo { FooSome(U), FooNone } - | ----------- required by `Foo` + | - required by this bound in `Foo` LL | fn foo1() { not_sized::>() } // Hunky dory. LL | fn foo2() { not_sized::>() } | - ^^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr index 5688ae5b89a04..808c9c583d458 100644 --- a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized-inherent-impl-self-type.rs:7:17 | LL | struct S5(Y); - | ---------------- required by `S5` + | - required by this bound in `S5` LL | LL | impl S5 { | - ^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/unsized/unsized-struct.stderr b/src/test/ui/unsized/unsized-struct.stderr index 653fb5c1ae8dc..42fc5569eceb4 100644 --- a/src/test/ui/unsized/unsized-struct.stderr +++ b/src/test/ui/unsized/unsized-struct.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/unsized-struct.rs:6:36 | LL | struct Foo { data: T } - | ------------- required by `Foo` + | - required by this bound in `Foo` LL | fn foo1() { not_sized::>() } // Hunky dory. LL | fn foo2() { not_sized::>() } | - ^^^^^^ doesn't have a size known at compile-time @@ -16,7 +16,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/unsized-struct.rs:13:24 | LL | fn is_sized() { } - | -------- - required by this bound in `is_sized` + | - required by this bound in `is_sized` ... LL | fn bar2() { is_sized::>() } | - ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr index 3597073e7e6c6..c2b2fe40ce67f 100644 --- a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized-trait-impl-self-type.rs:10:17 | LL | struct S5(Y); - | ---------------- required by `S5` + | - required by this bound in `S5` LL | LL | impl T3 for S5 { | - ^^^^^ doesn't have a size known at compile-time diff --git a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr index b37d9f9d5369e..4cf054d177f66 100644 --- a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr @@ -1,6 +1,9 @@ error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized-trait-impl-trait-arg.rs:8:17 | +LL | trait T2 { + | - required by this bound in `T2` +... LL | impl T2 for S4 { | - ^^^^^ doesn't have a size known at compile-time | | diff --git a/src/test/ui/unsized3.stderr b/src/test/ui/unsized3.stderr index 083c74ba1e05d..828e8bc9f4aa1 100644 --- a/src/test/ui/unsized3.stderr +++ b/src/test/ui/unsized3.stderr @@ -7,7 +7,7 @@ LL | f2::(x); | ^ doesn't have a size known at compile-time ... LL | fn f2(x: &X) { - | -- - required by this bound in `f2` + | - required by this bound in `f2` | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit @@ -25,20 +25,20 @@ LL | f4::(x); | ^ doesn't have a size known at compile-time ... LL | fn f4(x: &X) { - | -- - required by this bound in `f4` + | - required by this bound in `f4` | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit help: consider relaxing the implicit `Sized` restriction | -LL | fn f4(x: &X) { - | ^^^^^^^^^ +LL | fn f4(x: &X) { + | ^^^^^^^^ error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized3.rs:33:8 | LL | fn f5(x: &Y) {} - | -- - required by this bound in `f5` + | - required by this bound in `f5` ... LL | fn f8(x1: &S, x2: &S) { | - this type parameter needs to be `std::marker::Sized` @@ -80,7 +80,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized3.rs:45:8 | LL | fn f5(x: &Y) {} - | -- - required by this bound in `f5` + | - required by this bound in `f5` ... LL | fn f10(x1: Box>) { | - this type parameter needs to be `std::marker::Sized` diff --git a/src/test/ui/unsized7.stderr b/src/test/ui/unsized7.stderr index 0f71c5f6f8fe6..d18644f005a88 100644 --- a/src/test/ui/unsized7.stderr +++ b/src/test/ui/unsized7.stderr @@ -1,6 +1,9 @@ error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized7.rs:12:21 | +LL | trait T1 { + | - required by this bound in `T1` +... LL | impl T1 for S3 { | - ^^^^^ doesn't have a size known at compile-time | | diff --git a/src/test/ui/unspecified-self-in-trait-ref.stderr b/src/test/ui/unspecified-self-in-trait-ref.stderr index e057a7842b2aa..9310b3d7ede00 100644 --- a/src/test/ui/unspecified-self-in-trait-ref.stderr +++ b/src/test/ui/unspecified-self-in-trait-ref.stderr @@ -31,7 +31,7 @@ LL | | } | |_- type parameter `A` must be specified for this ... LL | let e = Bar::::lol(); - | ^^^ missing reference to `A` + | ^^^^^^^^^^^^^^^^^ missing reference to `A` | = note: because of the default `Self` reference, type parameters must be specified on object types diff --git a/src/test/ui/use/use-keyword.stderr b/src/test/ui/use/use-keyword.stderr index 62b6a77fbfb98..501d14be52177 100644 --- a/src/test/ui/use/use-keyword.stderr +++ b/src/test/ui/use/use-keyword.stderr @@ -2,7 +2,7 @@ error[E0429]: `self` imports are only allowed within a { } list --> $DIR/use-keyword.rs:6:13 | LL | use self as A; - | ^^^^^^^^^ + | ^^^^ error[E0432]: unresolved import `super` --> $DIR/use-keyword.rs:8:13 diff --git a/src/test/ui/use/use-mod/use-mod-4.stderr b/src/test/ui/use/use-mod/use-mod-4.stderr index e30e5c3ceb162..a29bd07ac4419 100644 --- a/src/test/ui/use/use-mod/use-mod-4.stderr +++ b/src/test/ui/use/use-mod/use-mod-4.stderr @@ -1,20 +1,38 @@ error[E0429]: `self` imports are only allowed within a { } list - --> $DIR/use-mod-4.rs:1:5 + --> $DIR/use-mod-4.rs:1:8 | LL | use foo::self; - | ^^^^^^^^^ + | ^^^^^^ + | +help: consider importing the module directly + | +LL | use foo; + | -- +help: alternatively, use the multi-path `use` syntax to import `self` + | +LL | use foo::{self}; + | ^ ^ error[E0429]: `self` imports are only allowed within a { } list - --> $DIR/use-mod-4.rs:4:5 + --> $DIR/use-mod-4.rs:4:13 | LL | use std::mem::self; - | ^^^^^^^^^^^^^^ + | ^^^^^^ + | +help: consider importing the module directly + | +LL | use std::mem; + | -- +help: alternatively, use the multi-path `use` syntax to import `self` + | +LL | use std::mem::{self}; + | ^ ^ error[E0432]: unresolved import `foo` --> $DIR/use-mod-4.rs:1:5 | LL | use foo::self; - | ^^^ maybe a missing crate `foo`? + | ^^^^^^^^^ no `foo` in the root error: aborting due to 3 previous errors diff --git a/src/test/ui/use/use-mod/use-mod-5.rs b/src/test/ui/use/use-mod/use-mod-5.rs new file mode 100644 index 0000000000000..df5b423ec57e6 --- /dev/null +++ b/src/test/ui/use/use-mod/use-mod-5.rs @@ -0,0 +1,13 @@ +mod foo { + pub mod bar { + pub fn drop() {} + } +} + +use foo::bar::self; +//~^ ERROR `self` imports are only allowed within a { } list + +fn main() { + // Because of error recovery this shouldn't error + bar::drop(); +} diff --git a/src/test/ui/use/use-mod/use-mod-5.stderr b/src/test/ui/use/use-mod/use-mod-5.stderr new file mode 100644 index 0000000000000..ebb71c51293ec --- /dev/null +++ b/src/test/ui/use/use-mod/use-mod-5.stderr @@ -0,0 +1,18 @@ +error[E0429]: `self` imports are only allowed within a { } list + --> $DIR/use-mod-5.rs:7:13 + | +LL | use foo::bar::self; + | ^^^^^^ + | +help: consider importing the module directly + | +LL | use foo::bar; + | -- +help: alternatively, use the multi-path `use` syntax to import `self` + | +LL | use foo::bar::{self}; + | ^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0429`. diff --git a/src/test/ui/use/use-mod/use-mod-6.rs b/src/test/ui/use/use-mod/use-mod-6.rs new file mode 100644 index 0000000000000..1f8777daca491 --- /dev/null +++ b/src/test/ui/use/use-mod/use-mod-6.rs @@ -0,0 +1,13 @@ +mod foo { + pub mod bar { + pub fn drop() {} + } +} + +use foo::bar::self as abc; +//~^ ERROR `self` imports are only allowed within a { } list + +fn main() { + // Because of error recovery this shouldn't error + abc::drop(); +} diff --git a/src/test/ui/use/use-mod/use-mod-6.stderr b/src/test/ui/use/use-mod/use-mod-6.stderr new file mode 100644 index 0000000000000..36fdf9c75c704 --- /dev/null +++ b/src/test/ui/use/use-mod/use-mod-6.stderr @@ -0,0 +1,18 @@ +error[E0429]: `self` imports are only allowed within a { } list + --> $DIR/use-mod-6.rs:7:13 + | +LL | use foo::bar::self as abc; + | ^^^^^^ + | +help: consider importing the module directly + | +LL | use foo::bar as abc; + | -- +help: alternatively, use the multi-path `use` syntax to import `self` + | +LL | use foo::bar::{self as abc}; + | ^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0429`. diff --git a/src/test/ui/use/use-super-global-path.stderr b/src/test/ui/use/use-super-global-path.stderr index 7f98ac7cd0fef..edde26c1fc15d 100644 --- a/src/test/ui/use/use-super-global-path.stderr +++ b/src/test/ui/use/use-super-global-path.stderr @@ -22,7 +22,7 @@ error[E0425]: cannot find function `main` in this scope LL | main(); | ^^^^ not found in this scope | -help: possible candidate is found in another module, you can import it into scope +help: consider importing this function | LL | use main; | diff --git a/src/test/ui/useless-comment.stderr b/src/test/ui/useless-comment.stderr index 92817321a88f0..5a0af8db7c504 100644 --- a/src/test/ui/useless-comment.stderr +++ b/src/test/ui/useless-comment.stderr @@ -2,7 +2,7 @@ error: unused doc comment --> $DIR/useless-comment.rs:9:1 | LL | /// foo - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macros + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations | note: the lint level is defined here --> $DIR/useless-comment.rs:3:9 @@ -15,7 +15,7 @@ error: unused doc comment --> $DIR/useless-comment.rs:32:5 | LL | /// bar - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macros + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations | = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion diff --git a/src/test/ui/utf8_idents.stderr b/src/test/ui/utf8_idents.stderr index 8defb2c2983ef..877412df8fa1c 100644 --- a/src/test/ui/utf8_idents.stderr +++ b/src/test/ui/utf8_idents.stderr @@ -42,6 +42,6 @@ LL | γ | = note: `#[warn(non_camel_case_types)]` on by default -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/run-fail/vec-overrun.rs b/src/test/ui/vec/vec-overrun.rs similarity index 83% rename from src/test/run-fail/vec-overrun.rs rename to src/test/ui/vec/vec-overrun.rs index 2be945f736cb7..bdc7d507d5305 100644 --- a/src/test/run-fail/vec-overrun.rs +++ b/src/test/ui/vec/vec-overrun.rs @@ -1,5 +1,6 @@ +// run-fail // error-pattern:index out of bounds: the len is 1 but the index is 2 - +// ignore-emscripten no processes fn main() { let v: Vec = vec![10]; diff --git a/src/test/ui/weird-exprs.rs b/src/test/ui/weird-exprs.rs index ca68a5af0ddca..d812bbd011e0e 100644 --- a/src/test/ui/weird-exprs.rs +++ b/src/test/ui/weird-exprs.rs @@ -5,7 +5,7 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] #![allow(unreachable_code)] -#![allow(unused_parens)] +#![allow(unused_braces, unused_parens)] #![recursion_limit = "256"] diff --git a/src/test/ui/wf/wf-const-type.stderr b/src/test/ui/wf/wf-const-type.stderr index 531aadc25dd2a..1b7f8b6fca100 100644 --- a/src/test/ui/wf/wf-const-type.stderr +++ b/src/test/ui/wf/wf-const-type.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `NotCopy: std::marker::Copy` is not satisfied --> $DIR/wf-const-type.rs:10:12 | LL | struct IsCopy { t: T } - | --------------------- required by `IsCopy` + | ---- required by this bound in `IsCopy` ... LL | const FOO: IsCopy> = IsCopy { t: None }; | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `NotCopy` diff --git a/src/test/ui/wf/wf-enum-bound.stderr b/src/test/ui/wf/wf-enum-bound.stderr index be64ddb975994..962a1b839a792 100644 --- a/src/test/ui/wf/wf-enum-bound.stderr +++ b/src/test/ui/wf/wf-enum-bound.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied --> $DIR/wf-enum-bound.rs:10:14 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` ... LL | where T: ExtraCopy | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` | -help: consider restricting this type parameter with `where U: std::marker::Copy` - --> $DIR/wf-enum-bound.rs:9:17 +help: consider further restricting type parameter `U` | -LL | enum SomeEnum - | ^ +LL | where T: ExtraCopy, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-enum-fields-struct-variant.stderr b/src/test/ui/wf/wf-enum-fields-struct-variant.stderr index 40454b33b7b76..0a3665fcf0436 100644 --- a/src/test/ui/wf/wf-enum-fields-struct-variant.stderr +++ b/src/test/ui/wf/wf-enum-fields-struct-variant.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `A: std::marker::Copy` is not satisfied --> $DIR/wf-enum-fields-struct-variant.rs:13:9 | LL | struct IsCopy { - | --------------------- required by `IsCopy` + | ---- required by this bound in `IsCopy` ... LL | f: IsCopy | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `A` | -help: consider restricting this type parameter with `A: std::marker::Copy` - --> $DIR/wf-enum-fields-struct-variant.rs:11:18 +help: consider restricting type parameter `A` | -LL | enum AnotherEnum { - | ^ +LL | enum AnotherEnum { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-enum-fields.stderr b/src/test/ui/wf/wf-enum-fields.stderr index e2612add776d4..a833eeacbdf9c 100644 --- a/src/test/ui/wf/wf-enum-fields.stderr +++ b/src/test/ui/wf/wf-enum-fields.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `A: std::marker::Copy` is not satisfied --> $DIR/wf-enum-fields.rs:12:17 | LL | struct IsCopy { - | --------------------- required by `IsCopy` + | ---- required by this bound in `IsCopy` ... LL | SomeVariant(IsCopy) | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `A` | -help: consider restricting this type parameter with `A: std::marker::Copy` - --> $DIR/wf-enum-fields.rs:11:15 +help: consider restricting type parameter `A` | -LL | enum SomeEnum { - | ^ +LL | enum SomeEnum { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-fn-where-clause.rs b/src/test/ui/wf/wf-fn-where-clause.rs index 5fd567bd32d04..adae536138b60 100644 --- a/src/test/ui/wf/wf-fn-where-clause.rs +++ b/src/test/ui/wf/wf-fn-where-clause.rs @@ -13,5 +13,8 @@ fn bar() where Vec:, {} //~^ ERROR E0277 //~| ERROR E0038 +struct Vec { + t: T, +} fn main() { } diff --git a/src/test/ui/wf/wf-fn-where-clause.stderr b/src/test/ui/wf/wf-fn-where-clause.stderr index 2a4f2df5a8986..59a4f9bad9dfa 100644 --- a/src/test/ui/wf/wf-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-fn-where-clause.stderr @@ -2,26 +2,27 @@ error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied --> $DIR/wf-fn-where-clause.rs:8:24 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` LL | LL | fn foo() where T: ExtraCopy | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` | -help: consider restricting this type parameter with `where U: std::marker::Copy` - --> $DIR/wf-fn-where-clause.rs:8:10 +help: consider further restricting type parameter `U` | -LL | fn foo() where T: ExtraCopy - | ^ +LL | fn foo() where T: ExtraCopy, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `(dyn std::marker::Copy + 'static)` cannot be known at compilation time --> $DIR/wf-fn-where-clause.rs:12:16 | LL | fn bar() where Vec:, {} | ^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Vec { + | - required by this bound in `Vec` | = help: the trait `std::marker::Sized` is not implemented for `(dyn std::marker::Copy + 'static)` = note: to learn more, visit - = note: required by `std::vec::Vec` error[E0038]: the trait `std::marker::Copy` cannot be made into an object --> $DIR/wf-fn-where-clause.rs:12:16 diff --git a/src/test/ui/wf/wf-impl-associated-type-trait.stderr b/src/test/ui/wf/wf-impl-associated-type-trait.stderr index 7774299b39357..89399e30cad45 100644 --- a/src/test/ui/wf/wf-impl-associated-type-trait.stderr +++ b/src/test/ui/wf/wf-impl-associated-type-trait.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: MyHash` is not satisfied --> $DIR/wf-impl-associated-type-trait.rs:17:5 | LL | pub struct MySet { - | -------------------------- required by `MySet` + | ------ required by this bound in `MySet` ... LL | type Bar = MySet; | ^^^^^^^^^^^^^^^^^^^^ the trait `MyHash` is not implemented for `T` | -help: consider restricting this type parameter with `T: MyHash` - --> $DIR/wf-impl-associated-type-trait.rs:16:6 +help: consider restricting type parameter `T` | -LL | impl Foo for T { - | ^ +LL | impl Foo for T { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-arg.stderr b/src/test/ui/wf/wf-in-fn-arg.stderr index c1a6657e63be7..84adf04a68a2b 100644 --- a/src/test/ui/wf/wf-in-fn-arg.stderr +++ b/src/test/ui/wf/wf-in-fn-arg.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/wf-in-fn-arg.rs:10:14 | LL | struct MustBeCopy { - | ------------------------- required by `MustBeCopy` + | ---- required by this bound in `MustBeCopy` ... LL | fn bar(_: &MustBeCopy) | ^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/wf-in-fn-arg.rs:10:8 +help: consider restricting type parameter `T` | -LL | fn bar(_: &MustBeCopy) - | ^ +LL | fn bar(_: &MustBeCopy) + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-ret.stderr b/src/test/ui/wf/wf-in-fn-ret.stderr index 754d64df0194b..68ef734be5599 100644 --- a/src/test/ui/wf/wf-in-fn-ret.stderr +++ b/src/test/ui/wf/wf-in-fn-ret.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/wf-in-fn-ret.rs:10:16 | LL | struct MustBeCopy { - | ------------------------- required by `MustBeCopy` + | ---- required by this bound in `MustBeCopy` ... LL | fn bar() -> MustBeCopy | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/wf-in-fn-ret.rs:10:8 +help: consider restricting type parameter `T` | -LL | fn bar() -> MustBeCopy - | ^ +LL | fn bar() -> MustBeCopy + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-type-arg.stderr b/src/test/ui/wf/wf-in-fn-type-arg.stderr index 97a5c0fd913a4..c0bb3a50b1f1e 100644 --- a/src/test/ui/wf/wf-in-fn-type-arg.stderr +++ b/src/test/ui/wf/wf-in-fn-type-arg.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/wf-in-fn-type-arg.rs:9:5 | LL | struct MustBeCopy { - | ------------------------- required by `MustBeCopy` + | ---- required by this bound in `MustBeCopy` ... LL | x: fn(MustBeCopy) | ^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/wf-in-fn-type-arg.rs:7:12 +help: consider restricting type parameter `T` | -LL | struct Bar { - | ^ +LL | struct Bar { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-type-ret.stderr b/src/test/ui/wf/wf-in-fn-type-ret.stderr index 527b000edf883..e203058250790 100644 --- a/src/test/ui/wf/wf-in-fn-type-ret.stderr +++ b/src/test/ui/wf/wf-in-fn-type-ret.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/wf-in-fn-type-ret.rs:9:5 | LL | struct MustBeCopy { - | ------------------------- required by `MustBeCopy` + | ---- required by this bound in `MustBeCopy` ... LL | x: fn() -> MustBeCopy | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/wf-in-fn-type-ret.rs:7:12 +help: consider restricting type parameter `T` | -LL | struct Foo { - | ^ +LL | struct Foo { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-where-clause.stderr b/src/test/ui/wf/wf-in-fn-where-clause.stderr index 62c672a21e86e..41cfb1863104b 100644 --- a/src/test/ui/wf/wf-in-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-in-fn-where-clause.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied --> $DIR/wf-in-fn-where-clause.rs:10:14 | LL | trait MustBeCopy { - | ------------------------ required by `MustBeCopy` + | ---- required by this bound in `MustBeCopy` ... LL | where T: MustBeCopy | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` | -help: consider restricting this type parameter with `where U: std::marker::Copy` - --> $DIR/wf-in-fn-where-clause.rs:9:10 +help: consider further restricting type parameter `U` | -LL | fn bar() - | ^ +LL | where T: MustBeCopy, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-obj-type-trait.stderr b/src/test/ui/wf/wf-in-obj-type-trait.stderr index 1b6438cdc2477..6d85cdde7f991 100644 --- a/src/test/ui/wf/wf-in-obj-type-trait.stderr +++ b/src/test/ui/wf/wf-in-obj-type-trait.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/wf-in-obj-type-trait.rs:11:5 | LL | struct MustBeCopy { - | ------------------------- required by `MustBeCopy` + | ---- required by this bound in `MustBeCopy` ... LL | x: dyn Object> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/wf-in-obj-type-trait.rs:9:12 +help: consider restricting type parameter `T` | -LL | struct Bar { - | ^ +LL | struct Bar { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr b/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr index 70337ee40eacf..1a2a20ec6845c 100644 --- a/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr +++ b/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied --> $DIR/wf-inherent-impl-method-where-clause.rs:12:27 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` ... LL | fn foo(self) where T: ExtraCopy | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` | -help: consider restricting this type parameter with `U: std::marker::Copy` - --> $DIR/wf-inherent-impl-method-where-clause.rs:11:8 +help: consider restricting type parameter `U` | -LL | impl Foo { - | ^ +LL | impl Foo { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-inherent-impl-where-clause.stderr b/src/test/ui/wf/wf-inherent-impl-where-clause.stderr index c26d0ef787195..ba1d4a036c6bf 100644 --- a/src/test/ui/wf/wf-inherent-impl-where-clause.stderr +++ b/src/test/ui/wf/wf-inherent-impl-where-clause.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied --> $DIR/wf-inherent-impl-where-clause.rs:11:29 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` ... LL | impl Foo where T: ExtraCopy | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` | -help: consider restricting this type parameter with `where U: std::marker::Copy` - --> $DIR/wf-inherent-impl-where-clause.rs:11:8 +help: consider further restricting type parameter `U` | -LL | impl Foo where T: ExtraCopy - | ^ +LL | impl Foo where T: ExtraCopy, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-static-type.stderr b/src/test/ui/wf/wf-static-type.stderr index 05a628d7c3e19..4e78090f99848 100644 --- a/src/test/ui/wf/wf-static-type.stderr +++ b/src/test/ui/wf/wf-static-type.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `NotCopy: std::marker::Copy` is not satisfied --> $DIR/wf-static-type.rs:10:13 | LL | struct IsCopy { t: T } - | --------------------- required by `IsCopy` + | ---- required by this bound in `IsCopy` ... LL | static FOO: IsCopy> = IsCopy { t: None }; | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `NotCopy` diff --git a/src/test/ui/wf/wf-struct-bound.stderr b/src/test/ui/wf/wf-struct-bound.stderr index 545e4f870954c..d9d193aa79d0c 100644 --- a/src/test/ui/wf/wf-struct-bound.stderr +++ b/src/test/ui/wf/wf-struct-bound.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied --> $DIR/wf-struct-bound.rs:10:14 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` ... LL | where T: ExtraCopy | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` | -help: consider restricting this type parameter with `where U: std::marker::Copy` - --> $DIR/wf-struct-bound.rs:9:21 +help: consider further restricting type parameter `U` | -LL | struct SomeStruct - | ^ +LL | where T: ExtraCopy, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-struct-field.stderr b/src/test/ui/wf/wf-struct-field.stderr index f0ebdfba2ffc6..cda3b8fe4fddb 100644 --- a/src/test/ui/wf/wf-struct-field.stderr +++ b/src/test/ui/wf/wf-struct-field.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `A: std::marker::Copy` is not satisfied --> $DIR/wf-struct-field.rs:12:5 | LL | struct IsCopy { - | --------------------- required by `IsCopy` + | ---- required by this bound in `IsCopy` ... LL | data: IsCopy | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `A` | -help: consider restricting this type parameter with `A: std::marker::Copy` - --> $DIR/wf-struct-field.rs:11:19 +help: consider restricting type parameter `A` | -LL | struct SomeStruct { - | ^ +LL | struct SomeStruct { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-associated-type-bound.stderr b/src/test/ui/wf/wf-trait-associated-type-bound.stderr index dfccd4865686e..a4ceb41ffa5b9 100644 --- a/src/test/ui/wf/wf-trait-associated-type-bound.stderr +++ b/src/test/ui/wf/wf-trait-associated-type-bound.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/wf-trait-associated-type-bound.rs:10:17 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` ... LL | type Type1: ExtraCopy; | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/wf-trait-associated-type-bound.rs:9:17 +help: consider restricting type parameter `T` | -LL | trait SomeTrait { - | ^ +LL | trait SomeTrait { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-associated-type-trait.stderr b/src/test/ui/wf/wf-trait-associated-type-trait.stderr index 93cb948cdbfcb..e2892e3d24888 100644 --- a/src/test/ui/wf/wf-trait-associated-type-trait.stderr +++ b/src/test/ui/wf/wf-trait-associated-type-trait.stderr @@ -2,13 +2,15 @@ error[E0277]: the trait bound `::Type1: std::marker::Copy` is --> $DIR/wf-trait-associated-type-trait.rs:11:5 | LL | struct IsCopy { x: T } - | --------------------- required by `IsCopy` -LL | -LL | trait SomeTrait { - | - help: consider further restricting the associated type: `where ::Type1: std::marker::Copy` -LL | type Type1; + | ---- required by this bound in `IsCopy` +... LL | type Type2 = IsCopy; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `::Type1` + | +help: consider further restricting the associated type + | +LL | trait SomeTrait where ::Type1: std::marker::Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-bound.stderr b/src/test/ui/wf/wf-trait-bound.stderr index 31faa14426a1f..384d668d80043 100644 --- a/src/test/ui/wf/wf-trait-bound.stderr +++ b/src/test/ui/wf/wf-trait-bound.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied --> $DIR/wf-trait-bound.rs:10:14 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` ... LL | where T: ExtraCopy | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` | -help: consider restricting this type parameter with `where U: std::marker::Copy` - --> $DIR/wf-trait-bound.rs:9:19 +help: consider further restricting type parameter `U` | -LL | trait SomeTrait - | ^ +LL | where T: ExtraCopy, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-default-fn-arg.stderr b/src/test/ui/wf/wf-trait-default-fn-arg.stderr index 6a97d31cf3e65..23d886e25ff15 100644 --- a/src/test/ui/wf/wf-trait-default-fn-arg.stderr +++ b/src/test/ui/wf/wf-trait-default-fn-arg.stderr @@ -2,12 +2,15 @@ error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied --> $DIR/wf-trait-default-fn-arg.rs:11:22 | LL | struct Bar { value: Box } - | ----------------------- required by `Bar` + | -- required by this bound in `Bar` ... LL | fn bar(&self, x: &Bar) { - | ^^^^^^^^^^ - help: consider further restricting `Self`: `where Self: std::cmp::Eq` - | | - | the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn bar(&self, x: &Bar) where Self: std::cmp::Eq { + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-default-fn-ret.stderr b/src/test/ui/wf/wf-trait-default-fn-ret.stderr index 36c1e486269f6..0214064017204 100644 --- a/src/test/ui/wf/wf-trait-default-fn-ret.stderr +++ b/src/test/ui/wf/wf-trait-default-fn-ret.stderr @@ -2,12 +2,15 @@ error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied --> $DIR/wf-trait-default-fn-ret.rs:11:22 | LL | struct Bar { value: Box } - | ----------------------- required by `Bar` + | -- required by this bound in `Bar` ... LL | fn bar(&self) -> Bar { - | ^^^^^^^^^- help: consider further restricting `Self`: `where Self: std::cmp::Eq` - | | - | the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn bar(&self) -> Bar where Self: std::cmp::Eq { + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr b/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr index 6b63feaba89a3..3664c8b25aaef 100644 --- a/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr @@ -2,12 +2,15 @@ error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied --> $DIR/wf-trait-default-fn-where-clause.rs:11:31 | LL | trait Bar { } - | ---------------------- required by `Bar` + | -- required by this bound in `Bar` ... LL | fn bar(&self) where A: Bar { - | ^^^^^^^^^- help: consider further restricting `Self`: `, Self: std::cmp::Eq` - | | - | the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn bar(&self) where A: Bar, Self: std::cmp::Eq { + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-fn-arg.stderr b/src/test/ui/wf/wf-trait-fn-arg.stderr index 69e2ab72912d4..b5f5f70ce8176 100644 --- a/src/test/ui/wf/wf-trait-fn-arg.stderr +++ b/src/test/ui/wf/wf-trait-fn-arg.stderr @@ -2,12 +2,15 @@ error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied --> $DIR/wf-trait-fn-arg.rs:10:22 | LL | struct Bar { value: Box } - | ----------------------- required by `Bar` + | -- required by this bound in `Bar` ... LL | fn bar(&self, x: &Bar); - | ^^^^^^^^^^ - help: consider further restricting `Self`: `where Self: std::cmp::Eq` - | | - | the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn bar(&self, x: &Bar) where Self: std::cmp::Eq; + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-fn-ret.stderr b/src/test/ui/wf/wf-trait-fn-ret.stderr index bfc6265662e48..5e7d8cbfea610 100644 --- a/src/test/ui/wf/wf-trait-fn-ret.stderr +++ b/src/test/ui/wf/wf-trait-fn-ret.stderr @@ -2,12 +2,15 @@ error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied --> $DIR/wf-trait-fn-ret.rs:10:22 | LL | struct Bar { value: Box } - | ----------------------- required by `Bar` + | -- required by this bound in `Bar` ... LL | fn bar(&self) -> &Bar; - | ^^^^^^^^^^- help: consider further restricting `Self`: `where Self: std::cmp::Eq` - | | - | the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn bar(&self) -> &Bar where Self: std::cmp::Eq; + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-fn-where-clause.stderr b/src/test/ui/wf/wf-trait-fn-where-clause.stderr index ec8f02c9c4fed..2d9ba56c58548 100644 --- a/src/test/ui/wf/wf-trait-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-trait-fn-where-clause.stderr @@ -2,12 +2,15 @@ error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied --> $DIR/wf-trait-fn-where-clause.rs:10:49 | LL | struct Bar { value: Box } - | ----------------------- required by `Bar` + | -- required by this bound in `Bar` ... LL | fn bar(&self) where Self: Sized, Bar: Copy; - | ^^^^- help: consider further restricting `Self`: `, Self: std::cmp::Eq` - | | - | the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | fn bar(&self) where Self: Sized, Bar: Copy, Self: std::cmp::Eq; + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-superbound.stderr b/src/test/ui/wf/wf-trait-superbound.stderr index 372a5f8ba5db7..7d99b8f5a2ae8 100644 --- a/src/test/ui/wf/wf-trait-superbound.stderr +++ b/src/test/ui/wf/wf-trait-superbound.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/wf-trait-superbound.rs:9:21 | LL | trait ExtraCopy { } - | ----------------------- required by `ExtraCopy` + | ---- required by this bound in `ExtraCopy` LL | LL | trait SomeTrait: ExtraCopy { | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/wf-trait-superbound.rs:9:17 +help: consider restricting type parameter `T` | -LL | trait SomeTrait: ExtraCopy { - | ^ +LL | trait SomeTrait: ExtraCopy { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr index c6f12e7753c82..7e88107470282 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/where-clause-constraints-are-local-for-inherent-impl.rs:13:22 | LL | fn require_copy(x: T) {} - | ------------ ---- required by this bound in `require_copy` + | ---- required by this bound in `require_copy` ... LL | require_copy(self.x); | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/where-clause-constraints-are-local-for-inherent-impl.rs:6:6 +help: consider restricting type parameter `T` | -LL | impl Foo { - | ^ +LL | impl Foo { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr index 95688d6f2e485..3558d779d9ddf 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr @@ -2,16 +2,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/where-clause-constraints-are-local-for-trait-impl.rs:18:22 | LL | fn require_copy(x: T) {} - | ------------ ---- required by this bound in `require_copy` + | ---- required by this bound in `require_copy` ... LL | require_copy(self.x); | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -help: consider restricting this type parameter with `T: std::marker::Copy` - --> $DIR/where-clause-constraints-are-local-for-trait-impl.rs:11:6 +help: consider restricting type parameter `T` | -LL | impl Foo for Bar { - | ^ +LL | impl Foo for Bar { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr b/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr index 1c859ac648c3b..e92a8fc83df03 100644 --- a/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr +++ b/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `Struct: std::cmp::Eq` is not satisfied --> $DIR/where-clauses-unsatisfied.rs:6:10 | LL | fn equal(a: &T, b: &T) -> bool where T : Eq { a == b } - | ----- -- required by this bound in `equal` + | -- required by this bound in `equal` ... LL | drop(equal(&Struct, &Struct)) | ^^^^^ the trait `std::cmp::Eq` is not implemented for `Struct` diff --git a/src/test/ui/where-clauses/where-for-self-2.stderr b/src/test/ui/where-clauses/where-for-self-2.stderr index b18b36d029d70..9976243b200dc 100644 --- a/src/test/ui/where-clauses/where-for-self-2.stderr +++ b/src/test/ui/where-clauses/where-for-self-2.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `for<'a> &'a _: Bar` is not satisfied --> $DIR/where-for-self-2.rs:21:5 | LL | fn foo(x: &T) - | --- + | --- required by a bound in this LL | where for<'a> &'a T: Bar | --- required by this bound in `foo` ... diff --git a/src/test/ui/while-let.stderr b/src/test/ui/while-let.stderr index b2f2ec97c1467..867c95c0e023d 100644 --- a/src/test/ui/while-let.stderr +++ b/src/test/ui/while-let.stderr @@ -34,3 +34,5 @@ LL | | break; LL | | } | |_____^ +warning: 3 warnings emitted + diff --git a/src/test/ui/write-to-static-mut-in-static.stderr b/src/test/ui/write-to-static-mut-in-static.stderr index 4349f6e89c119..6c2bd13d433ad 100644 --- a/src/test/ui/write-to-static-mut-in-static.stderr +++ b/src/test/ui/write-to-static-mut-in-static.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/write-to-static-mut-in-static.rs:2:33 | LL | pub static mut B: () = unsafe { A = 1; }; - | ^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^ modifying a static's initial value from another static's initializer error[E0391]: cycle detected when const-evaluating `C` --> $DIR/write-to-static-mut-in-static.rs:5:34 diff --git a/src/test/ui/zero-sized/zero-sized-tuple-struct.rs b/src/test/ui/zero-sized/zero-sized-tuple-struct.rs index 6c438720e5d86..2208590f7d61b 100644 --- a/src/test/ui/zero-sized/zero-sized-tuple-struct.rs +++ b/src/test/ui/zero-sized/zero-sized-tuple-struct.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_braces)] #![allow(unused_assignments)] // Make sure that the constructor args are codegened for zero-sized tuple structs diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 98e9fe7a8b221..39baa6b8540df 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -56,6 +56,8 @@ static TARGETS: &[&str] = &[ "aarch64-unknown-hermit", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", + "aarch64-unknown-none", + "aarch64-unknown-none-softfloat", "aarch64-unknown-redox", "arm-linux-androideabi", "arm-unknown-linux-gnueabi", @@ -137,6 +139,7 @@ static TARGETS: &[&str] = &[ "x86_64-pc-solaris", "x86_64-unknown-cloudabi", "x86_64-unknown-freebsd", + "x86_64-unknown-illumos", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnux32", "x86_64-unknown-linux-musl", @@ -225,7 +228,6 @@ struct Builder { clippy_release: String, rustfmt_release: String, llvm_tools_release: String, - lldb_release: String, miri_release: String, input: PathBuf, @@ -241,7 +243,6 @@ struct Builder { clippy_version: Option, rustfmt_version: Option, llvm_tools_version: Option, - lldb_version: Option, miri_version: Option, rust_git_commit_hash: Option, @@ -250,7 +251,6 @@ struct Builder { clippy_git_commit_hash: Option, rustfmt_git_commit_hash: Option, llvm_tools_git_commit_hash: Option, - lldb_git_commit_hash: Option, miri_git_commit_hash: Option, should_sign: bool, @@ -281,7 +281,6 @@ fn main() { let miri_release = args.next().unwrap(); let rustfmt_release = args.next().unwrap(); let llvm_tools_release = args.next().unwrap(); - let lldb_release = args.next().unwrap(); // Do not ask for a passphrase while manually testing let mut passphrase = String::new(); @@ -297,7 +296,6 @@ fn main() { clippy_release, rustfmt_release, llvm_tools_release, - lldb_release, miri_release, input, @@ -313,7 +311,6 @@ fn main() { clippy_version: None, rustfmt_version: None, llvm_tools_version: None, - lldb_version: None, miri_version: None, rust_git_commit_hash: None, @@ -322,7 +319,6 @@ fn main() { clippy_git_commit_hash: None, rustfmt_git_commit_hash: None, llvm_tools_git_commit_hash: None, - lldb_git_commit_hash: None, miri_git_commit_hash: None, should_sign, @@ -337,7 +333,6 @@ enum PkgType { Clippy, Rustfmt, LlvmTools, - Lldb, Miri, Other, } @@ -352,7 +347,6 @@ impl PkgType { "clippy" | "clippy-preview" => Clippy, "rustfmt" | "rustfmt-preview" => Rustfmt, "llvm-tools" | "llvm-tools-preview" => LlvmTools, - "lldb" | "lldb-preview" => Lldb, "miri" | "miri-preview" => Miri, _ => Other, } @@ -367,8 +361,6 @@ impl Builder { self.clippy_version = self.version("clippy", "x86_64-unknown-linux-gnu"); self.rustfmt_version = self.version("rustfmt", "x86_64-unknown-linux-gnu"); self.llvm_tools_version = self.version("llvm-tools", "x86_64-unknown-linux-gnu"); - // lldb is only built for macOS. - self.lldb_version = self.version("lldb", "x86_64-apple-darwin"); self.miri_version = self.version("miri", "x86_64-unknown-linux-gnu"); self.rust_git_commit_hash = self.git_commit_hash("rust", "x86_64-unknown-linux-gnu"); @@ -378,7 +370,6 @@ impl Builder { self.rustfmt_git_commit_hash = self.git_commit_hash("rustfmt", "x86_64-unknown-linux-gnu"); self.llvm_tools_git_commit_hash = self.git_commit_hash("llvm-tools", "x86_64-unknown-linux-gnu"); - self.lldb_git_commit_hash = self.git_commit_hash("lldb", "x86_64-unknown-linux-gnu"); self.miri_git_commit_hash = self.git_commit_hash("miri", "x86_64-unknown-linux-gnu"); self.check_toolstate(); @@ -453,7 +444,6 @@ impl Builder { package("rustfmt-preview", HOSTS); package("rust-analysis", TARGETS); package("llvm-tools-preview", TARGETS); - package("lldb-preview", TARGETS); } fn add_profiles_to(&mut self, manifest: &mut Manifest) { @@ -484,7 +474,6 @@ impl Builder { "rls-preview", "rust-src", "llvm-tools-preview", - "lldb-preview", "rust-analysis", "miri-preview", ], @@ -559,7 +548,6 @@ impl Builder { host_component("rls-preview"), host_component("rustfmt-preview"), host_component("llvm-tools-preview"), - host_component("lldb-preview"), host_component("rust-analysis"), ]); @@ -689,7 +677,6 @@ impl Builder { Clippy => format!("clippy-{}-{}.tar.gz", self.clippy_release, target), Rustfmt => format!("rustfmt-{}-{}.tar.gz", self.rustfmt_release, target), LlvmTools => format!("llvm-tools-{}-{}.tar.gz", self.llvm_tools_release, target), - Lldb => format!("lldb-{}-{}.tar.gz", self.lldb_release, target), Miri => format!("miri-{}-{}.tar.gz", self.miri_release, target), Other => format!("{}-{}-{}.tar.gz", component, self.rust_release, target), } @@ -703,7 +690,6 @@ impl Builder { Clippy => &self.clippy_version, Rustfmt => &self.rustfmt_version, LlvmTools => &self.llvm_tools_version, - Lldb => &self.lldb_version, Miri => &self.miri_version, _ => &self.rust_version, } @@ -717,7 +703,6 @@ impl Builder { Clippy => &self.clippy_git_commit_hash, Rustfmt => &self.rustfmt_git_commit_hash, LlvmTools => &self.llvm_tools_git_commit_hash, - Lldb => &self.lldb_git_commit_hash, Miri => &self.miri_git_commit_hash, _ => &self.rust_git_commit_hash, } diff --git a/src/tools/cargo b/src/tools/cargo index 7019b3ed3d539..500b2bd01c958 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 7019b3ed3d539db7429d10a343b69be8c426b576 +Subproject commit 500b2bd01c958f5a33b6aa3f080bea015877b83c diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index cf00cb1ab8a4b..6968822c1b8ae 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -58,13 +58,6 @@ const TEST_REPOS: &'static [Test] = &[ // This takes much less time to build than all of Servo and supports stable Rust. packages: &["selectors"], }, - Test { - name: "webrender", - repo: "https://github.com/servo/webrender", - sha: "a3d6e6894c5a601fa547c6273eb963ca1321c2bb", - lock: None, - packages: &[], - }, ]; fn main() { diff --git a/src/tools/clippy b/src/tools/clippy deleted file mode 160000 index 1ff81c1b6d7ab..0000000000000 --- a/src/tools/clippy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1ff81c1b6d7abdcc9ee47f4a8ab175082cad4421 diff --git a/src/tools/clippy/.cargo/config b/src/tools/clippy/.cargo/config new file mode 100644 index 0000000000000..2bad3b9c57f0c --- /dev/null +++ b/src/tools/clippy/.cargo/config @@ -0,0 +1,6 @@ +[alias] +uitest = "test --test compile-test" +dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" + +[build] +rustflags = ["-Zunstable-options"] diff --git a/src/tools/clippy/.editorconfig b/src/tools/clippy/.editorconfig new file mode 100644 index 0000000000000..a13173544d80a --- /dev/null +++ b/src/tools/clippy/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_size = 2 diff --git a/src/tools/clippy/.gitattributes b/src/tools/clippy/.gitattributes new file mode 100644 index 0000000000000..90cf33053c775 --- /dev/null +++ b/src/tools/clippy/.gitattributes @@ -0,0 +1,3 @@ +* text=auto eol=lf +*.rs text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4 +*.fixed linguist-language=Rust diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE.md b/src/tools/clippy/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000000..15006a07b44f2 --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,8 @@ + diff --git a/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000000..97aa220afea54 --- /dev/null +++ b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ +Thank you for making Clippy better! + +We're collecting our changelog from pull request descriptions. +If your PR only updates to the latest nightly, you can leave the +`changelog` entry as `none`. Otherwise, please write a short comment +explaining your change. + +If your PR fixes an issue, you can add "fixes #issue_number" into this +PR description. This way the issue will be automatically closed when +your PR is merged. + +If you added a new lint, here's a checklist for things that will be +checked during review or continuous integration. + +- [ ] Followed [lint naming conventions][lint_naming] +- [ ] Added passing UI tests (including committed `.stderr` file) +- [ ] `cargo test` passes locally +- [ ] Executed `cargo dev update_lints` +- [ ] Added lint documentation +- [ ] Run `cargo dev fmt` + +[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints + +Note that you can skip the above if you are just opening a WIP PR in +order to get feedback. + +Delete this line and everything above before opening your PR. + +--- + +changelog: none diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh new file mode 100644 index 0000000000000..3f425e5b7258d --- /dev/null +++ b/src/tools/clippy/.github/deploy.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +set -ex + +echo "Removing the current docs for master" +rm -rf out/master/ || exit 0 + +echo "Making the docs for master" +mkdir out/master/ +cp util/gh-pages/index.html out/master +python3 ./util/export.py out/master/lints.json + +if [[ -n $TAG_NAME ]]; then + echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it" + cp -r out/master "out/$TAG_NAME" + rm -f out/stable + ln -s "$TAG_NAME" out/stable +fi + +if [[ $BETA = "true" ]]; then + echo "Update documentation for the beta release" + cp -r out/master out/beta +fi + +# Generate version index that is shown as root index page +cp util/gh-pages/versions.html out/index.html + +echo "Making the versions.json file" +python3 ./util/versions.py out + +cd out +# Now let's go have some fun with the cloned repo +git config user.name "GHA CI" +git config user.email "gha@ci.invalid" + +if git diff --exit-code --quiet; then + echo "No changes to the output on this push; exiting." + exit 0 +fi + +if [[ -n $TAG_NAME ]]; then + # Add the new dir + git add "$TAG_NAME" + # Update the symlink + git add stable + # Update versions file + git add versions.json + git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" +elif [[ $BETA = "true" ]]; then + git add beta + git commit -m "Automatic deploy to GitHub Pages (beta): ${SHA}" +else + git add . + git commit -m "Automatic deploy to GitHub Pages: ${SHA}" +fi + +git push "$SSH_REPO" "$TARGET_BRANCH" diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh new file mode 100644 index 0000000000000..a2e87f5eb3745 --- /dev/null +++ b/src/tools/clippy/.github/driver.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -ex + +# Check sysroot handling +sysroot=$(./target/debug/clippy-driver --print sysroot) +test "$sysroot" = "$(rustc --print sysroot)" + +if [[ ${OS} == "Windows" ]]; then + desired_sysroot=C:/tmp +else + desired_sysroot=/tmp +fi +sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot) +test "$sysroot" = $desired_sysroot + +sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot) +test "$sysroot" = $desired_sysroot + +# Make sure this isn't set - clippy-driver should cope without it +unset CARGO_MANIFEST_DIR + +# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 +# FIXME: How to match the clippy invocation in compile-test.rs? +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cstring.rs 2> cstring.stderr && exit 1 +sed -e "s,tests/ui,\$DIR," -e "/= help/d" cstring.stderr > normalized.stderr +diff normalized.stderr tests/ui/cstring.stderr + +# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml new file mode 100644 index 0000000000000..8edf0c23860aa --- /dev/null +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -0,0 +1,99 @@ +name: Clippy Test + +on: + push: + # Ignore bors branches, since they are covered by `clippy_bors.yml` + branches-ignore: + - auto + - try + # Don't run Clippy tests, when only textfiles were modified + paths-ignore: + - 'COPYRIGHT' + - 'LICENSE-*' + - '**.md' + - '**.txt' + pull_request: + # Don't run Clippy tests, when only textfiles were modified + paths-ignore: + - 'COPYRIGHT' + - 'LICENSE-*' + - '**.md' + - '**.txt' + +env: + RUST_BACKTRACE: 1 + CARGO_TARGET_DIR: '${{ github.workspace }}/target' + NO_FMT_TEST: 1 + +jobs: + base: + runs-on: ubuntu-latest + + steps: + # Setup + - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + + - name: rust-toolchain + uses: actions-rs/toolchain@v1.0.3 + with: + toolchain: nightly + target: x86_64-unknown-linux-gnu + profile: minimal + + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Run cargo update + run: cargo update + + - name: Cache cargo dir + uses: actions/cache@v1 + with: + path: ~/.cargo + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu + + - name: Master Toolchain Setup + run: bash setup-toolchain.sh + + # Run + - name: Set LD_LIBRARY_PATH (Linux) + run: | + SYSROOT=$(rustc --print sysroot) + echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" + + - name: Build + run: cargo build --features deny-warnings + + - name: Test + run: cargo test --features deny-warnings + + - name: Test clippy_lints + run: cargo test --features deny-warnings + working-directory: clippy_lints + + - name: Test rustc_tools_util + run: cargo test --features deny-warnings + working-directory: rustc_tools_util + + - name: Test clippy_dev + run: cargo test --features deny-warnings + working-directory: clippy_dev + + - name: Test cargo-clippy + run: ../target/debug/cargo-clippy + working-directory: clippy_workspace_tests + + - name: Test clippy-driver + run: bash .github/driver.sh + env: + OS: ${{ runner.os }} + + # Cleanup + - name: Run cargo-cache --autoclean + run: | + cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo cache diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml new file mode 100644 index 0000000000000..6675a1029bbc8 --- /dev/null +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -0,0 +1,329 @@ +name: Clippy Test (bors) + +on: + push: + branches: + - auto + - try + +env: + RUST_BACKTRACE: 1 + CARGO_TARGET_DIR: '${{ github.workspace }}/target' + NO_FMT_TEST: 1 + +jobs: + changelog: + runs-on: ubuntu-latest + + steps: + - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + - name: Checkout + uses: actions/checkout@v2.0.0 + with: + ref: ${{ github.ref }} + + # Run + - name: Check Changelog + run: | + MESSAGE=$(git log --format=%B -n 1) + PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//') + output=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \ + python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \ + grep "^changelog: " | \ + sed "s/changelog: //g") + if [[ -z "$output" ]]; then + echo "ERROR: PR body must contain 'changelog: ...'" + exit 1 + elif [[ "$output" = "none" ]]; then + echo "WARNING: changelog is 'none'" + fi + env: + PYTHONIOENCODING: 'utf-8' + base: + needs: changelog + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc] + exclude: + - os: ubuntu-latest + host: x86_64-apple-darwin + - os: ubuntu-latest + host: x86_64-pc-windows-msvc + - os: macos-latest + host: x86_64-unknown-linux-gnu + - os: macos-latest + host: i686-unknown-linux-gnu + - os: macos-latest + host: x86_64-pc-windows-msvc + - os: windows-latest + host: x86_64-unknown-linux-gnu + - os: windows-latest + host: i686-unknown-linux-gnu + - os: windows-latest + host: x86_64-apple-darwin + + runs-on: ${{ matrix.os }} + + steps: + # Setup + - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + + - name: Install dependencies (Linux-i686) + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386 + if: matrix.host == 'i686-unknown-linux-gnu' + + - name: rust-toolchain + uses: actions-rs/toolchain@v1.0.3 + with: + toolchain: nightly + target: ${{ matrix.host }} + profile: minimal + + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Run cargo update + run: cargo update + + - name: Cache cargo dir + uses: actions/cache@v1 + with: + path: ~/.cargo + key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.host }} + + - name: Master Toolchain Setup + run: bash setup-toolchain.sh + env: + HOST_TOOLCHAIN: ${{ matrix.host }} + shell: bash + + # Run + - name: Set LD_LIBRARY_PATH (Linux) + if: runner.os == 'Linux' + run: | + SYSROOT=$(rustc --print sysroot) + echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" + - name: Link rustc dylib (MacOS) + if: runner.os == 'macOS' + run: | + SYSROOT=$(rustc --print sysroot) + sudo mkdir -p /usr/local/lib + sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \; + - name: Set PATH (Windows) + if: runner.os == 'Windows' + run: | + $sysroot = rustc --print sysroot + $env:PATH += ';' + $sysroot + '\bin' + echo "::set-env name=PATH::$env:PATH" + + - name: Build + run: cargo build --features deny-warnings + shell: bash + + - name: Test + run: cargo test --features deny-warnings + shell: bash + + - name: Test clippy_lints + run: cargo test --features deny-warnings + shell: bash + working-directory: clippy_lints + + - name: Test rustc_tools_util + run: cargo test --features deny-warnings + shell: bash + working-directory: rustc_tools_util + + - name: Test clippy_dev + run: cargo test --features deny-warnings + shell: bash + working-directory: clippy_dev + + - name: Test cargo-clippy + run: ../target/debug/cargo-clippy + shell: bash + working-directory: clippy_workspace_tests + + - name: Test clippy-driver + run: bash .github/driver.sh + shell: bash + env: + OS: ${{ runner.os }} + + # Cleanup + - name: Run cargo-cache --autoclean + run: | + cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo cache + shell: bash + integration_build: + needs: changelog + runs-on: ubuntu-latest + + steps: + # Setup + - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + + - name: rust-toolchain + uses: actions-rs/toolchain@v1.0.3 + with: + toolchain: nightly + target: x86_64-unknown-linux-gnu + profile: minimal + + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Run cargo update + run: cargo update + + - name: Cache cargo dir + uses: actions/cache@v1 + with: + path: ~/.cargo + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu + + - name: Master Toolchain Setup + run: bash setup-toolchain.sh + + # Run + - name: Build Integration Test + run: cargo test --test integration --features integration --no-run + + # Upload + - name: Extract Binaries + run: | + DIR=$CARGO_TARGET_DIR/debug + rm $DIR/deps/integration-*.d + mv $DIR/deps/integration-* $DIR/integration + find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf + rm -rf $CARGO_TARGET_DIR/release + + - name: Upload Binaries + uses: actions/upload-artifact@v1 + with: + name: target + path: target + + # Cleanup + - name: Run cargo-cache --autoclean + run: | + cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo cache + integration: + needs: integration_build + strategy: + fail-fast: false + max-parallel: 6 + matrix: + integration: + - 'rust-lang/cargo' + - 'rust-lang/rls' + - 'rust-lang/chalk' + - 'rust-lang/rustfmt' + - 'Marwes/combine' + - 'Geal/nom' + - 'rust-lang/stdarch' + - 'serde-rs/serde' + - 'chronotope/chrono' + - 'hyperium/hyper' + - 'rust-random/rand' + - 'rust-lang/futures-rs' + - 'rust-itertools/itertools' + - 'rust-lang-nursery/failure' + - 'rust-lang/log' + + runs-on: ubuntu-latest + + steps: + # Setup + - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + + - name: rust-toolchain + uses: actions-rs/toolchain@v1.0.3 + with: + toolchain: nightly + target: x86_64-unknown-linux-gnu + profile: minimal + + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Run cargo update + run: cargo update + + - name: Cache cargo dir + uses: actions/cache@v1 + with: + path: ~/.cargo + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu + + - name: Master Toolchain Setup + run: bash setup-toolchain.sh + + # Download + - name: Download target dir + uses: actions/download-artifact@v1 + with: + name: target + path: target + + - name: Make Binaries Executable + run: chmod +x $CARGO_TARGET_DIR/debug/* + + # Run + - name: Test ${{ matrix.integration }} + run: $CARGO_TARGET_DIR/debug/integration + env: + INTEGRATION: ${{ matrix.integration }} + RUSTUP_TOOLCHAIN: master + + # Cleanup + - name: Run cargo-cache --autoclean + run: | + cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache + cargo cache + + # These jobs doesn't actually test anything, but they're only used to tell + # bors the build completed, as there is no practical way to detect when a + # workflow is successful listening to webhooks only. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + + end-success: + name: bors test finished + if: github.event.pusher.name == 'bors' && success() + runs-on: ubuntu-latest + needs: [base, integration] + + steps: + - name: Mark the job as successful + run: exit 0 + + end-failure: + name: bors test finished + if: github.event.pusher.name == 'bors' && (failure() || cancelled()) + runs-on: ubuntu-latest + needs: [base, integration] + + steps: + - name: Mark the job as a failure + run: exit 1 diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml new file mode 100644 index 0000000000000..ec3b43c2f43bc --- /dev/null +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -0,0 +1,74 @@ +name: Clippy Dev Test + +on: + push: + branches: + - auto + - try + pull_request: + # Only run on paths, that get checked by the clippy_dev tool + paths: + - 'CHANGELOG.md' + - 'README.md' + - '**.stderr' + - '**.rs' + +env: + RUST_BACKTRACE: 1 + +jobs: + clippy_dev: + runs-on: ubuntu-latest + + steps: + # Setup + - name: rust-toolchain + uses: actions-rs/toolchain@v1.0.3 + with: + toolchain: nightly + target: x86_64-unknown-linux-gnu + profile: minimal + components: rustfmt + + - name: Checkout + uses: actions/checkout@v2.0.0 + + # Run + - name: Build + run: cargo build --features deny-warnings + working-directory: clippy_dev + + - name: Test limit_stderr_length + run: cargo dev limit_stderr_length + + - name: Test update_lints + run: cargo dev update_lints --check + + - name: Test fmt + run: cargo dev fmt --check + + # These jobs doesn't actually test anything, but they're only used to tell + # bors the build completed, as there is no practical way to detect when a + # workflow is successful listening to webhooks only. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + + end-success: + name: bors dev test finished + if: github.event.pusher.name == 'bors' && success() + runs-on: ubuntu-latest + needs: [clippy_dev] + + steps: + - name: Mark the job as successful + run: exit 0 + + end-failure: + name: bors dev test finished + if: github.event.pusher.name == 'bors' && (failure() || cancelled()) + runs-on: ubuntu-latest + needs: [clippy_dev] + + steps: + - name: Mark the job as a failure + run: exit 1 diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..f542f9b02c17b --- /dev/null +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -0,0 +1,51 @@ +name: Deploy + +on: + push: + branches: + - master + - beta + tags: + - rust-1.** + +env: + TARGET_BRANCH: 'gh-pages' + SHA: '${{ github.sha }}' + SSH_REPO: 'git@github.com:${{ github.repository }}.git' + +jobs: + deploy: + runs-on: ubuntu-latest + if: github.repository == 'rust-lang/rust-clippy' + + steps: + # Setup + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Checkout + uses: actions/checkout@v2.0.0 + with: + ref: ${{ env.TARGET_BRANCH }} + path: 'out' + + # Run + - name: Set tag name + if: startswith(github.ref, 'refs/tags/') + run: | + TAG=$(basename ${{ github.ref }}) + echo "::set-env name=TAG_NAME::$TAG" + - name: Set beta to true + if: github.ref == 'refs/heads/beta' + run: echo "::set-env name=BETA::true" + + - name: Use scripts and templates from master branch + run: | + git fetch --no-tags --prune --depth=1 origin master + git checkout origin/master -- .github/deploy.sh util/gh-pages/ util/*.py + + - name: Deploy + run: | + eval "$(ssh-agent -s)" + ssh-add - <<< "${{ secrets.DEPLOY_KEY }}" + bash .github/deploy.sh diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml new file mode 100644 index 0000000000000..cc175e8bf247f --- /dev/null +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -0,0 +1,55 @@ +name: Remark + +on: + push: + branches: + - auto + - try + pull_request: + paths: + - '**.md' + +jobs: + remark: + runs-on: ubuntu-latest + + steps: + # Setup + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Setup Node.js + uses: actions/setup-node@v1.1.0 + + - name: Install remark + run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended + + # Run + - name: Check *.md files + run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null + + # These jobs doesn't actually test anything, but they're only used to tell + # bors the build completed, as there is no practical way to detect when a + # workflow is successful listening to webhooks only. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + + end-success: + name: bors remark test finished + if: github.event.pusher.name == 'bors' && success() + runs-on: ubuntu-latest + needs: [remark] + + steps: + - name: Mark the job as successful + run: exit 0 + + end-failure: + name: bors remark test finished + if: github.event.pusher.name == 'bors' && (failure() || cancelled()) + runs-on: ubuntu-latest + needs: [remark] + + steps: + - name: Mark the job as a failure + run: exit 1 diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore new file mode 100644 index 0000000000000..adf5e8feddf4c --- /dev/null +++ b/src/tools/clippy/.gitignore @@ -0,0 +1,37 @@ +# Used by CI to be able to push: +/.github/deploy_key +out + +# Compiled files +*.o +*.d +*.so +*.rlib +*.dll +*.pyc +*.rmeta + +# Executables +*.exe + +# Generated by Cargo +*Cargo.lock +/target +/clippy_lints/target +/clippy_workspace_tests/target +/clippy_dev/target +/rustc_tools_util/target + +# Generated by dogfood +/target_recur/ + +# gh pages docs +util/gh-pages/lints.json + +# rustfmt backups +*.rs.bk + +helper.txt +*.iml +.vscode +.idea diff --git a/src/tools/clippy/.remarkrc b/src/tools/clippy/.remarkrc new file mode 100644 index 0000000000000..0ede7ac75cb6f --- /dev/null +++ b/src/tools/clippy/.remarkrc @@ -0,0 +1,12 @@ +{ + "plugins": [ + "remark-preset-lint-recommended", + ["remark-lint-list-item-indent", false], + ["remark-lint-no-literal-urls", false], + ["remark-lint-no-shortcut-reference-link", false], + ["remark-lint-maximum-line-length", 120] + ], + "settings": { + "commonmark": true + } +} diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md new file mode 100644 index 0000000000000..583c32ca9e032 --- /dev/null +++ b/src/tools/clippy/CHANGELOG.md @@ -0,0 +1,1653 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Changelog Update](doc/changelog_update.md) if you want to update this +document. + +## Unreleased / In Rust Nightly + +[891e1a8...master](https://github.com/rust-lang/rust-clippy/compare/891e1a8...master) + +## Rust 1.44 + +Current beta, release 2020-06-04 + +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8) + +### New lints + +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226) +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427) +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230) +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272) +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423) +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319) +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248) +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415) +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349) +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) + + +### Moves and Deprecations + +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380) +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428) +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364) +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412) +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401) +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419) +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409) +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410) +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411) + +### Enhancements + +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to + auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363) +* Make [`redundant_clone`] also trigger on cases where the cloned value is not + consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304) +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430) +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425) +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466) +* [`bool_comparison`] now also checks for inequality comparisons that can be + written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365) +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441) +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483) +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329) +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345) +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473) + +### False Positive Fixes + +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468) +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437) +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387) +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453) +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445) +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424) +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293) +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287) +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451) + + +### Suggestion Improvements + +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481) +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292) +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350) + +### ICE Fixes + +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296) +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297) + +### Documentation + +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353) +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448) +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403) +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454) +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is + not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312) + +## Rust 1.43 + +Current stable, released 2020-04-23 + +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b) + +### New lints + +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897) +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897) +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029) +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058) +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061) +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101) +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125) +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125) +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148) +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202) +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258) + +### Moves and Deprecations + +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200) + +### Enhancements + +* Make [`missing_errors_doc`] lint also trigger on `async` functions + [#5181](https://github.com/rust-lang/rust-clippy/pull/5181) +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193) +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266) + +### False Positive Fixes + +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047) +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132) +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170) +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216) + +### Suggestion Improvements + +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134) + +### ICE Fixes + +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129) +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213) +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256) + +### Documentation + +* Improve documentation of [`iter_nth_zero`] +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171) + +### Others + +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190) + + +## Rust 1.42 + +Released 2020-03-12 + +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206) + +### New lints + +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543) +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823) +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867) +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881) +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885) +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945) +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960) +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966) +* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999) +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067) + +### Moves and Deprecations + +* Move [`transmute_float_to_int`] from nursery to complexity group + [#5015](https://github.com/rust-lang/rust-clippy/pull/5015) +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057) +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106) +* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930) + +### Enhancements + +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027) +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081) +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910) +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915) +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017) + +### False Positive Fixes + +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937) +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977) +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008) +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079) +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083) +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106) +* Don't trigger [`let_underscore_must_use`] in external macros + [#5082](https://github.com/rust-lang/rust-clippy/pull/5082) +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086) + +### Suggestion Improvements + +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634) +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934) +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935) +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956) +* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963) +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978) +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022) +* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032) + +### ICE fixes + +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975) + +### Documentation + +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`] + + +## Rust 1.41 + +Released 2020-01-30 + +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7) + +* New Lints: + * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697) + * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801) + * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806) + * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807) + * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814) + * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816) + * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821) + * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884) + * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889) +* Remove plugin interface, see + [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for + details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714) +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863) +* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788) +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes + [#4808](https://github.com/rust-lang/rust-clippy/pull/4808) +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842) +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730) +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803) +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794) +* Fix false positive in `print_with_newline` and `write_with_newline` + [#4769](https://github.com/rust-lang/rust-clippy/pull/4769) +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766) +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870) +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880) +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851) +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883) +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877) +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772) +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776) +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780) +* Display help when running `clippy-driver` without arguments, instead of ICEing + [#4810](https://github.com/rust-lang/rust-clippy/pull/4810) +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588) +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757) +* Improve Documentation by adding positive examples to some lints + [#4832](https://github.com/rust-lang/rust-clippy/pull/4832) + +## Rust 1.40 + +Released 2019-12-19 + +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb) + +* New Lints: + * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537) + * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603) + * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615) + * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680) + * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619) + * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683) + * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560) + * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560) + * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560) + * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569) + * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592) + * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509) +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736) +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613) +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585) +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568) +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611) +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614) +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509) +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721) +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570) +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593) +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575) +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599) +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691) +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602) +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635) +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671) +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590) + +## Rust 1.39 + +Released 2019-11-07 + +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b) + +* New Lints: + * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479) + * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231) + * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535) + * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511) + * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394) + * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386) + * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498) +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348) +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403) +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539) +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425) +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525) +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518) +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469) +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458) +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473) +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411) +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487) +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490) +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365) +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478) +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450) +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477) +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460) +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495) +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445) +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489) +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369) +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558) +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352) +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544) +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522) +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446) +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382) +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401) +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418) + +## Rust 1.38 + +Released 2019-09-26 + +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860) + +* New Lints: + * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203) + * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259) + * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259) + * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766) + * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222) +* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307) +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308) +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262) +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337) +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345) +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257) +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233) +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266) +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275) +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274) +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246) +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335) +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257) +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361) +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314) +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268) +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250) + +## Rust 1.37 + +Released 2019-08-15 + +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e) + +* New Lints: + * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088) + * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832) + * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195) +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`]. + The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162) +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102) +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220) +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190) +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107) +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214) +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136) +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164) +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119) +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137) +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071) +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099) + +## Rust 1.36 + +Released 2019-07-04 + +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7) + +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039) +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954) +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013) +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101) +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007) +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084) +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018) +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035) +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008) +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024) +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006) +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989) +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043) +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049) +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984) +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975) +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053) +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021) +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082) +* Add macro check for [`unnecessary_cast`] [#4026](https://github.com/rust-lang/rust-clippy/pull/4026) +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027) +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960) +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931) + + +## Rust 1.35 + +Released 2019-05-20 + +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e) + +* New lint: [`drop_bounds`] to detect `T: Drop` bounds +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101) +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code +* Move [`get_unwrap`] to the restriction category +* Improve suggestions for [`iter_cloned_collect`] +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()` +* Fix false positive in [`bool_comparison`] pertaining to non-bool types +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows +* Fix false positive in `option_map_unwrap_or` on non-copy types +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros +* Fix false positive in [`needless_continue`] pertaining to loop labels +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures +* Fix false positive for [`use_self`] in nested functions +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846) +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes +* Avoid triggering [`redundant_closure`] in macros +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741) + +## Rust 1.34 + +Released 2019-04-10 + +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380) + +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)` +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const` +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be + configured using the `too-many-lines-threshold` configuration. +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_` +* Expand `redundant_closure` to also work for methods (not only functions) +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher` +* Fix false positive in `cast_sign_loss` +* Fix false positive in `integer_arithmetic` +* Fix false positive in `unit_arg` +* Fix false positives in `implicit_return` +* Add suggestion to `explicit_write` +* Improve suggestions for `question_mark` lint +* Fix incorrect suggestion for `cast_lossless` +* Fix incorrect suggestion for `expect_fun_call` +* Fix incorrect suggestion for `needless_bool` +* Fix incorrect suggestion for `needless_range_loop` +* Fix incorrect suggestion for `use_self` +* Fix incorrect suggestion for `while_let_on_iterator` +* Clippy is now slightly easier to invoke in non-cargo contexts. See + [#3665][pull3665] for more details. +* We now have [improved documentation][adding_lints] on how to add new lints + +## Rust 1.33 + +Released 2019-02-26 + +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724) + +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`] +* The `rust-clippy` repository is now part of the `rust-lang` org. +* Rename `stutter` to `module_name_repetitions` +* Merge `new_without_default_derive` into `new_without_default` lint +* Move `large_digit_groups` from `style` group to `pedantic` +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=` + comparisons against booleans +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2` +* Expand `redundant_clone` to work on struct fields +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}` +* Expand `use_self` to work on tuple structs and also in local macros +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn` +* Fix false positives in `implicit_return` +* Fix false positives in `use_self` +* Fix false negative in `clone_on_copy` +* Fix false positive in `doc_markdown` +* Fix false positive in `empty_loop` +* Fix false positive in `if_same_then_else` +* Fix false positive in `infinite_iter` +* Fix false positive in `question_mark` +* Fix false positive in `useless_asref` +* Fix false positive in `wildcard_dependencies` +* Fix false positive in `write_with_newline` +* Add suggestion to `explicit_write` +* Improve suggestions for `question_mark` lint +* Fix incorrect suggestion for `get_unwrap` + +## Rust 1.32 + +Released 2019-01-17 + +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be) + +* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`], + [`redundant_clone`], [`wildcard_dependencies`], + [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`], + [`mem_discriminant_non_enum`], [`cargo_common_metadata`] +* Add support for `u128` and `i128` to integer related lints +* Add float support to `mistyped_literal_suffixes` +* Fix false positives in `use_self` +* Fix false positives in `missing_comma` +* Fix false positives in `new_ret_no_self` +* Fix false positives in `possible_missing_comma` +* Fix false positive in `integer_arithmetic` in constant items +* Fix false positive in `needless_borrow` +* Fix false positive in `out_of_bounds_indexing` +* Fix false positive in `new_without_default_derive` +* Fix false positive in `string_lit_as_bytes` +* Fix false negative in `out_of_bounds_indexing` +* Fix false negative in `use_self`. It will now also check existential types +* Fix incorrect suggestion for `redundant_closure_call` +* Fix various suggestions that contained expanded macros +* Fix `bool_comparison` triggering 3 times on on on the same code +* Expand `trivially_copy_pass_by_ref` to work on trait methods +* Improve suggestion for `needless_range_loop` +* Move `needless_pass_by_value` from `pedantic` group to `style` + +## Rust 1.31 + +Released 2018-12-06 + +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2) + +* Clippy has been relicensed under a dual MIT / Apache license. + See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more + information. +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended + installation method is via `rustup component add clippy`. +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`], + [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`] +* Fix ICE in `if_let_redundant_pattern_matching` +* Fix ICE in `needless_pass_by_value` when encountering a generic function + argument with a lifetime parameter +* Fix ICE in `needless_range_loop` +* Fix ICE in `single_char_pattern` when encountering a constant value +* Fix false positive in `assign_op_pattern` +* Fix false positive in `boxed_local` on trait implementations +* Fix false positive in `cmp_owned` +* Fix false positive in `collapsible_if` when conditionals have comments +* Fix false positive in `double_parens` +* Fix false positive in `excessive_precision` +* Fix false positive in `explicit_counter_loop` +* Fix false positive in `fn_to_numeric_cast_with_truncation` +* Fix false positive in `map_clone` +* Fix false positive in `new_ret_no_self` +* Fix false positive in `new_without_default` when `new` is unsafe +* Fix false positive in `type_complexity` when using extern types +* Fix false positive in `useless_format` +* Fix false positive in `wrong_self_convention` +* Fix incorrect suggestion for `excessive_precision` +* Fix incorrect suggestion for `expect_fun_call` +* Fix incorrect suggestion for `get_unwrap` +* Fix incorrect suggestion for `useless_format` +* `fn_to_numeric_cast_with_truncation` lint can be disabled again +* Improve suggestions for `manual_memcpy` +* Improve help message for `needless_lifetimes` + +## Rust 1.30 + +Released 2018-10-25 + +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad) + +* Deprecate `assign_ops` lint +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`], + [`needless_collect`], [`copy_iterator`] +* `cargo clippy -V` now includes the Clippy commit hash of the Rust + Clippy component +* Fix ICE in `implicit_hasher` +* Fix ICE when encountering `println!("{}" a);` +* Fix ICE when encountering a macro call in match statements +* Fix false positive in `default_trait_access` +* Fix false positive in `trivially_copy_pass_by_ref` +* Fix false positive in `similar_names` +* Fix false positive in `redundant_field_name` +* Fix false positive in `expect_fun_call` +* Fix false negative in `identity_conversion` +* Fix false negative in `explicit_counter_loop` +* Fix `range_plus_one` suggestion and false negative +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them +* Fix `useless_attribute` to also whitelist `unused_extern_crates` +* Fix incorrect suggestion for `single_char_pattern` +* Improve suggestion for `identity_conversion` lint +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic` +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity` +* Move `shadow_unrelated` from `restriction` group to `pedantic` +* Move `indexing_slicing` from `pedantic` group to `restriction` + +## Rust 1.29 + +Released 2018-09-13 + +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503) + +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada: + :tada: + You can now run `rustup component add clippy-preview` and then `cargo + clippy` to run Clippy. This should put an end to the continuous nightly + upgrades for Clippy users. +* Clippy now follows the Rust versioning scheme instead of its own +* Fix ICE when encountering a `while let (..) = x.iter()` construct +* Fix false positives in `use_self` +* Fix false positive in `trivially_copy_pass_by_ref` +* Fix false positive in `useless_attribute` lint +* Fix false positive in `print_literal` +* Fix `use_self` regressions +* Improve lint message for `neg_cmp_op_on_partial_ord` +* Improve suggestion highlight for `single_char_pattern` +* Improve suggestions for various print/write macro lints +* Improve website header + +## 0.0.212 (2018-07-10) +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)* + +## 0.0.211 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)* + +## 0.0.210 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)* + +## 0.0.209 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)* + +## 0.0.208 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)* + +## 0.0.207 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)* + +## 0.0.206 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)* + +## 0.0.205 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)* +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint + +## 0.0.204 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)* + +## 0.0.203 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)* +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)` + +## 0.0.202 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)* + +## 0.0.201 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)* + +## 0.0.200 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)* + +## 0.0.199 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)* + +## 0.0.198 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)* + +## 0.0.197 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)* + +## 0.0.196 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)* + +## 0.0.195 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)* + +## 0.0.194 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)* +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`] + +## 0.0.193 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)* + +## 0.0.192 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)* +* New lint: [`print_literal`] + +## 0.0.191 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)* +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction. + +## 0.0.190 +* Fix a bunch of intermittent cargo bugs + +## 0.0.189 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)* + +## 0.0.188 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)* +* New lint: [`while_immutable_condition`] + +## 0.0.187 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)* +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`] + +## 0.0.186 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)* +* Various false positive fixes + +## 0.0.185 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)* +* New lint: [`question_mark`] + +## 0.0.184 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)* +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`] + +## 0.0.183 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)* +* New lint: [`misaligned_transmute`] + +## 0.0.182 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)* +* New lint: [`decimal_literal_representation`] + +## 0.0.181 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)* +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`] +* Removed `unit_expr` +* Various false positive fixes for [`needless_pass_by_value`] + +## 0.0.180 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)* + +## 0.0.179 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)* + +## 0.0.178 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)* + +## 0.0.177 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)* +* New lint: [`match_as_ref`] + +## 0.0.176 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)* + +## 0.0.175 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)* + +## 0.0.174 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)* + +## 0.0.173 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)* + +## 0.0.172 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)* + +## 0.0.171 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)* + +## 0.0.170 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)* + +## 0.0.169 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] + +## 0.0.168 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* + +## 0.0.167 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)* +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`] + +## 0.0.166 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], + [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], + [`transmute_int_to_float`] + +## 0.0.165 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27) +* New lint: [`mut_range_bound`] + +## 0.0.164 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)* +* New lint: [`int_plus_one`] + +## 0.0.163 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)* + +## 0.0.162 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)* +* New lint: [`chars_last_cmp`] +* Improved suggestions for [`needless_borrow`], [`ptr_arg`], + +## 0.0.161 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)* + +## 0.0.160 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)* + +## 0.0.159 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)* +* New lint: [`clone_on_ref_ptr`] + +## 0.0.158 +* New lint: [`manual_memcpy`] +* [`cast_lossless`] no longer has redundant parentheses in its suggestions +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)* + +## 0.0.157 - 2017-09-04 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)* +* New lint: `unit_expr` + +## 0.0.156 - 2017-09-03 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)* + +## 0.0.155 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)* +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`] + +## 0.0.154 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)* +* Fix [`use_self`] triggering inside derives +* Add support for linting an entire workspace with `cargo clippy --all` +* New lint: [`naive_bytecount`] + +## 0.0.153 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)* +* New lint: [`use_self`] + +## 0.0.152 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)* + +## 0.0.151 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)* + +## 0.0.150 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)* + +## 0.0.148 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)* +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`] + +## 0.0.147 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)* + +## 0.0.146 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)* +* Fixes false positives in `inline_always` +* Fixes false negatives in `panic_params` + +## 0.0.145 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)* + +## 0.0.144 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)* + +## 0.0.143 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)* +* Fix `cargo clippy` crashing on `dylib` projects +* Fix false positives around `nested_while_let` and `never_loop` + +## 0.0.142 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)* + +## 0.0.141 +* Rewrite of the `doc_markdown` lint. +* Deprecated [`range_step_by_zero`] +* New lint: [`iterator_step_by_zero`] +* New lint: [`needless_borrowed_reference`] +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)* + +## 0.0.140 - 2017-06-16 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)* + +## 0.0.139 — 2017-06-10 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)* +* Fix bugs with for loop desugaring +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`] + +## 0.0.138 — 2017-06-05 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)* + +## 0.0.137 — 2017-06-05 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)* + +## 0.0.136 — 2017—05—26 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)* + +## 0.0.135 — 2017—05—24 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)* + +## 0.0.134 — 2017—05—19 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)* + +## 0.0.133 — 2017—05—14 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)* + +## 0.0.132 — 2017—05—05 +* Fix various bugs and some ices + +## 0.0.131 — 2017—05—04 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)* + +## 0.0.130 — 2017—05—03 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)* + +## 0.0.129 — 2017-05-01 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)* + +## 0.0.128 — 2017-04-28 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)* + +## 0.0.127 — 2017-04-27 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)* +* New lint: [`needless_continue`] + +## 0.0.126 — 2017-04-24 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)* + +## 0.0.125 — 2017-04-19 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)* + +## 0.0.124 — 2017-04-16 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)* + +## 0.0.123 — 2017-04-07 +* Fix various false positives + +## 0.0.122 — 2017-04-07 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)* +* New lint: [`op_ref`] + +## 0.0.121 — 2017-03-21 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)* + +## 0.0.120 — 2017-03-17 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)* + +## 0.0.119 — 2017-03-13 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)* + +## 0.0.118 — 2017-03-05 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)* + +## 0.0.117 — 2017-03-01 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)* + +## 0.0.116 — 2017-02-28 +* Fix `cargo clippy` on 64 bit windows systems + +## 0.0.115 — 2017-02-27 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)* +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`] + +## 0.0.114 — 2017-02-08 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)* +* Tests are now ui tests (testing the exact output of rustc) + +## 0.0.113 — 2017-02-04 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)* +* New lint: [`large_enum_variant`] +* `explicit_into_iter_loop` provides suggestions + +## 0.0.112 — 2017-01-27 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)* + +## 0.0.111 — 2017-01-21 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)* + +## 0.0.110 — 2017-01-20 +* Add badges and categories to `Cargo.toml` + +## 0.0.109 — 2017-01-19 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)* + +## 0.0.108 — 2017-01-12 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)* + +## 0.0.107 — 2017-01-11 +* Update regex dependency +* Fix FP when matching `&&mut` by `&ref` +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()` +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`] + +## 0.0.106 — 2017-01-04 +* Fix FP introduced by rustup in [`wrong_self_convention`] + +## 0.0.105 — 2017-01-04 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)* +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`] +* Fix suggestion in [`new_without_default`] +* FP fix in [`absurd_extreme_comparisons`] + +## 0.0.104 — 2016-12-15 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)* + +## 0.0.103 — 2016-11-25 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)* + +## 0.0.102 — 2016-11-24 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)* + +## 0.0.101 — 2016-11-23 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)* +* New lint: [`string_extend_chars`] + +## 0.0.100 — 2016-11-20 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)* + +## 0.0.99 — 2016-11-18 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14) +* New lint: [`get_unwrap`] + +## 0.0.98 — 2016-11-08 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy` + +## 0.0.97 — 2016-11-03 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was + previously added for a short time under the name `clippy` but removed for + compatibility. +* `cargo clippy --help` is more helping (and less helpful :smile:) +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)* +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`] + +## 0.0.96 — 2016-10-22 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)* +* New lint: [`iter_skip_next`] + +## 0.0.95 — 2016-10-06 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)* + +## 0.0.94 — 2016-10-04 +* Fixes bustage on Windows due to forbidden directory name + +## 0.0.93 — 2016-10-03 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now + allowed by default. +* New lint: [`explicit_into_iter_loop`] + +## 0.0.92 — 2016-09-30 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)* + +## 0.0.91 — 2016-09-28 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)* + +## 0.0.90 — 2016-09-09 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)* + +## 0.0.89 — 2016-09-06 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)* + +## 0.0.88 — 2016-09-04 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* +* The following lints are not new but were only usable through the `clippy` + lint groups: [`filter_next`], `for_loop_over_option`, + `for_loop_over_result` and [`match_overlapping_arm`]. You should now be + able to `#[allow/deny]` them individually and they are available directly + through `cargo clippy`. + +## 0.0.87 — 2016-08-31 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)* +* New lints: [`builtin_type_shadow`] +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o` + +## 0.0.86 — 2016-08-28 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)* +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`] + +## 0.0.85 — 2016-08-19 +* Fix ICE with [`useless_attribute`] +* [`useless_attribute`] ignores `unused_imports` on `use` statements + +## 0.0.84 — 2016-08-18 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)* + +## 0.0.83 — 2016-08-17 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)* +* New lints: [`print_with_newline`], [`useless_attribute`] + +## 0.0.82 — 2016-08-17 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)* +* New lint: [`module_inception`] + +## 0.0.81 — 2016-08-14 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)* +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`] +* False positive fix in [`too_many_arguments`] +* Addition of functionality to [`needless_borrow`] +* Suggestions for [`clone_on_copy`] +* Bug fix in [`wrong_self_convention`] +* Doc improvements + +## 0.0.80 — 2016-07-31 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)* +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`] + +## 0.0.79 — 2016-07-10 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)* +* Major suggestions refactoring + +## 0.0.78 — 2016-07-02 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)* +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`] +* For compatibility, `cargo clippy` does not defines the `clippy` feature + introduced in 0.0.76 anymore +* [`collapsible_if`] now considers `if let` + +## 0.0.77 — 2016-06-21 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)* +* New lints: `stutter` and [`iter_nth`] + +## 0.0.76 — 2016-06-10 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)* +* `cargo clippy` now automatically defines the `clippy` feature +* New lint: [`not_unsafe_ptr_arg_deref`] + +## 0.0.75 — 2016-06-08 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)* + +## 0.0.74 — 2016-06-07 +* Fix bug with `cargo-clippy` JSON parsing +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the + “for further information visit *lint-link*” message. + +## 0.0.73 — 2016-06-05 +* Fix false positives in [`useless_let_if_seq`] + +## 0.0.72 — 2016-06-04 +* Fix false positives in [`useless_let_if_seq`] + +## 0.0.71 — 2016-05-31 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)* +* New lint: [`useless_let_if_seq`] + +## 0.0.70 — 2016-05-28 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)* +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`, + `RegexBuilder::new` and byte regexes + +## 0.0.69 — 2016-05-20 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)* +* [`used_underscore_binding`] has been made `Allow` temporarily + +## 0.0.68 — 2016-05-17 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)* +* New lint: [`unnecessary_operation`] + +## 0.0.67 — 2016-05-12 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)* + +## 0.0.66 — 2016-05-11 +* New `cargo clippy` subcommand +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`] + +## 0.0.65 — 2016-05-08 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)* +* New lints: [`float_arithmetic`], [`integer_arithmetic`] + +## 0.0.64 — 2016-04-26 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* +* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`] + +## 0.0.63 — 2016-04-08 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* + +## 0.0.62 — 2016-04-07 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)* + +## 0.0.61 — 2016-04-03 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)* +* New lint: [`invalid_upcast_comparisons`] + +## 0.0.60 — 2016-04-01 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)* + +## 0.0.59 — 2016-03-31 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)* +* New lints: [`logic_bug`], [`nonminimal_bool`] +* Fixed: [`match_same_arms`] now ignores arms with guards +* Improved: [`useless_vec`] now warns on `for … in vec![…]` + +## 0.0.58 — 2016-03-27 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)* +* New lint: [`doc_markdown`] + +## 0.0.57 — 2016-03-27 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)* +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`] +* New lint: [`crosspointer_transmute`] + +## 0.0.56 — 2016-03-23 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)* +* New lints: [`many_single_char_names`] and [`similar_names`] + +## 0.0.55 — 2016-03-21 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)* + +## 0.0.54 — 2016-03-16 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)* + +## 0.0.53 — 2016-03-15 +* Add a [configuration file] + +## ~~0.0.52~~ + +## 0.0.51 — 2016-03-13 +* Add `str` to types considered by [`len_zero`] +* New lints: [`indexing_slicing`] + +## 0.0.50 — 2016-03-11 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)* + +## 0.0.49 — 2016-03-09 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)* +* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`] + +## 0.0.48 — 2016-03-07 +* Fixed: ICE in [`needless_range_loop`] with globals + +## 0.0.47 — 2016-03-07 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)* +* New lint: [`redundant_closure_call`] + +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html +[configuration file]: ./rust-clippy#configuration +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md + + + +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss +[`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 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref +[`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 +[`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 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens +[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops +[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division +[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering +[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal +[`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_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max +[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute +[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect +[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue +[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes +[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value +[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop +[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return +[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update +[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord +[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect +[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool +[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options +[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref +[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref +[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic +[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting +[`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 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc +[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp +[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold +[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed +[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount +[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label +[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding +[`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_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 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr +[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset + diff --git a/src/tools/clippy/CODE_OF_CONDUCT.md b/src/tools/clippy/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000..dec13e44a17f8 --- /dev/null +++ b/src/tools/clippy/CODE_OF_CONDUCT.md @@ -0,0 +1,70 @@ +# The Rust Code of Conduct + +A version of this document [can be found online](https://www.rust-lang.org/conduct.html). + +## Conduct + +**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) + +* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, + gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, + religion, nationality, or other similar characteristic. +* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and + welcoming environment for all. +* Please be kind and courteous. There's no need to be mean or rude. +* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and + numerous costs. There is seldom a right answer. +* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and + see how it works. +* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We + interpret the term "harassment" as including the definition in the Citizen + Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their + definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. +* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or + made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation + team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a + safe place for you and we've got your back. +* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. + +## Moderation + + +These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, +please contact the [Rust moderation team][mod_team]. + +1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, + are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) +2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. +3. Moderators will first respond to such remarks with a warning. +4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. +5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. +6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended + party a genuine apology. +7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a + different moderator, **in private**. Complaints about bans in-channel are not allowed. +8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate + situation, they should expect less leeway than others. + +In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically +unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly +if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can +drive people away from the community entirely. + +And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was +they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good +there was something you could've communicated better — remember that it's your responsibility to make your fellow +Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about +cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their +trust. + +The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, +#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); +GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org +(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the +maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. + +*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the +[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* + +[mod_team]: https://www.rust-lang.org/team.html#Moderation-team diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md new file mode 100644 index 0000000000000..50a5ee8bbf3c8 --- /dev/null +++ b/src/tools/clippy/CONTRIBUTING.md @@ -0,0 +1,243 @@ +# Contributing to Clippy + +Hello fellow Rustacean! Great to see your interest in compiler internals and lints! + +**First**: if you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyway. You won't be +yelled at for giving it your best effort. The worst that can happen is that you'll be politely asked to change +something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that. + +Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document +explains how you can contribute and how to get started. If you have any questions about contributing or need help with +anything, feel free to ask questions on issues or visit the `#clippy` on [Discord]. + +All contributors are expected to follow the [Rust Code of Conduct]. + +* [Getting started](#getting-started) + * [Finding something to fix/improve](#finding-something-to-fiximprove) +* [Writing code](#writing-code) +* [How Clippy works](#how-clippy-works) +* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust) +* [Issue and PR Triage](#issue-and-pr-triage) +* [Bors and Homu](#bors-and-homu) +* [Contributions](#contributions) + +[Discord]: https://discord.gg/rust-lang +[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct + +## Getting started + +High level approach: + +1. Find something to fix/improve +2. Change code (likely some file in `clippy_lints/src/`) +3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script +4. Run `cargo test` in the root directory and wiggle code until it passes +5. Open a PR (also can be done after 2. if you run into problems) + +### Finding something to fix/improve + +All issues on Clippy are mentored, if you want help with a bug just ask +@Manishearth, @flip1995, @phansch or @yaahc. + +Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues. +If you want to work on an issue, please leave a comment so that we can assign it to you! + +There are also some abandoned PRs, marked with [`S-inactive-closed`]. +Pretty often these PRs are nearly completed and just need some extra steps +(formatting, addressing review comments, ...) to be merged. If you want to +complete such a PR, please leave a comment in the PR and open a new one based +on it. + +Issues marked [`T-AST`] involve simple matching of the syntax tree structure, +and are generally easier than [`T-middle`] issues, which involve types +and resolved paths. + +[`T-AST`] issues will generally need you to match against a predefined syntax structure. +To figure out how this syntax structure is encoded in the AST, it is recommended to run +`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs]. +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 [if_chain] macro, [like this][nest-less]. + +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an E-easy issue first. +They are mostly classified as [`E-medium`], since they might be somewhat involved code wise, +but not difficult per-se. + +[`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. + +[`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 +[`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium +[`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty +[nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/ +[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43 +[if_chain]: https://docs.rs/if_chain/*/if_chain +[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150 + +## Writing code + +Have a look at the [docs for writing lints][adding_lints] for more details. + +If you want to add a new lint or change existing ones apart from bugfixing, it's +also a good idea to give the [stability guarantees][rfc_stability] and +[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a +quick read. + +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md +[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md +[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees +[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories + +## How Clippy works + +[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`]. +For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this: + +```rust +// ./clippy_lints/src/lib.rs + +// ... +pub mod else_if_without_else; +// ... + +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { + // ... + store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); + // ... + + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ + // ... + LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), + // ... + ]); +} +``` + +The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints: +[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object +that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in +every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev +update_lints`. When you are writing your own lint, you can use that script to save you some time. + +```rust +// ./clippy_lints/src/else_if_without_else.rs + +use rustc_lint::{EarlyLintPass, EarlyContext}; + +// ... + +pub struct ElseIfWithoutElse; + +// ... + +impl EarlyLintPass for ElseIfWithoutElse { + // ... the functions needed, to make the lint work +} +``` + +The difference between `EarlyLintPass` and `LateLintPass` is that the methods of the `EarlyLintPass` trait only provide +AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information +via the `LateContext` parameter. + +That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the +[actual lint logic][else_if_without_else] does not depend on any type information. + +[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs +[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs +[`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html +[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass +[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html + +## Fixing build failures caused by Rust + +Clippy will sometimes fail to build from source because building it depends on unstable internal Rust features. Most of +the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures +caused by Rust updates, can be a good way to learn about Rust internals. + +In order to find out why Clippy does not work properly with a new Rust commit, you can use the [rust-toolstate commit +history][toolstate_commit_history]. You will then have to look for the last commit that contains +`test-pass -> build-fail` or `test-pass -> test-fail` for the `clippy-driver` component. +[Here][toolstate_commit] is an example. + +The commit message contains a link to the PR. The PRs are usually small enough to discover the breaking API change and +if they are bigger, they likely include some discussion that may help you to fix Clippy. + +To check if Clippy is available for a specific target platform, you can check +the [rustup component history][rustup_component_history]. + +If you decide to make Clippy work again with a Rust commit that breaks it, +you probably want to install the latest Rust from master locally and run Clippy +using that version of Rust. + +You can set up the master toolchain by running `./setup-toolchain.sh`. That script will install +[rustup-toolchain-install-master][rtim] and master toolchain, then run `rustup override set master`. + +After fixing the build failure on this repository, we can submit a pull request +to [`rust-lang/rust`] to fix the toolstate. + +To submit a pull request, you should follow these steps: + +```bash +# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory +git submodule update --remote src/tools/clippy +cargo update -p clippy +git add -u +git commit -m "Update Clippy" +./x.py test -i --stage 1 src/tools/clippy # This is optional and should succeed anyway +# Open a PR in rust-lang/rust +``` + +[rustup_component_history]: https://rust-lang.github.io/rustup-components-history +[toolstate_commit_history]: https://github.com/rust-lang-nursery/rust-toolstate/commits/master +[toolstate_commit]: https://github.com/rust-lang-nursery/rust-toolstate/commit/aad74d8294e198a7cf8ac81a91aebb7f3bbcf727 +[rtim]: https://github.com/kennytm/rustup-toolchain-install-master +[`rust-lang/rust`]: https://github.com/rust-lang/rust + +## Issue and PR triage + +Clippy is following the [Rust triage procedure][triage] for issues and pull +requests. + +However, we are a smaller project with all contributors being volunteers +currently. Between writing new lints, fixing issues, reviewing pull requests and +responding to issues there may not always be enough time to stay on top of it +all. + +Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug]. We don't +want Clippy to crash on your code and we want it to be as reliable as the +suggestions from Rust compiler errors. + +## Bors and Homu + +We use a bot powered by [Homu][homu] to help automate testing and landing of pull +requests in Clippy. The bot's username is @bors. + +You can find the Clippy bors queue [here][homu_queue]. + +If you have @bors permissions, you can find an overview of the available +commands [here][homu_instructions]. + +[triage]: https://forge.rust-lang.org/release/triage-procedure.html +[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash%20%3Aboom%3A +[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug%20%3Abeetle%3A +[homu]: https://github.com/rust-lang/homu +[homu_instructions]: https://buildbot2.rust-lang.org/homu/ +[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy + +## Contributions + +Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will +be reviewed by a core contributor (someone with permission to land patches) and either landed in the +main tree or given feedback for changes that would be required. + +All code in this repository is under the [Apache-2.0] or the [MIT] license. + + + +[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0 +[MIT]: https://opensource.org/licenses/MIT diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT new file mode 100644 index 0000000000000..80d64472c70a6 --- /dev/null +++ b/src/tools/clippy/COPYRIGHT @@ -0,0 +1,7 @@ +Copyright 2014-2020 The Rust Project Developers + +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. All files in the project carrying such notice may not be +copied, modified, or distributed except according to those terms. diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml new file mode 100644 index 0000000000000..6999b6bd74040 --- /dev/null +++ b/src/tools/clippy/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "clippy" +version = "0.0.212" +authors = [ + "Manish Goregaokar ", + "Andre Bogus ", + "Georg Brandl ", + "Martin Carton ", + "Oliver Schneider " +] +description = "A bunch of helpful lints to avoid common pitfalls in Rust" +repository = "https://github.com/rust-lang/rust-clippy" +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["clippy", "lint", "plugin"] +categories = ["development-tools", "development-tools::cargo-plugins"] +build = "build.rs" +edition = "2018" +publish = false + +[[bin]] +name = "cargo-clippy" +test = false +path = "src/main.rs" + +[[bin]] +name = "clippy-driver" +path = "src/driver.rs" + +[dependencies] +# begin automatic update +clippy_lints = { version = "0.0.212", path = "clippy_lints" } +# end automatic update +semver = "0.9" +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +tempfile = { version = "3.1.0", optional = true } +lazy_static = "1.0" + +[dev-dependencies] +cargo_metadata = "0.9.0" +compiletest_rs = { version = "0.5.0", features = ["tmp"] } +tester = "0.7" +lazy_static = "1.0" +clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } +serde = { version = "1.0", features = ["derive"] } +derive-new = "0.5" + +# A noop dependency that changes in the Rust repository, it's a bit of a hack. +# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` +# for more information. +rustc-workspace-hack = "1.0.0" + +[build-dependencies] +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} + +[features] +deny-warnings = [] +integration = ["tempfile"] diff --git a/src/tools/clippy/LICENSE-APACHE b/src/tools/clippy/LICENSE-APACHE new file mode 100644 index 0000000000000..d821a4de2bed8 --- /dev/null +++ b/src/tools/clippy/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2014-2020 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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/tools/clippy/LICENSE-MIT b/src/tools/clippy/LICENSE-MIT new file mode 100644 index 0000000000000..b7c70dd4026d9 --- /dev/null +++ b/src/tools/clippy/LICENSE-MIT @@ -0,0 +1,27 @@ +MIT License + +Copyright (c) 2014-2020 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md new file mode 100644 index 0000000000000..222b81023a770 --- /dev/null +++ b/src/tools/clippy/README.md @@ -0,0 +1,193 @@ +# Clippy + +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license) + +A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. + +[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) + +We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: + +* `clippy::all` (everything that is on by default: all the categories below except for `nursery`, `pedantic`, and `cargo`) +* `clippy::correctness` (code that is just **outright wrong** or **very very useless**, causes hard errors by default) +* `clippy::style` (code that should be written in a more idiomatic way) +* `clippy::complexity` (code that does something simple but in a complex way) +* `clippy::perf` (code that can be written in a faster way) +* `clippy::pedantic` (lints which are rather strict, off by default) +* `clippy::nursery` (new lints that aren't quite ready yet, off by default) +* `clippy::cargo` (checks against the cargo manifest, off by default) + +More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas! + +Only the following of those categories are enabled by default: + +* `clippy::style` +* `clippy::correctness` +* `clippy::complexity` +* `clippy::perf` + +Other categories need to be enabled in order for their lints to be executed. + +The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are +for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used +very selectively, if at all. + +Table of contents: + +* [Usage instructions](#usage) +* [Configuration](#configuration) +* [Contributing](#contributing) +* [License](#license) + +## Usage + +Since this is a tool for helping the developer of a library or application +write better code, it is recommended not to include Clippy as a hard dependency. +Options include using it as an optional dependency, as a cargo subcommand, or +as an included feature during build. These options are detailed below. + +### As a cargo subcommand (`cargo clippy`) + +One way to use Clippy is by installing Clippy through rustup as a cargo +subcommand. + +#### Step 1: Install rustup + +You can install [rustup](https://rustup.rs/) on supported platforms. This will help +us install Clippy and its dependencies. + +If you already have rustup installed, update to ensure you have the latest +rustup and compiler: + +```terminal +rustup update +``` + +#### Step 2: Install Clippy + +Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command: + +```terminal +rustup component add clippy +``` +If it says that it can't find the `clippy` component, please run `rustup self update`. + +#### Step 3: Run Clippy + +Now you can run Clippy by invoking the following command: + +```terminal +cargo clippy +``` + +#### Automatically applying Clippy suggestions + +Clippy can automatically apply some lint suggestions. +Note that this is still experimental and only supported on the nightly channel: + +```terminal +cargo clippy --fix -Z unstable-options +``` + +### Running Clippy from the command line without installing it + +To have cargo compile your crate with Clippy without Clippy installation +in your code, you can use: + +```terminal +cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml +``` + +*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here! + +### Travis CI + +You can add Clippy to Travis CI in the same way you use it locally: + +```yml +language: rust +rust: + - stable + - beta +before_script: + - rustup component add clippy +script: + - cargo clippy + # if you want the build job to fail when encountering warnings, use + - cargo clippy -- -D warnings + # in order to also check tests and non-default crate features, use + - cargo clippy --all-targets --all-features -- -D warnings + - cargo test + # etc. +``` + +If you are on nightly, It might happen that Clippy is not available for a certain nightly release. +In this case you can try to conditionally install Clippy from the Git repo. + +```yaml +language: rust +rust: + - nightly +before_script: + - rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy + # etc. +``` + +Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code. +That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause +an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command +line. (You can swap `clippy::all` with the specific lint category you are targeting.) + +## Configuration + +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = +value` mapping eg. + +```toml +blacklisted-names = ["toto", "tata", "titi"] +cognitive-complexity-threshold = 30 +``` + +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which +lints can be configured and the meaning of the variables. + +To deactivate the “for further information visit *lint-link*” message you can +define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. + +### Allowing/denying lints + +You can add options to your code to `allow`/`warn`/`deny` Clippy lints: + +* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`) + +* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, + `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive + lints prone to false positives. + +* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) + +* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc. + +Note: `deny` produces errors instead of warnings. + +If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra +flags to Clippy during the run: `cargo clippy -- -A clippy::lint_name` will run Clippy with `lint_name` disabled and +`cargo clippy -- -W clippy::lint_name` will run it with that enabled. This also works with lint groups. For example you +can run Clippy with warnings for all lints enabled: `cargo clippy -- -W clippy::pedantic` +If you care only about a single lint, you can allow all others and then explicitly reenable +the lint(s) you are interested in: `cargo clippy -- -Aclippy::all -Wclippy::useless_format -Wclippy::...` + +## Contributing + +If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md). + +## License + +Copyright 2014-2020 The Rust Project Developers + +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. Files in the project may not be +copied, modified, or distributed except according to those terms. diff --git a/src/tools/clippy/build.rs b/src/tools/clippy/build.rs new file mode 100644 index 0000000000000..018375dbada87 --- /dev/null +++ b/src/tools/clippy/build.rs @@ -0,0 +1,19 @@ +fn main() { + // Forward the profile to the main compilation + println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap()); + // Don't rebuild even if nothing changed + println!("cargo:rerun-if-changed=build.rs"); + // forward git repo hashes we build at + println!( + "cargo:rustc-env=GIT_HASH={}", + rustc_tools_util::get_commit_hash().unwrap_or_default() + ); + println!( + "cargo:rustc-env=COMMIT_DATE={}", + rustc_tools_util::get_commit_date().unwrap_or_default() + ); + println!( + "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", + rustc_tools_util::get_channel().unwrap_or_default() + ); +} diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml new file mode 100644 index 0000000000000..c861efc8afb50 --- /dev/null +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "clippy_dev" +version = "0.0.1" +authors = ["Philipp Hansch "] +edition = "2018" + +[dependencies] +bytecount = "0.6" +clap = "2.33" +itertools = "0.9" +regex = "1" +lazy_static = "1.0" +shell-escape = "0.1" +walkdir = "2" + +[features] +deny-warnings = [] diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs new file mode 100644 index 0000000000000..6ae3f58c1f2ad --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -0,0 +1,175 @@ +use crate::clippy_project_root; +use shell_escape::escape; +use std::ffi::OsStr; +use std::io; +use std::path::Path; +use std::process::{self, Command}; +use walkdir::WalkDir; + +#[derive(Debug)] +pub enum CliError { + CommandFailed(String), + IoError(io::Error), + RustfmtNotInstalled, + WalkDirError(walkdir::Error), +} + +impl From for CliError { + fn from(error: io::Error) -> Self { + Self::IoError(error) + } +} + +impl From for CliError { + fn from(error: walkdir::Error) -> Self { + Self::WalkDirError(error) + } +} + +struct FmtContext { + check: bool, + verbose: bool, +} + +pub fn run(check: bool, verbose: bool) { + fn try_run(context: &FmtContext) -> Result { + let mut success = true; + + let project_root = clippy_project_root(); + + rustfmt_test(context)?; + + success &= cargo_fmt(context, project_root.as_path())?; + success &= cargo_fmt(context, &project_root.join("clippy_dev"))?; + success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?; + + for entry in WalkDir::new(project_root.join("tests")) { + let entry = entry?; + let path = entry.path(); + + if path.extension() != Some("rs".as_ref()) + || entry.file_name() == "ice-3891.rs" + // Avoid rustfmt bug rust-lang/rustfmt#1873 + || cfg!(windows) && entry.file_name() == "implicit_hasher.rs" + { + continue; + } + + success &= rustfmt(context, &path)?; + } + + Ok(success) + } + + fn output_err(err: CliError) { + match err { + CliError::CommandFailed(command) => { + eprintln!("error: A command failed! `{}`", command); + }, + CliError::IoError(err) => { + eprintln!("error: {}", err); + }, + CliError::RustfmtNotInstalled => { + eprintln!("error: rustfmt nightly is not installed."); + }, + CliError::WalkDirError(err) => { + eprintln!("error: {}", err); + }, + } + } + + let context = FmtContext { check, verbose }; + let result = try_run(&context); + let code = match result { + Ok(true) => 0, + Ok(false) => { + eprintln!(); + eprintln!("Formatting check failed."); + eprintln!("Run `cargo dev fmt` to update formatting."); + 1 + }, + Err(err) => { + output_err(err); + 1 + }, + }; + process::exit(code); +} + +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( + context: &FmtContext, + program: impl AsRef, + dir: impl AsRef, + args: &[impl AsRef], +) -> Result { + if context.verbose { + println!("{}", format_command(&program, &dir, args)); + } + + let mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?; + let code = child.wait()?; + let success = code.success(); + + if !context.check && !success { + return Err(CliError::CommandFailed(format_command(&program, &dir, args))); + } + + Ok(success) +} + +fn cargo_fmt(context: &FmtContext, path: &Path) -> Result { + let mut args = vec!["+nightly", "fmt", "--all"]; + if context.check { + args.push("--"); + args.push("--check"); + } + let success = exec(context, "cargo", path, &args)?; + + Ok(success) +} + +fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { + let program = "rustfmt"; + let dir = std::env::current_dir()?; + let args = &["+nightly", "--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(CliError::RustfmtNotInstalled) + } else { + Err(CliError::CommandFailed(format_command(&program, &dir, args))) + } +} + +fn rustfmt(context: &FmtContext, path: &Path) -> Result { + let mut args = vec!["+nightly".as_ref(), path.as_os_str()]; + if context.check { + args.push("--check".as_ref()); + } + let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?; + if !success { + eprintln!("rustfmt failed on {}", path.display()); + } + Ok(success) +} diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs new file mode 100644 index 0000000000000..6fdd282c6849e --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -0,0 +1,524 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] + +use itertools::Itertools; +use lazy_static::lazy_static; +use regex::Regex; +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fs; +use std::path::{Path, PathBuf}; +use walkdir::WalkDir; + +pub mod fmt; +pub mod new_lint; +pub mod stderr_length_check; +pub mod update_lints; + +lazy_static! { + static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new( + r#"(?x) + declare_clippy_lint!\s*[\{(] + (?:\s+///.*)* + \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* + (?P[a-z_]+)\s*,\s* + "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] + "# + ) + .unwrap(); + static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new( + r#"(?x) + declare_deprecated_lint!\s*[{(]\s* + (?:\s+///.*)* + \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* + "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] + "# + ) + .unwrap(); + static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap(); +} + +pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; + +/// Lint data parsed from the Clippy source code. +#[derive(Clone, PartialEq, Debug)] +pub struct Lint { + pub name: String, + pub group: String, + pub desc: String, + pub deprecation: Option, + pub module: String, +} + +impl Lint { + #[must_use] + pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self { + Self { + name: name.to_lowercase(), + group: group.to_string(), + desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(), + deprecation: deprecation.map(ToString::to_string), + module: module.to_string(), + } + } + + /// Returns all non-deprecated lints and non-internal lints + #[must_use] + pub fn usable_lints(lints: &[Self]) -> Vec { + lints + .iter() + .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal")) + .cloned() + .collect() + } + + /// Returns all internal lints (not `internal_warn` lints) + #[must_use] + pub fn internal_lints(lints: &[Self]) -> Vec { + lints.iter().filter(|l| l.group == "internal").cloned().collect() + } + + /// Returns all deprecated lints + #[must_use] + pub fn deprecated_lints(lints: &[Self]) -> Vec { + lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect() + } + + /// Returns the lints in a `HashMap`, grouped by the different lint groups + #[must_use] + pub fn by_lint_group(lints: impl Iterator) -> HashMap> { + lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() + } +} + +/// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`. +#[must_use] +pub fn gen_lint_group_list<'a>(lints: impl Iterator) -> Vec { + lints + .map(|l| format!(" LintId::of(&{}::{}),", l.module, l.name.to_uppercase())) + .sorted() + .collect::>() +} + +/// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`. +#[must_use] +pub fn gen_modules_list<'a>(lints: impl Iterator) -> Vec { + lints + .map(|l| &l.module) + .unique() + .map(|module| format!("mod {};", module)) + .sorted() + .collect::>() +} + +/// Generates the list of lint links at the bottom of the README +#[must_use] +pub fn gen_changelog_lint_list<'a>(lints: impl Iterator) -> Vec { + lints + .sorted_by_key(|l| &l.name) + .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name)) + .collect() +} + +/// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`. +#[must_use] +pub fn gen_deprecated<'a>(lints: impl Iterator) -> Vec { + lints + .flat_map(|l| { + l.deprecation + .clone() + .map(|depr_text| { + vec![ + " store.register_removed(".to_string(), + format!(" \"clippy::{}\",", l.name), + format!(" \"{}\",", depr_text), + " );".to_string(), + ] + }) + .expect("only deprecated lints should be passed") + }) + .collect::>() +} + +#[must_use] +pub fn gen_register_lint_list<'a>(lints: impl Iterator) -> Vec { + let pre = " store.register_lints(&[".to_string(); + let post = " ]);".to_string(); + let mut inner = lints + .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) + .sorted() + .collect::>(); + inner.insert(0, pre); + inner.push(post); + inner +} + +/// Gathers all files in `src/clippy_lints` and gathers all lints inside +pub fn gather_all() -> impl Iterator { + lint_files().flat_map(|f| gather_from_file(&f)) +} + +fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator { + let content = fs::read_to_string(dir_entry.path()).unwrap(); + let path = dir_entry.path(); + let filename = path.file_stem().unwrap(); + let path_buf = path.with_file_name(filename); + let mut rel_path = path_buf + .strip_prefix(clippy_project_root().join("clippy_lints/src")) + .expect("only files in `clippy_lints/src` should be looked at"); + // If the lints are stored in mod.rs, we get the module name from + // the containing directory: + if filename == "mod" { + rel_path = rel_path.parent().unwrap(); + } + + let module = rel_path + .components() + .map(|c| c.as_os_str().to_str().unwrap()) + .collect::>() + .join("::"); + + parse_contents(&content, &module) +} + +fn parse_contents(content: &str, module: &str) -> impl Iterator { + let lints = DEC_CLIPPY_LINT_RE + .captures_iter(content) + .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module)); + let deprecated = DEC_DEPRECATED_LINT_RE + .captures_iter(content) + .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module)); + // Removing the `.collect::>().into_iter()` causes some lifetime issues due to the map + lints.chain(deprecated).collect::>().into_iter() +} + +/// Collects all .rs files in the `clippy_lints/src` directory +fn lint_files() -> impl Iterator { + // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. + // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`. + let path = clippy_project_root().join("clippy_lints/src"); + WalkDir::new(path) + .into_iter() + .filter_map(Result::ok) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) +} + +/// Whether a file has had its text changed or not +#[derive(PartialEq, Debug)] +pub struct FileChange { + pub changed: bool, + pub new_lines: String, +} + +/// 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. +pub fn replace_region_in_file( + path: &Path, + start: &str, + end: &str, + replace_start: bool, + write_back: bool, + replacements: F, +) -> FileChange +where + F: FnOnce() -> Vec, +{ + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e)); + let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements); + + if write_back { + if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) { + panic!("Cannot write to {}: {}", path.display(), e); + } + } + file_change +} + +/// Replaces a region in a text delimited by two lines matching regexes. +/// +/// * `text` is the input text on which you want to perform the replacement +/// * `start` is a `&str` that describes the delimiter line before the region you want to replace. +/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. +/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen. +/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. +/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end` +/// delimiter line is never replaced. +/// * `replacements` is a closure that has to return a `Vec` which contains the new text. +/// +/// If you want to perform the replacement on files instead of already parsed text, +/// use `replace_region_in_file`. +/// +/// # Example +/// +/// ``` +/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; +/// let result = +/// clippy_dev::replace_region_in_text(the_text, "replace_start", "replace_end", false, || { +/// vec!["a different".to_string(), "text".to_string()] +/// }) +/// .new_lines; +/// assert_eq!("replace_start\na different\ntext\nreplace_end", result); +/// ``` +pub fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange +where + F: FnOnce() -> Vec, +{ + let replace_it = replacements(); + let mut in_old_region = false; + let mut found = false; + let mut new_lines = vec![]; + let start = Regex::new(start).unwrap(); + let end = Regex::new(end).unwrap(); + + for line in text.lines() { + if in_old_region { + if end.is_match(line) { + in_old_region = false; + new_lines.extend(replace_it.clone()); + new_lines.push(line.to_string()); + } + } else if start.is_match(line) { + if !replace_start { + new_lines.push(line.to_string()); + } + in_old_region = true; + found = true; + } else { + new_lines.push(line.to_string()); + } + } + + if !found { + // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the + // given text or file. Most likely this is an error on the programmer's side and the Regex + // is incorrect. + eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start); + std::process::exit(1); + } + + let mut new_lines = new_lines.join("\n"); + if text.ends_with('\n') { + new_lines.push('\n'); + } + let changed = new_lines != text; + FileChange { changed, new_lines } +} + +/// Returns the path to the Clippy project 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 = std::fs::read_to_string(path.join("Cargo.toml")); + if let Err(err) = &result { + if err.kind() == std::io::ErrorKind::NotFound { + continue; + } + } + + let content = result.unwrap(); + if content.contains("[package]\nname = \"clippy\"") { + return path.to_path_buf(); + } + } + panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); +} + +#[test] +fn test_parse_contents() { + let result: Vec = parse_contents( + r#" +declare_clippy_lint! { + pub PTR_ARG, + style, + "really long \ + text" +} + +declare_clippy_lint!{ + pub DOC_MARKDOWN, + pedantic, + "single line" +} + +/// some doc comment +declare_deprecated_lint! { + pub SHOULD_ASSERT_EQ, + "`assert!()` will be more flexible with RFC 2011" +} + "#, + "module_name", + ) + .collect(); + + let expected = vec![ + Lint::new("ptr_arg", "style", "really long text", None, "module_name"), + Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"), + Lint::new( + "should_assert_eq", + "Deprecated", + "`assert!()` will be more flexible with RFC 2011", + Some("`assert!()` will be more flexible with RFC 2011"), + "module_name", + ), + ]; + assert_eq!(expected, result); +} + +#[test] +fn test_replace_region() { + let text = "\nabc\n123\n789\ndef\nghi"; + let expected = FileChange { + changed: true, + new_lines: "\nabc\nhello world\ndef\nghi".to_string(), + }; + let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || { + vec!["hello world".to_string()] + }); + assert_eq!(expected, result); +} + +#[test] +fn test_replace_region_with_start() { + let text = "\nabc\n123\n789\ndef\nghi"; + let expected = FileChange { + changed: true, + new_lines: "\nhello world\ndef\nghi".to_string(), + }; + let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || { + vec!["hello world".to_string()] + }); + assert_eq!(expected, result); +} + +#[test] +fn test_replace_region_no_changes() { + let text = "123\n456\n789"; + let expected = FileChange { + changed: false, + new_lines: "123\n456\n789".to_string(), + }; + let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, || vec![]); + assert_eq!(expected, result); +} + +#[test] +fn test_usable_lints() { + let lints = vec![ + Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"), + Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"), + ]; + let expected = vec![Lint::new( + "should_assert_eq2", + "Not Deprecated", + "abc", + None, + "module_name", + )]; + assert_eq!(expected, Lint::usable_lints(&lints)); +} + +#[test] +fn test_by_lint_group() { + let lints = vec![ + Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), + Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + ]; + let mut expected: HashMap> = HashMap::new(); + expected.insert( + "group1".to_string(), + vec![ + Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), + Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + ], + ); + expected.insert( + "group2".to_string(), + vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")], + ); + assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); +} + +#[test] +fn test_gen_changelog_lint_list() { + let lints = vec![ + Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), + ]; + let expected = vec![ + format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()), + format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()), + ]; + assert_eq!(expected, gen_changelog_lint_list(lints.iter())); +} + +#[test] +fn test_gen_deprecated() { + let lints = vec![ + Lint::new( + "should_assert_eq", + "group1", + "abc", + Some("has been superseded by should_assert_eq2"), + "module_name", + ), + Lint::new( + "another_deprecated", + "group2", + "abc", + Some("will be removed"), + "module_name", + ), + ]; + let expected: Vec = vec![ + " store.register_removed(", + " \"clippy::should_assert_eq\",", + " \"has been superseded by should_assert_eq2\",", + " );", + " store.register_removed(", + " \"clippy::another_deprecated\",", + " \"will be removed\",", + " );", + ] + .into_iter() + .map(String::from) + .collect(); + assert_eq!(expected, gen_deprecated(lints.iter())); +} + +#[test] +#[should_panic] +fn test_gen_deprecated_fail() { + let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")]; + let _ = gen_deprecated(lints.iter()); +} + +#[test] +fn test_gen_modules_list() { + let lints = vec![ + Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), + Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"), + ]; + let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()]; + assert_eq!(expected, gen_modules_list(lints.iter())); +} + +#[test] +fn test_gen_lint_group_list() { + let lints = vec![ + Lint::new("abc", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), + Lint::new("internal", "internal_style", "abc", None, "module_name"), + ]; + let expected = vec![ + " LintId::of(&module_name::ABC),".to_string(), + " LintId::of(&module_name::INTERNAL),".to_string(), + " LintId::of(&module_name::SHOULD_ASSERT_EQ),".to_string(), + ]; + assert_eq!(expected, gen_lint_group_list(lints.iter())); +} diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs new file mode 100644 index 0000000000000..d99235f7c07a7 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -0,0 +1,120 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] + +use clap::{App, Arg, SubCommand}; +use clippy_dev::{fmt, new_lint, stderr_length_check, update_lints}; + +fn main() { + let matches = App::new("Clippy developer tooling") + .subcommand( + SubCommand::with_name("fmt") + .about("Run rustfmt on all projects and tests") + .arg( + Arg::with_name("check") + .long("check") + .help("Use the rustfmt --check option"), + ) + .arg( + Arg::with_name("verbose") + .short("v") + .long("verbose") + .help("Echo commands run"), + ), + ) + .subcommand( + SubCommand::with_name("update_lints") + .about("Updates lint registration and information from the source code") + .long_about( + "Makes sure that:\n \ + * the lint count in README.md is correct\n \ + * the changelog contains markdown link references at the bottom\n \ + * all lint groups include the correct lints\n \ + * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ + * all lints are registered in the lint store", + ) + .arg(Arg::with_name("print-only").long("print-only").help( + "Print a table of lints to STDOUT. \ + This does not include deprecated and internal lints. \ + (Does not modify any files)", + )) + .arg( + Arg::with_name("check") + .long("check") + .help("Checks that `cargo dev update_lints` has been run. Used on CI."), + ), + ) + .subcommand( + SubCommand::with_name("new_lint") + .about("Create new lint and run `cargo dev update_lints`") + .arg( + Arg::with_name("pass") + .short("p") + .long("pass") + .help("Specify whether the lint runs during the early or late pass") + .takes_value(true) + .possible_values(&["early", "late"]) + .required(true), + ) + .arg( + Arg::with_name("name") + .short("n") + .long("name") + .help("Name of the new lint in snake case, ex: fn_too_long") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("category") + .short("c") + .long("category") + .help("What category the lint belongs to") + .default_value("nursery") + .possible_values(&[ + "style", + "correctness", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + "internal_warn", + ]) + .takes_value(true), + ), + ) + .subcommand( + SubCommand::with_name("limit_stderr_length") + .about("Ensures that stderr files do not grow longer than a certain amount of lines."), + ) + .get_matches(); + + match matches.subcommand() { + ("fmt", Some(matches)) => { + fmt::run(matches.is_present("check"), matches.is_present("verbose")); + }, + ("update_lints", Some(matches)) => { + if matches.is_present("print-only") { + update_lints::print_lints(); + } else if matches.is_present("check") { + update_lints::run(update_lints::UpdateMode::Check); + } else { + update_lints::run(update_lints::UpdateMode::Change); + } + }, + ("new_lint", Some(matches)) => { + match new_lint::create( + matches.value_of("pass"), + matches.value_of("name"), + matches.value_of("category"), + ) { + Ok(_) => update_lints::run(update_lints::UpdateMode::Change), + Err(e) => eprintln!("Unable to create lint: {}", e), + } + }, + ("limit_stderr_length", _) => { + stderr_length_check::check(); + }, + _ => {}, + } +} diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs new file mode 100644 index 0000000000000..44b2a5383d211 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -0,0 +1,177 @@ +use crate::clippy_project_root; +use std::fs::{File, OpenOptions}; +use std::io; +use std::io::prelude::*; +use std::io::ErrorKind; +use std::path::Path; + +/// Creates files required to implement and test a new lint and runs `update_lints`. +/// +/// # Errors +/// +/// This function errors, if the files couldn't be created +pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> { + let pass = pass.expect("`pass` argument is validated by clap"); + let lint_name = lint_name.expect("`name` argument is validated by clap"); + let category = category.expect("`category` argument is validated by clap"); + + match open_files(lint_name) { + Ok((mut test_file, mut lint_file)) => { + let (pass_type, pass_lifetimes, pass_import, context_import) = match pass { + "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), + "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), + _ => { + unreachable!("`pass_type` should only ever be `early` or `late`!"); + }, + }; + + let camel_case_name = to_camel_case(lint_name); + + if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) { + return Err(io::Error::new( + ErrorKind::Other, + format!("Could not write to test file: {}", e), + )); + }; + + if let Err(e) = lint_file.write_all( + get_lint_file_contents( + pass_type, + pass_lifetimes, + lint_name, + &camel_case_name, + category, + pass_import, + context_import, + ) + .as_bytes(), + ) { + return Err(io::Error::new( + ErrorKind::Other, + format!("Could not write to lint file: {}", e), + )); + } + Ok(()) + }, + Err(e) => Err(io::Error::new( + ErrorKind::Other, + format!("Unable to create lint: {}", e), + )), + } +} + +fn open_files(lint_name: &str) -> Result<(File, File), io::Error> { + let project_root = clippy_project_root(); + + let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name)); + let lint_file_path = project_root + .join("clippy_lints") + .join("src") + .join(format!("{}.rs", lint_name)); + + if Path::new(&test_file_path).exists() { + return Err(io::Error::new( + ErrorKind::AlreadyExists, + format!("test file {:?} already exists", test_file_path), + )); + } + if Path::new(&lint_file_path).exists() { + return Err(io::Error::new( + ErrorKind::AlreadyExists, + format!("lint file {:?} already exists", lint_file_path), + )); + } + + let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?; + let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?; + + Ok((test_file, lint_file)) +} + +fn to_camel_case(name: &str) -> String { + name.split('_') + .map(|s| { + if s.is_empty() { + String::from("") + } else { + [&s[0..1].to_uppercase(), &s[1..]].concat() + } + }) + .collect() +} + +fn get_test_file_contents(lint_name: &str) -> String { + format!( + "#![warn(clippy::{})] + +fn main() {{ + // test code goes here +}} +", + lint_name + ) +} + +fn get_lint_file_contents( + pass_type: &str, + pass_lifetimes: &str, + lint_name: &str, + camel_case_name: &str, + category: &str, + pass_import: &str, + context_import: &str, +) -> String { + format!( + "use rustc_lint::{{{type}, {context_import}}}; +use rustc_session::{{declare_lint_pass, declare_tool_lint}}; +{pass_import} + +declare_clippy_lint! {{ + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub {name_upper}, + {category}, + \"default lint description\" +}} + +declare_lint_pass!({name_camel} => [{name_upper}]); + +impl {type}{lifetimes} for {name_camel} {{}} +", + type=pass_type, + lifetimes=pass_lifetimes, + name_upper=lint_name.to_uppercase(), + name_camel=camel_case_name, + category=category, + pass_import=pass_import, + context_import=context_import + ) +} + +#[test] +fn test_camel_case() { + let s = "a_lint"; + let s2 = to_camel_case(s); + assert_eq!(s2, "ALint"); + + let name = "a_really_long_new_lint"; + let name2 = to_camel_case(name); + assert_eq!(name2, "AReallyLongNewLint"); + + let name3 = "lint__name"; + let name4 = to_camel_case(name3); + assert_eq!(name4, "LintName"); +} diff --git a/src/tools/clippy/clippy_dev/src/stderr_length_check.rs b/src/tools/clippy/clippy_dev/src/stderr_length_check.rs new file mode 100644 index 0000000000000..e02b6f7da5f7b --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/stderr_length_check.rs @@ -0,0 +1,51 @@ +use crate::clippy_project_root; +use std::ffi::OsStr; +use std::fs; +use std::path::{Path, PathBuf}; +use walkdir::WalkDir; + +// The maximum length allowed for stderr files. +// +// We limit this because small files are easier to deal with than bigger files. +const LENGTH_LIMIT: usize = 200; + +pub fn check() { + let exceeding_files: Vec<_> = exceeding_stderr_files(); + + if !exceeding_files.is_empty() { + eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT); + for (path, count) in exceeding_files { + println!("{}: {}", path.display(), count); + } + std::process::exit(1); + } +} + +fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> { + // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. + WalkDir::new(clippy_project_root().join("tests/ui")) + .into_iter() + .filter_map(Result::ok) + .filter(|f| !f.file_type().is_dir()) + .filter_map(|e| { + let p = e.into_path(); + let count = count_linenumbers(&p); + if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT { + Some((p, count)) + } else { + None + } + }) + .collect() +} + +#[must_use] +fn count_linenumbers(filepath: &Path) -> usize { + match fs::read(filepath) { + Ok(content) => bytecount::count(&content, b'\n'), + Err(e) => { + eprintln!("Failed to read file: {}", e); + 0 + }, + } +} diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs new file mode 100644 index 0000000000000..a9a7092994269 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -0,0 +1,162 @@ +use crate::{ + gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list, + replace_region_in_file, Lint, DOCS_LINK, +}; +use std::path::Path; + +#[derive(Clone, Copy, PartialEq)] +pub enum UpdateMode { + Check, + Change, +} + +#[allow(clippy::too_many_lines)] +pub fn run(update_mode: UpdateMode) { + let lint_list: Vec = gather_all().collect(); + + let internal_lints = Lint::internal_lints(&lint_list); + let deprecated_lints = Lint::deprecated_lints(&lint_list); + let usable_lints = Lint::usable_lints(&lint_list); + let mut sorted_usable_lints = usable_lints.clone(); + sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); + + let usable_lint_count = round_to_fifty(usable_lints.len()); + + let mut file_change = replace_region_in_file( + Path::new("src/lintlist/mod.rs"), + "begin lint list", + "end lint list", + false, + update_mode == UpdateMode::Change, + || { + format!("pub static ref ALL_LINTS: Vec = vec!{:#?};", sorted_usable_lints) + .lines() + .map(ToString::to_string) + .collect::>() + }, + ) + .changed; + + file_change |= replace_region_in_file( + Path::new("README.md"), + &format!( + r#"\[There are over \d+ lints included in this crate!\]\({}\)"#, + DOCS_LINK + ), + "", + true, + update_mode == UpdateMode::Change, + || { + vec![format!( + "[There are over {} lints included in this crate!]({})", + usable_lint_count, DOCS_LINK + )] + }, + ) + .changed; + + file_change |= replace_region_in_file( + Path::new("CHANGELOG.md"), + "", + "", + false, + update_mode == UpdateMode::Change, + || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())), + ) + .changed; + + file_change |= replace_region_in_file( + Path::new("clippy_lints/src/lib.rs"), + "begin deprecated lints", + "end deprecated lints", + false, + update_mode == UpdateMode::Change, + || gen_deprecated(deprecated_lints.iter()), + ) + .changed; + + file_change |= replace_region_in_file( + Path::new("clippy_lints/src/lib.rs"), + "begin register lints", + "end register lints", + false, + update_mode == UpdateMode::Change, + || gen_register_lint_list(usable_lints.iter().chain(internal_lints.iter())), + ) + .changed; + + file_change |= replace_region_in_file( + Path::new("clippy_lints/src/lib.rs"), + "begin lints modules", + "end lints modules", + false, + update_mode == UpdateMode::Change, + || gen_modules_list(usable_lints.iter()), + ) + .changed; + + // Generate lists of lints in the clippy::all lint group + file_change |= replace_region_in_file( + Path::new("clippy_lints/src/lib.rs"), + r#"store.register_group\(true, "clippy::all""#, + r#"\]\);"#, + false, + update_mode == UpdateMode::Change, + || { + // clippy::all should only include the following lint groups: + let all_group_lints = usable_lints.iter().filter(|l| { + l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf" + }); + + gen_lint_group_list(all_group_lints) + }, + ) + .changed; + + // Generate the list of lints for all other lint groups + for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { + file_change |= replace_region_in_file( + Path::new("clippy_lints/src/lib.rs"), + &format!("store.register_group\\(true, \"clippy::{}\"", lint_group), + r#"\]\);"#, + false, + update_mode == UpdateMode::Change, + || gen_lint_group_list(lints.iter()), + ) + .changed; + } + + if update_mode == UpdateMode::Check && file_change { + println!( + "Not all lints defined properly. \ + Please run `cargo dev update_lints` to make sure all lints are defined properly." + ); + std::process::exit(1); + } +} + +pub fn print_lints() { + let lint_list: Vec = gather_all().collect(); + 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 { + if lint_group == "Deprecated" { + continue; + } + println!("\n## {}", lint_group); + + lints.sort_by_key(|l| l.name.clone()); + + for lint in lints { + println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc); + } + } + + println!("there are {} lints", usable_lint_count); +} + +fn round_to_fifty(count: usize) -> usize { + count / 50 * 50 +} diff --git a/src/tools/clippy/clippy_dummy/Cargo.toml b/src/tools/clippy/clippy_dummy/Cargo.toml new file mode 100644 index 0000000000000..7b11795fafdc5 --- /dev/null +++ b/src/tools/clippy/clippy_dummy/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "clippy_dummy" # rename to clippy before publishing +version = "0.0.303" +authors = ["Manish Goregaokar "] +edition = "2018" +readme = "crates-readme.md" +description = "A bunch of helpful lints to avoid common pitfalls in Rust." +build = 'build.rs' + +repository = "https://github.com/rust-lang/rust-clippy" + +license = "MIT OR Apache-2.0" +keywords = ["clippy", "lint", "plugin"] +categories = ["development-tools", "development-tools::cargo-plugins"] + +[build-dependencies] +term = "0.6" diff --git a/src/tools/clippy/clippy_dummy/PUBLISH.md b/src/tools/clippy/clippy_dummy/PUBLISH.md new file mode 100644 index 0000000000000..8e420ec959a26 --- /dev/null +++ b/src/tools/clippy/clippy_dummy/PUBLISH.md @@ -0,0 +1,6 @@ +This is a dummy crate to publish to crates.io. It primarily exists to ensure +that folks trying to install clippy from crates.io get redirected to the +`rustup` technique. + +Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`, +it has a different name to avoid workspace issues. diff --git a/src/tools/clippy/clippy_dummy/build.rs b/src/tools/clippy/clippy_dummy/build.rs new file mode 100644 index 0000000000000..21af4f8244f44 --- /dev/null +++ b/src/tools/clippy/clippy_dummy/build.rs @@ -0,0 +1,42 @@ +use term::color::{GREEN, RED, WHITE}; +use term::{Attr, Error, Result}; + +fn main() { + if foo().is_err() { + eprintln!( + "error: Clippy is no longer available via crates.io\n\n\ + help: please run `rustup component add clippy` instead" + ); + } + std::process::exit(1); +} + +fn foo() -> Result<()> { + let mut t = term::stderr().ok_or(Error::NotSupported)?; + + t.attr(Attr::Bold)?; + t.fg(RED)?; + write!(t, "\nerror: ")?; + + t.reset()?; + t.fg(WHITE)?; + writeln!(t, "Clippy is no longer available via crates.io\n")?; + + t.attr(Attr::Bold)?; + t.fg(GREEN)?; + write!(t, "help: ")?; + + t.reset()?; + t.fg(WHITE)?; + write!(t, "please run `")?; + + t.attr(Attr::Bold)?; + write!(t, "rustup component add clippy")?; + + t.reset()?; + t.fg(WHITE)?; + writeln!(t, "` instead")?; + + t.reset()?; + Ok(()) +} diff --git a/src/tools/clippy/clippy_dummy/crates-readme.md b/src/tools/clippy/clippy_dummy/crates-readme.md new file mode 100644 index 0000000000000..0decae8b9103d --- /dev/null +++ b/src/tools/clippy/clippy_dummy/crates-readme.md @@ -0,0 +1,9 @@ +Installing clippy via crates.io is deprecated. Please use the following: + +```terminal +rustup component add clippy +``` + +on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary. + +See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information diff --git a/src/tools/clippy/clippy_dummy/src/main.rs b/src/tools/clippy/clippy_dummy/src/main.rs new file mode 100644 index 0000000000000..a118834f1fd47 --- /dev/null +++ b/src/tools/clippy/clippy_dummy/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + panic!("This shouldn't even compile") +} diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml new file mode 100644 index 0000000000000..1c0be72783462 --- /dev/null +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "clippy_lints" +# begin automatic update +version = "0.0.212" +# end automatic update +authors = [ + "Manish Goregaokar ", + "Andre Bogus ", + "Georg Brandl ", + "Martin Carton " +] +description = "A bunch of helpful lints to avoid common pitfalls in Rust" +repository = "https://github.com/rust-lang/rust-clippy" +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["clippy", "lint", "plugin"] +edition = "2018" + +[dependencies] +cargo_metadata = "0.9.0" +if_chain = "1.0.0" +itertools = "0.9" +lazy_static = "1.0.2" +pulldown-cmark = { version = "0.7", default-features = false } +quine-mc_cluskey = "0.2.2" +regex-syntax = "0.6" +serde = { version = "1.0", features = ["derive"] } +smallvec = { version = "1", features = ["union"] } +toml = "0.5.3" +unicode-normalization = "0.1" +semver = "0.9.0" +# NOTE: cargo requires serde feat in its url dep +# see +url = { version = "2.1.0", features = ["serde"] } + +[features] +deny-warnings = [] diff --git a/src/tools/clippy/clippy_lints/README.md b/src/tools/clippy/clippy_lints/README.md new file mode 100644 index 0000000000000..513583b7e349e --- /dev/null +++ b/src/tools/clippy/clippy_lints/README.md @@ -0,0 +1 @@ +This crate contains Clippy lints. For the main crate, check [GitHub](https://github.com/rust-lang/rust-clippy). diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs new file mode 100644 index 0000000000000..7e6e2c7eaebea --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -0,0 +1,117 @@ +use crate::utils::span_lint; +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol; +use std::f64::consts as f64; + +declare_clippy_lint! { + /// **What it does:** Checks for floating point literals that approximate + /// constants which are defined in + /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants) + /// or + /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants), + /// respectively, suggesting to use the predefined constant. + /// + /// **Why is this bad?** Usually, the definition in the standard library is more + /// precise than what people come up with. If you find that your definition is + /// actually more precise, please [file a Rust + /// issue](https://github.com/rust-lang/rust/issues). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = 3.14; + /// let y = 1_f64 / x; + /// ``` + /// Use predefined constants instead: + /// ```rust + /// let x = std::f32::consts::PI; + /// let y = std::f64::consts::FRAC_1_PI; + /// ``` + pub APPROX_CONSTANT, + correctness, + "the approximate of a known float constant (in `std::fXX::consts`)" +} + +// Tuples are of the form (constant, name, min_digits) +const KNOWN_CONSTS: [(f64, &str, usize); 18] = [ + (f64::E, "E", 4), + (f64::FRAC_1_PI, "FRAC_1_PI", 4), + (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5), + (f64::FRAC_2_PI, "FRAC_2_PI", 5), + (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5), + (f64::FRAC_PI_2, "FRAC_PI_2", 5), + (f64::FRAC_PI_3, "FRAC_PI_3", 5), + (f64::FRAC_PI_4, "FRAC_PI_4", 5), + (f64::FRAC_PI_6, "FRAC_PI_6", 5), + (f64::FRAC_PI_8, "FRAC_PI_8", 5), + (f64::LN_10, "LN_10", 5), + (f64::LN_2, "LN_2", 5), + (f64::LOG10_E, "LOG10_E", 5), + (f64::LOG2_E, "LOG2_E", 5), + (f64::LOG2_10, "LOG2_10", 5), + (f64::LOG10_2, "LOG10_2", 5), + (f64::PI, "PI", 3), + (f64::SQRT_2, "SQRT_2", 5), +]; + +declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Lit(lit) = &e.kind { + check_lit(cx, &lit.node, e); + } + } +} + +fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr<'_>) { + match *lit { + LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { + FloatTy::F32 => check_known_consts(cx, e, s, "f32"), + FloatTy::F64 => check_known_consts(cx, e, s, "f64"), + }, + LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"), + _ => (), + } +} + +fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { + let s = s.as_str(); + if s.parse::().is_ok() { + for &(constant, name, min_digits) in &KNOWN_CONSTS { + if is_approx_const(constant, &s, min_digits) { + span_lint( + cx, + APPROX_CONSTANT, + e.span, + &format!( + "approximate value of `{}::consts::{}` found. \ + Consider using it directly", + module, &name + ), + ); + return; + } + } + } +} + +/// Returns `false` if the number of significant figures in `value` are +/// less than `min_digits`; otherwise, returns true if `value` is equal +/// to `constant`, rounded to the number of digits present in `value`. +#[must_use] +fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool { + if value.len() <= min_digits { + false + } else if constant.to_string().starts_with(value) { + // The value is a truncated constant + true + } else { + let round_const = format!("{:.*}", value.len() - 2, constant); + value == round_const + } +} diff --git a/src/tools/clippy/clippy_lints/src/arithmetic.rs b/src/tools/clippy/clippy_lints/src/arithmetic.rs new file mode 100644 index 0000000000000..6cbe10a5352d1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/arithmetic.rs @@ -0,0 +1,149 @@ +use crate::consts::constant_simple; +use crate::utils::span_lint; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for integer arithmetic operations which could overflow or panic. + /// + /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable + /// of overflowing according to the [Rust + /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), + /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is + /// attempted. + /// + /// **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in + /// release mode. Division by zero will cause a panic in either mode. In some applications one + /// wants explicitly checked, wrapping or saturating arithmetic. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let a = 0; + /// a + 1; + /// ``` + pub INTEGER_ARITHMETIC, + restriction, + "any integer arithmetic expression which could overflow or panic" +} + +declare_clippy_lint! { + /// **What it does:** Checks for float arithmetic. + /// + /// **Why is this bad?** For some embedded systems or kernel development, it + /// can be useful to rule out floating-point numbers. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let a = 0.0; + /// a + 1.0; + /// ``` + pub FLOAT_ARITHMETIC, + restriction, + "any floating-point arithmetic statement" +} + +#[derive(Copy, Clone, Default)] +pub struct Arithmetic { + expr_span: Option, + /// This field is used to check whether expressions are constants, such as in enum discriminants + /// and consts + const_span: Option, +} + +impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if self.expr_span.is_some() { + return; + } + + if let Some(span) = self.const_span { + if span.contains(expr.span) { + return; + } + } + match &expr.kind { + hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => { + match op.node { + hir::BinOpKind::And + | hir::BinOpKind::Or + | hir::BinOpKind::BitAnd + | hir::BinOpKind::BitOr + | hir::BinOpKind::BitXor + | hir::BinOpKind::Eq + | hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ne + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt => return, + _ => (), + } + + let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r)); + if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { + span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); + self.expr_span = Some(expr.span); + } + }, + hir::ExprKind::Unary(hir::UnOp::UnNeg, arg) => { + let ty = cx.tables.expr_ty(arg); + if constant_simple(cx, cx.tables, expr).is_none() { + if ty.is_integral() { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } else if ty.is_floating_point() { + span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); + self.expr_span = Some(expr.span); + } + } + }, + _ => (), + } + } + + fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if Some(expr.span) == self.expr_span { + self.expr_span = None; + } + } + + fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) { + let body_owner = cx.tcx.hir().body_owner(body.id()); + + match cx.tcx.hir().body_owner_kind(body_owner) { + hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => { + let body_span = cx.tcx.hir().span(body_owner); + + if let Some(span) = self.const_span { + if span.contains(body_span) { + return; + } + } + self.const_span = Some(body_span); + }, + hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (), + } + } + + 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); + + if let Some(span) = self.const_span { + if span.contains(body_span) { + return; + } + } + self.const_span = None; + } +} diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs new file mode 100644 index 0000000000000..0c8efd755146e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -0,0 +1,58 @@ +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::span_lint_and_help; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `as` conversions. + /// + /// **Why is this bad?** `as` conversions will perform many kinds of + /// conversions, including silently lossy conversions and dangerous coercions. + /// There are cases when it makes sense to use `as`, so the lint is + /// Allow by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// let a: u32; + /// ... + /// f(a as u16); + /// ``` + /// + /// Usually better represents the semantics you expect: + /// ```rust,ignore + /// f(a.try_into()?); + /// ``` + /// or + /// ```rust,ignore + /// f(a.try_into().expect("Unexpected u16 overflow in f")); + /// ``` + /// + pub AS_CONVERSIONS, + restriction, + "using a potentially dangerous silent `as` conversion" +} + +declare_lint_pass!(AsConversions => [AS_CONVERSIONS]); + +impl EarlyLintPass for AsConversions { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Cast(_, _) = expr.kind { + span_lint_and_help( + cx, + AS_CONVERSIONS, + expr.span, + "using a potentially dangerous silent `as` conversion", + None, + "consider using a safe wrapper for this conversion", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs new file mode 100644 index 0000000000000..f8a8fdcd3aa35 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -0,0 +1,152 @@ +use crate::consts::{constant, Constant}; +use crate::utils::paths; +use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, snippet_opt, span_lint_and_help}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind, PatKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls. + /// + /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a + /// `panic!()` or `unreachable!()` + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust,ignore + /// assert!(false) + /// assert!(true) + /// const B: bool = false; + /// assert!(B) + /// ``` + pub ASSERTIONS_ON_CONSTANTS, + style, + "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`" +} + +declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + let lint_true = |is_debug: bool| { + span_lint_and_help( + cx, + ASSERTIONS_ON_CONSTANTS, + e.span, + if is_debug { + "`debug_assert!(true)` will be optimized out by the compiler" + } else { + "`assert!(true)` will be optimized out by the compiler" + }, + None, + "remove it", + ); + }; + let lint_false_without_message = || { + span_lint_and_help( + cx, + ASSERTIONS_ON_CONSTANTS, + e.span, + "`assert!(false)` should probably be replaced", + None, + "use `panic!()` or `unreachable!()`", + ); + }; + let lint_false_with_message = |panic_message: String| { + span_lint_and_help( + cx, + ASSERTIONS_ON_CONSTANTS, + e.span, + &format!("`assert!(false, {})` should probably be replaced", panic_message), + None, + &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message), + ) + }; + + if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") { + if debug_assert_span.from_expansion() { + return; + } + if_chain! { + if let ExprKind::Unary(_, ref lit) = e.kind; + if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, lit); + if is_true; + then { + lint_true(true); + } + }; + } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") { + if assert_span.from_expansion() { + return; + } + if let Some(assert_match) = match_assert_with_message(&cx, e) { + match assert_match { + // matched assert but not message + AssertKind::WithoutMessage(false) => lint_false_without_message(), + AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false), + AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message), + }; + } + } + } +} + +/// Result of calling `match_assert_with_message`. +enum AssertKind { + WithMessage(String, bool), + WithoutMessage(bool), +} + +/// Check if the expression matches +/// +/// ```rust,ignore +/// match { let _t = !c; _t } { +/// true => { +/// { +/// ::std::rt::begin_panic(message, _) +/// } +/// } +/// _ => { } +/// }; +/// ``` +/// +/// where `message` is any expression and `c` is a constant bool. +fn match_assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option { + if_chain! { + if let ExprKind::Match(ref expr, ref arms, _) = expr.kind; + // matches { let _t = expr; _t } + if let ExprKind::DropTemps(ref expr) = expr.kind; + if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind; + // bind the first argument of the `assert!` macro + if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, expr); + // arm 1 pattern + if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind; + if let ExprKind::Lit(ref lit) = lit_expr.kind; + if let LitKind::Bool(true) = lit.node; + // arm 1 block + if let ExprKind::Block(ref block, _) = arms[0].body.kind; + if block.stmts.is_empty(); + if let Some(block_expr) = &block.expr; + if let ExprKind::Block(ref inner_block, _) = block_expr.kind; + if let Some(begin_panic_call) = &inner_block.expr; + // function call + if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC); + if args.len() == 1; + // bind the second argument of the `assert!` macro if it exists + if let panic_message = snippet_opt(cx, args[0].span); + // second argument of begin_panic is irrelevant + // as is the second match arm + then { + // an empty message occurs when it was generated by the macro + // (and not passed by the user) + return panic_message + .filter(|msg| !msg.is_empty()) + .map(|msg| AssertKind::WithMessage(msg, is_true)) + .or(Some(AssertKind::WithoutMessage(is_true))); + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/assign_ops.rs b/src/tools/clippy/clippy_lints/src/assign_ops.rs new file mode 100644 index 0000000000000..05e2650d0b715 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/assign_ops.rs @@ -0,0 +1,261 @@ +use crate::utils::{ + get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq, +}; +use crate::utils::{higher, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `a = a op b` or `a = b commutative_op a` + /// patterns. + /// + /// **Why is this bad?** These can be written as the shorter `a op= b`. + /// + /// **Known problems:** While forbidden by the spec, `OpAssign` traits may have + /// implementations that differ from the regular `Op` impl. + /// + /// **Example:** + /// ```rust + /// let mut a = 5; + /// let b = 0; + /// // ... + /// a = a + b; + /// ``` + pub ASSIGN_OP_PATTERN, + style, + "assigning the result of an operation on a variable to that same variable" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns. + /// + /// **Why is this bad?** Most likely these are bugs where one meant to write `a + /// op= b`. + /// + /// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have + /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both. + /// If `a op= a op b` is really the correct behaviour it should be + /// written as `a = a op a op b` as it's less confusing. + /// + /// **Example:** + /// ```rust + /// let mut a = 5; + /// let b = 2; + /// // ... + /// a += a + b; + /// ``` + pub MISREFACTORED_ASSIGN_OP, + complexity, + "having a variable on both sides of an assign op" +} + +declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps { + #[allow(clippy::too_many_lines)] + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + match &expr.kind { + hir::ExprKind::AssignOp(op, lhs, rhs) => { + if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind { + if op.node != binop.node { + return; + } + // lhs op= l op r + if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) { + lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r); + } + // lhs op= l commutative_op r + if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) { + lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l); + } + } + }, + hir::ExprKind::Assign(assignee, e, _) => { + if let hir::ExprKind::Binary(op, l, r) = &e.kind { + let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| { + let ty = cx.tables.expr_ty(assignee); + let rty = cx.tables.expr_ty(rhs); + macro_rules! ops { + ($op:expr, + $cx:expr, + $ty:expr, + $rty:expr, + $($trait_name:ident),+) => { + match $op { + $(hir::BinOpKind::$trait_name => { + let [krate, module] = crate::utils::paths::OPS_MODULE; + let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")]; + let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) { + trait_id + } else { + return; // useless if the trait doesn't exist + }; + // check that we are not inside an `impl AssignOp` of this exact operation + let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id); + if_chain! { + if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn); + if trait_ref.path.res.def_id() == trait_id; + then { return; } + } + implements_trait($cx, $ty, trait_id, &[$rty]) + },)* + _ => false, + } + } + } + if ops!( + op.node, + cx, + ty, + rty.into(), + Add, + Sub, + Mul, + Div, + Rem, + And, + Or, + BitAnd, + BitOr, + BitXor, + Shr, + Shl + ) { + span_lint_and_then( + cx, + ASSIGN_OP_PATTERN, + expr.span, + "manual implementation of an assign operation", + |diag| { + if let (Some(snip_a), Some(snip_r)) = + (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) + { + diag.span_suggestion( + expr.span, + "replace it with", + format!("{} {}= {}", snip_a, op.node.as_str(), snip_r), + Applicability::MachineApplicable, + ); + } + }, + ); + } + }; + + let mut visitor = ExprVisitor { + assignee, + counter: 0, + cx, + }; + + walk_expr(&mut visitor, e); + + if visitor.counter == 1 { + // a = a op b + if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) { + lint(assignee, r); + } + // a = b commutative_op a + // Limited to primitive type as these ops are know to be commutative + if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) + && cx.tables.expr_ty(assignee).is_primitive_ty() + { + match op.node { + hir::BinOpKind::Add + | hir::BinOpKind::Mul + | hir::BinOpKind::And + | hir::BinOpKind::Or + | hir::BinOpKind::BitXor + | hir::BinOpKind::BitAnd + | hir::BinOpKind::BitOr => { + lint(assignee, l); + }, + _ => {}, + } + } + } + } + }, + _ => {}, + } + } +} + +fn lint_misrefactored_assign_op( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + op: hir::BinOp, + rhs: &hir::Expr<'_>, + assignee: &hir::Expr<'_>, + rhs_other: &hir::Expr<'_>, +) { + span_lint_and_then( + cx, + MISREFACTORED_ASSIGN_OP, + expr.span, + "variable appears on both sides of an assignment operation", + |diag| { + if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) { + let a = &sugg::Sugg::hir(cx, assignee, ".."); + let r = &sugg::Sugg::hir(cx, rhs, ".."); + let long = format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r)); + diag.span_suggestion( + expr.span, + &format!( + "Did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", + snip_a, + snip_a, + op.node.as_str(), + snip_r, + long + ), + format!("{} {}= {}", snip_a, op.node.as_str(), snip_r), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + expr.span, + "or", + long, + Applicability::MaybeIncorrect, // snippet + ); + } + }, + ); +} + +#[must_use] +fn is_commutative(op: hir::BinOpKind) -> bool { + use rustc_hir::BinOpKind::{ + Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, + }; + match op { + Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true, + Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false, + } +} + +struct ExprVisitor<'a, 'tcx> { + assignee: &'a hir::Expr<'a>, + counter: u8, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) { + self.counter += 1; + } + + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs new file mode 100644 index 0000000000000..73b4cef472505 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs @@ -0,0 +1,135 @@ +use crate::utils::{match_def_path, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of invalid atomic + /// ordering in atomic loads/stores and memory fences. + /// + /// **Why is this bad?** Using an invalid atomic ordering + /// will cause a panic at run-time. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,no_run + /// # use std::sync::atomic::{self, AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(true); + /// + /// let _ = x.load(Ordering::Release); + /// let _ = x.load(Ordering::AcqRel); + /// + /// x.store(false, Ordering::Acquire); + /// x.store(false, Ordering::AcqRel); + /// + /// atomic::fence(Ordering::Relaxed); + /// atomic::compiler_fence(Ordering::Relaxed); + /// ``` + pub INVALID_ATOMIC_ORDERING, + correctness, + "usage of invalid atomic ordering in atomic loads/stores and memory fences" +} + +declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); + +const ATOMIC_TYPES: [&str; 12] = [ + "AtomicBool", + "AtomicI8", + "AtomicI16", + "AtomicI32", + "AtomicI64", + "AtomicIsize", + "AtomicPtr", + "AtomicU8", + "AtomicU16", + "AtomicU32", + "AtomicU64", + "AtomicUsize", +]; + +fn type_is_atomic(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.tables.expr_ty(expr).kind { + ATOMIC_TYPES + .iter() + .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty])) + } else { + false + } +} + +fn match_ordering_def_path(cx: &LateContext<'_, '_>, did: DefId, orderings: &[&str]) -> bool { + orderings + .iter() + .any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering])) +} + +fn check_atomic_load_store(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref method_path, _, args) = &expr.kind; + let method = method_path.ident.name.as_str(); + if type_is_atomic(cx, &args[0]); + if method == "load" || method == "store"; + let ordering_arg = if method == "load" { &args[1] } else { &args[2] }; + if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind; + if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id(); + then { + if method == "load" && + match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) { + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + ordering_arg.span, + "atomic loads cannot have `Release` and `AcqRel` ordering", + None, + "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`" + ); + } else if method == "store" && + match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) { + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + ordering_arg.span, + "atomic stores cannot have `Acquire` and `AcqRel` ordering", + None, + "consider using ordering modes `Release`, `SeqCst` or `Relaxed`" + ); + } + } + } +} + +fn check_memory_fence(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if ["fence", "compiler_fence"] + .iter() + .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func])); + if let ExprKind::Path(ref ordering_qpath) = &args[0].kind; + if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id(); + if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]); + then { + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + args[0].span, + "memory fences cannot have `Relaxed` ordering", + None, + "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`" + ); + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AtomicOrdering { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + check_atomic_load_store(cx, expr); + check_memory_fence(cx, expr); + } +} diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs new file mode 100644 index 0000000000000..64abc9fdc7174 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -0,0 +1,657 @@ +//! checks for attributes + +use crate::reexport::Name; +use crate::utils::{ + first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg, + span_lint_and_then, without_block_comments, +}; +use if_chain::if_chain; +use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_errors::Applicability; +use rustc_hir::{ + Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, +}; +use rustc_lint::{CheckLintNameResult, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; +use semver::Version; + +static UNIX_SYSTEMS: &[&str] = &[ + "android", + "dragonfly", + "emscripten", + "freebsd", + "fuchsia", + "haiku", + "illumos", + "ios", + "l4re", + "linux", + "macos", + "netbsd", + "openbsd", + "redox", + "solaris", + "vxworks", +]; + +// NOTE: windows is excluded from the list because it's also a valid target family. +static NON_UNIX_SYSTEMS: &[&str] = &["cloudabi", "hermit", "none", "wasi"]; + +declare_clippy_lint! { + /// **What it does:** Checks for items annotated with `#[inline(always)]`, + /// unless the annotated function is empty or simply panics. + /// + /// **Why is this bad?** While there are valid uses of this annotation (and once + /// you know when to use it, by all means `allow` this lint), it's a common + /// newbie-mistake to pepper one's code with it. + /// + /// As a rule of thumb, before slapping `#[inline(always)]` on a function, + /// measure if that additional function call really affects your runtime profile + /// sufficiently to make up for the increase in compile time. + /// + /// **Known problems:** False positives, big time. This lint is meant to be + /// deactivated by everyone doing serious performance work. This means having + /// done the measurement. + /// + /// **Example:** + /// ```ignore + /// #[inline(always)] + /// fn not_quite_hot_code(..) { ... } + /// ``` + pub INLINE_ALWAYS, + pedantic, + "use of `#[inline(always)]`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `extern crate` and `use` items annotated with + /// lint attributes. + /// + /// This lint whitelists `#[allow(unused_imports)]`, `#[allow(deprecated)]` and + /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on + /// `extern crate` items with a `#[macro_use]` attribute. + /// + /// **Why is this bad?** Lint attributes have no effect on crate imports. Most + /// likely a `!` was forgotten. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// // Bad + /// #[deny(dead_code)] + /// extern crate foo; + /// #[forbid(dead_code)] + /// use foo::bar; + /// + /// // Ok + /// #[allow(unused_imports)] + /// use foo::baz; + /// #[allow(unused_imports)] + /// #[macro_use] + /// extern crate baz; + /// ``` + pub USELESS_ATTRIBUTE, + correctness, + "use of lint attributes on `extern crate` items" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `#[deprecated]` annotations with a `since` + /// field that is not a valid semantic version. + /// + /// **Why is this bad?** For checking the version of the deprecation, it must be + /// a valid semver. Failing that, the contained information is useless. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// #[deprecated(since = "forever")] + /// fn something_else() { /* ... */ } + /// ``` + pub DEPRECATED_SEMVER, + correctness, + "use of `#[deprecated(since = \"x\")]` where x is not semver" +} + +declare_clippy_lint! { + /// **What it does:** Checks for empty lines after outer attributes + /// + /// **Why is this bad?** + /// Most likely the attribute was meant to be an inner attribute using a '!'. + /// If it was meant to be an outer attribute, then the following item + /// should not be separated by empty lines. + /// + /// **Known problems:** Can cause false positives. + /// + /// From the clippy side it's difficult to detect empty lines between an attributes and the + /// following item because empty lines and comments are not part of the AST. The parsing + /// currently works for basic cases but is not perfect. + /// + /// **Example:** + /// ```rust + /// // Good (as inner attribute) + /// #![inline(always)] + /// + /// fn this_is_fine() { } + /// + /// // Bad + /// #[inline(always)] + /// + /// fn not_quite_good_code() { } + /// + /// // Good (as outer attribute) + /// #[inline(always)] + /// fn this_is_fine_too() { } + /// ``` + pub EMPTY_LINE_AFTER_OUTER_ATTR, + nursery, + "empty line after outer attribute" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy + /// lints and if those lints exist in clippy. If there is an uppercase letter in the lint name + /// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase + /// the lint name. + /// + /// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// #![warn(if_not_els)] + /// #![deny(clippy::All)] + /// ``` + /// + /// Good: + /// ```rust + /// #![warn(if_not_else)] + /// #![deny(clippy::all)] + /// ``` + pub UNKNOWN_CLIPPY_LINTS, + style, + "unknown_lints for scoped Clippy lints" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it + /// with `#[rustfmt::skip]`. + /// + /// **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) + /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes. + /// + /// **Known problems:** This lint doesn't detect crate level inner attributes, because they get + /// processed before the PreExpansionPass lints get executed. See + /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) + /// + /// **Example:** + /// + /// Bad: + /// ```rust + /// #[cfg_attr(rustfmt, rustfmt_skip)] + /// fn main() { } + /// ``` + /// + /// Good: + /// ```rust + /// #[rustfmt::skip] + /// fn main() { } + /// ``` + pub DEPRECATED_CFG_ATTR, + complexity, + "usage of `cfg_attr(rustfmt)` instead of tool attributes" +} + +declare_clippy_lint! { + /// **What it does:** Checks for cfg attributes having operating systems used in target family position. + /// + /// **Why is this bad?** The configuration option will not be recognised and the related item will not be included + /// by the conditional compilation engine. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// Bad: + /// ```rust + /// #[cfg(linux)] + /// fn conditional() { } + /// ``` + /// + /// Good: + /// ```rust + /// #[cfg(target_os = "linux")] + /// fn conditional() { } + /// ``` + /// + /// Or: + /// ```rust + /// #[cfg(unix)] + /// fn conditional() { } + /// ``` + /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. + pub MISMATCHED_TARGET_OS, + correctness, + "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" +} + +declare_lint_pass!(Attributes => [ + INLINE_ALWAYS, + DEPRECATED_SEMVER, + USELESS_ATTRIBUTE, + EMPTY_LINE_AFTER_OUTER_ATTR, + UNKNOWN_CLIPPY_LINTS, +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes { + fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) { + if let Some(items) = &attr.meta_item_list() { + if let Some(ident) = attr.ident() { + match &*ident.as_str() { + "allow" | "warn" | "deny" | "forbid" => { + check_clippy_lint_names(cx, items); + }, + _ => {}, + } + if items.is_empty() || !attr.check_name(sym!(deprecated)) { + return; + } + for item in items { + if_chain! { + if let NestedMetaItem::MetaItem(mi) = &item; + if let MetaItemKind::NameValue(lit) = &mi.kind; + if mi.check_name(sym!(since)); + then { + check_semver(cx, item.span(), lit); + } + } + } + } + } + } + + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if is_relevant_item(cx, item) { + check_attrs(cx, item.span, item.ident.name, &item.attrs) + } + match item.kind { + ItemKind::ExternCrate(..) | ItemKind::Use(..) => { + let skip_unused_imports = item.attrs.iter().any(|attr| attr.check_name(sym!(macro_use))); + + for attr in item.attrs { + if in_external_macro(cx.sess(), attr.span) { + return; + } + if let Some(lint_list) = &attr.meta_item_list() { + if let Some(ident) = attr.ident() { + match &*ident.as_str() { + "allow" | "warn" | "deny" | "forbid" => { + // whitelist `unused_imports`, `deprecated` and `unreachable_pub` for `use` items + // and `unused_imports` for `extern crate` items with `macro_use` + for lint in lint_list { + match item.kind { + ItemKind::Use(..) => { + if is_word(lint, sym!(unused_imports)) + || is_word(lint, sym!(deprecated)) + || is_word(lint, sym!(unreachable_pub)) + || is_word(lint, sym!(unused)) + { + 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); + + if let Some(mut sugg) = snippet_opt(cx, line_span) { + if sugg.contains("#[") { + span_lint_and_then( + cx, + USELESS_ATTRIBUTE, + line_span, + "useless lint attribute", + |diag| { + sugg = sugg.replacen("#[", "#![", 1); + diag.span_suggestion( + line_span, + "if you just forgot a `!`, use", + sugg, + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + }, + _ => {}, + } + } + } + } + }, + _ => {}, + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) { + if is_relevant_impl(cx, item) { + check_attrs(cx, item.span, item.ident.name, &item.attrs) + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) { + if is_relevant_trait(cx, item) { + check_attrs(cx, item.span, item.ident.name, &item.attrs) + } + } +} + +#[allow(clippy::single_match_else)] +fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) { + let lint_store = cx.lints(); + for lint in items { + if_chain! { + if let Some(meta_item) = lint.meta_item(); + if meta_item.path.segments.len() > 1; + if let tool_name = meta_item.path.segments[0].ident; + if tool_name.as_str() == "clippy"; + let name = meta_item.path.segments.last().unwrap().ident.name; + if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name( + &name.as_str(), + Some(tool_name.name), + ); + then { + span_lint_and_then( + cx, + UNKNOWN_CLIPPY_LINTS, + lint.span(), + &format!("unknown clippy lint: clippy::{}", name), + |diag| { + let name_lower = name.as_str().to_lowercase(); + let symbols = lint_store.get_lints().iter().map( + |l| Symbol::intern(&l.name_lower()) + ).collect::>(); + let sugg = find_best_match_for_name( + symbols.iter(), + &format!("clippy::{}", name_lower), + None, + ); + if name.as_str().chars().any(char::is_uppercase) + && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() { + diag.span_suggestion( + lint.span(), + "lowercase the lint name", + format!("clippy::{}", name_lower), + Applicability::MachineApplicable, + ); + } else if let Some(sugg) = sugg { + diag.span_suggestion( + lint.span(), + "did you mean", + sugg.to_string(), + Applicability::MachineApplicable, + ); + } + } + ); + } + }; + } +} + +fn is_relevant_item(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { + if let ItemKind::Fn(_, _, eid) = item.kind { + is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value) + } else { + true + } +} + +fn is_relevant_impl(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) -> bool { + match item.kind { + ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value), + _ => false, + } +} + +fn is_relevant_trait(cx: &LateContext<'_, '_>, item: &TraitItem<'_>) -> bool { + match item.kind { + TraitItemKind::Fn(_, TraitFn::Required(_)) => true, + TraitItemKind::Fn(_, TraitFn::Provided(eid)) => { + is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value) + }, + _ => false, + } +} + +fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { + if let Some(stmt) = block.stmts.first() { + match &stmt.kind { + StmtKind::Local(_) => true, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr), + _ => false, + } + } else { + block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)) + } +} + +fn is_relevant_expr(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool { + match &expr.kind { + ExprKind::Block(block, _) => is_relevant_block(cx, tables, block), + ExprKind::Ret(Some(e)) => is_relevant_expr(cx, tables, e), + ExprKind::Ret(None) | ExprKind::Break(_, None) => false, + ExprKind::Call(path_expr, _) => { + if let ExprKind::Path(qpath) = &path_expr.kind { + if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { + !match_def_path(cx, fun_id, &paths::BEGIN_PANIC) + } else { + true + } + } else { + true + } + }, + _ => true, + } +} + +fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attribute]) { + if span.from_expansion() { + return; + } + + for attr in attrs { + let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { + attr + } else { + continue; + }; + + if attr.style == AttrStyle::Outer { + if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { + return; + } + + let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt()); + let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt()); + + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { + let lines = snippet.split('\n').collect::>(); + let lines = without_block_comments(lines); + + if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { + span_lint( + cx, + EMPTY_LINE_AFTER_OUTER_ATTR, + begin_of_attr_to_item, + "Found an empty line after an outer attribute. \ + Perhaps you forgot to add a `!` to make it an inner attribute?", + ); + } + } + } + + if let Some(values) = attr.meta_item_list() { + if values.len() != 1 || !attr.check_name(sym!(inline)) { + continue; + } + if is_word(&values[0], sym!(always)) { + span_lint( + cx, + INLINE_ALWAYS, + attr.span, + &format!( + "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea", + name + ), + ); + } + } + } +} + +fn check_semver(cx: &LateContext<'_, '_>, span: Span, lit: &Lit) { + if let LitKind::Str(is, _) = lit.kind { + if Version::parse(&is.as_str()).is_ok() { + return; + } + } + span_lint( + cx, + DEPRECATED_SEMVER, + span, + "the since field must contain a semver-compliant version", + ); +} + +fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { + if let NestedMetaItem::MetaItem(mi) = &nmi { + mi.is_word() && mi.check_name(expected) + } else { + false + } +} + +declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]); + +impl EarlyLintPass for EarlyAttributes { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + check_deprecated_cfg_attr(cx, attr); + check_mismatched_target_os(cx, attr); + } +} + +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { + if_chain! { + // check cfg_attr + if attr.check_name(sym!(cfg_attr)); + if let Some(items) = attr.meta_item_list(); + if items.len() == 2; + // check for `rustfmt` + if let Some(feature_item) = items[0].meta_item(); + if feature_item.check_name(sym!(rustfmt)); + // check for `rustfmt_skip` and `rustfmt::skip` + if let Some(skip_item) = &items[1].meta_item(); + if skip_item.check_name(sym!(rustfmt_skip)) || + skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip); + // Only lint outer attributes, because custom inner attributes are unstable + // Tracking issue: https://github.com/rust-lang/rust/issues/54726 + if let AttrStyle::Outer = attr.style; + then { + span_lint_and_sugg( + cx, + DEPRECATED_CFG_ATTR, + attr.span, + "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", + "use", + "#[rustfmt::skip]".to_string(), + Applicability::MachineApplicable, + ); + } + } +} + +fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { + fn find_os(name: &str) -> Option<&'static str> { + UNIX_SYSTEMS + .iter() + .chain(NON_UNIX_SYSTEMS.iter()) + .find(|&&os| os == name) + .copied() + } + + fn is_unix(name: &str) -> bool { + UNIX_SYSTEMS.iter().any(|&os| os == name) + } + + fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { + let mut mismatched = Vec::new(); + + for item in items { + if let NestedMetaItem::MetaItem(meta) = item { + match &meta.kind { + MetaItemKind::List(list) => { + mismatched.extend(find_mismatched_target_os(&list)); + }, + MetaItemKind::Word => { + if_chain! { + if let Some(ident) = meta.ident(); + if let Some(os) = find_os(&*ident.name.as_str()); + then { + mismatched.push((os, ident.span)); + } + } + }, + _ => {}, + } + } + } + + mismatched + } + + if_chain! { + if attr.check_name(sym!(cfg)); + if let Some(list) = attr.meta_item_list(); + let mismatched = find_mismatched_target_os(&list); + if !mismatched.is_empty(); + then { + let mess = "operating system used in target family position"; + + span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, &mess, |diag| { + // Avoid showing the unix suggestion multiple times in case + // we have more than one mismatch for unix-like systems + let mut unix_suggested = false; + + for (os, span) in mismatched { + let sugg = format!("target_os = \"{}\"", os); + diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); + + if !unix_suggested && is_unix(os) { + diag.help("Did you mean `unix`?"); + unix_suggested = true; + } + } + }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs new file mode 100644 index 0000000000000..832910763e60c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs @@ -0,0 +1,97 @@ +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use rustc_hir::def_id::DefId; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// non-async-aware MutexGuard. + /// + /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot + /// are not designed to operator in an async context across await points. + /// + /// There are two potential solutions. One is to use an asynx-aware Mutex + /// type. Many asynchronous foundation crates provide such a Mutex type. The + /// other solution is to ensure the mutex is unlocked before calling await, + /// either by introducing a scope or an explicit call to Drop::drop. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_LOCK, + pedantic, + "Inside an async function, holding a MutexGuard while calling await" +} + +declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); + +impl LateLintPass<'_, '_> for AwaitHoldingLock { + fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + match body.generator_kind { + Some(GeneratorKind::Async(Block)) + | Some(GeneratorKind::Async(Closure)) + | Some(GeneratorKind::Async(Fn)) => { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let tables = cx.tcx.typeck_tables_of(def_id); + check_interior_types(cx, &tables.generator_interior_types, body.value.span); + }, + _ => {}, + } + } +} + +fn check_interior_types(cx: &LateContext<'_, '_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind { + if is_mutex_guard(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this lock is held through", + ); + } + } + } +} + +fn is_mutex_guard(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + || 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) +} diff --git a/src/tools/clippy/clippy_lints/src/bit_mask.rs b/src/tools/clippy/clippy_lints/src/bit_mask.rs new file mode 100644 index 0000000000000..ccb62cb038fd0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/bit_mask.rs @@ -0,0 +1,326 @@ +use crate::consts::{constant, Constant}; +use crate::utils::sugg::Sugg; +use crate::utils::{span_lint, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for incompatible bit masks in comparisons. + /// + /// The formula for detecting if an expression of the type `_ m + /// c` (where `` is one of {`&`, `|`} and `` is one of + /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following + /// table: + /// + /// |Comparison |Bit Op|Example |is always|Formula | + /// |------------|------|------------|---------|----------------------| + /// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` | + /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | + /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | + /// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` | + /// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` | + /// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` | + /// + /// **Why is this bad?** If the bits that the comparison cares about are always + /// set to zero or one by the bit mask, the comparison is constant `true` or + /// `false` (depending on mask, compared value, and operators). + /// + /// So the code is actively misleading, and the only reason someone would write + /// this intentionally is to win an underhanded Rust contest or create a + /// test-case for this lint. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// if (x & 1 == 2) { } + /// ``` + pub BAD_BIT_MASK, + correctness, + "expressions of the form `_ & mask == select` that will only ever return `true` or `false`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for bit masks in comparisons which can be removed + /// without changing the outcome. The basic structure can be seen in the + /// following table: + /// + /// |Comparison| Bit Op |Example |equals | + /// |----------|---------|-----------|-------| + /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`| + /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`| + /// + /// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask), + /// but still a bit misleading, because the bit mask is ineffective. + /// + /// **Known problems:** False negatives: This lint will only match instances + /// where we have figured out the math (which is for a power-of-two compared + /// value). This means things like `x | 1 >= 7` (which would be better written + /// as `x >= 6`) will not be reported (but bit masks like this are fairly + /// uncommon). + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// if (x | 1 > 3) { } + /// ``` + pub INEFFECTIVE_BIT_MASK, + correctness, + "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for bit masks that can be replaced by a call + /// to `trailing_zeros` + /// + /// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15 + /// == 0` + /// + /// **Known problems:** llvm generates better code for `x & 15 == 0` on x86 + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// if x & 0b1111 == 0 { } + /// ``` + pub VERBOSE_BIT_MASK, + style, + "expressions where a bit mask is less readable than the corresponding method call" +} + +#[derive(Copy, Clone)] +pub struct BitMask { + verbose_bit_mask_threshold: u64, +} + +impl BitMask { + #[must_use] + pub fn new(verbose_bit_mask_threshold: u64) -> Self { + Self { + verbose_bit_mask_threshold, + } + } +} + +impl_lint_pass!(BitMask => [BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Binary(cmp, left, right) = &e.kind { + if cmp.node.is_comparison() { + if let Some(cmp_opt) = fetch_int_literal(cx, right) { + check_compare(cx, left, cmp.node, cmp_opt, e.span) + } else if let Some(cmp_val) = fetch_int_literal(cx, left) { + check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span) + } + } + } + if_chain! { + if let ExprKind::Binary(op, left, right) = &e.kind; + if BinOpKind::Eq == op.node; + if let ExprKind::Binary(op1, left1, right1) = &left.kind; + if BinOpKind::BitAnd == op1.node; + if let ExprKind::Lit(lit) = &right1.kind; + if let LitKind::Int(n, _) = lit.node; + if let ExprKind::Lit(lit1) = &right.kind; + if let LitKind::Int(0, _) = lit1.node; + if n.leading_zeros() == n.count_zeros(); + if n > u128::from(self.verbose_bit_mask_threshold); + then { + span_lint_and_then(cx, + VERBOSE_BIT_MASK, + e.span, + "bit mask could be simplified with a call to `trailing_zeros`", + |diag| { + let sugg = Sugg::hir(cx, left1, "...").maybe_par(); + diag.span_suggestion( + e.span, + "try", + format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), + Applicability::MaybeIncorrect, + ); + }); + } + } + } +} + +#[must_use] +fn invert_cmp(cmp: BinOpKind) -> BinOpKind { + match cmp { + BinOpKind::Eq => BinOpKind::Eq, + BinOpKind::Ne => BinOpKind::Ne, + BinOpKind::Lt => BinOpKind::Gt, + BinOpKind::Gt => BinOpKind::Lt, + BinOpKind::Le => BinOpKind::Ge, + BinOpKind::Ge => BinOpKind::Le, + _ => BinOpKind::Or, // Dummy + } +} + +fn check_compare(cx: &LateContext<'_, '_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp_value: u128, span: Span) { + if let ExprKind::Binary(op, left, right) = &bit_op.kind { + if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr { + return; + } + fetch_int_literal(cx, right) + .or_else(|| fetch_int_literal(cx, left)) + .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span)) + } +} + +#[allow(clippy::too_many_lines)] +fn check_bit_mask( + cx: &LateContext<'_, '_>, + bit_op: BinOpKind, + cmp_op: BinOpKind, + mask_value: u128, + cmp_value: u128, + span: Span, +) { + match cmp_op { + BinOpKind::Eq | BinOpKind::Ne => match bit_op { + BinOpKind::BitAnd => { + if mask_value & cmp_value != cmp_value { + if cmp_value != 0 { + span_lint( + cx, + BAD_BIT_MASK, + span, + &format!( + "incompatible bit mask: `_ & {}` can never be equal to `{}`", + mask_value, cmp_value + ), + ); + } + } else if mask_value == 0 { + span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); + } + }, + BinOpKind::BitOr => { + if mask_value | cmp_value != cmp_value { + span_lint( + cx, + BAD_BIT_MASK, + span, + &format!( + "incompatible bit mask: `_ | {}` can never be equal to `{}`", + mask_value, cmp_value + ), + ); + } + }, + _ => (), + }, + BinOpKind::Lt | BinOpKind::Ge => match bit_op { + BinOpKind::BitAnd => { + if mask_value < cmp_value { + span_lint( + cx, + BAD_BIT_MASK, + span, + &format!( + "incompatible bit mask: `_ & {}` will always be lower than `{}`", + mask_value, cmp_value + ), + ); + } else if mask_value == 0 { + span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); + } + }, + BinOpKind::BitOr => { + if mask_value >= cmp_value { + span_lint( + cx, + BAD_BIT_MASK, + span, + &format!( + "incompatible bit mask: `_ | {}` will never be lower than `{}`", + mask_value, cmp_value + ), + ); + } else { + check_ineffective_lt(cx, span, mask_value, cmp_value, "|"); + } + }, + BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"), + _ => (), + }, + BinOpKind::Le | BinOpKind::Gt => match bit_op { + BinOpKind::BitAnd => { + if mask_value <= cmp_value { + span_lint( + cx, + BAD_BIT_MASK, + span, + &format!( + "incompatible bit mask: `_ & {}` will never be higher than `{}`", + mask_value, cmp_value + ), + ); + } else if mask_value == 0 { + span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); + } + }, + BinOpKind::BitOr => { + if mask_value > cmp_value { + span_lint( + cx, + BAD_BIT_MASK, + span, + &format!( + "incompatible bit mask: `_ | {}` will always be higher than `{}`", + mask_value, cmp_value + ), + ); + } else { + check_ineffective_gt(cx, span, mask_value, cmp_value, "|"); + } + }, + BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"), + _ => (), + }, + _ => (), + } +} + +fn check_ineffective_lt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) { + if c.is_power_of_two() && m < c { + span_lint( + cx, + INEFFECTIVE_BIT_MASK, + span, + &format!( + "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", + op, m, c + ), + ); + } +} + +fn check_ineffective_gt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) { + if (c + 1).is_power_of_two() && m <= c { + span_lint( + cx, + INEFFECTIVE_BIT_MASK, + span, + &format!( + "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", + op, m, c + ), + ); + } +} + +fn fetch_int_literal(cx: &LateContext<'_, '_>, lit: &Expr<'_>) -> Option { + match constant(cx, cx.tables, lit)?.0 { + Constant::Int(n) => Some(n), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/blacklisted_name.rs b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs new file mode 100644 index 0000000000000..6f26f031431db --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs @@ -0,0 +1,51 @@ +use crate::utils::span_lint; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of blacklisted names for variables, such + /// as `foo`. + /// + /// **Why is this bad?** These names are usually placeholder names and should be + /// avoided. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let foo = 3.14; + /// ``` + pub BLACKLISTED_NAME, + style, + "usage of a blacklisted/placeholder name" +} + +#[derive(Clone, Debug)] +pub struct BlacklistedName { + blacklist: FxHashSet, +} + +impl BlacklistedName { + pub fn new(blacklist: FxHashSet) -> Self { + Self { blacklist } + } +} + +impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlacklistedName { + fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) { + if let PatKind::Binding(.., ident, _) = pat.kind { + if self.blacklist.contains(&ident.name.to_string()) { + span_lint( + cx, + BLACKLISTED_NAME, + ident.span, + &format!("use of a blacklisted/placeholder name `{}`", ident.name), + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs new file mode 100644 index 0000000000000..8fa9b05ca3297 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs @@ -0,0 +1,145 @@ +use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `if` conditions that use blocks containing an + /// expression, statements or conditions that use closures with blocks. + /// + /// **Why is this bad?** Style, using blocks in the condition makes it hard to read. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// // Bad + /// if { true } { /* ... */ } + /// + /// // Good + /// if true { /* ... */ } + /// ``` + /// + /// // or + /// + /// ```rust + /// # fn somefunc() -> bool { true }; + /// + /// // Bad + /// if { let x = somefunc(); x } { /* ... */ } + /// + /// // Good + /// let res = { let x = somefunc(); x }; + /// if res { /* ... */ } + /// ``` + pub BLOCKS_IN_IF_CONDITIONS, + style, + "useless or complex blocks that can be eliminated in conditions" +} + +declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); + +struct ExVisitor<'a, 'tcx> { + found_block: Option<&'tcx Expr<'tcx>>, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { + let body = self.cx.tcx.hir().body(eid); + let ex = &body.value; + if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { + self.found_block = Some(ex); + return; + } + } + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; +const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ + instead, move the block or closure higher and bind it with a `let`"; + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlocksInIfConditions { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + if let Some((cond, _, _)) = higher::if_block(&expr) { + if let ExprKind::Block(block, _) = &cond.kind { + if block.rules == BlockCheckMode::DefaultBlock { + if block.stmts.is_empty() { + if let Some(ex) = &block.expr { + // don't dig into the expression here, just suggest that they remove + // the block + if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) { + return; + } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_IF_CONDITIONS, + cond.span, + BRACED_EXPR_MESSAGE, + "try", + format!( + "{}", + snippet_block_with_applicability( + cx, + ex.span, + "..", + Some(expr.span), + &mut applicability + ) + ), + applicability, + ); + } + } else { + let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); + if span.from_expansion() || differing_macro_contexts(expr.span, span) { + return; + } + // move block higher + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_IF_CONDITIONS, + expr.span.with_hi(cond.span.hi()), + COMPLEX_BLOCK_MESSAGE, + "try", + format!( + "let res = {}; if res", + snippet_block_with_applicability( + cx, + block.span, + "..", + Some(expr.span), + &mut applicability + ), + ), + applicability, + ); + } + } + } else { + let mut visitor = ExVisitor { found_block: None, cx }; + walk_expr(&mut visitor, cond); + if let Some(block) = visitor.found_block { + span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs new file mode 100644 index 0000000000000..8031052e073d5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -0,0 +1,499 @@ +use crate::utils::{ + get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg, + span_lint_and_then, SpanlessEq, +}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for boolean expressions that can be written more + /// concisely. + /// + /// **Why is this bad?** Readability of boolean expressions suffers from + /// unnecessary duplication. + /// + /// **Known problems:** Ignores short circuiting behavior of `||` and + /// `&&`. Ignores `|`, `&` and `^`. + /// + /// **Example:** + /// ```ignore + /// if a && true // should be: if a + /// if !(a == b) // should be: if a != b + /// ``` + pub NONMINIMAL_BOOL, + complexity, + "boolean expressions that can be written more concisely" +} + +declare_clippy_lint! { + /// **What it does:** Checks for boolean expressions that contain terminals that + /// can be eliminated. + /// + /// **Why is this bad?** This is most likely a logic bug. + /// + /// **Known problems:** Ignores short circuiting behavior. + /// + /// **Example:** + /// ```ignore + /// if a && b || a { ... } + /// ``` + /// The `b` is unnecessary, the expression is equivalent to `if a`. + pub LOGIC_BUG, + correctness, + "boolean expressions that contain terminals which can be eliminated" +} + +// For each pairs, both orders are considered. +const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")]; + +declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, LOGIC_BUG]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: FnKind<'tcx>, + _: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: HirId, + ) { + NonminimalBoolVisitor { cx }.visit_body(body) + } +} + +struct NonminimalBoolVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, +} + +use quine_mc_cluskey::Bool; +struct Hir2Qmm<'a, 'tcx, 'v> { + terminals: Vec<&'v Expr<'v>>, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, '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; + } + } + v.push(self.run(a)?); + } + Ok(v) + } + + fn run(&mut self, e: &'v Expr<'_>) -> Result { + fn negate(bin_op_kind: BinOpKind) -> Option { + match bin_op_kind { + BinOpKind::Eq => Some(BinOpKind::Ne), + BinOpKind::Ne => Some(BinOpKind::Eq), + BinOpKind::Gt => Some(BinOpKind::Le), + BinOpKind::Ge => Some(BinOpKind::Lt), + BinOpKind::Lt => Some(BinOpKind::Ge), + BinOpKind::Le => Some(BinOpKind::Gt), + _ => None, + } + } + + // prevent folding of `cfg!` macros and the like + if !e.span.from_expansion() { + match &e.kind { + ExprKind::Unary(UnOp::UnNot, inner) => return Ok(Bool::Not(box self.run(inner)?)), + ExprKind::Binary(binop, lhs, rhs) => match &binop.node { + BinOpKind::Or => return Ok(Bool::Or(self.extract(BinOpKind::Or, &[lhs, rhs], Vec::new())?)), + BinOpKind::And => return Ok(Bool::And(self.extract(BinOpKind::And, &[lhs, rhs], Vec::new())?)), + _ => (), + }, + ExprKind::Lit(lit) => match lit.node { + LitKind::Bool(true) => return Ok(Bool::True), + LitKind::Bool(false) => return Ok(Bool::False), + _ => (), + }, + _ => (), + } + } + for (n, expr) in self.terminals.iter().enumerate() { + if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) { + #[allow(clippy::cast_possible_truncation)] + return Ok(Bool::Term(n as u8)); + } + + if_chain! { + if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind; + if implements_ord(self.cx, e_lhs); + if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; + if negate(e_binop.node) == Some(expr_binop.node); + if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs); + if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs); + then { + #[allow(clippy::cast_possible_truncation)] + return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); + } + } + } + let n = self.terminals.len(); + self.terminals.push(e); + if n < 32 { + #[allow(clippy::cast_possible_truncation)] + Ok(Bool::Term(n as u8)) + } else { + Err("too many literals".to_owned()) + } + } +} + +struct SuggestContext<'a, 'tcx, 'v> { + terminals: &'v [&'v Expr<'v>], + cx: &'a LateContext<'a, 'tcx>, + output: String, +} + +impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { + fn recurse(&mut self, suggestion: &Bool) -> Option<()> { + use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; + match suggestion { + True => { + self.output.push_str("true"); + }, + False => { + self.output.push_str("false"); + }, + Not(inner) => match **inner { + And(_) | Or(_) => { + self.output.push('!'); + self.output.push('('); + self.recurse(inner); + self.output.push(')'); + }, + Term(n) => { + let terminal = self.terminals[n as usize]; + if let Some(str) = simplify_not(self.cx, terminal) { + self.output.push_str(&str) + } else { + self.output.push('!'); + let snip = snippet_opt(self.cx, terminal.span)?; + self.output.push_str(&snip); + } + }, + True | False | Not(_) => { + self.output.push('!'); + self.recurse(inner)?; + }, + }, + And(v) => { + for (index, inner) in v.iter().enumerate() { + if index > 0 { + self.output.push_str(" && "); + } + if let Or(_) = *inner { + self.output.push('('); + self.recurse(inner); + self.output.push(')'); + } else { + self.recurse(inner); + } + } + }, + Or(v) => { + for (index, inner) in v.iter().rev().enumerate() { + if index > 0 { + self.output.push_str(" || "); + } + self.recurse(inner); + } + }, + &Term(n) => { + let snip = snippet_opt(self.cx, self.terminals[n as usize].span)?; + self.output.push_str(&snip); + }, + } + Some(()) + } +} + +fn simplify_not(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::Binary(binop, lhs, rhs) => { + if !implements_ord(cx, lhs) { + return None; + } + + match binop.node { + BinOpKind::Eq => Some(" != "), + BinOpKind::Ne => Some(" == "), + BinOpKind::Lt => Some(" >= "), + BinOpKind::Gt => Some(" <= "), + BinOpKind::Le => Some(" > "), + BinOpKind::Ge => Some(" < "), + _ => None, + } + .and_then(|op| { + Some(format!( + "{}{}{}", + snippet_opt(cx, lhs.span)?, + op, + snippet_opt(cx, rhs.span)? + )) + }) + }, + ExprKind::MethodCall(path, _, args) if args.len() == 1 => { + let type_of_receiver = cx.tables.expr_ty(&args[0]); + if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type)) + && !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type)) + { + return None; + } + METHODS_WITH_NEGATION + .iter() + .cloned() + .flat_map(|(a, b)| vec![(a, b), (b, a)]) + .find(|&(a, _)| { + let path: &str = &path.ident.name.as_str(); + a == path + }) + .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, args[0].span)?, neg_method))) + }, + _ => None, + } +} + +fn suggest(cx: &LateContext<'_, '_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String { + let mut suggest_context = SuggestContext { + terminals, + cx, + output: String::new(), + }; + suggest_context.recurse(suggestion); + suggest_context.output +} + +fn simple_negate(b: Bool) -> Bool { + use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; + match b { + True => False, + False => True, + t @ Term(_) => Not(Box::new(t)), + And(mut v) => { + for el in &mut v { + *el = simple_negate(::std::mem::replace(el, True)); + } + Or(v) + }, + Or(mut v) => { + for el in &mut v { + *el = simple_negate(::std::mem::replace(el, True)); + } + And(v) + }, + Not(inner) => *inner, + } +} + +#[derive(Default)] +struct Stats { + terminals: [usize; 32], + negations: usize, + ops: usize, +} + +fn terminal_stats(b: &Bool) -> Stats { + fn recurse(b: &Bool, stats: &mut Stats) { + match b { + True | False => stats.ops += 1, + Not(inner) => { + match **inner { + And(_) | Or(_) => stats.ops += 1, // brackets are also operations + _ => stats.negations += 1, + } + recurse(inner, stats); + }, + And(v) | Or(v) => { + stats.ops += v.len() - 1; + for inner in v { + recurse(inner, stats); + } + }, + &Term(n) => stats.terminals[n as usize] += 1, + } + } + use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; + let mut stats = Stats::default(); + recurse(b, &mut stats); + stats +} + +impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { + fn bool_expr(&self, e: &'tcx Expr<'_>) { + let mut h2q = Hir2Qmm { + terminals: Vec::new(), + cx: self.cx, + }; + if let Ok(expr) = h2q.run(e) { + if h2q.terminals.len() > 8 { + // QMC has exponentially slow behavior as the number of terminals increases + // 8 is reasonable, it takes approximately 0.2 seconds. + // See #825 + return; + } + + let stats = terminal_stats(&expr); + let mut simplified = expr.simplify(); + for simple in Bool::Not(Box::new(expr)).simplify() { + match simple { + Bool::Not(_) | Bool::True | Bool::False => {}, + _ => simplified.push(Bool::Not(Box::new(simple.clone()))), + } + let simple_negated = simple_negate(simple); + if simplified.iter().any(|s| *s == simple_negated) { + continue; + } + simplified.push(simple_negated); + } + let mut improvements = Vec::with_capacity(simplified.len()); + 'simplified: for suggestion in &simplified { + let simplified_stats = terminal_stats(suggestion); + let mut improvement = false; + for i in 0..32 { + // ignore any "simplifications" that end up requiring a terminal more often + // than in the original expression + if stats.terminals[i] < simplified_stats.terminals[i] { + continue 'simplified; + } + if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 { + span_lint_and_then( + self.cx, + LOGIC_BUG, + e.span, + "this boolean expression contains a logic bug", + |diag| { + diag.span_help( + h2q.terminals[i].span, + "this expression can be optimized out by applying boolean operations to the \ + outer expression", + ); + diag.span_suggestion( + e.span, + "it would look like the following", + suggest(self.cx, suggestion, &h2q.terminals), + // nonminimal_bool can produce minimal but + // not human readable expressions (#3141) + Applicability::Unspecified, + ); + }, + ); + // don't also lint `NONMINIMAL_BOOL` + return; + } + // if the number of occurrences of a terminal decreases or any of the stats + // decreases while none increases + improvement |= (stats.terminals[i] > simplified_stats.terminals[i]) + || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops) + || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations); + } + if improvement { + improvements.push(suggestion); + } + } + let nonminimal_bool_lint = |suggestions: Vec<_>| { + span_lint_and_then( + self.cx, + NONMINIMAL_BOOL, + e.span, + "this boolean expression can be simplified", + |diag| { + diag.span_suggestions( + e.span, + "try", + suggestions.into_iter(), + // nonminimal_bool can produce minimal but + // not human readable expressions (#3141) + Applicability::Unspecified, + ); + }, + ); + }; + if improvements.is_empty() { + let mut visitor = NotSimplificationVisitor { cx: self.cx }; + visitor.visit_expr(e); + } else { + nonminimal_bool_lint( + improvements + .into_iter() + .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals)) + .collect(), + ); + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if in_macro(e.span) { + return; + } + match &e.kind { + ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => { + self.bool_expr(e) + }, + ExprKind::Unary(UnOp::UnNot, inner) => { + if self.cx.tables.node_types()[inner.hir_id].is_bool() { + self.bool_expr(e); + } else { + walk_expr(self, e); + } + }, + _ => walk_expr(self, e), + } + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn implements_ord<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) +} + +struct NotSimplificationVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if let ExprKind::Unary(UnOp::UnNot, inner) = &expr.kind { + if let Some(suggestion) = simplify_not(self.cx, inner) { + span_lint_and_sugg( + self.cx, + NONMINIMAL_BOOL, + expr.span, + "this boolean expression can be simplified", + "try", + suggestion, + Applicability::MachineApplicable, + ); + } + } + + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs new file mode 100644 index 0000000000000..90c00ad098ffe --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/bytecount.rs @@ -0,0 +1,118 @@ +use crate::utils::{ + contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, + span_lint_and_sugg, walk_ptrs_ty, +}; +use if_chain::if_chain; +use rustc_ast::ast::UintTy; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// **What it does:** Checks for naive byte counts + /// + /// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount) + /// crate has methods to count your bytes faster, especially for large slices. + /// + /// **Known problems:** If you have predominantly small slices, the + /// `bytecount::count(..)` method may actually be slower. However, if you can + /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be + /// faster in those cases. + /// + /// **Example:** + /// + /// ```rust + /// # let vec = vec![1_u8]; + /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead + /// ``` + pub NAIVE_BYTECOUNT, + perf, + "use of naive `.filter(|&x| x == y).count()` to count byte values" +} + +declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref count, _, ref count_args) = expr.kind; + if count.ident.name == sym!(count); + if count_args.len() == 1; + if let ExprKind::MethodCall(ref filter, _, ref filter_args) = count_args[0].kind; + if filter.ident.name == sym!(filter); + if filter_args.len() == 2; + if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind; + then { + let body = cx.tcx.hir().body(body_id); + if_chain! { + if body.params.len() == 1; + if let Some(argname) = get_pat_name(&body.params[0].pat); + if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; + if op.node == BinOpKind::Eq; + if match_type(cx, + walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])), + &paths::SLICE_ITER); + then { + let needle = match get_path_name(l) { + Some(name) if check_arg(name, argname, r) => r, + _ => match get_path_name(r) { + Some(name) if check_arg(name, argname, l) => l, + _ => { return; } + } + }; + if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.tables.expr_ty(needle)).kind { + return; + } + let haystack = if let ExprKind::MethodCall(ref path, _, ref args) = + filter_args[0].kind { + let p = path.ident.name; + if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 { + &args[0] + } else { + &filter_args[0] + } + } else { + &filter_args[0] + }; + let mut applicability = Applicability::MaybeIncorrect; + span_lint_and_sugg( + cx, + NAIVE_BYTECOUNT, + expr.span, + "You appear to be counting bytes the naive way", + "Consider using the bytecount crate", + format!("bytecount::count({}, {})", + snippet_with_applicability(cx, haystack.span, "..", &mut applicability), + snippet_with_applicability(cx, needle.span, "..", &mut applicability)), + applicability, + ); + } + }; + } + }; + } +} + +fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool { + name == arg && !contains_name(name, needle) +} + +fn get_path_name(expr: &Expr<'_>) -> Option { + match expr.kind { + ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => { + get_path_name(e) + }, + ExprKind::Block(ref b, _) => { + if b.stmts.is_empty() { + b.expr.as_ref().and_then(|p| get_path_name(p)) + } else { + None + } + }, + ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs new file mode 100644 index 0000000000000..782da249808d0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs @@ -0,0 +1,105 @@ +//! lint on missing cargo common metadata + +use std::path::PathBuf; + +use crate::utils::{run_lints, span_lint}; +use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::DUMMY_SP; + +declare_clippy_lint! { + /// **What it does:** Checks to see if all common metadata is defined in + /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata + /// + /// **Why is this bad?** It will be more difficult for users to discover the + /// purpose of the crate, and key information related to it. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```toml + /// # This `Cargo.toml` is missing an authors field: + /// [package] + /// name = "clippy" + /// version = "0.0.212" + /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" + /// repository = "https://github.com/rust-lang/rust-clippy" + /// readme = "README.md" + /// license = "MIT OR Apache-2.0" + /// keywords = ["clippy", "lint", "plugin"] + /// categories = ["development-tools", "development-tools::cargo-plugins"] + /// ``` + pub CARGO_COMMON_METADATA, + cargo, + "common metadata is defined in `Cargo.toml`" +} + +fn warning(cx: &LateContext<'_, '_>, message: &str) { + span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message); +} + +fn missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) { + let message = format!("package `{}` is missing `{}` metadata", package.name, field); + warning(cx, &message); +} + +fn is_empty_str(value: &Option) -> bool { + value.as_ref().map_or(true, String::is_empty) +} + +fn is_empty_path(value: &Option) -> bool { + value.as_ref().and_then(|x| x.to_str()).map_or(true, str::is_empty) +} + +fn is_empty_vec(value: &[String]) -> bool { + // This works because empty iterators return true + value.iter().all(String::is_empty) +} + +declare_lint_pass!(CargoCommonMetadata => [CARGO_COMMON_METADATA]); + +impl LateLintPass<'_, '_> for CargoCommonMetadata { + fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { + if !run_lints(cx, &[CARGO_COMMON_METADATA], CRATE_HIR_ID) { + return; + } + + let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() { + metadata + } else { + warning(cx, "could not read cargo metadata"); + return; + }; + + for package in metadata.packages { + if is_empty_vec(&package.authors) { + missing_warning(cx, &package, "package.authors"); + } + + if is_empty_str(&package.description) { + missing_warning(cx, &package, "package.description"); + } + + if is_empty_str(&package.license) && is_empty_path(&package.license_file) { + missing_warning(cx, &package, "either package.license or package.license_file"); + } + + if is_empty_str(&package.repository) { + missing_warning(cx, &package, "package.repository"); + } + + if is_empty_path(&package.readme) { + missing_warning(cx, &package, "package.readme"); + } + + if is_empty_vec(&package.keywords) { + missing_warning(cx, &package, "package.keywords"); + } + + if is_empty_vec(&package.categories) { + missing_warning(cx, &package, "package.categories"); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs new file mode 100644 index 0000000000000..d9776dd50a836 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -0,0 +1,345 @@ +//! lint on manually implemented checked conversions that could be transformed into `try_from` + +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; + +declare_clippy_lint! { + /// **What it does:** Checks for explicit bounds checking when casting. + /// + /// **Why is this bad?** Reduces the readability of statements & is error prone. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let foo: u32 = 5; + /// # let _ = + /// foo <= i32::MAX as u32 + /// # ; + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # use std::convert::TryFrom; + /// # let foo = 1; + /// # let _ = + /// i32::try_from(foo).is_ok() + /// # ; + /// ``` + pub CHECKED_CONVERSIONS, + pedantic, + "`try_from` could replace manual bounds checking when casting" +} + +declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, item: &Expr<'_>) { + let result = if_chain! { + if !in_external_macro(cx.sess(), item.span); + if let ExprKind::Binary(op, ref left, ref right) = &item.kind; + + then { + match op.node { + BinOpKind::Ge | BinOpKind::Le => single_check(item), + BinOpKind::And => double_check(cx, left, right), + _ => None, + } + } else { + None + } + }; + + if_chain! { + if let Some(cv) = result; + if let Some(to_type) = cv.to_type; + + then { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut + applicability); + span_lint_and_sugg( + cx, + CHECKED_CONVERSIONS, + item.span, + "Checked cast can be simplified.", + "try", + format!("{}::try_from({}).is_ok()", + to_type, + snippet), + applicability + ); + } + } + } +} + +/// Searches for a single check from unsigned to _ is done +/// todo: check for case signed -> larger unsigned == only x >= 0 +fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { + check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned) +} + +/// Searches for a combination of upper & lower bound checks +fn double_check<'a>(cx: &LateContext<'_, '_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option> { + let upper_lower = |l, r| { + let upper = check_upper_bound(l); + let lower = check_lower_bound(r); + + transpose(upper, lower).and_then(|(l, r)| l.combine(r, cx)) + }; + + upper_lower(left, right).or_else(|| upper_lower(right, left)) +} + +/// Contains the result of a tried conversion check +#[derive(Clone, Debug)] +struct Conversion<'a> { + cvt: ConversionType, + expr_to_cast: &'a Expr<'a>, + to_type: Option<&'a str>, +} + +/// The kind of conversion that is checked +#[derive(Copy, Clone, Debug, PartialEq)] +enum ConversionType { + SignedToUnsigned, + SignedToSigned, + FromUnsigned, +} + +impl<'a> Conversion<'a> { + /// Combine multiple conversions if the are compatible + pub fn combine(self, other: Self, cx: &LateContext<'_, '_>) -> Option> { + if self.is_compatible(&other, cx) { + // Prefer a Conversion that contains a type-constraint + Some(if self.to_type.is_some() { self } else { other }) + } else { + None + } + } + + /// Checks if two conversions are compatible + /// same type of conversion, same 'castee' and same 'to type' + pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_, '_>) -> bool { + (self.cvt == other.cvt) + && (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast)) + && (self.has_compatible_to_type(other)) + } + + /// Checks if the to-type is the same (if there is a type constraint) + fn has_compatible_to_type(&self, other: &Self) -> bool { + transpose(self.to_type.as_ref(), other.to_type.as_ref()).map_or(true, |(l, r)| l == r) + } + + /// Try to construct a new conversion if the conversion type is valid + fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option> { + ConversionType::try_new(from_type, to_type).map(|cvt| Conversion { + cvt, + expr_to_cast, + to_type: Some(to_type), + }) + } + + /// Construct a new conversion without type constraint + fn new_any(expr_to_cast: &'a Expr<'_>) -> Conversion<'a> { + Conversion { + cvt: ConversionType::SignedToUnsigned, + expr_to_cast, + to_type: None, + } + } +} + +impl ConversionType { + /// Creates a conversion type if the type is allowed & conversion is valid + #[must_use] + fn try_new(from: &str, to: &str) -> Option { + if UINTS.contains(&from) { + Some(Self::FromUnsigned) + } else if SINTS.contains(&from) { + if UINTS.contains(&to) { + Some(Self::SignedToUnsigned) + } else if SINTS.contains(&to) { + Some(Self::SignedToSigned) + } else { + None + } + } else { + None + } + } +} + +/// Check for `expr <= (to_type::MAX as from_type)` +fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { + if_chain! { + if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind; + if let Some((candidate, check)) = normalize_le_ge(op, left, right); + if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS); + + then { + Conversion::try_new(candidate, from, to) + } else { + None + } + } +} + +/// Check for `expr >= 0|(to_type::MIN as from_type)` +fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { + fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option> { + (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check))) + } + + // First of we need a binary containing the expression & the cast + if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind { + normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r)) + } else { + None + } +} + +/// Check for `expr >= 0` +fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { + if_chain! { + if let ExprKind::Lit(ref lit) = &check.kind; + if let LitKind::Int(0, _) = &lit.node; + + then { + Some(Conversion::new_any(candidate)) + } else { + None + } + } +} + +/// Check for `expr >= (to_type::MIN as from_type)` +fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { + if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) { + Conversion::try_new(candidate, from, to) + } else { + None + } +} + +/// Tries to extract the from- and to-type from a cast expression +fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> { + // `to_type::maxmin_value() as from_type` + let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { + // to_type::maxmin_value(), from_type + if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind; + if let TyKind::Path(ref from_type_path) = &from_type.kind; + if let Some(from_sym) = int_ty_to_sym(from_type_path); + + then { + Some((limit, from_sym)) + } else { + None + } + }; + + // `from_type::from(to_type::maxmin_value())` + let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { + if_chain! { + // `from_type::from, to_type::maxmin_value()` + if let ExprKind::Call(ref from_func, ref args) = &expr.kind; + // `to_type::maxmin_value()` + if args.len() == 1; + if let limit = &args[0]; + // `from_type::from` + if let ExprKind::Path(ref path) = &from_func.kind; + if let Some(from_sym) = get_implementing_type(path, INTS, FROM); + + then { + Some((limit, from_sym)) + } else { + None + } + } + }); + + if let Some((limit, from_type)) = limit_from { + if_chain! { + if let ExprKind::Call(ref fun_name, _) = &limit.kind; + // `to_type, maxmin_value` + if let ExprKind::Path(ref path) = &fun_name.kind; + // `to_type` + if let Some(to_type) = get_implementing_type(path, types, func); + + then { + Some((from_type, to_type)) + } else { + None + } + } + } else { + None + } +} + +/// Gets the type which implements the called function +fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> { + if_chain! { + if let QPath::TypeRelative(ref ty, ref path) = &path; + if path.ident.name.as_str() == function; + if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind; + if let [int] = &*tp.segments; + let name = &int.ident.name.as_str(); + + then { + candidates.iter().find(|c| name == *c).cloned() + } else { + None + } + } +} + +/// Gets the type as a string, if it is a supported integer +fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { + if_chain! { + if let QPath::Resolved(_, ref path) = *path; + if let [ty] = &*path.segments; + let name = &ty.ident.name.as_str(); + + then { + INTS.iter().find(|c| name == *c).cloned() + } else { + None + } + } +} + +/// (Option, Option) -> Option<(T, U)> +fn transpose(lhs: Option, rhs: Option) -> Option<(T, U)> { + match (lhs, rhs) { + (Some(l), Some(r)) => Some((l, r)), + _ => None, + } +} + +/// Will return the expressions as if they were expr1 <= expr2 +fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { + match op.node { + BinOpKind::Le => Some((left, right)), + BinOpKind::Ge => Some((right, left)), + _ => None, + } +} + +// Constants +const FROM: &str = "from"; +const MAX_VALUE: &str = "max_value"; +const MIN_VALUE: &str = "min_value"; + +const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"]; +const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"]; +const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"]; diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs new file mode 100644 index 0000000000000..3ba72e84fa827 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -0,0 +1,163 @@ +//! calculate cognitive complexity and warn about overly complex functions + +use rustc_ast::ast::Attribute; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; + +use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack}; + +declare_clippy_lint! { + /// **What it does:** Checks for methods with high cognitive complexity. + /// + /// **Why is this bad?** Methods of high cognitive complexity tend to be hard to + /// both read and maintain. Also LLVM will tend to optimize small methods better. + /// + /// **Known problems:** Sometimes it's hard to find a way to reduce the + /// complexity. + /// + /// **Example:** No. You'll see it when you get the warning. + pub COGNITIVE_COMPLEXITY, + nursery, + "functions that should be split up into multiple functions" +} + +pub struct CognitiveComplexity { + limit: LimitStack, +} + +impl CognitiveComplexity { + #[must_use] + pub fn new(limit: u64) -> Self { + Self { + limit: LimitStack::new(limit), + } + } +} + +impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); + +impl CognitiveComplexity { + #[allow(clippy::cast_possible_truncation)] + fn check<'a, 'tcx>( + &mut self, + cx: &'a LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + body_span: Span, + ) { + if body_span.from_expansion() { + return; + } + + let expr = &body.value; + + let mut helper = CCHelper { cc: 1, returns: 0 }; + helper.visit_expr(expr); + let CCHelper { cc, returns } = helper; + let ret_ty = cx.tables.node_type(expr.hir_id); + let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) { + returns + } else { + #[allow(clippy::integer_division)] + (returns / 2) + }; + + let mut rust_cc = cc; + // prevent degenerate cases where unreachable code contains `return` statements + if rust_cc >= ret_adjust { + rust_cc -= ret_adjust; + } + + if rust_cc > self.limit.limit() { + let fn_span = match kind { + FnKind::ItemFn(ident, _, _, _, _) | FnKind::Method(ident, _, _, _) => ident.span, + FnKind::Closure(_) => { + let header_span = body_span.with_hi(decl.output.span().lo()); + let pos = snippet_opt(cx, header_span).and_then(|snip| { + let low_offset = snip.find('|')?; + let high_offset = 1 + snip.get(low_offset + 1..)?.find('|')?; + let low = header_span.lo() + BytePos(low_offset as u32); + let high = low + BytePos(high_offset as u32 + 1); + + Some((low, high)) + }); + + if let Some((low, high)) = pos { + Span::new(low, high, header_span.ctxt()) + } else { + return; + } + }, + }; + + span_lint_and_help( + cx, + COGNITIVE_COMPLEXITY, + fn_span, + &format!( + "the function has a cognitive complexity of ({}/{})", + rust_cc, + self.limit.limit() + ), + None, + "you could split it up into multiple smaller functions", + ); + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CognitiveComplexity { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + hir_id: HirId, + ) { + let def_id = cx.tcx.hir().local_def_id(hir_id); + if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) { + self.check(cx, kind, decl, body, span); + } + } + + fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) { + self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity"); + } + fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) { + self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); + } +} + +struct CCHelper { + cc: u64, + returns: u64, +} + +impl<'tcx> Visitor<'tcx> for CCHelper { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + walk_expr(self, e); + match e.kind { + ExprKind::Match(_, ref arms, _) => { + if arms.len() > 1 { + self.cc += 1; + } + self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; + }, + ExprKind::Ret(_) => self.returns += 1, + _ => {}, + } + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs new file mode 100644 index 0000000000000..8090f4673aae0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -0,0 +1,170 @@ +//! Checks for if expressions that contain only an if expression. +//! +//! For example, the lint would catch: +//! +//! ```rust,ignore +//! if x { +//! if y { +//! println!("Hello world"); +//! } +//! } +//! ``` +//! +//! This lint is **warn** by default + +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::sugg::Sugg; +use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then}; +use rustc_errors::Applicability; + +declare_clippy_lint! { + /// **What it does:** Checks for nested `if` statements which can be collapsed + /// by `&&`-combining their conditions and for `else { if ... }` expressions + /// that + /// can be collapsed to `else if ...`. + /// + /// **Why is this bad?** Each `if`-statement adds one level of nesting, which + /// makes code look more complex than it really is. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// if x { + /// if y { + /// … + /// } + /// } + /// + /// // or + /// + /// if x { + /// … + /// } else { + /// if y { + /// … + /// } + /// } + /// ``` + /// + /// Should be written: + /// + /// ```rust.ignore + /// if x && y { + /// … + /// } + /// + /// // or + /// + /// if x { + /// … + /// } else if y { + /// … + /// } + /// ``` + pub COLLAPSIBLE_IF, + style, + "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)" +} + +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]); + +impl EarlyLintPass for CollapsibleIf { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if !expr.span.from_expansion() { + check_if(cx, expr) + } + } +} + +fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { + if let ast::ExprKind::If(check, then, else_) = &expr.kind { + if let Some(else_) = else_ { + check_collapsible_maybe_if_let(cx, else_); + } else if let ast::ExprKind::Let(..) = check.kind { + // Prevent triggering on `if let a = b { if c { .. } }`. + } else { + check_collapsible_no_if_let(cx, expr, check, then); + } + } +} + +fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::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) + .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<'_>, else_: &ast::Expr) { + if_chain! { + if let ast::ExprKind::Block(ref block, _) = else_.kind; + if !block_starts_with_comment(cx, block); + if let Some(else_) = expr_block(block); + if !else_.span.from_expansion(); + if let ast::ExprKind::If(..) = else_.kind; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COLLAPSIBLE_IF, + block.span, + "this `else { if .. }` block can be collapsed", + "try", + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), + applicability, + ); + } + } +} + +fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { + if_chain! { + if !block_starts_with_comment(cx, then); + if let Some(inner) = expr_block(then); + if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; + then { + if let ast::ExprKind::Let(..) = check_inner.kind { + // Prevent triggering on `if c { if let a = b { .. } }`. + return; + } + + if expr.span.ctxt() != inner.span.ctxt() { + return; + } + span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { + let lhs = Sugg::ast(cx, check, ".."); + let rhs = Sugg::ast(cx, check_inner, ".."); + diag.span_suggestion( + expr.span, + "try", + format!( + "if {} {}", + lhs.and(&rhs), + snippet_block(cx, content.span, "..", Some(expr.span)), + ), + Applicability::MachineApplicable, // snippet + ); + }); + } + } +} + +/// If the block contains only one expression, return it. +fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { + let mut it = block.stmts.iter(); + + if let (Some(stmt), None) = (it.next(), it.next()) { + match stmt.kind { + ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr), + _ => None, + } + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs new file mode 100644 index 0000000000000..93e29edcaa58f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -0,0 +1,129 @@ +use crate::utils::{ + get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq, +}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks comparison chains written with `if` that can be + /// rewritten with `match` and `cmp`. + /// + /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get + /// repetitive + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// # fn a() {} + /// # fn b() {} + /// # fn c() {} + /// fn f(x: u8, y: u8) { + /// if x > y { + /// a() + /// } else if x < y { + /// b() + /// } else { + /// c() + /// } + /// } + /// ``` + /// + /// Could be written: + /// + /// ```rust,ignore + /// use std::cmp::Ordering; + /// # fn a() {} + /// # fn b() {} + /// # fn c() {} + /// fn f(x: u8, y: u8) { + /// match x.cmp(&y) { + /// Ordering::Greater => a(), + /// Ordering::Less => b(), + /// Ordering::Equal => c() + /// } + /// } + /// ``` + pub COMPARISON_CHAIN, + style, + "`if`s that can be rewritten with `match` and `cmp`" +} + +declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + // We only care about the top-most `if` in the chain + if parent_node_is_if_expr(expr, cx) { + return; + } + + // Check that there exists at least one explicit else condition + let (conds, _) = if_sequence(expr); + if conds.len() < 2 { + return; + } + + for cond in conds.windows(2) { + if let ( + &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1), + &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2), + ) = (&cond[0].kind, &cond[1].kind) + { + if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) { + return; + } + + // Check that both sets of operands are equal + let mut spanless_eq = SpanlessEq::new(cx); + let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2); + let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2); + + if !same_fixed_operands && !same_transposed_operands { + return; + } + + // Check that if the operation is the same, either it's not `==` or the operands are transposed + if kind1.node == kind2.node { + if kind1.node == BinOpKind::Eq { + return; + } + if !same_transposed_operands { + return; + } + } + + // Check that the type being compared implements `core::cmp::Ord` + let ty = cx.tables.expr_ty(lhs1); + let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); + + if !is_ord { + return; + } + } else { + // We only care about comparison chains + return; + } + } + span_lint_and_help( + cx, + COMPARISON_CHAIN, + expr.span, + "`if` chain can be rewritten with `match`", + None, + "Consider rewriting the `if` chain to use `cmp` and `match`.", + ) + } +} + +fn kind_is_cmp(kind: BinOpKind) -> bool { + match kind { + BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/consts.rs b/src/tools/clippy/clippy_lints/src/consts.rs new file mode 100644 index 0000000000000..81ddc8c0067c7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/consts.rs @@ -0,0 +1,559 @@ +#![allow(clippy::float_cmp)] + +use crate::utils::{clip, higher, sext, unsext}; +use if_chain::if_chain; +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; +use rustc_data_structures::sync::Lrc; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; +use rustc_span::symbol::Symbol; +use std::cmp::Ordering::{self, Equal}; +use std::convert::TryInto; +use std::hash::{Hash, Hasher}; + +/// A `LitKind`-like enum to fold constant `Expr`s into. +#[derive(Debug, Clone)] +pub enum Constant { + /// A `String` (e.g., "abc"). + Str(String), + /// A binary string (e.g., `b"abc"`). + Binary(Lrc>), + /// A single `char` (e.g., `'a'`). + Char(char), + /// An integer's bit representation. + Int(u128), + /// An `f32`. + F32(f32), + /// An `f64`. + F64(f64), + /// `true` or `false`. + Bool(bool), + /// An array of constants. + Vec(Vec), + /// Also an array, but with only one constant, repeated N times. + Repeat(Box, u64), + /// A tuple of constants. + Tuple(Vec), + /// A raw pointer. + RawPtr(u128), + /// A literal with syntax error. + Err(Symbol), +} + +impl PartialEq for Constant { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, + (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, + (&Self::Char(l), &Self::Char(r)) => l == r, + (&Self::Int(l), &Self::Int(r)) => l == r, + (&Self::F64(l), &Self::F64(r)) => { + // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have + // `Fw32 == Fw64`, so don’t compare them. + // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + l.to_bits() == r.to_bits() + }, + (&Self::F32(l), &Self::F32(r)) => { + // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have + // `Fw32 == Fw64`, so don’t compare them. + // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + f64::from(l).to_bits() == f64::from(r).to_bits() + }, + (&Self::Bool(l), &Self::Bool(r)) => l == r, + (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, + (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, + // TODO: are there inter-type equalities? + _ => false, + } + } +} + +impl Hash for Constant { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + std::mem::discriminant(self).hash(state); + match *self { + Self::Str(ref s) => { + s.hash(state); + }, + Self::Binary(ref b) => { + b.hash(state); + }, + Self::Char(c) => { + c.hash(state); + }, + Self::Int(i) => { + i.hash(state); + }, + Self::F32(f) => { + f64::from(f).to_bits().hash(state); + }, + Self::F64(f) => { + f.to_bits().hash(state); + }, + Self::Bool(b) => { + b.hash(state); + }, + Self::Vec(ref v) | Self::Tuple(ref v) => { + v.hash(state); + }, + Self::Repeat(ref c, l) => { + c.hash(state); + l.hash(state); + }, + Self::RawPtr(u) => { + u.hash(state); + }, + Self::Err(ref s) => { + s.hash(state); + }, + } + } +} + +impl Constant { + pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { + match (left, right) { + (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), + (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), + (&Self::Int(l), &Self::Int(r)) => { + if let ty::Int(int_ty) = cmp_type.kind { + Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) + } else { + Some(l.cmp(&r)) + } + }, + (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), + (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), + (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), + (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l + .iter() + .zip(r.iter()) + .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + match Self::partial_cmp(tcx, cmp_type, lv, rv) { + Some(Equal) => Some(ls.cmp(rs)), + x => x, + } + }, + // TODO: are there any useful inter-type orderings? + _ => None, + } + } +} + +/// Parses a `LitKind` to a `Constant`. +pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { + match *lit { + LitKind::Str(ref is, _) => Constant::Str(is.to_string()), + LitKind::Byte(b) => Constant::Int(u128::from(b)), + LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)), + LitKind::Char(c) => Constant::Char(c), + LitKind::Int(n, _) => Constant::Int(n), + LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { + FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), + FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), + }, + LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind { + ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()), + ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()), + _ => bug!(), + }, + LitKind::Bool(b) => Constant::Bool(b), + LitKind::Err(s) => Constant::Err(s), + } +} + +pub fn constant<'c, 'cc>( + lcx: &LateContext<'c, 'cc>, + tables: &'c ty::TypeckTables<'cc>, + e: &Expr<'_>, +) -> Option<(Constant, bool)> { + let mut cx = ConstEvalLateContext { + lcx, + tables, + param_env: lcx.param_env, + needed_resolution: false, + substs: lcx.tcx.intern_substs(&[]), + }; + cx.expr(e).map(|cst| (cst, cx.needed_resolution)) +} + +pub fn constant_simple<'c, 'cc>( + lcx: &LateContext<'c, 'cc>, + tables: &'c ty::TypeckTables<'cc>, + e: &Expr<'_>, +) -> Option { + constant(lcx, tables, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) +} + +/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckTables`. +pub fn constant_context<'c, 'cc>( + lcx: &'c LateContext<'c, 'cc>, + tables: &'c ty::TypeckTables<'cc>, +) -> ConstEvalLateContext<'c, 'cc> { + ConstEvalLateContext { + lcx, + tables, + param_env: lcx.param_env, + needed_resolution: false, + substs: lcx.tcx.intern_substs(&[]), + } +} + +pub struct ConstEvalLateContext<'a, 'tcx> { + lcx: &'a LateContext<'a, 'tcx>, + tables: &'a ty::TypeckTables<'tcx>, + param_env: ty::ParamEnv<'tcx>, + needed_resolution: bool, + substs: SubstsRef<'tcx>, +} + +impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { + /// Simple constant folding: Insert an expression, get a constant or none. + pub fn expr(&mut self, e: &Expr<'_>) -> Option { + if let Some((ref cond, ref then, otherwise)) = higher::if_block(&e) { + return self.ifthenelse(cond, then, otherwise); + } + match e.kind { + ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.tables.expr_ty(e)), + ExprKind::Block(ref block, _) => self.block(block), + ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.tables.expr_ty_opt(e))), + ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec), + ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), + ExprKind::Repeat(ref value, _) => { + let n = match self.tables.expr_ty(e).kind { + ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?, + _ => span_bug!(e.span, "typeck error"), + }; + self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) + }, + ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { + UnOp::UnNot => self.constant_not(&o, self.tables.expr_ty(e)), + UnOp::UnNeg => self.constant_negate(&o, self.tables.expr_ty(e)), + UnOp::UnDeref => Some(o), + }), + ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), + ExprKind::Call(ref callee, ref args) => { + // We only handle a few const functions for now. + if_chain! { + if args.is_empty(); + if let ExprKind::Path(qpath) = &callee.kind; + let res = self.tables.qpath_res(qpath, callee.hir_id); + if let Some(def_id) = res.opt_def_id(); + let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect(); + let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect(); + if let ["core", "num", int_impl, "max_value"] = *def_path; + then { + let value = match int_impl { + "" => i8::max_value() as u128, + "" => i16::max_value() as u128, + "" => i32::max_value() as u128, + "" => i64::max_value() as u128, + "" => i128::max_value() as u128, + _ => return None, + }; + Some(Constant::Int(value)) + } + else { + None + } + } + }, + ExprKind::Index(ref arr, ref index) => self.index(arr, index), + // TODO: add other expressions. + _ => None, + } + } + + #[allow(clippy::cast_possible_wrap)] + fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { + use self::Constant::{Bool, Int}; + match *o { + Bool(b) => Some(Bool(!b)), + Int(value) => { + let value = !value; + match ty.kind { + ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), + ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), + _ => None, + } + }, + _ => None, + } + } + + fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { + use self::Constant::{Int, F32, F64}; + match *o { + Int(value) => { + let ity = match ty.kind { + ty::Int(ity) => ity, + _ => return None, + }; + // sign extend + let value = sext(self.lcx.tcx, value, ity); + let value = value.checked_neg()?; + // clear unused bits + Some(Int(unsext(self.lcx.tcx, value, ity))) + }, + F32(f) => Some(F32(-f)), + F64(f) => Some(F64(-f)), + _ => None, + } + } + + /// Create `Some(Vec![..])` of all constants, unless there is any + /// non-constant part. + fn multi(&mut self, vec: &[Expr<'_>]) -> Option> { + vec.iter().map(|elem| self.expr(elem)).collect::>() + } + + /// Lookup a possibly constant expression from a `ExprKind::Path`. + fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'cc>) -> Option { + let res = self.tables.qpath_res(qpath, id); + match res { + Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { + let substs = self.tables.node_substs(id); + let substs = if self.substs.is_empty() { + substs + } else { + substs.subst(self.lcx.tcx, self.substs) + }; + + let result = self + .lcx + .tcx + .const_eval_resolve(self.param_env, def_id, substs, None, None) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?; + let result = miri_to_const(&result); + if result.is_some() { + self.needed_resolution = true; + } + result + }, + // FIXME: cover all usable cases. + _ => None, + } + } + + fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { + let lhs = self.expr(lhs); + let index = self.expr(index); + + match (lhs, index) { + (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { + Some(Constant::F32(x)) => Some(Constant::F32(*x)), + Some(Constant::F64(x)) => Some(Constant::F64(*x)), + _ => None, + }, + (Some(Constant::Vec(vec)), _) => { + if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { + match vec.get(0) { + Some(Constant::F32(x)) => Some(Constant::F32(*x)), + Some(Constant::F64(x)) => Some(Constant::F64(*x)), + _ => None, + } + } else { + None + } + }, + _ => None, + } + } + + /// A block can only yield a constant if it only has one constant expression. + fn block(&mut self, block: &Block<'_>) -> Option { + if block.stmts.is_empty() { + block.expr.as_ref().and_then(|b| self.expr(b)) + } else { + None + } + } + + fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { + if let Some(Constant::Bool(b)) = self.expr(cond) { + if b { + self.expr(&*then) + } else { + otherwise.as_ref().and_then(|expr| self.expr(expr)) + } + } else { + None + } + } + + fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option { + let l = self.expr(left)?; + let r = self.expr(right); + match (l, r) { + (Constant::Int(l), Some(Constant::Int(r))) => match self.tables.expr_ty(left).kind { + ty::Int(ity) => { + let l = sext(self.lcx.tcx, l, ity); + let r = sext(self.lcx.tcx, r, ity); + let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); + match op.node { + BinOpKind::Add => l.checked_add(r).map(zext), + BinOpKind::Sub => l.checked_sub(r).map(zext), + BinOpKind::Mul => l.checked_mul(r).map(zext), + BinOpKind::Div if r != 0 => l.checked_div(r).map(zext), + BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext), + BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext), + BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext), + BinOpKind::BitXor => Some(zext(l ^ r)), + BinOpKind::BitOr => Some(zext(l | r)), + BinOpKind::BitAnd => Some(zext(l & r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + } + }, + ty::Uint(_) => match op.node { + BinOpKind::Add => l.checked_add(r).map(Constant::Int), + BinOpKind::Sub => l.checked_sub(r).map(Constant::Int), + BinOpKind::Mul => l.checked_mul(r).map(Constant::Int), + BinOpKind::Div => l.checked_div(r).map(Constant::Int), + BinOpKind::Rem => l.checked_rem(r).map(Constant::Int), + BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int), + BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int), + BinOpKind::BitXor => Some(Constant::Int(l ^ r)), + BinOpKind::BitOr => Some(Constant::Int(l | r)), + BinOpKind::BitAnd => Some(Constant::Int(l & r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + }, + _ => None, + }, + (Constant::F32(l), Some(Constant::F32(r))) => match op.node { + BinOpKind::Add => Some(Constant::F32(l + r)), + BinOpKind::Sub => Some(Constant::F32(l - r)), + BinOpKind::Mul => Some(Constant::F32(l * r)), + BinOpKind::Div => Some(Constant::F32(l / r)), + BinOpKind::Rem => Some(Constant::F32(l % r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + }, + (Constant::F64(l), Some(Constant::F64(r))) => match op.node { + BinOpKind::Add => Some(Constant::F64(l + r)), + BinOpKind::Sub => Some(Constant::F64(l - r)), + BinOpKind::Mul => Some(Constant::F64(l * r)), + BinOpKind::Div => Some(Constant::F64(l / r)), + BinOpKind::Rem => Some(Constant::F64(l % r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + }, + (l, r) => match (op.node, 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)) => { + Some(r) + }, + (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)), + (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)), + (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)), + _ => None, + }, + } + } +} + +pub fn miri_to_const(result: &ty::Const<'_>) -> Option { + use rustc_middle::mir::interpret::{ConstValue, Scalar}; + match result.val { + ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: d, .. })) => match result.ty.kind { + ty::Bool => Some(Constant::Bool(d == 1)), + ty::Uint(_) | ty::Int(_) => Some(Constant::Int(d)), + ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( + d.try_into().expect("invalid f32 bit representation"), + ))), + ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( + d.try_into().expect("invalid f64 bit representation"), + ))), + ty::RawPtr(type_and_mut) => { + if let ty::Uint(_) = type_and_mut.ty.kind { + return Some(Constant::RawPtr(d)); + } + None + }, + // FIXME: implement other conversions. + _ => None, + }, + ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind { + ty::Ref(_, tam, _) => match tam.kind { + ty::Str => String::from_utf8( + data.inspect_with_undef_and_ptr_outside_interpreter(start..end) + .to_owned(), + ) + .ok() + .map(Constant::Str), + _ => None, + }, + _ => None, + }, + ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind { + ty::Array(sub_type, len) => match sub_type.kind { + ty::Float(FloatTy::F32) => match miri_to_const(len) { + Some(Constant::Int(len)) => alloc + .inspect_with_undef_and_ptr_outside_interpreter(0..(4 * len as usize)) + .to_owned() + .chunks(4) + .map(|chunk| { + Some(Constant::F32(f32::from_le_bytes( + chunk.try_into().expect("this shouldn't happen"), + ))) + }) + .collect::>>() + .map(Constant::Vec), + _ => None, + }, + ty::Float(FloatTy::F64) => match miri_to_const(len) { + Some(Constant::Int(len)) => alloc + .inspect_with_undef_and_ptr_outside_interpreter(0..(8 * len as usize)) + .to_owned() + .chunks(8) + .map(|chunk| { + Some(Constant::F64(f64::from_le_bytes( + chunk.try_into().expect("this shouldn't happen"), + ))) + }) + .collect::>>() + .map(Constant::Vec), + _ => None, + }, + // FIXME: implement other array type conversions. + _ => None, + }, + _ => None, + }, + // FIXME: implement other conversions. + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs new file mode 100644 index 0000000000000..66722786eab49 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -0,0 +1,417 @@ +use crate::utils::{get_parent_expr, higher, if_sequence, same_tys, snippet, span_lint_and_note, span_lint_and_then}; +use crate::utils::{SpanlessEq, SpanlessHash}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; +use std::collections::hash_map::Entry; +use std::hash::BuildHasherDefault; + +declare_clippy_lint! { + /// **What it does:** Checks for consecutive `if`s with the same condition. + /// + /// **Why is this bad?** This is probably a copy & paste error. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```ignore + /// if a == b { + /// … + /// } else if a == b { + /// … + /// } + /// ``` + /// + /// Note that this lint ignores all conditions with a function call as it could + /// have side effects: + /// + /// ```ignore + /// if foo() { + /// … + /// } else if foo() { // not linted + /// … + /// } + /// ``` + pub IFS_SAME_COND, + correctness, + "consecutive `if`s with the same condition" +} + +declare_clippy_lint! { + /// **What it does:** Checks for consecutive `if`s with the same function call. + /// + /// **Why is this bad?** This is probably a copy & paste error. + /// Despite the fact that function can have side effects and `if` works as + /// intended, such an approach is implicit and can be considered a "code smell". + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```ignore + /// if foo() == bar { + /// … + /// } else if foo() == bar { + /// … + /// } + /// ``` + /// + /// This probably should be: + /// ```ignore + /// if foo() == bar { + /// … + /// } else if foo() == baz { + /// … + /// } + /// ``` + /// + /// or if the original code was not a typo and called function mutates a state, + /// consider move the mutation out of the `if` condition to avoid similarity to + /// a copy & paste error: + /// + /// ```ignore + /// let first = foo(); + /// if first == bar { + /// … + /// } else { + /// let second = foo(); + /// if second == bar { + /// … + /// } + /// } + /// ``` + pub SAME_FUNCTIONS_IN_IF_CONDITION, + pedantic, + "consecutive `if`s with the same function call" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `if/else` with the same body as the *then* part + /// and the *else* part. + /// + /// **Why is this bad?** This is probably a copy & paste error. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```ignore + /// let foo = if … { + /// 42 + /// } else { + /// 42 + /// }; + /// ``` + pub IF_SAME_THEN_ELSE, + correctness, + "`if` with the same `then` and `else` blocks" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `match` with identical arm bodies. + /// + /// **Why is this bad?** This is probably a copy & paste error. If arm bodies + /// are the same on purpose, you can factor them + /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). + /// + /// **Known problems:** False positive possible with order dependent `match` + /// (see issue + /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). + /// + /// **Example:** + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => bar(), // <= oops + /// } + /// ``` + /// + /// This should probably be + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => baz(), // <= fixed + /// } + /// ``` + /// + /// or if the original code was not a typo: + /// ```rust,ignore + /// match foo { + /// Bar | Baz => bar(), // <= shows the intent better + /// Quz => quz(), + /// } + /// ``` + pub MATCH_SAME_ARMS, + pedantic, + "`match` with identical arm bodies" +} + +declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if !expr.span.from_expansion() { + // skip ifs directly in else, it will be checked in the parent if + if let Some(expr) = get_parent_expr(cx, expr) { + if let Some((_, _, Some(ref else_expr))) = higher::if_block(&expr) { + if else_expr.hir_id == expr.hir_id { + return; + } + } + } + + let (conds, blocks) = if_sequence(expr); + lint_same_then_else(cx, &blocks); + lint_same_cond(cx, &conds); + lint_same_fns_in_if_cond(cx, &conds); + lint_match_arms(cx, expr); + } + } +} + +/// Implementation of `IF_SAME_THEN_ELSE`. +fn lint_same_then_else(cx: &LateContext<'_, '_>, blocks: &[&Block<'_>]) { + let eq: &dyn Fn(&&Block<'_>, &&Block<'_>) -> bool = + &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) }; + + if let Some((i, j)) = search_same_sequenced(blocks, eq) { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + j.span, + "this `if` has identical blocks", + Some(i.span), + "same as this", + ); + } +} + +/// Implementation of `IFS_SAME_COND`. +fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) { + let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { + let mut h = SpanlessHash::new(cx, cx.tables); + h.hash_expr(expr); + h.finish() + }; + + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = + &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) }; + + for (i, j) in search_same(conds, hash, eq) { + 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", + ); + } +} + +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. +fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) { + let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { + let mut h = SpanlessHash::new(cx, cx.tables); + h.hash_expr(expr); + h.finish() + }; + + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not spawn warning if `IFS_SAME_COND` already produced it. + if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) { + return false; + } + SpanlessEq::new(cx).eq_expr(lhs, rhs) + }; + + for (i, j) in search_same(conds, hash, eq) { + span_lint_and_note( + 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", + ); + } +} + +/// Implementation of `MATCH_SAME_ARMS`. +fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) { + fn same_bindings<'tcx>( + cx: &LateContext<'_, 'tcx>, + lhs: &FxHashMap>, + rhs: &FxHashMap>, + ) -> bool { + lhs.len() == rhs.len() + && lhs + .iter() + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| same_tys(cx, l_ty, r_ty))) + } + + if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { + let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { + let mut h = SpanlessHash::new(cx, cx.tables); + h.hash_expr(&arm.body); + h.finish() + }; + + let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { + let min_index = usize::min(lindex, rindex); + let max_index = usize::max(lindex, rindex); + + // Arms with a guard are ignored, those can’t always be merged together + // This is also the case for arms in-between each there is an arm with a guard + (min_index..=max_index).all(|index| arms[index].guard.is_none()) && + SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && + // all patterns should have the same bindings + same_bindings(cx, &bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) + }; + + let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); + for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + j.body.span, + "this `match` has identical arm bodies", + |diag| { + diag.span_note(i.body.span, "same as this"); + + // Note: this does not use `span_suggestion` on purpose: + // there is no clean way + // to remove the other arm. Building a span and suggest to replace it to "" + // makes an even more confusing error message. Also in order not to make up a + // span for the whole pattern, the suggestion is only shown when there is only + // one pattern. The user should know about `|` if they are already using it… + + let lhs = snippet(cx, i.pat.span, ""); + let rhs = snippet(cx, j.pat.span, ""); + + if let PatKind::Wild = j.pat.kind { + // if the last arm is _, then i could be integrated into _ + // note that i.pat cannot be _, because that would mean that we're + // hiding all the subsequent arms, and rust won't compile + diag.span_note( + i.body.span, + &format!( + "`{}` has the same arm body as the `_` wildcard, consider removing it", + lhs + ), + ); + } else { + diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); + } + }, + ); + } + } +} + +/// Returns the list of bindings in a pattern. +fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat<'_>) -> FxHashMap> { + fn bindings_impl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat<'_>, map: &mut FxHashMap>) { + match pat.kind { + PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), + PatKind::TupleStruct(_, pats, _) => { + for pat in pats { + bindings_impl(cx, pat, map); + } + }, + PatKind::Binding(.., ident, ref as_pat) => { + if let Entry::Vacant(v) = map.entry(ident.name) { + v.insert(cx.tables.pat_ty(pat)); + } + if let Some(ref as_pat) = *as_pat { + bindings_impl(cx, as_pat, map); + } + }, + PatKind::Or(fields) | PatKind::Tuple(fields, _) => { + for pat in fields { + bindings_impl(cx, pat, map); + } + }, + PatKind::Struct(_, fields, _) => { + for pat in fields { + bindings_impl(cx, &pat.pat, map); + } + }, + PatKind::Slice(lhs, ref mid, rhs) => { + for pat in lhs { + bindings_impl(cx, pat, map); + } + if let Some(ref mid) = *mid { + bindings_impl(cx, mid, map); + } + for pat in rhs { + bindings_impl(cx, pat, map); + } + }, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (), + } + } + + let mut result = FxHashMap::default(); + bindings_impl(cx, pat, &mut result); + result +} + +fn search_same_sequenced(exprs: &[T], eq: Eq) -> Option<(&T, &T)> +where + Eq: Fn(&T, &T) -> bool, +{ + for win in exprs.windows(2) { + if eq(&win[0], &win[1]) { + return Some((&win[0], &win[1])); + } + } + None +} + +fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)> +where + Eq: Fn(&T, &T) -> bool, +{ + if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) { + Some((&exprs[0], &exprs[1])) + } else { + None + } +} + +fn search_same(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> +where + Hash: Fn(&T) -> u64, + Eq: Fn(&T, &T) -> bool, +{ + if let Some(expr) = search_common_cases(&exprs, &eq) { + return vec![expr]; + } + + let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); + + let mut map: FxHashMap<_, Vec<&_>> = + FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::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)); + } + } + o.get_mut().push(expr); + }, + Entry::Vacant(v) => { + v.insert(vec![expr]); + }, + } + } + + match_expr_list +} diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs new file mode 100644 index 0000000000000..d79aa2ef0209e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs @@ -0,0 +1,55 @@ +use crate::utils::{is_copy, match_path, paths, span_lint_and_note}; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for types that implement `Copy` as well as + /// `Iterator`. + /// + /// **Why is this bad?** Implicit copies can be confusing when working with + /// iterator combinators. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// #[derive(Copy, Clone)] + /// struct Countdown(u8); + /// + /// impl Iterator for Countdown { + /// // ... + /// } + /// + /// let a: Vec<_> = my_iterator.take(1).collect(); + /// let b: Vec<_> = my_iterator.collect(); + /// ``` + pub COPY_ITERATOR, + pedantic, + "implementing `Iterator` on a `Copy` type" +} + +declare_lint_pass!(CopyIterator => [COPY_ITERATOR]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyIterator { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Impl { + of_trait: Some(ref trait_ref), + .. + } = item.kind + { + let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id)); + + if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) { + span_lint_and_note( + cx, + COPY_ITERATOR, + item.span, + "you are implementing `Iterator` on a `Copy` type", + None, + "consider implementing `IntoIterator` instead", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs new file mode 100644 index 0000000000000..e513dcce64e53 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -0,0 +1,65 @@ +use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use rustc_ast::ast; +use rustc_ast::tokenstream::TokenStream; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of dbg!() macro. + /// + /// **Why is this bad?** `dbg!` macro is intended as a debugging tool. It + /// should not be in version control. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// // Bad + /// dbg!(true) + /// + /// // Good + /// true + /// ``` + pub DBG_MACRO, + restriction, + "`dbg!` macro is intended as a debugging tool" +} + +declare_lint_pass!(DbgMacro => [DBG_MACRO]); + +impl EarlyLintPass for DbgMacro { + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + if mac.path == sym!(dbg) { + if let Some(sugg) = tts_span(mac.args.inner_tokens()).and_then(|span| snippet_opt(cx, span)) { + span_lint_and_sugg( + cx, + DBG_MACRO, + mac.span(), + "`dbg!` macro is intended as a debugging tool", + "ensure to avoid having uses of it in version control", + sugg, + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_help( + cx, + DBG_MACRO, + mac.span(), + "`dbg!` macro is intended as a debugging tool", + None, + "ensure to avoid having uses of it in version control", + ); + } + } + } +} + +// Get span enclosing entire the token stream. +fn tts_span(tts: TokenStream) -> Option { + let mut cursor = tts.into_trees(); + let first = cursor.next()?.span(); + let span = cursor.last().map_or(first, |tree| first.to(tree.span())); + Some(span) +} diff --git a/src/tools/clippy/clippy_lints/src/default_trait_access.rs b/src/tools/clippy/clippy_lints/src/default_trait_access.rs new file mode 100644 index 0000000000000..635d609c38289 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/default_trait_access.rs @@ -0,0 +1,76 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg}; + +declare_clippy_lint! { + /// **What it does:** Checks for literal calls to `Default::default()`. + /// + /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is + /// being gotten than the generic `Default`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let s: String = Default::default(); + /// + /// // Good + /// let s = String::default(); + /// ``` + pub DEFAULT_TRAIT_ACCESS, + pedantic, + "checks for literal calls to `Default::default()`" +} + +declare_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DefaultTraitAccess { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref path, ..) = expr.kind; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + then { + match qpath { + QPath::Resolved(..) => { + if_chain! { + // Detect and ignore ::default() because these calls do + // explicitly name the type. + if let ExprKind::Call(ref method, ref _args) = expr.kind; + if let ExprKind::Path(ref p) = method.kind; + if let QPath::Resolved(Some(_ty), _path) = p; + then { + return; + } + } + + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let expr_ty = cx.tables.expr_ty(expr); + if let ty::Adt(..) = expr_ty.kind { + let replacement = format!("{}::default()", expr_ty); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("Calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); + } + }, + QPath::TypeRelative(..) => {}, + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs new file mode 100644 index 0000000000000..6e8ca647dd7ae --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -0,0 +1,157 @@ +macro_rules! declare_deprecated_lint { + (pub $name: ident, $_reason: expr) => { + declare_lint!(pub $name, Allow, "deprecated lint") + } +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend + /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. + pub SHOULD_ASSERT_EQ, + "`assert!()` will be more flexible with RFC 2011" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than + /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true. + pub EXTEND_FROM_SLICE, + "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's + /// an infinite iterator, which is better expressed by `iter::repeat`, + /// but the method has been removed for `Iterator::step_by` which panics + /// if given a zero + pub RANGE_STEP_BY_ZERO, + "`iterator.step_by(0)` panics nowadays" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good + /// stable alternatives. `Vec::as_slice` has now been stabilized. + pub UNSTABLE_AS_SLICE, + "`Vec::as_slice` has been stabilized in 1.7" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good + /// stable alternatives. `Vec::as_mut_slice` has now been stabilized. + pub UNSTABLE_AS_MUT_SLICE, + "`Vec::as_mut_slice` has been stabilized in 1.7" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `.to_string()` method calls on values + /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be + /// specialized to be as efficient as `to_owned`. + pub STR_TO_STRING, + "using `str::to_string` is common even today and specialization will likely happen soon" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `.to_string()` method calls on values + /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be + /// specialized to be as efficient as `clone`. + pub STRING_TO_STRING, + "using `string::to_string` is common even today and specialization will likely happen soon" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting + /// between non-pointer types of differing alignment is well-defined behavior (it's semantically + /// equivalent to a memcpy). This lint has thus been refactored into two separate lints: + /// cast_ptr_alignment and transmute_ptr_to_ptr. + pub MISALIGNED_TRANSMUTE, + "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy. + /// Additionally, compound assignment operators may be overloaded separately from their non-assigning + /// counterparts, so this lint may suggest a change in behavior or the code may not compile. + pub ASSIGN_OPS, + "using compound assignment operators (e.g., `+=`) is harmless" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** The original rule will only lint for `if let`. After + /// making it support to lint `match`, naming as `if let` is not suitable for it. + /// So, this lint is deprecated. + pub IF_LET_REDUNDANT_PATTERN_MATCHING, + "this lint has been changed to redundant_pattern_matching" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint used to suggest replacing `let mut vec = + /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The + /// replacement has very different performance characteristics so the lint is + /// deprecated. + pub UNSAFE_VECTOR_INITIALIZATION, + "the replacement suggested by this lint had substantially different behavior" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been superseded by the warn-by-default + /// `invalid_value` rustc lint. + pub INVALID_REF, + "superseded by rustc lint `invalid_value`" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc. + pub UNUSED_COLLECT, + "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been uplifted to rustc and is now called + /// `array_into_iter`. + pub INTO_ITER_ON_ARRAY, + "this lint has been uplifted to rustc and is now called `array_into_iter`" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been uplifted to rustc and is now called + /// `unused_labels`. + pub UNUSED_LABEL, + "this lint has been uplifted to rustc and is now called `unused_labels`" +} + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** Associated-constants are now preferred. + pub REPLACE_CONSTS, + "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants" +} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs new file mode 100644 index 0000000000000..68ec07e2bcb0f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -0,0 +1,113 @@ +use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. + /// + /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise, + /// when not part of a method chain. + /// + /// **Example:** + /// ```rust + /// use std::ops::Deref; + /// let a: &mut String = &mut String::from("foo"); + /// let b: &str = a.deref(); + /// ``` + /// Could be written as: + /// ```rust + /// let a: &mut String = &mut String::from("foo"); + /// let b = &*a; + /// ``` + /// + /// This lint excludes + /// ```rust,ignore + /// let _ = d.unwrap().deref(); + /// ``` + pub EXPLICIT_DEREF_METHODS, + pedantic, + "Explicit use of deref or deref_mut method while not in a method chain." +} + +declare_lint_pass!(Dereferencing => [ + EXPLICIT_DEREF_METHODS +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Dereferencing { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if !expr.span.from_expansion(); + if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.kind; + if args.len() == 1; + + then { + if let Some(parent_expr) = get_parent_expr(cx, expr) { + // Check if we have the whole call chain here + if let ExprKind::MethodCall(..) = parent_expr.kind { + return; + } + // Check for Expr that we don't want to be linted + let precedence = parent_expr.precedence(); + match precedence { + // Lint a Call is ok though + ExprPrecedence::Call | ExprPrecedence::AddrOf => (), + _ => { + if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX { + return; + } + } + } + } + let name = method_name.ident.as_str(); + lint_deref(cx, &*name, &args[0], args[0].span, expr.span); + } + } + } +} + +fn lint_deref(cx: &LateContext<'_, '_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) { + match method_name { + "deref" => { + if cx + .tcx + .lang_items() + .deref_trait() + .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[])) + { + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + expr_span, + "explicit deref method call", + "try this", + format!("&*{}", &snippet(cx, var_span, "..")), + Applicability::MachineApplicable, + ); + } + }, + "deref_mut" => { + if cx + .tcx + .lang_items() + .deref_mut_trait() + .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[])) + { + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + expr_span, + "explicit deref_mut method call", + "try this", + format!("&mut *{}", &snippet(cx, var_span, "..")), + Applicability::MachineApplicable, + ); + } + }, + _ => (), + } +} diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs new file mode 100644 index 0000000000000..3cbb8fa72f74f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -0,0 +1,309 @@ +use crate::utils::paths; +use crate::utils::{ + is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{ + BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq` + /// explicitly or vice versa. + /// + /// **Why is this bad?** The implementation of these traits must agree (for + /// example for use with `HashMap`) so it’s probably a bad idea to use a + /// default-generated `Hash` implementation with an explicitly defined + /// `PartialEq`. In particular, the following must hold for any type: + /// + /// ```text + /// k1 == k2 ⇒ hash(k1) == hash(k2) + /// ``` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// #[derive(Hash)] + /// struct Foo; + /// + /// impl PartialEq for Foo { + /// ... + /// } + /// ``` + pub DERIVE_HASH_XOR_EQ, + correctness, + "deriving `Hash` but implementing `PartialEq` explicitly" +} + +declare_clippy_lint! { + /// **What it does:** Checks for explicit `Clone` implementations for `Copy` + /// types. + /// + /// **Why is this bad?** To avoid surprising behaviour, these traits should + /// agree and the behaviour of `Copy` cannot be overridden. In almost all + /// situations a `Copy` type should have a `Clone` implementation that does + /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` + /// gets you. + /// + /// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925 + /// + /// **Example:** + /// ```rust,ignore + /// #[derive(Copy)] + /// struct Foo; + /// + /// impl Clone for Foo { + /// // .. + /// } + /// ``` + pub EXPL_IMPL_CLONE_ON_COPY, + pedantic, + "implementing `Clone` explicitly on `Copy` types" +} + +declare_clippy_lint! { + /// **What it does:** Checks for deriving `serde::Deserialize` on a type that + /// has methods using `unsafe`. + /// + /// **Why is this bad?** Deriving `serde::Deserialize` will create a constructor + /// that may violate invariants hold by another constructor. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use serde::Deserialize; + /// + /// #[derive(Deserialize)] + /// pub struct Foo { + /// // .. + /// } + /// + /// impl Foo { + /// pub fn new() -> Self { + /// // setup here .. + /// } + /// + /// pub unsafe fn parts() -> (&str, &str) { + /// // assumes invariants hold + /// } + /// } + /// ``` + pub UNSAFE_DERIVE_DESERIALIZE, + pedantic, + "deriving `serde::Deserialize` on a type that has methods using `unsafe`" +} + +declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Impl { + of_trait: Some(ref trait_ref), + .. + } = item.kind + { + let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id)); + let is_automatically_derived = is_automatically_derived(&*item.attrs); + + check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); + + if is_automatically_derived { + check_unsafe_derive_deserialize(cx, item, trait_ref, ty); + } else { + check_copy_clone(cx, item, trait_ref, ty); + } + } + } +} + +/// Implementation of the `DERIVE_HASH_XOR_EQ` lint. +fn check_hash_peq<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + span: Span, + trait_ref: &TraitRef<'_>, + ty: Ty<'tcx>, + hash_is_automatically_derived: bool, +) { + if_chain! { + if match_path(&trait_ref.path, &paths::HASH); + if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait(); + if let Some(def_id) = &trait_ref.trait_def_id(); + if !def_id.is_local(); + then { + // 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 = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + + if peq_is_automatically_derived == hash_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialEq for Foo` + // For `impl PartialEq for A, input_types is [A, B] + if trait_ref.substs.type_at(1) == ty { + let mess = if peq_is_automatically_derived { + "you are implementing `Hash` explicitly but have derived `PartialEq`" + } else { + "you are deriving `Hash` but have implemented `PartialEq` explicitly" + }; + + span_lint_and_then( + cx, + DERIVE_HASH_XOR_EQ, + span, + mess, + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id); + diag.span_note( + cx.tcx.hir().span(hir_id), + "`PartialEq` implemented here" + ); + } + } + ); + } + }); + } + } +} + +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. +fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { + if match_path(&trait_ref.path, &paths::CLONE_TRAIT) { + if !is_copy(cx, ty) { + return; + } + + match ty.kind { + ty::Adt(def, _) if def.is_union() => return, + + // Some types are not Clone by default but could be cloned “by hand” if necessary + ty::Adt(def, substs) => { + for variant in &def.variants { + for field in &variant.fields { + if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind { + return; + } + } + for subst in substs { + if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() { + if let ty::Param(_) = subst.kind { + return; + } + } + } + } + }, + _ => (), + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); + } +} + +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. +fn check_unsafe_derive_deserialize<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + item: &Item<'_>, + trait_ref: &TraitRef<'_>, + ty: Ty<'tcx>, +) { + fn item_from_def_id<'tcx>(cx: &LateContext<'_, 'tcx>, def_id: DefId) -> &'tcx Item<'tcx> { + let hir_id = cx.tcx.hir().as_local_hir_id(def_id.expect_local()); + cx.tcx.hir().expect_item(hir_id) + } + + fn has_unsafe<'tcx>(cx: &LateContext<'_, 'tcx>, item: &'tcx Item<'_>) -> bool { + let mut visitor = UnsafeVisitor { cx, has_unsafe: false }; + walk_item(&mut visitor, item); + visitor.has_unsafe + } + + if_chain! { + if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); + if let ty::Adt(def, _) = ty.kind; + if def.did.is_local(); + if cx.tcx.inherent_impls(def.did) + .iter() + .map(|imp_did| item_from_def_id(cx, *imp_did)) + .any(|imp| has_unsafe(cx, imp)); + then { + span_lint_and_help( + cx, + UNSAFE_DERIVE_DESERIALIZE, + item.span, + "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", + None, + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html" + ); + } + } +} + +struct UnsafeVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + has_unsafe: bool, +} + +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) { + if self.has_unsafe { + return; + } + + if_chain! { + if let Some(header) = kind.header(); + if let Unsafety::Unsafe = header.unsafety; + then { + self.has_unsafe = true; + } + } + + walk_fn(self, kind, decl, body_id, span, id); + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.has_unsafe { + return; + } + + if let ExprKind::Block(block, _) = expr.kind { + match block.rules { + BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + | BlockCheckMode::PushUnsafeBlock(UnsafeSource::UserProvided) + | BlockCheckMode::PopUnsafeBlock(UnsafeSource::UserProvided) => { + self.has_unsafe = true; + }, + _ => {}, + } + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs new file mode 100644 index 0000000000000..8d1e91f9adbd6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -0,0 +1,525 @@ +use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; +use if_chain::if_chain; +use itertools::Itertools; +use rustc_ast::ast::{AttrKind, Attribute}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::{BytePos, MultiSpan, Span}; +use rustc_span::Pos; +use std::ops::Range; +use url::Url; + +declare_clippy_lint! { + /// **What it does:** Checks for the presence of `_`, `::` or camel-case words + /// outside ticks in documentation. + /// + /// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and + /// camel-case probably indicates some code which should be included between + /// ticks. `_` can also be used for emphasis in markdown, this lint tries to + /// consider that. + /// + /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks + /// for is limited, and there are still false positives. + /// + /// **Examples:** + /// ```rust + /// /// Do something with the foo_bar parameter. See also + /// /// that::other::module::foo. + /// // ^ `foo_bar` and `that::other::module::foo` should be ticked. + /// fn doit(foo_bar: usize) {} + /// ``` + pub DOC_MARKDOWN, + pedantic, + "presence of `_`, `::` or camel-case outside backticks in documentation" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the doc comments of publicly visible + /// unsafe functions and warns if there is no `# Safety` section. + /// + /// **Why is this bad?** Unsafe functions should document their safety + /// preconditions, so that users can be sure they are using them safely. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + ///# type Universe = (); + /// /// This function should really be documented + /// pub unsafe fn start_apocalypse(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + /// + /// At least write a line about safety: + /// + /// ```rust + ///# type Universe = (); + /// /// # Safety + /// /// + /// /// This function should not be called before the horsemen are ready. + /// pub unsafe fn start_apocalypse(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + pub MISSING_SAFETY_DOC, + style, + "`pub unsafe fn` without `# Safety` docs" +} + +declare_clippy_lint! { + /// **What it does:** Checks the doc comments of publicly visible functions that + /// return a `Result` type and warns if there is no `# Errors` section. + /// + /// **Why is this bad?** Documenting the type of errors that can be returned from a + /// function can help callers write code to handle the errors appropriately. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// + /// Since the following function returns a `Result` it has an `# Errors` section in + /// its doc comment: + /// + /// ```rust + ///# use std::io; + /// /// # Errors + /// /// + /// /// Will return `Err` if `filename` does not exist or the user does not have + /// /// permission to read it. + /// pub fn read(filename: String) -> io::Result { + /// unimplemented!(); + /// } + /// ``` + pub MISSING_ERRORS_DOC, + pedantic, + "`pub fn` returns `Result` without `# Errors` in doc comment" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `fn main() { .. }` in doctests + /// + /// **Why is this bad?** The test can be shorter (and likely more readable) + /// if the `fn main()` is left implicit. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ``````rust + /// /// An example of a doctest with a `main()` function + /// /// + /// /// # Examples + /// /// + /// /// ``` + /// /// fn main() { + /// /// // this needs not be in an `fn` + /// /// } + /// /// ``` + /// fn needless_main() { + /// unimplemented!(); + /// } + /// `````` + pub NEEDLESS_DOCTEST_MAIN, + style, + "presence of `fn main() {` in code examples" +} + +#[allow(clippy::module_name_repetitions)] +#[derive(Clone)] +pub struct DocMarkdown { + valid_idents: FxHashSet, + in_trait_impl: bool, +} + +impl DocMarkdown { + pub fn new(valid_idents: FxHashSet) -> Self { + Self { + valid_idents, + in_trait_impl: false, + } + } +} + +impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, NEEDLESS_DOCTEST_MAIN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown { + fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) { + check_attrs(cx, &self.valid_idents, &krate.item.attrs); + } + + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { + let headers = check_attrs(cx, &self.valid_idents, &item.attrs); + match item.kind { + hir::ItemKind::Fn(ref sig, _, body_id) => { + if !(is_entrypoint_fn(cx, cx.tcx.hir().local_def_id(item.hir_id).to_def_id()) + || in_external_macro(cx.tcx.sess, item.span)) + { + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id)); + } + }, + hir::ItemKind::Impl { + of_trait: ref trait_ref, + .. + } => { + self.in_trait_impl = trait_ref.is_some(); + }, + _ => {}, + } + } + + fn check_item_post(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { + if let hir::ItemKind::Impl { .. } = item.kind { + self.in_trait_impl = false; + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) { + let headers = check_attrs(cx, &self.valid_idents, &item.attrs); + if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { + if !in_external_macro(cx.tcx.sess, item.span) { + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, None); + } + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) { + let headers = check_attrs(cx, &self.valid_idents, &item.attrs); + if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { + return; + } + if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind { + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id)); + } + } +} + +fn lint_for_missing_headers<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + hir_id: hir::HirId, + span: impl Into + Copy, + sig: &hir::FnSig<'_>, + headers: DocHeaders, + body_id: Option, +) { + if !cx.access_levels.is_exported(hir_id) { + return; // Private functions do not require doc comments + } + if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { + span_lint( + cx, + MISSING_SAFETY_DOC, + span, + "unsafe function's docs miss `# Safety` section", + ); + } + if !headers.errors { + if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + span_lint( + cx, + MISSING_ERRORS_DOC, + span, + "docs for function returning `Result` missing `# Errors` section", + ); + } else { + if_chain! { + if let Some(body_id) = body_id; + if let Some(future) = cx.tcx.lang_items().future_trait(); + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let mir = cx.tcx.optimized_mir(def_id.to_def_id()); + let ret_ty = mir.return_ty(); + if implements_trait(cx, ret_ty, future, &[]); + if let ty::Opaque(_, subs) = ret_ty.kind; + if let Some(gen) = subs.types().next(); + if let ty::Generator(_, subs, _) = gen.kind; + if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type)); + then { + span_lint( + cx, + MISSING_ERRORS_DOC, + span, + "docs for function returning `Result` missing `# Errors` section", + ); + } + } + } + } +} + +/// Cleanup documentation decoration (`///` and such). +/// +/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or +/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we +/// need to keep track of +/// the spans but this function is inspired from the later. +#[allow(clippy::cast_possible_truncation)] +#[must_use] +pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) { + // one-line comments lose their prefix + const ONELINERS: &[&str] = &["///!", "///", "//!", "//"]; + for prefix in ONELINERS { + if comment.starts_with(*prefix) { + let doc = &comment[prefix.len()..]; + let mut doc = doc.to_owned(); + doc.push('\n'); + return ( + doc.to_owned(), + vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))], + ); + } + } + + if comment.starts_with("/*") { + let doc = &comment[3..comment.len() - 2]; + let mut sizes = vec![]; + let mut contains_initial_stars = false; + for line in doc.lines() { + let offset = line.as_ptr() as usize - comment.as_ptr() as usize; + debug_assert_eq!(offset as u32 as usize, offset); + contains_initial_stars |= line.trim_start().starts_with('*'); + // +1 for the newline + sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32)))); + } + if !contains_initial_stars { + return (doc.to_string(), sizes); + } + // remove the initial '*'s if any + let mut no_stars = String::with_capacity(doc.len()); + for line in doc.lines() { + let mut chars = line.chars(); + while let Some(c) = chars.next() { + if c.is_whitespace() { + no_stars.push(c); + } else { + no_stars.push(if c == '*' { ' ' } else { c }); + break; + } + } + no_stars.push_str(chars.as_str()); + no_stars.push('\n'); + } + return (no_stars, sizes); + } + + panic!("not a doc-comment: {}", comment); +} + +#[derive(Copy, Clone)] +struct DocHeaders { + safety: bool, + errors: bool, +} + +fn check_attrs<'a>(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet, attrs: &'a [Attribute]) -> DocHeaders { + let mut doc = String::new(); + let mut spans = vec![]; + + for attr in attrs { + if let AttrKind::DocComment(ref comment) = attr.kind { + let comment = comment.to_string(); + let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span); + spans.extend_from_slice(¤t_spans); + doc.push_str(&comment); + } else if attr.check_name(sym!(doc)) { + // ignore mix of sugared and non-sugared doc + // don't trigger the safety or errors check + return DocHeaders { + safety: true, + errors: true, + }; + } + } + + let mut current = 0; + for &mut (ref mut offset, _) in &mut spans { + let offset_copy = *offset; + *offset = current; + current += offset_copy; + } + + if doc.is_empty() { + return DocHeaders { + safety: false, + errors: false, + }; + } + + let parser = pulldown_cmark::Parser::new(&doc).into_offset_iter(); + // Iterate over all `Events` and combine consecutive events into one + let events = parser.coalesce(|previous, current| { + use pulldown_cmark::Event::Text; + + let previous_range = previous.1; + let current_range = current.1; + + match (previous.0, current.0) { + (Text(previous), Text(current)) => { + let mut previous = previous.to_string(); + previous.push_str(¤t); + Ok((Text(previous.into()), previous_range)) + }, + (previous, current) => Err(((previous, previous_range), (current, current_range))), + } + }); + check_doc(cx, valid_idents, events, &spans) +} + +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"]; + +fn check_doc<'a, Events: Iterator, Range)>>( + cx: &LateContext<'_, '_>, + valid_idents: &FxHashSet, + events: Events, + spans: &[(usize, Span)], +) -> DocHeaders { + // true if a safety header was found + use pulldown_cmark::CodeBlockKind; + use pulldown_cmark::Event::{ + Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, + }; + use pulldown_cmark::Tag::{CodeBlock, Heading, Link}; + + let mut headers = DocHeaders { + safety: false, + errors: false, + }; + let mut in_code = false; + let mut in_link = None; + let mut in_heading = false; + let mut is_rust = false; + for (event, range) in events { + match event { + Start(CodeBlock(ref kind)) => { + in_code = true; + if let CodeBlockKind::Fenced(lang) = kind { + is_rust = + lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i)); + } + }, + End(CodeBlock(_)) => { + in_code = false; + is_rust = false; + }, + Start(Link(_, url, _)) => in_link = Some(url), + End(Link(..)) => in_link = None, + Start(Heading(_)) => in_heading = true, + End(Heading(_)) => in_heading = false, + Start(_tag) | End(_tag) => (), // We don't care about other tags + Html(_html) => (), // HTML is weird, just ignore it + SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), + FootnoteReference(text) | Text(text) => { + if Some(&text) == in_link.as_ref() { + // Probably a link of the form `` + // Which are represented as a link to "http://example.com" with + // text "http://example.com" by pulldown-cmark + continue; + } + headers.safety |= in_heading && text.trim() == "Safety"; + headers.errors |= in_heading && text.trim() == "Errors"; + let index = match spans.binary_search_by(|c| c.0.cmp(&range.start)) { + Ok(o) => o, + Err(e) => e - 1, + }; + let (begin, span) = spans[index]; + if in_code { + if is_rust { + check_code(cx, &text, span); + } + } else { + // Adjust for the beginning of the current `Event` + let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin)); + + check_text(cx, valid_idents, &text, span); + } + }, + } + } + headers +} + +static LEAVE_MAIN_PATTERNS: &[&str] = &["static", "fn main() {}", "extern crate", "async fn main() {"]; + +fn check_code(cx: &LateContext<'_, '_>, text: &str, span: Span) { + if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { + span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); + } +} + +fn check_text(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet, text: &str, span: Span) { + for word in text.split(|c: char| c.is_whitespace() || c == '\'') { + // Trim punctuation as in `some comment (see foo::bar).` + // ^^ + // Or even as in `_foo bar_` which is emphasized. + let word = word.trim_matches(|c: char| !c.is_alphanumeric()); + + if valid_idents.contains(word) { + continue; + } + + // Adjust for the current word + let offset = word.as_ptr() as usize - text.as_ptr() as usize; + let span = Span::new( + span.lo() + BytePos::from_usize(offset), + span.lo() + BytePos::from_usize(offset + word.len()), + span.ctxt(), + ); + + check_word(cx, word, span); + } +} + +fn check_word(cx: &LateContext<'_, '_>, word: &str, span: Span) { + /// Checks if a string is camel-case, i.e., contains at least two uppercase + /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok). + /// Plurals are also excluded (`IDs` is ok). + fn is_camel_case(s: &str) -> bool { + if s.starts_with(|c: char| c.is_digit(10)) { + return false; + } + + let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s }; + + s.chars().all(char::is_alphanumeric) + && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 + && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0 + } + + fn has_underscore(s: &str) -> bool { + s != "_" && !s.contains("\\_") && s.contains('_') + } + + fn has_hyphen(s: &str) -> bool { + s != "-" && s.contains('-') + } + + 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( + cx, + DOC_MARKDOWN, + span, + "you should put bare URLs between `<`/`>` or make a proper Markdown link", + ); + + return; + } + } + + // We assume that mixed-case words are not meant to be put inside bacticks. (Issue #2343) + if has_underscore(word) && has_hyphen(word) { + return; + } + + if has_underscore(word) || word.contains("::") || is_camel_case(word) { + span_lint( + cx, + DOC_MARKDOWN, + span, + &format!("you should put `{}` between ticks in the documentation", word), + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/double_comparison.rs b/src/tools/clippy/clippy_lints/src/double_comparison.rs new file mode 100644 index 0000000000000..44f85d1ea6e19 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/double_comparison.rs @@ -0,0 +1,95 @@ +//! Lint on unnecessary double comparisons. Some examples: + +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; + +declare_clippy_lint! { + /// **What it does:** Checks for double comparisons that could be simplified to a single expression. + /// + /// + /// **Why is this bad?** Readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// # let y = 2; + /// if x == y || x < y {} + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// # let x = 1; + /// # let y = 2; + /// if x <= y {} + /// ``` + pub DOUBLE_COMPARISONS, + complexity, + "unnecessary double comparisons that can be simplified" +} + +declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]); + +impl<'a, 'tcx> DoubleComparisons { + #[allow(clippy::similar_names)] + fn check_binop(cx: &LateContext<'a, 'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) { + let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) { + (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => { + (lb.node, llhs, lrhs, rb.node, rlhs, rrhs) + }, + _ => return, + }; + let mut spanless_eq = SpanlessEq::new(cx).ignore_fn(); + if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) { + return; + } + macro_rules! lint_double_comparison { + ($op:tt) => {{ + let mut applicability = Applicability::MachineApplicable; + let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability); + let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability); + let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str); + span_lint_and_sugg( + cx, + DOUBLE_COMPARISONS, + span, + "This binary expression can be simplified", + "try", + sugg, + applicability, + ); + }}; + } + #[rustfmt::skip] + match (op, lkind, rkind) { + (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => { + lint_double_comparison!(<=) + }, + (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => { + lint_double_comparison!(>=) + }, + (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => { + lint_double_comparison!(!=) + }, + (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => { + lint_double_comparison!(==) + }, + _ => (), + }; + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DoubleComparisons { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = expr.kind { + Self::check_binop(cx, kind.node, lhs, rhs, expr.span); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs new file mode 100644 index 0000000000000..7f2ff8b9b26f6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/double_parens.rs @@ -0,0 +1,75 @@ +use crate::utils::span_lint; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary double parentheses. + /// + /// **Why is this bad?** This makes code harder to read and might indicate a + /// mistake. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn foo(bar: usize) {} + /// ((0)); + /// foo((0)); + /// ((1, 2)); + /// ``` + pub DOUBLE_PARENS, + complexity, + "Warn on unnecessary double parentheses" +} + +declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); + +impl EarlyLintPass for DoubleParens { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } + + match expr.kind { + ExprKind::Paren(ref in_paren) => match in_paren.kind { + ExprKind::Paren(_) | ExprKind::Tup(_) => { + span_lint( + cx, + DOUBLE_PARENS, + expr.span, + "Consider removing unnecessary double parentheses", + ); + }, + _ => {}, + }, + ExprKind::Call(_, ref params) => { + if params.len() == 1 { + let param = ¶ms[0]; + if let ExprKind::Paren(_) = param.kind { + span_lint( + cx, + DOUBLE_PARENS, + param.span, + "Consider removing unnecessary double parentheses", + ); + } + } + }, + ExprKind::MethodCall(_, ref params) => { + if params.len() == 2 { + let param = ¶ms[1]; + if let ExprKind::Paren(_) = param.kind { + span_lint( + cx, + DOUBLE_PARENS, + param.span, + "Consider removing unnecessary double parentheses", + ); + } + } + }, + _ => {}, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/drop_bounds.rs b/src/tools/clippy/clippy_lints/src/drop_bounds.rs new file mode 100644 index 0000000000000..f496680827913 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/drop_bounds.rs @@ -0,0 +1,69 @@ +use crate::utils::{match_def_path, paths, span_lint}; +use if_chain::if_chain; +use rustc_hir::{GenericBound, GenericParam, WhereBoundPredicate, WherePredicate}; +use rustc_lint::LateLintPass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for generics with `std::ops::Drop` as bounds. + /// + /// **Why is this bad?** `Drop` bounds do not really accomplish anything. + /// A type may have compiler-generated drop glue without implementing the + /// `Drop` trait itself. The `Drop` trait also only has one method, + /// `Drop::drop`, and that function is by fiat not callable in user code. + /// So there is really no use case for using `Drop` in trait bounds. + /// + /// The most likely use case of a drop bound is to distinguish between types + /// that have destructors and types that don't. Combined with specialization, + /// a naive coder would write an implementation that assumed a type could be + /// trivially dropped, then write a specialization for `T: Drop` that actually + /// calls the destructor. Except that doing so is not correct; String, for + /// example, doesn't actually implement Drop, but because String contains a + /// Vec, assuming it can be trivially dropped will leak memory. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo() {} + /// ``` + pub DROP_BOUNDS, + correctness, + "Bounds of the form `T: Drop` are useless" +} + +const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \ + Use `std::mem::needs_drop` to detect if a type has drop glue."; + +declare_lint_pass!(DropBounds => [DROP_BOUNDS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DropBounds { + fn check_generic_param(&mut self, cx: &rustc_lint::LateContext<'a, 'tcx>, p: &'tcx GenericParam<'_>) { + for bound in p.bounds.iter() { + lint_bound(cx, bound); + } + } + fn check_where_predicate(&mut self, cx: &rustc_lint::LateContext<'a, 'tcx>, p: &'tcx WherePredicate<'_>) { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { bounds, .. }) = p { + for bound in *bounds { + lint_bound(cx, bound); + } + } + } +} + +fn lint_bound<'a, 'tcx>(cx: &rustc_lint::LateContext<'a, 'tcx>, bound: &'tcx GenericBound<'_>) { + if_chain! { + if let GenericBound::Trait(t, _) = bound; + if let Some(def_id) = t.trait_ref.path.res.opt_def_id(); + if match_def_path(cx, def_id, &paths::DROP_TRAIT); + then { + span_lint( + cx, + DROP_BOUNDS, + t.span, + DROP_BOUNDS_SUMMARY + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs new file mode 100644 index 0000000000000..9de9056c14029 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -0,0 +1,160 @@ +use crate::utils::{is_copy, match_def_path, paths, qpath_res, span_lint_and_note}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `std::mem::drop` with a reference + /// instead of an owned value. + /// + /// **Why is this bad?** Calling `drop` on a reference will only drop the + /// reference itself, which is a no-op. It will not call the `drop` method (from + /// the `Drop` trait implementation) on the underlying referenced value, which + /// is likely what was intended. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// let mut lock_guard = mutex.lock(); + /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex + /// // still locked + /// operation_that_requires_mutex_to_be_unlocked(); + /// ``` + pub DROP_REF, + correctness, + "calls to `std::mem::drop` with a reference instead of an owned value" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `std::mem::forget` with a reference + /// instead of an owned value. + /// + /// **Why is this bad?** Calling `forget` on a reference will only forget the + /// reference itself, which is a no-op. It will not forget the underlying + /// referenced + /// value, which is likely what was intended. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = Box::new(1); + /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped + /// ``` + pub FORGET_REF, + correctness, + "calls to `std::mem::forget` with a reference instead of an owned value" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `std::mem::drop` with a value + /// that derives the Copy trait + /// + /// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that + /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the + /// value will be copied and moved into the function on invocation. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x: i32 = 42; // i32 implements Copy + /// std::mem::drop(x) // A copy of x is passed to the function, leaving the + /// // original unaffected + /// ``` + pub DROP_COPY, + correctness, + "calls to `std::mem::drop` with a value that implements Copy" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `std::mem::forget` with a value that + /// derives the Copy trait + /// + /// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that + /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the + /// value will be copied and moved into the function on invocation. + /// + /// An alternative, but also valid, explanation is that Copy types do not + /// implement + /// the Drop trait, which means they have no destructors. Without a destructor, + /// there + /// is nothing for `std::mem::forget` to ignore. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x: i32 = 42; // i32 implements Copy + /// std::mem::forget(x) // A copy of x is passed to the function, leaving the + /// // original unaffected + /// ``` + pub FORGET_COPY, + correctness, + "calls to `std::mem::forget` with a value that implements Copy" +} + +const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \ + Dropping a reference does nothing."; +const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \ + Forgetting a reference does nothing."; +const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \ + Dropping a copy leaves the original intact."; +const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ + Forgetting a copy leaves the original intact."; + +declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DropForgetRef { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref path, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = path.kind; + if args.len() == 1; + if let Some(def_id) = qpath_res(cx, qpath, path.hir_id).opt_def_id(); + then { + let lint; + let msg; + let arg = &args[0]; + let arg_ty = cx.tables.expr_ty(arg); + + if let ty::Ref(..) = arg_ty.kind { + if match_def_path(cx, def_id, &paths::DROP) { + lint = DROP_REF; + msg = DROP_REF_SUMMARY.to_string(); + } else if match_def_path(cx, def_id, &paths::MEM_FORGET) { + lint = FORGET_REF; + msg = FORGET_REF_SUMMARY.to_string(); + } else { + return; + } + span_lint_and_note(cx, + lint, + expr.span, + &msg, + Some(arg.span), + &format!("argument has type `{}`", arg_ty)); + } else if is_copy(cx, arg_ty) { + if match_def_path(cx, def_id, &paths::DROP) { + lint = DROP_COPY; + msg = DROP_COPY_SUMMARY.to_string(); + } else if match_def_path(cx, def_id, &paths::MEM_FORGET) { + lint = FORGET_COPY; + msg = FORGET_COPY_SUMMARY.to_string(); + } else { + return; + } + span_lint_and_note(cx, + lint, + expr.span, + &msg, + Some(arg.span), + &format!("argument has type {}", arg_ty)); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/duration_subsec.rs new file mode 100644 index 0000000000000..b35a8facf8b99 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/duration_subsec.rs @@ -0,0 +1,65 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +use crate::consts::{constant, Constant}; +use crate::utils::paths; +use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty}; + +declare_clippy_lint! { + /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds + /// from other `Duration` methods. + /// + /// **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or + /// `Duration::subsec_millis()` than to calculate them. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::time::Duration; + /// let dur = Duration::new(5, 0); + /// let _micros = dur.subsec_nanos() / 1_000; + /// let _millis = dur.subsec_nanos() / 1_000_000; + /// ``` + pub DURATION_SUBSEC, + complexity, + "checks for calculation of subsecond microseconds or milliseconds" +} + +declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DurationSubsec { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind; + if let ExprKind::MethodCall(ref method_path, _ , ref args) = left.kind; + if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::DURATION); + if let Some((Constant::Int(divisor), _)) = constant(cx, cx.tables, right); + then { + let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) { + ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", + ("subsec_nanos", 1_000) => "subsec_micros", + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + DURATION_SUBSEC, + expr.span, + &format!("Calling `{}()` is more concise than this calculation", suggested_fn), + "try", + format!( + "{}.{}()", + snippet_with_applicability(cx, args[0].span, "_", &mut applicability), + suggested_fn + ), + applicability, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs new file mode 100644 index 0000000000000..95123e6ff6fe2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs @@ -0,0 +1,72 @@ +//! Lint on if expressions with an else if, but without a final else branch. + +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::span_lint_and_help; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of if expressions with an `else if` branch, + /// but without a final `else` branch. + /// + /// **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn a() {} + /// # fn b() {} + /// # let x: i32 = 1; + /// if x.is_positive() { + /// a(); + /// } else if x.is_negative() { + /// b(); + /// } + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # fn a() {} + /// # fn b() {} + /// # let x: i32 = 1; + /// if x.is_positive() { + /// a(); + /// } else if x.is_negative() { + /// b(); + /// } else { + /// // We don't care about zero. + /// } + /// ``` + pub ELSE_IF_WITHOUT_ELSE, + restriction, + "`if` expression with an `else if`, but without a final `else` branch" +} + +declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); + +impl EarlyLintPass for ElseIfWithoutElse { + fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) { + if in_external_macro(cx.sess(), item.span) { + return; + } + + while let ExprKind::If(_, _, Some(ref els)) = item.kind { + if let ExprKind::If(_, _, None) = els.kind { + span_lint_and_help( + cx, + ELSE_IF_WITHOUT_ELSE, + els.span, + "`if` expression with an `else if`, but without a final `else`", + None, + "add an `else` block here", + ); + } + + item = els; + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs new file mode 100644 index 0000000000000..3bfef6f4bed12 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs @@ -0,0 +1,60 @@ +//! lint when there is an enum with no variants + +use crate::utils::span_lint_and_help; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `enum`s with no variants. + /// + /// **Why is this bad?** If you want to introduce a type which + /// can't be instantiated, you should use `!` (the never type), + /// or a wrapper around it, because `!` has more extensive + /// compiler support (type inference, etc...) and wrappers + /// around it are the conventional way to define an uninhabited type. + /// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html) + /// + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// Bad: + /// ```rust + /// enum Test {} + /// ``` + /// + /// Good: + /// ```rust + /// #![feature(never_type)] + /// + /// struct Test(!); + /// ``` + pub EMPTY_ENUM, + pedantic, + "enum with no variants" +} + +declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum { + fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { + let did = cx.tcx.hir().local_def_id(item.hir_id); + if let ItemKind::Enum(..) = item.kind { + let ty = cx.tcx.type_of(did); + let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); + if adt.variants.is_empty() { + span_lint_and_help( + cx, + EMPTY_ENUM, + item.span, + "enum with no variants", + None, + "consider using the uninhabited type `!` (never type) or a wrapper \ + around it to introduce a type which can't be instantiated", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs new file mode 100644 index 0000000000000..7b332c761a0c4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -0,0 +1,187 @@ +use crate::utils::SpanlessEq; +use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt}; +use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` + /// or `BTreeMap`. + /// + /// **Why is this bad?** Using `entry` is more efficient. + /// + /// **Known problems:** Some false negatives, eg.: + /// ```rust + /// # use std::collections::HashMap; + /// # let mut map = HashMap::new(); + /// # let v = 1; + /// # let k = 1; + /// if !map.contains_key(&k) { + /// map.insert(k.clone(), v); + /// } + /// ``` + /// + /// **Example:** + /// ```rust + /// # use std::collections::HashMap; + /// # let mut map = HashMap::new(); + /// # let k = 1; + /// # let v = 1; + /// if !map.contains_key(&k) { + /// map.insert(k, v); + /// } + /// ``` + /// can both be rewritten as: + /// ```rust + /// # use std::collections::HashMap; + /// # let mut map = HashMap::new(); + /// # let k = 1; + /// # let v = 1; + /// map.entry(k).or_insert(v); + /// ``` + pub MAP_ENTRY, + perf, + "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`" +} + +declare_lint_pass!(HashMapPass => [MAP_ENTRY]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HashMapPass { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let Some((ref check, ref then_block, ref else_block)) = higher::if_block(&expr) { + if let ExprKind::Unary(UnOp::UnNot, ref check) = check.kind { + if let Some((ty, map, key)) = check_cond(cx, check) { + // in case of `if !m.contains_key(&k) { m.insert(k, v); }` + // we can give a better error message + let sole_expr = { + else_block.is_none() + && if let ExprKind::Block(ref then_block, _) = then_block.kind { + (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1 + } else { + true + } + // XXXManishearth we can also check for if/else blocks containing `None`. + }; + + let mut visitor = InsertVisitor { + cx, + span: expr.span, + ty, + map, + key, + sole_expr, + }; + + walk_expr(&mut visitor, &**then_block); + } + } else if let Some(ref else_block) = *else_block { + if let Some((ty, map, key)) = check_cond(cx, check) { + let mut visitor = InsertVisitor { + cx, + span: expr.span, + ty, + map, + key, + sole_expr: false, + }; + + walk_expr(&mut visitor, else_block); + } + } + } + } +} + +fn check_cond<'a, 'tcx, 'b>( + cx: &'a LateContext<'a, 'tcx>, + check: &'b Expr<'b>, +) -> Option<(&'static str, &'b Expr<'b>, &'b Expr<'b>)> { + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref params) = check.kind; + if params.len() >= 2; + if path.ident.name == sym!(contains_key); + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind; + then { + let map = ¶ms[0]; + let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(map)); + + return if match_type(cx, obj_ty, &paths::BTREEMAP) { + Some(("BTreeMap", map, key)) + } + else if is_type_diagnostic_item(cx, obj_ty, sym!(hashmap_type)) { + Some(("HashMap", map, key)) + } + else { + None + }; + } + } + + None +} + +struct InsertVisitor<'a, 'tcx, 'b> { + cx: &'a LateContext<'a, 'tcx>, + span: Span, + ty: &'static str, + map: &'b Expr<'b>, + key: &'b Expr<'b>, + sole_expr: bool, +} + +impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref params) = expr.kind; + if params.len() == 3; + if path.ident.name == sym!(insert); + if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]); + if SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1]); + if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span); + then { + span_lint_and_then(self.cx, MAP_ENTRY, self.span, + &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| { + if self.sole_expr { + let mut app = Applicability::MachineApplicable; + let help = format!("{}.entry({}).or_insert({});", + snippet_with_applicability(self.cx, self.map.span, "map", &mut app), + snippet_with_applicability(self.cx, params[1].span, "..", &mut app), + snippet_with_applicability(self.cx, params[2].span, "..", &mut app)); + + diag.span_suggestion( + self.span, + "consider using", + help, + Applicability::MachineApplicable, // snippet + ); + } + else { + let help = format!("consider using `{}.entry({})`", + snippet(self.cx, self.map.span, "map"), + snippet(self.cx, params[1].span, "..")); + + diag.span_label( + self.span, + &help, + ); + } + }); + } + } + + if !self.sole_expr { + walk_expr(self, expr); + } + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs new file mode 100644 index 0000000000000..a1fed3fb6e205 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -0,0 +1,82 @@ +//! lint on C-like enums that are `repr(isize/usize)` and have values that +//! don't fit into an `i32` + +use crate::consts::{miri_to_const, Constant}; +use crate::utils::span_lint; +use rustc_ast::ast::{IntTy, UintTy}; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::util::IntTypeExt; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::convert::TryFrom; + +declare_clippy_lint! { + /// **What it does:** Checks for C-like enumerations that are + /// `repr(isize/usize)` and have values that don't fit into an `i32`. + /// + /// **Why is this bad?** This will truncate the variant value on 32 bit + /// architectures, but works fine on 64 bit. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # #[cfg(target_pointer_width = "64")] + /// #[repr(usize)] + /// enum NonPortable { + /// X = 0x1_0000_0000, + /// Y = 0, + /// } + /// ``` + pub ENUM_CLIKE_UNPORTABLE_VARIANT, + correctness, + "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`" +} + +declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant { + #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)] + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if cx.tcx.data_layout.pointer_size.bits() != 64 { + return; + } + 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); + let mut ty = cx.tcx.type_of(def_id.to_def_id()); + let constant = cx + .tcx + .const_eval_poly(def_id.to_def_id()) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); + if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) { + if let ty::Adt(adt, _) = ty.kind { + if adt.is_enum() { + ty = adt.repr.discr_type().to_ty(cx.tcx); + } + } + match ty.kind { + ty::Int(IntTy::Isize) => { + let val = ((val as i128) << 64) >> 64; + if i32::try_from(val).is_ok() { + continue; + } + }, + ty::Uint(UintTy::Usize) if val > u128::from(u32::max_value()) => {}, + _ => continue, + } + span_lint( + cx, + ENUM_CLIKE_UNPORTABLE_VARIANT, + var.span, + "Clike enum variant discriminant is not portable to 32-bit targets", + ); + }; + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/enum_variants.rs new file mode 100644 index 0000000000000..a5871cf0cd4dd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs @@ -0,0 +1,305 @@ +//! lint on enum variants that are prefixed or suffixed by the same characters + +use crate::utils::{camel_case, is_present_in_source}; +use crate::utils::{span_lint, span_lint_and_help}; +use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// **What it does:** Detects enumeration variants that are prefixed or suffixed + /// by the same characters. + /// + /// **Why is this bad?** Enumeration variant names should specify their variant, + /// not repeat the enumeration name. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// enum Cake { + /// BlackForestCake, + /// HummingbirdCake, + /// BattenbergCake, + /// } + /// ``` + pub ENUM_VARIANT_NAMES, + style, + "enums where all variants share a prefix/postfix" +} + +declare_clippy_lint! { + /// **What it does:** Detects enumeration variants that are prefixed or suffixed + /// by the same characters. + /// + /// **Why is this bad?** Enumeration variant names should specify their variant, + /// not repeat the enumeration name. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// enum Cake { + /// BlackForestCake, + /// HummingbirdCake, + /// BattenbergCake, + /// } + /// ``` + pub PUB_ENUM_VARIANT_NAMES, + pedantic, + "enums where all variants share a prefix/postfix" +} + +declare_clippy_lint! { + /// **What it does:** Detects type names that are prefixed or suffixed by the + /// containing module's name. + /// + /// **Why is this bad?** It requires the user to type the module name twice. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// mod cake { + /// struct BlackForestCake; + /// } + /// ``` + pub MODULE_NAME_REPETITIONS, + pedantic, + "type names prefixed/postfixed with their containing module's name" +} + +declare_clippy_lint! { + /// **What it does:** Checks for modules that have the same name as their + /// parent module + /// + /// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and + /// again `mod foo { .. + /// }` in `foo.rs`. + /// The expectation is that items inside the inner `mod foo { .. }` are then + /// available + /// through `foo::x`, but they are only available through + /// `foo::foo::x`. + /// If this is done on purpose, it would be better to choose a more + /// representative module name. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// // lib.rs + /// mod foo; + /// // foo.rs + /// mod foo { + /// ... + /// } + /// ``` + pub MODULE_INCEPTION, + style, + "modules that have the same name as their parent module" +} + +pub struct EnumVariantNames { + modules: Vec<(Symbol, String)>, + threshold: u64, +} + +impl EnumVariantNames { + #[must_use] + pub fn new(threshold: u64) -> Self { + Self { + modules: Vec::new(), + threshold, + } + } +} + +impl_lint_pass!(EnumVariantNames => [ + ENUM_VARIANT_NAMES, + PUB_ENUM_VARIANT_NAMES, + MODULE_NAME_REPETITIONS, + MODULE_INCEPTION +]); + +/// Returns the number of chars that match from the start +#[must_use] +fn partial_match(pre: &str, name: &str) -> usize { + let mut name_iter = name.chars(); + let _ = name_iter.next_back(); // make sure the name is never fully matched + pre.chars().zip(name_iter).take_while(|&(l, r)| l == r).count() +} + +/// Returns the number of chars that match from the end +#[must_use] +fn partial_rmatch(post: &str, name: &str) -> usize { + let mut name_iter = name.chars(); + let _ = name_iter.next(); // make sure the name is never fully matched + post.chars() + .rev() + .zip(name_iter.rev()) + .take_while(|&(l, r)| l == r) + .count() +} + +fn check_variant( + cx: &EarlyContext<'_>, + threshold: u64, + def: &EnumDef, + item_name: &str, + item_name_chars: usize, + span: Span, + lint: &'static Lint, +) { + if (def.variants.len() as u64) < threshold { + return; + } + for var in &def.variants { + let name = var.ident.name.as_str(); + if partial_match(item_name, &name) == item_name_chars + && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) + && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) + { + span_lint(cx, lint, var.span, "Variant name starts with the enum's name"); + } + if partial_rmatch(item_name, &name) == item_name_chars { + span_lint(cx, lint, var.span, "Variant name ends with the enum's name"); + } + } + let first = &def.variants[0].ident.name.as_str(); + let mut pre = &first[..camel_case::until(&*first)]; + let mut post = &first[camel_case::from(&*first)..]; + for var in &def.variants { + let name = var.ident.name.as_str(); + + let pre_match = partial_match(pre, &name); + pre = &pre[..pre_match]; + let pre_camel = camel_case::until(pre); + pre = &pre[..pre_camel]; + while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() { + if next.is_numeric() { + return; + } + if next.is_lowercase() { + let last = pre.len() - last.len_utf8(); + let last_camel = camel_case::until(&pre[..last]); + pre = &pre[..last_camel]; + } else { + break; + } + } + + let post_match = partial_rmatch(post, &name); + let post_end = post.len() - post_match; + post = &post[post_end..]; + let post_camel = camel_case::from(post); + post = &post[post_camel..]; + } + let (what, value) = match (pre.is_empty(), post.is_empty()) { + (true, true) => return, + (false, _) => ("pre", pre), + (true, false) => ("post", post), + }; + span_lint_and_help( + cx, + lint, + span, + &format!("All variants have the same {}fix: `{}`", what, value), + None, + &format!( + "remove the {}fixes and use full paths to \ + the variants instead of glob imports", + what + ), + ); +} + +#[must_use] +fn to_camel_case(item_name: &str) -> String { + let mut s = String::new(); + let mut up = true; + for c in item_name.chars() { + if c.is_uppercase() { + // we only turn snake case text into CamelCase + return item_name.to_string(); + } + if c == '_' { + up = true; + continue; + } + if up { + up = false; + s.extend(c.to_uppercase()); + } else { + s.push(c); + } + } + s +} + +impl EarlyLintPass for EnumVariantNames { + fn check_item_post(&mut self, _cx: &EarlyContext<'_>, _item: &Item) { + let last = self.modules.pop(); + assert!(last.is_some()); + } + + #[allow(clippy::similar_names)] + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let item_name = item.ident.name.as_str(); + let item_name_chars = item_name.chars().count(); + let item_camel = to_camel_case(&item_name); + if !item.span.from_expansion() && is_present_in_source(cx, item.span) { + if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { + // constants don't have surrounding modules + if !mod_camel.is_empty() { + if mod_name == &item.ident.name { + if let ItemKind::Mod(..) = item.kind { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } + } + if item.vis.node.is_pub() { + let matching = partial_match(mod_camel, &item_camel); + let rmatching = partial_rmatch(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 == nchars { + match item_camel.chars().nth(nchars) { + Some(c) if is_word_beginning(c) => span_lint( + cx, + MODULE_NAME_REPETITIONS, + item.span, + "item name starts with its containing module's name", + ), + _ => (), + } + } + if rmatching == nchars { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + item.span, + "item name ends with its containing module's name", + ); + } + } + } + } + } + if let ItemKind::Enum(ref def, _) = item.kind { + let lint = match item.vis.node { + VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES, + _ => ENUM_VARIANT_NAMES, + }; + check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint); + } + self.modules.push((item.ident.name, item_camel)); + } +} diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs new file mode 100644 index 0000000000000..4e1c1f131405f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/eq_op.rs @@ -0,0 +1,229 @@ +use crate::utils::{ + implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq, +}; +use rustc_errors::Applicability; +use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for equal operands to comparison, logical and + /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`, + /// `||`, `&`, `|`, `^`, `-` and `/`). + /// + /// **Why is this bad?** This is usually just a typo or a copy and paste error. + /// + /// **Known problems:** False negatives: We had some false positives regarding + /// calls (notably [racer](https://github.com/phildawes/racer) had one instance + /// of `x.pop() && x.pop()`), so we removed matching any function or method + /// calls. We may introduce a whitelist of known pure functions in the future. + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// if x + 1 == x + 1 {} + /// ``` + pub EQ_OP, + correctness, + "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)" +} + +declare_clippy_lint! { + /// **What it does:** Checks for arguments to `==` which have their address + /// taken to satisfy a bound + /// and suggests to dereference the other argument instead + /// + /// **Why is this bad?** It is more idiomatic to dereference the other argument. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```ignore + /// &x == y + /// ``` + pub OP_REF, + style, + "taking a reference to satisfy the type constraints on `==`" +} + +declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp { + #[allow(clippy::similar_names, clippy::too_many_lines)] + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Binary(op, ref left, ref right) = e.kind { + if e.span.from_expansion() { + return; + } + let macro_with_not_op = |expr_kind: &ExprKind<'_>| { + if let ExprKind::Unary(_, ref expr) = *expr_kind { + in_macro(expr.span) + } else { + false + } + }; + if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { + return; + } + if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) { + span_lint( + cx, + EQ_OP, + e.span, + &format!("equal expressions as operands to `{}`", op.node.as_str()), + ); + return; + } + let (trait_id, requires_ref) = match op.node { + BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false), + BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false), + BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false), + BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false), + BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false), + // don't lint short circuiting ops + BinOpKind::And | BinOpKind::Or => return, + BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false), + BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false), + BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false), + BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false), + BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false), + BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true), + BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => { + (cx.tcx.lang_items().partial_ord_trait(), true) + }, + }; + if let Some(trait_id) = trait_id { + #[allow(clippy::match_same_arms)] + match (&left.kind, &right.kind) { + // do not suggest to dereference literals + (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, + // &foo == &bar + (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + let lty = cx.tables.expr_ty(l); + let rty = cx.tables.expr_ty(r); + let lcpy = is_copy(cx, lty); + let rcpy = is_copy(cx, rty); + // either operator autorefs or both args are copyable + if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) { + span_lint_and_then( + cx, + OP_REF, + e.span, + "needlessly taken reference of both operands", + |diag| { + let lsnip = snippet(cx, l.span, "...").to_string(); + let rsnip = snippet(cx, r.span, "...").to_string(); + multispan_sugg( + diag, + "use the values directly", + vec![(left.span, lsnip), (right.span, rsnip)], + ); + }, + ) + } else if lcpy + && !rcpy + && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()]) + { + span_lint_and_then( + cx, + OP_REF, + e.span, + "needlessly taken reference of left operand", + |diag| { + let lsnip = snippet(cx, l.span, "...").to_string(); + diag.span_suggestion( + left.span, + "use the left value directly", + lsnip, + Applicability::MaybeIncorrect, // FIXME #2597 + ); + }, + ) + } else if !lcpy + && rcpy + && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()]) + { + span_lint_and_then( + cx, + OP_REF, + e.span, + "needlessly taken reference of right operand", + |diag| { + let rsnip = snippet(cx, r.span, "...").to_string(); + diag.span_suggestion( + right.span, + "use the right value directly", + rsnip, + Applicability::MaybeIncorrect, // FIXME #2597 + ); + }, + ) + } + }, + // &foo == bar + (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => { + let lty = cx.tables.expr_ty(l); + let lcpy = is_copy(cx, lty); + if (requires_ref || lcpy) + && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()]) + { + span_lint_and_then( + cx, + OP_REF, + e.span, + "needlessly taken reference of left operand", + |diag| { + let lsnip = snippet(cx, l.span, "...").to_string(); + diag.span_suggestion( + left.span, + "use the left value directly", + lsnip, + Applicability::MaybeIncorrect, // FIXME #2597 + ); + }, + ) + } + }, + // foo == &bar + (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + let rty = cx.tables.expr_ty(r); + let rcpy = is_copy(cx, rty); + if (requires_ref || rcpy) + && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()]) + { + span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| { + let rsnip = snippet(cx, r.span, "...").to_string(); + diag.span_suggestion( + right.span, + "use the right value directly", + rsnip, + Applicability::MaybeIncorrect, // FIXME #2597 + ); + }) + } + }, + _ => {}, + } + } + } + } +} + +fn is_valid_operator(op: BinOp) -> bool { + match op.node { + BinOpKind::Sub + | BinOpKind::Div + | BinOpKind::Eq + | BinOpKind::Lt + | BinOpKind::Le + | BinOpKind::Gt + | BinOpKind::Ge + | BinOpKind::Ne + | BinOpKind::And + | BinOpKind::Or + | BinOpKind::BitXor + | BinOpKind::BitAnd + | BinOpKind::BitOr => true, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/erasing_op.rs b/src/tools/clippy/clippy_lints/src/erasing_op.rs new file mode 100644 index 0000000000000..3ff0506e28d00 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/erasing_op.rs @@ -0,0 +1,59 @@ +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +use crate::consts::{constant_simple, Constant}; +use crate::utils::span_lint; + +declare_clippy_lint! { + /// **What it does:** Checks for erasing operations, e.g., `x * 0`. + /// + /// **Why is this bad?** The whole expression can be replaced by zero. + /// This is most likely not the intended outcome and should probably be + /// corrected + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = 1; + /// 0 / x; + /// 0 * x; + /// x & 0; + /// ``` + pub ERASING_OP, + correctness, + "using erasing operations, e.g., `x * 0` or `y & 0`" +} + +declare_lint_pass!(ErasingOp => [ERASING_OP]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ErasingOp { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind { + match cmp.node { + BinOpKind::Mul | BinOpKind::BitAnd => { + check(cx, left, e.span); + check(cx, right, e.span); + }, + BinOpKind::Div => check(cx, left, e.span), + _ => (), + } + } + } +} + +fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, span: Span) { + if let Some(Constant::Int(0)) = constant_simple(cx, cx.tables, e) { + span_lint( + cx, + ERASING_OP, + span, + "this operation will always return zero. This is likely not the intended outcome", + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs new file mode 100644 index 0000000000000..1ec60a0e6e67a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -0,0 +1,164 @@ +use rustc_hir::intravisit; +use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; +use rustc_target::abi::LayoutOf; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; + +use crate::utils::span_lint; + +#[derive(Copy, Clone)] +pub struct BoxedLocal { + pub too_large_for_stack: u64, +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `Box` where an unboxed `T` would + /// work fine. + /// + /// **Why is this bad?** This is an unnecessary allocation, and bad for + /// performance. It is only necessary to allocate if you wish to move the box + /// into something. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn foo(bar: usize) {} + /// let x = Box::new(1); + /// foo(*x); + /// println!("{}", *x); + /// ``` + pub BOXED_LOCAL, + perf, + "using `Box` where unnecessary" +} + +fn is_non_trait_box(ty: Ty<'_>) -> bool { + ty.is_box() && !ty.boxed_ty().is_trait() +} + +struct EscapeDelegate<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + set: HirIdSet, + too_large_for_stack: u64, +} + +impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: intravisit::FnKind<'tcx>, + _: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + hir_id: HirId, + ) { + // If the method is an impl for a trait, don't warn. + let parent_id = cx.tcx.hir().get_parent_item(hir_id); + let parent_node = cx.tcx.hir().find(parent_id); + + if let Some(Node::Item(item)) = parent_node { + if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + } + + let mut v = EscapeDelegate { + cx, + set: HirIdSet::default(), + too_large_for_stack: self.too_large_for_stack, + }; + + let fn_def_id = cx.tcx.hir().local_def_id(hir_id); + cx.tcx.infer_ctxt().enter(|infcx| { + ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body); + }); + + for node in v.set { + span_lint( + cx, + BOXED_LOCAL, + cx.tcx.hir().span(node), + "local variable doesn't need to be boxed here", + ); + } + } +} + +// TODO: Replace with Map::is_argument(..) when it's fixed +fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { + match map.find(id) { + Some(Node::Binding(_)) => (), + _ => return false, + } + + match map.find(map.get_parent_node(id)) { + Some(Node::Param(_)) => true, + _ => false, + } +} + +impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { + fn consume(&mut self, cmt: &Place<'tcx>, mode: ConsumeMode) { + if cmt.projections.is_empty() { + if let PlaceBase::Local(lid) = cmt.base { + if let ConsumeMode::Move = mode { + // moved out or in. clearly can't be localized + self.set.remove(&lid); + } + let map = &self.cx.tcx.hir(); + if let Some(Node::Binding(_)) = map.find(cmt.hir_id) { + if self.set.contains(&lid) { + // let y = x where x is known + // remove x, insert y + self.set.insert(cmt.hir_id); + self.set.remove(&lid); + } + } + } + } + } + + fn borrow(&mut self, cmt: &Place<'tcx>, _: ty::BorrowKind) { + if cmt.projections.is_empty() { + if let PlaceBase::Local(lid) = cmt.base { + self.set.remove(&lid); + } + } + } + + fn mutate(&mut self, cmt: &Place<'tcx>) { + if cmt.projections.is_empty() { + let map = &self.cx.tcx.hir(); + if is_argument(*map, cmt.hir_id) { + // Skip closure arguments + let parent_id = map.get_parent_node(cmt.hir_id); + if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) { + return; + } + + if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) { + self.set.insert(cmt.hir_id); + } + return; + } + } + } +} + +impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { + fn is_large_box(&self, ty: Ty<'tcx>) -> bool { + // Large types need to be boxed to avoid stack overflows. + if ty.is_box() { + self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack + } else { + false + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs new file mode 100644 index 0000000000000..e3e1136b67693 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -0,0 +1,227 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{ + implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then, + type_is_unsafe_function, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for closures which just call another function where + /// the function can be called directly. `unsafe` functions or calls where types + /// get adjusted are ignored. + /// + /// **Why is this bad?** Needlessly creating a closure adds code for no benefit + /// and gives the optimizer more work. + /// + /// **Known problems:** If creating the closure inside the closure has a side- + /// effect then moving the closure creation out will change when that side- + /// effect runs. + /// See rust-lang/rust-clippy#1439 for more details. + /// + /// **Example:** + /// ```rust,ignore + /// xs.map(|x| foo(x)) + /// ``` + /// where `foo(_)` is a plain function that takes the exact argument type of + /// `x`. + pub REDUNDANT_CLOSURE, + style, + "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)" +} + +declare_clippy_lint! { + /// **What it does:** Checks for closures which only invoke a method on the closure + /// argument and can be replaced by referencing the method directly. + /// + /// **Why is this bad?** It's unnecessary to create the closure. + /// + /// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002, + /// rust-lang/rust-clippy#3942 + /// + /// + /// **Example:** + /// ```rust,ignore + /// Some('a').map(|s| s.to_uppercase()); + /// ``` + /// may be rewritten as + /// ```rust,ignore + /// Some('a').map(char::to_uppercase); + /// ``` + pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + pedantic, + "redundant closures for method calls" +} + +declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaReduction { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + match expr.kind { + ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => { + for arg in args { + check_closure(cx, arg) + } + }, + _ => (), + } + } +} + +fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + + if_chain!( + if let ExprKind::Call(ref caller, ref args) = ex.kind; + + if let ExprKind::Path(_) = caller.kind; + + // Not the same number of arguments, there is no way the closure is the same as the function return; + if args.len() == decl.inputs.len(); + + // Are the expression or the arguments type-adjusted? Then we need the closure + if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg))); + + let fn_ty = cx.tables.expr_ty(caller); + + if matches!(fn_ty.kind, ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _)); + + if !type_is_unsafe_function(cx, fn_ty); + + if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter()); + + then { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |diag| { + if let Some(snippet) = snippet_opt(cx, caller.span) { + diag.span_suggestion( + expr.span, + "remove closure as shown", + snippet, + Applicability::MachineApplicable, + ); + } + }); + } + ); + + if_chain!( + if let ExprKind::MethodCall(ref path, _, ref args) = ex.kind; + + // Not the same number of arguments, there is no way the closure is the same as the function return; + if args.len() == decl.inputs.len(); + + // Are the expression or the arguments type-adjusted? Then we need the closure + if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg))); + + let method_def_id = cx.tables.type_dependent_def_id(ex.hir_id).unwrap(); + if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id)); + + if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter()); + + if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]); + + then { + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + expr.span, + "redundant closure found", + "remove closure as shown", + format!("{}::{}", name, path.ident.name), + Applicability::MachineApplicable, + ); + } + ); + } +} + +/// Tries to determine the type for universal function call to be used instead of the closure +fn get_ufcs_type_name(cx: &LateContext<'_, '_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option { + let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0]; + let actual_type_of_self = &cx.tables.node_type(self_arg.hir_id); + + if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) { + if match_borrow_depth(expected_type_of_self, &actual_type_of_self) + && implements_trait(cx, actual_type_of_self, trait_id, &[]) + { + return Some(cx.tcx.def_path_str(trait_id)); + } + } + + cx.tcx.impl_of_method(method_def_id).and_then(|_| { + //a type may implicitly implement other type's methods (e.g. Deref) + if match_types(expected_type_of_self, &actual_type_of_self) { + return Some(get_type_name(cx, &actual_type_of_self)); + } + None + }) +} + +fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { + match (&lhs.kind, &rhs.kind) { + (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), + (l, r) => match (l, r) { + (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false, + (_, _) => true, + }, + } +} + +fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { + match (&lhs.kind, &rhs.kind) { + (ty::Bool, ty::Bool) + | (ty::Char, ty::Char) + | (ty::Int(_), ty::Int(_)) + | (ty::Uint(_), ty::Uint(_)) + | (ty::Str, ty::Str) => true, + (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2), + (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2), + (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2, + (_, _) => false, + } +} + +fn get_type_name(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> String { + match ty.kind { + ty::Adt(t, _) => cx.tcx.def_path_str(t.did), + ty::Ref(_, r, _) => get_type_name(cx, &r), + _ => ty.to_string(), + } +} + +fn compare_inputs( + closure_inputs: &mut dyn Iterator>, + call_args: &mut dyn Iterator>, +) -> bool { + for (closure_input, function_arg) in closure_inputs.zip(call_args) { + if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind { + // XXXManishearth Should I be checking the binding mode here? + if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.kind { + if p.segments.len() != 1 { + // If it's a proper path, it can't be a local variable + return false; + } + if p.segments[0].ident.name != ident.name { + // The two idents should be the same + return false; + } + } else { + return false; + } + } else { + return false; + } + } + true +} diff --git a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs new file mode 100644 index 0000000000000..5206266ccf2a6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs @@ -0,0 +1,355 @@ +use crate::utils::{get_parent_expr, span_lint, span_lint_and_note}; +use if_chain::if_chain; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{def, BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for a read and a write to the same variable where + /// whether the read occurs before or after the write depends on the evaluation + /// order of sub-expressions. + /// + /// **Why is this bad?** It is often confusing to read. In addition, the + /// sub-expression evaluation order for Rust is not well documented. + /// + /// **Known problems:** Code which intentionally depends on the evaluation + /// order, or which is correct for any evaluation order. + /// + /// **Example:** + /// ```rust + /// let mut x = 0; + /// let a = { + /// x = 1; + /// 1 + /// } + x; + /// // Unclear whether a is 1 or 2. + /// ``` + pub EVAL_ORDER_DEPENDENCE, + complexity, + "whether a variable read occurs before a write depends on sub-expression evaluation order" +} + +declare_clippy_lint! { + /// **What it does:** Checks for diverging calls that are not match arms or + /// statements. + /// + /// **Why is this bad?** It is often confusing to read. In addition, the + /// sub-expression evaluation order for Rust is not well documented. + /// + /// **Known problems:** Someone might want to use `some_bool || panic!()` as a + /// shorthand. + /// + /// **Example:** + /// ```rust,no_run + /// # fn b() -> bool { true } + /// # fn c() -> bool { true } + /// let a = b() || panic!() || c(); + /// // `c()` is dead, `panic!()` is only called if `b()` returns `false` + /// let x = (a, b, c, panic!()); + /// // can simply be replaced by `panic!()` + /// ``` + pub DIVERGING_SUB_EXPRESSION, + complexity, + "whether an expression contains a diverging sub expression" +} + +declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EvalOrderDependence { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + // Find a write to a local variable. + match expr.kind { + ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => { + if let ExprKind::Path(ref qpath) = lhs.kind { + if let QPath::Resolved(_, ref path) = *qpath { + if path.segments.len() == 1 { + if let def::Res::Local(var) = cx.tables.qpath_res(qpath, lhs.hir_id) { + let mut visitor = ReadVisitor { + cx, + var, + write_expr: expr, + last_expr: expr, + }; + check_for_unsequenced_reads(&mut visitor); + } + } + } + } + }, + _ => {}, + } + } + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + match stmt.kind { + StmtKind::Local(ref local) => { + if let Local { init: Some(ref e), .. } = **local { + DivergenceVisitor { cx }.visit_expr(e); + } + }, + StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => DivergenceVisitor { cx }.maybe_walk_expr(e), + StmtKind::Item(..) => {}, + } + } +} + +struct DivergenceVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { + fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) { + match e.kind { + ExprKind::Closure(..) => {}, + ExprKind::Match(ref e, arms, _) => { + self.visit_expr(e); + for arm in arms { + if let Some(Guard::If(if_expr)) = arm.guard { + self.visit_expr(if_expr) + } + // make sure top level arm expressions aren't linted + self.maybe_walk_expr(&*arm.body); + } + }, + _ => walk_expr(self, e), + } + } + fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { + span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges"); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + match e.kind { + ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e), + ExprKind::Call(ref func, _) => { + let typ = self.cx.tables.expr_ty(func); + match typ.kind { + ty::FnDef(..) | ty::FnPtr(_) => { + let sig = typ.fn_sig(self.cx.tcx); + if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind { + self.report_diverging_sub_expr(e); + } + }, + _ => {}, + } + }, + ExprKind::MethodCall(..) => { + let borrowed_table = self.cx.tables; + if borrowed_table.expr_ty(e).is_never() { + self.report_diverging_sub_expr(e); + } + }, + _ => { + // do not lint expressions referencing objects of type `!`, as that required a + // diverging expression + // to begin with + }, + } + self.maybe_walk_expr(e); + } + fn visit_block(&mut self, _: &'tcx Block<'_>) { + // don't continue over blocks, LateLintPass already does that + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Walks up the AST from the given write expression (`vis.write_expr`) looking +/// for reads to the same variable that are unsequenced relative to the write. +/// +/// This means reads for which there is a common ancestor between the read and +/// the write such that +/// +/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x` +/// and `|| x = 1` don't necessarily evaluate `x`), and +/// +/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s, +/// loops, `match`es, and the short-circuiting logical operators are considered to have a defined +/// evaluation order. +/// +/// When such a read is found, the lint is triggered. +fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { + let map = &vis.cx.tcx.hir(); + let mut cur_id = vis.write_expr.hir_id; + loop { + let parent_id = map.get_parent_node(cur_id); + if parent_id == cur_id { + break; + } + let parent_node = match map.find(parent_id) { + Some(parent) => parent, + None => break, + }; + + let stop_early = match parent_node { + Node::Expr(expr) => check_expr(vis, expr), + Node::Stmt(stmt) => check_stmt(vis, stmt), + Node::Item(_) => { + // We reached the top of the function, stop. + break; + }, + _ => StopEarly::KeepGoing, + }; + match stop_early { + StopEarly::Stop => break, + StopEarly::KeepGoing => {}, + } + + cur_id = parent_id; + } +} + +/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If +/// `check_expr` weren't an independent function, this would be unnecessary and +/// we could just use `break`). +enum StopEarly { + KeepGoing, + Stop, +} + +fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { + if expr.hir_id == vis.last_expr.hir_id { + return StopEarly::KeepGoing; + } + + match expr.kind { + ExprKind::Array(_) + | ExprKind::Tup(_) + | ExprKind::MethodCall(..) + | ExprKind::Call(_, _) + | ExprKind::Assign(..) + | ExprKind::Index(_, _) + | ExprKind::Repeat(_, _) + | ExprKind::Struct(_, _, _) => { + walk_expr(vis, expr); + }, + ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => { + if op.node == BinOpKind::And || op.node == BinOpKind::Or { + // x && y and x || y always evaluate x first, so these are + // strictly sequenced. + } else { + walk_expr(vis, expr); + } + }, + ExprKind::Closure(_, _, _, _, _) => { + // Either + // + // * `var` is defined in the closure body, in which case we've reached the top of the enclosing + // function and can stop, or + // + // * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate + // its body, we don't necessarily have a write, so we need to stop to avoid generating false + // positives. + // + // This is also the only place we need to stop early (grrr). + return StopEarly::Stop; + }, + // All other expressions either have only one child or strictly + // sequence the evaluation order of their sub-expressions. + _ => {}, + } + + vis.last_expr = expr; + + StopEarly::KeepGoing +} + +fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => check_expr(vis, expr), + // If the declaration is of a local variable, check its initializer + // expression if it has one. Otherwise, keep going. + StmtKind::Local(ref local) => local + .init + .as_ref() + .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), + _ => StopEarly::KeepGoing, + } +} + +/// A visitor that looks for reads from a variable. +struct ReadVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + /// The ID of the variable we're looking for. + var: HirId, + /// The expressions where the write to the variable occurred (for reporting + /// in the lint). + write_expr: &'tcx Expr<'tcx>, + /// The last (highest in the AST) expression we've checked, so we know not + /// to recheck it. + last_expr: &'tcx Expr<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if expr.hir_id == self.last_expr.hir_id { + return; + } + + match expr.kind { + ExprKind::Path(ref qpath) => { + if_chain! { + if let QPath::Resolved(None, ref path) = *qpath; + if path.segments.len() == 1; + if let def::Res::Local(local_id) = self.cx.tables.qpath_res(qpath, expr.hir_id); + if local_id == self.var; + // Check that this is a read, not a write. + if !is_in_assignment_position(self.cx, expr); + then { + span_lint_and_note( + self.cx, + EVAL_ORDER_DEPENDENCE, + expr.span, + "unsequenced read of a variable", + Some(self.write_expr.span), + "whether read occurs before this write depends on evaluation order" + ); + } + } + } + // We're about to descend a closure. Since we don't know when (or + // if) the closure will be evaluated, any reads in it might not + // occur here (or ever). Like above, bail to avoid false positives. + ExprKind::Closure(_, _, _, _, _) | + + // We want to avoid a false positive when a variable name occurs + // only to have its address taken, so we stop here. Technically, + // this misses some weird cases, eg. + // + // ```rust + // let mut x = 0; + // let a = foo(&{x = 1; x}, x); + // ``` + // + // TODO: fix this + ExprKind::AddrOf(_, _, _) => { + return; + } + _ => {} + } + + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// 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(ref lhs, ..) = parent.kind { + return lhs.hir_id == expr.hir_id; + } + } + false +} diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs new file mode 100644 index 0000000000000..82ca4baacb7a9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -0,0 +1,176 @@ +use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help}; +use rustc_ast::ast::{AssocItemKind, Extern, FnSig, Item, ItemKind, Ty, TyKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; + +use std::convert::TryInto; + +declare_clippy_lint! { + /// **What it does:** Checks for excessive + /// 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. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// struct S { + /// is_pending: bool, + /// is_processing: bool, + /// is_finished: bool, + /// } + /// ``` + /// + /// Good: + /// ```rust + /// enum S { + /// Pending, + /// Processing, + /// Finished, + /// } + /// ``` + pub STRUCT_EXCESSIVE_BOOLS, + pedantic, + "using too many bools in a struct" +} + +declare_clippy_lint! { + /// **What it does:** Checks for excessive use of + /// bools in function definitions. + /// + /// **Why is this bad?** Calls to such functions + /// are confusing and error prone, because it's + /// hard to remember argument order and you have + /// no type system support to back you up. Using + /// two-variant enums instead of bools often makes + /// API easier to use. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// fn f(is_round: bool, is_hot: bool) { ... } + /// ``` + /// + /// Good: + /// ```rust,ignore + /// enum Shape { + /// Round, + /// Spiky, + /// } + /// + /// enum Temperature { + /// Hot, + /// IceCold, + /// } + /// + /// fn f(shape: Shape, temperature: Temperature) { ... } + /// ``` + pub FN_PARAMS_EXCESSIVE_BOOLS, + pedantic, + "using too many bools in function parameters" +} + +pub struct ExcessiveBools { + max_struct_bools: u64, + max_fn_params_bools: u64, +} + +impl ExcessiveBools { + #[must_use] + pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { + Self { + max_struct_bools, + max_fn_params_bools, + } + } + + fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) { + match fn_sig.header.ext { + Extern::Implicit | Extern::Explicit(_) => return, + Extern::None => (), + } + + let fn_sig_bools = fn_sig + .decl + .inputs + .iter() + .filter(|param| is_bool_ty(¶m.ty)) + .count() + .try_into() + .unwrap(); + if self.max_fn_params_bools < fn_sig_bools { + span_lint_and_help( + cx, + FN_PARAMS_EXCESSIVE_BOOLS, + span, + &format!("more than {} bools in function parameters", self.max_fn_params_bools), + None, + "consider refactoring bools into two-variant enums", + ); + } + } +} + +impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); + +fn is_bool_ty(ty: &Ty) -> bool { + if let TyKind::Path(None, path) = &ty.kind { + return match_path_ast(path, &["bool"]); + } + false +} + +impl EarlyLintPass for ExcessiveBools { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if in_macro(item.span) { + return; + } + match &item.kind { + ItemKind::Struct(variant_data, _) => { + if attr_by_name(&item.attrs, "repr").is_some() { + return; + } + + let struct_bools = variant_data + .fields() + .iter() + .filter(|field| is_bool_ty(&field.ty)) + .count() + .try_into() + .unwrap(); + if self.max_struct_bools < struct_bools { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + &format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ); + } + }, + ItemKind::Impl { + of_trait: None, items, .. + } + | ItemKind::Trait(_, _, _, _, items) => { + for item in items { + if let AssocItemKind::Fn(_, fn_sig, _, _) = &item.kind { + self.check_fn_sig(cx, fn_sig, item.span); + } + } + }, + ItemKind::Fn(_, fn_sig, _, _) => self.check_fn_sig(cx, fn_sig, item.span), + _ => (), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs new file mode 100644 index 0000000000000..621d56185a9dd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -0,0 +1,47 @@ +use crate::utils::{is_entrypoint_fn, match_def_path, paths, qpath_res, span_lint}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** `exit()` terminates the program and doesn't provide a + /// stack trace. + /// + /// **Why is this bad?** Ideally a program is terminated by finishing + /// the main function. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// std::process::exit(0) + /// ``` + pub EXIT, + restriction, + "`std::process::exit` is called, terminating the program" +} + +declare_lint_pass!(Exit => [EXIT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref path_expr, ref _args) = e.kind; + if let ExprKind::Path(ref path) = path_expr.kind; + if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::EXIT); + then { + let parent = cx.tcx.hir().get_parent_item(e.hir_id); + if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) { + // If the next item up is a function we check if it is an entry point + // and only then emit a linter warning + let def_id = cx.tcx.hir().local_def_id(parent); + if !is_entrypoint_fn(cx, def_id.to_def_id()) { + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs new file mode 100644 index 0000000000000..320121b277140 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -0,0 +1,149 @@ +use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be + /// replaced with `(e)print!()` / `(e)println!()` + /// + /// **Why is this bad?** Using `(e)println! is clearer and more concise + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::io::Write; + /// # let bar = "furchtbar"; + /// // this would be clearer as `eprintln!("foo: {:?}", bar);` + /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap(); + /// ``` + pub EXPLICIT_WRITE, + complexity, + "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work" +} + +declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitWrite { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // match call to unwrap + if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.kind; + if unwrap_fun.ident.name == sym!(unwrap); + // match call to write_fmt + if !unwrap_args.is_empty(); + if let ExprKind::MethodCall(ref write_fun, _, write_args) = + unwrap_args[0].kind; + if write_fun.ident.name == sym!(write_fmt); + // match calls to std::io::stdout() / std::io::stderr () + if !write_args.is_empty(); + if let Some(dest_name) = if match_function_call(cx, &write_args[0], &paths::STDOUT).is_some() { + Some("stdout") + } else if match_function_call(cx, &write_args[0], &paths::STDERR).is_some() { + Some("stderr") + } else { + None + }; + then { + let write_span = unwrap_args[0].span; + let calling_macro = + // ordering is important here, since `writeln!` uses `write!` internally + if is_expn_of(write_span, "writeln").is_some() { + Some("writeln") + } else if is_expn_of(write_span, "write").is_some() { + Some("write") + } else { + None + }; + let prefix = if dest_name == "stderr" { + "e" + } else { + "" + }; + + // We need to remove the last trailing newline from the string because the + // underlying `fmt::write` function doesn't know whether `println!` or `print!` was + // used. + if let Some(mut write_output) = write_output_string(write_args) { + if write_output.ends_with('\n') { + write_output.pop(); + } + + if let Some(macro_name) = calling_macro { + span_lint_and_sugg( + cx, + EXPLICIT_WRITE, + expr.span, + &format!( + "use of `{}!({}(), ...).unwrap()`", + macro_name, + dest_name + ), + "try this", + format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()), + Applicability::MachineApplicable + ); + } else { + span_lint_and_sugg( + cx, + EXPLICIT_WRITE, + expr.span, + &format!("use of `{}().write_fmt(...).unwrap()`", dest_name), + "try this", + format!("{}print!(\"{}\")", prefix, write_output.escape_default()), + Applicability::MachineApplicable + ); + } + } else { + // We don't have a proper suggestion + if let Some(macro_name) = calling_macro { + span_lint( + cx, + EXPLICIT_WRITE, + expr.span, + &format!( + "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead", + macro_name, + dest_name, + prefix, + macro_name.replace("write", "print") + ) + ); + } else { + span_lint( + cx, + EXPLICIT_WRITE, + expr.span, + &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix), + ); + } + } + + } + } + } +} + +// Extract the output string from the given `write_args`. +fn write_output_string(write_args: &[Expr<'_>]) -> Option { + if_chain! { + // Obtain the string that should be printed + if write_args.len() > 1; + if let ExprKind::Call(_, ref output_args) = write_args[1].kind; + if !output_args.is_empty(); + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref output_string_expr) = output_args[0].kind; + if let ExprKind::Array(ref string_exprs) = output_string_expr.kind; + // we only want to provide an automatic suggestion for simple (non-format) strings + if string_exprs.len() == 1; + if let ExprKind::Lit(ref lit) = string_exprs[0].kind; + if let LitKind::Str(ref write_output, _) = lit.node; + then { + return Some(write_output.to_string()) + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs new file mode 100644 index 0000000000000..17639cc2a0643 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -0,0 +1,130 @@ +use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT}; +use crate::utils::{ + is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty, +}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()` + /// + /// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// struct Foo(i32); + /// impl From for Foo { + /// fn from(s: String) -> Self { + /// Foo(s.parse().unwrap()) + /// } + /// } + /// ``` + pub FALLIBLE_IMPL_FROM, + nursery, + "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`" +} + +declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FallibleImplFrom { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { + // check for `impl From for ..` + let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id); + if_chain! { + if let hir::ItemKind::Impl{ items: impl_items, .. } = item.kind; + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id); + if match_def_path(cx, impl_trait_ref.def_id, &FROM_TRAIT); + then { + lint_impl_body(cx, item.span, impl_items); + } + } + } +} + +fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef<'_>]) { + use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; + use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath}; + + struct FindPanicUnwrap<'a, 'tcx> { + lcx: &'a LateContext<'a, 'tcx>, + tables: &'tcx ty::TypeckTables<'tcx>, + result: Vec, + } + + impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // check for `begin_panic` + if_chain! { + if let ExprKind::Call(ref func_expr, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + if let Some(path_def_id) = path.res.opt_def_id(); + if match_def_path(self.lcx, path_def_id, &BEGIN_PANIC) || + match_def_path(self.lcx, path_def_id, &BEGIN_PANIC_FMT); + if is_expn_of(expr.span, "unreachable").is_none(); + then { + self.result.push(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = walk_ptrs_ty(self.tables.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + } + + for impl_item in impl_items { + if_chain! { + if impl_item.ident.name == sym!(from); + if let ImplItemKind::Fn(_, body_id) = + cx.tcx.hir().impl_item(impl_item.id).kind; + then { + // check the body for `begin_panic` or `unwrap` + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.id.hir_id); + let mut fpu = FindPanicUnwrap { + lcx: cx, + tables: cx.tcx.typeck_tables_of(impl_item_def_id), + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + FALLIBLE_IMPL_FROM, + impl_span, + "consider implementing `TryFrom` instead", + move |diag| { + diag.help( + "`From` is intended for infallible conversions only. \ + Use `TryFrom` if there's a possibility for the conversion to fail."); + diag.span_note(fpu.result, "potential failure(s)"); + }); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs new file mode 100644 index 0000000000000..3a52b1d3fc20b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -0,0 +1,182 @@ +use crate::utils::{numeric_literal, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::fmt; + +declare_clippy_lint! { + /// **What it does:** Checks for float literals with a precision greater + /// than that supported by the underlying type. + /// + /// **Why is this bad?** Rust will truncate the literal silently. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let v: f32 = 0.123_456_789_9; + /// println!("{}", v); // 0.123_456_789 + /// + /// // Good + /// let v: f64 = 0.123_456_789_9; + /// println!("{}", v); // 0.123_456_789_9 + /// ``` + pub EXCESSIVE_PRECISION, + style, + "excessive precision for float literal" +} + +declare_clippy_lint! { + /// **What it does:** Checks for whole number float literals that + /// cannot be represented as the underlying type without loss. + /// + /// **Why is this bad?** Rust will silently lose precision during + /// conversion to a float. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let _: f32 = 16_777_217.0; // 16_777_216.0 + /// + /// // Good + /// let _: f32 = 16_777_216.0; + /// let _: f64 = 16_777_217.0; + /// ``` + pub LOSSY_FLOAT_LITERAL, + restriction, + "lossy whole number float literals" +} + +declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + let ty = cx.tables.expr_ty(expr); + if let ty::Float(fty) = ty.kind; + if let hir::ExprKind::Lit(ref lit) = expr.kind; + if let LitKind::Float(sym, lit_float_ty) = lit.node; + then { + let sym_str = sym.as_str(); + let formatter = FloatFormat::new(&sym_str); + // Try to bail out if the float is for sure fine. + // If its within the 2 decimal digits of being out of precision we + // check if the parsed representation is the same as the string + // since we'll need the truncated string anyway. + let digits = count_digits(&sym_str); + let max = max_digits(fty); + let type_suffix = match lit_float_ty { + LitFloatType::Suffixed(FloatTy::F32) => Some("f32"), + LitFloatType::Suffixed(FloatTy::F64) => Some("f64"), + _ => None + }; + let (is_whole, mut float_str) = match fty { + FloatTy::F32 => { + let value = sym_str.parse::().unwrap(); + + (value.fract() == 0.0, formatter.format(value)) + }, + FloatTy::F64 => { + let value = sym_str.parse::().unwrap(); + + (value.fract() == 0.0, formatter.format(value)) + }, + }; + + if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') { + // Normalize the literal by stripping the fractional portion + if sym_str.split('.').next().unwrap() != float_str { + // If the type suffix is missing the suggestion would be + // incorrectly interpreted as an integer so adding a `.0` + // suffix to prevent that. + if type_suffix.is_none() { + float_str.push_str(".0"); + } + + span_lint_and_sugg( + cx, + LOSSY_FLOAT_LITERAL, + expr.span, + "literal cannot be represented as the underlying type without loss of precision", + "consider changing the type or replacing it with", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + ); + } + } else if digits > max as usize && sym_str != float_str { + span_lint_and_sugg( + cx, + EXCESSIVE_PRECISION, + expr.span, + "float has excessive precision", + "consider changing the type or truncating it to", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + ); + } + } + } + } +} + +#[must_use] +fn max_digits(fty: FloatTy) -> u32 { + match fty { + FloatTy::F32 => f32::DIGITS, + FloatTy::F64 => f64::DIGITS, + } +} + +/// Counts the digits excluding leading zeros +#[must_use] +fn count_digits(s: &str) -> usize { + // Note that s does not contain the f32/64 suffix, and underscores have been stripped + s.chars() + .filter(|c| *c != '-' && *c != '.') + .take_while(|c| *c != 'e' && *c != 'E') + .fold(0, |count, c| { + // leading zeros + if c == '0' && count == 0 { + count + } else { + count + 1 + } + }) +} + +enum FloatFormat { + LowerExp, + UpperExp, + Normal, +} +impl FloatFormat { + #[must_use] + fn new(s: &str) -> Self { + s.chars() + .find_map(|x| match x { + 'e' => Some(Self::LowerExp), + 'E' => Some(Self::UpperExp), + _ => None, + }) + .unwrap_or(Self::Normal) + } + fn format(&self, f: T) -> String + where + T: fmt::UpperExp + fmt::LowerExp + fmt::Display, + { + match self { + Self::LowerExp => format!("{:e}", f), + Self::UpperExp => format!("{:E}", f), + Self::Normal => format!("{}", f), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs new file mode 100644 index 0000000000000..86317fb8bd5c4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -0,0 +1,503 @@ +use crate::consts::{ + constant, constant_simple, Constant, + Constant::{F32, F64}, +}; +use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +use rustc_ast::ast; +use std::f32::consts as f32_consts; +use std::f64::consts as f64_consts; +use sugg::Sugg; + +declare_clippy_lint! { + /// **What it does:** Looks for floating-point expressions that + /// can be expressed using built-in methods to improve accuracy + /// at the cost of performance. + /// + /// **Why is this bad?** Negatively impacts accuracy. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// + /// let a = 3f32; + /// let _ = a.powf(1.0 / 3.0); + /// let _ = (1.0 + a).ln(); + /// let _ = a.exp() - 1.0; + /// ``` + /// + /// is better expressed as + /// + /// ```rust + /// + /// let a = 3f32; + /// let _ = a.cbrt(); + /// let _ = a.ln_1p(); + /// let _ = a.exp_m1(); + /// ``` + pub IMPRECISE_FLOPS, + nursery, + "usage of imprecise floating point operations" +} + +declare_clippy_lint! { + /// **What it does:** Looks for floating-point expressions that + /// can be expressed using built-in methods to improve both + /// accuracy and performance. + /// + /// **Why is this bad?** Negatively impacts accuracy and performance. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// use std::f32::consts::E; + /// + /// let a = 3f32; + /// let _ = (2f32).powf(a); + /// let _ = E.powf(a); + /// let _ = a.powf(1.0 / 2.0); + /// let _ = a.log(2.0); + /// let _ = a.log(10.0); + /// let _ = a.log(E); + /// let _ = a.powf(2.0); + /// let _ = a * 2.0 + 4.0; + /// let _ = if a < 0.0 { + /// -a + /// } else { + /// a + /// }; + /// let _ = if a < 0.0 { + /// a + /// } else { + /// -a + /// }; + /// ``` + /// + /// is better expressed as + /// + /// ```rust + /// use std::f32::consts::E; + /// + /// let a = 3f32; + /// let _ = a.exp2(); + /// let _ = a.exp(); + /// let _ = a.sqrt(); + /// let _ = a.log2(); + /// let _ = a.log10(); + /// let _ = a.ln(); + /// let _ = a.powi(2); + /// let _ = a.mul_add(2.0, 4.0); + /// let _ = a.abs(); + /// let _ = -a.abs(); + /// ``` + pub SUBOPTIMAL_FLOPS, + nursery, + "usage of sub-optimal floating point operations" +} + +declare_lint_pass!(FloatingPointArithmetic => [ + IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS +]); + +// Returns the specialized log method for a given base if base is constant +// and is one of 2, 10 and e +fn get_specialized_log_method(cx: &LateContext<'_, '_>, base: &Expr<'_>) -> Option<&'static str> { + if let Some((value, _)) = constant(cx, cx.tables, base) { + if F32(2.0) == value || F64(2.0) == value { + return Some("log2"); + } else if F32(10.0) == value || F64(10.0) == value { + return Some("log10"); + } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value { + return Some("ln"); + } + } + + None +} + +// Adds type suffixes and parenthesis to method receivers if necessary +fn prepare_receiver_sugg<'a>(cx: &LateContext<'_, '_>, mut expr: &'a Expr<'a>) -> Sugg<'a> { + let mut suggestion = Sugg::hir(cx, expr, ".."); + + if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind { + expr = &inner_expr; + } + + if_chain! { + // if the expression is a float literal and it is unsuffixed then + // add a suffix so the suggestion is valid and unambiguous + if let ty::Float(float_ty) = cx.tables.expr_ty(expr).kind; + if let ExprKind::Lit(lit) = &expr.kind; + if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; + then { + let op = format!( + "{}{}{}", + suggestion, + // Check for float literals without numbers following the decimal + // separator such as `2.` and adds a trailing zero + if sym.as_str().ends_with('.') { + "0" + } else { + "" + }, + float_ty.name_str() + ).into(); + + suggestion = match suggestion { + Sugg::MaybeParen(_) => Sugg::MaybeParen(op), + _ => Sugg::NonParen(op) + }; + } + } + + suggestion.maybe_par() +} + +fn check_log_base(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + if let Some(method) = get_specialized_log_method(cx, &args[1]) { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "logarithm for bases 2, 10 and e can be computed more accurately", + "consider using", + format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method), + Applicability::MachineApplicable, + ); + } +} + +// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and +// suggest usage of `(x + (y - 1)).ln_1p()` instead +fn check_ln1p(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + lhs, + rhs, + ) = &args[0].kind + { + let recv = match (constant(cx, cx.tables, lhs), constant(cx, cx.tables, rhs)) { + (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs, + (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs, + _ => return, + }; + + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "ln(1 + x) can be computed more accurately", + "consider using", + format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)), + Applicability::MachineApplicable, + ); + } +} + +// Returns an integer if the float constant is a whole number and it can be +// converted to an integer without loss of precision. For now we only check +// ranges [-16777215, 16777216) for type f32 as whole number floats outside +// this range are lossy and ambiguous. +#[allow(clippy::cast_possible_truncation)] +fn get_integer_from_float_constant(value: &Constant) -> Option { + match value { + F32(num) if num.fract() == 0.0 => { + if (-16_777_215.0..16_777_216.0).contains(num) { + Some(num.round() as i32) + } else { + None + } + }, + F64(num) if num.fract() == 0.0 => { + if (-2_147_483_648.0..2_147_483_648.0).contains(num) { + Some(num.round() as i32) + } else { + None + } + }, + _ => None, + } +} + +fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + // Check receiver + if let Some((value, _)) = constant(cx, cx.tables, &args[0]) { + let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { + "exp" + } else if F32(2.0) == value || F64(2.0) == value { + "exp2" + } else { + return; + }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "exponent for bases 2 and e can be computed more accurately", + "consider using", + format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method), + Applicability::MachineApplicable, + ); + } + + // Check argument + if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { + ( + SUBOPTIMAL_FLOPS, + "square-root of a number can be computed more efficiently and accurately", + format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")), + ) + } 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, &args[0], "..")), + ) + } else if let Some(exponent) = get_integer_from_float_constant(&value) { + ( + SUBOPTIMAL_FLOPS, + "exponentiation with integer powers can be computed more efficiently", + format!( + "{}.powi({})", + Sugg::hir(cx, &args[0], ".."), + numeric_literal::format(&exponent.to_string(), None, false) + ), + ) + } else { + return; + }; + + span_lint_and_sugg( + cx, + lint, + expr.span, + help, + "consider using", + suggestion, + Applicability::MachineApplicable, + ); + } +} + +// TODO: Lint expressions of the form `x.exp() - y` where y > 1 +// and suggest usage of `x.exp_m1() - (y - 1)` instead +fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, ref lhs, ref rhs) = expr.kind; + if cx.tables.expr_ty(lhs).is_floating_point(); + if let Some((value, _)) = constant(cx, cx.tables, rhs); + if F32(1.0) == value || F64(1.0) == value; + if let ExprKind::MethodCall(ref path, _, ref method_args) = lhs.kind; + if cx.tables.expr_ty(&method_args[0]).is_floating_point(); + if path.ident.name.as_str() == "exp"; + then { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "(e.pow(x) - 1) can be computed more accurately", + "consider using", + format!( + "{}.exp_m1()", + Sugg::hir(cx, &method_args[0], "..") + ), + Applicability::MachineApplicable, + ); + } + } +} + +fn is_float_mul_expr<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lhs, ref rhs) = &expr.kind; + if cx.tables.expr_ty(lhs).is_floating_point(); + if cx.tables.expr_ty(rhs).is_floating_point(); + then { + return Some((lhs, rhs)); + } + } + + None +} + +// TODO: Fix rust-lang/rust-clippy#4735 +fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + lhs, + rhs, + ) = &expr.kind + { + let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { + (inner_lhs, inner_rhs, rhs) + } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { + (inner_lhs, inner_rhs, lhs) + } else { + return; + }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "multiply and add expressions can be calculated more efficiently and accurately", + "consider using", + format!( + "{}.mul_add({}, {})", + prepare_receiver_sugg(cx, recv), + Sugg::hir(cx, arg1, ".."), + Sugg::hir(cx, arg2, ".."), + ), + Applicability::MachineApplicable, + ); + } +} + +/// Returns true iff expr is an expression which tests whether or not +/// test is positive or an expression which tests whether or not test +/// is nonnegative. +/// Used for check-custom-abs function below +fn is_testing_positive(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { + if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { + match op { + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test), + _ => false, + } + } else { + false + } +} + +/// See [`is_testing_positive`] +fn is_testing_negative(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { + if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { + match op { + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test), + _ => false, + } + } else { + false + } +} + +fn are_exprs_equal(cx: &LateContext<'_, '_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool { + SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2) +} + +/// Returns true iff expr is some zero literal +fn is_zero(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + match constant_simple(cx, cx.tables, expr) { + Some(Constant::Int(i)) => i == 0, + Some(Constant::F32(f)) => f == 0.0, + Some(Constant::F64(f)) => f == 0.0, + _ => false, + } +} + +/// If the two expressions are negations of each other, then it returns +/// a tuple, in which the first element is true iff expr1 is the +/// positive expressions, and the second element is the positive +/// one of the two expressions +/// 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::UnNeg, expr1_negated) = &expr1.kind { + if are_exprs_equal(cx, expr1_negated, expr2) { + return Some((false, expr2)); + } + } + if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind { + if are_exprs_equal(cx, expr1, expr2_negated) { + return Some((true, expr1)); + } + } + None +} + +fn check_custom_abs(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let Some((cond, body, Some(else_body))) = higher::if_block(&expr); + if let ExprKind::Block(block, _) = body.kind; + if block.stmts.is_empty(); + if let Some(if_body_expr) = block.expr; + if let ExprKind::Block(else_block, _) = else_body.kind; + if else_block.stmts.is_empty(); + if let Some(else_body_expr) = else_block.expr; + if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr); + then { + let positive_abs_sugg = ( + "manual implementation of `abs` method", + format!("{}.abs()", Sugg::hir(cx, body, "..")), + ); + let negative_abs_sugg = ( + "manual implementation of negation of `abs` method", + format!("-{}.abs()", Sugg::hir(cx, body, "..")), + ); + let sugg = if is_testing_positive(cx, cond, body) { + if if_expr_positive { + positive_abs_sugg + } else { + negative_abs_sugg + } + } else if is_testing_negative(cx, cond, body) { + if if_expr_positive { + negative_abs_sugg + } else { + positive_abs_sugg + } + } else { + return; + }; + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + sugg.0, + "try", + sugg.1, + Applicability::MachineApplicable, + ); + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(ref path, _, args) = &expr.kind { + let recv_ty = cx.tables.expr_ty(&args[0]); + + if recv_ty.is_floating_point() { + match &*path.ident.name.as_str() { + "ln" => check_ln1p(cx, expr, args), + "log" => check_log_base(cx, expr, args), + "powf" => check_powf(cx, expr, args), + _ => {}, + } + } + } else { + check_expm1(cx, expr); + check_mul_add(cx, expr); + check_custom_abs(cx, expr); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs new file mode 100644 index 0000000000000..5b092526ce4f2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -0,0 +1,200 @@ +use crate::utils::paths; +use crate::utils::{ + is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, + span_lint_and_then, walk_ptrs_ty, +}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for the use of `format!("string literal with no + /// argument")` and `format!("{}", foo)` where `foo` is a string. + /// + /// **Why is this bad?** There is no point of doing that. `format!("foo")` can + /// be replaced by `"foo".to_owned()` if you really need a `String`. The even + /// worse `&format!("foo")` is often encountered in the wild. `format!("{}", + /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()` + /// if `foo: &str`. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// # let foo = "foo"; + /// format!("foo"); + /// format!("{}", foo); + /// ``` + pub USELESS_FORMAT, + complexity, + "useless use of `format!`" +} + +declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessFormat { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + let span = match is_expn_of(expr.span, "format") { + Some(s) if !s.from_expansion() => s, + _ => return, + }; + + // Operate on the only argument of `alloc::fmt::format`. + if let Some(sugg) = on_new_v1(cx, expr) { + span_useless_format(cx, span, "consider using `.to_string()`", sugg); + } else if let Some(sugg) = on_new_v1_fmt(cx, expr) { + span_useless_format(cx, span, "consider using `.to_string()`", sugg); + } + } +} + +fn span_useless_format(cx: &T, span: Span, help: &str, mut sugg: String) { + let to_replace = span.source_callsite(); + + // The callsite span contains the statement semicolon for some reason. + let snippet = snippet(cx, to_replace, ".."); + if snippet.ends_with(';') { + sugg.push(';'); + } + + span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |diag| { + diag.span_suggestion( + to_replace, + help, + sugg, + Applicability::MachineApplicable, // snippet + ); + }); +} + +fn on_argumentv1_new<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], +) -> Option { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref format_args) = expr.kind; + if let ExprKind::Array(ref elems) = arms[0].body.kind; + if elems.len() == 1; + if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW); + // matches `core::fmt::Display::fmt` + if args.len() == 2; + if let ExprKind::Path(ref qpath) = args[1].kind; + if let Some(did) = cx.tables.qpath_res(qpath, args[1].hir_id).opt_def_id(); + if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD); + // check `(arg0,)` in match block + if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind; + if pats.len() == 1; + then { + let ty = walk_ptrs_ty(cx.tables.pat_ty(&pats[0])); + if ty.kind != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { + return None; + } + if let ExprKind::Lit(ref lit) = format_args.kind { + if let LitKind::Str(ref s, _) = lit.node { + return Some(format!("{:?}.to_string()", s.as_str())); + } + } else { + let snip = snippet(cx, format_args.span, ""); + if let ExprKind::MethodCall(ref path, _, _) = format_args.kind { + if path.ident.name == sym!(to_string) { + return Some(format!("{}", snip)); + } + } else if let ExprKind::Binary(..) = format_args.kind { + return Some(format!("{}", snip)); + } + return Some(format!("{}.to_string()", snip)); + } + } + } + None +} + +fn on_new_v1<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option { + if_chain! { + if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1); + if args.len() == 2; + // Argument 1 in `new_v1()` + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind; + if let ExprKind::Array(ref pieces) = arr.kind; + if pieces.len() == 1; + if let ExprKind::Lit(ref lit) = pieces[0].kind; + if let LitKind::Str(ref s, _) = lit.node; + // Argument 2 in `new_v1()` + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind; + if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind; + if arms.len() == 1; + if let ExprKind::Tup(ref tup) = matchee.kind; + then { + // `format!("foo")` expansion contains `match () { () => [], }` + if tup.is_empty() { + return Some(format!("{:?}.to_string()", s.as_str())); + } else if s.as_str().is_empty() { + return on_argumentv1_new(cx, &tup[0], arms); + } + } + } + None +} + +fn on_new_v1_fmt<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option { + if_chain! { + if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1_FORMATTED); + if args.len() == 3; + if check_unformatted(&args[2]); + // Argument 1 in `new_v1_formatted()` + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind; + if let ExprKind::Array(ref pieces) = arr.kind; + if pieces.len() == 1; + if let ExprKind::Lit(ref lit) = pieces[0].kind; + if let LitKind::Str(..) = lit.node; + // Argument 2 in `new_v1_formatted()` + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind; + if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind; + if arms.len() == 1; + if let ExprKind::Tup(ref tup) = matchee.kind; + then { + return on_argumentv1_new(cx, &tup[0], arms); + } + } + None +} + +/// Checks if the expression matches +/// ```rust,ignore +/// &[_ { +/// format: _ { +/// width: _::Implied, +/// precision: _::Implied, +/// ... +/// }, +/// ..., +/// }] +/// ``` +fn check_unformatted(expr: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; + if let ExprKind::Array(ref exprs) = expr.kind; + if exprs.len() == 1; + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind; + if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym!(format)); + // struct `core::fmt::rt::v1::FormatSpec` + if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind; + if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym!(precision)); + if let ExprKind::Path(ref precision_path) = precision_field.expr.kind; + if last_path_segment(precision_path).ident.name == sym!(Implied); + if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym!(width)); + if let ExprKind::Path(ref width_qpath) = width_field.expr.kind; + if last_path_segment(width_qpath).ident.name == sym!(Implied); + then { + return true; + } + } + + false +} diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs new file mode 100644 index 0000000000000..eb4b7a826f2ce --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -0,0 +1,326 @@ +use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note}; +use if_chain::if_chain; +use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` + /// operators. + /// + /// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or + /// confusing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`? + /// ``` + pub SUSPICIOUS_ASSIGNMENT_FORMATTING, + style, + "suspicious formatting of `*=`, `-=` or `!=`" +} + +declare_clippy_lint! { + /// **What it does:** Checks the formatting of a unary operator on the right hand side + /// of a binary operator. It lints if there is no space between the binary and unary operators, + /// but there is a space between the unary and its operand. + /// + /// **Why is this bad?** This is either a typo in the binary operator or confusing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator + /// } + /// + /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator + /// } + /// ``` + pub SUSPICIOUS_UNARY_OP_FORMATTING, + style, + "suspicious formatting of unary `-` or `!` on the RHS of a BinOp" +} + +declare_clippy_lint! { + /// **What it does:** Checks for formatting of `else`. It lints if the `else` + /// is followed immediately by a newline or the `else` seems to be missing. + /// + /// **Why is this bad?** This is probably some refactoring remnant, even if the + /// code is correct, it might look confusing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// if foo { + /// } { // looks like an `else` is missing here + /// } + /// + /// if foo { + /// } if bar { // looks like an `else` is missing here + /// } + /// + /// if foo { + /// } else + /// + /// { // this is the `else` block of the previous `if`, but should it be? + /// } + /// + /// if foo { + /// } else + /// + /// if bar { // this is the `else` block of the previous `if`, but should it be? + /// } + /// ``` + pub SUSPICIOUS_ELSE_FORMATTING, + style, + "suspicious formatting of `else`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for possible missing comma in an array. It lints if + /// an array element is a binary operator expression and it lies on two lines. + /// + /// **Why is this bad?** This could lead to unexpected results. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// let a = &[ + /// -1, -2, -3 // <= no comma here + /// -4, -5, -6 + /// ]; + /// ``` + pub POSSIBLE_MISSING_COMMA, + correctness, + "possible missing comma in array" +} + +declare_lint_pass!(Formatting => [ + SUSPICIOUS_ASSIGNMENT_FORMATTING, + SUSPICIOUS_UNARY_OP_FORMATTING, + SUSPICIOUS_ELSE_FORMATTING, + POSSIBLE_MISSING_COMMA +]); + +impl EarlyLintPass for Formatting { + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + for w in block.stmts.windows(2) { + match (&w[0].kind, &w[1].kind) { + (&StmtKind::Expr(ref first), &StmtKind::Expr(ref second)) + | (&StmtKind::Expr(ref first), &StmtKind::Semi(ref second)) => { + check_missing_else(cx, first, second); + }, + _ => (), + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_assign(cx, expr); + check_unop(cx, expr); + check_else(cx, expr); + check_array(cx, expr); + } +} + +/// 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 !differing_macro_contexts(lhs.span, rhs.span) && !lhs.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 = UnOp::to_string(op); + 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} ..)`", + op = op + ), + None, + &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op), + ); + } + } + } + } + } +} + +/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint. +fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { + if_chain! { + if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind; + if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion(); + // span between BinOp LHS and RHS + let binop_span = lhs.span.between(rhs.span); + // if RHS is a UnOp + if let ExprKind::Unary(op, ref un_rhs) = rhs.kind; + // from UnOp operator to UnOp operand + let unop_operand_span = rhs.span.until(un_rhs.span); + if let Some(binop_snippet) = snippet_opt(cx, binop_span); + if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span); + let binop_str = BinOpKind::to_string(&binop.node); + // no space after BinOp operator and space after UnOp operator + if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' '); + then { + let unop_str = UnOp::to_string(op); + let eqop_span = lhs.span.between(un_rhs.span); + span_lint_and_help( + cx, + SUSPICIOUS_UNARY_OP_FORMATTING, + eqop_span, + &format!( + "by not having a space between `{binop}` and `{unop}` it looks like \ + `{binop}{unop}` is a single operator", + binop = binop_str, + unop = unop_str + ), + None, + &format!( + "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`", + binop = binop_str, + unop = unop_str + ), + ); + } + } +} + +/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`. +fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { + if_chain! { + if let ExprKind::If(_, then, Some(else_)) = &expr.kind; + if is_block(else_) || is_if(else_); + if !differing_macro_contexts(then.span, else_.span); + if !then.span.from_expansion() && !in_external_macro(cx.sess, expr.span); + + // workaround for rust-lang/rust#43081 + if expr.span.lo().0 != 0 && expr.span.hi().0 != 0; + + // this will be a span from the closing ‘}’ of the “then” block (excluding) to + // the “if” of the “else if” block (excluding) + let else_span = then.span.between(else_.span); + + // the snippet should look like " else \n " with maybe comments anywhere + // it’s bad when there is a ‘\n’ after the “else” + if let Some(else_snippet) = snippet_opt(cx, else_span); + if let Some(else_pos) = else_snippet.find("else"); + if else_snippet[else_pos..].contains('\n'); + let else_desc = if is_if(else_) { "if" } else { "{..}" }; + + then { + span_lint_and_note( + cx, + SUSPICIOUS_ELSE_FORMATTING, + else_span, + &format!("this is an `else {}` but the formatting might hide it", else_desc), + None, + &format!( + "to remove this lint, remove the `else` or remove the new line between \ + `else` and `{}`", + else_desc, + ), + ); + } + } +} + +#[must_use] +fn has_unary_equivalent(bin_op: BinOpKind) -> bool { + // &, *, - + bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub +} + +fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize { + cx.sess.source_map().lookup_char_pos(span.lo()).col.0 +} + +/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array +fn check_array(cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::Array(ref array) = expr.kind { + for element in array { + if_chain! { + if let ExprKind::Binary(ref op, ref lhs, _) = element.kind; + if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span); + let space_span = lhs.span.between(op.span); + if let Some(space_snippet) = snippet_opt(cx, space_span); + let lint_span = lhs.span.with_lo(lhs.span.hi()); + if space_snippet.contains('\n'); + if indentation(cx, op.span) <= indentation(cx, lhs.span); + then { + span_lint_and_note( + cx, + POSSIBLE_MISSING_COMMA, + lint_span, + "possibly missing a comma here", + None, + "to remove this lint, add a comma or write the expr in a single line", + ); + } + } + } + } +} + +fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { + if !differing_macro_contexts(first.span, second.span) + && !first.span.from_expansion() + && is_if(first) + && (is_block(second) || is_if(second)) + { + // where the else would be + let else_span = first.span.between(second.span); + + if let Some(else_snippet) = snippet_opt(cx, else_span) { + if !else_snippet.contains('\n') { + let (looks_like, next_thing) = if is_if(second) { + ("an `else if`", "the second `if`") + } else { + ("an `else {..}`", "the next block") + }; + + span_lint_and_note( + cx, + SUSPICIOUS_ELSE_FORMATTING, + else_span, + &format!("this looks like {} but the `else` is missing", looks_like), + None, + &format!( + "to remove this lint, add the missing `else` or add a new line before {}", + next_thing, + ), + ); + } + } + } +} + +fn is_block(expr: &Expr) -> bool { + if let ExprKind::Block(..) = expr.kind { + true + } else { + false + } +} + +/// Check if the expression is an `if` or `if let` +fn is_if(expr: &Expr) -> bool { + if let ExprKind::If(..) = expr.kind { + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs new file mode 100644 index 0000000000000..c24a24733d7f3 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions.rs @@ -0,0 +1,661 @@ +use crate::utils::{ + attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path, + must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then, + trait_ref_of_method, type_is_unsafe_function, +}; +use rustc_ast::ast::Attribute; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit; +use rustc_hir::{def::Res, def_id::DefId}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; +use rustc_target::spec::abi::Abi; + +declare_clippy_lint! { + /// **What it does:** Checks for functions with too many parameters. + /// + /// **Why is this bad?** Functions with lots of parameters are considered bad + /// style and reduce readability (“what does the 5th parameter mean?”). Consider + /// grouping some parameters into a new type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct Color; + /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { + /// // .. + /// } + /// ``` + pub TOO_MANY_ARGUMENTS, + complexity, + "functions with too many arguments" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions with a large amount of lines. + /// + /// **Why is this bad?** Functions with a lot of lines are harder to understand + /// due to having to look at a larger amount of code to understand what the + /// function is doing. Consider splitting the body of the function into + /// multiple functions. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ``` rust + /// fn im_too_long() { + /// println!(""); + /// // ... 100 more LoC + /// println!(""); + /// } + /// ``` + pub TOO_MANY_LINES, + pedantic, + "functions with too many lines" +} + +declare_clippy_lint! { + /// **What it does:** Checks for public functions that dereference raw pointer + /// arguments but are not marked unsafe. + /// + /// **Why is this bad?** The function should probably be marked `unsafe`, since + /// for an arbitrary raw pointer, there is no way of telling for sure if it is + /// valid. + /// + /// **Known problems:** + /// + /// * It does not check functions recursively so if the pointer is passed to a + /// private non-`unsafe` function which does the dereferencing, the lint won't + /// trigger. + /// * It only checks for arguments whose type are raw pointers, not raw pointers + /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or + /// `some_argument.get_raw_ptr()`). + /// + /// **Example:** + /// ```rust + /// pub fn foo(x: *const u8) { + /// println!("{}", unsafe { *x }); + /// } + /// ``` + pub NOT_UNSAFE_PTR_ARG_DEREF, + correctness, + "public functions dereferencing raw pointer arguments but not marked `unsafe`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for a [`#[must_use]`] attribute on + /// unit-returning functions and methods. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** Unit values are useless. The attribute is likely + /// a remnant of a refactoring that removed the return type. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// #[must_use] + /// fn useless() { } + /// ``` + pub MUST_USE_UNIT, + style, + "`#[must_use]` attribute on a unit-returning function / method" +} + +declare_clippy_lint! { + /// **What it does:** Checks for a [`#[must_use]`] attribute without + /// further information on functions and methods that return a type already + /// marked as `#[must_use]`. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** The attribute isn't needed. Not using the result + /// will already be reported. Alternatively, one can add some text to the + /// attribute to improve the lint message. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// #[must_use] + /// fn double_must_use() -> Result<(), ()> { + /// unimplemented!(); + /// } + /// ``` + pub DOUBLE_MUST_USE, + style, + "`#[must_use]` attribute on a `#[must_use]`-returning function / method" +} + +declare_clippy_lint! { + /// **What it does:** Checks for public functions that have no + /// [`#[must_use]`] attribute, but return something not already marked + /// must-use, have no mutable arg and mutate no statics. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** Not bad at all, this lint just shows places where + /// you could add the attribute. + /// + /// **Known problems:** The lint only checks the arguments for mutable + /// types without looking if they are actually changed. On the other hand, + /// it also ignores a broad range of potentially interesting side effects, + /// because we cannot decide whether the programmer intends the function to + /// be called for the side effect or the result. Expect many false + /// positives. At least we don't lint if the result type is unit or already + /// `#[must_use]`. + /// + /// **Examples:** + /// ```rust + /// // this could be annotated with `#[must_use]`. + /// fn id(t: T) -> T { t } + /// ``` + pub MUST_USE_CANDIDATE, + pedantic, + "function or method that could take a `#[must_use]` attribute" +} + +#[derive(Copy, Clone)] +pub struct Functions { + threshold: u64, + max_lines: u64, +} + +impl Functions { + pub fn new(threshold: u64, max_lines: u64) -> Self { + Self { threshold, max_lines } + } +} + +impl_lint_pass!(Functions => [ + TOO_MANY_ARGUMENTS, + TOO_MANY_LINES, + NOT_UNSAFE_PTR_ARG_DEREF, + MUST_USE_UNIT, + DOUBLE_MUST_USE, + MUST_USE_CANDIDATE, +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + span: Span, + hir_id: hir::HirId, + ) { + let unsafety = match kind { + intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety, + intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety, + intravisit::FnKind::Closure(_) => return, + }; + + // don't warn for implementations, it's not their fault + if !is_trait_impl_item(cx, hir_id) { + // don't lint extern functions decls, it's not their fault either + match kind { + intravisit::FnKind::Method( + _, + &hir::FnSig { + header: hir::FnHeader { abi: Abi::Rust, .. }, + .. + }, + _, + _, + ) + | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => { + self.check_arg_number(cx, decl, span.with_hi(decl.output.span().hi())) + }, + _ => {}, + } + } + + Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); + self.check_line_number(cx, span, body); + } + + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { + let attr = must_use_attr(&item.attrs); + if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + if let Some(attr) = attr { + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); + return; + } + if cx.access_levels.is_exported(item.hir_id) + && !is_proc_macro(&item.attrs) + && attr_by_name(&item.attrs, "no_mangle").is_none() + { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id, + item.span.with_hi(sig.decl.output.span().hi()), + "this function could have a `#[must_use]` attribute", + ); + } + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) { + if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + let attr = must_use_attr(&item.attrs); + if let Some(attr) = attr { + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); + } else if cx.access_levels.is_exported(item.hir_id) + && !is_proc_macro(&item.attrs) + && trait_ref_of_method(cx, item.hir_id).is_none() + { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id, + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { + // don't lint extern functions decls, it's not their fault + if sig.header.abi == Abi::Rust { + self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi())); + } + + let attr = must_use_attr(&item.attrs); + if let Some(attr) = attr { + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); + } + if let hir::TraitFn::Provided(eid) = *eid { + let body = cx.tcx.hir().body(eid); + Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); + + if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(&item.attrs) { + check_must_use_candidate( + cx, + &sig.decl, + body, + item.span, + item.hir_id, + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } + } + } +} + +impl<'a, 'tcx> Functions { + fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl<'_>, fn_span: Span) { + let args = decl.inputs.len() as u64; + if args > self.threshold { + span_lint( + cx, + TOO_MANY_ARGUMENTS, + fn_span, + &format!("this function has too many arguments ({}/{})", args, self.threshold), + ); + } + } + + fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span, body: &'tcx hir::Body<'_>) { + if in_external_macro(cx.sess(), span) { + return; + } + + let code_snippet = snippet(cx, body.value.span, ".."); + let mut line_count: u64 = 0; + let mut in_comment = false; + let mut code_in_line; + + // Skip the surrounding function decl. + let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); + let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); + let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); + + for mut line in function_lines { + code_in_line = false; + loop { + line = line.trim_start(); + if line.is_empty() { + break; + } + if in_comment { + match line.find("*/") { + Some(i) => { + line = &line[i + 2..]; + in_comment = false; + continue; + }, + None => break, + } + } else { + let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); + let single_idx = line.find("//").unwrap_or_else(|| line.len()); + code_in_line |= multi_idx > 0 && single_idx > 0; + // Implies multi_idx is below line.len() + if multi_idx < single_idx { + line = &line[multi_idx + 2..]; + in_comment = true; + continue; + } + break; + } + } + if code_in_line { + line_count += 1; + } + } + + if line_count > self.max_lines { + span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.") + } + } + + fn check_raw_ptr( + cx: &LateContext<'a, 'tcx>, + unsafety: hir::Unsafety, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + hir_id: hir::HirId, + ) { + let expr = &body.value; + if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { + let raw_ptrs = iter_input_pats(decl, body) + .zip(decl.inputs.iter()) + .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) + .collect::>(); + + if !raw_ptrs.is_empty() { + let tables = cx.tcx.body_tables(body.id()); + let mut v = DerefVisitor { + cx, + ptrs: raw_ptrs, + tables, + }; + + intravisit::walk_expr(&mut v, expr); + } + } + } +} + +fn check_needless_must_use( + cx: &LateContext<'_, '_>, + decl: &hir::FnDecl<'_>, + item_id: hir::HirId, + item_span: Span, + fn_header_span: Span, + attr: &Attribute, +) { + if in_external_macro(cx.sess(), item_span) { + return; + } + if returns_unit(decl) { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + diag.span_suggestion( + attr.span, + "remove the attribute", + "".into(), + Applicability::MachineApplicable, + ); + }, + ); + } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { + span_lint_and_help( + cx, + DOUBLE_MUST_USE, + fn_header_span, + "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", + None, + "either add some descriptive text or remove the attribute", + ); + } +} + +fn check_must_use_candidate<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + item_span: Span, + item_id: hir::HirId, + fn_span: Span, + msg: &str, +) { + if has_mutable_arg(cx, body) + || mutates_static(cx, body) + || in_external_macro(cx.sess(), item_span) + || returns_unit(decl) + || !cx.access_levels.is_exported(item_id) + || is_must_use_ty(cx, return_ty(cx, item_id)) + { + return; + } + span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { + if let Some(snippet) = snippet_opt(cx, fn_span) { + diag.span_suggestion( + fn_span, + "add the attribute", + format!("#[must_use] {}", snippet), + Applicability::MachineApplicable, + ); + } + }); +} + +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { + match decl.output { + hir::FnRetTy::DefaultReturn(_) => true, + hir::FnRetTy::Return(ref ty) => match ty.kind { + hir::TyKind::Tup(ref tys) => tys.is_empty(), + hir::TyKind::Never => true, + _ => false, + }, + } +} + +fn has_mutable_arg(cx: &LateContext<'_, '_>, body: &hir::Body<'_>) -> bool { + let mut tys = FxHashSet::default(); + body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) +} + +fn is_mutable_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { + if let hir::PatKind::Wild = pat.kind { + return false; // ignore `_` patterns + } + let def_id = pat.hir_id.owner.to_def_id(); + if cx.tcx.has_typeck_tables(def_id) { + is_mutable_ty( + cx, + &cx.tcx.typeck_tables_of(def_id.expect_local()).pat_ty(pat), + pat.span, + tys, + ) + } else { + false + } +} + +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; + +fn is_mutable_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { + match ty.kind { + // primitive types are never mutable + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, + ty::Adt(ref adt, ref substs) => { + tys.insert(adt.did) && !ty.is_freeze(cx.tcx, cx.param_env, span) + || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) + && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + }, + ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + }, + // calling something constitutes a side effect, so return true on all callables + // also never calls need not be used, so return true for them, too + _ => true, + } +} + +fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { + if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { + Some(id) + } else { + None + } +} + +struct DerefVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + ptrs: FxHashSet, + tables: &'a ty::TypeckTables<'tcx>, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + match expr.kind { + hir::ExprKind::Call(ref f, args) => { + let ty = self.tables.expr_ty(f); + + if type_is_unsafe_function(self.cx, ty) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::MethodCall(_, _, args) => { + let def_id = self.tables.type_dependent_def_id(expr.hir_id).unwrap(); + let base_type = self.cx.tcx.type_of(def_id); + + if type_is_unsafe_function(self.cx, base_type) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::UnDeref, ref ptr) => self.check_arg(ptr), + _ => (), + } + + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { + fn check_arg(&self, ptr: &hir::Expr<'_>) { + if let hir::ExprKind::Path(ref qpath) = ptr.kind { + if let Res::Local(id) = qpath_res(self.cx, qpath, ptr.hir_id) { + if self.ptrs.contains(&id) { + span_lint( + self.cx, + NOT_UNSAFE_PTR_ARG_DEREF, + ptr.span, + "this public function dereferences a raw pointer but is not marked `unsafe`", + ); + } + } + } + } +} + +struct StaticMutVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + mutates_static: bool, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; + + if self.mutates_static { + return; + } + match expr.kind { + Call(_, args) | MethodCall(_, _, args) => { + let mut tys = FxHashSet::default(); + for arg in args { + let def_id = arg.hir_id.owner.to_def_id(); + if self.cx.tcx.has_typeck_tables(def_id) + && is_mutable_ty( + self.cx, + self.cx.tcx.typeck_tables_of(def_id.expect_local()).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(self.cx, arg) + { + self.mutates_static = true; + return; + } + tys.clear(); + } + }, + Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { + self.mutates_static |= is_mutated_static(self.cx, target) + }, + _ => {}, + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +fn is_mutated_static(cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) -> bool { + use hir::ExprKind::{Field, Index, Path}; + + match e.kind { + Path(ref qpath) => { + if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) { + false + } else { + true + } + }, + Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner), + _ => false, + } +} + +fn mutates_static<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, body: &'tcx hir::Body<'_>) -> bool { + let mut v = StaticMutVisitor { + cx, + mutates_static: false, + }; + intravisit::walk_expr(&mut v, &body.value); + v.mutates_static +} diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs new file mode 100644 index 0000000000000..704a95ec0a090 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -0,0 +1,110 @@ +use crate::utils; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Opaque, Predicate::Trait, ToPolyTraitRef}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Span}; +use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; +use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine}; + +declare_clippy_lint! { + /// **What it does:** This lint requires Future implementations returned from + /// functions and methods to implement the `Send` marker trait. It is mostly + /// used by library authors (public and internal) that target an audience where + /// multithreaded executors are likely to be used for running these Futures. + /// + /// **Why is this bad?** A Future implementation captures some state that it + /// needs to eventually produce its final value. When targeting a multithreaded + /// executor (which is the norm on non-embedded devices) this means that this + /// state may need to be transported to other threads, in other words the + /// whole Future needs to implement the `Send` marker trait. If it does not, + /// then the resulting Future cannot be submitted to a thread pool in the + /// end user’s code. + /// + /// Especially for generic functions it can be confusing to leave the + /// discovery of this problem to the end user: the reported error location + /// will be far from its cause and can in many cases not even be fixed without + /// modifying the library where the offending Future implementation is + /// produced. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// async fn not_send(bytes: std::rc::Rc<[u8]>) {} + /// ``` + /// Use instead: + /// ```rust + /// async fn is_send(bytes: std::sync::Arc<[u8]>) {} + /// ``` + pub FUTURE_NOT_SEND, + nursery, + "public Futures must be Send" +} + +declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FutureNotSend { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'tcx>, + _: &'tcx Body<'tcx>, + _: Span, + hir_id: HirId, + ) { + if let FnKind::Closure(_) = kind { + return; + } + let ret_ty = utils::return_ty(cx, hir_id); + if let Opaque(id, subst) = ret_ty.kind { + let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst); + let mut is_future = false; + for p in preds.predicates { + if let Some(trait_ref) = p.to_opt_poly_trait_ref() { + if Some(trait_ref.def_id()) == cx.tcx.lang_items().future_trait() { + is_future = true; + break; + } + } + } + if is_future { + let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap(); + let span = decl.output.span(); + let send_result = cx.tcx.infer_ctxt().enter(|infcx| { + let cause = traits::ObligationCause::misc(span, hir_id); + let mut fulfillment_cx = traits::FulfillmentContext::new(); + fulfillment_cx.register_bound(&infcx, cx.param_env, ret_ty, send_trait, cause); + fulfillment_cx.select_all_or_error(&infcx) + }); + if let Err(send_errors) = send_result { + utils::span_lint_and_then( + cx, + FUTURE_NOT_SEND, + span, + "future cannot be sent between threads safely", + |db| { + cx.tcx.infer_ctxt().enter(|infcx| { + for FulfillmentError { obligation, .. } in send_errors { + infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred, _) = obligation.predicate { + let trait_ref = trait_pred.to_poly_trait_ref(); + db.note(&*format!( + "`{}` doesn't implement `{}`", + trait_ref.self_ty(), + trait_ref.print_only_trait_path(), + )); + } + } + }) + }, + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/get_last_with_len.rs b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs new file mode 100644 index 0000000000000..c32e0a2290d12 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs @@ -0,0 +1,103 @@ +//! lint on using `x.get(x.len() - 1)` instead of `x.last()` + +use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of + /// `x.last()`. + /// + /// **Why is this bad?** Using `x.last()` is easier to read and has the same + /// result. + /// + /// Note that using `x[x.len() - 1]` is semantically different from + /// `x.last()`. Indexing into the array will panic on out-of-bounds + /// accesses, while `x.get()` and `x.last()` will return `None`. + /// + /// There is another lint (get_unwrap) that covers the case of using + /// `x.get(index).unwrap()` instead of `x[index]`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let x = vec![2, 3, 5]; + /// let last_element = x.get(x.len() - 1); + /// + /// // Good + /// let x = vec![2, 3, 5]; + /// let last_element = x.last(); + /// ``` + pub GET_LAST_WITH_LEN, + complexity, + "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler" +} + +declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for GetLastWithLen { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Is a method call + if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind; + + // Method name is "get" + if path.ident.name == sym!(get); + + // Argument 0 (the struct we're calling the method on) is a vector + if let Some(struct_calling_on) = args.get(0); + let struct_ty = cx.tables.expr_ty(struct_calling_on); + if is_type_diagnostic_item(cx, struct_ty, sym!(vec_type)); + + // Argument to "get" is a subtraction + if let Some(get_index_arg) = args.get(1); + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, + .. + }, + lhs, + rhs, + ) = &get_index_arg.kind; + + // LHS of subtraction is "x.len()" + if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args) = &lhs.kind; + if arg_lhs_path.ident.name == sym!(len); + if let Some(arg_lhs_struct) = lhs_args.get(0); + + // The two vectors referenced (x in x.get(...) and in x.len()) + if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct); + + // RHS of subtraction is 1 + if let ExprKind::Lit(rhs_lit) = &rhs.kind; + if let LitKind::Int(1, ..) = rhs_lit.node; + + then { + let mut applicability = Applicability::MachineApplicable; + let vec_name = snippet_with_applicability( + cx, + struct_calling_on.span, "vec", + &mut applicability, + ); + + span_lint_and_sugg( + cx, + GET_LAST_WITH_LEN, + expr.span, + &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name), + "try", + format!("{}.last()", vec_name), + applicability, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/identity_op.rs new file mode 100644 index 0000000000000..78e07d25f67c5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/identity_op.rs @@ -0,0 +1,100 @@ +use if_chain::if_chain; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +use crate::consts::{constant_simple, Constant}; +use crate::utils::{clip, snippet, span_lint, unsext}; + +declare_clippy_lint! { + /// **What it does:** Checks for identity operations, e.g., `x + 0`. + /// + /// **Why is this bad?** This code can be removed without changing the + /// meaning. So it just obscures what's going on. Delete it mercilessly. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// x / 1 + 0 * 1 - 0 | 0; + /// ``` + pub IDENTITY_OP, + complexity, + "using identity operations, e.g., `x + 0` or `y / 1`" +} + +declare_lint_pass!(IdentityOp => [IDENTITY_OP]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + if let ExprKind::Binary(cmp, ref left, ref right) = e.kind { + if is_allowed(cx, cmp, left, right) { + return; + } + match cmp.node { + BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { + check(cx, left, 0, e.span, right.span); + check(cx, right, 0, e.span, left.span); + }, + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span), + BinOpKind::Mul => { + check(cx, left, 1, e.span, right.span); + check(cx, right, 1, e.span, left.span); + }, + BinOpKind::Div => check(cx, right, 1, e.span, left.span), + BinOpKind::BitAnd => { + check(cx, left, -1, e.span, right.span); + check(cx, right, -1, e.span, left.span); + }, + _ => (), + } + } + } +} + +fn is_allowed(cx: &LateContext<'_, '_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { + // `1 << 0` is a common pattern in bit manipulation code + if_chain! { + if let BinOpKind::Shl = cmp.node; + if let Some(Constant::Int(0)) = constant_simple(cx, cx.tables, right); + if let Some(Constant::Int(1)) = constant_simple(cx, cx.tables, left); + then { + return true; + } + } + + false +} + +#[allow(clippy::cast_possible_wrap)] +fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { + if let Some(Constant::Int(v)) = constant_simple(cx, cx.tables, e) { + let check = match cx.tables.expr_ty(e).kind { + ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), + ty::Uint(uty) => clip(cx.tcx, !0, uty), + _ => return, + }; + if match m { + 0 => v == 0, + -1 => v == check, + 1 => v == 1, + _ => unreachable!(), + } { + span_lint( + cx, + IDENTITY_OP, + span, + &format!( + "the operation is ineffective. Consider reducing it to `{}`", + snippet(cx, arg, "..") + ), + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs new file mode 100644 index 0000000000000..ae92a96d16347 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -0,0 +1,160 @@ +use crate::utils::{is_type_diagnostic_item, span_lint_and_help, SpanlessEq}; +use if_chain::if_chain; +use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ExprKind, MatchSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `Mutex::lock` calls in `if let` expression + /// with lock calls in any of the else blocks. + /// + /// **Why is this bad?** The Mutex lock remains held for the whole + /// `if let ... else` block and deadlocks. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// if let Ok(thing) = mutex.lock() { + /// do_thing(); + /// } else { + /// mutex.lock(); + /// } + /// ``` + /// Should be written + /// ```rust,ignore + /// let locked = mutex.lock(); + /// if let Ok(thing) = locked { + /// do_thing(thing); + /// } else { + /// use_locked(locked); + /// } + /// ``` + pub IF_LET_MUTEX, + correctness, + "locking a `Mutex` in an `if let` block can cause deadlocks" +} + +declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]); + +impl LateLintPass<'_, '_> for IfLetMutex { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, ex: &'_ Expr<'_>) { + let mut arm_visit = ArmVisitor { + mutex_lock_called: false, + found_mutex: None, + cx, + }; + let mut op_visit = OppVisitor { + mutex_lock_called: false, + found_mutex: None, + cx, + }; + if let ExprKind::Match( + ref op, + ref arms, + MatchSource::IfLetDesugar { + contains_else_clause: true, + }, + ) = ex.kind + { + op_visit.visit_expr(op); + if op_visit.mutex_lock_called { + for arm in *arms { + arm_visit.visit_arm(arm); + } + + if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) { + span_lint_and_help( + cx, + IF_LET_MUTEX, + ex.span, + "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", + None, + "move the lock call outside of the `if let ...` expression", + ); + } + } + } + } +} + +/// Checks if `Mutex::lock` is called in the `if let _ = expr. +pub struct OppVisitor<'tcx, 'l> { + mutex_lock_called: bool, + found_mutex: Option<&'tcx Expr<'tcx>>, + cx: &'tcx LateContext<'tcx, 'l>, +} + +impl<'tcx, 'l> Visitor<'tcx> for OppVisitor<'tcx, 'l> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if_chain! { + if let Some(mutex) = is_mutex_lock_call(self.cx, expr); + then { + self.found_mutex = Some(mutex); + self.mutex_lock_called = true; + return; + } + } + visit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Checks if `Mutex::lock` is called in any of the branches. +pub struct ArmVisitor<'tcx, 'l> { + mutex_lock_called: bool, + found_mutex: Option<&'tcx Expr<'tcx>>, + cx: &'tcx LateContext<'tcx, 'l>, +} + +impl<'tcx, 'l> Visitor<'tcx> for ArmVisitor<'tcx, 'l> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let Some(mutex) = is_mutex_lock_call(self.cx, expr); + then { + self.found_mutex = Some(mutex); + self.mutex_lock_called = true; + return; + } + } + visit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { + fn same_mutex(&self, cx: &LateContext<'_, '_>, op_mutex: &Expr<'_>) -> bool { + if let Some(arm_mutex) = self.found_mutex { + SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) + } else { + false + } + } +} + +fn is_mutex_lock_call<'a>(cx: &LateContext<'a, '_>, expr: &'a Expr<'_>) -> Option<&'a Expr<'a>> { + if_chain! { + if let ExprKind::MethodCall(path, _span, args) = &expr.kind; + if path.ident.to_string() == "lock"; + let ty = cx.tables.expr_ty(&args[0]); + if is_type_diagnostic_item(cx, ty, sym!(mutex_type)); + then { + Some(&args[0]) + } else { + None + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs new file mode 100644 index 0000000000000..9b13f7609247a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs @@ -0,0 +1,72 @@ +use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:*** Checks for unnecessary `ok()` in if let. + /// + /// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match + /// on `Ok(pat)` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// for i in iter { + /// if let Some(value) = i.parse().ok() { + /// vec.push(value) + /// } + /// } + /// ``` + /// Could be written: + /// + /// ```ignore + /// for i in iter { + /// if let Ok(value) = i.parse() { + /// vec.push(value) + /// } + /// } + /// ``` + pub IF_LET_SOME_RESULT, + style, + "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead" +} + +declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OkIfLet { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { //begin checking variables + if let ExprKind::Match(ref op, ref body, source) = expr.kind; //test if expr is a match + if let MatchSource::IfLetDesugar { .. } = source; //test if it is an If Let + if let ExprKind::MethodCall(_, ok_span, ref result_types) = op.kind; //check is expr.ok() has type Result.ok() + if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation + if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; + if is_type_diagnostic_item(cx, cx.tables.expr_ty(&result_types[0]), sym!(result_type)); + if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; + + then { + let mut applicability = Applicability::MachineApplicable; + let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); + let trimmed_ok = snippet_with_applicability(cx, op.span.until(ok_span), "", &mut applicability); + let sugg = format!( + "if let Ok({}) = {}", + some_expr_string, + trimmed_ok.trim().trim_end_matches('.'), + ); + span_lint_and_sugg( + cx, + IF_LET_SOME_RESULT, + expr.span.with_hi(op.span.hi()), + "Matching on `Some` with `ok()` is redundant", + &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + sugg, + applicability, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs new file mode 100644 index 0000000000000..c11e291f98e4b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -0,0 +1,83 @@ +//! lint on if branches that could be swapped so no `!` operation is necessary +//! on the condition + +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::span_lint_and_help; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `!` or `!=` in an if condition with an + /// else branch. + /// + /// **Why is this bad?** Negations reduce the readability of statements. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let v: Vec = vec![]; + /// # fn a() {} + /// # fn b() {} + /// if !v.is_empty() { + /// a() + /// } else { + /// b() + /// } + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # let v: Vec = vec![]; + /// # fn a() {} + /// # fn b() {} + /// if v.is_empty() { + /// b() + /// } else { + /// a() + /// } + /// ``` + pub IF_NOT_ELSE, + pedantic, + "`if` branches that could be swapped so no negation operation is necessary on the condition" +} + +declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); + +impl EarlyLintPass for IfNotElse { + fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { + if in_external_macro(cx.sess(), item.span) { + return; + } + if let ExprKind::If(ref cond, _, Some(ref els)) = item.kind { + if let ExprKind::Block(..) = els.kind { + match cond.kind { + ExprKind::Unary(UnOp::Not, _) => { + span_lint_and_help( + cx, + IF_NOT_ELSE, + item.span, + "Unnecessary boolean `not` operation", + None, + "remove the `!` and swap the blocks of the `if`/`else`", + ); + }, + ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => { + span_lint_and_help( + cx, + IF_NOT_ELSE, + item.span, + "Unnecessary `!=` operation", + None, + "change to `==` and swap the blocks of the `if`/`else`", + ); + }, + _ => (), + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs new file mode 100644 index 0000000000000..c4308fd26a302 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -0,0 +1,150 @@ +use crate::utils::{ + fn_has_unsatisfiable_preds, match_def_path, + paths::{BEGIN_PANIC, BEGIN_PANIC_FMT}, + snippet_opt, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for missing return statements at the end of a block. + /// + /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers + /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss + /// the last returning statement because the only difference is a missing `;`. Especially in bigger + /// code with multiple return paths having a `return` keyword makes it easier to find the + /// corresponding statements. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + /// add return + /// ```rust + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + pub IMPLICIT_RETURN, + restriction, + "use a return statement like `return expr` instead of an expression" +} + +declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); + +static LINT_BREAK: &str = "change `break` to `return` as shown"; +static LINT_RETURN: &str = "add `return` as shown"; + +fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) { + let outer_span = outer_span.source_callsite(); + let inner_span = inner_span.source_callsite(); + + span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion( + outer_span, + msg, + format!("return {}", snippet), + Applicability::MachineApplicable, + ); + } + }); +} + +fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + match expr.kind { + // loops could be using `break` instead of `return` + ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => { + if let Some(expr) = &block.expr { + expr_match(cx, expr); + } + // only needed in the case of `break` with `;` at the end + else if let Some(stmt) = block.stmts.last() { + if_chain! { + if let StmtKind::Semi(expr, ..) = &stmt.kind; + // make sure it's a break, otherwise we want to skip + if let ExprKind::Break(.., break_expr) = &expr.kind; + if let Some(break_expr) = break_expr; + then { + lint(cx, expr.span, break_expr.span, LINT_BREAK); + } + } + } + }, + // use `return` instead of `break` + ExprKind::Break(.., break_expr) => { + if let Some(break_expr) = break_expr { + lint(cx, expr.span, break_expr.span, LINT_BREAK); + } + }, + ExprKind::Match(.., arms, source) => { + let check_all_arms = match source { + MatchSource::IfLetDesugar { + contains_else_clause: has_else, + } => has_else, + _ => true, + }; + + if check_all_arms { + for arm in arms { + expr_match(cx, &arm.body); + } + } else { + expr_match(cx, &arms.first().expect("`if let` doesn't have a single arm").body); + } + }, + // skip if it already has a return statement + ExprKind::Ret(..) => (), + // make sure it's not a call that panics + ExprKind::Call(expr, ..) => { + if_chain! { + if let ExprKind::Path(qpath) = &expr.kind; + if let Some(path_def_id) = cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id(); + if match_def_path(cx, path_def_id, &BEGIN_PANIC) || + match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT); + then { } + else { + lint(cx, expr.span, expr.span, LINT_RETURN) + } + } + }, + // everything else is missing `return` + _ => lint(cx, expr.span, expr.span, LINT_RETURN), + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: FnKind<'tcx>, + _: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + _: HirId, + ) { + let def_id = cx.tcx.hir().body_owner_def_id(body.id()); + + // Building MIR for `fn`s with unsatisfiable preds results in ICE. + if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) { + return; + } + + let mir = cx.tcx.optimized_mir(def_id.to_def_id()); + + // checking return type through MIR, HIR is not able to determine inferred closure return types + // make sure it's not a macro + if !mir.return_ty().is_unit() && !span.from_expansion() { + expr_match(cx, &body.value); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs new file mode 100644 index 0000000000000..155a93de4facf --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -0,0 +1,173 @@ +use crate::utils::{higher, in_macro, match_qpath, span_lint_and_sugg, SpanlessEq}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for implicit saturating subtraction. + /// + /// **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let end: u32 = 10; + /// let start: u32 = 5; + /// + /// let mut i: u32 = end - start; + /// + /// // Bad + /// if i != 0 { + /// i -= 1; + /// } + /// ``` + /// Use instead: + /// ```rust + /// let end: u32 = 10; + /// let start: u32 = 5; + /// + /// let mut i: u32 = end - start; + /// + /// // Good + /// i = i.saturating_sub(1); + /// ``` + pub IMPLICIT_SATURATING_SUB, + pedantic, + "Perform saturating subtraction instead of implicitly checking lower bound of data type" +} + +declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitSaturatingSub { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) { + if in_macro(expr.span) { + return; + } + if_chain! { + if let Some((ref cond, ref then, None)) = higher::if_block(&expr); + + // Check if the conditional expression is a binary operation + if let ExprKind::Binary(ref cond_op, ref cond_left, ref cond_right) = cond.kind; + + // Ensure that the binary operator is >, != and < + if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; + + // Check if the true condition block has only one statement + if let ExprKind::Block(ref block, _) = then.kind; + if block.stmts.len() == 1 && block.expr.is_none(); + + // Check if assign operation is done + if let StmtKind::Semi(ref e) = block.stmts[0].kind; + if let Some(target) = subtracts_one(cx, e); + + // Extracting out the variable name + if let ExprKind::Path(ref assign_path) = target.kind; + if let QPath::Resolved(_, ref ares_path) = assign_path; + + then { + // Handle symmetric conditions in the if statement + let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { + if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { + (cond_left, cond_right) + } else { + return; + } + } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { + if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { + (cond_right, cond_left) + } else { + return; + } + } else { + return; + }; + + // Check if the variable in the condition statement is an integer + if !cx.tables.expr_ty(cond_var).is_integral() { + return; + } + + // Get the variable name + let var_name = ares_path.segments[0].ident.name.as_str(); + const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"]; + + match cond_num_val.kind { + ExprKind::Lit(ref cond_lit) => { + // Check if the constant is zero + if let LitKind::Int(0, _) = cond_lit.node { + if cx.tables.expr_ty(cond_left).is_signed() { + } else { + print_lint_and_sugg(cx, &var_name, expr); + }; + } + }, + ExprKind::Path(ref cond_num_path) => { + if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) { + print_lint_and_sugg(cx, &var_name, expr); + }; + }, + ExprKind::Call(ref func, _) => { + if let ExprKind::Path(ref cond_num_path) = func.kind { + if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) { + print_lint_and_sugg(cx, &var_name, expr); + } + }; + }, + _ => (), + } + } + } + } +} + +fn subtracts_one<'a>(cx: &LateContext<'_, '_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> { + match expr.kind { + ExprKind::AssignOp(ref op1, ref target, ref value) => { + if_chain! { + if BinOpKind::Sub == op1.node; + // Check if literal being subtracted is one + if let ExprKind::Lit(ref lit1) = value.kind; + if let LitKind::Int(1, _) = lit1.node; + then { + Some(target) + } else { + None + } + } + }, + ExprKind::Assign(ref target, ref value, _) => { + if_chain! { + if let ExprKind::Binary(ref op1, ref left1, ref right1) = value.kind; + if BinOpKind::Sub == op1.node; + + if SpanlessEq::new(cx).eq_expr(left1, target); + + if let ExprKind::Lit(ref lit1) = right1.kind; + if let LitKind::Int(1, _) = lit1.node; + then { + Some(target) + } else { + None + } + } + }, + _ => None, + } +} + +fn print_lint_and_sugg(cx: &LateContext<'_, '_>, var_name: &str, expr: &Expr<'_>) { + span_lint_and_sugg( + cx, + IMPLICIT_SATURATING_SUB, + expr.span, + "Implicitly performing saturating subtraction", + "try", + format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()), + Applicability::MachineApplicable, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs new file mode 100644 index 0000000000000..c5808dd540b6f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -0,0 +1,193 @@ +//! lint on indexing and slicing operations + +use crate::consts::{constant, Constant}; +use crate::utils::{higher, span_lint, span_lint_and_help}; +use rustc_ast::ast::RangeLimits; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for out of bounds array indexing with a constant + /// index. + /// + /// **Why is this bad?** This will always panic at runtime. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```no_run + /// # #![allow(const_err)] + /// let x = [1, 2, 3, 4]; + /// + /// // Bad + /// x[9]; + /// &x[2..9]; + /// + /// // Good + /// x[0]; + /// x[3]; + /// ``` + pub OUT_OF_BOUNDS_INDEXING, + correctness, + "out of bounds constant indexing" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint + /// does report on arrays if we can tell that slicing operations are in bounds and does not + /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint. + /// + /// **Why is this bad?** Indexing and slicing can panic at runtime and there are + /// safe alternatives. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```rust,no_run + /// // Vector + /// let x = vec![0; 5]; + /// + /// // Bad + /// x[2]; + /// &x[2..100]; + /// &x[2..]; + /// &x[..100]; + /// + /// // Good + /// x.get(2); + /// x.get(2..100); + /// x.get(2..); + /// x.get(..100); + /// + /// // Array + /// let y = [0, 1, 2, 3]; + /// + /// // Bad + /// &y[10..100]; + /// &y[10..]; + /// &y[..100]; + /// + /// // Good + /// &y[2..]; + /// &y[..2]; + /// &y[0..3]; + /// y.get(10); + /// y.get(10..100); + /// y.get(10..); + /// y.get(..100); + /// ``` + pub INDEXING_SLICING, + restriction, + "indexing/slicing usage" +} + +declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Index(ref array, ref index) = &expr.kind { + let ty = cx.tables.expr_ty(array); + if let Some(range) = higher::range(cx, index) { + // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] + if let ty::Array(_, s) = ty.kind { + let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) { + size.into() + } else { + return; + }; + + 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(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(_), Some(_)) = const_range { + // early return because both start and end are constants + // and we have proven above that they are in bounds + return; + } + } + + let help_msg = match (range.start, range.end) { + (None, Some(_)) => "Consider using `.get(..n)`or `.get_mut(..n)` instead", + (Some(_), None) => "Consider using `.get(n..)` or .get_mut(n..)` instead", + (Some(_), Some(_)) => "Consider using `.get(n..m)` or `.get_mut(n..m)` instead", + (None, None) => return, // [..] is ok. + }; + + span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg); + } else { + // Catchall non-range index, i.e., [n] or [n << m] + if let ty::Array(..) = ty.kind { + // Index is a constant uint. + if let Some(..) = constant(cx, cx.tables, index) { + // Let rustc's `const_err` lint handle constant `usize` indexing on arrays. + return; + } + } + + span_lint_and_help( + cx, + INDEXING_SLICING, + expr.span, + "indexing may panic.", + None, + "Consider using `.get(n)` or `.get_mut(n)` instead", + ); + } + } + } +} + +/// Returns a tuple of options with the start and end (exclusive) values of +/// the range. If the start or end is not constant, None is returned. +fn to_const_range<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + range: higher::Range<'_>, + array_size: u128, +) -> (Option, Option) { + let s = range.start.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c)); + let start = match s { + Some(Some(Constant::Int(x))) => Some(x), + Some(_) => None, + None => Some(0), + }; + + let e = range.end.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c)); + let end = match e { + Some(Some(Constant::Int(x))) => { + if range.limits == RangeLimits::Closed { + Some(x + 1) + } else { + Some(x) + } + }, + Some(_) => None, + None => Some(array_size), + }; + + (start, end) +} diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs new file mode 100644 index 0000000000000..cd989c0ea6f67 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -0,0 +1,253 @@ +use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for iteration that is guaranteed to be infinite. + /// + /// **Why is this bad?** While there may be places where this is acceptable + /// (e.g., in event streams), in most cases this is simply an error. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// use std::iter; + /// + /// iter::repeat(1_u8).collect::>(); + /// ``` + pub INFINITE_ITER, + correctness, + "infinite iteration" +} + +declare_clippy_lint! { + /// **What it does:** Checks for iteration that may be infinite. + /// + /// **Why is this bad?** While there may be places where this is acceptable + /// (e.g., in event streams), in most cases this is simply an error. + /// + /// **Known problems:** The code may have a condition to stop iteration, but + /// this lint is not clever enough to analyze it. + /// + /// **Example:** + /// ```rust + /// let infinite_iter = 0..; + /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); + /// ``` + pub MAYBE_INFINITE_ITER, + pedantic, + "possible infinite iteration" +} + +declare_lint_pass!(InfiniteIter => [INFINITE_ITER, MAYBE_INFINITE_ITER]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InfiniteIter { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + let (lint, msg) = match complete_infinite_iter(cx, expr) { + Infinite => (INFINITE_ITER, "infinite iteration detected"), + MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"), + Finite => { + return; + }, + }; + span_lint(cx, lint, expr.span, msg) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum Finiteness { + Infinite, + MaybeInfinite, + Finite, +} + +use self::Finiteness::{Finite, Infinite, MaybeInfinite}; + +impl Finiteness { + #[must_use] + fn and(self, b: Self) -> Self { + match (self, b) { + (Finite, _) | (_, Finite) => Finite, + (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite, + _ => Infinite, + } + } + + #[must_use] + fn or(self, b: Self) -> Self { + match (self, b) { + (Infinite, _) | (_, Infinite) => Infinite, + (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite, + _ => Finite, + } + } +} + +impl From for Finiteness { + #[must_use] + fn from(b: bool) -> Self { + if b { + Infinite + } else { + Finite + } + } +} + +/// This tells us what to look for to know if the iterator returned by +/// this method is infinite +#[derive(Copy, Clone)] +enum Heuristic { + /// infinite no matter what + Always, + /// infinite if the first argument is + First, + /// infinite if any of the supplied arguments is + Any, + /// infinite if all of the supplied arguments are + All, +} + +use self::Heuristic::{All, Always, Any, First}; + +/// a slice of (method name, number of args, heuristic, bounds) tuples +/// that will be used to determine whether the method in question +/// 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", 2, All, Infinite), + ("chain", 2, Any, Infinite), + ("cycle", 1, Always, Infinite), + ("map", 2, First, Infinite), + ("by_ref", 1, First, Infinite), + ("cloned", 1, First, Infinite), + ("rev", 1, First, Infinite), + ("inspect", 1, First, Infinite), + ("enumerate", 1, First, Infinite), + ("peekable", 2, First, Infinite), + ("fuse", 1, First, Infinite), + ("skip", 2, First, Infinite), + ("skip_while", 1, First, Infinite), + ("filter", 2, First, Infinite), + ("filter_map", 2, First, Infinite), + ("flat_map", 2, First, Infinite), + ("unzip", 1, First, Infinite), + ("take_while", 2, First, MaybeInfinite), + ("scan", 3, First, MaybeInfinite), +]; + +fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Finiteness { + match expr.kind { + ExprKind::MethodCall(ref method, _, ref args) => { + for &(name, len, heuristic, cap) in &HEURISTICS { + if method.ident.name.as_str() == name && args.len() == len { + return (match heuristic { + Always => Infinite, + First => is_infinite(cx, &args[0]), + Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])), + All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])), + }) + .and(cap); + } + } + if method.ident.name == sym!(flat_map) && args.len() == 2 { + if let ExprKind::Closure(_, _, body_id, _, _) = args[1].kind { + let body = cx.tcx.hir().body(body_id); + return is_infinite(cx, &body.value); + } + } + Finite + }, + ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), + ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) => is_infinite(cx, e), + ExprKind::Call(ref path, _) => { + if let ExprKind::Path(ref qpath) = path.kind { + match_qpath(qpath, &paths::REPEAT).into() + } else { + Finite + } + }, + ExprKind::Struct(..) => higher::range(cx, expr).map_or(false, |r| r.end.is_none()).into(), + _ => Finite, + } +} + +/// the names and argument lengths of methods that *may* exhaust their +/// iterators +const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [ + ("find", 2), + ("rfind", 2), + ("position", 2), + ("rposition", 2), + ("any", 2), + ("all", 2), +]; + +/// the names and argument lengths of methods that *always* exhaust +/// their iterators +const COMPLETING_METHODS: [(&str, usize); 12] = [ + ("count", 1), + ("fold", 3), + ("for_each", 2), + ("partition", 2), + ("max", 1), + ("max_by", 2), + ("max_by_key", 2), + ("min", 1), + ("min_by", 2), + ("min_by_key", 2), + ("sum", 1), + ("product", 1), +]; + +/// the paths of types that are known to be infinitely allocating +const INFINITE_COLLECTORS: [&[&str]; 8] = [ + &paths::BINARY_HEAP, + &paths::BTREEMAP, + &paths::BTREESET, + &paths::HASHMAP, + &paths::HASHSET, + &paths::LINKED_LIST, + &paths::VEC, + &paths::VEC_DEQUE, +]; + +fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Finiteness { + match expr.kind { + ExprKind::MethodCall(ref method, _, ref args) => { + for &(name, len) in &COMPLETING_METHODS { + if method.ident.name.as_str() == name && args.len() == len { + return is_infinite(cx, &args[0]); + } + } + for &(name, len) in &POSSIBLY_COMPLETING_METHODS { + if method.ident.name.as_str() == name && args.len() == len { + return MaybeInfinite.and(is_infinite(cx, &args[0])); + } + } + if method.ident.name == sym!(last) && args.len() == 1 { + let not_double_ended = get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR) + .map_or(false, |id| !implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[])); + if not_double_ended { + return is_infinite(cx, &args[0]); + } + } else if method.ident.name == sym!(collect) { + let ty = cx.tables.expr_ty(expr); + if INFINITE_COLLECTORS.iter().any(|path| match_type(cx, ty, path)) { + return is_infinite(cx, &args[0]); + } + } + }, + ExprKind::Binary(op, ref l, ref r) => { + if op.node.is_comparison() { + return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); + } + }, // TODO: ExprKind::Loop + Match + _ => (), + } + Finite +} diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs new file mode 100644 index 0000000000000..7e2975ac2ae90 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -0,0 +1,94 @@ +//! lint on inherent implementations + +use crate::utils::{in_macro, span_lint_and_then}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{def_id, Crate, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for multiple inherent implementations of a struct + /// + /// **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// struct X; + /// impl X { + /// fn one() {} + /// } + /// impl X { + /// fn other() {} + /// } + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// struct X; + /// impl X { + /// fn one() {} + /// fn other() {} + /// } + /// ``` + pub MULTIPLE_INHERENT_IMPL, + restriction, + "Multiple inherent impl that could be grouped" +} + +#[allow(clippy::module_name_repetitions)] +#[derive(Default)] +pub struct MultipleInherentImpl { + impls: FxHashMap, +} + +impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MultipleInherentImpl { + fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Impl { + ref generics, + of_trait: None, + .. + } = item.kind + { + // Remember for each inherent implementation encoutered its span and generics + // but filter out implementations that have generic params (type or lifetime) + // or are derived from a macro + if !in_macro(item.span) && generics.params.is_empty() { + self.impls.insert(item.hir_id.owner.to_def_id(), item.span); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx Crate<'_>) { + if let Some(item) = krate.items.values().next() { + // Retrieve all inherent implementations from the crate, grouped by type + for impls in cx + .tcx + .crate_inherent_impls(item.hir_id.owner.to_def_id().krate) + .inherent_impls + .values() + { + // Filter out implementations that have generic params (type or lifetime) + let mut impl_spans = impls.iter().filter_map(|impl_def| self.impls.get(impl_def)); + if let Some(initial_span) = impl_spans.next() { + impl_spans.for_each(|additional_span| { + span_lint_and_then( + cx, + MULTIPLE_INHERENT_IMPL, + *additional_span, + "Multiple implementations of this structure", + |diag| { + diag.span_note(*initial_span, "First implementation here"); + }, + ) + }) + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs new file mode 100644 index 0000000000000..289628a2752af --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -0,0 +1,156 @@ +use if_chain::if_chain; +use rustc_hir::{ImplItem, ImplItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{ + get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, + trait_ref_of_method, walk_ptrs_ty, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. + /// + /// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred. + /// + /// **Known problems:** None + /// + /// ** Example:** + /// + /// ```rust + /// // Bad + /// pub struct A; + /// + /// impl A { + /// pub fn to_string(&self) -> String { + /// "I am A".to_string() + /// } + /// } + /// ``` + /// + /// ```rust + /// // Good + /// use std::fmt; + /// + /// pub struct A; + /// + /// impl fmt::Display for A { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "I am A") + /// } + /// } + /// ``` + pub INHERENT_TO_STRING, + style, + "type implements inherent method `to_string()`, but should instead implement the `Display` trait" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait. + /// + /// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`. + /// + /// **Known problems:** None + /// + /// ** Example:** + /// + /// ```rust + /// // Bad + /// use std::fmt; + /// + /// pub struct A; + /// + /// impl A { + /// pub fn to_string(&self) -> String { + /// "I am A".to_string() + /// } + /// } + /// + /// impl fmt::Display for A { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "I am A, too") + /// } + /// } + /// ``` + /// + /// ```rust + /// // Good + /// use std::fmt; + /// + /// pub struct A; + /// + /// impl fmt::Display for A { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "I am A") + /// } + /// } + /// ``` + pub INHERENT_TO_STRING_SHADOW_DISPLAY, + correctness, + "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait" +} + +declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InherentToString { + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) { + if impl_item.span.from_expansion() { + return; + } + + if_chain! { + // Check if item is a method, called to_string and has a parameter 'self' + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; + if impl_item.ident.name.as_str() == "to_string"; + let decl = &signature.decl; + if decl.implicit_self.has_implicit_self(); + if decl.inputs.len() == 1; + + // Check if return type is String + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); + + // Filters instances of to_string which are required by a trait + if trait_ref_of_method(cx, impl_item.hir_id).is_none(); + + then { + show_lint(cx, impl_item); + } + } + } +} + +fn show_lint(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) { + let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!"); + + // Get the real type of 'self' + let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id); + let self_type = cx.tcx.fn_sig(fn_def_id).input(0); + let self_type = walk_ptrs_ty(self_type.skip_binder()); + + // Emit either a warning or an error + if implements_trait(cx, self_type, display_trait_id, &[]) { + span_lint_and_help( + cx, + INHERENT_TO_STRING_SHADOW_DISPLAY, + item.span, + &format!( + "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", + self_type.to_string() + ), + None, + &format!("remove the inherent method from type `{}`", self_type.to_string()) + ); + } else { + span_lint_and_help( + cx, + INHERENT_TO_STRING, + item.span, + &format!( + "implementation of inherent method `to_string(&self) -> String` for type `{}`", + self_type.to_string() + ), + None, + &format!("implement trait `Display` for type `{}` instead", self_type.to_string()), + ); + } +} 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 new file mode 100644 index 0000000000000..475610dda4753 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -0,0 +1,58 @@ +//! checks for `#[inline]` on trait methods without bodies + +use crate::utils::span_lint_and_then; +use crate::utils::sugg::DiagnosticBuilderExt; +use rustc_ast::ast::Attribute; +use rustc_errors::Applicability; +use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// **What it does:** Checks for `#[inline]` on trait methods without bodies + /// + /// **Why is this bad?** Only implementations of trait methods may be inlined. + /// The inline attribute is ignored for trait methods without bodies. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// trait Animal { + /// #[inline] + /// fn name(&self) -> &'static str; + /// } + /// ``` + pub INLINE_FN_WITHOUT_BODY, + correctness, + "use of `#[inline]` on trait methods without bodies" +} + +declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InlineFnWithoutBody { + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind { + check_attrs(cx, item.ident.name, &item.attrs); + } + } +} + +fn check_attrs(cx: &LateContext<'_, '_>, name: Symbol, attrs: &[Attribute]) { + for attr in attrs { + if !attr.check_name(sym!(inline)) { + continue; + } + + span_lint_and_then( + cx, + INLINE_FN_WITHOUT_BODY, + attr.span, + &format!("use of `#[inline]` on trait method `{}` which has no body", name), + |diag| { + diag.suggest_remove_item(cx, attr.span, "remove", 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 new file mode 100644 index 0000000000000..d5dbd495680b2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs @@ -0,0 +1,172 @@ +//! lint on blocks unnecessarily using >= with a + 1 or - 1 + +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{snippet_opt, span_lint_and_sugg}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block + /// + /// + /// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// # let y = 1; + /// if x >= y + 1 {} + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// # let x = 1; + /// # let y = 1; + /// if x > y {} + /// ``` + pub INT_PLUS_ONE, + complexity, + "instead of using `x >= y + 1`, use `x > y`" +} + +declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]); + +// cases: +// BinOpKind::Ge +// x >= y + 1 +// x - 1 >= y +// +// BinOpKind::Le +// x + 1 <= y +// x <= y - 1 + +#[derive(Copy, Clone)] +enum Side { + LHS, + RHS, +} + +impl IntPlusOne { + #[allow(clippy::cast_sign_loss)] + fn check_lit(lit: &Lit, target_value: i128) -> bool { + if let LitKind::Int(value, ..) = lit.kind { + return value == (target_value as u128); + } + false + } + + fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option { + match (binop, &lhs.kind, &rhs.kind) { + // case where `x - 1 >= ...` or `-1 + x >= ...` + (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { + match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { + // `-1 + x` + (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) + }, + // `x - 1` + (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) + }, + _ => None, + } + }, + // case where `... >= y + 1` or `... >= 1 + y` + (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) + if rhskind.node == BinOpKind::Add => + { + match (&rhslhs.kind, &rhsrhs.kind) { + // `y + 1` and `1 + y` + (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) + }, + (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) + }, + _ => None, + } + } + // case where `x + 1 <= ...` or `1 + x <= ...` + (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) + if lhskind.node == BinOpKind::Add => + { + match (&lhslhs.kind, &lhsrhs.kind) { + // `1 + x` and `x + 1` + (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) + }, + (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) + }, + _ => None, + } + } + // case where `... >= y - 1` or `... >= -1 + y` + (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { + match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { + // `-1 + y` + (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) + }, + // `y - 1` + (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) + }, + _ => None, + } + }, + _ => None, + } + } + + fn generate_recommendation( + cx: &EarlyContext<'_>, + binop: BinOpKind, + node: &Expr, + other_side: &Expr, + side: Side, + ) -> Option { + let binop_string = match binop { + BinOpKind::Ge => ">", + BinOpKind::Le => "<", + _ => return None, + }; + if let Some(snippet) = snippet_opt(cx, node.span) { + if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { + 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 + } + + fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) { + span_lint_and_sugg( + cx, + INT_PLUS_ONE, + block.span, + "Unnecessary `>= y + 1` or `x - 1 >=`", + "change it to", + recommendation, + Applicability::MachineApplicable, // snippet + ); + } +} + +impl EarlyLintPass for IntPlusOne { + fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { + if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { + if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) { + Self::emit_warning(cx, item, rec.clone()); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/integer_division.rs b/src/tools/clippy/clippy_lints/src/integer_division.rs new file mode 100644 index 0000000000000..fe34d33fe652c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/integer_division.rs @@ -0,0 +1,56 @@ +use crate::utils::span_lint_and_help; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for division of integers + /// + /// **Why is this bad?** When outside of some very specific algorithms, + /// integer division is very often a mistake because it discards the + /// remainder. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn main() { + /// let x = 3 / 2; + /// println!("{}", x); + /// } + /// ``` + pub INTEGER_DIVISION, + restriction, + "integer division may cause loss of precision" +} + +declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IntegerDivision { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_integer_division(cx, expr) { + span_lint_and_help( + cx, + INTEGER_DIVISION, + expr.span, + "integer division", + None, + "division of integers may cause loss of precision. consider using floats.", + ); + } + } +} + +fn is_integer_division<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { + if_chain! { + if let hir::ExprKind::Binary(binop, left, right) = &expr.kind; + if let hir::BinOpKind::Div = &binop.node; + then { + let (left_ty, right_ty) = (cx.tables.expr_ty(left), cx.tables.expr_ty(right)); + return left_ty.is_integral() && right_ty.is_integral(); + } + } + + false +} diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs new file mode 100644 index 0000000000000..e7062b7c16bb9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs @@ -0,0 +1,71 @@ +//! lint when items are used after statements + +use crate::utils::span_lint; +use rustc_ast::ast::{Block, ItemKind, StmtKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for items declared after some statement in a block. + /// + /// **Why is this bad?** Items live for the entire scope they are declared + /// in. But statements are processed in order. This might cause confusion as + /// it's hard to figure out which item is meant in a statement. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo() { + /// println!("cake"); + /// } + /// + /// fn main() { + /// foo(); // prints "foo" + /// fn foo() { + /// println!("foo"); + /// } + /// foo(); // prints "foo" + /// } + /// ``` + pub ITEMS_AFTER_STATEMENTS, + pedantic, + "blocks where an item comes after a statement" +} + +declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); + +impl EarlyLintPass for ItemsAfterStatements { + fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { + if item.span.from_expansion() { + return; + } + + // skip initial items + let stmts = item + .stmts + .iter() + .map(|stmt| &stmt.kind) + .skip_while(|s| matches!(**s, StmtKind::Item(..))); + + // lint on all further items + for stmt in stmts { + if let StmtKind::Item(ref it) = *stmt { + if it.span.from_expansion() { + return; + } + if let ItemKind::MacroDef(..) = it.kind { + // do not lint `macro_rules`, but continue processing further statements + continue; + } + span_lint( + cx, + ITEMS_AFTER_STATEMENTS, + it.span, + "adding items after statements is confusing, since items exist from the \ + start of the scope", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs new file mode 100644 index 0000000000000..c9e12fc535ec0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -0,0 +1,85 @@ +use crate::rustc_target::abi::LayoutOf; +use crate::utils::span_lint_and_then; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::{self, ConstKind}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{BytePos, Pos, Span}; +use rustc_typeck::hir_ty_to_ty; + +declare_clippy_lint! { + /// **What it does:** Checks for large `const` arrays that should + /// be defined as `static` instead. + /// + /// **Why is this bad?** Performance: const variables are inlined upon use. + /// Static items result in only one instance and has a fixed location in memory. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// // Bad + /// pub const a = [0u32; 1_000_000]; + /// + /// // Good + /// pub static a = [0u32; 1_000_000]; + /// ``` + pub LARGE_CONST_ARRAYS, + perf, + "large non-scalar const array may cause performance overhead" +} + +pub struct LargeConstArrays { + maximum_allowed_size: u64, +} + +impl LargeConstArrays { + #[must_use] + pub fn new(maximum_allowed_size: u64) -> Self { + Self { maximum_allowed_size } + } +} + +impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeConstArrays { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if_chain! { + if !item.span.from_expansion(); + if let ItemKind::Const(hir_ty, _) = &item.kind; + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let ty::Array(element_type, cst) = ty.kind; + if let ConstKind::Value(val) = cst.val; + if let ConstValue::Scalar(element_count) = val; + if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); + if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes()); + if self.maximum_allowed_size < element_count * element_size; + + then { + let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); + let sugg_span = Span::new( + hi_pos - BytePos::from_usize("const".len()), + hi_pos, + item.span.ctxt(), + ); + span_lint_and_then( + cx, + LARGE_CONST_ARRAYS, + item.span, + "large array defined as const", + |diag| { + diag.span_suggestion( + sugg_span, + "make this a static item", + "static".to_string(), + Applicability::MachineApplicable, + ); + } + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs new file mode 100644 index 0000000000000..5bc3234e3252f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs @@ -0,0 +1,134 @@ +//! lint when there is a large size difference between variants on an enum + +use crate::utils::{snippet_opt, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_target::abi::LayoutOf; + +declare_clippy_lint! { + /// **What it does:** Checks for large size differences between variants on + /// `enum`s. + /// + /// **Why is this bad?** Enum size is bounded by the largest variant. Having a + /// large variant can penalize the memory layout of that enum. + /// + /// **Known problems:** This lint obviously cannot take the distribution of + /// variants in your running program into account. It is possible that the + /// smaller variants make up less than 1% of all instances, in which case + /// the overhead is negligible and the boxing is counter-productive. Always + /// measure the change this lint suggests. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// enum Test { + /// A(i32), + /// B([i32; 8000]), + /// } + /// + /// // Possibly better + /// enum Test2 { + /// A(i32), + /// B(Box<[i32; 8000]>), + /// } + /// ``` + pub LARGE_ENUM_VARIANT, + perf, + "large size difference between variants on an enum" +} + +#[derive(Copy, Clone)] +pub struct LargeEnumVariant { + maximum_size_difference_allowed: u64, +} + +impl LargeEnumVariant { + #[must_use] + pub fn new(maximum_size_difference_allowed: u64) -> Self { + Self { + maximum_size_difference_allowed, + } + } +} + +impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant { + fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { + let did = cx.tcx.hir().local_def_id(item.hir_id); + if let ItemKind::Enum(ref def, _) = item.kind { + let ty = cx.tcx.type_of(did); + let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); + + let mut largest_variant: Option<(_, _)> = None; + let mut second_variant: Option<(_, _)> = None; + + for (i, variant) in adt.variants.iter().enumerate() { + let size: u64 = variant + .fields + .iter() + .filter_map(|f| { + let ty = cx.tcx.type_of(f.did); + // don't count generics by filtering out everything + // that does not have a layout + cx.layout_of(ty).ok().map(|l| l.size.bytes()) + }) + .sum(); + + let grouped = (size, (i, variant)); + + if grouped.0 >= largest_variant.map_or(0, |x| x.0) { + second_variant = largest_variant; + largest_variant = Some(grouped); + } + } + + if let (Some(largest), Some(second)) = (largest_variant, second_variant) { + let difference = largest.0 - second.0; + + if difference > self.maximum_size_difference_allowed { + let (i, variant) = largest.1; + + let help_text = "consider boxing the large fields to reduce the total size of the enum"; + span_lint_and_then( + cx, + LARGE_ENUM_VARIANT, + def.variants[i].span, + "large size difference between variants", + |diag| { + diag.span_label( + def.variants[(largest.1).0].span, + &format!("this variant is {} bytes", largest.0), + ); + diag.span_note( + def.variants[(second.1).0].span, + &format!("and the second-largest variant is {} bytes:", second.0), + ); + if variant.fields.len() == 1 { + let span = match def.variants[i].data { + VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => { + fields[0].ty.span + }, + VariantData::Unit(..) => unreachable!(), + }; + if let Some(snip) = snippet_opt(cx, span) { + diag.span_suggestion( + span, + help_text, + format!("Box<{}>", snip), + Applicability::MaybeIncorrect, + ); + return; + } + } + diag.span_help(def.variants[i].span, help_text); + }, + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs new file mode 100644 index 0000000000000..deb57db167896 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -0,0 +1,69 @@ +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::{self, ConstKind}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +use if_chain::if_chain; + +use crate::rustc_target::abi::LayoutOf; +use crate::utils::{snippet, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Checks for local arrays that may be too large. + /// + /// **Why is this bad?** Large local arrays may cause stack overflow. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// let a = [0u32; 1_000_000]; + /// ``` + pub LARGE_STACK_ARRAYS, + pedantic, + "allocating large arrays on stack may cause stack overflow" +} + +pub struct LargeStackArrays { + maximum_allowed_size: u64, +} + +impl LargeStackArrays { + #[must_use] + pub fn new(maximum_allowed_size: u64) -> Self { + Self { maximum_allowed_size } + } +} + +impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeStackArrays { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Repeat(_, _) = expr.kind; + if let ty::Array(element_type, cst) = cx.tables.expr_ty(expr).kind; + if let ConstKind::Value(val) = cst.val; + if let ConstValue::Scalar(element_count) = val; + if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); + if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes()); + if self.maximum_allowed_size < element_count * element_size; + then { + span_lint_and_help( + cx, + LARGE_STACK_ARRAYS, + expr.span, + &format!( + "allocating a local array larger than {} bytes", + self.maximum_allowed_size + ), + None, + &format!( + "consider allocating on the heap with `vec!{}.into_boxed_slice()`", + snippet(cx, expr.span, "[...]") + ), + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs new file mode 100644 index 0000000000000..2ec0b5a8d6fb4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -0,0 +1,304 @@ +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, TraitItemRef}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{Span, Spanned, Symbol}; + +declare_clippy_lint! { + /// **What it does:** Checks for getting the length of something via `.len()` + /// just to compare to zero, and suggests using `.is_empty()` where applicable. + /// + /// **Why is this bad?** Some structures can answer `.is_empty()` much faster + /// than calculating their length. So it is good to get into the habit of using + /// `.is_empty()`, and having it is cheap. + /// Besides, it makes the intent clearer than a manual comparison in some contexts. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// if x.len() == 0 { + /// .. + /// } + /// if y.len() != 0 { + /// .. + /// } + /// ``` + /// instead use + /// ```ignore + /// if x.is_empty() { + /// .. + /// } + /// if !y.is_empty() { + /// .. + /// } + /// ``` + pub LEN_ZERO, + style, + "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead" +} + +declare_clippy_lint! { + /// **What it does:** Checks for items that implement `.len()` but not + /// `.is_empty()`. + /// + /// **Why is this bad?** It is good custom to have both methods, because for + /// some data structures, asking about the length will be a costly operation, + /// whereas `.is_empty()` can usually answer in constant time. Also it used to + /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that + /// lint will ignore such entities. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// impl X { + /// pub fn len(&self) -> usize { + /// .. + /// } + /// } + /// ``` + pub LEN_WITHOUT_IS_EMPTY, + style, + "traits or impls with a public `len` method but no corresponding `is_empty` method" +} + +declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LenZero { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if item.span.from_expansion() { + return; + } + + match item.kind { + ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items), + ItemKind::Impl { + of_trait: None, + items: ref impl_items, + .. + } => check_impl_items(cx, item, impl_items), + _ => (), + } + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { + match cmp { + BinOpKind::Eq => { + check_cmp(cx, expr.span, left, right, "", 0); // len == 0 + check_cmp(cx, expr.span, right, left, "", 0); // 0 == len + }, + BinOpKind::Ne => { + check_cmp(cx, expr.span, left, right, "!", 0); // len != 0 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len + }, + BinOpKind::Gt => { + check_cmp(cx, expr.span, left, right, "!", 0); // len > 0 + check_cmp(cx, expr.span, right, left, "", 1); // 1 > len + }, + BinOpKind::Lt => { + check_cmp(cx, expr.span, left, right, "", 1); // len < 1 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len + }, + BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1 + BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len + _ => (), + } + } + } +} + +fn check_trait_items(cx: &LateContext<'_, '_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) { + fn is_named_self(cx: &LateContext<'_, '_>, item: &TraitItemRef, name: &str) -> bool { + item.ident.name.as_str() == name + && if let AssocItemKind::Fn { has_self } = item.kind { + has_self && { + let did = cx.tcx.hir().local_def_id(item.id.hir_id); + cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1 + } + } else { + false + } + } + + // fill the set with current and super traits + fn fill_trait_set(traitt: DefId, set: &mut FxHashSet, cx: &LateContext<'_, '_>) { + if set.insert(traitt) { + for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) { + fill_trait_set(supertrait, set, cx); + } + } + } + + if cx.access_levels.is_exported(visited_trait.hir_id) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) { + let mut current_and_super_traits = FxHashSet::default(); + let visited_trait_def_id = cx.tcx.hir().local_def_id(visited_trait.hir_id); + fill_trait_set(visited_trait_def_id.to_def_id(), &mut current_and_super_traits, cx); + + let is_empty_method_found = current_and_super_traits + .iter() + .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order()) + .any(|i| { + i.kind == ty::AssocKind::Fn + && i.fn_has_self_parameter + && i.ident.name == sym!(is_empty) + && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1 + }); + + if !is_empty_method_found { + span_lint( + cx, + LEN_WITHOUT_IS_EMPTY, + visited_trait.span, + &format!( + "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method", + visited_trait.ident.name + ), + ); + } + } +} + +fn check_impl_items(cx: &LateContext<'_, '_>, item: &Item<'_>, impl_items: &[ImplItemRef<'_>]) { + fn is_named_self(cx: &LateContext<'_, '_>, item: &ImplItemRef<'_>, name: &str) -> bool { + item.ident.name.as_str() == name + && if let AssocItemKind::Fn { has_self } = item.kind { + has_self && { + let did = cx.tcx.hir().local_def_id(item.id.hir_id); + cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1 + } + } else { + false + } + } + + let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) { + if cx.access_levels.is_exported(is_empty.id.hir_id) { + return; + } else { + "a private" + } + } else { + "no corresponding" + }; + + if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) { + if cx.access_levels.is_exported(i.id.hir_id) { + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + let ty = cx.tcx.type_of(def_id); + + span_lint( + cx, + LEN_WITHOUT_IS_EMPTY, + item.span, + &format!( + "item `{}` has a public `len` method but {} `is_empty` method", + ty, is_empty + ), + ); + } + } +} + +fn check_cmp(cx: &LateContext<'_, '_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { + if let (&ExprKind::MethodCall(ref method_path, _, ref args), &ExprKind::Lit(ref 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; + } + } + + check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to) + } +} + +fn check_len( + cx: &LateContext<'_, '_>, + span: Span, + method_name: Symbol, + args: &[Expr<'_>], + lit: &LitKind, + op: &str, + compare_to: u32, +) { + if let LitKind::Int(lit, _) = *lit { + // check if length is compared to the specified number + if lit != u128::from(compare_to) { + return; + } + + if method_name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + LEN_ZERO, + span, + &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), + &format!("using `{}is_empty` is clearer and more explicit", op), + format!( + "{}{}.is_empty()", + op, + snippet_with_applicability(cx, args[0].span, "_", &mut applicability) + ), + applicability, + ); + } + } +} + +/// Checks if this type has an `is_empty` method. +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 let ty::AssocKind::Fn = item.kind { + if item.ident.name.as_str() == "is_empty" { + let sig = cx.tcx.fn_sig(item.def_id); + let ty = sig.skip_binder(); + ty.inputs().len() == 1 + } else { + false + } + } else { + false + } + } + + /// Checks the inherent impl's items for an `is_empty(self)` method. + fn has_is_empty_impl(cx: &LateContext<'_, '_>, id: DefId) -> bool { + cx.tcx.inherent_impls(id).iter().any(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }) + } + + let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr)); + match ty.kind { + ty::Dynamic(ref tt, ..) => { + if let Some(principal) = tt.principal() { + cx.tcx + .associated_items(principal.def_id()) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + } else { + false + } + }, + ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), + ty::Adt(id, _) => has_is_empty_impl(cx, id.did), + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs new file mode 100644 index 0000000000000..d7bf8a1476817 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -0,0 +1,205 @@ +use crate::utils::{higher, qpath_res, snippet, span_lint_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::intravisit; +use rustc_hir::BindingAnnotation; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for variable declarations immediately followed by a + /// conditional affectation. + /// + /// **Why is this bad?** This is not idiomatic Rust. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// let foo; + /// + /// if bar() { + /// foo = 42; + /// } else { + /// foo = 0; + /// } + /// + /// let mut baz = None; + /// + /// if bar() { + /// baz = Some(42); + /// } + /// ``` + /// + /// should be written + /// + /// ```rust,ignore + /// let foo = if bar() { + /// 42 + /// } else { + /// 0 + /// }; + /// + /// let baz = if bar() { + /// Some(42) + /// } else { + /// None + /// }; + /// ``` + pub USELESS_LET_IF_SEQ, + nursery, + "unidiomatic `let mut` declaration followed by initialization in `if`" +} + +declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetIfSeq { + fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block<'_>) { + let mut it = block.stmts.iter().peekable(); + while let Some(stmt) = it.next() { + if_chain! { + if let Some(expr) = it.peek(); + if let hir::StmtKind::Local(ref local) = stmt.kind; + if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; + if let hir::StmtKind::Expr(ref if_) = expr.kind; + if let Some((ref cond, ref then, ref else_)) = higher::if_block(&if_); + if !used_in_expr(cx, canonical_id, cond); + if let hir::ExprKind::Block(ref then, _) = then.kind; + if let Some(value) = check_assign(cx, canonical_id, &*then); + if !used_in_expr(cx, canonical_id, value); + then { + let span = stmt.span.to(if_.span); + + let has_interior_mutability = !cx.tables.node_type(canonical_id).is_freeze( + cx.tcx, + cx.param_env, + span + ); + if has_interior_mutability { return; } + + let (default_multi_stmts, default) = if let Some(ref else_) = *else_ { + if let hir::ExprKind::Block(ref else_, _) = else_.kind { + if let Some(default) = check_assign(cx, canonical_id, else_) { + (else_.stmts.len() > 1, default) + } else if let Some(ref default) = local.init { + (true, &**default) + } else { + continue; + } + } else { + continue; + } + } else if let Some(ref default) = local.init { + (false, &**default) + } else { + continue; + }; + + let mutability = match mode { + BindingAnnotation::RefMut | BindingAnnotation::Mutable => " ", + _ => "", + }; + + // FIXME: this should not suggest `mut` if we can detect that the variable is not + // use mutably after the `if` + + let sug = format!( + "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", + mut=mutability, + name=ident.name, + cond=snippet(cx, cond.span, "_"), + then=if then.stmts.len() > 1 { " ..;" } else { "" }, + else=if default_multi_stmts { " ..;" } else { "" }, + value=snippet(cx, value.span, ""), + default=snippet(cx, default.span, ""), + ); + span_lint_and_then(cx, + USELESS_LET_IF_SEQ, + span, + "`if _ { .. } else { .. }` is an expression", + |diag| { + diag.span_suggestion( + span, + "it is more idiomatic to write", + sug, + Applicability::HasPlaceholders, + ); + if !mutability.is_empty() { + diag.note("you might not need `mut` at all"); + } + }); + } + } + } + } +} + +struct UsedVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + id: hir::HirId, + used: bool, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::Path(ref qpath) = expr.kind; + if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id); + if self.id == local_id; + then { + self.used = true; + return; + } + } + intravisit::walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +fn check_assign<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + decl: hir::HirId, + block: &'tcx hir::Block<'_>, +) -> Option<&'tcx hir::Expr<'tcx>> { + if_chain! { + if block.expr.is_none(); + if let Some(expr) = block.stmts.iter().last(); + if let hir::StmtKind::Semi(ref expr) = expr.kind; + if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind; + if let hir::ExprKind::Path(ref qpath) = var.kind; + if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id); + if decl == local_id; + then { + let mut v = UsedVisitor { + cx, + id: decl, + used: false, + }; + + for s in block.stmts.iter().take(block.stmts.len()-1) { + intravisit::walk_stmt(&mut v, s); + + if v.used { + return None; + } + } + + return Some(value); + } + } + + None +} + +fn used_in_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> bool { + let mut v = UsedVisitor { cx, id, used: false }; + intravisit::walk_expr(&mut v, expr); + v.used +} diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs new file mode 100644 index 0000000000000..710dec8d33fc9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -0,0 +1,119 @@ +use if_chain::if_chain; +use rustc_hir::{Local, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Checks for `let _ = ` + /// where expr is #[must_use] + /// + /// **Why is this bad?** It's better to explicitly + /// handle the value of a #[must_use] expr + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn f() -> Result { + /// Ok(0) + /// } + /// + /// let _ = f(); + /// // is_ok() is marked #[must_use] + /// let _ = f().is_ok(); + /// ``` + pub LET_UNDERSCORE_MUST_USE, + restriction, + "non-binding let on a `#[must_use]` expression" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `let _ = sync_lock` + /// + /// **Why is this bad?** This statement immediately drops the lock instead of + /// extending it's lifetime to the end of the scope, which is often not intended. + /// To extend lock lifetime to the end of the scope, use an underscore-prefixed + /// name instead (i.e. _lock). If you want to explicitly drop the lock, + /// `std::mem::drop` conveys your intention better and is less error-prone. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// Bad: + /// ```rust,ignore + /// let _ = mutex.lock(); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _lock = mutex.lock(); + /// ``` + pub LET_UNDERSCORE_LOCK, + correctness, + "non-binding let on a synchronization lock" +} + +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]); + +const SYNC_GUARD_PATHS: [&[&str]; 3] = [ + &paths::MUTEX_GUARD, + &paths::RWLOCK_READ_GUARD, + &paths::RWLOCK_WRITE_GUARD, +]; + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore { + fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) { + if in_external_macro(cx.tcx.sess, local.span) { + return; + } + + if_chain! { + if let PatKind::Wild = local.pat.kind; + if let Some(ref init) = local.init; + then { + let init_ty = cx.tables.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::Lifetime(_) | GenericArgKind::Const(_) => false, + }); + if contains_sync_guard { + span_lint_and_help( + cx, + LET_UNDERSCORE_LOCK, + local.span, + "non-binding let on a synchronization lock", + None, + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`" + ) + } else if is_must_use_ty(cx, cx.tables.expr_ty(init)) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding let on an expression with `#[must_use]` type", + None, + "consider explicitly using expression value" + ) + } else if is_must_use_func_call(cx, init) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding let on a result of a `#[must_use]` function", + None, + "consider explicitly using function result" + ) + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs new file mode 100644 index 0000000000000..e0787ac0887e4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -0,0 +1,1807 @@ +// error-pattern:cargo-clippy + +#![feature(box_syntax)] +#![feature(box_patterns)] +#![feature(or_patterns)] +#![feature(rustc_private)] +#![feature(stmt_expr_attributes)] +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)] +#![recursion_limit = "512"] +#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)] +#![deny(rustc::internal)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![feature(crate_visibility_modifier)] +#![feature(concat_idents)] + +// FIXME: switch to something more ergonomic here, once available. +// (Currently there is no way to opt into sysroot crates without `extern crate`.) +#[allow(unused_extern_crates)] +extern crate fmt_macros; +#[allow(unused_extern_crates)] +extern crate rustc_ast; +#[allow(unused_extern_crates)] +extern crate rustc_ast_pretty; +#[allow(unused_extern_crates)] +extern crate rustc_attr; +#[allow(unused_extern_crates)] +extern crate rustc_data_structures; +#[allow(unused_extern_crates)] +extern crate rustc_driver; +#[allow(unused_extern_crates)] +extern crate rustc_errors; +#[allow(unused_extern_crates)] +extern crate rustc_hir; +#[allow(unused_extern_crates)] +extern crate rustc_hir_pretty; +#[allow(unused_extern_crates)] +extern crate rustc_index; +#[allow(unused_extern_crates)] +extern crate rustc_infer; +#[allow(unused_extern_crates)] +extern crate rustc_lexer; +#[allow(unused_extern_crates)] +extern crate rustc_lint; +#[allow(unused_extern_crates)] +extern crate rustc_middle; +#[allow(unused_extern_crates)] +extern crate rustc_mir; +#[allow(unused_extern_crates)] +extern crate rustc_parse; +#[allow(unused_extern_crates)] +extern crate rustc_session; +#[allow(unused_extern_crates)] +extern crate rustc_span; +#[allow(unused_extern_crates)] +extern crate rustc_target; +#[allow(unused_extern_crates)] +extern crate rustc_trait_selection; +#[allow(unused_extern_crates)] +extern crate rustc_typeck; + +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::LintId; +use rustc_session::Session; + +/// Macro used to declare a Clippy lint. +/// +/// Every lint declaration consists of 4 parts: +/// +/// 1. The documentation, which is used for the website +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or +/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. +/// 4. The `description` that contains a short explanation on what's wrong with code where the +/// lint is triggered. +/// +/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. +/// As said in the README.md of this repository, if the lint level mapping changes, please update +/// README.md. +/// +/// # Example +/// +/// ``` +/// # #![feature(rustc_private)] +/// # #[allow(unused_extern_crates)] +/// # extern crate rustc_middle; +/// # #[allow(unused_extern_crates)] +/// # extern crate rustc_session; +/// # #[macro_use] +/// # use clippy_lints::declare_clippy_lint; +/// use rustc_session::declare_tool_lint; +/// +/// declare_clippy_lint! { +/// /// **What it does:** Checks for ... (describe what the lint matches). +/// /// +/// /// **Why is this bad?** Supply the reason for linting the code. +/// /// +/// /// **Known problems:** None. (Or describe where it could go wrong.) +/// /// +/// /// **Example:** +/// /// +/// /// ```rust +/// /// // Bad +/// /// Insert a short example of code that triggers the lint +/// /// +/// /// // Good +/// /// Insert a short example of improved code that doesn't trigger the lint +/// /// ``` +/// pub LINT_NAME, +/// pedantic, +/// "description" +/// } +/// ``` +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints +#[macro_export] +macro_rules! declare_clippy_lint { + { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; +} + +mod consts; +#[macro_use] +mod utils; + +// begin lints modules, do not remove this comment, it’s used in `update_lints` +mod approx_const; +mod arithmetic; +mod as_conversions; +mod assertions_on_constants; +mod assign_ops; +mod atomic_ordering; +mod attrs; +mod await_holding_lock; +mod bit_mask; +mod blacklisted_name; +mod blocks_in_if_conditions; +mod booleans; +mod bytecount; +mod cargo_common_metadata; +mod checked_conversions; +mod cognitive_complexity; +mod collapsible_if; +mod comparison_chain; +mod copies; +mod copy_iterator; +mod dbg_macro; +mod default_trait_access; +mod dereference; +mod derive; +mod doc; +mod double_comparison; +mod double_parens; +mod drop_bounds; +mod drop_forget_ref; +mod duration_subsec; +mod else_if_without_else; +mod empty_enum; +mod entry; +mod enum_clike; +mod enum_variants; +mod eq_op; +mod erasing_op; +mod escape; +mod eta_reduction; +mod eval_order_dependence; +mod excessive_bools; +mod exit; +mod explicit_write; +mod fallible_impl_from; +mod float_literal; +mod floating_point_arithmetic; +mod format; +mod formatting; +mod functions; +mod future_not_send; +mod get_last_with_len; +mod identity_op; +mod if_let_mutex; +mod if_let_some_result; +mod if_not_else; +mod implicit_return; +mod implicit_saturating_sub; +mod indexing_slicing; +mod infinite_iter; +mod inherent_impl; +mod inherent_to_string; +mod inline_fn_without_body; +mod int_plus_one; +mod integer_division; +mod items_after_statements; +mod large_const_arrays; +mod large_enum_variant; +mod large_stack_arrays; +mod len_zero; +mod let_if_seq; +mod let_underscore; +mod lifetimes; +mod literal_representation; +mod loops; +mod macro_use; +mod main_recursion; +mod manual_async_fn; +mod manual_non_exhaustive; +mod map_clone; +mod map_unit_fn; +mod match_on_vec_items; +mod matches; +mod mem_discriminant; +mod mem_forget; +mod mem_replace; +mod methods; +mod minmax; +mod misc; +mod misc_early; +mod missing_const_for_fn; +mod missing_doc; +mod missing_inline; +mod modulo_arithmetic; +mod multiple_crate_versions; +mod mut_key; +mod mut_mut; +mod mut_reference; +mod mutable_debug_assertion; +mod mutex_atomic; +mod needless_bool; +mod needless_borrow; +mod needless_borrowed_ref; +mod needless_continue; +mod needless_pass_by_value; +mod needless_update; +mod neg_cmp_op_on_partial_ord; +mod neg_multiply; +mod new_without_default; +mod no_effect; +mod non_copy_const; +mod non_expressive_names; +mod open_options; +mod option_env_unwrap; +mod overflow_check_conditional; +mod panic_unimplemented; +mod partialeq_ne_impl; +mod path_buf_push_overwrite; +mod precedence; +mod ptr; +mod ptr_offset_with_cast; +mod question_mark; +mod ranges; +mod redundant_clone; +mod redundant_field_names; +mod redundant_pattern_matching; +mod redundant_pub_crate; +mod redundant_static_lifetimes; +mod reference; +mod regex; +mod returns; +mod serde_api; +mod shadow; +mod single_component_path_imports; +mod slow_vector_initialization; +mod strings; +mod suspicious_trait_impl; +mod swap; +mod tabs_in_doc_comments; +mod temporary_assignment; +mod to_digit_is_some; +mod trait_bounds; +mod transmute; +mod transmuting_null; +mod trivially_copy_pass_by_ref; +mod try_err; +mod types; +mod unicode; +mod unnamed_address; +mod unsafe_removed_from_name; +mod unused_io_amount; +mod unused_self; +mod unwrap; +mod use_self; +mod useless_conversion; +mod vec; +mod verbose_file_reads; +mod wildcard_dependencies; +mod wildcard_imports; +mod write; +mod zero_div_zero; +// end lints modules, do not remove this comment, it’s used in `update_lints` + +pub use crate::utils::conf::Conf; + +mod reexport { + pub use rustc_span::Symbol as Name; +} + +/// Register all pre expansion lints +/// +/// Pre-expansion lints run before any macro expansion has happened. +/// +/// 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: &Conf) { + store.register_pre_expansion_pass(|| box write::Write::default()); + store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; + store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames { + single_char_binding_names_threshold, + }); + store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); + store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); +} + +#[doc(hidden)] +pub fn read_conf(args: &[rustc_ast::ast::NestedMetaItem], sess: &Session) -> Conf { + use std::path::Path; + match utils::conf::file_from_args(args) { + Ok(file_name) => { + // if the user specified a file, it must exist, otherwise default to `clippy.toml` but + // do not require the file to exist + let file_name = match file_name { + Some(file_name) => file_name, + None => match utils::conf::lookup_conf_file() { + Ok(Some(path)) => path, + Ok(None) => return Conf::default(), + Err(error) => { + sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) + .emit(); + return Conf::default(); + }, + }, + }; + + let file_name = if file_name.is_relative() { + sess.local_crate_source_file + .as_deref() + .and_then(Path::parent) + .unwrap_or_else(|| Path::new("")) + .join(file_name) + } else { + file_name + }; + + let (conf, errors) = utils::conf::read(&file_name); + + // all conf errors are non-fatal, we just use the default conf in case of error + for error in errors { + sess.struct_err(&format!( + "error reading Clippy's configuration file `{}`: {}", + file_name.display(), + error + )) + .emit(); + } + + conf + }, + Err((err, span)) => { + sess.struct_span_err(span, err) + .span_note(span, "Clippy will use default configuration") + .emit(); + Conf::default() + }, + } +} + +/// Register all lints and lint groups with the rustc plugin registry +/// +/// Used in `./src/driver.rs`. +#[allow(clippy::too_many_lines)] +#[rustfmt::skip] +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { + register_removed_non_tool_lints(store); + + // begin deprecated lints, do not remove this comment, it’s used in `update_lints` + store.register_removed( + "clippy::should_assert_eq", + "`assert!()` will be more flexible with RFC 2011", + ); + store.register_removed( + "clippy::extend_from_slice", + "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", + ); + store.register_removed( + "clippy::range_step_by_zero", + "`iterator.step_by(0)` panics nowadays", + ); + store.register_removed( + "clippy::unstable_as_slice", + "`Vec::as_slice` has been stabilized in 1.7", + ); + store.register_removed( + "clippy::unstable_as_mut_slice", + "`Vec::as_mut_slice` has been stabilized in 1.7", + ); + store.register_removed( + "clippy::str_to_string", + "using `str::to_string` is common even today and specialization will likely happen soon", + ); + store.register_removed( + "clippy::string_to_string", + "using `string::to_string` is common even today and specialization will likely happen soon", + ); + store.register_removed( + "clippy::misaligned_transmute", + "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", + ); + store.register_removed( + "clippy::assign_ops", + "using compound assignment operators (e.g., `+=`) is harmless", + ); + store.register_removed( + "clippy::if_let_redundant_pattern_matching", + "this lint has been changed to redundant_pattern_matching", + ); + store.register_removed( + "clippy::unsafe_vector_initialization", + "the replacement suggested by this lint had substantially different behavior", + ); + store.register_removed( + "clippy::invalid_ref", + "superseded by rustc lint `invalid_value`", + ); + store.register_removed( + "clippy::unused_collect", + "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", + ); + store.register_removed( + "clippy::into_iter_on_array", + "this lint has been uplifted to rustc and is now called `array_into_iter`", + ); + store.register_removed( + "clippy::unused_label", + "this lint has been uplifted to rustc and is now called `unused_labels`", + ); + store.register_removed( + "clippy::replace_consts", + "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants", + ); + // end deprecated lints, do not remove this comment, it’s used in `update_lints` + + // begin register lints, do not remove this comment, it’s used in `update_lints` + store.register_lints(&[ + &approx_const::APPROX_CONSTANT, + &arithmetic::FLOAT_ARITHMETIC, + &arithmetic::INTEGER_ARITHMETIC, + &as_conversions::AS_CONVERSIONS, + &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, + &assign_ops::ASSIGN_OP_PATTERN, + &assign_ops::MISREFACTORED_ASSIGN_OP, + &atomic_ordering::INVALID_ATOMIC_ORDERING, + &attrs::DEPRECATED_CFG_ATTR, + &attrs::DEPRECATED_SEMVER, + &attrs::EMPTY_LINE_AFTER_OUTER_ATTR, + &attrs::INLINE_ALWAYS, + &attrs::MISMATCHED_TARGET_OS, + &attrs::UNKNOWN_CLIPPY_LINTS, + &attrs::USELESS_ATTRIBUTE, + &await_holding_lock::AWAIT_HOLDING_LOCK, + &bit_mask::BAD_BIT_MASK, + &bit_mask::INEFFECTIVE_BIT_MASK, + &bit_mask::VERBOSE_BIT_MASK, + &blacklisted_name::BLACKLISTED_NAME, + &blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, + &booleans::LOGIC_BUG, + &booleans::NONMINIMAL_BOOL, + &bytecount::NAIVE_BYTECOUNT, + &cargo_common_metadata::CARGO_COMMON_METADATA, + &checked_conversions::CHECKED_CONVERSIONS, + &cognitive_complexity::COGNITIVE_COMPLEXITY, + &collapsible_if::COLLAPSIBLE_IF, + &comparison_chain::COMPARISON_CHAIN, + &copies::IFS_SAME_COND, + &copies::IF_SAME_THEN_ELSE, + &copies::MATCH_SAME_ARMS, + &copies::SAME_FUNCTIONS_IN_IF_CONDITION, + ©_iterator::COPY_ITERATOR, + &dbg_macro::DBG_MACRO, + &default_trait_access::DEFAULT_TRAIT_ACCESS, + &dereference::EXPLICIT_DEREF_METHODS, + &derive::DERIVE_HASH_XOR_EQ, + &derive::EXPL_IMPL_CLONE_ON_COPY, + &derive::UNSAFE_DERIVE_DESERIALIZE, + &doc::DOC_MARKDOWN, + &doc::MISSING_ERRORS_DOC, + &doc::MISSING_SAFETY_DOC, + &doc::NEEDLESS_DOCTEST_MAIN, + &double_comparison::DOUBLE_COMPARISONS, + &double_parens::DOUBLE_PARENS, + &drop_bounds::DROP_BOUNDS, + &drop_forget_ref::DROP_COPY, + &drop_forget_ref::DROP_REF, + &drop_forget_ref::FORGET_COPY, + &drop_forget_ref::FORGET_REF, + &duration_subsec::DURATION_SUBSEC, + &else_if_without_else::ELSE_IF_WITHOUT_ELSE, + &empty_enum::EMPTY_ENUM, + &entry::MAP_ENTRY, + &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, + &enum_variants::ENUM_VARIANT_NAMES, + &enum_variants::MODULE_INCEPTION, + &enum_variants::MODULE_NAME_REPETITIONS, + &enum_variants::PUB_ENUM_VARIANT_NAMES, + &eq_op::EQ_OP, + &eq_op::OP_REF, + &erasing_op::ERASING_OP, + &escape::BOXED_LOCAL, + &eta_reduction::REDUNDANT_CLOSURE, + &eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + &eval_order_dependence::DIVERGING_SUB_EXPRESSION, + &eval_order_dependence::EVAL_ORDER_DEPENDENCE, + &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, + &excessive_bools::STRUCT_EXCESSIVE_BOOLS, + &exit::EXIT, + &explicit_write::EXPLICIT_WRITE, + &fallible_impl_from::FALLIBLE_IMPL_FROM, + &float_literal::EXCESSIVE_PRECISION, + &float_literal::LOSSY_FLOAT_LITERAL, + &floating_point_arithmetic::IMPRECISE_FLOPS, + &floating_point_arithmetic::SUBOPTIMAL_FLOPS, + &format::USELESS_FORMAT, + &formatting::POSSIBLE_MISSING_COMMA, + &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, + &formatting::SUSPICIOUS_ELSE_FORMATTING, + &formatting::SUSPICIOUS_UNARY_OP_FORMATTING, + &functions::DOUBLE_MUST_USE, + &functions::MUST_USE_CANDIDATE, + &functions::MUST_USE_UNIT, + &functions::NOT_UNSAFE_PTR_ARG_DEREF, + &functions::TOO_MANY_ARGUMENTS, + &functions::TOO_MANY_LINES, + &future_not_send::FUTURE_NOT_SEND, + &get_last_with_len::GET_LAST_WITH_LEN, + &identity_op::IDENTITY_OP, + &if_let_mutex::IF_LET_MUTEX, + &if_let_some_result::IF_LET_SOME_RESULT, + &if_not_else::IF_NOT_ELSE, + &implicit_return::IMPLICIT_RETURN, + &implicit_saturating_sub::IMPLICIT_SATURATING_SUB, + &indexing_slicing::INDEXING_SLICING, + &indexing_slicing::OUT_OF_BOUNDS_INDEXING, + &infinite_iter::INFINITE_ITER, + &infinite_iter::MAYBE_INFINITE_ITER, + &inherent_impl::MULTIPLE_INHERENT_IMPL, + &inherent_to_string::INHERENT_TO_STRING, + &inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, + &inline_fn_without_body::INLINE_FN_WITHOUT_BODY, + &int_plus_one::INT_PLUS_ONE, + &integer_division::INTEGER_DIVISION, + &items_after_statements::ITEMS_AFTER_STATEMENTS, + &large_const_arrays::LARGE_CONST_ARRAYS, + &large_enum_variant::LARGE_ENUM_VARIANT, + &large_stack_arrays::LARGE_STACK_ARRAYS, + &len_zero::LEN_WITHOUT_IS_EMPTY, + &len_zero::LEN_ZERO, + &let_if_seq::USELESS_LET_IF_SEQ, + &let_underscore::LET_UNDERSCORE_LOCK, + &let_underscore::LET_UNDERSCORE_MUST_USE, + &lifetimes::EXTRA_UNUSED_LIFETIMES, + &lifetimes::NEEDLESS_LIFETIMES, + &literal_representation::DECIMAL_LITERAL_REPRESENTATION, + &literal_representation::INCONSISTENT_DIGIT_GROUPING, + &literal_representation::LARGE_DIGIT_GROUPS, + &literal_representation::MISTYPED_LITERAL_SUFFIXES, + &literal_representation::UNREADABLE_LITERAL, + &loops::EMPTY_LOOP, + &loops::EXPLICIT_COUNTER_LOOP, + &loops::EXPLICIT_INTO_ITER_LOOP, + &loops::EXPLICIT_ITER_LOOP, + &loops::FOR_KV_MAP, + &loops::FOR_LOOPS_OVER_FALLIBLES, + &loops::ITER_NEXT_LOOP, + &loops::MANUAL_MEMCPY, + &loops::MUT_RANGE_BOUND, + &loops::NEEDLESS_COLLECT, + &loops::NEEDLESS_RANGE_LOOP, + &loops::NEVER_LOOP, + &loops::WHILE_IMMUTABLE_CONDITION, + &loops::WHILE_LET_LOOP, + &loops::WHILE_LET_ON_ITERATOR, + ¯o_use::MACRO_USE_IMPORTS, + &main_recursion::MAIN_RECURSION, + &manual_async_fn::MANUAL_ASYNC_FN, + &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + &map_clone::MAP_CLONE, + &map_unit_fn::OPTION_MAP_UNIT_FN, + &map_unit_fn::RESULT_MAP_UNIT_FN, + &match_on_vec_items::MATCH_ON_VEC_ITEMS, + &matches::INFALLIBLE_DESTRUCTURING_MATCH, + &matches::MATCH_AS_REF, + &matches::MATCH_BOOL, + &matches::MATCH_OVERLAPPING_ARM, + &matches::MATCH_REF_PATS, + &matches::MATCH_SINGLE_BINDING, + &matches::MATCH_WILD_ERR_ARM, + &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, + &matches::SINGLE_MATCH, + &matches::SINGLE_MATCH_ELSE, + &matches::WILDCARD_ENUM_MATCH_ARM, + &matches::WILDCARD_IN_OR_PATTERNS, + &mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, + &mem_forget::MEM_FORGET, + &mem_replace::MEM_REPLACE_OPTION_WITH_NONE, + &mem_replace::MEM_REPLACE_WITH_DEFAULT, + &mem_replace::MEM_REPLACE_WITH_UNINIT, + &methods::BIND_INSTEAD_OF_MAP, + &methods::CHARS_LAST_CMP, + &methods::CHARS_NEXT_CMP, + &methods::CLONE_DOUBLE_REF, + &methods::CLONE_ON_COPY, + &methods::CLONE_ON_REF_PTR, + &methods::EXPECT_FUN_CALL, + &methods::EXPECT_USED, + &methods::FILETYPE_IS_FILE, + &methods::FILTER_MAP, + &methods::FILTER_MAP_NEXT, + &methods::FILTER_NEXT, + &methods::FIND_MAP, + &methods::FLAT_MAP_IDENTITY, + &methods::GET_UNWRAP, + &methods::INEFFICIENT_TO_STRING, + &methods::INTO_ITER_ON_REF, + &methods::ITERATOR_STEP_BY_ZERO, + &methods::ITER_CLONED_COLLECT, + &methods::ITER_NTH, + &methods::ITER_NTH_ZERO, + &methods::ITER_SKIP_NEXT, + &methods::MANUAL_SATURATING_ARITHMETIC, + &methods::MAP_FLATTEN, + &methods::MAP_UNWRAP_OR, + &methods::NEW_RET_NO_SELF, + &methods::OK_EXPECT, + &methods::OPTION_AS_REF_DEREF, + &methods::OPTION_MAP_OR_NONE, + &methods::OR_FUN_CALL, + &methods::RESULT_MAP_OR_INTO_OPTION, + &methods::SEARCH_IS_SOME, + &methods::SHOULD_IMPLEMENT_TRAIT, + &methods::SINGLE_CHAR_PATTERN, + &methods::SKIP_WHILE_NEXT, + &methods::STRING_EXTEND_CHARS, + &methods::SUSPICIOUS_MAP, + &methods::TEMPORARY_CSTRING_AS_PTR, + &methods::UNINIT_ASSUMED_INIT, + &methods::UNNECESSARY_FILTER_MAP, + &methods::UNNECESSARY_FOLD, + &methods::UNWRAP_USED, + &methods::USELESS_ASREF, + &methods::WRONG_PUB_SELF_CONVENTION, + &methods::WRONG_SELF_CONVENTION, + &methods::ZST_OFFSET, + &minmax::MIN_MAX, + &misc::CMP_NAN, + &misc::CMP_OWNED, + &misc::FLOAT_CMP, + &misc::FLOAT_CMP_CONST, + &misc::MODULO_ONE, + &misc::SHORT_CIRCUIT_STATEMENT, + &misc::TOPLEVEL_REF_ARG, + &misc::USED_UNDERSCORE_BINDING, + &misc::ZERO_PTR, + &misc_early::BUILTIN_TYPE_SHADOW, + &misc_early::DOUBLE_NEG, + &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, + &misc_early::MIXED_CASE_HEX_LITERALS, + &misc_early::REDUNDANT_CLOSURE_CALL, + &misc_early::REDUNDANT_PATTERN, + &misc_early::UNNEEDED_FIELD_PATTERN, + &misc_early::UNNEEDED_WILDCARD_PATTERN, + &misc_early::UNSEPARATED_LITERAL_SUFFIX, + &misc_early::ZERO_PREFIXED_LITERAL, + &missing_const_for_fn::MISSING_CONST_FOR_FN, + &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, + &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + &modulo_arithmetic::MODULO_ARITHMETIC, + &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, + &mut_key::MUTABLE_KEY_TYPE, + &mut_mut::MUT_MUT, + &mut_reference::UNNECESSARY_MUT_PASSED, + &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, + &mutex_atomic::MUTEX_ATOMIC, + &mutex_atomic::MUTEX_INTEGER, + &needless_bool::BOOL_COMPARISON, + &needless_bool::NEEDLESS_BOOL, + &needless_borrow::NEEDLESS_BORROW, + &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, + &needless_continue::NEEDLESS_CONTINUE, + &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, + &needless_update::NEEDLESS_UPDATE, + &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, + &neg_multiply::NEG_MULTIPLY, + &new_without_default::NEW_WITHOUT_DEFAULT, + &no_effect::NO_EFFECT, + &no_effect::UNNECESSARY_OPERATION, + &non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, + &non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, + &non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, + &non_expressive_names::MANY_SINGLE_CHAR_NAMES, + &non_expressive_names::SIMILAR_NAMES, + &open_options::NONSENSICAL_OPEN_OPTIONS, + &option_env_unwrap::OPTION_ENV_UNWRAP, + &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, + &panic_unimplemented::PANIC, + &panic_unimplemented::PANIC_PARAMS, + &panic_unimplemented::TODO, + &panic_unimplemented::UNIMPLEMENTED, + &panic_unimplemented::UNREACHABLE, + &partialeq_ne_impl::PARTIALEQ_NE_IMPL, + &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, + &precedence::PRECEDENCE, + &ptr::CMP_NULL, + &ptr::MUT_FROM_REF, + &ptr::PTR_ARG, + &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, + &question_mark::QUESTION_MARK, + &ranges::RANGE_MINUS_ONE, + &ranges::RANGE_PLUS_ONE, + &ranges::RANGE_ZIP_WITH_LEN, + &ranges::REVERSED_EMPTY_RANGES, + &redundant_clone::REDUNDANT_CLONE, + &redundant_field_names::REDUNDANT_FIELD_NAMES, + &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, + &redundant_pub_crate::REDUNDANT_PUB_CRATE, + &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, + &reference::DEREF_ADDROF, + &reference::REF_IN_DEREF, + ®ex::INVALID_REGEX, + ®ex::REGEX_MACRO, + ®ex::TRIVIAL_REGEX, + &returns::LET_AND_RETURN, + &returns::NEEDLESS_RETURN, + &returns::UNUSED_UNIT, + &serde_api::SERDE_API_MISUSE, + &shadow::SHADOW_REUSE, + &shadow::SHADOW_SAME, + &shadow::SHADOW_UNRELATED, + &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, + &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &strings::STRING_ADD, + &strings::STRING_ADD_ASSIGN, + &strings::STRING_LIT_AS_BYTES, + &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, + &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, + &swap::ALMOST_SWAPPED, + &swap::MANUAL_SWAP, + &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, + &temporary_assignment::TEMPORARY_ASSIGNMENT, + &to_digit_is_some::TO_DIGIT_IS_SOME, + &trait_bounds::TYPE_REPETITION_IN_BOUNDS, + &transmute::CROSSPOINTER_TRANSMUTE, + &transmute::TRANSMUTE_BYTES_TO_STR, + &transmute::TRANSMUTE_FLOAT_TO_INT, + &transmute::TRANSMUTE_INT_TO_BOOL, + &transmute::TRANSMUTE_INT_TO_CHAR, + &transmute::TRANSMUTE_INT_TO_FLOAT, + &transmute::TRANSMUTE_PTR_TO_PTR, + &transmute::TRANSMUTE_PTR_TO_REF, + &transmute::UNSOUND_COLLECTION_TRANSMUTE, + &transmute::USELESS_TRANSMUTE, + &transmute::WRONG_TRANSMUTE, + &transmuting_null::TRANSMUTING_NULL, + &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF, + &try_err::TRY_ERR, + &types::ABSURD_EXTREME_COMPARISONS, + &types::BORROWED_BOX, + &types::BOX_VEC, + &types::CAST_LOSSLESS, + &types::CAST_POSSIBLE_TRUNCATION, + &types::CAST_POSSIBLE_WRAP, + &types::CAST_PRECISION_LOSS, + &types::CAST_PTR_ALIGNMENT, + &types::CAST_REF_TO_MUT, + &types::CAST_SIGN_LOSS, + &types::CHAR_LIT_AS_U8, + &types::FN_TO_NUMERIC_CAST, + &types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + &types::IMPLICIT_HASHER, + &types::INVALID_UPCAST_COMPARISONS, + &types::LET_UNIT_VALUE, + &types::LINKEDLIST, + &types::OPTION_OPTION, + &types::REDUNDANT_ALLOCATION, + &types::TYPE_COMPLEXITY, + &types::UNIT_ARG, + &types::UNIT_CMP, + &types::UNNECESSARY_CAST, + &types::VEC_BOX, + &unicode::NON_ASCII_LITERAL, + &unicode::UNICODE_NOT_NFC, + &unicode::ZERO_WIDTH_SPACE, + &unnamed_address::FN_ADDRESS_COMPARISONS, + &unnamed_address::VTABLE_ADDRESS_COMPARISONS, + &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + &unused_io_amount::UNUSED_IO_AMOUNT, + &unused_self::UNUSED_SELF, + &unwrap::PANICKING_UNWRAP, + &unwrap::UNNECESSARY_UNWRAP, + &use_self::USE_SELF, + &useless_conversion::USELESS_CONVERSION, + &utils::internal_lints::CLIPPY_LINTS_INTERNAL, + &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + &utils::internal_lints::DEFAULT_LINT, + &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + &utils::internal_lints::PRODUCE_ICE, + &vec::USELESS_VEC, + &verbose_file_reads::VERBOSE_FILE_READS, + &wildcard_dependencies::WILDCARD_DEPENDENCIES, + &wildcard_imports::ENUM_GLOB_USE, + &wildcard_imports::WILDCARD_IMPORTS, + &write::PRINTLN_EMPTY_STRING, + &write::PRINT_LITERAL, + &write::PRINT_STDOUT, + &write::PRINT_WITH_NEWLINE, + &write::USE_DEBUG, + &write::WRITELN_EMPTY_STRING, + &write::WRITE_LITERAL, + &write::WRITE_WITH_NEWLINE, + &zero_div_zero::ZERO_DIVIDED_BY_ZERO, + ]); + // end register lints, do not remove this comment, it’s used in `update_lints` + + store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box serde_api::SerdeAPI); + store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + store.register_late_pass(|| box utils::author::Author); + let vec_box_size_threshold = conf.vec_box_size_threshold; + store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); + store.register_late_pass(|| box booleans::NonminimalBool); + store.register_late_pass(|| box eq_op::EqOp); + store.register_late_pass(|| box enum_clike::UnportableVariant); + store.register_late_pass(|| box float_literal::FloatLiteral); + let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; + store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold)); + store.register_late_pass(|| box ptr::Ptr); + store.register_late_pass(|| box needless_bool::NeedlessBool); + store.register_late_pass(|| box needless_bool::BoolComparison); + store.register_late_pass(|| box approx_const::ApproxConstant); + store.register_late_pass(|| box misc::MiscLints); + store.register_late_pass(|| box eta_reduction::EtaReduction); + store.register_late_pass(|| box identity_op::IdentityOp); + store.register_late_pass(|| box erasing_op::ErasingOp); + store.register_late_pass(|| box mut_mut::MutMut); + store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed); + store.register_late_pass(|| box len_zero::LenZero); + store.register_late_pass(|| box attrs::Attributes); + store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); + store.register_late_pass(|| box unicode::Unicode); + store.register_late_pass(|| box strings::StringAdd); + store.register_late_pass(|| box implicit_return::ImplicitReturn); + store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); + store.register_late_pass(|| box methods::Methods); + store.register_late_pass(|| box map_clone::MapClone); + store.register_late_pass(|| box shadow::Shadow); + store.register_late_pass(|| box types::LetUnitValue); + store.register_late_pass(|| box types::UnitCmp); + store.register_late_pass(|| box loops::Loops); + store.register_late_pass(|| box main_recursion::MainRecursion::default()); + store.register_late_pass(|| box lifetimes::Lifetimes); + store.register_late_pass(|| box entry::HashMapPass); + store.register_late_pass(|| box ranges::Ranges); + store.register_late_pass(|| box types::Casts); + let type_complexity_threshold = conf.type_complexity_threshold; + store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); + store.register_late_pass(|| box matches::Matches::default()); + store.register_late_pass(|| box minmax::MinMaxPass); + store.register_late_pass(|| box open_options::OpenOptions); + store.register_late_pass(|| box zero_div_zero::ZeroDiv); + store.register_late_pass(|| box mutex_atomic::Mutex); + store.register_late_pass(|| box needless_update::NeedlessUpdate); + store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default()); + store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef); + store.register_late_pass(|| box no_effect::NoEffect); + store.register_late_pass(|| box temporary_assignment::TemporaryAssignment); + store.register_late_pass(|| box transmute::Transmute); + let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; + store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)); + let too_large_for_stack = conf.too_large_for_stack; + store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); + store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); + store.register_late_pass(|| box strings::StringLitAsBytes); + store.register_late_pass(|| box derive::Derive); + store.register_late_pass(|| box types::CharLitAsU8); + store.register_late_pass(|| box vec::UselessVec); + store.register_late_pass(|| box drop_bounds::DropBounds); + store.register_late_pass(|| box get_last_with_len::GetLastWithLen); + store.register_late_pass(|| box drop_forget_ref::DropForgetRef); + store.register_late_pass(|| box empty_enum::EmptyEnum); + store.register_late_pass(|| box types::AbsurdExtremeComparisons); + store.register_late_pass(|| box types::InvalidUpcastComparisons); + store.register_late_pass(|| box regex::Regex::default()); + store.register_late_pass(|| box copies::CopyAndPaste); + store.register_late_pass(|| box copy_iterator::CopyIterator); + store.register_late_pass(|| box format::UselessFormat); + store.register_late_pass(|| box swap::Swap); + store.register_late_pass(|| box overflow_check_conditional::OverflowCheckConditional); + store.register_late_pass(|| box new_without_default::NewWithoutDefault::default()); + let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::>(); + store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone())); + let too_many_arguments_threshold1 = conf.too_many_arguments_threshold; + let too_many_lines_threshold2 = conf.too_many_lines_threshold; + store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold1, too_many_lines_threshold2)); + let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::>(); + store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone())); + store.register_late_pass(|| box neg_multiply::NegMultiply); + store.register_late_pass(|| box mem_discriminant::MemDiscriminant); + store.register_late_pass(|| box mem_forget::MemForget); + store.register_late_pass(|| box mem_replace::MemReplace); + store.register_late_pass(|| box arithmetic::Arithmetic::default()); + store.register_late_pass(|| box assign_ops::AssignOps); + store.register_late_pass(|| box let_if_seq::LetIfSeq); + store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); + store.register_late_pass(|| box missing_doc::MissingDoc::new()); + store.register_late_pass(|| box missing_inline::MissingInline); + store.register_late_pass(|| box if_let_some_result::OkIfLet); + store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching); + store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); + store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); + let enum_variant_size_threshold = conf.enum_variant_size_threshold; + store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)); + store.register_late_pass(|| box explicit_write::ExplicitWrite); + store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue); + let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new( + conf.trivial_copy_size_limit, + &sess.target, + ); + store.register_late_pass(move || box trivially_copy_pass_by_ref); + store.register_late_pass(|| box try_err::TryErr); + store.register_late_pass(|| box use_self::UseSelf); + store.register_late_pass(|| box bytecount::ByteCount); + store.register_late_pass(|| box infinite_iter::InfiniteIter); + store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); + store.register_late_pass(|| box useless_conversion::UselessConversion::default()); + store.register_late_pass(|| box types::ImplicitHasher); + store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); + store.register_late_pass(|| box types::UnitArg); + store.register_late_pass(|| box double_comparison::DoubleComparisons); + store.register_late_pass(|| box question_mark::QuestionMark); + store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); + store.register_late_pass(|| box map_unit_fn::MapUnit); + store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default()); + store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); + store.register_late_pass(|| box unwrap::Unwrap); + store.register_late_pass(|| box duration_subsec::DurationSubsec); + store.register_late_pass(|| box default_trait_access::DefaultTraitAccess); + store.register_late_pass(|| box indexing_slicing::IndexingSlicing); + store.register_late_pass(|| box non_copy_const::NonCopyConst); + store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); + store.register_late_pass(|| box redundant_clone::RedundantClone); + store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); + store.register_late_pass(|| box types::RefToMut); + store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); + store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); + store.register_late_pass(|| box transmuting_null::TransmutingNull); + store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); + store.register_late_pass(|| box checked_conversions::CheckedConversions); + store.register_late_pass(|| box integer_division::IntegerDivision); + store.register_late_pass(|| box inherent_to_string::InherentToString); + store.register_late_pass(|| box trait_bounds::TraitBounds); + store.register_late_pass(|| box comparison_chain::ComparisonChain); + store.register_late_pass(|| box mut_key::MutableKeyType); + store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic); + store.register_early_pass(|| box reference::DerefAddrOf); + store.register_early_pass(|| box reference::RefInDeref); + store.register_early_pass(|| box double_parens::DoubleParens); + store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval); + store.register_early_pass(|| box if_not_else::IfNotElse); + store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); + store.register_early_pass(|| box int_plus_one::IntPlusOne); + store.register_early_pass(|| box formatting::Formatting); + store.register_early_pass(|| box misc_early::MiscEarlyLints); + store.register_early_pass(|| box returns::Return); + store.register_early_pass(|| box collapsible_if::CollapsibleIf); + store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); + store.register_early_pass(|| box precedence::Precedence); + store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); + store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); + store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); + store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); + store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); + let literal_representation_threshold = conf.literal_representation_threshold; + store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); + store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); + let enum_variant_name_threshold = conf.enum_variant_name_threshold; + store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); + store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + store.register_late_pass(|| box unused_self::UnusedSelf); + store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); + store.register_late_pass(|| box exit::Exit); + store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome); + let array_size_threshold = conf.array_size_threshold; + store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold)); + store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); + store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic); + store.register_early_pass(|| box as_conversions::AsConversions); + store.register_early_pass(|| box utils::internal_lints::ProduceIce); + store.register_late_pass(|| box let_underscore::LetUnderscore); + store.register_late_pass(|| box atomic_ordering::AtomicOrdering); + store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); + let max_fn_params_bools = conf.max_fn_params_bools; + let max_struct_bools = conf.max_struct_bools; + store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)); + store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap); + let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; + store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)); + store.register_early_pass(|| box macro_use::MacroUseImports); + store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); + store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); + store.register_late_pass(|| box unnamed_address::UnnamedAddress); + store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box future_not_send::FutureNotSend); + store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); + store.register_late_pass(|| box if_let_mutex::IfLetMutex); + store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); + store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); + store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); + + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ + LintId::of(&arithmetic::FLOAT_ARITHMETIC), + LintId::of(&arithmetic::INTEGER_ARITHMETIC), + LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&dbg_macro::DBG_MACRO), + LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(&exit::EXIT), + LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), + LintId::of(&implicit_return::IMPLICIT_RETURN), + LintId::of(&indexing_slicing::INDEXING_SLICING), + LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL), + LintId::of(&integer_division::INTEGER_DIVISION), + LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE), + LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION), + LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), + LintId::of(&mem_forget::MEM_FORGET), + LintId::of(&methods::CLONE_ON_REF_PTR), + LintId::of(&methods::EXPECT_USED), + LintId::of(&methods::FILETYPE_IS_FILE), + LintId::of(&methods::GET_UNWRAP), + LintId::of(&methods::UNWRAP_USED), + LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), + LintId::of(&misc::FLOAT_CMP_CONST), + LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), + LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), + LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(&panic_unimplemented::PANIC), + LintId::of(&panic_unimplemented::TODO), + LintId::of(&panic_unimplemented::UNIMPLEMENTED), + LintId::of(&panic_unimplemented::UNREACHABLE), + LintId::of(&shadow::SHADOW_REUSE), + LintId::of(&shadow::SHADOW_SAME), + LintId::of(&strings::STRING_ADD), + LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(&write::PRINT_STDOUT), + LintId::of(&write::USE_DEBUG), + ]); + + store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ + LintId::of(&attrs::INLINE_ALWAYS), + LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&checked_conversions::CHECKED_CONVERSIONS), + LintId::of(&copies::MATCH_SAME_ARMS), + LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(©_iterator::COPY_ITERATOR), + LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), + LintId::of(&dereference::EXPLICIT_DEREF_METHODS), + LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY), + LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(&doc::DOC_MARKDOWN), + LintId::of(&doc::MISSING_ERRORS_DOC), + LintId::of(&empty_enum::EMPTY_ENUM), + LintId::of(&enum_variants::MODULE_NAME_REPETITIONS), + LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES), + LintId::of(&eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), + LintId::of(&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), + LintId::of(&excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(&functions::MUST_USE_CANDIDATE), + LintId::of(&functions::TOO_MANY_LINES), + LintId::of(&if_not_else::IF_NOT_ELSE), + LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB), + LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), + LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), + LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), + LintId::of(&literal_representation::UNREADABLE_LITERAL), + LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), + LintId::of(&loops::EXPLICIT_ITER_LOOP), + LintId::of(¯o_use::MACRO_USE_IMPORTS), + LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), + LintId::of(&matches::MATCH_BOOL), + LintId::of(&matches::SINGLE_MATCH_ELSE), + LintId::of(&methods::FILTER_MAP), + LintId::of(&methods::FILTER_MAP_NEXT), + LintId::of(&methods::FIND_MAP), + LintId::of(&methods::INEFFICIENT_TO_STRING), + LintId::of(&methods::MAP_FLATTEN), + LintId::of(&methods::MAP_UNWRAP_OR), + LintId::of(&misc::USED_UNDERSCORE_BINDING), + LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), + LintId::of(&mut_mut::MUT_MUT), + LintId::of(&needless_continue::NEEDLESS_CONTINUE), + LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), + LintId::of(&non_expressive_names::SIMILAR_NAMES), + LintId::of(&ranges::RANGE_PLUS_ONE), + LintId::of(&shadow::SHADOW_UNRELATED), + LintId::of(&strings::STRING_ADD_ASSIGN), + LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), + LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), + LintId::of(&types::CAST_LOSSLESS), + LintId::of(&types::CAST_POSSIBLE_TRUNCATION), + LintId::of(&types::CAST_POSSIBLE_WRAP), + LintId::of(&types::CAST_PRECISION_LOSS), + LintId::of(&types::CAST_SIGN_LOSS), + LintId::of(&types::IMPLICIT_HASHER), + LintId::of(&types::INVALID_UPCAST_COMPARISONS), + LintId::of(&types::LET_UNIT_VALUE), + LintId::of(&types::LINKEDLIST), + LintId::of(&types::OPTION_OPTION), + LintId::of(&unicode::NON_ASCII_LITERAL), + LintId::of(&unicode::UNICODE_NOT_NFC), + LintId::of(&unused_self::UNUSED_SELF), + LintId::of(&wildcard_imports::ENUM_GLOB_USE), + LintId::of(&wildcard_imports::WILDCARD_IMPORTS), + ]); + + store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ + LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), + LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), + LintId::of(&utils::internal_lints::DEFAULT_LINT), + LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), + LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), + LintId::of(&utils::internal_lints::PRODUCE_ICE), + ]); + + store.register_group(true, "clippy::all", Some("clippy"), vec![ + LintId::of(&approx_const::APPROX_CONSTANT), + LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(&assign_ops::ASSIGN_OP_PATTERN), + LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), + LintId::of(&attrs::DEPRECATED_CFG_ATTR), + LintId::of(&attrs::DEPRECATED_SEMVER), + LintId::of(&attrs::MISMATCHED_TARGET_OS), + LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), + LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&bit_mask::BAD_BIT_MASK), + LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(&bit_mask::VERBOSE_BIT_MASK), + LintId::of(&blacklisted_name::BLACKLISTED_NAME), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(&booleans::LOGIC_BUG), + LintId::of(&booleans::NONMINIMAL_BOOL), + LintId::of(&bytecount::NAIVE_BYTECOUNT), + LintId::of(&collapsible_if::COLLAPSIBLE_IF), + LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&copies::IFS_SAME_COND), + LintId::of(&copies::IF_SAME_THEN_ELSE), + LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&doc::MISSING_SAFETY_DOC), + LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(&double_comparison::DOUBLE_COMPARISONS), + LintId::of(&double_parens::DOUBLE_PARENS), + LintId::of(&drop_bounds::DROP_BOUNDS), + LintId::of(&drop_forget_ref::DROP_COPY), + LintId::of(&drop_forget_ref::DROP_REF), + LintId::of(&drop_forget_ref::FORGET_COPY), + LintId::of(&drop_forget_ref::FORGET_REF), + LintId::of(&duration_subsec::DURATION_SUBSEC), + LintId::of(&entry::MAP_ENTRY), + LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), + LintId::of(&enum_variants::ENUM_VARIANT_NAMES), + LintId::of(&enum_variants::MODULE_INCEPTION), + LintId::of(&eq_op::EQ_OP), + LintId::of(&eq_op::OP_REF), + LintId::of(&erasing_op::ERASING_OP), + LintId::of(&escape::BOXED_LOCAL), + LintId::of(&eta_reduction::REDUNDANT_CLOSURE), + LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), + LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(&explicit_write::EXPLICIT_WRITE), + LintId::of(&float_literal::EXCESSIVE_PRECISION), + LintId::of(&format::USELESS_FORMAT), + LintId::of(&formatting::POSSIBLE_MISSING_COMMA), + LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(&functions::DOUBLE_MUST_USE), + LintId::of(&functions::MUST_USE_UNIT), + LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(&functions::TOO_MANY_ARGUMENTS), + LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(&identity_op::IDENTITY_OP), + LintId::of(&if_let_mutex::IF_LET_MUTEX), + LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(&infinite_iter::INFINITE_ITER), + LintId::of(&inherent_to_string::INHERENT_TO_STRING), + LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(&int_plus_one::INT_PLUS_ONE), + LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), + LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), + LintId::of(&len_zero::LEN_ZERO), + LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), + LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(&lifetimes::NEEDLESS_LIFETIMES), + LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(&loops::EMPTY_LOOP), + LintId::of(&loops::EXPLICIT_COUNTER_LOOP), + LintId::of(&loops::FOR_KV_MAP), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(&loops::ITER_NEXT_LOOP), + LintId::of(&loops::MANUAL_MEMCPY), + LintId::of(&loops::MUT_RANGE_BOUND), + LintId::of(&loops::NEEDLESS_COLLECT), + LintId::of(&loops::NEEDLESS_RANGE_LOOP), + LintId::of(&loops::NEVER_LOOP), + LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(&loops::WHILE_LET_LOOP), + LintId::of(&loops::WHILE_LET_ON_ITERATOR), + LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(&map_clone::MAP_CLONE), + LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), + LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(&matches::MATCH_AS_REF), + LintId::of(&matches::MATCH_OVERLAPPING_ARM), + LintId::of(&matches::MATCH_REF_PATS), + LintId::of(&matches::MATCH_SINGLE_BINDING), + LintId::of(&matches::MATCH_WILD_ERR_ARM), + LintId::of(&matches::SINGLE_MATCH), + LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), + LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), + LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), + LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(&methods::BIND_INSTEAD_OF_MAP), + LintId::of(&methods::CHARS_LAST_CMP), + LintId::of(&methods::CHARS_NEXT_CMP), + LintId::of(&methods::CLONE_DOUBLE_REF), + LintId::of(&methods::CLONE_ON_COPY), + LintId::of(&methods::EXPECT_FUN_CALL), + LintId::of(&methods::FILTER_NEXT), + LintId::of(&methods::FLAT_MAP_IDENTITY), + LintId::of(&methods::INTO_ITER_ON_REF), + LintId::of(&methods::ITERATOR_STEP_BY_ZERO), + LintId::of(&methods::ITER_CLONED_COLLECT), + LintId::of(&methods::ITER_NTH), + LintId::of(&methods::ITER_NTH_ZERO), + LintId::of(&methods::ITER_SKIP_NEXT), + LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(&methods::NEW_RET_NO_SELF), + LintId::of(&methods::OK_EXPECT), + LintId::of(&methods::OPTION_AS_REF_DEREF), + LintId::of(&methods::OPTION_MAP_OR_NONE), + LintId::of(&methods::OR_FUN_CALL), + LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(&methods::SEARCH_IS_SOME), + LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::SINGLE_CHAR_PATTERN), + LintId::of(&methods::SKIP_WHILE_NEXT), + LintId::of(&methods::STRING_EXTEND_CHARS), + LintId::of(&methods::SUSPICIOUS_MAP), + LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR), + LintId::of(&methods::UNINIT_ASSUMED_INIT), + LintId::of(&methods::UNNECESSARY_FILTER_MAP), + LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::USELESS_ASREF), + LintId::of(&methods::WRONG_SELF_CONVENTION), + LintId::of(&methods::ZST_OFFSET), + LintId::of(&minmax::MIN_MAX), + LintId::of(&misc::CMP_NAN), + LintId::of(&misc::CMP_OWNED), + LintId::of(&misc::FLOAT_CMP), + LintId::of(&misc::MODULO_ONE), + LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), + LintId::of(&misc::TOPLEVEL_REF_ARG), + LintId::of(&misc::ZERO_PTR), + LintId::of(&misc_early::BUILTIN_TYPE_SHADOW), + LintId::of(&misc_early::DOUBLE_NEG), + LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), + LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), + LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), + LintId::of(&misc_early::REDUNDANT_PATTERN), + LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), + LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(&mut_key::MUTABLE_KEY_TYPE), + LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(&mutex_atomic::MUTEX_ATOMIC), + LintId::of(&needless_bool::BOOL_COMPARISON), + LintId::of(&needless_bool::NEEDLESS_BOOL), + LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_update::NEEDLESS_UPDATE), + LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), + LintId::of(&neg_multiply::NEG_MULTIPLY), + LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(&no_effect::NO_EFFECT), + LintId::of(&no_effect::UNNECESSARY_OPERATION), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), + LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), + LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + LintId::of(&panic_unimplemented::PANIC_PARAMS), + LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), + LintId::of(&precedence::PRECEDENCE), + LintId::of(&ptr::CMP_NULL), + LintId::of(&ptr::MUT_FROM_REF), + LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), + LintId::of(&question_mark::QUESTION_MARK), + LintId::of(&ranges::RANGE_MINUS_ONE), + LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), + LintId::of(&redundant_clone::REDUNDANT_CLONE), + LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), + LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), + LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(&reference::DEREF_ADDROF), + LintId::of(&reference::REF_IN_DEREF), + LintId::of(®ex::INVALID_REGEX), + LintId::of(®ex::REGEX_MACRO), + LintId::of(®ex::TRIVIAL_REGEX), + LintId::of(&returns::LET_AND_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), + LintId::of(&returns::UNUSED_UNIT), + LintId::of(&serde_api::SERDE_API_MISUSE), + LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&strings::STRING_LIT_AS_BYTES), + LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(&swap::ALMOST_SWAPPED), + LintId::of(&swap::MANUAL_SWAP), + LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), + LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), + LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), + LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR), + LintId::of(&transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(&transmute::WRONG_TRANSMUTE), + LintId::of(&transmuting_null::TRANSMUTING_NULL), + LintId::of(&try_err::TRY_ERR), + LintId::of(&types::ABSURD_EXTREME_COMPARISONS), + LintId::of(&types::BORROWED_BOX), + LintId::of(&types::BOX_VEC), + LintId::of(&types::CAST_PTR_ALIGNMENT), + LintId::of(&types::CAST_REF_TO_MUT), + LintId::of(&types::CHAR_LIT_AS_U8), + LintId::of(&types::FN_TO_NUMERIC_CAST), + LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(&types::REDUNDANT_ALLOCATION), + LintId::of(&types::TYPE_COMPLEXITY), + LintId::of(&types::UNIT_ARG), + LintId::of(&types::UNIT_CMP), + LintId::of(&types::UNNECESSARY_CAST), + LintId::of(&types::VEC_BOX), + LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), + LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(&unwrap::PANICKING_UNWRAP), + LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), + LintId::of(&vec::USELESS_VEC), + LintId::of(&write::PRINTLN_EMPTY_STRING), + LintId::of(&write::PRINT_LITERAL), + LintId::of(&write::PRINT_WITH_NEWLINE), + LintId::of(&write::WRITELN_EMPTY_STRING), + LintId::of(&write::WRITE_LITERAL), + LintId::of(&write::WRITE_WITH_NEWLINE), + LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), + ]); + + store.register_group(true, "clippy::style", Some("clippy_style"), vec![ + LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(&assign_ops::ASSIGN_OP_PATTERN), + LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), + LintId::of(&bit_mask::VERBOSE_BIT_MASK), + LintId::of(&blacklisted_name::BLACKLISTED_NAME), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(&collapsible_if::COLLAPSIBLE_IF), + LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&doc::MISSING_SAFETY_DOC), + LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(&enum_variants::ENUM_VARIANT_NAMES), + LintId::of(&enum_variants::MODULE_INCEPTION), + LintId::of(&eq_op::OP_REF), + LintId::of(&eta_reduction::REDUNDANT_CLOSURE), + LintId::of(&float_literal::EXCESSIVE_PRECISION), + LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(&functions::DOUBLE_MUST_USE), + LintId::of(&functions::MUST_USE_UNIT), + LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(&inherent_to_string::INHERENT_TO_STRING), + LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), + LintId::of(&len_zero::LEN_ZERO), + LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(&loops::EMPTY_LOOP), + LintId::of(&loops::FOR_KV_MAP), + LintId::of(&loops::NEEDLESS_RANGE_LOOP), + LintId::of(&loops::WHILE_LET_ON_ITERATOR), + LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(&map_clone::MAP_CLONE), + LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(&matches::MATCH_OVERLAPPING_ARM), + LintId::of(&matches::MATCH_REF_PATS), + LintId::of(&matches::MATCH_WILD_ERR_ARM), + LintId::of(&matches::SINGLE_MATCH), + LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), + LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), + LintId::of(&methods::CHARS_LAST_CMP), + LintId::of(&methods::CHARS_NEXT_CMP), + LintId::of(&methods::INTO_ITER_ON_REF), + LintId::of(&methods::ITER_CLONED_COLLECT), + LintId::of(&methods::ITER_NTH_ZERO), + LintId::of(&methods::ITER_SKIP_NEXT), + LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(&methods::NEW_RET_NO_SELF), + LintId::of(&methods::OK_EXPECT), + LintId::of(&methods::OPTION_MAP_OR_NONE), + LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::STRING_EXTEND_CHARS), + LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::WRONG_SELF_CONVENTION), + LintId::of(&misc::TOPLEVEL_REF_ARG), + LintId::of(&misc::ZERO_PTR), + LintId::of(&misc_early::BUILTIN_TYPE_SHADOW), + LintId::of(&misc_early::DOUBLE_NEG), + LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), + LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), + LintId::of(&misc_early::REDUNDANT_PATTERN), + LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(&neg_multiply::NEG_MULTIPLY), + LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), + LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(&panic_unimplemented::PANIC_PARAMS), + LintId::of(&ptr::CMP_NULL), + LintId::of(&ptr::PTR_ARG), + LintId::of(&question_mark::QUESTION_MARK), + LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), + LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), + LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(®ex::REGEX_MACRO), + LintId::of(®ex::TRIVIAL_REGEX), + LintId::of(&returns::LET_AND_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), + LintId::of(&returns::UNUSED_UNIT), + LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(&strings::STRING_LIT_AS_BYTES), + LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), + LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(&try_err::TRY_ERR), + LintId::of(&types::FN_TO_NUMERIC_CAST), + LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&write::PRINTLN_EMPTY_STRING), + LintId::of(&write::PRINT_LITERAL), + LintId::of(&write::PRINT_WITH_NEWLINE), + LintId::of(&write::WRITELN_EMPTY_STRING), + LintId::of(&write::WRITE_LITERAL), + LintId::of(&write::WRITE_WITH_NEWLINE), + ]); + + store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ + LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(&attrs::DEPRECATED_CFG_ATTR), + LintId::of(&booleans::NONMINIMAL_BOOL), + LintId::of(&double_comparison::DOUBLE_COMPARISONS), + LintId::of(&double_parens::DOUBLE_PARENS), + LintId::of(&duration_subsec::DURATION_SUBSEC), + LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), + LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(&explicit_write::EXPLICIT_WRITE), + LintId::of(&format::USELESS_FORMAT), + LintId::of(&functions::TOO_MANY_ARGUMENTS), + LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(&identity_op::IDENTITY_OP), + LintId::of(&int_plus_one::INT_PLUS_ONE), + LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(&lifetimes::NEEDLESS_LIFETIMES), + LintId::of(&loops::EXPLICIT_COUNTER_LOOP), + LintId::of(&loops::MUT_RANGE_BOUND), + LintId::of(&loops::WHILE_LET_LOOP), + LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), + LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(&matches::MATCH_AS_REF), + LintId::of(&matches::MATCH_SINGLE_BINDING), + LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(&methods::BIND_INSTEAD_OF_MAP), + LintId::of(&methods::CLONE_ON_COPY), + LintId::of(&methods::FILTER_NEXT), + LintId::of(&methods::FLAT_MAP_IDENTITY), + LintId::of(&methods::OPTION_AS_REF_DEREF), + LintId::of(&methods::SEARCH_IS_SOME), + LintId::of(&methods::SKIP_WHILE_NEXT), + LintId::of(&methods::SUSPICIOUS_MAP), + LintId::of(&methods::UNNECESSARY_FILTER_MAP), + LintId::of(&methods::USELESS_ASREF), + LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), + LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), + LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), + LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(&needless_bool::BOOL_COMPARISON), + LintId::of(&needless_bool::NEEDLESS_BOOL), + LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_update::NEEDLESS_UPDATE), + LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), + LintId::of(&no_effect::NO_EFFECT), + LintId::of(&no_effect::UNNECESSARY_OPERATION), + LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), + LintId::of(&precedence::PRECEDENCE), + LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), + LintId::of(&ranges::RANGE_MINUS_ONE), + LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&reference::DEREF_ADDROF), + LintId::of(&reference::REF_IN_DEREF), + LintId::of(&swap::MANUAL_SWAP), + LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), + LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), + LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR), + LintId::of(&transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(&types::BORROWED_BOX), + LintId::of(&types::CHAR_LIT_AS_U8), + LintId::of(&types::TYPE_COMPLEXITY), + LintId::of(&types::UNIT_ARG), + LintId::of(&types::UNNECESSARY_CAST), + LintId::of(&types::VEC_BOX), + LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), + LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), + ]); + + store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ + LintId::of(&approx_const::APPROX_CONSTANT), + LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), + LintId::of(&attrs::DEPRECATED_SEMVER), + LintId::of(&attrs::MISMATCHED_TARGET_OS), + LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&bit_mask::BAD_BIT_MASK), + LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(&booleans::LOGIC_BUG), + LintId::of(&copies::IFS_SAME_COND), + LintId::of(&copies::IF_SAME_THEN_ELSE), + LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&drop_bounds::DROP_BOUNDS), + LintId::of(&drop_forget_ref::DROP_COPY), + LintId::of(&drop_forget_ref::DROP_REF), + LintId::of(&drop_forget_ref::FORGET_COPY), + LintId::of(&drop_forget_ref::FORGET_REF), + LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), + LintId::of(&eq_op::EQ_OP), + LintId::of(&erasing_op::ERASING_OP), + LintId::of(&formatting::POSSIBLE_MISSING_COMMA), + LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(&if_let_mutex::IF_LET_MUTEX), + LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(&infinite_iter::INFINITE_ITER), + LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), + LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(&loops::ITER_NEXT_LOOP), + LintId::of(&loops::NEVER_LOOP), + LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), + LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(&methods::CLONE_DOUBLE_REF), + LintId::of(&methods::ITERATOR_STEP_BY_ZERO), + LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR), + LintId::of(&methods::UNINIT_ASSUMED_INIT), + LintId::of(&methods::ZST_OFFSET), + LintId::of(&minmax::MIN_MAX), + LintId::of(&misc::CMP_NAN), + LintId::of(&misc::FLOAT_CMP), + LintId::of(&misc::MODULO_ONE), + LintId::of(&mut_key::MUTABLE_KEY_TYPE), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), + LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(&ptr::MUT_FROM_REF), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), + LintId::of(®ex::INVALID_REGEX), + LintId::of(&serde_api::SERDE_API_MISUSE), + LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(&swap::ALMOST_SWAPPED), + LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(&transmute::WRONG_TRANSMUTE), + LintId::of(&transmuting_null::TRANSMUTING_NULL), + LintId::of(&types::ABSURD_EXTREME_COMPARISONS), + LintId::of(&types::CAST_PTR_ALIGNMENT), + LintId::of(&types::CAST_REF_TO_MUT), + LintId::of(&types::UNIT_CMP), + LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), + LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(&unwrap::PANICKING_UNWRAP), + ]); + + store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ + LintId::of(&bytecount::NAIVE_BYTECOUNT), + LintId::of(&entry::MAP_ENTRY), + LintId::of(&escape::BOXED_LOCAL), + LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), + LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(&loops::MANUAL_MEMCPY), + LintId::of(&loops::NEEDLESS_COLLECT), + LintId::of(&methods::EXPECT_FUN_CALL), + LintId::of(&methods::ITER_NTH), + LintId::of(&methods::OR_FUN_CALL), + LintId::of(&methods::SINGLE_CHAR_PATTERN), + LintId::of(&misc::CMP_OWNED), + LintId::of(&mutex_atomic::MUTEX_ATOMIC), + LintId::of(&redundant_clone::REDUNDANT_CLONE), + LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&types::BOX_VEC), + LintId::of(&types::REDUNDANT_ALLOCATION), + LintId::of(&vec::USELESS_VEC), + ]); + + store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ + LintId::of(&cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), + LintId::of(&wildcard_dependencies::WILDCARD_DEPENDENCIES), + ]); + + store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ + LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), + LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), + LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), + LintId::of(&future_not_send::FUTURE_NOT_SEND), + LintId::of(&let_if_seq::USELESS_LET_IF_SEQ), + LintId::of(&missing_const_for_fn::MISSING_CONST_FOR_FN), + LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), + LintId::of(&mutex_atomic::MUTEX_INTEGER), + LintId::of(&needless_borrow::NEEDLESS_BORROW), + LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), + LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), + LintId::of(&transmute::USELESS_TRANSMUTE), + LintId::of(&use_self::USE_SELF), + ]); +} + +#[rustfmt::skip] +fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { + store.register_removed( + "should_assert_eq", + "`assert!()` will be more flexible with RFC 2011", + ); + store.register_removed( + "extend_from_slice", + "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", + ); + store.register_removed( + "range_step_by_zero", + "`iterator.step_by(0)` panics nowadays", + ); + store.register_removed( + "unstable_as_slice", + "`Vec::as_slice` has been stabilized in 1.7", + ); + store.register_removed( + "unstable_as_mut_slice", + "`Vec::as_mut_slice` has been stabilized in 1.7", + ); + store.register_removed( + "str_to_string", + "using `str::to_string` is common even today and specialization will likely happen soon", + ); + store.register_removed( + "string_to_string", + "using `string::to_string` is common even today and specialization will likely happen soon", + ); + store.register_removed( + "misaligned_transmute", + "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", + ); + store.register_removed( + "assign_ops", + "using compound assignment operators (e.g., `+=`) is harmless", + ); + store.register_removed( + "if_let_redundant_pattern_matching", + "this lint has been changed to redundant_pattern_matching", + ); + store.register_removed( + "unsafe_vector_initialization", + "the replacement suggested by this lint had substantially different behavior", + ); + store.register_removed( + "reverse_range_loop", + "this lint is now included in reversed_empty_ranges", + ); +} + +/// Register renamed lints. +/// +/// Used in `./src/driver.rs`. +pub fn register_renamed(ls: &mut rustc_lint::LintStore) { + ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions"); + ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); + ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); + ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); + ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); + ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); + ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); + ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); +} + +// only exists to let the dogfood integration test works. +// Don't run clippy as an executable directly +#[allow(dead_code)] +fn main() { + panic!("Please use the cargo-clippy executable"); +} diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs new file mode 100644 index 0000000000000..d80ad47ab2468 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -0,0 +1,526 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{ + walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor, +}; +use rustc_hir::FnRetTy::Return; +use rustc_hir::{ + BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, + ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, + TyKind, WhereClause, WherePredicate, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::kw; + +use crate::reexport::Name; +use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; + +declare_clippy_lint! { + /// **What it does:** Checks for lifetime annotations which can be removed by + /// relying on lifetime elision. + /// + /// **Why is this bad?** The additional lifetimes make the code look more + /// complicated, while there is nothing out of the ordinary going on. Removing + /// them leads to more readable code. + /// + /// **Known problems:** Potential false negatives: we bail out if the function + /// has a `where` clause where lifetimes are mentioned. + /// + /// **Example:** + /// ```rust + /// // Bad: unnecessary lifetime annotations + /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { + /// x + /// } + /// + /// // Good + /// fn elided(x: &u8, y: u8) -> &u8 { + /// x + /// } + /// ``` + pub NEEDLESS_LIFETIMES, + complexity, + "using explicit lifetimes for references in function arguments when elision rules \ + would allow omitting them" +} + +declare_clippy_lint! { + /// **What it does:** Checks for lifetimes in generics that are never used + /// anywhere else. + /// + /// **Why is this bad?** The additional lifetimes make the code look more + /// complicated, while there is nothing out of the ordinary going on. Removing + /// them leads to more readable code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad: unnecessary lifetimes + /// fn unused_lifetime<'a>(x: u8) { + /// // .. + /// } + /// + /// // Good + /// fn no_lifetime(x: u8) { + /// // ... + /// } + /// ``` + pub EXTRA_UNUSED_LIFETIMES, + complexity, + "unused lifetimes in function definitions" +} + +declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Lifetimes { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { + check_fn_inner(cx, &sig.decl, Some(id), generics, item.span, true); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(ref sig, id) = item.kind { + let report_extra_lifetimes = trait_ref_of_method(cx, item.hir_id).is_none(); + check_fn_inner( + cx, + &sig.decl, + Some(id), + &item.generics, + item.span, + report_extra_lifetimes, + ); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(ref sig, ref body) = item.kind { + let body = match *body { + TraitFn::Required(_) => None, + TraitFn::Provided(id) => Some(id), + }; + check_fn_inner(cx, &sig.decl, body, &item.generics, item.span, true); + } + } +} + +/// The lifetime of a &-reference. +#[derive(PartialEq, Eq, Hash, Debug)] +enum RefLt { + Unnamed, + Static, + Named(Name), +} + +fn check_fn_inner<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + decl: &'tcx FnDecl<'_>, + body: Option, + generics: &'tcx Generics<'_>, + span: Span, + report_extra_lifetimes: bool, +) { + if in_macro(span) || has_where_lifetimes(cx, &generics.where_clause) { + return; + } + + let mut bounds_lts = Vec::new(); + let types = generics.params.iter().filter(|param| match param.kind { + GenericParamKind::Type { .. } => true, + _ => false, + }); + for typ in types { + for bound in typ.bounds { + let mut visitor = RefVisitor::new(cx); + walk_param_bound(&mut visitor, bound); + if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + return; + } + if let GenericBound::Trait(ref trait_ref, _) = *bound { + let params = &trait_ref + .trait_ref + .path + .segments + .last() + .expect("a path must have at least one segment") + .args; + if let Some(ref params) = *params { + let lifetimes = params.args.iter().filter_map(|arg| match arg { + GenericArg::Lifetime(lt) => Some(lt), + _ => None, + }); + for bound in lifetimes { + if bound.name != LifetimeName::Static && !bound.is_elided() { + return; + } + bounds_lts.push(bound); + } + } + } + } + } + if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { + span_lint( + cx, + NEEDLESS_LIFETIMES, + span.with_hi(decl.output.span().hi()), + "explicit lifetimes given in parameter types where they could be elided \ + (or replaced with `'_` if needed by type declaration)", + ); + } + if report_extra_lifetimes { + self::report_extra_lifetimes(cx, decl, generics); + } +} + +fn could_use_elision<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + func: &'tcx FnDecl<'_>, + body: Option, + named_generics: &'tcx [GenericParam<'_>], + bounds_lts: Vec<&'tcx Lifetime>, +) -> bool { + // There are two scenarios where elision works: + // * no output references, all input references have different LT + // * output references, exactly one input reference with same LT + // All lifetimes must be unnamed, 'static or defined without bounds on the + // level of the current item. + + // check named LTs + let allowed_lts = allowed_lts_from(named_generics); + + // these will collect all the lifetimes for references in arg/return types + let mut input_visitor = RefVisitor::new(cx); + let mut output_visitor = RefVisitor::new(cx); + + // extract lifetimes in input argument types + for arg in func.inputs { + input_visitor.visit_ty(arg); + } + // extract lifetimes in output type + if let Return(ref ty) = func.output { + output_visitor.visit_ty(ty); + } + + let input_lts = match input_visitor.into_vec() { + Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()), + None => return false, + }; + let output_lts = match output_visitor.into_vec() { + Some(val) => val, + None => return false, + }; + + if let Some(body_id) = body { + let mut checker = BodyLifetimeChecker { + lifetimes_used_in_body: false, + }; + checker.visit_expr(&cx.tcx.hir().body(body_id).value); + if checker.lifetimes_used_in_body { + return false; + } + } + + // check for lifetimes from higher scopes + for lt in input_lts.iter().chain(output_lts.iter()) { + if !allowed_lts.contains(lt) { + return false; + } + } + + // no input lifetimes? easy case! + if input_lts.is_empty() { + false + } else if output_lts.is_empty() { + // no output lifetimes, check distinctness of input lifetimes + + // only unnamed and static, ok + let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static); + if unnamed_and_static { + return false; + } + // we have no output reference, so we only need all distinct lifetimes + input_lts.len() == unique_lifetimes(&input_lts) + } else { + // we have output references, so we need one input reference, + // and all output lifetimes must be the same + if unique_lifetimes(&output_lts) > 1 { + return false; + } + if input_lts.len() == 1 { + match (&input_lts[0], &output_lts[0]) { + (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true, + (&RefLt::Named(_), &RefLt::Unnamed) => true, + _ => false, /* already elided, different named lifetimes + * or something static going on */ + } + } else { + false + } + } +} + +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { + let mut allowed_lts = FxHashSet::default(); + for par in named_generics.iter() { + if let GenericParamKind::Lifetime { .. } = par.kind { + if par.bounds.is_empty() { + allowed_lts.insert(RefLt::Named(par.name.ident().name)); + } + } + } + allowed_lts.insert(RefLt::Unnamed); + allowed_lts.insert(RefLt::Static); + allowed_lts +} + +fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { + for lt in bounds_lts { + if lt.name != LifetimeName::Static { + vec.push(RefLt::Named(lt.name.ident().name)); + } + } + + vec +} + +/// Number of unique lifetimes in the given vector. +#[must_use] +fn unique_lifetimes(lts: &[RefLt]) -> usize { + lts.iter().collect::>().len() +} + +/// A visitor usable for `rustc_front::visit::walk_ty()`. +struct RefVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + lts: Vec, + abort: bool, +} + +impl<'v, 't> RefVisitor<'v, 't> { + fn new(cx: &'v LateContext<'v, 't>) -> Self { + Self { + cx, + lts: Vec::new(), + abort: false, + } + } + + fn record(&mut self, lifetime: &Option) { + if let Some(ref lt) = *lifetime { + if lt.name == LifetimeName::Static { + self.lts.push(RefLt::Static); + } else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name { + // Fresh lifetimes generated should be ignored. + } else if lt.is_elided() { + self.lts.push(RefLt::Unnamed); + } else { + self.lts.push(RefLt::Named(lt.name.ident().name)); + } + } else { + self.lts.push(RefLt::Unnamed); + } + } + + fn into_vec(self) -> Option> { + if self.abort { + None + } else { + Some(self.lts) + } + } + + fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { + if let Some(ref last_path_segment) = last_path_segment(qpath).args { + if !last_path_segment.parenthesized + && !last_path_segment.args.iter().any(|arg| match arg { + GenericArg::Lifetime(_) => true, + _ => false, + }) + { + let hir_id = ty.hir_id; + match self.cx.tables.qpath_res(qpath, hir_id) { + Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => { + let generics = self.cx.tcx.generics_of(def_id); + for _ in generics.params.as_slice() { + self.record(&None); + } + }, + Res::Def(DefKind::Trait, def_id) => { + let trait_def = self.cx.tcx.trait_def(def_id); + for _ in &self.cx.tcx.generics_of(trait_def.def_id).params { + self.record(&None); + } + }, + _ => (), + } + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + // for lifetimes as parameters of generics + fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { + self.record(&Some(*lifetime)); + } + + fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { + match ty.kind { + TyKind::Rptr(ref lt, _) if lt.is_elided() => { + self.record(&None); + }, + TyKind::Path(ref path) => { + self.collect_anonymous_lifetimes(path, ty); + }, + TyKind::Def(item, _) => { + let map = self.cx.tcx.hir(); + if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind { + for bound in exist_ty.bounds { + if let GenericBound::Outlives(_) = *bound { + self.record(&None); + } + } + } else { + unreachable!() + } + walk_ty(self, ty); + }, + TyKind::TraitObject(bounds, ref lt) => { + if !lt.is_elided() { + self.abort = true; + } + for bound in bounds { + self.visit_poly_trait_ref(bound, TraitBoundModifier::None); + } + return; + }, + _ => (), + } + walk_ty(self, ty); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to +/// reason about elision. +fn has_where_lifetimes<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool { + for predicate in where_clause.predicates { + match *predicate { + WherePredicate::RegionPredicate(..) => return true, + WherePredicate::BoundPredicate(ref pred) => { + // a predicate like F: Trait or F: for<'a> Trait<'a> + let mut visitor = RefVisitor::new(cx); + // walk the type F, it may not contain LT refs + walk_ty(&mut visitor, &pred.bounded_ty); + if !visitor.lts.is_empty() { + return true; + } + // if the bounds define new lifetimes, they are fine to occur + let allowed_lts = allowed_lts_from(&pred.bound_generic_params); + // now walk the bounds + for bound in pred.bounds.iter() { + walk_param_bound(&mut visitor, bound); + } + // and check that all lifetimes are allowed + match visitor.into_vec() { + None => return false, + Some(lts) => { + for lt in lts { + if !allowed_lts.contains(<) { + return true; + } + } + }, + } + }, + WherePredicate::EqPredicate(ref pred) => { + let mut visitor = RefVisitor::new(cx); + walk_ty(&mut visitor, &pred.lhs_ty); + walk_ty(&mut visitor, &pred.rhs_ty); + if !visitor.lts.is_empty() { + return true; + } + }, + } + } + false +} + +struct LifetimeChecker { + map: FxHashMap, +} + +impl<'tcx> Visitor<'tcx> for LifetimeChecker { + type Map = Map<'tcx>; + + // for lifetimes as parameters of generics + fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { + self.map.remove(&lifetime.name.ident().name); + } + + fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) { + // don't actually visit `<'a>` or `<'a: 'b>` + // we've already visited the `'a` declarations and + // don't want to spuriously remove them + // `'b` in `'a: 'b` is useless unless used elsewhere in + // a non-lifetime bound + if let GenericParamKind::Type { .. } = param.kind { + walk_generic_param(self, param) + } + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn report_extra_lifetimes<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { + let hs = generics + .params + .iter() + .filter_map(|par| match par.kind { + GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + _ => None, + }) + .collect(); + let mut checker = LifetimeChecker { map: hs }; + + walk_generics(&mut checker, generics); + walk_fn_decl(&mut checker, func); + + for &v in checker.map.values() { + span_lint( + cx, + EXTRA_UNUSED_LIFETIMES, + v, + "this lifetime isn't used in the function definition", + ); + } +} + +struct BodyLifetimeChecker { + lifetimes_used_in_body: bool, +} + +impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { + type Map = Map<'tcx>; + + // for lifetimes as parameters of generics + fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { + if lifetime.name.ident().name != kw::Invalid && lifetime.name.ident().name != kw::StaticLifetime { + self.lifetimes_used_in_body = true; + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs new file mode 100644 index 0000000000000..ec7c4531ed716 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -0,0 +1,430 @@ +//! Lints concerned with the grouping of digits with underscores in integral or +//! floating-point literal expressions. + +use crate::utils::{ + in_macro, + numeric_literal::{NumericLiteral, Radix}, + snippet_opt, span_lint_and_sugg, +}; +use if_chain::if_chain; +use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Warns if a long integral or floating-point constant does + /// not contain underscores. + /// + /// **Why is this bad?** Reading long numbers is difficult without separators. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x: u64 = 61864918973511; + /// ``` + pub UNREADABLE_LITERAL, + pedantic, + "long integer literal without underscores" +} + +declare_clippy_lint! { + /// **What it does:** Warns for mistyped suffix in literals + /// + /// **Why is this bad?** This is most probably a typo + /// + /// **Known problems:** + /// - Recommends a signed suffix, even though the number might be too big and an unsigned + /// suffix is required + /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers + /// + /// **Example:** + /// + /// ```rust + /// 2_32; + /// ``` + pub MISTYPED_LITERAL_SUFFIXES, + correctness, + "mistyped literal suffix" +} + +declare_clippy_lint! { + /// **What it does:** Warns if an integral or floating-point constant is + /// grouped inconsistently with underscores. + /// + /// **Why is this bad?** Readers may incorrectly interpret inconsistently + /// grouped digits. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x: u64 = 618_64_9189_73_511; + /// ``` + pub INCONSISTENT_DIGIT_GROUPING, + style, + "integer literals with digits grouped inconsistently" +} + +declare_clippy_lint! { + /// **What it does:** Warns if the digits of an integral or floating-point + /// constant are grouped into groups that + /// are too large. + /// + /// **Why is this bad?** Negatively impacts readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x: u64 = 6186491_8973511; + /// ``` + pub LARGE_DIGIT_GROUPS, + pedantic, + "grouping digits into groups that are too large" +} + +declare_clippy_lint! { + /// **What it does:** Warns if there is a better representation for a numeric literal. + /// + /// **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more + /// readable than a decimal representation. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// `255` => `0xFF` + /// `65_535` => `0xFFFF` + /// `4_042_322_160` => `0xF0F0_F0F0` + pub DECIMAL_LITERAL_REPRESENTATION, + restriction, + "using decimal representation when hexadecimal would be better" +} + +enum WarningType { + UnreadableLiteral, + InconsistentDigitGrouping, + LargeDigitGroups, + DecimalRepresentation, + MistypedLiteralSuffix, +} + +impl WarningType { + fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: rustc_span::Span) { + match self { + Self::MistypedLiteralSuffix => span_lint_and_sugg( + cx, + MISTYPED_LITERAL_SUFFIXES, + span, + "mistyped literal suffix", + "did you mean to write", + suggested_format, + Applicability::MaybeIncorrect, + ), + Self::UnreadableLiteral => span_lint_and_sugg( + cx, + UNREADABLE_LITERAL, + span, + "long literal lacking separators", + "consider", + suggested_format, + Applicability::MachineApplicable, + ), + Self::LargeDigitGroups => span_lint_and_sugg( + cx, + LARGE_DIGIT_GROUPS, + span, + "digit groups should be smaller", + "consider", + suggested_format, + Applicability::MachineApplicable, + ), + Self::InconsistentDigitGrouping => span_lint_and_sugg( + cx, + INCONSISTENT_DIGIT_GROUPING, + span, + "digits grouped inconsistently by underscores", + "consider", + suggested_format, + Applicability::MachineApplicable, + ), + Self::DecimalRepresentation => span_lint_and_sugg( + cx, + DECIMAL_LITERAL_REPRESENTATION, + span, + "integer literal has a better hexadecimal representation", + "consider", + suggested_format, + Applicability::MachineApplicable, + ), + }; + } +} + +declare_lint_pass!(LiteralDigitGrouping => [ + UNREADABLE_LITERAL, + INCONSISTENT_DIGIT_GROUPING, + LARGE_DIGIT_GROUPS, + MISTYPED_LITERAL_SUFFIXES, +]); + +impl EarlyLintPass for LiteralDigitGrouping { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Lit(ref lit) = expr.kind { + Self::check_lit(cx, lit) + } + } +} + +// Length of each UUID hyphenated group in hex digits. +const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; + +impl LiteralDigitGrouping { + fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + if_chain! { + if let Some(src) = snippet_opt(cx, lit.span); + if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); + then { + if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { + return; + } + + if Self::is_literal_uuid_formatted(&mut num_lit) { + return; + } + + let result = (|| { + + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?; + if let Some(fraction) = num_lit.fraction { + let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?; + + let consistent = Self::parts_consistent(integral_group_size, + fractional_group_size, + num_lit.integer.len(), + fraction.len()); + if !consistent { + return Err(WarningType::InconsistentDigitGrouping); + }; + } + Ok(()) + })(); + + + if let Err(warning_type) = result { + let should_warn = match warning_type { + | WarningType::UnreadableLiteral + | WarningType::InconsistentDigitGrouping + | WarningType::LargeDigitGroups => { + !in_macro(lit.span) + } + WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => { + true + } + }; + if should_warn { + warning_type.display(num_lit.format(), cx, lit.span) + } + } + } + } + } + + // Returns `false` if the check fails + fn check_for_mistyped_suffix( + cx: &EarlyContext<'_>, + span: rustc_span::Span, + num_lit: &mut NumericLiteral<'_>, + ) -> bool { + if num_lit.suffix.is_some() { + return true; + } + + let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { + (exponent, &["32", "64"][..], 'f') + } else if let Some(fraction) = &mut num_lit.fraction { + (fraction, &["32", "64"][..], 'f') + } else { + (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') + }; + + let mut split = part.rsplit('_'); + let last_group = split.next().expect("At least one group"); + if split.next().is_some() && mistyped_suffixes.contains(&last_group) { + *part = &part[..part.len() - last_group.len()]; + let mut sugg = num_lit.format(); + sugg.push('_'); + sugg.push(missing_char); + sugg.push_str(last_group); + WarningType::MistypedLiteralSuffix.display(sugg, cx, span); + false + } else { + true + } + } + + /// Checks whether the numeric literal matches the formatting of a UUID. + /// + /// Returns `true` if the radix is hexadecimal, and the groups match the + /// UUID format of 8-4-4-4-12. + fn is_literal_uuid_formatted(num_lit: &mut NumericLiteral<'_>) -> bool { + if num_lit.radix != Radix::Hexadecimal { + return false; + } + + // UUIDs should not have a fraction + if num_lit.fraction.is_some() { + return false; + } + + let group_sizes: Vec = num_lit.integer.split('_').map(str::len).collect(); + if UUID_GROUP_LENS.len() == group_sizes.len() { + UUID_GROUP_LENS.iter().zip(&group_sizes).all(|(&a, &b)| a == b) + } else { + false + } + } + + /// Given the sizes of the digit groups of both integral and fractional + /// parts, and the length + /// of both parts, determine if the digits have been grouped consistently. + #[must_use] + fn parts_consistent( + int_group_size: Option, + frac_group_size: Option, + int_size: usize, + frac_size: usize, + ) -> bool { + match (int_group_size, frac_group_size) { + // No groups on either side of decimal point - trivially consistent. + (None, None) => true, + // Integral part has grouped digits, fractional part does not. + (Some(int_group_size), None) => frac_size <= int_group_size, + // Fractional part has grouped digits, integral part does not. + (None, Some(frac_group_size)) => int_size <= frac_group_size, + // Both parts have grouped digits. Groups should be the same size. + (Some(int_group_size), Some(frac_group_size)) => int_group_size == frac_group_size, + } + } + + /// Returns the size of the digit groups (or None if ungrouped) if successful, + /// otherwise returns a `WarningType` for linting. + fn get_group_size<'a>(groups: impl Iterator) -> Result, WarningType> { + let mut groups = groups.map(str::len); + + let first = groups.next().expect("At least one group"); + + if let Some(second) = groups.next() { + if !groups.all(|x| x == second) || first > second { + Err(WarningType::InconsistentDigitGrouping) + } else if second > 4 { + Err(WarningType::LargeDigitGroups) + } else { + Ok(Some(second)) + } + } else if first > 5 { + Err(WarningType::UnreadableLiteral) + } else { + Ok(None) + } + } +} + +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct DecimalLiteralRepresentation { + threshold: u64, +} + +impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]); + +impl EarlyLintPass for DecimalLiteralRepresentation { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Lit(ref lit) = expr.kind { + self.check_lit(cx, lit) + } + } +} + +impl DecimalLiteralRepresentation { + #[must_use] + pub fn new(threshold: u64) -> Self { + Self { threshold } + } + fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + // Lint integral literals. + if_chain! { + if let LitKind::Int(val, _) = lit.kind; + if let Some(src) = snippet_opt(cx, lit.span); + if let Some(num_lit) = NumericLiteral::from_lit(&src, &lit); + if num_lit.radix == Radix::Decimal; + if val >= u128::from(self.threshold); + then { + let hex = format!("{:#X}", val); + let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); + let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { + warning_type.display(num_lit.format(), cx, lit.span) + }); + } + } + } + + fn do_lint(digits: &str) -> Result<(), WarningType> { + if digits.len() == 1 { + // Lint for 1 digit literals, if someone really sets the threshold that low + if digits == "1" + || digits == "2" + || digits == "4" + || digits == "8" + || digits == "3" + || digits == "7" + || digits == "F" + { + return Err(WarningType::DecimalRepresentation); + } + } else if digits.len() < 4 { + // Lint for Literals with a hex-representation of 2 or 3 digits + let f = &digits[0..1]; // first digit + let s = &digits[1..]; // suffix + + // Powers of 2 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) + // Powers of 2 minus 1 + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } + } else { + // Lint for Literals with a hex-representation of 4 digits or more + let f = &digits[0..1]; // first digit + let m = &digits[1..digits.len() - 1]; // middle digits, except last + let s = &digits[1..]; // suffix + + // Powers of 2 with a margin of +15/-16 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) + // Lint for representations with only 0s and Fs, while allowing 7 as the first + // digit + || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } + } + + Ok(()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs new file mode 100644 index 0000000000000..38a5829b3f745 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -0,0 +1,2447 @@ +use crate::consts::constant; +use crate::reexport::Name; +use crate::utils::paths; +use crate::utils::usage::{is_unused, mutated_variables}; +use crate::utils::{ + get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var, + multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, + span_lint_and_sugg, span_lint_and_then, SpanlessEq, +}; +use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sugg}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; +use rustc_hir::{ + def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand, + LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, +}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::middle::region; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; +use std::iter::{once, Iterator}; +use std::mem; + +declare_clippy_lint! { + /// **What it does:** Checks for for-loops that manually copy items between + /// slices that could be optimized by having a memcpy. + /// + /// **Why is this bad?** It is not as fast as a memcpy. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let src = vec![1]; + /// # let mut dst = vec![0; 65]; + /// for i in 0..src.len() { + /// dst[i + 64] = src[i]; + /// } + /// ``` + /// Could be written as: + /// ```rust + /// # let src = vec![1]; + /// # let mut dst = vec![0; 65]; + /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]); + /// ``` + pub MANUAL_MEMCPY, + perf, + "manually copying items between slices" +} + +declare_clippy_lint! { + /// **What it does:** Checks for looping over the range of `0..len` of some + /// collection just to get the values by index. + /// + /// **Why is this bad?** Just iterating the collection itself makes the intent + /// more clear and is probably faster. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let vec = vec!['a', 'b', 'c']; + /// for i in 0..vec.len() { + /// println!("{}", vec[i]); + /// } + /// ``` + /// Could be written as: + /// ```rust + /// let vec = vec!['a', 'b', 'c']; + /// for i in vec { + /// println!("{}", i); + /// } + /// ``` + pub NEEDLESS_RANGE_LOOP, + style, + "for-looping over a range of indices where an iterator over items would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops on `x.iter()` where `&x` will do, and + /// suggests the latter. + /// + /// **Why is this bad?** Readability. + /// + /// **Known problems:** False negatives. We currently only warn on some known + /// types. + /// + /// **Example:** + /// ```rust + /// // with `y` a `Vec` or slice: + /// # let y = vec![1]; + /// for x in y.iter() { + /// // .. + /// } + /// ``` + /// can be rewritten to + /// ```rust + /// # let y = vec![1]; + /// for x in &y { + /// // .. + /// } + /// ``` + pub EXPLICIT_ITER_LOOP, + pedantic, + "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and + /// suggests the latter. + /// + /// **Why is this bad?** Readability. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// # let y = vec![1]; + /// // with `y` a `Vec` or slice: + /// for x in y.into_iter() { + /// // .. + /// } + /// ``` + /// can be rewritten to + /// ```rust + /// # let y = vec![1]; + /// for x in y { + /// // .. + /// } + /// ``` + pub EXPLICIT_INTO_ITER_LOOP, + pedantic, + "for-looping over `_.into_iter()` when `_` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops on `x.next()`. + /// + /// **Why is this bad?** `next()` returns either `Some(value)` if there was a + /// value, or `None` otherwise. The insidious thing is that `Option<_>` + /// implements `IntoIterator`, so that possibly one value will be iterated, + /// leading to some hard to find bugs. No one will want to write such code + /// [except to win an Underhanded Rust + /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// for x in y.next() { + /// .. + /// } + /// ``` + pub ITER_NEXT_LOOP, + correctness, + "for-looping over `_.next()` which is probably not intended" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `for` loops over `Option` or `Result` values. + /// + /// **Why is this bad?** Readability. This is more clearly expressed as an `if + /// let`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let opt = Some(1); + /// + /// // Bad + /// for x in opt { + /// // .. + /// } + /// + /// // Good + /// if let Some(x) = opt { + /// // .. + /// } + /// ``` + /// + /// // or + /// + /// ```rust + /// # let res: Result = Ok(1); + /// + /// // Bad + /// for x in &res { + /// // .. + /// } + /// + /// // Good + /// if let Ok(x) = res { + /// // .. + /// } + /// ``` + pub FOR_LOOPS_OVER_FALLIBLES, + correctness, + "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" +} + +declare_clippy_lint! { + /// **What it does:** Detects `loop + match` combinations that are easier + /// written as a `while let` loop. + /// + /// **Why is this bad?** The `while let` loop is usually shorter and more + /// readable. + /// + /// **Known problems:** Sometimes the wrong binding is displayed (#383). + /// + /// **Example:** + /// ```rust,no_run + /// # let y = Some(1); + /// loop { + /// let x = match y { + /// Some(x) => x, + /// None => break, + /// }; + /// // .. do something with x + /// } + /// // is easier written as + /// while let Some(x) = y { + /// // .. do something with x + /// }; + /// ``` + pub WHILE_LET_LOOP, + complexity, + "`loop { if let { ... } else break }`, which can be written as a `while let` loop" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions collecting an iterator when collect + /// is not needed. + /// + /// **Why is this bad?** `collect` causes the allocation of a new data structure, + /// when this allocation may not be needed. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// ```rust + /// # let iterator = vec![1].into_iter(); + /// let len = iterator.clone().collect::>().len(); + /// // should be + /// let len = iterator.count(); + /// ``` + pub NEEDLESS_COLLECT, + perf, + "collecting an iterator when collect is not needed" +} + +declare_clippy_lint! { + /// **What it does:** Checks `for` loops over slices with an explicit counter + /// and suggests the use of `.enumerate()`. + /// + /// **Why is it bad?** Using `.enumerate()` makes the intent more clear, + /// declutters the code and may be faster in some instances. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let v = vec![1]; + /// # fn bar(bar: usize, baz: usize) {} + /// let mut i = 0; + /// for item in &v { + /// bar(i, *item); + /// i += 1; + /// } + /// ``` + /// Could be written as + /// ```rust + /// # let v = vec![1]; + /// # fn bar(bar: usize, baz: usize) {} + /// for (i, item) in v.iter().enumerate() { bar(i, *item); } + /// ``` + pub EXPLICIT_COUNTER_LOOP, + complexity, + "for-looping with an explicit counter when `_.enumerate()` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for empty `loop` expressions. + /// + /// **Why is this bad?** Those busy loops burn CPU cycles without doing + /// anything. Think of the environment and either block on something or at least + /// make the thread sleep for some microseconds. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// loop {} + /// ``` + pub EMPTY_LOOP, + style, + "empty `loop {}`, which should block or sleep" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `while let` expressions on iterators. + /// + /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys + /// the intent better. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// while let Some(val) = iter() { + /// .. + /// } + /// ``` + pub WHILE_LET_ON_ITERATOR, + style, + "using a while-let loop instead of a for loop on an iterator" +} + +declare_clippy_lint! { + /// **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and + /// ignoring either the keys or values. + /// + /// **Why is this bad?** Readability. There are `keys` and `values` methods that + /// can be used to express that don't need the values or keys. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// for (k, _) in &map { + /// .. + /// } + /// ``` + /// + /// could be replaced by + /// + /// ```ignore + /// for k in map.keys() { + /// .. + /// } + /// ``` + pub FOR_KV_MAP, + style, + "looping on a map using `iter` when `keys` or `values` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops that will always `break`, `return` or + /// `continue` an outer loop. + /// + /// **Why is this bad?** This loop never loops, all it does is obfuscating the + /// code. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// loop { + /// ..; + /// break; + /// } + /// ``` + pub NEVER_LOOP, + correctness, + "any loop that will always `break` or `return`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops which have a range bound that is a mutable variable + /// + /// **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let mut foo = 42; + /// for i in 0..foo { + /// foo -= 1; + /// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21 + /// } + /// ``` + pub MUT_RANGE_BOUND, + complexity, + "for loop over a range where one of the bounds is a mutable variable" +} + +declare_clippy_lint! { + /// **What it does:** Checks whether variables used within while loop condition + /// can be (and are) mutated in the body. + /// + /// **Why is this bad?** If the condition is unchanged, entering the body of the loop + /// will lead to an infinite loop. + /// + /// **Known problems:** If the `while`-loop is in a closure, the check for mutation of the + /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is + /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger. + /// + /// **Example:** + /// ```rust + /// let i = 0; + /// while i > 10 { + /// println!("let me loop forever!"); + /// } + /// ``` + pub WHILE_IMMUTABLE_CONDITION, + correctness, + "variables used within while expression are not mutated in the body" +} + +declare_lint_pass!(Loops => [ + MANUAL_MEMCPY, + NEEDLESS_RANGE_LOOP, + EXPLICIT_ITER_LOOP, + EXPLICIT_INTO_ITER_LOOP, + ITER_NEXT_LOOP, + FOR_LOOPS_OVER_FALLIBLES, + WHILE_LET_LOOP, + NEEDLESS_COLLECT, + EXPLICIT_COUNTER_LOOP, + EMPTY_LOOP, + WHILE_LET_ON_ITERATOR, + FOR_KV_MAP, + NEVER_LOOP, + MUT_RANGE_BOUND, + WHILE_IMMUTABLE_CONDITION, +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Loops { + #[allow(clippy::too_many_lines)] + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let Some((pat, arg, body)) = higher::for_loop(expr) { + // we don't want to check expanded macros + // this check is not at the top of the function + // since higher::for_loop expressions are marked as expansions + if body.span.from_expansion() { + return; + } + check_for_loop(cx, pat, arg, body, expr); + } + + // we don't want to check expanded macros + if expr.span.from_expansion() { + return; + } + + // check for never_loop + if let ExprKind::Loop(ref block, _, _) = expr.kind { + match never_loop_block(block, expr.hir_id) { + NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"), + NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), + } + } + + // check for `loop { if let {} else break }` that could be `while let` + // (also matches an explicit "match" instead of "if let") + // (even if the "match" or "if let" is used for declaration) + if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { + // also check for empty `loop {}` statements + if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { + span_lint( + cx, + EMPTY_LOOP, + expr.span, + "empty `loop {}` detected. You may want to either use `panic!()` or add \ + `std::thread::sleep(..);` to the loop body.", + ); + } + + // extract the expression from the first statement (if any) in a block + let inner_stmt_expr = extract_expr_from_first_stmt(block); + // or extract the first expression (if any) from the block + if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) { + if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind { + // ensure "if let" compatible match structure + match *source { + MatchSource::Normal | MatchSource::IfLetDesugar { .. } => { + if arms.len() == 2 + && arms[0].guard.is_none() + && arms[1].guard.is_none() + && is_simple_break_expr(&arms[1].body) + { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + // NOTE: we used to build a body here instead of using + // ellipsis, this was removed because: + // 1) it was ugly with big bodies; + // 2) it was not indented properly; + // 3) it wasn’t very smart (see #675). + let mut applicability = Applicability::HasPlaceholders; + span_lint_and_sugg( + cx, + WHILE_LET_LOOP, + expr.span, + "this loop could be written as a `while let` loop", + "try", + format!( + "while let {} = {} {{ .. }}", + snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), + snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability), + ), + applicability, + ); + } + }, + _ => (), + } + } + } + } + if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind { + let pat = &arms[0].pat.kind; + if let ( + &PatKind::TupleStruct(ref qpath, ref pat_args, _), + &ExprKind::MethodCall(ref method_path, _, ref method_args), + ) = (pat, &match_expr.kind) + { + let iter_expr = &method_args[0]; + + // Don't lint when the iterator is recreated on every iteration + if_chain! { + if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind; + if let Some(iter_def_id) = get_trait_def_id(cx, &paths::ITERATOR); + if implements_trait(cx, cx.tables.expr_ty(iter_expr), iter_def_id, &[]); + then { + return; + } + } + + let lhs_constructor = last_path_segment(qpath); + if method_path.ident.name == sym!(next) + && match_trait_method(cx, match_expr, &paths::ITERATOR) + && lhs_constructor.ident.name == sym!(Some) + && (pat_args.is_empty() + || !is_refutable(cx, &pat_args[0]) + && !is_used_inside(cx, iter_expr, &arms[0].body) + && !is_iterator_used_after_while_let(cx, iter_expr) + && !is_nested(cx, expr, &method_args[0])) + { + let mut applicability = Applicability::MachineApplicable; + let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability); + let loop_var = if pat_args.is_empty() { + "_".to_string() + } else { + snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned() + }; + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(match_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {} in {}", loop_var, iterator), + applicability, + ); + } + } + } + + if let Some((cond, body)) = higher::while_loop(&expr) { + check_infinite_loop(cx, cond, body); + } + + check_needless_collect(expr, cx); + } +} + +enum NeverLoopResult { + // A break/return always get triggered but not necessarily for the main loop. + AlwaysBreak, + // A continue may occur for the main loop. + MayContinueMainLoop, + Otherwise, +} + +#[must_use] +fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { + match *arg { + NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, + NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, + } +} + +// Combine two results for parts that are called in order. +#[must_use] +fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult { + match first { + NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first, + NeverLoopResult::Otherwise => second, + } +} + +// Combine two results where both parts are called but not necessarily in order. +#[must_use] +fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult { + match (left, right) { + (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { + NeverLoopResult::MayContinueMainLoop + }, + (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, + (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, + } +} + +// Combine two results where only one of the part may have been executed. +#[must_use] +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult { + match (b1, b2) { + (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, + (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { + NeverLoopResult::MayContinueMainLoop + }, + (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, + } +} + +fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { + let stmts = block.stmts.iter().map(stmt_to_expr); + let expr = once(block.expr.as_deref()); + let mut iter = stmts.chain(expr).filter_map(|e| e); + never_loop_expr_seq(&mut iter, main_loop_id) +} + +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match stmt.kind { + StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e), + StmtKind::Local(ref local) => local.init.as_deref(), + _ => None, + } +} + +fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { + match expr.kind { + ExprKind::Box(ref e) + | ExprKind::Unary(_, ref e) + | ExprKind::Cast(ref e, _) + | ExprKind::Type(ref e, _) + | ExprKind::Field(ref e, _) + | ExprKind::AddrOf(_, _, ref e) + | ExprKind::Struct(_, _, Some(ref e)) + | ExprKind::Repeat(ref e, _) + | ExprKind::DropTemps(ref e) => never_loop_expr(e, main_loop_id), + ExprKind::Array(ref es) | ExprKind::MethodCall(_, _, ref es) | ExprKind::Tup(ref es) => { + never_loop_expr_all(&mut es.iter(), main_loop_id) + }, + ExprKind::Call(ref e, ref es) => never_loop_expr_all(&mut once(&**e).chain(es.iter()), main_loop_id), + ExprKind::Binary(_, ref e1, ref e2) + | ExprKind::Assign(ref e1, ref e2, _) + | ExprKind::AssignOp(_, ref e1, ref e2) + | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id), + ExprKind::Loop(ref b, _, _) => { + // Break can come from the inner loop so remove them. + absorb_break(&never_loop_block(b, main_loop_id)) + }, + ExprKind::Match(ref e, ref arms, _) => { + let e = never_loop_expr(e, main_loop_id); + if arms.is_empty() { + e + } else { + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id); + combine_seq(e, arms) + } + }, + ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id), + ExprKind::Continue(d) => { + let id = d + .target_id + .expect("target ID can only be missing in the presence of compilation errors"); + if id == main_loop_id { + NeverLoopResult::MayContinueMainLoop + } else { + NeverLoopResult::AlwaysBreak + } + }, + ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => { + if let Some(ref e) = *e { + combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + } else { + NeverLoopResult::AlwaysBreak + } + }, + ExprKind::InlineAsm(ref asm) => asm + .operands + .iter() + .map(|o| match o { + InlineAsmOperand::In { expr, .. } + | InlineAsmOperand::InOut { expr, .. } + | InlineAsmOperand::Const { expr } + | InlineAsmOperand::Sym { expr } => never_loop_expr(expr, main_loop_id), + InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id), + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id) + }, + }) + .fold(NeverLoopResult::Otherwise, combine_both), + ExprKind::Struct(_, _, None) + | ExprKind::Yield(_, _) + | ExprKind::Closure(_, _, _, _, _) + | ExprKind::LlvmInlineAsm(_) + | ExprKind::Path(_) + | ExprKind::Lit(_) + | ExprKind::Err => NeverLoopResult::Otherwise, + } +} + +fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, main_loop_id)) + .fold(NeverLoopResult::Otherwise, combine_seq) +} + +fn never_loop_expr_all<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, main_loop_id)) + .fold(NeverLoopResult::Otherwise, combine_both) +} + +fn never_loop_expr_branch<'a, T: Iterator>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { + e.map(|e| never_loop_expr(e, main_loop_id)) + .fold(NeverLoopResult::AlwaysBreak, combine_branches) +} + +fn check_for_loop<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + check_for_loop_range(cx, pat, arg, body, expr); + check_for_loop_arg(cx, pat, arg, expr); + check_for_loop_explicit_counter(cx, pat, arg, body, expr); + check_for_loop_over_map_kv(cx, pat, arg, body, expr); + check_for_mut_range_bound(cx, arg, body); + detect_manual_memcpy(cx, pat, arg, body, expr); +} + +fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> bool { + if_chain! { + if let ExprKind::Path(qpath) = &expr.kind; + if let QPath::Resolved(None, path) = qpath; + if path.segments.len() == 1; + if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id); + then { + // our variable! + local_id == var + } else { + false + } + } +} + +#[derive(Clone, Copy)] +enum OffsetSign { + Positive, + Negative, +} + +struct Offset { + value: String, + sign: OffsetSign, +} + +impl Offset { + fn negative(value: String) -> Self { + Self { + value, + sign: OffsetSign::Negative, + } + } + + fn positive(value: String) -> Self { + Self { + value, + sign: OffsetSign::Positive, + } + } +} + +struct FixedOffsetVar<'hir> { + var: &'hir Expr<'hir>, + offset: Offset, +} + +fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool { + let is_slice = match ty.kind { + ty::Ref(_, subty, _) => is_slice_like(cx, subty), + ty::Slice(..) | ty::Array(..) => true, + _ => false, + }; + + is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) +} + +fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + if_chain! { + if let ExprKind::MethodCall(method, _, args) = expr.kind; + if method.ident.name == sym!(clone); + if args.len() == 1; + if let Some(arg) = args.get(0); + then { arg } else { expr } + } +} + +fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) -> Option { + fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option { + match &e.kind { + ExprKind::Lit(l) => match l.node { + ast::LitKind::Int(x, _ty) => Some(x.to_string()), + _ => None, + }, + ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), + _ => None, + } + } + + match idx.kind { + ExprKind::Binary(op, lhs, rhs) => match op.node { + BinOpKind::Add => { + let offset_opt = if same_var(cx, lhs, var) { + extract_offset(cx, rhs, var) + } else if same_var(cx, rhs, var) { + extract_offset(cx, lhs, var) + } else { + None + }; + + offset_opt.map(Offset::positive) + }, + BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative), + _ => None, + }, + ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), + _ => None, + } +} + +fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator, &'tcx Expr<'tcx>)>> { + fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if let ExprKind::Assign(lhs, rhs, _) = e.kind { + Some((lhs, rhs)) + } else { + None + } + } + + // 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(b, _) = body.kind { + let Block { stmts, expr, .. } = *b; + + iter_a = stmts + .iter() + .filter_map(|stmt| match stmt.kind { + StmtKind::Local(..) | StmtKind::Item(..) => None, + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), + }) + .chain(expr.into_iter()) + .map(get_assignment) + .into() + } else { + iter_b = Some(get_assignment(body)) + } + + iter_a.into_iter().flatten().chain(iter_b.into_iter()) +} + +fn build_manual_memcpy_suggestion<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + start: &Expr<'_>, + end: &Expr<'_>, + limits: ast::RangeLimits, + dst_var: FixedOffsetVar<'_>, + src_var: FixedOffsetVar<'_>, +) -> String { + fn print_sum(arg1: &str, arg2: &Offset) -> String { + match (arg1, &arg2.value[..], arg2.sign) { + ("0", "0", _) => "0".into(), + ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), + ("0", x, OffsetSign::Negative) => format!("-{}", x), + (x, y, OffsetSign::Positive) => format!("({} + {})", x, y), + (x, y, OffsetSign::Negative) => { + if x == y { + "0".into() + } else { + format!("({} - {})", x, y) + } + }, + } + } + + fn print_offset(start_str: &str, inline_offset: &Offset) -> String { + let offset = print_sum(start_str, inline_offset); + if offset.as_str() == "0" { + "".into() + } else { + offset + } + } + + let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if var_def_id(cx, arg) == var_def_id(cx, var); + then { + match offset.sign { + OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), + OffsetSign::Positive => "".into(), + } + } else { + let end_str = match limits { + ast::RangeLimits::Closed => { + let end = sugg::Sugg::hir(cx, end, ""); + format!("{}", end + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), + }; + + print_sum(&end_str, &offset) + } + } + }; + + let start_str = snippet(cx, start.span, "").to_string(); + let dst_offset = print_offset(&start_str, &dst_var.offset); + let dst_limit = print_limit(end, dst_var.offset, dst_var.var); + let src_offset = print_offset(&start_str, &src_var.offset); + let src_limit = print_limit(end, src_var.offset, src_var.var); + + let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into()); + let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into()); + + let dst = if dst_offset == "" && dst_limit == "" { + dst_var_name + } else { + format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) + }; + + format!( + "{}.clone_from_slice(&{}[{}..{}])", + dst, src_var_name, src_offset, src_limit + ) +} +/// Checks for for loops that sequentially copy items from one slice-like +/// object to another. +fn detect_manual_memcpy<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if let Some(higher::Range { + start: Some(start), + end: Some(end), + limits, + }) = higher::range(cx, arg) + { + // the var must be a single name + if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { + // The only statements in the for loops can be indexed assignments from + // indexed retrievals. + let big_sugg = get_assignments(body) + .map(|o| { + o.and_then(|(lhs, rhs)| { + let rhs = fetch_cloned_expr(rhs); + if_chain! { + if let ExprKind::Index(seqexpr_left, idx_left) = lhs.kind; + if let ExprKind::Index(seqexpr_right, idx_right) = rhs.kind; + if is_slice_like(cx, cx.tables.expr_ty(seqexpr_left)) + && is_slice_like(cx, cx.tables.expr_ty(seqexpr_right)); + if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id); + if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id); + + // Source and destination must be different + if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right); + then { + Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left }, + FixedOffsetVar { var: seqexpr_right, offset: offset_right })) + } else { + None + } + } + }) + }) + .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, 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, + ); + } + } + } +} + +/// Checks for looping over a range and then indexing a sequence with it. +/// The iteratee must be a range literal. +#[allow(clippy::too_many_lines)] +fn check_for_loop_range<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if let Some(higher::Range { + start: Some(start), + ref end, + limits, + }) = higher::range(cx, 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: FxHashMap::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"); + + // ensure that the indexed variable was declared before the loop, see #601 + if let Some(indexed_extent) = indexed_extent { + let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); + let parent_def_id = cx.tcx.hir().local_def_id(parent_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); + if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { + 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; + } + + // don't lint if the container that is indexed into is also used without + // indexing + if visitor.referenced.contains(&indexed) { + return; + } + + let starts_at_zero = is_integer_const(cx, start, 0); + + let skip = if starts_at_zero { + String::new() + } else { + format!(".skip({})", snippet(cx, start.span, "..")) + }; + + let mut end_is_start_plus_val = false; + + let take = if let Some(end) = *end { + let mut take_expr = end; + + if let ExprKind::Binary(ref op, ref left, ref right) = end.kind { + if let BinOpKind::Add = op.node { + let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); + let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); + + if start_equal_left { + take_expr = right; + } else if start_equal_right { + take_expr = left; + } + + end_is_start_plus_val = start_equal_left | start_equal_right; + } + } + + if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { + String::new() + } 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() + }; + + let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { + ("mut ", "iter_mut") + } else { + ("", "iter") + }; + + let take_is_empty = take.is_empty(); + let mut method_1 = take; + let mut method_2 = skip; + + 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, + expr.span, + &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), + |diag| { + multispan_sugg( + diag, + "consider using an iterator", + vec![ + (pat.span, format!("({}, )", ident.name)), + ( + arg.span, + format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), + ), + ], + ); + }, + ); + } else { + let repl = if starts_at_zero && take_is_empty { + format!("&{}{}", ref_mut, indexed) + } else { + format!("{}.{}(){}{}", indexed, method, method_1, method_2) + }; + + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + expr.span, + &format!( + "the loop variable `{}` is only used to index `{}`.", + ident.name, indexed + ), + |diag| { + multispan_sugg( + diag, + "consider using an iterator", + vec![(pat.span, "".to_string()), (arg.span, repl)], + ); + }, + ); + } + } + } + } +} + +fn is_len_call(expr: &Expr<'_>, var: Name) -> bool { + if_chain! { + if let ExprKind::MethodCall(ref method, _, ref len_args) = expr.kind; + if len_args.len() == 1; + if method.ident.name == sym!(len); + if let ExprKind::Path(QPath::Resolved(_, ref path)) = len_args[0].kind; + if path.segments.len() == 1; + if path.segments[0].ident.name == var; + then { + return true; + } + } + + false +} + +fn is_end_eq_array_len<'tcx>( + cx: &LateContext<'_, 'tcx>, + end: &Expr<'_>, + limits: ast::RangeLimits, + indexed_ty: Ty<'tcx>, +) -> bool { + if_chain! { + if let ExprKind::Lit(ref lit) = end.kind; + if let ast::LitKind::Int(end_int, _) = lit.node; + if let ty::Array(_, arr_len_const) = indexed_ty.kind; + if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env); + then { + return match limits { + ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), + ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), + }; + } + } + + false +} + +fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { + let mut applicability = Applicability::MachineApplicable; + let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let muta = if method_name == "iter_mut" { "mut " } else { "" }; + span_lint_and_sugg( + cx, + EXPLICIT_ITER_LOOP, + arg.span, + "it is more concise to loop over references to containers instead of using explicit \ + iteration methods", + "to write this more concisely, try", + format!("&{}{}", muta, object), + applicability, + ) +} + +fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { + let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used + if let ExprKind::MethodCall(ref method, _, ref args) = arg.kind { + // just the receiver, no arguments + if args.len() == 1 { + let method_name = &*method.ident.as_str(); + // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x + if method_name == "iter" || method_name == "iter_mut" { + if is_ref_iterable_type(cx, &args[0]) { + lint_iter_method(cx, args, arg, method_name); + } + } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) { + let receiver_ty = cx.tables.expr_ty(&args[0]); + let receiver_ty_adjusted = cx.tables.expr_ty_adjusted(&args[0]); + if same_tys(cx, receiver_ty, receiver_ty_adjusted) { + let mut applicability = Applicability::MachineApplicable; + let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + span_lint_and_sugg( + cx, + EXPLICIT_INTO_ITER_LOOP, + arg.span, + "it is more concise to loop over containers instead of using explicit \ + iteration methods", + "to write this more concisely, try", + object.to_string(), + applicability, + ); + } else { + let ref_receiver_ty = cx.tcx.mk_ref( + cx.tcx.lifetimes.re_erased, + ty::TypeAndMut { + ty: receiver_ty, + mutbl: Mutability::Not, + }, + ); + if same_tys(cx, receiver_ty_adjusted, ref_receiver_ty) { + lint_iter_method(cx, args, arg, method_name) + } + } + } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) { + span_lint( + cx, + ITER_NEXT_LOOP, + expr.span, + "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ + probably not what you want", + ); + next_loop_linted = true; + } + } + } + if !next_loop_linted { + check_arg_type(cx, pat, arg); + } +} + +/// Checks for `for` loops over `Option`s and `Result`s. +fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { + let ty = cx.tables.expr_ty(arg); + if is_type_diagnostic_item(cx, ty, sym!(option_type)) { + span_lint_and_help( + cx, + FOR_LOOPS_OVER_FALLIBLES, + arg.span, + &format!( + "for loop over `{0}`, which is an `Option`. This is more readably written as an \ + `if let` statement.", + snippet(cx, arg.span, "_") + ), + None, + &format!( + "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ), + ); + } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { + span_lint_and_help( + cx, + FOR_LOOPS_OVER_FALLIBLES, + arg.span, + &format!( + "for loop over `{0}`, which is a `Result`. This is more readably written as an \ + `if let` statement.", + snippet(cx, arg.span, "_") + ), + None, + &format!( + "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ), + ); + } +} + +fn check_for_loop_explicit_counter<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + // Look for variables that are incremented once per loop iteration. + let mut visitor = IncrementVisitor { + cx, + states: FxHashMap::default(), + depth: 0, + done: false, + }; + walk_expr(&mut visitor, body); + + // For each candidate, check the parent block to see if + // it's initialized to zero at the start of the loop. + if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) { + let mut visitor2 = InitializeVisitor { + cx, + end_expr: expr, + var_id: *id, + state: VarState::IncrOnce, + name: None, + depth: 0, + past_loop: false, + }; + walk_block(&mut visitor2, block); + + if visitor2.state == VarState::Warn { + if let Some(name) = visitor2.name { + let mut applicability = Applicability::MachineApplicable; + + // for some reason this is the only way to get the `Span` + // of the entire `for` loop + let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind { + arms[0].body.span + } else { + unreachable!() + }; + + span_lint_and_sugg( + cx, + EXPLICIT_COUNTER_LOOP, + for_span.with_hi(arg.span.hi()), + &format!("the variable `{}` is used as a loop counter.", name), + "consider using", + format!( + "for ({}, {}) in {}.enumerate()", + name, + snippet_with_applicability(cx, pat.span, "item", &mut applicability), + make_iterator_snippet(cx, arg, &mut applicability), + ), + applicability, + ); + } + } + } + } +} + +/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the +/// actual `Iterator` that the loop uses. +fn make_iterator_snippet(cx: &LateContext<'_, '_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { + let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR) + .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(arg), id, &[])); + if impls_iterator { + format!( + "{}", + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + ) + } else { + // (&x).into_iter() ==> x.iter() + // (&mut x).into_iter() ==> x.iter_mut() + match &arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner) + if has_iter_method(cx, cx.tables.expr_ty(&arg_inner)).is_some() => + { + let meth_name = match mutability { + Mutability::Mut => "iter_mut", + Mutability::Not => "iter", + }; + format!( + "{}.{}()", + sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(), + meth_name, + ) + } + _ => format!( + "{}.into_iter()", + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + ), + } + } +} + +/// Checks for the `FOR_KV_MAP` lint. +fn check_for_loop_over_map_kv<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + let pat_span = pat.span; + + if let PatKind::Tuple(ref pat, _) = pat.kind { + if pat.len() == 2 { + let arg_span = arg.span; + let (new_pat_span, kind, ty, mutbl) = match cx.tables.expr_ty(arg).kind { + ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { + (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl), + (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not), + _ => return, + }, + _ => return, + }; + let mutbl = match mutbl { + Mutability::Not => "", + Mutability::Mut => "_mut", + }; + let arg = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) => &**expr, + _ => arg, + }; + + if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP) { + span_lint_and_then( + cx, + FOR_KV_MAP, + expr.span, + &format!("you seem to want to iterate on a map's {}s", kind), + |diag| { + let map = sugg::Sugg::hir(cx, arg, "map"); + multispan_sugg( + diag, + "use the corresponding method", + vec![ + (pat_span, snippet(cx, new_pat_span, kind).into_owned()), + (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), + ], + ); + }, + ); + } + } + } +} + +struct MutatePairDelegate { + hir_id_low: Option, + hir_id_high: Option, + span_low: Option, + span_high: Option, +} + +impl<'tcx> Delegate<'tcx> for MutatePairDelegate { + fn consume(&mut self, _: &Place<'tcx>, _: ConsumeMode) {} + + fn borrow(&mut self, cmt: &Place<'tcx>, bk: ty::BorrowKind) { + if let ty::BorrowKind::MutBorrow = bk { + if let PlaceBase::Local(id) = cmt.base { + if Some(id) == self.hir_id_low { + self.span_low = Some(cmt.span) + } + if Some(id) == self.hir_id_high { + self.span_high = Some(cmt.span) + } + } + } + } + + fn mutate(&mut self, cmt: &Place<'tcx>) { + if let PlaceBase::Local(id) = cmt.base { + if Some(id) == self.hir_id_low { + self.span_low = Some(cmt.span) + } + if Some(id) == self.hir_id_high { + self.span_high = Some(cmt.span) + } + } + } +} + +impl<'tcx> MutatePairDelegate { + fn mutation_span(&self) -> (Option, Option) { + (self.span_low, self.span_high) + } +} + +fn check_for_mut_range_bound(cx: &LateContext<'_, '_>, arg: &Expr<'_>, body: &Expr<'_>) { + if let Some(higher::Range { + start: Some(start), + end: Some(end), + .. + }) = higher::range(cx, arg) + { + let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; + if mut_ids[0].is_some() || mut_ids[1].is_some() { + let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids); + mut_warn_with_span(cx, span_low); + mut_warn_with_span(cx, span_high); + } + } +} + +fn mut_warn_with_span(cx: &LateContext<'_, '_>, span: Option) { + if let Some(sp) = span { + span_lint( + cx, + MUT_RANGE_BOUND, + sp, + "attempt to mutate range bound within loop; note that the range of the loop is unchanged", + ); + } +} + +fn check_for_mutability(cx: &LateContext<'_, '_>, bound: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::Path(ref qpath) = bound.kind; + if let QPath::Resolved(None, _) = *qpath; + then { + let res = qpath_res(cx, qpath, bound.hir_id); + if let Res::Local(hir_id) = res { + let node_str = cx.tcx.hir().get(hir_id); + if_chain! { + if let Node::Binding(pat) = node_str; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if let BindingAnnotation::Mutable = bind_ann; + then { + return Some(hir_id); + } + } + } + } + } + None +} + +fn check_for_mutation( + cx: &LateContext<'_, '_>, + body: &Expr<'_>, + bound_ids: &[Option], +) -> (Option, Option) { + let mut delegate = MutatePairDelegate { + hir_id_low: bound_ids[0], + hir_id_high: bound_ids[1], + span_low: None, + span_high: None, + }; + let def_id = body.hir_id.owner.to_def_id(); + cx.tcx.infer_ctxt().enter(|infcx| { + ExprUseVisitor::new(&mut delegate, &infcx, def_id.expect_local(), cx.param_env, cx.tables).walk_expr(body); + }); + delegate.mutation_span() +} + +/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`. +fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { + match *pat { + PatKind::Wild => true, + PatKind::Binding(.., ident, None) if ident.as_str().starts_with('_') => is_unused(&ident, body), + _ => false, + } +} + +struct LocalUsedVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + local: HirId, + used: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if same_var(self.cx, expr, self.local) { + self.used = true; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +struct VarVisitor<'a, 'tcx> { + /// context reference + cx: &'a LateContext<'a, 'tcx>, + /// var name to look for as index + var: HirId, + /// indexed variables that are used mutably + indexed_mut: FxHashSet, + /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global + indexed_indirectly: FxHashMap>, + /// subset of `indexed` of vars that are indexed directly: `v[i]` + /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` + indexed_directly: FxHashMap, Ty<'tcx>)>, + /// Any names that are used outside an index operation. + /// Used to detect things like `&mut vec` used together with `vec[i]` + referenced: FxHashSet, + /// has the loop variable been used in expressions other than the index of + /// an index op? + nonindex: bool, + /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar + /// takes `&mut self` + prefer_mutable: bool, +} + +impl<'a, 'tcx> VarVisitor<'a, 'tcx> { + fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { + if_chain! { + // the indexed container is referenced by a name + if let ExprKind::Path(ref seqpath) = seqexpr.kind; + if let QPath::Resolved(None, ref seqvar) = *seqpath; + if seqvar.segments.len() == 1; + then { + let index_used_directly = same_var(self.cx, idx, self.var); + let indexed_indirectly = { + let mut used_visitor = LocalUsedVisitor { + cx: self.cx, + local: self.var, + used: false, + }; + walk_expr(&mut used_visitor, idx); + used_visitor.used + }; + + if indexed_indirectly || index_used_directly { + if self.prefer_mutable { + self.indexed_mut.insert(seqvar.segments[0].ident.name); + } + let res = qpath_res(self.cx, seqpath, seqexpr.hir_id); + match res { + Res::Local(hir_id) => { + let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); + let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id); + let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); + if indexed_indirectly { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + } + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (Some(extent), self.cx.tables.node_type(seqexpr.hir_id)), + ); + } + return false; // no need to walk further *on the variable* + } + Res::Def(DefKind::Static | DefKind::Const, ..) => { + if indexed_indirectly { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); + } + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (None, self.cx.tables.node_type(seqexpr.hir_id)), + ); + } + return false; // no need to walk further *on the variable* + } + _ => (), + } + } + } + } + true + } +} + +impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if_chain! { + // a range index op + if let ExprKind::MethodCall(ref meth, _, ref args) = expr.kind; + if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX)) + || (meth.ident.name == sym!(index_mut) && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if !self.check(&args[1], &args[0], expr); + then { return } + } + + if_chain! { + // an index op + if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind; + if !self.check(idx, seqexpr, expr); + then { return } + } + + if_chain! { + // directly using a variable + if let ExprKind::Path(ref qpath) = expr.kind; + if let QPath::Resolved(None, ref path) = *qpath; + if path.segments.len() == 1; + then { + if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id) { + if local_id == self.var { + self.nonindex = true; + } else { + // not the correct variable, but still a variable + self.referenced.insert(path.segments[0].ident.name); + } + } + } + } + + let old = self.prefer_mutable; + match expr.kind { + ExprKind::AssignOp(_, ref lhs, ref rhs) | ExprKind::Assign(ref lhs, ref rhs, _) => { + self.prefer_mutable = true; + self.visit_expr(lhs); + self.prefer_mutable = false; + self.visit_expr(rhs); + }, + ExprKind::AddrOf(BorrowKind::Ref, mutbl, ref expr) => { + if mutbl == Mutability::Mut { + self.prefer_mutable = true; + } + self.visit_expr(expr); + }, + ExprKind::Call(ref f, args) => { + self.visit_expr(f); + for expr in args { + let ty = self.cx.tables.expr_ty_adjusted(expr); + self.prefer_mutable = false; + if let ty::Ref(_, _, mutbl) = ty.kind { + if mutbl == Mutability::Mut { + self.prefer_mutable = true; + } + } + self.visit_expr(expr); + } + }, + ExprKind::MethodCall(_, _, args) => { + let def_id = self.cx.tables.type_dependent_def_id(expr.hir_id).unwrap(); + for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) { + self.prefer_mutable = false; + if let ty::Ref(_, _, mutbl) = ty.kind { + if mutbl == Mutability::Mut { + self.prefer_mutable = true; + } + } + self.visit_expr(expr); + } + }, + ExprKind::Closure(_, _, body_id, ..) => { + let body = self.cx.tcx.hir().body(body_id); + self.visit_expr(&body.value); + }, + _ => walk_expr(self, expr), + } + self.prefer_mutable = old; + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn is_used_inside<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool { + let def_id = match var_def_id(cx, expr) { + Some(id) => id, + None => return false, + }; + if let Some(used_mutably) = mutated_variables(container, cx) { + if used_mutably.contains(&def_id) { + return true; + } + } + false +} + +fn is_iterator_used_after_while_let<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, iter_expr: &'tcx Expr<'_>) -> bool { + let def_id = match var_def_id(cx, iter_expr) { + Some(id) => id, + None => return false, + }; + let mut visitor = VarUsedAfterLoopVisitor { + cx, + def_id, + iter_expr_id: iter_expr.hir_id, + past_while_let: false, + var_used_after_while_let: false, + }; + if let Some(enclosing_block) = get_enclosing_block(cx, def_id) { + walk_block(&mut visitor, enclosing_block); + } + visitor.var_used_after_while_let +} + +struct VarUsedAfterLoopVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + def_id: HirId, + iter_expr_id: HirId, + past_while_let: bool, + var_used_after_while_let: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.past_while_let { + if Some(self.def_id) == var_def_id(self.cx, expr) { + self.var_used_after_while_let = true; + } + } else if self.iter_expr_id == expr.hir_id { + self.past_while_let = true; + } + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Returns `true` if the type of expr is one that provides `IntoIterator` impls +/// for `&T` and `&mut T`, such as `Vec`. +#[rustfmt::skip] +fn is_ref_iterable_type(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool { + // no walk_ptrs_ty: calling iter() on a reference can make sense because it + // will allow further borrows afterwards + let ty = cx.tables.expr_ty(e); + is_iterable_array(ty, cx) || + is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + match_type(cx, ty, &paths::LINKED_LIST) || + is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || + is_type_diagnostic_item(cx, ty, sym!(hashset_type)) || + is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || + match_type(cx, ty, &paths::BINARY_HEAP) || + match_type(cx, ty, &paths::BTREEMAP) || + match_type(cx, ty, &paths::BTREESET) +} + +fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'_, 'tcx>) -> bool { + // IntoIterator is currently only implemented for array sizes <= 32 in rustc + match ty.kind { + ty::Array(_, n) => { + if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) { + (0..=32).contains(&val) + } else { + false + } + }, + _ => false, + } +} + +/// If a block begins with a statement (possibly a `let` binding) and has an +/// expression, return it. +fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if block.stmts.is_empty() { + return None; + } + if let StmtKind::Local(ref local) = block.stmts[0].kind { + if let Some(expr) = local.init { + Some(expr) + } else { + None + } + } else { + None + } +} + +/// If a block begins with an expression (with or without semicolon), return it. +fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match block.expr { + Some(ref expr) if block.stmts.is_empty() => Some(expr), + None if !block.stmts.is_empty() => match block.stmts[0].kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr), + StmtKind::Local(..) | StmtKind::Item(..) => None, + }, + _ => None, + } +} + +/// Returns `true` if expr contains a single break expr without destination label +/// and +/// passed expression. The expression may be within a block. +fn is_simple_break_expr(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true, + ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), + _ => false, + } +} + +// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be +// incremented exactly once in the loop body, and initialized to zero +// at the start of the loop. +#[derive(Debug, PartialEq)] +enum VarState { + Initial, // Not examined yet + IncrOnce, // Incremented exactly once, may be a loop counter + Declared, // Declared but not (yet) initialized to zero + Warn, + DontWarn, +} + +/// Scan a for loop for variables that are incremented exactly once. +struct IncrementVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, // context reference + states: FxHashMap, // incremented variables + depth: u32, // depth of conditional expressions + done: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.done { + return; + } + + // If node is a variable + if let Some(def_id) = var_def_id(self.cx, expr) { + if let Some(parent) = get_parent_expr(self.cx, expr) { + let state = self.states.entry(def_id).or_insert(VarState::Initial); + + match parent.kind { + ExprKind::AssignOp(op, ref lhs, ref rhs) => { + if lhs.hir_id == expr.hir_id { + if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) { + *state = match *state { + VarState::Initial if self.depth == 0 => VarState::IncrOnce, + _ => VarState::DontWarn, + }; + } else { + // Assigned some other value + *state = VarState::DontWarn; + } + } + }, + ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn, + ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { + *state = VarState::DontWarn + }, + _ => (), + } + } + } else if is_loop(expr) || is_conditional(expr) { + self.depth += 1; + walk_expr(self, expr); + self.depth -= 1; + return; + } else if let ExprKind::Continue(_) = expr.kind { + self.done = true; + return; + } + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Checks whether a variable is initialized to zero at the start of a loop. +struct InitializeVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, // context reference + end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. + var_id: HirId, + state: VarState, + name: Option, + depth: u32, // depth of conditional expressions + past_loop: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + // Look for declarations of the variable + if let StmtKind::Local(ref local) = stmt.kind { + if local.pat.hir_id == self.var_id { + if let PatKind::Binding(.., ident, _) = local.pat.kind { + self.name = Some(ident.name); + + self.state = if let Some(ref init) = local.init { + if is_integer_const(&self.cx, init, 0) { + VarState::Warn + } else { + VarState::Declared + } + } else { + VarState::Declared + } + } + } + } + walk_stmt(self, stmt); + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.state == VarState::DontWarn { + return; + } + if SpanlessEq::new(self.cx).eq_expr(&expr, self.end_expr) { + self.past_loop = true; + return; + } + // No need to visit expressions before the variable is + // declared + if self.state == VarState::IncrOnce { + return; + } + + // If node is the desired variable, see how it's used + if var_def_id(self.cx, expr) == Some(self.var_id) { + if let Some(parent) = get_parent_expr(self.cx, expr) { + match parent.kind { + ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { + self.state = VarState::DontWarn; + }, + ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { + self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 { + VarState::Warn + } else { + VarState::DontWarn + } + }, + ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { + self.state = VarState::DontWarn + }, + _ => (), + } + } + + if self.past_loop { + self.state = VarState::DontWarn; + return; + } + } else if !self.past_loop && is_loop(expr) { + self.state = VarState::DontWarn; + return; + } else if is_conditional(expr) { + self.depth += 1; + walk_expr(self, expr); + self.depth -= 1; + return; + } + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +fn var_def_id(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + if let ExprKind::Path(ref qpath) = expr.kind { + let path_res = qpath_res(cx, qpath, expr.hir_id); + if let Res::Local(hir_id) = path_res { + return Some(hir_id); + } + } + None +} + +fn is_loop(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Loop(..) => true, + _ => false, + } +} + +fn is_conditional(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Match(..) => true, + _ => false, + } +} + +fn is_nested(cx: &LateContext<'_, '_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { + if_chain! { + if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id); + let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id); + if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node); + then { + return is_loop_nested(cx, loop_expr, iter_expr) + } + } + false +} + +fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { + let mut id = loop_expr.hir_id; + let iter_name = if let Some(name) = path_name(iter_expr) { + name + } else { + return true; + }; + loop { + let parent = cx.tcx.hir().get_parent_node(id); + if parent == id { + return false; + } + match cx.tcx.hir().find(parent) { + Some(Node::Expr(expr)) => { + if let ExprKind::Loop(..) = expr.kind { + return true; + }; + }, + Some(Node::Block(block)) => { + let mut block_visitor = LoopNestVisitor { + hir_id: id, + iterator: iter_name, + nesting: Unknown, + }; + walk_block(&mut block_visitor, block); + if block_visitor.nesting == RuledOut { + return false; + } + }, + Some(Node::Stmt(_)) => (), + _ => { + return false; + }, + } + id = parent; + } +} + +#[derive(PartialEq, Eq)] +enum Nesting { + Unknown, // no nesting detected yet + RuledOut, // the iterator is initialized or assigned within scope + LookFurther, // no nesting detected, no further walk required +} + +use self::Nesting::{LookFurther, RuledOut, Unknown}; + +struct LoopNestVisitor { + hir_id: HirId, + iterator: Name, + nesting: Nesting, +} + +impl<'tcx> Visitor<'tcx> for LoopNestVisitor { + type Map = Map<'tcx>; + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + if stmt.hir_id == self.hir_id { + self.nesting = LookFurther; + } else if self.nesting == Unknown { + walk_stmt(self, stmt); + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.nesting != Unknown { + return; + } + if expr.hir_id == self.hir_id { + self.nesting = LookFurther; + return; + } + match expr.kind { + ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => { + if match_var(path, self.iterator) { + self.nesting = RuledOut; + } + }, + _ => walk_expr(self, expr), + } + } + + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { + if self.nesting != Unknown { + return; + } + if let PatKind::Binding(.., span_name, _) = pat.kind { + if self.iterator == span_name.name { + self.nesting = RuledOut; + return; + } + } + walk_pat(self, pat) + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn path_name(e: &Expr<'_>) -> Option { + if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind { + let segments = &path.segments; + if segments.len() == 1 { + return Some(segments[0].ident.name); + } + }; + None +} + +fn check_infinite_loop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { + if constant(cx, cx.tables, cond).is_some() { + // A pure constant condition (e.g., `while false`) is not linted. + return; + } + + let mut var_visitor = VarCollectorVisitor { + cx, + ids: FxHashSet::default(), + def_ids: FxHashMap::default(), + skip: false, + }; + var_visitor.visit_expr(cond); + if var_visitor.skip { + return; + } + let used_in_condition = &var_visitor.ids; + let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) { + used_in_condition.is_disjoint(&used_mutably) + } else { + return; + }; + let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); + + let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { + has_break_or_return: false, + }; + has_break_or_return_visitor.visit_expr(expr); + let has_break_or_return = has_break_or_return_visitor.has_break_or_return; + + if no_cond_variable_mutated && !mutable_static_in_cond { + span_lint_and_then( + cx, + WHILE_IMMUTABLE_CONDITION, + cond.span, + "variables in the condition are not mutated in the loop body", + |diag| { + diag.note("this may lead to an infinite or to a never running loop"); + + if has_break_or_return { + diag.note("this loop contains `return`s or `break`s"); + diag.help("rewrite it as `if cond { loop { } }`"); + } + }, + ); + } +} + +struct HasBreakOrReturnVisitor { + has_break_or_return: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.has_break_or_return { + return; + } + + match expr.kind { + ExprKind::Ret(_) | ExprKind::Break(_, _) => { + self.has_break_or_return = true; + return; + }, + _ => {}, + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Collects the set of variables in an expression +/// Stops analysis if a function call is found +/// Note: In some cases such as `self`, there are no mutable annotation, +/// All variables definition IDs are collected +struct VarCollectorVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + ids: FxHashSet, + def_ids: FxHashMap, + skip: bool, +} + +impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { + fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Path(ref qpath) = ex.kind; + if let QPath::Resolved(None, _) = *qpath; + let res = qpath_res(self.cx, qpath, ex.hir_id); + then { + match res { + Res::Local(hir_id) => { + self.ids.insert(hir_id); + }, + Res::Def(DefKind::Static, def_id) => { + let mutable = self.cx.tcx.is_mutable_static(def_id); + self.def_ids.insert(def_id, mutable); + }, + _ => {}, + } + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + match ex.kind { + ExprKind::Path(_) => self.insert_def_id(ex), + // If there is any function/method call… we just stop analysis + ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true, + + _ => walk_expr(self, ex), + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; + +fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, 'tcx>) { + if_chain! { + if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind; + if let ExprKind::MethodCall(ref chain_method, _, _) = args[0].kind; + if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR); + if let Some(ref generic_args) = chain_method.args; + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + then { + let ty = cx.tables.node_type(ty.hir_id); + if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || + match_type(cx, ty, &paths::BTREEMAP) || + is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { + if method.ident.name == sym!(len) { + let span = shorten_needless_collect_span(expr); + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + "replace with", + ".count()".to_string(), + Applicability::MachineApplicable, + ); + } + if method.ident.name == sym!(is_empty) { + let span = shorten_needless_collect_span(expr); + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + "replace with", + ".next().is_none()".to_string(), + Applicability::MachineApplicable, + ); + } + if method.ident.name == sym!(contains) { + let contains_arg = snippet(cx, args[1].span, "??"); + let span = shorten_needless_collect_span(expr); + span_lint_and_then( + cx, + NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + |diag| { + let (arg, pred) = if contains_arg.starts_with('&') { + ("x", &contains_arg[1..]) + } else { + ("&x", &*contains_arg) + }; + diag.span_suggestion( + span, + "replace with", + format!( + ".any(|{}| x == {})", + arg, pred + ), + Applicability::MachineApplicable, + ); + } + ); + } + } + } + } +} + +fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { + if_chain! { + if let ExprKind::MethodCall(_, _, ref args) = expr.kind; + if let ExprKind::MethodCall(_, ref span, _) = args[0].kind; + then { + return expr.span.with_lo(span.lo() - BytePos(1)); + } + } + unreachable!() +} diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs new file mode 100644 index 0000000000000..b1345d0b751ae --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -0,0 +1,53 @@ +use crate::utils::{snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::edition::Edition; + +declare_clippy_lint! { + /// **What it does:** Checks for `#[macro_use] use...`. + /// + /// **Why is this bad?** Since the Rust 2018 edition you can import + /// macro's directly, this is considered idiomatic. + /// + /// **Known problems:** This lint does not generate an auto-applicable suggestion. + /// + /// **Example:** + /// ```rust + /// #[macro_use] + /// use lazy_static; + /// ``` + pub MACRO_USE_IMPORTS, + pedantic, + "#[macro_use] is no longer needed" +} + +declare_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]); + +impl EarlyLintPass for MacroUseImports { + fn check_item(&mut self, ecx: &EarlyContext<'_>, item: &ast::Item) { + if_chain! { + if ecx.sess.opts.edition == Edition::Edition2018; + if let ast::ItemKind::Use(use_tree) = &item.kind; + if let Some(mac_attr) = item + .attrs + .iter() + .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + then { + let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition"; + let help = format!("use {}::", snippet(ecx, use_tree.span, "_")); + span_lint_and_sugg( + ecx, + MACRO_USE_IMPORTS, + mac_attr.span, + msg, + "remove the attribute and import the macro directly, try", + help, + Applicability::HasPlaceholders, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/main_recursion.rs b/src/tools/clippy/clippy_lints/src/main_recursion.rs new file mode 100644 index 0000000000000..8a0e47a3d31c5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/main_recursion.rs @@ -0,0 +1,62 @@ +use rustc_hir::{Crate, Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +use crate::utils::{is_entrypoint_fn, is_no_std_crate, snippet, span_lint_and_help}; +use if_chain::if_chain; + +declare_clippy_lint! { + /// **What it does:** Checks for recursion using the entrypoint. + /// + /// **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]), + /// recursing into main() seems like an unintuitive antipattern we should be able to detect. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// fn main() { + /// main(); + /// } + /// ``` + pub MAIN_RECURSION, + style, + "recursion using the entrypoint" +} + +#[derive(Default)] +pub struct MainRecursion { + has_no_std_attr: bool, +} + +impl_lint_pass!(MainRecursion => [MAIN_RECURSION]); + +impl LateLintPass<'_, '_> for MainRecursion { + fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate<'_>) { + self.has_no_std_attr = is_no_std_crate(krate); + } + + fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if self.has_no_std_attr { + return; + } + + if_chain! { + if let ExprKind::Call(func, _) = &expr.kind; + if let ExprKind::Path(path) = &func.kind; + if let QPath::Resolved(_, path) = &path; + if let Some(def_id) = path.res.opt_def_id(); + if is_entrypoint_fn(cx, def_id); + then { + span_lint_and_help( + cx, + MAIN_RECURSION, + func.span, + &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), + None, + "consider using another function for this recursion" + ) + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs new file mode 100644 index 0000000000000..cb72a24058234 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -0,0 +1,159 @@ +use crate::utils::paths::FUTURE_FROM_GENERATOR; +use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync, + ItemKind, TraitRef, Ty, TyKind, TypeBindingKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** It checks for manual implementations of `async` functions. + /// + /// **Why is this bad?** It's more idiomatic to use the dedicated syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::future::Future; + /// + /// fn foo() -> impl Future { async { 42 } } + /// ``` + /// Use instead: + /// ```rust + /// use std::future::Future; + /// + /// async fn foo() -> i32 { 42 } + /// ``` + pub MANUAL_ASYNC_FN, + style, + "manual implementations of `async` functions can be simplified using the dedicated syntax" +} + +declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + _: HirId, + ) { + if_chain! { + if let Some(header) = kind.header(); + if let IsAsync::NotAsync = header.asyncness; + // Check that this function returns `impl Future` + if let FnRetTy::Return(ret_ty) = decl.output; + if let Some(trait_ref) = future_trait_ref(cx, ret_ty); + if let Some(output) = future_output_ty(trait_ref); + // Check that the body of the function consists of one async block + if let ExprKind::Block(block, _) = body.value.kind; + if block.stmts.is_empty(); + if let Some(closure_body) = desugared_async_block(cx, block); + then { + let header_span = span.with_hi(ret_ty.span.hi()); + + span_lint_and_then( + cx, + MANUAL_ASYNC_FN, + header_span, + "this function can be simplified using the `async fn` syntax", + |diag| { + if_chain! { + if let Some(header_snip) = snippet_opt(cx, header_span); + if let Some(ret_pos) = header_snip.rfind("->"); + if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); + then { + let help = format!("make the function `async` and {}", ret_sugg); + diag.span_suggestion( + header_span, + &help, + format!("async {}{}", &header_snip[..ret_pos], ret_snip), + Applicability::MachineApplicable + ); + + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); + diag.span_suggestion( + block.span, + "move the body of the async block to the enclosing function", + body_snip.to_string(), + Applicability::MachineApplicable + ); + } + } + }, + ); + } + } + } +} + +fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { + if_chain! { + if let TyKind::Def(item_id, _) = ty.kind; + let item = cx.tcx.hir().item(item_id.id); + if let ItemKind::OpaqueTy(opaque) = &item.kind; + if opaque.bounds.len() == 1; + if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; + if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); + then { + return Some(&poly.trait_ref); + } + } + + None +} + +fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> { + if_chain! { + if let Some(segment) = trait_ref.path.segments.last(); + if let Some(args) = segment.args; + if args.bindings.len() == 1; + let binding = &args.bindings[0]; + if binding.ident.as_str() == "Output"; + if let TypeBindingKind::Equality{ty: output} = binding.kind; + then { + return Some(output) + } + } + + None +} + +fn desugared_async_block<'tcx>(cx: &LateContext<'_, 'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { + if_chain! { + if let Some(block_expr) = block.expr; + if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); + if args.len() == 1; + if let Expr{kind: ExprKind::Closure(_, _, body_id, ..), ..} = args[0]; + let closure_body = cx.tcx.hir().body(body_id); + if let Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) = closure_body.generator_kind; + then { + return Some(closure_body); + } + } + + None +} + +fn suggested_ret(cx: &LateContext<'_, '_>, output: &Ty<'_>) -> Option<(&'static str, String)> { + match output.kind { + TyKind::Tup(tys) if tys.is_empty() => { + let sugg = "remove the return type"; + Some((sugg, "".into())) + }, + _ => { + let sugg = "return the output of the future directly"; + snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip))) + }, + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs new file mode 100644 index 0000000000000..f3b8902e26f67 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -0,0 +1,173 @@ +use crate::utils::{snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; +use rustc_attr as attr; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. + /// + /// **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent + /// and allows possible optimizations when applied to enums. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct S { + /// pub a: i32, + /// pub b: i32, + /// _c: (), + /// } + /// + /// enum E { + /// A, + /// B, + /// #[doc(hidden)] + /// _C, + /// } + /// + /// struct T(pub i32, pub i32, ()); + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// struct S { + /// pub a: i32, + /// pub b: i32, + /// } + /// + /// #[non_exhaustive] + /// enum E { + /// A, + /// B, + /// } + /// + /// #[non_exhaustive] + /// struct T(pub i32, pub i32); + /// ``` + pub MANUAL_NON_EXHAUSTIVE, + style, + "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" +} + +declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); + +impl EarlyLintPass for ManualNonExhaustive { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + match &item.kind { + ItemKind::Enum(def, _) => { + check_manual_non_exhaustive_enum(cx, item, &def.variants); + }, + ItemKind::Struct(variant_data, _) => { + if let VariantData::Unit(..) = variant_data { + return; + } + + check_manual_non_exhaustive_struct(cx, item, variant_data); + }, + _ => {}, + } + } +} + +fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { + fn is_non_exhaustive_marker(variant: &Variant) -> bool { + matches!(variant.data, VariantData::Unit(_)) + && variant.ident.as_str().starts_with('_') + && variant.attrs.iter().any(|a| is_doc_hidden(a)) + } + + fn is_doc_hidden(attr: &Attribute) -> bool { + attr.check_name(sym!(doc)) + && match attr.meta_item_list() { + Some(l) => attr::list_contains_name(&l, sym!(hidden)), + None => false, + } + } + + if_chain! { + let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); + if let Some(marker) = markers.next(); + if markers.count() == 0 && variants.len() > 1; + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let header_span = cx.sess.source_map().span_until_char(item.span, '{'); + if let Some(snippet) = snippet_opt(cx, header_span); + then { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } + } + diag.span_help(marker.span, "remove this variant"); + }); + } + } +} + +fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { + fn is_private(field: &StructField) -> bool { + matches!(field.vis.node, VisibilityKind::Inherited) + } + + fn is_non_exhaustive_marker(field: &StructField) -> bool { + is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) + } + + fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { + let delimiter = match data { + VariantData::Struct(..) => '{', + VariantData::Tuple(..) => '(', + VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), + }; + + cx.sess.source_map().span_until_char(item.span, delimiter) + } + + let fields = data.fields(); + let private_fields = fields.iter().filter(|f| is_private(f)).count(); + let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count(); + + if_chain! { + if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; + if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let header_span = find_header_span(cx, item, data); + if let Some(snippet) = snippet_opt(cx, header_span); + then { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } + } + diag.span_help(marker.span, "remove this field"); + }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs new file mode 100644 index 0000000000000..d5adf6b0f0dcb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -0,0 +1,150 @@ +use crate::utils::paths; +use crate::utils::{ + is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::Mutability; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests + /// `iterator.cloned()` instead + /// + /// **Why is this bad?** Readability, this can be written more concisely + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// let x = vec![42, 43]; + /// let y = x.iter(); + /// let z = y.map(|i| *i); + /// ``` + /// + /// The correct use would be: + /// + /// ```rust + /// let x = vec![42, 43]; + /// let y = x.iter(); + /// let z = y.cloned(); + /// ``` + pub MAP_CLONE, + style, + "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types" +} + +declare_lint_pass!(MapClone => [MAP_CLONE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapClone { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) { + if e.span.from_expansion() { + return; + } + + if_chain! { + if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.kind; + if args.len() == 2; + if method.ident.as_str() == "map"; + let ty = cx.tables.expr_ty(&args[0]); + if is_type_diagnostic_item(cx, ty, sym!(option_type)) || match_trait_method(cx, e, &paths::ITERATOR); + if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + then { + match closure_body.params[0].pat.kind { + hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding( + hir::BindingAnnotation::Unannotated, .., name, None + ) = inner.kind { + if ident_eq(name, closure_expr) { + lint(cx, e.span, args[0].span, true); + } + }, + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { + match closure_expr.kind { + hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => { + if ident_eq(name, inner) { + if let ty::Ref(.., Mutability::Not) = cx.tables.expr_ty(inner).kind { + lint(cx, e.span, args[0].span, true); + } + } + }, + hir::ExprKind::MethodCall(ref method, _, ref obj) => { + if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone" + && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { + + let obj_ty = cx.tables.expr_ty(&obj[0]); + if let ty::Ref(_, ty, _) = obj_ty.kind { + let copy = is_copy(cx, ty); + lint(cx, e.span, args[0].span, copy); + } else { + lint_needless_cloning(cx, e.span, args[0].span); + } + } + }, + _ => {}, + } + }, + _ => {}, + } + } + } + } +} + +fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.kind { + path.segments.len() == 1 && path.segments[0].ident == name + } else { + false + } +} + +fn lint_needless_cloning(cx: &LateContext<'_, '_>, root: Span, receiver: Span) { + span_lint_and_sugg( + cx, + MAP_CLONE, + root.trim_start(receiver).unwrap(), + "You are needlessly cloning iterator elements", + "Remove the `map` call", + String::new(), + Applicability::MachineApplicable, + ) +} + +fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span, copied: bool) { + let mut applicability = Applicability::MachineApplicable; + if copied { + span_lint_and_sugg( + cx, + MAP_CLONE, + replace, + "You are using an explicit closure for copying elements", + "Consider calling the dedicated `copied` method", + format!( + "{}.copied()", + snippet_with_applicability(cx, root, "..", &mut applicability) + ), + applicability, + ) + } else { + span_lint_and_sugg( + cx, + MAP_CLONE, + replace, + "You are using an explicit closure for cloning elements", + "Consider calling the dedicated `cloned` method", + format!( + "{}.cloned()", + snippet_with_applicability(cx, root, "..", &mut applicability) + ), + applicability, + ) + } +} diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs new file mode 100644 index 0000000000000..fecd91c7814dc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -0,0 +1,273 @@ +use crate::utils::{is_type_diagnostic_item, iter_input_pats, method_chain_args, snippet, span_lint_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `option.map(f)` where f is a function + /// or closure that returns the unit type. + /// + /// **Why is this bad?** Readability, this can be written more clearly with + /// an if let statement + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// # fn do_stuff() -> Option { Some(String::new()) } + /// # fn log_err_msg(foo: String) -> Option { Some(foo) } + /// # fn format_msg(foo: String) -> String { String::new() } + /// let x: Option = do_stuff(); + /// x.map(log_err_msg); + /// # let x: Option = do_stuff(); + /// x.map(|msg| log_err_msg(format_msg(msg))); + /// ``` + /// + /// The correct use would be: + /// + /// ```rust + /// # fn do_stuff() -> Option { Some(String::new()) } + /// # fn log_err_msg(foo: String) -> Option { Some(foo) } + /// # fn format_msg(foo: String) -> String { String::new() } + /// let x: Option = do_stuff(); + /// if let Some(msg) = x { + /// log_err_msg(msg); + /// } + /// + /// # let x: Option = do_stuff(); + /// if let Some(msg) = x { + /// log_err_msg(format_msg(msg)); + /// } + /// ``` + pub OPTION_MAP_UNIT_FN, + complexity, + "using `option.map(f)`, where `f` is a function or closure that returns `()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `result.map(f)` where f is a function + /// or closure that returns the unit type. + /// + /// **Why is this bad?** Readability, this can be written more clearly with + /// an if let statement + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// # fn do_stuff() -> Result { Ok(String::new()) } + /// # fn log_err_msg(foo: String) -> Result { Ok(foo) } + /// # fn format_msg(foo: String) -> String { String::new() } + /// let x: Result = do_stuff(); + /// x.map(log_err_msg); + /// # let x: Result = do_stuff(); + /// x.map(|msg| log_err_msg(format_msg(msg))); + /// ``` + /// + /// The correct use would be: + /// + /// ```rust + /// # fn do_stuff() -> Result { Ok(String::new()) } + /// # fn log_err_msg(foo: String) -> Result { Ok(foo) } + /// # fn format_msg(foo: String) -> String { String::new() } + /// let x: Result = do_stuff(); + /// if let Ok(msg) = x { + /// log_err_msg(msg); + /// }; + /// # let x: Result = do_stuff(); + /// if let Ok(msg) = x { + /// log_err_msg(format_msg(msg)); + /// }; + /// ``` + pub RESULT_MAP_UNIT_FN, + complexity, + "using `result.map(f)`, where `f` is a function or closure that returns `()`" +} + +declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]); + +fn is_unit_type(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Tuple(slice) => slice.is_empty(), + ty::Never => true, + _ => false, + } +} + +fn is_unit_function(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + + if let ty::FnDef(id, _) = ty.kind { + if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() { + return is_unit_type(fn_type.output()); + } + } + false +} + +fn is_unit_expression(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> bool { + is_unit_type(cx.tables.expr_ty(expr)) +} + +/// The expression inside a closure may or may not have surrounding braces and +/// semicolons, which causes problems when generating a suggestion. Given an +/// expression that evaluates to '()' or '!', recursively remove useless braces +/// and semi-colons until is suitable for including in the suggestion template +fn reduce_unit_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a hir::Expr<'_>) -> Option { + if !is_unit_expression(cx, expr) { + return None; + } + + match expr.kind { + hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(_, _, _) => { + // Calls can't be reduced any more + Some(expr.span) + }, + hir::ExprKind::Block(ref block, _) => { + match (&block.stmts[..], block.expr.as_ref()) { + (&[], Some(inner_expr)) => { + // If block only contains an expression, + // reduce `{ X }` to `X` + reduce_unit_expression(cx, inner_expr) + }, + (&[ref inner_stmt], None) => { + // If block only contains statements, + // reduce `{ X; }` to `X` or `X;` + match inner_stmt.kind { + hir::StmtKind::Local(ref local) => Some(local.span), + hir::StmtKind::Expr(ref e) => Some(e.span), + hir::StmtKind::Semi(..) => Some(inner_stmt.span), + hir::StmtKind::Item(..) => None, + } + }, + _ => { + // For closures that contain multiple statements + // it's difficult to get a correct suggestion span + // for all cases (multi-line closures specifically) + // + // We do not attempt to build a suggestion for those right now. + None + }, + } + }, + _ => None, + } +} + +fn unit_closure<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'a hir::Expr<'a>, +) -> Option<(&'tcx hir::Param<'tcx>, &'a hir::Expr<'a>)> { + if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind { + let body = cx.tcx.hir().body(inner_expr_id); + let body_expr = &body.value; + + if_chain! { + if decl.inputs.len() == 1; + if is_unit_expression(cx, body_expr); + if let Some(binding) = iter_input_pats(&decl, body).next(); + then { + return Some((binding, body_expr)); + } + } + } + None +} + +/// Builds a name for the let binding variable (`var_arg`) +/// +/// `x.field` => `x_field` +/// `y` => `_y` +/// +/// Anything else will return `a`. +fn let_binding_name(cx: &LateContext<'_, '_>, var_arg: &hir::Expr<'_>) -> String { + match &var_arg.kind { + hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"), + hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")), + _ => "a".to_string(), + } +} + +#[must_use] +fn suggestion_msg(function_type: &str, map_type: &str) -> String { + format!( + "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type", + map_type, function_type + ) +} + +fn lint_map_unit_fn(cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr<'_>, map_args: &[hir::Expr<'_>]) { + let var_arg = &map_args[0]; + + let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.tables.expr_ty(var_arg), sym!(option_type)) { + ("Option", "Some", OPTION_MAP_UNIT_FN) + } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(var_arg), sym!(result_type)) { + ("Result", "Ok", RESULT_MAP_UNIT_FN) + } else { + return; + }; + let fn_arg = &map_args[1]; + + if is_unit_function(cx, fn_arg) { + let msg = suggestion_msg("function", map_type); + let suggestion = format!( + "if let {0}({binding}) = {1} {{ {2}({binding}) }}", + variant, + snippet(cx, var_arg.span, "_"), + snippet(cx, fn_arg.span, "_"), + binding = let_binding_name(cx, var_arg) + ); + + span_lint_and_then(cx, lint, expr.span, &msg, |diag| { + diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable); + }); + } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { + let msg = suggestion_msg("closure", map_type); + + span_lint_and_then(cx, lint, expr.span, &msg, |diag| { + if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { + let suggestion = format!( + "if let {0}({1}) = {2} {{ {3} }}", + variant, + snippet(cx, binding.pat.span, "_"), + snippet(cx, var_arg.span, "_"), + snippet(cx, reduced_expr_span, "_") + ); + diag.span_suggestion( + stmt.span, + "try this", + suggestion, + Applicability::MachineApplicable, // snippet + ); + } else { + let suggestion = format!( + "if let {0}({1}) = {2} {{ ... }}", + variant, + snippet(cx, binding.pat.span, "_"), + snippet(cx, var_arg.span, "_"), + ); + diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::HasPlaceholders); + } + }); + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapUnit { + fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) { + if stmt.span.from_expansion() { + return; + } + + if let hir::StmtKind::Semi(ref expr) = stmt.kind { + if let Some(arglists) = method_chain_args(expr, &["map"]) { + lint_map_unit_fn(cx, stmt, expr, arglists[0]); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs new file mode 100644 index 0000000000000..ee69628e9f052 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs @@ -0,0 +1,100 @@ +use crate::utils::{self, is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, MatchSource}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`. + /// + /// **Why is this bad?** This can panic at runtime. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Bad + /// match arr[idx] { + /// 0 => println!("{}", 0), + /// 1 => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Good + /// match arr.get(idx) { + /// Some(0) => println!("{}", 0), + /// Some(1) => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + pub MATCH_ON_VEC_ITEMS, + pedantic, + "matching on vector elements can panic" +} + +declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchOnVecItems { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Match(ref match_expr, _, MatchSource::Normal) = expr.kind; + if let Some(idx_expr) = is_vec_indexing(cx, match_expr); + if let ExprKind::Index(vec, idx) = idx_expr.kind; + + then { + // 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, + match_expr.span, + "indexing into a vector may panic", + "try this", + format!( + "{}.get({})", + snippet(cx, vec.span, ".."), + snippet(cx, idx.span, "..") + ), + Applicability::MaybeIncorrect + ); + } + } + } +} + +fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::Index(ref array, ref index) = expr.kind; + if is_vector(cx, array); + if !is_full_range(cx, index); + + then { + return Some(expr); + } + } + + None +} + +fn is_vector(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + let ty = walk_ptrs_ty(ty); + is_type_diagnostic_item(cx, ty, sym!(vec_type)) +} + +fn is_full_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + let ty = walk_ptrs_ty(ty); + match_type(cx, ty, &utils::paths::RANGE_FULL) +} diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs new file mode 100644 index 0000000000000..bbf14374a1f7f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -0,0 +1,1242 @@ +use crate::consts::{constant, miri_to_const, Constant}; +use crate::utils::paths; +use crate::utils::sugg::Sugg; +use crate::utils::usage::is_unused; +use crate::utils::{ + expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, + is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet, + snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, walk_ptrs_ty, +}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def::CtorKind; +use rustc_hir::{ + Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind, + QPath, RangeEnd, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; +use std::cmp::Ordering; +use std::collections::Bound; + +declare_clippy_lint! { + /// **What it does:** Checks for matches with a single arm where an `if let` + /// will usually suffice. + /// + /// **Why is this bad?** Just readability – `if let` nests less than a `match`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn bar(stool: &str) {} + /// # let x = Some("abc"); + /// match x { + /// Some(ref foo) => bar(foo), + /// _ => (), + /// } + /// ``` + pub SINGLE_MATCH, + style, + "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for matches with two arms where an `if let else` will + /// usually suffice. + /// + /// **Why is this bad?** Just readability – `if let` nests less than a `match`. + /// + /// **Known problems:** Personal style preferences may differ. + /// + /// **Example:** + /// + /// Using `match`: + /// + /// ```rust + /// # fn bar(foo: &usize) {} + /// # let other_ref: usize = 1; + /// # let x: Option<&usize> = Some(&1); + /// match x { + /// Some(ref foo) => bar(foo), + /// _ => bar(&other_ref), + /// } + /// ``` + /// + /// Using `if let` with `else`: + /// + /// ```rust + /// # fn bar(foo: &usize) {} + /// # let other_ref: usize = 1; + /// # let x: Option<&usize> = Some(&1); + /// if let Some(ref foo) = x { + /// bar(foo); + /// } else { + /// bar(&other_ref); + /// } + /// ``` + pub SINGLE_MATCH_ELSE, + pedantic, + "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern" +} + +declare_clippy_lint! { + /// **What it does:** Checks for matches where all arms match a reference, + /// suggesting to remove the reference and deref the matched expression + /// instead. It also checks for `if let &foo = bar` blocks. + /// + /// **Why is this bad?** It just makes the code less readable. That reference + /// destructuring adds nothing to the code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// match x { + /// &A(ref y) => foo(y), + /// &B => bar(), + /// _ => frob(&x), + /// } + /// ``` + pub MATCH_REF_PATS, + style, + "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression" +} + +declare_clippy_lint! { + /// **What it does:** Checks for matches where match expression is a `bool`. It + /// suggests to replace the expression with an `if...else` block. + /// + /// **Why is this bad?** It makes the code less readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn foo() {} + /// # fn bar() {} + /// let condition: bool = true; + /// match condition { + /// true => foo(), + /// false => bar(), + /// } + /// ``` + /// Use if/else instead: + /// ```rust + /// # fn foo() {} + /// # fn bar() {} + /// let condition: bool = true; + /// if condition { + /// foo(); + /// } else { + /// bar(); + /// } + /// ``` + pub MATCH_BOOL, + pedantic, + "a `match` on a boolean expression instead of an `if..else` block" +} + +declare_clippy_lint! { + /// **What it does:** Checks for overlapping match arms. + /// + /// **Why is this bad?** It is likely to be an error and if not, makes the code + /// less obvious. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = 5; + /// match x { + /// 1...10 => println!("1 ... 10"), + /// 5...15 => println!("5 ... 15"), + /// _ => (), + /// } + /// ``` + pub MATCH_OVERLAPPING_ARM, + style, + "a `match` with overlapping arms" +} + +declare_clippy_lint! { + /// **What it does:** Checks for arm which matches all errors with `Err(_)` + /// and take drastic actions like `panic!`. + /// + /// **Why is this bad?** It is generally a bad practice, just like + /// catching all exceptions in java with `catch(Exception)` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x: Result = Ok(3); + /// match x { + /// Ok(_) => println!("ok"), + /// Err(_) => panic!("err"), + /// } + /// ``` + pub MATCH_WILD_ERR_ARM, + style, + "a `match` with `Err(_)` arm and take drastic actions" +} + +declare_clippy_lint! { + /// **What it does:** Checks for match which is used to add a reference to an + /// `Option` value. + /// + /// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x: Option<()> = None; + /// let r: Option<&()> = match x { + /// None => None, + /// Some(ref v) => Some(v), + /// }; + /// ``` + pub MATCH_AS_REF, + complexity, + "a `match` on an Option value instead of using `as_ref()` or `as_mut`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for wildcard enum matches using `_`. + /// + /// **Why is this bad?** New enum variants added by library updates can be missed. + /// + /// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some + /// variants, and also may not use correct path to enum if it's not present in the current scope. + /// + /// **Example:** + /// ```rust + /// # enum Foo { A(usize), B(usize) } + /// # let x = Foo::B(1); + /// match x { + /// A => {}, + /// _ => {}, + /// } + /// ``` + pub WILDCARD_ENUM_MATCH_ARM, + restriction, + "a wildcard enum match arm using `_`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm. + /// + /// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway. + /// It makes the code less readable, especially to spot wildcard pattern use in match arm. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// match "foo" { + /// "a" => {}, + /// "bar" | _ => {}, + /// } + /// ``` + pub WILDCARD_IN_OR_PATTERNS, + complexity, + "a wildcard pattern used with others patterns in same match arm" +} + +declare_clippy_lint! { + /// **What it does:** Checks for matches being used to destructure a single-variant enum + /// or tuple struct where a `let` will suffice. + /// + /// **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// enum Wrapper { + /// Data(i32), + /// } + /// + /// let wrapper = Wrapper::Data(42); + /// + /// let data = match wrapper { + /// Wrapper::Data(i) => i, + /// }; + /// ``` + /// + /// The correct use would be: + /// ```rust + /// enum Wrapper { + /// Data(i32), + /// } + /// + /// let wrapper = Wrapper::Data(42); + /// let Wrapper::Data(data) = wrapper; + /// ``` + pub INFALLIBLE_DESTRUCTURING_MATCH, + style, + "a `match` statement with a single infallible arm instead of a `let`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for useless match that binds to only one value. + /// + /// **Why is this bad?** Readability and needless complexity. + /// + /// **Known problems:** Suggested replacements may be incorrect when `match` + /// is actually binding temporary value, bringing a 'dropped while borrowed' error. + /// + /// **Example:** + /// ```rust + /// # let a = 1; + /// # let b = 2; + /// + /// // Bad + /// match (a, b) { + /// (c, d) => { + /// // useless match + /// } + /// } + /// + /// // Good + /// let (c, d) = (a, b); + /// ``` + pub MATCH_SINGLE_BINDING, + complexity, + "a match with a single binding instead of using `let` statement" +} + +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched. + /// + /// **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after + /// matching all enum variants explicitly. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct A { a: i32 } + /// let a = A { a: 5 }; + /// + /// // Bad + /// match a { + /// A { a: 5, .. } => {}, + /// _ => {}, + /// } + /// + /// // Good + /// match a { + /// A { a: 5 } => {}, + /// _ => {}, + /// } + /// ``` + pub REST_PAT_IN_FULLY_BOUND_STRUCTS, + restriction, + "a match on a struct that binds all fields but still uses the wildcard pattern" +} + +#[derive(Default)] +pub struct Matches { + infallible_destructuring_match_linted: bool, +} + +impl_lint_pass!(Matches => [ + SINGLE_MATCH, + MATCH_REF_PATS, + MATCH_BOOL, + SINGLE_MATCH_ELSE, + MATCH_OVERLAPPING_ARM, + MATCH_WILD_ERR_ARM, + MATCH_AS_REF, + WILDCARD_ENUM_MATCH_ARM, + WILDCARD_IN_OR_PATTERNS, + MATCH_SINGLE_BINDING, + INFALLIBLE_DESTRUCTURING_MATCH, + REST_PAT_IN_FULLY_BOUND_STRUCTS +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { + check_single_match(cx, ex, arms, expr); + check_match_bool(cx, ex, arms, expr); + check_overlapping_arms(cx, ex, arms); + check_wild_err_arm(cx, ex, arms); + check_wild_enum_match(cx, ex, arms); + check_match_as_ref(cx, ex, arms, expr); + check_wild_in_or_pats(cx, arms); + + if self.infallible_destructuring_match_linted { + self.infallible_destructuring_match_linted = false; + } else { + check_match_single_binding(cx, ex, arms, expr); + } + } + if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { + check_match_ref_pats(cx, ex, arms, expr); + } + } + + fn check_local(&mut self, cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>) { + if_chain! { + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); + if let Some(ref expr) = local.init; + if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind; + if arms.len() == 1 && arms[0].guard.is_none(); + if let PatKind::TupleStruct( + QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind; + if args.len() == 1; + if let Some(arg) = get_arg_name(&args[0]); + let body = remove_blocks(&arms[0].body); + if match_var(body, arg); + + then { + let mut applicability = Applicability::MachineApplicable; + self.infallible_destructuring_match_linted = true; + span_lint_and_sugg( + cx, + INFALLIBLE_DESTRUCTURING_MATCH, + local.span, + "you seem to be trying to use `match` to destructure a single infallible pattern. \ + Consider using `let`", + "try this", + format!( + "let {}({}) = {};", + snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), + snippet_with_applicability(cx, target.span, "..", &mut applicability), + ), + applicability, + ); + } + } + } + + fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) { + if_chain! { + if !in_external_macro(cx.sess(), pat.span); + if !in_macro(pat.span); + if let PatKind::Struct(ref qpath, fields, true) = pat.kind; + if let QPath::Resolved(_, ref path) = qpath; + if let Some(def_id) = path.res.opt_def_id(); + let ty = cx.tcx.type_of(def_id); + if let ty::Adt(def, _) = ty.kind; + if def.is_struct() || def.is_union(); + if fields.len() == def.non_enum_variant().fields.len(); + + then { + span_lint_and_help( + cx, + REST_PAT_IN_FULLY_BOUND_STRUCTS, + pat.span, + "unnecessary use of `..` pattern in struct binding. All fields were already bound", + None, + "consider removing `..` from this binding", + ); + } + } + } +} + +#[rustfmt::skip] +fn check_single_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { + if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { + if in_macro(expr.span) { + // Don't lint match expressions present in + // macro_rules! block + return; + } + if let PatKind::Or(..) = arms[0].pat.kind { + // don't lint for or patterns for now, this makes + // the lint noisy in unnecessary situations + return; + } + let els = remove_blocks(&arms[1].body); + let els = if is_unit_expr(els) { + None + } else if let ExprKind::Block(_, _) = els.kind { + // matches with blocks that contain statements are prettier as `if let + else` + Some(els) + } else { + // allow match arms with just expressions + return; + }; + let ty = cx.tables.expr_ty(ex); + if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { + check_single_match_single_pattern(cx, ex, arms, expr, els); + check_single_match_opt_like(cx, ex, arms, expr, ty, els); + } + } +} + +fn check_single_match_single_pattern( + cx: &LateContext<'_, '_>, + ex: &Expr<'_>, + arms: &[Arm<'_>], + expr: &Expr<'_>, + els: Option<&Expr<'_>>, +) { + if is_wild(&arms[1].pat) { + report_single_match_single_pattern(cx, ex, arms, expr, els); + } +} + +fn report_single_match_single_pattern( + cx: &LateContext<'_, '_>, + ex: &Expr<'_>, + arms: &[Arm<'_>], + expr: &Expr<'_>, + els: Option<&Expr<'_>>, +) { + let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; + let els_str = els.map_or(String::new(), |els| { + format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) + }); + span_lint_and_sugg( + cx, + lint, + expr.span, + "you seem to be trying to use match for destructuring a single pattern. Consider using `if \ + let`", + "try this", + format!( + "if let {} = {} {}{}", + snippet(cx, arms[0].pat.span, ".."), + snippet(cx, ex.span, ".."), + expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + els_str, + ), + Applicability::HasPlaceholders, + ); +} + +fn check_single_match_opt_like( + cx: &LateContext<'_, '_>, + ex: &Expr<'_>, + arms: &[Arm<'_>], + expr: &Expr<'_>, + ty: Ty<'_>, + els: Option<&Expr<'_>>, +) { + // list of candidate `Enum`s we know will never get any more members + let candidates = &[ + (&paths::COW, "Borrowed"), + (&paths::COW, "Cow::Borrowed"), + (&paths::COW, "Cow::Owned"), + (&paths::COW, "Owned"), + (&paths::OPTION, "None"), + (&paths::RESULT, "Err"), + (&paths::RESULT, "Ok"), + ]; + + let path = match arms[1].pat.kind { + PatKind::TupleStruct(ref path, ref inner, _) => { + // Contains any non wildcard patterns (e.g., `Err(err)`)? + if !inner.iter().all(is_wild) { + return; + } + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) + }, + PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(), + PatKind::Path(ref path) => { + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) + }, + _ => return, + }; + + for &(ty_path, pat_path) in candidates { + if path == *pat_path && match_type(cx, ty, ty_path) { + report_single_match_single_pattern(cx, ex, arms, expr, els); + } + } +} + +fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { + // Type of expression is `bool`. + if cx.tables.expr_ty(ex).kind == ty::Bool { + span_lint_and_then( + cx, + MATCH_BOOL, + expr.span, + "you seem to be trying to match on a boolean expression", + move |diag| { + if arms.len() == 2 { + // no guards + let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind { + if let ExprKind::Lit(ref lit) = arm_bool.kind { + match lit.node { + LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)), + LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)), + _ => None, + } + } else { + None + } + } else { + None + }; + + if let Some((true_expr, false_expr)) = exprs { + let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { + (false, false) => Some(format!( + "if {} {} else {}", + snippet(cx, ex.span, "b"), + expr_block(cx, true_expr, None, "..", Some(expr.span)), + expr_block(cx, false_expr, None, "..", Some(expr.span)) + )), + (false, true) => Some(format!( + "if {} {}", + snippet(cx, ex.span, "b"), + expr_block(cx, true_expr, None, "..", Some(expr.span)) + )), + (true, false) => { + let test = Sugg::hir(cx, ex, ".."); + Some(format!( + "if {} {}", + !test, + expr_block(cx, false_expr, None, "..", Some(expr.span)) + )) + }, + (true, true) => None, + }; + + if let Some(sugg) = sugg { + diag.span_suggestion( + expr.span, + "consider using an `if`/`else` expression", + sugg, + Applicability::HasPlaceholders, + ); + } + } + } + }, + ); + } +} + +fn check_overlapping_arms<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { + if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() { + let ranges = all_ranges(cx, arms, cx.tables.expr_ty(ex)); + let type_ranges = type_ranges(&ranges); + if !type_ranges.is_empty() { + if let Some((start, end)) = overlapping(&type_ranges) { + span_lint_and_note( + cx, + MATCH_OVERLAPPING_ARM, + start.span, + "some ranges overlap", + Some(end.span), + "overlaps with this", + ); + } + } + } +} + +fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { + let ex_ty = walk_ptrs_ty(cx.tables.expr_ty(ex)); + if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) { + for arm in arms { + if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { + let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); + if path_str == "Err" { + let mut matching_wild = inner.iter().any(is_wild); + let mut ident_bind_name = String::from("_"); + if !matching_wild { + // Looking for unused bindings (i.e.: `_e`) + inner.iter().for_each(|pat| { + if let PatKind::Binding(.., ident, None) = &pat.kind { + if ident.as_str().starts_with('_') && is_unused(ident, arm.body) { + ident_bind_name = (&ident.name.as_str()).to_string(); + matching_wild = true; + } + } + }); + } + if_chain! { + if matching_wild; + if let ExprKind::Block(ref block, _) = arm.body.kind; + if is_panic_block(block); + then { + // `Err(_)` or `Err(_e)` arm with `panic!` found + span_lint_and_note(cx, + MATCH_WILD_ERR_ARM, + arm.pat.span, + &format!("`Err({})` matches all errors", &ident_bind_name), + None, + "match each error separately or use the error output", + ); + } + } + } + } + } + } +} + +fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { + let ty = cx.tables.expr_ty(ex); + if !ty.is_enum() { + // If there isn't a nice closed set of possible values that can be conveniently enumerated, + // don't complain about not enumerating the mall. + return; + } + + // First pass - check for violation, but don't do much book-keeping because this is hopefully + // the uncommon case, and the book-keeping is slightly expensive. + let mut wildcard_span = None; + let mut wildcard_ident = None; + for arm in arms { + if let PatKind::Wild = arm.pat.kind { + wildcard_span = Some(arm.pat.span); + } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind { + wildcard_span = Some(arm.pat.span); + wildcard_ident = Some(ident); + } + } + + if let Some(wildcard_span) = wildcard_span { + // Accumulate the variants which should be put in place of the wildcard because they're not + // already covered. + + let mut missing_variants = vec![]; + if let ty::Adt(def, _) = ty.kind { + for variant in &def.variants { + missing_variants.push(variant); + } + } + + for arm in arms { + if arm.guard.is_some() { + // Guards mean that this case probably isn't exhaustively covered. Technically + // this is incorrect, as we should really check whether each variant is exhaustively + // covered by the set of guards that cover it, but that's really hard to do. + continue; + } + if let PatKind::Path(ref path) = arm.pat.kind { + if let QPath::Resolved(_, p) = path { + missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + } + } else if let PatKind::TupleStruct(ref path, ..) = arm.pat.kind { + if let QPath::Resolved(_, p) = path { + missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + } + } + } + + let mut suggestion: Vec = missing_variants + .iter() + .map(|v| { + let suffix = match v.ctor_kind { + CtorKind::Fn => "(..)", + CtorKind::Const | CtorKind::Fictive => "", + }; + let ident_str = if let Some(ident) = wildcard_ident { + format!("{} @ ", ident.name) + } else { + String::new() + }; + // This path assumes that the enum type is imported into scope. + format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix) + }) + .collect(); + + if suggestion.is_empty() { + return; + } + + let mut message = "wildcard match will miss any future added variants"; + + if let ty::Adt(def, _) = ty.kind { + if def.is_variant_list_non_exhaustive() { + message = "match on non-exhaustive enum doesn't explicitly match all known variants"; + suggestion.push(String::from("_")); + } + } + + span_lint_and_sugg( + cx, + WILDCARD_ENUM_MATCH_ARM, + wildcard_span, + message, + "try this", + suggestion.join(" | "), + Applicability::MachineApplicable, + ) + } +} + +// If the block contains only a `panic!` macro (as expression or statement) +fn is_panic_block(block: &Block<'_>) -> bool { + match (&block.expr, block.stmts.len(), block.stmts.first()) { + (&Some(ref exp), 0, _) => { + is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none() + }, + (&None, 1, Some(stmt)) => { + is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none() + }, + _ => false, + } +} + +fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { + if has_only_ref_pats(arms) { + let mut suggs = Vec::with_capacity(arms.len() + 1); + let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind { + let span = ex.span.source_callsite(); + suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); + ( + "you don't need to add `&` to both the expression and the patterns", + "try", + ) + } else { + let span = ex.span.source_callsite(); + suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string())); + ( + "you don't need to add `&` to all patterns", + "instead of prefixing all patterns with `&`, you can dereference the expression", + ) + }; + + suggs.extend(arms.iter().filter_map(|a| { + if let PatKind::Ref(ref refp, _) = a.pat.kind { + Some((a.pat.span, snippet(cx, refp.span, "..").to_string())) + } else { + None + } + })); + + span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| { + if !expr.span.from_expansion() { + multispan_sugg(diag, msg, suggs); + } + }); + } +} + +fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { + if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { + let arm_ref: Option = if is_none_arm(&arms[0]) { + is_ref_some_arm(&arms[1]) + } else if is_none_arm(&arms[1]) { + is_ref_some_arm(&arms[0]) + } else { + None + }; + if let Some(rb) = arm_ref { + let suggestion = if rb == BindingAnnotation::Ref { + "as_ref" + } else { + "as_mut" + }; + + let output_ty = cx.tables.expr_ty(expr); + let input_ty = cx.tables.expr_ty(ex); + + let cast = if_chain! { + if let ty::Adt(_, substs) = input_ty.kind; + let input_ty = substs.type_at(0); + if let ty::Adt(_, substs) = output_ty.kind; + let output_ty = substs.type_at(0); + if let ty::Ref(_, output_ty, _) = output_ty.kind; + if input_ty != output_ty; + then { + ".map(|x| x as _)" + } else { + "" + } + }; + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MATCH_AS_REF, + expr.span, + &format!("use `{}()` instead", suggestion), + "try this", + format!( + "{}.{}(){}", + snippet_with_applicability(cx, ex.span, "_", &mut applicability), + suggestion, + cast, + ), + applicability, + ) + } + } +} + +fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) { + for arm in arms { + if let PatKind::Or(ref 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.", + ); + } + } + } +} + +fn check_match_single_binding<'a>(cx: &LateContext<'_, 'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { + if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { + return; + } + let matched_vars = ex.span; + let bind_names = arms[0].pat.span; + let match_body = remove_blocks(&arms[0].body); + let mut snippet_body = if match_body.span.from_expansion() { + Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string() + } else { + snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string() + }; + + // Do we need to add ';' to suggestion ? + match match_body.kind { + ExprKind::Block(block, _) => { + // macro + expr_ty(body) == () + if block.span.from_expansion() && cx.tables.expr_ty(&match_body).is_unit() { + snippet_body.push(';'); + } + }, + _ => { + // expr_ty(body) == () + if cx.tables.expr_ty(&match_body).is_unit() { + snippet_body.push(';'); + } + }, + } + + let mut applicability = Applicability::MaybeIncorrect; + match arms[0].pat.kind { + PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => { + // If this match is in a local (`let`) stmt + let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) { + ( + parent_let_node.span, + format!( + "let {} = {};\n{}let {} = {};", + snippet_with_applicability(cx, bind_names, "..", &mut applicability), + snippet_with_applicability(cx, matched_vars, "..", &mut applicability), + " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), + snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability), + snippet_body + ), + ) + } else { + // If we are in closure, we need curly braces around suggestion + let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); + let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string()); + if let Some(parent_expr) = get_parent_expr(cx, 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); + } + }; + ( + expr.span, + format!( + "{}let {} = {};\n{}{}{}", + cbrace_start, + snippet_with_applicability(cx, bind_names, "..", &mut applicability), + snippet_with_applicability(cx, matched_vars, "..", &mut applicability), + indent, + snippet_body, + cbrace_end + ), + ) + }; + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + target_span, + "this match could be written as a `let` statement", + "consider using `let` statement", + sugg, + applicability, + ); + }, + PatKind::Wild => { + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its body itself", + "consider using the match body instead", + snippet_body, + Applicability::MachineApplicable, + ); + }, + _ => (), + } +} + +/// Returns true if the `ex` match expression is in a local (`let`) statement +fn opt_parent_let<'a>(cx: &LateContext<'_, 'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> { + if_chain! { + let map = &cx.tcx.hir(); + if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)); + if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id)); + then { + return Some(parent_let_expr); + } + } + None +} + +/// Gets all arms that are unbounded `PatRange`s. +fn all_ranges<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + arms: &'tcx [Arm<'_>], + ty: Ty<'tcx>, +) -> Vec> { + arms.iter() + .flat_map(|arm| { + if let Arm { + ref pat, guard: None, .. + } = *arm + { + if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { + let lhs = match lhs { + Some(lhs) => constant(cx, cx.tables, lhs)?.0, + None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?, + }; + let rhs = match rhs { + Some(rhs) => constant(cx, cx.tables, rhs)?.0, + None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?, + }; + let rhs = match range_end { + RangeEnd::Included => Bound::Included(rhs), + RangeEnd::Excluded => Bound::Excluded(rhs), + }; + return Some(SpannedRange { + span: pat.span, + node: (lhs, rhs), + }); + } + + if let PatKind::Lit(ref value) = pat.kind { + let value = constant(cx, cx.tables, value)?.0; + return Some(SpannedRange { + span: pat.span, + node: (value.clone(), Bound::Included(value)), + }); + } + } + None + }) + .collect() +} + +#[derive(Debug, Eq, PartialEq)] +pub struct SpannedRange { + pub span: Span, + pub node: (T, Bound), +} + +type TypedRanges = Vec>; + +/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway +/// and other types than +/// `Uint` and `Int` probably don't make sense. +fn type_ranges(ranges: &[SpannedRange]) -> TypedRanges { + ranges + .iter() + .filter_map(|range| match range.node { + (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange { + span: range.span, + node: (start, Bound::Included(end)), + }), + (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange { + span: range.span, + node: (start, Bound::Excluded(end)), + }), + (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange { + span: range.span, + node: (start, Bound::Unbounded), + }), + _ => None, + }) + .collect() +} + +fn is_unit_expr(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Tup(ref v) if v.is_empty() => true, + ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true, + _ => false, + } +} + +// Checks if arm has the form `None => None` +fn is_none_arm(arm: &Arm<'_>) -> bool { + match arm.pat.kind { + PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true, + _ => false, + } +} + +// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) +fn is_ref_some_arm(arm: &Arm<'_>) -> Option { + if_chain! { + if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind; + if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); + if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; + if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; + if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind; + if let ExprKind::Path(ref some_path) = e.kind; + if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; + if let ExprKind::Path(ref qpath) = args[0].kind; + if let &QPath::Resolved(_, ref path2) = qpath; + if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; + then { + return Some(rb) + } + } + None +} + +fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool { + let mapped = arms + .iter() + .map(|a| { + match a.pat.kind { + PatKind::Ref(..) => Some(true), // &-patterns + PatKind::Wild => Some(false), // an "anything" wildcard is also fine + _ => None, // any other pattern is not fine + } + }) + .collect::>>(); + // look for Some(v) where there's at least one true element + mapped.map_or(false, |v| v.iter().any(|el| *el)) +} + +pub fn overlapping(ranges: &[SpannedRange]) -> Option<(&SpannedRange, &SpannedRange)> +where + T: Copy + Ord, +{ + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + enum Kind<'a, T> { + Start(T, &'a SpannedRange), + End(Bound, &'a SpannedRange), + } + + impl<'a, T: Copy> Kind<'a, T> { + fn range(&self) -> &'a SpannedRange { + match *self { + Kind::Start(_, r) | Kind::End(_, r) => r, + } + } + + fn value(self) -> Bound { + match self { + Kind::Start(t, _) => Bound::Included(t), + Kind::End(t, _) => t, + } + } + } + + impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl<'a, T: Copy + Ord> Ord for Kind<'a, T> { + fn cmp(&self, other: &Self) -> Ordering { + match (self.value(), other.value()) { + (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b), + // Range patterns cannot be unbounded (yet) + (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(), + (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) { + Ordering::Equal => Ordering::Greater, + other => other, + }, + (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) { + Ordering::Equal => Ordering::Less, + other => other, + }, + } + } + } + + let mut values = Vec::with_capacity(2 * ranges.len()); + + for r in ranges { + values.push(Kind::Start(r.node.0, r)); + values.push(Kind::End(r.node.1, r)); + } + + values.sort(); + + for (a, b) in values.iter().zip(values.iter().skip(1)) { + match (a, b) { + (&Kind::Start(_, ra), &Kind::End(_, rb)) => { + if ra.node != rb.node { + return Some((ra, rb)); + } + }, + (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (), + _ => return Some((a.range(), b.range())), + } + } + + None +} + +#[test] +fn test_overlapping() { + use rustc_span::source_map::DUMMY_SP; + + let sp = |s, e| SpannedRange { + span: DUMMY_SP, + node: (s, e), + }; + + assert_eq!(None, overlapping::(&[])); + assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))])); + assert_eq!( + None, + overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))]) + ); + assert_eq!( + None, + overlapping(&[ + sp(1, Bound::Included(4)), + sp(5, Bound::Included(6)), + sp(10, Bound::Included(11)) + ],) + ); + assert_eq!( + Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))), + overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))]) + ); + assert_eq!( + Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))), + overlapping(&[ + sp(1, Bound::Included(4)), + sp(5, Bound::Included(6)), + sp(6, Bound::Included(11)) + ],) + ); +} diff --git a/src/tools/clippy/clippy_lints/src/mem_discriminant.rs b/src/tools/clippy/clippy_lints/src/mem_discriminant.rs new file mode 100644 index 0000000000000..3f953655670cf --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mem_discriminant.rs @@ -0,0 +1,81 @@ +use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::iter; + +declare_clippy_lint! { + /// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type. + /// + /// **Why is this bad?** The value of `mem::discriminant()` on non-enum types + /// is unspecified. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// use std::mem; + /// + /// mem::discriminant(&"hello"); + /// mem::discriminant(&&Some(2)); + /// ``` + pub MEM_DISCRIMINANT_NON_ENUM, + correctness, + "calling `mem::descriminant` on non-enum type" +} + +declare_lint_pass!(MemDiscriminant => [MEM_DISCRIMINANT_NON_ENUM]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref func, ref func_args) = expr.kind; + // is `mem::discriminant` + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_DISCRIMINANT); + // type is non-enum + let ty_param = cx.tables.node_substs(func.hir_id).type_at(0); + if !ty_param.is_enum(); + + then { + span_lint_and_then( + cx, + MEM_DISCRIMINANT_NON_ENUM, + expr.span, + &format!("calling `mem::discriminant` on non-enum type `{}`", ty_param), + |diag| { + // if this is a reference to an enum, suggest dereferencing + let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param); + if ptr_depth >= 1 && base_ty.is_enum() { + let param = &func_args[0]; + + // cancel out '&'s first + let mut derefs_needed = ptr_depth; + let mut cur_expr = param; + while derefs_needed > 0 { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref inner_expr) = cur_expr.kind { + derefs_needed -= 1; + cur_expr = inner_expr; + } else { + break; + } + } + + let derefs: String = iter::repeat('*').take(derefs_needed).collect(); + diag.span_suggestion( + param.span, + "try dereferencing", + format!("{}{}", derefs, snippet(cx, cur_expr.span, "")), + Applicability::MachineApplicable, + ); + } + }, + ) + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/mem_forget.rs b/src/tools/clippy/clippy_lints/src/mem_forget.rs new file mode 100644 index 0000000000000..c6ddc5de63b0e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mem_forget.rs @@ -0,0 +1,44 @@ +use crate::utils::{match_def_path, paths, qpath_res, span_lint}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is + /// `Drop`. + /// + /// **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its + /// destructor, possibly causing leaks. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::mem; + /// # use std::rc::Rc; + /// mem::forget(Rc::new(55)) + /// ``` + pub MEM_FORGET, + restriction, + "`mem::forget` usage on `Drop` types, likely to cause memory leaks" +} + +declare_lint_pass!(MemForget => [MEM_FORGET]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Call(ref path_expr, ref args) = e.kind { + if let ExprKind::Path(ref qpath) = path_expr.kind { + if let Some(def_id) = qpath_res(cx, qpath, path_expr.hir_id).opt_def_id() { + if match_def_path(cx, def_id, &paths::MEM_FORGET) { + let forgot_ty = cx.tables.expr_ty(&args[0]); + + if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) { + span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type"); + } + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs new file mode 100644 index 0000000000000..ab6865bf0f3b7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -0,0 +1,217 @@ +use crate::utils::{ + in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help, + span_lint_and_sugg, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// **What it does:** Checks for `mem::replace()` on an `Option` with + /// `None`. + /// + /// **Why is this bad?** `Option` already has the method `take()` for + /// taking its current value (Some(..) or None) and replacing it with + /// `None`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// use std::mem; + /// + /// let mut an_option = Some(0); + /// let replaced = mem::replace(&mut an_option, None); + /// ``` + /// Is better expressed with: + /// ```rust + /// let mut an_option = Some(0); + /// let taken = an_option.take(); + /// ``` + pub MEM_REPLACE_OPTION_WITH_NONE, + style, + "replacing an `Option` with `None` instead of `take()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())` + /// and `mem::replace(&mut _, mem::zeroed())`. + /// + /// **Why is this bad?** This will lead to undefined behavior even if the + /// value is overwritten later, because the uninitialized value may be + /// observed in the case of a panic. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ``` + /// use std::mem; + ///# fn may_panic(v: Vec) -> Vec { v } + /// + /// #[allow(deprecated, invalid_value)] + /// fn myfunc (v: &mut Vec) { + /// let taken_v = unsafe { mem::replace(v, mem::uninitialized()) }; + /// let new_v = may_panic(taken_v); // undefined behavior on panic + /// mem::forget(mem::replace(v, new_v)); + /// } + /// ``` + /// + /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution, + /// at the cost of either lazily creating a replacement value or aborting + /// on panic, to ensure that the uninitialized value cannot be observed. + pub MEM_REPLACE_WITH_UNINIT, + correctness, + "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `std::mem::replace` on a value of type + /// `T` with `T::default()`. + /// + /// **Why is this bad?** `std::mem` module already has the method `take` to + /// take the current value and replace it with the default value of that type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let mut text = String::from("foo"); + /// let replaced = std::mem::replace(&mut text, String::default()); + /// ``` + /// Is better expressed with: + /// ```rust + /// let mut text = String::from("foo"); + /// let taken = std::mem::take(&mut text); + /// ``` + pub MEM_REPLACE_WITH_DEFAULT, + style, + "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`" +} + +declare_lint_pass!(MemReplace => + [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); + +fn check_replace_option_with_none(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { + if let ExprKind::Path(ref replacement_qpath) = src.kind { + // Check that second argument is `Option::None` + if match_qpath(replacement_qpath, &paths::OPTION_NONE) { + // Since this is a late pass (already type-checked), + // and we already know that the second argument is an + // `Option`, we do not need to check the first + // argument's type. All that's left is to get + // replacee's path. + let replaced_path = match dest.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => { + if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind { + replaced_path + } else { + return; + } + }, + ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path, + _ => return, + }; + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_OPTION_WITH_NONE, + expr_span, + "replacing an `Option` with `None`", + "consider `Option::take()` instead", + format!( + "{}.take()", + snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) + ), + applicability, + ); + } + } +} + +fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, expr_span: Span) { + if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind { + if_chain! { + if repl_args.is_empty(); + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + then { + if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { + span_lint_and_help( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::uninitialized()`", + None, + "consider using the `take_mut` crate instead", + ); + } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) && + !cx.tables.expr_ty(src).is_primitive() { + span_lint_and_help( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::zeroed()`", + None, + "consider using a default value or the `take_mut` crate instead", + ); + } + } + } + } +} + +fn check_replace_with_default(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { + if let ExprKind::Call(ref repl_func, _) = src.kind { + if_chain! { + if !in_external_macro(cx.tcx.sess, expr_span); + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD); + then { + span_lint_and_then( + cx, + MEM_REPLACE_WITH_DEFAULT, + expr_span, + "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", + |diag| { + if !in_macro(expr_span) { + let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); + + diag.span_suggestion( + expr_span, + "consider using", + suggestion, + Applicability::MachineApplicable + ); + } + } + ); + } + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Check that `expr` is a call to `mem::replace()` + if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_REPLACE); + if let [dest, src] = &**func_args; + then { + check_replace_option_with_none(cx, src, dest, expr.span); + check_replace_with_uninit(cx, src, expr.span); + check_replace_with_default(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 new file mode 100644 index 0000000000000..32e86637569ed --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -0,0 +1,309 @@ +use super::{contains_return, BIND_INSTEAD_OF_MAP}; +use crate::utils::{ + in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, + snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::Span; + +pub(crate) struct OptionAndThenSome; +impl BindInsteadOfMap for OptionAndThenSome { + const TYPE_NAME: &'static str = "Option"; + const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Some"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::OPTION_SOME; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultAndThenOk; +impl BindInsteadOfMap for ResultAndThenOk { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Ok"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_OK; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultOrElseErrInfo; +impl BindInsteadOfMap for ResultOrElseErrInfo { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "or_else"; + const BAD_VARIANT_NAME: &'static str = "Err"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_ERR; + + const GOOD_METHOD_NAME: &'static str = "map_err"; +} + +pub(crate) trait BindInsteadOfMap { + const TYPE_NAME: &'static str; + const TYPE_QPATH: &'static [&'static str]; + + const BAD_METHOD_NAME: &'static str; + const BAD_VARIANT_NAME: &'static str; + const BAD_VARIANT_QPATH: &'static [&'static str]; + + const GOOD_METHOD_NAME: &'static str; + + fn no_op_msg() -> String { + format!( + "using `{}.{}({})`, which is a no-op", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME + ) + } + + fn lint_msg() -> String { + format!( + "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME, + Self::GOOD_METHOD_NAME + ) + } + + fn lint_closure_autofixable( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + args: &[hir::Expr<'_>], + closure_expr: &hir::Expr<'_>, + closure_args_span: Span, + ) -> bool { + if_chain! { + if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; + if let hir::ExprKind::Path(ref qpath) = some_expr.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if some_args.len() == 1; + then { + let inner_expr = &some_args[0]; + + if contains_return(inner_expr) { + return false; + } + + let some_inner_snip = if inner_expr.span.from_expansion() { + snippet_with_macro_callsite(cx, inner_expr.span, "_") + } else { + snippet(cx, inner_expr.span, "_") + }; + + let closure_args_snip = snippet(cx, closure_args_span, ".."); + let option_snip = snippet(cx, args[0].span, ".."); + let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::lint_msg().as_ref(), + "try this", + note, + Applicability::MachineApplicable, + ); + true + } else { + false + } + } + } + + fn lint_closure(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) { + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| { + if_chain! { + if !in_macro(ret_expr.span); + if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; + if let hir::ExprKind::Path(ref qpath) = func_path.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if args.len() == 1; + if !contains_return(&args[0]); + then { + suggs.push((ret_expr.span, args[0].span.source_callsite())); + true + } else { + false + } + } + }); + + if can_sugg { + span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, Self::lint_msg().as_ref(), |diag| { + multispan_sugg_with_applicability( + diag, + "try this", + Applicability::MachineApplicable, + std::iter::once((*method_calls(expr, 1).2.get(0).unwrap(), Self::GOOD_METHOD_NAME.into())).chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ), + ) + }); + } + } + + /// Lint use of `_.and_then(|x| Some(y))` for `Option`s + fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if !match_type(cx, cx.tables.expr_ty(&args[0]), Self::TYPE_QPATH) { + return; + } + + match args[1].kind { + hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + Self::lint_closure(cx, expr, closure_expr); + } + }, + // `_.and_then(Some)` case, which is no-op. + hir::ExprKind::Path(ref qpath) if match_qpath(qpath, Self::BAD_VARIANT_QPATH) => { + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::no_op_msg().as_ref(), + "use the expression directly", + snippet(cx, args[0].span, "..").into(), + Applicability::MachineApplicable, + ); + }, + _ => {}, + } + } +} + +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &hir::Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } + + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } + } + } + + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + +fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_, '_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir hir::Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } + + impl RetFinder { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); + WithStmtGuarg { + val: self, + prev_in_stmt, + } + } + } + + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val + } + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val + } + } + + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; + } + } + + impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + hir::ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), + hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), + } + } + } + } + + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs new file mode 100644 index 0000000000000..06138ab9783c3 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -0,0 +1,62 @@ +use super::INEFFICIENT_TO_STRING; +use crate::utils::{ + is_type_diagnostic_item, match_def_path, paths, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty_depth, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for the `INEFFICIENT_TO_STRING` lint +pub fn lint<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { + if_chain! { + if let Some(to_string_meth_did) = cx.tables.type_dependent_def_id(expr.hir_id); + if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); + if let Some(substs) = cx.tables.node_substs_opt(expr.hir_id); + let self_ty = substs.type_at(0); + let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty); + if deref_count >= 1; + if specializes_tostring(cx, deref_self_ty); + then { + span_lint_and_then( + cx, + INEFFICIENT_TO_STRING, + expr.span, + &format!("calling `to_string` on `{}`", arg_ty), + |diag| { + diag.help(&format!( + "`{}` implements `ToString` through a slower blanket impl, but `{}` has a fast specialization of `ToString`", + self_ty, deref_self_ty + )); + let mut applicability = Applicability::MachineApplicable; + let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + diag.span_suggestion( + expr.span, + "try dereferencing the receiver", + format!("({}{}).to_string()", "*".repeat(deref_count), arg_snippet), + applicability, + ); + }, + ); + } + } +} + +/// Returns whether `ty` specializes `ToString`. +/// Currently, these are `str`, `String`, and `Cow<'_, str>`. +fn specializes_tostring(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool { + if let ty::Str = ty.kind { + return true; + } + + if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + return true; + } + + if let ty::Adt(adt, substs) = ty.kind { + match_def_path(cx, adt.did, &paths::COW) && substs.type_at(1).is_str() + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs new file mode 100644 index 0000000000000..aaed6d75048c6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -0,0 +1,176 @@ +use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_target::abi::LayoutOf; + +pub fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) { + let unwrap_arg = &args[0][1]; + let arith_lhs = &args[1][0]; + let arith_rhs = &args[1][1]; + + let ty = cx.tables.expr_ty(arith_lhs); + if !ty.is_integral() { + return; + } + + let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) { + mm + } else { + return; + }; + + if ty.is_signed() { + use self::{ + MinMax::{Max, Min}, + Sign::{Neg, Pos}, + }; + + let sign = if let Some(sign) = lit_sign(arith_rhs) { + sign + } else { + return; + }; + + match (arith, sign, mm) { + ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (), + // "mul" is omitted because lhs can be negative. + _ => return, + } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + super::MANUAL_SATURATING_ARITHMETIC, + expr.span, + "manual saturating arithmetic", + &format!("try using `saturating_{}`", arith), + format!( + "{}.saturating_{}({})", + snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), + arith, + snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), + ), + applicability, + ); + } else { + match (mm, arith) { + (MinMax::Max, "add") | (MinMax::Max, "mul") | (MinMax::Min, "sub") => (), + _ => return, + } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + super::MANUAL_SATURATING_ARITHMETIC, + expr.span, + "manual saturating arithmetic", + &format!("try using `saturating_{}`", arith), + format!( + "{}.saturating_{}({})", + snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), + arith, + snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), + ), + applicability, + ); + } +} + +#[derive(PartialEq, Eq)] +enum MinMax { + Min, + Max, +} + +fn is_min_or_max<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &hir::Expr<'_>) -> Option { + // `T::max_value()` `T::min_value()` inherent methods + if_chain! { + if let hir::ExprKind::Call(func, args) = &expr.kind; + if args.is_empty(); + if let hir::ExprKind::Path(path) = &func.kind; + if let hir::QPath::TypeRelative(_, segment) = path; + then { + match &*segment.ident.as_str() { + "max_value" => return Some(MinMax::Max), + "min_value" => return Some(MinMax::Min), + _ => {} + } + } + } + + let ty = cx.tables.expr_ty(expr); + let ty_str = ty.to_string(); + + // `std::T::MAX` `std::T::MIN` constants + if let hir::ExprKind::Path(path) = &expr.kind { + if match_qpath(path, &["core", &ty_str, "MAX"][..]) { + return Some(MinMax::Max); + } + + if match_qpath(path, &["core", &ty_str, "MIN"][..]) { + return Some(MinMax::Min); + } + } + + // Literals + let bits = cx.layout_of(ty).unwrap().size.bits(); + let (minval, maxval): (u128, u128) = if ty.is_signed() { + let minval = 1 << (bits - 1); + let mut maxval = !(1 << (bits - 1)); + if bits != 128 { + maxval &= (1 << bits) - 1; + } + (minval, maxval) + } else { + (0, if bits == 128 { !0 } else { (1 << bits) - 1 }) + }; + + 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); + } + } + } + + None + }; + + if let r @ Some(_) = check_lit(expr, !ty.is_signed()) { + return r; + } + + if ty.is_signed() { + if let hir::ExprKind::Unary(hir::UnOp::UnNeg, val) = &expr.kind { + return check_lit(val, true); + } + } + + None +} + +#[derive(PartialEq, Eq)] +enum Sign { + Pos, + Neg, +} + +fn lit_sign(expr: &hir::Expr<'_>) -> Option { + if let hir::ExprKind::Unary(hir::UnOp::UnNeg, inner) = &expr.kind { + if let hir::ExprKind::Lit(..) = &inner.kind { + return Some(Sign::Neg); + } + } else if let hir::ExprKind::Lit(..) = &expr.kind { + return Some(Sign::Pos); + } + + None +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs new file mode 100644 index 0000000000000..626427c15ecf5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -0,0 +1,3534 @@ +mod bind_instead_of_map; +mod inefficient_to_string; +mod manual_saturating_arithmetic; +mod option_map_unwrap_or; +mod unnecessary_filter_map; + +use std::borrow::Cow; +use std::fmt; +use std::iter; + +use bind_instead_of_map::BindInsteadOfMap; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Predicate, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::{sym, SymbolStr}; + +use crate::consts::{constant, Constant}; +use crate::utils::usage::mutated_variables; +use crate::utils::{ + get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy, + is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, + match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, + remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. + /// + /// **Why is this bad?** It is better to handle the `None` or `Err` case, + /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of + /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is + /// `Allow` by default. + /// + /// `result.unwrap()` will let the thread panic on `Err` values. + /// Normally, you want to implement more sophisticated error handling, + /// and propagate errors upwards with `?` operator. + /// + /// Even if you want to panic on errors, not all `Error`s implement good + /// messages on display. Therefore, it may be beneficial to look at the places + /// where they may get displayed. Activate this lint to do just that. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// # let opt = Some(1); + /// + /// // Bad + /// opt.unwrap(); + /// + /// // Good + /// opt.expect("more helpful message"); + /// ``` + /// + /// // or + /// + /// ```rust + /// # let res: Result = Ok(1); + /// + /// // Bad + /// res.unwrap(); + /// + /// // Good + /// res.expect("more helpful message"); + /// ``` + pub UNWRAP_USED, + restriction, + "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s. + /// + /// **Why is this bad?** Usually it is better to handle the `None` or `Err` case. + /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why + /// this lint is `Allow` by default. + /// + /// `result.expect()` will let the thread panic on `Err` + /// values. Normally, you want to implement more sophisticated error handling, + /// and propagate errors upwards with `?` operator. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust,ignore + /// # let opt = Some(1); + /// + /// // Bad + /// opt.expect("one"); + /// + /// // Good + /// let opt = Some(1); + /// opt?; + /// ``` + /// + /// // or + /// + /// ```rust + /// # let res: Result = Ok(1); + /// + /// // Bad + /// res.expect("one"); + /// + /// // Good + /// res?; + /// # Ok::<(), ()>(()) + /// ``` + pub EXPECT_USED, + restriction, + "using `.expect()` on `Result` or `Option`, which might be better handled" +} + +declare_clippy_lint! { + /// **What it does:** Checks for methods that should live in a trait + /// implementation of a `std` trait (see [llogiq's blog + /// post](http://llogiq.github.io/2015/07/30/traits.html) for further + /// information) instead of an inherent implementation. + /// + /// **Why is this bad?** Implementing the traits improve ergonomics for users of + /// the code, often with very little cost. Also people seeing a `mul(...)` + /// method + /// may expect `*` to work equally, so you should have good reason to disappoint + /// them. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// struct X; + /// impl X { + /// fn add(&self, other: &X) -> X { + /// // .. + /// # X + /// } + /// } + /// ``` + pub SHOULD_IMPLEMENT_TRAIT, + style, + "defining a method that should be implementing a std trait" +} + +declare_clippy_lint! { + /// **What it does:** Checks for methods with certain name prefixes and which + /// doesn't match how self is taken. The actual rules are: + /// + /// |Prefix |`self` taken | + /// |-------|----------------------| + /// |`as_` |`&self` or `&mut self`| + /// |`from_`| none | + /// |`into_`|`self` | + /// |`is_` |`&self` or none | + /// |`to_` |`&self` | + /// + /// **Why is this bad?** Consistency breeds readability. If you follow the + /// conventions, your users won't be surprised that they, e.g., need to supply a + /// mutable reference to a `as_..` function. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct X; + /// impl X { + /// fn as_str(self) -> &'static str { + /// // .. + /// # "" + /// } + /// } + /// ``` + pub WRONG_SELF_CONVENTION, + style, + "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention" +} + +declare_clippy_lint! { + /// **What it does:** This is the same as + /// [`wrong_self_convention`](#wrong_self_convention), but for public items. + /// + /// **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention). + /// + /// **Known problems:** Actually *renaming* the function may break clients if + /// the function is part of the public interface. In that case, be mindful of + /// the stability guarantees you've given your users. + /// + /// **Example:** + /// ```rust + /// # struct X; + /// impl<'a> X { + /// pub fn as_str(self) -> &'a str { + /// "foo" + /// } + /// } + /// ``` + pub WRONG_PUB_SELF_CONVENTION, + restriction, + "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `ok().expect(..)`. + /// + /// **Why is this bad?** 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:** + /// ```rust + /// # let x = Ok::<_, ()>(()); + /// x.ok().expect("why did I do this again?") + /// ``` + pub OK_EXPECT, + style, + "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or + /// `result.map(_).unwrap_or_else(_)`. + /// + /// **Why is this bad?** Readability, these can be written more concisely (resp.) as + /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`. + /// + /// **Known problems:** The order of the arguments is not in execution order + /// + /// **Examples:** + /// ```rust + /// # let x = Some(1); + /// + /// // Bad + /// x.map(|a| a + 1).unwrap_or(0); + /// + /// // Good + /// x.map_or(0, |a| a + 1); + /// ``` + /// + /// // or + /// + /// ```rust + /// # let x: Result = Ok(1); + /// # fn some_function(foo: ()) -> usize { 1 } + /// + /// // Bad + /// x.map(|a| a + 1).unwrap_or_else(some_function); + /// + /// // Good + /// x.map_or_else(some_function, |a| a + 1); + /// ``` + pub MAP_UNWRAP_OR, + pedantic, + "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.map_or(None, _)`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.and_then(_)`. + /// + /// **Known problems:** The order of the arguments is not in execution order. + /// + /// **Example:** + /// ```rust + /// # let opt = Some(1); + /// opt.map_or(None, |a| Some(a + 1)) + /// # ; + /// ``` + pub OPTION_MAP_OR_NONE, + style, + "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.map_or(None, Some)`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.ok()`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// Bad: + /// ```rust + /// # let r: Result = Ok(1); + /// assert_eq!(Some(1), r.map_or(None, Some)); + /// ``` + /// + /// Good: + /// ```rust + /// # let r: Result = Ok(1); + /// assert_eq!(Some(1), r.ok()); + /// ``` + pub RESULT_MAP_OR_INTO_OPTION, + style, + "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or + /// `_.or_else(|x| Err(y))`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.map(|x| y)` or `_.map_err(|x| y)`. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().and_then(|s| Some(s.len())); + /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) }); + /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) }); + /// ``` + /// + /// The correct use would be: + /// + /// ```rust + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().map(|s| s.len()); + /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 }); + /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 }); + /// ``` + pub BIND_INSTEAD_OF_MAP, + complexity, + "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.filter(_).next()`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find(_)`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let vec = vec![1]; + /// vec.iter().filter(|x| **x == 0).next(); + /// ``` + /// Could be written as + /// ```rust + /// # let vec = vec![1]; + /// vec.iter().find(|x| **x == 0); + /// ``` + pub FILTER_NEXT, + complexity, + "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.skip_while(condition).next()`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find(!condition)`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let vec = vec![1]; + /// vec.iter().skip_while(|x| **x == 0).next(); + /// ``` + /// Could be written as + /// ```rust + /// # let vec = vec![1]; + /// vec.iter().find(|x| **x != 0); + /// ``` + pub SKIP_WHILE_NEXT, + complexity, + "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.map(_).flatten(_)`, + /// + /// **Why is this bad?** Readability, this can be written more concisely as a + /// single method call. + /// + /// **Known problems:** + /// + /// **Example:** + /// ```rust + /// let vec = vec![vec![1]]; + /// vec.iter().map(|x| x.iter()).flatten(); + /// ``` + pub MAP_FLATTEN, + pedantic, + "using combinations of `flatten` and `map` which can usually be written as a single method call" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.filter(_).map(_)`, + /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar. + /// + /// **Why is this bad?** Readability, this can be written more concisely as a + /// single method call. + /// + /// **Known problems:** Often requires a condition + Option/Iterator creation + /// inside the closure. + /// + /// **Example:** + /// ```rust + /// let vec = vec![1]; + /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); + /// ``` + pub FILTER_MAP, + pedantic, + "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.filter_map(_).next()`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as a + /// single method call. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next(); + /// ``` + /// Can be written as + /// + /// ```rust + /// (0..3).find_map(|x| if x == 2 { Some(x) } else { None }); + /// ``` + pub FILTER_MAP_NEXT, + pedantic, + "using combination of `filter_map` and `next` which can usually be written as a single method call" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `flat_map(|x| x)`. + /// + /// **Why is this bad?** Readability, this can be written more concisely by using `flatten`. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// # let iter = vec![vec![0]].into_iter(); + /// iter.flat_map(|x| x); + /// ``` + /// Can be written as + /// ```rust + /// # let iter = vec![vec![0]].into_iter(); + /// iter.flatten(); + /// ``` + pub FLAT_MAP_IDENTITY, + complexity, + "call to `flat_map` where `flatten` is sufficient" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.find(_).map(_)`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as a + /// single method call. + /// + /// **Known problems:** Often requires a condition + Option/Iterator creation + /// inside the closure. + /// + /// **Example:** + /// ```rust + /// (0..3).find(|x| *x == 2).map(|x| x * 2); + /// ``` + /// Can be written as + /// ```rust + /// (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None }); + /// ``` + pub FIND_MAP, + pedantic, + "using a combination of `find` and `map` can usually be written as a single method call" +} + +declare_clippy_lint! { + /// **What it does:** Checks for an iterator search (such as `find()`, + /// `position()`, or `rposition()`) followed by a call to `is_some()`. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.any(_)`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let vec = vec![1]; + /// vec.iter().find(|x| **x == 0).is_some(); + /// ``` + /// Could be written as + /// ```rust + /// # let vec = vec![1]; + /// vec.iter().any(|x| *x == 0); + /// ``` + pub SEARCH_IS_SOME, + complexity, + "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.chars().next()` on a `str` to check + /// if it starts with a given char. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.starts_with(_)`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let name = "foo"; + /// if name.chars().next() == Some('_') {}; + /// ``` + /// Could be written as + /// ```rust + /// let name = "foo"; + /// if name.starts_with('_') {}; + /// ``` + pub CHARS_NEXT_CMP, + style, + "using `.chars().next()` to check if a string starts with a char" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, + /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or + /// `unwrap_or_default` instead. + /// + /// **Why is this bad?** The function will always be called and potentially + /// allocate an object acting as the default. + /// + /// **Known problems:** If the function has side-effects, not calling it will + /// change the semantic of the program, but you shouldn't rely on that anyway. + /// + /// **Example:** + /// ```rust + /// # let foo = Some(String::new()); + /// foo.unwrap_or(String::new()); + /// ``` + /// this can instead be written: + /// ```rust + /// # let foo = Some(String::new()); + /// foo.unwrap_or_else(String::new); + /// ``` + /// or + /// ```rust + /// # let foo = Some(String::new()); + /// foo.unwrap_or_default(); + /// ``` + pub OR_FUN_CALL, + perf, + "using any `*or` method with a function call, which suggests `*or_else`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, + /// etc., and suggests to use `unwrap_or_else` instead + /// + /// **Why is this bad?** The function will always be called. + /// + /// **Known problems:** If the function has side-effects, not calling it will + /// change the semantics of the program, but you shouldn't rely on that anyway. + /// + /// **Example:** + /// ```rust + /// # let foo = Some(String::new()); + /// # let err_code = "418"; + /// # let err_msg = "I'm a teapot"; + /// foo.expect(&format!("Err {}: {}", err_code, err_msg)); + /// ``` + /// or + /// ```rust + /// # let foo = Some(String::new()); + /// # let err_code = "418"; + /// # let err_msg = "I'm a teapot"; + /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); + /// ``` + /// this can instead be written: + /// ```rust + /// # let foo = Some(String::new()); + /// # let err_code = "418"; + /// # let err_msg = "I'm a teapot"; + /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg)); + /// ``` + pub EXPECT_FUN_CALL, + perf, + "using any `expect` method with a function call" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.clone()` on a `Copy` type. + /// + /// **Why is this bad?** The only reason `Copy` types implement `Clone` is for + /// generics, not for using the `clone` method on a concrete type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// 42u64.clone(); + /// ``` + pub CLONE_ON_COPY, + complexity, + "using `clone` on a `Copy` type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, + /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified + /// function syntax instead (e.g., `Rc::clone(foo)`). + /// + /// **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak + /// can obscure the fact that only the pointer is being cloned, not the underlying + /// data. + /// + /// **Example:** + /// ```rust + /// # use std::rc::Rc; + /// let x = Rc::new(1); + /// x.clone(); + /// ``` + pub CLONE_ON_REF_PTR, + restriction, + "using 'clone' on a ref-counted pointer" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.clone()` on an `&&T`. + /// + /// **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of + /// cloning the underlying `T`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn main() { + /// let x = vec![1]; + /// let y = &&x; + /// let z = y.clone(); + /// println!("{:p} {:p}", *y, z); // prints out the same pointer + /// } + /// ``` + pub CLONE_DOUBLE_REF, + correctness, + "using `clone` on `&&T`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.to_string()` on an `&&T` where + /// `T` implements `ToString` directly (like `&&str` or `&&String`). + /// + /// **Why is this bad?** This bypasses the specialized implementation of + /// `ToString` and instead goes through the more expensive string formatting + /// facilities. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Generic implementation for `T: Display` is used (slow) + /// ["foo", "bar"].iter().map(|s| s.to_string()); + /// + /// // OK, the specialized impl is used + /// ["foo", "bar"].iter().map(|&s| s.to_string()); + /// ``` + pub INEFFICIENT_TO_STRING, + pedantic, + "using `to_string` on `&&T` where `T: ToString`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `new` not returning a type that contains `Self`. + /// + /// **Why is this bad?** As a convention, `new` methods are used to make a new + /// instance of a type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct Foo; + /// # struct NotAFoo; + /// impl Foo { + /// fn new() -> NotAFoo { + /// # NotAFoo + /// } + /// } + /// ``` + /// + /// ```rust + /// # struct Foo; + /// # struct FooError; + /// impl Foo { + /// // Good. Return type contains `Self` + /// fn new() -> Result { + /// # Ok(Foo) + /// } + /// } + /// ``` + /// + /// ```rust + /// # struct Foo; + /// struct Bar(Foo); + /// impl Foo { + /// // Bad. The type name must contain `Self`. + /// fn new() -> Bar { + /// # Bar(Foo) + /// } + /// } + /// ``` + pub NEW_RET_NO_SELF, + style, + "not returning type containing `Self` in a `new` method" +} + +declare_clippy_lint! { + /// **What it does:** Checks for string methods that receive a single-character + /// `str` as an argument, e.g., `_.split("x")`. + /// + /// **Why is this bad?** Performing these methods using a `char` is faster than + /// using a `str`. + /// + /// **Known problems:** Does not catch multi-byte unicode characters. + /// + /// **Example:** + /// `_.split("x")` could be `_.split('x')` + pub SINGLE_CHAR_PATTERN, + perf, + "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for getting the inner pointer of a temporary + /// `CString`. + /// + /// **Why is this bad?** The inner pointer of a `CString` is only valid as long + /// as the `CString` is alive. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::ffi::CString; + /// # fn call_some_ffi_func(_: *const i8) {} + /// # + /// let c_str = CString::new("foo").unwrap().as_ptr(); + /// unsafe { + /// call_some_ffi_func(c_str); + /// } + /// ``` + /// Here `c_str` point to a freed address. The correct use would be: + /// ```rust + /// # use std::ffi::CString; + /// # fn call_some_ffi_func(_: *const i8) {} + /// # + /// let c_str = CString::new("foo").unwrap(); + /// unsafe { + /// call_some_ffi_func(c_str.as_ptr()); + /// } + /// ``` + pub TEMPORARY_CSTRING_AS_PTR, + correctness, + "getting the inner pointer of a temporary `CString`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calling `.step_by(0)` on iterators which panics. + /// + /// **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you + /// actually intend to panic. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,should_panic + /// for x in (0..100).step_by(0) { + /// //.. + /// } + /// ``` + pub ITERATOR_STEP_BY_ZERO, + correctness, + "using `Iterator::step_by(0)`, which will panic at runtime" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the use of `iter.nth(0)`. + /// + /// **Why is this bad?** `iter.next()` is equivalent to + /// `iter.nth(0)`, as they both consume the next element, + /// but is more readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// # use std::collections::HashSet; + /// // Bad + /// # let mut s = HashSet::new(); + /// # s.insert(1); + /// let x = s.iter().nth(0); + /// + /// // Good + /// # let mut s = HashSet::new(); + /// # s.insert(1); + /// let x = s.iter().next(); + /// ``` + pub ITER_NTH_ZERO, + style, + "replace `iter.nth(0)` with `iter.next()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `.iter().nth()` (and the related + /// `.iter_mut().nth()`) on standard library types with O(1) element access. + /// + /// **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more + /// readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let some_vec = vec![0, 1, 2, 3]; + /// let bad_vec = some_vec.iter().nth(3); + /// let bad_slice = &some_vec[..].iter().nth(3); + /// ``` + /// The correct use would be: + /// ```rust + /// let some_vec = vec![0, 1, 2, 3]; + /// let bad_vec = some_vec.get(3); + /// let bad_slice = &some_vec[..].get(3); + /// ``` + pub ITER_NTH, + perf, + "using `.iter().nth()` on a standard library type with O(1) element access" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `.skip(x).next()` on iterators. + /// + /// **Why is this bad?** `.nth(x)` is cleaner + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let some_vec = vec![0, 1, 2, 3]; + /// let bad_vec = some_vec.iter().skip(3).next(); + /// let bad_slice = &some_vec[..].iter().skip(3).next(); + /// ``` + /// The correct use would be: + /// ```rust + /// let some_vec = vec![0, 1, 2, 3]; + /// let bad_vec = some_vec.iter().nth(3); + /// let bad_slice = &some_vec[..].iter().nth(3); + /// ``` + pub ITER_SKIP_NEXT, + style, + "using `.skip(x).next()` on an iterator" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `.get().unwrap()` (or + /// `.get_mut().unwrap`) on a standard library type which implements `Index` + /// + /// **Why is this bad?** Using the Index trait (`[]`) is more clear and more + /// concise. + /// + /// **Known problems:** Not a replacement for error handling: Using either + /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic` + /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a + /// temporary placeholder for dealing with the `Option` type, then this does + /// not mitigate the need for error handling. If there is a chance that `.get()` + /// will be `None` in your program, then it is advisable that the `None` case + /// is handled in a future refactor instead of using `.unwrap()` or the Index + /// trait. + /// + /// **Example:** + /// ```rust + /// let mut some_vec = vec![0, 1, 2, 3]; + /// let last = some_vec.get(3).unwrap(); + /// *some_vec.get_mut(0).unwrap() = 1; + /// ``` + /// The correct use would be: + /// ```rust + /// let mut some_vec = vec![0, 1, 2, 3]; + /// let last = some_vec[3]; + /// some_vec[0] = 1; + /// ``` + pub GET_UNWRAP, + restriction, + "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the use of `.extend(s.chars())` where s is a + /// `&str` or `String`. + /// + /// **Why is this bad?** `.push_str(s)` is clearer + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let abc = "abc"; + /// let def = String::from("def"); + /// let mut s = String::new(); + /// s.extend(abc.chars()); + /// s.extend(def.chars()); + /// ``` + /// The correct use would be: + /// ```rust + /// let abc = "abc"; + /// let def = String::from("def"); + /// let mut s = String::new(); + /// s.push_str(abc); + /// s.push_str(&def); + /// ``` + pub STRING_EXTEND_CHARS, + style, + "using `x.extend(s.chars())` where s is a `&str` or `String`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the use of `.cloned().collect()` on slice to + /// create a `Vec`. + /// + /// **Why is this bad?** `.to_vec()` is clearer + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let s = [1, 2, 3, 4, 5]; + /// let s2: Vec = s[..].iter().cloned().collect(); + /// ``` + /// The better use would be: + /// ```rust + /// let s = [1, 2, 3, 4, 5]; + /// let s2: Vec = s.to_vec(); + /// ``` + pub ITER_CLONED_COLLECT, + style, + "using `.cloned().collect()` on slice to create a `Vec`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.chars().last()` or + /// `.chars().next_back()` on a `str` to check if it ends with a given char. + /// + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.ends_with(_)`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let name = "_"; + /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-') + /// # ; + /// ``` + pub CHARS_LAST_CMP, + style, + "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the + /// types before and after the call are the same. + /// + /// **Why is this bad?** The call is unnecessary. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn do_stuff(x: &[i32]) {} + /// let x: &[i32] = &[1, 2, 3, 4, 5]; + /// do_stuff(x.as_ref()); + /// ``` + /// The correct use would be: + /// ```rust + /// # fn do_stuff(x: &[i32]) {} + /// let x: &[i32] = &[1, 2, 3, 4, 5]; + /// do_stuff(x); + /// ``` + pub USELESS_ASREF, + complexity, + "using `as_ref` where the types before and after the call are the same" +} + +declare_clippy_lint! { + /// **What it does:** Checks for using `fold` when a more succinct alternative exists. + /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`, + /// `sum` or `product`. + /// + /// **Why is this bad?** Readability. + /// + /// **Known problems:** False positive in pattern guards. Will be resolved once + /// non-lexical lifetimes are stable. + /// + /// **Example:** + /// ```rust + /// let _ = (0..3).fold(false, |acc, x| acc || x > 2); + /// ``` + /// This could be written as: + /// ```rust + /// let _ = (0..3).any(|x| x > 2); + /// ``` + pub UNNECESSARY_FOLD, + style, + "using `fold` when a more succinct alternative exists" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. + /// More specifically it checks if the closure provided is only performing one of the + /// filter or map operations and suggests the appropriate option. + /// + /// **Why is this bad?** Complexity. The intent is also clearer if only a single + /// operation is being performed. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None }); + /// ``` + /// As there is no transformation of the argument this could be written as: + /// ```rust + /// let _ = (0..3).filter(|&x| x > 2); + /// ``` + /// + /// ```rust + /// let _ = (0..4).filter_map(|x| Some(x + 1)); + /// ``` + /// As there is no conditional check on the argument this could be written as: + /// ```rust + /// let _ = (0..4).map(|x| x + 1); + /// ``` + pub UNNECESSARY_FILTER_MAP, + complexity, + "using `filter_map` when a more succinct alternative exists" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` + /// or `iter_mut`. + /// + /// **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its + /// content into the resulting iterator, which is confusing. It is better just call `iter` or + /// `iter_mut` directly. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// let _ = (&vec![3, 4, 5]).into_iter(); + /// ``` + pub INTO_ITER_ON_REF, + style, + "using `.into_iter()` on a reference" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `map` followed by a `count`. + /// + /// **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`. + /// If the `map` call is intentional, this should be rewritten. Or, if you intend to + /// drive the iterator to completion, you can just use `for_each` instead. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// let _ = (0..3).map(|x| x + 2).count(); + /// ``` + pub SUSPICIOUS_MAP, + complexity, + "suspicious usage of map" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `MaybeUninit::uninit().assume_init()`. + /// + /// **Why is this bad?** For most types, this is undefined behavior. + /// + /// **Known problems:** For now, we accept empty tuples and tuples / arrays + /// of `MaybeUninit`. There may be other types that allow uninitialized + /// data, but those are not yet rigorously defined. + /// + /// **Example:** + /// + /// ```rust + /// // Beware the UB + /// use std::mem::MaybeUninit; + /// + /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + /// ``` + /// + /// Note that the following is OK: + /// + /// ```rust + /// use std::mem::MaybeUninit; + /// + /// let _: [MaybeUninit; 5] = unsafe { + /// MaybeUninit::uninit().assume_init() + /// }; + /// ``` + pub UNINIT_ASSUMED_INIT, + correctness, + "`MaybeUninit::uninit().assume_init()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`. + /// + /// **Why is this bad?** These can be written simply with `saturating_add/sub` methods. + /// + /// **Example:** + /// + /// ```rust + /// # let y: u32 = 0; + /// # let x: u32 = 100; + /// let add = x.checked_add(y).unwrap_or(u32::MAX); + /// let sub = x.checked_sub(y).unwrap_or(u32::MIN); + /// ``` + /// + /// can be written using dedicated methods for saturating addition/subtraction as: + /// + /// ```rust + /// # let y: u32 = 0; + /// # let x: u32 = 100; + /// let add = x.saturating_add(y); + /// let sub = x.saturating_sub(y); + /// ``` + pub MANUAL_SATURATING_ARITHMETIC, + style, + "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to + /// zero-sized types + /// + /// **Why is this bad?** This is a no-op, and likely unintended + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// unsafe { (&() as *const ()).offset(1) }; + /// ``` + pub ZST_OFFSET, + correctness, + "Check for offset calculations on raw pointers to zero-sized types" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `FileType::is_file()`. + /// + /// **Why is this bad?** When people testing a file type with `FileType::is_file` + /// they are testing whether a path is something they can get bytes from. But + /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover + /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention. + /// + /// **Example:** + /// + /// ```rust + /// # || { + /// let metadata = std::fs::metadata("foo.txt")?; + /// let filetype = metadata.file_type(); + /// + /// if filetype.is_file() { + /// // read file + /// } + /// # Ok::<_, std::io::Error>(()) + /// # }; + /// ``` + /// + /// should be written as: + /// + /// ```rust + /// # || { + /// let metadata = std::fs::metadata("foo.txt")?; + /// let filetype = metadata.file_type(); + /// + /// if !filetype.is_dir() { + /// // read file + /// } + /// # Ok::<_, std::io::Error>(()) + /// # }; + /// ``` + pub FILETYPE_IS_FILE, + restriction, + "`FileType::is_file` is not recommended to test for readable file type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). + /// + /// **Why is this bad?** Readability, this can be written more concisely as a + /// single method call. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let opt = Some("".to_string()); + /// opt.as_ref().map(String::as_str) + /// # ; + /// ``` + /// Can be written as + /// ```rust + /// # let opt = Some("".to_string()); + /// opt.as_deref() + /// # ; + /// ``` + pub OPTION_AS_REF_DEREF, + complexity, + "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`" +} + +declare_lint_pass!(Methods => [ + UNWRAP_USED, + EXPECT_USED, + SHOULD_IMPLEMENT_TRAIT, + WRONG_SELF_CONVENTION, + WRONG_PUB_SELF_CONVENTION, + OK_EXPECT, + MAP_UNWRAP_OR, + RESULT_MAP_OR_INTO_OPTION, + OPTION_MAP_OR_NONE, + BIND_INSTEAD_OF_MAP, + OR_FUN_CALL, + EXPECT_FUN_CALL, + CHARS_NEXT_CMP, + CHARS_LAST_CMP, + CLONE_ON_COPY, + CLONE_ON_REF_PTR, + CLONE_DOUBLE_REF, + INEFFICIENT_TO_STRING, + NEW_RET_NO_SELF, + SINGLE_CHAR_PATTERN, + SEARCH_IS_SOME, + TEMPORARY_CSTRING_AS_PTR, + FILTER_NEXT, + SKIP_WHILE_NEXT, + FILTER_MAP, + FILTER_MAP_NEXT, + FLAT_MAP_IDENTITY, + FIND_MAP, + MAP_FLATTEN, + ITERATOR_STEP_BY_ZERO, + ITER_NTH, + ITER_NTH_ZERO, + ITER_SKIP_NEXT, + GET_UNWRAP, + STRING_EXTEND_CHARS, + ITER_CLONED_COLLECT, + USELESS_ASREF, + UNNECESSARY_FOLD, + UNNECESSARY_FILTER_MAP, + INTO_ITER_ON_REF, + SUSPICIOUS_MAP, + UNINIT_ASSUMED_INIT, + MANUAL_SATURATING_ARITHMETIC, + ZST_OFFSET, + FILETYPE_IS_FILE, + OPTION_AS_REF_DEREF, +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { + #[allow(clippy::too_many_lines)] + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if in_macro(expr.span) { + return; + } + + let (method_names, arg_lists, method_spans) = method_calls(expr, 2); + let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); + let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); + + match method_names.as_slice() { + ["unwrap", "get"] => lint_get_unwrap(cx, expr, arg_lists[1], false), + ["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true), + ["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]), + ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]), + ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), + ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), + ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), + ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), + ["and_then", ..] => { + bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); + bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + }, + ["or_else", ..] => { + bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); + }, + ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), + ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), + ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), + ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), + ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), + ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), + ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), + ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), + ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), + ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]), + ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), + ["is_some", "position"] => { + lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1]) + }, + ["is_some", "rposition"] => { + lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) + }, + ["extend", ..] => lint_extend(cx, expr, arg_lists[0]), + ["as_ptr", "unwrap"] | ["as_ptr", "expect"] => { + lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0]) + }, + ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false), + ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), + ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), + ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]), + ["next", "skip"] => lint_iter_skip_next(cx, expr), + ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]), + ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]), + ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]), + ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]), + ["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]), + ["count", "map"] => lint_suspicious_map(cx, expr), + ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr), + ["unwrap_or", arith @ "checked_add"] + | ["unwrap_or", arith @ "checked_sub"] + | ["unwrap_or", arith @ "checked_mul"] => { + manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..]) + }, + ["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => { + check_pointer_offset(cx, expr, arg_lists[0]) + }, + ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), + ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), + ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + _ => {}, + } + + match expr.kind { + hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args) => { + lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); + lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); + + let self_ty = cx.tables.expr_ty_adjusted(&args[0]); + if args.len() == 1 && method_call.ident.name == sym!(clone) { + lint_clone_on_copy(cx, expr, &args[0], self_ty); + lint_clone_on_ref_ptr(cx, expr, &args[0]); + } + if args.len() == 1 && method_call.ident.name == sym!(to_string) { + inefficient_to_string::lint(cx, expr, &args[0], self_ty); + } + + match self_ty.kind { + ty::Ref(_, ty, _) if ty.kind == ty::Str => { + for &(method, pos) in &PATTERN_METHODS { + if method_call.ident.name.as_str() == method && args.len() > pos { + lint_single_char_pattern(cx, expr, &args[pos]); + } + } + }, + ty::Ref(..) if method_call.ident.name == sym!(into_iter) => { + lint_into_iter(cx, expr, self_ty, *method_span); + }, + _ => (), + } + }, + hir::ExprKind::Binary(op, ref lhs, ref rhs) + if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => + { + let mut info = BinaryExprInfo { + expr, + chain: lhs, + other: rhs, + eq: op.node == hir::BinOpKind::Eq, + }; + lint_binary_expr_with_method_call(cx, &mut info); + } + _ => (), + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if in_external_macro(cx.sess(), impl_item.span) { + return; + } + let name = impl_item.ident.name.as_str(); + let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id); + let item = cx.tcx.hir().expect_item(parent); + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + let self_ty = cx.tcx.type_of(def_id); + if_chain! { + if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; + if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); + if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind; + + let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let method_sig = cx.tcx.fn_sig(method_def_id); + let method_sig = cx.tcx.erase_late_bound_regions(&method_sig); + + let first_arg_ty = &method_sig.inputs().iter().next(); + + // check conventions w.r.t. conversion method names and predicates + if let Some(first_arg_ty) = first_arg_ty; + + then { + if cx.access_levels.is_exported(impl_item.hir_id) { + // check missing trait implementations + for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { + if name == method_name && + sig.decl.inputs.len() == n_args && + out_type.matches(cx, &sig.decl.output) && + self_kind.matches(cx, self_ty, first_arg_ty) && + fn_header_equals(*fn_header, sig.header) { + span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( + "defining a method called `{}` on this type; consider implementing \ + the `{}` trait or choosing a less ambiguous name", name, trait_name)); + } + } + } + + if let Some((ref conv, self_kinds)) = &CONVENTIONS + .iter() + .find(|(ref conv, _)| conv.check(&name)) + { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + let lint = if item.vis.node.is_pub() { + WRONG_PUB_SELF_CONVENTION + } else { + WRONG_SELF_CONVENTION + }; + + span_lint( + cx, + lint, + first_arg.pat.span, + &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } + } + } + + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { + let ret_ty = return_ty(cx, impl_item.hir_id); + + let contains_self_ty = |ty: Ty<'tcx>| { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => same_tys(cx, self_ty, inner_ty), + + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + }; + + // walk the return type and check for Self (this does not check associated types) + if contains_self_ty(ret_ty) { + return; + } + + // if return type is impl trait, check the associated types + if let ty::Opaque(def_id, _) = ret_ty.kind { + // one of the associated types must be Self + for predicate in cx.tcx.predicates_of(def_id).predicates { + match predicate { + (Predicate::Projection(poly_projection_predicate), _) => { + let binder = poly_projection_predicate.ty(); + let associated_type = binder.skip_binder(); + + // walk the associated type and check for Self + if contains_self_ty(associated_type) { + return; + } + }, + (_, _) => {}, + } + } + } + + if name == "new" && !same_tys(cx, ret_ty, self_ty) { + span_lint( + cx, + NEW_RET_NO_SELF, + impl_item.span, + "methods called `new` usually return `Self`", + ); + } + } + } +} + +/// Checks for the `OR_FUN_CALL` lint. +#[allow(clippy::too_many_lines)] +fn lint_or_fun_call<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &'tcx [hir::Expr<'_>], +) { + // Searches an expression for method calls or function calls that aren't ctors + struct FunCallFinder<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + found: bool, + } + + impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + let call_found = match &expr.kind { + // ignore enum and struct constructors + hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), + hir::ExprKind::MethodCall(..) => true, + _ => false, + }; + + if call_found { + self.found |= true; + } + + if !self.found { + intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + } + + /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. + fn check_unwrap_or_default( + cx: &LateContext<'_, '_>, + name: &str, + fun: &hir::Expr<'_>, + self_expr: &hir::Expr<'_>, + arg: &hir::Expr<'_>, + or_has_args: bool, + span: Span, + ) -> bool { + if_chain! { + if !or_has_args; + if name == "unwrap_or"; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + let path = &*last_path_segment(qpath).ident.as_str(); + if ["default", "new"].contains(&path); + let arg_ty = cx.tables.expr_ty(arg); + if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); + if implements_trait(cx, arg_ty, default_trait_id, &[]); + + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span, + &format!("use of `{}` followed by a call to `{}`", name, path), + "try this", + format!( + "{}.unwrap_or_default()", + snippet_with_applicability(cx, self_expr.span, "_", &mut applicability) + ), + applicability, + ); + + true + } else { + false + } + } + } + + /// Checks for `*or(foo())`. + #[allow(clippy::too_many_arguments)] + fn check_general_case<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + name: &str, + method_span: Span, + fun_span: Span, + self_expr: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + or_has_args: bool, + span: Span, + ) { + // (path, fn_has_argument, methods, suffix) + let know_types: &[(&[_], _, &[_], _)] = &[ + (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + ]; + + if_chain! { + if know_types.iter().any(|k| k.2.contains(&name)); + + let mut finder = FunCallFinder { cx: &cx, found: false }; + if { finder.visit_expr(&arg); finder.found }; + if !contains_return(&arg); + + let self_ty = cx.tables.expr_ty(self_expr); + + if let Some(&(_, fn_has_arguments, poss, suffix)) = + know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); + + if poss.contains(&name); + + then { + let sugg: Cow<'_, _> = match (fn_has_arguments, !or_has_args) { + (true, _) => format!("|_| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), + (false, false) => format!("|| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), + (false, true) => snippet_with_macro_callsite(cx, fun_span, ".."), + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!("{}_{}({})", name, suffix, sugg), + Applicability::HasPlaceholders, + ); + } + } + } + + if args.len() == 2 { + match args[1].kind { + hir::ExprKind::Call(ref fun, ref or_args) => { + let or_has_args = !or_args.is_empty(); + if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { + check_general_case( + cx, + name, + method_span, + fun.span, + &args[0], + &args[1], + or_has_args, + expr.span, + ); + } + }, + hir::ExprKind::MethodCall(_, span, ref or_args) => check_general_case( + cx, + name, + method_span, + span, + &args[0], + &args[1], + !or_args.is_empty(), + expr.span, + ), + _ => {}, + } + } +} + +/// Checks for the `EXPECT_FUN_CALL` lint. +#[allow(clippy::too_many_lines)] +fn lint_expect_fun_call( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &[hir::Expr<'_>], +) { + // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or + // `&str` + fn get_arg_root<'a>(cx: &LateContext<'_, '_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + let mut arg_root = arg; + loop { + arg_root = match &arg_root.kind { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, + hir::ExprKind::MethodCall(method_name, _, call_args) => { + if call_args.len() == 1 + && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref)) + && { + let arg_type = cx.tables.expr_ty(&call_args[0]); + let base_type = walk_ptrs_ty(arg_type); + base_type.kind == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) + } + { + &call_args[0] + } else { + break; + } + }, + _ => break, + }; + } + arg_root + } + + // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be + // converted to string. + fn requires_to_string(cx: &LateContext<'_, '_>, arg: &hir::Expr<'_>) -> bool { + let arg_ty = cx.tables.expr_ty(arg); + if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) { + return false; + } + if let ty::Ref(_, ty, ..) = arg_ty.kind { + if ty.kind == ty::Str && can_be_static_str(cx, arg) { + return false; + } + }; + true + } + + // Check if an expression could have type `&'static str`, knowing that it + // has type `&str` for some lifetime. + fn can_be_static_str(cx: &LateContext<'_, '_>, arg: &hir::Expr<'_>) -> bool { + match arg.kind { + hir::ExprKind::Lit(_) => true, + hir::ExprKind::Call(fun, _) => { + if let hir::ExprKind::Path(ref p) = fun.kind { + match cx.tables.qpath_res(p, fun.hir_id) { + hir::def::Res::Def(hir::def::DefKind::Fn, def_id) + | hir::def::Res::Def(hir::def::DefKind::AssocFn, def_id) => matches!( + cx.tcx.fn_sig(def_id).output().skip_binder().kind, + ty::Ref(ty::ReStatic, ..) + ), + _ => false, + } + } else { + false + } + }, + hir::ExprKind::MethodCall(..) => cx.tables.type_dependent_def_id(arg.hir_id).map_or(false, |method_id| { + matches!( + cx.tcx.fn_sig(method_id).output().skip_binder().kind, + ty::Ref(ty::ReStatic, ..) + ) + }), + hir::ExprKind::Path(ref p) => match cx.tables.qpath_res(p, arg.hir_id) { + hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true, + _ => false, + }, + _ => false, + } + } + + fn generate_format_arg_snippet( + cx: &LateContext<'_, '_>, + a: &hir::Expr<'_>, + applicability: &mut Applicability, + ) -> Vec { + if_chain! { + if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind; + if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind; + if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind; + + then { + format_arg_expr_tup + .iter() + .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned()) + .collect() + } else { + unreachable!() + } + } + } + + fn is_call(node: &hir::ExprKind<'_>) -> bool { + match node { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => { + is_call(&expr.kind) + }, + hir::ExprKind::Call(..) + | hir::ExprKind::MethodCall(..) + // These variants are debatable or require further examination + | hir::ExprKind::Match(..) + | hir::ExprKind::Block{ .. } => true, + _ => false, + } + } + + if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) { + return; + } + + let receiver_type = cx.tables.expr_ty_adjusted(&args[0]); + let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym!(option_type)) { + "||" + } else if is_type_diagnostic_item(cx, receiver_type, sym!(result_type)) { + "|_|" + } else { + return; + }; + + let arg_root = get_arg_root(cx, &args[1]); + + let span_replace_word = method_span.with_hi(expr.span.hi()); + + let mut applicability = Applicability::MachineApplicable; + + //Special handling for `format!` as arg_root + if_chain! { + if let hir::ExprKind::Block(block, None) = &arg_root.kind; + if block.stmts.len() == 1; + if let hir::StmtKind::Local(local) = &block.stmts[0].kind; + if let Some(arg_root) = &local.init; + if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind; + if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1; + if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind; + then { + let fmt_spec = &format_args[0]; + let fmt_args = &format_args[1]; + + let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()]; + + args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability)); + + let sugg = args.join(", "); + + span_lint_and_sugg( + cx, + EXPECT_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!("unwrap_or_else({} panic!({}))", closure_args, sugg), + applicability, + ); + + return; + } + } + + let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability); + if requires_to_string(cx, arg_root) { + arg_root_snippet.to_mut().push_str(".to_string()"); + } + + span_lint_and_sugg( + cx, + EXPECT_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!("unwrap_or_else({} {{ panic!({}) }})", closure_args, arg_root_snippet), + applicability, + ); +} + +/// Checks for the `CLONE_ON_COPY` lint. +fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { + let ty = cx.tables.expr_ty(expr); + if let ty::Ref(_, inner, _) = arg_ty.kind { + if let ty::Ref(_, innermost, _) = inner.kind { + span_lint_and_then( + cx, + CLONE_DOUBLE_REF, + expr.span, + "using `clone` on a double-reference; \ + this will copy the reference instead of cloning the inner type", + |diag| { + if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { + let mut ty = innermost; + let mut n = 0; + while let ty::Ref(_, inner, _) = ty.kind { + ty = inner; + n += 1; + } + let refs: String = iter::repeat('&').take(n + 1).collect(); + let derefs: String = iter::repeat('*').take(n).collect(); + let explicit = format!("<{}{}>::clone({})", refs, ty, snip); + diag.span_suggestion( + expr.span, + "try dereferencing it", + format!("{}({}{}).clone()", refs, derefs, snip.deref()), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + expr.span, + "or try being explicit if you are sure, that you want to clone a reference", + explicit, + Applicability::MaybeIncorrect, + ); + } + }, + ); + return; // don't report clone_on_copy + } + } + + if is_copy(cx, ty) { + let snip; + if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { + let parent = cx.tcx.hir().get_parent_node(expr.hir_id); + match &cx.tcx.hir().get(parent) { + hir::Node::Expr(parent) => match parent.kind { + // &*x is a nop, &x.clone() is not + hir::ExprKind::AddrOf(..) => return, + // (*x).func() is useless, x.clone().func() can work in case func borrows mutably + hir::ExprKind::MethodCall(_, _, parent_args) if expr.hir_id == parent_args[0].hir_id => return, + + _ => {}, + }, + hir::Node::Stmt(stmt) => { + if let hir::StmtKind::Local(ref loc) = stmt.kind { + if let hir::PatKind::Ref(..) = loc.pat.kind { + // let ref y = *x borrows x, let ref y = x.clone() does not + return; + } + } + }, + _ => {}, + } + + // x.clone() might have dereferenced x, possibly through Deref impls + if cx.tables.expr_ty(arg) == ty { + snip = Some(("try removing the `clone` call", format!("{}", snippet))); + } else { + let deref_count = cx + .tables + .expr_adjustments(arg) + .iter() + .filter(|adj| { + if let ty::adjustment::Adjust::Deref(_) = adj.kind { + true + } else { + false + } + }) + .count(); + let derefs: String = iter::repeat('*').take(deref_count).collect(); + snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); + } + } else { + snip = None; + } + span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| { + if let Some((text, snip)) = snip { + diag.span_suggestion(expr.span, text, snip, Applicability::Unspecified); + } + }); + } +} + +fn lint_clone_on_ref_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(arg)); + + if let ty::Adt(_, subst) = obj_ty.kind { + let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) { + "Rc" + } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) { + "Arc" + } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) { + "Weak" + } else { + return; + }; + + span_lint_and_sugg( + cx, + CLONE_ON_REF_PTR, + expr.span, + "using `.clone()` on a ref-counted pointer", + "try this", + format!( + "{}::<{}>::clone(&{})", + caller_type, + subst.type_at(0), + snippet(cx, arg.span, "_") + ), + Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak + ); + } +} + +fn lint_string_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let arg = &args[1]; + if let Some(arglists) = method_chain_args(arg, &["chars"]) { + let target = &arglists[0][0]; + let self_ty = walk_ptrs_ty(cx.tables.expr_ty(target)); + let ref_str = if self_ty.kind == ty::Str { + "" + } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + "&" + } else { + return; + }; + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + STRING_EXTEND_CHARS, + expr.span, + "calling `.extend(_.chars())`", + "try this", + format!( + "{}.push_str({}{})", + snippet_with_applicability(cx, args[0].span, "_", &mut applicability), + ref_str, + snippet_with_applicability(cx, target.span, "_", &mut applicability) + ), + applicability, + ); + } +} + +fn lint_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); + if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) { + lint_string_extend(cx, expr, args); + } +} + +fn lint_cstring_as_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) { + if_chain! { + let source_type = cx.tables.expr_ty(source); + if let ty::Adt(def, substs) = source_type.kind; + if cx.tcx.is_diagnostic_item(sym!(result_type), def.did); + if match_type(cx, substs.type_at(0), &paths::CSTRING); + then { + span_lint_and_then( + cx, + TEMPORARY_CSTRING_AS_PTR, + expr.span, + "you are getting the inner pointer of a temporary `CString`", + |diag| { + diag.note("that pointer will be invalid outside this expression"); + diag.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime"); + }); + } + } +} + +fn lint_iter_cloned_collect<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &hir::Expr<'_>, + iter_args: &'tcx [hir::Expr<'_>], +) { + if_chain! { + if is_type_diagnostic_item(cx, cx.tables.expr_ty(expr), sym!(vec_type)); + if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])); + if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); + + then { + span_lint_and_sugg( + cx, + ITER_CLONED_COLLECT, + to_replace, + "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ + more readable", + "try", + ".to_vec()".to_string(), + Applicability::MachineApplicable, + ); + } + } +} + +fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { + fn check_fold_with_op( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + fold_args: &[hir::Expr<'_>], + fold_span: Span, + op: hir::BinOpKind, + replacement_method_name: &str, + replacement_has_args: bool, + ) { + if_chain! { + // Extract the body of the closure passed to fold + if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind; + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + // Check if the closure body is of the form `acc some_expr(x)` + if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind; + if bin_op.node == op; + + // Extract the names of the two arguments to the closure + if let Some(first_arg_ident) = get_arg_name(&closure_body.params[0].pat); + if let Some(second_arg_ident) = get_arg_name(&closure_body.params[1].pat); + + if match_var(&*left_expr, first_arg_ident); + if replacement_has_args || match_var(&*right_expr, second_arg_ident); + + then { + let mut applicability = Applicability::MachineApplicable; + let sugg = if replacement_has_args { + format!( + "{replacement}(|{s}| {r})", + replacement = replacement_method_name, + s = second_arg_ident, + r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), + ) + } else { + format!( + "{replacement}()", + replacement = replacement_method_name, + ) + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_FOLD, + fold_span.with_hi(expr.span.hi()), + // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) + "this `.fold` can be written more succinctly using another method", + "try", + sugg, + applicability, + ); + } + } + } + + // Check that this is a call to Iterator::fold rather than just some function called fold + if !match_trait_method(cx, expr, &paths::ITERATOR) { + return; + } + + assert!( + fold_args.len() == 3, + "Expected fold_args to have three entries - the receiver, the initial value and the closure" + ); + + // Check if the first argument to .fold is a suitable literal + if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind { + match lit.node { + ast::LitKind::Bool(false) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true) + }, + ast::LitKind::Bool(true) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true) + }, + ast::LitKind::Int(0, _) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false) + }, + ast::LitKind::Int(1, _) => { + check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false) + }, + _ => (), + } + } +} + +fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &args[1]) { + span_lint( + cx, + ITERATOR_STEP_BY_ZERO, + expr.span, + "Iterator::step_by(0) will panic at runtime", + ); + } + } +} + +fn lint_iter_nth<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &hir::Expr<'_>, + nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]], + is_mut: bool, +) { + let iter_args = nth_and_iter_args[1]; + let mut_str = if is_mut { "_mut" } else { "" }; + let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() { + "slice" + } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), sym!(vec_type)) { + "Vec" + } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), sym!(vecdeque_type)) { + "VecDeque" + } else { + let nth_args = nth_and_iter_args[0]; + lint_iter_nth_zero(cx, expr, &nth_args); + return; // caller is not a type that we want to lint + }; + + span_lint_and_help( + cx, + ITER_NTH, + expr.span, + &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), + None, + &format!("calling `.get{}()` is both faster and more readable", mut_str), + ); +} + +fn lint_iter_nth_zero<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) { + if_chain! { + if match_trait_method(cx, expr, &paths::ITERATOR); + if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &nth_args[1]); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NTH_ZERO, + expr.span, + "called `.nth(0)` on a `std::iter::Iterator`", + "try calling", + format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), + applicability, + ); + } + } +} + +fn lint_get_unwrap<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &hir::Expr<'_>, + get_args: &'tcx [hir::Expr<'_>], + is_mut: bool, +) { + // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`, + // because they do not implement `IndexMut` + let mut applicability = Applicability::MachineApplicable; + let expr_ty = cx.tables.expr_ty(&get_args[0]); + let get_args_str = if get_args.len() > 1 { + snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability) + } else { + return; // not linting on a .get().unwrap() chain or variant + }; + let mut needs_ref; + let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() { + needs_ref = get_args_str.parse::().is_ok(); + "slice" + } else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) { + needs_ref = get_args_str.parse::().is_ok(); + "Vec" + } else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) { + needs_ref = get_args_str.parse::().is_ok(); + "VecDeque" + } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym!(hashmap_type)) { + needs_ref = true; + "HashMap" + } else if !is_mut && match_type(cx, expr_ty, &paths::BTREEMAP) { + needs_ref = true; + "BTreeMap" + } else { + return; // caller is not a type that we want to lint + }; + + let mut span = expr.span; + + // Handle the case where the result is immediately dereferenced + // by not requiring ref and pulling the dereference into the + // suggestion. + if_chain! { + if needs_ref; + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::Unary(hir::UnOp::UnDeref, _) = parent.kind; + then { + needs_ref = false; + span = parent.span; + } + } + + let mut_str = if is_mut { "_mut" } else { "" }; + let borrow_str = if !needs_ref { + "" + } else if is_mut { + "&mut " + } else { + "&" + }; + + span_lint_and_sugg( + cx, + GET_UNWRAP, + span, + &format!( + "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise", + mut_str, caller_type + ), + "try this", + format!( + "{}{}[{}]", + borrow_str, + snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability), + get_args_str + ), + applicability, + ); +} + +fn lint_iter_skip_next(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { + // lint if caller of skip is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + span_lint_and_help( + cx, + ITER_SKIP_NEXT, + expr.span, + "called `skip(x).next()` on an iterator", + None, + "this is more succinctly expressed by calling `nth(x)`", + ); + } +} + +fn derefs_to_slice<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ty: Ty<'tcx>, +) -> Option<&'tcx hir::Expr<'tcx>> { + fn may_slice<'a>(cx: &LateContext<'_, 'a>, ty: Ty<'a>) -> bool { + match ty.kind { + ty::Slice(_) => true, + ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), + ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), + ty::Array(_, size) => { + if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) { + size < 32 + } else { + false + } + }, + ty::Ref(_, inner, _) => may_slice(cx, inner), + _ => false, + } + } + + if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.kind { + if path.ident.name == sym!(iter) && may_slice(cx, cx.tables.expr_ty(&args[0])) { + Some(&args[0]) + } else { + None + } + } else { + match ty.kind { + ty::Slice(_) => Some(expr), + ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), + ty::Ref(_, inner, _) => { + if may_slice(cx, inner) { + Some(expr) + } else { + None + } + }, + _ => None, + } + } +} + +/// lint use of `unwrap()` for `Option`s and `Result`s +fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { + let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&unwrap_args[0])); + + let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { + Some((UNWRAP_USED, "an Option", "None")) + } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { + Some((UNWRAP_USED, "a Result", "Err")) + } else { + None + }; + + if let Some((lint, kind, none_value)) = mess { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("used `unwrap()` on `{}` value", kind,), + None, + &format!( + "if you don't want to handle the `{}` case gracefully, consider \ + using `expect()` to provide a better panic message", + none_value, + ), + ); + } +} + +/// lint use of `expect()` for `Option`s and `Result`s +fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { + let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0])); + + let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { + Some((EXPECT_USED, "an Option", "None")) + } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { + Some((EXPECT_USED, "a Result", "Err")) + } else { + None + }; + + if let Some((lint, kind, none_value)) = mess { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("used `expect()` on `{}` value", kind,), + None, + &format!("if this value is an `{}`, it will panic", none_value,), + ); + } +} + +/// lint use of `ok().expect()` for `Result`s +fn lint_ok_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) { + if_chain! { + // lint if the caller of `ok()` is a `Result` + if is_type_diagnostic_item(cx, cx.tables.expr_ty(&ok_args[0]), sym!(result_type)); + let result_type = cx.tables.expr_ty(&ok_args[0]); + if let Some(error_type) = get_error_type(cx, result_type); + if has_debug_impl(error_type, cx); + + then { + span_lint_and_help( + cx, + OK_EXPECT, + expr.span, + "called `ok().expect()` on a `Result` value", + None, + "you can call `expect()` directly on the `Result`", + ); + } + } +} + +/// lint use of `map().flatten()` for `Iterators` and 'Options' +fn lint_map_flatten<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { + // lint if caller of `.map().flatten()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `map(..).flatten()` on an `Iterator`. \ + This is more succinctly expressed by calling `.flat_map(..)`"; + let self_snippet = snippet(cx, map_args[0].span, ".."); + let func_snippet = snippet(cx, map_args[1].span, ".."); + let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); + span_lint_and_sugg( + cx, + MAP_FLATTEN, + expr.span, + msg, + "try using `flat_map` instead", + hint, + Applicability::MachineApplicable, + ); + } + + // lint if caller of `.map().flatten()` is an Option + if is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type)) { + let msg = "called `map(..).flatten()` on an `Option`. \ + This is more succinctly expressed by calling `.and_then(..)`"; + let self_snippet = snippet(cx, map_args[0].span, ".."); + let func_snippet = snippet(cx, map_args[1].span, ".."); + let hint = format!("{0}.and_then({1})", self_snippet, func_snippet); + span_lint_and_sugg( + cx, + MAP_FLATTEN, + expr.span, + msg, + "try using `and_then` instead", + hint, + Applicability::MachineApplicable, + ); + } +} + +/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +fn lint_map_unwrap_or_else<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + map_args: &'tcx [hir::Expr<'_>], + unwrap_args: &'tcx [hir::Expr<'_>], +) { + // lint if the caller of `map()` is an `Option` + let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(result_type)); + + if is_option || is_result { + // Don't make a suggestion that may fail to compile due to mutably borrowing + // the same variable twice. + let map_mutated_vars = mutated_variables(&map_args[0], cx); + let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); + if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { + if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { + return; + } + } else { + return; + } + + // lint message + let msg = if is_option { + "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ + `map_or_else(g, f)` instead" + } else { + "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ + `.map_or_else(g, f)` instead" + }; + // get snippets for args to map() and unwrap_or_else() + let map_snippet = snippet(cx, map_args[1].span, ".."); + let unwrap_snippet = snippet(cx, unwrap_args[1].span, ".."); + // lint, with note if neither arg is > 1 line and both map() and + // unwrap_or_else() have the same span + let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1; + let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt(); + if same_span && !multiline { + span_lint_and_note( + cx, + MAP_UNWRAP_OR, + expr.span, + msg, + None, + &format!( + "replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`", + map_snippet, unwrap_snippet, + ), + ); + } else if same_span && multiline { + span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); + }; + } +} + +/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s +fn lint_map_or_none<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + map_or_args: &'tcx [hir::Expr<'_>], +) { + let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_or_args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_or_args[0]), sym!(result_type)); + + // There are two variants of this `map_or` lint: + // (1) using `map_or` as an adapter from `Result` to `Option` + // (2) using `map_or` as a combinator instead of `and_then` + // + // (For this lint) we don't care if any other type calls `map_or` + if !is_option && !is_result { + return; + } + + let (lint_name, msg, instead, hint) = { + let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind { + match_qpath(qpath, &paths::OPTION_NONE) + } else { + return; + }; + + if !default_arg_is_none { + // nothing to lint! + return; + } + + let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind { + match_qpath(qpath, &paths::OPTION_SOME) + } else { + false + }; + + if is_option { + let self_snippet = snippet(cx, map_or_args[0].span, ".."); + let func_snippet = snippet(cx, map_or_args[2].span, ".."); + let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \ + `and_then(f)` instead"; + ( + OPTION_MAP_OR_NONE, + msg, + "try using `and_then` instead", + format!("{0}.and_then({1})", self_snippet, func_snippet), + ) + } else if f_arg_is_some { + let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ + `ok()` instead"; + let self_snippet = snippet(cx, map_or_args[0].span, ".."); + ( + RESULT_MAP_OR_INTO_OPTION, + msg, + "try using `ok` instead", + format!("{0}.ok()", self_snippet), + ) + } else { + // nothing to lint! + return; + } + }; + + span_lint_and_sugg( + cx, + lint_name, + expr.span, + msg, + instead, + hint, + Applicability::MachineApplicable, + ); +} + +/// lint use of `filter().next()` for `Iterators` +fn lint_filter_next<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter().next()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find(p)` instead."; + let filter_snippet = snippet(cx, filter_args[1].span, ".."); + if filter_snippet.lines().count() <= 1 { + // add note if not multi-line + span_lint_and_note( + cx, + FILTER_NEXT, + expr.span, + msg, + None, + &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet), + ); + } else { + span_lint(cx, FILTER_NEXT, expr.span, msg); + } + } +} + +/// lint use of `skip_while().next()` for `Iterators` +fn lint_skip_while_next<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + _skip_while_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.skip_while().next()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + span_lint_and_help( + cx, + SKIP_WHILE_NEXT, + expr.span, + "called `skip_while(p).next()` on an `Iterator`", + None, + "this is more succinctly expressed by calling `.find(!p)` instead", + ); + } +} + +/// lint use of `filter().map()` for `Iterators` +fn lint_filter_map<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + _filter_args: &'tcx [hir::Expr<'_>], + _map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter().map()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `filter(p).map(q)` on an `Iterator`"; + let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead"; + span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); + } +} + +/// lint use of `filter_map().next()` for `Iterators` +fn lint_filter_map_next<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_args: &'tcx [hir::Expr<'_>], +) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find_map(p)` instead."; + let filter_snippet = snippet(cx, filter_args[1].span, ".."); + if filter_snippet.lines().count() <= 1 { + span_lint_and_note( + cx, + FILTER_MAP_NEXT, + expr.span, + msg, + None, + &format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet), + ); + } else { + span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); + } + } +} + +/// lint use of `find().map()` for `Iterators` +fn lint_find_map<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + _find_args: &'tcx [hir::Expr<'_>], + map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter().map()` is an Iterator + if match_trait_method(cx, &map_args[0], &paths::ITERATOR) { + let msg = "called `find(p).map(q)` on an `Iterator`"; + let hint = "this is more succinctly expressed by calling `.find_map(..)` instead"; + span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint); + } +} + +/// lint use of `filter_map().map()` for `Iterators` +fn lint_filter_map_map<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + _filter_args: &'tcx [hir::Expr<'_>], + _map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter().map()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `filter_map(p).map(q)` on an `Iterator`"; + let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; + span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); + } +} + +/// lint use of `filter().flat_map()` for `Iterators` +fn lint_filter_flat_map<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + _filter_args: &'tcx [hir::Expr<'_>], + _map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter().flat_map()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `filter(p).flat_map(q)` on an `Iterator`"; + let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ + and filtering by returning `iter::empty()`"; + span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); + } +} + +/// lint use of `filter_map().flat_map()` for `Iterators` +fn lint_filter_map_flat_map<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + _filter_args: &'tcx [hir::Expr<'_>], + _map_args: &'tcx [hir::Expr<'_>], +) { + // lint if caller of `.filter_map().flat_map()` is an Iterator + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`"; + let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ + and filtering by returning `iter::empty()`"; + span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); + } +} + +/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient +fn lint_flat_map_identity<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + flat_map_args: &'tcx [hir::Expr<'_>], + flat_map_span: Span, +) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + let arg_node = &flat_map_args[1].kind; + + let apply_lint = |message: &str| { + span_lint_and_sugg( + cx, + FLAT_MAP_IDENTITY, + flat_map_span.with_hi(expr.span.hi()), + message, + "try", + "flatten()".to_string(), + Applicability::MachineApplicable, + ); + }; + + if_chain! { + if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; + let body = cx.tcx.hir().body(*body_id); + + if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; + + if path.segments.len() == 1; + if path.segments[0].ident.as_str() == binding_ident.as_str(); + + then { + apply_lint("called `flat_map(|x| x)` on an `Iterator`"); + } + } + + if_chain! { + if let hir::ExprKind::Path(ref qpath) = arg_node; + + if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); + + then { + apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); + } + } + } +} + +/// lint searching an Iterator followed by `is_some()` +fn lint_search_is_some<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + search_method: &str, + search_args: &'tcx [hir::Expr<'_>], + is_some_args: &'tcx [hir::Expr<'_>], + method_span: Span, +) { + // lint if caller of search is an Iterator + if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { + let msg = format!( + "called `is_some()` after searching an `Iterator` with {}. This is more succinctly \ + expressed by calling `any()`.", + search_method + ); + let search_snippet = snippet(cx, search_args[1].span, ".."); + if search_snippet.lines().count() <= 1 { + // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` + // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` + let any_search_snippet = if_chain! { + if search_method == "find"; + if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind; + let closure_body = cx.tcx.hir().body(body_id); + if let Some(closure_arg) = closure_body.params.get(0); + then { + if let hir::PatKind::Ref(..) = closure_arg.pat.kind { + Some(search_snippet.replacen('&', "", 1)) + } else if let Some(name) = get_arg_name(&closure_arg.pat) { + Some(search_snippet.replace(&format!("*{}", name), &name.as_str())) + } else { + None + } + } else { + None + } + }; + // add note if not multi-line + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "try this", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + } else { + span_lint(cx, SEARCH_IS_SOME, expr.span, &msg); + } + } +} + +/// Used for `lint_binary_expr_with_method_call`. +#[derive(Copy, Clone)] +struct BinaryExprInfo<'a> { + expr: &'a hir::Expr<'a>, + chain: &'a hir::Expr<'a>, + other: &'a hir::Expr<'a>, + eq: bool, +} + +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. +fn lint_binary_expr_with_method_call(cx: &LateContext<'_, '_>, info: &mut BinaryExprInfo<'_>) { + macro_rules! lint_with_both_lhs_and_rhs { + ($func:ident, $cx:expr, $info:ident) => { + if !$func($cx, $info) { + ::std::mem::swap(&mut $info.chain, &mut $info.other); + if $func($cx, $info) { + return; + } + } + }; + } + + lint_with_both_lhs_and_rhs!(lint_chars_next_cmp, cx, info); + lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info); + lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info); + lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info); +} + +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. +fn lint_chars_cmp( + cx: &LateContext<'_, '_>, + info: &BinaryExprInfo<'_>, + chain_methods: &[&str], + lint: &'static Lint, + suggest: &str, +) -> bool { + if_chain! { + if let Some(args) = method_chain_args(info.chain, chain_methods); + if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind; + if arg_char.len() == 1; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + if let Some(segment) = single_segment_path(qpath); + if segment.ident.name == sym!(Some); + then { + let mut applicability = Applicability::MachineApplicable; + let self_ty = walk_ptrs_ty(cx.tables.expr_ty_adjusted(&args[0][0])); + + if self_ty.kind != ty::Str { + return false; + } + + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{}` method", suggest), + "like this", + format!("{}{}.{}({})", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability), + suggest, + snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)), + applicability, + ); + + return true; + } + } + + false +} + +/// Checks for the `CHARS_NEXT_CMP` lint. +fn lint_chars_next_cmp<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool { + lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") +} + +/// Checks for the `CHARS_LAST_CMP` lint. +fn lint_chars_last_cmp<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool { + if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { + true + } else { + lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") + } +} + +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. +fn lint_chars_cmp_with_unwrap<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + info: &BinaryExprInfo<'_>, + chain_methods: &[&str], + lint: &'static Lint, + suggest: &str, +) -> bool { + if_chain! { + if let Some(args) = method_chain_args(info.chain, chain_methods); + if let hir::ExprKind::Lit(ref lit) = info.other.kind; + if let ast::LitKind::Char(c) = lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{}` method", suggest), + "like this", + format!("{}{}.{}('{}')", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability), + suggest, + c), + applicability, + ); + + true + } else { + false + } + } +} + +/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. +fn lint_chars_next_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool { + lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") +} + +/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. +fn lint_chars_last_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool { + if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { + true + } else { + lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") + } +} + +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +fn lint_single_char_pattern<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + _expr: &'tcx hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, +) { + if_chain! { + if let hir::ExprKind::Lit(lit) = &arg.kind; + if let ast::LitKind::Str(r, style) = lit.node; + if r.as_str().len() == 1; + then { + let mut applicability = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let ch = if let ast::StrStyle::Raw(nhash) = style { + let nhash = nhash as usize; + // for raw string: r##"a"## + &snip[(nhash + 2)..(snip.len() - 1 - nhash)] + } else { + // for regular string: "a" + &snip[1..(snip.len() - 1)] + }; + let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } + } +} + +/// Checks for the `USELESS_ASREF` lint. +fn lint_asref(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { + // when we get here, we've already checked that the call name is "as_ref" or "as_mut" + // check if the call is to the actual `AsRef` or `AsMut` trait + if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { + // check if the type after `as_ref` or `as_mut` is the same as before + let recvr = &as_ref_args[0]; + let rcv_ty = cx.tables.expr_ty(recvr); + let res_ty = cx.tables.expr_ty(expr); + let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); + let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); + if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { + // allow the `as_ref` or `as_mut` if it is followed by another method call + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::MethodCall(_, ref span, _) = parent.kind; + if span != &expr.span; + then { + return; + } + } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + USELESS_ASREF, + expr.span, + &format!("this call to `{}` does nothing", call_name), + "try this", + snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(), + applicability, + ); + } + } +} + +fn ty_has_iter_method(cx: &LateContext<'_, '_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> { + has_iter_method(cx, self_ref_ty).map(|ty_name| { + let mutbl = match self_ref_ty.kind { + ty::Ref(_, _, mutbl) => mutbl, + _ => unreachable!(), + }; + let method_name = match mutbl { + hir::Mutability::Not => "iter", + hir::Mutability::Mut => "iter_mut", + }; + (ty_name, method_name) + }) +} + +fn lint_into_iter(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) { + if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) { + return; + } + if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) { + span_lint_and_sugg( + cx, + INTO_ITER_ON_REF, + method_span, + &format!( + "this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`", + method_name, kind, + ), + "call directly", + method_name.to_string(), + Applicability::MachineApplicable, + ); + } +} + +/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) +fn lint_maybe_uninit(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::Call(ref callee, ref args) = expr.kind; + if args.is_empty(); + if let hir::ExprKind::Path(ref path) = callee.kind; + if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); + if !is_maybe_uninit_ty_valid(cx, cx.tables.expr_ty_adjusted(outer)); + then { + span_lint( + cx, + UNINIT_ASSUMED_INIT, + outer.span, + "this call for this type may be undefined behavior" + ); + } + } +} + +fn is_maybe_uninit_ty_valid(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool { + match ty.kind { + ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component), + ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), + ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), + _ => false, + } +} + +fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { + span_lint_and_help( + cx, + SUSPICIOUS_MAP, + expr.span, + "this call to `map()` won't have an effect on the call to `count()`", + None, + "make sure you did not confuse `map` with `filter` or `for_each`", + ); +} + +/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s +fn lint_option_as_ref_deref<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &hir::Expr<'_>, + as_ref_args: &[hir::Expr<'_>], + map_args: &[hir::Expr<'_>], + is_mut: bool, +) { + let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); + + let option_ty = cx.tables.expr_ty(&as_ref_args[0]); + if !is_type_diagnostic_item(cx, option_ty, sym!(option_type)) { + return; + } + + let deref_aliases: [&[&str]; 9] = [ + &paths::DEREF_TRAIT_METHOD, + &paths::DEREF_MUT_TRAIT_METHOD, + &paths::CSTRING_AS_C_STR, + &paths::OS_STRING_AS_OS_STR, + &paths::PATH_BUF_AS_PATH, + &paths::STRING_AS_STR, + &paths::STRING_AS_MUT_STR, + &paths::VEC_AS_SLICE, + &paths::VEC_AS_MUT_SLICE, + ]; + + let is_deref = match map_args[1].kind { + hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)), + hir::ExprKind::Closure(_, _, body_id, _, _) => { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + match &closure_expr.kind { + hir::ExprKind::MethodCall(_, _, args) => { + if_chain! { + if args.len() == 1; + if let hir::ExprKind::Path(qpath) = &args[0].kind; + if let hir::def::Res::Local(local_id) = cx.tables.qpath_res(qpath, args[0].hir_id); + if closure_body.params[0].pat.hir_id == local_id; + let adj = cx.tables.expr_adjustments(&args[0]).iter().map(|x| &x.kind).collect::>(); + if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; + then { + let method_did = cx.tables.type_dependent_def_id(closure_expr.hir_id).unwrap(); + deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + } else { + false + } + } + }, + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => { + if_chain! { + if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner1) = inner.kind; + if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner2) = inner1.kind; + if let hir::ExprKind::Path(ref qpath) = inner2.kind; + if let hir::def::Res::Local(local_id) = cx.tables.qpath_res(qpath, inner2.hir_id); + then { + closure_body.params[0].pat.hir_id == local_id + } else { + false + } + } + }, + _ => false, + } + }, + _ => false, + }; + + if is_deref { + let current_method = if is_mut { + format!(".as_mut().map({})", snippet(cx, map_args[1].span, "..")) + } else { + format!(".as_ref().map({})", snippet(cx, map_args[1].span, "..")) + }; + let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; + let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint); + let suggestion = format!("try using {} instead", method_hint); + + let msg = format!( + "called `{0}` on an Option value. This can be done more directly \ + by calling `{1}` instead", + current_method, hint + ); + span_lint_and_sugg( + cx, + OPTION_AS_REF_DEREF, + expr.span, + &msg, + &suggestion, + hint, + Applicability::MachineApplicable, + ); + } +} + +/// Given a `Result` type, return its error type (`E`). +fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option> { + match ty.kind { + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1), + _ => None, + } +} + +/// This checks whether a given type is known to implement Debug. +fn has_debug_impl<'a, 'b>(ty: Ty<'a>, cx: &LateContext<'b, 'a>) -> bool { + cx.tcx + .get_diagnostic_item(sym::debug_trait) + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) +} + +enum Convention { + Eq(&'static str), + StartsWith(&'static str), +} + +#[rustfmt::skip] +const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [ + (Convention::Eq("new"), &[SelfKind::No]), + (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]), + (Convention::StartsWith("from_"), &[SelfKind::No]), + (Convention::StartsWith("into_"), &[SelfKind::Value]), + (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]), + (Convention::Eq("to_mut"), &[SelfKind::RefMut]), + (Convention::StartsWith("to_"), &[SelfKind::Ref]), +]; + +const FN_HEADER: hir::FnHeader = hir::FnHeader { + unsafety: hir::Unsafety::Normal, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + abi: rustc_target::spec::abi::Abi::Rust, +}; + +#[rustfmt::skip] +const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [ + ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"), + ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"), + ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"), + ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"), + ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"), + ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"), + ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"), + ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), + ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), + ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), + ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), + ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), + ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), + ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"), + ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"), + ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"), + ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"), + ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"), + ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"), + ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"), + ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"), + ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"), + ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"), + ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"), + ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"), + ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"), + ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"), + ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"), + ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"), + ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"), +]; + +#[rustfmt::skip] +const PATTERN_METHODS: [(&str, usize); 17] = [ + ("contains", 1), + ("starts_with", 1), + ("ends_with", 1), + ("find", 1), + ("rfind", 1), + ("split", 1), + ("rsplit", 1), + ("split_terminator", 1), + ("rsplit_terminator", 1), + ("splitn", 2), + ("rsplitn", 2), + ("matches", 1), + ("rmatches", 1), + ("match_indices", 1), + ("rmatch_indices", 1), + ("trim_start_matches", 1), + ("trim_end_matches", 1), +]; + +#[derive(Clone, Copy, PartialEq, Debug)] +enum SelfKind { + Value, + Ref, + RefMut, + No, +} + +impl SelfKind { + fn matches<'a>(self, cx: &LateContext<'_, 'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + fn matches_value<'a>(cx: &LateContext<'_, 'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool { + if ty == parent_ty { + true + } else if ty.is_box() { + ty.boxed_ty() == parent_ty + } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { + if let ty::Adt(_, substs) = ty.kind { + substs.types().next().map_or(false, |t| t == parent_ty) + } else { + false + } + } else { + false + } + } + + fn matches_ref<'a>( + cx: &LateContext<'_, 'a>, + mutability: hir::Mutability, + parent_ty: Ty<'a>, + ty: Ty<'a>, + ) -> bool { + if let ty::Ref(_, t, m) = ty.kind { + return m == mutability && t == parent_ty; + } + + let trait_path = match mutability { + hir::Mutability::Not => &paths::ASREF_TRAIT, + hir::Mutability::Mut => &paths::ASMUT_TRAIT, + }; + + let trait_def_id = match get_trait_def_id(cx, trait_path) { + Some(did) => did, + None => return false, + }; + implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) + } + + match self { + Self::Value => matches_value(cx, parent_ty, ty), + Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), + Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), + Self::No => ty != parent_ty, + } + } + + #[must_use] + fn description(self) -> &'static str { + match self { + Self::Value => "self by value", + Self::Ref => "self by reference", + Self::RefMut => "self by mutable reference", + Self::No => "no self", + } + } +} + +impl Convention { + #[must_use] + fn check(&self, other: &str) -> bool { + match *self { + Self::Eq(this) => this == other, + Self::StartsWith(this) => other.starts_with(this) && this != other, + } + } +} + +impl fmt::Display for Convention { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match *self { + Self::Eq(this) => this.fmt(f), + Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), + } + } +} + +#[derive(Clone, Copy)] +enum OutType { + Unit, + Bool, + Any, + Ref, +} + +impl OutType { + fn matches(self, cx: &LateContext<'_, '_>, ty: &hir::FnRetTy<'_>) -> bool { + let is_unit = |ty: &hir::Ty<'_>| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(&[])); + match (self, ty) { + (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, + (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true, + (Self::Bool, &hir::FnRetTy::Return(ref ty)) if is_bool(ty) => true, + (Self::Any, &hir::FnRetTy::Return(ref ty)) if !is_unit(ty) => true, + (Self::Ref, &hir::FnRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), + _ => false, + } + } +} + +fn is_bool(ty: &hir::Ty<'_>) -> bool { + if let hir::TyKind::Path(ref p) = ty.kind { + match_qpath(p, &["bool"]) + } else { + false + } +} + +// Returns `true` if `expr` contains a return expression +fn contains_return(expr: &hir::Expr<'_>) -> bool { + struct RetCallFinder { + found: bool, + } + + impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + if self.found { + return; + } + if let hir::ExprKind::Ret(..) = &expr.kind { + self.found = true; + } else { + intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + } + + let mut visitor = RetCallFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + +fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if_chain! { + if args.len() == 2; + if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.tables.expr_ty(&args[0]).kind; + if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); + if layout.is_zst(); + then { + span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); + } + } +} + +fn lint_filetype_is_file(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let ty = cx.tables.expr_ty(&args[0]); + + if !match_type(cx, ty, &paths::FILE_TYPE) { + return; + } + + let span: Span; + let verb: &str; + let lint_unary: &str; + let help_unary: &str; + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::Unary(op, _) = parent.kind; + if op == hir::UnOp::UnNot; + then { + lint_unary = "!"; + verb = "denies"; + help_unary = ""; + span = parent.span; + } else { + lint_unary = ""; + verb = "covers"; + help_unary = "!"; + span = expr.span; + } + } + let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); + let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); + span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); +} + +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { + expected.constness == actual.constness + && expected.unsafety == actual.unsafety + && expected.asyncness == actual.asyncness +} diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs new file mode 100644 index 0000000000000..20c60ef33189d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -0,0 +1,135 @@ +use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then}; +use crate::utils::{is_copy, is_type_diagnostic_item}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; +use rustc_hir::{self, HirId, Path}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; + +use super::MAP_UNWRAP_OR; + +/// lint use of `map().unwrap_or()` for `Option`s +pub(super) fn lint<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &rustc_hir::Expr<'_>, + map_args: &'tcx [rustc_hir::Expr<'_>], + unwrap_args: &'tcx [rustc_hir::Expr<'_>], + map_span: Span, +) { + // lint if the caller of `map()` is an `Option` + if is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type)) { + if !is_copy(cx, cx.tables.expr_ty(&unwrap_args[1])) { + // Do not lint if the `map` argument uses identifiers in the `map` + // argument that are also used in the `unwrap_or` argument + + let mut unwrap_visitor = UnwrapVisitor { + cx, + identifiers: FxHashSet::default(), + }; + unwrap_visitor.visit_expr(&unwrap_args[1]); + + let mut map_expr_visitor = MapExprVisitor { + cx, + identifiers: unwrap_visitor.identifiers, + found_identifier: false, + }; + map_expr_visitor.visit_expr(&map_args[1]); + + if map_expr_visitor.found_identifier { + return; + } + } + + if differing_macro_contexts(unwrap_args[1].span, map_span) { + return; + } + + let mut applicability = Applicability::MachineApplicable; + // get snippet for unwrap_or() + let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span, "..", &mut applicability); + // lint message + // comparing the snippet from source to raw text ("None") below is safe + // because we already have checked the type. + let arg = if unwrap_snippet == "None" { "None" } else { "a" }; + let unwrap_snippet_none = unwrap_snippet == "None"; + let suggest = if unwrap_snippet_none { + "and_then(f)" + } else { + "map_or(a, f)" + }; + let msg = &format!( + "called `map(f).unwrap_or({})` on an `Option` value. \ + This can be done more directly by calling `{}` instead", + arg, suggest + ); + + span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { + let map_arg_span = map_args[1].span; + + let mut suggestion = vec![ + ( + map_span, + String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }), + ), + (expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")), + ]; + + if !unwrap_snippet_none { + suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet))); + } + + diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability); + }); + } +} + +struct UnwrapVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + identifiers: FxHashSet, +} + +impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + self.identifiers.insert(ident(path)); + walk_path(self, path); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} + +struct MapExprVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + identifiers: FxHashSet, + found_identifier: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + if self.identifiers.contains(&ident(path)) { + self.found_identifier = true; + return; + } + walk_path(self, path); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} + +fn ident(path: &Path<'_>) -> Symbol { + path.segments + .last() + .expect("segments should be composed of at least 1 element") + .ident + .name +} 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 new file mode 100644 index 0000000000000..41c9ce7cda3e6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -0,0 +1,142 @@ +use crate::utils::paths; +use crate::utils::usage::mutated_variables; +use crate::utils::{match_qpath, match_trait_method, span_lint}; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; + +use if_chain::if_chain; + +use super::UNNECESSARY_FILTER_MAP; + +pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if !match_trait_method(cx, expr, &paths::ITERATOR) { + return; + } + + if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].kind { + let body = cx.tcx.hir().body(body_id); + let arg_id = body.params[0].pat.hir_id; + let mutates_arg = + mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id)); + + let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value); + + let mut return_visitor = ReturnVisitor::new(&cx, arg_id); + return_visitor.visit_expr(&body.value); + found_mapping |= return_visitor.found_mapping; + found_filtering |= return_visitor.found_filtering; + + if !found_filtering { + span_lint( + cx, + UNNECESSARY_FILTER_MAP, + expr.span, + "this `.filter_map` can be written more simply using `.map`", + ); + return; + } + + if !found_mapping && !mutates_arg { + span_lint( + cx, + UNNECESSARY_FILTER_MAP, + expr.span, + "this `.filter_map` can be written more simply using `.filter`", + ); + return; + } + } +} + +// returns (found_mapping, found_filtering) +fn check_expression<'a, 'tcx>( + cx: &'a LateContext<'a, 'tcx>, + arg_id: hir::HirId, + expr: &'tcx hir::Expr<'_>, +) -> (bool, bool) { + match &expr.kind { + hir::ExprKind::Call(ref func, ref args) => { + if_chain! { + if let hir::ExprKind::Path(ref path) = func.kind; + then { + if match_qpath(path, &paths::OPTION_SOME) { + if_chain! { + if let hir::ExprKind::Path(path) = &args[0].kind; + if let Res::Local(ref local) = cx.tables.qpath_res(path, args[0].hir_id); + then { + if arg_id == *local { + return (false, false) + } + } + } + return (true, false); + } else { + // We don't know. It might do anything. + return (true, true); + } + } + } + (true, true) + }, + hir::ExprKind::Block(ref block, _) => { + if let Some(expr) = &block.expr { + check_expression(cx, arg_id, &expr) + } else { + (false, false) + } + }, + hir::ExprKind::Match(_, arms, _) => { + let mut found_mapping = false; + let mut found_filtering = false; + for arm in *arms { + let (m, f) = check_expression(cx, arg_id, &arm.body); + found_mapping |= m; + found_filtering |= f; + } + (found_mapping, found_filtering) + }, + hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true), + _ => (true, true), + } +} + +struct ReturnVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + arg_id: hir::HirId, + // Found a non-None return that isn't Some(input) + found_mapping: bool, + // Found a return that isn't Some + found_filtering: bool, +} + +impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> { + ReturnVisitor { + cx, + arg_id, + found_mapping: false, + found_filtering: false, + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + if let hir::ExprKind::Ret(Some(expr)) = &expr.kind { + let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr); + self.found_mapping |= found_mapping; + self.found_filtering |= found_filtering; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs new file mode 100644 index 0000000000000..b02c993de526b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -0,0 +1,102 @@ +use crate::consts::{constant_simple, Constant}; +use crate::utils::{match_def_path, paths, span_lint}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::cmp::Ordering; + +declare_clippy_lint! { + /// **What it does:** Checks for expressions where `std::cmp::min` and `max` are + /// used to clamp values, but switched so that the result is constant. + /// + /// **Why is this bad?** This is in all probability not the intended outcome. At + /// the least it hurts readability of the code. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```ignore + /// min(0, max(100, x)) + /// ``` + /// It will always be equal to `0`. Probably the author meant to clamp the value + /// between 0 and 100, but has erroneously swapped `min` and `max`. + pub MIN_MAX, + correctness, + "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant" +} + +declare_lint_pass!(MinMaxPass => [MIN_MAX]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) { + if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) { + if outer_max == inner_max { + return; + } + match ( + outer_max, + Constant::partial_cmp(cx.tcx, cx.tables.expr_ty(ie), &outer_c, &inner_c), + ) { + (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (), + _ => { + span_lint( + cx, + MIN_MAX, + expr.span, + "this `min`/`max` combination leads to constant result", + ); + }, + } + } + } + } +} + +#[derive(PartialEq, Eq, Debug)] +enum MinMax { + Min, + Max, +} + +fn min_max<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { + if let ExprKind::Call(ref path, ref args) = expr.kind { + if let ExprKind::Path(ref qpath) = path.kind { + cx.tables.qpath_res(qpath, path.hir_id).opt_def_id().and_then(|def_id| { + if match_def_path(cx, def_id, &paths::CMP_MIN) { + fetch_const(cx, args, MinMax::Min) + } else if match_def_path(cx, def_id, &paths::CMP_MAX) { + fetch_const(cx, args, MinMax::Max) + } else { + None + } + }) + } else { + None + } + } else { + None + } +} + +fn fetch_const<'a>( + cx: &LateContext<'_, '_>, + args: &'a [Expr<'a>], + m: MinMax, +) -> Option<(MinMax, Constant, &'a Expr<'a>)> { + if args.len() != 2 { + return None; + } + if let Some(c) = constant_simple(cx, cx.tables, &args[0]) { + if constant_simple(cx, cx.tables, &args[1]).is_none() { + // otherwise ignore + Some((m, c, &args[1])) + } else { + None + } + } else if let Some(c) = constant_simple(cx, cx.tables, &args[1]) { + Some((m, c, &args[0])) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs new file mode 100644 index 0000000000000..e1d524c2231e4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -0,0 +1,696 @@ +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty, + TyKind, UnOp, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::source_map::{ExpnKind, Span}; + +use crate::consts::{constant, Constant}; +use crate::utils::sugg::Sugg; +use crate::utils::{ + get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, + last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, + span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for function arguments and let bindings denoted as + /// `ref`. + /// + /// **Why is this bad?** The `ref` declaration makes the function take an owned + /// value, but turns the argument into a reference (which means that the value + /// is destroyed when exiting the function). This adds not much value: either + /// take a reference type, or take an owned value and create references in the + /// body. + /// + /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The + /// type of `x` is more obvious with the former. + /// + /// **Known problems:** If the argument is dereferenced within the function, + /// removing the `ref` will lead to errors. This can be fixed by removing the + /// dereferences, e.g., changing `*x` to `x` within the function. + /// + /// **Example:** + /// ```rust + /// fn foo(ref x: u8) -> bool { + /// true + /// } + /// ``` + pub TOPLEVEL_REF_ARG, + style, + "an entire binding declared as `ref`, in a function argument or a `let` statement" +} + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons to NaN. + /// + /// **Why is this bad?** NaN does not compare meaningfully to anything – not + /// even itself – so those comparisons are simply wrong. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = 1.0; + /// + /// if x == f32::NAN { } + /// ``` + pub CMP_NAN, + correctness, + "comparisons to `NAN`, which will always return false, probably not intended" +} + +declare_clippy_lint! { + /// **What it does:** Checks for (in-)equality comparisons on floating-point + /// values (apart from zero), except in functions called `*eq*` (which probably + /// implement equality for a type involving floats). + /// + /// **Why is this bad?** Floating point calculations are usually imprecise, so + /// asking if two values are *exactly* equal is asking for trouble. For a good + /// guide on what to do, see [the floating point + /// guide](http://www.floating-point-gui.de/errors/comparison). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = 1.2331f64; + /// let y = 1.2332f64; + /// if y == 1.23f64 { } + /// if y != x {} // where both are floats + /// ``` + pub FLOAT_CMP, + correctness, + "using `==` or `!=` on float values instead of comparing difference with an epsilon" +} + +declare_clippy_lint! { + /// **What it does:** Checks for conversions to owned values just for the sake + /// of a comparison. + /// + /// **Why is this bad?** The comparison can operate on a reference, so creating + /// an owned value effectively throws it away directly afterwards, which is + /// needlessly consuming code and heap space. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = "foo"; + /// # let y = String::from("foo"); + /// if x.to_owned() == y {} + /// ``` + /// Could be written as + /// ```rust + /// # let x = "foo"; + /// # let y = String::from("foo"); + /// if x == y {} + /// ``` + pub CMP_OWNED, + perf, + "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for getting the remainder of a division by one. + /// + /// **Why is this bad?** The result can only ever be zero. No one will write + /// such code deliberately, unless trying to win an Underhanded Rust + /// Contest. Even for that contest, it's probably a bad idea. Use something more + /// underhanded. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// let a = x % 1; + /// ``` + pub MODULO_ONE, + correctness, + "taking a number modulo 1, which always returns 0" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the use of bindings with a single leading + /// underscore. + /// + /// **Why is this bad?** A single leading underscore is usually used to indicate + /// that a binding will not be used. Using such a binding breaks this + /// expectation. + /// + /// **Known problems:** The lint does not work properly with desugaring and + /// macro, it has been allowed in the mean time. + /// + /// **Example:** + /// ```rust + /// let _x = 0; + /// let y = _x + 1; // Here we are using `_x`, even though it has a leading + /// // underscore. We should rename `_x` to `x` + /// ``` + pub USED_UNDERSCORE_BINDING, + pedantic, + "using a binding which is prefixed with an underscore" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the use of short circuit boolean conditions as + /// a + /// statement. + /// + /// **Why is this bad?** Using a short circuit boolean condition as a statement + /// may hide the fact that the second part is executed or not depending on the + /// outcome of the first part. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// f() && g(); // We should write `if f() { g(); }`. + /// ``` + pub SHORT_CIRCUIT_STATEMENT, + complexity, + "using a short circuit boolean condition as a statement" +} + +declare_clippy_lint! { + /// **What it does:** Catch casts from `0` to some pointer type + /// + /// **Why is this bad?** This generally means `null` and is better expressed as + /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let a = 0 as *const u32; + /// ``` + pub ZERO_PTR, + style, + "using `0 as *{const, mut} T`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for (in-)equality comparisons on floating-point + /// value and constant, except in functions called `*eq*` (which probably + /// implement equality for a type involving floats). + /// + /// **Why is this bad?** Floating point calculations are usually imprecise, so + /// asking if two values are *exactly* equal is asking for trouble. For a good + /// guide on what to do, see [the floating point + /// guide](http://www.floating-point-gui.de/errors/comparison). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x: f64 = 1.0; + /// const ONE: f64 = 1.00; + /// x == ONE; // where both are floats + /// ``` + pub FLOAT_CMP_CONST, + restriction, + "using `==` or `!=` on float constants instead of comparing difference with an epsilon" +} + +declare_lint_pass!(MiscLints => [ + TOPLEVEL_REF_ARG, + CMP_NAN, + FLOAT_CMP, + CMP_OWNED, + MODULO_ONE, + USED_UNDERSCORE_BINDING, + SHORT_CIRCUIT_STATEMENT, + ZERO_PTR, + FLOAT_CMP_CONST +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + k: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: HirId, + ) { + if let FnKind::Closure(_) = k { + // Does not apply to closures + return; + } + for arg in iter_input_pats(decl, body) { + match arg.pat.kind { + PatKind::Binding(BindingAnnotation::Ref, ..) | PatKind::Binding(BindingAnnotation::RefMut, ..) => { + span_lint( + cx, + TOPLEVEL_REF_ARG, + arg.pat.span, + "`ref` directly on a function argument is ignored. Consider using a reference type \ + instead.", + ); + }, + _ => {}, + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let PatKind::Binding(an, .., name, None) = local.pat.kind; + if let Some(ref init) = local.init; + if !higher::is_from_for_desugar(local); + then { + if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut { + let sugg_init = if init.span.from_expansion() { + Sugg::hir_with_macro_callsite(cx, init, "..") + } else { + Sugg::hir(cx, init, "..") + }; + let (mutopt, initref) = if an == BindingAnnotation::RefMut { + ("mut ", sugg_init.mut_addr()) + } else { + ("", sugg_init.addr()) + }; + let tyopt = if let Some(ref ty) = local.ty { + format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_")) + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!( + "let {name}{tyopt} = {initref};", + name=snippet(cx, name.span, "_"), + tyopt=tyopt, + initref=initref, + ), + Applicability::MachineApplicable, + ); + } + ); + } + } + }; + if_chain! { + if let StmtKind::Semi(ref expr) = stmt.kind; + if let ExprKind::Binary(ref binop, ref a, ref b) = expr.kind; + if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; + if let Some(sugg) = Sugg::hir_opt(cx, a); + then { + span_lint_and_then(cx, + SHORT_CIRCUIT_STATEMENT, + stmt.span, + "boolean short circuit operator in statement may be clearer using an explicit test", + |diag| { + let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; + diag.span_suggestion( + stmt.span, + "replace it with", + format!( + "if {} {{ {}; }}", + sugg, + &snippet(cx, b.span, ".."), + ), + Applicability::MachineApplicable, // snippet + ); + }); + } + }; + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + match expr.kind { + ExprKind::Cast(ref e, ref ty) => { + check_cast(cx, expr.span, e, ty); + return; + }, + ExprKind::Binary(ref cmp, ref left, ref right) => { + let op = cmp.node; + if op.is_comparison() { + check_nan(cx, left, expr); + check_nan(cx, right, expr); + check_to_owned(cx, left, right); + check_to_owned(cx, right, left); + } + if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { + if is_allowed(cx, left) || is_allowed(cx, right) { + return; + } + + // Allow comparing the results of signum() + if is_signum(cx, left) && is_signum(cx, right) { + return; + } + + if let Some(name) = get_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; + } + } + let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); + let (lint, msg) = get_lint_and_message( + is_named_constant(cx, left) || is_named_constant(cx, right), + is_comparing_arrays, + ); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let lhs = Sugg::hir(cx, left, ".."); + let rhs = Sugg::hir(cx, right, ".."); + + if !is_comparing_arrays { + diag.span_suggestion( + expr.span, + "consider comparing them within some error", + format!( + "({}).abs() {} error", + lhs - rhs, + if op == BinOpKind::Eq { '<' } else { '>' } + ), + Applicability::HasPlaceholders, // snippet + ); + } + diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`"); + }); + } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) { + span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } + }, + _ => {}, + } + if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { + // Don't lint things expanded by #[derive(...)], etc or `await` desugaring + return; + } + let binding = match expr.kind { + ExprKind::Path(ref qpath) => { + let binding = last_path_segment(qpath).ident.as_str(); + if binding.starts_with('_') && + !binding.starts_with("__") && + binding != "_result" && // FIXME: #944 + is_used(cx, expr) && + // don't lint if the declaration is in a macro + non_macro_local(cx, cx.tables.qpath_res(qpath, expr.hir_id)) + { + Some(binding) + } else { + None + } + }, + ExprKind::Field(_, ident) => { + let name = ident.as_str(); + if name.starts_with('_') && !name.starts_with("__") { + Some(name) + } else { + None + } + }, + _ => None, + }; + if let Some(binding) = binding { + span_lint( + cx, + USED_UNDERSCORE_BINDING, + expr.span, + &format!( + "used binding `{}` which is prefixed with an underscore. A leading \ + underscore signals that a binding will not be used.", + binding + ), + ); + } + } +} + +fn get_lint_and_message( + is_comparing_constants: bool, + is_comparing_arrays: bool, +) -> (&'static rustc_lint::Lint, &'static str) { + if is_comparing_constants { + ( + FLOAT_CMP_CONST, + if is_comparing_arrays { + "strict comparison of `f32` or `f64` constant arrays" + } else { + "strict comparison of `f32` or `f64` constant" + }, + ) + } else { + ( + FLOAT_CMP, + if is_comparing_arrays { + "strict comparison of `f32` or `f64` arrays" + } else { + "strict comparison of `f32` or `f64`" + }, + ) + } +} + +fn check_nan(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) { + if_chain! { + if !in_constant(cx, cmp_expr.hir_id); + if let Some((value, _)) = constant(cx, cx.tables, expr); + then { + let needs_lint = match value { + Constant::F32(num) => num.is_nan(), + Constant::F64(num) => num.is_nan(), + _ => false, + }; + + if needs_lint { + span_lint( + cx, + CMP_NAN, + cmp_expr.span, + "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead", + ); + } + } + } +} + +fn is_named_constant<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> bool { + if let Some((_, res)) = constant(cx, cx.tables, expr) { + res + } else { + false + } +} + +fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> bool { + match constant(cx, cx.tables, expr) { + Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(), + Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(), + Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f { + Constant::F32(f) => *f == 0.0 || (*f).is_infinite(), + Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), + _ => false, + }), + _ => false, + } +} + +// Return true if `expr` is the result of `signum()` invoked on a float value. +fn is_signum(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + // The negation of a signum is still a signum + if let ExprKind::Unary(UnOp::UnNeg, ref child_expr) = expr.kind { + return is_signum(cx, &child_expr); + } + + if_chain! { + if let ExprKind::MethodCall(ref method_name, _, ref expressions) = expr.kind; + if sym!(signum) == method_name.ident.name; + // Check that the receiver of the signum() is a float (expressions[0] is the receiver of + // the method call) + then { + return is_float(cx, &expressions[0]); + } + } + false +} + +fn is_float(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let value = &walk_ptrs_ty(cx.tables.expr_ty(expr)).kind; + + if let ty::Array(arr_ty, _) = value { + return matches!(arr_ty.kind, ty::Float(_)); + }; + + matches!(value, ty::Float(_)) +} + +fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!(&walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Array(_, _)) +} + +fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) { + let (arg_ty, snip) = match expr.kind { + ExprKind::MethodCall(.., ref args) if args.len() == 1 => { + if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) { + (cx.tables.expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, "..")) + } else { + return; + } + }, + ExprKind::Call(ref path, ref v) if v.len() == 1 => { + if let ExprKind::Path(ref path) = path.kind { + if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) { + (cx.tables.expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, "..")) + } else { + return; + } + } else { + return; + } + }, + _ => return, + }; + + let other_ty = cx.tables.expr_ty_adjusted(other); + let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() { + Some(id) => id, + None => return, + }; + + let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| { + implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()]) + }); + let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| { + implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()]) + }); + let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]); + + if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other { + return; + } + + let other_gets_derefed = match other.kind { + ExprKind::Unary(UnOp::UnDeref, _) => true, + _ => false, + }; + + let lint_span = if other_gets_derefed { + expr.span.to(other.span) + } else { + expr.span + }; + + span_lint_and_then( + cx, + CMP_OWNED, + lint_span, + "this creates an owned instance just for comparison", + |diag| { + // This also catches `PartialEq` implementations that call `to_owned`. + if other_gets_derefed { + diag.span_label(lint_span, "try implementing the comparison without allocating"); + return; + } + + let try_hint = if deref_arg_impl_partial_eq_other { + // suggest deref on the left + format!("*{}", snip) + } else { + // suggest dropping the to_owned on the left + snip.to_string() + }; + + diag.span_suggestion( + lint_span, + "try", + try_hint, + Applicability::MachineApplicable, // snippet + ); + }, + ); +} + +/// Heuristic to see if an expression is used. Should be compatible with +/// `unused_variables`'s idea +/// of what it means for an expression to be "used". +fn is_used(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, expr) { + match parent.kind { + ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => { + SpanlessEq::new(cx).eq_expr(rhs, expr) + }, + _ => is_used(cx, parent), + } + } else { + true + } +} + +/// Tests whether an expression is in a macro expansion (e.g., something +/// generated by `#[derive(...)]` or the like). +fn in_attributes_expansion(expr: &Expr<'_>) -> bool { + use rustc_span::hygiene::MacroKind; + if expr.span.from_expansion() { + let data = expr.span.ctxt().outer_expn_data(); + + if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind { + true + } else { + false + } + } else { + false + } +} + +/// Tests whether `res` is a variable defined outside a macro. +fn non_macro_local(cx: &LateContext<'_, '_>, res: def::Res) -> bool { + if let def::Res::Local(id) = res { + !cx.tcx.hir().span(id).from_expansion() + } else { + false + } +} + +fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) { + if_chain! { + if let TyKind::Ptr(ref mut_ty) = ty.kind; + if let ExprKind::Lit(ref lit) = e.kind; + if let LitKind::Int(0, _) = lit.node; + if !in_constant(cx, e.hir_id); + then { + let (msg, sugg_fn) = match mut_ty.mutbl { + Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"), + Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"), + }; + + let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { + (format!("{}()", sugg_fn), Applicability::MachineApplicable) + } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable) + } else { + // `MaybeIncorrect` as type inference may not work with the suggested code + (format!("{}()", sugg_fn), Applicability::MaybeIncorrect) + }; + span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/misc_early.rs b/src/tools/clippy/clippy_lints/src/misc_early.rs new file mode 100644 index 0000000000000..62ee051624b48 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/misc_early.rs @@ -0,0 +1,655 @@ +use crate::utils::{ + constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_ast::ast::{ + BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, + NodeId, Pat, PatKind, StmtKind, UnOp, +}; +use rustc_ast::visit::{walk_expr, FnKind, Visitor}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for structure field patterns bound to wildcards. + /// + /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on + /// the fields that are actually bound. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct Foo { + /// # a: i32, + /// # b: i32, + /// # c: i32, + /// # } + /// let f = Foo { a: 0, b: 0, c: 0 }; + /// + /// // Bad + /// match f { + /// Foo { a: _, b: 0, .. } => {}, + /// Foo { a: _, b: _, c: _ } => {}, + /// } + /// + /// // Good + /// match f { + /// Foo { b: 0, .. } => {}, + /// Foo { .. } => {}, + /// } + /// ``` + pub UNNEEDED_FIELD_PATTERN, + restriction, + "struct fields bound to a wildcard instead of using `..`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for function arguments having the similar names + /// differing by an underscore. + /// + /// **Why is this bad?** It affects code readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo(a: i32, _a: i32) {} + /// ``` + pub DUPLICATE_UNDERSCORE_ARGUMENT, + style, + "function arguments having names which only differ by an underscore" +} + +declare_clippy_lint! { + /// **What it does:** Detects closures called in the same expression where they + /// are defined. + /// + /// **Why is this bad?** It is unnecessarily adding to the expression's + /// complexity. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// (|| 42)() + /// ``` + pub REDUNDANT_CLOSURE_CALL, + complexity, + "throwaway closures called in the expression they are defined" +} + +declare_clippy_lint! { + /// **What it does:** Detects expressions of the form `--x`. + /// + /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was + /// decremented. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let mut x = 3; + /// --x; + /// ``` + pub DOUBLE_NEG, + style, + "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++" +} + +declare_clippy_lint! { + /// **What it does:** Warns on hexadecimal literals with mixed-case letter + /// digits. + /// + /// **Why is this bad?** It looks confusing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let y = 0x1a9BAcD; + /// ``` + pub MIXED_CASE_HEX_LITERALS, + style, + "hex literals whose letter digits are not consistently upper- or lowercased" +} + +declare_clippy_lint! { + /// **What it does:** Warns if literal suffixes are not separated by an + /// underscore. + /// + /// **Why is this bad?** It is much less readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let y = 123832i32; + /// ``` + pub UNSEPARATED_LITERAL_SUFFIX, + pedantic, + "literals whose suffix is not separated by an underscore" +} + +declare_clippy_lint! { + /// **What it does:** Warns if an integral constant literal starts with `0`. + /// + /// **Why is this bad?** In some languages (including the infamous C language + /// and most of its + /// family), this marks an octal constant. In Rust however, this is a decimal + /// constant. This could + /// be confusing for both the writer and a reader of the constant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// In Rust: + /// ```rust + /// fn main() { + /// let a = 0123; + /// println!("{}", a); + /// } + /// ``` + /// + /// prints `123`, while in C: + /// + /// ```c + /// #include + /// + /// int main() { + /// int a = 0123; + /// printf("%d\n", a); + /// } + /// ``` + /// + /// prints `83` (as `83 == 0o123` while `123 == 0o173`). + pub ZERO_PREFIXED_LITERAL, + complexity, + "integer literals starting with `0`" +} + +declare_clippy_lint! { + /// **What it does:** Warns if a generic shadows a built-in type. + /// + /// **Why is this bad?** This gives surprising type errors. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```ignore + /// impl Foo { + /// fn impl_func(&self) -> u32 { + /// 42 + /// } + /// } + /// ``` + pub BUILTIN_TYPE_SHADOW, + style, + "shadowing a builtin type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for patterns in the form `name @ _`. + /// + /// **Why is this bad?** It's almost always more readable to just use direct + /// bindings. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let v = Some("abc"); + /// + /// match v { + /// Some(x) => (), + /// y @ _ => (), // easier written as `y`, + /// } + /// ``` + pub REDUNDANT_PATTERN, + style, + "using `name @ _` in a pattern" +} + +declare_clippy_lint! { + /// **What it does:** Checks for tuple patterns with a wildcard + /// pattern (`_`) is next to a rest pattern (`..`). + /// + /// _NOTE_: While `_, ..` means there is at least one element left, `..` + /// means there are 0 or more elements left. This can make a difference + /// when refactoring, but shouldn't result in errors in the refactored code, + /// since the wildcard pattern isn't used anyway. + /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern + /// can match that element as well. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct TupleStruct(u32, u32, u32); + /// # let t = TupleStruct(1, 2, 3); + /// + /// match t { + /// TupleStruct(0, .., _) => (), + /// _ => (), + /// } + /// ``` + /// can be written as + /// ```rust + /// # struct TupleStruct(u32, u32, u32); + /// # let t = TupleStruct(1, 2, 3); + /// + /// match t { + /// TupleStruct(0, ..) => (), + /// _ => (), + /// } + /// ``` + pub UNNEEDED_WILDCARD_PATTERN, + complexity, + "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)" +} + +declare_lint_pass!(MiscEarlyLints => [ + UNNEEDED_FIELD_PATTERN, + DUPLICATE_UNDERSCORE_ARGUMENT, + REDUNDANT_CLOSURE_CALL, + DOUBLE_NEG, + MIXED_CASE_HEX_LITERALS, + UNSEPARATED_LITERAL_SUFFIX, + ZERO_PREFIXED_LITERAL, + BUILTIN_TYPE_SHADOW, + REDUNDANT_PATTERN, + UNNEEDED_WILDCARD_PATTERN, +]); + +// Used to find `return` statements or equivalents e.g., `?` +struct ReturnVisitor { + found_return: bool, +} + +impl ReturnVisitor { + #[must_use] + fn new() -> Self { + Self { found_return: false } + } +} + +impl<'ast> Visitor<'ast> for ReturnVisitor { + fn visit_expr(&mut self, ex: &'ast Expr) { + if let ExprKind::Ret(_) = ex.kind { + self.found_return = true; + } else if let ExprKind::Try(_) = ex.kind { + self.found_return = true; + } + + walk_expr(self, ex) + } +} + +impl EarlyLintPass for MiscEarlyLints { + fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { + for param in &gen.params { + if let GenericParamKind::Type { .. } = param.kind { + let name = param.ident.as_str(); + if constants::BUILTIN_TYPES.contains(&&*name) { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + &format!("This generic shadows the built-in type `{}`", name), + ); + } + } + } + } + + fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { + let mut wilds = 0; + let type_name = npat + .segments + .last() + .expect("A path must have at least one segment") + .ident + .name; + + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds += 1; + } + } + if !pfields.is_empty() && wilds == pfields.len() { + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + pat.span, + "All the struct fields are matched to a wildcard pattern, consider using `..`.", + None, + &format!("Try with `{} {{ .. }}` instead", type_name), + ); + return; + } + if wilds > 0 { + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds -= 1; + if wilds > 0 { + span_lint( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "You matched a field with a wildcard pattern. Consider using `..` instead", + ); + } else { + let mut normal = vec![]; + + for field in pfields { + match field.pat.kind { + PatKind::Wild => {}, + _ => { + if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { + normal.push(n); + } + }, + } + } + + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "You matched a field with a wildcard pattern. Consider using `..` \ + instead", + None, + &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + ); + } + } + } + } + } + + if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind { + let left_binding = match left { + BindingMode::ByRef(Mutability::Mut) => "ref mut ", + BindingMode::ByRef(Mutability::Not) => "ref ", + _ => "", + }; + + 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!("{}{}", left_binding, ident.name), + Applicability::MachineApplicable, + ); + } + } + + check_unneeded_wildcard_pattern(cx, pat); + } + + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + let mut registered_names: FxHashMap = FxHashMap::default(); + + for arg in &fn_kind.decl().inputs { + if let PatKind::Ident(_, ident, None) = arg.pat.kind { + let arg_name = ident.to_string(); + + if arg_name.starts_with('_') { + if let Some(correspondence) = registered_names.get(&arg_name[1..]) { + span_lint( + cx, + DUPLICATE_UNDERSCORE_ARGUMENT, + *correspondence, + &format!( + "`{}` already exists, having another argument having almost the same \ + name makes code comprehension and documentation more difficult", + arg_name[1..].to_owned() + ), + ); + } + } else { + registered_names.insert(arg_name, arg.pat.span); + } + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + match expr.kind { + ExprKind::Call(ref paren, _) => { + if let ExprKind::Paren(ref closure) = paren.kind { + if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind { + let mut visitor = ReturnVisitor::new(); + visitor.visit_expr(block); + if !visitor.found_return { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_CALL, + expr.span, + "Try not to call a closure in the expression where it is declared.", + |diag| { + if decl.inputs.is_empty() { + let mut app = Applicability::MachineApplicable; + let hint = + snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); + diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); + } + }, + ); + } + } + } + }, + ExprKind::Unary(UnOp::Neg, ref inner) => { + if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { + span_lint( + cx, + DOUBLE_NEG, + expr.span, + "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op", + ); + } + }, + ExprKind::Lit(ref lit) => Self::check_lit(cx, lit), + _ => (), + } + } + + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + for w in block.stmts.windows(2) { + if_chain! { + if let StmtKind::Local(ref local) = w[0].kind; + if let Option::Some(ref t) = local.init; + if let ExprKind::Closure(..) = t.kind; + if let PatKind::Ident(_, ident, _) = local.pat.kind; + if let StmtKind::Semi(ref second) = w[1].kind; + if let ExprKind::Assign(_, ref call, _) = second.kind; + if let ExprKind::Call(ref closure, _) = call.kind; + if let ExprKind::Path(_, ref path) = closure.kind; + then { + if ident == path.segments[0].ident { + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "Closure called just once immediately after it was declared", + ); + } + } + } + } + } +} + +impl MiscEarlyLints { + fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + // We test if first character in snippet is a number, because the snippet could be an expansion + // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. + // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. + // See for a regression. + // FIXME: Find a better way to detect those cases. + let lit_snip = match snippet_opt(cx, lit.span) { + Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, + _ => return, + }; + + if let LitKind::Int(value, lit_int_type) = lit.kind { + let suffix = match lit_int_type { + LitIntType::Signed(ty) => ty.name_str(), + LitIntType::Unsigned(ty) => ty.name_str(), + LitIntType::Unsuffixed => "", + }; + + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; + // Do not lint when literal is unsuffixed. + if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + span_lint_and_sugg( + cx, + UNSEPARATED_LITERAL_SUFFIX, + lit.span, + "integer type suffix should be separated by an underscore", + "add an underscore", + format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + Applicability::MachineApplicable, + ); + } + + if lit_snip.starts_with("0x") { + if maybe_last_sep_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].iter() { + match ch { + b'a'..=b'f' => seen.0 = true, + b'A'..=b'F' => seen.1 = true, + _ => {}, + } + if seen.0 && seen.1 { + span_lint( + cx, + MIXED_CASE_HEX_LITERALS, + lit.span, + "inconsistent casing in hexadecimal literal", + ); + break; + } + } + } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { + /* nothing to do */ + } else if value != 0 && lit_snip.starts_with('0') { + span_lint_and_then( + cx, + ZERO_PREFIXED_LITERAL, + lit.span, + "this is a decimal constant", + |diag| { + diag.span_suggestion( + lit.span, + "if you mean to use a decimal constant, remove the `0` to avoid confusion", + lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { + let suffix = float_ty.name_str(); + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; + if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + span_lint_and_sugg( + cx, + UNSEPARATED_LITERAL_SUFFIX, + lit.span, + "float type suffix should be separated by an underscore", + "add an underscore", + format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { + fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { + span_lint_and_sugg( + cx, + UNNEEDED_WILDCARD_PATTERN, + span, + if only_one { + "this pattern is unneeded as the `..` pattern can match that element" + } else { + "these patterns are unneeded as the `..` pattern can match those elements" + }, + if only_one { "remove it" } else { "remove them" }, + "".to_string(), + Applicability::MachineApplicable, + ); + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_wild>(pat: &&P) -> bool { + if let PatKind::Wild = pat.kind { + true + } else { + false + } + } + + 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(is_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(is_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/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs new file mode 100644 index 0000000000000..4301157e16440 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -0,0 +1,145 @@ +use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; +use rustc_typeck::hir_ty_to_ty; + +declare_clippy_lint! { + /// **What it does:** + /// + /// Suggests the use of `const` in functions and methods where possible. + /// + /// **Why is this bad?** + /// + /// Not having the function const prevents callers of the function from being const as well. + /// + /// **Known problems:** + /// + /// Const functions are currently still being worked on, with some features only being available + /// on nightly. This lint does not consider all edge cases currently and the suggestions may be + /// incorrect if you are using this lint on stable. + /// + /// Also, the lint only runs one pass over the code. Consider these two non-const functions: + /// + /// ```rust + /// fn a() -> i32 { + /// 0 + /// } + /// fn b() -> i32 { + /// a() + /// } + /// ``` + /// + /// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time + /// can't be const as it calls a non-const function. Making `a` const and running Clippy again, + /// will suggest to make `b` const, too. + /// + /// **Example:** + /// + /// ```rust + /// # struct Foo { + /// # random_number: usize, + /// # } + /// # impl Foo { + /// fn new() -> Self { + /// Self { random_number: 42 } + /// } + /// # } + /// ``` + /// + /// Could be a const fn: + /// + /// ```rust + /// # struct Foo { + /// # random_number: usize, + /// # } + /// # impl Foo { + /// const fn new() -> Self { + /// Self { random_number: 42 } + /// } + /// # } + /// ``` + pub MISSING_CONST_FOR_FN, + nursery, + "Lint functions definitions that could be made `const fn`" +} + +declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn { + fn check_fn( + &mut self, + cx: &LateContext<'_, '_>, + kind: FnKind<'_>, + _: &FnDecl<'_>, + _: &Body<'_>, + span: Span, + hir_id: HirId, + ) { + let def_id = cx.tcx.hir().local_def_id(hir_id); + + if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) { + return; + } + + // Building MIR for `fn`s with unsatisfiable preds results in ICE. + if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) { + return; + } + + // Perform some preliminary checks that rule out constness on the Clippy side. This way we + // can skip the actual const check and return early. + match kind { + FnKind::ItemFn(_, generics, header, ..) => { + let has_const_generic_params = generics + .params + .iter() + .any(|param| matches!(param.kind, GenericParamKind::Const{ .. })); + + if already_const(header) || has_const_generic_params { + return; + } + }, + FnKind::Method(_, sig, ..) => { + if trait_ref_of_method(cx, hir_id).is_some() + || already_const(sig.header) + || method_accepts_dropable(cx, sig.decl.inputs) + { + return; + } + }, + _ => return, + } + + let mir = cx.tcx.optimized_mir(def_id); + + if let Err((span, err)) = is_min_const_fn(cx.tcx, def_id.to_def_id(), &mir) { + if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { + cx.tcx.sess.span_err(span, &err); + } + } else { + span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); + } + } +} + +/// Returns true if any of the method parameters is a type that implements `Drop`. The method +/// can't be made const then, because `drop` can't be const-evaluated. +fn method_accepts_dropable(cx: &LateContext<'_, '_>, param_tys: &[hir::Ty<'_>]) -> bool { + // If any of the params are dropable, return true + param_tys.iter().any(|hir_ty| { + let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); + has_drop(cx, ty_ty) + }) +} + +// We don't have to lint on something that's already `const` +#[must_use] +fn already_const(header: hir::FnHeader) -> bool { + header.constness == Constness::Const +} diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs new file mode 100644 index 0000000000000..2eefb6bbaf424 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -0,0 +1,204 @@ +// Note: More specifically this lint is largely inspired (aka copied) from +// *rustc*'s +// [`missing_doc`]. +// +// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246 +// + +use crate::utils::span_lint; +use if_chain::if_chain; +use rustc_ast::ast::{self, MetaItem, MetaItemKind}; +use rustc_ast::attr; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Warns if there is missing doc for any documentable item + /// (public or private). + /// + /// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS` + /// allowed-by-default lint for + /// public members, but has no way to enforce documentation of private items. + /// This lint fixes that. + /// + /// **Known problems:** None. + pub MISSING_DOCS_IN_PRIVATE_ITEMS, + restriction, + "detects missing documentation for public and private members" +} + +pub struct MissingDoc { + /// Stack of whether #[doc(hidden)] is set + /// at each level which has lint attributes. + doc_hidden_stack: Vec, +} + +impl Default for MissingDoc { + #[must_use] + fn default() -> Self { + Self::new() + } +} + +impl MissingDoc { + #[must_use] + pub fn new() -> Self { + Self { + doc_hidden_stack: vec![false], + } + } + + fn doc_hidden(&self) -> bool { + *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") + } + + fn has_include(meta: Option) -> bool { + if_chain! { + if let Some(meta) = meta; + if let MetaItemKind::List(list) = meta.kind; + if let Some(meta) = list.get(0); + if let Some(name) = meta.ident(); + then { + name.as_str() == "include" + } else { + false + } + } + } + + fn check_missing_docs_attrs( + &self, + cx: &LateContext<'_, '_>, + attrs: &[ast::Attribute], + sp: Span, + desc: &'static str, + ) { + // If we're building a test harness, then warning about + // documentation is probably not really relevant right now. + if cx.sess().opts.test { + return; + } + + // `#[doc(hidden)]` disables missing_docs check. + if self.doc_hidden() { + return; + } + + if sp.from_expansion() { + return; + } + + let has_doc = attrs + .iter() + .any(|a| a.is_doc_comment() || a.doc_str().is_some() || a.is_value_str() || Self::has_include(a.meta())); + if !has_doc { + span_lint( + cx, + MISSING_DOCS_IN_PRIVATE_ITEMS, + sp, + &format!("missing documentation for {}", desc), + ); + } + } +} + +impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { + fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) { + let doc_hidden = self.doc_hidden() + || attrs.iter().any(|attr| { + attr.check_name(sym!(doc)) + && match attr.meta_item_list() { + None => false, + Some(l) => attr::list_contains_name(&l[..], sym!(hidden)), + } + }); + self.doc_hidden_stack.push(doc_hidden); + } + + fn exit_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx [ast::Attribute]) { + self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); + } + + fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) { + self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate"); + } + + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) { + let desc = match it.kind { + hir::ItemKind::Const(..) => "a constant", + hir::ItemKind::Enum(..) => "an enum", + hir::ItemKind::Fn(..) => { + // ignore main() + if it.ident.name == sym!(main) { + let def_id = it.hir_id.owner; + let def_key = cx.tcx.hir().def_key(def_id); + if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) { + return; + } + } + "a function" + }, + hir::ItemKind::Mod(..) => "a module", + hir::ItemKind::Static(..) => "a static", + hir::ItemKind::Struct(..) => "a struct", + hir::ItemKind::Trait(..) => "a trait", + hir::ItemKind::TraitAlias(..) => "a trait alias", + hir::ItemKind::TyAlias(..) => "a type alias", + hir::ItemKind::Union(..) => "a union", + hir::ItemKind::OpaqueTy(..) => "an existential type", + hir::ItemKind::ExternCrate(..) + | hir::ItemKind::ForeignMod(..) + | hir::ItemKind::GlobalAsm(..) + | hir::ItemKind::Impl { .. } + | hir::ItemKind::Use(..) => return, + }; + + self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc); + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { + let desc = match trait_item.kind { + hir::TraitItemKind::Const(..) => "an associated constant", + hir::TraitItemKind::Fn(..) => "a trait method", + hir::TraitItemKind::Type(..) => "an associated type", + }; + + self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc); + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + // If the method is an impl for a trait, don't doc. + let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + match cx.tcx.associated_item(def_id).container { + ty::TraitContainer(_) => return, + ty::ImplContainer(cid) => { + if cx.tcx.impl_trait_ref(cid).is_some() { + return; + } + }, + } + + let desc = match impl_item.kind { + hir::ImplItemKind::Const(..) => "an associated constant", + hir::ImplItemKind::Fn(..) => "a method", + hir::ImplItemKind::TyAlias(_) => "an associated type", + hir::ImplItemKind::OpaqueTy(_) => "an existential type", + }; + self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc); + } + + fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, sf: &'tcx hir::StructField<'_>) { + if !sf.is_positional() { + self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field"); + } + } + + fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, v: &'tcx hir::Variant<'_>) { + self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant"); + } +} diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs new file mode 100644 index 0000000000000..5300fd2215b39 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -0,0 +1,164 @@ +use crate::utils::span_lint; +use rustc_ast::ast; +use rustc_hir as hir; +use rustc_lint::{self, LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** it lints if an exported function, method, trait method with default impl, + /// or trait method impl is not `#[inline]`. + /// + /// **Why is this bad?** In general, it is not. Functions can be inlined across + /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled, + /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates + /// might intend for most of the methods in their public API to be able to be inlined across + /// crates even when LTO is disabled. For these types of crates, enabling this lint might make + /// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and + /// then opt out for specific methods where this might not make sense. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// pub fn foo() {} // missing #[inline] + /// fn ok() {} // ok + /// #[inline] pub fn bar() {} // ok + /// #[inline(always)] pub fn baz() {} // ok + /// + /// pub trait Bar { + /// fn bar(); // ok + /// fn def_bar() {} // missing #[inline] + /// } + /// + /// struct Baz; + /// impl Baz { + /// fn private() {} // ok + /// } + /// + /// impl Bar for Baz { + /// fn bar() {} // ok - Baz is not exported + /// } + /// + /// pub struct PubBaz; + /// impl PubBaz { + /// fn private() {} // ok + /// pub fn not_ptrivate() {} // missing #[inline] + /// } + /// + /// impl Bar for PubBaz { + /// fn bar() {} // missing #[inline] + /// fn def_bar() {} // missing #[inline] + /// } + /// ``` + pub MISSING_INLINE_IN_PUBLIC_ITEMS, + restriction, + "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)" +} + +fn check_missing_inline_attrs(cx: &LateContext<'_, '_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { + let has_inline = attrs.iter().any(|a| a.check_name(sym!(inline))); + if !has_inline { + span_lint( + cx, + MISSING_INLINE_IN_PUBLIC_ITEMS, + sp, + &format!("missing `#[inline]` for {}", desc), + ); + } +} + +fn is_executable(cx: &LateContext<'_, '_>) -> bool { + use rustc_session::config::CrateType; + + cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| match t { + CrateType::Executable => true, + _ => false, + }) +} + +declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) { + if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable(cx) { + return; + } + + if !cx.access_levels.is_exported(it.hir_id) { + return; + } + match it.kind { + hir::ItemKind::Fn(..) => { + let desc = "a function"; + check_missing_inline_attrs(cx, &it.attrs, it.span, desc); + }, + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, ref _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 { + let tit_ = cx.tcx.hir().trait_item(tit.id); + match tit_.kind { + hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, + hir::TraitItemKind::Fn(..) => { + if tit.defaultness.has_value() { + // trait method with default body needs inline in case + // an impl is not provided + let desc = "a default trait method"; + let item = cx.tcx.hir().expect_trait_item(tit.id.hir_id); + check_missing_inline_attrs(cx, &item.attrs, item.span, desc); + } + }, + } + } + }, + hir::ItemKind::Const(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::GlobalAsm(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::OpaqueTy(..) + | hir::ItemKind::ExternCrate(..) + | hir::ItemKind::ForeignMod(..) + | hir::ItemKind::Impl { .. } + | hir::ItemKind::Use(..) => {}, + }; + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + use rustc_middle::ty::{ImplContainer, TraitContainer}; + if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable(cx) { + return; + } + + // If the item being implemented is not exported, then we don't need #[inline] + if !cx.access_levels.is_exported(impl_item.hir_id) { + return; + } + + let desc = match impl_item.kind { + hir::ImplItemKind::Fn(..) => "a method", + hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => return, + }; + + let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let trait_def_id = match cx.tcx.associated_item(def_id).container { + TraitContainer(cid) => Some(cid), + ImplContainer(cid) => cx.tcx.impl_trait_ref(cid).map(|t| t.def_id), + }; + + if let Some(trait_def_id) = trait_def_id { + if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.hir_id) { + // If a trait is being implemented for an item, and the + // trait is not exported, we don't need #[inline] + return; + } + } + + check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc); + } +} diff --git a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs new file mode 100644 index 0000000000000..4ca90455bc4d1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs @@ -0,0 +1,148 @@ +use crate::consts::{constant, Constant}; +use crate::utils::{sext, span_lint_and_then}; +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::fmt::Display; + +declare_clippy_lint! { + /// **What it does:** Checks for modulo arithemtic. + /// + /// **Why is this bad?** The results of modulo (%) operation might differ + /// depending on the language, when negative numbers are involved. + /// If you interop with different languages it might be beneficial + /// to double check all places that use modulo arithmetic. + /// + /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = -17 % 3; + /// ``` + pub MODULO_ARITHMETIC, + restriction, + "any modulo arithmetic statement" +} + +declare_lint_pass!(ModuloArithmetic => [MODULO_ARITHMETIC]); + +struct OperandInfo { + string_representation: Option, + is_negative: bool, + is_integral: bool, +} + +fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + match constant(cx, cx.tables, operand) { + Some((Constant::Int(v), _)) => match cx.tables.expr_ty(expr).kind { + ty::Int(ity) => { + let value = sext(cx.tcx, v, ity); + return Some(OperandInfo { + string_representation: Some(value.to_string()), + is_negative: value < 0, + is_integral: true, + }); + }, + ty::Uint(_) => { + return Some(OperandInfo { + string_representation: None, + is_negative: false, + is_integral: true, + }); + }, + _ => {}, + }, + Some((Constant::F32(f), _)) => { + return Some(floating_point_operand_info(&f)); + }, + Some((Constant::F64(f), _)) => { + return Some(floating_point_operand_info(&f)); + }, + _ => {}, + } + None +} + +fn floating_point_operand_info>(f: &T) -> OperandInfo { + OperandInfo { + string_representation: Some(format!("{:.3}", *f)), + is_negative: *f < 0.0.into(), + is_integral: false, + } +} + +fn might_have_negative_value(t: &ty::TyS<'_>) -> bool { + t.is_signed() || t.is_floating_point() +} + +fn check_const_operands<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx Expr<'_>, + lhs_operand: &OperandInfo, + rhs_operand: &OperandInfo, +) { + if lhs_operand.is_negative ^ rhs_operand.is_negative { + span_lint_and_then( + cx, + MODULO_ARITHMETIC, + expr.span, + &format!( + "you are using modulo operator on constants with different signs: `{} % {}`", + lhs_operand.string_representation.as_ref().unwrap(), + rhs_operand.string_representation.as_ref().unwrap() + ), + |diag| { + diag.note("double check for expected result especially when interoperating with different languages"); + if lhs_operand.is_integral { + diag.note("or consider using `rem_euclid` or similar function"); + } + }, + ); + } +} + +fn check_non_const_operands<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, operand: &Expr<'_>) { + let operand_type = cx.tables.expr_ty(operand); + if might_have_negative_value(operand_type) { + span_lint_and_then( + cx, + MODULO_ARITHMETIC, + expr.span, + "you are using modulo operator on types that might have different signs", + |diag| { + diag.note("double check for expected result especially when interoperating with different languages"); + if operand_type.is_integral() { + diag.note("or consider using `rem_euclid` or similar function"); + } + }, + ); + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ModuloArithmetic { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + match &expr.kind { + ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => { + if let BinOpKind::Rem = op.node { + let lhs_operand = analyze_operand(lhs, cx, expr); + let rhs_operand = analyze_operand(rhs, cx, expr); + if_chain! { + if let Some(lhs_operand) = lhs_operand; + if let Some(rhs_operand) = rhs_operand; + then { + check_const_operands(cx, expr, &lhs_operand, &rhs_operand); + } + else { + check_non_const_operands(cx, expr, lhs); + } + } + }; + }, + _ => {}, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs new file mode 100644 index 0000000000000..ed85d0315bd25 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs @@ -0,0 +1,68 @@ +//! lint on multiple versions of a crate being used + +use crate::utils::{run_lints, span_lint}; +use rustc_hir::{Crate, CRATE_HIR_ID}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::DUMMY_SP; + +use itertools::Itertools; + +declare_clippy_lint! { + /// **What it does:** Checks to see if multiple versions of a crate are being + /// used. + /// + /// **Why is this bad?** This bloats the size of targets, and can lead to + /// confusing error messages when structs or traits are used interchangeably + /// between different versions of a crate. + /// + /// **Known problems:** Because this can be caused purely by the dependencies + /// themselves, it's not always possible to fix this issue. + /// + /// **Example:** + /// ```toml + /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning. + /// [dependencies] + /// ctrlc = "=3.1.0" + /// ansi_term = "=0.11.0" + /// ``` + pub MULTIPLE_CRATE_VERSIONS, + cargo, + "multiple versions of the same crate being used" +} + +declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]); + +impl LateLintPass<'_, '_> for MultipleCrateVersions { + fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { + if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) { + return; + } + + let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() { + metadata + } else { + span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata"); + + return; + }; + + let mut packages = metadata.packages; + packages.sort_by(|a, b| a.name.cmp(&b.name)); + + for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) { + let group: Vec = group.collect(); + + if group.len() > 1 { + let versions = group.into_iter().map(|p| p.version).join(", "); + + span_lint( + cx, + MULTIPLE_CRATE_VERSIONS, + DUMMY_SP, + &format!("multiple versions for dependency `{}`: {}", name, versions), + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs new file mode 100644 index 0000000000000..0b9b7e1b8cc1b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -0,0 +1,124 @@ +use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty}; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for sets/maps with mutable key types. + /// + /// **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and + /// `BtreeSet` rely on either the hash or the order of keys be unchanging, + /// so having types with interior mutability is a bad idea. + /// + /// **Known problems:** We don't currently account for `Rc` or `Arc`, so + /// this may yield false positives. + /// + /// **Example:** + /// ```rust + /// use std::cmp::{PartialEq, Eq}; + /// use std::collections::HashSet; + /// use std::hash::{Hash, Hasher}; + /// use std::sync::atomic::AtomicUsize; + ///# #[allow(unused)] + /// + /// struct Bad(AtomicUsize); + /// impl PartialEq for Bad { + /// fn eq(&self, rhs: &Self) -> bool { + /// .. + /// ; unimplemented!(); + /// } + /// } + /// + /// impl Eq for Bad {} + /// + /// impl Hash for Bad { + /// fn hash(&self, h: &mut H) { + /// .. + /// ; unimplemented!(); + /// } + /// } + /// + /// fn main() { + /// let _: HashSet = HashSet::new(); + /// } + /// ``` + pub MUTABLE_KEY_TYPE, + correctness, + "Check for mutable `Map`/`Set` key type" +} + +declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableKeyType { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'tcx>) { + if let hir::ItemKind::Fn(ref sig, ..) = item.kind { + check_sig(cx, item.hir_id, &sig.decl); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'tcx>) { + if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { + if trait_ref_of_method(cx, item.hir_id).is_none() { + check_sig(cx, item.hir_id, &sig.decl); + } + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'tcx>) { + if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { + check_sig(cx, item.hir_id, &sig.decl); + } + } + + fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &hir::Local<'_>) { + if let hir::PatKind::Wild = local.pat.kind { + return; + } + check_ty(cx, local.span, cx.tables.pat_ty(&*local.pat)); + } +} + +fn check_sig<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { + let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); + let fn_sig = cx.tcx.fn_sig(fn_def_id); + for (hir_ty, ty) in decl.inputs.iter().zip(fn_sig.inputs().skip_binder().iter()) { + check_ty(cx, hir_ty.span, ty); + } + check_ty( + cx, + decl.output.span(), + cx.tcx.erase_late_bound_regions(&fn_sig.output()), + ); +} + +// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased +// generics (because the compiler cannot ensure immutability for unknown types). +fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, ty: Ty<'tcx>) { + let ty = walk_ptrs_ty(ty); + if let Adt(def, substs) = ty.kind { + if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET] + .iter() + .any(|path| match_def_path(cx, def.did, &**path)) + && is_mutable_type(cx, substs.type_at(0), span) + { + span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); + } + } +} + +fn is_mutable_type<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, span: Span) -> bool { + match ty.kind { + RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => { + mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span) + }, + Slice(inner_ty) => is_mutable_type(cx, inner_ty, span), + Array(inner_ty, size) => { + size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span) + }, + Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)), + Adt(..) => cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env, span), + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs new file mode 100644 index 0000000000000..f7a20a74b85e2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -0,0 +1,114 @@ +use crate::utils::{higher, span_lint}; +use rustc_hir as hir; +use rustc_hir::intravisit; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for instances of `mut mut` references. + /// + /// **Why is this bad?** Multiple `mut`s don't add anything meaningful to the + /// source. This is either a copy'n'paste error, or it shows a fundamental + /// misunderstanding of references. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let mut y = 1; + /// let x = &mut &mut y; + /// ``` + pub MUT_MUT, + pedantic, + "usage of double-mut refs, e.g., `&mut &mut ...`" +} + +declare_lint_pass!(MutMut => [MUT_MUT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutMut { + fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block<'_>) { + intravisit::walk_block(&mut MutVisitor { cx }, block); + } + + fn check_ty(&mut self, cx: &LateContext<'a, 'tcx>, ty: &'tcx hir::Ty<'_>) { + use rustc_hir::intravisit::Visitor; + + MutVisitor { cx }.visit_ty(ty); + } +} + +pub struct MutVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + if in_external_macro(self.cx.sess(), expr.span) { + return; + } + + if let Some((_, arg, body)) = higher::for_loop(expr) { + // A `for` loop lowers to: + // ```rust + // match ::std::iter::Iterator::next(&mut iter) { + // // ^^^^ + // ``` + // Let's ignore the generated code. + intravisit::walk_expr(self, arg); + intravisit::walk_expr(self, body); + } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, ref e) = expr.kind { + if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { + span_lint( + self.cx, + MUT_MUT, + expr.span, + "generally you want to avoid `&mut &mut _` if possible", + ); + } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.tables.expr_ty(e).kind { + span_lint( + self.cx, + MUT_MUT, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); + } + } + } + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + if let hir::TyKind::Rptr( + _, + hir::MutTy { + ty: ref pty, + mutbl: hir::Mutability::Mut, + }, + ) = ty.kind + { + if let hir::TyKind::Rptr( + _, + hir::MutTy { + mutbl: hir::Mutability::Mut, + .. + }, + ) = pty.kind + { + span_lint( + self.cx, + MUT_MUT, + ty.span, + "generally you want to avoid `&mut &mut _` if possible", + ); + } + } + + intravisit::walk_ty(self, ty); + } + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs new file mode 100644 index 0000000000000..e5680482e5bfb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -0,0 +1,82 @@ +use crate::utils::span_lint; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects giving a mutable reference to a function that only + /// requires an immutable reference. + /// + /// **Why is this bad?** The immutable reference rules out all other references + /// to the value. Also the code misleads about the intent of the call site. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// my_vec.push(&mut value) + /// ``` + pub UNNECESSARY_MUT_PASSED, + style, + "an argument passed as a mutable reference although the callee only demands an immutable reference" +} + +declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + match e.kind { + ExprKind::Call(ref fn_expr, ref arguments) => { + if let ExprKind::Path(ref path) = fn_expr.kind { + check_arguments( + cx, + arguments, + cx.tables.expr_ty(fn_expr), + &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)), + ); + } + }, + ExprKind::MethodCall(ref path, _, ref arguments) => { + let def_id = cx.tables.type_dependent_def_id(e.hir_id).unwrap(); + let substs = cx.tables.node_substs(e.hir_id); + let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); + check_arguments(cx, arguments, method_type, &path.ident.as_str()) + }, + _ => (), + } + } +} + +fn check_arguments<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + arguments: &[Expr<'_>], + type_definition: Ty<'tcx>, + name: &str, +) { + match type_definition.kind { + ty::FnDef(..) | ty::FnPtr(_) => { + let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); + for (argument, parameter) in arguments.iter().zip(parameters.iter()) { + match parameter.kind { + ty::Ref(_, _, Mutability::Not) + | ty::RawPtr(ty::TypeAndMut { + mutbl: Mutability::Not, .. + }) => { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind { + span_lint( + cx, + UNNECESSARY_MUT_PASSED, + argument.span, + &format!("The function/method `{}` doesn't need a mutable reference", name), + ); + } + }, + _ => (), + } + } + }, + _ => (), + } +} diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs new file mode 100644 index 0000000000000..119e0905ff442 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -0,0 +1,159 @@ +use crate::utils::{is_direct_expn_of, span_lint}; +use if_chain::if_chain; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability, StmtKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for function/method calls with a mutable + /// parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros. + /// + /// **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the + /// compiler. + /// Therefore mutating something in a `debug_assert!` macro results in different behaviour + /// between a release and debug build. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust,ignore + /// debug_assert_eq!(vec![3].pop(), Some(3)); + /// // or + /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() } + /// debug_assert!(take_a_mut_parameter(&mut 5)); + /// ``` + pub DEBUG_ASSERT_WITH_MUT_CALL, + nursery, + "mutable arguments in `debug_assert{,_ne,_eq}!`" +} + +declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]); + +const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"]; + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DebugAssertWithMutCall { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + for dmn in &DEBUG_MACRO_NAMES { + if is_direct_expn_of(e.span, dmn).is_some() { + if let Some(span) = extract_call(cx, e) { + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &format!("do not call a function with mutable arguments inside of `{}!`", dmn), + ); + } + } + } + } +} + +//HACK(hellow554): remove this when #4694 is implemented +fn extract_call<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) -> Option { + if_chain! { + if let ExprKind::Block(ref block, _) = e.kind; + if block.stmts.len() == 1; + if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind; + then { + // debug_assert + if_chain! { + if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind; + if let ExprKind::DropTemps(ref droptmp) = ifclause.kind; + if let ExprKind::Unary(UnOp::UnNot, ref condition) = droptmp.kind; + then { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(condition); + return visitor.expr_span(); + } + } + + // debug_assert_{eq,ne} + if_chain! { + if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; + if let Some(ref matchheader) = matchblock.expr; + if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; + if let ExprKind::Tup(ref conditions) = headerexpr.kind; + if conditions.len() == 2; + then { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(lhs); + if let Some(span) = visitor.expr_span() { + return Some(span); + } + } + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(rhs); + if let Some(span) = visitor.expr_span() { + return Some(span); + } + } + } + } + } + } + + None +} + +struct MutArgVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + expr_span: Option, + found: bool, +} + +impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + Self { + cx, + expr_span: None, + found: false, + } + } + + fn expr_span(&self) -> Option { + if self.found { + self.expr_span + } else { + None + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match expr.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => { + self.found = true; + return; + }, + ExprKind::Path(_) => { + if let Some(adj) = self.cx.tables.adjustments().get(expr.hir_id) { + if adj + .iter() + .any(|a| matches!(a.target.kind, ty::Ref(_, _, Mutability::Mut))) + { + self.found = true; + return; + } + } + }, + // Don't check await desugars + ExprKind::Match(_, _, MatchSource::AwaitDesugar) => return, + _ if !self.found => self.expr_span = Some(expr.span), + _ => return, + } + walk_expr(self, expr) + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs new file mode 100644 index 0000000000000..4e1a8be4892e6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -0,0 +1,88 @@ +//! Checks for uses of mutex where an atomic value could be used +//! +//! This lint is **warn** by default + +use crate::utils::{is_type_diagnostic_item, span_lint}; +use rustc_ast::ast; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usages of `Mutex` where an atomic will do. + /// + /// **Why is this bad?** Using a mutex just to make access to a plain bool or + /// reference sequential is shooting flies with cannons. + /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and + /// faster. + /// + /// **Known problems:** This lint cannot detect if the mutex is actually used + /// for waiting before a critical section. + /// + /// **Example:** + /// ```rust + /// # use std::sync::Mutex; + /// # let y = 1; + /// let x = Mutex::new(&y); + /// ``` + pub MUTEX_ATOMIC, + perf, + "using a mutex where an atomic value could be used instead" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usages of `Mutex` where `X` is an integral + /// type. + /// + /// **Why is this bad?** Using a mutex just to make access to a plain integer + /// sequential is + /// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster. + /// + /// **Known problems:** This lint cannot detect if the mutex is actually used + /// for waiting before a critical section. + /// + /// **Example:** + /// ```rust + /// # use std::sync::Mutex; + /// let x = Mutex::new(0usize); + /// ``` + pub MUTEX_INTEGER, + nursery, + "using a mutex for an integer type" +} + +declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Mutex { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + let ty = cx.tables.expr_ty(expr); + if let ty::Adt(_, subst) = ty.kind { + if is_type_diagnostic_item(cx, ty, sym!(mutex_type)) { + let mutex_param = subst.type_at(0); + if let Some(atomic_name) = get_atomic_name(mutex_param) { + let msg = format!( + "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`.", + atomic_name + ); + match mutex_param.kind { + ty::Uint(t) if t != ast::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), + ty::Int(t) if t != ast::IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), + _ => span_lint(cx, MUTEX_ATOMIC, expr.span, &msg), + }; + } + } + } + } +} + +fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind { + ty::Bool => Some("AtomicBool"), + ty::Uint(_) => Some("AtomicUsize"), + ty::Int(_) => Some("AtomicIsize"), + ty::RawPtr(_) => Some("AtomicPtr"), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs new file mode 100644 index 0000000000000..efa77db822dd0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -0,0 +1,348 @@ +//! Checks for needless boolean results of if-else expressions +//! +//! This lint is **warn** by default + +use crate::utils::sugg::Sugg; +use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for expressions of the form `if c { true } else { + /// false }` + /// (or vice versa) and suggest using the condition directly. + /// + /// **Why is this bad?** Redundant code. + /// + /// **Known problems:** Maybe false positives: Sometimes, the two branches are + /// painstakingly documented (which we, of course, do not detect), so they *may* + /// have some value. Even then, the documentation can be rewritten to match the + /// shorter code. + /// + /// **Example:** + /// ```rust,ignore + /// if x { + /// false + /// } else { + /// true + /// } + /// ``` + /// Could be written as + /// ```rust,ignore + /// !x + /// ``` + pub NEEDLESS_BOOL, + complexity, + "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for expressions of the form `x == true`, + /// `x != true` and order comparisons such as `x < true` (or vice versa) and + /// suggest using the variable directly. + /// + /// **Why is this bad?** Unnecessary code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// if x == true {} + /// if y == false {} + /// ``` + /// use `x` directly: + /// ```rust,ignore + /// if x {} + /// if !y {} + /// ``` + pub BOOL_COMPARISON, + complexity, + "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" +} + +declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + use self::Expression::{Bool, RetBool}; + if let Some((ref pred, ref then_block, Some(ref else_expr))) = higher::if_block(&e) { + let reduce = |ret, not| { + let mut applicability = Applicability::MachineApplicable; + let snip = Sugg::hir_with_applicability(cx, pred, "", &mut applicability); + let mut snip = if not { !snip } else { snip }; + + if ret { + snip = snip.make_return(); + } + + if parent_node_is_if_expr(&e, &cx) { + snip = snip.blockify() + } + + span_lint_and_sugg( + cx, + NEEDLESS_BOOL, + e.span, + "this if-then-else expression returns a bool literal", + "you can reduce it to", + snip.to_string(), + applicability, + ); + }; + if let ExprKind::Block(ref then_block, _) = then_block.kind { + match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) { + (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { + span_lint( + cx, + NEEDLESS_BOOL, + e.span, + "this if-then-else expression will always return true", + ); + }, + (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => { + span_lint( + cx, + NEEDLESS_BOOL, + e.span, + "this if-then-else expression will always return false", + ); + }, + (RetBool(true), RetBool(false)) => reduce(true, false), + (Bool(true), Bool(false)) => reduce(false, false), + (RetBool(false), RetBool(true)) => reduce(true, true), + (Bool(false), Bool(true)) => reduce(false, true), + _ => (), + } + } else { + panic!("IfExpr `then` node is not an `ExprKind::Block`"); + } + } + } +} + +declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind { + let ignore_case = None::<(fn(_) -> _, &str)>; + let ignore_no_literal = None::<(fn(_, _) -> _, &str)>; + match node { + BinOpKind::Eq => { + let true_case = Some((|h| h, "equality checks against true are unnecessary")); + let false_case = Some(( + |h: Sugg<'_>| !h, + "equality checks against false can be replaced by a negation", + )); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal) + }, + BinOpKind::Ne => { + let true_case = Some(( + |h: Sugg<'_>| !h, + "inequality checks against true can be replaced by a negation", + )); + let false_case = Some((|h| h, "inequality checks against false are unnecessary")); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal) + }, + BinOpKind::Lt => check_comparison( + cx, + e, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |h: Sugg<'_>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + Some(( + |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r), + "order comparisons between booleans can be simplified", + )), + ), + BinOpKind::Gt => check_comparison( + cx, + e, + Some(( + |h: Sugg<'_>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)), + "order comparisons between booleans can be simplified", + )), + ), + _ => (), + } + } + } +} + +struct ExpressionInfoWithSpan { + one_side_is_unary_not: bool, + left_span: Span, + right_span: Span, +} + +fn is_unary_not(e: &Expr<'_>) -> (bool, Span) { + if_chain! { + if let ExprKind::Unary(unop, operand) = e.kind; + if let UnOp::UnNot = unop; + then { + return (true, operand.span); + } + }; + (false, e.span) +} + +fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan { + let left = is_unary_not(left_side); + let right = is_unary_not(right_side); + + ExpressionInfoWithSpan { + one_side_is_unary_not: left.0 != right.0, + left_span: left.1, + right_span: right.1, + } +} + +fn check_comparison<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + e: &'tcx Expr<'_>, + left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, + left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, + right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, + right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, + no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>, +) { + use self::Expression::{Bool, Other}; + + if let ExprKind::Binary(op, ref left_side, ref right_side) = e.kind { + let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side)); + if l_ty.is_bool() && r_ty.is_bool() { + let mut applicability = Applicability::MachineApplicable; + + if let BinOpKind::Eq = op.node { + let expression_info = one_side_is_unary_not(&left_side, &right_side); + if expression_info.one_side_is_unary_not { + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + e.span, + "This comparison might be written more concisely", + "try simplifying it as shown", + format!( + "{} != {}", + snippet_with_applicability(cx, expression_info.left_span, "..", &mut applicability), + snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability) + ), + applicability, + ) + } + } + + match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { + (Bool(true), Other) => left_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, e, right_side, applicability, m, h) + }), + (Other, Bool(true)) => right_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, e, left_side, applicability, m, h) + }), + (Bool(false), Other) => left_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, e, right_side, applicability, m, h) + }), + (Other, Bool(false)) => right_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, e, left_side, applicability, m, h) + }), + (Other, Other) => no_literal.map_or((), |(h, m)| { + let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability); + let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + e.span, + m, + "try simplifying it as shown", + h(left_side, right_side).to_string(), + applicability, + ) + }), + _ => (), + } + } + } +} + +fn suggest_bool_comparison<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + e: &'tcx Expr<'_>, + expr: &Expr<'_>, + mut applicability: Applicability, + message: &str, + conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, +) { + let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + e.span, + message, + "try simplifying it as shown", + conv_hint(hint).to_string(), + applicability, + ); +} + +enum Expression { + Bool(bool), + RetBool(bool), + Other, +} + +fn fetch_bool_block(block: &Block<'_>) -> Expression { + match (&*block.stmts, block.expr.as_ref()) { + (&[], Some(e)) => fetch_bool_expr(&**e), + (&[ref e], None) => { + if let StmtKind::Semi(ref e) = e.kind { + if let ExprKind::Ret(_) = e.kind { + fetch_bool_expr(&**e) + } else { + Expression::Other + } + } else { + Expression::Other + } + }, + _ => Expression::Other, + } +} + +fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { + match expr.kind { + ExprKind::Block(ref block, _) => fetch_bool_block(block), + ExprKind::Lit(ref lit_ptr) => { + if let LitKind::Bool(value) = lit_ptr.node { + Expression::Bool(value) + } else { + Expression::Other + } + }, + ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) { + Expression::Bool(value) => Expression::RetBool(value), + _ => Expression::Other, + }, + _ => Expression::Other, + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs new file mode 100644 index 0000000000000..9ee875d7516eb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -0,0 +1,124 @@ +//! Checks for needless address of operations (`&`) +//! +//! This lint is **warn** by default + +use crate::utils::{snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for address of operations (`&`) that are going to + /// be dereferenced immediately by the compiler. + /// + /// **Why is this bad?** Suggests that the receiver of the expression borrows + /// the expression. + /// + /// **Example:** + /// ```rust + /// let x: &i32 = &&&&&&5; + /// ``` + /// + /// **Known problems:** None. + pub NEEDLESS_BORROW, + nursery, + "taking a reference that is going to be automatically dereferenced" +} + +#[derive(Default)] +pub struct NeedlessBorrow { + derived_item: Option, +} + +impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrow { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() || self.derived_item.is_some() { + return; + } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { + if let ty::Ref(..) = cx.tables.expr_ty(inner).kind { + for adj3 in cx.tables.expr_adjustments(e).windows(3) { + if let [Adjustment { + kind: Adjust::Deref(_), .. + }, Adjustment { + kind: Adjust::Deref(_), .. + }, Adjustment { + kind: Adjust::Borrow(_), + .. + }] = *adj3 + { + span_lint_and_then( + cx, + NEEDLESS_BORROW, + e.span, + "this expression borrows a reference that is immediately dereferenced \ + by the compiler", + |diag| { + if let Some(snippet) = snippet_opt(cx, inner.span) { + diag.span_suggestion( + e.span, + "change this to", + snippet, + Applicability::MachineApplicable, + ); + } + }, + ); + } + } + } + } + } + fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) { + if pat.span.from_expansion() || self.derived_item.is_some() { + return; + } + if_chain! { + if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind; + if let ty::Ref(_, tam, mutbl) = cx.tables.pat_ty(pat).kind; + if mutbl == Mutability::Not; + if let ty::Ref(_, _, mutbl) = tam.kind; + // only lint immutable refs, because borrowed `&mut T` cannot be moved out + if mutbl == Mutability::Not; + then { + span_lint_and_then( + cx, + NEEDLESS_BORROW, + pat.span, + "this pattern creates a reference to a reference", + |diag| { + if let Some(snippet) = snippet_opt(cx, name.span) { + diag.span_suggestion( + pat.span, + "change this to", + snippet, + Applicability::MachineApplicable, + ); + } + } + ) + } + } + } + + fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if item.attrs.iter().any(|a| a.check_name(sym!(automatically_derived))) { + debug_assert!(self.derived_item.is_none()); + self.derived_item = Some(item.hir_id); + } + } + + fn check_item_post(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if let Some(id) = self.derived_item { + if item.hir_id == id { + self.derived_item = None; + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs new file mode 100644 index 0000000000000..e56489c6d434d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs @@ -0,0 +1,92 @@ +//! Checks for useless borrowed references. +//! +//! This lint is **warn** by default + +use crate::utils::{snippet_with_applicability, span_lint_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for useless borrowed references. + /// + /// **Why is this bad?** It is mostly useless and make the code look more + /// complex than it + /// actually is. + /// + /// **Known problems:** It seems that the `&ref` pattern is sometimes useful. + /// For instance in the following snippet: + /// ```rust,ignore + /// enum Animal { + /// Cat(u64), + /// Dog(u64), + /// } + /// + /// fn foo(a: &Animal, b: &Animal) { + /// match (a, b) { + /// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error + /// (&Animal::Dog(ref c), &Animal::Dog(_)) => () + /// } + /// } + /// ``` + /// There is a lifetime mismatch error for `k` (indeed a and b have distinct + /// lifetime). + /// This can be fixed by using the `&ref` pattern. + /// However, the code can also be fixed by much cleaner ways + /// + /// **Example:** + /// ```rust + /// let mut v = Vec::::new(); + /// let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + /// ``` + /// This closure takes a reference on something that has been matched as a + /// reference and + /// de-referenced. + /// As such, it could just be |a| a.is_empty() + pub NEEDLESS_BORROWED_REFERENCE, + complexity, + "taking a needless borrowed reference" +} + +declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrowedRef { + fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) { + if pat.span.from_expansion() { + // OK, simple enough, lints doesn't check in macro. + return; + } + + if_chain! { + // Only lint immutable refs, because `&mut ref T` may be useful. + if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind; + + // Check sub_pat got a `ref` keyword (excluding `ref mut`). + if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind; + let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id); + if let Some(parent_node) = cx.tcx.hir().find(parent_id); + then { + // do not recurse within patterns, as they may have other references + // XXXManishearth we can relax this constraint if we only check patterns + // with a single ref pattern inside them + if let Node::Pat(_) = parent_node { + return; + } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span, + "this pattern takes a reference on something that is being de-referenced", + |diag| { + let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned(); + diag.span_suggestion( + pat.span, + "try removing the `&ref` part and just keep", + hint, + applicability, + ); + }); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs new file mode 100644 index 0000000000000..28183810df489 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -0,0 +1,463 @@ +//! Checks for continue statements in loops that are redundant. +//! +//! For example, the lint would catch +//! +//! ```rust +//! let mut a = 1; +//! let x = true; +//! +//! while a < 5 { +//! a = 6; +//! if x { +//! // ... +//! } else { +//! continue; +//! } +//! println!("Hello, world"); +//! } +//! ``` +//! +//! And suggest something like this: +//! +//! ```rust +//! let mut a = 1; +//! let x = true; +//! +//! while a < 5 { +//! a = 6; +//! if x { +//! // ... +//! println!("Hello, world"); +//! } +//! } +//! ``` +//! +//! This lint is **warn** by default. +use rustc_ast::ast; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{original_sp, DUMMY_SP}; +use rustc_span::Span; + +use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `if`-statements appearing in loops + /// that contain a `continue` statement in either their main blocks or their + /// `else`-blocks, when omitting the `else`-block possibly with some + /// rearrangement of code can make the code easier to understand. + /// + /// **Why is this bad?** Having explicit `else` blocks for `if` statements + /// containing `continue` in their THEN branch adds unnecessary branching and + /// nesting to the code. Having an else block containing just `continue` can + /// also be better written by grouping the statements following the whole `if` + /// statement within the THEN block and omitting the else block completely. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// # fn condition() -> bool { false } + /// # fn update_condition() {} + /// # let x = false; + /// while condition() { + /// update_condition(); + /// if x { + /// // ... + /// } else { + /// continue; + /// } + /// println!("Hello, world"); + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// # fn condition() -> bool { false } + /// # fn update_condition() {} + /// # let x = false; + /// while condition() { + /// update_condition(); + /// if x { + /// // ... + /// println!("Hello, world"); + /// } + /// } + /// ``` + /// + /// As another example, the following code + /// + /// ```rust + /// # fn waiting() -> bool { false } + /// loop { + /// if waiting() { + /// continue; + /// } else { + /// // Do something useful + /// } + /// # break; + /// } + /// ``` + /// Could be rewritten as + /// + /// ```rust + /// # fn waiting() -> bool { false } + /// loop { + /// if waiting() { + /// continue; + /// } + /// // Do something useful + /// # break; + /// } + /// ``` + pub NEEDLESS_CONTINUE, + pedantic, + "`continue` statements that can be replaced by a rearrangement of code" +} + +declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); + +impl EarlyLintPass for NeedlessContinue { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if !expr.span.from_expansion() { + check_and_warn(cx, expr); + } + } +} + +/* This lint has to mainly deal with two cases of needless continue + * statements. */ +// Case 1 [Continue inside else block]: +// +// loop { +// // region A +// if cond { +// // region B +// } else { +// continue; +// } +// // region C +// } +// +// This code can better be written as follows: +// +// loop { +// // region A +// if cond { +// // region B +// // region C +// } +// } +// +// Case 2 [Continue inside then block]: +// +// loop { +// // region A +// if cond { +// continue; +// // potentially more code here. +// } else { +// // region B +// } +// // region C +// } +// +// +// This snippet can be refactored to: +// +// loop { +// // region A +// if !cond { +// // region B +// // region C +// } +// } +// + +/// Given an expression, returns true if either of the following is true +/// +/// - The expression is a `continue` node. +/// - The expression node is a block with the first statement being a +/// `continue`. +fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { + match else_expr.kind { + ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), + ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), + _ => false, + } +} + +fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool { + block.stmts.get(0).map_or(false, |stmt| match stmt.kind { + ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { + if let ast::ExprKind::Continue(ref l) = e.kind { + compare_labels(label, l.as_ref()) + } else { + false + } + }, + _ => false, + }) +} + +/// If the `continue` has a label, check it matches the label of the loop. +fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool { + match (loop_label, continue_label) { + // `loop { continue; }` or `'a loop { continue; }` + (_, None) => true, + // `loop { continue 'a; }` + (None, _) => false, + // `'a loop { continue 'a; }` or `'a loop { continue 'b; }` + (Some(x), Some(y)) => x.ident == y.ident, + } +} + +/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with +/// the AST object representing the loop block of `expr`. +fn with_loop_block(expr: &ast::Expr, mut func: F) +where + F: FnMut(&ast::Block, Option<&ast::Label>), +{ + if let ast::ExprKind::While(_, loop_block, label) + | ast::ExprKind::ForLoop(_, _, loop_block, label) + | ast::ExprKind::Loop(loop_block, label) = &expr.kind + { + func(loop_block, label.as_ref()); + } +} + +/// If `stmt` is an if expression node with an `else` branch, calls func with +/// the +/// following: +/// +/// - The `if` expression itself, +/// - The `if` condition expression, +/// - The `then` block, and +/// - The `else` expression. +fn with_if_expr(stmt: &ast::Stmt, mut func: F) +where + F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr), +{ + match stmt.kind { + ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { + if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind { + func(e, cond, if_block, else_expr); + } + }, + _ => {}, + } +} + +/// A type to distinguish between the two distinct cases this lint handles. +#[derive(Copy, Clone, Debug)] +enum LintType { + ContinueInsideElseBlock, + ContinueInsideThenBlock, +} + +/// Data we pass around for construction of help messages. +struct LintData<'a> { + /// The `if` expression encountered in the above loop. + if_expr: &'a ast::Expr, + /// The condition expression for the above `if`. + if_cond: &'a ast::Expr, + /// The `then` block of the `if` statement. + if_block: &'a ast::Block, + /// The `else` block of the `if` statement. + /// Note that we only work with `if` exprs that have an `else` branch. + else_expr: &'a ast::Expr, + /// The 0-based index of the `if` statement in the containing loop block. + stmt_idx: usize, + /// The statements of the loop block. + block_stmts: &'a [ast::Stmt], +} + +const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant"; + +const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \ + expression"; + +const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "consider dropping the `else` clause and merging the code that \ + follows (in the loop) with the `if` block"; + +const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; + +fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) { + // snip is the whole *help* message that appears after the warning. + // message is the warning message. + // expr is the expression which the lint warning message refers to. + let (snip, message, expr) = match typ { + LintType::ContinueInsideElseBlock => ( + suggestion_snippet_for_continue_inside_else(cx, data), + MSG_REDUNDANT_ELSE_BLOCK, + data.else_expr, + ), + LintType::ContinueInsideThenBlock => ( + suggestion_snippet_for_continue_inside_if(cx, data), + MSG_ELSE_BLOCK_NOT_NEEDED, + data.if_expr, + ), + }; + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + expr.span, + message, + None, + &format!("{}\n{}", header, snip), + ); +} + +fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { + let cond_code = snippet(cx, data.if_cond.span, ".."); + + let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); + + let else_code = snippet_block(cx, data.else_expr.span, "..", Some(data.if_expr.span)); + + let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); + format!( + "{indent}if {} {}\n{indent}{}", + cond_code, + continue_code, + else_code, + indent = " ".repeat(indent_if), + ) +} + +fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { + let cond_code = snippet(cx, data.if_cond.span, ".."); + + // Region B + let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span))); + + // Region C + // These is the code in the loop block that follows the if/else construction + // we are complaining about. We want to pull all of this code into the + // `then` block of the `if` statement. + let indent = span_of_first_expr_in_block(data.if_block) + .and_then(|span| indent_of(cx, span)) + .unwrap_or(0); + let to_annex = data.block_stmts[data.stmt_idx + 1..] + .iter() + .map(|stmt| original_sp(stmt.span, DUMMY_SP)) + .map(|span| { + let snip = snippet_block(cx, span, "..", None).into_owned(); + snip.lines() + .map(|line| format!("{}{}", " ".repeat(indent), line)) + .collect::>() + .join("\n") + }) + .collect::>() + .join("\n"); + + let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); + format!( + "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}", + cond_code, + block_code, + to_annex, + indent = " ".repeat(indent), + indent_if = " ".repeat(indent_if), + ) +} + +fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) { + with_loop_block(expr, |loop_block, label| { + for (i, stmt) in loop_block.stmts.iter().enumerate() { + with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { + let data = &LintData { + stmt_idx: i, + if_expr, + if_cond: cond, + if_block: then_block, + else_expr, + block_stmts: &loop_block.stmts, + }; + if needless_continue_in_else(else_expr, label) { + emit_warning( + cx, + data, + DROP_ELSE_BLOCK_AND_MERGE_MSG, + LintType::ContinueInsideElseBlock, + ); + } else if is_first_block_stmt_continue(then_block, label) { + emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } + }); + } + }); +} + +/// Eats at `s` from the end till a closing brace `}` is encountered, and then continues eating +/// till a non-whitespace character is found. e.g., the string. If no closing `}` is present, the +/// string will be preserved. +/// +/// ```rust +/// { +/// let x = 5; +/// } +/// ``` +/// +/// is transformed to +/// +/// ```ignore +/// { +/// let x = 5; +/// ``` +#[must_use] +fn erode_from_back(s: &str) -> String { + let mut ret = s.to_string(); + while ret.pop().map_or(false, |c| c != '}') {} + while let Some(c) = ret.pop() { + if !c.is_whitespace() { + ret.push(c); + break; + } + } + if ret.is_empty() { + s.to_string() + } else { + ret + } +} + +fn span_of_first_expr_in_block(block: &ast::Block) -> Option { + block.stmts.iter().next().map(|stmt| stmt.span) +} + +#[cfg(test)] +mod test { + use super::erode_from_back; + + #[test] + #[rustfmt::skip] + fn test_erode_from_back() { + let input = "\ +{ + let x = 5; + let y = format!(\"{}\", 42); +}"; + + let expected = "\ +{ + let x = 5; + let y = format!(\"{}\", 42);"; + + let got = erode_from_back(input); + assert_eq!(expected, got); + } + + #[test] + #[rustfmt::skip] + fn test_erode_from_back_no_brace() { + let input = "\ +let x = 5; +let y = something(); +"; + let expected = input; + let got = erode_from_back(input); + assert_eq!(expected, got); + } +} 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 new file mode 100644 index 0000000000000..ed48ab548978c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -0,0 +1,347 @@ +use crate::utils::ptr::get_spans; +use crate::utils::{ + get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet, + snippet_opt, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_ast::ast::Attribute; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Node, PatKind, QPath, TyKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, TypeFoldable}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::misc::can_type_implement_copy; +use rustc_typeck::expr_use_visitor as euv; +use std::borrow::Cow; + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by value, but not + /// consuming them in its + /// body. + /// + /// **Why is this bad?** Taking arguments by reference is more flexible and can + /// sometimes avoid + /// unnecessary allocations. + /// + /// **Known problems:** + /// * This lint suggests taking an argument by reference, + /// however sometimes it is better to let users decide the argument type + /// (by using `Borrow` trait, for example), depending on how the function is used. + /// + /// **Example:** + /// ```rust + /// fn foo(v: Vec) { + /// assert_eq!(v.len(), 42); + /// } + /// ``` + /// + /// ```rust + /// // should be + /// fn foo(v: &[i32]) { + /// assert_eq!(v.len(), 42); + /// } + /// ``` + pub NEEDLESS_PASS_BY_VALUE, + pedantic, + "functions taking arguments by value, but not consuming them in its body" +} + +declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]); + +macro_rules! need { + ($e: expr) => { + if let Some(x) = $e { + x + } else { + return; + } + }; +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { + #[allow(clippy::too_many_lines)] + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + hir_id: HirId, + ) { + if span.from_expansion() { + return; + } + + match kind { + FnKind::ItemFn(.., header, _, attrs) => { + if header.abi != Abi::Rust || requires_exact_signature(attrs) { + return; + } + }, + FnKind::Method(..) => (), + _ => return, + } + + // Exclude non-inherent impls + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | + ItemKind::Trait(..)) + { + return; + } + } + + // Allow `Borrow` or functions to be taken by value + let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT)); + let whitelisted_traits = [ + need!(cx.tcx.lang_items().fn_trait()), + need!(cx.tcx.lang_items().fn_once_trait()), + need!(cx.tcx.lang_items().fn_mut_trait()), + need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)), + ]; + + let sized_trait = need!(cx.tcx.lang_items().sized_trait()); + + let fn_def_id = cx.tcx.hir().local_def_id(hir_id); + + let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds.iter().copied()) + .filter(|p| !p.is_global()) + .filter_map(|obligation| { + if let ty::Predicate::Trait(poly_trait_ref, _) = obligation.predicate { + if poly_trait_ref.def_id() == sized_trait || poly_trait_ref.skip_binder().has_escaping_bound_vars() + { + return None; + } + Some(poly_trait_ref) + } else { + None + } + }) + .collect::>(); + + // Collect moved variables and spans which will need dereferencings from the + // function body. + let MovedVariablesCtxt { + moved_vars, + spans_need_deref, + .. + } = { + let mut ctx = MovedVariablesCtxt::default(); + cx.tcx.infer_ctxt().enter(|infcx| { + euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body); + }); + ctx + }; + + let fn_sig = cx.tcx.fn_sig(fn_def_id); + let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); + + for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() { + // All spans generated from a proc-macro invocation are the same... + if span == input.span { + return; + } + + // Ignore `self`s. + if idx == 0 { + if let PatKind::Binding(.., ident, _) = arg.pat.kind { + if ident.as_str() == "self" { + continue; + } + } + } + + // + // * Exclude a type that is specifically bounded by `Borrow`. + // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`, + // `serde::Serialize`) + let (implements_borrow_trait, all_borrowable_trait) = { + let preds = preds + .iter() + .filter(|t| t.skip_binder().self_ty() == ty) + .collect::>(); + + ( + preds.iter().any(|t| t.def_id() == borrow_trait), + !preds.is_empty() && { + let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty); + preds.iter().all(|t| { + let ty_params = &t + .skip_binder() + .trait_ref + .substs + .iter() + .skip(1) + .cloned() + .collect::>(); + implements_trait(cx, ty_empty_region, t.def_id(), ty_params) + }) + }, + ) + }; + + if_chain! { + if !is_self(arg); + if !ty.is_mutable_ptr(); + if !is_copy(cx, ty); + if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])); + if !implements_borrow_trait; + if !all_borrowable_trait; + + if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind; + if !moved_vars.contains(&canonical_id); + then { + if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut { + continue; + } + + // Dereference suggestion + let sugg = |diag: &mut DiagnosticBuilder<'_>| { + if let ty::Adt(def, ..) = ty.kind { + if let Some(span) = cx.tcx.hir().span_if_local(def.did) { + if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() { + diag.span_help(span, "consider marking this type as `Copy`"); + } + } + } + + let deref_span = spans_need_deref.get(&canonical_id); + if_chain! { + if is_type_diagnostic_item(cx, ty, sym!(vec_type)); + if let Some(clone_spans) = + get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); + if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind; + if let Some(elem_ty) = path.segments.iter() + .find(|seg| seg.ident.name == sym!(Vec)) + .and_then(|ps| ps.args.as_ref()) + .map(|params| params.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }).unwrap()); + then { + let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_")); + diag.span_suggestion( + input.span, + "consider changing the type to", + slice_ty, + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { + diag.span_suggestion( + span, + &snippet_opt(cx, span) + .map_or( + "change the call to".into(), + |x| Cow::from(format!("change `{}` to", x)), + ), + suggestion.into(), + Applicability::Unspecified, + ); + } + + // cannot be destructured, no need for `*` suggestion + assert!(deref_span.is_none()); + return; + } + } + + if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if let Some(clone_spans) = + get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { + diag.span_suggestion( + input.span, + "consider changing the type to", + "&str".to_string(), + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { + diag.span_suggestion( + span, + &snippet_opt(cx, span) + .map_or( + "change the call to".into(), + |x| Cow::from(format!("change `{}` to", x)) + ), + suggestion.into(), + Applicability::Unspecified, + ); + } + + assert!(deref_span.is_none()); + return; + } + } + + let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; + + // Suggests adding `*` to dereference the added reference. + if let Some(deref_span) = deref_span { + spans.extend( + deref_span + .iter() + .cloned() + .map(|span| (span, format!("*{}", snippet(cx, span, "")))), + ); + spans.sort_by_key(|&(span, _)| span); + } + multispan_sugg(diag, "consider taking a reference instead", spans); + }; + + span_lint_and_then( + cx, + NEEDLESS_PASS_BY_VALUE, + input.span, + "this argument is passed by value, but not consumed in the function body", + sugg, + ); + } + } + } + } +} + +/// Functions marked with these attributes must have the exact signature. +fn requires_exact_signature(attrs: &[Attribute]) -> bool { + attrs.iter().any(|attr| { + [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)] + .iter() + .any(|&allow| attr.check_name(allow)) + }) +} + +#[derive(Default)] +struct MovedVariablesCtxt { + moved_vars: FxHashSet, + /// Spans which need to be prefixed with `*` for dereferencing the + /// suggested additional reference. + spans_need_deref: FxHashMap>, +} + +impl MovedVariablesCtxt { + fn move_common(&mut self, cmt: &euv::Place<'_>) { + if let euv::PlaceBase::Local(vid) = cmt.base { + self.moved_vars.insert(vid); + } + } +} + +impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { + fn consume(&mut self, cmt: &euv::Place<'tcx>, mode: euv::ConsumeMode) { + if let euv::ConsumeMode::Move = mode { + self.move_common(cmt); + } + } + + fn borrow(&mut self, _: &euv::Place<'tcx>, _: ty::BorrowKind) {} + + fn mutate(&mut self, _: &euv::Place<'tcx>) {} +} diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs new file mode 100644 index 0000000000000..4b2586877e562 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -0,0 +1,53 @@ +use crate::utils::span_lint; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for needlessly including a base struct on update + /// when all fields are changed anyway. + /// + /// **Why is this bad?** This will cost resources (because the base has to be + /// somewhere), and make the code less readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct Point { + /// # x: i32, + /// # y: i32, + /// # z: i32, + /// # } + /// # let zero_point = Point { x: 0, y: 0, z: 0 }; + /// Point { + /// x: 1, + /// y: 1, + /// ..zero_point + /// }; + /// ``` + pub NEEDLESS_UPDATE, + complexity, + "using `Foo { ..base }` when there are no missing fields" +} + +declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessUpdate { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { + let ty = cx.tables.expr_ty(expr); + if let ty::Adt(def, _) = ty.kind { + if fields.len() == def.non_enum_variant().fields.len() { + 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_cmp_op_on_partial_ord.rs b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs new file mode 100644 index 0000000000000..54536ed57d3e9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -0,0 +1,91 @@ +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{self, paths, span_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for the usage of negated comparison operators on types which only implement + /// `PartialOrd` (e.g., `f64`). + /// + /// **Why is this bad?** + /// These operators make it easy to forget that the underlying types actually allow not only three + /// potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is + /// especially easy to miss if the operator based comparison result is negated. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::cmp::Ordering; + /// + /// // Bad + /// let a = 1.0; + /// let b = f64::NAN; + /// + /// let _not_less_or_equal = !(a <= b); + /// + /// // Good + /// let a = 1.0; + /// let b = f64::NAN; + /// + /// let _not_less_or_equal = match a.partial_cmp(&b) { + /// None | Some(Ordering::Greater) => true, + /// _ => false, + /// }; + /// ``` + pub NEG_CMP_OP_ON_PARTIAL_ORD, + complexity, + "The use of negated comparison operators on partially ordered types may produce confusing code." +} + +declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NoNegCompOpForPartialOrd { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Unary(UnOp::UnNot, ref inner) = expr.kind; + if let ExprKind::Binary(ref op, ref left, _) = inner.kind; + if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; + + then { + + let ty = cx.tables.expr_ty(left); + + let implements_ord = { + if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) { + utils::implements_trait(cx, ty, id, &[]) + } else { + return; + } + }; + + let implements_partial_ord = { + if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { + utils::implements_trait(cx, ty, id, &[]) + } else { + return; + } + }; + + if implements_partial_ord && !implements_ord { + span_lint( + cx, + NEG_CMP_OP_ON_PARTIAL_ORD, + expr.span, + "The use of negated comparison operators on partially ordered \ + types produces code that is hard to read and refactor. Please \ + consider using the `partial_cmp` method instead, to make it \ + clear that the two values could be incomparable." + ) + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs new file mode 100644 index 0000000000000..4681e990df88a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -0,0 +1,53 @@ +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +use crate::consts::{self, Constant}; +use crate::utils::span_lint; + +declare_clippy_lint! { + /// **What it does:** Checks for multiplication by -1 as a form of negation. + /// + /// **Why is this bad?** It's more readable to just negate. + /// + /// **Known problems:** This only catches integers (for now). + /// + /// **Example:** + /// ```ignore + /// x * -1 + /// ``` + pub NEG_MULTIPLY, + style, + "multiplying integers with `-1`" +} + +declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); + +#[allow(clippy::match_same_arms)] +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref op, ref left, ref right) = e.kind { + if BinOpKind::Mul == op.node { + match (&left.kind, &right.kind) { + (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, + (&ExprKind::Unary(UnOp::UnNeg, ref lit), _) => check_mul(cx, e.span, lit, right), + (_, &ExprKind::Unary(UnOp::UnNeg, ref lit)) => check_mul(cx, e.span, lit, left), + _ => {}, + } + } + } + } +} + +fn check_mul(cx: &LateContext<'_, '_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { + if_chain! { + if let ExprKind::Lit(ref l) = lit.kind; + if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.tables.expr_ty_opt(lit)); + if cx.tables.expr_ty(exp).is_integral(); + then { + span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`"); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs new file mode 100644 index 0000000000000..a599667b8d8a8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -0,0 +1,234 @@ +use crate::utils::paths; +use crate::utils::sugg::DiagnosticBuilderExt; +use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::HirIdSet; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for types with a `fn new() -> Self` method and no + /// implementation of + /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html). + /// + /// It detects both the case when a manual + /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) + /// implementation is required and also when it can be created with + /// `#[derive(Default)]` + /// + /// **Why is this bad?** The user might expect to be able to use + /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the + /// type can be constructed without arguments. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// + /// ```ignore + /// struct Foo(Bar); + /// + /// impl Foo { + /// fn new() -> Self { + /// Foo(Bar::new()) + /// } + /// } + /// ``` + /// + /// Instead, use: + /// + /// ```ignore + /// struct Foo(Bar); + /// + /// impl Default for Foo { + /// fn default() -> Self { + /// Foo(Bar::new()) + /// } + /// } + /// ``` + /// + /// Or, if + /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) + /// can be derived by `#[derive(Default)]`: + /// + /// ```rust + /// struct Foo; + /// + /// impl Foo { + /// fn new() -> Self { + /// Foo + /// } + /// } + /// ``` + /// + /// Instead, use: + /// + /// ```rust + /// #[derive(Default)] + /// struct Foo; + /// + /// impl Foo { + /// fn new() -> Self { + /// Foo + /// } + /// } + /// ``` + /// + /// You can also have `new()` call `Default::default()`. + pub NEW_WITHOUT_DEFAULT, + style, + "`fn new() -> Self` method without `Default` implementation" +} + +#[derive(Clone, Default)] +pub struct NewWithoutDefault { + impling_types: Option, +} + +impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { + #[allow(clippy::too_many_lines)] + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { + if let hir::ItemKind::Impl { + of_trait: None, items, .. + } = item.kind + { + for assoc_item in items { + if let hir::AssocItemKind::Fn { has_self: false } = assoc_item.kind { + let impl_item = cx.tcx.hir().impl_item(assoc_item.id); + if in_external_macro(cx.sess(), impl_item.span) { + return; + } + if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { + let name = impl_item.ident.name; + let id = impl_item.hir_id; + if sig.header.constness == hir::Constness::Const { + // can't be implemented by default + return; + } + if sig.header.unsafety == hir::Unsafety::Unsafe { + // can't be implemented for unsafe new + return; + } + if impl_item.generics.params.iter().any(|gen| match gen.kind { + hir::GenericParamKind::Type { .. } => true, + _ => false, + }) { + // when the result of `new()` depends on a type parameter we should not require + // an + // impl of `Default` + return; + } + if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) { + let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); + let self_ty = cx.tcx.type_of(self_did); + if_chain! { + if same_tys(cx, self_ty, return_ty(cx, id)); + if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); + then { + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + cx.tcx.for_each_impl(default_trait_id, |d| { + if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { + if let Some(local_def_id) = ty_def.did.as_local() { + impls.insert(cx.tcx.hir().as_local_hir_id(local_def_id)); + } + } + }); + self.impling_types = Some(impls); + } + + // Check if a Default implementation exists for the Self type, regardless of + // generics + if_chain! { + if let Some(ref impling_types) = self.impling_types; + if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def(); + if let Some(self_def_id) = self_def.did.as_local(); + then { + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id); + if impling_types.contains(&self_id) { + return; + } + } + } + + if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) { + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id, + impl_item.span, + &format!( + "you should consider deriving a `Default` implementation for `{}`", + self_ty + ), + |diag| { + diag.suggest_item_with_attr( + cx, + sp, + "try this", + "#[derive(Default)]", + Applicability::MaybeIncorrect, + ); + }); + } else { + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id, + impl_item.span, + &format!( + "you should consider adding a `Default` implementation for `{}`", + self_ty + ), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try this", + &create_new_without_default_suggest_msg(self_ty), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } + } + } + } + } + } +} + +fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String { + #[rustfmt::skip] + format!( +"impl Default for {} {{ + fn default() -> Self {{ + Self::new() + }} +}}", ty) +} + +fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option { + match ty.kind { + ty::Adt(adt_def, substs) if adt_def.is_struct() => { + for field in adt_def.all_fields() { + let f_ty = field.ty(cx.tcx, substs); + if !implements_trait(cx, f_ty, default_trait_id, &[]) { + return None; + } + } + Some(cx.tcx.def_span(adt_def.did)) + }, + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs new file mode 100644 index 0000000000000..b8bfa676a1608 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -0,0 +1,177 @@ +use crate::utils::{has_drop, qpath_res, snippet_opt, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::ops::Deref; + +declare_clippy_lint! { + /// **What it does:** Checks for statements which have no effect. + /// + /// **Why is this bad?** Similar to dead code, these statements are actually + /// executed. However, as they have no effect, all they do is make the code less + /// readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// 0; + /// ``` + pub NO_EFFECT, + complexity, + "statements with no effect" +} + +declare_clippy_lint! { + /// **What it does:** Checks for expression statements that can be reduced to a + /// sub-expression. + /// + /// **Why is this bad?** Expressions by themselves often have no side-effects. + /// Having such expressions reduces readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// compute_array()[0]; + /// ``` + pub UNNECESSARY_OPERATION, + complexity, + "outer expressions with no effect" +} + +fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + if expr.span.from_expansion() { + return false; + } + match expr.kind { + ExprKind::Lit(..) | ExprKind::Closure(..) => true, + ExprKind::Path(..) => !has_drop(cx, cx.tables.expr_ty(expr)), + ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => { + has_no_effect(cx, a) && has_no_effect(cx, b) + }, + ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)), + ExprKind::Repeat(ref inner, _) + | ExprKind::Cast(ref inner, _) + | ExprKind::Type(ref inner, _) + | ExprKind::Unary(_, ref inner) + | ExprKind::Field(ref inner, _) + | ExprKind::AddrOf(_, _, ref inner) + | ExprKind::Box(ref inner) => has_no_effect(cx, inner), + ExprKind::Struct(_, ref fields, ref base) => { + !has_drop(cx, cx.tables.expr_ty(expr)) + && fields.iter().all(|field| has_no_effect(cx, &field.expr)) + && base.as_ref().map_or(true, |base| has_no_effect(cx, base)) + }, + ExprKind::Call(ref callee, ref args) => { + if let ExprKind::Path(ref qpath) = callee.kind { + let res = qpath_res(cx, qpath, callee.hir_id); + match res { + Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) => { + !has_drop(cx, cx.tables.expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg)) + }, + _ => false, + } + } else { + false + } + }, + ExprKind::Block(ref block, _) => { + block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr)) + }, + _ => false, + } +} + +declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NoEffect { + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Semi(ref expr) = stmt.kind { + if has_no_effect(cx, expr) { + span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect"); + } else if let Some(reduced) = reduce_expression(cx, expr) { + let mut snippet = String::new(); + for e in reduced { + if e.span.from_expansion() { + return; + } + if let Some(snip) = snippet_opt(cx, e.span) { + snippet.push_str(&snip); + snippet.push(';'); + } else { + return; + } + } + span_lint_and_sugg( + cx, + UNNECESSARY_OPERATION, + stmt.span, + "statement can be reduced", + "replace it with", + snippet, + Applicability::MachineApplicable, + ); + } + } + } +} + +fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option>> { + if expr.span.from_expansion() { + return None; + } + match expr.kind { + ExprKind::Index(ref a, ref b) => Some(vec![&**a, &**b]), + ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { + Some(vec![&**a, &**b]) + }, + ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()), + ExprKind::Repeat(ref inner, _) + | ExprKind::Cast(ref inner, _) + | ExprKind::Type(ref inner, _) + | ExprKind::Unary(_, ref inner) + | ExprKind::Field(ref inner, _) + | ExprKind::AddrOf(_, _, ref inner) + | ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Struct(_, ref fields, ref base) => { + if has_drop(cx, cx.tables.expr_ty(expr)) { + None + } else { + Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()) + } + }, + ExprKind::Call(ref callee, ref args) => { + if let ExprKind::Path(ref qpath) = callee.kind { + let res = qpath_res(cx, qpath, callee.hir_id); + match res { + Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(..), _) + if !has_drop(cx, cx.tables.expr_ty(expr)) => + { + Some(args.iter().collect()) + }, + _ => None, + } + } else { + None + } + }, + ExprKind::Block(ref block, _) => { + if block.stmts.is_empty() { + block.expr.as_ref().and_then(|e| { + match block.rules { + BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None, + BlockCheckMode::DefaultBlock => Some(vec![&**e]), + // in case of compiler-inserted signaling blocks + _ => reduce_expression(cx, e), + } + }) + } else { + None + } + }, + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs new file mode 100644 index 0000000000000..bb257e5a542d9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -0,0 +1,261 @@ +//! Checks for uses of const which the type is not `Freeze` (`Cell`-free). +//! +//! This lint is **deny** by default. + +use std::ptr; + +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::{Ty, TypeFlags}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{InnerSpan, Span, DUMMY_SP}; +use rustc_typeck::hir_ty_to_ty; + +use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for declaration of `const` items which is interior + /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). + /// + /// **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. + /// + /// 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. + /// + /// **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. + /// + /// **Example:** + /// ```rust + /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + /// + /// // Bad. + /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); + /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged + /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct + /// + /// // Good. + /// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15); + /// STATIC_ATOM.store(9, SeqCst); + /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance + /// ``` + pub DECLARE_INTERIOR_MUTABLE_CONST, + correctness, + "declaring `const` with interior mutability" +} + +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. + /// + /// **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. + /// + /// The `const` value should be stored inside a `static` item. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); + /// + /// // Bad. + /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged + /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct + /// + /// // Good. + /// static STATIC_ATOM: AtomicUsize = CONST_ATOM; + /// STATIC_ATOM.store(9, SeqCst); + /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance + /// ``` + pub BORROW_INTERIOR_MUTABLE_CONST, + correctness, + "referencing `const` with interior mutability" +} + +#[allow(dead_code)] +#[derive(Copy, Clone)] +enum Source { + Item { item: Span }, + Assoc { item: Span, ty: Span }, + Expr { expr: Span }, +} + +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 never be interior mutable", + *item, + ), + Self::Expr { expr } => ( + BORROW_INTERIOR_MUTABLE_CONST, + "a `const` item with interior mutability should not be borrowed", + *expr, + ), + } + } +} + +fn verify_ty_bound<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, source: Source) { + if ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) || is_copy(cx, ty) { + // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which + // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze` + // as well. + return; + } + + 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 { .. } => { + let const_kw_span = span.from_inner(InnerSpan::new(0, 5)); + diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); + }, + Source::Assoc { ty: ty_span, .. } => { + if ty.flags.intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { + diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty)); + } + }, + Source::Expr { .. } => { + diag.help("assign this const to a local or static variable, and use the variable here"); + }, + } + }); +} + +declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCopyConst { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx Item<'_>) { + if let ItemKind::Const(hir_ty, ..) = &it.kind { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + verify_ty_bound(cx, ty, Source::Item { item: it.span }); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + verify_ty_bound( + cx, + ty, + Source::Assoc { + ty: hir_ty.span, + item: trait_item.span, + }, + ); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind { + let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id); + let item = cx.tcx.hir().expect_item(item_hir_id); + // Ensure the impl is an inherent impl. + if let ItemKind::Impl { of_trait: None, .. } = item.kind { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + verify_ty_bound( + cx, + ty, + Source::Assoc { + ty: hir_ty.span, + item: impl_item.span, + }, + ); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Path(qpath) = &expr.kind { + // Only lint if we use the const item inside a function. + if in_constant(cx, expr.hir_id) { + return; + } + + // Make sure it is a const item. + match qpath_res(cx, qpath, expr.hir_id) { + Res::Def(DefKind::Const | DefKind::AssocConst, _) => {}, + _ => 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; + loop { + let parent_id = cx.tcx.hir().get_parent_node(cur_expr.hir_id); + if parent_id == cur_expr.hir_id { + break; + } + if let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find(parent_id) { + match &parent_expr.kind { + ExprKind::AddrOf(..) => { + // `&e` => `e` must be referenced. + needs_check_adjustment = false; + }, + ExprKind::Field(..) => { + dereferenced_expr = parent_expr; + needs_check_adjustment = true; + }, + 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::UnDeref, _) => { + // `*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; + }, + _ => break, + } + cur_expr = parent_expr; + } else { + break; + } + } + + let ty = if needs_check_adjustment { + let adjustments = cx.tables.expr_adjustments(dereferenced_expr); + if let Some(i) = adjustments.iter().position(|adj| match adj.kind { + Adjust::Borrow(_) | Adjust::Deref(_) => true, + _ => false, + }) { + if i == 0 { + cx.tables.expr_ty(dereferenced_expr) + } else { + adjustments[i - 1].target + } + } else { + // No borrow adjustments means the entire const is moved. + return; + } + } else { + cx.tables.expr_ty(dereferenced_expr) + }; + + verify_ty_bound(cx, ty, Source::Expr { expr: expr.span }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs new file mode 100644 index 0000000000000..2b51b73207585 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -0,0 +1,410 @@ +use crate::utils::{span_lint, span_lint_and_then}; +use rustc_ast::ast::{ + Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind, +}; +use rustc_ast::attr; +use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; +use rustc_span::symbol::{Ident, SymbolStr}; +use std::cmp::Ordering; + +declare_clippy_lint! { + /// **What it does:** Checks for names that are very similar and thus confusing. + /// + /// **Why is this bad?** It's hard to distinguish between names that differ only + /// by a single character. + /// + /// **Known problems:** None? + /// + /// **Example:** + /// ```ignore + /// let checked_exp = something; + /// let checked_expr = something_else; + /// ``` + pub SIMILAR_NAMES, + pedantic, + "similarly named items and bindings" +} + +declare_clippy_lint! { + /// **What it does:** Checks for too many variables whose name consists of a + /// single character. + /// + /// **Why is this bad?** It's hard to memorize what a variable means without a + /// descriptive name. + /// + /// **Known problems:** None? + /// + /// **Example:** + /// ```ignore + /// let (a, b, c, d, e, f, g) = (...); + /// ``` + pub MANY_SINGLE_CHAR_NAMES, + style, + "too many single character bindings" +} + +declare_clippy_lint! { + /// **What it does:** Checks if you have variables whose name consists of just + /// underscores and digits. + /// + /// **Why is this bad?** It's hard to memorize what a variable means without a + /// descriptive name. + /// + /// **Known problems:** None? + /// + /// **Example:** + /// ```rust + /// let _1 = 1; + /// let ___1 = 1; + /// let __1___2 = 11; + /// ``` + pub JUST_UNDERSCORES_AND_DIGITS, + style, + "unclear name" +} + +#[derive(Copy, Clone)] +pub struct NonExpressiveNames { + pub single_char_binding_names_threshold: u64, +} + +impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]); + +struct ExistingName { + interned: SymbolStr, + span: Span, + len: usize, + whitelist: &'static [&'static str], +} + +struct SimilarNamesLocalVisitor<'a, 'tcx> { + names: Vec, + cx: &'a EarlyContext<'tcx>, + lint: &'a NonExpressiveNames, + + /// A stack of scopes containing the single-character bindings in each scope. + single_char_names: Vec>, +} + +impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { + fn check_single_char_names(&self) { + let num_single_char_names = self.single_char_names.iter().flatten().count(); + let threshold = self.lint.single_char_binding_names_threshold; + if num_single_char_names as u64 > threshold { + let span = self + .single_char_names + .iter() + .flatten() + .map(|ident| ident.span) + .collect::>(); + span_lint( + self.cx, + MANY_SINGLE_CHAR_NAMES, + span, + &format!( + "{} bindings with single-character names in scope", + num_single_char_names + ), + ); + } + } +} + +// this list contains lists of names that are allowed to be similar +// the assumption is that no name is ever contained in multiple lists. +#[rustfmt::skip] +const WHITELIST: &[&[&str]] = &[ + &["parsed", "parser"], + &["lhs", "rhs"], + &["tx", "rx"], + &["set", "get"], + &["args", "arms"], + &["qpath", "path"], + &["lit", "lint"], +]; + +struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>); + +impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { + fn visit_pat(&mut self, pat: &'tcx Pat) { + match pat.kind { + PatKind::Ident(_, ident, _) => self.check_ident(ident), + PatKind::Struct(_, ref fields, _) => { + for field in fields { + if !field.is_shorthand { + self.visit_pat(&field.pat); + } + } + }, + // just go through the first pattern, as either all patterns + // bind the same bindings or rustc would have errored much earlier + PatKind::Or(ref pats) => self.visit_pat(&pats[0]), + _ => walk_pat(self, pat), + } + } + fn visit_mac(&mut self, _mac: &MacCall) { + // do not check macs + } +} + +#[must_use] +fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> { + for &allow in WHITELIST { + if whitelisted(interned_name, allow) { + return Some(allow); + } + } + None +} + +#[must_use] +fn whitelisted(interned_name: &str, list: &[&str]) -> bool { + list.iter() + .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name)) +} + +impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { + fn check_short_ident(&mut self, ident: Ident) { + // Ignore shadowing + if self + .0 + .single_char_names + .iter() + .flatten() + .any(|id| id.name == ident.name) + { + return; + } + + if let Some(scope) = &mut self.0.single_char_names.last_mut() { + scope.push(ident); + } + } + + #[allow(clippy::too_many_lines)] + fn check_ident(&mut self, ident: Ident) { + let interned_name = ident.name.as_str(); + if interned_name.chars().any(char::is_uppercase) { + return; + } + if interned_name.chars().all(|c| c.is_digit(10) || c == '_') { + span_lint( + self.0.cx, + JUST_UNDERSCORES_AND_DIGITS, + ident.span, + "consider choosing a more descriptive name", + ); + return; + } + let count = interned_name.chars().count(); + if count < 3 { + if count == 1 { + self.check_short_ident(ident); + } + return; + } + for existing_name in &self.0.names { + if whitelisted(&interned_name, existing_name.whitelist) { + continue; + } + let mut split_at = None; + match existing_name.len.cmp(&count) { + Ordering::Greater => { + if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) { + continue; + } + }, + Ordering::Less => { + if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) { + continue; + } + }, + Ordering::Equal => { + let mut interned_chars = interned_name.chars(); + let mut existing_chars = existing_name.interned.chars(); + let first_i = interned_chars.next().expect("we know we have at least one char"); + let first_e = existing_chars.next().expect("we know we have at least one char"); + let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric(); + + if eq_or_numeric((first_i, first_e)) { + let last_i = interned_chars.next_back().expect("we know we have at least two chars"); + let last_e = existing_chars.next_back().expect("we know we have at least two chars"); + if eq_or_numeric((last_i, last_e)) { + if interned_chars + .zip(existing_chars) + .filter(|&ie| !eq_or_numeric(ie)) + .count() + != 1 + { + continue; + } + } else { + let second_last_i = interned_chars + .next_back() + .expect("we know we have at least three chars"); + let second_last_e = existing_chars + .next_back() + .expect("we know we have at least three chars"); + if !eq_or_numeric((second_last_i, second_last_e)) + || second_last_i == '_' + || !interned_chars.zip(existing_chars).all(eq_or_numeric) + { + // allowed similarity foo_x, foo_y + // or too many chars differ (foo_x, boo_y) or (foox, booy) + continue; + } + split_at = interned_name.char_indices().rev().next().map(|(i, _)| i); + } + } else { + let second_i = interned_chars.next().expect("we know we have at least two chars"); + let second_e = existing_chars.next().expect("we know we have at least two chars"); + if !eq_or_numeric((second_i, second_e)) + || second_i == '_' + || !interned_chars.zip(existing_chars).all(eq_or_numeric) + { + // allowed similarity x_foo, y_foo + // or too many chars differ (x_foo, y_boo) or (xfoo, yboo) + continue; + } + split_at = interned_name.chars().next().map(char::len_utf8); + } + }, + } + span_lint_and_then( + self.0.cx, + SIMILAR_NAMES, + ident.span, + "binding's name is too similar to existing binding", + |diag| { + diag.span_note(existing_name.span, "existing binding defined here"); + if let Some(split) = split_at { + diag.span_help( + ident.span, + &format!( + "separate the discriminating character by an \ + underscore like: `{}_{}`", + &interned_name[..split], + &interned_name[split..] + ), + ); + } + }, + ); + return; + } + self.0.names.push(ExistingName { + whitelist: get_whitelist(&interned_name).unwrap_or(&[]), + interned: interned_name, + span: ident.span, + len: count, + }); + } +} + +impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> { + /// ensure scoping rules work + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + let n = self.names.len(); + let single_char_count = self.single_char_names.len(); + f(self); + self.names.truncate(n); + self.single_char_names.truncate(single_char_count); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { + fn visit_local(&mut self, local: &'tcx Local) { + if let Some(ref init) = local.init { + self.apply(|this| walk_expr(this, &**init)); + } + // add the pattern after the expression because the bindings aren't available + // yet in the init + // expression + SimilarNamesNameVisitor(self).visit_pat(&*local.pat); + } + fn visit_block(&mut self, blk: &'tcx Block) { + self.single_char_names.push(vec![]); + + self.apply(|this| walk_block(this, blk)); + + self.check_single_char_names(); + self.single_char_names.pop(); + } + fn visit_arm(&mut self, arm: &'tcx Arm) { + self.single_char_names.push(vec![]); + + self.apply(|this| { + SimilarNamesNameVisitor(this).visit_pat(&arm.pat); + this.apply(|this| walk_expr(this, &arm.body)); + }); + + self.check_single_char_names(); + self.single_char_names.pop(); + } + fn visit_item(&mut self, _: &Item) { + // do not recurse into inner items + } + fn visit_mac(&mut self, _mac: &MacCall) { + // do not check macs + } +} + +impl EarlyLintPass for NonExpressiveNames { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { + do_check(self, cx, &item.attrs, &sig.decl, blk); + } + } + + fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) { + if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { + do_check(self, cx, &item.attrs, &sig.decl, blk); + } + } +} + +fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { + if !attr::contains_name(attrs, sym!(test)) { + let mut visitor = SimilarNamesLocalVisitor { + names: Vec::new(), + cx, + lint, + single_char_names: vec![vec![]], + }; + + // initialize with function arguments + for arg in &decl.inputs { + SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat); + } + // walk all other bindings + walk_block(&mut visitor, blk); + + visitor.check_single_char_names(); + } +} + +/// Precondition: `a_name.chars().count() < b_name.chars().count()`. +#[must_use] +fn levenstein_not_1(a_name: &str, b_name: &str) -> bool { + debug_assert!(a_name.chars().count() < b_name.chars().count()); + let mut a_chars = a_name.chars(); + let mut b_chars = b_name.chars(); + while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) { + if a == b { + continue; + } + if let Some(b2) = b_chars.next() { + // check if there's just one character inserted + return a != b2 || a_chars.ne(b_chars); + } else { + // tuple + // ntuple + return true; + } + } + // for item in items + true +} diff --git a/src/tools/clippy/clippy_lints/src/open_options.rs b/src/tools/clippy/clippy_lints/src/open_options.rs new file mode 100644 index 0000000000000..9d3b67988dbb5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/open_options.rs @@ -0,0 +1,203 @@ +use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty}; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{Span, Spanned}; + +declare_clippy_lint! { + /// **What it does:** Checks for duplicate open options as well as combinations + /// that make no sense. + /// + /// **Why is this bad?** In the best case, the code will be harder to read than + /// necessary. I don't know the worst case. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// use std::fs::OpenOptions; + /// + /// OpenOptions::new().read(true).truncate(true); + /// ``` + pub NONSENSICAL_OPEN_OPTIONS, + correctness, + "nonsensical combination of options for opening a file" +} + +declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OpenOptions { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(ref path, _, ref arguments) = e.kind { + let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0])); + if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { + let mut options = Vec::new(); + get_open_options(cx, &arguments[0], &mut options); + check_open_options(cx, &options, e.span); + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum Argument { + True, + False, + Unknown, +} + +#[derive(Debug)] +enum OpenOption { + Write, + Read, + Truncate, + Create, + Append, +} + +fn get_open_options(cx: &LateContext<'_, '_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) { + if let ExprKind::MethodCall(ref path, _, ref arguments) = argument.kind { + let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0])); + + // Only proceed if this is a call on some object of type std::fs::OpenOptions + if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 { + let argument_option = match arguments[1].kind { + ExprKind::Lit(ref span) => { + if let Spanned { + node: LitKind::Bool(lit), + .. + } = *span + { + if lit { + Argument::True + } else { + Argument::False + } + } else { + return; // The function is called with a literal + // which is not a boolean literal. This is theoretically + // possible, but not very likely. + } + }, + _ => Argument::Unknown, + }; + + match &*path.ident.as_str() { + "create" => { + options.push((OpenOption::Create, argument_option)); + }, + "append" => { + options.push((OpenOption::Append, argument_option)); + }, + "truncate" => { + options.push((OpenOption::Truncate, argument_option)); + }, + "read" => { + options.push((OpenOption::Read, argument_option)); + }, + "write" => { + options.push((OpenOption::Write, argument_option)); + }, + _ => (), + } + + get_open_options(cx, &arguments[0], options); + } + } +} + +fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument)], span: Span) { + let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false); + let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) = + (false, false, false, false, false); + // This code is almost duplicated (oh, the irony), but I haven't found a way to + // unify it. + + for option in options { + match *option { + (OpenOption::Create, arg) => { + if create { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + span, + "the method `create` is called more than once", + ); + } else { + create = true + } + create_arg = create_arg || (arg == Argument::True); + }, + (OpenOption::Append, arg) => { + if append { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + span, + "the method `append` is called more than once", + ); + } else { + append = true + } + append_arg = append_arg || (arg == Argument::True); + }, + (OpenOption::Truncate, arg) => { + if truncate { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + span, + "the method `truncate` is called more than once", + ); + } else { + truncate = true + } + truncate_arg = truncate_arg || (arg == Argument::True); + }, + (OpenOption::Read, arg) => { + if read { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + span, + "the method `read` is called more than once", + ); + } else { + read = true + } + read_arg = read_arg || (arg == Argument::True); + }, + (OpenOption::Write, arg) => { + if write { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + span, + "the method `write` is called more than once", + ); + } else { + write = true + } + write_arg = write_arg || (arg == Argument::True); + }, + } + } + + if read && truncate && read_arg && truncate_arg && !(write && write_arg) { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + span, + "file opened with `truncate` and `read`", + ); + } + if append && truncate && append_arg && truncate_arg { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + span, + "file opened with `append` and `truncate`", + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs new file mode 100644 index 0000000000000..66dfa20edb5e7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs @@ -0,0 +1,55 @@ +use crate::utils::{is_direct_expn_of, span_lint_and_help}; +use if_chain::if_chain; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `option_env!(...).unwrap()` and + /// suggests usage of the `env!` macro. + /// + /// **Why is this bad?** Unwrapping the result of `option_env!` will panic + /// at run-time if the environment variable doesn't exist, whereas `env!` + /// catches it at compile-time. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// let _ = option_env!("HOME").unwrap(); + /// ``` + /// + /// Is better expressed as: + /// + /// ```rust,no_run + /// let _ = env!("HOME"); + /// ``` + pub OPTION_ENV_UNWRAP, + correctness, + "using `option_env!(...).unwrap()` to get environment variable" +} + +declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); + +impl EarlyLintPass for OptionEnvUnwrap { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if_chain! { + if let ExprKind::MethodCall(path_segment, args) = &expr.kind; + let method_name = path_segment.ident.as_str(); + if method_name == "expect" || method_name == "unwrap"; + if let ExprKind::Call(caller, _) = &args[0].kind; + if is_direct_expn_of(caller.span, "option_env").is_some(); + then { + span_lint_and_help( + cx, + OPTION_ENV_UNWRAP, + expr.span, + "this will panic at run-time if the environment variable doesn't exist at compile-time", + None, + "consider using the `env!` macro instead" + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs new file mode 100644 index 0000000000000..b90fdc232e72c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs @@ -0,0 +1,82 @@ +use crate::utils::{span_lint, SpanlessEq}; +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects classic underflow/overflow checks. + /// + /// **Why is this bad?** Most classic C underflow/overflow checks will fail in + /// Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let a = 1; + /// # let b = 2; + /// a + b < a; + /// ``` + pub OVERFLOW_CHECK_CONDITIONAL, + complexity, + "overflow checks inspired by C which are likely to panic" +} + +declare_lint_pass!(OverflowCheckConditional => [OVERFLOW_CHECK_CONDITIONAL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OverflowCheckConditional { + // a + b < a, a > a + b, a < a - b, a - b > a + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r); + if_chain! { + if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; + if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = first.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path3)) = second.kind; + if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); + if cx.tables.expr_ty(ident1).is_integral(); + if cx.tables.expr_ty(ident2).is_integral(); + then { + if let BinOpKind::Lt = op.node { + if let BinOpKind::Add = op2.node { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, + "You are trying to use classic C overflow conditions that will fail in Rust."); + } + } + if let BinOpKind::Gt = op.node { + if let BinOpKind::Sub = op2.node { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, + "You are trying to use classic C underflow conditions that will fail in Rust."); + } + } + } + } + + if_chain! { + if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; + if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = second.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path3)) = first.kind; + if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); + if cx.tables.expr_ty(ident1).is_integral(); + if cx.tables.expr_ty(ident2).is_integral(); + then { + if let BinOpKind::Gt = op.node { + if let BinOpKind::Add = op2.node { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, + "You are trying to use classic C overflow conditions that will fail in Rust."); + } + } + if let BinOpKind::Lt = op.node { + if let BinOpKind::Sub = op2.node { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, + "You are trying to use classic C underflow conditions that will fail in Rust."); + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs new file mode 100644 index 0000000000000..2cd9200ddb252 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -0,0 +1,154 @@ +use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, paths, span_lint}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for missing parameters in `panic!`. + /// + /// **Why is this bad?** Contrary to the `format!` family of macros, there are + /// two forms of `panic!`: if there are no parameters given, the first argument + /// is not a format string and used literally. So while `format!("{}")` will + /// fail to compile, `panic!("{}")` will not. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// panic!("This `panic!` is probably missing a parameter there: {}"); + /// ``` + pub PANIC_PARAMS, + style, + "missing parameters in `panic!` calls" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `panic!`. + /// + /// **Why is this bad?** `panic!` will stop the execution of the executable + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// panic!("even with a good reason"); + /// ``` + pub PANIC, + restriction, + "usage of the `panic!` macro" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `unimplemented!`. + /// + /// **Why is this bad?** This macro should not be present in production code + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// unimplemented!(); + /// ``` + pub UNIMPLEMENTED, + restriction, + "`unimplemented!` should not be present in production code" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `todo!`. + /// + /// **Why is this bad?** This macro should not be present in production code + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// todo!(); + /// ``` + pub TODO, + restriction, + "`todo!` should not be present in production code" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `unreachable!`. + /// + /// **Why is this bad?** This macro can cause code to panic + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// unreachable!(); + /// ``` + pub UNREACHABLE, + restriction, + "`unreachable!` should not be present in production code" +} + +declare_lint_pass!(PanicUnimplemented => [PANIC_PARAMS, UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PanicUnimplemented { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Block(ref block, _) = expr.kind; + if let Some(ref ex) = block.expr; + if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC); + if params.len() == 1; + then { + if is_expn_of(expr.span, "unimplemented").is_some() { + let span = get_outer_span(expr); + span_lint(cx, UNIMPLEMENTED, span, + "`unimplemented` should not be present in production code"); + } else if is_expn_of(expr.span, "todo").is_some() { + let span = get_outer_span(expr); + span_lint(cx, TODO, span, + "`todo` should not be present in production code"); + } else if is_expn_of(expr.span, "unreachable").is_some() { + let span = get_outer_span(expr); + span_lint(cx, UNREACHABLE, span, + "`unreachable` should not be present in production code"); + } else if is_expn_of(expr.span, "panic").is_some() { + let span = get_outer_span(expr); + span_lint(cx, PANIC, span, + "`panic` should not be present in production code"); + match_panic(params, expr, cx); + } + } + } + } +} + +fn get_outer_span(expr: &Expr<'_>) -> Span { + if_chain! { + if expr.span.from_expansion(); + let first = expr.span.ctxt().outer_expn_data(); + if first.call_site.from_expansion(); + let second = first.call_site.ctxt().outer_expn_data(); + then { + second.call_site + } else { + expr.span + } + } +} + +fn match_panic(params: &[Expr<'_>], expr: &Expr<'_>, cx: &LateContext<'_, '_>) { + if_chain! { + if let ExprKind::Lit(ref lit) = params[0].kind; + if is_direct_expn_of(expr.span, "panic").is_some(); + if let LitKind::Str(ref string, _) = lit.node; + let string = string.as_str().replace("{{", "").replace("}}", ""); + if let Some(par) = string.find('{'); + if string[par..].contains('}'); + if params[0].span.source_callee().is_none(); + if params[0].span.lo() != params[0].span.hi(); + then { + span_lint(cx, PANIC_PARAMS, params[0].span, + "you probably are missing some parameter in your format string"); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs new file mode 100644 index 0000000000000..1445df41c452f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -0,0 +1,55 @@ +use crate::utils::{is_automatically_derived, span_lint_hir}; +use if_chain::if_chain; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for manual re-implementations of `PartialEq::ne`. + /// + /// **Why is this bad?** `PartialEq::ne` is required to always return the + /// negated result of `PartialEq::eq`, which is exactly what the default + /// implementation does. Therefore, there should never be any need to + /// re-implement it. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// struct Foo; + /// + /// impl PartialEq for Foo { + /// fn eq(&self, other: &Foo) -> bool { true } + /// fn ne(&self, other: &Foo) -> bool { !(self == other) } + /// } + /// ``` + pub PARTIALEQ_NE_IMPL, + complexity, + "re-implementing `PartialEq::ne`" +} + +declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PartialEqNeImpl { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if_chain! { + if let ItemKind::Impl{ of_trait: Some(ref trait_ref), items: impl_items, .. } = item.kind; + if !is_automatically_derived(&*item.attrs); + if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); + if trait_ref.path.res.def_id() == eq_trait; + then { + for impl_item in impl_items { + if impl_item.ident.name == sym!(ne) { + span_lint_hir( + cx, + PARTIALEQ_NE_IMPL, + impl_item.id.hir_id, + impl_item.span, + "re-implementing `PartialEq::ne` is unnecessary", + ); + } + } + } + }; + } +} diff --git a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs new file mode 100644 index 0000000000000..bdbaf2695c8ef --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs @@ -0,0 +1,71 @@ +use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::path::{Component, Path}; + +declare_clippy_lint! { + /// **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) + /// calls on `PathBuf` that can cause overwrites. + /// + /// **Why is this bad?** Calling `push` with a root path at the start can overwrite the + /// previous defined path. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// use std::path::PathBuf; + /// + /// let mut x = PathBuf::from("/foo"); + /// x.push("/bar"); + /// assert_eq!(x, PathBuf::from("/bar")); + /// ``` + /// Could be written: + /// + /// ```rust + /// use std::path::PathBuf; + /// + /// let mut x = PathBuf::from("/foo"); + /// x.push("bar"); + /// assert_eq!(x, PathBuf::from("/foo/bar")); + /// ``` + pub PATH_BUF_PUSH_OVERWRITE, + nursery, + "calling `push` with file system root on `PathBuf` can overwrite it" +} + +declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathBufPushOverwrite { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind; + if path.ident.name == sym!(push); + if args.len() == 2; + if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::PATH_BUF); + if let Some(get_index_arg) = args.get(1); + if let ExprKind::Lit(ref lit) = get_index_arg.kind; + if let LitKind::Str(ref path_lit, _) = lit.node; + if let pushed_path = Path::new(&*path_lit.as_str()); + if let Some(pushed_path_lit) = pushed_path.to_str(); + if pushed_path.has_root(); + if let Some(root) = pushed_path.components().next(); + if root == Component::RootDir; + then { + span_lint_and_sugg( + cx, + PATH_BUF_PUSH_OVERWRITE, + lit.span, + "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "try", + format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs new file mode 100644 index 0000000000000..cc783baa68723 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -0,0 +1,164 @@ +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +const ODD_FUNCTIONS_WHITELIST: [&str; 14] = [ + "asin", + "asinh", + "atan", + "atanh", + "cbrt", + "fract", + "round", + "signum", + "sin", + "sinh", + "tan", + "tanh", + "to_degrees", + "to_radians", +]; + +declare_clippy_lint! { + /// **What it does:** Checks for operations where precedence may be unclear + /// and suggests to add parentheses. Currently it catches the following: + /// * mixed usage of arithmetic and bit shifting/combining operators without + /// parentheses + /// * a "negative" numeric literal (which is really a unary `-` followed by a + /// numeric literal) + /// followed by a method call + /// + /// **Why is this bad?** Not everyone knows the precedence of those operators by + /// heart, so expressions like these may trip others trying to reason about the + /// code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 + /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1 + pub PRECEDENCE, + complexity, + "operations where precedence may be unclear" +} + +declare_lint_pass!(Precedence => [PRECEDENCE]); + +impl EarlyLintPass for Precedence { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind { + let span_sugg = |expr: &Expr, sugg, appl| { + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "operator precedence can trip the unwary", + "consider parenthesizing your expression", + sugg, + appl, + ); + }; + + if !is_bit_op(op) { + return; + } + let mut applicability = Applicability::MachineApplicable; + match (is_arith_expr(left), is_arith_expr(right)) { + (true, true) => { + let sugg = format!( + "({}) {} ({})", + snippet_with_applicability(cx, left.span, "..", &mut applicability), + op.to_string(), + snippet_with_applicability(cx, right.span, "..", &mut applicability) + ); + span_sugg(expr, sugg, applicability); + }, + (true, false) => { + let sugg = format!( + "({}) {} {}", + snippet_with_applicability(cx, left.span, "..", &mut applicability), + op.to_string(), + snippet_with_applicability(cx, right.span, "..", &mut applicability) + ); + span_sugg(expr, sugg, applicability); + }, + (false, true) => { + let sugg = format!( + "{} {} ({})", + snippet_with_applicability(cx, left.span, "..", &mut applicability), + op.to_string(), + snippet_with_applicability(cx, right.span, "..", &mut applicability) + ); + span_sugg(expr, sugg, applicability); + }, + (false, false) => (), + } + } + + if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind { + if let ExprKind::MethodCall(ref path_segment, ref args) = rhs.kind { + let path_segment_str = path_segment.ident.name.as_str(); + if let Some(slf) = args.first() { + if let ExprKind::Lit(ref lit) = slf.kind { + match lit.kind { + LitKind::Int(..) | LitKind::Float(..) => { + if ODD_FUNCTIONS_WHITELIST + .iter() + .any(|odd_function| **odd_function == *path_segment_str) + { + return; + } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, rhs.span, "..", &mut applicability) + ), + applicability, + ); + }, + _ => (), + } + } + } + } + } + } +} + +fn is_arith_expr(expr: &Expr) -> bool { + match expr.kind { + ExprKind::Binary(Spanned { node: op, .. }, _, _) => is_arith_op(op), + _ => false, + } +} + +#[must_use] +fn is_bit_op(op: BinOpKind) -> bool { + use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr}; + match op { + BitXor | BitAnd | BitOr | Shl | Shr => true, + _ => false, + } +} + +#[must_use] +fn is_arith_op(op: BinOpKind) -> bool { + use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub}; + match op { + Add | Sub | Mul | Div | Rem => true, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs new file mode 100644 index 0000000000000..2cdf96714195a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -0,0 +1,301 @@ +//! Checks for usage of `&Vec[_]` and `&String`. + +use crate::utils::ptr::get_spans; +use crate::utils::{ + is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, + span_lint_and_then, walk_ptrs_hir_ty, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{ + BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, + Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::MultiSpan; +use std::borrow::Cow; + +declare_clippy_lint! { + /// **What it does:** This lint checks for function arguments of type `&String` + /// or `&Vec` unless the references are mutable. It will also suggest you + /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()` + /// calls. + /// + /// **Why is this bad?** Requiring the argument to be of the specific size + /// makes the function less useful for no benefit; slices in the form of `&[T]` + /// or `&str` usually suffice and can be obtained from other types, too. + /// + /// **Known problems:** The lint does not follow data. So if you have an + /// argument `x` and write `let y = x; y.clone()` the lint will not suggest + /// changing that `.clone()` to `.to_owned()`. + /// + /// Other functions called from this function taking a `&String` or `&Vec` + /// argument may also fail to compile if you change the argument. Applying + /// this lint on them will fix the problem, but they may be in other crates. + /// + /// Also there may be `fn(&Vec)`-typed references pointing to your function. + /// If you have them, you will get a compiler error after applying this lint's + /// suggestions. You then have the choice to undo your changes or change the + /// type of the reference. + /// + /// Note that if the function is part of your public interface, there may be + /// other crates referencing it you may not be aware. Carefully deprecate the + /// function before applying the lint suggestions in this case. + /// + /// **Example:** + /// ```ignore + /// fn foo(&Vec) { .. } + /// ``` + pub PTR_ARG, + style, + "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively" +} + +declare_clippy_lint! { + /// **What it does:** This lint checks for equality comparisons with `ptr::null` + /// + /// **Why is this bad?** It's easier and more readable to use the inherent + /// `.is_null()` + /// method instead + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// if x == ptr::null { + /// .. + /// } + /// ``` + pub CMP_NULL, + style, + "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead." +} + +declare_clippy_lint! { + /// **What it does:** This lint checks for functions that take immutable + /// references and return + /// mutable ones. + /// + /// **Why is this bad?** This is trivially unsound, as one can create two + /// mutable references + /// from the same (immutable!) source. This + /// [error](https://github.com/rust-lang/rust/issues/39465) + /// actually lead to an interim Rust release 1.15.1. + /// + /// **Known problems:** To be on the conservative side, if there's at least one + /// mutable reference + /// with the output lifetime, this lint will not trigger. In practice, this + /// case is unlikely anyway. + /// + /// **Example:** + /// ```ignore + /// fn foo(&Foo) -> &mut Bar { .. } + /// ``` + pub MUT_FROM_REF, + correctness, + "fns that create mutable refs from immutable ref args" +} + +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ptr { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Fn(ref sig, _, body_id) = item.kind { + check_fn(cx, &sig.decl, item.hir_id, Some(body_id)); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(ref sig, body_id) = item.kind { + let parent_item = cx.tcx.hir().get_parent_item(item.hir_id); + if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) { + if let ItemKind::Impl { of_trait: Some(_), .. } = it.kind { + return; // ignore trait impls + } + } + check_fn(cx, &sig.decl, item.hir_id, Some(body_id)); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind { + let body_id = if let TraitFn::Provided(b) = *trait_method { + Some(b) + } else { + None + }; + check_fn(cx, &sig.decl, item.hir_id, body_id); + } + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind { + if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) { + span_lint( + cx, + CMP_NULL, + expr.span, + "Comparing with null is better expressed by the `.is_null()` method", + ); + } + } + } +} + +#[allow(clippy::too_many_lines)] +fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option) { + let fn_def_id = cx.tcx.hir().local_def_id(fn_id); + let sig = cx.tcx.fn_sig(fn_def_id); + let fn_ty = sig.skip_binder(); + + for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() { + if let ty::Ref(_, ty, Mutability::Not) = ty.kind { + if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { + let mut ty_snippet = None; + if_chain! { + if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; + if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); + then { + let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }).collect(); + if types.len() == 1 { + ty_snippet = snippet_opt(cx, types[0].span); + } + } + }; + if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) { + span_lint_and_then( + cx, + PTR_ARG, + arg.span, + "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ + with non-Vec-based slices.", + |diag| { + if let Some(ref snippet) = ty_snippet { + diag.span_suggestion( + arg.span, + "change this to", + format!("&[{}]", snippet), + Applicability::Unspecified, + ); + } + for (clonespan, suggestion) in spans { + diag.span_suggestion( + clonespan, + &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { + Cow::Owned(format!("change `{}` to", x)) + }), + suggestion.into(), + Applicability::Unspecified, + ); + } + }, + ); + } + } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) { + span_lint_and_then( + cx, + PTR_ARG, + arg.span, + "writing `&String` instead of `&str` involves a new object where a slice will do.", + |diag| { + diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified); + for (clonespan, suggestion) in spans { + diag.span_suggestion_short( + clonespan, + &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { + Cow::Owned(format!("change `{}` to", x)) + }), + suggestion.into(), + Applicability::Unspecified, + ); + } + }, + ); + } + } else if match_type(cx, ty, &paths::COW) { + if_chain! { + if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; + if let TyKind::Path(ref path) = ty.kind; + if let QPath::Resolved(None, ref pp) = *path; + if let [ref bx] = *pp.segments; + if let Some(ref params) = bx.args; + if !params.parenthesized; + if let Some(inner) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + then { + let replacement = snippet_opt(cx, inner.span); + if let Some(r) = replacement { + span_lint_and_sugg( + cx, + PTR_ARG, + arg.span, + "using a reference to `Cow` is not recommended.", + "change this to", + "&".to_owned() + &r, + Applicability::Unspecified, + ); + } + } + } + } + } + } + + if let FnRetTy::Return(ref ty) = decl.output { + if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { + let mut immutables = vec![]; + for (_, ref mutbl, ref argspan) in decl + .inputs + .iter() + .filter_map(|ty| get_rptr_lm(ty)) + .filter(|&(lt, _, _)| lt.name == out.name) + { + if *mutbl == Mutability::Mut { + return; + } + immutables.push(*argspan); + } + if immutables.is_empty() { + return; + } + span_lint_and_then( + cx, + MUT_FROM_REF, + ty.span, + "mutable borrow from immutable input(s)", + |diag| { + let ms = MultiSpan::from_spans(immutables); + diag.span_note(ms, "immutable borrow here"); + }, + ); + } + } +} + +fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { + if let TyKind::Rptr(ref lt, ref m) = ty.kind { + Some((lt, m.mutbl, ty.span)) + } else { + None + } +} + +fn is_null_path(expr: &Expr<'_>) -> bool { + if let ExprKind::Call(ref pathexp, ref args) = expr.kind { + if args.is_empty() { + if let ExprKind::Path(ref path) = pathexp.kind { + return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT); + } + } + } + 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 new file mode 100644 index 0000000000000..ffc59d43750e0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -0,0 +1,150 @@ +use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::fmt; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an + /// `isize`. + /// + /// **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric + /// cast by using the `add` method instead. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.offset(offset as isize); + /// } + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.add(offset); + /// } + /// ``` + pub PTR_OFFSET_WITH_CAST, + complexity, + "unneeded pointer offset cast" +} + +declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PtrOffsetWithCast { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call + let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) { + Some(call_arg) => call_arg, + None => return, + }; + + // Check if the argument to the method call is a cast from usize + let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) { + Some(cast_lhs_expr) => cast_lhs_expr, + None => return, + }; + + let msg = format!("use of `{}` with a `usize` casted to an `isize`", method); + if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { + span_lint_and_sugg( + cx, + PTR_OFFSET_WITH_CAST, + expr.span, + &msg, + "try", + sugg, + Applicability::MachineApplicable, + ); + } else { + span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg); + } + } +} + +// If the given expression is a cast from a usize, return the lhs of the cast +fn expr_as_cast_from_usize<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Cast(ref cast_lhs_expr, _) = expr.kind { + if is_expr_ty_usize(cx, &cast_lhs_expr) { + return Some(cast_lhs_expr); + } + } + None +} + +// If the given expression is a ptr::offset or ptr::wrapping_offset method call, return the +// receiver, the arg of the method call, and the method. +fn expr_as_ptr_offset_call<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx Expr<'_>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { + if let ExprKind::MethodCall(ref path_segment, _, ref args) = expr.kind { + if is_expr_ty_raw_ptr(cx, &args[0]) { + if path_segment.ident.name == sym!(offset) { + return Some((&args[0], &args[1], Method::Offset)); + } + if path_segment.ident.name == sym!(wrapping_offset) { + return Some((&args[0], &args[1], Method::WrappingOffset)); + } + } + } + None +} + +// Is the type of the expression a usize? +fn is_expr_ty_usize<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool { + cx.tables.expr_ty(expr) == cx.tcx.types.usize +} + +// Is the type of the expression a raw pointer? +fn is_expr_ty_raw_ptr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool { + cx.tables.expr_ty(expr).is_unsafe_ptr() +} + +fn build_suggestion<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + method: Method, + receiver_expr: &Expr<'_>, + cast_lhs_expr: &Expr<'_>, +) -> Option { + let receiver = snippet_opt(cx, receiver_expr.span)?; + let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?; + Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs)) +} + +#[derive(Copy, Clone)] +enum Method { + Offset, + WrappingOffset, +} + +impl Method { + #[must_use] + fn suggestion(self) -> &'static str { + match self { + Self::Offset => "add", + Self::WrappingOffset => "wrapping_add", + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Offset => write!(f, "offset"), + Self::WrappingOffset => write!(f, "wrapping_offset"), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs new file mode 100644 index 0000000000000..ea654467b8668 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -0,0 +1,204 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::sugg::Sugg; +use crate::utils::{ + higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, + span_lint_and_sugg, SpanlessEq, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for expressions that could be replaced by the question mark operator. + /// + /// **Why is this bad?** Question mark usage is more idiomatic. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```ignore + /// if option.is_none() { + /// return None; + /// } + /// ``` + /// + /// Could be written: + /// + /// ```ignore + /// option?; + /// ``` + pub QUESTION_MARK, + style, + "checks for expressions that could be replaced by the question mark operator" +} + +declare_lint_pass!(QuestionMark => [QUESTION_MARK]); + +impl QuestionMark { + /// Checks if the given expression on the given context matches the following structure: + /// + /// ```ignore + /// if option.is_none() { + /// return None; + /// } + /// ``` + /// + /// If it matches, it will suggest to use the question mark operator instead + fn check_is_none_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let Some((if_expr, body, else_)) = higher::if_block(&expr); + if let ExprKind::MethodCall(segment, _, args) = &if_expr.kind; + if segment.ident.name == sym!(is_none); + if Self::expression_returns_none(cx, body); + if let Some(subject) = args.get(0); + if Self::is_option(cx, subject); + + then { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); + let mut replacement: Option = None; + if let Some(else_) = else_ { + if_chain! { + if let ExprKind::Block(block, None) = &else_.kind; + if block.stmts.is_empty(); + if let Some(block_expr) = &block.expr; + if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr); + then { + replacement = Some(format!("Some({}?)", receiver_str)); + } + } + } else if Self::moves_by_default(cx, subject) + && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) + { + replacement = Some(format!("{}.as_ref()?;", receiver_str)); + } else { + replacement = Some(format!("{}?;", receiver_str)); + } + + if let Some(replacement_str) = replacement { + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + "replace it with", + replacement_str, + applicability, + ) + } + } + } + } + + fn check_if_let_some_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Match(subject, arms, source) = &expr.kind; + if *source == MatchSource::IfLetDesugar { contains_else_clause: true }; + if Self::is_option(cx, subject); + + if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind; + if match_qpath(path1, &["Some"]); + if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind; + let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); + + if let ExprKind::Block(block, None) = &arms[0].body.kind; + if block.stmts.is_empty(); + if let Some(trailing_expr) = &block.expr; + if let ExprKind::Path(path) = &trailing_expr.kind; + if match_qpath(path, &[&bind.as_str()]); + + if let PatKind::Wild = arms[1].pat.kind; + if Self::expression_returns_none(cx, arms[1].body); + then { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, subject.span, "..", &mut applicability); + let replacement = format!( + "{}{}?", + receiver_str, + if by_ref { ".as_ref()" } else { "" }, + ); + + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this if-let-else may be rewritten with the `?` operator", + "replace it with", + replacement, + applicability, + ) + } + } + } + + fn moves_by_default(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool { + let expr_ty = cx.tables.expr_ty(expression); + + !expr_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, expression.span) + } + + fn is_option(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool { + let expr_ty = cx.tables.expr_ty(expression); + + is_type_diagnostic_item(cx, expr_ty, sym!(option_type)) + } + + fn expression_returns_none(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool { + match expression.kind { + ExprKind::Block(ref block, _) => { + if let Some(return_expression) = Self::return_expression(block) { + return Self::expression_returns_none(cx, &return_expression); + } + + false + }, + ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr), + ExprKind::Path(ref qp) => { + if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) = + cx.tables.qpath_res(qp, expression.hir_id) + { + return match_def_path(cx, def_id, &paths::OPTION_NONE); + } + + false + }, + _ => false, + } + } + + fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + // Check if last expression is a return statement. Then, return the expression + if_chain! { + if block.stmts.len() == 1; + if let Some(expr) = block.stmts.iter().last(); + if let StmtKind::Semi(ref expr) = expr.kind; + if let ExprKind::Ret(ret_expr) = expr.kind; + if let Some(ret_expr) = ret_expr; + + then { + return Some(ret_expr); + } + } + + // Check for `return` without a semicolon. + if_chain! { + if block.stmts.is_empty(); + if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind); + then { + return Some(ret_expr); + } + } + + None + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for QuestionMark { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + Self::check_is_none_and_early_return_none(cx, expr); + Self::check_if_let_some_and_early_return_none(cx, expr); + } +} diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs new file mode 100644 index 0000000000000..83c6faac04149 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -0,0 +1,345 @@ +use crate::consts::{constant, Constant}; +use if_chain::if_chain; +use rustc_ast::ast::RangeLimits; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use std::cmp::Ordering; + +use crate::utils::sugg::Sugg; +use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; +use crate::utils::{higher, SpanlessEq}; + +declare_clippy_lint! { + /// **What it does:** Checks for zipping a collection with the range of + /// `0.._.len()`. + /// + /// **Why is this bad?** The code is better expressed with `.enumerate()`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let x = vec![1]; + /// x.iter().zip(0..x.len()); + /// ``` + /// Could be written as + /// ```rust + /// # let x = vec![1]; + /// x.iter().enumerate(); + /// ``` + pub RANGE_ZIP_WITH_LEN, + complexity, + "zipping iterator with a range when `enumerate()` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for exclusive ranges where 1 is added to the + /// upper bound, e.g., `x..(y+1)`. + /// + /// **Why is this bad?** The code is more readable with an inclusive range + /// like `x..=y`. + /// + /// **Known problems:** Will add unnecessary pair of parentheses when the + /// expression is not wrapped in a pair but starts with a opening parenthesis + /// and ends with a closing one. + /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`. + /// + /// Also in many cases, inclusive ranges are still slower to run than + /// exclusive ranges, because they essentially add an extra branch that + /// LLVM may fail to hoist out of the loop. + /// + /// **Example:** + /// ```rust,ignore + /// for x..(y+1) { .. } + /// ``` + /// Could be written as + /// ```rust,ignore + /// for x..=y { .. } + /// ``` + pub RANGE_PLUS_ONE, + pedantic, + "`x..(y+1)` reads better as `x..=y`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for inclusive ranges where 1 is subtracted from + /// the upper bound, e.g., `x..=(y-1)`. + /// + /// **Why is this bad?** The code is more readable with an exclusive range + /// like `x..y`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// for x..=(y-1) { .. } + /// ``` + /// Could be written as + /// ```rust,ignore + /// for x..y { .. } + /// ``` + pub RANGE_MINUS_ONE, + complexity, + "`x..=(y-1)` reads better as `x..y`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for range expressions `x..y` where both `x` and `y` + /// are constant and `x` is greater or equal to `y`. + /// + /// **Why is this bad?** Empty ranges yield no values so iterating them is a no-op. + /// Moreover, trying to use a reversed range to index a slice will panic at run-time. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// fn main() { + /// (10..=0).for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[3..1]; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// (0..=10).rev().for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[1..3]; + /// } + /// ``` + pub REVERSED_EMPTY_RANGES, + correctness, + "reversing the limits of range expressions, resulting in empty ranges" +} + +declare_lint_pass!(Ranges => [ + RANGE_ZIP_WITH_LEN, + RANGE_PLUS_ONE, + RANGE_MINUS_ONE, + REVERSED_EMPTY_RANGES, +]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind { + let name = path.ident.as_str(); + if name == "zip" && args.len() == 2 { + let iter = &args[0].kind; + let zip_arg = &args[1]; + if_chain! { + // `.iter()` call + if let ExprKind::MethodCall(ref iter_path, _, ref iter_args ) = *iter; + if iter_path.ident.name == sym!(iter); + // range expression in `.zip()` call: `0..x.len()` + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(cx, zip_arg); + if is_integer_const(cx, start, 0); + // `.len()` call + if let ExprKind::MethodCall(ref len_path, _, ref len_args) = end.kind; + if len_path.ident.name == sym!(len) && len_args.len() == 1; + // `.iter()` and `.len()` called on same `Path` + if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; + if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + then { + span_lint(cx, + RANGE_ZIP_WITH_LEN, + expr.span, + &format!("It is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, iter_args[0].span, "_"))); + } + } + } + } + + check_exclusive_range_plus_one(cx, expr); + check_inclusive_range_minus_one(cx, expr); + check_reversed_empty_range(cx, expr); + } +} + +// exclusive range plus one: `x..(y+1)` +fn check_exclusive_range_plus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let Some(higher::Range { + start, + end: Some(end), + limits: RangeLimits::HalfOpen + }) = higher::range(cx, expr); + if let Some(y) = y_plus_one(cx, end); + then { + let span = if expr.span.from_expansion() { + expr.span + .ctxt() + .outer_expn_data() + .call_site + } else { + expr.span + }; + span_lint_and_then( + cx, + RANGE_PLUS_ONE, + span, + "an inclusive range would be more readable", + |diag| { + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string()); + let end = Sugg::hir(cx, y, "y"); + if let Some(is_wrapped) = &snippet_opt(cx, span) { + if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') { + diag.span_suggestion( + span, + "use", + format!("({}..={})", start, end), + Applicability::MaybeIncorrect, + ); + } else { + diag.span_suggestion( + span, + "use", + format!("{}..={}", start, end), + Applicability::MachineApplicable, // snippet + ); + } + } + }, + ); + } + } +} + +// inclusive range minus one: `x..=(y-1)` +fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if_chain! { + if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(cx, expr); + if let Some(y) = y_minus_one(cx, end); + then { + span_lint_and_then( + cx, + RANGE_MINUS_ONE, + expr.span, + "an exclusive range would be more readable", + |diag| { + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string()); + let end = Sugg::hir(cx, y, "y"); + diag.span_suggestion( + expr.span, + "use", + format!("{}..{}", start, end), + Applicability::MachineApplicable, // snippet + ); + }, + ); + } + } +} + +fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!( + get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::Index(..), + .. + }) + ) + } + + fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { + match limits { + RangeLimits::HalfOpen => ordering != Ordering::Less, + RangeLimits::Closed => ordering == Ordering::Greater, + } + } + + if_chain! { + if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr); + let ty = cx.tables.expr_ty(start); + if let ty::Int(_) | ty::Uint(_) = ty.kind; + if let Some((start_idx, _)) = constant(cx, cx.tables, start); + if let Some((end_idx, _)) = constant(cx, cx.tables, end); + if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); + if is_empty_range(limits, ordering); + then { + if inside_indexing_expr(cx, expr) { + let (reason, outcome) = if ordering == Ordering::Equal { + ("empty", "always yield an empty slice") + } else { + ("reversed", "panic at run-time") + }; + + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + &format!("this range is {} and using it to index a slice will {}", reason, outcome), + ); + } else { + span_lint_and_then( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is empty so it will yield no values", + |diag| { + if ordering != Ordering::Equal { + let start_snippet = snippet(cx, start.span, "_"); + let end_snippet = snippet(cx, end.span, "_"); + let dots = match limits { + RangeLimits::HalfOpen => "..", + RangeLimits::Closed => "..=" + }; + + diag.span_suggestion( + expr.span, + "consider using the following if you are attempting to iterate over this \ + range in reverse", + format!("({}{}{}).rev()", end_snippet, dots, start_snippet), + Applicability::MaybeIncorrect, + ); + } + }, + ); + } + } + } +} + +fn y_plus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> { + match expr.kind { + ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref lhs, + ref rhs, + ) => { + if is_integer_const(cx, lhs, 1) { + Some(rhs) + } else if is_integer_const(cx, rhs, 1) { + Some(lhs) + } else { + None + } + }, + _ => None, + } +} + +fn y_minus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> { + match expr.kind { + ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + ref lhs, + ref rhs, + ) if is_integer_const(cx, rhs, 1) => Some(lhs), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs new file mode 100644 index 0000000000000..d563eb886ae7e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -0,0 +1,613 @@ +use crate::utils::{ + fn_has_unsatisfiable_preds, has_drop, is_copy, is_type_diagnostic_item, match_def_path, match_type, paths, + snippet_opt, span_lint_hir, span_lint_hir_and_then, walk_ptrs_ty_depth, +}; +use if_chain::if_chain; +use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{def_id, Body, FnDecl, HirId}; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{ + self, traversal, + visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, +}; +use rustc_middle::ty::{self, fold::TypeVisitor, Ty}; +use rustc_mir::dataflow::BottomValue; +use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{BytePos, Span}; +use std::convert::TryFrom; + +macro_rules! unwrap_or_continue { + ($x:expr) => { + match $x { + Some(x) => x, + None => continue, + } + }; +} + +declare_clippy_lint! { + /// **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned + /// value that is going to be dropped without further use. + /// + /// **Why is this bad?** It is not always possible for the compiler to eliminate useless + /// allocations and deallocations generated by redundant `clone()`s. + /// + /// **Known problems:** + /// + /// False-negatives: analysis performed by this lint is conservative and limited. + /// + /// **Example:** + /// ```rust + /// # use std::path::Path; + /// # #[derive(Clone)] + /// # struct Foo; + /// # impl Foo { + /// # fn new() -> Self { Foo {} } + /// # } + /// # fn call(x: Foo) {} + /// { + /// let x = Foo::new(); + /// call(x.clone()); + /// call(x.clone()); // this can just pass `x` + /// } + /// + /// ["lorem", "ipsum"].join(" ").to_string(); + /// + /// Path::new("/a/b").join("c").to_path_buf(); + /// ``` + pub REDUNDANT_CLONE, + perf, + "`clone()` of an owned value that is going to be dropped immediately" +} + +declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { + #[allow(clippy::too_many_lines)] + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: FnKind<'tcx>, + _: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: HirId, + ) { + let def_id = cx.tcx.hir().body_owner_def_id(body.id()); + + // Building MIR for `fn`s with unsatisfiable preds results in ICE. + if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) { + return; + } + + let mir = cx.tcx.optimized_mir(def_id.to_def_id()); + + let maybe_storage_live_result = MaybeStorageLive + .into_engine(cx.tcx, mir, def_id.to_def_id()) + .iterate_to_fixpoint() + .into_results_cursor(mir); + let mut possible_borrower = { + let mut vis = PossibleBorrowerVisitor::new(cx, mir); + vis.visit_body(&mir); + vis.into_map(cx, maybe_storage_live_result) + }; + + for (bb, bbdata) in mir.basic_blocks().iter_enumerated() { + let terminator = bbdata.terminator(); + + if terminator.source_info.span.from_expansion() { + continue; + } + + // Give up on loops + if terminator.successors().any(|s| *s == bb) { + continue; + } + + let (fn_def_id, arg, arg_ty, clone_ret) = + unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind)); + + let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) + || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD) + || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) + && is_type_diagnostic_item(cx, arg_ty, sym!(string_type))); + + let from_deref = !from_borrow + && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) + || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING)); + + if !from_borrow && !from_deref { + continue; + } + + // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }` + let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb)); + + let loc = mir::Location { + block: bb, + statement_index: bbdata.statements.len(), + }; + + // `Local` to be cloned, and a local of `clone` call's destination + let (local, ret_local) = if from_borrow { + // `res = clone(arg)` can be turned into `res = move arg;` + // if `arg` is the only borrow of `cloned` at this point. + + if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) { + continue; + } + + (cloned, clone_ret) + } else { + // `arg` is a reference as it is `.deref()`ed in the previous block. + // Look into the predecessor block and find out the source of deref. + + let ps = &mir.predecessors()[bb]; + if ps.len() != 1 { + continue; + } + let pred_terminator = mir[ps[0]].terminator(); + + // receiver of the `deref()` call + let (pred_arg, deref_clone_ret) = if_chain! { + if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) = + is_call_with_ref_arg(cx, mir, &pred_terminator.kind); + if res == cloned; + if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD); + if match_type(cx, pred_arg_ty, &paths::PATH_BUF) + || match_type(cx, pred_arg_ty, &paths::OS_STRING); + then { + (pred_arg, res) + } else { + continue; + } + }; + + let (local, cannot_move_out) = + unwrap_or_continue!(find_stmt_assigns_to(cx, mir, pred_arg, true, ps[0])); + let loc = mir::Location { + block: bb, + statement_index: mir.basic_blocks()[bb].statements.len(), + }; + + // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed + // at the last statement: + // + // ``` + // pred_arg = &local; + // cloned = deref(pred_arg); + // arg = &cloned; + // StorageDead(pred_arg); + // res = to_path_buf(cloned); + // ``` + if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) { + continue; + } + + (local, deref_clone_ret) + }; + + let is_temp = mir.local_kind(ret_local) == mir::LocalKind::Temp; + + // 1. `local` can be moved out if it is not used later. + // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone` + // call anyway. + let (used, consumed_or_mutated) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold( + (false, !is_temp), + |(used, consumed), (tbb, tdata)| { + // Short-circuit + if (used && consumed) || + // Give up on loops + tdata.terminator().successors().any(|s| *s == bb) + { + return (true, true); + } + + let mut vis = LocalUseVisitor { + used: (local, false), + consumed_or_mutated: (ret_local, false), + }; + vis.visit_basic_block_data(tbb, tdata); + (used || vis.used.1, consumed || vis.consumed_or_mutated.1) + }, + ); + + if !used || !consumed_or_mutated { + let span = terminator.source_info.span; + let scope = terminator.source_info.scope; + let node = mir.source_scopes[scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + if_chain! { + if let Some(snip) = snippet_opt(cx, span); + if let Some(dot) = snip.rfind('.'); + then { + let sugg_span = span.with_lo( + span.lo() + BytePos(u32::try_from(dot).unwrap()) + ); + let mut app = Applicability::MaybeIncorrect; + + let mut call_snip = &snip[dot + 1..]; + // Machine applicable when `call_snip` looks like `foobar()` + if call_snip.ends_with("()") { + call_snip = call_snip[..call_snip.len()-2].trim(); + if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { + app = Applicability::MachineApplicable; + } + } + + span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { + diag.span_suggestion( + sugg_span, + "remove this", + String::new(), + app, + ); + if used { + diag.span_note( + span, + "cloned value is neither consumed nor mutated", + ); + } else { + diag.span_note( + span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), + "this value is dropped without further use", + ); + } + }); + } else { + span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); + } + } + } + } + } +} + +/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`. +fn is_call_with_ref_arg<'tcx>( + cx: &LateContext<'_, 'tcx>, + mir: &'tcx mir::Body<'tcx>, + kind: &'tcx mir::TerminatorKind<'tcx>, +) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> { + if_chain! { + if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; + if args.len() == 1; + if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; + if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).kind; + if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx)); + if !is_copy(cx, inner_ty); + then { + Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)?.as_local()?)) + } else { + None + } + } +} + +type CannotMoveOut = bool; + +/// Finds the first `to = (&)from`, and returns +/// ``Some((from, whether `from` cannot be moved out))``. +fn find_stmt_assigns_to<'tcx>( + cx: &LateContext<'_, 'tcx>, + mir: &mir::Body<'tcx>, + to_local: mir::Local, + by_ref: bool, + bb: mir::BasicBlock, +) -> Option<(mir::Local, CannotMoveOut)> { + let rvalue = mir.basic_blocks()[bb].statements.iter().rev().find_map(|stmt| { + if let mir::StatementKind::Assign(box (mir::Place { local, .. }, v)) = &stmt.kind { + return if *local == to_local { Some(v) } else { None }; + } + + None + })?; + + match (by_ref, &*rvalue) { + (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => { + base_local_and_movability(cx, mir, *place) + }, + (false, mir::Rvalue::Ref(_, _, place)) => { + if let [mir::ProjectionElem::Deref] = place.as_ref().projection { + base_local_and_movability(cx, mir, *place) + } else { + None + } + }, + _ => None, + } +} + +/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself +/// if it is already a `Local`. +/// +/// Also reports whether given `place` cannot be moved out. +fn base_local_and_movability<'tcx>( + cx: &LateContext<'_, 'tcx>, + mir: &mir::Body<'tcx>, + place: mir::Place<'tcx>, +) -> Option<(mir::Local, CannotMoveOut)> { + use rustc_middle::mir::PlaceRef; + + // Dereference. You cannot move things out from a borrowed value. + let mut deref = false; + // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509. + let mut field = false; + // If projection is a slice index then clone can be removed only if the + // underlying type implements Copy + let mut slice = false; + + let PlaceRef { local, mut projection } = place.as_ref(); + while let [base @ .., elem] = projection { + projection = base; + deref |= matches!(elem, mir::ProjectionElem::Deref); + field |= matches!(elem, mir::ProjectionElem::Field(..)) + && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty); + slice |= matches!(elem, mir::ProjectionElem::Index(..)) + && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty); + } + + Some((local, deref || field || slice)) +} + +struct LocalUseVisitor { + used: (mir::Local, bool), + consumed_or_mutated: (mir::Local, bool), +} + +impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { + fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { + let statements = &data.statements; + for (statement_index, statement) in statements.iter().enumerate() { + self.visit_statement(statement, mir::Location { block, statement_index }); + } + + self.visit_terminator( + data.terminator(), + mir::Location { + block, + statement_index: statements.len(), + }, + ); + } + + fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, _: mir::Location) { + let local = place.local; + + if local == self.used.0 + && !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)) + { + self.used.1 = true; + } + + if local == self.consumed_or_mutated.0 { + match ctx { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + self.consumed_or_mutated.1 = true; + }, + _ => {}, + } + } + } +} + +/// Determines liveness of each local purely based on `StorageLive`/`Dead`. +#[derive(Copy, Clone)] +struct MaybeStorageLive; + +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + const NAME: &'static str = "maybe_storage_live"; + + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { + body.local_decls.len() + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet) { + for arg in body.args_iter() { + state.insert(arg); + } + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { + match stmt.kind { + mir::StatementKind::StorageLive(l) => trans.gen(l), + mir::StatementKind::StorageDead(l) => trans.kill(l), + _ => (), + } + } + + fn terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _loc: mir::Location, + ) { + } + + fn call_return_effect( + &self, + _in_out: &mut impl GenKill, + _block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + _return_place: mir::Place<'tcx>, + ) { + // Nothing to do when a call returns successfully + } +} + +impl BottomValue for MaybeStorageLive { + /// bottom = dead + const BOTTOM_VALUE: bool = false; +} + +/// Collects the possible borrowers of each local. +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` +/// possible borrowers of `a`. +struct PossibleBorrowerVisitor<'a, 'tcx> { + possible_borrower: TransitiveRelation, + body: &'a mir::Body<'tcx>, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>, body: &'a mir::Body<'tcx>) -> Self { + Self { + possible_borrower: TransitiveRelation::default(), + cx, + body, + } + } + + fn into_map( + self, + cx: &LateContext<'a, 'tcx>, + maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>, + ) -> PossibleBorrowerMap<'a, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let borrowers = self.possible_borrower.reachable_from(&row); + if !borrowers.is_empty() { + let mut bs = HybridBitSet::new_empty(self.body.local_decls.len()); + for &c in borrowers { + if c != mir::Local::from_usize(0) { + bs.insert(c); + } + } + + if !bs.is_empty() { + map.insert(row, bs); + } + } + } + + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), + } + } +} + +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if !ContainsRegion.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); + } + }); + }, + } + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + if let mir::TerminatorKind::Call { + args, + destination: Some((mir::Place { local: dest, .. }, _)), + .. + } = &terminator.kind + { + // If the call returns something with lifetimes, + // let's conservatively assume the returned value contains lifetime of all the arguments. + // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. + if !ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty) { + return; + } + + for op in args { + match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => { + self.possible_borrower.add(p.local, *dest); + }, + _ => (), + } + } + } + } +} + +struct ContainsRegion; + +impl TypeVisitor<'_> for ContainsRegion { + fn visit_region(&mut self, _: ty::Region<'_>) -> bool { + true + } +} + +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + + let mut visit_op = |op: &mir::Operand<'_>| match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), + _ => (), + }; + + match rvalue { + Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), + Aggregate(_, ops) => ops.iter().for_each(visit_op), + BinaryOp(_, lhs, rhs) | CheckedBinaryOp(_, lhs, rhs) => { + visit_op(lhs); + visit_op(rhs); + }, + _ => (), + } +} + +/// Result of `PossibleBorrowerVisitor`. +struct PossibleBorrowerMap<'a, 'tcx> { + /// Mapping `Local -> its possible borrowers` + map: FxHashMap>, + maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>, + // Caches to avoid allocation of `BitSet` on every query + bitset: (BitSet, BitSet), +} + +impl PossibleBorrowerMap<'_, '_> { + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); + } + } else { + return false; + } + + self.bitset.1.clear(); + for b in borrowers { + self.bitset.1.insert(*b); + } + + self.bitset.0 == self.bitset.1 + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs new file mode 100644 index 0000000000000..b12c3c344ef4c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -0,0 +1,63 @@ +use crate::utils::span_lint_and_sugg; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for fields in struct literals where shorthands + /// could be used. + /// + /// **Why is this bad?** If the field and variable names are the same, + /// the field name is redundant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let bar: u8 = 123; + /// + /// struct Foo { + /// bar: u8, + /// } + /// + /// let foo = Foo { bar: bar }; + /// ``` + /// the last line can be simplified to + /// ```ignore + /// let foo = Foo { bar }; + /// ``` + pub REDUNDANT_FIELD_NAMES, + style, + "checks for fields in struct literals where shorthands could be used" +} + +declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); + +impl EarlyLintPass for RedundantFieldNames { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::Struct(_, ref fields, _) = expr.kind { + for field in fields { + if field.is_shorthand { + continue; + } + if let ExprKind::Path(None, path) = &field.expr.kind { + if path.segments.len() == 1 + && path.segments[0].ident == field.ident + && path.segments[0].args.is_none() + { + span_lint_and_sugg( + cx, + REDUNDANT_FIELD_NAMES, + field.span, + "redundant field names in struct initialization", + "replace it with", + field.ident.to_string(), + Applicability::MachineApplicable, + ); + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs b/src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs new file mode 100644 index 0000000000000..7ee298e9833f2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs @@ -0,0 +1,216 @@ +use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Lint for redundant pattern matching over `Result` or + /// `Option` + /// + /// **Why is this bad?** It's more concise and clear to just use the proper + /// utility function + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// if let Ok(_) = Ok::(42) {} + /// if let Err(_) = Err::(42) {} + /// if let None = None::<()> {} + /// if let Some(_) = Some(42) {} + /// match Ok::(42) { + /// Ok(_) => true, + /// Err(_) => false, + /// }; + /// ``` + /// + /// The more idiomatic use would be: + /// + /// ```rust + /// if Ok::(42).is_ok() {} + /// if Err::(42).is_err() {} + /// if None::<()>.is_none() {} + /// if Some(42).is_some() {} + /// Ok::(42).is_ok(); + /// ``` + pub REDUNDANT_PATTERN_MATCHING, + style, + "use the proper utility function avoiding an `if let`" +} + +declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantPatternMatching { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { + match match_source { + MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), + MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), + MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), + _ => return, + } + } + } +} + +fn find_sugg_for_if_let<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx Expr<'_>, + op: &Expr<'_>, + arms: &[Arm<'_>], + keyword: &'static str, +) { + let good_method = match arms[0].pat.kind { + PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { + if let PatKind::Wild = patterns[0].kind { + if match_qpath(path, &paths::RESULT_OK) { + "is_ok()" + } else if match_qpath(path, &paths::RESULT_ERR) { + "is_err()" + } else if match_qpath(path, &paths::OPTION_SOME) { + "is_some()" + } else { + return; + } + } else { + return; + } + }, + + PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", + + _ => return, + }; + + // check that `while_let_on_iterator` lint does not trigger + if_chain! { + if keyword == "while"; + if let ExprKind::MethodCall(method_path, _, _) = op.kind; + if method_path.ident.name == sym!(next); + if match_trait_method(cx, op, &paths::ITERATOR); + then { + return; + } + } + + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + arms[0].pat.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let expr_span = expr.span; + + // while let ... = ... { ... } + // ^^^ + let op_span = op.span.source_callsite(); + + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^ + let span = expr_span.until(op_span.shrink_to_hi()); + diag.span_suggestion( + span, + "try this", + format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), + Applicability::MachineApplicable, // snippet + ); + }, + ); +} + +fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { + if arms.len() == 2 { + let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); + + let found_good_method = match node_pair { + ( + PatKind::TupleStruct(ref path_left, ref patterns_left, _), + PatKind::TupleStruct(ref path_right, ref patterns_right, _), + ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::RESULT_OK, + &paths::RESULT_ERR, + "is_ok()", + "is_err()", + ) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) + if patterns.len() == 1 => + { + if let PatKind::Wild = patterns[0].kind { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::OPTION_SOME, + &paths::OPTION_NONE, + "is_some()", + "is_none()", + ) + } else { + None + } + }, + _ => None, + }; + + if let Some(good_method) = found_good_method { + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + expr.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + let span = expr.span.to(op.span); + diag.span_suggestion( + span, + "try this", + format!("{}.{}", snippet(cx, op.span, "_"), good_method), + Applicability::MaybeIncorrect, // snippet + ); + }, + ); + } + } +} + +fn find_good_method_for_match<'a>( + arms: &[Arm<'_>], + path_left: &QPath<'_>, + path_right: &QPath<'_>, + expected_left: &[&str], + expected_right: &[&str], + should_be_left: &'a str, + should_be_right: &'a str, +) -> Option<&'a str> { + let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + (&(*arms[0].body).kind, &(*arms[1].body).kind) + } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + (&(*arms[1].body).kind, &(*arms[0].body).kind) + } else { + return None; + }; + + match body_node_pair { + (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { + (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), + _ => None, + }, + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs new file mode 100644 index 0000000000000..6fc07f91660ee --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -0,0 +1,78 @@ +use crate::utils::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind, VisibilityKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they + /// are inside a private module. + /// + /// **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent + /// module's visibility. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// mod internal { + /// pub(crate) fn internal_fn() { } + /// } + /// ``` + /// This function is not visible outside the module and it can be declared with `pub` or + /// private visibility + /// ```rust + /// mod internal { + /// pub fn internal_fn() { } + /// } + /// ``` + pub REDUNDANT_PUB_CRATE, + nursery, + "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them." +} + +#[derive(Default)] +pub struct RedundantPubCrate { + is_exported: Vec, +} + +impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantPubCrate { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'tcx>) { + if let VisibilityKind::Crate { .. } = item.vis.node { + if !cx.access_levels.is_exported(item.hir_id) { + if let Some(false) = self.is_exported.last() { + let span = item.span.with_hi(item.ident.span.hi()); + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + let descr = cx.tcx.def_kind(def_id).descr(def_id.to_def_id()); + span_lint_and_then( + cx, + REDUNDANT_PUB_CRATE, + span, + &format!("pub(crate) {} inside private module", descr), + |diag| { + diag.span_suggestion( + item.vis.span, + "consider using", + "pub".to_string(), + Applicability::MachineApplicable, + ); + }, + ) + } + } + } + + if let ItemKind::Mod { .. } = item.kind { + self.is_exported.push(cx.access_levels.is_exported(item.hir_id)); + } + } + + fn check_item_post(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Mod { .. } = item.kind { + self.is_exported.pop().expect("unbalanced check_item/check_item_post"); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs new file mode 100644 index 0000000000000..c6f57298c2601 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -0,0 +1,99 @@ +use crate::utils::{snippet, span_lint_and_then}; +use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. + /// + /// **Why is this bad?** Adding `'static` to every reference can create very + /// complicated types. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = + /// &[...] + /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = + /// &[...] + /// ``` + /// This code can be rewritten as + /// ```ignore + /// const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...] + /// static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...] + /// ``` + pub REDUNDANT_STATIC_LIFETIMES, + style, + "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them." +} + +declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); + +impl RedundantStaticLifetimes { + // Recursively visit types + fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) { + match ty.kind { + // Be careful of nested structures (arrays and tuples) + TyKind::Array(ref ty, _) => { + self.visit_type(&*ty, cx, reason); + }, + TyKind::Tup(ref tup) => { + for tup_ty in tup { + self.visit_type(&*tup_ty, cx, reason); + } + }, + // This is what we are looking for ! + TyKind::Rptr(ref optional_lifetime, ref borrow_type) => { + // Match the 'static lifetime + if let Some(lifetime) = *optional_lifetime { + match borrow_type.ty.kind { + TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { + if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { + let snip = snippet(cx, borrow_type.ty.span, ""); + let sugg = format!("&{}", snip); + span_lint_and_then( + cx, + REDUNDANT_STATIC_LIFETIMES, + lifetime.ident.span, + reason, + |diag| { + diag.span_suggestion( + ty.span, + "consider removing `'static`", + sugg, + Applicability::MachineApplicable, //snippet + ); + }, + ); + } + }, + _ => {}, + } + } + self.visit_type(&*borrow_type.ty, cx, reason); + }, + TyKind::Slice(ref ty) => { + self.visit_type(ty, cx, reason); + }, + _ => {}, + } + } +} + +impl EarlyLintPass for RedundantStaticLifetimes { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !item.span.from_expansion() { + if let ItemKind::Const(_, ref var_type, _) = item.kind { + self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime"); + // Don't check associated consts because `'static` cannot be elided on those (issue + // #2438) + } + + if let ItemKind::Static(ref var_type, _, _) = item.kind { + self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime"); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs new file mode 100644 index 0000000000000..d5797468e9d53 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -0,0 +1,98 @@ +use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::{Expr, ExprKind, UnOp}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. + /// + /// **Why is this bad?** Immediately dereferencing a reference is no-op and + /// makes the code less clear. + /// + /// **Known problems:** Multiple dereference/addrof pairs are not handled so + /// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect. + /// + /// **Example:** + /// ```rust,ignore + /// let a = f(*&mut b); + /// let c = *&d; + /// ``` + pub DEREF_ADDROF, + complexity, + "use of `*&` or `*&mut` in an expression" +} + +declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]); + +fn without_parens(mut e: &Expr) -> &Expr { + while let ExprKind::Paren(ref child_e) = e.kind { + e = child_e; + } + e +} + +impl EarlyLintPass for DerefAddrOf { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + if_chain! { + if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; + if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind; + if !in_macro(addrof_target.span); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try this", + format!("{}", snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)), + applicability, + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for references in expressions that use + /// auto dereference. + /// + /// **Why is this bad?** The reference is a no-op and is automatically + /// dereferenced by the compiler and makes the code less clear. + /// + /// **Example:** + /// ```rust + /// struct Point(u32, u32); + /// let point = Point(30, 20); + /// let x = (&point).0; + /// ``` + pub REF_IN_DEREF, + complexity, + "Use of reference in auto dereference expression." +} + +declare_lint_pass!(RefInDeref => [REF_IN_DEREF]); + +impl EarlyLintPass for RefInDeref { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + if_chain! { + if let ExprKind::Field(ref object, _) = e.kind; + if let ExprKind::Paren(ref parened) = object.kind; + if let ExprKind::AddrOf(_, _, ref inner) = parened.kind; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REF_IN_DEREF, + object.span, + "Creating a reference that is immediately dereferenced.", + "try this", + snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), + applicability, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs new file mode 100644 index 0000000000000..30084e3e1ffce --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -0,0 +1,263 @@ +use crate::consts::{constant, Constant}; +use crate::utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_lint_and_help}; +use if_chain::if_chain; +use rustc_ast::ast::{LitKind, StrStyle}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{Block, BorrowKind, Crate, Expr, ExprKind, HirId}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::{BytePos, Span}; +use std::convert::TryFrom; + +declare_clippy_lint! { + /// **What it does:** Checks [regex](https://crates.io/crates/regex) creation + /// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct + /// regex syntax. + /// + /// **Why is this bad?** This will lead to a runtime panic. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// Regex::new("|") + /// ``` + pub INVALID_REGEX, + correctness, + "invalid regular expressions" +} + +declare_clippy_lint! { + /// **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) + /// creation (with `Regex::new`, `RegexBuilder::new` or `RegexSet::new`). + /// + /// **Why is this bad?** Matching the regex can likely be replaced by `==` or + /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str` + /// methods. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// Regex::new("^foobar") + /// ``` + pub TRIVIAL_REGEX, + style, + "trivial regular expressions" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `regex!(_)` which (as of now) is + /// usually slower than `Regex::new(_)` unless called in a loop (which is a bad + /// idea anyway). + /// + /// **Why is this bad?** Performance, at least for now. The macro version is + /// likely to catch up long-term, but for now the dynamic version is faster. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// regex!("foo|bar") + /// ``` + pub REGEX_MACRO, + style, + "use of `regex!(_)` instead of `Regex::new(_)`" +} + +#[derive(Clone, Default)] +pub struct Regex { + spans: FxHashSet, + last: Option, +} + +impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex { + fn check_crate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) { + self.spans.clear(); + } + + fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { + if_chain! { + if self.last.is_none(); + if let Some(ref expr) = block.expr; + if match_type(cx, cx.tables.expr_ty(expr), &paths::REGEX); + if let Some(span) = is_expn_of(expr.span, "regex"); + then { + if !self.spans.contains(&span) { + span_lint(cx, + REGEX_MACRO, + span, + "`regex!(_)` found. \ + Please use `Regex::new(_)`, which is faster for now."); + self.spans.insert(span); + } + self.last = Some(block.hir_id); + } + } + } + + fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { + if self.last.map_or(false, |id| block.hir_id == id) { + self.last = None; + } + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if args.len() == 1; + if let Some(def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id(); + then { + if match_def_path(cx, def_id, &paths::REGEX_NEW) || + match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) { + check_regex(cx, &args[0], true); + } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) || + match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) { + check_regex(cx, &args[0], false); + } else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) { + check_set(cx, &args[0], true); + } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) { + check_set(cx, &args[0], false); + } + } + } + } +} + +#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here +#[must_use] +fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u16) -> Span { + let offset = u32::from(offset); + let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset); + let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset); + assert!(start <= end); + Span::new(start, end, base.ctxt()) +} + +fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) -> Option { + constant(cx, cx.tables, e).and_then(|(c, _)| match c { + Constant::Str(s) => Some(s), + _ => None, + }) +} + +fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { + use regex_syntax::hir::Anchor::{EndText, StartText}; + use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal}; + + let is_literal = |e: &[regex_syntax::hir::Hir]| { + e.iter().all(|e| match *e.kind() { + Literal(_) => true, + _ => false, + }) + }; + + match *s.kind() { + Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"), + Literal(_) => Some("consider using `str::contains`"), + Alternation(ref exprs) => { + if exprs.iter().all(|e| e.kind().is_empty()) { + Some("the regex is unlikely to be useful as it is") + } else { + None + } + }, + Concat(ref exprs) => match (exprs[0].kind(), exprs[exprs.len() - 1].kind()) { + (&Anchor(StartText), &Anchor(EndText)) if exprs[1..(exprs.len() - 1)].is_empty() => { + Some("consider using `str::is_empty`") + }, + (&Anchor(StartText), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { + Some("consider using `==` on `str`s") + }, + (&Anchor(StartText), &Literal(_)) if is_literal(&exprs[1..]) => Some("consider using `str::starts_with`"), + (&Literal(_), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { + Some("consider using `str::ends_with`") + }, + _ if is_literal(exprs) => Some("consider using `str::contains`"), + _ => None, + }, + _ => None, + } +} + +fn check_set<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; + if let ExprKind::Array(exprs) = expr.kind; + then { + for expr in exprs { + check_regex(cx, expr, utf8); + } + } + } +} + +fn check_regex<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { + let mut parser = regex_syntax::ParserBuilder::new() + .unicode(utf8) + .allow_invalid_utf8(!utf8) + .build(); + + if let ExprKind::Lit(ref lit) = expr.kind { + if let LitKind::Str(ref r, style) = lit.node { + let r = &r.as_str(); + let offset = if let StrStyle::Raw(n) = style { 2 + n } else { 1 }; + match parser.parse(r) { + Ok(r) => { + if let Some(repl) = is_trivial_regex(&r) { + span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); + } + }, + Err(regex_syntax::Error::Parse(e)) => { + span_lint( + cx, + INVALID_REGEX, + str_span(expr.span, *e.span(), offset), + &format!("regex syntax error: {}", e.kind()), + ); + }, + Err(regex_syntax::Error::Translate(e)) => { + span_lint( + cx, + INVALID_REGEX, + str_span(expr.span, *e.span(), offset), + &format!("regex syntax error: {}", e.kind()), + ); + }, + Err(e) => { + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + }, + } + } + } else if let Some(r) = const_str(cx, expr) { + match parser.parse(&r) { + Ok(r) => { + if let Some(repl) = is_trivial_regex(&r) { + span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); + } + }, + Err(regex_syntax::Error::Parse(e)) => { + span_lint( + cx, + INVALID_REGEX, + expr.span, + &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), + ); + }, + Err(regex_syntax::Error::Translate(e)) => { + span_lint( + cx, + INVALID_REGEX, + expr.span, + &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), + ); + }, + Err(e) => { + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + }, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs new file mode 100644 index 0000000000000..35464f629c364 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -0,0 +1,359 @@ +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit::FnKind; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; + +use crate::utils::{in_macro, match_path_ast, snippet_opt, span_lint_and_sugg, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for return statements at the end of a block. + /// + /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// **Known problems:** If the computation returning the value borrows a local + /// variable, removing the `return` may run afoul of the borrow checker. + /// + /// **Example:** + /// ```rust + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + pub NEEDLESS_RETURN, + style, + "using a return statement like `return expr;` where an expression would suffice" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_clippy_lint! { + /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// + /// **Why is this bad?** Such expressions add no value, but can make the code + /// less readable. Depending on formatting they can make a `break` or `return` + /// statement look like a function call. + /// + /// **Known problems:** The lint currently misses unit return types in types, + /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// + /// **Example:** + /// ```rust + /// fn return_unit() -> () { + /// () + /// } + /// ``` + pub UNUSED_UNIT, + style, + "needless unit expression" +} + +#[derive(PartialEq, Eq, Copy, Clone)] +enum RetReplacement { + Empty, + Block, +} + +declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]); + +impl Return { + // Check the final stmt or expr in a block for unnecessary return. + fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { + if let Some(stmt) = block.stmts.last() { + match stmt.kind { + ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => { + self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + }, + _ => (), + } + } + } + + // Check a the final expression in a block if it's a return. + fn check_final_expr( + &mut self, + cx: &EarlyContext<'_>, + expr: &ast::Expr, + span: Option, + replacement: RetReplacement, + ) { + match expr.kind { + // simple return is always "bad" + ast::ExprKind::Ret(ref inner) => { + // allow `#[cfg(a)] return a; #[cfg(b)] return b;` + if !expr.attrs.iter().any(attr_is_cfg) { + Self::emit_return_lint( + cx, + span.expect("`else return` is not possible"), + inner.as_ref().map(|i| i.span), + replacement, + ); + } + }, + // a whole block? check it! + ast::ExprKind::Block(ref block, _) => { + self.check_block_return(cx, block); + }, + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => { + self.check_block_return(cx, ifblock); + self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty); + }, + // a match expr, check all arms + ast::ExprKind::Match(_, ref arms) => { + for arm in arms { + self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + } + }, + _ => (), + } + } + + fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + match inner_span { + Some(inner_span) => { + if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() { + return; + } + + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); + } + }) + }, + None => match replacement { + RetReplacement::Empty => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", + String::new(), + Applicability::MachineApplicable, + ); + }, + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), + Applicability::MachineApplicable, + ); + }, + }, + } + } + + // Check for "let x = EXPR; x" + fn check_let_return(cx: &EarlyContext<'_>, block: &ast::Block) { + let mut it = block.stmts.iter(); + + // we need both a let-binding stmt and an expr + if_chain! { + if let Some(retexpr) = it.next_back(); + if let ast::StmtKind::Expr(ref retexpr) = retexpr.kind; + if let Some(stmt) = it.next_back(); + if let ast::StmtKind::Local(ref local) = stmt.kind; + // don't lint in the presence of type inference + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(ref initexpr) = local.init; + if let ast::PatKind::Ident(_, ident, _) = local.pat.kind; + if let ast::ExprKind::Path(_, ref path) = retexpr.kind; + if match_path_ast(path, &[&*ident.name.as_str()]); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); + then { + span_lint_and_then( + cx, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(snippet) = snippet_opt(cx, initexpr.span) { + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } + } + } +} + +impl EarlyLintPass for Return { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { + match kind { + FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block), + FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty), + FnKind::Fn(.., None) => {}, + } + if_chain! { + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; + if let ast::TyKind::Tup(ref vals) = ty.kind; + if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); + then { + lint_unneeded_unit_return(cx, ty, span); + } + } + } + + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { + Self::check_let_return(cx, block); + if_chain! { + if let Some(ref stmt) = block.stmts.last(); + if let ast::StmtKind::Expr(ref expr) = stmt.kind; + if is_unit_expr(expr) && !stmt.span.from_expansion(); + then { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + match e.kind { + ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { + if is_unit_expr(expr) && !expr.span.from_expansion() { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + }, + _ => (), + } + } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } +} + +fn attr_is_cfg(attr: &ast::Attribute) -> bool { + attr.meta_item_list().is_some() && attr.check_name(sym!(cfg)) +} + +// get the def site +#[must_use] +fn get_def(span: Span) -> Option { + if span.from_expansion() { + Some(span.ctxt().outer_expn_data().def_site) + } else { + None + } +} + +// is this expr a `()` unit? +fn is_unit_expr(expr: &ast::Expr) -> bool { + if let ast::ExprKind::Tup(ref vals) = expr.kind { + vals.is_empty() + } else { + false + } +} + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + if let Some(rpos) = fn_source.rfind("->") { + #[allow(clippy::cast_possible_truncation)] + ( + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + } else { + (ty.span, Applicability::MaybeIncorrect) + } + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs new file mode 100644 index 0000000000000..6820d1620bd18 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -0,0 +1,57 @@ +use crate::utils::{get_trait_def_id, paths, span_lint}; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for mis-uses of the serde API. + /// + /// **Why is this bad?** Serde is very finnicky about how its API should be + /// used, but the type system can't be used to enforce it (yet?). + /// + /// **Known problems:** None. + /// + /// **Example:** Implementing `Visitor::visit_string` but not + /// `Visitor::visit_str`. + pub SERDE_API_MISUSE, + correctness, + "various things that will negatively affect your serde experience" +} + +declare_lint_pass!(SerdeAPI => [SERDE_API_MISUSE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SerdeAPI { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Impl { + of_trait: Some(ref trait_ref), + items, + .. + } = item.kind + { + let did = trait_ref.path.res.def_id(); + if let Some(visit_did) = get_trait_def_id(cx, &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`", + ); + } + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs new file mode 100644 index 0000000000000..11360b0ef8495 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -0,0 +1,389 @@ +use crate::reexport::Name; +use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind, + UnOp, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for bindings that shadow other bindings already in + /// scope, while just changing reference level or mutability. + /// + /// **Why is this bad?** Not much, in fact it's a very common pattern in Rust + /// code. Still, some may opt to avoid it in their code base, they can set this + /// lint to `Warn`. + /// + /// **Known problems:** This lint, as the other shadowing related lints, + /// currently only catches very simple patterns. + /// + /// **Example:** + /// ```rust + /// # let x = 1; + /// let x = &x; + /// ``` + pub SHADOW_SAME, + restriction, + "rebinding a name to itself, e.g., `let mut x = &mut x`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for bindings that shadow other bindings already in + /// scope, while reusing the original value. + /// + /// **Why is this bad?** Not too much, in fact it's a common pattern in Rust + /// code. Still, some argue that name shadowing like this hurts readability, + /// because a value may be bound to different things depending on position in + /// the code. + /// + /// **Known problems:** This lint, as the other shadowing related lints, + /// currently only catches very simple patterns. + /// + /// **Example:** + /// ```rust + /// let x = 2; + /// let x = x + 1; + /// ``` + /// use different variable name: + /// ```rust + /// let x = 2; + /// let y = x + 1; + /// ``` + pub SHADOW_REUSE, + restriction, + "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for bindings that shadow other bindings already in + /// scope, either without a initialization or with one that does not even use + /// the original value. + /// + /// **Why is this bad?** Name shadowing can hurt readability, especially in + /// large code bases, because it is easy to lose track of the active binding at + /// any place in the code. This can be alleviated by either giving more specific + /// names to bindings or introducing more scopes to contain the bindings. + /// + /// **Known problems:** This lint, as the other shadowing related lints, + /// currently only catches very simple patterns. + /// + /// **Example:** + /// ```rust + /// # let y = 1; + /// # let z = 2; + /// let x = y; + /// let x = z; // shadows the earlier binding + /// ``` + pub SHADOW_UNRELATED, + pedantic, + "rebinding a name without even using the original value" +} + +declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Shadow { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: HirId, + ) { + if in_external_macro(cx.sess(), body.value.span) { + return; + } + check_fn(cx, decl, body); + } +} + +fn check_fn<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) { + let mut bindings = Vec::with_capacity(decl.inputs.len()); + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(.., ident, _) = arg.pat.kind { + bindings.push((ident.name, ident.span)) + } + } + check_expr(cx, &body.value, &mut bindings); +} + +fn check_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) { + let len = bindings.len(); + for stmt in block.stmts { + match stmt.kind { + StmtKind::Local(ref local) => check_local(cx, local, bindings), + StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => check_expr(cx, e, bindings), + StmtKind::Item(..) => {}, + } + } + if let Some(ref o) = block.expr { + check_expr(cx, o, bindings); + } + bindings.truncate(len); +} + +fn check_local<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) { + if in_external_macro(cx.sess(), local.span) { + return; + } + if higher::is_from_for_desugar(local) { + return; + } + let Local { + ref pat, + ref ty, + ref init, + span, + .. + } = *local; + if let Some(ref t) = *ty { + check_ty(cx, t, bindings) + } + if let Some(ref o) = *init { + check_expr(cx, o, bindings); + check_pat(cx, pat, Some(o), span, bindings); + } else { + check_pat(cx, pat, None, span, bindings); + } +} + +fn is_binding(cx: &LateContext<'_, '_>, pat_id: HirId) -> bool { + let var_ty = cx.tables.node_type_opt(pat_id); + if let Some(var_ty) = var_ty { + match var_ty.kind { + ty::Adt(..) => false, + _ => true, + } + } else { + false + } +} + +fn check_pat<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &'tcx Pat<'_>, + init: Option<&'tcx Expr<'_>>, + span: Span, + bindings: &mut Vec<(Name, Span)>, +) { + // TODO: match more stuff / destructuring + match pat.kind { + PatKind::Binding(.., ident, ref inner) => { + let name = ident.name; + if is_binding(cx, pat.hir_id) { + let mut new_binding = true; + for tup in bindings.iter_mut() { + if tup.0 == name { + lint_shadow(cx, name, span, pat.span, init, tup.1); + tup.1 = ident.span; + new_binding = false; + break; + } + } + if new_binding { + bindings.push((name, ident.span)); + } + } + if let Some(ref p) = *inner { + check_pat(cx, p, init, span, bindings); + } + }, + PatKind::Struct(_, pfields, _) => { + if let Some(init_struct) = init { + if let ExprKind::Struct(_, ref efields, _) = init_struct.kind { + for field in pfields { + let name = field.ident.name; + let efield = efields + .iter() + .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None }); + check_pat(cx, &field.pat, efield, span, bindings); + } + } else { + for field in pfields { + check_pat(cx, &field.pat, init, span, bindings); + } + } + } else { + for field in pfields { + check_pat(cx, &field.pat, None, span, bindings); + } + } + }, + PatKind::Tuple(inner, _) => { + if let Some(init_tup) = init { + if let ExprKind::Tup(ref tup) = init_tup.kind { + for (i, p) in inner.iter().enumerate() { + check_pat(cx, p, Some(&tup[i]), p.span, bindings); + } + } else { + for p in inner { + check_pat(cx, p, init, span, bindings); + } + } + } else { + for p in inner { + check_pat(cx, p, None, span, bindings); + } + } + }, + PatKind::Box(ref inner) => { + if let Some(initp) = init { + if let ExprKind::Box(ref inner_init) = initp.kind { + check_pat(cx, inner, Some(&**inner_init), span, bindings); + } else { + check_pat(cx, inner, init, span, bindings); + } + } else { + check_pat(cx, inner, init, span, bindings); + } + }, + PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings), + // PatVec(Vec>, Option>, Vec>), + _ => (), + } +} + +fn lint_shadow<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + name: Name, + span: Span, + pattern_span: Span, + init: Option<&'tcx Expr<'_>>, + prev_span: Span, +) { + if let Some(expr) = init { + if is_self_shadow(name, expr) { + span_lint_and_then( + cx, + SHADOW_SAME, + span, + &format!( + "`{}` is shadowed by itself in `{}`", + snippet(cx, pattern_span, "_"), + snippet(cx, expr.span, "..") + ), + |diag| { + diag.span_note(prev_span, "previous binding is here"); + }, + ); + } else if contains_name(name, expr) { + span_lint_and_then( + cx, + SHADOW_REUSE, + pattern_span, + &format!( + "`{}` is shadowed by `{}` which reuses the original value", + snippet(cx, pattern_span, "_"), + snippet(cx, expr.span, "..") + ), + |diag| { + diag.span_note(expr.span, "initialization happens here"); + diag.span_note(prev_span, "previous binding is here"); + }, + ); + } else { + span_lint_and_then( + cx, + SHADOW_UNRELATED, + pattern_span, + &format!( + "`{}` is shadowed by `{}`", + snippet(cx, pattern_span, "_"), + snippet(cx, expr.span, "..") + ), + |diag| { + diag.span_note(expr.span, "initialization happens here"); + diag.span_note(prev_span, "previous binding is here"); + }, + ); + } + } else { + span_lint_and_then( + cx, + SHADOW_UNRELATED, + span, + &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")), + |diag| { + diag.span_note(prev_span, "previous binding is here"); + }, + ); + } +} + +fn check_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + match expr.kind { + ExprKind::Unary(_, ref e) + | ExprKind::Field(ref e, _) + | ExprKind::AddrOf(_, _, ref e) + | ExprKind::Box(ref e) => check_expr(cx, e, bindings), + ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, _, _) => check_block(cx, block, bindings), + // ExprKind::Call + // ExprKind::MethodCall + ExprKind::Array(v) | ExprKind::Tup(v) => { + for e in v { + check_expr(cx, e, bindings) + } + }, + ExprKind::Match(ref init, arms, _) => { + check_expr(cx, init, bindings); + let len = bindings.len(); + for arm in arms { + check_pat(cx, &arm.pat, Some(&**init), arm.pat.span, bindings); + // This is ugly, but needed to get the right type + if let Some(ref guard) = arm.guard { + match guard { + Guard::If(if_expr) => check_expr(cx, if_expr, bindings), + } + } + check_expr(cx, &arm.body, bindings); + bindings.truncate(len); + } + }, + _ => (), + } +} + +fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) { + match ty.kind { + TyKind::Slice(ref sty) => check_ty(cx, sty, bindings), + TyKind::Array(ref fty, ref anon_const) => { + check_ty(cx, fty, bindings); + check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings); + }, + TyKind::Ptr(MutTy { ty: ref mty, .. }) | TyKind::Rptr(_, MutTy { ty: ref mty, .. }) => { + check_ty(cx, mty, bindings) + }, + TyKind::Tup(tup) => { + for t in tup { + check_ty(cx, t, bindings) + } + }, + TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings), + _ => (), + } +} + +fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner), + ExprKind::Block(ref block, _) => { + block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e)) + }, + ExprKind::Unary(op, ref inner) => (UnOp::UnDeref == op) && is_self_shadow(name, inner), + ExprKind::Path(QPath::Resolved(_, ref path)) => path_eq_name(name, path), + _ => false, + } +} + +fn path_eq_name(name: Name, path: &Path<'_>) -> bool { + !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str() +} diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs new file mode 100644 index 0000000000000..8d767a7fec88d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -0,0 +1,62 @@ +use crate::utils::{in_macro, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::{Item, ItemKind, UseTreeKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::edition::Edition; + +declare_clippy_lint! { + /// **What it does:** Checking for imports with single component use path. + /// + /// **Why is this bad?** Import with single component use path such as `use cratename;` + /// is not necessary, and thus should be removed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust, ignore + /// use regex; + /// + /// fn main() { + /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); + /// } + /// ``` + /// Better as + /// ```rust, ignore + /// fn main() { + /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); + /// } + /// ``` + pub SINGLE_COMPONENT_PATH_IMPORTS, + style, + "imports with single component path are redundant" +} + +declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); + +impl EarlyLintPass for SingleComponentPathImports { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if !in_macro(item.span); + if cx.sess.opts.edition == Edition::Edition2018; + if !item.vis.node.is_pub(); + if let ItemKind::Use(use_tree) = &item.kind; + if let segments = &use_tree.prefix.segments; + if segments.len() == 1; + if let UseTreeKind::Simple(None, _, _) = use_tree.kind; + then { + span_lint_and_sugg( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + item.span, + "this import is redundant", + "remove it entirely", + String::new(), + Applicability::MachineApplicable + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs new file mode 100644 index 0000000000000..fb3706be1c213 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -0,0 +1,324 @@ +use crate::utils::sugg::Sugg; +use crate::utils::{get_enclosing_block, match_qpath, span_lint_and_then, SpanlessEq}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// **What it does:** Checks slow zero-filled vector initialization + /// + /// **Why is this bad?** These structures are non-idiomatic and less efficient than simply using + /// `vec![0; len]`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use core::iter::repeat; + /// # let len = 4; + /// let mut vec1 = Vec::with_capacity(len); + /// vec1.resize(len, 0); + /// + /// let mut vec2 = Vec::with_capacity(len); + /// vec2.extend(repeat(0).take(len)) + /// ``` + pub SLOW_VECTOR_INITIALIZATION, + perf, + "slow vector initialization" +} + +declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]); + +/// `VecAllocation` contains data regarding a vector allocated with `with_capacity` and then +/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or +/// `vec = Vec::with_capacity(0)` +struct VecAllocation<'tcx> { + /// Symbol of the local variable name + variable_name: Symbol, + + /// Reference to the expression which allocates the vector + allocation_expr: &'tcx Expr<'tcx>, + + /// Reference to the expression used as argument on `with_capacity` call. This is used + /// to only match slow zero-filling idioms of the same length than vector initialization. + len_expr: &'tcx Expr<'tcx>, +} + +/// Type of slow initialization +enum InitializationType<'tcx> { + /// Extend is a slow initialization with the form `vec.extend(repeat(0).take(..))` + Extend(&'tcx Expr<'tcx>), + + /// Resize is a slow initialization with the form `vec.resize(.., 0)` + Resize(&'tcx Expr<'tcx>), +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SlowVectorInit { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)` + if_chain! { + if let ExprKind::Assign(ref left, ref right, _) = expr.kind; + + // Extract variable name + if let ExprKind::Path(QPath::Resolved(_, ref path)) = left.kind; + if let Some(variable_name) = path.segments.get(0); + + // Extract len argument + if let Some(ref len_arg) = Self::is_vec_with_capacity(right); + + then { + let vi = VecAllocation { + variable_name: variable_name.ident.name, + allocation_expr: right, + len_expr: len_arg, + }; + + Self::search_initialization(cx, vi, expr.hir_id); + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` + if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind; + if let Some(ref init) = local.init; + if let Some(ref len_arg) = Self::is_vec_with_capacity(init); + + then { + let vi = VecAllocation { + variable_name: variable_name.name, + allocation_expr: init, + len_expr: len_arg, + }; + + Self::search_initialization(cx, vi, stmt.hir_id); + } + } + } +} + +impl SlowVectorInit { + /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression + /// of the first argument of `with_capacity` call if it matches or `None` if it does not. + fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["Vec", "with_capacity"]); + if args.len() == 1; + + then { + return Some(&args[0]); + } + } + + None + } + + /// Search initialization for the given vector + fn search_initialization<'tcx>(cx: &LateContext<'_, 'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) { + let enclosing_body = get_enclosing_block(cx, parent_node); + + if enclosing_body.is_none() { + return; + } + + let mut v = VectorInitializationVisitor { + cx, + vec_alloc, + slow_expression: None, + initialization_found: false, + }; + + v.visit_block(enclosing_body.unwrap()); + + if let Some(ref allocation_expr) = v.slow_expression { + Self::lint_initialization(cx, allocation_expr, &v.vec_alloc); + } + } + + fn lint_initialization<'tcx>( + cx: &LateContext<'_, 'tcx>, + initialization: &InitializationType<'tcx>, + vec_alloc: &VecAllocation<'_>, + ) { + match initialization { + InitializationType::Extend(e) | InitializationType::Resize(e) => Self::emit_lint( + cx, + e, + vec_alloc, + "slow zero-filling initialization", + SLOW_VECTOR_INITIALIZATION, + ), + }; + } + + fn emit_lint<'tcx>( + cx: &LateContext<'_, 'tcx>, + slow_fill: &Expr<'_>, + vec_alloc: &VecAllocation<'_>, + msg: &str, + lint: &'static Lint, + ) { + let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len"); + + span_lint_and_then(cx, lint, slow_fill.span, msg, |diag| { + diag.span_suggestion( + vec_alloc.allocation_expr.span, + "consider replace allocation with", + format!("vec![0; {}]", len_expr), + Applicability::Unspecified, + ); + }); + } +} + +/// `VectorInitializationVisitor` searches for unsafe or slow vector initializations for the given +/// vector. +struct VectorInitializationVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + + /// Contains the information. + vec_alloc: VecAllocation<'tcx>, + + /// Contains the slow initialization expression, if one was found. + slow_expression: Option>, + + /// `true` if the initialization of the vector has been found on the visited block. + initialization_found: bool, +} + +impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { + /// Checks if the given expression is extending a vector with `repeat(0).take(..)` + fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { + if_chain! { + if self.initialization_found; + if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind; + if let ExprKind::Path(ref qpath_subj) = args[0].kind; + if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if path.ident.name == sym!(extend); + if let Some(ref extend_arg) = args.get(1); + if self.is_repeat_take(extend_arg); + + then { + self.slow_expression = Some(InitializationType::Extend(expr)); + } + } + } + + /// Checks if the given expression is resizing a vector with 0 + fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { + if_chain! { + if self.initialization_found; + if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind; + if let ExprKind::Path(ref qpath_subj) = args[0].kind; + if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if path.ident.name == sym!(resize); + if let (Some(ref len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); + + // Check that is filled with 0 + if let ExprKind::Lit(ref lit) = fill_arg.kind; + if let LitKind::Int(0, _) = lit.node; + + // Check that len expression is equals to `with_capacity` expression + if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr); + + then { + self.slow_expression = Some(InitializationType::Resize(expr)); + } + } + } + + /// Returns `true` if give expression is `repeat(0).take(...)` + fn is_repeat_take(&self, expr: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::MethodCall(ref take_path, _, ref take_args) = expr.kind; + if take_path.ident.name == sym!(take); + + // Check that take is applied to `repeat(0)` + if let Some(ref repeat_expr) = take_args.get(0); + if Self::is_repeat_zero(repeat_expr); + + // Check that len expression is equals to `with_capacity` expression + if let Some(ref len_arg) = take_args.get(1); + if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr); + + then { + return true; + } + } + + false + } + + /// Returns `true` if given expression is `repeat(0)` + fn is_repeat_zero(expr: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::Call(ref fn_expr, ref repeat_args) = expr.kind; + if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind; + if match_qpath(&qpath_repeat, &["repeat"]); + if let Some(ref repeat_arg) = repeat_args.get(0); + if let ExprKind::Lit(ref lit) = repeat_arg.kind; + if let LitKind::Int(0, _) = lit.node; + + then { + return true + } + } + + false + } +} + +impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + if self.initialization_found { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + self.search_slow_extend_filling(expr); + self.search_slow_resize_filling(expr); + }, + _ => (), + } + + self.initialization_found = false; + } else { + walk_stmt(self, stmt); + } + } + + fn visit_block(&mut self, block: &'tcx Block<'_>) { + if self.initialization_found { + if let Some(ref s) = block.stmts.get(0) { + self.visit_stmt(s) + } + + self.initialization_found = false; + } else { + walk_block(self, block); + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // Skip all the expressions previous to the vector initialization + if self.vec_alloc.allocation_expr.hir_id == expr.hir_id { + self.initialization_found = true; + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs new file mode 100644 index 0000000000000..2c51271e312de --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -0,0 +1,200 @@ +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +use if_chain::if_chain; + +use crate::utils::SpanlessEq; +use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty}; + +declare_clippy_lint! { + /// **What it does:** Checks for string appends of the form `x = x + y` (without + /// `let`!). + /// + /// **Why is this bad?** It's not really bad, but some people think that the + /// `.push_str(_)` method is more readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let mut x = "Hello".to_owned(); + /// x = x + ", World"; + /// ``` + pub STRING_ADD_ASSIGN, + pedantic, + "using `x = x + ..` where x is a `String` instead of `push_str()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for all instances of `x + _` where `x` is of type + /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not* + /// match. + /// + /// **Why is this bad?** It's not bad in and of itself. However, this particular + /// `Add` implementation is asymmetric (the other operand need not be `String`, + /// but `x` does), while addition as mathematically defined is symmetric, also + /// the `String::push_str(_)` function is a perfectly good replacement. + /// Therefore, some dislike it and wish not to have it in their code. + /// + /// That said, other people think that string addition, having a long tradition + /// in other languages is actually fine, which is why we decided to make this + /// particular lint `allow` by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x = "Hello".to_owned(); + /// x + ", World"; + /// ``` + pub STRING_ADD, + restriction, + "using `x + ..` where x is a `String` instead of `push_str()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the `as_bytes` method called on string literals + /// that contain only ASCII characters. + /// + /// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used + /// instead. They are shorter but less discoverable than `as_bytes()`. + /// + /// **Known Problems:** None. + /// + /// **Example:** + /// ```rust + /// let bs = "a byte string".as_bytes(); + /// ``` + pub STRING_LIT_AS_BYTES, + style, + "calling `as_bytes` on a string literal instead of using a byte string literal" +} + +declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), e.span) { + return; + } + + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref left, + _, + ) = e.kind + { + if is_string(cx, left) { + if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { + let parent = get_parent_expr(cx, e); + if let Some(p) = parent { + if let ExprKind::Assign(ref target, _, _) = p.kind { + // avoid duplicate matches + if SpanlessEq::new(cx).eq_expr(target, left) { + return; + } + } + } + } + span_lint( + cx, + STRING_ADD, + e.span, + "you added something to a string. Consider using `String::push_str()` instead", + ); + } + } else if let ExprKind::Assign(ref target, ref src, _) = e.kind { + if is_string(cx, target) && is_add(cx, src, target) { + span_lint( + cx, + STRING_ADD_ASSIGN, + e.span, + "you assigned the result of adding something to this string. Consider using \ + `String::push_str()` instead", + ); + } + } + } +} + +fn is_string(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool { + is_type_diagnostic_item(cx, walk_ptrs_ty(cx.tables.expr_ty(e)), sym!(string_type)) +} + +fn is_add(cx: &LateContext<'_, '_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { + match src.kind { + ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref left, + _, + ) => SpanlessEq::new(cx).eq_expr(target, left), + ExprKind::Block(ref block, _) => { + block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target)) + }, + _ => false, + } +} + +// Max length a b"foo" string can take +const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; + +declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + use crate::utils::{snippet, snippet_with_applicability}; + use rustc_ast::ast::LitKind; + + if_chain! { + if let ExprKind::MethodCall(path, _, args) = &e.kind; + if path.ident.name == sym!(as_bytes); + if let ExprKind::Lit(lit) = &args[0].kind; + if let LitKind::Str(lit_content, _) = &lit.node; + then { + let callsite = snippet(cx, args[0].span.source_callsite(), r#""foo""#); + let mut applicability = Applicability::MachineApplicable; + if callsite.starts_with("include_str!") { + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `as_bytes()` on `include_str!(..)`", + "consider using `include_bytes!(..)` instead", + snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability).replacen( + "include_str", + "include_bytes", + 1, + ), + applicability, + ); + } else if lit_content.as_str().is_ascii() + && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT + && !args[0].span.from_expansion() + { + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `as_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}", + snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability) + ), + applicability, + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs new file mode 100644 index 0000000000000..f1e223d9a48c6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -0,0 +1,205 @@ +use crate::utils::{get_trait_def_id, span_lint, trait_ref_of_method}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g. + /// subtracting elements in an Add impl. + /// + /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// impl Add for Foo { + /// type Output = Foo; + /// + /// fn add(self, other: Foo) -> Foo { + /// Foo(self.0 - other.0) + /// } + /// } + /// ``` + pub SUSPICIOUS_ARITHMETIC_IMPL, + correctness, + "suspicious use of operators in impl of arithmetic trait" +} + +declare_clippy_lint! { + /// **What it does:** Lints for suspicious operations in impls of OpAssign, e.g. + /// subtracting elements in an AddAssign impl. + /// + /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// impl AddAssign for Foo { + /// fn add_assign(&mut self, other: Foo) { + /// *self = *self - other; + /// } + /// } + /// ``` + pub SUSPICIOUS_OP_ASSIGN_IMPL, + correctness, + "suspicious use of operators in impl of OpAssign trait" +} + +declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SuspiciousImpl { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind { + match binop.node { + hir::BinOpKind::Eq + | hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ne + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt => return, + _ => {}, + } + // Check if the binary expression is part of another bi/unary expression + // or operator assignment as a child node + let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id); + while parent_expr != hir::CRATE_HIR_ID { + if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) { + match e.kind { + hir::ExprKind::Binary(..) + | hir::ExprKind::Unary(hir::UnOp::UnNot, _) + | hir::ExprKind::Unary(hir::UnOp::UnNeg, _) + | hir::ExprKind::AssignOp(..) => return, + _ => {}, + } + } + parent_expr = cx.tcx.hir().get_parent_node(parent_expr); + } + // as a parent node + let mut visitor = BinaryExprVisitor { in_binary_expr: false }; + walk_expr(&mut visitor, expr); + + if visitor.in_binary_expr { + return; + } + + if let Some(impl_trait) = check_binop( + cx, + expr, + binop.node, + &["Add", "Sub", "Mul", "Div"], + &[ + hir::BinOpKind::Add, + hir::BinOpKind::Sub, + hir::BinOpKind::Mul, + hir::BinOpKind::Div, + ], + ) { + span_lint( + cx, + SUSPICIOUS_ARITHMETIC_IMPL, + binop.span, + &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + ); + } + + if let Some(impl_trait) = check_binop( + cx, + expr, + binop.node, + &[ + "AddAssign", + "SubAssign", + "MulAssign", + "DivAssign", + "BitAndAssign", + "BitOrAssign", + "BitXorAssign", + "RemAssign", + "ShlAssign", + "ShrAssign", + ], + &[ + hir::BinOpKind::Add, + hir::BinOpKind::Sub, + hir::BinOpKind::Mul, + hir::BinOpKind::Div, + hir::BinOpKind::BitAnd, + hir::BinOpKind::BitOr, + hir::BinOpKind::BitXor, + hir::BinOpKind::Rem, + hir::BinOpKind::Shl, + hir::BinOpKind::Shr, + ], + ) { + span_lint( + cx, + SUSPICIOUS_OP_ASSIGN_IMPL, + binop.span, + &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + ); + } + } + } +} + +fn check_binop( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + binop: hir::BinOpKind, + traits: &[&'static str], + expected_ops: &[hir::BinOpKind], +) -> Option<&'static str> { + let mut trait_ids = vec![]; + let [krate, module] = crate::utils::paths::OPS_MODULE; + + for &t in traits { + let path = [krate, module, t]; + if let Some(trait_id) = get_trait_def_id(cx, &path) { + trait_ids.push(trait_id); + } else { + return None; + } + } + + // Get the actually implemented trait + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); + + if_chain! { + if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn); + if let Some(idx) = trait_ids.iter().position(|&tid| tid == trait_ref.path.res.def_id()); + if binop != expected_ops[idx]; + then{ + return Some(traits[idx]) + } + } + + None +} + +struct BinaryExprVisitor { + in_binary_expr: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for BinaryExprVisitor { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + match expr.kind { + hir::ExprKind::Binary(..) + | hir::ExprKind::Unary(hir::UnOp::UnNot, _) + | hir::ExprKind::Unary(hir::UnOp::UnNeg, _) + | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true, + _ => {}, + } + + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs new file mode 100644 index 0000000000000..c52e6a643f2a2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -0,0 +1,263 @@ +use crate::utils::sugg::Sugg; +use crate::utils::{ + differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty, + SpanlessEq, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for manual swapping. + /// + /// **Why is this bad?** The `std::mem::swap` function exposes the intent better + /// without deinitializing or copying either variable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let mut a = 42; + /// let mut b = 1337; + /// + /// let t = b; + /// b = a; + /// a = t; + /// ``` + /// Use std::mem::swap(): + /// ```rust + /// let mut a = 1; + /// let mut b = 2; + /// std::mem::swap(&mut a, &mut b); + /// ``` + pub MANUAL_SWAP, + complexity, + "manual swap of two variables" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `foo = bar; bar = foo` sequences. + /// + /// **Why is this bad?** This looks like a failed attempt to swap. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let mut a = 1; + /// # let mut b = 2; + /// a = b; + /// b = a; + /// ``` + /// If swapping is intended, use `swap()` instead: + /// ```rust + /// # let mut a = 1; + /// # let mut b = 2; + /// std::mem::swap(&mut a, &mut b); + /// ``` + pub ALMOST_SWAPPED, + correctness, + "`foo = bar; bar = foo` sequence" +} + +declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap { + fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { + check_manual_swap(cx, block); + check_suspicious_swap(cx, block); + } +} + +/// Implementation of the `MANUAL_SWAP` lint. +fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block<'_>) { + for w in block.stmts.windows(3) { + if_chain! { + // let t = foo(); + if let StmtKind::Local(ref tmp) = w[0].kind; + if let Some(ref tmp_init) = tmp.init; + if let PatKind::Binding(.., ident, None) = tmp.pat.kind; + + // foo() = bar(); + if let StmtKind::Semi(ref first) = w[1].kind; + if let ExprKind::Assign(ref lhs1, ref rhs1, _) = first.kind; + + // bar() = t; + if let StmtKind::Semi(ref second) = w[2].kind; + if let ExprKind::Assign(ref lhs2, ref rhs2, _) = second.kind; + if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind; + if rhs2.segments.len() == 1; + + if ident.as_str() == rhs2.segments[0].ident.as_str(); + if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1); + if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2); + then { + if let ExprKind::Field(ref lhs1, _) = lhs1.kind { + if let ExprKind::Field(ref lhs2, _) = lhs2.kind { + if lhs1.hir_id.owner == lhs2.hir_id.owner { + return; + } + } + } + + let mut applicability = Applicability::MachineApplicable; + + let slice = check_for_slice(cx, lhs1, lhs2); + let (replace, what, sugg) = if let Slice::NotSwappable = slice { + return; + } else if let Slice::Swappable(slice, idx1, idx2) = slice { + if let Some(slice) = Sugg::hir_opt(cx, slice) { + ( + false, + format!(" elements of `{}`", slice), + format!( + "{}.swap({}, {})", + slice.maybe_par(), + snippet_with_applicability(cx, idx1.span, "..", &mut applicability), + snippet_with_applicability(cx, idx2.span, "..", &mut applicability), + ), + ) + } else { + (false, String::new(), String::new()) + } + } else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) { + ( + true, + format!(" `{}` and `{}`", first, second), + format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), + ) + } else { + (true, String::new(), String::new()) + }; + + let span = w[0].span.to(second.span); + + span_lint_and_then( + cx, + MANUAL_SWAP, + span, + &format!("this looks like you are swapping{} manually", what), + |diag| { + if !sugg.is_empty() { + diag.span_suggestion( + span, + "try", + sugg, + applicability, + ); + + if replace { + diag.note("or maybe you should use `std::mem::replace`?"); + } + } + } + ); + } + } + } +} + +enum Slice<'a> { + /// `slice.swap(idx1, idx2)` can be used + /// + /// ## Example + /// + /// ```rust + /// # let mut a = vec![0, 1]; + /// let t = a[1]; + /// a[1] = a[0]; + /// a[0] = t; + /// // can be written as + /// a.swap(0, 1); + /// ``` + Swappable(&'a Expr<'a>, &'a Expr<'a>, &'a Expr<'a>), + /// The `swap` function cannot be used. + /// + /// ## Example + /// + /// ```rust + /// # let mut a = [vec![1, 2], vec![3, 4]]; + /// let t = a[0][1]; + /// a[0][1] = a[1][0]; + /// a[1][0] = t; + /// ``` + NotSwappable, + /// Not a slice + None, +} + +/// Checks if both expressions are index operations into "slice-like" types. +fn check_for_slice<'a>(cx: &LateContext<'_, '_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { + if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { + if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { + if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) { + let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1)); + + if matches!(ty.kind, ty::Slice(_)) + || matches!(ty.kind, ty::Array(_, _)) + || is_type_diagnostic_item(cx, ty, sym!(vec_type)) + || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) + { + return Slice::Swappable(lhs1, idx1, idx2); + } + } else { + return Slice::NotSwappable; + } + } + } + + Slice::None +} + +/// Implementation of the `ALMOST_SWAPPED` lint. +fn check_suspicious_swap(cx: &LateContext<'_, '_>, block: &Block<'_>) { + for w in block.stmts.windows(2) { + if_chain! { + if let StmtKind::Semi(ref first) = w[0].kind; + if let StmtKind::Semi(ref second) = w[1].kind; + if !differing_macro_contexts(first.span, second.span); + if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind; + if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind; + if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1); + if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0); + then { + let lhs0 = Sugg::hir_opt(cx, lhs0); + let rhs0 = Sugg::hir_opt(cx, rhs0); + let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) { + ( + format!(" `{}` and `{}`", first, second), + first.mut_addr().to_string(), + second.mut_addr().to_string(), + ) + } else { + (String::new(), String::new(), String::new()) + }; + + let span = first.span.to(second.span); + + span_lint_and_then(cx, + ALMOST_SWAPPED, + span, + &format!("this looks like you are trying to swap{}", what), + |diag| { + if !what.is_empty() { + diag.span_suggestion( + span, + "try", + format!( + "std::mem::swap({}, {})", + lhs, + rhs, + ), + Applicability::MaybeIncorrect, + ); + diag.note("or maybe you should use `std::mem::replace`?"); + } + }); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs new file mode 100644 index 0000000000000..7b673e15b764a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs @@ -0,0 +1,219 @@ +use crate::utils::span_lint_and_sugg; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{BytePos, Span}; +use std::convert::TryFrom; + +declare_clippy_lint! { + /// **What it does:** Checks doc comments for usage of tab characters. + /// + /// **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation. + /// To keep a consistent view on the source, also doc comments should not have tabs. + /// Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the + /// display settings of the author and reader differ. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// /// + /// /// Struct to hold two strings: + /// /// - first one + /// /// - second one + /// pub struct DoubleString { + /// /// + /// /// - First String: + /// /// - needs to be inside here + /// first_string: String, + /// /// + /// /// - Second String: + /// /// - needs to be inside here + /// second_string: String, + ///} + /// ``` + /// + /// Will be converted to: + /// ```rust + /// /// + /// /// Struct to hold two strings: + /// /// - first one + /// /// - second one + /// pub struct DoubleString { + /// /// + /// /// - First String: + /// /// - needs to be inside here + /// first_string: String, + /// /// + /// /// - Second String: + /// /// - needs to be inside here + /// second_string: String, + ///} + /// ``` + pub TABS_IN_DOC_COMMENTS, + style, + "using tabs in doc comments is not recommended" +} + +declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]); + +impl TabsInDocComments { + fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) { + if let ast::AttrKind::DocComment(comment) = attr.kind { + let comment = comment.as_str(); + + for (lo, hi) in get_chunks_of_tabs(&comment) { + let new_span = Span::new( + attr.span.lo() + BytePos(lo), + attr.span.lo() + BytePos(hi), + attr.span.ctxt(), + ); + span_lint_and_sugg( + cx, + TABS_IN_DOC_COMMENTS, + new_span, + "using tabs in doc comments is not recommended", + "consider using four spaces per tab", + " ".repeat((hi - lo) as usize), + Applicability::MaybeIncorrect, + ); + } + } + } +} + +impl EarlyLintPass for TabsInDocComments { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) { + Self::warn_if_tabs_in_doc(cx, &attribute); + } +} + +/// +/// scans the string for groups of tabs and returns the start(inclusive) and end positions +/// (exclusive) of all groups +/// e.g. "sd\tasd\t\taa" will be converted to [(2, 3), (6, 8)] as +/// 012 3456 7 89 +/// ^-^ ^---^ +fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { + let line_length_way_to_long = "doc comment longer than 2^32 chars"; + let mut spans: Vec<(u32, u32)> = vec![]; + let mut current_start: u32 = 0; + + // tracker to decide if the last group of tabs is not closed by a non-tab character + let mut is_active = false; + + let chars_array: Vec<_> = the_str.chars().collect(); + + if chars_array == vec!['\t'] { + return vec![(0, 1)]; + } + + for (index, arr) in chars_array.windows(2).enumerate() { + let index = u32::try_from(index).expect(line_length_way_to_long); + match arr { + ['\t', '\t'] => { + // either string starts with double tab, then we have to set it active, + // otherwise is_active is true anyway + is_active = true; + }, + [_, '\t'] => { + // as ['\t', '\t'] is excluded, this has to be a start of a tab group, + // set indices accordingly + is_active = true; + current_start = index + 1; + }, + ['\t', _] => { + // this now has to be an end of the group, hence we have to push a new tuple + is_active = false; + spans.push((current_start, index + 1)); + }, + _ => {}, + } + } + + // only possible when tabs are at the end, insert last group + if is_active { + spans.push(( + current_start, + u32::try_from(the_str.chars().count()).expect(line_length_way_to_long), + )); + } + + spans +} + +#[cfg(test)] +mod tests_for_get_chunks_of_tabs { + use super::get_chunks_of_tabs; + + #[test] + fn test_empty_string() { + let res = get_chunks_of_tabs(""); + + assert_eq!(res, vec![]); + } + + #[test] + fn test_simple() { + let res = get_chunks_of_tabs("sd\t\t\taa"); + + assert_eq!(res, vec![(2, 5)]); + } + + #[test] + fn test_only_t() { + let res = get_chunks_of_tabs("\t\t"); + + assert_eq!(res, vec![(0, 2)]); + } + + #[test] + fn test_only_one_t() { + let res = get_chunks_of_tabs("\t"); + + assert_eq!(res, vec![(0, 1)]); + } + + #[test] + fn test_double() { + let res = get_chunks_of_tabs("sd\tasd\t\taa"); + + assert_eq!(res, vec![(2, 3), (6, 8)]); + } + + #[test] + fn test_start() { + let res = get_chunks_of_tabs("\t\taa"); + + assert_eq!(res, vec![(0, 2)]); + } + + #[test] + fn test_end() { + let res = get_chunks_of_tabs("aa\t\t"); + + assert_eq!(res, vec![(2, 4)]); + } + + #[test] + fn test_start_single() { + let res = get_chunks_of_tabs("\taa"); + + assert_eq!(res, vec![(0, 1)]); + } + + #[test] + fn test_end_single() { + let res = get_chunks_of_tabs("aa\t"); + + assert_eq!(res, vec![(2, 3)]); + } + + #[test] + fn test_no_tabs() { + let res = get_chunks_of_tabs("dsfs"); + + assert_eq!(res, vec![]); + } +} diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs new file mode 100644 index 0000000000000..bbb883aaf3287 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs @@ -0,0 +1,53 @@ +use crate::utils::{is_adjusted, span_lint}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for construction of a structure or tuple just to + /// assign a value in it. + /// + /// **Why is this bad?** Readability. If the structure is only created to be + /// updated, why not write the structure you want in the first place? + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// (0, 0).0 = 1 + /// ``` + pub TEMPORARY_ASSIGNMENT, + complexity, + "assignments to temporaries" +} + +fn is_temporary(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + match &expr.kind { + ExprKind::Struct(..) | ExprKind::Tup(..) => true, + ExprKind::Path(qpath) => { + if let Res::Def(DefKind::Const, ..) = cx.tables.qpath_res(qpath, expr.hir_id) { + true + } else { + false + } + }, + _ => false, + } +} + +declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TemporaryAssignment { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(target, ..) = &expr.kind { + let mut base = target; + while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind { + base = f; + } + if is_temporary(cx, base) && !is_adjusted(cx, base) { + span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary"); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs new file mode 100644 index 0000000000000..c6302ca03d918 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -0,0 +1,94 @@ +use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `.to_digit(..).is_some()` on `char`s. + /// + /// **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's + /// more straight forward to use the dedicated `is_digit` method. + /// + /// **Example:** + /// ```rust + /// # let c = 'c'; + /// # let radix = 10; + /// let is_digit = c.to_digit(radix).is_some(); + /// ``` + /// can be written as: + /// ``` + /// # let c = 'c'; + /// # let radix = 10; + /// let is_digit = c.is_digit(radix); + /// ``` + pub TO_DIGIT_IS_SOME, + style, + "`char.is_digit()` is clearer" +} + +declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ToDigitIsSome { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(is_some_path, _, is_some_args) = &expr.kind; + if is_some_path.ident.name.as_str() == "is_some"; + if let [to_digit_expr] = &**is_some_args; + then { + let match_result = match &to_digit_expr.kind { + hir::ExprKind::MethodCall(to_digits_path, _, to_digit_args) => { + if_chain! { + if let [char_arg, radix_arg] = &**to_digit_args; + if to_digits_path.ident.name.as_str() == "to_digit"; + let char_arg_ty = cx.tables.expr_ty_adjusted(char_arg); + if char_arg_ty.kind == ty::Char; + then { + Some((true, char_arg, radix_arg)) + } else { + None + } + } + } + hir::ExprKind::Call(to_digits_call, to_digit_args) => { + if_chain! { + if let [char_arg, radix_arg] = &**to_digit_args; + if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind; + if let to_digits_call_res = cx.tables.qpath_res(to_digits_path, to_digits_call.hir_id); + if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id(); + if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "", "to_digit"]); + then { + Some((false, char_arg, radix_arg)) + } else { + None + } + } + } + _ => None + }; + + if let Some((is_method_call, char_arg, radix_arg)) = match_result { + 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); + + span_lint_and_sugg( + cx, + TO_DIGIT_IS_SOME, + expr.span, + "use of `.to_digit(..).is_some()`", + "try this", + if is_method_call { + format!("{}.is_digit({})", char_arg_snip, radix_snip) + } else { + format!("char::is_digit({}, {})", char_arg_snip, radix_snip) + }, + applicability, + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs new file mode 100644 index 0000000000000..67121729663c6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -0,0 +1,86 @@ +use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::{GenericBound, Generics, WherePredicate}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +#[derive(Copy, Clone)] +pub struct TraitBounds; + +declare_clippy_lint! { + /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds + /// + /// **Why is this bad?** Repeating the type for every bound makes the code + /// less readable than combining the bounds + /// + /// **Example:** + /// ```rust + /// pub fn foo(t: T) where T: Copy, T: Clone {} + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// pub fn foo(t: T) where T: Copy + Clone {} + /// ``` + pub TYPE_REPETITION_IN_BOUNDS, + pedantic, + "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" +} + +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds { + fn check_generics(&mut self, cx: &LateContext<'a, 'tcx>, gen: &'tcx Generics<'_>) { + if in_macro(gen.span) { + return; + } + let hash = |ty| -> u64 { + let mut hasher = SpanlessHash::new(cx, cx.tables); + hasher.hash_ty(ty); + hasher.finish() + }; + let mut map = FxHashMap::default(); + let mut applicability = Applicability::MaybeIncorrect; + for bound in gen.where_clause.predicates { + if let WherePredicate::BoundPredicate(ref p) = bound { + let h = hash(&p.bounded_ty); + if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()) { + let mut hint_string = format!( + "consider combining the bounds: `{}:", + snippet(cx, p.bounded_ty.span, "_") + ); + for b in v.iter() { + if let GenericBound::Trait(ref poly_trait_ref, _) = b { + let path = &poly_trait_ref.trait_ref.path; + hint_string.push_str(&format!( + " {} +", + snippet_with_applicability(cx, path.span, "..", &mut applicability) + )); + } + } + for b in p.bounds.iter() { + if let GenericBound::Trait(ref poly_trait_ref, _) = b { + let path = &poly_trait_ref.trait_ref.path; + hint_string.push_str(&format!( + " {} +", + snippet_with_applicability(cx, path.span, "..", &mut applicability) + )); + } + } + hint_string.truncate(hint_string.len() - 2); + hint_string.push('`'); + span_lint_and_help( + cx, + TYPE_REPETITION_IN_BOUNDS, + p.span, + "this type has already been used as a bound predicate", + None, + &hint_string, + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute.rs b/src/tools/clippy/clippy_lints/src/transmute.rs new file mode 100644 index 0000000000000..e24d2c4f495db --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute.rs @@ -0,0 +1,653 @@ +use crate::utils::{ + is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, + span_lint_and_then, sugg, +}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::borrow::Cow; + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes that can't ever be correct on any + /// architecture. + /// + /// **Why is this bad?** It's basically guaranteed to be undefined behaviour. + /// + /// **Known problems:** When accessing C, users might want to store pointer + /// sized objects in `extradata` arguments to save an allocation. + /// + /// **Example:** + /// ```ignore + /// let ptr: *const T = core::intrinsics::transmute('x') + /// ``` + pub WRONG_TRANSMUTE, + correctness, + "transmutes that are confusing at best, undefined behaviour at worst and always useless" +} + +// FIXME: Move this to `complexity` again, after #5343 is fixed +declare_clippy_lint! { + /// **What it does:** Checks for transmutes to the original type of the object + /// and transmutes that could be a cast. + /// + /// **Why is this bad?** Readability. The code tricks people into thinking that + /// something complex is going on. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s + /// ``` + pub USELESS_TRANSMUTE, + nursery, + "transmutes that have the same to and from types or could be a cast/coercion" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes between a type `T` and `*T`. + /// + /// **Why is this bad?** It's easy to mistakenly transmute between a type and a + /// pointer to that type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// core::intrinsics::transmute(t) // where the result type is the same as + /// // `*t` or `&t`'s + /// ``` + pub CROSSPOINTER_TRANSMUTE, + complexity, + "transmutes that have to or from types that are a pointer to the other" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from a pointer to a reference. + /// + /// **Why is this bad?** This can always be rewritten with `&` and `*`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// unsafe { + /// let _: &T = std::mem::transmute(p); // where p: *const T + /// } + /// + /// // can be written: + /// let _: &T = &*p; + /// ``` + pub TRANSMUTE_PTR_TO_REF, + complexity, + "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:** + /// ```rust + /// let x = 1_u32; + /// unsafe { + /// let _: char = std::mem::transmute(x); // where x: u32 + /// } + /// + /// // should be: + /// let _ = std::char::from_u32(x).unwrap(); + /// ``` + 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`. + /// + /// **Why is this bad?** Not every byte slice is a valid UTF-8 string. + /// + /// **Known problems:** + /// - [`from_utf8`] 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 UTF-8, + /// use [`from_utf8_unchecked`] which is as fast as `transmute` + /// but has a semantically meaningful name. + /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`. + /// + /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html + /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html + /// + /// **Example:** + /// ```rust + /// let b: &[u8] = &[1_u8, 2_u8]; + /// unsafe { + /// let _: &str = std::mem::transmute(b); // where b: &[u8] + /// } + /// + /// // should be: + /// let _ = std::str::from_utf8(b).unwrap(); + /// ``` + pub TRANSMUTE_BYTES_TO_STR, + complexity, + "transmutes from a `&[u8]` to a `&str`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from an integer to a `bool`. + /// + /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = 1_u8; + /// unsafe { + /// let _: bool = std::mem::transmute(x); // where x: u8 + /// } + /// + /// // should be: + /// let _: bool = x != 0; + /// ``` + pub TRANSMUTE_INT_TO_BOOL, + complexity, + "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. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// unsafe { + /// let _: f32 = std::mem::transmute(1_u32); // where x: u32 + /// } + /// + /// // should be: + /// let _: f32 = f32::from_bits(1_u32); + /// ``` + pub TRANSMUTE_INT_TO_FLOAT, + complexity, + "transmutes from an integer to a float" +} + +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. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// unsafe { + /// let _: u32 = std::mem::transmute(1f32); + /// } + /// + /// // should be: + /// let _: u32 = 1f32.to_bits(); + /// ``` + pub TRANSMUTE_FLOAT_TO_INT, + complexity, + "transmutes from a float to an integer" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from a pointer to a pointer, or + /// from a reference to a reference. + /// + /// **Why is this bad?** Transmutes are dangerous, and these can instead be + /// written as casts. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let ptr = &1u32 as *const u32; + /// unsafe { + /// // pointer-to-pointer transmute + /// let _: *const f32 = std::mem::transmute(ptr); + /// // ref-ref transmute + /// let _: &f32 = std::mem::transmute(&1u32); + /// } + /// // These can be respectively written: + /// let _ = ptr as *const f32; + /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; + /// ``` + pub TRANSMUTE_PTR_TO_PTR, + complexity, + "transmutes from a pointer to a pointer / a reference to a reference" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes between collections whose + /// types have different ABI, size or alignment. + /// + /// **Why is this bad?** This is undefined behavior. + /// + /// **Known problems:** Currently, we cannot know whether a type is a + /// collection, so we just lint the ones that come with `std`. + /// + /// **Example:** + /// ```rust + /// // different size, therefore likely out-of-bounds memory access + /// // You absolutely do not want this in your code! + /// unsafe { + /// std::mem::transmute::<_, Vec>(vec![2_u16]) + /// }; + /// ``` + /// + /// You must always iterate, map and collect the values: + /// + /// ```rust + /// vec![2_u16].into_iter().map(u32::from).collect::>(); + /// ``` + pub UNSOUND_COLLECTION_TRANSMUTE, + correctness, + "transmute between collections of layout-incompatible types" +} +declare_lint_pass!(Transmute => [ + CROSSPOINTER_TRANSMUTE, + TRANSMUTE_PTR_TO_REF, + 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_FLOAT_TO_INT, + UNSOUND_COLLECTION_TRANSMUTE, +]); + +// used to check for UNSOUND_COLLECTION_TRANSMUTE +static COLLECTIONS: &[&[&str]] = &[ + &paths::VEC, + &paths::VEC_DEQUE, + &paths::BINARY_HEAP, + &paths::BTREESET, + &paths::BTREEMAP, + &paths::HASHSET, + &paths::HASHMAP, +]; +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute { + #[allow(clippy::similar_names, clippy::too_many_lines)] + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref path_expr, ref args) = e.kind; + if let ExprKind::Path(ref qpath) = path_expr.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::TRANSMUTE); + then { + let from_ty = cx.tables.expr_ty(&args[0]); + let to_ty = cx.tables.expr_ty(e); + + match (&from_ty.kind, &to_ty.kind) { + _ if from_ty == to_ty => span_lint( + cx, + USELESS_TRANSMUTE, + e.span, + &format!("transmute from a type (`{}`) to itself", from_ty), + ), + (&ty::Ref(_, rty, rty_mutbl), &ty::RawPtr(ptr_ty)) => span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from a reference to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let rty_and_mut = ty::TypeAndMut { + ty: rty, + mutbl: rty_mutbl, + }; + + let sugg = if ptr_ty == rty_and_mut { + arg.as_ty(to_ty) + } else { + arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) + }; + + diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ), + (&ty::Int(_), &ty::RawPtr(_)) | (&ty::Uint(_), &ty::RawPtr(_)) => span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from an integer to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + diag.span_suggestion( + e.span, + "try", + arg.as_ty(&to_ty.to_string()).to_string(), + Applicability::Unspecified, + ); + } + }, + ), + (&ty::Float(_), &ty::Ref(..)) + | (&ty::Float(_), &ty::RawPtr(_)) + | (&ty::Char, &ty::Ref(..)) + | (&ty::Char, &ty::RawPtr(_)) => span_lint( + cx, + WRONG_TRANSMUTE, + e.span, + &format!("transmute from a `{}` to a pointer", from_ty), + ), + (&ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint( + cx, + CROSSPOINTER_TRANSMUTE, + e.span, + &format!( + "transmute from a type (`{}`) to the type that it points to (`{}`)", + from_ty, to_ty + ), + ), + (_, &ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint( + cx, + CROSSPOINTER_TRANSMUTE, + e.span, + &format!( + "transmute from a type (`{}`) to a pointer to that type (`{}`)", + from_ty, to_ty + ), + ), + (&ty::RawPtr(from_pty), &ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_REF, + e.span, + &format!( + "transmute from a pointer type (`{}`) to a reference type \ + (`{}`)", + from_ty, to_ty + ), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let (deref, cast) = if mutbl == Mutability::Mut { + ("&mut *", "*mut") + } else { + ("&*", "*const") + }; + + let arg = if from_pty.ty == to_ref_ty { + arg + } else { + arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty))) + }; + + diag.span_suggestion( + e.span, + "try", + sugg::make_unop(deref, arg).to_string(), + Applicability::Unspecified, + ); + }, + ), + (&ty::Int(ast::IntTy::I32), &ty::Char) | (&ty::Uint(ast::UintTy::U32), &ty::Char) => { + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_CHAR, + e.span, + &format!("transmute from a `{}` to a `char`", from_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::Int(_) = from_ty.kind { + arg.as_ty(ast::UintTy::U32.name_str()) + } else { + arg + }; + diag.span_suggestion( + e.span, + "consider using", + format!("std::char::from_u32({}).unwrap()", arg.to_string()), + Applicability::Unspecified, + ); + }, + ) + }, + (&ty::Ref(_, ty_from, from_mutbl), &ty::Ref(_, ty_to, to_mutbl)) => { + if_chain! { + if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind); + if let ty::Uint(ast::UintTy::U8) = slice_ty.kind; + if from_mutbl == to_mutbl; + then { + let postfix = if from_mutbl == Mutability::Mut { + "_mut" + } else { + "" + }; + + span_lint_and_sugg( + cx, + TRANSMUTE_BYTES_TO_STR, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + "consider using", + format!( + "std::str::from_utf8{}({}).unwrap()", + postfix, + snippet(cx, args[0].span, ".."), + ), + Applicability::Unspecified, + ); + } else { + if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { + span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a reference to a reference", + |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let ty_from_and_mut = ty::TypeAndMut { + ty: ty_from, + mutbl: from_mutbl + }; + let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: to_mutbl }; + let sugg_paren = arg + .as_ty(cx.tcx.mk_ptr(ty_from_and_mut)) + .as_ty(cx.tcx.mk_ptr(ty_to_and_mut)); + let sugg = if to_mutbl == Mutability::Mut { + sugg_paren.mut_addr_deref() + } else { + sugg_paren.addr_deref() + }; + diag.span_suggestion( + e.span, + "try", + sugg.to_string(), + Applicability::Unspecified, + ); + }, + ) + } + } + } + }, + (&ty::RawPtr(_), &ty::RawPtr(to_ty)) => span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a pointer to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let sugg = arg.as_ty(cx.tcx.mk_ptr(to_ty)); + diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ), + (&ty::Int(ast::IntTy::I8), &ty::Bool) | (&ty::Uint(ast::UintTy::U8), &ty::Bool) => { + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_BOOL, + e.span, + &format!("transmute from a `{}` to a `bool`", from_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let zero = sugg::Sugg::NonParen(Cow::from("0")); + diag.span_suggestion( + e.span, + "consider using", + sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(), + Applicability::Unspecified, + ); + }, + ) + }, + (&ty::Int(_), &ty::Float(_)) | (&ty::Uint(_), &ty::Float(_)) => span_lint_and_then( + cx, + TRANSMUTE_INT_TO_FLOAT, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::Int(int_ty) = from_ty.kind { + 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!("{}::from_bits({})", to_ty, arg.to_string()), + Applicability::Unspecified, + ); + }, + ), + (&ty::Float(float_ty), &ty::Int(_)) | (&ty::Float(float_ty), &ty::Uint(_)) => span_lint_and_then( + cx, + TRANSMUTE_FLOAT_TO_INT, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |diag| { + let mut expr = &args[0]; + let mut arg = sugg::Sugg::hir(cx, expr, ".."); + + if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind { + expr = &inner_expr; + } + + if_chain! { + // if the expression is a float literal and it is unsuffixed then + // add a suffix so the suggestion is valid and unambiguous + let op = format!("{}{}", arg, float_ty.name_str()).into(); + if let ExprKind::Lit(lit) = &expr.kind; + if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; + then { + match arg { + sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op), + _ => arg = sugg::Sugg::NonParen(op) + } + } + } + + arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into()); + + // cast the result of `to_bits` if `to_ty` is signed + arg = if let ty::Int(int_ty) = to_ty.kind { + arg.as_ty(int_ty.name_str().to_string()) + } else { + arg + }; + + diag.span_suggestion( + e.span, + "consider using", + arg.to_string(), + Applicability::Unspecified, + ); + }, + ), + (&ty::Adt(ref from_adt, ref from_substs), &ty::Adt(ref to_adt, ref to_substs)) => { + if from_adt.did != to_adt.did || + !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) { + return; + } + if from_substs.types().zip(to_substs.types()) + .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) { + span_lint( + cx, + UNSOUND_COLLECTION_TRANSMUTE, + e.span, + &format!( + "transmute from `{}` to `{}` with mismatched layout is unsound", + from_ty, + to_ty + ) + ); + } + }, + _ => return, + } + } + } + } +} + +/// Gets the snippet of `Bar` in `…::transmute`. If that snippet is +/// not available , use +/// the type's `ToString` implementation. In weird cases it could lead to types +/// with invalid `'_` +/// lifetime, but it should be rare. +fn get_type_snippet(cx: &LateContext<'_, '_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String { + let seg = last_path_segment(path); + if_chain! { + if let Some(ref params) = seg.args; + if !params.parenthesized; + if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }).nth(1); + if let TyKind::Rptr(_, ref to_ty) = to_ty.kind; + then { + return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string(); + } + } + + to_ref_ty.to_string() +} + +// check if the component types of the transmuted collection and the result have different ABI, +// size or alignment +fn is_layout_incompatible<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { + let empty_param_env = ty::ParamEnv::empty(); + // check if `from` and `to` are normalizable to avoid ICE (#4968) + if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) { + return false; + } + let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from)); + let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to)); + if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { + from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi + } else { + // no idea about layout, so don't lint + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmuting_null.rs new file mode 100644 index 0000000000000..1d0332c580500 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmuting_null.rs @@ -0,0 +1,89 @@ +use crate::consts::{constant_context, Constant}; +use crate::utils::{match_qpath, paths, span_lint}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for transmute calls which would receive a null pointer. + /// + /// **Why is this bad?** Transmuting a null pointer is undefined behavior. + /// + /// **Known problems:** Not all cases can be detected at the moment of this writing. + /// For example, variables which hold a null pointer and are then fed to a `transmute` + /// call, aren't detectable yet. + /// + /// **Example:** + /// ```rust + /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) }; + /// ``` + pub TRANSMUTING_NULL, + correctness, + "transmutes from a null pointer to a reference, which is undefined behavior" +} + +declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]); + +const LINT_MSG: &str = "transmuting a known null pointer into a reference."; + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TransmutingNull { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &paths::STD_MEM_TRANSMUTE); + if args.len() == 1; + + then { + + // Catching transmute over constants that resolve to `null`. + let mut const_eval_context = constant_context(cx, cx.tables); + if_chain! { + if let ExprKind::Path(ref _qpath) = args[0].kind; + let x = const_eval_context.expr(&args[0]); + if let Some(constant) = x; + if let Constant::RawPtr(0) = constant; + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) + } + } + + // Catching: + // `std::mem::transmute(0 as *const i32)` + if_chain! { + if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].kind; + if let ExprKind::Lit(ref lit) = inner_expr.kind; + if let LitKind::Int(0, _) = lit.node; + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) + } + } + + // Catching: + // `std::mem::transmute(std::ptr::null::())` + if_chain! { + if let ExprKind::Call(ref func1, ref args1) = args[0].kind; + if let ExprKind::Path(ref path1) = func1.kind; + if match_qpath(path1, &paths::STD_PTR_NULL); + if args1.is_empty(); + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) + } + } + + // FIXME: + // Also catch transmutations of variables which are known nulls. + // To do this, MIR const propagation seems to be the better tool. + // Whenever MIR const prop routines are more developed, this will + // become available. As of this writing (25/03/19) it is not yet. + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs b/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs new file mode 100644 index 0000000000000..2c101220c5d68 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -0,0 +1,178 @@ +use std::cmp; + +use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::config::Config as SessionConfig; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; +use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by reference, where + /// the argument type is `Copy` and small enough to be more efficient to always + /// pass by value. + /// + /// **Why is this bad?** In many calling conventions instances of structs will + /// be passed through registers if they fit into two or less general purpose + /// registers. + /// + /// **Known problems:** This lint is target register size dependent, it is + /// limited to 32-bit to try and reduce portability problems between 32 and + /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit + /// will be different. + /// + /// The configuration option `trivial_copy_size_limit` can be set to override + /// this limit for a project. + /// + /// This lint attempts to allow passing arguments by reference if a reference + /// to that argument is returned. This is implemented by comparing the lifetime + /// of the argument and return value for equality. However, this can cause + /// false positives in cases involving multiple lifetimes that are bounded by + /// each other. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// fn foo(v: &u32) {} + /// ``` + /// + /// ```rust + /// // Better + /// fn foo(v: u32) {} + /// ``` + pub TRIVIALLY_COPY_PASS_BY_REF, + pedantic, + "functions taking small copyable arguments by reference" +} + +#[derive(Copy, Clone)] +pub struct TriviallyCopyPassByRef { + limit: u64, +} + +impl<'a, 'tcx> TriviallyCopyPassByRef { + pub fn new(limit: Option, target: &SessionConfig) -> Self { + let limit = limit.unwrap_or_else(|| { + let bit_width = u64::from(target.ptr_width); + // Cap the calculated bit width at 32-bits to reduce + // portability problems between 32 and 64-bit targets + let bit_width = cmp::min(bit_width, 32); + #[allow(clippy::integer_division)] + let byte_width = bit_width / 8; + // Use a limit of 2 times the register byte width + byte_width * 2 + }); + Self { limit } + } + + fn check_poly_fn(&mut self, cx: &LateContext<'_, 'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { + let fn_def_id = cx.tcx.hir().local_def_id(hir_id); + + let fn_sig = cx.tcx.fn_sig(fn_def_id); + let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); + + // Use lifetimes to determine if we're returning a reference to the + // argument. In that case we can't switch to pass-by-value as the + // argument will not live long enough. + let output_lts = match fn_sig.output().kind { + ty::Ref(output_lt, _, _) => vec![output_lt], + ty::Adt(_, substs) => substs.regions().collect(), + _ => vec![], + }; + + for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) { + // All spans generated from a proc-macro invocation are the same... + match span { + Some(s) if s == input.span => return, + _ => (), + } + + if_chain! { + if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind; + if !output_lts.contains(&input_lt); + if is_copy(cx, ty); + if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); + if size <= self.limit; + if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; + then { + let value_type = if is_self_ty(decl_ty) { + "self".into() + } else { + snippet(cx, decl_ty.span, "_").into() + }; + span_lint_and_sugg( + cx, + TRIVIALLY_COPY_PASS_BY_REF, + input.span, + &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit), + "consider passing by value instead", + value_type, + Applicability::Unspecified, + ); + } + } + } + } +} + +impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef { + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) { + if item.span.from_expansion() { + return; + } + + if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { + self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + _body: &'tcx Body<'_>, + span: Span, + hir_id: HirId, + ) { + if span.from_expansion() { + return; + } + + match kind { + FnKind::ItemFn(.., header, _, attrs) => { + if header.abi != Abi::Rust { + return; + } + for a in attrs { + if a.meta_item_list().is_some() && a.check_name(sym!(proc_macro_derive)) { + return; + } + } + }, + FnKind::Method(..) => (), + _ => return, + } + + // Exclude non-inherent impls + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | + ItemKind::Trait(..)) + { + return; + } + } + + self.check_poly_fn(cx, hir_id, decl, Some(span)); + } +} diff --git a/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs new file mode 100644 index 0000000000000..7018fa6804ba7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/try_err.rs @@ -0,0 +1,122 @@ +use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usages of `Err(x)?`. + /// + /// **Why is this bad?** The `?` operator is designed to allow calls that + /// can fail to be easily chained. For example, `foo()?.bar()` or + /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will + /// always return), it is more clear to write `return Err(x)`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// Err("failed")?; + /// } + /// Ok(0) + /// } + /// ``` + /// Could be written: + /// + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// return Err("failed".into()); + /// } + /// Ok(0) + /// } + /// ``` + pub TRY_ERR, + style, + "return errors explicitly rather than hiding them behind a `?`" +} + +declare_lint_pass!(TryErr => [TRY_ERR]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TryErr { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + // Looks for a structure like this: + // match ::std::ops::Try::into_result(Err(5)) { + // ::std::result::Result::Err(err) => + // #[allow(unreachable_code)] + // return ::std::ops::Try::from_error(::std::convert::From::from(err)), + // ::std::result::Result::Ok(val) => + // #[allow(unreachable_code)] + // val, + // }; + if_chain! { + if !in_external_macro(cx.tcx.sess, expr.span); + if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind; + if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind; + if let ExprKind::Path(ref match_fun_path) = match_fun.kind; + if match_qpath(match_fun_path, &paths::TRY_INTO_RESULT); + if let Some(ref try_arg) = try_args.get(0); + if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind; + if let Some(ref err_arg) = err_args.get(0); + if let ExprKind::Path(ref err_fun_path) = err_fun.kind; + if match_qpath(err_fun_path, &paths::RESULT_ERR); + if let Some(return_type) = find_err_return_type(cx, &expr.kind); + + then { + let err_type = cx.tables.expr_ty(err_arg); + let origin_snippet = if err_arg.span.from_expansion() { + snippet_with_macro_callsite(cx, err_arg.span, "_") + } else { + snippet(cx, err_arg.span, "_") + }; + let suggestion = if err_type == return_type { + format!("return Err({})", origin_snippet) + } else { + format!("return Err({}.into())", origin_snippet) + }; + + span_lint_and_sugg( + cx, + TRY_ERR, + expr.span, + "returning an `Err(_)` with the `?` operator", + "try this", + suggestion, + Applicability::MachineApplicable + ); + } + } + } +} + +// In order to determine whether to suggest `.into()` or not, we need to find the error type the +// function returns. To do that, we look for the From::from call (see tree above), and capture +// its output type. +fn find_err_return_type<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { + if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr { + arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty)) + } else { + None + } +} + +// Check for From::from in one of the match arms. +fn find_err_return_type_arm<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arm: &'tcx Arm<'_>) -> Option> { + if_chain! { + if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind; + if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind; + if let ExprKind::Path(ref from_error_fn) = from_error_path.kind; + if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR); + if let Some(from_error_arg) = from_error_args.get(0); + then { + Some(cx.tables.expr_ty(from_error_arg)) + } else { + None + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs new file mode 100644 index 0000000000000..6ed9ff22e4664 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -0,0 +1,2562 @@ +#![allow(rustc::default_hash_types)] + +use std::borrow::Cow; +use std::cmp::Ordering; +use std::collections::BTreeMap; + +use if_chain::if_chain; +use rustc_ast::ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{ + BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, + TraitItem, TraitItemKind, TyKind, UnOp, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TypeckTables}; +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; +use rustc_span::hygiene::{ExpnKind, MacroKind}; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; +use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; +use rustc_typeck::hir_ty_to_ty; + +use crate::consts::{constant, Constant}; +use crate::utils::paths; +use crate::utils::{ + clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, + last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, + qpath_res, same_tys, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for use of `Box>` anywhere in the code. + /// + /// **Why is this bad?** `Vec` already keeps its contents in a separate area on + /// the heap. So if you `Box` it, you just add another level of indirection + /// without any benefit whatsoever. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// struct X { + /// values: Box>, + /// } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// struct X { + /// values: Vec, + /// } + /// ``` + pub BOX_VEC, + perf, + "usage of `Box>`, vector elements are already on the heap" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. + /// + /// **Why is this bad?** `Vec` already keeps its contents in a separate area on + /// the heap. So if you `Box` its contents, you just add another level of indirection. + /// + /// **Known problems:** Vec> makes sense if T is a large type (see #3530, + /// 1st comment). + /// + /// **Example:** + /// ```rust + /// struct X { + /// values: Vec>, + /// } + /// ``` + /// + /// Better: + /// + /// ```rust + /// struct X { + /// values: Vec, + /// } + /// ``` + pub VEC_BOX, + complexity, + "usage of `Vec>` where T: Sized, vector elements are already on the heap" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `Option>` in function signatures and type + /// definitions + /// + /// **Why is this bad?** `Option<_>` represents an optional value. `Option>` + /// represents an optional optional value which is logically the same thing as an optional + /// value but has an unneeded extra level of wrapping. + /// + /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases, + /// consider a custom `enum` instead, with clear names for each case. + /// + /// **Known problems:** None. + /// + /// **Example** + /// ```rust + /// fn get_data() -> Option> { + /// None + /// } + /// ``` + /// + /// Better: + /// + /// ```rust + /// pub enum Contents { + /// Data(Vec), // Was Some(Some(Vec)) + /// NotYetFetched, // Was Some(None) + /// None, // Was None + /// } + /// + /// fn get_data() -> Contents { + /// Contents::None + /// } + /// ``` + pub OPTION_OPTION, + pedantic, + "usage of `Option>`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of any `LinkedList`, suggesting to use a + /// `Vec` or a `VecDeque` (formerly called `RingBuf`). + /// + /// **Why is this bad?** Gankro says: + /// + /// > The TL;DR of `LinkedList` is that it's built on a massive amount of + /// pointers and indirection. + /// > It wastes memory, it has terrible cache locality, and is all-around slow. + /// `RingBuf`, while + /// > "only" amortized for push/pop, should be faster in the general case for + /// almost every possible + /// > workload, and isn't even amortized at all if you can predict the capacity + /// you need. + /// > + /// > `LinkedList`s are only really good if you're doing a lot of merging or + /// splitting of lists. + /// > This is because they can just mangle some pointers instead of actually + /// copying the data. Even + /// > if you're doing a lot of insertion in the middle of the list, `RingBuf` + /// can still be better + /// > because of how expensive it is to seek to the middle of a `LinkedList`. + /// + /// **Known problems:** False positives – the instances where using a + /// `LinkedList` makes sense are few and far between, but they can still happen. + /// + /// **Example:** + /// ```rust + /// # use std::collections::LinkedList; + /// let x: LinkedList = LinkedList::new(); + /// ``` + pub LINKEDLIST, + pedantic, + "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `&Box` anywhere in the code. + /// + /// **Why is this bad?** Any `&Box` can also be a `&T`, which is more + /// general. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// fn foo(bar: &Box) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// fn foo(bar: &T) { ... } + /// ``` + pub BORROWED_BOX, + complexity, + "a borrow of a boxed type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of redundant allocations anywhere in the code. + /// + /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Box<&T>` + /// add an unnecessary level of indirection. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::rc::Rc; + /// fn foo(bar: Rc<&usize>) {} + /// ``` + /// + /// Better: + /// + /// ```rust + /// fn foo(bar: &usize) {} + /// ``` + pub REDUNDANT_ALLOCATION, + perf, + "redundant allocation" +} + +pub struct Types { + vec_box_size_threshold: u64, +} + +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Types { + fn check_fn( + &mut self, + cx: &LateContext<'_, '_>, + _: FnKind<'_>, + decl: &FnDecl<'_>, + _: &Body<'_>, + _: Span, + id: HirId, + ) { + // Skip trait implementations; see issue #605. + if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id)) { + if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + } + + self.check_fn_decl(cx, decl); + } + + fn check_struct_field(&mut self, cx: &LateContext<'_, '_>, field: &hir::StructField<'_>) { + self.check_ty(cx, &field.ty, false); + } + + fn check_trait_item(&mut self, cx: &LateContext<'_, '_>, item: &TraitItem<'_>) { + match item.kind { + TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false), + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl), + _ => (), + } + } + + fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) { + if let Some(ref ty) = local.ty { + self.check_ty(cx, ty, true); + } + } +} + +/// Checks if `qpath` has last segment with type parameter matching `path` +fn match_type_parameter(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, path: &[&str]) -> Option { + let last = last_path_segment(qpath); + if_chain! { + if let Some(ref params) = last.args; + if !params.parenthesized; + if let Some(ty) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + if let TyKind::Path(ref qpath) = ty.kind; + if let Some(did) = qpath_res(cx, qpath, ty.hir_id).opt_def_id(); + if match_def_path(cx, did, path); + then { + return Some(ty.span); + } + } + None +} + +fn match_borrows_parameter(_cx: &LateContext<'_, '_>, qpath: &QPath<'_>) -> Option { + let last = last_path_segment(qpath); + if_chain! { + if let Some(ref params) = last.args; + if !params.parenthesized; + if let Some(ty) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + if let TyKind::Rptr(..) = ty.kind; + then { + return Some(ty.span); + } + } + None +} + +impl Types { + pub fn new(vec_box_size_threshold: u64) -> Self { + Self { vec_box_size_threshold } + } + + fn check_fn_decl(&mut self, cx: &LateContext<'_, '_>, decl: &FnDecl<'_>) { + for input in decl.inputs { + self.check_ty(cx, input, false); + } + + if let FnRetTy::Return(ref ty) = decl.output { + self.check_ty(cx, ty, false); + } + } + + /// Recursively check for `TypePass` lints in the given type. Stop at the first + /// lint found. + /// + /// The parameter `is_local` distinguishes the context of the type; types from + /// local bindings should only be checked for the `BORROWED_BOX` lint. + #[allow(clippy::too_many_lines)] + fn check_ty(&mut self, cx: &LateContext<'_, '_>, hir_ty: &hir::Ty<'_>, is_local: bool) { + if hir_ty.span.from_expansion() { + return; + } + match hir_ty.kind { + TyKind::Path(ref qpath) if !is_local => { + let hir_id = hir_ty.hir_id; + let res = qpath_res(cx, qpath, hir_id); + if let Some(def_id) = res.opt_def_id() { + if Some(def_id) == cx.tcx.lang_items().owned_box() { + if let Some(span) = match_borrows_parameter(cx, qpath) { + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Box<&T>`", + "try", + snippet(cx, span, "..").to_string(), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + span_lint_and_help( + cx, + BOX_VEC, + hir_ty.span, + "you seem to be trying to use `Box>`. Consider using just `Vec`", + None, + "`Vec` is already on the heap, `Box>` makes an extra allocation.", + ); + return; // don't recurse into the type + } + } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { + if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) { + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Rc>`", + "try", + snippet(cx, span, "..").to_string(), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) { + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Rc>`", + "try", + snippet(cx, span, "..").to_string(), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if let Some(span) = match_borrows_parameter(cx, qpath) { + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Rc<&T>`", + "try", + snippet(cx, span, "..").to_string(), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) { + if_chain! { + // Get the _ part of Vec<_> + if let Some(ref last) = last_path_segment(qpath).args; + if let Some(ty) = last.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + // ty is now _ at this point + if let TyKind::Path(ref ty_qpath) = ty.kind; + let res = qpath_res(cx, ty_qpath, ty.hir_id); + if let Some(def_id) = res.opt_def_id(); + if Some(def_id) == cx.tcx.lang_items().owned_box(); + // At this point, we know ty is Box, now get T + if let Some(ref last) = last_path_segment(ty_qpath).args; + if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); + if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); + if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); + if ty_ty_size <= self.vec_box_size_threshold; + then { + span_lint_and_sugg( + cx, + VEC_BOX, + hir_ty.span, + "`Vec` is already on the heap, the boxing is unnecessary.", + "try", + format!("Vec<{}>", ty_ty), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + } + } else if cx.tcx.is_diagnostic_item(sym!(option_type), def_id) { + if match_type_parameter(cx, qpath, &paths::OPTION).is_some() { + span_lint( + cx, + OPTION_OPTION, + hir_ty.span, + "consider using `Option` instead of `Option>` or a custom \ + enum if you need to distinguish all 3 cases", + ); + return; // don't recurse into the type + } + } else if match_def_path(cx, def_id, &paths::LINKED_LIST) { + span_lint_and_help( + cx, + LINKEDLIST, + hir_ty.span, + "I see you're using a LinkedList! Perhaps you meant some other data structure?", + None, + "a `VecDeque` might work", + ); + return; // don't recurse into the type + } + } + match *qpath { + QPath::Resolved(Some(ref ty), ref p) => { + self.check_ty(cx, ty, is_local); + for ty in p.segments.iter().flat_map(|seg| { + seg.args + .as_ref() + .map_or_else(|| [].iter(), |params| params.args.iter()) + .filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + }) { + self.check_ty(cx, ty, is_local); + } + }, + QPath::Resolved(None, ref p) => { + for ty in p.segments.iter().flat_map(|seg| { + seg.args + .as_ref() + .map_or_else(|| [].iter(), |params| params.args.iter()) + .filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + }) { + self.check_ty(cx, ty, is_local); + } + }, + QPath::TypeRelative(ref ty, ref seg) => { + self.check_ty(cx, ty, is_local); + if let Some(ref params) = seg.args { + for ty in params.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) { + self.check_ty(cx, ty, is_local); + } + } + }, + } + }, + TyKind::Rptr(ref lt, ref mut_ty) => self.check_ty_rptr(cx, hir_ty, is_local, lt, mut_ty), + // recurse + TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => { + self.check_ty(cx, ty, is_local) + }, + TyKind::Tup(tys) => { + for ty in tys { + self.check_ty(cx, ty, is_local); + } + }, + _ => {}, + } + } + + fn check_ty_rptr( + &mut self, + cx: &LateContext<'_, '_>, + hir_ty: &hir::Ty<'_>, + is_local: bool, + lt: &Lifetime, + mut_ty: &MutTy<'_>, + ) { + match mut_ty.ty.kind { + TyKind::Path(ref qpath) => { + let hir_id = mut_ty.ty.hir_id; + let def = qpath_res(cx, qpath, hir_id); + if_chain! { + if let Some(def_id) = def.opt_def_id(); + if Some(def_id) == cx.tcx.lang_items().owned_box(); + if let QPath::Resolved(None, ref path) = *qpath; + if let [ref bx] = *path.segments; + if let Some(ref params) = bx.args; + if !params.parenthesized; + if let Some(inner) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + then { + if is_any_trait(inner) { + // Ignore `Box` types; see issue #1884 for details. + return; + } + + let ltopt = if lt.is_elided() { + String::new() + } else { + format!("{} ", lt.name.ident().as_str()) + }; + + if mut_ty.mutbl == Mutability::Mut { + // Ignore `&mut Box` types; see issue #2907 for + // details. + return; + } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BORROWED_BOX, + hir_ty.span, + "you seem to be trying to use `&Box`. Consider using just `&T`", + "try", + format!( + "&{}{}", + ltopt, + &snippet_with_applicability(cx, inner.span, "..", &mut applicability) + ), + Applicability::Unspecified, + ); + return; // don't recurse into the type + } + }; + self.check_ty(cx, &mut_ty.ty, is_local); + }, + _ => self.check_ty(cx, &mut_ty.ty, is_local), + } + } +} + +// Returns true if given type is `Any` trait. +fn is_any_trait(t: &hir::Ty<'_>) -> bool { + if_chain! { + if let TyKind::TraitObject(ref traits, _) = t.kind; + if !traits.is_empty(); + // Only Send/Sync can be used as additional traits, so it is enough to + // check only the first trait. + if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT); + then { + return true; + } + } + + false +} + +declare_clippy_lint! { + /// **What it does:** Checks for binding a unit value. + /// + /// **Why is this bad?** A unit value cannot usefully be used anywhere. So + /// binding one is kind of pointless. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = { + /// 1; + /// }; + /// ``` + pub LET_UNIT_VALUE, + pedantic, + "creating a `let` binding to a value of unit type, which usually can't be used afterwards" +} + +declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnitValue { + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Local(ref local) = stmt.kind { + if is_unit(cx.tables.pat_ty(&local.pat)) { + if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { + return; + } + if higher::is_from_for_desugar(local) { + return; + } + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + if let Some(expr) = &local.init { + let snip = snippet_with_macro_callsite(cx, expr.span, "()"); + diag.span_suggestion( + stmt.span, + "omit the `let` binding", + format!("{};", snip), + Applicability::MachineApplicable, // snippet + ); + } + }, + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons to unit. This includes all binary + /// comparisons (like `==` and `<`) and asserts. + /// + /// **Why is this bad?** Unit is always equal to itself, and thus is just a + /// clumsily written constant. Mostly this happens when someone accidentally + /// adds semicolons at the end of the operands. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// # fn baz() {}; + /// if { + /// foo(); + /// } == { + /// bar(); + /// } { + /// baz(); + /// } + /// ``` + /// is equal to + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// # fn baz() {}; + /// { + /// foo(); + /// bar(); + /// baz(); + /// } + /// ``` + /// + /// For asserts: + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// assert_eq!({ foo(); }, { bar(); }); + /// ``` + /// will always succeed + pub UNIT_CMP, + correctness, + "comparing unit values" +} + +declare_lint_pass!(UnitCmp => [UNIT_CMP]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitCmp { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) { + if expr.span.from_expansion() { + if let Some(callee) = expr.span.source_callee() { + if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { + if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + let op = cmp.node; + if op.is_comparison() && is_unit(cx.tables.expr_ty(left)) { + let result = match &*symbol.as_str() { + "assert_eq" | "debug_assert_eq" => "succeed", + "assert_ne" | "debug_assert_ne" => "fail", + _ => return, + }; + span_lint( + cx, + UNIT_CMP, + expr.span, + &format!( + "`{}` of unit values detected. This will always {}", + symbol.as_str(), + result + ), + ); + } + } + } + } + return; + } + if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + let op = cmp.node; + if op.is_comparison() && is_unit(cx.tables.expr_ty(left)) { + let result = match op { + BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", + _ => "false", + }; + span_lint( + cx, + UNIT_CMP, + expr.span, + &format!( + "{}-comparison of unit values detected. This will always be {}", + op.as_str(), + result + ), + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for passing a unit value as an argument to a function without using a + /// unit literal (`()`). + /// + /// **Why is this bad?** This is likely the result of an accidental semicolon. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// foo({ + /// let a = bar(); + /// baz(a); + /// }) + /// ``` + pub UNIT_ARG, + complexity, + "passing unit to a function" +} + +declare_lint_pass!(UnitArg => [UNIT_ARG]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + // apparently stuff in the desugaring of `?` can trigger this + // so check for that here + // only the calls to `Try::from_error` is marked as desugared, + // so we need to check both the current Expr and its parent. + if is_questionmark_desugar_marked_call(expr) { + return; + } + if_chain! { + let map = &cx.tcx.hir(); + let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); + if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; + if is_questionmark_desugar_marked_call(parent_expr); + then { + return; + } + } + + match expr.kind { + ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => { + for arg in args { + if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { + if let ExprKind::Match(.., match_source) = &arg.kind { + if *match_source == MatchSource::TryDesugar { + continue; + } + } + + span_lint_and_sugg( + cx, + UNIT_ARG, + arg.span, + "passing a unit value to a function", + "if you intended to pass a unit value, use a unit literal instead", + "()".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + }, + _ => (), + } + } +} + +fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { + use rustc_span::hygiene::DesugaringKind; + if let ExprKind::Call(ref callee, _) = expr.kind { + callee.span.is_desugaring(DesugaringKind::QuestionMark) + } else { + false + } +} + +fn is_unit(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Tuple(slice) if slice.is_empty() => true, + _ => false, + } +} + +fn is_unit_literal(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Tup(ref slice) if slice.is_empty() => true, + _ => false, + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts from any numerical to a float type where + /// the receiving type cannot store all values from the original type without + /// rounding errors. This possible rounding is to be expected, so this lint is + /// `Allow` by default. + /// + /// Basically, this warns on casting any integer with 32 or more bits to `f32` + /// or any 64-bit integer to `f64`. + /// + /// **Why is this bad?** It's not bad at all. But in some applications it can be + /// helpful to know where precision loss can take place. This lint can help find + /// those places in the code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = u64::MAX; + /// x as f64; + /// ``` + pub CAST_PRECISION_LOSS, + pedantic, + "casts that cause loss of precision, e.g., `x as f32` where `x: u64`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts from a signed to an unsigned numerical + /// type. In this case, negative values wrap around to large positive values, + /// which can be quite surprising in practice. However, as the cast works as + /// defined, this lint is `Allow` by default. + /// + /// **Why is this bad?** Possibly surprising results. You can activate this lint + /// as a one-time check to see where numerical wrapping can arise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let y: i8 = -1; + /// y as u128; // will return 18446744073709551615 + /// ``` + pub CAST_SIGN_LOSS, + pedantic, + "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts between numerical types that may + /// truncate large values. This is expected behavior, so the cast is `Allow` by + /// default. + /// + /// **Why is this bad?** In some problem domains, it is good practice to avoid + /// truncation. This lint can be activated to help assess where additional + /// checks could be beneficial. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn as_u8(x: u64) -> u8 { + /// x as u8 + /// } + /// ``` + pub CAST_POSSIBLE_TRUNCATION, + pedantic, + "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts from an unsigned type to a signed type of + /// the same size. Performing such a cast is a 'no-op' for the compiler, + /// i.e., nothing is changed at the bit level, and the binary representation of + /// the value is reinterpreted. This can cause wrapping if the value is too big + /// for the target signed type. However, the cast works as defined, so this lint + /// is `Allow` by default. + /// + /// **Why is this bad?** While such a cast is not bad in itself, the results can + /// be surprising when this is not the intended behavior, as demonstrated by the + /// example below. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// u32::MAX as i32; // will yield a value of `-1` + /// ``` + pub CAST_POSSIBLE_WRAP, + pedantic, + "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts between numerical types that may + /// be replaced by safe conversion functions. + /// + /// **Why is this bad?** Rust's `as` keyword will perform many kinds of + /// conversions, including silently lossy conversions. Conversion functions such + /// as `i32::from` will only perform lossless conversions. Using the conversion + /// functions prevents conversions from turning into silent lossy conversions if + /// the types of the input expressions ever change, and make it easier for + /// people reading the code to know that the conversion is lossless. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn as_u64(x: u8) -> u64 { + /// x as u64 + /// } + /// ``` + /// + /// Using `::from` would look like this: + /// + /// ```rust + /// fn as_u64(x: u8) -> u64 { + /// u64::from(x) + /// } + /// ``` + pub CAST_LOSSLESS, + pedantic, + "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts to the same type. + /// + /// **Why is this bad?** It's just unnecessary. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let _ = 2i32 as i32; + /// ``` + pub UNNECESSARY_CAST, + complexity, + "cast to the same type, e.g., `x as i32` where `x: i32`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts from a less-strictly-aligned pointer to a + /// more-strictly-aligned pointer + /// + /// **Why is this bad?** Dereferencing the resulting pointer may be undefined + /// behavior. + /// + /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar + /// on the resulting pointer is fine. + /// + /// **Example:** + /// ```rust + /// let _ = (&1u8 as *const u8) as *const u16; + /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// ``` + pub CAST_PTR_ALIGNMENT, + correctness, + "cast from a pointer to a more-strictly-aligned pointer" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts of function pointers to something other than usize + /// + /// **Why is this bad?** + /// Casting a function pointer to anything other than usize/isize is not portable across + /// architectures, because you end up losing bits if the target type is too small or end up with a + /// bunch of extra bits that waste space and add more instructions to the final binary than + /// strictly necessary for the problem + /// + /// Casting to isize also doesn't make sense since there are no signed addresses. + /// + /// **Example** + /// + /// ```rust + /// // Bad + /// fn fun() -> i32 { 1 } + /// let a = fun as i64; + /// + /// // Good + /// fn fun2() -> i32 { 1 } + /// let a = fun2 as usize; + /// ``` + pub FN_TO_NUMERIC_CAST, + style, + "casting a function pointer to a numeric type other than usize" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to + /// store address. + /// + /// **Why is this bad?** + /// Such a cast discards some bits of the function's address. If this is intended, it would be more + /// clearly expressed by casting to usize first, then casting the usize to the intended type (with + /// a comment) to perform the truncation. + /// + /// **Example** + /// + /// ```rust + /// // Bad + /// fn fn1() -> i16 { + /// 1 + /// }; + /// let _ = fn1 as i32; + /// + /// // Better: Cast to usize first, then comment with the reason for the truncation + /// fn fn2() -> i16 { + /// 1 + /// }; + /// let fn_ptr = fn2 as usize; + /// let fn_ptr_truncated = fn_ptr as i32; + /// ``` + pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + style, + "casting a function pointer to a numeric type not wide enough to store the address" +} + +/// Returns the size in bits of an integral type. +/// Will return 0 if the type is not an int or uint variant +fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { + match typ.kind { + ty::Int(i) => match i { + IntTy::Isize => tcx.data_layout.pointer_size.bits(), + IntTy::I8 => 8, + IntTy::I16 => 16, + IntTy::I32 => 32, + IntTy::I64 => 64, + IntTy::I128 => 128, + }, + ty::Uint(i) => match i { + UintTy::Usize => tcx.data_layout.pointer_size.bits(), + UintTy::U8 => 8, + UintTy::U16 => 16, + UintTy::U32 => 32, + UintTy::U64 => 64, + UintTy::U128 => 128, + }, + _ => 0, + } +} + +fn is_isize_or_usize(typ: Ty<'_>) -> bool { + match typ.kind { + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true, + _ => false, + } +} + +fn span_precision_loss_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) { + let mantissa_nbits = if cast_to_f64 { 52 } else { 23 }; + let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64; + let arch_dependent_str = "on targets with 64-bit wide pointers "; + let from_nbits_str = if arch_dependent { + "64".to_owned() + } else if is_isize_or_usize(cast_from) { + "32 or 64".to_owned() + } else { + int_ty_to_nbits(cast_from, cx.tcx).to_string() + }; + span_lint( + cx, + CAST_PRECISION_LOSS, + expr.span, + &format!( + "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \ + but `{1}`'s mantissa is only {4} bits wide)", + cast_from, + if cast_to_f64 { "f64" } else { "f32" }, + if arch_dependent { arch_dependent_str } else { "" }, + from_nbits_str, + mantissa_nbits + ), + ); +} + +fn should_strip_parens(op: &Expr<'_>, snip: &str) -> bool { + if let ExprKind::Binary(_, _, _) = op.kind { + if snip.starts_with('(') && snip.ends_with(')') { + return true; + } + } + false +} + +fn span_lossless_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // Do not suggest using From in consts/statics until it is valid to do so (see #2267). + if in_constant(cx, expr.hir_id) { + return; + } + // The suggestion is to use a function call, so if the original expression + // has parens on the outside, they are no longer needed. + let mut applicability = Applicability::MachineApplicable; + let opt = snippet_opt(cx, op.span); + let sugg = if let Some(ref snip) = opt { + if should_strip_parens(op, snip) { + &snip[1..snip.len() - 1] + } else { + snip.as_str() + } + } else { + applicability = Applicability::HasPlaceholders; + ".." + }; + + span_lint_and_sugg( + cx, + CAST_LOSSLESS, + expr.span, + &format!( + "casting `{}` to `{}` may become silently lossy if you later change the type", + cast_from, cast_to + ), + "try", + format!("{}::from({})", cast_to, sugg), + applicability, + ); +} + +enum ArchSuffix { + _32, + _64, + None, +} + +fn check_loss_of_sign(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + if !cast_from.is_signed() || cast_to.is_signed() { + return; + } + + // don't lint for positive constants + let const_val = constant(cx, &cx.tables, op); + if_chain! { + if let Some((const_val, _)) = const_val; + if let Constant::Int(n) = const_val; + if let ty::Int(ity) = cast_from.kind; + if sext(cx.tcx, n, ity) >= 0; + then { + return + } + } + + // don't lint for the result of methods that always return non-negative values + if let ExprKind::MethodCall(ref path, _, _) = op.kind { + let mut method_name = path.ident.name.as_str(); + let whitelisted_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; + + if_chain! { + if method_name == "unwrap"; + if let Some(arglist) = method_chain_args(op, &["unwrap"]); + if let ExprKind::MethodCall(ref inner_path, _, _) = &arglist[0][0].kind; + then { + method_name = inner_path.ident.name.as_str(); + } + } + + if whitelisted_methods.iter().any(|&name| method_name == name) { + return; + } + } + + span_lint( + cx, + CAST_SIGN_LOSS, + expr.span, + &format!( + "casting `{}` to `{}` may lose the sign of the value", + cast_from, cast_to + ), + ); +} + +fn check_truncation_and_wrapping(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + let arch_64_suffix = " on targets with 64-bit wide pointers"; + let arch_32_suffix = " on targets with 32-bit wide pointers"; + let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed(); + let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); + let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) = + match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) { + (true, true) | (false, false) => ( + to_nbits < from_nbits, + ArchSuffix::None, + to_nbits == from_nbits && cast_unsigned_to_signed, + ArchSuffix::None, + ), + (true, false) => ( + to_nbits <= 32, + if to_nbits == 32 { + ArchSuffix::_64 + } else { + ArchSuffix::None + }, + to_nbits <= 32 && cast_unsigned_to_signed, + ArchSuffix::_32, + ), + (false, true) => ( + from_nbits == 64, + ArchSuffix::_32, + cast_unsigned_to_signed, + if from_nbits == 64 { + ArchSuffix::_64 + } else { + ArchSuffix::_32 + }, + ), + }; + if span_truncation { + span_lint( + cx, + CAST_POSSIBLE_TRUNCATION, + expr.span, + &format!( + "casting `{}` to `{}` may truncate the value{}", + cast_from, + cast_to, + match suffix_truncation { + ArchSuffix::_32 => arch_32_suffix, + ArchSuffix::_64 => arch_64_suffix, + ArchSuffix::None => "", + } + ), + ); + } + if span_wrap { + span_lint( + cx, + CAST_POSSIBLE_WRAP, + expr.span, + &format!( + "casting `{}` to `{}` may wrap around the value{}", + cast_from, + cast_to, + match suffix_wrap { + ArchSuffix::_32 => arch_32_suffix, + ArchSuffix::_64 => arch_64_suffix, + ArchSuffix::None => "", + } + ), + ); + } +} + +fn check_lossless(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed(); + let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); + if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits && !cast_signed_to_unsigned + { + span_lossless_lint(cx, expr, op, cast_from, cast_to); + } +} + +declare_lint_pass!(Casts => [ + CAST_PRECISION_LOSS, + CAST_SIGN_LOSS, + CAST_POSSIBLE_TRUNCATION, + CAST_POSSIBLE_WRAP, + CAST_LOSSLESS, + UNNECESSARY_CAST, + CAST_PTR_ALIGNMENT, + FN_TO_NUMERIC_CAST, + FN_TO_NUMERIC_CAST_WITH_TRUNCATION, +]); + +// Check if the given type is either `core::ffi::c_void` or +// one of the platform specific `libc::::c_void` of libc. +fn is_c_void(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool { + if let ty::Adt(adt, _) = ty.kind { + let names = cx.get_def_path(adt.did); + + if names.is_empty() { + return false; + } + if names[0] == sym!(libc) || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { + return true; + } + } + false +} + +/// Returns the mantissa bits wide of a fp type. +/// Will return 0 if the type is not a fp +fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { + match typ.kind { + ty::Float(FloatTy::F32) => 23, + ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52, + _ => 0, + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Casts { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::Cast(ref ex, _) = expr.kind { + let (cast_from, cast_to) = (cx.tables.expr_ty(ex), cx.tables.expr_ty(expr)); + lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); + if let ExprKind::Lit(ref lit) = ex.kind { + if_chain! { + if let LitKind::Int(n, _) = lit.node; + if let Some(src) = snippet_opt(cx, lit.span); + if cast_to.is_floating_point(); + if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node); + let from_nbits = 128 - n.leading_zeros(); + let to_nbits = fp_ty_mantissa_nbits(cast_to); + if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting integer literal to `{}` is unnecessary", cast_to), + "try", + format!("{}_{}", n, cast_to), + Applicability::MachineApplicable, + ); + return; + } + } + match lit.node { + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, + _ => { + if cast_from.kind == cast_to.kind && !in_external_macro(cx.sess(), expr.span) { + span_lint( + cx, + UNNECESSARY_CAST, + expr.span, + &format!( + "casting to the same type is unnecessary (`{}` -> `{}`)", + cast_from, cast_to + ), + ); + } + }, + } + } + if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { + lint_numeric_casts(cx, expr, ex, cast_from, cast_to); + } + + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } + } +} + +fn lint_numeric_casts<'tcx>( + cx: &LateContext<'_, 'tcx>, + expr: &Expr<'tcx>, + cast_expr: &Expr<'_>, + cast_from: Ty<'tcx>, + cast_to: Ty<'tcx>, +) { + match (cast_from.is_integral(), cast_to.is_integral()) { + (true, false) => { + let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind { + 32 + } else { + 64 + }; + if is_isize_or_usize(cast_from) || from_nbits >= to_nbits { + span_precision_loss_lint(cx, expr, cast_from, to_nbits == 64); + } + if from_nbits < to_nbits { + span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to); + } + }, + (false, true) => { + span_lint( + cx, + CAST_POSSIBLE_TRUNCATION, + expr.span, + &format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to), + ); + if !cast_to.is_signed() { + span_lint( + cx, + CAST_SIGN_LOSS, + expr.span, + &format!( + "casting `{}` to `{}` may lose the sign of the value", + cast_from, cast_to + ), + ); + } + }, + (true, true) => { + check_loss_of_sign(cx, expr, cast_expr, cast_from, cast_to); + check_truncation_and_wrapping(cx, expr, cast_from, cast_to); + check_lossless(cx, expr, cast_expr, cast_from, cast_to); + }, + (false, false) => { + if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind, &cast_to.kind) { + span_lint( + cx, + CAST_POSSIBLE_TRUNCATION, + expr.span, + "casting `f64` to `f32` may truncate the value", + ); + } + if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind, &cast_to.kind) { + span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to); + } + }, + } +} + +fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { + if_chain! { + if let ty::RawPtr(from_ptr_ty) = &cast_from.kind; + if let ty::RawPtr(to_ptr_ty) = &cast_to.kind; + if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); + if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); + if from_layout.align.abi < to_layout.align.abi; + // with c_void, we inherently need to trust the user + if !is_c_void(cx, from_ptr_ty.ty); + // when casting from a ZST, we don't know enough to properly lint + if !from_layout.is_zst(); + then { + span_lint( + cx, + CAST_PTR_ALIGNMENT, + expr.span, + &format!( + "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", + cast_from, + cast_to, + from_layout.align.abi.bytes(), + to_layout.align.abi.bytes(), + ), + ); + } + } +} + +fn lint_fn_to_numeric_cast( + cx: &LateContext<'_, '_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) { + // We only want to check casts to `ty::Uint` or `ty::Int` + match cast_to.kind { + ty::Uint(_) | ty::Int(..) => { /* continue on */ }, + _ => return, + } + match cast_from.kind { + ty::FnDef(..) | ty::FnPtr(_) => { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); + + let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); + if to_nbits < cx.tcx.data_layout.pointer_size.bits() { + span_lint_and_sugg( + cx, + FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + expr.span, + &format!( + "casting function pointer `{}` to `{}`, which truncates the value", + from_snippet, cast_to + ), + "try", + format!("{} as usize", from_snippet), + applicability, + ); + } else if cast_to.kind != ty::Uint(UintTy::Usize) { + span_lint_and_sugg( + cx, + FN_TO_NUMERIC_CAST, + expr.span, + &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + "try", + format!("{} as usize", from_snippet), + applicability, + ); + } + }, + _ => {}, + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for types used in structs, parameters and `let` + /// declarations above a certain complexity threshold. + /// + /// **Why is this bad?** Too complex types make the code less readable. Consider + /// using a `type` definition to simplify them. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::rc::Rc; + /// struct Foo { + /// inner: Rc>>>, + /// } + /// ``` + pub TYPE_COMPLEXITY, + complexity, + "usage of very complex types that might be better factored into `type` definitions" +} + +pub struct TypeComplexity { + threshold: u64, +} + +impl TypeComplexity { + #[must_use] + pub fn new(threshold: u64) -> Self { + Self { threshold } + } +} + +impl_lint_pass!(TypeComplexity => [TYPE_COMPLEXITY]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexity { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + _: &'tcx Body<'_>, + _: Span, + _: HirId, + ) { + self.check_fndecl(cx, decl); + } + + fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField<'_>) { + // enum variants are also struct fields now + self.check_type(cx, &field.ty); + } + + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + match item.kind { + ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_type(cx, ty), + // functions, enums, structs, impls and traits are covered + _ => (), + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) { + match item.kind { + TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty), + TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl), + // methods with default impl are covered by check_fn + _ => (), + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) { + match item.kind { + ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty), + // methods are covered by check_fn + _ => (), + } + } + + fn check_local(&mut self, cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>) { + if let Some(ref ty) = local.ty { + self.check_type(cx, ty); + } + } +} + +impl<'a, 'tcx> TypeComplexity { + fn check_fndecl(&self, cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl<'_>) { + for arg in decl.inputs { + self.check_type(cx, arg); + } + if let FnRetTy::Return(ref ty) = decl.output { + self.check_type(cx, ty); + } + } + + fn check_type(&self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) { + if ty.span.from_expansion() { + return; + } + let score = { + let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 }; + visitor.visit_ty(ty); + visitor.score + }; + + if score > self.threshold { + span_lint( + cx, + TYPE_COMPLEXITY, + ty.span, + "very complex type used. Consider factoring parts into `type` definitions", + ); + } + } +} + +/// Walks a type and assigns a complexity score to it. +struct TypeComplexityVisitor { + /// total complexity score of the type + score: u64, + /// current nesting level + nest: u64, +} + +impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + let (add_score, sub_nest) = match ty.kind { + // _, &x and *x have only small overhead; don't mess with nesting level + TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0), + + // the "normal" components of a type: named types, arrays/tuples + TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), + + // function types bring a lot of overhead + TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), + + TyKind::TraitObject(ref param_bounds, _) => { + let has_lifetime_parameters = param_bounds.iter().any(|bound| { + bound.bound_generic_params.iter().any(|gen| match gen.kind { + GenericParamKind::Lifetime { .. } => true, + _ => false, + }) + }); + if has_lifetime_parameters { + // complex trait bounds like A<'a, 'b> + (50 * self.nest, 1) + } else { + // simple trait bounds like A + B + (20 * self.nest, 0) + } + }, + + _ => (0, 0), + }; + self.score += add_score; + self.nest += sub_nest; + walk_ty(self, ty); + self.nest -= sub_nest; + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for expressions where a character literal is cast + /// to `u8` and suggests using a byte literal instead. + /// + /// **Why is this bad?** In general, casting values to smaller types is + /// error-prone and should be avoided where possible. In the particular case of + /// converting a character literal to u8, it is easy to avoid by just using a + /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter + /// than `'a' as u8`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// 'x' as u8 + /// ``` + /// + /// A better version, using the byte literal: + /// + /// ```rust,ignore + /// b'x' + /// ``` + pub CHAR_LIT_AS_U8, + complexity, + "casting a character literal to `u8` truncates" +} + +declare_lint_pass!(CharLitAsU8 => [CHAR_LIT_AS_U8]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CharLitAsU8 { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if !expr.span.from_expansion(); + if let ExprKind::Cast(e, _) = &expr.kind; + if let ExprKind::Lit(l) = &e.kind; + if let LitKind::Char(c) = l.node; + if ty::Uint(UintTy::U8) == cx.tables.expr_ty(expr).kind; + then { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); + + span_lint_and_then( + cx, + CHAR_LIT_AS_U8, + expr.span, + "casting a character literal to `u8` truncates", + |diag| { + diag.note("`char` is four bytes wide, but `u8` is a single byte"); + + if c.is_ascii() { + diag.span_suggestion( + expr.span, + "use a byte literal instead", + format!("b{}", snippet), + applicability, + ); + } + }); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons where one side of the relation is + /// either the minimum or maximum value for its type and warns if it involves a + /// case that is always true or always false. Only integer and boolean types are + /// checked. + /// + /// **Why is this bad?** An expression like `min <= x` may misleadingly imply + /// that it is possible for `x` to be less than the minimum. Expressions like + /// `max < x` are probably mistakes. + /// + /// **Known problems:** For `usize` the size of the current compile target will + /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such + /// a comparison to detect target pointer width will trigger this lint. One can + /// use `mem::sizeof` and compare its value or conditional compilation + /// attributes + /// like `#[cfg(target_pointer_width = "64")] ..` instead. + /// + /// **Example:** + /// + /// ```rust + /// let vec: Vec = Vec::new(); + /// if vec.len() <= 0 {} + /// if 100 > i32::MAX {} + /// ``` + pub ABSURD_EXTREME_COMPARISONS, + correctness, + "a comparison with a maximum or minimum value that is always true or false" +} + +declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]); + +enum ExtremeType { + Minimum, + Maximum, +} + +struct ExtremeExpr<'a> { + which: ExtremeType, + expr: &'a Expr<'a>, +} + +enum AbsurdComparisonResult { + AlwaysFalse, + AlwaysTrue, + InequalityImpossible, +} + +fn is_cast_between_fixed_and_target<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + let precast_ty = cx.tables.expr_ty(cast_exp); + let cast_ty = cx.tables.expr_ty(expr); + + return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty); + } + + false +} + +fn detect_absurd_comparison<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + op: BinOpKind, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, +) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> { + use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; + use crate::types::ExtremeType::{Maximum, Minimum}; + use crate::utils::comparisons::{normalize_comparison, Rel}; + + // absurd comparison only makes sense on primitive types + // primitive types don't implement comparison operators with each other + if cx.tables.expr_ty(lhs) != cx.tables.expr_ty(rhs) { + return None; + } + + // comparisons between fix sized types and target sized types are considered unanalyzable + if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) { + return None; + } + + let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?; + + let lx = detect_extreme_expr(cx, normalized_lhs); + let rx = detect_extreme_expr(cx, normalized_rhs); + + Some(match rel { + Rel::Lt => { + match (lx, rx) { + (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x + (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min + _ => return None, + } + }, + Rel::Le => { + match (lx, rx) { + (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x + (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x + (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min + (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max + _ => return None, + } + }, + Rel::Ne | Rel::Eq => return None, + }) +} + +fn detect_extreme_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option> { + use crate::types::ExtremeType::{Maximum, Minimum}; + + let ty = cx.tables.expr_ty(expr); + + let cv = constant(cx, cx.tables, expr)?.0; + + let which = match (&ty.kind, cv) { + (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum, + (&ty::Int(ity), Constant::Int(i)) + if i == unsext(cx.tcx, i128::min_value() >> (128 - int_bits(cx.tcx, ity)), ity) => + { + Minimum + }, + + (&ty::Bool, Constant::Bool(true)) => Maximum, + (&ty::Int(ity), Constant::Int(i)) + if i == unsext(cx.tcx, i128::max_value() >> (128 - int_bits(cx.tcx, ity)), ity) => + { + Maximum + }, + (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::max_value(), uty) == i => Maximum, + + _ => return None, + }; + Some(ExtremeExpr { which, expr }) +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AbsurdExtremeComparisons { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; + use crate::types::ExtremeType::{Maximum, Minimum}; + + if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) { + if !expr.span.from_expansion() { + let msg = "this comparison involving the minimum or maximum element for this \ + type contains a case that is always true or always false"; + + let conclusion = match result { + AlwaysFalse => "this comparison is always false".to_owned(), + AlwaysTrue => "this comparison is always true".to_owned(), + InequalityImpossible => format!( + "the case where the two sides are not equal never occurs, consider using `{} == {}` \ + instead", + snippet(cx, lhs.span, "lhs"), + snippet(cx, rhs.span, "rhs") + ), + }; + + let help = format!( + "because `{}` is the {} value for this type, {}", + snippet(cx, culprit.expr.span, "x"), + match culprit.which { + Minimum => "minimum", + Maximum => "maximum", + }, + conclusion + ); + + span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); + } + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons where the relation is always either + /// true or false, but where one side has been upcast so that the comparison is + /// necessary. Only integer types are checked. + /// + /// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300` + /// will mistakenly imply that it is possible for `x` to be outside the range of + /// `u8`. + /// + /// **Known problems:** + /// https://github.com/rust-lang/rust-clippy/issues/886 + /// + /// **Example:** + /// ```rust + /// let x: u8 = 1; + /// (x as u32) > 300; + /// ``` + pub INVALID_UPCAST_COMPARISONS, + pedantic, + "a comparison involving an upcast which is always true or false" +} + +declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); + +#[derive(Copy, Clone, Debug, Eq)] +enum FullInt { + S(i128), + U(u128), +} + +impl FullInt { + #[allow(clippy::cast_sign_loss)] + #[must_use] + fn cmp_s_u(s: i128, u: u128) -> Ordering { + if s < 0 { + Ordering::Less + } else if u > (i128::max_value() as u128) { + Ordering::Greater + } else { + (s as u128).cmp(&u) + } + } +} + +impl PartialEq for FullInt { + #[must_use] + fn eq(&self, other: &Self) -> bool { + self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal + } +} + +impl PartialOrd for FullInt { + #[must_use] + fn partial_cmp(&self, other: &Self) -> Option { + Some(match (self, other) { + (&Self::S(s), &Self::S(o)) => s.cmp(&o), + (&Self::U(s), &Self::U(o)) => s.cmp(&o), + (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o), + (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(), + }) + } +} +impl Ord for FullInt { + #[must_use] + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other) + .expect("`partial_cmp` for FullInt can never return `None`") + } +} + +fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { + if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + let pre_cast_ty = cx.tables.expr_ty(cast_exp); + let cast_ty = cx.tables.expr_ty(expr); + // if it's a cast from i32 to u32 wrapping will invalidate all these checks + if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) { + return None; + } + match pre_cast_ty.kind { + ty::Int(int_ty) => Some(match int_ty { + IntTy::I8 => ( + FullInt::S(i128::from(i8::min_value())), + FullInt::S(i128::from(i8::max_value())), + ), + IntTy::I16 => ( + FullInt::S(i128::from(i16::min_value())), + FullInt::S(i128::from(i16::max_value())), + ), + IntTy::I32 => ( + FullInt::S(i128::from(i32::min_value())), + FullInt::S(i128::from(i32::max_value())), + ), + IntTy::I64 => ( + FullInt::S(i128::from(i64::min_value())), + FullInt::S(i128::from(i64::max_value())), + ), + IntTy::I128 => (FullInt::S(i128::min_value()), FullInt::S(i128::max_value())), + IntTy::Isize => ( + FullInt::S(isize::min_value() as i128), + FullInt::S(isize::max_value() as i128), + ), + }), + ty::Uint(uint_ty) => Some(match uint_ty { + UintTy::U8 => ( + FullInt::U(u128::from(u8::min_value())), + FullInt::U(u128::from(u8::max_value())), + ), + UintTy::U16 => ( + FullInt::U(u128::from(u16::min_value())), + FullInt::U(u128::from(u16::max_value())), + ), + UintTy::U32 => ( + FullInt::U(u128::from(u32::min_value())), + FullInt::U(u128::from(u32::max_value())), + ), + UintTy::U64 => ( + FullInt::U(u128::from(u64::min_value())), + FullInt::U(u128::from(u64::max_value())), + ), + UintTy::U128 => (FullInt::U(u128::min_value()), FullInt::U(u128::max_value())), + UintTy::Usize => ( + FullInt::U(usize::min_value() as u128), + FullInt::U(usize::max_value() as u128), + ), + }), + _ => None, + } + } else { + None + } +} + +fn node_as_const_fullint<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option { + let val = constant(cx, cx.tables, expr)?.0; + if let Constant::Int(const_int) = val { + match cx.tables.expr_ty(expr).kind { + ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), + ty::Uint(_) => Some(FullInt::U(const_int)), + _ => None, + } + } else { + None + } +} + +fn err_upcast_comparison(cx: &LateContext<'_, '_>, span: Span, expr: &Expr<'_>, always: bool) { + if let ExprKind::Cast(ref cast_val, _) = expr.kind { + span_lint( + cx, + INVALID_UPCAST_COMPARISONS, + span, + &format!( + "because of the numeric bounds on `{}` prior to casting, this expression is always {}", + snippet(cx, cast_val.span, "the expression"), + if always { "true" } else { "false" }, + ), + ); + } +} + +fn upcast_comparison_bounds_err<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + span: Span, + rel: comparisons::Rel, + lhs_bounds: Option<(FullInt, FullInt)>, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, + invert: bool, +) { + use crate::utils::comparisons::Rel; + + if let Some((lb, ub)) = lhs_bounds { + if let Some(norm_rhs_val) = node_as_const_fullint(cx, 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) + } + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidUpcastComparisons { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); + let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { + val + } else { + return; + }; + + let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); + let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); + + upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); + upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for public `impl` or `fn` missing generalization + /// over different hashers and implicitly defaulting to the default hashing + /// algorithm (`SipHash`). + /// + /// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be + /// used with them. + /// + /// **Known problems:** Suggestions for replacing constructors can contain + /// false-positives. Also applying suggestions can require modification of other + /// pieces of code, possibly including external crates. + /// + /// **Example:** + /// ```rust + /// # use std::collections::HashMap; + /// # use std::hash::{Hash, BuildHasher}; + /// # trait Serialize {}; + /// impl Serialize for HashMap { } + /// + /// pub fn foo(map: &mut HashMap) { } + /// ``` + /// could be rewritten as + /// ```rust + /// # use std::collections::HashMap; + /// # use std::hash::{Hash, BuildHasher}; + /// # trait Serialize {}; + /// impl Serialize for HashMap { } + /// + /// pub fn foo(map: &mut HashMap) { } + /// ``` + pub IMPLICIT_HASHER, + pedantic, + "missing generalization over different hashers" +} + +declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher { + #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + use rustc_span::BytePos; + + fn suggestion<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + diag: &mut DiagnosticBuilder<'_>, + generics_span: Span, + generics_suggestion_span: Span, + target: &ImplicitHasherType<'_>, + vis: ImplicitHasherConstructorVisitor<'_, '_, '_>, + ) { + let generics_snip = snippet(cx, generics_span, ""); + // trim `<` `>` + let generics_snip = if generics_snip.is_empty() { + "" + } else { + &generics_snip[1..generics_snip.len() - 1] + }; + + multispan_sugg( + diag, + "consider adding a type parameter", + vec![ + ( + generics_suggestion_span, + format!( + "<{}{}S: ::std::hash::BuildHasher{}>", + generics_snip, + if generics_snip.is_empty() { "" } else { ", " }, + if vis.suggestions.is_empty() { + "" + } else { + // request users to add `Default` bound so that generic constructors can be used + " + Default" + }, + ), + ), + ( + target.span(), + format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + ), + ], + ); + + if !vis.suggestions.is_empty() { + multispan_sugg(diag, "...and use generic constructor", vis.suggestions); + } + } + + if !cx.access_levels.is_exported(item.hir_id) { + return; + } + + match item.kind { + ItemKind::Impl { + ref generics, + self_ty: ref ty, + ref items, + .. + } => { + let mut vis = ImplicitHasherTypeVisitor::new(cx); + vis.visit_ty(ty); + + for target in &vis.found { + if differing_macro_contexts(item.span, target.span()) { + return; + } + + let generics_suggestion_span = generics.span.substitute_dummy({ + let pos = snippet_opt(cx, item.span.until(target.span())) + .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4))); + if let Some(pos) = pos { + Span::new(pos, pos, item.span.data().ctxt) + } else { + return; + } + }); + + let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); + for item in items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) { + ctr_vis.visit_impl_item(item); + } + + span_lint_and_then( + cx, + IMPLICIT_HASHER, + target.span(), + &format!( + "impl for `{}` should be generalized over different hashers", + target.type_name() + ), + move |diag| { + suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis); + }, + ); + } + }, + ItemKind::Fn(ref sig, ref generics, body_id) => { + let body = cx.tcx.hir().body(body_id); + + for ty in sig.decl.inputs { + let mut vis = ImplicitHasherTypeVisitor::new(cx); + vis.visit_ty(ty); + + for target in &vis.found { + if in_external_macro(cx.sess(), generics.span) { + continue; + } + let generics_suggestion_span = generics.span.substitute_dummy({ + let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span)) + .and_then(|snip| { + let i = snip.find("fn")?; + Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) + }) + .expect("failed to create span for type parameters"); + Span::new(pos, pos, item.span.data().ctxt) + }); + + let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); + ctr_vis.visit_body(body); + + span_lint_and_then( + cx, + IMPLICIT_HASHER, + target.span(), + &format!( + "parameter of type `{}` should be generalized over different hashers", + target.type_name() + ), + move |diag| { + suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis); + }, + ); + } + } + }, + _ => {}, + } + } +} + +enum ImplicitHasherType<'tcx> { + HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>), + HashSet(Span, Ty<'tcx>, Cow<'static, str>), +} + +impl<'tcx> ImplicitHasherType<'tcx> { + /// Checks that `ty` is a target type without a `BuildHasher`. + fn new<'a>(cx: &LateContext<'a, 'tcx>, hir_ty: &hir::Ty<'_>) -> Option { + if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind { + let params: Vec<_> = path + .segments + .last() + .as_ref()? + .args + .as_ref()? + .args + .iter() + .filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + .collect(); + let params_len = params.len(); + + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + + if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) && params_len == 2 { + Some(ImplicitHasherType::HashMap( + hir_ty.span, + ty, + snippet(cx, params[0].span, "K"), + snippet(cx, params[1].span, "V"), + )) + } else if is_type_diagnostic_item(cx, ty, sym!(hashset_type)) && params_len == 1 { + Some(ImplicitHasherType::HashSet( + hir_ty.span, + ty, + snippet(cx, params[0].span, "T"), + )) + } else { + None + } + } else { + None + } + } + + fn type_name(&self) -> &'static str { + match *self { + ImplicitHasherType::HashMap(..) => "HashMap", + ImplicitHasherType::HashSet(..) => "HashSet", + } + } + + fn type_arguments(&self) -> String { + match *self { + ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), + ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), + } + } + + fn ty(&self) -> Ty<'tcx> { + match *self { + ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty, + } + } + + fn span(&self) -> Span { + match *self { + ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span, + } + } +} + +struct ImplicitHasherTypeVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + found: Vec>, +} + +impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + Self { cx, found: vec![] } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { + if let Some(target) = ImplicitHasherType::new(self.cx, t) { + self.found.push(target); + } + + walk_ty(self, t); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Looks for default-hasher-dependent constructors like `HashMap::new`. +struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + body: &'a TypeckTables<'tcx>, + target: &'b ImplicitHasherType<'tcx>, + suggestions: BTreeMap, +} + +impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self { + Self { + cx, + body: cx.tables, + target, + suggestions: BTreeMap::new(), + } + } +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + type Map = Map<'tcx>; + + fn visit_body(&mut self, body: &'tcx Body<'_>) { + let prev_body = self.body; + self.body = self.cx.tcx.body_tables(body.id()); + walk_body(self, body); + self.body = prev_body; + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref fun, ref args) = e.kind; + if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind; + if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; + then { + if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) { + return; + } + + if match_path(ty_path, &paths::HASHMAP) { + if method.ident.name == sym!(new) { + self.suggestions + .insert(e.span, "HashMap::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashMap::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } else if match_path(ty_path, &paths::HASHSET) { + if method.ident.name == sym!(new) { + self.suggestions + .insert(e.span, "HashSet::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashSet::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } + } + } + + walk_expr(self, e); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code. + /// + /// **Why is this bad?** It’s basically guaranteed to be undefined behaviour. + /// `UnsafeCell` is the only way to obtain aliasable data that is considered + /// mutable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// fn x(r: &i32) { + /// unsafe { + /// *(r as *const _ as *mut _) += 1; + /// } + /// } + /// ``` + /// + /// Instead consider using interior mutability types. + /// + /// ```rust + /// use std::cell::UnsafeCell; + /// + /// fn x(r: &UnsafeCell) { + /// unsafe { + /// *r.get() += 1; + /// } + /// } + /// ``` + pub CAST_REF_TO_MUT, + correctness, + "a cast of reference to a mutable pointer" +} + +declare_lint_pass!(RefToMut => [CAST_REF_TO_MUT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RefToMut { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Unary(UnOp::UnDeref, e) = &expr.kind; + if let ExprKind::Cast(e, t) = &e.kind; + if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind; + if let ExprKind::Cast(e, t) = &e.kind; + if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind; + if let ty::Ref(..) = cx.tables.node_type(e.hir_id).kind; + then { + span_lint( + cx, + CAST_REF_TO_MUT, + expr.span, + "casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs new file mode 100644 index 0000000000000..d073c197656c6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unicode.rs @@ -0,0 +1,131 @@ +use crate::utils::{is_allowed, snippet, span_lint_and_sugg}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use unicode_normalization::UnicodeNormalization; + +declare_clippy_lint! { + /// **What it does:** Checks for the Unicode zero-width space in the code. + /// + /// **Why is this bad?** Having an invisible character in the code makes for all + /// sorts of April fools, but otherwise is very much frowned upon. + /// + /// **Known problems:** None. + /// + /// **Example:** You don't see it, but there may be a zero-width space + /// somewhere in this text. + pub ZERO_WIDTH_SPACE, + correctness, + "using a zero-width space in a string literal, which is confusing" +} + +declare_clippy_lint! { + /// **What it does:** Checks for non-ASCII characters in string literals. + /// + /// **Why is this bad?** Yeah, we know, the 90's called and wanted their charset + /// back. Even so, there still are editors and other programs out there that + /// don't work well with Unicode. So if the code is meant to be used + /// internationally, on multiple operating systems, or has other portability + /// requirements, activating this lint could be useful. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = String::from("€"); + /// ``` + /// Could be written as: + /// ```rust + /// let x = String::from("\u{20ac}"); + /// ``` + pub NON_ASCII_LITERAL, + pedantic, + "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape" +} + +declare_clippy_lint! { + /// **What it does:** Checks for string literals that contain Unicode in a form + /// that is not equal to its + /// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms). + /// + /// **Why is this bad?** If such a string is compared to another, the results + /// may be surprising. + /// + /// **Known problems** None. + /// + /// **Example:** You may not see it, but "à"" and "à"" aren't the same string. The + /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`. + pub UNICODE_NOT_NFC, + pedantic, + "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" +} + +declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); + +impl LateLintPass<'_, '_> for Unicode { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) { + if let ExprKind::Lit(ref lit) = expr.kind { + if let LitKind::Str(_, _) = lit.node { + check_str(cx, lit.span, expr.hir_id) + } + } + } +} + +fn escape>(s: T) -> String { + let mut result = String::new(); + for c in s { + if c as u32 > 0x7F { + for d in c.escape_unicode() { + result.push(d) + } + } else { + result.push(c); + } + } + result +} + +fn check_str(cx: &LateContext<'_, '_>, span: Span, id: HirId) { + let string = snippet(cx, span, ""); + if string.contains('\u{200B}') { + span_lint_and_sugg( + cx, + ZERO_WIDTH_SPACE, + span, + "zero-width space detected", + "consider replacing the string with", + string.replace("\u{200B}", "\\u{200B}"), + Applicability::MachineApplicable, + ); + } + if string.chars().any(|c| c as u32 > 0x7F) { + span_lint_and_sugg( + cx, + NON_ASCII_LITERAL, + span, + "literal non-ASCII character detected", + "consider replacing the string with", + if is_allowed(cx, UNICODE_NOT_NFC, id) { + escape(string.chars()) + } else { + escape(string.nfc()) + }, + Applicability::MachineApplicable, + ); + } + if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) { + span_lint_and_sugg( + cx, + UNICODE_NOT_NFC, + span, + "non-NFC Unicode sequence detected", + "consider replacing the string with", + string.nfc().collect::(), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs new file mode 100644 index 0000000000000..4e077b95b5c68 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs @@ -0,0 +1,135 @@ +use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons with an address of a function item. + /// + /// **Why is this bad?** Function item address is not guaranteed to be unique and could vary + /// between different code generation units. Furthermore different function items could have + /// the same address after being merged together. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// type F = fn(); + /// fn a() {} + /// let f: F = a; + /// if f == a { + /// // ... + /// } + /// ``` + pub FN_ADDRESS_COMPARISONS, + correctness, + "comparison with an address of a function item" +} + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons with an address of a trait vtable. + /// + /// **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which + /// are not guaranteed to be unique and could vary between different code generation units. + /// Furthermore vtables for different types could have the same address after being merged + /// together. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// let a: Rc = ... + /// let b: Rc = ... + /// if Rc::ptr_eq(&a, &b) { + /// ... + /// } + /// ``` + pub VTABLE_ADDRESS_COMPARISONS, + correctness, + "comparison with an address of a trait vtable" +} + +declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COMPARISONS]); + +impl LateLintPass<'_, '_> for UnnamedAddress { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + fn is_comparison(binop: BinOpKind) -> bool { + match binop { + BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true, + _ => false, + } + } + + fn is_trait_ptr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + match cx.tables.expr_ty_adjusted(expr).kind { + ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(), + _ => false, + } + } + + fn is_fn_def(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + if let ty::FnDef(..) = cx.tables.expr_ty(expr).kind { + true + } else { + false + } + } + + if_chain! { + if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if is_comparison(binop.node); + if is_trait_ptr(cx, left) && is_trait_ptr(cx, right); + then { + span_lint_and_help( + cx, + VTABLE_ADDRESS_COMPARISONS, + expr.span, + "comparing trait object pointers compares a non-unique vtable address", + None, + "consider extracting and comparing data pointers only", + ); + } + } + + if_chain! { + if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind; + if let ExprKind::Path(ref func_qpath) = func.kind; + if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::PTR_EQ) || + match_def_path(cx, def_id, &paths::RC_PTR_EQ) || + match_def_path(cx, def_id, &paths::ARC_PTR_EQ); + let ty_param = cx.tables.node_substs(func.hir_id).type_at(0); + if ty_param.is_trait(); + then { + span_lint_and_help( + cx, + VTABLE_ADDRESS_COMPARISONS, + expr.span, + "comparing trait object pointers compares a non-unique vtable address", + None, + "consider extracting and comparing data pointers only", + ); + } + } + + if_chain! { + if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if is_comparison(binop.node); + if cx.tables.expr_ty_adjusted(left).is_fn_ptr() && + cx.tables.expr_ty_adjusted(right).is_fn_ptr(); + if is_fn_def(cx, left) || is_fn_def(cx, right); + then { + span_lint( + cx, + FN_ADDRESS_COMPARISONS, + expr.span, + "comparing with a non-unique address of a function item", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs new file mode 100644 index 0000000000000..735800e7e7416 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs @@ -0,0 +1,78 @@ +use crate::utils::span_lint; +use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::{Ident, SymbolStr}; + +declare_clippy_lint! { + /// **What it does:** Checks for imports that remove "unsafe" from an item's + /// name. + /// + /// **Why is this bad?** Renaming makes it less clear which traits and + /// structures are unsafe. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// use std::cell::{UnsafeCell as TotallySafeCell}; + /// + /// extern crate crossbeam; + /// use crossbeam::{spawn_unsafe as spawn}; + /// ``` + pub UNSAFE_REMOVED_FROM_NAME, + style, + "`unsafe` removed from API names on import" +} + +declare_lint_pass!(UnsafeNameRemoval => [UNSAFE_REMOVED_FROM_NAME]); + +impl EarlyLintPass for UnsafeNameRemoval { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Use(ref use_tree) = item.kind { + check_use_tree(use_tree, cx, item.span); + } + } +} + +fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { + match use_tree.kind { + UseTreeKind::Simple(Some(new_name), ..) => { + let old_name = use_tree + .prefix + .segments + .last() + .expect("use paths cannot be empty") + .ident; + unsafe_to_safe_check(old_name, new_name, cx, span); + }, + UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {}, + UseTreeKind::Nested(ref nested_use_tree) => { + for &(ref use_tree, _) in nested_use_tree { + check_use_tree(use_tree, cx, span); + } + }, + } +} + +fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, span: Span) { + let old_str = old_name.name.as_str(); + let new_str = new_name.name.as_str(); + if contains_unsafe(&old_str) && !contains_unsafe(&new_str) { + span_lint( + cx, + UNSAFE_REMOVED_FROM_NAME, + span, + &format!( + "removed `unsafe` from the name of `{}` in use as `{}`", + old_str, new_str + ), + ); + } +} + +#[must_use] +fn contains_unsafe(name: &SymbolStr) -> bool { + name.contains("Unsafe") || name.contains("unsafe") +} diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs new file mode 100644 index 0000000000000..b85134e3d7a9a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -0,0 +1,91 @@ +use crate::utils::{is_try, match_qpath, match_trait_method, paths, span_lint}; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for unused written/read amount. + /// + /// **Why is this bad?** `io::Write::write(_vectored)` and + /// `io::Read::read(_vectored)` are not guaranteed to + /// process the entire buffer. They return how many bytes were processed, which + /// might be smaller + /// than a given buffer's length. If you don't need to deal with + /// partial-write/read, use + /// `write_all`/`read_exact` instead. + /// + /// **Known problems:** Detects only common patterns. + /// + /// **Example:** + /// ```rust,ignore + /// use std::io; + /// fn foo(w: &mut W) -> io::Result<()> { + /// // must be `w.write_all(b"foo")?;` + /// w.write(b"foo")?; + /// Ok(()) + /// } + /// ``` + pub UNUSED_IO_AMOUNT, + correctness, + "unused written/read amount" +} + +declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedIoAmount { + fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) { + let expr = match s.kind { + hir::StmtKind::Semi(ref expr) | hir::StmtKind::Expr(ref expr) => &**expr, + _ => return, + }; + + match expr.kind { + hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => { + if let hir::ExprKind::Call(ref func, ref args) = res.kind { + if let hir::ExprKind::Path(ref path) = func.kind { + if match_qpath(path, &paths::TRY_INTO_RESULT) && args.len() == 1 { + check_method_call(cx, &args[0], expr); + } + } + } else { + check_method_call(cx, res, expr); + } + }, + + hir::ExprKind::MethodCall(ref path, _, ref args) => match &*path.ident.as_str() { + "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { + check_method_call(cx, &args[0], expr); + }, + _ => (), + }, + + _ => (), + } + } +} + +fn check_method_call(cx: &LateContext<'_, '_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { + if let hir::ExprKind::MethodCall(ref path, _, _) = call.kind { + let symbol = &*path.ident.as_str(); + let read_trait = match_trait_method(cx, call, &paths::IO_READ); + let write_trait = match_trait_method(cx, call, &paths::IO_WRITE); + + match (read_trait, write_trait, symbol) { + (true, _, "read") => span_lint( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "read amount is not handled. Use `Read::read_exact` instead", + ), + (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"), + (_, true, "write") => span_lint( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "written amount is not handled. Use `Write::write_all` instead", + ), + (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"), + _ => (), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs new file mode 100644 index 0000000000000..3d5e2f9fd2155 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -0,0 +1,105 @@ +use if_chain::if_chain; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; +use rustc_hir::{HirId, ImplItem, ImplItemKind, ItemKind, Path}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::span_lint_and_help; + +declare_clippy_lint! { + /// **What it does:** Checks methods that contain a `self` argument but don't use it + /// + /// **Why is this bad?** It may be clearer to define the method as an associated function instead + /// of an instance method if it doesn't require `self`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// struct A; + /// impl A { + /// fn method(&self) {} + /// } + /// ``` + /// + /// Could be written: + /// + /// ```rust,ignore + /// struct A; + /// impl A { + /// fn method() {} + /// } + /// ``` + pub UNUSED_SELF, + pedantic, + "methods that contain a `self` argument but don't use it" +} + +declare_lint_pass!(UnusedSelf => [UNUSED_SELF]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedSelf { + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &ImplItem<'_>) { + if impl_item.span.from_expansion() { + return; + } + let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id); + let parent_item = cx.tcx.hir().expect_item(parent); + let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let assoc_item = cx.tcx.associated_item(def_id); + if_chain! { + if let ItemKind::Impl { of_trait: None, .. } = parent_item.kind; + if assoc_item.fn_has_self_parameter; + if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; + let body = cx.tcx.hir().body(*body_id); + if !body.params.is_empty(); + then { + let self_param = &body.params[0]; + let self_hir_id = self_param.pat.hir_id; + let mut visitor = UnusedSelfVisitor { + cx, + uses_self: false, + self_hir_id: &self_hir_id, + }; + visitor.visit_body(body); + if !visitor.uses_self { + span_lint_and_help( + cx, + UNUSED_SELF, + self_param.span, + "unused `self` argument", + None, + "consider refactoring to a associated function", + ); + return; + } + } + } + } +} + +struct UnusedSelfVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + uses_self: bool, + self_hir_id: &'a HirId, +} + +impl<'a, 'tcx> Visitor<'tcx> for UnusedSelfVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + if self.uses_self { + // This function already uses `self` + return; + } + if let Res::Local(hir_id) = &path.res { + self.uses_self = self.self_hir_id == hir_id + } + walk_path(self, path); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs new file mode 100644 index 0000000000000..8b971e7064b52 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -0,0 +1,233 @@ +use crate::utils::{ + differing_macro_contexts, higher::if_block, is_type_diagnostic_item, span_lint_and_then, + usage::is_potentially_mutated, +}; +use if_chain::if_chain; +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail. + /// + /// **Why is this bad?** Using `if let` or `match` is more idiomatic. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// # let option = Some(0); + /// # fn do_something_with(_x: usize) {} + /// if option.is_some() { + /// do_something_with(option.unwrap()) + /// } + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # let option = Some(0); + /// # fn do_something_with(_x: usize) {} + /// if let Some(value) = option { + /// do_something_with(value) + /// } + /// ``` + pub UNNECESSARY_UNWRAP, + complexity, + "checks for calls of `unwrap[_err]()` that cannot fail" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail. + /// + /// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used. + /// + /// **Known problems:** This lint only checks `if` conditions not assignments. + /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized. + /// + /// **Example:** + /// ```rust + /// # let option = Some(0); + /// # fn do_something_with(_x: usize) {} + /// if option.is_none() { + /// do_something_with(option.unwrap()) + /// } + /// ``` + /// + /// This code will always panic. The if condition should probably be inverted. + pub PANICKING_UNWRAP, + correctness, + "checks for calls of `unwrap[_err]()` that will always fail" +} + +/// Visitor that keeps track of which variables are unwrappable. +struct UnwrappableVariablesVisitor<'a, 'tcx> { + unwrappables: Vec>, + cx: &'a LateContext<'a, 'tcx>, +} +/// Contains information about whether a variable can be unwrapped. +#[derive(Copy, Clone, Debug)] +struct UnwrapInfo<'tcx> { + /// The variable that is checked + ident: &'tcx Path<'tcx>, + /// The check, like `x.is_ok()` + check: &'tcx Expr<'tcx>, + /// 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()`). + safe_to_unwrap: bool, +} + +/// Collects the information about unwrappable variables from an if condition +/// The `invert` argument tells us whether the condition is negated. +fn collect_unwrap_info<'a, 'tcx>( + cx: &'a LateContext<'a, 'tcx>, + expr: &'tcx Expr<'_>, + branch: &'tcx Expr<'_>, + invert: bool, +) -> Vec> { + fn is_relevant_option_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name) + } + + fn is_relevant_result_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name) + } + + if let ExprKind::Binary(op, left, right) = &expr.kind { + match (invert, op.node) { + (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => { + let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert); + unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert)); + return unwrap_info; + }, + _ => (), + } + } else if let ExprKind::Unary(UnOp::UnNot, expr) = &expr.kind { + return collect_unwrap_info(cx, expr, branch, !invert); + } else { + if_chain! { + if let ExprKind::MethodCall(method_name, _, args) = &expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind; + let ty = cx.tables.expr_ty(&args[0]); + let name = method_name.ident.as_str(); + if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name); + then { + assert!(args.len() == 1); + let unwrappable = match name.as_ref() { + "is_some" | "is_ok" => true, + "is_err" | "is_none" => false, + _ => unreachable!(), + }; + let safe_to_unwrap = unwrappable != invert; + return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }]; + } + } + } + Vec::new() +} + +impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { + fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) { + let prev_len = self.unwrappables.len(); + for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) { + if is_potentially_mutated(unwrap_info.ident, cond, self.cx) + || is_potentially_mutated(unwrap_info.ident, branch, self.cx) + { + // if the variable is mutated, we don't know whether it can be unwrapped: + continue; + } + self.unwrappables.push(unwrap_info); + } + walk_expr(self, branch); + self.unwrappables.truncate(prev_len); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // Shouldn't lint when `expr` is in macro. + if in_external_macro(self.cx.tcx.sess, expr.span) { + return; + } + if let Some((cond, then, els)) = if_block(&expr) { + walk_expr(self, cond); + self.visit_branch(cond, then, false); + if let Some(els) = els { + self.visit_branch(cond, els, true); + } + } else { + // find `unwrap[_err]()` calls: + if_chain! { + if let ExprKind::MethodCall(ref method_name, _, ref args) = expr.kind; + if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind; + if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name); + let call_to_unwrap = method_name.ident.name == sym!(unwrap); + if let Some(unwrappable) = self.unwrappables.iter() + .find(|u| u.ident.res == path.res); + // Span contexts should not differ with the conditional branch + if !differing_macro_contexts(unwrappable.branch.span, expr.span); + if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span); + then { + if call_to_unwrap == unwrappable.safe_to_unwrap { + span_lint_and_then( + self.cx, + UNNECESSARY_UNWRAP, + expr.span, + &format!("You checked before that `{}()` cannot fail. \ + Instead of checking and unwrapping, it's better to use `if let` or `match`.", + method_name.ident.name), + |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, + ); + } else { + span_lint_and_then( + self.cx, + PANICKING_UNWRAP, + expr.span, + &format!("This call to `{}()` will always panic.", + method_name.ident.name), + |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, + ); + } + } + } + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unwrap { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + fn_id: HirId, + ) { + if span.from_expansion() { + return; + } + + let mut v = UnwrappableVariablesVisitor { + cx, + unwrappables: Vec::new(), + }; + + walk_fn(&mut v, kind, decl, body.id(), span, fn_id); + } +} diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs new file mode 100644 index 0000000000000..f8e1aff33e773 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -0,0 +1,272 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_item, walk_path, walk_ty, NestedVisitorMap, Visitor}; +use rustc_hir::{ + def, FnDecl, FnRetTy, FnSig, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment, QPath, + TyKind, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_middle::ty::{DefIdTree, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_typeck::hir_ty_to_ty; + +use crate::utils::{differing_macro_contexts, span_lint_and_sugg}; + +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary repetition of structure name when a + /// replacement with `Self` is applicable. + /// + /// **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct + /// name + /// feels inconsistent. + /// + /// **Known problems:** + /// - False positive when using associated types (#2843) + /// - False positives in some situations when using generics (#3410) + /// + /// **Example:** + /// ```rust + /// struct Foo {} + /// impl Foo { + /// fn new() -> Foo { + /// Foo {} + /// } + /// } + /// ``` + /// could be + /// ```rust + /// struct Foo {} + /// impl Foo { + /// fn new() -> Self { + /// Self {} + /// } + /// } + /// ``` + pub USE_SELF, + nursery, + "Unnecessary structure name repetition whereas `Self` is applicable" +} + +declare_lint_pass!(UseSelf => [USE_SELF]); + +const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; + +fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: Option<&PathSegment<'_>>) { + let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG)); + + // Path segments only include actual path, no methods or fields. + let last_path_span = last_segment.ident.span; + + if differing_macro_contexts(path.span, last_path_span) { + return; + } + + // Only take path up to the end of last_path_span. + let span = path.span.with_hi(last_path_span.hi()); + + span_lint_and_sugg( + cx, + USE_SELF, + span, + "unnecessary structure name repetition", + "use the applicable keyword", + "Self".to_owned(), + Applicability::MachineApplicable, + ); +} + +// FIXME: always use this (more correct) visitor, not just in method signatures. +struct SemanticUseSelfVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + self_ty: Ty<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) { + if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind { + match path.res { + def::Res::SelfTy(..) => {}, + _ => { + if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { + span_use_self_lint(self.cx, path, None); + } + }, + } + } + + walk_ty(self, hir_ty) + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn check_trait_method_impl_decl<'a, 'tcx>( + cx: &'a LateContext<'a, 'tcx>, + impl_item: &ImplItem<'_>, + impl_decl: &'tcx FnDecl<'_>, + impl_trait_ref: ty::TraitRef<'tcx>, +) { + let trait_method = cx + .tcx + .associated_items(impl_trait_ref.def_id) + .find_by_name_and_kind(cx.tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id) + .expect("impl method matches a trait method"); + + let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id); + let trait_method_sig = cx.tcx.erase_late_bound_regions(&trait_method_sig); + + let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output { + Some(&**ty) + } else { + None + }; + + // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. + // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait. + // We use `impl_hir_ty` to see if the type was written as `Self`, + // `hir_ty_to_ty(...)` to check semantic types of paths, and + // `trait_ty` to determine which parts of the signature in the trait, mention + // the type being implemented verbatim (as opposed to `Self`). + for (impl_hir_ty, trait_ty) in impl_decl + .inputs + .iter() + .chain(output_hir_ty) + .zip(trait_method_sig.inputs_and_output) + { + // Check if the input/output type in the trait method specifies the implemented + // type verbatim, and only suggest `Self` if that isn't the case. + // This avoids suggestions to e.g. replace `Vec` with `Vec`, + // in an `impl Trait for u8`, when the trait always uses `Vec`. + // See also https://github.com/rust-lang/rust-clippy/issues/2894. + let self_ty = impl_trait_ref.self_ty(); + if !trait_ty.walk().any(|inner| inner == self_ty.into()) { + let mut visitor = SemanticUseSelfVisitor { cx, self_ty }; + + visitor.visit_ty(&impl_hir_ty); + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if in_external_macro(cx.sess(), item.span) { + return; + } + if_chain! { + if let ItemKind::Impl{ self_ty: ref item_type, items: refs, .. } = item.kind; + if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind; + then { + let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; + let should_check = if let Some(ref params) = *parameters { + !params.parenthesized && !params.args.iter().any(|arg| match arg { + GenericArg::Lifetime(_) => true, + _ => false, + }) + } else { + true + }; + + if should_check { + let visitor = &mut UseSelfVisitor { + item_path, + cx, + }; + let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id); + let impl_trait_ref = cx.tcx.impl_trait_ref(impl_def_id); + + if let Some(impl_trait_ref) = impl_trait_ref { + for impl_item_ref in refs { + let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); + if let ImplItemKind::Fn(FnSig{ decl: impl_decl, .. }, impl_body_id) + = &impl_item.kind { + check_trait_method_impl_decl(cx, impl_item, impl_decl, impl_trait_ref); + + let body = cx.tcx.hir().body(*impl_body_id); + visitor.visit_body(body); + } else { + visitor.visit_impl_item(impl_item); + } + } + } else { + for impl_item_ref in refs { + let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); + visitor.visit_impl_item(impl_item); + } + } + } + } + } + } +} + +struct UseSelfVisitor<'a, 'tcx> { + item_path: &'a Path<'a>, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + if !path.segments.iter().any(|p| p.ident.span.is_dummy()) { + if path.segments.len() >= 2 { + let last_but_one = &path.segments[path.segments.len() - 2]; + if last_but_one.ident.name != kw::SelfUpper { + let enum_def_id = match path.res { + Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id), + Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => { + let variant_def_id = self.cx.tcx.parent(ctor_def_id); + variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id)) + }, + _ => None, + }; + + if self.item_path.res.opt_def_id() == enum_def_id { + span_use_self_lint(self.cx, path, Some(last_but_one)); + } + } + } + + if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper { + if self.item_path.res == path.res { + span_use_self_lint(self.cx, path, None); + } else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), ctor_def_id) = path.res { + if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) { + span_use_self_lint(self.cx, path, None); + } + } + } + } + + walk_path(self, path); + } + + fn visit_item(&mut self, item: &'tcx Item<'_>) { + match item.kind { + ItemKind::Use(..) + | ItemKind::Static(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Impl { .. } + | ItemKind::Fn(..) => { + // Don't check statements that shadow `Self` or where `Self` can't be used + }, + _ => walk_item(self, item), + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs new file mode 100644 index 0000000000000..95921518986bc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -0,0 +1,130 @@ +use crate::utils::{ + match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, +}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for `Into`/`From`/`IntoIter` calls that useless converts + /// to the same type as caller. + /// + /// **Why is this bad?** Redundant code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// // format!() returns a `String` + /// let s: String = format!("hello").into(); + /// + /// // Good + /// let s: String = format!("hello"); + /// ``` + pub USELESS_CONVERSION, + complexity, + "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type" +} + +#[derive(Default)] +pub struct UselessConversion { + try_desugar_arm: Vec, +} + +impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + + if Some(&e.hir_id) == self.try_desugar_arm.last() { + return; + } + + match e.kind { + ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { + let e = match arms[0].body.kind { + ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, + _ => return, + }; + if let ExprKind::Call(_, ref args) = e.kind { + self.try_desugar_arm.push(args[0].hir_id); + } + }, + + ExprKind::MethodCall(ref name, .., ref args) => { + if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if same_tys(cx, a, b) { + let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); + + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + "consider removing `.into()`", + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if same_tys(cx, a, b) { + let sugg = snippet(cx, args[0].span, "").into_owned(); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + "consider removing `.into_iter()`", + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + }, + + ExprKind::Call(ref path, ref args) => { + if let ExprKind::Path(ref qpath) = path.kind { + if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() { + if match_def_path(cx, def_id, &paths::FROM_FROM) { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if same_tys(cx, a, b) { + let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); + let sugg_msg = + format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + &sugg_msg, + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + } + } + }, + + _ => {}, + } + } + + fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if Some(&e.hir_id) == self.try_desugar_arm.last() { + self.try_desugar_arm.pop(); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs new file mode 100644 index 0000000000000..4dcf6c105ec63 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/attrs.rs @@ -0,0 +1,128 @@ +use rustc_ast::ast; +use rustc_ast::expand::is_proc_macro_attr; +use rustc_errors::Applicability; +use rustc_session::Session; +use std::str::FromStr; + +/// Deprecation status of attributes known by Clippy. +#[allow(dead_code)] +pub enum DeprecationStatus { + /// Attribute is deprecated + Deprecated, + /// Attribute is deprecated and was replaced by the named attribute + Replaced(&'static str), + None, +} + +pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ + ("author", DeprecationStatus::None), + ("cognitive_complexity", DeprecationStatus::None), + ( + "cyclomatic_complexity", + DeprecationStatus::Replaced("cognitive_complexity"), + ), + ("dump", DeprecationStatus::None), +]; + +pub struct LimitStack { + stack: Vec, +} + +impl Drop for LimitStack { + fn drop(&mut self) { + assert_eq!(self.stack.len(), 1); + } +} + +impl LimitStack { + #[must_use] + pub fn new(limit: u64) -> Self { + Self { stack: vec![limit] } + } + 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: &[ast::Attribute], name: &'static str) { + let stack = &mut self.stack; + parse_attrs(sess, attrs, name, |val| stack.push(val)); + } + pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) { + let stack = &mut self.stack; + parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val))); + } +} + +pub fn get_attr<'a>( + sess: &'a Session, + attrs: &'a [ast::Attribute], + name: &'static str, +) -> impl Iterator { + attrs.iter().filter(move |attr| { + let attr = if let ast::AttrKind::Normal(ref attr) = attr.kind { + attr + } else { + return false; + }; + let attr_segments = &attr.path.segments; + if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { + if let Some(deprecation_status) = + BUILTIN_ATTRIBUTES + .iter() + .find_map(|(builtin_name, deprecation_status)| { + if *builtin_name == attr_segments[1].ident.to_string() { + Some(deprecation_status) + } else { + None + } + }) + { + let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); + match *deprecation_status { + DeprecationStatus::Deprecated => { + diag.emit(); + false + }, + DeprecationStatus::Replaced(new_name) => { + diag.span_suggestion( + attr_segments[1].ident.span, + "consider using", + new_name.to_string(), + Applicability::MachineApplicable, + ); + diag.emit(); + false + }, + DeprecationStatus::None => { + diag.cancel(); + attr_segments[1].ident.to_string() == name + }, + } + } else { + sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + false + } + } else { + false + } + }) +} + +fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) { + for attr in get_attr(sess, attrs, name) { + if let Some(ref value) = attr.value_str() { + if let Ok(value) = FromStr::from_str(&value.as_str()) { + f(value) + } else { + sess.span_err(attr.span, "not a number"); + } + } else { + sess.span_err(attr.span, "bad clippy attribute"); + } + } +} + +/// Return true if the attributes contain any of `proc_macro`, +/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise +pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(is_proc_macro_attr) +} diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs new file mode 100644 index 0000000000000..bbcf396eef7d6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -0,0 +1,761 @@ +//! A group of attributes that can be attached to Rust code in order +//! to generate a clippy lint detecting said code automatically. + +use crate::utils::{get_attr, higher}; +use rustc_ast::ast::{Attribute, LitFloatType, LitKind}; +use rustc_ast::walk_list; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_session::Session; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Generates clippy code that detects the offending pattern + /// + /// **Example:** + /// ```rust,ignore + /// // ./tests/ui/my_lint.rs + /// fn foo() { + /// // detect the following pattern + /// #[clippy::author] + /// if x == 42 { + /// // but ignore everything from here on + /// #![clippy::author = "ignore"] + /// } + /// () + /// } + /// ``` + /// + /// Running `TESTNAME=ui/my_lint cargo uitest` will produce + /// a `./tests/ui/new_lint.stdout` file with the generated code: + /// + /// ```rust,ignore + /// // ./tests/ui/new_lint.stdout + /// if_chain! { + /// if let ExprKind::If(ref cond, ref then, None) = item.kind, + /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, + /// if let ExprKind::Path(ref path) = left.kind, + /// if let ExprKind::Lit(ref lit) = right.kind, + /// if let LitKind::Int(42, _) = lit.node, + /// then { + /// // report your lint here + /// } + /// } + /// ``` + pub LINT_AUTHOR, + internal_warn, + "helper for writing lints" +} + +declare_lint_pass!(Author => [LINT_AUTHOR]); + +fn prelude() { + println!("if_chain! {{"); +} + +fn done() { + println!(" then {{"); + println!(" // report your lint here"); + println!(" }}"); + println!("}}"); +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Author { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_item(item); + done(); + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_impl_item(item); + done(); + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_trait_item(item); + done(); + } + + fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx hir::Variant<'_>) { + if !has_attr(cx.sess(), &var.attrs) { + return; + } + prelude(); + let parent_hir_id = cx.tcx.hir().get_parent_node(var.id); + PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id); + done(); + } + + fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField<'_>) { + if !has_attr(cx.sess(), &field.attrs) { + return; + } + prelude(); + PrintVisitor::new("field").visit_struct_field(field); + done(); + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if !has_attr(cx.sess(), &expr.attrs) { + return; + } + prelude(); + PrintVisitor::new("expr").visit_expr(expr); + done(); + } + + fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm<'_>) { + if !has_attr(cx.sess(), &arm.attrs) { + return; + } + prelude(); + PrintVisitor::new("arm").visit_arm(arm); + done(); + } + + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) { + if !has_attr(cx.sess(), stmt.kind.attrs()) { + return; + } + prelude(); + PrintVisitor::new("stmt").visit_stmt(stmt); + done(); + } + + fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ForeignItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_foreign_item(item); + done(); + } +} + +impl PrintVisitor { + #[must_use] + fn new(s: &'static str) -> Self { + Self { + ids: FxHashMap::default(), + current: s.to_owned(), + } + } + + fn next(&mut self, s: &'static str) -> String { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + match self.ids.entry(s) { + // already there: start numbering from `1` + Occupied(mut occ) => { + let val = occ.get_mut(); + *val += 1; + format!("{}{}", s, *val) + }, + // not there: insert and return name as given + Vacant(vac) => { + vac.insert(0); + s.to_owned() + }, + } + } + + fn print_qpath(&mut self, path: &QPath<'_>) { + print!(" if match_qpath({}, &[", self.current); + print_path(path, &mut true); + println!("]);"); + } +} + +struct PrintVisitor { + /// Fields are the current index that needs to be appended to pattern + /// binding names + ids: FxHashMap<&'static str, usize>, + /// the name that needs to be destructured + current: String, +} + +impl<'tcx> Visitor<'tcx> for PrintVisitor { + type Map = Map<'tcx>; + + #[allow(clippy::too_many_lines)] + fn visit_expr(&mut self, expr: &Expr<'_>) { + // handle if desugarings + // TODO add more desugarings here + if let Some((cond, then, opt_else)) = higher::if_block(&expr) { + let cond_pat = self.next("cond"); + let then_pat = self.next("then"); + if let Some(else_) = opt_else { + let else_pat = self.next("else_"); + println!( + " if let Some((ref {}, ref {}, Some({}))) = higher::if_block(&{});", + cond_pat, then_pat, else_pat, self.current + ); + self.current = else_pat; + self.visit_expr(else_); + } else { + println!( + " if let Some((ref {}, ref {}, None)) = higher::if_block(&{});", + cond_pat, then_pat, self.current + ); + } + self.current = cond_pat; + self.visit_expr(cond); + self.current = then_pat; + self.visit_expr(then); + return; + } + + print!(" if let ExprKind::"); + let current = format!("{}.kind", self.current); + match expr.kind { + ExprKind::Box(ref inner) => { + let inner_pat = self.next("inner"); + println!("Box(ref {}) = {};", inner_pat, current); + self.current = inner_pat; + self.visit_expr(inner); + }, + ExprKind::Array(ref elements) => { + let elements_pat = self.next("elements"); + println!("Array(ref {}) = {};", elements_pat, current); + println!(" if {}.len() == {};", elements_pat, elements.len()); + for (i, element) in elements.iter().enumerate() { + self.current = format!("{}[{}]", elements_pat, i); + self.visit_expr(element); + } + }, + ExprKind::Call(ref func, ref args) => { + let func_pat = self.next("func"); + let args_pat = self.next("args"); + println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current); + self.current = func_pat; + self.visit_expr(func); + println!(" if {}.len() == {};", args_pat, args.len()); + for (i, arg) in args.iter().enumerate() { + self.current = format!("{}[{}]", args_pat, i); + self.visit_expr(arg); + } + }, + ExprKind::MethodCall(ref _method_name, ref _generics, ref _args) => { + println!("MethodCall(ref method_name, ref generics, ref args) = {};", current); + println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment"); + }, + ExprKind::Tup(ref elements) => { + let elements_pat = self.next("elements"); + println!("Tup(ref {}) = {};", elements_pat, current); + println!(" if {}.len() == {};", elements_pat, elements.len()); + for (i, element) in elements.iter().enumerate() { + self.current = format!("{}[{}]", elements_pat, i); + self.visit_expr(element); + } + }, + ExprKind::Binary(ref op, ref left, ref right) => { + let op_pat = self.next("op"); + let left_pat = self.next("left"); + let right_pat = self.next("right"); + println!( + "Binary(ref {}, ref {}, ref {}) = {};", + op_pat, left_pat, right_pat, current + ); + println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); + self.current = left_pat; + self.visit_expr(left); + self.current = right_pat; + self.visit_expr(right); + }, + ExprKind::Unary(ref op, ref inner) => { + let inner_pat = self.next("inner"); + println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current); + self.current = inner_pat; + self.visit_expr(inner); + }, + ExprKind::Lit(ref lit) => { + let lit_pat = self.next("lit"); + println!("Lit(ref {}) = {};", lit_pat, current); + match lit.node { + LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat), + LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat), + LitKind::Err(val) => println!(" if let LitKind::Err({}) = {}.node;", val, lit_pat), + LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat), + // FIXME: also check int type + LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat), + LitKind::Float(_, LitFloatType::Suffixed(_)) => println!( + " if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;", + lit_pat + ), + LitKind::Float(_, LitFloatType::Unsuffixed) => println!( + " if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;", + lit_pat + ), + LitKind::ByteStr(ref vec) => { + let vec_pat = self.next("vec"); + println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat); + println!(" if let [{:?}] = **{};", vec, vec_pat); + }, + LitKind::Str(ref text, _) => { + let str_pat = self.next("s"); + println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat); + println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str()) + }, + } + }, + ExprKind::Cast(ref expr, ref ty) => { + let cast_pat = self.next("expr"); + let cast_ty = self.next("cast_ty"); + let qp_label = self.next("qp"); + + println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current); + if let TyKind::Path(ref qp) = ty.kind { + println!(" if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty); + self.current = qp_label; + self.print_qpath(qp); + } + self.current = cast_pat; + self.visit_expr(expr); + }, + ExprKind::Type(ref expr, ref _ty) => { + let cast_pat = self.next("expr"); + println!("Type(ref {}, _) = {};", cast_pat, current); + self.current = cast_pat; + self.visit_expr(expr); + }, + ExprKind::Loop(ref body, _, desugaring) => { + let body_pat = self.next("body"); + let des = loop_desugaring_name(desugaring); + let label_pat = self.next("label"); + println!("Loop(ref {}, ref {}, {}) = {};", body_pat, label_pat, des, current); + self.current = body_pat; + self.visit_block(body); + }, + ExprKind::Match(ref expr, ref arms, desugaring) => { + let des = desugaring_name(desugaring); + let expr_pat = self.next("expr"); + let arms_pat = self.next("arms"); + println!("Match(ref {}, ref {}, {}) = {};", expr_pat, arms_pat, des, current); + self.current = expr_pat; + self.visit_expr(expr); + println!(" if {}.len() == {};", arms_pat, arms.len()); + for (i, arm) in arms.iter().enumerate() { + self.current = format!("{}[{}].body", arms_pat, i); + self.visit_expr(&arm.body); + if let Some(ref guard) = arm.guard { + let guard_pat = self.next("guard"); + println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i); + match guard { + hir::Guard::If(ref if_expr) => { + let if_expr_pat = self.next("expr"); + println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat); + self.current = if_expr_pat; + self.visit_expr(if_expr); + }, + } + } + self.current = format!("{}[{}].pat", arms_pat, i); + self.visit_pat(&arm.pat); + } + }, + ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => { + println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current); + println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment"); + }, + ExprKind::Yield(ref sub, _) => { + let sub_pat = self.next("sub"); + println!("Yield(ref sub) = {};", current); + self.current = sub_pat; + self.visit_expr(sub); + }, + ExprKind::Block(ref block, _) => { + let block_pat = self.next("block"); + println!("Block(ref {}) = {};", block_pat, current); + self.current = block_pat; + self.visit_block(block); + }, + ExprKind::Assign(ref target, ref value, _) => { + let target_pat = self.next("target"); + let value_pat = self.next("value"); + println!( + "Assign(ref {}, ref {}, ref _span) = {};", + target_pat, value_pat, current + ); + self.current = target_pat; + self.visit_expr(target); + self.current = value_pat; + self.visit_expr(value); + }, + ExprKind::AssignOp(ref op, ref target, ref value) => { + let op_pat = self.next("op"); + let target_pat = self.next("target"); + let value_pat = self.next("value"); + println!( + "AssignOp(ref {}, ref {}, ref {}) = {};", + op_pat, target_pat, value_pat, current + ); + println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); + self.current = target_pat; + self.visit_expr(target); + self.current = value_pat; + self.visit_expr(value); + }, + ExprKind::Field(ref object, ref field_ident) => { + let obj_pat = self.next("object"); + let field_name_pat = self.next("field_name"); + println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current); + println!(" if {}.as_str() == {:?}", field_name_pat, field_ident.as_str()); + self.current = obj_pat; + self.visit_expr(object); + }, + ExprKind::Index(ref object, ref index) => { + let object_pat = self.next("object"); + let index_pat = self.next("index"); + println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current); + self.current = object_pat; + self.visit_expr(object); + self.current = index_pat; + self.visit_expr(index); + }, + ExprKind::Path(ref path) => { + let path_pat = self.next("path"); + println!("Path(ref {}) = {};", path_pat, current); + self.current = path_pat; + self.print_qpath(path); + }, + ExprKind::AddrOf(kind, mutability, ref inner) => { + let inner_pat = self.next("inner"); + println!( + "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};", + kind, mutability, inner_pat, current + ); + self.current = inner_pat; + self.visit_expr(inner); + }, + ExprKind::Break(ref _destination, ref opt_value) => { + let destination_pat = self.next("destination"); + if let Some(ref value) = *opt_value { + let value_pat = self.next("value"); + println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current); + self.current = value_pat; + self.visit_expr(value); + } else { + println!("Break(ref {}, None) = {};", destination_pat, current); + } + // FIXME: implement label printing + }, + ExprKind::Continue(ref _destination) => { + let destination_pat = self.next("destination"); + println!("Again(ref {}) = {};", destination_pat, current); + // FIXME: implement label printing + }, + ExprKind::Ret(ref opt_value) => { + if let Some(ref value) = *opt_value { + let value_pat = self.next("value"); + println!("Ret(Some(ref {})) = {};", value_pat, current); + self.current = value_pat; + self.visit_expr(value); + } else { + println!("Ret(None) = {};", current); + } + }, + ExprKind::InlineAsm(_) => { + println!("InlineAsm(_) = {};", current); + println!(" // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); + }, + ExprKind::LlvmInlineAsm(_) => { + println!("LlvmInlineAsm(_) = {};", current); + println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); + }, + ExprKind::Struct(ref path, ref fields, ref opt_base) => { + let path_pat = self.next("path"); + let fields_pat = self.next("fields"); + if let Some(ref base) = *opt_base { + let base_pat = self.next("base"); + println!( + "Struct(ref {}, ref {}, Some(ref {})) = {};", + path_pat, fields_pat, base_pat, current + ); + self.current = base_pat; + self.visit_expr(base); + } else { + println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current); + } + self.current = path_pat; + self.print_qpath(path); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + // FIXME: compute length (needs type info) + ExprKind::Repeat(ref value, _) => { + let value_pat = self.next("value"); + println!("Repeat(ref {}, _) = {};", value_pat, current); + println!("// unimplemented: repeat count check"); + self.current = value_pat; + self.visit_expr(value); + }, + ExprKind::Err => { + println!("Err = {}", current); + }, + ExprKind::DropTemps(ref expr) => { + let expr_pat = self.next("expr"); + println!("DropTemps(ref {}) = {};", expr_pat, current); + self.current = expr_pat; + self.visit_expr(expr); + }, + } + } + + fn visit_block(&mut self, block: &Block<'_>) { + let trailing_pat = self.next("trailing_expr"); + println!(" if let Some({}) = &{}.expr;", trailing_pat, self.current); + println!(" if {}.stmts.len() == {};", self.current, block.stmts.len()); + let current = self.current.clone(); + for (i, stmt) in block.stmts.iter().enumerate() { + self.current = format!("{}.stmts[{}]", current, i); + self.visit_stmt(stmt); + } + } + + #[allow(clippy::too_many_lines)] + fn visit_pat(&mut self, pat: &Pat<'_>) { + print!(" if let PatKind::"); + let current = format!("{}.kind", self.current); + match pat.kind { + PatKind::Wild => println!("Wild = {};", current), + PatKind::Binding(anno, .., ident, ref sub) => { + let anno_pat = match anno { + BindingAnnotation::Unannotated => "BindingAnnotation::Unannotated", + BindingAnnotation::Mutable => "BindingAnnotation::Mutable", + BindingAnnotation::Ref => "BindingAnnotation::Ref", + BindingAnnotation::RefMut => "BindingAnnotation::RefMut", + }; + let name_pat = self.next("name"); + if let Some(ref sub) = *sub { + let sub_pat = self.next("sub"); + println!( + "Binding({}, _, {}, Some(ref {})) = {};", + anno_pat, name_pat, sub_pat, current + ); + self.current = sub_pat; + self.visit_pat(sub); + } else { + println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current); + } + println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str()); + }, + PatKind::Struct(ref path, ref fields, ignore) => { + let path_pat = self.next("path"); + let fields_pat = self.next("fields"); + println!( + "Struct(ref {}, ref {}, {}) = {};", + path_pat, fields_pat, ignore, current + ); + self.current = path_pat; + self.print_qpath(path); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::Or(ref fields) => { + let fields_pat = self.next("fields"); + println!("Or(ref {}) = {};", fields_pat, current); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::TupleStruct(ref path, ref fields, skip_pos) => { + let path_pat = self.next("path"); + let fields_pat = self.next("fields"); + println!( + "TupleStruct(ref {}, ref {}, {:?}) = {};", + path_pat, fields_pat, skip_pos, current + ); + self.current = path_pat; + self.print_qpath(path); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::Path(ref path) => { + let path_pat = self.next("path"); + println!("Path(ref {}) = {};", path_pat, current); + self.current = path_pat; + self.print_qpath(path); + }, + PatKind::Tuple(ref fields, skip_pos) => { + let fields_pat = self.next("fields"); + println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::Box(ref pat) => { + let pat_pat = self.next("pat"); + println!("Box(ref {}) = {};", pat_pat, current); + self.current = pat_pat; + self.visit_pat(pat); + }, + PatKind::Ref(ref pat, muta) => { + let pat_pat = self.next("pat"); + println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current); + self.current = pat_pat; + self.visit_pat(pat); + }, + PatKind::Lit(ref lit_expr) => { + let lit_expr_pat = self.next("lit_expr"); + println!("Lit(ref {}) = {}", lit_expr_pat, current); + self.current = lit_expr_pat; + self.visit_expr(lit_expr); + }, + PatKind::Range(ref start, ref end, end_kind) => { + let start_pat = self.next("start"); + let end_pat = self.next("end"); + println!( + "Range(ref {}, ref {}, RangeEnd::{:?}) = {};", + start_pat, end_pat, end_kind, current + ); + self.current = start_pat; + walk_list!(self, visit_expr, start); + self.current = end_pat; + walk_list!(self, visit_expr, end); + }, + PatKind::Slice(ref start, ref middle, ref end) => { + let start_pat = self.next("start"); + let end_pat = self.next("end"); + if let Some(ref middle) = middle { + let middle_pat = self.next("middle"); + println!( + "Slice(ref {}, Some(ref {}), ref {}) = {};", + start_pat, middle_pat, end_pat, current + ); + self.current = middle_pat; + self.visit_pat(middle); + } else { + println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current); + } + println!(" if {}.len() == {};", start_pat, start.len()); + for (i, pat) in start.iter().enumerate() { + self.current = format!("{}[{}]", start_pat, i); + self.visit_pat(pat); + } + println!(" if {}.len() == {};", end_pat, end.len()); + for (i, pat) in end.iter().enumerate() { + self.current = format!("{}[{}]", end_pat, i); + self.visit_pat(pat); + } + }, + } + } + + fn visit_stmt(&mut self, s: &Stmt<'_>) { + print!(" if let StmtKind::"); + let current = format!("{}.kind", self.current); + match s.kind { + // A local (let) binding: + StmtKind::Local(ref local) => { + let local_pat = self.next("local"); + println!("Local(ref {}) = {};", local_pat, current); + if let Some(ref init) = local.init { + let init_pat = self.next("init"); + println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat); + self.current = init_pat; + self.visit_expr(init); + } + self.current = format!("{}.pat", local_pat); + self.visit_pat(&local.pat); + }, + // An item binding: + StmtKind::Item(_) => { + println!("Item(item_id) = {};", current); + }, + + // Expr without trailing semi-colon (must have unit type): + StmtKind::Expr(ref e) => { + let e_pat = self.next("e"); + println!("Expr(ref {}, _) = {}", e_pat, current); + self.current = e_pat; + self.visit_expr(e); + }, + + // Expr with trailing semi-colon (may have any type): + StmtKind::Semi(ref e) => { + let e_pat = self.next("e"); + println!("Semi(ref {}, _) = {}", e_pat, current); + self.current = e_pat; + self.visit_expr(e); + }, + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { + get_attr(sess, attrs, "author").count() > 0 +} + +#[must_use] +fn desugaring_name(des: hir::MatchSource) -> String { + match des { + hir::MatchSource::ForLoopDesugar => "MatchSource::ForLoopDesugar".to_string(), + hir::MatchSource::TryDesugar => "MatchSource::TryDesugar".to_string(), + hir::MatchSource::WhileDesugar => "MatchSource::WhileDesugar".to_string(), + hir::MatchSource::WhileLetDesugar => "MatchSource::WhileLetDesugar".to_string(), + hir::MatchSource::Normal => "MatchSource::Normal".to_string(), + hir::MatchSource::IfLetDesugar { contains_else_clause } => format!( + "MatchSource::IfLetDesugar {{ contains_else_clause: {} }}", + contains_else_clause + ), + hir::MatchSource::IfDesugar { contains_else_clause } => format!( + "MatchSource::IfDesugar {{ contains_else_clause: {} }}", + contains_else_clause + ), + hir::MatchSource::AwaitDesugar => "MatchSource::AwaitDesugar".to_string(), + } +} + +#[must_use] +fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { + match des { + hir::LoopSource::ForLoop => "LoopSource::ForLoop", + hir::LoopSource::Loop => "LoopSource::Loop", + hir::LoopSource::While => "LoopSource::While", + hir::LoopSource::WhileLet => "LoopSource::WhileLet", + } +} + +fn print_path(path: &QPath<'_>, first: &mut bool) { + match *path { + QPath::Resolved(_, ref path) => { + for segment in path.segments { + if *first { + *first = false; + } else { + print!(", "); + } + print!("{:?}", segment.ident.as_str()); + } + }, + QPath::TypeRelative(ref ty, ref segment) => match ty.kind { + hir::TyKind::Path(ref inner_path) => { + print_path(inner_path, first); + if *first { + *first = false; + } else { + print!(", "); + } + print!("{:?}", segment.ident.as_str()); + }, + ref other => print!("/* unimplemented: {:?}*/", other), + }, + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/camel_case.rs b/src/tools/clippy/clippy_lints/src/utils/camel_case.rs new file mode 100644 index 0000000000000..4192a26d3c800 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/camel_case.rs @@ -0,0 +1,115 @@ +/// Returns the index of the character after the first camel-case component of `s`. +#[must_use] +pub fn until(s: &str) -> usize { + let mut iter = s.char_indices(); + if let Some((_, first)) = iter.next() { + if !first.is_uppercase() { + return 0; + } + } else { + return 0; + } + let mut up = true; + let mut last_i = 0; + for (i, c) in iter { + if up { + if c.is_lowercase() { + up = false; + } else { + return last_i; + } + } else if c.is_uppercase() { + up = true; + last_i = i; + } else if !c.is_lowercase() { + return i; + } + } + if up { + last_i + } else { + s.len() + } +} + +/// Returns index of the last camel-case component of `s`. +#[must_use] +pub fn from(s: &str) -> usize { + let mut iter = s.char_indices().rev(); + if let Some((_, first)) = iter.next() { + if !first.is_lowercase() { + return s.len(); + } + } else { + return s.len(); + } + let mut down = true; + let mut last_i = s.len(); + for (i, c) in iter { + if down { + if c.is_uppercase() { + down = false; + last_i = i; + } else if !c.is_lowercase() { + return last_i; + } + } else if c.is_lowercase() { + down = true; + } else { + return last_i; + } + } + last_i +} + +#[cfg(test)] +mod test { + use super::{from, until}; + + #[test] + fn from_full() { + assert_eq!(from("AbcDef"), 0); + assert_eq!(from("Abc"), 0); + } + + #[test] + fn from_partial() { + assert_eq!(from("abcDef"), 3); + assert_eq!(from("aDbc"), 1); + } + + #[test] + fn from_not() { + assert_eq!(from("AbcDef_"), 7); + assert_eq!(from("AbcDD"), 5); + } + + #[test] + fn from_caps() { + assert_eq!(from("ABCD"), 4); + } + + #[test] + fn until_full() { + assert_eq!(until("AbcDef"), 6); + assert_eq!(until("Abc"), 3); + } + + #[test] + fn until_not() { + assert_eq!(until("abcDef"), 0); + assert_eq!(until("aDbc"), 0); + } + + #[test] + fn until_partial() { + assert_eq!(until("AbcDef_"), 6); + assert_eq!(until("CallTypeC"), 8); + assert_eq!(until("AbcDD"), 3); + } + + #[test] + fn until_caps() { + assert_eq!(until("ABCD"), 0); + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/comparisons.rs new file mode 100644 index 0000000000000..7a18d5e818fb1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/comparisons.rs @@ -0,0 +1,36 @@ +//! Utility functions about comparison operators. + +#![deny(clippy::missing_docs_in_private_items)] + +use rustc_hir::{BinOpKind, Expr}; + +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +/// Represent a normalized comparison operator. +pub enum Rel { + /// `<` + Lt, + /// `<=` + Le, + /// `==` + Eq, + /// `!=` + Ne, +} + +/// Put the expression in the form `lhs < rhs`, `lhs <= rhs`, `lhs == rhs` or +/// `lhs != rhs`. +pub fn normalize_comparison<'a>( + op: BinOpKind, + lhs: &'a Expr<'a>, + rhs: &'a Expr<'a>, +) -> Option<(Rel, &'a Expr<'a>, &'a Expr<'a>)> { + match op { + BinOpKind::Lt => Some((Rel::Lt, lhs, rhs)), + BinOpKind::Le => Some((Rel::Le, lhs, rhs)), + BinOpKind::Gt => Some((Rel::Lt, rhs, lhs)), + BinOpKind::Ge => Some((Rel::Le, rhs, lhs)), + BinOpKind::Eq => Some((Rel::Eq, rhs, lhs)), + BinOpKind::Ne => Some((Rel::Ne, rhs, lhs)), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs new file mode 100644 index 0000000000000..57b9eafd14dbd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -0,0 +1,242 @@ +//! Read configurations files. + +#![deny(clippy::missing_docs_in_private_items)] + +use lazy_static::lazy_static; +use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem}; +use rustc_span::source_map; +use source_map::Span; +use std::path::{Path, PathBuf}; +use std::sync::Mutex; +use std::{env, fmt, fs, io}; + +/// Gets the configuration file from arguments. +pub fn file_from_args(args: &[NestedMetaItem]) -> Result, (&'static str, Span)> { + for arg in args.iter().filter_map(NestedMetaItem::meta_item) { + if arg.check_name(sym!(conf_file)) { + return match arg.kind { + MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)), + MetaItemKind::NameValue(ref value) => { + if let LitKind::Str(ref file, _) = value.kind { + Ok(Some(file.to_string().into())) + } else { + Err(("`conf_file` value must be a string", value.span)) + } + }, + }; + } + } + + Ok(None) +} + +/// Error from reading a configuration file. +#[derive(Debug)] +pub enum Error { + /// An I/O error. + Io(io::Error), + /// Not valid toml or doesn't fit the expected config format + Toml(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Io(err) => err.fmt(f), + Self::Toml(err) => err.fmt(f), + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Self::Io(e) + } +} + +lazy_static! { + static ref ERRORS: Mutex> = Mutex::new(Vec::new()); +} + +macro_rules! define_Conf { + ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { + mod helpers { + use serde::Deserialize; + /// Type used to store lint configuration. + #[derive(Deserialize)] + #[serde(rename_all = "kebab-case", deny_unknown_fields)] + pub struct Conf { + $( + #[$doc] + #[serde(default = $config_str)] + #[serde(with = $config_str)] + pub $config: $Ty, + )+ + #[allow(dead_code)] + #[serde(default)] + third_party: Option<::toml::Value>, + } + + $( + mod $config { + use serde::Deserialize; + pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> { + use super::super::{ERRORS, Error}; + Ok( + <$Ty>::deserialize(deserializer).unwrap_or_else(|e| { + ERRORS + .lock() + .expect("no threading here") + .push(Error::Toml(e.to_string())); + super::$config() + }) + ) + } + } + + #[must_use] + fn $config() -> $Ty { + let x = $default; + x + } + )+ + } + }; +} + +pub use self::helpers::Conf; +define_Conf! { + /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about + (blacklisted_names, "blacklisted_names": Vec, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()), + /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have + (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25), + /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. + (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option, None), + /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks + (doc_valid_idents, "doc_valid_idents": Vec, [ + "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", + "DirectX", + "ECMAScript", + "GPLv2", "GPLv3", + "GitHub", "GitLab", + "IPv4", "IPv6", + "JavaScript", + "NaN", "NaNs", + "OAuth", + "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap", + "TrueType", + "iOS", "macOS", + "TeX", "LaTeX", "BibTeX", "BibLaTeX", + "MinGW", + "CamelCase", + ].iter().map(ToString::to_string).collect()), + /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have + (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7), + /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have + (type_complexity_threshold, "type_complexity_threshold": u64, 250), + /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have + (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), + /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + (too_large_for_stack, "too_large_for_stack": u64, 200), + /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger + (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), + /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion + (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200), + /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' + (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1), + /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals + (literal_representation_threshold, "literal_representation_threshold": u64, 16384), + /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. + (trivial_copy_size_limit, "trivial_copy_size_limit": Option, None), + /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have + (too_many_lines_threshold, "too_many_lines_threshold": u64, 100), + /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack + (array_size_threshold, "array_size_threshold": u64, 512_000), + /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed + (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096), + /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have + (max_struct_bools, "max_struct_bools": u64, 3), + /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have + (max_fn_params_bools, "max_fn_params_bools": u64, 3), + /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). + (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), +} + +impl Default for Conf { + #[must_use] + fn default() -> Self { + toml::from_str("").expect("we never error on empty config files") + } +} + +/// Search for the configuration file. +pub fn lookup_conf_file() -> io::Result> { + /// Possible filename to search for. + const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; + + // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR. + // If neither of those exist, use ".". + let mut current = env::var_os("CLIPPY_CONF_DIR") + .or_else(|| env::var_os("CARGO_MANIFEST_DIR")) + .map_or_else(|| PathBuf::from("."), PathBuf::from); + loop { + for config_file_name in &CONFIG_FILE_NAMES { + let config_file = current.join(config_file_name); + match fs::metadata(&config_file) { + // Only return if it's a file to handle the unlikely situation of a directory named + // `clippy.toml`. + Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)), + // Return the error if it's something other than `NotFound`; otherwise we didn't + // find the project file yet, and continue searching. + Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e), + _ => {}, + } + } + + // If the current directory has no parent, we're done searching. + if !current.pop() { + return Ok(None); + } + } +} + +/// Produces a `Conf` filled with the default values and forwards the errors +/// +/// Used internally for convenience +fn default(errors: Vec) -> (Conf, Vec) { + (Conf::default(), errors) +} + +/// Read the `toml` configuration file. +/// +/// In case of error, the function tries to continue as much as possible. +pub fn read(path: &Path) -> (Conf, Vec) { + let content = match fs::read_to_string(path) { + Ok(content) => content, + Err(err) => return default(vec![err.into()]), + }; + + assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty()); + match toml::from_str(&content) { + Ok(toml) => { + let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); + + let toml_ref: &Conf = &toml; + + let cyc_field: Option = toml_ref.cyclomatic_complexity_threshold; + + if cyc_field.is_some() { + let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string(); + errors.push(Error::Toml(cyc_err)); + } + + (toml, errors) + }, + Err(e) => { + let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); + errors.push(Error::Toml(e.to_string())); + + default(errors) + }, + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/constants.rs b/src/tools/clippy/clippy_lints/src/utils/constants.rs new file mode 100644 index 0000000000000..522932f054d89 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/constants.rs @@ -0,0 +1,13 @@ +//! This module contains some useful constants. + +#![deny(clippy::missing_docs_in_private_items)] + +/// List of the built-in types names. +/// +/// See also [the reference][reference-types] for a list of such types. +/// +/// [reference-types]: https://doc.rust-lang.org/reference/types.html +pub const BUILTIN_TYPES: &[&str] = &[ + "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128", "isize", "usize", "f32", "f64", "bool", + "str", "char", +]; diff --git a/src/tools/clippy/clippy_lints/src/utils/diagnostics.rs b/src/tools/clippy/clippy_lints/src/utils/diagnostics.rs new file mode 100644 index 0000000000000..f6d87c8532e43 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/diagnostics.rs @@ -0,0 +1,217 @@ +//! Clippy wrappers around rustc's diagnostic functions. + +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir::HirId; +use rustc_lint::{LateContext, Lint, LintContext}; +use rustc_span::source_map::{MultiSpan, Span}; +use std::env; + +fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) { + if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { + diag.help(&format!( + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", + &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { + // extract just major + minor version and ignore patch versions + format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap()) + }), + lint.name_lower().replacen("clippy::", "", 1) + )); + } +} + +/// Emit a basic lint message with a `msg` and a `span`. +/// +/// This is the most primitive of our lint emission methods and can +/// be a good way to get a new lint started. +/// +/// Usually it's nicer to provide more context for lint messages. +/// Be sure the output is understandable when you use this method. +/// +/// # Example +/// +/// ```ignore +/// error: usage of mem::forget on Drop type +/// --> $DIR/mem_forget.rs:17:5 +/// | +/// 17 | std::mem::forget(seven); +/// | ^^^^^^^^^^^^^^^^^^^^^^^ +/// ``` +pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: &str) { + cx.struct_span_lint(lint, sp, |diag| { + let mut diag = diag.build(msg); + docs_link(&mut diag, lint); + diag.emit(); + }); +} + +/// Same as `span_lint` but with an extra `help` message. +/// +/// Use this if you want to provide some general help but +/// can't provide a specific machine applicable suggestion. +/// +/// The `help` message can be optionally attached to a `Span`. +/// +/// # Example +/// +/// ```ignore +/// error: constant division of 0.0 with 0.0 will always result in NaN +/// --> $DIR/zero_div_zero.rs:6:25 +/// | +/// 6 | let other_f64_nan = 0.0f64 / 0.0; +/// | ^^^^^^^^^^^^ +/// | +/// = help: Consider using `f64::NAN` if you would like a constant representing NaN +/// ``` +pub fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + help_span: Option, + help: &str, +) { + cx.struct_span_lint(lint, span, |diag| { + let mut diag = diag.build(msg); + if let Some(help_span) = help_span { + diag.span_help(help_span, help); + } else { + diag.help(help); + } + docs_link(&mut diag, lint); + diag.emit(); + }); +} + +/// Like `span_lint` but with a `note` section instead of a `help` message. +/// +/// The `note` message is presented separately from the main lint message +/// and is attached to a specific span: +/// +/// # Example +/// +/// ```ignore +/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +/// --> $DIR/drop_forget_ref.rs:10:5 +/// | +/// 10 | forget(&SomeStruct); +/// | ^^^^^^^^^^^^^^^^^^^ +/// | +/// = note: `-D clippy::forget-ref` implied by `-D warnings` +/// note: argument has type &SomeStruct +/// --> $DIR/drop_forget_ref.rs:10:12 +/// | +/// 10 | forget(&SomeStruct); +/// | ^^^^^^^^^^^ +/// ``` +pub fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option, + note: &str, +) { + cx.struct_span_lint(lint, span, |diag| { + let mut diag = diag.build(msg); + if let Some(note_span) = note_span { + diag.span_note(note_span, note); + } else { + diag.note(note); + } + docs_link(&mut diag, lint); + diag.emit(); + }); +} + +/// Like `span_lint` but allows to add notes, help and suggestions using a closure. +/// +/// If you need to customize your lint output a lot, use this function. +pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +where + F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), +{ + cx.struct_span_lint(lint, sp, |diag| { + let mut diag = diag.build(msg); + f(&mut diag); + docs_link(&mut diag, lint); + diag.emit(); + }); +} + +pub fn span_lint_hir(cx: &LateContext<'_, '_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { + cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| { + let mut diag = diag.build(msg); + docs_link(&mut diag, lint); + diag.emit(); + }); +} + +pub fn span_lint_hir_and_then( + cx: &LateContext<'_, '_>, + lint: &'static Lint, + hir_id: HirId, + sp: Span, + msg: &str, + f: impl FnOnce(&mut DiagnosticBuilder<'_>), +) { + cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| { + let mut diag = diag.build(msg); + f(&mut diag); + docs_link(&mut diag, lint); + diag.emit(); + }); +} + +/// Add a span lint with a suggestion on how to fix it. +/// +/// These suggestions can be parsed by rustfix to allow it to automatically fix your code. +/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x > +/// 2)"`. +/// +/// ```ignore +/// error: This `.fold` can be more succinctly expressed as `.any` +/// --> $DIR/methods.rs:390:13 +/// | +/// 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2); +/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)` +/// | +/// = note: `-D fold-any` implied by `-D warnings` +/// ``` +#[allow(clippy::collapsible_span_lint_calls)] +pub fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { + span_lint_and_then(cx, lint, sp, msg, |diag| { + diag.span_suggestion(sp, help, sugg, applicability); + }); +} + +/// Create a suggestion made from several `span → replacement`. +/// +/// Note: in the JSON format (used by `compiletest_rs`), the help message will +/// appear once per +/// replacement. In human-readable format though, it only appears once before +/// the whole suggestion. +pub fn multispan_sugg(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I) +where + I: IntoIterator, +{ + multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg) +} + +pub fn multispan_sugg_with_applicability( + diag: &mut DiagnosticBuilder<'_>, + help_msg: &str, + applicability: Applicability, + sugg: I, +) where + I: IntoIterator, +{ + diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability); +} diff --git a/src/tools/clippy/clippy_lints/src/utils/higher.rs b/src/tools/clippy/clippy_lints/src/utils/higher.rs new file mode 100644 index 0000000000000..33fba7df8d336 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/higher.rs @@ -0,0 +1,293 @@ +//! This module contains functions for retrieve the original AST from lowered +//! `hir`. + +#![deny(clippy::missing_docs_in_private_items)] + +use crate::utils::{is_expn_of, match_def_path, match_qpath, paths}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; + +/// Converts a hir binary operator to the corresponding `ast` type. +#[must_use] +pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind { + match op { + hir::BinOpKind::Eq => ast::BinOpKind::Eq, + hir::BinOpKind::Ge => ast::BinOpKind::Ge, + hir::BinOpKind::Gt => ast::BinOpKind::Gt, + hir::BinOpKind::Le => ast::BinOpKind::Le, + hir::BinOpKind::Lt => ast::BinOpKind::Lt, + hir::BinOpKind::Ne => ast::BinOpKind::Ne, + hir::BinOpKind::Or => ast::BinOpKind::Or, + hir::BinOpKind::Add => ast::BinOpKind::Add, + hir::BinOpKind::And => ast::BinOpKind::And, + hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd, + hir::BinOpKind::BitOr => ast::BinOpKind::BitOr, + hir::BinOpKind::BitXor => ast::BinOpKind::BitXor, + hir::BinOpKind::Div => ast::BinOpKind::Div, + hir::BinOpKind::Mul => ast::BinOpKind::Mul, + hir::BinOpKind::Rem => ast::BinOpKind::Rem, + hir::BinOpKind::Shl => ast::BinOpKind::Shl, + hir::BinOpKind::Shr => ast::BinOpKind::Shr, + hir::BinOpKind::Sub => ast::BinOpKind::Sub, + } +} + +/// Represent a range akin to `ast::ExprKind::Range`. +#[derive(Debug, Copy, Clone)] +pub struct Range<'a> { + /// The lower bound of the range, or `None` for ranges such as `..X`. + pub start: Option<&'a hir::Expr<'a>>, + /// The upper bound of the range, or `None` for ranges such as `X..`. + pub end: Option<&'a hir::Expr<'a>>, + /// Whether the interval is open or closed. + pub limits: ast::RangeLimits, +} + +/// Higher a `hir` range to something similar to `ast::ExprKind::Range`. +pub fn range<'a, 'b, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'b hir::Expr<'_>) -> Option> { + /// Finds the field named `name` in the field. Always return `Some` for + /// convenience. + fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> { + let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr; + + Some(expr) + } + + let def_path = match cx.tables.expr_ty(expr).kind { + ty::Adt(def, _) => cx.tcx.def_path(def.did), + _ => return None, + }; + + // sanity checks for std::ops::RangeXXXX + if def_path.data.len() != 3 { + return None; + } + if def_path.data.get(0)?.data.as_symbol() != sym!(ops) { + return None; + } + if def_path.data.get(1)?.data.as_symbol() != sym!(range) { + return None; + } + let type_name = def_path.data.get(2)?.data.as_symbol(); + let range_types = [ + "RangeFrom", + "RangeFull", + "RangeInclusive", + "Range", + "RangeTo", + "RangeToInclusive", + ]; + if !range_types.contains(&&*type_name.as_str()) { + return None; + } + + // The range syntax is expanded to literal paths starting with `core` or `std` + // depending on + // `#[no_std]`. Testing both instead of resolving the paths. + + match expr.kind { + hir::ExprKind::Path(ref path) => { + if match_qpath(path, &paths::RANGE_FULL_STD) || match_qpath(path, &paths::RANGE_FULL) { + Some(Range { + start: None, + end: None, + limits: ast::RangeLimits::HalfOpen, + }) + } else { + None + } + }, + hir::ExprKind::Call(ref path, ref args) => { + if let hir::ExprKind::Path(ref path) = path.kind { + if match_qpath(path, &paths::RANGE_INCLUSIVE_STD_NEW) || match_qpath(path, &paths::RANGE_INCLUSIVE_NEW) + { + Some(Range { + start: Some(&args[0]), + end: Some(&args[1]), + limits: ast::RangeLimits::Closed, + }) + } else { + None + } + } else { + None + } + }, + hir::ExprKind::Struct(ref path, ref fields, None) => { + if match_qpath(path, &paths::RANGE_FROM_STD) || match_qpath(path, &paths::RANGE_FROM) { + Some(Range { + start: Some(get_field("start", fields)?), + end: None, + limits: ast::RangeLimits::HalfOpen, + }) + } else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) { + Some(Range { + start: Some(get_field("start", fields)?), + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }) + } else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_TO_INCLUSIVE) + { + Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::Closed, + }) + } else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) { + Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }) + } else { + None + } + }, + _ => None, + } +} + +/// Checks if a `let` statement is from a `for` loop desugaring. +pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { + // This will detect plain for-loops without an actual variable binding: + // + // ``` + // for x in some_vec { + // // do stuff + // } + // ``` + if_chain! { + if let Some(ref expr) = local.init; + if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind; + then { + return true; + } + } + + // This detects a variable binding in for loop to avoid `let_unit_value` + // lint (see issue #1964). + // + // ``` + // for _ in vec![()] { + // // anything + // } + // ``` + if let hir::LocalSource::ForLoopDesugar = local.source { + return true; + } + + false +} + +/// Recover the essential nodes of a desugared for loop: +/// `for pat in arg { body }` becomes `(pat, arg, body)`. +pub fn for_loop<'tcx>( + expr: &'tcx hir::Expr<'tcx>, +) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { + if_chain! { + if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; + if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; + if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(); + if let hir::ExprKind::Loop(ref block, _, _) = arms[0].body.kind; + if block.expr.is_none(); + if let [ _, _, ref let_stmt, ref body ] = *block.stmts; + if let hir::StmtKind::Local(ref local) = let_stmt.kind; + if let hir::StmtKind::Expr(ref expr) = body.kind; + then { + return Some((&*local.pat, &iterargs[0], expr)); + } + } + None +} + +/// Recover the essential nodes of a desugared while loop: +/// `while cond { body }` becomes `(cond, body)`. +pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { + if_chain! { + if let hir::ExprKind::Loop(block, _, hir::LoopSource::While) = &expr.kind; + if let hir::Block { expr: Some(expr), .. } = &**block; + if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind; + if let hir::ExprKind::DropTemps(cond) = &cond.kind; + if let [arm, ..] = &arms[..]; + if let hir::Arm { body, .. } = arm; + then { + return Some((cond, body)); + } + } + None +} + +/// Recover the essential nodes of a desugared if block +/// `if cond { then } else { els }` becomes `(cond, then, Some(els))` +pub fn if_block<'tcx>( + expr: &'tcx hir::Expr<'tcx>, +) -> Option<( + &'tcx hir::Expr<'tcx>, + &'tcx hir::Expr<'tcx>, + Option<&'tcx hir::Expr<'tcx>>, +)> { + if let hir::ExprKind::Match(ref cond, ref arms, hir::MatchSource::IfDesugar { contains_else_clause }) = expr.kind { + let cond = if let hir::ExprKind::DropTemps(ref cond) = cond.kind { + cond + } else { + panic!("If block desugar must contain DropTemps"); + }; + let then = &arms[0].body; + let els = if contains_else_clause { + Some(&*arms[1].body) + } else { + None + }; + Some((cond, then, els)) + } else { + None + } +} + +/// Represent the pre-expansion arguments of a `vec!` invocation. +pub enum VecArgs<'a> { + /// `vec![elem; len]` + Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>), + /// `vec![a, b, c]` + Vec(&'a [hir::Expr<'a>]), +} + +/// Returns the arguments of the `vec!` macro if this expression was expanded +/// from `vec!`. +pub fn vec_macro<'e>(cx: &LateContext<'_, '_>, expr: &'e hir::Expr<'_>) -> Option> { + if_chain! { + if let hir::ExprKind::Call(ref fun, ref args) = expr.kind; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + if is_expn_of(fun.span, "vec").is_some(); + if let Some(fun_def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id(); + then { + return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { + // `vec![elem; size]` case + Some(VecArgs::Repeat(&args[0], &args[1])) + } + else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { + // `vec![a, b, c]` case + if_chain! { + if let hir::ExprKind::Box(ref boxed) = args[0].kind; + if let hir::ExprKind::Array(ref args) = boxed.kind; + then { + return Some(VecArgs::Vec(&*args)); + } + } + + None + } + else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { + Some(VecArgs::Vec(&[])) + } + else { + None + }; + } + } + + None +} diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs new file mode 100644 index 0000000000000..92c27e79452ab --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs @@ -0,0 +1,745 @@ +use crate::consts::{constant_context, constant_simple}; +use crate::utils::differing_macro_contexts; +use rustc_ast::ast::InlineAsmTemplatePiece; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::{ + BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg, + GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, + Stmt, StmtKind, Ty, TyKind, TypeBinding, +}; +use rustc_lint::LateContext; +use rustc_middle::ich::StableHashingContextProvider; +use rustc_middle::ty::TypeckTables; +use rustc_span::Symbol; +use std::hash::Hash; + +/// Type used to check whether two ast are the same. This is different from the +/// operator +/// `==` on ast types as this operator would compare true equality with ID and +/// span. +/// +/// Note that some expressions kinds are not considered but could be added. +pub struct SpanlessEq<'a, 'tcx> { + /// Context used to evaluate constant expressions. + cx: &'a LateContext<'a, 'tcx>, + tables: &'a TypeckTables<'tcx>, + /// If is true, never consider as equal expressions containing function + /// calls. + ignore_fn: bool, +} + +impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { + pub fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + Self { + cx, + tables: cx.tables, + ignore_fn: false, + } + } + + pub fn ignore_fn(self) -> Self { + Self { + cx: self.cx, + tables: self.cx.tables, + ignore_fn: true, + } + } + + /// Checks whether two statements are the same. + pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { + match (&left.kind, &right.kind) { + (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { + self.eq_pat(&l.pat, &r.pat) + && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) + && both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) + }, + (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => { + self.eq_expr(l, r) + }, + _ => false, + } + } + + /// Checks whether two blocks are the same. + pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { + over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) + && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) + } + + #[allow(clippy::similar_names)] + pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + if self.ignore_fn && differing_macro_contexts(left.span, right.span) { + return false; + } + + if let (Some(l), Some(r)) = ( + constant_simple(self.cx, self.tables, left), + constant_simple(self.cx, self.tables, right), + ) { + if l == r { + return true; + } + } + + match (&left.kind, &right.kind) { + (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { + lb == rb && l_mut == r_mut && self.eq_expr(le, re) + }, + (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { + both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + }, + (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { + self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + }, + (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => { + lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + }, + (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r), + (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => { + l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + || swap_binop(l_op.node, ll, lr).map_or(false, |(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)) => { + both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + && both(le, re, |l, r| self.eq_expr(l, r)) + }, + (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), + (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { + !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) + }, + (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt)) + | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => { + self.eq_expr(lx, rx) && self.eq_ty(lt, rt) + }, + (&ExprKind::Field(ref l_f_exp, ref l_f_ident), &ExprKind::Field(ref r_f_exp, ref r_f_ident)) => { + l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) + }, + (&ExprKind::Index(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => { + self.eq_expr(la, ra) && self.eq_expr(li, ri) + }, + (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, + (&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => { + lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str()) + }, + (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => { + ls == rs + && self.eq_expr(le, re) + && over(la, ra, |l, r| { + self.eq_expr(&l.body, &r.body) + && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r)) + && self.eq_pat(&l.pat, &r.pat) + }) + }, + (&ExprKind::MethodCall(l_path, _, l_args), &ExprKind::MethodCall(r_path, _, r_args)) => { + !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) + }, + (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => { + let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(ll_id.body)); + let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value); + let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(rl_id.body)); + let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value); + + self.eq_expr(le, re) && ll == rl + }, + (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)), + (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r), + (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => { + self.eq_qpath(l_path, r_path) + && both(lo, ro, |l, r| self.eq_expr(l, r)) + && over(lf, rf, |l, r| self.eq_field(l, r)) + }, + (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), + (&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re), + (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), + (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), + _ => false, + } + } + + fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { + over(left, right, |l, r| self.eq_expr(l, r)) + } + + fn eq_field(&mut self, left: &Field<'_>, right: &Field<'_>) -> bool { + left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr) + } + + fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool { + match (left, right) { + (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r), + } + } + + fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool { + match (left, right) { + (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt), + (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty), + _ => false, + } + } + + fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool { + left.name == right.name + } + + /// Checks whether two patterns are the same. + pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { + match (&left.kind, &right.kind) { + (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), + (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { + self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs + }, + (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => { + lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r)) + }, + (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), + (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), + (&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref 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)) => { + both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri) + }, + (&PatKind::Ref(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re), + (&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref re)) => { + over(ls, rs, |l, r| self.eq_pat(l, r)) + && over(le, re, |l, r| self.eq_pat(l, r)) + && both(li, ri, |l, r| self.eq_pat(l, r)) + }, + (&PatKind::Wild, &PatKind::Wild) => true, + _ => false, + } + } + + #[allow(clippy::similar_names)] + fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { + match (left, right) { + (&QPath::Resolved(ref lty, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => { + both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath) + }, + (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => { + self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) + }, + _ => false, + } + } + + fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { + left.is_global() == right.is_global() + && over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)) + } + + fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { + if !(left.parenthesized || right.parenthesized) { + over(&left.args, &right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work + && over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r)) + } else if left.parenthesized && right.parenthesized { + over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r)) + && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| { + self.eq_ty(l, r) + }) + } else { + false + } + } + + pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { + left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r)) + } + + pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { + // The == of idents doesn't work with different contexts, + // we have to be explicit about hygiene + if left.ident.as_str() != right.ident.as_str() { + return false; + } + match (&left.args, &right.args) { + (&None, &None) => true, + (&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r), + _ => false, + } + } + + pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { + self.eq_ty_kind(&left.kind, &right.kind) + } + + #[allow(clippy::similar_names)] + pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { + match (left, right) { + (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec), + (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => { + let full_table = self.tables; + + let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(ll_id.body)); + self.tables = self.cx.tcx.body_tables(ll_id.body); + let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value); + + let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(rl_id.body)); + self.tables = self.cx.tcx.body_tables(rl_id.body); + let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value); + + let eq_ty = self.eq_ty(lt, rt); + self.tables = full_table; + eq_ty && ll == rl + }, + (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { + l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) + }, + (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { + l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty) + }, + (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), + (&TyKind::Tup(ref l), &TyKind::Tup(ref r)) => over(l, r, |l, r| self.eq_ty(l, r)), + (&TyKind::Infer, &TyKind::Infer) => true, + _ => false, + } + } + + fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool { + left.ident.name == right.ident.name && self.eq_ty(&left.ty(), &right.ty()) + } +} + +fn swap_binop<'a>( + binop: BinOpKind, + lhs: &'a Expr<'a>, + rhs: &'a Expr<'a>, +) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> { + match binop { + BinOpKind::Add + | BinOpKind::Mul + | BinOpKind::Eq + | BinOpKind::Ne + | BinOpKind::BitAnd + | BinOpKind::BitXor + | BinOpKind::BitOr => Some((binop, rhs, lhs)), + BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)), + BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)), + BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)), + BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)), + BinOpKind::Shl + | BinOpKind::Shr + | BinOpKind::Rem + | BinOpKind::Sub + | BinOpKind::Div + | BinOpKind::And + | BinOpKind::Or => None, + } +} + +/// Checks if the two `Option`s are both `None` or some equal values as per +/// `eq_fn`. +fn both(l: &Option, r: &Option, mut eq_fn: F) -> bool +where + F: FnMut(&X, &X) -> bool, +{ + l.as_ref() + .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y))) +} + +/// Checks if two slices are equal as per `eq_fn`. +fn over(left: &[X], right: &[X], mut eq_fn: F) -> bool +where + F: FnMut(&X, &X) -> bool, +{ + left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) +} + +/// Type used to hash an ast element. This is different from the `Hash` trait +/// on ast types as this +/// trait would consider IDs and spans. +/// +/// All expressions kind are hashed, but some might have a weaker hash. +pub struct SpanlessHash<'a, 'tcx> { + /// Context used to evaluate constant expressions. + cx: &'a LateContext<'a, 'tcx>, + tables: &'a TypeckTables<'tcx>, + s: StableHasher, +} + +impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { + pub fn new(cx: &'a LateContext<'a, 'tcx>, tables: &'a TypeckTables<'tcx>) -> Self { + Self { + cx, + tables, + s: StableHasher::new(), + } + } + + pub fn finish(self) -> u64 { + self.s.finish() + } + + pub fn hash_block(&mut self, b: &Block<'_>) { + for s in b.stmts { + self.hash_stmt(s); + } + + if let Some(ref e) = b.expr { + self.hash_expr(e); + } + + match b.rules { + BlockCheckMode::DefaultBlock => 0, + BlockCheckMode::UnsafeBlock(_) => 1, + BlockCheckMode::PushUnsafeBlock(_) => 2, + BlockCheckMode::PopUnsafeBlock(_) => 3, + } + .hash(&mut self.s); + } + + #[allow(clippy::many_single_char_names, clippy::too_many_lines)] + pub fn hash_expr(&mut self, e: &Expr<'_>) { + let simple_const = constant_simple(self.cx, self.tables, e); + + // const hashing may result in the same hash as some unrelated node, so add a sort of + // discriminant depending on which path we're choosing next + simple_const.is_some().hash(&mut self.s); + + if let Some(e) = simple_const { + return e.hash(&mut self.s); + } + + std::mem::discriminant(&e.kind).hash(&mut self.s); + + match e.kind { + ExprKind::AddrOf(kind, m, ref e) => { + match kind { + BorrowKind::Ref => 0, + BorrowKind::Raw => 1, + } + .hash(&mut self.s); + m.hash(&mut self.s); + self.hash_expr(e); + }, + ExprKind::Continue(i) => { + if let Some(i) = i.label { + self.hash_name(i.ident.name); + } + }, + ExprKind::Assign(ref l, ref r, _) => { + self.hash_expr(l); + self.hash_expr(r); + }, + ExprKind::AssignOp(ref o, ref l, ref r) => { + o.node + .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); + self.hash_expr(l); + self.hash_expr(r); + }, + ExprKind::Block(ref b, _) => { + self.hash_block(b); + }, + ExprKind::Binary(op, ref l, ref r) => { + op.node + .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); + self.hash_expr(l); + self.hash_expr(r); + }, + ExprKind::Break(i, ref j) => { + if let Some(i) = i.label { + self.hash_name(i.ident.name); + } + if let Some(ref j) = *j { + self.hash_expr(&*j); + } + }, + ExprKind::Box(ref e) | ExprKind::DropTemps(ref e) | ExprKind::Yield(ref e, _) => { + self.hash_expr(e); + }, + ExprKind::Call(ref fun, args) => { + self.hash_expr(fun); + self.hash_exprs(args); + }, + ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => { + self.hash_expr(e); + self.hash_ty(ty); + }, + ExprKind::Closure(cap, _, eid, _, _) => { + match cap { + CaptureBy::Value => 0, + CaptureBy::Ref => 1, + } + .hash(&mut self.s); + // closures inherit TypeckTables + self.hash_expr(&self.cx.tcx.hir().body(eid).value); + }, + ExprKind::Field(ref e, ref f) => { + self.hash_expr(e); + self.hash_name(f.name); + }, + ExprKind::Index(ref a, ref i) => { + self.hash_expr(a); + self.hash_expr(i); + }, + ExprKind::InlineAsm(ref asm) => { + for piece in asm.template { + match piece { + InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s), + InlineAsmTemplatePiece::Placeholder { + operand_idx, + modifier, + span: _, + } => { + operand_idx.hash(&mut self.s); + modifier.hash(&mut self.s); + }, + } + } + asm.options.hash(&mut self.s); + for op in asm.operands { + match op { + InlineAsmOperand::In { reg, expr } => { + reg.hash(&mut self.s); + self.hash_expr(expr); + }, + InlineAsmOperand::Out { reg, late, expr } => { + reg.hash(&mut self.s); + late.hash(&mut self.s); + if let Some(expr) = expr { + self.hash_expr(expr); + } + }, + InlineAsmOperand::InOut { reg, late, expr } => { + reg.hash(&mut self.s); + late.hash(&mut self.s); + self.hash_expr(expr); + }, + InlineAsmOperand::SplitInOut { + reg, + late, + in_expr, + out_expr, + } => { + reg.hash(&mut self.s); + late.hash(&mut self.s); + self.hash_expr(in_expr); + if let Some(out_expr) = out_expr { + self.hash_expr(out_expr); + } + }, + InlineAsmOperand::Const { expr } | InlineAsmOperand::Sym { expr } => self.hash_expr(expr), + } + } + }, + ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {}, + ExprKind::Lit(ref l) => { + l.node.hash(&mut self.s); + }, + ExprKind::Loop(ref b, ref i, _) => { + self.hash_block(b); + if let Some(i) = *i { + self.hash_name(i.ident.name); + } + }, + ExprKind::Match(ref e, arms, ref s) => { + self.hash_expr(e); + + for arm in arms { + // TODO: arm.pat? + if let Some(ref e) = arm.guard { + self.hash_guard(e); + } + self.hash_expr(&arm.body); + } + + s.hash(&mut self.s); + }, + ExprKind::MethodCall(ref path, ref _tys, args) => { + self.hash_name(path.ident.name); + self.hash_exprs(args); + }, + ExprKind::Repeat(ref e, ref l_id) => { + self.hash_expr(e); + self.hash_body(l_id.body); + }, + ExprKind::Ret(ref e) => { + if let Some(ref e) = *e { + self.hash_expr(e); + } + }, + ExprKind::Path(ref qpath) => { + self.hash_qpath(qpath); + }, + ExprKind::Struct(ref path, fields, ref expr) => { + self.hash_qpath(path); + + for f in fields { + self.hash_name(f.ident.name); + self.hash_expr(&f.expr); + } + + if let Some(ref e) = *expr { + self.hash_expr(e); + } + }, + ExprKind::Tup(tup) => { + self.hash_exprs(tup); + }, + ExprKind::Array(v) => { + self.hash_exprs(v); + }, + ExprKind::Unary(lop, ref le) => { + lop.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); + self.hash_expr(le); + }, + } + } + + pub fn hash_exprs(&mut self, e: &[Expr<'_>]) { + for e in e { + self.hash_expr(e); + } + } + + pub fn hash_name(&mut self, n: Symbol) { + n.as_str().hash(&mut self.s); + } + + pub fn hash_qpath(&mut self, p: &QPath<'_>) { + match *p { + QPath::Resolved(_, ref path) => { + self.hash_path(path); + }, + QPath::TypeRelative(_, ref path) => { + self.hash_name(path.ident.name); + }, + } + // self.cx.tables.qpath_res(p, id).hash(&mut self.s); + } + + pub fn hash_path(&mut self, p: &Path<'_>) { + p.is_global().hash(&mut self.s); + for p in p.segments { + self.hash_name(p.ident.name); + } + } + + pub fn hash_stmt(&mut self, b: &Stmt<'_>) { + std::mem::discriminant(&b.kind).hash(&mut self.s); + + match &b.kind { + StmtKind::Local(local) => { + if let Some(ref init) = local.init { + self.hash_expr(init); + } + }, + StmtKind::Item(..) => {}, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { + self.hash_expr(expr); + }, + } + } + + pub fn hash_guard(&mut self, g: &Guard<'_>) { + match g { + Guard::If(ref expr) => { + self.hash_expr(expr); + }, + } + } + + pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { + std::mem::discriminant(&lifetime.name).hash(&mut self.s); + if let LifetimeName::Param(ref name) = lifetime.name { + std::mem::discriminant(name).hash(&mut self.s); + match name { + ParamName::Plain(ref ident) => { + ident.name.hash(&mut self.s); + }, + ParamName::Fresh(ref size) => { + size.hash(&mut self.s); + }, + ParamName::Error => {}, + } + } + } + + pub fn hash_ty(&mut self, ty: &Ty<'_>) { + self.hash_tykind(&ty.kind); + } + + pub fn hash_tykind(&mut self, ty: &TyKind<'_>) { + std::mem::discriminant(ty).hash(&mut self.s); + match ty { + TyKind::Slice(ty) => { + self.hash_ty(ty); + }, + TyKind::Array(ty, anon_const) => { + self.hash_ty(ty); + self.hash_body(anon_const.body); + }, + TyKind::Ptr(mut_ty) => { + self.hash_ty(&mut_ty.ty); + mut_ty.mutbl.hash(&mut self.s); + }, + TyKind::Rptr(lifetime, mut_ty) => { + self.hash_lifetime(lifetime); + self.hash_ty(&mut_ty.ty); + mut_ty.mutbl.hash(&mut self.s); + }, + TyKind::BareFn(bfn) => { + bfn.unsafety.hash(&mut self.s); + bfn.abi.hash(&mut self.s); + for arg in bfn.decl.inputs { + self.hash_ty(&arg); + } + match bfn.decl.output { + FnRetTy::DefaultReturn(_) => { + ().hash(&mut self.s); + }, + FnRetTy::Return(ref ty) => { + self.hash_ty(ty); + }, + } + bfn.decl.c_variadic.hash(&mut self.s); + }, + TyKind::Tup(ty_list) => { + for ty in *ty_list { + self.hash_ty(ty); + } + }, + TyKind::Path(qpath) => match qpath { + QPath::Resolved(ref maybe_ty, ref path) => { + if let Some(ref ty) = maybe_ty { + self.hash_ty(ty); + } + for segment in path.segments { + segment.ident.name.hash(&mut self.s); + } + }, + QPath::TypeRelative(ref ty, ref segment) => { + self.hash_ty(ty); + segment.ident.name.hash(&mut self.s); + }, + }, + TyKind::Def(_, arg_list) => { + for arg in *arg_list { + match arg { + GenericArg::Lifetime(ref l) => self.hash_lifetime(l), + GenericArg::Type(ref ty) => self.hash_ty(&ty), + GenericArg::Const(ref ca) => self.hash_body(ca.value.body), + } + } + }, + TyKind::TraitObject(_, lifetime) => { + self.hash_lifetime(lifetime); + }, + TyKind::Typeof(anon_const) => { + self.hash_body(anon_const.body); + }, + TyKind::Err | TyKind::Infer | TyKind::Never => {}, + } + } + + pub fn hash_body(&mut self, body_id: BodyId) { + // swap out TypeckTables when hashing a body + let old_tables = self.tables; + self.tables = self.cx.tcx.body_tables(body_id); + self.hash_expr(&self.cx.tcx.hir().body(body_id).value); + self.tables = old_tables; + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs new file mode 100644 index 0000000000000..748c11fac64ff --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -0,0 +1,550 @@ +//! checks for attributes + +use crate::utils::get_attr; +use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::Session; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Dumps every ast/hir node which has the `#[clippy::dump]` + /// attribute + /// + /// **Example:** + /// ```rust,ignore + /// #[clippy::dump] + /// extern crate foo; + /// ``` + /// + /// prints + /// + /// ```text + /// item `foo` + /// visibility inherited from outer item + /// extern crate dylib source: "/path/to/foo.so" + /// ``` + pub DEEP_CODE_INSPECTION, + internal_warn, + "helper to dump info about code" +} + +declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DeepCodeInspector { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + print_item(cx, item); + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + println!("impl item `{}`", item.ident.name); + match item.vis.node { + hir::VisibilityKind::Public => println!("public"), + hir::VisibilityKind::Crate(_) => println!("visible crate wide"), + hir::VisibilityKind::Restricted { ref path, .. } => println!( + "visible in module `{}`", + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) + ), + hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), + } + if item.defaultness.is_default() { + println!("default"); + } + match item.kind { + hir::ImplItemKind::Const(_, body_id) => { + println!("associated constant"); + print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); + }, + hir::ImplItemKind::Fn(..) => println!("method"), + hir::ImplItemKind::TyAlias(_) => println!("associated type"), + hir::ImplItemKind::OpaqueTy(_) => println!("existential type"), + } + } + // fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx + // hir::TraitItem) { + // if !has_attr(&item.attrs) { + // return; + // } + // } + // + // fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx + // hir::Variant, _: + // &hir::Generics) { + // if !has_attr(&var.node.attrs) { + // return; + // } + // } + // + // fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx + // hir::StructField) { + // if !has_attr(&field.attrs) { + // return; + // } + // } + // + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if !has_attr(cx.sess(), &expr.attrs) { + return; + } + print_expr(cx, expr, 0); + } + + fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm<'_>) { + if !has_attr(cx.sess(), &arm.attrs) { + return; + } + print_pat(cx, &arm.pat, 1); + if let Some(ref guard) = arm.guard { + println!("guard:"); + print_guard(cx, guard, 1); + } + println!("body:"); + print_expr(cx, &arm.body, 1); + } + + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) { + if !has_attr(cx.sess(), stmt.kind.attrs()) { + return; + } + match stmt.kind { + hir::StmtKind::Local(ref local) => { + println!("local variable of type {}", cx.tables.node_type(local.hir_id)); + println!("pattern:"); + print_pat(cx, &local.pat, 0); + if let Some(ref e) = local.init { + println!("init expression:"); + print_expr(cx, e, 0); + } + }, + hir::StmtKind::Item(_) => println!("item decl"), + hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0), + } + } + // fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx + // hir::ForeignItem) { + // if !has_attr(&item.attrs) { + // return; + // } + // } + // +} + +fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { + get_attr(sess, attrs, "dump").count() > 0 +} + +#[allow(clippy::similar_names)] +#[allow(clippy::too_many_lines)] +fn print_expr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, indent: usize) { + let ind = " ".repeat(indent); + println!("{}+", ind); + println!("{}ty: {}", ind, cx.tables.expr_ty(expr)); + println!("{}adjustments: {:?}", ind, cx.tables.adjustments().get(expr.hir_id)); + match expr.kind { + hir::ExprKind::Box(ref e) => { + println!("{}Box", ind); + print_expr(cx, e, indent + 1); + }, + hir::ExprKind::Array(v) => { + println!("{}Array", ind); + for e in v { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Call(ref func, args) => { + println!("{}Call", ind); + println!("{}function:", ind); + print_expr(cx, func, indent + 1); + println!("{}arguments:", ind); + for arg in args { + print_expr(cx, arg, indent + 1); + } + }, + hir::ExprKind::MethodCall(ref path, _, args) => { + println!("{}MethodCall", ind); + println!("{}method name: {}", ind, path.ident.name); + for arg in args { + print_expr(cx, arg, indent + 1); + } + }, + hir::ExprKind::Tup(v) => { + println!("{}Tup", ind); + for e in v { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + println!("{}Binary", ind); + println!("{}op: {:?}", ind, op.node); + println!("{}lhs:", ind); + print_expr(cx, lhs, indent + 1); + println!("{}rhs:", ind); + print_expr(cx, rhs, indent + 1); + }, + hir::ExprKind::Unary(op, ref inner) => { + println!("{}Unary", ind); + println!("{}op: {:?}", ind, op); + print_expr(cx, inner, indent + 1); + }, + hir::ExprKind::Lit(ref lit) => { + println!("{}Lit", ind); + println!("{}{:?}", ind, lit); + }, + hir::ExprKind::Cast(ref e, ref target) => { + println!("{}Cast", ind); + print_expr(cx, e, indent + 1); + println!("{}target type: {:?}", ind, target); + }, + hir::ExprKind::Type(ref e, ref target) => { + println!("{}Type", ind); + print_expr(cx, e, indent + 1); + println!("{}target type: {:?}", ind, target); + }, + hir::ExprKind::Loop(..) => { + println!("{}Loop", ind); + }, + hir::ExprKind::Match(ref cond, _, ref source) => { + println!("{}Match", ind); + println!("{}condition:", ind); + print_expr(cx, cond, indent + 1); + println!("{}source: {:?}", ind, source); + }, + hir::ExprKind::Closure(ref clause, _, _, _, _) => { + println!("{}Closure", ind); + println!("{}clause: {:?}", ind, clause); + }, + hir::ExprKind::Yield(ref sub, _) => { + println!("{}Yield", ind); + print_expr(cx, sub, indent + 1); + }, + hir::ExprKind::Block(_, _) => { + println!("{}Block", ind); + }, + hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + println!("{}Assign", ind); + println!("{}lhs:", ind); + print_expr(cx, lhs, indent + 1); + println!("{}rhs:", ind); + print_expr(cx, rhs, indent + 1); + }, + hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => { + println!("{}AssignOp", ind); + println!("{}op: {:?}", ind, binop.node); + println!("{}lhs:", ind); + print_expr(cx, lhs, indent + 1); + println!("{}rhs:", ind); + print_expr(cx, rhs, indent + 1); + }, + hir::ExprKind::Field(ref e, ident) => { + println!("{}Field", ind); + println!("{}field name: {}", ind, ident.name); + println!("{}struct expr:", ind); + print_expr(cx, e, indent + 1); + }, + hir::ExprKind::Index(ref arr, ref idx) => { + println!("{}Index", ind); + println!("{}array expr:", ind); + print_expr(cx, arr, indent + 1); + println!("{}index expr:", ind); + print_expr(cx, idx, indent + 1); + }, + hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + println!("{}Resolved Path, {:?}", ind, ty); + println!("{}path: {:?}", ind, path); + }, + hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + println!("{}Relative Path, {:?}", ind, ty); + println!("{}seg: {:?}", ind, seg); + }, + hir::ExprKind::AddrOf(kind, ref muta, ref e) => { + println!("{}AddrOf", ind); + println!("kind: {:?}", kind); + println!("mutability: {:?}", muta); + print_expr(cx, e, indent + 1); + }, + hir::ExprKind::Break(_, ref e) => { + println!("{}Break", ind); + if let Some(ref e) = *e { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Continue(_) => println!("{}Again", ind), + hir::ExprKind::Ret(ref e) => { + println!("{}Ret", ind); + if let Some(ref e) = *e { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::InlineAsm(ref asm) => { + println!("{}InlineAsm", ind); + println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); + println!("{}options: {:?}", ind, asm.options); + println!("{}operands:", ind); + for op in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } => print_expr(cx, expr, indent + 1), + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + print_expr(cx, expr, indent + 1); + } + }, + hir::InlineAsmOperand::InOut { expr, .. } => print_expr(cx, expr, indent + 1), + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + print_expr(cx, in_expr, indent + 1); + if let Some(out_expr) = out_expr { + print_expr(cx, out_expr, indent + 1); + } + }, + hir::InlineAsmOperand::Const { expr } => print_expr(cx, expr, indent + 1), + hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), + } + } + }, + hir::ExprKind::LlvmInlineAsm(ref asm) => { + let inputs = &asm.inputs_exprs; + let outputs = &asm.outputs_exprs; + println!("{}LlvmInlineAsm", ind); + println!("{}inputs:", ind); + for e in inputs.iter() { + print_expr(cx, e, indent + 1); + } + println!("{}outputs:", ind); + for e in outputs.iter() { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Struct(ref path, fields, ref base) => { + println!("{}Struct", ind); + println!("{}path: {:?}", ind, path); + for field in fields { + println!("{}field \"{}\":", ind, field.ident.name); + print_expr(cx, &field.expr, indent + 1); + } + if let Some(ref base) = *base { + println!("{}base:", ind); + print_expr(cx, base, indent + 1); + } + }, + hir::ExprKind::Repeat(ref val, ref anon_const) => { + println!("{}Repeat", ind); + println!("{}value:", ind); + print_expr(cx, val, indent + 1); + println!("{}repeat count:", ind); + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, + hir::ExprKind::Err => { + println!("{}Err", ind); + }, + hir::ExprKind::DropTemps(ref e) => { + println!("{}DropTemps", ind); + print_expr(cx, e, indent + 1); + }, + } +} + +fn print_item(cx: &LateContext<'_, '_>, item: &hir::Item<'_>) { + let did = cx.tcx.hir().local_def_id(item.hir_id); + println!("item `{}`", item.ident.name); + match item.vis.node { + hir::VisibilityKind::Public => println!("public"), + hir::VisibilityKind::Crate(_) => println!("visible crate wide"), + hir::VisibilityKind::Restricted { ref path, .. } => println!( + "visible in module `{}`", + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) + ), + hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), + } + match item.kind { + hir::ItemKind::ExternCrate(ref _renamed_from) => { + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(def_id) { + let source = cx.tcx.used_crate_source(crate_id); + if let Some(ref src) = source.dylib { + println!("extern crate dylib source: {:?}", src.0); + } + if let Some(ref src) = source.rlib { + println!("extern crate rlib source: {:?}", src.0); + } + } else { + println!("weird extern crate without a crate id"); + } + }, + hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind), + hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), + hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), + hir::ItemKind::Fn(..) => { + let item_ty = cx.tcx.type_of(did); + println!("function of type {:#?}", item_ty); + }, + hir::ItemKind::Mod(..) => println!("module"), + hir::ItemKind::ForeignMod(ref fm) => println!("foreign module with abi: {}", fm.abi), + hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm), + hir::ItemKind::TyAlias(..) => { + println!("type alias for {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::OpaqueTy(..) => { + println!("existential type with real type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Enum(..) => { + println!("enum definition of type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Struct(..) => { + println!("struct definition of type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Union(..) => { + println!("union definition of type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Trait(..) => { + println!("trait decl"); + if cx.tcx.trait_is_auto(did.to_def_id()) { + println!("trait is auto"); + } else { + println!("trait is not auto"); + } + }, + hir::ItemKind::TraitAlias(..) => { + println!("trait alias"); + }, + hir::ItemKind::Impl { + of_trait: Some(ref _trait_ref), + .. + } => { + println!("trait impl"); + }, + hir::ItemKind::Impl { of_trait: None, .. } => { + println!("impl"); + }, + } +} + +#[allow(clippy::similar_names)] +#[allow(clippy::too_many_lines)] +fn print_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, indent: usize) { + let ind = " ".repeat(indent); + println!("{}+", ind); + match pat.kind { + hir::PatKind::Wild => println!("{}Wild", ind), + hir::PatKind::Binding(ref mode, .., ident, ref inner) => { + println!("{}Binding", ind); + println!("{}mode: {:?}", ind, mode); + println!("{}name: {}", ind, ident.name); + if let Some(ref inner) = *inner { + println!("{}inner:", ind); + print_pat(cx, inner, indent + 1); + } + }, + hir::PatKind::Or(fields) => { + println!("{}Or", ind); + for field in fields { + print_pat(cx, field, indent + 1); + } + }, + hir::PatKind::Struct(ref path, fields, ignore) => { + println!("{}Struct", ind); + println!( + "{}name: {}", + ind, + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) + ); + println!("{}ignore leftover fields: {}", ind, ignore); + println!("{}fields:", ind); + for field in fields { + println!("{} field name: {}", ind, field.ident.name); + if field.is_shorthand { + println!("{} in shorthand notation", ind); + } + print_pat(cx, &field.pat, indent + 1); + } + }, + hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { + println!("{}TupleStruct", ind); + println!( + "{}path: {}", + ind, + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) + ); + if let Some(dot_position) = opt_dots_position { + println!("{}dot position: {}", ind, dot_position); + } + for field in fields { + print_pat(cx, field, indent + 1); + } + }, + hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + println!("{}Resolved Path, {:?}", ind, ty); + println!("{}path: {:?}", ind, path); + }, + hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + println!("{}Relative Path, {:?}", ind, ty); + println!("{}seg: {:?}", ind, seg); + }, + hir::PatKind::Tuple(pats, opt_dots_position) => { + println!("{}Tuple", ind); + if let Some(dot_position) = opt_dots_position { + println!("{}dot position: {}", ind, dot_position); + } + for field in pats { + print_pat(cx, field, indent + 1); + } + }, + hir::PatKind::Box(ref inner) => { + println!("{}Box", ind); + print_pat(cx, inner, indent + 1); + }, + hir::PatKind::Ref(ref inner, ref muta) => { + println!("{}Ref", ind); + println!("{}mutability: {:?}", ind, muta); + print_pat(cx, inner, indent + 1); + }, + hir::PatKind::Lit(ref e) => { + println!("{}Lit", ind); + print_expr(cx, e, indent + 1); + }, + hir::PatKind::Range(ref l, ref r, ref range_end) => { + println!("{}Range", ind); + if let Some(expr) = l { + print_expr(cx, expr, indent + 1); + } + if let Some(expr) = r { + print_expr(cx, expr, indent + 1); + } + match *range_end { + hir::RangeEnd::Included => println!("{} end included", ind), + hir::RangeEnd::Excluded => println!("{} end excluded", ind), + } + }, + hir::PatKind::Slice(first_pats, ref range, last_pats) => { + println!("{}Slice [a, b, ..i, y, z]", ind); + println!("[a, b]:"); + for pat in first_pats { + print_pat(cx, pat, indent + 1); + } + println!("i:"); + if let Some(ref pat) = *range { + print_pat(cx, pat, indent + 1); + } + println!("[y, z]:"); + for pat in last_pats { + print_pat(cx, pat, indent + 1); + } + }, + } +} + +fn print_guard(cx: &LateContext<'_, '_>, guard: &hir::Guard<'_>, indent: usize) { + let ind = " ".repeat(indent); + println!("{}+", ind); + match guard { + hir::Guard::If(expr) => { + println!("{}If", ind); + print_expr(cx, expr, indent + 1); + }, + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs new file mode 100644 index 0000000000000..8e1b047f6f80a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -0,0 +1,658 @@ +use crate::utils::SpanlessEq; +use crate::utils::{ + is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, + span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, +}; +use if_chain::if_chain; +use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; +use rustc_ast::visit::FnKind; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::hir_id::CRATE_HIR_ID; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::{Symbol, SymbolStr}; + +use std::borrow::{Borrow, Cow}; + +declare_clippy_lint! { + /// **What it does:** Checks for various things we like to keep tidy in clippy. + /// + /// **Why is this bad?** We like to pretend we're an example of tidy code. + /// + /// **Known problems:** None. + /// + /// **Example:** Wrong ordering of the util::paths constants. + pub CLIPPY_LINTS_INTERNAL, + internal, + "various things that will negatively affect your clippy experience" +} + +declare_clippy_lint! { + /// **What it does:** Ensures every lint is associated to a `LintPass`. + /// + /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without + /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not + /// know the name of the lint. + /// + /// **Known problems:** Only checks for lints associated using the + /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. + /// + /// **Example:** + /// ```rust,ignore + /// declare_lint! { pub LINT_1, ... } + /// declare_lint! { pub LINT_2, ... } + /// declare_lint! { pub FORGOTTEN_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" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` + /// variant of the function. + /// + /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the + /// warning/error messages. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// cx.span_lint(LINT_NAME, "message"); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// utils::span_lint(cx, LINT_NAME, "message"); + /// ``` + pub COMPILER_LINT_FUNCTIONS, + internal, + "usage of the lint functions of the compiler instead of the utils::* variant" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use + /// the `cx.outer_expn_data()` + /// + /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// expr.span.ctxt().outer().expn_data() + /// ``` + /// + /// Good: + /// ```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()`" +} + +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 + /// + /// **Known problems:** None + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + +declare_clippy_lint! { + /// **What it does:** Checks for cases of an auto-generated lint without an updated description, + /// i.e. `default lint description`. + /// + /// **Why is this bad?** Indicates that the lint is not finished. + /// + /// **Known problems:** None + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Good: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } + /// ``` + pub DEFAULT_LINT, + internal, + "found 'default lint description' in a lint declaration" +} + +declare_clippy_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 + /// call to `span_suggestion`, `span_help`, `span_note` (using the same + /// span), `help` or `note`. + /// + /// These usages of `span_lint_and_then` should be replaced with one of the + /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or + /// `span_lint_and_note`. + /// + /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more + /// convenient, readable and less error prone. + /// + /// **Known problems:** None + /// + /// *Example:** + /// Bad: + /// ```rust,ignore + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_suggestion( + /// expr.span, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_help(expr.span, help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.help(help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_note(expr.span, note_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.note(note_msg); + /// }); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// span_lint_and_sugg( + /// cx, + /// TEST_LINT, + /// expr.span, + /// lint_msg, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + /// 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" +} + +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); + +impl EarlyLintPass for ClippyLintsInternal { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) { + if let Some(utils) = krate + .module + .items + .iter() + .find(|item| item.ident.name.as_str() == "utils") + { + if let ItemKind::Mod(ref utils_mod) = utils.kind { + if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") { + if let ItemKind::Mod(ref paths_mod) = paths.kind { + let mut last_name: Option = None; + for item in &*paths_mod.items { + let name = item.ident.as_str(); + if let Some(ref last_name) = last_name { + if **last_name > *name { + span_lint( + cx, + CLIPPY_LINTS_INTERNAL, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + } + last_name = Some(name); + } + } + } + } + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct LintWithoutLintPass { + declared_lints: FxHashMap, + registered_lints: FxHashSet, +} + +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { + if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) { + return; + } + + if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind { + if is_lint_ref_type(cx, ty) { + let expr = &cx.tcx.hir().body(body_id).value; + if_chain! { + if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; + if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind; + let field = fields + .iter() + .find(|f| f.ident.as_str() == "desc") + .expect("lints must have a description field"); + if let ExprKind::Lit(Spanned { + node: LitKind::Str(ref sym, _), + .. + }) = field.expr.kind; + if sym.as_str() == "default lint description"; + + then { + span_lint( + cx, + DEFAULT_LINT, + item.span, + &format!("the lint `{}` has the default lint description", item.ident.name), + ); + } + } + self.declared_lints.insert(item.ident.name, item.span); + } + } else if is_expn_of(item.span, "impl_lint_pass").is_some() + || is_expn_of(item.span, "declare_lint_pass").is_some() + { + if let hir::ItemKind::Impl { + of_trait: None, + items: ref impl_item_refs, + .. + } = item.kind + { + let mut collector = LintCollector { + output: &mut self.registered_lints, + cx, + }; + let body_id = cx.tcx.hir().body_owned_by( + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .hir_id, + ); + collector.visit_expr(&cx.tcx.hir().body(body_id).value); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) { + if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) { + return; + } + + for (lint_name, &lint_span) in &self.declared_lints { + // When using the `declare_tool_lint!` macro, the original `lint_span`'s + // file points to "". + // `compiletest-rs` thinks that's an error in a different file and + // just ignores it. This causes the test in compile-fail/lint_pass + // not able to capture the error. + // Therefore, we need to climb the macro expansion tree and find the + // actual span that invoked `declare_tool_lint!`: + let lint_span = lint_span.ctxt().outer_expn_data().call_site; + + if !self.registered_lints.contains(lint_name) { + span_lint( + cx, + LINT_WITHOUT_LINT_PASS, + lint_span, + &format!("the lint `{}` is not added to any `LintPass`", lint_name), + ); + } + } + } +} + +fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty<'_>) -> bool { + if let TyKind::Rptr( + _, + MutTy { + ty: ref inner, + mutbl: Mutability::Not, + }, + ) = ty.kind + { + if let TyKind::Path(ref path) = inner.kind { + if let Res::Def(DefKind::Struct, def_id) = cx.tables.qpath_res(path, inner.hir_id) { + return match_def_path(cx, def_id, &paths::LINT); + } + } + } + + false +} + +struct LintCollector<'a, 'tcx> { + output: &'a mut FxHashSet, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { + if path.segments.len() == 1 { + self.output.insert(path.segments[0].ident.name); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} + +#[derive(Clone, Default)] +pub struct CompilerLintFunctions { + map: FxHashMap<&'static str, &'static str>, +} + +impl CompilerLintFunctions { + #[must_use] + pub fn new() -> Self { + let mut map = FxHashMap::default(); + map.insert("span_lint", "utils::span_lint"); + map.insert("struct_span_lint", "utils::span_lint"); + map.insert("lint", "utils::span_lint"); + map.insert("span_lint_note", "utils::span_lint_and_note"); + map.insert("span_lint_help", "utils::span_lint_and_help"); + Self { map } + } +} + +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind; + let fn_name = path.ident; + if let Some(sugg) = self.map.get(&*fn_name.as_str()); + let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); + if match_type(cx, ty, &paths::EARLY_CONTEXT) + || match_type(cx, ty, &paths::LATE_CONTEXT); + then { + span_lint_and_help( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + None, + &format!("please use the Clippy variant of this function: `{}`", sugg), + ); + } + } + } +} + +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnDataPass { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) { + return; + } + + let (method_names, arg_lists, spans) = method_calls(expr, 2); + let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); + let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); + if_chain! { + if let ["expn_data", "outer_expn"] = method_names.as_slice(); + let args = arg_lists[1]; + if args.len() == 1; + let self_arg = &args[0]; + let self_ty = walk_ptrs_ty(cx.tables.expr_ty(self_arg)); + if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); + then { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + if is_trigger_fn(fn_kind) { + panic!("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, + } +} + +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CollapsibleCalls { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(ref func, ref and_then_args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["span_lint_and_then"]); + if and_then_args.len() == 5; + if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; + let body = cx.tcx.hir().body(*body_id); + if let ExprKind::Block(block, _) = &body.value.kind; + let stmts = &block.stmts; + if stmts.len() == 1 && block.expr.is_none(); + if let StmtKind::Semi(only_expr) = &stmts[0].kind; + if let ExprKind::MethodCall(ref ps, _, ref span_call_args) = &only_expr.kind; + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).ignore_fn(); + then { + match &*ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { + suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { + let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { + let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + } + "note" => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + } + _ => (), + } + } + } + } +} + +struct AndThenSnippets<'a> { + cx: Cow<'a, str>, + lint: Cow<'a, str>, + span: Cow<'a, str>, + msg: Cow<'a, str>, +} + +fn get_and_then_snippets<'a, 'hir>( + cx: &LateContext<'_, '_>, + and_then_snippets: &'hir [Expr<'hir>], +) -> AndThenSnippets<'a> { + let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); + let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); + let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); + let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); + + AndThenSnippets { + cx: cx_snippet, + lint: lint_snippet, + span: span_snippet, + msg: msg_snippet, + } +} + +struct SpanSuggestionSnippets<'a> { + help: Cow<'a, str>, + sugg: Cow<'a, str>, + applicability: Cow<'a, str>, +} + +fn span_suggestion_snippets<'a, 'hir>( + cx: &LateContext<'_, '_>, + span_call_args: &'hir [Expr<'hir>], +) -> SpanSuggestionSnippets<'a> { + let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#); + let sugg_snippet = snippet(cx, span_call_args[3].span, ".."); + let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable"); + + SpanSuggestionSnippets { + help: help_snippet, + sugg: sugg_snippet, + applicability: applicability_snippet, + } +} + +fn suggest_suggestion( + cx: &LateContext<'_, '_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + span_suggestion_snippets: &SpanSuggestionSnippets<'_>, +) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + span_suggestion_snippets.help, + span_suggestion_snippets.sugg, + span_suggestion_snippets.applicability + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_help( + cx: &LateContext<'_, '_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + help: &str, + with_span: bool, +) { + let option_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_help({}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + &option_span, + help + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_note( + cx: &LateContext<'_, '_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + note: &str, + with_span: bool, +) { + let note_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collspible", + "collapse into", + format!( + "span_lint_and_note({}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + note_span, + note + ), + Applicability::MachineApplicable, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs new file mode 100644 index 0000000000000..438a9f42ccd23 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -0,0 +1,1485 @@ +#[macro_use] +pub mod sym; + +pub mod attrs; +pub mod author; +pub mod camel_case; +pub mod comparisons; +pub mod conf; +pub mod constants; +mod diagnostics; +pub mod higher; +mod hir_utils; +pub mod inspector; +pub mod internal_lints; +pub mod numeric_literal; +pub mod paths; +pub mod ptr; +pub mod sugg; +pub mod usage; +pub use self::attrs::*; +pub use self::diagnostics::*; +pub use self::hir_utils::{SpanlessEq, SpanlessHash}; + +use std::borrow::Cow; +use std::mem; + +use if_chain::if_chain; +use rustc_ast::ast::{self, Attribute, LitKind}; +use rustc_attr as attr; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::Node; +use rustc_hir::{ + def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind, + MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, +}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable}; +use rustc_span::hygiene::{ExpnKind, MacroKind}; +use rustc_span::source_map::original_sp; +use rustc_span::symbol::{self, kw, Symbol}; +use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; +use rustc_target::abi::Integer; +use rustc_trait_selection::traits::query::normalize::AtExt; +use smallvec::SmallVec; + +use crate::consts::{constant, Constant}; +use crate::reexport::Name; + +/// Returns `true` if the two spans come from differing expansions (i.e., one is +/// from a macro and one isn't). +#[must_use] +pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool { + rhs.ctxt() != lhs.ctxt() +} + +/// Returns `true` if the given `NodeId` is inside a constant context +/// +/// # Example +/// +/// ```rust,ignore +/// if in_constant(cx, expr.hir_id) { +/// // Do something +/// } +/// ``` +pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool { + let parent_id = cx.tcx.hir().get_parent_item(id); + match cx.tcx.hir().get(parent_id) { + Node::Item(&Item { + kind: ItemKind::Const(..), + .. + }) + | Node::TraitItem(&TraitItem { + kind: TraitItemKind::Const(..), + .. + }) + | Node::ImplItem(&ImplItem { + kind: ImplItemKind::Const(..), + .. + }) + | Node::AnonConst(_) + | Node::Item(&Item { + kind: ItemKind::Static(..), + .. + }) => true, + Node::Item(&Item { + kind: ItemKind::Fn(ref sig, ..), + .. + }) + | Node::ImplItem(&ImplItem { + kind: ImplItemKind::Fn(ref sig, _), + .. + }) => sig.header.constness == Constness::Const, + _ => false, + } +} + +/// Returns `true` if this `span` was expanded by any macro. +#[must_use] +pub fn in_macro(span: Span) -> bool { + if span.from_expansion() { + if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind { + false + } else { + true + } + } else { + false + } +} +// If the snippet is empty, it's an attribute that was inserted during macro +// expansion and we want to ignore those, because they could come from external +// sources that the user has no control over. +// 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(cx: &T, span: Span) -> bool { + if let Some(snippet) = snippet_opt(cx, span) { + if snippet.is_empty() { + return false; + } + } + true +} + +/// Checks if given pattern is a wildcard (`_`) +pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { + match pat.kind { + PatKind::Wild => true, + _ => false, + } +} + +/// Checks if type is struct, enum or union type with the given def path. +pub fn match_type(cx: &LateContext<'_, '_>, ty: Ty<'_>, path: &[&str]) -> bool { + match ty.kind { + ty::Adt(adt, _) => match_def_path(cx, adt.did, path), + _ => false, + } +} + +/// Checks if the type is equal to a diagnostic item +pub fn is_type_diagnostic_item(cx: &LateContext<'_, '_>, ty: Ty<'_>, diag_item: Symbol) -> bool { + match ty.kind { + ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), + _ => false, + } +} + +/// Checks if the method call given in `expr` belongs to the given trait. +pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr<'_>, path: &[&str]) -> bool { + let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap(); + let trt_id = cx.tcx.trait_of_item(def_id); + if let Some(trt_id) = trt_id { + match_def_path(cx, trt_id, path) + } else { + false + } +} + +/// Checks if an expression references a variable of the given name. +pub fn match_var(expr: &Expr<'_>, var: Name) -> bool { + if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { + if path.segments.len() == 1 && path.segments[0].ident.name == var { + return true; + } + } + false +} + +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { + match *path { + QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"), + QPath::TypeRelative(_, ref seg) => seg, + } +} + +pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { + match *path { + QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]), + QPath::Resolved(..) => None, + QPath::TypeRelative(_, ref seg) => Some(seg), + } +} + +/// Matches a `QPath` against a slice of segment string literals. +/// +/// 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(_, ref path) => match_path(path, segments), + QPath::TypeRelative(ref ty, ref segment) => match ty.kind { + TyKind::Path(ref inner_path) => { + !segments.is_empty() + && match_qpath(inner_path, &segments[..(segments.len() - 1)]) + && segment.ident.name.as_str() == segments[segments.len() - 1] + }, + _ => false, + }, + } +} + +/// Matches a `Path` against a slice of segment string literals. +/// +/// 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) +} + +/// Matches a `Path` against a slice of segment string literals, e.g. +/// +/// # Examples +/// ```rust,ignore +/// match_path_ast(path, &["std", "rt", "begin_unwind"]) +/// ``` +pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { + path.segments + .iter() + .rev() + .zip(segments.iter().rev()) + .all(|(a, b)| a.ident.name.as_str() == *b) +} + +/// Gets the definition associated to a path. +pub fn path_to_res(cx: &LateContext<'_, '_>, path: &[&str]) -> Option { + let crates = cx.tcx.crates(); + let krate = crates + .iter() + .find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]); + if let Some(krate) = krate { + let krate = DefId { + krate: *krate, + index: CRATE_DEF_INDEX, + }; + let mut items = cx.tcx.item_children(krate); + let mut path_it = path.iter().skip(1).peekable(); + + loop { + let segment = match path_it.next() { + Some(segment) => segment, + None => return None, + }; + + let result = SmallVec::<[_; 8]>::new(); + for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { + if item.ident.name.as_str() == *segment { + if path_it.peek().is_none() { + return Some(item.res); + } + + items = cx.tcx.item_children(item.res.def_id()); + break; + } + } + } + } else { + None + } +} + +pub fn qpath_res(cx: &LateContext<'_, '_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { + match qpath { + hir::QPath::Resolved(_, path) => path.res, + hir::QPath::TypeRelative(..) => { + if cx.tcx.has_typeck_tables(id.owner.to_def_id()) { + cx.tcx + .typeck_tables_of(id.owner.to_def_id().expect_local()) + .qpath_res(qpath, id) + } else { + Res::Err + } + }, + } +} + +/// Convenience function to get the `DefId` of a trait by path. +/// It could be a trait or trait alias. +pub fn get_trait_def_id(cx: &LateContext<'_, '_>, path: &[&str]) -> Option { + let res = match path_to_res(cx, path) { + Some(res) => res, + None => return None, + }; + + match res { + Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), + Res::Err => unreachable!("this trait resolution is impossible: {:?}", &path), + _ => None, + } +} + +/// Checks whether a type implements a trait. +/// See also `get_trait_def_id`. +pub fn implements_trait<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + ty_params: &[GenericArg<'tcx>], +) -> bool { + let ty_params = cx.tcx.mk_substs(ty_params.iter()); + cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env)) +} + +/// 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: +/// +/// ```rust +/// struct Point(isize, isize); +/// +/// impl std::ops::Add for Point { +/// type Output = Self; +/// +/// fn add(self, other: Self) -> Self { +/// Point(0, 0) +/// } +/// } +/// ``` +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> { + // Get the implemented trait for the current function + let parent_impl = cx.tcx.hir().get_parent_item(hir_id); + if_chain! { + if parent_impl != hir::CRATE_HIR_ID; + if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl); + if let hir::ItemKind::Impl{ of_trait: trait_ref, .. } = &item.kind; + then { return trait_ref.as_ref(); } + } + None +} + +/// Checks whether this type implements `Drop`. +pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + match ty.ty_adt_def() { + Some(def) => def.has_dtor(cx.tcx), + _ => false, + } +} + +/// Returns the method names and argument list of nested method call expressions that make up +/// `expr`. method/span lists are sorted with the most recent call first. +pub fn method_calls<'tcx>( + expr: &'tcx Expr<'tcx>, + max_depth: usize, +) -> (Vec, Vec<&'tcx [Expr<'tcx>]>, Vec) { + let mut method_names = Vec::with_capacity(max_depth); + let mut arg_lists = Vec::with_capacity(max_depth); + let mut spans = Vec::with_capacity(max_depth); + + let mut current = expr; + for _ in 0..max_depth { + if let ExprKind::MethodCall(path, span, args) = ¤t.kind { + if args.iter().any(|e| e.span.from_expansion()) { + break; + } + method_names.push(path.ident.name); + arg_lists.push(&**args); + spans.push(*span); + current = &args[0]; + } else { + break; + } + } + + (method_names, arg_lists, spans) +} + +/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s. +/// +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`, +/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec` +/// containing the `Expr`s for +/// `.bar()` and `.baz()` +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option]>> { + 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(ref path, _, ref args) = current.kind { + if path.ident.name.as_str() == *method_name { + if args.iter().any(|e| e.span.from_expansion()) { + return None; + } + matched.push(&**args); // build up `matched` backwards + current = &args[0] // go to parent expression + } else { + return None; + } + } else { + return None; + } + } + // Reverse `matched` so that it is in the same order as `methods`. + matched.reverse(); + Some(matched) +} + +/// Returns `true` if the provided `def_id` is an entrypoint to a program. +pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { + cx.tcx + .entry_fn(LOCAL_CRATE) + .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id()) +} + +/// Gets the name of the item the expression is in, if available. +pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); + match cx.tcx.hir().find(parent_id) { + Some( + Node::Item(Item { ident, .. }) + | Node::TraitItem(TraitItem { ident, .. }) + | Node::ImplItem(ImplItem { ident, .. }), + ) => Some(ident.name), + _ => None, + } +} + +/// Gets the name of a `Pat`, if any. +pub fn get_pat_name(pat: &Pat<'_>) -> Option { + match pat.kind { + PatKind::Binding(.., ref spname, _) => Some(spname.name), + PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name), + PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p), + _ => None, + } +} + +struct ContainsName { + name: Name, + result: bool, +} + +impl<'tcx> Visitor<'tcx> for ContainsName { + type Map = Map<'tcx>; + + fn visit_name(&mut self, _: Span, name: Name) { + if self.name == name { + self.result = true; + } + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Checks if an `Expr` contains a certain name. +pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool { + let mut cn = ContainsName { name, result: false }; + cn.visit_expr(expr); + cn.result +} + +/// Converts a span to a code snippet if available, otherwise use default. +/// +/// This is useful if you want to provide suggestions for your lint or more generally, if you want +/// to convert a given `Span` to a `str`. +/// +/// # Example +/// ```rust,ignore +/// snippet(cx, expr.span, "..") +/// ``` +pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) +} + +/// Same as `snippet`, but it adapts the applicability level by following rules: +/// +/// - Applicability level `Unspecified` will never be changed. +/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. +/// - If the default value is used and the applicability level is `MachineApplicable`, change it to +/// `HasPlaceholders` +pub fn snippet_with_applicability<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + applicability: &mut Applicability, +) -> Cow<'a, str> { + if *applicability != Applicability::Unspecified && span.from_expansion() { + *applicability = Applicability::MaybeIncorrect; + } + snippet_opt(cx, span).map_or_else( + || { + if *applicability == Applicability::MachineApplicable { + *applicability = Applicability::HasPlaceholders; + } + Cow::Borrowed(default) + }, + From::from, + ) +} + +/// Same as `snippet`, but should only be used when it's clear that the input span is +/// not a macro argument. +pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { + snippet(cx, span.source_callsite(), default) +} + +/// Converts a span to a code snippet. Returns `None` if not available. +pub fn snippet_opt(cx: &T, span: Span) -> Option { + cx.sess().source_map().span_to_snippet(span).ok() +} + +/// Converts a span (from a block) to a code snippet if available, otherwise use default. +/// +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like +/// things which need to be printed as such. +/// +/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the +/// resulting snippet of the given span. +/// +/// # Example +/// +/// ```rust,ignore +/// snippet_block(cx, block.span, "..", None) +/// // where, `block` is the block of the if expr +/// if x { +/// y; +/// } +/// // will return the snippet +/// { +/// y; +/// } +/// ``` +/// +/// ```rust,ignore +/// snippet_block(cx, block.span, "..", Some(if_expr.span)) +/// // where, `block` is the block of the if expr +/// if x { +/// y; +/// } +/// // will return the snippet +/// { +/// y; +/// } // aligned with `if` +/// ``` +/// Note that the first line of the snippet always has 0 indentation. +pub fn snippet_block<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { + let snip = snippet(cx, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + trim_multiline(snip, true, indent) +} + +/// Same as `snippet_block`, but adapts the applicability level by the rules of +/// `snippet_with_applicabiliy`. +pub fn snippet_block_with_applicability<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + indent_relative_to: Option, + applicability: &mut Applicability, +) -> Cow<'a, str> { + let snip = snippet_with_applicability(cx, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + trim_multiline(snip, true, indent) +} + +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first +/// line. +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ +/// // will be converted to +/// let x = (); +/// // ^^^^^^^^^^ +/// ``` +pub fn first_line_of_span(cx: &T, span: Span) -> Span { + if let Some(first_char_pos) = first_char_in_first_line(cx, span) { + span.with_lo(first_char_pos) + } else { + span + } +} + +fn first_char_in_first_line(cx: &T, span: Span) -> Option { + let line_span = line_span(cx, span); + if let Some(snip) = snippet_opt(cx, line_span) { + snip.find(|c: char| !c.is_whitespace()) + .map(|pos| line_span.lo() + BytePos::from_usize(pos)) + } else { + None + } +} + +/// Returns the indentation of the line of a span +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ -- will return 0 +/// let x = (); +/// // ^^ -- will return 4 +/// ``` +pub fn indent_of(cx: &T, span: Span) -> Option { + if let Some(snip) = snippet_opt(cx, line_span(cx, span)) { + snip.find(|c: char| !c.is_whitespace()) + } else { + None + } +} + +/// Extends the span to the beginning of the spans line, incl. whitespaces. +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ +/// // will be converted to +/// let x = (); +/// // ^^^^^^^^^^^^^^ +/// ``` +fn line_span(cx: &T, span: Span) -> Span { + let span = original_sp(span, DUMMY_SP); + let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); + let line_no = source_map_and_line.line; + let line_start = source_map_and_line.sf.lines[line_no]; + Span::new(line_start, span.hi(), span.ctxt()) +} + +/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. +/// Also takes an `Option` which can be put inside the braces. +pub fn expr_block<'a, T: LintContext>( + cx: &T, + expr: &Expr<'_>, + option: Option, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { + let code = snippet_block(cx, expr.span, default, indent_relative_to); + let string = option.unwrap_or_default(); + if expr.span.from_expansion() { + Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) + } else if let ExprKind::Block(_, _) = expr.kind { + Cow::Owned(format!("{}{}", code, string)) + } else if string.is_empty() { + Cow::Owned(format!("{{ {} }}", code)) + } else { + Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) + } +} + +/// Trim indentation from a multiline string with possibility of ignoring the +/// first line. +fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { + let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); + let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); + trim_multiline_inner(s_tab, ignore_first, indent, ' ') +} + +fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option, ch: char) -> Cow<'_, str> { + let mut x = s + .lines() + .skip(ignore_first as usize) + .filter_map(|l| { + if l.is_empty() { + None + } else { + // ignore empty lines + Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0) + } + }) + .min() + .unwrap_or(0); + if let Some(indent) = indent { + x = x.saturating_sub(indent); + } + if x > 0 { + Cow::Owned( + s.lines() + .enumerate() + .map(|(i, l)| { + if (ignore_first && i == 0) || l.is_empty() { + l + } else { + l.split_at(x).1 + } + }) + .collect::>() + .join("\n"), + ) + } else { + s + } +} + +/// Gets the parent expression, if any –- this is useful to constrain a lint. +pub fn get_parent_expr<'c>(cx: &'c LateContext<'_, '_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> { + let map = &cx.tcx.hir(); + let hir_id = e.hir_id; + let parent_id = map.get_parent_node(hir_id); + if hir_id == parent_id { + return None; + } + map.find(parent_id).and_then(|node| { + if let Node::Expr(parent) = node { + Some(parent) + } else { + None + } + }) +} + +pub fn get_enclosing_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { + let map = &cx.tcx.hir(); + let enclosing_node = map + .get_enclosing_scope(hir_id) + .and_then(|enclosing_id| map.find(enclosing_id)); + if let Some(node) = enclosing_node { + match node { + Node::Block(block) => Some(block), + Node::Item(&Item { + kind: ItemKind::Fn(_, _, eid), + .. + }) + | Node::ImplItem(&ImplItem { + kind: ImplItemKind::Fn(_, eid), + .. + }) => match cx.tcx.hir().body(eid).value.kind { + ExprKind::Block(ref block, _) => Some(block), + _ => None, + }, + _ => None, + } + } else { + None + } +} + +/// Returns the base type for HIR references and pointers. +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + match ty.kind { + TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty), + _ => ty, + } +} + +/// Returns the base type for references and raw pointers. +pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> { + match ty.kind { + ty::Ref(_, ty, _) => walk_ptrs_ty(ty), + _ => ty, + } +} + +/// Returns the base type for references and raw pointers, and count reference +/// depth. +pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { + match ty.kind { + ty::Ref(_, ty, _) => inner(ty, depth + 1), + _ => (ty, depth), + } + } + inner(ty, 0) +} + +/// Checks whether the given expression is a constant integer of the given value. +/// unlike `is_integer_literal`, this version does const folding +pub fn is_integer_const(cx: &LateContext<'_, '_>, e: &Expr<'_>, value: u128) -> bool { + if is_integer_literal(e, value) { + return true; + } + let map = cx.tcx.hir(); + let parent_item = map.get_parent_item(e.hir_id); + if let Some((Constant::Int(v), _)) = map + .maybe_body_owned_by(parent_item) + .and_then(|body_id| constant(cx, cx.tcx.body_tables(body_id), e)) + { + value == v + } else { + false + } +} + +/// 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(ref spanned) = expr.kind { + if let LitKind::Int(v, _) = spanned.node { + return v == value; + } + } + false +} + +/// Returns `true` if the given `Expr` has been coerced before. +/// +/// Examples of coercions can be found in the Nomicon at +/// . +/// +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more +/// information on adjustments and coercions. +pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool { + cx.tables.adjustments().get(e.hir_id).is_some() +} + +/// Returns the pre-expansion span if is this comes from an expansion of the +/// macro `name`. +/// See also `is_direct_expn_of`. +#[must_use] +pub fn is_expn_of(mut span: Span, name: &str) -> 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); + } + } + + span = new_span; + } else { + return None; + } + } +} + +/// Returns the pre-expansion span if the span directly comes from an expansion +/// of the macro `name`. +/// The difference with `is_expn_of` is that in +/// ```rust,ignore +/// foo!(bar!(42)); +/// ``` +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only +/// `bar!` by +/// `is_direct_expn_of`. +#[must_use] +pub fn is_direct_expn_of(span: Span, name: &str) -> 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); + } + } + } + + None +} + +/// Convenience function to get the return type of a function. +pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: hir::HirId) -> Ty<'tcx> { + let fn_def_id = cx.tcx.hir().local_def_id(fn_item); + let ret_ty = cx.tcx.fn_sig(fn_def_id).output(); + cx.tcx.erase_late_bound_regions(&ret_ty) +} + +/// Checks if two types are the same. +/// +/// This discards any lifetime annotations, too. +// +// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` == +// `for <'b> Foo<'b>`, but not for type parameters). +pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + let a = cx.tcx.erase_late_bound_regions(&Binder::bind(a)); + let b = cx.tcx.erase_late_bound_regions(&Binder::bind(b)); + cx.tcx + .infer_ctxt() + .enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok()) +} + +/// Returns `true` if the given type is an `unsafe` function. +pub fn type_is_unsafe_function<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind { + ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe, + _ => false, + } +} + +pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_copy_modulo_regions(cx.tcx, cx.param_env, DUMMY_SP) +} + +/// 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(ref fun, _) = expr.kind { + if let ExprKind::Path(ref qp) = fun.kind { + let res = cx.tables.qpath_res(qp, fun.hir_id); + return match res { + def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), + _ => false, + }; + } + } + false +} + +/// Returns `true` if a pattern is refutable. +// TODO: should be implemented using rustc/mir_build/hair machinery +pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool { + fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool { + matches!( + cx.tables.qpath_res(qpath, id), + def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _) + ) + } + + fn are_refutable<'a, I: Iterator>>(cx: &LateContext<'_, '_>, mut i: I) -> bool { + i.any(|pat| is_refutable(cx, pat)) + } + + match pat.kind { + PatKind::Wild => false, + PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)), + PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat), + PatKind::Lit(..) | PatKind::Range(..) => true, + PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), + PatKind::Or(ref pats) => { + // TODO: should be the honest check, that pats is exhaustive set + are_refutable(cx, pats.iter().map(|pat| &**pat)) + }, + PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), + PatKind::Struct(ref qpath, ref fields, _) => { + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat)) + }, + PatKind::TupleStruct(ref qpath, ref pats, _) => { + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat)) + }, + PatKind::Slice(ref head, ref middle, ref tail) => { + match &cx.tables.node_type(pat.hir_id).kind { + ty::Slice(..) => { + // [..] is the only irrefutable slice pattern. + !head.is_empty() || middle.is_none() || !tail.is_empty() + }, + ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)), + _ => { + // unreachable!() + true + }, + } + }, + } +} + +/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d +/// implementations have. +pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { + attr::contains_name(attrs, sym!(automatically_derived)) +} + +/// Remove blocks around an expression. +/// +/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return +/// themselves. +pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + while let ExprKind::Block(ref block, ..) = expr.kind { + match (block.stmts.is_empty(), block.expr.as_ref()) { + (true, Some(e)) => expr = e, + _ => break, + } + } + expr +} + +pub fn is_self(slf: &Param<'_>) -> bool { + if let PatKind::Binding(.., name, _) = slf.pat.kind { + name.name == kw::SelfLower + } else { + false + } +} + +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { + if_chain! { + if let TyKind::Path(ref qp) = slf.kind; + if let QPath::Resolved(None, ref path) = *qp; + if let Res::SelfTy(..) = path.res; + then { + return true + } + } + false +} + +pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator> { + (0..decl.inputs.len()).map(move |i| &body.params[i]) +} + +/// Checks if a given expression is a match expression expanded from the `?` +/// operator or the `try` macro. +pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + fn is_ok(arm: &Arm<'_>) -> bool { + if_chain! { + if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind; + if match_qpath(path, &paths::RESULT_OK[1..]); + if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; + if let ExprKind::Path(QPath::Resolved(None, ref path)) = arm.body.kind; + if let Res::Local(lid) = path.res; + if lid == hir_id; + then { + return true; + } + } + false + } + + fn is_err(arm: &Arm<'_>) -> bool { + if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { + match_qpath(path, &paths::RESULT_ERR[1..]) + } else { + false + } + } + + if let ExprKind::Match(_, ref arms, ref source) = expr.kind { + // desugared from a `?` operator + if let MatchSource::TryDesugar = *source { + return Some(expr); + } + + if_chain! { + if arms.len() == 2; + if arms[0].guard.is_none(); + if arms[1].guard.is_none(); + if (is_ok(&arms[0]) && is_err(&arms[1])) || + (is_ok(&arms[1]) && is_err(&arms[0])); + then { + return Some(expr); + } + } + } + + None +} + +/// Returns `true` if the lint is allowed in the current context +/// +/// Useful for skipping long running code when it's unnecessary +pub fn is_allowed(cx: &LateContext<'_, '_>, lint: &'static Lint, id: HirId) -> bool { + cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow +} + +pub fn get_arg_name(pat: &Pat<'_>) -> Option { + match pat.kind { + PatKind::Binding(.., ident, None) => Some(ident.name), + PatKind::Ref(ref subpat, _) => get_arg_name(subpat), + _ => None, + } +} + +pub fn int_bits(tcx: TyCtxt<'_>, ity: ast::IntTy) -> u64 { + Integer::from_attr(&tcx, attr::IntType::SignedInt(ity)).size().bits() +} + +#[allow(clippy::cast_possible_wrap)] +/// Turn a constant int byte representation into an i128 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ast::IntTy) -> i128 { + let amt = 128 - int_bits(tcx, ity); + ((u as i128) << amt) >> amt +} + +#[allow(clippy::cast_sign_loss)] +/// clip unused bytes +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ast::IntTy) -> u128 { + let amt = 128 - int_bits(tcx, ity); + ((u as u128) << amt) >> amt +} + +/// clip unused bytes +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ast::UintTy) -> u128 { + let bits = Integer::from_attr(&tcx, attr::IntType::UnsignedInt(ity)).size().bits(); + let amt = 128 - bits; + (u << amt) >> amt +} + +/// Removes block comments from the given `Vec` of lines. +/// +/// # Examples +/// +/// ```rust,ignore +/// without_block_comments(vec!["/*", "foo", "*/"]); +/// // => vec![] +/// +/// without_block_comments(vec!["bar", "/*", "foo", "*/"]); +/// // => vec!["bar"] +/// ``` +pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { + let mut without = vec![]; + + let mut nest_level = 0; + + for line in lines { + if line.contains("/*") { + nest_level += 1; + continue; + } else if line.contains("*/") { + nest_level -= 1; + continue; + } + + if nest_level == 0 { + without.push(line); + } + } + + without +} + +pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool { + let map = &tcx.hir(); + let mut prev_enclosing_node = None; + let mut enclosing_node = node; + while Some(enclosing_node) != prev_enclosing_node { + if is_automatically_derived(map.attrs(enclosing_node)) { + return true; + } + prev_enclosing_node = Some(enclosing_node); + enclosing_node = map.get_parent_item(enclosing_node); + } + false +} + +/// Returns true if ty has `iter` or `iter_mut` methods +pub fn has_iter_method(cx: &LateContext<'_, '_>, probably_ref_ty: Ty<'_>) -> Option<&'static str> { + // FIXME: instead of this hard-coded list, we should check if `::iter` + // exists and has the desired signature. Unfortunately FnCtxt is not exported + // so we can't use its `lookup_method` method. + let into_iter_collections: [&[&str]; 13] = [ + &paths::VEC, + &paths::OPTION, + &paths::RESULT, + &paths::BTREESET, + &paths::BTREEMAP, + &paths::VEC_DEQUE, + &paths::LINKED_LIST, + &paths::BINARY_HEAP, + &paths::HASHSET, + &paths::HASHMAP, + &paths::PATH_BUF, + &paths::PATH, + &paths::RECEIVER, + ]; + + let ty_to_check = match probably_ref_ty.kind { + ty::Ref(_, ty_to_check, _) => ty_to_check, + _ => probably_ref_ty, + }; + + let def_id = match ty_to_check.kind { + ty::Array(..) => return Some("array"), + ty::Slice(..) => return Some("slice"), + ty::Adt(adt, _) => adt.did, + _ => return None, + }; + + for path in &into_iter_collections { + if match_def_path(cx, def_id, path) { + return Some(*path.last().unwrap()); + } + } + None +} + +/// Matches a function call with the given path and returns the arguments. +/// +/// Usage: +/// +/// ```rust,ignore +/// if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC); +/// ``` +pub fn match_function_call<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx Expr<'_>, + path: &[&str], +) -> Option<&'tcx [Expr<'tcx>]> { + if_chain! { + if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id(); + if match_def_path(cx, fun_def_id, path); + then { + return Some(&args) + } + }; + None +} + +/// Checks if `Ty` is normalizable. This function is useful +/// to avoid crashes on `layout_of`. +pub fn is_normalizable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + cx.tcx.infer_ctxt().enter(|infcx| { + let cause = rustc_middle::traits::ObligationCause::dummy(); + infcx.at(&cause, param_env).normalize(&ty).is_ok() + }) +} + +pub fn match_def_path<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, did: DefId, syms: &[&str]) -> bool { + // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path` + // accepts only that. We should probably move to Symbols in Clippy as well. + let syms = syms.iter().map(|p| Symbol::intern(p)).collect::>(); + cx.match_def_path(did, &syms) +} + +/// Returns the list of condition expressions and the list of blocks in a +/// sequence of `if/else`. +/// E.g., this returns `([a, b], [c, d, e])` for the expression +/// `if a { c } else if b { d } else { e }`. +pub fn if_sequence<'tcx>( + mut expr: &'tcx Expr<'tcx>, +) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) { + let mut conds = SmallVec::new(); + let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new(); + + while let Some((ref cond, ref then_expr, ref else_expr)) = higher::if_block(&expr) { + conds.push(&**cond); + if let ExprKind::Block(ref block, _) = then_expr.kind { + blocks.push(block); + } else { + panic!("ExprKind::If node is not an ExprKind::Block"); + } + + if let Some(ref else_expr) = *else_expr { + expr = else_expr; + } else { + break; + } + } + + // final `else {..}` + if !blocks.is_empty() { + if let ExprKind::Block(ref block, _) = expr.kind { + blocks.push(&**block); + } + } + + (conds, blocks) +} + +pub fn parent_node_is_if_expr<'a, 'b>(expr: &Expr<'_>, cx: &LateContext<'a, 'b>) -> bool { + let map = cx.tcx.hir(); + let parent_id = map.get_parent_node(expr.hir_id); + let parent_node = map.get(parent_id); + + match parent_node { + Node::Expr(e) => higher::if_block(&e).is_some(), + Node::Arm(e) => higher::if_block(&e.body).is_some(), + _ => false, + } +} + +// Finds the attribute with the given name, if any +pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> { + attrs + .iter() + .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name)) +} + +// Finds the `#[must_use]` attribute, if any +pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { + attr_by_name(attrs, "must_use") +} + +// Returns whether the type has #[must_use] attribute +pub fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind { + ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), + ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), + ty::Slice(ref ty) + | ty::Array(ref ty, _) + | ty::RawPtr(ty::TypeAndMut { ref ty, .. }) + | ty::Ref(_, ref ty, _) => { + // for the Array case we don't need to care for the len == 0 case + // because we don't want to lint functions returning empty arrays + is_must_use_ty(cx, *ty) + }, + ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), + ty::Opaque(ref def_id, _) => { + for (predicate, _) in cx.tcx.predicates_of(*def_id).predicates { + if let ty::Predicate::Trait(ref poly_trait_predicate, _) = predicate { + if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() { + return true; + } + } + } + false + }, + ty::Dynamic(binder, _) => { + for predicate in binder.skip_binder().iter() { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate { + if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() { + return true; + } + } + } + false + }, + _ => false, + } +} + +// check if expr is calling method or function with #[must_use] attribyte +pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let did = match expr.kind { + ExprKind::Call(ref path, _) => if_chain! { + if let ExprKind::Path(ref qpath) = path.kind; + if let def::Res::Def(_, did) = cx.tables.qpath_res(qpath, path.hir_id); + then { + Some(did) + } else { + None + } + }, + ExprKind::MethodCall(_, _, _) => cx.tables.type_dependent_def_id(expr.hir_id), + _ => None, + }; + + if let Some(did) = did { + must_use_attr(&cx.tcx.get_attrs(did)).is_some() + } else { + false + } +} + +pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { + krate.item.attrs.iter().any(|attr| { + if let ast::AttrKind::Normal(ref attr) = attr.kind { + attr.path == symbol::sym::no_std + } else { + false + } + }) +} + +/// Check if parent of a hir node is a trait implementation block. +/// For example, `f` in +/// ```rust,ignore +/// impl Trait for S { +/// fn f() {} +/// } +/// ``` +pub fn is_trait_impl_item(cx: &LateContext<'_, '_>, hir_id: HirId) -> bool { + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. }) + } else { + false + } +} + +/// Check if it's even possible to satisfy the `where` clause for the item. +/// +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example: +/// +/// ```ignore +/// fn foo() where i32: Iterator { +/// for _ in 2i32 {} +/// } +/// ``` +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_, '_>, did: DefId) -> bool { + use rustc_trait_selection::traits; + let predicates = + cx.tcx + .predicates_of(did) + .predicates + .iter() + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + !traits::normalize_and_test_predicates( + cx.tcx, + traits::elaborate_predicates(cx.tcx, predicates) + .map(|o| o.predicate) + .collect::>(), + ) +} + +pub fn run_lints(cx: &LateContext<'_, '_>, lints: &[&'static Lint], id: HirId) -> bool { + lints.iter().any(|lint| { + matches!( + cx.tcx.lint_level_at_node(lint, id), + (Level::Forbid | Level::Deny | Level::Warn, _) + ) + }) +} + +#[cfg(test)] +mod test { + use super::{trim_multiline, without_block_comments}; + + #[test] + fn test_trim_multiline_single_line() { + assert_eq!("", trim_multiline("".into(), false, None)); + assert_eq!("...", trim_multiline("...".into(), false, None)); + assert_eq!("...", trim_multiline(" ...".into(), false, None)); + assert_eq!("...", trim_multiline("\t...".into(), false, None)); + assert_eq!("...", trim_multiline("\t\t...".into(), false, None)); + } + + #[test] + #[rustfmt::skip] + fn test_trim_multiline_block() { + assert_eq!("\ + if x { + y + } else { + z + }", trim_multiline(" if x { + y + } else { + z + }".into(), false, None)); + assert_eq!("\ + if x { + \ty + } else { + \tz + }", trim_multiline(" if x { + \ty + } else { + \tz + }".into(), false, None)); + } + + #[test] + #[rustfmt::skip] + fn test_trim_multiline_empty_line() { + assert_eq!("\ + if x { + y + + } else { + z + }", trim_multiline(" if x { + y + + } else { + z + }".into(), false, None)); + } + + #[test] + fn test_without_block_comments_lines_without_block_comments() { + let result = without_block_comments(vec!["/*", "", "*/"]); + println!("result: {:?}", result); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); + assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]); + + let result = without_block_comments(vec!["/* rust", "", "*/"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* one-line comment */"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["foo", "bar", "baz"]); + assert_eq!(result, vec!["foo", "bar", "baz"]); + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs new file mode 100644 index 0000000000000..99413153d49bb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs @@ -0,0 +1,227 @@ +use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; + +#[derive(Debug, PartialEq)] +pub enum Radix { + Binary, + Octal, + Decimal, + Hexadecimal, +} + +impl Radix { + /// Returns a reasonable digit group size for this radix. + #[must_use] + fn suggest_grouping(&self) -> usize { + match *self { + Self::Binary | Self::Hexadecimal => 4, + Self::Octal | Self::Decimal => 3, + } + } +} + +/// A helper method to format numeric literals with digit grouping. +/// `lit` must be a valid numeric literal without suffix. +pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String { + NumericLiteral::new(lit, type_suffix, float).format() +} + +#[derive(Debug)] +pub struct NumericLiteral<'a> { + /// Which radix the literal was represented in. + pub radix: Radix, + /// The radix prefix, if present. + pub prefix: Option<&'a str>, + + /// The integer part of the number. + pub integer: &'a str, + /// The fraction part of the number. + pub fraction: Option<&'a str>, + /// The character used as exponent seperator (b'e' or b'E') and the exponent part. + pub exponent: Option<(char, &'a str)>, + + /// The type suffix, including preceding underscore if present. + pub suffix: Option<&'a str>, +} + +impl<'a> NumericLiteral<'a> { + pub fn from_lit(src: &'a str, lit: &Lit) -> Option> { + NumericLiteral::from_lit_kind(src, &lit.kind) + } + + pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { + if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) { + let (unsuffixed, suffix) = split_suffix(&src, lit_kind); + let float = if let LitKind::Float(..) = lit_kind { true } else { false }; + Some(NumericLiteral::new(unsuffixed, suffix, float)) + } else { + None + } + } + + #[must_use] + pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + // Determine delimiter for radix prefix, if present, and radix. + let radix = if lit.starts_with("0x") { + Radix::Hexadecimal + } else if lit.starts_with("0b") { + Radix::Binary + } else if lit.starts_with("0o") { + Radix::Octal + } else { + Radix::Decimal + }; + + // Grab part of the literal after prefix, if present. + let (prefix, mut sans_prefix) = if let Radix::Decimal = radix { + (None, lit) + } else { + let (p, s) = lit.split_at(2); + (Some(p), s) + }; + + if suffix.is_some() && sans_prefix.ends_with('_') { + // The '_' before the suffix isn't part of the digits + sans_prefix = &sans_prefix[..sans_prefix.len() - 1]; + } + + let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float); + + Self { + radix, + prefix, + integer, + fraction, + exponent, + suffix, + } + } + + pub fn is_decimal(&self) -> bool { + self.radix == Radix::Decimal + } + + pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { + let mut integer = digits; + let mut fraction = None; + let mut exponent = None; + + if float { + for (i, c) in digits.char_indices() { + match c { + '.' => { + integer = &digits[..i]; + fraction = Some(&digits[i + 1..]); + }, + 'e' | 'E' => { + if integer.len() > i { + integer = &digits[..i]; + } else { + fraction = Some(&digits[integer.len() + 1..i]); + }; + exponent = Some((c, &digits[i + 1..])); + break; + }, + _ => {}, + } + } + } + + (integer, fraction, exponent) + } + + /// Returns literal formatted in a sensible way. + pub fn format(&self) -> String { + let mut output = String::new(); + + if let Some(prefix) = self.prefix { + output.push_str(prefix); + } + + let group_size = self.radix.suggest_grouping(); + + Self::group_digits( + &mut output, + self.integer, + group_size, + true, + self.radix == Radix::Hexadecimal, + ); + + if let Some(fraction) = self.fraction { + output.push('.'); + Self::group_digits(&mut output, fraction, group_size, false, false); + } + + if let Some((separator, exponent)) = self.exponent { + output.push(separator); + Self::group_digits(&mut output, exponent, group_size, true, false); + } + + if let Some(suffix) = self.suffix { + output.push('_'); + output.push_str(suffix); + } + + output + } + + pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) { + debug_assert!(group_size > 0); + + let mut digits = input.chars().filter(|&c| c != '_'); + + let first_group_size; + + if partial_group_first { + first_group_size = (digits.clone().count() - 1) % group_size + 1; + if pad { + for _ in 0..group_size - first_group_size { + output.push('0'); + } + } + } else { + first_group_size = group_size; + } + + for _ in 0..first_group_size { + if let Some(digit) = digits.next() { + output.push(digit); + } + } + + for (c, i) in digits.zip((0..group_size).cycle()) { + if i == 0 { + output.push('_'); + } + output.push(c); + } + } +} + +fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) { + debug_assert!(lit_kind.is_numeric()); + if let Some(suffix_length) = lit_suffix_length(lit_kind) { + let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length); + (unsuffixed, Some(suffix)) + } else { + (src, None) + } +} + +fn lit_suffix_length(lit_kind: &LitKind) -> Option { + debug_assert!(lit_kind.is_numeric()); + let suffix = match lit_kind { + LitKind::Int(_, int_lit_kind) => match int_lit_kind { + LitIntType::Signed(int_ty) => Some(int_ty.name_str()), + LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()), + LitIntType::Unsuffixed => None, + }, + LitKind::Float(_, float_lit_kind) => match float_lit_kind { + LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()), + LitFloatType::Unsuffixed => None, + }, + _ => None, + }; + + suffix.map(str::len) +} diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs new file mode 100644 index 0000000000000..b3ad2ad9d9987 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -0,0 +1,140 @@ +//! This module contains paths to types and functions Clippy needs to know +//! about. +//! +//! Whenever possible, please consider diagnostic items over hardcoded paths. +//! See for more information. + +pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; +pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; +pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; +pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; +pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"]; +pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; +pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; +pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"]; +pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; +pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"]; +pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; +pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"]; +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; +pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; +pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; +pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; +pub const CSTRING: [&str; 4] = ["std", "ffi", "c_str", "CString"]; +pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; +pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; +pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; +pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; +pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; +pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"]; +pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; +pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; +pub const DROP: [&str; 3] = ["core", "mem", "drop"]; +pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"]; +pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; +pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; +pub const EXIT: [&str; 3] = ["std", "process", "exit"]; +pub const FILE: [&str; 3] = ["std", "fs", "File"]; +pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; +pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; +pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; +pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; +pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; +pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; +pub const HASH: [&str; 2] = ["hash", "Hash"]; +pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; +pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; +pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; +pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; +pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; +pub const INTO: [&str; 3] = ["core", "convert", "Into"]; +pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; +pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; +pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; +pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; +pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"]; +pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; +pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"]; +pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; +pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; +pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; +pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; +pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; +pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; +pub const OPS_MODULE: [&str; 2] = ["core", "ops"]; +pub const OPTION: [&str; 3] = ["core", "option", "Option"]; +pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"]; +pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; +pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; +pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"]; +pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; +pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; +pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; +pub const PATH: [&str; 3] = ["std", "path", "Path"]; +pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; +pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; +pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; +pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; +pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; +pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; +pub const RANGE: [&str; 3] = ["core", "ops", "Range"]; +pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; +pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"]; +pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"]; +pub const RANGE_FULL: [&str; 4] = ["core", "ops", "range", "RangeFull"]; +pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"]; +pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"]; +pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"]; +pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"]; +pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"]; +pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"]; +pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"]; +pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"]; +pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; +pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; +pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; +pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"]; +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; +pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; +pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; +pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"]; +pub const RESULT: [&str; 3] = ["core", "result", "Result"]; +pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; +pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; +pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; +pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; +pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"]; +pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; +pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; +pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"]; +pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; +pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; +pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; +pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; +pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; +pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; +pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; +pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; +pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; +pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; +pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; +pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; +pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; +pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; +pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; diff --git a/src/tools/clippy/clippy_lints/src/utils/ptr.rs b/src/tools/clippy/clippy_lints/src/utils/ptr.rs new file mode 100644 index 0000000000000..fb6bd5e815859 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/ptr.rs @@ -0,0 +1,87 @@ +use crate::utils::{get_pat_name, match_var, snippet}; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +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)], +) -> Option)>> { + if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) { + get_binding_name(&body.params[idx]).map_or_else( + || Some(vec![]), + |name| extract_clone_suggestions(cx, name, replacements, body), + ) + } else { + Some(vec![]) + } +} + +fn extract_clone_suggestions<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + name: Symbol, + replace: &[(&'static str, &'static str)], + body: &'tcx Body<'_>, +) -> Option)>> { + let mut visitor = PtrCloneVisitor { + cx, + name, + replace, + spans: vec![], + abort: false, + }; + visitor.visit_body(body); + if visitor.abort { + None + } else { + Some(visitor.spans) + } +} + +struct PtrCloneVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + name: Symbol, + replace: &'a [(&'static str, &'static str)], + spans: Vec<(Span, Cow<'static, str>)>, + abort: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.abort { + return; + } + if let ExprKind::MethodCall(ref seg, _, ref args) = expr.kind { + if args.len() == 1 && match_var(&args[0], self.name) { + if seg.ident.name.as_str() == "capacity" { + self.abort = true; + return; + } + for &(fn_name, suffix) in self.replace { + if seg.ident.name.as_str() == fn_name { + self.spans + .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix)); + return; + } + } + } + return; + } + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn get_binding_name(arg: &Param<'_>) -> Option { + get_pat_name(&arg.pat) +} diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs new file mode 100644 index 0000000000000..4ebe2e2852fb4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs @@ -0,0 +1,630 @@ +//! Contains utility functions to generate suggestions. +#![deny(clippy::missing_docs_in_private_items)] + +use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite}; +use rustc_ast::util::parser::AssocOp; +use rustc_ast::{ast, token}; +use rustc_ast_pretty::pprust::token_kind_to_string; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{EarlyContext, LateContext, LintContext}; +use rustc_span::source_map::{CharPos, Span}; +use rustc_span::{BytePos, Pos}; +use std::borrow::Cow; +use std::convert::TryInto; +use std::fmt::Display; + +/// A helper type to build suggestion correctly handling parenthesis. +pub enum Sugg<'a> { + /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. + NonParen(Cow<'a, str>), + /// An expression that does not fit in other variants. + MaybeParen(Cow<'a, str>), + /// A binary operator expression, including `as`-casts and explicit type + /// coercion. + BinOp(AssocOp, Cow<'a, str>), +} + +/// Literal constant `1`, for convenience. +pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1")); + +impl Display for Sugg<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match *self { + Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f), + } + } +} + +#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method +impl<'a> Sugg<'a> { + /// Prepare a suggestion from an expression. + pub fn hir_opt(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> Option { + snippet_opt(cx, expr.span).map(|snippet| { + let snippet = Cow::Owned(snippet); + Self::hir_from_snippet(cx, expr, snippet) + }) + } + + /// Convenience function around `hir_opt` for suggestions with a default + /// text. + pub fn hir(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { + Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default))) + } + + /// Same as `hir`, but it adapts the applicability level by following rules: + /// + /// - Applicability level `Unspecified` will never be changed. + /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. + /// - If the default value is used and the applicability level is `MachineApplicable`, change it + /// to + /// `HasPlaceholders` + pub fn hir_with_applicability( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + default: &'a str, + applicability: &mut Applicability, + ) -> Self { + if *applicability != Applicability::Unspecified && expr.span.from_expansion() { + *applicability = Applicability::MaybeIncorrect; + } + Self::hir_opt(cx, expr).unwrap_or_else(|| { + if *applicability == Applicability::MachineApplicable { + *applicability = Applicability::HasPlaceholders; + } + Sugg::NonParen(Cow::Borrowed(default)) + }) + } + + /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro. + pub fn hir_with_macro_callsite(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { + let snippet = snippet_with_macro_callsite(cx, expr.span, default); + + Self::hir_from_snippet(cx, expr, snippet) + } + + /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` + /// function variants of `Sugg`, since these use different snippet functions. + fn hir_from_snippet(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { + if let Some(range) = higher::range(cx, expr) { + let op = match range.limits { + ast::RangeLimits::HalfOpen => AssocOp::DotDot, + ast::RangeLimits::Closed => AssocOp::DotDotEq, + }; + return Sugg::BinOp(op, snippet); + } + + match expr.kind { + hir::ExprKind::AddrOf(..) + | hir::ExprKind::Box(..) + | hir::ExprKind::Closure(..) + | hir::ExprKind::Unary(..) + | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet), + hir::ExprKind::Continue(..) + | hir::ExprKind::Yield(..) + | hir::ExprKind::Array(..) + | hir::ExprKind::Block(..) + | hir::ExprKind::Break(..) + | hir::ExprKind::Call(..) + | hir::ExprKind::Field(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::LlvmInlineAsm(..) + | hir::ExprKind::Lit(..) + | hir::ExprKind::Loop(..) + | hir::ExprKind::MethodCall(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Repeat(..) + | hir::ExprKind::Ret(..) + | hir::ExprKind::Struct(..) + | hir::ExprKind::Tup(..) + | hir::ExprKind::DropTemps(_) + | hir::ExprKind::Err => Sugg::NonParen(snippet), + hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet), + hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet), + hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet), + hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet), + hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet), + } + } + + /// Prepare a suggestion from an expression. + pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { + use rustc_ast::ast::RangeLimits; + + let snippet = snippet(cx, expr.span, default); + + match expr.kind { + ast::ExprKind::AddrOf(..) + | ast::ExprKind::Box(..) + | ast::ExprKind::Closure(..) + | ast::ExprKind::If(..) + | ast::ExprKind::Let(..) + | ast::ExprKind::Unary(..) + | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet), + ast::ExprKind::Async(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::Break(..) + | ast::ExprKind::Call(..) + | ast::ExprKind::Continue(..) + | ast::ExprKind::Yield(..) + | ast::ExprKind::Field(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::Index(..) + | ast::ExprKind::InlineAsm(..) + | ast::ExprKind::LlvmInlineAsm(..) + | ast::ExprKind::Lit(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::MacCall(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::Paren(..) + | ast::ExprKind::Path(..) + | ast::ExprKind::Repeat(..) + | ast::ExprKind::Ret(..) + | ast::ExprKind::Struct(..) + | ast::ExprKind::Try(..) + | ast::ExprKind::TryBlock(..) + | ast::ExprKind::Tup(..) + | ast::ExprKind::Array(..) + | ast::ExprKind::While(..) + | ast::ExprKind::Await(..) + | ast::ExprKind::Err => Sugg::NonParen(snippet), + ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet), + ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet), + ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet), + ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet), + ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet), + ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet), + ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet), + } + } + + /// Convenience method to create the ` && ` suggestion. + pub fn and(self, rhs: &Self) -> Sugg<'static> { + make_binop(ast::BinOpKind::And, &self, rhs) + } + + /// Convenience method to create the ` & ` suggestion. + pub fn bit_and(self, rhs: &Self) -> Sugg<'static> { + make_binop(ast::BinOpKind::BitAnd, &self, rhs) + } + + /// Convenience method to create the ` as ` suggestion. + pub fn as_ty(self, rhs: R) -> Sugg<'static> { + make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into())) + } + + /// Convenience method to create the `&` suggestion. + pub fn addr(self) -> Sugg<'static> { + make_unop("&", self) + } + + /// Convenience method to create the `&mut ` suggestion. + pub fn mut_addr(self) -> Sugg<'static> { + make_unop("&mut ", self) + } + + /// Convenience method to create the `*` suggestion. + pub fn deref(self) -> Sugg<'static> { + make_unop("*", self) + } + + /// Convenience method to create the `&*` suggestion. Currently this + /// is needed because `sugg.deref().addr()` produces an unnecessary set of + /// parentheses around the deref. + pub fn addr_deref(self) -> Sugg<'static> { + make_unop("&*", self) + } + + /// Convenience method to create the `&mut *` suggestion. Currently + /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary + /// set of parentheses around the deref. + pub fn mut_addr_deref(self) -> Sugg<'static> { + make_unop("&mut *", self) + } + + /// Convenience method to transform suggestion into a return call + pub fn make_return(self) -> Sugg<'static> { + Sugg::NonParen(Cow::Owned(format!("return {}", self))) + } + + /// Convenience method to transform suggestion into a block + /// where the suggestion is a trailing expression + pub fn blockify(self) -> Sugg<'static> { + Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self))) + } + + /// Convenience method to create the `..` or `...` + /// suggestion. + #[allow(dead_code)] + pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> { + match limit { + ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end), + ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end), + } + } + + /// Adds parenthesis to any expression that might need them. Suitable to the + /// `self` argument of a method call + /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`). + pub fn maybe_par(self) -> Self { + match self { + Sugg::NonParen(..) => self, + // `(x)` and `(x).y()` both don't need additional parens. + Sugg::MaybeParen(sugg) => { + if sugg.starts_with('(') && sugg.ends_with(')') { + Sugg::MaybeParen(sugg) + } else { + Sugg::NonParen(format!("({})", sugg).into()) + } + }, + Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()), + } + } +} + +impl<'a, 'b> std::ops::Add> for Sugg<'a> { + type Output = Sugg<'static>; + fn add(self, rhs: Sugg<'b>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Add, &self, &rhs) + } +} + +impl<'a, 'b> std::ops::Sub> for Sugg<'a> { + type Output = Sugg<'static>; + fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Sub, &self, &rhs) + } +} + +impl<'a> std::ops::Not for Sugg<'a> { + type Output = Sugg<'static>; + fn not(self) -> Sugg<'static> { + make_unop("!", self) + } +} + +/// Helper type to display either `foo` or `(foo)`. +struct ParenHelper { + /// `true` if parentheses are needed. + paren: bool, + /// The main thing to display. + wrapped: T, +} + +impl ParenHelper { + /// Builds a `ParenHelper`. + fn new(paren: bool, wrapped: T) -> Self { + Self { paren, wrapped } + } +} + +impl Display for ParenHelper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + if self.paren { + write!(f, "({})", self.wrapped) + } else { + self.wrapped.fmt(f) + } + } +} + +/// Builds the string for `` adding parenthesis when necessary. +/// +/// For convenience, the operator is taken as a string because all unary +/// operators have the same +/// precedence. +pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { + Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into()) +} + +/// Builds the string for ` ` adding parenthesis when necessary. +/// +/// Precedence of shift operator relative to other arithmetic operation is +/// often confusing so +/// parenthesis will always be added for a mix of these. +pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> { + /// Returns `true` if the operator is a shift operator `<<` or `>>`. + fn is_shift(op: &AssocOp) -> bool { + matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight) + } + + /// Returns `true` if the operator is a arithmetic operator + /// (i.e., `+`, `-`, `*`, `/`, `%`). + fn is_arith(op: &AssocOp) -> bool { + matches!( + *op, + AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus + ) + } + + /// Returns `true` if the operator `op` needs parenthesis with the operator + /// `other` in the direction `dir`. + fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool { + other.precedence() < op.precedence() + || (other.precedence() == op.precedence() + && ((op != other && associativity(op) != dir) + || (op == other && associativity(op) != Associativity::Both))) + || is_shift(op) && is_arith(other) + || is_shift(other) && is_arith(op) + } + + let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs { + needs_paren(&op, lop, Associativity::Left) + } else { + false + }; + + let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs { + needs_paren(&op, rop, Associativity::Right) + } else { + false + }; + + let lhs = ParenHelper::new(lhs_paren, lhs); + let rhs = ParenHelper::new(rhs_paren, rhs); + let sugg = match op { + AssocOp::Add + | AssocOp::BitAnd + | AssocOp::BitOr + | AssocOp::BitXor + | AssocOp::Divide + | AssocOp::Equal + | AssocOp::Greater + | AssocOp::GreaterEqual + | AssocOp::LAnd + | AssocOp::LOr + | AssocOp::Less + | AssocOp::LessEqual + | AssocOp::Modulus + | AssocOp::Multiply + | AssocOp::NotEqual + | AssocOp::ShiftLeft + | AssocOp::ShiftRight + | AssocOp::Subtract => format!( + "{} {} {}", + lhs, + op.to_ast_binop().expect("Those are AST ops").to_string(), + rhs + ), + AssocOp::Assign => format!("{} = {}", lhs, rhs), + AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs), + AssocOp::As => format!("{} as {}", lhs, rhs), + AssocOp::DotDot => format!("{}..{}", lhs, rhs), + AssocOp::DotDotEq => format!("{}..={}", lhs, rhs), + AssocOp::Colon => format!("{}: {}", lhs, rhs), + }; + + Sugg::BinOp(op, sugg.into()) +} + +/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`. +pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> { + make_assoc(AssocOp::from_ast_binop(op), lhs, rhs) +} + +#[derive(PartialEq, Eq, Clone, Copy)] +/// Operator associativity. +enum Associativity { + /// The operator is both left-associative and right-associative. + Both, + /// The operator is left-associative. + Left, + /// The operator is not associative. + None, + /// The operator is right-associative. + Right, +} + +/// Returns the associativity/fixity of an operator. The difference with +/// `AssocOp::fixity` is that an operator can be both left and right associative +/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`. +/// +/// Chained `as` and explicit `:` type coercion never need inner parenthesis so +/// they are considered +/// associative. +#[must_use] +fn associativity(op: &AssocOp) -> Associativity { + use rustc_ast::util::parser::AssocOp::{ + Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater, + GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract, + }; + + match *op { + Assign | AssignOp(_) => Associativity::Right, + Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both, + Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight + | Subtract => Associativity::Left, + DotDot | DotDotEq => Associativity::None, + } +} + +/// Converts a `hir::BinOp` to the corresponding assigning binary operator. +fn hirbinop2assignop(op: hir::BinOp) -> AssocOp { + use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star}; + + AssocOp::AssignOp(match op.node { + hir::BinOpKind::Add => Plus, + hir::BinOpKind::BitAnd => And, + hir::BinOpKind::BitOr => Or, + hir::BinOpKind::BitXor => Caret, + hir::BinOpKind::Div => Slash, + hir::BinOpKind::Mul => Star, + hir::BinOpKind::Rem => Percent, + hir::BinOpKind::Shl => Shl, + hir::BinOpKind::Shr => Shr, + hir::BinOpKind::Sub => Minus, + + hir::BinOpKind::And + | hir::BinOpKind::Eq + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt + | hir::BinOpKind::Le + | hir::BinOpKind::Lt + | hir::BinOpKind::Ne + | hir::BinOpKind::Or => panic!("This operator does not exist"), + }) +} + +/// Converts an `ast::BinOp` to the corresponding assigning binary operator. +fn astbinop2assignop(op: ast::BinOp) -> AssocOp { + use rustc_ast::ast::BinOpKind::{ + Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, + }; + use rustc_ast::token::BinOpToken; + + AssocOp::AssignOp(match op.node { + Add => BinOpToken::Plus, + BitAnd => BinOpToken::And, + BitOr => BinOpToken::Or, + BitXor => BinOpToken::Caret, + Div => BinOpToken::Slash, + Mul => BinOpToken::Star, + Rem => BinOpToken::Percent, + Shl => BinOpToken::Shl, + Shr => BinOpToken::Shr, + Sub => BinOpToken::Minus, + And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"), + }) +} + +/// Returns the indentation before `span` if there are nothing but `[ \t]` +/// before it on its line. +fn indentation(cx: &T, span: Span) -> Option { + let lo = cx.sess().source_map().lookup_char_pos(span.lo()); + if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) { + if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { + // We can mix char and byte positions here because we only consider `[ \t]`. + if lo.col == CharPos(pos) { + Some(line[..pos].into()) + } else { + None + } + } else { + None + } + } else { + None + } +} + +/// Convenience extension trait for `DiagnosticBuilder`. +pub trait DiagnosticBuilderExt<'a, T: LintContext> { + /// Suggests to add an attribute to an item. + /// + /// Correctly handles indentation of the attribute and item. + /// + /// # Example + /// + /// ```rust,ignore + /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]"); + /// ``` + fn suggest_item_with_attr( + &mut self, + cx: &T, + item: Span, + msg: &str, + attr: &D, + applicability: Applicability, + ); + + /// Suggest to add an item before another. + /// + /// The item should not be indented (expect for inner indentation). + /// + /// # Example + /// + /// ```rust,ignore + /// diag.suggest_prepend_item(cx, item, + /// "fn foo() { + /// bar(); + /// }"); + /// ``` + fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability); + + /// Suggest to completely remove an item. + /// + /// This will remove an item and all following whitespace until the next non-whitespace + /// character. This should work correctly if item is on the same indentation level as the + /// following item. + /// + /// # Example + /// + /// ```rust,ignore + /// diag.suggest_remove_item(cx, item, "remove this") + /// ``` + fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability); +} + +impl<'a, 'b, 'c, T: LintContext> DiagnosticBuilderExt<'c, T> for rustc_errors::DiagnosticBuilder<'b> { + fn suggest_item_with_attr( + &mut self, + cx: &T, + item: Span, + msg: &str, + attr: &D, + applicability: Applicability, + ) { + if let Some(indent) = indentation(cx, item) { + let span = item.with_hi(item.lo()); + + self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability); + } + } + + fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) { + if let Some(indent) = indentation(cx, item) { + let span = item.with_hi(item.lo()); + + let mut first = true; + let new_item = new_item + .lines() + .map(|l| { + if first { + first = false; + format!("{}\n", l) + } else { + format!("{}{}\n", indent, l) + } + }) + .collect::(); + + self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability); + } + } + + fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) { + let mut remove_span = item; + let hi = cx.sess().source_map().next_point(remove_span).hi(); + let fmpos = cx.sess().source_map().lookup_byte_offset(hi); + + if let Some(ref src) = fmpos.sf.src { + let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n'); + + if let Some(non_whitespace_offset) = non_whitespace_offset { + remove_span = remove_span + .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large"))) + } + } + + self.span_suggestion(remove_span, msg, String::new(), applicability); + } +} + +#[cfg(test)] +mod test { + use super::Sugg; + use std::borrow::Cow; + + const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()")); + + #[test] + fn make_return_transform_sugg_into_a_return_call() { + assert_eq!("return function_call()", SUGGESTION.make_return().to_string()); + } + + #[test] + fn blockify_transforms_sugg_into_a_block() { + assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string()); + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/sym.rs b/src/tools/clippy/clippy_lints/src/utils/sym.rs new file mode 100644 index 0000000000000..273288c3d52c5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/sym.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} diff --git a/src/tools/clippy/clippy_lints/src/utils/usage.rs b/src/tools/clippy/clippy_lints/src/utils/usage.rs new file mode 100644 index 0000000000000..904d948ad29ed --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/usage.rs @@ -0,0 +1,107 @@ +use crate::utils::match_var; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, HirId, Path}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; + +/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. +pub fn mutated_variables<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &'a LateContext<'a, 'tcx>) -> Option> { + let mut delegate = MutVarsDelegate { + used_mutably: FxHashSet::default(), + skip: false, + }; + let def_id = expr.hir_id.owner.to_def_id(); + cx.tcx.infer_ctxt().enter(|infcx| { + ExprUseVisitor::new(&mut delegate, &infcx, def_id.expect_local(), cx.param_env, cx.tables).walk_expr(expr); + }); + + if delegate.skip { + return None; + } + Some(delegate.used_mutably) +} + +pub fn is_potentially_mutated<'a, 'tcx>( + variable: &'tcx Path<'_>, + expr: &'tcx Expr<'_>, + cx: &'a LateContext<'a, 'tcx>, +) -> bool { + if let Res::Local(id) = variable.res { + mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id)) + } else { + true + } +} + +struct MutVarsDelegate { + used_mutably: FxHashSet, + skip: bool, +} + +impl<'tcx> MutVarsDelegate { + #[allow(clippy::similar_names)] + fn update(&mut self, cat: &Place<'tcx>) { + match cat.base { + PlaceBase::Local(id) => { + self.used_mutably.insert(id); + }, + PlaceBase::Upvar(_) => { + //FIXME: This causes false negatives. We can't get the `NodeId` from + //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the + //`while`-body, not just the ones in the condition. + self.skip = true + }, + _ => {}, + } + } +} + +impl<'tcx> Delegate<'tcx> for MutVarsDelegate { + fn consume(&mut self, _: &Place<'tcx>, _: ConsumeMode) {} + + fn borrow(&mut self, cmt: &Place<'tcx>, bk: ty::BorrowKind) { + if let ty::BorrowKind::MutBorrow = bk { + self.update(&cmt) + } + } + + fn mutate(&mut self, cmt: &Place<'tcx>) { + self.update(&cmt) + } +} + +pub struct UsedVisitor { + pub var: Symbol, // var to look for + pub used: bool, // has the var been used otherwise? +} + +impl<'tcx> Visitor<'tcx> for UsedVisitor { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if match_var(expr, self.var) { + self.used = true; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool { + let mut visitor = UsedVisitor { + var: ident.name, + used: false, + }; + walk_expr(&mut visitor, body); + !visitor.used +} diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs new file mode 100644 index 0000000000000..1174f42157749 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -0,0 +1,106 @@ +use crate::consts::constant; +use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would + /// be possible. + /// + /// **Why is this bad?** This is less efficient. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// foo(&vec![1, 2]) + /// ``` + pub USELESS_VEC, + perf, + "useless `vec!`" +} + +declare_lint_pass!(UselessVec => [USELESS_VEC]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessVec { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + // search for `&vec![_]` expressions where the adjusted type is `&[_]` + if_chain! { + if let ty::Ref(_, ty, _) = cx.tables.expr_ty_adjusted(expr).kind; + if let ty::Slice(..) = ty.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; + if let Some(vec_args) = higher::vec_macro(cx, addressee); + then { + check_vec_macro(cx, &vec_args, expr.span); + } + } + + // search for `for _ in vec![…]` + if_chain! { + if let Some((_, arg, _)) = higher::for_loop(expr); + if let Some(vec_args) = higher::vec_macro(cx, arg); + if is_copy(cx, vec_type(cx.tables.expr_ty_adjusted(arg))); + then { + // report the error around the `vec!` not inside `:` + let span = arg.span + .ctxt() + .outer_expn_data() + .call_site + .ctxt() + .outer_expn_data() + .call_site; + check_vec_macro(cx, &vec_args, span); + } + } + } +} + +fn check_vec_macro<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + let mut applicability = Applicability::MachineApplicable; + let snippet = match *vec_args { + higher::VecArgs::Repeat(elem, len) => { + if constant(cx, cx.tables, len).is_some() { + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Some(last) = args.iter().last() { + let span = args[0].span.to(last.span); + + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + } else { + "&[]".into() + } + }, + }; + + span_lint_and_sugg( + cx, + USELESS_VEC, + span, + "useless use of `vec!`", + "you can use a slice directly", + snippet, + applicability, + ); +} + +/// Returns the item type of the vector (i.e., the `T` in `Vec`). +fn vec_type(ty: Ty<'_>) -> Ty<'_> { + if let ty::Adt(_, substs) = ty.kind { + substs.type_at(0) + } else { + panic!("The type of `vec!` is a not a struct?"); + } +} diff --git a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs new file mode 100644 index 0000000000000..4d8d4438d881d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs @@ -0,0 +1,85 @@ +use crate::utils::{match_type, paths, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for use of File::read_to_end and File::read_to_string. + /// + /// **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. + /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # use std::io::Read; + /// # use std::fs::File; + /// let mut f = File::open("foo.txt").unwrap(); + /// let mut bytes = Vec::new(); + /// f.read_to_end(&mut bytes).unwrap(); + /// ``` + /// Can be written more concisely as + /// ```rust,no_run + /// # use std::fs; + /// let mut bytes = fs::read("foo.txt").unwrap(); + /// ``` + pub VERBOSE_FILE_READS, + restriction, + "use of `File::read_to_end` or `File::read_to_string`" +} + +declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VerboseFileReads { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) { + if is_file_read_to_end(cx, expr) { + span_lint_and_help( + cx, + VERBOSE_FILE_READS, + expr.span, + "use of `File::read_to_end`", + None, + "consider using `fs::read` instead", + ); + } else if is_file_read_to_string(cx, expr) { + span_lint_and_help( + cx, + VERBOSE_FILE_READS, + expr.span, + "use of `File::read_to_string`", + None, + "consider using `fs::read_to_string` instead", + ) + } + } +} + +fn is_file_read_to_end<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + if_chain! { + if let ExprKind::MethodCall(method_name, _, exprs) = expr.kind; + if method_name.ident.as_str() == "read_to_end"; + if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind; + let ty = cx.tables.expr_ty(&exprs[0]); + if match_type(cx, ty, &paths::FILE); + then { + return true + } + } + false +} + +fn is_file_read_to_string<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + if_chain! { + if let ExprKind::MethodCall(method_name, _, exprs) = expr.kind; + if method_name.ident.as_str() == "read_to_string"; + if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind; + let ty = cx.tables.expr_ty(&exprs[0]); + if match_type(cx, ty, &paths::FILE); + then { + return true + } + } + false +} diff --git a/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs b/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs new file mode 100644 index 0000000000000..d8d48eb15358d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs @@ -0,0 +1,62 @@ +use crate::utils::{run_lints, span_lint}; +use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::DUMMY_SP; + +use if_chain::if_chain; + +declare_clippy_lint! { + /// **What it does:** Checks for wildcard dependencies in the `Cargo.toml`. + /// + /// **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html), + /// it is highly unlikely that you work with any possible version of your dependency, + /// and wildcard dependencies would cause unnecessary breakage in the ecosystem. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```toml + /// [dependencies] + /// regex = "*" + /// ``` + pub WILDCARD_DEPENDENCIES, + cargo, + "wildcard dependencies being used" +} + +declare_lint_pass!(WildcardDependencies => [WILDCARD_DEPENDENCIES]); + +impl LateLintPass<'_, '_> for WildcardDependencies { + fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { + if !run_lints(cx, &[WILDCARD_DEPENDENCIES], CRATE_HIR_ID) { + return; + } + + let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() { + metadata + } else { + span_lint(cx, WILDCARD_DEPENDENCIES, DUMMY_SP, "could not read cargo metadata"); + return; + }; + + for dep in &metadata.packages[0].dependencies { + // VersionReq::any() does not work + if_chain! { + if let Ok(wildcard_ver) = semver::VersionReq::parse("*"); + if let Some(ref source) = dep.source; + if !source.starts_with("git"); + if dep.req == wildcard_ver; + then { + span_lint( + cx, + WILDCARD_DEPENDENCIES, + DUMMY_SP, + &format!("wildcard dependency for `{}`", dep.name), + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs new file mode 100644 index 0000000000000..32d9a45c37d78 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -0,0 +1,208 @@ +use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{ + def::{DefKind, Res}, + Item, ItemKind, PathSegment, UseKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::BytePos; + +declare_clippy_lint! { + /// **What it does:** Checks for `use Enum::*`. + /// + /// **Why is this bad?** It is usually better style to use the prefixed name of + /// an enumeration variant, rather than importing variants. + /// + /// **Known problems:** Old-style enumerations that prefix the variants are + /// still around. + /// + /// **Example:** + /// ```rust + /// use std::cmp::Ordering::*; + /// ``` + pub ENUM_GLOB_USE, + pedantic, + "use items that import all variants of an enum" +} + +declare_clippy_lint! { + /// **What it does:** Checks for wildcard imports `use _::*`. + /// + /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if + /// you try to import something through a wildcard, that already has been imported by name from + /// a different source: + /// + /// ```rust,ignore + /// use crate1::foo; // Imports a function named foo + /// use crate2::*; // Has a function named foo + /// + /// foo(); // Calls crate1::foo + /// ``` + /// + /// This can lead to confusing error messages at best and to unexpected behavior at worst. + /// + /// **Exceptions:** + /// + /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) + /// provide modules named "prelude" specifically designed for wildcard import. + /// + /// `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. + /// + /// **Known problems:** If macros are imported through the wildcard, this macro is not included + /// by the suggestion and has to be added by hand. + /// + /// Applying the suggestion when explicit imports of the things imported with a glob import + /// exist, may result in `unused_imports` warnings. + /// + /// **Example:** + /// + /// Bad: + /// ```rust,ignore + /// use crate1::*; + /// + /// foo(); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// use crate1::foo; + /// + /// foo(); + /// ``` + pub WILDCARD_IMPORTS, + pedantic, + "lint `use _::*` statements" +} + +#[derive(Default)] +pub struct WildcardImports { + warn_on_all: bool, + test_modules_deep: u32, +} + +impl WildcardImports { + pub fn new(warn_on_all: bool) -> Self { + Self { + warn_on_all, + test_modules_deep: 0, + } + } +} + +impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); + +impl LateLintPass<'_, '_> for WildcardImports { + fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { + if is_test_module_or_function(item) { + self.test_modules_deep = self.test_modules_deep.saturating_add(1); + } + if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { + return; + } + if_chain! { + if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; + if self.warn_on_all || !self.check_exceptions(item, use_path.segments); + let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); + if !used_imports.is_empty(); // Already handled by `unused_imports` + then { + let mut applicability = Applicability::MachineApplicable; + let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); + let (span, braced_glob) = if import_source_snippet.is_empty() { + // This is a `_::{_, *}` import + // In this case `use_path.span` is empty and ends directly in front of the `*`, + // so we need to extend it by one byte. + ( + use_path.span.with_hi(use_path.span.hi() + BytePos(1)), + true, + ) + } else { + // In this case, the `use_path.span` ends right before the `::*`, so we need to + // extend it up to the `*`. Since it is hard to find the `*` in weird + // formattings like `use _ :: *;`, we extend it up to, but not including the + // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we + // can just use the end of the item span + let mut span = use_path.span.with_hi(item.span.hi()); + if snippet(cx, span, "").ends_with(';') { + span = use_path.span.with_hi(item.span.hi() - BytePos(1)); + } + ( + span, false, + ) + }; + + let imports_string = if used_imports.len() == 1 { + used_imports.iter().next().unwrap().to_string() + } else { + let mut imports = used_imports + .iter() + .map(ToString::to_string) + .collect::>(); + imports.sort(); + if braced_glob { + imports.join(", ") + } else { + format!("{{{}}}", imports.join(", ")) + } + }; + + let sugg = if braced_glob { + imports_string + } else { + format!("{}::{}", import_source_snippet, imports_string) + }; + + let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res { + (ENUM_GLOB_USE, "usage of wildcard import for enum variants") + } else { + (WILDCARD_IMPORTS, "usage of wildcard import") + }; + + span_lint_and_sugg( + cx, + lint, + span, + message, + "try", + sugg, + applicability, + ); + } + } + } + + fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) { + if is_test_module_or_function(item) { + self.test_modules_deep = self.test_modules_deep.saturating_sub(1); + } + } +} + +impl WildcardImports { + fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { + in_macro(item.span) + || is_prelude_import(segments) + || (is_super_only_import(segments) && self.test_modules_deep > 0) + } +} + +// Allow "...prelude::*" imports. +// Many crates have a prelude, and it is imported as a glob by design. +fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { + segments + .iter() + .last() + .map_or(false, |ps| ps.ident.as_str() == "prelude") +} + +// Allow "super::*" imports in tests. +fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { + segments.len() == 1 && segments[0].ident.as_str() == "super" +} + +fn is_test_module_or_function(item: &Item<'_>) -> bool { + matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") +} diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs new file mode 100644 index 0000000000000..dfa6223f1b9dd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -0,0 +1,492 @@ +use std::borrow::Cow; +use std::ops::Range; + +use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; +use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; +use rustc_ast::token; +use rustc_ast::tokenstream::TokenStream; +use rustc_errors::Applicability; +use rustc_lexer::unescape::{self, EscapeError}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_parse::parser; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// **What it does:** This lint warns when you use `println!("")` to + /// print a newline. + /// + /// **Why is this bad?** You should use `println!()`, which is simpler. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// println!(""); + /// ``` + pub PRINTLN_EMPTY_STRING, + style, + "using `println!(\"\")` with an empty string" +} + +declare_clippy_lint! { + /// **What it does:** This lint warns when you use `print!()` with a format + /// string that + /// ends in a newline. + /// + /// **Why is this bad?** You should use `println!()` instead, which appends the + /// newline. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let name = "World"; + /// print!("Hello {}!\n", name); + /// ``` + /// use println!() instead + /// ```rust + /// # let name = "World"; + /// println!("Hello {}!", name); + /// ``` + pub PRINT_WITH_NEWLINE, + style, + "using `print!()` with a format string that ends in a single newline" +} + +declare_clippy_lint! { + /// **What it does:** Checks for printing on *stdout*. The purpose of this lint + /// is to catch debugging remnants. + /// + /// **Why is this bad?** People often print on *stdout* while debugging an + /// application and might forget to remove those prints afterward. + /// + /// **Known problems:** Only catches `print!` and `println!` calls. + /// + /// **Example:** + /// ```rust + /// println!("Hello world!"); + /// ``` + pub PRINT_STDOUT, + restriction, + "printing on stdout" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `Debug` formatting. The purpose of this + /// lint is to catch debugging remnants. + /// + /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate + /// debugging Rust code. It should not be used in user-facing output. + /// + /// **Example:** + /// ```rust + /// # let foo = "bar"; + /// println!("{:?}", foo); + /// ``` + pub USE_DEBUG, + restriction, + "use of `Debug`-based formatting" +} + +declare_clippy_lint! { + /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args. + /// + /// **Why is this bad?** Using literals as `println!` args is inefficient + /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary + /// (i.e., just put the literal in the format string) + /// + /// **Known problems:** Will also warn with macro calls as arguments that expand to literals + /// -- e.g., `println!("{}", env!("FOO"))`. + /// + /// **Example:** + /// ```rust + /// println!("{}", "foo"); + /// ``` + /// use the literal without formatting: + /// ```rust + /// println!("foo"); + /// ``` + pub PRINT_LITERAL, + style, + "printing a literal with a format string" +} + +declare_clippy_lint! { + /// **What it does:** This lint warns when you use `writeln!(buf, "")` to + /// print a newline. + /// + /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// writeln!(buf, ""); + /// ``` + pub WRITELN_EMPTY_STRING, + style, + "using `writeln!(buf, \"\")` with an empty string" +} + +declare_clippy_lint! { + /// **What it does:** This lint warns when you use `write!()` with a format + /// string that + /// ends in a newline. + /// + /// **Why is this bad?** You should use `writeln!()` instead, which appends the + /// newline. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let name = "World"; + /// write!(buf, "Hello {}!\n", name); + /// ``` + pub WRITE_WITH_NEWLINE, + style, + "using `write!()` with a format string that ends in a single newline" +} + +declare_clippy_lint! { + /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args. + /// + /// **Why is this bad?** Using literals as `writeln!` args is inefficient + /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary + /// (i.e., just put the literal in the format string) + /// + /// **Known problems:** Will also warn with macro calls as arguments that expand to literals + /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`. + /// + /// **Example:** + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// writeln!(buf, "{}", "foo"); + /// ``` + pub WRITE_LITERAL, + style, + "writing a literal with a format string" +} + +#[derive(Default)] +pub struct Write { + in_debug_impl: bool, +} + +impl_lint_pass!(Write => [ + PRINT_WITH_NEWLINE, + PRINTLN_EMPTY_STRING, + PRINT_STDOUT, + USE_DEBUG, + PRINT_LITERAL, + WRITE_WITH_NEWLINE, + WRITELN_EMPTY_STRING, + WRITE_LITERAL +]); + +impl EarlyLintPass for Write { + fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Impl { + of_trait: Some(trait_ref), + .. + } = &item.kind + { + let trait_name = trait_ref + .path + .segments + .iter() + .last() + .expect("path has at least one segment") + .ident + .name; + if trait_name == sym!(Debug) { + self.in_debug_impl = true; + } + } + } + + fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) { + self.in_debug_impl = false; + } + + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { + if mac.path == sym!(println) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if fmt_str.symbol == Symbol::intern("") { + span_lint_and_sugg( + cx, + PRINTLN_EMPTY_STRING, + mac.span(), + "using `println!(\"\")`", + "replace it with", + "println!()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } else if mac.path == sym!(print) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + "using `print!()` with a format string that ends in a single newline", + |err| { + err.multipart_suggestion( + "use `println!` instead", + vec![ + (mac.path.span, String::from("println")), + (newline_span(&fmt_str), String::new()), + ], + Applicability::MachineApplicable, + ); + }, + ); + } + } + } else if mac.path == sym!(write) { + if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if check_newlines(&fmt_str) { + span_lint_and_then( + cx, + WRITE_WITH_NEWLINE, + mac.span(), + "using `write!()` with a format string that ends in a single newline", + |err| { + err.multipart_suggestion( + "use `writeln!()` instead", + vec![ + (mac.path.span, String::from("writeln")), + (newline_span(&fmt_str), String::new()), + ], + Applicability::MachineApplicable, + ); + }, + ) + } + } + } else if mac.path == sym!(writeln) { + if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if fmt_str.symbol == Symbol::intern("") { + let mut applicability = Applicability::MachineApplicable; + let suggestion = expr.map_or_else( + move || { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }, + move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability), + ); + + span_lint_and_sugg( + cx, + WRITELN_EMPTY_STRING, + mac.span(), + format!("using `writeln!({}, \"\")`", suggestion).as_str(), + "replace it with", + format!("writeln!({})", suggestion), + applicability, + ); + } + } + } + } +} + +/// Given a format string that ends in a newline and its span, calculates the span of the +/// newline. +fn newline_span(fmtstr: &StrLit) -> Span { + let sp = fmtstr.span; + let contents = &fmtstr.symbol.as_str(); + + let newline_sp_hi = sp.hi() + - match fmtstr.style { + StrStyle::Cooked => BytePos(1), + StrStyle::Raw(hashes) => BytePos((1 + hashes).into()), + }; + + let newline_sp_len = if contents.ends_with('\n') { + BytePos(1) + } else if contents.ends_with(r"\n") { + BytePos(2) + } else { + panic!("expected format string to contain a newline"); + }; + + sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi) +} + +impl Write { + /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two + /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes + /// the contents of the string, whether it's a raw string, and the span of the literal in the + /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the + /// `format_str` should be written to. + /// + /// Example: + /// + /// Calling this function on + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let something = "something"; + /// writeln!(buf, "string to write: {}", something); + /// ``` + /// will return + /// ```rust,ignore + /// (Some("string to write: {}"), Some(buf)) + /// ``` + #[allow(clippy::too_many_lines)] + fn check_tts<'a>( + &self, + cx: &EarlyContext<'a>, + tts: &TokenStream, + is_write: bool, + ) -> (Option, Option) { + use fmt_macros::{ + AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser, + Piece, + }; + let tts = tts.clone(); + + let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); + let mut expr: Option = None; + if is_write { + expr = match parser.parse_expr().map_err(|mut err| err.cancel()) { + Ok(p) => Some(p.into_inner()), + Err(_) => return (None, None), + }; + // might be `writeln!(foo)` + if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() { + return (None, expr); + } + } + + let fmtstr = match parser.parse_str_lit() { + Ok(fmtstr) => fmtstr, + Err(_) => return (None, expr), + }; + let tmp = fmtstr.symbol.as_str(); + let mut args = vec![]; + let mut fmt_parser = Parser::new(&tmp, None, None, false, ParseMode::Format); + while let Some(piece) = fmt_parser.next() { + if !fmt_parser.errors.is_empty() { + return (None, expr); + } + if let Piece::NextArgument(arg) = piece { + if !self.in_debug_impl && arg.format.ty == "?" { + // FIXME: modify rustc's fmt string parser to give us the current span + span_lint(cx, USE_DEBUG, parser.prev_token.span, "use of `Debug`-based formatting"); + } + args.push(arg); + } + } + let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL }; + let mut idx = 0; + loop { + const SIMPLE: FormatSpec<'_> = FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + precision_span: None, + width: CountImplied, + width_span: None, + ty: "", + ty_span: None, + }; + if !parser.eat(&token::Comma) { + return (Some(fmtstr), expr); + } + let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) { + expr + } else { + return (Some(fmtstr), None); + }; + match &token_expr.kind { + ExprKind::Lit(_) => { + let mut all_simple = true; + let mut seen = false; + for arg in &args { + match arg.position { + ArgumentImplicitlyIs(n) | ArgumentIs(n) => { + if n == idx { + all_simple &= arg.format == SIMPLE; + seen = true; + } + }, + ArgumentNamed(_) => {}, + } + } + if all_simple && seen { + span_lint(cx, lint, token_expr.span, "literal with an empty format string"); + } + idx += 1; + }, + ExprKind::Assign(lhs, rhs, _) => { + if let ExprKind::Lit(_) = rhs.kind { + if let ExprKind::Path(_, p) = &lhs.kind { + let mut all_simple = true; + let mut seen = false; + for arg in &args { + match arg.position { + ArgumentImplicitlyIs(_) | ArgumentIs(_) => {}, + ArgumentNamed(name) => { + if *p == name { + seen = true; + all_simple &= arg.format == SIMPLE; + } + }, + } + } + if all_simple && seen { + span_lint(cx, lint, rhs.span, "literal with an empty format string"); + } + } + } + }, + _ => idx += 1, + } + } + } +} + +/// Checks if the format string contains a single newline that terminates it. +/// +/// Literal and escaped newlines are both checked (only literal for raw strings). +fn check_newlines(fmtstr: &StrLit) -> bool { + let mut has_internal_newline = false; + let mut last_was_cr = false; + let mut should_lint = false; + + let contents = &fmtstr.symbol.as_str(); + + let mut cb = |r: Range, c: Result| { + let c = c.unwrap(); + + if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline { + should_lint = true; + } else { + last_was_cr = c == '\r'; + if c == '\n' { + has_internal_newline = true; + } + } + }; + + match fmtstr.style { + StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb), + StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb), + } + + should_lint +} diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs new file mode 100644 index 0000000000000..fb4700d8743fd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs @@ -0,0 +1,61 @@ +use crate::consts::{constant_simple, Constant}; +use crate::utils::span_lint_and_help; +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `0.0 / 0.0`. + /// + /// **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// 0.0f32 / 0.0; + /// ``` + pub ZERO_DIVIDED_BY_ZERO, + complexity, + "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`" +} + +declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ZeroDiv { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + // check for instances of 0.0/0.0 + if_chain! { + if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind; + if let BinOpKind::Div = op.node; + // TODO - constant_simple does not fold many operations involving floats. + // That's probably fine for this lint - it's pretty unlikely that someone would + // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. + if let Some(lhs_value) = constant_simple(cx, cx.tables, left); + if let Some(rhs_value) = constant_simple(cx, cx.tables, right); + if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value; + if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value; + then { + // since we're about to suggest a use of f32::NAN or f64::NAN, + // match the precision of the literals that are given. + let float_type = match (lhs_value, rhs_value) { + (Constant::F64(_), _) + | (_, Constant::F64(_)) => "f64", + _ => "f32" + }; + span_lint_and_help( + cx, + ZERO_DIVIDED_BY_ZERO, + expr.span, + "constant division of `0.0` with `0.0` will always result in NaN", + None, + &format!( + "Consider using `{}::NAN` if you would like a constant representing NaN", + float_type, + ), + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_workspace_tests/Cargo.toml b/src/tools/clippy/clippy_workspace_tests/Cargo.toml new file mode 100644 index 0000000000000..7a235b215d38d --- /dev/null +++ b/src/tools/clippy/clippy_workspace_tests/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "clippy_workspace_tests" +version = "0.1.0" +edition = "2018" + +[workspace] +members = ["subcrate"] diff --git a/src/tools/clippy/clippy_workspace_tests/src/main.rs b/src/tools/clippy/clippy_workspace_tests/src/main.rs new file mode 100644 index 0000000000000..b322eca1db51d --- /dev/null +++ b/src/tools/clippy/clippy_workspace_tests/src/main.rs @@ -0,0 +1,3 @@ +#![deny(rust_2018_idioms)] + +fn main() {} diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml b/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml new file mode 100644 index 0000000000000..83ea5868160bb --- /dev/null +++ b/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "subcrate" +version = "0.1.0" diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs b/src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs @@ -0,0 +1 @@ + diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md new file mode 100644 index 0000000000000..9ad1315c17521 --- /dev/null +++ b/src/tools/clippy/doc/adding_lints.md @@ -0,0 +1,477 @@ +# Adding a new lint + +You are probably here because you want to add a new lint to Clippy. If this is +the first time you're contributing to Clippy, this document guides you through +creating an example lint from scratch. + +To get started, we will create a lint that detects functions called `foo`, +because that's clearly a non-descriptive name. + +- [Adding a new lint](#adding-a-new-lint) + - [Setup](#setup) + - [Getting Started](#getting-started) + - [Testing](#testing) + - [Rustfix tests](#rustfix-tests) + - [Edition 2018 tests](#edition-2018-tests) + - [Testing manually](#testing-manually) + - [Lint declaration](#lint-declaration) + - [Lint passes](#lint-passes) + - [Emitting a lint](#emitting-a-lint) + - [Adding the lint logic](#adding-the-lint-logic) + - [Author lint](#author-lint) + - [Documentation](#documentation) + - [Running rustfmt](#running-rustfmt) + - [Debugging](#debugging) + - [PR Checklist](#pr-checklist) + - [Cheatsheet](#cheatsheet) + +## Setup + +When working on Clippy, you will need the current git master version of rustc, +which can change rapidly. Make sure you're working near rust-clippy's master, +and use the `setup-toolchain.sh` script to configure the appropriate toolchain +for the Clippy directory. + +## Getting Started + +There is a bit of boilerplate code that needs to be set up when creating a new +lint. Fortunately, you can use the clippy dev tools to handle this for you. We +are naming our new lint `foo_functions` (lints are generally written in snake +case), and we don't need type information so it will have an early pass type +(more on this later on). To get started on this lint you can run +`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` +(category will default to nursery if not provided). This command will create +two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`, +as well as run `cargo dev update_lints` to register the new lint. Next, we'll +open up these files and add our lint! + +## Testing + +Let's write some tests first that we can execute while we iterate on our lint. + +Clippy uses UI tests for testing. UI tests check that the output of Clippy is +exactly as expected. Each test is just a plain Rust file that contains the code +we want to check. The output of Clippy is compared against a `.stderr` file. +Note that you don't have to create this file yourself, we'll get to +generating the `.stderr` files further down. + +We start by opening the test file created at `tests/ui/foo_functions.rs`. + +Update the file with some examples to get started: + +```rust +#![warn(clippy::foo_functions)] + +// Impl methods +struct A; +impl A { + pub fn fo(&self) {} + pub fn foo(&self) {} + pub fn food(&self) {} +} + +// Default trait methods +trait B { + fn fo(&self) {} + fn foo(&self) {} + fn food(&self) {} +} + +// Plain functions +fn fo() {} +fn foo() {} +fn food() {} + +fn main() { + // We also don't want to lint method calls + foo(); + let a = A; + a.foo(); +} +``` + +Now we can run the test with `TESTNAME=foo_functions cargo uitest`, +currently this test is meaningless though. + +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. + +Once we are satisfied with the output, we need to run +`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint. +Please note that, we should run `TESTNAME=foo_functions cargo uitest` +every time before running `tests/ui/update-all-references.sh`. +Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit +our lint, we need to commit the generated `.stderr` files, too. In general, you +should only commit files changed by `tests/ui/update-all-references.sh` for the +specific lint you are creating/editing. + +## Rustfix tests + +If the lint you are working on is making use of structured suggestions, the +test file should include a `// run-rustfix` comment at the top. This will +additionally run [rustfix] for that test. Rustfix will apply the suggestions +from the lint to the code of the test file and compare that to the contents of +a `.fixed` file. + +Use `tests/ui/update-all-references.sh` to automatically generate the +`.fixed` file after running the tests. + +[rustfix]: https://github.com/rust-lang/rustfix + +## Edition 2018 tests + +Some features require the 2018 edition to work (e.g. `async_await`), but +compile-test tests run on the 2015 edition by default. To change this behavior +add `// edition:2018` at the top of the test file (note that it's space-sensitive). + +## Testing manually + +Manually testing against an example file can be useful if you have added some +`println!`s and the test suite output becomes unreadable. To try Clippy with +your local modifications, run `env CLIPPY_TESTS=true cargo run --bin +clippy-driver -- -L ./target/debug input.rs` from the working copy root. + +With tests in place, let's have a look at implementing our lint now. + +## Lint declaration + +Let's start by opening the new file created in the `clippy_lints` crate +at `clippy_lints/src/foo_functions.rs`. That's the crate where all the +lint code is. This file has already imported some initial things we will need: + +```rust +use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_ast::ast::*; +``` + +The next step is to update the lint declaration. Lints are declared using the +[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update +the auto-generated lint declaration to have a real description, something like this: + +```rust +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code + /// ``` + pub FOO_FUNCTIONS, + pedantic, + "function named `foo`, which is not a descriptive name" +} +``` + +* The section of lines prefixed with `///` constitutes the lint documentation + section. This is the default documentation style and will be displayed + [like this][example_lint_page]. +* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the + [lint naming guidelines][lint_naming] here when naming your lint. + In short, the name should state the thing that is being checked for and + read well when used with `allow`/`warn`/`deny`. +* `pedantic` sets the lint level to `Allow`. + The exact mapping can be found [here][category_level_mapping] +* The last part should be a text that explains what exactly is wrong with the + code + +The rest of this file contains an empty implementation for our lint pass, +which in this case is `EarlyLintPass` and should look like this: + +```rust +// clippy_lints/src/foo_functions.rs + +// .. imports and lint declaration .. + +declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]); + +impl EarlyLintPass for FooFunctions {} +``` + +Normally after declaring the lint, we have to run `cargo dev update_lints`, +which updates some files, so Clippy knows about the new lint. Since we used +`cargo dev new_lint ...` to generate the lint declaration, this was done +automatically. While `update_lints` automates most of the things, it doesn't +automate everything. We will have to register our lint pass manually in the +`register_plugins` function in `clippy_lints/src/lib.rs`: + +```rust +store.register_early_pass(|| box foo_functions::FooFunctions); +``` + +[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60 +[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure +[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints +[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 + +## Lint passes + +Writing a lint that only checks for the name of a function means that we only +have to deal with the AST and don't have to deal with the type system at all. +This is good, because it makes writing this particular lint less complicated. + +We have to make this decision with every new Clippy lint. It boils down to using +either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. + +In short, the `LateLintPass` has access to type information while the +`EarlyLintPass` doesn't. If you don't need access to type information, use the +`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed +hasn't really been a concern with Clippy so far. + +Since we don't need type information for checking the function name, we used +`--pass=early` when running the new lint automation and all the imports were +added accordingly. + +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html + +## Emitting a lint + +With UI tests and the lint declaration in place, we can start working on the +implementation of the lint logic. + +Let's start by implementing the `EarlyLintPass` for our `FooFunctions`: + +```rust +impl EarlyLintPass for FooFunctions { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + // TODO: Emit lint here + } +} +``` + +We implement the [`check_fn`][check_fn] method from the +[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various +information about the function that is currently being checked. More on that in +the next section. Let's worry about the details later and emit our lint for +*every* function definition first. + +Depending on how complex we want our lint message to be, we can choose from a +variety of lint emission functions. They can all be found in +[`clippy_lints/src/utils/diagnostics.rs`][diagnostics]. + +`span_lint_and_help` seems most appropriate in this case. It allows us to +provide an extra help message and we can't really suggest a better name +automatically. This is how it looks: + +```rust +impl EarlyLintPass for FooFunctions { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + span_lint_and_help( + cx, + FOO_FUNCTIONS, + span, + "function named `foo`", + None, + "consider using a more meaningful name" + ); + } +} +``` + +Running our UI test should now produce output that contains the lint message. + +[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn +[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs + +## Adding the lint logic + +Writing the logic for your lint will most likely be different from our example, +so this section is kept rather short. + +Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind] +that has the [`FnKind::Fn`] variant. It provides access to the name of the +function/method via an [`Ident`][ident]. + +With that we can expand our `check_fn` method to: + +```rust +impl EarlyLintPass for FooFunctions { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + if is_foo_fn(fn_kind) { + span_lint_and_help( + cx, + FOO_FUNCTIONS, + span, + "function named `foo`", + None, + "consider using a more meaningful name" + ); + } + } +} +``` + +We separate the lint conditional from the lint emissions because it makes the +code a bit easier to read. In some cases this separation would also allow to +write some unit tests (as opposed to only UI tests) for the separate function. + +In our example, `is_foo_fn` looks like: + +```rust +// use statements, impl EarlyLintPass, check_fn, .. + +fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, ident, ..) => { + // check if `fn` name is `foo` + ident.name.as_str() == "foo" + } + // ignore closures + FnKind::Closure(..) => false + } +} +``` + +Now we should also run the full test suite with `cargo test`. At this point +running `cargo test` should produce the expected output. Remember to run +`tests/ui/update-all-references.sh` to update the `.stderr` file. + +`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint +implementation is not violating any Clippy lints itself. + +That should be it for the lint implementation. Running `cargo test` should now +pass. + +[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html +[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn +[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html + +## Author lint + +If you have trouble implementing your lint, there is also the internal `author` +lint to generate Clippy code that detects the offending pattern. It does not +work for all of the Rust syntax, but can give a good starting point. + +The quickest way to use it, is the +[Rust playground: play.rust-lang.org][author_example]. +Put the code you want to lint into the editor and add the `#[clippy::author]` +attribute above the item. Then run Clippy via `Tools -> Clippy` and you should +see the generated code in the output below. + +[Here][author_example] is an example on the playground. + +If the command was executed successfully, you can copy the code over to where +you are implementing your lint. + +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 + +## Documentation + +The final thing before submitting our PR is to add some documentation to our +lint declaration. + +Please document your lint with a doc comment akin to the following: + +```rust +declare_clippy_lint! { + /// **What it does:** Checks for ... (describe what the lint matches). + /// + /// **Why is this bad?** Supply the reason for linting the code. + /// + /// **Known problems:** None. (Or describe where it could go wrong.) + /// + /// **Example:** + /// + /// ```rust,ignore + /// // Bad + /// Insert a short example of code that triggers the lint + /// + /// // Good + /// Insert a short example of improved code that doesn't trigger the lint + /// ``` + pub FOO_FUNCTIONS, + pedantic, + "function named `foo`, which is not a descriptive name" +} +``` + +Once your lint is merged, this documentation will show up in the [lint +list][lint_list]. + +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html + +## Running rustfmt + +[Rustfmt] is a tool for formatting Rust code according to style guidelines. +Your code has to be formatted by `rustfmt` before a PR can be merged. +Clippy uses nightly `rustfmt` in the CI. + +It can be installed via `rustup`: + +```bash +rustup component add rustfmt --toolchain=nightly +``` + +Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is +installed for the nightly toolchain. + +[Rustfmt]: https://github.com/rust-lang/rustfmt + +## Debugging + +If you want to debug parts of your lint implementation, you can use the [`dbg!`] +macro anywhere in your code. Running the tests should then include the debug +output in the `stdout` part. + +[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html + +## PR Checklist + +Before submitting your PR make sure you followed all of the basic requirements: + + + +- [ ] Followed [lint naming conventions][lint_naming] +- [ ] Added passing UI tests (including committed `.stderr` file) +- [ ] `cargo test` passes locally +- [ ] Executed `cargo dev update_lints` +- [ ] Added lint documentation +- [ ] Run `cargo dev fmt` + +## Cheatsheet + +Here are some pointers to things you are likely going to need for every lint: + +* [Clippy utils][utils] - Various helper functions. Maybe the function you need + is already in here (`implements_trait`, `match_path`, `snippet`, etc) +* [Clippy diagnostics][diagnostics] +* [The `if_chain` macro][if_chain] +* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] +* [`Span`][span] +* [`Applicability`][applicability] +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts +* [The nightly rustc docs][nightly_docs] which has been linked to throughout + this guide + +For `EarlyLintPass` lints: + +* [`EarlyLintPass`][early_lint_pass] +* [`rustc_ast::ast`][ast] + +For `LateLintPass` lints: + +* [`LateLintPass`][late_lint_pass] +* [`Ty::TyKind`][ty] + +While most of Clippy's lint utils are documented, most of rustc's internals lack +documentation currently. This is unfortunate, but in most cases you can probably +get away with copying things from existing similar lints. If you are stuck, +don't hesitate to ask on [Discord] or in the issue/PR. + +[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs +[if_chain]: https://docs.rs/if_chain/*/if_chain/ +[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion +[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html +[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html +[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ +[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html +[Discord]: https://discord.gg/rust-lang diff --git a/src/tools/clippy/doc/backport.md b/src/tools/clippy/doc/backport.md new file mode 100644 index 0000000000000..259696658eaba --- /dev/null +++ b/src/tools/clippy/doc/backport.md @@ -0,0 +1,50 @@ +# Backport Changes + +Sometimes it is necessary to backport changes to the beta release of Clippy. +Backports in Clippy are rare and should be approved by the Clippy team. For +example, a backport is done, if a crucial ICE was fixed or a lint is broken to a +point, that it has to be disabled, before landing on stable. + +Backports are done to the `beta` release of Clippy. Backports to stable Clippy +releases basically don't exist, since this would require a Rust point release, +which is almost never justifiable for a Clippy fix. + + +## Backport the changes + +Backports are done on the beta branch of the Clippy repository. + +```bash +# Assuming the current directory corresponds to the Clippy repository +$ git checkout beta +$ git checkout -b backport +$ git cherry-pick # `` is the commit hash of the commit, that should be backported +$ git push origin backport +``` + +After this, you can open a PR to the `beta` branch of the Clippy repository. + + +## Update Clippy in the Rust Repository + +This step must be done, **after** the PR of the previous step was merged. + +After the backport landed in the Clippy repository, also the Clippy version on +the Rust `beta` branch has to be updated. + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git checkout beta +$ git checkout -b clippy_backport +$ pushd src/tools/clippy +$ git fetch +$ git checkout beta +$ popd +$ git add src/tools/clippy +§ git commit -m "Update Clippy" +$ git push origin clippy_backport +``` + +After this you can open a PR to the `beta` branch of the Rust repository. In +this PR you should tag the Clippy team member, that agreed to the backport or +the `@rust-lang/clippy` team. Make sure to add `[beta]` to the title of the PR. diff --git a/src/tools/clippy/doc/changelog_update.md b/src/tools/clippy/doc/changelog_update.md new file mode 100644 index 0000000000000..0b80cce6d23ea --- /dev/null +++ b/src/tools/clippy/doc/changelog_update.md @@ -0,0 +1,78 @@ +# Changelog Update + +If you want to help with updating the [changelog][changelog], you're in the right place. + +## When to update + +Typos and other small fixes/additions are _always_ welcome. + +Special care needs to be taken when it comes to updating the changelog for a new +Rust release. For that purpose, the changelog is ideally updated during the week +before an upcoming stable release. You can find the release dates on the [Rust +Forge][forge]. + +Most of the time we only need to update the changelog for minor Rust releases. It's +been very rare that Clippy changes were included in a patch release. + +## Changelog update walkthrough + +### 1. Finding the relevant Clippy commits + +Each Rust release ships with its own version of Clippy. The Clippy submodule can +be found in the `tools` directory of the Rust repository. + +Depending on the current time and what exactly you want to update, the following +bullet points might be helpful: + +* When writing the release notes for the **upcoming stable release** you need to check + out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools] +* When writing the release notes for the **upcoming beta release**, you need to check + out the Clippy commit of the current Rust `master`. [Link][rust_master_tools] +* When writing the (forgotten) release notes for a **past stable release**, you + need to select the Rust release tag from the dropdown and then check the + commit of the Clippy directory: + + ![Explanation of how to find the commit hash](https://user-images.githubusercontent.com/2042399/62846160-1f8b0480-bcce-11e9-9da8-7964ca034e7a.png) + + +### 2. Fetching the PRs between those commits + +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. + +When updating the changelog it's also a good idea to make sure that `commit1` is +already correct in the current changelog. + +### 3. Authoring the final 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 ask for feedback. + +With the PRs filtered, you can start to take each PR and move the +`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but +try to keep it somewhat coherent. + +The order should roughly be: + +1. New lints +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 + +Please also be sure to update the Beta/Unreleased sections at the top with the +relevant commit ranges. + +[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md +[forge]: https://forge.rust-lang.org/ +[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools +[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools diff --git a/src/tools/clippy/doc/release.md b/src/tools/clippy/doc/release.md new file mode 100644 index 0000000000000..9d69fa8a7f69a --- /dev/null +++ b/src/tools/clippy/doc/release.md @@ -0,0 +1,111 @@ +# Release a new Clippy Version + +_NOTE: This document is probably only relevant to you, if you're a member of the +Clippy team._ + +Clippy is released together with stable Rust releases. The dates for these +releases can be found at the [Rust Forge]. This document explains the necessary +steps to create a Clippy release. + +1. [Find the Clippy commit](#find-the-clippy-commit) +2. [Tag the stable commit](#tag-the-stable-commit) +3. [Update `CHANGELOG.md`](#update-changelogmd) +4. [Remerge the `beta` branch](#remerge-the-beta-branch) +5. [Update the `beta` branch](#update-the-beta-branch) + +_NOTE: This document is for stable Rust releases, not for point releases. For +point releases, step 1. and 2. should be enough._ + +[Rust Forge]: https://forge.rust-lang.org/ + + +## Find the Clippy commit + +The first step is to tag the Clippy commit, that is included in the stable Rust +release. This commit can be found in the Rust repository. + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git fetch upstream # `upstream` is the `rust-lang/rust` remote +$ git checkout 1.XX.0 # XX should be exchanged with the corresponding version +$ git submodule update +$ SHA=$(git submodule status src/tools/clippy | awk '{print $1}') +``` + + +## Tag the stable commit + +After finding the Clippy commit, it can be tagged with the release number. + +```bash +# Assuming the current directory corresponds to the Clippy repository +$ git checkout $SHA +$ git tag rust-1.XX.0 # XX should be exchanged with the corresponding version +$ git push upstream master --tags # `upstream` is the `rust-lang/rust-clippy` remote +``` + +After this, the release should be available on the Clippy [release page]. + +[release page]: https://github.com/rust-lang/rust-clippy/releases + + +## Update `CHANGELOG.md` + +For this see the document on [how to update the changelog]. + +[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md + + +## Remerge the `beta` branch + +This step is only necessary, if since the last release something was backported +to the beta Rust release. The remerge is then necessary, to make sure that the +Clippy commit, that was used by the now stable Rust release, persists in the +tree of the Clippy repository. + +To find out if this step is necessary run + +```bash +# Assumes that the local master branch is up-to-date +$ git fetch upstream +$ git branch master --contains upstream/beta +``` + +If this command outputs `master`, this step is **not** necessary. + +```bash +# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy +$ git checkout -b backport_remerge +$ git merge beta +$ git diff # This diff has to be empty, otherwise something with the remerge failed +$ git push origin backport_remerge # This can be pushed to your fork +``` + +After this, open a PR to the master branch. In this PR, the commit hash of the +`HEAD` of the `beta` branch must exists. In addition to that, no files should +be changed by this PR. + + +## Update the `beta` branch + +This step must be done **after** the PR of the previous step was merged. + +First, the Clippy commit of the `beta` branch of the Rust repository has to be +determined. + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git checkout beta +$ git submodule update +$ BETA_SHA=$(git submodule status src/tools/clippy | awk '{print $1}') +``` + +After finding the Clippy commit, the `beta` branch in the Clippy repository can +be updated. + +```bash +# Assuming the current directory corresponds to the Clippy repository +$ git checkout beta +$ git rebase $BETA_SHA +$ git push upstream beta +``` diff --git a/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md b/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md new file mode 100644 index 0000000000000..fcd7abbf3f169 --- /dev/null +++ b/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md @@ -0,0 +1,69 @@ +This repository was previously licensed under MPL-2.0, however in #3093 +([archive](http://web.archive.org/web/20181005185227/https://github.com/rust-lang-nursery/rust-clippy/issues/3093), +[screenshot](https://user-images.githubusercontent.com/1617736/46573505-5b856880-c94b-11e8-9a14-981c889b4981.png)) we +relicensed it to the Rust license (dual licensed as Apache v2 / MIT) + +At the time, the contributors were those listed in contributors.txt. + +We opened a bunch of issues asking for an explicit relicensing approval. Screenshots of all these issues at the time of +relicensing are archived on GitHub. We also have saved Wayback Machine copies of these: + +- #3094 + ([archive](http://web.archive.org/web/20181005191247/https://github.com/rust-lang-nursery/rust-clippy/issues/3094), + [screenshot](https://user-images.githubusercontent.com/1617736/46573506-5b856880-c94b-11e8-8a44-51cb40bc16ee.png)) +- #3095 + ([archive](http://web.archive.org/web/20181005184416/https://github.com/rust-lang-nursery/rust-clippy/issues/3095), + [screenshot](https://user-images.githubusercontent.com/1617736/46573507-5c1dff00-c94b-11e8-912a-4bd6b5f838f5.png)) +- #3096 + ([archive](http://web.archive.org/web/20181005184802/https://github.com/rust-lang-nursery/rust-clippy/issues/3096), + [screenshot](https://user-images.githubusercontent.com/1617736/46573508-5c1dff00-c94b-11e8-9425-2464f7260ff0.png)) +- #3097 + ([archive](http://web.archive.org/web/20181005184821/https://github.com/rust-lang-nursery/rust-clippy/issues/3097), + [screenshot](https://user-images.githubusercontent.com/1617736/46573509-5c1dff00-c94b-11e8-8ba2-53f687984fe7.png)) +- #3098 + ([archive](http://web.archive.org/web/20181005184900/https://github.com/rust-lang-nursery/rust-clippy/issues/3098), + [screenshot](https://user-images.githubusercontent.com/1617736/46573510-5c1dff00-c94b-11e8-8f64-371698401c60.png)) +- #3099 + ([archive](http://web.archive.org/web/20181005184901/https://github.com/rust-lang-nursery/rust-clippy/issues/3099), + [screenshot](https://user-images.githubusercontent.com/1617736/46573511-5c1dff00-c94b-11e8-8e20-7d0eeb392b95.png)) +- #3100 + ([archive](http://web.archive.org/web/20181005184901/https://github.com/rust-lang-nursery/rust-clippy/issues/3100), + [screenshot](https://user-images.githubusercontent.com/1617736/46573512-5c1dff00-c94b-11e8-8a13-7d758ed3563d.png)) +- #3230 + ([archive](http://web.archive.org/web/20181005184903/https://github.com/rust-lang-nursery/rust-clippy/issues/3230), + [screenshot](https://user-images.githubusercontent.com/1617736/46573513-5cb69580-c94b-11e8-86b1-14ce82741e5c.png)) + +The usernames of commenters on these issues can be found in relicense_comments.txt + +There are a couple people in relicense_comments.txt who are not found in contributors.txt: + +- @EpocSquadron has [made minor text contributions to the + README](https://github.com/rust-lang/rust-clippy/commits?author=EpocSquadron) which have since been overwritten, and + doesn't count +- @JayKickliter [agreed to the relicense on their pull + request](https://github.com/rust-lang/rust-clippy/pull/3195#issuecomment-423781016) + ([archive](https://web.archive.org/web/20181005190730/https://github.com/rust-lang/rust-clippy/pull/3195), + [screenshot](https://user-images.githubusercontent.com/1617736/46573514-5cb69580-c94b-11e8-8ffb-05a5bd02e2cc.png) + +- @sanmai-NL's [contribution](https://github.com/rust-lang/rust-clippy/commits?author=sanmai-NL) is a minor one-word + addition which doesn't count for copyright assignment +- @zmt00's [contributions](https://github.com/rust-lang/rust-clippy/commits?author=zmt00) are minor typo fixes and don't + count +- @VKlayd has [nonminor contributions](https://github.com/rust-lang/rust-clippy/commits?author=VKlayd) which we rewrote + (see below) +- @wartman4404 has [nonminor contributions](https://github.com/rust-lang/rust-clippy/commits?author=wartman4404) which + we rewrote (see below) + + +Two of these contributors had nonminor contributions (#2184, #427) requiring a rewrite, carried out in #3251 +([archive](http://web.archive.org/web/20181005192411/https://github.com/rust-lang-nursery/rust-clippy/pull/3251), +[screenshot](https://user-images.githubusercontent.com/1617736/46573515-5cb69580-c94b-11e8-86e5-b456452121b2.png)) + +First, I (Manishearth) removed the lints they had added. I then documented at a high level what the lints did in #3251, +asking for co-maintainers who had not seen the code for the lints to rewrite them. #2814 was rewritten by @phansch, and +#427 was rewritten by @oli-obk, who did not recall having previously seen the code they were rewriting. + +------ + +Since this document was written, @JayKickliter and @sanmai-ML added their consent in #3230 +([archive](http://web.archive.org/web/20181006171926/https://github.com/rust-lang-nursery/rust-clippy/issues/3230)) diff --git a/src/tools/clippy/etc/relicense/contributors.txt b/src/tools/clippy/etc/relicense/contributors.txt new file mode 100644 index 0000000000000..e81ebf2148498 --- /dev/null +++ b/src/tools/clippy/etc/relicense/contributors.txt @@ -0,0 +1,232 @@ +0ndorio +0xbsec +17cupsofcoffee +Aaron1011 +Aaronepower +aaudiber +afck +alexcrichton +AlexEne +alexeyzab +alexheretic +alexreg +alusch +andersk +aochagavia +apasel422 +Arnavion +AtheMathmo +auscompgeek +AVerm +badboy +Baelyk +BenoitZugmeyer +bestouff +birkenfeld +bjgill +bkchr +Bobo1239 +bood +bootandy +b-r-u +budziq +CAD97 +Caemor +camsteffen +carols10cents +CBenoit +cesarb +cgm616 +chrisduerr +chrisvittal +chyvonomys +clarcharr +clippered +commandline +cramertj +csmoe +ctjhoa +cuviper +CYBAI +darArch +DarkEld3r +dashed +daubaris +d-dorazio +debris +dereckson +detrumi +devonhollowood +dtolnay +durka +dwijnand +eddyb +elliottneilclark +elpiel +ensch +EpicatSupercell +EpocSquadron +erickt +estk +etaoins +F001 +fanzier +FauxFaux +fhartwig +flip1995 +Fraser999 +Frederick888 +frewsxcv +gbip +gendx +gibfahn +gnieto +gnzlbg +goodmanjonathan +guido4000 +GuillaumeGomez +Hanaasagi +hdhoang +HMPerson1 +hobofan +iKevinY +illicitonion +imp +inrustwetrust +ishitatsuyuki +Jascha-N +jayhardee9 +JayKickliter +JDemler +jedisct1 +jmquigs +joelgallant +joeratt +josephDunne +JoshMcguigan +joshtriplett +jugglerchris +karyon +Keats +kennytm +Kha +killercup +kimsnj +KitFreddura +koivunej +kraai +kvikas +LaurentMazare +letheed +llogiq +lo48576 +lpesk +lucab +luisbg +lukasstevens +Machtan +MaloJaffre +Manishearth +marcusklaas +mark-i-m +martiansideofthemoon +martinlindhe +mathstuf +mati865 +matthiaskrgr +mattyhall +mbrubeck +mcarton +memoryleak47 +messense +michaelrutherford +mikerite +mipli +mockersf +montrivo +mrecachinas +Mrmaxmeier +mrmonday +ms2300 +Ms2ger +musoke +nathan +Nemo157 +NiekGr +niklasf +nrc +nweston +o01eg +ogham +oli-obk +ordovicia +pengowen123 +pgerber +phansch +philipturnbull +pickfire +pietro +PixelPirate +pizzaiter +PSeitz +Pyriphlegethon +pythonesque +quininer +Rantanen +rcoh +reiner-dolp +reujab +Robzz +samueltardieu +sanmai-NL +sanxiyn +scott-linder +scottmcm +scurest +senden9 +shahn +shepmaster +shnewto +shssoichiro +siiptuo +sinkuu +skade +sourcefrog +sourcejedi +steveklabnik +sunfishcode +sunjay +swgillespie +Techcable +terry90 +theemathas +thekidxp +theotherphil +TimNN +TomasKralCZ +tomprince +topecongiro +tspiteri +Twisol +U007D +uHOOCCOOHu +untitaker +upsuper +utaal +utam0k +vi +VKlayd +Vlad-Shcherbina +vorner +wafflespeanut +wartman4404 +waywardmonkeys +yaahallo +yangby-cryptape +yati-sagade +ykrivopalov +ysimonson +zayenz +zmanian +zmbush +zmt00 diff --git a/src/tools/clippy/etc/relicense/relicense_comments.txt b/src/tools/clippy/etc/relicense/relicense_comments.txt new file mode 100644 index 0000000000000..52c25eb201fb3 --- /dev/null +++ b/src/tools/clippy/etc/relicense/relicense_comments.txt @@ -0,0 +1,227 @@ +0ndorio +0xbsec +17cupsofcoffee +Aaron1011 +Aaronepower +aaudiber +afck +alexcrichton +AlexEne +alexeyzab +alexheretic +alexreg +alusch +andersk +aochagavia +apasel422 +Arnavion +AtheMathmo +auscompgeek +AVerm +badboy +Baelyk +BenoitZugmeyer +bestouff +birkenfeld +bjgill +bkchr +Bobo1239 +bood +bootandy +b-r-u +budziq +CAD97 +Caemor +camsteffen +carols10cents +CBenoit +cesarb +cgm616 +chrisduerr +chrisvittal +chyvonomys +clarcharr +clippered +commandline +cramertj +csmoe +ctjhoa +cuviper +CYBAI +darArch +DarkEld3r +dashed +daubaris +d-dorazio +debris +dereckson +detrumi +devonhollowood +dtolnay +durka +dwijnand +eddyb +elliottneilclark +elpiel +ensch +EpicatSupercell +erickt +estk +etaoins +F001 +fanzier +FauxFaux +fhartwig +flip1995 +Fraser999 +Frederick888 +frewsxcv +gbip +gendx +gibfahn +gnieto +gnzlbg +goodmanjonathan +guido4000 +GuillaumeGomez +Hanaasagi +hdhoang +HMPerson1 +hobofan +iKevinY +illicitonion +imp +inrustwetrust +ishitatsuyuki +Jascha-N +jayhardee9 +JDemler +jedisct1 +jmquigs +joelgallant +joeratt +josephDunne +JoshMcguigan +joshtriplett +jugglerchris +karyon +Keats +kennytm +Kha +killercup +kimsnj +KitFreddura +koivunej +kraai +kvikas +LaurentMazare +letheed +llogiq +lo48576 +lpesk +lucab +luisbg +lukasstevens +Machtan +MaloJaffre +Manishearth +marcusklaas +mark-i-m +martiansideofthemoon +martinlindhe +mathstuf +mati865 +matthiaskrgr +mattyhall +mbrubeck +mcarton +memoryleak47 +messense +michaelrutherford +mikerite +mipli +mockersf +montrivo +mrecachinas +Mrmaxmeier +mrmonday +ms2300 +Ms2ger +musoke +nathan +Nemo157 +NiekGr +niklasf +nrc +nweston +o01eg +ogham +oli-obk +ordovicia +pengowen123 +pgerber +phansch +philipturnbull +pickfire +pietro +PixelPirate +pizzaiter +PSeitz +Pyriphlegethon +pythonesque +quininer +Rantanen +rcoh +reiner-dolp +reujab +Robzz +samueltardieu +sanxiyn +scott-linder +scottmcm +scurest +senden9 +shahn +shepmaster +shnewto +shssoichiro +siiptuo +sinkuu +skade +sourcefrog +sourcejedi +steveklabnik +sunfishcode +sunjay +swgillespie +Techcable +terry90 +theemathas +thekidxp +theotherphil +TimNN +TomasKralCZ +tommilligan +tomprince +topecongiro +tspiteri +Twisol +U007D +uHOOCCOOHu +untitaker +upsuper +utaal +utam0k +vi +Vlad-Shcherbina +vorner +wafflespeanut +waywardmonkeys +yaahallo +yangby-cryptape +yati-sagade +ykrivopalov +ysimonson +zayenz +zmanian +zmbush diff --git a/src/tools/clippy/mini-macro/Cargo.toml b/src/tools/clippy/mini-macro/Cargo.toml new file mode 100644 index 0000000000000..75ab17588a7f4 --- /dev/null +++ b/src/tools/clippy/mini-macro/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "clippy-mini-macro-test" +version = "0.2.0" +authors = [ + "Manish Goregaokar ", + "Andre Bogus ", + "Georg Brandl ", + "Martin Carton ", + "Oliver Schneider " +] +license = "MIT OR Apache-2.0" +description = "A macro to test clippy's procedural macro checks" +repository = "https://github.com/rust-lang/rust-clippy" +edition = "2018" + +[lib] +name = "clippy_mini_macro_test" +proc-macro = true + +[dependencies] diff --git a/src/tools/clippy/mini-macro/src/lib.rs b/src/tools/clippy/mini-macro/src/lib.rs new file mode 100644 index 0000000000000..ba946563ec595 --- /dev/null +++ b/src/tools/clippy/mini-macro/src/lib.rs @@ -0,0 +1,26 @@ +#![feature(proc_macro_quote)] +#![deny(rust_2018_idioms)] +// FIXME: Remove this attribute once the weird failure is gone. +#![allow(unused_extern_crates)] +extern crate proc_macro; + +use proc_macro::{quote, TokenStream}; + +#[proc_macro_derive(ClippyMiniMacroTest)] +pub fn mini_macro(_: TokenStream) -> TokenStream { + quote!( + #[allow(unused)] + fn needless_take_by_value(s: String) { + println!("{}", s.len()); + } + #[allow(unused)] + fn needless_loop(items: &[u8]) { + for i in 0..items.len() { + println!("{}", items[i]); + } + } + fn line_wrapper() { + println!("{}", line!()); + } + ) +} diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain new file mode 100644 index 0000000000000..bf867e0ae5b6c --- /dev/null +++ b/src/tools/clippy/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/src/tools/clippy/rustc_tools_util/Cargo.toml b/src/tools/clippy/rustc_tools_util/Cargo.toml new file mode 100644 index 0000000000000..6f0fc5bee8f09 --- /dev/null +++ b/src/tools/clippy/rustc_tools_util/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rustc_tools_util" +version = "0.2.0" +authors = ["Matthias Krüger "] +description = "small helper to generate version information for git packages" +repository = "https://github.com/rust-lang/rust-clippy" +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["rustc", "tool", "git", "version", "hash"] +categories = ["development-tools"] +edition = "2018" + +[dependencies] + +[features] +deny-warnings = [] diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md new file mode 100644 index 0000000000000..6027538dc4ab2 --- /dev/null +++ b/src/tools/clippy/rustc_tools_util/README.md @@ -0,0 +1,62 @@ +# rustc_tools_util + +A small tool to help you generate version information +for packages installed from a git repo + +## Usage + +Add a `build.rs` file to your repo and list it in `Cargo.toml` +```` +build = "build.rs" +```` + +List rustc_tools_util as regular AND build dependency. +```` +[dependencies] +rustc_tools_util = "0.1" + +[build-dependencies] +rustc_tools_util = "0.1" +```` + +In `build.rs`, generate the data in your `main()` +````rust +fn main() { + println!( + "cargo:rustc-env=GIT_HASH={}", + rustc_tools_util::get_commit_hash().unwrap_or_default() + ); + println!( + "cargo:rustc-env=COMMIT_DATE={}", + rustc_tools_util::get_commit_date().unwrap_or_default() + ); + println!( + "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", + rustc_tools_util::get_channel().unwrap_or_default() + ); +} + +```` + +Use the version information in your main.rs +````rust +use rustc_tools_util::*; + +fn show_version() { + let version_info = rustc_tools_util::get_version_info!(); + println!("{}", version_info); +} +```` +This gives the following output in clippy: +`clippy 0.0.212 (a416c5e 2018-12-14)` + + +## License + +Copyright 2014-2020 The Rust Project Developers + +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. All files in the project carrying such notice may not be +copied, modified, or distributed except according to those terms. diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs new file mode 100644 index 0000000000000..ff2a7de572571 --- /dev/null +++ b/src/tools/clippy/rustc_tools_util/src/lib.rs @@ -0,0 +1,162 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] + +use std::env; + +#[macro_export] +macro_rules! get_version_info { + () => {{ + let major = env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(); + let minor = env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(); + let patch = env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(); + let crate_name = String::from(env!("CARGO_PKG_NAME")); + + let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string); + let commit_hash = option_env!("GIT_HASH").map(str::to_string); + let commit_date = option_env!("COMMIT_DATE").map(str::to_string); + + VersionInfo { + major, + minor, + patch, + host_compiler, + commit_hash, + commit_date, + crate_name, + } + }}; +} + +// some code taken and adapted from RLS and cargo +pub struct VersionInfo { + pub major: u8, + pub minor: u8, + pub patch: u16, + pub host_compiler: Option, + pub commit_hash: Option, + pub commit_date: Option, + pub crate_name: String, +} + +impl std::fmt::Display for VersionInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let hash = self.commit_hash.clone().unwrap_or_default(); + let hash_trimmed = hash.trim(); + + let date = self.commit_date.clone().unwrap_or_default(); + let date_trimmed = date.trim(); + + if (hash_trimmed.len() + date_trimmed.len()) > 0 { + write!( + f, + "{} {}.{}.{} ({} {})", + self.crate_name, self.major, self.minor, self.patch, hash_trimmed, date_trimmed, + )?; + } else { + write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?; + } + + Ok(()) + } +} + +impl std::fmt::Debug for VersionInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}", + self.crate_name, self.major, self.minor, self.patch, + )?; + if self.commit_hash.is_some() { + write!( + f, + ", commit_hash: \"{}\", commit_date: \"{}\" }}", + self.commit_hash.clone().unwrap_or_default().trim(), + self.commit_date.clone().unwrap_or_default().trim() + )?; + } else { + write!(f, " }}")?; + } + + Ok(()) + } +} + +#[must_use] +pub fn get_commit_hash() -> Option { + std::process::Command::new("git") + .args(&["rev-parse", "--short", "HEAD"]) + .output() + .ok() + .and_then(|r| String::from_utf8(r.stdout).ok()) +} + +#[must_use] +pub fn get_commit_date() -> Option { + std::process::Command::new("git") + .args(&["log", "-1", "--date=short", "--pretty=format:%cd"]) + .output() + .ok() + .and_then(|r| String::from_utf8(r.stdout).ok()) +} + +#[must_use] +pub fn get_channel() -> Option { + match env::var("CFG_RELEASE_CHANNEL") { + Ok(channel) => Some(channel), + Err(_) => { + // if that failed, try to ask rustc -V, do some parsing and find out + match std::process::Command::new("rustc") + .arg("-V") + .output() + .ok() + .and_then(|r| String::from_utf8(r.stdout).ok()) + { + Some(rustc_output) => { + if rustc_output.contains("beta") { + Some(String::from("beta")) + } else if rustc_output.contains("stable") { + Some(String::from("stable")) + } else { + // default to nightly if we fail to parse + Some(String::from("nightly")) + } + }, + // default to nightly + None => Some(String::from("nightly")), + } + }, + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_struct_local() { + let vi = get_version_info!(); + assert_eq!(vi.major, 0); + assert_eq!(vi.minor, 2); + assert_eq!(vi.patch, 0); + assert_eq!(vi.crate_name, "rustc_tools_util"); + // hard to make positive tests for these since they will always change + assert!(vi.commit_hash.is_none()); + assert!(vi.commit_date.is_none()); + } + + #[test] + fn test_display_local() { + let vi = get_version_info!(); + assert_eq!(vi.to_string(), "rustc_tools_util 0.2.0"); + } + + #[test] + fn test_debug_local() { + let vi = get_version_info!(); + let s = format!("{:?}", vi); + assert_eq!( + s, + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 0 }" + ); + } +} diff --git a/src/tools/clippy/rustfmt.toml b/src/tools/clippy/rustfmt.toml new file mode 100644 index 0000000000000..f1241e74b0a3d --- /dev/null +++ b/src/tools/clippy/rustfmt.toml @@ -0,0 +1,6 @@ +max_width = 120 +comment_width = 100 +match_block_trailing_comma = true +wrap_comments = true +edition = "2018" +error_on_line_overflow = true diff --git a/src/tools/clippy/setup-toolchain.sh b/src/tools/clippy/setup-toolchain.sh new file mode 100755 index 0000000000000..6038ed697f91e --- /dev/null +++ b/src/tools/clippy/setup-toolchain.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Set up the appropriate rustc toolchain + +set -e + +cd "$(dirname "$0")" + +RTIM_PATH=$(command -v rustup-toolchain-install-master) || INSTALLED=false +CARGO_HOME=${CARGO_HOME:-$HOME/.cargo} + +# Check if RTIM is not installed or installed in other locations not in ~/.cargo/bin +if [[ "$INSTALLED" == false || "$RTIM_PATH" == $CARGO_HOME/bin/rustup-toolchain-install-master ]]; then + cargo +nightly install rustup-toolchain-install-master +else + VERSION=$(rustup-toolchain-install-master -V | grep -o "[0-9.]*") + REMOTE=$(cargo +nightly search rustup-toolchain-install-master | grep -o "[0-9.]*") + echo "info: skipping updating rustup-toolchain-install-master at $RTIM_PATH" + echo " current version : $VERSION" + echo " remote version : $REMOTE" +fi + +RUST_COMMIT=$(git ls-remote https://github.com/rust-lang/rust master | awk '{print $1}') + +if rustc +master -Vv 2>/dev/null | grep -q "$RUST_COMMIT"; then + echo "info: master toolchain is up-to-date" + exit 0 +fi + +if [[ -n "$HOST_TOOLCHAIN" ]]; then + TOOLCHAIN=('--host' "$HOST_TOOLCHAIN") +else + TOOLCHAIN=() +fi + +rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -- "$RUST_COMMIT" +rustup override set master diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs new file mode 100644 index 0000000000000..d3a7e24937f95 --- /dev/null +++ b/src/tools/clippy/src/driver.rs @@ -0,0 +1,413 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![feature(rustc_private)] +#![feature(str_strip)] + +// FIXME: switch to something more ergonomic here, once available. +// (Currently there is no way to opt into sysroot crates without `extern crate`.) +#[allow(unused_extern_crates)] +extern crate rustc_driver; +#[allow(unused_extern_crates)] +extern crate rustc_errors; +#[allow(unused_extern_crates)] +extern crate rustc_interface; +#[allow(unused_extern_crates)] +extern crate rustc_middle; + +use rustc_interface::interface; +use rustc_middle::ty::TyCtxt; +use rustc_tools_util::VersionInfo; + +use lazy_static::lazy_static; +use std::borrow::Cow; +use std::env; +use std::ops::Deref; +use std::panic; +use std::path::{Path, PathBuf}; +use std::process::{exit, Command}; + +mod lintlist; + +/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If +/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`. +fn arg_value<'a, T: Deref>( + args: &'a [T], + find_arg: &str, + pred: impl Fn(&str) -> bool, +) -> Option<&'a str> { + let mut args = args.iter().map(Deref::deref); + while let Some(arg) = args.next() { + let mut arg = arg.splitn(2, '='); + if arg.next() != Some(find_arg) { + continue; + } + + match arg.next().or_else(|| args.next()) { + Some(v) if pred(v) => return Some(v), + _ => {}, + } + } + None +} + +#[test] +fn test_arg_value() { + let args = &["--bar=bar", "--foobar", "123", "--foo"]; + + assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); + assert_eq!(arg_value(args, "--bar", |_| false), None); + assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); + assert_eq!(arg_value(args, "--foo", |_| true), None); +} + +struct DefaultCallbacks; +impl rustc_driver::Callbacks for DefaultCallbacks {} + +struct ClippyCallbacks; +impl rustc_driver::Callbacks for ClippyCallbacks { + fn config(&mut self, config: &mut interface::Config) { + let previous = config.register_lints.take(); + config.register_lints = Some(Box::new(move |sess, mut lint_store| { + // technically we're ~guaranteed that this is none but might as well call anything that + // is there already. Certainly it can't hurt. + if let Some(previous) = &previous { + (previous)(sess, lint_store); + } + + let conf = clippy_lints::read_conf(&[], &sess); + clippy_lints::register_plugins(&mut lint_store, &sess, &conf); + clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf); + clippy_lints::register_renamed(&mut lint_store); + })); + + // 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 + // MIR passes can be enabled / disabled separately, we should figure out, what passes to + // use for Clippy. + config.opts.debugging_opts.mir_opt_level = 0; + } +} + +#[allow(clippy::find_map, clippy::filter_map)] +fn describe_lints() { + use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS}; + use std::collections::HashSet; + + println!( + " +Available lint options: + -W Warn about + -A Allow + -D Deny + -F Forbid (deny and all attempts to override) + +" + ); + + let lint_level = |lint: &Lint| { + LINT_LEVELS + .iter() + .find(|level_mapping| level_mapping.0 == lint.group) + .map(|(_, level)| match level { + Level::Allow => "allow", + Level::Warn => "warn", + Level::Deny => "deny", + }) + .unwrap() + }; + + let mut lints: Vec<_> = ALL_LINTS.iter().collect(); + // The sort doesn't case-fold but it's doubtful we care. + lints.sort_by_cached_key(|x: &&Lint| (lint_level(x), x.name)); + + let max_lint_name_len = lints + .iter() + .map(|lint| lint.name.len()) + .map(|len| len + "clippy::".len()) + .max() + .unwrap_or(0); + + let padded = |x: &str| { + let mut s = " ".repeat(max_lint_name_len - x.chars().count()); + s.push_str(x); + s + }; + + let scoped = |x: &str| format!("clippy::{}", x); + + let lint_groups: HashSet<_> = lints.iter().map(|lint| lint.group).collect(); + + println!("Lint checks provided by clippy:\n"); + println!(" {} {:7.7} meaning", padded("name"), "default"); + println!(" {} {:7.7} -------", padded("----"), "-------"); + + let print_lints = |lints: &[&Lint]| { + for lint in lints { + let name = lint.name.replace("_", "-"); + println!( + " {} {:7.7} {}", + padded(&scoped(&name)), + lint_level(lint), + lint.desc + ); + } + println!("\n"); + }; + + print_lints(&lints); + + let max_group_name_len = std::cmp::max( + "clippy::all".len(), + lint_groups + .iter() + .map(|group| group.len()) + .map(|len| len + "clippy::".len()) + .max() + .unwrap_or(0), + ); + + let padded_group = |x: &str| { + let mut s = " ".repeat(max_group_name_len - x.chars().count()); + s.push_str(x); + s + }; + + println!("Lint groups provided by clippy:\n"); + println!(" {} sub-lints", padded_group("name")); + println!(" {} ---------", padded_group("----")); + println!(" {} the set of all clippy lints", padded_group("clippy::all")); + + let print_lint_groups = || { + for group in lint_groups { + let name = group.to_lowercase().replace("_", "-"); + let desc = lints + .iter() + .filter(|&lint| lint.group == group) + .map(|lint| lint.name) + .map(|name| name.replace("_", "-")) + .collect::>() + .join(", "); + println!(" {} {}", padded_group(&scoped(&name)), desc); + } + println!("\n"); + }; + + print_lint_groups(); +} + +fn display_help() { + println!( + "\ +Checks a package to catch common mistakes and improve your Rust code. + +Usage: + cargo clippy [options] [--] [...] + +Common options: + -h, --help Print this message + -V, --version Print version info and exit + +Other options are the same as `cargo check`. + +To allow or deny a lint from the command line you can use `cargo clippy --` +with: + + -W --warn OPT Set lint warnings + -A --allow OPT Set lint allowed + -D --deny OPT Set lint denied + -F --forbid OPT Set lint forbidden + +You can use tool lints to allow or deny lints from your code, eg.: + + #[allow(clippy::needless_lifetimes)] +" + ); +} + +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new"; + +lazy_static! { + static ref ICE_HOOK: Box) + Sync + Send + 'static> = { + let hook = panic::take_hook(); + panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL))); + hook + }; +} + +fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { + // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace + (*ICE_HOOK)(info); + + // Separate the output with an empty line + eprintln!(); + + let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( + rustc_errors::ColorConfig::Auto, + None, + false, + false, + None, + false, + )); + let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + + // a .span_bug or .bug call has already printed what + // it wants to print. + if !info.payload().is::() { + let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic"); + handler.emit_diagnostic(&d); + } + + let version_info = rustc_tools_util::get_version_info!(); + + let xs: Vec> = vec![ + "the compiler unexpectedly panicked. this is a bug.".into(), + format!("we would appreciate a bug report: {}", bug_report_url).into(), + format!("Clippy version: {}", version_info).into(), + ]; + + for note in &xs { + handler.note_without_error(¬e); + } + + // If backtraces are enabled, also print the query stack + let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); + + if backtrace { + TyCtxt::try_print_query_stack(&handler); + } +} + +fn toolchain_path(home: Option, toolchain: Option) -> Option { + home.and_then(|home| { + toolchain.map(|toolchain| { + let mut path = PathBuf::from(home); + path.push("toolchains"); + path.push(toolchain); + path + }) + }) +} + +pub fn main() { + rustc_driver::init_rustc_env_logger(); + lazy_static::initialize(&ICE_HOOK); + exit(rustc_driver::catch_with_exit_code(move || { + let mut orig_args: Vec = env::args().collect(); + + if orig_args.iter().any(|a| a == "--version" || a == "-V") { + let version_info = rustc_tools_util::get_version_info!(); + println!("{}", version_info); + exit(0); + } + + // Get the sysroot, looking from most specific to this invocation to the least: + // - command line + // - runtime environment + // - SYSROOT + // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN + // - sysroot from rustc in the path + // - compile-time environment + // - SYSROOT + // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN + let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true); + let have_sys_root_arg = sys_root_arg.is_some(); + let sys_root = sys_root_arg + .map(PathBuf::from) + .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from)) + .or_else(|| { + let home = std::env::var("RUSTUP_HOME") + .or_else(|_| std::env::var("MULTIRUST_HOME")) + .ok(); + let toolchain = std::env::var("RUSTUP_TOOLCHAIN") + .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN")) + .ok(); + toolchain_path(home, toolchain) + }) + .or_else(|| { + Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output() + .ok() + .and_then(|out| String::from_utf8(out.stdout).ok()) + .map(|s| PathBuf::from(s.trim())) + }) + .or_else(|| option_env!("SYSROOT").map(PathBuf::from)) + .or_else(|| { + let home = option_env!("RUSTUP_HOME") + .or(option_env!("MULTIRUST_HOME")) + .map(ToString::to_string); + let toolchain = option_env!("RUSTUP_TOOLCHAIN") + .or(option_env!("MULTIRUST_TOOLCHAIN")) + .map(ToString::to_string); + toolchain_path(home, toolchain) + }) + .map(|pb| pb.to_string_lossy().to_string()) + .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"); + + // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. + // We're invoking the compiler programmatically, so we ignore this/ + let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref()); + + if wrapper_mode { + // we still want to be able to invoke it normally though + orig_args.remove(1); + } + + if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) { + display_help(); + exit(0); + } + + let should_describe_lints = || { + let args: Vec<_> = env::args().collect(); + args.windows(2).any(|args| { + args[1] == "help" + && match args[0].as_str() { + "-W" | "-A" | "-D" | "-F" => true, + _ => false, + } + }) + }; + + if !wrapper_mode && should_describe_lints() { + describe_lints(); + exit(0); + } + + // this conditional check for the --sysroot flag is there so users can call + // `clippy_driver` directly + // without having to pass --sysroot or anything + let mut args: Vec = orig_args.clone(); + if !have_sys_root_arg { + args.extend(vec!["--sysroot".into(), sys_root]); + }; + + // this check ensures that dependencies are built but not linted and the final + // crate is linted but not built + let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true") + || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none(); + + if clippy_enabled { + args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); + if let Ok(extra_args) = env::var("CLIPPY_ARGS") { + args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| { + if s.is_empty() { + None + } else { + Some(s.to_string()) + } + })); + } + } + let mut clippy = ClippyCallbacks; + let mut default = DefaultCallbacks; + let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = + if clippy_enabled { &mut clippy } else { &mut default }; + rustc_driver::run_compiler(&args, callbacks, None, None) + })) +} diff --git a/src/tools/clippy/src/lintlist/lint.rs b/src/tools/clippy/src/lintlist/lint.rs new file mode 100644 index 0000000000000..c817d83b33aeb --- /dev/null +++ b/src/tools/clippy/src/lintlist/lint.rs @@ -0,0 +1,27 @@ +/// Lint data parsed from the Clippy source code. +#[derive(Clone, PartialEq, Debug)] +pub struct Lint { + pub name: &'static str, + pub group: &'static str, + pub desc: &'static str, + pub deprecation: Option<&'static str>, + pub module: &'static str, +} + +#[derive(PartialOrd, PartialEq, Ord, Eq)] +pub enum Level { + Allow, + Warn, + Deny, +} + +pub const LINT_LEVELS: [(&str, Level); 8] = [ + ("correctness", Level::Deny), + ("style", Level::Warn), + ("complexity", Level::Warn), + ("perf", Level::Warn), + ("restriction", Level::Allow), + ("pedantic", Level::Allow), + ("nursery", Level::Allow), + ("cargo", Level::Allow), +]; diff --git a/src/tools/clippy/src/lintlist/mod.rs b/src/tools/clippy/src/lintlist/mod.rs new file mode 100644 index 0000000000000..9457a64f9c6b9 --- /dev/null +++ b/src/tools/clippy/src/lintlist/mod.rs @@ -0,0 +1,2605 @@ +//! This file is managed by `cargo dev update_lints`. Do not edit. + +use lazy_static::lazy_static; + +pub mod lint; +pub use lint::Level; +pub use lint::Lint; +pub use lint::LINT_LEVELS; + +lazy_static! { +// begin lint list, do not remove this comment, it’s used in `update_lints` +pub static ref ALL_LINTS: Vec = vec![ + Lint { + name: "absurd_extreme_comparisons", + group: "correctness", + desc: "a comparison with a maximum or minimum value that is always true or false", + deprecation: None, + module: "types", + }, + Lint { + name: "almost_swapped", + group: "correctness", + desc: "`foo = bar; bar = foo` sequence", + deprecation: None, + module: "swap", + }, + Lint { + name: "approx_constant", + group: "correctness", + desc: "the approximate of a known float constant (in `std::fXX::consts`)", + deprecation: None, + module: "approx_const", + }, + Lint { + name: "as_conversions", + group: "restriction", + desc: "using a potentially dangerous silent `as` conversion", + deprecation: None, + module: "as_conversions", + }, + Lint { + name: "assertions_on_constants", + group: "style", + desc: "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`", + deprecation: None, + module: "assertions_on_constants", + }, + Lint { + name: "assign_op_pattern", + group: "style", + desc: "assigning the result of an operation on a variable to that same variable", + deprecation: None, + module: "assign_ops", + }, + Lint { + name: "await_holding_lock", + group: "pedantic", + desc: "Inside an async function, holding a MutexGuard while calling await", + deprecation: None, + module: "await_holding_lock", + }, + Lint { + name: "bad_bit_mask", + group: "correctness", + desc: "expressions of the form `_ & mask == select` that will only ever return `true` or `false`", + deprecation: None, + module: "bit_mask", + }, + Lint { + name: "bind_instead_of_map", + group: "complexity", + desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`", + deprecation: None, + module: "methods", + }, + Lint { + name: "blacklisted_name", + group: "style", + desc: "usage of a blacklisted/placeholder name", + deprecation: None, + module: "blacklisted_name", + }, + Lint { + name: "blocks_in_if_conditions", + group: "style", + desc: "useless or complex blocks that can be eliminated in conditions", + deprecation: None, + module: "blocks_in_if_conditions", + }, + Lint { + name: "bool_comparison", + group: "complexity", + desc: "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`", + deprecation: None, + module: "needless_bool", + }, + Lint { + name: "borrow_interior_mutable_const", + group: "correctness", + desc: "referencing `const` with interior mutability", + deprecation: None, + module: "non_copy_const", + }, + Lint { + name: "borrowed_box", + group: "complexity", + desc: "a borrow of a boxed type", + deprecation: None, + module: "types", + }, + Lint { + name: "box_vec", + group: "perf", + desc: "usage of `Box>`, vector elements are already on the heap", + deprecation: None, + module: "types", + }, + Lint { + name: "boxed_local", + group: "perf", + desc: "using `Box` where unnecessary", + deprecation: None, + module: "escape", + }, + Lint { + name: "builtin_type_shadow", + group: "style", + desc: "shadowing a builtin type", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "cargo_common_metadata", + group: "cargo", + desc: "common metadata is defined in `Cargo.toml`", + deprecation: None, + module: "cargo_common_metadata", + }, + Lint { + name: "cast_lossless", + group: "pedantic", + desc: "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`", + deprecation: None, + module: "types", + }, + Lint { + name: "cast_possible_truncation", + group: "pedantic", + desc: "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`", + deprecation: None, + module: "types", + }, + Lint { + name: "cast_possible_wrap", + group: "pedantic", + desc: "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`", + deprecation: None, + module: "types", + }, + Lint { + name: "cast_precision_loss", + group: "pedantic", + desc: "casts that cause loss of precision, e.g., `x as f32` where `x: u64`", + deprecation: None, + module: "types", + }, + Lint { + name: "cast_ptr_alignment", + group: "correctness", + desc: "cast from a pointer to a more-strictly-aligned pointer", + deprecation: None, + module: "types", + }, + Lint { + name: "cast_ref_to_mut", + group: "correctness", + desc: "a cast of reference to a mutable pointer", + deprecation: None, + module: "types", + }, + Lint { + name: "cast_sign_loss", + group: "pedantic", + desc: "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`", + deprecation: None, + module: "types", + }, + Lint { + name: "char_lit_as_u8", + group: "complexity", + desc: "casting a character literal to `u8` truncates", + deprecation: None, + module: "types", + }, + Lint { + name: "chars_last_cmp", + group: "style", + desc: "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char", + deprecation: None, + module: "methods", + }, + Lint { + name: "chars_next_cmp", + group: "style", + desc: "using `.chars().next()` to check if a string starts with a char", + deprecation: None, + module: "methods", + }, + Lint { + name: "checked_conversions", + group: "pedantic", + desc: "`try_from` could replace manual bounds checking when casting", + deprecation: None, + module: "checked_conversions", + }, + Lint { + name: "clone_double_ref", + group: "correctness", + desc: "using `clone` on `&&T`", + deprecation: None, + module: "methods", + }, + Lint { + name: "clone_on_copy", + group: "complexity", + desc: "using `clone` on a `Copy` type", + deprecation: None, + module: "methods", + }, + Lint { + name: "clone_on_ref_ptr", + group: "restriction", + desc: "using \'clone\' on a ref-counted pointer", + deprecation: None, + module: "methods", + }, + Lint { + name: "cmp_nan", + group: "correctness", + desc: "comparisons to `NAN`, which will always return false, probably not intended", + deprecation: None, + module: "misc", + }, + Lint { + name: "cmp_null", + group: "style", + desc: "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead.", + deprecation: None, + module: "ptr", + }, + Lint { + name: "cmp_owned", + group: "perf", + desc: "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`", + deprecation: None, + module: "misc", + }, + Lint { + name: "cognitive_complexity", + group: "nursery", + desc: "functions that should be split up into multiple functions", + deprecation: None, + module: "cognitive_complexity", + }, + Lint { + name: "collapsible_if", + group: "style", + desc: "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)", + deprecation: None, + module: "collapsible_if", + }, + Lint { + name: "comparison_chain", + group: "style", + desc: "`if`s that can be rewritten with `match` and `cmp`", + deprecation: None, + module: "comparison_chain", + }, + Lint { + name: "copy_iterator", + group: "pedantic", + desc: "implementing `Iterator` on a `Copy` type", + deprecation: None, + module: "copy_iterator", + }, + Lint { + name: "crosspointer_transmute", + group: "complexity", + desc: "transmutes that have to or from types that are a pointer to the other", + deprecation: None, + module: "transmute", + }, + Lint { + name: "dbg_macro", + group: "restriction", + desc: "`dbg!` macro is intended as a debugging tool", + deprecation: None, + module: "dbg_macro", + }, + Lint { + name: "debug_assert_with_mut_call", + group: "nursery", + desc: "mutable arguments in `debug_assert{,_ne,_eq}!`", + deprecation: None, + module: "mutable_debug_assertion", + }, + Lint { + name: "decimal_literal_representation", + group: "restriction", + desc: "using decimal representation when hexadecimal would be better", + deprecation: None, + module: "literal_representation", + }, + Lint { + name: "declare_interior_mutable_const", + group: "correctness", + desc: "declaring `const` with interior mutability", + deprecation: None, + module: "non_copy_const", + }, + Lint { + name: "default_trait_access", + group: "pedantic", + desc: "checks for literal calls to `Default::default()`", + deprecation: None, + module: "default_trait_access", + }, + Lint { + name: "deprecated_cfg_attr", + group: "complexity", + desc: "usage of `cfg_attr(rustfmt)` instead of tool attributes", + deprecation: None, + module: "attrs", + }, + Lint { + name: "deprecated_semver", + group: "correctness", + desc: "use of `#[deprecated(since = \"x\")]` where x is not semver", + deprecation: None, + module: "attrs", + }, + Lint { + name: "deref_addrof", + group: "complexity", + desc: "use of `*&` or `*&mut` in an expression", + deprecation: None, + module: "reference", + }, + Lint { + name: "derive_hash_xor_eq", + group: "correctness", + desc: "deriving `Hash` but implementing `PartialEq` explicitly", + deprecation: None, + module: "derive", + }, + Lint { + name: "diverging_sub_expression", + group: "complexity", + desc: "whether an expression contains a diverging sub expression", + deprecation: None, + module: "eval_order_dependence", + }, + Lint { + name: "doc_markdown", + group: "pedantic", + desc: "presence of `_`, `::` or camel-case outside backticks in documentation", + deprecation: None, + module: "doc", + }, + Lint { + name: "double_comparisons", + group: "complexity", + desc: "unnecessary double comparisons that can be simplified", + deprecation: None, + module: "double_comparison", + }, + Lint { + name: "double_must_use", + group: "style", + desc: "`#[must_use]` attribute on a `#[must_use]`-returning function / method", + deprecation: None, + module: "functions", + }, + Lint { + name: "double_neg", + group: "style", + desc: "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "double_parens", + group: "complexity", + desc: "Warn on unnecessary double parentheses", + deprecation: None, + module: "double_parens", + }, + Lint { + name: "drop_bounds", + group: "correctness", + desc: "Bounds of the form `T: Drop` are useless", + deprecation: None, + module: "drop_bounds", + }, + Lint { + name: "drop_copy", + group: "correctness", + desc: "calls to `std::mem::drop` with a value that implements Copy", + deprecation: None, + module: "drop_forget_ref", + }, + Lint { + name: "drop_ref", + group: "correctness", + desc: "calls to `std::mem::drop` with a reference instead of an owned value", + deprecation: None, + module: "drop_forget_ref", + }, + Lint { + name: "duplicate_underscore_argument", + group: "style", + desc: "function arguments having names which only differ by an underscore", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "duration_subsec", + group: "complexity", + desc: "checks for calculation of subsecond microseconds or milliseconds", + deprecation: None, + module: "duration_subsec", + }, + Lint { + name: "else_if_without_else", + group: "restriction", + desc: "`if` expression with an `else if`, but without a final `else` branch", + deprecation: None, + module: "else_if_without_else", + }, + Lint { + name: "empty_enum", + group: "pedantic", + desc: "enum with no variants", + deprecation: None, + module: "empty_enum", + }, + Lint { + name: "empty_line_after_outer_attr", + group: "nursery", + desc: "empty line after outer attribute", + deprecation: None, + module: "attrs", + }, + Lint { + name: "empty_loop", + group: "style", + desc: "empty `loop {}`, which should block or sleep", + deprecation: None, + module: "loops", + }, + Lint { + name: "enum_clike_unportable_variant", + group: "correctness", + desc: "C-like enums that are `repr(isize/usize)` and have values that don\'t fit into an `i32`", + deprecation: None, + module: "enum_clike", + }, + Lint { + name: "enum_glob_use", + group: "pedantic", + desc: "use items that import all variants of an enum", + deprecation: None, + module: "wildcard_imports", + }, + Lint { + name: "enum_variant_names", + group: "style", + desc: "enums where all variants share a prefix/postfix", + deprecation: None, + module: "enum_variants", + }, + Lint { + name: "eq_op", + group: "correctness", + desc: "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)", + deprecation: None, + module: "eq_op", + }, + Lint { + name: "erasing_op", + group: "correctness", + desc: "using erasing operations, e.g., `x * 0` or `y & 0`", + deprecation: None, + module: "erasing_op", + }, + Lint { + name: "eval_order_dependence", + group: "complexity", + desc: "whether a variable read occurs before a write depends on sub-expression evaluation order", + deprecation: None, + module: "eval_order_dependence", + }, + Lint { + name: "excessive_precision", + group: "style", + desc: "excessive precision for float literal", + deprecation: None, + module: "float_literal", + }, + Lint { + name: "exit", + group: "restriction", + desc: "`std::process::exit` is called, terminating the program", + deprecation: None, + module: "exit", + }, + Lint { + name: "expect_fun_call", + group: "perf", + desc: "using any `expect` method with a function call", + deprecation: None, + module: "methods", + }, + Lint { + name: "expect_used", + group: "restriction", + desc: "using `.expect()` on `Result` or `Option`, which might be better handled", + deprecation: None, + module: "methods", + }, + Lint { + name: "expl_impl_clone_on_copy", + group: "pedantic", + desc: "implementing `Clone` explicitly on `Copy` types", + deprecation: None, + module: "derive", + }, + Lint { + name: "explicit_counter_loop", + group: "complexity", + desc: "for-looping with an explicit counter when `_.enumerate()` would do", + deprecation: None, + module: "loops", + }, + Lint { + name: "explicit_deref_methods", + group: "pedantic", + desc: "Explicit use of deref or deref_mut method while not in a method chain.", + deprecation: None, + module: "dereference", + }, + Lint { + name: "explicit_into_iter_loop", + group: "pedantic", + desc: "for-looping over `_.into_iter()` when `_` would do", + deprecation: None, + module: "loops", + }, + Lint { + name: "explicit_iter_loop", + group: "pedantic", + desc: "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do", + deprecation: None, + module: "loops", + }, + Lint { + name: "explicit_write", + group: "complexity", + desc: "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work", + deprecation: None, + module: "explicit_write", + }, + Lint { + name: "extra_unused_lifetimes", + group: "complexity", + desc: "unused lifetimes in function definitions", + deprecation: None, + module: "lifetimes", + }, + Lint { + name: "fallible_impl_from", + group: "nursery", + desc: "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`", + deprecation: None, + module: "fallible_impl_from", + }, + Lint { + name: "filetype_is_file", + group: "restriction", + desc: "`FileType::is_file` is not recommended to test for readable file type", + deprecation: None, + module: "methods", + }, + Lint { + name: "filter_map", + group: "pedantic", + desc: "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call", + deprecation: None, + module: "methods", + }, + Lint { + name: "filter_map_next", + group: "pedantic", + desc: "using combination of `filter_map` and `next` which can usually be written as a single method call", + deprecation: None, + module: "methods", + }, + Lint { + name: "filter_next", + group: "complexity", + desc: "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`", + deprecation: None, + module: "methods", + }, + Lint { + name: "find_map", + group: "pedantic", + desc: "using a combination of `find` and `map` can usually be written as a single method call", + deprecation: None, + module: "methods", + }, + Lint { + name: "flat_map_identity", + group: "complexity", + desc: "call to `flat_map` where `flatten` is sufficient", + deprecation: None, + module: "methods", + }, + Lint { + name: "float_arithmetic", + group: "restriction", + desc: "any floating-point arithmetic statement", + deprecation: None, + module: "arithmetic", + }, + Lint { + name: "float_cmp", + group: "correctness", + desc: "using `==` or `!=` on float values instead of comparing difference with an epsilon", + deprecation: None, + module: "misc", + }, + Lint { + name: "float_cmp_const", + group: "restriction", + desc: "using `==` or `!=` on float constants instead of comparing difference with an epsilon", + deprecation: None, + module: "misc", + }, + Lint { + name: "fn_address_comparisons", + group: "correctness", + desc: "comparison with an address of a function item", + deprecation: None, + module: "unnamed_address", + }, + Lint { + name: "fn_params_excessive_bools", + group: "pedantic", + desc: "using too many bools in function parameters", + deprecation: None, + module: "excessive_bools", + }, + Lint { + name: "fn_to_numeric_cast", + group: "style", + desc: "casting a function pointer to a numeric type other than usize", + deprecation: None, + module: "types", + }, + Lint { + name: "fn_to_numeric_cast_with_truncation", + group: "style", + desc: "casting a function pointer to a numeric type not wide enough to store the address", + deprecation: None, + module: "types", + }, + Lint { + name: "for_kv_map", + group: "style", + desc: "looping on a map using `iter` when `keys` or `values` would do", + deprecation: None, + module: "loops", + }, + Lint { + name: "for_loops_over_fallibles", + group: "correctness", + desc: "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`", + deprecation: None, + module: "loops", + }, + Lint { + name: "forget_copy", + group: "correctness", + desc: "calls to `std::mem::forget` with a value that implements Copy", + deprecation: None, + module: "drop_forget_ref", + }, + Lint { + name: "forget_ref", + group: "correctness", + desc: "calls to `std::mem::forget` with a reference instead of an owned value", + deprecation: None, + module: "drop_forget_ref", + }, + Lint { + name: "future_not_send", + group: "nursery", + desc: "public Futures must be Send", + deprecation: None, + module: "future_not_send", + }, + Lint { + name: "get_last_with_len", + group: "complexity", + desc: "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler", + deprecation: None, + module: "get_last_with_len", + }, + Lint { + name: "get_unwrap", + group: "restriction", + desc: "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead", + deprecation: None, + module: "methods", + }, + Lint { + name: "identity_op", + group: "complexity", + desc: "using identity operations, e.g., `x + 0` or `y / 1`", + deprecation: None, + module: "identity_op", + }, + Lint { + name: "if_let_mutex", + group: "correctness", + desc: "locking a `Mutex` in an `if let` block can cause deadlocks", + deprecation: None, + module: "if_let_mutex", + }, + Lint { + name: "if_let_some_result", + group: "style", + desc: "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead", + deprecation: None, + module: "if_let_some_result", + }, + Lint { + name: "if_not_else", + group: "pedantic", + desc: "`if` branches that could be swapped so no negation operation is necessary on the condition", + deprecation: None, + module: "if_not_else", + }, + Lint { + name: "if_same_then_else", + group: "correctness", + desc: "`if` with the same `then` and `else` blocks", + deprecation: None, + module: "copies", + }, + Lint { + name: "ifs_same_cond", + group: "correctness", + desc: "consecutive `if`s with the same condition", + deprecation: None, + module: "copies", + }, + Lint { + name: "implicit_hasher", + group: "pedantic", + desc: "missing generalization over different hashers", + deprecation: None, + module: "types", + }, + Lint { + name: "implicit_return", + group: "restriction", + desc: "use a return statement like `return expr` instead of an expression", + deprecation: None, + module: "implicit_return", + }, + Lint { + name: "implicit_saturating_sub", + group: "pedantic", + desc: "Perform saturating subtraction instead of implicitly checking lower bound of data type", + deprecation: None, + module: "implicit_saturating_sub", + }, + Lint { + name: "imprecise_flops", + group: "nursery", + desc: "usage of imprecise floating point operations", + deprecation: None, + module: "floating_point_arithmetic", + }, + Lint { + name: "inconsistent_digit_grouping", + group: "style", + desc: "integer literals with digits grouped inconsistently", + deprecation: None, + module: "literal_representation", + }, + Lint { + name: "indexing_slicing", + group: "restriction", + desc: "indexing/slicing usage", + deprecation: None, + module: "indexing_slicing", + }, + Lint { + name: "ineffective_bit_mask", + group: "correctness", + desc: "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`", + deprecation: None, + module: "bit_mask", + }, + Lint { + name: "inefficient_to_string", + group: "pedantic", + desc: "using `to_string` on `&&T` where `T: ToString`", + deprecation: None, + module: "methods", + }, + Lint { + name: "infallible_destructuring_match", + group: "style", + desc: "a `match` statement with a single infallible arm instead of a `let`", + deprecation: None, + module: "matches", + }, + Lint { + name: "infinite_iter", + group: "correctness", + desc: "infinite iteration", + deprecation: None, + module: "infinite_iter", + }, + Lint { + name: "inherent_to_string", + group: "style", + desc: "type implements inherent method `to_string()`, but should instead implement the `Display` trait", + deprecation: None, + module: "inherent_to_string", + }, + Lint { + name: "inherent_to_string_shadow_display", + group: "correctness", + desc: "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait", + deprecation: None, + module: "inherent_to_string", + }, + Lint { + name: "inline_always", + group: "pedantic", + desc: "use of `#[inline(always)]`", + deprecation: None, + module: "attrs", + }, + Lint { + name: "inline_fn_without_body", + group: "correctness", + desc: "use of `#[inline]` on trait methods without bodies", + deprecation: None, + module: "inline_fn_without_body", + }, + Lint { + name: "int_plus_one", + group: "complexity", + desc: "instead of using `x >= y + 1`, use `x > y`", + deprecation: None, + module: "int_plus_one", + }, + Lint { + name: "integer_arithmetic", + group: "restriction", + desc: "any integer arithmetic expression which could overflow or panic", + deprecation: None, + module: "arithmetic", + }, + Lint { + name: "integer_division", + group: "restriction", + desc: "integer division may cause loss of precision", + deprecation: None, + module: "integer_division", + }, + Lint { + name: "into_iter_on_ref", + group: "style", + desc: "using `.into_iter()` on a reference", + deprecation: None, + module: "methods", + }, + Lint { + name: "invalid_atomic_ordering", + group: "correctness", + desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences", + deprecation: None, + module: "atomic_ordering", + }, + Lint { + name: "invalid_regex", + group: "correctness", + desc: "invalid regular expressions", + deprecation: None, + module: "regex", + }, + Lint { + name: "invalid_upcast_comparisons", + group: "pedantic", + desc: "a comparison involving an upcast which is always true or false", + deprecation: None, + module: "types", + }, + Lint { + name: "items_after_statements", + group: "pedantic", + desc: "blocks where an item comes after a statement", + deprecation: None, + module: "items_after_statements", + }, + Lint { + name: "iter_cloned_collect", + group: "style", + desc: "using `.cloned().collect()` on slice to create a `Vec`", + deprecation: None, + module: "methods", + }, + Lint { + name: "iter_next_loop", + group: "correctness", + desc: "for-looping over `_.next()` which is probably not intended", + deprecation: None, + module: "loops", + }, + Lint { + name: "iter_nth", + group: "perf", + desc: "using `.iter().nth()` on a standard library type with O(1) element access", + deprecation: None, + module: "methods", + }, + Lint { + name: "iter_nth_zero", + group: "style", + desc: "replace `iter.nth(0)` with `iter.next()`", + deprecation: None, + module: "methods", + }, + Lint { + name: "iter_skip_next", + group: "style", + desc: "using `.skip(x).next()` on an iterator", + deprecation: None, + module: "methods", + }, + Lint { + name: "iterator_step_by_zero", + group: "correctness", + desc: "using `Iterator::step_by(0)`, which will panic at runtime", + deprecation: None, + module: "methods", + }, + Lint { + name: "just_underscores_and_digits", + group: "style", + desc: "unclear name", + deprecation: None, + module: "non_expressive_names", + }, + Lint { + name: "large_const_arrays", + group: "perf", + desc: "large non-scalar const array may cause performance overhead", + deprecation: None, + module: "large_const_arrays", + }, + Lint { + name: "large_digit_groups", + group: "pedantic", + desc: "grouping digits into groups that are too large", + deprecation: None, + module: "literal_representation", + }, + Lint { + name: "large_enum_variant", + group: "perf", + desc: "large size difference between variants on an enum", + deprecation: None, + module: "large_enum_variant", + }, + Lint { + name: "large_stack_arrays", + group: "pedantic", + desc: "allocating large arrays on stack may cause stack overflow", + deprecation: None, + module: "large_stack_arrays", + }, + Lint { + name: "len_without_is_empty", + group: "style", + desc: "traits or impls with a public `len` method but no corresponding `is_empty` method", + deprecation: None, + module: "len_zero", + }, + Lint { + name: "len_zero", + group: "style", + desc: "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead", + deprecation: None, + module: "len_zero", + }, + Lint { + name: "let_and_return", + group: "style", + desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block", + deprecation: None, + module: "returns", + }, + Lint { + name: "let_underscore_lock", + group: "correctness", + desc: "non-binding let on a synchronization lock", + deprecation: None, + module: "let_underscore", + }, + Lint { + name: "let_underscore_must_use", + group: "restriction", + desc: "non-binding let on a `#[must_use]` expression", + deprecation: None, + module: "let_underscore", + }, + Lint { + name: "let_unit_value", + group: "pedantic", + desc: "creating a `let` binding to a value of unit type, which usually can\'t be used afterwards", + deprecation: None, + module: "types", + }, + Lint { + name: "linkedlist", + group: "pedantic", + desc: "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`", + deprecation: None, + module: "types", + }, + Lint { + name: "logic_bug", + group: "correctness", + desc: "boolean expressions that contain terminals which can be eliminated", + deprecation: None, + module: "booleans", + }, + Lint { + name: "lossy_float_literal", + group: "restriction", + desc: "lossy whole number float literals", + deprecation: None, + module: "float_literal", + }, + Lint { + name: "macro_use_imports", + group: "pedantic", + desc: "#[macro_use] is no longer needed", + deprecation: None, + module: "macro_use", + }, + Lint { + name: "main_recursion", + group: "style", + desc: "recursion using the entrypoint", + deprecation: None, + module: "main_recursion", + }, + Lint { + name: "manual_async_fn", + group: "style", + desc: "manual implementations of `async` functions can be simplified using the dedicated syntax", + deprecation: None, + module: "manual_async_fn", + }, + Lint { + name: "manual_memcpy", + group: "perf", + desc: "manually copying items between slices", + deprecation: None, + module: "loops", + }, + Lint { + name: "manual_non_exhaustive", + group: "style", + desc: "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]", + deprecation: None, + module: "manual_non_exhaustive", + }, + Lint { + name: "manual_saturating_arithmetic", + group: "style", + desc: "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`", + deprecation: None, + module: "methods", + }, + Lint { + name: "manual_swap", + group: "complexity", + desc: "manual swap of two variables", + deprecation: None, + module: "swap", + }, + Lint { + name: "many_single_char_names", + group: "style", + desc: "too many single character bindings", + deprecation: None, + module: "non_expressive_names", + }, + Lint { + name: "map_clone", + group: "style", + desc: "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types", + deprecation: None, + module: "map_clone", + }, + Lint { + name: "map_entry", + group: "perf", + desc: "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`", + deprecation: None, + module: "entry", + }, + Lint { + name: "map_flatten", + group: "pedantic", + desc: "using combinations of `flatten` and `map` which can usually be written as a single method call", + deprecation: None, + module: "methods", + }, + Lint { + name: "map_unwrap_or", + group: "pedantic", + desc: "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`", + deprecation: None, + module: "methods", + }, + Lint { + name: "match_as_ref", + group: "complexity", + desc: "a `match` on an Option value instead of using `as_ref()` or `as_mut`", + deprecation: None, + module: "matches", + }, + Lint { + name: "match_bool", + group: "pedantic", + desc: "a `match` on a boolean expression instead of an `if..else` block", + deprecation: None, + module: "matches", + }, + Lint { + name: "match_on_vec_items", + group: "pedantic", + desc: "matching on vector elements can panic", + deprecation: None, + module: "match_on_vec_items", + }, + Lint { + name: "match_overlapping_arm", + group: "style", + desc: "a `match` with overlapping arms", + deprecation: None, + module: "matches", + }, + Lint { + name: "match_ref_pats", + group: "style", + desc: "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression", + deprecation: None, + module: "matches", + }, + Lint { + name: "match_same_arms", + group: "pedantic", + desc: "`match` with identical arm bodies", + deprecation: None, + module: "copies", + }, + Lint { + name: "match_single_binding", + group: "complexity", + desc: "a match with a single binding instead of using `let` statement", + deprecation: None, + module: "matches", + }, + Lint { + name: "match_wild_err_arm", + group: "style", + desc: "a `match` with `Err(_)` arm and take drastic actions", + deprecation: None, + module: "matches", + }, + Lint { + name: "maybe_infinite_iter", + group: "pedantic", + desc: "possible infinite iteration", + deprecation: None, + module: "infinite_iter", + }, + Lint { + name: "mem_discriminant_non_enum", + group: "correctness", + desc: "calling `mem::descriminant` on non-enum type", + deprecation: None, + module: "mem_discriminant", + }, + Lint { + name: "mem_forget", + group: "restriction", + desc: "`mem::forget` usage on `Drop` types, likely to cause memory leaks", + deprecation: None, + module: "mem_forget", + }, + Lint { + name: "mem_replace_option_with_none", + group: "style", + desc: "replacing an `Option` with `None` instead of `take()`", + deprecation: None, + module: "mem_replace", + }, + Lint { + name: "mem_replace_with_default", + group: "style", + desc: "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`", + deprecation: None, + module: "mem_replace", + }, + Lint { + name: "mem_replace_with_uninit", + group: "correctness", + desc: "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`", + deprecation: None, + module: "mem_replace", + }, + Lint { + name: "min_max", + group: "correctness", + desc: "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant", + deprecation: None, + module: "minmax", + }, + Lint { + name: "mismatched_target_os", + group: "correctness", + desc: "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`", + deprecation: None, + module: "attrs", + }, + Lint { + name: "misrefactored_assign_op", + group: "complexity", + desc: "having a variable on both sides of an assign op", + deprecation: None, + module: "assign_ops", + }, + Lint { + name: "missing_const_for_fn", + group: "nursery", + desc: "Lint functions definitions that could be made `const fn`", + deprecation: None, + module: "missing_const_for_fn", + }, + Lint { + name: "missing_docs_in_private_items", + group: "restriction", + desc: "detects missing documentation for public and private members", + deprecation: None, + module: "missing_doc", + }, + Lint { + name: "missing_errors_doc", + group: "pedantic", + desc: "`pub fn` returns `Result` without `# Errors` in doc comment", + deprecation: None, + module: "doc", + }, + Lint { + name: "missing_inline_in_public_items", + group: "restriction", + desc: "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)", + deprecation: None, + module: "missing_inline", + }, + Lint { + name: "missing_safety_doc", + group: "style", + desc: "`pub unsafe fn` without `# Safety` docs", + deprecation: None, + module: "doc", + }, + Lint { + name: "mistyped_literal_suffixes", + group: "correctness", + desc: "mistyped literal suffix", + deprecation: None, + module: "literal_representation", + }, + Lint { + name: "mixed_case_hex_literals", + group: "style", + desc: "hex literals whose letter digits are not consistently upper- or lowercased", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "module_inception", + group: "style", + desc: "modules that have the same name as their parent module", + deprecation: None, + module: "enum_variants", + }, + Lint { + name: "module_name_repetitions", + group: "pedantic", + desc: "type names prefixed/postfixed with their containing module\'s name", + deprecation: None, + module: "enum_variants", + }, + Lint { + name: "modulo_arithmetic", + group: "restriction", + desc: "any modulo arithmetic statement", + deprecation: None, + module: "modulo_arithmetic", + }, + Lint { + name: "modulo_one", + group: "correctness", + desc: "taking a number modulo 1, which always returns 0", + deprecation: None, + module: "misc", + }, + Lint { + name: "multiple_crate_versions", + group: "cargo", + desc: "multiple versions of the same crate being used", + deprecation: None, + module: "multiple_crate_versions", + }, + Lint { + name: "multiple_inherent_impl", + group: "restriction", + desc: "Multiple inherent impl that could be grouped", + deprecation: None, + module: "inherent_impl", + }, + Lint { + name: "must_use_candidate", + group: "pedantic", + desc: "function or method that could take a `#[must_use]` attribute", + deprecation: None, + module: "functions", + }, + Lint { + name: "must_use_unit", + group: "style", + desc: "`#[must_use]` attribute on a unit-returning function / method", + deprecation: None, + module: "functions", + }, + Lint { + name: "mut_from_ref", + group: "correctness", + desc: "fns that create mutable refs from immutable ref args", + deprecation: None, + module: "ptr", + }, + Lint { + name: "mut_mut", + group: "pedantic", + desc: "usage of double-mut refs, e.g., `&mut &mut ...`", + deprecation: None, + module: "mut_mut", + }, + Lint { + name: "mut_range_bound", + group: "complexity", + desc: "for loop over a range where one of the bounds is a mutable variable", + deprecation: None, + module: "loops", + }, + Lint { + name: "mutable_key_type", + group: "correctness", + desc: "Check for mutable `Map`/`Set` key type", + deprecation: None, + module: "mut_key", + }, + Lint { + name: "mutex_atomic", + group: "perf", + desc: "using a mutex where an atomic value could be used instead", + deprecation: None, + module: "mutex_atomic", + }, + Lint { + name: "mutex_integer", + group: "nursery", + desc: "using a mutex for an integer type", + deprecation: None, + module: "mutex_atomic", + }, + Lint { + name: "naive_bytecount", + group: "perf", + desc: "use of naive `.filter(|&x| x == y).count()` to count byte values", + deprecation: None, + module: "bytecount", + }, + Lint { + name: "needless_bool", + group: "complexity", + desc: "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`", + deprecation: None, + module: "needless_bool", + }, + Lint { + name: "needless_borrow", + group: "nursery", + desc: "taking a reference that is going to be automatically dereferenced", + deprecation: None, + module: "needless_borrow", + }, + Lint { + name: "needless_borrowed_reference", + group: "complexity", + desc: "taking a needless borrowed reference", + deprecation: None, + module: "needless_borrowed_ref", + }, + Lint { + name: "needless_collect", + group: "perf", + desc: "collecting an iterator when collect is not needed", + deprecation: None, + module: "loops", + }, + Lint { + name: "needless_continue", + group: "pedantic", + desc: "`continue` statements that can be replaced by a rearrangement of code", + deprecation: None, + module: "needless_continue", + }, + Lint { + name: "needless_doctest_main", + group: "style", + desc: "presence of `fn main() {` in code examples", + deprecation: None, + module: "doc", + }, + Lint { + name: "needless_lifetimes", + group: "complexity", + desc: "using explicit lifetimes for references in function arguments when elision rules would allow omitting them", + deprecation: None, + module: "lifetimes", + }, + Lint { + name: "needless_pass_by_value", + group: "pedantic", + desc: "functions taking arguments by value, but not consuming them in its body", + deprecation: None, + module: "needless_pass_by_value", + }, + Lint { + name: "needless_range_loop", + group: "style", + desc: "for-looping over a range of indices where an iterator over items would do", + deprecation: None, + module: "loops", + }, + Lint { + name: "needless_return", + group: "style", + desc: "using a return statement like `return expr;` where an expression would suffice", + deprecation: None, + module: "returns", + }, + Lint { + name: "needless_update", + group: "complexity", + desc: "using `Foo { ..base }` when there are no missing fields", + deprecation: None, + module: "needless_update", + }, + Lint { + name: "neg_cmp_op_on_partial_ord", + group: "complexity", + desc: "The use of negated comparison operators on partially ordered types may produce confusing code.", + deprecation: None, + module: "neg_cmp_op_on_partial_ord", + }, + Lint { + name: "neg_multiply", + group: "style", + desc: "multiplying integers with `-1`", + deprecation: None, + module: "neg_multiply", + }, + Lint { + name: "never_loop", + group: "correctness", + desc: "any loop that will always `break` or `return`", + deprecation: None, + module: "loops", + }, + Lint { + name: "new_ret_no_self", + group: "style", + desc: "not returning type containing `Self` in a `new` method", + deprecation: None, + module: "methods", + }, + Lint { + name: "new_without_default", + group: "style", + desc: "`fn new() -> Self` method without `Default` implementation", + deprecation: None, + module: "new_without_default", + }, + Lint { + name: "no_effect", + group: "complexity", + desc: "statements with no effect", + deprecation: None, + module: "no_effect", + }, + Lint { + name: "non_ascii_literal", + group: "pedantic", + desc: "using any literal non-ASCII chars in a string literal instead of using the `\\\\u` escape", + deprecation: None, + module: "unicode", + }, + Lint { + name: "nonminimal_bool", + group: "complexity", + desc: "boolean expressions that can be written more concisely", + deprecation: None, + module: "booleans", + }, + Lint { + name: "nonsensical_open_options", + group: "correctness", + desc: "nonsensical combination of options for opening a file", + deprecation: None, + module: "open_options", + }, + Lint { + name: "not_unsafe_ptr_arg_deref", + group: "correctness", + desc: "public functions dereferencing raw pointer arguments but not marked `unsafe`", + deprecation: None, + module: "functions", + }, + Lint { + name: "ok_expect", + group: "style", + desc: "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result", + deprecation: None, + module: "methods", + }, + Lint { + name: "op_ref", + group: "style", + desc: "taking a reference to satisfy the type constraints on `==`", + deprecation: None, + module: "eq_op", + }, + Lint { + name: "option_as_ref_deref", + group: "complexity", + desc: "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`", + deprecation: None, + module: "methods", + }, + Lint { + name: "option_env_unwrap", + group: "correctness", + desc: "using `option_env!(...).unwrap()` to get environment variable", + deprecation: None, + module: "option_env_unwrap", + }, + Lint { + name: "option_map_or_none", + group: "style", + desc: "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`", + deprecation: None, + module: "methods", + }, + Lint { + name: "option_map_unit_fn", + group: "complexity", + desc: "using `option.map(f)`, where `f` is a function or closure that returns `()`", + deprecation: None, + module: "map_unit_fn", + }, + Lint { + name: "option_option", + group: "pedantic", + desc: "usage of `Option>`", + deprecation: None, + module: "types", + }, + Lint { + name: "or_fun_call", + group: "perf", + desc: "using any `*or` method with a function call, which suggests `*or_else`", + deprecation: None, + module: "methods", + }, + Lint { + name: "out_of_bounds_indexing", + group: "correctness", + desc: "out of bounds constant indexing", + deprecation: None, + module: "indexing_slicing", + }, + Lint { + name: "overflow_check_conditional", + group: "complexity", + desc: "overflow checks inspired by C which are likely to panic", + deprecation: None, + module: "overflow_check_conditional", + }, + Lint { + name: "panic", + group: "restriction", + desc: "usage of the `panic!` macro", + deprecation: None, + module: "panic_unimplemented", + }, + Lint { + name: "panic_params", + group: "style", + desc: "missing parameters in `panic!` calls", + deprecation: None, + module: "panic_unimplemented", + }, + Lint { + name: "panicking_unwrap", + group: "correctness", + desc: "checks for calls of `unwrap[_err]()` that will always fail", + deprecation: None, + module: "unwrap", + }, + Lint { + name: "partialeq_ne_impl", + group: "complexity", + desc: "re-implementing `PartialEq::ne`", + deprecation: None, + module: "partialeq_ne_impl", + }, + Lint { + name: "path_buf_push_overwrite", + group: "nursery", + desc: "calling `push` with file system root on `PathBuf` can overwrite it", + deprecation: None, + module: "path_buf_push_overwrite", + }, + Lint { + name: "possible_missing_comma", + group: "correctness", + desc: "possible missing comma in array", + deprecation: None, + module: "formatting", + }, + Lint { + name: "precedence", + group: "complexity", + desc: "operations where precedence may be unclear", + deprecation: None, + module: "precedence", + }, + Lint { + name: "print_literal", + group: "style", + desc: "printing a literal with a format string", + deprecation: None, + module: "write", + }, + Lint { + name: "print_stdout", + group: "restriction", + desc: "printing on stdout", + deprecation: None, + module: "write", + }, + Lint { + name: "print_with_newline", + group: "style", + desc: "using `print!()` with a format string that ends in a single newline", + deprecation: None, + module: "write", + }, + Lint { + name: "println_empty_string", + group: "style", + desc: "using `println!(\"\")` with an empty string", + deprecation: None, + module: "write", + }, + Lint { + name: "ptr_arg", + group: "style", + desc: "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively", + deprecation: None, + module: "ptr", + }, + Lint { + name: "ptr_offset_with_cast", + group: "complexity", + desc: "unneeded pointer offset cast", + deprecation: None, + module: "ptr_offset_with_cast", + }, + Lint { + name: "pub_enum_variant_names", + group: "pedantic", + desc: "enums where all variants share a prefix/postfix", + deprecation: None, + module: "enum_variants", + }, + Lint { + name: "question_mark", + group: "style", + desc: "checks for expressions that could be replaced by the question mark operator", + deprecation: None, + module: "question_mark", + }, + Lint { + name: "range_minus_one", + group: "complexity", + desc: "`x..=(y-1)` reads better as `x..y`", + deprecation: None, + module: "ranges", + }, + Lint { + name: "range_plus_one", + group: "pedantic", + desc: "`x..(y+1)` reads better as `x..=y`", + deprecation: None, + module: "ranges", + }, + Lint { + name: "range_zip_with_len", + group: "complexity", + desc: "zipping iterator with a range when `enumerate()` would do", + deprecation: None, + module: "ranges", + }, + Lint { + name: "redundant_allocation", + group: "perf", + desc: "redundant allocation", + deprecation: None, + module: "types", + }, + Lint { + name: "redundant_clone", + group: "perf", + desc: "`clone()` of an owned value that is going to be dropped immediately", + deprecation: None, + module: "redundant_clone", + }, + Lint { + name: "redundant_closure", + group: "style", + desc: "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)", + deprecation: None, + module: "eta_reduction", + }, + Lint { + name: "redundant_closure_call", + group: "complexity", + desc: "throwaway closures called in the expression they are defined", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "redundant_closure_for_method_calls", + group: "pedantic", + desc: "redundant closures for method calls", + deprecation: None, + module: "eta_reduction", + }, + Lint { + name: "redundant_field_names", + group: "style", + desc: "checks for fields in struct literals where shorthands could be used", + deprecation: None, + module: "redundant_field_names", + }, + Lint { + name: "redundant_pattern", + group: "style", + desc: "using `name @ _` in a pattern", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "redundant_pattern_matching", + group: "style", + desc: "use the proper utility function avoiding an `if let`", + deprecation: None, + module: "redundant_pattern_matching", + }, + Lint { + name: "redundant_pub_crate", + group: "nursery", + desc: "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them.", + deprecation: None, + module: "redundant_pub_crate", + }, + Lint { + name: "redundant_static_lifetimes", + group: "style", + desc: "Using explicit `\'static` lifetime for constants or statics when elision rules would allow omitting them.", + deprecation: None, + module: "redundant_static_lifetimes", + }, + Lint { + name: "ref_in_deref", + group: "complexity", + desc: "Use of reference in auto dereference expression.", + deprecation: None, + module: "reference", + }, + Lint { + name: "regex_macro", + group: "style", + desc: "use of `regex!(_)` instead of `Regex::new(_)`", + deprecation: None, + module: "regex", + }, + Lint { + name: "rest_pat_in_fully_bound_structs", + group: "restriction", + desc: "a match on a struct that binds all fields but still uses the wildcard pattern", + deprecation: None, + module: "matches", + }, + Lint { + name: "result_map_or_into_option", + group: "style", + desc: "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`", + deprecation: None, + module: "methods", + }, + Lint { + name: "result_map_unit_fn", + group: "complexity", + desc: "using `result.map(f)`, where `f` is a function or closure that returns `()`", + deprecation: None, + module: "map_unit_fn", + }, + Lint { + name: "reversed_empty_ranges", + group: "correctness", + desc: "reversing the limits of range expressions, resulting in empty ranges", + deprecation: None, + module: "ranges", + }, + Lint { + name: "same_functions_in_if_condition", + group: "pedantic", + desc: "consecutive `if`s with the same function call", + deprecation: None, + module: "copies", + }, + Lint { + name: "search_is_some", + group: "complexity", + desc: "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`", + deprecation: None, + module: "methods", + }, + Lint { + name: "serde_api_misuse", + group: "correctness", + desc: "various things that will negatively affect your serde experience", + deprecation: None, + module: "serde_api", + }, + Lint { + name: "shadow_reuse", + group: "restriction", + desc: "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`", + deprecation: None, + module: "shadow", + }, + Lint { + name: "shadow_same", + group: "restriction", + desc: "rebinding a name to itself, e.g., `let mut x = &mut x`", + deprecation: None, + module: "shadow", + }, + Lint { + name: "shadow_unrelated", + group: "pedantic", + desc: "rebinding a name without even using the original value", + deprecation: None, + module: "shadow", + }, + Lint { + name: "short_circuit_statement", + group: "complexity", + desc: "using a short circuit boolean condition as a statement", + deprecation: None, + module: "misc", + }, + Lint { + name: "should_implement_trait", + group: "style", + desc: "defining a method that should be implementing a std trait", + deprecation: None, + module: "methods", + }, + Lint { + name: "similar_names", + group: "pedantic", + desc: "similarly named items and bindings", + deprecation: None, + module: "non_expressive_names", + }, + Lint { + name: "single_char_pattern", + group: "perf", + desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", + deprecation: None, + module: "methods", + }, + Lint { + name: "single_component_path_imports", + group: "style", + desc: "imports with single component path are redundant", + deprecation: None, + module: "single_component_path_imports", + }, + Lint { + name: "single_match", + group: "style", + desc: "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`", + deprecation: None, + module: "matches", + }, + Lint { + name: "single_match_else", + group: "pedantic", + desc: "a `match` statement with two arms where the second arm\'s pattern is a placeholder instead of a specific match pattern", + deprecation: None, + module: "matches", + }, + Lint { + name: "skip_while_next", + group: "complexity", + desc: "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`", + deprecation: None, + module: "methods", + }, + Lint { + name: "slow_vector_initialization", + group: "perf", + desc: "slow vector initialization", + deprecation: None, + module: "slow_vector_initialization", + }, + Lint { + name: "string_add", + group: "restriction", + desc: "using `x + ..` where x is a `String` instead of `push_str()`", + deprecation: None, + module: "strings", + }, + Lint { + name: "string_add_assign", + group: "pedantic", + desc: "using `x = x + ..` where x is a `String` instead of `push_str()`", + deprecation: None, + module: "strings", + }, + Lint { + name: "string_extend_chars", + group: "style", + desc: "using `x.extend(s.chars())` where s is a `&str` or `String`", + deprecation: None, + module: "methods", + }, + Lint { + name: "string_lit_as_bytes", + group: "style", + desc: "calling `as_bytes` on a string literal instead of using a byte string literal", + deprecation: None, + module: "strings", + }, + Lint { + name: "struct_excessive_bools", + group: "pedantic", + desc: "using too many bools in a struct", + deprecation: None, + module: "excessive_bools", + }, + Lint { + name: "suboptimal_flops", + group: "nursery", + desc: "usage of sub-optimal floating point operations", + deprecation: None, + module: "floating_point_arithmetic", + }, + Lint { + name: "suspicious_arithmetic_impl", + group: "correctness", + desc: "suspicious use of operators in impl of arithmetic trait", + deprecation: None, + module: "suspicious_trait_impl", + }, + Lint { + name: "suspicious_assignment_formatting", + group: "style", + desc: "suspicious formatting of `*=`, `-=` or `!=`", + deprecation: None, + module: "formatting", + }, + Lint { + name: "suspicious_else_formatting", + group: "style", + desc: "suspicious formatting of `else`", + deprecation: None, + module: "formatting", + }, + Lint { + name: "suspicious_map", + group: "complexity", + desc: "suspicious usage of map", + deprecation: None, + module: "methods", + }, + Lint { + name: "suspicious_op_assign_impl", + group: "correctness", + desc: "suspicious use of operators in impl of OpAssign trait", + deprecation: None, + module: "suspicious_trait_impl", + }, + Lint { + name: "suspicious_unary_op_formatting", + group: "style", + desc: "suspicious formatting of unary `-` or `!` on the RHS of a BinOp", + deprecation: None, + module: "formatting", + }, + Lint { + name: "tabs_in_doc_comments", + group: "style", + desc: "using tabs in doc comments is not recommended", + deprecation: None, + module: "tabs_in_doc_comments", + }, + Lint { + name: "temporary_assignment", + group: "complexity", + desc: "assignments to temporaries", + deprecation: None, + module: "temporary_assignment", + }, + Lint { + name: "temporary_cstring_as_ptr", + group: "correctness", + desc: "getting the inner pointer of a temporary `CString`", + deprecation: None, + module: "methods", + }, + Lint { + name: "to_digit_is_some", + group: "style", + desc: "`char.is_digit()` is clearer", + deprecation: None, + module: "to_digit_is_some", + }, + Lint { + name: "todo", + group: "restriction", + desc: "`todo!` should not be present in production code", + deprecation: None, + module: "panic_unimplemented", + }, + Lint { + name: "too_many_arguments", + group: "complexity", + desc: "functions with too many arguments", + deprecation: None, + module: "functions", + }, + Lint { + name: "too_many_lines", + group: "pedantic", + desc: "functions with too many lines", + deprecation: None, + module: "functions", + }, + Lint { + name: "toplevel_ref_arg", + group: "style", + desc: "an entire binding declared as `ref`, in a function argument or a `let` statement", + deprecation: None, + module: "misc", + }, + Lint { + name: "transmute_bytes_to_str", + group: "complexity", + desc: "transmutes from a `&[u8]` to a `&str`", + deprecation: None, + module: "transmute", + }, + Lint { + name: "transmute_float_to_int", + group: "complexity", + desc: "transmutes from a float to an integer", + deprecation: None, + module: "transmute", + }, + Lint { + name: "transmute_int_to_bool", + group: "complexity", + desc: "transmutes from an integer to a `bool`", + deprecation: None, + module: "transmute", + }, + Lint { + name: "transmute_int_to_char", + group: "complexity", + desc: "transmutes from an integer to a `char`", + deprecation: None, + module: "transmute", + }, + Lint { + name: "transmute_int_to_float", + group: "complexity", + desc: "transmutes from an integer to a float", + deprecation: None, + module: "transmute", + }, + Lint { + name: "transmute_ptr_to_ptr", + group: "complexity", + desc: "transmutes from a pointer to a pointer / a reference to a reference", + deprecation: None, + module: "transmute", + }, + Lint { + name: "transmute_ptr_to_ref", + group: "complexity", + desc: "transmutes from a pointer to a reference type", + deprecation: None, + module: "transmute", + }, + Lint { + name: "transmuting_null", + group: "correctness", + desc: "transmutes from a null pointer to a reference, which is undefined behavior", + deprecation: None, + module: "transmuting_null", + }, + Lint { + name: "trivial_regex", + group: "style", + desc: "trivial regular expressions", + deprecation: None, + module: "regex", + }, + Lint { + name: "trivially_copy_pass_by_ref", + group: "pedantic", + desc: "functions taking small copyable arguments by reference", + deprecation: None, + module: "trivially_copy_pass_by_ref", + }, + Lint { + name: "try_err", + group: "style", + desc: "return errors explicitly rather than hiding them behind a `?`", + deprecation: None, + module: "try_err", + }, + Lint { + name: "type_complexity", + group: "complexity", + desc: "usage of very complex types that might be better factored into `type` definitions", + deprecation: None, + module: "types", + }, + Lint { + name: "type_repetition_in_bounds", + group: "pedantic", + desc: "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`", + deprecation: None, + module: "trait_bounds", + }, + Lint { + name: "unicode_not_nfc", + group: "pedantic", + desc: "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)", + deprecation: None, + module: "unicode", + }, + Lint { + name: "unimplemented", + group: "restriction", + desc: "`unimplemented!` should not be present in production code", + deprecation: None, + module: "panic_unimplemented", + }, + Lint { + name: "uninit_assumed_init", + group: "correctness", + desc: "`MaybeUninit::uninit().assume_init()`", + deprecation: None, + module: "methods", + }, + Lint { + name: "unit_arg", + group: "complexity", + desc: "passing unit to a function", + deprecation: None, + module: "types", + }, + Lint { + name: "unit_cmp", + group: "correctness", + desc: "comparing unit values", + deprecation: None, + module: "types", + }, + Lint { + name: "unknown_clippy_lints", + group: "style", + desc: "unknown_lints for scoped Clippy lints", + deprecation: None, + module: "attrs", + }, + Lint { + name: "unnecessary_cast", + group: "complexity", + desc: "cast to the same type, e.g., `x as i32` where `x: i32`", + deprecation: None, + module: "types", + }, + Lint { + name: "unnecessary_filter_map", + group: "complexity", + desc: "using `filter_map` when a more succinct alternative exists", + deprecation: None, + module: "methods", + }, + Lint { + name: "unnecessary_fold", + group: "style", + desc: "using `fold` when a more succinct alternative exists", + deprecation: None, + module: "methods", + }, + Lint { + name: "unnecessary_mut_passed", + group: "style", + desc: "an argument passed as a mutable reference although the callee only demands an immutable reference", + deprecation: None, + module: "mut_reference", + }, + Lint { + name: "unnecessary_operation", + group: "complexity", + desc: "outer expressions with no effect", + deprecation: None, + module: "no_effect", + }, + Lint { + name: "unnecessary_unwrap", + group: "complexity", + desc: "checks for calls of `unwrap[_err]()` that cannot fail", + deprecation: None, + module: "unwrap", + }, + Lint { + name: "unneeded_field_pattern", + group: "restriction", + desc: "struct fields bound to a wildcard instead of using `..`", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "unneeded_wildcard_pattern", + group: "complexity", + desc: "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "unreachable", + group: "restriction", + desc: "`unreachable!` should not be present in production code", + deprecation: None, + module: "panic_unimplemented", + }, + Lint { + name: "unreadable_literal", + group: "pedantic", + desc: "long integer literal without underscores", + deprecation: None, + module: "literal_representation", + }, + Lint { + name: "unsafe_derive_deserialize", + group: "pedantic", + desc: "deriving `serde::Deserialize` on a type that has methods using `unsafe`", + deprecation: None, + module: "derive", + }, + Lint { + name: "unsafe_removed_from_name", + group: "style", + desc: "`unsafe` removed from API names on import", + deprecation: None, + module: "unsafe_removed_from_name", + }, + Lint { + name: "unseparated_literal_suffix", + group: "pedantic", + desc: "literals whose suffix is not separated by an underscore", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "unsound_collection_transmute", + group: "correctness", + desc: "transmute between collections of layout-incompatible types", + deprecation: None, + module: "transmute", + }, + Lint { + name: "unused_io_amount", + group: "correctness", + desc: "unused written/read amount", + deprecation: None, + module: "unused_io_amount", + }, + Lint { + name: "unused_self", + group: "pedantic", + desc: "methods that contain a `self` argument but don\'t use it", + deprecation: None, + module: "unused_self", + }, + Lint { + name: "unused_unit", + group: "style", + desc: "needless unit expression", + deprecation: None, + module: "returns", + }, + Lint { + name: "unwrap_used", + group: "restriction", + desc: "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`", + deprecation: None, + module: "methods", + }, + Lint { + name: "use_debug", + group: "restriction", + desc: "use of `Debug`-based formatting", + deprecation: None, + module: "write", + }, + Lint { + name: "use_self", + group: "nursery", + desc: "Unnecessary structure name repetition whereas `Self` is applicable", + deprecation: None, + module: "use_self", + }, + Lint { + name: "used_underscore_binding", + group: "pedantic", + desc: "using a binding which is prefixed with an underscore", + deprecation: None, + module: "misc", + }, + Lint { + name: "useless_asref", + group: "complexity", + desc: "using `as_ref` where the types before and after the call are the same", + deprecation: None, + module: "methods", + }, + Lint { + name: "useless_attribute", + group: "correctness", + desc: "use of lint attributes on `extern crate` items", + deprecation: None, + module: "attrs", + }, + Lint { + name: "useless_conversion", + group: "complexity", + desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type", + deprecation: None, + module: "useless_conversion", + }, + Lint { + name: "useless_format", + group: "complexity", + desc: "useless use of `format!`", + deprecation: None, + module: "format", + }, + Lint { + name: "useless_let_if_seq", + group: "nursery", + desc: "unidiomatic `let mut` declaration followed by initialization in `if`", + deprecation: None, + module: "let_if_seq", + }, + Lint { + name: "useless_transmute", + group: "nursery", + desc: "transmutes that have the same to and from types or could be a cast/coercion", + deprecation: None, + module: "transmute", + }, + Lint { + name: "useless_vec", + group: "perf", + desc: "useless `vec!`", + deprecation: None, + module: "vec", + }, + Lint { + name: "vec_box", + group: "complexity", + desc: "usage of `Vec>` where T: Sized, vector elements are already on the heap", + deprecation: None, + module: "types", + }, + Lint { + name: "verbose_bit_mask", + group: "style", + desc: "expressions where a bit mask is less readable than the corresponding method call", + deprecation: None, + module: "bit_mask", + }, + Lint { + name: "verbose_file_reads", + group: "restriction", + desc: "use of `File::read_to_end` or `File::read_to_string`", + deprecation: None, + module: "verbose_file_reads", + }, + Lint { + name: "vtable_address_comparisons", + group: "correctness", + desc: "comparison with an address of a trait vtable", + deprecation: None, + module: "unnamed_address", + }, + Lint { + name: "while_immutable_condition", + group: "correctness", + desc: "variables used within while expression are not mutated in the body", + deprecation: None, + module: "loops", + }, + Lint { + name: "while_let_loop", + group: "complexity", + desc: "`loop { if let { ... } else break }`, which can be written as a `while let` loop", + deprecation: None, + module: "loops", + }, + Lint { + name: "while_let_on_iterator", + group: "style", + desc: "using a while-let loop instead of a for loop on an iterator", + deprecation: None, + module: "loops", + }, + Lint { + name: "wildcard_dependencies", + group: "cargo", + desc: "wildcard dependencies being used", + deprecation: None, + module: "wildcard_dependencies", + }, + Lint { + name: "wildcard_enum_match_arm", + group: "restriction", + desc: "a wildcard enum match arm using `_`", + deprecation: None, + module: "matches", + }, + Lint { + name: "wildcard_imports", + group: "pedantic", + desc: "lint `use _::*` statements", + deprecation: None, + module: "wildcard_imports", + }, + Lint { + name: "wildcard_in_or_patterns", + group: "complexity", + desc: "a wildcard pattern used with others patterns in same match arm", + deprecation: None, + module: "matches", + }, + Lint { + name: "write_literal", + group: "style", + desc: "writing a literal with a format string", + deprecation: None, + module: "write", + }, + Lint { + name: "write_with_newline", + group: "style", + desc: "using `write!()` with a format string that ends in a single newline", + deprecation: None, + module: "write", + }, + Lint { + name: "writeln_empty_string", + group: "style", + desc: "using `writeln!(buf, \"\")` with an empty string", + deprecation: None, + module: "write", + }, + Lint { + name: "wrong_pub_self_convention", + group: "restriction", + desc: "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention", + deprecation: None, + module: "methods", + }, + Lint { + name: "wrong_self_convention", + group: "style", + desc: "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention", + deprecation: None, + module: "methods", + }, + Lint { + name: "wrong_transmute", + group: "correctness", + desc: "transmutes that are confusing at best, undefined behaviour at worst and always useless", + deprecation: None, + module: "transmute", + }, + Lint { + name: "zero_divided_by_zero", + group: "complexity", + desc: "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`", + deprecation: None, + module: "zero_div_zero", + }, + Lint { + name: "zero_prefixed_literal", + group: "complexity", + desc: "integer literals starting with `0`", + deprecation: None, + module: "misc_early", + }, + Lint { + name: "zero_ptr", + group: "style", + desc: "using `0 as *{const, mut} T`", + deprecation: None, + module: "misc", + }, + Lint { + name: "zero_width_space", + group: "correctness", + desc: "using a zero-width space in a string literal, which is confusing", + deprecation: None, + module: "unicode", + }, + Lint { + name: "zst_offset", + group: "correctness", + desc: "Check for offset calculations on raw pointers to zero-sized types", + deprecation: None, + module: "methods", + }, +]; +// end lint list, do not remove this comment, it’s used in `update_lints` +} diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs new file mode 100644 index 0000000000000..bc43a34ed5d4a --- /dev/null +++ b/src/tools/clippy/src/main.rs @@ -0,0 +1,219 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] + +use rustc_tools_util::VersionInfo; +use std::env; +use std::ffi::OsString; +use std::path::PathBuf; +use std::process::{self, Command}; + +const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code. + +Usage: + cargo clippy [options] [--] [...] + +Common options: + -h, --help Print this message + -V, --version Print version info and exit + +Other options are the same as `cargo check`. + +To allow or deny a lint from the command line you can use `cargo clippy --` +with: + + -W --warn OPT Set lint warnings + -A --allow OPT Set lint allowed + -D --deny OPT Set lint denied + -F --forbid OPT Set lint forbidden + +You can use tool lints to allow or deny lints from your code, eg.: + + #[allow(clippy::needless_lifetimes)] +"#; + +fn show_help() { + println!("{}", CARGO_CLIPPY_HELP); +} + +fn show_version() { + let version_info = rustc_tools_util::get_version_info!(); + println!("{}", version_info); +} + +pub fn main() { + // Check for version and help flags even when invoked as 'cargo-clippy' + if env::args().any(|a| a == "--help" || a == "-h") { + show_help(); + return; + } + + if env::args().any(|a| a == "--version" || a == "-V") { + show_version(); + return; + } + + if let Err(code) = process(env::args().skip(2)) { + process::exit(code); + } +} + +struct ClippyCmd { + unstable_options: bool, + cargo_subcommand: &'static str, + args: Vec, + clippy_args: String, +} + +impl ClippyCmd { + fn new(mut old_args: I) -> Self + where + I: Iterator, + { + let mut cargo_subcommand = "check"; + let mut unstable_options = false; + let mut args = vec![]; + + for arg in old_args.by_ref() { + match arg.as_str() { + "--fix" => { + cargo_subcommand = "fix"; + continue; + }, + "--" => break, + // Cover -Zunstable-options and -Z unstable-options + s if s.ends_with("unstable-options") => unstable_options = true, + _ => {}, + } + + args.push(arg); + } + + if cargo_subcommand == "fix" && !unstable_options { + panic!("Usage of `--fix` requires `-Z unstable-options`"); + } + + // Run the dogfood tests directly on nightly cargo. This is required due + // to a bug in rustup.rs when running cargo on custom toolchains. See issue #3118. + if env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) { + args.insert(0, "+nightly".to_string()); + } + + let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect(); + + ClippyCmd { + unstable_options, + cargo_subcommand, + args, + clippy_args, + } + } + + fn path_env(&self) -> &'static str { + if self.unstable_options { + "RUSTC_WORKSPACE_WRAPPER" + } else { + "RUSTC_WRAPPER" + } + } + + fn path() -> PathBuf { + let mut path = env::current_exe() + .expect("current executable path invalid") + .with_file_name("clippy-driver"); + + if cfg!(windows) { + path.set_extension("exe"); + } + + path + } + + fn target_dir() -> Option<(&'static str, OsString)> { + env::var_os("CLIPPY_DOGFOOD") + .map(|_| { + env::var_os("CARGO_MANIFEST_DIR").map_or_else( + || std::ffi::OsString::from("clippy_dogfood"), + |d| { + std::path::PathBuf::from(d) + .join("target") + .join("dogfood") + .into_os_string() + }, + ) + }) + .map(|p| ("CARGO_TARGET_DIR", p)) + } + + fn into_std_cmd(self) -> Command { + let mut cmd = Command::new("cargo"); + + cmd.env(self.path_env(), Self::path()) + .envs(ClippyCmd::target_dir()) + .env("CLIPPY_ARGS", self.clippy_args) + .arg(self.cargo_subcommand) + .args(&self.args); + + cmd + } +} + +fn process(old_args: I) -> Result<(), i32> +where + I: Iterator, +{ + let cmd = ClippyCmd::new(old_args); + + let mut cmd = cmd.into_std_cmd(); + + let exit_status = cmd + .spawn() + .expect("could not run cargo") + .wait() + .expect("failed to wait for cargo?"); + + if exit_status.success() { + Ok(()) + } else { + Err(exit_status.code().unwrap_or(-1)) + } +} + +#[cfg(test)] +mod tests { + use super::ClippyCmd; + + #[test] + #[should_panic] + fn fix_without_unstable() { + let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string); + let _ = ClippyCmd::new(args); + } + + #[test] + fn fix_unstable() { + let args = "cargo clippy --fix -Zunstable-options" + .split_whitespace() + .map(ToString::to_string); + let cmd = ClippyCmd::new(args); + assert_eq!("fix", cmd.cargo_subcommand); + assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); + assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options"))); + } + + #[test] + fn check() { + let args = "cargo clippy".split_whitespace().map(ToString::to_string); + let cmd = ClippyCmd::new(args); + assert_eq!("check", cmd.cargo_subcommand); + assert_eq!("RUSTC_WRAPPER", cmd.path_env()); + } + + #[test] + fn check_unstable() { + let args = "cargo clippy -Zunstable-options" + .split_whitespace() + .map(ToString::to_string); + let cmd = ClippyCmd::new(args); + assert_eq!("check", cmd.cargo_subcommand); + assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); + } +} diff --git a/src/tools/clippy/tests/auxiliary/test_macro.rs b/src/tools/clippy/tests/auxiliary/test_macro.rs new file mode 100644 index 0000000000000..624ca892add31 --- /dev/null +++ b/src/tools/clippy/tests/auxiliary/test_macro.rs @@ -0,0 +1,11 @@ +pub trait A {} + +macro_rules! __implicit_hasher_test_macro { + (impl< $($impl_arg:tt),* > for $kind:ty where $($bounds:tt)*) => { + __implicit_hasher_test_macro!( ($($impl_arg),*) ($kind) ($($bounds)*) ); + }; + + (($($impl_arg:tt)*) ($($kind_arg:tt)*) ($($bounds:tt)*)) => { + impl< $($impl_arg)* > test_macro::A for $($kind_arg)* where $($bounds)* { } + }; +} diff --git a/src/tools/clippy/tests/cargo/mod.rs b/src/tools/clippy/tests/cargo/mod.rs new file mode 100644 index 0000000000000..3c385343f7010 --- /dev/null +++ b/src/tools/clippy/tests/cargo/mod.rs @@ -0,0 +1,29 @@ +use lazy_static::lazy_static; +use std::env; +use std::path::PathBuf; + +lazy_static! { + pub static ref CARGO_TARGET_DIR: PathBuf = { + match env::var_os("CARGO_TARGET_DIR") { + Some(v) => v.into(), + None => env::current_dir().unwrap().join("target"), + } + }; + pub static ref TARGET_LIB: PathBuf = { + if let Some(path) = option_env!("TARGET_LIBS") { + path.into() + } else { + let mut dir = CARGO_TARGET_DIR.clone(); + if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { + dir.push(target); + } + dir.push(env!("PROFILE")); + dir + } + }; +} + +#[must_use] +pub fn is_rustc_test_suite() -> bool { + option_env!("RUSTC_TEST_SUITE").is_some() +} diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs new file mode 100644 index 0000000000000..de2cf6d7873f8 --- /dev/null +++ b/src/tools/clippy/tests/compile-test.rs @@ -0,0 +1,168 @@ +#![feature(test)] // compiletest_rs requires this attribute + +use compiletest_rs as compiletest; +use compiletest_rs::common::Mode as TestMode; + +use std::env::{self, set_var}; +use std::ffi::OsStr; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; + +mod cargo; + +fn host_lib() -> PathBuf { + if let Some(path) = option_env!("HOST_LIBS") { + PathBuf::from(path) + } else { + cargo::CARGO_TARGET_DIR.join(env!("PROFILE")) + } +} + +fn clippy_driver_path() -> PathBuf { + if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") { + PathBuf::from(path) + } else { + cargo::TARGET_LIB.join("clippy-driver") + } +} + +// When we'll want to use `extern crate ..` for a dependency that is used +// both by the crate and the compiler itself, we can't simply pass -L flags +// as we'll get a duplicate matching versions. Instead, disambiguate with +// `--extern dep=path`. +// See https://github.com/rust-lang/rust-clippy/issues/4015. +// +// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files. +// Because it would force-rebuild if the options passed to `build` command is not the same +// as what we manually pass to `cargo` invocation +fn third_party_crates() -> String { + use std::collections::HashMap; + static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"]; + let dep_dir = cargo::TARGET_LIB.join("deps"); + let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len()); + for entry in fs::read_dir(dep_dir).unwrap() { + let path = match entry { + Ok(entry) => entry.path(), + _ => continue, + }; + if let Some(name) = path.file_name().and_then(OsStr::to_str) { + for dep in CRATES { + if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") { + crates.entry(dep).or_insert(path); + break; + } + } + } + } + + let v: Vec<_> = crates + .into_iter() + .map(|(dep, path)| format!("--extern {}={}", dep, path.display())) + .collect(); + v.join(" ") +} + +fn default_config() -> compiletest::Config { + let mut config = compiletest::Config::default(); + + if let Ok(name) = env::var("TESTNAME") { + config.filter = Some(name); + } + + if let Some(path) = option_env!("RUSTC_LIB_PATH") { + let path = PathBuf::from(path); + config.run_lib_path = path.clone(); + config.compile_lib_path = path; + } + + config.target_rustcflags = Some(format!( + "-L {0} -L {1} -Dwarnings -Zui-testing {2}", + host_lib().join("deps").display(), + cargo::TARGET_LIB.join("deps").display(), + third_party_crates(), + )); + + config.build_base = if cargo::is_rustc_test_suite() { + // This make the stderr files go to clippy OUT_DIR on rustc repo build dir + let mut path = PathBuf::from(env!("OUT_DIR")); + path.push("test_build_base"); + path + } else { + host_lib().join("test_build_base") + }; + config.rustc_path = clippy_driver_path(); + config +} + +fn run_mode(cfg: &mut compiletest::Config) { + cfg.mode = TestMode::Ui; + cfg.src_base = Path::new("tests").join("ui"); + compiletest::run_tests(&cfg); +} + +#[allow(clippy::identity_conversion)] +fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec) -> Result { + let mut result = true; + let opts = compiletest::test_opts(config); + for dir in fs::read_dir(&config.src_base)? { + let dir = dir?; + if !dir.file_type()?.is_dir() { + continue; + } + let dir_path = dir.path(); + set_var("CARGO_MANIFEST_DIR", &dir_path); + for file in fs::read_dir(&dir_path)? { + let file = file?; + let file_path = file.path(); + if file.file_type()?.is_dir() { + continue; + } + if file_path.extension() != Some(OsStr::new("rs")) { + continue; + } + let paths = compiletest::common::TestPaths { + file: file_path, + base: config.src_base.clone(), + relative_dir: dir_path.file_name().unwrap().into(), + }; + let test_name = compiletest::make_test_name(&config, &paths); + let index = tests + .iter() + .position(|test| test.desc.name == test_name) + .expect("The test should be in there"); + result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?; + } + } + Ok(result) +} + +fn run_ui_toml(config: &mut compiletest::Config) { + config.mode = TestMode::Ui; + config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap(); + + let tests = compiletest::make_tests(&config); + + let res = run_ui_toml_tests(&config, tests); + match res { + Ok(true) => {}, + Ok(false) => panic!("Some tests failed"), + Err(e) => { + println!("I/O failure during tests: {:?}", e); + }, + } +} + +fn prepare_env() { + set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + set_var("CLIPPY_TESTS", "true"); + //set_var("RUST_BACKTRACE", "0"); +} + +#[test] +fn compile_test() { + prepare_env(); + let mut config = default_config(); + run_mode(&mut config); + run_ui_toml(&mut config); +} diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs new file mode 100644 index 0000000000000..81af3d3033b23 --- /dev/null +++ b/src/tools/clippy/tests/dogfood.rs @@ -0,0 +1,76 @@ +// Dogfood cannot run on Windows +#![cfg(not(windows))] + +use lazy_static::lazy_static; +use std::path::PathBuf; +use std::process::Command; + +mod cargo; + +lazy_static! { + static ref CLIPPY_PATH: PathBuf = cargo::TARGET_LIB.join("cargo-clippy"); +} + +#[test] +fn dogfood_clippy() { + // run clippy on itself and fail the test if lint warnings are reported + if cargo::is_rustc_test_suite() { + return; + } + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + let output = Command::new(&*CLIPPY_PATH) + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::internal"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); +} + +#[test] +fn dogfood_subprojects() { + // run clippy on remaining subprojects and fail the test if lint warnings are reported + if cargo::is_rustc_test_suite() { + return; + } + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + for d in &[ + "clippy_workspace_tests", + "clippy_workspace_tests/src", + "clippy_workspace_tests/subcrate", + "clippy_workspace_tests/subcrate/src", + "clippy_dev", + "rustc_tools_util", + ] { + let output = Command::new(&*CLIPPY_PATH) + .current_dir(root_dir.join(d)) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + } +} diff --git a/src/tools/clippy/tests/fmt.rs b/src/tools/clippy/tests/fmt.rs new file mode 100644 index 0000000000000..3aff8741f6051 --- /dev/null +++ b/src/tools/clippy/tests/fmt.rs @@ -0,0 +1,39 @@ +use std::path::PathBuf; +use std::process::Command; + +#[test] +fn fmt() { + if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() { + return; + } + + // Skip this test if rustup nightly is unavailable + let rustup_output = Command::new("rustup") + .args(&["component", "list", "--toolchain", "nightly"]) + .output() + .unwrap(); + assert!(rustup_output.status.success()); + let component_output = String::from_utf8_lossy(&rustup_output.stdout); + if !component_output.contains("rustfmt") { + return; + } + + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let dev_dir = root_dir.join("clippy_dev"); + let target_dir = root_dir.join("target"); + let target_dir = target_dir.to_str().unwrap(); + let output = Command::new("cargo") + .current_dir(dev_dir) + .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"]) + .output() + .unwrap(); + + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!( + output.status.success(), + "Formatting check failed. Run `cargo dev fmt` to update formatting." + ); +} diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs new file mode 100644 index 0000000000000..a78273ce0da41 --- /dev/null +++ b/src/tools/clippy/tests/integration.rs @@ -0,0 +1,82 @@ +#![cfg(feature = "integration")] + +use std::env; +use std::ffi::OsStr; +use std::process::Command; + +#[cfg_attr(feature = "integration", test)] +fn integration_test() { + let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); + let repo_url = format!("https://github.com/{}", repo_name); + let crate_name = repo_name + .split('/') + .nth(1) + .expect("repo name should have format `/`"); + + let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path(); + repo_dir.push(crate_name); + + let st = Command::new("git") + .args(&[ + OsStr::new("clone"), + OsStr::new("--depth=1"), + OsStr::new(&repo_url), + OsStr::new(&repo_dir), + ]) + .status() + .expect("unable to run git"); + assert!(st.success()); + + let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = std::path::Path::new(&root_dir).join("target"); + let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy"); + + let output = Command::new(clippy_binary) + .current_dir(repo_dir) + .env("RUST_BACKTRACE", "full") + .env("CARGO_TARGET_DIR", target_dir) + .args(&[ + "clippy", + "--all-targets", + "--all-features", + "--", + "--cap-lints", + "warn", + "-Wclippy::pedantic", + "-Wclippy::nursery", + ]) + .output() + .expect("unable to run clippy"); + + let stderr = String::from_utf8_lossy(&output.stderr); + if stderr.contains("internal compiler error") { + let backtrace_start = stderr + .find("thread 'rustc' panicked at") + .expect("start of backtrace not found"); + let backtrace_end = stderr + .rfind("error: internal compiler error") + .expect("end of backtrace not found"); + + panic!( + "internal compiler error\nBacktrace:\n\n{}", + &stderr[backtrace_start..backtrace_end] + ); + } else if stderr.contains("query stack during panic") { + panic!("query stack during panic in the output"); + } else if stderr.contains("E0463") { + // Encountering E0463 (can't find crate for `x`) did _not_ cause the build to fail in the + // past. Even though it should have. That's why we explicitly panic here. + // See PR #3552 and issue #3523 for more background. + panic!("error: E0463"); + } else if stderr.contains("E0514") { + panic!("incompatible crate versions"); + } else if stderr.contains("failed to run `rustc` to learn about target-specific information") { + panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`"); + } + + match output.status.code() { + Some(0) => println!("Compilation successful"), + Some(code) => eprintln!("Compilation failed. Exit code: {}", code), + None => panic!("Process terminated by signal"), + } +} diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs new file mode 100644 index 0000000000000..d87bb4be3c3f9 --- /dev/null +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -0,0 +1,56 @@ +#![allow(clippy::assertions_on_constants)] + +use std::fs::{self, DirEntry}; +use std::path::Path; + +#[test] +fn test_missing_tests() { + let missing_files = explore_directory(Path::new("./tests")); + if !missing_files.is_empty() { + assert!( + false, + format!( + "Didn't see a test file for the following files:\n\n{}\n", + missing_files + .iter() + .map(|s| format!("\t{}", s)) + .collect::>() + .join("\n") + ) + ); + } +} + +/* +Test for missing files. + +Since rs files are alphabetically before stderr/stdout, we can sort by the full name +and iter in that order. If we've seen the file stem for the first time and it's not +a rust file, it means the rust file has to be missing. +*/ +fn explore_directory(dir: &Path) -> Vec { + let mut missing_files: Vec = Vec::new(); + let mut current_file = String::new(); + let mut files: Vec = fs::read_dir(dir).unwrap().filter_map(Result::ok).collect(); + files.sort_by_key(std::fs::DirEntry::path); + for entry in &files { + let path = entry.path(); + if path.is_dir() { + missing_files.extend(explore_directory(&path)); + } else { + let file_stem = path.file_stem().unwrap().to_str().unwrap().to_string(); + if let Some(ext) = path.extension() { + match ext.to_str().unwrap() { + "rs" => current_file = file_stem.clone(), + "stderr" | "stdout" => { + if file_stem != current_file { + missing_files.push(path.to_str().unwrap().to_string()); + } + }, + _ => continue, + }; + } + } + } + missing_files +} diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml b/src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml new file mode 100644 index 0000000000000..823e01a33b961 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml @@ -0,0 +1,2 @@ +fn this_is_obviously(not: a, toml: file) { +} diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs new file mode 100644 index 0000000000000..3b9458fc2840e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs @@ -0,0 +1,3 @@ +// error-pattern: error reading Clippy's configuration file + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr new file mode 100644 index 0000000000000..28c1a568a632b --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4 + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml b/src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml new file mode 100644 index 0000000000000..168675394d7f4 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml @@ -0,0 +1 @@ +blacklisted-names = 42 diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs new file mode 100644 index 0000000000000..8a0062423ad16 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs @@ -0,0 +1,4 @@ +// error-pattern: error reading Clippy's configuration file: `blacklisted-names` is expected to be a +// `Vec < String >` but is a `integer` + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr new file mode 100644 index 0000000000000..efd02bcbb6e28 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml new file mode 100644 index 0000000000000..ac47b195042eb --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml @@ -0,0 +1,6 @@ +# that one is an error +cyclomatic-complexity-threshold = 42 + +# that one is white-listed +[third-party] +clippy-feature = "nightly" diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs new file mode 100644 index 0000000000000..2577c1eef92bc --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -0,0 +1,4 @@ +// error-pattern: error reading Clippy's configuration file: found deprecated field +// `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr new file mode 100644 index 0000000000000..34267c0daf7c2 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml new file mode 100644 index 0000000000000..022eec3e0e2bf --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml @@ -0,0 +1 @@ +max-fn-params-bools = 1 diff --git a/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs new file mode 100644 index 0000000000000..42897b389edfc --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs @@ -0,0 +1,6 @@ +#![warn(clippy::fn_params_excessive_bools)] + +fn f(_: bool) {} +fn g(_: bool, _: bool) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr new file mode 100644 index 0000000000000..d05adc3d36e33 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr @@ -0,0 +1,11 @@ +error: more than 1 bools in function parameters + --> $DIR/test.rs:4:1 + | +LL | fn g(_: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings` + = help: consider refactoring bools into two-variant enums + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml b/src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml new file mode 100644 index 0000000000000..951dbb523d955 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml @@ -0,0 +1 @@ +too-many-lines-threshold = 1 diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs new file mode 100644 index 0000000000000..a47677a1f3a2e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs @@ -0,0 +1,45 @@ +#![warn(clippy::too_many_lines)] + +// This function should be considered one line. +fn many_comments_but_one_line_of_code() { + /* println!("This is good."); */ + // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* println!("This is good."); + println!("This is good."); + println!("This is good."); */ + println!("This is good."); +} + +// This should be considered two and a fail. +fn too_many_lines() { + println!("This is bad."); + println!("This is bad."); +} + +// This should be considered one line. +#[rustfmt::skip] +fn comment_starts_after_code() { + let _ = 5; /* closing comment. */ /* + this line shouldn't be counted theoretically. + */ +} + +// This should be considered one line. +fn comment_after_code() { + let _ = 5; /* this line should get counted once. */ +} + +// This should fail since it is technically two lines. +#[rustfmt::skip] +fn comment_before_code() { + let _ = "test"; + /* This comment extends to the front of + the code but this line should still count. */ let _ = 5; +} + +// This should be considered one line. +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr new file mode 100644 index 0000000000000..4b77ac551e770 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr @@ -0,0 +1,23 @@ +error: This function has a large number of lines. + --> $DIR/test.rs:18:1 + | +LL | / fn too_many_lines() { +LL | | println!("This is bad."); +LL | | println!("This is bad."); +LL | | } + | |_^ + | + = note: `-D clippy::too-many-lines` implied by `-D warnings` + +error: This function has a large number of lines. + --> $DIR/test.rs:38:1 + | +LL | / fn comment_before_code() { +LL | | let _ = "test"; +LL | | /* This comment extends to the front of +LL | | the code but this line should still count. */ let _ = 5; +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml new file mode 100644 index 0000000000000..a1dd6b2f0819c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml @@ -0,0 +1,3 @@ +# that one is white-listed +[third-party] +clippy-feature = "nightly" diff --git a/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs new file mode 100644 index 0000000000000..270b9c5c43c13 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs @@ -0,0 +1,3 @@ +// error-pattern: should give absolutely no error + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml new file mode 100644 index 0000000000000..3912ab542777d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml @@ -0,0 +1 @@ +max-struct-bools = 0 diff --git a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs new file mode 100644 index 0000000000000..242984680e163 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs @@ -0,0 +1,9 @@ +#![warn(clippy::struct_excessive_bools)] + +struct S { + a: bool, +} + +struct Foo {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr new file mode 100644 index 0000000000000..65861d10d0fdb --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr @@ -0,0 +1,13 @@ +error: more than 0 bools in a struct + --> $DIR/test.rs:3:1 + | +LL | / struct S { +LL | | a: bool, +LL | | } + | |_^ + | + = note: `-D clippy::struct-excessive-bools` implied by `-D warnings` + = help: consider using a state machine or refactoring bools into two-variant enums + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml new file mode 100644 index 0000000000000..6abe5a3bbc273 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml @@ -0,0 +1 @@ +blacklisted-names = ["toto", "tata", "titi"] diff --git a/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs new file mode 100644 index 0000000000000..cb35d0e8589d2 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs @@ -0,0 +1,20 @@ +#![allow(dead_code)] +#![allow(clippy::single_match)] +#![allow(unused_variables)] +#![warn(clippy::blacklisted_name)] + +fn test(toto: ()) {} + +fn main() { + let toto = 42; + let tata = 42; + let titi = 42; + + let tatab = 42; + let tatatataic = 42; + + match (42, Some(1337), Some(0)) { + (toto, Some(tata), titi @ Some(_)) => (), + _ => (), + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr new file mode 100644 index 0000000000000..84ba77851f77e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr @@ -0,0 +1,46 @@ +error: use of a blacklisted/placeholder name `toto` + --> $DIR/conf_french_blacklisted_name.rs:6:9 + | +LL | fn test(toto: ()) {} + | ^^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: use of a blacklisted/placeholder name `toto` + --> $DIR/conf_french_blacklisted_name.rs:9:9 + | +LL | let toto = 42; + | ^^^^ + +error: use of a blacklisted/placeholder name `tata` + --> $DIR/conf_french_blacklisted_name.rs:10:9 + | +LL | let tata = 42; + | ^^^^ + +error: use of a blacklisted/placeholder name `titi` + --> $DIR/conf_french_blacklisted_name.rs:11:9 + | +LL | let titi = 42; + | ^^^^ + +error: use of a blacklisted/placeholder name `toto` + --> $DIR/conf_french_blacklisted_name.rs:17:10 + | +LL | (toto, Some(tata), titi @ Some(_)) => (), + | ^^^^ + +error: use of a blacklisted/placeholder name `tata` + --> $DIR/conf_french_blacklisted_name.rs:17:21 + | +LL | (toto, Some(tata), titi @ Some(_)) => (), + | ^^^^ + +error: use of a blacklisted/placeholder name `titi` + --> $DIR/conf_french_blacklisted_name.rs:17:28 + | +LL | (toto, Some(tata), titi @ Some(_)) => (), + | ^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml new file mode 100644 index 0000000000000..3b96f1fd000bb --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml @@ -0,0 +1 @@ +trivial-copy-size-limit = 2 diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs new file mode 100644 index 0000000000000..19019a2541631 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs @@ -0,0 +1,21 @@ +// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" +// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" + +#![deny(clippy::trivially_copy_pass_by_ref)] +#![allow(clippy::many_single_char_names)] + +#[derive(Copy, Clone)] +struct Foo(u8); + +#[derive(Copy, Clone)] +struct Bar(u32); + +fn good(a: &mut u32, b: u32, c: &Bar, d: &u32) {} + +fn bad(x: &u16, y: &Foo) {} + +fn main() { + let (mut a, b, c, d, x, y) = (0, 0, Bar(0), 0, 0, Foo(0)); + good(&mut a, b, &c, &d); + bad(&x, &y); +} diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr new file mode 100644 index 0000000000000..912761a8f009c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr @@ -0,0 +1,20 @@ +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/test.rs:15:11 + | +LL | fn bad(x: &u16, y: &Foo) {} + | ^^^^ help: consider passing by value instead: `u16` + | +note: the lint level is defined here + --> $DIR/test.rs:4:9 + | +LL | #![deny(clippy::trivially_copy_pass_by_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/test.rs:15:20 + | +LL | fn bad(x: &u16, y: &Foo) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml new file mode 100644 index 0000000000000..554b87cc50be1 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml @@ -0,0 +1,6 @@ +# that one is an error +foobar = 42 + +# that one is white-listed +[third-party] +clippy-feature = "nightly" diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs new file mode 100644 index 0000000000000..a47569f62a322 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs @@ -0,0 +1,3 @@ +// error-pattern: error reading Clippy's configuration file: unknown key `foobar` + +fn main() {} 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 new file mode 100644 index 0000000000000..53970af41079d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1 + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/update-all-references.sh b/src/tools/clippy/tests/ui-toml/update-all-references.sh new file mode 100755 index 0000000000000..7028b251ea030 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/update-all-references.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# A script to update the references for all tests. The idea is that +# you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. You then +# run this script, which will copy those files over. If you find +# yourself manually editing a foo.stderr file, you're doing it wrong. +# +# See all `update-references.sh`, if you just want to update a single test. + +if [[ "$1" == "--help" || "$1" == "-h" ]]; then + echo "usage: $0" +fi + +BUILD_DIR=$PWD/target/debug/test_build_base +MY_DIR=$(dirname "$0") +cd "$MY_DIR" || exit +find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + diff --git a/src/tools/clippy/tests/ui-toml/update-references.sh b/src/tools/clippy/tests/ui-toml/update-references.sh new file mode 100755 index 0000000000000..50d42678734e9 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/update-references.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# A script to update the references for particular tests. The idea is +# that you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. This +# script will then copy that output and replace the "expected output" +# files. You can then commit the changes. +# +# If you find yourself manually editing a foo.stderr file, you're +# doing it wrong. + +if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then + echo "usage: $0 " + echo "" + echo "For example:" + echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" +fi + +MYDIR=$(dirname "$0") + +BUILD_DIR="$1" +shift + +while [[ "$1" != "" ]]; do + STDERR_NAME="${1/%.rs/.stderr}" + STDOUT_NAME="${1/%.rs/.stdout}" + shift + if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then + echo updating "$MYDIR"/"$STDOUT_NAME" + cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + fi + if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then + echo updating "$MYDIR"/"$STDERR_NAME" + cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + fi +done diff --git a/src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml b/src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml new file mode 100644 index 0000000000000..039ea47fc32d8 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml @@ -0,0 +1 @@ +vec-box-size-threshold = 4 diff --git a/src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs new file mode 100644 index 0000000000000..bf04bee16373c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs @@ -0,0 +1,15 @@ +struct S { + x: u64, +} + +struct C { + y: u16, +} + +struct Foo(Vec>); +struct Bar(Vec>); +struct Baz(Vec>); +struct BarBaz(Vec>); +struct FooBarBaz(Vec>); + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr new file mode 100644 index 0000000000000..3bdeca0bc8774 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr @@ -0,0 +1,22 @@ +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/test.rs:9:12 + | +LL | struct Foo(Vec>); + | ^^^^^^^^^^^^ help: try: `Vec` + | + = note: `-D clippy::vec-box` implied by `-D warnings` + +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/test.rs:10:12 + | +LL | struct Bar(Vec>); + | ^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/test.rs:13:18 + | +LL | struct FooBarBaz(Vec>); + | ^^^^^^^^^^^ help: try: `Vec` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml b/src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml new file mode 100644 index 0000000000000..42a1067b95edd --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml @@ -0,0 +1 @@ +single-char-binding-names-threshold = 0 diff --git a/src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs b/src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs new file mode 100644 index 0000000000000..22aaa242b9b9d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs @@ -0,0 +1,3 @@ +#![warn(clippy::many_single_char_names)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs new file mode 100644 index 0000000000000..d205b383d1ff6 --- /dev/null +++ b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs @@ -0,0 +1,61 @@ +#![warn(clippy::absurd_extreme_comparisons)] +#![allow( + unused, + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::needless_pass_by_value +)] + +#[rustfmt::skip] +fn main() { + const Z: u32 = 0; + let u: u32 = 42; + u <= 0; + u <= Z; + u < Z; + Z >= u; + Z > u; + u > u32::MAX; + u >= u32::MAX; + u32::MAX < u; + u32::MAX <= u; + 1-1 > u; + u >= !0; + u <= 12 - 2*6; + let i: i8 = 0; + i < -127 - 1; + i8::MAX >= i; + 3-7 < i32::MIN; + let b = false; + b >= true; + false > b; + u > 0; // ok + // this is handled by clippy::unit_cmp + () < {}; +} + +use std::cmp::{Ordering, PartialEq, PartialOrd}; + +#[derive(PartialEq, PartialOrd)] +pub struct U(u64); + +impl PartialEq for U { + fn eq(&self, other: &u32) -> bool { + self.eq(&U(u64::from(*other))) + } +} +impl PartialOrd for U { + fn partial_cmp(&self, other: &u32) -> Option { + self.partial_cmp(&U(u64::from(*other))) + } +} + +pub fn foo(val: U) -> bool { + val > u32::MAX +} + +pub fn bar(len: u64) -> bool { + // This is OK as we are casting from target sized to fixed size + len >= usize::MAX as u64 +} diff --git a/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr new file mode 100644 index 0000000000000..6de554378aaa9 --- /dev/null +++ b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr @@ -0,0 +1,147 @@ +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:14:5 + | +LL | u <= 0; + | ^^^^^^ + | + = note: `-D clippy::absurd-extreme-comparisons` implied by `-D warnings` + = help: because `0` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 0` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:15:5 + | +LL | u <= Z; + | ^^^^^^ + | + = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == Z` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:16:5 + | +LL | u < Z; + | ^^^^^ + | + = help: because `Z` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:17:5 + | +LL | Z >= u; + | ^^^^^^ + | + = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `Z == u` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:18:5 + | +LL | Z > u; + | ^^^^^ + | + = help: because `Z` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:19:5 + | +LL | u > u32::MAX; + | ^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:20:5 + | +LL | u >= u32::MAX; + | ^^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == u32::MAX` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:21:5 + | +LL | u32::MAX < u; + | ^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:22:5 + | +LL | u32::MAX <= u; + | ^^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u32::MAX == u` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:23:5 + | +LL | 1-1 > u; + | ^^^^^^^ + | + = help: because `1-1` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:24:5 + | +LL | u >= !0; + | ^^^^^^^ + | + = help: because `!0` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == !0` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:25:5 + | +LL | u <= 12 - 2*6; + | ^^^^^^^^^^^^^ + | + = help: because `12 - 2*6` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 12 - 2*6` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:27:5 + | +LL | i < -127 - 1; + | ^^^^^^^^^^^^ + | + = help: because `-127 - 1` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:28:5 + | +LL | i8::MAX >= i; + | ^^^^^^^^^^^^ + | + = help: because `i8::MAX` is the maximum value for this type, this comparison is always true + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:29:5 + | +LL | 3-7 < i32::MIN; + | ^^^^^^^^^^^^^^ + | + = help: because `i32::MIN` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:31:5 + | +LL | b >= true; + | ^^^^^^^^^ + | + = help: because `true` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `b == true` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:32:5 + | +LL | false > b; + | ^^^^^^^^^ + | + = help: because `false` is the minimum value for this type, this comparison is always false + +error: <-comparison of unit values detected. This will always be false + --> $DIR/absurd-extreme-comparisons.rs:35:5 + | +LL | () < {}; + | ^^^^^^^ + | + = note: `#[deny(clippy::unit_cmp)]` on by default + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/approx_const.rs b/src/tools/clippy/tests/ui/approx_const.rs new file mode 100644 index 0000000000000..fb57a0becbb25 --- /dev/null +++ b/src/tools/clippy/tests/ui/approx_const.rs @@ -0,0 +1,60 @@ +#[warn(clippy::approx_constant)] +#[allow(unused, clippy::shadow_unrelated, clippy::similar_names)] +fn main() { + let my_e = 2.7182; + let almost_e = 2.718; + let no_e = 2.71; + + let my_1_frac_pi = 0.3183; + let no_1_frac_pi = 0.31; + + let my_frac_1_sqrt_2 = 0.70710678; + let almost_frac_1_sqrt_2 = 0.70711; + let my_frac_1_sqrt_2 = 0.707; + + let my_frac_2_pi = 0.63661977; + let no_frac_2_pi = 0.636; + + let my_frac_2_sq_pi = 1.128379; + let no_frac_2_sq_pi = 1.128; + + let my_frac_pi_2 = 1.57079632679; + let no_frac_pi_2 = 1.5705; + + let my_frac_pi_3 = 1.04719755119; + let no_frac_pi_3 = 1.047; + + let my_frac_pi_4 = 0.785398163397; + let no_frac_pi_4 = 0.785; + + let my_frac_pi_6 = 0.523598775598; + let no_frac_pi_6 = 0.523; + + let my_frac_pi_8 = 0.3926990816987; + let no_frac_pi_8 = 0.392; + + let my_ln_10 = 2.302585092994046; + let no_ln_10 = 2.303; + + let my_ln_2 = 0.6931471805599453; + let no_ln_2 = 0.693; + + let my_log10_e = 0.4342944819032518; + let no_log10_e = 0.434; + + let my_log2_e = 1.4426950408889634; + let no_log2_e = 1.442; + + let log2_10 = 3.321928094887362; + let no_log2_10 = 3.321; + + let log10_2 = 0.301029995663981; + let no_log10_2 = 0.301; + + let my_pi = 3.1415; + let almost_pi = 3.14; + let no_pi = 3.15; + + let my_sq2 = 1.4142; + let no_sq2 = 1.414; +} diff --git a/src/tools/clippy/tests/ui/approx_const.stderr b/src/tools/clippy/tests/ui/approx_const.stderr new file mode 100644 index 0000000000000..98b85443f0b70 --- /dev/null +++ b/src/tools/clippy/tests/ui/approx_const.stderr @@ -0,0 +1,130 @@ +error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly + --> $DIR/approx_const.rs:4:16 + | +LL | let my_e = 2.7182; + | ^^^^^^ + | + = note: `-D clippy::approx-constant` implied by `-D warnings` + +error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly + --> $DIR/approx_const.rs:5:20 + | +LL | let almost_e = 2.718; + | ^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly + --> $DIR/approx_const.rs:8:24 + | +LL | let my_1_frac_pi = 0.3183; + | ^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly + --> $DIR/approx_const.rs:11:28 + | +LL | let my_frac_1_sqrt_2 = 0.70710678; + | ^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly + --> $DIR/approx_const.rs:12:32 + | +LL | let almost_frac_1_sqrt_2 = 0.70711; + | ^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly + --> $DIR/approx_const.rs:15:24 + | +LL | let my_frac_2_pi = 0.63661977; + | ^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly + --> $DIR/approx_const.rs:18:27 + | +LL | let my_frac_2_sq_pi = 1.128379; + | ^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly + --> $DIR/approx_const.rs:21:24 + | +LL | let my_frac_pi_2 = 1.57079632679; + | ^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly + --> $DIR/approx_const.rs:24:24 + | +LL | let my_frac_pi_3 = 1.04719755119; + | ^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly + --> $DIR/approx_const.rs:27:24 + | +LL | let my_frac_pi_4 = 0.785398163397; + | ^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly + --> $DIR/approx_const.rs:30:24 + | +LL | let my_frac_pi_6 = 0.523598775598; + | ^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly + --> $DIR/approx_const.rs:33:24 + | +LL | let my_frac_pi_8 = 0.3926990816987; + | ^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly + --> $DIR/approx_const.rs:36:20 + | +LL | let my_ln_10 = 2.302585092994046; + | ^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly + --> $DIR/approx_const.rs:39:19 + | +LL | let my_ln_2 = 0.6931471805599453; + | ^^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly + --> $DIR/approx_const.rs:42:22 + | +LL | let my_log10_e = 0.4342944819032518; + | ^^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly + --> $DIR/approx_const.rs:45:21 + | +LL | let my_log2_e = 1.4426950408889634; + | ^^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly + --> $DIR/approx_const.rs:48:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly + --> $DIR/approx_const.rs:51:19 + | +LL | let log10_2 = 0.301029995663981; + | ^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly + --> $DIR/approx_const.rs:54:17 + | +LL | let my_pi = 3.1415; + | ^^^^^^ + +error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly + --> $DIR/approx_const.rs:55:21 + | +LL | let almost_pi = 3.14; + | ^^^^ + +error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly + --> $DIR/approx_const.rs:58:18 + | +LL | let my_sq2 = 1.4142; + | ^^^^^^ + +error: aborting due to 21 previous errors + diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs new file mode 100644 index 0000000000000..e01ba0c64df32 --- /dev/null +++ b/src/tools/clippy/tests/ui/as_conversions.rs @@ -0,0 +1,7 @@ +#[warn(clippy::as_conversions)] + +fn main() { + let i = 0u32 as u64; + + let j = &i as *const u64 as *mut u64; +} diff --git a/src/tools/clippy/tests/ui/as_conversions.stderr b/src/tools/clippy/tests/ui/as_conversions.stderr new file mode 100644 index 0000000000000..312d3a7460ebe --- /dev/null +++ b/src/tools/clippy/tests/ui/as_conversions.stderr @@ -0,0 +1,27 @@ +error: using a potentially dangerous silent `as` conversion + --> $DIR/as_conversions.rs:4:13 + | +LL | let i = 0u32 as u64; + | ^^^^^^^^^^^ + | + = note: `-D clippy::as-conversions` implied by `-D warnings` + = help: consider using a safe wrapper for this conversion + +error: using a potentially dangerous silent `as` conversion + --> $DIR/as_conversions.rs:6:13 + | +LL | let j = &i as *const u64 as *mut u64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a safe wrapper for this conversion + +error: using a potentially dangerous silent `as` conversion + --> $DIR/as_conversions.rs:6:13 + | +LL | let j = &i as *const u64 as *mut u64; + | ^^^^^^^^^^^^^^^^ + | + = help: consider using a safe wrapper for this conversion + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs new file mode 100644 index 0000000000000..60d721c2f2049 --- /dev/null +++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs @@ -0,0 +1,29 @@ +macro_rules! assert_const { + ($len:expr) => { + assert!($len > 0); + debug_assert!($len < 0); + }; +} + +fn main() { + assert!(true); + assert!(false); + assert!(true, "true message"); + assert!(false, "false message"); + + let msg = "panic message"; + assert!(false, msg.to_uppercase()); + + const B: bool = true; + assert!(B); + + const C: bool = false; + assert!(C); + assert!(C, "C message"); + + debug_assert!(true); + // Don't lint this, since there is no better way for expressing "Only panic in debug mode". + debug_assert!(false); // #3948 + assert_const!(3); + assert_const!(-1); +} diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.stderr b/src/tools/clippy/tests/ui/assertions_on_constants.stderr new file mode 100644 index 0000000000000..8f09c8ce9d52a --- /dev/null +++ b/src/tools/clippy/tests/ui/assertions_on_constants.stderr @@ -0,0 +1,84 @@ +error: `assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:9:5 + | +LL | assert!(true); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::assertions-on-constants` implied by `-D warnings` + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false)` should probably be replaced + --> $DIR/assertions_on_constants.rs:10:5 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^^ + | + = help: use `panic!()` or `unreachable!()` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:11:5 + | +LL | assert!(true, "true message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false, "false message")` should probably be replaced + --> $DIR/assertions_on_constants.rs:12:5 + | +LL | assert!(false, "false message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!("false message")` or `unreachable!("false message")` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false, msg.to_uppercase())` should probably be replaced + --> $DIR/assertions_on_constants.rs:15:5 + | +LL | assert!(false, msg.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!(msg.to_uppercase())` or `unreachable!(msg.to_uppercase())` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:18:5 + | +LL | assert!(B); + | ^^^^^^^^^^^ + | + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false)` should probably be replaced + --> $DIR/assertions_on_constants.rs:21:5 + | +LL | assert!(C); + | ^^^^^^^^^^^ + | + = help: use `panic!()` or `unreachable!()` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false, "C message")` should probably be replaced + --> $DIR/assertions_on_constants.rs:22:5 + | +LL | assert!(C, "C message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!("C message")` or `unreachable!("C message")` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `debug_assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:24:5 + | +LL | debug_assert!(true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed new file mode 100644 index 0000000000000..52b1b3afe16fe --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(dead_code, unused_assignments)] +#[warn(clippy::assign_op_pattern)] +fn main() { + let mut a = 5; + a += 1; + a += 1; + a -= 1; + a *= 99; + a *= 42; + a /= 2; + a %= 5; + a &= 1; + a = 1 - a; + a = 5 / a; + a = 42 % a; + a = 6 << a; + let mut s = String::new(); + s += "bla"; +} diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs new file mode 100644 index 0000000000000..527a46b2c2bb9 --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(dead_code, unused_assignments)] +#[warn(clippy::assign_op_pattern)] +fn main() { + let mut a = 5; + a = a + 1; + a = 1 + a; + a = a - 1; + a = a * 99; + a = 42 * a; + a = a / 2; + a = a % 5; + a = a & 1; + a = 1 - a; + a = 5 / a; + a = 42 % a; + a = 6 << a; + let mut s = String::new(); + s = s + "bla"; +} diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr new file mode 100644 index 0000000000000..3486bd8da4d07 --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops.stderr @@ -0,0 +1,58 @@ +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:7:5 + | +LL | a = a + 1; + | ^^^^^^^^^ help: replace it with: `a += 1` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:8:5 + | +LL | a = 1 + a; + | ^^^^^^^^^ help: replace it with: `a += 1` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:9:5 + | +LL | a = a - 1; + | ^^^^^^^^^ help: replace it with: `a -= 1` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:10:5 + | +LL | a = a * 99; + | ^^^^^^^^^^ help: replace it with: `a *= 99` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:11:5 + | +LL | a = 42 * a; + | ^^^^^^^^^^ help: replace it with: `a *= 42` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:12:5 + | +LL | a = a / 2; + | ^^^^^^^^^ help: replace it with: `a /= 2` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:13:5 + | +LL | a = a % 5; + | ^^^^^^^^^ help: replace it with: `a %= 5` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:14:5 + | +LL | a = a & 1; + | ^^^^^^^^^ help: replace it with: `a &= 1` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:20:5 + | +LL | s = s + "bla"; + | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/assign_ops2.rs b/src/tools/clippy/tests/ui/assign_ops2.rs new file mode 100644 index 0000000000000..4703a8c777788 --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops2.rs @@ -0,0 +1,55 @@ +#[allow(unused_assignments)] +#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] +fn main() { + let mut a = 5; + a += a + 1; + a += 1 + a; + a -= a - 1; + a *= a * 99; + a *= 42 * a; + a /= a / 2; + a %= a % 5; + a &= a & 1; + a *= a * a; + 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)] +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(); + + // 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 new file mode 100644 index 0000000000000..70b15d18a568b --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops2.stderr @@ -0,0 +1,146 @@ +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:5:5 + | +LL | a += a + 1; + | ^^^^^^^^^^ + | + = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings` +help: Did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with + | +LL | a += 1; + | ^^^^^^ +help: or + | +LL | a = a + a + 1; + | ^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:6:5 + | +LL | a += 1 + a; + | ^^^^^^^^^^ + | +help: Did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with + | +LL | a += 1; + | ^^^^^^ +help: or + | +LL | a = a + 1 + a; + | ^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:7:5 + | +LL | a -= a - 1; + | ^^^^^^^^^^ + | +help: Did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with + | +LL | a -= 1; + | ^^^^^^ +help: or + | +LL | a = a - (a - 1); + | ^^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:8:5 + | +LL | a *= a * 99; + | ^^^^^^^^^^^ + | +help: Did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with + | +LL | a *= 99; + | ^^^^^^^ +help: or + | +LL | a = a * a * 99; + | ^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:9:5 + | +LL | a *= 42 * a; + | ^^^^^^^^^^^ + | +help: Did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with + | +LL | a *= 42; + | ^^^^^^^ +help: or + | +LL | a = a * 42 * a; + | ^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:10:5 + | +LL | a /= a / 2; + | ^^^^^^^^^^ + | +help: Did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with + | +LL | a /= 2; + | ^^^^^^ +help: or + | +LL | a = a / (a / 2); + | ^^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:11:5 + | +LL | a %= a % 5; + | ^^^^^^^^^^ + | +help: Did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with + | +LL | a %= 5; + | ^^^^^^ +help: or + | +LL | a = a % (a % 5); + | ^^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.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 &= 1; + | ^^^^^^ +help: or + | +LL | a = a & a & 1; + | ^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:13:5 + | +LL | a *= a * a; + | ^^^^^^^^^^ + | +help: Did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with + | +LL | a *= a; + | ^^^^^^ +help: or + | +LL | a = a * a * a; + | ^^^^^^^^^^^^^ + +error: manual implementation of an assign operation + --> $DIR/assign_ops2.rs:50:5 + | +LL | buf = buf + cows.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_bool.rs b/src/tools/clippy/tests/ui/atomic_ordering_bool.rs new file mode 100644 index 0000000000000..cdbde79b19ebf --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_bool.rs @@ -0,0 +1,25 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicBool, Ordering}; + +fn main() { + let x = AtomicBool::new(true); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(false, Ordering::Release); + x.store(false, Ordering::SeqCst); + x.store(false, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(false, Ordering::Acquire); + x.store(false, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_bool.stderr b/src/tools/clippy/tests/ui/atomic_ordering_bool.stderr new file mode 100644 index 0000000000000..397b893aed964 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_bool.stderr @@ -0,0 +1,35 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:14:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:15:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:23:20 + | +LL | x.store(false, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:24:20 + | +LL | x.store(false, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_fence.rs b/src/tools/clippy/tests/ui/atomic_ordering_fence.rs new file mode 100644 index 0000000000000..5ee5182ca051a --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_fence.rs @@ -0,0 +1,20 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{compiler_fence, fence, Ordering}; + +fn main() { + // Allowed fence ordering modes + fence(Ordering::Acquire); + fence(Ordering::Release); + fence(Ordering::AcqRel); + fence(Ordering::SeqCst); + + // Disallowed fence ordering modes + fence(Ordering::Relaxed); + + compiler_fence(Ordering::Acquire); + compiler_fence(Ordering::Release); + compiler_fence(Ordering::AcqRel); + compiler_fence(Ordering::SeqCst); + compiler_fence(Ordering::Relaxed); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_fence.stderr b/src/tools/clippy/tests/ui/atomic_ordering_fence.stderr new file mode 100644 index 0000000000000..3ceff27d9ad5e --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_fence.stderr @@ -0,0 +1,19 @@ +error: memory fences cannot have `Relaxed` ordering + --> $DIR/atomic_ordering_fence.rs:13:11 + | +LL | fence(Ordering::Relaxed); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` + +error: memory fences cannot have `Relaxed` ordering + --> $DIR/atomic_ordering_fence.rs:19:20 + | +LL | compiler_fence(Ordering::Relaxed); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_int.rs b/src/tools/clippy/tests/ui/atomic_ordering_int.rs new file mode 100644 index 0000000000000..40a00ba3de350 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_int.rs @@ -0,0 +1,86 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, Ordering}; + +fn main() { + // `AtomicI8` test cases + let x = AtomicI8::new(0); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicI16` test cases + let x = AtomicI16::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicI32` test cases + let x = AtomicI32::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicI64` test cases + let x = AtomicI64::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicIsize` test cases + let x = AtomicIsize::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_int.stderr b/src/tools/clippy/tests/ui/atomic_ordering_int.stderr new file mode 100644 index 0000000000000..bbaf234d3c9f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_int.stderr @@ -0,0 +1,163 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:15:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:16:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:24:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:25:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:33:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:34:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:39:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:40:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:48:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:49:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:54:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:55:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:63:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:64:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:69:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:70:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:78:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:79:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:84:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:85:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_ptr.rs b/src/tools/clippy/tests/ui/atomic_ordering_ptr.rs new file mode 100644 index 0000000000000..ecbb05c7fbc39 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_ptr.rs @@ -0,0 +1,27 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicPtr, Ordering}; + +fn main() { + let ptr = &mut 5; + let other_ptr = &mut 10; + let x = AtomicPtr::new(ptr); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(other_ptr, Ordering::Release); + x.store(other_ptr, Ordering::SeqCst); + x.store(other_ptr, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(other_ptr, Ordering::Acquire); + x.store(other_ptr, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_ptr.stderr b/src/tools/clippy/tests/ui/atomic_ordering_ptr.stderr new file mode 100644 index 0000000000000..558ae55518d5a --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_ptr.stderr @@ -0,0 +1,35 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:16:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:17:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:25:24 + | +LL | x.store(other_ptr, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:26:24 + | +LL | x.store(other_ptr, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_uint.rs b/src/tools/clippy/tests/ui/atomic_ordering_uint.rs new file mode 100644 index 0000000000000..a0d5d7c401035 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_uint.rs @@ -0,0 +1,86 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering}; + +fn main() { + // `AtomicU8` test cases + let x = AtomicU8::new(0); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicU16` test cases + let x = AtomicU16::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicU32` test cases + let x = AtomicU32::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicU64` test cases + let x = AtomicU64::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicUsize` test cases + let x = AtomicUsize::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_uint.stderr b/src/tools/clippy/tests/ui/atomic_ordering_uint.stderr new file mode 100644 index 0000000000000..5703135bcf1e2 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_uint.stderr @@ -0,0 +1,163 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:15:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:16:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:24:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:25:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:33:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:34:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:39:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:40:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:48:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:49:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:54:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:55:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:63:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:64:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:69:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:70:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:78:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:79:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:84:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:85:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/attrs.rs b/src/tools/clippy/tests/ui/attrs.rs new file mode 100644 index 0000000000000..91b65a43be77f --- /dev/null +++ b/src/tools/clippy/tests/ui/attrs.rs @@ -0,0 +1,43 @@ +#![warn(clippy::inline_always, clippy::deprecated_semver)] +#![allow(clippy::assertions_on_constants)] +#[inline(always)] +fn test_attr_lint() { + assert!(true) +} + +#[inline(always)] +fn false_positive_expr() { + unreachable!() +} + +#[inline(always)] +fn false_positive_stmt() { + unreachable!(); +} + +#[inline(always)] +fn empty_and_false_positive_stmt() { + unreachable!(); +} + +#[deprecated(since = "forever")] +pub const SOME_CONST: u8 = 42; + +#[deprecated(since = "1")] +pub const ANOTHER_CONST: u8 = 23; + +#[deprecated(since = "0.1.1")] +pub const YET_ANOTHER_CONST: u8 = 0; + +fn main() { + test_attr_lint(); + if false { + false_positive_expr() + } + if false { + false_positive_stmt() + } + if false { + empty_and_false_positive_stmt() + } +} diff --git a/src/tools/clippy/tests/ui/attrs.stderr b/src/tools/clippy/tests/ui/attrs.stderr new file mode 100644 index 0000000000000..39ddf6f226d95 --- /dev/null +++ b/src/tools/clippy/tests/ui/attrs.stderr @@ -0,0 +1,24 @@ +error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea + --> $DIR/attrs.rs:3:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-always` implied by `-D warnings` + +error: the since field must contain a semver-compliant version + --> $DIR/attrs.rs:23:14 + | +LL | #[deprecated(since = "forever")] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::deprecated-semver` implied by `-D warnings` + +error: the since field must contain a semver-compliant version + --> $DIR/attrs.rs:26:14 + | +LL | #[deprecated(since = "1")] + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/author.rs b/src/tools/clippy/tests/ui/author.rs new file mode 100644 index 0000000000000..0a1be35689670 --- /dev/null +++ b/src/tools/clippy/tests/ui/author.rs @@ -0,0 +1,4 @@ +fn main() { + #[clippy::author] + let x: char = 0x45 as char; +} diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout new file mode 100644 index 0000000000000..211d11c7c1aa3 --- /dev/null +++ b/src/tools/clippy/tests/ui/author.stdout @@ -0,0 +1,14 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind; + if let TyKind::Path(ref qp) = cast_ty.kind; + if match_qpath(qp, &["char"]); + if let ExprKind::Lit(ref lit) = expr.kind; + if let LitKind::Int(69, _) = lit.node; + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind; + if name.as_str() == "x"; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/blocks.rs b/src/tools/clippy/tests/ui/author/blocks.rs new file mode 100644 index 0000000000000..a8068436b70bf --- /dev/null +++ b/src/tools/clippy/tests/ui/author/blocks.rs @@ -0,0 +1,16 @@ +#![feature(stmt_expr_attributes)] +#![allow(redundant_semicolons, clippy::no_effect)] + +#[rustfmt::skip] +fn main() { + #[clippy::author] + { + ;;;; + } +} + +#[clippy::author] +fn foo() { + let x = 42i32; + -x; +} diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout new file mode 100644 index 0000000000000..c8ef75e48fc22 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -0,0 +1,13 @@ +if_chain! { + if let ExprKind::Block(ref block) = expr.kind; + if let Some(trailing_expr) = &block.expr; + if block.stmts.len() == 0; + then { + // report your lint here + } +} +if_chain! { + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/call.rs b/src/tools/clippy/tests/ui/author/call.rs new file mode 100644 index 0000000000000..e99c3c41dc4e1 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/call.rs @@ -0,0 +1,4 @@ +fn main() { + #[clippy::author] + let _ = ::std::cmp::min(3, 4); +} diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout new file mode 100644 index 0000000000000..4dccf666631a9 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/call.stdout @@ -0,0 +1,16 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Call(ref func, ref args) = init.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["{{root}}", "std", "cmp", "min"]); + if args.len() == 2; + if let ExprKind::Lit(ref lit) = args[0].kind; + if let LitKind::Int(3, _) = lit.node; + if let ExprKind::Lit(ref lit1) = args[1].kind; + if let LitKind::Int(4, _) = lit1.node; + if let PatKind::Wild = local.pat.kind; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/for_loop.rs b/src/tools/clippy/tests/ui/author/for_loop.rs new file mode 100644 index 0000000000000..b3dec876535c5 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/for_loop.rs @@ -0,0 +1,8 @@ +#![feature(stmt_expr_attributes)] + +fn main() { + #[clippy::author] + for y in 0..10 { + let z = y; + } +} diff --git a/src/tools/clippy/tests/ui/author/for_loop.stdout b/src/tools/clippy/tests/ui/author/for_loop.stdout new file mode 100644 index 0000000000000..81ede955347d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/for_loop.stdout @@ -0,0 +1,62 @@ +if_chain! { + if let ExprKind::DropTemps(ref expr) = expr.kind; + if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind; + if let ExprKind::Call(ref func, ref args) = expr1.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["{{root}}", "std", "iter", "IntoIterator", "into_iter"]); + if args.len() == 1; + if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind; + if match_qpath(path1, &["{{root}}", "std", "ops", "Range"]); + if fields.len() == 2; + // unimplemented: field checks + if arms.len() == 1; + if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind; + if let Some(trailing_expr) = &body.expr; + if body.stmts.len() == 4; + if let StmtKind::Local(ref local) = body.stmts[0].kind; + if let PatKind::Binding(BindingAnnotation::Mutable, _, name, None) = local.pat.kind; + if name.as_str() == "__next"; + if let StmtKind::Expr(ref e, _) = body.stmts[1].kind + if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind; + if let ExprKind::Call(ref func1, ref args1) = expr2.kind; + if let ExprKind::Path(ref path2) = func1.kind; + if match_qpath(path2, &["{{root}}", "std", "iter", "Iterator", "next"]); + if args1.len() == 1; + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind; + if let ExprKind::Path(ref path3) = inner.kind; + if match_qpath(path3, &["iter"]); + if arms1.len() == 2; + if let ExprKind::Assign(ref target, ref value, ref _span) = arms1[0].body.kind; + if let ExprKind::Path(ref path4) = target.kind; + if match_qpath(path4, &["__next"]); + if let ExprKind::Path(ref path5) = value.kind; + if match_qpath(path5, &["val"]); + if let PatKind::TupleStruct(ref path6, ref fields1, None) = arms1[0].pat.kind; + if match_qpath(path6, &["{{root}}", "std", "option", "Option", "Some"]); + if fields1.len() == 1; + // unimplemented: field checks + if let ExprKind::Break(ref destination, None) = arms1[1].body.kind; + if let PatKind::Path(ref path7) = arms1[1].pat.kind; + if match_qpath(path7, &["{{root}}", "std", "option", "Option", "None"]); + if let StmtKind::Local(ref local1) = body.stmts[2].kind; + if let Some(ref init) = local1.init; + if let ExprKind::Path(ref path8) = init.kind; + if match_qpath(path8, &["__next"]); + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind; + if name1.as_str() == "y"; + if let StmtKind::Expr(ref e1, _) = body.stmts[3].kind + if let ExprKind::Block(ref block) = e1.kind; + if let Some(trailing_expr1) = &block.expr; + if block.stmts.len() == 1; + if let StmtKind::Local(ref local2) = block.stmts[0].kind; + if let Some(ref init1) = local2.init; + if let ExprKind::Path(ref path9) = init1.kind; + if match_qpath(path9, &["y"]); + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name2, None) = local2.pat.kind; + if name2.as_str() == "z"; + if let PatKind::Binding(BindingAnnotation::Mutable, _, name3, None) = arms[0].pat.kind; + if name3.as_str() == "iter"; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs new file mode 100644 index 0000000000000..2e9cb1466d0b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/if.rs @@ -0,0 +1,10 @@ +#[allow(clippy::all)] + +fn main() { + #[clippy::author] + let _ = if true { + 1 == 1; + } else { + 2 == 2; + }; +} diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout new file mode 100644 index 0000000000000..c18d035953e53 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -0,0 +1,31 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let Some((ref cond, ref then, Some(else_))) = higher::if_block(&init); + if let ExprKind::Block(ref block) = else_.kind; + if let Some(trailing_expr) = &block.expr; + if block.stmts.len() == 1; + if let StmtKind::Semi(ref e, _) = block.stmts[0].kind + if let ExprKind::Binary(ref op, ref left, ref right) = e.kind; + if BinOpKind::Eq == op.node; + if let ExprKind::Lit(ref lit) = left.kind; + if let LitKind::Int(2, _) = lit.node; + if let ExprKind::Lit(ref lit1) = right.kind; + if let LitKind::Int(2, _) = lit1.node; + if let ExprKind::Lit(ref lit2) = cond.kind; + if let LitKind::Bool(true) = lit2.node; + if let ExprKind::Block(ref block1) = then.kind; + if let Some(trailing_expr1) = &block1.expr; + if block1.stmts.len() == 1; + if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind + if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind; + if BinOpKind::Eq == op1.node; + if let ExprKind::Lit(ref lit3) = left1.kind; + if let LitKind::Int(1, _) = lit3.node; + if let ExprKind::Lit(ref lit4) = right1.kind; + if let LitKind::Int(1, _) = lit4.node; + if let PatKind::Wild = local.pat.kind; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/issue_3849.rs b/src/tools/clippy/tests/ui/author/issue_3849.rs new file mode 100644 index 0000000000000..bae4570e539a1 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_3849.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] +#![allow(clippy::zero_ptr)] +#![allow(clippy::transmute_ptr_to_ref)] +#![allow(clippy::transmuting_null)] + +pub const ZPTR: *const usize = 0 as *const _; + +fn main() { + unsafe { + #[clippy::author] + let _: &i32 = std::mem::transmute(ZPTR); + let _: &i32 = std::mem::transmute(0 as *const i32); + } +} diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout new file mode 100644 index 0000000000000..65f93f3cdc06b --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout @@ -0,0 +1,14 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Call(ref func, ref args) = init.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["std", "mem", "transmute"]); + if args.len() == 1; + if let ExprKind::Path(ref path1) = args[0].kind; + if match_qpath(path1, &["ZPTR"]); + if let PatKind::Wild = local.pat.kind; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/matches.rs b/src/tools/clippy/tests/ui/author/matches.rs new file mode 100644 index 0000000000000..674e07ec2d3da --- /dev/null +++ b/src/tools/clippy/tests/ui/author/matches.rs @@ -0,0 +1,13 @@ +#![allow(clippy::let_and_return)] + +fn main() { + #[clippy::author] + let a = match 42 { + 16 => 5, + 17 => { + let x = 3; + x + }, + _ => 1, + }; +} diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout new file mode 100644 index 0000000000000..2e8f8227dca12 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -0,0 +1,33 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind; + if let ExprKind::Lit(ref lit) = expr.kind; + if let LitKind::Int(42, _) = lit.node; + if arms.len() == 3; + if let ExprKind::Lit(ref lit1) = arms[0].body.kind; + if let LitKind::Int(5, _) = lit1.node; + if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind + if let ExprKind::Lit(ref lit2) = lit_expr.kind; + if let LitKind::Int(16, _) = lit2.node; + if let ExprKind::Block(ref block) = arms[1].body.kind; + if let Some(trailing_expr) = &block.expr; + if block.stmts.len() == 1; + if let StmtKind::Local(ref local1) = block.stmts[0].kind; + if let Some(ref init1) = local1.init; + if let ExprKind::Lit(ref lit3) = init1.kind; + if let LitKind::Int(3, _) = lit3.node; + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind; + if name.as_str() == "x"; + if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind + if let ExprKind::Lit(ref lit4) = lit_expr1.kind; + if let LitKind::Int(17, _) = lit4.node; + if let ExprKind::Lit(ref lit5) = arms[2].body.kind; + if let LitKind::Int(1, _) = lit5.node; + if let PatKind::Wild = arms[2].pat.kind; + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind; + if name1.as_str() == "a"; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs b/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs new file mode 100644 index 0000000000000..869672d1eda5e --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs @@ -0,0 +1,8 @@ +#[macro_export] +macro_rules! undocd_unsafe { + () => { + pub unsafe fn oy_vey() { + unimplemented!(); + } + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs b/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs new file mode 100644 index 0000000000000..1eb77c531835a --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! implicit_hasher_fn { + () => { + pub fn f(input: &HashMap) {} + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs new file mode 100644 index 0000000000000..0bbb9534928ef --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -0,0 +1,58 @@ +#![allow(dead_code)] + +//! Used to test that certain lints don't trigger in imported external macros + +#[macro_export] +macro_rules! foofoo { + () => { + loop {} + }; +} + +#[macro_export] +macro_rules! must_use_unit { + () => { + #[must_use] + fn foo() {} + }; +} + +#[macro_export] +macro_rules! try_err { + () => { + pub fn try_err_fn() -> Result { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { + Err(err)? + } else { + Ok(2) + } + } + }; +} + +#[macro_export] +macro_rules! string_add { + () => { + let y = "".to_owned(); + let z = y + "..."; + }; +} + +#[macro_export] +macro_rules! take_external { + ($s:expr) => { + std::mem::replace($s, Default::default()) + }; +} + +#[macro_export] +macro_rules! option_env_unwrap_external { + ($env: expr) => { + option_env!($env).unwrap() + }; + ($env: expr, $message: expr) => { + option_env!($env).expect($message) + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs new file mode 100644 index 0000000000000..ed11c41e21c1a --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs @@ -0,0 +1,51 @@ +#![allow(dead_code, unused_variables)] + +/// Utility macro to test linting behavior in `option_methods()` +/// The lints included in `option_methods()` should not lint if the call to map is partially +/// within a macro +#[macro_export] +macro_rules! opt_map { + ($opt:expr, $map:expr) => { + ($opt).map($map) + }; +} + +/// Struct to generate false positive for Iterator-based lints +#[derive(Copy, Clone)] +pub struct IteratorFalsePositives { + pub foo: u32, +} + +impl IteratorFalsePositives { + pub fn filter(self) -> IteratorFalsePositives { + self + } + + pub fn next(self) -> IteratorFalsePositives { + self + } + + pub fn find(self) -> Option { + Some(self.foo) + } + + pub fn position(self) -> Option { + Some(self.foo) + } + + pub fn rposition(self) -> Option { + Some(self.foo) + } + + pub fn nth(self, n: usize) -> Option { + Some(self.foo) + } + + pub fn skip(self, _: usize) -> IteratorFalsePositives { + self + } + + pub fn skip_while(self) -> IteratorFalsePositives { + self + } +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs new file mode 100644 index 0000000000000..05ffb55f6207e --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -0,0 +1,22 @@ +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(repr128, proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::{quote, TokenStream}; + +#[proc_macro_derive(DeriveSomething)] +pub fn derive(_: TokenStream) -> TokenStream { + // Shound not trigger `used_underscore_binding` + let _inside_derive = 1; + assert_eq!(_inside_derive, _inside_derive); + + let output = quote! { + // Should not trigger `useless_attribute` + #[allow(dead_code)] + extern crate rustc_middle; + }; + output +} diff --git a/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs b/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs new file mode 100644 index 0000000000000..a8a85b4baefb7 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs @@ -0,0 +1,15 @@ +macro_rules! use_self { + ( + impl $ty:ident { + fn func(&$this:ident) { + [fields($($field:ident)*)] + } + } + ) => ( + impl $ty { + fn func(&$this) { + let $ty { $($field),* } = $this; + } + } + ) +} diff --git a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs new file mode 100644 index 0000000000000..414477aedd783 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -0,0 +1,21 @@ +pub use crate::extern_exports::*; + +pub fn extern_foo() {} +pub fn extern_bar() {} + +pub struct ExternA; + +pub mod inner { + pub mod inner_for_self_import { + pub fn inner_extern_foo() {} + pub fn inner_extern_bar() {} + } +} + +mod extern_exports { + pub fn extern_exported() {} + pub struct ExternExportedStruct; + pub enum ExternExportedEnum { + A, + } +} diff --git a/src/tools/clippy/tests/ui/await_holding_lock.rs b/src/tools/clippy/tests/ui/await_holding_lock.rs new file mode 100644 index 0000000000000..5c1fdd83efb0d --- /dev/null +++ b/src/tools/clippy/tests/ui/await_holding_lock.rs @@ -0,0 +1,64 @@ +// edition:2018 +#![warn(clippy::await_holding_lock)] + +use std::sync::Mutex; + +async fn bad(x: &Mutex) -> u32 { + let guard = x.lock().unwrap(); + baz().await +} + +async fn good(x: &Mutex) -> u32 { + { + let guard = x.lock().unwrap(); + let y = *guard + 1; + } + baz().await; + let guard = x.lock().unwrap(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &Mutex) -> u32 { + let first = baz().await; + + let guard = x.lock().unwrap(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &Mutex) -> u32 { + let first = baz().await; + + let second = { + let guard = x.lock().unwrap(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { + async move { + let guard = x.lock().unwrap(); + baz().await + } +} + +fn main() { + let m = Mutex::new(100); + good(&m); + bad(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr new file mode 100644 index 0000000000000..8c47cb37d8c99 --- /dev/null +++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr @@ -0,0 +1,63 @@ +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:7:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | + = note: `-D clippy::await-holding-lock` implied by `-D warnings` +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:7:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:28:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:28:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:41:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:41:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:52:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:52:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.fixed b/src/tools/clippy/tests/ui/bind_instead_of_map.fixed new file mode 100644 index 0000000000000..5815550d7a6a1 --- /dev/null +++ b/src/tools/clippy/tests/ui/bind_instead_of_map.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x; + let _ = x.map(|o| o + 1); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result = Ok(1); + let _ = x; +} + +pub fn foo() -> Option { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.rs b/src/tools/clippy/tests/ui/bind_instead_of_map.rs new file mode 100644 index 0000000000000..623b100a4ce7e --- /dev/null +++ b/src/tools/clippy/tests/ui/bind_instead_of_map.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x.and_then(Some); + let _ = x.and_then(|o| Some(o + 1)); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result = Ok(1); + let _ = x.and_then(Ok); +} + +pub fn foo() -> Option { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.stderr b/src/tools/clippy/tests/ui/bind_instead_of_map.stderr new file mode 100644 index 0000000000000..24c6b7f9ef32b --- /dev/null +++ b/src/tools/clippy/tests/ui/bind_instead_of_map.stderr @@ -0,0 +1,26 @@ +error: using `Option.and_then(Some)`, which is a no-op + --> $DIR/bind_instead_of_map.rs:8:13 + | +LL | let _ = x.and_then(Some); + | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` + | +note: the lint level is defined here + --> $DIR/bind_instead_of_map.rs:2:9 + | +LL | #![deny(clippy::bind_instead_of_map)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map.rs:9:13 + | +LL | let _ = x.and_then(|o| Some(o + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` + +error: using `Result.and_then(Ok)`, which is a no-op + --> $DIR/bind_instead_of_map.rs:15:13 + | +LL | let _ = x.and_then(Ok); + | ^^^^^^^^^^^^^^ help: use the expression directly: `x` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs new file mode 100644 index 0000000000000..91d9d11e3c110 --- /dev/null +++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs @@ -0,0 +1,61 @@ +#![deny(clippy::bind_instead_of_map)] +#![allow(clippy::blocks_in_if_conditions)] + +pub fn main() { + let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) }); + + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) }); + + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) }); + + hard_example(); + macro_example(); +} + +fn hard_example() { + Some("42").and_then(|s| { + if { + if s == "43" { + return Some(43); + } + s == "42" + } { + return Some(45); + } + match s.len() { + 10 => Some(2), + 20 => { + if foo() { + return { + if foo() { + return Some(20); + } + println!("foo"); + Some(3) + }; + } + Some(20) + }, + 40 => Some(30), + _ => Some(1), + } + }); +} + +fn foo() -> bool { + true +} + +macro_rules! m { + () => { + Some(10) + }; +} + +fn macro_example() { + let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) }); + let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); +} diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr new file mode 100644 index 0000000000000..50ce2f4051e00 --- /dev/null +++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr @@ -0,0 +1,73 @@ +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:5:13 + | +LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/bind_instead_of_map_multipart.rs:1:9 + | +LL | #![deny(clippy::bind_instead_of_map)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try this + | +LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:8:13 + | +LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:11:13 + | +LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); + | ^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^ + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:19:5 + | +LL | / Some("42").and_then(|s| { +LL | | if { +LL | | if s == "43" { +LL | | return Some(43); +... | +LL | | } +LL | | }); + | |______^ + | +help: try this + | +LL | Some("42").map(|s| { +LL | if { +LL | if s == "43" { +LL | return 43; +LL | } +LL | s == "42" + ... + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:60:13 + | +LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); + | ^^^ ^^^^ ^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/bit_masks.rs b/src/tools/clippy/tests/ui/bit_masks.rs new file mode 100644 index 0000000000000..cfb493fb52afb --- /dev/null +++ b/src/tools/clippy/tests/ui/bit_masks.rs @@ -0,0 +1,63 @@ +const THREE_BITS: i64 = 7; +const EVEN_MORE_REDIRECTION: i64 = THREE_BITS; + +#[warn(clippy::bad_bit_mask)] +#[allow( + clippy::ineffective_bit_mask, + clippy::identity_op, + clippy::no_effect, + clippy::unnecessary_operation +)] +fn main() { + let x = 5; + + x & 0 == 0; + x & 1 == 1; //ok, distinguishes bit 0 + x & 1 == 0; //ok, compared with zero + x & 2 == 1; + x | 0 == 0; //ok, equals x == 0 (maybe warn?) + x | 1 == 3; //ok, equals x == 2 || x == 3 + x | 3 == 3; //ok, equals x <= 3 + x | 3 == 2; + + x & 1 > 1; + x & 2 > 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0 + x & 2 < 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0 + x | 1 > 1; // ok (if a bit silly), equals x > 1 + x | 2 > 1; + x | 2 <= 2; // ok (if a bit silly), equals x <= 2 + + x & 192 == 128; // ok, tests for bit 7 and not bit 6 + x & 0xffc0 == 0xfe80; // ok + + // this also now works with constants + x & THREE_BITS == 8; + x | EVEN_MORE_REDIRECTION < 7; + + 0 & x == 0; + 1 | x > 1; + + // and should now also match uncommon usage + 1 < 2 | x; + 2 == 3 | x; + 1 == x & 2; + + x | 1 > 2; // no error, because we allowed ineffective bit masks + ineffective(); +} + +#[warn(clippy::ineffective_bit_mask)] +#[allow(clippy::bad_bit_mask, clippy::no_effect, clippy::unnecessary_operation)] +fn ineffective() { + let x = 5; + + x | 1 > 3; + x | 1 < 4; + x | 1 <= 3; + x | 1 >= 8; + + x | 1 > 2; // not an error (yet), better written as x >= 2 + x | 1 >= 7; // not an error (yet), better written as x >= 6 + x | 3 > 4; // not an error (yet), better written as x >= 4 + x | 4 <= 19; +} diff --git a/src/tools/clippy/tests/ui/bit_masks.stderr b/src/tools/clippy/tests/ui/bit_masks.stderr new file mode 100644 index 0000000000000..dc5ad6dfbdff9 --- /dev/null +++ b/src/tools/clippy/tests/ui/bit_masks.stderr @@ -0,0 +1,110 @@ +error: &-masking with zero + --> $DIR/bit_masks.rs:14:5 + | +LL | x & 0 == 0; + | ^^^^^^^^^^ + | + = note: `-D clippy::bad-bit-mask` implied by `-D warnings` + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/bit_masks.rs:14:5 + | +LL | x & 0 == 0; + | ^^^^^ + | + = note: `#[deny(clippy::erasing_op)]` on by default + +error: incompatible bit mask: `_ & 2` can never be equal to `1` + --> $DIR/bit_masks.rs:17:5 + | +LL | x & 2 == 1; + | ^^^^^^^^^^ + +error: incompatible bit mask: `_ | 3` can never be equal to `2` + --> $DIR/bit_masks.rs:21:5 + | +LL | x | 3 == 2; + | ^^^^^^^^^^ + +error: incompatible bit mask: `_ & 1` will never be higher than `1` + --> $DIR/bit_masks.rs:23:5 + | +LL | x & 1 > 1; + | ^^^^^^^^^ + +error: incompatible bit mask: `_ | 2` will always be higher than `1` + --> $DIR/bit_masks.rs:27:5 + | +LL | x | 2 > 1; + | ^^^^^^^^^ + +error: incompatible bit mask: `_ & 7` can never be equal to `8` + --> $DIR/bit_masks.rs:34:5 + | +LL | x & THREE_BITS == 8; + | ^^^^^^^^^^^^^^^^^^^ + +error: incompatible bit mask: `_ | 7` will never be lower than `7` + --> $DIR/bit_masks.rs:35:5 + | +LL | x | EVEN_MORE_REDIRECTION < 7; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: &-masking with zero + --> $DIR/bit_masks.rs:37:5 + | +LL | 0 & x == 0; + | ^^^^^^^^^^ + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/bit_masks.rs:37:5 + | +LL | 0 & x == 0; + | ^^^^^ + +error: incompatible bit mask: `_ | 2` will always be higher than `1` + --> $DIR/bit_masks.rs:41:5 + | +LL | 1 < 2 | x; + | ^^^^^^^^^ + +error: incompatible bit mask: `_ | 3` can never be equal to `2` + --> $DIR/bit_masks.rs:42:5 + | +LL | 2 == 3 | x; + | ^^^^^^^^^^ + +error: incompatible bit mask: `_ & 2` can never be equal to `1` + --> $DIR/bit_masks.rs:43:5 + | +LL | 1 == x & 2; + | ^^^^^^^^^^ + +error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly + --> $DIR/bit_masks.rs:54:5 + | +LL | x | 1 > 3; + | ^^^^^^^^^ + | + = note: `-D clippy::ineffective-bit-mask` implied by `-D warnings` + +error: ineffective bit mask: `x | 1` compared to `4`, is the same as x compared directly + --> $DIR/bit_masks.rs:55:5 + | +LL | x | 1 < 4; + | ^^^^^^^^^ + +error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly + --> $DIR/bit_masks.rs:56:5 + | +LL | x | 1 <= 3; + | ^^^^^^^^^^ + +error: ineffective bit mask: `x | 1` compared to `8`, is the same as x compared directly + --> $DIR/bit_masks.rs:57:5 + | +LL | x | 1 >= 8; + | ^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/blacklisted_name.rs b/src/tools/clippy/tests/ui/blacklisted_name.rs new file mode 100644 index 0000000000000..ca9d8d16b787d --- /dev/null +++ b/src/tools/clippy/tests/ui/blacklisted_name.rs @@ -0,0 +1,40 @@ +#![allow( + dead_code, + clippy::similar_names, + clippy::single_match, + clippy::toplevel_ref_arg, + unused_mut, + unused_variables +)] +#![warn(clippy::blacklisted_name)] + +fn test(foo: ()) {} + +fn main() { + let foo = 42; + let bar = 42; + let baz = 42; + + let barb = 42; + let barbaric = 42; + + match (42, Some(1337), Some(0)) { + (foo, Some(bar), baz @ Some(_)) => (), + _ => (), + } +} + +fn issue_1647(mut foo: u8) { + let mut bar = 0; + if let Some(mut baz) = Some(42) {} +} + +fn issue_1647_ref() { + let ref bar = 0; + if let Some(ref baz) = Some(42) {} +} + +fn issue_1647_ref_mut() { + let ref mut bar = 0; + if let Some(ref mut baz) = Some(42) {} +} diff --git a/src/tools/clippy/tests/ui/blacklisted_name.stderr b/src/tools/clippy/tests/ui/blacklisted_name.stderr new file mode 100644 index 0000000000000..44123829fb0f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/blacklisted_name.stderr @@ -0,0 +1,88 @@ +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:11:9 + | +LL | fn test(foo: ()) {} + | ^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:14:9 + | +LL | let foo = 42; + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:15:9 + | +LL | let bar = 42; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:16:9 + | +LL | let baz = 42; + | ^^^ + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:22:10 + | +LL | (foo, Some(bar), baz @ Some(_)) => (), + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:22:20 + | +LL | (foo, Some(bar), baz @ Some(_)) => (), + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:22:26 + | +LL | (foo, Some(bar), baz @ Some(_)) => (), + | ^^^ + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:27:19 + | +LL | fn issue_1647(mut foo: u8) { + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:28:13 + | +LL | let mut bar = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:29:21 + | +LL | if let Some(mut baz) = Some(42) {} + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:33:13 + | +LL | let ref bar = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:34:21 + | +LL | if let Some(ref baz) = Some(42) {} + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:38:17 + | +LL | let ref mut bar = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:39:25 + | +LL | if let Some(ref mut baz) = Some(42) {} + | ^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed new file mode 100644 index 0000000000000..14562c4d32c12 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed @@ -0,0 +1,74 @@ +// run-rustfix +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + let res = { + let x = 3; + x == 3 + }; if res { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if true { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .map(|val| { + let mut v = val * 2; + v -= 1; + v * 3 + }) + .is_some()); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs new file mode 100644 index 0000000000000..bda87650f6da2 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs @@ -0,0 +1,74 @@ +// run-rustfix +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + if { + let x = 3; + x == 3 + } { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if { true } { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if true && x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .map(|val| { + let mut v = val * 2; + v -= 1; + v * 3 + }) + .is_some()); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr new file mode 100644 index 0000000000000..9bdddc8e15248 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr @@ -0,0 +1,34 @@ +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_if_conditions.rs:26:5 + | +LL | / if { +LL | | let x = 3; +LL | | x == 3 +LL | | } { + | |_____^ + | + = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` +help: try + | +LL | let res = { +LL | let x = 3; +LL | x == 3 +LL | }; if res { + | + +error: omit braces around single expression condition + --> $DIR/blocks_in_if_conditions.rs:37:8 + | +LL | if { true } { + | ^^^^^^^^ help: try: `true` + +error: this boolean expression can be simplified + --> $DIR/blocks_in_if_conditions.rs:46:8 + | +LL | if true && x == 3 { + | ^^^^^^^^^^^^^^ help: try: `x == 3` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs new file mode 100644 index 0000000000000..acbabfa20d737 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs @@ -0,0 +1,47 @@ +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] + +fn predicate bool, T>(pfn: F, val: T) -> bool { + pfn(val) +} + +fn pred_test() { + let v = 3; + let sky = "blue"; + // This is a sneaky case, where the block isn't directly in the condition, + // but is actually inside a closure that the condition is using. + // The same principle applies -- add some extra expressions to make sure + // linter isn't confused by them. + if v == 3 + && sky == "blue" + && predicate( + |x| { + let target = 3; + x == target + }, + v, + ) + {} + + if predicate( + |x| { + let target = 3; + x == target + }, + v, + ) {} +} + +fn closure_without_block() { + if predicate(|x| x == 3, 6) {} +} + +fn macro_in_closure() { + let option = Some(true); + + if option.unwrap_or_else(|| unimplemented!()) { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr new file mode 100644 index 0000000000000..941d604dd5f91 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr @@ -0,0 +1,24 @@ +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_if_conditions_closure.rs:18:17 + | +LL | |x| { + | _________________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_____________^ + | + = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` + +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_if_conditions_closure.rs:27:13 + | +LL | |x| { + | _____________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_________^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed new file mode 100644 index 0000000000000..9121176475938 --- /dev/null +++ b/src/tools/clippy/tests/ui/bool_comparison.fixed @@ -0,0 +1,129 @@ +// run-rustfix + +#[warn(clippy::bool_comparison)] +fn main() { + let x = true; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + let y = true; + if !x & y { + "yes" + } else { + "no" + }; + if x & !y { + "yes" + } else { + "no" + }; +} + +#[allow(dead_code)] +fn issue3703() { + struct Foo; + impl PartialEq for Foo { + fn eq(&self, _: &bool) -> bool { + true + } + } + impl PartialEq for bool { + fn eq(&self, _: &Foo) -> bool { + true + } + } + impl PartialOrd for Foo { + fn partial_cmp(&self, _: &bool) -> Option { + None + } + } + impl PartialOrd for bool { + fn partial_cmp(&self, _: &Foo) -> Option { + None + } + } + + if Foo == true {} + if true == Foo {} + if Foo != true {} + if true != Foo {} + if Foo == false {} + if false == Foo {} + if Foo != false {} + if false != Foo {} + if Foo < false {} + if false < Foo {} +} + +#[allow(dead_code)] +fn issue4983() { + let a = true; + let b = false; + + if a != b {}; + if a != b {}; + if a == b {}; + if !a == !b {}; + + if b != a {}; + if b != a {}; + if b == a {}; + if !b == !a {}; +} diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs new file mode 100644 index 0000000000000..01ee35859f0da --- /dev/null +++ b/src/tools/clippy/tests/ui/bool_comparison.rs @@ -0,0 +1,129 @@ +// run-rustfix + +#[warn(clippy::bool_comparison)] +fn main() { + let x = true; + if x == true { + "yes" + } else { + "no" + }; + if x == false { + "yes" + } else { + "no" + }; + if true == x { + "yes" + } else { + "no" + }; + if false == x { + "yes" + } else { + "no" + }; + if x != true { + "yes" + } else { + "no" + }; + if x != false { + "yes" + } else { + "no" + }; + if true != x { + "yes" + } else { + "no" + }; + if false != x { + "yes" + } else { + "no" + }; + if x < true { + "yes" + } else { + "no" + }; + if false < x { + "yes" + } else { + "no" + }; + if x > false { + "yes" + } else { + "no" + }; + if true > x { + "yes" + } else { + "no" + }; + let y = true; + if x < y { + "yes" + } else { + "no" + }; + if x > y { + "yes" + } else { + "no" + }; +} + +#[allow(dead_code)] +fn issue3703() { + struct Foo; + impl PartialEq for Foo { + fn eq(&self, _: &bool) -> bool { + true + } + } + impl PartialEq for bool { + fn eq(&self, _: &Foo) -> bool { + true + } + } + impl PartialOrd for Foo { + fn partial_cmp(&self, _: &bool) -> Option { + None + } + } + impl PartialOrd for bool { + fn partial_cmp(&self, _: &Foo) -> Option { + None + } + } + + if Foo == true {} + if true == Foo {} + if Foo != true {} + if true != Foo {} + if Foo == false {} + if false == Foo {} + if Foo != false {} + if false != Foo {} + if Foo < false {} + if false < Foo {} +} + +#[allow(dead_code)] +fn issue4983() { + let a = true; + let b = false; + + if a == !b {}; + if !a == b {}; + if a == b {}; + if !a == !b {}; + + if b == !a {}; + if !b == a {}; + if b == a {}; + if !b == !a {}; +} diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr new file mode 100644 index 0000000000000..eeb1f20ee894d --- /dev/null +++ b/src/tools/clippy/tests/ui/bool_comparison.stderr @@ -0,0 +1,112 @@ +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:6:8 + | +LL | if x == true { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + | + = note: `-D clippy::bool-comparison` implied by `-D warnings` + +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:11:8 + | +LL | if x == false { + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:16:8 + | +LL | if true == x { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:21:8 + | +LL | if false == x { + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: inequality checks against true can be replaced by a negation + --> $DIR/bool_comparison.rs:26:8 + | +LL | if x != true { + | ^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: inequality checks against false are unnecessary + --> $DIR/bool_comparison.rs:31:8 + | +LL | if x != false { + | ^^^^^^^^^^ help: try simplifying it as shown: `x` + +error: inequality checks against true can be replaced by a negation + --> $DIR/bool_comparison.rs:36:8 + | +LL | if true != x { + | ^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: inequality checks against false are unnecessary + --> $DIR/bool_comparison.rs:41:8 + | +LL | if false != x { + | ^^^^^^^^^^ help: try simplifying it as shown: `x` + +error: less than comparison against true can be replaced by a negation + --> $DIR/bool_comparison.rs:46:8 + | +LL | if x < true { + | ^^^^^^^^ help: try simplifying it as shown: `!x` + +error: greater than checks against false are unnecessary + --> $DIR/bool_comparison.rs:51:8 + | +LL | if false < x { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: greater than checks against false are unnecessary + --> $DIR/bool_comparison.rs:56:8 + | +LL | if x > false { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: less than comparison against true can be replaced by a negation + --> $DIR/bool_comparison.rs:61:8 + | +LL | if true > x { + | ^^^^^^^^ help: try simplifying it as shown: `!x` + +error: order comparisons between booleans can be simplified + --> $DIR/bool_comparison.rs:67:8 + | +LL | if x < y { + | ^^^^^ help: try simplifying it as shown: `!x & y` + +error: order comparisons between booleans can be simplified + --> $DIR/bool_comparison.rs:72:8 + | +LL | if x > y { + | ^^^^^ help: try simplifying it as shown: `x & !y` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:120:8 + | +LL | if a == !b {}; + | ^^^^^^^ help: try simplifying it as shown: `a != b` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:121:8 + | +LL | if !a == b {}; + | ^^^^^^^ help: try simplifying it as shown: `a != b` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:125:8 + | +LL | if b == !a {}; + | ^^^^^^^ help: try simplifying it as shown: `b != a` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:126:8 + | +LL | if !b == a {}; + | ^^^^^^^ help: try simplifying it as shown: `b != a` + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_box.rs b/src/tools/clippy/tests/ui/borrow_box.rs new file mode 100644 index 0000000000000..1901de46ca894 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_box.rs @@ -0,0 +1,99 @@ +#![deny(clippy::borrowed_box)] +#![allow(clippy::blacklisted_name)] +#![allow(unused_variables)] +#![allow(dead_code)] + +pub fn test1(foo: &mut Box) { + // Although this function could be changed to "&mut bool", + // avoiding the Box, mutable references to boxes are not + // flagged by this lint. + // + // This omission is intentional: By passing a mutable Box, + // the memory location of the pointed-to object could be + // modified. By passing a mutable reference, the contents + // could change, but not the location. + println!("{:?}", foo) +} + +pub fn test2() { + let foo: &Box; +} + +struct Test3<'a> { + foo: &'a Box, +} + +trait Test4 { + fn test4(a: &Box); +} + +impl<'a> Test4 for Test3<'a> { + fn test4(a: &Box) { + unimplemented!(); + } +} + +use std::any::Any; + +pub fn test5(foo: &mut Box) { + println!("{:?}", foo) +} + +pub fn test6() { + let foo: &Box; +} + +struct Test7<'a> { + foo: &'a Box, +} + +trait Test8 { + fn test8(a: &Box); +} + +impl<'a> Test8 for Test7<'a> { + fn test8(a: &Box) { + unimplemented!(); + } +} + +pub fn test9(foo: &mut Box) { + let _ = foo; +} + +pub fn test10() { + let foo: &Box; +} + +struct Test11<'a> { + foo: &'a Box, +} + +trait Test12 { + fn test4(a: &Box); +} + +impl<'a> Test12 for Test11<'a> { + fn test4(a: &Box) { + unimplemented!(); + } +} + +pub fn test13(boxed_slice: &mut Box<[i32]>) { + // Unconditionally replaces the box pointer. + // + // This cannot be accomplished if "&mut [i32]" is passed, + // and provides a test case where passing a reference to + // a Box is valid. + let mut data = vec![12]; + *boxed_slice = data.into_boxed_slice(); +} + +fn main() { + test1(&mut Box::new(false)); + test2(); + test5(&mut (Box::new(false) as Box)); + test6(); + test9(&mut (Box::new(false) as Box)); + test10(); +} diff --git a/src/tools/clippy/tests/ui/borrow_box.stderr b/src/tools/clippy/tests/ui/borrow_box.stderr new file mode 100644 index 0000000000000..b5db691f89f39 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_box.stderr @@ -0,0 +1,26 @@ +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:19:14 + | +LL | let foo: &Box; + | ^^^^^^^^^^ help: try: `&bool` + | +note: the lint level is defined here + --> $DIR/borrow_box.rs:1:9 + | +LL | #![deny(clippy::borrowed_box)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:23:10 + | +LL | foo: &'a Box, + | ^^^^^^^^^^^^^ help: try: `&'a bool` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:27:17 + | +LL | fn test4(a: &Box); + | ^^^^^^^^^^ help: try: `&bool` + +error: aborting due to 3 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..fef9f4f39f809 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs @@ -0,0 +1,85 @@ +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] + +use std::borrow::Cow; +use std::cell::Cell; +use std::fmt::Display; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Once; + +const ATOMIC: AtomicUsize = AtomicUsize::new(5); +const CELL: Cell = Cell::new(6); +const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 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(); + +trait Trait: Copy { + type NonCopyType; + + const ATOMIC: AtomicUsize; +} + +impl Trait for u64 { + type NonCopyType = u16; + + const ATOMIC: AtomicUsize = AtomicUsize::new(9); +} + +fn main() { + ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability + + let _once = ONCE_INIT; + let _once_ref = &ONCE_INIT; //~ ERROR interior mutability + let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability + let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability + let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability + 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; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability + let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability + let _ = &*ATOMIC_TUPLE.1; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.2; + let _ = (&&&&ATOMIC_TUPLE).0; + let _ = (&&&&ATOMIC_TUPLE).2; + let _ = ATOMIC_TUPLE.0; + let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + let _ = ATOMIC_TUPLE.1.into_iter(); + let _ = ATOMIC_TUPLE.2; + let _ = &{ ATOMIC_TUPLE }; + + CELL.set(2); //~ ERROR interior mutability + assert_eq!(CELL.get(), 6); //~ ERROR interior mutability + + 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()); + + u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability + + assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. +} 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..dc738064a1718 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr @@ -0,0 +1,131 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:34:5 + | +LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:35:16 + | +LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability + | ^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:38:22 + | +LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:39:25 + | +LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:40:27 + | +LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:41:26 + | +LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:52:14 + | +LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:53:14 + | +LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:54:19 + | +LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:55:14 + | +LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:56:13 + | +LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:62:13 + | +LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:67:5 + | +LL | CELL.set(2); //~ ERROR interior mutability + | ^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:68:16 + | +LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability + | ^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:81:5 + | +LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:82:16 + | +LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/box_vec.rs b/src/tools/clippy/tests/ui/box_vec.rs new file mode 100644 index 0000000000000..87b67c23704c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/box_vec.rs @@ -0,0 +1,32 @@ +#![warn(clippy::all)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name)] + +macro_rules! boxit { + ($init:expr, $x:ty) => { + let _: Box<$x> = Box::new($init); + }; +} + +fn test_macro() { + boxit!(Vec::new(), Vec); +} +pub fn test(foo: Box>) { + println!("{:?}", foo.get(0)) +} + +pub fn test2(foo: Box)>) { + // pass if #31 is fixed + foo(vec![1, 2, 3]) +} + +pub fn test_local_not_linted() { + let _: Box>; +} + +fn main() { + test(Box::new(Vec::new())); + test2(Box::new(|v| println!("{:?}", v))); + test_macro(); + test_local_not_linted(); +} diff --git a/src/tools/clippy/tests/ui/box_vec.stderr b/src/tools/clippy/tests/ui/box_vec.stderr new file mode 100644 index 0000000000000..fca12eddd573f --- /dev/null +++ b/src/tools/clippy/tests/ui/box_vec.stderr @@ -0,0 +1,11 @@ +error: you seem to be trying to use `Box>`. Consider using just `Vec` + --> $DIR/box_vec.rs:14:18 + | +LL | pub fn test(foo: Box>) { + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::box-vec` implied by `-D warnings` + = help: `Vec` is already on the heap, `Box>` makes an extra allocation. + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/builtin-type-shadow.rs b/src/tools/clippy/tests/ui/builtin-type-shadow.rs new file mode 100644 index 0000000000000..69b8b6a0e68c3 --- /dev/null +++ b/src/tools/clippy/tests/ui/builtin-type-shadow.rs @@ -0,0 +1,9 @@ +#![warn(clippy::builtin_type_shadow)] +#![allow(non_camel_case_types)] + +fn foo(a: u32) -> u32 { + 42 + // ^ rustc's type error +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/builtin-type-shadow.stderr b/src/tools/clippy/tests/ui/builtin-type-shadow.stderr new file mode 100644 index 0000000000000..bc785b075e028 --- /dev/null +++ b/src/tools/clippy/tests/ui/builtin-type-shadow.stderr @@ -0,0 +1,24 @@ +error: This generic shadows the built-in type `u32` + --> $DIR/builtin-type-shadow.rs:4:8 + | +LL | fn foo(a: u32) -> u32 { + | ^^^ + | + = note: `-D clippy::builtin-type-shadow` implied by `-D warnings` + +error[E0308]: mismatched types + --> $DIR/builtin-type-shadow.rs:5:5 + | +LL | fn foo(a: u32) -> u32 { + | --- --- expected `u32` because of return type + | | + | this type parameter +LL | 42 + | ^^ expected type parameter `u32`, found integer + | + = note: expected type parameter `u32` + found type `{integer}` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/tools/clippy/tests/ui/bytecount.rs b/src/tools/clippy/tests/ui/bytecount.rs new file mode 100644 index 0000000000000..c724ee21be310 --- /dev/null +++ b/src/tools/clippy/tests/ui/bytecount.rs @@ -0,0 +1,24 @@ +#[deny(clippy::naive_bytecount)] +fn main() { + let x = vec![0_u8; 16]; + + let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count + + let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count + + let _ = x.iter().filter(|a| **a > 0).count(); // not an equality count, OK. + + let _ = x.iter().map(|a| a + 1).filter(|&a| a < 15).count(); // not a slice + + let b = 0; + + let _ = x.iter().filter(|_| b > 0).count(); // woah there + + let _ = x.iter().filter(|_a| b == b + 1).count(); // nothing to see here, move along + + let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count + + let y = vec![0_u16; 3]; + + let _ = y.iter().filter(|&&a| a == 0).count(); // naive count, but not bytes +} diff --git a/src/tools/clippy/tests/ui/bytecount.stderr b/src/tools/clippy/tests/ui/bytecount.stderr new file mode 100644 index 0000000000000..436f5d86a0627 --- /dev/null +++ b/src/tools/clippy/tests/ui/bytecount.stderr @@ -0,0 +1,26 @@ +error: You appear to be counting bytes the naive way + --> $DIR/bytecount.rs:5:13 + | +LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)` + | +note: the lint level is defined here + --> $DIR/bytecount.rs:1:8 + | +LL | #[deny(clippy::naive_bytecount)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: You appear to be counting bytes the naive way + --> $DIR/bytecount.rs:7:13 + | +LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)` + +error: You appear to be counting bytes the naive way + --> $DIR/bytecount.rs:19:13 + | +LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs new file mode 100644 index 0000000000000..7e0b211d862ca --- /dev/null +++ b/src/tools/clippy/tests/ui/cast.rs @@ -0,0 +1,95 @@ +#[warn( + clippy::cast_precision_loss, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_possible_wrap +)] +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + // Test clippy::cast_precision_loss + let x0 = 1i32; + x0 as f32; + let x1 = 1i64; + x1 as f32; + x1 as f64; + let x2 = 1u32; + x2 as f32; + let x3 = 1u64; + x3 as f32; + x3 as f64; + // Test clippy::cast_possible_truncation + 1f32 as i32; + 1f32 as u32; + 1f64 as f32; + 1i32 as i8; + 1i32 as u8; + 1f64 as isize; + 1f64 as usize; + // Test clippy::cast_possible_wrap + 1u8 as i8; + 1u16 as i16; + 1u32 as i32; + 1u64 as i64; + 1usize as isize; + // Test clippy::cast_sign_loss + 1i32 as u32; + -1i32 as u32; + 1isize as usize; + -1isize as usize; + 0i8 as u8; + i8::max_value() as u8; + i16::max_value() as u16; + i32::max_value() as u32; + i64::max_value() as u64; + i128::max_value() as u128; + + (-1i8).abs() as u8; + (-1i16).abs() as u16; + (-1i32).abs() as u32; + (-1i64).abs() as u64; + (-1isize).abs() as usize; + + (-1i8).checked_abs().unwrap() as u8; + (-1i16).checked_abs().unwrap() as u16; + (-1i32).checked_abs().unwrap() as u32; + (-1i64).checked_abs().unwrap() as u64; + (-1isize).checked_abs().unwrap() as usize; + + (-1i8).rem_euclid(1i8) as u8; + (-1i8).rem_euclid(1i8) as u16; + (-1i16).rem_euclid(1i16) as u16; + (-1i16).rem_euclid(1i16) as u32; + (-1i32).rem_euclid(1i32) as u32; + (-1i32).rem_euclid(1i32) as u64; + (-1i64).rem_euclid(1i64) as u64; + (-1i64).rem_euclid(1i64) as u128; + (-1isize).rem_euclid(1isize) as usize; + (1i8).rem_euclid(-1i8) as u8; + (1i8).rem_euclid(-1i8) as u16; + (1i16).rem_euclid(-1i16) as u16; + (1i16).rem_euclid(-1i16) as u32; + (1i32).rem_euclid(-1i32) as u32; + (1i32).rem_euclid(-1i32) as u64; + (1i64).rem_euclid(-1i64) as u64; + (1i64).rem_euclid(-1i64) as u128; + (1isize).rem_euclid(-1isize) as usize; + + (-1i8).checked_rem_euclid(1i8).unwrap() as u8; + (-1i8).checked_rem_euclid(1i8).unwrap() as u16; + (-1i16).checked_rem_euclid(1i16).unwrap() as u16; + (-1i16).checked_rem_euclid(1i16).unwrap() as u32; + (-1i32).checked_rem_euclid(1i32).unwrap() as u32; + (-1i32).checked_rem_euclid(1i32).unwrap() as u64; + (-1i64).checked_rem_euclid(1i64).unwrap() as u64; + (-1i64).checked_rem_euclid(1i64).unwrap() as u128; + (-1isize).checked_rem_euclid(1isize).unwrap() as usize; + (1i8).checked_rem_euclid(-1i8).unwrap() as u8; + (1i8).checked_rem_euclid(-1i8).unwrap() as u16; + (1i16).checked_rem_euclid(-1i16).unwrap() as u16; + (1i16).checked_rem_euclid(-1i16).unwrap() as u32; + (1i32).checked_rem_euclid(-1i32).unwrap() as u32; + (1i32).checked_rem_euclid(-1i32).unwrap() as u64; + (1i64).checked_rem_euclid(-1i64).unwrap() as u64; + (1i64).checked_rem_euclid(-1i64).unwrap() as u128; + (1isize).checked_rem_euclid(-1isize).unwrap() as usize; +} diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr new file mode 100644 index 0000000000000..4c66d73649484 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -0,0 +1,142 @@ +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:11:5 + | +LL | x0 as f32; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + +error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:13:5 + | +LL | x1 as f32; + | ^^^^^^^^^ + +error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast.rs:14:5 + | +LL | x1 as f64; + | ^^^^^^^^^ + +error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:16:5 + | +LL | x2 as f32; + | ^^^^^^^^^ + +error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:18:5 + | +LL | x3 as f32; + | ^^^^^^^^^ + +error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast.rs:19:5 + | +LL | x3 as f64; + | ^^^^^^^^^ + +error: casting `f32` to `i32` may truncate the value + --> $DIR/cast.rs:21:5 + | +LL | 1f32 as i32; + | ^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + +error: casting `f32` to `u32` may truncate the value + --> $DIR/cast.rs:22:5 + | +LL | 1f32 as u32; + | ^^^^^^^^^^^ + +error: casting `f32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:22:5 + | +LL | 1f32 as u32; + | ^^^^^^^^^^^ + | + = note: `-D clippy::cast-sign-loss` implied by `-D warnings` + +error: casting `f64` to `f32` may truncate the value + --> $DIR/cast.rs:23:5 + | +LL | 1f64 as f32; + | ^^^^^^^^^^^ + +error: casting `i32` to `i8` may truncate the value + --> $DIR/cast.rs:24:5 + | +LL | 1i32 as i8; + | ^^^^^^^^^^ + +error: casting `i32` to `u8` may truncate the value + --> $DIR/cast.rs:25:5 + | +LL | 1i32 as u8; + | ^^^^^^^^^^ + +error: casting `f64` to `isize` may truncate the value + --> $DIR/cast.rs:26:5 + | +LL | 1f64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `f64` to `usize` may truncate the value + --> $DIR/cast.rs:27:5 + | +LL | 1f64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `f64` to `usize` may lose the sign of the value + --> $DIR/cast.rs:27:5 + | +LL | 1f64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u8` to `i8` may wrap around the value + --> $DIR/cast.rs:29:5 + | +LL | 1u8 as i8; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + +error: casting `u16` to `i16` may wrap around the value + --> $DIR/cast.rs:30:5 + | +LL | 1u16 as i16; + | ^^^^^^^^^^^ + +error: casting `u32` to `i32` may wrap around the value + --> $DIR/cast.rs:31:5 + | +LL | 1u32 as i32; + | ^^^^^^^^^^^ + +error: casting `u64` to `i64` may wrap around the value + --> $DIR/cast.rs:32:5 + | +LL | 1u64 as i64; + | ^^^^^^^^^^^ + +error: casting `usize` to `isize` may wrap around the value + --> $DIR/cast.rs:33:5 + | +LL | 1usize as isize; + | ^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:36:5 + | +LL | -1i32 as u32; + | ^^^^^^^^^^^^ + +error: casting `isize` to `usize` may lose the sign of the value + --> $DIR/cast.rs:38:5 + | +LL | -1isize as usize; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs new file mode 100644 index 0000000000000..4c08935639f1f --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -0,0 +1,27 @@ +//! Test casts for alignment issues + +#![feature(rustc_private)] +extern crate libc; + +#[warn(clippy::cast_ptr_alignment)] +#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)] +fn main() { + /* These should be warned against */ + + // cast to more-strictly-aligned type + (&1u8 as *const u8) as *const u16; + (&mut 1u8 as *mut u8) as *mut u16; + + /* These should be ok */ + + // not a pointer type + 1u8 as u16; + // cast to less-strictly-aligned type + (&1u16 as *const u16) as *const u8; + (&mut 1u16 as *mut u16) as *mut u8; + // For c_void, we should trust the user. See #2677 + (&1u32 as *const u32 as *const std::os::raw::c_void) as *const u32; + (&1u32 as *const u32 as *const libc::c_void) as *const u32; + // For ZST, we should trust the user. See #4256 + (&1u32 as *const u32 as *const ()) as *const u32; +} diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr new file mode 100644 index 0000000000000..79219f86155a4 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_alignment.stderr @@ -0,0 +1,16 @@ +error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:12:5 + | +LL | (&1u8 as *const u8) as *const u16; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings` + +error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:13:5 + | +LL | (&mut 1u8 as *mut u8) as *mut u16; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.fixed b/src/tools/clippy/tests/ui/cast_lossless_float.fixed new file mode 100644 index 0000000000000..709d58b596c83 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_float.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to floating-point types + let x0 = 1i8; + f32::from(x0); + f64::from(x0); + let x1 = 1u8; + f32::from(x1); + f64::from(x1); + let x2 = 1i16; + f32::from(x2); + f64::from(x2); + let x3 = 1u16; + f32::from(x3); + f64::from(x3); + let x4 = 1i32; + f64::from(x4); + let x5 = 1u32; + f64::from(x5); + + // Test with casts from floating-point types + f64::from(1.0f32); +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: f32) -> f64 { + input as f64 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: f32) -> f64 { + x as f64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.rs b/src/tools/clippy/tests/ui/cast_lossless_float.rs new file mode 100644 index 0000000000000..eb0aab8864290 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_float.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to floating-point types + let x0 = 1i8; + x0 as f32; + x0 as f64; + let x1 = 1u8; + x1 as f32; + x1 as f64; + let x2 = 1i16; + x2 as f32; + x2 as f64; + let x3 = 1u16; + x3 as f32; + x3 as f64; + let x4 = 1i32; + x4 as f64; + let x5 = 1u32; + x5 as f64; + + // Test with casts from floating-point types + 1.0f32 as f64; +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: f32) -> f64 { + input as f64 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: f32) -> f64 { + x as f64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.stderr b/src/tools/clippy/tests/ui/cast_lossless_float.stderr new file mode 100644 index 0000000000000..0ed09f3083c28 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_float.stderr @@ -0,0 +1,70 @@ +error: casting `i8` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:9:5 + | +LL | x0 as f32; + | ^^^^^^^^^ help: try: `f32::from(x0)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: casting `i8` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:10:5 + | +LL | x0 as f64; + | ^^^^^^^^^ help: try: `f64::from(x0)` + +error: casting `u8` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:12:5 + | +LL | x1 as f32; + | ^^^^^^^^^ help: try: `f32::from(x1)` + +error: casting `u8` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:13:5 + | +LL | x1 as f64; + | ^^^^^^^^^ help: try: `f64::from(x1)` + +error: casting `i16` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:15:5 + | +LL | x2 as f32; + | ^^^^^^^^^ help: try: `f32::from(x2)` + +error: casting `i16` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:16:5 + | +LL | x2 as f64; + | ^^^^^^^^^ help: try: `f64::from(x2)` + +error: casting `u16` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:18:5 + | +LL | x3 as f32; + | ^^^^^^^^^ help: try: `f32::from(x3)` + +error: casting `u16` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:19:5 + | +LL | x3 as f64; + | ^^^^^^^^^ help: try: `f64::from(x3)` + +error: casting `i32` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:21:5 + | +LL | x4 as f64; + | ^^^^^^^^^ help: try: `f64::from(x4)` + +error: casting `u32` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:23:5 + | +LL | x5 as f64; + | ^^^^^^^^^ help: try: `f64::from(x5)` + +error: casting `f32` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:26:5 + | +LL | 1.0f32 as f64; + | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.fixed b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed new file mode 100644 index 0000000000000..03e49adb117db --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed @@ -0,0 +1,47 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to integer types + i16::from(1i8); + i32::from(1i8); + i64::from(1i8); + i16::from(1u8); + i32::from(1u8); + i64::from(1u8); + u16::from(1u8); + u32::from(1u8); + u64::from(1u8); + i32::from(1i16); + i64::from(1i16); + i32::from(1u16); + i64::from(1u16); + u32::from(1u16); + u64::from(1u16); + i64::from(1i32); + i64::from(1u32); + u64::from(1u32); + + // Test with an expression wrapped in parens + u16::from(1u8 + 1u8); +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: u16) -> u32 { + input as u32 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: u32) -> u64 { + x as u64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.rs b/src/tools/clippy/tests/ui/cast_lossless_integer.rs new file mode 100644 index 0000000000000..6a984d245963f --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.rs @@ -0,0 +1,47 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to integer types + 1i8 as i16; + 1i8 as i32; + 1i8 as i64; + 1u8 as i16; + 1u8 as i32; + 1u8 as i64; + 1u8 as u16; + 1u8 as u32; + 1u8 as u64; + 1i16 as i32; + 1i16 as i64; + 1u16 as i32; + 1u16 as i64; + 1u16 as u32; + 1u16 as u64; + 1i32 as i64; + 1u32 as i64; + 1u32 as u64; + + // Test with an expression wrapped in parens + (1u8 + 1u8) as u16; +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: u16) -> u32 { + input as u32 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: u32) -> u64 { + x as u64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr new file mode 100644 index 0000000000000..8e2890f9c28d0 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr @@ -0,0 +1,118 @@ +error: casting `i8` to `i16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:8:5 + | +LL | 1i8 as i16; + | ^^^^^^^^^^ help: try: `i16::from(1i8)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:9:5 + | +LL | 1i8 as i32; + | ^^^^^^^^^^ help: try: `i32::from(1i8)` + +error: casting `i8` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:10:5 + | +LL | 1i8 as i64; + | ^^^^^^^^^^ help: try: `i64::from(1i8)` + +error: casting `u8` to `i16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:11:5 + | +LL | 1u8 as i16; + | ^^^^^^^^^^ help: try: `i16::from(1u8)` + +error: casting `u8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:12:5 + | +LL | 1u8 as i32; + | ^^^^^^^^^^ help: try: `i32::from(1u8)` + +error: casting `u8` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:13:5 + | +LL | 1u8 as i64; + | ^^^^^^^^^^ help: try: `i64::from(1u8)` + +error: casting `u8` to `u16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:14:5 + | +LL | 1u8 as u16; + | ^^^^^^^^^^ help: try: `u16::from(1u8)` + +error: casting `u8` to `u32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:15:5 + | +LL | 1u8 as u32; + | ^^^^^^^^^^ help: try: `u32::from(1u8)` + +error: casting `u8` to `u64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:16:5 + | +LL | 1u8 as u64; + | ^^^^^^^^^^ help: try: `u64::from(1u8)` + +error: casting `i16` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:17:5 + | +LL | 1i16 as i32; + | ^^^^^^^^^^^ help: try: `i32::from(1i16)` + +error: casting `i16` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:18:5 + | +LL | 1i16 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1i16)` + +error: casting `u16` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:19:5 + | +LL | 1u16 as i32; + | ^^^^^^^^^^^ help: try: `i32::from(1u16)` + +error: casting `u16` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:20:5 + | +LL | 1u16 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1u16)` + +error: casting `u16` to `u32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:21:5 + | +LL | 1u16 as u32; + | ^^^^^^^^^^^ help: try: `u32::from(1u16)` + +error: casting `u16` to `u64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:22:5 + | +LL | 1u16 as u64; + | ^^^^^^^^^^^ help: try: `u64::from(1u16)` + +error: casting `i32` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:23:5 + | +LL | 1i32 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1i32)` + +error: casting `u32` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:24:5 + | +LL | 1u32 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1u32)` + +error: casting `u32` to `u64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:25:5 + | +LL | 1u32 as u64; + | ^^^^^^^^^^^ help: try: `u64::from(1u32)` + +error: casting `u8` to `u16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:28:5 + | +LL | (1u8 + 1u8) as u16; + | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` + +error: aborting due to 19 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs new file mode 100644 index 0000000000000..089e5cfabe4b9 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs @@ -0,0 +1,31 @@ +#![warn(clippy::cast_ref_to_mut)] +#![allow(clippy::no_effect)] + +extern "C" { + // N.B., mutability can be easily incorrect in FFI calls -- as + // in C, the default is mutable pointers. + fn ffi(c: *mut u8); + fn int_ffi(c: *mut i32); +} + +fn main() { + let s = String::from("Hello"); + let a = &s; + unsafe { + let num = &3i32; + let mut_num = &mut 3i32; + // Should be warned against + (*(a as *const _ as *mut String)).push_str(" world"); + *(a as *const _ as *mut _) = String::from("Replaced"); + *(a as *const _ as *mut String) += " world"; + // Shouldn't be warned against + println!("{}", *(num as *const _ as *const i16)); + println!("{}", *(mut_num as *mut _ as *mut i16)); + ffi(a.as_ptr() as *mut _); + int_ffi(num as *const _ as *mut _); + int_ffi(&3 as *const _ as *mut _); + let mut value = 3; + let value: *const i32 = &mut value; + *(value as *const i16 as *mut i16) = 42; + } +} diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr b/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr new file mode 100644 index 0000000000000..aacd99437d9fc --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr @@ -0,0 +1,22 @@ +error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` + --> $DIR/cast_ref_to_mut.rs:18:9 + | +LL | (*(a as *const _ as *mut String)).push_str(" world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings` + +error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` + --> $DIR/cast_ref_to_mut.rs:19:9 + | +LL | *(a as *const _ as *mut _) = String::from("Replaced"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` + --> $DIR/cast_ref_to_mut.rs:20:9 + | +LL | *(a as *const _ as *mut String) += " world"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs new file mode 100644 index 0000000000000..595109be46bb8 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -0,0 +1,35 @@ +// ignore-32bit +#[warn( + clippy::cast_precision_loss, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_possible_wrap, + clippy::cast_lossless +)] +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + // Casting from *size + 1isize as i8; + let x0 = 1isize; + let x1 = 1usize; + x0 as f64; + x1 as f64; + x0 as f32; + x1 as f32; + 1isize as i32; + 1isize as u32; + 1usize as u32; + 1usize as i32; + // Casting to *size + 1i64 as isize; + 1i64 as usize; + 1u64 as isize; + 1u64 as usize; + 1u32 as isize; + 1u32 as usize; // Should not trigger any lint + 1i32 as isize; // Neither should this + 1i32 as usize; + // Big integer literal to float + 999_999_999 as f32; + 9_999_999_999_999_999usize as f64; +} diff --git a/src/tools/clippy/tests/ui/cast_size.stderr b/src/tools/clippy/tests/ui/cast_size.stderr new file mode 100644 index 0000000000000..95552f2e28539 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size.stderr @@ -0,0 +1,116 @@ +error: casting `isize` to `i8` may truncate the value + --> $DIR/cast_size.rs:12:5 + | +LL | 1isize as i8; + | ^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + +error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:15:5 + | +LL | x0 as f64; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:16:5 + | +LL | x1 as f64; + | ^^^^^^^^^ + +error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size.rs:17:5 + | +LL | x0 as f32; + | ^^^^^^^^^ + +error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size.rs:18:5 + | +LL | x1 as f32; + | ^^^^^^^^^ + +error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:19:5 + | +LL | 1isize as i32; + | ^^^^^^^^^^^^^ + +error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:20:5 + | +LL | 1isize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:21:5 + | +LL | 1usize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + +error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:24:5 + | +LL | 1i64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:25:5 + | +LL | 1i64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:27:5 + | +LL | 1u64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:28:5 + | +LL | 1u32 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size.rs:33:5 + | +LL | 999_999_999 as f32; + | ^^^^^^^^^^^^^^^^^^ + +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:34:5 + | +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.rs b/src/tools/clippy/tests/ui/cast_size_32bit.rs new file mode 100644 index 0000000000000..99aac6deca324 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size_32bit.rs @@ -0,0 +1,35 @@ +// ignore-64bit +#[warn( + clippy::cast_precision_loss, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_possible_wrap, + clippy::cast_lossless +)] +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + // Casting from *size + 1isize as i8; + let x0 = 1isize; + let x1 = 1usize; + x0 as f64; + x1 as f64; + x0 as f32; + x1 as f32; + 1isize as i32; + 1isize as u32; + 1usize as u32; + 1usize as i32; + // Casting to *size + 1i64 as isize; + 1i64 as usize; + 1u64 as isize; + 1u64 as usize; + 1u32 as isize; + 1u32 as usize; // Should not trigger any lint + 1i32 as isize; // Neither should this + 1i32 as usize; + // Big integer literal to float + 999_999_999 as f32; + 3_999_999_999usize as f64; +} diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.stderr b/src/tools/clippy/tests/ui/cast_size_32bit.stderr new file mode 100644 index 0000000000000..2eec51895f59f --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size_32bit.stderr @@ -0,0 +1,132 @@ +error: casting `isize` to `i8` may truncate the value + --> $DIR/cast_size_32bit.rs:12:5 + | +LL | 1isize as i8; + | ^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + +error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size_32bit.rs:15:5 + | +LL | x0 as f64; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + +error: casting `isize` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_size_32bit.rs:15:5 + | +LL | x0 as f64; + | ^^^^^^^^^ help: try: `f64::from(x0)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size_32bit.rs:16:5 + | +LL | x1 as f64; + | ^^^^^^^^^ + +error: casting `usize` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_size_32bit.rs:16:5 + | +LL | x1 as f64; + | ^^^^^^^^^ help: try: `f64::from(x1)` + +error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size_32bit.rs:17:5 + | +LL | x0 as f32; + | ^^^^^^^^^ + +error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size_32bit.rs:18:5 + | +LL | x1 as f32; + | ^^^^^^^^^ + +error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:19:5 + | +LL | 1isize as i32; + | ^^^^^^^^^^^^^ + +error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:20:5 + | +LL | 1isize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:21:5 + | +LL | 1usize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + +error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:24:5 + | +LL | 1i64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:25:5 + | +LL | 1i64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:27:5 + | +LL | 1u64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:28:5 + | +LL | 1u32 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size_32bit.rs:33:5 + | +LL | 999_999_999 as f32; + | ^^^^^^^^^^^^^^^^^^ + +error: casting integer literal to `f64` is unnecessary + --> $DIR/cast_size_32bit.rs:34:5 + | +LL | 3_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64` + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed new file mode 100644 index 0000000000000..4e583a25b94c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed @@ -0,0 +1,31 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] + +#![allow(unused, clippy::no_effect)] +#![warn(clippy::deprecated_cfg_attr)] + +// This doesn't get linted, see known problems +#![cfg_attr(rustfmt, rustfmt_skip)] + +#[rustfmt::skip] +trait Foo +{ +fn foo( +); +} + +fn skip_on_statements() { + #[rustfmt::skip] + 5+3; +} + +#[rustfmt::skip] +fn main() { + foo::f(); +} + +mod foo { + #![cfg_attr(rustfmt, rustfmt_skip)] + + pub fn f() {} +} diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs new file mode 100644 index 0000000000000..9c0fcf6fb454c --- /dev/null +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs @@ -0,0 +1,31 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] + +#![allow(unused, clippy::no_effect)] +#![warn(clippy::deprecated_cfg_attr)] + +// This doesn't get linted, see known problems +#![cfg_attr(rustfmt, rustfmt_skip)] + +#[rustfmt::skip] +trait Foo +{ +fn foo( +); +} + +fn skip_on_statements() { + #[cfg_attr(rustfmt, rustfmt::skip)] + 5+3; +} + +#[cfg_attr(rustfmt, rustfmt_skip)] +fn main() { + foo::f(); +} + +mod foo { + #![cfg_attr(rustfmt, rustfmt_skip)] + + pub fn f() {} +} diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr new file mode 100644 index 0000000000000..c1efd47db90b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr @@ -0,0 +1,16 @@ +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:18:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + | + = note: `-D clippy::deprecated-cfg-attr` implied by `-D warnings` + +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:22:1 + | +LL | #[cfg_attr(rustfmt, rustfmt_skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.rs b/src/tools/clippy/tests/ui/char_lit_as_u8.rs new file mode 100644 index 0000000000000..0a53a3d6490a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8.rs @@ -0,0 +1,5 @@ +#![warn(clippy::char_lit_as_u8)] + +fn main() { + let _ = '❤' as u8; // no suggestion, since a byte literal won't work. +} diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr new file mode 100644 index 0000000000000..b9836d2f25532 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr @@ -0,0 +1,11 @@ +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8.rs:4:13 + | +LL | let _ = '❤' as u8; // no suggestion, since a byte literal won't work. + | ^^^^^^^^^ + | + = note: `-D clippy::char-lit-as-u8` implied by `-D warnings` + = note: `char` is four bytes wide, but `u8` is a single byte + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed new file mode 100644 index 0000000000000..3dc3cb4e7573d --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::char_lit_as_u8)] + +fn main() { + let _ = b'a'; + let _ = b'\n'; + let _ = b'\0'; + let _ = b'\x01'; +} diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs new file mode 100644 index 0000000000000..d379a0234942a --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::char_lit_as_u8)] + +fn main() { + let _ = 'a' as u8; + let _ = '\n' as u8; + let _ = '\0' as u8; + let _ = '\x01' as u8; +} diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr new file mode 100644 index 0000000000000..bf7cb1607b4e2 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr @@ -0,0 +1,35 @@ +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:6:13 + | +LL | let _ = 'a' as u8; + | ^^^^^^^^^ help: use a byte literal instead: `b'a'` + | + = note: `-D clippy::char-lit-as-u8` implied by `-D warnings` + = note: `char` is four bytes wide, but `u8` is a single byte + +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:7:13 + | +LL | let _ = '/n' as u8; + | ^^^^^^^^^^ help: use a byte literal instead: `b'/n'` + | + = note: `char` is four bytes wide, but `u8` is a single byte + +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:8:13 + | +LL | let _ = '/0' as u8; + | ^^^^^^^^^^ help: use a byte literal instead: `b'/0'` + | + = note: `char` is four bytes wide, but `u8` is a single byte + +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:9:13 + | +LL | let _ = '/x01' as u8; + | ^^^^^^^^^^^^ help: use a byte literal instead: `b'/x01'` + | + = note: `char` is four bytes wide, but `u8` is a single byte + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_conversions.fixed b/src/tools/clippy/tests/ui/checked_conversions.fixed new file mode 100644 index 0000000000000..7febd6f376135 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_conversions.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![warn(clippy::checked_conversions)] +#![allow(clippy::cast_lossless)] +#![allow(dead_code)] +use std::convert::TryFrom; + +// Positive tests + +// Signed to unsigned + +fn i64_to_u32(value: i64) -> Option { + if u32::try_from(value).is_ok() { + Some(value as u32) + } else { + None + } +} + +fn i64_to_u16(value: i64) -> Option { + if u16::try_from(value).is_ok() { + Some(value as u16) + } else { + None + } +} + +fn isize_to_u8(value: isize) -> Option { + if u8::try_from(value).is_ok() { + Some(value as u8) + } else { + None + } +} + +// Signed to signed + +fn i64_to_i32(value: i64) -> Option { + if i32::try_from(value).is_ok() { + Some(value as i32) + } else { + None + } +} + +fn i64_to_i16(value: i64) -> Option { + if i16::try_from(value).is_ok() { + Some(value as i16) + } else { + None + } +} + +// Unsigned to X + +fn u32_to_i32(value: u32) -> Option { + if i32::try_from(value).is_ok() { + Some(value as i32) + } else { + None + } +} + +fn usize_to_isize(value: usize) -> isize { + if isize::try_from(value).is_ok() && value as i32 == 5 { + 5 + } else { + 1 + } +} + +fn u32_to_u16(value: u32) -> isize { + if u16::try_from(value).is_ok() && value as i32 == 5 { + 5 + } else { + 1 + } +} + +// Negative tests + +fn no_i64_to_i32(value: i64) -> Option { + if value <= (i32::max_value() as i64) && value >= 0 { + Some(value as i32) + } else { + None + } +} + +fn no_isize_to_u8(value: isize) -> Option { + if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { + Some(value as u8) + } else { + None + } +} + +fn i8_to_u8(value: i8) -> Option { + if value >= 0 { + Some(value as u8) + } else { + None + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_conversions.rs b/src/tools/clippy/tests/ui/checked_conversions.rs new file mode 100644 index 0000000000000..a643354e2438f --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_conversions.rs @@ -0,0 +1,106 @@ +// run-rustfix + +#![warn(clippy::checked_conversions)] +#![allow(clippy::cast_lossless)] +#![allow(dead_code)] +use std::convert::TryFrom; + +// Positive tests + +// Signed to unsigned + +fn i64_to_u32(value: i64) -> Option { + if value <= (u32::max_value() as i64) && value >= 0 { + Some(value as u32) + } else { + None + } +} + +fn i64_to_u16(value: i64) -> Option { + if value <= i64::from(u16::max_value()) && value >= 0 { + Some(value as u16) + } else { + None + } +} + +fn isize_to_u8(value: isize) -> Option { + if value <= (u8::max_value() as isize) && value >= 0 { + Some(value as u8) + } else { + None + } +} + +// Signed to signed + +fn i64_to_i32(value: i64) -> Option { + if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { + Some(value as i32) + } else { + None + } +} + +fn i64_to_i16(value: i64) -> Option { + if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { + Some(value as i16) + } else { + None + } +} + +// Unsigned to X + +fn u32_to_i32(value: u32) -> Option { + if value <= i32::max_value() as u32 { + Some(value as i32) + } else { + None + } +} + +fn usize_to_isize(value: usize) -> isize { + if value <= isize::max_value() as usize && value as i32 == 5 { + 5 + } else { + 1 + } +} + +fn u32_to_u16(value: u32) -> isize { + if value <= u16::max_value() as u32 && value as i32 == 5 { + 5 + } else { + 1 + } +} + +// Negative tests + +fn no_i64_to_i32(value: i64) -> Option { + if value <= (i32::max_value() as i64) && value >= 0 { + Some(value as i32) + } else { + None + } +} + +fn no_isize_to_u8(value: isize) -> Option { + if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { + Some(value as u8) + } else { + None + } +} + +fn i8_to_u8(value: i8) -> Option { + if value >= 0 { + Some(value as u8) + } else { + None + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_conversions.stderr b/src/tools/clippy/tests/ui/checked_conversions.stderr new file mode 100644 index 0000000000000..f678f009621f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_conversions.stderr @@ -0,0 +1,52 @@ +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:13:8 + | +LL | if value <= (u32::max_value() as i64) && value >= 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + | + = note: `-D clippy::checked-conversions` implied by `-D warnings` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:21:8 + | +LL | if value <= i64::from(u16::max_value()) && value >= 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:29:8 + | +LL | if value <= (u8::max_value() as isize) && value >= 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:39:8 + | +LL | if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:47:8 + | +LL | if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:57:8 + | +LL | if value <= i32::max_value() as u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:65:8 + | +LL | if value <= isize::max_value() as usize && value as i32 == 5 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:73:8 + | +LL | if value <= u16::max_value() as u32 && value as i32 == 5 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_conversions.stdout b/src/tools/clippy/tests/ui/checked_conversions.stdout new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs new file mode 100644 index 0000000000000..c986c992a07a6 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs @@ -0,0 +1,54 @@ +#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![allow(clippy::if_same_then_else)] + +fn test_complex_conditions() { + let x: Result<(), ()> = Ok(()); + let y: Result<(), ()> = Ok(()); + if x.is_ok() && y.is_err() { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + y.unwrap(); // will panic + y.unwrap_err(); // unnecessary + } else { + // not statically determinable whether any of the following will always succeed or always fail: + x.unwrap(); + x.unwrap_err(); + y.unwrap(); + y.unwrap_err(); + } + + if x.is_ok() || y.is_ok() { + // not statically determinable whether any of the following will always succeed or always fail: + x.unwrap(); + y.unwrap(); + } else { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + y.unwrap(); // will panic + y.unwrap_err(); // unnecessary + } + let z: Result<(), ()> = Ok(()); + if x.is_ok() && !(y.is_ok() || z.is_err()) { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + y.unwrap(); // will panic + y.unwrap_err(); // unnecessary + z.unwrap(); // unnecessary + z.unwrap_err(); // will panic + } + if x.is_ok() || !(y.is_ok() && z.is_err()) { + // not statically determinable whether any of the following will always succeed or always fail: + x.unwrap(); + y.unwrap(); + z.unwrap(); + } else { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + y.unwrap(); // unnecessary + y.unwrap_err(); // will panic + z.unwrap(); // will panic + z.unwrap_err(); // unnecessary + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr new file mode 100644 index 0000000000000..dc666bab46039 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -0,0 +1,192 @@ +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:8:9 + | +LL | if x.is_ok() && y.is_err() { + | --------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals.rs:1:35 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:9:9 + | +LL | if x.is_ok() && y.is_err() { + | --------- because of this check +LL | x.unwrap(); // unnecessary +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals.rs:1:9 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:10:9 + | +LL | if x.is_ok() && y.is_err() { + | ---------- because of this check +... +LL | y.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:11:9 + | +LL | if x.is_ok() && y.is_err() { + | ---------- the check is happening here +... +LL | y.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:25:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:26:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- the check is happening here +... +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:27:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- because of this check +... +LL | y.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:28:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- the check is happening here +... +LL | y.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:32:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:33:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- because of this check +LL | x.unwrap(); // unnecessary +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:34:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- because of this check +... +LL | y.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:35:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- the check is happening here +... +LL | y.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:36:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | ---------- the check is happening here +... +LL | z.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:37:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | ---------- because of this check +... +LL | z.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:45:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:46:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- the check is happening here +... +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:47:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- the check is happening here +... +LL | y.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:48:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- because of this check +... +LL | y.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:49:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | ---------- because of this check +... +LL | z.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:50:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | ---------- the check is happening here +... +LL | z.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: aborting due to 20 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 new file mode 100644 index 0000000000000..2307996a48ffc --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -0,0 +1,15 @@ +#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![allow(clippy::if_same_then_else)] + +fn test_nested() { + fn nested() { + let x = Some(()); + if x.is_some() { + x.unwrap(); // unnecessary + } else { + x.unwrap(); // will panic + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr new file mode 100644 index 0000000000000..e4d085470c3b4 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -0,0 +1,31 @@ +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals_nested.rs:8:13 + | +LL | if x.is_some() { + | ----------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals_nested.rs:1:35 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals_nested.rs:10:13 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals_nested.rs:1:9 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs new file mode 100644 index 0000000000000..3e7b4b390bad4 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -0,0 +1,80 @@ +#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![allow(clippy::if_same_then_else)] + +macro_rules! m { + ($a:expr) => { + if $a.is_some() { + $a.unwrap(); // unnecessary + } + }; +} + +macro_rules! checks_in_param { + ($a:expr, $b:expr) => { + if $a { + $b; + } + }; +} + +macro_rules! checks_unwrap { + ($a:expr, $b:expr) => { + if $a.is_some() { + $b; + } + }; +} + +macro_rules! checks_some { + ($a:expr, $b:expr) => { + if $a { + $b.unwrap(); + } + }; +} + +fn main() { + let x = Some(()); + if x.is_some() { + x.unwrap(); // unnecessary + } else { + x.unwrap(); // will panic + } + if x.is_none() { + x.unwrap(); // will panic + } else { + x.unwrap(); // unnecessary + } + m!(x); + checks_in_param!(x.is_some(), x.unwrap()); // ok + checks_unwrap!(x, x.unwrap()); // ok + checks_some!(x.is_some(), x); // ok + let mut x: Result<(), ()> = Ok(()); + if x.is_ok() { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + } else { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + } + if x.is_err() { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + } else { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + } + if x.is_ok() { + x = Err(()); + x.unwrap(); // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it only + // checks if conditions). + } else { + x = Ok(()); + x.unwrap_err(); // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it + // only checks if conditions). + } + + assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern +} diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr new file mode 100644 index 0000000000000..4013d1ed667f1 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -0,0 +1,131 @@ +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:39:9 + | +LL | if x.is_some() { + | ----------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/simple_conditionals.rs:1:35 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:41:9 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/simple_conditionals.rs:1:9 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:44:9 + | +LL | if x.is_none() { + | ----------- because of this check +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:46:9 + | +LL | if x.is_none() { + | ----------- the check is happening here +... +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:7:13 + | +LL | if $a.is_some() { + | ------------ the check is happening here +LL | $a.unwrap(); // unnecessary + | ^^^^^^^^^^^ +... +LL | m!(x); + | ------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:54:9 + | +LL | if x.is_ok() { + | --------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/simple_conditionals.rs:55:9 + | +LL | if x.is_ok() { + | --------- because of this check +LL | x.unwrap(); // unnecessary +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:57:9 + | +LL | if x.is_ok() { + | --------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:58:9 + | +LL | if x.is_ok() { + | --------- the check is happening here +... +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:61:9 + | +LL | if x.is_err() { + | ---------- because of this check +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:62:9 + | +LL | if x.is_err() { + | ---------- the check is happening here +LL | x.unwrap(); // will panic +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:64:9 + | +LL | if x.is_err() { + | ---------- the check is happening here +... +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/simple_conditionals.rs:65:9 + | +LL | if x.is_err() { + | ---------- because of this check +... +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs new file mode 100644 index 0000000000000..8f9f2a0db8c43 --- /dev/null +++ b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs @@ -0,0 +1,22 @@ +use std::fmt; +use std::marker::PhantomData; + +pub struct Key { + #[doc(hidden)] + pub __name: &'static str, + #[doc(hidden)] + pub __phantom: PhantomData, +} + +impl Copy for Key {} + +impl Clone for Key { + fn clone(&self) -> Self { + Key { + __name: self.__name, + __phantom: self.__phantom, + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/clone_on_copy_mut.rs b/src/tools/clippy/tests/ui/clone_on_copy_mut.rs new file mode 100644 index 0000000000000..5bfa256623b6b --- /dev/null +++ b/src/tools/clippy/tests/ui/clone_on_copy_mut.rs @@ -0,0 +1,18 @@ +pub fn dec_read_dec(i: &mut i32) -> i32 { + *i -= 1; + let ret = *i; + *i -= 1; + ret +} + +pub fn minus_1(i: &i32) -> i32 { + dec_read_dec(&mut i.clone()) +} + +fn main() { + let mut i = 10; + assert_eq!(minus_1(&i), 9); + assert_eq!(i, 10); + assert_eq!(dec_read_dec(&mut i), 9); + assert_eq!(i, 8); +} diff --git a/src/tools/clippy/tests/ui/cmp_nan.rs b/src/tools/clippy/tests/ui/cmp_nan.rs new file mode 100644 index 0000000000000..64ca52b010a7e --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_nan.rs @@ -0,0 +1,34 @@ +const NAN_F32: f32 = f32::NAN; +const NAN_F64: f64 = f64::NAN; + +#[warn(clippy::cmp_nan)] +#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + let x = 5f32; + x == f32::NAN; + x != f32::NAN; + x < f32::NAN; + x > f32::NAN; + x <= f32::NAN; + x >= f32::NAN; + x == NAN_F32; + x != NAN_F32; + x < NAN_F32; + x > NAN_F32; + x <= NAN_F32; + x >= NAN_F32; + + let y = 0f64; + y == f64::NAN; + y != f64::NAN; + y < f64::NAN; + y > f64::NAN; + y <= f64::NAN; + y >= f64::NAN; + y == NAN_F64; + y != NAN_F64; + y < NAN_F64; + y > NAN_F64; + y <= NAN_F64; + y >= NAN_F64; +} diff --git a/src/tools/clippy/tests/ui/cmp_nan.stderr b/src/tools/clippy/tests/ui/cmp_nan.stderr new file mode 100644 index 0000000000000..867516661a539 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_nan.stderr @@ -0,0 +1,148 @@ +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:8:5 + | +LL | x == f32::NAN; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::cmp-nan` implied by `-D warnings` + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:9:5 + | +LL | x != f32::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:10:5 + | +LL | x < f32::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:11:5 + | +LL | x > f32::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:12:5 + | +LL | x <= f32::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:13:5 + | +LL | x >= f32::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:14:5 + | +LL | x == NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:15:5 + | +LL | x != NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:16:5 + | +LL | x < NAN_F32; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:17:5 + | +LL | x > NAN_F32; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:18:5 + | +LL | x <= NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:19:5 + | +LL | x >= NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:22:5 + | +LL | y == f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:23:5 + | +LL | y != f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:24:5 + | +LL | y < f64::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:25:5 + | +LL | y > f64::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:26:5 + | +LL | y <= f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:27:5 + | +LL | y >= f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:28:5 + | +LL | y == NAN_F64; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:29:5 + | +LL | y != NAN_F64; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:30:5 + | +LL | y < NAN_F64; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:31:5 + | +LL | y > NAN_F64; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:32:5 + | +LL | y <= NAN_F64; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:33:5 + | +LL | y >= NAN_F64; + | ^^^^^^^^^^^^ + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs new file mode 100644 index 0000000000000..2d2d04178c35d --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_null.rs @@ -0,0 +1,17 @@ +#![warn(clippy::cmp_null)] +#![allow(unused_mut)] + +use std::ptr; + +fn main() { + let x = 0; + let p: *const usize = &x; + if p == ptr::null() { + println!("This is surprising!"); + } + let mut y = 0; + let mut m: *mut usize = &mut y; + if m == ptr::null_mut() { + println!("This is surprising, too!"); + } +} diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr new file mode 100644 index 0000000000000..b563a2ebec2d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_null.stderr @@ -0,0 +1,16 @@ +error: Comparing with null is better expressed by the `.is_null()` method + --> $DIR/cmp_null.rs:9:8 + | +LL | if p == ptr::null() { + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cmp-null` implied by `-D warnings` + +error: Comparing with null is better expressed by the `.is_null()` method + --> $DIR/cmp_null.rs:14:8 + | +LL | if m == ptr::null_mut() { + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 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 new file mode 100644 index 0000000000000..05fb96339e33e --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed @@ -0,0 +1,72 @@ +// run-rustfix + +#[warn(clippy::cmp_owned)] +#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)] +fn main() { + fn with_to_string(x: &str) { + x != "foo"; + + "foo" != x; + } + + let x = "oh"; + + with_to_string(x); + + x != "foo"; + + x != "foo"; + + 42.to_string() == "42"; + + Foo == Foo; + + "abc".chars().filter(|c| *c != 'X'); + + "abc".chars().filter(|c| *c != 'X'); +} + +struct Foo; + +impl PartialEq for Foo { + // Allow this here, because it emits the lint + // without a suggestion. This is tested in + // `tests/ui/cmp_owned/without_suggestion.rs` + #[allow(clippy::cmp_owned)] + fn eq(&self, other: &Self) -> bool { + self.to_owned() == *other + } +} + +impl ToOwned for Foo { + type Owned = Bar; + fn to_owned(&self) -> Bar { + Bar + } +} + +#[derive(PartialEq)] +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Foo) -> bool { + true + } +} + +impl std::borrow::Borrow for Bar { + fn borrow(&self) -> &Foo { + static FOO: Foo = Foo; + &FOO + } +} + +#[derive(PartialEq)] +struct Baz; + +impl ToOwned for Baz { + type Owned = Baz; + fn to_owned(&self) -> Baz { + Baz + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs new file mode 100644 index 0000000000000..0a02825ed82f1 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs @@ -0,0 +1,72 @@ +// run-rustfix + +#[warn(clippy::cmp_owned)] +#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)] +fn main() { + fn with_to_string(x: &str) { + x != "foo".to_string(); + + "foo".to_string() != x; + } + + let x = "oh"; + + with_to_string(x); + + x != "foo".to_owned(); + + x != String::from("foo"); + + 42.to_string() == "42"; + + Foo.to_owned() == Foo; + + "abc".chars().filter(|c| c.to_owned() != 'X'); + + "abc".chars().filter(|c| *c != 'X'); +} + +struct Foo; + +impl PartialEq for Foo { + // Allow this here, because it emits the lint + // without a suggestion. This is tested in + // `tests/ui/cmp_owned/without_suggestion.rs` + #[allow(clippy::cmp_owned)] + fn eq(&self, other: &Self) -> bool { + self.to_owned() == *other + } +} + +impl ToOwned for Foo { + type Owned = Bar; + fn to_owned(&self) -> Bar { + Bar + } +} + +#[derive(PartialEq)] +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Foo) -> bool { + true + } +} + +impl std::borrow::Borrow for Bar { + fn borrow(&self) -> &Foo { + static FOO: Foo = Foo; + &FOO + } +} + +#[derive(PartialEq)] +struct Baz; + +impl ToOwned for Baz { + type Owned = Baz; + fn to_owned(&self) -> Baz { + Baz + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr new file mode 100644 index 0000000000000..2f333e6ea8ecb --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr @@ -0,0 +1,40 @@ +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:7:14 + | +LL | x != "foo".to_string(); + | ^^^^^^^^^^^^^^^^^ help: try: `"foo"` + | + = note: `-D clippy::cmp-owned` implied by `-D warnings` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:9:9 + | +LL | "foo".to_string() != x; + | ^^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:16:10 + | +LL | x != "foo".to_owned(); + | ^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:18:10 + | +LL | x != String::from("foo"); + | ^^^^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:22:5 + | +LL | Foo.to_owned() == Foo; + | ^^^^^^^^^^^^^^ help: try: `Foo` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:24:30 + | +LL | "abc".chars().filter(|c| c.to_owned() != 'X'); + | ^^^^^^^^^^^^ help: try: `*c` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs new file mode 100644 index 0000000000000..9ab8795474c67 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs @@ -0,0 +1,52 @@ +#[allow(clippy::unnecessary_operation)] + +fn main() { + let x = &Baz; + let y = &Baz; + y.to_owned() == *x; + + let x = &&Baz; + let y = &Baz; + y.to_owned() == **x; +} + +struct Foo; + +impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { + self.to_owned() == *other + } +} + +impl ToOwned for Foo { + type Owned = Bar; + fn to_owned(&self) -> Bar { + Bar + } +} + +#[derive(PartialEq)] +struct Baz; + +impl ToOwned for Baz { + type Owned = Baz; + fn to_owned(&self) -> Baz { + Baz + } +} + +#[derive(PartialEq)] +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Foo) -> bool { + true + } +} + +impl std::borrow::Borrow for Bar { + fn borrow(&self) -> &Foo { + static FOO: Foo = Foo; + &FOO + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr new file mode 100644 index 0000000000000..6e8a5ad2a17b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr @@ -0,0 +1,22 @@ +error: this creates an owned instance just for comparison + --> $DIR/without_suggestion.rs:6:5 + | +LL | y.to_owned() == *x; + | ^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating + | + = note: `-D clippy::cmp-owned` implied by `-D warnings` + +error: this creates an owned instance just for comparison + --> $DIR/without_suggestion.rs:10:5 + | +LL | y.to_owned() == **x; + | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating + +error: this creates an owned instance just for comparison + --> $DIR/without_suggestion.rs:17:9 + | +LL | self.to_owned() == *other + | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.rs b/src/tools/clippy/tests/ui/cognitive_complexity.rs new file mode 100644 index 0000000000000..1d3fe405521cd --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity.rs @@ -0,0 +1,395 @@ +#![allow(clippy::all)] +#![warn(clippy::cognitive_complexity)] +#![allow(unused)] + +#[rustfmt::skip] +fn main() { + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } +} + +#[clippy::cognitive_complexity = "1"] +fn kaboom() { + let n = 0; + 'a: for i in 0..20 { + 'b: for j in i..20 { + for k in j..20 { + if k == 5 { + break 'b; + } + if j == 3 && k == 6 { + continue 'a; + } + if k == j { + continue; + } + println!("bake"); + } + } + println!("cake"); + } +} + +fn bloo() { + match 42 { + 0 => println!("hi"), + 1 => println!("hai"), + 2 => println!("hey"), + 3 => println!("hallo"), + 4 => println!("hello"), + 5 => println!("salut"), + 6 => println!("good morning"), + 7 => println!("good evening"), + 8 => println!("good afternoon"), + 9 => println!("good night"), + 10 => println!("bonjour"), + 11 => println!("hej"), + 12 => println!("hej hej"), + 13 => println!("greetings earthling"), + 14 => println!("take us to you leader"), + 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 => println!("take us to you leader"), + 35 | 37 | 39 | 41 | 43 | 45 | 47 | 49 | 51 | 53 => println!("there is no undefined behavior"), + 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 => println!("I know borrow-fu"), + _ => println!("bye"), + } +} + +// Short circuiting operations don't increase the complexity of a function. +// Note that the minimum complexity of a function is 1. +#[clippy::cognitive_complexity = "1"] +fn lots_of_short_circuits() -> bool { + true && false && true && false && true && false && true +} + +#[clippy::cognitive_complexity = "1"] +fn lots_of_short_circuits2() -> bool { + true || false || true || false || true || false || true +} + +#[clippy::cognitive_complexity = "1"] +fn baa() { + let x = || match 99 { + 0 => 0, + 1 => 1, + 2 => 2, + 4 => 4, + 6 => 6, + 9 => 9, + _ => 42, + }; + if x() == 42 { + println!("x"); + } else { + println!("not x"); + } +} + +#[clippy::cognitive_complexity = "1"] +fn bar() { + match 99 { + 0 => println!("hi"), + _ => println!("bye"), + } +} + +#[test] +#[clippy::cognitive_complexity = "1"] +/// Tests are usually complex but simple at the same time. `clippy::cognitive_complexity` used to +/// give lots of false-positives in tests. +fn dont_warn_on_tests() { + match 99 { + 0 => println!("hi"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barr() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barr2() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrr() { + match 99 { + 0 => println!("hi"), + 1 => panic!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrr2() { + match 99 { + 0 => println!("hi"), + 1 => panic!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } + match 99 { + 0 => println!("hi"), + 1 => panic!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrrr() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => panic!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrrr2() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => panic!("blub"), + _ => println!("bye"), + } + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => panic!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn cake() { + if 4 == 5 { + println!("yea"); + } else { + panic!("meh"); + } + println!("whee"); +} + +#[clippy::cognitive_complexity = "1"] +pub fn read_file(input_path: &str) -> String { + use std::fs::File; + use std::io::{Read, Write}; + use std::path::Path; + let mut file = match File::open(&Path::new(input_path)) { + Ok(f) => f, + Err(err) => { + panic!("Can't open {}: {}", input_path, err); + }, + }; + + let mut bytes = Vec::new(); + + match file.read_to_end(&mut bytes) { + Ok(..) => {}, + Err(_) => { + panic!("Can't read {}", input_path); + }, + }; + + match String::from_utf8(bytes) { + Ok(contents) => contents, + Err(_) => { + panic!("{} is not UTF-8 encoded", input_path); + }, + } +} + +enum Void {} + +#[clippy::cognitive_complexity = "1"] +fn void(void: Void) { + if true { + match void {} + } +} + +#[clippy::cognitive_complexity = "1"] +fn mcarton_sees_all() { + panic!("meh"); + panic!("möh"); +} + +#[clippy::cognitive_complexity = "1"] +fn try_() -> Result { + match 5 { + 5 => Ok(5), + _ => return Err("bla"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn try_again() -> Result { + let _ = Ok(42)?; + let _ = Ok(43)?; + let _ = Ok(44)?; + let _ = Ok(45)?; + let _ = Ok(46)?; + let _ = Ok(47)?; + let _ = Ok(48)?; + let _ = Ok(49)?; + match 5 { + 5 => Ok(5), + _ => return Err("bla"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn early() -> Result { + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); +} + +#[rustfmt::skip] +#[clippy::cognitive_complexity = "1"] +fn early_ret() -> i32 { + let a = if true { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + match 5 { + 5 => 5, + _ => return 6, + } +} + +#[clippy::cognitive_complexity = "1"] +fn closures() { + let x = |a: i32, b: i32| -> i32 { + if true { + println!("moo"); + } + + a + b + }; +} + +struct Moo; + +#[clippy::cognitive_complexity = "1"] +impl Moo { + fn moo(&self) { + if true { + println!("moo"); + } + } +} diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.stderr b/src/tools/clippy/tests/ui/cognitive_complexity.stderr new file mode 100644 index 0000000000000..a0ddc673abcc1 --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity.stderr @@ -0,0 +1,139 @@ +error: the function has a cognitive complexity of (28/25) + --> $DIR/cognitive_complexity.rs:6:4 + | +LL | fn main() { + | ^^^^ + | + = note: `-D clippy::cognitive-complexity` implied by `-D warnings` + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (7/1) + --> $DIR/cognitive_complexity.rs:91:4 + | +LL | fn kaboom() { + | ^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:149:4 + | +LL | fn baa() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:150:13 + | +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) + --> $DIR/cognitive_complexity.rs:167:4 + | +LL | fn bar() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:186:4 + | +LL | fn barr() { + | ^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (3/1) + --> $DIR/cognitive_complexity.rs:196:4 + | +LL | fn barr2() { + | ^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:212:4 + | +LL | fn barrr() { + | ^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (3/1) + --> $DIR/cognitive_complexity.rs:222:4 + | +LL | fn barrr2() { + | ^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:238:4 + | +LL | fn barrrr() { + | ^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (3/1) + --> $DIR/cognitive_complexity.rs:248:4 + | +LL | fn barrrr2() { + | ^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:264:4 + | +LL | fn cake() { + | ^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (4/1) + --> $DIR/cognitive_complexity.rs:274:8 + | +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) + --> $DIR/cognitive_complexity.rs:305:4 + | +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) + --> $DIR/cognitive_complexity.rs:356:4 + | +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) + --> $DIR/cognitive_complexity.rs:377:13 + | +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) + --> $DIR/cognitive_complexity.rs:390:8 + | +LL | fn moo(&self) { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs new file mode 100644 index 0000000000000..403eff566ed6d --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs @@ -0,0 +1,15 @@ +#![warn(clippy::cognitive_complexity)] +#![warn(unused)] + +fn main() { + kaboom(); +} + +#[clippy::cognitive_complexity = "0"] +fn kaboom() { + if 42 == 43 { + panic!(); + } else if "cake" == "lie" { + println!("what?"); + } +} diff --git a/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr new file mode 100644 index 0000000000000..f5ff53dda603b --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr @@ -0,0 +1,11 @@ +error: the function has a cognitive complexity of (3/0) + --> $DIR/cognitive_complexity_attr_used.rs:9:4 + | +LL | fn kaboom() { + | ^^^^^^ + | + = note: `-D clippy::cognitive-complexity` implied by `-D warnings` + = help: you could split it up into multiple smaller functions + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed new file mode 100644 index 0000000000000..ce2a1c28c8a80 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed @@ -0,0 +1,66 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + // Collapse `else { if .. }` to `else if ..` + if x == "hello" { + print!("Hello "); + } else if y == "world" { + println!("world!") + } + + if x == "hello" { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world!") + } + + if x == "hello" { + print!("Hello "); + } else if y == "world" { + println!("world") + } + else { + println!("!") + } + + if x == "hello" { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else if x == "hello" { + println!("world") + } + else { + println!("!") + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs new file mode 100644 index 0000000000000..99c40b8d38eb9 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs @@ -0,0 +1,80 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + // Collapse `else { if .. }` to `else if ..` + if x == "hello" { + print!("Hello "); + } else { + if y == "world" { + println!("world!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + if y == "world" { + println!("world") + } + else { + println!("!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else { + if x == "hello" { + println!("world") + } + else { + println!("!") + } + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr new file mode 100644 index 0000000000000..28048999e8ec9 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr @@ -0,0 +1,154 @@ +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:12:12 + | +LL | } else { + | ____________^ +LL | | if y == "world" { +LL | | println!("world!") +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` +help: try + | +LL | } else if y == "world" { +LL | println!("world!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:20:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world!") +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:28:12 + | +LL | } else { + | ____________^ +LL | | if y == "world" { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if y == "world" { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:39:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:50:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:61:12 + | +LL | } else { + | ____________^ +LL | | if x == "hello" { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if x == "hello" { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:72:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed new file mode 100644 index 0000000000000..561283fc8e73d --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -0,0 +1,138 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + if x == "hello" && y == "world" { + println!("Hello world!"); + } + + if (x == "hello" || x == "world") && (y == "world" || y == "hello") { + println!("Hello world!"); + } + + if x == "hello" && x == "world" && (y == "world" || y == "hello") { + println!("Hello world!"); + } + + if (x == "hello" || x == "world") && y == "world" && y == "hello" { + println!("Hello world!"); + } + + if x == "hello" && x == "world" && y == "world" && y == "hello" { + println!("Hello world!"); + } + + if 42 == 1337 && 'a' != 'A' { + println!("world!") + } + + // Works because any if with an else statement cannot be collapsed. + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } + } else { + println!("Not Hello world"); + } + + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } else { + println!("Hello something else"); + } + } + + if x == "hello" { + print!("Hello "); + if y == "world" { + println!("world!") + } + } + + if true { + } else { + 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" && y == "world" { // Collapsible + println!("Hello world!"); + } + + if x == "hello" { + print!("Hello "); + } else { + // Not collapsible + if y == "world" { + println!("world!") + } + } + + if x == "hello" { + 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!"); + } + } + + // 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() {} + } + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs new file mode 100644 index 0000000000000..dc9d9b451c0f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -0,0 +1,152 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" || x == "world" { + if y == "world" || y == "hello" { + println!("Hello world!"); + } + } + + if x == "hello" && x == "world" { + if y == "world" || y == "hello" { + println!("Hello world!"); + } + } + + if x == "hello" || x == "world" { + if y == "world" && y == "hello" { + println!("Hello world!"); + } + } + + if x == "hello" && x == "world" { + if y == "world" && y == "hello" { + println!("Hello world!"); + } + } + + if 42 == 1337 { + if 'a' != 'A' { + println!("world!") + } + } + + // Works because any if with an else statement cannot be collapsed. + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } + } else { + println!("Not Hello world"); + } + + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } else { + println!("Hello something else"); + } + } + + if x == "hello" { + print!("Hello "); + if y == "world" { + println!("world!") + } + } + + if true { + } else { + 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!"); + } + } + + if x == "hello" { + print!("Hello "); + } else { + // Not collapsible + if y == "world" { + println!("world!") + } + } + + if x == "hello" { + 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!"); + } + } + + // 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() {} + } + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr new file mode 100644 index 0000000000000..6440ff41be81e --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -0,0 +1,122 @@ +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:9:5 + | +LL | / if x == "hello" { +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` +help: try + | +LL | if x == "hello" && y == "world" { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:15:5 + | +LL | / if x == "hello" || x == "world" { +LL | | if y == "world" || y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:21:5 + | +LL | / if x == "hello" && x == "world" { +LL | | if y == "world" || y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:27:5 + | +LL | / if x == "hello" || x == "world" { +LL | | if y == "world" && y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:33:5 + | +LL | / if x == "hello" && x == "world" { +LL | | if y == "world" && y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if x == "hello" && x == "world" && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:39:5 + | +LL | / if 42 == 1337 { +LL | | if 'a' != 'A' { +LL | | println!("world!") +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if 42 == 1337 && 'a' != 'A' { +LL | println!("world!") +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:95:5 + | +LL | / if x == "hello" { +LL | | if y == "world" { // Collapsible +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if x == "hello" && y == "world" { // Collapsible +LL | println!("Hello world!"); +LL | } + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.fixed new file mode 100644 index 0000000000000..e588c23345e2f --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +where + F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + 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); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.rs new file mode 100644 index 0000000000000..d5dd3bb562b42 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.rs @@ -0,0 +1,101 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +where + F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_help(expr.span, help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.help(help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_note(expr.span, note_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + }); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.stderr new file mode 100644 index 0000000000000..874d4a9f255c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.stderr @@ -0,0 +1,49 @@ +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:75:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +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)` + | +note: the lint level is defined here + --> $DIR/collapsible_span_lint_calls.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` + +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:78:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +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 + --> $DIR/collapsible_span_lint_calls.rs:81:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +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 collspible + --> $DIR/collapsible_span_lint_calls.rs:84:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +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 collspible + --> $DIR/collapsible_span_lint_calls.rs:87:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.note(note_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/comparison_chain.rs b/src/tools/clippy/tests/ui/comparison_chain.rs new file mode 100644 index 0000000000000..3b03f8c7dfe7c --- /dev/null +++ b/src/tools/clippy/tests/ui/comparison_chain.rs @@ -0,0 +1,206 @@ +#![allow(dead_code)] +#![warn(clippy::comparison_chain)] + +fn a() {} +fn b() {} +fn c() {} + +fn f(x: u8, y: u8, z: u8) { + // Ignored: Only one branch + if x > y { + a() + } + + if x > y { + a() + } else if x < y { + b() + } + + // Ignored: Only one explicit conditional + if x > y { + a() + } else { + b() + } + + if x > y { + a() + } else if x < y { + b() + } else { + c() + } + + if x > y { + a() + } else if y > x { + b() + } else { + c() + } + + if x > 1 { + a() + } else if x < 1 { + b() + } else if x == 1 { + c() + } + + // Ignored: Binop args are not equivalent + if x > 1 { + a() + } else if y > 1 { + b() + } else { + c() + } + + // Ignored: Binop args are not equivalent + if x > y { + a() + } else if x > z { + b() + } else if y > z { + c() + } + + // Ignored: Not binary comparisons + if true { + a() + } else if false { + b() + } else { + c() + } +} + +#[allow(clippy::float_cmp)] +fn g(x: f64, y: f64, z: f64) { + // Ignored: f64 doesn't implement Ord + if x > y { + a() + } else if x < y { + b() + } + + // Ignored: f64 doesn't implement Ord + if x > y { + a() + } else if x < y { + b() + } else { + c() + } + + // Ignored: f64 doesn't implement Ord + if x > y { + a() + } else if y > x { + b() + } else { + c() + } + + // Ignored: f64 doesn't implement Ord + if x > 1.0 { + a() + } else if x < 1.0 { + b() + } else if x == 1.0 { + c() + } +} + +fn h(x: T, y: T, z: T) { + if x > y { + a() + } else if x < y { + b() + } + + if x > y { + a() + } else if x < y { + b() + } else { + c() + } + + if x > y { + a() + } else if y > x { + b() + } else { + c() + } +} + +// The following uses should be ignored +mod issue_5212 { + use super::{a, b, c}; + fn foo() -> u8 { + 21 + } + + fn same_operation_equals() { + // operands are fixed + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } else { + c() + } + + // operands are transposed + + if foo() == 42 { + a() + } else if 42 == foo() { + b() + } + } + + fn same_operation_not_equals() { + // operands are fixed + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } else { + c() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } else { + c() + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr new file mode 100644 index 0000000000000..69db88b03b5b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/comparison_chain.stderr @@ -0,0 +1,97 @@ +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:14:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } + | |_____^ + | + = note: `-D clippy::comparison-chain` implied by `-D warnings` + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:27:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:35:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if y > x { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:43:5 + | +LL | / if x > 1 { +LL | | a() +LL | | } else if x < 1 { +LL | | b() +LL | | } else if x == 1 { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:117:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:123:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:131:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if y > x { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/complex_types.rs b/src/tools/clippy/tests/ui/complex_types.rs new file mode 100644 index 0000000000000..be61fb6b9be61 --- /dev/null +++ b/src/tools/clippy/tests/ui/complex_types.rs @@ -0,0 +1,60 @@ +#![warn(clippy::all)] +#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)] +#![feature(associated_type_defaults)] + +type Alias = Vec>>; // no warning here + +const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); +static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + +struct S { + f: Vec>>, +} + +struct TS(Vec>>); + +enum E { + Tuple(Vec>>), + Struct { f: Vec>> }, +} + +impl S { + const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + fn impl_method(&self, p: Vec>>) {} +} + +trait T { + const A: Vec>>; + type B = Vec>>; + fn method(&self, p: Vec>>); + fn def_method(&self, p: Vec>>) {} +} + +fn test1() -> Vec>> { + vec![] +} + +fn test2(_x: Vec>>) {} + +fn test3() { + let _y: Vec>> = vec![]; +} + +#[repr(C)] +struct D { + // should not warn, since we don't have control over the signature (#3222) + test4: extern "C" fn( + itself: &D, + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + f: usize, + g: usize, + h: usize, + i: usize, + ), +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/complex_types.stderr b/src/tools/clippy/tests/ui/complex_types.stderr new file mode 100644 index 0000000000000..8f5dbd27956c5 --- /dev/null +++ b/src/tools/clippy/tests/ui/complex_types.stderr @@ -0,0 +1,94 @@ +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:7:12 + | +LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::type-complexity` implied by `-D warnings` + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:8: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 + --> $DIR/complex_types.rs:11:8 + | +LL | f: Vec>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:14:11 + | +LL | struct TS(Vec>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:17:11 + | +LL | Tuple(Vec>>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:18:17 + | +LL | Struct { f: Vec>> }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:22: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 + --> $DIR/complex_types.rs:23:30 + | +LL | fn impl_method(&self, p: Vec>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:27:14 + | +LL | const A: Vec>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:28:14 + | +LL | type B = Vec>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:29:25 + | +LL | fn method(&self, p: Vec>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:30:29 + | +LL | fn def_method(&self, p: Vec>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:33:15 + | +LL | fn test1() -> Vec>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:37:14 + | +LL | fn test2(_x: Vec>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:40:13 + | +LL | let _y: Vec>> = vec![]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/copy_iterator.rs b/src/tools/clippy/tests/ui/copy_iterator.rs new file mode 100644 index 0000000000000..e3d5928be231b --- /dev/null +++ b/src/tools/clippy/tests/ui/copy_iterator.rs @@ -0,0 +1,23 @@ +#![warn(clippy::copy_iterator)] + +#[derive(Copy, Clone)] +struct Countdown(u8); + +impl Iterator for Countdown { + type Item = u8; + + fn next(&mut self) -> Option { + self.0.checked_sub(1).map(|c| { + self.0 = c; + c + }) + } +} + +fn main() { + let my_iterator = Countdown(5); + let a: Vec<_> = my_iterator.take(1).collect(); + assert_eq!(a.len(), 1); + let b: Vec<_> = my_iterator.collect(); + assert_eq!(b.len(), 5); +} diff --git a/src/tools/clippy/tests/ui/copy_iterator.stderr b/src/tools/clippy/tests/ui/copy_iterator.stderr new file mode 100644 index 0000000000000..f8ce6af7961a6 --- /dev/null +++ b/src/tools/clippy/tests/ui/copy_iterator.stderr @@ -0,0 +1,17 @@ +error: you are implementing `Iterator` on a `Copy` type + --> $DIR/copy_iterator.rs:6:1 + | +LL | / impl Iterator for Countdown { +LL | | type Item = u8; +LL | | +LL | | fn next(&mut self) -> Option { +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::copy-iterator` implied by `-D warnings` + = note: consider implementing `IntoIterator` instead + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs new file mode 100644 index 0000000000000..4bb833795bb16 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs @@ -0,0 +1,15 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1698 + +pub trait Trait { + const CONSTANT: u8; +} + +impl Trait for u8 { + const CONSTANT: u8 = 2; +} + +fn main() { + println!("{}", u8::CONSTANT * 10); +} diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs new file mode 100644 index 0000000000000..58a20caf6c676 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs @@ -0,0 +1,9 @@ +pub trait Trait { + fn fun(par: &str) -> &str; +} + +impl Trait for str { + fn fun(par: &str) -> &str { + &par[0..1] + } +} diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs new file mode 100644 index 0000000000000..086548e58ed67 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -0,0 +1,37 @@ +// no-prefer-dynamic +// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro +// crates. If we don't set this, compiletest will override the `crate_type` attribute below and +// compile this as dylib. Removing this then causes the test to fail because a `dylib` crate can't +// contain a proc-macro. + +#![feature(repr128)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; +use std::iter::FromIterator; + +#[proc_macro] +pub fn macro_test(input_stream: TokenStream) -> TokenStream { + let first_token = input_stream.into_iter().next().unwrap(); + let span = first_token.span(); + + TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("fn", Span::call_site())), + TokenTree::Ident(Ident::new("code", Span::call_site())), + TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), + TokenTree::Group(Group::new(Delimiter::Brace, { + let mut clause = Group::new(Delimiter::Brace, TokenStream::new()); + clause.set_span(span); + + TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("if", Span::call_site())), + TokenTree::Ident(Ident::new("true", Span::call_site())), + TokenTree::Group(clause.clone()), + TokenTree::Ident(Ident::new("else", Span::call_site())), + TokenTree::Group(clause), + ]) + })), + ]) +} diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs new file mode 100644 index 0000000000000..a8a85b4baefb7 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs @@ -0,0 +1,15 @@ +macro_rules! use_self { + ( + impl $ty:ident { + fn func(&$this:ident) { + [fields($($field:ident)*)] + } + } + ) => ( + impl $ty { + fn func(&$this) { + let $ty { $($field),* } = $this; + } + } + ) +} diff --git a/src/tools/clippy/tests/ui/crashes/cc_seme.rs b/src/tools/clippy/tests/ui/crashes/cc_seme.rs new file mode 100644 index 0000000000000..c48c7e9e6c6ba --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/cc_seme.rs @@ -0,0 +1,29 @@ +// run-pass + +#[allow(dead_code)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/478 + +enum Baz { + One, + Two, +} + +struct Test { + t: Option, + b: Baz, +} + +fn main() {} + +pub fn foo() { + use Baz::*; + let x = Test { t: Some(0), b: One }; + + match x { + Test { t: Some(_), b: One } => unreachable!(), + Test { t: Some(42), b: Two } => unreachable!(), + Test { t: None, .. } => unreachable!(), + Test { .. } => unreachable!(), + } +} 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 new file mode 100644 index 0000000000000..db1fa871afe00 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs @@ -0,0 +1,8 @@ +// run-pass + +#![deny(clippy::all)] +#![allow(unused_imports)] + +use std::*; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1588.rs b/src/tools/clippy/tests/ui/crashes/ice-1588.rs new file mode 100644 index 0000000000000..15d0f705b367f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-1588.rs @@ -0,0 +1,15 @@ +// run-pass + +#![allow(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1588 + +fn main() { + match 1 { + 1 => {}, + 2 => { + [0; 1]; + }, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs new file mode 100644 index 0000000000000..1ca6b6976b38f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs @@ -0,0 +1,28 @@ +// run-pass + +#![allow(dead_code, unused_variables)] + +/// Should not trigger an ICE in `SpanlessEq` / `consts::constant` +/// +/// Issue: https://github.com/rust-lang/rust-clippy/issues/1782 +use std::{mem, ptr}; + +fn spanless_eq_ice() { + let txt = "something"; + match txt { + "something" => unsafe { + ptr::write( + ptr::null_mut() as *mut u32, + mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), + ) + }, + _ => unsafe { + ptr::write( + ptr::null_mut() as *mut u32, + mem::transmute::<[u8; 4], _>([13, 246, 24, 255]), + ) + }, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1969.rs b/src/tools/clippy/tests/ui/crashes/ice-1969.rs new file mode 100644 index 0000000000000..837ec9df31ab1 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-1969.rs @@ -0,0 +1,15 @@ +// run-pass + +#![allow(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1969 + +fn main() {} + +pub trait Convert { + type Action: From<*const f64>; + + fn convert(val: *const f64) -> Self::Action { + val.into() + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2499.rs b/src/tools/clippy/tests/ui/crashes/ice-2499.rs new file mode 100644 index 0000000000000..ffef1631775ee --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2499.rs @@ -0,0 +1,28 @@ +// run-pass + +#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] + +/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` +/// +/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499 + +fn f(s: &[u8]) -> bool { + let t = s[0] as char; + + match t { + 'E' | 'W' => {}, + 'T' => { + if s[0..4] != ['0' as u8; 4] { + return false; + } else { + return true; + } + }, + _ => { + return false; + }, + } + true +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2594.rs b/src/tools/clippy/tests/ui/crashes/ice-2594.rs new file mode 100644 index 0000000000000..ac19f1976e912 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2594.rs @@ -0,0 +1,22 @@ +// run-pass + +#![allow(dead_code, unused_variables)] + +/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` +/// +/// Issue: https://github.com/rust-lang/rust-clippy/issues/2594 + +fn spanless_hash_ice() { + let txt = "something"; + let empty_header: [u8; 1] = [1; 1]; + + match txt { + "something" => { + let mut headers = [empty_header; 1]; + }, + "" => (), + _ => (), + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2636.rs b/src/tools/clippy/tests/ui/crashes/ice-2636.rs new file mode 100644 index 0000000000000..e0b58157590ab --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2636.rs @@ -0,0 +1,22 @@ +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +macro_rules! test_hash { + ($foo:expr, $($t:ident => $ord:expr),+ ) => { + use self::Foo::*; + match $foo { + $ ( & $t => $ord, + )* + }; + }; +} + +fn main() { + let a = Foo::A; + test_hash!(&a, A => 0, B => 1, C => 2); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2636.stderr b/src/tools/clippy/tests/ui/crashes/ice-2636.stderr new file mode 100644 index 0000000000000..53799b4fbf1d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2636.stderr @@ -0,0 +1,17 @@ +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/ice-2636.rs:12:9 + | +LL | / match $foo { +LL | | $ ( & $t => $ord, +LL | | )* +LL | | }; + | |_________^ +... +LL | test_hash!(&a, A => 0, B => 1, C => 2); + | --------------------------------------- in this macro invocation + | + = note: `-D clippy::match-ref-pats` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-2727.rs b/src/tools/clippy/tests/ui/crashes/ice-2727.rs new file mode 100644 index 0000000000000..d832c2860332d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2727.rs @@ -0,0 +1,9 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2727 + +pub fn f(new: fn()) { + new(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2760.rs b/src/tools/clippy/tests/ui/crashes/ice-2760.rs new file mode 100644 index 0000000000000..9e5e299c336a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2760.rs @@ -0,0 +1,25 @@ +// run-pass + +#![allow( + unused_variables, + clippy::blacklisted_name, + clippy::needless_pass_by_value, + dead_code +)] + +/// This should not compile-fail with: +/// +/// error[E0277]: the trait bound `T: Foo` is not satisfied +// See rust-lang/rust-clippy#2760. + +trait Foo { + type Bar; +} + +struct Baz { + bar: T::Bar, +} + +fn take(baz: Baz) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.rs b/src/tools/clippy/tests/ui/crashes/ice-2774.rs new file mode 100644 index 0000000000000..47f8e3b18eeaa --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2774.rs @@ -0,0 +1,29 @@ +// run-pass + +use std::collections::HashSet; + +// See rust-lang/rust-clippy#2774. + +#[derive(Eq, PartialEq, Debug, Hash)] +pub struct Bar { + foo: Foo, +} + +#[derive(Eq, PartialEq, Debug, Hash)] +pub struct Foo {} + +#[allow(clippy::implicit_hasher)] +// This should not cause a "cannot relate bound region" ICE. +pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { + let mut foos = HashSet::new(); + foos.extend(bars.iter().map(|b| &b.foo)); +} + +#[allow(clippy::implicit_hasher)] +// Also, this should not cause a "cannot relate bound region" ICE. +pub fn add_barfoos_to_foos2(bars: &HashSet<&Bar>) { + let mut foos = HashSet::new(); + foos.extend(bars.iter().map(|b| &b.foo)); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2862.rs b/src/tools/clippy/tests/ui/crashes/ice-2862.rs new file mode 100644 index 0000000000000..47324ce183169 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2862.rs @@ -0,0 +1,18 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2862 + +pub trait FooMap { + fn map B>(&self, f: F) -> B; +} + +impl FooMap for bool { + fn map B>(&self, f: F) -> B { + f() + } +} + +fn main() { + let a = true; + a.map(|| false); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2865.rs b/src/tools/clippy/tests/ui/crashes/ice-2865.rs new file mode 100644 index 0000000000000..c4f6c0fed6820 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2865.rs @@ -0,0 +1,18 @@ +// run-pass + +#[allow(dead_code)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 + +struct Ice { + size: String, +} + +impl<'a> From for Ice { + fn from(_: String) -> Self { + let text = || "iceberg".to_string(); + Self { size: text() } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3151.rs b/src/tools/clippy/tests/ui/crashes/ice-3151.rs new file mode 100644 index 0000000000000..ffad2d06b56e2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3151.rs @@ -0,0 +1,17 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 + +#[derive(Clone)] +pub struct HashMap { + hash_builder: S, + table: RawTable, +} + +#[derive(Clone)] +pub struct RawTable { + size: usize, + val: V, +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs new file mode 100644 index 0000000000000..95c7dff9be36b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs @@ -0,0 +1,25 @@ +// run-pass + +#![warn(clippy::all)] +#![allow(clippy::blacklisted_name)] +#![allow(unused)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/3462 + +enum Foo { + Bar, + Baz, +} + +fn bar(foo: Foo) { + macro_rules! baz { + () => { + if let Foo::Bar = foo {} + }; + } + + baz!(); + baz!(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.rs b/src/tools/clippy/tests/ui/crashes/ice-360.rs new file mode 100644 index 0000000000000..6555c19ca6a26 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-360.rs @@ -0,0 +1,12 @@ +fn main() {} + +fn no_panic(slice: &[T]) { + let mut iter = slice.iter(); + loop { + let _ = match iter.next() { + Some(ele) => ele, + None => break, + }; + loop {} + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.stderr b/src/tools/clippy/tests/ui/crashes/ice-360.stderr new file mode 100644 index 0000000000000..84e31eaf2e9f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-360.stderr @@ -0,0 +1,24 @@ +error: this loop could be written as a `while let` loop + --> $DIR/ice-360.rs:5:5 + | +LL | / loop { +LL | | let _ = match iter.next() { +LL | | Some(ele) => ele, +LL | | None => break, +LL | | }; +LL | | loop {} +LL | | } + | |_____^ help: try: `while let Some(ele) = iter.next() { .. }` + | + = note: `-D clippy::while-let-loop` implied by `-D warnings` + +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/ice-360.rs:10:9 + | +LL | loop {} + | ^^^^^^^ + | + = note: `-D clippy::empty-loop` implied by `-D warnings` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.rs b/src/tools/clippy/tests/ui/crashes/ice-3717.rs new file mode 100644 index 0000000000000..f50714643fd25 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3717.rs @@ -0,0 +1,10 @@ +#![deny(clippy::implicit_hasher)] + +use std::collections::HashSet; + +fn main() {} + +pub fn ice_3717(_: &HashSet) { + let _ = [0u8; 0]; + let _: HashSet = HashSet::new(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr new file mode 100644 index 0000000000000..296c95abb96d3 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr @@ -0,0 +1,22 @@ +error: parameter of type `HashSet` should be generalized over different hashers + --> $DIR/ice-3717.rs:7:21 + | +LL | pub fn ice_3717(_: &HashSet) { + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/ice-3717.rs:1:9 + | +LL | #![deny(clippy::implicit_hasher)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: consider adding a type parameter + | +LL | pub fn ice_3717(_: &HashSet) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | let _: HashSet = HashSet::default(); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-3741.rs b/src/tools/clippy/tests/ui/crashes/ice-3741.rs new file mode 100644 index 0000000000000..a548415da62bd --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3741.rs @@ -0,0 +1,11 @@ +// aux-build:proc_macro_crash.rs +// run-pass + +#![warn(clippy::suspicious_else_formatting)] + +extern crate proc_macro_crash; +use proc_macro_crash::macro_test; + +fn main() { + macro_test!(2); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3747.rs b/src/tools/clippy/tests/ui/crashes/ice-3747.rs new file mode 100644 index 0000000000000..d0b44ebafeeb2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3747.rs @@ -0,0 +1,19 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/3747 + +macro_rules! a { + ( $pub:tt $($attr:tt)* ) => { + $($attr)* $pub fn say_hello() {} + }; +} + +macro_rules! b { + () => { + a! { pub } + }; +} + +b! {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3891.rs b/src/tools/clippy/tests/ui/crashes/ice-3891.rs new file mode 100644 index 0000000000000..05c5134c8457b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3891.rs @@ -0,0 +1,3 @@ +fn main() { + 1x; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3891.stderr b/src/tools/clippy/tests/ui/crashes/ice-3891.stderr new file mode 100644 index 0000000000000..5a285b0e71492 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3891.stderr @@ -0,0 +1,10 @@ +error: invalid suffix `x` for integer literal + --> $DIR/ice-3891.rs:2:5 + | +LL | 1x; + | ^^ invalid suffix `x` + | + = help: the suffix must be one of the integral types (`u32`, `isize`, etc) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-4121.rs b/src/tools/clippy/tests/ui/crashes/ice-4121.rs new file mode 100644 index 0000000000000..e1a142fdcb67f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4121.rs @@ -0,0 +1,13 @@ +use std::mem; + +pub struct Foo(A, B); + +impl Foo { + const HOST_SIZE: usize = mem::size_of::(); + + pub fn crash() -> bool { + Self::HOST_SIZE == 0 + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4545.rs b/src/tools/clippy/tests/ui/crashes/ice-4545.rs new file mode 100644 index 0000000000000..d9c9c2096d97c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4545.rs @@ -0,0 +1,14 @@ +fn repro() { + trait Foo { + type Bar; + } + + #[allow(dead_code)] + struct Baz { + field: T::Bar, + } +} + +fn main() { + repro(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4579.rs b/src/tools/clippy/tests/ui/crashes/ice-4579.rs new file mode 100644 index 0000000000000..2e7e279f847dd --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4579.rs @@ -0,0 +1,13 @@ +#![allow(clippy::single_match)] + +use std::ptr; + +fn main() { + match Some(0_usize) { + Some(_) => { + let s = "012345"; + unsafe { ptr::read(s.as_ptr().offset(1) as *const [u8; 5]) }; + }, + _ => (), + }; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4671.rs b/src/tools/clippy/tests/ui/crashes/ice-4671.rs new file mode 100644 index 0000000000000..64e8e7769412d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4671.rs @@ -0,0 +1,21 @@ +#![warn(clippy::use_self)] + +#[macro_use] +#[path = "auxiliary/use_self_macro.rs"] +mod use_self_macro; + +struct Foo { + a: u32, +} + +use_self! { + impl Foo { + fn func(&self) { + [fields( + a + )] + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4727.rs b/src/tools/clippy/tests/ui/crashes/ice-4727.rs new file mode 100644 index 0000000000000..cdb59caec67ed --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4727.rs @@ -0,0 +1,8 @@ +// run-pass + +#![warn(clippy::use_self)] + +#[path = "auxiliary/ice-4727-aux.rs"] +mod aux; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4760.rs b/src/tools/clippy/tests/ui/crashes/ice-4760.rs new file mode 100644 index 0000000000000..ead67d5ed1b1e --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4760.rs @@ -0,0 +1,10 @@ +// run-pass +const COUNT: usize = 2; +struct Thing; +trait Dummy {} + +const _: () = { + impl Dummy for Thing where [i32; COUNT]: Sized {} +}; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4775.rs b/src/tools/clippy/tests/ui/crashes/ice-4775.rs new file mode 100644 index 0000000000000..31e53e846d54d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4775.rs @@ -0,0 +1,14 @@ +#![feature(const_generics)] +#![allow(incomplete_features)] + +pub struct ArrayWrapper([usize; N]); + +impl ArrayWrapper<{ N }> { + pub fn ice(&self) { + for i in self.0.iter() { + println!("{}", i); + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4968.rs b/src/tools/clippy/tests/ui/crashes/ice-4968.rs new file mode 100644 index 0000000000000..3822f17459854 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4968.rs @@ -0,0 +1,20 @@ +// check-pass + +// Test for https://github.com/rust-lang/rust-clippy/issues/4968 + +#![warn(clippy::unsound_collection_transmute)] + +trait Trait { + type Assoc; +} + +use std::mem::{self, ManuallyDrop}; + +#[allow(unused)] +fn func(slice: Vec) { + unsafe { + let _: Vec> = mem::transmute(slice); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5207.rs b/src/tools/clippy/tests/ui/crashes/ice-5207.rs new file mode 100644 index 0000000000000..1b20c9defac82 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5207.rs @@ -0,0 +1,7 @@ +// edition:2018 + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/5207 + +pub async fn bar<'a, T: 'a>(_: T) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5223.rs b/src/tools/clippy/tests/ui/crashes/ice-5223.rs new file mode 100644 index 0000000000000..9bb2e227fc12e --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5223.rs @@ -0,0 +1,18 @@ +// Regression test for #5233 + +#![feature(const_generics)] +#![allow(incomplete_features)] +#![warn(clippy::indexing_slicing, clippy::iter_cloned_collect)] + +pub struct KotomineArray { + arr: [T; N], +} + +impl KotomineArray { + pub fn ice(self) { + let _ = self.arr[..]; + let _ = self.arr.iter().cloned().collect::>(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5238.rs b/src/tools/clippy/tests/ui/crashes/ice-5238.rs new file mode 100644 index 0000000000000..989eb6d448557 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5238.rs @@ -0,0 +1,9 @@ +// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562 + +#![feature(generators, generator_trait)] + +fn main() { + let _ = || { + yield; + }; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5497.rs b/src/tools/clippy/tests/ui/crashes/ice-5497.rs new file mode 100644 index 0000000000000..0769bce5fc809 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5497.rs @@ -0,0 +1,11 @@ +// reduced from rustc issue-69020-assoc-const-arith-overflow.rs +pub fn main() {} + +pub trait Foo { + const OOB: i32; +} + +impl Foo for Vec { + const OOB: i32 = [1][1] + T::OOB; + //~^ ERROR operation will panic +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5579.rs b/src/tools/clippy/tests/ui/crashes/ice-5579.rs new file mode 100644 index 0000000000000..e1842c73f0e31 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5579.rs @@ -0,0 +1,17 @@ +trait IsErr { + fn is_err(&self, err: &str) -> bool; +} + +impl IsErr for Option { + fn is_err(&self, _err: &str) -> bool { + true + } +} + +fn main() { + let t = Some(1); + + if t.is_err("") { + t.unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-700.rs b/src/tools/clippy/tests/ui/crashes/ice-700.rs new file mode 100644 index 0000000000000..b06df83d51a5b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-700.rs @@ -0,0 +1,11 @@ +// run-pass + +#![deny(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/700 + +fn core() {} + +fn main() { + core(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs b/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs new file mode 100644 index 0000000000000..e02eb28ab8650 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs @@ -0,0 +1,21 @@ +// run-pass + +#![deny(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1336 + +#[allow(dead_code)] +struct Foo; + +impl Iterator for Foo { + type Item = (); + + fn next(&mut self) -> Option<()> { + let _ = self.len() == 0; + unimplemented!() + } +} + +impl ExactSizeIterator for Foo {} + +fn main() {} 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 new file mode 100644 index 0000000000000..4ef992b05e761 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs @@ -0,0 +1,18 @@ +// run-pass + +#![allow(clippy::comparison_chain)] +#![deny(clippy::if_same_then_else)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2426 + +fn main() {} + +pub fn foo(a: i32, b: i32) -> Option<&'static str> { + if a == b { + None + } else if a > b { + Some("a pfeil b") + } else { + None + } +} diff --git a/src/tools/clippy/tests/ui/crashes/inherent_impl.rs b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs new file mode 100644 index 0000000000000..aeb27b5ba8c2f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs @@ -0,0 +1,26 @@ +#![deny(clippy::multiple_inherent_impl)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/4578 + +macro_rules! impl_foo { + ($struct:ident) => { + impl $struct { + fn foo() {} + } + }; +} + +macro_rules! impl_bar { + ($struct:ident) => { + impl $struct { + fn bar() {} + } + }; +} + +struct MyStruct; + +impl_foo!(MyStruct); +impl_bar!(MyStruct); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/issue-825.rs b/src/tools/clippy/tests/ui/crashes/issue-825.rs new file mode 100644 index 0000000000000..3d4a88ab3c4e5 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/issue-825.rs @@ -0,0 +1,27 @@ +// run-pass + +#![allow(warnings)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/825 + +// this should compile in a reasonable amount of time +fn rust_type_id(name: &str) { + if "bool" == &name[..] + || "uint" == &name[..] + || "u8" == &name[..] + || "u16" == &name[..] + || "u32" == &name[..] + || "f32" == &name[..] + || "f64" == &name[..] + || "i8" == &name[..] + || "i16" == &name[..] + || "i32" == &name[..] + || "i64" == &name[..] + || "Self" == &name[..] + || "str" == &name[..] + { + unreachable!(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs new file mode 100644 index 0000000000000..c4acd5cda1b0a --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs @@ -0,0 +1,30 @@ +// run-pass + +#![allow(dead_code)] + +/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 +pub fn loop_on_block_condition(u: &mut isize) { + while { *u < 0 } { + *u += 1; + } +} + +/// https://github.com/rust-lang/rust-clippy/issues/2584 +fn loop_with_unsafe_condition(ptr: *const u8) { + let mut len = 0; + while unsafe { *ptr.offset(len) } != 0 { + len += 1; + } +} + +/// https://github.com/rust-lang/rust-clippy/issues/2710 +static mut RUNNING: bool = true; +fn loop_on_static_condition() { + unsafe { + while RUNNING { + RUNNING = false; + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs new file mode 100644 index 0000000000000..848f0ea52ca5e --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs @@ -0,0 +1,20 @@ +// run-pass + +#![deny(clippy::match_same_arms)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2427 + +const PRICE_OF_SWEETS: u32 = 5; +const PRICE_OF_KINDNESS: u32 = 0; +const PRICE_OF_DRINKS: u32 = 5; + +pub fn price(thing: &str) -> u32 { + match thing { + "rolo" => PRICE_OF_SWEETS, + "advice" => PRICE_OF_KINDNESS, + "juice" => PRICE_OF_DRINKS, + _ => panic!(), + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs new file mode 100644 index 0000000000000..d8fbaa5414664 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs @@ -0,0 +1,36 @@ +// run-pass + +#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] +#![allow(dead_code)] + +// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need +// the following three lines and the lazy_static crate. +// +// #[macro_use] +// extern crate lazy_static; +// use std::collections::HashMap; + +/// ensure that we don't suggest `is_nan` and `is_null` inside constants +/// FIXME: once const fn is stable, suggest these functions again in constants + +const BAA: *const i32 = 0 as *const i32; +static mut BAR: *const i32 = BAA; +static mut FOO: *const i32 = 0 as *const i32; +static mut BUH: bool = 42.0 < f32::NAN; + +#[allow(unused_variables, unused_mut)] +fn main() { + /* + lazy_static! { + static ref MUT_MAP : HashMap = { + let mut m = HashMap::new(); + m.insert(0, "zero"); + m + }; + static ref MUT_COUNT : usize = MUT_MAP.len(); + } + assert_eq!(*MUT_COUNT, 1); + */ + // FIXME: don't lint in array length, requires `check_body` + //let _ = [""; (42.0 < f32::NAN) as usize]; +} diff --git a/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs new file mode 100644 index 0000000000000..48507efe1e98b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs @@ -0,0 +1,9 @@ +// run-pass + +#[deny(clippy::all)] +#[derive(Debug)] +pub enum Error { + Type(&'static str), +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs new file mode 100644 index 0000000000000..bd1fa4a0b1ef2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -0,0 +1,22 @@ +// run-pass + +#![deny(clippy::needless_lifetimes)] +#![allow(dead_code)] + +trait Foo {} + +struct Bar {} + +struct Baz<'a> { + bar: &'a Bar, +} + +impl<'a> Foo for Baz<'a> {} + +impl Bar { + fn baz<'a>(&'a self) -> impl Foo + 'a { + Baz { bar: self } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/procedural_macro.rs b/src/tools/clippy/tests/ui/crashes/procedural_macro.rs new file mode 100644 index 0000000000000..f79d9ab6460b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/procedural_macro.rs @@ -0,0 +1,13 @@ +// run-pass + +#[macro_use] +extern crate clippy_mini_macro_test; + +#[deny(warnings)] +fn main() { + let x = Foo; + println!("{:?}", x); +} + +#[derive(ClippyMiniMacroTest, Debug)] +struct Foo; diff --git a/src/tools/clippy/tests/ui/crashes/regressions.rs b/src/tools/clippy/tests/ui/crashes/regressions.rs new file mode 100644 index 0000000000000..623ae51f9f08c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/regressions.rs @@ -0,0 +1,9 @@ +// run-pass + +#![allow(clippy::blacklisted_name)] + +pub fn foo(bar: *const u8) { + println!("{:#p}", bar); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/returns.rs b/src/tools/clippy/tests/ui/crashes/returns.rs new file mode 100644 index 0000000000000..f2153efc38800 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/returns.rs @@ -0,0 +1,25 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1346 + +#[deny(warnings)] +fn cfg_return() -> i32 { + #[cfg(unix)] + return 1; + #[cfg(not(unix))] + return 2; +} + +#[deny(warnings)] +fn cfg_let_and_return() -> i32 { + #[cfg(unix)] + let x = 1; + #[cfg(not(unix))] + let x = 2; + x +} + +fn main() { + cfg_return(); + cfg_let_and_return(); +} diff --git a/src/tools/clippy/tests/ui/crashes/shadow.rs b/src/tools/clippy/tests/ui/crashes/shadow.rs new file mode 100644 index 0000000000000..843e8ef64dcd9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/shadow.rs @@ -0,0 +1,6 @@ +fn main() { + let x: [i32; { + let u = 2; + 4 + }] = [2; { 4 }]; +} diff --git a/src/tools/clippy/tests/ui/crashes/single-match-else.rs b/src/tools/clippy/tests/ui/crashes/single-match-else.rs new file mode 100644 index 0000000000000..3a4bbe310ccaa --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/single-match-else.rs @@ -0,0 +1,13 @@ +// run-pass + +#![warn(clippy::single_match_else)] + +//! Test for https://github.com/rust-lang/rust-clippy/issues/1588 + +fn main() { + let n = match (42, 43) { + (42, n) => n, + _ => panic!("typeck error"), + }; + assert_eq!(n, 43); +} diff --git a/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs b/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs new file mode 100644 index 0000000000000..2bb95c18a3912 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(trivial_bounds)] +#![allow(unused, trivial_bounds)] + +fn test_trivial_bounds() +where + i32: Iterator, +{ + for _ in 2i32 {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs new file mode 100644 index 0000000000000..265017c51d924 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs @@ -0,0 +1,21 @@ +// run-pass + +#![allow(clippy::useless_attribute)] //issue #2910 + +#[macro_use] +extern crate serde_derive; + +/// Tests that we do not lint for unused underscores in a `MacroAttribute` +/// expansion +#[deny(clippy::used_underscore_binding)] +#[derive(Deserialize)] +struct MacroAttributesTest { + _foo: u32, +} + +#[test] +fn macro_attributes_test() { + let _ = MacroAttributesTest { _foo: 0 }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/whitelist/clippy.toml b/src/tools/clippy/tests/ui/crashes/whitelist/clippy.toml new file mode 100644 index 0000000000000..9f87de20baff3 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/whitelist/clippy.toml @@ -0,0 +1,3 @@ +# this is ignored by Clippy, but allowed for other tools like clippy-service +[third-party] +clippy-feature = "nightly" diff --git a/src/tools/clippy/tests/ui/crashes/whitelist/conf_whitelisted.rs b/src/tools/clippy/tests/ui/crashes/whitelist/conf_whitelisted.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/whitelist/conf_whitelisted.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs new file mode 100644 index 0000000000000..995787c533668 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -0,0 +1,12 @@ +// ignore-macos +// ignore-windows + +#![feature(main)] + +#[warn(clippy::main_recursion)] +#[allow(unconditional_recursion)] +#[main] +fn a() { + println!("Hello, World!"); + a(); +} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr new file mode 100644 index 0000000000000..f52fc949f6c3e --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr @@ -0,0 +1,11 @@ +error: recursing into entrypoint `a` + --> $DIR/entrypoint_recursion.rs:11:5 + | +LL | a(); + | ^ + | + = note: `-D clippy::main-recursion` implied by `-D warnings` + = help: consider using another function for this recursion + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs new file mode 100644 index 0000000000000..25b1417be9766 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -0,0 +1,33 @@ +// ignore-macos +// ignore-windows + +#![feature(lang_items, link_args, start, libc)] +#![link_args = "-nostartfiles"] +#![no_std] + +use core::panic::PanicInfo; +use core::sync::atomic::{AtomicUsize, Ordering}; + +static N: AtomicUsize = AtomicUsize::new(0); + +#[warn(clippy::main_recursion)] +#[start] +fn main(argc: isize, argv: *const *const u8) -> isize { + let x = N.load(Ordering::Relaxed); + N.store(x + 1, Ordering::Relaxed); + + if x < 3 { + main(argc, argv); + } + + 0 +} + +#[allow(clippy::empty_loop)] +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs new file mode 100644 index 0000000000000..89ff6609934d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs @@ -0,0 +1,6 @@ +#[warn(clippy::main_recursion)] +#[allow(unconditional_recursion)] +fn main() { + println!("Hello, World!"); + main(); +} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr new file mode 100644 index 0000000000000..0a260f9d2309e --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr @@ -0,0 +1,11 @@ +error: recursing into entrypoint `main` + --> $DIR/std_main_recursion.rs:5:5 + | +LL | main(); + | ^^^^ + | + = note: `-D clippy::main-recursion` implied by `-D warnings` + = help: consider using another function for this recursion + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/cstring.rs b/src/tools/clippy/tests/ui/cstring.rs new file mode 100644 index 0000000000000..6cdd6b4ff6e77 --- /dev/null +++ b/src/tools/clippy/tests/ui/cstring.rs @@ -0,0 +1,24 @@ +#![deny(clippy::temporary_cstring_as_ptr)] + +fn main() {} + +fn temporary_cstring() { + use std::ffi::CString; + + CString::new("foo").unwrap().as_ptr(); + CString::new("foo").expect("dummy").as_ptr(); +} + +mod issue4375 { + use std::ffi::CString; + use std::os::raw::c_char; + + extern "C" { + fn foo(data: *const c_char); + } + + pub fn bar(v: &[u8]) { + let cstr = CString::new(v); + unsafe { foo(cstr.unwrap().as_ptr()) } + } +} diff --git a/src/tools/clippy/tests/ui/cstring.stderr b/src/tools/clippy/tests/ui/cstring.stderr new file mode 100644 index 0000000000000..87cb29be57758 --- /dev/null +++ b/src/tools/clippy/tests/ui/cstring.stderr @@ -0,0 +1,46 @@ +error: you are getting the inner pointer of a temporary `CString` + --> $DIR/cstring.rs:8:5 + | +LL | CString::new("foo").unwrap().as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/cstring.rs:1:9 + | +LL | #![deny(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: that pointer will be invalid outside this expression +help: assign the `CString` to a variable to extend its lifetime + --> $DIR/cstring.rs:8:5 + | +LL | CString::new("foo").unwrap().as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you are getting the inner pointer of a temporary `CString` + --> $DIR/cstring.rs:9:5 + | +LL | CString::new("foo").expect("dummy").as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: that pointer will be invalid outside this expression +help: assign the `CString` to a variable to extend its lifetime + --> $DIR/cstring.rs:9:5 + | +LL | CString::new("foo").expect("dummy").as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you are getting the inner pointer of a temporary `CString` + --> $DIR/cstring.rs:22:22 + | +LL | unsafe { foo(cstr.unwrap().as_ptr()) } + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: that pointer will be invalid outside this expression +help: assign the `CString` to a variable to extend its lifetime + --> $DIR/cstring.rs:22:22 + | +LL | unsafe { foo(cstr.unwrap().as_ptr()) } + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/custom_ice_message.rs b/src/tools/clippy/tests/ui/custom_ice_message.rs new file mode 100644 index 0000000000000..5b30c9d5721ca --- /dev/null +++ b/src/tools/clippy/tests/ui/custom_ice_message.rs @@ -0,0 +1,10 @@ +// rustc-env:RUST_BACKTRACE=0 +// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" +// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" +// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" + +#![deny(clippy::internal)] + +fn it_looks_like_you_are_trying_to_kill_clippy() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/custom_ice_message.stderr b/src/tools/clippy/tests/ui/custom_ice_message.stderr new file mode 100644 index 0000000000000..a9a65a38c109d --- /dev/null +++ b/src/tools/clippy/tests/ui/custom_ice_message.stderr @@ -0,0 +1,11 @@ +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +error: internal compiler error: unexpected panic + +note: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new + +note: Clippy version: foo + diff --git a/src/tools/clippy/tests/ui/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro.rs new file mode 100644 index 0000000000000..d2df7fbd3e84c --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro.rs @@ -0,0 +1,23 @@ +#![warn(clippy::dbg_macro)] + +fn foo(n: u32) -> u32 { + if let Some(n) = dbg!(n.checked_sub(4)) { + n + } else { + n + } +} + +fn factorial(n: u32) -> u32 { + if dbg!(n <= 1) { + dbg!(1) + } else { + dbg!(n * factorial(n - 1)) + } +} + +fn main() { + dbg!(42); + dbg!(dbg!(dbg!(42))); + foo(3) + dbg!(factorial(4)); +} diff --git a/src/tools/clippy/tests/ui/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro.stderr new file mode 100644 index 0000000000000..b8aafe9667846 --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro.stderr @@ -0,0 +1,80 @@ +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:4:22 + | +LL | if let Some(n) = dbg!(n.checked_sub(4)) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::dbg-macro` implied by `-D warnings` +help: ensure to avoid having uses of it in version control + | +LL | if let Some(n) = n.checked_sub(4) { + | ^^^^^^^^^^^^^^^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:12:8 + | +LL | if dbg!(n <= 1) { + | ^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | if n <= 1 { + | ^^^^^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:13:9 + | +LL | dbg!(1) + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1 + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:15:9 + | +LL | dbg!(n * factorial(n - 1)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | n * factorial(n - 1) + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:20:5 + | +LL | dbg!(42); + | ^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 42; + | ^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:21:5 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | dbg!(dbg!(42)); + | ^^^^^^^^^^^^^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:22:14 + | +LL | foo(3) + dbg!(factorial(4)); + | ^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | foo(3) + factorial(4); + | ^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs new file mode 100644 index 0000000000000..477a47118d411 --- /dev/null +++ b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs @@ -0,0 +1,133 @@ +// compile-flags: --edition=2018 +#![feature(custom_inner_attributes)] +#![rustfmt::skip] +#![warn(clippy::debug_assert_with_mut_call)] +#![allow(clippy::redundant_closure_call)] + +struct S; + +impl S { + fn bool_self_ref(&self) -> bool { false } + fn bool_self_mut(&mut self) -> bool { false } + fn bool_self_ref_arg_ref(&self, _: &u32) -> bool { false } + fn bool_self_ref_arg_mut(&self, _: &mut u32) -> bool { false } + fn bool_self_mut_arg_ref(&mut self, _: &u32) -> bool { false } + fn bool_self_mut_arg_mut(&mut self, _: &mut u32) -> bool { false } + + fn u32_self_ref(&self) -> u32 { 0 } + fn u32_self_mut(&mut self) -> u32 { 0 } + fn u32_self_ref_arg_ref(&self, _: &u32) -> u32 { 0 } + fn u32_self_ref_arg_mut(&self, _: &mut u32) -> u32 { 0 } + fn u32_self_mut_arg_ref(&mut self, _: &u32) -> u32 { 0 } + fn u32_self_mut_arg_mut(&mut self, _: &mut u32) -> u32 { 0 } +} + +fn bool_ref(_: &u32) -> bool { false } +fn bool_mut(_: &mut u32) -> bool { false } +fn u32_ref(_: &u32) -> u32 { 0 } +fn u32_mut(_: &mut u32) -> u32 { 0 } + +fn func_non_mutable() { + debug_assert!(bool_ref(&3)); + debug_assert!(!bool_ref(&3)); + + debug_assert_eq!(0, u32_ref(&3)); + debug_assert_eq!(u32_ref(&3), 0); + + debug_assert_ne!(1, u32_ref(&3)); + debug_assert_ne!(u32_ref(&3), 1); +} + +fn func_mutable() { + debug_assert!(bool_mut(&mut 3)); + debug_assert!(!bool_mut(&mut 3)); + + debug_assert_eq!(0, u32_mut(&mut 3)); + debug_assert_eq!(u32_mut(&mut 3), 0); + + debug_assert_ne!(1, u32_mut(&mut 3)); + debug_assert_ne!(u32_mut(&mut 3), 1); +} + +fn method_non_mutable() { + debug_assert!(S.bool_self_ref()); + debug_assert!(S.bool_self_ref_arg_ref(&3)); + + debug_assert_eq!(S.u32_self_ref(), 0); + debug_assert_eq!(S.u32_self_ref_arg_ref(&3), 0); + + debug_assert_ne!(S.u32_self_ref(), 1); + debug_assert_ne!(S.u32_self_ref_arg_ref(&3), 1); +} + +fn method_mutable() { + debug_assert!(S.bool_self_mut()); + debug_assert!(!S.bool_self_mut()); + debug_assert!(S.bool_self_ref_arg_mut(&mut 3)); + debug_assert!(S.bool_self_mut_arg_ref(&3)); + debug_assert!(S.bool_self_mut_arg_mut(&mut 3)); + + debug_assert_eq!(S.u32_self_mut(), 0); + debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0); + debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0); + debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0); + + debug_assert_ne!(S.u32_self_mut(), 1); + debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1); + debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1); + debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1); +} + +fn misc() { + // with variable + let mut v: Vec = vec![1, 2, 3, 4]; + debug_assert_eq!(v.get(0), Some(&1)); + debug_assert_ne!(v[0], 2); + debug_assert_eq!(v.pop(), Some(1)); + debug_assert_ne!(Some(3), v.pop()); + + let a = &mut 3; + debug_assert!(bool_mut(a)); + + // nested + debug_assert!(!(bool_ref(&u32_mut(&mut 3)))); + + // chained + debug_assert_eq!(v.pop().unwrap(), 3); + + // format args + debug_assert!(bool_ref(&3), "w/o format"); + debug_assert!(bool_mut(&mut 3), "w/o format"); + debug_assert!(bool_ref(&3), "{} format", "w/"); + debug_assert!(bool_mut(&mut 3), "{} format", "w/"); + + // sub block + let mut x = 42_u32; + debug_assert!({ + bool_mut(&mut x); + x > 10 + }); + + // closures + debug_assert!((|| { + let mut x = 42; + bool_mut(&mut x); + x > 10 + })()); +} + +async fn debug_await() { + debug_assert!(async { + true + }.await); +} + +fn main() { + func_non_mutable(); + func_mutable(); + method_non_mutable(); + method_mutable(); + + misc(); + debug_await(); +} diff --git a/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr new file mode 100644 index 0000000000000..a2ca71b57a6fd --- /dev/null +++ b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr @@ -0,0 +1,172 @@ +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:42:19 + | +LL | debug_assert!(bool_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::debug-assert-with-mut-call` implied by `-D warnings` + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:43:20 + | +LL | debug_assert!(!bool_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:45:25 + | +LL | debug_assert_eq!(0, u32_mut(&mut 3)); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:46:22 + | +LL | debug_assert_eq!(u32_mut(&mut 3), 0); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:48:25 + | +LL | debug_assert_ne!(1, u32_mut(&mut 3)); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:49:22 + | +LL | debug_assert_ne!(u32_mut(&mut 3), 1); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:64:19 + | +LL | debug_assert!(S.bool_self_mut()); + | ^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:65:20 + | +LL | debug_assert!(!S.bool_self_mut()); + | ^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:66:19 + | +LL | debug_assert!(S.bool_self_ref_arg_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:67:19 + | +LL | debug_assert!(S.bool_self_mut_arg_ref(&3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:68:19 + | +LL | debug_assert!(S.bool_self_mut_arg_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:70:22 + | +LL | debug_assert_eq!(S.u32_self_mut(), 0); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:71:22 + | +LL | debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:72:22 + | +LL | debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:73:22 + | +LL | debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:75:22 + | +LL | debug_assert_ne!(S.u32_self_mut(), 1); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:76:22 + | +LL | debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:77:22 + | +LL | debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:78:22 + | +LL | debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:86:22 + | +LL | debug_assert_eq!(v.pop(), Some(1)); + | ^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:87:31 + | +LL | debug_assert_ne!(Some(3), v.pop()); + | ^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:90:19 + | +LL | debug_assert!(bool_mut(a)); + | ^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:93:31 + | +LL | debug_assert!(!(bool_ref(&u32_mut(&mut 3)))); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:96:22 + | +LL | debug_assert_eq!(v.pop().unwrap(), 3); + | ^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:100:19 + | +LL | debug_assert!(bool_mut(&mut 3), "w/o format"); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:102:19 + | +LL | debug_assert!(bool_mut(&mut 3), "{} format", "w/"); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:107:9 + | +LL | bool_mut(&mut x); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:114:9 + | +LL | bool_mut(&mut x); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.fixed b/src/tools/clippy/tests/ui/decimal_literal_representation.fixed new file mode 100644 index 0000000000000..de391465125ce --- /dev/null +++ b/src/tools/clippy/tests/ui/decimal_literal_representation.fixed @@ -0,0 +1,27 @@ +// run-rustfix + +#[warn(clippy::decimal_literal_representation)] +#[allow(unused_variables)] +#[rustfmt::skip] +fn main() { + let good = ( // Hex: + 127, // 0x7F + 256, // 0x100 + 511, // 0x1FF + 2048, // 0x800 + 4090, // 0xFFA + 16_371, // 0x3FF3 + 61_683, // 0xF0F3 + 2_131_750_925, // 0x7F0F_F00D + ); + let bad = ( // Hex: + 0x8005, // 0x8005 + 0xFF00, // 0xFF00 + 0x7F0F_F00F, // 0x7F0F_F00F + 0x7FFF_FFFF, // 0x7FFF_FFFF + #[allow(overflowing_literals)] + 0xF0F0_F0F0, // 0xF0F0_F0F0 + 0x8005_usize, // 0x8005_usize + 0x7F0F_F00F_isize, // 0x7F0F_F00F_isize + ); +} diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.rs b/src/tools/clippy/tests/ui/decimal_literal_representation.rs new file mode 100644 index 0000000000000..55d07698e7e5b --- /dev/null +++ b/src/tools/clippy/tests/ui/decimal_literal_representation.rs @@ -0,0 +1,27 @@ +// run-rustfix + +#[warn(clippy::decimal_literal_representation)] +#[allow(unused_variables)] +#[rustfmt::skip] +fn main() { + let good = ( // Hex: + 127, // 0x7F + 256, // 0x100 + 511, // 0x1FF + 2048, // 0x800 + 4090, // 0xFFA + 16_371, // 0x3FF3 + 61_683, // 0xF0F3 + 2_131_750_925, // 0x7F0F_F00D + ); + let bad = ( // Hex: + 32_773, // 0x8005 + 65_280, // 0xFF00 + 2_131_750_927, // 0x7F0F_F00F + 2_147_483_647, // 0x7FFF_FFFF + #[allow(overflowing_literals)] + 4_042_322_160, // 0xF0F0_F0F0 + 32_773usize, // 0x8005_usize + 2_131_750_927isize, // 0x7F0F_F00F_isize + ); +} diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.stderr b/src/tools/clippy/tests/ui/decimal_literal_representation.stderr new file mode 100644 index 0000000000000..8d50c8f83db44 --- /dev/null +++ b/src/tools/clippy/tests/ui/decimal_literal_representation.stderr @@ -0,0 +1,46 @@ +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:18:9 + | +LL | 32_773, // 0x8005 + | ^^^^^^ help: consider: `0x8005` + | + = note: `-D clippy::decimal-literal-representation` implied by `-D warnings` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:19:9 + | +LL | 65_280, // 0xFF00 + | ^^^^^^ help: consider: `0xFF00` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:20:9 + | +LL | 2_131_750_927, // 0x7F0F_F00F + | ^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:21:9 + | +LL | 2_147_483_647, // 0x7FFF_FFFF + | ^^^^^^^^^^^^^ help: consider: `0x7FFF_FFFF` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:23:9 + | +LL | 4_042_322_160, // 0xF0F0_F0F0 + | ^^^^^^^^^^^^^ help: consider: `0xF0F0_F0F0` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:24:9 + | +LL | 32_773usize, // 0x8005_usize + | ^^^^^^^^^^^ help: consider: `0x8005_usize` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:25:9 + | +LL | 2_131_750_927isize, // 0x7F0F_F00F_isize + | ^^^^^^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F_isize` + +error: aborting due to 7 previous errors + 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..b4003ed8932d3 --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs @@ -0,0 +1,93 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::borrow::Cow; +use std::cell::Cell; +use std::fmt::Display; +use std::sync::atomic::AtomicUsize; +use std::sync::Once; + +const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable +const CELL: Cell = Cell::new(6); //~ ERROR interior mutable +const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); +//~^ ERROR interior mutable + +macro_rules! declare_const { + ($name:ident: $ty:ty = $e:expr) => { + const $name: $ty = $e; + }; +} +declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + +// 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 this line + +#[allow(clippy::declare_interior_mutable_const)] +const ONCE_INIT: Once = Once::new(); + +trait Trait: Copy { + type NonCopyType; + + const ATOMIC: AtomicUsize; //~ ERROR interior mutable + const INTEGER: u64; + const STRING: String; + const SELF: Self; // (no error) + const INPUT: T; + //~^ ERROR interior mutable + //~| HELP consider requiring `T` to be `Copy` + const ASSOC: Self::NonCopyType; + //~^ ERROR interior mutable + //~| HELP consider requiring `>::NonCopyType` to be `Copy` + + const AN_INPUT: T = Self::INPUT; + //~^ ERROR interior mutable + //~| ERROR consider requiring `T` to be `Copy` + declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable +} + +trait Trait2 { + type CopyType: Copy; + + const SELF_2: Self; + //~^ ERROR interior mutable + //~| HELP consider requiring `Self` to be `Copy` + const ASSOC_2: Self::CopyType; // (no error) +} + +// we don't lint impl of traits, because an impl has no power to change the interface. +impl Trait for u64 { + type NonCopyType = u16; + + const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INTEGER: u64 = 10; + const STRING: String = String::new(); + const SELF: Self = 11; + const INPUT: u32 = 12; + const ASSOC: Self::NonCopyType = 13; +} + +struct Local(T, U); + +impl, U: Trait2> Local { + const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable + const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); + const T_SELF: T = T::SELF_2; + const U_SELF: U = U::SELF_2; + //~^ ERROR interior mutable + //~| HELP consider requiring `U` to be `Copy` + const T_ASSOC: T::NonCopyType = T::ASSOC; + //~^ ERROR interior mutable + //~| HELP consider requiring `>::NonCopyType` to be `Copy` + const U_ASSOC: U::CopyType = U::ASSOC_2; +} + +fn main() {} 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..6a9a57361f9f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr @@ -0,0 +1,110 @@ +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:9:1 + | +LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:10:1 + | +LL | const CELL: Cell = Cell::new(6); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:11:1 + | +LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:16:9 + | +LL | const $name: $ty = $e; + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + | ------------------------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:40:5 + | +LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:44:5 + | +LL | const INPUT: T; + | ^^^^^^^^^^^^^-^ + | | + | consider requiring `T` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:47:5 + | +LL | const ASSOC: Self::NonCopyType; + | ^^^^^^^^^^^^^-----------------^ + | | + | consider requiring `>::NonCopyType` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:51:5 + | +LL | const AN_INPUT: T = Self::INPUT; + | ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^ + | | + | consider requiring `T` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:16:9 + | +LL | const $name: $ty = $e; + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable + | ----------------------------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:60:5 + | +LL | const SELF_2: Self; + | ^^^^^^^^^^^^^^----^ + | | + | consider requiring `Self` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:81:5 + | +LL | const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:84:5 + | +LL | const U_SELF: U = U::SELF_2; + | ^^^^^^^^^^^^^^-^^^^^^^^^^^^^ + | | + | consider requiring `U` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:87:5 + | +LL | const T_ASSOC: T::NonCopyType = T::ASSOC; + | ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^ + | | + | consider requiring `>::NonCopyType` to be `Copy` + +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 new file mode 100644 index 0000000000000..2a948d60b1089 --- /dev/null +++ b/src/tools/clippy/tests/ui/def_id_nocore.rs @@ -0,0 +1,29 @@ +// ignore-windows +// ignore-macos + +#![feature(no_core, lang_items, start)] +#![no_core] + +#[link(name = "c")] +extern "C" {} + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +#[lang = "freeze"] +pub unsafe trait Freeze {} + +#[lang = "start"] +#[start] +fn start(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} + +pub struct A; + +impl A { + pub fn as_ref(self) -> &'static str { + "A" + } +} diff --git a/src/tools/clippy/tests/ui/def_id_nocore.stderr b/src/tools/clippy/tests/ui/def_id_nocore.stderr new file mode 100644 index 0000000000000..ed87a50547d17 --- /dev/null +++ b/src/tools/clippy/tests/ui/def_id_nocore.stderr @@ -0,0 +1,10 @@ +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/def_id_nocore.rs:26:19 + | +LL | pub fn as_ref(self) -> &'static str { + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/default_lint.rs b/src/tools/clippy/tests/ui/default_lint.rs new file mode 100644 index 0000000000000..053faae02ce3e --- /dev/null +++ b/src/tools/clippy/tests/ui/default_lint.rs @@ -0,0 +1,27 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_DEFAULT, + Warn, + "default lint description", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); +declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/default_lint.stderr b/src/tools/clippy/tests/ui/default_lint.stderr new file mode 100644 index 0000000000000..5c5836a7d297e --- /dev/null +++ b/src/tools/clippy/tests/ui/default_lint.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT_DEFAULT` has the default lint description + --> $DIR/default_lint.rs:17:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT_DEFAULT, +LL | | Warn, +LL | | "default lint description", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/default_lint.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/default_trait_access.rs b/src/tools/clippy/tests/ui/default_trait_access.rs new file mode 100644 index 0000000000000..2f1490a70369e --- /dev/null +++ b/src/tools/clippy/tests/ui/default_trait_access.rs @@ -0,0 +1,103 @@ +#![warn(clippy::default_trait_access)] + +use std::default; +use std::default::Default as D2; +use std::string; + +fn main() { + let s1: String = Default::default(); + + let s2 = String::default(); + + let s3: String = D2::default(); + + let s4: String = std::default::Default::default(); + + let s5 = string::String::default(); + + let s6: String = default::Default::default(); + + let s7 = std::string::String::default(); + + let s8: String = DefaultFactory::make_t_badly(); + + let s9: String = DefaultFactory::make_t_nicely(); + + let s10 = DerivedDefault::default(); + + let s11: GenericDerivedDefault = Default::default(); + + let s12 = GenericDerivedDefault::::default(); + + let s13 = TupleDerivedDefault::default(); + + let s14: TupleDerivedDefault = Default::default(); + + let s15: ArrayDerivedDefault = Default::default(); + + let s16 = ArrayDerivedDefault::default(); + + let s17: TupleStructDerivedDefault = Default::default(); + + let s18 = TupleStructDerivedDefault::default(); + + let s19 = ::default(); + + println!( + "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + ); +} + +struct DefaultFactory; + +impl DefaultFactory { + pub fn make_t_badly() -> T { + Default::default() + } + + pub fn make_t_nicely() -> T { + T::default() + } +} + +#[derive(Debug, Default)] +struct DerivedDefault { + pub s: String, +} + +#[derive(Debug, Default)] +struct GenericDerivedDefault { + pub s: T, +} + +#[derive(Debug, Default)] +struct TupleDerivedDefault { + pub s: (String, String), +} + +#[derive(Debug, Default)] +struct ArrayDerivedDefault { + pub s: [String; 10], +} + +#[derive(Debug, Default)] +struct TupleStructDerivedDefault(String); diff --git a/src/tools/clippy/tests/ui/default_trait_access.stderr b/src/tools/clippy/tests/ui/default_trait_access.stderr new file mode 100644 index 0000000000000..453760c6b9211 --- /dev/null +++ b/src/tools/clippy/tests/ui/default_trait_access.stderr @@ -0,0 +1,52 @@ +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:8:22 + | +LL | let s1: String = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | + = note: `-D clippy::default-trait-access` implied by `-D warnings` + +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:12:22 + | +LL | let s3: String = D2::default(); + | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` + +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:14:22 + | +LL | let s4: String = std::default::Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:18:22 + | +LL | let s6: String = default::Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + +error: Calling `GenericDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:28:46 + | +LL | let s11: GenericDerivedDefault = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` + +error: Calling `TupleDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:34:36 + | +LL | let s14: TupleDerivedDefault = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` + +error: Calling `ArrayDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:36:36 + | +LL | let s15: ArrayDerivedDefault = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` + +error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:40:42 + | +LL | let s17: TupleStructDerivedDefault = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs new file mode 100644 index 0000000000000..188a641aa1af2 --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -0,0 +1,11 @@ +#[warn(clippy::str_to_string)] +#[warn(clippy::string_to_string)] +#[warn(clippy::unstable_as_slice)] +#[warn(clippy::unstable_as_mut_slice)] +#[warn(clippy::misaligned_transmute)] +#[warn(clippy::unused_collect)] +#[warn(clippy::invalid_ref)] +#[warn(clippy::into_iter_on_array)] +#[warn(clippy::unused_label)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr new file mode 100644 index 0000000000000..a4efe3d15a952 --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -0,0 +1,64 @@ +error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated.rs:1:8 + | +LL | #[warn(clippy::str_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + +error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated.rs:2:8 + | +LL | #[warn(clippy::string_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` + --> $DIR/deprecated.rs:3:8 + | +LL | #[warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` + --> $DIR/deprecated.rs:4:8 + | +LL | #[warn(clippy::unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` + --> $DIR/deprecated.rs:5:8 + | +LL | #[warn(clippy::misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unused_collect` has been removed: ``collect` has been marked as #[must_use] in rustc and that covers all cases of this lint` + --> $DIR/deprecated.rs:6:8 + | +LL | #[warn(clippy::unused_collect)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value`` + --> $DIR/deprecated.rs:7:8 + | +LL | #[warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter`` + --> $DIR/deprecated.rs:8:8 + | +LL | #[warn(clippy::into_iter_on_array)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels`` + --> $DIR/deprecated.rs:9:8 + | +LL | #[warn(clippy::unused_label)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated.rs:1:8 + | +LL | #[warn(clippy::str_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/deprecated_old.rs b/src/tools/clippy/tests/ui/deprecated_old.rs new file mode 100644 index 0000000000000..2e5c5b7ead12c --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated_old.rs @@ -0,0 +1,7 @@ +#[warn(str_to_string)] +#[warn(string_to_string)] +#[warn(unstable_as_slice)] +#[warn(unstable_as_mut_slice)] +#[warn(misaligned_transmute)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated_old.stderr b/src/tools/clippy/tests/ui/deprecated_old.stderr new file mode 100644 index 0000000000000..ff3e9e8fcf367 --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated_old.stderr @@ -0,0 +1,40 @@ +error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated_old.rs:1:8 + | +LL | #[warn(str_to_string)] + | ^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + +error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated_old.rs:2:8 + | +LL | #[warn(string_to_string)] + | ^^^^^^^^^^^^^^^^ + +error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` + --> $DIR/deprecated_old.rs:3:8 + | +LL | #[warn(unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^ + +error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` + --> $DIR/deprecated_old.rs:4:8 + | +LL | #[warn(unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` + --> $DIR/deprecated_old.rs:5:8 + | +LL | #[warn(misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated_old.rs:1:8 + | +LL | #[warn(str_to_string)] + | ^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed new file mode 100644 index 0000000000000..9e5b51d6d5e6d --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof.fixed @@ -0,0 +1,39 @@ +// run-rustfix + +fn get_number() -> usize { + 10 +} + +fn get_reference(n: &usize) -> &usize { + n +} + +#[allow(clippy::many_single_char_names, clippy::double_parens)] +#[allow(unused_variables, unused_parens)] +#[warn(clippy::deref_addrof)] +fn main() { + let a = 10; + let aref = &a; + + let b = a; + + let b = get_number(); + + let b = *get_reference(&a); + + let bytes: Vec = vec![1, 2, 3, 4]; + let b = bytes[1..2][0]; + + //This produces a suggestion of 'let b = (a);' which + //will trigger the 'unused_parens' lint + let b = (a); + + let b = a; + + #[rustfmt::skip] + let b = a; + + let b = &a; + + let b = *aref; +} diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs new file mode 100644 index 0000000000000..5641a73cbc1cb --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof.rs @@ -0,0 +1,39 @@ +// run-rustfix + +fn get_number() -> usize { + 10 +} + +fn get_reference(n: &usize) -> &usize { + n +} + +#[allow(clippy::many_single_char_names, clippy::double_parens)] +#[allow(unused_variables, unused_parens)] +#[warn(clippy::deref_addrof)] +fn main() { + let a = 10; + let aref = &a; + + let b = *&a; + + let b = *&get_number(); + + let b = *get_reference(&a); + + let bytes: Vec = vec![1, 2, 3, 4]; + let b = *&bytes[1..2][0]; + + //This produces a suggestion of 'let b = (a);' which + //will trigger the 'unused_parens' lint + let b = *&(a); + + let b = *(&a); + + #[rustfmt::skip] + let b = *((&a)); + + let b = *&&a; + + let b = **&aref; +} diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr new file mode 100644 index 0000000000000..bc51719e8a7a0 --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof.stderr @@ -0,0 +1,52 @@ +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:18:13 + | +LL | let b = *&a; + | ^^^ help: try this: `a` + | + = note: `-D clippy::deref-addrof` implied by `-D warnings` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:20:13 + | +LL | let b = *&get_number(); + | ^^^^^^^^^^^^^^ help: try this: `get_number()` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:25:13 + | +LL | let b = *&bytes[1..2][0]; + | ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:29:13 + | +LL | let b = *&(a); + | ^^^^^ help: try this: `(a)` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:31:13 + | +LL | let b = *(&a); + | ^^^^^ help: try this: `a` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:34:13 + | +LL | let b = *((&a)); + | ^^^^^^^ help: try this: `a` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:36:13 + | +LL | let b = *&&a; + | ^^^^ help: try this: `&a` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:38:14 + | +LL | let b = **&aref; + | ^^^^^^ help: try this: `aref` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs new file mode 100644 index 0000000000000..4531943299cd1 --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs @@ -0,0 +1,23 @@ +// This test can't work with run-rustfix because it needs two passes of test+fix + +#[warn(clippy::deref_addrof)] +#[allow(unused_variables, unused_mut)] +fn main() { + let a = 10; + + //This produces a suggestion of 'let b = *&a;' which + //will trigger the 'clippy::deref_addrof' lint again + let b = **&&a; + + { + let mut x = 10; + let y = *&mut x; + } + + { + //This produces a suggestion of 'let y = *&mut x' which + //will trigger the 'clippy::deref_addrof' lint again + let mut x = 10; + let y = **&mut &mut x; + } +} diff --git a/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr new file mode 100644 index 0000000000000..2c55a4ed6acdb --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr @@ -0,0 +1,22 @@ +error: immediately dereferencing a reference + --> $DIR/deref_addrof_double_trigger.rs:10:14 + | +LL | let b = **&&a; + | ^^^^ help: try this: `&a` + | + = note: `-D clippy::deref-addrof` implied by `-D warnings` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof_double_trigger.rs:14:17 + | +LL | let y = *&mut x; + | ^^^^^^^ help: try this: `x` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof_double_trigger.rs:21:18 + | +LL | let y = **&mut &mut x; + | ^^^^^^^^^^^^ help: try this: `&mut x` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/deref_addrof_macro.rs b/src/tools/clippy/tests/ui/deref_addrof_macro.rs new file mode 100644 index 0000000000000..dcebd6c6e29c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof_macro.rs @@ -0,0 +1,10 @@ +macro_rules! m { + ($($x:tt),*) => { &[$(($x, stringify!(x)),)*] }; +} + +#[warn(clippy::deref_addrof)] +fn f() -> [(i32, &'static str); 3] { + *m![1, 2, 3] // should be fine +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/dereference.fixed b/src/tools/clippy/tests/ui/dereference.fixed new file mode 100644 index 0000000000000..459ca91b93b9e --- /dev/null +++ b/src/tools/clippy/tests/ui/dereference.fixed @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec); +impl Deref for CustomVec { + type Target = Vec; + + fn deref(&self) -> &Vec { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + // both derefs should get linted here + let b: String = format!("{}, {}", &*a, &*a); + + println!("{}", &*a); + + #[allow(clippy::match_single_binding)] + match &*a { + _ => (), + } + + let b: String = concat(&*a); + + let b = &*just_return(a); + + let b: String = concat(&*just_return(a)); + + let b: &str = &*a.deref(); + + let opt_a = Some(a.clone()); + let b = &*opt_a.unwrap(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/dereference.rs b/src/tools/clippy/tests/ui/dereference.rs new file mode 100644 index 0000000000000..8dc5272e67fa5 --- /dev/null +++ b/src/tools/clippy/tests/ui/dereference.rs @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec); +impl Deref for CustomVec { + type Target = Vec; + + fn deref(&self) -> &Vec { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = a.deref(); + + let b: &mut str = a.deref_mut(); + + // both derefs should get linted here + let b: String = format!("{}, {}", a.deref(), a.deref()); + + println!("{}", a.deref()); + + #[allow(clippy::match_single_binding)] + match a.deref() { + _ => (), + } + + let b: String = concat(a.deref()); + + let b = just_return(a).deref(); + + let b: String = concat(just_return(a).deref()); + + let b: &str = a.deref().deref(); + + let opt_a = Some(a.clone()); + let b = opt_a.unwrap().deref(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/dereference.stderr b/src/tools/clippy/tests/ui/dereference.stderr new file mode 100644 index 0000000000000..d26b462a43362 --- /dev/null +++ b/src/tools/clippy/tests/ui/dereference.stderr @@ -0,0 +1,70 @@ +error: explicit deref method call + --> $DIR/dereference.rs:30:19 + | +LL | let b: &str = a.deref(); + | ^^^^^^^^^ help: try this: `&*a` + | + = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` + +error: explicit deref_mut method call + --> $DIR/dereference.rs:32:23 + | +LL | let b: &mut str = a.deref_mut(); + | ^^^^^^^^^^^^^ help: try this: `&mut *a` + +error: explicit deref method call + --> $DIR/dereference.rs:35:39 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:35:50 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:37:20 + | +LL | println!("{}", a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:40:11 + | +LL | match a.deref() { + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:44:28 + | +LL | let b: String = concat(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:46:13 + | +LL | let b = just_return(a).deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` + +error: explicit deref method call + --> $DIR/dereference.rs:48:28 + | +LL | let b: String = concat(just_return(a).deref()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` + +error: explicit deref method call + --> $DIR/dereference.rs:50:19 + | +LL | let b: &str = a.deref().deref(); + | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()` + +error: explicit deref method call + --> $DIR/dereference.rs:53:13 + | +LL | let b = opt_a.unwrap().deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs new file mode 100644 index 0000000000000..8fcb0e8b28d09 --- /dev/null +++ b/src/tools/clippy/tests/ui/derive.rs @@ -0,0 +1,74 @@ +#![feature(untagged_unions)] +#![allow(dead_code)] +#![warn(clippy::expl_impl_clone_on_copy)] + +#[derive(Copy)] +struct Qux; + +impl Clone for Qux { + fn clone(&self) -> Self { + Qux + } +} + +// looks like unions don't support deriving Clone for now +#[derive(Copy)] +union Union { + a: u8, +} + +impl Clone for Union { + fn clone(&self) -> Self { + Union { a: 42 } + } +} + +// See #666 +#[derive(Copy)] +struct Lt<'a> { + a: &'a u8, +} + +impl<'a> Clone for Lt<'a> { + fn clone(&self) -> Self { + unimplemented!() + } +} + +// Ok, `Clone` cannot be derived because of the big array +#[derive(Copy)] +struct BigArray { + a: [u8; 65], +} + +impl Clone for BigArray { + fn clone(&self) -> Self { + unimplemented!() + } +} + +// Ok, function pointers are not always Clone +#[derive(Copy)] +struct FnPtr { + a: fn() -> !, +} + +impl Clone for FnPtr { + fn clone(&self) -> Self { + unimplemented!() + } +} + +// Ok, generics +#[derive(Copy)] +struct Generic { + a: T, +} + +impl Clone for Generic { + fn clone(&self) -> Self { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr new file mode 100644 index 0000000000000..1328a9b31077e --- /dev/null +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -0,0 +1,83 @@ +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:8:1 + | +LL | / impl Clone for Qux { +LL | | fn clone(&self) -> Self { +LL | | Qux +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:8:1 + | +LL | / impl Clone for Qux { +LL | | fn clone(&self) -> Self { +LL | | Qux +LL | | } +LL | | } + | |_^ + +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:32:1 + | +LL | / impl<'a> Clone for Lt<'a> { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:32:1 + | +LL | / impl<'a> Clone for Lt<'a> { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:44:1 + | +LL | / impl Clone for BigArray { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:44:1 + | +LL | / impl Clone for BigArray { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:56:1 + | +LL | / impl Clone for FnPtr { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:56:1 + | +LL | / impl Clone for FnPtr { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs b/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs new file mode 100644 index 0000000000000..10abe22d53e5c --- /dev/null +++ b/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs @@ -0,0 +1,54 @@ +#[derive(PartialEq, Hash)] +struct Foo; + +impl PartialEq for Foo { + fn eq(&self, _: &u64) -> bool { + true + } +} + +#[derive(Hash)] +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Bar) -> bool { + true + } +} + +#[derive(Hash)] +struct Baz; + +impl PartialEq for Baz { + fn eq(&self, _: &Baz) -> bool { + true + } +} + +#[derive(PartialEq)] +struct Bah; + +impl std::hash::Hash for Bah { + fn hash(&self, _: &mut H) {} +} + +#[derive(PartialEq)] +struct Foo2; + +trait Hash {} + +// We don't want to lint on user-defined traits called `Hash` +impl Hash for Foo2 {} + +mod use_hash { + use std::hash::{Hash, Hasher}; + + #[derive(PartialEq)] + struct Foo3; + + impl Hash for Foo3 { + fn hash(&self, _: &mut H) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr b/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr new file mode 100644 index 0000000000000..2287a548fe46c --- /dev/null +++ b/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr @@ -0,0 +1,67 @@ +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derive_hash_xor_eq.rs:10:10 + | +LL | #[derive(Hash)] + | ^^^^ + | + = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:13:1 + | +LL | / impl PartialEq for Bar { +LL | | fn eq(&self, _: &Bar) -> bool { +LL | | true +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derive_hash_xor_eq.rs:19:10 + | +LL | #[derive(Hash)] + | ^^^^ + | +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:22:1 + | +LL | / impl PartialEq for Baz { +LL | | fn eq(&self, _: &Baz) -> bool { +LL | | true +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Hash` explicitly but have derived `PartialEq` + --> $DIR/derive_hash_xor_eq.rs:31:1 + | +LL | / impl std::hash::Hash for Bah { +LL | | fn hash(&self, _: &mut H) {} +LL | | } + | |_^ + | +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:28:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Hash` explicitly but have derived `PartialEq` + --> $DIR/derive_hash_xor_eq.rs:49:5 + | +LL | / impl Hash for Foo3 { +LL | | fn hash(&self, _: &mut H) {} +LL | | } + | |_____^ + | +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:46:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.rs b/src/tools/clippy/tests/ui/diverging_sub_expression.rs new file mode 100644 index 0000000000000..4df241c9fc39b --- /dev/null +++ b/src/tools/clippy/tests/ui/diverging_sub_expression.rs @@ -0,0 +1,42 @@ +#![warn(clippy::diverging_sub_expression)] +#![allow(clippy::match_same_arms, clippy::logic_bug)] + +#[allow(clippy::empty_loop)] +fn diverge() -> ! { + loop {} +} + +struct A; + +impl A { + fn foo(&self) -> ! { + diverge() + } +} + +#[allow(unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] +fn main() { + let b = true; + b || diverge(); + b || A.foo(); +} + +#[allow(dead_code, unused_variables)] +fn foobar() { + loop { + let x = match 5 { + 4 => return, + 5 => continue, + 6 => true || return, + 7 => true || continue, + 8 => break, + 9 => diverge(), + 3 => true || diverge(), + 10 => match 42 { + 99 => return, + _ => true || panic!("boo"), + }, + _ => true || break, + }; + } +} diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr new file mode 100644 index 0000000000000..170e7d92de4ac --- /dev/null +++ b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr @@ -0,0 +1,40 @@ +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:20:10 + | +LL | b || diverge(); + | ^^^^^^^^^ + | + = note: `-D clippy::diverging-sub-expression` implied by `-D warnings` + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:21:10 + | +LL | b || A.foo(); + | ^^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:30:26 + | +LL | 6 => true || return, + | ^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:31:26 + | +LL | 7 => true || continue, + | ^^^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:34:26 + | +LL | 3 => true || diverge(), + | ^^^^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:39:26 + | +LL | _ => true || break, + | ^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/dlist.rs b/src/tools/clippy/tests/ui/dlist.rs new file mode 100644 index 0000000000000..2940d2d290110 --- /dev/null +++ b/src/tools/clippy/tests/ui/dlist.rs @@ -0,0 +1,40 @@ +#![feature(associated_type_defaults)] +#![warn(clippy::linkedlist)] +#![allow(dead_code, clippy::needless_pass_by_value)] + +extern crate alloc; +use alloc::collections::linked_list::LinkedList; + +trait Foo { + type Baz = LinkedList; + fn foo(_: LinkedList); + const BAR: Option>; +} + +// Ok, we don’t want to warn for implementations; see issue #605. +impl Foo for LinkedList { + fn foo(_: LinkedList) {} + const BAR: Option> = None; +} + +struct Bar; +impl Bar { + fn foo(_: LinkedList) {} +} + +pub fn test(my_favourite_linked_list: LinkedList) { + println!("{:?}", my_favourite_linked_list) +} + +pub fn test_ret() -> Option> { + unimplemented!(); +} + +pub fn test_local_not_linted() { + let _: LinkedList; +} + +fn main() { + test(LinkedList::new()); + test_local_not_linted(); +} diff --git a/src/tools/clippy/tests/ui/dlist.stderr b/src/tools/clippy/tests/ui/dlist.stderr new file mode 100644 index 0000000000000..64fde33c64f52 --- /dev/null +++ b/src/tools/clippy/tests/ui/dlist.stderr @@ -0,0 +1,51 @@ +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:9:16 + | +LL | type Baz = LinkedList; + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::linkedlist` implied by `-D warnings` + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:10:15 + | +LL | fn foo(_: LinkedList); + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:11:23 + | +LL | const BAR: Option>; + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:22:15 + | +LL | fn foo(_: LinkedList) {} + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:25:39 + | +LL | pub fn test(my_favourite_linked_list: LinkedList) { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:29:29 + | +LL | pub fn test_ret() -> Option> { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/doc.rs b/src/tools/clippy/tests/ui/doc.rs new file mode 100644 index 0000000000000..77620c857e66e --- /dev/null +++ b/src/tools/clippy/tests/ui/doc.rs @@ -0,0 +1,184 @@ +//! This file tests for the `DOC_MARKDOWN` lint. + +#![allow(dead_code)] +#![warn(clippy::doc_markdown)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) +/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun +/// which should be reported only once despite being __doubly bad__. +/// Here be ::a::global:path. +/// That's not code ~NotInCodeBlock~. +/// be_sure_we_got_to_the_end_of_it +fn foo_bar() { +} + +/// That one tests multiline ticks. +/// ```rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ``` +/// +/// ~~~rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ~~~ +/// be_sure_we_got_to_the_end_of_it +fn multiline_codeblock() { +} + +/// This _is a test for +/// multiline +/// emphasis_. +/// be_sure_we_got_to_the_end_of_it +fn test_emphasis() { +} + +/// This tests units. See also #835. +/// kiB MiB GiB TiB PiB EiB +/// kib Mib Gib Tib Pib Eib +/// kB MB GB TB PB EB +/// kb Mb Gb Tb Pb Eb +/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB +/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib +/// 32kB 32MB 32GB 32TB 32PB 32EB +/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb +/// NaN +/// be_sure_we_got_to_the_end_of_it +fn test_units() { +} + +/// This test has [a link_with_underscores][chunked-example] inside it. See #823. +/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) +/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. +/// It can also be [inline_link2]. +/// +/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example +/// [inline_link]: https://foobar +/// [inline_link2]: https://foobar +/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and +/// `multiline_ticks` functions. +/// +/// expression of the type `_ m c` (where `` +/// is one of {`&`, '|'} and `` is one of {`!=`, `>=`, `>` , +/// be_sure_we_got_to_the_end_of_it +fn main() { + foo_bar(); + multiline_codeblock(); + test_emphasis(); + test_units(); +} + +/// ## CamelCaseThing +/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897. +/// +/// # CamelCaseThing +/// +/// Not a title #897 CamelCaseThing +/// be_sure_we_got_to_the_end_of_it +fn issue897() { +} + +/// I am confused by brackets? (`x_y`) +/// I am confused by brackets? (foo `x_y`) +/// I am confused by brackets? (`x_y` foo) +/// be_sure_we_got_to_the_end_of_it +fn issue900() { +} + +/// Diesel queries also have a similar problem to [Iterator][iterator], where +/// /// More talking +/// returning them from a function requires exposing the implementation of that +/// function. The [`helper_types`][helper_types] module exists to help with this, +/// but you might want to hide the return type or have it conditionally change. +/// Boxing can achieve both. +/// +/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html +/// [helper_types]: ../helper_types/index.html +/// be_sure_we_got_to_the_end_of_it +fn issue883() { +} + +/// `foo_bar +/// baz_quz` +/// [foo +/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html) +fn multiline() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: `PackedNode` +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: PackedNode +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073_alt() { +} + +/// Tests more than three quotes: +/// ```` +/// DoNotWarn +/// ``` +/// StillDont +/// ```` +/// be_sure_we_got_to_the_end_of_it +fn four_quotes() { +} + +/// See [NIST SP 800-56A, revision 2]. +/// +/// [NIST SP 800-56A, revision 2]: +/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419 +fn issue_902_comment() {} + +#[cfg_attr(feature = "a", doc = " ```")] +#[cfg_attr(not(feature = "a"), doc = " ```ignore")] +/// fn main() { +/// let s = "localhost:10000".to_string(); +/// println!("{}", s); +/// } +/// ``` +fn issue_1469() {} + +/** + * This is a doc comment that should not be a list + *This would also be an error under a strict common mark interpretation + */ +fn issue_1920() {} + +/// Ok: +/// +/// Not ok: http://www.unicode.org +/// Not ok: https://www.unicode.org +/// Not ok: http://www.unicode.org/ +/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels +fn issue_1832() {} + +/// Ok: CamelCase (It should not be surrounded by backticks) +fn issue_2395() {} + +/// An iterator over mycrate::Collection's values. +/// It should not lint a `'static` lifetime in ticks. +fn issue_2210() {} + +/// This should not cause the lint to trigger: +/// #REQ-data-family.lint_partof_exists +fn issue_2343() {} + +/// This should not cause an ICE: +/// __|_ _|__||_| +fn pulldown_cmark_crash() {} diff --git a/src/tools/clippy/tests/ui/doc.stderr b/src/tools/clippy/tests/ui/doc.stderr new file mode 100644 index 0000000000000..ae9bb394cb9ac --- /dev/null +++ b/src/tools/clippy/tests/ui/doc.stderr @@ -0,0 +1,184 @@ +error: you should put `foo_bar` between ticks in the documentation + --> $DIR/doc.rs:8:9 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + +error: you should put `foo::bar` between ticks in the documentation + --> $DIR/doc.rs:8:51 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^^ + +error: you should put `Foo::some_fun` between ticks in the documentation + --> $DIR/doc.rs:9:83 + | +LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun + | ^^^^^^^^^^^^^ + +error: you should put `a::global:path` between ticks in the documentation + --> $DIR/doc.rs:11:15 + | +LL | /// Here be ::a::global:path. + | ^^^^^^^^^^^^^^ + +error: you should put `NotInCodeBlock` between ticks in the documentation + --> $DIR/doc.rs:12:22 + | +LL | /// That's not code ~NotInCodeBlock~. + | ^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:13:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:27:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:34:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:48:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `link_with_underscores` between ticks in the documentation + --> $DIR/doc.rs:52:22 + | +LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. + | ^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `inline_link2` between ticks in the documentation + --> $DIR/doc.rs:55:21 + | +LL | /// It can also be [inline_link2]. + | ^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:65:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:73:8 + | +LL | /// ## CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:76:7 + | +LL | /// # CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:78:22 + | +LL | /// Not a title #897 CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:79:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:86:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:99:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:110:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:115:5 + | +LL | And BarQuz too. + | ^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:116:1 + | +LL | be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:121:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:126:5 + | +LL | And BarQuz too. + | ^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:127:1 + | +LL | be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:138:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:165:13 + | +LL | /// Not ok: http://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:166:13 + | +LL | /// Not ok: https://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:167:13 + | +LL | /// Not ok: http://www.unicode.org/ + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:168:13 + | +LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `mycrate::Collection` between ticks in the documentation + --> $DIR/doc.rs:174:22 + | +LL | /// An iterator over mycrate::Collection's values. + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 30 previous errors + diff --git a/src/tools/clippy/tests/ui/doc_errors.rs b/src/tools/clippy/tests/ui/doc_errors.rs new file mode 100644 index 0000000000000..445fc8d31d77a --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_errors.rs @@ -0,0 +1,103 @@ +// edition:2018 +#![warn(clippy::missing_errors_doc)] + +use std::io; + +pub fn pub_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// This is not sufficiently documented. +pub fn pub_fn_returning_io_result() -> io::Result<()> { + unimplemented!(); +} + +/// This is not sufficiently documented. +pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { + unimplemented!(); +} + +/// # Errors +/// A description of the errors goes here. +pub fn pub_fn_with_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// # Errors +/// A description of the errors goes here. +pub async fn async_pub_fn_with_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// This function doesn't require the documentation because it is private +fn priv_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// This function doesn't require the documentation because it is private +async fn async_priv_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +pub struct Struct1; + +impl Struct1 { + /// This is not sufficiently documented. + pub fn pub_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// This is not sufficiently documented. + pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// # Errors + /// A description of the errors goes here. + pub fn pub_method_with_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// # Errors + /// A description of the errors goes here. + pub async fn async_pub_method_with_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// This function doesn't require the documentation because it is private. + fn priv_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// This function doesn't require the documentation because it is private. + async fn async_priv_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } +} + +pub trait Trait1 { + /// This is not sufficiently documented. + fn trait_method_missing_errors_header() -> Result<(), ()>; + + /// # Errors + /// A description of the errors goes here. + fn trait_method_with_errors_header() -> Result<(), ()>; +} + +impl Trait1 for Struct1 { + fn trait_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + fn trait_method_with_errors_header() -> Result<(), ()> { + unimplemented!(); + } +} + +fn main() -> Result<(), ()> { + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/doc_errors.stderr b/src/tools/clippy/tests/ui/doc_errors.stderr new file mode 100644 index 0000000000000..f44d6693d303b --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_errors.stderr @@ -0,0 +1,58 @@ +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:6:1 + | +LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_^ + | + = note: `-D clippy::missing-errors-doc` implied by `-D warnings` + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:10:1 + | +LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:15:1 + | +LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { +LL | | unimplemented!(); +LL | | } + | |_^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:20:1 + | +LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { +LL | | unimplemented!(); +LL | | } + | |_^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:50:5 + | +LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:55:5 + | +LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:84:5 + | +LL | fn trait_method_missing_errors_header() -> Result<(), ()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs new file mode 100644 index 0000000000000..484aa72d59a25 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_unsafe.rs @@ -0,0 +1,100 @@ +// aux-build:doc_unsafe_macros.rs + +#[macro_use] +extern crate doc_unsafe_macros; + +/// This is not sufficiently documented +pub unsafe fn destroy_the_planet() { + unimplemented!(); +} + +/// This one is +/// +/// # Safety +/// +/// This function shouldn't be called unless the horsemen are ready +pub unsafe fn apocalypse(universe: &mut ()) { + unimplemented!(); +} + +/// This is a private function, so docs aren't necessary +unsafe fn you_dont_see_me() { + unimplemented!(); +} + +mod private_mod { + pub unsafe fn only_crate_wide_accessible() { + unimplemented!(); + } + + pub unsafe fn republished() { + unimplemented!(); + } +} + +pub use private_mod::republished; + +pub trait UnsafeTrait { + unsafe fn woefully_underdocumented(self); + + /// # Safety + unsafe fn at_least_somewhat_documented(self); +} + +pub struct Struct; + +impl UnsafeTrait for Struct { + unsafe fn woefully_underdocumented(self) { + // all is well + } + + unsafe fn at_least_somewhat_documented(self) { + // all is still well + } +} + +impl Struct { + pub unsafe fn more_undocumented_unsafe() -> Self { + unimplemented!(); + } + + /// # Safety + pub unsafe fn somewhat_documented(&self) { + unimplemented!(); + } + + unsafe fn private(&self) { + unimplemented!(); + } +} + +macro_rules! very_unsafe { + () => { + pub unsafe fn whee() { + unimplemented!() + } + + /// # Safety + /// + /// Please keep the seat belt fastened + pub unsafe fn drive() { + whee() + } + }; +} + +very_unsafe!(); + +// we don't lint code from external macros +undocd_unsafe!(); + +fn main() { + unsafe { + you_dont_see_me(); + destroy_the_planet(); + let mut universe = (); + apocalypse(&mut universe); + private_mod::only_crate_wide_accessible(); + drive(); + } +} diff --git a/src/tools/clippy/tests/ui/doc_unsafe.stderr b/src/tools/clippy/tests/ui/doc_unsafe.stderr new file mode 100644 index 0000000000000..c784d41ba1724 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_unsafe.stderr @@ -0,0 +1,47 @@ +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:7:1 + | +LL | / pub unsafe fn destroy_the_planet() { +LL | | unimplemented!(); +LL | | } + | |_^ + | + = note: `-D clippy::missing-safety-doc` implied by `-D warnings` + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:30:5 + | +LL | / pub unsafe fn republished() { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:38:5 + | +LL | unsafe fn woefully_underdocumented(self); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:57:5 + | +LL | / pub unsafe fn more_undocumented_unsafe() -> Self { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:73:9 + | +LL | / pub unsafe fn whee() { +LL | | unimplemented!() +LL | | } + | |_________^ +... +LL | very_unsafe!(); + | --------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/double_comparison.fixed b/src/tools/clippy/tests/ui/double_comparison.fixed new file mode 100644 index 0000000000000..bb6cdaa667d4f --- /dev/null +++ b/src/tools/clippy/tests/ui/double_comparison.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +fn main() { + let x = 1; + let y = 2; + if x <= y { + // do something + } + if x <= y { + // do something + } + if x >= y { + // do something + } + if x >= y { + // do something + } + if x != y { + // do something + } + if x != y { + // do something + } + if x == y { + // do something + } + if x == y { + // do something + } +} diff --git a/src/tools/clippy/tests/ui/double_comparison.rs b/src/tools/clippy/tests/ui/double_comparison.rs new file mode 100644 index 0000000000000..9a2a9068a28de --- /dev/null +++ b/src/tools/clippy/tests/ui/double_comparison.rs @@ -0,0 +1,30 @@ +// run-rustfix + +fn main() { + let x = 1; + let y = 2; + if x == y || x < y { + // do something + } + if x < y || x == y { + // do something + } + if x == y || x > y { + // do something + } + if x > y || x == y { + // do something + } + if x < y || x > y { + // do something + } + if x > y || x < y { + // do something + } + if x <= y && x >= y { + // do something + } + if x >= y && x <= y { + // do something + } +} diff --git a/src/tools/clippy/tests/ui/double_comparison.stderr b/src/tools/clippy/tests/ui/double_comparison.stderr new file mode 100644 index 0000000000000..5dcda7b3af4ac --- /dev/null +++ b/src/tools/clippy/tests/ui/double_comparison.stderr @@ -0,0 +1,52 @@ +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:6:8 + | +LL | if x == y || x < y { + | ^^^^^^^^^^^^^^^ help: try: `x <= y` + | + = note: `-D clippy::double-comparisons` implied by `-D warnings` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:9:8 + | +LL | if x < y || x == y { + | ^^^^^^^^^^^^^^^ help: try: `x <= y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:12:8 + | +LL | if x == y || x > y { + | ^^^^^^^^^^^^^^^ help: try: `x >= y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:15:8 + | +LL | if x > y || x == y { + | ^^^^^^^^^^^^^^^ help: try: `x >= y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:18:8 + | +LL | if x < y || x > y { + | ^^^^^^^^^^^^^^ help: try: `x != y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:21:8 + | +LL | if x > y || x < y { + | ^^^^^^^^^^^^^^ help: try: `x != y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:24:8 + | +LL | if x <= y && x >= y { + | ^^^^^^^^^^^^^^^^ help: try: `x == y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:27:8 + | +LL | if x >= y && x <= y { + | ^^^^^^^^^^^^^^^^ help: try: `x == y` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/double_must_use.rs b/src/tools/clippy/tests/ui/double_must_use.rs new file mode 100644 index 0000000000000..a48e675e4ea23 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_must_use.rs @@ -0,0 +1,27 @@ +#![warn(clippy::double_must_use)] + +#[must_use] +pub fn must_use_result() -> Result<(), ()> { + unimplemented!(); +} + +#[must_use] +pub fn must_use_tuple() -> (Result<(), ()>, u8) { + unimplemented!(); +} + +#[must_use] +pub fn must_use_array() -> [Result<(), ()>; 1] { + unimplemented!(); +} + +#[must_use = "With note"] +pub fn must_use_with_note() -> Result<(), ()> { + unimplemented!(); +} + +fn main() { + must_use_result(); + must_use_tuple(); + must_use_with_note(); +} diff --git a/src/tools/clippy/tests/ui/double_must_use.stderr b/src/tools/clippy/tests/ui/double_must_use.stderr new file mode 100644 index 0000000000000..bc37785294fca --- /dev/null +++ b/src/tools/clippy/tests/ui/double_must_use.stderr @@ -0,0 +1,27 @@ +error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` + --> $DIR/double_must_use.rs:4:1 + | +LL | pub fn must_use_result() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::double-must-use` implied by `-D warnings` + = help: either add some descriptive text or remove the attribute + +error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` + --> $DIR/double_must_use.rs:9:1 + | +LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: either add some descriptive text or remove the attribute + +error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` + --> $DIR/double_must_use.rs:14:1 + | +LL | pub fn must_use_array() -> [Result<(), ()>; 1] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: either add some descriptive text or remove the attribute + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/double_neg.rs b/src/tools/clippy/tests/ui/double_neg.rs new file mode 100644 index 0000000000000..d47dfcb5ba1ea --- /dev/null +++ b/src/tools/clippy/tests/ui/double_neg.rs @@ -0,0 +1,7 @@ +#[warn(clippy::double_neg)] +fn main() { + let x = 1; + -x; + -(-x); + --x; +} diff --git a/src/tools/clippy/tests/ui/double_neg.stderr b/src/tools/clippy/tests/ui/double_neg.stderr new file mode 100644 index 0000000000000..d82ed05f0543d --- /dev/null +++ b/src/tools/clippy/tests/ui/double_neg.stderr @@ -0,0 +1,10 @@ +error: `--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op + --> $DIR/double_neg.rs:6:5 + | +LL | --x; + | ^^^ + | + = note: `-D clippy::double-neg` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/double_parens.rs b/src/tools/clippy/tests/ui/double_parens.rs new file mode 100644 index 0000000000000..9c7590c7dd632 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_parens.rs @@ -0,0 +1,56 @@ +#![warn(clippy::double_parens)] +#![allow(dead_code)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +fn dummy_fn(_: T) {} + +struct DummyStruct; + +impl DummyStruct { + fn dummy_method(self, _: T) {} +} + +fn simple_double_parens() -> i32 { + ((0)) +} + +fn fn_double_parens() { + dummy_fn((0)); +} + +fn method_double_parens(x: DummyStruct) { + x.dummy_method((0)); +} + +fn tuple_double_parens() -> (i32, i32) { + ((1, 2)) +} + +fn unit_double_parens() { + (()) +} + +fn fn_tuple_ok() { + dummy_fn((1, 2)); +} + +fn method_tuple_ok(x: DummyStruct) { + x.dummy_method((1, 2)); +} + +fn fn_unit_ok() { + dummy_fn(()); +} + +fn method_unit_ok(x: DummyStruct) { + x.dummy_method(()); +} + +// Issue #3206 +fn inside_macro() { + assert_eq!((1, 2), (1, 2), "Error"); + assert_eq!(((1, 2)), (1, 2), "Error"); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/double_parens.stderr b/src/tools/clippy/tests/ui/double_parens.stderr new file mode 100644 index 0000000000000..0e4c9b5682dfb --- /dev/null +++ b/src/tools/clippy/tests/ui/double_parens.stderr @@ -0,0 +1,40 @@ +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:15:5 + | +LL | ((0)) + | ^^^^^ + | + = note: `-D clippy::double-parens` implied by `-D warnings` + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:19:14 + | +LL | dummy_fn((0)); + | ^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:23:20 + | +LL | x.dummy_method((0)); + | ^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:27:5 + | +LL | ((1, 2)) + | ^^^^^^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:31:5 + | +LL | (()) + | ^^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:53:16 + | +LL | assert_eq!(((1, 2)), (1, 2), "Error"); + | ^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/drop_bounds.rs b/src/tools/clippy/tests/ui/drop_bounds.rs new file mode 100644 index 0000000000000..6d6a9dc078399 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_bounds.rs @@ -0,0 +1,8 @@ +#![allow(unused)] +fn foo() {} +fn bar() +where + T: Drop, +{ +} +fn main() {} diff --git a/src/tools/clippy/tests/ui/drop_bounds.stderr b/src/tools/clippy/tests/ui/drop_bounds.stderr new file mode 100644 index 0000000000000..5d360ef30a1d8 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_bounds.stderr @@ -0,0 +1,16 @@ +error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. + --> $DIR/drop_bounds.rs:2:11 + | +LL | fn foo() {} + | ^^^^ + | + = note: `#[deny(clippy::drop_bounds)]` on by default + +error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. + --> $DIR/drop_bounds.rs:5:8 + | +LL | T: Drop, + | ^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.rs b/src/tools/clippy/tests/ui/drop_forget_copy.rs new file mode 100644 index 0000000000000..9ddd6d64701a6 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_forget_copy.rs @@ -0,0 +1,66 @@ +#![warn(clippy::drop_copy, clippy::forget_copy)] +#![allow(clippy::toplevel_ref_arg, clippy::drop_ref, clippy::forget_ref, unused_mut)] + +use std::mem::{drop, forget}; +use std::vec::Vec; + +#[derive(Copy, Clone)] +struct SomeStruct {} + +struct AnotherStruct { + x: u8, + y: u8, + z: Vec, +} + +impl Clone for AnotherStruct { + fn clone(&self) -> AnotherStruct { + AnotherStruct { + x: self.x, + y: self.y, + z: self.z.clone(), + } + } +} + +fn main() { + let s1 = SomeStruct {}; + let s2 = s1; + let s3 = &s1; + let mut s4 = s1; + let ref s5 = s1; + + drop(s1); + drop(s2); + drop(s3); + drop(s4); + drop(s5); + + forget(s1); + forget(s2); + forget(s3); + forget(s4); + forget(s5); + + let a1 = AnotherStruct { + x: 255, + y: 0, + z: vec![1, 2, 3], + }; + let a2 = &a1; + let mut a3 = a1.clone(); + let ref a4 = a1; + let a5 = a1.clone(); + + drop(a2); + drop(a3); + drop(a4); + drop(a5); + + forget(a2); + let a3 = &a1; + forget(a3); + forget(a4); + let a5 = a1.clone(); + forget(a5); +} diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.stderr b/src/tools/clippy/tests/ui/drop_forget_copy.stderr new file mode 100644 index 0000000000000..82a4f047ba858 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_forget_copy.stderr @@ -0,0 +1,76 @@ +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:33:5 + | +LL | drop(s1); + | ^^^^^^^^ + | + = note: `-D clippy::drop-copy` implied by `-D warnings` +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:33:10 + | +LL | drop(s1); + | ^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:34:5 + | +LL | drop(s2); + | ^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:34:10 + | +LL | drop(s2); + | ^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:36:5 + | +LL | drop(s4); + | ^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:36:10 + | +LL | drop(s4); + | ^^ + +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:39:5 + | +LL | forget(s1); + | ^^^^^^^^^^ + | + = note: `-D clippy::forget-copy` implied by `-D warnings` +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:39:12 + | +LL | forget(s1); + | ^^ + +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:40:5 + | +LL | forget(s2); + | ^^^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:40:12 + | +LL | forget(s2); + | ^^ + +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:42:5 + | +LL | forget(s4); + | ^^^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:42:12 + | +LL | forget(s4); + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/drop_ref.rs b/src/tools/clippy/tests/ui/drop_ref.rs new file mode 100644 index 0000000000000..9181d789d4fb1 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_ref.rs @@ -0,0 +1,72 @@ +#![warn(clippy::drop_ref)] +#![allow(clippy::toplevel_ref_arg)] + +use std::mem::drop; + +struct SomeStruct; + +fn main() { + drop(&SomeStruct); + + let mut owned1 = SomeStruct; + drop(&owned1); + drop(&&owned1); + drop(&mut owned1); + drop(owned1); //OK + + let reference1 = &SomeStruct; + drop(reference1); + + let reference2 = &mut SomeStruct; + drop(reference2); + + let ref reference3 = SomeStruct; + drop(reference3); +} + +#[allow(dead_code)] +fn test_generic_fn_drop(val: T) { + drop(&val); + drop(val); //OK +} + +#[allow(dead_code)] +fn test_similarly_named_function() { + fn drop(_val: T) {} + drop(&SomeStruct); //OK; call to unrelated function which happens to have the same name + std::mem::drop(&SomeStruct); +} + +#[derive(Copy, Clone)] +pub struct Error; +fn produce_half_owl_error() -> Result<(), Error> { + Ok(()) +} + +fn produce_half_owl_ok() -> Result { + Ok(true) +} + +#[allow(dead_code)] +fn test_owl_result() -> Result<(), ()> { + produce_half_owl_error().map_err(|_| ())?; + produce_half_owl_ok().map(|_| ())?; + // the following should not be linted, + // we should not force users to use toilet closures + // to produce owl results when drop is more convenient + produce_half_owl_error().map_err(drop)?; + produce_half_owl_ok().map_err(drop)?; + Ok(()) +} + +#[allow(dead_code)] +fn test_owl_result_2() -> Result { + produce_half_owl_error().map_err(|_| ())?; + produce_half_owl_ok().map(|_| ())?; + // the following should not be linted, + // we should not force users to use toilet closures + // to produce owl results when drop is more convenient + produce_half_owl_error().map_err(drop)?; + produce_half_owl_ok().map(drop)?; + Ok(1) +} diff --git a/src/tools/clippy/tests/ui/drop_ref.stderr b/src/tools/clippy/tests/ui/drop_ref.stderr new file mode 100644 index 0000000000000..35ae88b78a4c5 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_ref.stderr @@ -0,0 +1,111 @@ +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:9:5 + | +LL | drop(&SomeStruct); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::drop-ref` implied by `-D warnings` +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:9:10 + | +LL | drop(&SomeStruct); + | ^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:12:5 + | +LL | drop(&owned1); + | ^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:12:10 + | +LL | drop(&owned1); + | ^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:13:5 + | +LL | drop(&&owned1); + | ^^^^^^^^^^^^^^ + | +note: argument has type `&&SomeStruct` + --> $DIR/drop_ref.rs:13:10 + | +LL | drop(&&owned1); + | ^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:14:5 + | +LL | drop(&mut owned1); + | ^^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/drop_ref.rs:14:10 + | +LL | drop(&mut owned1); + | ^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:18:5 + | +LL | drop(reference1); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:18:10 + | +LL | drop(reference1); + | ^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:21:5 + | +LL | drop(reference2); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/drop_ref.rs:21:10 + | +LL | drop(reference2); + | ^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:24:5 + | +LL | drop(reference3); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:24:10 + | +LL | drop(reference3); + | ^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:29:5 + | +LL | drop(&val); + | ^^^^^^^^^^ + | +note: argument has type `&T` + --> $DIR/drop_ref.rs:29:10 + | +LL | drop(&val); + | ^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:37:5 + | +LL | std::mem::drop(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:37:20 + | +LL | std::mem::drop(&SomeStruct); + | ^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs new file mode 100644 index 0000000000000..54d748c7ce280 --- /dev/null +++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs @@ -0,0 +1,10 @@ +#![warn(clippy::duplicate_underscore_argument)] +#[allow(dead_code, unused)] + +fn join_the_dark_side(darth: i32, _darth: i32) {} +fn join_the_light_side(knight: i32, _master: i32) {} // the Force is strong with this one + +fn main() { + join_the_dark_side(0, 0); + join_the_light_side(0, 0); +} diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr new file mode 100644 index 0000000000000..f71614a5fd16e --- /dev/null +++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr @@ -0,0 +1,10 @@ +error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult + --> $DIR/duplicate_underscore_argument.rs:4:23 + | +LL | fn join_the_dark_side(darth: i32, _darth: i32) {} + | ^^^^^ + | + = note: `-D clippy::duplicate-underscore-argument` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/duration_subsec.fixed b/src/tools/clippy/tests/ui/duration_subsec.fixed new file mode 100644 index 0000000000000..ee5c7863effcb --- /dev/null +++ b/src/tools/clippy/tests/ui/duration_subsec.fixed @@ -0,0 +1,29 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::duration_subsec)] + +use std::time::Duration; + +fn main() { + let dur = Duration::new(5, 0); + + let bad_millis_1 = dur.subsec_millis(); + let bad_millis_2 = dur.subsec_millis(); + let good_millis = dur.subsec_millis(); + assert_eq!(bad_millis_1, good_millis); + assert_eq!(bad_millis_2, good_millis); + + let bad_micros = dur.subsec_micros(); + let good_micros = dur.subsec_micros(); + assert_eq!(bad_micros, good_micros); + + // Handle refs + let _ = (&dur).subsec_micros(); + + // Handle constants + const NANOS_IN_MICRO: u32 = 1_000; + let _ = dur.subsec_micros(); + + // Other literals aren't linted + let _ = dur.subsec_nanos() / 699; +} diff --git a/src/tools/clippy/tests/ui/duration_subsec.rs b/src/tools/clippy/tests/ui/duration_subsec.rs new file mode 100644 index 0000000000000..3c9d2a2862110 --- /dev/null +++ b/src/tools/clippy/tests/ui/duration_subsec.rs @@ -0,0 +1,29 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::duration_subsec)] + +use std::time::Duration; + +fn main() { + let dur = Duration::new(5, 0); + + let bad_millis_1 = dur.subsec_micros() / 1_000; + let bad_millis_2 = dur.subsec_nanos() / 1_000_000; + let good_millis = dur.subsec_millis(); + assert_eq!(bad_millis_1, good_millis); + assert_eq!(bad_millis_2, good_millis); + + let bad_micros = dur.subsec_nanos() / 1_000; + let good_micros = dur.subsec_micros(); + assert_eq!(bad_micros, good_micros); + + // Handle refs + let _ = (&dur).subsec_nanos() / 1_000; + + // Handle constants + const NANOS_IN_MICRO: u32 = 1_000; + let _ = dur.subsec_nanos() / NANOS_IN_MICRO; + + // Other literals aren't linted + let _ = dur.subsec_nanos() / 699; +} diff --git a/src/tools/clippy/tests/ui/duration_subsec.stderr b/src/tools/clippy/tests/ui/duration_subsec.stderr new file mode 100644 index 0000000000000..bd8adc2c57055 --- /dev/null +++ b/src/tools/clippy/tests/ui/duration_subsec.stderr @@ -0,0 +1,34 @@ +error: Calling `subsec_millis()` is more concise than this calculation + --> $DIR/duration_subsec.rs:10:24 + | +LL | let bad_millis_1 = dur.subsec_micros() / 1_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` + | + = note: `-D clippy::duration-subsec` implied by `-D warnings` + +error: Calling `subsec_millis()` is more concise than this calculation + --> $DIR/duration_subsec.rs:11:24 + | +LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` + +error: Calling `subsec_micros()` is more concise than this calculation + --> $DIR/duration_subsec.rs:16:22 + | +LL | let bad_micros = dur.subsec_nanos() / 1_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` + +error: Calling `subsec_micros()` is more concise than this calculation + --> $DIR/duration_subsec.rs:21:13 + | +LL | let _ = (&dur).subsec_nanos() / 1_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` + +error: Calling `subsec_micros()` is more concise than this calculation + --> $DIR/duration_subsec.rs:25:13 + | +LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/else_if_without_else.rs b/src/tools/clippy/tests/ui/else_if_without_else.rs new file mode 100644 index 0000000000000..879b3ac398e45 --- /dev/null +++ b/src/tools/clippy/tests/ui/else_if_without_else.rs @@ -0,0 +1,58 @@ +#![warn(clippy::all)] +#![warn(clippy::else_if_without_else)] + +fn bla1() -> bool { + unimplemented!() +} +fn bla2() -> bool { + unimplemented!() +} +fn bla3() -> bool { + unimplemented!() +} + +fn main() { + if bla1() { + println!("if"); + } + + if bla1() { + println!("if"); + } else { + println!("else"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if"); + } else { + println!("else") + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + println!("else if 2"); + } else { + println!("else") + } + + if bla1() { + println!("if"); + } else if bla2() { + //~ ERROR else if without else + println!("else if"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + //~ ERROR else if without else + println!("else if 2"); + } +} diff --git a/src/tools/clippy/tests/ui/else_if_without_else.stderr b/src/tools/clippy/tests/ui/else_if_without_else.stderr new file mode 100644 index 0000000000000..6f47658cfb184 --- /dev/null +++ b/src/tools/clippy/tests/ui/else_if_without_else.stderr @@ -0,0 +1,27 @@ +error: `if` expression with an `else if`, but without a final `else` + --> $DIR/else_if_without_else.rs:45:12 + | +LL | } else if bla2() { + | ____________^ +LL | | //~ ERROR else if without else +LL | | println!("else if"); +LL | | } + | |_____^ + | + = note: `-D clippy::else-if-without-else` implied by `-D warnings` + = help: add an `else` block here + +error: `if` expression with an `else if`, but without a final `else` + --> $DIR/else_if_without_else.rs:54:12 + | +LL | } else if bla3() { + | ____________^ +LL | | //~ ERROR else if without else +LL | | println!("else if 2"); +LL | | } + | |_____^ + | + = help: add an `else` block here + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/empty_enum.rs b/src/tools/clippy/tests/ui/empty_enum.rs new file mode 100644 index 0000000000000..12428f29625c0 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum.rs @@ -0,0 +1,6 @@ +#![allow(dead_code)] +#![warn(clippy::empty_enum)] + +enum Empty {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum.stderr b/src/tools/clippy/tests/ui/empty_enum.stderr new file mode 100644 index 0000000000000..466dfbe7cee7a --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum.stderr @@ -0,0 +1,11 @@ +error: enum with no variants + --> $DIR/empty_enum.rs:4:1 + | +LL | enum Empty {} + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::empty-enum` implied by `-D warnings` + = help: consider using the uninhabited type `!` (never type) or a wrapper around it to introduce a type which can't be instantiated + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs new file mode 100644 index 0000000000000..5343dff9da1db --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs @@ -0,0 +1,96 @@ +#![warn(clippy::empty_line_after_outer_attr)] +#![allow(clippy::assertions_on_constants)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +// This should produce a warning +#[crate_type = "lib"] + +/// some comment +fn with_one_newline_and_comment() { assert!(true) } + +// This should not produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() { assert!(true) } + + +// This should produce a warning +#[crate_type = "lib"] + +fn with_one_newline() { assert!(true) } + +// This should produce a warning, too +#[crate_type = "lib"] + + +fn with_two_newlines() { assert!(true) } + + +// This should produce a warning +#[crate_type = "lib"] + +enum Baz { + One, + Two +} + +// This should produce a warning +#[crate_type = "lib"] + +struct Foo { + one: isize, + two: isize +} + +// This should produce a warning +#[crate_type = "lib"] + +mod foo { +} + +/// This doc comment should not produce a warning + +/** This is also a doc comment and should not produce a warning + */ + +// This should not produce a warning +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(missing_docs)] +fn three_attributes() { assert!(true) } + +// This should not produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should not produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4 +} + +// This should not produce a warning because the empty line is inside a block comment +#[crate_type = "lib"] +/* + +*/ +pub struct S; + +// This should not produce a warning +#[crate_type = "lib"] +/* test */ +pub struct T; + +fn main() { } diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr new file mode 100644 index 0000000000000..d8c9786541f0b --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr @@ -0,0 +1,54 @@ +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:7:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | /// some comment +LL | | fn with_one_newline_and_comment() { assert!(true) } + | |_ + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:19:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | fn with_one_newline() { assert!(true) } + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:24:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | +LL | | fn with_two_newlines() { assert!(true) } + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:31:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | enum Baz { + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:39:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | struct Foo { + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:47:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | mod foo { + | |_ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/empty_loop.rs b/src/tools/clippy/tests/ui/empty_loop.rs new file mode 100644 index 0000000000000..8fd7697eb3b29 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_loop.rs @@ -0,0 +1,51 @@ +// aux-build:macro_rules.rs + +#![warn(clippy::empty_loop)] + +#[macro_use] +extern crate macro_rules; + +fn should_trigger() { + loop {} + loop { + loop {} + } + + 'outer: loop { + 'inner: loop {} + } +} + +fn should_not_trigger() { + loop { + panic!("This is fine") + } + let ten_millis = std::time::Duration::from_millis(10); + loop { + std::thread::sleep(ten_millis) + } + + #[allow(clippy::never_loop)] + 'outer: loop { + 'inner: loop { + break 'inner; + } + break 'outer; + } + + // Make sure `allow` works for this lint + #[allow(clippy::empty_loop)] + loop {} + + // We don't lint loops inside macros + macro_rules! foo { + () => { + loop {} + }; + } + + // We don't lint external macros + foofoo!() +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_loop.stderr b/src/tools/clippy/tests/ui/empty_loop.stderr new file mode 100644 index 0000000000000..e44c58ea770ae --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_loop.stderr @@ -0,0 +1,22 @@ +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/empty_loop.rs:9:5 + | +LL | loop {} + | ^^^^^^^ + | + = note: `-D clippy::empty-loop` implied by `-D warnings` + +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/empty_loop.rs:11:9 + | +LL | loop {} + | ^^^^^^^ + +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/empty_loop.rs:15:9 + | +LL | 'inner: loop {} + | ^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/entry_fixable.fixed b/src/tools/clippy/tests/ui/entry_fixable.fixed new file mode 100644 index 0000000000000..dcdaae7e72430 --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_fixable.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { + m.entry(k).or_insert(v); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_fixable.rs b/src/tools/clippy/tests/ui/entry_fixable.rs new file mode 100644 index 0000000000000..55d5b21568d0e --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_fixable.rs @@ -0,0 +1,17 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + m.insert(k, v); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_fixable.stderr b/src/tools/clippy/tests/ui/entry_fixable.stderr new file mode 100644 index 0000000000000..87403200ced50 --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_fixable.stderr @@ -0,0 +1,12 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_fixable.rs:12:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } + | |_____^ help: consider using: `m.entry(k).or_insert(v);` + | + = note: `-D clippy::map-entry` implied by `-D warnings` + +error: aborting due to previous error + 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..f530fc023cfbf --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_unfixable.rs @@ -0,0 +1,73 @@ +#![allow(unused, clippy::needless_pass_by_value)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +fn foo() {} + +fn insert_if_absent2(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + m.insert(k, v) + } else { + None + }; +} + +fn insert_if_present2(m: &mut HashMap, k: K, v: V) { + if m.contains_key(&k) { + None + } else { + m.insert(k, v) + }; +} + +fn insert_if_absent3(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + foo(); + m.insert(k, v) + } else { + None + }; +} + +fn insert_if_present3(m: &mut HashMap, k: K, v: V) { + if m.contains_key(&k) { + None + } else { + foo(); + m.insert(k, v) + }; +} + +fn insert_in_btreemap(m: &mut BTreeMap, k: K, v: V) { + if !m.contains_key(&k) { + foo(); + m.insert(k, v) + } else { + None + }; +} + +// should not trigger +fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { + if !m.contains_key(&k) { + m.insert(o, v); + } +} + +// should not trigger, because the one uses different HashMap from another one +fn insert_from_different_map(m: HashMap, n: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + n.insert(k, v); + } +} + +// should not trigger, because the one uses different HashMap from another one +fn insert_from_different_map2(m: &mut HashMap, n: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + n.insert(k, v); + } +} + +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..e58c8d22dc45e --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_unfixable.stderr @@ -0,0 +1,57 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:10:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ consider using `m.entry(k)` + | + = note: `-D clippy::map-entry` implied by `-D warnings` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:18:5 + | +LL | / if m.contains_key(&k) { +LL | | None +LL | | } else { +LL | | m.insert(k, v) +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:26:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:35:5 + | +LL | / if m.contains_key(&k) { +LL | | None +LL | | } else { +LL | | foo(); +LL | | m.insert(k, v) +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: usage of `contains_key` followed by `insert` on a `BTreeMap` + --> $DIR/entry_unfixable.rs:44:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs new file mode 100644 index 0000000000000..7d6842f5b5421 --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs @@ -0,0 +1,50 @@ +// ignore-x86 + +#![warn(clippy::enum_clike_unportable_variant)] +#![allow(unused, non_upper_case_globals)] + +#[repr(usize)] +enum NonPortable { + X = 0x1_0000_0000, + Y = 0, + Z = 0x7FFF_FFFF, + A = 0xFFFF_FFFF, +} + +enum NonPortableNoHint { + X = 0x1_0000_0000, + Y = 0, + Z = 0x7FFF_FFFF, + A = 0xFFFF_FFFF, +} + +#[repr(isize)] +enum NonPortableSigned { + X = -1, + Y = 0x7FFF_FFFF, + Z = 0xFFFF_FFFF, + A = 0x1_0000_0000, + B = i32::MIN as isize, + C = (i32::MIN as isize) - 1, +} + +enum NonPortableSignedNoHint { + X = -1, + Y = 0x7FFF_FFFF, + Z = 0xFFFF_FFFF, + A = 0x1_0000_0000, +} + +#[repr(usize)] +enum NonPortable2 { + X = ::Number, + Y = 0, +} + +trait Trait { + const Number: usize = 0x1_0000_0000; +} + +impl Trait for usize {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr new file mode 100644 index 0000000000000..71f3f5e083e0d --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr @@ -0,0 +1,58 @@ +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:8:5 + | +LL | X = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:15:5 + | +LL | X = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:18:5 + | +LL | A = 0xFFFF_FFFF, + | ^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:25:5 + | +LL | Z = 0xFFFF_FFFF, + | ^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:26:5 + | +LL | A = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:28:5 + | +LL | C = (i32::MIN as isize) - 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:34:5 + | +LL | Z = 0xFFFF_FFFF, + | ^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:35:5 + | +LL | A = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:40:5 + | +LL | X = ::Number, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/enum_glob_use.fixed b/src/tools/clippy/tests/ui/enum_glob_use.fixed new file mode 100644 index 0000000000000..a98216758bb9e --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_glob_use.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::enum_glob_use)] +#![allow(unused)] +#![warn(unused_imports)] + +use std::cmp::Ordering::Less; + +enum Enum { + Foo, +} + +use self::Enum::Foo; + +mod in_fn_test { + fn blarg() { + use crate::Enum::Foo; + + let _ = Foo; + } +} + +mod blurg { + pub use std::cmp::Ordering::*; // ok, re-export +} + +fn main() { + let _ = Foo; + let _ = Less; +} diff --git a/src/tools/clippy/tests/ui/enum_glob_use.rs b/src/tools/clippy/tests/ui/enum_glob_use.rs new file mode 100644 index 0000000000000..5d929c9731d34 --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_glob_use.rs @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::enum_glob_use)] +#![allow(unused)] +#![warn(unused_imports)] + +use std::cmp::Ordering::*; + +enum Enum { + Foo, +} + +use self::Enum::*; + +mod in_fn_test { + fn blarg() { + use crate::Enum::*; + + let _ = Foo; + } +} + +mod blurg { + pub use std::cmp::Ordering::*; // ok, re-export +} + +fn main() { + let _ = Foo; + let _ = Less; +} diff --git a/src/tools/clippy/tests/ui/enum_glob_use.stderr b/src/tools/clippy/tests/ui/enum_glob_use.stderr new file mode 100644 index 0000000000000..69531aed39bd4 --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_glob_use.stderr @@ -0,0 +1,22 @@ +error: usage of wildcard import for enum variants + --> $DIR/enum_glob_use.rs:7:5 + | +LL | use std::cmp::Ordering::*; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::cmp::Ordering::Less` + | + = note: `-D clippy::enum-glob-use` implied by `-D warnings` + +error: usage of wildcard import for enum variants + --> $DIR/enum_glob_use.rs:13:5 + | +LL | use self::Enum::*; + | ^^^^^^^^^^^^^ help: try: `self::Enum::Foo` + +error: usage of wildcard import for enum variants + --> $DIR/enum_glob_use.rs:17:13 + | +LL | use crate::Enum::*; + | ^^^^^^^^^^^^^^ help: try: `crate::Enum::Foo` + +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 new file mode 100644 index 0000000000000..01774a2a9845c --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_variants.rs @@ -0,0 +1,136 @@ +#![feature(non_ascii_idents)] +#![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)] +#![allow(non_camel_case_types)] + +enum FakeCallType { + CALL, + CREATE, +} + +enum FakeCallType2 { + CALL, + CREATELL, +} + +enum Foo { + cFoo, + cBar, + cBaz, +} + +enum Fooo { + cFoo, // no error, threshold is 3 variants by default + cBar, +} + +enum Food { + FoodGood, + FoodMiddle, + FoodBad, +} + +enum Stuff { + StuffBad, // no error +} + +enum BadCallType { + CallTypeCall, + CallTypeCreate, + CallTypeDestroy, +} + +enum TwoCallType { + // no error + CallTypeCall, + CallTypeCreate, +} + +enum Consts { + ConstantInt, + ConstantCake, + ConstantLie, +} + +enum Two { + // no error here + ConstantInt, + ConstantInfer, +} + +enum Something { + CCall, + CCreate, + CCryogenize, +} + +enum Seal { + With, + Without, +} + +enum Seall { + With, + WithOut, + Withbroken, +} + +enum Sealll { + With, + WithOut, +} + +enum Seallll { + WithOutCake, + WithOutTea, + WithOut, +} + +enum NonCaps { + Prefix的, + PrefixTea, + PrefixCake, +} + +pub enum PubSeall { + WithOutCake, + WithOutTea, + WithOut, +} + +#[allow(clippy::pub_enum_variant_names)] +mod allowed { + pub enum PubAllowed { + SomeThis, + SomeThat, + SomeOtherWhat, + } +} + +// should not lint +enum Pat { + Foo, + Bar, + Path, +} + +// should not lint +enum N { + Pos, + Neg, + Float, +} + +// should not lint +enum Peek { + Peek1, + Peek2, + Peek3, +} + +// should not lint +pub enum NetworkLayer { + Layer2, + Layer3, +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/enum_variants.stderr b/src/tools/clippy/tests/ui/enum_variants.stderr new file mode 100644 index 0000000000000..2835391de7f58 --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_variants.stderr @@ -0,0 +1,101 @@ +error: Variant name ends with the enum's name + --> $DIR/enum_variants.rs:16:5 + | +LL | cFoo, + | ^^^^ + | + = note: `-D clippy::enum-variant-names` implied by `-D warnings` + +error: Variant name starts with the enum's name + --> $DIR/enum_variants.rs:27:5 + | +LL | FoodGood, + | ^^^^^^^^ + +error: Variant name starts with the enum's name + --> $DIR/enum_variants.rs:28:5 + | +LL | FoodMiddle, + | ^^^^^^^^^^ + +error: Variant name starts with the enum's name + --> $DIR/enum_variants.rs:29:5 + | +LL | FoodBad, + | ^^^^^^^ + +error: All variants have the same prefix: `Food` + --> $DIR/enum_variants.rs:26:1 + | +LL | / enum Food { +LL | | FoodGood, +LL | | FoodMiddle, +LL | | FoodBad, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `CallType` + --> $DIR/enum_variants.rs:36:1 + | +LL | / enum BadCallType { +LL | | CallTypeCall, +LL | | CallTypeCreate, +LL | | CallTypeDestroy, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `Constant` + --> $DIR/enum_variants.rs:48:1 + | +LL | / enum Consts { +LL | | ConstantInt, +LL | | ConstantCake, +LL | | ConstantLie, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `With` + --> $DIR/enum_variants.rs:82:1 + | +LL | / enum Seallll { +LL | | WithOutCake, +LL | | WithOutTea, +LL | | WithOut, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `Prefix` + --> $DIR/enum_variants.rs:88:1 + | +LL | / enum NonCaps { +LL | | Prefix的, +LL | | PrefixTea, +LL | | PrefixCake, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `With` + --> $DIR/enum_variants.rs:94:1 + | +LL | / pub enum PubSeall { +LL | | WithOutCake, +LL | | WithOutTea, +LL | | WithOut, +LL | | } + | |_^ + | + = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings` + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/eq_op.rs b/src/tools/clippy/tests/ui/eq_op.rs new file mode 100644 index 0000000000000..272b0900a31c6 --- /dev/null +++ b/src/tools/clippy/tests/ui/eq_op.rs @@ -0,0 +1,87 @@ +// does not test any rustfixable lints + +#[rustfmt::skip] +#[warn(clippy::eq_op)] +#[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)] +#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] +#[allow(clippy::nonminimal_bool)] +#[allow(unused)] +fn main() { + // simple values and comparisons + 1 == 1; + "no" == "no"; + // even though I agree that no means no ;-) + false != false; + 1.5 < 1.5; + 1u64 >= 1u64; + + // casts, methods, parentheses + (1 as u64) & (1 as u64); + 1 ^ ((((((1)))))); + + // unary and binary operators + (-(2) < -(2)); + ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; + + // various other things + ([1] != [1]); + ((1, 2) != (1, 2)); + vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros + + // const folding + 1 + 1 == 2; + 1 - 1 == 0; + + 1 - 1; + 1 / 1; + true && true; + + true || true; + + + let a: u32 = 0; + let b: u32 = 0; + + a == b && b == a; + a != b && b != a; + a < b && b > a; + a <= b && b >= a; + + let mut a = vec![1]; + a == a; + 2*a.len() == 2*a.len(); // ok, functions + a.pop() == a.pop(); // ok, functions + + check_ignore_macro(); + + // named constants + const A: u32 = 10; + const B: u32 = 10; + const C: u32 = A / B; // ok, different named constants + const D: u32 = A / A; +} + +#[rustfmt::skip] +macro_rules! check_if_named_foo { + ($expression:expr) => ( + if stringify!($expression) == "foo" { + println!("foo!"); + } else { + println!("not foo."); + } + ) +} + +macro_rules! bool_macro { + ($expression:expr) => { + true + }; +} + +#[allow(clippy::short_circuit_statement)] +fn check_ignore_macro() { + check_if_named_foo!(foo); + // checks if the lint ignores macros with `!` operator + !bool_macro!(1) && !bool_macro!(""); +} diff --git a/src/tools/clippy/tests/ui/eq_op.stderr b/src/tools/clippy/tests/ui/eq_op.stderr new file mode 100644 index 0000000000000..5b80e6078eed7 --- /dev/null +++ b/src/tools/clippy/tests/ui/eq_op.stderr @@ -0,0 +1,166 @@ +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:11:5 + | +LL | 1 == 1; + | ^^^^^^ + | + = note: `-D clippy::eq-op` implied by `-D warnings` + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:12:5 + | +LL | "no" == "no"; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `!=` + --> $DIR/eq_op.rs:14:5 + | +LL | false != false; + | ^^^^^^^^^^^^^^ + +error: equal expressions as operands to `<` + --> $DIR/eq_op.rs:15:5 + | +LL | 1.5 < 1.5; + | ^^^^^^^^^ + +error: equal expressions as operands to `>=` + --> $DIR/eq_op.rs:16:5 + | +LL | 1u64 >= 1u64; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `&` + --> $DIR/eq_op.rs:19:5 + | +LL | (1 as u64) & (1 as u64); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `^` + --> $DIR/eq_op.rs:20:5 + | +LL | 1 ^ ((((((1)))))); + | ^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `<` + --> $DIR/eq_op.rs:23:5 + | +LL | (-(2) < -(2)); + | ^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:24:5 + | +LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&` + --> $DIR/eq_op.rs:24:6 + | +LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + | ^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&` + --> $DIR/eq_op.rs:24:27 + | +LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + | ^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:25:5 + | +LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `!=` + --> $DIR/eq_op.rs:28:5 + | +LL | ([1] != [1]); + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `!=` + --> $DIR/eq_op.rs:29:5 + | +LL | ((1, 2) != (1, 2)); + | ^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:33:5 + | +LL | 1 + 1 == 2; + | ^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:34:5 + | +LL | 1 - 1 == 0; + | ^^^^^^^^^^ + +error: equal expressions as operands to `-` + --> $DIR/eq_op.rs:34:5 + | +LL | 1 - 1 == 0; + | ^^^^^ + +error: equal expressions as operands to `-` + --> $DIR/eq_op.rs:36:5 + | +LL | 1 - 1; + | ^^^^^ + +error: equal expressions as operands to `/` + --> $DIR/eq_op.rs:37:5 + | +LL | 1 / 1; + | ^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:38:5 + | +LL | true && true; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `||` + --> $DIR/eq_op.rs:40:5 + | +LL | true || true; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:46:5 + | +LL | a == b && b == a; + | ^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:47:5 + | +LL | a != b && b != a; + | ^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:48:5 + | +LL | a < b && b > a; + | ^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:49:5 + | +LL | a <= b && b >= a; + | ^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:52:5 + | +LL | a == a; + | ^^^^^^ + +error: equal expressions as operands to `/` + --> $DIR/eq_op.rs:62:20 + | +LL | const D: u32 = A / A; + | ^^^^^ + +error: aborting due to 27 previous errors + diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs new file mode 100644 index 0000000000000..1540062a4bc3e --- /dev/null +++ b/src/tools/clippy/tests/ui/erasing_op.rs @@ -0,0 +1,9 @@ +#[allow(clippy::no_effect)] +#[warn(clippy::erasing_op)] +fn main() { + let x: u8 = 0; + + x * 0; + 0 & x; + 0 / x; +} diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr new file mode 100644 index 0000000000000..e54ce85f98ec7 --- /dev/null +++ b/src/tools/clippy/tests/ui/erasing_op.stderr @@ -0,0 +1,22 @@ +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:6:5 + | +LL | x * 0; + | ^^^^^ + | + = note: `-D clippy::erasing-op` implied by `-D warnings` + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:7:5 + | +LL | 0 & x; + | ^^^^^ + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:8:5 + | +LL | 0 / x; + | ^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/escape_analysis.rs b/src/tools/clippy/tests/ui/escape_analysis.rs new file mode 100644 index 0000000000000..c0a52d832c00a --- /dev/null +++ b/src/tools/clippy/tests/ui/escape_analysis.rs @@ -0,0 +1,176 @@ +#![feature(box_syntax)] +#![allow( + clippy::borrowed_box, + clippy::needless_pass_by_value, + clippy::unused_unit, + clippy::redundant_clone, + clippy::match_single_binding +)] +#![warn(clippy::boxed_local)] + +#[derive(Clone)] +struct A; + +impl A { + fn foo(&self) {} +} + +trait Z { + fn bar(&self); +} + +impl Z for A { + fn bar(&self) { + //nothing + } +} + +fn main() {} + +fn ok_box_trait(boxed_trait: &Box) { + let boxed_local = boxed_trait; + // done +} + +fn warn_call() { + let x = box A; + x.foo(); +} + +fn warn_arg(x: Box) { + x.foo(); +} + +fn nowarn_closure_arg() { + let x = Some(box A); + x.map_or((), |x| take_ref(&x)); +} + +fn warn_rename_call() { + let x = box A; + + let y = x; + y.foo(); // via autoderef +} + +fn warn_notuse() { + let bz = box A; +} + +fn warn_pass() { + let bz = box A; + take_ref(&bz); // via deref coercion +} + +fn nowarn_return() -> Box { + box A // moved out, "escapes" +} + +fn nowarn_move() { + let bx = box A; + drop(bx) // moved in, "escapes" +} +fn nowarn_call() { + let bx = box A; + bx.clone(); // method only available to Box, not via autoderef +} + +fn nowarn_pass() { + let bx = box A; + take_box(&bx); // fn needs &Box +} + +fn take_box(x: &Box) {} +fn take_ref(x: &A) {} + +fn nowarn_ref_take() { + // false positive, should actually warn + let x = box A; + let y = &x; + take_box(y); +} + +fn nowarn_match() { + let x = box A; // moved into a match + match x { + y => drop(y), + } +} + +fn warn_match() { + let x = box A; + match &x { + // not moved + ref y => (), + } +} + +fn nowarn_large_array() { + // should not warn, is large array + // and should not be on stack + let x = box [1; 10000]; + match &x { + // not moved + ref y => (), + } +} + +/// ICE regression test +pub trait Foo { + type Item; +} + +impl<'a> Foo for &'a () { + type Item = (); +} + +pub struct PeekableSeekable { + _peeked: I::Item, +} + +pub fn new(_needs_name: Box>) -> () {} + +/// Regression for #916, #1123 +/// +/// This shouldn't warn for `boxed_local`as the implementation of a trait +/// can't change much about the trait definition. +trait BoxedAction { + fn do_sth(self: Box); +} + +impl BoxedAction for u64 { + fn do_sth(self: Box) { + println!("{}", *self) + } +} + +/// Regression for #1478 +/// +/// This shouldn't warn for `boxed_local`as self itself is a box type. +trait MyTrait { + fn do_sth(self); +} + +impl MyTrait for Box { + fn do_sth(self) {} +} + +// Issue #3739 - capture in closures +mod issue_3739 { + use super::A; + + fn consume(_: T) {} + fn borrow(_: &T) {} + + fn closure_consume(x: Box) { + let _ = move || { + consume(x); + }; + } + + fn closure_borrow(x: Box) { + let _ = || { + borrow(&x); + }; + } +} diff --git a/src/tools/clippy/tests/ui/escape_analysis.stderr b/src/tools/clippy/tests/ui/escape_analysis.stderr new file mode 100644 index 0000000000000..c86a769a3da4b --- /dev/null +++ b/src/tools/clippy/tests/ui/escape_analysis.stderr @@ -0,0 +1,16 @@ +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:40:13 + | +LL | fn warn_arg(x: Box) { + | ^ + | + = note: `-D clippy::boxed-local` implied by `-D warnings` + +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:131:12 + | +LL | pub fn new(_needs_name: Box>) -> () {} + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed new file mode 100644 index 0000000000000..1b34c2f74eba1 --- /dev/null +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -0,0 +1,204 @@ +// run-rustfix + +#![allow( + unused, + clippy::no_effect, + clippy::redundant_closure_call, + clippy::many_single_char_names, + clippy::needless_pass_by_value, + clippy::option_map_unit_fn +)] +#![warn( + clippy::redundant_closure, + clippy::redundant_closure_for_method_calls, + clippy::needless_borrow +)] + +use std::path::PathBuf; + +fn main() { + let a = Some(1u8).map(foo); + meta(foo); + let c = Some(1u8).map(|a| {1+2; foo}(a)); + let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? + all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted + unsafe { + Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn + } + + // See #815 + let e = Some(1u8).map(|a| divergent(a)); + let e = Some(1u8).map(generic); + let e = Some(1u8).map(generic); + // See #515 + let a: Option)>> = + Some(vec![1i32, 2]).map(|v| -> Box)> { Box::new(v) }); +} + +trait TestTrait { + fn trait_foo(self) -> bool; + fn trait_foo_ref(&self) -> bool; +} + +struct TestStruct<'a> { + some_ref: &'a i32, +} + +impl<'a> TestStruct<'a> { + fn foo(self) -> bool { + false + } + unsafe fn foo_unsafe(self) -> bool { + true + } +} + +impl<'a> TestTrait for TestStruct<'a> { + fn trait_foo(self) -> bool { + false + } + fn trait_foo_ref(&self) -> bool { + false + } +} + +impl<'a> std::ops::Deref for TestStruct<'a> { + type Target = char; + fn deref(&self) -> &char { + &'a' + } +} + +fn test_redundant_closures_containing_method_calls() { + let i = 10; + let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); + let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); + let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); + let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); + let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); + let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); + unsafe { + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe()); + } + let e = Some("str").map(std::string::ToString::to_string); + let e = Some("str").map(str::to_string); + let e = Some('a').map(char::to_uppercase); + let e = Some('a').map(char::to_uppercase); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect(); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); + let p = Some(PathBuf::new()); + let e = p.as_ref().and_then(|s| s.to_str()); + let c = Some(TestStruct { some_ref: &i }) + .as_ref() + .map(|c| c.to_ascii_uppercase()); + + fn test_different_borrow_levels(t: &[&T]) + where + T: TestTrait, + { + t.iter().filter(|x| x.trait_foo_ref()); + t.iter().map(|x| x.trait_foo_ref()); + } + + let mut some = Some(|x| x * x); + let arr = [Ok(1), Err(2)]; + let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); +} + +struct Thunk(Box T>); + +impl Thunk { + fn new T>(f: F) -> Thunk { + let mut option = Some(f); + // This should not trigger redundant_closure (#1439) + Thunk(Box::new(move || option.take().unwrap()())) + } + + fn unwrap(self) -> T { + let Thunk(mut f) = self; + f() + } +} + +fn foobar() { + let thunk = Thunk::new(|| println!("Hello, world!")); + thunk.unwrap() +} + +fn meta(f: F) +where + F: Fn(u8), +{ + f(1u8) +} + +fn foo(_: u8) {} + +fn foo2(_: u8) -> u8 { + 1u8 +} + +fn all(x: &[X], y: &X, f: F) -> bool +where + F: Fn(&X, &X) -> bool, +{ + x.iter().all(|e| f(e, y)) +} + +fn below(x: &u8, y: &u8) -> bool { + x < y +} + +unsafe fn unsafe_fn(_: u8) {} + +fn divergent(_: u8) -> ! { + unimplemented!() +} + +fn generic(_: T) -> u8 { + 0 +} + +fn passes_fn_mut(mut x: Box) { + requires_fn_once(|| x()); +} +fn requires_fn_once(_: T) {} + +fn test_redundant_closure_with_function_pointer() { + type FnPtrType = fn(u8); + let foo_ptr: FnPtrType = foo; + let a = Some(1u8).map(foo_ptr); +} + +fn test_redundant_closure_with_another_closure() { + let closure = |a| println!("{}", a); + let a = Some(1u8).map(closure); +} + +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 { + // Currently f is called when result of make_lazy is called. + // If the closure is removed, f will be called when make_lazy itself is + // called. This changes semantics, so the closure must stay. + Box::new(move |x| f()(x)) +} + +fn call String>(f: F) -> String { + f(&mut "Hello".to_owned()) +} +fn test_difference_in_mutability() { + call(|s| s.clone()); +} + +struct Bar; +impl std::ops::Deref for Bar { + type Target = str; + fn deref(&self) -> &str { + "hi" + } +} + +fn test_deref_with_trait_method() { + let _ = [Bar].iter().map(|s| s.to_string()).collect::>(); +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs new file mode 100644 index 0000000000000..4f050bd8479ae --- /dev/null +++ b/src/tools/clippy/tests/ui/eta.rs @@ -0,0 +1,204 @@ +// run-rustfix + +#![allow( + unused, + clippy::no_effect, + clippy::redundant_closure_call, + clippy::many_single_char_names, + clippy::needless_pass_by_value, + clippy::option_map_unit_fn +)] +#![warn( + clippy::redundant_closure, + clippy::redundant_closure_for_method_calls, + clippy::needless_borrow +)] + +use std::path::PathBuf; + +fn main() { + let a = Some(1u8).map(|a| foo(a)); + meta(|a| foo(a)); + let c = Some(1u8).map(|a| {1+2; foo}(a)); + let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? + all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted + unsafe { + Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn + } + + // See #815 + let e = Some(1u8).map(|a| divergent(a)); + let e = Some(1u8).map(|a| generic(a)); + let e = Some(1u8).map(generic); + // See #515 + let a: Option)>> = + Some(vec![1i32, 2]).map(|v| -> Box)> { Box::new(v) }); +} + +trait TestTrait { + fn trait_foo(self) -> bool; + fn trait_foo_ref(&self) -> bool; +} + +struct TestStruct<'a> { + some_ref: &'a i32, +} + +impl<'a> TestStruct<'a> { + fn foo(self) -> bool { + false + } + unsafe fn foo_unsafe(self) -> bool { + true + } +} + +impl<'a> TestTrait for TestStruct<'a> { + fn trait_foo(self) -> bool { + false + } + fn trait_foo_ref(&self) -> bool { + false + } +} + +impl<'a> std::ops::Deref for TestStruct<'a> { + type Target = char; + fn deref(&self) -> &char { + &'a' + } +} + +fn test_redundant_closures_containing_method_calls() { + let i = 10; + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); + let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); + let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); + let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); + let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); + unsafe { + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe()); + } + let e = Some("str").map(|s| s.to_string()); + let e = Some("str").map(str::to_string); + let e = Some('a').map(|s| s.to_uppercase()); + let e = Some('a').map(char::to_uppercase); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect(); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); + let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); + let p = Some(PathBuf::new()); + let e = p.as_ref().and_then(|s| s.to_str()); + let c = Some(TestStruct { some_ref: &i }) + .as_ref() + .map(|c| c.to_ascii_uppercase()); + + fn test_different_borrow_levels(t: &[&T]) + where + T: TestTrait, + { + t.iter().filter(|x| x.trait_foo_ref()); + t.iter().map(|x| x.trait_foo_ref()); + } + + let mut some = Some(|x| x * x); + let arr = [Ok(1), Err(2)]; + let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); +} + +struct Thunk(Box T>); + +impl Thunk { + fn new T>(f: F) -> Thunk { + let mut option = Some(f); + // This should not trigger redundant_closure (#1439) + Thunk(Box::new(move || option.take().unwrap()())) + } + + fn unwrap(self) -> T { + let Thunk(mut f) = self; + f() + } +} + +fn foobar() { + let thunk = Thunk::new(|| println!("Hello, world!")); + thunk.unwrap() +} + +fn meta(f: F) +where + F: Fn(u8), +{ + f(1u8) +} + +fn foo(_: u8) {} + +fn foo2(_: u8) -> u8 { + 1u8 +} + +fn all(x: &[X], y: &X, f: F) -> bool +where + F: Fn(&X, &X) -> bool, +{ + x.iter().all(|e| f(e, y)) +} + +fn below(x: &u8, y: &u8) -> bool { + x < y +} + +unsafe fn unsafe_fn(_: u8) {} + +fn divergent(_: u8) -> ! { + unimplemented!() +} + +fn generic(_: T) -> u8 { + 0 +} + +fn passes_fn_mut(mut x: Box) { + requires_fn_once(|| x()); +} +fn requires_fn_once(_: T) {} + +fn test_redundant_closure_with_function_pointer() { + type FnPtrType = fn(u8); + let foo_ptr: FnPtrType = foo; + let a = Some(1u8).map(|a| foo_ptr(a)); +} + +fn test_redundant_closure_with_another_closure() { + let closure = |a| println!("{}", a); + let a = Some(1u8).map(|a| closure(a)); +} + +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 { + // Currently f is called when result of make_lazy is called. + // If the closure is removed, f will be called when make_lazy itself is + // called. This changes semantics, so the closure must stay. + Box::new(move |x| f()(x)) +} + +fn call String>(f: F) -> String { + f(&mut "Hello".to_owned()) +} +fn test_difference_in_mutability() { + call(|s| s.clone()); +} + +struct Bar; +impl std::ops::Deref for Bar { + type Target = str; + fn deref(&self) -> &str { + "hi" + } +} + +fn test_deref_with_trait_method() { + let _ = [Bar].iter().map(|s| s.to_string()).collect::>(); +} diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr new file mode 100644 index 0000000000000..c4713ca8083dd --- /dev/null +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -0,0 +1,80 @@ +error: redundant closure found + --> $DIR/eta.rs:20:27 + | +LL | let a = Some(1u8).map(|a| foo(a)); + | ^^^^^^^^^^ help: remove closure as shown: `foo` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + +error: redundant closure found + --> $DIR/eta.rs:21:10 + | +LL | meta(|a| foo(a)); + | ^^^^^^^^^^ help: remove closure as shown: `foo` + +error: this expression borrows a reference that is immediately dereferenced by the compiler + --> $DIR/eta.rs:24:21 + | +LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted + | ^^^ help: change this to: `&2` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + +error: redundant closure found + --> $DIR/eta.rs:31:27 + | +LL | let e = Some(1u8).map(|a| generic(a)); + | ^^^^^^^^^^^^^^ help: remove closure as shown: `generic` + +error: redundant closure found + --> $DIR/eta.rs:74:51 + | +LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); + | ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo` + | + = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` + +error: redundant closure found + --> $DIR/eta.rs:76:51 + | +LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); + | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo` + +error: redundant closure found + --> $DIR/eta.rs:79:42 + | +LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); + | ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear` + +error: redundant closure found + --> $DIR/eta.rs:84:29 + | +LL | let e = Some("str").map(|s| s.to_string()); + | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string` + +error: redundant closure found + --> $DIR/eta.rs:86:27 + | +LL | let e = Some('a').map(|s| s.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase` + +error: redundant closure found + --> $DIR/eta.rs:89:65 + | +LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` + +error: redundant closure found + --> $DIR/eta.rs:172:27 + | +LL | let a = Some(1u8).map(|a| foo_ptr(a)); + | ^^^^^^^^^^^^^^ help: remove closure as shown: `foo_ptr` + +error: redundant closure found + --> $DIR/eta.rs:177:27 + | +LL | let a = Some(1u8).map(|a| closure(a)); + | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/eval_order_dependence.rs b/src/tools/clippy/tests/ui/eval_order_dependence.rs new file mode 100644 index 0000000000000..d806bc6d40102 --- /dev/null +++ b/src/tools/clippy/tests/ui/eval_order_dependence.rs @@ -0,0 +1,109 @@ +#[warn(clippy::eval_order_dependence)] +#[allow( + unused_assignments, + unused_variables, + clippy::many_single_char_names, + clippy::no_effect, + dead_code, + clippy::blacklisted_name +)] +fn main() { + let mut x = 0; + let a = { + x = 1; + 1 + } + x; + + // Example from iss#277 + x += { + x = 20; + 2 + }; + + // Does it work in weird places? + // ...in the base for a struct expression? + struct Foo { + a: i32, + b: i32, + }; + let base = Foo { a: 4, b: 5 }; + let foo = Foo { + a: x, + ..{ + x = 6; + base + } + }; + // ...inside a closure? + let closure = || { + let mut x = 0; + x += { + x = 20; + 2 + }; + }; + // ...not across a closure? + let mut y = 0; + let b = (y, || y = 1); + + // && and || evaluate left-to-right. + let a = { + x = 1; + true + } && (x == 3); + let a = { + x = 1; + true + } || (x == 3); + + // Make sure we don't get confused by alpha conversion. + let a = { + let mut x = 1; + x = 2; + 1 + } + x; + + // No warning if we don't read the variable... + x = { + x = 20; + 2 + }; + // ...if the assignment is in a closure... + let b = { + || { + x = 1; + }; + 1 + } + x; + // ... or the access is under an address. + let b = ( + { + let p = &x; + 1 + }, + { + x = 1; + x + }, + ); + + // Limitation: l-values other than simple variables don't trigger + // the warning. + let mut tup = (0, 0); + let c = { + tup.0 = 1; + 1 + } + tup.0; + // Limitation: you can get away with a read under address-of. + let mut z = 0; + let b = ( + &{ + z = x; + x + }, + { + x = 3; + x + }, + ); +} diff --git a/src/tools/clippy/tests/ui/eval_order_dependence.stderr b/src/tools/clippy/tests/ui/eval_order_dependence.stderr new file mode 100644 index 0000000000000..8f4fa2228f7f4 --- /dev/null +++ b/src/tools/clippy/tests/ui/eval_order_dependence.stderr @@ -0,0 +1,51 @@ +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:15:9 + | +LL | } + x; + | ^ + | + = note: `-D clippy::eval-order-dependence` implied by `-D warnings` +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:13:9 + | +LL | x = 1; + | ^^^^^ + +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:18:5 + | +LL | x += { + | ^ + | +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:19:9 + | +LL | x = 20; + | ^^^^^^ + +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:31:12 + | +LL | a: x, + | ^ + | +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:33:13 + | +LL | x = 6; + | ^^^^^ + +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:40:9 + | +LL | x += { + | ^ + | +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:41:13 + | +LL | x = 20; + | ^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed new file mode 100644 index 0000000000000..bf0325fec7923 --- /dev/null +++ b/src/tools/clippy/tests/ui/excessive_precision.fixed @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::print_literal)] + +fn main() { + // Consts + const GOOD32: f32 = 0.123_456; + const GOOD32_SM: f32 = 0.000_000_000_1; + const GOOD32_DOT: f32 = 10_000_000_000.0; + const GOOD32_EDGE: f32 = 1.000_000_8; + const GOOD64: f64 = 0.123_456_789_012; + const GOOD64_SM: f32 = 0.000_000_000_000_000_1; + const GOOD64_DOT: f32 = 10_000_000_000_000_000.0; + + const BAD32_1: f32 = 0.123_456_79_f32; + const BAD32_2: f32 = 0.123_456_79; + const BAD32_3: f32 = 0.1; + const BAD32_EDGE: f32 = 1.000_001; + + const BAD64_1: f64 = 0.123_456_789_012_345_66_f64; + const BAD64_2: f64 = 0.123_456_789_012_345_66; + const BAD64_3: f64 = 0.1; + + // Literal as param + println!("{:?}", 8.888_888_888_888_89); + + // // TODO add inferred type tests for f32 + // Locals + let good32: f32 = 0.123_456_f32; + let good32_2: f32 = 0.123_456; + + let good64: f64 = 0.123_456_789_012; + let good64_suf: f64 = 0.123_456_789_012f64; + let good64_inf = 0.123_456_789_012; + + let bad32: f32 = 1.123_456_8; + let bad32_suf: f32 = 1.123_456_8_f32; + let bad32_inf = 1.123_456_8_f32; + + let bad64: f64 = 0.123_456_789_012_345_66; + let bad64_suf: f64 = 0.123_456_789_012_345_66_f64; + let bad64_inf = 0.123_456_789_012_345_66; + + // Vectors + let good_vec32: Vec = vec![0.123_456]; + let good_vec64: Vec = vec![0.123_456_789]; + + let bad_vec32: Vec = vec![0.123_456_79]; + let bad_vec64: Vec = vec![0.123_456_789_123_456_78]; + + // Exponential float notation + let good_e32: f32 = 1e-10; + let bad_e32: f32 = 1.123_456_8e-10; + + let good_bige32: f32 = 1E-10; + let bad_bige32: f32 = 1.123_456_8E-10; + + // Inferred type + let good_inferred: f32 = 1f32 * 1_000_000_000.; + + // issue #2840 + let num = 0.000_000_000_01e-10f64; +} diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs new file mode 100644 index 0000000000000..ce4722a90f900 --- /dev/null +++ b/src/tools/clippy/tests/ui/excessive_precision.rs @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::print_literal)] + +fn main() { + // Consts + const GOOD32: f32 = 0.123_456; + const GOOD32_SM: f32 = 0.000_000_000_1; + const GOOD32_DOT: f32 = 10_000_000_000.0; + const GOOD32_EDGE: f32 = 1.000_000_8; + const GOOD64: f64 = 0.123_456_789_012; + const GOOD64_SM: f32 = 0.000_000_000_000_000_1; + const GOOD64_DOT: f32 = 10_000_000_000_000_000.0; + + const BAD32_1: f32 = 0.123_456_789_f32; + const BAD32_2: f32 = 0.123_456_789; + const BAD32_3: f32 = 0.100_000_000_000_1; + const BAD32_EDGE: f32 = 1.000_000_9; + + const BAD64_1: f64 = 0.123_456_789_012_345_67f64; + const BAD64_2: f64 = 0.123_456_789_012_345_67; + const BAD64_3: f64 = 0.100_000_000_000_000_000_1; + + // Literal as param + println!("{:?}", 8.888_888_888_888_888_888_888); + + // // TODO add inferred type tests for f32 + // Locals + let good32: f32 = 0.123_456_f32; + let good32_2: f32 = 0.123_456; + + let good64: f64 = 0.123_456_789_012; + let good64_suf: f64 = 0.123_456_789_012f64; + let good64_inf = 0.123_456_789_012; + + let bad32: f32 = 1.123_456_789; + let bad32_suf: f32 = 1.123_456_789_f32; + let bad32_inf = 1.123_456_789_f32; + + let bad64: f64 = 0.123_456_789_012_345_67; + let bad64_suf: f64 = 0.123_456_789_012_345_67f64; + let bad64_inf = 0.123_456_789_012_345_67; + + // Vectors + let good_vec32: Vec = vec![0.123_456]; + let good_vec64: Vec = vec![0.123_456_789]; + + let bad_vec32: Vec = vec![0.123_456_789]; + let bad_vec64: Vec = vec![0.123_456_789_123_456_789]; + + // Exponential float notation + let good_e32: f32 = 1e-10; + let bad_e32: f32 = 1.123_456_788_888e-10; + + let good_bige32: f32 = 1E-10; + let bad_bige32: f32 = 1.123_456_788_888E-10; + + // Inferred type + let good_inferred: f32 = 1f32 * 1_000_000_000.; + + // issue #2840 + let num = 0.000_000_000_01e-10f64; +} diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr new file mode 100644 index 0000000000000..599773f2f70c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -0,0 +1,112 @@ +error: float has excessive precision + --> $DIR/excessive_precision.rs:15:26 + | +LL | const BAD32_1: f32 = 0.123_456_789_f32; + | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32` + | + = note: `-D clippy::excessive-precision` implied by `-D warnings` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:16:26 + | +LL | const BAD32_2: f32 = 0.123_456_789; + | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:17:26 + | +LL | const BAD32_3: f32 = 0.100_000_000_000_1; + | ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:18:29 + | +LL | const BAD32_EDGE: f32 = 1.000_000_9; + | ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:20:26 + | +LL | const BAD64_1: f64 = 0.123_456_789_012_345_67f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:21:26 + | +LL | const BAD64_2: f64 = 0.123_456_789_012_345_67; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:22:26 + | +LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:25:22 + | +LL | println!("{:?}", 8.888_888_888_888_888_888_888); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:36:22 + | +LL | let bad32: f32 = 1.123_456_789; + | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:37:26 + | +LL | let bad32_suf: f32 = 1.123_456_789_f32; + | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:38:21 + | +LL | let bad32_inf = 1.123_456_789_f32; + | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:40:22 + | +LL | let bad64: f64 = 0.123_456_789_012_345_67; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:41:26 + | +LL | let bad64_suf: f64 = 0.123_456_789_012_345_67f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:42:21 + | +LL | let bad64_inf = 0.123_456_789_012_345_67; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:48:36 + | +LL | let bad_vec32: Vec = vec![0.123_456_789]; + | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:49:36 + | +LL | let bad_vec64: Vec = vec![0.123_456_789_123_456_789]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:53:24 + | +LL | let bad_e32: f32 = 1.123_456_788_888e-10; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:56:27 + | +LL | let bad_bige32: f32 = 1.123_456_788_888E-10; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10` + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/exit1.rs b/src/tools/clippy/tests/ui/exit1.rs new file mode 100644 index 0000000000000..4eac6eb74672f --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1.rs @@ -0,0 +1,15 @@ +#[warn(clippy::exit)] + +fn not_main() { + if true { + std::process::exit(4); + } +} + +fn main() { + if true { + std::process::exit(2); + }; + not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit1.stderr b/src/tools/clippy/tests/ui/exit1.stderr new file mode 100644 index 0000000000000..a8d3956aa27a0 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit1.rs:5:9 + | +LL | std::process::exit(4); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/exit2.rs b/src/tools/clippy/tests/ui/exit2.rs new file mode 100644 index 0000000000000..4b693ed7083f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2.rs @@ -0,0 +1,13 @@ +#[warn(clippy::exit)] + +fn also_not_main() { + std::process::exit(3); +} + +fn main() { + if true { + std::process::exit(2); + }; + also_not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit2.stderr b/src/tools/clippy/tests/ui/exit2.stderr new file mode 100644 index 0000000000000..7263e156a9d26 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit2.rs:4:5 + | +LL | std::process::exit(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/exit3.rs b/src/tools/clippy/tests/ui/exit3.rs new file mode 100644 index 0000000000000..9dc0e1015a4f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit3.rs @@ -0,0 +1,8 @@ +#[warn(clippy::exit)] + +fn main() { + if true { + std::process::exit(2); + }; + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/expect.rs b/src/tools/clippy/tests/ui/expect.rs new file mode 100644 index 0000000000000..1073acf6f0cd6 --- /dev/null +++ b/src/tools/clippy/tests/ui/expect.rs @@ -0,0 +1,16 @@ +#![warn(clippy::expect_used)] + +fn expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +fn expect_result() { + let res: Result = Ok(0); + let _ = res.expect(""); +} + +fn main() { + expect_option(); + expect_result(); +} diff --git a/src/tools/clippy/tests/ui/expect.stderr b/src/tools/clippy/tests/ui/expect.stderr new file mode 100644 index 0000000000000..9d3fc7df15cc7 --- /dev/null +++ b/src/tools/clippy/tests/ui/expect.stderr @@ -0,0 +1,19 @@ +error: used `expect()` on `an Option` value + --> $DIR/expect.rs:5:13 + | +LL | let _ = opt.expect(""); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::expect-used` implied by `-D warnings` + = help: if this value is an `None`, it will panic + +error: used `expect()` on `a Result` value + --> $DIR/expect.rs:10:13 + | +LL | let _ = res.expect(""); + | ^^^^^^^^^^^^^^ + | + = help: if this value is an `Err`, it will panic + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/expect_fun_call.fixed b/src/tools/clippy/tests/ui/expect_fun_call.fixed new file mode 100644 index 0000000000000..f3d8a941a92bc --- /dev/null +++ b/src/tools/clippy/tests/ui/expect_fun_call.fixed @@ -0,0 +1,94 @@ +// run-rustfix + +#![warn(clippy::expect_fun_call)] + +/// Checks implementation of the `EXPECT_FUN_CALL` lint + +fn main() { + struct Foo; + + impl Foo { + fn new() -> Self { + Foo + } + + fn expect(&self, msg: &str) { + panic!("{}", msg) + } + } + + let with_some = Some("value"); + with_some.expect("error"); + + let with_none: Option = None; + with_none.expect("error"); + + let error_code = 123_i32; + let with_none_and_format: Option = None; + with_none_and_format.unwrap_or_else(|| panic!("Error {}: fake error", error_code)); + + let with_none_and_as_str: Option = None; + with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code)); + + let with_ok: Result<(), ()> = Ok(()); + with_ok.expect("error"); + + let with_err: Result<(), ()> = Err(()); + with_err.expect("error"); + + let error_code = 123_i32; + let with_err_and_format: Result<(), ()> = Err(()); + with_err_and_format.unwrap_or_else(|_| panic!("Error {}: fake error", error_code)); + + let with_err_and_as_str: Result<(), ()> = Err(()); + with_err_and_as_str.unwrap_or_else(|_| panic!("Error {}: fake error", error_code)); + + let with_dummy_type = Foo::new(); + with_dummy_type.expect("another test string"); + + let with_dummy_type_and_format = Foo::new(); + with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_dummy_type_and_as_str = Foo::new(); + with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + //Issue #2937 + Some("foo").unwrap_or_else(|| panic!("{} {}", 1, 2)); + + //Issue #2979 - this should not lint + { + let msg = "bar"; + Some("foo").expect(msg); + } + + { + fn get_string() -> String { + "foo".to_string() + } + + fn get_static_str() -> &'static str { + "foo" + } + + fn get_non_static_str(_: &u32) -> &str { + "foo" + } + + Some("foo").unwrap_or_else(|| { panic!(get_string()) }); + Some("foo").unwrap_or_else(|| { panic!(get_string()) }); + Some("foo").unwrap_or_else(|| { panic!(get_string()) }); + + Some("foo").unwrap_or_else(|| { panic!(get_static_str()) }); + Some("foo").unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) }); + } + + //Issue #3839 + Some(true).unwrap_or_else(|| panic!("key {}, {}", 1, 2)); + + //Issue #4912 - the receiver is a &Option + { + let opt = Some(1); + let opt_ref = &opt; + opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref)); + } +} diff --git a/src/tools/clippy/tests/ui/expect_fun_call.rs b/src/tools/clippy/tests/ui/expect_fun_call.rs new file mode 100644 index 0000000000000..60bbaa89d4282 --- /dev/null +++ b/src/tools/clippy/tests/ui/expect_fun_call.rs @@ -0,0 +1,94 @@ +// run-rustfix + +#![warn(clippy::expect_fun_call)] + +/// Checks implementation of the `EXPECT_FUN_CALL` lint + +fn main() { + struct Foo; + + impl Foo { + fn new() -> Self { + Foo + } + + fn expect(&self, msg: &str) { + panic!("{}", msg) + } + } + + let with_some = Some("value"); + with_some.expect("error"); + + let with_none: Option = None; + with_none.expect("error"); + + let error_code = 123_i32; + let with_none_and_format: Option = None; + with_none_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_none_and_as_str: Option = None; + with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + let with_ok: Result<(), ()> = Ok(()); + with_ok.expect("error"); + + let with_err: Result<(), ()> = Err(()); + with_err.expect("error"); + + let error_code = 123_i32; + let with_err_and_format: Result<(), ()> = Err(()); + with_err_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_err_and_as_str: Result<(), ()> = Err(()); + with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + let with_dummy_type = Foo::new(); + with_dummy_type.expect("another test string"); + + let with_dummy_type_and_format = Foo::new(); + with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_dummy_type_and_as_str = Foo::new(); + with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + //Issue #2937 + Some("foo").expect(format!("{} {}", 1, 2).as_ref()); + + //Issue #2979 - this should not lint + { + let msg = "bar"; + Some("foo").expect(msg); + } + + { + fn get_string() -> String { + "foo".to_string() + } + + fn get_static_str() -> &'static str { + "foo" + } + + fn get_non_static_str(_: &u32) -> &str { + "foo" + } + + Some("foo").expect(&get_string()); + Some("foo").expect(get_string().as_ref()); + Some("foo").expect(get_string().as_str()); + + Some("foo").expect(get_static_str()); + Some("foo").expect(get_non_static_str(&0)); + } + + //Issue #3839 + Some(true).expect(&format!("key {}, {}", 1, 2)); + + //Issue #4912 - the receiver is a &Option + { + let opt = Some(1); + let opt_ref = &opt; + opt_ref.expect(&format!("{:?}", opt_ref)); + } +} diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr new file mode 100644 index 0000000000000..a492e2df89d47 --- /dev/null +++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr @@ -0,0 +1,76 @@ +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:28:26 + | +LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` + | + = note: `-D clippy::expect-fun-call` implied by `-D warnings` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:31:26 + | +LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:41:25 + | +LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:44:25 + | +LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:56:17 + | +LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:77:21 + | +LL | Some("foo").expect(&get_string()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:78:21 + | +LL | Some("foo").expect(get_string().as_ref()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:79:21 + | +LL | Some("foo").expect(get_string().as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:81:21 + | +LL | Some("foo").expect(get_static_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_static_str()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:82:21 + | +LL | Some("foo").expect(get_non_static_str(&0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:86:16 + | +LL | Some(true).expect(&format!("key {}, {}", 1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:92:17 + | +LL | opt_ref.expect(&format!("{:?}", opt_ref)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs new file mode 100644 index 0000000000000..aa6ef162fe401 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs @@ -0,0 +1,147 @@ +#![warn(clippy::explicit_counter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3, 4]; + let mut _index = 0; + for _v in &vec { + _index += 1 + } + + let mut _index = 1; + _index = 0; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + for _v in &mut vec { + _index += 1; + } + + let mut _index = 0; + for _v in vec { + _index += 1; + } +} + +mod issue_1219 { + pub fn test() { + // should not trigger the lint because variable is used after the loop #473 + let vec = vec![1, 2, 3]; + let mut index = 0; + for _v in &vec { + index += 1 + } + println!("index: {}", index); + + // should not trigger the lint because the count is conditional #1219 + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + if ch == 'a' { + continue; + } + count += 1; + println!("{}", count); + } + + // should not trigger the lint because the count is conditional + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + if ch == 'a' { + count += 1; + } + println!("{}", count); + } + + // should trigger the lint because the count is not conditional + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + count += 1; + if ch == 'a' { + continue; + } + println!("{}", count); + } + + // should trigger the lint because the count is not conditional + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + count += 1; + for i in 0..2 { + let _ = 123; + } + println!("{}", count); + } + + // should not trigger the lint because the count is incremented multiple times + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + count += 1; + for i in 0..2 { + count += 1; + } + println!("{}", count); + } + } +} + +mod issue_3308 { + pub fn test() { + // should not trigger the lint because the count is incremented multiple times + let mut skips = 0; + let erasures = vec![]; + for i in 0..10 { + while erasures.contains(&(i + skips)) { + skips += 1; + } + println!("{}", skips); + } + + // should not trigger the lint because the count is incremented multiple times + let mut skips = 0; + for i in 0..10 { + let mut j = 0; + while j < 5 { + skips += 1; + j += 1; + } + println!("{}", skips); + } + + // should not trigger the lint because the count is incremented multiple times + let mut skips = 0; + for i in 0..10 { + for j in 0..5 { + skips += 1; + } + println!("{}", skips); + } + } +} + +mod issue_1670 { + pub fn test() { + let mut count = 0; + for _i in 3..10 { + count += 1; + } + } +} + +mod issue_4732 { + pub fn test() { + let slice = &[1, 2, 3]; + let mut index = 0; + + // should not trigger the lint because the count is used after the loop + for _v in slice { + index += 1 + } + let _closure = || println!("index: {}", index); + } +} diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr new file mode 100644 index 0000000000000..931af46efe663 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr @@ -0,0 +1,46 @@ +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:6:5 + | +LL | for _v in &vec { + | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` + | + = note: `-D clippy::explicit-counter-loop` implied by `-D warnings` + +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:12:5 + | +LL | for _v in &vec { + | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` + +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:17:5 + | +LL | for _v in &mut vec { + | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()` + +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:22:5 + | +LL | for _v in vec { + | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` + +error: the variable `count` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:61:9 + | +LL | for ch in text.chars() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` + +error: the variable `count` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:72:9 + | +LL | for ch in text.chars() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` + +error: the variable `count` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:130:9 + | +LL | for _i in 3..10 { + | ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/explicit_write.fixed b/src/tools/clippy/tests/ui/explicit_write.fixed new file mode 100644 index 0000000000000..692d2ca675f91 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write.fixed @@ -0,0 +1,51 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn(clippy::explicit_write)] + +fn stdout() -> String { + String::new() +} + +fn stderr() -> String { + String::new() +} + +fn main() { + // these should warn + { + use std::io::Write; + print!("test"); + eprint!("test"); + println!("test"); + eprintln!("test"); + print!("test"); + eprint!("test"); + + // including newlines + println!("test\ntest"); + eprintln!("test\ntest"); + } + // these should not warn, different destination + { + use std::fmt::Write; + let mut s = String::new(); + write!(s, "test").unwrap(); + write!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + write!(stdout(), "test").unwrap(); + write!(stderr(), "test").unwrap(); + writeln!(stdout(), "test").unwrap(); + writeln!(stderr(), "test").unwrap(); + stdout().write_fmt(format_args!("test")).unwrap(); + stderr().write_fmt(format_args!("test")).unwrap(); + } + // these should not warn, no unwrap + { + use std::io::Write; + std::io::stdout().write_fmt(format_args!("test")).expect("no stdout"); + std::io::stderr().write_fmt(format_args!("test")).expect("no stderr"); + } +} diff --git a/src/tools/clippy/tests/ui/explicit_write.rs b/src/tools/clippy/tests/ui/explicit_write.rs new file mode 100644 index 0000000000000..455c5ef55d05c --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write.rs @@ -0,0 +1,51 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn(clippy::explicit_write)] + +fn stdout() -> String { + String::new() +} + +fn stderr() -> String { + String::new() +} + +fn main() { + // these should warn + { + use std::io::Write; + write!(std::io::stdout(), "test").unwrap(); + write!(std::io::stderr(), "test").unwrap(); + writeln!(std::io::stdout(), "test").unwrap(); + writeln!(std::io::stderr(), "test").unwrap(); + std::io::stdout().write_fmt(format_args!("test")).unwrap(); + std::io::stderr().write_fmt(format_args!("test")).unwrap(); + + // including newlines + writeln!(std::io::stdout(), "test\ntest").unwrap(); + writeln!(std::io::stderr(), "test\ntest").unwrap(); + } + // these should not warn, different destination + { + use std::fmt::Write; + let mut s = String::new(); + write!(s, "test").unwrap(); + write!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + write!(stdout(), "test").unwrap(); + write!(stderr(), "test").unwrap(); + writeln!(stdout(), "test").unwrap(); + writeln!(stderr(), "test").unwrap(); + stdout().write_fmt(format_args!("test")).unwrap(); + stderr().write_fmt(format_args!("test")).unwrap(); + } + // these should not warn, no unwrap + { + use std::io::Write; + std::io::stdout().write_fmt(format_args!("test")).expect("no stdout"); + std::io::stderr().write_fmt(format_args!("test")).expect("no stderr"); + } +} diff --git a/src/tools/clippy/tests/ui/explicit_write.stderr b/src/tools/clippy/tests/ui/explicit_write.stderr new file mode 100644 index 0000000000000..9feef9c0dc844 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write.stderr @@ -0,0 +1,52 @@ +error: use of `write!(stdout(), ...).unwrap()` + --> $DIR/explicit_write.rs:17:9 + | +LL | write!(std::io::stdout(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` + | + = note: `-D clippy::explicit-write` implied by `-D warnings` + +error: use of `write!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:18:9 + | +LL | write!(std::io::stderr(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` + +error: use of `writeln!(stdout(), ...).unwrap()` + --> $DIR/explicit_write.rs:19:9 + | +LL | writeln!(std::io::stdout(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")` + +error: use of `writeln!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:20:9 + | +LL | writeln!(std::io::stderr(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")` + +error: use of `stdout().write_fmt(...).unwrap()` + --> $DIR/explicit_write.rs:21:9 + | +LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` + +error: use of `stderr().write_fmt(...).unwrap()` + --> $DIR/explicit_write.rs:22:9 + | +LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` + +error: use of `writeln!(stdout(), ...).unwrap()` + --> $DIR/explicit_write.rs:25:9 + | +LL | writeln!(std::io::stdout(), "test/ntest").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")` + +error: use of `writeln!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:26:9 + | +LL | writeln!(std::io::stderr(), "test/ntest").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs new file mode 100644 index 0000000000000..f21e8ef935bd0 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs @@ -0,0 +1,8 @@ +#![allow(unused_imports, clippy::blacklisted_name)] +#![warn(clippy::explicit_write)] + +fn main() { + use std::io::Write; + let bar = "bar"; + writeln!(std::io::stderr(), "foo {}", bar).unwrap(); +} diff --git a/src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr new file mode 100644 index 0000000000000..77cadb99bb551 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr @@ -0,0 +1,10 @@ +error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead + --> $DIR/explicit_write_non_rustfix.rs:7:5 + | +LL | writeln!(std::io::stderr(), "foo {}", bar).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::explicit-write` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs new file mode 100644 index 0000000000000..ddbf4e98c51ab --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs @@ -0,0 +1,69 @@ +#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)] +#![warn(clippy::extra_unused_lifetimes)] + +fn empty() {} + +fn used_lt<'a>(x: &'a u8) {} + +fn unused_lt<'a>(x: u8) {} + +fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { + // 'a is useless here since it's not directly bound +} + +fn lt_return<'a, 'b: 'a>(x: &'b u8) -> &'a u8 { + panic!() +} + +fn lt_return_only<'a>() -> &'a u8 { + panic!() +} + +fn unused_lt_blergh<'a>(x: Option>) {} + +trait Foo<'a> { + fn x(&self, a: &'a u8); +} + +impl<'a> Foo<'a> for u8 { + fn x(&self, a: &'a u8) {} +} + +struct Bar; + +impl Bar { + fn x<'a>(&self) {} +} + +// test for #489 (used lifetimes in bounds) +pub fn parse<'a, I: Iterator>(_it: &mut I) { + unimplemented!() +} +pub fn parse2<'a, I>(_it: &mut I) +where + I: Iterator, +{ + unimplemented!() +} + +struct X { + x: u32, +} + +impl X { + fn self_ref_with_lifetime<'a>(&'a self) {} + fn explicit_self_with_lifetime<'a>(self: &'a Self) {} +} + +// Methods implementing traits must have matching lifetimes +mod issue4291 { + trait BadTrait { + fn unused_lt<'a>(x: u8) {} + } + + impl BadTrait for () { + fn unused_lt<'a>(_x: u8) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr new file mode 100644 index 0000000000000..16bbb1c037d84 --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr @@ -0,0 +1,28 @@ +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:8:14 + | +LL | fn unused_lt<'a>(x: u8) {} + | ^^ + | + = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings` + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:10:25 + | +LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:35:10 + | +LL | fn x<'a>(&self) {} + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:61:22 + | +LL | fn unused_lt<'a>(x: u8) {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.rs b/src/tools/clippy/tests/ui/fallible_impl_from.rs new file mode 100644 index 0000000000000..679f4a7dc357d --- /dev/null +++ b/src/tools/clippy/tests/ui/fallible_impl_from.rs @@ -0,0 +1,76 @@ +#![deny(clippy::fallible_impl_from)] + +// docs example +struct Foo(i32); +impl From for Foo { + fn from(s: String) -> Self { + Foo(s.parse().unwrap()) + } +} + +struct Valid(Vec); + +impl<'a> From<&'a str> for Valid { + fn from(s: &'a str) -> Valid { + Valid(s.to_owned().into_bytes()) + } +} +impl From for Valid { + fn from(i: usize) -> Valid { + Valid(Vec::with_capacity(i)) + } +} + +struct Invalid; + +impl From for Invalid { + fn from(i: usize) -> Invalid { + if i != 42 { + panic!(); + } + Invalid + } +} + +impl From> for Invalid { + fn from(s: Option) -> Invalid { + let s = s.unwrap(); + if !s.is_empty() { + panic!(42); + } else if s.parse::().unwrap() != 42 { + panic!("{:?}", s); + } + Invalid + } +} + +trait ProjStrTrait { + type ProjString; +} +impl ProjStrTrait for Box { + type ProjString = String; +} +impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { + fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { + if s.parse::().ok().unwrap() != 42 { + panic!("{:?}", s); + } + Invalid + } +} + +struct Unreachable; + +impl From for Unreachable { + fn from(s: String) -> Unreachable { + if s.is_empty() { + return Unreachable; + } + match s.chars().next() { + Some(_) => Unreachable, + None => unreachable!(), // do not lint the unreachable macro + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.stderr b/src/tools/clippy/tests/ui/fallible_impl_from.stderr new file mode 100644 index 0000000000000..ab976b947b356 --- /dev/null +++ b/src/tools/clippy/tests/ui/fallible_impl_from.stderr @@ -0,0 +1,93 @@ +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:5:1 + | +LL | / impl From for Foo { +LL | | fn from(s: String) -> Self { +LL | | Foo(s.parse().unwrap()) +LL | | } +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/fallible_impl_from.rs:1:9 + | +LL | #![deny(clippy::fallible_impl_from)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:7:13 + | +LL | Foo(s.parse().unwrap()) + | ^^^^^^^^^^^^^^^^^^ + +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:26:1 + | +LL | / impl From for Invalid { +LL | | fn from(i: usize) -> Invalid { +LL | | if i != 42 { +LL | | panic!(); +... | +LL | | } +LL | | } + | |_^ + | + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:29:13 + | +LL | panic!(); + | ^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:35:1 + | +LL | / impl From> for Invalid { +LL | | fn from(s: Option) -> Invalid { +LL | | let s = s.unwrap(); +LL | | if !s.is_empty() { +... | +LL | | } +LL | | } + | |_^ + | + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:37:17 + | +LL | let s = s.unwrap(); + | ^^^^^^^^^^ +LL | if !s.is_empty() { +LL | panic!(42); + | ^^^^^^^^^^^ +LL | } else if s.parse::().unwrap() != 42 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | panic!("{:?}", s); + | ^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:53:1 + | +LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { +LL | | fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { +LL | | if s.parse::().ok().unwrap() != 42 { +LL | | panic!("{:?}", s); +... | +LL | | } +LL | | } + | |_^ + | + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:55:12 + | +LL | if s.parse::().ok().unwrap() != 42 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | panic!("{:?}", s); + | ^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/filetype_is_file.rs b/src/tools/clippy/tests/ui/filetype_is_file.rs new file mode 100644 index 0000000000000..5de8fe8cdd7b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/filetype_is_file.rs @@ -0,0 +1,23 @@ +#![warn(clippy::filetype_is_file)] + +fn main() -> std::io::Result<()> { + use std::fs; + use std::ops::BitOr; + + // !filetype.is_dir() + if fs::metadata("foo.txt")?.file_type().is_file() { + // read file + } + + // positive of filetype.is_dir() + if !fs::metadata("foo.txt")?.file_type().is_file() { + // handle dir + } + + // false positive of filetype.is_dir() + if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) { + // ... + } + + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/filetype_is_file.stderr b/src/tools/clippy/tests/ui/filetype_is_file.stderr new file mode 100644 index 0000000000000..cd1e3ac37fe86 --- /dev/null +++ b/src/tools/clippy/tests/ui/filetype_is_file.stderr @@ -0,0 +1,27 @@ +error: `FileType::is_file()` only covers regular files + --> $DIR/filetype_is_file.rs:8:8 + | +LL | if fs::metadata("foo.txt")?.file_type().is_file() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filetype-is-file` implied by `-D warnings` + = help: use `!FileType::is_dir()` instead + +error: `!FileType::is_file()` only denies regular files + --> $DIR/filetype_is_file.rs:13:8 + | +LL | if !fs::metadata("foo.txt")?.file_type().is_file() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `FileType::is_dir()` instead + +error: `FileType::is_file()` only covers regular files + --> $DIR/filetype_is_file.rs:18:9 + | +LL | if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `!FileType::is_dir()` instead + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_map_next.rs b/src/tools/clippy/tests/ui/filter_map_next.rs new file mode 100644 index 0000000000000..f5d051be198ed --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_next.rs @@ -0,0 +1,20 @@ +#![warn(clippy::all, clippy::pedantic)] + +fn main() { + let a = ["1", "lol", "3", "NaN", "5"]; + + let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + assert_eq!(element, Some(1)); + + #[rustfmt::skip] + let _: Option = vec![1, 2, 3, 4, 5, 6] + .into_iter() + .filter_map(|x| { + if x == 2 { + Some(x * 2) + } else { + None + } + }) + .next(); +} diff --git a/src/tools/clippy/tests/ui/filter_map_next.stderr b/src/tools/clippy/tests/ui/filter_map_next.stderr new file mode 100644 index 0000000000000..d69ae212414f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_next.stderr @@ -0,0 +1,24 @@ +error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. + --> $DIR/filter_map_next.rs:6:32 + | +LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-map-next` implied by `-D warnings` + = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())` + +error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. + --> $DIR/filter_map_next.rs:10:26 + | +LL | let _: Option = vec![1, 2, 3, 4, 5, 6] + | __________________________^ +LL | | .into_iter() +LL | | .filter_map(|x| { +LL | | if x == 2 { +... | +LL | | }) +LL | | .next(); + | |_______________^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_methods.rs b/src/tools/clippy/tests/ui/filter_methods.rs new file mode 100644 index 0000000000000..ef434245fd70c --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_methods.rs @@ -0,0 +1,24 @@ +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_docs_in_private_items)] + +fn main() { + let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .filter(|&x| x == 0) + .flat_map(|x| x.checked_mul(2)) + .collect(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .filter_map(|x| x.checked_mul(2)) + .flat_map(|x| x.checked_mul(2)) + .collect(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .filter_map(|x| x.checked_mul(2)) + .map(|x| x.checked_mul(2)) + .collect(); +} diff --git a/src/tools/clippy/tests/ui/filter_methods.stderr b/src/tools/clippy/tests/ui/filter_methods.stderr new file mode 100644 index 0000000000000..84a957a374c6b --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_methods.stderr @@ -0,0 +1,47 @@ +error: called `filter(p).map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:5:21 + | +LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-map` implied by `-D warnings` + = help: this is more succinctly expressed by calling `.filter_map(..)` instead + +error: called `filter(p).flat_map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:7:21 + | +LL | let _: Vec<_> = vec![5_i8; 6] + | _____________________^ +LL | | .into_iter() +LL | | .filter(|&x| x == 0) +LL | | .flat_map(|x| x.checked_mul(2)) + | |_______________________________________^ + | + = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` + +error: called `filter_map(p).flat_map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:13:21 + | +LL | let _: Vec<_> = vec![5_i8; 6] + | _____________________^ +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .flat_map(|x| x.checked_mul(2)) + | |_______________________________________^ + | + = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` + +error: called `filter_map(p).map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:19:21 + | +LL | let _: Vec<_> = vec![5_i8; 6] + | _____________________^ +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .map(|x| x.checked_mul(2)) + | |__________________________________^ + | + = help: this is more succinctly expressed by only calling `.filter_map(..)` instead + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/find_map.rs b/src/tools/clippy/tests/ui/find_map.rs new file mode 100644 index 0000000000000..c28cca144ca3f --- /dev/null +++ b/src/tools/clippy/tests/ui/find_map.rs @@ -0,0 +1,32 @@ +#![warn(clippy::all, clippy::pedantic)] + +#[derive(Debug, Copy, Clone)] +enum Flavor { + Chocolate, +} + +#[derive(Debug, Copy, Clone)] +enum Dessert { + Banana, + Pudding, + Cake(Flavor), +} + +fn main() { + let desserts_of_the_week = vec![Dessert::Banana, Dessert::Cake(Flavor::Chocolate), Dessert::Pudding]; + + let a = ["lol", "NaN", "2", "5", "Xunda"]; + + let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); + + let _: Option = desserts_of_the_week + .iter() + .find(|dessert| match *dessert { + Dessert::Cake(_) => true, + _ => false, + }) + .map(|dessert| match *dessert { + Dessert::Cake(ref flavor) => *flavor, + _ => unreachable!(), + }); +} diff --git a/src/tools/clippy/tests/ui/find_map.stderr b/src/tools/clippy/tests/ui/find_map.stderr new file mode 100644 index 0000000000000..92f40fe6f1fb2 --- /dev/null +++ b/src/tools/clippy/tests/ui/find_map.stderr @@ -0,0 +1,26 @@ +error: called `find(p).map(q)` on an `Iterator` + --> $DIR/find_map.rs:20:26 + | +LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::find-map` implied by `-D warnings` + = help: this is more succinctly expressed by calling `.find_map(..)` instead + +error: called `find(p).map(q)` on an `Iterator` + --> $DIR/find_map.rs:22:29 + | +LL | let _: Option = desserts_of_the_week + | _____________________________^ +LL | | .iter() +LL | | .find(|dessert| match *dessert { +LL | | Dessert::Cake(_) => true, +... | +LL | | _ => unreachable!(), +LL | | }); + | |__________^ + | + = help: this is more succinctly expressed by calling `.find_map(..)` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/float_arithmetic.rs b/src/tools/clippy/tests/ui/float_arithmetic.rs new file mode 100644 index 0000000000000..60fa7569eb9dd --- /dev/null +++ b/src/tools/clippy/tests/ui/float_arithmetic.rs @@ -0,0 +1,52 @@ +#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref +)] + +#[rustfmt::skip] +fn main() { + let mut f = 1.0f32; + + f * 2.0; + + 1.0 + f; + f * 2.0; + f / 2.0; + f - 2.0 * 4.2; + -f; + + f += 1.0; + f -= 1.0; + f *= 2.0; + f /= 2.0; +} + +// also warn about floating point arith with references involved + +pub fn float_arith_ref() { + 3.1_f32 + &1.2_f32; + &3.4_f32 + 1.5_f32; + &3.5_f32 + &1.3_f32; +} + +pub fn float_foo(f: &f32) -> f32 { + let a = 5.1; + a + f +} + +pub fn float_bar(f1: &f32, f2: &f32) -> f32 { + f1 + f2 +} + +pub fn float_baz(f1: f32, f2: &f32) -> f32 { + f1 + f2 +} + +pub fn float_qux(f1: f32, f2: f32) -> f32 { + (&f1 + &f2) +} diff --git a/src/tools/clippy/tests/ui/float_arithmetic.stderr b/src/tools/clippy/tests/ui/float_arithmetic.stderr new file mode 100644 index 0000000000000..1ceffb35beede --- /dev/null +++ b/src/tools/clippy/tests/ui/float_arithmetic.stderr @@ -0,0 +1,106 @@ +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:15:5 + | +LL | f * 2.0; + | ^^^^^^^ + | + = note: `-D clippy::float-arithmetic` implied by `-D warnings` + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:17:5 + | +LL | 1.0 + f; + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:18:5 + | +LL | f * 2.0; + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:19:5 + | +LL | f / 2.0; + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:20:5 + | +LL | f - 2.0 * 4.2; + | ^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:21:5 + | +LL | -f; + | ^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:23:5 + | +LL | f += 1.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:24:5 + | +LL | f -= 1.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:25:5 + | +LL | f *= 2.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:26:5 + | +LL | f /= 2.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:32:5 + | +LL | 3.1_f32 + &1.2_f32; + | ^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:33:5 + | +LL | &3.4_f32 + 1.5_f32; + | ^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:34:5 + | +LL | &3.5_f32 + &1.3_f32; + | ^^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:39:5 + | +LL | a + f + | ^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:43:5 + | +LL | f1 + f2 + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:47:5 + | +LL | f1 + f2 + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:51:5 + | +LL | (&f1 + &f2) + | ^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs new file mode 100644 index 0000000000000..9fa0e5f5c079b --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp.rs @@ -0,0 +1,119 @@ +#![warn(clippy::float_cmp)] +#![allow( + unused, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::cast_lossless, + clippy::many_single_char_names +)] + +use std::ops::Add; + +const ZERO: f32 = 0.0; +const ONE: f32 = ZERO + 1.0; + +fn twice(x: T) -> T +where + T: Add + Copy, +{ + x + x +} + +fn eq_fl(x: f32, y: f32) -> bool { + if x.is_nan() { + y.is_nan() + } else { + x == y + } // no error, inside "eq" fn +} + +fn fl_eq(x: f32, y: f32) -> bool { + if x.is_nan() { + y.is_nan() + } else { + x == y + } // no error, inside "eq" fn +} + +struct X { + val: f32, +} + +impl PartialEq for X { + fn eq(&self, o: &X) -> bool { + if self.val.is_nan() { + o.val.is_nan() + } else { + self.val == o.val // no error, inside "eq" fn + } + } +} + +fn main() { + ZERO == 0f32; //no error, comparison with zero is ok + 1.0f32 != f32::INFINITY; // also comparison with infinity + 1.0f32 != f32::NEG_INFINITY; // and negative infinity + ZERO == 0.0; //no error, comparison with zero is ok + ZERO + ZERO != 1.0; //no error, comparison with zero is ok + + ONE == 1f32; + ONE == 1.0 + 0.0; + ONE + ONE == ZERO + ONE + ONE; + ONE != 2.0; + ONE != 0.0; // no error, comparison with zero is ok + twice(ONE) != ONE; + ONE as f64 != 2.0; + ONE as f64 != 0.0; // no error, comparison with zero is ok + + let x: f64 = 1.0; + + x == 1.0; + x != 0f64; // no error, comparison with zero is ok + + twice(x) != twice(ONE as f64); + + x < 0.0; // no errors, lower or greater comparisons need no fuzzyness + x > 0.0; + x <= 0.0; + x >= 0.0; + + let xs: [f32; 1] = [0.0]; + let a: *const f32 = xs.as_ptr(); + let b: *const f32 = xs.as_ptr(); + + assert_eq!(a, b); // no errors + + const ZERO_ARRAY: [f32; 2] = [0.0, 0.0]; + const NON_ZERO_ARRAY: [f32; 2] = [0.0, 0.1]; + + let i = 0; + let j = 1; + + ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i + NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; + + let a1: [f32; 1] = [0.0]; + let a2: [f32; 1] = [1.1]; + + a1 == a2; + a1[0] == a2[0]; + + // no errors - comparing signums is ok + let x32 = 3.21f32; + 1.23f32.signum() == x32.signum(); + 1.23f32.signum() == -(x32.signum()); + 1.23f32.signum() == 3.21f32.signum(); + + 1.23f32.signum() != x32.signum(); + 1.23f32.signum() != -(x32.signum()); + 1.23f32.signum() != 3.21f32.signum(); + + let x64 = 3.21f64; + 1.23f64.signum() == x64.signum(); + 1.23f64.signum() == -(x64.signum()); + 1.23f64.signum() == 3.21f64.signum(); + + 1.23f64.signum() != x64.signum(); + 1.23f64.signum() != -(x64.signum()); + 1.23f64.signum() != 3.21f64.signum(); +} diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr new file mode 100644 index 0000000000000..2d454e8e70de5 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp.stderr @@ -0,0 +1,51 @@ +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:65:5 + | +LL | ONE as f64 != 2.0; + | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error` + | + = note: `-D clippy::float-cmp` implied by `-D warnings` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:70:5 + | +LL | x == 1.0; + | ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:73:5 + | +LL | twice(x) != twice(ONE as f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:93:5 + | +LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` arrays + --> $DIR/float_cmp.rs:98:5 + | +LL | a1 == a2; + | ^^^^^^^^ + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:99:5 + | +LL | a1[0] == a2[0]; + | ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/float_cmp_const.rs b/src/tools/clippy/tests/ui/float_cmp_const.rs new file mode 100644 index 0000000000000..dfc025558a2f4 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp_const.rs @@ -0,0 +1,62 @@ +// does not test any rustfixable lints + +#![warn(clippy::float_cmp_const)] +#![allow(clippy::float_cmp)] +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] + +const ONE: f32 = 1.0; +const TWO: f32 = 2.0; + +fn eq_one(x: f32) -> bool { + if x.is_nan() { + false + } else { + x == ONE + } // no error, inside "eq" fn +} + +fn main() { + // has errors + 1f32 == ONE; + TWO == ONE; + TWO != ONE; + ONE + ONE == TWO; + let x = 1; + x as f32 == ONE; + + let v = 0.9; + v == ONE; + v != ONE; + + // no errors, lower than or greater than comparisons + v < ONE; + v > ONE; + v <= ONE; + v >= ONE; + + // no errors, zero and infinity values + ONE != 0f32; + TWO == 0f32; + ONE != f32::INFINITY; + ONE == f32::NEG_INFINITY; + + // no errors, but will warn clippy::float_cmp if '#![allow(float_cmp)]' above is removed + let w = 1.1; + v == w; + v != w; + v == 1.0; + v != 1.0; + + const ZERO_ARRAY: [f32; 3] = [0.0, 0.0, 0.0]; + const ZERO_INF_ARRAY: [f32; 3] = [0.0, ::std::f32::INFINITY, ::std::f32::NEG_INFINITY]; + const NON_ZERO_ARRAY: [f32; 3] = [0.0, 0.1, 0.2]; + const NON_ZERO_ARRAY2: [f32; 3] = [0.2, 0.1, 0.0]; + + // no errors, zero and infinity values + NON_ZERO_ARRAY[0] == NON_ZERO_ARRAY2[1]; // lhs is 0.0 + ZERO_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros + ZERO_INF_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros or infinities + + // has errors + NON_ZERO_ARRAY == NON_ZERO_ARRAY2; +} diff --git a/src/tools/clippy/tests/ui/float_cmp_const.stderr b/src/tools/clippy/tests/ui/float_cmp_const.stderr new file mode 100644 index 0000000000000..19dc4a284b726 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp_const.stderr @@ -0,0 +1,67 @@ +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:20:5 + | +LL | 1f32 == ONE; + | ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error` + | + = note: `-D clippy::float-cmp-const` implied by `-D warnings` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:21:5 + | +LL | TWO == ONE; + | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:22:5 + | +LL | TWO != ONE; + | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:23:5 + | +LL | ONE + ONE == TWO; + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:25:5 + | +LL | x as f32 == ONE; + | ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:28:5 + | +LL | v == ONE; + | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:29:5 + | +LL | v != ONE; + | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant arrays + --> $DIR/float_cmp_const.rs:61:5 + | +LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_abs.fixed b/src/tools/clippy/tests/ui/floating_point_abs.fixed new file mode 100644 index 0000000000000..b623e4988e7d4 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_abs.fixed @@ -0,0 +1,98 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +struct A { + a: f64, + b: f64, +} + +fn fake_abs1(num: f64) -> f64 { + num.abs() +} + +fn fake_abs2(num: f64) -> f64 { + num.abs() +} + +fn fake_abs3(a: A) -> f64 { + a.a.abs() +} + +fn fake_abs4(num: f64) -> f64 { + num.abs() +} + +fn fake_abs5(a: A) -> f64 { + a.a.abs() +} + +fn fake_nabs1(num: f64) -> f64 { + -num.abs() +} + +fn fake_nabs2(num: f64) -> f64 { + -num.abs() +} + +fn fake_nabs3(a: A) -> A { + A { + a: -a.a.abs(), + b: a.b, + } +} + +fn not_fake_abs1(num: f64) -> f64 { + if num > 0.0 { + num + } else { + -num - 1f64 + } +} + +fn not_fake_abs2(num: f64) -> f64 { + if num > 0.0 { + num + 1.0 + } else { + -(num + 1.0) + } +} + +fn not_fake_abs3(num1: f64, num2: f64) -> f64 { + if num1 > 0.0 { + num2 + } else { + -num2 + } +} + +fn not_fake_abs4(a: A) -> f64 { + if a.a > 0.0 { + a.b + } else { + -a.b + } +} + +fn not_fake_abs5(a: A) -> f64 { + if a.a > 0.0 { + a.a + } else { + -a.b + } +} + +fn main() { + fake_abs1(5.0); + fake_abs2(5.0); + fake_abs3(A { a: 5.0, b: 5.0 }); + fake_abs4(5.0); + fake_abs5(A { a: 5.0, b: 5.0 }); + fake_nabs1(5.0); + fake_nabs2(5.0); + fake_nabs3(A { a: 5.0, b: 5.0 }); + not_fake_abs1(5.0); + not_fake_abs2(5.0); + not_fake_abs3(5.0, 5.0); + not_fake_abs4(A { a: 5.0, b: 5.0 }); + not_fake_abs5(A { a: 5.0, b: 5.0 }); +} diff --git a/src/tools/clippy/tests/ui/floating_point_abs.rs b/src/tools/clippy/tests/ui/floating_point_abs.rs new file mode 100644 index 0000000000000..cbf9c94e41e6a --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_abs.rs @@ -0,0 +1,126 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +struct A { + a: f64, + b: f64, +} + +fn fake_abs1(num: f64) -> f64 { + if num >= 0.0 { + num + } else { + -num + } +} + +fn fake_abs2(num: f64) -> f64 { + if 0.0 < num { + num + } else { + -num + } +} + +fn fake_abs3(a: A) -> f64 { + if a.a > 0.0 { + a.a + } else { + -a.a + } +} + +fn fake_abs4(num: f64) -> f64 { + if 0.0 >= num { + -num + } else { + num + } +} + +fn fake_abs5(a: A) -> f64 { + if a.a < 0.0 { + -a.a + } else { + a.a + } +} + +fn fake_nabs1(num: f64) -> f64 { + if num < 0.0 { + num + } else { + -num + } +} + +fn fake_nabs2(num: f64) -> f64 { + if 0.0 >= num { + num + } else { + -num + } +} + +fn fake_nabs3(a: A) -> A { + A { + a: if a.a >= 0.0 { -a.a } else { a.a }, + b: a.b, + } +} + +fn not_fake_abs1(num: f64) -> f64 { + if num > 0.0 { + num + } else { + -num - 1f64 + } +} + +fn not_fake_abs2(num: f64) -> f64 { + if num > 0.0 { + num + 1.0 + } else { + -(num + 1.0) + } +} + +fn not_fake_abs3(num1: f64, num2: f64) -> f64 { + if num1 > 0.0 { + num2 + } else { + -num2 + } +} + +fn not_fake_abs4(a: A) -> f64 { + if a.a > 0.0 { + a.b + } else { + -a.b + } +} + +fn not_fake_abs5(a: A) -> f64 { + if a.a > 0.0 { + a.a + } else { + -a.b + } +} + +fn main() { + fake_abs1(5.0); + fake_abs2(5.0); + fake_abs3(A { a: 5.0, b: 5.0 }); + fake_abs4(5.0); + fake_abs5(A { a: 5.0, b: 5.0 }); + fake_nabs1(5.0); + fake_nabs2(5.0); + fake_nabs3(A { a: 5.0, b: 5.0 }); + not_fake_abs1(5.0); + not_fake_abs2(5.0); + not_fake_abs3(5.0, 5.0); + not_fake_abs4(A { a: 5.0, b: 5.0 }); + not_fake_abs5(A { a: 5.0, b: 5.0 }); +} diff --git a/src/tools/clippy/tests/ui/floating_point_abs.stderr b/src/tools/clippy/tests/ui/floating_point_abs.stderr new file mode 100644 index 0000000000000..74a71f2ca7c57 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_abs.stderr @@ -0,0 +1,80 @@ +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:10:5 + | +LL | / if num >= 0.0 { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `num.abs()` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:18:5 + | +LL | / if 0.0 < num { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `num.abs()` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:26:5 + | +LL | / if a.a > 0.0 { +LL | | a.a +LL | | } else { +LL | | -a.a +LL | | } + | |_____^ help: try: `a.a.abs()` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:34:5 + | +LL | / if 0.0 >= num { +LL | | -num +LL | | } else { +LL | | num +LL | | } + | |_____^ help: try: `num.abs()` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:42:5 + | +LL | / if a.a < 0.0 { +LL | | -a.a +LL | | } else { +LL | | a.a +LL | | } + | |_____^ help: try: `a.a.abs()` + +error: manual implementation of negation of `abs` method + --> $DIR/floating_point_abs.rs:50:5 + | +LL | / if num < 0.0 { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `-num.abs()` + +error: manual implementation of negation of `abs` method + --> $DIR/floating_point_abs.rs:58:5 + | +LL | / if 0.0 >= num { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `-num.abs()` + +error: manual implementation of negation of `abs` method + --> $DIR/floating_point_abs.rs:67:12 + | +LL | a: if a.a >= 0.0 { -a.a } else { a.a }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_exp.fixed b/src/tools/clippy/tests/ui/floating_point_exp.fixed new file mode 100644 index 0000000000000..ae7805fdf018e --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_exp.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 2f32; + let _ = x.exp_m1(); + let _ = x.exp_m1() + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; + + let x = 2f64; + let _ = x.exp_m1(); + let _ = x.exp_m1() + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; +} diff --git a/src/tools/clippy/tests/ui/floating_point_exp.rs b/src/tools/clippy/tests/ui/floating_point_exp.rs new file mode 100644 index 0000000000000..27e0b9bcbc937 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_exp.rs @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 2f32; + let _ = x.exp() - 1.0; + let _ = x.exp() - 1.0 + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; + + let x = 2f64; + let _ = x.exp() - 1.0; + let _ = x.exp() - 1.0 + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; +} diff --git a/src/tools/clippy/tests/ui/floating_point_exp.stderr b/src/tools/clippy/tests/ui/floating_point_exp.stderr new file mode 100644 index 0000000000000..5cd999ad47cdd --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_exp.stderr @@ -0,0 +1,28 @@ +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:6:13 + | +LL | let _ = x.exp() - 1.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:7:13 + | +LL | let _ = x.exp() - 1.0 + 2.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:13:13 + | +LL | let _ = x.exp() - 1.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:14:13 + | +LL | let _ = x.exp() - 1.0 + 2.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed new file mode 100644 index 0000000000000..42c5e5d2bae24 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_log.fixed @@ -0,0 +1,58 @@ +// run-rustfix +#![allow(dead_code, clippy::double_parens)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +const TWO: f32 = 2.0; +const E: f32 = std::f32::consts::E; + +fn check_log_base() { + let x = 1f32; + let _ = x.log2(); + let _ = x.log10(); + let _ = x.ln(); + let _ = x.log2(); + let _ = x.ln(); + + let x = 1f64; + let _ = x.log2(); + let _ = x.log10(); + let _ = x.ln(); +} + +fn check_ln1p() { + let x = 1f32; + let _ = 2.0f32.ln_1p(); + let _ = 2.0f32.ln_1p(); + let _ = x.ln_1p(); + let _ = (x / 2.0).ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = (x.powi(2) / 2.0).ln_1p(); + let _ = ((std::f32::consts::E - 1.0)).ln_1p(); + let _ = x.ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = (x + 2.0).ln_1p(); + let _ = (x / 2.0).ln_1p(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); + + let x = 1f64; + let _ = 2.0f64.ln_1p(); + let _ = 2.0f64.ln_1p(); + let _ = x.ln_1p(); + let _ = (x / 2.0).ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = x.ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = (x + 2.0).ln_1p(); + let _ = (x / 2.0).ln_1p(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs new file mode 100644 index 0000000000000..8be0d9ad56fc3 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_log.rs @@ -0,0 +1,58 @@ +// run-rustfix +#![allow(dead_code, clippy::double_parens)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +const TWO: f32 = 2.0; +const E: f32 = std::f32::consts::E; + +fn check_log_base() { + let x = 1f32; + let _ = x.log(2f32); + let _ = x.log(10f32); + let _ = x.log(std::f32::consts::E); + let _ = x.log(TWO); + let _ = x.log(E); + + let x = 1f64; + let _ = x.log(2f64); + let _ = x.log(10f64); + let _ = x.log(std::f64::consts::E); +} + +fn check_ln1p() { + let x = 1f32; + let _ = (1f32 + 2.).ln(); + let _ = (1f32 + 2.0).ln(); + let _ = (1.0 + x).ln(); + let _ = (1.0 + x / 2.0).ln(); + let _ = (1.0 + x.powi(2)).ln(); + let _ = (1.0 + x.powi(2) / 2.0).ln(); + let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); + let _ = (x + 1.0).ln(); + let _ = (x.powi(2) + 1.0).ln(); + let _ = (x + 2.0 + 1.0).ln(); + let _ = (x / 2.0 + 1.0).ln(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); + + let x = 1f64; + let _ = (1f64 + 2.).ln(); + let _ = (1f64 + 2.0).ln(); + let _ = (1.0 + x).ln(); + let _ = (1.0 + x / 2.0).ln(); + let _ = (1.0 + x.powi(2)).ln(); + let _ = (x + 1.0).ln(); + let _ = (x.powi(2) + 1.0).ln(); + let _ = (x + 2.0 + 1.0).ln(); + let _ = (x / 2.0 + 1.0).ln(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/floating_point_log.stderr b/src/tools/clippy/tests/ui/floating_point_log.stderr new file mode 100644 index 0000000000000..943fbdb0b8323 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_log.stderr @@ -0,0 +1,174 @@ +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:10:13 + | +LL | let _ = x.log(2f32); + | ^^^^^^^^^^^ help: consider using: `x.log2()` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:11:13 + | +LL | let _ = x.log(10f32); + | ^^^^^^^^^^^^ help: consider using: `x.log10()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:12:13 + | +LL | let _ = x.log(std::f32::consts::E); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:13:13 + | +LL | let _ = x.log(TWO); + | ^^^^^^^^^^ help: consider using: `x.log2()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:14:13 + | +LL | let _ = x.log(E); + | ^^^^^^^^ help: consider using: `x.ln()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:17:13 + | +LL | let _ = x.log(2f64); + | ^^^^^^^^^^^ help: consider using: `x.log2()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:18:13 + | +LL | let _ = x.log(10f64); + | ^^^^^^^^^^^^ help: consider using: `x.log10()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:19:13 + | +LL | let _ = x.log(std::f64::consts::E); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:24:13 + | +LL | let _ = (1f32 + 2.).ln(); + | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:25:13 + | +LL | let _ = (1f32 + 2.0).ln(); + | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:26:13 + | +LL | let _ = (1.0 + x).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:27:13 + | +LL | let _ = (1.0 + x / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:28:13 + | +LL | let _ = (1.0 + x.powi(2)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:29:13 + | +LL | let _ = (1.0 + x.powi(2) / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:30:13 + | +LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `((std::f32::consts::E - 1.0)).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:31:13 + | +LL | let _ = (x + 1.0).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:32:13 + | +LL | let _ = (x.powi(2) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:33:13 + | +LL | let _ = (x + 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:34:13 + | +LL | let _ = (x / 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:42:13 + | +LL | let _ = (1f64 + 2.).ln(); + | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:43:13 + | +LL | let _ = (1f64 + 2.0).ln(); + | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:44:13 + | +LL | let _ = (1.0 + x).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:45:13 + | +LL | let _ = (1.0 + x / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:46:13 + | +LL | let _ = (1.0 + x.powi(2)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:47:13 + | +LL | let _ = (x + 1.0).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:48:13 + | +LL | let _ = (x.powi(2) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:49:13 + | +LL | let _ = (x + 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:50:13 + | +LL | let _ = (x / 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed new file mode 100644 index 0000000000000..e343c37740da5 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed @@ -0,0 +1,21 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let a: f64 = 1234.567; + let b: f64 = 45.67834; + let c: f64 = 0.0004; + let d: f64 = 0.0001; + + let _ = a.mul_add(b, c); + let _ = a.mul_add(b, c); + let _ = 2.0f64.mul_add(4.0, a); + let _ = 2.0f64.mul_add(4., a); + + let _ = a.mul_add(b, c); + let _ = a.mul_add(b, c); + let _ = (a * b).mul_add(c, d); + + let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c; + let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64); +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs new file mode 100644 index 0000000000000..810f929c8568b --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs @@ -0,0 +1,21 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let a: f64 = 1234.567; + let b: f64 = 45.67834; + let c: f64 = 0.0004; + let d: f64 = 0.0001; + + let _ = a * b + c; + let _ = c + a * b; + let _ = a + 2.0 * 4.0; + let _ = a + 2. * 4.; + + let _ = (a * b) + c; + let _ = c + (a * b); + let _ = a * b * c + d; + + let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; + let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr new file mode 100644 index 0000000000000..2dfbf562d15fc --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr @@ -0,0 +1,58 @@ +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:10:13 + | +LL | let _ = a * b + c; + | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:11:13 + | +LL | let _ = c + a * b; + | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:12:13 + | +LL | let _ = a + 2.0 * 4.0; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:13:13 + | +LL | let _ = a + 2. * 4.; + | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:15:13 + | +LL | let _ = (a * b) + c; + | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:16:13 + | +LL | let _ = c + (a * b); + | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:17:13 + | +LL | let _ = a * b * c + d; + | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:19:13 + | +LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:20:13 + | +LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_powf.fixed b/src/tools/clippy/tests/ui/floating_point_powf.fixed new file mode 100644 index 0000000000000..78a9d44829bb1 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_powf.fixed @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x.exp2(); + let _ = 3.1f32.exp2(); + let _ = (-3.1f32).exp2(); + let _ = x.exp(); + let _ = 3.1f32.exp(); + let _ = (-3.1f32).exp(); + let _ = x.sqrt(); + let _ = x.cbrt(); + let _ = x.powi(2); + let _ = x.powi(-2); + let _ = x.powi(16_777_215); + let _ = x.powi(-16_777_215); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(16_777_216.0); + let _ = x.powf(-16_777_216.0); + + let x = 3f64; + let _ = x.exp2(); + let _ = 3.1f64.exp2(); + let _ = (-3.1f64).exp2(); + let _ = x.exp(); + let _ = 3.1f64.exp(); + let _ = (-3.1f64).exp(); + let _ = x.sqrt(); + let _ = x.cbrt(); + let _ = x.powi(2); + let _ = x.powi(-2); + let _ = x.powi(-2_147_483_648); + let _ = x.powi(2_147_483_647); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(-2_147_483_649.0); + let _ = x.powf(2_147_483_648.0); +} diff --git a/src/tools/clippy/tests/ui/floating_point_powf.rs b/src/tools/clippy/tests/ui/floating_point_powf.rs new file mode 100644 index 0000000000000..dbc1cac5cb431 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_powf.rs @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = 2f32.powf(x); + let _ = 2f32.powf(3.1); + let _ = 2f32.powf(-3.1); + let _ = std::f32::consts::E.powf(x); + let _ = std::f32::consts::E.powf(3.1); + let _ = std::f32::consts::E.powf(-3.1); + let _ = x.powf(1.0 / 2.0); + let _ = x.powf(1.0 / 3.0); + let _ = x.powf(2.0); + let _ = x.powf(-2.0); + let _ = x.powf(16_777_215.0); + let _ = x.powf(-16_777_215.0); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(16_777_216.0); + let _ = x.powf(-16_777_216.0); + + let x = 3f64; + let _ = 2f64.powf(x); + let _ = 2f64.powf(3.1); + let _ = 2f64.powf(-3.1); + let _ = std::f64::consts::E.powf(x); + let _ = std::f64::consts::E.powf(3.1); + let _ = std::f64::consts::E.powf(-3.1); + let _ = x.powf(1.0 / 2.0); + let _ = x.powf(1.0 / 3.0); + let _ = x.powf(2.0); + let _ = x.powf(-2.0); + let _ = x.powf(-2_147_483_648.0); + let _ = x.powf(2_147_483_647.0); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(-2_147_483_649.0); + let _ = x.powf(2_147_483_648.0); +} diff --git a/src/tools/clippy/tests/ui/floating_point_powf.stderr b/src/tools/clippy/tests/ui/floating_point_powf.stderr new file mode 100644 index 0000000000000..ad5163f0079be --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_powf.stderr @@ -0,0 +1,150 @@ +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:6:13 + | +LL | let _ = 2f32.powf(x); + | ^^^^^^^^^^^^ help: consider using: `x.exp2()` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:7:13 + | +LL | let _ = 2f32.powf(3.1); + | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:8:13 + | +LL | let _ = 2f32.powf(-3.1); + | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:9:13 + | +LL | let _ = std::f32::consts::E.powf(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:10:13 + | +LL | let _ = std::f32::consts::E.powf(3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:11:13 + | +LL | let _ = std::f32::consts::E.powf(-3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()` + +error: square-root of a number can be computed more efficiently and accurately + --> $DIR/floating_point_powf.rs:12:13 + | +LL | let _ = x.powf(1.0 / 2.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` + +error: cube-root of a number can be computed more accurately + --> $DIR/floating_point_powf.rs:13:13 + | +LL | let _ = x.powf(1.0 / 3.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:14:13 + | +LL | let _ = x.powf(2.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:15:13 + | +LL | let _ = x.powf(-2.0); + | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:16:13 + | +LL | let _ = x.powf(16_777_215.0); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:17:13 + | +LL | let _ = x.powf(-16_777_215.0); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:25:13 + | +LL | let _ = 2f64.powf(x); + | ^^^^^^^^^^^^ help: consider using: `x.exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:26:13 + | +LL | let _ = 2f64.powf(3.1); + | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:27:13 + | +LL | let _ = 2f64.powf(-3.1); + | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:28:13 + | +LL | let _ = std::f64::consts::E.powf(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:29:13 + | +LL | let _ = std::f64::consts::E.powf(3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:30:13 + | +LL | let _ = std::f64::consts::E.powf(-3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` + +error: square-root of a number can be computed more efficiently and accurately + --> $DIR/floating_point_powf.rs:31:13 + | +LL | let _ = x.powf(1.0 / 2.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` + +error: cube-root of a number can be computed more accurately + --> $DIR/floating_point_powf.rs:32:13 + | +LL | let _ = x.powf(1.0 / 3.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:33:13 + | +LL | let _ = x.powf(2.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:34:13 + | +LL | let _ = x.powf(-2.0); + | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:35:13 + | +LL | let _ = x.powf(-2_147_483_648.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:36:13 + | +LL | let _ = x.powf(2_147_483_647.0); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.rs b/src/tools/clippy/tests/ui/fn_address_comparisons.rs new file mode 100644 index 0000000000000..362dcb4fd80ca --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_address_comparisons.rs @@ -0,0 +1,20 @@ +use std::fmt::Debug; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; + +fn a() {} + +#[warn(clippy::fn_address_comparisons)] +fn main() { + type F = fn(); + let f: F = a; + let g: F = f; + + // These should fail: + let _ = f == a; + let _ = f != a; + + // These should be fine: + let _ = f == g; +} diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.stderr b/src/tools/clippy/tests/ui/fn_address_comparisons.stderr new file mode 100644 index 0000000000000..9c1b5419a4319 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_address_comparisons.stderr @@ -0,0 +1,16 @@ +error: comparing with a non-unique address of a function item + --> $DIR/fn_address_comparisons.rs:15:13 + | +LL | let _ = f == a; + | ^^^^^^ + | + = note: `-D clippy::fn-address-comparisons` implied by `-D warnings` + +error: comparing with a non-unique address of a function item + --> $DIR/fn_address_comparisons.rs:16:13 + | +LL | let _ = f != a; + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs new file mode 100644 index 0000000000000..7d6fd607e6545 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs @@ -0,0 +1,44 @@ +#![warn(clippy::fn_params_excessive_bools)] + +extern "C" { + fn f(_: bool, _: bool, _: bool, _: bool); +} + +macro_rules! foo { + () => { + fn fff(_: bool, _: bool, _: bool, _: bool) {} + }; +} + +foo!(); + +#[no_mangle] +extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} +fn g(_: bool, _: bool, _: bool, _: bool) {} +fn h(_: bool, _: bool, _: bool) {} +fn e(_: S, _: S, _: Box, _: Vec) {} +fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} + +struct S {} +trait Trait { + fn f(_: bool, _: bool, _: bool, _: bool); + fn g(_: bool, _: bool, _: bool, _: Vec); +} + +impl S { + fn f(&self, _: bool, _: bool, _: bool, _: bool) {} + fn g(&self, _: bool, _: bool, _: bool) {} + #[no_mangle] + extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} +} + +impl Trait for S { + fn f(_: bool, _: bool, _: bool, _: bool) {} + fn g(_: bool, _: bool, _: bool, _: Vec) {} +} + +fn main() { + fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { + fn nn(_: bool, _: bool, _: bool, _: bool) {} + } +} diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr b/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr new file mode 100644 index 0000000000000..4e5dbc261d66b --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr @@ -0,0 +1,53 @@ +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:17:1 + | +LL | fn g(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings` + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:20:1 + | +LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:24:5 + | +LL | fn f(_: bool, _: bool, _: bool, _: bool); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:29:5 + | +LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:41:5 + | +LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { +LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} +LL | | } + | |_____^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:42:9 + | +LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs new file mode 100644 index 0000000000000..a456c085c8761 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs @@ -0,0 +1,55 @@ +// ignore-32bit + +#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] + +fn foo() -> String { + String::new() +} + +fn test_function_to_numeric_cast() { + let _ = foo as i8; + let _ = foo as i16; + let _ = foo as i32; + let _ = foo as i64; + let _ = foo as i128; + let _ = foo as isize; + + let _ = foo as u8; + let _ = foo as u16; + let _ = foo as u32; + let _ = foo as u64; + let _ = foo as u128; + + // Casting to usize is OK and should not warn + let _ = foo as usize; + + // Cast `f` (a `FnDef`) to `fn()` should not warn + fn f() {} + let _ = f as fn(); +} + +fn test_function_var_to_numeric_cast() { + let abc: fn() -> String = foo; + + let _ = abc as i8; + let _ = abc as i16; + let _ = abc as i32; + let _ = abc as i64; + let _ = abc as i128; + let _ = abc as isize; + + let _ = abc as u8; + let _ = abc as u16; + let _ = abc as u32; + let _ = abc as u64; + let _ = abc as u128; + + // Casting to usize is OK and should not warn + let _ = abc as usize; +} + +fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { + f as i32 +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr new file mode 100644 index 0000000000000..e9549e157cd91 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr @@ -0,0 +1,144 @@ +error: casting function pointer `foo` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:10:13 + | +LL | let _ = foo as i8; + | ^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + +error: casting function pointer `foo` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:11:13 + | +LL | let _ = foo as i16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:12:13 + | +LL | let _ = foo as i32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i64` + --> $DIR/fn_to_numeric_cast.rs:13:13 + | +LL | let _ = foo as i64; + | ^^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + +error: casting function pointer `foo` to `i128` + --> $DIR/fn_to_numeric_cast.rs:14:13 + | +LL | let _ = foo as i128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `isize` + --> $DIR/fn_to_numeric_cast.rs:15:13 + | +LL | let _ = foo as isize; + | ^^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:17:13 + | +LL | let _ = foo as u8; + | ^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:18:13 + | +LL | let _ = foo as u16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:19:13 + | +LL | let _ = foo as u32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u64` + --> $DIR/fn_to_numeric_cast.rs:20:13 + | +LL | let _ = foo as u64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u128` + --> $DIR/fn_to_numeric_cast.rs:21:13 + | +LL | let _ = foo as u128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `abc` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:34:13 + | +LL | let _ = abc as i8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:35:13 + | +LL | let _ = abc as i16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:36:13 + | +LL | let _ = abc as i32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i64` + --> $DIR/fn_to_numeric_cast.rs:37:13 + | +LL | let _ = abc as i64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i128` + --> $DIR/fn_to_numeric_cast.rs:38:13 + | +LL | let _ = abc as i128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `isize` + --> $DIR/fn_to_numeric_cast.rs:39:13 + | +LL | let _ = abc as isize; + | ^^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:41:13 + | +LL | let _ = abc as u8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:42:13 + | +LL | let _ = abc as u16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:43:13 + | +LL | let _ = abc as u32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u64` + --> $DIR/fn_to_numeric_cast.rs:44:13 + | +LL | let _ = abc as u64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u128` + --> $DIR/fn_to_numeric_cast.rs:45:13 + | +LL | let _ = abc as u128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `f` to `i32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:52:5 + | +LL | f as i32 + | ^^^^^^^^ help: try: `f as usize` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs new file mode 100644 index 0000000000000..04ee985c0863d --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs @@ -0,0 +1,55 @@ +// ignore-64bit + +#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] + +fn foo() -> String { + String::new() +} + +fn test_function_to_numeric_cast() { + let _ = foo as i8; + let _ = foo as i16; + let _ = foo as i32; + let _ = foo as i64; + let _ = foo as i128; + let _ = foo as isize; + + let _ = foo as u8; + let _ = foo as u16; + let _ = foo as u32; + let _ = foo as u64; + let _ = foo as u128; + + // Casting to usize is OK and should not warn + let _ = foo as usize; + + // Cast `f` (a `FnDef`) to `fn()` should not warn + fn f() {} + let _ = f as fn(); +} + +fn test_function_var_to_numeric_cast() { + let abc: fn() -> String = foo; + + let _ = abc as i8; + let _ = abc as i16; + let _ = abc as i32; + let _ = abc as i64; + let _ = abc as i128; + let _ = abc as isize; + + let _ = abc as u8; + let _ = abc as u16; + let _ = abc as u32; + let _ = abc as u64; + let _ = abc as u128; + + // Casting to usize is OK and should not warn + let _ = abc as usize; +} + +fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { + f as i32 +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr new file mode 100644 index 0000000000000..08dd611d67524 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr @@ -0,0 +1,144 @@ +error: casting function pointer `foo` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:10:13 + | +LL | let _ = foo as i8; + | ^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + +error: casting function pointer `foo` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:11:13 + | +LL | let _ = foo as i16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast_32bit.rs:12:13 + | +LL | let _ = foo as i32; + | ^^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + +error: casting function pointer `foo` to `i64` + --> $DIR/fn_to_numeric_cast_32bit.rs:13:13 + | +LL | let _ = foo as i64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i128` + --> $DIR/fn_to_numeric_cast_32bit.rs:14:13 + | +LL | let _ = foo as i128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `isize` + --> $DIR/fn_to_numeric_cast_32bit.rs:15:13 + | +LL | let _ = foo as isize; + | ^^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:17:13 + | +LL | let _ = foo as u8; + | ^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:18:13 + | +LL | let _ = foo as u16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast_32bit.rs:19:13 + | +LL | let _ = foo as u32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u64` + --> $DIR/fn_to_numeric_cast_32bit.rs:20:13 + | +LL | let _ = foo as u64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u128` + --> $DIR/fn_to_numeric_cast_32bit.rs:21:13 + | +LL | let _ = foo as u128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `abc` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:34:13 + | +LL | let _ = abc as i8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:35:13 + | +LL | let _ = abc as i16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i32` + --> $DIR/fn_to_numeric_cast_32bit.rs:36:13 + | +LL | let _ = abc as i32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i64` + --> $DIR/fn_to_numeric_cast_32bit.rs:37:13 + | +LL | let _ = abc as i64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i128` + --> $DIR/fn_to_numeric_cast_32bit.rs:38:13 + | +LL | let _ = abc as i128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `isize` + --> $DIR/fn_to_numeric_cast_32bit.rs:39:13 + | +LL | let _ = abc as isize; + | ^^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:41:13 + | +LL | let _ = abc as u8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:42:13 + | +LL | let _ = abc as u16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u32` + --> $DIR/fn_to_numeric_cast_32bit.rs:43:13 + | +LL | let _ = abc as u32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u64` + --> $DIR/fn_to_numeric_cast_32bit.rs:44:13 + | +LL | let _ = abc as u64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u128` + --> $DIR/fn_to_numeric_cast_32bit.rs:45:13 + | +LL | let _ = abc as u128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `f` to `i32` + --> $DIR/fn_to_numeric_cast_32bit.rs:52:5 + | +LL | f as i32 + | ^^^^^^^^ help: try: `f as usize` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/for_kv_map.rs b/src/tools/clippy/tests/ui/for_kv_map.rs new file mode 100644 index 0000000000000..39a8d960a7e91 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_kv_map.rs @@ -0,0 +1,50 @@ +#![warn(clippy::for_kv_map)] +#![allow(clippy::used_underscore_binding)] + +use std::collections::*; +use std::rc::Rc; + +fn main() { + let m: HashMap = HashMap::new(); + for (_, v) in &m { + let _v = v; + } + + let m: Rc> = Rc::new(HashMap::new()); + for (_, v) in &*m { + let _v = v; + // Here the `*` is not actually necessary, but the test tests that we don't + // suggest + // `in *m.values()` as we used to + } + + let mut m: HashMap = HashMap::new(); + for (_, v) in &mut m { + let _v = v; + } + + let m: &mut HashMap = &mut HashMap::new(); + for (_, v) in &mut *m { + let _v = v; + } + + let m: HashMap = HashMap::new(); + let rm = &m; + for (k, _value) in rm { + let _k = k; + } + + // The following should not produce warnings. + + let m: HashMap = HashMap::new(); + // No error, _value is actually used + for (k, _value) in &m { + let _ = _value; + let _k = k; + } + + let m: HashMap = Default::default(); + for (_, v) in m { + let _v = v; + } +} diff --git a/src/tools/clippy/tests/ui/for_kv_map.stderr b/src/tools/clippy/tests/ui/for_kv_map.stderr new file mode 100644 index 0000000000000..241758c542cfb --- /dev/null +++ b/src/tools/clippy/tests/ui/for_kv_map.stderr @@ -0,0 +1,58 @@ +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:9:19 + | +LL | for (_, v) in &m { + | ^^ + | + = note: `-D clippy::for-kv-map` implied by `-D warnings` +help: use the corresponding method + | +LL | for v in m.values() { + | ^ ^^^^^^^^^^ + +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:14:19 + | +LL | for (_, v) in &*m { + | ^^^ + | +help: use the corresponding method + | +LL | for v in (*m).values() { + | ^ ^^^^^^^^^^^^^ + +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:22:19 + | +LL | for (_, v) in &mut m { + | ^^^^^^ + | +help: use the corresponding method + | +LL | for v in m.values_mut() { + | ^ ^^^^^^^^^^^^^^ + +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:27:19 + | +LL | for (_, v) in &mut *m { + | ^^^^^^^ + | +help: use the corresponding method + | +LL | for v in (*m).values_mut() { + | ^ ^^^^^^^^^^^^^^^^^ + +error: you seem to want to iterate on a map's keys + --> $DIR/for_kv_map.rs:33:24 + | +LL | for (k, _value) in rm { + | ^^ + | +help: use the corresponding method + | +LL | for k in rm.keys() { + | ^ ^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.fixed b/src/tools/clippy/tests/ui/for_loop_fixable.fixed new file mode 100644 index 0000000000000..249a88a0b3982 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_fixable.fixed @@ -0,0 +1,283 @@ +// run-rustfix + +#![allow(dead_code, unused)] + +use std::collections::*; + +#[warn(clippy::all)] +struct Unrelated(Vec); +impl Unrelated { + fn next(&self) -> std::slice::Iter { + self.0.iter() + } + + fn iter(&self) -> std::slice::Iter { + self.0.iter() + } +} + +#[warn( + clippy::needless_range_loop, + clippy::explicit_iter_loop, + clippy::explicit_into_iter_loop, + clippy::iter_next_loop, + clippy::for_kv_map +)] +#[allow( + clippy::linkedlist, + clippy::shadow_unrelated, + clippy::unnecessary_mut_passed, + clippy::similar_names +)] +#[allow(clippy::many_single_char_names, unused_variables)] +fn main() { + let mut vec = vec![1, 2, 3, 4]; + + // See #601 + for i in 0..10 { + // no error, id_col does not exist outside the loop + let mut id_col = vec![0f64; 10]; + id_col[i] = 1f64; + } + + for _v in &vec {} + + for _v in &mut vec {} + + let out_vec = vec![1, 2, 3]; + for _v in out_vec {} + + for _v in &vec {} // these are fine + for _v in &mut vec {} // these are fine + + for _v in &[1, 2, 3] {} + + for _v in (&mut [1, 2, 3]).iter() {} // no error + + for _v in &[0; 32] {} + + for _v in [0; 33].iter() {} // no error + + let ll: LinkedList<()> = LinkedList::new(); + for _v in &ll {} + + let vd: VecDeque<()> = VecDeque::new(); + for _v in &vd {} + + let bh: BinaryHeap<()> = BinaryHeap::new(); + for _v in &bh {} + + let hm: HashMap<(), ()> = HashMap::new(); + for _v in &hm {} + + let bt: BTreeMap<(), ()> = BTreeMap::new(); + for _v in &bt {} + + let hs: HashSet<()> = HashSet::new(); + for _v in &hs {} + + let bs: BTreeSet<()> = BTreeSet::new(); + for _v in &bs {} + + let u = Unrelated(vec![]); + for _v in u.next() {} // no error + for _v in u.iter() {} // no error + + let mut out = vec![]; + vec.iter().cloned().map(|x| out.push(x)).collect::>(); + let _y = vec.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Loop with explicit counter variable + + // Potential false positives + let mut _index = 0; + _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + _index += 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + if true { + _index = 1 + } + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + let mut _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index *= 2; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index = 1; + _index += 1 + } + + let mut _index = 0; + + for _v in &vec { + let mut _index = 0; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index = 0; + } + + let mut _index = 0; + for _v in &vec { + for _x in 0..1 { + _index += 1; + } + _index += 1 + } + + let mut _index = 0; + for x in &vec { + if *x == 1 { + _index += 1 + } + } + + let mut _index = 0; + if true { + _index = 1 + }; + for _v in &vec { + _index += 1 + } + + let mut _index = 1; + if false { + _index = 0 + }; + for _v in &vec { + _index += 1 + } + + let mut index = 0; + { + let mut _x = &mut index; + } + for _v in &vec { + _index += 1 + } + + let mut index = 0; + for _v in &vec { + index += 1 + } + println!("index: {}", index); + + fn f(_: &T, _: &T) -> bool { + unimplemented!() + } + fn g(_: &mut [T], _: usize, _: usize) { + unimplemented!() + } + for i in 1..vec.len() { + if f(&vec[i - 1], &vec[i]) { + g(&mut vec, i - 1, i); + } + } + + for mid in 1..vec.len() { + let (_, _) = vec.split_at(mid); + } +} + +fn partition(v: &mut [T]) -> usize { + let pivot = v.len() - 1; + let mut i = 0; + for j in 0..pivot { + if v[j] <= v[pivot] { + v.swap(i, j); + i += 1; + } + } + v.swap(i, pivot); + i +} + +#[warn(clippy::needless_range_loop)] +pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) { + // Same source and destination - don't trigger lint + for i in 0..dst.len() { + dst[d + i] = dst[s + i]; + } +} + +mod issue_2496 { + pub trait Handle { + fn new_for_index(index: usize) -> Self; + fn index(&self) -> usize; + } + + pub fn test() -> H { + for x in 0..5 { + let next_handle = H::new_for_index(x); + println!("{}", next_handle.index()); + } + unimplemented!() + } +} + +// explicit_into_iter_loop bad suggestions +#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)] +mod issue_4958 { + fn takes_iterator(iterator: &T) + where + for<'a> &'a T: IntoIterator, + { + for i in iterator { + println!("{}", i); + } + } + + struct T; + impl IntoIterator for &T { + type Item = (); + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + vec![].into_iter() + } + } + + fn more_tests() { + let t = T; + let r = &t; + let rr = &&t; + + // This case is handled by `explicit_iter_loop`. No idea why. + for _ in &t {} + + for _ in r {} + + // No suggestion for this. + // We'd have to suggest `for _ in *rr {}` which is less clear. + for _ in rr.into_iter() {} + } +} diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.rs b/src/tools/clippy/tests/ui/for_loop_fixable.rs new file mode 100644 index 0000000000000..306d85a6351e1 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_fixable.rs @@ -0,0 +1,283 @@ +// run-rustfix + +#![allow(dead_code, unused)] + +use std::collections::*; + +#[warn(clippy::all)] +struct Unrelated(Vec); +impl Unrelated { + fn next(&self) -> std::slice::Iter { + self.0.iter() + } + + fn iter(&self) -> std::slice::Iter { + self.0.iter() + } +} + +#[warn( + clippy::needless_range_loop, + clippy::explicit_iter_loop, + clippy::explicit_into_iter_loop, + clippy::iter_next_loop, + clippy::for_kv_map +)] +#[allow( + clippy::linkedlist, + clippy::shadow_unrelated, + clippy::unnecessary_mut_passed, + clippy::similar_names +)] +#[allow(clippy::many_single_char_names, unused_variables)] +fn main() { + let mut vec = vec![1, 2, 3, 4]; + + // See #601 + for i in 0..10 { + // no error, id_col does not exist outside the loop + let mut id_col = vec![0f64; 10]; + id_col[i] = 1f64; + } + + for _v in vec.iter() {} + + for _v in vec.iter_mut() {} + + let out_vec = vec![1, 2, 3]; + for _v in out_vec.into_iter() {} + + for _v in &vec {} // these are fine + for _v in &mut vec {} // these are fine + + for _v in [1, 2, 3].iter() {} + + for _v in (&mut [1, 2, 3]).iter() {} // no error + + for _v in [0; 32].iter() {} + + for _v in [0; 33].iter() {} // no error + + let ll: LinkedList<()> = LinkedList::new(); + for _v in ll.iter() {} + + let vd: VecDeque<()> = VecDeque::new(); + for _v in vd.iter() {} + + let bh: BinaryHeap<()> = BinaryHeap::new(); + for _v in bh.iter() {} + + let hm: HashMap<(), ()> = HashMap::new(); + for _v in hm.iter() {} + + let bt: BTreeMap<(), ()> = BTreeMap::new(); + for _v in bt.iter() {} + + let hs: HashSet<()> = HashSet::new(); + for _v in hs.iter() {} + + let bs: BTreeSet<()> = BTreeSet::new(); + for _v in bs.iter() {} + + let u = Unrelated(vec![]); + for _v in u.next() {} // no error + for _v in u.iter() {} // no error + + let mut out = vec![]; + vec.iter().cloned().map(|x| out.push(x)).collect::>(); + let _y = vec.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Loop with explicit counter variable + + // Potential false positives + let mut _index = 0; + _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + _index += 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + if true { + _index = 1 + } + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + let mut _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index *= 2; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index = 1; + _index += 1 + } + + let mut _index = 0; + + for _v in &vec { + let mut _index = 0; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index = 0; + } + + let mut _index = 0; + for _v in &vec { + for _x in 0..1 { + _index += 1; + } + _index += 1 + } + + let mut _index = 0; + for x in &vec { + if *x == 1 { + _index += 1 + } + } + + let mut _index = 0; + if true { + _index = 1 + }; + for _v in &vec { + _index += 1 + } + + let mut _index = 1; + if false { + _index = 0 + }; + for _v in &vec { + _index += 1 + } + + let mut index = 0; + { + let mut _x = &mut index; + } + for _v in &vec { + _index += 1 + } + + let mut index = 0; + for _v in &vec { + index += 1 + } + println!("index: {}", index); + + fn f(_: &T, _: &T) -> bool { + unimplemented!() + } + fn g(_: &mut [T], _: usize, _: usize) { + unimplemented!() + } + for i in 1..vec.len() { + if f(&vec[i - 1], &vec[i]) { + g(&mut vec, i - 1, i); + } + } + + for mid in 1..vec.len() { + let (_, _) = vec.split_at(mid); + } +} + +fn partition(v: &mut [T]) -> usize { + let pivot = v.len() - 1; + let mut i = 0; + for j in 0..pivot { + if v[j] <= v[pivot] { + v.swap(i, j); + i += 1; + } + } + v.swap(i, pivot); + i +} + +#[warn(clippy::needless_range_loop)] +pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) { + // Same source and destination - don't trigger lint + for i in 0..dst.len() { + dst[d + i] = dst[s + i]; + } +} + +mod issue_2496 { + pub trait Handle { + fn new_for_index(index: usize) -> Self; + fn index(&self) -> usize; + } + + pub fn test() -> H { + for x in 0..5 { + let next_handle = H::new_for_index(x); + println!("{}", next_handle.index()); + } + unimplemented!() + } +} + +// explicit_into_iter_loop bad suggestions +#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)] +mod issue_4958 { + fn takes_iterator(iterator: &T) + where + for<'a> &'a T: IntoIterator, + { + for i in iterator.into_iter() { + println!("{}", i); + } + } + + struct T; + impl IntoIterator for &T { + type Item = (); + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + vec![].into_iter() + } + } + + fn more_tests() { + let t = T; + let r = &t; + let rr = &&t; + + // This case is handled by `explicit_iter_loop`. No idea why. + for _ in t.into_iter() {} + + for _ in r.into_iter() {} + + // No suggestion for this. + // We'd have to suggest `for _ in *rr {}` which is less clear. + for _ in rr.into_iter() {} + } +} diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.stderr b/src/tools/clippy/tests/ui/for_loop_fixable.stderr new file mode 100644 index 0000000000000..ddfe66d675f91 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_fixable.stderr @@ -0,0 +1,96 @@ +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:43:15 + | +LL | for _v in vec.iter() {} + | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:45:15 + | +LL | for _v in vec.iter_mut() {} + | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` + +error: it is more concise to loop over containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:48:15 + | +LL | for _v in out_vec.into_iter() {} + | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` + | + = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:53:15 + | +LL | for _v in [1, 2, 3].iter() {} + | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:57:15 + | +LL | for _v in [0; 32].iter() {} + | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:62:15 + | +LL | for _v in ll.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&ll` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:65:15 + | +LL | for _v in vd.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&vd` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:68:15 + | +LL | for _v in bh.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&bh` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:71:15 + | +LL | for _v in hm.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&hm` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:74:15 + | +LL | for _v in bt.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&bt` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:77:15 + | +LL | for _v in hs.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&hs` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:80:15 + | +LL | for _v in bs.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&bs` + +error: it is more concise to loop over containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:255:18 + | +LL | for i in iterator.into_iter() { + | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:275:18 + | +LL | for _ in t.into_iter() {} + | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t` + +error: it is more concise to loop over containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:277:18 + | +LL | for _ in r.into_iter() {} + | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.rs b/src/tools/clippy/tests/ui/for_loop_unfixable.rs new file mode 100644 index 0000000000000..e73536052f0f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_unfixable.rs @@ -0,0 +1,22 @@ +// Tests from for_loop.rs that don't have suggestions + +#[warn( + clippy::needless_range_loop, + clippy::explicit_iter_loop, + clippy::explicit_into_iter_loop, + clippy::iter_next_loop, + clippy::for_kv_map +)] +#[allow( + clippy::linkedlist, + clippy::shadow_unrelated, + clippy::unnecessary_mut_passed, + clippy::similar_names, + unused, + dead_code +)] +fn main() { + let vec = vec![1, 2, 3, 4]; + + for _v in vec.iter().next() {} +} diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr new file mode 100644 index 0000000000000..1c9287b6acbb3 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr @@ -0,0 +1,10 @@ +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> $DIR/for_loop_unfixable.rs:21:15 + | +LL | for _v in vec.iter().next() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-next-loop` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs new file mode 100644 index 0000000000000..1b9dde87cd5a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs @@ -0,0 +1,57 @@ +#![warn(clippy::for_loops_over_fallibles)] + +fn for_loops_over_fallibles() { + let option = Some(1); + let result = option.ok_or("x not found"); + let v = vec![0, 1, 2]; + + // check over an `Option` + for x in option { + println!("{}", x); + } + + // check over a `Result` + for x in result { + println!("{}", x); + } + + for x in option.ok_or("x not found") { + println!("{}", x); + } + + // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call + // in the chain + for x in v.iter().next() { + println!("{}", x); + } + + // make sure we lint when next() is not the last call in the chain + for x in v.iter().next().and(Some(0)) { + println!("{}", x); + } + + for x in v.iter().next().ok_or("x not found") { + println!("{}", x); + } + + // check for false positives + + // for loop false positive + for x in v { + println!("{}", x); + } + + // while let false positive for Option + while let Some(x) = option { + println!("{}", x); + break; + } + + // while let false positive for Result + while let Ok(x) = result { + println!("{}", x); + break; + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr new file mode 100644 index 0000000000000..bef228d4b93af --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr @@ -0,0 +1,71 @@ +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:9:14 + | +LL | for x in option { + | ^^^^^^ + | + = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` + = help: consider replacing `for x in option` with `if let Some(x) = option` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:14:14 + | +LL | for x in result { + | ^^^^^^ + | + = help: consider replacing `for x in result` with `if let Ok(x) = result` + +error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:18:14 + | +LL | for x in option.ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` + +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> $DIR/for_loops_over_fallibles.rs:24:14 + | +LL | for x in v.iter().next() { + | ^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::iter_next_loop)]` on by default + +error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:29:14 + | +LL | for x in v.iter().next().and(Some(0)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` + +error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:33:14 + | +LL | for x in v.iter().next().ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` + +error: this loop never actually loops + --> $DIR/for_loops_over_fallibles.rs:45:5 + | +LL | / while let Some(x) = option { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/for_loops_over_fallibles.rs:51:5 + | +LL | / while let Ok(x) = result { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/forget_ref.rs b/src/tools/clippy/tests/ui/forget_ref.rs new file mode 100644 index 0000000000000..447fdbe1fac53 --- /dev/null +++ b/src/tools/clippy/tests/ui/forget_ref.rs @@ -0,0 +1,48 @@ +#![warn(clippy::forget_ref)] +#![allow(clippy::toplevel_ref_arg)] + +use std::mem::forget; + +struct SomeStruct; + +fn main() { + forget(&SomeStruct); + + let mut owned = SomeStruct; + forget(&owned); + forget(&&owned); + forget(&mut owned); + forget(owned); //OK + + let reference1 = &SomeStruct; + forget(&*reference1); + + let reference2 = &mut SomeStruct; + forget(reference2); + + let ref reference3 = SomeStruct; + forget(reference3); +} + +#[allow(dead_code)] +fn test_generic_fn_forget(val: T) { + forget(&val); + forget(val); //OK +} + +#[allow(dead_code)] +fn test_similarly_named_function() { + fn forget(_val: T) {} + forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name + std::mem::forget(&SomeStruct); +} + +#[derive(Copy, Clone)] +pub struct Error; +fn produce_half_owl_error() -> Result<(), Error> { + Ok(()) +} + +fn produce_half_owl_ok() -> Result { + Ok(true) +} diff --git a/src/tools/clippy/tests/ui/forget_ref.stderr b/src/tools/clippy/tests/ui/forget_ref.stderr new file mode 100644 index 0000000000000..f90bcc2762cee --- /dev/null +++ b/src/tools/clippy/tests/ui/forget_ref.stderr @@ -0,0 +1,111 @@ +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:9:5 + | +LL | forget(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::forget-ref` implied by `-D warnings` +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:9:12 + | +LL | forget(&SomeStruct); + | ^^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:12:5 + | +LL | forget(&owned); + | ^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:12:12 + | +LL | forget(&owned); + | ^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:13:5 + | +LL | forget(&&owned); + | ^^^^^^^^^^^^^^^ + | +note: argument has type `&&SomeStruct` + --> $DIR/forget_ref.rs:13:12 + | +LL | forget(&&owned); + | ^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:14:5 + | +LL | forget(&mut owned); + | ^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/forget_ref.rs:14:12 + | +LL | forget(&mut owned); + | ^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:18:5 + | +LL | forget(&*reference1); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:18:12 + | +LL | forget(&*reference1); + | ^^^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:21:5 + | +LL | forget(reference2); + | ^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/forget_ref.rs:21:12 + | +LL | forget(reference2); + | ^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:24:5 + | +LL | forget(reference3); + | ^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:24:12 + | +LL | forget(reference3); + | ^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:29:5 + | +LL | forget(&val); + | ^^^^^^^^^^^^ + | +note: argument has type `&T` + --> $DIR/forget_ref.rs:29:12 + | +LL | forget(&val); + | ^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:37:5 + | +LL | std::mem::forget(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:37:22 + | +LL | std::mem::forget(&SomeStruct); + | ^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed new file mode 100644 index 0000000000000..306514769990d --- /dev/null +++ b/src/tools/clippy/tests/ui/format.fixed @@ -0,0 +1,67 @@ +// run-rustfix + +#![allow(clippy::print_literal, clippy::redundant_clone)] +#![warn(clippy::useless_format)] + +struct Foo(pub String); + +macro_rules! foo { + ($($t:tt)*) => (Foo(format!($($t)*))) +} + +fn main() { + "foo".to_string(); + "{}".to_string(); + "{} abc {}".to_string(); + "foo {}\n\" bar".to_string(); + + "foo".to_string(); + format!("{:?}", "foo"); // Don't warn about `Debug`. + format!("{:8}", "foo"); + format!("{:width$}", "foo", width = 8); + "foo".to_string(); // Warn when the format makes no difference. + "foo".to_string(); // Warn when the format makes no difference. + format!("foo {}", "bar"); + format!("{} bar", "foo"); + + let arg: String = "".to_owned(); + arg.to_string(); + format!("{:?}", arg); // Don't warn about debug. + format!("{:8}", arg); + format!("{:width$}", arg, width = 8); + arg.to_string(); // Warn when the format makes no difference. + arg.to_string(); // Warn when the format makes no difference. + format!("foo {}", arg); + format!("{} bar", arg); + + // We don’t want to warn for non-string args; see issue #697. + format!("{}", 42); + format!("{:?}", 42); + format!("{:+}", 42); + format!("foo {}", 42); + format!("{} bar", 42); + + // We only want to warn about `format!` itself. + println!("foo"); + println!("{}", "foo"); + println!("foo {}", "foo"); + println!("{}", 42); + println!("foo {}", 42); + + // A `format!` inside a macro should not trigger a warning. + foo!("should not warn"); + + // Precision on string means slicing without panicking on size. + format!("{:.1}", "foo"); // Could be `"foo"[..1]` + format!("{:.10}", "foo"); // Could not be `"foo"[..10]` + format!("{:.prec$}", "foo", prec = 1); + format!("{:.prec$}", "foo", prec = 10); + + 42.to_string(); + let x = std::path::PathBuf::from("/bar/foo/qux"); + x.display().to_string(); + + // False positive + let a = "foo".to_string(); + let _ = Some(a + "bar"); +} diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs new file mode 100644 index 0000000000000..b604d79cca373 --- /dev/null +++ b/src/tools/clippy/tests/ui/format.rs @@ -0,0 +1,70 @@ +// run-rustfix + +#![allow(clippy::print_literal, clippy::redundant_clone)] +#![warn(clippy::useless_format)] + +struct Foo(pub String); + +macro_rules! foo { + ($($t:tt)*) => (Foo(format!($($t)*))) +} + +fn main() { + format!("foo"); + format!("{{}}"); + format!("{{}} abc {{}}"); + format!( + r##"foo {{}} +" bar"## + ); + + format!("{}", "foo"); + format!("{:?}", "foo"); // Don't warn about `Debug`. + format!("{:8}", "foo"); + format!("{:width$}", "foo", width = 8); + format!("{:+}", "foo"); // Warn when the format makes no difference. + format!("{:<}", "foo"); // Warn when the format makes no difference. + format!("foo {}", "bar"); + format!("{} bar", "foo"); + + let arg: String = "".to_owned(); + format!("{}", arg); + format!("{:?}", arg); // Don't warn about debug. + format!("{:8}", arg); + format!("{:width$}", arg, width = 8); + format!("{:+}", arg); // Warn when the format makes no difference. + format!("{:<}", arg); // Warn when the format makes no difference. + format!("foo {}", arg); + format!("{} bar", arg); + + // We don’t want to warn for non-string args; see issue #697. + format!("{}", 42); + format!("{:?}", 42); + format!("{:+}", 42); + format!("foo {}", 42); + format!("{} bar", 42); + + // We only want to warn about `format!` itself. + println!("foo"); + println!("{}", "foo"); + println!("foo {}", "foo"); + println!("{}", 42); + println!("foo {}", 42); + + // A `format!` inside a macro should not trigger a warning. + foo!("should not warn"); + + // Precision on string means slicing without panicking on size. + format!("{:.1}", "foo"); // Could be `"foo"[..1]` + format!("{:.10}", "foo"); // Could not be `"foo"[..10]` + format!("{:.prec$}", "foo", prec = 1); + format!("{:.prec$}", "foo", prec = 10); + + format!("{}", 42.to_string()); + let x = std::path::PathBuf::from("/bar/foo/qux"); + format!("{}", x.display().to_string()); + + // False positive + let a = "foo".to_string(); + let _ = Some(format!("{}", a + "bar")); +} diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr new file mode 100644 index 0000000000000..9734492154e80 --- /dev/null +++ b/src/tools/clippy/tests/ui/format.stderr @@ -0,0 +1,85 @@ +error: useless use of `format!` + --> $DIR/format.rs:13:5 + | +LL | format!("foo"); + | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + | + = note: `-D clippy::useless-format` implied by `-D warnings` + +error: useless use of `format!` + --> $DIR/format.rs:14:5 + | +LL | format!("{{}}"); + | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:15:5 + | +LL | format!("{{}} abc {{}}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:16:5 + | +LL | / format!( +LL | | r##"foo {{}} +LL | | " bar"## +LL | | ); + | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:21:5 + | +LL | format!("{}", "foo"); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:25:5 + | +LL | format!("{:+}", "foo"); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:26:5 + | +LL | format!("{:<}", "foo"); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:31:5 + | +LL | format!("{}", arg); + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:35:5 + | +LL | format!("{:+}", arg); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:36:5 + | +LL | format!("{:<}", arg); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:63:5 + | +LL | format!("{}", 42.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:65:5 + | +LL | format!("{}", x.display().to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:69:18 + | +LL | let _ = Some(format!("{}", a + "bar")); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs new file mode 100644 index 0000000000000..078811b8d882b --- /dev/null +++ b/src/tools/clippy/tests/ui/formatting.rs @@ -0,0 +1,157 @@ +#![warn(clippy::all)] +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(clippy::if_same_then_else)] +#![allow(clippy::deref_addrof)] + +fn foo() -> bool { + true +} + +#[rustfmt::skip] +fn main() { + // weird `else` formatting: + if foo() { + } { + } + + if foo() { + } if foo() { + } + + let _ = { // if as the last expression + let _ = 0; + + if foo() { + } if foo() { + } + else { + } + }; + + let _ = { // if in the middle of a block + if foo() { + } if foo() { + } + else { + } + + let _ = 0; + }; + + if foo() { + } else + { + } + + if foo() { + } + else + { + } + + if foo() { + } else + if foo() { // the span of the above error should continue here + } + + if foo() { + } + else + if foo() { // the span of the above error should continue here + } + + // those are ok: + if foo() { + } + { + } + + if foo() { + } else { + } + + if foo() { + } + else { + } + + if foo() { + } + if foo() { + } + + if foo() { + } else if foo() { + } + + if foo() { + } + else if foo() { + } + + if foo() { + } + else if + foo() {} + + // weird op_eq formatting: + let mut a = 42; + a =- 35; + a =* &191; + + let mut b = true; + b =! false; + + // those are ok: + a = -35; + a = *&191; + b = !false; + + // possible missing comma in an array + let _ = &[ + -1, -2, -3 // <= no comma here + -4, -5, -6 + ]; + let _ = &[ + -1, -2, -3 // <= no comma here + *4, -5, -6 + ]; + + // those are ok: + let _ = &[ + -1, -2, -3, + -4, -5, -6 + ]; + let _ = &[ + -1, -2, -3, + -4, -5, -6, + ]; + let _ = &[ + 1 + 2, 3 + + 4, 5 + 6, + ]; + + // don't lint for bin op without unary equiv + // issue 3244 + vec![ + 1 + / 2, + ]; + // issue 3396 + vec![ + true + | false, + ]; + + // don't lint if the indentation suggests not to + let _ = &[ + 1 + 2, 3 + - 4, 5 + ]; + // lint if it doesnt + let _ = &[ + -1 + -4, + ]; +} diff --git a/src/tools/clippy/tests/ui/formatting.stderr b/src/tools/clippy/tests/ui/formatting.stderr new file mode 100644 index 0000000000000..e2095cc125bb7 --- /dev/null +++ b/src/tools/clippy/tests/ui/formatting.stderr @@ -0,0 +1,127 @@ +error: this looks like an `else {..}` but the `else` is missing + --> $DIR/formatting.rs:15:6 + | +LL | } { + | ^ + | + = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` + = note: to remove this lint, add the missing `else` or add a new line before the next block + +error: this looks like an `else if` but the `else` is missing + --> $DIR/formatting.rs:19:6 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/formatting.rs:26:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/formatting.rs:34:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/formatting.rs:43:6 + | +LL | } else + | ______^ +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/formatting.rs:48:6 + | +LL | } + | ______^ +LL | | else +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else if` but the formatting might hide it + --> $DIR/formatting.rs:54:6 + | +LL | } else + | ______^ +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: this is an `else if` but the formatting might hide it + --> $DIR/formatting.rs:59:6 + | +LL | } + | ______^ +LL | | else +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` + --> $DIR/formatting.rs:100:6 + | +LL | a =- 35; + | ^^^^ + | + = note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings` + = note: to remove this lint, use either `-=` or `= -` + +error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` + --> $DIR/formatting.rs:101:6 + | +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 `.. = (! ..)` + --> $DIR/formatting.rs:104:6 + | +LL | b =! false; + | ^^^^ + | + = note: to remove this lint, use either `!=` or `= !` + +error: possibly missing a comma here + --> $DIR/formatting.rs:113:19 + | +LL | -1, -2, -3 // <= no comma here + | ^ + | + = note: `-D clippy::possible-missing-comma` implied by `-D warnings` + = note: to remove this lint, add a comma or write the expr in a single line + +error: possibly missing a comma here + --> $DIR/formatting.rs:117:19 + | +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 + --> $DIR/formatting.rs:154:11 + | +LL | -1 + | ^ + | + = note: to remove this lint, add a comma or write the expr in a single line + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/functions.rs b/src/tools/clippy/tests/ui/functions.rs new file mode 100644 index 0000000000000..271754cb06ee7 --- /dev/null +++ b/src/tools/clippy/tests/ui/functions.rs @@ -0,0 +1,104 @@ +#![warn(clippy::all)] +#![allow(dead_code)] +#![allow(unused_unsafe, clippy::missing_safety_doc)] + +// TOO_MANY_ARGUMENTS +fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} + +fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + +#[rustfmt::skip] +fn bad_multiline( + one: u32, + two: u32, + three: &str, + four: bool, + five: f32, + six: f32, + seven: bool, + eight: () +) { + let _one = one; + let _two = two; + let _three = three; + let _four = four; + let _five = five; + let _six = six; + let _seven = seven; +} + +// don't lint extern fns +extern "C" fn extern_fn( + _one: u32, + _two: u32, + _three: *const u8, + _four: bool, + _five: f32, + _six: f32, + _seven: bool, + _eight: *const std::ffi::c_void, +) { +} + +pub trait Foo { + fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool); + fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()); + + fn ptr(p: *const u8); +} + +pub struct Bar; + +impl Bar { + fn good_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} + fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} +} + +// ok, we don’t want to warn implementations +impl Foo for Bar { + fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} + fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + + fn ptr(p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + unsafe { std::ptr::read(p) }; + } +} + +// NOT_UNSAFE_PTR_ARG_DEREF + +fn private(p: *const u8) { + println!("{}", unsafe { *p }); +} + +pub fn public(p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + unsafe { std::ptr::read(p) }; +} + +impl Bar { + fn private(self, p: *const u8) { + println!("{}", unsafe { *p }); + } + + pub fn public(self, p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + unsafe { std::ptr::read(p) }; + } + + pub fn public_ok(self, p: *const u8) { + if !p.is_null() { + println!("{:p}", p); + } + } + + pub unsafe fn public_unsafe(self, p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/functions.stderr b/src/tools/clippy/tests/ui/functions.stderr new file mode 100644 index 0000000000000..0a86568b18de9 --- /dev/null +++ b/src/tools/clippy/tests/ui/functions.stderr @@ -0,0 +1,90 @@ +error: this function has too many arguments (8/7) + --> $DIR/functions.rs:8:1 + | +LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::too-many-arguments` implied by `-D warnings` + +error: this function has too many arguments (8/7) + --> $DIR/functions.rs:11:1 + | +LL | / fn bad_multiline( +LL | | one: u32, +LL | | two: u32, +LL | | three: &str, +... | +LL | | eight: () +LL | | ) { + | |__^ + +error: this function has too many arguments (8/7) + --> $DIR/functions.rs:45: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) + --> $DIR/functions.rs:54:5 + | +LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:63:34 + | +LL | println!("{}", unsafe { *p }); + | ^ + | + = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings` + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:64:35 + | +LL | println!("{:?}", unsafe { p.as_ref() }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:65:33 + | +LL | unsafe { std::ptr::read(p) }; + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:76:30 + | +LL | println!("{}", unsafe { *p }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:77:31 + | +LL | println!("{:?}", unsafe { p.as_ref() }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:78:29 + | +LL | unsafe { std::ptr::read(p) }; + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:87:34 + | +LL | println!("{}", unsafe { *p }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:88:35 + | +LL | println!("{:?}", unsafe { p.as_ref() }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:89:33 + | +LL | unsafe { std::ptr::read(p) }; + | ^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/functions_maxlines.rs b/src/tools/clippy/tests/ui/functions_maxlines.rs new file mode 100644 index 0000000000000..5e1ee55e01040 --- /dev/null +++ b/src/tools/clippy/tests/ui/functions_maxlines.rs @@ -0,0 +1,163 @@ +#![warn(clippy::too_many_lines)] + +fn good_lines() { + /* println!("This is good."); */ + // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); */ + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); +} + +fn bad_lines() { + println!("Dont get confused by braces: {{}}"); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/functions_maxlines.stderr b/src/tools/clippy/tests/ui/functions_maxlines.stderr new file mode 100644 index 0000000000000..9b0e7550cc314 --- /dev/null +++ b/src/tools/clippy/tests/ui/functions_maxlines.stderr @@ -0,0 +1,16 @@ +error: This function has a large number of lines. + --> $DIR/functions_maxlines.rs:58:1 + | +LL | / fn bad_lines() { +LL | | println!("Dont get confused by braces: {{}}"); +LL | | println!("This is bad."); +LL | | println!("This is bad."); +... | +LL | | println!("This is bad."); +LL | | } + | |_^ + | + = note: `-D clippy::too-many-lines` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/future_not_send.rs b/src/tools/clippy/tests/ui/future_not_send.rs new file mode 100644 index 0000000000000..d3a920de4b6ad --- /dev/null +++ b/src/tools/clippy/tests/ui/future_not_send.rs @@ -0,0 +1,80 @@ +// edition:2018 +#![warn(clippy::future_not_send)] + +use std::cell::Cell; +use std::rc::Rc; +use std::sync::Arc; + +async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { + async { true }.await +} + +pub async fn public_future(rc: Rc<[u8]>) { + async { true }.await; +} + +pub async fn public_send(arc: Arc<[u8]>) -> bool { + async { false }.await +} + +async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + true +} + +pub async fn public_future2(rc: Rc<[u8]>) {} + +pub async fn public_send2(arc: Arc<[u8]>) -> bool { + false +} + +struct Dummy { + rc: Rc<[u8]>, +} + +impl Dummy { + async fn private_future(&self) -> usize { + async { true }.await; + self.rc.len() + } + + pub async fn public_future(&self) { + self.private_future().await; + } + + #[allow(clippy::manual_async_fn)] + pub fn public_send(&self) -> impl std::future::Future { + async { false } + } +} + +async fn generic_future(t: T) -> T +where + T: Send, +{ + let rt = &t; + async { true }.await; + t +} + +async fn generic_future_send(t: T) +where + T: Send, +{ + async { true }.await; +} + +async fn unclear_future(t: T) {} + +fn main() { + let rc = Rc::new([1, 2, 3]); + private_future(rc.clone(), &Cell::new(42)); + public_future(rc.clone()); + let arc = Arc::new([4, 5, 6]); + public_send(arc); + generic_future(42); + generic_future_send(42); + + let dummy = Dummy { rc }; + dummy.public_future(); + dummy.public_send(); +} diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr new file mode 100644 index 0000000000000..d1863701bfe7c --- /dev/null +++ b/src/tools/clippy/tests/ui/future_not_send.stderr @@ -0,0 +1,125 @@ +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:8:62 + | +LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^^^ future returned by `private_future` is not `Send` + | + = note: `-D clippy::future-not-send` implied by `-D warnings` +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:9:5 + | +LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { + | -- has type `std::rc::Rc<[u8]>` which is not `Send` +LL | async { true }.await + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later +LL | } + | - `rc` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:9:5 + | +LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ---- has type `&std::cell::Cell` which is not `Send` +LL | async { true }.await + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `cell` maybe used later +LL | } + | - `cell` is later dropped here + = note: `std::cell::Cell` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:12:42 + | +LL | pub async fn public_future(rc: Rc<[u8]>) { + | ^ future returned by `public_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:13:5 + | +LL | pub async fn public_future(rc: Rc<[u8]>) { + | -- has type `std::rc::Rc<[u8]>` which is not `Send` +LL | async { true }.await; + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later +LL | } + | - `rc` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:20:63 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^^^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + = note: `std::cell::Cell` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:24:43 + | +LL | pub async fn public_future2(rc: Rc<[u8]>) {} + | ^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:35:39 + | +LL | async fn private_future(&self) -> usize { + | ^^^^^ future returned by `private_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:36:9 + | +LL | async fn private_future(&self) -> usize { + | ----- has type `&Dummy` which is not `Send` +LL | async { true }.await; + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later +LL | self.rc.len() +LL | } + | - `&self` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:40:39 + | +LL | pub async fn public_future(&self) { + | ^ future returned by `public_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:41:9 + | +LL | pub async fn public_future(&self) { + | ----- has type `&Dummy` which is not `Send` +LL | self.private_future().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later +LL | } + | - `&self` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:50:37 + | +LL | async fn generic_future(t: T) -> T + | ^ future returned by `generic_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:55:5 + | +LL | let rt = &t; + | -- has type `&T` which is not `Send` +LL | async { true }.await; + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rt` maybe used later +LL | t +LL | } + | - `rt` is later dropped here + = note: `T` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:66:34 + | +LL | async fn unclear_future(t: T) {} + | ^ + | + = note: `T` doesn't implement `std::marker::Send` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/get_last_with_len.fixed b/src/tools/clippy/tests/ui/get_last_with_len.fixed new file mode 100644 index 0000000000000..c8b363f9c38e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/get_last_with_len.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::get_last_with_len)] + +fn dont_use_last() { + let x = vec![2, 3, 5]; + let _ = x.last(); // ~ERROR Use x.last() +} + +fn indexing_two_from_end() { + let x = vec![2, 3, 5]; + let _ = x.get(x.len() - 2); +} + +fn index_into_last() { + let x = vec![2, 3, 5]; + let _ = x[x.len() - 1]; +} + +fn use_last_with_different_vec_length() { + let x = vec![2, 3, 5]; + let y = vec!['a', 'b', 'c']; + let _ = x.get(y.len() - 1); +} + +fn main() { + dont_use_last(); + indexing_two_from_end(); + index_into_last(); + use_last_with_different_vec_length(); +} diff --git a/src/tools/clippy/tests/ui/get_last_with_len.rs b/src/tools/clippy/tests/ui/get_last_with_len.rs new file mode 100644 index 0000000000000..bf9cb2d7e0ccd --- /dev/null +++ b/src/tools/clippy/tests/ui/get_last_with_len.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::get_last_with_len)] + +fn dont_use_last() { + let x = vec![2, 3, 5]; + let _ = x.get(x.len() - 1); // ~ERROR Use x.last() +} + +fn indexing_two_from_end() { + let x = vec![2, 3, 5]; + let _ = x.get(x.len() - 2); +} + +fn index_into_last() { + let x = vec![2, 3, 5]; + let _ = x[x.len() - 1]; +} + +fn use_last_with_different_vec_length() { + let x = vec![2, 3, 5]; + let y = vec!['a', 'b', 'c']; + let _ = x.get(y.len() - 1); +} + +fn main() { + dont_use_last(); + indexing_two_from_end(); + index_into_last(); + use_last_with_different_vec_length(); +} diff --git a/src/tools/clippy/tests/ui/get_last_with_len.stderr b/src/tools/clippy/tests/ui/get_last_with_len.stderr new file mode 100644 index 0000000000000..55baf87384a24 --- /dev/null +++ b/src/tools/clippy/tests/ui/get_last_with_len.stderr @@ -0,0 +1,10 @@ +error: accessing last element with `x.get(x.len() - 1)` + --> $DIR/get_last_with_len.rs:7:13 + | +LL | let _ = x.get(x.len() - 1); // ~ERROR Use x.last() + | ^^^^^^^^^^^^^^^^^^ help: try: `x.last()` + | + = note: `-D clippy::get-last-with-len` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/get_unwrap.fixed b/src/tools/clippy/tests/ui/get_unwrap.fixed new file mode 100644 index 0000000000000..97e6b20f471fc --- /dev/null +++ b/src/tools/clippy/tests/ui/get_unwrap.fixed @@ -0,0 +1,62 @@ +// run-rustfix +#![allow(unused_mut)] +#![deny(clippy::get_unwrap)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; +use std::iter::FromIterator; + +struct GetFalsePositive { + arr: [u32; 3], +} + +impl GetFalsePositive { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } + fn get_mut(&mut self, pos: usize) -> Option<&mut u32> { + self.arr.get_mut(pos) + } +} + +fn main() { + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_slice = &mut [0, 1, 2, 3]; + let mut some_vec = vec![0, 1, 2, 3]; + let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hashmap: HashMap = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut some_btreemap: BTreeMap = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut false_positive = GetFalsePositive { arr: [0, 1, 2] }; + + { + // Test `get().unwrap()` + let _ = &boxed_slice[1]; + let _ = &some_slice[0]; + let _ = &some_vec[0]; + let _ = &some_vecdeque[0]; + let _ = &some_hashmap[&1]; + let _ = &some_btreemap[&1]; + let _ = false_positive.get(0).unwrap(); + // Test with deref + let _: u8 = boxed_slice[1]; + } + + { + // Test `get_mut().unwrap()` + boxed_slice[0] = 1; + some_slice[0] = 1; + some_vec[0] = 1; + some_vecdeque[0] = 1; + // Check false positives + *some_hashmap.get_mut(&1).unwrap() = 'b'; + *some_btreemap.get_mut(&1).unwrap() = 'b'; + *false_positive.get_mut(0).unwrap() = 1; + } + + { + // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()` + let _ = some_vec[0..1].to_vec(); + let _ = some_vec[0..1].to_vec(); + } +} diff --git a/src/tools/clippy/tests/ui/get_unwrap.rs b/src/tools/clippy/tests/ui/get_unwrap.rs new file mode 100644 index 0000000000000..1c9a71c09699a --- /dev/null +++ b/src/tools/clippy/tests/ui/get_unwrap.rs @@ -0,0 +1,62 @@ +// run-rustfix +#![allow(unused_mut)] +#![deny(clippy::get_unwrap)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; +use std::iter::FromIterator; + +struct GetFalsePositive { + arr: [u32; 3], +} + +impl GetFalsePositive { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } + fn get_mut(&mut self, pos: usize) -> Option<&mut u32> { + self.arr.get_mut(pos) + } +} + +fn main() { + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_slice = &mut [0, 1, 2, 3]; + let mut some_vec = vec![0, 1, 2, 3]; + let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hashmap: HashMap = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut some_btreemap: BTreeMap = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut false_positive = GetFalsePositive { arr: [0, 1, 2] }; + + { + // Test `get().unwrap()` + let _ = boxed_slice.get(1).unwrap(); + let _ = some_slice.get(0).unwrap(); + let _ = some_vec.get(0).unwrap(); + let _ = some_vecdeque.get(0).unwrap(); + let _ = some_hashmap.get(&1).unwrap(); + let _ = some_btreemap.get(&1).unwrap(); + let _ = false_positive.get(0).unwrap(); + // Test with deref + let _: u8 = *boxed_slice.get(1).unwrap(); + } + + { + // Test `get_mut().unwrap()` + *boxed_slice.get_mut(0).unwrap() = 1; + *some_slice.get_mut(0).unwrap() = 1; + *some_vec.get_mut(0).unwrap() = 1; + *some_vecdeque.get_mut(0).unwrap() = 1; + // Check false positives + *some_hashmap.get_mut(&1).unwrap() = 'b'; + *some_btreemap.get_mut(&1).unwrap() = 'b'; + *false_positive.get_mut(0).unwrap() = 1; + } + + { + // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()` + let _ = some_vec.get(0..1).unwrap().to_vec(); + let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + } +} diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr new file mode 100644 index 0000000000000..76a098df82aa1 --- /dev/null +++ b/src/tools/clippy/tests/ui/get_unwrap.stderr @@ -0,0 +1,86 @@ +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:34:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | +note: the lint level is defined here + --> $DIR/get_unwrap.rs:3:9 + | +LL | #![deny(clippy::get_unwrap)] + | ^^^^^^^^^^^^^^^^^^ + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:35:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:36:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` + +error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:37:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` + +error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:38:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` + +error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:39:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:42:21 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:47:9 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:48:9 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:49:9 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` + +error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:50:9 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:59:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:60:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs new file mode 100644 index 0000000000000..ceaacaaf6bd70 --- /dev/null +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -0,0 +1,41 @@ +const ONE: i64 = 1; +const NEG_ONE: i64 = -1; +const ZERO: i64 = 0; + +#[allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::double_parens +)] +#[warn(clippy::identity_op)] +#[rustfmt::skip] +fn main() { + let x = 0; + + x + 0; + x + (1 - 1); + x + 1; + 0 + x; + 1 + x; + x - ZERO; //no error, as we skip lookups (for now) + x | (0); + ((ZERO)) | x; //no error, as we skip lookups (for now) + + x * 1; + 1 * x; + x / ONE; //no error, as we skip lookups (for now) + + x / 2; //no false positive + + x & NEG_ONE; //no error, as we skip lookups (for now) + -1 & x; + + let u: u8 = 0; + u & 255; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42 << 0; + 1 >> 0; + 42 >> 0; +} diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr new file mode 100644 index 0000000000000..d8d44a74f9ab0 --- /dev/null +++ b/src/tools/clippy/tests/ui/identity_op.stderr @@ -0,0 +1,70 @@ +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:16:5 + | +LL | x + 0; + | ^^^^^ + | + = note: `-D clippy::identity-op` implied by `-D warnings` + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:17:5 + | +LL | x + (1 - 1); + | ^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:19:5 + | +LL | 0 + x; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:22:5 + | +LL | x | (0); + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:25:5 + | +LL | x * 1; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:26:5 + | +LL | 1 * x; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:32:5 + | +LL | -1 & x; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `u` + --> $DIR/identity_op.rs:35:5 + | +LL | u & 255; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:38:5 + | +LL | 42 << 0; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:39:5 + | +LL | 1 >> 0; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:40:5 + | +LL | 42 >> 0; + | ^^^^^^^ + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/if_let_mutex.rs b/src/tools/clippy/tests/ui/if_let_mutex.rs new file mode 100644 index 0000000000000..58feae422a3c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_mutex.rs @@ -0,0 +1,42 @@ +#![warn(clippy::if_let_mutex)] + +use std::ops::Deref; +use std::sync::Mutex; + +fn do_stuff(_: T) {} + +fn if_let() { + let m = Mutex::new(1_u8); + if let Err(locked) = m.lock() { + do_stuff(locked); + } else { + let lock = m.lock().unwrap(); + do_stuff(lock); + }; +} + +// This is the most common case as the above case is pretty +// contrived. +fn if_let_option() { + let m = Mutex::new(Some(0_u8)); + if let Some(locked) = m.lock().unwrap().deref() { + do_stuff(locked); + } else { + let lock = m.lock().unwrap(); + do_stuff(lock); + }; +} + +// When mutexs are different don't warn +fn if_let_different_mutex() { + let m = Mutex::new(Some(0_u8)); + let other = Mutex::new(None::); + if let Some(locked) = m.lock().unwrap().deref() { + do_stuff(locked); + } else { + let lock = other.lock().unwrap(); + do_stuff(lock); + }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_let_mutex.stderr b/src/tools/clippy/tests/ui/if_let_mutex.stderr new file mode 100644 index 0000000000000..e9c4d9163328f --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_mutex.stderr @@ -0,0 +1,29 @@ +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock + --> $DIR/if_let_mutex.rs:10:5 + | +LL | / if let Err(locked) = m.lock() { +LL | | do_stuff(locked); +LL | | } else { +LL | | let lock = m.lock().unwrap(); +LL | | do_stuff(lock); +LL | | }; + | |_____^ + | + = note: `-D clippy::if-let-mutex` implied by `-D warnings` + = help: move the lock call outside of the `if let ...` expression + +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock + --> $DIR/if_let_mutex.rs:22:5 + | +LL | / if let Some(locked) = m.lock().unwrap().deref() { +LL | | do_stuff(locked); +LL | | } else { +LL | | let lock = m.lock().unwrap(); +LL | | do_stuff(lock); +LL | | }; + | |_____^ + | + = help: move the lock call outside of the `if let ...` expression + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/if_let_some_result.fixed b/src/tools/clippy/tests/ui/if_let_some_result.fixed new file mode 100644 index 0000000000000..80505fd997f42 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_some_result.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::if_let_some_result)] + +fn str_to_int(x: &str) -> i32 { + if let Ok(y) = x.parse() { + y + } else { + 0 + } +} + +fn str_to_int_ok(x: &str) -> i32 { + if let Ok(y) = x.parse() { + y + } else { + 0 + } +} + +#[rustfmt::skip] +fn strange_some_no_else(x: &str) -> i32 { + { + if let Ok(y) = x . parse() { + return y; + }; + 0 + } +} + +fn main() { + let _ = str_to_int("1"); + let _ = str_to_int_ok("2"); + let _ = strange_some_no_else("3"); +} diff --git a/src/tools/clippy/tests/ui/if_let_some_result.rs b/src/tools/clippy/tests/ui/if_let_some_result.rs new file mode 100644 index 0000000000000..ecac13574456c --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_some_result.rs @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::if_let_some_result)] + +fn str_to_int(x: &str) -> i32 { + if let Some(y) = x.parse().ok() { + y + } else { + 0 + } +} + +fn str_to_int_ok(x: &str) -> i32 { + if let Ok(y) = x.parse() { + y + } else { + 0 + } +} + +#[rustfmt::skip] +fn strange_some_no_else(x: &str) -> i32 { + { + if let Some(y) = x . parse() . ok () { + return y; + }; + 0 + } +} + +fn main() { + let _ = str_to_int("1"); + let _ = str_to_int_ok("2"); + let _ = strange_some_no_else("3"); +} diff --git a/src/tools/clippy/tests/ui/if_let_some_result.stderr b/src/tools/clippy/tests/ui/if_let_some_result.stderr new file mode 100644 index 0000000000000..334ccb0101678 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_some_result.stderr @@ -0,0 +1,25 @@ +error: Matching on `Some` with `ok()` is redundant + --> $DIR/if_let_some_result.rs:6:5 + | +LL | if let Some(y) = x.parse().ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::if-let-some-result` implied by `-D warnings` +help: Consider matching on `Ok(y)` and removing the call to `ok` instead + | +LL | if let Ok(y) = x.parse() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Matching on `Some` with `ok()` is redundant + --> $DIR/if_let_some_result.rs:24:9 + | +LL | if let Some(y) = x . parse() . ok () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: Consider matching on `Ok(y)` and removing the call to `ok` instead + | +LL | if let Ok(y) = x . parse() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/if_not_else.rs b/src/tools/clippy/tests/ui/if_not_else.rs new file mode 100644 index 0000000000000..dc3fb1ceac9a4 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_not_else.rs @@ -0,0 +1,19 @@ +#![warn(clippy::all)] +#![warn(clippy::if_not_else)] + +fn bla() -> bool { + unimplemented!() +} + +fn main() { + if !bla() { + println!("Bugs"); + } else { + println!("Bunny"); + } + if 4 != 5 { + println!("Bugs"); + } else { + println!("Bunny"); + } +} diff --git a/src/tools/clippy/tests/ui/if_not_else.stderr b/src/tools/clippy/tests/ui/if_not_else.stderr new file mode 100644 index 0000000000000..78bc4d4bd20a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_not_else.stderr @@ -0,0 +1,27 @@ +error: Unnecessary boolean `not` operation + --> $DIR/if_not_else.rs:9:5 + | +LL | / if !bla() { +LL | | println!("Bugs"); +LL | | } else { +LL | | println!("Bunny"); +LL | | } + | |_____^ + | + = note: `-D clippy::if-not-else` implied by `-D warnings` + = help: remove the `!` and swap the blocks of the `if`/`else` + +error: Unnecessary `!=` operation + --> $DIR/if_not_else.rs:14:5 + | +LL | / if 4 != 5 { +LL | | println!("Bugs"); +LL | | } else { +LL | | println!("Bunny"); +LL | | } + | |_____^ + | + = help: change to `==` and swap the blocks of the `if`/`else` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs new file mode 100644 index 0000000000000..6bbf79edfcf70 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else.rs @@ -0,0 +1,145 @@ +#![warn(clippy::if_same_then_else)] +#![allow( + clippy::blacklisted_name, + clippy::eq_op, + clippy::never_loop, + clippy::no_effect, + clippy::unused_unit, + clippy::zero_divided_by_zero +)] + +struct Foo { + bar: u8, +} + +fn foo() -> bool { + unimplemented!() +} + +fn if_same_then_else() { + if true { + Foo { bar: 42 }; + 0..10; + ..; + 0..; + ..10; + 0..=10; + foo(); + } else { + //~ ERROR same body as `if` block + Foo { bar: 42 }; + 0..10; + ..; + 0..; + ..10; + 0..=10; + foo(); + } + + if true { + Foo { bar: 42 }; + } else { + Foo { bar: 43 }; + } + + if true { + (); + } else { + () + } + + if true { + 0..10; + } else { + 0..=10; + } + + if true { + foo(); + foo(); + } else { + foo(); + } + + let _ = if true { + 0.0 + } else { + //~ ERROR same body as `if` block + 0.0 + }; + + let _ = if true { + -0.0 + } else { + //~ ERROR same body as `if` block + -0.0 + }; + + let _ = if true { 0.0 } else { -0.0 }; + + // Different NaNs + let _ = if true { 0.0 / 0.0 } else { f32::NAN }; + + if true { + foo(); + } + + let _ = if true { + 42 + } else { + //~ ERROR same body as `if` block + 42 + }; + + if true { + let bar = if true { 42 } else { 43 }; + + while foo() { + break; + } + bar + 1; + } else { + //~ ERROR same body as `if` block + let bar = if true { 42 } else { 43 }; + + while foo() { + break; + } + bar + 1; + } + + if true { + let _ = match 42 { + 42 => 1, + a if a > 0 => 2, + 10..=15 => 3, + _ => 4, + }; + } else if false { + foo(); + } else if foo() { + let _ = match 42 { + 42 => 1, + a if a > 0 => 2, + 10..=15 => 3, + _ => 4, + }; + } +} + +// Issue #2423. This was causing an ICE. +fn func() { + if true { + f(&[0; 62]); + f(&[0; 4]); + f(&[0; 3]); + } else { + f(&[0; 62]); + f(&[0; 6]); + f(&[0; 6]); + } +} + +fn f(val: &[u8]) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_same_then_else.stderr b/src/tools/clippy/tests/ui/if_same_then_else.stderr new file mode 100644 index 0000000000000..d9fdf06fa8b73 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else.stderr @@ -0,0 +1,112 @@ +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:28:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | Foo { bar: 42 }; +LL | | 0..10; +... | +LL | | foo(); +LL | | } + | |_____^ + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else.rs:20:13 + | +LL | if true { + | _____________^ +LL | | Foo { bar: 42 }; +LL | | 0..10; +LL | | ..; +... | +LL | | foo(); +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:66:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | 0.0 +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:64:21 + | +LL | let _ = if true { + | _____________________^ +LL | | 0.0 +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:73:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | -0.0 +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:71:21 + | +LL | let _ = if true { + | _____________________^ +LL | | -0.0 +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:89:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | 42 +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:87:21 + | +LL | let _ = if true { + | _____________________^ +LL | | 42 +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:101:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | let bar = if true { 42 } else { 43 }; +LL | | +... | +LL | | bar + 1; +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:94:13 + | +LL | if true { + | _____________^ +LL | | let bar = if true { 42 } else { 43 }; +LL | | +LL | | while foo() { +... | +LL | | bar + 1; +LL | | } else { + | |_____^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs new file mode 100644 index 0000000000000..3cc21809264f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs @@ -0,0 +1,139 @@ +#![warn(clippy::if_same_then_else)] +#![allow( + clippy::blacklisted_name, + clippy::collapsible_if, + clippy::ifs_same_cond, + clippy::needless_return +)] + +fn if_same_then_else2() -> Result<&'static str, ()> { + if true { + for _ in &[42] { + let foo: &Option<_> = &Some::(42); + if true { + break; + } else { + continue; + } + } + } else { + //~ ERROR same body as `if` block + for _ in &[42] { + let foo: &Option<_> = &Some::(42); + if true { + break; + } else { + continue; + } + } + } + + if true { + if let Some(a) = Some(42) {} + } else { + //~ ERROR same body as `if` block + if let Some(a) = Some(42) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + //~ ERROR same body as `if` block + if let (1, .., 3) = (1, 2, 3) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + if let (.., 3) = (1, 2, 3) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + if let (.., 4) = (1, 2, 3) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + if let (.., 1, 3) = (1, 2, 3) {} + } + + if true { + if let Some(42) = None {} + } else { + if let Option::Some(42) = None {} + } + + if true { + if let Some(42) = None:: {} + } else { + if let Some(42) = None {} + } + + if true { + if let Some(42) = None:: {} + } else { + if let Some(42) = None:: {} + } + + if true { + if let Some(a) = Some(42) {} + } else { + if let Some(a) = Some(43) {} + } + + // Same NaNs + let _ = if true { + f32::NAN + } else { + //~ ERROR same body as `if` block + f32::NAN + }; + + if true { + Ok("foo")?; + } else { + //~ ERROR same body as `if` block + Ok("foo")?; + } + + if true { + let foo = ""; + return Ok(&foo[0..]); + } else if false { + let foo = "bar"; + return Ok(&foo[0..]); + } else { + let foo = ""; + return Ok(&foo[0..]); + } + + if true { + let foo = ""; + return Ok(&foo[0..]); + } else if false { + let foo = "bar"; + return Ok(&foo[0..]); + } else if true { + let foo = ""; + return Ok(&foo[0..]); + } else { + let foo = ""; + return Ok(&foo[0..]); + } + + // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559. + if true { + let foo = ""; + let (x, y) = (1, 2); + return Ok(&foo[x..y]); + } else { + let foo = ""; + let (y, x) = (1, 2); + return Ok(&foo[x..y]); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr new file mode 100644 index 0000000000000..f5d087fe12838 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr @@ -0,0 +1,125 @@ +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:19:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | for _ in &[42] { +LL | | let foo: &Option<_> = &Some::(42); +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else2.rs:10:13 + | +LL | if true { + | _____________^ +LL | | for _ in &[42] { +LL | | let foo: &Option<_> = &Some::(42); +LL | | if true { +... | +LL | | } +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:33:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | if let Some(a) = Some(42) {} +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:31:13 + | +LL | if true { + | _____________^ +LL | | if let Some(a) = Some(42) {} +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:40:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:38:13 + | +LL | if true { + | _____________^ +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:90:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | f32::NAN +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:88:21 + | +LL | let _ = if true { + | _____________________^ +LL | | f32::NAN +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:97:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | Ok("foo")?; +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:95:13 + | +LL | if true { + | _____________^ +LL | | Ok("foo")?; +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:122:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:119:20 + | +LL | } else if true { + | ____________________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } else { + | |_____^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs new file mode 100644 index 0000000000000..80e9839ff40bd --- /dev/null +++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs @@ -0,0 +1,46 @@ +#![warn(clippy::ifs_same_cond)] +#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks + +fn ifs_same_cond() { + let a = 0; + let b = false; + + if b { + } else if b { + //~ ERROR ifs same condition + } + + if a == 1 { + } else if a == 1 { + //~ ERROR ifs same condition + } + + if 2 * a == 1 { + } else if 2 * a == 2 { + } else if 2 * a == 1 { + //~ ERROR ifs same condition + } else if a == 1 { + } + + // See #659 + if cfg!(feature = "feature1-659") { + 1 + } else if cfg!(feature = "feature2-659") { + 2 + } else { + 3 + }; + + let mut v = vec![1]; + if v.pop() == None { + // ok, functions + } else if v.pop() == None { + } + + if v.len() == 42 { + // ok, functions + } else if v.len() == 42 { + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr new file mode 100644 index 0000000000000..0c8f49b8687f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr @@ -0,0 +1,39 @@ +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:9:15 + | +LL | } else if b { + | ^ + | + = note: `-D clippy::ifs-same-cond` implied by `-D warnings` +note: same as this + --> $DIR/ifs_same_cond.rs:8:8 + | +LL | if b { + | ^ + +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:14:15 + | +LL | } else if a == 1 { + | ^^^^^^ + | +note: same as this + --> $DIR/ifs_same_cond.rs:13:8 + | +LL | if a == 1 { + | ^^^^^^ + +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:20:15 + | +LL | } else if 2 * a == 1 { + | ^^^^^^^^^^ + | +note: same as this + --> $DIR/ifs_same_cond.rs:18:8 + | +LL | if 2 * a == 1 { + | ^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs new file mode 100644 index 0000000000000..1c46e3a533782 --- /dev/null +++ b/src/tools/clippy/tests/ui/impl.rs @@ -0,0 +1,36 @@ +#![allow(dead_code)] +#![warn(clippy::multiple_inherent_impl)] + +struct MyStruct; + +impl MyStruct { + fn first() {} +} + +impl MyStruct { + fn second() {} +} + +impl<'a> MyStruct { + fn lifetimed() {} +} + +mod submod { + struct MyStruct; + impl MyStruct { + fn other() {} + } + + impl super::MyStruct { + fn third() {} + } +} + +use std::fmt; +impl fmt::Debug for MyStruct { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "MyStruct {{ }}") + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/impl.stderr b/src/tools/clippy/tests/ui/impl.stderr new file mode 100644 index 0000000000000..585d32845d290 --- /dev/null +++ b/src/tools/clippy/tests/ui/impl.stderr @@ -0,0 +1,35 @@ +error: Multiple implementations of this structure + --> $DIR/impl.rs:10:1 + | +LL | / impl MyStruct { +LL | | fn second() {} +LL | | } + | |_^ + | + = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings` +note: First implementation here + --> $DIR/impl.rs:6:1 + | +LL | / impl MyStruct { +LL | | fn first() {} +LL | | } + | |_^ + +error: Multiple implementations of this structure + --> $DIR/impl.rs:24:5 + | +LL | / impl super::MyStruct { +LL | | fn third() {} +LL | | } + | |_____^ + | +note: First implementation here + --> $DIR/impl.rs:6:1 + | +LL | / impl MyStruct { +LL | | fn first() {} +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs new file mode 100644 index 0000000000000..fdcc9a33f55fe --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_hasher.rs @@ -0,0 +1,99 @@ +// aux-build:implicit_hasher_macros.rs +#![deny(clippy::implicit_hasher)] +#![allow(unused)] + +#[macro_use] +extern crate implicit_hasher_macros; + +use std::cmp::Eq; +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasher, Hash}; + +pub trait Foo: Sized { + fn make() -> (Self, Self); +} + +impl Foo for HashMap { + fn make() -> (Self, Self) { + // OK, don't suggest to modify these + let _: HashMap = HashMap::new(); + let _: HashSet = HashSet::new(); + + (HashMap::new(), HashMap::with_capacity(10)) + } +} +impl Foo for (HashMap,) { + fn make() -> (Self, Self) { + ((HashMap::new(),), (HashMap::with_capacity(10),)) + } +} +impl Foo for HashMap { + fn make() -> (Self, Self) { + (HashMap::new(), HashMap::with_capacity(10)) + } +} + +impl Foo for HashMap { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} +impl Foo for HashMap { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} + +impl Foo for HashSet { + fn make() -> (Self, Self) { + (HashSet::new(), HashSet::with_capacity(10)) + } +} +impl Foo for HashSet { + fn make() -> (Self, Self) { + (HashSet::new(), HashSet::with_capacity(10)) + } +} + +impl Foo for HashSet { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} +impl Foo for HashSet { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} + +pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} + +macro_rules! gen { + (impl) => { + impl Foo for HashMap { + fn make() -> (Self, Self) { + (HashMap::new(), HashMap::with_capacity(10)) + } + } + }; + + (fn $name:ident) => { + pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} + }; +} +#[rustfmt::skip] +gen!(impl); +gen!(fn bar); + +// When the macro is in a different file, the suggestion spans can't be combined properly +// and should not cause an ICE +// See #2707 +#[macro_use] +#[path = "../auxiliary/test_macro.rs"] +pub mod test_macro; +__implicit_hasher_test_macro!(impl for HashMap where V: test_macro::A); + +// #4260 +implicit_hasher_fn!(); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr new file mode 100644 index 0000000000000..2b06d661772d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr @@ -0,0 +1,153 @@ +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:16:35 + | +LL | impl Foo for HashMap { + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/implicit_hasher.rs:2:9 + | +LL | #![deny(clippy::implicit_hasher)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: consider adding a type parameter + | +LL | impl Foo for HashMap { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:25:36 + | +LL | impl Foo for (HashMap,) { + | ^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl Foo for (HashMap,) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),)) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:30:19 + | +LL | impl Foo for HashMap { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl Foo for HashMap { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:47:32 + | +LL | impl Foo for HashSet { + | ^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl Foo for HashSet { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:52:19 + | +LL | impl Foo for HashSet { + | ^^^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl Foo for HashSet { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:69:23 + | +LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:69:53 + | +LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:73:43 + | +LL | impl Foo for HashMap { + | ^^^^^^^^^^^^^ +... +LL | gen!(impl); + | ----------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider adding a type parameter + | +LL | impl Foo for HashMap { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:81:33 + | +LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^^^^^^ +... +LL | gen!(fn bar); + | ------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider adding a type parameter + | +LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:81:63 + | +LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^ +... +LL | gen!(fn bar); + | ------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider adding a type parameter + | +LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_return.fixed b/src/tools/clippy/tests/ui/implicit_return.fixed new file mode 100644 index 0000000000000..9066dc3fedfd6 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_return.fixed @@ -0,0 +1,101 @@ +// run-rustfix + +#![warn(clippy::implicit_return)] +#![allow(clippy::needless_return, unused)] + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + + return true +} + +#[allow(clippy::needless_bool)] +fn test_if_block() -> bool { + if true { + return true + } else { + return false + } +} + +#[rustfmt::skip] +fn test_match(x: bool) -> bool { + match x { + true => return false, + false => { return true }, + } +} + +#[allow(clippy::needless_return)] +fn test_match_with_unreachable(x: bool) -> bool { + match x { + true => return false, + false => unreachable!(), + } +} + +#[allow(clippy::never_loop)] +fn test_loop() -> bool { + loop { + return true; + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_block() -> bool { + loop { + { + return true; + } + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_nests() -> bool { + loop { + if true { + return true; + } else { + let _ = true; + } + } +} + +#[allow(clippy::redundant_pattern_matching)] +fn test_loop_with_if_let() -> bool { + loop { + if let Some(x) = Some(true) { + return x; + } + } +} + +fn test_closure() { + #[rustfmt::skip] + let _ = || { return true }; + let _ = || return true; +} + +fn test_panic() -> bool { + panic!() +} + +fn test_return_macro() -> String { + return format!("test {}", "test") +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_if_block(); + let _ = test_match(true); + let _ = test_match_with_unreachable(true); + let _ = test_loop(); + let _ = test_loop_with_block(); + let _ = test_loop_with_nests(); + let _ = test_loop_with_if_let(); + test_closure(); + let _ = test_return_macro(); +} diff --git a/src/tools/clippy/tests/ui/implicit_return.rs b/src/tools/clippy/tests/ui/implicit_return.rs new file mode 100644 index 0000000000000..c0d70ecf502ed --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_return.rs @@ -0,0 +1,101 @@ +// run-rustfix + +#![warn(clippy::implicit_return)] +#![allow(clippy::needless_return, unused)] + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + + true +} + +#[allow(clippy::needless_bool)] +fn test_if_block() -> bool { + if true { + true + } else { + false + } +} + +#[rustfmt::skip] +fn test_match(x: bool) -> bool { + match x { + true => false, + false => { true }, + } +} + +#[allow(clippy::needless_return)] +fn test_match_with_unreachable(x: bool) -> bool { + match x { + true => return false, + false => unreachable!(), + } +} + +#[allow(clippy::never_loop)] +fn test_loop() -> bool { + loop { + break true; + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_block() -> bool { + loop { + { + break true; + } + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_nests() -> bool { + loop { + if true { + break true; + } else { + let _ = true; + } + } +} + +#[allow(clippy::redundant_pattern_matching)] +fn test_loop_with_if_let() -> bool { + loop { + if let Some(x) = Some(true) { + return x; + } + } +} + +fn test_closure() { + #[rustfmt::skip] + let _ = || { true }; + let _ = || true; +} + +fn test_panic() -> bool { + panic!() +} + +fn test_return_macro() -> String { + format!("test {}", "test") +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_if_block(); + let _ = test_match(true); + let _ = test_match_with_unreachable(true); + let _ = test_loop(); + let _ = test_loop_with_block(); + let _ = test_loop_with_nests(); + let _ = test_loop_with_if_let(); + test_closure(); + let _ = test_return_macro(); +} diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr new file mode 100644 index 0000000000000..fb2ec90276454 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_return.stderr @@ -0,0 +1,70 @@ +error: missing `return` statement + --> $DIR/implicit_return.rs:12:5 + | +LL | true + | ^^^^ help: add `return` as shown: `return true` + | + = note: `-D clippy::implicit-return` implied by `-D warnings` + +error: missing `return` statement + --> $DIR/implicit_return.rs:18:9 + | +LL | true + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:20:9 + | +LL | false + | ^^^^^ help: add `return` as shown: `return false` + +error: missing `return` statement + --> $DIR/implicit_return.rs:27:17 + | +LL | true => false, + | ^^^^^ help: add `return` as shown: `return false` + +error: missing `return` statement + --> $DIR/implicit_return.rs:28:20 + | +LL | false => { true }, + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:43:9 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:51:13 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:60:13 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:78:18 + | +LL | let _ = || { true }; + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:79:16 + | +LL | let _ = || true; + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:87:5 + | +LL | format!("test {}", "test") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed new file mode 100644 index 0000000000000..859765d08a70b --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed @@ -0,0 +1,160 @@ +// run-rustfix +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] +#![warn(clippy::implicit_saturating_sub)] + +fn main() { + // Tests for unsigned integers + + let end_8: u8 = 10; + let start_8: u8 = 5; + let mut u_8: u8 = end_8 - start_8; + + // Lint + u_8 = u_8.saturating_sub(1); + + match end_8 { + 10 => { + // Lint + u_8 = u_8.saturating_sub(1); + }, + 11 => u_8 += 1, + _ => u_8 = 0, + } + + let end_16: u16 = 40; + let start_16: u16 = 35; + + let mut u_16: u16 = end_16 - start_16; + + // Lint + u_16 = u_16.saturating_sub(1); + + let mut end_32: u32 = 7010; + let mut start_32: u32 = 7000; + + let mut u_32: u32 = end_32 - start_32; + + // Lint + u_32 = u_32.saturating_sub(1); + + // No Lint + if u_32 > 0 { + u_16 += 1; + } + + // No Lint + if u_32 != 0 { + end_32 -= 1; + start_32 += 1; + } + + let mut end_64: u64 = 75001; + let mut start_64: u64 = 75000; + + let mut u_64: u64 = end_64 - start_64; + + // Lint + u_64 = u_64.saturating_sub(1); + + // Lint + u_64 = u_64.saturating_sub(1); + + // Lint + u_64 = u_64.saturating_sub(1); + + // No Lint + if u_64 >= 1 { + u_64 -= 1; + } + + // No Lint + if u_64 > 0 { + end_64 -= 1; + } + + // Tests for usize + let end_usize: usize = 8054; + let start_usize: usize = 8050; + + let mut u_usize: usize = end_usize - start_usize; + + // Lint + u_usize = u_usize.saturating_sub(1); + + // Tests for signed integers + + let endi_8: i8 = 10; + let starti_8: i8 = 50; + + let mut i_8: i8 = endi_8 - starti_8; + + // Lint + i_8 = i_8.saturating_sub(1); + + // Lint + i_8 = i_8.saturating_sub(1); + + // Lint + i_8 = i_8.saturating_sub(1); + + // Lint + i_8 = i_8.saturating_sub(1); + + let endi_16: i16 = 45; + let starti_16: i16 = 44; + + let mut i_16: i16 = endi_16 - starti_16; + + // Lint + i_16 = i_16.saturating_sub(1); + + // Lint + i_16 = i_16.saturating_sub(1); + + // Lint + i_16 = i_16.saturating_sub(1); + + // Lint + i_16 = i_16.saturating_sub(1); + + let endi_32: i32 = 45; + let starti_32: i32 = 44; + + let mut i_32: i32 = endi_32 - starti_32; + + // Lint + i_32 = i_32.saturating_sub(1); + + // Lint + i_32 = i_32.saturating_sub(1); + + // Lint + i_32 = i_32.saturating_sub(1); + + // Lint + i_32 = i_32.saturating_sub(1); + + let endi_64: i64 = 45; + let starti_64: i64 = 44; + + let mut i_64: i64 = endi_64 - starti_64; + + // Lint + i_64 = i_64.saturating_sub(1); + + // Lint + i_64 = i_64.saturating_sub(1); + + // Lint + i_64 = i_64.saturating_sub(1); + + // No Lint + if i_64 > 0 { + i_64 -= 1; + } + + // No Lint + if i_64 != 0 { + i_64 -= 1; + } +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs new file mode 100644 index 0000000000000..24cb216e79bf3 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs @@ -0,0 +1,206 @@ +// run-rustfix +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] +#![warn(clippy::implicit_saturating_sub)] + +fn main() { + // Tests for unsigned integers + + let end_8: u8 = 10; + let start_8: u8 = 5; + let mut u_8: u8 = end_8 - start_8; + + // Lint + if u_8 > 0 { + u_8 = u_8 - 1; + } + + match end_8 { + 10 => { + // Lint + if u_8 > 0 { + u_8 -= 1; + } + }, + 11 => u_8 += 1, + _ => u_8 = 0, + } + + let end_16: u16 = 40; + let start_16: u16 = 35; + + let mut u_16: u16 = end_16 - start_16; + + // Lint + if u_16 > 0 { + u_16 -= 1; + } + + let mut end_32: u32 = 7010; + let mut start_32: u32 = 7000; + + let mut u_32: u32 = end_32 - start_32; + + // Lint + if u_32 != 0 { + u_32 -= 1; + } + + // No Lint + if u_32 > 0 { + u_16 += 1; + } + + // No Lint + if u_32 != 0 { + end_32 -= 1; + start_32 += 1; + } + + let mut end_64: u64 = 75001; + let mut start_64: u64 = 75000; + + let mut u_64: u64 = end_64 - start_64; + + // Lint + if u_64 > 0 { + u_64 -= 1; + } + + // Lint + if 0 < u_64 { + u_64 -= 1; + } + + // Lint + if 0 != u_64 { + u_64 -= 1; + } + + // No Lint + if u_64 >= 1 { + u_64 -= 1; + } + + // No Lint + if u_64 > 0 { + end_64 -= 1; + } + + // Tests for usize + let end_usize: usize = 8054; + let start_usize: usize = 8050; + + let mut u_usize: usize = end_usize - start_usize; + + // Lint + if u_usize > 0 { + u_usize -= 1; + } + + // Tests for signed integers + + let endi_8: i8 = 10; + let starti_8: i8 = 50; + + let mut i_8: i8 = endi_8 - starti_8; + + // Lint + if i_8 > i8::MIN { + i_8 -= 1; + } + + // Lint + if i_8 > i8::min_value() { + i_8 -= 1; + } + + // Lint + if i_8 != i8::MIN { + i_8 -= 1; + } + + // Lint + if i_8 != i8::min_value() { + i_8 -= 1; + } + + let endi_16: i16 = 45; + let starti_16: i16 = 44; + + let mut i_16: i16 = endi_16 - starti_16; + + // Lint + if i_16 > i16::MIN { + i_16 -= 1; + } + + // Lint + if i_16 > i16::min_value() { + i_16 -= 1; + } + + // Lint + if i_16 != i16::MIN { + i_16 -= 1; + } + + // Lint + if i_16 != i16::min_value() { + i_16 -= 1; + } + + let endi_32: i32 = 45; + let starti_32: i32 = 44; + + let mut i_32: i32 = endi_32 - starti_32; + + // Lint + if i_32 > i32::MIN { + i_32 -= 1; + } + + // Lint + if i_32 > i32::min_value() { + i_32 -= 1; + } + + // Lint + if i_32 != i32::MIN { + i_32 -= 1; + } + + // Lint + if i_32 != i32::min_value() { + i_32 -= 1; + } + + let endi_64: i64 = 45; + let starti_64: i64 = 44; + + let mut i_64: i64 = endi_64 - starti_64; + + // Lint + if i64::min_value() < i_64 { + i_64 -= 1; + } + + // Lint + if i64::MIN != i_64 { + i_64 -= 1; + } + + // Lint + if i64::MIN < i_64 { + i_64 -= 1; + } + + // No Lint + if i_64 > 0 { + i_64 -= 1; + } + + // No Lint + if i_64 != 0 { + i_64 -= 1; + } +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr new file mode 100644 index 0000000000000..a8ba870b1dda6 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr @@ -0,0 +1,188 @@ +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:13:5 + | +LL | / if u_8 > 0 { +LL | | u_8 = u_8 - 1; +LL | | } + | |_____^ help: try: `u_8 = u_8.saturating_sub(1);` + | + = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:20:13 + | +LL | / if u_8 > 0 { +LL | | u_8 -= 1; +LL | | } + | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:34:5 + | +LL | / if u_16 > 0 { +LL | | u_16 -= 1; +LL | | } + | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:44:5 + | +LL | / if u_32 != 0 { +LL | | u_32 -= 1; +LL | | } + | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:65:5 + | +LL | / if u_64 > 0 { +LL | | u_64 -= 1; +LL | | } + | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:70:5 + | +LL | / if 0 < u_64 { +LL | | u_64 -= 1; +LL | | } + | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:75:5 + | +LL | / if 0 != u_64 { +LL | | u_64 -= 1; +LL | | } + | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:96:5 + | +LL | / if u_usize > 0 { +LL | | u_usize -= 1; +LL | | } + | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:108:5 + | +LL | / if i_8 > i8::MIN { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:113:5 + | +LL | / if i_8 > i8::min_value() { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:118:5 + | +LL | / if i_8 != i8::MIN { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:123:5 + | +LL | / if i_8 != i8::min_value() { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:133:5 + | +LL | / if i_16 > i16::MIN { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:138:5 + | +LL | / if i_16 > i16::min_value() { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:143:5 + | +LL | / if i_16 != i16::MIN { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:148:5 + | +LL | / if i_16 != i16::min_value() { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:158:5 + | +LL | / if i_32 > i32::MIN { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:163:5 + | +LL | / if i_32 > i32::min_value() { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:168:5 + | +LL | / if i_32 != i32::MIN { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:173:5 + | +LL | / if i_32 != i32::min_value() { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:183:5 + | +LL | / if i64::min_value() < i_64 { +LL | | i_64 -= 1; +LL | | } + | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:188:5 + | +LL | / if i64::MIN != i_64 { +LL | | i_64 -= 1; +LL | | } + | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:193:5 + | +LL | / if i64::MIN < i_64 { +LL | | i_64 -= 1; +LL | | } + | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed new file mode 100644 index 0000000000000..b75f10917df18 --- /dev/null +++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#[warn(clippy::inconsistent_digit_grouping)] +#[deny(clippy::unreadable_literal)] +#[allow(unused_variables, clippy::excessive_precision)] +fn main() { + macro_rules! mac1 { + () => { + 1_23_456 + }; + } + macro_rules! mac2 { + () => { + 1_234.5678_f32 + }; + } + + let good = ( + 123, + 1_234, + 1_2345_6789, + 123_f32, + 1_234.12_f32, + 1_234.123_4_f32, + 1.123_456_7_f32, + ); + let bad = (123_456, 12_345_678, 1_234_567, 1_234.567_8_f32, 1.234_567_8_f32); + + // Test padding + let _ = 0x0010_0000; + let _ = 0x0100_0000; + let _ = 0x1000_0000; + let _ = 0x0001_0000_0000_u64; + + // Test suggestion when fraction has no digits + let _: f32 = 123_456.; + + // Test UUID formatted literal + let _: u128 = 0x12345678_1234_1234_1234_123456789012; + + // Ignore literals in macros + let _ = mac1!(); + let _ = mac2!(); +} diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs new file mode 100644 index 0000000000000..79ce38be19bd3 --- /dev/null +++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs @@ -0,0 +1,43 @@ +// run-rustfix +#[warn(clippy::inconsistent_digit_grouping)] +#[deny(clippy::unreadable_literal)] +#[allow(unused_variables, clippy::excessive_precision)] +fn main() { + macro_rules! mac1 { + () => { + 1_23_456 + }; + } + macro_rules! mac2 { + () => { + 1_234.5678_f32 + }; + } + + let good = ( + 123, + 1_234, + 1_2345_6789, + 123_f32, + 1_234.12_f32, + 1_234.123_4_f32, + 1.123_456_7_f32, + ); + let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + + // Test padding + let _ = 0x100000; + let _ = 0x1000000; + let _ = 0x10000000; + let _ = 0x100000000_u64; + + // Test suggestion when fraction has no digits + let _: f32 = 1_23_456.; + + // Test UUID formatted literal + let _: u128 = 0x12345678_1234_1234_1234_123456789012; + + // Ignore literals in macros + let _ = mac1!(); + let _ = mac2!(); +} diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr new file mode 100644 index 0000000000000..b8ac915546200 --- /dev/null +++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr @@ -0,0 +1,70 @@ +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:16 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^ help: consider: `123_456` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:26 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^^^ help: consider: `12_345_678` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:38 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^ help: consider: `1_234_567` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:48 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^^^^^^^ help: consider: `1_234.567_8_f32` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:64 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^^^^^^^ help: consider: `1.234_567_8_f32` + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:29:13 + | +LL | let _ = 0x100000; + | ^^^^^^^^ help: consider: `0x0010_0000` + | +note: the lint level is defined here + --> $DIR/inconsistent_digit_grouping.rs:3:8 + | +LL | #[deny(clippy::unreadable_literal)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:30:13 + | +LL | let _ = 0x1000000; + | ^^^^^^^^^ help: consider: `0x0100_0000` + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:31:13 + | +LL | let _ = 0x10000000; + | ^^^^^^^^^^ help: consider: `0x1000_0000` + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:32:13 + | +LL | let _ = 0x100000000_u64; + | ^^^^^^^^^^^^^^^ help: consider: `0x0001_0000_0000_u64` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:35:18 + | +LL | let _: f32 = 1_23_456.; + | ^^^^^^^^^ help: consider: `123_456.` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs new file mode 100644 index 0000000000000..000d5269930ba --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs @@ -0,0 +1,31 @@ +#![warn(clippy::indexing_slicing)] +// We also check the out_of_bounds_indexing lint here, because it lints similar things and +// we want to avoid false positives. +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + x[index]; + x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + + x[0]; // Ok, should not produce stderr. + x[3]; // Ok, should not produce stderr. + + let y = &x; + y[0]; + + let v = vec![0; 5]; + v[0]; + v[10]; + v[1 << 3]; + + const N: usize = 15; // Out of bounds + const M: usize = 3; // In bounds + x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[M]; // Ok, should not produce stderr. + v[N]; + v[M]; +} diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr new file mode 100644 index 0000000000000..ac5f0d0a39e89 --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -0,0 +1,79 @@ +error: this operation will panic at runtime + --> $DIR/indexing_slicing_index.rs:11:5 + | +LL | x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + | ^^^^ index out of bounds: the len is 4 but the index is 4 + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/indexing_slicing_index.rs:12:5 + | +LL | x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + | ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8 + +error: this operation will panic at runtime + --> $DIR/indexing_slicing_index.rs:27:5 + | +LL | x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + | ^^^^ index out of bounds: the len is 4 but the index is 15 + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:10:5 + | +LL | x[index]; + | ^^^^^^^^ + | + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:18:5 + | +LL | y[0]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:21:5 + | +LL | v[0]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:22:5 + | +LL | v[10]; + | ^^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:23:5 + | +LL | v[1 << 3]; + | ^^^^^^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:29:5 + | +LL | v[N]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:30:5 + | +LL | v[M]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs new file mode 100644 index 0000000000000..7b107db39f022 --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs @@ -0,0 +1,37 @@ +#![warn(clippy::indexing_slicing)] +// We also check the out_of_bounds_indexing lint here, because it lints similar things and +// we want to avoid false positives. +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + let index_from: usize = 2; + let index_to: usize = 3; + &x[index..]; + &x[..index]; + &x[index_from..index_to]; + &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. + &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. + &x[0..][..3]; + &x[1..][..5]; + + &x[0..].get(..3); // Ok, should not produce stderr. + &x[0..3]; // Ok, should not produce stderr. + + let y = &x; + &y[1..2]; + &y[0..=4]; + &y[..=4]; + + &y[..]; // Ok, should not produce stderr. + + let v = vec![0; 5]; + &v[10..100]; + &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. + &v[10..]; + &v[..100]; + + &v[..]; // Ok, should not produce stderr. +} diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr new file mode 100644 index 0000000000000..ec6c157ac1a26 --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr @@ -0,0 +1,137 @@ +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:12:6 + | +LL | &x[index..]; + | ^^^^^^^^^^ + | + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + = help: Consider using `.get(n..)` or .get_mut(n..)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:13:6 + | +LL | &x[..index]; + | ^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:14:6 + | +LL | &x[index_from..index_to]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:15:6 + | +LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:15:6 + | +LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. + | ^^^^^^^^^^^^^^^ + | + = help: Consider using `.get(n..)` or .get_mut(n..)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:16:6 + | +LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. + | ^^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:16:8 + | +LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. + | ^ + | + = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:17:6 + | +LL | &x[0..][..3]; + | ^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:18:6 + | +LL | &x[1..][..5]; + | ^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:24:6 + | +LL | &y[1..2]; + | ^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:25:6 + | +LL | &y[0..=4]; + | ^^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:26:6 + | +LL | &y[..=4]; + | ^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:31:6 + | +LL | &v[10..100]; + | ^^^^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:32:6 + | +LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. + | ^^^^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:32:8 + | +LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. + | ^^ + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:33:6 + | +LL | &v[10..]; + | ^^^^^^^ + | + = help: Consider using `.get(n..)` or .get_mut(n..)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:34:6 + | +LL | &v[..100]; + | ^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.fixed b/src/tools/clippy/tests/ui/inefficient_to_string.fixed new file mode 100644 index 0000000000000..c972b9419ef76 --- /dev/null +++ b/src/tools/clippy/tests/ui/inefficient_to_string.fixed @@ -0,0 +1,31 @@ +// run-rustfix +#![deny(clippy::inefficient_to_string)] + +use std::borrow::Cow; + +fn main() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let rrrstr: &&&str = &rrstr; + let _: String = rstr.to_string(); + let _: String = (*rrstr).to_string(); + let _: String = (**rrrstr).to_string(); + + let string: String = String::from("hello"); + let rstring: &String = &string; + let rrstring: &&String = &rstring; + let rrrstring: &&&String = &rrstring; + let _: String = string.to_string(); + let _: String = rstring.to_string(); + let _: String = (*rrstring).to_string(); + let _: String = (**rrrstring).to_string(); + + let cow: Cow<'_, str> = Cow::Borrowed("hello"); + let rcow: &Cow<'_, str> = &cow; + let rrcow: &&Cow<'_, str> = &rcow; + let rrrcow: &&&Cow<'_, str> = &rrcow; + let _: String = cow.to_string(); + let _: String = rcow.to_string(); + let _: String = (*rrcow).to_string(); + let _: String = (**rrrcow).to_string(); +} diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.rs b/src/tools/clippy/tests/ui/inefficient_to_string.rs new file mode 100644 index 0000000000000..acdc55aa0d69d --- /dev/null +++ b/src/tools/clippy/tests/ui/inefficient_to_string.rs @@ -0,0 +1,31 @@ +// run-rustfix +#![deny(clippy::inefficient_to_string)] + +use std::borrow::Cow; + +fn main() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let rrrstr: &&&str = &rrstr; + let _: String = rstr.to_string(); + let _: String = rrstr.to_string(); + let _: String = rrrstr.to_string(); + + let string: String = String::from("hello"); + let rstring: &String = &string; + let rrstring: &&String = &rstring; + let rrrstring: &&&String = &rrstring; + let _: String = string.to_string(); + let _: String = rstring.to_string(); + let _: String = rrstring.to_string(); + let _: String = rrrstring.to_string(); + + let cow: Cow<'_, str> = Cow::Borrowed("hello"); + let rcow: &Cow<'_, str> = &cow; + let rrcow: &&Cow<'_, str> = &rcow; + let rrrcow: &&&Cow<'_, str> = &rrcow; + let _: String = cow.to_string(); + let _: String = rcow.to_string(); + let _: String = rrcow.to_string(); + let _: String = rrrcow.to_string(); +} diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.stderr b/src/tools/clippy/tests/ui/inefficient_to_string.stderr new file mode 100644 index 0000000000000..4be46161e8b74 --- /dev/null +++ b/src/tools/clippy/tests/ui/inefficient_to_string.stderr @@ -0,0 +1,55 @@ +error: calling `to_string` on `&&str` + --> $DIR/inefficient_to_string.rs:11:21 + | +LL | let _: String = rrstr.to_string(); + | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()` + | +note: the lint level is defined here + --> $DIR/inefficient_to_string.rs:2:9 + | +LL | #![deny(clippy::inefficient_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: `&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` + +error: calling `to_string` on `&&&str` + --> $DIR/inefficient_to_string.rs:12:21 + | +LL | let _: String = rrrstr.to_string(); + | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()` + | + = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` + +error: calling `to_string` on `&&std::string::String` + --> $DIR/inefficient_to_string.rs:20:21 + | +LL | let _: String = rrstring.to_string(); + | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()` + | + = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` + +error: calling `to_string` on `&&&std::string::String` + --> $DIR/inefficient_to_string.rs:21:21 + | +LL | let _: String = rrrstring.to_string(); + | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()` + | + = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` + +error: calling `to_string` on `&&std::borrow::Cow` + --> $DIR/inefficient_to_string.rs:29:21 + | +LL | let _: String = rrcow.to_string(); + | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()` + | + = help: `&std::borrow::Cow` implements `ToString` through a slower blanket impl, but `std::borrow::Cow` has a fast specialization of `ToString` + +error: calling `to_string` on `&&&std::borrow::Cow` + --> $DIR/inefficient_to_string.rs:30:21 + | +LL | let _: String = rrrcow.to_string(); + | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()` + | + = help: `&&std::borrow::Cow` implements `ToString` through a slower blanket impl, but `std::borrow::Cow` has a fast specialization of `ToString` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed new file mode 100644 index 0000000000000..b8e40d995531a --- /dev/null +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed @@ -0,0 +1,112 @@ +// run-rustfix +#![feature(exhaustive_patterns, never_type)] +#![allow(dead_code, unreachable_code, unused_variables)] +#![allow(clippy::let_and_return)] + +enum SingleVariantEnum { + Variant(i32), +} + +struct TupleStruct(i32); + +enum EmptyEnum {} + +macro_rules! match_enum { + ($param:expr) => { + let data = match $param { + SingleVariantEnum::Variant(i) => i, + }; + }; +} + +fn infallible_destructuring_match_enum() { + let wrapper = SingleVariantEnum::Variant(0); + + // This should lint! + let SingleVariantEnum::Variant(data) = wrapper; + + // This shouldn't (inside macro) + match_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + SingleVariantEnum::Variant(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + SingleVariantEnum::Variant(i) => -1, + }; + + let SingleVariantEnum::Variant(data) = wrapper; +} + +macro_rules! match_struct { + ($param:expr) => { + let data = match $param { + TupleStruct(i) => i, + }; + }; +} + +fn infallible_destructuring_match_struct() { + let wrapper = TupleStruct(0); + + // This should lint! + let TupleStruct(data) = wrapper; + + // This shouldn't (inside macro) + match_struct!(wrapper); + + // This shouldn't! + let data = match wrapper { + TupleStruct(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + TupleStruct(i) => -1, + }; + + let TupleStruct(data) = wrapper; +} + +macro_rules! match_never_enum { + ($param:expr) => { + let data = match $param { + Ok(i) => i, + }; + }; +} + +fn never_enum() { + let wrapper: Result = Ok(23); + + // This should lint! + let Ok(data) = wrapper; + + // This shouldn't (inside macro) + match_never_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + Ok(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + Ok(i) => -1, + }; + + let Ok(data) = wrapper; +} + +impl EmptyEnum { + fn match_on(&self) -> ! { + // The lint shouldn't pick this up, as `let` won't work here! + let data = match *self {}; + data + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.rs b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs new file mode 100644 index 0000000000000..106cd438b90e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs @@ -0,0 +1,118 @@ +// run-rustfix +#![feature(exhaustive_patterns, never_type)] +#![allow(dead_code, unreachable_code, unused_variables)] +#![allow(clippy::let_and_return)] + +enum SingleVariantEnum { + Variant(i32), +} + +struct TupleStruct(i32); + +enum EmptyEnum {} + +macro_rules! match_enum { + ($param:expr) => { + let data = match $param { + SingleVariantEnum::Variant(i) => i, + }; + }; +} + +fn infallible_destructuring_match_enum() { + let wrapper = SingleVariantEnum::Variant(0); + + // This should lint! + let data = match wrapper { + SingleVariantEnum::Variant(i) => i, + }; + + // This shouldn't (inside macro) + match_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + SingleVariantEnum::Variant(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + SingleVariantEnum::Variant(i) => -1, + }; + + let SingleVariantEnum::Variant(data) = wrapper; +} + +macro_rules! match_struct { + ($param:expr) => { + let data = match $param { + TupleStruct(i) => i, + }; + }; +} + +fn infallible_destructuring_match_struct() { + let wrapper = TupleStruct(0); + + // This should lint! + let data = match wrapper { + TupleStruct(i) => i, + }; + + // This shouldn't (inside macro) + match_struct!(wrapper); + + // This shouldn't! + let data = match wrapper { + TupleStruct(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + TupleStruct(i) => -1, + }; + + let TupleStruct(data) = wrapper; +} + +macro_rules! match_never_enum { + ($param:expr) => { + let data = match $param { + Ok(i) => i, + }; + }; +} + +fn never_enum() { + let wrapper: Result = Ok(23); + + // This should lint! + let data = match wrapper { + Ok(i) => i, + }; + + // This shouldn't (inside macro) + match_never_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + Ok(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + Ok(i) => -1, + }; + + let Ok(data) = wrapper; +} + +impl EmptyEnum { + fn match_on(&self) -> ! { + // The lint shouldn't pick this up, as `let` won't work here! + let data = match *self {}; + data + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr b/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr new file mode 100644 index 0000000000000..1b78db42014a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr @@ -0,0 +1,28 @@ +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:26:5 + | +LL | / let data = match wrapper { +LL | | SingleVariantEnum::Variant(i) => i, +LL | | }; + | |______^ help: try this: `let SingleVariantEnum::Variant(data) = wrapper;` + | + = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:58:5 + | +LL | / let data = match wrapper { +LL | | TupleStruct(i) => i, +LL | | }; + | |______^ help: try this: `let TupleStruct(data) = wrapper;` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:90:5 + | +LL | / let data = match wrapper { +LL | | Ok(i) => i, +LL | | }; + | |______^ help: try this: `let Ok(data) = wrapper;` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/infinite_iter.rs b/src/tools/clippy/tests/ui/infinite_iter.rs new file mode 100644 index 0000000000000..1fe688977659d --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_iter.rs @@ -0,0 +1,69 @@ +use std::iter::repeat; +fn square_is_lower_64(x: &u32) -> bool { + x * x < 64 +} + +#[allow(clippy::maybe_infinite_iter)] +#[deny(clippy::infinite_iter)] +fn infinite_iters() { + repeat(0_u8).collect::>(); // infinite iter + (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter + (0..8_u64).chain(0..).max(); // infinite iter + (0_usize..) + .chain([0usize, 1, 2].iter().cloned()) + .skip_while(|x| *x != 42) + .min(); // infinite iter + (0..8_u32) + .rev() + .cycle() + .map(|x| x + 1_u32) + .for_each(|x| println!("{}", x)); // infinite iter + (0..3_u32).flat_map(|x| x..).sum::(); // infinite iter + (0_usize..).flat_map(|x| 0..x).product::(); // infinite iter + (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter + (0..42_u64).by_ref().last(); // not an infinite, because ranges are double-ended + (0..).next(); // iterator is not exhausted +} + +#[deny(clippy::maybe_infinite_iter)] +fn potential_infinite_iters() { + (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter + repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter + (1..) + .scan(0, |state, x| { + *state += x; + Some(*state) + }) + .min(); // maybe infinite iter + (0..).find(|x| *x == 24); // maybe infinite iter + (0..).position(|x| x == 24); // maybe infinite iter + (0..).any(|x| x == 24); // maybe infinite iter + (0..).all(|x| x == 24); // maybe infinite iter + + (0..).zip(0..42).take_while(|&(x, _)| x != 42).count(); // not infinite + repeat(42).take_while(|x| *x == 42).next(); // iterator is not exhausted +} + +fn main() { + infinite_iters(); + potential_infinite_iters(); +} + +mod finite_collect { + use std::collections::HashSet; + use std::iter::FromIterator; + + struct C; + impl FromIterator for C { + fn from_iter>(iter: I) -> Self { + C + } + } + + fn check_collect() { + let _: HashSet = (0..).collect(); // Infinite iter + + // Some data structures don't collect infinitely, such as `ArrayVec` + let _: C = (0..).collect(); + } +} diff --git a/src/tools/clippy/tests/ui/infinite_iter.stderr b/src/tools/clippy/tests/ui/infinite_iter.stderr new file mode 100644 index 0000000000000..5f5e7ac9f253a --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_iter.stderr @@ -0,0 +1,109 @@ +error: infinite iteration detected + --> $DIR/infinite_iter.rs:9:5 + | +LL | repeat(0_u8).collect::>(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/infinite_iter.rs:7:8 + | +LL | #[deny(clippy::infinite_iter)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:10:5 + | +LL | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:11:5 + | +LL | (0..8_u64).chain(0..).max(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:16:5 + | +LL | / (0..8_u32) +LL | | .rev() +LL | | .cycle() +LL | | .map(|x| x + 1_u32) +LL | | .for_each(|x| println!("{}", x)); // infinite iter + | |________________________________________^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:22:5 + | +LL | (0_usize..).flat_map(|x| 0..x).product::(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:23:5 + | +LL | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:30:5 + | +LL | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/infinite_iter.rs:28:8 + | +LL | #[deny(clippy::maybe_infinite_iter)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:31:5 + | +LL | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:32:5 + | +LL | / (1..) +LL | | .scan(0, |state, x| { +LL | | *state += x; +LL | | Some(*state) +LL | | }) +LL | | .min(); // maybe infinite iter + | |______________^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:38:5 + | +LL | (0..).find(|x| *x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:39:5 + | +LL | (0..).position(|x| x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:40:5 + | +LL | (0..).any(|x| x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:41:5 + | +LL | (0..).all(|x| x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:64:31 + | +LL | let _: HashSet = (0..).collect(); // Infinite iter + | ^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::infinite_iter)]` on by default + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs new file mode 100644 index 0000000000000..72591f12baf85 --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_loop.rs @@ -0,0 +1,206 @@ +fn fn_val(i: i32) -> i32 { + unimplemented!() +} +fn fn_constref(i: &i32) -> i32 { + unimplemented!() +} +fn fn_mutref(i: &mut i32) { + unimplemented!() +} +fn fooi() -> i32 { + unimplemented!() +} +fn foob() -> bool { + unimplemented!() +} + +#[allow(clippy::many_single_char_names)] +fn immutable_condition() { + // Should warn when all vars mentioned are immutable + let y = 0; + while y < 10 { + println!("KO - y is immutable"); + } + + let x = 0; + while y < 10 && x < 3 { + let mut k = 1; + k += 2; + println!("KO - x and y immutable"); + } + + let cond = false; + while !cond { + println!("KO - cond immutable"); + } + + let mut i = 0; + while y < 10 && i < 3 { + i += 1; + println!("OK - i is mutable"); + } + + let mut mut_cond = false; + while !mut_cond || cond { + mut_cond = true; + println!("OK - mut_cond is mutable"); + } + + while fooi() < x { + println!("OK - Fn call results may vary"); + } + + while foob() { + println!("OK - Fn call results may vary"); + } + + let mut a = 0; + let mut c = move || { + while a < 5 { + a += 1; + println!("OK - a is mutable"); + } + }; + c(); + + let mut tup = (0, 0); + while tup.0 < 5 { + tup.0 += 1; + println!("OK - tup.0 gets mutated") + } +} + +fn unused_var() { + // Should warn when a (mutable) var is not used in while body + let (mut i, mut j) = (0, 0); + + while i < 3 { + j = 3; + println!("KO - i not mentioned"); + } + + while i < 3 && j > 0 { + println!("KO - i and j not mentioned"); + } + + while i < 3 { + let mut i = 5; + fn_mutref(&mut i); + println!("KO - shadowed"); + } + + while i < 3 && j > 0 { + i = 5; + println!("OK - i in cond and mentioned"); + } +} + +fn used_immutable() { + let mut i = 0; + + while i < 3 { + fn_constref(&i); + println!("KO - const reference"); + } + + while i < 3 { + fn_val(i); + println!("KO - passed by value"); + } + + while i < 3 { + println!("OK - passed by mutable reference"); + fn_mutref(&mut i) + } + + while i < 3 { + fn_mutref(&mut i); + println!("OK - passed by mutable reference"); + } +} + +const N: i32 = 5; +const B: bool = false; + +fn consts() { + while false { + println!("Constants are not linted"); + } + + while B { + println!("Constants are not linted"); + } + + while N > 0 { + println!("Constants are not linted"); + } +} + +use std::cell::Cell; + +fn maybe_i_mutate(i: &Cell) { + unimplemented!() +} + +fn internally_mutable() { + let b = Cell::new(true); + + while b.get() { + // b cannot be silently coerced to `bool` + maybe_i_mutate(&b); + println!("OK - Method call within condition"); + } +} + +struct Counter { + count: usize, +} + +impl Counter { + fn inc(&mut self) { + self.count += 1; + } + + fn inc_n(&mut self, n: usize) { + while self.count < n { + self.inc(); + } + println!("OK - self borrowed mutably"); + } + + fn print_n(&self, n: usize) { + while self.count < n { + println!("KO - {} is not mutated", self.count); + } + } +} + +fn while_loop_with_break_and_return() { + let y = 0; + while y < 10 { + if y == 0 { + break; + } + println!("KO - loop contains break"); + } + + while y < 10 { + if y == 0 { + return; + } + println!("KO - loop contains return"); + } +} + +fn main() { + immutable_condition(); + unused_var(); + used_immutable(); + internally_mutable(); + + let mut c = Counter { count: 0 }; + c.inc_n(5); + c.print_n(2); + + while_loop_with_break_and_return(); +} diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr new file mode 100644 index 0000000000000..1fcb29eff18e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_loop.stderr @@ -0,0 +1,95 @@ +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:21:11 + | +LL | while y < 10 { + | ^^^^^^ + | + = note: `#[deny(clippy::while_immutable_condition)]` on by default + = 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 + --> $DIR/infinite_loop.rs:26:11 + | +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 + --> $DIR/infinite_loop.rs:33:11 + | +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 + --> $DIR/infinite_loop.rs:77:11 + | +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 + --> $DIR/infinite_loop.rs:82:11 + | +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 + --> $DIR/infinite_loop.rs:86:11 + | +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 + --> $DIR/infinite_loop.rs:101:11 + | +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 + --> $DIR/infinite_loop.rs:106:11 + | +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 + --> $DIR/infinite_loop.rs:172:15 + | +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 + --> $DIR/infinite_loop.rs:180:11 + | +LL | while y < 10 { + | ^^^^^^ + | + = note: this may lead to an infinite or to a never running loop + = note: this loop contains `return`s or `break`s + = help: rewrite it as `if cond { loop { } }` + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:187:11 + | +LL | while y < 10 { + | ^^^^^^ + | + = note: this may lead to an infinite or to a never running loop + = note: this loop contains `return`s or `break`s + = help: rewrite it as `if cond { loop { } }` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/inherent_to_string.rs b/src/tools/clippy/tests/ui/inherent_to_string.rs new file mode 100644 index 0000000000000..e6cf337d1bb1b --- /dev/null +++ b/src/tools/clippy/tests/ui/inherent_to_string.rs @@ -0,0 +1,96 @@ +#![warn(clippy::inherent_to_string)] +#![deny(clippy::inherent_to_string_shadow_display)] +#![allow(clippy::many_single_char_names)] + +use std::fmt; + +trait FalsePositive { + fn to_string(&self) -> String; +} + +struct A; +struct B; +struct C; +struct D; +struct E; +struct F; + +impl A { + // Should be detected; emit warning + fn to_string(&self) -> String { + "A.to_string()".to_string() + } + + // Should not be detected as it does not match the function signature + fn to_str(&self) -> String { + "A.to_str()".to_string() + } +} + +// Should not be detected as it is a free function +fn to_string() -> String { + "free to_string()".to_string() +} + +impl B { + // Should not be detected, wrong return type + fn to_string(&self) -> i32 { + 42 + } +} + +impl C { + // Should be detected and emit error as C also implements Display + fn to_string(&self) -> String { + "C.to_string()".to_string() + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "impl Display for C") + } +} + +impl FalsePositive for D { + // Should not be detected, as it is a trait function + fn to_string(&self) -> String { + "impl FalsePositive for D".to_string() + } +} + +impl E { + // Should not be detected, as it is not bound to an instance + fn to_string() -> String { + "E::to_string()".to_string() + } +} + +impl F { + // Should not be detected, as it does not match the function signature + fn to_string(&self, _i: i32) -> String { + "F.to_string()".to_string() + } +} + +fn main() { + let a = A; + a.to_string(); + a.to_str(); + + to_string(); + + let b = B; + b.to_string(); + + let c = C; + C.to_string(); + + let d = D; + d.to_string(); + + E::to_string(); + + let f = F; + f.to_string(1); +} diff --git a/src/tools/clippy/tests/ui/inherent_to_string.stderr b/src/tools/clippy/tests/ui/inherent_to_string.stderr new file mode 100644 index 0000000000000..4f331f5bec9e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/inherent_to_string.stderr @@ -0,0 +1,28 @@ +error: implementation of inherent method `to_string(&self) -> String` for type `A` + --> $DIR/inherent_to_string.rs:20:5 + | +LL | / fn to_string(&self) -> String { +LL | | "A.to_string()".to_string() +LL | | } + | |_____^ + | + = note: `-D clippy::inherent-to-string` implied by `-D warnings` + = help: implement trait `Display` for type `A` instead + +error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display` + --> $DIR/inherent_to_string.rs:44:5 + | +LL | / fn to_string(&self) -> String { +LL | | "C.to_string()".to_string() +LL | | } + | |_____^ + | +note: the lint level is defined here + --> $DIR/inherent_to_string.rs:2:9 + | +LL | #![deny(clippy::inherent_to_string_shadow_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: remove the inherent method from type `C` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.fixed b/src/tools/clippy/tests/ui/inline_fn_without_body.fixed new file mode 100644 index 0000000000000..fe21a71a42c26 --- /dev/null +++ b/src/tools/clippy/tests/ui/inline_fn_without_body.fixed @@ -0,0 +1,17 @@ +// run-rustfix + +#![warn(clippy::inline_fn_without_body)] +#![allow(clippy::inline_always)] + +trait Foo { + fn default_inline(); + + fn always_inline(); + + fn never_inline(); + + #[inline] + fn has_body() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.rs b/src/tools/clippy/tests/ui/inline_fn_without_body.rs new file mode 100644 index 0000000000000..5074698946650 --- /dev/null +++ b/src/tools/clippy/tests/ui/inline_fn_without_body.rs @@ -0,0 +1,20 @@ +// run-rustfix + +#![warn(clippy::inline_fn_without_body)] +#![allow(clippy::inline_always)] + +trait Foo { + #[inline] + fn default_inline(); + + #[inline(always)] + fn always_inline(); + + #[inline(never)] + fn never_inline(); + + #[inline] + fn has_body() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.stderr b/src/tools/clippy/tests/ui/inline_fn_without_body.stderr new file mode 100644 index 0000000000000..32d35e209b01b --- /dev/null +++ b/src/tools/clippy/tests/ui/inline_fn_without_body.stderr @@ -0,0 +1,28 @@ +error: use of `#[inline]` on trait method `default_inline` which has no body + --> $DIR/inline_fn_without_body.rs:7:5 + | +LL | #[inline] + | _____-^^^^^^^^ +LL | | fn default_inline(); + | |____- help: remove + | + = note: `-D clippy::inline-fn-without-body` implied by `-D warnings` + +error: use of `#[inline]` on trait method `always_inline` which has no body + --> $DIR/inline_fn_without_body.rs:10:5 + | +LL | #[inline(always)] + | _____-^^^^^^^^^^^^^^^^ +LL | | fn always_inline(); + | |____- help: remove + +error: use of `#[inline]` on trait method `never_inline` which has no body + --> $DIR/inline_fn_without_body.rs:13:5 + | +LL | #[inline(never)] + | _____-^^^^^^^^^^^^^^^ +LL | | fn never_inline(); + | |____- help: remove + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/int_plus_one.fixed b/src/tools/clippy/tests/ui/int_plus_one.fixed new file mode 100644 index 0000000000000..642830f24f58a --- /dev/null +++ b/src/tools/clippy/tests/ui/int_plus_one.fixed @@ -0,0 +1,17 @@ +// run-rustfix + +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#[warn(clippy::int_plus_one)] +fn main() { + let x = 1i32; + let y = 0i32; + + let _ = x > y; + let _ = y < x; + + let _ = x > y; + let _ = y < x; + + let _ = x > y; // should be ok + let _ = y < x; // should be ok +} diff --git a/src/tools/clippy/tests/ui/int_plus_one.rs b/src/tools/clippy/tests/ui/int_plus_one.rs new file mode 100644 index 0000000000000..0755a0c79d280 --- /dev/null +++ b/src/tools/clippy/tests/ui/int_plus_one.rs @@ -0,0 +1,17 @@ +// run-rustfix + +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#[warn(clippy::int_plus_one)] +fn main() { + let x = 1i32; + let y = 0i32; + + let _ = x >= y + 1; + let _ = y + 1 <= x; + + let _ = x - 1 >= y; + let _ = y <= x - 1; + + let _ = x > y; // should be ok + let _ = y < x; // should be ok +} diff --git a/src/tools/clippy/tests/ui/int_plus_one.stderr b/src/tools/clippy/tests/ui/int_plus_one.stderr new file mode 100644 index 0000000000000..29a6914761c9f --- /dev/null +++ b/src/tools/clippy/tests/ui/int_plus_one.stderr @@ -0,0 +1,28 @@ +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:9:13 + | +LL | let _ = x >= y + 1; + | ^^^^^^^^^^ help: change it to: `x > y` + | + = note: `-D clippy::int-plus-one` implied by `-D warnings` + +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:10:13 + | +LL | let _ = y + 1 <= x; + | ^^^^^^^^^^ help: change it to: `y < x` + +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:12:13 + | +LL | let _ = x - 1 >= y; + | ^^^^^^^^^^ help: change it to: `x > y` + +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:13:13 + | +LL | let _ = y <= x - 1; + | ^^^^^^^^^^ help: change it to: `y < x` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs new file mode 100644 index 0000000000000..7b1b64f390a5a --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_arithmetic.rs @@ -0,0 +1,99 @@ +#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref +)] + +#[rustfmt::skip] +fn main() { + let mut i = 1i32; + 1 + i; + i * 2; + 1 % + i / 2; // no error, this is part of the expression in the preceding line + i - 2 + 2 - i; + -i; + i >> 1; + i << 1; + + // no error, overflows are checked by `overflowing_literals` + -1; + -(-1); + + i & 1; // no wrapping + i | 1; + i ^ 1; + + i += 1; + i -= 1; + i *= 2; + i /= 2; + i %= 2; + i <<= 3; + i >>= 2; + + // no errors + i |= 1; + i &= 1; + i ^= i; + + // No errors for the following items because they are constant expressions + enum Foo { + Bar = -2, + } + struct Baz([i32; 1 + 1]); + union Qux { + field: [i32; 1 + 1], + } + type Alias = [i32; 1 + 1]; + + const FOO: i32 = -2; + static BAR: i32 = -2; + + let _: [i32; 1 + 1] = [0, 0]; + + let _: [i32; 1 + 1] = { + let a: [i32; 1 + 1] = [0, 0]; + a + }; + + trait Trait { + const ASSOC: i32 = 1 + 1; + } + + impl Trait for Foo { + const ASSOC: i32 = { + let _: [i32; 1 + 1]; + fn foo() {} + 1 + 1 + }; + } +} + +// warn on references as well! (#5328) +pub fn int_arith_ref() { + 3 + &1; + &3 + 1; + &3 + &1; +} + +pub fn foo(x: &i32) -> i32 { + let a = 5; + a + x +} + +pub fn bar(x: &i32, y: &i32) -> i32 { + x + y +} + +pub fn baz(x: i32, y: &i32) -> i32 { + x + y +} + +pub fn qux(x: i32, y: i32) -> i32 { + (&x + &y) +} diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.stderr b/src/tools/clippy/tests/ui/integer_arithmetic.stderr new file mode 100644 index 0000000000000..83e8a9cde3ff1 --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_arithmetic.stderr @@ -0,0 +1,131 @@ +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:14:5 + | +LL | 1 + i; + | ^^^^^ + | + = note: `-D clippy::integer-arithmetic` implied by `-D warnings` + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:15:5 + | +LL | i * 2; + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:16:5 + | +LL | / 1 % +LL | | i / 2; // no error, this is part of the expression in the preceding line + | |_________^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:18:5 + | +LL | i - 2 + 2 - i; + | ^^^^^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:19:5 + | +LL | -i; + | ^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:20:5 + | +LL | i >> 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:21:5 + | +LL | i << 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:31:5 + | +LL | i += 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:32:5 + | +LL | i -= 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:33:5 + | +LL | i *= 2; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:34:5 + | +LL | i /= 2; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:35:5 + | +LL | i %= 2; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:36:5 + | +LL | i <<= 3; + | ^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:37:5 + | +LL | i >>= 2; + | ^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:79:5 + | +LL | 3 + &1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:80:5 + | +LL | &3 + 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:81:5 + | +LL | &3 + &1; + | ^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:86:5 + | +LL | a + x + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:90:5 + | +LL | x + y + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:94:5 + | +LL | x + y + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:98:5 + | +LL | (&x + &y) + | ^^^^^^^^^ + +error: aborting due to 21 previous errors + diff --git a/src/tools/clippy/tests/ui/integer_division.rs b/src/tools/clippy/tests/ui/integer_division.rs new file mode 100644 index 0000000000000..800c75257524a --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_division.rs @@ -0,0 +1,9 @@ +#![warn(clippy::integer_division)] + +fn main() { + let two = 2; + let n = 1 / 2; + let o = 1 / two; + let p = two / 4; + let x = 1. / 2.0; +} diff --git a/src/tools/clippy/tests/ui/integer_division.stderr b/src/tools/clippy/tests/ui/integer_division.stderr new file mode 100644 index 0000000000000..72a232ef3d750 --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_division.stderr @@ -0,0 +1,27 @@ +error: integer division + --> $DIR/integer_division.rs:5:13 + | +LL | let n = 1 / 2; + | ^^^^^ + | + = note: `-D clippy::integer-division` implied by `-D warnings` + = help: division of integers may cause loss of precision. consider using floats. + +error: integer division + --> $DIR/integer_division.rs:6:13 + | +LL | let o = 1 / two; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats. + +error: integer division + --> $DIR/integer_division.rs:7: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 + diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed new file mode 100644 index 0000000000000..c30d23de3f869 --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(clippy::useless_vec)] +#![warn(clippy::into_iter_on_ref)] + +struct X; +use std::collections::*; + +fn main() { + for _ in &[1, 2, 3] {} + for _ in vec![X, X] {} + for _ in &vec![X, X] {} + + let _ = vec![1, 2, 3].into_iter(); + let _ = (&vec![1, 2, 3]).iter(); //~ WARN equivalent to .iter() + let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ WARN equivalent to .iter() + let _ = std::rc::Rc::from(&[X][..]).iter(); //~ WARN equivalent to .iter() + let _ = std::sync::Arc::from(&[X][..]).iter(); //~ WARN equivalent to .iter() + + let _ = (&&&&&&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter() + let _ = (&&&&mut &&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter() + let _ = (&mut &mut &mut [1, 2, 3]).iter_mut(); //~ ERROR equivalent to .iter_mut() + + let _ = (&Some(4)).iter(); //~ WARN equivalent to .iter() + let _ = (&mut Some(5)).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&Ok::<_, i32>(6)).iter(); //~ WARN equivalent to .iter() + let _ = (&mut Err::(7)).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&Vec::::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut Vec::::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&BTreeMap::::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut BTreeMap::::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&VecDeque::::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut VecDeque::::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&LinkedList::::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut LinkedList::::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&HashMap::::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut HashMap::::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + + let _ = (&BTreeSet::::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&BinaryHeap::::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&HashSet::::new()).iter(); //~ WARN equivalent to .iter() + let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter() + let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter() +} diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs new file mode 100644 index 0000000000000..94bc1689619a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(clippy::useless_vec)] +#![warn(clippy::into_iter_on_ref)] + +struct X; +use std::collections::*; + +fn main() { + for _ in &[1, 2, 3] {} + for _ in vec![X, X] {} + for _ in &vec![X, X] {} + + let _ = vec![1, 2, 3].into_iter(); + let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() + let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter() + let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + + let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut() + + let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut Err::(7)).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&Vec::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut Vec::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&HashMap::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut HashMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + + let _ = (&BTreeSet::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&BinaryHeap::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() + let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() +} diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr new file mode 100644 index 0000000000000..80e2d104f824f --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr @@ -0,0 +1,160 @@ +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` + --> $DIR/into_iter_on_ref.rs:14:30 + | +LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + | + = note: `-D clippy::into-iter-on-ref` implied by `-D warnings` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` + --> $DIR/into_iter_on_ref.rs:15:46 + | +LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` + --> $DIR/into_iter_on_ref.rs:16:41 + | +LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` + --> $DIR/into_iter_on_ref.rs:17:44 + | +LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:19:32 + | +LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:20:36 + | +LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:21:40 + | +LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option` + --> $DIR/into_iter_on_ref.rs:23:24 + | +LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option` + --> $DIR/into_iter_on_ref.rs:24:28 + | +LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result` + --> $DIR/into_iter_on_ref.rs:25:32 + | +LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result` + --> $DIR/into_iter_on_ref.rs:26:37 + | +LL | let _ = (&mut Err::(7)).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` + --> $DIR/into_iter_on_ref.rs:27:34 + | +LL | let _ = (&Vec::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec` + --> $DIR/into_iter_on_ref.rs:28:38 + | +LL | let _ = (&mut Vec::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap` + --> $DIR/into_iter_on_ref.rs:29:44 + | +LL | let _ = (&BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap` + --> $DIR/into_iter_on_ref.rs:30:48 + | +LL | let _ = (&mut BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque` + --> $DIR/into_iter_on_ref.rs:31:39 + | +LL | let _ = (&VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque` + --> $DIR/into_iter_on_ref.rs:32:43 + | +LL | let _ = (&mut VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList` + --> $DIR/into_iter_on_ref.rs:33:41 + | +LL | let _ = (&LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList` + --> $DIR/into_iter_on_ref.rs:34:45 + | +LL | let _ = (&mut LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap` + --> $DIR/into_iter_on_ref.rs:35:43 + | +LL | let _ = (&HashMap::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap` + --> $DIR/into_iter_on_ref.rs:36:47 + | +LL | let _ = (&mut HashMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet` + --> $DIR/into_iter_on_ref.rs:38:39 + | +LL | let _ = (&BTreeSet::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap` + --> $DIR/into_iter_on_ref.rs:39:41 + | +LL | let _ = (&BinaryHeap::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet` + --> $DIR/into_iter_on_ref.rs:40:38 + | +LL | let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path` + --> $DIR/into_iter_on_ref.rs:41:43 + | +LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf` + --> $DIR/into_iter_on_ref.rs:42:47 + | +LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: aborting due to 26 previous errors + diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs new file mode 100644 index 0000000000000..697416dcee831 --- /dev/null +++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs @@ -0,0 +1,85 @@ +#![warn(clippy::invalid_upcast_comparisons)] +#![allow( + unused, + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::cast_lossless +)] + +fn mk_value() -> T { + unimplemented!() +} + +fn main() { + let u32: u32 = mk_value(); + let u8: u8 = mk_value(); + let i32: i32 = mk_value(); + let i8: i8 = mk_value(); + + // always false, since no u8 can be > 300 + (u8 as u32) > 300; + (u8 as i32) > 300; + (u8 as u32) == 300; + (u8 as i32) == 300; + 300 < (u8 as u32); + 300 < (u8 as i32); + 300 == (u8 as u32); + 300 == (u8 as i32); + // inverted of the above + (u8 as u32) <= 300; + (u8 as i32) <= 300; + (u8 as u32) != 300; + (u8 as i32) != 300; + 300 >= (u8 as u32); + 300 >= (u8 as i32); + 300 != (u8 as u32); + 300 != (u8 as i32); + + // always false, since u8 -> i32 doesn't wrap + (u8 as i32) < 0; + -5 != (u8 as i32); + // inverted of the above + (u8 as i32) >= 0; + -5 == (u8 as i32); + + // always false, since no u8 can be 1337 + 1337 == (u8 as i32); + 1337 == (u8 as u32); + // inverted of the above + 1337 != (u8 as i32); + 1337 != (u8 as u32); + + // Those are Ok: + (u8 as u32) > 20; + 42 == (u8 as i32); + 42 != (u8 as i32); + 42 > (u8 as i32); + (u8 as i32) == 42; + (u8 as i32) != 42; + (u8 as i32) > 42; + (u8 as i32) < 42; + + (u8 as i8) == -1; + (u8 as i8) != -1; + (u8 as i32) > -1; + (u8 as i32) < -1; + (u32 as i32) < -5; + (u32 as i32) < 10; + + (i8 as u8) == 1; + (i8 as u8) != 1; + (i8 as u8) < 1; + (i8 as u8) > 1; + (i32 as u32) < 5; + (i32 as u32) < 10; + + -5 < (u32 as i32); + 0 <= (u32 as i32); + 0 < (u32 as i32); + + -5 > (u32 as i32); + -5 >= (u8 as i32); + + -5 == (u32 as i32); +} diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr new file mode 100644 index 0000000000000..03c3fb80aaabc --- /dev/null +++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr @@ -0,0 +1,166 @@ +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:21:5 + | +LL | (u8 as u32) > 300; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-upcast-comparisons` implied by `-D warnings` + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:22:5 + | +LL | (u8 as i32) > 300; + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:23:5 + | +LL | (u8 as u32) == 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:24:5 + | +LL | (u8 as i32) == 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:25:5 + | +LL | 300 < (u8 as u32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:26:5 + | +LL | 300 < (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:27:5 + | +LL | 300 == (u8 as u32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:28:5 + | +LL | 300 == (u8 as i32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:30:5 + | +LL | (u8 as u32) <= 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:31:5 + | +LL | (u8 as i32) <= 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:32:5 + | +LL | (u8 as u32) != 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:33:5 + | +LL | (u8 as i32) != 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:34:5 + | +LL | 300 >= (u8 as u32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:35:5 + | +LL | 300 >= (u8 as i32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:36:5 + | +LL | 300 != (u8 as u32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:37:5 + | +LL | 300 != (u8 as i32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:40:5 + | +LL | (u8 as i32) < 0; + | ^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:41:5 + | +LL | -5 != (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:43:5 + | +LL | (u8 as i32) >= 0; + | ^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:44:5 + | +LL | -5 == (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:47:5 + | +LL | 1337 == (u8 as i32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:48:5 + | +LL | 1337 == (u8 as u32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:50:5 + | +LL | 1337 != (u8 as i32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:51:5 + | +LL | 1337 != (u8 as u32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:65:5 + | +LL | (u8 as i32) > -1; + | ^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:66:5 + | +LL | (u8 as i32) < -1; + | ^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:82:5 + | +LL | -5 >= (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 27 previous errors + diff --git a/src/tools/clippy/tests/ui/issue-3145.rs b/src/tools/clippy/tests/ui/issue-3145.rs new file mode 100644 index 0000000000000..f497d5550af5f --- /dev/null +++ b/src/tools/clippy/tests/ui/issue-3145.rs @@ -0,0 +1,3 @@ +fn main() { + println!("{}" a); //~ERROR expected token: `,` +} diff --git a/src/tools/clippy/tests/ui/issue-3145.stderr b/src/tools/clippy/tests/ui/issue-3145.stderr new file mode 100644 index 0000000000000..cb0d95f5e2643 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue-3145.stderr @@ -0,0 +1,8 @@ +error: expected token: `,` + --> $DIR/issue-3145.rs:2:19 + | +LL | println!("{}" a); //~ERROR expected token: `,` + | ^ expected `,` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/issue-3746.rs b/src/tools/clippy/tests/ui/issue-3746.rs new file mode 100644 index 0000000000000..879d1d5d916e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue-3746.rs @@ -0,0 +1,22 @@ +// ignore-macos +// ignore-windows + +#![warn(clippy::empty_loop)] +#![feature(lang_items, link_args, start, libc)] +#![link_args = "-nostartfiles"] +#![no_std] + +use core::panic::PanicInfo; + +#[start] +fn main(argc: isize, argv: *const *const u8) -> isize { + loop {} +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/issue_2356.rs b/src/tools/clippy/tests/ui/issue_2356.rs new file mode 100644 index 0000000000000..da580a1839a17 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_2356.rs @@ -0,0 +1,24 @@ +#![deny(clippy::while_let_on_iterator)] + +use std::iter::Iterator; + +struct Foo; + +impl Foo { + fn foo1>(mut it: I) { + while let Some(_) = it.next() { + println!("{:?}", it.size_hint()); + } + } + + fn foo2>(mut it: I) { + while let Some(e) = it.next() { + println!("{:?}", e); + } + } +} + +fn main() { + Foo::foo1(vec![].into_iter()); + Foo::foo2(vec![].into_iter()); +} diff --git a/src/tools/clippy/tests/ui/issue_2356.stderr b/src/tools/clippy/tests/ui/issue_2356.stderr new file mode 100644 index 0000000000000..51b872e21c085 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_2356.stderr @@ -0,0 +1,14 @@ +error: this loop could be written as a `for` loop + --> $DIR/issue_2356.rs:15:9 + | +LL | while let Some(e) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` + | +note: the lint level is defined here + --> $DIR/issue_2356.rs:1:9 + | +LL | #![deny(clippy::while_let_on_iterator)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/issue_4266.rs b/src/tools/clippy/tests/ui/issue_4266.rs new file mode 100644 index 0000000000000..8a9d5a3d1d569 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_4266.rs @@ -0,0 +1,37 @@ +// edition:2018 +#![allow(dead_code)] + +async fn sink1<'a>(_: &'a str) {} // lint +async fn sink1_elided(_: &str) {} // ok + +// lint +async fn one_to_one<'a>(s: &'a str) -> &'a str { + s +} + +// ok +async fn one_to_one_elided(s: &str) -> &str { + s +} + +// ok +async fn all_to_one<'a>(a: &'a str, _b: &'a str) -> &'a str { + a +} + +// async fn unrelated(_: &str, _: &str) {} // Not allowed in async fn + +// #3988 +struct Foo; +impl Foo { + // ok + pub async fn foo(&mut self) {} +} + +// rust-lang/rust#61115 +// ok +async fn print(s: &str) { + println!("{}", s); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/issue_4266.stderr b/src/tools/clippy/tests/ui/issue_4266.stderr new file mode 100644 index 0000000000000..0426508e622f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_4266.stderr @@ -0,0 +1,16 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/issue_4266.rs:4:1 + | +LL | async fn sink1<'a>(_: &'a str) {} // lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/issue_4266.rs:8:1 + | +LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/item_after_statement.rs b/src/tools/clippy/tests/ui/item_after_statement.rs new file mode 100644 index 0000000000000..c17a7cbc8d905 --- /dev/null +++ b/src/tools/clippy/tests/ui/item_after_statement.rs @@ -0,0 +1,36 @@ +#![warn(clippy::items_after_statements)] + +fn ok() { + fn foo() { + println!("foo"); + } + foo(); +} + +fn last() { + foo(); + fn foo() { + println!("foo"); + } +} + +fn main() { + foo(); + fn foo() { + println!("foo"); + } + foo(); +} + +fn mac() { + let mut a = 5; + println!("{}", a); + // do not lint this, because it needs to be after `a` + macro_rules! b { + () => {{ + a = 6 + }}; + } + b!(); + println!("{}", a); +} diff --git a/src/tools/clippy/tests/ui/item_after_statement.stderr b/src/tools/clippy/tests/ui/item_after_statement.stderr new file mode 100644 index 0000000000000..f8f010b5e5c1f --- /dev/null +++ b/src/tools/clippy/tests/ui/item_after_statement.stderr @@ -0,0 +1,20 @@ +error: adding items after statements is confusing, since items exist from the start of the scope + --> $DIR/item_after_statement.rs:12:5 + | +LL | / fn foo() { +LL | | println!("foo"); +LL | | } + | |_____^ + | + = note: `-D clippy::items-after-statements` implied by `-D warnings` + +error: adding items after statements is confusing, since items exist from the start of the scope + --> $DIR/item_after_statement.rs:19:5 + | +LL | / fn foo() { +LL | | println!("foo"); +LL | | } + | |_____^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed new file mode 100644 index 0000000000000..2773227e26bca --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![allow(unused)] + +use std::collections::HashSet; +use std::collections::VecDeque; + +fn main() { + let v = [1, 2, 3, 4, 5]; + let v2: Vec = v.to_vec(); + let v3: HashSet = v.iter().cloned().collect(); + let v4: VecDeque = v.iter().cloned().collect(); + + // Handle macro expansion in suggestion + let _: Vec = vec![1, 2, 3].to_vec(); + + // Issue #3704 + unsafe { + let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) + .to_bytes().to_vec(); + } +} diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.rs b/src/tools/clippy/tests/ui/iter_cloned_collect.rs new file mode 100644 index 0000000000000..60a4eac23c79f --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.rs @@ -0,0 +1,25 @@ +// run-rustfix + +#![allow(unused)] + +use std::collections::HashSet; +use std::collections::VecDeque; + +fn main() { + let v = [1, 2, 3, 4, 5]; + let v2: Vec = v.iter().cloned().collect(); + let v3: HashSet = v.iter().cloned().collect(); + let v4: VecDeque = v.iter().cloned().collect(); + + // Handle macro expansion in suggestion + let _: Vec = vec![1, 2, 3].iter().cloned().collect(); + + // Issue #3704 + unsafe { + let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) + .to_bytes() + .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 new file mode 100644 index 0000000000000..b90a1e6c91967 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr @@ -0,0 +1,26 @@ +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:10:27 + | +LL | let v2: Vec = v.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + | + = note: `-D clippy::iter-cloned-collect` implied by `-D warnings` + +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:15:38 + | +LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:20:24 + | +LL | .to_bytes() + | ________________________^ +LL | | .iter() +LL | | .cloned() +LL | | .collect(); + | |______________________^ help: try: `.to_vec()` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_nth.rs b/src/tools/clippy/tests/ui/iter_nth.rs new file mode 100644 index 0000000000000..9c21dd82ee45e --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth.rs @@ -0,0 +1,56 @@ +// aux-build:option_helpers.rs + +#![warn(clippy::iter_nth)] + +#[macro_use] +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::VecDeque; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +/// Checks implementation of `ITER_NTH` lint. +fn iter_nth() { + let mut some_vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); + + { + // Make sure we lint `.iter()` for relevant types. + let bad_vec = some_vec.iter().nth(3); + let bad_slice = &some_vec[..].iter().nth(3); + let bad_boxed_slice = boxed_slice.iter().nth(3); + let bad_vec_deque = some_vec_deque.iter().nth(3); + } + + { + // Make sure we lint `.iter_mut()` for relevant types. + let bad_vec = some_vec.iter_mut().nth(3); + } + { + let bad_slice = &some_vec[..].iter_mut().nth(3); + } + { + let bad_vec_deque = some_vec_deque.iter_mut().nth(3); + } + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + let ok = false_positive.iter().nth(3); + let ok_mut = false_positive.iter_mut().nth(3); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_nth.stderr b/src/tools/clippy/tests/ui/iter_nth.stderr new file mode 100644 index 0000000000000..d00b2fb672bb6 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth.stderr @@ -0,0 +1,59 @@ +error: called `.iter().nth()` on a Vec + --> $DIR/iter_nth.rs:33:23 + | +LL | let bad_vec = some_vec.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-nth` implied by `-D warnings` + = help: calling `.get()` is both faster and more readable + +error: called `.iter().nth()` on a slice + --> $DIR/iter_nth.rs:34:26 + | +LL | let bad_slice = &some_vec[..].iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get()` is both faster and more readable + +error: called `.iter().nth()` on a slice + --> $DIR/iter_nth.rs:35:31 + | +LL | let bad_boxed_slice = boxed_slice.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get()` is both faster and more readable + +error: called `.iter().nth()` on a VecDeque + --> $DIR/iter_nth.rs:36:29 + | +LL | let bad_vec_deque = some_vec_deque.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get()` is both faster and more readable + +error: called `.iter_mut().nth()` on a Vec + --> $DIR/iter_nth.rs:41:23 + | +LL | let bad_vec = some_vec.iter_mut().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get_mut()` is both faster and more readable + +error: called `.iter_mut().nth()` on a slice + --> $DIR/iter_nth.rs:44:26 + | +LL | let bad_slice = &some_vec[..].iter_mut().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get_mut()` is both faster and more readable + +error: called `.iter_mut().nth()` on a VecDeque + --> $DIR/iter_nth.rs:47:29 + | +LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get_mut()` is both faster and more readable + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.fixed b/src/tools/clippy/tests/ui/iter_nth_zero.fixed new file mode 100644 index 0000000000000..b54147c94d192 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth_zero.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::iter_nth_zero)] +use std::collections::HashSet; + +struct Foo {} + +impl Foo { + fn nth(&self, index: usize) -> usize { + index + 1 + } +} + +fn main() { + let f = Foo {}; + f.nth(0); // lint does not apply here + + let mut s = HashSet::new(); + s.insert(1); + let _x = s.iter().next(); + + let mut s2 = HashSet::new(); + s2.insert(2); + let mut iter = s2.iter(); + let _y = iter.next(); + + let mut s3 = HashSet::new(); + s3.insert(3); + let mut iter2 = s3.iter(); + let _unwrapped = iter2.next().unwrap(); +} diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.rs b/src/tools/clippy/tests/ui/iter_nth_zero.rs new file mode 100644 index 0000000000000..b92c7d18adb4f --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth_zero.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::iter_nth_zero)] +use std::collections::HashSet; + +struct Foo {} + +impl Foo { + fn nth(&self, index: usize) -> usize { + index + 1 + } +} + +fn main() { + let f = Foo {}; + f.nth(0); // lint does not apply here + + let mut s = HashSet::new(); + s.insert(1); + let _x = s.iter().nth(0); + + let mut s2 = HashSet::new(); + s2.insert(2); + let mut iter = s2.iter(); + let _y = iter.nth(0); + + let mut s3 = HashSet::new(); + s3.insert(3); + let mut iter2 = s3.iter(); + let _unwrapped = iter2.nth(0).unwrap(); +} diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.stderr b/src/tools/clippy/tests/ui/iter_nth_zero.stderr new file mode 100644 index 0000000000000..2b20a4ceb4ab8 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth_zero.stderr @@ -0,0 +1,22 @@ +error: called `.nth(0)` on a `std::iter::Iterator` + --> $DIR/iter_nth_zero.rs:20:14 + | +LL | let _x = s.iter().nth(0); + | ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()` + | + = note: `-D clippy::iter-nth-zero` implied by `-D warnings` + +error: called `.nth(0)` on a `std::iter::Iterator` + --> $DIR/iter_nth_zero.rs:25:14 + | +LL | let _y = iter.nth(0); + | ^^^^^^^^^^^ help: try calling: `iter.next()` + +error: called `.nth(0)` on a `std::iter::Iterator` + --> $DIR/iter_nth_zero.rs:30:22 + | +LL | let _unwrapped = iter2.nth(0).unwrap(); + | ^^^^^^^^^^^^ help: try calling: `iter2.next()` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_skip_next.rs b/src/tools/clippy/tests/ui/iter_skip_next.rs new file mode 100644 index 0000000000000..a65ca3bbb131b --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_skip_next.rs @@ -0,0 +1,22 @@ +// aux-build:option_helpers.rs + +#![warn(clippy::iter_skip_next)] +#![allow(clippy::blacklisted_name)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; + +/// Checks implementation of `ITER_SKIP_NEXT` lint +fn iter_skip_next() { + let mut some_vec = vec![0, 1, 2, 3]; + let _ = some_vec.iter().skip(42).next(); + let _ = some_vec.iter().cycle().skip(42).next(); + let _ = (1..10).skip(10).next(); + let _ = &some_vec[..].iter().skip(3).next(); + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip(42).next(); + let _ = foo.filter().skip(42).next(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr new file mode 100644 index 0000000000000..5709f3355298b --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_skip_next.stderr @@ -0,0 +1,35 @@ +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:13:13 + | +LL | let _ = some_vec.iter().skip(42).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-skip-next` implied by `-D warnings` + = help: this is more succinctly expressed by calling `nth(x)` + +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:14:13 + | +LL | let _ = some_vec.iter().cycle().skip(42).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this is more succinctly expressed by calling `nth(x)` + +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:15:13 + | +LL | let _ = (1..10).skip(10).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this is more succinctly expressed by calling `nth(x)` + +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:16:14 + | +LL | let _ = &some_vec[..].iter().skip(3).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this is more succinctly expressed by calling `nth(x)` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.rs b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs new file mode 100644 index 0000000000000..13d1cfd428185 --- /dev/null +++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs @@ -0,0 +1,28 @@ +#[warn(clippy::iterator_step_by_zero)] +fn main() { + let _ = vec!["A", "B", "B"].iter().step_by(0); + let _ = "XXX".chars().step_by(0); + let _ = (0..1).step_by(0); + + // No error, not an iterator. + let y = NotIterator; + y.step_by(0); + + // No warning for non-zero step + let _ = (0..1).step_by(1); + + let _ = (1..).step_by(0); + let _ = (1..=2).step_by(0); + + let x = 0..1; + let _ = x.step_by(0); + + // check const eval + let v1 = vec![1, 2, 3]; + let _ = v1.iter().step_by(2 / 3); +} + +struct NotIterator; +impl NotIterator { + fn step_by(&self, _: u32) {} +} diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr new file mode 100644 index 0000000000000..c2c6803b3e6e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr @@ -0,0 +1,46 @@ +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:3:13 + | +LL | let _ = vec!["A", "B", "B"].iter().step_by(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iterator-step-by-zero` implied by `-D warnings` + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:4:13 + | +LL | let _ = "XXX".chars().step_by(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:5:13 + | +LL | let _ = (0..1).step_by(0); + | ^^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:14:13 + | +LL | let _ = (1..).step_by(0); + | ^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:15:13 + | +LL | let _ = (1..=2).step_by(0); + | ^^^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:18:13 + | +LL | let _ = x.step_by(0); + | ^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:22:13 + | +LL | let _ = v1.iter().step_by(2 / 3); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/large_const_arrays.fixed b/src/tools/clippy/tests/ui/large_const_arrays.fixed new file mode 100644 index 0000000000000..c5af07c8a1728 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_const_arrays.fixed @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::large_const_arrays)] +#![allow(dead_code)] + +#[derive(Clone, Copy)] +pub struct S { + pub data: [u64; 32], +} + +// Should lint +pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; +pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +static FOO: [u32; 1_000_000] = [0u32; 1_000_000]; + +// Good +pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000]; +pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000]; +const G_FOO: [u32; 1_000] = [0u32; 1_000]; + +fn main() { + // Should lint + pub static BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + static BAR: [u32; 1_000_000] = [0u32; 1_000_000]; + pub static BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + static BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + pub static BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; + static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; + + // Good + pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000]; + const G_BAR: [u32; 1_000] = [0u32; 1_000]; + pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500]; + const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500]; + pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200]; + const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200]; +} diff --git a/src/tools/clippy/tests/ui/large_const_arrays.rs b/src/tools/clippy/tests/ui/large_const_arrays.rs new file mode 100644 index 0000000000000..a160b9f8ad5b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_const_arrays.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::large_const_arrays)] +#![allow(dead_code)] + +#[derive(Clone, Copy)] +pub struct S { + pub data: [u64; 32], +} + +// Should lint +pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; +pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; + +// Good +pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000]; +pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000]; +const G_FOO: [u32; 1_000] = [0u32; 1_000]; + +fn main() { + // Should lint + pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; + pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; + const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; + + // Good + pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000]; + const G_BAR: [u32; 1_000] = [0u32; 1_000]; + pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500]; + const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500]; + pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200]; + const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200]; +} diff --git a/src/tools/clippy/tests/ui/large_const_arrays.stderr b/src/tools/clippy/tests/ui/large_const_arrays.stderr new file mode 100644 index 0000000000000..3fb0acbca67de --- /dev/null +++ b/src/tools/clippy/tests/ui/large_const_arrays.stderr @@ -0,0 +1,76 @@ +error: large array defined as const + --> $DIR/large_const_arrays.rs:12:1 + | +LL | pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; + | ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + | + = note: `-D clippy::large-const-arrays` implied by `-D warnings` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:13:1 + | +LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:14:1 + | +LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:23:5 + | +LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:24:5 + | +LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:25:5 + | +LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:26:5 + | +LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:27:5 + | +LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:28:5 + | +LL | const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/large_digit_groups.fixed b/src/tools/clippy/tests/ui/large_digit_groups.fixed new file mode 100644 index 0000000000000..859fad2f54d9d --- /dev/null +++ b/src/tools/clippy/tests/ui/large_digit_groups.fixed @@ -0,0 +1,31 @@ +// run-rustfix +#![warn(clippy::large_digit_groups)] + +fn main() { + macro_rules! mac { + () => { + 0b1_10110_i64 + }; + } + + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = ( + 0b11_0110_i64, + 0xdead_beef_usize, + 123_456_f32, + 123_456.12_f32, + 123_456.123_45_f64, + 123_456.123_456_f64, + ); + // Ignore literals in macros + let _ = mac!(); +} diff --git a/src/tools/clippy/tests/ui/large_digit_groups.rs b/src/tools/clippy/tests/ui/large_digit_groups.rs new file mode 100644 index 0000000000000..ac116d5dbda15 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_digit_groups.rs @@ -0,0 +1,31 @@ +// run-rustfix +#![warn(clippy::large_digit_groups)] + +fn main() { + macro_rules! mac { + () => { + 0b1_10110_i64 + }; + } + + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = ( + 0b1_10110_i64, + 0xd_e_adbee_f_usize, + 1_23456_f32, + 1_23456.12_f32, + 1_23456.12345_f64, + 1_23456.12345_6_f64, + ); + // Ignore literals in macros + let _ = mac!(); +} diff --git a/src/tools/clippy/tests/ui/large_digit_groups.stderr b/src/tools/clippy/tests/ui/large_digit_groups.stderr new file mode 100644 index 0000000000000..b6d9672a78e21 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_digit_groups.stderr @@ -0,0 +1,42 @@ +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:22:9 + | +LL | 0b1_10110_i64, + | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64` + | + = note: `-D clippy::large-digit-groups` implied by `-D warnings` + +error: digits grouped inconsistently by underscores + --> $DIR/large_digit_groups.rs:23:9 + | +LL | 0xd_e_adbee_f_usize, + | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:24:9 + | +LL | 1_23456_f32, + | ^^^^^^^^^^^ help: consider: `123_456_f32` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:25:9 + | +LL | 1_23456.12_f32, + | ^^^^^^^^^^^^^^ help: consider: `123_456.12_f32` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:26:9 + | +LL | 1_23456.12345_f64, + | ^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_45_f64` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:27:9 + | +LL | 1_23456.12345_6_f64, + | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs new file mode 100644 index 0000000000000..852ef5fec0e7b --- /dev/null +++ b/src/tools/clippy/tests/ui/large_enum_variant.rs @@ -0,0 +1,54 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![warn(clippy::large_enum_variant)] + +enum LargeEnum { + A(i32), + B([i32; 8000]), +} + +enum GenericEnumOk { + A(i32), + B([T; 8000]), +} + +enum GenericEnum2 { + A(i32), + B([i32; 8000]), + C(T, [i32; 8000]), +} + +trait SomeTrait { + type Item; +} + +enum LargeEnumGeneric { + Var(A::Item), +} + +enum LargeEnum2 { + VariantOk(i32, u32), + ContainingLargeEnum(LargeEnum), +} +enum LargeEnum3 { + ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), + VoidVariant, + StructLikeLittle { x: i32, y: i32 }, +} + +enum LargeEnum4 { + VariantOk(i32, u32), + StructLikeLarge { x: [i32; 8000], y: i32 }, +} + +enum LargeEnum5 { + VariantOk(i32, u32), + StructLikeLarge2 { x: [i32; 8000] }, +} + +enum LargeEnumOk { + LargeA([i32; 8000]), + LargeB([i32; 8001]), +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.stderr new file mode 100644 index 0000000000000..8ce641a81f297 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_enum_variant.stderr @@ -0,0 +1,68 @@ +error: large size difference between variants + --> $DIR/large_enum_variant.rs:7:5 + | +LL | B([i32; 8000]), + | ^^^^^^^^^^^^^^ this variant is 32000 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` +note: and the second-largest variant is 4 bytes: + --> $DIR/large_enum_variant.rs:6:5 + | +LL | A(i32), + | ^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[i32; 8000]>), + | ^^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:31:5 + | +LL | ContainingLargeEnum(LargeEnum), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:30:5 + | +LL | VariantOk(i32, u32), + | ^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingLargeEnum(Box), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:41:5 + | +LL | StructLikeLarge { x: [i32; 8000], y: i32 }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:40:5 + | +LL | VariantOk(i32, u32), + | ^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:41:5 + | +LL | StructLikeLarge { x: [i32; 8000], y: i32 }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:46:5 + | +LL | StructLikeLarge2 { x: [i32; 8000] }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:45:5 + | +LL | VariantOk(i32, u32), + | ^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs new file mode 100644 index 0000000000000..d9161bfcf1543 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs @@ -0,0 +1,30 @@ +#![warn(clippy::large_stack_arrays)] +#![allow(clippy::large_enum_variant)] + +#[derive(Clone, Copy)] +struct S { + pub data: [u64; 32], +} + +#[derive(Clone, Copy)] +enum E { + S(S), + T(u32), +} + +fn main() { + let bad = ( + [0u32; 20_000_000], + [S { data: [0; 32] }; 5000], + [Some(""); 20_000_000], + [E::T(0); 5000], + ); + + let good = ( + [0u32; 1000], + [S { data: [0; 32] }; 1000], + [Some(""); 1000], + [E::T(0); 1000], + [(); 20_000_000], + ); +} diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr new file mode 100644 index 0000000000000..58c0a77c1c841 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr @@ -0,0 +1,35 @@ +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:17:9 + | +LL | [0u32; 20_000_000], + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::large-stack-arrays` implied by `-D warnings` + = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:18:9 + | +LL | [S { data: [0; 32] }; 5000], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:19:9 + | +LL | [Some(""); 20_000_000], + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:20:9 + | +LL | [E::T(0); 5000], + | ^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs new file mode 100644 index 0000000000000..3ef29dd63880b --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -0,0 +1,145 @@ +#![warn(clippy::len_without_is_empty)] +#![allow(dead_code, unused)] + +pub struct PubOne; + +impl PubOne { + pub fn len(self: &Self) -> isize { + 1 + } +} + +impl PubOne { + // A second impl for this struct -- the error span shouldn't mention this. + pub fn irrelevant(self: &Self) -> bool { + false + } +} + +// Identical to `PubOne`, but with an `allow` attribute on the impl complaining `len`. +pub struct PubAllowed; + +#[allow(clippy::len_without_is_empty)] +impl PubAllowed { + pub fn len(self: &Self) -> isize { + 1 + } +} + +// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the +// impl containing `len`. +impl PubAllowed { + pub fn irrelevant(self: &Self) -> bool { + false + } +} + +pub trait PubTraitsToo { + fn len(self: &Self) -> isize; +} + +impl PubTraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +pub struct HasIsEmpty; + +impl HasIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub struct HasWrongIsEmpty; + +impl HasWrongIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + pub fn is_empty(self: &Self, x: u32) -> bool { + false + } +} + +struct NotPubOne; + +impl NotPubOne { + pub fn len(self: &Self) -> isize { + // No error; `len` is pub but `NotPubOne` is not exported anyway. + 1 + } +} + +struct One; + +impl One { + fn len(self: &Self) -> isize { + // No error; `len` is private; see issue #1085. + 1 + } +} + +trait TraitsToo { + fn len(self: &Self) -> isize; + // No error; `len` is private; see issue #1085. +} + +impl TraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +struct HasPrivateIsEmpty; + +impl HasPrivateIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +struct Wither; + +pub trait WithIsEmpty { + fn len(self: &Self) -> isize; + fn is_empty(self: &Self) -> bool; +} + +impl WithIsEmpty for Wither { + fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub trait Empty { + fn is_empty(&self) -> bool; +} + +pub trait InheritingEmpty: Empty { + // Must not trigger `LEN_WITHOUT_IS_EMPTY`. + fn len(&self) -> isize; +} + +// This used to ICE. +pub trait Foo: Sized {} + +pub trait DependsOnFoo: Foo { + fn len(&mut self) -> usize; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr new file mode 100644 index 0000000000000..4493b17a4b4e5 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr @@ -0,0 +1,54 @@ +error: item `PubOne` has a public `len` method but no corresponding `is_empty` method + --> $DIR/len_without_is_empty.rs:6:1 + | +LL | / impl PubOne { +LL | | pub fn len(self: &Self) -> isize { +LL | | 1 +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::len-without-is-empty` implied by `-D warnings` + +error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method + --> $DIR/len_without_is_empty.rs:37:1 + | +LL | / pub trait PubTraitsToo { +LL | | fn len(self: &Self) -> isize; +LL | | } + | |_^ + +error: item `HasIsEmpty` has a public `len` method but a private `is_empty` method + --> $DIR/len_without_is_empty.rs:49:1 + | +LL | / impl HasIsEmpty { +LL | | pub fn len(self: &Self) -> isize { +LL | | 1 +LL | | } +... | +LL | | } +LL | | } + | |_^ + +error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is_empty` method + --> $DIR/len_without_is_empty.rs:61:1 + | +LL | / impl HasWrongIsEmpty { +LL | | pub fn len(self: &Self) -> isize { +LL | | 1 +LL | | } +... | +LL | | } +LL | | } + | |_^ + +error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method + --> $DIR/len_without_is_empty.rs:141:1 + | +LL | / pub trait DependsOnFoo: Foo { +LL | | fn len(&mut self) -> usize; +LL | | } + | |_^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed new file mode 100644 index 0000000000000..624e5ef8fcf13 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -0,0 +1,143 @@ +// run-rustfix + +#![warn(clippy::len_zero)] +#![allow(dead_code, unused, clippy::len_without_is_empty)] + +pub struct One; +struct Wither; + +trait TraitsToo { + fn len(self: &Self) -> isize; + // No error; `len` is private; see issue #1085. +} + +impl TraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +pub struct HasIsEmpty; + +impl HasIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub struct HasWrongIsEmpty; + +impl HasWrongIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + pub fn is_empty(self: &Self, x: u32) -> bool { + false + } +} + +pub trait WithIsEmpty { + fn len(self: &Self) -> isize; + fn is_empty(self: &Self) -> bool; +} + +impl WithIsEmpty for Wither { + fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +fn main() { + let x = [1, 2]; + if x.is_empty() { + println!("This should not happen!"); + } + + if "".is_empty() {} + + let y = One; + if y.len() == 0 { + // No error; `One` does not have `.is_empty()`. + println!("This should not happen either!"); + } + + let z: &dyn TraitsToo = &y; + if z.len() > 0 { + // No error; `TraitsToo` has no `.is_empty()` method. + println!("Nor should this!"); + } + + let has_is_empty = HasIsEmpty; + if has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if has_is_empty.len() > 1 { + // No error. + println!("This can happen."); + } + if has_is_empty.len() <= 1 { + // No error. + println!("This can happen."); + } + if has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if has_is_empty.is_empty() { + println!("Or this!"); + } + if 1 < has_is_empty.len() { + // No error. + println!("This can happen."); + } + if 1 >= has_is_empty.len() { + // No error. + println!("This can happen."); + } + assert!(!has_is_empty.is_empty()); + + let with_is_empty: &dyn WithIsEmpty = &Wither; + if with_is_empty.is_empty() { + println!("Or this!"); + } + assert!(!with_is_empty.is_empty()); + + let has_wrong_is_empty = HasWrongIsEmpty; + if has_wrong_is_empty.len() == 0 { + // No error; `HasWrongIsEmpty` does not have `.is_empty()`. + println!("Or this!"); + } +} + +fn test_slice(b: &[u8]) { + if !b.is_empty() {} +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs new file mode 100644 index 0000000000000..7fba971cfd887 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -0,0 +1,143 @@ +// run-rustfix + +#![warn(clippy::len_zero)] +#![allow(dead_code, unused, clippy::len_without_is_empty)] + +pub struct One; +struct Wither; + +trait TraitsToo { + fn len(self: &Self) -> isize; + // No error; `len` is private; see issue #1085. +} + +impl TraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +pub struct HasIsEmpty; + +impl HasIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub struct HasWrongIsEmpty; + +impl HasWrongIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + pub fn is_empty(self: &Self, x: u32) -> bool { + false + } +} + +pub trait WithIsEmpty { + fn len(self: &Self) -> isize; + fn is_empty(self: &Self) -> bool; +} + +impl WithIsEmpty for Wither { + fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +fn main() { + let x = [1, 2]; + if x.len() == 0 { + println!("This should not happen!"); + } + + if "".len() == 0 {} + + let y = One; + if y.len() == 0 { + // No error; `One` does not have `.is_empty()`. + println!("This should not happen either!"); + } + + let z: &dyn TraitsToo = &y; + if z.len() > 0 { + // No error; `TraitsToo` has no `.is_empty()` method. + println!("Nor should this!"); + } + + let has_is_empty = HasIsEmpty; + if has_is_empty.len() == 0 { + println!("Or this!"); + } + if has_is_empty.len() != 0 { + println!("Or this!"); + } + if has_is_empty.len() > 0 { + println!("Or this!"); + } + if has_is_empty.len() < 1 { + println!("Or this!"); + } + if has_is_empty.len() >= 1 { + println!("Or this!"); + } + if has_is_empty.len() > 1 { + // No error. + println!("This can happen."); + } + if has_is_empty.len() <= 1 { + // No error. + println!("This can happen."); + } + if 0 == has_is_empty.len() { + println!("Or this!"); + } + if 0 != has_is_empty.len() { + println!("Or this!"); + } + if 0 < has_is_empty.len() { + println!("Or this!"); + } + if 1 <= has_is_empty.len() { + println!("Or this!"); + } + if 1 > has_is_empty.len() { + println!("Or this!"); + } + if 1 < has_is_empty.len() { + // No error. + println!("This can happen."); + } + if 1 >= has_is_empty.len() { + // No error. + println!("This can happen."); + } + assert!(!has_is_empty.is_empty()); + + let with_is_empty: &dyn WithIsEmpty = &Wither; + if with_is_empty.len() == 0 { + println!("Or this!"); + } + assert!(!with_is_empty.is_empty()); + + let has_wrong_is_empty = HasWrongIsEmpty; + if has_wrong_is_empty.len() == 0 { + // No error; `HasWrongIsEmpty` does not have `.is_empty()`. + println!("Or this!"); + } +} + +fn test_slice(b: &[u8]) { + if b.len() != 0 {} +} diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr new file mode 100644 index 0000000000000..6c71f1beeac67 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.stderr @@ -0,0 +1,88 @@ +error: length comparison to zero + --> $DIR/len_zero.rs:61:8 + | +LL | if x.len() == 0 { + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + +error: length comparison to zero + --> $DIR/len_zero.rs:65:8 + | +LL | if "".len() == 0 {} + | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:80:8 + | +LL | if has_is_empty.len() == 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:83:8 + | +LL | if has_is_empty.len() != 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:86:8 + | +LL | if has_is_empty.len() > 0 { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:89:8 + | +LL | if has_is_empty.len() < 1 { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:92:8 + | +LL | if has_is_empty.len() >= 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:103:8 + | +LL | if 0 == has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:106:8 + | +LL | if 0 != has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:109:8 + | +LL | if 0 < has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:112:8 + | +LL | if 1 <= has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:115:8 + | +LL | if 1 > has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:129:8 + | +LL | if with_is_empty.len() == 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:142:8 + | +LL | if b.len() != 0 {} + | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/let_if_seq.rs b/src/tools/clippy/tests/ui/let_if_seq.rs new file mode 100644 index 0000000000000..802beeb4be6b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_if_seq.rs @@ -0,0 +1,119 @@ +#![allow( + unused_variables, + unused_assignments, + clippy::similar_names, + clippy::blacklisted_name +)] +#![warn(clippy::useless_let_if_seq)] + +fn f() -> bool { + true +} +fn g(x: i32) -> i32 { + x + 1 +} + +fn issue985() -> i32 { + let mut x = 42; + if f() { + x = g(x); + } + + x +} + +fn issue985_alt() -> i32 { + let mut x = 42; + if f() { + f(); + } else { + x = g(x); + } + + x +} + +fn issue975() -> String { + let mut udn = "dummy".to_string(); + if udn.starts_with("uuid:") { + udn = String::from(&udn[5..]); + } + udn +} + +fn early_return() -> u8 { + // FIXME: we could extend the lint to include such cases: + let foo; + + if f() { + return 42; + } else { + foo = 0; + } + + foo +} + +fn main() { + early_return(); + issue975(); + issue985(); + issue985_alt(); + + let mut foo = 0; + if f() { + foo = 42; + } + + let mut bar = 0; + if f() { + f(); + bar = 42; + } else { + f(); + } + + let quz; + if f() { + quz = 42; + } else { + quz = 0; + } + + // `toto` is used several times + let mut toto; + if f() { + toto = 42; + } else { + for i in &[1, 2] { + toto = *i; + } + + toto = 2; + } + + // found in libcore, the inner if is not a statement but the block's expr + let mut ch = b'x'; + if f() { + ch = b'*'; + if f() { + ch = b'?'; + } + } + + // baz needs to be mut + let mut baz = 0; + if f() { + baz = 42; + } + + baz = 1337; + + // issue 3043 - types with interior mutability should not trigger this lint + use std::cell::Cell; + let mut val = Cell::new(1); + if true { + val = Cell::new(2); + } + println!("{}", val.get()); +} diff --git a/src/tools/clippy/tests/ui/let_if_seq.stderr b/src/tools/clippy/tests/ui/let_if_seq.stderr new file mode 100644 index 0000000000000..c53a63a541bc9 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_if_seq.stderr @@ -0,0 +1,50 @@ +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:63:5 + | +LL | / let mut foo = 0; +LL | | if f() { +LL | | foo = 42; +LL | | } + | |_____^ help: it is more idiomatic to write: `let foo = if f() { 42 } else { 0 };` + | + = note: `-D clippy::useless-let-if-seq` implied by `-D warnings` + = note: you might not need `mut` at all + +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:68:5 + | +LL | / let mut bar = 0; +LL | | if f() { +LL | | f(); +LL | | bar = 42; +LL | | } else { +LL | | f(); +LL | | } + | |_____^ help: it is more idiomatic to write: `let bar = if f() { ..; 42 } else { ..; 0 };` + | + = note: you might not need `mut` at all + +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:76:5 + | +LL | / let quz; +LL | | if f() { +LL | | quz = 42; +LL | | } else { +LL | | quz = 0; +LL | | } + | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` + +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:105:5 + | +LL | / let mut baz = 0; +LL | | if f() { +LL | | baz = 42; +LL | | } + | |_____^ help: it is more idiomatic to write: `let baz = if f() { 42 } else { 0 };` + | + = note: you might not need `mut` at all + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/let_return.rs b/src/tools/clippy/tests/ui/let_return.rs new file mode 100644 index 0000000000000..23645d48fe799 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_return.rs @@ -0,0 +1,70 @@ +#![allow(unused)] +#![warn(clippy::let_and_return)] + +fn test() -> i32 { + let _y = 0; // no warning + let x = 5; + x +} + +fn test_inner() -> i32 { + if true { + let x = 5; + x + } else { + 0 + } +} + +fn test_nowarn_1() -> i32 { + let mut x = 5; + x += 1; + x +} + +fn test_nowarn_2() -> i32 { + let x = 5; + x + 1 +} + +fn test_nowarn_3() -> (i32, i32) { + // this should technically warn, but we do not compare complex patterns + let (x, y) = (5, 9); + (x, y) +} + +fn test_nowarn_4() -> i32 { + // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type + let x: i32 = 5; + x +} + +fn test_nowarn_5(x: i16) -> u16 { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let x = x as u16; + x +} + +// False positive example +trait Decode { + fn decode(d: D) -> Result + where + Self: Sized; +} + +macro_rules! tuple_encode { + ($($x:ident),*) => ( + impl<$($x: Decode),*> Decode for ($($x),*) { + #[inline] + #[allow(non_snake_case)] + fn decode(mut d: D) -> Result { + // Shouldn't trigger lint + Ok(($({let $x = Decode::decode(&mut d)?; $x }),*)) + } + } + ); +} + +tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/let_return.stderr b/src/tools/clippy/tests/ui/let_return.stderr new file mode 100644 index 0000000000000..128a22c86e360 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_return.stderr @@ -0,0 +1,31 @@ +error: returning the result of a `let` binding from a block + --> $DIR/let_return.rs:7:5 + | +LL | let x = 5; + | ---------- unnecessary `let` binding +LL | x + | ^ + | + = note: `-D clippy::let-and-return` implied by `-D warnings` +help: return the expression directly + | +LL | +LL | 5 + | + +error: returning the result of a `let` binding from a block + --> $DIR/let_return.rs:13:9 + | +LL | let x = 5; + | ---------- unnecessary `let` binding +LL | x + | ^ + | +help: return the expression directly + | +LL | +LL | 5 + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.rs b/src/tools/clippy/tests/ui/let_underscore_lock.rs new file mode 100644 index 0000000000000..88fb216a74329 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_lock.rs @@ -0,0 +1,13 @@ +#![warn(clippy::let_underscore_lock)] + +fn main() { + let m = std::sync::Mutex::new(()); + let rw = std::sync::RwLock::new(()); + + let _ = m.lock(); + let _ = rw.read(); + let _ = rw.write(); + let _ = m.try_lock(); + let _ = rw.try_read(); + let _ = rw.try_write(); +} diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.stderr b/src/tools/clippy/tests/ui/let_underscore_lock.stderr new file mode 100644 index 0000000000000..5d5f6059ef13e --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_lock.stderr @@ -0,0 +1,51 @@ +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:7:5 + | +LL | let _ = m.lock(); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-lock` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:8:5 + | +LL | let _ = rw.read(); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:9:5 + | +LL | let _ = rw.write(); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:10:5 + | +LL | let _ = m.try_lock(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:11:5 + | +LL | let _ = rw.try_read(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:12:5 + | +LL | let _ = rw.try_write(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/let_underscore_must_use.rs b/src/tools/clippy/tests/ui/let_underscore_must_use.rs new file mode 100644 index 0000000000000..27dda606067aa --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_must_use.rs @@ -0,0 +1,94 @@ +#![warn(clippy::let_underscore_must_use)] + +// Debug implementations can fire this lint, +// so we shouldn't lint external macros +#[derive(Debug)] +struct Foo { + field: i32, +} + +#[must_use] +fn f() -> u32 { + 0 +} + +fn g() -> Result { + Ok(0) +} + +#[must_use] +fn l(x: T) -> T { + x +} + +fn h() -> u32 { + 0 +} + +struct S {} + +impl S { + #[must_use] + pub fn f(&self) -> u32 { + 0 + } + + pub fn g(&self) -> Result { + Ok(0) + } + + fn k(&self) -> u32 { + 0 + } + + #[must_use] + fn h() -> u32 { + 0 + } + + fn p() -> Result { + Ok(0) + } +} + +trait Trait { + #[must_use] + fn a() -> u32; +} + +impl Trait for S { + fn a() -> u32 { + 0 + } +} + +fn main() { + let _ = f(); + let _ = g(); + let _ = h(); + let _ = l(0_u32); + + let s = S {}; + + let _ = s.f(); + let _ = s.g(); + let _ = s.k(); + + let _ = S::h(); + let _ = S::p(); + + let _ = S::a(); + + let _ = if true { Ok(()) } else { Err(()) }; + + let a = Result::<(), ()>::Ok(()); + + let _ = a.is_ok(); + + let _ = a.map(|_| ()); + + let _ = a; + + #[allow(clippy::let_underscore_must_use)] + let _ = a; +} diff --git a/src/tools/clippy/tests/ui/let_underscore_must_use.stderr b/src/tools/clippy/tests/ui/let_underscore_must_use.stderr new file mode 100644 index 0000000000000..447f2419e3bdb --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_must_use.stderr @@ -0,0 +1,99 @@ +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:66:5 + | +LL | let _ = f(); + | ^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-must-use` implied by `-D warnings` + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:67:5 + | +LL | let _ = g(); + | ^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:69:5 + | +LL | let _ = l(0_u32); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:73:5 + | +LL | let _ = s.f(); + | ^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:74:5 + | +LL | let _ = s.g(); + | ^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:77:5 + | +LL | let _ = S::h(); + | ^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:78:5 + | +LL | let _ = S::p(); + | ^^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:80:5 + | +LL | let _ = S::a(); + | ^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:82:5 + | +LL | let _ = if true { Ok(()) } else { Err(()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:86:5 + | +LL | let _ = a.is_ok(); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:88:5 + | +LL | let _ = a.map(|_| ()); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:90:5 + | +LL | let _ = a; + | ^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed new file mode 100644 index 0000000000000..f398edc23cb5e --- /dev/null +++ b/src/tools/clippy/tests/ui/let_unit.fixed @@ -0,0 +1,63 @@ +// run-rustfix + +#![warn(clippy::let_unit_value)] +#![allow(clippy::no_effect)] +#![allow(unused_variables)] + +macro_rules! let_and_return { + ($n:expr) => {{ + let ret = $n; + }}; +} + +fn main() { + println!("x"); + let _y = 1; // this is fine + let _z = ((), 1); // this as well + if true { + (); + } + + consume_units_with_for_loop(); // should be fine as well + + multiline_sugg(); + + let_and_return!(()) // should be fine +} + +// Related to issue #1964 +fn consume_units_with_for_loop() { + // `for_let_unit` lint should not be triggered by consuming them using for loop. + let v = vec![(), (), ()]; + let mut count = 0; + for _ in v { + count += 1; + } + assert_eq!(count, 3); + + // Same for consuming from some other Iterator. + let (tx, rx) = ::std::sync::mpsc::channel(); + tx.send(()).unwrap(); + drop(tx); + + count = 0; + for _ in rx.iter() { + count += 1; + } + assert_eq!(count, 1); +} + +fn multiline_sugg() { + let v: Vec = vec![2]; + + v + .into_iter() + .map(|i| i * 2) + .filter(|i| i % 2 == 0) + .map(|_| ()) + .next() + .unwrap(); +} + +#[derive(Copy, Clone)] +pub struct ContainsUnit(()); // should be fine diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs new file mode 100644 index 0000000000000..af5b1fb2ac7e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -0,0 +1,63 @@ +// run-rustfix + +#![warn(clippy::let_unit_value)] +#![allow(clippy::no_effect)] +#![allow(unused_variables)] + +macro_rules! let_and_return { + ($n:expr) => {{ + let ret = $n; + }}; +} + +fn main() { + let _x = println!("x"); + let _y = 1; // this is fine + let _z = ((), 1); // this as well + if true { + let _a = (); + } + + consume_units_with_for_loop(); // should be fine as well + + multiline_sugg(); + + let_and_return!(()) // should be fine +} + +// Related to issue #1964 +fn consume_units_with_for_loop() { + // `for_let_unit` lint should not be triggered by consuming them using for loop. + let v = vec![(), (), ()]; + let mut count = 0; + for _ in v { + count += 1; + } + assert_eq!(count, 3); + + // Same for consuming from some other Iterator. + let (tx, rx) = ::std::sync::mpsc::channel(); + tx.send(()).unwrap(); + drop(tx); + + count = 0; + for _ in rx.iter() { + count += 1; + } + assert_eq!(count, 1); +} + +fn multiline_sugg() { + let v: Vec = vec![2]; + + let _ = v + .into_iter() + .map(|i| i * 2) + .filter(|i| i % 2 == 0) + .map(|_| ()) + .next() + .unwrap(); +} + +#[derive(Copy, Clone)] +pub struct ContainsUnit(()); // should be fine diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr new file mode 100644 index 0000000000000..eb8482087bcc8 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_unit.stderr @@ -0,0 +1,38 @@ +error: this let-binding has unit value + --> $DIR/let_unit.rs:14:5 + | +LL | let _x = println!("x"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` + | + = note: `-D clippy::let-unit-value` implied by `-D warnings` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:18:9 + | +LL | let _a = (); + | ^^^^^^^^^^^^ help: omit the `let` binding: `();` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:53:5 + | +LL | / let _ = v +LL | | .into_iter() +LL | | .map(|i| i * 2) +LL | | .filter(|i| i % 2 == 0) +LL | | .map(|_| ()) +LL | | .next() +LL | | .unwrap(); + | |__________________^ + | +help: omit the `let` binding + | +LL | v +LL | .into_iter() +LL | .map(|i| i * 2) +LL | .filter(|i| i % 2 == 0) +LL | .map(|_| ()) +LL | .next() + ... + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui/lint_without_lint_pass.rs new file mode 100644 index 0000000000000..beaef79a340af --- /dev/null +++ b/src/tools/clippy/tests/ui/lint_without_lint_pass.rs @@ -0,0 +1,44 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; +use rustc_lint::LintPass; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL, + Warn, + "", + report_in_external_macro: true +} + +pub struct Pass; +impl LintPass for Pass { + fn name(&self) -> &'static str { + "TEST_LINT" + } +} + +declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]); + +pub struct Pass3; +impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/lint_without_lint_pass.stderr b/src/tools/clippy/tests/ui/lint_without_lint_pass.stderr new file mode 100644 index 0000000000000..1257dae96d71c --- /dev/null +++ b/src/tools/clippy/tests/ui/lint_without_lint_pass.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT` is not added to any `LintPass` + --> $DIR/lint_without_lint_pass.rs:11:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT, +LL | | Warn, +LL | | "", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/lint_without_lint_pass.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/literals.rs b/src/tools/clippy/tests/ui/literals.rs new file mode 100644 index 0000000000000..c299b16c8ce85 --- /dev/null +++ b/src/tools/clippy/tests/ui/literals.rs @@ -0,0 +1,36 @@ +// does not test any rustfixable lints + +#![warn(clippy::mixed_case_hex_literals)] +#![warn(clippy::zero_prefixed_literal)] +#![allow(clippy::unseparated_literal_suffix)] +#![allow(dead_code)] + +fn main() { + let ok1 = 0xABCD; + let ok3 = 0xab_cd; + let ok4 = 0xab_cd_i32; + let ok5 = 0xAB_CD_u32; + let ok5 = 0xAB_CD_isize; + let fail1 = 0xabCD; + let fail2 = 0xabCD_u32; + let fail2 = 0xabCD_isize; + let fail_multi_zero = 000_123usize; + + let ok9 = 0; + let ok10 = 0_i64; + let fail8 = 0123; + + let ok11 = 0o123; + let ok12 = 0b10_1010; + + let ok13 = 0xab_abcd; + let ok14 = 0xBAFE_BAFE; + let ok15 = 0xab_cabc_abca_bcab_cabc; + let ok16 = 0xFE_BAFE_ABAB_ABCD; + let ok17 = 0x123_4567_8901_usize; + let ok18 = 0xF; + + let fail19 = 12_3456_21; + let fail22 = 3__4___23; + let fail23 = 3__16___23; +} diff --git a/src/tools/clippy/tests/ui/literals.stderr b/src/tools/clippy/tests/ui/literals.stderr new file mode 100644 index 0000000000000..0b3af2d8bc35f --- /dev/null +++ b/src/tools/clippy/tests/ui/literals.stderr @@ -0,0 +1,73 @@ +error: inconsistent casing in hexadecimal literal + --> $DIR/literals.rs:14:17 + | +LL | let fail1 = 0xabCD; + | ^^^^^^ + | + = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings` + +error: inconsistent casing in hexadecimal literal + --> $DIR/literals.rs:15:17 + | +LL | let fail2 = 0xabCD_u32; + | ^^^^^^^^^^ + +error: inconsistent casing in hexadecimal literal + --> $DIR/literals.rs:16:17 + | +LL | let fail2 = 0xabCD_isize; + | ^^^^^^^^^^^^ + +error: this is a decimal constant + --> $DIR/literals.rs:17:27 + | +LL | let fail_multi_zero = 000_123usize; + | ^^^^^^^^^^^^ + | + = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings` +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let fail_multi_zero = 123usize; + | ^^^^^^^^ +help: if you mean to use an octal constant, use `0o` + | +LL | let fail_multi_zero = 0o123usize; + | ^^^^^^^^^^ + +error: this is a decimal constant + --> $DIR/literals.rs:21:17 + | +LL | let fail8 = 0123; + | ^^^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let fail8 = 123; + | ^^^ +help: if you mean to use an octal constant, use `0o` + | +LL | let fail8 = 0o123; + | ^^^^^ + +error: digits grouped inconsistently by underscores + --> $DIR/literals.rs:33:18 + | +LL | let fail19 = 12_3456_21; + | ^^^^^^^^^^ help: consider: `12_345_621` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: digits grouped inconsistently by underscores + --> $DIR/literals.rs:34:18 + | +LL | let fail22 = 3__4___23; + | ^^^^^^^^^ help: consider: `3_423` + +error: digits grouped inconsistently by underscores + --> $DIR/literals.rs:35:18 + | +LL | let fail23 = 3__16___23; + | ^^^^^^^^^^ help: consider: `31_623` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/logic_bug.rs b/src/tools/clippy/tests/ui/logic_bug.rs new file mode 100644 index 0000000000000..b4163d776e73a --- /dev/null +++ b/src/tools/clippy/tests/ui/logic_bug.rs @@ -0,0 +1,26 @@ +#![allow(unused, clippy::many_single_char_names)] +#![warn(clippy::logic_bug)] + +fn main() { + let a: bool = unimplemented!(); + let b: bool = unimplemented!(); + let c: bool = unimplemented!(); + let d: bool = unimplemented!(); + let e: bool = unimplemented!(); + let _ = a && b || a; + let _ = !(a && b); + let _ = false && a; + // don't lint on cfgs + let _ = cfg!(you_shall_not_not_pass) && a; + let _ = a || !b || !c || !d || !e; + let _ = !(a && b || c); +} + +fn equality_stuff() { + let a: i32 = unimplemented!(); + let b: i32 = unimplemented!(); + let _ = a == b && a != b; + let _ = a < b && a >= b; + let _ = a > b && a <= b; + let _ = a > b && a == b; +} diff --git a/src/tools/clippy/tests/ui/logic_bug.stderr b/src/tools/clippy/tests/ui/logic_bug.stderr new file mode 100644 index 0000000000000..8f55e1c8ad859 --- /dev/null +++ b/src/tools/clippy/tests/ui/logic_bug.stderr @@ -0,0 +1,63 @@ +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:10:13 + | +LL | let _ = a && b || a; + | ^^^^^^^^^^^ help: it would look like the following: `a` + | + = note: `-D clippy::logic-bug` implied by `-D warnings` +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:10:18 + | +LL | let _ = a && b || a; + | ^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:12:13 + | +LL | let _ = false && a; + | ^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:12:22 + | +LL | let _ = false && a; + | ^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:22:13 + | +LL | let _ = a == b && a != b; + | ^^^^^^^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:22:13 + | +LL | let _ = a == b && a != b; + | ^^^^^^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:23:13 + | +LL | let _ = a < b && a >= b; + | ^^^^^^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:23:13 + | +LL | let _ = a < b && a >= b; + | ^^^^^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:24:13 + | +LL | let _ = a > b && a <= b; + | ^^^^^^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:24:13 + | +LL | let _ = a > b && a <= b; + | ^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.fixed b/src/tools/clippy/tests/ui/lossy_float_literal.fixed new file mode 100644 index 0000000000000..24e372354fc05 --- /dev/null +++ b/src/tools/clippy/tests/ui/lossy_float_literal.fixed @@ -0,0 +1,35 @@ +// run-rustfix +#![warn(clippy::lossy_float_literal)] + +fn main() { + // Lossy whole-number float literals + let _: f32 = 16_777_216.0; + let _: f32 = 16_777_220.0; + let _: f32 = 16_777_220.0; + let _: f32 = 16_777_220.0; + let _ = 16_777_220_f32; + let _: f32 = -16_777_220.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = 9_007_199_254_740_992.0; + let _ = 9_007_199_254_740_992_f64; + let _: f64 = -9_007_199_254_740_992.0; + + // Lossless whole number float literals + let _: f32 = 16_777_216.0; + let _: f32 = 16_777_218.0; + let _: f32 = 16_777_220.0; + let _: f32 = -16_777_216.0; + let _: f32 = -16_777_220.0; + let _: f64 = 16_777_217.0; + let _: f64 = -16_777_217.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = -9_007_199_254_740_992.0; + + // Ignored whole number float literals + let _: f32 = 1e25; + let _: f32 = 1E25; + let _: f64 = 1e99; + let _: f64 = 1E99; + let _: f32 = 0.1; +} diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.rs b/src/tools/clippy/tests/ui/lossy_float_literal.rs new file mode 100644 index 0000000000000..3dcf98fa0bdda --- /dev/null +++ b/src/tools/clippy/tests/ui/lossy_float_literal.rs @@ -0,0 +1,35 @@ +// run-rustfix +#![warn(clippy::lossy_float_literal)] + +fn main() { + // Lossy whole-number float literals + let _: f32 = 16_777_217.0; + let _: f32 = 16_777_219.0; + let _: f32 = 16_777_219.; + let _: f32 = 16_777_219.000; + let _ = 16_777_219f32; + let _: f32 = -16_777_219.0; + let _: f64 = 9_007_199_254_740_993.0; + let _: f64 = 9_007_199_254_740_993.; + let _: f64 = 9_007_199_254_740_993.00; + let _ = 9_007_199_254_740_993f64; + let _: f64 = -9_007_199_254_740_993.0; + + // Lossless whole number float literals + let _: f32 = 16_777_216.0; + let _: f32 = 16_777_218.0; + let _: f32 = 16_777_220.0; + let _: f32 = -16_777_216.0; + let _: f32 = -16_777_220.0; + let _: f64 = 16_777_217.0; + let _: f64 = -16_777_217.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = -9_007_199_254_740_992.0; + + // Ignored whole number float literals + let _: f32 = 1e25; + let _: f32 = 1E25; + let _: f64 = 1e99; + let _: f64 = 1E99; + let _: f32 = 0.1; +} diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.stderr b/src/tools/clippy/tests/ui/lossy_float_literal.stderr new file mode 100644 index 0000000000000..d2193c0c81955 --- /dev/null +++ b/src/tools/clippy/tests/ui/lossy_float_literal.stderr @@ -0,0 +1,70 @@ +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:6:18 + | +LL | let _: f32 = 16_777_217.0; + | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0` + | + = note: `-D clippy::lossy-float-literal` implied by `-D warnings` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:7:18 + | +LL | let _: f32 = 16_777_219.0; + | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:8:18 + | +LL | let _: f32 = 16_777_219.; + | ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:9:18 + | +LL | let _: f32 = 16_777_219.000; + | ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:10:13 + | +LL | let _ = 16_777_219f32; + | ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:11:19 + | +LL | let _: f32 = -16_777_219.0; + | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:12:18 + | +LL | let _: f64 = 9_007_199_254_740_993.0; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:13:18 + | +LL | let _: f64 = 9_007_199_254_740_993.; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:14:18 + | +LL | let _: f64 = 9_007_199_254_740_993.00; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:15:13 + | +LL | let _ = 9_007_199_254_740_993f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:16:19 + | +LL | let _: f64 = -9_007_199_254_740_993.0; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs new file mode 100644 index 0000000000000..60c64ee8146e5 --- /dev/null +++ b/src/tools/clippy/tests/ui/macro_use_imports.rs @@ -0,0 +1,11 @@ +// edition:2018 +#![warn(clippy::macro_use_imports)] + +use std::collections::HashMap; +#[macro_use] +use std::prelude; + +fn main() { + let _ = HashMap::::new(); + println!(); +} diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr new file mode 100644 index 0000000000000..b5e3dbec57277 --- /dev/null +++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr @@ -0,0 +1,10 @@ +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:5:1 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::` + | + = note: `-D clippy::macro-use-imports` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed new file mode 100644 index 0000000000000..6bb1032a17299 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed @@ -0,0 +1,67 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::manual_async_fn)] +#![allow(unused)] + +use std::future::Future; + +async fn fut() -> i32 { 42 } + +async fn empty_fut() {} + +async fn core_fut() -> i32 { 42 } + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + async fn inh_fut() -> i32 { + // NOTE: this code is here just to check that the identation is correct in the suggested fix + let a = 42; + let b = 21; + if a < b { + let c = 21; + let d = 42; + if c < d { + let _ = 42; + } + } + 42 + } + + async fn meth_fut(&self) -> i32 { 42 } + + async fn empty_fut(&self) {} + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs new file mode 100644 index 0000000000000..d50c919188be1 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_async_fn.rs @@ -0,0 +1,79 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::manual_async_fn)] +#![allow(unused)] + +use std::future::Future; + +fn fut() -> impl Future { + async { 42 } +} + +fn empty_fut() -> impl Future { + async {} +} + +fn core_fut() -> impl core::future::Future { + async move { 42 } +} + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + fn inh_fut() -> impl Future { + async { + // NOTE: this code is here just to check that the identation is correct in the suggested fix + let a = 42; + let b = 21; + if a < b { + let c = 21; + let d = 42; + if c < d { + let _ = 42; + } + } + 42 + } + } + + fn meth_fut(&self) -> impl Future { + async { 42 } + } + + fn empty_fut(&self) -> impl Future { + async {} + } + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_async_fn.stderr b/src/tools/clippy/tests/ui/manual_async_fn.stderr new file mode 100644 index 0000000000000..f278ee41aa335 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_async_fn.stderr @@ -0,0 +1,98 @@ +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:8:1 + | +LL | fn fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-async-fn` implied by `-D warnings` +help: make the function `async` and return the output of the future directly + | +LL | async fn fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut() -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:12:1 + | +LL | fn empty_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut() { + | ^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut() -> impl Future {} + | ^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:16:1 + | +LL | fn core_fut() -> impl core::future::Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn core_fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn core_fut() -> impl core::future::Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:38:5 + | +LL | fn inh_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn inh_fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn inh_fut() -> impl Future { +LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix +LL | let a = 42; +LL | let b = 21; +LL | if a < b { +LL | let c = 21; + ... + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:54:5 + | +LL | fn meth_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn meth_fut(&self) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn meth_fut(&self) -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:58:5 + | +LL | fn empty_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut(&self) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut(&self) -> impl Future {} + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_memcpy.rs b/src/tools/clippy/tests/ui/manual_memcpy.rs new file mode 100644 index 0000000000000..0083f94798fe4 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_memcpy.rs @@ -0,0 +1,125 @@ +#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] + +const LOOP_OFFSET: usize = 5000; + +pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + // plain manual memcpy + for i in 0..src.len() { + dst[i] = src[i]; + } + + // dst offset memcpy + for i in 0..src.len() { + dst[i + 10] = src[i]; + } + + // src offset memcpy + for i in 0..src.len() { + dst[i] = src[i + 10]; + } + + // src offset memcpy + for i in 11..src.len() { + dst[i] = src[i - 10]; + } + + // overwrite entire dst + for i in 0..dst.len() { + dst[i] = src[i]; + } + + // manual copy with branch - can't easily convert to memcpy! + for i in 0..src.len() { + dst[i] = src[i]; + if dst[i] > 5 { + break; + } + } + + // multiple copies - suggest two memcpy statements + for i in 10..256 { + dst[i] = src[i - 5]; + dst2[i + 500] = src[i] + } + + // this is a reversal - the copy lint shouldn't be triggered + for i in 10..LOOP_OFFSET { + dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i]; + } + + let some_var = 5; + // Offset in variable + for i in 10..LOOP_OFFSET { + dst[i + LOOP_OFFSET] = src[i - some_var]; + } + + // Non continuous copy - don't trigger lint + for i in 0..10 { + dst[i + i] = src[i]; + } + + let src_vec = vec![1, 2, 3, 4, 5]; + let mut dst_vec = vec![0, 0, 0, 0, 0]; + + // make sure vectors are supported + for i in 0..src_vec.len() { + dst_vec[i] = src_vec[i]; + } + + // lint should not trigger when either + // source or destination type is not + // slice-like, like DummyStruct + struct DummyStruct(i32); + + impl ::std::ops::Index for DummyStruct { + type Output = i32; + + fn index(&self, _: usize) -> &i32 { + &self.0 + } + } + + let src = DummyStruct(5); + let mut dst_vec = vec![0; 10]; + + for i in 0..10 { + dst_vec[i] = src[i]; + } + + // Simplify suggestion (issue #3004) + let src = [0, 1, 2, 3, 4]; + let mut dst = [0, 0, 0, 0, 0, 0]; + let from = 1; + + for i in from..from + src.len() { + dst[i] = src[i - from]; + } + + for i in from..from + 3 { + dst[i] = src[i - from]; + } + + #[allow(clippy::identity_op)] + for i in 0..5 { + dst[i - 0] = src[i]; + } + + #[allow(clippy::reversed_empty_ranges)] + for i in 0..0 { + dst[i] = src[i]; + } + + // `RangeTo` `for` loop - don't trigger lint + for i in 0.. { + dst[i] = src[i]; + } +} + +#[warn(clippy::needless_range_loop, clippy::manual_memcpy)] +pub fn manual_clone(src: &[String], dst: &mut [String]) { + for i in 0..src.len() { + dst[i] = src[i].clone(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_memcpy.stderr b/src/tools/clippy/tests/ui/manual_memcpy.stderr new file mode 100644 index 0000000000000..bad84a5890081 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_memcpy.stderr @@ -0,0 +1,88 @@ +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:7:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` + | + = note: `-D clippy::manual-memcpy` implied by `-D warnings` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:12:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:17:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:22:14 + | +LL | for i in 11..src.len() { + | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:27:14 + | +LL | for i in 0..dst.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:40:14 + | +LL | for i in 10..256 { + | ^^^^^^^ + | +help: try replacing the loop by + | +LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]) +LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) { + | + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:52:14 + | +LL | for i in 10..LOOP_OFFSET { + | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:65:14 + | +LL | for i in 0..src_vec.len() { + | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:94:14 + | +LL | for i in from..from + src.len() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:98:14 + | +LL | for i in from..from + 3 { + | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:103:14 + | +LL | for i in 0..5 { + | ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:108:14 + | +LL | for i in 0..0 { + | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:120:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive.rs new file mode 100644 index 0000000000000..7a788f4852072 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive.rs @@ -0,0 +1,137 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +mod enums { + enum E { + A, + B, + #[doc(hidden)] + _C, + } + + // user forgot to remove the marker + #[non_exhaustive] + enum Ep { + A, + B, + #[doc(hidden)] + _C, + } + + // marker variant does not have doc hidden attribute, should be ignored + enum NoDocHidden { + A, + B, + _C, + } + + // name of variant with doc hidden does not start with underscore, should be ignored + enum NoUnderscore { + A, + B, + #[doc(hidden)] + C, + } + + // variant with doc hidden is not unit, should be ignored + enum NotUnit { + A, + B, + #[doc(hidden)] + _C(bool), + } + + // variant with doc hidden is the only one, should be ignored + enum OnlyMarker { + #[doc(hidden)] + _A, + } + + // variant with multiple markers, should be ignored + enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, + } + + // already non_exhaustive and no markers, should be ignored + #[non_exhaustive] + enum NonExhaustive { + A, + B, + } +} + +mod structs { + struct S { + pub a: i32, + pub b: i32, + _c: (), + } + + // user forgot to remove the private field + #[non_exhaustive] + struct Sp { + pub a: i32, + pub b: i32, + _c: (), + } + + // some other fields are private, should be ignored + struct PrivateFields { + a: i32, + pub b: i32, + _c: (), + } + + // private field name does not start with underscore, should be ignored + struct NoUnderscore { + pub a: i32, + pub b: i32, + c: (), + } + + // private field is not unit type, should be ignored + struct NotUnit { + pub a: i32, + pub b: i32, + _c: i32, + } + + // private field is the only field, should be ignored + struct OnlyMarker { + _a: (), + } + + // already non exhaustive and no private fields, should be ignored + #[non_exhaustive] + struct NonExhaustive { + pub a: i32, + pub b: i32, + } +} + +mod tuple_structs { + struct T(pub i32, pub i32, ()); + + // user forgot to remove the private field + #[non_exhaustive] + struct Tp(pub i32, pub i32, ()); + + // some other fields are private, should be ignored + struct PrivateFields(pub i32, i32, ()); + + // private field is not unit type, should be ignored + struct NotUnit(pub i32, pub i32, i32); + + // private field is the only field, should be ignored + struct OnlyMarker(()); + + // already non exhaustive and no private fields, should be ignored + #[non_exhaustive] + struct NonExhaustive(pub i32, pub i32); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive.stderr new file mode 100644 index 0000000000000..613c5e8ca1d45 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive.stderr @@ -0,0 +1,103 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:5:5 + | +LL | enum E { + | ^----- + | | + | _____help: add the attribute: `#[non_exhaustive] enum E` + | | +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_____^ + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: remove this variant + --> $DIR/manual_non_exhaustive.rs:9:9 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:14:5 + | +LL | / enum Ep { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_____^ + | +help: remove this variant + --> $DIR/manual_non_exhaustive.rs:18:9 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:68:5 + | +LL | struct S { + | ^------- + | | + | _____help: add the attribute: `#[non_exhaustive] struct S` + | | +LL | | pub a: i32, +LL | | pub b: i32, +LL | | _c: (), +LL | | } + | |_____^ + | +help: remove this field + --> $DIR/manual_non_exhaustive.rs:71:9 + | +LL | _c: (), + | ^^^^^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:76:5 + | +LL | / struct Sp { +LL | | pub a: i32, +LL | | pub b: i32, +LL | | _c: (), +LL | | } + | |_____^ + | +help: remove this field + --> $DIR/manual_non_exhaustive.rs:79:9 + | +LL | _c: (), + | ^^^^^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:117:5 + | +LL | struct T(pub i32, pub i32, ()); + | --------^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: add the attribute: `#[non_exhaustive] struct T` + | +help: remove this field + --> $DIR/manual_non_exhaustive.rs:117:32 + | +LL | struct T(pub i32, pub i32, ()); + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:121:5 + | +LL | struct Tp(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove this field + --> $DIR/manual_non_exhaustive.rs:121:33 + | +LL | struct Tp(pub i32, pub i32, ()); + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed new file mode 100644 index 0000000000000..c4f53c446c9f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![allow(unused_imports)] + +use std::{i128, i32, u128, u32}; + +fn main() { + let _ = 1u32.saturating_add(1); + let _ = 1u32.saturating_add(1); + let _ = 1u8.saturating_add(1); + let _ = 1u128.saturating_add(1); + let _ = 1u32.checked_add(1).unwrap_or(1234); // ok + let _ = 1u8.checked_add(1).unwrap_or(0); // ok + let _ = 1u32.saturating_mul(1); + + let _ = 1u32.saturating_sub(1); + let _ = 1u32.saturating_sub(1); + let _ = 1u8.saturating_sub(1); + let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1u8.checked_sub(1).unwrap_or(255); // ok + + let _ = 1i32.saturating_add(1); + let _ = 1i32.saturating_add(1); + let _ = 1i8.saturating_add(1); + let _ = 1i128.saturating_add(1); + let _ = 1i32.saturating_add(-1); + let _ = 1i32.saturating_add(-1); + let _ = 1i8.saturating_add(-1); + let _ = 1i128.saturating_add(-1); + let _ = 1i32.checked_add(1).unwrap_or(1234); // ok + let _ = 1i8.checked_add(1).unwrap_or(-128); // ok + let _ = 1i8.checked_add(-1).unwrap_or(127); // ok + + let _ = 1i32.saturating_sub(1); + let _ = 1i32.saturating_sub(1); + let _ = 1i8.saturating_sub(1); + let _ = 1i128.saturating_sub(1); + let _ = 1i32.saturating_sub(-1); + let _ = 1i32.saturating_sub(-1); + let _ = 1i8.saturating_sub(-1); + let _ = 1i128.saturating_sub(-1); + let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1i8.checked_sub(1).unwrap_or(127); // ok + let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok +} diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs new file mode 100644 index 0000000000000..cd83cf6e65e94 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs @@ -0,0 +1,55 @@ +// run-rustfix + +#![allow(unused_imports)] + +use std::{i128, i32, u128, u32}; + +fn main() { + let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); + let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); + let _ = 1u8.checked_add(1).unwrap_or(255); + let _ = 1u128 + .checked_add(1) + .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); + let _ = 1u32.checked_add(1).unwrap_or(1234); // ok + let _ = 1u8.checked_add(1).unwrap_or(0); // ok + let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); + + let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); + let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); + let _ = 1u8.checked_sub(1).unwrap_or(0); + let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1u8.checked_sub(1).unwrap_or(255); // ok + + let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); + let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); + let _ = 1i8.checked_add(1).unwrap_or(127); + let _ = 1i128 + .checked_add(1) + .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); + let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); + let _ = 1i8.checked_add(-1).unwrap_or(-128); + let _ = 1i128 + .checked_add(-1) + .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + let _ = 1i32.checked_add(1).unwrap_or(1234); // ok + let _ = 1i8.checked_add(1).unwrap_or(-128); // ok + let _ = 1i8.checked_add(-1).unwrap_or(127); // ok + + let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); + let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); + let _ = 1i8.checked_sub(1).unwrap_or(-128); + let _ = 1i128 + .checked_sub(1) + .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); + let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); + let _ = 1i8.checked_sub(-1).unwrap_or(127); + let _ = 1i128 + .checked_sub(-1) + .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1i8.checked_sub(1).unwrap_or(127); // ok + let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok +} diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr new file mode 100644 index 0000000000000..d985f2e754bc3 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr @@ -0,0 +1,163 @@ +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:8:13 + | +LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + | + = note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:9:13 + | +LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:10:13 + | +LL | let _ = 1u8.checked_add(1).unwrap_or(255); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:11:13 + | +LL | let _ = 1u128 + | _____________^ +LL | | .checked_add(1) +LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); + | |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:16:13 + | +LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:18:13 + | +LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:19:13 + | +LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:20:13 + | +LL | let _ = 1u8.checked_sub(1).unwrap_or(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:24:13 + | +LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:25:13 + | +LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:26:13 + | +LL | let _ = 1i8.checked_add(1).unwrap_or(127); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:27:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_add(1) +LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + | |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:30:13 + | +LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:31:13 + | +LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:32:13 + | +LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:33:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_add(-1) +LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + | |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:40:13 + | +LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:41:13 + | +LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:42:13 + | +LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:43:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_sub(1) +LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + | |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:46:13 + | +LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:47:13 + | +LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:48:13 + | +LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:49:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_sub(-1) +LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + | |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)` + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/many_single_char_names.rs b/src/tools/clippy/tests/ui/many_single_char_names.rs new file mode 100644 index 0000000000000..80800e487248f --- /dev/null +++ b/src/tools/clippy/tests/ui/many_single_char_names.rs @@ -0,0 +1,73 @@ +#[warn(clippy::many_single_char_names)] + +fn bla() { + let a: i32; + let (b, c, d): (i32, i64, i16); + { + { + let cdefg: i32; + let blar: i32; + } + { + let e: i32; + } + { + let e: i32; + let f: i32; + } + match 5 { + 1 => println!(), + e => panic!(), + } + match 5 { + 1 => println!(), + _ => panic!(), + } + } +} + +fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {} + +fn bindings2() { + let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!(); +} + +fn shadowing() { + let a = 0i32; + let a = 0i32; + let a = 0i32; + let a = 0i32; + let a = 0i32; + let a = 0i32; + { + let a = 0i32; + } +} + +fn patterns() { + enum Z { + A(i32), + B(i32), + C(i32), + D(i32), + E(i32), + F(i32), + } + + // These should not trigger a warning, since the pattern bindings are a new scope. + match Z::A(0) { + Z::A(a) => {}, + Z::B(b) => {}, + Z::C(c) => {}, + Z::D(d) => {}, + Z::E(e) => {}, + Z::F(f) => {}, + } +} + +#[allow(clippy::many_single_char_names)] +fn issue_3198_allow_works() { + let (a, b, c, d, e) = (0, 0, 0, 0, 0); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/many_single_char_names.stderr b/src/tools/clippy/tests/ui/many_single_char_names.stderr new file mode 100644 index 0000000000000..27e62e641ade9 --- /dev/null +++ b/src/tools/clippy/tests/ui/many_single_char_names.stderr @@ -0,0 +1,51 @@ +error: 5 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:4:9 + | +LL | let a: i32; + | ^ +LL | let (b, c, d): (i32, i64, i16); + | ^ ^ ^ +... +LL | let e: i32; + | ^ + | + = note: `-D clippy::many-single-char-names` implied by `-D warnings` + +error: 6 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:4:9 + | +LL | let a: i32; + | ^ +LL | let (b, c, d): (i32, i64, i16); + | ^ ^ ^ +... +LL | let e: i32; + | ^ +LL | let f: i32; + | ^ + +error: 5 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:4:9 + | +LL | let a: i32; + | ^ +LL | let (b, c, d): (i32, i64, i16); + | ^ ^ ^ +... +LL | e => panic!(), + | ^ + +error: 8 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:29:13 + | +LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {} + | ^ ^ ^ ^ ^ ^ ^ ^ + +error: 8 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:32:10 + | +LL | let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!(); + | ^ ^ ^ ^ ^ ^ ^ ^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed new file mode 100644 index 0000000000000..81c7f659efb1f --- /dev/null +++ b/src/tools/clippy/tests/ui/map_clone.fixed @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::iter_cloned_collect)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] +#![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::redundant_closure_for_method_calls)] +#![allow(clippy::many_single_char_names)] + +fn main() { + let _: Vec = vec![5_i8; 6].iter().copied().collect(); + let _: Vec = vec![String::new()].iter().cloned().collect(); + let _: Vec = vec![42, 43].iter().copied().collect(); + let _: Option = Some(Box::new(16)).map(|b| *b); + let _: Option = Some(&16).copied(); + let _: Option = Some(&1).copied(); + + // Don't lint these + let v = vec![5_i8; 6]; + let a = 0; + let b = &a; + let _ = v.iter().map(|_x| *b); + let _ = v.iter().map(|_x| a.clone()); + let _ = v.iter().map(|&_x| a); + + // Issue #498 + let _ = std::env::args(); + + // Issue #4824 item types that aren't references + { + use std::rc::Rc; + + let o: Option> = Some(Rc::new(0_u32)); + let _: Option = o.map(|x| *x); + let v: Vec> = vec![Rc::new(0_u32)]; + let _: Vec = v.into_iter().map(|x| *x).collect(); + } + + // Issue #5524 mutable references + { + let mut c = 42; + let v = vec![&mut c]; + let _: Vec = v.into_iter().map(|x| *x).collect(); + let mut d = 21; + let v = vec![&mut d]; + let _: Vec = v.into_iter().map(|&mut x| x).collect(); + } +} diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs new file mode 100644 index 0000000000000..8ed164f0ed51d --- /dev/null +++ b/src/tools/clippy/tests/ui/map_clone.rs @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::iter_cloned_collect)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] +#![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::redundant_closure_for_method_calls)] +#![allow(clippy::many_single_char_names)] + +fn main() { + let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); + let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); + let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); + let _: Option = Some(Box::new(16)).map(|b| *b); + let _: Option = Some(&16).map(|b| *b); + let _: Option = Some(&1).map(|x| x.clone()); + + // Don't lint these + let v = vec![5_i8; 6]; + let a = 0; + let b = &a; + let _ = v.iter().map(|_x| *b); + let _ = v.iter().map(|_x| a.clone()); + let _ = v.iter().map(|&_x| a); + + // Issue #498 + let _ = std::env::args().map(|v| v.clone()); + + // Issue #4824 item types that aren't references + { + use std::rc::Rc; + + let o: Option> = Some(Rc::new(0_u32)); + let _: Option = o.map(|x| *x); + let v: Vec> = vec![Rc::new(0_u32)]; + let _: Vec = v.into_iter().map(|x| *x).collect(); + } + + // Issue #5524 mutable references + { + let mut c = 42; + let v = vec![&mut c]; + let _: Vec = v.into_iter().map(|x| *x).collect(); + let mut d = 21; + let v = vec![&mut d]; + let _: Vec = v.into_iter().map(|&mut x| x).collect(); + } +} diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr new file mode 100644 index 0000000000000..9eec6928e8cee --- /dev/null +++ b/src/tools/clippy/tests/ui/map_clone.stderr @@ -0,0 +1,40 @@ +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:10:22 + | +LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` + | + = note: `-D clippy::map-clone` implied by `-D warnings` + +error: You are using an explicit closure for cloning elements + --> $DIR/map_clone.rs:11:26 + | +LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` + +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:12:23 + | +LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` + +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:14:26 + | +LL | let _: Option = Some(&16).map(|b| *b); + | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()` + +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:15:25 + | +LL | let _: Option = Some(&1).map(|x| x.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()` + +error: You are needlessly cloning iterator elements + --> $DIR/map_clone.rs:26:29 + | +LL | let _ = std::env::args().map(|v| v.clone()); + | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/map_flatten.fixed b/src/tools/clippy/tests/ui/map_flatten.fixed new file mode 100644 index 0000000000000..7ac368878ab7e --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten.fixed @@ -0,0 +1,9 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_docs_in_private_items)] + +fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + let _: Option<_> = (Some(Some(1))).and_then(|x| x); +} diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs new file mode 100644 index 0000000000000..a608601039ce7 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -0,0 +1,9 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_docs_in_private_items)] + +fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); +} diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr new file mode 100644 index 0000000000000..3cf2abd5b6d85 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten.stderr @@ -0,0 +1,16 @@ +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` + --> $DIR/map_flatten.rs:7:21 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + | + = note: `-D clippy::map-flatten` implied by `-D warnings` + +error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` + --> $DIR/map_flatten.rs:8:24 + | +LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/map_unit_fn.rs b/src/tools/clippy/tests/ui/map_unit_fn.rs new file mode 100644 index 0000000000000..9a74da4e3b8b6 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_unit_fn.rs @@ -0,0 +1,11 @@ +#![allow(unused)] +struct Mappable {} + +impl Mappable { + pub fn map(&self) {} +} + +fn main() { + let m = Mappable {}; + m.map(); +} diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs new file mode 100644 index 0000000000000..585944032e70d --- /dev/null +++ b/src/tools/clippy/tests/ui/map_unwrap_or.rs @@ -0,0 +1,99 @@ +// FIXME: Add "run-rustfix" once it's supported for multipart suggestions +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or(0); + let _ = opt.map(|x| x + 1) + .unwrap_or({ + 0 + }); + // Single line `map(f).unwrap_or(None)` case. + let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + // Multi-line `map(f).unwrap_or(None)` cases. + let _ = opt.map(|x| { + Some(x + 1) + } + ).unwrap_or(None); + let _ = opt + .map(|x| Some(x + 1)) + .unwrap_or(None); + // macro case + let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint + + // Should not lint if not copyable + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); + // ...but DO lint if the `unwrap_or` argument is not used in the `map` + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or_else(|| 0); + let _ = opt.map(|x| x + 1) + .unwrap_or_else(|| + 0 + ); + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr new file mode 100644 index 0000000000000..b62080a073f35 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_unwrap_or.stderr @@ -0,0 +1,161 @@ +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:17:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or(0); + | |_____________________^ + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| x + 1); + | ^^^^^^ ^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:21:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or(0); + | |__________________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| { +LL | x + 1 +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:25:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or({ +LL | | 0 +LL | | }); + | |__________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or({ +LL | 0 +LL | }, |x| x + 1); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap_or.rs:30:13 + | +LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap_or.rs:32:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | Some(x + 1) +LL | | } +LL | | ).unwrap_or(None); + | |_____________________^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap_or.rs:36:13 + | +LL | let _ = opt + | _____________^ +LL | | .map(|x| Some(x + 1)) +LL | | .unwrap_or(None); + | |________________________^ + | +help: use `and_then(f)` instead + | +LL | .and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:47:13 + | +LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); + | ^^^^^^ ^^^ -- + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:51:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:55:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or_else(|| 0); + | |__________________________^ + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:59:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|| +LL | | 0 +LL | | ); + | |_________^ + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:88:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:90:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:91:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/match_as_ref.fixed b/src/tools/clippy/tests/ui/match_as_ref.fixed new file mode 100644 index 0000000000000..c61eb9216643e --- /dev/null +++ b/src/tools/clippy/tests/ui/match_as_ref.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::match_as_ref)] + +fn match_as_ref() { + let owned: Option<()> = None; + let borrowed: Option<&()> = owned.as_ref(); + + let mut mut_owned: Option<()> = None; + let borrow_mut: Option<&mut ()> = mut_owned.as_mut(); +} + +mod issue4437 { + use std::{error::Error, fmt, num::ParseIntError}; + + #[derive(Debug)] + struct E { + source: Option, + } + + impl Error for E { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.source.as_ref().map(|x| x as _) + } + } + + impl fmt::Display for E { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unimplemented!() + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_as_ref.rs b/src/tools/clippy/tests/ui/match_as_ref.rs new file mode 100644 index 0000000000000..2fbd0b255faae --- /dev/null +++ b/src/tools/clippy/tests/ui/match_as_ref.rs @@ -0,0 +1,44 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::match_as_ref)] + +fn match_as_ref() { + let owned: Option<()> = None; + let borrowed: Option<&()> = match owned { + None => None, + Some(ref v) => Some(v), + }; + + let mut mut_owned: Option<()> = None; + let borrow_mut: Option<&mut ()> = match mut_owned { + None => None, + Some(ref mut v) => Some(v), + }; +} + +mod issue4437 { + use std::{error::Error, fmt, num::ParseIntError}; + + #[derive(Debug)] + struct E { + source: Option, + } + + impl Error for E { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self.source { + Some(ref s) => Some(s), + None => None, + } + } + } + + impl fmt::Display for E { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unimplemented!() + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_as_ref.stderr b/src/tools/clippy/tests/ui/match_as_ref.stderr new file mode 100644 index 0000000000000..c3b62849cb33f --- /dev/null +++ b/src/tools/clippy/tests/ui/match_as_ref.stderr @@ -0,0 +1,33 @@ +error: use `as_ref()` instead + --> $DIR/match_as_ref.rs:8:33 + | +LL | let borrowed: Option<&()> = match owned { + | _________________________________^ +LL | | None => None, +LL | | Some(ref v) => Some(v), +LL | | }; + | |_____^ help: try this: `owned.as_ref()` + | + = note: `-D clippy::match-as-ref` implied by `-D warnings` + +error: use `as_mut()` instead + --> $DIR/match_as_ref.rs:14:39 + | +LL | let borrow_mut: Option<&mut ()> = match mut_owned { + | _______________________________________^ +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ help: try this: `mut_owned.as_mut()` + +error: use `as_ref()` instead + --> $DIR/match_as_ref.rs:30:13 + | +LL | / match self.source { +LL | | Some(ref s) => Some(s), +LL | | None => None, +LL | | } + | |_____________^ help: try this: `self.source.as_ref().map(|x| x as _)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/match_bool.rs b/src/tools/clippy/tests/ui/match_bool.rs new file mode 100644 index 0000000000000..9ed55ca7ae7f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_bool.rs @@ -0,0 +1,55 @@ +#![deny(clippy::match_bool)] + +fn match_bool() { + let test: bool = true; + + match test { + true => 0, + false => 42, + }; + + let option = 1; + match option == 1 { + true => 1, + false => 0, + }; + + match test { + true => (), + false => { + println!("Noooo!"); + }, + }; + + match test { + false => { + println!("Noooo!"); + }, + _ => (), + }; + + match test && test { + false => { + println!("Noooo!"); + }, + _ => (), + }; + + match test { + false => { + println!("Noooo!"); + }, + true => { + println!("Yes!"); + }, + }; + + // Not linted + match option { + 1..=10 => 1, + 11..=20 => 2, + _ => 3, + }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.stderr b/src/tools/clippy/tests/ui/match_bool.stderr new file mode 100644 index 0000000000000..1ad78c740c68b --- /dev/null +++ b/src/tools/clippy/tests/ui/match_bool.stderr @@ -0,0 +1,117 @@ +error: this boolean expression can be simplified + --> $DIR/match_bool.rs:31:11 + | +LL | match test && test { + | ^^^^^^^^^^^^ help: try: `test` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:6:5 + | +LL | / match test { +LL | | true => 0, +LL | | false => 42, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }` + | +note: the lint level is defined here + --> $DIR/match_bool.rs:1:9 + | +LL | #![deny(clippy::match_bool)] + | ^^^^^^^^^^^^^^^^^^ + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:12:5 + | +LL | / match option == 1 { +LL | | true => 1, +LL | | false => 0, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if option == 1 { 1 } else { 0 }` + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:17:5 + | +LL | / match test { +LL | | true => (), +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if !test { +LL | println!("Noooo!"); +LL | }; + | + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:24:5 + | +LL | / match test { +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +LL | | _ => (), +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if !test { +LL | println!("Noooo!"); +LL | }; + | + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:31:5 + | +LL | / match test && test { +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +LL | | _ => (), +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if !(test && test) { +LL | println!("Noooo!"); +LL | }; + | + +error: equal expressions as operands to `&&` + --> $DIR/match_bool.rs:31:11 + | +LL | match test && test { + | ^^^^^^^^^^^^ + | + = note: `#[deny(clippy::eq_op)]` on by default + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:38:5 + | +LL | / match test { +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +... | +LL | | }, +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if test { +LL | println!("Yes!"); +LL | } else { +LL | println!("Noooo!"); +LL | }; + | + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.rs b/src/tools/clippy/tests/ui/match_on_vec_items.rs new file mode 100644 index 0000000000000..30415e3b94dc4 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_on_vec_items.rs @@ -0,0 +1,152 @@ +#![warn(clippy::match_on_vec_items)] + +fn match_with_wildcard() { + let arr = vec![0, 1, 2, 3]; + let range = 1..3; + let idx = 1; + + // Lint, may panic + match arr[idx] { + 0 => println!("0"), + 1 => println!("1"), + _ => {}, + } + + // Lint, may panic + match arr[range] { + [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] { + 0 => println!("0"), + 1 => println!("1"), + num => {}, + } + + // Lint, may panic + match arr[range] { + [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] { + 0 => println!("0"), + 1 => println!("1"), + _ => println!("Hello, World!"), + } + + // Lint, may panic + match arr[range] { + [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] { + 0 => println!("0"), + 1 => println!("1"), + _ => {}, + } + + // Lint, may panic + match arr[range] { + [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 new file mode 100644 index 0000000000000..49446d715abe2 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_on_vec_items.stderr @@ -0,0 +1,52 @@ +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:9:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + | + = note: `-D clippy::match-on-vec-items` implied by `-D warnings` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:16:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:29:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:36:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:49:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:56:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:69:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:76:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs new file mode 100644 index 0000000000000..97789bb766f89 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs @@ -0,0 +1,82 @@ +#![feature(exclusive_range_pattern)] +#![feature(half_open_range_patterns)] +#![warn(clippy::match_overlapping_arm)] +#![allow(clippy::redundant_pattern_matching)] + +/// Tests for match_overlapping_arm + +fn overlapping() { + const FOO: u64 = 2; + + match 42 { + 0..=10 => println!("0 ... 10"), + 0..=11 => println!("0 ... 11"), + _ => (), + } + + match 42 { + 0..=5 => println!("0 ... 5"), + 6..=7 => println!("6 ... 7"), + FOO..=11 => println!("0 ... 11"), + _ => (), + } + + match 42 { + 2 => println!("2"), + 0..=5 => println!("0 ... 5"), + _ => (), + } + + match 42 { + 2 => println!("2"), + 0..=2 => println!("0 ... 2"), + _ => (), + } + + match 42 { + 0..=10 => println!("0 ... 10"), + 11..=50 => println!("11 ... 50"), + _ => (), + } + + match 42 { + 2 => println!("2"), + 0..2 => println!("0 .. 2"), + _ => (), + } + + match 42 { + 0..10 => println!("0 .. 10"), + 10..50 => println!("10 .. 50"), + _ => (), + } + + match 42 { + 0..11 => println!("0 .. 11"), + 0..=11 => println!("0 ... 11"), + _ => (), + } + + /* + // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns + match 42 { + 0.. => println!("0 .. 42"), + 3.. => println!("3 .. 42"), + _ => (), + } + + match 42 { + ..=23 => println!("0 ... 23"), + ..26 => println!("0 .. 26"), + _ => (), + } + */ + + if let None = Some(42) { + // nothing + } else if let None = Some(42) { + // another nothing :-) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr new file mode 100644 index 0000000000000..eb20d5405a95e --- /dev/null +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr @@ -0,0 +1,63 @@ +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:12:9 + | +LL | 0..=10 => println!("0 ... 10"), + | ^^^^^^ + | + = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:13:9 + | +LL | 0..=11 => println!("0 ... 11"), + | ^^^^^^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:18:9 + | +LL | 0..=5 => println!("0 ... 5"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:20:9 + | +LL | FOO..=11 => println!("0 ... 11"), + | ^^^^^^^^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:26:9 + | +LL | 0..=5 => println!("0 ... 5"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:25:9 + | +LL | 2 => println!("2"), + | ^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:32:9 + | +LL | 0..=2 => println!("0 ... 2"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:31:9 + | +LL | 2 => println!("2"), + | ^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:55:9 + | +LL | 0..11 => println!("0 .. 11"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:56:9 + | +LL | 0..=11 => println!("0 ... 11"), + | ^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/match_ref_pats.rs b/src/tools/clippy/tests/ui/match_ref_pats.rs new file mode 100644 index 0000000000000..5de43733ad336 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_ref_pats.rs @@ -0,0 +1,74 @@ +#![warn(clippy::match_ref_pats)] + +fn ref_pats() { + { + let v = &Some(0); + match v { + &Some(v) => println!("{:?}", v), + &None => println!("none"), + } + match v { + // This doesn't trigger; we have a different pattern. + &Some(v) => println!("some"), + other => println!("other"), + } + } + let tup = &(1, 2); + match tup { + &(v, 1) => println!("{}", v), + _ => println!("none"), + } + // Special case: using `&` both in expr and pats. + let w = Some(0); + match &w { + &Some(v) => println!("{:?}", v), + &None => println!("none"), + } + // False positive: only wildcard pattern. + let w = Some(0); + #[allow(clippy::match_single_binding)] + match w { + _ => println!("none"), + } + + let a = &Some(0); + if let &None = a { + println!("none"); + } + + let b = Some(0); + if let &None = &b { + println!("none"); + } +} + +mod ice_3719 { + macro_rules! foo_variant( + ($idx:expr) => (Foo::get($idx).unwrap()) + ); + + enum Foo { + A, + B, + } + + impl Foo { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&Foo::A), + 1 => Some(&Foo::B), + _ => None, + } + } + } + + fn ice_3719() { + // ICE #3719 + match foo_variant!(0) { + &Foo::A => println!("A"), + _ => println!("Wild"), + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_ref_pats.stderr b/src/tools/clippy/tests/ui/match_ref_pats.stderr new file mode 100644 index 0000000000000..52cb4a14b72bc --- /dev/null +++ b/src/tools/clippy/tests/ui/match_ref_pats.stderr @@ -0,0 +1,91 @@ +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:6:9 + | +LL | / match v { +LL | | &Some(v) => println!("{:?}", v), +LL | | &None => println!("none"), +LL | | } + | |_________^ + | + = note: `-D clippy::match-ref-pats` implied by `-D warnings` +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | match *v { +LL | Some(v) => println!("{:?}", v), +LL | None => println!("none"), + | + +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:17:5 + | +LL | / match tup { +LL | | &(v, 1) => println!("{}", v), +LL | | _ => println!("none"), +LL | | } + | |_____^ + | +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | match *tup { +LL | (v, 1) => println!("{}", v), + | + +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/match_ref_pats.rs:23:5 + | +LL | / match &w { +LL | | &Some(v) => println!("{:?}", v), +LL | | &None => println!("none"), +LL | | } + | |_____^ + | +help: try + | +LL | match w { +LL | Some(v) => println!("{:?}", v), +LL | None => println!("none"), + | + +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:35:5 + | +LL | / if let &None = a { +LL | | println!("none"); +LL | | } + | |_____^ + | +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | if let None = *a { + | ^^^^ ^^ + +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/match_ref_pats.rs:40:5 + | +LL | / if let &None = &b { +LL | | println!("none"); +LL | | } + | |_____^ + | +help: try + | +LL | if let None = b { + | ^^^^ ^ + +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:67:9 + | +LL | / match foo_variant!(0) { +LL | | &Foo::A => println!("A"), +LL | | _ => println!("Wild"), +LL | | } + | |_________^ + | +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | match *foo_variant!(0) { +LL | Foo::A => println!("A"), + | + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs new file mode 100644 index 0000000000000..0b9342c9c4234 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -0,0 +1,56 @@ +#![warn(clippy::match_same_arms)] + +pub enum Abc { + A, + B, + C, +} + +fn match_same_arms() { + let _ = match Abc::A { + Abc::A => 0, + Abc::B => 1, + _ => 0, //~ ERROR match arms have same body + }; + + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; + + let _ = match 42 { + 42 => 1, + 51 => 1, //~ ERROR match arms have same body + 41 => 2, + 52 => 2, //~ ERROR match arms have same body + _ => 0, + }; + + let _ = match 42 { + 1 => 2, + 2 => 2, //~ ERROR 2nd matched arms have same body + 3 => 2, //~ ERROR 3rd matched arms have same body + 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 { + CommandInfo::BuiltIn { name, .. } => name.to_string(), + CommandInfo::External { name, .. } => name.to_string(), + } + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr new file mode 100644 index 0000000000000..0549886a1e8ec --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -0,0 +1,139 @@ +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:13:14 + | +LL | _ => 0, //~ ERROR match arms have same body + | ^ + | + = note: `-D clippy::match-same-arms` implied by `-D warnings` +note: same as this + --> $DIR/match_same_arms.rs:11:19 + | +LL | Abc::A => 0, + | ^ +note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it + --> $DIR/match_same_arms.rs:11:19 + | +LL | Abc::A => 0, + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:18:20 + | +LL | (.., 3) => 42, //~ ERROR match arms have same body + | ^^ + | +note: same as this + --> $DIR/match_same_arms.rs:17:23 + | +LL | (1, .., 3) => 42, + | ^^ +help: consider refactoring into `(1, .., 3) | (.., 3)` + --> $DIR/match_same_arms.rs:17:9 + | +LL | (1, .., 3) => 42, + | ^^^^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:24:15 + | +LL | 51 => 1, //~ ERROR match arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:23:15 + | +LL | 42 => 1, + | ^ +help: consider refactoring into `42 | 51` + --> $DIR/match_same_arms.rs:23:9 + | +LL | 42 => 1, + | ^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:26:15 + | +LL | 52 => 2, //~ ERROR match arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:25:15 + | +LL | 41 => 2, + | ^ +help: consider refactoring into `41 | 52` + --> $DIR/match_same_arms.rs:25:9 + | +LL | 41 => 2, + | ^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:32:14 + | +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:31:14 + | +LL | 1 => 2, + | ^ +help: consider refactoring into `1 | 2` + --> $DIR/match_same_arms.rs:31:9 + | +LL | 1 => 2, + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:33:14 + | +LL | 3 => 2, //~ ERROR 3rd matched arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:31:14 + | +LL | 1 => 2, + | ^ +help: consider refactoring into `1 | 3` + --> $DIR/match_same_arms.rs:31:9 + | +LL | 1 => 2, + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:33:14 + | +LL | 3 => 2, //~ ERROR 3rd matched arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:32:14 + | +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | ^ +help: consider refactoring into `2 | 3` + --> $DIR/match_same_arms.rs:32:9 + | +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:50:55 + | +LL | CommandInfo::External { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/match_same_arms.rs:49:54 + | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^ +help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }` + --> $DIR/match_same_arms.rs:49:17 + | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs new file mode 100644 index 0000000000000..e1401d2796a52 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -0,0 +1,124 @@ +#![warn(clippy::match_same_arms)] +#![allow(clippy::blacklisted_name)] + +fn bar(_: T) {} +fn foo() -> bool { + unimplemented!() +} + +fn match_same_arms() { + let _ = match 42 { + 42 => { + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + _ => { + //~ ERROR match arms have same body + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + }; + + let _ = match 42 { + 42 => foo(), + 51 => foo(), //~ ERROR match arms have same body + _ => true, + }; + + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; + + let _ = match Some(42) { + Some(foo) => 24, + None => 24, + }; + + let _ = match Some(42) { + Some(42) => 24, + Some(a) => 24, // bindings are different + None => 0, + }; + + let _ = match Some(42) { + Some(a) if a > 0 => 24, + Some(a) => 24, // one arm has a guard + None => 0, + }; + + match (Some(42), Some(42)) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), //~ ERROR match arms have same body + _ => (), + } + + match (Some(42), Some(42)) { + (Some(a), ..) => bar(a), + (.., Some(a)) => bar(a), //~ ERROR match arms have same body + _ => (), + } + + let _ = match Some(()) { + Some(()) => 0.0, + None => -0.0, + }; + + match (Some(42), Some("")) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), // bindings have different types + _ => (), + } + + let x: Result = Ok(3); + + // No warning because of the guard. + match x { + Ok(x) if x * x == 64 => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // This used to be a false positive; see issue #1996. + match x { + Ok(3) => println!("ok"), + Ok(x) if x * x == 64 => println!("ok 64"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + match (x, Some(1i32)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // No warning; different types for `x`. + match (x, Some(1.0f64)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // False negative #2251. + match x { + Ok(_tmp) => println!("ok"), + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => { + unreachable!(); + }, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr new file mode 100644 index 0000000000000..26c65f32ad780 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -0,0 +1,145 @@ +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:20:14 + | +LL | _ => { + | ______________^ +LL | | //~ ERROR match arms have same body +LL | | foo(); +LL | | let mut a = 42 + [23].len() as i32; +... | +LL | | a +LL | | }, + | |_________^ + | + = note: `-D clippy::match-same-arms` implied by `-D warnings` +note: same as this + --> $DIR/match_same_arms2.rs:11:15 + | +LL | 42 => { + | _______________^ +LL | | foo(); +LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { +... | +LL | | a +LL | | }, + | |_________^ +note: `42` has the same arm body as the `_` wildcard, consider removing it + --> $DIR/match_same_arms2.rs:11:15 + | +LL | 42 => { + | _______________^ +LL | | foo(); +LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { +... | +LL | | a +LL | | }, + | |_________^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:34:15 + | +LL | 51 => foo(), //~ ERROR match arms have same body + | ^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:33:15 + | +LL | 42 => foo(), + | ^^^^^ +help: consider refactoring into `42 | 51` + --> $DIR/match_same_arms2.rs:33:9 + | +LL | 42 => foo(), + | ^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:40:17 + | +LL | None => 24, //~ ERROR match arms have same body + | ^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:39:20 + | +LL | Some(_) => 24, + | ^^ +help: consider refactoring into `Some(_) | None` + --> $DIR/match_same_arms2.rs:39:9 + | +LL | Some(_) => 24, + | ^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:62:28 + | +LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body + | ^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:61:28 + | +LL | (Some(a), None) => bar(a), + | ^^^^^^ +help: consider refactoring into `(Some(a), None) | (None, Some(a))` + --> $DIR/match_same_arms2.rs:61:9 + | +LL | (Some(a), None) => bar(a), + | ^^^^^^^^^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:68:26 + | +LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body + | ^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:67:26 + | +LL | (Some(a), ..) => bar(a), + | ^^^^^^ +help: consider refactoring into `(Some(a), ..) | (.., Some(a))` + --> $DIR/match_same_arms2.rs:67:9 + | +LL | (Some(a), ..) => bar(a), + | ^^^^^^^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:102:29 + | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:101:29 + | +LL | (Ok(x), Some(_)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^ +help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))` + --> $DIR/match_same_arms2.rs:101:9 + | +LL | (Ok(x), Some(_)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:117:18 + | +LL | Ok(_) => println!("ok"), + | ^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:116:18 + | +LL | Ok(3) => println!("ok"), + | ^^^^^^^^^^^^^^ +help: consider refactoring into `Ok(3) | Ok(_)` + --> $DIR/match_same_arms2.rs:116:9 + | +LL | Ok(3) => println!("ok"), + | ^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed new file mode 100644 index 0000000000000..f3627902eec98 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding.fixed @@ -0,0 +1,90 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)] + +struct Point { + x: i32, + y: i32, +} + +fn coords() -> Point { + Point { x: 1, y: 2 } +} + +macro_rules! foo { + ($param:expr) => { + match $param { + _ => println!("whatever"), + } + }; +} + +fn main() { + let a = 1; + let b = 2; + let c = 3; + // Lint + let (x, y, z) = (a, b, c); + { + println!("{} {} {}", x, y, z); + } + // Lint + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + // Ok + foo!(a); + // Ok + match a { + 2 => println!("2"), + _ => println!("Not 2"), + } + // Ok + let d = Some(5); + match d { + Some(d) => println!("{}", d), + _ => println!("None"), + } + // Lint + println!("whatever"); + // Lint + { + let x = 29; + println!("x has a value of {}", x); + } + // Lint + { + let e = 5 * a; + if e >= 5 { + println!("e is superior to 5"); + } + } + // Lint + let p = Point { x: 0, y: 7 }; + let Point { x, y } = p; + println!("Coords: ({}, {})", x, y); + // Lint + let Point { x: x1, y: y1 } = p; + println!("Coords: ({}, {})", x1, y1); + // Lint + let x = 5; + let ref r = x; + println!("Got a reference to {}", r); + // Lint + let mut x = 5; + let ref mut mr = x; + println!("Got a mutable reference to {}", mr); + // Lint + let Point { x, y } = coords(); + let product = x * y; + // Lint + let v = vec![Some(1), Some(2), Some(3), Some(4)]; + #[allow(clippy::let_and_return)] + let _ = v + .iter() + .map(|i| { + let unwrapped = i.unwrap(); + unwrapped + }) + .collect::>(); +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs new file mode 100644 index 0000000000000..8c182148ae184 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding.rs @@ -0,0 +1,102 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)] + +struct Point { + x: i32, + y: i32, +} + +fn coords() -> Point { + Point { x: 1, y: 2 } +} + +macro_rules! foo { + ($param:expr) => { + match $param { + _ => println!("whatever"), + } + }; +} + +fn main() { + let a = 1; + let b = 2; + let c = 3; + // Lint + match (a, b, c) { + (x, y, z) => { + println!("{} {} {}", x, y, z); + }, + } + // Lint + match (a, b, c) { + (x, y, z) => println!("{} {} {}", x, y, z), + } + // Ok + foo!(a); + // Ok + match a { + 2 => println!("2"), + _ => println!("Not 2"), + } + // Ok + let d = Some(5); + match d { + Some(d) => println!("{}", d), + _ => println!("None"), + } + // Lint + match a { + _ => println!("whatever"), + } + // Lint + match a { + _ => { + let x = 29; + println!("x has a value of {}", x); + }, + } + // Lint + match a { + _ => { + let e = 5 * a; + if e >= 5 { + println!("e is superior to 5"); + } + }, + } + // Lint + let p = Point { x: 0, y: 7 }; + match p { + Point { x, y } => println!("Coords: ({}, {})", x, y), + } + // Lint + match p { + Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), + } + // Lint + let x = 5; + match x { + ref r => println!("Got a reference to {}", r), + } + // Lint + let mut x = 5; + match x { + ref mut mr => println!("Got a mutable reference to {}", mr), + } + // Lint + let product = match coords() { + Point { x, y } => x * y, + }; + // Lint + let v = vec![Some(1), Some(2), Some(3), Some(4)]; + #[allow(clippy::let_and_return)] + let _ = v + .iter() + .map(|i| match i.unwrap() { + unwrapped => unwrapped, + }) + .collect::>(); +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr new file mode 100644 index 0000000000000..795c8c3e24d7e --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding.stderr @@ -0,0 +1,171 @@ +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:28:5 + | +LL | / match (a, b, c) { +LL | | (x, y, z) => { +LL | | println!("{} {} {}", x, y, z); +LL | | }, +LL | | } + | |_____^ + | + = note: `-D clippy::match-single-binding` implied by `-D warnings` +help: consider using `let` statement + | +LL | let (x, y, z) = (a, b, c); +LL | { +LL | println!("{} {} {}", x, y, z); +LL | } + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:34:5 + | +LL | / match (a, b, c) { +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let (x, y, z) = (a, b, c); +LL | println!("{} {} {}", x, y, z); + | + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:51:5 + | +LL | / match a { +LL | | _ => println!("whatever"), +LL | | } + | |_____^ help: consider using the match body instead: `println!("whatever");` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:55:5 + | +LL | / match a { +LL | | _ => { +LL | | let x = 29; +LL | | println!("x has a value of {}", x); +LL | | }, +LL | | } + | |_____^ + | +help: consider using the match body instead + | +LL | { +LL | let x = 29; +LL | println!("x has a value of {}", x); +LL | } + | + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:62:5 + | +LL | / match a { +LL | | _ => { +LL | | let e = 5 * a; +LL | | if e >= 5 { +... | +LL | | }, +LL | | } + | |_____^ + | +help: consider using the match body instead + | +LL | { +LL | let e = 5 * a; +LL | if e >= 5 { +LL | println!("e is superior to 5"); +LL | } +LL | } + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:72:5 + | +LL | / match p { +LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let Point { x, y } = p; +LL | println!("Coords: ({}, {})", x, y); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:76:5 + | +LL | / match p { +LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let Point { x: x1, y: y1 } = p; +LL | println!("Coords: ({}, {})", x1, y1); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:81:5 + | +LL | / match x { +LL | | ref r => println!("Got a reference to {}", r), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let ref r = x; +LL | println!("Got a reference to {}", r); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:86:5 + | +LL | / match x { +LL | | ref mut mr => println!("Got a mutable reference to {}", mr), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let ref mut mr = x; +LL | println!("Got a mutable reference to {}", mr); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:90:5 + | +LL | / let product = match coords() { +LL | | Point { x, y } => x * y, +LL | | }; + | |______^ + | +help: consider using `let` statement + | +LL | let Point { x, y } = coords(); +LL | let product = x * y; + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:98:18 + | +LL | .map(|i| match i.unwrap() { + | __________________^ +LL | | unwrapped => unwrapped, +LL | | }) + | |_________^ + | +help: consider using `let` statement + | +LL | .map(|i| { +LL | let unwrapped = i.unwrap(); +LL | unwrapped +LL | }) + | + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.rs b/src/tools/clippy/tests/ui/match_wild_err_arm.rs new file mode 100644 index 0000000000000..823be65efe065 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_wild_err_arm.rs @@ -0,0 +1,65 @@ +#![feature(exclusive_range_pattern)] +#![allow(clippy::match_same_arms)] +#![warn(clippy::match_wild_err_arm)] + +fn match_wild_err_arm() { + let x: Result = Ok(3); + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => panic!("err"), + } + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => panic!(), + } + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => { + panic!(); + }, + } + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_e) => panic!(), + } + + // Allowed when used in `panic!`. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_e) => panic!("{}", _e), + } + + // Allowed when not with `panic!` block. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // Allowed when used with `unreachable!`. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => unreachable!(), + } + + // Allowed when used with `unreachable!`. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => { + unreachable!(); + }, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.stderr b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr new file mode 100644 index 0000000000000..20d4c933418b7 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr @@ -0,0 +1,35 @@ +error: `Err(_)` matches all errors + --> $DIR/match_wild_err_arm.rs:11:9 + | +LL | Err(_) => panic!("err"), + | ^^^^^^ + | + = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` + = note: match each error separately or use the error output + +error: `Err(_)` matches all errors + --> $DIR/match_wild_err_arm.rs:17:9 + | +LL | Err(_) => panic!(), + | ^^^^^^ + | + = note: match each error separately or use the error output + +error: `Err(_)` matches all errors + --> $DIR/match_wild_err_arm.rs:23:9 + | +LL | Err(_) => { + | ^^^^^^ + | + = note: match each error separately or use the error output + +error: `Err(_e)` matches all errors + --> $DIR/match_wild_err_arm.rs:31:9 + | +LL | Err(_e) => panic!(), + | ^^^^^^^ + | + = note: match each error separately or use the error output + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_discriminant.fixed b/src/tools/clippy/tests/ui/mem_discriminant.fixed new file mode 100644 index 0000000000000..69a8f286d050d --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![deny(clippy::mem_discriminant_non_enum)] + +use std::mem; + +enum Foo { + One(usize), + Two(u8), +} + +fn main() { + // bad + mem::discriminant(&Some(2)); + mem::discriminant(&None::); + mem::discriminant(&Foo::One(5)); + mem::discriminant(&Foo::Two(5)); + + let ro = &Some(3); + let rro = &ro; + mem::discriminant(ro); + mem::discriminant(*rro); + mem::discriminant(*rro); + + macro_rules! mem_discriminant_but_in_a_macro { + ($param:expr) => { + mem::discriminant($param) + }; + } + + mem_discriminant_but_in_a_macro!(*rro); + + let rrrrro = &&&rro; + mem::discriminant(****rrrrro); + mem::discriminant(****rrrrro); + + // ok + mem::discriminant(&Some(2)); + mem::discriminant(&None::); + mem::discriminant(&Foo::One(5)); + mem::discriminant(&Foo::Two(5)); + mem::discriminant(ro); + mem::discriminant(*rro); + mem::discriminant(****rrrrro); +} diff --git a/src/tools/clippy/tests/ui/mem_discriminant.rs b/src/tools/clippy/tests/ui/mem_discriminant.rs new file mode 100644 index 0000000000000..55db50fcdc733 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#![deny(clippy::mem_discriminant_non_enum)] + +use std::mem; + +enum Foo { + One(usize), + Two(u8), +} + +fn main() { + // bad + mem::discriminant(&&Some(2)); + mem::discriminant(&&None::); + mem::discriminant(&&Foo::One(5)); + mem::discriminant(&&Foo::Two(5)); + + let ro = &Some(3); + let rro = &ro; + mem::discriminant(&ro); + mem::discriminant(rro); + mem::discriminant(&rro); + + macro_rules! mem_discriminant_but_in_a_macro { + ($param:expr) => { + mem::discriminant($param) + }; + } + + mem_discriminant_but_in_a_macro!(&rro); + + let rrrrro = &&&rro; + mem::discriminant(&rrrrro); + mem::discriminant(*rrrrro); + + // ok + mem::discriminant(&Some(2)); + mem::discriminant(&None::); + mem::discriminant(&Foo::One(5)); + mem::discriminant(&Foo::Two(5)); + mem::discriminant(ro); + mem::discriminant(*rro); + mem::discriminant(****rrrrro); +} diff --git a/src/tools/clippy/tests/ui/mem_discriminant.stderr b/src/tools/clippy/tests/ui/mem_discriminant.stderr new file mode 100644 index 0000000000000..8d9810970adee --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant.stderr @@ -0,0 +1,94 @@ +error: calling `mem::discriminant` on non-enum type `&std::option::Option` + --> $DIR/mem_discriminant.rs:14:5 + | +LL | mem::discriminant(&&Some(2)); + | ^^^^^^^^^^^^^^^^^^---------^ + | | + | help: try dereferencing: `&Some(2)` + | +note: the lint level is defined here + --> $DIR/mem_discriminant.rs:3:9 + | +LL | #![deny(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `mem::discriminant` on non-enum type `&std::option::Option` + --> $DIR/mem_discriminant.rs:15:5 + | +LL | mem::discriminant(&&None::); + | ^^^^^^^^^^^^^^^^^^------------^ + | | + | help: try dereferencing: `&None::` + +error: calling `mem::discriminant` on non-enum type `&Foo` + --> $DIR/mem_discriminant.rs:16:5 + | +LL | mem::discriminant(&&Foo::One(5)); + | ^^^^^^^^^^^^^^^^^^-------------^ + | | + | help: try dereferencing: `&Foo::One(5)` + +error: calling `mem::discriminant` on non-enum type `&Foo` + --> $DIR/mem_discriminant.rs:17:5 + | +LL | mem::discriminant(&&Foo::Two(5)); + | ^^^^^^^^^^^^^^^^^^-------------^ + | | + | help: try dereferencing: `&Foo::Two(5)` + +error: calling `mem::discriminant` on non-enum type `&std::option::Option` + --> $DIR/mem_discriminant.rs:21:5 + | +LL | mem::discriminant(&ro); + | ^^^^^^^^^^^^^^^^^^---^ + | | + | help: try dereferencing: `ro` + +error: calling `mem::discriminant` on non-enum type `&std::option::Option` + --> $DIR/mem_discriminant.rs:22:5 + | +LL | mem::discriminant(rro); + | ^^^^^^^^^^^^^^^^^^---^ + | | + | help: try dereferencing: `*rro` + +error: calling `mem::discriminant` on non-enum type `&&std::option::Option` + --> $DIR/mem_discriminant.rs:23:5 + | +LL | mem::discriminant(&rro); + | ^^^^^^^^^^^^^^^^^^----^ + | | + | help: try dereferencing: `*rro` + +error: calling `mem::discriminant` on non-enum type `&&std::option::Option` + --> $DIR/mem_discriminant.rs:27:13 + | +LL | mem::discriminant($param) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | mem_discriminant_but_in_a_macro!(&rro); + | --------------------------------------- + | | | + | | help: try dereferencing: `*rro` + | in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: calling `mem::discriminant` on non-enum type `&&&&&std::option::Option` + --> $DIR/mem_discriminant.rs:34:5 + | +LL | mem::discriminant(&rrrrro); + | ^^^^^^^^^^^^^^^^^^-------^ + | | + | help: try dereferencing: `****rrrrro` + +error: calling `mem::discriminant` on non-enum type `&&&std::option::Option` + --> $DIR/mem_discriminant.rs:35:5 + | +LL | mem::discriminant(*rrrrro); + | ^^^^^^^^^^^^^^^^^^-------^ + | | + | help: try dereferencing: `****rrrrro` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_discriminant_unfixable.rs b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.rs new file mode 100644 index 0000000000000..e245d3257d55d --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.rs @@ -0,0 +1,16 @@ +#![deny(clippy::mem_discriminant_non_enum)] + +use std::mem; + +enum Foo { + One(usize), + Two(u8), +} + +struct A(Foo); + +fn main() { + // bad + mem::discriminant(&"hello"); + mem::discriminant(&A(Foo::One(0))); +} diff --git a/src/tools/clippy/tests/ui/mem_discriminant_unfixable.stderr b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.stderr new file mode 100644 index 0000000000000..e2de3776f2c91 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.stderr @@ -0,0 +1,20 @@ +error: calling `mem::discriminant` on non-enum type `&str` + --> $DIR/mem_discriminant_unfixable.rs:14:5 + | +LL | mem::discriminant(&"hello"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/mem_discriminant_unfixable.rs:1:9 + | +LL | #![deny(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `mem::discriminant` on non-enum type `A` + --> $DIR/mem_discriminant_unfixable.rs:15:5 + | +LL | mem::discriminant(&A(Foo::One(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_forget.rs b/src/tools/clippy/tests/ui/mem_forget.rs new file mode 100644 index 0000000000000..e5b35c098a239 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_forget.rs @@ -0,0 +1,23 @@ +use std::rc::Rc; +use std::sync::Arc; + +use std::mem as memstuff; +use std::mem::forget as forgetSomething; + +#[warn(clippy::mem_forget)] +#[allow(clippy::forget_copy)] +fn main() { + let five: i32 = 5; + forgetSomething(five); + + let six: Arc = Arc::new(6); + memstuff::forget(six); + + let seven: Rc = Rc::new(7); + std::mem::forget(seven); + + let eight: Vec = vec![8]; + forgetSomething(eight); + + std::mem::forget(7); +} diff --git a/src/tools/clippy/tests/ui/mem_forget.stderr b/src/tools/clippy/tests/ui/mem_forget.stderr new file mode 100644 index 0000000000000..a90d8b1655dc4 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_forget.stderr @@ -0,0 +1,22 @@ +error: usage of `mem::forget` on `Drop` type + --> $DIR/mem_forget.rs:14:5 + | +LL | memstuff::forget(six); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mem-forget` implied by `-D warnings` + +error: usage of `mem::forget` on `Drop` type + --> $DIR/mem_forget.rs:17:5 + | +LL | std::mem::forget(seven); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: usage of `mem::forget` on `Drop` type + --> $DIR/mem_forget.rs:20:5 + | +LL | forgetSomething(eight); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed new file mode 100644 index 0000000000000..54e962e7116e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace.fixed @@ -0,0 +1,30 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] + +use std::mem; + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = an_option.take(); + let an_option = &mut Some(1); + let _ = an_option.take(); +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); + let s = &mut String::from("foo"); + let _ = std::mem::take(s); + let _ = std::mem::take(s); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); +} diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs new file mode 100644 index 0000000000000..60f527810716f --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace.rs @@ -0,0 +1,30 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] + +use std::mem; + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = mem::replace(&mut an_option, None); + let an_option = &mut Some(1); + let _ = mem::replace(an_option, None); +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); + let s = &mut String::from("foo"); + let _ = std::mem::replace(s, String::default()); + let _ = std::mem::replace(s, Default::default()); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); +} diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr new file mode 100644 index 0000000000000..245d33aa4f260 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace.stderr @@ -0,0 +1,36 @@ +error: replacing an `Option` with `None` + --> $DIR/mem_replace.rs:14:13 + | +LL | let _ = mem::replace(&mut an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + | + = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace.rs:16:13 + | +LL | let _ = mem::replace(an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:21:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:23:13 + | +LL | let _ = std::mem::replace(s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:24:13 + | +LL | let _ = std::mem::replace(s, Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.rs b/src/tools/clippy/tests/ui/mem_replace_macro.rs new file mode 100644 index 0000000000000..0c09344b80d10 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace_macro.rs @@ -0,0 +1,21 @@ +// aux-build:macro_rules.rs +#![warn(clippy::mem_replace_with_default)] + +#[macro_use] +extern crate macro_rules; + +macro_rules! take { + ($s:expr) => { + std::mem::replace($s, Default::default()) + }; +} + +fn replace_with_default() { + let s = &mut String::from("foo"); + take!(s); + take_external!(s); +} + +fn main() { + replace_with_default(); +} diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.stderr b/src/tools/clippy/tests/ui/mem_replace_macro.stderr new file mode 100644 index 0000000000000..4971a91050bf2 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace_macro.stderr @@ -0,0 +1,14 @@ +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace_macro.rs:9:9 + | +LL | std::mem::replace($s, Default::default()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | take!(s); + | --------- in this macro invocation + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs new file mode 100644 index 0000000000000..7880cf36415ff --- /dev/null +++ b/src/tools/clippy/tests/ui/methods.rs @@ -0,0 +1,247 @@ +// aux-build:option_helpers.rs +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::blacklisted_name, + clippy::default_trait_access, + clippy::missing_docs_in_private_items, + clippy::missing_safety_doc, + clippy::non_ascii_literal, + clippy::new_without_default, + clippy::needless_pass_by_value, + clippy::print_stdout, + clippy::must_use_candidate, + clippy::use_self, + clippy::useless_format, + clippy::wrong_self_convention, + clippy::unused_self, + unused +)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::HashSet; +use std::collections::VecDeque; +use std::iter::FromIterator; +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +use option_helpers::IteratorFalsePositives; + +pub struct T; + +impl T { + pub fn add(self, other: T) -> T { + self + } + + // no error, not public interface + pub(crate) fn drop(&mut self) {} + + // no error, private function + fn neg(self) -> Self { + self + } + + // no error, private function + fn eq(&self, other: T) -> bool { + true + } + + // No error; self is a ref. + fn sub(&self, other: T) -> &T { + self + } + + // No error; different number of arguments. + fn div(self) -> T { + self + } + + // No error; wrong return type. + fn rem(self, other: T) {} + + // Fine + fn into_u32(self) -> u32 { + 0 + } + + fn into_u16(&self) -> u16 { + 0 + } + + fn to_something(self) -> u32 { + 0 + } + + fn new(self) -> Self { + unimplemented!(); + } +} + +pub struct T1; + +impl T1 { + // Shouldn't trigger lint as it is unsafe. + pub unsafe fn add(self, rhs: T1) -> T1 { + self + } + + // Should not trigger lint since this is an async function. + pub async fn next(&mut self) -> Option { + None + } +} + +struct Lt<'a> { + foo: &'a u32, +} + +impl<'a> Lt<'a> { + // The lifetime is different, but that’s irrelevant; see issue #734. + #[allow(clippy::needless_lifetimes)] + pub fn new<'b>(s: &'b str) -> Lt<'b> { + unimplemented!() + } +} + +struct Lt2<'a> { + foo: &'a u32, +} + +impl<'a> Lt2<'a> { + // The lifetime is different, but that’s irrelevant; see issue #734. + pub fn new(s: &str) -> Lt2 { + unimplemented!() + } +} + +struct Lt3<'a> { + foo: &'a u32, +} + +impl<'a> Lt3<'a> { + // The lifetime is different, but that’s irrelevant; see issue #734. + pub fn new() -> Lt3<'static> { + unimplemented!() + } +} + +#[derive(Clone, Copy)] +struct U; + +impl U { + fn new() -> Self { + U + } + // Ok because `U` is `Copy`. + fn to_something(self) -> u32 { + 0 + } +} + +struct V { + _dummy: T, +} + +impl V { + fn new() -> Option> { + None + } +} + +struct AsyncNew; + +impl AsyncNew { + async fn new() -> Option { + None + } +} + +struct BadNew; + +impl BadNew { + fn new() -> i32 { + 0 + } +} + +impl Mul for T { + type Output = T; + // No error, obviously. + fn mul(self, other: T) -> T { + self + } +} + +/// Checks implementation of `FILTER_NEXT` lint. +#[rustfmt::skip] +fn filter_next() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); + + // Multi-line case. + let _ = v.iter().filter(|&x| { + *x < 0 + } + ).next(); + + // Check that hat we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); +} + +/// Checks implementation of `SEARCH_IS_SOME` lint. +#[rustfmt::skip] +fn search_is_some() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_some()`, single-line case. + let _ = v.iter().find(|&x| *x < 0).is_some(); + let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + let _ = (0..1).find(|x| *x == 0).is_some(); + let _ = v.iter().find(|x| **x == 0).is_some(); + + // Check `find().is_some()`, multi-line case. + let _ = v.iter().find(|&x| { + *x < 0 + } + ).is_some(); + + // Check `position().is_some()`, single-line case. + let _ = v.iter().position(|&x| x < 0).is_some(); + + // Check `position().is_some()`, multi-line case. + let _ = v.iter().position(|&x| { + x < 0 + } + ).is_some(); + + // Check `rposition().is_some()`, single-line case. + let _ = v.iter().rposition(|&x| x < 0).is_some(); + + // Check `rposition().is_some()`, multi-line case. + let _ = v.iter().rposition(|&x| { + x < 0 + } + ).is_some(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.find().is_some(); + let _ = foo.position().is_some(); + let _ = foo.rposition().is_some(); +} + +fn main() { + filter_next(); + search_is_some(); +} diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr new file mode 100644 index 0000000000000..01cf487ac148e --- /dev/null +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -0,0 +1,109 @@ +error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name + --> $DIR/methods.rs:39:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | self +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + +error: methods called `new` usually return `Self` + --> $DIR/methods.rs:169:5 + | +LL | / fn new() -> i32 { +LL | | 0 +LL | | } + | |_____^ + | + = note: `-D clippy::new-ret-no-self` implied by `-D warnings` + +error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. + --> $DIR/methods.rs:188:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` + = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` + +error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. + --> $DIR/methods.rs:191:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:208:22 + | +LL | let _ = v.iter().find(|&x| *x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:209:20 + | +LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:210:20 + | +LL | let _ = (0..1).find(|x| *x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:211:22 + | +LL | let _ = v.iter().find(|x| **x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:214:13 + | +LL | let _ = v.iter().find(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:220:22 + | +LL | let _ = v.iter().position(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:223:13 + | +LL | let _ = v.iter().position(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:229:22 + | +LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:232:13 + | +LL | let _ = v.iter().rposition(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs new file mode 100644 index 0000000000000..8307d4b3019f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -0,0 +1,33 @@ +#![warn(clippy::all)] + +use std::cmp::max as my_max; +use std::cmp::min as my_min; +use std::cmp::{max, min}; + +const LARGE: usize = 3; + +fn main() { + let x; + x = 2usize; + min(1, max(3, x)); + min(max(3, x), 1); + max(min(x, 1), 3); + max(3, min(x, 1)); + + my_max(3, my_min(x, 1)); + + min(3, max(1, x)); // ok, could be 1, 2 or 3 depending on x + + min(1, max(LARGE, x)); // no error, we don't lookup consts here + + let y = 2isize; + min(max(y, -1), 3); + + let s; + s = "Hello"; + + min("Apple", max("Zoo", s)); + max(min(s, "Apple"), "Zoo"); + + max("Apple", min(s, "Zoo")); // ok +} diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr new file mode 100644 index 0000000000000..b552c137f7c7c --- /dev/null +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -0,0 +1,46 @@ +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:12:5 + | +LL | min(1, max(3, x)); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::min-max` implied by `-D warnings` + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:13:5 + | +LL | min(max(3, x), 1); + | ^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:14:5 + | +LL | max(min(x, 1), 3); + | ^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:15:5 + | +LL | max(3, min(x, 1)); + | ^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:17:5 + | +LL | my_max(3, my_min(x, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:29:5 + | +LL | min("Apple", max("Zoo", s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:30:5 + | +LL | max(min(s, "Apple"), "Zoo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed new file mode 100644 index 0000000000000..3ee77dcac31a0 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(target_os = "cloudabi")] +fn cloudabi() {} + +#[cfg(target_os = "hermit")] +fn hermit() {} + +#[cfg(target_os = "wasi")] +fn wasi() {} + +#[cfg(target_os = "none")] +fn none() {} + +// list with conditions +#[cfg(all(not(any(windows, target_os = "cloudabi")), target_os = "wasi"))] +fn list() {} + +// windows is a valid target family, should be ignored +#[cfg(windows)] +fn windows() {} + +// correct use, should be ignored +#[cfg(target_os = "hermit")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs new file mode 100644 index 0000000000000..9cc411418e4c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(cloudabi)] +fn cloudabi() {} + +#[cfg(hermit)] +fn hermit() {} + +#[cfg(wasi)] +fn wasi() {} + +#[cfg(none)] +fn none() {} + +// list with conditions +#[cfg(all(not(any(windows, cloudabi)), wasi))] +fn list() {} + +// windows is a valid target family, should be ignored +#[cfg(windows)] +fn windows() {} + +// correct use, should be ignored +#[cfg(target_os = "hermit")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr new file mode 100644 index 0000000000000..78fc27752d239 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr @@ -0,0 +1,51 @@ +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:6:1 + | +LL | #[cfg(cloudabi)] + | ^^^^^^--------^^ + | | + | help: try: `target_os = "cloudabi"` + | + = note: `-D clippy::mismatched-target-os` implied by `-D warnings` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:9:1 + | +LL | #[cfg(hermit)] + | ^^^^^^------^^ + | | + | help: try: `target_os = "hermit"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:12:1 + | +LL | #[cfg(wasi)] + | ^^^^^^----^^ + | | + | help: try: `target_os = "wasi"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:15:1 + | +LL | #[cfg(none)] + | ^^^^^^----^^ + | | + | help: try: `target_os = "none"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:19:1 + | +LL | #[cfg(all(not(any(windows, cloudabi)), wasi))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | #[cfg(all(not(any(windows, target_os = "cloudabi")), wasi))] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try + | +LL | #[cfg(all(not(any(windows, cloudabi)), target_os = "wasi"))] + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed new file mode 100644 index 0000000000000..7d9d406d99dfc --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed @@ -0,0 +1,62 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(target_os = "linux")] +fn linux() {} + +#[cfg(target_os = "freebsd")] +fn freebsd() {} + +#[cfg(target_os = "dragonfly")] +fn dragonfly() {} + +#[cfg(target_os = "openbsd")] +fn openbsd() {} + +#[cfg(target_os = "netbsd")] +fn netbsd() {} + +#[cfg(target_os = "macos")] +fn macos() {} + +#[cfg(target_os = "ios")] +fn ios() {} + +#[cfg(target_os = "android")] +fn android() {} + +#[cfg(target_os = "emscripten")] +fn emscripten() {} + +#[cfg(target_os = "fuchsia")] +fn fuchsia() {} + +#[cfg(target_os = "haiku")] +fn haiku() {} + +#[cfg(target_os = "illumos")] +fn illumos() {} + +#[cfg(target_os = "l4re")] +fn l4re() {} + +#[cfg(target_os = "redox")] +fn redox() {} + +#[cfg(target_os = "solaris")] +fn solaris() {} + +#[cfg(target_os = "vxworks")] +fn vxworks() {} + +// list with conditions +#[cfg(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd"))] +fn list() {} + +// correct use, should be ignored +#[cfg(target_os = "freebsd")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs new file mode 100644 index 0000000000000..c1177f1eedc62 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs @@ -0,0 +1,62 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(linux)] +fn linux() {} + +#[cfg(freebsd)] +fn freebsd() {} + +#[cfg(dragonfly)] +fn dragonfly() {} + +#[cfg(openbsd)] +fn openbsd() {} + +#[cfg(netbsd)] +fn netbsd() {} + +#[cfg(macos)] +fn macos() {} + +#[cfg(ios)] +fn ios() {} + +#[cfg(android)] +fn android() {} + +#[cfg(emscripten)] +fn emscripten() {} + +#[cfg(fuchsia)] +fn fuchsia() {} + +#[cfg(haiku)] +fn haiku() {} + +#[cfg(illumos)] +fn illumos() {} + +#[cfg(l4re)] +fn l4re() {} + +#[cfg(redox)] +fn redox() {} + +#[cfg(solaris)] +fn solaris() {} + +#[cfg(vxworks)] +fn vxworks() {} + +// list with conditions +#[cfg(all(not(any(solaris, linux)), freebsd))] +fn list() {} + +// correct use, should be ignored +#[cfg(target_os = "freebsd")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr new file mode 100644 index 0000000000000..fe9aeedb59c45 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr @@ -0,0 +1,183 @@ +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:6:1 + | +LL | #[cfg(linux)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "linux"` + | + = note: `-D clippy::mismatched-target-os` implied by `-D warnings` + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:9:1 + | +LL | #[cfg(freebsd)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "freebsd"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:12:1 + | +LL | #[cfg(dragonfly)] + | ^^^^^^---------^^ + | | + | help: try: `target_os = "dragonfly"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:15:1 + | +LL | #[cfg(openbsd)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "openbsd"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:18:1 + | +LL | #[cfg(netbsd)] + | ^^^^^^------^^ + | | + | help: try: `target_os = "netbsd"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:21:1 + | +LL | #[cfg(macos)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "macos"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:24:1 + | +LL | #[cfg(ios)] + | ^^^^^^---^^ + | | + | help: try: `target_os = "ios"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:27:1 + | +LL | #[cfg(android)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "android"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:30:1 + | +LL | #[cfg(emscripten)] + | ^^^^^^----------^^ + | | + | help: try: `target_os = "emscripten"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:33:1 + | +LL | #[cfg(fuchsia)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "fuchsia"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:36:1 + | +LL | #[cfg(haiku)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "haiku"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:39:1 + | +LL | #[cfg(illumos)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "illumos"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:42:1 + | +LL | #[cfg(l4re)] + | ^^^^^^----^^ + | | + | help: try: `target_os = "l4re"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:45:1 + | +LL | #[cfg(redox)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "redox"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:48:1 + | +LL | #[cfg(solaris)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "solaris"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:51:1 + | +LL | #[cfg(vxworks)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "vxworks"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:55:1 + | +LL | #[cfg(all(not(any(solaris, linux)), freebsd))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Did you mean `unix`? +help: try + | +LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))] + | ^^^^^^^^^^^^^^^^^^^^^ +help: try + | +LL | #[cfg(all(not(any(solaris, target_os = "linux")), freebsd))] + | ^^^^^^^^^^^^^^^^^^^ +help: try + | +LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs b/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs new file mode 100644 index 0000000000000..51fd57df8df1d --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs @@ -0,0 +1,3 @@ +#![warn(clippy::missing_docs_in_private_items)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr new file mode 100644 index 0000000000000..da46f9886366c --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr @@ -0,0 +1,12 @@ +error: missing documentation for crate + --> $DIR/missing-doc-crate-missing.rs:1:1 + | +LL | / #![warn(clippy::missing_docs_in_private_items)] +LL | | +LL | | fn main() {} + | |____________^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/missing-doc-crate.rs b/src/tools/clippy/tests/ui/missing-doc-crate.rs new file mode 100644 index 0000000000000..04711f864886b --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-crate.rs @@ -0,0 +1,5 @@ +#![warn(clippy::missing_docs_in_private_items)] +#![feature(external_doc)] +#![doc(include = "../../README.md")] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing-doc-crate.stderr b/src/tools/clippy/tests/ui/missing-doc-crate.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.rs b/src/tools/clippy/tests/ui/missing-doc-impl.rs new file mode 100644 index 0000000000000..57af84dcdf4d0 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-impl.rs @@ -0,0 +1,87 @@ +#![warn(clippy::missing_docs_in_private_items)] +#![allow(dead_code)] +#![feature(associated_type_defaults)] + +//! Some garbage docs for the crate here +#![doc = "More garbage"] + +struct Foo { + a: isize, + b: isize, +} + +pub struct PubFoo { + pub a: isize, + b: isize, +} + +#[allow(clippy::missing_docs_in_private_items)] +pub struct PubFoo2 { + pub a: isize, + pub c: isize, +} + +/// dox +pub trait A { + /// dox + fn foo(&self); + /// dox + fn foo_with_impl(&self) {} +} + +#[allow(clippy::missing_docs_in_private_items)] +trait B { + fn foo(&self); + fn foo_with_impl(&self) {} +} + +pub trait C { + fn foo(&self); + fn foo_with_impl(&self) {} +} + +#[allow(clippy::missing_docs_in_private_items)] +pub trait D { + fn dummy(&self) {} +} + +/// dox +pub trait E: Sized { + type AssociatedType; + type AssociatedTypeDef = Self; + + /// dox + type DocumentedType; + /// dox + type DocumentedTypeDef = Self; + /// dox + fn dummy(&self) {} +} + +impl Foo { + pub fn foo() {} + fn bar() {} +} + +impl PubFoo { + pub fn foo() {} + /// dox + pub fn foo1() {} + fn foo2() {} + #[allow(clippy::missing_docs_in_private_items)] + pub fn foo3() {} +} + +#[allow(clippy::missing_docs_in_private_items)] +trait F { + fn a(); + fn b(&self); +} + +// should need to redefine documentation for implementations of traits +impl F for Foo { + fn a() {} + fn b(&self) {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.stderr b/src/tools/clippy/tests/ui/missing-doc-impl.stderr new file mode 100644 index 0000000000000..9656a39abceb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-impl.stderr @@ -0,0 +1,103 @@ +error: missing documentation for a struct + --> $DIR/missing-doc-impl.rs:8:1 + | +LL | / struct Foo { +LL | | a: isize, +LL | | b: isize, +LL | | } + | |_^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:9:5 + | +LL | a: isize, + | ^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:10:5 + | +LL | b: isize, + | ^^^^^^^^ + +error: missing documentation for a struct + --> $DIR/missing-doc-impl.rs:13:1 + | +LL | / pub struct PubFoo { +LL | | pub a: isize, +LL | | b: isize, +LL | | } + | |_^ + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:14:5 + | +LL | pub a: isize, + | ^^^^^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:15:5 + | +LL | b: isize, + | ^^^^^^^^ + +error: missing documentation for a trait + --> $DIR/missing-doc-impl.rs:38:1 + | +LL | / pub trait C { +LL | | fn foo(&self); +LL | | fn foo_with_impl(&self) {} +LL | | } + | |_^ + +error: missing documentation for a trait method + --> $DIR/missing-doc-impl.rs:39:5 + | +LL | fn foo(&self); + | ^^^^^^^^^^^^^^ + +error: missing documentation for a trait method + --> $DIR/missing-doc-impl.rs:40:5 + | +LL | fn foo_with_impl(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for an associated type + --> $DIR/missing-doc-impl.rs:50:5 + | +LL | type AssociatedType; + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for an associated type + --> $DIR/missing-doc-impl.rs:51:5 + | +LL | type AssociatedTypeDef = Self; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:62:5 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:63:5 + | +LL | fn bar() {} + | ^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:67:5 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:70:5 + | +LL | fn foo2() {} + | ^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/missing-doc.rs b/src/tools/clippy/tests/ui/missing-doc.rs new file mode 100644 index 0000000000000..a9bf7140a1e59 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc.rs @@ -0,0 +1,102 @@ +#![warn(clippy::missing_docs_in_private_items)] +// When denying at the crate level, be sure to not get random warnings from the +// injected intrinsics by the compiler. +#![allow(dead_code)] +#![feature(global_asm)] + +//! Some garbage docs for the crate here +#![doc = "More garbage"] + +type Typedef = String; +pub type PubTypedef = String; + +mod module_no_dox {} +pub mod pub_module_no_dox {} + +/// dox +pub fn foo() {} +pub fn foo2() {} +fn foo3() {} +#[allow(clippy::missing_docs_in_private_items)] +pub fn foo4() {} + +// It sure is nice if doc(hidden) implies allow(missing_docs), and that it +// applies recursively +#[doc(hidden)] +mod a { + pub fn baz() {} + pub mod b { + pub fn baz() {} + } +} + +enum Baz { + BazA { a: isize, b: isize }, + BarB, +} + +pub enum PubBaz { + PubBazA { a: isize }, +} + +/// dox +pub enum PubBaz2 { + /// dox + PubBaz2A { + /// dox + a: isize, + }, +} + +#[allow(clippy::missing_docs_in_private_items)] +pub enum PubBaz3 { + PubBaz3A { b: isize }, +} + +#[doc(hidden)] +pub fn baz() {} + +const FOO: u32 = 0; +/// dox +pub const FOO1: u32 = 0; +#[allow(clippy::missing_docs_in_private_items)] +pub const FOO2: u32 = 0; +#[doc(hidden)] +pub const FOO3: u32 = 0; +pub const FOO4: u32 = 0; + +static BAR: u32 = 0; +/// dox +pub static BAR1: u32 = 0; +#[allow(clippy::missing_docs_in_private_items)] +pub static BAR2: u32 = 0; +#[doc(hidden)] +pub static BAR3: u32 = 0; +pub static BAR4: u32 = 0; + +mod internal_impl { + /// dox + pub fn documented() {} + pub fn undocumented1() {} + pub fn undocumented2() {} + fn undocumented3() {} + /// dox + pub mod globbed { + /// dox + pub fn also_documented() {} + pub fn also_undocumented1() {} + fn also_undocumented2() {} + } +} +/// dox +pub mod public_interface { + pub use internal_impl::documented as foo; + pub use internal_impl::globbed::*; + pub use internal_impl::undocumented1 as bar; + pub use internal_impl::{documented, undocumented2}; +} + +fn main() {} + +// Ensure global asm doesn't require documentation. +global_asm! { "" } diff --git a/src/tools/clippy/tests/ui/missing-doc.stderr b/src/tools/clippy/tests/ui/missing-doc.stderr new file mode 100644 index 0000000000000..a876dc078ebff --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc.stderr @@ -0,0 +1,159 @@ +error: missing documentation for a type alias + --> $DIR/missing-doc.rs:10:1 + | +LL | type Typedef = String; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + +error: missing documentation for a type alias + --> $DIR/missing-doc.rs:11:1 + | +LL | pub type PubTypedef = String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a module + --> $DIR/missing-doc.rs:13:1 + | +LL | mod module_no_dox {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a module + --> $DIR/missing-doc.rs:14:1 + | +LL | pub mod pub_module_no_dox {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:18:1 + | +LL | pub fn foo2() {} + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:19:1 + | +LL | fn foo3() {} + | ^^^^^^^^^^^^ + +error: missing documentation for an enum + --> $DIR/missing-doc.rs:33:1 + | +LL | / enum Baz { +LL | | BazA { a: isize, b: isize }, +LL | | BarB, +LL | | } + | |_^ + +error: missing documentation for a variant + --> $DIR/missing-doc.rs:34:5 + | +LL | BazA { a: isize, b: isize }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc.rs:34:12 + | +LL | BazA { a: isize, b: isize }, + | ^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc.rs:34:22 + | +LL | BazA { a: isize, b: isize }, + | ^^^^^^^^ + +error: missing documentation for a variant + --> $DIR/missing-doc.rs:35:5 + | +LL | BarB, + | ^^^^ + +error: missing documentation for an enum + --> $DIR/missing-doc.rs:38:1 + | +LL | / pub enum PubBaz { +LL | | PubBazA { a: isize }, +LL | | } + | |_^ + +error: missing documentation for a variant + --> $DIR/missing-doc.rs:39:5 + | +LL | PubBazA { a: isize }, + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc.rs:39:15 + | +LL | PubBazA { a: isize }, + | ^^^^^^^^ + +error: missing documentation for a constant + --> $DIR/missing-doc.rs:59:1 + | +LL | const FOO: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a constant + --> $DIR/missing-doc.rs:66:1 + | +LL | pub const FOO4: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a static + --> $DIR/missing-doc.rs:68:1 + | +LL | static BAR: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a static + --> $DIR/missing-doc.rs:75:1 + | +LL | pub static BAR4: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a module + --> $DIR/missing-doc.rs:77:1 + | +LL | / mod internal_impl { +LL | | /// dox +LL | | pub fn documented() {} +LL | | pub fn undocumented1() {} +... | +LL | | } +LL | | } + | |_^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:80:5 + | +LL | pub fn undocumented1() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:81:5 + | +LL | pub fn undocumented2() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:82:5 + | +LL | fn undocumented3() {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:87:9 + | +LL | pub fn also_undocumented1() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:88:9 + | +LL | fn also_undocumented2() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 24 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 new file mode 100644 index 0000000000000..ba352ef9ee932 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -0,0 +1,103 @@ +//! False-positive tests to ensure we don't suggest `const` for things where it would cause a +//! compilation error. +//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. + +#![warn(clippy::missing_const_for_fn)] +#![allow(incomplete_features)] +#![feature(start, const_generics)] + +struct Game; + +// This should not be linted because it's already const +const fn already_const() -> i32 { + 32 +} + +impl Game { + // This should not be linted because it's already const + pub const fn already_const() -> i32 { + 32 + } +} + +// Allowing on this function, because it would lint, which we don't want in this case. +#[allow(clippy::missing_const_for_fn)] +fn random() -> u32 { + 42 +} + +// We should not suggest to make this function `const` because `random()` is non-const +fn random_caller() -> u32 { + random() +} + +static Y: u32 = 0; + +// We should not suggest to make this function `const` because const functions are not allowed to +// refer to a static variable +fn get_y() -> u32 { + Y + //~^ ERROR E0013 +} + +// Don't lint entrypoint functions +#[start] +fn init(num: isize, something: *const *const u8) -> isize { + 1 +} + +trait Foo { + // This should not be suggested to be made const + // (rustc doesn't allow const trait methods) + fn f() -> u32; + + // This should not be suggested to be made const either + fn g() -> u32 { + 33 + } +} + +// Don't lint in external macros (derive) +#[derive(PartialEq, Eq)] +struct Point(isize, isize); + +impl std::ops::Add for Point { + type Output = Self; + + // Don't lint in trait impls of derived methods + fn add(self, other: Self) -> Self { + Point(self.0 + other.0, self.1 + other.1) + } +} + +mod with_drop { + pub struct A; + pub struct B; + impl Drop for A { + fn drop(&mut self) {} + } + + impl A { + // This can not be const because the type implements `Drop`. + pub fn a(self) -> B { + B + } + } + + impl B { + // This can not be const because `a` implements `Drop`. + pub fn a(self, a: A) -> B { + B + } + } +} + +fn const_generic_params(t: &[T; N]) -> &[T; N] { + t +} + +fn const_generic_return(t: &[T]) -> &[T; N] { + let p = t.as_ptr() as *const [T; N]; + + unsafe { &*p } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d 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 new file mode 100644 index 0000000000000..c6f44b7daa342 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -0,0 +1,74 @@ +#![warn(clippy::missing_const_for_fn)] +#![allow(incomplete_features, clippy::let_and_return)] +#![feature(const_generics)] + +use std::mem::transmute; + +struct Game { + guess: i32, +} + +impl Game { + // Could be const + pub fn new() -> Self { + Self { guess: 42 } + } + + fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { + b + } +} + +// Could be const +fn one() -> i32 { + 1 +} + +// Could also be const +fn two() -> i32 { + let abc = 2; + abc +} + +// Could be const (since Rust 1.39) +fn string() -> String { + String::new() +} + +// Could be const +unsafe fn four() -> i32 { + 4 +} + +// Could also be const +fn generic(t: T) -> T { + t +} + +fn sub(x: u32) -> usize { + unsafe { transmute(&x) } +} + +// NOTE: This is currently not yet allowed to be const +// Once implemented, Clippy should be able to suggest this as const, too. +fn generic_arr(t: [T; 1]) -> T { + t[0] +} + +mod with_drop { + pub struct A; + pub struct B; + impl Drop for A { + fn drop(&mut self) {} + } + + impl B { + // This can be const, because `a` is passed by reference + pub fn b(self, a: &A) -> B { + B + } + } +} + +// Should not be const +fn main() {} 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 new file mode 100644 index 0000000000000..8dde56cd79f44 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -0,0 +1,77 @@ +error: this could be a `const fn` + --> $DIR/could_be_const.rs:13:5 + | +LL | / pub fn new() -> Self { +LL | | Self { guess: 42 } +LL | | } + | |_____^ + | + = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:17:5 + | +LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { +LL | | b +LL | | } + | |_____^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:23:1 + | +LL | / fn one() -> i32 { +LL | | 1 +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:28:1 + | +LL | / fn two() -> i32 { +LL | | let abc = 2; +LL | | abc +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:34:1 + | +LL | / fn string() -> String { +LL | | String::new() +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:39:1 + | +LL | / unsafe fn four() -> i32 { +LL | | 4 +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:44:1 + | +LL | / fn generic(t: T) -> T { +LL | | t +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:48:1 + | +LL | / fn sub(x: u32) -> usize { +LL | | unsafe { transmute(&x) } +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:67:9 + | +LL | / pub fn b(self, a: &A) -> B { +LL | | B +LL | | } + | |_________^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs new file mode 100644 index 0000000000000..b73b24b8e0a3b --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline.rs @@ -0,0 +1,66 @@ +#![warn(clippy::missing_inline_in_public_items)] +#![crate_type = "dylib"] +// When denying at the crate level, be sure to not get random warnings from the +// injected intrinsics by the compiler. +#![allow(dead_code, non_snake_case)] + +type Typedef = String; +pub type PubTypedef = String; + +struct Foo {} // ok +pub struct PubFoo {} // ok +enum FooE {} // ok +pub enum PubFooE {} // ok + +mod module {} // ok +pub mod pub_module {} // ok + +fn foo() {} +pub fn pub_foo() {} // missing #[inline] +#[inline] +pub fn pub_foo_inline() {} // ok +#[inline(always)] +pub fn pub_foo_inline_always() {} // ok + +#[allow(clippy::missing_inline_in_public_items)] +pub fn pub_foo_no_inline() {} + +trait Bar { + fn Bar_a(); // ok + fn Bar_b() {} // ok +} + +pub trait PubBar { + fn PubBar_a(); // ok + fn PubBar_b() {} // missing #[inline] + #[inline] + fn PubBar_c() {} // ok +} + +// none of these need inline because Foo is not exported +impl PubBar for Foo { + fn PubBar_a() {} // ok + fn PubBar_b() {} // ok + fn PubBar_c() {} // ok +} + +// all of these need inline because PubFoo is exported +impl PubBar for PubFoo { + fn PubBar_a() {} // missing #[inline] + fn PubBar_b() {} // missing #[inline] + fn PubBar_c() {} // missing #[inline] +} + +// do not need inline because Foo is not exported +impl Foo { + fn FooImpl() {} // ok +} + +// need inline because PubFoo is exported +impl PubFoo { + pub fn PubFooImpl() {} // missing #[inline] +} + +// do not lint this since users cannot control the external code +#[derive(Debug)] +pub struct S {} diff --git a/src/tools/clippy/tests/ui/missing_inline.stderr b/src/tools/clippy/tests/ui/missing_inline.stderr new file mode 100644 index 0000000000000..40b92b7647bf7 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline.stderr @@ -0,0 +1,40 @@ +error: missing `#[inline]` for a function + --> $DIR/missing_inline.rs:19:1 + | +LL | pub fn pub_foo() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings` + +error: missing `#[inline]` for a default trait method + --> $DIR/missing_inline.rs:35:5 + | +LL | fn PubBar_b() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:49:5 + | +LL | fn PubBar_a() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:50:5 + | +LL | fn PubBar_b() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:51:5 + | +LL | fn PubBar_c() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:61:5 + | +LL | pub fn PubFooImpl() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed new file mode 100644 index 0000000000000..baee773573038 --- /dev/null +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed @@ -0,0 +1,24 @@ +// run-rustfix + +#![allow(dead_code, unused_variables, clippy::excessive_precision)] + +fn main() { + let fail14 = 2_i32; + let fail15 = 4_i64; + let fail16 = 7_i8; // + let fail17 = 23_i16; // + let ok18 = 23_128; + + let fail20 = 2_i8; // + let fail21 = 4_i16; // + + let fail24 = 12.34_f64; + let fail25 = 1E2_f32; + let fail26 = 43E7_f64; + let fail27 = 243E17_f32; + #[allow(overflowing_literals)] + let fail28 = 241_251_235E723_f64; + let fail29 = 42_279.911_f32; + + let _ = 1.123_45E1_f32; +} diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs new file mode 100644 index 0000000000000..6de447f40214b --- /dev/null +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs @@ -0,0 +1,24 @@ +// run-rustfix + +#![allow(dead_code, unused_variables, clippy::excessive_precision)] + +fn main() { + let fail14 = 2_32; + let fail15 = 4_64; + let fail16 = 7_8; // + let fail17 = 23_16; // + let ok18 = 23_128; + + let fail20 = 2__8; // + let fail21 = 4___16; // + + let fail24 = 12.34_64; + let fail25 = 1E2_32; + let fail26 = 43E7_64; + let fail27 = 243E17_32; + #[allow(overflowing_literals)] + let fail28 = 241251235E723_64; + let fail29 = 42279.911_32; + + let _ = 1.12345E1_32; +} diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr new file mode 100644 index 0000000000000..48a7ae904948c --- /dev/null +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr @@ -0,0 +1,82 @@ +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:6:18 + | +LL | let fail14 = 2_32; + | ^^^^ help: did you mean to write: `2_i32` + | + = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:7:18 + | +LL | let fail15 = 4_64; + | ^^^^ help: did you mean to write: `4_i64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:8:18 + | +LL | let fail16 = 7_8; // + | ^^^ help: did you mean to write: `7_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:9:18 + | +LL | let fail17 = 23_16; // + | ^^^^^ help: did you mean to write: `23_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:12:18 + | +LL | let fail20 = 2__8; // + | ^^^^ help: did you mean to write: `2_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:13:18 + | +LL | let fail21 = 4___16; // + | ^^^^^^ help: did you mean to write: `4_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:15:18 + | +LL | let fail24 = 12.34_64; + | ^^^^^^^^ help: did you mean to write: `12.34_f64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:16:18 + | +LL | let fail25 = 1E2_32; + | ^^^^^^ help: did you mean to write: `1E2_f32` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:17:18 + | +LL | let fail26 = 43E7_64; + | ^^^^^^^ help: did you mean to write: `43E7_f64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:18:18 + | +LL | let fail27 = 243E17_32; + | ^^^^^^^^^ help: did you mean to write: `243E17_f32` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:20:18 + | +LL | let fail28 = 241251235E723_64; + | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:21:18 + | +LL | let fail29 = 42279.911_32; + | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:23:13 + | +LL | let _ = 1.12345E1_32; + | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/module_inception.rs b/src/tools/clippy/tests/ui/module_inception.rs new file mode 100644 index 0000000000000..a23aba9164a5c --- /dev/null +++ b/src/tools/clippy/tests/ui/module_inception.rs @@ -0,0 +1,21 @@ +#![warn(clippy::module_inception)] + +mod foo { + mod bar { + mod bar { + mod foo {} + } + mod foo {} + } + mod foo { + mod bar {} + } +} + +// No warning. See . +mod bar { + #[allow(clippy::module_inception)] + mod bar {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/module_inception.stderr b/src/tools/clippy/tests/ui/module_inception.stderr new file mode 100644 index 0000000000000..77564dce9eb48 --- /dev/null +++ b/src/tools/clippy/tests/ui/module_inception.stderr @@ -0,0 +1,20 @@ +error: module has the same name as its containing module + --> $DIR/module_inception.rs:5:9 + | +LL | / mod bar { +LL | | mod foo {} +LL | | } + | |_________^ + | + = note: `-D clippy::module-inception` implied by `-D warnings` + +error: module has the same name as its containing module + --> $DIR/module_inception.rs:10:5 + | +LL | / mod foo { +LL | | mod bar {} +LL | | } + | |_____^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs new file mode 100644 index 0000000000000..669bf01a84c10 --- /dev/null +++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs @@ -0,0 +1,26 @@ +// compile-flags: --test + +#![warn(clippy::module_name_repetitions)] +#![allow(dead_code)] + +mod foo { + pub fn foo() {} + pub fn foo_bar() {} + pub fn bar_foo() {} + pub struct FooCake {} + pub enum CakeFoo {} + pub struct Foo7Bar; + + // Should not warn + pub struct Foobar; +} + +#[cfg(test)] +mod test { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr new file mode 100644 index 0000000000000..bdd217a969c05 --- /dev/null +++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr @@ -0,0 +1,34 @@ +error: item name starts with its containing module's name + --> $DIR/module_name_repetitions.rs:8:5 + | +LL | pub fn foo_bar() {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::module-name-repetitions` implied by `-D warnings` + +error: item name ends with its containing module's name + --> $DIR/module_name_repetitions.rs:9:5 + | +LL | pub fn bar_foo() {} + | ^^^^^^^^^^^^^^^^^^^ + +error: item name starts with its containing module's name + --> $DIR/module_name_repetitions.rs:10:5 + | +LL | pub struct FooCake {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: item name ends with its containing module's name + --> $DIR/module_name_repetitions.rs:11:5 + | +LL | pub enum CakeFoo {} + | ^^^^^^^^^^^^^^^^^^^ + +error: item name starts with its containing module's name + --> $DIR/module_name_repetitions.rs:12:5 + | +LL | pub struct Foo7Bar; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs new file mode 100644 index 0000000000000..b010b0dbdfa69 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs @@ -0,0 +1,36 @@ +#![warn(clippy::modulo_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one +)] + +fn main() { + // Lint when both sides are const and of the opposite sign + -1.6 % 2.1; + 1.6 % -2.1; + (1.1 - 2.3) % (1.1 + 2.3); + (1.1 + 2.3) % (1.1 - 2.3); + + // Lint on floating point numbers + let a_f32: f32 = -1.6; + let mut b_f32: f32 = 2.1; + a_f32 % b_f32; + b_f32 % a_f32; + b_f32 %= a_f32; + + let a_f64: f64 = -1.6; + let mut b_f64: f64 = 2.1; + a_f64 % b_f64; + b_f64 % a_f64; + b_f64 %= a_f64; + + // No lint when both sides are const and of the same sign + 1.6 % 2.1; + -1.6 % -2.1; + (1.1 + 2.3) % (-1.1 + 2.3); + (-1.1 - 2.3) % (1.1 - 2.3); +} diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr new file mode 100644 index 0000000000000..7bfdb0bde6070 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr @@ -0,0 +1,83 @@ +error: you are using modulo operator on constants with different signs: `-1.600 % 2.100` + --> $DIR/modulo_arithmetic_float.rs:13:5 + | +LL | -1.6 % 2.1; + | ^^^^^^^^^^ + | + = note: `-D clippy::modulo-arithmetic` implied by `-D warnings` + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on constants with different signs: `1.600 % -2.100` + --> $DIR/modulo_arithmetic_float.rs:14:5 + | +LL | 1.6 % -2.1; + | ^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on constants with different signs: `-1.200 % 3.400` + --> $DIR/modulo_arithmetic_float.rs:15:5 + | +LL | (1.1 - 2.3) % (1.1 + 2.3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on constants with different signs: `3.400 % -1.200` + --> $DIR/modulo_arithmetic_float.rs:16:5 + | +LL | (1.1 + 2.3) % (1.1 - 2.3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:21:5 + | +LL | a_f32 % b_f32; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:22:5 + | +LL | b_f32 % a_f32; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:23:5 + | +LL | b_f32 %= a_f32; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:27:5 + | +LL | a_f64 % b_f64; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:28:5 + | +LL | b_f64 % a_f64; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:29:5 + | +LL | b_f64 %= a_f64; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs new file mode 100644 index 0000000000000..779d035c5f8a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs @@ -0,0 +1,90 @@ +#![warn(clippy::modulo_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one +)] + +fn main() { + // Lint on signed integral numbers + let a = -1; + let mut b = 2; + a % b; + b % a; + b %= a; + + let a_i8: i8 = 1; + let mut b_i8: i8 = 2; + a_i8 % b_i8; + b_i8 %= a_i8; + + let a_i16: i16 = 1; + let mut b_i16: i16 = 2; + a_i16 % b_i16; + b_i16 %= a_i16; + + let a_i32: i32 = 1; + let mut b_i32: i32 = 2; + a_i32 % b_i32; + b_i32 %= a_i32; + + let a_i64: i64 = 1; + let mut b_i64: i64 = 2; + a_i64 % b_i64; + b_i64 %= a_i64; + + let a_i128: i128 = 1; + let mut b_i128: i128 = 2; + a_i128 % b_i128; + b_i128 %= a_i128; + + let a_isize: isize = 1; + let mut b_isize: isize = 2; + a_isize % b_isize; + b_isize %= a_isize; + + let a = 1; + let mut b = 2; + a % b; + b %= a; + + // No lint on unsigned integral value + let a_u8: u8 = 17; + let b_u8: u8 = 3; + a_u8 % b_u8; + let mut a_u8: u8 = 1; + a_u8 %= 2; + + let a_u16: u16 = 17; + let b_u16: u16 = 3; + a_u16 % b_u16; + let mut a_u16: u16 = 1; + a_u16 %= 2; + + let a_u32: u32 = 17; + let b_u32: u32 = 3; + a_u32 % b_u32; + let mut a_u32: u32 = 1; + a_u32 %= 2; + + let a_u64: u64 = 17; + let b_u64: u64 = 3; + a_u64 % b_u64; + let mut a_u64: u64 = 1; + a_u64 %= 2; + + let a_u128: u128 = 17; + let b_u128: u128 = 3; + a_u128 % b_u128; + let mut a_u128: u128 = 1; + a_u128 %= 2; + + let a_usize: usize = 17; + let b_usize: usize = 3; + a_usize % b_usize; + let mut a_usize: usize = 1; + a_usize %= 2; +} diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr new file mode 100644 index 0000000000000..e863b838699e9 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr @@ -0,0 +1,156 @@ +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:15:5 + | +LL | a % b; + | ^^^^^ + | + = note: `-D clippy::modulo-arithmetic` implied by `-D warnings` + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:16:5 + | +LL | b % a; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:17:5 + | +LL | b %= a; + | ^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:21:5 + | +LL | a_i8 % b_i8; + | ^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:22:5 + | +LL | b_i8 %= a_i8; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:26:5 + | +LL | a_i16 % b_i16; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:27:5 + | +LL | b_i16 %= a_i16; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:31:5 + | +LL | a_i32 % b_i32; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:32:5 + | +LL | b_i32 %= a_i32; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:36:5 + | +LL | a_i64 % b_i64; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:37:5 + | +LL | b_i64 %= a_i64; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:41:5 + | +LL | a_i128 % b_i128; + | ^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:42:5 + | +LL | b_i128 %= a_i128; + | ^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:46:5 + | +LL | a_isize % b_isize; + | ^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:47:5 + | +LL | b_isize %= a_isize; + | ^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:51:5 + | +LL | a % b; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:52:5 + | +LL | b %= a; + | ^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs new file mode 100644 index 0000000000000..57a96692c0097 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs @@ -0,0 +1,44 @@ +#![warn(clippy::modulo_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one +)] + +fn main() { + // Lint when both sides are const and of the opposite sign + -1 % 2; + 1 % -2; + (1 - 2) % (1 + 2); + (1 + 2) % (1 - 2); + 35 * (7 - 4 * 2) % (-500 * -600); + + -1i8 % 2i8; + 1i8 % -2i8; + -1i16 % 2i16; + 1i16 % -2i16; + -1i32 % 2i32; + 1i32 % -2i32; + -1i64 % 2i64; + 1i64 % -2i64; + -1i128 % 2i128; + 1i128 % -2i128; + -1isize % 2isize; + 1isize % -2isize; + + // No lint when both sides are const and of the same sign + 1 % 2; + -1 % -2; + (1 + 2) % (-1 + 2); + (-1 - 2) % (1 - 2); + + 1u8 % 2u8; + 1u16 % 2u16; + 1u32 % 2u32; + 1u64 % 2u64; + 1u128 % 2u128; + 1usize % 2usize; +} diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr new file mode 100644 index 0000000000000..de328bb75fe91 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr @@ -0,0 +1,156 @@ +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:13:5 + | +LL | -1 % 2; + | ^^^^^^ + | + = note: `-D clippy::modulo-arithmetic` implied by `-D warnings` + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:14:5 + | +LL | 1 % -2; + | ^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 3` + --> $DIR/modulo_arithmetic_integral_const.rs:15:5 + | +LL | (1 - 2) % (1 + 2); + | ^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `3 % -1` + --> $DIR/modulo_arithmetic_integral_const.rs:16:5 + | +LL | (1 + 2) % (1 - 2); + | ^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-35 % 300000` + --> $DIR/modulo_arithmetic_integral_const.rs:17:5 + | +LL | 35 * (7 - 4 * 2) % (-500 * -600); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:19:5 + | +LL | -1i8 % 2i8; + | ^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:20:5 + | +LL | 1i8 % -2i8; + | ^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:21:5 + | +LL | -1i16 % 2i16; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:22:5 + | +LL | 1i16 % -2i16; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:23:5 + | +LL | -1i32 % 2i32; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:24:5 + | +LL | 1i32 % -2i32; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:25:5 + | +LL | -1i64 % 2i64; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:26:5 + | +LL | 1i64 % -2i64; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:27:5 + | +LL | -1i128 % 2i128; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:28:5 + | +LL | 1i128 % -2i128; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:29:5 + | +LL | -1isize % 2isize; + | ^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:30:5 + | +LL | 1isize % -2isize; + | ^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_one.rs b/src/tools/clippy/tests/ui/modulo_one.rs new file mode 100644 index 0000000000000..cc8c8e7cdaefd --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_one.rs @@ -0,0 +1,14 @@ +#![warn(clippy::modulo_one)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +static STATIC_ONE: usize = 2 - 1; + +fn main() { + 10 % 1; + 10 % 2; + + const ONE: u32 = 1 * 1; + + 2 % ONE; + 5 % STATIC_ONE; +} diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr new file mode 100644 index 0000000000000..6bee68360b6fb --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_one.stderr @@ -0,0 +1,30 @@ +error: any number modulo 1 will be 0 + --> $DIR/modulo_one.rs:7:5 + | +LL | 10 % 1; + | ^^^^^^ + | + = note: `-D clippy::modulo-one` implied by `-D warnings` + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/modulo_one.rs:10:22 + | +LL | const ONE: u32 = 1 * 1; + | ^^^^^ + | + = note: `-D clippy::identity-op` implied by `-D warnings` + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/modulo_one.rs:10:22 + | +LL | const ONE: u32 = 1 * 1; + | ^^^^^ + +error: any number modulo 1 will be 0 + --> $DIR/modulo_one.rs:12:5 + | +LL | 2 % ONE; + | ^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed new file mode 100644 index 0000000000000..9556f6f82cc63 --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -0,0 +1,93 @@ +// run-rustfix +#![feature(never_type)] +#![allow(unused_mut, clippy::redundant_allocation)] +#![warn(clippy::must_use_candidate)] +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +pub struct MyAtomic(AtomicBool); +pub struct MyPure; + +#[must_use] pub fn pure(i: u8) -> u8 { + i +} + +impl MyPure { + #[must_use] pub fn inherent_pure(&self) -> u8 { + 0 + } +} + +pub trait MyPureTrait { + fn trait_pure(&self, i: u32) -> u32 { + self.trait_impl_pure(i) + 1 + } + + fn trait_impl_pure(&self, i: u32) -> u32; +} + +impl MyPureTrait for MyPure { + fn trait_impl_pure(&self, i: u32) -> u32 { + i + } +} + +pub fn without_result() { + // OK +} + +pub fn impure_primitive(i: &mut u8) -> u8 { + *i +} + +pub fn with_callback bool>(f: &F) -> bool { + f(0) +} + +#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { + true +} + +pub fn quoth_the_raven(_more: !) -> u32 { + unimplemented!(); +} + +pub fn atomics(b: &AtomicBool) -> bool { + b.load(Ordering::SeqCst) +} + +#[must_use] pub fn rcd(_x: Rc) -> bool { + true +} + +pub fn rcmut(_x: Rc<&mut u32>) -> bool { + true +} + +#[must_use] pub fn arcd(_x: Arc) -> bool { + false +} + +pub fn inner_types(_m: &MyAtomic) -> bool { + true +} + +static mut COUNTER: usize = 0; + +/// # Safety +/// +/// Don't ever call this from multiple threads +pub unsafe fn mutates_static() -> usize { + COUNTER += 1; + COUNTER +} + +#[no_mangle] +pub fn unmangled(i: bool) -> bool { + !i +} + +fn main() { + assert_eq!(1, pure(1)); +} diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs new file mode 100644 index 0000000000000..3732422017104 --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -0,0 +1,93 @@ +// run-rustfix +#![feature(never_type)] +#![allow(unused_mut, clippy::redundant_allocation)] +#![warn(clippy::must_use_candidate)] +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +pub struct MyAtomic(AtomicBool); +pub struct MyPure; + +pub fn pure(i: u8) -> u8 { + i +} + +impl MyPure { + pub fn inherent_pure(&self) -> u8 { + 0 + } +} + +pub trait MyPureTrait { + fn trait_pure(&self, i: u32) -> u32 { + self.trait_impl_pure(i) + 1 + } + + fn trait_impl_pure(&self, i: u32) -> u32; +} + +impl MyPureTrait for MyPure { + fn trait_impl_pure(&self, i: u32) -> u32 { + i + } +} + +pub fn without_result() { + // OK +} + +pub fn impure_primitive(i: &mut u8) -> u8 { + *i +} + +pub fn with_callback bool>(f: &F) -> bool { + f(0) +} + +pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { + true +} + +pub fn quoth_the_raven(_more: !) -> u32 { + unimplemented!(); +} + +pub fn atomics(b: &AtomicBool) -> bool { + b.load(Ordering::SeqCst) +} + +pub fn rcd(_x: Rc) -> bool { + true +} + +pub fn rcmut(_x: Rc<&mut u32>) -> bool { + true +} + +pub fn arcd(_x: Arc) -> bool { + false +} + +pub fn inner_types(_m: &MyAtomic) -> bool { + true +} + +static mut COUNTER: usize = 0; + +/// # Safety +/// +/// Don't ever call this from multiple threads +pub unsafe fn mutates_static() -> usize { + COUNTER += 1; + COUNTER +} + +#[no_mangle] +pub fn unmangled(i: bool) -> bool { + !i +} + +fn main() { + assert_eq!(1, pure(1)); +} diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr new file mode 100644 index 0000000000000..0fa3849d03bff --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr @@ -0,0 +1,34 @@ +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:12:1 + | +LL | pub fn pure(i: u8) -> u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8` + | + = note: `-D clippy::must-use-candidate` implied by `-D warnings` + +error: this method could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:17:5 + | +LL | pub fn inherent_pure(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8` + +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:48:1 + | +LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool` + +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:60:1 + | +LL | pub fn rcd(_x: Rc) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc) -> bool` + +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:68:1 + | +LL | pub fn arcd(_x: Arc) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc) -> bool` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed new file mode 100644 index 0000000000000..6c9aa434ac016 --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -0,0 +1,26 @@ +//run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::must_use_unit)] +#![allow(clippy::unused_unit)] + +#[macro_use] +extern crate macro_rules; + + +pub fn must_use_default() {} + + +pub fn must_use_unit() -> () {} + + +pub fn must_use_with_note() {} + +fn main() { + must_use_default(); + must_use_unit(); + must_use_with_note(); + + // We should not lint in external macros + must_use_unit!(); +} diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs new file mode 100644 index 0000000000000..8a395dc284db4 --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit.rs @@ -0,0 +1,26 @@ +//run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::must_use_unit)] +#![allow(clippy::unused_unit)] + +#[macro_use] +extern crate macro_rules; + +#[must_use] +pub fn must_use_default() {} + +#[must_use] +pub fn must_use_unit() -> () {} + +#[must_use = "With note"] +pub fn must_use_with_note() {} + +fn main() { + must_use_default(); + must_use_unit(); + must_use_with_note(); + + // We should not lint in external macros + must_use_unit!(); +} diff --git a/src/tools/clippy/tests/ui/must_use_unit.stderr b/src/tools/clippy/tests/ui/must_use_unit.stderr new file mode 100644 index 0000000000000..15e0906b66b5e --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit.stderr @@ -0,0 +1,28 @@ +error: this unit-returning function has a `#[must_use]` attribute + --> $DIR/must_use_unit.rs:11:1 + | +LL | #[must_use] + | ----------- help: remove the attribute +LL | pub fn must_use_default() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::must-use-unit` implied by `-D warnings` + +error: this unit-returning function has a `#[must_use]` attribute + --> $DIR/must_use_unit.rs:14:1 + | +LL | #[must_use] + | ----------- help: remove the attribute +LL | pub fn must_use_unit() -> () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this unit-returning function has a `#[must_use]` attribute + --> $DIR/must_use_unit.rs:17:1 + | +LL | #[must_use = "With note"] + | ------------------------- help: remove the attribute +LL | pub fn must_use_with_note() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs new file mode 100644 index 0000000000000..a9a04c8f56b94 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -0,0 +1,46 @@ +#![allow(unused)] +#![warn(clippy::mut_from_ref)] + +struct Foo; + +impl Foo { + fn this_wont_hurt_a_bit(&self) -> &mut Foo { + unimplemented!() + } +} + +trait Ouch { + fn ouch(x: &Foo) -> &mut Foo; +} + +impl Ouch for Foo { + fn ouch(x: &Foo) -> &mut Foo { + unimplemented!() + } +} + +fn fail(x: &u32) -> &mut u16 { + unimplemented!() +} + +fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { + unimplemented!() +} + +fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { + unimplemented!() +} + +// this is OK, because the result borrows y +fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { + unimplemented!() +} + +// this is also OK, because the result could borrow y +fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { + unimplemented!() +} + +fn main() { + //TODO +} diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr new file mode 100644 index 0000000000000..4787999920bc2 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -0,0 +1,63 @@ +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:7:39 + | +LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { + | ^^^^^^^^ + | + = note: `-D clippy::mut-from-ref` implied by `-D warnings` +note: immutable borrow here + --> $DIR/mut_from_ref.rs:7:29 + | +LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { + | ^^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:13:25 + | +LL | fn ouch(x: &Foo) -> &mut Foo; + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:13:16 + | +LL | fn ouch(x: &Foo) -> &mut Foo; + | ^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:22:21 + | +LL | fn fail(x: &u32) -> &mut u16 { + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:22:12 + | +LL | fn fail(x: &u32) -> &mut u16 { + | ^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:26:50 + | +LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:26:25 + | +LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { + | ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:30:67 + | +LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:30:27 + | +LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { + | ^^^^^^^ ^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_key.rs b/src/tools/clippy/tests/ui/mut_key.rs new file mode 100644 index 0000000000000..2d227e6654c36 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_key.rs @@ -0,0 +1,55 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::{Hash, Hasher}; +use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + +struct Key(AtomicUsize); + +impl Clone for Key { + fn clone(&self) -> Self { + Key(AtomicUsize::new(self.0.load(Relaxed))) + } +} + +impl PartialEq for Key { + fn eq(&self, other: &Self) -> bool { + self.0.load(Relaxed) == other.0.load(Relaxed) + } +} + +impl Eq for Key {} + +impl Hash for Key { + fn hash(&self, h: &mut H) { + self.0.load(Relaxed).hash(h); + } +} + +fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { + let _other: HashMap = HashMap::new(); + m.keys().cloned().collect() +} + +fn this_is_ok(_m: &mut HashMap) {} + +#[allow(unused)] +trait Trait { + type AssociatedType; + + fn trait_fn(&self, set: std::collections::HashSet); +} + +fn generics_are_ok_too(_m: &mut HashSet) { + // nothing to see here, move along +} + +fn tuples(_m: &mut HashMap<((), U), ()>) {} + +fn tuples_bad(_m: &mut HashMap<(Key, U), bool>) {} + +fn main() { + let _ = should_not_take_this_arg(&mut HashMap::new(), 1); + this_is_ok(&mut HashMap::new()); + tuples::(&mut HashMap::new()); + tuples::<()>(&mut HashMap::new()); + tuples_bad::<()>(&mut HashMap::new()); +} diff --git a/src/tools/clippy/tests/ui/mut_key.stderr b/src/tools/clippy/tests/ui/mut_key.stderr new file mode 100644 index 0000000000000..8d6a259c7e385 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_key.stderr @@ -0,0 +1,28 @@ +error: mutable key type + --> $DIR/mut_key.rs:27:32 + | +LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::mutable_key_type)]` on by default + +error: mutable key type + --> $DIR/mut_key.rs:27:72 + | +LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { + | ^^^^^^^^^^^^ + +error: mutable key type + --> $DIR/mut_key.rs:28:5 + | +LL | let _other: HashMap = HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: mutable key type + --> $DIR/mut_key.rs:47:22 + | +LL | fn tuples_bad(_m: &mut HashMap<(Key, U), bool>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs new file mode 100644 index 0000000000000..8965cef66dedd --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_mut.rs @@ -0,0 +1,49 @@ +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] +#![warn(clippy::mut_mut)] + +fn fun(x: &mut &mut u32) -> bool { + **x > 0 +} + +fn less_fun(x: *mut *mut u32) { + let y = x; +} + +macro_rules! mut_ptr { + ($p:expr) => { + &mut $p + }; +} + +#[allow(unused_mut, unused_variables)] +fn main() { + let mut x = &mut &mut 1u32; + { + let mut y = &mut x; + } + + if fun(x) { + let y: &mut &mut u32 = &mut &mut 2; + **y + **x; + } + + if fun(x) { + let y: &mut &mut &mut u32 = &mut &mut &mut 2; + ***y + **x; + } + + let mut z = mut_ptr!(&mut 3u32); +} + +fn issue939() { + let array = [5, 6, 7, 8, 9]; + let mut args = array.iter().skip(2); + for &arg in &mut args { + println!("{}", arg); + } + + let args = &mut args; + for arg in args { + println!(":{}", arg); + } +} diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr new file mode 100644 index 0000000000000..44e8142271418 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_mut.stderr @@ -0,0 +1,63 @@ +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:4:11 + | +LL | fn fun(x: &mut &mut u32) -> bool { + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::mut-mut` implied by `-D warnings` + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:20:17 + | +LL | let mut x = &mut &mut 1u32; + | ^^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:14:9 + | +LL | &mut $p + | ^^^^^^^ +... +LL | let mut z = mut_ptr!(&mut 3u32); + | ------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this expression mutably borrows a mutable reference. Consider reborrowing + --> $DIR/mut_mut.rs:22:21 + | +LL | let mut y = &mut x; + | ^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:26:32 + | +LL | let y: &mut &mut u32 = &mut &mut 2; + | ^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:26:16 + | +LL | let y: &mut &mut u32 = &mut &mut 2; + | ^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:31:37 + | +LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:31:16 + | +LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:31:21 + | +LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; + | ^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_range_bound.rs b/src/tools/clippy/tests/ui/mut_range_bound.rs new file mode 100644 index 0000000000000..1348dd2a3d8bb --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_range_bound.rs @@ -0,0 +1,63 @@ +#![allow(unused)] + +fn main() { + mut_range_bound_upper(); + mut_range_bound_lower(); + mut_range_bound_both(); + mut_range_bound_no_mutation(); + immut_range_bound(); + mut_borrow_range_bound(); + immut_borrow_range_bound(); +} + +fn mut_range_bound_upper() { + let mut m = 4; + for i in 0..m { + m = 5; + } // warning +} + +fn mut_range_bound_lower() { + let mut m = 4; + for i in m..10 { + m *= 2; + } // warning +} + +fn mut_range_bound_both() { + let mut m = 4; + let mut n = 6; + for i in m..n { + m = 5; + n = 7; + } // warning (1 for each mutated bound) +} + +fn mut_range_bound_no_mutation() { + let mut m = 4; + for i in 0..m { + continue; + } // no warning +} + +fn mut_borrow_range_bound() { + let mut m = 4; + for i in 0..m { + let n = &mut m; // warning + *n += 1; + } +} + +fn immut_borrow_range_bound() { + let mut m = 4; + for i in 0..m { + let n = &m; // should be no warning? + } +} + +fn immut_range_bound() { + let m = 4; + for i in 0..m { + continue; + } // no warning +} diff --git a/src/tools/clippy/tests/ui/mut_range_bound.stderr b/src/tools/clippy/tests/ui/mut_range_bound.stderr new file mode 100644 index 0000000000000..0eeb76e0ec5fd --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_range_bound.stderr @@ -0,0 +1,34 @@ +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:16:9 + | +LL | m = 5; + | ^ + | + = note: `-D clippy::mut-range-bound` implied by `-D warnings` + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:23:9 + | +LL | m *= 2; + | ^ + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:31:9 + | +LL | m = 5; + | ^ + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:32:9 + | +LL | n = 7; + | ^ + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:46:22 + | +LL | let n = &mut m; // warning + | ^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_reference.rs b/src/tools/clippy/tests/ui/mut_reference.rs new file mode 100644 index 0000000000000..73906121c402e --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_reference.rs @@ -0,0 +1,43 @@ +#![allow(unused_variables)] + +fn takes_an_immutable_reference(a: &i32) {} +fn takes_a_mutable_reference(a: &mut i32) {} + +struct MyStruct; + +impl MyStruct { + fn takes_an_immutable_reference(&self, a: &i32) {} + + fn takes_a_mutable_reference(&self, a: &mut i32) {} +} + +#[warn(clippy::unnecessary_mut_passed)] +fn main() { + // Functions + takes_an_immutable_reference(&mut 42); + let as_ptr: fn(&i32) = takes_an_immutable_reference; + as_ptr(&mut 42); + + // Methods + let my_struct = MyStruct; + my_struct.takes_an_immutable_reference(&mut 42); + + // No error + + // Functions + takes_an_immutable_reference(&42); + let as_ptr: fn(&i32) = takes_an_immutable_reference; + as_ptr(&42); + + takes_a_mutable_reference(&mut 42); + let as_ptr: fn(&mut i32) = takes_a_mutable_reference; + as_ptr(&mut 42); + + let a = &mut 42; + takes_an_immutable_reference(a); + + // Methods + my_struct.takes_an_immutable_reference(&42); + my_struct.takes_a_mutable_reference(&mut 42); + my_struct.takes_an_immutable_reference(a); +} diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr new file mode 100644 index 0000000000000..fa8c82ae0f340 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_reference.stderr @@ -0,0 +1,22 @@ +error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference + --> $DIR/mut_reference.rs:17:34 + | +LL | takes_an_immutable_reference(&mut 42); + | ^^^^^^^ + | + = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` + +error: The function/method `as_ptr` doesn't need a mutable reference + --> $DIR/mut_reference.rs:19:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ + +error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference + --> $DIR/mut_reference.rs:23:44 + | +LL | my_struct.takes_an_immutable_reference(&mut 42); + | ^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs new file mode 100644 index 0000000000000..b9d78b7f47924 --- /dev/null +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -0,0 +1,15 @@ +#![warn(clippy::all)] +#![warn(clippy::mutex_integer)] + +fn main() { + use std::sync::Mutex; + Mutex::new(true); + Mutex::new(5usize); + Mutex::new(9isize); + let mut x = 4u32; + Mutex::new(&x as *const u32); + Mutex::new(&mut x as *mut u32); + Mutex::new(0u32); + Mutex::new(0i32); + Mutex::new(0f32); // there are no float atomics, so this should not lint +} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr new file mode 100644 index 0000000000000..7dac086585548 --- /dev/null +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -0,0 +1,48 @@ +error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:6:5 + | +LL | Mutex::new(true); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mutex-atomic` implied by `-D warnings` + +error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:7:5 + | +LL | Mutex::new(5usize); + | ^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:8:5 + | +LL | Mutex::new(9isize); + | ^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:10:5 + | +LL | Mutex::new(&x as *const u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:11:5 + | +LL | Mutex::new(&mut x as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:12:5 + | +LL | Mutex::new(0u32); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mutex-integer` implied by `-D warnings` + +error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:13:5 + | +LL | Mutex::new(0i32); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed new file mode 100644 index 0000000000000..567dbc54100a6 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -0,0 +1,98 @@ +// run-rustfix + +#![warn(clippy::needless_bool)] +#![allow( + unused, + dead_code, + clippy::no_effect, + clippy::if_same_then_else, + clippy::needless_return +)] + +use std::cell::Cell; + +macro_rules! bool_comparison_trigger { + ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => ( + + #[derive(Clone)] + pub struct Trigger { + $($i: (Cell, bool, bool)),+ + } + + #[allow(dead_code)] + impl Trigger { + pub fn trigger(&self, key: &str) -> bool { + $( + if let stringify!($i) = key { + return self.$i.1 && self.$i.2 == $def; + } + )+ + false + } + } + ) +} + +fn main() { + let x = true; + let y = false; + x; + !x; + !(x && y); + if x { + x + } else { + false + }; // would also be questionable, but we don't catch this yet + bool_ret3(x); + bool_ret4(x); + bool_ret5(x, x); + bool_ret6(x, x); + needless_bool(x); + needless_bool2(x); + needless_bool3(x); +} + +fn bool_ret3(x: bool) -> bool { + return x; +} + +fn bool_ret4(x: bool) -> bool { + return !x; +} + +fn bool_ret5(x: bool, y: bool) -> bool { + return x && y; +} + +fn bool_ret6(x: bool, y: bool) -> bool { + return !(x && y); +} + +fn needless_bool(x: bool) { + if x {}; +} + +fn needless_bool2(x: bool) { + if !x {}; +} + +fn needless_bool3(x: bool) { + bool_comparison_trigger! { + test_one: false, false; + test_three: false, false; + test_two: true, true; + } + + if x {}; + if !x {}; +} + +fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() { + let b = false; + let returns_bool = || false; + + let x = if b { + true + } else { !returns_bool() }; +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs new file mode 100644 index 0000000000000..10126ad4dbb15 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -0,0 +1,130 @@ +// run-rustfix + +#![warn(clippy::needless_bool)] +#![allow( + unused, + dead_code, + clippy::no_effect, + clippy::if_same_then_else, + clippy::needless_return +)] + +use std::cell::Cell; + +macro_rules! bool_comparison_trigger { + ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => ( + + #[derive(Clone)] + pub struct Trigger { + $($i: (Cell, bool, bool)),+ + } + + #[allow(dead_code)] + impl Trigger { + pub fn trigger(&self, key: &str) -> bool { + $( + if let stringify!($i) = key { + return self.$i.1 && self.$i.2 == $def; + } + )+ + false + } + } + ) +} + +fn main() { + let x = true; + let y = false; + if x { + true + } else { + false + }; + if x { + false + } else { + true + }; + if x && y { + false + } else { + true + }; + if x { + x + } else { + false + }; // would also be questionable, but we don't catch this yet + bool_ret3(x); + bool_ret4(x); + bool_ret5(x, x); + bool_ret6(x, x); + needless_bool(x); + needless_bool2(x); + needless_bool3(x); +} + +fn bool_ret3(x: bool) -> bool { + if x { + return true; + } else { + return false; + }; +} + +fn bool_ret4(x: bool) -> bool { + if x { + return false; + } else { + return true; + }; +} + +fn bool_ret5(x: bool, y: bool) -> bool { + if x && y { + return true; + } else { + return false; + }; +} + +fn bool_ret6(x: bool, y: bool) -> bool { + if x && y { + return false; + } else { + return true; + }; +} + +fn needless_bool(x: bool) { + if x == true {}; +} + +fn needless_bool2(x: bool) { + if x == false {}; +} + +fn needless_bool3(x: bool) { + bool_comparison_trigger! { + test_one: false, false; + test_three: false, false; + test_two: true, true; + } + + if x == true {}; + if x == false {}; +} + +fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() { + let b = false; + let returns_bool = || false; + + let x = if b { + true + } else if returns_bool() { + false + } else { + true + }; +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr new file mode 100644 index 0000000000000..25abfb2a472b6 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -0,0 +1,111 @@ +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:39:5 + | +LL | / if x { +LL | | true +LL | | } else { +LL | | false +LL | | }; + | |_____^ help: you can reduce it to: `x` + | + = note: `-D clippy::needless-bool` implied by `-D warnings` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:44:5 + | +LL | / if x { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `!x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:49:5 + | +LL | / if x && y { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `!(x && y)` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:69:5 + | +LL | / if x { +LL | | return true; +LL | | } else { +LL | | return false; +LL | | }; + | |_____^ help: you can reduce it to: `return x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:77:5 + | +LL | / if x { +LL | | return false; +LL | | } else { +LL | | return true; +LL | | }; + | |_____^ help: you can reduce it to: `return !x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:85:5 + | +LL | / if x && y { +LL | | return true; +LL | | } else { +LL | | return false; +LL | | }; + | |_____^ help: you can reduce it to: `return x && y` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:93:5 + | +LL | / if x && y { +LL | | return false; +LL | | } else { +LL | | return true; +LL | | }; + | |_____^ help: you can reduce it to: `return !(x && y)` + +error: equality checks against true are unnecessary + --> $DIR/fixable.rs:101:8 + | +LL | if x == true {}; + | ^^^^^^^^^ help: try simplifying it as shown: `x` + | + = note: `-D clippy::bool-comparison` implied by `-D warnings` + +error: equality checks against false can be replaced by a negation + --> $DIR/fixable.rs:105:8 + | +LL | if x == false {}; + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: equality checks against true are unnecessary + --> $DIR/fixable.rs:115:8 + | +LL | if x == true {}; + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: equality checks against false can be replaced by a negation + --> $DIR/fixable.rs:116:8 + | +LL | if x == false {}; + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:125:12 + | +LL | } else if returns_bool() { + | ____________^ +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `{ !returns_bool() }` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.rs b/src/tools/clippy/tests/ui/needless_bool/simple.rs new file mode 100644 index 0000000000000..e9f1428fc3a43 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/simple.rs @@ -0,0 +1,46 @@ +#![warn(clippy::needless_bool)] +#![allow( + unused, + dead_code, + clippy::no_effect, + clippy::if_same_then_else, + clippy::needless_return +)] + +fn main() { + let x = true; + let y = false; + if x { + true + } else { + true + }; + if x { + false + } else { + false + }; + if x { + x + } else { + false + }; // would also be questionable, but we don't catch this yet + bool_ret(x); + bool_ret2(x); +} + +fn bool_ret(x: bool) -> bool { + if x { + return true; + } else { + return true; + }; +} + +fn bool_ret2(x: bool) -> bool { + if x { + return false; + } else { + return false; + }; +} diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.stderr b/src/tools/clippy/tests/ui/needless_bool/simple.stderr new file mode 100644 index 0000000000000..c57a8a042fb88 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/simple.stderr @@ -0,0 +1,44 @@ +error: this if-then-else expression will always return true + --> $DIR/simple.rs:13:5 + | +LL | / if x { +LL | | true +LL | | } else { +LL | | true +LL | | }; + | |_____^ + | + = note: `-D clippy::needless-bool` implied by `-D warnings` + +error: this if-then-else expression will always return false + --> $DIR/simple.rs:18:5 + | +LL | / if x { +LL | | false +LL | | } else { +LL | | false +LL | | }; + | |_____^ + +error: this if-then-else expression will always return true + --> $DIR/simple.rs:33:5 + | +LL | / if x { +LL | | return true; +LL | | } else { +LL | | return true; +LL | | }; + | |_____^ + +error: this if-then-else expression will always return false + --> $DIR/simple.rs:41:5 + | +LL | / if x { +LL | | return false; +LL | | } else { +LL | | return false; +LL | | }; + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed new file mode 100644 index 0000000000000..5ae4a0e79b99d --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrow.fixed @@ -0,0 +1,61 @@ +// run-rustfix + +#![allow(clippy::needless_borrowed_reference)] + +fn x(y: &i32) -> i32 { + *y +} + +#[warn(clippy::all, clippy::needless_borrow)] +#[allow(unused_variables)] +fn main() { + let a = 5; + let b = x(&a); + let c = x(&a); + let s = &String::from("hi"); + let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not + let g_val = g(&Vec::new()); // should not error, because `&Vec` derefs to `&[T]` + let vec = Vec::new(); + let vec_val = g(&vec); // should not error, because `&Vec` derefs to `&[T]` + h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` + if let Some(cake) = Some(&5) {} + let garbl = match 42 { + 44 => &a, + 45 => { + println!("foo"); + &&a // FIXME: this should lint, too + }, + 46 => &a, + _ => panic!(), + }; +} + +fn f(y: &T) -> T { + *y +} + +fn g(y: &[u8]) -> u8 { + y[0] +} + +trait Trait {} + +impl<'a> Trait for &'a str {} + +fn h(_: &dyn Trait) {} +#[warn(clippy::needless_borrow)] +#[allow(dead_code)] +fn issue_1432() { + let mut v = Vec::::new(); + let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + let _ = v.iter().filter(|&a| a.is_empty()); + + let _ = v.iter().filter(|&a| a.is_empty()); +} + +#[allow(dead_code)] +#[warn(clippy::needless_borrow)] +#[derive(Debug)] +enum Foo<'a> { + Str(&'a str), +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs new file mode 100644 index 0000000000000..1e281316c8a39 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrow.rs @@ -0,0 +1,61 @@ +// run-rustfix + +#![allow(clippy::needless_borrowed_reference)] + +fn x(y: &i32) -> i32 { + *y +} + +#[warn(clippy::all, clippy::needless_borrow)] +#[allow(unused_variables)] +fn main() { + let a = 5; + let b = x(&a); + let c = x(&&a); + let s = &String::from("hi"); + let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not + let g_val = g(&Vec::new()); // should not error, because `&Vec` derefs to `&[T]` + let vec = Vec::new(); + let vec_val = g(&vec); // should not error, because `&Vec` derefs to `&[T]` + h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` + if let Some(ref cake) = Some(&5) {} + let garbl = match 42 { + 44 => &a, + 45 => { + println!("foo"); + &&a // FIXME: this should lint, too + }, + 46 => &&a, + _ => panic!(), + }; +} + +fn f(y: &T) -> T { + *y +} + +fn g(y: &[u8]) -> u8 { + y[0] +} + +trait Trait {} + +impl<'a> Trait for &'a str {} + +fn h(_: &dyn Trait) {} +#[warn(clippy::needless_borrow)] +#[allow(dead_code)] +fn issue_1432() { + let mut v = Vec::::new(); + let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + let _ = v.iter().filter(|&ref a| a.is_empty()); + + let _ = v.iter().filter(|&a| a.is_empty()); +} + +#[allow(dead_code)] +#[warn(clippy::needless_borrow)] +#[derive(Debug)] +enum Foo<'a> { + Str(&'a str), +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr new file mode 100644 index 0000000000000..0bfeda7914db7 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrow.stderr @@ -0,0 +1,28 @@ +error: this expression borrows a reference that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:14:15 + | +LL | let c = x(&&a); + | ^^^ help: change this to: `&a` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow.rs:21:17 + | +LL | if let Some(ref cake) = Some(&5) {} + | ^^^^^^^^ help: change this to: `cake` + +error: this expression borrows a reference that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:28:15 + | +LL | 46 => &&a, + | ^^^ help: change this to: `&a` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow.rs:51:31 + | +LL | let _ = v.iter().filter(|&ref a| a.is_empty()); + | ^^^^^ help: change this to: `a` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed new file mode 100644 index 0000000000000..a0937a2c5f62f --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#[warn(clippy::needless_borrowed_reference)] +#[allow(unused_variables)] +fn main() { + let mut v = Vec::::new(); + let _ = v.iter_mut().filter(|a| a.is_empty()); + // ^ should be linted + + let var = 3; + let thingy = Some(&var); + if let Some(&ref v) = thingy { + // ^ should be linted + } + + let mut var2 = 5; + let thingy2 = Some(&mut var2); + if let Some(&mut ref mut v) = thingy2 { + // ^ should **not** be linted + // v is borrowed as mutable. + *v = 10; + } + if let Some(&mut ref v) = thingy2 { + // ^ should **not** be linted + // here, v is borrowed as immutable. + // can't do that: + //*v = 15; + } +} + +#[allow(dead_code)] +enum Animal { + Cat(u64), + Dog(u64), +} + +#[allow(unused_variables)] +#[allow(dead_code)] +fn foo(a: &Animal, b: &Animal) { + match (a, b) { + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // ^ and ^ should **not** be linted + (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs new file mode 100644 index 0000000000000..500ac448f0d58 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#[warn(clippy::needless_borrowed_reference)] +#[allow(unused_variables)] +fn main() { + let mut v = Vec::::new(); + let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + // ^ should be linted + + let var = 3; + let thingy = Some(&var); + if let Some(&ref v) = thingy { + // ^ should be linted + } + + let mut var2 = 5; + let thingy2 = Some(&mut var2); + if let Some(&mut ref mut v) = thingy2 { + // ^ should **not** be linted + // v is borrowed as mutable. + *v = 10; + } + if let Some(&mut ref v) = thingy2 { + // ^ should **not** be linted + // here, v is borrowed as immutable. + // can't do that: + //*v = 15; + } +} + +#[allow(dead_code)] +enum Animal { + Cat(u64), + Dog(u64), +} + +#[allow(unused_variables)] +#[allow(dead_code)] +fn foo(a: &Animal, b: &Animal) { + match (a, b) { + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // ^ and ^ should **not** be linted + (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr new file mode 100644 index 0000000000000..0a5cfb3db0b11 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr @@ -0,0 +1,10 @@ +error: this pattern takes a reference on something that is being de-referenced + --> $DIR/needless_borrowed_ref.rs:7:34 + | +LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + | ^^^^^^ help: try removing the `&ref` part and just keep: `a` + | + = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed new file mode 100644 index 0000000000000..b4227eaf2f8bb --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#![allow(unused, clippy::suspicious_map)] + +use std::collections::{BTreeSet, HashMap, HashSet}; + +#[warn(clippy::needless_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect)] +fn main() { + let sample = [1; 5]; + let len = sample.iter().count(); + if sample.iter().next().is_none() { + // Empty + } + sample.iter().cloned().any(|x| x == 1); + sample.iter().map(|x| (x, x)).count(); + // Notice the `HashSet`--this should not be linted + sample.iter().collect::>().len(); + // Neither should this + sample.iter().collect::>().len(); +} diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs new file mode 100644 index 0000000000000..7ee603afeb077 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#![allow(unused, clippy::suspicious_map)] + +use std::collections::{BTreeSet, HashMap, HashSet}; + +#[warn(clippy::needless_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect)] +fn main() { + let sample = [1; 5]; + let len = sample.iter().collect::>().len(); + if sample.iter().collect::>().is_empty() { + // Empty + } + sample.iter().cloned().collect::>().contains(&1); + sample.iter().map(|x| (x, x)).collect::>().len(); + // Notice the `HashSet`--this should not be linted + sample.iter().collect::>().len(); + // Neither should this + sample.iter().collect::>().len(); +} diff --git a/src/tools/clippy/tests/ui/needless_collect.stderr b/src/tools/clippy/tests/ui/needless_collect.stderr new file mode 100644 index 0000000000000..8884c8e161293 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect.stderr @@ -0,0 +1,28 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:11:28 + | +LL | let len = sample.iter().collect::>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:12:21 + | +LL | if sample.iter().collect::>().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:15:27 + | +LL | sample.iter().cloned().collect::>().contains(&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|x| x == 1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:16:34 + | +LL | sample.iter().map(|x| (x, x)).collect::>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs new file mode 100644 index 0000000000000..5da95647f2c15 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_continue.rs @@ -0,0 +1,115 @@ +#![warn(clippy::needless_continue)] + +macro_rules! zero { + ($x:expr) => { + $x == 0 + }; +} + +macro_rules! nonzero { + ($x:expr) => { + !zero!($x) + }; +} + +fn main() { + let mut i = 1; + while i < 10 { + i += 1; + + if i % 2 == 0 && i % 3 == 0 { + println!("{}", i); + println!("{}", i + 1); + if i % 5 == 0 { + println!("{}", i + 2); + } + let i = 0; + println!("bar {} ", i); + } else { + continue; + } + + println!("bleh"); + { + println!("blah"); + } + + // some comments that also should ideally be included in the + // output of the lint suggestion if possible. + if !(!(i == 2) || !(i == 5)) { + println!("lama"); + } + + if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { + continue; + } else { + println!("Blabber"); + println!("Jabber"); + } + + println!("bleh"); + } +} + +mod issue_2329 { + fn condition() -> bool { + unimplemented!() + } + fn update_condition() {} + + // only the outer loop has a label + fn foo() { + 'outer: loop { + println!("Entry"); + while condition() { + update_condition(); + if condition() { + println!("foo-1"); + } else { + continue 'outer; // should not lint here + } + println!("foo-2"); + + update_condition(); + if condition() { + continue 'outer; // should not lint here + } else { + println!("foo-3"); + } + println!("foo-4"); + } + } + } + + // both loops have labels + fn bar() { + 'outer: loop { + println!("Entry"); + 'inner: while condition() { + update_condition(); + if condition() { + println!("bar-1"); + } else { + continue 'outer; // should not lint here + } + println!("bar-2"); + + update_condition(); + if condition() { + println!("bar-3"); + } else { + continue 'inner; // should lint here + } + println!("bar-4"); + + update_condition(); + if condition() { + continue; // should lint here + } else { + println!("bar-5"); + } + println!("bar-6"); + } + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr new file mode 100644 index 0000000000000..8d6a37df9601a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_continue.stderr @@ -0,0 +1,99 @@ +error: this `else` block is redundant + --> $DIR/needless_continue.rs:28:16 + | +LL | } else { + | ________________^ +LL | | continue; +LL | | } + | |_________^ + | + = note: `-D clippy::needless-continue` implied by `-D warnings` + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if i % 2 == 0 && i % 3 == 0 { + println!("{}", i); + println!("{}", i + 1); + if i % 5 == 0 { + println!("{}", i + 2); + } + let i = 0; + println!("bar {} ", i); + // merged code follows: + println!("bleh"); + { + println!("blah"); + } + if !(!(i == 2) || !(i == 5)) { + println!("lama"); + } + if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { + continue; + } else { + println!("Blabber"); + println!("Jabber"); + } + println!("bleh"); + } + +error: there is no need for an explicit `else` block for this `if` expression + --> $DIR/needless_continue.rs:43:9 + | +LL | / if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { +LL | | continue; +LL | | } else { +LL | | println!("Blabber"); +LL | | println!("Jabber"); +LL | | } + | |_________^ + | + = help: consider dropping the `else` clause + if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { + continue; + } + { + println!("Blabber"); + println!("Jabber"); + } + +error: this `else` block is redundant + --> $DIR/needless_continue.rs:100:24 + | +LL | } else { + | ________________________^ +LL | | continue 'inner; // should lint here +LL | | } + | |_________________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if condition() { + println!("bar-3"); + // merged code follows: + println!("bar-4"); + update_condition(); + if condition() { + continue; // should lint here + } else { + println!("bar-5"); + } + println!("bar-6"); + } + +error: there is no need for an explicit `else` block for this `if` expression + --> $DIR/needless_continue.rs:106:17 + | +LL | / if condition() { +LL | | continue; // should lint here +LL | | } else { +LL | | println!("bar-5"); +LL | | } + | |_________________^ + | + = help: consider dropping the `else` clause + if condition() { + continue; // should lint here + } + { + println!("bar-5"); + } + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs new file mode 100644 index 0000000000000..682d7b3c4ceb4 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_doc_main.rs @@ -0,0 +1,74 @@ +/// This is a test for needless `fn main()` in doctests. +/// +/// # Examples +/// +/// This should lint +/// ``` +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This should, too. +/// +/// ```rust +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This one too. +/// +/// ```no_run +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +fn bad_doctests() {} + +/// # Examples +/// +/// This shouldn't lint, because the `main` is empty: +/// ``` +/// fn main(){} +/// ``` +/// +/// This shouldn't lint either, because there's a `static`: +/// ``` +/// static ANSWER: i32 = 42; +/// +/// fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// ``` +/// +/// Neither should this lint because of `extern crate`: +/// ``` +/// #![feature(test)] +/// extern crate test; +/// fn main() { +/// assert_eq(1u8, test::black_box(1)); +/// } +/// ``` +/// +/// We should not lint ignored examples: +/// +/// ```rust,ignore +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// Or even non-rust examples: +/// +/// ```text +/// fn main() { +/// is what starts the program +/// } +/// ``` +fn no_false_positives() {} + +fn main() { + bad_doctests(); + no_false_positives(); +} diff --git a/src/tools/clippy/tests/ui/needless_doc_main.stderr b/src/tools/clippy/tests/ui/needless_doc_main.stderr new file mode 100644 index 0000000000000..65d40ee6832f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_doc_main.stderr @@ -0,0 +1,22 @@ +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:7:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + | + = note: `-D clippy::needless-doctest-main` implied by `-D warnings` + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:15:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:23:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs new file mode 100644 index 0000000000000..913cd004f19f4 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -0,0 +1,262 @@ +#![warn(clippy::needless_lifetimes)] +#![allow(dead_code, clippy::needless_pass_by_value)] + +fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} + +fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} + +// No error; same lifetime on two params. +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {} + +// No error; static involved. +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {} + +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {} + +fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { + x +} + +// No error; static involved. +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { + x +} + +// No error. +fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// No error; two input refs. +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { + x.unwrap() +} + +fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// Where-clause, but without lifetimes. +fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> +where + T: Copy, +{ + Ok(x) +} + +type Ref<'r> = &'r u8; + +// No error; same lifetime on two params. +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {} + +fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) +where + 'b: 'a, +{ +} + +struct Lt<'a, I: 'static> { + x: &'a I, +} + +// No error; fn bound references `'a`. +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + F: Fn(Lt<'a, I>) -> Lt<'a, I>, +{ + unreachable!() +} + +fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, +{ + unreachable!() +} + +// No error; see below. +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) { + f(x); +} + +fn fn_bound_3_cannot_elide() { + let x = 42; + let p = &x; + let mut q = &x; + // This will fail if we elide lifetimes of `fn_bound_3`. + fn_bound_3(p, |y| q = y); +} + +// No error; multiple input refs. +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { + if cond { + x + } else { + f() + } +} + +struct X { + x: u8, +} + +impl X { + fn self_and_out<'s>(&'s self) -> &'s u8 { + &self.x + } + + // No error; multiple input refs. + fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { + &self.x + } + + fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} + + // No error; same lifetimes on two params. + fn self_and_same_in<'s>(&'s self, _x: &'s u8) {} +} + +struct Foo<'a>(&'a u8); + +impl<'a> Foo<'a> { + // No error; lifetime `'a` not defined in method. + fn self_shared_lifetime(&self, _: &'a u8) {} + // No error; bounds exist. + fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {} +} + +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 { + unimplemented!() +} + +fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`). +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`). +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes. +fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { + unimplemented!() +} + +trait WithLifetime<'a> {} + +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>; + +// Should not warn because it won't build without the lifetime. +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str { + unimplemented!() +} + +// Should warn because there is no lifetime on `Drop`, so this would be +// unambiguous if we elided the lifetime. +fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { + unimplemented!() +} + +type FooAlias<'a> = Foo<'a>; + +fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`). +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`). +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes. +fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { + unimplemented!() +} + +fn named_input_elided_output<'a>(_arg: &'a str) -> &str { + unimplemented!() +} + +fn elided_input_named_output<'a>(_arg: &str) -> &'a str { + unimplemented!() +} + +fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { + unimplemented!() +} +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { + unimplemented!() +} + +// Don't warn on these; see issue #292. +fn trait_bound_bug<'a, T: WithLifetime<'a>>() { + unimplemented!() +} + +// See issue #740. +struct Test { + vec: Vec, +} + +impl Test { + fn iter<'a>(&'a self) -> Box + 'a> { + unimplemented!() + } +} + +trait LintContext<'a> {} + +fn f<'a, T: LintContext<'a>>(_: &T) {} + +fn test<'a>(x: &'a [u8]) -> u8 { + let y: &'a u8 = &x[5]; + *y +} + +// Issue #3284: give hint regarding lifetime in return type. +struct Cow<'a> { + x: &'a str, +} +fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { + unimplemented!() +} + +// Make sure we still warn on implementations +mod issue4291 { + trait BadTrait { + fn needless_lt<'a>(x: &'a u8) {} + } + + impl BadTrait for () { + fn needless_lt<'a>(_x: &'a u8) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr new file mode 100644 index 0000000000000..d3a360ed8b576 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -0,0 +1,106 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:4:1 + | +LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:6:1 + | +LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:16:1 + | +LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:45:1 + | +LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:50:1 + | +LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:62:1 + | +LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:86:1 + | +LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:120:5 + | +LL | fn self_and_out<'s>(&'s self) -> &'s u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:129:5 + | +LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:148:1 + | +LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:178:1 + | +LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:184:1 + | +LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:203:1 + | +LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:211:1 + | +LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:247:1 + | +LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:254:9 + | +LL | fn needless_lt<'a>(x: &'a u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:258:9 + | +LL | fn needless_lt<'a>(_x: &'a u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.rs b/src/tools/clippy/tests/ui/needless_pass_by_value.rs new file mode 100644 index 0000000000000..e93a7fe2985b3 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.rs @@ -0,0 +1,161 @@ +#![warn(clippy::needless_pass_by_value)] +#![allow( + dead_code, + clippy::single_match, + clippy::redundant_pattern_matching, + clippy::many_single_char_names, + clippy::option_option, + clippy::redundant_clone +)] + +use std::borrow::Borrow; +use std::collections::HashSet; +use std::convert::AsRef; +use std::mem::MaybeUninit; + +// `v` should be warned +// `w`, `x` and `y` are allowed (moved or mutated) +fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec { + assert_eq!(v.len(), 42); + + consume(w); + + x.push(T::default()); + + y +} + +fn consume(_: T) {} + +struct Wrapper(String); + +fn bar(x: String, y: Wrapper) { + assert_eq!(x.len(), 42); + assert_eq!(y.0.len(), 42); +} + +// V implements `Borrow`, but should be warned correctly +fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { + println!("{}", t.borrow()); + println!("{}", u.as_ref()); + consume(&v); +} + +// ok +fn test_fn i32>(f: F) { + f(1); +} + +// x should be warned, but y is ok +fn test_match(x: Option>, y: Option>) { + match x { + Some(Some(_)) => 1, // not moved + _ => 0, + }; + + match y { + Some(Some(s)) => consume(s), // moved + _ => (), + }; +} + +// x and y should be warned, but z is ok +fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { + let Wrapper(s) = z; // moved + let Wrapper(ref t) = y; // not moved + let Wrapper(_) = y; // still not moved + + assert_eq!(x.0.len(), s.len()); + println!("{}", t); +} + +trait Foo {} + +// `S: Serialize` is allowed to be passed by value, since a caller can pass `&S` instead +trait Serialize {} +impl<'a, T> Serialize for &'a T where T: Serialize {} +impl Serialize for i32 {} + +fn test_blanket_ref(_foo: T, _serializable: S) {} + +fn issue_2114(s: String, t: String, u: Vec, v: Vec) { + s.capacity(); + let _ = t.clone(); + u.capacity(); + let _ = v.clone(); +} + +struct S(T, U); + +impl S { + fn foo( + self, + // taking `self` by value is always allowed + s: String, + t: String, + ) -> usize { + s.len() + t.capacity() + } + + fn bar(_t: T, // Ok, since `&T: Serialize` too + ) { + } + + fn baz(&self, _u: U, _s: Self) {} +} + +trait FalsePositive { + fn visit_str(s: &str); + fn visit_string(s: String) { + Self::visit_str(&s); + } +} + +// shouldn't warn on extern funcs +extern "C" fn ext(x: MaybeUninit) -> usize { + unsafe { x.assume_init() } +} + +// whitelist RangeArgument +fn range>(range: T) { + let _ = range.start_bound(); +} + +struct CopyWrapper(u32); + +fn bar_copy(x: u32, y: CopyWrapper) { + assert_eq!(x, 42); + assert_eq!(y.0, 42); +} + +// x and y should be warned, but z is ok +fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + let CopyWrapper(s) = z; // moved + let CopyWrapper(ref t) = y; // not moved + let CopyWrapper(_) = y; // still not moved + + assert_eq!(x.0, s); + println!("{}", t); +} + +// The following 3 lines should not cause an ICE. See #2831 +trait Bar<'a, A> {} +impl<'b, T> Bar<'b, T> for T {} +fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} + +// Also this should not cause an ICE. See #2831 +trait Club<'a, A> {} +impl Club<'static, T> for T {} +fn more_fun(_item: impl Club<'static, i32>) {} + +fn is_sync(_: T) +where + T: Sync, +{ +} + +fn main() { + // This should not cause an ICE either + // https://github.com/rust-lang/rust-clippy/issues/3144 + is_sync(HashSet::::new()); +} diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr new file mode 100644 index 0000000000000..9aa783bf904e1 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr @@ -0,0 +1,178 @@ +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:18:23 + | +LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec { + | ^^^^^^ help: consider changing the type to: `&[T]` + | + = note: `-D clippy::needless-pass-by-value` implied by `-D warnings` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:32:11 + | +LL | fn bar(x: String, y: Wrapper) { + | ^^^^^^ help: consider changing the type to: `&str` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:32:22 + | +LL | fn bar(x: String, y: Wrapper) { + | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:38:71 + | +LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { + | ^ help: consider taking a reference instead: `&V` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:50:18 + | +LL | fn test_match(x: Option>, y: Option>) { + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option>` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:63:24 + | +LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { + | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:63:36 + | +LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { + | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:79:49 + | +LL | fn test_blanket_ref(_foo: T, _serializable: S) {} + | ^ help: consider taking a reference instead: `&T` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:18 + | +LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { + | ^^^^^^ help: consider taking a reference instead: `&String` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:29 + | +LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { + | ^^^^^^ + | +help: consider changing the type to + | +LL | fn issue_2114(s: String, t: &str, u: Vec, v: Vec) { + | ^^^^ +help: change `t.clone()` to + | +LL | let _ = t.to_string(); + | ^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:40 + | +LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { + | ^^^^^^^^ help: consider taking a reference instead: `&Vec` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:53 + | +LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { + | ^^^^^^^^ + | +help: consider changing the type to + | +LL | fn issue_2114(s: String, t: String, u: Vec, v: &[i32]) { + | ^^^^^^ +help: change `v.clone()` to + | +LL | let _ = v.to_owned(); + | ^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:94:12 + | +LL | s: String, + | ^^^^^^ help: consider changing the type to: `&str` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:95:12 + | +LL | t: String, + | ^^^^^^ help: consider taking a reference instead: `&String` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:104:23 + | +LL | fn baz(&self, _u: U, _s: Self) {} + | ^ help: consider taking a reference instead: `&U` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:104:30 + | +LL | fn baz(&self, _u: U, _s: Self) {} + | ^^^^ help: consider taking a reference instead: `&Self` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:126:24 + | +LL | fn bar_copy(x: u32, y: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:132:29 + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:132:45 + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:132:61 + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:144:40 + | +LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} + | ^ help: consider taking a reference instead: `&S` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:149:20 + | +LL | fn more_fun(_item: impl Club<'static, i32>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs new file mode 100644 index 0000000000000..78a0e92d17979 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs @@ -0,0 +1,21 @@ +#![crate_type = "proc-macro"] +#![warn(clippy::needless_pass_by_value)] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(Foo)] +pub fn foo(_input: TokenStream) -> TokenStream { + unimplemented!() +} + +#[proc_macro] +pub fn bar(_input: TokenStream) -> TokenStream { + unimplemented!() +} + +#[proc_macro_attribute] +pub fn baz(_args: TokenStream, _input: TokenStream) -> TokenStream { + unimplemented!() +} diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs new file mode 100644 index 0000000000000..3fce34367ae50 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop.rs @@ -0,0 +1,95 @@ +#![warn(clippy::needless_range_loop)] + +static STATIC: [usize; 4] = [0, 1, 8, 16]; +const CONST: [usize; 4] = [0, 1, 8, 16]; +const MAX_LEN: usize = 42; + +fn main() { + let mut vec = vec![1, 2, 3, 4]; + let vec2 = vec![1, 2, 3, 4]; + for i in 0..vec.len() { + println!("{}", vec[i]); + } + + for i in 0..vec.len() { + let i = 42; // make a different `i` + println!("{}", vec[i]); // ok, not the `i` of the for-loop + } + + for i in 0..vec.len() { + let _ = vec[i]; + } + + // ICE #746 + for j in 0..4 { + println!("{:?}", STATIC[j]); + } + + for j in 0..4 { + println!("{:?}", CONST[j]); + } + + for i in 0..vec.len() { + println!("{} {}", vec[i], i); + } + for i in 0..vec.len() { + // not an error, indexing more than one variable + println!("{} {}", vec[i], vec2[i]); + } + + for i in 0..vec.len() { + println!("{}", vec2[i]); + } + + for i in 5..vec.len() { + println!("{}", vec[i]); + } + + for i in 0..MAX_LEN { + println!("{}", vec[i]); + } + + for i in 0..=MAX_LEN { + println!("{}", vec[i]); + } + + for i in 5..10 { + println!("{}", vec[i]); + } + + for i in 5..=10 { + println!("{}", vec[i]); + } + + for i in 5..vec.len() { + println!("{} {}", vec[i], i); + } + + for i in 5..10 { + println!("{} {}", vec[i], i); + } + + // #2542 + for i in 0..vec.len() { + vec[i] = Some(1).unwrap_or_else(|| panic!("error on {}", i)); + } + + // #3788 + let test = Test { + inner: vec![1, 2, 3, 4], + }; + for i in 0..2 { + println!("{}", test[i]); + } +} + +struct Test { + inner: Vec, +} + +impl std::ops::Index for Test { + type Output = usize; + fn index(&self, index: usize) -> &Self::Output { + &self.inner[index] + } +} diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr new file mode 100644 index 0000000000000..c50c4931fb4cc --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -0,0 +1,157 @@ +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:10:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | + = note: `-D clippy::needless-range-loop` implied by `-D warnings` +help: consider using an iterator + | +LL | for in &vec { + | ^^^^^^ ^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:19:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in &vec { + | ^^^^^^ ^^^^ + +error: the loop variable `j` is only used to index `STATIC`. + --> $DIR/needless_range_loop.rs:24:14 + | +LL | for j in 0..4 { + | ^^^^ + | +help: consider using an iterator + | +LL | for in &STATIC { + | ^^^^^^ ^^^^^^^ + +error: the loop variable `j` is only used to index `CONST`. + --> $DIR/needless_range_loop.rs:28:14 + | +LL | for j in 0..4 { + | ^^^^ + | +help: consider using an iterator + | +LL | for in &CONST { + | ^^^^^^ ^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:32:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for (i, ) in vec.iter().enumerate() { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec2`. + --> $DIR/needless_range_loop.rs:40:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in vec2.iter().take(vec.len()) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:44:14 + | +LL | for i in 5..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in vec.iter().skip(5) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:48:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in vec.iter().take(MAX_LEN) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:52:14 + | +LL | for i in 0..=MAX_LEN { + | ^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in vec.iter().take(MAX_LEN + 1) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:56:14 + | +LL | for i in 5..10 { + | ^^^^^ + | +help: consider using an iterator + | +LL | for in vec.iter().take(10).skip(5) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:60:14 + | +LL | for i in 5..=10 { + | ^^^^^^ + | +help: consider using an iterator + | +LL | for in vec.iter().take(10 + 1).skip(5) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:64:14 + | +LL | for i in 5..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for (i, ) in vec.iter().enumerate().skip(5) { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:68:14 + | +LL | for i in 5..10 { + | ^^^^^ + | +help: consider using an iterator + | +LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:73:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for (i, ) in vec.iter_mut().enumerate() { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.rs b/src/tools/clippy/tests/ui/needless_range_loop2.rs new file mode 100644 index 0000000000000..2ed1b09bece74 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop2.rs @@ -0,0 +1,85 @@ +#![warn(clippy::needless_range_loop)] + +fn calc_idx(i: usize) -> usize { + (i + i + 20) % 4 +} + +fn main() { + let ns = vec![2, 3, 5, 7]; + + for i in 3..10 { + println!("{}", ns[i]); + } + + for i in 3..10 { + println!("{}", ns[i % 4]); + } + + for i in 3..10 { + println!("{}", ns[i % ns.len()]); + } + + for i in 3..10 { + println!("{}", ns[calc_idx(i)]); + } + + for i in 3..10 { + println!("{}", ns[calc_idx(i) % 4]); + } + + let mut ms = vec![1, 2, 3, 4, 5, 6]; + for i in 0..ms.len() { + ms[i] *= 2; + } + assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]); + + let mut ms = vec![1, 2, 3, 4, 5, 6]; + for i in 0..ms.len() { + let x = &mut ms[i]; + *x *= 2; + } + assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]); + + let g = vec![1, 2, 3, 4, 5, 6]; + let glen = g.len(); + for i in 0..glen { + let x: u32 = g[i + 1..].iter().sum(); + println!("{}", g[i] + x); + } + assert_eq!(g, vec![20, 18, 15, 11, 6, 0]); + + let mut g = vec![1, 2, 3, 4, 5, 6]; + let glen = g.len(); + for i in 0..glen { + g[i] = g[i + 1..].iter().sum(); + } + assert_eq!(g, vec![20, 18, 15, 11, 6, 0]); + + let x = 5; + let mut vec = vec![0; 9]; + + for i in x..x + 4 { + vec[i] += 1; + } + + let x = 5; + let mut vec = vec![0; 10]; + + for i in x..=x + 4 { + vec[i] += 1; + } + + let arr = [1, 2, 3]; + + for i in 0..3 { + println!("{}", arr[i]); + } + + for i in 0..2 { + println!("{}", arr[i]); + } + + for i in 1..3 { + println!("{}", arr[i]); + } +} diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.stderr b/src/tools/clippy/tests/ui/needless_range_loop2.stderr new file mode 100644 index 0000000000000..c54ab5ec9809a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop2.stderr @@ -0,0 +1,91 @@ +error: the loop variable `i` is only used to index `ns`. + --> $DIR/needless_range_loop2.rs:10:14 + | +LL | for i in 3..10 { + | ^^^^^ + | + = note: `-D clippy::needless-range-loop` implied by `-D warnings` +help: consider using an iterator + | +LL | for in ns.iter().take(10).skip(3) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `ms`. + --> $DIR/needless_range_loop2.rs:31:14 + | +LL | for i in 0..ms.len() { + | ^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in &mut ms { + | ^^^^^^ ^^^^^^^ + +error: the loop variable `i` is only used to index `ms`. + --> $DIR/needless_range_loop2.rs:37:14 + | +LL | for i in 0..ms.len() { + | ^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in &mut ms { + | ^^^^^^ ^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop2.rs:61:14 + | +LL | for i in x..x + 4 { + | ^^^^^^^^ + | +help: consider using an iterator + | +LL | for in vec.iter_mut().skip(x).take(4) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop2.rs:68:14 + | +LL | for i in x..=x + 4 { + | ^^^^^^^^^ + | +help: consider using an iterator + | +LL | for in vec.iter_mut().skip(x).take(4 + 1) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `arr`. + --> $DIR/needless_range_loop2.rs:74:14 + | +LL | for i in 0..3 { + | ^^^^ + | +help: consider using an iterator + | +LL | for in &arr { + | ^^^^^^ ^^^^ + +error: the loop variable `i` is only used to index `arr`. + --> $DIR/needless_range_loop2.rs:78:14 + | +LL | for i in 0..2 { + | ^^^^ + | +help: consider using an iterator + | +LL | for in arr.iter().take(2) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `arr`. + --> $DIR/needless_range_loop2.rs:82:14 + | +LL | for i in 1..3 { + | ^^^^ + | +help: consider using an iterator + | +LL | for in arr.iter().skip(1) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed new file mode 100644 index 0000000000000..ad20e2381073a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -0,0 +1,78 @@ +// run-rustfix + +#![allow(unused, clippy::needless_bool)] +#![allow(clippy::if_same_then_else, clippy::single_match)] +#![warn(clippy::needless_return)] + +macro_rules! the_answer { + () => { + 42 + }; +} + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + true +} + +fn test_no_semicolon() -> bool { + true +} + +fn test_if_block() -> bool { + if true { + true + } else { + false + } +} + +fn test_match(x: bool) -> bool { + match x { + true => false, + false => { + true + }, + } +} + +fn test_closure() { + let _ = || { + true + }; + let _ = || true; +} + +fn test_macro_call() -> i32 { + return the_answer!(); +} + +fn test_void_fun() { + +} + +fn test_void_if_fun(b: bool) { + if b { + + } else { + + } +} + +fn test_void_match(x: u32) { + match x { + 0 => (), + _ => {}, + } +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_no_semicolon(); + let _ = test_if_block(); + let _ = test_match(true); + test_closure(); +} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs new file mode 100644 index 0000000000000..af0cdfb207ff5 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -0,0 +1,78 @@ +// run-rustfix + +#![allow(unused, clippy::needless_bool)] +#![allow(clippy::if_same_then_else, clippy::single_match)] +#![warn(clippy::needless_return)] + +macro_rules! the_answer { + () => { + 42 + }; +} + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + return true; +} + +fn test_no_semicolon() -> bool { + return true; +} + +fn test_if_block() -> bool { + if true { + return true; + } else { + return false; + } +} + +fn test_match(x: bool) -> bool { + match x { + true => return false, + false => { + return true; + }, + } +} + +fn test_closure() { + let _ = || { + return true; + }; + let _ = || return true; +} + +fn test_macro_call() -> i32 { + return the_answer!(); +} + +fn test_void_fun() { + return; +} + +fn test_void_if_fun(b: bool) { + if b { + return; + } else { + return; + } +} + +fn test_void_match(x: u32) { + match x { + 0 => (), + _ => return, + } +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_no_semicolon(); + let _ = test_if_block(); + let _ = test_match(true); + test_closure(); +} diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr new file mode 100644 index 0000000000000..c34eecbcbb639 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -0,0 +1,76 @@ +error: unneeded `return` statement + --> $DIR/needless_return.rs:18:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + | + = note: `-D clippy::needless-return` implied by `-D warnings` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:22:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:27:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:29:9 + | +LL | return false; + | ^^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:35:17 + | +LL | true => return false, + | ^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:37:13 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:44:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:46:16 + | +LL | let _ = || return true; + | ^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:54:5 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:59:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:61:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:68:14 + | +LL | _ => return, + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_update.rs b/src/tools/clippy/tests/ui/needless_update.rs new file mode 100644 index 0000000000000..bfa005a19f910 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_update.rs @@ -0,0 +1,14 @@ +#![warn(clippy::needless_update)] +#![allow(clippy::no_effect)] + +struct S { + pub a: i32, + pub b: i32, +} + +fn main() { + let base = S { a: 0, b: 0 }; + S { ..base }; // no error + S { a: 1, ..base }; // no error + S { a: 1, b: 1, ..base }; +} diff --git a/src/tools/clippy/tests/ui/needless_update.stderr b/src/tools/clippy/tests/ui/needless_update.stderr new file mode 100644 index 0000000000000..133c834880dd9 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_update.stderr @@ -0,0 +1,10 @@ +error: struct update has no effect, all the fields in the struct have already been specified + --> $DIR/needless_update.rs:13:23 + | +LL | S { a: 1, b: 1, ..base }; + | ^^^^ + | + = note: `-D clippy::needless-update` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs new file mode 100644 index 0000000000000..856a430ba2b55 --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs @@ -0,0 +1,61 @@ +//! This test case utilizes `f64` an easy example for `PartialOrd` only types +//! but the lint itself actually validates any expression where the left +//! operand implements `PartialOrd` but not `Ord`. + +use std::cmp::Ordering; + +#[warn(clippy::neg_cmp_op_on_partial_ord)] +fn main() { + let a_value = 1.0; + let another_value = 7.0; + + // --- Bad --- + + // Not Less but potentially Greater, Equal or Uncomparable. + let _not_less = !(a_value < another_value); + + // Not Less or Equal but potentially Greater or Uncomparable. + let _not_less_or_equal = !(a_value <= another_value); + + // Not Greater but potentially Less, Equal or Uncomparable. + let _not_greater = !(a_value > another_value); + + // Not Greater or Equal but potentially Less or Uncomparable. + let _not_greater_or_equal = !(a_value >= another_value); + + // --- Good --- + + let _not_less = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Greater) | Some(Ordering::Equal) => true, + _ => false, + }; + let _not_less_or_equal = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Greater) => true, + _ => false, + }; + let _not_greater = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Less) | Some(Ordering::Equal) => true, + _ => false, + }; + let _not_greater_or_equal = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Less) => true, + _ => false, + }; + + // --- Should not trigger --- + + let _ = a_value < another_value; + let _ = a_value <= another_value; + let _ = a_value > another_value; + let _ = a_value >= another_value; + + // --- regression tests --- + + // Issue 2856: False positive on assert!() + // + // The macro always negates the result of the given comparison in its + // internal check which automatically triggered the lint. As it's an + // external macro there was no chance to do anything about it which led + // to a whitelisting of all external macros. + assert!(a_value < another_value); +} diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr new file mode 100644 index 0000000000000..d05fd34ce33be --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -0,0 +1,28 @@ +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:15:21 + | +LL | let _not_less = !(a_value < another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:18:30 + | +LL | let _not_less_or_equal = !(a_value <= another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:21:24 + | +LL | let _not_greater = !(a_value > another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:24:33 + | +LL | let _not_greater_or_equal = !(a_value >= another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs new file mode 100644 index 0000000000000..d4a20ce9db1c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_multiply.rs @@ -0,0 +1,35 @@ +#![warn(clippy::neg_multiply)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +use std::ops::Mul; + +struct X; + +impl Mul for X { + type Output = X; + + fn mul(self, _r: isize) -> Self { + self + } +} + +impl Mul for isize { + type Output = X; + + fn mul(self, _r: X) -> X { + X + } +} + +fn main() { + let x = 0; + + x * -1; + + -1 * x; + + -1 * -1; // should be ok + + X * -1; // should be ok + -1 * X; // should also be ok +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr new file mode 100644 index 0000000000000..f08bbd6a12c59 --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_multiply.stderr @@ -0,0 +1,16 @@ +error: Negation by multiplying with `-1` + --> $DIR/neg_multiply.rs:27:5 + | +LL | x * -1; + | ^^^^^^ + | + = note: `-D clippy::neg-multiply` implied by `-D warnings` + +error: Negation by multiplying with `-1` + --> $DIR/neg_multiply.rs:29:5 + | +LL | -1 * x; + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs new file mode 100644 index 0000000000000..cbc4ca3916168 --- /dev/null +++ b/src/tools/clippy/tests/ui/never_loop.rs @@ -0,0 +1,204 @@ +#![allow( + clippy::single_match, + unused_assignments, + unused_variables, + clippy::while_immutable_condition +)] + +fn test1() { + let mut x = 0; + loop { + // clippy::never_loop + x += 1; + if x == 1 { + return; + } + break; + } +} + +fn test2() { + let mut x = 0; + loop { + x += 1; + if x == 1 { + break; + } + } +} + +fn test3() { + let mut x = 0; + loop { + // never loops + x += 1; + break; + } +} + +fn test4() { + let mut x = 1; + loop { + x += 1; + match x { + 5 => return, + _ => (), + } + } +} + +fn test5() { + let i = 0; + loop { + // never loops + while i == 0 { + // never loops + break; + } + return; + } +} + +fn test6() { + let mut x = 0; + 'outer: loop { + x += 1; + loop { + // never loops + if x == 5 { + break; + } + continue 'outer; + } + return; + } +} + +fn test7() { + let mut x = 0; + loop { + x += 1; + match x { + 1 => continue, + _ => (), + } + return; + } +} + +fn test8() { + let mut x = 0; + loop { + x += 1; + match x { + 5 => return, + _ => continue, + } + } +} + +fn test9() { + let x = Some(1); + while let Some(y) = x { + // never loops + return; + } +} + +fn test10() { + for x in 0..10 { + // never loops + match x { + 1 => break, + _ => return, + } + } +} + +fn test11 i32>(mut f: F) { + loop { + return match f() { + 1 => continue, + _ => (), + }; + } +} + +pub fn test12(a: bool, b: bool) { + 'label: loop { + loop { + if a { + continue 'label; + } + if b { + break; + } + } + break; + } +} + +pub fn test13() { + let mut a = true; + loop { + // infinite loop + while a { + if true { + a = false; + continue; + } + return; + } + } +} + +pub fn test14() { + let mut a = true; + 'outer: while a { + // never loops + while a { + if a { + a = false; + continue; + } + } + break 'outer; + } +} + +// Issue #1991: the outter loop should not warn. +pub fn test15() { + 'label: loop { + while false { + break 'label; + } + } +} + +// Issue #4058: `continue` in `break` expression +pub fn test16() { + let mut n = 1; + loop { + break if n != 5 { + n += 1; + continue; + }; + } +} + +fn main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + test10(); + test11(|| 0); + test12(true, false); + test13(); + test14(); +} diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr new file mode 100644 index 0000000000000..c00b4c78cf28b --- /dev/null +++ b/src/tools/clippy/tests/ui/never_loop.stderr @@ -0,0 +1,100 @@ +error: this loop never actually loops + --> $DIR/never_loop.rs:10:5 + | +LL | / loop { +LL | | // clippy::never_loop +LL | | x += 1; +LL | | if x == 1 { +... | +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/never_loop.rs:32:5 + | +LL | / loop { +LL | | // never loops +LL | | x += 1; +LL | | break; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:52:5 + | +LL | / loop { +LL | | // never loops +LL | | while i == 0 { +LL | | // never loops +... | +LL | | return; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:54:9 + | +LL | / while i == 0 { +LL | | // never loops +LL | | break; +LL | | } + | |_________^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:66:9 + | +LL | / loop { +LL | | // never loops +LL | | if x == 5 { +LL | | break; +LL | | } +LL | | continue 'outer; +LL | | } + | |_________^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:102:5 + | +LL | / while let Some(y) = x { +LL | | // never loops +LL | | return; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:109:5 + | +LL | / for x in 0..10 { +LL | | // never loops +LL | | match x { +LL | | 1 => break, +LL | | _ => return, +LL | | } +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:157:5 + | +LL | / 'outer: while a { +LL | | // never loops +LL | | while a { +LL | | if a { +... | +LL | | break 'outer; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:172:9 + | +LL | / while false { +LL | | break 'label; +LL | | } + | |_________^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs new file mode 100644 index 0000000000000..2c2d1e275893f --- /dev/null +++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs @@ -0,0 +1,212 @@ +#![warn(clippy::new_ret_no_self)] +#![allow(dead_code)] + +fn main() {} + +trait R { + type Item; +} + +trait Q { + type Item; + type Item2; +} + +struct S; + +impl R for S { + type Item = Self; +} + +impl S { + // should not trigger the lint + pub fn new() -> impl R { + S + } +} + +struct S2; + +impl R for S2 { + type Item = Self; +} + +impl S2 { + // should not trigger the lint + pub fn new(_: String) -> impl R { + S2 + } +} + +struct S3; + +impl R for S3 { + type Item = u32; +} + +impl S3 { + // should trigger the lint + pub fn new(_: String) -> impl R { + S3 + } +} + +struct S4; + +impl Q for S4 { + type Item = u32; + type Item2 = Self; +} + +impl S4 { + // should not trigger the lint + pub fn new(_: String) -> impl Q { + S4 + } +} + +struct T; + +impl T { + // should not trigger lint + pub fn new() -> Self { + unimplemented!(); + } +} + +struct U; + +impl U { + // should trigger lint + pub fn new() -> u32 { + unimplemented!(); + } +} + +struct V; + +impl V { + // should trigger lint + pub fn new(_: String) -> u32 { + unimplemented!(); + } +} + +struct TupleReturnerOk; + +impl TupleReturnerOk { + // should not trigger lint + pub fn new() -> (Self, u32) { + unimplemented!(); + } +} + +struct TupleReturnerOk2; + +impl TupleReturnerOk2 { + // should not trigger lint (it doesn't matter which element in the tuple is Self) + pub fn new() -> (u32, Self) { + unimplemented!(); + } +} + +struct TupleReturnerOk3; + +impl TupleReturnerOk3 { + // should not trigger lint (tuple can contain multiple Self) + pub fn new() -> (Self, Self) { + unimplemented!(); + } +} + +struct TupleReturnerBad; + +impl TupleReturnerBad { + // should trigger lint + pub fn new() -> (u32, u32) { + unimplemented!(); + } +} + +struct MutPointerReturnerOk; + +impl MutPointerReturnerOk { + // should not trigger lint + pub fn new() -> *mut Self { + unimplemented!(); + } +} + +struct MutPointerReturnerOk2; + +impl MutPointerReturnerOk2 { + // should not trigger lint + pub fn new() -> *const Self { + unimplemented!(); + } +} + +struct MutPointerReturnerBad; + +impl MutPointerReturnerBad { + // should trigger lint + pub fn new() -> *mut V { + unimplemented!(); + } +} + +struct GenericReturnerOk; + +impl GenericReturnerOk { + // should not trigger lint + pub fn new() -> Option { + unimplemented!(); + } +} + +struct GenericReturnerBad; + +impl GenericReturnerBad { + // should trigger lint + pub fn new() -> Option { + unimplemented!(); + } +} + +struct NestedReturnerOk; + +impl NestedReturnerOk { + // should not trigger lint + pub fn new() -> (Option, u32) { + unimplemented!(); + } +} + +struct NestedReturnerOk2; + +impl NestedReturnerOk2 { + // should not trigger lint + pub fn new() -> ((Self, u32), u32) { + unimplemented!(); + } +} + +struct NestedReturnerOk3; + +impl NestedReturnerOk3 { + // should not trigger lint + pub fn new() -> Option<(Self, u32)> { + unimplemented!(); + } +} + +struct WithLifetime<'a> { + cat: &'a str, +} + +impl<'a> WithLifetime<'a> { + // should not trigger the lint, because the lifetimes are different + pub fn new<'b: 'a>(s: &'b str) -> WithLifetime<'b> { + unimplemented!(); + } +} diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr new file mode 100644 index 0000000000000..dd5a24bcbe7ae --- /dev/null +++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr @@ -0,0 +1,52 @@ +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:49:5 + | +LL | / pub fn new(_: String) -> impl R { +LL | | S3 +LL | | } + | |_____^ + | + = note: `-D clippy::new-ret-no-self` implied by `-D warnings` + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:81:5 + | +LL | / pub fn new() -> u32 { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:90:5 + | +LL | / pub fn new(_: String) -> u32 { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:126:5 + | +LL | / pub fn new() -> (u32, u32) { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:153:5 + | +LL | / pub fn new() -> *mut V { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:171:5 + | +LL | / pub fn new() -> Option { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs new file mode 100644 index 0000000000000..781ea7bb15283 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -0,0 +1,151 @@ +#![feature(const_fn)] +#![allow(dead_code, clippy::missing_safety_doc)] +#![warn(clippy::new_without_default)] + +pub struct Foo; + +impl Foo { + pub fn new() -> Foo { + Foo + } +} + +pub struct Bar; + +impl Bar { + pub fn new() -> Self { + Bar + } +} + +pub struct Ok; + +impl Ok { + pub fn new() -> Self { + Ok + } +} + +impl Default for Ok { + fn default() -> Self { + Ok + } +} + +pub struct Params; + +impl Params { + pub fn new(_: u32) -> Self { + Params + } +} + +pub struct GenericsOk { + bar: T, +} + +impl Default for GenericsOk { + fn default() -> Self { + unimplemented!(); + } +} + +impl<'c, V> GenericsOk { + pub fn new() -> GenericsOk { + unimplemented!() + } +} + +pub struct LtOk<'a> { + foo: &'a bool, +} + +impl<'b> Default for LtOk<'b> { + fn default() -> Self { + unimplemented!(); + } +} + +impl<'c> LtOk<'c> { + pub fn new() -> LtOk<'c> { + unimplemented!() + } +} + +pub struct LtKo<'a> { + foo: &'a bool, +} + +impl<'c> LtKo<'c> { + pub fn new() -> LtKo<'c> { + unimplemented!() + } + // FIXME: that suggestion is missing lifetimes +} + +struct Private; + +impl Private { + fn new() -> Private { + unimplemented!() + } // We don't lint private items +} + +struct Const; + +impl Const { + pub const fn new() -> Const { + Const + } // const fns can't be implemented via Default +} + +pub struct IgnoreGenericNew; + +impl IgnoreGenericNew { + pub fn new() -> Self { + IgnoreGenericNew + } // the derived Default does not make sense here as the result depends on T +} + +pub trait TraitWithNew: Sized { + fn new() -> Self { + panic!() + } +} + +pub struct IgnoreUnsafeNew; + +impl IgnoreUnsafeNew { + pub unsafe fn new() -> Self { + IgnoreUnsafeNew + } +} + +#[derive(Default)] +pub struct OptionRefWrapper<'a, T>(Option<&'a T>); + +impl<'a, T> OptionRefWrapper<'a, T> { + pub fn new() -> Self { + OptionRefWrapper(None) + } +} + +pub struct Allow(Foo); + +impl Allow { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + unimplemented!() + } +} + +pub struct AllowDerive; + +impl AllowDerive { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/new_without_default.stderr b/src/tools/clippy/tests/ui/new_without_default.stderr new file mode 100644 index 0000000000000..5e485d40663f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_without_default.stderr @@ -0,0 +1,46 @@ +error: you should consider deriving a `Default` implementation for `Foo` + --> $DIR/new_without_default.rs:8:5 + | +LL | / pub fn new() -> Foo { +LL | | Foo +LL | | } + | |_____^ + | + = note: `-D clippy::new-without-default` implied by `-D warnings` +help: try this + | +LL | #[derive(Default)] + | + +error: you should consider deriving a `Default` implementation for `Bar` + --> $DIR/new_without_default.rs:16:5 + | +LL | / pub fn new() -> Self { +LL | | Bar +LL | | } + | |_____^ + | +help: try this + | +LL | #[derive(Default)] + | + +error: you should consider adding a `Default` implementation for `LtKo<'c>` + --> $DIR/new_without_default.rs:80:5 + | +LL | / pub fn new() -> LtKo<'c> { +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for LtKo<'c> { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs new file mode 100644 index 0000000000000..8fbfcb79860a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -0,0 +1,102 @@ +#![feature(box_syntax)] +#![warn(clippy::no_effect)] +#![allow(dead_code)] +#![allow(path_statements)] +#![allow(clippy::deref_addrof)] +#![allow(clippy::redundant_field_names)] +#![feature(untagged_unions)] + +struct Unit; +struct Tuple(i32); +struct Struct { + field: i32, +} +enum Enum { + Tuple(i32), + Struct { field: i32 }, +} +struct DropUnit; +impl Drop for DropUnit { + fn drop(&mut self) {} +} +struct DropStruct { + field: i32, +} +impl Drop for DropStruct { + fn drop(&mut self) {} +} +struct DropTuple(i32); +impl Drop for DropTuple { + fn drop(&mut self) {} +} +enum DropEnum { + Tuple(i32), + Struct { field: i32 }, +} +impl Drop for DropEnum { + fn drop(&mut self) {} +} +struct FooString { + s: String, +} +union Union { + a: u8, + b: f64, +} + +fn get_number() -> i32 { + 0 +} +fn get_struct() -> Struct { + Struct { field: 0 } +} +fn get_drop_struct() -> DropStruct { + DropStruct { field: 0 } +} + +unsafe fn unsafe_fn() -> i32 { + 0 +} + +fn main() { + let s = get_struct(); + let s2 = get_struct(); + + 0; + s2; + Unit; + Tuple(0); + Struct { field: 0 }; + Struct { ..s }; + Union { a: 0 }; + Enum::Tuple(0); + Enum::Struct { field: 0 }; + 5 + 6; + *&42; + &6; + (5, 6, 7); + box 42; + ..; + 5..; + ..5; + 5..6; + 5..=6; + [42, 55]; + [42, 55][1]; + (42, 55).1; + [42; 55]; + [42; 55][13]; + let mut x = 0; + || x += 5; + let s: String = "foo".into(); + FooString { s: s }; + + // Do not warn + get_number(); + unsafe { unsafe_fn() }; + DropUnit; + DropStruct { field: 0 }; + DropTuple(0); + DropEnum::Tuple(0); + DropEnum::Struct { field: 0 }; +} diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr new file mode 100644 index 0000000000000..834b9056e311d --- /dev/null +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -0,0 +1,154 @@ +error: statement with no effect + --> $DIR/no_effect.rs:65:5 + | +LL | 0; + | ^^ + | + = note: `-D clippy::no-effect` implied by `-D warnings` + +error: statement with no effect + --> $DIR/no_effect.rs:66:5 + | +LL | s2; + | ^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:67:5 + | +LL | Unit; + | ^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:68:5 + | +LL | Tuple(0); + | ^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:69:5 + | +LL | Struct { field: 0 }; + | ^^^^^^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:70:5 + | +LL | Struct { ..s }; + | ^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:71:5 + | +LL | Union { a: 0 }; + | ^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:72:5 + | +LL | Enum::Tuple(0); + | ^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:73:5 + | +LL | Enum::Struct { field: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:74:5 + | +LL | 5 + 6; + | ^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:75:5 + | +LL | *&42; + | ^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:76:5 + | +LL | &6; + | ^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:77:5 + | +LL | (5, 6, 7); + | ^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:78:5 + | +LL | box 42; + | ^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:79:5 + | +LL | ..; + | ^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:80:5 + | +LL | 5..; + | ^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:81:5 + | +LL | ..5; + | ^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:82:5 + | +LL | 5..6; + | ^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:84:5 + | +LL | [42, 55]; + | ^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:85:5 + | +LL | [42, 55][1]; + | ^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:86:5 + | +LL | (42, 55).1; + | ^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:87:5 + | +LL | [42; 55]; + | ^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:88:5 + | +LL | [42; 55][13]; + | ^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:90:5 + | +LL | || x += 5; + | ^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:92:5 + | +LL | FooString { s: s }; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/non_expressive_names.rs b/src/tools/clippy/tests/ui/non_expressive_names.rs new file mode 100644 index 0000000000000..58415b4aede26 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_expressive_names.rs @@ -0,0 +1,56 @@ +#![warn(clippy::all)] +#![allow(unused, clippy::println_empty_string)] + +#[derive(Clone, Debug)] +enum MaybeInst { + Split, + Split1(usize), + Split2(usize), +} + +struct InstSplit { + uiae: usize, +} + +impl MaybeInst { + fn fill(&mut self) { + let filled = match *self { + MaybeInst::Split1(goto1) => panic!(1), + MaybeInst::Split2(goto2) => panic!(2), + _ => unimplemented!(), + }; + unimplemented!() + } +} + +fn underscores_and_numbers() { + let _1 = 1; //~ERROR Consider a more descriptive name + let ____1 = 1; //~ERROR Consider a more descriptive name + let __1___2 = 12; //~ERROR Consider a more descriptive name + let _1_ok = 1; +} + +fn issue2927() { + let args = 1; + format!("{:?}", 2); +} + +fn issue3078() { + match "a" { + stringify!(a) => {}, + _ => {}, + } +} + +struct Bar; + +impl Bar { + fn bar() { + let _1 = 1; + let ____1 = 1; + let __1___2 = 12; + let _1_ok = 1; + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stderr b/src/tools/clippy/tests/ui/non_expressive_names.stderr new file mode 100644 index 0000000000000..a0ca46f0efc60 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_expressive_names.stderr @@ -0,0 +1,40 @@ +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:27:9 + | +LL | let _1 = 1; //~ERROR Consider a more descriptive name + | ^^ + | + = note: `-D clippy::just-underscores-and-digits` implied by `-D warnings` + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:28:9 + | +LL | let ____1 = 1; //~ERROR Consider a more descriptive name + | ^^^^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:29:9 + | +LL | let __1___2 = 12; //~ERROR Consider a more descriptive name + | ^^^^^^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:49:13 + | +LL | let _1 = 1; + | ^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:50:13 + | +LL | let ____1 = 1; + | ^^^^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:51:13 + | +LL | let __1___2 = 12; + | ^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stdout b/src/tools/clippy/tests/ui/non_expressive_names.stdout new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs new file mode 100644 index 0000000000000..7ea154cb9b018 --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -0,0 +1,52 @@ +#![allow(unused, clippy::many_single_char_names)] +#![warn(clippy::nonminimal_bool)] + +fn main() { + let a: bool = unimplemented!(); + let b: bool = unimplemented!(); + let c: bool = unimplemented!(); + let d: bool = unimplemented!(); + let e: bool = unimplemented!(); + let _ = !true; + let _ = !false; + let _ = !!a; + let _ = false || a; + // don't lint on cfgs + let _ = cfg!(you_shall_not_not_pass) && a; + let _ = a || !b || !c || !d || !e; + let _ = !(!a && b); + let _ = !(!a || b); + let _ = !a && !(b && c); +} + +fn equality_stuff() { + let a: i32 = unimplemented!(); + let b: i32 = unimplemented!(); + let c: i32 = unimplemented!(); + let d: i32 = unimplemented!(); + let _ = a == b && c == 5 && a == b; + let _ = a == b || c == 5 || a == b; + let _ = a == b && c == 5 && b == a; + let _ = a != b || !(a != b || c == d); + let _ = a != b && !(a != b && c == d); +} + +fn issue3847(a: u32, b: u32) -> bool { + const THRESHOLD: u32 = 1_000; + + if a < THRESHOLD && b >= THRESHOLD || a >= THRESHOLD && b < THRESHOLD { + return false; + } + true +} + +fn issue4548() { + fn f(_i: u32, _j: u32) -> u32 { + unimplemented!(); + } + + let i = 0; + let j = 0; + + if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {} +} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr new file mode 100644 index 0000000000000..d34d106cb2fbb --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr @@ -0,0 +1,111 @@ +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:10:13 + | +LL | let _ = !true; + | ^^^^^ help: try: `false` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:11:13 + | +LL | let _ = !false; + | ^^^^^^ help: try: `true` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:12:13 + | +LL | let _ = !!a; + | ^^^ help: try: `a` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:13:13 + | +LL | let _ = false || a; + | ^^^^^^^^^^ help: try: `a` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:17:13 + | +LL | let _ = !(!a && b); + | ^^^^^^^^^^ help: try: `a || !b` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:18:13 + | +LL | let _ = !(!a || b); + | ^^^^^^^^^^ help: try: `a && !b` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:19:13 + | +LL | let _ = !a && !(b && c); + | ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:27:13 + | +LL | let _ = a == b && c == 5 && a == b; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a == b && c == 5; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a != b || c != 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:28:13 + | +LL | let _ = a == b || c == 5 || a == b; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a == b || c == 5; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a != b && c != 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:29:13 + | +LL | let _ = a == b && c == 5 && b == a; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a == b && c == 5; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a != b || c != 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:30:13 + | +LL | let _ = a != b || !(a != b || c == d); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a != b || c != d; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a == b && c == d); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:31:13 + | +LL | let _ = a != b && !(a != b && c == d); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a != b && c != d; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a == b || c == d); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs new file mode 100644 index 0000000000000..4de48cd0879a1 --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs @@ -0,0 +1,110 @@ +#![allow(unused, clippy::many_single_char_names)] +#![warn(clippy::nonminimal_bool)] + +fn methods_with_negation() { + let a: Option = unimplemented!(); + let b: Result = unimplemented!(); + let _ = a.is_some(); + let _ = !a.is_some(); + let _ = a.is_none(); + let _ = !a.is_none(); + let _ = b.is_err(); + let _ = !b.is_err(); + let _ = b.is_ok(); + let _ = !b.is_ok(); + let c = false; + let _ = !(a.is_some() && !c); + let _ = !(a.is_some() || !c); + let _ = !(!c ^ c) || !a.is_some(); + let _ = (!c ^ c) || !a.is_some(); + let _ = !c ^ c || !a.is_some(); +} + +// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638 +// clippy::nonminimal_bool should only check the built-in Result and Some type, not +// any other types like the following. +enum CustomResultOk { + Ok, + Err(E), +} +enum CustomResultErr { + Ok, + Err(E), +} +enum CustomSomeSome { + Some(T), + None, +} +enum CustomSomeNone { + Some(T), + None, +} + +impl CustomResultOk { + pub fn is_ok(&self) -> bool { + true + } +} + +impl CustomResultErr { + pub fn is_err(&self) -> bool { + true + } +} + +impl CustomSomeSome { + pub fn is_some(&self) -> bool { + true + } +} + +impl CustomSomeNone { + pub fn is_none(&self) -> bool { + true + } +} + +fn dont_warn_for_custom_methods_with_negation() { + let res = CustomResultOk::Err("Error"); + // Should not warn and suggest 'is_err()' because the type does not + // implement is_err(). + if !res.is_ok() {} + + let res = CustomResultErr::Err("Error"); + // Should not warn and suggest 'is_ok()' because the type does not + // implement is_ok(). + if !res.is_err() {} + + let res = CustomSomeSome::Some("thing"); + // Should not warn and suggest 'is_none()' because the type does not + // implement is_none(). + if !res.is_some() {} + + let res = CustomSomeNone::Some("thing"); + // Should not warn and suggest 'is_some()' because the type does not + // implement is_some(). + if !res.is_none() {} +} + +// Only Built-in Result and Some types should suggest the negated alternative +fn warn_for_built_in_methods_with_negation() { + let res: Result = Ok(1); + if !res.is_ok() {} + if !res.is_err() {} + + let res = Some(1); + if !res.is_some() {} + if !res.is_none() {} +} + +#[allow(clippy::neg_cmp_op_on_partial_ord)] +fn dont_warn_for_negated_partial_ord_comparison() { + let a: f64 = unimplemented!(); + let b: f64 = unimplemented!(); + let _ = !(a < b); + let _ = !(a <= b); + let _ = !(a > b); + let _ = !(a >= b); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr new file mode 100644 index 0000000000000..a2df889d62302 --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr @@ -0,0 +1,82 @@ +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:8:13 + | +LL | let _ = !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:10:13 + | +LL | let _ = !a.is_none(); + | ^^^^^^^^^^^^ help: try: `a.is_some()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:12:13 + | +LL | let _ = !b.is_err(); + | ^^^^^^^^^^^ help: try: `b.is_ok()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:14:13 + | +LL | let _ = !b.is_ok(); + | ^^^^^^^^^^ help: try: `b.is_err()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:16:13 + | +LL | let _ = !(a.is_some() && !c); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:17:13 + | +LL | let _ = !(a.is_some() || !c); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:18:26 + | +LL | let _ = !(!c ^ c) || !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:19:25 + | +LL | let _ = (!c ^ c) || !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:20:23 + | +LL | let _ = !c ^ c || !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:92:8 + | +LL | if !res.is_ok() {} + | ^^^^^^^^^^^^ help: try: `res.is_err()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:93:8 + | +LL | if !res.is_err() {} + | ^^^^^^^^^^^^^ help: try: `res.is_ok()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:96:8 + | +LL | if !res.is_some() {} + | ^^^^^^^^^^^^^^ help: try: `res.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:97:8 + | +LL | if !res.is_none() {} + | ^^^^^^^^^^^^^^ help: try: `res.is_some()` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/ok_expect.rs b/src/tools/clippy/tests/ui/ok_expect.rs new file mode 100644 index 0000000000000..ff68d38c73bf5 --- /dev/null +++ b/src/tools/clippy/tests/ui/ok_expect.rs @@ -0,0 +1,27 @@ +use std::io; + +struct MyError(()); // doesn't implement Debug + +#[derive(Debug)] +struct MyErrorWithParam { + x: T, +} + +fn main() { + let res: Result = Ok(0); + let _ = res.unwrap(); + + res.ok().expect("disaster!"); + // the following should not warn, since `expect` isn't implemented unless + // the error type implements `Debug` + let res2: Result = Ok(0); + res2.ok().expect("oh noes!"); + let res3: Result> = Ok(0); + res3.ok().expect("whoof"); + let res4: Result = Ok(0); + res4.ok().expect("argh"); + let res5: io::Result = Ok(0); + res5.ok().expect("oops"); + let res6: Result = Ok(0); + res6.ok().expect("meh"); +} diff --git a/src/tools/clippy/tests/ui/ok_expect.stderr b/src/tools/clippy/tests/ui/ok_expect.stderr new file mode 100644 index 0000000000000..b02b28e7f68c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/ok_expect.stderr @@ -0,0 +1,43 @@ +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:14:5 + | +LL | res.ok().expect("disaster!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::ok-expect` implied by `-D warnings` + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:20:5 + | +LL | res3.ok().expect("whoof"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:22:5 + | +LL | res4.ok().expect("argh"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:24:5 + | +LL | res5.ok().expect("oops"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:26:5 + | +LL | res6.ok().expect("meh"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs new file mode 100644 index 0000000000000..6605c967c8e7e --- /dev/null +++ b/src/tools/clippy/tests/ui/op_ref.rs @@ -0,0 +1,58 @@ +#![allow(unused_variables, clippy::blacklisted_name)] +#![warn(clippy::op_ref)] +#![allow(clippy::many_single_char_names)] +use std::collections::HashSet; +use std::ops::BitAnd; + +fn main() { + let tracked_fds: HashSet = HashSet::new(); + let new_fds = HashSet::new(); + let unwanted = &tracked_fds - &new_fds; + + let foo = &5 - &6; + + let bar = String::new(); + let bar = "foo" == &bar; + + let a = "a".to_string(); + let b = "a"; + + if b < &a { + println!("OK"); + } + + struct X(i32); + impl BitAnd for X { + type Output = X; + fn bitand(self, rhs: X) -> X { + X(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a X> for X { + type Output = X; + fn bitand(self, rhs: &'a X) -> X { + X(self.0 & rhs.0) + } + } + let x = X(1); + let y = X(2); + let z = x & &y; + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & &y; +} diff --git a/src/tools/clippy/tests/ui/op_ref.stderr b/src/tools/clippy/tests/ui/op_ref.stderr new file mode 100644 index 0000000000000..a3a9adcc48355 --- /dev/null +++ b/src/tools/clippy/tests/ui/op_ref.stderr @@ -0,0 +1,22 @@ +error: needlessly taken reference of both operands + --> $DIR/op_ref.rs:12:15 + | +LL | let foo = &5 - &6; + | ^^^^^^^ + | + = note: `-D clippy::op-ref` implied by `-D warnings` +help: use the values directly + | +LL | let foo = 5 - 6; + | ^ ^ + +error: taken reference of right operand + --> $DIR/op_ref.rs:57:13 + | +LL | let z = x & &y; + | ^^^^-- + | | + | help: use the right value directly: `y` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/open_options.rs b/src/tools/clippy/tests/ui/open_options.rs new file mode 100644 index 0000000000000..9063fafbcd040 --- /dev/null +++ b/src/tools/clippy/tests/ui/open_options.rs @@ -0,0 +1,14 @@ +use std::fs::OpenOptions; + +#[allow(unused_must_use)] +#[warn(clippy::nonsensical_open_options)] +fn main() { + OpenOptions::new().read(true).truncate(true).open("foo.txt"); + OpenOptions::new().append(true).truncate(true).open("foo.txt"); + + OpenOptions::new().read(true).read(false).open("foo.txt"); + OpenOptions::new().create(true).create(false).open("foo.txt"); + OpenOptions::new().write(true).write(false).open("foo.txt"); + OpenOptions::new().append(true).append(false).open("foo.txt"); + OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); +} diff --git a/src/tools/clippy/tests/ui/open_options.stderr b/src/tools/clippy/tests/ui/open_options.stderr new file mode 100644 index 0000000000000..26fe9f6fb206f --- /dev/null +++ b/src/tools/clippy/tests/ui/open_options.stderr @@ -0,0 +1,46 @@ +error: file opened with `truncate` and `read` + --> $DIR/open_options.rs:6:5 + | +LL | OpenOptions::new().read(true).truncate(true).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::nonsensical-open-options` implied by `-D warnings` + +error: file opened with `append` and `truncate` + --> $DIR/open_options.rs:7:5 + | +LL | OpenOptions::new().append(true).truncate(true).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `read` is called more than once + --> $DIR/open_options.rs:9:5 + | +LL | OpenOptions::new().read(true).read(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `create` is called more than once + --> $DIR/open_options.rs:10:5 + | +LL | OpenOptions::new().create(true).create(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `write` is called more than once + --> $DIR/open_options.rs:11:5 + | +LL | OpenOptions::new().write(true).write(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `append` is called more than once + --> $DIR/open_options.rs:12:5 + | +LL | OpenOptions::new().append(true).append(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `truncate` is called more than once + --> $DIR/open_options.rs:13:5 + | +LL | OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed new file mode 100644 index 0000000000000..076692e644517 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#![allow(unused_imports, clippy::redundant_clone)] +#![warn(clippy::option_as_ref_deref)] + +use std::ffi::{CString, OsString}; +use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; + +fn main() { + let mut opt = Some(String::from("123")); + + let _ = opt.clone().as_deref().map(str::len); + + #[rustfmt::skip] + let _ = opt.clone().as_deref() + .map(str::len); + + let _ = opt.as_deref_mut(); + + let _ = opt.as_deref(); + let _ = opt.as_deref(); + let _ = opt.as_deref_mut(); + let _ = opt.as_deref_mut(); + let _ = Some(CString::new(vec![]).unwrap()).as_deref(); + let _ = Some(OsString::new()).as_deref(); + let _ = Some(PathBuf::new()).as_deref(); + let _ = Some(Vec::<()>::new()).as_deref(); + let _ = Some(Vec::<()>::new()).as_deref_mut(); + + let _ = opt.as_deref(); + let _ = opt.clone().as_deref_mut().map(|x| x.len()); + + let vc = vec![String::new()]; + let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted + + let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted + + let _ = opt.as_deref(); + let _ = opt.as_deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.rs b/src/tools/clippy/tests/ui/option_as_ref_deref.rs new file mode 100644 index 0000000000000..3bf5f715f8339 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.rs @@ -0,0 +1,44 @@ +// run-rustfix + +#![allow(unused_imports, clippy::redundant_clone)] +#![warn(clippy::option_as_ref_deref)] + +use std::ffi::{CString, OsString}; +use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; + +fn main() { + let mut opt = Some(String::from("123")); + + let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); + + #[rustfmt::skip] + let _ = opt.clone() + .as_ref().map( + Deref::deref + ) + .map(str::len); + + let _ = opt.as_mut().map(DerefMut::deref_mut); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); + let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); + let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); + let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); + let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); + let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); + + let _ = opt.as_ref().map(|x| x.deref()); + let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); + + let vc = vec![String::new()]; + let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted + + let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted + + let _ = opt.as_ref().map(|x| &**x); + let _ = opt.as_mut().map(|x| &mut **x); +} diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr new file mode 100644 index 0000000000000..a106582a63323 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr @@ -0,0 +1,104 @@ +error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead + --> $DIR/option_as_ref_deref.rs:13:13 + | +LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` + | + = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` + +error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead + --> $DIR/option_as_ref_deref.rs:16:13 + | +LL | let _ = opt.clone() + | _____________^ +LL | | .as_ref().map( +LL | | Deref::deref +LL | | ) + | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` + +error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:22:13 + | +LL | let _ = opt.as_mut().map(DerefMut::deref_mut); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:24:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:25:13 + | +LL | let _ = opt.as_ref().map(|x| x.as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:26:13 + | +LL | let _ = opt.as_mut().map(String::as_mut_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:27:13 + | +LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:28:13 + | +LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` + +error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:29:13 + | +LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` + +error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:30:13 + | +LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` + +error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:31:13 + | +LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` + +error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:32:13 + | +LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` + +error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:34:13 + | +LL | let _ = opt.as_ref().map(|x| x.deref()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:35:13 + | +LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` + +error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:42:13 + | +LL | let _ = opt.as_ref().map(|x| &**x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:43:13 + | +LL | let _ = opt.as_mut().map(|x| &mut **x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.rs b/src/tools/clippy/tests/ui/option_env_unwrap.rs new file mode 100644 index 0000000000000..642c77460a340 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_env_unwrap.rs @@ -0,0 +1,23 @@ +// aux-build:macro_rules.rs +#![warn(clippy::option_env_unwrap)] + +#[macro_use] +extern crate macro_rules; + +macro_rules! option_env_unwrap { + ($env: expr) => { + option_env!($env).unwrap() + }; + ($env: expr, $message: expr) => { + option_env!($env).expect($message) + }; +} + +fn main() { + let _ = option_env!("PATH").unwrap(); + let _ = option_env!("PATH").expect("environment variable PATH isn't set"); + let _ = option_env_unwrap!("PATH"); + let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set"); + let _ = option_env_unwrap_external!("PATH"); + let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set"); +} diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.stderr b/src/tools/clippy/tests/ui/option_env_unwrap.stderr new file mode 100644 index 0000000000000..8de9c8a9d29e0 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_env_unwrap.stderr @@ -0,0 +1,61 @@ +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:17:13 + | +LL | let _ = option_env!("PATH").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::option-env-unwrap` implied by `-D warnings` + = help: consider using the `env!` macro instead + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:18:13 + | +LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `env!` macro instead + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:9:9 + | +LL | option_env!($env).unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | let _ = option_env_unwrap!("PATH"); + | -------------------------- in this macro invocation + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:12:9 + | +LL | option_env!($env).expect($message) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set"); + | ----------------------------------------------------------------- in this macro invocation + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:21:13 + | +LL | let _ = option_env_unwrap_external!("PATH"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:22:13 + | +LL | let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/option_map_or_none.fixed b/src/tools/clippy/tests/ui/option_map_or_none.fixed new file mode 100644 index 0000000000000..d80c3c7c1b722 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_or_none.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(clippy::bind_instead_of_map)] + +fn main() { + let opt = Some(1); + + // Check `OPTION_MAP_OR_NONE`. + // Single line case. + let _ = opt.and_then(|x| Some(x + 1)); + // Multi-line case. + #[rustfmt::skip] + let _ = opt.and_then(|x| { + Some(x + 1) + }); +} diff --git a/src/tools/clippy/tests/ui/option_map_or_none.rs b/src/tools/clippy/tests/ui/option_map_or_none.rs new file mode 100644 index 0000000000000..629842419e546 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_or_none.rs @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(clippy::bind_instead_of_map)] + +fn main() { + let opt = Some(1); + + // Check `OPTION_MAP_OR_NONE`. + // Single line case. + let _ = opt.map_or(None, |x| Some(x + 1)); + // Multi-line case. + #[rustfmt::skip] + let _ = opt.map_or(None, |x| { + Some(x + 1) + }); +} diff --git a/src/tools/clippy/tests/ui/option_map_or_none.stderr b/src/tools/clippy/tests/ui/option_map_or_none.stderr new file mode 100644 index 0000000000000..6f707987dbcaa --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_or_none.stderr @@ -0,0 +1,26 @@ +error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/option_map_or_none.rs:10:13 + | +LL | let _ = opt.map_or(None, |x| Some(x + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(|x| Some(x + 1))` + | + = note: `-D clippy::option-map-or-none` implied by `-D warnings` + +error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/option_map_or_none.rs:13:13 + | +LL | let _ = opt.map_or(None, |x| { + | _____________^ +LL | | Some(x + 1) +LL | | }); + | |_________________________^ + | +help: try using `and_then` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +LL | }); + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed new file mode 100644 index 0000000000000..9a0da404cb6db --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed @@ -0,0 +1,84 @@ +// run-rustfix + +#![warn(clippy::option_map_unit_fn)] +#![allow(unused)] + +fn do_nothing(_: T) {} + +fn diverge(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +fn option() -> Option { + Some(10) +} + +struct HasOption { + field: Option, +} + +impl HasOption { + fn do_option_nothing(self: &Self, value: usize) {} + + fn do_option_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} +#[rustfmt::skip] +fn option_map_unit_fn() { + let x = HasOption { field: Some(10) }; + + x.field.map(plus_one); + let _ : Option<()> = x.field.map(do_nothing); + + if let Some(x_field) = x.field { do_nothing(x_field) } + + if let Some(x_field) = x.field { do_nothing(x_field) } + + if let Some(x_field) = x.field { diverge(x_field) } + + let captured = 10; + if let Some(value) = x.field { do_nothing(value + captured) }; + let _ : Option<()> = x.field.map(|value| do_nothing(value + captured)); + + if let Some(value) = x.field { x.do_option_nothing(value + captured) } + + if let Some(value) = x.field { x.do_option_plus_one(value + captured); } + + + if let Some(value) = x.field { do_nothing(value + captured) } + + if let Some(value) = x.field { do_nothing(value + captured) } + + if let Some(value) = x.field { do_nothing(value + captured); } + + if let Some(value) = x.field { do_nothing(value + captured); } + + + if let Some(value) = x.field { diverge(value + captured) } + + if let Some(value) = x.field { diverge(value + captured) } + + if let Some(value) = x.field { diverge(value + captured); } + + if let Some(value) = x.field { diverge(value + captured); } + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + if let Some(value) = x.field { let y = plus_one(value + captured); } + + if let Some(value) = x.field { plus_one(value + captured); } + + if let Some(value) = x.field { plus_one(value + captured); } + + + if let Some(ref value) = x.field { do_nothing(value + captured) } + + if let Some(a) = option() { do_nothing(a) }} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs new file mode 100644 index 0000000000000..58041b62df35a --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs @@ -0,0 +1,84 @@ +// run-rustfix + +#![warn(clippy::option_map_unit_fn)] +#![allow(unused)] + +fn do_nothing(_: T) {} + +fn diverge(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +fn option() -> Option { + Some(10) +} + +struct HasOption { + field: Option, +} + +impl HasOption { + fn do_option_nothing(self: &Self, value: usize) {} + + fn do_option_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} +#[rustfmt::skip] +fn option_map_unit_fn() { + let x = HasOption { field: Some(10) }; + + x.field.map(plus_one); + let _ : Option<()> = x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(diverge); + + let captured = 10; + if let Some(value) = x.field { do_nothing(value + captured) }; + let _ : Option<()> = x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| x.do_option_nothing(value + captured)); + + x.field.map(|value| { x.do_option_plus_one(value + captured); }); + + + x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| { do_nothing(value + captured) }); + + x.field.map(|value| { do_nothing(value + captured); }); + + x.field.map(|value| { { do_nothing(value + captured); } }); + + + x.field.map(|value| diverge(value + captured)); + + x.field.map(|value| { diverge(value + captured) }); + + x.field.map(|value| { diverge(value + captured); }); + + x.field.map(|value| { { diverge(value + captured); } }); + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + x.field.map(|value| { let y = plus_one(value + captured); }); + + x.field.map(|value| { plus_one(value + captured); }); + + x.field.map(|value| { { plus_one(value + captured); } }); + + + x.field.map(|ref value| { do_nothing(value + captured) }); + + option().map(do_nothing);} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr new file mode 100644 index 0000000000000..1312c70b6d592 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr @@ -0,0 +1,148 @@ +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:38:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | + = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:40:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:42:5 + | +LL | x.field.map(diverge); + | ^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:48:5 + | +LL | x.field.map(|value| x.do_option_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:50:5 + | +LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:53:5 + | +LL | x.field.map(|value| do_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:55:5 + | +LL | x.field.map(|value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:57:5 + | +LL | x.field.map(|value| { do_nothing(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:59:5 + | +LL | x.field.map(|value| { { do_nothing(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:62:5 + | +LL | x.field.map(|value| diverge(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:64:5 + | +LL | x.field.map(|value| { diverge(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:66:5 + | +LL | x.field.map(|value| { diverge(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:68:5 + | +LL | x.field.map(|value| { { diverge(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:73:5 + | +LL | x.field.map(|value| { let y = plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:75:5 + | +LL | x.field.map(|value| { plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:77:5 + | +LL | x.field.map(|value| { { plus_one(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:80:5 + | +LL | x.field.map(|ref value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:82:5 + | +LL | option().map(do_nothing);} + | ^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(a) = option() { do_nothing(a) }` + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs new file mode 100644 index 0000000000000..20e6c15b18d5f --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs @@ -0,0 +1,39 @@ +#![warn(clippy::option_map_unit_fn)] +#![allow(unused)] + +fn do_nothing(_: T) {} + +fn diverge(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +#[rustfmt::skip] +fn option_map_unit_fn() { + + x.field.map(|value| { do_nothing(value); do_nothing(value) }); + + x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + + // Suggestion for the let block should be `{ ... }` as it's too difficult to build a + // proper suggestion for these cases + x.field.map(|value| { + do_nothing(value); + do_nothing(value) + }); + x.field.map(|value| { do_nothing(value); do_nothing(value); }); + + // The following should suggest `if let Some(_X) ...` as it's difficult to generate a proper let variable name for them + Some(42).map(diverge); + "12".parse::().ok().map(diverge); + Some(plus_one(1)).map(do_nothing); + + // Should suggest `if let Some(_y) ...` to not override the existing foo variable + let y = Some(42); + y.map(do_nothing); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr new file mode 100644 index 0000000000000..a53f5889c58da --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr @@ -0,0 +1,27 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:17:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:19:5 + | +LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:23:5 + | +LL | x.field.map(|value| { + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:27:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); + | ^ not found in this scope + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/tools/clippy/tests/ui/option_option.rs b/src/tools/clippy/tests/ui/option_option.rs new file mode 100644 index 0000000000000..904c50e14039a --- /dev/null +++ b/src/tools/clippy/tests/ui/option_option.rs @@ -0,0 +1,62 @@ +#![deny(clippy::option_option)] + +fn input(_: Option>) {} + +fn output() -> Option> { + None +} + +fn output_nested() -> Vec>> { + vec![None] +} + +// The lint only generates one warning for this +fn output_nested_nested() -> Option>> { + None +} + +struct Struct { + x: Option>, +} + +impl Struct { + fn struct_fn() -> Option> { + None + } +} + +trait Trait { + fn trait_fn() -> Option>; +} + +enum Enum { + Tuple(Option>), + Struct { x: Option> }, +} + +// The lint allows this +type OptionOption = Option>; + +// The lint allows this +fn output_type_alias() -> OptionOption { + None +} + +// The line allows this +impl Trait for Struct { + fn trait_fn() -> Option> { + None + } +} + +fn main() { + input(None); + output(); + output_nested(); + + // The lint allows this + let local: Option> = None; + + // The lint allows this + let expr = Some(Some(true)); +} diff --git a/src/tools/clippy/tests/ui/option_option.stderr b/src/tools/clippy/tests/ui/option_option.stderr new file mode 100644 index 0000000000000..79db186d7ea77 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_option.stderr @@ -0,0 +1,62 @@ +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:3:13 + | +LL | fn input(_: Option>) {} + | ^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/option_option.rs:1:9 + | +LL | #![deny(clippy::option_option)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:5:16 + | +LL | fn output() -> Option> { + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:9:27 + | +LL | fn output_nested() -> Vec>> { + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:14:30 + | +LL | fn output_nested_nested() -> Option>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:19:8 + | +LL | x: Option>, + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:23:23 + | +LL | fn struct_fn() -> Option> { + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:29:22 + | +LL | fn trait_fn() -> Option>; + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:33:11 + | +LL | Tuple(Option>), + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:34:17 + | +LL | Struct { x: Option> }, + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed new file mode 100644 index 0000000000000..8ea03fe42616c --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -0,0 +1,110 @@ +// run-rustfix + +#![warn(clippy::or_fun_call)] +#![allow(dead_code)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::time::Duration; + +/// Checks implementation of the `OR_FUN_CALL` lint. +fn or_fun_call() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + } + + enum Enum { + A(i32), + } + + fn make() -> T { + unimplemented!(); + } + + let with_enum = Some(Enum::A(1)); + with_enum.unwrap_or(Enum::A(5)); + + let with_const_fn = Some(Duration::from_secs(1)); + with_const_fn.unwrap_or(Duration::from_secs(5)); + + let with_constructor = Some(vec![1]); + with_constructor.unwrap_or_else(make); + + let with_new = Some(vec![1]); + with_new.unwrap_or_default(); + + let with_const_args = Some(vec![1]); + with_const_args.unwrap_or_else(|| Vec::with_capacity(12)); + + let with_err: Result<_, ()> = Ok(vec![1]); + with_err.unwrap_or_else(|_| make()); + + let with_err_args: Result<_, ()> = Ok(vec![1]); + with_err_args.unwrap_or_else(|_| Vec::with_capacity(12)); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or_default(); + + let with_default_type = Some(1); + with_default_type.unwrap_or_default(); + + let with_vec = Some(vec![1]); + with_vec.unwrap_or_default(); + + let without_default = Some(Foo); + without_default.unwrap_or_else(Foo::new); + + let mut map = HashMap::::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert_with(String::new); + + let stringy = Some(String::from("")); + let _ = stringy.unwrap_or_else(|| "".to_owned()); + + let opt = Some(1); + let hello = "Hello"; + let _ = opt.ok_or(format!("{} world.", hello)); +} + +struct Foo(u8); +struct Bar(String, Duration); +#[rustfmt::skip] +fn test_or_with_ctors() { + let opt = Some(1); + let opt_opt = Some(Some(1)); + // we also test for const promotion, this makes sure we don't hit that + let two = 2; + + let _ = opt_opt.unwrap_or(Some(2)); + let _ = opt_opt.unwrap_or(Some(two)); + let _ = opt.ok_or(Some(2)); + let _ = opt.ok_or(Some(two)); + let _ = opt.ok_or(Foo(2)); + let _ = opt.ok_or(Foo(two)); + let _ = opt.or(Some(2)); + let _ = opt.or(Some(two)); + + let _ = Some("a".to_string()).or_else(|| Some("b".to_string())); + + let b = "b".to_string(); + let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) + .or(Some(Bar(b, Duration::from_secs(2)))); +} + +// Issue 4514 - early return +fn f() -> Option<()> { + let a = Some(1); + let b = 1i32; + + let _ = a.unwrap_or(b.checked_mul(3)?.min(240)); + + Some(()) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs new file mode 100644 index 0000000000000..7599b945a9137 --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -0,0 +1,110 @@ +// run-rustfix + +#![warn(clippy::or_fun_call)] +#![allow(dead_code)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::time::Duration; + +/// Checks implementation of the `OR_FUN_CALL` lint. +fn or_fun_call() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + } + + enum Enum { + A(i32), + } + + fn make() -> T { + unimplemented!(); + } + + let with_enum = Some(Enum::A(1)); + with_enum.unwrap_or(Enum::A(5)); + + let with_const_fn = Some(Duration::from_secs(1)); + with_const_fn.unwrap_or(Duration::from_secs(5)); + + let with_constructor = Some(vec![1]); + with_constructor.unwrap_or(make()); + + let with_new = Some(vec![1]); + with_new.unwrap_or(Vec::new()); + + let with_const_args = Some(vec![1]); + with_const_args.unwrap_or(Vec::with_capacity(12)); + + let with_err: Result<_, ()> = Ok(vec![1]); + with_err.unwrap_or(make()); + + let with_err_args: Result<_, ()> = Ok(vec![1]); + with_err_args.unwrap_or(Vec::with_capacity(12)); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or(Default::default()); + + let with_default_type = Some(1); + with_default_type.unwrap_or(u64::default()); + + let with_vec = Some(vec![1]); + with_vec.unwrap_or(vec![]); + + let without_default = Some(Foo); + without_default.unwrap_or(Foo::new()); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); + + let stringy = Some(String::from("")); + let _ = stringy.unwrap_or("".to_owned()); + + let opt = Some(1); + let hello = "Hello"; + let _ = opt.ok_or(format!("{} world.", hello)); +} + +struct Foo(u8); +struct Bar(String, Duration); +#[rustfmt::skip] +fn test_or_with_ctors() { + let opt = Some(1); + let opt_opt = Some(Some(1)); + // we also test for const promotion, this makes sure we don't hit that + let two = 2; + + let _ = opt_opt.unwrap_or(Some(2)); + let _ = opt_opt.unwrap_or(Some(two)); + let _ = opt.ok_or(Some(2)); + let _ = opt.ok_or(Some(two)); + let _ = opt.ok_or(Foo(2)); + let _ = opt.ok_or(Foo(two)); + let _ = opt.or(Some(2)); + let _ = opt.or(Some(two)); + + let _ = Some("a".to_string()).or(Some("b".to_string())); + + let b = "b".to_string(); + let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) + .or(Some(Bar(b, Duration::from_secs(2)))); +} + +// Issue 4514 - early return +fn f() -> Option<()> { + let a = Some(1); + let b = 1i32; + + let _ = a.unwrap_or(b.checked_mul(3)?.min(240)); + + Some(()) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr new file mode 100644 index 0000000000000..96d55771e6cef --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -0,0 +1,82 @@ +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:35:22 + | +LL | with_constructor.unwrap_or(make()); + | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:38:5 + | +LL | with_new.unwrap_or(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:41:21 + | +LL | with_const_args.unwrap_or(Vec::with_capacity(12)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:44:14 + | +LL | with_err.unwrap_or(make()); + | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:47:19 + | +LL | with_err_args.unwrap_or(Vec::with_capacity(12)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` + +error: use of `unwrap_or` followed by a call to `default` + --> $DIR/or_fun_call.rs:50:5 + | +LL | with_default_trait.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()` + +error: use of `unwrap_or` followed by a call to `default` + --> $DIR/or_fun_call.rs:53:5 + | +LL | with_default_type.unwrap_or(u64::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()` + +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:56:5 + | +LL | with_vec.unwrap_or(vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:59:21 + | +LL | without_default.unwrap_or(Foo::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:62:19 + | +LL | map.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:65:21 + | +LL | btree.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:68:21 + | +LL | let _ = stringy.unwrap_or("".to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` + +error: use of `or` followed by a function call + --> $DIR/or_fun_call.rs:93:35 + | +LL | let _ = Some("a".to_string()).or(Some("b".to_string())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs new file mode 100644 index 0000000000000..f20a0ede1137c --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs @@ -0,0 +1,11 @@ +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, const_err)] + +fn main() { + let x = [1, 2, 3, 4]; + + // issue 3102 + let num = 1; + &x[num..10]; // should trigger out of bounds error + &x[10..num]; // should trigger out of bounds error +} diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr new file mode 100644 index 0000000000000..516c1df40be0a --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr @@ -0,0 +1,16 @@ +error: range is out of bounds + --> $DIR/issue-3102.rs:9:13 + | +LL | &x[num..10]; // should trigger out of bounds error + | ^^ + | + = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` + +error: range is out of bounds + --> $DIR/issue-3102.rs:10:8 + | +LL | &x[10..num]; // should trigger out of bounds error + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs new file mode 100644 index 0000000000000..590e578d758ea --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs @@ -0,0 +1,22 @@ +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)] + +fn main() { + let x = [1, 2, 3, 4]; + + &x[..=4]; + &x[1..5]; + &x[5..]; + &x[..5]; + &x[5..].iter().map(|x| 2 * x).collect::>(); + &x[0..=4]; + + &x[4..]; // Ok, should not produce stderr. + &x[..4]; // Ok, should not produce stderr. + &x[..]; // Ok, should not produce stderr. + &x[1..]; // Ok, should not produce stderr. + &x[2..].iter().map(|x| 2 * x).collect::>(); // Ok, should not produce stderr. + + &x[0..].get(..3); // Ok, should not produce stderr. + &x[0..3]; // Ok, should not produce stderr. +} diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr new file mode 100644 index 0000000000000..3d95afcdab233 --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr @@ -0,0 +1,40 @@ +error: range is out of bounds + --> $DIR/simple.rs:7:11 + | +LL | &x[..=4]; + | ^ + | + = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` + +error: range is out of bounds + --> $DIR/simple.rs:8:11 + | +LL | &x[1..5]; + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:9:8 + | +LL | &x[5..]; + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:10:10 + | +LL | &x[..5]; + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:11:8 + | +LL | &x[5..].iter().map(|x| 2 * x).collect::>(); + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:12:12 + | +LL | &x[0..=4]; + | ^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/outer_expn_data.fixed b/src/tools/clippy/tests/ui/outer_expn_data.fixed new file mode 100644 index 0000000000000..999a19b289e18 --- /dev/null +++ b/src/tools/clippy/tests/ui/outer_expn_data.fixed @@ -0,0 +1,28 @@ +// run-rustfix + +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn_data(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/outer_expn_data.rs b/src/tools/clippy/tests/ui/outer_expn_data.rs new file mode 100644 index 0000000000000..5405d475d1acc --- /dev/null +++ b/src/tools/clippy/tests/ui/outer_expn_data.rs @@ -0,0 +1,28 @@ +// run-rustfix + +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn().expn_data(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/outer_expn_data.stderr b/src/tools/clippy/tests/ui/outer_expn_data.stderr new file mode 100644 index 0000000000000..56b6ce1f78ea4 --- /dev/null +++ b/src/tools/clippy/tests/ui/outer_expn_data.stderr @@ -0,0 +1,15 @@ +error: usage of `outer_expn().expn_data()` + --> $DIR/outer_expn_data.rs:24:34 + | +LL | let _ = expr.span.ctxt().outer_expn().expn_data(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()` + | +note: the lint level is defined here + --> $DIR/outer_expn_data.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs new file mode 100644 index 0000000000000..84332040dbadb --- /dev/null +++ b/src/tools/clippy/tests/ui/overflow_check_conditional.rs @@ -0,0 +1,26 @@ +#![allow(clippy::many_single_char_names)] +#![warn(clippy::overflow_check_conditional)] + +fn main() { + let a: u32 = 1; + let b: u32 = 2; + let c: u32 = 3; + if a + b < a {} + if a > a + b {} + if a + b < b {} + if b > a + b {} + if a - b > b {} + if b < a - b {} + if a - b > a {} + if a < a - b {} + if a + b < c {} + if c > a + b {} + if a - b < c {} + if c > a - b {} + let i = 1.1; + let j = 2.2; + if i + j < i {} + if i - j < i {} + if i > i + j {} + if i - j < i {} +} diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr new file mode 100644 index 0000000000000..ad66135d326bd --- /dev/null +++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr @@ -0,0 +1,52 @@ +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:8:8 + | +LL | if a + b < a {} + | ^^^^^^^^^ + | + = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` + +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:9:8 + | +LL | if a > a + b {} + | ^^^^^^^^^ + +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:10:8 + | +LL | if a + b < b {} + | ^^^^^^^^^ + +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:11:8 + | +LL | if b > a + b {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:12:8 + | +LL | if a - b > b {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:13:8 + | +LL | if b < a - b {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:14:8 + | +LL | if a - b > a {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:15:8 + | +LL | if a < a - b {} + | ^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/panic.rs b/src/tools/clippy/tests/ui/panic.rs new file mode 100644 index 0000000000000..6e004aa9a924f --- /dev/null +++ b/src/tools/clippy/tests/ui/panic.rs @@ -0,0 +1,61 @@ +#![warn(clippy::panic_params)] +#![allow(clippy::assertions_on_constants)] +fn missing() { + if true { + panic!("{}"); + } else if false { + panic!("{:?}"); + } else { + assert!(true, "here be missing values: {}"); + } + + panic!("{{{this}}}"); +} + +fn ok_single() { + panic!("foo bar"); +} + +fn ok_inner() { + // Test for #768 + assert!("foo bar".contains(&format!("foo {}", "bar"))); +} + +fn ok_multiple() { + panic!("{}", "This is {ok}"); +} + +fn ok_bracket() { + match 42 { + 1337 => panic!("{so is this"), + 666 => panic!("so is this}"), + _ => panic!("}so is that{"), + } +} + +const ONE: u32 = 1; + +fn ok_nomsg() { + assert!({ 1 == ONE }); + assert!(if 1 == ONE { ONE == 1 } else { false }); +} + +fn ok_escaped() { + panic!("{{ why should this not be ok? }}"); + panic!(" or {{ that ?"); + panic!(" or }} this ?"); + panic!(" {or {{ that ?"); + panic!(" }or }} this ?"); + panic!("{{ test }"); + panic!("{case }}"); +} + +fn main() { + missing(); + ok_single(); + ok_multiple(); + ok_bracket(); + ok_inner(); + ok_nomsg(); + ok_escaped(); +} diff --git a/src/tools/clippy/tests/ui/panic.stderr b/src/tools/clippy/tests/ui/panic.stderr new file mode 100644 index 0000000000000..1f8ff8ccf5575 --- /dev/null +++ b/src/tools/clippy/tests/ui/panic.stderr @@ -0,0 +1,28 @@ +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:5:16 + | +LL | panic!("{}"); + | ^^^^ + | + = note: `-D clippy::panic-params` implied by `-D warnings` + +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:7:16 + | +LL | panic!("{:?}"); + | ^^^^^^ + +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:9:23 + | +LL | assert!(true, "here be missing values: {}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:12:12 + | +LL | panic!("{{{this}}}"); + | ^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs new file mode 100644 index 0000000000000..dabb695368dba --- /dev/null +++ b/src/tools/clippy/tests/ui/panicking_macros.rs @@ -0,0 +1,33 @@ +#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] +#![allow(clippy::assertions_on_constants)] + +fn panic() { + let a = 2; + panic!(); + let b = a + 2; +} + +fn todo() { + let a = 2; + todo!(); + let b = a + 2; +} + +fn unimplemented() { + let a = 2; + unimplemented!(); + let b = a + 2; +} + +fn unreachable() { + let a = 2; + unreachable!(); + let b = a + 2; +} + +fn main() { + panic(); + todo(); + unimplemented(); + unreachable(); +} diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr new file mode 100644 index 0000000000000..72319bc7e4584 --- /dev/null +++ b/src/tools/clippy/tests/ui/panicking_macros.stderr @@ -0,0 +1,34 @@ +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:6:5 + | +LL | panic!(); + | ^^^^^^^^^ + | + = note: `-D clippy::panic` implied by `-D warnings` + +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:12:5 + | +LL | todo!(); + | ^^^^^^^^ + | + = note: `-D clippy::todo` implied by `-D warnings` + +error: `unimplemented` should not be present in production code + --> $DIR/panicking_macros.rs:18:5 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unimplemented` implied by `-D warnings` + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:24:5 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unreachable` implied by `-D warnings` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/partialeq_ne_impl.rs b/src/tools/clippy/tests/ui/partialeq_ne_impl.rs new file mode 100644 index 0000000000000..1338d3c74d554 --- /dev/null +++ b/src/tools/clippy/tests/ui/partialeq_ne_impl.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] + +struct Foo; + +impl PartialEq for Foo { + fn eq(&self, _: &Foo) -> bool { + true + } + fn ne(&self, _: &Foo) -> bool { + false + } +} + +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Bar) -> bool { + true + } + #[allow(clippy::partialeq_ne_impl)] + fn ne(&self, _: &Bar) -> bool { + false + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr new file mode 100644 index 0000000000000..b92da4511b48d --- /dev/null +++ b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr @@ -0,0 +1,12 @@ +error: re-implementing `PartialEq::ne` is unnecessary + --> $DIR/partialeq_ne_impl.rs:9:5 + | +LL | / fn ne(&self, _: &Foo) -> bool { +LL | | false +LL | | } + | |_____^ + | + = note: `-D clippy::partialeq-ne-impl` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed new file mode 100644 index 0000000000000..ef8856830fc99 --- /dev/null +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed @@ -0,0 +1,8 @@ +// run-rustfix +use std::path::PathBuf; + +#[warn(clippy::all, clippy::path_buf_push_overwrite)] +fn main() { + let mut x = PathBuf::from("/foo"); + x.push("bar"); +} diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs new file mode 100644 index 0000000000000..6e2d483f45410 --- /dev/null +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs @@ -0,0 +1,8 @@ +// run-rustfix +use std::path::PathBuf; + +#[warn(clippy::all, clippy::path_buf_push_overwrite)] +fn main() { + let mut x = PathBuf::from("/foo"); + x.push("/bar"); +} diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr new file mode 100644 index 0000000000000..09b18d71baf93 --- /dev/null +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr @@ -0,0 +1,10 @@ +error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition + --> $DIR/path_buf_push_overwrite.rs:7:12 + | +LL | x.push("/bar"); + | ^^^^^^ help: try: `"bar"` + | + = note: `-D clippy::path-buf-push-overwrite` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/patterns.fixed b/src/tools/clippy/tests/ui/patterns.fixed new file mode 100644 index 0000000000000..f22388154499a --- /dev/null +++ b/src/tools/clippy/tests/ui/patterns.fixed @@ -0,0 +1,36 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::all)] + +fn main() { + let v = Some(true); + let s = [0, 1, 2, 3, 4]; + match v { + Some(x) => (), + y => (), + } + match v { + Some(x) => (), + y @ None => (), // no error + } + match s { + [x, inside @ .., y] => (), // no error + [..] => (), + } + + let mut mutv = vec![1, 2, 3]; + + // required "ref" left out in suggestion: #5271 + match mutv { + ref mut x => { + x.push(4); + println!("vec: {:?}", x); + }, + ref y if y == &vec![0] => (), + } + + match mutv { + ref x => println!("vec: {:?}", x), + ref y if y == &vec![0] => (), + } +} diff --git a/src/tools/clippy/tests/ui/patterns.rs b/src/tools/clippy/tests/ui/patterns.rs new file mode 100644 index 0000000000000..5848ecd38d98d --- /dev/null +++ b/src/tools/clippy/tests/ui/patterns.rs @@ -0,0 +1,36 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::all)] + +fn main() { + let v = Some(true); + let s = [0, 1, 2, 3, 4]; + match v { + Some(x) => (), + y @ _ => (), + } + match v { + Some(x) => (), + y @ None => (), // no error + } + match s { + [x, inside @ .., y] => (), // no error + [..] => (), + } + + let mut mutv = vec![1, 2, 3]; + + // required "ref" left out in suggestion: #5271 + match mutv { + ref mut x @ _ => { + x.push(4); + println!("vec: {:?}", x); + }, + ref y if y == &vec![0] => (), + } + + match mutv { + ref x @ _ => println!("vec: {:?}", x), + ref y if y == &vec![0] => (), + } +} diff --git a/src/tools/clippy/tests/ui/patterns.stderr b/src/tools/clippy/tests/ui/patterns.stderr new file mode 100644 index 0000000000000..af067580688b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/patterns.stderr @@ -0,0 +1,22 @@ +error: the `y @ _` pattern can be written as just `y` + --> $DIR/patterns.rs:10:9 + | +LL | y @ _ => (), + | ^^^^^ help: try: `y` + | + = note: `-D clippy::redundant-pattern` implied by `-D warnings` + +error: the `x @ _` pattern can be written as just `x` + --> $DIR/patterns.rs:25:9 + | +LL | ref mut x @ _ => { + | ^^^^^^^^^^^^^ help: try: `ref mut x` + +error: the `x @ _` pattern can be written as just `x` + --> $DIR/patterns.rs:33:9 + | +LL | ref x @ _ => println!("vec: {:?}", x), + | ^^^^^^^^^ help: try: `ref x` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed new file mode 100644 index 0000000000000..17b1f1bd0bf30 --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -0,0 +1,53 @@ +// run-rustfix +#![warn(clippy::precedence)] +#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << (2 + 3); + (1 + 2) << 3; + 4 >> (1 + 1); + (1 + 3) >> 2; + 1 ^ (1 - 1); + 3 | (2 - 1); + 3 & (5 - 2); + -(1i32.abs()); + -(1f32.abs()); + + // These should not trigger an error + let _ = (-1i32).abs(); + let _ = (-1f32).abs(); + let _ = -(1i32).abs(); + let _ = -(1f32).abs(); + let _ = -(1i32.abs()); + let _ = -(1f32.abs()); + + // Odd functions shoud not trigger an error + let _ = -1f64.asin(); + let _ = -1f64.asinh(); + let _ = -1f64.atan(); + let _ = -1f64.atanh(); + let _ = -1f64.cbrt(); + let _ = -1f64.fract(); + let _ = -1f64.round(); + let _ = -1f64.signum(); + let _ = -1f64.sin(); + let _ = -1f64.sinh(); + let _ = -1f64.tan(); + let _ = -1f64.tanh(); + let _ = -1f64.to_degrees(); + let _ = -1f64.to_radians(); + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence.rs b/src/tools/clippy/tests/ui/precedence.rs new file mode 100644 index 0000000000000..2d0891fd3c20c --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence.rs @@ -0,0 +1,53 @@ +// run-rustfix +#![warn(clippy::precedence)] +#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << 2 + 3; + 1 + 2 << 3; + 4 >> 1 + 1; + 1 + 3 >> 2; + 1 ^ 1 - 1; + 3 | 2 - 1; + 3 & 5 - 2; + -1i32.abs(); + -1f32.abs(); + + // These should not trigger an error + let _ = (-1i32).abs(); + let _ = (-1f32).abs(); + let _ = -(1i32).abs(); + let _ = -(1f32).abs(); + let _ = -(1i32.abs()); + let _ = -(1f32.abs()); + + // Odd functions shoud not trigger an error + let _ = -1f64.asin(); + let _ = -1f64.asinh(); + let _ = -1f64.atan(); + let _ = -1f64.atanh(); + let _ = -1f64.cbrt(); + let _ = -1f64.fract(); + let _ = -1f64.round(); + let _ = -1f64.signum(); + let _ = -1f64.sin(); + let _ = -1f64.sinh(); + let _ = -1f64.tan(); + let _ = -1f64.tanh(); + let _ = -1f64.to_degrees(); + let _ = -1f64.to_radians(); + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr new file mode 100644 index 0000000000000..a2ed5392bfc7c --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -0,0 +1,58 @@ +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:17:5 + | +LL | 1 << 2 + 3; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)` + | + = note: `-D clippy::precedence` implied by `-D warnings` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:18:5 + | +LL | 1 + 2 << 3; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:19:5 + | +LL | 4 >> 1 + 1; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:20:5 + | +LL | 1 + 3 >> 2; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:21:5 + | +LL | 1 ^ 1 - 1; + | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:22:5 + | +LL | 3 | 2 - 1; + | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:23:5 + | +LL | 3 & 5 - 2; + | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:24:5 + | +LL | -1i32.abs(); + | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1i32.abs())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:25:5 + | +LL | -1f32.abs(); + | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/print.rs b/src/tools/clippy/tests/ui/print.rs new file mode 100644 index 0000000000000..366ccc2b3bd58 --- /dev/null +++ b/src/tools/clippy/tests/ui/print.rs @@ -0,0 +1,35 @@ +#![allow(clippy::print_literal, clippy::write_literal)] +#![warn(clippy::print_stdout, clippy::use_debug)] + +use std::fmt::{Debug, Display, Formatter, Result}; + +#[allow(dead_code)] +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}", 43.1415) + } +} + +impl Debug for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } +} + +fn main() { + println!("Hello"); + print!("Hello"); + + print!("Hello {}", "World"); + + print!("Hello {:?}", "World"); + + print!("Hello {:#?}", "#orld"); + + assert_eq!(42, 1337); + + vec![1, 2]; +} diff --git a/src/tools/clippy/tests/ui/print.stderr b/src/tools/clippy/tests/ui/print.stderr new file mode 100644 index 0000000000000..208d953262851 --- /dev/null +++ b/src/tools/clippy/tests/ui/print.stderr @@ -0,0 +1,54 @@ +error: use of `Debug`-based formatting + --> $DIR/print.rs:11:19 + | +LL | write!(f, "{:?}", 43.1415) + | ^^^^^^ + | + = note: `-D clippy::use-debug` implied by `-D warnings` + +error: use of `println!` + --> $DIR/print.rs:23:5 + | +LL | println!("Hello"); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stdout` implied by `-D warnings` + +error: use of `print!` + --> $DIR/print.rs:24:5 + | +LL | print!("Hello"); + | ^^^^^^^^^^^^^^^ + +error: use of `print!` + --> $DIR/print.rs:26:5 + | +LL | print!("Hello {}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> $DIR/print.rs:28:5 + | +LL | print!("Hello {:?}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `Debug`-based formatting + --> $DIR/print.rs:28:12 + | +LL | print!("Hello {:?}", "World"); + | ^^^^^^^^^^^^ + +error: use of `print!` + --> $DIR/print.rs:30:5 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `Debug`-based formatting + --> $DIR/print.rs:30:12 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs new file mode 100644 index 0000000000000..40ed18e930263 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -0,0 +1,38 @@ +#![warn(clippy::print_literal)] + +fn main() { + // these should be fine + print!("Hello"); + println!("Hello"); + let world = "world"; + println!("Hello {}", world); + println!("Hello {world}", world = world); + println!("3 in hex is {:X}", 3); + println!("2 + 1 = {:.4}", 3); + println!("2 + 1 = {:5.4}", 3); + println!("Debug test {:?}", "hello, world"); + println!("{0:8} {1:>8}", "hello", "world"); + println!("{1:8} {0:>8}", "hello", "world"); + println!("{foo:8} {bar:>8}", foo = "hello", bar = "world"); + println!("{bar:8} {foo:>8}", foo = "hello", bar = "world"); + println!("{number:>width$}", number = 1, width = 6); + println!("{number:>0width$}", number = 1, width = 6); + + // these should throw warnings + println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + print!("Hello {}", "world"); + println!("Hello {} {}", world, "world"); + println!("Hello {}", "world"); + println!("10 / 4 is {}", 2.5); + println!("2 + 1 = {}", 3); + + // positional args don't change the fact + // that we're using a literal -- this should + // throw a warning + println!("{0} {1}", "hello", "world"); + println!("{1} {0}", "hello", "world"); + + // named args shouldn't change anything either + println!("{foo} {bar}", foo = "hello", bar = "world"); + println!("{bar} {foo}", foo = "hello", bar = "world"); +} diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr new file mode 100644 index 0000000000000..fc502e9f71d52 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -0,0 +1,88 @@ +error: literal with an empty format string + --> $DIR/print_literal.rs:22:71 + | +LL | println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + | ^ + | + = note: `-D clippy::print-literal` implied by `-D warnings` + +error: literal with an empty format string + --> $DIR/print_literal.rs:23:24 + | +LL | print!("Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:24:36 + | +LL | println!("Hello {} {}", world, "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:25:26 + | +LL | println!("Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:26:30 + | +LL | println!("10 / 4 is {}", 2.5); + | ^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:27:28 + | +LL | println!("2 + 1 = {}", 3); + | ^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:32:25 + | +LL | println!("{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:32:34 + | +LL | println!("{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:33:25 + | +LL | println!("{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:33:34 + | +LL | println!("{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:36:35 + | +LL | println!("{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:36:50 + | +LL | println!("{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:37:35 + | +LL | println!("{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:37:50 + | +LL | println!("{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/print_with_newline.rs b/src/tools/clippy/tests/ui/print_with_newline.rs new file mode 100644 index 0000000000000..3f710540e9038 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_with_newline.rs @@ -0,0 +1,51 @@ +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 +// // run-rustfix + +#![allow(clippy::print_literal)] +#![warn(clippy::print_with_newline)] + +fn main() { + print!("Hello\n"); + print!("Hello {}\n", "world"); + print!("Hello {} {}\n", "world", "#2"); + print!("{}\n", 1265); + + // these are all fine + print!(""); + print!("Hello"); + println!("Hello"); + println!("Hello\n"); + println!("Hello {}\n", "world"); + print!("Issue\n{}", 1265); + print!("{}", 1265); + print!("\n{}", 1275); + print!("\n\n"); + print!("like eof\n\n"); + print!("Hello {} {}\n\n", "world", "#2"); + println!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + println!("\nbla\n\n"); // #3126 + + // Escaping + print!("\\n"); // #3514 + print!("\\\n"); // should fail + print!("\\\\n"); + + // Raw strings + print!(r"\n"); // #3778 + + // Literal newlines should also fail + print!( + " +" + ); + print!( + r" +" + ); + + // Don't warn on CRLF (#4208) + print!("\r\n"); + print!("foo\r\n"); + print!("\\r\n"); //~ ERROR + print!("foo\rbar\n") // ~ ERROR +} diff --git a/src/tools/clippy/tests/ui/print_with_newline.stderr b/src/tools/clippy/tests/ui/print_with_newline.stderr new file mode 100644 index 0000000000000..05fe88915d6ef --- /dev/null +++ b/src/tools/clippy/tests/ui/print_with_newline.stderr @@ -0,0 +1,110 @@ +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:8:5 + | +LL | print!("Hello/n"); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-with-newline` implied by `-D warnings` +help: use `println!` instead + | +LL | println!("Hello"); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:9:5 + | +LL | print!("Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("Hello {}", "world"); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:10:5 + | +LL | print!("Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("Hello {} {}", "world", "#2"); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:11:5 + | +LL | print!("{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("{}", 1265); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:30:5 + | +LL | print!("//n"); // should fail + | ^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("/"); // should fail + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:37:5 + | +LL | / print!( +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `println!` instead + | +LL | println!( +LL | "" + | + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:41:5 + | +LL | / print!( +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `println!` instead + | +LL | println!( +LL | r"" + | + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:49:5 + | +LL | print!("/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("/r"); //~ ERROR + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:50:5 + | +LL | print!("foo/rbar/n") // ~ ERROR + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("foo/rbar") // ~ ERROR + | ^^^^^^^ -- + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/println_empty_string.fixed b/src/tools/clippy/tests/ui/println_empty_string.fixed new file mode 100644 index 0000000000000..2b889b62ea991 --- /dev/null +++ b/src/tools/clippy/tests/ui/println_empty_string.fixed @@ -0,0 +1,11 @@ +// run-rustfix +#![allow(clippy::match_single_binding)] + +fn main() { + println!(); + println!(); + + match "a" { + _ => println!(), + } +} diff --git a/src/tools/clippy/tests/ui/println_empty_string.rs b/src/tools/clippy/tests/ui/println_empty_string.rs new file mode 100644 index 0000000000000..890f5f6847603 --- /dev/null +++ b/src/tools/clippy/tests/ui/println_empty_string.rs @@ -0,0 +1,11 @@ +// run-rustfix +#![allow(clippy::match_single_binding)] + +fn main() { + println!(); + println!(""); + + match "a" { + _ => println!(""), + } +} diff --git a/src/tools/clippy/tests/ui/println_empty_string.stderr b/src/tools/clippy/tests/ui/println_empty_string.stderr new file mode 100644 index 0000000000000..23112b8816893 --- /dev/null +++ b/src/tools/clippy/tests/ui/println_empty_string.stderr @@ -0,0 +1,16 @@ +error: using `println!("")` + --> $DIR/println_empty_string.rs:6:5 + | +LL | println!(""); + | ^^^^^^^^^^^^ help: replace it with: `println!()` + | + = note: `-D clippy::println-empty-string` implied by `-D warnings` + +error: using `println!("")` + --> $DIR/println_empty_string.rs:9:14 + | +LL | _ => println!(""), + | ^^^^^^^^^^^^ help: replace it with: `println!()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/proc_macro.rs b/src/tools/clippy/tests/ui/proc_macro.rs new file mode 100644 index 0000000000000..59914b8b8f627 --- /dev/null +++ b/src/tools/clippy/tests/ui/proc_macro.rs @@ -0,0 +1,26 @@ +//! Check that we correctly lint procedural macros. +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[allow(dead_code)] +fn f() { + let _x = 3.14; +} + +#[proc_macro] +pub fn mybangmacro(t: TokenStream) -> TokenStream { + t +} + +#[proc_macro_derive(MyDerivedTrait)] +pub fn myderive(t: TokenStream) -> TokenStream { + t +} + +#[proc_macro_attribute] +pub fn myattribute(t: TokenStream, a: TokenStream) -> TokenStream { + t +} diff --git a/src/tools/clippy/tests/ui/proc_macro.stderr b/src/tools/clippy/tests/ui/proc_macro.stderr new file mode 100644 index 0000000000000..872cbc66af622 --- /dev/null +++ b/src/tools/clippy/tests/ui/proc_macro.stderr @@ -0,0 +1,10 @@ +error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly + --> $DIR/proc_macro.rs:10:14 + | +LL | let _x = 3.14; + | ^^^^ + | + = note: `#[deny(clippy::approx_constant)]` on by default + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs new file mode 100644 index 0000000000000..30f39e9b06398 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -0,0 +1,86 @@ +#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +#![warn(clippy::ptr_arg)] + +use std::borrow::Cow; + +fn do_vec(x: &Vec) { + //Nothing here +} + +fn do_vec_mut(x: &mut Vec) { + // no error here + //Nothing here +} + +fn do_str(x: &String) { + //Nothing here either +} + +fn do_str_mut(x: &mut String) { + // no error here + //Nothing here either +} + +fn main() {} + +trait Foo { + type Item; + fn do_vec(x: &Vec); + fn do_item(x: &Self::Item); +} + +struct Bar; + +// no error, in trait impl (#425) +impl Foo for Bar { + type Item = Vec; + fn do_vec(x: &Vec) {} + fn do_item(x: &Vec) {} +} + +fn cloned(x: &Vec) -> Vec { + let e = x.clone(); + let f = e.clone(); // OK + let g = x; + let h = g.clone(); // Alas, we cannot reliably detect this without following data. + let i = (e).clone(); + x.clone() +} + +fn str_cloned(x: &String) -> String { + let a = x.clone(); + let b = x.clone(); + let c = b.clone(); + let d = a.clone().clone().clone(); + x.clone() +} + +fn false_positive_capacity(x: &Vec, y: &String) { + let a = x.capacity(); + let b = y.clone(); + let c = y.as_str(); +} + +fn false_positive_capacity_too(x: &String) -> String { + if x.capacity() > 1024 { + panic!("Too large!"); + } + x.clone() +} + +#[allow(dead_code)] +fn test_cow_with_ref(c: &Cow<[i32]>) {} + +#[allow(dead_code)] +fn test_cow(c: Cow<[i32]>) { + let _c = c; +} + +trait Foo2 { + fn do_string(&self); +} + +// no error for &self references where self is of type String (#2293) +impl Foo2 for String { + fn do_string(&self) {} +} diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr new file mode 100644 index 0000000000000..314f23497f971 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -0,0 +1,89 @@ +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:6:14 + | +LL | fn do_vec(x: &Vec) { + | ^^^^^^^^^ help: change this to: `&[i64]` + | + = note: `-D clippy::ptr-arg` implied by `-D warnings` + +error: writing `&String` instead of `&str` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:15:14 + | +LL | fn do_str(x: &String) { + | ^^^^^^^ help: change this to: `&str` + +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:28:18 + | +LL | fn do_vec(x: &Vec); + | ^^^^^^^^^ help: change this to: `&[i64]` + +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:41:14 + | +LL | fn cloned(x: &Vec) -> Vec { + | ^^^^^^^^ + | +help: change this to + | +LL | fn cloned(x: &[u8]) -> Vec { + | ^^^^^ +help: change `x.clone()` to + | +LL | let e = x.to_owned(); + | ^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | x.to_owned() + | + +error: writing `&String` instead of `&str` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:50:18 + | +LL | fn str_cloned(x: &String) -> String { + | ^^^^^^^ + | +help: change this to + | +LL | fn str_cloned(x: &str) -> String { + | ^^^^ +help: change `x.clone()` to + | +LL | let a = x.to_string(); + | ^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | let b = x.to_string(); + | ^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | x.to_string() + | + +error: writing `&String` instead of `&str` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:58:44 + | +LL | fn false_positive_capacity(x: &Vec, y: &String) { + | ^^^^^^^ + | +help: change this to + | +LL | fn false_positive_capacity(x: &Vec, y: &str) { + | ^^^^ +help: change `y.clone()` to + | +LL | let b = y.to_string(); + | ^^^^^^^^^^^^^ +help: change `y.as_str()` to + | +LL | let c = y; + | ^ + +error: using a reference to `Cow` is not recommended. + --> $DIR/ptr_arg.rs:72:25 + | +LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} + | ^^^^^^^^^^^ help: change this to: `&[i32]` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed new file mode 100644 index 0000000000000..ebdd6c4003d19 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed @@ -0,0 +1,20 @@ +// run-rustfix + +fn main() { + let vec = vec![b'a', b'b', b'c']; + let ptr = vec.as_ptr(); + + let offset_u8 = 1_u8; + let offset_usize = 1_usize; + let offset_isize = 1_isize; + + unsafe { + ptr.add(offset_usize); + ptr.offset(offset_isize as isize); + ptr.offset(offset_u8 as isize); + + ptr.wrapping_add(offset_usize); + ptr.wrapping_offset(offset_isize as isize); + ptr.wrapping_offset(offset_u8 as isize); + } +} diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs new file mode 100644 index 0000000000000..3416c4b727a53 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs @@ -0,0 +1,20 @@ +// run-rustfix + +fn main() { + let vec = vec![b'a', b'b', b'c']; + let ptr = vec.as_ptr(); + + let offset_u8 = 1_u8; + let offset_usize = 1_usize; + let offset_isize = 1_isize; + + unsafe { + ptr.offset(offset_usize as isize); + ptr.offset(offset_isize as isize); + ptr.offset(offset_u8 as isize); + + ptr.wrapping_offset(offset_usize as isize); + ptr.wrapping_offset(offset_isize as isize); + ptr.wrapping_offset(offset_u8 as isize); + } +} diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr new file mode 100644 index 0000000000000..b5c7a03e2775e --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr @@ -0,0 +1,16 @@ +error: use of `offset` with a `usize` casted to an `isize` + --> $DIR/ptr_offset_with_cast.rs:12:9 + | +LL | ptr.offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` + | + = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` + +error: use of `wrapping_offset` with a `usize` casted to an `isize` + --> $DIR/ptr_offset_with_cast.rs:16:9 + | +LL | ptr.wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed new file mode 100644 index 0000000000000..11dff94a28865 --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -0,0 +1,125 @@ +// run-rustfix +#![allow(unreachable_code)] + +fn some_func(a: Option) -> Option { + a?; + + a +} + +fn some_other_func(a: Option) -> Option { + if a.is_none() { + return None; + } else { + return Some(0); + } + unreachable!() +} + +pub enum SeemsOption { + Some(T), + None, +} + +impl SeemsOption { + pub fn is_none(&self) -> bool { + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } + } +} + +fn returns_something_similar_to_option(a: SeemsOption) -> SeemsOption { + if a.is_none() { + return SeemsOption::None; + } + + a +} + +pub struct CopyStruct { + pub opt: Option, +} + +impl CopyStruct { + #[rustfmt::skip] + pub fn func(&self) -> Option { + (self.opt)?; + + self.opt?; + + let _ = Some(self.opt?); + + let _ = self.opt?; + + self.opt + } +} + +#[derive(Clone)] +pub struct MoveStruct { + pub opt: Option>, +} + +impl MoveStruct { + pub fn ref_func(&self) -> Option> { + self.opt.as_ref()?; + + self.opt.clone() + } + + pub fn mov_func_reuse(self) -> Option> { + self.opt.as_ref()?; + + self.opt + } + + pub fn mov_func_no_use(self) -> Option> { + self.opt.as_ref()?; + Some(Vec::new()) + } + + pub fn if_let_ref_func(self) -> Option> { + let v: &Vec<_> = self.opt.as_ref()?; + + Some(v.clone()) + } + + pub fn if_let_mov_func(self) -> Option> { + let v = self.opt?; + + Some(v) + } +} + +fn func() -> Option { + fn f() -> Option { + Some(String::new()) + } + + f()?; + + Some(0) +} + +fn main() { + some_func(Some(42)); + some_func(None); + some_other_func(Some(42)); + + let copy_struct = CopyStruct { opt: Some(54) }; + copy_struct.func(); + + let move_struct = MoveStruct { + opt: Some(vec![42, 1337]), + }; + move_struct.ref_func(); + move_struct.clone().mov_func_reuse(); + move_struct.mov_func_no_use(); + + let so = SeemsOption::Some(45); + returns_something_similar_to_option(so); + + func(); +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs new file mode 100644 index 0000000000000..1d0ee82b4f778 --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -0,0 +1,155 @@ +// run-rustfix +#![allow(unreachable_code)] + +fn some_func(a: Option) -> Option { + if a.is_none() { + return None; + } + + a +} + +fn some_other_func(a: Option) -> Option { + if a.is_none() { + return None; + } else { + return Some(0); + } + unreachable!() +} + +pub enum SeemsOption { + Some(T), + None, +} + +impl SeemsOption { + pub fn is_none(&self) -> bool { + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } + } +} + +fn returns_something_similar_to_option(a: SeemsOption) -> SeemsOption { + if a.is_none() { + return SeemsOption::None; + } + + a +} + +pub struct CopyStruct { + pub opt: Option, +} + +impl CopyStruct { + #[rustfmt::skip] + pub fn func(&self) -> Option { + if (self.opt).is_none() { + return None; + } + + if self.opt.is_none() { + return None + } + + let _ = if self.opt.is_none() { + return None; + } else { + self.opt + }; + + let _ = if let Some(x) = self.opt { + x + } else { + return None; + }; + + self.opt + } +} + +#[derive(Clone)] +pub struct MoveStruct { + pub opt: Option>, +} + +impl MoveStruct { + pub fn ref_func(&self) -> Option> { + if self.opt.is_none() { + return None; + } + + self.opt.clone() + } + + pub fn mov_func_reuse(self) -> Option> { + if self.opt.is_none() { + return None; + } + + self.opt + } + + pub fn mov_func_no_use(self) -> Option> { + if self.opt.is_none() { + return None; + } + Some(Vec::new()) + } + + pub fn if_let_ref_func(self) -> Option> { + let v: &Vec<_> = if let Some(ref v) = self.opt { + v + } else { + return None; + }; + + Some(v.clone()) + } + + pub fn if_let_mov_func(self) -> Option> { + let v = if let Some(v) = self.opt { + v + } else { + return None; + }; + + Some(v) + } +} + +fn func() -> Option { + fn f() -> Option { + Some(String::new()) + } + + if f().is_none() { + return None; + } + + Some(0) +} + +fn main() { + some_func(Some(42)); + some_func(None); + some_other_func(Some(42)); + + let copy_struct = CopyStruct { opt: Some(54) }; + copy_struct.func(); + + let move_struct = MoveStruct { + opt: Some(vec![42, 1337]), + }; + move_struct.ref_func(); + move_struct.clone().mov_func_reuse(); + move_struct.mov_func_no_use(); + + let so = SeemsOption::Some(45); + returns_something_similar_to_option(so); + + func(); +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr new file mode 100644 index 0000000000000..502615fb175a1 --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -0,0 +1,104 @@ +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:5:5 + | +LL | / if a.is_none() { +LL | | return None; +LL | | } + | |_____^ help: replace it with: `a?;` + | + = note: `-D clippy::question-mark` implied by `-D warnings` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:50:9 + | +LL | / if (self.opt).is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `(self.opt)?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:54:9 + | +LL | / if self.opt.is_none() { +LL | | return None +LL | | } + | |_________^ help: replace it with: `self.opt?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:58:17 + | +LL | let _ = if self.opt.is_none() { + | _________________^ +LL | | return None; +LL | | } else { +LL | | self.opt +LL | | }; + | |_________^ help: replace it with: `Some(self.opt?)` + +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:64:17 + | +LL | let _ = if let Some(x) = self.opt { + | _________________^ +LL | | x +LL | | } else { +LL | | return None; +LL | | }; + | |_________^ help: replace it with: `self.opt?` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:81:9 + | +LL | / if self.opt.is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `self.opt.as_ref()?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:89:9 + | +LL | / if self.opt.is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `self.opt.as_ref()?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:97:9 + | +LL | / if self.opt.is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `self.opt.as_ref()?;` + +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:104:26 + | +LL | let v: &Vec<_> = if let Some(ref v) = self.opt { + | __________________________^ +LL | | v +LL | | } else { +LL | | return None; +LL | | }; + | |_________^ help: replace it with: `self.opt.as_ref()?` + +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:114:17 + | +LL | let v = if let Some(v) = self.opt { + | _________________^ +LL | | v +LL | | } else { +LL | | return None; +LL | | }; + | |_________^ help: replace it with: `self.opt?` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:129:5 + | +LL | / if f().is_none() { +LL | | return None; +LL | | } + | |_____^ help: replace it with: `f()?;` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/range.rs b/src/tools/clippy/tests/ui/range.rs new file mode 100644 index 0000000000000..628282509c1a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/range.rs @@ -0,0 +1,16 @@ +#[warn(clippy::range_zip_with_len)] +fn main() { + let v1 = vec![1, 2, 3]; + let v2 = vec![4, 5]; + let _x = v1.iter().zip(0..v1.len()); + let _y = v1.iter().zip(0..v2.len()); // No error +} + +#[allow(unused)] +fn no_panic_with_fake_range_types() { + struct Range { + foo: i32, + } + + let _ = Range { foo: 0 }; +} diff --git a/src/tools/clippy/tests/ui/range.stderr b/src/tools/clippy/tests/ui/range.stderr new file mode 100644 index 0000000000000..d53c1edecac01 --- /dev/null +++ b/src/tools/clippy/tests/ui/range.stderr @@ -0,0 +1,10 @@ +error: It is more idiomatic to use `v1.iter().enumerate()` + --> $DIR/range.rs:5:14 + | +LL | let _x = v1.iter().zip(0..v1.len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::range-zip-with-len` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.fixed b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed new file mode 100644 index 0000000000000..6b40211409974 --- /dev/null +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#![allow(unused_parens)] + +fn f() -> usize { + 42 +} + +#[warn(clippy::range_plus_one)] +fn main() { + for _ in 0..2 {} + for _ in 0..=2 {} + + for _ in 0..=3 {} + for _ in 0..=3 + 1 {} + + for _ in 0..=5 {} + for _ in 0..=1 + 5 {} + + for _ in 1..=1 {} + for _ in 1..=1 + 1 {} + + for _ in 0..13 + 13 {} + for _ in 0..=13 - 7 {} + + for _ in 0..=f() {} + for _ in 0..=(1 + f()) {} + + let _ = ..11 - 1; + let _ = ..11; + let _ = ..11; + let _ = (1..=11); + let _ = ((f() + 1)..=f()); + + const ONE: usize = 1; + // integer consts are linted, too + for _ in 1..=ONE {} + + let mut vec: Vec<()> = std::vec::Vec::new(); + vec.drain(..); +} diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.rs b/src/tools/clippy/tests/ui/range_plus_minus_one.rs new file mode 100644 index 0000000000000..3cfed4125b35c --- /dev/null +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![allow(unused_parens)] + +fn f() -> usize { + 42 +} + +#[warn(clippy::range_plus_one)] +fn main() { + for _ in 0..2 {} + for _ in 0..=2 {} + + for _ in 0..3 + 1 {} + for _ in 0..=3 + 1 {} + + for _ in 0..1 + 5 {} + for _ in 0..=1 + 5 {} + + for _ in 1..1 + 1 {} + for _ in 1..=1 + 1 {} + + for _ in 0..13 + 13 {} + for _ in 0..=13 - 7 {} + + for _ in 0..(1 + f()) {} + for _ in 0..=(1 + f()) {} + + let _ = ..11 - 1; + let _ = ..=11 - 1; + let _ = ..=(11 - 1); + let _ = (1..11 + 1); + let _ = (f() + 1)..(f() + 1); + + const ONE: usize = 1; + // integer consts are linted, too + for _ in 1..ONE + ONE {} + + let mut vec: Vec<()> = std::vec::Vec::new(); + vec.drain(..); +} diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr new file mode 100644 index 0000000000000..f72943a04f252 --- /dev/null +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr @@ -0,0 +1,60 @@ +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:14:14 + | +LL | for _ in 0..3 + 1 {} + | ^^^^^^^^ help: use: `0..=3` + | + = note: `-D clippy::range-plus-one` implied by `-D warnings` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:17:14 + | +LL | for _ in 0..1 + 5 {} + | ^^^^^^^^ help: use: `0..=5` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:20:14 + | +LL | for _ in 1..1 + 1 {} + | ^^^^^^^^ help: use: `1..=1` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:26:14 + | +LL | for _ in 0..(1 + f()) {} + | ^^^^^^^^^^^^ help: use: `0..=f()` + +error: an exclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:30:13 + | +LL | let _ = ..=11 - 1; + | ^^^^^^^^^ help: use: `..11` + | + = note: `-D clippy::range-minus-one` implied by `-D warnings` + +error: an exclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:31:13 + | +LL | let _ = ..=(11 - 1); + | ^^^^^^^^^^^ help: use: `..11` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:32:13 + | +LL | let _ = (1..11 + 1); + | ^^^^^^^^^^^ help: use: `(1..=11)` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:33:13 + | +LL | let _ = (f() + 1)..(f() + 1); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:37:14 + | +LL | for _ in 1..ONE + ONE {} + | ^^^^^^^^^^^^ help: use: `1..=ONE` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_allocation.fixed b/src/tools/clippy/tests/ui/redundant_allocation.fixed new file mode 100644 index 0000000000000..266358334587d --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_allocation.fixed @@ -0,0 +1,48 @@ +// run-rustfix +#![warn(clippy::all)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::boxed::Box; +use std::rc::Rc; + +pub struct MyStruct {} + +pub struct SubT { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +// Rc<&T> + +pub fn test1(foo: &T) {} + +pub fn test2(foo: &MyStruct) {} + +pub fn test3(foo: &MyEnum) {} + +pub fn test4_neg(foo: Rc>) {} + +// Rc> + +pub fn test5(a: Rc) {} + +// Rc> + +pub fn test6(a: Box) {} + +// Box<&T> + +pub fn test7(foo: &T) {} + +pub fn test8(foo: &MyStruct) {} + +pub fn test9(foo: &MyEnum) {} + +pub fn test10_neg(foo: Box>) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_allocation.rs b/src/tools/clippy/tests/ui/redundant_allocation.rs new file mode 100644 index 0000000000000..677b3e56d4dce --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_allocation.rs @@ -0,0 +1,48 @@ +// run-rustfix +#![warn(clippy::all)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::boxed::Box; +use std::rc::Rc; + +pub struct MyStruct {} + +pub struct SubT { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +// Rc<&T> + +pub fn test1(foo: Rc<&T>) {} + +pub fn test2(foo: Rc<&MyStruct>) {} + +pub fn test3(foo: Rc<&MyEnum>) {} + +pub fn test4_neg(foo: Rc>) {} + +// Rc> + +pub fn test5(a: Rc>) {} + +// Rc> + +pub fn test6(a: Rc>) {} + +// Box<&T> + +pub fn test7(foo: Box<&T>) {} + +pub fn test8(foo: Box<&MyStruct>) {} + +pub fn test9(foo: Box<&MyEnum>) {} + +pub fn test10_neg(foo: Box>) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_allocation.stderr b/src/tools/clippy/tests/ui/redundant_allocation.stderr new file mode 100644 index 0000000000000..eaa57ce3024b6 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_allocation.stderr @@ -0,0 +1,52 @@ +error: usage of `Rc<&T>` + --> $DIR/redundant_allocation.rs:22:22 + | +LL | pub fn test1(foo: Rc<&T>) {} + | ^^^^^^ help: try: `&T` + | + = note: `-D clippy::redundant-allocation` implied by `-D warnings` + +error: usage of `Rc<&T>` + --> $DIR/redundant_allocation.rs:24:19 + | +LL | pub fn test2(foo: Rc<&MyStruct>) {} + | ^^^^^^^^^^^^^ help: try: `&MyStruct` + +error: usage of `Rc<&T>` + --> $DIR/redundant_allocation.rs:26:19 + | +LL | pub fn test3(foo: Rc<&MyEnum>) {} + | ^^^^^^^^^^^ help: try: `&MyEnum` + +error: usage of `Rc>` + --> $DIR/redundant_allocation.rs:32:17 + | +LL | pub fn test5(a: Rc>) {} + | ^^^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc>` + --> $DIR/redundant_allocation.rs:36:17 + | +LL | pub fn test6(a: Rc>) {} + | ^^^^^^^^^^^^^ help: try: `Box` + +error: usage of `Box<&T>` + --> $DIR/redundant_allocation.rs:40:22 + | +LL | pub fn test7(foo: Box<&T>) {} + | ^^^^^^^ help: try: `&T` + +error: usage of `Box<&T>` + --> $DIR/redundant_allocation.rs:42:19 + | +LL | pub fn test8(foo: Box<&MyStruct>) {} + | ^^^^^^^^^^^^^^ help: try: `&MyStruct` + +error: usage of `Box<&T>` + --> $DIR/redundant_allocation.rs:44:19 + | +LL | pub fn test9(foo: Box<&MyEnum>) {} + | ^^^^^^^^^^^^ help: try: `&MyEnum` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed new file mode 100644 index 0000000000000..764c10a6d398f --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -0,0 +1,172 @@ +// run-rustfix +// rustfix-only-machine-applicable + +use std::ffi::OsString; +use std::path::Path; + +fn main() { + let _s = ["lorem", "ipsum"].join(" "); + + let s = String::from("foo"); + let _s = s; + + let s = String::from("foo"); + let _s = s; + + let s = String::from("foo"); + let _s = s; + + let _s = Path::new("/a/b/").join("c"); + + let _s = Path::new("/a/b/").join("c"); + + let _s = OsString::new(); + + let _s = OsString::new(); + + // Check that lint level works + #[allow(clippy::redundant_clone)] + let _s = String::new().to_string(); + + let tup = (String::from("foo"),); + let _t = tup.0; + + let tup_ref = &(String::from("foo"),); + let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed + + { + let x = String::new(); + let y = &x; + + let _x = x.clone(); // ok; `x` is borrowed by `y` + + let _ = y.len(); + } + + let x = (String::new(),); + let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x` + + with_branch(Alpha, true); + cannot_double_move(Alpha); + cannot_move_from_type_with_drop(); + borrower_propagation(); + not_consumed(); + issue_5405(); +} + +#[derive(Clone)] +struct Alpha; +fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { + if b { + (a.clone(), a) + } else { + (Alpha, a) + } +} + +fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { + (a.clone(), a) +} + +struct TypeWithDrop { + x: String, +} + +impl Drop for TypeWithDrop { + fn drop(&mut self) {} +} + +fn cannot_move_from_type_with_drop() -> String { + let s = TypeWithDrop { x: String::new() }; + s.x.clone() // removing this `clone()` summons E0509 +} + +fn borrower_propagation() { + let s = String::new(); + let t = String::new(); + + { + fn b() -> bool { + unimplemented!() + } + let _u = if b() { &s } else { &t }; + + // ok; `s` and `t` are possibly borrowed + let _s = s.clone(); + let _t = t.clone(); + } + + { + let _u = || s.len(); + let _v = [&t; 32]; + let _s = s.clone(); // ok + let _t = t.clone(); // ok + } + + { + let _u = { + let u = Some(&s); + let _ = s.clone(); // ok + u + }; + let _s = s.clone(); // ok + } + + { + use std::convert::identity as id; + let _u = id(id(&s)); + let _s = s.clone(); // ok, `u` borrows `s` + } + + let _s = s; + let _t = t; + + #[derive(Clone)] + struct Foo { + x: usize, + } + + { + let f = Foo { x: 123 }; + let _x = Some(f.x); + let _f = f; + } + + { + let f = Foo { x: 123 }; + let _x = &f.x; + let _f = f.clone(); // ok + } +} + +fn not_consumed() { + let x = std::path::PathBuf::from("home"); + let y = x.join("matthias"); + // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is + // redundant. (It also does not consume the PathBuf) + + println!("x: {:?}, y: {:?}", x, y); + + let mut s = String::new(); + s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior. + s.push_str("bar"); + assert_eq!(s, "bar"); + + let t = Some(s); + // OK + if let Some(x) = t.clone() { + println!("{}", x); + } + if let Some(x) = t { + println!("{}", x); + } +} + +#[allow(clippy::clone_on_copy)] +fn issue_5405() { + let a: [String; 1] = [String::from("foo")]; + let _b: String = a[0].clone(); + + let c: [usize; 2] = [2, 3]; + let _d: usize = c[1].clone(); +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs new file mode 100644 index 0000000000000..839747b131d77 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -0,0 +1,172 @@ +// run-rustfix +// rustfix-only-machine-applicable + +use std::ffi::OsString; +use std::path::Path; + +fn main() { + let _s = ["lorem", "ipsum"].join(" ").to_string(); + + let s = String::from("foo"); + let _s = s.clone(); + + let s = String::from("foo"); + let _s = s.to_string(); + + let s = String::from("foo"); + let _s = s.to_owned(); + + let _s = Path::new("/a/b/").join("c").to_owned(); + + let _s = Path::new("/a/b/").join("c").to_path_buf(); + + let _s = OsString::new().to_owned(); + + let _s = OsString::new().to_os_string(); + + // Check that lint level works + #[allow(clippy::redundant_clone)] + let _s = String::new().to_string(); + + let tup = (String::from("foo"),); + let _t = tup.0.clone(); + + let tup_ref = &(String::from("foo"),); + let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed + + { + let x = String::new(); + let y = &x; + + let _x = x.clone(); // ok; `x` is borrowed by `y` + + let _ = y.len(); + } + + let x = (String::new(),); + let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x` + + with_branch(Alpha, true); + cannot_double_move(Alpha); + cannot_move_from_type_with_drop(); + borrower_propagation(); + not_consumed(); + issue_5405(); +} + +#[derive(Clone)] +struct Alpha; +fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { + if b { + (a.clone(), a.clone()) + } else { + (Alpha, a) + } +} + +fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { + (a.clone(), a) +} + +struct TypeWithDrop { + x: String, +} + +impl Drop for TypeWithDrop { + fn drop(&mut self) {} +} + +fn cannot_move_from_type_with_drop() -> String { + let s = TypeWithDrop { x: String::new() }; + s.x.clone() // removing this `clone()` summons E0509 +} + +fn borrower_propagation() { + let s = String::new(); + let t = String::new(); + + { + fn b() -> bool { + unimplemented!() + } + let _u = if b() { &s } else { &t }; + + // ok; `s` and `t` are possibly borrowed + let _s = s.clone(); + let _t = t.clone(); + } + + { + let _u = || s.len(); + let _v = [&t; 32]; + let _s = s.clone(); // ok + let _t = t.clone(); // ok + } + + { + let _u = { + let u = Some(&s); + let _ = s.clone(); // ok + u + }; + let _s = s.clone(); // ok + } + + { + use std::convert::identity as id; + let _u = id(id(&s)); + let _s = s.clone(); // ok, `u` borrows `s` + } + + let _s = s.clone(); + let _t = t.clone(); + + #[derive(Clone)] + struct Foo { + x: usize, + } + + { + let f = Foo { x: 123 }; + let _x = Some(f.x); + let _f = f.clone(); + } + + { + let f = Foo { x: 123 }; + let _x = &f.x; + let _f = f.clone(); // ok + } +} + +fn not_consumed() { + let x = std::path::PathBuf::from("home"); + let y = x.clone().join("matthias"); + // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is + // redundant. (It also does not consume the PathBuf) + + println!("x: {:?}, y: {:?}", x, y); + + let mut s = String::new(); + s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior. + s.push_str("bar"); + assert_eq!(s, "bar"); + + let t = Some(s); + // OK + if let Some(x) = t.clone() { + println!("{}", x); + } + if let Some(x) = t { + println!("{}", x); + } +} + +#[allow(clippy::clone_on_copy)] +fn issue_5405() { + let a: [String; 1] = [String::from("foo")]; + let _b: String = a[0].clone(); + + let c: [usize; 2] = [2, 3]; + let _d: usize = c[1].clone(); +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr new file mode 100644 index 0000000000000..eced198283ce8 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_clone.stderr @@ -0,0 +1,171 @@ +error: redundant clone + --> $DIR/redundant_clone.rs:8:42 + | +LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); + | ^^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::redundant-clone` implied by `-D warnings` +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:8:14 + | +LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:11:15 + | +LL | let _s = s.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:11:14 + | +LL | let _s = s.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:14:15 + | +LL | let _s = s.to_string(); + | ^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:14:14 + | +LL | let _s = s.to_string(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:17:15 + | +LL | let _s = s.to_owned(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:17:14 + | +LL | let _s = s.to_owned(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:19:42 + | +LL | let _s = Path::new("/a/b/").join("c").to_owned(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:19:14 + | +LL | let _s = Path::new("/a/b/").join("c").to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:21:42 + | +LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); + | ^^^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:21:14 + | +LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:23:29 + | +LL | let _s = OsString::new().to_owned(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:23:14 + | +LL | let _s = OsString::new().to_owned(); + | ^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:25:29 + | +LL | let _s = OsString::new().to_os_string(); + | ^^^^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:25:14 + | +LL | let _s = OsString::new().to_os_string(); + | ^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:32:19 + | +LL | let _t = tup.0.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:32:14 + | +LL | let _t = tup.0.clone(); + | ^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:61:22 + | +LL | (a.clone(), a.clone()) + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:61:21 + | +LL | (a.clone(), a.clone()) + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:121:15 + | +LL | let _s = s.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:121:14 + | +LL | let _s = s.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:122:15 + | +LL | let _t = t.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:122:14 + | +LL | let _t = t.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:132:19 + | +LL | let _f = f.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:132:18 + | +LL | let _f = f.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:144:14 + | +LL | let y = x.clone().join("matthias"); + | ^^^^^^^^ help: remove this + | +note: cloned value is neither consumed nor mutated + --> $DIR/redundant_clone.rs:144:13 + | +LL | let y = x.clone().join("matthias"); + | ^^^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_closure_call.rs b/src/tools/clippy/tests/ui/redundant_closure_call.rs new file mode 100644 index 0000000000000..bacd67db7c305 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call.rs @@ -0,0 +1,23 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + let mut k = (|m| m + 1)(i); + + k = (|a, b| a * b)(1, 5); + + let closure = || 32; + i = closure(); + + let closure = |i| i + 1; + i = closure(3); + + i = closure(4); + + #[allow(clippy::needless_return)] + (|| return 2)(); + (|| -> Option { None? })(); + (|| -> Result { Err(2)? })(); +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call.stderr b/src/tools/clippy/tests/ui/redundant_closure_call.stderr new file mode 100644 index 0000000000000..68c1416bb6b1a --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call.stderr @@ -0,0 +1,28 @@ +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call.rs:12:5 + | +LL | i = closure(); + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call.rs:15:5 + | +LL | i = closure(3); + | ^^^^^^^^^^^^^^ + +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call.rs:7:17 + | +LL | let mut k = (|m| m + 1)(i); + | ^^^^^^^^^^^^^^ + +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call.rs:9:9 + | +LL | k = (|a, b| a * b)(1, 5); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed new file mode 100644 index 0000000000000..0abca6fca0613 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed @@ -0,0 +1,8 @@ +// run-rustfix + +#![warn(clippy::redundant_closure_call)] +#![allow(unused)] + +fn main() { + let a = 42; +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs new file mode 100644 index 0000000000000..f8b9d37a5cc4e --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs @@ -0,0 +1,8 @@ +// run-rustfix + +#![warn(clippy::redundant_closure_call)] +#![allow(unused)] + +fn main() { + let a = (|| 42)(); +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr new file mode 100644 index 0000000000000..e7737f9dd856f --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr @@ -0,0 +1,10 @@ +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call_fixable.rs:7:13 + | +LL | let a = (|| 42)(); + | ^^^^^^^^^ help: Try doing something like: : `42` + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/redundant_field_names.fixed b/src/tools/clippy/tests/ui/redundant_field_names.fixed new file mode 100644 index 0000000000000..5b4b8eeedd469 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_field_names.fixed @@ -0,0 +1,71 @@ +// run-rustfix +#![warn(clippy::redundant_field_names)] +#![allow(clippy::no_effect, dead_code, unused_variables)] + +#[macro_use] +extern crate derive_new; + +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; + +mod foo { + pub const BAR: u8 = 0; +} + +struct Person { + gender: u8, + age: u8, + name: u8, + buzz: u64, + foo: u8, +} + +#[derive(new)] +pub struct S { + v: String, +} + +fn main() { + let gender: u8 = 42; + let age = 0; + let fizz: u64 = 0; + let name: u8 = 0; + + let me = Person { + gender, + age, + + name, //should be ok + buzz: fizz, //should be ok + foo: foo::BAR, //should be ok + }; + + // Range expressions + let (start, end) = (0, 0); + + let _ = start..; + let _ = ..end; + let _ = start..end; + + let _ = ..=end; + let _ = start..=end; + + // Issue #2799 + let _: Vec<_> = (start..end).collect(); + + // hand-written Range family structs are linted + let _ = RangeFrom { start }; + let _ = RangeTo { end }; + let _ = Range { start, end }; + let _ = RangeInclusive::new(start, end); + let _ = RangeToInclusive { end }; +} + +fn issue_3476() { + fn foo() {} + + struct S { + foo: fn(), + } + + S { foo: foo:: }; +} diff --git a/src/tools/clippy/tests/ui/redundant_field_names.rs b/src/tools/clippy/tests/ui/redundant_field_names.rs new file mode 100644 index 0000000000000..3f97b80c56828 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_field_names.rs @@ -0,0 +1,71 @@ +// run-rustfix +#![warn(clippy::redundant_field_names)] +#![allow(clippy::no_effect, dead_code, unused_variables)] + +#[macro_use] +extern crate derive_new; + +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; + +mod foo { + pub const BAR: u8 = 0; +} + +struct Person { + gender: u8, + age: u8, + name: u8, + buzz: u64, + foo: u8, +} + +#[derive(new)] +pub struct S { + v: String, +} + +fn main() { + let gender: u8 = 42; + let age = 0; + let fizz: u64 = 0; + let name: u8 = 0; + + let me = Person { + gender: gender, + age: age, + + name, //should be ok + buzz: fizz, //should be ok + foo: foo::BAR, //should be ok + }; + + // Range expressions + let (start, end) = (0, 0); + + let _ = start..; + let _ = ..end; + let _ = start..end; + + let _ = ..=end; + let _ = start..=end; + + // Issue #2799 + let _: Vec<_> = (start..end).collect(); + + // hand-written Range family structs are linted + let _ = RangeFrom { start: start }; + let _ = RangeTo { end: end }; + let _ = Range { start: start, end: end }; + let _ = RangeInclusive::new(start, end); + let _ = RangeToInclusive { end: end }; +} + +fn issue_3476() { + fn foo() {} + + struct S { + foo: fn(), + } + + S { foo: foo:: }; +} diff --git a/src/tools/clippy/tests/ui/redundant_field_names.stderr b/src/tools/clippy/tests/ui/redundant_field_names.stderr new file mode 100644 index 0000000000000..7976292df2241 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_field_names.stderr @@ -0,0 +1,46 @@ +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:34:9 + | +LL | gender: gender, + | ^^^^^^^^^^^^^^ help: replace it with: `gender` + | + = note: `-D clippy::redundant-field-names` implied by `-D warnings` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:35:9 + | +LL | age: age, + | ^^^^^^^^ help: replace it with: `age` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:56:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:57:23 + | +LL | let _ = RangeTo { end: end }; + | ^^^^^^^^ help: replace it with: `end` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:58:21 + | +LL | let _ = Range { start: start, end: end }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:58:35 + | +LL | let _ = Range { start: start, end: end }; + | ^^^^^^^^ help: replace it with: `end` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:60:32 + | +LL | let _ = RangeToInclusive { end: end }; + | ^^^^^^^^ help: replace it with: `end` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed new file mode 100644 index 0000000000000..fc8cb0e747c73 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] + +fn main() { + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + if None::<()>.is_none() {} + + if Some(42).is_some() {} + + if Some(42).is_some() { + foo(); + } else { + bar(); + } + + while Some(42).is_some() {} + + while Some(42).is_none() {} + + while None::<()>.is_none() {} + + while Ok::(10).is_ok() {} + + while Ok::(10).is_err() {} + + let mut v = vec![1, 2, 3]; + while v.pop().is_some() { + foo(); + } + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + if None::.is_none() {} + + if Some(42).is_some() {} + + if let Ok(x) = Ok::(42) { + println!("{}", x); + } + + Ok::(42).is_ok(); + + Ok::(42).is_err(); + + Err::(42).is_err(); + + Err::(42).is_ok(); + + Some(42).is_some(); + + None::<()>.is_none(); + + let _ = None::<()>.is_none(); + + let _ = if Ok::(4).is_ok() { true } else { false }; + + let opt = Some(false); + let x = if opt.is_some() { true } else { false }; + takes_bool(x); + + issue5504(); + + let _ = if gen_opt().is_some() { + 1 + } else if gen_opt().is_none() { + 2 + } else if gen_res().is_ok() { + 3 + } else if gen_res().is_err() { + 4 + } else { + 5 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result, i32> { + Err(42) + } + + fn try_result_opt() -> Result { + while r#try!(result_opt()).is_some() {} + if r#try!(result_opt()).is_some() {} + Ok(42) + } + + try_result_opt(); + + if m!().is_some() {} + while m!().is_some() {} +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs new file mode 100644 index 0000000000000..51912dade0356 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs @@ -0,0 +1,140 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] + +fn main() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + if let None = None::<()> {} + + if let Some(_) = Some(42) {} + + if let Some(_) = Some(42) { + foo(); + } else { + bar(); + } + + while let Some(_) = Some(42) {} + + while let None = Some(42) {} + + while let None = None::<()> {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + let mut v = vec![1, 2, 3]; + while let Some(_) = v.pop() { + foo(); + } + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + if None::.is_none() {} + + if Some(42).is_some() {} + + if let Ok(x) = Ok::(42) { + println!("{}", x); + } + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Ok::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; + + let _ = match None::<()> { + Some(_) => false, + None => true, + }; + + let _ = if let Ok(_) = Ok::(4) { true } else { false }; + + let opt = Some(false); + let x = if let Some(_) = opt { true } else { false }; + takes_bool(x); + + issue5504(); + + let _ = if let Some(_) = gen_opt() { + 1 + } else if let None = gen_opt() { + 2 + } else if let Ok(_) = gen_res() { + 3 + } else if let Err(_) = gen_res() { + 4 + } else { + 5 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result, i32> { + Err(42) + } + + fn try_result_opt() -> Result { + while let Some(_) = r#try!(result_opt()) {} + if let Some(_) = r#try!(result_opt()) {} + Ok(42) + } + + try_result_opt(); + + if let Some(_) = m!() {} + while let Some(_) = m!() {} +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr new file mode 100644 index 0000000000000..b58deb7954efe --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr @@ -0,0 +1,194 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:8:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:10:12 + | +LL | if let Err(_) = Err::(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:12:12 + | +LL | if let None = None::<()> {} + | -------^^^^------------- help: try this: `if None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:14:12 + | +LL | if let Some(_) = Some(42) {} + | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:16:12 + | +LL | if let Some(_) = Some(42) { + | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:22:15 + | +LL | while let Some(_) = Some(42) {} + | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:24:15 + | +LL | while let None = Some(42) {} + | ----------^^^^----------- help: try this: `while Some(42).is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:26:15 + | +LL | while let None = None::<()> {} + | ----------^^^^------------- help: try this: `while None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:28:15 + | +LL | while let Ok(_) = Ok::(10) {} + | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:30:15 + | +LL | while let Err(_) = Ok::(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:33:15 + | +LL | while let Some(_) = v.pop() { + | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:49:5 + | +LL | / match Ok::(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:54:5 + | +LL | / match Ok::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Ok::(42).is_err()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:59:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:64:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Err::(42).is_ok()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:69:5 + | +LL | / match Some(42) { +LL | | Some(_) => true, +LL | | None => false, +LL | | }; + | |_____^ help: try this: `Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:74:5 + | +LL | / match None::<()> { +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:79:13 + | +LL | let _ = match None::<()> { + | _____________^ +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:84:20 + | +LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; + | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:87:20 + | +LL | let x = if let Some(_) = opt { true } else { false }; + | -------^^^^^^^------ help: try this: `if opt.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:92:20 + | +LL | let _ = if let Some(_) = gen_opt() { + | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:94:19 + | +LL | } else if let None = gen_opt() { + | -------^^^^------------ help: try this: `if gen_opt().is_none()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:96:19 + | +LL | } else if let Ok(_) = gen_res() { + | -------^^^^^------------ help: try this: `if gen_res().is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:98:19 + | +LL | } else if let Err(_) = gen_res() { + | -------^^^^^^------------ help: try this: `if gen_res().is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:131:19 + | +LL | while let Some(_) = r#try!(result_opt()) {} + | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:132:16 + | +LL | if let Some(_) = r#try!(result_opt()) {} + | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:138:12 + | +LL | if let Some(_) = m!() {} + | -------^^^^^^^------- help: try this: `if m!().is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:139:15 + | +LL | while let Some(_) = m!() {} + | ----------^^^^^^^------- help: try this: `while m!().is_some()` + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed new file mode 100644 index 0000000000000..25f2fd061b88e --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed @@ -0,0 +1,107 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::redundant_pub_crate)] + +mod m1 { + fn f() {} + pub fn g() {} // private due to m1 + pub fn h() {} + + mod m1_1 { + fn f() {} + pub fn g() {} // private due to m1_1 and m1 + pub fn h() {} + } + + pub mod m1_2 { + // ^ private due to m1 + fn f() {} + pub fn g() {} // private due to m1_2 and m1 + pub fn h() {} + } + + pub mod m1_3 { + fn f() {} + pub fn g() {} // private due to m1 + pub fn h() {} + } +} + +pub(crate) mod m2 { + fn f() {} + pub fn g() {} // already crate visible due to m2 + pub fn h() {} + + mod m2_1 { + fn f() {} + pub fn g() {} // private due to m2_1 + pub fn h() {} + } + + pub mod m2_2 { + // ^ already crate visible due to m2 + fn f() {} + pub fn g() {} // already crate visible due to m2_2 and m2 + pub fn h() {} + } + + pub mod m2_3 { + fn f() {} + pub fn g() {} // already crate visible due to m2 + pub fn h() {} + } +} + +pub mod m3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 is exported + pub fn h() {} + + mod m3_1 { + fn f() {} + pub fn g() {} // private due to m3_1 + pub fn h() {} + } + + pub(crate) mod m3_2 { + // ^ ok + fn f() {} + pub fn g() {} // already crate visible due to m3_2 + pub fn h() {} + } + + pub mod m3_3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 and m3_3 are exported + pub fn h() {} + } +} + +mod m4 { + fn f() {} + pub fn g() {} // private: not re-exported by `pub use m4::*` + pub fn h() {} + + mod m4_1 { + fn f() {} + pub fn g() {} // private due to m4_1 + pub fn h() {} + } + + pub mod m4_2 { + // ^ private: not re-exported by `pub use m4::*` + fn f() {} + pub fn g() {} // private due to m4_2 + pub fn h() {} + } + + pub mod m4_3 { + fn f() {} + pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*` + pub fn h() {} + } +} + +pub use m4::*; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs new file mode 100644 index 0000000000000..616286b4f39f4 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs @@ -0,0 +1,107 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::redundant_pub_crate)] + +mod m1 { + fn f() {} + pub(crate) fn g() {} // private due to m1 + pub fn h() {} + + mod m1_1 { + fn f() {} + pub(crate) fn g() {} // private due to m1_1 and m1 + pub fn h() {} + } + + pub(crate) mod m1_2 { + // ^ private due to m1 + fn f() {} + pub(crate) fn g() {} // private due to m1_2 and m1 + pub fn h() {} + } + + pub mod m1_3 { + fn f() {} + pub(crate) fn g() {} // private due to m1 + pub fn h() {} + } +} + +pub(crate) mod m2 { + fn f() {} + pub(crate) fn g() {} // already crate visible due to m2 + pub fn h() {} + + mod m2_1 { + fn f() {} + pub(crate) fn g() {} // private due to m2_1 + pub fn h() {} + } + + pub(crate) mod m2_2 { + // ^ already crate visible due to m2 + fn f() {} + pub(crate) fn g() {} // already crate visible due to m2_2 and m2 + pub fn h() {} + } + + pub mod m2_3 { + fn f() {} + pub(crate) fn g() {} // already crate visible due to m2 + pub fn h() {} + } +} + +pub mod m3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 is exported + pub fn h() {} + + mod m3_1 { + fn f() {} + pub(crate) fn g() {} // private due to m3_1 + pub fn h() {} + } + + pub(crate) mod m3_2 { + // ^ ok + fn f() {} + pub(crate) fn g() {} // already crate visible due to m3_2 + pub fn h() {} + } + + pub mod m3_3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 and m3_3 are exported + pub fn h() {} + } +} + +mod m4 { + fn f() {} + pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` + pub fn h() {} + + mod m4_1 { + fn f() {} + pub(crate) fn g() {} // private due to m4_1 + pub fn h() {} + } + + pub(crate) mod m4_2 { + // ^ private: not re-exported by `pub use m4::*` + fn f() {} + pub(crate) fn g() {} // private due to m4_2 + pub fn h() {} + } + + pub mod m4_3 { + fn f() {} + pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*` + pub fn h() {} + } +} + +pub use m4::*; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr new file mode 100644 index 0000000000000..6fccdaa4e2037 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr @@ -0,0 +1,132 @@ +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:7:5 + | +LL | pub(crate) fn g() {} // private due to m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + | + = note: `-D clippy::redundant-pub-crate` implied by `-D warnings` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:12:9 + | +LL | pub(crate) fn g() {} // private due to m1_1 and m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) module inside private module + --> $DIR/redundant_pub_crate.rs:16:5 + | +LL | pub(crate) mod m1_2 { + | ----------^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:19:9 + | +LL | pub(crate) fn g() {} // private due to m1_2 and m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:25:9 + | +LL | pub(crate) fn g() {} // private due to m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:32:5 + | +LL | pub(crate) fn g() {} // already crate visible due to m2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:37:9 + | +LL | pub(crate) fn g() {} // private due to m2_1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) module inside private module + --> $DIR/redundant_pub_crate.rs:41:5 + | +LL | pub(crate) mod m2_2 { + | ----------^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:44:9 + | +LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:50:9 + | +LL | pub(crate) fn g() {} // already crate visible due to m2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:62:9 + | +LL | pub(crate) fn g() {} // private due to m3_1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:69:9 + | +LL | pub(crate) fn g() {} // already crate visible due to m3_2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:82:5 + | +LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:87:9 + | +LL | pub(crate) fn g() {} // private due to m4_1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) module inside private module + --> $DIR/redundant_pub_crate.rs:91:5 + | +LL | pub(crate) mod m4_2 { + | ----------^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:94:9 + | +LL | pub(crate) fn g() {} // private due to m4_2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed new file mode 100644 index 0000000000000..921249606ad27 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed @@ -0,0 +1,56 @@ +// run-rustfix + +#![allow(unused)] + +#[derive(Debug)] +struct Foo {} + +const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning. + +const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static + +const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + +const VAR_SIX: &u8 = &5; + +const VAR_HEIGHT: &Foo = &Foo {}; + +const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static. + +static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static. + +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning. + +static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static + +static STATIC_VAR_SIX: &u8 = &5; + +static STATIC_VAR_HEIGHT: &Foo = &Foo {}; + +static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static. + +static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static. + +fn main() { + let false_positive: &'static str = "test"; +} + +trait Bar { + const TRAIT_VAR: &'static str; +} + +impl Foo { + const IMPL_VAR: &'static str = "var"; +} + +impl Bar for Foo { + const TRAIT_VAR: &'static str = "foo"; +} diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs new file mode 100644 index 0000000000000..4d4b249d076ff --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs @@ -0,0 +1,56 @@ +// run-rustfix + +#![allow(unused)] + +#[derive(Debug)] +struct Foo {} + +const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning. + +const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + +const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + +const VAR_SIX: &'static u8 = &5; + +const VAR_HEIGHT: &'static Foo = &Foo {}; + +const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + +static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. + +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning. + +static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + +static STATIC_VAR_SIX: &'static u8 = &5; + +static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; + +static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. + +static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + +fn main() { + let false_positive: &'static str = "test"; +} + +trait Bar { + const TRAIT_VAR: &'static str; +} + +impl Foo { + const IMPL_VAR: &'static str = "var"; +} + +impl Bar for Foo { + const TRAIT_VAR: &'static str = "foo"; +} diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr new file mode 100644 index 0000000000000..3c3d2eacd8d9c --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr @@ -0,0 +1,100 @@ +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:8:17 + | +LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. + | -^^^^^^^---- help: consider removing `'static`: `&str` + | + = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:12:21 + | +LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:14:32 + | +LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:14:47 + | +LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:16:17 + | +LL | const VAR_SIX: &'static u8 = &5; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:18:20 + | +LL | const VAR_HEIGHT: &'static Foo = &Foo {}; + | -^^^^^^^---- help: consider removing `'static`: `&Foo` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:20:19 + | +LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. + | -^^^^^^^----- help: consider removing `'static`: `&[u8]` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:22:19 + | +LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:24:19 + | +LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:26:25 + | +LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:30:29 + | +LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:32:25 + | +LL | static STATIC_VAR_SIX: &'static u8 = &5; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:34:28 + | +LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; + | -^^^^^^^---- help: consider removing `'static`: `&Foo` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:36:27 + | +LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. + | -^^^^^^^----- help: consider removing `'static`: `&[u8]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:38:27 + | +LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:40:27 + | +LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs new file mode 100644 index 0000000000000..f57dd58e230a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs @@ -0,0 +1,13 @@ +// these are rustfixable, but run-rustfix tests cannot handle them + +const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + +const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + +static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + +static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + +static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr new file mode 100644 index 0000000000000..afc853dcfce83 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr @@ -0,0 +1,64 @@ +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:3:18 + | +LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` + | + = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:3:30 + | +LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:5:29 + | +LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:5:39 + | +LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:7:40 + | +LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:7:55 + | +LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:9:26 + | +LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:9:38 + | +LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:11:37 + | +LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:11:47 + | +LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs new file mode 100644 index 0000000000000..b523fa5b711ae --- /dev/null +++ b/src/tools/clippy/tests/ui/regex.rs @@ -0,0 +1,79 @@ +#![allow(unused)] +#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_macro)] + +extern crate regex; + +use regex::bytes::{Regex as BRegex, RegexBuilder as BRegexBuilder, RegexSet as BRegexSet}; +use regex::{Regex, RegexBuilder, RegexSet}; + +const OPENING_PAREN: &str = "("; +const NOT_A_REAL_REGEX: &str = "foobar"; + +fn syntax_error() { + let pipe_in_wrong_position = Regex::new("|"); + let pipe_in_wrong_position_builder = RegexBuilder::new("|"); + let wrong_char_ranice = Regex::new("[z-a]"); + let some_unicode = Regex::new("[é-è]"); + + let some_regex = Regex::new(OPENING_PAREN); + + let binary_pipe_in_wrong_position = BRegex::new("|"); + let some_binary_regex = BRegex::new(OPENING_PAREN); + let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); + + let closing_paren = ")"; + let not_linted = Regex::new(closing_paren); + + let set = RegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]); + let bset = BRegexSet::new(&[ + r"[a-z]+@[a-z]+\.(com|org|net)", + r"[a-z]+\.(com|org|net)", + r".", // regression test + ]); + + let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); + let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); + + let raw_string_error = Regex::new(r"[...\/...]"); + let raw_string_error = Regex::new(r#"[...\/...]"#); +} + +fn trivial_regex() { + let trivial_eq = Regex::new("^foobar$"); + + let trivial_eq_builder = RegexBuilder::new("^foobar$"); + + let trivial_starts_with = Regex::new("^foobar"); + + let trivial_ends_with = Regex::new("foobar$"); + + let trivial_contains = Regex::new("foobar"); + + let trivial_contains = Regex::new(NOT_A_REAL_REGEX); + + let trivial_backslash = Regex::new("a\\.b"); + + // unlikely corner cases + let trivial_empty = Regex::new(""); + + let trivial_empty = Regex::new("^"); + + let trivial_empty = Regex::new("^$"); + + let binary_trivial_empty = BRegex::new("^$"); + + // non-trivial regexes + let non_trivial_dot = Regex::new("a.b"); + let non_trivial_dot_builder = RegexBuilder::new("a.b"); + let non_trivial_eq = Regex::new("^foo|bar$"); + let non_trivial_starts_with = Regex::new("^foo|bar"); + let non_trivial_ends_with = Regex::new("^foo|bar"); + let non_trivial_ends_with = Regex::new("foo|bar"); + let non_trivial_binary = BRegex::new("foo|bar"); + let non_trivial_binary_builder = BRegexBuilder::new("foo|bar"); +} + +fn main() { + syntax_error(); + trivial_regex(); +} diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr new file mode 100644 index 0000000000000..1394a9b63bc61 --- /dev/null +++ b/src/tools/clippy/tests/ui/regex.stderr @@ -0,0 +1,171 @@ +error: trivial regex + --> $DIR/regex.rs:13:45 + | +LL | let pipe_in_wrong_position = Regex::new("|"); + | ^^^ + | + = note: `-D clippy::trivial-regex` implied by `-D warnings` + = help: the regex is unlikely to be useful as it is + +error: trivial regex + --> $DIR/regex.rs:14:60 + | +LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); + | ^^^ + | + = help: the regex is unlikely to be useful as it is + +error: regex syntax error: invalid character class range, the start must be <= the end + --> $DIR/regex.rs:15:42 + | +LL | let wrong_char_ranice = Regex::new("[z-a]"); + | ^^^ + | + = note: `-D clippy::invalid-regex` implied by `-D warnings` + +error: regex syntax error: invalid character class range, the start must be <= the end + --> $DIR/regex.rs:16:37 + | +LL | let some_unicode = Regex::new("[é-è]"); + | ^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:18:33 + | +LL | let some_regex = Regex::new(OPENING_PAREN); + | ^^^^^^^^^^^^^ + +error: trivial regex + --> $DIR/regex.rs:20:53 + | +LL | let binary_pipe_in_wrong_position = BRegex::new("|"); + | ^^^ + | + = help: the regex is unlikely to be useful as it is + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:21:41 + | +LL | let some_binary_regex = BRegex::new(OPENING_PAREN); + | ^^^^^^^^^^^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:22:56 + | +LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); + | ^^^^^^^^^^^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:34:37 + | +LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); + | ^^^^^^^^^^^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:35:39 + | +LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); + | ^^^^^^^^^^^^^ + +error: regex syntax error: unrecognized escape sequence + --> $DIR/regex.rs:37:45 + | +LL | let raw_string_error = Regex::new(r"[...//...]"); + | ^^ + +error: regex syntax error: unrecognized escape sequence + --> $DIR/regex.rs:38:46 + | +LL | let raw_string_error = Regex::new(r#"[...//...]"#); + | ^^ + +error: trivial regex + --> $DIR/regex.rs:42:33 + | +LL | let trivial_eq = Regex::new("^foobar$"); + | ^^^^^^^^^^ + | + = help: consider using `==` on `str`s + +error: trivial regex + --> $DIR/regex.rs:44:48 + | +LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); + | ^^^^^^^^^^ + | + = help: consider using `==` on `str`s + +error: trivial regex + --> $DIR/regex.rs:46:42 + | +LL | let trivial_starts_with = Regex::new("^foobar"); + | ^^^^^^^^^ + | + = help: consider using `str::starts_with` + +error: trivial regex + --> $DIR/regex.rs:48:40 + | +LL | let trivial_ends_with = Regex::new("foobar$"); + | ^^^^^^^^^ + | + = help: consider using `str::ends_with` + +error: trivial regex + --> $DIR/regex.rs:50:39 + | +LL | let trivial_contains = Regex::new("foobar"); + | ^^^^^^^^ + | + = help: consider using `str::contains` + +error: trivial regex + --> $DIR/regex.rs:52:39 + | +LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using `str::contains` + +error: trivial regex + --> $DIR/regex.rs:54:40 + | +LL | let trivial_backslash = Regex::new("a/.b"); + | ^^^^^^^ + | + = help: consider using `str::contains` + +error: trivial regex + --> $DIR/regex.rs:57:36 + | +LL | let trivial_empty = Regex::new(""); + | ^^ + | + = help: the regex is unlikely to be useful as it is + +error: trivial regex + --> $DIR/regex.rs:59:36 + | +LL | let trivial_empty = Regex::new("^"); + | ^^^ + | + = help: the regex is unlikely to be useful as it is + +error: trivial regex + --> $DIR/regex.rs:61:36 + | +LL | let trivial_empty = Regex::new("^$"); + | ^^^^ + | + = help: consider using `str::is_empty` + +error: trivial regex + --> $DIR/regex.rs:63:44 + | +LL | let binary_trivial_empty = BRegex::new("^$"); + | ^^^^ + | + = help: consider using `str::is_empty` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed new file mode 100644 index 0000000000000..13fbb6e2a6eed --- /dev/null +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -0,0 +1,19 @@ +//! Test for Clippy lint renames. +// run-rustfix + +#![allow(dead_code)] +// allow the new lint name here, to test if the new name works +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_static_lifetimes)] +// warn for the old lint name here, to test if the renaming worked +#![warn(clippy::cognitive_complexity)] + +#[warn(clippy::module_name_repetitions)] +fn main() {} + +#[warn(clippy::new_without_default)] +struct Foo; + +#[warn(clippy::redundant_static_lifetimes)] +fn foo() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs new file mode 100644 index 0000000000000..cbd3b1e91666a --- /dev/null +++ b/src/tools/clippy/tests/ui/rename.rs @@ -0,0 +1,19 @@ +//! Test for Clippy lint renames. +// run-rustfix + +#![allow(dead_code)] +// allow the new lint name here, to test if the new name works +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_static_lifetimes)] +// warn for the old lint name here, to test if the renaming worked +#![warn(clippy::cyclomatic_complexity)] + +#[warn(clippy::stutter)] +fn main() {} + +#[warn(clippy::new_without_default_derive)] +struct Foo; + +#[warn(clippy::const_static_lifetime)] +fn foo() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr new file mode 100644 index 0000000000000..a9e803946041e --- /dev/null +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -0,0 +1,34 @@ +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` + --> $DIR/rename.rs:10:9 + | +LL | #![warn(clippy::cyclomatic_complexity)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` + --> $DIR/rename.rs:12:8 + | +LL | #[warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` + +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` + --> $DIR/rename.rs:15:8 + | +LL | #[warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` + +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` + --> $DIR/rename.rs:18:8 + | +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` + --> $DIR/rename.rs:10:9 + | +LL | #![warn(clippy::cyclomatic_complexity)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed b/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed new file mode 100644 index 0000000000000..cb91b841d2cb2 --- /dev/null +++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed @@ -0,0 +1,4 @@ +// run-rustfix + +#[clippy::cognitive_complexity = "1"] +fn main() {} diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.rs b/src/tools/clippy/tests/ui/renamed_builtin_attr.rs new file mode 100644 index 0000000000000..b3ce2758067cf --- /dev/null +++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.rs @@ -0,0 +1,4 @@ +// run-rustfix + +#[clippy::cyclomatic_complexity = "1"] +fn main() {} diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr new file mode 100644 index 0000000000000..a399ff52fb8b6 --- /dev/null +++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr @@ -0,0 +1,8 @@ +error: Usage of deprecated attribute + --> $DIR/renamed_builtin_attr.rs:3:11 + | +LL | #[clippy::cyclomatic_complexity = "1"] + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/repl_uninit.rs b/src/tools/clippy/tests/ui/repl_uninit.rs new file mode 100644 index 0000000000000..346972b7bb4e0 --- /dev/null +++ b/src/tools/clippy/tests/ui/repl_uninit.rs @@ -0,0 +1,35 @@ +#![allow(deprecated, invalid_value)] +#![warn(clippy::all)] + +use std::mem; + +fn might_panic(x: X) -> X { + // in practice this would be a possibly-panicky operation + x +} + +fn main() { + let mut v = vec![0i32; 4]; + // the following is UB if `might_panic` panics + unsafe { + let taken_v = mem::replace(&mut v, mem::uninitialized()); + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } + + unsafe { + let taken_v = mem::replace(&mut v, mem::zeroed()); + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } + + // this is silly but OK, because usize is a primitive type + let mut u: usize = 42; + let uref = &mut u; + let taken_u = unsafe { mem::replace(uref, mem::zeroed()) }; + *uref = taken_u + 1; + + // this is still not OK, because uninit + let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) }; + *uref = taken_u + 1; +} diff --git a/src/tools/clippy/tests/ui/repl_uninit.stderr b/src/tools/clippy/tests/ui/repl_uninit.stderr new file mode 100644 index 0000000000000..c1f55d7601e5c --- /dev/null +++ b/src/tools/clippy/tests/ui/repl_uninit.stderr @@ -0,0 +1,27 @@ +error: replacing with `mem::uninitialized()` + --> $DIR/repl_uninit.rs:15:23 + | +LL | let taken_v = mem::replace(&mut v, mem::uninitialized()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings` + = help: consider using the `take_mut` crate instead + +error: replacing with `mem::zeroed()` + --> $DIR/repl_uninit.rs:21:23 + | +LL | let taken_v = mem::replace(&mut v, mem::zeroed()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a default value or the `take_mut` crate instead + +error: replacing with `mem::uninitialized()` + --> $DIR/repl_uninit.rs:33:28 + | +LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `take_mut` crate instead + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs new file mode 100644 index 0000000000000..38fc9969804fa --- /dev/null +++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -0,0 +1,42 @@ +#![warn(clippy::rest_pat_in_fully_bound_structs)] + +struct A { + a: i32, + b: i64, + c: &'static str, +} + +macro_rules! foo { + ($param:expr) => { + match $param { + A { a: 0, b: 0, c: "", .. } => {}, + _ => {}, + } + }; +} + +fn main() { + let a_struct = A { a: 5, b: 42, c: "A" }; + + match a_struct { + A { a: 5, b: 42, c: "", .. } => {}, // Lint + A { a: 0, b: 0, c: "", .. } => {}, // Lint + _ => {}, + } + + match a_struct { + A { a: 5, b: 42, .. } => {}, + A { a: 0, b: 0, c: "", .. } => {}, // Lint + _ => {}, + } + + // No lint + match a_struct { + A { a: 5, .. } => {}, + A { a: 0, b: 0, .. } => {}, + _ => {}, + } + + // No lint + foo!(a_struct); +} diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr new file mode 100644 index 0000000000000..57ebd47f8c7ac --- /dev/null +++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -0,0 +1,27 @@ +error: unnecessary use of `..` pattern in struct binding. All fields were already bound + --> $DIR/rest_pat_in_fully_bound_structs.rs:22:9 + | +LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings` + = help: consider removing `..` from this binding + +error: unnecessary use of `..` pattern in struct binding. All fields were already bound + --> $DIR/rest_pat_in_fully_bound_structs.rs:23:9 + | +LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `..` from this binding + +error: unnecessary use of `..` pattern in struct binding. All fields were already bound + --> $DIR/rest_pat_in_fully_bound_structs.rs:29:9 + | +LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `..` from this binding + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.fixed b/src/tools/clippy/tests/ui/result_map_or_into_option.fixed new file mode 100644 index 0000000000000..331531b5165f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_or_into_option.fixed @@ -0,0 +1,19 @@ +// run-rustfix + +#![warn(clippy::result_map_or_into_option)] + +fn main() { + let opt: Result = Ok(1); + let _ = opt.ok(); + + let rewrap = |s: u32| -> Option { Some(s) }; + + // A non-Some `f` arg should not emit the lint + let opt: Result = Ok(1); + let _ = opt.map_or(None, rewrap); + + // A non-Some `f` closure where the argument is not used as the + // return should not emit the lint + let opt: Result = Ok(1); + opt.map_or(None, |_x| Some(1)); +} diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.rs b/src/tools/clippy/tests/ui/result_map_or_into_option.rs new file mode 100644 index 0000000000000..3058480e2ad3d --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_or_into_option.rs @@ -0,0 +1,19 @@ +// run-rustfix + +#![warn(clippy::result_map_or_into_option)] + +fn main() { + let opt: Result = Ok(1); + let _ = opt.map_or(None, Some); + + let rewrap = |s: u32| -> Option { Some(s) }; + + // A non-Some `f` arg should not emit the lint + let opt: Result = Ok(1); + let _ = opt.map_or(None, rewrap); + + // A non-Some `f` closure where the argument is not used as the + // return should not emit the lint + let opt: Result = Ok(1); + opt.map_or(None, |_x| Some(1)); +} diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.stderr b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr new file mode 100644 index 0000000000000..febf32147d132 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr @@ -0,0 +1,10 @@ +error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead + --> $DIR/result_map_or_into_option.rs:7:13 + | +LL | let _ = opt.map_or(None, Some); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | + = note: `-D clippy::result-map-or-into-option` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed new file mode 100644 index 0000000000000..1d0a3ecd0ff8d --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed @@ -0,0 +1,80 @@ +// run-rustfix + +#![warn(clippy::result_map_unit_fn)] +#![allow(unused)] + +fn do_nothing(_: T) {} + +fn diverge(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +struct HasResult { + field: Result, +} + +impl HasResult { + fn do_result_nothing(self: &Self, value: usize) {} + + fn do_result_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} + +#[rustfmt::skip] +fn result_map_unit_fn() { + let x = HasResult { field: Ok(10) }; + + x.field.map(plus_one); + let _: Result<(), usize> = x.field.map(do_nothing); + + if let Ok(x_field) = x.field { do_nothing(x_field) } + + if let Ok(x_field) = x.field { do_nothing(x_field) } + + if let Ok(x_field) = x.field { diverge(x_field) } + + let captured = 10; + if let Ok(value) = x.field { do_nothing(value + captured) }; + let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured)); + + if let Ok(value) = x.field { x.do_result_nothing(value + captured) } + + if let Ok(value) = x.field { x.do_result_plus_one(value + captured); } + + + if let Ok(value) = x.field { do_nothing(value + captured) } + + if let Ok(value) = x.field { do_nothing(value + captured) } + + if let Ok(value) = x.field { do_nothing(value + captured); } + + if let Ok(value) = x.field { do_nothing(value + captured); } + + + if let Ok(value) = x.field { diverge(value + captured) } + + if let Ok(value) = x.field { diverge(value + captured) } + + if let Ok(value) = x.field { diverge(value + captured); } + + if let Ok(value) = x.field { diverge(value + captured); } + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + if let Ok(value) = x.field { let y = plus_one(value + captured); } + + if let Ok(value) = x.field { plus_one(value + captured); } + + if let Ok(value) = x.field { plus_one(value + captured); } + + + if let Ok(ref value) = x.field { do_nothing(value + captured) } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs new file mode 100644 index 0000000000000..2fe18f923f08f --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs @@ -0,0 +1,80 @@ +// run-rustfix + +#![warn(clippy::result_map_unit_fn)] +#![allow(unused)] + +fn do_nothing(_: T) {} + +fn diverge(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +struct HasResult { + field: Result, +} + +impl HasResult { + fn do_result_nothing(self: &Self, value: usize) {} + + fn do_result_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} + +#[rustfmt::skip] +fn result_map_unit_fn() { + let x = HasResult { field: Ok(10) }; + + x.field.map(plus_one); + let _: Result<(), usize> = x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(diverge); + + let captured = 10; + if let Ok(value) = x.field { do_nothing(value + captured) }; + let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| x.do_result_nothing(value + captured)); + + x.field.map(|value| { x.do_result_plus_one(value + captured); }); + + + x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| { do_nothing(value + captured) }); + + x.field.map(|value| { do_nothing(value + captured); }); + + x.field.map(|value| { { do_nothing(value + captured); } }); + + + x.field.map(|value| diverge(value + captured)); + + x.field.map(|value| { diverge(value + captured) }); + + x.field.map(|value| { diverge(value + captured); }); + + x.field.map(|value| { { diverge(value + captured); } }); + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + x.field.map(|value| { let y = plus_one(value + captured); }); + + x.field.map(|value| { plus_one(value + captured); }); + + x.field.map(|value| { { plus_one(value + captured); } }); + + + x.field.map(|ref value| { do_nothing(value + captured) }); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr new file mode 100644 index 0000000000000..467e00263cd3a --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr @@ -0,0 +1,140 @@ +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:35:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | + = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:37:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:39:5 + | +LL | x.field.map(diverge); + | ^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:45:5 + | +LL | x.field.map(|value| x.do_result_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:47:5 + | +LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:50:5 + | +LL | x.field.map(|value| do_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:52:5 + | +LL | x.field.map(|value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:54:5 + | +LL | x.field.map(|value| { do_nothing(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:56:5 + | +LL | x.field.map(|value| { { do_nothing(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:59:5 + | +LL | x.field.map(|value| diverge(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:61:5 + | +LL | x.field.map(|value| { diverge(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:63:5 + | +LL | x.field.map(|value| { diverge(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:65:5 + | +LL | x.field.map(|value| { { diverge(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:70:5 + | +LL | x.field.map(|value| { let y = plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:72:5 + | +LL | x.field.map(|value| { plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:74:5 + | +LL | x.field.map(|value| { { plus_one(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:77:5 + | +LL | x.field.map(|ref value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs new file mode 100644 index 0000000000000..b197c609d7bfc --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs @@ -0,0 +1,46 @@ +#![warn(clippy::result_map_unit_fn)] +#![feature(never_type)] +#![allow(unused)] + +struct HasResult { + field: Result, +} + +fn do_nothing(_: T) {} + +fn diverge(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +#[rustfmt::skip] +fn result_map_unit_fn() { + let x = HasResult { field: Ok(10) }; + + x.field.map(|value| { do_nothing(value); do_nothing(value) }); + + x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + + // Suggestion for the let block should be `{ ... }` as it's too difficult to build a + // proper suggestion for these cases + x.field.map(|value| { + do_nothing(value); + do_nothing(value) + }); + x.field.map(|value| { do_nothing(value); do_nothing(value); }); + + // The following should suggest `if let Ok(_X) ...` as it's difficult to generate a proper let variable name for them + let res: Result = Ok(42).map(diverge); + "12".parse::().map(diverge); + + let res: Result<(), usize> = Ok(plus_one(1)).map(do_nothing); + + // Should suggest `if let Ok(_y) ...` to not override the existing foo variable + let y: Result = Ok(42); + y.map(do_nothing); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr new file mode 100644 index 0000000000000..b23cc608621d0 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr @@ -0,0 +1,58 @@ +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:23:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { ... }` + | + = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:25:5 + | +LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { ... }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:29:5 + | +LL | x.field.map(|value| { + | _____^ + | |_____| + | || +LL | || do_nothing(value); +LL | || do_nothing(value) +LL | || }); + | ||______^- help: try this: `if let Ok(value) = x.field { ... }` + | |_______| + | + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:33:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { ... }` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:37:5 + | +LL | "12".parse::().map(diverge); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(a) = "12".parse::() { diverge(a) }` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:43:5 + | +LL | y.map(do_nothing); + | ^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(_y) = y { do_nothing(_y) }` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed new file mode 100644 index 0000000000000..ee2cbc3cf540e --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (21..=42).rev().for_each(|x| println!("{}", x)); + let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in (-42..=-21).rev() {} + for _ in (21u32..42u32).rev() {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs new file mode 100644 index 0000000000000..6ed5ca6daa0e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (42..=21).for_each(|x| println!("{}", x)); + let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in -21..=-42 {} + for _ in 42u32..21u32 {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr new file mode 100644 index 0000000000000..97933b8ff8515 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr @@ -0,0 +1,47 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:7:5 + | +LL | (42..=21).for_each(|x| println!("{}", x)); + | ^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | (21..=42).rev().for_each(|x| println!("{}", x)); + | ^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:8:13 + | +LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:10:14 + | +LL | for _ in -21..=-42 {} + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (-42..=-21).rev() {} + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:11:14 + | +LL | for _ in 42u32..21u32 {} + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (21u32..42u32).rev() {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed new file mode 100644 index 0000000000000..f1503ed6d12f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + const MAX_LEN: usize = 42; + + for i in (0..10).rev() { + println!("{}", i); + } + + for i in (0..=10).rev() { + println!("{}", i); + } + + for i in (0..MAX_LEN).rev() { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (0..10).rev().map(|x| x * 2) { + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in (5 + 4..10).rev() { + println!("{}", i); + } + + for i in ((3 - 1)..(5 + 2)).rev() { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } +} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.rs new file mode 100644 index 0000000000000..a733788dc22c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.rs @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + const MAX_LEN: usize = 42; + + for i in 10..0 { + println!("{}", i); + } + + for i in 10..=0 { + println!("{}", i); + } + + for i in MAX_LEN..0 { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (10..0).map(|x| x * 2) { + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in 10..5 + 4 { + println!("{}", i); + } + + for i in (5 + 2)..(3 - 1) { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } +} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr new file mode 100644 index 0000000000000..e89e040a0ff9e --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -0,0 +1,69 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:7:14 + | +LL | for i in 10..0 { + | ^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..10).rev() { + | ^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:11:14 + | +LL | for i in 10..=0 { + | ^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..=10).rev() { + | ^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:15:14 + | +LL | for i in MAX_LEN..0 { + | ^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..MAX_LEN).rev() { + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:34:14 + | +LL | for i in (10..0).map(|x| x * 2) { + | ^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..10).rev().map(|x| x * 2) { + | ^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:39:14 + | +LL | for i in 10..5 + 4 { + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (5 + 4..10).rev() { + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:43:14 + | +LL | for i in (5 + 2)..(3 - 1) { + | ^^^^^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in ((3 - 1)..(5 + 2)).rev() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.rs new file mode 100644 index 0000000000000..c4c572244168b --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.rs @@ -0,0 +1,11 @@ +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + for i in 5..5 { + println!("{}", i); + } + + for i in (5 + 2)..(8 - 1) { + println!("{}", i); + } +} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.stderr new file mode 100644 index 0000000000000..30095d20cfd41 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.stderr @@ -0,0 +1,16 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:4:14 + | +LL | for i in 5..5 { + | ^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:8:14 + | +LL | for i in (5 + 2)..(8 - 1) { + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.rs new file mode 100644 index 0000000000000..c9ca4c4766831 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.rs @@ -0,0 +1,15 @@ +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; +const SOME_NUM: usize = 3; + +fn main() { + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[3usize..=1usize]; + let _ = &arr[SOME_NUM..1]; + let _ = &arr[3..3]; + + for _ in ANSWER..ANSWER {} +} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr new file mode 100644 index 0000000000000..12e5483ecdfff --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -0,0 +1,34 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:7:13 + | +LL | let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:10:18 + | +LL | let _ = &arr[3usize..=1usize]; + | ^^^^^^^^^^^^^^^ + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:11:18 + | +LL | let _ = &arr[SOME_NUM..1]; + | ^^^^^^^^^^^ + +error: this range is empty and using it to index a slice will always yield an empty slice + --> $DIR/reversed_empty_ranges_unfixable.rs:12:18 + | +LL | let _ = &arr[3..3]; + | ^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:14:14 + | +LL | for _ in ANSWER..ANSWER {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 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 new file mode 100644 index 0000000000000..686867cf5c6f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -0,0 +1,80 @@ +#![warn(clippy::same_functions_in_if_condition)] +#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. +#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks + +fn function() -> bool { + true +} + +fn fn_arg(_arg: u8) -> bool { + true +} + +struct Struct; + +impl Struct { + fn method(&self) -> bool { + true + } + fn method_arg(&self, _arg: u8) -> bool { + true + } +} + +fn ifs_same_cond_fn() { + let a = 0; + let obj = Struct; + + if function() { + } else if function() { + //~ ERROR ifs same condition + } + + if fn_arg(a) { + } else if fn_arg(a) { + //~ ERROR ifs same condition + } + + if obj.method() { + } else if obj.method() { + //~ ERROR ifs same condition + } + + if obj.method_arg(a) { + } else if obj.method_arg(a) { + //~ ERROR ifs same condition + } + + let mut v = vec![1]; + if v.pop() == None { + //~ ERROR ifs same condition + } else if v.pop() == None { + } + + if v.len() == 42 { + //~ ERROR ifs same condition + } else if v.len() == 42 { + } + + if v.len() == 1 { + // ok, different conditions + } else if v.len() == 2 { + } + + if fn_arg(0) { + // ok, different arguments. + } else if fn_arg(1) { + } + + if obj.method_arg(0) { + // ok, different arguments. + } else if obj.method_arg(1) { + } + + if a == 1 { + // ok, warning is on `ifs_same_cond` behalf. + } else if a == 1 { + } +} + +fn main() {} 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 new file mode 100644 index 0000000000000..363a03846d236 --- /dev/null +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -0,0 +1,75 @@ +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:29:15 + | +LL | } else if function() { + | ^^^^^^^^^^ + | + = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` +note: same as this + --> $DIR/same_functions_in_if_condition.rs:28:8 + | +LL | if function() { + | ^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:34:15 + | +LL | } else if fn_arg(a) { + | ^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:33:8 + | +LL | if fn_arg(a) { + | ^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:39:15 + | +LL | } else if obj.method() { + | ^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:38:8 + | +LL | if obj.method() { + | ^^^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:44:15 + | +LL | } else if obj.method_arg(a) { + | ^^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:43:8 + | +LL | if obj.method_arg(a) { + | ^^^^^^^^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:51:15 + | +LL | } else if v.pop() == None { + | ^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:49:8 + | +LL | if v.pop() == None { + | ^^^^^^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:56:15 + | +LL | } else if v.len() == 42 { + | ^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:54:8 + | +LL | if v.len() == 42 { + | ^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/serde.rs b/src/tools/clippy/tests/ui/serde.rs new file mode 100644 index 0000000000000..5843344eba89a --- /dev/null +++ b/src/tools/clippy/tests/ui/serde.rs @@ -0,0 +1,47 @@ +#![warn(clippy::serde_api_misuse)] +#![allow(dead_code)] + +extern crate serde; + +struct A; + +impl<'de> serde::de::Visitor<'de> for A { + type Value = (); + + fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + unimplemented!() + } + + fn visit_str(self, _v: &str) -> Result + where + E: serde::de::Error, + { + unimplemented!() + } + + fn visit_string(self, _v: String) -> Result + where + E: serde::de::Error, + { + unimplemented!() + } +} + +struct B; + +impl<'de> serde::de::Visitor<'de> for B { + type Value = (); + + fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + unimplemented!() + } + + fn visit_string(self, _v: String) -> Result + where + E: serde::de::Error, + { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/serde.stderr b/src/tools/clippy/tests/ui/serde.stderr new file mode 100644 index 0000000000000..760c9c9908a6f --- /dev/null +++ b/src/tools/clippy/tests/ui/serde.stderr @@ -0,0 +1,15 @@ +error: you should not implement `visit_string` without also implementing `visit_str` + --> $DIR/serde.rs:39:5 + | +LL | / fn visit_string(self, _v: String) -> Result +LL | | where +LL | | E: serde::de::Error, +LL | | { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::serde-api-misuse` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs new file mode 100644 index 0000000000000..bd91ae4e9340c --- /dev/null +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -0,0 +1,53 @@ +#![warn( + clippy::all, + clippy::pedantic, + clippy::shadow_same, + clippy::shadow_reuse, + clippy::shadow_unrelated +)] +#![allow( + unused_parens, + unused_variables, + clippy::missing_docs_in_private_items, + clippy::single_match +)] + +fn id(x: T) -> T { + x +} + +#[must_use] +fn first(x: (isize, isize)) -> isize { + x.0 +} + +fn main() { + let mut x = 1; + let x = &mut x; + let x = { x }; + let x = (&*x); + let x = { *x + 1 }; + let x = id(x); + let x = (1, x); + let x = first(x); + let y = 1; + let x = y; + + let x; + x = 42; + + let o = Some(1_u8); + + if let Some(p) = o { + assert_eq!(1, p); + } + match o { + Some(p) => p, // no error, because the p above is in its own scope + None => 0, + }; + + match (x, o) { + (1, Some(a)) | (a, Some(1)) => (), // no error though `a` appears twice + _ => (), + } +} diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr new file mode 100644 index 0000000000000..7fa58cf76499b --- /dev/null +++ b/src/tools/clippy/tests/ui/shadow.stderr @@ -0,0 +1,138 @@ +error: `x` is shadowed by itself in `&mut x` + --> $DIR/shadow.rs:26:5 + | +LL | let x = &mut x; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::shadow-same` implied by `-D warnings` +note: previous binding is here + --> $DIR/shadow.rs:25:13 + | +LL | let mut x = 1; + | ^ + +error: `x` is shadowed by itself in `{ x }` + --> $DIR/shadow.rs:27:5 + | +LL | let x = { x }; + | ^^^^^^^^^^^^^^ + | +note: previous binding is here + --> $DIR/shadow.rs:26:9 + | +LL | let x = &mut x; + | ^ + +error: `x` is shadowed by itself in `(&*x)` + --> $DIR/shadow.rs:28:5 + | +LL | let x = (&*x); + | ^^^^^^^^^^^^^^ + | +note: previous binding is here + --> $DIR/shadow.rs:27:9 + | +LL | let x = { x }; + | ^ + +error: `x` is shadowed by `{ *x + 1 }` which reuses the original value + --> $DIR/shadow.rs:29:9 + | +LL | let x = { *x + 1 }; + | ^ + | + = note: `-D clippy::shadow-reuse` implied by `-D warnings` +note: initialization happens here + --> $DIR/shadow.rs:29:13 + | +LL | let x = { *x + 1 }; + | ^^^^^^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:28:9 + | +LL | let x = (&*x); + | ^ + +error: `x` is shadowed by `id(x)` which reuses the original value + --> $DIR/shadow.rs:30:9 + | +LL | let x = id(x); + | ^ + | +note: initialization happens here + --> $DIR/shadow.rs:30:13 + | +LL | let x = id(x); + | ^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:29:9 + | +LL | let x = { *x + 1 }; + | ^ + +error: `x` is shadowed by `(1, x)` which reuses the original value + --> $DIR/shadow.rs:31:9 + | +LL | let x = (1, x); + | ^ + | +note: initialization happens here + --> $DIR/shadow.rs:31:13 + | +LL | let x = (1, x); + | ^^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:30:9 + | +LL | let x = id(x); + | ^ + +error: `x` is shadowed by `first(x)` which reuses the original value + --> $DIR/shadow.rs:32:9 + | +LL | let x = first(x); + | ^ + | +note: initialization happens here + --> $DIR/shadow.rs:32:13 + | +LL | let x = first(x); + | ^^^^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:31:9 + | +LL | let x = (1, x); + | ^ + +error: `x` is shadowed by `y` + --> $DIR/shadow.rs:34:9 + | +LL | let x = y; + | ^ + | + = note: `-D clippy::shadow-unrelated` implied by `-D warnings` +note: initialization happens here + --> $DIR/shadow.rs:34:13 + | +LL | let x = y; + | ^ +note: previous binding is here + --> $DIR/shadow.rs:32:9 + | +LL | let x = first(x); + | ^ + +error: `x` shadows a previous declaration + --> $DIR/shadow.rs:36:5 + | +LL | let x; + | ^^^^^^ + | +note: previous binding is here + --> $DIR/shadow.rs:34:9 + | +LL | let x = y; + | ^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.fixed b/src/tools/clippy/tests/ui/short_circuit_statement.fixed new file mode 100644 index 0000000000000..af0a397bd1aff --- /dev/null +++ b/src/tools/clippy/tests/ui/short_circuit_statement.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::short_circuit_statement)] +#![allow(clippy::nonminimal_bool)] + +fn main() { + if f() { g(); } + if !f() { g(); } + if !(1 == 2) { g(); } +} + +fn f() -> bool { + true +} + +fn g() -> bool { + false +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.rs b/src/tools/clippy/tests/ui/short_circuit_statement.rs new file mode 100644 index 0000000000000..73a55bf1f5e27 --- /dev/null +++ b/src/tools/clippy/tests/ui/short_circuit_statement.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::short_circuit_statement)] +#![allow(clippy::nonminimal_bool)] + +fn main() { + f() && g(); + f() || g(); + 1 == 2 || g(); +} + +fn f() -> bool { + true +} + +fn g() -> bool { + false +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.stderr b/src/tools/clippy/tests/ui/short_circuit_statement.stderr new file mode 100644 index 0000000000000..0a3f60c3d132d --- /dev/null +++ b/src/tools/clippy/tests/ui/short_circuit_statement.stderr @@ -0,0 +1,22 @@ +error: boolean short circuit operator in statement may be clearer using an explicit test + --> $DIR/short_circuit_statement.rs:7:5 + | +LL | f() && g(); + | ^^^^^^^^^^^ help: replace it with: `if f() { g(); }` + | + = note: `-D clippy::short-circuit-statement` implied by `-D warnings` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> $DIR/short_circuit_statement.rs:8:5 + | +LL | f() || g(); + | ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> $DIR/short_circuit_statement.rs:9:5 + | +LL | 1 == 2 || g(); + | ^^^^^^^^^^^^^^ help: replace it with: `if !(1 == 2) { g(); }` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/similar_names.rs b/src/tools/clippy/tests/ui/similar_names.rs new file mode 100644 index 0000000000000..6796b15289ecd --- /dev/null +++ b/src/tools/clippy/tests/ui/similar_names.rs @@ -0,0 +1,103 @@ +#![warn(clippy::similar_names)] +#![allow(unused, clippy::println_empty_string)] + +struct Foo { + apple: i32, + bpple: i32, +} + +fn main() { + let specter: i32; + let spectre: i32; + + let apple: i32; + + let bpple: i32; + + let cpple: i32; + + let a_bar: i32; + let b_bar: i32; + let c_bar: i32; + + let items = [5]; + for item in &items { + loop {} + } + + let foo_x: i32; + let foo_y: i32; + + let rhs: i32; + let lhs: i32; + + let bla_rhs: i32; + let bla_lhs: i32; + + let blubrhs: i32; + let blublhs: i32; + + let blubx: i32; + let bluby: i32; + + let cake: i32; + let cakes: i32; + let coke: i32; + + match 5 { + cheese @ 1 => {}, + rabbit => panic!(), + } + let cheese: i32; + match (42, 43) { + (cheese1, 1) => {}, + (cheese2, 2) => panic!(), + _ => println!(""), + } + let ipv4: i32; + let ipv6: i32; + let abcd1: i32; + let abdc2: i32; + let xyz1abc: i32; + let xyz2abc: i32; + let xyzeabc: i32; + + let parser: i32; + let parsed: i32; + let parsee: i32; + + let setter: i32; + let getter: i32; + let tx1: i32; + let rx1: i32; + let tx_cake: i32; + let rx_cake: i32; +} + +fn foo() { + let Foo { apple, bpple } = unimplemented!(); + let Foo { + apple: spring, + bpple: sprang, + } = unimplemented!(); +} + +// false positive similar_names (#3057, #2651) +// clippy claimed total_reg_src_size and total_size and +// numb_reg_src_checkouts and total_bin_size were similar +#[derive(Debug, Clone)] +pub(crate) struct DirSizes { + pub(crate) total_size: u64, + pub(crate) numb_bins: u64, + pub(crate) total_bin_size: u64, + pub(crate) total_reg_size: u64, + pub(crate) total_git_db_size: u64, + pub(crate) total_git_repos_bare_size: u64, + pub(crate) numb_git_repos_bare_repos: u64, + pub(crate) numb_git_checkouts: u64, + pub(crate) total_git_chk_size: u64, + pub(crate) total_reg_cache_size: u64, + pub(crate) total_reg_src_size: u64, + pub(crate) numb_reg_cache_entries: u64, + pub(crate) numb_reg_src_checkouts: u64, +} diff --git a/src/tools/clippy/tests/ui/similar_names.stderr b/src/tools/clippy/tests/ui/similar_names.stderr new file mode 100644 index 0000000000000..0256f126a94fc --- /dev/null +++ b/src/tools/clippy/tests/ui/similar_names.stderr @@ -0,0 +1,107 @@ +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:15:9 + | +LL | let bpple: i32; + | ^^^^^ + | + = note: `-D clippy::similar-names` implied by `-D warnings` +note: existing binding defined here + --> $DIR/similar_names.rs:13:9 + | +LL | let apple: i32; + | ^^^^^ +help: separate the discriminating character by an underscore like: `b_pple` + --> $DIR/similar_names.rs:15:9 + | +LL | let bpple: i32; + | ^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:17:9 + | +LL | let cpple: i32; + | ^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:13:9 + | +LL | let apple: i32; + | ^^^^^ +help: separate the discriminating character by an underscore like: `c_pple` + --> $DIR/similar_names.rs:17:9 + | +LL | let cpple: i32; + | ^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:41:9 + | +LL | let bluby: i32; + | ^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:40:9 + | +LL | let blubx: i32; + | ^^^^^ +help: separate the discriminating character by an underscore like: `blub_y` + --> $DIR/similar_names.rs:41:9 + | +LL | let bluby: i32; + | ^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:45:9 + | +LL | let coke: i32; + | ^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:43:9 + | +LL | let cake: i32; + | ^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:63:9 + | +LL | let xyzeabc: i32; + | ^^^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:61:9 + | +LL | let xyz1abc: i32; + | ^^^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:67:9 + | +LL | let parsee: i32; + | ^^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:65:9 + | +LL | let parser: i32; + | ^^^^^^ +help: separate the discriminating character by an underscore like: `parse_e` + --> $DIR/similar_names.rs:67:9 + | +LL | let parsee: i32; + | ^^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:81:16 + | +LL | bpple: sprang, + | ^^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:80:16 + | +LL | apple: spring, + | ^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/single_char_pattern.fixed b/src/tools/clippy/tests/ui/single_char_pattern.fixed new file mode 100644 index 0000000000000..3871c4f2268cc --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_pattern.fixed @@ -0,0 +1,63 @@ +// run-rustfix + +#![allow(unused_must_use)] + +use std::collections::HashSet; + +fn main() { + let x = "foo"; + x.split('x'); + x.split("xx"); + x.split('x'); + + let y = "x"; + x.split(y); + // Not yet testing for multi-byte characters + // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` + // should have done this but produced an ICE + // + // We may not want to suggest changing these anyway + // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 + x.split("ß"); + x.split("ℝ"); + x.split("💣"); + // Can't use this lint for unicode code points which don't fit in a char + x.split("❤️"); + x.contains('x'); + x.starts_with('x'); + x.ends_with('x'); + x.find('x'); + x.rfind('x'); + x.rsplit('x'); + x.split_terminator('x'); + x.rsplit_terminator('x'); + x.splitn(0, 'x'); + x.rsplitn(0, 'x'); + x.matches('x'); + x.rmatches('x'); + x.match_indices('x'); + x.rmatch_indices('x'); + x.trim_start_matches('x'); + x.trim_end_matches('x'); + // Make sure we escape characters correctly. + x.split('\n'); + x.split('\''); + x.split('\''); + + let h = HashSet::::new(); + h.contains("X"); // should not warn + + x.replace(";", ",").split(','); // issue #2978 + x.starts_with('\x03'); // issue #2996 + + // Issue #3204 + const S: &str = "#"; + x.find(S); + + // Raw string + x.split('a'); + x.split('a'); + x.split('a'); + x.split('\''); + x.split('#'); +} diff --git a/src/tools/clippy/tests/ui/single_char_pattern.rs b/src/tools/clippy/tests/ui/single_char_pattern.rs new file mode 100644 index 0000000000000..32afe339cd81c --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_pattern.rs @@ -0,0 +1,63 @@ +// run-rustfix + +#![allow(unused_must_use)] + +use std::collections::HashSet; + +fn main() { + let x = "foo"; + x.split("x"); + x.split("xx"); + x.split('x'); + + let y = "x"; + x.split(y); + // Not yet testing for multi-byte characters + // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` + // should have done this but produced an ICE + // + // We may not want to suggest changing these anyway + // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 + x.split("ß"); + x.split("ℝ"); + x.split("💣"); + // Can't use this lint for unicode code points which don't fit in a char + x.split("❤️"); + x.contains("x"); + x.starts_with("x"); + x.ends_with("x"); + x.find("x"); + x.rfind("x"); + x.rsplit("x"); + x.split_terminator("x"); + x.rsplit_terminator("x"); + x.splitn(0, "x"); + x.rsplitn(0, "x"); + x.matches("x"); + x.rmatches("x"); + x.match_indices("x"); + x.rmatch_indices("x"); + x.trim_start_matches("x"); + x.trim_end_matches("x"); + // Make sure we escape characters correctly. + x.split("\n"); + x.split("'"); + x.split("\'"); + + let h = HashSet::::new(); + h.contains("X"); // should not warn + + x.replace(";", ",").split(","); // issue #2978 + x.starts_with("\x03"); // issue #2996 + + // Issue #3204 + const S: &str = "#"; + x.find(S); + + // Raw string + x.split(r"a"); + x.split(r#"a"#); + x.split(r###"a"###); + x.split(r###"'"###); + x.split(r###"#"###); +} diff --git a/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr new file mode 100644 index 0000000000000..fe7211c53f852 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr @@ -0,0 +1,166 @@ +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:9:13 + | +LL | x.split("x"); + | ^^^ help: try using a `char` instead: `'x'` + | + = note: `-D clippy::single-char-pattern` implied by `-D warnings` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:26:16 + | +LL | x.contains("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:27:19 + | +LL | x.starts_with("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:28:17 + | +LL | x.ends_with("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:29:12 + | +LL | x.find("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:30:13 + | +LL | x.rfind("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:31:14 + | +LL | x.rsplit("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:32:24 + | +LL | x.split_terminator("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:33:25 + | +LL | x.rsplit_terminator("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:34:17 + | +LL | x.splitn(0, "x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:35:18 + | +LL | x.rsplitn(0, "x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:36:15 + | +LL | x.matches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:37:16 + | +LL | x.rmatches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:38:21 + | +LL | x.match_indices("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:39:22 + | +LL | x.rmatch_indices("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:40:26 + | +LL | x.trim_start_matches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:41:24 + | +LL | x.trim_end_matches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:43:13 + | +LL | x.split("/n"); + | ^^^^ help: try using a `char` instead: `'/n'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:44:13 + | +LL | x.split("'"); + | ^^^ help: try using a `char` instead: `'/''` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:45:13 + | +LL | x.split("/'"); + | ^^^^ help: try using a `char` instead: `'/''` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:50:31 + | +LL | x.replace(";", ",").split(","); // issue #2978 + | ^^^ help: try using a `char` instead: `','` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:51:19 + | +LL | x.starts_with("/x03"); // issue #2996 + | ^^^^^^ help: try using a `char` instead: `'/x03'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:58:13 + | +LL | x.split(r"a"); + | ^^^^ help: try using a `char` instead: `'a'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:59:13 + | +LL | x.split(r#"a"#); + | ^^^^^^ help: try using a `char` instead: `'a'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:60:13 + | +LL | x.split(r###"a"###); + | ^^^^^^^^^^ help: try using a `char` instead: `'a'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:61:13 + | +LL | x.split(r###"'"###); + | ^^^^^^^^^^ help: try using a `char` instead: `'/''` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:62:13 + | +LL | x.split(r###"#"###); + | ^^^^^^^^^^ help: try using a `char` instead: `'#'` + +error: aborting due to 27 previous errors + diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.fixed b/src/tools/clippy/tests/ui/single_component_path_imports.fixed new file mode 100644 index 0000000000000..a7a8499b58f00 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_component_path_imports.fixed @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + + +use serde as edres; +pub use serde; + +macro_rules! m { + () => { + use regex; + }; +} + +fn main() { + regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); + + // False positive #5154, shouldn't trigger lint. + m!(); +} diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.rs b/src/tools/clippy/tests/ui/single_component_path_imports.rs new file mode 100644 index 0000000000000..9a427e90ad3df --- /dev/null +++ b/src/tools/clippy/tests/ui/single_component_path_imports.rs @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use regex; +use serde as edres; +pub use serde; + +macro_rules! m { + () => { + use regex; + }; +} + +fn main() { + regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); + + // False positive #5154, shouldn't trigger lint. + m!(); +} diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.stderr b/src/tools/clippy/tests/ui/single_component_path_imports.stderr new file mode 100644 index 0000000000000..519ada0169a6d --- /dev/null +++ b/src/tools/clippy/tests/ui/single_component_path_imports.stderr @@ -0,0 +1,10 @@ +error: this import is redundant + --> $DIR/single_component_path_imports.rs:6:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs new file mode 100644 index 0000000000000..1c55af5dfb673 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -0,0 +1,95 @@ +#![warn(clippy::single_match)] + +fn dummy() {} + +fn single_match() { + let x = Some(1u8); + + match x { + Some(y) => { + println!("{:?}", y); + }, + _ => (), + }; + + 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), + _ => (), + } + + let z = (1u8, 1u8); + match z { + (2..=3, 7..=9) => dummy(), + _ => {}, + }; + + // Not linted (pattern guards used) + match x { + Some(y) if y == 0 => println!("{:?}", y), + _ => (), + } + + // Not linted (no block with statements in the single arm) + match z { + (2..=3, 7..=9) => println!("{:?}", z), + _ => println!("nope"), + } +} + +enum Foo { + Bar, + Baz(u8), +} +use std::borrow::Cow; +use Foo::*; + +fn single_match_know_enum() { + let x = Some(1u8); + let y: Result<_, i8> = Ok(1i8); + + match x { + Some(y) => dummy(), + None => (), + }; + + match y { + Ok(y) => dummy(), + Err(..) => (), + }; + + let c = Cow::Borrowed(""); + + match c { + Cow::Borrowed(..) => dummy(), + Cow::Owned(..) => (), + }; + + let z = Foo::Bar; + // no warning + match z { + Bar => println!("42"), + Baz(_) => (), + } + + match z { + Baz(_) => println!("42"), + Bar => (), + } +} + +macro_rules! single_match { + ($num:literal) => { + match $num { + 15 => println!("15"), + _ => (), + } + }; +} + +fn main() { + single_match!(5); +} diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr new file mode 100644 index 0000000000000..f69554d75f9bf --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -0,0 +1,69 @@ +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:8:5 + | +LL | / match x { +LL | | Some(y) => { +LL | | println!("{:?}", y); +LL | | }, +LL | | _ => (), +LL | | }; + | |_____^ + | + = note: `-D clippy::single-match` implied by `-D warnings` +help: try this + | +LL | if let Some(y) = x { +LL | println!("{:?}", y); +LL | }; + | + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:16:5 + | +LL | / match x { +LL | | // Note the missing block braces. +LL | | // We suggest `if let Some(y) = x { .. }` because the macro +LL | | // is expanded before we can do anything. +LL | | Some(y) => println!("{:?}", y), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:25:5 + | +LL | / match z { +LL | | (2..=3, 7..=9) => dummy(), +LL | | _ => {}, +LL | | }; + | |_____^ help: try this: `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` + --> $DIR/single_match.rs:54:5 + | +LL | / match x { +LL | | Some(y) => dummy(), +LL | | None => (), +LL | | }; + | |_____^ help: try this: `if let Some(y) = x { dummy() }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:59:5 + | +LL | / match y { +LL | | Ok(y) => dummy(), +LL | | Err(..) => (), +LL | | }; + | |_____^ help: try this: `if let Ok(y) = y { dummy() }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:66:5 + | +LL | / match c { +LL | | Cow::Borrowed(..) => dummy(), +LL | | Cow::Owned(..) => (), +LL | | }; + | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs new file mode 100644 index 0000000000000..34193be0b75e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -0,0 +1,35 @@ +#![warn(clippy::single_match_else)] + +enum ExprNode { + ExprAddrOf, + Butterflies, + Unicorns, +} + +static NODE: ExprNode = ExprNode::Unicorns; + +fn unwrap_addr() -> Option<&'static ExprNode> { + match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + } +} + +macro_rules! unwrap_addr { + ($expression:expr) => { + match $expression { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + } + }; +} + +fn main() { + unwrap_addr!(ExprNode::Unicorns); +} diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr new file mode 100644 index 0000000000000..59861d46eb34c --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -0,0 +1,23 @@ +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:12:5 + | +LL | / match ExprNode::Butterflies { +LL | | ExprNode::ExprAddrOf => Some(&NODE), +LL | | _ => { +LL | | let x = 5; +LL | | None +LL | | }, +LL | | } + | |_____^ + | + = note: `-D clippy::single-match-else` implied by `-D warnings` +help: try this + | +LL | if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { +LL | let x = 5; +LL | None +LL | } + | + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/skip_while_next.rs b/src/tools/clippy/tests/ui/skip_while_next.rs new file mode 100644 index 0000000000000..a522c0f08b207 --- /dev/null +++ b/src/tools/clippy/tests/ui/skip_while_next.rs @@ -0,0 +1,29 @@ +// aux-build:option_helpers.rs + +#![warn(clippy::skip_while_next)] +#![allow(clippy::blacklisted_name)] + +extern crate option_helpers; +use option_helpers::IteratorFalsePositives; + +#[rustfmt::skip] +fn skip_while_next() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().skip_while(|&x| *x < 0).next(); + + // Multi-line case. + let _ = v.iter().skip_while(|&x| { + *x < 0 + } + ).next(); + + // Check that hat we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip_while().next(); +} + +fn main() { + skip_while_next(); +} diff --git a/src/tools/clippy/tests/ui/skip_while_next.stderr b/src/tools/clippy/tests/ui/skip_while_next.stderr new file mode 100644 index 0000000000000..a6b7bcd63ff39 --- /dev/null +++ b/src/tools/clippy/tests/ui/skip_while_next.stderr @@ -0,0 +1,23 @@ +error: called `skip_while(p).next()` on an `Iterator` + --> $DIR/skip_while_next.rs:14:13 + | +LL | let _ = v.iter().skip_while(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::skip-while-next` implied by `-D warnings` + = help: this is more succinctly expressed by calling `.find(!p)` instead + +error: called `skip_while(p).next()` on an `Iterator` + --> $DIR/skip_while_next.rs:17:13 + | +LL | let _ = v.iter().skip_while(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + | + = help: this is more succinctly expressed by calling `.find(!p)` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs new file mode 100644 index 0000000000000..c5ae3ff769b11 --- /dev/null +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -0,0 +1,63 @@ +use std::iter::repeat; + +fn main() { + resize_vector(); + extend_vector(); + mixed_extend_resize_vector(); +} + +fn extend_vector() { + // Extend with constant expression + let len = 300; + let mut vec1 = Vec::with_capacity(len); + vec1.extend(repeat(0).take(len)); + + // Extend with len expression + let mut vec2 = Vec::with_capacity(len - 10); + vec2.extend(repeat(0).take(len - 10)); + + // Extend with mismatching expression should not be warned + let mut vec3 = Vec::with_capacity(24322); + vec3.extend(repeat(0).take(2)); +} + +fn mixed_extend_resize_vector() { + // Mismatching len + let mut mismatching_len = Vec::with_capacity(30); + mismatching_len.extend(repeat(0).take(40)); + + // Slow initialization + let mut resized_vec = Vec::with_capacity(30); + resized_vec.resize(30, 0); + + let mut extend_vec = Vec::with_capacity(30); + extend_vec.extend(repeat(0).take(30)); +} + +fn resize_vector() { + // Resize with constant expression + let len = 300; + let mut vec1 = Vec::with_capacity(len); + vec1.resize(len, 0); + + // Resize mismatch len + let mut vec2 = Vec::with_capacity(200); + vec2.resize(10, 0); + + // Resize with len expression + let mut vec3 = Vec::with_capacity(len - 10); + vec3.resize(len - 10, 0); + + // Reinitialization should be warned + vec1 = Vec::with_capacity(10); + vec1.resize(10, 0); +} + +fn do_stuff(vec: &mut Vec) {} + +fn extend_vector_with_manipulations_between() { + let len = 300; + let mut vec1: Vec = Vec::with_capacity(len); + do_stuff(&mut vec1); + vec1.extend(repeat(0).take(len)); +} diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr new file mode 100644 index 0000000000000..5d2788ec26086 --- /dev/null +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -0,0 +1,60 @@ +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:13:5 + | +LL | let mut vec1 = Vec::with_capacity(len); + | ----------------------- help: consider replace allocation with: `vec![0; len]` +LL | vec1.extend(repeat(0).take(len)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::slow-vector-initialization` implied by `-D warnings` + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:17:5 + | +LL | let mut vec2 = Vec::with_capacity(len - 10); + | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]` +LL | vec2.extend(repeat(0).take(len - 10)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:31:5 + | +LL | let mut resized_vec = Vec::with_capacity(30); + | ---------------------- help: consider replace allocation with: `vec![0; 30]` +LL | resized_vec.resize(30, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:34:5 + | +LL | let mut extend_vec = Vec::with_capacity(30); + | ---------------------- help: consider replace allocation with: `vec![0; 30]` +LL | extend_vec.extend(repeat(0).take(30)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:41:5 + | +LL | let mut vec1 = Vec::with_capacity(len); + | ----------------------- help: consider replace allocation with: `vec![0; len]` +LL | vec1.resize(len, 0); + | ^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:49:5 + | +LL | let mut vec3 = Vec::with_capacity(len - 10); + | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]` +LL | vec3.resize(len - 10, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:53:5 + | +LL | vec1 = Vec::with_capacity(10); + | ---------------------- help: consider replace allocation with: `vec![0; 10]` +LL | vec1.resize(10, 0); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed new file mode 100644 index 0000000000000..7dfcf9c91e486 --- /dev/null +++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed @@ -0,0 +1,46 @@ +// run-rustfix +#![allow(dead_code, unused_must_use)] + +fn main() {} + +#[allow(clippy::unnecessary_operation)] +fn starts_with() { + "".starts_with(' '); + !"".starts_with(' '); +} + +fn chars_cmp_with_unwrap() { + let s = String::from("foo"); + if s.starts_with('f') { + // s.starts_with('f') + // Nothing here + } + if s.ends_with('o') { + // s.ends_with('o') + // Nothing here + } + if s.ends_with('o') { + // s.ends_with('o') + // Nothing here + } + if !s.starts_with('f') { + // !s.starts_with('f') + // Nothing here + } + if !s.ends_with('o') { + // !s.ends_with('o') + // Nothing here + } + if !s.ends_with('o') { + // !s.ends_with('o') + // Nothing here + } +} + +#[allow(clippy::unnecessary_operation)] +fn ends_with() { + "".ends_with(' '); + !"".ends_with(' '); + "".ends_with(' '); + !"".ends_with(' '); +} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs new file mode 100644 index 0000000000000..e48a424635439 --- /dev/null +++ b/src/tools/clippy/tests/ui/starts_ends_with.rs @@ -0,0 +1,46 @@ +// run-rustfix +#![allow(dead_code, unused_must_use)] + +fn main() {} + +#[allow(clippy::unnecessary_operation)] +fn starts_with() { + "".chars().next() == Some(' '); + Some(' ') != "".chars().next(); +} + +fn chars_cmp_with_unwrap() { + let s = String::from("foo"); + if s.chars().next().unwrap() == 'f' { + // s.starts_with('f') + // Nothing here + } + if s.chars().next_back().unwrap() == 'o' { + // s.ends_with('o') + // Nothing here + } + if s.chars().last().unwrap() == 'o' { + // s.ends_with('o') + // Nothing here + } + if s.chars().next().unwrap() != 'f' { + // !s.starts_with('f') + // Nothing here + } + if s.chars().next_back().unwrap() != 'o' { + // !s.ends_with('o') + // Nothing here + } + if s.chars().last().unwrap() != 'o' { + // !s.ends_with('o') + // Nothing here + } +} + +#[allow(clippy::unnecessary_operation)] +fn ends_with() { + "".chars().last() == Some(' '); + Some(' ') != "".chars().last(); + "".chars().next_back() == Some(' '); + Some(' ') != "".chars().next_back(); +} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.stderr b/src/tools/clippy/tests/ui/starts_ends_with.stderr new file mode 100644 index 0000000000000..7c726d0e01026 --- /dev/null +++ b/src/tools/clippy/tests/ui/starts_ends_with.stderr @@ -0,0 +1,78 @@ +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:8:5 + | +LL | "".chars().next() == Some(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".starts_with(' ')` + | + = note: `-D clippy::chars-next-cmp` implied by `-D warnings` + +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:9:5 + | +LL | Some(' ') != "".chars().next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with(' ')` + +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:14:8 + | +LL | if s.chars().next().unwrap() == 'f' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.starts_with('f')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:18:8 + | +LL | if s.chars().next_back().unwrap() == 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')` + | + = note: `-D clippy::chars-last-cmp` implied by `-D warnings` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:22:8 + | +LL | if s.chars().last().unwrap() == 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')` + +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:26:8 + | +LL | if s.chars().next().unwrap() != 'f' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.starts_with('f')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:30:8 + | +LL | if s.chars().next_back().unwrap() != 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:34:8 + | +LL | if s.chars().last().unwrap() != 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:42:5 + | +LL | "".chars().last() == Some(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:43:5 + | +LL | Some(' ') != "".chars().last(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:44:5 + | +LL | "".chars().next_back() == Some(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:45:5 + | +LL | Some(' ') != "".chars().next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/string_add.rs b/src/tools/clippy/tests/ui/string_add.rs new file mode 100644 index 0000000000000..30fd17c59e518 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add.rs @@ -0,0 +1,26 @@ +// aux-build:macro_rules.rs + +#[macro_use] +extern crate macro_rules; + +#[warn(clippy::string_add)] +#[allow(clippy::string_add_assign, unused)] +fn main() { + // ignores assignment distinction + let mut x = "".to_owned(); + + for _ in 1..3 { + x = x + "."; + } + + let y = "".to_owned(); + let z = y + "..."; + + assert_eq!(&x, &z); + + let mut x = 1; + x = x + 1; + assert_eq!(2, x); + + string_add!(); +} diff --git a/src/tools/clippy/tests/ui/string_add.stderr b/src/tools/clippy/tests/ui/string_add.stderr new file mode 100644 index 0000000000000..3987641c75a30 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add.stderr @@ -0,0 +1,30 @@ +error: manual implementation of an assign operation + --> $DIR/string_add.rs:13:9 + | +LL | x = x + "."; + | ^^^^^^^^^^^ help: replace it with: `x += "."` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: you added something to a string. Consider using `String::push_str()` instead + --> $DIR/string_add.rs:13:13 + | +LL | x = x + "."; + | ^^^^^^^ + | + = note: `-D clippy::string-add` implied by `-D warnings` + +error: you added something to a string. Consider using `String::push_str()` instead + --> $DIR/string_add.rs:17:13 + | +LL | let z = y + "..."; + | ^^^^^^^^^ + +error: manual implementation of an assign operation + --> $DIR/string_add.rs:22:5 + | +LL | x = x + 1; + | ^^^^^^^^^ help: replace it with: `x += 1` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/string_add_assign.fixed b/src/tools/clippy/tests/ui/string_add_assign.fixed new file mode 100644 index 0000000000000..db71bab1e5214 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add_assign.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(clippy::string_add, unused)] +#[warn(clippy::string_add_assign)] +fn main() { + // ignores assignment distinction + let mut x = "".to_owned(); + + for _ in 1..3 { + x += "."; + } + + let y = "".to_owned(); + let z = y + "..."; + + assert_eq!(&x, &z); + + let mut x = 1; + x += 1; + assert_eq!(2, x); +} diff --git a/src/tools/clippy/tests/ui/string_add_assign.rs b/src/tools/clippy/tests/ui/string_add_assign.rs new file mode 100644 index 0000000000000..644991945cbe2 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add_assign.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(clippy::string_add, unused)] +#[warn(clippy::string_add_assign)] +fn main() { + // ignores assignment distinction + let mut x = "".to_owned(); + + for _ in 1..3 { + x = x + "."; + } + + let y = "".to_owned(); + let z = y + "..."; + + assert_eq!(&x, &z); + + let mut x = 1; + x = x + 1; + assert_eq!(2, x); +} diff --git a/src/tools/clippy/tests/ui/string_add_assign.stderr b/src/tools/clippy/tests/ui/string_add_assign.stderr new file mode 100644 index 0000000000000..7676175c1b82f --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add_assign.stderr @@ -0,0 +1,24 @@ +error: you assigned the result of adding something to this string. Consider using `String::push_str()` instead + --> $DIR/string_add_assign.rs:10:9 + | +LL | x = x + "."; + | ^^^^^^^^^^^ + | + = note: `-D clippy::string-add-assign` implied by `-D warnings` + +error: manual implementation of an assign operation + --> $DIR/string_add_assign.rs:10:9 + | +LL | x = x + "."; + | ^^^^^^^^^^^ help: replace it with: `x += "."` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: manual implementation of an assign operation + --> $DIR/string_add_assign.rs:19:5 + | +LL | x = x + 1; + | ^^^^^^^^^ help: replace it with: `x += 1` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/string_extend.fixed b/src/tools/clippy/tests/ui/string_extend.fixed new file mode 100644 index 0000000000000..1883a9f832578 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_extend.fixed @@ -0,0 +1,32 @@ +// run-rustfix + +#[derive(Copy, Clone)] +struct HasChars; + +impl HasChars { + fn chars(self) -> std::str::Chars<'static> { + "HasChars".chars() + } +} + +fn main() { + let abc = "abc"; + let def = String::from("def"); + let mut s = String::new(); + + s.push_str(abc); + s.push_str(abc); + + s.push_str("abc"); + s.push_str("abc"); + + s.push_str(&def); + s.push_str(&def); + + s.extend(abc.chars().skip(1)); + s.extend("abc".chars().skip(1)); + s.extend(['a', 'b', 'c'].iter()); + + let f = HasChars; + s.extend(f.chars()); +} diff --git a/src/tools/clippy/tests/ui/string_extend.rs b/src/tools/clippy/tests/ui/string_extend.rs new file mode 100644 index 0000000000000..07d0baa1be6c7 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_extend.rs @@ -0,0 +1,32 @@ +// run-rustfix + +#[derive(Copy, Clone)] +struct HasChars; + +impl HasChars { + fn chars(self) -> std::str::Chars<'static> { + "HasChars".chars() + } +} + +fn main() { + let abc = "abc"; + let def = String::from("def"); + let mut s = String::new(); + + s.push_str(abc); + s.extend(abc.chars()); + + s.push_str("abc"); + s.extend("abc".chars()); + + s.push_str(&def); + s.extend(def.chars()); + + s.extend(abc.chars().skip(1)); + s.extend("abc".chars().skip(1)); + s.extend(['a', 'b', 'c'].iter()); + + let f = HasChars; + s.extend(f.chars()); +} diff --git a/src/tools/clippy/tests/ui/string_extend.stderr b/src/tools/clippy/tests/ui/string_extend.stderr new file mode 100644 index 0000000000000..6af8c9e1662b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_extend.stderr @@ -0,0 +1,22 @@ +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:18:5 + | +LL | s.extend(abc.chars()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(abc)` + | + = note: `-D clippy::string-extend-chars` implied by `-D warnings` + +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:21:5 + | +LL | s.extend("abc".chars()); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str("abc")` + +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:24:5 + | +LL | s.extend(def.chars()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed new file mode 100644 index 0000000000000..7ad272ade5f9c --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::string_lit_as_bytes)] + +fn str_lit_as_bytes() { + let bs = b"hello there"; + + let bs = br###"raw string with 3# plus " ""###; + + // no warning, because these cannot be written as byte string literals: + let ubs = "☃".as_bytes(); + let ubs = "hello there! this is a very long string".as_bytes(); + + let strify = stringify!(foobar).as_bytes(); + + let includestr = include_bytes!("entry_unfixable.rs"); + + let _ = b"string with newline\t\n"; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs new file mode 100644 index 0000000000000..1bf4538b7c94f --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::string_lit_as_bytes)] + +fn str_lit_as_bytes() { + let bs = "hello there".as_bytes(); + + let bs = r###"raw string with 3# plus " ""###.as_bytes(); + + // no warning, because these cannot be written as byte string literals: + let ubs = "☃".as_bytes(); + let ubs = "hello there! this is a very long string".as_bytes(); + + let strify = stringify!(foobar).as_bytes(); + + let includestr = include_str!("entry_unfixable.rs").as_bytes(); + + let _ = "string with newline\t\n".as_bytes(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr new file mode 100644 index 0000000000000..ff6e3346dfc75 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr @@ -0,0 +1,28 @@ +error: calling `as_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:7:14 + | +LL | let bs = "hello there".as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"hello there"` + | + = note: `-D clippy::string-lit-as-bytes` implied by `-D warnings` + +error: calling `as_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:9:14 + | +LL | let bs = r###"raw string with 3# plus " ""###.as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###` + +error: calling `as_bytes()` on `include_str!(..)` + --> $DIR/string_lit_as_bytes.rs:17:22 + | +LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` + +error: calling `as_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:19:13 + | +LL | let _ = "string with newline/t/n".as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/struct_excessive_bools.rs b/src/tools/clippy/tests/ui/struct_excessive_bools.rs new file mode 100644 index 0000000000000..ce4fe830a0a21 --- /dev/null +++ b/src/tools/clippy/tests/ui/struct_excessive_bools.rs @@ -0,0 +1,44 @@ +#![warn(clippy::struct_excessive_bools)] + +macro_rules! foo { + () => { + struct MacroFoo { + a: bool, + b: bool, + c: bool, + d: bool, + } + }; +} + +foo!(); + +struct Foo { + a: bool, + b: bool, + c: bool, +} + +struct BadFoo { + a: bool, + b: bool, + c: bool, + d: bool, +} + +#[repr(C)] +struct Bar { + a: bool, + b: bool, + c: bool, + d: bool, +} + +fn main() { + struct FooFoo { + a: bool, + b: bool, + c: bool, + d: bool, + } +} diff --git a/src/tools/clippy/tests/ui/struct_excessive_bools.stderr b/src/tools/clippy/tests/ui/struct_excessive_bools.stderr new file mode 100644 index 0000000000000..2941bf2983aa8 --- /dev/null +++ b/src/tools/clippy/tests/ui/struct_excessive_bools.stderr @@ -0,0 +1,29 @@ +error: more than 3 bools in a struct + --> $DIR/struct_excessive_bools.rs:22:1 + | +LL | / struct BadFoo { +LL | | a: bool, +LL | | b: bool, +LL | | c: bool, +LL | | d: bool, +LL | | } + | |_^ + | + = note: `-D clippy::struct-excessive-bools` implied by `-D warnings` + = help: consider using a state machine or refactoring bools into two-variant enums + +error: more than 3 bools in a struct + --> $DIR/struct_excessive_bools.rs:38:5 + | +LL | / struct FooFoo { +LL | | a: bool, +LL | | b: bool, +LL | | c: bool, +LL | | d: bool, +LL | | } + | |_____^ + | + = help: consider using a state machine or refactoring bools into two-variant enums + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs new file mode 100644 index 0000000000000..1f5b981188706 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs @@ -0,0 +1,90 @@ +#![warn(clippy::suspicious_arithmetic_impl)] +use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub}; + +#[derive(Copy, Clone)] +struct Foo(u32); + +impl Add for Foo { + type Output = Foo; + + fn add(self, other: Self) -> Self { + Foo(self.0 - other.0) + } +} + +impl AddAssign for Foo { + fn add_assign(&mut self, other: Foo) { + *self = *self - other; + } +} + +impl BitOrAssign for Foo { + fn bitor_assign(&mut self, other: Foo) { + let idx = other.0; + self.0 |= 1 << idx; // OK: BinOpKind::Shl part of AssignOp as child node + } +} + +impl MulAssign for Foo { + fn mul_assign(&mut self, other: Foo) { + self.0 /= other.0; + } +} + +impl DivAssign for Foo { + fn div_assign(&mut self, other: Foo) { + self.0 /= other.0; // OK: BinOpKind::Div == DivAssign + } +} + +impl Mul for Foo { + type Output = Foo; + + fn mul(self, other: Foo) -> Foo { + Foo(self.0 * other.0 % 42) // OK: BinOpKind::Rem part of BiExpr as parent node + } +} + +impl Sub for Foo { + type Output = Foo; + + fn sub(self, other: Self) -> Self { + Foo(self.0 * other.0 - 42) // OK: BinOpKind::Mul part of BiExpr as child node + } +} + +impl Div for Foo { + type Output = Foo; + + fn div(self, other: Self) -> Self { + Foo(do_nothing(self.0 + other.0) / 42) // OK: BinOpKind::Add part of BiExpr as child node + } +} + +struct Bar(i32); + +impl Add for Bar { + type Output = Bar; + + fn add(self, other: Self) -> Self { + Bar(self.0 & !other.0) // OK: UnNot part of BiExpr as child node + } +} + +impl Sub for Bar { + type Output = Bar; + + fn sub(self, other: Self) -> Self { + if self.0 <= other.0 { + Bar(-(self.0 & other.0)) // OK: UnNeg part of BiExpr as parent node + } else { + Bar(0) + } + } +} + +fn main() {} + +fn do_nothing(x: u32) -> u32 { + x +} diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr new file mode 100644 index 0000000000000..7e42d72c30b2c --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr @@ -0,0 +1,24 @@ +error: Suspicious use of binary operator in `Add` impl + --> $DIR/suspicious_arithmetic_impl.rs:11:20 + | +LL | Foo(self.0 - other.0) + | ^ + | + = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` + +error: Suspicious use of binary operator in `AddAssign` impl + --> $DIR/suspicious_arithmetic_impl.rs:17:23 + | +LL | *self = *self - other; + | ^ + | + = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default + +error: Suspicious use of binary operator in `MulAssign` impl + --> $DIR/suspicious_arithmetic_impl.rs:30:16 + | +LL | self.0 /= other.0; + | ^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/suspicious_map.rs b/src/tools/clippy/tests/ui/suspicious_map.rs new file mode 100644 index 0000000000000..d838d8fde2105 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_map.rs @@ -0,0 +1,5 @@ +#![warn(clippy::suspicious_map)] + +fn main() { + let _ = (0..3).map(|x| x + 2).count(); +} diff --git a/src/tools/clippy/tests/ui/suspicious_map.stderr b/src/tools/clippy/tests/ui/suspicious_map.stderr new file mode 100644 index 0000000000000..e1b4ba40376f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_map.stderr @@ -0,0 +1,11 @@ +error: this call to `map()` won't have an effect on the call to `count()` + --> $DIR/suspicious_map.rs:4:13 + | +LL | let _ = (0..3).map(|x| x + 2).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::suspicious-map` implied by `-D warnings` + = help: make sure you did not confuse `map` with `filter` or `for_each` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs new file mode 100644 index 0000000000000..9564e373c246d --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs @@ -0,0 +1,23 @@ +#![warn(clippy::suspicious_unary_op_formatting)] + +#[rustfmt::skip] +fn main() { + // weird binary operator formatting: + let a = 42; + + if a >- 30 {} + if a >=- 30 {} + + let b = true; + let c = false; + + if b &&! c {} + + if a >- 30 {} + + // those are ok: + if a >-30 {} + if a < -30 {} + if b && !c {} + if a > - 30 {} +} diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr new file mode 100644 index 0000000000000..581527dcff8e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr @@ -0,0 +1,35 @@ +error: by not having a space between `>` and `-` it looks like `>-` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:8:9 + | +LL | if a >- 30 {} + | ^^^^ + | + = note: `-D clippy::suspicious-unary-op-formatting` implied by `-D warnings` + = help: put a space between `>` and `-` and remove the space after `-` + +error: by not having a space between `>=` and `-` it looks like `>=-` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:9:9 + | +LL | if a >=- 30 {} + | ^^^^^ + | + = help: put a space between `>=` and `-` and remove the space after `-` + +error: by not having a space between `&&` and `!` it looks like `&&!` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:14:9 + | +LL | if b &&! c {} + | ^^^^^ + | + = help: put a space between `&&` and `!` and remove the space after `!` + +error: by not having a space between `>` and `-` it looks like `>-` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:16:9 + | +LL | if a >- 30 {} + | ^^^^^^ + | + = help: put a space between `>` and `-` and remove the space after `-` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed new file mode 100644 index 0000000000000..0f8f839a0d542 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -0,0 +1,83 @@ +// run-rustfix + +#![warn(clippy::all)] +#![allow( + clippy::blacklisted_name, + clippy::no_effect, + clippy::redundant_clone, + redundant_semicolons, + unused_assignments +)] + +struct Foo(u32); + +#[derive(Clone)] +struct Bar { + a: u32, + b: u32, +} + +fn field() { + let mut bar = Bar { a: 1, b: 2 }; + + let temp = bar.a; + bar.a = bar.b; + bar.b = temp; + + let mut baz = vec![bar.clone(), bar.clone()]; + let temp = baz[0].a; + baz[0].a = baz[1].a; + baz[1].a = temp; +} + +fn array() { + let mut foo = [1, 2]; + foo.swap(0, 1); + + foo.swap(0, 1); +} + +fn slice() { + let foo = &mut [1, 2]; + foo.swap(0, 1); + + foo.swap(0, 1); +} + +fn unswappable_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + let temp = foo[0][1]; + foo[0][1] = foo[1][0]; + foo[1][0] = temp; + + // swap(foo[0][1], foo[1][0]) would fail +} + +fn vec() { + let mut foo = vec![1, 2]; + foo.swap(0, 1); + + foo.swap(0, 1); +} + +#[rustfmt::skip] +fn main() { + field(); + array(); + slice(); + unswappable_slice(); + vec(); + + let mut a = 42; + let mut b = 1337; + + std::mem::swap(&mut a, &mut b); + + ; std::mem::swap(&mut a, &mut b); + + let mut c = Foo(42); + + std::mem::swap(&mut c.0, &mut a); + + ; std::mem::swap(&mut c.0, &mut a); +} diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs new file mode 100644 index 0000000000000..5763d9e82d486 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap.rs @@ -0,0 +1,95 @@ +// run-rustfix + +#![warn(clippy::all)] +#![allow( + clippy::blacklisted_name, + clippy::no_effect, + clippy::redundant_clone, + redundant_semicolons, + unused_assignments +)] + +struct Foo(u32); + +#[derive(Clone)] +struct Bar { + a: u32, + b: u32, +} + +fn field() { + let mut bar = Bar { a: 1, b: 2 }; + + let temp = bar.a; + bar.a = bar.b; + bar.b = temp; + + let mut baz = vec![bar.clone(), bar.clone()]; + let temp = baz[0].a; + baz[0].a = baz[1].a; + baz[1].a = temp; +} + +fn array() { + let mut foo = [1, 2]; + let temp = foo[0]; + foo[0] = foo[1]; + foo[1] = temp; + + foo.swap(0, 1); +} + +fn slice() { + let foo = &mut [1, 2]; + let temp = foo[0]; + foo[0] = foo[1]; + foo[1] = temp; + + foo.swap(0, 1); +} + +fn unswappable_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + let temp = foo[0][1]; + foo[0][1] = foo[1][0]; + foo[1][0] = temp; + + // swap(foo[0][1], foo[1][0]) would fail +} + +fn vec() { + let mut foo = vec![1, 2]; + let temp = foo[0]; + foo[0] = foo[1]; + foo[1] = temp; + + foo.swap(0, 1); +} + +#[rustfmt::skip] +fn main() { + field(); + array(); + slice(); + unswappable_slice(); + vec(); + + let mut a = 42; + let mut b = 1337; + + a = b; + b = a; + + ; let t = a; + a = b; + b = t; + + let mut c = Foo(42); + + c.0 = a; + a = c.0; + + ; let t = c.0; + c.0 = a; + a = t; +} diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr new file mode 100644 index 0000000000000..f49bcfedf3a19 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -0,0 +1,69 @@ +error: this looks like you are swapping elements of `foo` manually + --> $DIR/swap.rs:35:5 + | +LL | / let temp = foo[0]; +LL | | foo[0] = foo[1]; +LL | | foo[1] = temp; + | |_________________^ help: try: `foo.swap(0, 1)` + | + = note: `-D clippy::manual-swap` implied by `-D warnings` + +error: this looks like you are swapping elements of `foo` manually + --> $DIR/swap.rs:44:5 + | +LL | / let temp = foo[0]; +LL | | foo[0] = foo[1]; +LL | | foo[1] = temp; + | |_________________^ help: try: `foo.swap(0, 1)` + +error: this looks like you are swapping elements of `foo` manually + --> $DIR/swap.rs:62:5 + | +LL | / let temp = foo[0]; +LL | | foo[0] = foo[1]; +LL | | foo[1] = temp; + | |_________________^ help: try: `foo.swap(0, 1)` + +error: this looks like you are swapping `a` and `b` manually + --> $DIR/swap.rs:83:7 + | +LL | ; let t = a; + | _______^ +LL | | a = b; +LL | | b = t; + | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` + | + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are swapping `c.0` and `a` manually + --> $DIR/swap.rs:92:7 + | +LL | ; let t = c.0; + | _______^ +LL | | c.0 = a; +LL | | a = t; + | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)` + | + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are trying to swap `a` and `b` + --> $DIR/swap.rs:80:5 + | +LL | / a = b; +LL | | b = a; + | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` + | + = note: `-D clippy::almost-swapped` implied by `-D warnings` + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are trying to swap `c.0` and `a` + --> $DIR/swap.rs:89:5 + | +LL | / c.0 = a; +LL | | a = c.0; + | |___________^ help: try: `std::mem::swap(&mut c.0, &mut a)` + | + = note: or maybe you should use `std::mem::replace`? + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed new file mode 100644 index 0000000000000..4bc4bc86c76c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::tabs_in_doc_comments)] +#[allow(dead_code)] + +/// +/// Struct to hold two strings: +/// - first one +/// - second one +pub struct DoubleString { + /// + /// - First String: + /// - needs to be inside here + first_string: String, + /// + /// - Second String: + /// - needs to be inside here + second_string: String, +} + +/// This is main +fn main() {} diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs new file mode 100644 index 0000000000000..9db3416e65964 --- /dev/null +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::tabs_in_doc_comments)] +#[allow(dead_code)] + +/// +/// Struct to hold two strings: +/// - first one +/// - second one +pub struct DoubleString { + /// + /// - First String: + /// - needs to be inside here + first_string: String, + /// + /// - Second String: + /// - needs to be inside here + second_string: String, +} + +/// This is main +fn main() {} diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr new file mode 100644 index 0000000000000..355f2e8057964 --- /dev/null +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr @@ -0,0 +1,52 @@ +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:12:9 + | +LL | /// - First String: + | ^^^^ help: consider using four spaces per tab + | + = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:13:9 + | +LL | /// - needs to be inside here + | ^^^^^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:16:9 + | +LL | /// - Second String: + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:17:9 + | +LL | /// - needs to be inside here + | ^^^^^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:8:5 + | +LL | /// - first one + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:8:13 + | +LL | /// - first one + | ^^^^^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:9:5 + | +LL | /// - second one + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:9:14 + | +LL | /// - second one + | ^^^^ help: consider using four spaces per tab + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/temporary_assignment.rs b/src/tools/clippy/tests/ui/temporary_assignment.rs new file mode 100644 index 0000000000000..c6c315d5fab5d --- /dev/null +++ b/src/tools/clippy/tests/ui/temporary_assignment.rs @@ -0,0 +1,75 @@ +#![warn(clippy::temporary_assignment)] + +use std::ops::{Deref, DerefMut}; + +struct TupleStruct(i32); + +struct Struct { + field: i32, +} + +struct MultiStruct { + structure: Struct, +} + +struct Wrapper<'a> { + inner: &'a mut Struct, +} + +impl<'a> Deref for Wrapper<'a> { + type Target = Struct; + fn deref(&self) -> &Struct { + self.inner + } +} + +impl<'a> DerefMut for Wrapper<'a> { + fn deref_mut(&mut self) -> &mut Struct { + self.inner + } +} + +struct ArrayStruct { + array: [i32; 1], +} + +const A: TupleStruct = TupleStruct(1); +const B: Struct = Struct { field: 1 }; +const C: MultiStruct = MultiStruct { + structure: Struct { field: 1 }, +}; +const D: ArrayStruct = ArrayStruct { array: [1] }; + +fn main() { + let mut s = Struct { field: 0 }; + let mut t = (0, 0); + + Struct { field: 0 }.field = 1; + MultiStruct { + structure: Struct { field: 0 }, + } + .structure + .field = 1; + ArrayStruct { array: [0] }.array[0] = 1; + (0, 0).0 = 1; + + A.0 = 2; + B.field = 2; + C.structure.field = 2; + D.array[0] = 2; + + // no error + s.field = 1; + t.0 = 1; + Wrapper { inner: &mut s }.field = 1; + let mut a_mut = TupleStruct(1); + a_mut.0 = 2; + let mut b_mut = Struct { field: 1 }; + b_mut.field = 2; + let mut c_mut = MultiStruct { + structure: Struct { field: 1 }, + }; + c_mut.structure.field = 2; + let mut d_mut = ArrayStruct { array: [1] }; + d_mut.array[0] = 2; +} diff --git a/src/tools/clippy/tests/ui/temporary_assignment.stderr b/src/tools/clippy/tests/ui/temporary_assignment.stderr new file mode 100644 index 0000000000000..4efe2d4bb6713 --- /dev/null +++ b/src/tools/clippy/tests/ui/temporary_assignment.stderr @@ -0,0 +1,56 @@ +error: assignment to temporary + --> $DIR/temporary_assignment.rs:47:5 + | +LL | Struct { field: 0 }.field = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::temporary-assignment` implied by `-D warnings` + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:48:5 + | +LL | / MultiStruct { +LL | | structure: Struct { field: 0 }, +LL | | } +LL | | .structure +LL | | .field = 1; + | |______________^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:53:5 + | +LL | ArrayStruct { array: [0] }.array[0] = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:54:5 + | +LL | (0, 0).0 = 1; + | ^^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:56:5 + | +LL | A.0 = 2; + | ^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:57:5 + | +LL | B.field = 2; + | ^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:58:5 + | +LL | C.structure.field = 2; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:59:5 + | +LL | D.array[0] = 2; + | ^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.fixed b/src/tools/clippy/tests/ui/to_digit_is_some.fixed new file mode 100644 index 0000000000000..19184df0becb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/to_digit_is_some.fixed @@ -0,0 +1,11 @@ +//run-rustfix + +#![warn(clippy::to_digit_is_some)] + +fn main() { + let c = 'x'; + let d = &c; + + let _ = d.is_digit(10); + let _ = char::is_digit(c, 10); +} diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.rs b/src/tools/clippy/tests/ui/to_digit_is_some.rs new file mode 100644 index 0000000000000..45a6728ebf578 --- /dev/null +++ b/src/tools/clippy/tests/ui/to_digit_is_some.rs @@ -0,0 +1,11 @@ +//run-rustfix + +#![warn(clippy::to_digit_is_some)] + +fn main() { + let c = 'x'; + let d = &c; + + let _ = d.to_digit(10).is_some(); + let _ = char::to_digit(c, 10).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 new file mode 100644 index 0000000000000..177d3ccd3e23d --- /dev/null +++ b/src/tools/clippy/tests/ui/to_digit_is_some.stderr @@ -0,0 +1,16 @@ +error: use of `.to_digit(..).is_some()` + --> $DIR/to_digit_is_some.rs:9:13 + | +LL | let _ = d.to_digit(10).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)` + | + = note: `-D clippy::to-digit-is-some` implied by `-D warnings` + +error: use of `.to_digit(..).is_some()` + --> $DIR/to_digit_is_some.rs:10:13 + | +LL | let _ = char::to_digit(c, 10).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed new file mode 100644 index 0000000000000..33605aca0199c --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed @@ -0,0 +1,29 @@ +// run-rustfix + +#![warn(clippy::toplevel_ref_arg)] + +fn main() { + // Closures should not warn + let y = |ref x| println!("{:?}", x); + y(1u8); + + let _x = &1; + + let _y: &(&_, u8) = &(&1, 2); + + let _z = &(1 + 2); + + let _z = &mut (1 + 2); + + let (ref x, _) = (1, 2); // ok, not top level + println!("The answer is {}.", x); + + let _x = &vec![1, 2, 3]; + + // Make sure that allowing the lint works + #[allow(clippy::toplevel_ref_arg)] + let ref mut _x = 1_234_543; + + // ok + for ref _x in 0..10 {} +} diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs new file mode 100644 index 0000000000000..59759f1189333 --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs @@ -0,0 +1,29 @@ +// run-rustfix + +#![warn(clippy::toplevel_ref_arg)] + +fn main() { + // Closures should not warn + let y = |ref x| println!("{:?}", x); + y(1u8); + + let ref _x = 1; + + let ref _y: (&_, u8) = (&1, 2); + + let ref _z = 1 + 2; + + let ref mut _z = 1 + 2; + + let (ref x, _) = (1, 2); // ok, not top level + println!("The answer is {}.", x); + + let ref _x = vec![1, 2, 3]; + + // Make sure that allowing the lint works + #[allow(clippy::toplevel_ref_arg)] + let ref mut _x = 1_234_543; + + // ok + for ref _x in 0..10 {} +} diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr new file mode 100644 index 0000000000000..19d69496709be --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr @@ -0,0 +1,34 @@ +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:10:9 + | +LL | let ref _x = 1; + | ----^^^^^^----- help: try: `let _x = &1;` + | + = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:12:9 + | +LL | let ref _y: (&_, u8) = (&1, 2); + | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:14:9 + | +LL | let ref _z = 1 + 2; + | ----^^^^^^--------- help: try: `let _z = &(1 + 2);` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:16:9 + | +LL | let ref mut _z = 1 + 2; + | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:21:9 + | +LL | let ref _x = vec![1, 2, 3]; + | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs new file mode 100644 index 0000000000000..42cac2ba4de25 --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs @@ -0,0 +1,11 @@ +#![warn(clippy::toplevel_ref_arg)] +#![allow(unused)] + +fn the_answer(ref mut x: u8) { + *x = 42; +} + +fn main() { + let mut x = 0; + the_answer(x); +} diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr new file mode 100644 index 0000000000000..295e2f35608bf --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -0,0 +1,10 @@ +error: `ref` directly on a function argument is ignored. Consider using a reference type instead. + --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15 + | +LL | fn the_answer(ref mut x: u8) { + | ^^^^^^^^^ + | + = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/trailing_zeros.rs b/src/tools/clippy/tests/ui/trailing_zeros.rs new file mode 100644 index 0000000000000..1cef8c2cfc997 --- /dev/null +++ b/src/tools/clippy/tests/ui/trailing_zeros.rs @@ -0,0 +1,9 @@ +#![allow(unused_parens)] + +fn main() { + let x: i32 = 42; + let _ = (x & 0b1111 == 0); // suggest trailing_zeros + let _ = x & 0b1_1111 == 0; // suggest trailing_zeros + let _ = x & 0b1_1010 == 0; // do not lint + let _ = x & 1 == 0; // do not lint +} diff --git a/src/tools/clippy/tests/ui/trailing_zeros.stderr b/src/tools/clippy/tests/ui/trailing_zeros.stderr new file mode 100644 index 0000000000000..320d9cc3f6434 --- /dev/null +++ b/src/tools/clippy/tests/ui/trailing_zeros.stderr @@ -0,0 +1,16 @@ +error: bit mask could be simplified with a call to `trailing_zeros` + --> $DIR/trailing_zeros.rs:5:13 + | +LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros + | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4` + | + = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` + +error: bit mask could be simplified with a call to `trailing_zeros` + --> $DIR/trailing_zeros.rs:6:13 + | +LL | let _ = x & 0b1_1111 == 0; // suggest trailing_zeros + | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs new file mode 100644 index 0000000000000..bb853d237047f --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -0,0 +1,94 @@ +#![allow(dead_code)] + +extern crate core; + +use std::mem::transmute as my_transmute; +use std::vec::Vec as MyVec; + +fn my_int() -> Usize { + Usize(42) +} + +fn my_vec() -> MyVec { + vec![] +} + +#[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] +#[warn(clippy::useless_transmute)] +unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { + let _: &'a T = core::intrinsics::transmute(t); + + let _: &'a U = core::intrinsics::transmute(t); + + let _: *const T = core::intrinsics::transmute(t); + + let _: *mut T = core::intrinsics::transmute(t); + + let _: *const U = core::intrinsics::transmute(t); +} + +#[warn(clippy::useless_transmute)] +fn useless() { + unsafe { + let _: Vec = core::intrinsics::transmute(my_vec()); + + let _: Vec = core::mem::transmute(my_vec()); + + let _: Vec = std::intrinsics::transmute(my_vec()); + + let _: Vec = std::mem::transmute(my_vec()); + + let _: Vec = my_transmute(my_vec()); + + let _: *const usize = std::mem::transmute(5_isize); + + let _ = 5_isize as *const usize; + + let _: *const usize = std::mem::transmute(1 + 1usize); + + let _ = (1 + 1_usize) as *const usize; + } +} + +struct Usize(usize); + +#[warn(clippy::crosspointer_transmute)] +fn crosspointer() { + let mut int: Usize = Usize(0); + let int_const_ptr: *const Usize = &int as *const Usize; + let int_mut_ptr: *mut Usize = &mut int as *mut Usize; + + unsafe { + let _: Usize = core::intrinsics::transmute(int_const_ptr); + + let _: Usize = core::intrinsics::transmute(int_mut_ptr); + + let _: *const Usize = core::intrinsics::transmute(my_int()); + + let _: *mut Usize = core::intrinsics::transmute(my_int()); + } +} + +#[warn(clippy::transmute_int_to_char)] +fn int_to_char() { + let _: char = unsafe { std::mem::transmute(0_u32) }; + let _: char = unsafe { std::mem::transmute(0_i32) }; +} + +#[warn(clippy::transmute_int_to_bool)] +fn int_to_bool() { + let _: bool = unsafe { std::mem::transmute(0_u8) }; +} + +#[warn(clippy::transmute_int_to_float)] +fn int_to_float() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; +} + +fn bytes_to_str(b: &[u8], mb: &mut [u8]) { + let _: &str = unsafe { std::mem::transmute(b) }; + let _: &mut str = unsafe { std::mem::transmute(mb) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr new file mode 100644 index 0000000000000..8582080498f3e --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -0,0 +1,146 @@ +error: transmute from a type (`&T`) to itself + --> $DIR/transmute.rs:19:20 + | +LL | let _: &'a T = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::useless-transmute` implied by `-D warnings` + +error: transmute from a reference to a pointer + --> $DIR/transmute.rs:23:23 + | +LL | let _: *const T = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` + +error: transmute from a reference to a pointer + --> $DIR/transmute.rs:25:21 + | +LL | let _: *mut T = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` + +error: transmute from a reference to a pointer + --> $DIR/transmute.rs:27:23 + | +LL | let _: *const U = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` + +error: transmute from a type (`std::vec::Vec`) to itself + --> $DIR/transmute.rs:33:27 + | +LL | let _: Vec = core::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec`) to itself + --> $DIR/transmute.rs:35:27 + | +LL | let _: Vec = core::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec`) to itself + --> $DIR/transmute.rs:37:27 + | +LL | let _: Vec = std::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec`) to itself + --> $DIR/transmute.rs:39:27 + | +LL | let _: Vec = std::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec`) to itself + --> $DIR/transmute.rs:41:27 + | +LL | let _: Vec = my_transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from an integer to a pointer + --> $DIR/transmute.rs:43:31 + | +LL | let _: *const usize = std::mem::transmute(5_isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` + +error: transmute from an integer to a pointer + --> $DIR/transmute.rs:47:31 + | +LL | let _: *const usize = std::mem::transmute(1 + 1usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` + +error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) + --> $DIR/transmute.rs:62:24 + | +LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` + +error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) + --> $DIR/transmute.rs:64:24 + | +LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) + --> $DIR/transmute.rs:66:31 + | +LL | let _: *const Usize = core::intrinsics::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) + --> $DIR/transmute.rs:68:29 + | +LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a `u32` to a `char` + --> $DIR/transmute.rs:74: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` + +error: transmute from a `i32` to a `char` + --> $DIR/transmute.rs:75:28 + | +LL | let _: char = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` + +error: transmute from a `u8` to a `bool` + --> $DIR/transmute.rs:80:28 + | +LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` + | + = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` + +error: transmute from a `u32` to a `f32` + --> $DIR/transmute.rs:85:27 + | +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` + | + = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` + +error: transmute from a `i32` to a `f32` + --> $DIR/transmute.rs:86:27 + | +LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` + +error: transmute from a `&[u8]` to a `&str` + --> $DIR/transmute.rs:90:28 + | +LL | let _: &str = unsafe { std::mem::transmute(b) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` + | + = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` + +error: transmute from a `&mut [u8]` to a `&mut str` + --> $DIR/transmute.rs:91:32 + | +LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_32bit.rs b/src/tools/clippy/tests/ui/transmute_32bit.rs new file mode 100644 index 0000000000000..ffe22b12f5510 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_32bit.rs @@ -0,0 +1,14 @@ +// ignore-64bit + +#[warn(clippy::wrong_transmute)] +fn main() { + unsafe { + let _: *const usize = std::mem::transmute(6.0f32); + + let _: *mut usize = std::mem::transmute(6.0f32); + + let _: *const usize = std::mem::transmute('x'); + + let _: *mut usize = std::mem::transmute('x'); + } +} diff --git a/src/tools/clippy/tests/ui/transmute_32bit.stderr b/src/tools/clippy/tests/ui/transmute_32bit.stderr new file mode 100644 index 0000000000000..040519564b94c --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_32bit.stderr @@ -0,0 +1,28 @@ +error: transmute from a `f32` to a pointer + --> $DIR/transmute_32bit.rs:6:31 + | +LL | let _: *const usize = std::mem::transmute(6.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::wrong-transmute` implied by `-D warnings` + +error: transmute from a `f32` to a pointer + --> $DIR/transmute_32bit.rs:8:29 + | +LL | let _: *mut usize = std::mem::transmute(6.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a `char` to a pointer + --> $DIR/transmute_32bit.rs:10:31 + | +LL | let _: *const usize = std::mem::transmute('x'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a `char` to a pointer + --> $DIR/transmute_32bit.rs:12:29 + | +LL | let _: *mut usize = std::mem::transmute('x'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_64bit.rs b/src/tools/clippy/tests/ui/transmute_64bit.rs new file mode 100644 index 0000000000000..00dc0b2c36081 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_64bit.rs @@ -0,0 +1,10 @@ +// ignore-32bit + +#[warn(clippy::wrong_transmute)] +fn main() { + unsafe { + let _: *const usize = std::mem::transmute(6.0f64); + + let _: *mut usize = std::mem::transmute(6.0f64); + } +} diff --git a/src/tools/clippy/tests/ui/transmute_64bit.stderr b/src/tools/clippy/tests/ui/transmute_64bit.stderr new file mode 100644 index 0000000000000..d1854c009ef56 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_64bit.stderr @@ -0,0 +1,16 @@ +error: transmute from a `f64` to a pointer + --> $DIR/transmute_64bit.rs:6:31 + | +LL | let _: *const usize = std::mem::transmute(6.0f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::wrong-transmute` implied by `-D warnings` + +error: transmute from a `f64` to a pointer + --> $DIR/transmute_64bit.rs:8:29 + | +LL | let _: *mut usize = std::mem::transmute(6.0f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_collection.rs b/src/tools/clippy/tests/ui/transmute_collection.rs new file mode 100644 index 0000000000000..cd5a7127791a8 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_collection.rs @@ -0,0 +1,47 @@ +#![warn(clippy::unsound_collection_transmute)] + +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; +use std::mem::transmute; + +fn main() { + unsafe { + // wrong size + let _ = transmute::<_, Vec>(vec![0u8]); + // wrong layout + let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + + // wrong size + let _ = transmute::<_, VecDeque>(VecDeque::::new()); + // wrong layout + let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BinaryHeap>(BinaryHeap::::new()); + // wrong layout + let _ = transmute::<_, BinaryHeap>(BinaryHeap::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BTreeSet>(BTreeSet::::new()); + // wrong layout + let _ = transmute::<_, BTreeSet>(BTreeSet::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, HashSet>(HashSet::::new()); + // wrong layout + let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + // wrong layout + let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); + + // wrong size + let _ = transmute::<_, HashMap>(HashMap::::new()); + let _ = transmute::<_, HashMap>(HashMap::::new()); + // wrong layout + let _ = transmute::<_, HashMap>(HashMap::::new()); + let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); + } +} diff --git a/src/tools/clippy/tests/ui/transmute_collection.stderr b/src/tools/clippy/tests/ui/transmute_collection.stderr new file mode 100644 index 0000000000000..ebc05c402abf6 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_collection.stderr @@ -0,0 +1,112 @@ +error: transmute from `std::vec::Vec` to `std::vec::Vec` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:9:17 + | +LL | let _ = transmute::<_, Vec>(vec![0u8]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings` + +error: transmute from `std::vec::Vec` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:11:17 + | +LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::VecDeque` to `std::collections::VecDeque` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:14:17 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:16:17 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BinaryHeap` to `std::collections::BinaryHeap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:19:17 + | +LL | let _ = transmute::<_, BinaryHeap>(BinaryHeap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:21:17 + | +LL | let _ = transmute::<_, BinaryHeap>(BinaryHeap::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeSet` to `std::collections::BTreeSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:24:17 + | +LL | let _ = transmute::<_, BTreeSet>(BTreeSet::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:26:17 + | +LL | let _ = transmute::<_, BTreeSet>(BTreeSet::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashSet` to `std::collections::HashSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:29:17 + | +LL | let _ = transmute::<_, HashSet>(HashSet::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:31:17 + | +LL | let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:34:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:35:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:37:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:38:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:41:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:42:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:44:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:45:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs new file mode 100644 index 0000000000000..ce942751ada82 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -0,0 +1,12 @@ +#[warn(clippy::transmute_float_to_int)] + +fn float_to_int() { + let _: u32 = unsafe { std::mem::transmute(1f32) }; + let _: i32 = unsafe { std::mem::transmute(1f32) }; + let _: u64 = unsafe { std::mem::transmute(1f64) }; + let _: i64 = unsafe { std::mem::transmute(1f64) }; + let _: u64 = unsafe { std::mem::transmute(1.0) }; + let _: u64 = unsafe { std::mem::transmute(-1.0) }; +} + +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 new file mode 100644 index 0000000000000..eb786bb39f95a --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr @@ -0,0 +1,40 @@ +error: transmute from a `f32` to a `u32` + --> $DIR/transmute_float_to_int.rs:4: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` + +error: transmute from a `f32` to a `i32` + --> $DIR/transmute_float_to_int.rs:5:27 + | +LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute_float_to_int.rs:6:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` + +error: transmute from a `f64` to a `i64` + --> $DIR/transmute_float_to_int.rs:7:27 + | +LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute_float_to_int.rs:8:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute_float_to_int.rs:9:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs new file mode 100644 index 0000000000000..0d8a322f2b2b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -0,0 +1,54 @@ +#![warn(clippy::transmute_ptr_to_ptr)] + +// Make sure we can modify lifetimes, which is one of the recommended uses +// of transmute + +// Make sure we can do static lifetime transmutes +unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { + std::mem::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 { + std::mem::transmute::<&'a T, &'b T>(t) +} + +struct LifetimeParam<'a> { + s: &'a str, +} + +struct GenericParam { + t: T, +} + +fn transmute_ptr_to_ptr() { + let ptr = &1u32 as *const u32; + let mut_ptr = &mut 1u32 as *mut u32; + unsafe { + // pointer-to-pointer transmutes; bad + let _: *const f32 = std::mem::transmute(ptr); + let _: *mut f32 = std::mem::transmute(mut_ptr); + // ref-ref transmutes; bad + let _: &f32 = std::mem::transmute(&1u32); + let _: &f64 = std::mem::transmute(&1f32); + // ^ this test is here because both f32 and f64 are the same TypeVariant, but they are not + // the same type + let _: &mut f32 = std::mem::transmute(&mut 1u32); + let _: &GenericParam = std::mem::transmute(&GenericParam { t: 1u32 }); + } + + // these are recommendations for solving the above; if these lint we need to update + // those suggestions + let _ = ptr as *const f32; + let _ = mut_ptr as *mut f32; + let _ = unsafe { &*(&1u32 as *const u32 as *const f32) }; + let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) }; + + // transmute internal lifetimes, should not lint + let s = "hello world".to_owned(); + let lp = LifetimeParam { s: &s }; + let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) }; + let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr new file mode 100644 index 0000000000000..4d1b8fcc199e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr @@ -0,0 +1,40 @@ +error: transmute from a pointer to a pointer + --> $DIR/transmute_ptr_to_ptr.rs:29:29 + | +LL | let _: *const f32 = std::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` + | + = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` + +error: transmute from a pointer to a pointer + --> $DIR/transmute_ptr_to_ptr.rs:30:27 + | +LL | let _: *mut f32 = std::mem::transmute(mut_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:32:23 + | +LL | let _: &f32 = std::mem::transmute(&1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:33:23 + | +LL | let _: &f64 = std::mem::transmute(&1f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:36:27 + | +LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:37:37 + | +LL | let _: &GenericParam = std::mem::transmute(&GenericParam { t: 1u32 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam)` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs new file mode 100644 index 0000000000000..ba35c6adc4dee --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs @@ -0,0 +1,41 @@ +#![warn(clippy::transmute_ptr_to_ref)] + +unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { + let _: &T = std::mem::transmute(p); + let _: &T = &*p; + + let _: &mut T = std::mem::transmute(m); + let _: &mut T = &mut *m; + + let _: &T = std::mem::transmute(m); + let _: &T = &*m; + + let _: &mut T = std::mem::transmute(p as *mut T); + let _ = &mut *(p as *mut T); + + let _: &T = std::mem::transmute(o); + let _: &T = &*(o as *const T); + + let _: &mut T = std::mem::transmute(om); + let _: &mut T = &mut *(om as *mut T); + + let _: &T = std::mem::transmute(om); + let _: &T = &*(om as *const T); +} + +fn issue1231() { + struct Foo<'a, T> { + bar: &'a T, + } + + let raw = 42 as *const i32; + let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; + + let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; + + type Bar<'a> = &'a u8; + let raw = 42 as *const i32; + unsafe { std::mem::transmute::<_, Bar>(raw) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr new file mode 100644 index 0000000000000..df0598a58cd36 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr @@ -0,0 +1,64 @@ +error: transmute from a pointer type (`*const T`) to a reference type (`&T`) + --> $DIR/transmute_ptr_to_ref.rs:4:17 + | +LL | let _: &T = std::mem::transmute(p); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` + | + = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` + +error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) + --> $DIR/transmute_ptr_to_ref.rs:7:21 + | +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`) + --> $DIR/transmute_ptr_to_ref.rs:10:17 + | +LL | let _: &T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` + +error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) + --> $DIR/transmute_ptr_to_ref.rs:13:21 + | +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`) + --> $DIR/transmute_ptr_to_ref.rs:16:17 + | +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`) + --> $DIR/transmute_ptr_to_ref.rs:19:21 + | +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`) + --> $DIR/transmute_ptr_to_ref.rs:22:17 + | +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`) + --> $DIR/transmute_ptr_to_ref.rs:32:32 + | +LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<_>)` + +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<&u8>`) + --> $DIR/transmute_ptr_to_ref.rs:34:33 + | +LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<&_>)` + +error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) + --> $DIR/transmute_ptr_to_ref.rs:38:14 + | +LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/transmuting_null.rs b/src/tools/clippy/tests/ui/transmuting_null.rs new file mode 100644 index 0000000000000..ea3ee8edc81b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmuting_null.rs @@ -0,0 +1,30 @@ +#![allow(dead_code)] +#![warn(clippy::transmuting_null)] +#![allow(clippy::zero_ptr)] +#![allow(clippy::transmute_ptr_to_ref)] +#![allow(clippy::eq_op)] + +// Easy to lint because these only span one line. +fn one_liners() { + unsafe { + let _: &u64 = std::mem::transmute(0 as *const u64); + let _: &u64 = std::mem::transmute(std::ptr::null::()); + } +} + +pub const ZPTR: *const usize = 0 as *const _; +pub const NOT_ZPTR: *const usize = 1 as *const _; + +fn transmute_const() { + unsafe { + // Should raise a lint. + let _: &u64 = std::mem::transmute(ZPTR); + // Should NOT raise a lint. + let _: &u64 = std::mem::transmute(NOT_ZPTR); + } +} + +fn main() { + one_liners(); + transmute_const(); +} diff --git a/src/tools/clippy/tests/ui/transmuting_null.stderr b/src/tools/clippy/tests/ui/transmuting_null.stderr new file mode 100644 index 0000000000000..05f91ee2adaa8 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmuting_null.stderr @@ -0,0 +1,22 @@ +error: transmuting a known null pointer into a reference. + --> $DIR/transmuting_null.rs:10:23 + | +LL | let _: &u64 = std::mem::transmute(0 as *const u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::transmuting-null` implied by `-D warnings` + +error: transmuting a known null pointer into a reference. + --> $DIR/transmuting_null.rs:11:23 + | +LL | let _: &u64 = std::mem::transmute(std::ptr::null::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmuting a known null pointer into a reference. + --> $DIR/transmuting_null.rs:21:23 + | +LL | let _: &u64 = std::mem::transmute(ZPTR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs new file mode 100644 index 0000000000000..316426f1cf181 --- /dev/null +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs @@ -0,0 +1,114 @@ +// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" +// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" + +#![deny(clippy::trivially_copy_pass_by_ref)] +#![allow( + clippy::many_single_char_names, + clippy::blacklisted_name, + clippy::redundant_field_names +)] + +#[derive(Copy, Clone)] +struct Foo(u32); + +#[derive(Copy, Clone)] +struct Bar([u8; 24]); + +#[derive(Copy, Clone)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +struct FooRef<'a> { + foo: &'a Foo, +} + +type Baz = u32; + +fn good(a: &mut u32, b: u32, c: &Bar) {} + +fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 { + &foo.0 +} + +#[allow(clippy::needless_lifetimes)] +fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 { + &foo.0 +} + +fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef { + FooRef { foo } +} + +#[allow(clippy::needless_lifetimes)] +fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> { + FooRef { foo } +} + +fn bad(x: &u32, y: &Foo, z: &Baz) {} + +impl Foo { + fn good(self, a: &mut u32, b: u32, c: &Bar) {} + + fn good2(&mut self) {} + + fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + + fn bad2(x: &u32, y: &Foo, z: &Baz) {} +} + +impl AsRef for Foo { + fn as_ref(&self) -> &u32 { + &self.0 + } +} + +impl Bar { + fn good(&self, a: &mut u32, b: u32, c: &Bar) {} + + fn bad2(x: &u32, y: &Foo, z: &Baz) {} +} + +trait MyTrait { + fn trait_method(&self, _foo: &Foo); +} + +pub trait MyTrait2 { + fn trait_method2(&self, _color: &Color); +} + +impl MyTrait for Foo { + fn trait_method(&self, _foo: &Foo) { + unimplemented!() + } +} + +#[allow(unused_variables)] +mod issue3992 { + pub trait A { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn a(b: &u16) {} + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn c(d: &u16) {} +} + +fn main() { + let (mut foo, bar) = (Foo(0), Bar([0; 24])); + let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); + good(&mut a, b, &c); + good_return_implicit_lt_ref(&y); + good_return_explicit_lt_ref(&y); + bad(&x, &y, &z); + foo.good(&mut a, b, &c); + foo.good2(); + foo.bad(&x, &y, &z); + Foo::bad2(&x, &y, &z); + bar.good(&mut a, b, &c); + Bar::bad2(&x, &y, &z); + foo.as_ref(); +} diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr new file mode 100644 index 0000000000000..be0914e4a7947 --- /dev/null +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr @@ -0,0 +1,98 @@ +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:51:11 + | +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + | +note: the lint level is defined here + --> $DIR/trivially_copy_pass_by_ref.rs:4:9 + | +LL | #![deny(clippy::trivially_copy_pass_by_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:51:20 + | +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:51:29 + | +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:12 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^^ help: consider passing by value instead: `self` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:22 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:31 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:40 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:60:16 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:60:25 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:60:34 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:72:16 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:72:25 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:72:34 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:76:34 + | +LL | fn trait_method(&self, _foo: &Foo); + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:80:37 + | +LL | fn trait_method2(&self, _color: &Color); + | ^^^^^^ help: consider passing by value instead: `Color` + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed new file mode 100644 index 0000000000000..29d9139d3e346 --- /dev/null +++ b/src/tools/clippy/tests/ui/try_err.fixed @@ -0,0 +1,106 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![deny(clippy::try_err)] + +#[macro_use] +extern crate macro_rules; + +// Tests that a simple case works +// Should flag `Err(err)?` +pub fn basic_test() -> Result { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { + return Err(err); + } + Ok(0) +} + +// Tests that `.into()` is added when appropriate +pub fn into_test() -> Result { + let err: u8 = 1; + // To avoid warnings during rustfix + if true { + return Err(err.into()); + } + Ok(0) +} + +// Tests that tries in general don't trigger the error +pub fn negative_test() -> Result { + Ok(nested_error()? + 1) +} + +// Tests that `.into()` isn't added when the error type +// matches the surrounding closure's return type, even +// when it doesn't match the surrounding function's. +pub fn closure_matches_test() -> Result { + let res: Result = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + return Err(err); + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +// Tests that `.into()` isn't added when the error type +// doesn't match the surrounding closure's return type. +pub fn closure_into_test() -> Result { + let res: Result = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + return Err(err.into()); + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +fn nested_error() -> Result { + Ok(1) +} + +fn main() { + basic_test().unwrap(); + into_test().unwrap(); + negative_test().unwrap(); + closure_matches_test().unwrap(); + closure_into_test().unwrap(); + + // We don't want to lint in external macros + try_err!(); +} + +macro_rules! bar { + () => { + String::from("aasdfasdfasdfa") + }; +} + +macro_rules! foo { + () => { + bar!() + }; +} + +pub fn macro_inside(fail: bool) -> Result { + if fail { + return Err(foo!()); + } + Ok(0) +} diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs new file mode 100644 index 0000000000000..5e85d091a2ae7 --- /dev/null +++ b/src/tools/clippy/tests/ui/try_err.rs @@ -0,0 +1,106 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![deny(clippy::try_err)] + +#[macro_use] +extern crate macro_rules; + +// Tests that a simple case works +// Should flag `Err(err)?` +pub fn basic_test() -> Result { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(0) +} + +// Tests that `.into()` is added when appropriate +pub fn into_test() -> Result { + let err: u8 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(0) +} + +// Tests that tries in general don't trigger the error +pub fn negative_test() -> Result { + Ok(nested_error()? + 1) +} + +// Tests that `.into()` isn't added when the error type +// matches the surrounding closure's return type, even +// when it doesn't match the surrounding function's. +pub fn closure_matches_test() -> Result { + let res: Result = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +// Tests that `.into()` isn't added when the error type +// doesn't match the surrounding closure's return type. +pub fn closure_into_test() -> Result { + let res: Result = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +fn nested_error() -> Result { + Ok(1) +} + +fn main() { + basic_test().unwrap(); + into_test().unwrap(); + negative_test().unwrap(); + closure_matches_test().unwrap(); + closure_into_test().unwrap(); + + // We don't want to lint in external macros + try_err!(); +} + +macro_rules! bar { + () => { + String::from("aasdfasdfasdfa") + }; +} + +macro_rules! foo { + () => { + bar!() + }; +} + +pub fn macro_inside(fail: bool) -> Result { + if fail { + Err(foo!())?; + } + Ok(0) +} diff --git a/src/tools/clippy/tests/ui/try_err.stderr b/src/tools/clippy/tests/ui/try_err.stderr new file mode 100644 index 0000000000000..21e9d4048a588 --- /dev/null +++ b/src/tools/clippy/tests/ui/try_err.stderr @@ -0,0 +1,38 @@ +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:15:9 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err)` + | +note: the lint level is defined here + --> $DIR/try_err.rs:4:9 + | +LL | #![deny(clippy::try_err)] + | ^^^^^^^^^^^^^^^ + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:25:9 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err.into())` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:45:17 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err)` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:64:17 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err.into())` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:103:9 + | +LL | Err(foo!())?; + | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/ty_fn_sig.rs b/src/tools/clippy/tests/ui/ty_fn_sig.rs new file mode 100644 index 0000000000000..9e2753dcb18d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/ty_fn_sig.rs @@ -0,0 +1,14 @@ +// Regression test + +pub fn retry(f: F) { + for _i in 0.. { + f(); + } +} + +fn main() { + for y in 0..4 { + let func = || (); + func(); + } +} diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs new file mode 100644 index 0000000000000..8b538be762b0c --- /dev/null +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -0,0 +1,19 @@ +#[deny(clippy::type_repetition_in_bounds)] + +pub fn foo(_t: T) +where + T: Copy, + T: Clone, +{ + unimplemented!(); +} + +pub fn bar(_t: T, _u: U) +where + T: Copy, + U: Clone, +{ + unimplemented!(); +} + +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 new file mode 100644 index 0000000000000..4264e2e10bf17 --- /dev/null +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -0,0 +1,15 @@ +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:6:5 + | +LL | T: Clone, + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/type_repetition_in_bounds.rs:1:8 + | +LL | #[deny(clippy::type_repetition_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider combining the bounds: `T: Copy + Clone` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/types.fixed b/src/tools/clippy/tests/ui/types.fixed new file mode 100644 index 0000000000000..417da42edf17b --- /dev/null +++ b/src/tools/clippy/tests/ui/types.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::cast_lossless)] + +// should not warn on lossy casting in constant types +// because not supported yet +const C: i32 = 42; +const C_I64: i64 = C as i64; + +fn main() { + // should suggest i64::from(c) + let c: i32 = 42; + let c_i64: i64 = i64::from(c); +} diff --git a/src/tools/clippy/tests/ui/types.rs b/src/tools/clippy/tests/ui/types.rs new file mode 100644 index 0000000000000..b16e9e538b106 --- /dev/null +++ b/src/tools/clippy/tests/ui/types.rs @@ -0,0 +1,15 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::cast_lossless)] + +// should not warn on lossy casting in constant types +// because not supported yet +const C: i32 = 42; +const C_I64: i64 = C as i64; + +fn main() { + // should suggest i64::from(c) + let c: i32 = 42; + let c_i64: i64 = c as i64; +} diff --git a/src/tools/clippy/tests/ui/types.stderr b/src/tools/clippy/tests/ui/types.stderr new file mode 100644 index 0000000000000..59c3e05a1aa3c --- /dev/null +++ b/src/tools/clippy/tests/ui/types.stderr @@ -0,0 +1,10 @@ +error: casting `i32` to `i64` may become silently lossy if you later change the type + --> $DIR/types.rs:14:22 + | +LL | let c_i64: i64 = c as i64; + | ^^^^^^^^ help: try: `i64::from(c)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/unicode.rs b/src/tools/clippy/tests/ui/unicode.rs new file mode 100644 index 0000000000000..27db9594f3b33 --- /dev/null +++ b/src/tools/clippy/tests/ui/unicode.rs @@ -0,0 +1,23 @@ +#[warn(clippy::zero_width_space)] +fn zero() { + print!("Here >​< is a ZWS, and ​another"); + print!("This\u{200B}is\u{200B}fine"); +} + +#[warn(clippy::unicode_not_nfc)] +fn canon() { + print!("̀àh?"); + print!("a\u{0300}h?"); // also ok +} + +#[warn(clippy::non_ascii_literal)] +fn uni() { + print!("Üben!"); + print!("\u{DC}ben!"); // this is ok +} + +fn main() { + zero(); + uni(); + canon(); +} diff --git a/src/tools/clippy/tests/ui/unicode.stderr b/src/tools/clippy/tests/ui/unicode.stderr new file mode 100644 index 0000000000000..4575a132e5b2c --- /dev/null +++ b/src/tools/clippy/tests/ui/unicode.stderr @@ -0,0 +1,26 @@ +error: zero-width space detected + --> $DIR/unicode.rs:3:12 + | +LL | print!("Here >​< is a ZWS, and ​another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` + | + = note: `-D clippy::zero-width-space` implied by `-D warnings` + +error: non-NFC Unicode sequence detected + --> $DIR/unicode.rs:9:12 + | +LL | print!("̀àh?"); + | ^^^^^ help: consider replacing the string with: `"̀àh?"` + | + = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` + +error: literal non-ASCII character detected + --> $DIR/unicode.rs:15:12 + | +LL | print!("Üben!"); + | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` + | + = note: `-D clippy::non-ascii-literal` implied by `-D warnings` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/uninit.rs b/src/tools/clippy/tests/ui/uninit.rs new file mode 100644 index 0000000000000..f42b884e0f0e5 --- /dev/null +++ b/src/tools/clippy/tests/ui/uninit.rs @@ -0,0 +1,22 @@ +#![feature(stmt_expr_attributes)] + +use std::mem::MaybeUninit; + +fn main() { + let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + + // edge case: For now we lint on empty arrays + let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; + + // edge case: For now we accept unit tuples + let _: () = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because `MaybeUninit` allows uninitialized data. + let _: MaybeUninit = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because all constitutent types are uninit-compatible. + let _: (MaybeUninit, MaybeUninit) = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because all constitutent types are uninit-compatible. + let _: (MaybeUninit, [MaybeUninit; 2]) = unsafe { MaybeUninit::uninit().assume_init() }; +} diff --git a/src/tools/clippy/tests/ui/uninit.stderr b/src/tools/clippy/tests/ui/uninit.stderr new file mode 100644 index 0000000000000..a37233ecddaee --- /dev/null +++ b/src/tools/clippy/tests/ui/uninit.stderr @@ -0,0 +1,16 @@ +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:6:29 + | +LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::uninit_assumed_init)]` on by default + +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:9:31 + | +LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unit_arg.fixed b/src/tools/clippy/tests/ui/unit_arg.fixed new file mode 100644 index 0000000000000..a739cf7ad814e --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.fixed @@ -0,0 +1,64 @@ +// run-rustfix +#![warn(clippy::unit_arg)] +#![allow(unused_braces, clippy::no_effect, unused_must_use)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +struct Bar; + +impl Bar { + fn bar(&self, t: T) { + println!("{:?}", t); + } +} + +fn bad() { + foo(()); + foo(()); + foo(()); + foo(()); + foo3((), 2, 2); + let b = Bar; + b.bar(()); +} + +fn ok() { + foo(()); + foo(1); + foo({ 1 }); + foo3("a", 3, vec![3]); + let b = Bar; + b.bar({ 1 }); + b.bar(()); + question_mark(); +} + +fn question_mark() -> Result<(), ()> { + Ok(Ok(())?)?; + Ok(Ok(()))??; + Ok(()) +} + +#[allow(dead_code)] +mod issue_2945 { + fn unit_fn() -> Result<(), i32> { + Ok(()) + } + + fn fallible() -> Result<(), i32> { + Ok(unit_fn()?) + } +} + +fn main() { + bad(); + ok(); +} diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs new file mode 100644 index 0000000000000..d90c49f79de62 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -0,0 +1,71 @@ +// run-rustfix +#![warn(clippy::unit_arg)] +#![allow(unused_braces, clippy::no_effect, unused_must_use)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +struct Bar; + +impl Bar { + fn bar(&self, t: T) { + println!("{:?}", t); + } +} + +fn bad() { + foo({}); + foo({ + 1; + }); + foo(foo(1)); + foo({ + foo(1); + foo(2); + }); + foo3({}, 2, 2); + let b = Bar; + b.bar({ + 1; + }); +} + +fn ok() { + foo(()); + foo(1); + foo({ 1 }); + foo3("a", 3, vec![3]); + let b = Bar; + b.bar({ 1 }); + b.bar(()); + question_mark(); +} + +fn question_mark() -> Result<(), ()> { + Ok(Ok(())?)?; + Ok(Ok(()))??; + Ok(()) +} + +#[allow(dead_code)] +mod issue_2945 { + fn unit_fn() -> Result<(), i32> { + Ok(()) + } + + fn fallible() -> Result<(), i32> { + Ok(unit_fn()?) + } +} + +fn main() { + bad(); + ok(); +} diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr new file mode 100644 index 0000000000000..21ccc684ea9de --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -0,0 +1,79 @@ +error: passing a unit value to a function + --> $DIR/unit_arg.rs:24:9 + | +LL | foo({}); + | ^^ + | + = note: `-D clippy::unit-arg` implied by `-D warnings` +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:25:9 + | +LL | foo({ + | _________^ +LL | | 1; +LL | | }); + | |_____^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:28:9 + | +LL | foo(foo(1)); + | ^^^^^^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:29:9 + | +LL | foo({ + | _________^ +LL | | foo(1); +LL | | foo(2); +LL | | }); + | |_____^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:33:10 + | +LL | foo3({}, 2, 2); + | ^^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo3((), 2, 2); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:35:11 + | +LL | b.bar({ + | ___________^ +LL | | 1; +LL | | }); + | |_____^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | b.bar(()); + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unit_cmp.rs b/src/tools/clippy/tests/ui/unit_cmp.rs new file mode 100644 index 0000000000000..8d3a4eed82e3e --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_cmp.rs @@ -0,0 +1,57 @@ +#![warn(clippy::unit_cmp)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +#[derive(PartialEq)] +pub struct ContainsUnit(()); // should be fine + +fn main() { + // this is fine + if true == false {} + + // this warns + if { + true; + } == { + false; + } {} + + if { + true; + } > { + false; + } {} + + assert_eq!( + { + true; + }, + { + false; + } + ); + debug_assert_eq!( + { + true; + }, + { + false; + } + ); + + assert_ne!( + { + true; + }, + { + false; + } + ); + debug_assert_ne!( + { + true; + }, + { + false; + } + ); +} diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr new file mode 100644 index 0000000000000..c8c0a85dfc102 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_cmp.stderr @@ -0,0 +1,82 @@ +error: ==-comparison of unit values detected. This will always be true + --> $DIR/unit_cmp.rs:12:8 + | +LL | if { + | ________^ +LL | | true; +LL | | } == { +LL | | false; +LL | | } {} + | |_____^ + | + = note: `-D clippy::unit-cmp` implied by `-D warnings` + +error: >-comparison of unit values detected. This will always be false + --> $DIR/unit_cmp.rs:18:8 + | +LL | if { + | ________^ +LL | | true; +LL | | } > { +LL | | false; +LL | | } {} + | |_____^ + +error: `assert_eq` of unit values detected. This will always succeed + --> $DIR/unit_cmp.rs:24:5 + | +LL | / assert_eq!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `debug_assert_eq` of unit values detected. This will always succeed + --> $DIR/unit_cmp.rs:32:5 + | +LL | / debug_assert_eq!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert_ne` of unit values detected. This will always fail + --> $DIR/unit_cmp.rs:41:5 + | +LL | / assert_ne!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `debug_assert_ne` of unit values detected. This will always fail + --> $DIR/unit_cmp.rs:49:5 + | +LL | / debug_assert_ne!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unknown_attribute.rs b/src/tools/clippy/tests/ui/unknown_attribute.rs new file mode 100644 index 0000000000000..e993e63f8ed86 --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_attribute.rs @@ -0,0 +1,3 @@ +#[clippy::unknown] +#[clippy::cognitive_complexity = "1"] +fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_attribute.stderr b/src/tools/clippy/tests/ui/unknown_attribute.stderr new file mode 100644 index 0000000000000..47e37aed2464e --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_attribute.stderr @@ -0,0 +1,8 @@ +error: Usage of unknown attribute + --> $DIR/unknown_attribute.rs:1:11 + | +LL | #[clippy::unknown] + | ^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed new file mode 100644 index 0000000000000..4249ff8a958d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::pedantic)] +// Should suggest lowercase +#![allow(clippy::all)] +#![warn(clippy::cmp_nan)] + +// Should suggest similar clippy lint name +#[warn(clippy::if_not_else)] +#[warn(clippy::unnecessary_cast)] +#[warn(clippy::useless_transmute)] +// Shouldn't suggest rustc lint name(`dead_code`) +#[warn(clippy::drop_copy)] +// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) +#[warn(clippy::unused_self)] +// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) +#[warn(clippy::redundant_static_lifetimes)] +fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs new file mode 100644 index 0000000000000..5db345f544413 --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::pedantic)] +// Should suggest lowercase +#![allow(clippy::All)] +#![warn(clippy::CMP_NAN)] + +// Should suggest similar clippy lint name +#[warn(clippy::if_not_els)] +#[warn(clippy::UNNecsaRy_cAst)] +#[warn(clippy::useles_transute)] +// Shouldn't suggest rustc lint name(`dead_code`) +#[warn(clippy::dead_cod)] +// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) +#[warn(clippy::unused_colle)] +// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) +#[warn(clippy::const_static_lifetim)] +fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr new file mode 100644 index 0000000000000..1b859043bb53b --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr @@ -0,0 +1,52 @@ +error: unknown clippy lint: clippy::if_not_els + --> $DIR/unknown_clippy_lints.rs:9:8 + | +LL | #[warn(clippy::if_not_els)] + | ^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::if_not_else` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: unknown clippy lint: clippy::UNNecsaRy_cAst + --> $DIR/unknown_clippy_lints.rs:10:8 + | +LL | #[warn(clippy::UNNecsaRy_cAst)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_cast` + +error: unknown clippy lint: clippy::useles_transute + --> $DIR/unknown_clippy_lints.rs:11:8 + | +LL | #[warn(clippy::useles_transute)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::useless_transmute` + +error: unknown clippy lint: clippy::dead_cod + --> $DIR/unknown_clippy_lints.rs:13:8 + | +LL | #[warn(clippy::dead_cod)] + | ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::drop_copy` + +error: unknown clippy lint: clippy::unused_colle + --> $DIR/unknown_clippy_lints.rs:15:8 + | +LL | #[warn(clippy::unused_colle)] + | ^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unused_self` + +error: unknown clippy lint: clippy::const_static_lifetim + --> $DIR/unknown_clippy_lints.rs:17:8 + | +LL | #[warn(clippy::const_static_lifetim)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes` + +error: unknown clippy lint: clippy::All + --> $DIR/unknown_clippy_lints.rs:5:10 + | +LL | #![allow(clippy::All)] + | ^^^^^^^^^^^ help: lowercase the lint name: `clippy::all` + +error: unknown clippy lint: clippy::CMP_NAN + --> $DIR/unknown_clippy_lints.rs:6:9 + | +LL | #![warn(clippy::CMP_NAN)] + | ^^^^^^^^^^^^^^^ help: lowercase the lint name: `clippy::cmp_nan` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs new file mode 100644 index 0000000000000..df9b227eeb3f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -0,0 +1,23 @@ +#![warn(clippy::unnecessary_cast)] +#![allow(clippy::no_effect)] + +fn main() { + // Test cast_unnecessary + 1i32 as i32; + 1f32 as f32; + false as bool; + &1i32 as &i32; + + // macro version + macro_rules! foo { + ($a:ident, $b:ident) => { + #[allow(unused)] + pub fn $a() -> $b { + 1 as $b + } + }; + } + foo!(a, i32); + foo!(b, f32); + foo!(c, f64); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr new file mode 100644 index 0000000000000..8981d13e8eabb --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -0,0 +1,22 @@ +error: casting to the same type is unnecessary (`i32` -> `i32`) + --> $DIR/unnecessary_cast.rs:6:5 + | +LL | 1i32 as i32; + | ^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + +error: casting to the same type is unnecessary (`f32` -> `f32`) + --> $DIR/unnecessary_cast.rs:7:5 + | +LL | 1f32 as f32; + | ^^^^^^^^^^^ + +error: casting to the same type is unnecessary (`bool` -> `bool`) + --> $DIR/unnecessary_cast.rs:8:5 + | +LL | false as bool; + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed new file mode 100644 index 0000000000000..fb89a9fce3d5b --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::unnecessary_cast)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + // casting integer literal to float is unnecessary + 100_f32; + 100_f64; + 100_f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 1.0 as f64; + 1 as u64; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs new file mode 100644 index 0000000000000..4a0c8620dc134 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::unnecessary_cast)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + // casting integer literal to float is unnecessary + 100 as f32; + 100 as f64; + 100_i32 as f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 1.0 as f64; + 1 as u64; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr new file mode 100644 index 0000000000000..8ff1e5dea6003 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr @@ -0,0 +1,22 @@ +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:8:5 + | +LL | 100 as f32; + | ^^^^^^^^^^ help: try: `100_f32` + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:9:5 + | +LL | 100 as f64; + | ^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:10:5 + | +LL | 100_i32 as f64; + | ^^^^^^^^^^^^^^ help: try: `100_f64` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.rs b/src/tools/clippy/tests/ui/unnecessary_clone.rs new file mode 100644 index 0000000000000..f1cc5b564c1dc --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_clone.rs @@ -0,0 +1,117 @@ +// does not test any rustfixable lints + +#![warn(clippy::clone_on_ref_ptr)] +#![allow(unused, clippy::redundant_clone)] + +use std::cell::RefCell; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn main() {} + +fn is_ascii(ch: char) -> bool { + ch.is_ascii() +} + +fn clone_on_copy() { + 42.clone(); + + vec![1].clone(); // ok, not a Copy type + Some(vec![1]).clone(); // ok, not a Copy type + (&42).clone(); + + let rc = RefCell::new(0); + rc.borrow().clone(); + + // Issue #4348 + let mut x = 43; + let _ = &x.clone(); // ok, getting a ref + 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate + is_ascii('z'.clone()); + + // Issue #5436 + let mut vec = Vec::new(); + vec.push(42.clone()); +} + +fn clone_on_ref_ptr() { + let rc = Rc::new(true); + let arc = Arc::new(true); + + let rcweak = Rc::downgrade(&rc); + let arc_weak = Arc::downgrade(&arc); + + rc.clone(); + Rc::clone(&rc); + + arc.clone(); + Arc::clone(&arc); + + rcweak.clone(); + rc::Weak::clone(&rcweak); + + arc_weak.clone(); + sync::Weak::clone(&arc_weak); + + let x = Arc::new(SomeImpl); + let _: Arc = x.clone(); +} + +fn clone_on_copy_generic(t: T) { + t.clone(); + + Some(t).clone(); +} + +fn clone_on_double_ref() { + let x = vec![1]; + let y = &&x; + let z: &Vec<_> = y.clone(); + + println!("{:p} {:p}", *y, z); +} + +mod many_derefs { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = a.clone(); + let _: E = *****a; + } + + fn check(mut encoded: &[u8]) { + let _ = &mut encoded.clone(); + let _ = &encoded.clone(); + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr new file mode 100644 index 0000000000000..6176a2bc46479 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr @@ -0,0 +1,130 @@ +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:21:5 + | +LL | 42.clone(); + | ^^^^^^^^^^ help: try removing the `clone` call: `42` + | + = note: `-D clippy::clone-on-copy` implied by `-D warnings` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:25:5 + | +LL | (&42).clone(); + | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:28:5 + | +LL | rc.borrow().clone(); + | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:34:14 + | +LL | is_ascii('z'.clone()); + | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:38:14 + | +LL | vec.push(42.clone()); + | ^^^^^^^^^^ help: try removing the `clone` call: `42` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:48:5 + | +LL | rc.clone(); + | ^^^^^^^^^^ help: try this: `Rc::::clone(&rc)` + | + = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:51:5 + | +LL | arc.clone(); + | ^^^^^^^^^^^ help: try this: `Arc::::clone(&arc)` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:54:5 + | +LL | rcweak.clone(); + | ^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&rcweak)` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:57:5 + | +LL | arc_weak.clone(); + | ^^^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&arc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:61:33 + | +LL | let _: Arc = x.clone(); + | ^^^^^^^^^ help: try this: `Arc::::clone(&x)` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:65:5 + | +LL | t.clone(); + | ^^^^^^^^^ help: try removing the `clone` call: `t` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:67:5 + | +LL | Some(t).clone(); + | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` + +error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type + --> $DIR/unnecessary_clone.rs:73:22 + | +LL | let z: &Vec<_> = y.clone(); + | ^^^^^^^^^ + | + = note: `#[deny(clippy::clone_double_ref)]` on by default +help: try dereferencing it + | +LL | let z: &Vec<_> = &(*y).clone(); + | ^^^^^^^^^^^^^ +help: or try being explicit if you are sure, that you want to clone a reference + | +LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:109:20 + | +LL | let _: E = a.clone(); + | ^^^^^^^^^ help: try dereferencing it: `*****a` + +error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type + --> $DIR/unnecessary_clone.rs:114:22 + | +LL | let _ = &mut encoded.clone(); + | ^^^^^^^^^^^^^^^ + | +help: try dereferencing it + | +LL | let _ = &mut &(*encoded).clone(); + | ^^^^^^^^^^^^^^^^^^^ +help: or try being explicit if you are sure, that you want to clone a reference + | +LL | let _ = &mut <&[u8]>::clone(encoded); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type + --> $DIR/unnecessary_clone.rs:115:18 + | +LL | let _ = &encoded.clone(); + | ^^^^^^^^^^^^^^^ + | +help: try dereferencing it + | +LL | let _ = &&(*encoded).clone(); + | ^^^^^^^^^^^^^^^^^^^ +help: or try being explicit if you are sure, that you want to clone a reference + | +LL | let _ = &<&[u8]>::clone(encoded); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs new file mode 100644 index 0000000000000..af858e4abcf55 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs @@ -0,0 +1,17 @@ +fn main() { + let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); + let _ = (0..4).filter_map(|x| { + if x > 1 { + return Some(x); + }; + None + }); + let _ = (0..4).filter_map(|x| match x { + 0 | 1 => None, + _ => Some(x), + }); + + let _ = (0..4).filter_map(|x| Some(x + 1)); + + let _ = (0..4).filter_map(i32::checked_abs); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr new file mode 100644 index 0000000000000..041829c3c7855 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr @@ -0,0 +1,38 @@ +error: this `.filter_map` can be written more simply using `.filter` + --> $DIR/unnecessary_filter_map.rs:2:13 + | +LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` + +error: this `.filter_map` can be written more simply using `.filter` + --> $DIR/unnecessary_filter_map.rs:3:13 + | +LL | let _ = (0..4).filter_map(|x| { + | _____________^ +LL | | if x > 1 { +LL | | return Some(x); +LL | | }; +LL | | None +LL | | }); + | |______^ + +error: this `.filter_map` can be written more simply using `.filter` + --> $DIR/unnecessary_filter_map.rs:9:13 + | +LL | let _ = (0..4).filter_map(|x| match x { + | _____________^ +LL | | 0 | 1 => None, +LL | | _ => Some(x), +LL | | }); + | |______^ + +error: this `.filter_map` can be written more simply using `.map` + --> $DIR/unnecessary_filter_map.rs:14:13 + | +LL | let _ = (0..4).filter_map(|x| Some(x + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_flat_map.fixed b/src/tools/clippy/tests/ui/unnecessary_flat_map.fixed new file mode 100644 index 0000000000000..dfe3bd47e1394 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_flat_map.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused_imports)] +#![warn(clippy::flat_map_identity)] + +use std::convert; + +fn main() { + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_flat_map.rs b/src/tools/clippy/tests/ui/unnecessary_flat_map.rs new file mode 100644 index 0000000000000..393b95692554c --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_flat_map.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused_imports)] +#![warn(clippy::flat_map_identity)] + +use std::convert; + +fn main() { + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(|x| x); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(convert::identity); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_flat_map.stderr b/src/tools/clippy/tests/ui/unnecessary_flat_map.stderr new file mode 100644 index 0000000000000..a1cd5745e4949 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_flat_map.stderr @@ -0,0 +1,16 @@ +error: called `flat_map(|x| x)` on an `Iterator` + --> $DIR/unnecessary_flat_map.rs:10:22 + | +LL | let _ = iterator.flat_map(|x| x); + | ^^^^^^^^^^^^^^^ help: try: `flatten()` + | + = note: `-D clippy::flat-map-identity` implied by `-D warnings` + +error: called `flat_map(std::convert::identity)` on an `Iterator` + --> $DIR/unnecessary_flat_map.rs:13:22 + | +LL | let _ = iterator.flat_map(convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.fixed b/src/tools/clippy/tests/ui/unnecessary_fold.fixed new file mode 100644 index 0000000000000..52300a3b64061 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_fold.fixed @@ -0,0 +1,52 @@ +// run-rustfix + +#![allow(dead_code)] + +/// Calls which should trigger the `UNNECESSARY_FOLD` lint +fn unnecessary_fold() { + // Can be replaced by .any + let _ = (0..3).any(|x| x > 2); + // Can be replaced by .all + let _ = (0..3).all(|x| x > 2); + // Can be replaced by .sum + let _: i32 = (0..3).sum(); + // Can be replaced by .product + let _: i32 = (0..3).product(); +} + +/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)` +fn unnecessary_fold_span_for_multi_element_chain() { + let _: bool = (0..3).map(|x| 2 * x).any(|x| x > 2); +} + +/// Calls which should not trigger the `UNNECESSARY_FOLD` lint +fn unnecessary_fold_should_ignore() { + let _ = (0..3).fold(true, |acc, x| acc || x > 2); + let _ = (0..3).fold(false, |acc, x| acc && x > 2); + let _ = (0..3).fold(1, |acc, x| acc + x); + let _ = (0..3).fold(0, |acc, x| acc * x); + let _ = (0..3).fold(0, |acc, x| 1 + acc + x); + + // We only match against an accumulator on the left + // hand side. We could lint for .sum and .product when + // it's on the right, but don't for now (and this wouldn't + // be valid if we extended the lint to cover arbitrary numeric + // types). + let _ = (0..3).fold(false, |acc, x| x > 2 || acc); + let _ = (0..3).fold(true, |acc, x| x > 2 && acc); + let _ = (0..3).fold(0, |acc, x| x + acc); + let _ = (0..3).fold(1, |acc, x| x * acc); + + let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len()); + let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len()); +} + +/// Should lint only the line containing the fold +fn unnecessary_fold_over_multiple_lines() { + let _ = (0..3) + .map(|x| x + 1) + .filter(|x| x % 2 == 0) + .any(|x| x > 2); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.rs b/src/tools/clippy/tests/ui/unnecessary_fold.rs new file mode 100644 index 0000000000000..4028d80c0a3cb --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_fold.rs @@ -0,0 +1,52 @@ +// run-rustfix + +#![allow(dead_code)] + +/// Calls which should trigger the `UNNECESSARY_FOLD` lint +fn unnecessary_fold() { + // Can be replaced by .any + let _ = (0..3).fold(false, |acc, x| acc || x > 2); + // Can be replaced by .all + let _ = (0..3).fold(true, |acc, x| acc && x > 2); + // Can be replaced by .sum + let _: i32 = (0..3).fold(0, |acc, x| acc + x); + // Can be replaced by .product + let _: i32 = (0..3).fold(1, |acc, x| acc * x); +} + +/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)` +fn unnecessary_fold_span_for_multi_element_chain() { + let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2); +} + +/// Calls which should not trigger the `UNNECESSARY_FOLD` lint +fn unnecessary_fold_should_ignore() { + let _ = (0..3).fold(true, |acc, x| acc || x > 2); + let _ = (0..3).fold(false, |acc, x| acc && x > 2); + let _ = (0..3).fold(1, |acc, x| acc + x); + let _ = (0..3).fold(0, |acc, x| acc * x); + let _ = (0..3).fold(0, |acc, x| 1 + acc + x); + + // We only match against an accumulator on the left + // hand side. We could lint for .sum and .product when + // it's on the right, but don't for now (and this wouldn't + // be valid if we extended the lint to cover arbitrary numeric + // types). + let _ = (0..3).fold(false, |acc, x| x > 2 || acc); + let _ = (0..3).fold(true, |acc, x| x > 2 && acc); + let _ = (0..3).fold(0, |acc, x| x + acc); + let _ = (0..3).fold(1, |acc, x| x * acc); + + let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len()); + let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len()); +} + +/// Should lint only the line containing the fold +fn unnecessary_fold_over_multiple_lines() { + let _ = (0..3) + .map(|x| x + 1) + .filter(|x| x % 2 == 0) + .fold(false, |acc, x| acc || x > 2); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.stderr b/src/tools/clippy/tests/ui/unnecessary_fold.stderr new file mode 100644 index 0000000000000..22c44588ab7af --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_fold.stderr @@ -0,0 +1,40 @@ +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:8:20 + | +LL | let _ = (0..3).fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + | + = note: `-D clippy::unnecessary-fold` implied by `-D warnings` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:10:20 + | +LL | let _ = (0..3).fold(true, |acc, x| acc && x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:12:25 + | +LL | let _: i32 = (0..3).fold(0, |acc, x| acc + x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:14:25 + | +LL | let _: i32 = (0..3).fold(1, |acc, x| acc * x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:19:41 + | +LL | let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:49:10 + | +LL | .fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed new file mode 100644 index 0000000000000..2fca96c4cd556 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -0,0 +1,79 @@ +// run-rustfix + +#![feature(box_syntax)] +#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] +#![warn(clippy::unnecessary_operation)] + +struct Tuple(i32); +struct Struct { + field: i32, +} +enum Enum { + Tuple(i32), + Struct { field: i32 }, +} +struct DropStruct { + field: i32, +} +impl Drop for DropStruct { + fn drop(&mut self) {} +} +struct DropTuple(i32); +impl Drop for DropTuple { + fn drop(&mut self) {} +} +enum DropEnum { + Tuple(i32), + Struct { field: i32 }, +} +impl Drop for DropEnum { + fn drop(&mut self) {} +} +struct FooString { + s: String, +} + +fn get_number() -> i32 { + 0 +} + +fn get_usize() -> usize { + 0 +} +fn get_struct() -> Struct { + Struct { field: 0 } +} +fn get_drop_struct() -> DropStruct { + DropStruct { field: 0 } +} + +fn main() { + get_number(); + get_number(); + get_struct(); + get_number(); + get_number(); + 5;get_number(); + get_number(); + get_number(); + 5;6;get_number(); + get_number(); + get_number(); + get_number(); + 5;get_number(); + 42;get_number(); + [42, 55];get_usize(); + 42;get_number(); + get_number(); + [42; 55];get_usize(); + get_number(); + String::from("blah"); + + // Do not warn + DropTuple(get_number()); + DropStruct { field: get_number() }; + DropStruct { field: get_number() }; + DropStruct { ..get_drop_struct() }; + DropEnum::Tuple(get_number()); + DropEnum::Struct { field: get_number() }; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs new file mode 100644 index 0000000000000..08cb9ab522ee0 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -0,0 +1,83 @@ +// run-rustfix + +#![feature(box_syntax)] +#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] +#![warn(clippy::unnecessary_operation)] + +struct Tuple(i32); +struct Struct { + field: i32, +} +enum Enum { + Tuple(i32), + Struct { field: i32 }, +} +struct DropStruct { + field: i32, +} +impl Drop for DropStruct { + fn drop(&mut self) {} +} +struct DropTuple(i32); +impl Drop for DropTuple { + fn drop(&mut self) {} +} +enum DropEnum { + Tuple(i32), + Struct { field: i32 }, +} +impl Drop for DropEnum { + fn drop(&mut self) {} +} +struct FooString { + s: String, +} + +fn get_number() -> i32 { + 0 +} + +fn get_usize() -> usize { + 0 +} +fn get_struct() -> Struct { + Struct { field: 0 } +} +fn get_drop_struct() -> DropStruct { + DropStruct { field: 0 } +} + +fn main() { + Tuple(get_number()); + Struct { field: get_number() }; + Struct { ..get_struct() }; + Enum::Tuple(get_number()); + Enum::Struct { field: get_number() }; + 5 + get_number(); + *&get_number(); + &get_number(); + (5, 6, get_number()); + box get_number(); + get_number()..; + ..get_number(); + 5..get_number(); + [42, get_number()]; + [42, 55][get_usize()]; + (42, get_number()).1; + [get_number(); 55]; + [42; 55][get_usize()]; + { + get_number() + }; + FooString { + s: String::from("blah"), + }; + + // Do not warn + DropTuple(get_number()); + DropStruct { field: get_number() }; + DropStruct { field: get_number() }; + DropStruct { ..get_drop_struct() }; + DropEnum::Tuple(get_number()); + DropEnum::Struct { field: get_number() }; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr new file mode 100644 index 0000000000000..f88c9f9908bea --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr @@ -0,0 +1,128 @@ +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:51:5 + | +LL | Tuple(get_number()); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | + = note: `-D clippy::unnecessary-operation` implied by `-D warnings` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:52:5 + | +LL | Struct { field: get_number() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:53:5 + | +LL | Struct { ..get_struct() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:54:5 + | +LL | Enum::Tuple(get_number()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:55:5 + | +LL | Enum::Struct { field: get_number() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:56:5 + | +LL | 5 + get_number(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:57:5 + | +LL | *&get_number(); + | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:58:5 + | +LL | &get_number(); + | ^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:59:5 + | +LL | (5, 6, get_number()); + | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:60:5 + | +LL | box get_number(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:61:5 + | +LL | get_number()..; + | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:62:5 + | +LL | ..get_number(); + | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:63:5 + | +LL | 5..get_number(); + | ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:64:5 + | +LL | [42, get_number()]; + | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:65:5 + | +LL | [42, 55][get_usize()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:66:5 + | +LL | (42, get_number()).1; + | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:67:5 + | +LL | [get_number(); 55]; + | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:68:5 + | +LL | [42; 55][get_usize()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:69:5 + | +LL | / { +LL | | get_number() +LL | | }; + | |______^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:72:5 + | +LL | / FooString { +LL | | s: String::from("blah"), +LL | | }; + | |______^ help: replace it with: `String::from("blah");` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_ref.fixed b/src/tools/clippy/tests/ui/unnecessary_ref.fixed new file mode 100644 index 0000000000000..f7b94118d4e86 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_ref.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(stmt_expr_attributes)] +#![allow(unused_variables)] + +struct Outer { + inner: u32, +} + +#[deny(clippy::ref_in_deref)] +fn main() { + let outer = Outer { inner: 0 }; + let inner = outer.inner; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_ref.rs b/src/tools/clippy/tests/ui/unnecessary_ref.rs new file mode 100644 index 0000000000000..4e585b9b96ba9 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_ref.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(stmt_expr_attributes)] +#![allow(unused_variables)] + +struct Outer { + inner: u32, +} + +#[deny(clippy::ref_in_deref)] +fn main() { + let outer = Outer { inner: 0 }; + let inner = (&outer).inner; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_ref.stderr b/src/tools/clippy/tests/ui/unnecessary_ref.stderr new file mode 100644 index 0000000000000..34ba167a94790 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_ref.stderr @@ -0,0 +1,14 @@ +error: Creating a reference that is immediately dereferenced. + --> $DIR/unnecessary_ref.rs:13:17 + | +LL | let inner = (&outer).inner; + | ^^^^^^^^ help: try this: `outer` + | +note: the lint level is defined here + --> $DIR/unnecessary_ref.rs:10:8 + | +LL | #[deny(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.rs b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs new file mode 100644 index 0000000000000..fa639aa70d61d --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unneeded_field_pattern)] +#[allow(dead_code, unused)] + +struct Foo { + a: i32, + b: i32, + c: i32, +} + +fn main() { + let f = Foo { a: 0, b: 0, c: 0 }; + + match f { + Foo { a: _, b: 0, .. } => {}, + + Foo { a: _, b: _, c: _ } => {}, + } + match f { + Foo { b: 0, .. } => {}, // should be OK + Foo { .. } => {}, // and the Force might be with this one + } +} diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr new file mode 100644 index 0000000000000..e7b92ce1e197b --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr @@ -0,0 +1,19 @@ +error: You matched a field with a wildcard pattern. Consider using `..` instead + --> $DIR/unneeded_field_pattern.rs:14:15 + | +LL | Foo { a: _, b: 0, .. } => {}, + | ^^^^ + | + = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings` + = help: Try with `Foo { b: 0, .. }` + +error: All the struct fields are matched to a wildcard pattern, consider using `..`. + --> $DIR/unneeded_field_pattern.rs:16:9 + | +LL | Foo { a: _, b: _, c: _ } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Try with `Foo { .. }` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed new file mode 100644 index 0000000000000..12c3461c95579 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed @@ -0,0 +1,45 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] +#![deny(clippy::unneeded_wildcard_pattern)] + +fn main() { + let t = (0, 1, 2, 3); + + if let (0, ..) = t {}; + if let (0, ..) = t {}; + if let (.., 0) = t {}; + if let (.., 0) = t {}; + if let (0, ..) = t {}; + if let (0, ..) = t {}; + if let (_, 0, ..) = t {}; + if let (.., 0, _) = t {}; + if let (0, _, _, _) = t {}; + if let (0, ..) = t {}; + if let (.., 0) = t {}; + + #[rustfmt::skip] + { + if let (0, ..,) = t {}; + } + + struct S(usize, usize, usize, usize); + + let s = S(0, 1, 2, 3); + + if let S(0, ..) = s {}; + if let S(0, ..) = s {}; + if let S(.., 0) = s {}; + if let S(.., 0) = s {}; + if let S(0, ..) = s {}; + if let S(0, ..) = s {}; + if let S(_, 0, ..) = s {}; + if let S(.., 0, _) = s {}; + if let S(0, _, _, _) = s {}; + if let S(0, ..) = s {}; + if let S(.., 0) = s {}; + + #[rustfmt::skip] + { + if let S(0, ..,) = s {}; + } +} diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs new file mode 100644 index 0000000000000..4ac01d5d23b04 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs @@ -0,0 +1,45 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] +#![deny(clippy::unneeded_wildcard_pattern)] + +fn main() { + let t = (0, 1, 2, 3); + + if let (0, .., _) = t {}; + if let (0, _, ..) = t {}; + if let (_, .., 0) = t {}; + if let (.., _, 0) = t {}; + if let (0, _, _, ..) = t {}; + if let (0, .., _, _) = t {}; + if let (_, 0, ..) = t {}; + if let (.., 0, _) = t {}; + if let (0, _, _, _) = t {}; + if let (0, ..) = t {}; + if let (.., 0) = t {}; + + #[rustfmt::skip] + { + if let (0, .., _, _,) = t {}; + } + + struct S(usize, usize, usize, usize); + + let s = S(0, 1, 2, 3); + + if let S(0, .., _) = s {}; + if let S(0, _, ..) = s {}; + if let S(_, .., 0) = s {}; + if let S(.., _, 0) = s {}; + if let S(0, _, _, ..) = s {}; + if let S(0, .., _, _) = s {}; + if let S(_, 0, ..) = s {}; + if let S(.., 0, _) = s {}; + if let S(0, _, _, _) = s {}; + if let S(0, ..) = s {}; + if let S(.., 0) = s {}; + + #[rustfmt::skip] + { + if let S(0, .., _, _,) = s {}; + } +} diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr new file mode 100644 index 0000000000000..716d9ecff89af --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr @@ -0,0 +1,92 @@ +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:8:18 + | +LL | if let (0, .., _) = t {}; + | ^^^ help: remove it + | +note: the lint level is defined here + --> $DIR/unneeded_wildcard_pattern.rs:3:9 + | +LL | #![deny(clippy::unneeded_wildcard_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:9:16 + | +LL | if let (0, _, ..) = t {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:10:13 + | +LL | if let (_, .., 0) = t {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:11:15 + | +LL | if let (.., _, 0) = t {}; + | ^^^ help: remove it + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:12:16 + | +LL | if let (0, _, _, ..) = t {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:13:18 + | +LL | if let (0, .., _, _) = t {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:22:22 + | +LL | if let (0, .., _, _,) = t {}; + | ^^^^^^ help: remove them + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:29:19 + | +LL | if let S(0, .., _) = s {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:30:17 + | +LL | if let S(0, _, ..) = s {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:31:14 + | +LL | if let S(_, .., 0) = s {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:32:16 + | +LL | if let S(.., _, 0) = s {}; + | ^^^ help: remove it + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:33:17 + | +LL | if let S(0, _, _, ..) = s {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:34:19 + | +LL | if let S(0, .., _, _) = s {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:43:23 + | +LL | if let S(0, .., _, _,) = s {}; + | ^^^^^^ help: remove them + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/unreadable_literal.fixed b/src/tools/clippy/tests/ui/unreadable_literal.fixed new file mode 100644 index 0000000000000..3f358d9ecaa0a --- /dev/null +++ b/src/tools/clippy/tests/ui/unreadable_literal.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::unreadable_literal)] + +struct Foo(u64); + +macro_rules! foo { + () => { + Foo(123123123123) + }; +} + +fn main() { + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 65536, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = (0b11_0110_i64, 0xcafe_babe_usize, 123_456_f32, 1.234_567_f32); + let _good_sci = 1.1234e1; + let _bad_sci = 1.123_456e1; + + let _fail9 = 0x00ab_cdef; + let _fail10: u32 = 0xBAFE_BAFE; + let _fail11 = 0x0abc_deff; + let _fail12: i128 = 0x00ab_cabc_abca_bcab_cabc; + + let _ = foo!(); +} diff --git a/src/tools/clippy/tests/ui/unreadable_literal.rs b/src/tools/clippy/tests/ui/unreadable_literal.rs new file mode 100644 index 0000000000000..e658a5f28c90e --- /dev/null +++ b/src/tools/clippy/tests/ui/unreadable_literal.rs @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::unreadable_literal)] + +struct Foo(u64); + +macro_rules! foo { + () => { + Foo(123123123123) + }; +} + +fn main() { + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 65536, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + let _good_sci = 1.1234e1; + let _bad_sci = 1.123456e1; + + let _fail9 = 0xabcdef; + let _fail10: u32 = 0xBAFEBAFE; + let _fail11 = 0xabcdeff; + let _fail12: i128 = 0xabcabcabcabcabcabc; + + let _ = foo!(); +} diff --git a/src/tools/clippy/tests/ui/unreadable_literal.stderr b/src/tools/clippy/tests/ui/unreadable_literal.stderr new file mode 100644 index 0000000000000..1b2ff6bff048c --- /dev/null +++ b/src/tools/clippy/tests/ui/unreadable_literal.stderr @@ -0,0 +1,58 @@ +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:17 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64` + | + = note: `-D clippy::unreadable-literal` implied by `-D warnings` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:31 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:49 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^ help: consider: `123_456_f32` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:61 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^^^ help: consider: `1.234_567_f32` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:27:20 + | +LL | let _bad_sci = 1.123456e1; + | ^^^^^^^^^^ help: consider: `1.123_456e1` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:29:18 + | +LL | let _fail9 = 0xabcdef; + | ^^^^^^^^ help: consider: `0x00ab_cdef` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:30:24 + | +LL | let _fail10: u32 = 0xBAFEBAFE; + | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:31:19 + | +LL | let _fail11 = 0xabcdeff; + | ^^^^^^^^^ help: consider: `0x0abc_deff` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:32:25 + | +LL | let _fail12: i128 = 0xabcabcabcabcabcabc; + | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs new file mode 100644 index 0000000000000..7bee9c499e1f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs @@ -0,0 +1,60 @@ +#![warn(clippy::unsafe_derive_deserialize)] +#![allow(unused, clippy::missing_safety_doc)] + +extern crate serde; + +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct A {} +impl A { + pub unsafe fn new(_a: i32, _b: i32) -> Self { + Self {} + } +} + +#[derive(Deserialize)] +pub struct B {} +impl B { + pub unsafe fn unsafe_method(&self) {} +} + +#[derive(Deserialize)] +pub struct C {} +impl C { + pub fn unsafe_block(&self) { + unsafe {} + } +} + +#[derive(Deserialize)] +pub struct D {} +impl D { + pub fn inner_unsafe_fn(&self) { + unsafe fn inner() {} + } +} + +// Does not derive `Deserialize`, should be ignored +pub struct E {} +impl E { + pub unsafe fn new(_a: i32, _b: i32) -> Self { + Self {} + } + + pub unsafe fn unsafe_method(&self) {} + + pub fn unsafe_block(&self) { + unsafe {} + } + + pub fn inner_unsafe_fn(&self) { + unsafe fn inner() {} + } +} + +// Does not have methods using `unsafe`, should be ignored +#[derive(Deserialize)] +pub struct F {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr new file mode 100644 index 0000000000000..1978bd95a6703 --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr @@ -0,0 +1,39 @@ +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:8:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = note: `-D clippy::unsafe-derive-deserialize` implied by `-D warnings` + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:16:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:22:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:30:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs new file mode 100644 index 0000000000000..a1f616733bd92 --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs @@ -0,0 +1,27 @@ +#![allow(unused_imports)] +#![allow(dead_code)] +#![warn(clippy::unsafe_removed_from_name)] + +use std::cell::UnsafeCell as TotallySafeCell; + +use std::cell::UnsafeCell as TotallySafeCellAgain; + +// Shouldn't error +use std::cell::RefCell as ProbablyNotUnsafe; +use std::cell::RefCell as RefCellThatCantBeUnsafe; +use std::cell::UnsafeCell as SuperDangerousUnsafeCell; +use std::cell::UnsafeCell as Dangerunsafe; +use std::cell::UnsafeCell as Bombsawayunsafe; + +mod mod_with_some_unsafe_things { + pub struct Safe {} + pub struct Unsafe {} +} + +use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; + +// Shouldn't error +use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime; +use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr b/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr new file mode 100644 index 0000000000000..4f871cbe41b06 --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr @@ -0,0 +1,22 @@ +error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCell` + --> $DIR/unsafe_removed_from_name.rs:5:1 + | +LL | use std::cell::UnsafeCell as TotallySafeCell; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsafe-removed-from-name` implied by `-D warnings` + +error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain` + --> $DIR/unsafe_removed_from_name.rs:7:1 + | +LL | use std::cell::UnsafeCell as TotallySafeCellAgain; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: removed `unsafe` from the name of `Unsafe` in use as `LieAboutModSafety` + --> $DIR/unsafe_removed_from_name.rs:21:1 + | +LL | use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed new file mode 100644 index 0000000000000..3c422cc4fee72 --- /dev/null +++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#![warn(clippy::unseparated_literal_suffix)] +#![allow(dead_code)] + +#[macro_use] +extern crate clippy_mini_macro_test; + +// Test for proc-macro attribute +#[derive(ClippyMiniMacroTest)] +struct Foo; + +macro_rules! lit_from_macro { + () => { + 42_usize + }; +} + +fn main() { + let _ok1 = 1234_i32; + let _ok2 = 1234_isize; + let _ok3 = 0x123_isize; + let _fail1 = 1234_i32; + let _fail2 = 1234_u32; + let _fail3 = 1234_isize; + let _fail4 = 1234_usize; + let _fail5 = 0x123_isize; + + let _okf1 = 1.5_f32; + let _okf2 = 1_f32; + let _failf1 = 1.5_f32; + let _failf2 = 1_f32; + + // Test for macro + let _ = lit_from_macro!(); + + // Counter example + let _ = line!(); + // Because `assert!` contains `line!()` macro. + assert_eq!(4897_u32, 32223); +} diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs new file mode 100644 index 0000000000000..09608661e0ef5 --- /dev/null +++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![warn(clippy::unseparated_literal_suffix)] +#![allow(dead_code)] + +#[macro_use] +extern crate clippy_mini_macro_test; + +// Test for proc-macro attribute +#[derive(ClippyMiniMacroTest)] +struct Foo; + +macro_rules! lit_from_macro { + () => { + 42usize + }; +} + +fn main() { + let _ok1 = 1234_i32; + let _ok2 = 1234_isize; + let _ok3 = 0x123_isize; + let _fail1 = 1234i32; + let _fail2 = 1234u32; + let _fail3 = 1234isize; + let _fail4 = 1234usize; + let _fail5 = 0x123isize; + + let _okf1 = 1.5_f32; + let _okf2 = 1_f32; + let _failf1 = 1.5f32; + let _failf2 = 1f32; + + // Test for macro + let _ = lit_from_macro!(); + + // Counter example + let _ = line!(); + // Because `assert!` contains `line!()` macro. + assert_eq!(4897u32, 32223); +} diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr b/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr new file mode 100644 index 0000000000000..d7dd526bcb9af --- /dev/null +++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr @@ -0,0 +1,63 @@ +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:23:18 + | +LL | let _fail1 = 1234i32; + | ^^^^^^^ help: add an underscore: `1234_i32` + | + = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:24:18 + | +LL | let _fail2 = 1234u32; + | ^^^^^^^ help: add an underscore: `1234_u32` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:25:18 + | +LL | let _fail3 = 1234isize; + | ^^^^^^^^^ help: add an underscore: `1234_isize` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:26:18 + | +LL | let _fail4 = 1234usize; + | ^^^^^^^^^ help: add an underscore: `1234_usize` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:27:18 + | +LL | let _fail5 = 0x123isize; + | ^^^^^^^^^^ help: add an underscore: `0x123_isize` + +error: float type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:31:19 + | +LL | let _failf1 = 1.5f32; + | ^^^^^^ help: add an underscore: `1.5_f32` + +error: float type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:32:19 + | +LL | let _failf2 = 1f32; + | ^^^^ help: add an underscore: `1_f32` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:15:9 + | +LL | 42usize + | ^^^^^^^ help: add an underscore: `42_usize` +... +LL | let _ = lit_from_macro!(); + | ----------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:40:16 + | +LL | assert_eq!(4897u32, 32223); + | ^^^^^^^ help: add an underscore: `4897_u32` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs new file mode 100644 index 0000000000000..ebaba9629db16 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -0,0 +1,25 @@ +#![allow(dead_code)] +#![warn(clippy::unused_io_amount)] + +use std::io; + +fn question_mark(s: &mut T) -> io::Result<()> { + s.write(b"test")?; + let mut buf = [0u8; 4]; + s.read(&mut buf)?; + Ok(()) +} + +fn unwrap(s: &mut T) { + s.write(b"test").unwrap(); + let mut buf = [0u8; 4]; + s.read(&mut buf).unwrap(); +} + +fn vectored(s: &mut T) -> io::Result<()> { + s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; + s.write_vectored(&[io::IoSlice::new(&[])])?; + Ok(()) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr new file mode 100644 index 0000000000000..5219d63980b4b --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr @@ -0,0 +1,40 @@ +error: written amount is not handled. Use `Write::write_all` instead + --> $DIR/unused_io_amount.rs:7:5 + | +LL | s.write(b"test")?; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unused-io-amount` implied by `-D warnings` + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:9:5 + | +LL | s.read(&mut buf)?; + | ^^^^^^^^^^^^^^^^^ + +error: written amount is not handled. Use `Write::write_all` instead + --> $DIR/unused_io_amount.rs:14:5 + | +LL | s.write(b"test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:16:5 + | +LL | s.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:20:5 + | +LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:21:5 + | +LL | s.write_vectored(&[io::IoSlice::new(&[])])?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_self.rs b/src/tools/clippy/tests/ui/unused_self.rs new file mode 100644 index 0000000000000..7a4bbdda1ab27 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_self.rs @@ -0,0 +1,140 @@ +#![warn(clippy::unused_self)] +#![allow(clippy::boxed_local, clippy::fn_params_excessive_bools)] + +mod unused_self { + use std::pin::Pin; + use std::sync::{Arc, Mutex}; + + struct A {} + + impl A { + fn unused_self_move(self) {} + fn unused_self_ref(&self) {} + fn unused_self_mut_ref(&mut self) {} + fn unused_self_pin_ref(self: Pin<&Self>) {} + fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {} + fn unused_self_pin_nested(self: Pin>) {} + fn unused_self_box(self: Box) {} + fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 { + x + y + } + fn unused_self_class_method(&self) { + Self::static_method(); + } + + fn static_method() {} + } +} + +mod unused_self_allow { + struct A {} + + impl A { + // shouldn't trigger + #[allow(clippy::unused_self)] + fn unused_self_move(self) {} + } + + struct B {} + + // shouldn't trigger + #[allow(clippy::unused_self)] + impl B { + fn unused_self_move(self) {} + } + + struct C {} + + #[allow(clippy::unused_self)] + impl C { + #[warn(clippy::unused_self)] + fn some_fn((): ()) {} + + // shouldn't trigger + fn unused_self_move(self) {} + } +} + +mod used_self { + use std::pin::Pin; + + struct A { + x: u8, + } + + impl A { + fn used_self_move(self) -> u8 { + self.x + } + fn used_self_ref(&self) -> u8 { + self.x + } + fn used_self_mut_ref(&mut self) { + self.x += 1 + } + fn used_self_pin_ref(self: Pin<&Self>) -> u8 { + self.x + } + fn used_self_box(self: Box) -> u8 { + self.x + } + fn used_self_with_other_unused_args(&self, x: u8, y: u8) -> u8 { + self.x + } + fn used_in_nested_closure(&self) -> u8 { + let mut a = || -> u8 { self.x }; + a() + } + + #[allow(clippy::collapsible_if)] + fn used_self_method_nested_conditions(&self, a: bool, b: bool, c: bool, d: bool) { + if a { + if b { + if c { + if d { + self.used_self_ref(); + } + } + } + } + } + + fn foo(&self) -> u32 { + let mut sum = 0u32; + for i in 0..self.x { + sum += i as u32; + } + sum + } + + fn bar(&mut self, x: u8) -> u32 { + let mut y = 0u32; + for i in 0..x { + y += self.foo() + } + y + } + } +} + +mod not_applicable { + use std::fmt; + + struct A {} + + impl fmt::Debug for A { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "A") + } + } + + impl A { + fn method(x: u8, y: u8) {} + } + + trait B { + fn method(&self) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_self.stderr b/src/tools/clippy/tests/ui/unused_self.stderr new file mode 100644 index 0000000000000..0534b40eabb75 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_self.stderr @@ -0,0 +1,75 @@ +error: unused `self` argument + --> $DIR/unused_self.rs:11:29 + | +LL | fn unused_self_move(self) {} + | ^^^^ + | + = note: `-D clippy::unused-self` implied by `-D warnings` + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:12:28 + | +LL | fn unused_self_ref(&self) {} + | ^^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:13:32 + | +LL | fn unused_self_mut_ref(&mut self) {} + | ^^^^^^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:14:32 + | +LL | fn unused_self_pin_ref(self: Pin<&Self>) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:15:36 + | +LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:16:35 + | +LL | fn unused_self_pin_nested(self: Pin>) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:17:28 + | +LL | fn unused_self_box(self: Box) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:18:40 + | +LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 { + | ^^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:21:37 + | +LL | fn unused_self_class_method(&self) { + | ^^^^^ + | + = help: consider refactoring to a associated function + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed new file mode 100644 index 0000000000000..07f2791786d7f --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.fixed @@ -0,0 +1,72 @@ +// run-rustfix + +// 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)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + + } +} + +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() ; +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() {} +} + +fn return_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; + } + return; +} + +// 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 => () + } +} diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs new file mode 100644 index 0000000000000..e2c6afb020f58 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.rs @@ -0,0 +1,72 @@ +// run-rustfix + +// 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)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit (), G>(&self, f: F, _g: G) -> () + where G: Fn() -> () { + let _y: &dyn Fn() -> () = &f; + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) -> () { + () + } +} + +trait Trait { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> (); +} + +impl Trait for Unitter { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> () {} +} + +fn return_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(); + } + return(); +} + +// 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 => () + } +} diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr new file mode 100644 index 0000000000000..81e6738e6bf67 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.stderr @@ -0,0 +1,104 @@ +error: unneeded unit return type + --> $DIR/unused_unit.rs:18:29 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` + | +note: the lint level is defined here + --> $DIR/unused_unit.rs:12:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit return type + --> $DIR/unused_unit.rs:19:19 + | +LL | where G: Fn() -> () { + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:18:59 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:20:27 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:27:19 + | +LL | fn into(self) -> () { + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit expression + --> $DIR/unused_unit.rs:28:9 + | +LL | () + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:33:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:35:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:36:17 + | +LL | H: Fn() -> (); + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:40:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:42:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:43:17 + | +LL | H: Fn() -> () {} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:46:18 + | +LL | fn return_unit() -> () { () } + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit expression + --> $DIR/unused_unit.rs:46:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded `()` + --> $DIR/unused_unit.rs:56:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> $DIR/unused_unit.rs:58:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/unwrap.rs b/src/tools/clippy/tests/ui/unwrap.rs new file mode 100644 index 0000000000000..a4a3cd1d37977 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap.rs @@ -0,0 +1,16 @@ +#![warn(clippy::unwrap_used)] + +fn unwrap_option() { + let opt = Some(0); + let _ = opt.unwrap(); +} + +fn unwrap_result() { + let res: Result = Ok(0); + let _ = res.unwrap(); +} + +fn main() { + unwrap_option(); + unwrap_result(); +} diff --git a/src/tools/clippy/tests/ui/unwrap.stderr b/src/tools/clippy/tests/ui/unwrap.stderr new file mode 100644 index 0000000000000..4f0858005f6e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap.stderr @@ -0,0 +1,19 @@ +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap.rs:5:13 + | +LL | let _ = opt.unwrap(); + | ^^^^^^^^^^^^ + | + = note: `-D clippy::unwrap-used` implied by `-D warnings` + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: used `unwrap()` on `a Result` value + --> $DIR/unwrap.rs:10:13 + | +LL | let _ = res.unwrap(); + | ^^^^^^^^^^^^ + | + = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs new file mode 100644 index 0000000000000..bfb41e4394731 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap_or.rs @@ -0,0 +1,9 @@ +#![warn(clippy::all)] + +fn main() { + let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); +} + +fn new_lines() { + let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); +} diff --git a/src/tools/clippy/tests/ui/unwrap_or.stderr b/src/tools/clippy/tests/ui/unwrap_or.stderr new file mode 100644 index 0000000000000..c3a7464fd470e --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap_or.stderr @@ -0,0 +1,16 @@ +error: use of `unwrap_or` followed by a function call + --> $DIR/unwrap_or.rs:4:47 + | +LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + +error: use of `unwrap_or` followed by a function call + --> $DIR/unwrap_or.rs:8:47 + | +LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/update-all-references.sh b/src/tools/clippy/tests/ui/update-all-references.sh new file mode 100755 index 0000000000000..30ba9188db43d --- /dev/null +++ b/src/tools/clippy/tests/ui/update-all-references.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# A script to update the references for all tests. The idea is that +# you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. You then +# run this script, which will copy those files over. If you find +# yourself manually editing a foo.stderr file, you're doing it wrong. +# +# See all `update-references.sh`, if you just want to update a single test. + +if [[ "$1" == "--help" || "$1" == "-h" ]]; then + echo "usage: $0" +fi + +CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target} +PROFILE=${PROFILE:-debug} +BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base + +MY_DIR=$(dirname "$0") +cd "$MY_DIR" || exit +find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + diff --git a/src/tools/clippy/tests/ui/update-references.sh b/src/tools/clippy/tests/ui/update-references.sh new file mode 100755 index 0000000000000..2c13c327d7980 --- /dev/null +++ b/src/tools/clippy/tests/ui/update-references.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# A script to update the references for particular tests. The idea is +# that you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. This +# script will then copy that output and replace the "expected output" +# files. You can then commit the changes. +# +# If you find yourself manually editing a `foo.stderr` file, you're +# doing it wrong. + +if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then + echo "usage: $0 " + echo "" + echo "For example:" + echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" +fi + +MYDIR=$(dirname "$0") + +BUILD_DIR="$1" +shift + +while [[ "$1" != "" ]]; do + STDERR_NAME="${1/%.rs/.stderr}" + STDOUT_NAME="${1/%.rs/.stdout}" + FIXED_NAME="${1/%.rs/.fixed}" + shift + if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then + echo updating "$MYDIR"/"$STDOUT_NAME" + cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + fi + if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then + echo updating "$MYDIR"/"$STDERR_NAME" + cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + fi + if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then + echo updating "$MYDIR"/"$FIXED_NAME" + cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" + fi +done diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed new file mode 100644 index 0000000000000..ebb3aa28daf3d --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -0,0 +1,253 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait)] + +fn main() {} + +mod use_self { + struct Foo {} + + impl Foo { + fn new() -> Self { + Self {} + } + fn test() -> Self { + Self::new() + } + } + + impl Default for Foo { + fn default() -> Self { + Self::new() + } + } +} + +mod better { + struct Foo {} + + impl Foo { + fn new() -> Self { + Self {} + } + fn test() -> Self { + Self::new() + } + } + + impl Default for Foo { + fn default() -> Self { + Self::new() + } + } +} + +mod lifetimes { + struct Foo<'a> { + foo_str: &'a str, + } + + impl<'a> Foo<'a> { + // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) -> + // Foo<'b>` + fn foo(s: &str) -> Foo { + Foo { foo_str: s } + } + // cannot replace with `Self`, because that's `Foo<'a>` + fn bar() -> Foo<'static> { + Foo { foo_str: "foo" } + } + + // FIXME: the lint does not handle lifetimed struct + // `Self` should be applicable here + fn clone(&self) -> Foo<'a> { + Foo { foo_str: self.foo_str } + } + } +} + +mod issue2894 { + trait IntoBytes { + fn into_bytes(&self) -> Vec; + } + + // This should not be linted + impl IntoBytes for u8 { + fn into_bytes(&self) -> Vec { + vec![*self] + } + } +} + +mod existential { + struct Foo; + + impl Foo { + fn bad(foos: &[Self]) -> impl Iterator { + foos.iter() + } + + fn good(foos: &[Self]) -> impl Iterator { + foos.iter() + } + } +} + +mod tuple_structs { + pub struct TS(i32); + + impl TS { + pub fn ts() -> Self { + Self(0) + } + } +} + +mod macros { + macro_rules! use_self_expand { + () => { + fn new() -> Self { + Self {} + } + }; + } + + struct Foo {} + + impl Foo { + use_self_expand!(); // Should lint in local macros + } +} + +mod nesting { + struct Foo {} + impl Foo { + fn foo() { + #[allow(unused_imports)] + use self::Foo; // Can't use Self here + struct Bar { + foo: Foo, // Foo != Self + } + + impl Bar { + fn bar() -> Self { + Self { foo: Foo {} } + } + } + + // Can't use Self here + fn baz() -> Foo { + Foo {} + } + } + + // Should lint here + fn baz() -> Self { + Self {} + } + } + + enum Enum { + A, + B(u64), + C { field: bool }, + } + impl Enum { + fn method() { + #[allow(unused_imports)] + use self::Enum::*; // Issue 3425 + static STATIC: Enum = Enum::A; // Can't use Self as type + } + + fn method2() { + let _ = Self::B(42); + let _ = Self::C { field: true }; + let _ = Self::A; + } + } +} + +mod issue3410 { + + struct A; + struct B; + + trait Trait { + fn a(v: T); + } + + impl Trait> for Vec { + fn a(_: Vec) {} + } +} + +#[allow(clippy::no_effect, path_statements)] +mod rustfix { + mod nested { + pub struct A {} + } + + impl nested::A { + const A: bool = true; + + fn fun_1() {} + + fn fun_2() { + Self::fun_1(); + Self::A; + + Self {}; + } + } +} + +mod issue3567 { + struct TestStruct {} + impl TestStruct { + fn from_something() -> Self { + Self {} + } + } + + trait Test { + fn test() -> TestStruct; + } + + impl Test for TestStruct { + fn test() -> TestStruct { + Self::from_something() + } + } +} + +mod paths_created_by_lowering { + use std::ops::Range; + + struct S {} + + impl S { + const A: usize = 0; + const B: usize = 1; + + async fn g() -> Self { + Self {} + } + + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[Self::A..Self::B] + } + } + + trait T { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8]; + } + + impl T for Range { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[0..1] + } + } +} diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs new file mode 100644 index 0000000000000..8a182192ab34d --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -0,0 +1,253 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait)] + +fn main() {} + +mod use_self { + struct Foo {} + + impl Foo { + fn new() -> Foo { + Foo {} + } + fn test() -> Foo { + Foo::new() + } + } + + impl Default for Foo { + fn default() -> Foo { + Foo::new() + } + } +} + +mod better { + struct Foo {} + + impl Foo { + fn new() -> Self { + Self {} + } + fn test() -> Self { + Self::new() + } + } + + impl Default for Foo { + fn default() -> Self { + Self::new() + } + } +} + +mod lifetimes { + struct Foo<'a> { + foo_str: &'a str, + } + + impl<'a> Foo<'a> { + // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) -> + // Foo<'b>` + fn foo(s: &str) -> Foo { + Foo { foo_str: s } + } + // cannot replace with `Self`, because that's `Foo<'a>` + fn bar() -> Foo<'static> { + Foo { foo_str: "foo" } + } + + // FIXME: the lint does not handle lifetimed struct + // `Self` should be applicable here + fn clone(&self) -> Foo<'a> { + Foo { foo_str: self.foo_str } + } + } +} + +mod issue2894 { + trait IntoBytes { + fn into_bytes(&self) -> Vec; + } + + // This should not be linted + impl IntoBytes for u8 { + fn into_bytes(&self) -> Vec { + vec![*self] + } + } +} + +mod existential { + struct Foo; + + impl Foo { + fn bad(foos: &[Self]) -> impl Iterator { + foos.iter() + } + + fn good(foos: &[Self]) -> impl Iterator { + foos.iter() + } + } +} + +mod tuple_structs { + pub struct TS(i32); + + impl TS { + pub fn ts() -> Self { + TS(0) + } + } +} + +mod macros { + macro_rules! use_self_expand { + () => { + fn new() -> Foo { + Foo {} + } + }; + } + + struct Foo {} + + impl Foo { + use_self_expand!(); // Should lint in local macros + } +} + +mod nesting { + struct Foo {} + impl Foo { + fn foo() { + #[allow(unused_imports)] + use self::Foo; // Can't use Self here + struct Bar { + foo: Foo, // Foo != Self + } + + impl Bar { + fn bar() -> Bar { + Bar { foo: Foo {} } + } + } + + // Can't use Self here + fn baz() -> Foo { + Foo {} + } + } + + // Should lint here + fn baz() -> Foo { + Foo {} + } + } + + enum Enum { + A, + B(u64), + C { field: bool }, + } + impl Enum { + fn method() { + #[allow(unused_imports)] + use self::Enum::*; // Issue 3425 + static STATIC: Enum = Enum::A; // Can't use Self as type + } + + fn method2() { + let _ = Enum::B(42); + let _ = Enum::C { field: true }; + let _ = Enum::A; + } + } +} + +mod issue3410 { + + struct A; + struct B; + + trait Trait { + fn a(v: T); + } + + impl Trait> for Vec { + fn a(_: Vec) {} + } +} + +#[allow(clippy::no_effect, path_statements)] +mod rustfix { + mod nested { + pub struct A {} + } + + impl nested::A { + const A: bool = true; + + fn fun_1() {} + + fn fun_2() { + nested::A::fun_1(); + nested::A::A; + + nested::A {}; + } + } +} + +mod issue3567 { + struct TestStruct {} + impl TestStruct { + fn from_something() -> Self { + Self {} + } + } + + trait Test { + fn test() -> TestStruct; + } + + impl Test for TestStruct { + fn test() -> TestStruct { + TestStruct::from_something() + } + } +} + +mod paths_created_by_lowering { + use std::ops::Range; + + struct S {} + + impl S { + const A: usize = 0; + const B: usize = 1; + + async fn g() -> S { + S {} + } + + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[S::A..S::B] + } + } + + trait T { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8]; + } + + impl T for Range { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[0..1] + } + } +} diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr new file mode 100644 index 0000000000000..b33928597c145 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self.stderr @@ -0,0 +1,164 @@ +error: unnecessary structure name repetition + --> $DIR/use_self.rs:14:21 + | +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:15:13 + | +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:17:22 + | +LL | fn test() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:18:13 + | +LL | Foo::new() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:23:25 + | +LL | fn default() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:24:13 + | +LL | Foo::new() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:89:56 + | +LL | fn bad(foos: &[Self]) -> impl Iterator { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:104:13 + | +LL | TS(0) + | ^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:112:25 + | +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` +... +LL | use_self_expand!(); // Should lint in local macros + | ------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:113:17 + | +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` +... +LL | use_self_expand!(); // Should lint in local macros + | ------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:148:21 + | +LL | fn baz() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:149:13 + | +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:136:29 + | +LL | fn bar() -> Bar { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:137:21 + | +LL | Bar { foo: Foo {} } + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:166:21 + | +LL | let _ = Enum::B(42); + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:167:21 + | +LL | let _ = Enum::C { field: true }; + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:168:21 + | +LL | let _ = Enum::A; + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:199:13 + | +LL | nested::A::fun_1(); + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:200:13 + | +LL | nested::A::A; + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:202:13 + | +LL | nested::A {}; + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:221:13 + | +LL | TestStruct::from_something() + | ^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:235:25 + | +LL | async fn g() -> S { + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:236:13 + | +LL | S {} + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:240:16 + | +LL | &p[S::A..S::B] + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:240:22 + | +LL | &p[S::A..S::B] + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/use_self_trait.fixed b/src/tools/clippy/tests/ui/use_self_trait.fixed new file mode 100644 index 0000000000000..1582ae114bf4c --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_trait.fixed @@ -0,0 +1,114 @@ +// run-rustfix + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait, clippy::boxed_local)] + +use std::ops::Mul; + +trait SelfTrait { + fn refs(p1: &Self) -> &Self; + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self; + fn mut_refs(p1: &mut Self) -> &mut Self; + fn nested(p1: Box, p2: (&u8, &Self)); + fn vals(r: Self) -> Self; +} + +#[derive(Default)] +struct Bad; + +impl SelfTrait for Bad { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box, _p2: (&u8, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +impl Mul for Bad { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + rhs + } +} + +impl Clone for Bad { + fn clone(&self) -> Self { + Self + } +} + +#[derive(Default)] +struct Good; + +impl SelfTrait for Good { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box, _p2: (&u8, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +impl Mul for Good { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + rhs + } +} + +trait NameTrait { + fn refs(p1: &u8) -> &u8; + fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8; + fn mut_refs(p1: &mut u8) -> &mut u8; + fn nested(p1: Box, p2: (&u8, &u8)); + fn vals(p1: u8) -> u8; +} + +// Using `Self` instead of the type name is OK +impl NameTrait for u8 { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box, _p2: (&Self, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/use_self_trait.rs b/src/tools/clippy/tests/ui/use_self_trait.rs new file mode 100644 index 0000000000000..70667b9797e76 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_trait.rs @@ -0,0 +1,114 @@ +// run-rustfix + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait, clippy::boxed_local)] + +use std::ops::Mul; + +trait SelfTrait { + fn refs(p1: &Self) -> &Self; + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self; + fn mut_refs(p1: &mut Self) -> &mut Self; + fn nested(p1: Box, p2: (&u8, &Self)); + fn vals(r: Self) -> Self; +} + +#[derive(Default)] +struct Bad; + +impl SelfTrait for Bad { + fn refs(p1: &Bad) -> &Bad { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + p1 + } + + fn mut_refs(p1: &mut Bad) -> &mut Bad { + p1 + } + + fn nested(_p1: Box, _p2: (&u8, &Bad)) {} + + fn vals(_: Bad) -> Bad { + Bad::default() + } +} + +impl Mul for Bad { + type Output = Bad; + + fn mul(self, rhs: Bad) -> Bad { + rhs + } +} + +impl Clone for Bad { + fn clone(&self) -> Self { + Bad + } +} + +#[derive(Default)] +struct Good; + +impl SelfTrait for Good { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box, _p2: (&u8, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +impl Mul for Good { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + rhs + } +} + +trait NameTrait { + fn refs(p1: &u8) -> &u8; + fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8; + fn mut_refs(p1: &mut u8) -> &mut u8; + fn nested(p1: Box, p2: (&u8, &u8)); + fn vals(p1: u8) -> u8; +} + +// Using `Self` instead of the type name is OK +impl NameTrait for u8 { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box, _p2: (&Self, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/use_self_trait.stderr b/src/tools/clippy/tests/ui/use_self_trait.stderr new file mode 100644 index 0000000000000..4f2506cc1192f --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_trait.stderr @@ -0,0 +1,94 @@ +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:21:18 + | +LL | fn refs(p1: &Bad) -> &Bad { + | ^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:21:27 + | +LL | fn refs(p1: &Bad) -> &Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:25:33 + | +LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:25:49 + | +LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:29:26 + | +LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:29:39 + | +LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:33:24 + | +LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:33:42 + | +LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:35:16 + | +LL | fn vals(_: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:35:24 + | +LL | fn vals(_: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:36:9 + | +LL | Bad::default() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:41:19 + | +LL | type Output = Bad; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:43:23 + | +LL | fn mul(self, rhs: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:43:31 + | +LL | fn mul(self, rhs: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:50:9 + | +LL | Bad + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.rs b/src/tools/clippy/tests/ui/used_underscore_binding.rs new file mode 100644 index 0000000000000..8e0243c49aaa0 --- /dev/null +++ b/src/tools/clippy/tests/ui/used_underscore_binding.rs @@ -0,0 +1,119 @@ +// edition:2018 +// aux-build:proc_macro_derive.rs + +#![feature(rustc_private)] +#![warn(clippy::all)] +#![allow(clippy::blacklisted_name)] +#![warn(clippy::used_underscore_binding)] + +#[macro_use] +extern crate proc_macro_derive; + +// This should not trigger the lint. There's underscore binding inside the external derive that +// would trigger the `used_underscore_binding` lint. +#[derive(DeriveSomething)] +struct Baz; + +macro_rules! test_macro { + () => {{ + let _foo = 42; + _foo + 1 + }}; +} + +/// Tests that we lint if we use a binding with a single leading underscore +fn prefix_underscore(_foo: u32) -> u32 { + _foo + 1 +} + +/// Tests that we lint if we use a `_`-variable defined outside within a macro expansion +fn in_macro_or_desugar(_foo: u32) { + println!("{}", _foo); + assert_eq!(_foo, _foo); + + test_macro!() + 1; +} + +// Struct for testing use of fields prefixed with an underscore +struct StructFieldTest { + _underscore_field: u32, +} + +/// Tests that we lint the use of a struct field which is prefixed with an underscore +fn in_struct_field() { + let mut s = StructFieldTest { _underscore_field: 0 }; + s._underscore_field += 1; +} + +/// Tests that we do not lint if the underscore is not a prefix +fn non_prefix_underscore(some_foo: u32) -> u32 { + some_foo + 1 +} + +/// Tests that we do not lint if we do not use the binding (simple case) +fn unused_underscore_simple(_foo: u32) -> u32 { + 1 +} + +/// Tests that we do not lint if we do not use the binding (complex case). This checks for +/// compatibility with the built-in `unused_variables` lint. +fn unused_underscore_complex(mut _foo: u32) -> u32 { + _foo += 1; + _foo = 2; + 1 +} + +/// Test that we do not lint for multiple underscores +fn multiple_underscores(__foo: u32) -> u32 { + __foo + 1 +} + +// Non-variable bindings with preceding underscore +fn _fn_test() {} +struct _StructTest; +enum _EnumTest { + _Empty, + _Value(_StructTest), +} + +/// Tests that we do not lint for non-variable bindings +fn non_variables() { + _fn_test(); + let _s = _StructTest; + let _e = match _EnumTest::_Value(_StructTest) { + _EnumTest::_Empty => 0, + _EnumTest::_Value(_st) => 1, + }; + let f = _fn_test; + f(); +} + +// Tests that we do not lint if the binding comes from await desugaring, +// but we do lint the awaited expression. See issue 5360. +async fn await_desugaring() { + async fn foo() {} + fn uses_i(_i: i32) {} + + foo().await; + ({ + let _i = 5; + uses_i(_i); + foo() + }) + .await +} + +fn main() { + let foo = 0u32; + // tests of unused_underscore lint + let _ = prefix_underscore(foo); + in_macro_or_desugar(foo); + in_struct_field(); + // possible false positives + let _ = non_prefix_underscore(foo); + let _ = unused_underscore_simple(foo); + let _ = unused_underscore_complex(foo); + let _ = multiple_underscores(foo); + non_variables(); + await_desugaring(); +} diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.stderr b/src/tools/clippy/tests/ui/used_underscore_binding.stderr new file mode 100644 index 0000000000000..68e96148093d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/used_underscore_binding.stderr @@ -0,0 +1,40 @@ +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:26:5 + | +LL | _foo + 1 + | ^^^^ + | + = note: `-D clippy::used-underscore-binding` implied by `-D warnings` + +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:31:20 + | +LL | println!("{}", _foo); + | ^^^^ + +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:32:16 + | +LL | assert_eq!(_foo, _foo); + | ^^^^ + +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:32:22 + | +LL | assert_eq!(_foo, _foo); + | ^^^^ + +error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:45:5 + | +LL | s._underscore_field += 1; + | ^^^^^^^^^^^^^^^^^^^ + +error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:100:16 + | +LL | uses_i(_i); + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/useful_asref.rs b/src/tools/clippy/tests/ui/useful_asref.rs new file mode 100644 index 0000000000000..a9f0170a79cd9 --- /dev/null +++ b/src/tools/clippy/tests/ui/useful_asref.rs @@ -0,0 +1,13 @@ +#![deny(clippy::useless_asref)] + +trait Trait { + fn as_ptr(&self); +} + +impl<'a> Trait for &'a [u8] { + fn as_ptr(&self) { + self.as_ref().as_ptr(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed new file mode 100644 index 0000000000000..e356f13d087b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -0,0 +1,135 @@ +// run-rustfix + +#![deny(clippy::useless_asref)] + +use std::fmt::Debug; + +struct FakeAsRef; + +#[allow(clippy::should_implement_trait)] +impl FakeAsRef { + fn as_ref(&self) -> &Self { + self + } +} + +struct MoreRef; + +impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef { + fn as_ref(&self) -> &&'a &'b &'c MoreRef { + &&&&MoreRef + } +} + +fn foo_rstr(x: &str) { + println!("{:?}", x); +} +fn foo_rslice(x: &[i32]) { + println!("{:?}", x); +} +fn foo_mrslice(x: &mut [i32]) { + println!("{:?}", x); +} +fn foo_rrrrmr(_: &&&&MoreRef) { + println!("so many refs"); +} + +fn not_ok() { + let rstr: &str = "hello"; + let mut mrslice: &mut [i32] = &mut [1, 2, 3]; + + { + let rslice: &[i32] = &*mrslice; + foo_rstr(rstr); + foo_rstr(rstr); + foo_rslice(rslice); + foo_rslice(rslice); + } + { + foo_mrslice(mrslice); + foo_mrslice(mrslice); + foo_rslice(mrslice); + foo_rslice(mrslice); + } + + { + let rrrrrstr = &&&&rstr; + let rrrrrslice = &&&&&*mrslice; + foo_rslice(rrrrrslice); + foo_rslice(rrrrrslice); + foo_rstr(rrrrrstr); + foo_rstr(rrrrrstr); + } + { + let mrrrrrslice = &mut &mut &mut &mut mrslice; + foo_mrslice(mrrrrrslice); + foo_mrslice(mrrrrrslice); + foo_rslice(mrrrrrslice); + foo_rslice(mrrrrrslice); + } + #[allow(unused_parens, clippy::double_parens)] + foo_rrrrmr((&&&&MoreRef)); + + generic_not_ok(mrslice); + generic_ok(mrslice); +} + +fn ok() { + let string = "hello".to_owned(); + let mut arr = [1, 2, 3]; + let mut vec = vec![1, 2, 3]; + + { + foo_rstr(string.as_ref()); + foo_rslice(arr.as_ref()); + foo_rslice(vec.as_ref()); + } + { + foo_mrslice(arr.as_mut()); + foo_mrslice(vec.as_mut()); + } + + { + let rrrrstring = &&&&string; + let rrrrarr = &&&&arr; + let rrrrvec = &&&&vec; + foo_rstr(rrrrstring.as_ref()); + foo_rslice(rrrrarr.as_ref()); + foo_rslice(rrrrvec.as_ref()); + } + { + let mrrrrarr = &mut &mut &mut &mut arr; + let mrrrrvec = &mut &mut &mut &mut vec; + foo_mrslice(mrrrrarr.as_mut()); + foo_mrslice(mrrrrvec.as_mut()); + } + FakeAsRef.as_ref(); + foo_rrrrmr(MoreRef.as_ref()); + + generic_not_ok(arr.as_mut()); + generic_ok(&mut arr); +} + +fn foo_mrt(t: &mut T) { + println!("{:?}", t); +} +fn foo_rt(t: &T) { + println!("{:?}", t); +} + +fn generic_not_ok + AsRef + Debug + ?Sized>(mrt: &mut T) { + foo_mrt(mrt); + foo_mrt(mrt); + foo_rt(mrt); + foo_rt(mrt); +} + +fn generic_ok + AsRef + ?Sized, T: Debug + ?Sized>(mru: &mut U) { + foo_mrt(mru.as_mut()); + foo_rt(mru.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 new file mode 100644 index 0000000000000..2a80291f5d837 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -0,0 +1,135 @@ +// run-rustfix + +#![deny(clippy::useless_asref)] + +use std::fmt::Debug; + +struct FakeAsRef; + +#[allow(clippy::should_implement_trait)] +impl FakeAsRef { + fn as_ref(&self) -> &Self { + self + } +} + +struct MoreRef; + +impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef { + fn as_ref(&self) -> &&'a &'b &'c MoreRef { + &&&&MoreRef + } +} + +fn foo_rstr(x: &str) { + println!("{:?}", x); +} +fn foo_rslice(x: &[i32]) { + println!("{:?}", x); +} +fn foo_mrslice(x: &mut [i32]) { + println!("{:?}", x); +} +fn foo_rrrrmr(_: &&&&MoreRef) { + println!("so many refs"); +} + +fn not_ok() { + let rstr: &str = "hello"; + let mut mrslice: &mut [i32] = &mut [1, 2, 3]; + + { + let rslice: &[i32] = &*mrslice; + foo_rstr(rstr.as_ref()); + foo_rstr(rstr); + foo_rslice(rslice.as_ref()); + foo_rslice(rslice); + } + { + foo_mrslice(mrslice.as_mut()); + foo_mrslice(mrslice); + foo_rslice(mrslice.as_ref()); + foo_rslice(mrslice); + } + + { + let rrrrrstr = &&&&rstr; + let rrrrrslice = &&&&&*mrslice; + foo_rslice(rrrrrslice.as_ref()); + foo_rslice(rrrrrslice); + foo_rstr(rrrrrstr.as_ref()); + foo_rstr(rrrrrstr); + } + { + let mrrrrrslice = &mut &mut &mut &mut mrslice; + foo_mrslice(mrrrrrslice.as_mut()); + foo_mrslice(mrrrrrslice); + foo_rslice(mrrrrrslice.as_ref()); + foo_rslice(mrrrrrslice); + } + #[allow(unused_parens, clippy::double_parens)] + foo_rrrrmr((&&&&MoreRef).as_ref()); + + generic_not_ok(mrslice); + generic_ok(mrslice); +} + +fn ok() { + let string = "hello".to_owned(); + let mut arr = [1, 2, 3]; + let mut vec = vec![1, 2, 3]; + + { + foo_rstr(string.as_ref()); + foo_rslice(arr.as_ref()); + foo_rslice(vec.as_ref()); + } + { + foo_mrslice(arr.as_mut()); + foo_mrslice(vec.as_mut()); + } + + { + let rrrrstring = &&&&string; + let rrrrarr = &&&&arr; + let rrrrvec = &&&&vec; + foo_rstr(rrrrstring.as_ref()); + foo_rslice(rrrrarr.as_ref()); + foo_rslice(rrrrvec.as_ref()); + } + { + let mrrrrarr = &mut &mut &mut &mut arr; + let mrrrrvec = &mut &mut &mut &mut vec; + foo_mrslice(mrrrrarr.as_mut()); + foo_mrslice(mrrrrvec.as_mut()); + } + FakeAsRef.as_ref(); + foo_rrrrmr(MoreRef.as_ref()); + + generic_not_ok(arr.as_mut()); + generic_ok(&mut arr); +} + +fn foo_mrt(t: &mut T) { + println!("{:?}", t); +} +fn foo_rt(t: &T) { + println!("{:?}", t); +} + +fn generic_not_ok + AsRef + Debug + ?Sized>(mrt: &mut T) { + foo_mrt(mrt.as_mut()); + foo_mrt(mrt); + foo_rt(mrt.as_ref()); + foo_rt(mrt); +} + +fn generic_ok + AsRef + ?Sized, T: Debug + ?Sized>(mru: &mut U) { + foo_mrt(mru.as_mut()); + foo_rt(mru.as_ref()); +} + +fn main() { + not_ok(); + ok(); +} diff --git a/src/tools/clippy/tests/ui/useless_asref.stderr b/src/tools/clippy/tests/ui/useless_asref.stderr new file mode 100644 index 0000000000000..5876b54aca8f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_asref.stderr @@ -0,0 +1,74 @@ +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:43:18 + | +LL | foo_rstr(rstr.as_ref()); + | ^^^^^^^^^^^^^ help: try this: `rstr` + | +note: the lint level is defined here + --> $DIR/useless_asref.rs:3:9 + | +LL | #![deny(clippy::useless_asref)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:45:20 + | +LL | foo_rslice(rslice.as_ref()); + | ^^^^^^^^^^^^^^^ help: try this: `rslice` + +error: this call to `as_mut` does nothing + --> $DIR/useless_asref.rs:49:21 + | +LL | foo_mrslice(mrslice.as_mut()); + | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:51:20 + | +LL | foo_rslice(mrslice.as_ref()); + | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:58:20 + | +LL | foo_rslice(rrrrrslice.as_ref()); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:60:18 + | +LL | foo_rstr(rrrrrstr.as_ref()); + | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr` + +error: this call to `as_mut` does nothing + --> $DIR/useless_asref.rs:65:21 + | +LL | foo_mrslice(mrrrrrslice.as_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:67:20 + | +LL | foo_rslice(mrrrrrslice.as_ref()); + | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:71:16 + | +LL | foo_rrrrmr((&&&&MoreRef).as_ref()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)` + +error: this call to `as_mut` does nothing + --> $DIR/useless_asref.rs:121:13 + | +LL | foo_mrt(mrt.as_mut()); + | ^^^^^^^^^^^^ help: try this: `mrt` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:123:12 + | +LL | foo_rt(mrt.as_ref()); + | ^^^^^^^^^^^^ help: try this: `mrt` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed new file mode 100644 index 0000000000000..b222e2f7976d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// aux-build:proc_macro_derive.rs + +#![warn(clippy::useless_attribute)] +#![warn(unreachable_pub)] +#![feature(rustc_private)] + +#![allow(dead_code)] +#![cfg_attr(feature = "cargo-clippy", allow(dead_code))] +#[rustfmt::skip] +#[allow(unused_imports)] +#[allow(unused_extern_crates)] +#[macro_use] +extern crate rustc_middle; + +#[macro_use] +extern crate proc_macro_derive; + +// don't lint on unused_import for `use` items +#[allow(unused_imports)] +use std::collections; + +// don't lint on unused for `use` items +#[allow(unused)] +use std::option; + +// don't lint on deprecated for `use` items +mod foo { + #[deprecated] + pub struct Bar; +} +#[allow(deprecated)] +pub use foo::Bar; + +// This should not trigger the lint. There's lint level definitions inside the external derive +// that would trigger the useless_attribute lint. +#[derive(DeriveSomething)] +struct Baz; + +// don't lint on unreachable_pub for `use` items +mod a { + mod b { + #[allow(dead_code)] + #[allow(unreachable_pub)] + pub struct C {} + } + + #[allow(unreachable_pub)] + pub use self::b::C; +} + +fn test_indented_attr() { + #![allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + +fn main() { + test_indented_attr(); +} diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs new file mode 100644 index 0000000000000..3422eace4ab97 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -0,0 +1,61 @@ +// run-rustfix +// aux-build:proc_macro_derive.rs + +#![warn(clippy::useless_attribute)] +#![warn(unreachable_pub)] +#![feature(rustc_private)] + +#[allow(dead_code)] +#[cfg_attr(feature = "cargo-clippy", allow(dead_code))] +#[rustfmt::skip] +#[allow(unused_imports)] +#[allow(unused_extern_crates)] +#[macro_use] +extern crate rustc_middle; + +#[macro_use] +extern crate proc_macro_derive; + +// don't lint on unused_import for `use` items +#[allow(unused_imports)] +use std::collections; + +// don't lint on unused for `use` items +#[allow(unused)] +use std::option; + +// don't lint on deprecated for `use` items +mod foo { + #[deprecated] + pub struct Bar; +} +#[allow(deprecated)] +pub use foo::Bar; + +// This should not trigger the lint. There's lint level definitions inside the external derive +// that would trigger the useless_attribute lint. +#[derive(DeriveSomething)] +struct Baz; + +// don't lint on unreachable_pub for `use` items +mod a { + mod b { + #[allow(dead_code)] + #[allow(unreachable_pub)] + pub struct C {} + } + + #[allow(unreachable_pub)] + pub use self::b::C; +} + +fn test_indented_attr() { + #[allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + +fn main() { + test_indented_attr(); +} diff --git a/src/tools/clippy/tests/ui/useless_attribute.stderr b/src/tools/clippy/tests/ui/useless_attribute.stderr new file mode 100644 index 0000000000000..57ba976730c17 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_attribute.stderr @@ -0,0 +1,22 @@ +error: useless lint attribute + --> $DIR/useless_attribute.rs:8:1 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]` + | + = note: `-D clippy::useless-attribute` implied by `-D warnings` + +error: useless lint attribute + --> $DIR/useless_attribute.rs:9:1 + | +LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` + +error: useless lint attribute + --> $DIR/useless_attribute.rs:53:5 + | +LL | #[allow(clippy::almost_swapped)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed new file mode 100644 index 0000000000000..fdd4bc581f305 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::useless_conversion)] + +fn test_generic(val: T) -> T { + let _ = val; + val +} + +fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32; + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::useless_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string(); + let _: String = "foo".to_string(); + let _ = "foo".to_string(); + let _ = format!("A: {:04}", 123); + let _ = "".lines(); + let _ = vec![1, 2, 3].into_iter(); + let _: String = format!("Hello {}", "world"); +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs new file mode 100644 index 0000000000000..4cae745e7c021 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::useless_conversion)] + +fn test_generic(val: T) -> T { + let _ = T::from(val); + val.into() +} + +fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32.into(); + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::useless_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string().into(); + let _: String = From::from("foo".to_string()); + let _ = String::from("foo".to_string()); + let _ = String::from(format!("A: {:04}", 123)); + let _ = "".lines().into_iter(); + let _ = vec![1, 2, 3].into_iter().into_iter(); + let _: String = format!("Hello {}", "world").into(); +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr new file mode 100644 index 0000000000000..7df3507edfd9e --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -0,0 +1,68 @@ +error: useless conversion + --> $DIR/useless_conversion.rs:6:13 + | +LL | let _ = T::from(val); + | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` + | +note: the lint level is defined here + --> $DIR/useless_conversion.rs:3:9 + | +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: useless conversion + --> $DIR/useless_conversion.rs:7:5 + | +LL | val.into() + | ^^^^^^^^^^ help: consider removing `.into()`: `val` + +error: useless conversion + --> $DIR/useless_conversion.rs:19:22 + | +LL | let _: i32 = 0i32.into(); + | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` + +error: useless conversion + --> $DIR/useless_conversion.rs:51:21 + | +LL | let _: String = "foo".to_string().into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` + +error: useless conversion + --> $DIR/useless_conversion.rs:52:21 + | +LL | let _: String = From::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` + +error: useless conversion + --> $DIR/useless_conversion.rs:53:13 + | +LL | let _ = String::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` + +error: useless conversion + --> $DIR/useless_conversion.rs:54:13 + | +LL | let _ = String::from(format!("A: {:04}", 123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` + +error: useless conversion + --> $DIR/useless_conversion.rs:55:13 + | +LL | let _ = "".lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` + +error: useless conversion + --> $DIR/useless_conversion.rs:56:13 + | +LL | let _ = vec![1, 2, 3].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` + +error: useless conversion + --> $DIR/useless_conversion.rs:57:21 + | +LL | let _: String = format!("Hello {}", "world").into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed new file mode 100644 index 0000000000000..e73a791891f89 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -0,0 +1,55 @@ +// run-rustfix + +#![warn(clippy::useless_vec)] + +#[derive(Debug)] +struct NonCopy; + +fn on_slice(_: &[u8]) {} +#[allow(clippy::ptr_arg)] +fn on_vec(_: &Vec) {} + +struct Line { + length: usize, +} + +impl Line { + fn length(&self) -> usize { + self.length + } +} + +fn main() { + on_slice(&[]); + on_slice(&[]); + + on_slice(&[1, 2]); + on_slice(&[1, 2]); + + on_slice(&[1, 2]); + on_slice(&[1, 2]); + #[rustfmt::skip] + on_slice(&[1, 2]); + on_slice(&[1, 2]); + + on_slice(&[1; 2]); + on_slice(&[1; 2]); + + on_vec(&vec![]); + on_vec(&vec![1, 2]); + on_vec(&vec![1; 2]); + + // Now with non-constant expressions + let line = Line { length: 2 }; + + on_slice(&vec![2; line.length]); + on_slice(&vec![2; line.length()]); + + for a in &[1, 2, 3] { + println!("{:?}", a); + } + + for a in vec![NonCopy, NonCopy] { + println!("{:?}", a); + } +} diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs new file mode 100644 index 0000000000000..3eb960f53d7af --- /dev/null +++ b/src/tools/clippy/tests/ui/vec.rs @@ -0,0 +1,55 @@ +// run-rustfix + +#![warn(clippy::useless_vec)] + +#[derive(Debug)] +struct NonCopy; + +fn on_slice(_: &[u8]) {} +#[allow(clippy::ptr_arg)] +fn on_vec(_: &Vec) {} + +struct Line { + length: usize, +} + +impl Line { + fn length(&self) -> usize { + self.length + } +} + +fn main() { + on_slice(&vec![]); + on_slice(&[]); + + on_slice(&vec![1, 2]); + on_slice(&[1, 2]); + + on_slice(&vec![1, 2]); + on_slice(&[1, 2]); + #[rustfmt::skip] + on_slice(&vec!(1, 2)); + on_slice(&[1, 2]); + + on_slice(&vec![1; 2]); + on_slice(&[1; 2]); + + on_vec(&vec![]); + on_vec(&vec![1, 2]); + on_vec(&vec![1; 2]); + + // Now with non-constant expressions + let line = Line { length: 2 }; + + on_slice(&vec![2; line.length]); + on_slice(&vec![2; line.length()]); + + for a in vec![1, 2, 3] { + println!("{:?}", a); + } + + for a in vec![NonCopy, NonCopy] { + println!("{:?}", a); + } +} diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr new file mode 100644 index 0000000000000..37e28ebddb553 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec.stderr @@ -0,0 +1,40 @@ +error: useless use of `vec!` + --> $DIR/vec.rs:23:14 + | +LL | on_slice(&vec![]); + | ^^^^^^^ help: you can use a slice directly: `&[]` + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + +error: useless use of `vec!` + --> $DIR/vec.rs:26:14 + | +LL | on_slice(&vec![1, 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:29:14 + | +LL | on_slice(&vec![1, 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:32:14 + | +LL | on_slice(&vec!(1, 2)); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:35:14 + | +LL | on_slice(&vec![1; 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:48:14 + | +LL | for a in vec![1, 2, 3] { + | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/vec_box_sized.fixed b/src/tools/clippy/tests/ui/vec_box_sized.fixed new file mode 100644 index 0000000000000..d0bee2460dd84 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_box_sized.fixed @@ -0,0 +1,38 @@ +// run-rustfix + +#![allow(dead_code)] + +struct SizedStruct(i32); +struct UnsizedStruct([i32]); +struct BigStruct([i32; 10000]); + +/// The following should trigger the lint +mod should_trigger { + use super::SizedStruct; + + struct StructWithVecBox { + sized_type: Vec, + } + + struct A(Vec); + struct B(Vec>); +} + +/// The following should not trigger the lint +mod should_not_trigger { + use super::{BigStruct, UnsizedStruct}; + + struct C(Vec>); + struct D(Vec>); + + struct StructWithVecBoxButItsUnsized { + unsized_type: Vec>, + } + + struct TraitVec { + // Regression test for #3720. This was causing an ICE. + inner: Vec>, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/vec_box_sized.rs b/src/tools/clippy/tests/ui/vec_box_sized.rs new file mode 100644 index 0000000000000..500a0ae263ea5 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_box_sized.rs @@ -0,0 +1,38 @@ +// run-rustfix + +#![allow(dead_code)] + +struct SizedStruct(i32); +struct UnsizedStruct([i32]); +struct BigStruct([i32; 10000]); + +/// The following should trigger the lint +mod should_trigger { + use super::SizedStruct; + + struct StructWithVecBox { + sized_type: Vec>, + } + + struct A(Vec>); + struct B(Vec>>); +} + +/// The following should not trigger the lint +mod should_not_trigger { + use super::{BigStruct, UnsizedStruct}; + + struct C(Vec>); + struct D(Vec>); + + struct StructWithVecBoxButItsUnsized { + unsized_type: Vec>, + } + + struct TraitVec { + // Regression test for #3720. This was causing an ICE. + inner: Vec>, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/vec_box_sized.stderr b/src/tools/clippy/tests/ui/vec_box_sized.stderr new file mode 100644 index 0000000000000..29bf7069e8adb --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_box_sized.stderr @@ -0,0 +1,22 @@ +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:14:21 + | +LL | sized_type: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + | + = note: `-D clippy::vec-box` implied by `-D warnings` + +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:17:14 + | +LL | struct A(Vec>); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:18:18 + | +LL | struct B(Vec>>); + | ^^^^^^^^^^^^^^^ help: try: `Vec` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/verbose_file_reads.rs b/src/tools/clippy/tests/ui/verbose_file_reads.rs new file mode 100644 index 0000000000000..e0065e05ade62 --- /dev/null +++ b/src/tools/clippy/tests/ui/verbose_file_reads.rs @@ -0,0 +1,28 @@ +#![warn(clippy::verbose_file_reads)] +use std::env::temp_dir; +use std::fs::File; +use std::io::Read; + +struct Struct; +// To make sure we only warn on File::{read_to_end, read_to_string} calls +impl Struct { + pub fn read_to_end(&self) {} + + pub fn read_to_string(&self) {} +} + +fn main() -> std::io::Result<()> { + let path = "foo.txt"; + // Lint shouldn't catch this + let s = Struct; + s.read_to_end(); + s.read_to_string(); + // Should catch this + let mut f = File::open(&path)?; + let mut buffer = Vec::new(); + f.read_to_end(&mut buffer)?; + // ...and this + let mut string_buffer = String::new(); + f.read_to_string(&mut string_buffer)?; + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/verbose_file_reads.stderr b/src/tools/clippy/tests/ui/verbose_file_reads.stderr new file mode 100644 index 0000000000000..550b6ab679f19 --- /dev/null +++ b/src/tools/clippy/tests/ui/verbose_file_reads.stderr @@ -0,0 +1,19 @@ +error: use of `File::read_to_end` + --> $DIR/verbose_file_reads.rs:23:5 + | +LL | f.read_to_end(&mut buffer)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::verbose-file-reads` implied by `-D warnings` + = help: consider using `fs::read` instead + +error: use of `File::read_to_string` + --> $DIR/verbose_file_reads.rs:26:5 + | +LL | f.read_to_string(&mut string_buffer)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `fs::read_to_string` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs new file mode 100644 index 0000000000000..c91d96ee18a31 --- /dev/null +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs @@ -0,0 +1,42 @@ +use std::fmt::Debug; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; + +#[warn(clippy::vtable_address_comparisons)] +fn main() { + let a: *const dyn Debug = &1 as &dyn Debug; + let b: *const dyn Debug = &1 as &dyn Debug; + + // These should fail: + let _ = a == b; + let _ = a != b; + let _ = a < b; + let _ = a <= b; + let _ = a > b; + let _ = a >= b; + ptr::eq(a, b); + + let a = &1 as &dyn Debug; + let b = &1 as &dyn Debug; + ptr::eq(a, b); + + let a: Rc = Rc::new(1); + Rc::ptr_eq(&a, &a); + + let a: Arc = Arc::new(1); + Arc::ptr_eq(&a, &a); + + // These should be fine: + let a = &1; + ptr::eq(a, a); + + let a = Rc::new(1); + Rc::ptr_eq(&a, &a); + + let a = Arc::new(1); + Arc::ptr_eq(&a, &a); + + let a: &[u8] = b""; + ptr::eq(a, a); +} diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr new file mode 100644 index 0000000000000..76bd57217d784 --- /dev/null +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr @@ -0,0 +1,83 @@ +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:12:13 + | +LL | let _ = a == b; + | ^^^^^^ + | + = note: `-D clippy::vtable-address-comparisons` implied by `-D warnings` + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:13:13 + | +LL | let _ = a != b; + | ^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:14:13 + | +LL | let _ = a < b; + | ^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:15:13 + | +LL | let _ = a <= b; + | ^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:16:13 + | +LL | let _ = a > b; + | ^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:17:13 + | +LL | let _ = a >= b; + | ^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:18:5 + | +LL | ptr::eq(a, b); + | ^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:22:5 + | +LL | ptr::eq(a, b); + | ^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:25:5 + | +LL | Rc::ptr_eq(&a, &a); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:28:5 + | +LL | Arc::ptr_eq(&a, &a); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/while_let_loop.rs b/src/tools/clippy/tests/ui/while_let_loop.rs new file mode 100644 index 0000000000000..3ce699f551b20 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_loop.rs @@ -0,0 +1,119 @@ +#![warn(clippy::while_let_loop)] + +fn main() { + let y = Some(true); + loop { + if let Some(_x) = y { + let _v = 1; + } else { + break; + } + } + + #[allow(clippy::never_loop)] + loop { + // no error, break is not in else clause + if let Some(_x) = y { + let _v = 1; + } + break; + } + + loop { + match y { + Some(_x) => true, + None => break, + }; + } + + loop { + let x = match y { + Some(x) => x, + None => break, + }; + let _x = x; + let _str = "foo"; + } + + loop { + let x = match y { + Some(x) => x, + None => break, + }; + { + let _a = "bar"; + }; + { + let _b = "foobar"; + } + } + + loop { + // no error, else branch does something other than break + match y { + Some(_x) => true, + _ => { + let _z = 1; + break; + }, + }; + } + + while let Some(x) = y { + // no error, obviously + println!("{}", x); + } + + // #675, this used to have a wrong suggestion + loop { + let (e, l) = match "".split_whitespace().next() { + Some(word) => (word.is_empty(), word.len()), + None => break, + }; + + let _ = (e, l); + } +} + +fn issue771() { + let mut a = 100; + let b = Some(true); + loop { + if a > 10 { + break; + } + + match b { + Some(_) => a = 0, + None => break, + } + } +} + +fn issue1017() { + let r: Result = Ok(42); + let mut len = 1337; + + loop { + match r { + Err(_) => len = 0, + Ok(length) => { + len = length; + break; + }, + } + } +} + +#[allow(clippy::never_loop)] +fn issue1948() { + // should not trigger clippy::while_let_loop lint because break passes an expression + let a = Some(10); + let b = loop { + if let Some(c) = a { + break Some(c); + } else { + break None; + } + }; +} diff --git a/src/tools/clippy/tests/ui/while_let_loop.stderr b/src/tools/clippy/tests/ui/while_let_loop.stderr new file mode 100644 index 0000000000000..13dd0ee224c10 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_loop.stderr @@ -0,0 +1,63 @@ +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:5:5 + | +LL | / loop { +LL | | if let Some(_x) = y { +LL | | let _v = 1; +LL | | } else { +LL | | break; +LL | | } +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + | + = note: `-D clippy::while-let-loop` implied by `-D warnings` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:22:5 + | +LL | / loop { +LL | | match y { +LL | | Some(_x) => true, +LL | | None => break, +LL | | }; +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:29:5 + | +LL | / loop { +LL | | let x = match y { +LL | | Some(x) => x, +LL | | None => break, +... | +LL | | let _str = "foo"; +LL | | } + | |_____^ help: try: `while let Some(x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:38:5 + | +LL | / loop { +LL | | let x = match y { +LL | | Some(x) => x, +LL | | None => break, +... | +LL | | } +LL | | } + | |_____^ help: try: `while let Some(x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:68:5 + | +LL | / loop { +LL | | let (e, l) = match "".split_whitespace().next() { +LL | | Some(word) => (word.is_empty(), word.len()), +LL | | None => break, +... | +LL | | let _ = (e, l); +LL | | } + | |_____^ help: try: `while let Some(word) = "".split_whitespace().next() { .. }` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed new file mode 100644 index 0000000000000..e99c98ac79f2a --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed @@ -0,0 +1,218 @@ +// run-rustfix + +#![warn(clippy::while_let_on_iterator)] +#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![feature(or_patterns)] + +fn base() { + let mut iter = 1..20; + for x in iter { + println!("{}", x); + } + + let mut iter = 1..20; + for x in iter { + println!("{}", x); + } + + let mut iter = 1..20; + for _ in iter {} + + let mut iter = 1..20; + while let None = iter.next() {} // this is fine (if nonsensical) + + let mut iter = 1..20; + if let Some(x) = iter.next() { + // also fine + println!("{}", x) + } + + // the following shouldn't warn because it can't be written with a for loop + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()) + } + + // neither can this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()); + } + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + break; + } + println!("Remaining iter {:?}", iter); + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + iter = 1..20; + } +} + +// Issue #1188 +fn refutable() { + let a = [42, 1337]; + let mut b = a.iter(); + + // consume all the 42s + while let Some(&42) = b.next() {} + + let a = [(1, 2, 3)]; + let mut b = a.iter(); + + while let Some(&(1, 2, 3)) = b.next() {} + + let a = [Some(42)]; + let mut b = a.iter(); + + while let Some(&None) = b.next() {} + + /* This gives “refutable pattern in `for` loop binding: `&_` not covered” + for &42 in b {} + for &(1, 2, 3) in b {} + for &Option::None in b.next() {} + // */ +} + +fn refutable2() { + // Issue 3780 + { + let v = vec![1, 2, 3]; + let mut it = v.windows(2); + while let Some([x, y]) = it.next() { + println!("x: {}", x); + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([x, ..]) = it.next() { + println!("x: {}", x); + } + + let mut it = v.windows(2); + while let Some([.., y]) = it.next() { + println!("y: {}", y); + } + + let mut it = v.windows(2); + for [..] in it {} + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some([1]) = it.next() {} + + let mut it = v.iter(); + for [_x] in it {} + } + + // binding + { + let v = vec![1, 2, 3]; + let mut it = v.iter(); + while let Some(x @ 1) = it.next() { + println!("{}", x); + } + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + for x @ [_] in it { + println!("{:?}", x); + } + } + + // false negative + { + let v = vec![1, 2, 3]; + let mut it = v.iter().map(Some); + while let Some(Some(_) | None) = it.next() { + println!("1"); + } + } +} + +fn nested_loops() { + let a = [42, 1337]; + let mut y = a.iter(); + loop { + // x is reused, so don't lint here + while let Some(_) = y.next() {} + } + + let mut y = a.iter(); + for _ in 0..2 { + while let Some(_) = y.next() { + // y is reused, don't lint + } + } + + loop { + let mut y = a.iter(); + for _ in y { + // use a for loop here + } + } +} + +fn issue1121() { + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(&value) = values.iter().next() { + values.remove(&value); + } +} + +fn issue2965() { + // This should not cause an ICE and suggest: + // + // for _ in values.iter() {} + // + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() {} +} + +fn issue3670() { + let array = [Some(0), None, Some(1)]; + let mut iter = array.iter(); + + while let Some(elem) = iter.next() { + let _ = elem.or_else(|| *iter.next()?); + } +} + +fn issue1654() { + // should not lint if the iterator is generated on every iteration + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() { + values.remove(&1); + } + + while let Some(..) = values.iter().map(|x| x + 1).next() {} + + let chars = "Hello, World!".char_indices(); + while let Some((i, ch)) = chars.clone().next() { + println!("{}: {}", i, ch); + } +} + +fn main() { + base(); + refutable(); + refutable2(); + nested_loops(); + issue1121(); + issue2965(); + issue3670(); + issue1654(); +} diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs new file mode 100644 index 0000000000000..ba13172428e13 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs @@ -0,0 +1,218 @@ +// run-rustfix + +#![warn(clippy::while_let_on_iterator)] +#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![feature(or_patterns)] + +fn base() { + let mut iter = 1..20; + while let Option::Some(x) = iter.next() { + println!("{}", x); + } + + let mut iter = 1..20; + while let Some(x) = iter.next() { + println!("{}", x); + } + + let mut iter = 1..20; + while let Some(_) = iter.next() {} + + let mut iter = 1..20; + while let None = iter.next() {} // this is fine (if nonsensical) + + let mut iter = 1..20; + if let Some(x) = iter.next() { + // also fine + println!("{}", x) + } + + // the following shouldn't warn because it can't be written with a for loop + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()) + } + + // neither can this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()); + } + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + break; + } + println!("Remaining iter {:?}", iter); + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + iter = 1..20; + } +} + +// Issue #1188 +fn refutable() { + let a = [42, 1337]; + let mut b = a.iter(); + + // consume all the 42s + while let Some(&42) = b.next() {} + + let a = [(1, 2, 3)]; + let mut b = a.iter(); + + while let Some(&(1, 2, 3)) = b.next() {} + + let a = [Some(42)]; + let mut b = a.iter(); + + while let Some(&None) = b.next() {} + + /* This gives “refutable pattern in `for` loop binding: `&_` not covered” + for &42 in b {} + for &(1, 2, 3) in b {} + for &Option::None in b.next() {} + // */ +} + +fn refutable2() { + // Issue 3780 + { + let v = vec![1, 2, 3]; + let mut it = v.windows(2); + while let Some([x, y]) = it.next() { + println!("x: {}", x); + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([x, ..]) = it.next() { + println!("x: {}", x); + } + + let mut it = v.windows(2); + while let Some([.., y]) = it.next() { + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([..]) = it.next() {} + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some([1]) = it.next() {} + + let mut it = v.iter(); + while let Some([_x]) = it.next() {} + } + + // binding + { + let v = vec![1, 2, 3]; + let mut it = v.iter(); + while let Some(x @ 1) = it.next() { + println!("{}", x); + } + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some(x @ [_]) = it.next() { + println!("{:?}", x); + } + } + + // false negative + { + let v = vec![1, 2, 3]; + let mut it = v.iter().map(Some); + while let Some(Some(_) | None) = it.next() { + println!("1"); + } + } +} + +fn nested_loops() { + let a = [42, 1337]; + let mut y = a.iter(); + loop { + // x is reused, so don't lint here + while let Some(_) = y.next() {} + } + + let mut y = a.iter(); + for _ in 0..2 { + while let Some(_) = y.next() { + // y is reused, don't lint + } + } + + loop { + let mut y = a.iter(); + while let Some(_) = y.next() { + // use a for loop here + } + } +} + +fn issue1121() { + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(&value) = values.iter().next() { + values.remove(&value); + } +} + +fn issue2965() { + // This should not cause an ICE and suggest: + // + // for _ in values.iter() {} + // + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() {} +} + +fn issue3670() { + let array = [Some(0), None, Some(1)]; + let mut iter = array.iter(); + + while let Some(elem) = iter.next() { + let _ = elem.or_else(|| *iter.next()?); + } +} + +fn issue1654() { + // should not lint if the iterator is generated on every iteration + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() { + values.remove(&1); + } + + while let Some(..) = values.iter().map(|x| x + 1).next() {} + + let chars = "Hello, World!".char_indices(); + while let Some((i, ch)) = chars.clone().next() { + println!("{}: {}", i, ch); + } +} + +fn main() { + base(); + refutable(); + refutable2(); + nested_loops(); + issue1121(); + issue2965(); + issue3670(); + issue1654(); +} diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr new file mode 100644 index 0000000000000..aa980d9965c76 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr @@ -0,0 +1,46 @@ +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:9:5 + | +LL | while let Option::Some(x) = iter.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` + | + = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:14:5 + | +LL | while let Some(x) = iter.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:19:5 + | +LL | while let Some(_) = iter.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:102:9 + | +LL | while let Some([..]) = it.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:109:9 + | +LL | while let Some([_x]) = it.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:122:9 + | +LL | while let Some(x @ [_]) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:154:9 + | +LL | while let Some(_) = y.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/wild_in_or_pats.rs b/src/tools/clippy/tests/ui/wild_in_or_pats.rs new file mode 100644 index 0000000000000..ad600f125772f --- /dev/null +++ b/src/tools/clippy/tests/ui/wild_in_or_pats.rs @@ -0,0 +1,36 @@ +#![warn(clippy::wildcard_in_or_patterns)] + +fn main() { + match "foo" { + "a" => { + dbg!("matched a"); + }, + "bar" | _ => { + dbg!("matched (bar or) wild"); + }, + }; + match "foo" { + "a" => { + dbg!("matched a"); + }, + "bar" | "bar2" | _ => { + dbg!("matched (bar or bar2 or) wild"); + }, + }; + match "foo" { + "a" => { + dbg!("matched a"); + }, + _ | "bar" | _ => { + dbg!("matched (bar or) wild"); + }, + }; + match "foo" { + "a" => { + dbg!("matched a"); + }, + _ | "bar" => { + dbg!("matched (bar or) wild"); + }, + }; +} diff --git a/src/tools/clippy/tests/ui/wild_in_or_pats.stderr b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr new file mode 100644 index 0000000000000..33c34cbbd4088 --- /dev/null +++ b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr @@ -0,0 +1,35 @@ +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:8:9 + | +LL | "bar" | _ => { + | ^^^^^^^^^ + | + = note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings` + = help: Consider handling `_` separately. + +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:16:9 + | +LL | "bar" | "bar2" | _ => { + | ^^^^^^^^^^^^^^^^^^ + | + = help: Consider handling `_` separately. + +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:24:9 + | +LL | _ | "bar" | _ => { + | ^^^^^^^^^^^^^ + | + = help: Consider handling `_` separately. + +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:32:9 + | +LL | _ | "bar" => { + | ^^^^^^^^^ + | + = help: Consider handling `_` separately. + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed new file mode 100644 index 0000000000000..2aa24ea1156aa --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed @@ -0,0 +1,101 @@ +// run-rustfix + +#![deny(clippy::wildcard_enum_match_arm)] +#![allow( + unreachable_code, + unused_variables, + dead_code, + clippy::single_match, + clippy::wildcard_in_or_patterns +)] + +use std::io::ErrorKind; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), + Cyan, +} + +impl Color { + fn is_monochrome(self) -> bool { + match self { + Color::Red | Color::Green | Color::Blue => true, + Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0, + Color::Cyan => false, + } + } +} + +fn main() { + let color = Color::Rgb(0, 0, 127); + match color { + Color::Red => println!("Red"), + Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => eprintln!("Not red"), + }; + match color { + Color::Red => println!("Red"), + _not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan => eprintln!("Not red"), + }; + let _str = match color { + Color::Red => "Red".to_owned(), + not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan => format!("{:?}", not_red), + }; + match color { + Color::Red => {}, + Color::Green => {}, + Color::Blue => {}, + Color::Cyan => {}, + c if c.is_monochrome() => {}, + Color::Rgb(_, _, _) => {}, + }; + let _str = match color { + Color::Red => "Red", + c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red", + }; + match color { + Color::Rgb(r, _, _) if r > 0 => "Some red", + Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => "No red", + }; + match color { + Color::Red | Color::Green | Color::Blue | Color::Cyan => {}, + Color::Rgb(..) => {}, + }; + let x: u8 = unimplemented!(); + match x { + 0 => {}, + 140 => {}, + _ => {}, + }; + // We need to use an enum not defined in this test because non_exhaustive is ignored for the + // purposes of dead code analysis within a crate. + let error_kind = ErrorKind::NotFound; + match error_kind { + ErrorKind::NotFound => {}, + std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {}, + } + match error_kind { + ErrorKind::NotFound => {}, + ErrorKind::PermissionDenied => {}, + ErrorKind::ConnectionRefused => {}, + ErrorKind::ConnectionReset => {}, + ErrorKind::ConnectionAborted => {}, + ErrorKind::NotConnected => {}, + ErrorKind::AddrInUse => {}, + ErrorKind::AddrNotAvailable => {}, + ErrorKind::BrokenPipe => {}, + ErrorKind::AlreadyExists => {}, + ErrorKind::WouldBlock => {}, + ErrorKind::InvalidInput => {}, + ErrorKind::InvalidData => {}, + ErrorKind::TimedOut => {}, + ErrorKind::WriteZero => {}, + ErrorKind::Interrupted => {}, + ErrorKind::Other => {}, + ErrorKind::UnexpectedEof => {}, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs new file mode 100644 index 0000000000000..07c93feaf284e --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs @@ -0,0 +1,101 @@ +// run-rustfix + +#![deny(clippy::wildcard_enum_match_arm)] +#![allow( + unreachable_code, + unused_variables, + dead_code, + clippy::single_match, + clippy::wildcard_in_or_patterns +)] + +use std::io::ErrorKind; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), + Cyan, +} + +impl Color { + fn is_monochrome(self) -> bool { + match self { + Color::Red | Color::Green | Color::Blue => true, + Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0, + Color::Cyan => false, + } + } +} + +fn main() { + let color = Color::Rgb(0, 0, 127); + match color { + Color::Red => println!("Red"), + _ => eprintln!("Not red"), + }; + match color { + Color::Red => println!("Red"), + _not_red => eprintln!("Not red"), + }; + let _str = match color { + Color::Red => "Red".to_owned(), + not_red => format!("{:?}", not_red), + }; + match color { + Color::Red => {}, + Color::Green => {}, + Color::Blue => {}, + Color::Cyan => {}, + c if c.is_monochrome() => {}, + Color::Rgb(_, _, _) => {}, + }; + let _str = match color { + Color::Red => "Red", + c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red", + }; + match color { + Color::Rgb(r, _, _) if r > 0 => "Some red", + _ => "No red", + }; + match color { + Color::Red | Color::Green | Color::Blue | Color::Cyan => {}, + Color::Rgb(..) => {}, + }; + let x: u8 = unimplemented!(); + match x { + 0 => {}, + 140 => {}, + _ => {}, + }; + // We need to use an enum not defined in this test because non_exhaustive is ignored for the + // purposes of dead code analysis within a crate. + let error_kind = ErrorKind::NotFound; + match error_kind { + ErrorKind::NotFound => {}, + _ => {}, + } + match error_kind { + ErrorKind::NotFound => {}, + ErrorKind::PermissionDenied => {}, + ErrorKind::ConnectionRefused => {}, + ErrorKind::ConnectionReset => {}, + ErrorKind::ConnectionAborted => {}, + ErrorKind::NotConnected => {}, + ErrorKind::AddrInUse => {}, + ErrorKind::AddrNotAvailable => {}, + ErrorKind::BrokenPipe => {}, + ErrorKind::AlreadyExists => {}, + ErrorKind::WouldBlock => {}, + ErrorKind::InvalidInput => {}, + ErrorKind::InvalidData => {}, + ErrorKind::TimedOut => {}, + ErrorKind::WriteZero => {}, + ErrorKind::Interrupted => {}, + ErrorKind::Other => {}, + ErrorKind::UnexpectedEof => {}, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr new file mode 100644 index 0000000000000..7151a5c5770bf --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr @@ -0,0 +1,38 @@ +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:37:9 + | +LL | _ => eprintln!("Not red"), + | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` + | +note: the lint level is defined here + --> $DIR/wildcard_enum_match_arm.rs:3:9 + | +LL | #![deny(clippy::wildcard_enum_match_arm)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:41:9 + | +LL | _not_red => eprintln!("Not red"), + | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` + +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:45:9 + | +LL | not_red => format!("{:?}", not_red), + | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` + +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:61:9 + | +LL | _ => "No red", + | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/wildcard_enum_match_arm.rs:78:9 + | +LL | _ => {}, + | ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed new file mode 100644 index 0000000000000..67423e6ec1d19 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -0,0 +1,230 @@ +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +//#![allow(clippy::redundant_pub_crate)] +#![allow(unused)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +#[macro_use] +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import; +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_futher_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs new file mode 100644 index 0000000000000..3ad1a29aebad1 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_imports.rs @@ -0,0 +1,231 @@ +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +//#![allow(clippy::redundant_pub_crate)] +#![allow(unused)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::*; +use crate::mod_mod::*; +use crate::multi_fn_mod::*; +#[macro_use] +use crate::struct_mod::*; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import; +use wildcard_imports_helper::inner::inner_for_self_import::*; +use wildcard_imports_helper::*; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::*; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + use wildcard_imports_helper::*; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::*, inner2::*}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::*; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::*; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test:: * ; + use crate:: fn_mod:: + *; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_futher_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr new file mode 100644 index 0000000000000..fab43b738eb43 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -0,0 +1,126 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:11:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:12:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:13:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:15:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:20:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:89:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:95:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:96:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:107:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:107:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:114:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:143:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:152:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:153:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:164:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:199:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:207:13 + | +LL | use super_imports::*; + | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:216:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:225:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs new file mode 100644 index 0000000000000..d8205c5eb6700 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal.rs @@ -0,0 +1,43 @@ +#![allow(unused_must_use)] +#![warn(clippy::write_literal)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // these should be fine + write!(&mut v, "Hello"); + writeln!(&mut v, "Hello"); + let world = "world"; + writeln!(&mut v, "Hello {}", world); + writeln!(&mut v, "Hello {world}", world = world); + writeln!(&mut v, "3 in hex is {:X}", 3); + writeln!(&mut v, "2 + 1 = {:.4}", 3); + writeln!(&mut v, "2 + 1 = {:5.4}", 3); + writeln!(&mut v, "Debug test {:?}", "hello, world"); + writeln!(&mut v, "{0:8} {1:>8}", "hello", "world"); + writeln!(&mut v, "{1:8} {0:>8}", "hello", "world"); + writeln!(&mut v, "{foo:8} {bar:>8}", foo = "hello", bar = "world"); + writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world"); + writeln!(&mut v, "{number:>width$}", number = 1, width = 6); + writeln!(&mut v, "{number:>0width$}", number = 1, width = 6); + + // these should throw warnings + writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); + write!(&mut v, "Hello {}", "world"); + writeln!(&mut v, "Hello {} {}", world, "world"); + writeln!(&mut v, "Hello {}", "world"); + writeln!(&mut v, "10 / 4 is {}", 2.5); + writeln!(&mut v, "2 + 1 = {}", 3); + + // positional args don't change the fact + // that we're using a literal -- this should + // throw a warning + writeln!(&mut v, "{0} {1}", "hello", "world"); + writeln!(&mut v, "{1} {0}", "hello", "world"); + + // named args shouldn't change anything either + writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); + writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); +} diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr new file mode 100644 index 0000000000000..54a787fe555af --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -0,0 +1,88 @@ +error: literal with an empty format string + --> $DIR/write_literal.rs:27:79 + | +LL | writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); + | ^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` + +error: literal with an empty format string + --> $DIR/write_literal.rs:28:32 + | +LL | write!(&mut v, "Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:29:44 + | +LL | writeln!(&mut v, "Hello {} {}", world, "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:30:34 + | +LL | writeln!(&mut v, "Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:31:38 + | +LL | writeln!(&mut v, "10 / 4 is {}", 2.5); + | ^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:32:36 + | +LL | writeln!(&mut v, "2 + 1 = {}", 3); + | ^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:37:33 + | +LL | writeln!(&mut v, "{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:37:42 + | +LL | writeln!(&mut v, "{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:38:33 + | +LL | writeln!(&mut v, "{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:38:42 + | +LL | writeln!(&mut v, "{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:41:43 + | +LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:41:58 + | +LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:42:43 + | +LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:42:58 + | +LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/write_with_newline.rs b/src/tools/clippy/tests/ui/write_with_newline.rs new file mode 100644 index 0000000000000..93afd73d1114d --- /dev/null +++ b/src/tools/clippy/tests/ui/write_with_newline.rs @@ -0,0 +1,58 @@ +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 +// // run-rustfix + +#![allow(clippy::write_literal)] +#![warn(clippy::write_with_newline)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // These should fail + write!(&mut v, "Hello\n"); + write!(&mut v, "Hello {}\n", "world"); + write!(&mut v, "Hello {} {}\n", "world", "#2"); + write!(&mut v, "{}\n", 1265); + + // These should be fine + write!(&mut v, ""); + write!(&mut v, "Hello"); + writeln!(&mut v, "Hello"); + writeln!(&mut v, "Hello\n"); + writeln!(&mut v, "Hello {}\n", "world"); + write!(&mut v, "Issue\n{}", 1265); + write!(&mut v, "{}", 1265); + write!(&mut v, "\n{}", 1275); + write!(&mut v, "\n\n"); + write!(&mut v, "like eof\n\n"); + write!(&mut v, "Hello {} {}\n\n", "world", "#2"); + writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + writeln!(&mut v, "\nbla\n\n"); // #3126 + + // Escaping + write!(&mut v, "\\n"); // #3514 + write!(&mut v, "\\\n"); // should fail + write!(&mut v, "\\\\n"); + + // Raw strings + write!(&mut v, r"\n"); // #3778 + + // Literal newlines should also fail + write!( + &mut v, + " +" + ); + write!( + &mut v, + r" +" + ); + + // Don't warn on CRLF (#4208) + write!(&mut v, "\r\n"); + write!(&mut v, "foo\r\n"); + write!(&mut v, "\\r\n"); //~ ERROR + write!(&mut v, "foo\rbar\n"); +} diff --git a/src/tools/clippy/tests/ui/write_with_newline.stderr b/src/tools/clippy/tests/ui/write_with_newline.stderr new file mode 100644 index 0000000000000..2473329ca7276 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_with_newline.stderr @@ -0,0 +1,114 @@ +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:13:5 + | +LL | write!(&mut v, "Hello/n"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::write-with-newline` implied by `-D warnings` +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "Hello"); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:14:5 + | +LL | write!(&mut v, "Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "Hello {}", "world"); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:15:5 + | +LL | write!(&mut v, "Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "Hello {} {}", "world", "#2"); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:16:5 + | +LL | write!(&mut v, "{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "{}", 1265); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:35:5 + | +LL | write!(&mut v, "//n"); // should fail + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "/"); // should fail + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:42:5 + | +LL | / write!( +LL | | &mut v, +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `writeln!()` instead + | +LL | writeln!( +LL | &mut v, +LL | "" + | + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:47:5 + | +LL | / write!( +LL | | &mut v, +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `writeln!()` instead + | +LL | writeln!( +LL | &mut v, +LL | r"" + | + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:56:5 + | +LL | write!(&mut v, "/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "/r"); //~ ERROR + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:57:5 + | +LL | write!(&mut v, "foo/rbar/n"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "foo/rbar"); + | ^^^^^^^ -- + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.fixed b/src/tools/clippy/tests/ui/writeln_empty_string.fixed new file mode 100644 index 0000000000000..c3ac15b03751c --- /dev/null +++ b/src/tools/clippy/tests/ui/writeln_empty_string.fixed @@ -0,0 +1,20 @@ +// run-rustfix + +#![allow(unused_must_use)] +#![warn(clippy::writeln_empty_string)] +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // These should fail + writeln!(&mut v); + + let mut suggestion = Vec::new(); + writeln!(&mut suggestion); + + // These should be fine + writeln!(&mut v); + writeln!(&mut v, " "); + write!(&mut v, ""); +} diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.rs b/src/tools/clippy/tests/ui/writeln_empty_string.rs new file mode 100644 index 0000000000000..9a8894b6c0d32 --- /dev/null +++ b/src/tools/clippy/tests/ui/writeln_empty_string.rs @@ -0,0 +1,20 @@ +// run-rustfix + +#![allow(unused_must_use)] +#![warn(clippy::writeln_empty_string)] +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // These should fail + writeln!(&mut v, ""); + + let mut suggestion = Vec::new(); + writeln!(&mut suggestion, ""); + + // These should be fine + writeln!(&mut v); + writeln!(&mut v, " "); + write!(&mut v, ""); +} diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.stderr b/src/tools/clippy/tests/ui/writeln_empty_string.stderr new file mode 100644 index 0000000000000..99635229b3e13 --- /dev/null +++ b/src/tools/clippy/tests/ui/writeln_empty_string.stderr @@ -0,0 +1,16 @@ +error: using `writeln!(&mut v, "")` + --> $DIR/writeln_empty_string.rs:11:5 + | +LL | writeln!(&mut v, ""); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut v)` + | + = note: `-D clippy::writeln-empty-string` implied by `-D warnings` + +error: using `writeln!(&mut suggestion, "")` + --> $DIR/writeln_empty_string.rs:14:5 + | +LL | writeln!(&mut suggestion, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut suggestion)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs new file mode 100644 index 0000000000000..99652ca4470c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -0,0 +1,77 @@ +#![warn(clippy::wrong_self_convention)] +#![warn(clippy::wrong_pub_self_convention)] +#![allow(dead_code)] + +fn main() {} + +#[derive(Clone, Copy)] +struct Foo; + +impl Foo { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn from_i32(self) {} + + pub fn as_i64(self) {} + pub fn into_i64(self) {} + pub fn is_i64(self) {} + pub fn to_i64(self) {} + pub fn from_i64(self) {} + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + pub fn from_cake(self) {} + + fn as_x>(_: F) {} + fn as_y>(_: F) {} +} + +struct Bar; + +impl Bar { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(&self) {} + fn into_u32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn to_u32(&self) {} + fn from_i32(self) {} + + pub fn as_i64(self) {} + pub fn into_i64(&self) {} + pub fn is_i64(self) {} + pub fn to_i64(self) {} + pub fn from_i64(self) {} + + // test for false positives + fn as_(self) {} + fn into_(&self) {} + fn is_(self) {} + fn to_(self) {} + fn from_(self) {} + fn to_mut(&mut self) {} +} + +// Allow Box, Rc, Arc for methods that take conventionally take Self by value +#[allow(clippy::boxed_local)] +mod issue4293 { + use std::rc::Rc; + use std::sync::Arc; + + struct T; + + impl T { + fn into_s1(self: Box) {} + fn into_s2(self: Rc) {} + fn into_s3(self: Arc) {} + + fn into_t1(self: Box) {} + fn into_t2(self: Rc) {} + fn into_t3(self: Arc) {} + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr new file mode 100644 index 0000000000000..0d0eb19cd0723 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -0,0 +1,76 @@ +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:17:17 + | +LL | fn from_i32(self) {} + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:23:21 + | +LL | pub fn from_i64(self) {} + | ^^^^ + +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:35:15 + | +LL | fn as_i32(self) {} + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:37:17 + | +LL | fn into_i32(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:39:15 + | +LL | fn is_i32(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:41:15 + | +LL | fn to_i32(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:43:17 + | +LL | fn from_i32(self) {} + | ^^^^ + +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:45:19 + | +LL | pub fn as_i64(self) {} + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:46:21 + | +LL | pub fn into_i64(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:47:19 + | +LL | pub fn is_i64(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:48:19 + | +LL | pub fn to_i64(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:49:21 + | +LL | pub fn from_i64(self) {} + | ^^^^ + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/zero_div_zero.rs b/src/tools/clippy/tests/ui/zero_div_zero.rs new file mode 100644 index 0000000000000..09db130a76431 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_div_zero.rs @@ -0,0 +1,13 @@ +#[allow(unused_variables)] +#[warn(clippy::zero_divided_by_zero)] +fn main() { + let nan = 0.0 / 0.0; + let f64_nan = 0.0 / 0.0f64; + let other_f64_nan = 0.0f64 / 0.0; + let one_more_f64_nan = 0.0f64 / 0.0f64; + let zero = 0.0; + let other_zero = 0.0; + let other_nan = zero / other_zero; // fine - this lint doesn't propegate constants. + let not_nan = 2.0 / 0.0; // not an error: 2/0 = inf + let also_not_nan = 0.0 / 2.0; // not an error: 0/2 = 0 +} diff --git a/src/tools/clippy/tests/ui/zero_div_zero.stderr b/src/tools/clippy/tests/ui/zero_div_zero.stderr new file mode 100644 index 0000000000000..d0e88f3c5a546 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_div_zero.stderr @@ -0,0 +1,61 @@ +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:4:15 + | +LL | let nan = 0.0 / 0.0; + | ^^^^^^^^^ + | + = note: `#[deny(clippy::eq_op)]` on by default + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:4:15 + | +LL | let nan = 0.0 / 0.0; + | ^^^^^^^^^ + | + = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings` + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:5:19 + | +LL | let f64_nan = 0.0 / 0.0f64; + | ^^^^^^^^^^^^ + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:5:19 + | +LL | let f64_nan = 0.0 / 0.0f64; + | ^^^^^^^^^^^^ + | + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:6:25 + | +LL | let other_f64_nan = 0.0f64 / 0.0; + | ^^^^^^^^^^^^ + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:6:25 + | +LL | let other_f64_nan = 0.0f64 / 0.0; + | ^^^^^^^^^^^^ + | + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:7:28 + | +LL | let one_more_f64_nan = 0.0f64 / 0.0f64; + | ^^^^^^^^^^^^^^^ + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:7:28 + | +LL | let one_more_f64_nan = 0.0f64 / 0.0f64; + | ^^^^^^^^^^^^^^^ + | + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/zero_offset.rs b/src/tools/clippy/tests/ui/zero_offset.rs new file mode 100644 index 0000000000000..2de904376ad45 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_offset.rs @@ -0,0 +1,12 @@ +fn main() { + unsafe { + let x = &() as *const (); + x.offset(0); + x.wrapping_add(0); + x.sub(0); + x.wrapping_sub(0); + + let y = &1 as *const u8; + y.offset(0); + } +} diff --git a/src/tools/clippy/tests/ui/zero_offset.stderr b/src/tools/clippy/tests/ui/zero_offset.stderr new file mode 100644 index 0000000000000..cfcd7de2b3d2c --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_offset.stderr @@ -0,0 +1,9 @@ +error[E0606]: casting `&i32` as `*const u8` is invalid + --> $DIR/zero_offset.rs:9:17 + | +LL | let y = &1 as *const u8; + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0606`. diff --git a/src/tools/clippy/tests/ui/zero_ptr.fixed b/src/tools/clippy/tests/ui/zero_ptr.fixed new file mode 100644 index 0000000000000..489aa4121a3a9 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_ptr.fixed @@ -0,0 +1,14 @@ +// run-rustfix +pub fn foo(_const: *const f32, _mut: *mut i64) {} + +fn main() { + let _ = std::ptr::null::(); + let _ = std::ptr::null_mut::(); + let _: *const u8 = std::ptr::null(); + + foo(0 as _, 0 as _); + foo(std::ptr::null(), std::ptr::null_mut()); + + let z = 0; + let _ = z as *const usize; // this is currently not caught +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.rs b/src/tools/clippy/tests/ui/zero_ptr.rs new file mode 100644 index 0000000000000..c3b55ef9ebd90 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_ptr.rs @@ -0,0 +1,14 @@ +// run-rustfix +pub fn foo(_const: *const f32, _mut: *mut i64) {} + +fn main() { + let _ = 0 as *const usize; + let _ = 0 as *mut f64; + let _: *const u8 = 0 as *const _; + + foo(0 as _, 0 as _); + foo(0 as *const _, 0 as *mut _); + + let z = 0; + let _ = z as *const usize; // this is currently not caught +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.stderr b/src/tools/clippy/tests/ui/zero_ptr.stderr new file mode 100644 index 0000000000000..4ee5e9a261686 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_ptr.stderr @@ -0,0 +1,34 @@ +error: `0 as *const _` detected + --> $DIR/zero_ptr.rs:5:13 + | +LL | let _ = 0 as *const usize; + | ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::null::()` + | + = note: `-D clippy::zero-ptr` implied by `-D warnings` + +error: `0 as *mut _` detected + --> $DIR/zero_ptr.rs:6:13 + | +LL | let _ = 0 as *mut f64; + | ^^^^^^^^^^^^^ help: try: `std::ptr::null_mut::()` + +error: `0 as *const _` detected + --> $DIR/zero_ptr.rs:7:24 + | +LL | let _: *const u8 = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `std::ptr::null()` + +error: `0 as *const _` detected + --> $DIR/zero_ptr.rs:10:9 + | +LL | foo(0 as *const _, 0 as *mut _); + | ^^^^^^^^^^^^^ help: try: `std::ptr::null()` + +error: `0 as *mut _` detected + --> $DIR/zero_ptr.rs:10:24 + | +LL | foo(0 as *const _, 0 as *mut _); + | ^^^^^^^^^^^ help: try: `std::ptr::null_mut()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs new file mode 100644 index 0000000000000..f5d03c645df04 --- /dev/null +++ b/src/tools/clippy/tests/versioncheck.rs @@ -0,0 +1,19 @@ +#[test] +fn check_that_clippy_lints_has_the_same_version_as_clippy() { + let clippy_meta = cargo_metadata::MetadataCommand::new() + .no_deps() + .exec() + .expect("could not obtain cargo metadata"); + std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap(); + let clippy_lints_meta = cargo_metadata::MetadataCommand::new() + .no_deps() + .exec() + .expect("could not obtain cargo metadata"); + assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version); + for package in &clippy_meta.packages[0].dependencies { + if package.name == "clippy_lints" { + assert!(package.req.matches(&clippy_lints_meta.packages[0].version)); + return; + } + } +} diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml new file mode 100644 index 0000000000000..411229935c36a --- /dev/null +++ b/src/tools/clippy/triagebot.toml @@ -0,0 +1,7 @@ +[relabel] +allow-unauthenticated = [ + "C-*", "A-*", "E-*", "L-*", "M-*", "O-*", + "good first issue", "needs test" +] + +[assign] diff --git a/src/tools/clippy/util/cov.sh b/src/tools/clippy/util/cov.sh new file mode 100755 index 0000000000000..3f9a6b06f7255 --- /dev/null +++ b/src/tools/clippy/util/cov.sh @@ -0,0 +1,37 @@ +#!/usr/bin/bash + +# This run `kcov` on Clippy. The coverage report will be at +# `./target/cov/index.html`. +# `compile-test` is special. `kcov` does not work directly on it so these files +# are compiled manually. + +tests=$(find tests/ -maxdepth 1 -name '*.rs' ! -name compile-test.rs -exec basename {} .rs \;) +tmpdir=$(mktemp -d) + +cargo test --no-run --verbose + +for t in $tests; do + kcov \ + --verify \ + --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \ + "$tmpdir/$t" \ + cargo test --test "$t" +done + +for t in ./tests/compile-fail/*.rs; do + kcov \ + --verify \ + --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \ + "$tmpdir/compile-fail-$(basename "$t")" \ + cargo run -- -L target/debug -L target/debug/deps -Z no-trans "$t" +done + +for t in ./tests/run-pass/*.rs; do + kcov \ + --verify \ + --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \ + "$tmpdir/run-pass-$(basename "$t")" \ + cargo run -- -L target/debug -L target/debug/deps -Z no-trans "$t" +done + +kcov --verify --merge target/cov "$tmpdir"/* diff --git a/src/tools/clippy/util/dev b/src/tools/clippy/util/dev new file mode 100755 index 0000000000000..319de217e0d90 --- /dev/null +++ b/src/tools/clippy/util/dev @@ -0,0 +1,7 @@ +#!/bin/sh +CARGO_TARGET_DIR=$(pwd)/target/ +export CARGO_TARGET_DIR + +echo 'Deprecated! `util/dev` usage is deprecated, please use `cargo dev` instead.' + +cd clippy_dev && cargo run -- "$@" diff --git a/src/tools/clippy/util/export.py b/src/tools/clippy/util/export.py new file mode 100755 index 0000000000000..5d1bd60acf3d6 --- /dev/null +++ b/src/tools/clippy/util/export.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +# Build the gh-pages + +from collections import OrderedDict +import re +import sys +import json + +from lintlib import parse_all, log + +lint_subheadline = re.compile(r'''^\*\*([\w\s]+?)[:?.!]?\*\*(.*)''') +rust_code_block = re.compile(r'''```rust.+?```''', flags=re.DOTALL) + +CONF_TEMPLATE = """\ +This lint has the following configuration variables: + +* `%s: %s`: %s (defaults to `%s`).""" + + +def parse_code_block(match): + lines = [] + + for line in match.group(0).split('\n'): + if not line.startswith('# '): + lines.append(line) + + return '\n'.join(lines) + + +def parse_lint_def(lint): + lint_dict = {} + lint_dict['id'] = lint.name + lint_dict['group'] = lint.group + lint_dict['level'] = lint.level + lint_dict['docs'] = OrderedDict() + + last_section = None + + for line in lint.doc: + match = re.match(lint_subheadline, line) + if match: + last_section = match.groups()[0] + text = match.groups()[1] + else: + text = line + + if not last_section: + log.warning("Skipping comment line as it was not preceded by a heading") + log.debug("in lint `%s`, line `%s`", lint.name, line) + + if last_section not in lint_dict['docs']: + lint_dict['docs'][last_section] = "" + + lint_dict['docs'][last_section] += text + "\n" + + for section in lint_dict['docs']: + lint_dict['docs'][section] = re.sub(rust_code_block, parse_code_block, lint_dict['docs'][section].strip()) + + return lint_dict + + +def main(): + lintlist, configs = parse_all() + lints = {} + for lint in lintlist: + lints[lint.name] = parse_lint_def(lint) + if lint.name in configs: + lints[lint.name]['docs']['Configuration'] = \ + CONF_TEMPLATE % configs[lint.name] + + outfile = sys.argv[1] if len(sys.argv) > 1 else "util/gh-pages/lints.json" + with open(outfile, "w") as fp: + lints = list(lints.values()) + lints.sort(key=lambda x: x['id']) + json.dump(lints, fp, indent=2) + log.info("wrote JSON for great justice") + + +if __name__ == "__main__": + main() diff --git a/src/tools/clippy/util/fetch_prs_between.sh b/src/tools/clippy/util/fetch_prs_between.sh new file mode 100755 index 0000000000000..6865abf971b28 --- /dev/null +++ b/src/tools/clippy/util/fetch_prs_between.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Fetches the merge commits between two git commits and prints the PR URL +# together with the full commit message +# +# If you want to use this to update the Clippy changelog, be sure to manually +# exclude the non-user facing changes like 'rustup' PRs, typo fixes, etc. + +first=$1 +last=$2 + +IFS=' +' +for pr in $(git log --oneline --grep "Merge #" --grep "Merge pull request" --grep "Auto merge of" --grep "Rollup merge of" "$first...$last" | sort -rn | uniq); do + id=$(echo "$pr" | rg -o '#[0-9]{3,5}' | cut -c 2-) + commit=$(echo "$pr" | cut -d' ' -f 1) + message=$(git --no-pager show --pretty=medium "$commit") + if [[ -n $(echo "$message" | rg "^[\s]{4}changelog: [nN]one\.*$") ]]; then + continue + fi + + echo "URL: https://github.com/rust-lang/rust-clippy/pull/$id" + echo "Markdown URL: [#$id](https://github.com/rust-lang/rust-clippy/pull/$id)" + echo "$message" + echo "---------------------------------------------------------" + echo +done diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html new file mode 100644 index 0000000000000..e11f2eeba3b32 --- /dev/null +++ b/src/tools/clippy/util/gh-pages/index.html @@ -0,0 +1,277 @@ + + + + + + + ALL the Clippy Lints + + + + + + +

+ + + Fork me on Github + + + + + + + + + diff --git a/src/tools/clippy/util/gh-pages/versions.html b/src/tools/clippy/util/gh-pages/versions.html new file mode 100644 index 0000000000000..6e810a349bfcc --- /dev/null +++ b/src/tools/clippy/util/gh-pages/versions.html @@ -0,0 +1,91 @@ + + + + + + + Clippy lints documentation + + + + + +
+ + +
+ + + + +
+
+ + + + + + + + + + diff --git a/src/tools/clippy/util/lintlib.py b/src/tools/clippy/util/lintlib.py new file mode 100644 index 0000000000000..d0d9beb9b2d9f --- /dev/null +++ b/src/tools/clippy/util/lintlib.py @@ -0,0 +1,114 @@ +# Common utils for the several housekeeping scripts. + +import os +import re +import collections + +import logging as log +log.basicConfig(level=log.INFO, format='%(levelname)s: %(message)s') + +Lint = collections.namedtuple('Lint', 'name level doc sourcefile group') +Config = collections.namedtuple('Config', 'name ty doc default') + +lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''') +group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''') +conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE) +confvar_re = re.compile( + r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE) +comment_re = re.compile(r'''\s*/// ?(.*)''') + +lint_levels = { + "correctness": 'Deny', + "style": 'Warn', + "complexity": 'Warn', + "perf": 'Warn', + "restriction": 'Allow', + "pedantic": 'Allow', + "nursery": 'Allow', + "cargo": 'Allow', +} + + +def parse_lints(lints, filepath): + comment = [] + clippy = False + deprecated = False + name = "" + + with open(filepath) as fp: + for line in fp: + if clippy or deprecated: + m = lintname_re.search(line) + if m: + name = m.group(1).lower() + line = next(fp) + + if deprecated: + level = "Deprecated" + group = "deprecated" + else: + while True: + g = group_re.search(line) + if g: + group = g.group(1).lower() + level = lint_levels.get(group, None) + break + line = next(fp) + + if level is None: + continue + + log.info("found %s with level %s in %s", + name, level, filepath) + lints.append(Lint(name, level, comment, filepath, group)) + comment = [] + + clippy = False + deprecated = False + name = "" + else: + m = comment_re.search(line) + if m: + comment.append(m.group(1)) + elif line.startswith("declare_clippy_lint!"): + clippy = True + deprecated = False + elif line.startswith("declare_deprecated_lint!"): + clippy = False + deprecated = True + elif line.startswith("declare_lint!"): + import sys + print( + "don't use `declare_lint!` in Clippy, " + "use `declare_clippy_lint!` instead" + ) + sys.exit(42) + + +def parse_configs(path): + configs = {} + with open(os.path.join(path, 'utils/conf.rs')) as fp: + contents = fp.read() + + match = re.search(conf_re, contents) + confvars = re.findall(confvar_re, match.group(1)) + + for (lints, doc, name, ty, default) in confvars: + for lint in lints.split(','): + configs[lint.strip().lower()] = Config(name.replace("_", "-"), ty, doc, default) + return configs + + +def parse_all(path="clippy_lints/src"): + lints = [] + for root, dirs, files in os.walk(path): + for fn in files: + if fn.endswith('.rs'): + parse_lints(lints, os.path.join(root, fn)) + + log.info("got %s lints", len(lints)) + + configs = parse_configs(path) + log.info("got %d configs", len(configs)) + + return lints, configs diff --git a/src/tools/clippy/util/versions.py b/src/tools/clippy/util/versions.py new file mode 100755 index 0000000000000..5cdc7313f5439 --- /dev/null +++ b/src/tools/clippy/util/versions.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +import json +import os +import sys + +from lintlib import log + + +def key(v): + if v == 'master': + return float('inf') + if v == 'stable': + return sys.maxsize + if v == 'beta': + return sys.maxsize - 1 + + v = v.replace('v', '').replace('rust-', '') + + s = 0 + for i, val in enumerate(v.split('.')[::-1]): + s += int(val) * 100**i + + return s + + +def main(): + if len(sys.argv) < 2: + print("Error: specify output directory") + return + + outdir = sys.argv[1] + versions = [ + dir for dir in os.listdir(outdir) if not dir.startswith(".") and os.path.isdir(os.path.join(outdir, dir)) + ] + versions.sort(key=key) + + with open(os.path.join(outdir, "versions.json"), "w") as fp: + json.dump(versions, fp, indent=2) + log.info("wrote JSON for great justice") + + +if __name__ == "__main__": + main() diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index a61b940398704..93a414ff6b9f3 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -347,7 +347,10 @@ pub fn run_tests(config: Config) { Ok(true) => {} Ok(false) => panic!("Some tests failed"), Err(e) => { - println!("I/O failure during tests: {:?}", e); + // We don't know if tests passed or not, but if there was an error + // during testing we don't want to just suceeed (we may not have + // tested something), so fail. + panic!("I/O failure during tests: {:?}", e); } } } @@ -449,15 +452,8 @@ pub fn test_opts(config: &Config) -> test::TestOpts { pub fn make_tests(config: &Config, tests: &mut Vec) { debug!("making tests from {:?}", config.src_base.display()); let inputs = common_inputs_stamp(config); - collect_tests_from_dir( - config, - &config.src_base, - &config.src_base, - &PathBuf::new(), - &inputs, - tests, - ) - .expect(&format!("Could not read tests from {}", config.src_base.display())); + collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests) + .expect(&format!("Could not read tests from {}", config.src_base.display())); } /// Returns a stamp constructed from input files common to all test cases. @@ -494,7 +490,6 @@ fn common_inputs_stamp(config: &Config) -> Stamp { fn collect_tests_from_dir( config: &Config, - base: &Path, dir: &Path, relative_dir_path: &Path, inputs: &Stamp, @@ -538,14 +533,7 @@ fn collect_tests_from_dir( 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( - config, - base, - &file_path, - &relative_file_path, - inputs, - tests, - )?; + collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?; } } else { debug!("found other file/directory: {:?}", file_path.display()); @@ -846,12 +834,28 @@ fn extract_gdb_version(full_version_line: &str) -> Option { // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both // of the ? sections being optional - // We will parse up to 3 digits for minor and patch, ignoring the date - // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version + // We will parse up to 3 digits for each component, ignoring the date + + // We skip text in parentheses. This avoids accidentally parsing + // the openSUSE version, which looks like: + // GNU gdb (GDB; openSUSE Leap 15.0) 8.1 + // This particular form is documented in the GNU coding standards: + // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion // don't start parsing in the middle of a number let mut prev_was_digit = false; + let mut in_parens = false; for (pos, c) in full_version_line.char_indices() { + if in_parens { + if c == ')' { + in_parens = false; + } + continue; + } else if c == '(' { + in_parens = true; + continue; + } + if prev_was_digit || !c.is_digit(10) { prev_was_digit = c.is_digit(10); continue; @@ -891,7 +895,7 @@ fn extract_gdb_version(full_version_line: &str) -> Option { None => (line, None), }; - if major.len() != 1 || minor.is_empty() { + if minor.is_empty() { continue; } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index b04012af515dd..63fd052a5560d 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -11,6 +11,7 @@ use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT}; use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::json; +use crate::util::get_pointer_width; use crate::util::{logv, PathBufExt}; use diff; use regex::{Captures, Regex}; @@ -20,7 +21,6 @@ use std::collections::hash_map::DefaultHasher; use std::collections::{HashMap, HashSet, VecDeque}; use std::env; use std::ffi::{OsStr, OsString}; -use std::fmt; use std::fs::{self, create_dir_all, File, OpenOptions}; use std::hash::{Hash, Hasher}; use std::io::prelude::*; @@ -45,7 +45,7 @@ fn disable_error_reporting R, R>(f: F) -> R { use winapi::um::winbase::SEM_NOGPFAULTERRORBOX; lazy_static! { - static ref LOCK: Mutex<()> = { Mutex::new(()) }; + static ref LOCK: Mutex<()> = Mutex::new(()); } // Error mode is a global variable, so lock it so only one thread will change it let _lock = LOCK.lock().unwrap(); @@ -178,6 +178,29 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec { + println!("-\t{}", e); + line_number += 1; + } + DiffLine::Context(c) => { + println!("{}\t{}", line_number, c); + line_number += 1; + } + DiffLine::Resulting(r) => { + println!("+\t{}", r); + } + } + } + println!(); + } +} + pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) { match &*config.target { "arm-linux-androideabi" @@ -334,14 +357,15 @@ impl<'test> TestCx<'test> { Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => { WillExecute::Yes } - Ui => WillExecute::No, + MirOpt if pm == Some(PassMode::Run) => WillExecute::Yes, + Ui | MirOpt => WillExecute::No, mode => panic!("unimplemented for mode {:?}", mode), } } fn should_run_successfully(&self, pm: Option) -> bool { match self.config.mode { - Ui => pm == Some(PassMode::Run), + Ui | MirOpt => pm == Some(PassMode::Run), mode => panic!("unimplemented for mode {:?}", mode), } } @@ -1726,6 +1750,7 @@ impl<'test> TestCx<'test> { || self.config.target.contains("wasm32") || self.config.target.contains("nvptx") || self.is_vxworks_pure_static() + || self.config.target.contains("sgx") { // We primarily compile all auxiliary libraries as dynamic libraries // to avoid code size bloat and large binaries as much as possible @@ -1852,7 +1877,6 @@ impl<'test> TestCx<'test> { if let Some(ref incremental_dir) = self.props.incremental_dir { rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]); rustc.args(&["-Z", "incremental-verify-ich"]); - rustc.args(&["-Z", "incremental-queries"]); } if self.config.mode == CodegenUnits { @@ -1936,6 +1960,12 @@ impl<'test> TestCx<'test> { None => {} } + // Add `-A unused` before `config` flags and in-test (`props`) flags, so that they can + // overwrite this. + if let AllowUnused::Yes = allow_unused { + rustc.args(&["-A", "unused"]); + } + if self.props.force_host { self.maybe_add_external_args( &mut rustc, @@ -1958,10 +1988,6 @@ impl<'test> TestCx<'test> { rustc.arg("-Ctarget-feature=-crt-static"); } - if let AllowUnused::Yes = allow_unused { - rustc.args(&["-A", "unused"]); - } - rustc.args(&self.props.compile_flags); rustc @@ -2490,7 +2516,7 @@ impl<'test> TestCx<'test> { .filter(|s| !s.is_empty()) .map(|s| { if cgu_has_crate_disambiguator { - remove_crate_disambiguator_from_cgu(s) + remove_crate_disambiguators_from_set_of_cgu_names(s) } else { s.to_string() } @@ -2540,6 +2566,16 @@ impl<'test> TestCx<'test> { new_name } + + // The name of merged CGUs is constructed as the names of the original + // CGUs joined with "--". This function splits such composite CGU names + // and handles each component individually. + fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String { + cgus.split("--") + .map(|cgu| remove_crate_disambiguator_from_cgu(cgu)) + .collect::>() + .join("--") + } } fn init_incremental_test(&self) { @@ -2571,12 +2607,12 @@ impl<'test> TestCx<'test> { // - if `cfail`, expect compilation to fail // - if `rfail`, expect execution to fail // - create a directory build/foo/bar.incremental - // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass1 + // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1 // - because name of revision starts with "rpass", expect success - // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C cfail2 + // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C cfail2 // - because name of revision starts with "cfail", expect an error // - load expected errors as usual, but filter for those that end in `[rfail2]` - // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass3 + // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass3 // - because name of revision starts with "rpass", expect success // - execute build/foo/bar.exe and save output // @@ -2775,10 +2811,16 @@ impl<'test> TestCx<'test> { self.document(&out_dir); let root = self.config.find_rust_src_root().unwrap(); + let file_stem = + self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem"); let res = self.cmd2procres( Command::new(&nodejs) .arg(root.join("src/tools/rustdoc-js/tester.js")) - .arg(out_dir.parent().expect("no parent")) + .arg("--doc-folder") + .arg(out_dir) + .arg("--crate-name") + .arg(file_stem.replace("-", "_")) + .arg("--test-file") .arg(self.testpaths.file.with_extension("js")), ); if !res.status.success() { @@ -3025,48 +3067,107 @@ impl<'test> TestCx<'test> { } fn run_mir_opt_test(&self) { - let proc_res = self.compile_test(WillExecute::Yes, EmitMetadata::No); + let pm = self.pass_mode(); + let should_run = self.should_run(pm); + let emit_metadata = self.should_emit_metadata(pm); + let proc_res = self.compile_test(should_run, emit_metadata); if !proc_res.status.success() { self.fatal_proc_rec("compilation failed!", &proc_res); } - let proc_res = self.exec_compiled_test(); + self.check_mir_dump(); - if !proc_res.status.success() { - self.fatal_proc_rec("test run failed!", &proc_res); + if let WillExecute::Yes = should_run { + let proc_res = self.exec_compiled_test(); + + if !proc_res.status.success() { + self.fatal_proc_rec("test run failed!", &proc_res); + } } - self.check_mir_dump(); } fn check_mir_dump(&self) { let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap(); - if let Some(idx) = test_file_contents.find("// END RUST SOURCE") { - let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len()); - let tests_text_str = String::from(tests_text); - let mut curr_test: Option<&str> = None; - let mut curr_test_contents = vec![ExpectedLine::Elision]; - for l in tests_text_str.lines() { - debug!("line: {:?}", l); - if l.starts_with("// START ") { - let (_, t) = l.split_at("// START ".len()); - curr_test = Some(t); - } else if l.starts_with("// END") { - let (_, t) = l.split_at("// END ".len()); - if Some(t) != curr_test { - panic!("mismatched START END test name"); + + let mut test_dir = self.testpaths.file.with_extension(""); + + if test_file_contents.lines().any(|l| l == "// EMIT_MIR_FOR_EACH_BIT_WIDTH") { + test_dir.push(get_pointer_width(&self.config.target)) + } + + if self.config.bless { + let _ = std::fs::remove_dir_all(&test_dir); + } + for l in test_file_contents.lines() { + if l.starts_with("// EMIT_MIR ") { + let test_name = l.trim_start_matches("// EMIT_MIR "); + let expected_file = test_dir.join(test_name); + + let dumped_string = if test_name.ends_with(".diff") { + let test_name = test_name.trim_end_matches(".diff"); + let before = format!("{}.before.mir", test_name); + let after = format!("{}.after.mir", test_name); + let before = self.get_mir_dump_dir().join(before); + let after = self.get_mir_dump_dir().join(after); + debug!( + "comparing the contents of: {} with {}", + before.display(), + after.display() + ); + let before = fs::read_to_string(before).unwrap(); + let after = fs::read_to_string(after).unwrap(); + let before = self.normalize_output(&before, &[]); + let after = self.normalize_output(&after, &[]); + let mut dumped_string = String::new(); + for result in diff::lines(&before, &after) { + use std::fmt::Write; + match result { + diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(), + diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(), + diff::Result::Both(s, _) => writeln!(dumped_string, " {}", s).unwrap(), + } + } + dumped_string + } else { + let mut output_file = PathBuf::new(); + output_file.push(self.get_mir_dump_dir()); + output_file.push(test_name); + debug!( + "comparing the contents of: {} with {}", + output_file.display(), + expected_file.display() + ); + if !output_file.exists() { + panic!( + "Output file `{}` from test does not exist, available files are in `{}`", + output_file.display(), + output_file.parent().unwrap().display() + ); + } + self.check_mir_test_timestamp(test_name, &output_file); + let dumped_string = fs::read_to_string(&output_file).unwrap(); + self.normalize_output(&dumped_string, &[]) + }; + if self.config.bless { + let _ = std::fs::create_dir_all(&test_dir); + let _ = std::fs::remove_file(&expected_file); + std::fs::write(expected_file, dumped_string.as_bytes()).unwrap(); + } else { + if !expected_file.exists() { + panic!( + "Output file `{}` from test does not exist", + expected_file.display() + ); + } + let expected_string = fs::read_to_string(&expected_file).unwrap(); + if dumped_string != expected_string { + print_diff(&expected_string, &dumped_string, 3); + panic!( + "Actual MIR output differs from expected MIR output {}", + expected_file.display() + ); } - self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents); - curr_test = None; - curr_test_contents.clear(); - curr_test_contents.push(ExpectedLine::Elision); - } else if l.is_empty() { - // ignore - } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." { - curr_test_contents.push(ExpectedLine::Elision) - } else if l.starts_with("// ") { - let (_, test_content) = l.split_at("// ".len()); - curr_test_contents.push(ExpectedLine::Text(test_content)); } } } @@ -3087,110 +3188,6 @@ impl<'test> TestCx<'test> { } } - fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) { - let mut output_file = PathBuf::new(); - output_file.push(self.get_mir_dump_dir()); - output_file.push(test_name); - debug!("comparing the contests of: {:?}", output_file); - debug!("with: {:?}", expected_content); - if !output_file.exists() { - panic!( - "Output file `{}` from test does not exist", - output_file.into_os_string().to_string_lossy() - ); - } - self.check_mir_test_timestamp(test_name, &output_file); - - let dumped_string = fs::read_to_string(&output_file).unwrap(); - let mut dumped_lines = - dumped_string.lines().map(|l| nocomment_mir_line(l)).filter(|l| !l.is_empty()); - let mut expected_lines = expected_content - .iter() - .filter(|&l| if let &ExpectedLine::Text(l) = l { !l.is_empty() } else { true }) - .peekable(); - - let compare = |expected_line, dumped_line| { - let e_norm = normalize_mir_line(expected_line); - let d_norm = normalize_mir_line(dumped_line); - debug!("found: {:?}", d_norm); - debug!("expected: {:?}", e_norm); - e_norm == d_norm - }; - - let error = |expected_line, extra_msg| { - let normalize_all = dumped_string - .lines() - .map(nocomment_mir_line) - .filter(|l| !l.is_empty()) - .collect::>() - .join("\n"); - let f = |l: &ExpectedLine<_>| match l { - &ExpectedLine::Elision => "... (elided)".into(), - &ExpectedLine::Text(t) => t, - }; - let expected_content = - expected_content.iter().map(|l| f(l)).collect::>().join("\n"); - panic!( - "Did not find expected line, error: {}\n\ - Expected Line: {:?}\n\ - Test Name: {}\n\ - Expected:\n{}\n\ - Actual:\n{}", - extra_msg, expected_line, test_name, expected_content, normalize_all - ); - }; - - // We expect each non-empty line to appear consecutively, non-consecutive lines - // must be separated by at least one Elision - let mut start_block_line = None; - while let Some(dumped_line) = dumped_lines.next() { - match expected_lines.next() { - Some(&ExpectedLine::Text(expected_line)) => { - let normalized_expected_line = normalize_mir_line(expected_line); - if normalized_expected_line.contains(":{") { - start_block_line = Some(expected_line); - } - - if !compare(expected_line, dumped_line) { - error!("{:?}", start_block_line); - error( - expected_line, - format!( - "Mismatch in lines\n\ - Current block: {}\n\ - Actual Line: {:?}", - start_block_line.unwrap_or("None"), - dumped_line - ), - ); - } - } - Some(&ExpectedLine::Elision) => { - // skip any number of elisions in a row. - while let Some(&&ExpectedLine::Elision) = expected_lines.peek() { - expected_lines.next(); - } - if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() { - let mut found = compare(expected_line, dumped_line); - if found { - continue; - } - while let Some(dumped_line) = dumped_lines.next() { - found = compare(expected_line, dumped_line); - if found { - break; - } - } - if !found { - error(expected_line, "ran out of mir dump to match against".into()); - } - } - } - None => {} - } - } - } - fn get_mir_dump_dir(&self) -> PathBuf { let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path()); debug!("input_file: {:?}", self.testpaths.file); @@ -3249,7 +3246,7 @@ impl<'test> TestCx<'test> { // with placeholders as we do not want tests needing updated when compiler source code // changes. // eg. $SRC_DIR/libcore/mem.rs:323:14 becomes $SRC_DIR/libcore/mem.rs:LL:COL - normalized = Regex::new("SRC_DIR(.+):\\d+:\\d+") + normalized = Regex::new("SRC_DIR(.+):\\d+:\\d+(: \\d+:\\d+)?") .unwrap() .replace_all(&normalized, "SRC_DIR$1:LL:COL") .into_owned(); @@ -3342,6 +3339,10 @@ impl<'test> TestCx<'test> { } fn delete_file(&self, file: &PathBuf) { + if !file.exists() { + // Deleting a nonexistant file would error. + return; + } if let Err(e) = fs::remove_file(file) { self.fatal(&format!("failed to delete `{}`: {}", file.display(), e,)); } @@ -3357,26 +3358,7 @@ impl<'test> TestCx<'test> { println!("normalized {}:\n{}\n", kind, actual); } else { println!("diff of {}:\n", kind); - let diff_results = make_diff(expected, actual, 3); - for result in diff_results { - let mut line_number = result.line_number; - for line in result.lines { - match line { - DiffLine::Expected(e) => { - println!("-\t{}", e); - line_number += 1; - } - DiffLine::Context(c) => { - println!("{}\t{}", line_number, c); - line_number += 1; - } - DiffLine::Resulting(r) => { - println!("+\t{}", r); - } - } - } - println!(); - } + print_diff(expected, actual, 3); } } @@ -3423,7 +3405,7 @@ impl<'test> TestCx<'test> { let examined_content = self.load_expected_output_from_path(&examined_path).unwrap_or_else(|_| String::new()); - if examined_path.exists() && canon_content == &examined_content { + if canon_content == &examined_content { self.delete_file(&examined_path); } } @@ -3495,43 +3477,11 @@ enum TargetLocation { ThisDirectory(PathBuf), } -#[derive(Clone, PartialEq, Eq)] -enum ExpectedLine> { - Elision, - Text(T), -} - enum AllowUnused { Yes, No, } -impl fmt::Debug for ExpectedLine -where - T: AsRef + fmt::Debug, -{ - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - if let &ExpectedLine::Text(ref t) = self { - write!(formatter, "{:?}", t) - } else { - write!(formatter, "\"...\" (Elision)") - } - } -} - -fn normalize_mir_line(line: &str) -> String { - nocomment_mir_line(line).replace(char::is_whitespace, "") -} - -fn nocomment_mir_line(line: &str) -> &str { - if let Some(idx) = line.find("//") { - let (l, _) = line.split_at(idx); - l.trim_end() - } else { - line - } -} - fn read2_abbreviated(mut child: Child) -> io::Result { use crate::read2::read2; use std::mem::replace; diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index 388ad75757f61..31c151d29e916 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -7,9 +7,9 @@ fn test_extract_gdb_version() { )*}}} test! { - 7000001: "GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)", + 7000001: "GNU gdb (GDB) CentOS 7.0.1-45.el5.centos", - 7002000: "GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)", + 7002000: "GNU gdb (GDB) Red Hat Enterprise Linux 7.2-90.el6", 7004000: "GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04", 7004001: "GNU gdb (GDB) 7.4.1-debian", diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 2663b3d160a7a..c61bee0f8d9ea 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -21,6 +21,7 @@ const OS_TABLE: &'static [(&'static str, &'static str)] = &[ ("fuchsia", "fuchsia"), ("haiku", "haiku"), ("hermit", "hermit"), + ("illumos", "illumos"), ("ios", "ios"), ("l4re", "l4re"), ("linux", "linux"), diff --git a/src/tools/expand-yaml-anchors/Cargo.toml b/src/tools/expand-yaml-anchors/Cargo.toml new file mode 100644 index 0000000000000..2c63e28b693da --- /dev/null +++ b/src/tools/expand-yaml-anchors/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "expand-yaml-anchors" +version = "0.1.0" +authors = ["Pietro Albini "] +edition = "2018" + +[dependencies] +yaml-rust = "0.4.3" +yaml-merge-keys = "0.4.0" diff --git a/src/tools/expand-yaml-anchors/src/main.rs b/src/tools/expand-yaml-anchors/src/main.rs new file mode 100644 index 0000000000000..f2ed8aa409a36 --- /dev/null +++ b/src/tools/expand-yaml-anchors/src/main.rs @@ -0,0 +1,202 @@ +use std::error::Error; +use std::path::{Path, PathBuf}; +use yaml_rust::{Yaml, YamlEmitter, YamlLoader}; + +/// List of directories containing files to expand. The first tuple element is the source +/// directory, while the second tuple element is the destination directory. +#[rustfmt::skip] +static TO_EXPAND: &[(&str, &str)] = &[ + ("src/ci/github-actions", ".github/workflows"), +]; + +/// Name of a special key that will be removed from all the maps in expanded configuration files. +/// This key can then be used to contain shared anchors. +static REMOVE_MAP_KEY: &str = "x--expand-yaml-anchors--remove"; + +/// Message that will be included at the top of all the expanded files. {source} will be replaced +/// with the source filename relative to the base path. +static HEADER_MESSAGE: &str = "\ +############################################################# +# WARNING: automatically generated file, DO NOT CHANGE! # +############################################################# + +# This file was automatically generated by the expand-yaml-anchors tool. The +# source file that generated this one is: +# +# {source} +# +# Once you make changes to that file you need to run: +# +# ./x.py run src/tools/expand-yaml-anchors/ +# +# The CI build will fail if the tool is not run after changes to this file. + +"; + +enum Mode { + Check, + Generate, +} + +struct App { + mode: Mode, + base: PathBuf, +} + +impl App { + fn from_args() -> Result> { + // Parse CLI arguments + let args = std::env::args().skip(1).collect::>(); + let (mode, base) = match args.iter().map(|s| s.as_str()).collect::>().as_slice() { + &["generate", ref base] => (Mode::Generate, PathBuf::from(base)), + &["check", ref base] => (Mode::Check, PathBuf::from(base)), + _ => { + eprintln!("usage: expand-yaml-anchors "); + std::process::exit(1); + } + }; + + Ok(App { mode, base }) + } + + fn run(&self) -> Result<(), Box> { + for (source, dest) in TO_EXPAND { + let source = self.base.join(source); + let dest = self.base.join(dest); + for entry in std::fs::read_dir(&source)? { + let path = entry?.path(); + if !path.is_file() || path.extension().and_then(|e| e.to_str()) != Some("yml") { + continue; + } + + let dest_path = dest.join(path.file_name().unwrap()); + self.expand(&path, &dest_path).with_context(|| match self.mode { + Mode::Generate => format!( + "failed to expand {} into {}", + self.path(&path), + self.path(&dest_path) + ), + Mode::Check => format!("{} is not up to date", self.path(&dest_path)), + })?; + } + } + Ok(()) + } + + fn expand(&self, source: &Path, dest: &Path) -> Result<(), Box> { + let content = std::fs::read_to_string(source) + .with_context(|| format!("failed to read {}", self.path(source)))?; + + let mut buf = HEADER_MESSAGE.replace("{source}", &self.path(source).to_string()); + + let documents = YamlLoader::load_from_str(&content) + .with_context(|| format!("failed to parse {}", self.path(source)))?; + for mut document in documents.into_iter() { + document = yaml_merge_keys::merge_keys(document) + .with_context(|| format!("failed to expand {}", self.path(source)))?; + document = filter_document(document); + + YamlEmitter::new(&mut buf).dump(&document).map_err(|err| WithContext { + context: "failed to serialize the expanded yaml".into(), + source: Box::new(err), + })?; + buf.push('\n'); + } + + match self.mode { + Mode::Check => { + let old = std::fs::read_to_string(dest) + .with_context(|| format!("failed to read {}", self.path(dest)))?; + if old != buf { + return Err(Box::new(StrError(format!( + "{} and {} are different", + self.path(source), + self.path(dest), + )))); + } + } + Mode::Generate => { + std::fs::write(dest, buf.as_bytes()) + .with_context(|| format!("failed to write to {}", self.path(dest)))?; + } + } + Ok(()) + } + + fn path<'a>(&self, path: &'a Path) -> impl std::fmt::Display + 'a { + path.strip_prefix(&self.base).unwrap_or(path).display() + } +} + +fn filter_document(document: Yaml) -> Yaml { + match document { + Yaml::Hash(map) => Yaml::Hash( + map.into_iter() + .filter(|(key, _)| { + if let Yaml::String(string) = &key { string != REMOVE_MAP_KEY } else { true } + }) + .map(|(key, value)| (filter_document(key), filter_document(value))) + .collect(), + ), + Yaml::Array(vec) => { + Yaml::Array(vec.into_iter().map(|item| filter_document(item)).collect()) + } + other => other, + } +} + +fn main() { + if let Err(err) = App::from_args().and_then(|app| app.run()) { + eprintln!("error: {}", err); + + let mut source = err.as_ref() as &dyn Error; + while let Some(err) = source.source() { + eprintln!("caused by: {}", err); + source = err; + } + + std::process::exit(1); + } +} + +#[derive(Debug)] +struct StrError(String); + +impl Error for StrError {} + +impl std::fmt::Display for StrError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } +} + +#[derive(Debug)] +struct WithContext { + context: String, + source: Box, +} + +impl std::fmt::Display for WithContext { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.context) + } +} + +impl Error for WithContext { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(self.source.as_ref()) + } +} + +pub(crate) trait ResultExt { + fn with_context String>(self, f: F) -> Result>; +} + +impl>> ResultExt for Result { + fn with_context String>(self, f: F) -> Result> { + match self { + Ok(ok) => Ok(ok), + Err(err) => Err(WithContext { source: err.into(), context: f() }.into()), + } + } +} diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index fb4611ed1ca4b..570ffd5d30622 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -114,7 +114,7 @@ fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) { } fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Option { - // Ignore none HTML files. + // Ignore non-HTML files. if file.extension().and_then(|s| s.to_str()) != Some("html") { return None; } diff --git a/src/tools/miri b/src/tools/miri index aaa16a5f4b8ca..10419b3f2fc62 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit aaa16a5f4b8caabf8e044e6dd1c48330dfb7900d +Subproject commit 10419b3f2fc625bb9d746c16d768e433a894484d diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index b389cd0373cc4..988a226706dc0 100755 --- a/src/tools/publish_toolstate.py +++ b/src/tools/publish_toolstate.py @@ -25,10 +25,6 @@ # read privileges on it). CI will fail otherwise. MAINTAINERS = { 'miri': {'oli-obk', 'RalfJung', 'eddyb'}, - 'clippy-driver': { - 'Manishearth', 'llogiq', 'mcarton', 'oli-obk', 'phansch', 'flip1995', - 'yaahc', - }, 'rls': {'Xanewok'}, 'rustfmt': {'topecongiro'}, 'book': {'carols10cents', 'steveklabnik'}, @@ -39,13 +35,12 @@ 'adamgreig', 'andre-richter', 'jamesmunns', 'korken89', 'ryankurte', 'thejpster', 'therealprof', }, - 'edition-guide': {'ehuss', 'Centril', 'steveklabnik'}, - 'rustc-guide': {'mark-i-m', 'spastorino', 'amanjeev', 'JohnTitor'}, + 'edition-guide': {'ehuss', 'steveklabnik'}, + 'rustc-dev-guide': {'mark-i-m', 'spastorino', 'amanjeev', 'JohnTitor'}, } REPOS = { 'miri': 'https://github.com/rust-lang/miri', - 'clippy-driver': 'https://github.com/rust-lang/rust-clippy', 'rls': 'https://github.com/rust-lang/rls', 'rustfmt': 'https://github.com/rust-lang/rustfmt', 'book': 'https://github.com/rust-lang/book', @@ -54,9 +49,16 @@ 'rust-by-example': 'https://github.com/rust-lang/rust-by-example', 'embedded-book': 'https://github.com/rust-embedded/book', 'edition-guide': 'https://github.com/rust-lang/edition-guide', - 'rustc-guide': 'https://github.com/rust-lang/rustc-guide', + 'rustc-dev-guide': 'https://github.com/rust-lang/rustc-dev-guide', } +def load_json_from_response(resp): + content = resp.read() + if isinstance(content, bytes): + content = content.decode('utf-8') + else: + print("Refusing to decode " + str(type(content)) + " to str") + return json.loads(content) def validate_maintainers(repo, github_token): '''Ensure all maintainers are assignable on a GitHub repo''' @@ -71,7 +73,7 @@ def validate_maintainers(repo, github_token): # Properly load nested teams. 'Accept': 'application/vnd.github.hellcat-preview+json', })) - assignable.extend(user['login'] for user in json.load(response)) + assignable.extend(user['login'] for user in load_json_from_response(response)) # Load the next page if available url = None link_header = response.headers.get('Link') @@ -153,7 +155,6 @@ def issue( )), 'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number), 'assignees': list(assignees), - 'labels': ['T-compiler', 'I-nominated'], }) print("Creating issue:\n{}".format(request)) response = urllib2.urlopen(urllib2.Request( @@ -177,7 +178,7 @@ def update_latest( ): '''Updates `_data/latest.json` to match build result of the given commit. ''' - with open('_data/latest.json', 'rb+') as f: + with open('_data/latest.json', 'r+') as f: latest = json.load(f, object_pairs_hook=collections.OrderedDict) current_status = { @@ -203,7 +204,7 @@ def update_latest( old = status[os] new = s.get(tool, old) status[os] = new - maintainers = ' '.join('@'+name for name in MAINTAINERS[tool]) + maintainers = ' '.join('@'+name for name in MAINTAINERS.get(tool, ())) # comparing the strings, but they are ordered appropriately: # "test-pass" > "test-fail" > "build-fail" if new > old: diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index d0ae8300bd6af..3379d82eda829 100644 --- a/src/tools/remote-test-client/src/main.rs +++ b/src/tools/remote-test-client/src/main.rs @@ -18,6 +18,7 @@ use std::thread; use std::time::Duration; const REMOTE_ADDR_ENV: &str = "TEST_DEVICE_ADDR"; +const DEFAULT_ADDR: &str = "127.0.0.1:12345"; macro_rules! t { ($e:expr) => { @@ -30,8 +31,12 @@ macro_rules! t { fn main() { let mut args = env::args().skip(1); + let next = args.next(); + if next.is_none() { + return help(); + } - match &args.next().unwrap()[..] { + match &next.unwrap()[..] { "spawn-emulator" => spawn_emulator( &args.next().unwrap(), Path::new(&args.next().unwrap()), @@ -40,12 +45,16 @@ fn main() { ), "push" => push(Path::new(&args.next().unwrap())), "run" => run(args.next().unwrap(), args.collect()), - cmd => panic!("unknown command: {}", cmd), + "help" | "-h" | "--help" => help(), + cmd => { + println!("unknown command: {}", cmd); + help(); + } } } fn spawn_emulator(target: &str, server: &Path, tmpdir: &Path, rootfs: Option) { - let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string()); + let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string()); if env::var(REMOTE_ADDR_ENV).is_ok() { println!("Connecting to remote device {} ...", device_address); @@ -172,7 +181,7 @@ fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path } fn push(path: &Path) { - let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string()); + let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string()); let client = t!(TcpStream::connect(device_address)); let mut client = BufWriter::new(client); t!(client.write_all(b"push")); @@ -189,7 +198,7 @@ fn push(path: &Path) { } fn run(files: String, args: Vec) { - let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string()); + let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string()); let client = t!(TcpStream::connect(device_address)); let mut client = BufWriter::new(client); t!(client.write_all(b"run ")); @@ -284,3 +293,40 @@ fn send(path: &Path, dst: &mut dyn Write) { t!(dst.write_all(&[(amt >> 24) as u8, (amt >> 16) as u8, (amt >> 8) as u8, (amt >> 0) as u8,])); t!(io::copy(&mut file, dst)); } + +fn help() { + println!( + " +Usage: {0} [] + +Sub-commands: + spawn-emulator [rootfs] See below + push Copy to emulator + run [args...] Run program on emulator + help Display help message + +Spawning an emulator: + +For Android s, adb will push the , set up TCP forwarding and run +the . Otherwise qemu emulates the target using a rootfs image created in + and generated from plus the executable. +If {1} is set in the environment, this step is skipped. + +Pushing a path to a running emulator: + +A running emulator or adb device is connected to at the IP address and port in +the {1} environment variable or {2} if this isn't +specified. The file at is sent to this target. + +Executing commands on a running emulator: + +First the target emulator/adb session is connected to as for pushing files. Next +the colon separated list of is pushed to the target. Finally, the first +file in is executed in the emulator, preserving the current environment. +That command's status code is returned. +", + env::args().next().unwrap(), + REMOTE_ADDR_ENV, + DEFAULT_ADDR + ); +} diff --git a/src/tools/rls b/src/tools/rls index 5fde462d8c53b..1cb7c09eb2454 160000 --- a/src/tools/rls +++ b/src/tools/rls @@ -1 +1 @@ -Subproject commit 5fde462d8c53b86840100a927a17c8353bba3e3f +Subproject commit 1cb7c09eb245454648bdecd61fa93bace3041b6d diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index e6e758dccdf0a..ff41197faa1a6 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -23,6 +23,6 @@ codespan-reporting = { version = "0.5", optional = true } rustc-workspace-hack = "1.0.0" [dependencies.mdbook] -version = "0.3.0" +version = "0.3.7" default-features = false features = ["search"] diff --git a/src/tools/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs index 01f324f0c5a0a..60bd0b72910a4 100644 --- a/src/tools/rustbook/src/main.rs +++ b/src/tools/rustbook/src/main.rs @@ -108,7 +108,9 @@ pub fn linkcheck( is_real_error = true; } Reason::UnsuccessfulServerResponse(status) => { - if status.is_client_error() { + if status.as_u16() == 429 { + eprintln!("Received 429 (TOO_MANY_REQUESTS) for link `{}`", link.link.uri); + } else if status.is_client_error() { is_real_error = true; } else { eprintln!("Unsuccessful server response for link `{}`", link.link.uri); diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 936e8ae895a74..cbf7d09f2e42c 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -17,6 +17,8 @@ path = "lib.rs" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" features = [ + "aclapi", + "accctrl", "basetsd", "consoleapi", "errhandlingapi", @@ -59,17 +61,19 @@ features = [ [dependencies] curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true } -crossbeam-utils = { version = "0.6.5", features = ["nightly"] } +crossbeam-utils = { version = "0.7.2", features = ["nightly"] } +proc-macro2 = { version = "1", features = ["default"] } +quote = { version = "1", features = ["default"] } serde = { version = "1.0.82", features = ['derive'] } serde_json = { version = "1.0.31", features = ["raw_value"] } smallvec-0_6 = { package = "smallvec", version = "0.6", features = ['union', 'may_dangle'] } smallvec = { version = "1.0", features = ['union', 'may_dangle'] } -url = { version = "2.0", features = ['serde'] } syn = { version = "0.15", features = ['full', 'extra-traits'] } +syn-1 = { package = "syn", version = "1", features = ['fold', 'full', 'extra-traits', 'visit'] } +url = { version = "2.0", features = ['serde'] } [target.'cfg(not(windows))'.dependencies] openssl = { version = "0.10.12", optional = true } - [features] all-static = ['openssl/vendored', 'curl-sys/static-curl', 'curl-sys/force-system-lib-on-osx'] diff --git a/src/tools/rustdoc-js-std/tester.js b/src/tools/rustdoc-js-std/tester.js deleted file mode 100644 index 08930ff122797..0000000000000 --- a/src/tools/rustdoc-js-std/tester.js +++ /dev/null @@ -1,342 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -function getNextStep(content, pos, stop) { - while (pos < content.length && content[pos] !== stop && - (content[pos] === ' ' || content[pos] === '\t' || content[pos] === '\n')) { - pos += 1; - } - if (pos >= content.length) { - return null; - } - if (content[pos] !== stop) { - return pos * -1; - } - return pos; -} - -// Stupid function extractor based on indent. Doesn't support block -// comments. If someone puts a ' or an " in a block comment this -// will blow up. Template strings are not tested and might also be -// broken. -function extractFunction(content, functionName) { - var indent = 0; - var splitter = "function " + functionName + "("; - - while (true) { - var start = content.indexOf(splitter); - if (start === -1) { - break; - } - var pos = start; - while (pos < content.length && content[pos] !== ')') { - pos += 1; - } - if (pos >= content.length) { - break; - } - pos = getNextStep(content, pos + 1, '{'); - if (pos === null) { - break; - } else if (pos < 0) { - content = content.slice(-pos); - continue; - } - while (pos < content.length) { - // Eat single-line comments - if (content[pos] === '/' && pos > 0 && content[pos-1] === '/') { - do { - pos += 1; - } while (pos < content.length && content[pos] !== '\n'); - - // Eat quoted strings - } else if (content[pos] === '"' || content[pos] === "'" || content[pos] === "`") { - var stop = content[pos]; - var is_escaped = false; - do { - if (content[pos] === '\\') { - pos += 2; - } else { - pos += 1; - } - } while (pos < content.length && - (content[pos] !== stop || content[pos - 1] === '\\')); - - // Otherwise, check for indent - } else if (content[pos] === '{') { - indent += 1; - } else if (content[pos] === '}') { - indent -= 1; - if (indent === 0) { - return content.slice(start, pos + 1); - } - } - pos += 1; - } - content = content.slice(start + 1); - } - return null; -} - -// Stupid function extractor for array. -function extractArrayVariable(content, arrayName) { - var splitter = "var " + arrayName; - while (true) { - var start = content.indexOf(splitter); - if (start === -1) { - break; - } - var pos = getNextStep(content, start, '='); - if (pos === null) { - break; - } else if (pos < 0) { - content = content.slice(-pos); - continue; - } - pos = getNextStep(content, pos, '['); - if (pos === null) { - break; - } else if (pos < 0) { - content = content.slice(-pos); - continue; - } - while (pos < content.length) { - if (content[pos] === '"' || content[pos] === "'") { - var stop = content[pos]; - do { - if (content[pos] === '\\') { - pos += 2; - } else { - pos += 1; - } - } while (pos < content.length && - (content[pos] !== stop || content[pos - 1] === '\\')); - } else if (content[pos] === ']' && - pos + 1 < content.length && - content[pos + 1] === ';') { - return content.slice(start, pos + 2); - } - pos += 1; - } - content = content.slice(start + 1); - } - return null; -} - -// Stupid function extractor for variable. -function extractVariable(content, varName) { - var splitter = "var " + varName; - while (true) { - var start = content.indexOf(splitter); - if (start === -1) { - break; - } - var pos = getNextStep(content, start, '='); - if (pos === null) { - break; - } else if (pos < 0) { - content = content.slice(-pos); - continue; - } - while (pos < content.length) { - if (content[pos] === '"' || content[pos] === "'") { - var stop = content[pos]; - do { - if (content[pos] === '\\') { - pos += 2; - } else { - pos += 1; - } - } while (pos < content.length && - (content[pos] !== stop || content[pos - 1] === '\\')); - } else if (content[pos] === ';') { - return content.slice(start, pos + 1); - } - pos += 1; - } - content = content.slice(start + 1); - } - return null; -} - -function loadContent(content) { - var Module = module.constructor; - var m = new Module(); - m._compile(content, "tmp.js"); - m.exports.ignore_order = content.indexOf("\n// ignore-order\n") !== -1 || - content.startsWith("// ignore-order\n"); - m.exports.exact_check = content.indexOf("\n// exact-check\n") !== -1 || - content.startsWith("// exact-check\n"); - m.exports.should_fail = content.indexOf("\n// should-fail\n") !== -1 || - content.startsWith("// should-fail\n"); - return m.exports; -} - -function readFile(filePath) { - return fs.readFileSync(filePath, 'utf8'); -} - -function loadThings(thingsToLoad, kindOfLoad, funcToCall, fileContent) { - var content = ''; - for (var i = 0; i < thingsToLoad.length; ++i) { - var tmp = funcToCall(fileContent, thingsToLoad[i]); - if (tmp === null) { - console.error('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"'); - process.exit(1); - } - content += tmp; - content += 'exports.' + thingsToLoad[i] + ' = ' + thingsToLoad[i] + ';'; - } - return content; -} - -function lookForEntry(entry, data) { - for (var i = 0; i < data.length; ++i) { - var allGood = true; - for (var key in entry) { - if (!entry.hasOwnProperty(key)) { - continue; - } - var value = data[i][key]; - // To make our life easier, if there is a "parent" type, we add it to the path. - if (key === 'path' && data[i]['parent'] !== undefined) { - if (value.length > 0) { - value += '::' + data[i]['parent']['name']; - } else { - value = data[i]['parent']['name']; - } - } - if (value !== entry[key]) { - allGood = false; - break; - } - } - if (allGood === true) { - return i; - } - } - return null; -} - -function findFile(dir, name, extension) { - var entries = fs.readdirSync(dir); - for (var i = 0; i < entries.length; ++i) { - var entry = entries[i]; - var file_type = fs.statSync(dir + entry); - if (file_type.isDirectory()) { - continue; - } - if (entry.startsWith(name) && entry.endsWith(extension)) { - return entry; - } - } - return null; -} - -function readFileMatching(dir, name, extension) { - if (dir.endsWith("/") === false) { - dir += "/"; - } - var f = findFile(dir, name, extension); - if (f === null) { - return ""; - } - return readFile(dir + f); -} - -function main(argv) { - if (argv.length !== 4) { - console.error("USAGE: node tester.js STD_DOCS TEST_FOLDER"); - return 1; - } - var std_docs = argv[2]; - var test_folder = argv[3]; - - var mainJs = readFileMatching(std_docs, "main", ".js"); - var ALIASES = readFileMatching(std_docs, "aliases", ".js"); - var searchIndex = readFileMatching(std_docs, "search-index", ".js").split("\n"); - if (searchIndex[searchIndex.length - 1].length === 0) { - searchIndex.pop(); - } - searchIndex.pop(); - searchIndex = loadContent(searchIndex.join("\n") + '\nexports.searchIndex = searchIndex;'); - finalJS = ""; - - var arraysToLoad = ["itemTypes"]; - var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", "NO_TYPE_FILTER", - "GENERICS_DATA", "NAME", "INPUTS_DATA", "OUTPUT_DATA", - "TY_PRIMITIVE", "TY_KEYWORD", - "levenshtein_row2"]; - // execQuery first parameter is built in getQuery (which takes in the search input). - // execQuery last parameter is built in buildIndex. - // buildIndex requires the hashmap from search-index. - var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult", - "getQuery", "buildIndex", "execQuery", "execSearch"]; - - finalJS += 'window = { "currentCrate": "std" };\n'; - finalJS += 'var rootPath = "../";\n'; - finalJS += ALIASES; - finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs); - finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs); - finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs); - - var loaded = loadContent(finalJS); - var index = loaded.buildIndex(searchIndex.searchIndex); - - var errors = 0; - - fs.readdirSync(test_folder).forEach(function(file) { - var loadedFile = loadContent(readFile(path.join(test_folder, file)) + - 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;'); - const expected = loadedFile.EXPECTED; - const query = loadedFile.QUERY; - const filter_crate = loadedFile.FILTER_CRATE; - const ignore_order = loadedFile.ignore_order; - const exact_check = loadedFile.exact_check; - const should_fail = loadedFile.should_fail; - var results = loaded.execSearch(loaded.getQuery(query), index); - process.stdout.write('Checking "' + file + '" ... '); - var error_text = []; - for (var key in expected) { - if (!expected.hasOwnProperty(key)) { - continue; - } - if (!results.hasOwnProperty(key)) { - error_text.push('==> Unknown key "' + key + '"'); - break; - } - var entry = expected[key]; - var prev_pos = -1; - for (var i = 0; i < entry.length; ++i) { - var entry_pos = lookForEntry(entry[i], results[key]); - if (entry_pos === null) { - error_text.push("==> Result not found in '" + key + "': '" + - JSON.stringify(entry[i]) + "'"); - } else if (exact_check === true && prev_pos + 1 !== entry_pos) { - error_text.push("==> Exact check failed at position " + (prev_pos + 1) + ": " + - "expected '" + JSON.stringify(entry[i]) + "' but found '" + - JSON.stringify(results[key][i]) + "'"); - } else if (ignore_order === false && entry_pos < prev_pos) { - error_text.push("==> '" + JSON.stringify(entry[i]) + "' was supposed to be " + - " before '" + JSON.stringify(results[key][entry_pos]) + "'"); - } else { - prev_pos = entry_pos; - } - } - } - if (error_text.length === 0 && should_fail === true) { - errors += 1; - console.error("FAILED"); - console.error("==> Test was supposed to fail but all items were found..."); - } else if (error_text.length !== 0 && should_fail === false) { - errors += 1; - console.error("FAILED"); - console.error(error_text.join("\n")); - } else { - console.log("OK"); - } - }); - return errors > 0 ? 1 : 0; -} - -process.exit(main(process.argv)); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 143e1a7480d3d..1fa46ce99f5e6 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -1,6 +1,5 @@ const fs = require('fs'); const path = require('path'); -const { spawnSync } = require('child_process'); function getNextStep(content, pos, stop) { while (pos < content.length && content[pos] !== stop && @@ -150,7 +149,7 @@ function extractVariable(content, varName) { } } while (pos < content.length && (content[pos] !== stop || content[pos - 1] === '\\')); - } else if (content[pos] === ';') { + } else if (content[pos] === ';' || content[pos] === ',') { return content.slice(start, pos + 1); } pos += 1; @@ -182,7 +181,7 @@ function loadThings(thingsToLoad, kindOfLoad, funcToCall, fileContent) { for (var i = 0; i < thingsToLoad.length; ++i) { var tmp = funcToCall(fileContent, thingsToLoad[i]); if (tmp === null) { - console.error('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"'); + console.log('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"'); process.exit(1); } content += tmp; @@ -219,16 +218,14 @@ function lookForEntry(entry, data) { return null; } -function load_files(out_folder, crate) { - var mainJs = readFile(out_folder + "/main.js"); - var ALIASES = readFile(out_folder + "/aliases.js"); - var searchIndex = readFile(out_folder + "/search-index.js").split("\n"); +function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) { if (searchIndex[searchIndex.length - 1].length === 0) { searchIndex.pop(); } searchIndex.pop(); - searchIndex = loadContent(searchIndex.join("\n") + '\nexports.searchIndex = searchIndex;'); - finalJS = ""; + var fullSearchIndex = searchIndex.join("\n") + '\nexports.rawSearchIndex = searchIndex;'; + searchIndex = loadContent(fullSearchIndex); + var finalJS = ""; var arraysToLoad = ["itemTypes"]; var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", "NO_TYPE_FILTER", @@ -239,95 +236,205 @@ function load_files(out_folder, crate) { // execQuery last parameter is built in buildIndex. // buildIndex requires the hashmap from search-index. var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult", - "getQuery", "buildIndex", "execQuery", "execSearch"]; + "handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch"]; + ALIASES = {}; finalJS += 'window = { "currentCrate": "' + crate + '" };\n'; finalJS += 'var rootPath = "../";\n'; - finalJS += ALIASES; + finalJS += loadThings(["onEach"], 'function', extractFunction, storageJs); finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs); finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs); finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs); var loaded = loadContent(finalJS); - return [loaded, loaded.buildIndex(searchIndex.searchIndex)]; + var index = loaded.buildIndex(searchIndex.rawSearchIndex); + + return [loaded, index]; } -function main(argv) { - if (argv.length < 4) { - console.error("USAGE: node tester.js OUT_FOLDER [TESTS]"); - return 1; +function runSearch(query, expected, index, loaded, loadedFile, queryName) { + const filter_crate = loadedFile.FILTER_CRATE; + const ignore_order = loadedFile.ignore_order; + const exact_check = loadedFile.exact_check; + + var results = loaded.execSearch(loaded.getQuery(query), index, filter_crate); + var error_text = []; + + for (var key in expected) { + if (!expected.hasOwnProperty(key)) { + continue; + } + if (!results.hasOwnProperty(key)) { + error_text.push('==> Unknown key "' + key + '"'); + break; + } + var entry = expected[key]; + var prev_pos = -1; + for (var i = 0; i < entry.length; ++i) { + var entry_pos = lookForEntry(entry[i], results[key]); + if (entry_pos === null) { + error_text.push(queryName + "==> Result not found in '" + key + "': '" + + JSON.stringify(entry[i]) + "'"); + } else if (exact_check === true && prev_pos + 1 !== entry_pos) { + error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) + + ": expected '" + JSON.stringify(entry[i]) + "' but found '" + + JSON.stringify(results[key][i]) + "'"); + } else if (ignore_order === false && entry_pos < prev_pos) { + error_text.push(queryName + "==> '" + JSON.stringify(entry[i]) + "' was supposed " + + "to be before '" + JSON.stringify(results[key][entry_pos]) + "'"); + } else { + prev_pos = entry_pos; + } + } } - if (argv[2].substr(-1) !== "/") { - argv[2] += "/"; + return error_text; +} + +function checkResult(error_text, loadedFile, displaySuccess) { + if (error_text.length === 0 && loadedFile.should_fail === true) { + console.log("FAILED"); + console.log("==> Test was supposed to fail but all items were found..."); + } else if (error_text.length !== 0 && loadedFile.should_fail === false) { + console.log("FAILED"); + console.log(error_text.join("\n")); + } else { + if (displaySuccess) { + console.log("OK"); + } + return 0; } - const out_folder = argv[2]; + return 1; +} - var errors = 0; +function runChecks(testFile, loaded, index) { + var loadedFile = loadContent( + readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;'); - for (var j = 3; j < argv.length; ++j) { - const test_file = argv[j]; - const test_name = path.basename(test_file, ".js"); + const expected = loadedFile.EXPECTED; + const query = loadedFile.QUERY; - process.stdout.write('Checking "' + test_name + '" ... '); - if (!fs.existsSync(test_file)) { - errors += 1; - console.error("FAILED"); - console.error("==> Missing '" + test_name + ".js' file..."); - continue; + if (Array.isArray(query)) { + if (!Array.isArray(expected)) { + console.log("FAILED"); + console.log("==> If QUERY variable is an array, EXPECTED should be an array too"); + return 1; + } else if (query.length !== expected.length) { + console.log("FAILED"); + console.log("==> QUERY variable should have the same length as EXPECTED"); + return 1; } - - const test_out_folder = out_folder + test_name; - - var [loaded, index] = load_files(test_out_folder, test_name); - var loadedFile = loadContent(readFile(test_file) + - 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;'); - const expected = loadedFile.EXPECTED; - const query = loadedFile.QUERY; - const filter_crate = loadedFile.FILTER_CRATE; - const ignore_order = loadedFile.ignore_order; - const exact_check = loadedFile.exact_check; - const should_fail = loadedFile.should_fail; - var results = loaded.execSearch(loaded.getQuery(query), index); - var error_text = []; - for (var key in expected) { - if (!expected.hasOwnProperty(key)) { - continue; - } - if (!results.hasOwnProperty(key)) { - error_text.push('==> Unknown key "' + key + '"'); - break; - } - var entry = expected[key]; - var prev_pos = -1; - for (var i = 0; i < entry.length; ++i) { - var entry_pos = lookForEntry(entry[i], results[key]); - if (entry_pos === null) { - error_text.push("==> Result not found in '" + key + "': '" + - JSON.stringify(entry[i]) + "'"); - } else if (exact_check === true && prev_pos + 1 !== entry_pos) { - error_text.push("==> Exact check failed at position " + (prev_pos + 1) + ": " + - "expected '" + JSON.stringify(entry[i]) + "' but found '" + - JSON.stringify(results[key][i]) + "'"); - } else if (ignore_order === false && entry_pos < prev_pos) { - error_text.push("==> '" + JSON.stringify(entry[i]) + "' was supposed to be " + - " before '" + JSON.stringify(results[key][entry_pos]) + "'"); - } else { - prev_pos = entry_pos; - } + for (var i = 0; i < query.length; ++i) { + var error_text = runSearch(query[i], expected[i], index, loaded, loadedFile, + "[ query `" + query[i] + "`]"); + if (checkResult(error_text, loadedFile, false) !== 0) { + return 1; } } - if (error_text.length === 0 && should_fail === true) { - errors += 1; - console.error("FAILED"); - console.error("==> Test was supposed to fail but all items were found..."); - } else if (error_text.length !== 0 && should_fail === false) { - errors += 1; - console.error("FAILED"); - console.error(error_text.join("\n")); + console.log("OK"); + return 0; + } + var error_text = runSearch(query, expected, index, loaded, loadedFile, ""); + return checkResult(error_text, loadedFile, true); +} + +function load_files(doc_folder, resource_suffix, crate) { + var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js")); + var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js")); + var searchIndex = readFile( + path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n"); + + return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate); +} + +function showHelp() { + console.log("rustdoc-js options:"); + console.log(" --doc-folder [PATH] : location of the generated doc folder"); + console.log(" --help : show this message then quit"); + console.log(" --crate-name [STRING] : crate name to be used"); + console.log(" --test-file [PATH] : location of the JS test file"); + console.log(" --test-folder [PATH] : location of the JS tests folder"); + console.log(" --resource-suffix [STRING] : suffix to refer to the correct files"); +} + +function parseOptions(args) { + var opts = { + "crate_name": "", + "resource_suffix": "", + "doc_folder": "", + "test_folder": "", + "test_file": "", + }; + var correspondances = { + "--resource-suffix": "resource_suffix", + "--doc-folder": "doc_folder", + "--test-folder": "test_folder", + "--test-file": "test_file", + "--crate-name": "crate_name", + }; + + for (var i = 0; i < args.length; ++i) { + if (args[i] === "--resource-suffix" + || args[i] === "--doc-folder" + || args[i] === "--test-folder" + || args[i] === "--test-file" + || args[i] === "--crate-name") { + i += 1; + if (i >= args.length) { + console.log("Missing argument after `" + args[i - 1] + "` option."); + return null; + } + opts[correspondances[args[i - 1]]] = args[i]; + } else if (args[i] === "--help") { + showHelp(); + process.exit(0); } else { - console.log("OK"); + console.log("Unknown option `" + args[i] + "`."); + console.log("Use `--help` to see the list of options"); + return null; } } + if (opts["doc_folder"].length < 1) { + console.log("Missing `--doc-folder` option."); + } else if (opts["crate_name"].length < 1) { + console.log("Missing `--crate-name` option."); + } else if (opts["test_folder"].length < 1 && opts["test_file"].length < 1) { + console.log("At least one of `--test-folder` or `--test-file` option is required."); + } else { + return opts; + } + return null; +} + +function checkFile(test_file, opts, loaded, index) { + const test_name = path.basename(test_file, ".js"); + + process.stdout.write('Checking "' + test_name + '" ... '); + return runChecks(test_file, loaded, index); +} + +function main(argv) { + var opts = parseOptions(argv.slice(2)); + if (opts === null) { + return 1; + } + + var [loaded, index] = load_files( + opts["doc_folder"], + opts["resource_suffix"], + opts["crate_name"]); + var errors = 0; + + if (opts["test_file"].length !== 0) { + errors += checkFile(opts["test_file"], opts, loaded, index); + } + if (opts["test_folder"].length !== 0) { + fs.readdirSync(opts["test_folder"]).forEach(function(file) { + if (!file.endsWith(".js")) { + return; + } + errors += checkFile(path.join(opts["test_folder"], file), opts, loaded, index); + }); + } return errors > 0 ? 1 : 0; } diff --git a/src/tools/rustfmt b/src/tools/rustfmt index 9f53665f91be1..a5cb5d26833cf 160000 --- a/src/tools/rustfmt +++ b/src/tools/rustfmt @@ -1 +1 @@ -Subproject commit 9f53665f91be16c9aa7afd83f7c79357fec9152b +Subproject commit a5cb5d26833cfda6fa2ed35735448953f728bd5e diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 1ffc415fb2497..d3c7b7a068bc3 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -26,7 +26,6 @@ const LICENSES: &[&str] = &[ const EXCEPTIONS: &[(&str, &str)] = &[ ("mdbook", "MPL-2.0"), // mdbook ("openssl", "Apache-2.0"), // cargo, mdbook - ("arrayref", "BSD-2-Clause"), // mdbook via handlebars via pest ("toml-query", "MPL-2.0"), // mdbook ("toml-query_derive", "MPL-2.0"), // mdbook ("is-match", "MPL-2.0"), // mdbook @@ -57,7 +56,7 @@ const EXCEPTIONS: &[(&str, &str)] = &[ const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"]; /// Which crates to check against the whitelist? -const WHITELIST_CRATES: &[&str] = &["rustc", "rustc_codegen_llvm"]; +const WHITELIST_CRATES: &[&str] = &["rustc_middle", "rustc_codegen_llvm"]; /// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible. /// @@ -74,10 +73,17 @@ const WHITELIST: &[&str] = &[ "backtrace", "backtrace-sys", "bitflags", + "block-buffer", + "block-padding", + "byte-tools", "byteorder", "c2-chacha", "cc", "cfg-if", + "chalk-derive", + "chalk-engine", + "chalk-ir", + "chalk-macros", "cloudabi", "cmake", "compiler_builtins", @@ -87,15 +93,18 @@ const WHITELIST: &[&str] = &[ "crossbeam-queue", "crossbeam-utils", "datafrog", + "digest", "dlmalloc", "either", "ena", "env_logger", + "fake-simd", "filetime", "flate2", "fortanix-sgx-abi", "fuchsia-zircon", "fuchsia-zircon-sys", + "generic-array", "getopts", "getrandom", "hashbrown", @@ -111,6 +120,7 @@ const WHITELIST: &[&str] = &[ "lock_api", "log", "log_settings", + "md-5", "measureme", "memchr", "memmap", @@ -118,12 +128,14 @@ const WHITELIST: &[&str] = &[ "miniz_oxide", "nodrop", "num_cpus", + "opaque-debug", "parking_lot", "parking_lot_core", "pkg-config", "polonius-engine", "ppv-lite86", "proc-macro2", + "psm", "punycode", "quick-error", "quote", @@ -150,8 +162,10 @@ const WHITELIST: &[&str] = &[ "semver-parser", "serde", "serde_derive", + "sha-1", "smallvec", "stable_deref_trait", + "stacker", "syn", "synstructure", "tempfile", @@ -159,6 +173,7 @@ const WHITELIST: &[&str] = &[ "termion", "termize", "thread_local", + "typenum", "ucd-util", "unicode-normalization", "unicode-script", @@ -350,7 +365,7 @@ fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) { // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times // under control. "cargo", - "rustc-ap-syntax", + "rustc-ap-rustc_ast", ]; for &name in FORBIDDEN_TO_HAVE_DUPLICATES { diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index 428c57d3ee822..f7fd0c670d704 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -15,21 +15,57 @@ const WHITELIST: &[&str] = &[ "E0727", "E0729", ]; +// Some error codes don't have any tests apparently... +const IGNORE_EXPLANATION_CHECK: &[&str] = + &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750"]; + fn check_error_code_explanation( f: &str, error_codes: &mut HashMap, err_code: String, -) { +) -> bool { + let mut invalid_compile_fail_format = false; + let mut found_error_code = false; + for line in f.lines() { let s = line.trim(); - if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') { - error_codes.insert(err_code, true); - return; + if s.starts_with("```") { + if s.contains("compile_fail") && s.contains('E') { + if !found_error_code { + error_codes.insert(err_code.clone(), true); + found_error_code = true; + } + } else if s.contains("compile-fail") { + invalid_compile_fail_format = true; + } } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") { - error_codes.get_mut(&err_code).map(|x| *x = true); - return; + if !found_error_code { + error_codes.get_mut(&err_code).map(|x| *x = true); + found_error_code = true; + } } } + invalid_compile_fail_format +} + +fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &String) -> bool { + let mut can_be_ignored = false; + + for line in f.lines() { + let s = line.trim(); + if s.starts_with("#### Note: this error code is no longer emitted by the compiler") { + return true; + } + if s.starts_with("```") { + if s.contains("compile_fail") && s.contains(err_code) { + return true; + } else if s.contains("(") { + // It's very likely that we can't actually make it fail compilation... + can_be_ignored = true; + } + } + } + can_be_ignored } macro_rules! some_or_continue { @@ -41,7 +77,12 @@ macro_rules! some_or_continue { }; } -fn extract_error_codes(f: &str, error_codes: &mut HashMap, path: &Path) { +fn extract_error_codes( + f: &str, + error_codes: &mut HashMap, + path: &Path, + errors: &mut Vec, +) { let mut reached_no_explanation = false; for line in f.lines() { @@ -55,10 +96,26 @@ fn extract_error_codes(f: &str, error_codes: &mut HashMap, path: & // Now we extract the tests from the markdown file! let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1)); let md_file_name = some_or_continue!(md.splitn(2, "\")").next()); - let path = some_or_continue!(path.parent()).join(md_file_name); + let path = some_or_continue!(path.parent()) + .join(md_file_name) + .canonicalize() + .expect("failed to canonicalize error explanation file path"); match read_to_string(&path) { Ok(content) => { - check_error_code_explanation(&content, error_codes, err_code); + if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) + && !check_if_error_code_is_test_in_explanation(&content, &err_code) + { + errors.push(format!( + "`{}` doesn't use its own error code in compile_fail example", + path.display(), + )); + } + if check_error_code_explanation(&content, error_codes, err_code) { + errors.push(format!( + "`{}` uses invalid tag `compile-fail` instead of `compile_fail`", + path.display(), + )); + } } Err(e) => { eprintln!("Couldn't read `{}`: {}", path.display(), e); @@ -94,22 +151,24 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap = HashMap::new(); super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| { let file_name = entry.file_name(); if file_name == "error_codes.rs" { - extract_error_codes(contents, &mut error_codes, entry.path()); + extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors); } else if entry.path().extension() == Some(OsStr::new("stderr")) { extract_error_codes_from_tests(contents, &mut error_codes); } }); - println!("Found {} error codes", error_codes.len()); + if errors.is_empty() { + println!("Found {} error codes", error_codes.len()); - let mut errors = Vec::new(); - for (err_code, nb) in &error_codes { - if !*nb && !WHITELIST.contains(&err_code.as_str()) { - errors.push(format!("Error code {} needs to have at least one UI test!", err_code)); + for (err_code, nb) in &error_codes { + if !*nb && !WHITELIST.contains(&err_code.as_str()) { + errors.push(format!("Error code {} needs to have at least one UI test!", err_code)); + } } } errors.sort(); diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index 839d914baa954..6f73b172feae3 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -1,9 +1,83 @@ +//! This implements the core logic of the compression scheme used to compactly +//! encode Unicode properties. +//! +//! We have two primary goals with the encoding: we want to be compact, because +//! these tables often end up in ~every Rust program (especially the +//! grapheme_extend table, used for str debugging), including those for embedded +//! targets (where space is important). We also want to be relatively fast, +//! though this is more of a nice to have rather than a key design constraint. +//! It is expected that libraries/applications which are performance-sensitive +//! to Unicode property lookups are extremely rare, and those that care may find +//! the tradeoff of the raw bitsets worth it. For most applications, a +//! relatively fast but much smaller (and as such less cache-impacting, etc.) +//! data set is likely preferable. +//! +//! We have two separate encoding schemes: a skiplist-like approach, and a +//! compressed bitset. The datasets we consider mostly use the skiplist (it's +//! smaller) but the lowercase and uppercase sets are sufficiently sparse for +//! the bitset to be worthwhile -- for those sets the biset is a 2x size win. +//! Since the bitset is also faster, this seems an obvious choice. (As a +//! historical note, the bitset was also the prior implementation, so its +//! relative complexity had already been paid). +//! +//! ## The bitset +//! +//! The primary idea is that we 'flatten' the Unicode ranges into an enormous +//! bitset. To represent any arbitrary codepoint in a raw bitset, we would need +//! over 17 kilobytes of data per character set -- way too much for our +//! purposes. +//! +//! First, the raw bitset (one bit for every valid `char`, from 0 to 0x10FFFF, +//! not skipping the small 'gap') is associated into words (u64) and +//! deduplicated. On random data, this would be useless; on our data, this is +//! incredibly beneficial -- our data sets have (far) less than 256 unique +//! words. +//! +//! This gives us an array that maps `u8 -> word`; the current algorithm does +//! not handle the case of more than 256 unique words, but we are relatively far +//! from coming that close. +//! +//! With that scheme, we now have a single byte for every 64 codepoints. +//! +//! We further chunk these by some constant N (between 1 and 64 per group, +//! dynamically chosen for smallest size), and again deduplicate and store in an +//! array (u8 -> [u8; N]). +//! +//! The bytes of this array map into the words from the bitset above, but we +//! apply another trick here: some of these words are similar enough that they +//! can be represented by some function of another word. The particular +//! functions chosen are rotation, inversion, and shifting (right). +//! +//! ## The skiplist +//! +//! The skip list arose out of the desire for an even smaller encoding than the +//! bitset -- and was the answer to the question "what is the smallest +//! representation we can imagine?". However, it is not necessarily the +//! smallest, and if you have a better proposal, please do suggest it! +//! +//! This is a relatively straightforward encoding. First, we break up all the +//! ranges in the input data into offsets from each other, essentially a gap +//! encoding. In practice, most gaps are small -- less than u8::MAX -- so we +//! store those directly. We make use of the larger gaps (which are nicely +//! interspersed already) throughout the dataset to index this data set. +//! +//! In particular, each run of small gaps (terminating in a large gap) is +//! indexed in a separate dataset. That data set stores an index into the +//! primary offset list and a prefix sum of that offset list. These are packed +//! into a single u32 (11 bits for the offset, 21 bits for the prefix sum). +//! +//! Lookup proceeds via a binary search in the index and then a straightforward +//! linear scan (adding up the offsets) until we reach the needle, and then the +//! index of that offset is utilized as the answer to whether we're in the set +//! or not. + use std::collections::{BTreeMap, HashMap}; use std::ops::Range; use ucd_parse::Codepoints; mod case_mapping; mod raw_emitter; +mod skiplist; mod unicode_download; use raw_emitter::{emit_codepoints, RawEmitter}; @@ -152,9 +226,17 @@ fn main() { std::process::exit(1); }); + // Optional test path, which is a Rust source file testing that the unicode + // property lookups are correct. + let test_path = std::env::args().nth(2); + let unicode_data = load_data(); let ranges_by_property = &unicode_data.ranges; + if let Some(path) = test_path { + std::fs::write(&path, generate_tests(&write_location, &ranges_by_property)).unwrap(); + } + let mut total_bytes = 0; let mut modules = Vec::new(); for (property, ranges) in ranges_by_property { @@ -163,7 +245,16 @@ fn main() { emit_codepoints(&mut emitter, &ranges); modules.push((property.to_lowercase().to_string(), emitter.file)); - println!("{:15}: {} bytes, {} codepoints", property, emitter.bytes_used, datapoints,); + println!( + "{:15}: {} bytes, {} codepoints in {} ranges ({} - {}) using {}", + property, + emitter.bytes_used, + datapoints, + ranges.len(), + ranges.first().unwrap().start, + ranges.last().unwrap().end, + emitter.desc, + ); total_bytes += emitter.bytes_used; } @@ -173,7 +264,10 @@ fn main() { "///! This file is generated by src/tools/unicode-table-generator; do not edit manually!\n", ); - table_file.push_str("use super::range_search;\n\n"); + // Include the range search function + table_file.push('\n'); + table_file.push_str(include_str!("range_search.rs")); + table_file.push('\n'); table_file.push_str(&version()); @@ -201,7 +295,7 @@ fn main() { fn version() -> String { let mut out = String::new(); - out.push_str("pub const UNICODE_VERSION: (u32, u32, u32) = "); + out.push_str("pub const UNICODE_VERSION: (u8, u8, u8) = "); let readme = std::fs::read_to_string(std::path::Path::new(UNICODE_DIRECTORY).join("ReadMe.txt")) @@ -236,21 +330,97 @@ fn fmt_list(values: impl IntoIterator) -> String { out } +fn generate_tests(data_path: &str, ranges: &[(&str, Vec>)]) -> String { + let mut s = String::new(); + s.push_str("#![allow(incomplete_features, unused)]\n"); + s.push_str("#![feature(const_generics)]\n\n"); + s.push_str("\n#[allow(unused)]\nuse std::hint;\n"); + s.push_str(&format!("#[path = \"{}\"]\n", data_path)); + s.push_str("mod unicode_data;\n\n"); + + s.push_str("\nfn main() {\n"); + + for (property, ranges) in ranges { + s.push_str(&format!(r#" println!("Testing {}");"#, property)); + s.push('\n'); + s.push_str(&format!(" {}_true();\n", property.to_lowercase())); + s.push_str(&format!(" {}_false();\n", property.to_lowercase())); + let mut is_true = Vec::new(); + let mut is_false = Vec::new(); + for ch_num in 0..(std::char::MAX as u32) { + if std::char::from_u32(ch_num).is_none() { + continue; + } + if ranges.iter().any(|r| r.contains(&ch_num)) { + is_true.push(ch_num); + } else { + is_false.push(ch_num); + } + } + + s.push_str(&format!(" fn {}_true() {{\n", property.to_lowercase())); + generate_asserts(&mut s, property, &is_true, true); + s.push_str(" }\n\n"); + s.push_str(&format!(" fn {}_false() {{\n", property.to_lowercase())); + generate_asserts(&mut s, property, &is_false, false); + s.push_str(" }\n\n"); + } + + s.push_str("}"); + s +} + +fn generate_asserts(s: &mut String, property: &str, points: &[u32], truthy: bool) { + for range in ranges_from_set(points) { + if range.end == range.start + 1 { + s.push_str(&format!( + " assert!({}unicode_data::{}::lookup({:?}), \"{}\");\n", + if truthy { "" } else { "!" }, + property.to_lowercase(), + std::char::from_u32(range.start).unwrap(), + range.start, + )); + } else { + s.push_str(&format!(" for chn in {:?}u32 {{\n", range)); + s.push_str(&format!( + " assert!({}unicode_data::{}::lookup(std::char::from_u32(chn).unwrap()), \"{{:?}}\", chn);\n", + if truthy { "" } else { "!" }, + property.to_lowercase(), + )); + s.push_str(" }\n"); + } + } +} + +fn ranges_from_set(set: &[u32]) -> Vec> { + let mut ranges = set.iter().map(|e| (*e)..(*e + 1)).collect::>>(); + merge_ranges(&mut ranges); + ranges +} + fn merge_ranges(ranges: &mut Vec>) { loop { let mut new_ranges = Vec::new(); let mut idx_iter = 0..(ranges.len() - 1); + let mut should_insert_last = true; while let Some(idx) = idx_iter.next() { let cur = ranges[idx].clone(); let next = ranges[idx + 1].clone(); if cur.end == next.start { - let _ = idx_iter.next(); // skip next as we're merging it in + if idx_iter.next().is_none() { + // We're merging the last element + should_insert_last = false; + } new_ranges.push(cur.start..next.end); } else { + // We're *not* merging the last element + should_insert_last = true; new_ranges.push(cur); } } - new_ranges.push(ranges.last().unwrap().clone()); + if should_insert_last { + new_ranges.push(ranges.last().unwrap().clone()); + } if new_ranges.len() == ranges.len() { *ranges = new_ranges; break; @@ -258,4 +428,12 @@ fn merge_ranges(ranges: &mut Vec>) { *ranges = new_ranges; } } + + let mut last_end = None; + for range in ranges { + if let Some(last) = last_end { + assert!(range.start > last, "{:?}", range); + } + last_end = Some(range.end); + } } diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs new file mode 100644 index 0000000000000..39b47ce703f37 --- /dev/null +++ b/src/tools/unicode-table-generator/src/range_search.rs @@ -0,0 +1,93 @@ +#[inline(always)] +fn bitset_search< + const N: usize, + const CHUNK_SIZE: usize, + const N1: usize, + const CANONICAL: usize, + const CANONICALIZED: usize, +>( + needle: u32, + chunk_idx_map: &[u8; N], + bitset_chunk_idx: &[[u8; CHUNK_SIZE]; N1], + bitset_canonical: &[u64; CANONICAL], + bitset_canonicalized: &[(u8, u8); CANONICALIZED], +) -> bool { + let bucket_idx = (needle / 64) as usize; + let chunk_map_idx = bucket_idx / CHUNK_SIZE; + let chunk_piece = bucket_idx % CHUNK_SIZE; + let chunk_idx = if let Some(&v) = chunk_idx_map.get(chunk_map_idx) { + v + } else { + return false; + }; + let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize; + let word = if let Some(word) = bitset_canonical.get(idx) { + *word + } else { + let (real_idx, mapping) = bitset_canonicalized[idx - bitset_canonical.len()]; + let mut word = bitset_canonical[real_idx as usize]; + let should_invert = mapping & (1 << 6) != 0; + if should_invert { + word = !word; + } + // Lower 6 bits + let quantity = mapping & ((1 << 6) - 1); + if mapping & (1 << 7) != 0 { + // shift + word >>= quantity as u64; + } else { + word = word.rotate_left(quantity as u32); + } + word + }; + (word & (1 << (needle % 64) as u64)) != 0 +} + +fn decode_prefix_sum(short_offset_run_header: u32) -> u32 { + short_offset_run_header & ((1 << 21) - 1) +} + +fn decode_length(short_offset_run_header: u32) -> usize { + (short_offset_run_header >> 21) as usize +} + +#[inline(always)] +fn skip_search( + needle: u32, + short_offset_runs: &[u32; 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 last_idx = + match short_offset_runs.binary_search_by_key(&(needle << 11), |header| header << 11) { + Ok(idx) => idx + 1, + Err(idx) => idx, + }; + + let mut offset_idx = decode_length(short_offset_runs[last_idx]); + let length = if let Some(next) = short_offset_runs.get(last_idx + 1) { + decode_length(*next) - 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); + + let total = needle - prev; + let mut prefix_sum = 0; + for _ in 0..(length - 1) { + let offset = offsets[offset_idx]; + prefix_sum += offset as u32; + if prefix_sum > total { + break; + } + offset_idx += 1; + } + offset_idx % 2 == 1 +} diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index 3e60ce13f9223..95b63aca1549b 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -1,55 +1,19 @@ -//! This implements the core logic of the compression scheme used to compactly -//! encode the Unicode character classes. -//! -//! The primary idea is that we 'flatten' the Unicode ranges into an enormous -//! bitset. To represent any arbitrary codepoint in a raw bitset, we would need -//! over 17 kilobytes of data per character set -- way too much for our -//! purposes. -//! -//! We have two primary goals with the encoding: we want to be compact, because -//! these tables often end up in ~every Rust program (especially the -//! grapheme_extend table, used for str debugging), including those for embedded -//! targets (where space is important). We also want to be relatively fast, -//! though this is more of a nice to have rather than a key design constraint. -//! In practice, due to modern processor design these two are closely related. -//! -//! The encoding scheme here compresses the bitset by first deduplicating the -//! "words" (64 bits on all platforms). In practice very few words are present -//! in most data sets. -//! -//! This gives us an array that maps `u8 -> word` (if we ever went beyond 256 -//! words, we could go to u16 -> word or have some dual compression scheme -//! mapping into two separate sets; currently this is not dealt with). -//! -//! With that scheme, we now have a single byte for every 64 codepoints. We -//! further group these by 16 (arbitrarily chosen), and again deduplicate and -//! store in an array (u8 -> [u8; 16]). -//! -//! The indices into this array represent ranges of 64*16 = 1024 codepoints. -//! -//! This already reduces the top-level array to at most 1,086 bytes, but in -//! practice we usually can encode in far fewer (the first couple Unicode planes -//! are dense). -//! -//! The last byte of this top-level array is pulled out to a separate static -//! and trailing zeros are dropped; this is simply because grapheme_extend and -//! case_ignorable have a single entry in the 896th entry, so this shrinks them -//! down considerably. - use crate::fmt_list; -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::convert::TryFrom; -use std::fmt::Write; +use std::fmt::{self, Write}; use std::ops::Range; +#[derive(Clone)] pub struct RawEmitter { pub file: String, + pub desc: String, pub bytes_used: usize, } impl RawEmitter { pub fn new() -> RawEmitter { - RawEmitter { file: String::new(), bytes_used: 0 } + RawEmitter { file: String::new(), bytes_used: 0, desc: String::new() } } fn blank_line(&mut self) { @@ -59,30 +23,100 @@ impl RawEmitter { writeln!(&mut self.file, "").unwrap(); } - fn emit_bitset(&mut self, words: &[u64]) { + fn emit_bitset(&mut self, ranges: &[Range]) { + let last_code_point = ranges.last().unwrap().end; + // bitset for every bit in the codepoint range + // + // + 2 to ensure an all zero word to use for padding + let mut buckets = vec![0u64; (last_code_point as usize / 64) + 2]; + for range in ranges { + for codepoint in range.clone() { + let bucket = codepoint as usize / 64; + let bit = codepoint as u64 % 64; + buckets[bucket] |= 1 << bit; + } + } + + let mut words = buckets; + // Ensure that there's a zero word in the dataset, used for padding and + // such. + words.push(0); let unique_words = words.iter().cloned().collect::>().into_iter().collect::>(); if unique_words.len() > u8::max_value() as usize { panic!("cannot pack {} into 8 bits", unique_words.len()); } + // needed for the chunk mapping to work + assert_eq!(unique_words[0], 0, "has a zero word"); + let canonicalized = Canonicalized::canonicalize(&unique_words); - let word_indices = unique_words - .iter() - .cloned() - .enumerate() - .map(|(idx, word)| (word, u8::try_from(idx).unwrap())) - .collect::>(); + let word_indices = canonicalized.unique_mapping.clone(); + let compressed_words = words.iter().map(|w| word_indices[w]).collect::>(); + + let mut best = None; + for length in 1..=64 { + let mut temp = self.clone(); + temp.emit_chunk_map(word_indices[&0], &compressed_words, length); + if let Some((_, size)) = best { + if temp.bytes_used < size { + best = Some((length, temp.bytes_used)); + } + } else { + best = Some((length, temp.bytes_used)); + } + } + self.emit_chunk_map(word_indices[&0], &compressed_words, best.unwrap().0); + + struct Bits(u64); + impl fmt::Debug for Bits { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0b{:064b}", self.0) + } + } + + writeln!( + &mut self.file, + "static BITSET_CANONICAL: [u64; {}] = [{}];", + canonicalized.canonical_words.len(), + fmt_list(canonicalized.canonical_words.iter().map(|v| Bits(*v))), + ) + .unwrap(); + self.bytes_used += 8 * canonicalized.canonical_words.len(); + writeln!( + &mut self.file, + "static BITSET_MAPPING: [(u8, u8); {}] = [{}];", + canonicalized.canonicalized_words.len(), + fmt_list(&canonicalized.canonicalized_words), + ) + .unwrap(); + // 8 bit index into shifted words, 7 bits for shift + optional flip + // We only need it for the words that we removed by applying a shift and + // flip to them. + self.bytes_used += 2 * canonicalized.canonicalized_words.len(); + + self.blank_line(); - let mut idx = words.iter().map(|w| word_indices[w]).collect::>(); - let chunk_length = 16; - for _ in 0..(chunk_length - (idx.len() % chunk_length)) { - assert_eq!(unique_words[0], 0, "first word is all zeros"); - // pad out bitset index with zero words so we have all chunks of 16 - idx.push(0); + writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " super::bitset_search(",).unwrap(); + writeln!(&mut self.file, " c as u32,").unwrap(); + writeln!(&mut self.file, " &BITSET_CHUNKS_MAP,").unwrap(); + writeln!(&mut self.file, " &BITSET_INDEX_CHUNKS,").unwrap(); + writeln!(&mut self.file, " &BITSET_CANONICAL,").unwrap(); + writeln!(&mut self.file, " &BITSET_MAPPING,").unwrap(); + writeln!(&mut self.file, " )").unwrap(); + writeln!(&mut self.file, "}}").unwrap(); + } + + fn emit_chunk_map(&mut self, zero_at: u8, compressed_words: &[u8], chunk_length: usize) { + let mut compressed_words = compressed_words.to_vec(); + for _ in 0..(chunk_length - (compressed_words.len() % chunk_length)) { + // pad out bitset index with zero words so we have all chunks of + // chunkchunk_length + compressed_words.push(zero_at); } let mut chunks = BTreeSet::new(); - for chunk in idx.chunks(chunk_length) { + for chunk in compressed_words.chunks(chunk_length) { chunks.insert(chunk); } let chunk_map = chunks @@ -92,23 +126,10 @@ impl RawEmitter { .map(|(idx, chunk)| (chunk, idx)) .collect::>(); let mut chunk_indices = Vec::new(); - for chunk in idx.chunks(chunk_length) { + for chunk in compressed_words.chunks(chunk_length) { chunk_indices.push(chunk_map[chunk]); } - writeln!( - &mut self.file, - "static BITSET_LAST_CHUNK_MAP: (u16, u8) = ({}, {});", - chunk_indices.len() - 1, - chunk_indices.pop().unwrap(), - ) - .unwrap(); - self.bytes_used += 3; - // Strip out the empty pieces, presuming our above pop() made us now - // have some trailing zeros. - assert_eq!(unique_words[0], 0, "first word is all zeros"); - while let Some(0) = chunk_indices.last() { - chunk_indices.pop(); - } + writeln!( &mut self.file, "static BITSET_CHUNKS_MAP: [u8; {}] = [{}];", @@ -119,52 +140,253 @@ impl RawEmitter { self.bytes_used += chunk_indices.len(); writeln!( &mut self.file, - "static BITSET_INDEX_CHUNKS: [[u8; 16]; {}] = [{}];", + "static BITSET_INDEX_CHUNKS: [[u8; {}]; {}] = [{}];", + chunk_length, chunks.len(), fmt_list(chunks.iter()), ) .unwrap(); - self.bytes_used += 16 * chunks.len(); - writeln!( - &mut self.file, - "static BITSET: [u64; {}] = [{}];", - unique_words.len(), - fmt_list(&unique_words), - ) - .unwrap(); - self.bytes_used += 8 * unique_words.len(); - } - - pub fn emit_lookup(&mut self) { - writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); - writeln!(&mut self.file, " super::range_search(",).unwrap(); - writeln!(&mut self.file, " c as u32,").unwrap(); - writeln!(&mut self.file, " &BITSET_CHUNKS_MAP,").unwrap(); - writeln!(&mut self.file, " BITSET_LAST_CHUNK_MAP,").unwrap(); - writeln!(&mut self.file, " &BITSET_INDEX_CHUNKS,").unwrap(); - writeln!(&mut self.file, " &BITSET,").unwrap(); - writeln!(&mut self.file, " )").unwrap(); - writeln!(&mut self.file, "}}").unwrap(); + self.bytes_used += chunk_length * chunks.len(); } } pub fn emit_codepoints(emitter: &mut RawEmitter, ranges: &[Range]) { emitter.blank_line(); - let last_code_point = ranges.last().unwrap().end; - // bitset for every bit in the codepoint range - // - // + 2 to ensure an all zero word to use for padding - let mut buckets = vec![0u64; (last_code_point as usize / 64) + 2]; - for range in ranges { - for codepoint in range.clone() { - let bucket = codepoint as usize / 64; - let bit = codepoint as u64 % 64; - buckets[bucket] |= 1 << bit; - } + let mut bitset = emitter.clone(); + bitset.emit_bitset(&ranges); + + let mut skiplist = emitter.clone(); + skiplist.emit_skiplist(&ranges); + + if bitset.bytes_used <= skiplist.bytes_used { + *emitter = bitset; + emitter.desc = format!("bitset"); + } else { + *emitter = skiplist; + emitter.desc = format!("skiplist"); } +} - emitter.emit_bitset(&buckets); - emitter.blank_line(); - emitter.emit_lookup(); +struct Canonicalized { + canonical_words: Vec, + canonicalized_words: Vec<(u8, u8)>, + + /// Maps an input unique word to the associated index (u8) which is into + /// canonical_words or canonicalized_words (in order). + unique_mapping: HashMap, +} + +impl Canonicalized { + fn canonicalize(unique_words: &[u64]) -> Self { + #[derive(Copy, Clone, Debug)] + enum Mapping { + Rotate(u32), + Invert, + RotateAndInvert(u32), + ShiftRight(u32), + } + + // key is the word being mapped to + let mut mappings: BTreeMap> = BTreeMap::new(); + for &a in unique_words { + 'b: for &b in unique_words { + // skip self + if a == b { + continue; + } + + // All possible distinct rotations + for rotation in 1..64 { + if a.rotate_right(rotation) == b { + mappings.entry(b).or_default().push((a, Mapping::Rotate(rotation))); + // We're not interested in further mappings between a and b + continue 'b; + } + } + + if (!a) == b { + mappings.entry(b).or_default().push((a, Mapping::Invert)); + // We're not interested in further mappings between a and b + continue 'b; + } + + // All possible distinct rotations, inverted + for rotation in 1..64 { + if (!a.rotate_right(rotation)) == b { + mappings + .entry(b) + .or_default() + .push((a, Mapping::RotateAndInvert(rotation))); + // We're not interested in further mappings between a and b + continue 'b; + } + } + + // All possible shifts + for shift_by in 1..64 { + if a == (b >> shift_by) { + mappings + .entry(b) + .or_default() + .push((a, Mapping::ShiftRight(shift_by as u32))); + // We're not interested in further mappings between a and b + continue 'b; + } + } + } + } + // These are the bitset words which will be represented "raw" (as a u64) + let mut canonical_words = Vec::new(); + // These are mapped words, which will be represented by an index into + // the canonical_words and a Mapping; u16 when encoded. + let mut canonicalized_words = Vec::new(); + let mut unique_mapping = HashMap::new(); + + #[derive(Debug, PartialEq, Eq)] + enum UniqueMapping { + Canonical(usize), + Canonicalized(usize), + } + + // Map 0 first, so that it is the first canonical word. + // This is realistically not inefficient because 0 is not mapped to by + // anything else (a shift pattern could do it, but would be wasteful). + // + // However, 0s are quite common in the overall dataset, and it is quite + // wasteful to have to go through a mapping function to determine that + // we have a zero. + // + // FIXME: Experiment with choosing most common words in overall data set + // for canonical when possible. + while let Some((&to, _)) = mappings + .iter() + .find(|(&to, _)| to == 0) + .or_else(|| mappings.iter().max_by_key(|m| m.1.len())) + { + // Get the mapping with the most entries. Currently, no mapping can + // only exist transitively (i.e., there is no A, B, C such that A + // does not map to C and but A maps to B maps to C), so this is + // guaranteed to be acceptable. + // + // In the future, we may need a more sophisticated algorithm to + // identify which keys to prefer as canonical. + let mapped_from = mappings.remove(&to).unwrap(); + for (from, how) in &mapped_from { + // Remove the entries which mapped to this one. + // Noting that it should be associated with the Nth canonical word. + // + // We do not assert that this is present, because there may be + // no mappings to the `from` word; that's fine. + mappings.remove(from); + assert_eq!( + unique_mapping + .insert(*from, UniqueMapping::Canonicalized(canonicalized_words.len())), + None + ); + canonicalized_words.push((canonical_words.len(), *how)); + + // Remove the now-canonicalized word from other mappings, + // to ensure that we deprioritize them in the next iteration of + // the while loop. + for (_, mapped) in &mut mappings { + let mut i = 0; + while i != mapped.len() { + if mapped[i].0 == *from { + mapped.remove(i); + } else { + i += 1; + } + } + } + } + assert!( + unique_mapping + .insert(to, UniqueMapping::Canonical(canonical_words.len())) + .is_none() + ); + canonical_words.push(to); + + // Remove the now-canonical word from other mappings, to ensure that + // we deprioritize them in the next iteration of the while loop. + for (_, mapped) in &mut mappings { + let mut i = 0; + while i != mapped.len() { + if mapped[i].0 == to { + mapped.remove(i); + } else { + i += 1; + } + } + } + } + + // Any words which we couldn't shrink, just stick into the canonical + // words. + // + // FIXME: work harder -- there are more possibilities for mapping + // functions (e.g., multiplication, shifting instead of rotation, etc.) + // We'll probably always have some slack though so this loop will still + // be needed. + for &w in unique_words { + if !unique_mapping.contains_key(&w) { + assert!( + unique_mapping + .insert(w, UniqueMapping::Canonical(canonical_words.len())) + .is_none() + ); + canonical_words.push(w); + } + } + assert_eq!(canonicalized_words.len() + canonical_words.len(), unique_words.len()); + assert_eq!(unique_mapping.len(), unique_words.len()); + + let unique_mapping = unique_mapping + .into_iter() + .map(|(key, value)| { + ( + key, + match value { + UniqueMapping::Canonicalized(idx) => { + u8::try_from(canonical_words.len() + idx).unwrap() + } + UniqueMapping::Canonical(idx) => u8::try_from(idx).unwrap(), + }, + ) + }) + .collect::>(); + + let mut distinct_indices = BTreeSet::new(); + for &w in unique_words { + let idx = unique_mapping.get(&w).unwrap(); + assert!(distinct_indices.insert(idx)); + } + + const LOWER_6: u32 = (1 << 6) - 1; + + let canonicalized_words = canonicalized_words + .into_iter() + .map(|v| { + ( + u8::try_from(v.0).unwrap(), + match v.1 { + Mapping::RotateAndInvert(amount) => { + assert_eq!(amount, amount & LOWER_6); + 1 << 6 | (amount as u8) + } + Mapping::Rotate(amount) => { + assert_eq!(amount, amount & LOWER_6); + amount as u8 + } + Mapping::Invert => 1 << 6, + Mapping::ShiftRight(shift_by) => { + assert_eq!(shift_by, shift_by & LOWER_6); + 1 << 7 | (shift_by as u8) + } + }, + ) + }) + .collect::>(); + Canonicalized { unique_mapping, canonical_words, canonicalized_words } + } } diff --git a/src/tools/unicode-table-generator/src/skiplist.rs b/src/tools/unicode-table-generator/src/skiplist.rs new file mode 100644 index 0000000000000..6e439968c3bfd --- /dev/null +++ b/src/tools/unicode-table-generator/src/skiplist.rs @@ -0,0 +1,98 @@ +use crate::fmt_list; +use crate::raw_emitter::RawEmitter; +use std::convert::TryInto; +use std::fmt::Write as _; +use std::ops::Range; + +/// This will get packed into a single u32 before inserting into the data set. +#[derive(Debug, PartialEq)] +struct ShortOffsetRunHeader { + /// Note, we only allow for 21 bits here. + prefix_sum: u32, + + /// Note, we actually only allow for 11 bits here. This should be enough -- + /// our largest sets are around ~1400 offsets long. + start_idx: u16, +} + +impl ShortOffsetRunHeader { + fn pack(&self) -> u32 { + assert!(self.start_idx < (1 << 11)); + assert!(self.prefix_sum < (1 << 21)); + + (self.start_idx as u32) << 21 | self.prefix_sum + } +} + +impl RawEmitter { + pub fn emit_skiplist(&mut self, ranges: &[Range]) { + let mut offsets = Vec::::new(); + let points = ranges.iter().flat_map(|r| vec![r.start, r.end]).collect::>(); + let mut offset = 0; + for pt in points { + let delta = pt - offset; + offsets.push(delta); + offset = pt; + } + // Guaranteed to terminate, as it's impossible to subtract a value this + // large from a valid char. + offsets.push(std::char::MAX as u32 + 1); + let mut coded_offsets: Vec = Vec::new(); + let mut short_offset_runs: Vec = vec![]; + let mut iter = offsets.iter().cloned(); + let mut prefix_sum = 0; + loop { + let mut any_elements = false; + let mut inserted = false; + let start = coded_offsets.len(); + for offset in iter.by_ref() { + any_elements = true; + prefix_sum += offset; + if let Ok(offset) = offset.try_into() { + coded_offsets.push(offset); + } else { + short_offset_runs.push(ShortOffsetRunHeader { + start_idx: start.try_into().unwrap(), + prefix_sum, + }); + // This is just needed to maintain indices even/odd + // correctly. + coded_offsets.push(0); + inserted = true; + break; + } + } + if !any_elements { + break; + } + // We always append the huge char::MAX offset to the end which + // should never be able to fit into the u8 offsets. + assert!(inserted); + } + + writeln!( + &mut self.file, + "static SHORT_OFFSET_RUNS: [u32; {}] = [{}];", + short_offset_runs.len(), + fmt_list(short_offset_runs.iter().map(|v| v.pack())) + ) + .unwrap(); + self.bytes_used += 4 * short_offset_runs.len(); + writeln!( + &mut self.file, + "static OFFSETS: [u8; {}] = [{}];", + coded_offsets.len(), + fmt_list(&coded_offsets) + ) + .unwrap(); + self.bytes_used += coded_offsets.len(); + + writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " super::skip_search(",).unwrap(); + writeln!(&mut self.file, " c as u32,").unwrap(); + writeln!(&mut self.file, " &SHORT_OFFSET_RUNS,").unwrap(); + writeln!(&mut self.file, " &OFFSETS,").unwrap(); + writeln!(&mut self.file, " )").unwrap(); + writeln!(&mut self.file, "}}").unwrap(); + } +} diff --git a/src/tools/unicode-table-generator/src/unicode_download.rs b/src/tools/unicode-table-generator/src/unicode_download.rs index 3f6de9ea3bbd7..fa57f650ac082 100644 --- a/src/tools/unicode-table-generator/src/unicode_download.rs +++ b/src/tools/unicode-table-generator/src/unicode_download.rs @@ -11,10 +11,15 @@ static RESOURCES: &[&str] = pub fn fetch_latest() { let directory = Path::new(UNICODE_DIRECTORY); + if directory.exists() { + eprintln!( + "Not refetching unicode data, already exists, please delete {:?} to regenerate", + directory + ); + return; + } if let Err(e) = std::fs::create_dir_all(directory) { - if e.kind() != std::io::ErrorKind::AlreadyExists { - panic!("Failed to create {:?}: {}", UNICODE_DIRECTORY, e); - } + panic!("Failed to create {:?}: {}", UNICODE_DIRECTORY, e); } let output = Command::new("curl").arg(URL_PREFIX.to_owned() + README).output().unwrap(); if !output.status.success() { diff --git a/triagebot.toml b/triagebot.toml index 2476f414e0e03..2210a8ff8e656 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -33,3 +33,9 @@ Thanks! <3 [instructions]: https://rustc-dev-guide.rust-lang.org/ice-breaker/cleanup-crew.html """ label = "ICEBreaker-Cleanup-Crew" + +[prioritize] +label = "I-prioritize" +prioritize_on = ["regression-from-stable-to-stable", "regression-from-stable-to-beta", "regression-from-stable-to-nightly"] +priority_labels = "P-*" +zulip_stream = 227806

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + I: DoubleEndedIterator, + { + unchecked!(self).rfind(predicate) + } + + #[inline] + fn len(&self) -> usize + where + I: ExactSizeIterator, + { + unchecked!(self).len() + } + + #[inline] + fn is_empty(&self) -> bool + where + I: ExactSizeIterator, + { + unchecked!(self).is_empty() + } +} diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs index 6759a6b2d7349..195847ee98dc4 100644 --- a/src/libcore/iter/adapters/mod.rs +++ b/src/libcore/iter/adapters/mod.rs @@ -2,18 +2,19 @@ use crate::cmp; use crate::fmt; use crate::intrinsics; use crate::ops::{Add, AddAssign, Try}; -use crate::usize; use super::{from_fn, LoopState}; use super::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedLen}; mod chain; mod flatten; +mod fuse; mod zip; pub use self::chain::Chain; #[stable(feature = "rust1", since = "1.0.0")] pub use self::flatten::{FlatMap, Flatten}; +pub use self::fuse::Fuse; pub(crate) use self::zip::TrustedRandomAccess; pub use self::zip::Zip; @@ -511,6 +512,9 @@ where acc = self.iter.try_fold(acc, &mut f)?; } } + + // No `fold` override, because `fold` doesn't make much sense for `Cycle`, + // and we can't do anything better than the default. } #[stable(feature = "fused", since = "1.26.0")] @@ -642,6 +646,25 @@ where } from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f) } + + fn fold(mut self, mut acc: Acc, mut f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn nth(iter: &mut I, step: usize) -> impl FnMut() -> Option + '_ { + move || iter.nth(step) + } + + if self.first_take { + self.first_take = false; + match self.iter.next() { + None => return acc, + Some(x) => acc = f(acc, x), + } + } + from_fn(nth(&mut self.iter, self.step)).fold(acc, f) + } } impl StepBy @@ -701,6 +724,29 @@ where } } } + + #[inline] + fn rfold(mut self, init: Acc, mut f: F) -> Acc + where + Self: Sized, + F: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn nth_back( + iter: &mut I, + step: usize, + ) -> impl FnMut() -> Option + '_ { + move || iter.nth_back(step) + } + + match self.next_back() { + None => init, + Some(x) => { + let acc = f(init, x); + from_fn(nth_back(&mut self.iter, self.step)).fold(acc, f) + } + } + } } // StepBy can only make the iterator shorter, so the len will still fit. @@ -1766,6 +1812,20 @@ where self.iter.try_fold(init, check(flag, p, fold)).into_try() } } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } } #[stable(feature = "fused", since = "1.26.0")] @@ -1837,6 +1897,20 @@ where }) .into_try() } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } } /// An iterator that skips over `n` elements of `iter`. @@ -2005,6 +2079,18 @@ where self.iter.try_rfold(init, check(n, fold)).into_try() } } + + fn rfold(mut self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(Acc, T) -> Acc) -> impl FnMut(Acc, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_rfold(init, ok(fold)).unwrap() + } } #[stable(feature = "fused", since = "1.26.0")] @@ -2104,6 +2190,20 @@ where self.iter.try_fold(init, check(n, fold)).into_try() } } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } } #[stable(feature = "double_ended_take_iterator", since = "1.38.0")] @@ -2155,6 +2255,24 @@ where } } } + + #[inline] + fn rfold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.n == 0 { + init + } else { + let len = self.iter.len(); + if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() { + init + } else { + self.iter.rfold(init, fold) + } + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2236,260 +2354,19 @@ where let f = &mut self.f; self.iter.try_fold(init, scan(state, f, fold)).into_try() } -} - -/// An iterator that yields `None` forever after the underlying iterator -/// yields `None` once. -/// -/// This `struct` is created by the [`fuse`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`fuse`]: trait.Iterator.html#method.fuse -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Fuse { - iter: I, - done: bool, -} -impl Fuse { - pub(super) fn new(iter: I) -> Fuse { - Fuse { iter, done: false } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Fuse where I: Iterator {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Fuse -where - I: Iterator, -{ - type Item = ::Item; - - #[inline] - default fn next(&mut self) -> Option<::Item> { - if self.done { - None - } else { - let next = self.iter.next(); - self.done = next.is_none(); - next - } - } #[inline] - default fn nth(&mut self, n: usize) -> Option { - if self.done { - None - } else { - let nth = self.iter.nth(n); - self.done = nth.is_none(); - nth - } - } - - #[inline] - default fn last(self) -> Option { - if self.done { None } else { self.iter.last() } - } - - #[inline] - default fn count(self) -> usize { - if self.done { 0 } else { self.iter.count() } - } - - #[inline] - default fn size_hint(&self) -> (usize, Option) { - if self.done { (0, Some(0)) } else { self.iter.size_hint() } - } - - #[inline] - default fn try_fold(&mut self, init: Acc, fold: Fold) -> R + fn fold(mut self, init: Acc, fold: Fold) -> Acc where Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - if self.done { - Try::from_ok(init) - } else { - let acc = self.iter.try_fold(init, fold)?; - self.done = true; - Try::from_ok(acc) - } - } - - #[inline] - default fn fold(self, init: Acc, fold: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, { - if self.done { init } else { self.iter.fold(init, fold) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Fuse -where - I: DoubleEndedIterator, -{ - #[inline] - default fn next_back(&mut self) -> Option<::Item> { - if self.done { - None - } else { - let next = self.iter.next_back(); - self.done = next.is_none(); - next - } - } - - #[inline] - default fn nth_back(&mut self, n: usize) -> Option<::Item> { - if self.done { - None - } else { - let nth = self.iter.nth_back(n); - self.done = nth.is_none(); - nth - } - } - - #[inline] - default fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - if self.done { - Try::from_ok(init) - } else { - let acc = self.iter.try_rfold(init, fold)?; - self.done = true; - Try::from_ok(acc) + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) } - } - - #[inline] - default fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - if self.done { init } else { self.iter.rfold(init, fold) } - } -} - -unsafe impl TrustedRandomAccess for Fuse -where - I: TrustedRandomAccess, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> I::Item { - self.iter.get_unchecked(i) - } - - fn may_have_side_effect() -> bool { - I::may_have_side_effect() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl Iterator for Fuse -where - I: FusedIterator, -{ - #[inline] - fn next(&mut self) -> Option<::Item> { - self.iter.next() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.iter.nth(n) - } - - #[inline] - fn last(self) -> Option { - self.iter.last() - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_fold(init, fold) - } - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(init, fold) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl DoubleEndedIterator for Fuse -where - I: DoubleEndedIterator + FusedIterator, -{ - #[inline] - fn next_back(&mut self) -> Option<::Item> { - self.iter.next_back() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option<::Item> { - self.iter.nth_back(n) - } - - #[inline] - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_rfold(init, fold) - } - - #[inline] - fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.rfold(init, fold) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Fuse -where - I: ExactSizeIterator, -{ - fn len(&self) -> usize { - self.iter.len() - } - - fn is_empty(&self) -> bool { - self.iter.is_empty() + self.try_fold(init, ok(fold)).unwrap() } } @@ -2698,4 +2575,17 @@ where }) .into_try() } + + fn fold(mut self, init: B, fold: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } } diff --git a/src/libcore/iter/adapters/zip.rs b/src/libcore/iter/adapters/zip.rs index b13e12e2e8608..e83d36a580f06 100644 --- a/src/libcore/iter/adapters/zip.rs +++ b/src/libcore/iter/adapters/zip.rs @@ -1,5 +1,3 @@ -// ignore-tidy-undocumented-unsafe - use crate::cmp; use super::super::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedLen}; @@ -176,9 +174,11 @@ where if self.index < self.len { let i = self.index; self.index += 1; + // SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()` unsafe { Some((self.a.get_unchecked(i), self.b.get_unchecked(i))) } } else if A::may_have_side_effect() && self.index < self.a.len() { // match the base implementation's potential side effects + // SAFETY: we just checked that `self.index` < `self.a.len()` unsafe { self.a.get_unchecked(self.index); } @@ -203,11 +203,15 @@ where let i = self.index; self.index += 1; if A::may_have_side_effect() { + // SAFETY: the usage of `cmp::min` to calculate `delta` + // ensures that `end` is smaller than or equal to `self.len`, + // so `i` is also smaller than `self.len`. unsafe { self.a.get_unchecked(i); } } if B::may_have_side_effect() { + // SAFETY: same as above. unsafe { self.b.get_unchecked(i); } @@ -243,6 +247,8 @@ where if self.index < self.len { self.len -= 1; let i = self.len; + // SAFETY: `i` is smaller than the previous value of `self.len`, + // which is also smaller than or equal to `self.a.len()` and `self.b.len()` unsafe { Some((self.a.get_unchecked(i), self.b.get_unchecked(i))) } } else { None diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 28fbd00f36b33..388a5548a31a5 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -1,51 +1,185 @@ use crate::convert::TryFrom; use crate::mem; use crate::ops::{self, Add, Sub, Try}; -use crate::usize; use super::{FusedIterator, TrustedLen}; -/// Objects that can be stepped over in both directions. +/// Objects that have a notion of *successor* and *predecessor* operations. /// -/// The `steps_between` function provides a way to efficiently compare -/// two `Step` objects. -#[unstable( - feature = "step_trait", - reason = "likely to be replaced by finer-grained traits", - issue = "42168" -)] -pub trait Step: Clone + PartialOrd + Sized { - /// Returns the number of steps between two step objects. The count is - /// inclusive of `start` and exclusive of `end`. - /// - /// Returns `None` if it is not possible to calculate `steps_between` - /// without overflow. +/// The *successor* operation moves towards values that compare greater. +/// The *predecessor* operation moves towards values that compare lesser. +/// +/// # Safety +/// +/// This trait is `unsafe` because its implementation must be correct for +/// the safety of `unsafe trait TrustedLen` implementations, and the results +/// of using this trait can otherwise be trusted by `unsafe` code to be correct +/// and fulfill the listed obligations. +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +pub unsafe trait Step: Clone + PartialOrd + Sized { + /// Returns the number of *successor* steps required to get from `start` to `end`. + /// + /// Returns `None` if the number of steps would overflow `usize` + /// (or is infinite, or if `end` would never be reached). + /// + /// # Invariants + /// + /// For any `a`, `b`, and `n`: + /// + /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward_checked(&a, n) == Some(b)` + /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&a, n) == Some(a)` + /// * `steps_between(&a, &b) == Some(n)` only if `a <= b` + /// * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b` + /// * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`; + /// this is the case wheen it would require more than `usize::MAX` steps to get to `b` + /// * `steps_between(&a, &b) == None` if `a > b` fn steps_between(start: &Self, end: &Self) -> Option; - /// Replaces this step with `1`, returning a clone of itself. + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, returns `None`. + /// + /// # Invariants /// - /// The output of this method should always be greater than the output of replace_zero. - fn replace_one(&mut self) -> Self; + /// For any `a`, `n`, and `m`: + /// + /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))` + /// + /// For any `a`, `n`, and `m` where `n + m` does not overflow: + /// + /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + m)` + /// + /// For any `a` and `n`: + /// + /// * `Step::forward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::forward_checked(&x, 1))` + /// * Corollary: `Step::forward_checked(&a, 0) == Some(a)` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn forward_checked(start: Self, count: usize) -> Option; - /// Replaces this step with `0`, returning a clone of itself. + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, + /// this function is allowed to panic, wrap, or saturate. + /// The suggested behavior is to panic when debug assertions are enabled, + /// and to wrap or saturate otherwise. + /// + /// Unsafe code should not rely on the correctness of behavior after overflow. + /// + /// # Invariants /// - /// The output of this method should always be less than the output of replace_one. - fn replace_zero(&mut self) -> Self; + /// For any `a`, `n`, and `m`, where no overflow occurs: + /// + /// * `Step::forward(Step::forward(a, n), m) == Step::forward(a, n + m)` + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::forward_checked(a, n) == Some(Step::forward(a, n))` + /// * `Step::forward(a, n) == (0..n).fold(a, |x, _| Step::forward(x, 1))` + /// * Corollary: `Step::forward(a, 0) == a` + /// * `Step::forward(a, n) >= a` + /// * `Step::backward(Step::forward(a, n), n) == a` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn forward(start: Self, count: usize) -> Self { + Step::forward_checked(start, count).expect("overflow in `Step::forward`") + } - /// Adds one to this step, returning the result. - fn add_one(&self) -> Self; + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// # Safety + /// + /// It is undefined behavior for this operation to overflow the + /// range of values supported by `Self`. If you cannot guarantee that this + /// will not overflow, use `forward` or `forward_checked` instead. + /// + /// # Invariants + /// + /// For any `a`: + /// + /// * if there exists `b` such that `b > a`, it is safe to call `Step::forward_unchecked(a, 1)` + /// * if there exists `b`, `n` such that `steps_between(&a, &b) == Some(n)`, + /// it is safe to call `Step::forward_unchecked(a, m)` for any `m <= n`. + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::forward_unchecked(a, n)` is equivalent to `Step::forward(a, n)` + #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] + unsafe fn forward_unchecked(start: Self, count: usize) -> Self { + Step::forward(start, count) + } - /// Subtracts one to this step, returning the result. - fn sub_one(&self) -> Self; + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, returns `None`. + /// + /// # Invariants + /// + /// For any `a`, `n`, and `m`: + /// + /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == n.checked_add(m).and_then(|x| Step::backward_checked(a, x))` + /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == try { Step::backward_checked(a, n.checked_add(m)?) }` + /// + /// For any `a` and `n`: + /// + /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(&x, 1))` + /// * Corollary: `Step::backward_checked(&a, 0) == Some(a)` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn backward_checked(start: Self, count: usize) -> Option; - /// Adds a `usize`, returning `None` on overflow. - fn add_usize(&self, n: usize) -> Option; + /// Returns the value that would be obtained by taking the *predecessor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, + /// this function is allowed to panic, wrap, or saturate. + /// The suggested behavior is to panic when debug assertions are enabled, + /// and to wrap or saturate otherwise. + /// + /// Unsafe code should not rely on the correctness of behavior after overflow. + /// + /// # Invariants + /// + /// For any `a`, `n`, and `m`, where no overflow occurs: + /// + /// * `Step::backward(Step::backward(a, n), m) == Step::backward(a, n + m)` + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::backward_checked(a, n) == Some(Step::backward(a, n))` + /// * `Step::backward(a, n) == (0..n).fold(a, |x, _| Step::backward(x, 1))` + /// * Corollary: `Step::backward(a, 0) == a` + /// * `Step::backward(a, n) <= a` + /// * `Step::forward(Step::backward(a, n), n) == a` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn backward(start: Self, count: usize) -> Self { + Step::backward_checked(start, count).expect("overflow in `Step::backward`") + } - /// Subtracts a `usize`, returning `None` on underflow. - fn sub_usize(&self, n: usize) -> Option { - // this default implementation makes the addition of `sub_usize` a non-breaking change - let _ = n; - unimplemented!() + /// Returns the value that would be obtained by taking the *predecessor* + /// of `self` `count` times. + /// + /// # Safety + /// + /// It is undefined behavior for this operation to overflow the + /// range of values supported by `Self`. If you cannot guarantee that this + /// will not overflow, use `backward` or `backward_checked` instead. + /// + /// # Invariants + /// + /// For any `a`: + /// + /// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)` + /// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`, + /// it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`. + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::backward_unchecked(a, n)` is equivalent to `Step::backward(a, n)` + #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] + unsafe fn backward_unchecked(start: Self, count: usize) -> Self { + Step::backward(start, count) } } @@ -53,127 +187,218 @@ pub trait Step: Clone + PartialOrd + Sized { macro_rules! step_identical_methods { () => { #[inline] - fn replace_one(&mut self) -> Self { - mem::replace(self, 1) + unsafe fn forward_unchecked(start: Self, n: usize) -> Self { + start.unchecked_add(n as Self) } #[inline] - fn replace_zero(&mut self) -> Self { - mem::replace(self, 0) + unsafe fn backward_unchecked(start: Self, n: usize) -> Self { + start.unchecked_sub(n as Self) } #[inline] - fn add_one(&self) -> Self { - Add::add(*self, 1) + fn forward(start: Self, n: usize) -> Self { + // In debug builds, trigger a panic on overflow. + // This should optimize completely out in release builds. + if Self::forward_checked(start, n).is_none() { + let _ = Add::add(Self::MAX, 1); + } + // Do wrapping math to allow e.g. `Step::forward(-128i8, 255)`. + start.wrapping_add(n as Self) } #[inline] - fn sub_one(&self) -> Self { - Sub::sub(*self, 1) + fn backward(start: Self, n: usize) -> Self { + // In debug builds, trigger a panic on overflow. + // This should optimize completely out in release builds. + if Self::backward_checked(start, n).is_none() { + let _ = Sub::sub(Self::MIN, 1); + } + // Do wrapping math to allow e.g. `Step::backward(127i8, 255)`. + start.wrapping_sub(n as Self) } - } + }; } -macro_rules! step_impl_unsigned { - ($($t:ty)*) => ($( - #[unstable(feature = "step_trait", - reason = "likely to be replaced by finer-grained traits", - issue = "42168")] - impl Step for $t { - #[inline] - fn steps_between(start: &$t, end: &$t) -> Option { - if *start < *end { - usize::try_from(*end - *start).ok() - } else { - Some(0) +macro_rules! step_integer_impls { + { + narrower than or same width as usize: + $( [ $u_narrower:ident $i_narrower:ident ] ),+; + wider than usize: + $( [ $u_wider:ident $i_wider:ident ] ),+; + } => { + $( + #[allow(unreachable_patterns)] + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $u_narrower { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + // This relies on $u_narrower <= usize + Some((*end - *start) as usize) + } else { + None + } } - } - #[inline] - #[allow(unreachable_patterns)] - fn add_usize(&self, n: usize) -> Option { - match <$t>::try_from(n) { - Ok(n_as_t) => self.checked_add(n_as_t), - Err(_) => None, + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + match Self::try_from(n) { + Ok(n) => start.checked_add(n), + Err(_) => None, // if n is out of range, `unsigned_start + n` is too + } + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + match Self::try_from(n) { + Ok(n) => start.checked_sub(n), + Err(_) => None, // if n is out of range, `unsigned_start - n` is too + } } } - #[inline] #[allow(unreachable_patterns)] - fn sub_usize(&self, n: usize) -> Option { - match <$t>::try_from(n) { - Ok(n_as_t) => self.checked_sub(n_as_t), - Err(_) => None, + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $i_narrower { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + // This relies on $i_narrower <= usize + // + // Casting to isize extends the width but preserves the sign. + // Use wrapping_sub in isize space and cast to usize to compute + // the difference that may not fit inside the range of isize. + Some((*end as isize).wrapping_sub(*start as isize) as usize) + } else { + None + } } - } - step_identical_methods!(); - } - )*) -} -macro_rules! step_impl_signed { - ($( [$t:ty : $unsigned:ty] )*) => ($( - #[unstable(feature = "step_trait", - reason = "likely to be replaced by finer-grained traits", - issue = "42168")] - impl Step for $t { - #[inline] - fn steps_between(start: &$t, end: &$t) -> Option { - if *start < *end { - // Use .wrapping_sub and cast to unsigned to compute the - // difference that may not fit inside the range of $t. - usize::try_from(end.wrapping_sub(*start) as $unsigned).ok() - } else { - Some(0) + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + match $u_narrower::try_from(n) { + Ok(n) => { + // Wrapping handles cases like + // `Step::forward(-120_i8, 200) == Some(80_i8)`, + // even though 200 is out of range for i8. + let wrapped = start.wrapping_add(n as Self); + if wrapped >= start { + Some(wrapped) + } else { + None // Addition overflowed + } + } + // If n is out of range of e.g. u8, + // then it is bigger than the entire range for i8 is wide + // so `any_i8 + n` necessarily overflows i8. + Err(_) => None, + } + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + match $u_narrower::try_from(n) { + Ok(n) => { + // Wrapping handles cases like + // `Step::forward(-120_i8, 200) == Some(80_i8)`, + // even though 200 is out of range for i8. + let wrapped = start.wrapping_sub(n as Self); + if wrapped <= start { + Some(wrapped) + } else { + None // Subtraction overflowed + } + } + // If n is out of range of e.g. u8, + // then it is bigger than the entire range for i8 is wide + // so `any_i8 - n` necessarily overflows i8. + Err(_) => None, + } } } + )+ - #[inline] + $( #[allow(unreachable_patterns)] - fn add_usize(&self, n: usize) -> Option { - match <$unsigned>::try_from(n) { - Ok(n_as_unsigned) => { - // Wrapping in unsigned space handles cases like - // `-120_i8.add_usize(200) == Some(80_i8)`, - // even though 200_usize is out of range for i8. - let wrapped = (*self as $unsigned).wrapping_add(n_as_unsigned) as $t; - if wrapped >= *self { - Some(wrapped) - } else { - None // Addition overflowed - } + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $u_wider { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + usize::try_from(*end - *start).ok() + } else { + None } - Err(_) => None, + } + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + start.checked_add(n as Self) + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + start.checked_sub(n as Self) } } - #[inline] #[allow(unreachable_patterns)] - fn sub_usize(&self, n: usize) -> Option { - match <$unsigned>::try_from(n) { - Ok(n_as_unsigned) => { - // Wrapping in unsigned space handles cases like - // `80_i8.sub_usize(200) == Some(-120_i8)`, - // even though 200_usize is out of range for i8. - let wrapped = (*self as $unsigned).wrapping_sub(n_as_unsigned) as $t; - if wrapped <= *self { - Some(wrapped) - } else { - None // Subtraction underflowed + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $i_wider { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + match end.checked_sub(*start) { + Some(result) => usize::try_from(result).ok(), + // If the difference is too big for e.g. i128, + // it's also gonna be too big for usize with fewer bits. + None => None, } + } else { + None } - Err(_) => None, + } + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + start.checked_add(n as Self) + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + start.checked_sub(n as Self) } } + )+ + }; +} - step_identical_methods!(); - } - )*) +#[cfg(target_pointer_width = "64")] +step_integer_impls! { + narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize]; + wider than usize: [u128 i128]; +} + +#[cfg(target_pointer_width = "32")] +step_integer_impls! { + narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize]; + wider than usize: [u64 i64], [u128 i128]; } -step_impl_unsigned!(usize u8 u16 u32 u64 u128); -step_impl_signed!([isize: usize][i8: u8][i16: u16]); -step_impl_signed!([i32: u32][i64: u64][i128: u128]); +#[cfg(target_pointer_width = "16")] +step_integer_impls! { + narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize]; + wider than usize: [u32 i32], [u64 i64], [u128 i128]; +} macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( @@ -189,20 +414,6 @@ macro_rules! range_incl_exact_iter_impl { )*) } -macro_rules! range_trusted_len_impl { - ($($t:ty)*) => ($( - #[unstable(feature = "trusted_len", issue = "37572")] - unsafe impl TrustedLen for ops::Range<$t> { } - )*) -} - -macro_rules! range_incl_trusted_len_impl { - ($($t:ty)*) => ($( - #[unstable(feature = "trusted_len", issue = "37572")] - unsafe impl TrustedLen for ops::RangeInclusive<$t> { } - )*) -} - #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for ops::Range { type Item = A; @@ -210,16 +421,12 @@ impl Iterator for ops::Range { #[inline] fn next(&mut self) -> Option { if self.start < self.end { - // We check for overflow here, even though it can't actually - // happen. Adding this check does however help llvm vectorize loops - // for some ranges that don't get vectorized otherwise, - // and this won't actually result in an extra check in an optimized build. - if let Some(mut n) = self.start.add_usize(1) { - mem::swap(&mut n, &mut self.start); - Some(n) - } else { - None - } + // SAFETY: just checked precondition + // We use the unchecked version here, because + // this helps LLVM vectorize loops for some ranges + // that don't get vectorized otherwise. + let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + Some(mem::replace(&mut self.start, n)) } else { None } @@ -227,17 +434,19 @@ impl Iterator for ops::Range { #[inline] fn size_hint(&self) -> (usize, Option) { - match Step::steps_between(&self.start, &self.end) { - Some(hint) => (hint, Some(hint)), - None => (usize::MAX, None), + if self.start < self.end { + let hint = Step::steps_between(&self.start, &self.end); + (hint.unwrap_or(usize::MAX), hint) + } else { + (0, Some(0)) } } #[inline] fn nth(&mut self, n: usize) -> Option { - if let Some(plus_n) = self.start.add_usize(n) { + if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) { if plus_n < self.end { - self.start = plus_n.add_one(); + self.start = Step::forward(plus_n.clone(), 1); return Some(plus_n); } } @@ -263,25 +472,42 @@ impl Iterator for ops::Range { } // These macros generate `ExactSizeIterator` impls for various range types. -// Range<{u,i}64> and RangeInclusive<{u,i}{32,64,size}> are excluded -// because they cannot guarantee having a length <= usize::MAX, which is -// required by ExactSizeIterator. -range_exact_iter_impl!(usize u8 u16 u32 isize i8 i16 i32); -range_incl_exact_iter_impl!(u8 u16 i8 i16); - -// These macros generate `TrustedLen` impls. // -// They need to guarantee that .size_hint() is either exact, or that -// the upper bound is None when it does not fit the type limits. -range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128); -range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128); +// * `ExactSizeIterator::len` is required to always return an exact `usize`, +// so no range can be longer than `usize::MAX`. +// * For integer types in `Range<_>` this is the case for types narrower than or as wide as `usize`. +// For integer types in `RangeInclusive<_>` +// this is the case for types *strictly narrower* than `usize` +// since e.g. `(0..=u64::MAX).len()` would be `u64::MAX + 1`. +range_exact_iter_impl! { + usize u8 u16 + isize i8 i16 + + // These are incorect per the reasoning above, + // but removing them would be a breaking change as they were stabilized in Rust 1.0.0. + // So e.g. `(0..66_000_u32).len()` for example will compile without error or warnings + // on 16-bit platforms, but continue to give a wrong result. + u32 + i32 +} +range_incl_exact_iter_impl! { + u8 + i8 + + // These are incorect per the reasoning above, + // but removing them would be a breaking change as they were stabilized in Rust 1.26.0. + // So e.g. `(0..=u16::MAX).len()` for example will compile without error or warnings + // on 16-bit platforms, but continue to give a wrong result. + u16 + i16 +} #[stable(feature = "rust1", since = "1.0.0")] impl DoubleEndedIterator for ops::Range { #[inline] fn next_back(&mut self) -> Option { if self.start < self.end { - self.end = self.end.sub_one(); + self.end = Step::backward(self.end.clone(), 1); Some(self.end.clone()) } else { None @@ -290,9 +516,9 @@ impl DoubleEndedIterator for ops::Range { #[inline] fn nth_back(&mut self, n: usize) -> Option { - if let Some(minus_n) = self.end.sub_usize(n) { + if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) { if minus_n > self.start { - self.end = minus_n.sub_one(); + self.end = Step::backward(minus_n, 1); return Some(self.end.clone()); } } @@ -302,6 +528,9 @@ impl DoubleEndedIterator for ops::Range { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ops::Range {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ops::Range {} @@ -311,9 +540,8 @@ impl Iterator for ops::RangeFrom { #[inline] fn next(&mut self) -> Option { - let mut n = self.start.add_one(); - mem::swap(&mut n, &mut self.start); - Some(n) + let n = Step::forward(self.start.clone(), 1); + Some(mem::replace(&mut self.start, n)) } #[inline] @@ -323,8 +551,16 @@ impl Iterator for ops::RangeFrom { #[inline] fn nth(&mut self, n: usize) -> Option { - let plus_n = self.start.add_usize(n).expect("overflow in RangeFrom::nth"); - self.start = plus_n.add_one(); + // If we would jump over the maximum value, panic immediately. + // This is consistent with behavior before the Step redesign, + // even though it's inconsistent with n `next` calls. + // To get consistent behavior, change it to use `forward` instead. + // This change should go through FCP separately to the redesign, so is for now left as a + // FIXME: make this consistent + let plus_n = + Step::forward_checked(self.start.clone(), n).expect("overflow in RangeFrom::nth"); + // The final step should always be debug-checked. + self.start = Step::forward(plus_n.clone(), 1); Some(plus_n) } } @@ -346,7 +582,7 @@ impl Iterator for ops::RangeInclusive { } let is_iterating = self.start < self.end; Some(if is_iterating { - let n = self.start.add_one(); + let n = Step::forward(self.start.clone(), 1); mem::replace(&mut self.start, n) } else { self.exhausted = true; @@ -372,12 +608,12 @@ impl Iterator for ops::RangeInclusive { return None; } - if let Some(plus_n) = self.start.add_usize(n) { + if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) { use crate::cmp::Ordering::*; match plus_n.partial_cmp(&self.end) { Some(Less) => { - self.start = plus_n.add_one(); + self.start = Step::forward(plus_n.clone(), 1); return Some(plus_n); } Some(Equal) => { @@ -408,7 +644,7 @@ impl Iterator for ops::RangeInclusive { let mut accum = init; while self.start < self.end { - let n = self.start.add_one(); + let n = Step::forward(self.start.clone(), 1); let n = mem::replace(&mut self.start, n); accum = f(accum, n)?; } @@ -422,6 +658,20 @@ impl Iterator for ops::RangeInclusive { Try::from_ok(accum) } + #[inline] + fn fold(mut self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(f)).unwrap() + } + #[inline] fn last(mut self) -> Option { self.next_back() @@ -447,7 +697,7 @@ impl DoubleEndedIterator for ops::RangeInclusive { } let is_iterating = self.start < self.end; Some(if is_iterating { - let n = self.end.sub_one(); + let n = Step::backward(self.end.clone(), 1); mem::replace(&mut self.end, n) } else { self.exhausted = true; @@ -461,12 +711,12 @@ impl DoubleEndedIterator for ops::RangeInclusive { return None; } - if let Some(minus_n) = self.end.sub_usize(n) { + if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) { use crate::cmp::Ordering::*; match minus_n.partial_cmp(&self.start) { Some(Greater) => { - self.end = minus_n.sub_one(); + self.end = Step::backward(minus_n.clone(), 1); return Some(minus_n); } Some(Equal) => { @@ -497,7 +747,7 @@ impl DoubleEndedIterator for ops::RangeInclusive { let mut accum = init; while self.start < self.end { - let n = self.end.sub_one(); + let n = Step::backward(self.end.clone(), 1); let n = mem::replace(&mut self.end, n); accum = f(accum, n)?; } @@ -510,7 +760,24 @@ impl DoubleEndedIterator for ops::RangeInclusive { Try::from_ok(accum) } + + #[inline] + fn rfold(mut self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_rfold(init, ok(f)).unwrap() + } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ops::RangeInclusive {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ops::RangeInclusive {} diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index a1d4e1b31e9b1..d76fa89bd012c 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -1,6 +1,5 @@ use crate::fmt; use crate::marker; -use crate::usize; use super::{FusedIterator, TrustedLen}; diff --git a/src/libcore/iter/traits/double_ended.rs b/src/libcore/iter/traits/double_ended.rs index 104724d9fb63a..cceb373d552a8 100644 --- a/src/libcore/iter/traits/double_ended.rs +++ b/src/libcore/iter/traits/double_ended.rs @@ -221,17 +221,16 @@ pub trait DoubleEndedIterator: Iterator { /// ``` #[inline] #[stable(feature = "iter_rfold", since = "1.27.0")] - fn rfold(mut self, accum: B, f: F) -> B + fn rfold(mut self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) + let mut accum = init; + while let Some(x) = self.next_back() { + accum = f(accum, x); } - - self.try_rfold(accum, ok(f)).unwrap() + accum } /// Searches for an element of an iterator from the back that satisfies a predicate. diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index 39bd270da8680..1c3d95cbb8c35 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -198,7 +198,7 @@ pub trait Iterator { /// // and the maximum possible lower bound /// let iter = 0..; /// - /// assert_eq!((usize::max_value(), None), iter.size_hint()); + /// assert_eq!((usize::MAX, None), iter.size_hint()); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -333,7 +333,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn nth(&mut self, mut n: usize) -> Option { - for x in self { + while let Some(x) = self.next() { if n == 0 { return Some(x); } @@ -1697,8 +1697,8 @@ pub trait Iterator { mut f: impl FnMut(&T) -> bool + 'a, left: &'a mut B, right: &'a mut B, - ) -> impl FnMut(T) + 'a { - move |x| { + ) -> impl FnMut((), T) + 'a { + move |(), x| { if f(&x) { left.extend(Some(x)); } else { @@ -1710,7 +1710,7 @@ pub trait Iterator { let mut left: B = Default::default(); let mut right: B = Default::default(); - self.for_each(extend(f, &mut left, &mut right)); + self.fold((), extend(f, &mut left, &mut right)); (left, right) } @@ -1826,7 +1826,7 @@ pub trait Iterator { /// /// # Note to Implementors /// - /// Most of the other (forward) methods have default implementations in + /// Several of the other (forward) methods have default implementations in /// terms of this one, so try to implement this explicitly if it can /// do something better than the default `for` loop implementation. /// @@ -1944,6 +1944,15 @@ pub trait Iterator { /// may not terminate for infinite iterators, even on traits for which a /// result is determinable in finite time. /// + /// # Note to Implementors + /// + /// Several of the other (forward) methods have default implementations in + /// terms of this one, so try to implement this explicitly if it can + /// do something better than the default `for` loop implementation. + /// + /// In particular, try to have this call `fold()` on the internal parts + /// from which this iterator is composed. + /// /// # Examples /// /// Basic usage: @@ -1992,17 +2001,53 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - fn fold(mut self, init: B, f: F) -> B + fn fold(mut self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) + let mut accum = init; + while let Some(x) = self.next() { + accum = f(accum, x); } + accum + } - self.try_fold(init, ok(f)).unwrap() + /// The same as [`fold()`](#method.fold), but uses the first element in the + /// iterator as the initial value, folding every subsequent element into it. + /// If the iterator is empty, return `None`; otherwise, return the result + /// of the fold. + /// + /// # Example + /// + /// Find the maximum value: + /// + /// ``` + /// #![feature(iterator_fold_self)] + /// + /// fn find_max(iter: I) -> Option + /// where I: Iterator, + /// I::Item: Ord, + /// { + /// iter.fold_first(|a, b| { + /// if a >= b { a } else { b } + /// }) + /// } + /// let a = [10, 20, 5, -23, 0]; + /// let b: [u32; 0] = []; + /// + /// assert_eq!(find_max(a.iter()), Some(&20)); + /// assert_eq!(find_max(b.iter()), None); + /// ``` + #[inline] + #[unstable(feature = "iterator_fold_self", issue = "68125")] + fn fold_first(mut self, f: F) -> Option + where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Self::Item, + { + let first = self.next()?; + Some(self.fold(first, f)) } /// Tests if every element of the iterator matches a predicate. @@ -2236,7 +2281,7 @@ pub trait Iterator { F: FnMut(&Self::Item) -> R, R: Try, { - self.try_for_each(move |x| match f(&x).into_result() { + self.try_fold((), move |(), x| match f(&x).into_result() { Ok(false) => LoopState::Continue(()), Ok(true) => LoopState::Break(Ok(x)), Err(x) => LoopState::Break(Err(x)), @@ -2497,7 +2542,7 @@ pub trait Iterator { move |x, y| cmp::max_by(x, y, &mut compare) } - fold1(self, fold(compare)) + self.fold_first(fold(compare)) } /// Returns the element that gives the minimum value from the @@ -2561,7 +2606,7 @@ pub trait Iterator { move |x, y| cmp::min_by(x, y, &mut compare) } - fold1(self, fold(compare)) + self.fold_first(fold(compare)) } /// Reverses an iterator's direction. @@ -2628,8 +2673,8 @@ pub trait Iterator { fn extend<'a, A, B>( ts: &'a mut impl Extend, us: &'a mut impl Extend, - ) -> impl FnMut((A, B)) + 'a { - move |(t, u)| { + ) -> impl FnMut((), (A, B)) + 'a { + move |(), (t, u)| { ts.extend(Some(t)); us.extend(Some(u)); } @@ -2638,7 +2683,7 @@ pub trait Iterator { let mut ts: FromA = Default::default(); let mut us: FromB = Default::default(); - self.for_each(extend(&mut ts, &mut us)); + self.fold((), extend(&mut ts, &mut us)); (ts, us) } @@ -2883,7 +2928,7 @@ pub trait Iterator { /// assert_eq!([1.].iter().partial_cmp([1., 2.].iter()), Some(Ordering::Less)); /// assert_eq!([1., 2.].iter().partial_cmp([1.].iter()), Some(Ordering::Greater)); /// - /// assert_eq!([std::f64::NAN].iter().partial_cmp([1.].iter()), None); + /// assert_eq!([f64::NAN].iter().partial_cmp([1.].iter()), None); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] fn partial_cmp(self, other: I) -> Option @@ -3072,7 +3117,7 @@ pub trait Iterator { Self::Item: PartialOrd, Self: Sized, { - matches!(self.partial_cmp(other), Some(Ordering::Less) | Some(Ordering::Equal)) + matches!(self.partial_cmp(other), Some(Ordering::Less | Ordering::Equal)) } /// Determines if the elements of this `Iterator` are lexicographically @@ -3112,7 +3157,7 @@ pub trait Iterator { Self::Item: PartialOrd, Self: Sized, { - matches!(self.partial_cmp(other), Some(Ordering::Greater) | Some(Ordering::Equal)) + matches!(self.partial_cmp(other), Some(Ordering::Greater | Ordering::Equal)) } /// Checks if the elements of this iterator are sorted. @@ -3133,7 +3178,7 @@ pub trait Iterator { /// assert!(![1, 3, 2, 4].iter().is_sorted()); /// assert!([0].iter().is_sorted()); /// assert!(std::iter::empty::().is_sorted()); - /// assert!(![0.0, 1.0, std::f32::NAN].iter().is_sorted()); + /// assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); /// ``` #[inline] #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] @@ -3160,7 +3205,7 @@ pub trait Iterator { /// assert!(![1, 3, 2, 4].iter().is_sorted_by(|a, b| a.partial_cmp(b))); /// assert!([0].iter().is_sorted_by(|a, b| a.partial_cmp(b))); /// assert!(std::iter::empty::().is_sorted_by(|a, b| a.partial_cmp(b))); - /// assert!(![0.0, 1.0, std::f32::NAN].iter().is_sorted_by(|a, b| a.partial_cmp(b))); + /// assert!(![0.0, 1.0, f32::NAN].iter().is_sorted_by(|a, b| a.partial_cmp(b))); /// ``` /// /// [`is_sorted`]: trait.Iterator.html#method.is_sorted @@ -3214,20 +3259,6 @@ pub trait Iterator { } } -/// Fold an iterator without having to provide an initial value. -#[inline] -fn fold1(mut it: I, f: F) -> Option -where - I: Iterator, - F: FnMut(I::Item, I::Item) -> I::Item, -{ - // start with the first element as our selection. This avoids - // having to use `Option`s inside the loop, translating to a - // sizeable performance gain (6x in one case). - let first = it.next()?; - Some(it.fold(first, f)) -} - #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for &mut I { type Item = I::Item; diff --git a/src/libcore/iter/traits/marker.rs b/src/libcore/iter/traits/marker.rs index 404cc84495c96..a9ba3908c3898 100644 --- a/src/libcore/iter/traits/marker.rs +++ b/src/libcore/iter/traits/marker.rs @@ -13,6 +13,7 @@ /// [`Iterator::fuse`]: ../../std/iter/trait.Iterator.html#method.fuse /// [`Fuse`]: ../../std/iter/struct.Fuse.html #[stable(feature = "fused", since = "1.26.0")] +#[rustc_unsafe_specialization_marker] pub trait FusedIterator: Iterator {} #[stable(feature = "fused", since = "1.26.0")] @@ -38,6 +39,7 @@ impl FusedIterator for &mut I {} /// [`usize::MAX`]: ../../std/usize/constant.MAX.html /// [`.size_hint`]: ../../std/iter/trait.Iterator.html#method.size_hint #[unstable(feature = "trusted_len", issue = "37572")] +#[rustc_unsafe_specialization_marker] pub unsafe trait TrustedLen: Iterator {} #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 94fc2fd357a06..3b7929f00168a 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -98,11 +98,14 @@ #![feature(is_sorted)] #![feature(lang_items)] #![feature(link_llvm_intrinsics)] +#![feature(llvm_asm)] +#![feature(negative_impls)] #![feature(never_type)] #![feature(nll)] #![feature(exhaustive_patterns)] #![feature(no_core)] #![feature(optin_builtin_traits)] +#![feature(or_patterns)] #![feature(prelude_import)] #![feature(repr_simd, platform_intrinsics)] #![feature(rustc_attrs)] @@ -132,7 +135,6 @@ #![feature(f16c_target_feature)] #![feature(hexagon_target_feature)] #![feature(const_transmute)] -#![feature(structural_match)] #![feature(abi_unadjusted)] #![feature(adx_target_feature)] #![feature(maybe_uninit_slice)] diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs index a0873fe6b625d..625ceb0953b0a 100644 --- a/src/libcore/macros/mod.rs +++ b/src/libcore/macros/mod.rs @@ -1,27 +1,3 @@ -#[cfg(bootstrap)] -#[doc(include = "panic.md")] -#[macro_export] -#[allow_internal_unstable(core_panic, track_caller)] -#[stable(feature = "core", since = "1.6.0")] -macro_rules! panic { - () => ( - $crate::panic!("explicit panic") - ); - ($msg:expr) => ( - $crate::panicking::panic($msg) - ); - ($msg:expr,) => ( - $crate::panic!($msg) - ); - ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt( - $crate::format_args!($fmt, $($arg)+), - $crate::panic::Location::caller(), - ) - ); -} - -#[cfg(not(bootstrap))] #[doc(include = "panic.md")] #[macro_export] #[allow_internal_unstable(core_panic, track_caller)] @@ -1070,8 +1046,10 @@ pub(crate) mod builtin { /// Includes a utf8-encoded file as a string. /// - /// The file is located relative to the current file. (similarly to how - /// modules are found) + /// The file is located relative to the current file (similarly to how + /// modules are found). The provided path is interpreted in a platform-specific + /// way at compile time. So, for instance, an invocation with a Windows path + /// containing backslashes `\` would not compile correctly on Unix. /// /// This macro will yield an expression of type `&'static str` which is the /// contents of the file. @@ -1108,8 +1086,10 @@ pub(crate) mod builtin { /// Includes a file as a reference to a byte array. /// - /// The file is located relative to the current file. (similarly to how - /// modules are found) + /// The file is located relative to the current file (similarly to how + /// modules are found). The provided path is interpreted in a platform-specific + /// way at compile time. So, for instance, an invocation with a Windows path + /// containing backslashes `\` would not compile correctly on Unix. /// /// This macro will yield an expression of type `&'static [u8; N]` which is /// the contents of the file. @@ -1179,6 +1159,10 @@ pub(crate) mod builtin { /// The syntax given to this macro is the same syntax as the [`cfg`] /// attribute. /// + /// `cfg!`, unlike `#[cfg]`, does not remove any code and only evaluates to true or false. For + /// example, all blocks in an if/else expression need to be valid when `cfg!` is used for + /// the condition, regardless of what `cfg!` is evaluating. + /// /// [`cfg`]: ../reference/conditional-compilation.html#the-cfg-attribute /// /// # Examples @@ -1202,7 +1186,9 @@ pub(crate) mod builtin { /// Parses a file as an expression or an item according to the context. /// /// The file is located relative to the current file (similarly to how - /// modules are found). + /// modules are found). The provided path is interpreted in a platform-specific + /// way at compile time. So, for instance, an invocation with a Windows path + /// containing backslashes `\` would not compile correctly on Unix. /// /// Using this macro is often a bad idea, because if the file is /// parsed as an expression, it is going to be placed in the @@ -1257,7 +1243,7 @@ pub(crate) mod builtin { /// be disabled. See [`debug_assert!`] for assertions that are not enabled in /// release builds by default. /// - /// Unsafe code relies on `assert!` to enforce run-time invariants that, if + /// Unsafe code may rely on `assert!` to enforce run-time invariants that, if /// violated could lead to unsafety. /// /// Other use-cases of `assert!` include testing and enforcing run-time @@ -1307,12 +1293,33 @@ pub(crate) mod builtin { /// [unstable book]: ../unstable-book/library-features/asm.html #[unstable( feature = "asm", - issue = "29722", + issue = "72016", reason = "inline assembly is not stable enough for use and is subject to change" )] #[rustc_builtin_macro] #[macro_export] macro_rules! asm { + ("assembly template", + $(operands,)* + $(options($(option),*))? + ) => { + /* compiler built-in */ + }; + } + + /// LLVM-style inline assembly. + /// + /// Read the [unstable book] for the usage. + /// + /// [unstable book]: ../unstable-book/library-features/llvm-asm.html + #[unstable( + feature = "llvm_asm", + issue = "70173", + reason = "LLVM-style inline assembly will never be stabilized, prefer using asm! instead" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! llvm_asm { ("assembly template" : $("output"(operand),)* : $("input"(operand),)* @@ -1405,7 +1412,6 @@ pub(crate) mod builtin { } /// Keeps the item it's applied to if the passed path is accessible, and removes it otherwise. - #[cfg(not(bootstrap))] #[unstable( feature = "cfg_accessible", issue = "64797", diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 2b908f07af8da..c0c0f66aff908 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -8,6 +8,7 @@ use crate::cell::UnsafeCell; use crate::cmp; +use crate::fmt::Debug; use crate::hash::Hash; use crate::hash::Hasher; @@ -87,10 +88,10 @@ impl !Send for *mut T {} message = "the size for values of type `{Self}` cannot be known at compilation time", label = "doesn't have a size known at compile-time", note = "to learn more, visit " + ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>" )] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable -#[cfg_attr(not(bootstrap), rustc_specialization_trait)] +#[rustc_specialization_trait] pub trait Sized { // Empty. } @@ -363,6 +364,13 @@ pub trait StructuralEq { /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "copy"] +// FIXME(matthewjasper) This allows copying a type that doesn't implement +// `Copy` because of unsatisfied lifetime bounds (copying `A<'_>` when only +// `A<'static>: Copy` and `A<'_>: Clone`). +// We have this attribute here for now only because there are quite a few +// existing specializations on `Copy` that already exist in the standard +// library, and there's no way to safely have this behavior right now. +#[rustc_unsafe_specialization_marker] pub trait Copy: Clone { // Empty. } @@ -660,7 +668,6 @@ macro_rules! impls { /// /// [drop check]: ../../nomicon/dropck.html #[lang = "phantom_data"] -#[structural_match] #[stable(feature = "rust1", since = "1.0.0")] pub struct PhantomData; @@ -673,6 +680,37 @@ mod impls { unsafe impl Send for &mut T {} } +/// Compiler-internal trait used to indicate the type of enum discriminants. +/// +/// This trait is automatically implemented for every type and does not add any +/// guarantees to [`mem::Discriminant`]. It is **undefined behavior** to transmute +/// between `DiscriminantKind::Discriminant` and `mem::Discriminant`. +/// +/// [`mem::Discriminant`]: https://doc.rust-lang.org/stable/core/mem/struct.Discriminant.html +#[unstable( + feature = "discriminant_kind", + issue = "none", + reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" +)] +#[cfg_attr(not(bootstrap), lang = "discriminant_kind")] +pub trait DiscriminantKind { + /// The type of the dicriminant, which must satisfy the trait + /// bounds required by `mem::Discriminant`. + type Discriminant: Clone + Copy + Debug + Eq + PartialEq + Hash + Send + Sync + Unpin; +} + +// Manually implement `DiscriminantKind` for all types during bootstrap +// to reduce the required amount of conditional compilation. +#[unstable( + feature = "discriminant_kind", + issue = "none", + reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" +)] +#[cfg(bootstrap)] +impl DiscriminantKind for T { + type Discriminant = u64; +} + /// Compiler-internal trait used to determine whether a type contains /// any `UnsafeCell` internally, but not through an indirection. /// This affects, for example, whether a `static` of that type is @@ -710,6 +748,7 @@ unsafe impl Freeze for &mut T {} /// So this, for example, can only be done on types implementing `Unpin`: /// /// ```rust +/// # #![allow(unused_must_use)] /// use std::mem; /// use std::pin::Pin; /// @@ -760,7 +799,8 @@ impl Unpin for *mut T {} /// Implementations of `Copy` for primitive types. /// /// Implementations that cannot be described in Rust -/// are implemented in `SelectionContext::copy_clone_conditions()` in librustc. +/// are implemented in `traits::SelectionContext::copy_clone_conditions()` +/// in `rustc_trait_selection`. mod copy_impls { use super::Copy; @@ -790,7 +830,7 @@ mod copy_impls { #[stable(feature = "rust1", since = "1.0.0")] impl Copy for *mut T {} - // Shared references can be copied, but mutable references *cannot*! + /// Shared references can be copied, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] impl Copy for &T {} } diff --git a/src/libcore/mem/manually_drop.rs b/src/libcore/mem/manually_drop.rs index 9e5c2b10d0d9e..18767c482c77e 100644 --- a/src/libcore/mem/manually_drop.rs +++ b/src/libcore/mem/manually_drop.rs @@ -2,19 +2,23 @@ use crate::ops::{Deref, DerefMut}; use crate::ptr; /// A wrapper to inhibit compiler from automatically calling `T`’s destructor. -/// /// This wrapper is 0-cost. /// /// `ManuallyDrop` is subject to the same layout optimizations as `T`. /// As a consequence, it has *no effect* on the assumptions that the compiler makes -/// about all values being initialized at their type. In particular, initializing -/// a `ManuallyDrop<&mut T>` with [`mem::zeroed`] is undefined behavior. +/// about its contents. For example, initializing a `ManuallyDrop<&mut T>` +/// with [`mem::zeroed`] is undefined behavior. /// If you need to handle uninitialized data, use [`MaybeUninit`] instead. /// +/// Note that accessing the value inside a `ManuallyDrop` is safe. +/// This means that a `ManuallyDrop` whose content has been dropped must not +/// be exposed through a public safe API. +/// Correspondingly, `ManuallyDrop::drop` is unsafe. +/// /// # Examples /// -/// This wrapper helps with explicitly documenting the drop order dependencies between fields of -/// the type: +/// This wrapper can be used to enforce a particular drop order on fields, regardless +/// of how they are defined in the struct: /// /// ```rust /// use std::mem::ManuallyDrop; @@ -43,8 +47,18 @@ use crate::ptr; /// } /// ``` /// +/// However, care should be taken when using this pattern as it can lead to *leak amplification*. +/// In this example, if the `Drop` implementation for `Peach` were to panic, the `banana` field +/// would also be leaked. +/// +/// In contrast, the automatically-generated compiler drop implementation would have ensured +/// that all fields are dropped even in the presence of panics. This is especially important when +/// working with [pinned] data, where reusing the memory without calling the destructor could lead +/// to Undefined Behaviour. +/// /// [`mem::zeroed`]: fn.zeroed.html /// [`MaybeUninit`]: union.MaybeUninit.html +/// [pinned]: ../pin/index.html #[stable(feature = "manually_drop", since = "1.20.0")] #[lang = "manually_drop"] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -113,19 +127,28 @@ impl ManuallyDrop { } impl ManuallyDrop { - /// Manually drops the contained value. + /// Manually drops the contained value. This is exactly equivalent to calling + /// [`ptr::drop_in_place`] with a pointer to the contained value. As such, unless + /// the contained value is a packed struct, the destructor will be called in-place + /// without moving the value, and thus can be used to safely drop [pinned] data. /// /// If you have ownership of the value, you can use [`ManuallyDrop::into_inner`] instead. /// /// # Safety /// - /// This function runs the destructor of the contained value and thus the wrapped value - /// now represents uninitialized data. It is up to the user of this method to ensure the - /// uninitialized data is not actually used. - /// In particular, this function can only be called at most once - /// for a given instance of `ManuallyDrop`. + /// This function runs the destructor of the contained value. Other than changes made by + /// the destructor itself, the memory is left unchanged, and so as far as the compiler is + /// concerned still holds a bit-pattern which is valid for the type `T`. + /// + /// However, this "zombie" value should not be exposed to safe code, and this function + /// should not be called more than once. To use a value after it's been dropped, or drop + /// a value multiple times, can cause Undefined Behavior (depending on what `drop` does). + /// This is normally prevented by the type system, but users of `ManuallyDrop` must + /// uphold those guarantees without assistance from the compiler. /// /// [`ManuallyDrop::into_inner`]: #method.into_inner + /// [`ptr::drop_in_place`]: ../ptr/fn.drop_in_place.html + /// [pinned]: ../pin/index.html #[stable(feature = "manually_drop", since = "1.20.0")] #[inline] pub unsafe fn drop(slot: &mut ManuallyDrop) { diff --git a/src/libcore/mem/maybe_uninit.rs b/src/libcore/mem/maybe_uninit.rs index bf39d56fc115c..f7ea7eba7b16b 100644 --- a/src/libcore/mem/maybe_uninit.rs +++ b/src/libcore/mem/maybe_uninit.rs @@ -495,9 +495,6 @@ impl MaybeUninit { #[inline(always)] #[rustc_diagnostic_item = "assume_init"] pub unsafe fn assume_init(self) -> T { - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); - #[cfg(not(bootstrap))] intrinsics::assert_inhabited::(); ManuallyDrop::into_inner(self.value) } @@ -562,9 +559,6 @@ impl MaybeUninit { #[unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] pub unsafe fn read(&self) -> T { - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); - #[cfg(not(bootstrap))] intrinsics::assert_inhabited::(); self.as_ptr().read() } @@ -627,9 +621,6 @@ impl MaybeUninit { #[unstable(feature = "maybe_uninit_ref", issue = "63568")] #[inline(always)] pub unsafe fn get_ref(&self) -> &T { - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); - #[cfg(not(bootstrap))] intrinsics::assert_inhabited::(); &*self.value } @@ -748,9 +739,6 @@ impl MaybeUninit { #[unstable(feature = "maybe_uninit_ref", issue = "63568")] #[inline(always)] pub unsafe fn get_mut(&mut self) -> &mut T { - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); - #[cfg(not(bootstrap))] intrinsics::assert_inhabited::(); &mut *self.value } diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 07f7d28bb7546..010f2958e36b9 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -10,7 +10,7 @@ use crate::cmp; use crate::fmt; use crate::hash; use crate::intrinsics; -use crate::marker::{Copy, PhantomData, Sized}; +use crate::marker::{Copy, DiscriminantKind, Sized}; use crate::ptr; mod manually_drop; @@ -378,7 +378,6 @@ pub fn size_of_val(val: &T) -> usize { /// assert_eq!(13, unsafe { mem::size_of_val_raw(y) }); /// ``` #[inline] -#[cfg(not(bootstrap))] #[unstable(feature = "layout_for_ptr", issue = "69835")] pub unsafe fn size_of_val_raw(val: *const T) -> usize { intrinsics::size_of_val(val) @@ -509,7 +508,6 @@ pub fn align_of_val(val: &T) -> usize { /// assert_eq!(4, unsafe { mem::align_of_val_raw(&5i32) }); /// ``` #[inline] -#[cfg(not(bootstrap))] #[unstable(feature = "layout_for_ptr", issue = "69835")] pub unsafe fn align_of_val_raw(val: *const T) -> usize { intrinsics::min_align_of_val(val) @@ -621,10 +619,7 @@ pub const fn needs_drop() -> bool { #[allow(deprecated)] #[rustc_diagnostic_item = "mem_zeroed"] pub unsafe fn zeroed() -> T { - #[cfg(not(bootstrap))] intrinsics::assert_zero_valid::(); - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); MaybeUninit::zeroed().assume_init() } @@ -657,10 +652,7 @@ pub unsafe fn zeroed() -> T { #[allow(deprecated)] #[rustc_diagnostic_item = "mem_uninitialized"] pub unsafe fn uninitialized() -> T { - #[cfg(not(bootstrap))] intrinsics::assert_uninit_valid::(); - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); MaybeUninit::uninit().assume_init() } @@ -808,6 +800,7 @@ pub fn take(dest: &mut T) -> T { /// [`Clone`]: ../../std/clone/trait.Clone.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[must_use = "if you don't need the old value, you can just assign the new value directly"] pub fn replace(dest: &mut T, mut src: T) -> T { swap(dest, &mut src); src @@ -923,7 +916,12 @@ pub fn drop(_x: T) {} #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn transmute_copy(src: &T) -> U { - ptr::read_unaligned(src as *const T as *const U) + // If U has a higher alignment requirement, src may not be suitably aligned. + if align_of::() > align_of::() { + ptr::read_unaligned(src as *const T as *const U) + } else { + ptr::read(src as *const T as *const U) + } } /// Opaque type representing the discriminant of an enum. @@ -932,7 +930,7 @@ pub unsafe fn transmute_copy(src: &T) -> U { /// /// [`discriminant`]: fn.discriminant.html #[stable(feature = "discriminant_value", since = "1.21.0")] -pub struct Discriminant(u64, PhantomData T>); +pub struct Discriminant(::Discriminant); // N.B. These trait implementations cannot be derived because we don't want any bounds on T. @@ -997,5 +995,5 @@ impl fmt::Debug for Discriminant { #[stable(feature = "discriminant_value", since = "1.21.0")] #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub const fn discriminant(v: &T) -> Discriminant { - Discriminant(intrinsics::discriminant_value(v), PhantomData) + Discriminant(intrinsics::discriminant_value(v)) } diff --git a/src/libcore/num/dec2flt/algorithm.rs b/src/libcore/num/dec2flt/algorithm.rs index c5f6903f379c4..aaeb4d8a22c29 100644 --- a/src/libcore/num/dec2flt/algorithm.rs +++ b/src/libcore/num/dec2flt/algorithm.rs @@ -60,7 +60,7 @@ mod fpu_precision { fn set_cw(cw: u16) { // SAFETY: the `fldcw` instruction has been audited to be able to work correctly with // any `u16` - unsafe { asm!("fldcw $0" :: "m" (cw) :: "volatile") } + unsafe { llvm_asm!("fldcw $0" :: "m" (cw) :: "volatile") } } /// Sets the precision field of the FPU to `T` and returns a `FPUControlWord`. @@ -78,7 +78,7 @@ mod fpu_precision { // `FPUControlWord` structure is dropped // SAFETY: the `fnstcw` instruction has been audited to be able to work correctly with // any `u16` - unsafe { asm!("fnstcw $0" : "=*m" (&cw) ::: "volatile") } + unsafe { llvm_asm!("fnstcw $0" : "=*m" (&cw) ::: "volatile") } // Set the control word to the desired precision. This is achieved by masking away the old // precision (bits 8 and 9, 0x300) and replacing it with the precision flag computed above. diff --git a/src/libcore/num/dec2flt/parse.rs b/src/libcore/num/dec2flt/parse.rs index 93b08bce853c7..2766843155a0e 100644 --- a/src/libcore/num/dec2flt/parse.rs +++ b/src/libcore/num/dec2flt/parse.rs @@ -54,7 +54,7 @@ pub fn parse_decimal(s: &str) -> ParseResult<'_> { match s.first() { None => Valid(Decimal::new(integral, b"", 0)), - Some(&b'e') | Some(&b'E') => { + Some(&b'e' | &b'E') => { if integral.is_empty() { return Invalid; // No digits before 'e' } @@ -70,7 +70,7 @@ pub fn parse_decimal(s: &str) -> ParseResult<'_> { match s.first() { None => Valid(Decimal::new(integral, fractional, 0)), - Some(&b'e') | Some(&b'E') => parse_exp(integral, fractional, &s[1..]), + Some(&b'e' | &b'E') => parse_exp(integral, fractional, &s[1..]), _ => Invalid, // Trailing junk after fractional part } } diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 3fdc2bae33876..434569020d2a8 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -18,15 +18,46 @@ use crate::num::FpCategory; /// The radix or base of the internal representation of `f32`. /// Use [`f32::RADIX`](../../std/primitive.f32.html#associatedconstant.RADIX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let r = std::f32::RADIX; +/// +/// // intended way +/// let r = f32::RADIX; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const RADIX: u32 = f32::RADIX; /// Number of significant digits in base 2. /// Use [`f32::MANTISSA_DIGITS`](../../std/primitive.f32.html#associatedconstant.MANTISSA_DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f32::MANTISSA_DIGITS; +/// +/// // intended way +/// let d = f32::MANTISSA_DIGITS; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MANTISSA_DIGITS: u32 = f32::MANTISSA_DIGITS; + /// Approximate number of significant digits in base 10. /// Use [`f32::DIGITS`](../../std/primitive.f32.html#associatedconstant.DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f32::DIGITS; +/// +/// // intended way +/// let d = f32::DIGITS; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const DIGITS: u32 = f32::DIGITS; @@ -36,50 +67,166 @@ pub const DIGITS: u32 = f32::DIGITS; /// This is the difference between `1.0` and the next larger representable number. /// /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let e = std::f32::EPSILON; +/// +/// // intended way +/// let e = f32::EPSILON; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const EPSILON: f32 = f32::EPSILON; /// Smallest finite `f32` value. /// Use [`f32::MIN`](../../std/primitive.f32.html#associatedconstant.MIN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN; +/// +/// // intended way +/// let min = f32::MIN; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN: f32 = f32::MIN; + /// Smallest positive normal `f32` value. /// Use [`f32::MIN_POSITIVE`](../../std/primitive.f32.html#associatedconstant.MIN_POSITIVE) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN_POSITIVE; +/// +/// // intended way +/// let min = f32::MIN_POSITIVE; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN_POSITIVE: f32 = f32::MIN_POSITIVE; + /// Largest finite `f32` value. /// Use [`f32::MAX`](../../std/primitive.f32.html#associatedconstant.MAX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f32::MAX; +/// +/// // intended way +/// let max = f32::MAX; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MAX: f32 = f32::MAX; /// One greater than the minimum possible normal power of 2 exponent. /// Use [`f32::MIN_EXP`](../../std/primitive.f32.html#associatedconstant.MIN_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN_EXP; +/// +/// // intended way +/// let min = f32::MIN_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN_EXP: i32 = f32::MIN_EXP; + /// Maximum possible power of 2 exponent. /// Use [`f32::MAX_EXP`](../../std/primitive.f32.html#associatedconstant.MAX_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f32::MAX_EXP; +/// +/// // intended way +/// let max = f32::MAX_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MAX_EXP: i32 = f32::MAX_EXP; /// Minimum possible normal power of 10 exponent. /// Use [`f32::MIN_10_EXP`](../../std/primitive.f32.html#associatedconstant.MIN_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN_10_EXP; +/// +/// // intended way +/// let min = f32::MIN_10_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN_10_EXP: i32 = f32::MIN_10_EXP; + /// Maximum possible power of 10 exponent. /// Use [`f32::MAX_10_EXP`](../../std/primitive.f32.html#associatedconstant.MAX_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f32::MAX_10_EXP; +/// +/// // intended way +/// let max = f32::MAX_10_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MAX_10_EXP: i32 = f32::MAX_10_EXP; /// Not a Number (NaN). /// Use [`f32::NAN`](../../std/primitive.f32.html#associatedconstant.NAN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let nan = std::f32::NAN; +/// +/// // intended way +/// let nan = f32::NAN; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const NAN: f32 = f32::NAN; + /// Infinity (∞). /// Use [`f32::INFINITY`](../../std/primitive.f32.html#associatedconstant.INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let inf = std::f32::INFINITY; +/// +/// // intended way +/// let inf = f32::INFINITY; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const INFINITY: f32 = f32::INFINITY; + /// Negative infinity (−∞). /// Use [`f32::NEG_INFINITY`](../../std/primitive.f32.html#associatedconstant.NEG_INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let ninf = std::f32::NEG_INFINITY; +/// +/// // intended way +/// let ninf = f32::NEG_INFINITY; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const NEG_INFINITY: f32 = f32::NEG_INFINITY; @@ -220,7 +367,7 @@ impl f32 { /// Infinity (∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const INFINITY: f32 = 1.0_f32 / 0.0_f32; - /// Negative infinity (-∞). + /// Negative infinity (−∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; @@ -265,7 +412,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_infinite(self) -> bool { - self.abs_private() == INFINITY + self.abs_private() == Self::INFINITY } /// Returns `true` if this number is neither infinite nor `NaN`. @@ -287,7 +434,7 @@ impl f32 { pub fn is_finite(self) -> bool { // There's no need to handle NaN separately: if self is NaN, // the comparison is not true, exactly as desired. - self.abs_private() < INFINITY + self.abs_private() < Self::INFINITY } /// Returns `true` if the number is neither zero, infinite, @@ -394,9 +541,7 @@ impl f32 { /// Converts radians to degrees. /// /// ``` - /// use std::f32::consts; - /// - /// let angle = consts::PI; + /// let angle = std::f32::consts::PI; /// /// let abs_difference = (angle.to_degrees() - 180.0).abs(); /// @@ -413,11 +558,9 @@ impl f32 { /// Converts degrees to radians. /// /// ``` - /// use std::f32::consts; - /// /// let angle = 180.0f32; /// - /// let abs_difference = (angle.to_radians() - consts::PI).abs(); + /// let abs_difference = (angle.to_radians() - std::f32::consts::PI).abs(); /// /// assert!(abs_difference <= f32::EPSILON); /// ``` @@ -464,15 +607,13 @@ impl f32 { /// assuming that the value is finite and fits in that type. /// /// ``` - /// #![feature(float_approx_unchecked_to)] - /// /// let value = 4.6_f32; - /// let rounded = unsafe { value.approx_unchecked_to::() }; + /// let rounded = unsafe { value.to_int_unchecked::() }; /// assert_eq!(rounded, 4); /// /// let value = -128.9_f32; - /// let rounded = unsafe { value.approx_unchecked_to::() }; - /// assert_eq!(rounded, std::i8::MIN); + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); /// ``` /// /// # Safety @@ -482,13 +623,13 @@ impl f32 { /// * Not be `NaN` /// * Not be infinite /// * Be representable in the return type `Int`, after truncating off its fractional part - #[unstable(feature = "float_approx_unchecked_to", issue = "67058")] + #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] #[inline] - pub unsafe fn approx_unchecked_to(self) -> Int + pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt, { - FloatToInt::::approx_unchecked(self) + FloatToInt::::to_int_unchecked(self) } /// Raw transmutation to `u32`. diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 129df937c0bb8..6476ddb4541ff 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -18,15 +18,46 @@ use crate::num::FpCategory; /// The radix or base of the internal representation of `f64`. /// Use [`f64::RADIX`](../../std/primitive.f64.html#associatedconstant.RADIX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let r = std::f64::RADIX; +/// +/// // intended way +/// let r = f64::RADIX; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const RADIX: u32 = f64::RADIX; /// Number of significant digits in base 2. /// Use [`f64::MANTISSA_DIGITS`](../../std/primitive.f64.html#associatedconstant.MANTISSA_DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f64::MANTISSA_DIGITS; +/// +/// // intended way +/// let d = f64::MANTISSA_DIGITS; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MANTISSA_DIGITS: u32 = f64::MANTISSA_DIGITS; + /// Approximate number of significant digits in base 10. /// Use [`f64::DIGITS`](../../std/primitive.f64.html#associatedconstant.DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f64::DIGITS; +/// +/// // intended way +/// let d = f64::DIGITS; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const DIGITS: u32 = f64::DIGITS; @@ -36,50 +67,166 @@ pub const DIGITS: u32 = f64::DIGITS; /// This is the difference between `1.0` and the next larger representable number. /// /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let e = std::f64::EPSILON; +/// +/// // intended way +/// let e = f64::EPSILON; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const EPSILON: f64 = f64::EPSILON; /// Smallest finite `f64` value. /// Use [`f64::MIN`](../../std/primitive.f64.html#associatedconstant.MIN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN; +/// +/// // intended way +/// let min = f64::MIN; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN: f64 = f64::MIN; + /// Smallest positive normal `f64` value. /// Use [`f64::MIN_POSITIVE`](../../std/primitive.f64.html#associatedconstant.MIN_POSITIVE) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN_POSITIVE; +/// +/// // intended way +/// let min = f64::MIN_POSITIVE; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN_POSITIVE: f64 = f64::MIN_POSITIVE; + /// Largest finite `f64` value. /// Use [`f64::MAX`](../../std/primitive.f64.html#associatedconstant.MAX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f64::MAX; +/// +/// // intended way +/// let max = f64::MAX; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MAX: f64 = f64::MAX; /// One greater than the minimum possible normal power of 2 exponent. /// Use [`f64::MIN_EXP`](../../std/primitive.f64.html#associatedconstant.MIN_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN_EXP; +/// +/// // intended way +/// let min = f64::MIN_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN_EXP: i32 = f64::MIN_EXP; + /// Maximum possible power of 2 exponent. /// Use [`f64::MAX_EXP`](../../std/primitive.f64.html#associatedconstant.MAX_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f64::MAX_EXP; +/// +/// // intended way +/// let max = f64::MAX_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MAX_EXP: i32 = f64::MAX_EXP; /// Minimum possible normal power of 10 exponent. /// Use [`f64::MIN_10_EXP`](../../std/primitive.f64.html#associatedconstant.MIN_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN_10_EXP; +/// +/// // intended way +/// let min = f64::MIN_10_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MIN_10_EXP: i32 = f64::MIN_10_EXP; + /// Maximum possible power of 10 exponent. /// Use [`f64::MAX_10_EXP`](../../std/primitive.f64.html#associatedconstant.MAX_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f64::MAX_10_EXP; +/// +/// // intended way +/// let max = f64::MAX_10_EXP; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MAX_10_EXP: i32 = f64::MAX_10_EXP; /// Not a Number (NaN). /// Use [`f64::NAN`](../../std/primitive.f64.html#associatedconstant.NAN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let nan = std::f64::NAN; +/// +/// // intended way +/// let nan = f64::NAN; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const NAN: f64 = f64::NAN; + /// Infinity (∞). /// Use [`f64::INFINITY`](../../std/primitive.f64.html#associatedconstant.INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let inf = std::f64::INFINITY; +/// +/// // intended way +/// let inf = f64::INFINITY; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const INFINITY: f64 = f64::INFINITY; + /// Negative infinity (−∞). /// Use [`f64::NEG_INFINITY`](../../std/primitive.f64.html#associatedconstant.NEG_INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let ninf = std::f64::NEG_INFINITY; +/// +/// // intended way +/// let ninf = f64::NEG_INFINITY; +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const NEG_INFINITY: f64 = f64::NEG_INFINITY; @@ -219,7 +366,7 @@ impl f64 { /// Infinity (∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const INFINITY: f64 = 1.0_f64 / 0.0_f64; - /// Negative infinity (-∞). + /// Negative infinity (−∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; @@ -264,7 +411,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_infinite(self) -> bool { - self.abs_private() == INFINITY + self.abs_private() == Self::INFINITY } /// Returns `true` if this number is neither infinite nor `NaN`. @@ -286,7 +433,7 @@ impl f64 { pub fn is_finite(self) -> bool { // There's no need to handle NaN separately: if self is NaN, // the comparison is not true, exactly as desired. - self.abs_private() < INFINITY + self.abs_private() < Self::INFINITY } /// Returns `true` if the number is neither zero, infinite, @@ -407,9 +554,7 @@ impl f64 { /// Converts radians to degrees. /// /// ``` - /// use std::f64::consts; - /// - /// let angle = consts::PI; + /// let angle = std::f64::consts::PI; /// /// let abs_difference = (angle.to_degrees() - 180.0).abs(); /// @@ -427,11 +572,9 @@ impl f64 { /// Converts degrees to radians. /// /// ``` - /// use std::f64::consts; - /// /// let angle = 180.0_f64; /// - /// let abs_difference = (angle.to_radians() - consts::PI).abs(); + /// let abs_difference = (angle.to_radians() - std::f64::consts::PI).abs(); /// /// assert!(abs_difference < 1e-10); /// ``` @@ -478,15 +621,13 @@ impl f64 { /// assuming that the value is finite and fits in that type. /// /// ``` - /// #![feature(float_approx_unchecked_to)] - /// - /// let value = 4.6_f32; - /// let rounded = unsafe { value.approx_unchecked_to::() }; + /// let value = 4.6_f64; + /// let rounded = unsafe { value.to_int_unchecked::() }; /// assert_eq!(rounded, 4); /// - /// let value = -128.9_f32; - /// let rounded = unsafe { value.approx_unchecked_to::() }; - /// assert_eq!(rounded, std::i8::MIN); + /// let value = -128.9_f64; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); /// ``` /// /// # Safety @@ -496,13 +637,13 @@ impl f64 { /// * Not be `NaN` /// * Not be infinite /// * Be representable in the return type `Int`, after truncating off its fractional part - #[unstable(feature = "float_approx_unchecked_to", issue = "67058")] + #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] #[inline] - pub unsafe fn approx_unchecked_to(self) -> Int + pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt, { - FloatToInt::::approx_unchecked(self) + FloatToInt::::to_int_unchecked(self) } /// Raw transmutation to `u64`. diff --git a/src/libcore/num/flt2dec/decoder.rs b/src/libcore/num/flt2dec/decoder.rs index 2b74effbe2e98..c43536c6fcca8 100644 --- a/src/libcore/num/flt2dec/decoder.rs +++ b/src/libcore/num/flt2dec/decoder.rs @@ -2,7 +2,6 @@ use crate::num::dec2flt::rawfp::RawFloat; use crate::num::FpCategory; -use crate::{f32, f64}; /// Decoded unsigned finite value, such that: /// diff --git a/src/libcore/num/flt2dec/mod.rs b/src/libcore/num/flt2dec/mod.rs index f5cd26a1852d6..9bf56e93d896f 100644 --- a/src/libcore/num/flt2dec/mod.rs +++ b/src/libcore/num/flt2dec/mod.rs @@ -123,7 +123,6 @@ functions. )] pub use self::decoder::{decode, DecodableFloat, Decoded, FullDecoded}; -use crate::i16; pub mod decoder; pub mod estimator; @@ -422,14 +421,14 @@ fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static "+" } } - (_, Sign::Minus) | (_, Sign::MinusRaw) => { + (_, Sign::Minus | Sign::MinusRaw) => { if negative { "-" } else { "" } } - (_, Sign::MinusPlus) | (_, Sign::MinusPlusRaw) => { + (_, Sign::MinusPlus | Sign::MinusPlusRaw) => { if negative { "-" } else { diff --git a/src/libcore/num/int_macros.rs b/src/libcore/num/int_macros.rs index b68a09e113180..ffd30b03f2109 100644 --- a/src/libcore/num/int_macros.rs +++ b/src/libcore/num/int_macros.rs @@ -12,16 +12,38 @@ macro_rules! int_module { ($T:ident, #[$attr:meta]) => ( doc_comment! { concat!("The smallest value that can be represented by this integer type. -Use [`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN) instead."), +Use [`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN) instead. + +# Examples + +```rust +// deprecated way +let min = std::", stringify!($T), "::MIN; + +// intended way +let min = ", stringify!($T), "::MIN; +``` +"), #[$attr] - pub const MIN: $T = $T::min_value(); + pub const MIN: $T = $T::MIN; } doc_comment! { concat!("The largest value that can be represented by this integer type. -Use [`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX) instead."), +Use [`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX) instead. + +# Examples + +```rust +// deprecated way +let max = std::", stringify!($T), "::MAX; + +// intended way +let max = ", stringify!($T), "::MAX; +``` +"), #[$attr] - pub const MAX: $T = $T::max_value(); + pub const MAX: $T = $T::MAX; } ) } diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 853092dd85ee9..c164e893b4fbf 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -8,6 +8,7 @@ use crate::convert::Infallible; use crate::fmt; use crate::intrinsics; use crate::mem; +use crate::ops::{BitOr, BitOrAssign}; use crate::str::FromStr; // Used because the `?` operator is not allowed in a const context. @@ -110,6 +111,57 @@ assert_eq!(size_of::>(), size_of::<", s } } + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOr for $Ty { + type Output = Self; + #[inline] + fn bitor(self, rhs: Self) -> Self::Output { + // Safety: since `self` and `rhs` are both nonzero, the + // result of the bitwise-or will be nonzero. + unsafe { $Ty::new_unchecked(self.get() | rhs.get()) } + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOr<$Int> for $Ty { + type Output = Self; + #[inline] + fn bitor(self, rhs: $Int) -> Self::Output { + // Safety: since `self` is nonzero, the result of the + // bitwise-or will be nonzero regardless of the value of + // `rhs`. + unsafe { $Ty::new_unchecked(self.get() | rhs) } + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOr<$Ty> for $Int { + type Output = $Ty; + #[inline] + fn bitor(self, rhs: $Ty) -> Self::Output { + // Safety: since `rhs` is nonzero, the result of the + // bitwise-or will be nonzero regardless of the value of + // `self`. + unsafe { $Ty::new_unchecked(self | rhs.get()) } + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOrAssign for $Ty { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOrAssign<$Int> for $Ty { + #[inline] + fn bitor_assign(&mut self, rhs: $Int) { + *self = *self | rhs; + } + } + impl_nonzero_fmt! { #[$stability] (Debug, Display, Binary, Octal, LowerHex, UpperHex) for $Ty } @@ -174,7 +226,7 @@ NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize } /// let zero = Wrapping(0u32); /// let one = Wrapping(1u32); /// -/// assert_eq!(std::u32::MAX, (zero - one).0); +/// assert_eq!(u32::MAX, (zero - one).0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)] @@ -697,6 +749,23 @@ $EndFeature, " } } + doc_comment! { + concat!("Unchecked integer addition. Computes `self + rhs, assuming overflow +cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), +"::max_value()` or `self + rhs < ", stringify!($SelfT), "::min_value()`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + intrinsics::unchecked_add(self, rhs) + } + } + doc_comment! { concat!("Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred. @@ -722,6 +791,23 @@ $EndFeature, " } } + doc_comment! { + concat!("Unchecked integer subtraction. Computes `self - rhs, assuming overflow +cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), +"::max_value()` or `self - rhs < ", stringify!($SelfT), "::min_value()`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + intrinsics::unchecked_sub(self, rhs) + } + } + doc_comment! { concat!("Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow occurred. @@ -747,6 +833,23 @@ $EndFeature, " } } + doc_comment! { + concat!("Unchecked integer multiplication. Computes `self * rhs, assuming overflow +cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), +"::max_value()` or `self * rhs < ", stringify!($SelfT), "::min_value()`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + intrinsics::unchecked_mul(self, rhs) + } + } + doc_comment! { concat!("Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` or the division results in overflow. @@ -1062,8 +1165,7 @@ instead of overflowing. Basic usage: ``` -", $Feature, "#![feature(saturating_neg)] -assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100); +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100); assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100); assert_eq!(", stringify!($SelfT), "::MIN.saturating_neg(), ", stringify!($SelfT), "::MAX); @@ -1072,7 +1174,7 @@ assert_eq!(", stringify!($SelfT), "::MAX.saturating_neg(), ", stringify!($SelfT) $EndFeature, " ```"), - #[unstable(feature = "saturating_neg", issue = "59983")] + #[stable(feature = "saturating_neg", since = "1.45.0")] #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] #[inline] pub const fn saturating_neg(self) -> Self { @@ -1089,8 +1191,7 @@ MIN` instead of overflowing. Basic usage: ``` -", $Feature, "#![feature(saturating_neg)] -assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100); +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100); assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100); assert_eq!(", stringify!($SelfT), "::MIN.saturating_abs(), ", stringify!($SelfT), "::MAX); @@ -1099,7 +1200,7 @@ assert_eq!((", stringify!($SelfT), "::MIN + 1).saturating_abs(), ", stringify!($ $EndFeature, " ```"), - #[unstable(feature = "saturating_neg", issue = "59983")] + #[stable(feature = "saturating_neg", since = "1.45.0")] #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] #[inline] pub const fn saturating_abs(self) -> Self { @@ -1396,8 +1497,8 @@ any high-order bits of `rhs` that would cause the shift to exceed the bitwidth o Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to the range of the type, rather than the bits shifted out of the LHS being returned to the other end. -The primitive integer types all implement a `rotate_left` function, which may be what you want -instead. +The primitive integer types all implement a `[`rotate_left`](#method.rotate_left) function, +which may be what you want instead. # Examples @@ -1428,8 +1529,8 @@ removes any high-order bits of `rhs` that would cause the shift to exceed the bi Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted to the range of the type, rather than the bits shifted out of the LHS being returned to the other -end. The primitive integer types all implement a `rotate_right` function, which may be what you want -instead. +end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function, +which may be what you want instead. # Examples @@ -2884,6 +2985,23 @@ assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", $EndFeat } } + doc_comment! { + concat!("Unchecked integer addition. Computes `self + rhs, assuming overflow +cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), +"::max_value()` or `self + rhs < ", stringify!($SelfT), "::min_value()`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + intrinsics::unchecked_add(self, rhs) + } + } + doc_comment! { concat!("Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred. @@ -2907,6 +3025,23 @@ assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);", $EndFeature, " } } + doc_comment! { + concat!("Unchecked integer subtraction. Computes `self - rhs, assuming overflow +cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), +"::max_value()` or `self - rhs < ", stringify!($SelfT), "::min_value()`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + intrinsics::unchecked_sub(self, rhs) + } + } + doc_comment! { concat!("Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow occurred. @@ -2930,6 +3065,23 @@ assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", $EndFeature, " } } + doc_comment! { + concat!("Unchecked integer multiplication. Computes `self * rhs, assuming overflow +cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), +"::max_value()` or `self * rhs < ", stringify!($SelfT), "::min_value()`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + intrinsics::unchecked_mul(self, rhs) + } + } + doc_comment! { concat!("Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0`. @@ -3456,8 +3608,8 @@ Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to the range of the type, rather than the bits shifted out of the LHS being returned to the other end. The primitive integer -types all implement a `rotate_left` function, which may -be what you want instead. +types all implement a [`rotate_left`](#method.rotate_left) function, +which may be what you want instead. # Examples @@ -3490,8 +3642,8 @@ Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted to the range of the type, rather than the bits shifted out of the LHS being returned to the other end. The primitive integer -types all implement a `rotate_right` function, which may -be what you want instead. +types all implement a [`rotate_right`](#method.rotate_right) function, +which may be what you want instead. # Examples @@ -4376,7 +4528,7 @@ impl u8 { #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_uppercase(&self) -> u8 { - // Unset the fith bit if this is a lowercase letter + // Unset the fifth bit if this is a lowercase letter *self & !((self.is_ascii_lowercase() as u8) << 5) } @@ -4399,7 +4551,7 @@ impl u8 { #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_lowercase(&self) -> u8 { - // Set the fith bit if this is an uppercase letter + // Set the fifth bit if this is an uppercase letter *self | ((self.is_ascii_uppercase() as u8) << 5) } diff --git a/src/libcore/ops/arith.rs b/src/libcore/ops/arith.rs index e9ec81394e32d..622a138abe9d1 100644 --- a/src/libcore/ops/arith.rs +++ b/src/libcore/ops/arith.rs @@ -617,35 +617,22 @@ pub trait Neg { fn neg(self) -> Self::Output; } -macro_rules! neg_impl_core { - ($id:ident => $body:expr, $($t:ty)*) => ($( +macro_rules! neg_impl { + ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] impl Neg for $t { type Output = $t; #[inline] #[rustc_inherit_overflow_checks] - fn neg(self) -> $t { let $id = self; $body } + fn neg(self) -> $t { -self } } forward_ref_unop! { impl Neg, neg for $t } )*) } -macro_rules! neg_impl_numeric { - ($($t:ty)*) => { neg_impl_core!{ x => -x, $($t)*} } -} - -#[allow(unused_macros)] -macro_rules! neg_impl_unsigned { - ($($t:ty)*) => { - neg_impl_core!{ x => { - !x.wrapping_add(1) - }, $($t)*} } -} - -// neg_impl_unsigned! { usize u8 u16 u32 u64 } -neg_impl_numeric! { isize i8 i16 i32 i64 i128 f32 f64 } +neg_impl! { isize i8 i16 i32 i64 i128 f32 f64 } /// The addition assignment operator `+=`. /// diff --git a/src/libcore/ops/deref.rs b/src/libcore/ops/deref.rs index 68244fdb38114..6e96aa330ff19 100644 --- a/src/libcore/ops/deref.rs +++ b/src/libcore/ops/deref.rs @@ -81,6 +81,9 @@ impl Deref for &T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl !DerefMut for &T {} + #[stable(feature = "rust1", since = "1.0.0")] impl Deref for &mut T { type Target = T; diff --git a/src/libcore/ops/drop.rs b/src/libcore/ops/drop.rs index 5233b475c4646..06cfc36363615 100644 --- a/src/libcore/ops/drop.rs +++ b/src/libcore/ops/drop.rs @@ -1,85 +1,139 @@ -/// Used to run some code when a value goes out of scope. -/// This is sometimes called a 'destructor'. +/// Custom code within the destructor. /// -/// When a value goes out of scope, it will have its `drop` method called if -/// its type implements `Drop`. Then, any fields the value contains will also -/// be dropped recursively. +/// 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. /// -/// Because of this recursive dropping, you do not need to implement this trait -/// unless your type needs its own destructor logic. +/// [the reference]: https://doc.rust-lang.org/reference/destructors.html /// -/// Refer to [the chapter on `Drop` in *The Rust Programming Language*][book] -/// for some more elaboration. +/// This destructor consists of two components: +/// - A call to `Drop::drop` for that value, if this special `Drop` trait is implemented for its type. +/// - The automatically generated "drop glue" which recursively calls the destructors +/// of the all fields of this value. /// -/// [book]: ../../book/ch15-03-drop.html +/// As Rust automatically calls the destructors of all contained fields, +/// you don't have to implement `Drop` in most cases. But there are some cases where +/// it is useful, for example for types which directly manage a resource. +/// That resource may be memory, it may be a file descriptor, it may be a network socket. +/// Once a value of that type is no longer going to be used, it should "clean up" its +/// resource by freeing the memory or closing the file or socket. This is +/// the job of a destructor, and therefore the job of `Drop::drop`. /// -/// # Examples +/// ## Examples /// -/// ## Implementing `Drop` +/// To see destructors in action, let's take a look at the following program: /// -/// The `drop` method is called when `_x` goes out of scope, and therefore -/// `main` prints `Dropping!`. -/// -/// ``` +/// ```rust /// struct HasDrop; /// /// impl Drop for HasDrop { /// fn drop(&mut self) { -/// println!("Dropping!"); +/// println!("Dropping HasDrop!"); +/// } +/// } +/// +/// struct HasTwoDrops { +/// one: HasDrop, +/// two: HasDrop, +/// } +/// +/// impl Drop for HasTwoDrops { +/// fn drop(&mut self) { +/// println!("Dropping HasTwoDrops!"); /// } /// } /// /// fn main() { -/// let _x = HasDrop; +/// let _x = HasTwoDrops { one: HasDrop, two: HasDrop }; +/// println!("Running!"); /// } /// ``` /// -/// ## Dropping is done recursively +/// Rust will first call `Drop::drop` for `_x` and then for both `_x.one` and `_x.two`, +/// meaning that running this will print /// -/// When `outer` goes out of scope, the `drop` method will be called first for -/// `Outer`, then for `Inner`. Therefore, `main` prints `Dropping Outer!` and -/// then `Dropping Inner!`. +/// ```text +/// Running! +/// Dropping HasTwoDrops! +/// Dropping HasDrop! +/// Dropping HasDrop! +/// ``` +/// +/// Even if we remove the implementation of `Drop` for `HasTwoDrop`, the destructors of its fields are still called. +/// This would result in /// +/// ```test +/// Running! +/// Dropping HasDrop! +/// Dropping HasDrop! /// ``` -/// struct Inner; -/// struct Outer(Inner); /// -/// impl Drop for Inner { +/// ## You cannot call `Drop::drop` yourself +/// +/// Because `Drop::drop` is used to clean up a value, it may be dangerous to use this value after +/// the method has been called. As `Drop::drop` does not take ownership of its input, +/// Rust prevents misuse by not allowing you to call `Drop::drop` directly. +/// +/// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error. +/// +/// If you'd like explicitly call the destructor of a value, [`std::mem::drop`] can be used instead. +/// +/// [`std::mem::drop`]: ../../std/mem/fn.drop.html +/// +/// ## Drop order +/// +/// Which of our two `HasDrop` drops first, though? For structs, it's the same +/// order that they're declared: first `one`, then `two`. If you'd like to try +/// this yourself, you can modify `HasDrop` above to contain some data, like an +/// integer, and then use it in the `println!` inside of `Drop`. This behavior is +/// guaranteed by the language. +/// +/// Unlike for structs, local variables are dropped in reverse order: +/// +/// ```rust +/// struct Foo; +/// +/// impl Drop for Foo { /// fn drop(&mut self) { -/// println!("Dropping Inner!"); +/// println!("Dropping Foo!") /// } /// } /// -/// impl Drop for Outer { +/// struct Bar; +/// +/// impl Drop for Bar { /// fn drop(&mut self) { -/// println!("Dropping Outer!"); +/// println!("Dropping Bar!") /// } /// } /// /// fn main() { -/// let _x = Outer(Inner); +/// let _foo = Foo; +/// let _bar = Bar; /// } /// ``` /// -/// ## Variables are dropped in reverse order of declaration -/// -/// `_first` is declared first and `_second` is declared second, so `main` will -/// print `Declared second!` and then `Declared first!`. +/// This will print /// +/// ```text +/// Dropping Bar! +/// Dropping Foo! /// ``` -/// struct PrintOnDrop(&'static str); /// -/// impl Drop for PrintOnDrop { -/// fn drop(&mut self) { -/// println!("{}", self.0); -/// } -/// } +/// Please see [the reference] for the full rules. /// -/// fn main() { -/// let _first = PrintOnDrop("Declared first!"); -/// let _second = PrintOnDrop("Declared second!"); -/// } -/// ``` +/// [the reference]: https://doc.rust-lang.org/reference/destructors.html +/// +/// ## `Copy` and `Drop` are exclusive +/// +/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that +/// are `Copy` get implicitly duplicated by the compiler, making it very +/// hard to predict when, and how often destructors will be executed. As such, +/// these types cannot have destructors. +/// +/// [`Copy`]: ../../std/marker/trait.Copy.html #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Drop { diff --git a/src/libcore/ops/index.rs b/src/libcore/ops/index.rs index aae0691122415..763b33606fe88 100644 --- a/src/libcore/ops/index.rs +++ b/src/libcore/ops/index.rs @@ -65,6 +65,7 @@ pub trait Index { /// Performs the indexing (`container[index]`) operation. #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] fn index(&self, index: Idx) -> &Self::Output; } @@ -166,5 +167,6 @@ see chapter in The Book : Index { /// Performs the mutable indexing (`container[index]`) operation. #[stable(feature = "rust1", since = "1.0.0")] + #[track_caller] fn index_mut(&mut self, index: Idx) -> &mut Self::Output; } diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index adee8cea442b4..d4e6048579a56 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -98,8 +98,6 @@ impl> Range { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!(!(3..5).contains(&2)); /// assert!( (3..5).contains(&3)); /// assert!( (3..5).contains(&4)); @@ -139,10 +137,9 @@ impl> Range { /// ``` /// #![feature(range_is_empty)] /// - /// use std::f32::NAN; /// assert!(!(3.0..5.0).is_empty()); - /// assert!( (3.0..NAN).is_empty()); - /// assert!( (NAN..5.0).is_empty()); + /// assert!( (3.0..f32::NAN).is_empty()); + /// assert!( (f32::NAN..5.0).is_empty()); /// ``` #[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")] pub fn is_empty(&self) -> bool { @@ -199,8 +196,6 @@ impl> RangeFrom { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!(!(3..).contains(&2)); /// assert!( (3..).contains(&3)); /// assert!( (3..).contains(&1_000_000_000)); @@ -283,8 +278,6 @@ impl> RangeTo { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!( (..5).contains(&-1_000_000_000)); /// assert!( (..5).contains(&4)); /// assert!(!(..5).contains(&5)); @@ -454,8 +447,6 @@ impl> RangeInclusive { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!(!(3..=5).contains(&2)); /// assert!( (3..=5).contains(&3)); /// assert!( (3..=5).contains(&4)); @@ -496,10 +487,9 @@ impl> RangeInclusive { /// ``` /// #![feature(range_is_empty)] /// - /// use std::f32::NAN; /// assert!(!(3.0..=5.0).is_empty()); - /// assert!( (3.0..=NAN).is_empty()); - /// assert!( (NAN..=5.0).is_empty()); + /// assert!( (3.0..=f32::NAN).is_empty()); + /// assert!( (f32::NAN..=5.0).is_empty()); /// ``` /// /// This method returns `true` after iteration has finished: @@ -583,8 +573,6 @@ impl> RangeToInclusive { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!( (..=5).contains(&-1_000_000_000)); /// assert!( (..=5).contains(&5)); /// assert!(!(..=5).contains(&6)); @@ -723,8 +711,6 @@ pub trait RangeBounds { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!( (3..5).contains(&4)); /// assert!(!(3..5).contains(&2)); /// diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 3aab8b1b3337c..e8483875c97e5 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -133,8 +133,6 @@ //! [`Box`]: ../../std/boxed/struct.Box.html //! [`i32`]: ../../std/primitive.i32.html -// ignore-tidy-undocumented-unsafe - #![stable(feature = "rust1", since = "1.0.0")] use crate::iter::{FromIterator, FusedIterator, TrustedLen}; @@ -301,6 +299,8 @@ impl Option { #[inline] #[stable(feature = "pin", since = "1.33.0")] pub fn as_pin_ref(self: Pin<&Self>) -> Option> { + // SAFETY: `x` is guaranteed to be pinned because it comes from `self` + // which is pinned. unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } } @@ -310,6 +310,8 @@ impl Option { #[inline] #[stable(feature = "pin", since = "1.33.0")] pub fn as_pin_mut(self: Pin<&mut Self>) -> Option> { + // SAFETY: `get_unchecked_mut` is never used to move the `Option` inside `self`. + // `x` is guaranteed to be pinned because it comes from `self` which is pinned. unsafe { Pin::get_unchecked_mut(self).as_mut().map(|x| Pin::new_unchecked(x)) } } @@ -858,6 +860,8 @@ impl Option { match *self { Some(ref mut v) => v, + // SAFETY: a `None` variant for `self` would have been replaced by a `Some` + // variant in the code above. None => unsafe { hint::unreachable_unchecked() }, } } @@ -1353,6 +1357,15 @@ impl<'a, T> IntoIterator for &'a mut Option { #[stable(since = "1.12.0", feature = "option_from")] impl From for Option { + /// Copies `val` into a new `Some`. + /// + /// # Examples + /// + /// ``` + /// let o: Option = Option::from(67); + /// + /// assert_eq!(Some(67), o); + /// ``` fn from(val: T) -> Option { Some(val) } @@ -1360,6 +1373,27 @@ impl From for Option { #[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] impl<'a, T> From<&'a Option> for Option<&'a T> { + /// Converts from `&Option` to `Option<&T>`. + /// + /// # Examples + /// + /// Converts an `Option<`[`String`]`>` into an `Option<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take an `Option` to a reference + /// to the value inside the original. + /// + /// [`map`]: ../../std/option/enum.Option.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// let s: Option = Some(String::from("Hello, Rustaceans!")); + /// let o: Option = Option::from(&s).map(|ss: &String| ss.len()); + /// + /// println!("Can still print s: {:?}", s); + /// + /// assert_eq!(o, Some(18)); + /// ``` fn from(o: &'a Option) -> Option<&'a T> { o.as_ref() } @@ -1367,6 +1401,21 @@ impl<'a, T> From<&'a Option> for Option<&'a T> { #[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] impl<'a, T> From<&'a mut Option> for Option<&'a mut T> { + /// Converts from `&mut Option` to `Option<&mut T>` + /// + /// # Examples + /// + /// ``` + /// let mut s = Some(String::from("Hello")); + /// let o: Option<&mut String> = Option::from(&mut s); + /// + /// match o { + /// Some(t) => *t = String::from("Hello, Rustaceans!"), + /// None => (), + /// } + /// + /// assert_eq!(s, Some(String::from("Hello, Rustaceans!"))); + /// ``` fn from(o: &'a mut Option) -> Option<&'a mut T> { o.as_mut() } diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index dbfcbca3ffcbf..1ffd493d8130b 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -77,7 +77,11 @@ impl<'a> PanicInfo<'a> { /// use std::panic; /// /// panic::set_hook(Box::new(|panic_info| { - /// println!("panic occurred: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); + /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + /// println!("panic occurred: {:?}", s); + /// } else { + /// println!("panic occurred"); + /// } /// })); /// /// panic!("Normal panic"); @@ -112,8 +116,10 @@ impl<'a> PanicInfo<'a> { /// /// panic::set_hook(Box::new(|panic_info| { /// if let Some(location) = panic_info.location() { - /// println!("panic occurred in file '{}' at line {}", location.file(), - /// location.line()); + /// println!("panic occurred in file '{}' at line {}", + /// location.file(), + /// location.line(), + /// ); /// } else { /// println!("panic occurred but can't get location information..."); /// } @@ -222,6 +228,8 @@ impl<'a> Location<'a> { /// assert_ne!(this_location.line(), another_location.line()); /// assert_ne!(this_location.column(), another_location.column()); /// ``` + // FIXME: When stabilizing this method, please also update the documentation + // of `intrinsics::caller_location`. #[unstable( feature = "track_caller", reason = "uses #[track_caller] which is not yet stable", diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 3587f3f0ebf56..16739b4a1afdf 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -19,8 +19,6 @@ //! necessary lang items for the compiler. All panics are funneled through this //! one function. The actual symbol is declared through the `#[panic_handler]` attribute. -// ignore-tidy-undocumented-unsafe - #![allow(dead_code, missing_docs)] #![unstable( feature = "core_panic", @@ -41,7 +39,12 @@ use crate::panic::{Location, PanicInfo}; #[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators pub fn panic(expr: &str) -> ! { if cfg!(feature = "panic_immediate_abort") { - unsafe { super::intrinsics::abort() } + // remove `unsafe` (and safety comment) on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + // SAFETY: the `abort` intrinsic has no requirements to be called. + unsafe { + super::intrinsics::abort() + } } // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially @@ -50,50 +53,39 @@ pub fn panic(expr: &str) -> ! { // truncation and padding (even though none is used here). Using // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. - #[cfg(not(bootstrap))] panic_fmt(fmt::Arguments::new_v1(&[expr], &[])); - #[cfg(bootstrap)] - panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), Location::caller()); } -#[cfg(not(bootstrap))] #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access fn panic_bounds_check(index: usize, len: usize) -> ! { if cfg!(feature = "panic_immediate_abort") { - unsafe { super::intrinsics::abort() } + // remove `unsafe` (and safety comment) on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + // SAFETY: the `abort` intrinsic has no requirements to be called. + unsafe { + super::intrinsics::abort() + } } panic!("index out of bounds: the len is {} but the index is {}", len, index) } -// For bootstrap, we need a variant with the old argument order, and a corresponding -// `panic_fmt`. -#[cfg(bootstrap)] -#[cold] -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -#[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access -fn panic_bounds_check(location: &Location<'_>, index: usize, len: usize) -> ! { - if cfg!(feature = "panic_immediate_abort") { - unsafe { super::intrinsics::abort() } - } - - panic_fmt( - format_args!("index out of bounds: the len is {} but the index is {}", len, index), - location, - ) -} - /// The underlying implementation of libcore's `panic!` macro when formatting is used. #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cfg_attr(not(bootstrap), track_caller)] -pub fn panic_fmt(fmt: fmt::Arguments<'_>, #[cfg(bootstrap)] location: &Location<'_>) -> ! { +#[track_caller] +pub fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { - unsafe { super::intrinsics::abort() } + // remove `unsafe` (and safety comment) on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + // SAFETY: the `abort` intrinsic has no requirements to be called. + unsafe { + super::intrinsics::abort() + } } // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call @@ -103,10 +95,8 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>, #[cfg(bootstrap)] location: &Location< fn panic_impl(pi: &PanicInfo<'_>) -> !; } - #[cfg(bootstrap)] - let pi = PanicInfo::internal_constructor(Some(&fmt), location); - #[cfg(not(bootstrap))] let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller()); + // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. unsafe { panic_impl(&pi) } } diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs index c91370b271992..b4fff3d67b555 100644 --- a/src/libcore/prelude/v1.rs +++ b/src/libcore/prelude/v1.rs @@ -54,11 +54,12 @@ pub use crate::fmt::macros::Debug; pub use crate::hash::macros::Hash; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] #[doc(no_inline)] pub use crate::{ asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, global_asm, include, include_bytes, include_str, line, log_syntax, module_path, - option_env, stringify, trace_macros, + format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, + module_path, option_env, stringify, trace_macros, }; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] @@ -68,7 +69,6 @@ pub use crate::macros::builtin::{ bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable, }; -#[cfg(not(bootstrap))] #[unstable( feature = "cfg_accessible", issue = "64797", diff --git a/src/libcore/ptr/const_ptr.rs b/src/libcore/ptr/const_ptr.rs index a540016854df3..85ba5fc0638ea 100644 --- a/src/libcore/ptr/const_ptr.rs +++ b/src/libcore/ptr/const_ptr.rs @@ -3,8 +3,6 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics; use crate::mem; -// ignore-tidy-undocumented-unsafe - #[lang = "const_ptr"] impl *const T { /// Returns `true` if the pointer is null. @@ -152,6 +150,7 @@ impl *const T { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub unsafe fn offset(self, count: isize) -> *const T where @@ -210,11 +209,13 @@ impl *const T { /// } /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub fn wrapping_offset(self, count: isize) -> *const T where T: Sized, { + // SAFETY: the `arith_offset` intrinsic has no prerequisites to be called. unsafe { intrinsics::arith_offset(self, count) } } @@ -391,6 +392,7 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub unsafe fn add(self, count: usize) -> Self where @@ -452,6 +454,7 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub unsafe fn sub(self, count: usize) -> Self where @@ -507,6 +510,7 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub fn wrapping_add(self, count: usize) -> Self where @@ -562,6 +566,7 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub fn wrapping_sub(self, count: usize) -> Self where @@ -659,8 +664,8 @@ impl *const T { /// `align`. /// /// If it is not possible to align the pointer, the implementation returns - /// `usize::max_value()`. It is permissible for the implementation to *always* - /// return `usize::max_value()`. Only your algorithm's performance can depend + /// `usize::MAX`. It is permissible for the implementation to *always* + /// return `usize::MAX`. Only your algorithm's performance can depend /// on getting a usable offset here, not its correctness. /// /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be @@ -702,10 +707,40 @@ impl *const T { if !align.is_power_of_two() { panic!("align_offset: align is not a power-of-two"); } + // SAFETY: `align` has been checked to be a power of 2 above unsafe { align_offset(self, align) } } } +#[lang = "const_slice_ptr"] +impl *const [T] { + /// Returns the length of a raw slice. + /// + /// The returned value is the number of **elements**, not the number of bytes. + /// + /// This function is safe, even when the raw slice cannot be cast to a slice + /// reference because the pointer is null or unaligned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_len)] + /// + /// use std::ptr; + /// + /// let slice: *const [i8] = ptr::slice_from_raw_parts(ptr::null(), 3); + /// assert_eq!(slice.len(), 3); + /// ``` + #[inline] + #[unstable(feature = "slice_ptr_len", issue = "71146")] + #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + pub const fn len(self) -> usize { + // SAFETY: this is safe because `*const [T]` and `FatPtr` have the same layout. + // Only `std` can make this guarantee. + unsafe { Repr { rust: self }.raw }.len + } +} + // Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 4913cd73a2a9a..ecc70adda4111 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -65,8 +65,6 @@ //! [`write_volatile`]: ./fn.write_volatile.html //! [`NonNull::dangling`]: ./struct.NonNull.html#method.dangling -// ignore-tidy-undocumented-unsafe - #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering; @@ -76,12 +74,15 @@ use crate::intrinsics::{self, is_aligned_and_not_null, is_nonoverlapping}; use crate::mem::{self, MaybeUninit}; #[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] pub use crate::intrinsics::copy_nonoverlapping; #[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] pub use crate::intrinsics::copy; #[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] pub use crate::intrinsics::write_bytes; mod non_null; @@ -109,11 +110,17 @@ mod mut_ptr; /// as the compiler doesn't need to prove that it's sound to elide the /// copy. /// +/// * It can be used to drop [pinned] data when `T` is not `repr(packed)` +/// (pinned data must not be moved before it is dropped). +/// /// Unaligned values cannot be dropped in place, they must be copied to an aligned -/// location first using [`ptr::read_unaligned`]. +/// location first using [`ptr::read_unaligned`]. For packed structs, this move is +/// done automatically by the compiler. This means the fields of packed structs +/// are not dropped in-place. /// /// [`ptr::read`]: ../ptr/fn.read.html /// [`ptr::read_unaligned`]: ../ptr/fn.read_unaligned.html +/// [pinned]: ../pin/index.html /// /// # Safety /// @@ -245,14 +252,17 @@ pub(crate) struct FatPtr { /// /// // create a slice pointer when starting out with a pointer to the first element /// let x = [5, 6, 7]; -/// let ptr = x.as_ptr(); -/// let slice = ptr::slice_from_raw_parts(ptr, 3); +/// let raw_pointer = x.as_ptr(); +/// let slice = ptr::slice_from_raw_parts(raw_pointer, 3); /// assert_eq!(unsafe { &*slice }[2], 7); /// ``` #[inline] #[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { + // SAFETY: Accessing the value from the `Repr` union is safe since *const [T] + // and FatPtr have the same memory layouts. Only std can make this + // guarantee. unsafe { Repr { raw: FatPtr { data, len } }.rust } } @@ -266,10 +276,28 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { /// /// [`slice_from_raw_parts`]: fn.slice_from_raw_parts.html /// [`from_raw_parts_mut`]: ../../std/slice/fn.from_raw_parts_mut.html +/// +/// # Examples +/// +/// ```rust +/// use std::ptr; +/// +/// let x = &mut [5, 6, 7]; +/// let raw_pointer = x.as_mut_ptr(); +/// let slice = ptr::slice_from_raw_parts_mut(raw_pointer, 3); +/// +/// unsafe { +/// (*slice)[2] = 99; // assign a value at an index in the slice +/// }; +/// +/// assert_eq!(unsafe { &*slice }[2], 99); +/// ``` #[inline] #[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { + // SAFETY: Accessing the value from the `Repr` union is safe since *mut [T] + // and FatPtr have the same memory layouts unsafe { Repr { raw: FatPtr { data, len } }.rust_mut } } diff --git a/src/libcore/ptr/mut_ptr.rs b/src/libcore/ptr/mut_ptr.rs index 01d830ca18602..0781d7e6cac45 100644 --- a/src/libcore/ptr/mut_ptr.rs +++ b/src/libcore/ptr/mut_ptr.rs @@ -2,8 +2,6 @@ use super::*; use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics; -// ignore-tidy-undocumented-unsafe - #[lang = "mut_ptr"] impl *mut T { /// Returns `true` if the pointer is null. @@ -146,6 +144,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub unsafe fn offset(self, count: isize) -> *mut T where @@ -203,11 +202,13 @@ impl *mut T { /// assert_eq!(&data, &[0, 2, 0, 4, 0]); /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub fn wrapping_offset(self, count: isize) -> *mut T where T: Sized, { + // SAFETY: the `arith_offset` intrinsic has no prerequisites to be called. unsafe { intrinsics::arith_offset(self, count) as *mut T } } @@ -437,6 +438,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub unsafe fn add(self, count: usize) -> Self where @@ -498,6 +500,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub unsafe fn sub(self, count: usize) -> Self where @@ -553,6 +556,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub fn wrapping_add(self, count: usize) -> Self where @@ -608,6 +612,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub fn wrapping_sub(self, count: usize) -> Self where @@ -847,8 +852,8 @@ impl *mut T { /// `align`. /// /// If it is not possible to align the pointer, the implementation returns - /// `usize::max_value()`. It is permissible for the implementation to *always* - /// return `usize::max_value()`. Only your algorithm's performance can depend + /// `usize::MAX`. It is permissible for the implementation to *always* + /// return `usize::MAX`. Only your algorithm's performance can depend /// on getting a usable offset here, not its correctness. /// /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be @@ -890,10 +895,40 @@ impl *mut T { if !align.is_power_of_two() { panic!("align_offset: align is not a power-of-two"); } + // SAFETY: `align` has been checked to be a power of 2 above unsafe { align_offset(self, align) } } } +#[lang = "mut_slice_ptr"] +impl *mut [T] { + /// Returns the length of a raw slice. + /// + /// The returned value is the number of **elements**, not the number of bytes. + /// + /// This function is safe, even when the raw slice cannot be cast to a slice + /// reference because the pointer is null or unaligned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_len)] + /// + /// use std::ptr; + /// + /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3); + /// assert_eq!(slice.len(), 3); + /// ``` + #[inline] + #[unstable(feature = "slice_ptr_len", issue = "71146")] + #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + pub const fn len(self) -> usize { + // SAFETY: this is safe because `*const [T]` and `FatPtr` have the same layout. + // Only `std` can make this guarantee. + unsafe { Repr { rust_mut: self }.raw }.len + } +} + // Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { diff --git a/src/libcore/ptr/non_null.rs b/src/libcore/ptr/non_null.rs index 626e58d49306e..7d08503215ed0 100644 --- a/src/libcore/ptr/non_null.rs +++ b/src/libcore/ptr/non_null.rs @@ -7,8 +7,6 @@ use crate::mem; use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::ptr::Unique; -// ignore-tidy-undocumented-unsafe - /// `*mut T` but non-zero and covariant. /// /// This is often the correct thing to use when building data structures using @@ -69,6 +67,9 @@ impl NonNull { #[rustc_const_stable(feature = "const_nonnull_dangling", since = "1.32.0")] #[inline] pub const fn dangling() -> Self { + // SAFETY: mem::align_of() returns a non-zero usize which is then casted + // to a *mut T. Therefore, `ptr` is not null and the conditions for + // calling new_unchecked() are respected. unsafe { let ptr = mem::align_of::() as *mut T; NonNull::new_unchecked(ptr) @@ -93,7 +94,12 @@ impl NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[inline] pub fn new(ptr: *mut T) -> Option { - if !ptr.is_null() { Some(unsafe { Self::new_unchecked(ptr) }) } else { None } + if !ptr.is_null() { + // SAFETY: The pointer is already checked and is not null + Some(unsafe { Self::new_unchecked(ptr) }) + } else { + None + } } /// Acquires the underlying `*mut` pointer. @@ -131,6 +137,7 @@ impl NonNull { #[rustc_const_stable(feature = "const_nonnull_cast", since = "1.32.0")] #[inline] pub const fn cast(self) -> NonNull { + // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) } } } @@ -205,6 +212,8 @@ impl hash::Hash for NonNull { impl From> for NonNull { #[inline] fn from(unique: Unique) -> Self { + // SAFETY: A Unique pointer cannot be null, so the conditions for + // new_unchecked() are respected. unsafe { NonNull::new_unchecked(unique.as_ptr()) } } } @@ -213,6 +222,7 @@ impl From> for NonNull { impl From<&mut T> for NonNull { #[inline] fn from(reference: &mut T) -> Self { + // SAFETY: A mutable reference cannot be null. unsafe { NonNull { pointer: reference as *mut T } } } } @@ -221,6 +231,8 @@ impl From<&mut T> for NonNull { impl From<&T> for NonNull { #[inline] fn from(reference: &T) -> Self { + // SAFETY: A reference cannot be null, so the conditions for + // new_unchecked() are respected. unsafe { NonNull { pointer: reference as *const T } } } } diff --git a/src/libcore/ptr/unique.rs b/src/libcore/ptr/unique.rs index 87b56d951c6ce..f58d35f06137d 100644 --- a/src/libcore/ptr/unique.rs +++ b/src/libcore/ptr/unique.rs @@ -3,7 +3,6 @@ use crate::fmt; use crate::marker::{PhantomData, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, DispatchFromDyn}; -use crate::ptr::NonNull; // ignore-tidy-undocumented-unsafe @@ -71,9 +70,10 @@ impl Unique { /// a `T`, which means this must not be used as a "not yet initialized" /// sentinel value. Types that lazily allocate must track initialization by /// some other means. - // FIXME: rename to dangling() to match NonNull? #[inline] - pub const fn empty() -> Self { + pub const fn dangling() -> Self { + // SAFETY: mem::align_of() returns a valid, non-null pointer. The + // conditions to call new_unchecked() are thus respected. unsafe { Unique::new_unchecked(mem::align_of::() as *mut T) } } } @@ -94,6 +94,7 @@ impl Unique { #[inline] pub fn new(ptr: *mut T) -> Option { if !ptr.is_null() { + // SAFETY: The pointer has already been checked and is not null. Some(unsafe { Unique { pointer: ptr as _, _marker: PhantomData } }) } else { None @@ -129,6 +130,9 @@ impl Unique { /// Casts to a pointer of another type. #[inline] pub const fn cast(self) -> Unique { + // SAFETY: Unique::new_unchecked() creates a new unique and needs + // the given pointer to not be null. + // Since we are passing self as a pointer, it cannot be null. unsafe { Unique::new_unchecked(self.as_ptr() as *mut U) } } } @@ -168,22 +172,7 @@ impl fmt::Pointer for Unique { impl From<&mut T> for Unique { #[inline] fn from(reference: &mut T) -> Self { + // SAFETY: A mutable reference cannot be null unsafe { Unique { pointer: reference as *mut T, _marker: PhantomData } } } } - -#[unstable(feature = "ptr_internals", issue = "none")] -impl From<&T> for Unique { - #[inline] - fn from(reference: &T) -> Self { - unsafe { Unique { pointer: reference as *const T, _marker: PhantomData } } - } -} - -#[unstable(feature = "ptr_internals", issue = "none")] -impl From> for Unique { - #[inline] - fn from(p: NonNull) -> Self { - unsafe { Unique::new_unchecked(p.as_ptr()) } - } -} diff --git a/src/libcore/raw.rs b/src/libcore/raw.rs index 75c329a7d6c10..cb0fb8795e581 100644 --- a/src/libcore/raw.rs +++ b/src/libcore/raw.rs @@ -6,7 +6,8 @@ //! They can be used as targets of transmutes in unsafe code for manipulating //! the raw representations directly. //! -//! Their definition should always match the ABI defined in `rustc::back::abi`. +//! Their definition should always match the ABI defined in +//! `rustc_middle::ty::layout`. /// The representation of a trait object like `&SomeTrait`. /// diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 0bc29e1bc662c..c7b5777a16e7f 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -230,9 +230,9 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::fmt; use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; use crate::ops::{self, Deref, DerefMut}; +use crate::{convert, fmt}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). /// @@ -521,14 +521,16 @@ impl Result { } } - /// Applies a function to the contained value (if any), - /// or returns the provided default (if not). + /// Applies a function to the contained value (if [`Ok`]), + /// or returns the provided default (if [`Err`]). /// /// Arguments passed to `map_or` are eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`map_or_else`], /// which is lazily evaluated. /// /// [`map_or_else`]: #method.map_or_else + /// [`Ok`]: enum.Result.html#variant.Ok + /// [`Err`]: enum.Result.html#variant.Err /// /// # Examples /// @@ -1214,6 +1216,38 @@ impl Result, E> { } } +impl Result, E> { + /// Converts from `Result, E>` to `Result` + /// + /// # Examples + /// Basic usage: + /// ``` + /// #![feature(result_flattening)] + /// let x: Result, u32> = Ok(Ok("hello")); + /// assert_eq!(Ok("hello"), x.flatten()); + /// + /// let x: Result, u32> = Ok(Err(6)); + /// assert_eq!(Err(6), x.flatten()); + /// + /// let x: Result, u32> = Err(6); + /// assert_eq!(Err(6), x.flatten()); + /// ``` + /// + /// Flattening once only removes one level of nesting: + /// + /// ``` + /// #![feature(result_flattening)] + /// let x: Result, u32>, u32> = Ok(Ok(Ok("hello"))); + /// assert_eq!(Ok(Ok("hello")), x.flatten()); + /// assert_eq!(Ok("hello"), x.flatten().flatten()); + /// ``` + #[inline] + #[unstable(feature = "result_flattening", issue = "70142")] + pub fn flatten(self) -> Result { + self.and_then(convert::identity) + } +} + // This is a separate function to reduce the code size of the methods #[inline(never)] #[cold] diff --git a/src/libcore/slice/memchr.rs b/src/libcore/slice/memchr.rs index 2a2169dd348c2..3b13ed5fed396 100644 --- a/src/libcore/slice/memchr.rs +++ b/src/libcore/slice/memchr.rs @@ -34,7 +34,7 @@ fn repeat_byte(b: u8) -> usize { #[cfg(not(target_pointer_width = "16"))] #[inline] fn repeat_byte(b: u8) -> usize { - (b as usize) * (crate::usize::MAX / 255) + (b as usize) * (usize::MAX / 255) } /// Returns the first index matching the byte `x` in `text`. diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 0e12e6360da95..9582ac33ff6b7 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -27,7 +27,6 @@ use crate::cmp; use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; use crate::intrinsics::{assume, exact_div, is_aligned_and_not_null, unchecked_sub}; -use crate::isize; use crate::iter::*; use crate::marker::{self, Copy, Send, Sized, Sync}; use crate::mem; @@ -1170,7 +1169,7 @@ impl [T] { /// assert_eq!(iter.next().unwrap(), &[10, 40, 33]); /// assert!(iter.next().is_none()); /// ``` - #[unstable(feature = "split_inclusive", issue = "none")] + #[unstable(feature = "split_inclusive", issue = "72360")] #[inline] pub fn split_inclusive(&self, pred: F) -> SplitInclusive<'_, T, F> where @@ -1195,7 +1194,7 @@ impl [T] { /// } /// assert_eq!(v, [10, 40, 1, 20, 1, 1]); /// ``` - #[unstable(feature = "split_inclusive", issue = "none")] + #[unstable(feature = "split_inclusive", issue = "72360")] #[inline] pub fn split_inclusive_mut(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F> where @@ -1605,7 +1604,7 @@ impl [T] { /// Sorts the slice, but may not preserve the order of equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place - /// (i.e., does not allocate), and `O(n log n)` worst-case. + /// (i.e., does not allocate), and `O(n * log(n))` worst-case. /// /// # Current implementation /// @@ -1641,7 +1640,7 @@ impl [T] { /// 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. + /// (i.e., does not allocate), and `O(n * log(n))` worst-case. /// /// The comparator function must define a total ordering for the elements in the slice. If /// the ordering is not total, the order of the elements is unspecified. An order is a @@ -1696,7 +1695,7 @@ impl [T] { /// elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place - /// (i.e., does not allocate), and `O(m n log(m n))` worst-case, where the key function is + /// (i.e., does not allocate), and `O(m * n * log(n))` worst-case, where the key function is /// `O(m)`. /// /// # Current implementation @@ -1956,7 +1955,7 @@ impl [T] { // over all the elements, swapping as we go so that at the end // the elements we wish to keep are in the front, and those we // wish to reject are at the back. We can then split the slice. - // This operation is still O(n). + // This operation is still `O(n)`. // // Example: We start in this state, where `r` represents "next // read" and `w` represents "next_write`. @@ -2145,6 +2144,31 @@ impl [T] { } } + /// Fills `self` with elements by cloning `value`. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_fill)] + /// + /// let mut buf = vec![0; 10]; + /// buf.fill(1); + /// assert_eq!(buf, vec![1; 10]); + /// ``` + #[unstable(feature = "slice_fill", issue = "70758")] + pub fn fill(&mut self, value: T) + where + T: Clone, + { + if let Some((last, elems)) = self.split_last_mut() { + for el in elems { + el.clone_from(&value); + } + + *last = value + } + } + /// Copies the elements from `src` into `self`. /// /// The length of `src` must be the same as `self`. @@ -2306,6 +2330,7 @@ impl [T] { /// assert_eq!(&bytes, b"Hello, Wello!"); /// ``` #[stable(feature = "copy_within", since = "1.37.0")] + #[track_caller] pub fn copy_within>(&mut self, src: R, dest: usize) where T: Copy, @@ -2587,7 +2612,7 @@ impl [T] { /// assert!(![1, 3, 2, 4].is_sorted()); /// assert!([0].is_sorted()); /// assert!(empty.is_sorted()); - /// assert!(![0.0, 1.0, std::f32::NAN].is_sorted()); + /// assert!(![0.0, 1.0, f32::NAN].is_sorted()); /// ``` #[inline] #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] @@ -2721,18 +2746,21 @@ where #[inline(never)] #[cold] +#[track_caller] fn slice_index_len_fail(index: usize, len: usize) -> ! { panic!("index {} out of range for slice of length {}", index, len); } #[inline(never)] #[cold] +#[track_caller] fn slice_index_order_fail(index: usize, end: usize) -> ! { panic!("slice index starts at {} but ends at {}", index, end); } #[inline(never)] #[cold] +#[track_caller] fn slice_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); } @@ -2804,11 +2832,13 @@ pub trait SliceIndex: private_slice_index::Sealed { /// Returns a shared reference to the output at this location, panicking /// if out of bounds. #[unstable(feature = "slice_index_methods", issue = "none")] + #[track_caller] fn index(self, slice: &T) -> &Self::Output; /// Returns a mutable reference to the output at this location, panicking /// if out of bounds. #[unstable(feature = "slice_index_methods", issue = "none")] + #[track_caller] fn index_mut(self, slice: &mut T) -> &mut Self::Output; } @@ -3149,6 +3179,7 @@ macro_rules! is_empty { $self.ptr.as_ptr() as *const T == $self.end }; } + // To get rid of some bounds checks (see `position`), we compute the length in a somewhat // unexpected way. (Tested by `codegen/slice-position-bounds-check`.) macro_rules! len { @@ -3317,40 +3348,127 @@ macro_rules! iterator { self.next_back() } + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn for_each(mut self, mut f: F) + where + Self: Sized, + F: FnMut(Self::Item), + { + while let Some(x) = self.next() { + f(x); + } + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn all(&mut self, mut f: F) -> bool + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + while let Some(x) = self.next() { + if !f(x) { + return false; + } + } + true + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn any(&mut self, mut f: F) -> bool + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + while let Some(x) = self.next() { + if f(x) { + return true; + } + } + false + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn find